@design-core/core 0.0.1 → 0.0.3

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 (254) hide show
  1. package/.eslintrc.json +14 -7
  2. package/.github/workflows/DesignCoreQA.yml +3 -1
  3. package/README.md +11 -4
  4. package/TODO.md +19 -0
  5. package/core/core/core.js +49 -42
  6. package/core/designCore.js +61 -2
  7. package/core/dimensions/alignedDimension.js +142 -0
  8. package/core/dimensions/angularDimension.js +550 -0
  9. package/core/dimensions/baseCircularDimension.js +244 -0
  10. package/core/dimensions/baseDimension.js +558 -0
  11. package/core/dimensions/baseLinearDimension.js +371 -0
  12. package/core/dimensions/diametricDimension.js +82 -0
  13. package/core/dimensions/dimension.js +298 -0
  14. package/core/dimensions/linearDimension.js +29 -0
  15. package/core/dimensions/radialDimension.js +78 -0
  16. package/core/dimensions/rotatedDimension.js +167 -0
  17. package/core/entities/arc.js +314 -60
  18. package/core/entities/arctext.js +642 -0
  19. package/core/entities/basePolyline.js +553 -94
  20. package/core/entities/circle.js +181 -54
  21. package/core/entities/entity.js +154 -74
  22. package/core/entities/hatch.js +807 -0
  23. package/core/entities/insert.js +200 -39
  24. package/core/entities/line.js +196 -134
  25. package/core/entities/lwpolyline.js +21 -44
  26. package/core/entities/point.js +194 -29
  27. package/core/entities/polyline.js +54 -7
  28. package/core/entities/rectangle.js +43 -13
  29. package/core/entities/solid.js +43 -6
  30. package/core/entities/text.js +324 -181
  31. package/core/lib/boundingBox.js +73 -13
  32. package/core/lib/canvas.js +256 -65
  33. package/core/lib/clipboard.js +198 -0
  34. package/core/lib/colour.js +144 -0
  35. package/core/lib/colours.js +367 -301
  36. package/core/lib/commandLine.js +16 -28
  37. package/core/lib/commandManager.js +115 -58
  38. package/core/lib/constants.js +23 -0
  39. package/core/lib/dxf/dxf.js +249 -34
  40. package/core/lib/dxf/dxfFile.js +23 -38
  41. package/core/lib/dxf/dxfIterator.js +17 -5
  42. package/core/lib/dxf/dxfRead.js +23 -7
  43. package/core/lib/dxf/dxfWrite.js +39 -129
  44. package/core/lib/dxf/handleManager.js +97 -0
  45. package/core/lib/dxf/sections/blocks.js +16 -2
  46. package/core/lib/dxf/sections/entities.js +17 -3
  47. package/core/lib/dxf/sections/header.js +10 -1
  48. package/core/lib/dxf/sections/objects.js +36 -0
  49. package/core/lib/dxf/sections/section.js +43 -5
  50. package/core/lib/dxf/sections/tables.js +17 -3
  51. package/core/lib/entityManager.js +178 -0
  52. package/core/lib/fileio.js +14 -4
  53. package/core/lib/headers.js +229 -0
  54. package/core/lib/input.js +46 -0
  55. package/core/lib/inputManager.js +199 -194
  56. package/core/lib/intersect.js +341 -468
  57. package/core/lib/logging.js +25 -1
  58. package/core/lib/matrix.js +6 -8
  59. package/core/lib/mouse.js +24 -79
  60. package/core/lib/patterns.js +690 -0
  61. package/core/lib/promptOptions.js +133 -0
  62. package/core/lib/scene.js +71 -174
  63. package/core/lib/selectionManager.js +74 -48
  64. package/core/lib/selectionWindow.js +24 -10
  65. package/core/lib/settings.js +18 -11
  66. package/core/lib/snapping.js +129 -21
  67. package/core/lib/stateManager.js +288 -0
  68. package/core/lib/strings.js +57 -5
  69. package/core/lib/utils.js +95 -11
  70. package/core/objects/dictionary.js +43 -0
  71. package/core/objects/dictionaryManager.js +80 -0
  72. package/core/properties/dimType.js +117 -0
  73. package/core/properties/flags.js +66 -0
  74. package/core/properties/property.js +26 -0
  75. package/core/properties/propertyManager.js +65 -19
  76. package/core/tables/appId.js +31 -0
  77. package/core/tables/appIdManager.js +44 -0
  78. package/core/tables/block.js +323 -0
  79. package/core/tables/blockManager.js +48 -0
  80. package/core/tables/blockRecord.js +29 -0
  81. package/core/tables/blockRecordManager.js +37 -0
  82. package/core/tables/dimStyle.js +627 -0
  83. package/core/tables/dimStyleManager.js +187 -0
  84. package/core/tables/endblk.js +28 -0
  85. package/core/tables/layer.js +219 -0
  86. package/core/tables/layerManager.js +47 -0
  87. package/core/tables/ltype.js +85 -0
  88. package/core/tables/ltypeManager.js +65 -0
  89. package/core/tables/style.js +145 -0
  90. package/core/tables/styleManager.js +55 -0
  91. package/core/tables/styleManagerBase.js +110 -0
  92. package/core/tables/tableManagerBase.js +354 -0
  93. package/core/tables/ucs.js +28 -0
  94. package/core/tables/ucsManager.js +44 -0
  95. package/core/tables/view.js +28 -0
  96. package/core/tables/viewManager.js +44 -0
  97. package/core/tables/vport.js +70 -0
  98. package/core/tables/vportManager.js +65 -0
  99. package/core/tools/chamfer.js +343 -0
  100. package/core/tools/chamferFilletBase.js +162 -0
  101. package/core/tools/copy.js +68 -40
  102. package/core/tools/copybase.js +65 -0
  103. package/core/tools/copyclip.js +59 -0
  104. package/core/tools/cornerEntity.js +138 -0
  105. package/core/tools/cutclip.js +72 -0
  106. package/core/tools/distance.js +37 -9
  107. package/core/tools/erase.js +43 -13
  108. package/core/tools/explode.js +117 -0
  109. package/core/tools/extend.js +71 -32
  110. package/core/tools/fillet.js +336 -0
  111. package/core/tools/identify.js +33 -6
  112. package/core/tools/matchProp.js +106 -0
  113. package/core/tools/mirror.js +141 -0
  114. package/core/tools/move.js +65 -32
  115. package/core/tools/offset.js +321 -0
  116. package/core/tools/pan.js +54 -0
  117. package/core/tools/pasteclip.js +93 -0
  118. package/core/tools/purge.js +90 -0
  119. package/core/tools/rotate.js +105 -70
  120. package/core/tools/scale.js +166 -0
  121. package/core/tools/tool.js +14 -4
  122. package/core/tools/trim.js +71 -32
  123. package/core/tools/zoom.js +152 -0
  124. package/package.json +3 -305
  125. package/test/dimensions/alignedDimension.test.js +211 -0
  126. package/test/dimensions/angularDimension.test.js +243 -0
  127. package/test/dimensions/baseDimension.test.js +239 -0
  128. package/test/dimensions/diametricDimension.test.js +203 -0
  129. package/test/dimensions/dimension.test.js +102 -0
  130. package/test/dimensions/radialDimension.test.js +203 -0
  131. package/test/dimensions/rotatedDimension.test.js +224 -0
  132. package/test/entities/arc.test.js +463 -41
  133. package/test/entities/arctext.test.js +329 -0
  134. package/test/entities/basePolyline.test.js +459 -10
  135. package/test/entities/circle.test.js +312 -7
  136. package/test/entities/entity.test.js +28 -9
  137. package/test/entities/hatch.test.js +606 -0
  138. package/test/entities/insert.test.js +90 -0
  139. package/test/entities/line.test.js +182 -6
  140. package/test/entities/point.test.js +375 -37
  141. package/test/entities/solid.test.js +156 -0
  142. package/test/entities/text.test.js +533 -7
  143. package/test/integration/exportIntegration.reference.dxf +1706 -0
  144. package/test/integration/exportIntegration.test.js +186 -0
  145. package/test/integration/importExportIntegration.test.js +126 -0
  146. package/test/integration/intersectionIntegration.test.js +880 -0
  147. package/test/integration/userInputIntegration.reference.dxf +1410 -0
  148. package/test/integration/userInputIntegration.test.js +269 -0
  149. package/test/lib/boundingBox.test.js +26 -2
  150. package/test/lib/canvas.test.js +613 -5
  151. package/test/lib/clipboard.test.js +70 -0
  152. package/test/lib/colour.test.js +114 -0
  153. package/test/lib/colours.test.js +63 -18
  154. package/test/lib/commandLine.test.js +159 -22
  155. package/test/lib/commandManager.test.js +4 -4
  156. package/test/lib/constants.test.js +9 -0
  157. package/test/lib/dxf/dxfFile.test.js +6 -2
  158. package/test/lib/dxf/dxfIterator.test.js +1 -1
  159. package/test/lib/dxf/dxfWrite.test.js +173 -0
  160. package/test/lib/dxf/handleManager.test.js +137 -0
  161. package/test/lib/dxf/sections/blocks.test.js +5 -5
  162. package/test/lib/dxf/sections/entities.test.js +4 -4
  163. package/test/lib/dxf/sections/header.test.js +18 -4
  164. package/test/lib/dxf/sections/objects.test.js +132 -0
  165. package/test/lib/dxf/sections/section.test.js +29 -29
  166. package/test/lib/dxf/sections/tables.test.js +3 -3
  167. package/test/lib/entityManager.test.js +163 -0
  168. package/test/lib/headers.test.js +233 -0
  169. package/test/lib/inputManager.test.js +1144 -5
  170. package/test/lib/intersect.test.js +454 -331
  171. package/test/lib/logging.test.js +1 -1
  172. package/test/lib/matrix.test.js +1 -1
  173. package/test/lib/mouse.test.js +327 -4
  174. package/test/lib/patterns.test.js +105 -0
  175. package/test/lib/promptOptions.test.js +179 -0
  176. package/test/lib/scene.test.js +16 -52
  177. package/test/lib/selectionManager.test.js +17 -5
  178. package/test/lib/settings.test.js +111 -5
  179. package/test/lib/snapping.test.js +142 -4
  180. package/test/lib/stateManager.test.js +155 -0
  181. package/test/lib/utils.test.js +81 -1
  182. package/test/objects/dictionary.test.js +81 -0
  183. package/test/objects/dictionaryManager.test.js +34 -0
  184. package/test/properties/dimType.test.js +62 -0
  185. package/test/properties/flags.test.js +164 -0
  186. package/test/properties/property.test.js +13 -0
  187. package/test/properties/propertyManager.test.js +10 -11
  188. package/test/tables/appId.test.js +46 -0
  189. package/test/tables/appIdManager.test.js +72 -0
  190. package/test/tables/block.test.js +157 -0
  191. package/test/tables/blockManager.test.js +52 -0
  192. package/test/tables/blockRecord.test.js +39 -0
  193. package/test/tables/blockRecordManager.test.js +60 -0
  194. package/test/tables/dimstyle.test.js +72 -0
  195. package/test/tables/endblk.test.js +30 -0
  196. package/test/tables/layer.test.js +95 -0
  197. package/test/tables/layerManager.test.js +172 -0
  198. package/test/{styles → tables}/ltype.test.js +7 -7
  199. package/test/{styles → tables}/ltypeManager.test.js +2 -2
  200. package/test/tables/style.test.js +44 -0
  201. package/test/tables/stylemanagerbase.test.js +220 -0
  202. package/test/tables/tableManagerBase.test.js +194 -0
  203. package/test/tables/ucs.test.js +37 -0
  204. package/test/tables/ucsManager.test.js +48 -0
  205. package/test/tables/view.test.js +37 -0
  206. package/test/tables/viewManager.test.js +48 -0
  207. package/test/tables/vport.test.js +117 -0
  208. package/test/tables/vportManager.test.js +54 -0
  209. package/test/test-helpers/test-helpers.js +66 -0
  210. package/test/tools/chamfer.test.js +970 -0
  211. package/test/tools/chamferFilletBase.test.js +446 -0
  212. package/test/tools/copy.test.js +22 -14
  213. package/test/tools/copybase.test.js +93 -0
  214. package/test/tools/copyclip.test.js +92 -0
  215. package/test/tools/cornerEntity.test.js +201 -0
  216. package/test/tools/cutclip.test.js +94 -0
  217. package/test/tools/distance.test.js +140 -18
  218. package/test/tools/erase.test.js +12 -12
  219. package/test/tools/explode.test.js +45 -0
  220. package/test/tools/extend.test.js +326 -44
  221. package/test/tools/fillet.test.js +831 -0
  222. package/test/tools/identify.test.js +115 -16
  223. package/test/tools/matchProp.test.js +84 -0
  224. package/test/tools/mirror.test.js +475 -0
  225. package/test/tools/move.test.js +75 -19
  226. package/test/tools/offset.test.js +748 -0
  227. package/test/tools/pan.test.js +72 -0
  228. package/test/tools/pasteclip.test.js +100 -0
  229. package/test/tools/purge.test.js +89 -0
  230. package/test/tools/rotate.test.js +89 -23
  231. package/test/tools/scale.test.js +529 -0
  232. package/test/tools/trim.test.js +419 -84
  233. package/test/tools/zoom.test.js +504 -0
  234. package/core/entities/alignedDimension.js +0 -279
  235. package/core/entities/angularDimension.js +0 -194
  236. package/core/entities/baseDimension.js +0 -227
  237. package/core/entities/block.js +0 -247
  238. package/core/entities/diametricDimension.js +0 -133
  239. package/core/entities/dimension.js +0 -202
  240. package/core/entities/radialDimension.js +0 -126
  241. package/core/styles/dimStyle.js +0 -719
  242. package/core/styles/dimStyleManager.js +0 -47
  243. package/core/styles/layer.js +0 -133
  244. package/core/styles/layerManager.js +0 -40
  245. package/core/styles/ltype.js +0 -86
  246. package/core/styles/ltypeManager.js +0 -50
  247. package/core/styles/style.js +0 -158
  248. package/core/styles/styleManager.js +0 -43
  249. package/core/styles/styleManagerBase.js +0 -299
  250. package/test/entities/block.test.js +0 -22
  251. package/test/styles/dimstyle.test.js +0 -19
  252. package/test/styles/layerManager.test.js +0 -150
  253. package/test/styles/style.test.js +0 -33
  254. package/test/styles/stylemanagerbase.test.js +0 -219
