12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241 |
- import { Vec } from '@tldraw/vec'
-
- export type TLIntersection = {
- didIntersect: boolean
- message: string
- points: number[][]
- }
-
- export interface TLBounds {
- minX: number
- minY: number
- maxX: number
- maxY: number
- width: number
- height: number
- rotation?: number
- }
-
- /**
- * Get an intersection.
- * @param message
- * @param points
- * @internal
- */
- function createIntersection(message: string, ...points: number[][]): TLIntersection {
- const didIntersect = points.length > 0
- return { didIntersect, message, points }
- }
-
- /**
- *
- * @param point
- * @param size
- * @param rotation
- * @internal
- */
- function getRectangleSides(point: number[], size: number[], rotation = 0): [string, number[][]][] {
- const center = [point[0] + size[0] / 2, point[1] + size[1] / 2]
- const tl = Vec.rotWith(point, center, rotation)
- const tr = Vec.rotWith(Vec.add(point, [size[0], 0]), center, rotation)
- const br = Vec.rotWith(Vec.add(point, size), center, rotation)
- const bl = Vec.rotWith(Vec.add(point, [0, size[1]]), center, rotation)
-
- return [
- ['top', [tl, tr]],
- ['right', [tr, br]],
- ['bottom', [br, bl]],
- ['left', [bl, tl]],
- ]
- }
-
- /**
- * Get whether angle c lies between angles a and b.
- * @param a
- * @param b
- * @param c
- * @internal
- */
- function isAngleBetween(a: number, b: number, c: number): boolean {
- if (c === a || c === b) return true
- const PI2 = Math.PI * 2
- const AB = (b - a + PI2) % PI2
- const AC = (c - a + PI2) % PI2
- return AB <= Math.PI !== AC > AB
- }
-
- /* -------------------------------------------------- */
- /* Ray */
- /* -------------------------------------------------- */
-
- /**
- * Find the intersection between a ray and a ray.
- * @param p0 The first ray's point
- * @param n0 The first ray's direction vector.
- * @param p1 The second ray's point.
- * @param n1 The second ray's direction vector.
- */
- export function intersectRayRay(
- p0: number[],
- n0: number[],
- p1: number[],
- n1: number[]
- ): TLIntersection {
- const dx = p1[0] - p0[0]
- const dy = p1[1] - p0[1]
- const det = n1[0] * n0[1] - n1[1] * n0[0]
- const u = (dy * n1[0] - dx * n1[1]) / det
- const v = (dy * n0[0] - dx * n0[1]) / det
- if (u < 0 || v < 0) return createIntersection('miss')
-
- const m0 = n0[1] / n0[0]
- const m1 = n1[1] / n1[0]
- const b0 = p0[1] - m0 * p0[0]
- const b1 = p1[1] - m1 * p1[0]
- const x = (b1 - b0) / (m0 - m1)
- const y = m0 * x + b0
-
- return Number.isFinite(x)
- ? createIntersection('intersection', [x, y])
- : createIntersection('parallel')
- }
-
- /**
- * Find the intersections between a ray and a line segment.
- * @param origin
- * @param direction
- * @param a1
- * @param a2
- */
- export function intersectRayLineSegment(
- origin: number[],
- direction: number[],
- a1: number[],
- a2: number[]
- ): TLIntersection {
- const [x, y] = origin
- const [dx, dy] = direction
- const [x1, y1] = a1
- const [x2, y2] = a2
-
- if (dy / dx !== (y2 - y1) / (x2 - x1)) {
- const d = dx * (y2 - y1) - dy * (x2 - x1)
- if (d !== 0) {
- const r = ((y - y1) * (x2 - x1) - (x - x1) * (y2 - y1)) / d
- const s = ((y - y1) * dx - (x - x1) * dy) / d
- if (r >= 0 && s >= 0 && s <= 1) {
- return createIntersection('intersection', [x + r * dx, y + r * dy])
- }
- }
- }
- return createIntersection('no intersection')
- }
-
- /**
- * Find the intersections between a ray and a rectangle.
- * @param origin
- * @param direction
- * @param point
- * @param size
- * @param rotation
- */
- export function intersectRayRectangle(
- origin: number[],
- direction: number[],
- point: number[],
- size: number[],
- rotation = 0
- ): TLIntersection[] {
- return intersectRectangleRay(point, size, rotation, origin, direction)
- }
-
- /**
- * Find the intersections between a ray and an ellipse.
- * @param origin
- * @param direction
- * @param center
- * @param rx
- * @param ry
- * @param rotation
- */
- export function intersectRayEllipse(
- origin: number[],
- direction: number[],
- center: number[],
- rx: number,
- ry: number,
- rotation: number
- ): TLIntersection {
- const a1 = origin
- const a2 = Vec.mul(direction, 999999999)
- return intersectLineSegmentEllipse(a1, a2, center, rx, ry, rotation)
- }
-
- /**
- * Find the intersections between a ray and a bounding box.
- * @param origin
- * @param direction
- * @param bounds
- * @param rotation
- */
- export function intersectRayBounds(
- origin: number[],
- direction: number[],
- bounds: TLBounds,
- rotation = 0
- ): TLIntersection[] {
- const { minX, minY, width, height } = bounds
- return intersectRayRectangle(origin, direction, [minX, minY], [width, height], rotation)
- }
-
- /* -------------------------------------------------- */
- /* Line Segment */
- /* -------------------------------------------------- */
-
- /**
- * Find the intersection between a line segment and a ray.
- * @param a1
- * @param a2
- * @param origin
- * @param direction
- */
- export function intersectLineSegmentRay(
- a1: number[],
- a2: number[],
- origin: number[],
- direction: number[]
- ): TLIntersection {
- return intersectRayLineSegment(origin, direction, a1, a2)
- }
-
- /**
- * Find the intersection between a line segment and a line segment.
- * @param a1
- * @param a2
- * @param b1
- * @param b2
- */
- export function intersectLineSegmentLineSegment(
- a1: number[],
- a2: number[],
- b1: number[],
- b2: number[]
- ): TLIntersection {
- const AB = Vec.sub(a1, b1)
- const BV = Vec.sub(b2, b1)
- const AV = Vec.sub(a2, a1)
-
- const ua_t = BV[0] * AB[1] - BV[1] * AB[0]
- const ub_t = AV[0] * AB[1] - AV[1] * AB[0]
- const u_b = BV[1] * AV[0] - BV[0] * AV[1]
-
- if (ua_t === 0 || ub_t === 0) {
- return createIntersection('coincident')
- }
-
- if (u_b === 0) {
- return createIntersection('parallel')
- }
-
- if (u_b !== 0) {
- const ua = ua_t / u_b
- const ub = ub_t / u_b
- if (0 <= ua && ua <= 1 && 0 <= ub && ub <= 1) {
- return createIntersection('intersection', Vec.add(a1, Vec.mul(AV, ua)))
- }
- }
-
- return createIntersection('no intersection')
- }
-
- /**
- * Find the intersections between a line segment and a rectangle.
- * @param a1
- * @param a2
- * @param point
- * @param size
- */
- export function intersectLineSegmentRectangle(
- a1: number[],
- a2: number[],
- point: number[],
- size: number[]
- ): TLIntersection[] {
- return intersectRectangleLineSegment(point, size, a1, a2)
- }
-
- /**
- * Find the intersections between a line segment and an arc.
- * @param a1
- * @param a2
- * @param center
- * @param radius
- * @param start
- * @param end
- */
- export function intersectLineSegmentArc(
- a1: number[],
- a2: number[],
- center: number[],
- radius: number,
- start: number[],
- end: number[]
- ): TLIntersection {
- const sa = Vec.angle(center, start)
- const ea = Vec.angle(center, end)
- const ellipseTest = intersectEllipseLineSegment(center, radius, radius, 0, a1, a2)
-
- if (!ellipseTest.didIntersect) return createIntersection('no intersection')
-
- const points = ellipseTest.points.filter((point) =>
- isAngleBetween(sa, ea, Vec.angle(center, point))
- )
-
- if (points.length === 0) {
- return createIntersection('no intersection')
- }
-
- return createIntersection('intersection', ...points)
- }
-
- /**
- * Find the intersections between a line segment and a circle.
- * @param a1
- * @param a2
- * @param c
- * @param r
- */
- export function intersectLineSegmentCircle(
- a1: number[],
- a2: number[],
- c: number[],
- r: number
- ): TLIntersection {
- const a = (a2[0] - a1[0]) * (a2[0] - a1[0]) + (a2[1] - a1[1]) * (a2[1] - a1[1])
- const b = 2 * ((a2[0] - a1[0]) * (a1[0] - c[0]) + (a2[1] - a1[1]) * (a1[1] - c[1]))
- const cc =
- c[0] * c[0] +
- c[1] * c[1] +
- a1[0] * a1[0] +
- a1[1] * a1[1] -
- 2 * (c[0] * a1[0] + c[1] * a1[1]) -
- r * r
-
- const deter = b * b - 4 * a * cc
-
- if (deter < 0) {
- return createIntersection('outside')
- }
-
- if (deter === 0) {
- return createIntersection('tangent')
- }
-
- const e = Math.sqrt(deter)
- const u1 = (-b + e) / (2 * a)
- const u2 = (-b - e) / (2 * a)
- if ((u1 < 0 || u1 > 1) && (u2 < 0 || u2 > 1)) {
- if ((u1 < 0 && u2 < 0) || (u1 > 1 && u2 > 1)) {
- return createIntersection('outside')
- } else {
- return createIntersection('inside')
- }
- }
-
- const results: number[][] = []
- if (0 <= u1 && u1 <= 1) results.push(Vec.lrp(a1, a2, u1))
- if (0 <= u2 && u2 <= 1) results.push(Vec.lrp(a1, a2, u2))
-
- return createIntersection('intersection', ...results)
- }
-
- /**
- * Find the intersections between a line segment and an ellipse.
- * @param a1
- * @param a2
- * @param center
- * @param rx
- * @param ry
- * @param rotation
- */
- export function intersectLineSegmentEllipse(
- a1: number[],
- a2: number[],
- center: number[],
- rx: number,
- ry: number,
- rotation = 0
- ): TLIntersection {
- // If the ellipse or line segment are empty, return no tValues.
- if (rx === 0 || ry === 0 || Vec.isEqual(a1, a2)) {
- return createIntersection('no intersection')
- }
-
- // Get the semimajor and semiminor axes.
- rx = rx < 0 ? rx : -rx
- ry = ry < 0 ? ry : -ry
-
- // Rotate points and translate so the ellipse is centered at the origin.
- a1 = Vec.sub(Vec.rotWith(a1, center, -rotation), center)
- a2 = Vec.sub(Vec.rotWith(a2, center, -rotation), center)
-
- // Calculate the quadratic parameters.
- const diff = Vec.sub(a2, a1)
-
- const A = (diff[0] * diff[0]) / rx / rx + (diff[1] * diff[1]) / ry / ry
- const B = (2 * a1[0] * diff[0]) / rx / rx + (2 * a1[1] * diff[1]) / ry / ry
- const C = (a1[0] * a1[0]) / rx / rx + (a1[1] * a1[1]) / ry / ry - 1
-
- // Make a list of t values (normalized points on the line where intersections occur).
- const tValues: number[] = []
-
- // Calculate the discriminant.
- const discriminant = B * B - 4 * A * C
-
- if (discriminant === 0) {
- // One real solution.
- tValues.push(-B / 2 / A)
- } else if (discriminant > 0) {
- const root = Math.sqrt(discriminant)
- // Two real solutions.
- tValues.push((-B + root) / 2 / A)
- tValues.push((-B - root) / 2 / A)
- }
-
- // Filter to only points that are on the segment.
- // Solve for points, then counter-rotate points.
- const points = tValues
- .filter((t) => t >= 0 && t <= 1)
- .map((t) => Vec.add(center, Vec.add(a1, Vec.mul(Vec.sub(a2, a1), t))))
- .map((p) => Vec.rotWith(p, center, rotation))
-
- return createIntersection('intersection', ...points)
- }
-
- /**
- * Find the intersections between a line segment and a bounding box.
- * @param a1
- * @param a2
- * @param bounds
- */
- export function intersectLineSegmentBounds(
- a1: number[],
- a2: number[],
- bounds: TLBounds
- ): TLIntersection[] {
- return intersectBoundsLineSegment(bounds, a1, a2)
- }
-
- /**
- * Find the intersections between a line segment and a polyline.
- * @param a1
- * @param a2
- * @param points
- */
- export function intersectLineSegmentPolyline(
- a1: number[],
- a2: number[],
- points: number[][]
- ): TLIntersection {
- const pts: number[][] = []
-
- for (let i = 1; i < points.length; i++) {
- const int = intersectLineSegmentLineSegment(a1, a2, points[i - 1], points[i])
-
- if (int) {
- pts.push(...int.points)
- }
- }
-
- if (pts.length === 0) {
- return createIntersection('no intersection')
- }
-
- return createIntersection('intersection', ...points)
- }
- /**
- * Find the intersections between a line segment and a closed polygon.
- * @param a1
- * @param a2
- * @param points
- */
- export function intersectLineSegmentPolygon(
- a1: number[],
- a2: number[],
- points: number[][]
- ): TLIntersection {
- const pts: number[][] = []
-
- for (let i = 1; i < points.length + 1; i++) {
- const int = intersectLineSegmentLineSegment(a1, a2, points[i - 1], points[i % points.length])
-
- if (int) {
- pts.push(...int.points)
- }
- }
-
- if (pts.length === 0) {
- return createIntersection('no intersection')
- }
-
- return createIntersection('intersection', ...points)
- }
-
- /* -------------------------------------------------- */
- /* Rectangle */
- /* -------------------------------------------------- */
-
- /**
- * Find the intersections between a rectangle and a ray.
- * @param point
- * @param size
- * @param rotation
- * @param origin
- * @param direction
- */
- export function intersectRectangleRay(
- point: number[],
- size: number[],
- rotation: number,
- origin: number[],
- direction: number[]
- ): TLIntersection[] {
- const sideIntersections = getRectangleSides(point, size, rotation).reduce<TLIntersection[]>(
- (acc, [message, [a1, a2]]) => {
- const intersection = intersectRayLineSegment(origin, direction, a1, a2)
-
- if (intersection) {
- acc.push(createIntersection(message, ...intersection.points))
- }
-
- return acc
- },
- []
- )
-
- return sideIntersections.filter((int) => int.didIntersect)
- }
-
- /**
- * Find the intersections between a rectangle and a line segment.
- * @param point
- * @param size
- * @param a1
- * @param a2
- */
- export function intersectRectangleLineSegment(
- point: number[],
- size: number[],
- a1: number[],
- a2: number[]
- ): TLIntersection[] {
- const sideIntersections = getRectangleSides(point, size).reduce<TLIntersection[]>(
- (acc, [message, [b1, b2]]) => {
- const intersection = intersectLineSegmentLineSegment(a1, a2, b1, b2)
-
- if (intersection) {
- acc.push(createIntersection(message, ...intersection.points))
- }
-
- return acc
- },
- []
- )
-
- return sideIntersections.filter((int) => int.didIntersect)
- }
-
- /**
- * Find the intersections between a rectangle and a rectangle.
- * @param point1
- * @param size1
- * @param point2
- * @param size2
- */
- export function intersectRectangleRectangle(
- point1: number[],
- size1: number[],
- point2: number[],
- size2: number[]
- ): TLIntersection[] {
- const sideIntersections = getRectangleSides(point1, size1).reduce<TLIntersection[]>(
- (acc, [message, [a1, a2]]) => {
- const intersections = intersectRectangleLineSegment(point2, size2, a1, a2)
-
- acc.push(
- ...intersections.map((int) =>
- createIntersection(`${message} ${int.message}`, ...int.points)
- )
- )
-
- return acc
- },
- []
- )
-
- return sideIntersections.filter((int) => int.didIntersect)
- }
-
- /**
- * Find the intersections between a rectangle and an arc.
- * @param point
- * @param size
- * @param center
- * @param radius
- * @param start
- * @param end
- */
- export function intersectRectangleArc(
- point: number[],
- size: number[],
- center: number[],
- radius: number,
- start: number[],
- end: number[]
- ): TLIntersection[] {
- const sideIntersections = getRectangleSides(point, size).reduce<TLIntersection[]>(
- (acc, [message, [a1, a2]]) => {
- const intersection = intersectArcLineSegment(center, radius, start, end, a1, a2)
-
- if (intersection) {
- acc.push({ ...intersection, message })
- }
-
- return acc
- },
- []
- )
-
- return sideIntersections.filter((int) => int.didIntersect)
- }
-
- /**
- * Find the intersections between a rectangle and a circle.
- * @param point
- * @param size
- * @param c
- * @param r
- */
- export function intersectRectangleCircle(
- point: number[],
- size: number[],
- c: number[],
- r: number
- ): TLIntersection[] {
- const sideIntersections = getRectangleSides(point, size).reduce<TLIntersection[]>(
- (acc, [message, [a1, a2]]) => {
- const intersection = intersectLineSegmentCircle(a1, a2, c, r)
-
- if (intersection) {
- acc.push({ ...intersection, message })
- }
-
- return acc
- },
- []
- )
-
- return sideIntersections.filter((int) => int.didIntersect)
- }
-
- /**
- * Find the intersections between a rectangle and an ellipse.
- * @param point
- * @param size
- * @param c
- * @param rx
- * @param ry
- * @param rotation
- */
- export function intersectRectangleEllipse(
- point: number[],
- size: number[],
- c: number[],
- rx: number,
- ry: number,
- rotation = 0
- ): TLIntersection[] {
- const sideIntersections = getRectangleSides(point, size).reduce<TLIntersection[]>(
- (acc, [message, [a1, a2]]) => {
- const intersection = intersectLineSegmentEllipse(a1, a2, c, rx, ry, rotation)
-
- if (intersection) {
- acc.push({ ...intersection, message })
- }
-
- return acc
- },
- []
- )
-
- return sideIntersections.filter((int) => int.didIntersect)
- }
-
- /**
- * Find the intersections between a rectangle and a bounding box.
- * @param point
- * @param size
- * @param bounds
- */
- export function intersectRectangleBounds(
- point: number[],
- size: number[],
- bounds: TLBounds
- ): TLIntersection[] {
- const { minX, minY, width, height } = bounds
- return intersectRectangleRectangle(point, size, [minX, minY], [width, height])
- }
-
- /**
- * Find the intersections between a rectangle and a polyline.
- * @param point
- * @param size
- * @param points
- */
- export function intersectRectanglePolyline(
- point: number[],
- size: number[],
- points: number[][]
- ): TLIntersection[] {
- const sideIntersections = getRectangleSides(point, size).reduce<TLIntersection[]>(
- (acc, [message, [a1, a2]]) => {
- const intersection = intersectLineSegmentPolyline(a1, a2, points)
-
- if (intersection.didIntersect) {
- acc.push(createIntersection(message, ...intersection.points))
- }
-
- return acc
- },
- []
- )
-
- return sideIntersections.filter((int) => int.didIntersect)
- }
- /**
- * Find the intersections between a rectangle and a polygon.
- * @param point
- * @param size
- * @param points
- */
- export function intersectRectanglePolygon(
- point: number[],
- size: number[],
- points: number[][]
- ): TLIntersection[] {
- const sideIntersections = getRectangleSides(point, size).reduce<TLIntersection[]>(
- (acc, [message, [a1, a2]]) => {
- const intersection = intersectLineSegmentPolygon(a1, a2, points)
-
- if (intersection.didIntersect) {
- acc.push(createIntersection(message, ...intersection.points))
- }
-
- return acc
- },
- []
- )
-
- return sideIntersections.filter((int) => int.didIntersect)
- }
-
- /* -------------------------------------------------- */
- /* Arc */
- /* -------------------------------------------------- */
-
- /**
- * Find the intersections between a arc and a line segment.
- * @param center
- * @param radius
- * @param start
- * @param end
- * @param a1
- * @param a2
- */
- export function intersectArcLineSegment(
- center: number[],
- radius: number,
- start: number[],
- end: number[],
- a1: number[],
- a2: number[]
- ): TLIntersection {
- return intersectLineSegmentArc(a1, a2, center, radius, start, end)
- }
-
- /**
- * Find the intersections between a arc and a rectangle.
- * @param center
- * @param radius
- * @param start
- * @param end
- * @param point
- * @param size
- */
- export function intersectArcRectangle(
- center: number[],
- radius: number,
- start: number[],
- end: number[],
- point: number[],
- size: number[]
- ): TLIntersection[] {
- return intersectRectangleArc(point, size, center, radius, start, end)
- }
-
- /**
- * Find the intersections between a arc and a bounding box.
- * @param center
- * @param radius
- * @param start
- * @param end
- * @param bounds
- */
- export function intersectArcBounds(
- center: number[],
- radius: number,
- start: number[],
- end: number[],
- bounds: TLBounds
- ): TLIntersection[] {
- const { minX, minY, width, height } = bounds
- return intersectArcRectangle(center, radius, start, end, [minX, minY], [width, height])
- }
-
- /* -------------------------------------------------- */
- /* Circle */
- /* -------------------------------------------------- */
-
- /**
- * Find the intersections between a circle and a line segment.
- * @param c
- * @param r
- * @param a1
- * @param a2
- */
- export function intersectCircleLineSegment(
- c: number[],
- r: number,
- a1: number[],
- a2: number[]
- ): TLIntersection {
- return intersectLineSegmentCircle(a1, a2, c, r)
- }
-
- /**
- * Find the intersections between a circle and a circle.
- * @param c1
- * @param r1
- * @param c2
- * @param r2
- */
- export function intersectCircleCircle(
- c1: number[],
- r1: number,
- c2: number[],
- r2: number
- ): TLIntersection {
- let dx = c2[0] - c1[0],
- dy = c2[1] - c1[1]
-
- const d = Math.sqrt(dx * dx + dy * dy),
- x = (d * d - r2 * r2 + r1 * r1) / (2 * d),
- y = Math.sqrt(r1 * r1 - x * x)
-
- dx /= d
- dy /= d
-
- return createIntersection(
- 'intersection',
- [c1[0] + dx * x - dy * y, c1[1] + dy * x + dx * y],
- [c1[0] + dx * x + dy * y, c1[1] + dy * x - dx * y]
- )
- }
-
- /**
- * Find the intersections between a circle and a rectangle.
- * @param c
- * @param r
- * @param point
- * @param size
- */
- export function intersectCircleRectangle(
- c: number[],
- r: number,
- point: number[],
- size: number[]
- ): TLIntersection[] {
- return intersectRectangleCircle(point, size, c, r)
- }
-
- /**
- * Find the intersections between a circle and a bounding box.
- * @param c
- * @param r
- * @param bounds
- */
- export function intersectCircleBounds(c: number[], r: number, bounds: TLBounds): TLIntersection[] {
- const { minX, minY, width, height } = bounds
- return intersectCircleRectangle(c, r, [minX, minY], [width, height])
- }
-
- /* -------------------------------------------------- */
- /* Ellipse */
- /* -------------------------------------------------- */
-
- /**
- * Find the intersections between an ellipse and a ray.
- * @param center
- * @param rx
- * @param ry
- * @param rotation
- * @param point
- * @param direction
- */
- export function intersectEllipseRay(
- center: number[],
- rx: number,
- ry: number,
- rotation: number,
- point: number[],
- direction: number[]
- ): TLIntersection {
- return intersectRayEllipse(point, direction, center, rx, ry, rotation)
- }
-
- /**
- * Find the intersections between an ellipse and a line segment.
- * @param center
- * @param rx
- * @param ry
- * @param rotation
- * @param a1
- * @param a2
- */
- export function intersectEllipseLineSegment(
- center: number[],
- rx: number,
- ry: number,
- rotation = 0,
- a1: number[],
- a2: number[]
- ): TLIntersection {
- if (rx === ry) {
- return intersectLineSegmentCircle(a1, a2, center, rx)
- }
-
- return intersectLineSegmentEllipse(a1, a2, center, rx, ry, rotation)
- }
-
- /**
- * Find the intersections between an ellipse and a rectangle.
- * @param center
- * @param rx
- * @param ry
- * @param rotation
- * @param point
- * @param size
- */
- export function intersectEllipseRectangle(
- center: number[],
- rx: number,
- ry: number,
- rotation = 0,
- point: number[],
- size: number[]
- ): TLIntersection[] {
- if (rx === ry) {
- return intersectRectangleCircle(point, size, center, rx)
- }
-
- return intersectRectangleEllipse(point, size, center, rx, ry, rotation)
- }
-
- /**
- * Find the intersections between an ellipse and an ellipse.
- * Adapted from https://gist.github.com/drawable/92792f59b6ff8869d8b1
- * @param _c1
- * @param _rx1
- * @param _ry1
- * @param _r1
- * @param _c2
- * @param _rx2
- * @param _ry2
- * @param _r2
- */
- export function intersectEllipseEllipse(
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
- _c1: number[],
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
- _rx1: number,
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
- _ry1: number,
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
- _r1: number,
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
- _c2: number[],
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
- _rx2: number,
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
- _ry2: number,
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
- _r2: number
- ): TLIntersection {
- // TODO
- return createIntersection('no intersection')
- }
-
- /**
- * Find the intersections between an ellipse and a circle.
- * @param c
- * @param rx
- * @param ry
- * @param rotation
- * @param c2
- * @param r2
- */
- export function intersectEllipseCircle(
- c: number[],
- rx: number,
- ry: number,
- rotation: number,
- c2: number[],
- r2: number
- ): TLIntersection {
- return intersectEllipseEllipse(c, rx, ry, rotation, c2, r2, r2, 0)
- }
-
- /**
- * Find the intersections between an ellipse and a bounding box.
- * @param c
- * @param rx
- * @param ry
- * @param rotation
- * @param bounds
- */
- export function intersectEllipseBounds(
- c: number[],
- rx: number,
- ry: number,
- rotation: number,
- bounds: TLBounds
- ): TLIntersection[] {
- const { minX, minY, width, height } = bounds
- return intersectEllipseRectangle(c, rx, ry, rotation, [minX, minY], [width, height])
- }
-
- /**
- * Find the intersections between a bounding box and a ray.
- * @param bounds
- * @param origin
- * @param direction
- */
- export function intersectBoundsRay(
- bounds: TLBounds,
- origin: number[],
- direction: number[]
- ): TLIntersection[] {
- const { minX, minY, width, height } = bounds
- return intersectRayRectangle(origin, direction, [minX, minY], [width, height])
- }
-
- /**
- * Find the intersections between a bounding box and a line segment.
- * @param bounds
- * @param a1
- * @param a2
- */
- export function intersectBoundsLineSegment(
- bounds: TLBounds,
- a1: number[],
- a2: number[]
- ): TLIntersection[] {
- const { minX, minY, width, height } = bounds
- return intersectLineSegmentRectangle(a1, a2, [minX, minY], [width, height])
- }
-
- /**
- * Find the intersections between a bounding box and a rectangle.
- * @param bounds
- * @param point
- * @param size
- */
- export function intersectBoundsRectangle(
- bounds: TLBounds,
- point: number[],
- size: number[]
- ): TLIntersection[] {
- const { minX, minY, width, height } = bounds
- return intersectRectangleRectangle(point, size, [minX, minY], [width, height])
- }
-
- /**
- * Find the intersections between a bounding box and a bounding box.
- * @param bounds1
- * @param bounds2
- */
- export function intersectBoundsBounds(bounds1: TLBounds, bounds2: TLBounds): TLIntersection[] {
- return intersectRectangleRectangle(
- [bounds1.minX, bounds1.minY],
- [bounds1.width, bounds1.height],
- [bounds2.minX, bounds2.minY],
- [bounds2.width, bounds2.height]
- )
- }
-
- /**
- * Find the intersections between a bounding box and an arc.
- * @param bounds
- * @param center
- * @param radius
- * @param start
- * @param end
- */
- export function intersectBoundsArc(
- bounds: TLBounds,
- center: number[],
- radius: number,
- start: number[],
- end: number[]
- ): TLIntersection[] {
- const { minX, minY, width, height } = bounds
- return intersectArcRectangle(center, radius, start, end, [minX, minY], [width, height])
- }
-
- /**
- * Find the intersections between a bounding box and a circle.
- * @param bounds
- * @param c
- * @param r
- */
- export function intersectBoundsCircle(bounds: TLBounds, c: number[], r: number): TLIntersection[] {
- const { minX, minY, width, height } = bounds
- return intersectCircleRectangle(c, r, [minX, minY], [width, height])
- }
-
- /**
- * Find the intersections between a bounding box and an ellipse.
- * @param bounds
- * @param c
- * @param rx
- * @param ry
- * @param rotation
- */
- export function intersectBoundsEllipse(
- bounds: TLBounds,
- c: number[],
- rx: number,
- ry: number,
- rotation = 0
- ): TLIntersection[] {
- const { minX, minY, width, height } = bounds
- return intersectEllipseRectangle(c, rx, ry, rotation, [minX, minY], [width, height])
- }
-
- /**
- * Find the intersections between a bounding box and a polyline.
- * @param bounds
- * @param points
- */
- export function intersectBoundsPolyline(bounds: TLBounds, points: number[][]): TLIntersection[] {
- return intersectPolylineBounds(points, bounds)
- }
-
- /**
- * Find the intersections between a bounding box and a polygon.
- * @param bounds
- * @param points
- */
- export function intersectBoundsPolygon(bounds: TLBounds, points: number[][]): TLIntersection[] {
- return intersectPolygonBounds(points, bounds)
- }
-
- /* -------------------------------------------------- */
- /* Polyline */
- /* -------------------------------------------------- */
-
- /**
- * Find the intersections between a polyline and a line segment.
- * @param points
- * @param a1
- * @param a2
- */
- export function intersectPolylineLineSegment(
- points: number[][],
- a1: number[],
- a2: number[]
- ): TLIntersection {
- return intersectLineSegmentPolyline(a1, a2, points)
- }
-
- /**
- * Find the intersections between a polyline and a rectangle.
- * @param points
- * @param point
- * @param size
- */
- export function intersectPolylineRectangle(
- points: number[][],
- point: number[],
- size: number[]
- ): TLIntersection[] {
- return intersectRectanglePolyline(point, size, points)
- }
-
- /**
- * Find the intersections between a polyline and a bounding box.
- * @param points
- * @param bounds
- */
- export function intersectPolylineBounds(points: number[][], bounds: TLBounds): TLIntersection[] {
- return intersectRectanglePolyline(
- [bounds.minX, bounds.minY],
- [bounds.width, bounds.height],
- points
- )
- }
-
- /* -------------------------------------------------- */
- /* Polygon */
- /* -------------------------------------------------- */
-
- /**
- * Find the intersections between a polygon nd a line segment.
- * @param points
- * @param a1
- * @param a2
- */
- export function intersectPolygonLineSegment(
- points: number[][],
- a1: number[],
- a2: number[]
- ): TLIntersection {
- return intersectLineSegmentPolyline(a1, a2, points)
- }
-
- /**
- * Find the intersections between a polygon and a rectangle.
- * @param points
- * @param point
- * @param size
- */
- export function intersectPolygonRectangle(
- points: number[][],
- point: number[],
- size: number[]
- ): TLIntersection[] {
- return intersectRectanglePolyline(point, size, points)
- }
-
- /**
- * Find the intersections between a polygon and a bounding box.
- * @param points
- * @param bounds
- */
- export function intersectPolygonBounds(points: number[][], bounds: TLBounds): TLIntersection[] {
- return intersectRectanglePolygon(
- [bounds.minX, bounds.minY],
- [bounds.width, bounds.height],
- points
- )
- }
|