@itwin/core-geometry 5.8.0-dev.1 → 5.8.0-dev.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (144) hide show
  1. package/CHANGELOG.md +26 -1
  2. package/lib/cjs/Geometry.d.ts +24 -14
  3. package/lib/cjs/Geometry.d.ts.map +1 -1
  4. package/lib/cjs/Geometry.js +34 -14
  5. package/lib/cjs/Geometry.js.map +1 -1
  6. package/lib/cjs/core-geometry.d.ts +1 -0
  7. package/lib/cjs/core-geometry.d.ts.map +1 -1
  8. package/lib/cjs/core-geometry.js +1 -0
  9. package/lib/cjs/core-geometry.js.map +1 -1
  10. package/lib/cjs/curve/Arc3d.d.ts +4 -0
  11. package/lib/cjs/curve/Arc3d.d.ts.map +1 -1
  12. package/lib/cjs/curve/Arc3d.js +14 -0
  13. package/lib/cjs/curve/Arc3d.js.map +1 -1
  14. package/lib/cjs/curve/ConstrainedCurve2d.d.ts +149 -0
  15. package/lib/cjs/curve/ConstrainedCurve2d.d.ts.map +1 -0
  16. package/lib/cjs/curve/ConstrainedCurve2d.js +317 -0
  17. package/lib/cjs/curve/ConstrainedCurve2d.js.map +1 -0
  18. package/lib/cjs/curve/CurveFactory.d.ts.map +1 -1
  19. package/lib/cjs/curve/CurveFactory.js.map +1 -1
  20. package/lib/cjs/curve/CurvePrimitive.d.ts +2 -2
  21. package/lib/cjs/curve/CurvePrimitive.js +2 -2
  22. package/lib/cjs/curve/CurvePrimitive.js.map +1 -1
  23. package/lib/cjs/curve/internalContexts/PointToCurveTangentHandler.d.ts +26 -0
  24. package/lib/cjs/curve/internalContexts/PointToCurveTangentHandler.d.ts.map +1 -0
  25. package/lib/cjs/curve/internalContexts/PointToCurveTangentHandler.js +78 -0
  26. package/lib/cjs/curve/internalContexts/PointToCurveTangentHandler.js.map +1 -0
  27. package/lib/cjs/curve/internalContexts/geometry2d/ConstrainedImplicitCurve2d.d.ts +152 -0
  28. package/lib/cjs/curve/internalContexts/geometry2d/ConstrainedImplicitCurve2d.d.ts.map +1 -0
  29. package/lib/cjs/curve/internalContexts/geometry2d/ConstrainedImplicitCurve2d.js +843 -0
  30. package/lib/cjs/curve/internalContexts/geometry2d/ConstrainedImplicitCurve2d.js.map +1 -0
  31. package/lib/cjs/curve/internalContexts/geometry2d/ConstraintSet.d.ts +97 -0
  32. package/lib/cjs/curve/internalContexts/geometry2d/ConstraintSet.d.ts.map +1 -0
  33. package/lib/cjs/curve/internalContexts/geometry2d/ConstraintSet.js +300 -0
  34. package/lib/cjs/curve/internalContexts/geometry2d/ConstraintSet.js.map +1 -0
  35. package/lib/cjs/curve/internalContexts/geometry2d/ImplicitCurve2d.d.ts +105 -0
  36. package/lib/cjs/curve/internalContexts/geometry2d/ImplicitCurve2d.d.ts.map +1 -0
  37. package/lib/cjs/curve/internalContexts/geometry2d/ImplicitCurve2d.js +141 -0
  38. package/lib/cjs/curve/internalContexts/geometry2d/ImplicitCurve2d.js.map +1 -0
  39. package/lib/cjs/curve/internalContexts/geometry2d/ImplicitCurve2dConverter.d.ts +27 -0
  40. package/lib/cjs/curve/internalContexts/geometry2d/ImplicitCurve2dConverter.d.ts.map +1 -0
  41. package/lib/cjs/curve/internalContexts/geometry2d/ImplicitCurve2dConverter.js +152 -0
  42. package/lib/cjs/curve/internalContexts/geometry2d/ImplicitCurve2dConverter.js.map +1 -0
  43. package/lib/cjs/curve/internalContexts/geometry2d/UnboundedCircle2d.d.ts +81 -0
  44. package/lib/cjs/curve/internalContexts/geometry2d/UnboundedCircle2d.d.ts.map +1 -0
  45. package/lib/cjs/curve/internalContexts/geometry2d/UnboundedCircle2d.js +170 -0
  46. package/lib/cjs/curve/internalContexts/geometry2d/UnboundedCircle2d.js.map +1 -0
  47. package/lib/cjs/curve/internalContexts/geometry2d/UnboundedEllipse2d.d.ts +96 -0
  48. package/lib/cjs/curve/internalContexts/geometry2d/UnboundedEllipse2d.d.ts.map +1 -0
  49. package/lib/cjs/curve/internalContexts/geometry2d/UnboundedEllipse2d.js +187 -0
  50. package/lib/cjs/curve/internalContexts/geometry2d/UnboundedEllipse2d.js.map +1 -0
  51. package/lib/cjs/curve/internalContexts/geometry2d/UnboundedHyperbola2d.d.ts +91 -0
  52. package/lib/cjs/curve/internalContexts/geometry2d/UnboundedHyperbola2d.d.ts.map +1 -0
  53. package/lib/cjs/curve/internalContexts/geometry2d/UnboundedHyperbola2d.js +197 -0
  54. package/lib/cjs/curve/internalContexts/geometry2d/UnboundedHyperbola2d.js.map +1 -0
  55. package/lib/cjs/curve/internalContexts/geometry2d/UnboundedLine2d.d.ts +129 -0
  56. package/lib/cjs/curve/internalContexts/geometry2d/UnboundedLine2d.d.ts.map +1 -0
  57. package/lib/cjs/curve/internalContexts/geometry2d/UnboundedLine2d.js +208 -0
  58. package/lib/cjs/curve/internalContexts/geometry2d/UnboundedLine2d.js.map +1 -0
  59. package/lib/cjs/curve/internalContexts/geometry2d/UnboundedParabola2d.d.ts +87 -0
  60. package/lib/cjs/curve/internalContexts/geometry2d/UnboundedParabola2d.d.ts.map +1 -0
  61. package/lib/cjs/curve/internalContexts/geometry2d/UnboundedParabola2d.js +193 -0
  62. package/lib/cjs/curve/internalContexts/geometry2d/UnboundedParabola2d.js.map +1 -0
  63. package/lib/cjs/geometry3d/Point2dVector2d.d.ts +7 -0
  64. package/lib/cjs/geometry3d/Point2dVector2d.d.ts.map +1 -1
  65. package/lib/cjs/geometry3d/Point2dVector2d.js +9 -0
  66. package/lib/cjs/geometry3d/Point2dVector2d.js.map +1 -1
  67. package/lib/cjs/numerics/Polynomials.d.ts.map +1 -1
  68. package/lib/cjs/numerics/Polynomials.js +8 -6
  69. package/lib/cjs/numerics/Polynomials.js.map +1 -1
  70. package/lib/cjs/topology/HalfEdgeGraphSpineContext.d.ts.map +1 -1
  71. package/lib/cjs/topology/HalfEdgeGraphSpineContext.js +3 -1
  72. package/lib/cjs/topology/HalfEdgeGraphSpineContext.js.map +1 -1
  73. package/lib/esm/Geometry.d.ts +24 -14
  74. package/lib/esm/Geometry.d.ts.map +1 -1
  75. package/lib/esm/Geometry.js +34 -14
  76. package/lib/esm/Geometry.js.map +1 -1
  77. package/lib/esm/core-geometry.d.ts +1 -0
  78. package/lib/esm/core-geometry.d.ts.map +1 -1
  79. package/lib/esm/core-geometry.js +1 -0
  80. package/lib/esm/core-geometry.js.map +1 -1
  81. package/lib/esm/curve/Arc3d.d.ts +4 -0
  82. package/lib/esm/curve/Arc3d.d.ts.map +1 -1
  83. package/lib/esm/curve/Arc3d.js +14 -0
  84. package/lib/esm/curve/Arc3d.js.map +1 -1
  85. package/lib/esm/curve/ConstrainedCurve2d.d.ts +149 -0
  86. package/lib/esm/curve/ConstrainedCurve2d.d.ts.map +1 -0
  87. package/lib/esm/curve/ConstrainedCurve2d.js +313 -0
  88. package/lib/esm/curve/ConstrainedCurve2d.js.map +1 -0
  89. package/lib/esm/curve/CurveFactory.d.ts.map +1 -1
  90. package/lib/esm/curve/CurveFactory.js.map +1 -1
  91. package/lib/esm/curve/CurvePrimitive.d.ts +2 -2
  92. package/lib/esm/curve/CurvePrimitive.js +2 -2
  93. package/lib/esm/curve/CurvePrimitive.js.map +1 -1
  94. package/lib/esm/curve/internalContexts/PointToCurveTangentHandler.d.ts +26 -0
  95. package/lib/esm/curve/internalContexts/PointToCurveTangentHandler.d.ts.map +1 -0
  96. package/lib/esm/curve/internalContexts/PointToCurveTangentHandler.js +74 -0
  97. package/lib/esm/curve/internalContexts/PointToCurveTangentHandler.js.map +1 -0
  98. package/lib/esm/curve/internalContexts/geometry2d/ConstrainedImplicitCurve2d.d.ts +152 -0
  99. package/lib/esm/curve/internalContexts/geometry2d/ConstrainedImplicitCurve2d.d.ts.map +1 -0
  100. package/lib/esm/curve/internalContexts/geometry2d/ConstrainedImplicitCurve2d.js +839 -0
  101. package/lib/esm/curve/internalContexts/geometry2d/ConstrainedImplicitCurve2d.js.map +1 -0
  102. package/lib/esm/curve/internalContexts/geometry2d/ConstraintSet.d.ts +97 -0
  103. package/lib/esm/curve/internalContexts/geometry2d/ConstraintSet.d.ts.map +1 -0
  104. package/lib/esm/curve/internalContexts/geometry2d/ConstraintSet.js +295 -0
  105. package/lib/esm/curve/internalContexts/geometry2d/ConstraintSet.js.map +1 -0
  106. package/lib/esm/curve/internalContexts/geometry2d/ImplicitCurve2d.d.ts +105 -0
  107. package/lib/esm/curve/internalContexts/geometry2d/ImplicitCurve2d.d.ts.map +1 -0
  108. package/lib/esm/curve/internalContexts/geometry2d/ImplicitCurve2d.js +135 -0
  109. package/lib/esm/curve/internalContexts/geometry2d/ImplicitCurve2d.js.map +1 -0
  110. package/lib/esm/curve/internalContexts/geometry2d/ImplicitCurve2dConverter.d.ts +27 -0
  111. package/lib/esm/curve/internalContexts/geometry2d/ImplicitCurve2dConverter.d.ts.map +1 -0
  112. package/lib/esm/curve/internalContexts/geometry2d/ImplicitCurve2dConverter.js +148 -0
  113. package/lib/esm/curve/internalContexts/geometry2d/ImplicitCurve2dConverter.js.map +1 -0
  114. package/lib/esm/curve/internalContexts/geometry2d/UnboundedCircle2d.d.ts +81 -0
  115. package/lib/esm/curve/internalContexts/geometry2d/UnboundedCircle2d.d.ts.map +1 -0
  116. package/lib/esm/curve/internalContexts/geometry2d/UnboundedCircle2d.js +166 -0
  117. package/lib/esm/curve/internalContexts/geometry2d/UnboundedCircle2d.js.map +1 -0
  118. package/lib/esm/curve/internalContexts/geometry2d/UnboundedEllipse2d.d.ts +96 -0
  119. package/lib/esm/curve/internalContexts/geometry2d/UnboundedEllipse2d.d.ts.map +1 -0
  120. package/lib/esm/curve/internalContexts/geometry2d/UnboundedEllipse2d.js +183 -0
  121. package/lib/esm/curve/internalContexts/geometry2d/UnboundedEllipse2d.js.map +1 -0
  122. package/lib/esm/curve/internalContexts/geometry2d/UnboundedHyperbola2d.d.ts +91 -0
  123. package/lib/esm/curve/internalContexts/geometry2d/UnboundedHyperbola2d.d.ts.map +1 -0
  124. package/lib/esm/curve/internalContexts/geometry2d/UnboundedHyperbola2d.js +193 -0
  125. package/lib/esm/curve/internalContexts/geometry2d/UnboundedHyperbola2d.js.map +1 -0
  126. package/lib/esm/curve/internalContexts/geometry2d/UnboundedLine2d.d.ts +129 -0
  127. package/lib/esm/curve/internalContexts/geometry2d/UnboundedLine2d.d.ts.map +1 -0
  128. package/lib/esm/curve/internalContexts/geometry2d/UnboundedLine2d.js +204 -0
  129. package/lib/esm/curve/internalContexts/geometry2d/UnboundedLine2d.js.map +1 -0
  130. package/lib/esm/curve/internalContexts/geometry2d/UnboundedParabola2d.d.ts +87 -0
  131. package/lib/esm/curve/internalContexts/geometry2d/UnboundedParabola2d.d.ts.map +1 -0
  132. package/lib/esm/curve/internalContexts/geometry2d/UnboundedParabola2d.js +189 -0
  133. package/lib/esm/curve/internalContexts/geometry2d/UnboundedParabola2d.js.map +1 -0
  134. package/lib/esm/geometry3d/Point2dVector2d.d.ts +7 -0
  135. package/lib/esm/geometry3d/Point2dVector2d.d.ts.map +1 -1
  136. package/lib/esm/geometry3d/Point2dVector2d.js +9 -0
  137. package/lib/esm/geometry3d/Point2dVector2d.js.map +1 -1
  138. package/lib/esm/numerics/Polynomials.d.ts.map +1 -1
  139. package/lib/esm/numerics/Polynomials.js +8 -6
  140. package/lib/esm/numerics/Polynomials.js.map +1 -1
  141. package/lib/esm/topology/HalfEdgeGraphSpineContext.d.ts.map +1 -1
  142. package/lib/esm/topology/HalfEdgeGraphSpineContext.js +3 -1
  143. package/lib/esm/topology/HalfEdgeGraphSpineContext.js.map +1 -1
  144. package/package.json +3 -3