@@ -1,287 +1,140 @@
1
- import {Point} from '../../core/entities/point.js';
2
- import {Intersection} from '../../core/lib/intersect';
1
+ import { Point } from '../../core/entities/point.js';
2
+ import { Intersection } from '../../core/lib/intersect';
3
3
 
4
4
  test('Test Intersection.intersectCircleEllipse()', () => {
5
5
  expect(true).toBeTruthy();
6
6
  // TODO: intersectCircleEllipse
7
7
  });
8
8
 
9
- test('Test Intersection.intersectCircleLine()', () => {
10
- // Intersection
11
- const circle = {centre: new Point(10, 10), radius: 5};
12
- const line = {start: new Point(), end: new Point(10, 10)};
13
- const result = Intersection.intersectCircleLine(circle, line, false);
14
- expect(result.status).toBe('Intersection');
15
- expect(result.points.length).toBe(1);
16
- expect(result.points[0].x).toBeCloseTo(6.46);
17
- expect(result.points[0].y).toBeCloseTo(6.46);
18
-
19
- // No Intersection - Outside
20
- const circle1 = {centre: new Point(10, 10), radius: 2};
21
- const line1 = {start: new Point(), end: new Point(0, 10)};
22
- const result1 = Intersection.intersectCircleLine(circle1, line1, false);
23
- expect(result1.status).toBe('Outside');
24
- expect(result1.points.length).toBe(0);
25
-
26
- // No Intersection - Inside
27
- const circle2 = {centre: new Point(), radius: 20};
28
- const line2 = {start: new Point(), end: new Point(0, 10)};
29
- const result2 = Intersection.intersectCircleLine(circle2, line2, false);
30
- expect(result2.status).toBe('Inside');
31
- expect(result2.points.length).toBe(0);
32
- });
33
-
34
- test('Test Intersection.intersectLineCircle()', () => {
35
- // Intersection
36
- const circle = {centre: new Point(10, 10), radius: 5};
37
- const line = {start: new Point(), end: new Point(10, 10)};
38
- const result = Intersection.intersectLineCircle(line, circle, false);
39
- expect(result.status).toBe('Intersection');
40
- expect(result.points.length).toBe(1);
41
- expect(result.points[0].x).toBeCloseTo(6.46);
42
- expect(result.points[0].y).toBeCloseTo(6.46);
43
-
44
- // No Intersection - Outside
45
- const circle1 = {centre: new Point(10, 10), radius: 2};
46
- const line1 = {start: new Point(), end: new Point(0, 10)};
47
- const result1 = Intersection.intersectLineCircle(line1, circle1, false);
48
- expect(result1.status).toBe('Outside');
49
- expect(result1.points.length).toBe(0);
50
-
51
- // No Intersection - Inside
52
- const circle2 = {centre: new Point(), radius: 20};
53
- const line2 = {start: new Point(), end: new Point(0, 10)};
54
- const result2 = Intersection.intersectLineCircle(line2, circle2, false);
55
- expect(result2.status).toBe('Inside');
56
- expect(result2.points.length).toBe(0);
57
- });
58
-
59
- test('Test Intersection.intersectCircleCircle()', () => {
60
- // Intersection
61
- const circle1 = {centre: new Point(10, 10), radius: 5};
62
- const circle2 = {centre: new Point(15, 10), radius: 5};
63
- const result = Intersection.intersectCircleCircle(circle1, circle2, false);
64
- expect(result.status).toBe('Intersection');
65
- expect(result.points.length).toBe(2);
66
- expect(result.points[0].x).toBeCloseTo(12.5);
67
- expect(result.points[0].y).toBeCloseTo(14.33);
68
-
69
- // No Intersection - Inside
70
- const circle3 = {centre: new Point(10, 10), radius: 5};
71
- const circle4 = {centre: new Point(10, 10), radius: 2};
72
- const result1 = Intersection.intersectCircleCircle(circle3, circle4, false);
73
- expect(result1.status).toBe('Inside');
74
- expect(result1.points.length).toBe(0);
75
-
76
- // No Intersection - Outside
77
- const circle5 = {centre: new Point(10, 10), radius: 5};
78
- const circle6 = {centre: new Point(-10, 10), radius: 2};
79
- const result2 = Intersection.intersectCircleCircle(circle5, circle6, false);
80
- expect(result2.status).toBe('Outside');
81
- expect(result2.points.length).toBe(0);
82
- });
83
-
84
- test('Test Intersection.intersectCirclePolygon()', () => {
85
- // Intersection.intersectCirclePolygon(c,r,points);
86
- expect(true).toBeTruthy();
87
- // TODO: intersectCirclePolygon
88
- });
89
-
90
- test('Test Intersection.intersectArcRectangle()', () => {
91
- // Intersection
92
- const arc = {centre: new Point(), radius: 14.14, startPoint: new Point(10, 10), endPoint: new Point(-10, 10)};
93
- const rectangle = {start: new Point(), end: new Point(25, 25)};
94
- const result = Intersection.intersectArcRectangle(arc, rectangle, false);
9
+ test('Test intersectSegmentSegment - circle line', () => {
10
+ // CCW semicircular arc from (10,0) to (-10,0) with bulge=1
11
+ // centre (0,0), radius 10, passes through (0,10)
12
+ // Vertical line from (0,-15) to (0,15) crosses the arc at (0,10)
13
+ const result = Intersection.intersectSegmentSegment(
14
+ new Point(10, 0, 1), new Point(-10, 0),
15
+ new Point(0, -15), new Point(0, 15),
16
+ false,
17
+ );
95
18
  expect(result.status).toBe('Intersection');
96
19
  expect(result.points.length).toBe(1);
97
20
  expect(result.points[0].x).toBeCloseTo(0);
98
- expect(result.points[0].y).toBeCloseTo(14.14);
99
-
100
- // No Intersection
101
- const arc1 = {centre: new Point(), radius: 14.14, startPoint: new Point(-10, 0), endPoint: new Point(0, -10)};
102
- const rectangle1 = {start: new Point(), end: new Point(25, 25)};
103
- const result1 = Intersection.intersectArcRectangle(arc1, rectangle1, false);
104
- expect(result1.status).toBe('No Intersection');
105
- expect(result1.points.length).toBe(0);
106
- });
107
-
108
- test('Test Intersection.intersectArcLine()', () => {
109
- // Intersection
110
- const arc = {centre: new Point(), radius: 14.14, startPoint: new Point(10, 10), endPoint: new Point(-10, 10)};
111
- const line = {start: new Point(), end: new Point(0, 25)};
112
- const result = Intersection.intersectArcLine(arc, line, false);
113
- expect(result.status).toBe('Intersection');
114
- expect(result.points.length).toBe(1);
115
- expect(result.points[0].x).toBeCloseTo(0);
116
- expect(result.points[0].y).toBeCloseTo(14.14);
117
-
118
- // Intersection - Arc straddles 0 with startAngle > endAngle
119
- const arc1 = {centre: new Point(), radius: 14.14, startPoint: new Point(10, -10), endPoint: new Point(-10, 10)};
120
- const line1 = {start: new Point(), end: new Point(0, 25)};
121
- const result1 = Intersection.intersectArcLine(arc1, line1, false);
122
- expect(result1.status).toBe('Intersection');
123
- expect(result1.points.length).toBe(1);
124
- expect(result1.points[0].x).toBeCloseTo(0);
125
- expect(result1.points[0].y).toBeCloseTo(14.14);
126
-
127
- // No Intersection
128
- const arc2 = {centre: new Point(), radius: 14.14, startPoint: new Point(-10, 0), endPoint: new Point(0, -10)};
129
- const line2 = {start: new Point(), end: new Point(0, 25)};
130
- const result2 = Intersection.intersectArcLine(arc2, line2, false);
131
- expect(result2.status).toBe('No Intersection');
132
- expect(result2.points.length).toBe(0);
21
+ expect(result.points[0].y).toBeCloseTo(10);
133
22
  });
134
23
 
135
- test('Test Intersection.intersectLineArc()', () => {
136
- // Intersection
137
- const arc = {centre: new Point(), radius: 14.14, startPoint: new Point(10, 10), endPoint: new Point(-10, 10)};
138
- const line = {start: new Point(), end: new Point(0, 25)};
139
- const result = Intersection.intersectLineArc(line, arc, false);
140
- expect(result.status).toBe('Intersection');
24
+ test('Test intersectSegmentSegment - tangent arc line', () => {
25
+ // CCW semicircular arc from (10,0) to (-10,0), centre (0,0), radius 10
26
+ // Horizontal line y=10 is tangent to the top of the arc at (0,10)
27
+ const result = Intersection.intersectSegmentSegment(
28
+ new Point(10, 0, 1), new Point(-10, 0),
29
+ new Point(-15, 10), new Point(15, 10),
30
+ false,
31
+ );
32
+ expect(result.status).toBe('Tangent');
141
33
  expect(result.points.length).toBe(1);
142
34
  expect(result.points[0].x).toBeCloseTo(0);
143
- expect(result.points[0].y).toBeCloseTo(14.14);
144
-
145
- // No Intersection
146
- const arc1 = {centre: new Point(), radius: 14.14, startPoint: new Point(-10, 0), endPoint: new Point(0, -10)};
147
- const line1 = {start: new Point(), end: new Point(0, 25)};
148
- const result1 = Intersection.intersectLineArc(line1, arc1, false);
149
- expect(result1.status).toBe('No Intersection');
150
- expect(result1.points.length).toBe(0);
151
- });
152
-
153
- test('Test Intersection.intersectCircleArc()', () => {
154
- // Intersection
155
- const arc = {centre: new Point(), radius: 14.14, startPoint: new Point(10, 10), endPoint: new Point(-10, 10)};
156
- const circle = {centre: new Point(10, 10), radius: 5};
157
- const result = Intersection.intersectCircleArc(circle, arc, false);
158
- expect(result.status).toBe('Intersection');
159
- expect(result.points.length).toBe(1);
160
- expect(result.points[0].x).toBeCloseTo(5.894);
161
- expect(result.points[0].y).toBeCloseTo(12.853);
162
-
163
- // No Intersection
164
- const arc1 = {centre: new Point(), radius: 14.14, startPoint: new Point(-10, 0), endPoint: new Point(0, -10)};
165
- const circle1 = {centre: new Point(10, 10), radius: 5};
166
- const result1 = Intersection.intersectCircleArc(circle1, arc1, false);
167
- expect(result1.status).toBe('No Intersection');
168
- expect(result1.points.length).toBe(0);
169
- });
170
-
171
- test('Test Intersection.intersectArcCircle()', () => {
172
- // Intersection
173
- const arc = {centre: new Point(), radius: 14.14, startPoint: new Point(10, 10), endPoint: new Point(-10, 10)};
174
- const circle = {centre: new Point(10, 10), radius: 5};
175
- const result = Intersection.intersectArcCircle(arc, circle, false);
176
- expect(result.status).toBe('Intersection');
177
- expect(result.points.length).toBe(1);
178
- expect(result.points[0].x).toBeCloseTo(5.894);
179
- expect(result.points[0].y).toBeCloseTo(12.853);
180
-
181
- // Clockwise Arc Intersection
182
- const cwArc = {centre: new Point(), radius: 14.14, startPoint: new Point(10, 10), endPoint: new Point(-10, 10), direction: -1};
183
- const cwResult = Intersection.intersectArcCircle(cwArc, circle, false);
184
- expect(cwResult.status).toBe('Intersection');
185
- expect(cwResult.points.length).toBe(1);
186
- expect(cwResult.points[0].x).toBeCloseTo(12.853);
187
- expect(cwResult.points[0].y).toBeCloseTo(5.894);
188
-
189
- // No Intersection
190
- const arc1 = {centre: new Point(), radius: 14.14, startPoint: new Point(-10, 0), endPoint: new Point(0, -10)};
191
- const circle1 = {centre: new Point(10, 10), radius: 5};
192
- const result1 = Intersection.intersectArcCircle(arc1, circle1, false);
193
- expect(result1.status).toBe('No Intersection');
194
- expect(result1.points.length).toBe(0);
195
- });
196
-
197
- test('Test Intersection.intersectCircleRectangle()', () => {
198
- // Intersection
199
- const circle = {centre: new Point(), radius: 14.14};
200
- const rectangle = {start: new Point(), end: new Point(25, 25)};
201
- const result = Intersection.intersectCircleRectangle(circle, rectangle, false);
202
- expect(result.status).toBe('Intersection');
203
- expect(result.points.length).toBe(2);
204
- expect(result.points[0].x).toBeCloseTo(14.14);
205
- expect(result.points[0].y).toBeCloseTo(0);
206
-
207
- // No Intersection - Outside
208
- const circle1 = {centre: new Point(12.5, 12.5), radius: 10};
209
- const rectangle1 = {start: new Point(), end: new Point(25, 25)};
210
- const result1 = Intersection.intersectCircleRectangle(circle1, rectangle1, false);
211
- expect(result1.status).toBe('Outside');
212
- expect(result1.points.length).toBe(0);
213
-
214
- // No Intersection - Inside
215
- const circle2 = {centre: new Point(12.5, 12.5), radius: 100};
216
- const rectangle2 = {start: new Point(), end: new Point(25, 25)};
217
- const result2 = Intersection.intersectCircleRectangle(circle2, rectangle2, false);
218
- expect(result2.status).toBe('Inside');
219
- expect(result2.points.length).toBe(0);
220
- });
221
-
222
- test('Test Intersection.intersectEllipseEllipse()', () => {
223
- // Intersection.intersectEllipseEllipse(c1,rx1,ry1,c2,rx2,ry2);
224
- expect(true).toBeTruthy();
225
- // TODO: intersectEllipseEllipse
226
- });
227
-
228
- test('Test Intersection.intersectEllipseLine()', () => {
229
- // Intersection.intersectEllipseLine(ellipse,line,extend);
230
- expect(true).toBeTruthy();
231
- // TODO: intersectEllipseLine
35
+ expect(result.points[0].y).toBeCloseTo(10);
232
36
  });
233
37
 
234
- test('Test Intersection.intersectEllipsePolygon()', () => {
235
- // Intersection.intersectEllipsePolygon(c,rx,ry,points);
236
- expect(true).toBeTruthy();
237
- // TODO: intersectEllipsePolygon
38
+ test('Test intersectSegmentSegment - concentric arcs same radius', () => {
39
+ // Two semicircular arcs with the same centre and radius
40
+ const result = Intersection.intersectSegmentSegment(
41
+ new Point(10, 0, 1), new Point(-10, 0),
42
+ new Point(0, 10, 1), new Point(0, -10),
43
+ false,
44
+ );
45
+ expect(result.status).toBe('Coincident');
46
+ expect(result.points.length).toBe(0);
238
47
  });
239
48
 
240
- test('Test Intersection.intersectEllipseRectangle()', () => {
241
- // Intersection.intersectEllipseRectangle(ellipse,rectangle,extend);
242
- expect(true).toBeTruthy();
243
- // TODO: intersectEllipseRectangle
49
+ test('Test intersectSegmentSegment - concentric arcs different radius', () => {
50
+ // Two semicircular arcs with the same centre but different radii
51
+ const result = Intersection.intersectSegmentSegment(
52
+ new Point(10, 0, 1), new Point(-10, 0),
53
+ new Point(5, 0, 1), new Point(-5, 0),
54
+ false,
55
+ );
56
+ expect(result.status).toBe('Inside');
57
+ expect(result.points.length).toBe(0);
244
58
  });
245
59
 
246
- test('Test Intersection.intersectLineLine()', () => {
247
- // Intersection
248
- const line1 = {start: new Point(), end: new Point(0, 10)};
249
- const line2 = {start: new Point(-5, 5), end: new Point(5, 5)};
250
- const result = Intersection.intersectLineLine(line1, line2, false);
251
- expect(result.status).toBe('Intersection');
60
+ test('Test intersectSegmentSegment - line line', () => {
61
+ // Crossing - segments cross in their interiors
62
+ const result = Intersection.intersectSegmentSegment(
63
+ new Point(), new Point(0, 10),
64
+ new Point(-5, 5), new Point(5, 5),
65
+ false,
66
+ );
67
+ expect(result.status).toBe('Perpendicular');
252
68
  expect(result.points.length).toBe(1);
253
69
  expect(result.points[0].x).toBeCloseTo(0);
254
70
  expect(result.points[0].y).toBeCloseTo(5);
255
71
 
256
- // Intersection - Extend
257
- const line3 = {start: new Point(1, 5), end: new Point(5, 5)};
258
- const result1 = Intersection.intersectLineLine(line1, line3, true);
259
- expect(result1.status).toBe('Intersection');
72
+ // Crossing - non-perpendicular crossing
73
+ const resultCross = Intersection.intersectSegmentSegment(
74
+ new Point(0, 0), new Point(10, 5),
75
+ new Point(0, 5), new Point(10, 0),
76
+ false,
77
+ );
78
+ expect(resultCross.status).toBe('Crossing');
79
+
80
+ // Crossing - Extend (perpendicular - vertical boundary, horizontal selected extended)
81
+ const result1 = Intersection.intersectSegmentSegment(
82
+ new Point(), new Point(0, 10),
83
+ new Point(1, 5), new Point(5, 5),
84
+ false, true,
85
+ );
86
+ expect(result1.status).toBe('Perpendicular');
260
87
  expect(result1.points.length).toBe(1);
261
88
  expect(result1.points[0].x).toBeCloseTo(0);
262
89
  expect(result1.points[0].y).toBeCloseTo(5);
263
90
 
264
91
  // No Intersection - Parallel
265
- const line4 = {start: new Point(1, 0), end: new Point(1, 10)};
266
- const result2 = Intersection.intersectLineLine(line1, line4, false);
92
+ const result2 = Intersection.intersectSegmentSegment(
93
+ new Point(), new Point(0, 10),
94
+ new Point(1, 0), new Point(1, 10),
95
+ false,
96
+ );
267
97
  expect(result2.status).toBe('Parallel');
268
98
  expect(result2.points.length).toBe(0);
269
99
 
270
- // No Intersection - Coincident
271
- const line5 = {start: new Point(0, 1), end: new Point(0, 8)};
272
- const result3 = Intersection.intersectLineLine(line1, line5, false);
273
- expect(result3.status).toBe('Coincident');
274
- expect(result3.points.length).toBe(0);
100
+ // Touching - endpoint of one segment lies on interior of the other
101
+ const result3 = Intersection.intersectSegmentSegment(
102
+ new Point(), new Point(0, 10),
103
+ new Point(0, 1), new Point(0, 8),
104
+ false,
105
+ );
106
+ expect(result3.status).toBe('Touching');
107
+ expect(result3.points.length).toBe(1);
108
+
109
+ // No Intersection - Collinear but disjoint (no overlap)
110
+ const result4 = Intersection.intersectSegmentSegment(
111
+ new Point(0, 0), new Point(0, 5),
112
+ new Point(0, 7), new Point(0, 10),
113
+ false,
114
+ );
115
+ expect(result4.status).toBe('None');
116
+ expect(result4.points.length).toBe(0);
117
+
118
+ // Endpoint - segments share an endpoint
119
+ const result5 = Intersection.intersectSegmentSegment(
120
+ new Point(0, 0), new Point(0, 10),
121
+ new Point(0, 10), new Point(10, 10),
122
+ false,
123
+ );
124
+ expect(result5.status).toBe('Endpoint');
125
+ expect(result5.points.length).toBe(1);
126
+ expect(result5.points[0].x).toBeCloseTo(0);
127
+ expect(result5.points[0].y).toBeCloseTo(10);
275
128
  });
276
129
 
277
- test('Test Intersection.intersectPolylineLine()', () => {
130
+ test('Test intersectPolylinePolyline - polyline line', () => {
278
131
  // Intersection
279
- const points = [new Point(), new Point(0, 10)];
280
- const polyline = {points: points};
281
- const line = {start: new Point(-5, 5), end: new Point(5, 5)};
132
+ let points = [new Point(), new Point(0, 10)];
133
+ let polyline = points;
134
+ let line = [new Point(-10, 5), new Point(10, 5)];
282
135
 
283
136
  // Test polyline with single segment
284
- const result = Intersection.intersectPolylineLine(polyline, line, false);
137
+ let result = Intersection.intersectPolylinePolyline(polyline, line, false);
285
138
  expect(result.status).toBe('Intersection');
286
139
  expect(result.points.length).toBe(1);
287
140
  expect(result.points[0].x).toBeCloseTo(0);
@@ -289,26 +142,93 @@ test('Test Intersection.intersectPolylineLine()', () => {
289
142
 
290
143
  // Test polyline with multiple segments
291
144
  points.push(new Point(2, 10), new Point(2, 0));
292
- const result2 = Intersection.intersectPolylineLine(polyline, line, false);
293
- expect(result2.status).toBe('Intersection');
294
- expect(result2.points.length).toBe(2);
295
- expect(result2.points[1].x).toBeCloseTo(2);
296
- expect(result2.points[1].y).toBeCloseTo(5);
145
+ result = Intersection.intersectPolylinePolyline(polyline, line, false);
146
+ expect(result.status).toBe('Intersection');
147
+ expect(result.points.length).toBe(2);
148
+ expect(result.points[1].x).toBeCloseTo(2);
149
+ expect(result.points[1].y).toBeCloseTo(5);
297
150
 
298
151
  // No Intersection
299
- const line2 = {start: new Point(5, 0), end: new Point(5, 10)};
300
- const result3 = Intersection.intersectPolylineLine(polyline, line2, false);
301
- expect(result3.status).toBe('No Intersection');
302
- expect(result3.points.length).toBe(0);
152
+ const line2 = [new Point(5, 0), new Point(5, 10)];
153
+ result = Intersection.intersectPolylinePolyline(polyline, line2, false);
154
+ expect(result.status).toBe('None');
155
+ expect(result.points.length).toBe(0);
156
+
157
+ // Test polyline with 180 degree arc segment - 270 - 90 - negative bulge
158
+ // counter clockwise bulge = +ve, clockwise bulge = -ve,
159
+ points = [new Point(0, 0, -1), new Point(0, 10)];
160
+ polyline = points;
161
+ result = Intersection.intersectPolylinePolyline(polyline, line, false);
162
+ expect(result.status).toBe('Intersection');
163
+ expect(result.points.length).toBe(1);
164
+ expect(result.points[0].x).toBeCloseTo(-5);
165
+ expect(result.points[0].y).toBeCloseTo(5);
166
+
167
+ // Test polyline with 180 degree arc segment - 270 - 90 - positive bulge
168
+ // counter clockwise bulge = +ve, clockwise bulge = -ve,
169
+ points = [new Point(0, 0, 1), new Point(0, 10)];
170
+ polyline = points;
171
+ result = Intersection.intersectPolylinePolyline(polyline, line, false);
172
+ expect(result.status).toBe('Intersection');
173
+ expect(result.points.length).toBe(1);
174
+ expect(result.points[0].x).toBeCloseTo(5);
175
+ expect(result.points[0].y).toBeCloseTo(5);
176
+
177
+ // Test polyline with 180 degree arc segment 180 - 0 - negative bulge
178
+ // counter clockwise bulge = +ve, clockwise bulge = -ve,
179
+ points = [new Point(-5, 5, -1), new Point(5, 5)];
180
+ polyline = points;
181
+ line = [new Point(-10, 5), new Point(10, 5)];
182
+ result = Intersection.intersectPolylinePolyline(polyline, line, false);
183
+ expect(result.status).toBe('Intersection');
184
+ expect(result.points.length).toBe(2);
185
+ expect(result.points[0].x).toBeCloseTo(5);
186
+ expect(result.points[0].y).toBeCloseTo(5);
187
+ expect(result.points[1].x).toBeCloseTo(-5);
188
+ expect(result.points[1].y).toBeCloseTo(5);
189
+
190
+ // Test polyline with 180 degree arc segment 180 - 0 - positive bulge
191
+ // counter clockwise bulge = +ve, clockwise bulge = -ve,
192
+ points = [new Point(-5, 5, 1), new Point(5, 5)];
193
+ polyline = points;
194
+ line = [new Point(-10, 5), new Point(10, 5)];
195
+ result = Intersection.intersectPolylinePolyline(polyline, line, false);
196
+ expect(result.status).toBe('Intersection');
197
+ expect(result.points.length).toBe(2);
198
+ expect(result.points[0].x).toBeCloseTo(5);
199
+ expect(result.points[0].y).toBeCloseTo(5);
200
+ expect(result.points[1].x).toBeCloseTo(-5);
201
+ expect(result.points[1].y).toBeCloseTo(5);
202
+
203
+ // Test polyline with 180 degree arc segment 180 - 0 - negative bulge - offset line
204
+ // counter clockwise bulge = +ve, clockwise bulge = -ve
205
+ // direction: - ccw > 0, cw <= 0
206
+ points = [new Point(-5, 5, -1), new Point(5, 5)];
207
+ polyline = points;
208
+ line = [new Point(0, 7), new Point(10, 7)];
209
+ result = Intersection.intersectPolylinePolyline(polyline, line, false);
210
+ expect(result.status).toBe('Intersection');
211
+ expect(result.points.length).toBe(1);
212
+ expect(result.points[0].x).toBeCloseTo(4.5826);
213
+ expect(result.points[0].y).toBeCloseTo(7);
214
+
215
+ // Test polyline with 180 degree arc segment 180 - 0 - positive bulge - offset line
216
+ // counter clockwise bulge = +ve, clockwise bulge = -ve,
217
+ points = [new Point(-5, 5, 1), new Point(5, 5)];
218
+ polyline = points;
219
+ line = [new Point(0, 7), new Point(10, 7)];
220
+ result = Intersection.intersectPolylinePolyline(polyline, line, false);
221
+ expect(result.status).toBe('None');
222
+ expect(result.points.length).toBe(0);
303
223
  });
304
224
 
305
- test('Test Intersection.intersectPolylineRectangle()', () => {
225
+ test('Test intersectPolylinePolyline with rectangle polyline', () => {
306
226
  const points = [new Point(-5, 5), new Point(5, 5)];
307
- const polyline = {points: points};
308
- const rectangle = {start: new Point(), end: new Point(25, 25)};
227
+ const polyline = points;
228
+ const rectangle = [new Point(), new Point(25, 0), new Point(25, 25), new Point(0, 25), new Point()];
309
229
 
310
230
  // Test polyline with single segment
311
- const result = Intersection.intersectPolylineRectangle(polyline, rectangle, false);
231
+ const result = Intersection.intersectPolylinePolyline(polyline, rectangle, false);
312
232
  expect(result.status).toBe('Intersection');
313
233
  expect(result.points.length).toBe(1);
314
234
  expect(result.points[0].x).toBeCloseTo(0);
@@ -316,103 +236,306 @@ test('Test Intersection.intersectPolylineRectangle()', () => {
316
236
 
317
237
  // Test polyline with multiple segments
318
238
  points.push(new Point(5, 20), new Point(30, 20));
319
- const result2 = Intersection.intersectPolylineRectangle(polyline, rectangle, false);
239
+ const result2 = Intersection.intersectPolylinePolyline(polyline, rectangle, false);
320
240
  expect(result2.status).toBe('Intersection');
321
241
  expect(result2.points.length).toBe(2);
322
- expect(result2.points[0].x).toBeCloseTo(25);
323
- expect(result2.points[0].y).toBeCloseTo(20);
242
+ expect(result2.points[0].x).toBeCloseTo(0);
243
+ expect(result2.points[0].y).toBeCloseTo(5);
244
+ expect(result2.points[1].x).toBeCloseTo(25);
245
+ expect(result2.points[1].y).toBeCloseTo(20);
324
246
 
325
- // No Intersection
247
+ // Overlapping collinear segments
326
248
  const points2 = [new Point(5, 5), new Point(20, 20)];
327
- const polyline2 = {points: points2};
328
- const result3 = Intersection.intersectPolylineLine(polyline2, rectangle, false);
329
- expect(result3.status).toBe('No Intersection');
330
- expect(result3.points.length).toBe(0);
249
+ const polyline2 = points2;
250
+ const result3 = Intersection.intersectPolylinePolyline(polyline2, [new Point(), new Point(25, 25)], false);
251
+ expect(result3.status).toBe('Intersection');
252
+ expect(result3.points.length).toBe(1);
253
+ });
254
+
255
+ test('Test intersectPolylinePolyline - polyline arc', () => {
256
+ // Intersection - polyline(boundary) vs arc segment(selected)
257
+ const polyline = [new Point(), new Point(0, 25)];
258
+ // Arc segment from (10,10) to (-10,10) with bulge=1
259
+ // This creates a CCW semicircular arc with centre (0,10), radius 10
260
+ const arcPolyline = [new Point(10, 10, 1), new Point(-10, 10)];
261
+ const result = Intersection.intersectPolylinePolyline(polyline, arcPolyline, false);
262
+ expect(result.status).toBe('Intersection');
263
+ expect(result.points.length).toBe(1);
264
+ expect(result.points[0].x).toBeCloseTo(0);
265
+ expect(result.points[0].y).toBeCloseTo(20);
331
266
  });
332
267
 
333
- test('Test Intersection.intersectLineRectangle()', () => {
334
- const line1 = {start: new Point(-5, 5), end: new Point(5, 5)};
335
- const rectangle = {start: new Point(), end: new Point(25, 25)};
268
+ test('Test Intersection.intersectPolylinePolyline()', () => {
269
+ // Two crossing polylines with line segments
270
+ const polyline1 = [new Point(-10, 0), new Point(10, 0)];
271
+ const polyline2 = [new Point(0, -10), new Point(0, 10)];
336
272
 
337
- // Test rectangle with single intersection
338
- const result = Intersection.intersectLineRectangle(line1, rectangle, false);
273
+ const result = Intersection.intersectPolylinePolyline(polyline1, polyline2, false);
339
274
  expect(result.status).toBe('Intersection');
340
275
  expect(result.points.length).toBe(1);
341
276
  expect(result.points[0].x).toBeCloseTo(0);
342
- expect(result.points[0].y).toBeCloseTo(5);
277
+ expect(result.points[0].y).toBeCloseTo(0);
343
278
 
344
- // Test rectangle with multiple intersections
345
- const line2 = {start: new Point(-5, 5), end: new Point(30, 20)};
346
- const result2 = Intersection.intersectLineRectangle(line2, rectangle, false);
347
- expect(result2.status).toBe('Intersection');
348
- expect(result2.points.length).toBe(2);
349
- expect(result2.points[0].x).toBeCloseTo(25);
350
- expect(result2.points[0].y).toBeCloseTo(17.857);
351
- expect(result2.points[1].x).toBeCloseTo(0);
352
- expect(result2.points[1].y).toBeCloseTo(7.14);
279
+ // No intersection
280
+ const polyline3 = [new Point(20, 0), new Point(30, 0)];
281
+ const result2 = Intersection.intersectPolylinePolyline(polyline1, polyline3, false);
282
+ expect(result2.status).toBe('None');
283
+ expect(result2.points.length).toBe(0);
353
284
 
354
- // No Intersection
355
- const line3 = {start: new Point(5, 5), end: new Point(20, 20)};
356
- const result3 = Intersection.intersectLineRectangle(line3, rectangle, false);
357
- expect(result3.status).toBe('No Intersection');
358
- expect(result3.points.length).toBe(0);
285
+ // Polyline with arc segment intersecting a line-segment polyline
286
+ // CCW arc from (-5,0) to (5,0) with bulge=1 goes through (0,-5)
287
+ const polyline4 = [new Point(-5, 0, 1), new Point(5, 0)];
288
+ const polyline5 = [new Point(0, -10), new Point(0, -3)];
289
+
290
+ const result3 = Intersection.intersectPolylinePolyline(polyline4, polyline5, false);
291
+ expect(result3.status).toBe('Intersection');
292
+ expect(result3.points.length).toBe(1);
293
+ expect(result3.points[0].x).toBeCloseTo(0);
294
+ expect(result3.points[0].y).toBeCloseTo(-5);
295
+ });
296
+
297
+ test('Test intersectPolylinePolyline - polyline arc with extend', () => {
298
+ // Arc: quarter circle from (10,0) to (0,10) centred at origin, radius 10, CCW
299
+ // Represent as polyline with arc segment
300
+ const arcPolyline = [new Point(10, 0, 1), new Point(0, 10)];
301
+
302
+ // Polyline crossing the arc
303
+ const polyline = [new Point(0, 0), new Point(15, 15)];
304
+
305
+ // Without extend - intersection should be on the arc
306
+ const result1 = Intersection.intersectPolylinePolyline(polyline, arcPolyline, false);
307
+ expect(result1.status).toBe('Intersection');
308
+ expect(result1.points.length).toBe(1);
309
+
310
+ // With extend - all circle intersections returned (not filtered by arc bounds)
311
+ const result2 = Intersection.intersectPolylinePolyline(polyline, arcPolyline, true);
312
+ expect(result2.status).toBe('Intersection');
313
+ expect(result2.points.length).toBeGreaterThanOrEqual(1);
359
314
  });
360
315
 
361
- test('Test Intersection.intersectRectangleLine()', () => {
362
- const line1 = {start: new Point(-5, 5), end: new Point(5, 5)};
363
- const rectangle = {start: new Point(), end: new Point(25, 25)};
316
+ test('Test intersectSegmentSegment - both segments extended (ray-ray equivalent)', () => {
317
+ // Perpendicular lines meeting at origin
318
+ const r1 = Intersection.intersectSegmentSegment(
319
+ new Point(-5, 0), new Point(5, 0),
320
+ new Point(0, -5), new Point(0, 5),
321
+ true, true,
322
+ );
323
+ expect(r1.points.length).toBe(1);
324
+ expect(r1.points[0].x).toBeCloseTo(0);
325
+ expect(r1.points[0].y).toBeCloseTo(0);
326
+
327
+ // Non-perpendicular: lines y=1 and x=1 meeting at (1, 1)
328
+ const r2 = Intersection.intersectSegmentSegment(
329
+ new Point(0, 1), new Point(10, 1),
330
+ new Point(1, 0), new Point(1, 10),
331
+ true, true,
332
+ );
333
+ expect(r2.points.length).toBe(1);
334
+ expect(r2.points[0].x).toBeCloseTo(1);
335
+ expect(r2.points[0].y).toBeCloseTo(1);
336
+
337
+ // 45-degree lines crossing at (5, 5)
338
+ const r3 = Intersection.intersectSegmentSegment(
339
+ new Point(0, 0), new Point(10, 10),
340
+ new Point(10, 0), new Point(0, 10),
341
+ true, true,
342
+ );
343
+ expect(r3.points.length).toBe(1);
344
+ expect(r3.points[0].x).toBeCloseTo(5);
345
+ expect(r3.points[0].y).toBeCloseTo(5);
346
+
347
+ // Parallel lines — should return no points
348
+ const r4 = Intersection.intersectSegmentSegment(
349
+ new Point(0, 0), new Point(10, 0),
350
+ new Point(0, 1), new Point(10, 1),
351
+ true, true,
352
+ );
353
+ expect(r4.points.length).toBe(0);
354
+
355
+ // Disjoint segments that only intersect when both extended
356
+ const r5 = Intersection.intersectSegmentSegment(
357
+ new Point(0, 0), new Point(2, 0),
358
+ new Point(5, -2), new Point(5, -1),
359
+ true, true,
360
+ );
361
+ expect(r5.points.length).toBe(1);
362
+ expect(r5.points[0].x).toBeCloseTo(5);
363
+ expect(r5.points[0].y).toBeCloseTo(0);
364
+ });
364
365
 
365
- // Test rectangle with single intersection
366
- const result = Intersection.intersectRectangleLine(rectangle, line1, false);
366
+ // ------------------------------------------------------------
367
+ // intersectSegmentSegment — circle/arc vs line, non-origin centre
368
+ // These tests specifically exercise the numerical stability fix:
369
+ // computing centreToStart = a1 - c before squaring avoids
370
+ // floating-point cancellation that occurs when the expanded form
371
+ // a1² + c² - 2·a1·c is used with an off-origin circle centre.
372
+ // ------------------------------------------------------------
373
+
374
+ test('intersectSegmentSegment - arc vs line, non-origin centre, two circle intersections but one on arc', () => {
375
+ // Circle centred at (1000, 1000), radius 50.
376
+ // Top point: (1000, 1050), bottom: (1000, 950).
377
+ // Left semicircle arc: top → bottom with bulge=1 (CCW).
378
+ // Horizontal line through centre: (950, 1000) → (1050, 1000).
379
+ // The full circle intersects the line at (950,1000) and (1050,1000),
380
+ // but only (950,1000) lies on the left semicircle arc — the arc filter
381
+ // removes the right-side point, leaving one result.
382
+ const leftArcStart = new Point(1000, 1050, 1);
383
+ const leftArcEnd = new Point(1000, 950);
384
+
385
+ const lineStart = new Point(950, 1000);
386
+ const lineEnd = new Point(1050, 1000);
387
+
388
+ // arc(boundary) vs line(selected)
389
+ const result = Intersection.intersectSegmentSegment(
390
+ leftArcStart, leftArcEnd,
391
+ lineStart, lineEnd,
392
+ false,
393
+ );
394
+ // Only the point on the left semicircle passes the arc filter
367
395
  expect(result.status).toBe('Intersection');
368
396
  expect(result.points.length).toBe(1);
369
- expect(result.points[0].x).toBeCloseTo(0);
370
- expect(result.points[0].y).toBeCloseTo(5);
397
+ expect(result.points[0].x).toBeCloseTo(950);
398
+ expect(result.points[0].y).toBeCloseTo(1000);
399
+ });
371
400
 
372
- // Test rectangle with multiple intersections
373
- const line2 = {start: new Point(-5, 5), end: new Point(30, 20)};
374
- const result2 = Intersection.intersectRectangleLine(rectangle, line2, false);
375
- expect(result2.status).toBe('Intersection');
376
- expect(result2.points.length).toBe(2);
377
- expect(result2.points[0].x).toBeCloseTo(25);
378
- expect(result2.points[0].y).toBeCloseTo(17.857);
379
- expect(result2.points[1].x).toBeCloseTo(0);
380
- expect(result2.points[1].y).toBeCloseTo(7.14);
401
+ test('intersectSegmentSegment - arc vs line, non-origin centre, line outside arc', () => {
402
+ // Same circle at (1000, 1000) radius 50.
403
+ // Line entirely to the right of centre: (1060, 900) → (1060, 1100).
404
+ // This line is outside the circle entirely.
405
+ const arcStart = new Point(1000, 1050, 1);
406
+ const arcEnd = new Point(1000, 950);
407
+
408
+ const result = Intersection.intersectSegmentSegment(
409
+ arcStart, arcEnd,
410
+ new Point(1060, 900), new Point(1060, 1100),
411
+ false,
412
+ );
413
+ expect(result.status).toBe('Outside');
414
+ expect(result.points.length).toBe(0);
415
+ });
381
416
 
382
- // No Intersection
383
- const line3 = {start: new Point(5, 5), end: new Point(20, 20)};
384
- const result3 = Intersection.intersectRectangleLine(rectangle, line3, false);
385
- expect(result3.status).toBe('No Intersection');
386
- expect(result3.points.length).toBe(0);
417
+ test('intersectSegmentSegment - arc vs line, non-origin centre, line inside circle', () => {
418
+ // Same circle at (1000, 1000) radius 50.
419
+ // Short line fully inside the circle: (1000, 990) → (1000, 1010).
420
+ // The circle-line quadratic yields two roots outside [0,1];
421
+ // one u<0 and one u>1 → INSIDE status, no points without extend.
422
+ const arcStart = new Point(1000, 1050, 1);
423
+ const arcEnd = new Point(1000, 950);
424
+
425
+ const result = Intersection.intersectSegmentSegment(
426
+ arcStart, arcEnd,
427
+ new Point(1000, 990), new Point(1000, 1010),
428
+ false,
429
+ );
430
+ expect(result.status).toBe('Inside');
431
+ expect(result.points.length).toBe(0);
387
432
  });
388
433
 
389
- test('Test Intersection.intersectPolygonPolygon()', () => {
390
- // Intersection.intersectPolygonPolygon(points1,points2);
391
- expect(true).toBeTruthy();
392
- // TODO: intersectPolygonPolygon
434
+ test('intersectPolylinePolyline - full circle vs diameter line, non-origin centre', () => {
435
+ // Reproduces the real-world trim failure:
436
+ // Circle centred at (588.75, 354.25), radius 78.136.
437
+ // The line connecting the two points where it intersects the
438
+ // circle is a near-diameter, with large absolute coordinates.
439
+ // Prior to the fix the discriminant underflowed to 0 or negative
440
+ // due to floating-point cancellation, returning no intersections.
441
+ const cx = 588.7494381894326;
442
+ const cy = 354.25057706935644;
443
+ const r = 78.13609384123743;
444
+
445
+ // Circle represented as two CCW semicircular arc segments (matching
446
+ // Circle.toPolylinePoints() output):
447
+ // top = centre.project(0, r) → (cx, cy + r)
448
+ // bot = centre.project(0, -r) → (cx, cy - r)
449
+ const top = new Point(cx, cy + r, 1); // left semicircle start
450
+ const bot = new Point(cx, cy - r, 1); // right semicircle start
451
+ const topClose = new Point(cx, cy + r); // closure point
452
+
453
+ const circlePolyline = [top, bot, topClose];
454
+
455
+ // The failing line from the DXF file — a near-diameter of the circle.
456
+ const linePolyline = [
457
+ new Point(644, 299.00001525878906),
458
+ new Point(533.4988763788651, 409.5011388799238),
459
+ ];
460
+
461
+ const result = Intersection.intersectPolylinePolyline(linePolyline, circlePolyline, false);
462
+ expect(result.status).toBe('Intersection');
463
+ // The line passes through both halves of the circle, yielding two points.
464
+ expect(result.points.length).toBe(2);
465
+ // Each intersection point must lie approximately on the circle.
466
+ for (const pt of result.points) {
467
+ const dist = Math.sqrt((pt.x - cx) ** 2 + (pt.y - cy) ** 2);
468
+ expect(dist).toBeCloseTo(r, 3);
469
+ }
393
470
  });
394
471
 
395
- test('Test Intersection.intersectPolygonRectangle()', () => {
396
- // Intersection.intersectPolygonRectangle(points,r1,r2);
397
- expect(true).toBeTruthy();
398
- // TODO: intersectPolygonRectangle
472
+ // ------------------------------------------------------------
473
+ // intersectCircleCircle — covering the INTERSECTION and OUTSIDE
474
+ // branches of #intersectCircleCircle, including the refactored
475
+ // axialDist / halfChordLength / chordMidpoint / perpOffset logic.
476
+ // ------------------------------------------------------------
477
+
478
+ test('intersectSegmentSegment - arc vs arc, two intersecting circles, single shared point per arc pair', () => {
479
+ // Circle 1: centre (0,0), radius 5 — top CCW semicircle: (5,0) → (-5,0)
480
+ // Circle 2: centre (6,0), radius 5 — top CCW semicircle: (11,0) → (1,0)
481
+ // The two circles intersect at (3,4) and (3,-4).
482
+ // Both top arcs contain (3,4); neither contains (3,-4).
483
+ const result = Intersection.intersectSegmentSegment(
484
+ new Point(5, 0, 1), new Point(-5, 0),
485
+ new Point(11, 0, 1), new Point(1, 0),
486
+ false,
487
+ );
488
+ expect(result.status).toBe('Intersection');
489
+ expect(result.points.length).toBe(1);
490
+ expect(result.points[0].x).toBeCloseTo(3);
491
+ expect(result.points[0].y).toBeCloseTo(4);
399
492
  });
400
493
 
401
- test('Test Intersection.intersectRectangleRectangle()', () => {
402
- const rectangle1 = {start: new Point(), end: new Point(25, 25)};
403
- const rectangle2 = {start: new Point(10, 10), end: new Point(35, 35)};
494
+ test('intersectSegmentSegment - arc vs arc, circles outside each other', () => {
495
+ // Circle 1: centre (0,0), radius 5 — top arc: (5,0) → (-5,0)
496
+ // Circle 2: centre (20,0), radius 5 — top arc: (25,0) → (15,0)
497
+ // cDist (20) > rMax (10): OUTSIDE, no intersection points.
498
+ const result = Intersection.intersectSegmentSegment(
499
+ new Point(5, 0, 1), new Point(-5, 0),
500
+ new Point(25, 0, 1), new Point(15, 0),
501
+ false,
502
+ );
503
+ expect(result.status).toBe('Outside');
504
+ expect(result.points.length).toBe(0);
505
+ });
506
+
507
+ test('intersectPolylinePolyline - two full circles (two arcs each) intersecting at two points', () => {
508
+ // Circle 1: centre (0,0), radius 5.
509
+ // Circle 2: centre (6,0), radius 5.
510
+ // Intersection points: (3,4) and (3,-4).
511
+ // Each circle is represented as two CCW semicircular arc segments.
512
+ const circle1 = [new Point(5, 0, 1), new Point(-5, 0, 1), new Point(5, 0)];
513
+ const circle2 = [new Point(11, 0, 1), new Point(1, 0, 1), new Point(11, 0)];
404
514
 
405
- const result = Intersection.intersectRectangleRectangle(rectangle1, rectangle2, false);
515
+ const result = Intersection.intersectPolylinePolyline(circle1, circle2, false);
406
516
  expect(result.status).toBe('Intersection');
407
517
  expect(result.points.length).toBe(2);
408
- expect(result.points[0].x).toBeCloseTo(25);
409
- expect(result.points[0].y).toBeCloseTo(10);
410
- expect(result.points[1].x).toBeCloseTo(10);
411
- expect(result.points[1].y).toBeCloseTo(25);
412
518
 
413
- // No Intersection
414
- const rectangle3 = {start: new Point(10, 10), end: new Point(20, 20)};
415
- const result2 = Intersection.intersectRectangleRectangle(rectangle1, rectangle3, false);
416
- expect(result2.status).toBe('No Intersection');
417
- expect(result2.points.length).toBe(0);
519
+ // Both points must lie on both circles (radius 5 from each centre).
520
+ for (const pt of result.points) {
521
+ expect(Math.sqrt(pt.x ** 2 + pt.y ** 2)).toBeCloseTo(5);
522
+ expect(Math.sqrt((pt.x - 6) ** 2 + pt.y ** 2)).toBeCloseTo(5);
523
+ }
524
+
525
+ // Verify the specific coordinate values.
526
+ const ys = result.points.map((p) => p.y).sort((a, b) => a - b);
527
+ expect(ys[0]).toBeCloseTo(-4);
528
+ expect(ys[1]).toBeCloseTo(4);
529
+ result.points.forEach((p) => expect(p.x).toBeCloseTo(3));
530
+ });
531
+
532
+ test('intersectPolylinePolyline - two full circles outside each other, no intersection', () => {
533
+ // Circle 1: centre (0,0), radius 5.
534
+ // Circle 2: centre (20,0), radius 5. cDist=20 > rMax=10.
535
+ const circle1 = [new Point(5, 0, 1), new Point(-5, 0, 1), new Point(5, 0)];
536
+ const circle2 = [new Point(25, 0, 1), new Point(15, 0, 1), new Point(25, 0)];
537
+
538
+ const result = Intersection.intersectPolylinePolyline(circle1, circle2, false);
539
+ expect(result.status).toBe('None');
540
+ expect(result.points.length).toBe(0);
418
541
  });