@@ -0,0 +1,839 @@
1
+ /*---------------------------------------------------------------------------------------------
2
+ * Copyright (c) Bentley Systems, Incorporated. All rights reserved.
3
+ * See LICENSE.md in the project root for license terms and full copyright notice.
4
+ *--------------------------------------------------------------------------------------------*/
5
+ /** @packageDocumentation
6
+ * @module CartesianGeometry
7
+ */
8
+ import { Geometry } from "../../../Geometry";
9
+ import { Matrix3d } from "../../../geometry3d/Matrix3d";
10
+ import { Point2d, Vector2d } from "../../../geometry3d/Point2dVector2d";
11
+ import { Vector3d } from "../../../geometry3d/Point3dVector3d";
12
+ import { Degree2PowerPolynomial } from "../../../numerics/Polynomials";
13
+ import { SmallSystem } from "../../../numerics/SmallSystem";
14
+ import { ImplicitGeometryMarkup, Point2dImplicitCurve2d } from "./ImplicitCurve2d";
15
+ import { UnboundedCircle2dByCenterAndRadius } from "./UnboundedCircle2d";
16
+ import { UnboundedEllipse2d } from "./UnboundedEllipse2d";
17
+ import { UnboundedHyperbola2d } from "./UnboundedHyperbola2d";
18
+ import { UnboundedLine2dByPointAndNormal } from "./UnboundedLine2d";
19
+ import { UnboundedParabola2d } from "./UnboundedParabola2d";
20
+ /**
21
+ * Static methods for special case circle and line tangent constructions.
22
+ * @internal
23
+ */
24
+ export class ConstrainedImplicitCurve2d {
25
+ /**
26
+ * Return all (i.e., up to 4) circles that are tangent to 3 given lines.
27
+ * @param lineA first line
28
+ * @param lineB second line
29
+ * @param lineC third line
30
+ */
31
+ static circlesTangentLLL(lineA, lineB, lineC) {
32
+ /*--------------------------------------------------------------------------------------
33
+ (X-A).normalA = a*r
34
+ (X-B).normalB = b*r
35
+ (X-C).normalC = c*r
36
+ Typical expansion is
37
+ x*mx + y*my - a*r = A.normalA
38
+ Where a,b,c are combinations of {+1,-1}
39
+ Need to consider 4 combinations of signs: (+++) (++-) (+-+) (+--) The other 4 generate negated r as solution.
40
+ ----------------------------------------------------------------------------------------*/
41
+ // make lines with normal vectors and nearby origin
42
+ const origin = lineA.point;
43
+ const lineA1 = lineA.cloneNormalizedFromOrigin(origin);
44
+ const lineB1 = lineB.cloneNormalizedFromOrigin(origin);
45
+ const lineC1 = lineC.cloneNormalizedFromOrigin(origin);
46
+ if (lineA1 === undefined || lineB1 === undefined || lineC1 === undefined)
47
+ return undefined;
48
+ const result = [];
49
+ const a = lineA1.normal.dotProduct(lineA1.point);
50
+ const b = lineB1.normal.dotProduct(lineB1.point);
51
+ const c = lineC1.normal.dotProduct(lineC1.point);
52
+ const signA = 1;
53
+ for (const signB of [-1, 1]) {
54
+ for (const signC of [-1, 1]) {
55
+ const matrix = Matrix3d.createRowValues(lineA1.normal.x, lineA1.normal.y, signA, lineB1.normal.x, lineB1.normal.y, signB, lineC1.normal.x, lineC1.normal.y, signC);
56
+ const rhs = Vector3d.create(a, b, c);
57
+ const xyr = matrix.multiplyInverse(rhs);
58
+ if (xyr !== undefined) {
59
+ const circle = UnboundedCircle2dByCenterAndRadius.createXYRadius(origin.x + xyr.x, origin.y + xyr.y, Math.abs(xyr.z));
60
+ const markup = new ImplicitGeometryMarkup(circle);
61
+ markup.appendClosePoint(circle.center, lineA, circle.center, circle.radius);
62
+ markup.appendClosePoint(circle.center, lineB, circle.center, circle.radius);
63
+ markup.appendClosePoint(circle.center, lineC, circle.center, circle.radius);
64
+ result.push(markup);
65
+ }
66
+ }
67
+ }
68
+ return result;
69
+ }
70
+ /**
71
+ * Return all (i.e., up to 2) unbounded lines perpendicular to a line and tangent to a circle.
72
+ * @param line the line
73
+ * @param circle the circle
74
+ */
75
+ static linesPerpLTangentC(line, circle) {
76
+ // Project the circle center to the line.
77
+ // The endpoints of the perpendicular line are the circle center and that line point shifted
78
+ // by positive and negative radius in the direction of the line.
79
+ const lineTangent = line.unitVectorAlongLine();
80
+ const unitNormal = line.unitNormal();
81
+ const linePoint = line.closestPoint(circle.center);
82
+ if (linePoint === undefined || lineTangent === undefined || unitNormal === undefined)
83
+ return undefined;
84
+ const unitPerp = unitNormal.rotate90CCWXY();
85
+ const result = [];
86
+ for (const r of signedValues(circle.radius)) {
87
+ const pointA = linePoint.plusScaled(lineTangent, r);
88
+ const pointB = circle.center.plusScaled(lineTangent, r);
89
+ const lineC = UnboundedLine2dByPointAndNormal.createPointNormal(pointA, unitPerp);
90
+ const taggedLine = new ImplicitGeometryMarkup(lineC);
91
+ taggedLine.data.push(new Point2dImplicitCurve2d(pointA, line)); // CLONE
92
+ taggedLine.data.push(new Point2dImplicitCurve2d(pointB, circle)); // CLONE
93
+ result.push(taggedLine);
94
+ }
95
+ return result;
96
+ }
97
+ /**
98
+ * Return all (i.e., 4) variants of the line perpendicular to 2 circles (line between centers, with ends at crossing
99
+ * points on respective circles).
100
+ * @param circleA first circle
101
+ * @param circleB second circle
102
+ */
103
+ static linesPerpCPerpC(circleA, circleB) {
104
+ // There infinite line containing the circle centers is the only line perp to both circles.
105
+ // The endpoints for the 4 line segments are the centers shifted by the respective radii along the infinite line.
106
+ const centerToCenter = Vector2d.createStartEnd(circleA.center, circleB.center);
107
+ const unitCenterToCenter = centerToCenter.normalize();
108
+ if (unitCenterToCenter === undefined)
109
+ return undefined;
110
+ const unitNormal = unitCenterToCenter.rotate90CCWXY();
111
+ const result = [];
112
+ for (const rA of signedValues(circleA.radius)) {
113
+ for (const rB of signedValues(circleB.radius)) {
114
+ const pointA = circleA.center.plusScaled(unitCenterToCenter, rA);
115
+ const pointB = circleB.center.plusScaled(unitCenterToCenter, rB);
116
+ const lineC = UnboundedLine2dByPointAndNormal.createPointNormal(pointA, unitNormal);
117
+ const taggedLine = new ImplicitGeometryMarkup(lineC);
118
+ taggedLine.data.push(new Point2dImplicitCurve2d(pointA, circleA)); // CLONE
119
+ taggedLine.data.push(new Point2dImplicitCurve2d(pointB, circleB)); // CLONE
120
+ result.push(taggedLine);
121
+ }
122
+ }
123
+ return result;
124
+ }
125
+ /**
126
+ * Return all (i.e., up to 2) unbounded lines perpendicular to a line and a circle.
127
+ * @param line the line
128
+ * @param circle the circle
129
+ */
130
+ static linesPerpLPerpC(line, circle) {
131
+ // The infinite line through the circle center and in the direction of the line normal is the containing line.
132
+ // Make segments from its intersection with the line to the near and far intersections with the circle.
133
+ // (these are the centers shifted by radius along the line)
134
+ const lineTangent = line.unitVectorAlongLine();
135
+ const unitNormal = line.unitNormal();
136
+ const linePoint = line.closestPoint(circle.center);
137
+ if (linePoint === undefined || lineTangent === undefined || unitNormal === undefined)
138
+ return undefined;
139
+ const unitPerp = unitNormal.rotate90CCWXY();
140
+ const result = [];
141
+ for (const r of signedValues(circle.radius)) {
142
+ const pointA = linePoint.clone();
143
+ const pointB = circle.center.plusScaled(unitNormal, r);
144
+ const lineC = UnboundedLine2dByPointAndNormal.createPointNormal(pointA, unitPerp);
145
+ const taggedLine = new ImplicitGeometryMarkup(lineC);
146
+ taggedLine.data.push(new Point2dImplicitCurve2d(pointA, line)); // CLONE!
147
+ taggedLine.data.push(new Point2dImplicitCurve2d(pointB, circle)); // CLONE!
148
+ result.push(taggedLine);
149
+ }
150
+ return result;
151
+ }
152
+ /**
153
+ * Return all (i.e., up to 4) unbounded lines perpendicular to a circle and tangent to a circle.
154
+ * Note that multiple colinear lines are returned tagged with diametrically opposing points of circleA.
155
+ * @param circleA first circle (for perpendicular constraint)
156
+ * @param circleB second circle (for tangent constraint)
157
+ */
158
+ static linesPerpCTangentC(circleA, circleB) {
159
+ // The solution lines are through the perp circle center and tangent to the tangent circle.
160
+ // Hence this is the two infinite lines "through first circle center, tangent to second circle",
161
+ // Bounded line segments go from the tangent circle points to the near and far intersections with the perp circle.
162
+ const centerToCenter = Vector2d.createStartEnd(circleA.center, circleB.center);
163
+ const centerToCenterDistance = centerToCenter.magnitude();
164
+ const unitCenterToCenter = centerToCenter.normalize();
165
+ if (unitCenterToCenter === undefined)
166
+ return undefined;
167
+ const centerToCenterNormal = unitCenterToCenter.rotate90CCWXY();
168
+ const sine = Geometry.safeDivideFraction(circleB.radius, centerToCenterDistance, 0.0);
169
+ if (sine > 1.0)
170
+ return undefined;
171
+ const absoluteCosine = Math.sqrt(1.0 - sine * sine);
172
+ const result = [];
173
+ for (const rA of signedValues(circleA.radius)) {
174
+ for (const rB of signedValues(circleB.radius)) {
175
+ const cosine = rB > 0 ? absoluteCosine : -absoluteCosine;
176
+ const lineNormal = Vector2d.createAdd2Scaled(unitCenterToCenter, -sine, centerToCenterNormal, cosine);
177
+ const lineDirection = Vector2d.createAdd2Scaled(unitCenterToCenter, cosine, centerToCenterNormal, sine);
178
+ const pointA = circleA.center.plusScaled(lineDirection, rA);
179
+ const pointB = circleB.center.plusScaled(lineNormal, Math.abs(rB));
180
+ const lineC = UnboundedLine2dByPointAndNormal.createPointNormal(pointA, lineNormal);
181
+ const taggedLine = new ImplicitGeometryMarkup(lineC);
182
+ taggedLine.data.push(new Point2dImplicitCurve2d(pointA, circleA)); // CLONE
183
+ taggedLine.data.push(new Point2dImplicitCurve2d(pointB, circleB)); // CLONE
184
+ result.push(taggedLine);
185
+ }
186
+ }
187
+ return result;
188
+ }
189
+ /**
190
+ * Return all (i.e., up to 4) unbounded lines tangent to 2 circles.
191
+ * * There are 4 lines if there is neither intersection nor containment between the circles
192
+ * * There are 2 lines if the circles intersect
193
+ * * THere are no lines if the one circle is entirely inside the other.
194
+ * @param circleA first circle
195
+ * @param circleB second circle
196
+ */
197
+ static linesTangentCC(circleA, circleB) {
198
+ // draw a line tangent to both circles. This may have both circles on the same side,
199
+ // or one circle on each side.
200
+ // draw radii from both centers to the tangencies. (These are parallel to each other)
201
+ // the (sum or difference of the radii) and the (distance between centers) are side and hypotenuse of
202
+ // // a right triangle, with the tangent-to-tangent segment as the other side.
203
+ // The lengths give sine and cosine of angles in the triangle, and those give vectors
204
+ // from center to tangency points.
205
+ const distanceAB = circleA.center.distance(circleB.center);
206
+ if (distanceAB + Math.abs(circleB.radius) <= Math.abs(circleA.radius))
207
+ return undefined;
208
+ if (distanceAB + Math.abs(circleA.radius) <= Math.abs(circleB.radius))
209
+ return undefined;
210
+ const unitAB = Vector2d.createStartEnd(circleA.center, circleB.center);
211
+ if (!unitAB.normalize(unitAB))
212
+ return undefined;
213
+ const result = [];
214
+ if (unitAB !== undefined) {
215
+ const radiusA = circleA.radius;
216
+ const radiusB = circleB.radius;
217
+ for (const signQ of [-1, 1]) {
218
+ const q = radiusB + signQ * radiusA;
219
+ const sine = q / distanceAB;
220
+ if (Math.abs(sine) < 1.0) {
221
+ const cosine = Math.sqrt(1.0 - sine * sine);
222
+ for (const sign of [-1, 1]) {
223
+ let pointA, pointB;
224
+ if (signQ < 0) {
225
+ pointA = circleA.center.addForwardLeft(radiusA * -sine, radiusA * sign * cosine, unitAB);
226
+ pointB = circleB.center.addForwardLeft(radiusB * -sine, radiusB * sign * cosine, unitAB);
227
+ }
228
+ else {
229
+ pointA = circleA.center.addForwardLeft(radiusA * sine, radiusA * sign * cosine, unitAB);
230
+ pointB = circleB.center.addForwardLeft(-radiusB * sine, -radiusB * sign * cosine, unitAB);
231
+ }
232
+ const lineA = UnboundedLine2dByPointAndNormal.createPointPoint(pointA, pointB);
233
+ if (lineA === undefined)
234
+ return undefined;
235
+ const taggedLine = new ImplicitGeometryMarkup(lineA);
236
+ taggedLine.data.push(new Point2dImplicitCurve2d(pointA, circleA)); // CLONE
237
+ taggedLine.data.push(new Point2dImplicitCurve2d(pointB, circleB)); // CLONE
238
+ result.push(taggedLine);
239
+ }
240
+ }
241
+ }
242
+ }
243
+ return result;
244
+ }
245
+ /**
246
+ * Return all (i.e., up to 8) circles tangent to two lines and a circle.
247
+ * * There are 8 circles if the circle contains the intersection of the lines.
248
+ * * There are 2 circles if the circle is completely contained in one quadrant bounded by the two lines.
249
+ * * There are 2 circles if the circle intersects one ray outward from the intersection.
250
+ * * There are 4 circles if the circle intersects two of the outward rays.
251
+ */
252
+ static circlesTangentLLC(lineA, lineB, circle) {
253
+ /*--------------------------------------------------------------------------------------
254
+ Put origin at circle center.
255
+ A,B are line points.
256
+ M,N are line normals.
257
+ (X).(X) = (a +- r)^2 = 0
258
+ (X-A).M = +-r
259
+ (X-B).N = +-r
260
+ Need to consider 4 combinations of signs: (+++) (++-) (-++) (-+-) The other 4 are negations.
261
+ Write the linear part as
262
+ [M N]^ * X = [M.A N.B]^ + r Ei
263
+ where Ei is one of E0=[1 1]^ or E1=[1 -1]^
264
+ Multiply by inverse of matrix X = F + r G
265
+ The quadratic part is
266
+ (F + rG).(F + rG) = (a +- r)^2
267
+ (F + rG).(F + rG) = a^2 +- 2ar + r^2
268
+ F.F + 2r G.F + r^2 G.G = a^2 +- 2ar + r^2
269
+ r^2 (1-G.G) + 2(+-a - G.F)r + a^2-F.F = 0.
270
+ Solve with positive, negative branch. Each generates 2 solutions to go back through Ei.
271
+ ----------------------------------------------------------------------------------------*/
272
+ // lines with unit vector and point coordinates from circle center
273
+ const lineA1 = lineA.cloneNormalizedFromOrigin(circle.center);
274
+ const lineB1 = lineB.cloneNormalizedFromOrigin(circle.center);
275
+ if (lineA1 === undefined || lineB1 === undefined)
276
+ return undefined;
277
+ // perpendicular distance from each line to center of circle
278
+ const dotMA = lineA1.normal.dotProduct(lineA1.point);
279
+ const dotMB = lineB1.normal.dotProduct(lineB1.point);
280
+ const a = circle.radius;
281
+ const vectorF = Vector2d.create(); // vector from circle center to intersection of the lines
282
+ if (lineA1.normal.isParallelTo(lineB1.normal)
283
+ || !SmallSystem.linearSystem2d(lineA1.normal.x, lineA1.normal.y, lineB1.normal.x, lineB1.normal.y, dotMA, dotMB, vectorF)) {
284
+ // SPECIAL CASE: PARALLEL LINES
285
+ // Lines are parallel
286
+ // Make a midline. Half of line separation is the tangent circle radius.
287
+ const midLinePoint = lineA1.point.interpolate(0.5, lineB1.point);
288
+ const lineDirection = lineA1.normal.rotate90CCWXY();
289
+ const a1 = lineA1.functionValue(midLinePoint);
290
+ // vector from centerC to midline point midline point is vectorCP + s * lineDirectionA
291
+ const coffSine2 = lineDirection.dotProduct(lineDirection);
292
+ const coffSine = 2.0 * lineDirection.dotProduct(midLinePoint);
293
+ const coffConstant = Geometry.hypotenuseSquaredXY(midLinePoint.x, midLinePoint.y);
294
+ const targetRadius = [];
295
+ // special case zero radius
296
+ if (!Geometry.isSmallMetricDistance(circle.radius)) {
297
+ targetRadius.push(circle.radius + a1);
298
+ targetRadius.push(circle.radius - a1);
299
+ }
300
+ else {
301
+ // circle is just a point -- the quadratic solver only has to happen once
302
+ targetRadius.push(circle.radius + a1);
303
+ // But if the center is ON one of the lines, construct by projecting to the other.
304
+ // REMARK: In C++ code this pointOnLine test was done with messy
305
+ // tolerance relative to the various line points' distance to origin.
306
+ // Here we trust metric distance condition.
307
+ let center;
308
+ if (Geometry.isSmallMetricDistance(dotMA)) // tangency is on lineA; move towards line B
309
+ center = circle.center.plusScaled(lineA1.normal, dotMB > 0.0 ? -a1 : a1);
310
+ else if (Geometry.isSmallMetricDistance(dotMB)) // tangency is on lineB; move towards line A
311
+ center = circle.center.plusScaled(lineB1.normal, dotMA > 0.0 ? a1 : -a1);
312
+ if (center !== undefined) {
313
+ const newCircle = UnboundedCircle2dByCenterAndRadius.createPointRadius(center, a1);
314
+ const markup = new ImplicitGeometryMarkup(newCircle);
315
+ markup.closePointsOfGeometry(newCircle.center, newCircle.center, newCircle.radius, [lineA, lineB, circle]);
316
+ return [markup];
317
+ }
318
+ }
319
+ const resultA = [];
320
+ for (const radius of targetRadius) {
321
+ const roots = Degree2PowerPolynomial.solveQuadratic(coffSine2, coffSine, coffConstant - radius * radius);
322
+ if (roots) {
323
+ for (const alpha of roots) {
324
+ const center = circle.center.plus2Scaled(midLinePoint, 1.0, lineDirection, alpha);
325
+ const newCircle = UnboundedCircle2dByCenterAndRadius.createPointRadius(center, a1);
326
+ const markup = new ImplicitGeometryMarkup(newCircle);
327
+ markup.closePointsOfGeometry(newCircle.center, newCircle.center, newCircle.radius, [lineA, lineB, circle]);
328
+ resultA.push(markup);
329
+ }
330
+ }
331
+ }
332
+ return resultA;
333
+ }
334
+ const result = [];
335
+ for (const sign1 of [-1, 1]) {
336
+ const vectorG = Vector2d.create();
337
+ if (SmallSystem.linearSystem2d(lineA1.normal.x, lineA1.normal.y, lineB1.normal.x, lineB1.normal.y, 1.0, sign1, vectorG)) {
338
+ for (const sign2 of [-1, 1]) {
339
+ const coffA = 1.0 - vectorG.dotProduct(vectorG);
340
+ const coffB = 2.0 * (sign2 * a - vectorG.dotProduct(vectorF));
341
+ const coffC = a * a - vectorF.dotProduct(vectorF);
342
+ const roots = Degree2PowerPolynomial.solveQuadratic(coffA, coffB, coffC);
343
+ if (roots !== undefined) {
344
+ for (const r of roots) {
345
+ const center = circle.center.plus2Scaled(vectorF, 1.0, vectorG, r);
346
+ const newCircle = UnboundedCircle2dByCenterAndRadius.createPointRadius(center, r);
347
+ if (!isThisCirclePresent(result, newCircle)) {
348
+ const markup = new ImplicitGeometryMarkup(newCircle);
349
+ markup.appendClosePoint(newCircle.center, lineA, newCircle.center, newCircle.radius);
350
+ markup.appendClosePoint(newCircle.center, lineB, newCircle.center, newCircle.radius);
351
+ markup.appendClosePoint(newCircle.center, circle, newCircle.center, newCircle.radius);
352
+ result.push(markup);
353
+ }
354
+ }
355
+ }
356
+ }
357
+ }
358
+ }
359
+ return result;
360
+ }
361
+ /**
362
+ * Return all (i.e., up to 4) circles tangent to 2 circles and a line.
363
+ * @param circleA first circle
364
+ * @param circleB second circle
365
+ * @param line the line
366
+ */
367
+ static circlesTangentCCL(circleA, circleB, line) {
368
+ /*--------------------------------------------------------------------------------------
369
+ (x-xA)^2 + (y-y0)^2 - (r + r0)^2 = 0
370
+ (x-xB)^2 + (y-y1)^2 - (r + r1)^2 = 0
371
+ y = r NOTE: Cannot arbitrarily change sign of r as is done in 3-circle case.
372
+ -------------------------------------------
373
+ x^2 -2 xA x + xA^2 -2 y0 y + y0^2 - 2 r0 y - r0^2 = 0
374
+ x^2 -2 xB x + xB^2 -2 y1 y + y1^2 - 2 r1 y - r1^2 = 0
375
+ -------------------------------------------
376
+ x^2 -2 xA x + -2 (y0 + r0) y + a0= 0 a0 = xA^2 + y0^2 - r0^2
377
+ x^2 -2 xB x + -2 (y1 + r1) y + a1= 0 a1 = xB^2 + y1^2 - r1^2
378
+ -------------------------------------------
379
+ (A0) x^2 + b0 x + c0 y + a0= 0 b0 = -2x0 c0 = -2 (y0 + r0)
380
+ (A1) x^2 + b1 x + c1 y + a1= 0 b1 = -2x1 c1 = -2 (y1 + r1)
381
+ -------------------------------------------
382
+ c1 x^2 c1 c0 y + c1 a0= 0
383
+ c0 x^2 + c0 b1 x + c0 c1 y + c0 a1= 0
384
+ ---------------------------------------
385
+ Subtract first from second.
386
+ (c0 - c1) x^2 + (c0 b1 - c1 b0) x + (c0 a1 - c1 a0) = 0.
387
+ Solve for two x values. Substitute in with largest c0, c1.
388
+ ---------------------------------------
389
+ Repeat with signed combinations of the circle radii.
390
+ ----------------------------------------------------------------------------------------*/
391
+ const lineUnitNormal = line.normal.normalize();
392
+ if (lineUnitNormal === undefined)
393
+ return undefined;
394
+ const lineUnitAlong = lineUnitNormal.rotate90CCWXY();
395
+ const circleGlobalOffsets = [
396
+ Vector2d.createStartEnd(line.point, circleA.center),
397
+ Vector2d.createStartEnd(line.point, circleB.center),
398
+ ];
399
+ const circleLocalOffset = [];
400
+ for (const i of [0, 1]) {
401
+ circleLocalOffset.push(Vector2d.create(circleGlobalOffsets[i].dotProduct(lineUnitAlong), circleGlobalOffsets[i].dotProduct(lineUnitNormal)));
402
+ }
403
+ const coffA = [0, 0];
404
+ const coffB = [0, 0];
405
+ const coffC = [0, 0];
406
+ const circleRadius = [0, 0];
407
+ const result = [];
408
+ const radiiFromA = signedValues(circleA.radius, circleA.radius);
409
+ const radiiFromB = signedValues(circleB.radius, circleB.radius);
410
+ for (const radiusA of radiiFromA) {
411
+ circleRadius[0] = radiusA;
412
+ for (const radiusB of radiiFromB) {
413
+ circleRadius[1] = radiusB;
414
+ for (const i of [0, 1]) {
415
+ const r = circleRadius[i];
416
+ coffA[i] = circleLocalOffset[i].magnitudeSquared() - r * r;
417
+ coffB[i] = -2.0 * circleLocalOffset[i].x;
418
+ coffC[i] = -2.0 * (circleLocalOffset[i].y + r);
419
+ }
420
+ const k = Math.abs(circleRadius[0]) > Math.abs(circleRadius[1])
421
+ ? 0 : 1;
422
+ const qa = coffC[0] - coffC[1];
423
+ const qb = coffC[0] * coffB[1] - coffC[1] * coffB[0];
424
+ const qc = coffC[0] * coffA[1] - coffC[1] * coffA[0];
425
+ const xRoot = Degree2PowerPolynomial.solveQuadratic(qa, qb, qc);
426
+ if (xRoot !== undefined) {
427
+ for (const x of xRoot) {
428
+ const y = Geometry.conditionalDivideCoordinate(x * x + coffB[k] * x + coffA[k], -coffC[k]);
429
+ if (y !== undefined) {
430
+ const r = Math.abs(y);
431
+ const center = line.point.plus2Scaled(lineUnitAlong, x, lineUnitNormal, y);
432
+ const newCircle = UnboundedCircle2dByCenterAndRadius.createPointRadius(center, r);
433
+ if (!isThisCirclePresent(result, newCircle)) {
434
+ const markup = new ImplicitGeometryMarkup(newCircle);
435
+ markup.appendClosePoint(newCircle.center, circleA, newCircle.center, r);
436
+ markup.appendClosePoint(newCircle.center, circleB, newCircle.center, r);
437
+ markup.appendClosePoint(newCircle.center, line, newCircle.center, r);
438
+ result.push(markup);
439
+ }
440
+ }
441
+ }
442
+ }
443
+ }
444
+ }
445
+ return result;
446
+ }
447
+ /**
448
+ * Special case CCC tangent construction when inputs are colinear.
449
+ * @param circleA first input circle
450
+ * @param circleB second input circle
451
+ * @param circleC third input circle
452
+ * @param rA signed radius for circleA
453
+ * @param rB signed radius for circleB
454
+ * @param rC signed radius for circleC
455
+ * @param result pre-initialized array to which tangent circle markup will be added.
456
+ */
457
+ static solveColinearCCCTangents(circleA, circleB, circleC, rA, rB, rC, result) {
458
+ const vectorAB = Vector2d.createStartEnd(circleA.center, circleB.center);
459
+ const vectorAC = Vector2d.createStartEnd(circleA.center, circleC.center);
460
+ const xB = vectorAB.magnitude();
461
+ let xC = vectorAC.magnitude();
462
+ if (vectorAB.dotProduct(vectorAC) < 0)
463
+ xC = -xC;
464
+ const unitAB = vectorAB.normalize();
465
+ if (unitAB === undefined)
466
+ return; // there are circle-in-circle cases with common centers
467
+ const unitPerp = unitAB.rotate90CCWXY();
468
+ /*
469
+ Measuring from center A in rotated system:
470
+ x^2 + y^2 = (r+rA)^2
471
+ (x-xB)^2 + y^2 = (r+rB)^2
472
+ (x-xC)^2 + y^2 = (r+rC)^2
473
+ -------------------------
474
+ x^2 + y^2 = (r+rA)^2
475
+ -2xB x + xB^2 = (r+rB)^2 - (r+rA)^2
476
+ -2xC x + xC^2 = (r+rC)^2 - (r+rA)^2
477
+ -------------------------
478
+ x^2 + y^2 = (r+rA)^2
479
+ -2xB x = 2 (rB-rA) r + rB^2 - rA^2 - xB^2
480
+ -2x2 x = 2 (rC-rA) r + rC^2 - rA^2 - xC^2
481
+ -------------------------
482
+ Solve
483
+ -2xB x - 2 (rB-rA) r = rB^2 - rA^2 - xB^2
484
+ -2xC x - 2 (rC-rA) r = rC^2 - rA^2 - xC^2
485
+ */
486
+ const ax = -2.0 * xB;
487
+ const ar = -2.0 * (rB - rA);
488
+ const a = rB * rB - rA * rA - xB * xB;
489
+ const bx = -2.0 * xC;
490
+ const br = -2.0 * (rC - rA);
491
+ const b = rC * rC - rA * rA - xC * xC;
492
+ const sRelTol = 1.0e-14;
493
+ const origin = Point2d.create(circleA.center.x, circleA.center.y);
494
+ const solutionVector = Vector2d.create();
495
+ if (SmallSystem.linearSystem2d(ax, ar, bx, br, a, b, solutionVector)) {
496
+ const x = solutionVector.x;
497
+ const r = solutionVector.y;
498
+ let dd = (r + rA) * (r + rA) - x * x;
499
+ const tol = sRelTol * x * x;
500
+ if (Math.abs(dd) < tol)
501
+ dd = 0.0;
502
+ if (dd >= 0.0) {
503
+ const y = Math.sqrt(dd);
504
+ const xy0 = origin.plus2Scaled(unitAB, x, unitPerp, y);
505
+ const xy1 = origin.plus2Scaled(unitAB, x, unitPerp, -y);
506
+ for (const newCenter of [xy0, xy1]) {
507
+ const newCircle = UnboundedCircle2dByCenterAndRadius.createPointRadius(newCenter, r);
508
+ if (!isThisCirclePresent(result, newCircle)) {
509
+ const markup = new ImplicitGeometryMarkup(newCircle);
510
+ markup.appendClosePoint(newCircle.center, circleA, newCircle.center, r);
511
+ markup.appendClosePoint(newCircle.center, circleB, newCircle.center, r);
512
+ markup.appendClosePoint(newCircle.center, circleC, newCircle.center, r);
513
+ result.push(markup);
514
+ }
515
+ }
516
+ }
517
+ }
518
+ }
519
+ /**
520
+ * Return all (i.e., up to 8) circles tangent to 3 circles.
521
+ * @param circles the three input circles
522
+ */
523
+ static circlesTangentCCCThisOrder(circles) {
524
+ // Call the circle centers and radii
525
+ // (ax, ay, ar)
526
+ // (bx, by, br)
527
+ // (cx, cy, cr)
528
+ // For a tangent circle with center (x,y) and radius r,
529
+ // (x-ax)^2 + (y-ay)^2 = (r+ar)^2
530
+ // (x-bx)^2 + (y-by)^2 = (r+br)^2
531
+ // (x-cx)^2 + (y-cy)^2 = (r+cr)^2
532
+ // This has squares of all the unknowns x,y,r
533
+ // BUT !!! all coefficients of squares are 1.
534
+ // Subtract the first from the second and third equations.
535
+ // All the squares go away in the shifted equations.
536
+ // What's left is 2 linear equations with x,y,r as unknowns.
537
+ // Move the r and constant parts to the right side.
538
+ // Solve for x and y as a linear function of r.
539
+ // Substitute those linear forms back into the x and y of the first equation.
540
+ // A quadratic in r is left!
541
+ // Solve it for r, and then the linear forms give x and y.
542
+ // Do the whole process with every combination of signed br and cr.
543
+ // (No need to treat negated ar because negating all of them is redundant)
544
+ const result = [];
545
+ const vector01 = Vector2d.createStartEnd(circles[0].center, circles[1].center);
546
+ const vector02 = Vector2d.createStartEnd(circles[0].center, circles[2].center);
547
+ const dot12 = vector01.dotProduct(vector02);
548
+ const determinant = vector01.crossProduct(vector02);
549
+ const determinantTol = 1.0e-8;
550
+ const oneOverDeterminant = Geometry.conditionalDivideFraction(1.0, determinant);
551
+ // messy tolerance test ported. Why not just a parallel test on the vectors?
552
+ // maybe having 1/determinant is worth it?
553
+ if (oneOverDeterminant === undefined
554
+ || Math.abs(determinant) <= determinantTol * Math.abs(dot12)
555
+ || oneOverDeterminant === undefined) {
556
+ const signedR0 = circles[0].radius;
557
+ for (const signedR1 of signedValues(circles[1].radius)) {
558
+ for (const signedR2 of signedValues(circles[2].radius)) {
559
+ this.solveColinearCCCTangents(circles[0], circles[1], circles[2], signedR0, signedR1, signedR2, result);
560
+ }
561
+ }
562
+ return result.length > 0 ? result : undefined;
563
+ }
564
+ const r0 = circles[0].radius;
565
+ const inverseMatrix = Matrix3d.createRowValues(vector02.y * oneOverDeterminant, -vector01.y * oneOverDeterminant, 0.0, -vector02.x * oneOverDeterminant, vector01.x * oneOverDeterminant, 0.0, 0.0, 0.0, 1.0);
566
+ for (const r1 of signedValues(circles[1].radius)) {
567
+ for (const r2 of signedValues(circles[2].radius)) {
568
+ const vectorA = Vector3d.create(-0.5 * (Geometry.square(r1) - Geometry.square(vector01.x) - Geometry.square(vector01.y) - Geometry.square(r0)), -0.5 * (Geometry.square(r2) - Geometry.square(vector02.x) - Geometry.square(vector02.y) - Geometry.square(r0)), 0.0);
569
+ const vectorB = Vector3d.create(-(r1 - r0), -(r2 - r0), 0.0);
570
+ const vectorA1 = inverseMatrix.multiplyVector(vectorA);
571
+ const vectorB1 = inverseMatrix.multiplyVector(vectorB);
572
+ const qa = vectorB1.magnitudeSquared() - 1.0;
573
+ const qb = 2 * (vectorA1.dotProduct(vectorB1) - r0);
574
+ const qc = vectorA1.magnitudeSquared() - Geometry.square(r0);
575
+ const roots = Degree2PowerPolynomial.solveQuadratic(qa, qb, qc);
576
+ if (roots !== undefined) {
577
+ if (roots.length === 2
578
+ && Geometry.isSmallMetricDistance(Math.abs(roots[0]) - Math.abs(roots[1])))
579
+ roots.pop();
580
+ for (const newRadius of roots) {
581
+ const newCenter = circles[0].center.plus2Scaled(vectorA1, 1.0, vectorB1, newRadius);
582
+ const newCircle = UnboundedCircle2dByCenterAndRadius.createPointRadius(newCenter, newRadius);
583
+ if (!isThisCirclePresent(result, newCircle)) {
584
+ const markup = new ImplicitGeometryMarkup(newCircle);
585
+ markup.appendClosePoint(newCircle.center, circles[0], newCircle.center, newRadius);
586
+ markup.appendClosePoint(newCircle.center, circles[1], newCircle.center, newRadius);
587
+ markup.appendClosePoint(newCircle.center, circles[2], newCircle.center, newRadius);
588
+ result.push(markup);
589
+ }
590
+ }
591
+ }
592
+ }
593
+ }
594
+ return result;
595
+ }
596
+ /**
597
+ * Return all (i.e., up to 8) circles tangent to 3 circles.
598
+ * @param circleA first circle
599
+ * @param circleB second circle
600
+ * @param circleC third circle
601
+ */
602
+ static circlesTangentCCC(circleA, circleB, circleC) {
603
+ const circlesInOrder = [circleA, circleB, circleC];
604
+ return this.circlesTangentCCCThisOrder(circlesInOrder);
605
+ }
606
+ /**
607
+ * Return an unbounded line with the midpoint between pointA and pointB as its reference point
608
+ * and the unit vector from pointA towards pointB as its normal.
609
+ * @param pointA first point
610
+ * @param pointB second point
611
+ * @returns unbounded line, or undefined if pointA and pointB are coincident.
612
+ */
613
+ static bisector(pointA, pointB) {
614
+ const vectorAB = Vector2d.createStartEnd(pointA, pointB);
615
+ const midPoint = Point2d.createInterpolated(pointA, 0.5, pointB);
616
+ const unitAB = vectorAB.normalize();
617
+ if (unitAB === undefined)
618
+ return undefined;
619
+ return UnboundedLine2dByPointAndNormal.createPointNormal(midPoint, unitAB);
620
+ }
621
+ /**
622
+ * Compute circles of specified radius tangent to each of the lines
623
+ * * There are normally 4 circles
624
+ * * The undefined case occurs when the lines are parallel.
625
+ * @param lineA first line
626
+ * @param lineB second line
627
+ * @param radius radius of tangent circles
628
+ * @returns array of circles with annotated tangencies. returns undefined if lines are parallel.
629
+ */
630
+ static circlesTangentLLR(lineA, lineB, radius) {
631
+ // Construct lines offset by (positive and negative) radii.
632
+ // For each of the 4 combinations of offset lines, the intersection of offsets is the
633
+ // center of the tangent circle.
634
+ if (Geometry.isSmallMetricDistance(radius))
635
+ return undefined;
636
+ const offsetA0 = lineA.cloneShifted(radius);
637
+ const offsetA1 = lineA.cloneShifted(-radius);
638
+ const offsetB0 = lineB.cloneShifted(radius);
639
+ const offsetB1 = lineB.cloneShifted(-radius);
640
+ if (offsetA0 === undefined || offsetA1 === undefined || offsetB0 === undefined || offsetB1 === undefined)
641
+ return undefined;
642
+ const offsetsA = [offsetA0, offsetA1];
643
+ const offsetsB = [offsetB0, offsetB1];
644
+ const result = [];
645
+ for (const offsetA of offsetsA) {
646
+ for (const offsetB of offsetsB) {
647
+ const p = offsetA.intersectUnboundedLine2dByPointAndNormalWithOffsets(offsetB);
648
+ if (p !== undefined) {
649
+ const newCircle = UnboundedCircle2dByCenterAndRadius.createPointRadius(p, radius);
650
+ if (!isThisCirclePresent(result, newCircle)) {
651
+ const markup = new ImplicitGeometryMarkup(newCircle);
652
+ markup.appendClosePoint(newCircle.center, lineA, newCircle.center, newCircle.radius);
653
+ markup.appendClosePoint(newCircle.center, lineB, newCircle.center, newCircle.radius);
654
+ result.push(markup);
655
+ }
656
+ }
657
+ }
658
+ }
659
+ return result.length > 0 ? result : undefined;
660
+ }
661
+ /**
662
+ * Compute circles of specified radius tangent to both a line and an arc
663
+ * * There can be 0 to 8 circles
664
+ * * The undefined case occurs when the smallest distance from circle to line exceeds radius.
665
+ * @param circleA the circle
666
+ * @param lineB the line
667
+ * @param radius radius of tangent circles
668
+ * @returns array of circles with annotated tangencies
669
+ */
670
+ static circlesTangentCLR(circleA, lineB, radius) {
671
+ // From circleA, construct circles offset by positive and negative radii.
672
+ // From lineB, construct lines offset by positive and negative radii.
673
+ // Each combination of [offsetCircle, offsetLine] can have 2 intersections.
674
+ // Each such intersection is the center of a tangent circle.
675
+ if (Geometry.isSmallMetricDistance(radius))
676
+ return undefined;
677
+ const offsetsA = [
678
+ UnboundedCircle2dByCenterAndRadius.createPointRadius(circleA.center, circleA.radius + radius),
679
+ UnboundedCircle2dByCenterAndRadius.createPointRadius(circleA.center, circleA.radius - radius),
680
+ ];
681
+ const offsetB0 = lineB.cloneShifted(radius);
682
+ const offsetB1 = lineB.cloneShifted(-radius);
683
+ if (offsetB0 === undefined || offsetB1 === undefined)
684
+ return undefined;
685
+ const offsetsB = [offsetB0, offsetB1];
686
+ const result = [];
687
+ for (const offsetA of offsetsA) {
688
+ for (const offsetB of offsetsB) {
689
+ const points = offsetA.intersectLine(offsetB);
690
+ for (const p of points) {
691
+ const newCircle = UnboundedCircle2dByCenterAndRadius.createPointRadius(p, radius);
692
+ if (!isThisCirclePresent(result, newCircle)) {
693
+ const markup = new ImplicitGeometryMarkup(newCircle);
694
+ markup.appendClosePoint(newCircle.center, circleA, newCircle.center, newCircle.radius);
695
+ markup.appendClosePoint(newCircle.center, lineB, newCircle.center, newCircle.radius);
696
+ result.push(markup);
697
+ }
698
+ }
699
+ }
700
+ }
701
+ return result.length > 0 ? result : undefined;
702
+ }
703
+ /**
704
+ * Compute circles of specified radius tangent to both circles
705
+ * * There can be 0 to 8 circles
706
+ * * The undefined case is when the smallest distance between the circles exceeds the requested radius.
707
+ * @param circleA the first circle
708
+ * @param circleB the second circle
709
+ * @param radius radius of tangent circles
710
+ * @returns array of circles with annotated tangencies
711
+ */
712
+ static circlesTangentCCR(circleA, circleB, radius) {
713
+ // From circleA, construct circles offset by positive and negative radii.
714
+ // From circleB, construct circles offset by positive and negative radii.
715
+ // Each combination of [offsetA, offsetsB] can have 2 intersections.
716
+ // Each such intersection is the center of a tangent circle.
717
+ if (Geometry.isSmallMetricDistance(radius))
718
+ return undefined;
719
+ const offsetsA = [
720
+ UnboundedCircle2dByCenterAndRadius.createPointRadius(circleA.center, circleA.radius + radius),
721
+ UnboundedCircle2dByCenterAndRadius.createPointRadius(circleA.center, circleA.radius - radius),
722
+ ];
723
+ const offsetsB = [
724
+ UnboundedCircle2dByCenterAndRadius.createPointRadius(circleB.center, circleB.radius + radius),
725
+ UnboundedCircle2dByCenterAndRadius.createPointRadius(circleB.center, circleB.radius - radius),
726
+ ];
727
+ const result = [];
728
+ for (const offsetA of offsetsA) {
729
+ for (const offsetB of offsetsB) {
730
+ const points = offsetA.intersectCircle(offsetB);
731
+ for (const p of points) {
732
+ const newCircle = UnboundedCircle2dByCenterAndRadius.createPointRadius(p, radius);
733
+ if (!isThisCirclePresent(result, newCircle)) {
734
+ const markup = new ImplicitGeometryMarkup(newCircle);
735
+ markup.appendClosePoint(newCircle.center, circleA, newCircle.center, newCircle.radius);
736
+ markup.appendClosePoint(newCircle.center, circleB, newCircle.center, newCircle.radius);
737
+ result.push(markup);
738
+ }
739
+ }
740
+ }
741
+ }
742
+ return result.length > 0 ? result : undefined;
743
+ }
744
+ /**
745
+ * Construct basis vectors for hyperbola or ellipse whose points are equidistant from tangencies with
746
+ * circleA and circleB.
747
+ * * If the curve is a hyperbola, the equation is
748
+ * X = center + vectorU * sec(theta) + vectorV * tan(theta)
749
+ * * If the curve is an ellipse, the equation is
750
+ * X = center + vectorU * cos(theta) + vectorV * sin(theta)
751
+ * @param circleA first circle
752
+ * @param circleB second circle
753
+ */
754
+ static medialCurveCircleCircle(circleA, circleB) {
755
+ const d = circleA.center.distance(circleB.center);
756
+ const origin = circleA.center.interpolate(0.5, circleB.center);
757
+ const h = circleA.radius + circleB.radius;
758
+ const discriminant = d * d - h * h;
759
+ const hy = Math.sqrt(Math.abs(discriminant));
760
+ const xAxis = Vector2d.createStartEnd(origin, circleB.center).normalize();
761
+ if (xAxis === undefined)
762
+ return undefined;
763
+ const yAxis = xAxis.rotate90CCWXY();
764
+ const ax = 0.5 * h;
765
+ const ay = 0.5 * hy;
766
+ const vectorU = xAxis.scale(ax);
767
+ const vectorV = yAxis.scale(ay);
768
+ if (discriminant > 0.0) {
769
+ return UnboundedHyperbola2d.createCenterAndAxisVectors(origin, vectorU, vectorV);
770
+ }
771
+ else if (discriminant < 0.0) {
772
+ return UnboundedEllipse2d.createCenterAndAxisVectors(origin, vectorU, vectorV);
773
+ }
774
+ return undefined;
775
+ }
776
+ /**
777
+ * Construct parabolas whose points are equidistant from tangencies with
778
+ * circleA and circleB.
779
+ * * Note that if th e circle has non-zero radius there are two parabolas based on
780
+ * tangencies on near or far side of the circle.
781
+ * @param line the line
782
+ * @param circle the circle
783
+ */
784
+ static medialCurveLineCircle(line, circle) {
785
+ const linePoint = line.closestPoint(circle.center);
786
+ if (linePoint === undefined)
787
+ return undefined;
788
+ const lineA = line.cloneNormalizedWithOrigin(linePoint);
789
+ if (lineA === undefined)
790
+ return undefined;
791
+ const b = linePoint.distance(circle.center);
792
+ const side = lineA.functionValue(circle.center) > 0 ? 1 : -1;
793
+ const signedB = b * side;
794
+ const signedRadii = [];
795
+ if (Geometry.isSameCoordinate(circle.radius, -circle.radius))
796
+ signedRadii.push(0);
797
+ else {
798
+ signedRadii.push(circle.radius);
799
+ signedRadii.push(-circle.radius);
800
+ }
801
+ const result = [];
802
+ for (const r of signedRadii) {
803
+ const vertexDistance = (signedB - r) * 0.5;
804
+ const vertex = lineA.point.plusScaled(lineA.normal, vertexDistance);
805
+ const c = 2 * (signedB + r);
806
+ const oneOverC = Geometry.conditionalDivideCoordinate(1, c);
807
+ if (oneOverC !== undefined) {
808
+ const vectorV = lineA.normal.clone().scale(oneOverC);
809
+ const vectorU = lineA.normal.unitPerpendicularXY();
810
+ const parabola = UnboundedParabola2d.createCenterAndAxisVectors(vertex, vectorU, vectorV);
811
+ result.push(parabola);
812
+ }
813
+ }
814
+ return result;
815
+ }
816
+ }
817
+ /**
818
+ * If distance is near zero metric, return an array containing only value.
819
+ * If not near zero, return an array with both value and its negative (in that order).
820
+ * @param distance the distance
821
+ * @param value the value
822
+ */
823
+ function signedValues(distance, value) {
824
+ if (value === undefined)
825
+ value = distance;
826
+ const values = [value];
827
+ if (!Geometry.isSmallMetricDistance(distance))
828
+ values.push(-value);
829
+ return values;
830
+ }
831
+ /** Search an array of circles to see if a particular new circle is already present. */
832
+ function isThisCirclePresent(circles, circle) {
833
+ for (const c of circles) {
834
+ if (circle.isSameCircle(c.curve, true))
835
+ return true;
836
+ }
837
+ return false;
838
+ }
839
+ //# sourceMappingURL=ConstrainedImplicitCurve2d.js.map