@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,9 +1,44 @@
1
- import {Core} from '../../core/core/core.js';
2
- import {Point} from '../../core/entities/point.js';
3
- import {Trim} from '../../core/tools/trim.js';
1
+ import { Core } from '../../core/core/core.js';
2
+ import { Point } from '../../core/entities/point.js';
3
+ import { Trim } from '../../core/tools/trim.js';
4
+ import { SingleSelection } from '../../core/lib/selectionManager.js';
5
+ import { Strings } from '../../core/lib/strings.js';
6
+ import { expect, jest } from '@jest/globals';
7
+ import { withMockInput } from '../test-helpers/test-helpers.js';
4
8
 
5
9
  const core = new Core();
6
10
 
11
+ // Test cases for user input
12
+ const inputScenarios = [
13
+
14
+ { desc: 'Trim selection',
15
+ inputs: [new SingleSelection(1, new Point())],
16
+ },
17
+ ];
18
+
19
+ test.each(inputScenarios)('Trim.execute handles $desc', async (scenario) => {
20
+ const { inputs } = scenario;
21
+ const actionSpy = jest.fn();
22
+
23
+ // clear all scene entities
24
+ core.scene.clear();
25
+ // create line
26
+ core.scene.addItem('Line', { points: [new Point(), new Point(10, 0)] });
27
+ // create circle
28
+ core.scene.addItem('Circle', { points: [new Point(), new Point(10, 0)] });
29
+ // select line
30
+ core.scene.selectionManager.selectionSet.selectionSet.push(0);
31
+
32
+ await withMockInput(core.scene, inputs, async () => {
33
+ const trim = new Trim();
34
+ await trim.execute();
35
+
36
+ expect(trim.selectedBoundaryItems[0]).toEqual(core.scene.entities.get(0));
37
+ expect(trim.selectedItem).toBe(core.scene.entities.get(1));
38
+ expect(actionSpy).toHaveBeenCalled();
39
+ }, { extraMethods: { actionCommand: () => actionSpy() } });
40
+ });
41
+
7
42
  test('Test Trim.action', () => {
8
43
  const lineOneStart = new Point();
9
44
  const lineOneEnd = new Point(0, 100);
@@ -21,29 +56,31 @@ test('Test Trim.action', () => {
21
56
  * perpendicular lines
22
57
  * trim end from horizontal line
23
58
  */
59
+ // clear all scene entities
60
+ core.scene.clear();
24
61
  // Add items to scene
25
- core.scene.addItem('Line', {points: [lineOneStart, lineOneEnd]});
26
- core.scene.addItem('Line', {points: [lineTwoStart, lineTwoEnd]});
62
+ core.scene.addItem('Line', { points: [lineOneStart, lineOneEnd] });
63
+ core.scene.addItem('Line', { points: [lineTwoStart, lineTwoEnd] });
27
64
  // Select boundary item
28
- core.scene.selectionManager.addToSelectionSet(0);
65
+ trim.selectedBoundaryItems = [core.scene.entities.get(0)];
29
66
  // select item to trim
30
- trim.selectedIndex = 1;
67
+ trim.selectedItem = core.scene.entities.get(1);
31
68
  // set mouse location - required for trim
32
69
  core.mouse.setPosFromScenePoint(new Point(10, 50));
33
70
  // Perform trim
34
71
  trim.action();
35
72
 
36
73
  // line one
37
- expect(core.scene.items[0].points[0].x).toBe(lineOneStart.x);
38
- expect(core.scene.items[0].points[0].y).toBe(lineOneStart.y);
39
- expect(core.scene.items[0].points[1].x).toBe(lineOneEnd.x);
40
- expect(core.scene.items[0].points[1].y).toBe(lineOneEnd.y);
74
+ expect(core.scene.entities.get(0).points[0].x).toBe(lineOneStart.x);
75
+ expect(core.scene.entities.get(0).points[0].y).toBe(lineOneStart.y);
76
+ expect(core.scene.entities.get(0).points[1].x).toBe(lineOneEnd.x);
77
+ expect(core.scene.entities.get(0).points[1].y).toBe(lineOneEnd.y);
41
78
 
42
79
  // line two
43
- expect(core.scene.items[1].points[0].x).toBe(lineTwoStart.x);
44
- expect(core.scene.items[1].points[0].y).toBe(lineTwoStart.y);
45
- expect(core.scene.items[1].points[1].x).toBe(0);
46
- expect(core.scene.items[1].points[1].y).toBe(lineTwoEnd.y);
80
+ expect(core.scene.entities.get(1).points[0].x).toBe(lineTwoStart.x);
81
+ expect(core.scene.entities.get(1).points[0].y).toBe(lineTwoStart.y);
82
+ expect(core.scene.entities.get(1).points[1].x).toBe(0);
83
+ expect(core.scene.entities.get(1).points[1].y).toBe(lineTwoEnd.y);
47
84
 
48
85
  /**
49
86
  * Trim test two
@@ -51,30 +88,30 @@ test('Test Trim.action', () => {
51
88
  * trim start from horizontal line
52
89
  */
53
90
  // clear scene items
54
- core.scene.items = [];
91
+ core.scene.clear();
55
92
  // Add items to scene
56
- core.scene.addItem('Line', {points: [lineOneStart, lineOneEnd]});
57
- core.scene.addItem('Line', {points: [lineTwoStart, lineTwoEnd]});
93
+ core.scene.addItem('Line', { points: [lineOneStart, lineOneEnd] });
94
+ core.scene.addItem('Line', { points: [lineTwoStart, lineTwoEnd] });
58
95
  // Select boundary item
59
- core.scene.selectionManager.addToSelectionSet(0);
96
+ trim.selectedBoundaryItems = [core.scene.entities.get(0)];
60
97
  // select item to trim
61
- trim.selectedIndex = 1;
98
+ trim.selectedItem = core.scene.entities.get(1);
62
99
  // set mouse location - required for trim
63
100
  core.mouse.setPosFromScenePoint(new Point(-10, 50));
64
101
  // Perform trim
65
102
  trim.action();
66
103
 
67
104
  // line one
68
- expect(core.scene.items[0].points[0].x).toBe(lineOneStart.x);
69
- expect(core.scene.items[0].points[0].y).toBe(lineOneStart.y);
70
- expect(core.scene.items[0].points[1].x).toBe(lineOneEnd.x);
71
- expect(core.scene.items[0].points[1].y).toBe(lineOneEnd.y);
105
+ expect(core.scene.entities.get(0).points[0].x).toBe(lineOneStart.x);
106
+ expect(core.scene.entities.get(0).points[0].y).toBe(lineOneStart.y);
107
+ expect(core.scene.entities.get(0).points[1].x).toBe(lineOneEnd.x);
108
+ expect(core.scene.entities.get(0).points[1].y).toBe(lineOneEnd.y);
72
109
 
73
110
  // line two
74
- expect(core.scene.items[1].points[0].x).toBe(0);
75
- expect(core.scene.items[1].points[0].y).toBe(lineTwoStart.y);
76
- expect(core.scene.items[1].points[1].x).toBe(lineTwoEnd.x);
77
- expect(core.scene.items[1].points[1].y).toBe(lineTwoEnd.y);
111
+ expect(core.scene.entities.get(1).points[0].x).toBe(0);
112
+ expect(core.scene.entities.get(1).points[0].y).toBe(lineTwoStart.y);
113
+ expect(core.scene.entities.get(1).points[1].x).toBe(lineTwoEnd.x);
114
+ expect(core.scene.entities.get(1).points[1].y).toBe(lineTwoEnd.y);
78
115
 
79
116
  /**
80
117
  * Trim test three
@@ -82,30 +119,32 @@ test('Test Trim.action', () => {
82
119
  * trim end from vertical line
83
120
  */
84
121
  // clear scene items
85
- core.scene.items = [];
122
+ core.scene.clear();
86
123
  // Add items to scene
87
- core.scene.addItem('Line', {points: [lineOneStart, lineOneEnd]});
88
- core.scene.addItem('Line', {points: [lineTwoStart, lineTwoEnd]});
124
+ core.scene.addItem('Line', { points: [lineOneStart, lineOneEnd] });
125
+ core.scene.addItem('Line', { points: [lineTwoStart, lineTwoEnd] });
89
126
  // Select boundary item
90
- core.scene.selectionManager.addToSelectionSet(1);
127
+ trim.selectedBoundaryItems = [core.scene.entities.get(1)];
91
128
  // select item to trim
92
- trim.selectedIndex = 0;
129
+ trim.selectedItem = core.scene.entities.get(0);
93
130
  // set mouse location - required for trim
94
131
  core.mouse.setPosFromScenePoint(new Point(0, 60));
95
132
  // Perform trim
96
133
  trim.action();
97
134
 
98
- // line one
99
- expect(core.scene.items[0].points[0].x).toBe(lineOneStart.x);
100
- expect(core.scene.items[0].points[0].y).toBe(lineOneStart.y);
101
- expect(core.scene.items[0].points[1].x).toBe(lineOneEnd.x);
102
- expect(core.scene.items[0].points[1].y).toBe(50);
135
+ // index 0 is now line two
136
+ // line Two
137
+ expect(core.scene.entities.get(0).points[0].x).toBe(lineTwoStart.x);
138
+ expect(core.scene.entities.get(0).points[0].y).toBe(lineTwoStart.y);
139
+ expect(core.scene.entities.get(0).points[1].x).toBe(lineTwoEnd.x);
140
+ expect(core.scene.entities.get(0).points[1].y).toBe(lineTwoEnd.y);
103
141
 
104
- // line two
105
- expect(core.scene.items[1].points[0].x).toBe(lineTwoStart.x);
106
- expect(core.scene.items[1].points[0].y).toBe(lineTwoStart.y);
107
- expect(core.scene.items[1].points[1].x).toBe(lineTwoEnd.x);
108
- expect(core.scene.items[1].points[1].y).toBe(lineTwoEnd.y);
142
+ // index 1 is now line one
143
+ // line One
144
+ expect(core.scene.entities.get(1).points[0].x).toBe(lineOneStart.x);
145
+ expect(core.scene.entities.get(1).points[0].y).toBe(lineOneStart.y);
146
+ expect(core.scene.entities.get(1).points[1].x).toBe(lineOneEnd.x);
147
+ expect(core.scene.entities.get(1).points[1].y).toBe(50);
109
148
 
110
149
  /**
111
150
  * Trim test four
@@ -113,30 +152,32 @@ test('Test Trim.action', () => {
113
152
  * trim start from vertical line
114
153
  */
115
154
  // clear scene items
116
- core.scene.items = [];
155
+ core.scene.clear();
117
156
  // Add items to scene
118
- core.scene.addItem('Line', {points: [lineOneStart, lineOneEnd]});
119
- core.scene.addItem('Line', {points: [lineTwoStart, lineTwoEnd]});
157
+ core.scene.addItem('Line', { points: [lineOneStart, lineOneEnd] });
158
+ core.scene.addItem('Line', { points: [lineTwoStart, lineTwoEnd] });
120
159
  // Select boundary item
121
- core.scene.selectionManager.addToSelectionSet(1);
160
+ trim.selectedBoundaryItems = [core.scene.entities.get(1)];
122
161
  // select item to trim
123
- trim.selectedIndex = 0;
162
+ trim.selectedItem = core.scene.entities.get(0);
124
163
  // set mouse location - required for trim
125
164
  core.mouse.setPosFromScenePoint(new Point(0, 30));
126
165
  // Perform trim
127
166
  trim.action();
128
167
 
129
- // line one
130
- expect(core.scene.items[0].points[0].x).toBe(lineOneStart.x);
131
- expect(core.scene.items[0].points[0].y).toBe(50);
132
- expect(core.scene.items[0].points[1].x).toBe(lineOneEnd.x);
133
- expect(core.scene.items[0].points[1].y).toBe(lineOneEnd.y);
134
-
168
+ // index 0 is now line two
135
169
  // line two
136
- expect(core.scene.items[1].points[0].x).toBe(lineTwoStart.x);
137
- expect(core.scene.items[1].points[0].y).toBe(lineTwoStart.y);
138
- expect(core.scene.items[1].points[1].x).toBe(lineTwoEnd.x);
139
- expect(core.scene.items[1].points[1].y).toBe(lineTwoEnd.y);
170
+ expect(core.scene.entities.get(1).points[0].x).toBe(lineOneStart.x);
171
+ expect(core.scene.entities.get(1).points[0].y).toBe(50);
172
+ expect(core.scene.entities.get(1).points[1].x).toBe(lineOneEnd.x);
173
+ expect(core.scene.entities.get(1).points[1].y).toBe(lineOneEnd.y);
174
+
175
+ // index 1 is now line two
176
+ // line one
177
+ expect(core.scene.entities.get(0).points[0].x).toBe(lineTwoStart.x);
178
+ expect(core.scene.entities.get(0).points[0].y).toBe(lineTwoStart.y);
179
+ expect(core.scene.entities.get(0).points[1].x).toBe(lineTwoEnd.x);
180
+ expect(core.scene.entities.get(0).points[1].y).toBe(lineTwoEnd.y);
140
181
 
141
182
 
142
183
  /**
@@ -145,30 +186,30 @@ test('Test Trim.action', () => {
145
186
  * trim end from crossing line
146
187
  */
147
188
  // clear scene items
148
- core.scene.items = [];
189
+ core.scene.clear();
149
190
  // Add items to scene
150
- core.scene.addItem('Line', {points: [lineOneStart, lineOneEnd]});
151
- core.scene.addItem('Line', {points: [crossingLineStart, crossingLineEnd]});
191
+ core.scene.addItem('Line', { points: [lineOneStart, lineOneEnd] });
192
+ core.scene.addItem('Line', { points: [crossingLineStart, crossingLineEnd] });
152
193
  // Select boundary item
153
- core.scene.selectionManager.addToSelectionSet(0);
194
+ trim.selectedBoundaryItems = [core.scene.entities.get(0)];
154
195
  // select item to trim
155
- trim.selectedIndex = 1;
196
+ trim.selectedItem = core.scene.entities.get(1);
156
197
  // set mouse location - required for trim
157
198
  core.mouse.setPosFromScenePoint(new Point(25, 75));
158
199
  // Perform trim
159
200
  trim.action();
160
201
 
161
202
  // line one
162
- expect(core.scene.items[0].points[0].x).toBe(lineOneStart.x);
163
- expect(core.scene.items[0].points[0].y).toBe(lineOneStart.y);
164
- expect(core.scene.items[0].points[1].x).toBe(lineOneEnd.x);
165
- expect(core.scene.items[0].points[1].y).toBe(lineOneEnd.y);
203
+ expect(core.scene.entities.get(0).points[0].x).toBe(lineOneStart.x);
204
+ expect(core.scene.entities.get(0).points[0].y).toBe(lineOneStart.y);
205
+ expect(core.scene.entities.get(0).points[1].x).toBe(lineOneEnd.x);
206
+ expect(core.scene.entities.get(0).points[1].y).toBe(lineOneEnd.y);
166
207
 
167
208
  // crossing line
168
- expect(core.scene.items[1].points[0].x).toBe(crossingLineStart.x);
169
- expect(core.scene.items[1].points[0].y).toBe(crossingLineStart.y);
170
- expect(core.scene.items[1].points[1].x).toBe(0);
171
- expect(core.scene.items[1].points[1].y).toBe(50);
209
+ expect(core.scene.entities.get(1).points[0].x).toBe(crossingLineStart.x);
210
+ expect(core.scene.entities.get(1).points[0].y).toBe(crossingLineStart.y);
211
+ expect(core.scene.entities.get(1).points[1].x).toBe(0);
212
+ expect(core.scene.entities.get(1).points[1].y).toBe(50);
172
213
 
173
214
  /**
174
215
  * Trim test six
@@ -176,28 +217,322 @@ test('Test Trim.action', () => {
176
217
  * trim start from crossing line
177
218
  */
178
219
  // clear scene items
179
- core.scene.items = [];
220
+ core.scene.clear();
180
221
  // Add items to scene
181
- core.scene.addItem('Line', {points: [lineOneStart, lineOneEnd]});
182
- core.scene.addItem('Line', {points: [crossingLineStart, crossingLineEnd]});
222
+ core.scene.addItem('Line', { points: [lineOneStart, lineOneEnd] });
223
+ core.scene.addItem('Line', { points: [crossingLineStart, crossingLineEnd] });
183
224
  // Select boundary item
184
- core.scene.selectionManager.addToSelectionSet(0);
225
+ trim.selectedBoundaryItems = [core.scene.entities.get(0)];
185
226
  // select item to trim
186
- trim.selectedIndex = 1;
227
+ trim.selectedItem = core.scene.entities.get(1);
187
228
  // set mouse location - required for trim
188
229
  core.mouse.setPosFromScenePoint(new Point(-25, 25));
189
230
  // Perform trim
190
231
  trim.action();
191
232
 
192
233
  // line one
193
- expect(core.scene.items[0].points[0].x).toBe(lineOneStart.x);
194
- expect(core.scene.items[0].points[0].y).toBe(lineOneStart.y);
195
- expect(core.scene.items[0].points[1].x).toBe(lineOneEnd.x);
196
- expect(core.scene.items[0].points[1].y).toBe(lineOneEnd.y);
234
+ expect(core.scene.entities.get(0).points[0].x).toBe(lineOneStart.x);
235
+ expect(core.scene.entities.get(0).points[0].y).toBe(lineOneStart.y);
236
+ expect(core.scene.entities.get(0).points[1].x).toBe(lineOneEnd.x);
237
+ expect(core.scene.entities.get(0).points[1].y).toBe(lineOneEnd.y);
197
238
 
198
239
  // crossing line
199
- expect(core.scene.items[1].points[0].x).toBe(0);
200
- expect(core.scene.items[1].points[0].y).toBe(50);
201
- expect(core.scene.items[1].points[1].x).toBe(crossingLineEnd.x);
202
- expect(core.scene.items[1].points[1].y).toBe(crossingLineEnd.y);
240
+ expect(core.scene.entities.get(1).points[0].x).toBe(0);
241
+ expect(core.scene.entities.get(1).points[0].y).toBe(50);
242
+ expect(core.scene.entities.get(1).points[1].x).toBe(crossingLineEnd.x);
243
+ expect(core.scene.entities.get(1).points[1].y).toBe(crossingLineEnd.y);
244
+ });
245
+
246
+ test('Test Trim.action polyline - trim end segment', () => {
247
+ // Polyline from (0,0) to (50,0) to (100,0)
248
+ // Vertical line at x=75 as boundary
249
+ // Mouse near (90,0) - to the right of the boundary
250
+ // Expected: Polyline trimmed to (0,0) (50,0) (75,0)
251
+
252
+ const trim = new Trim();
253
+ core.scene.clear();
254
+
255
+ core.scene.addItem('Lwpolyline', { points: [new Point(0, 0), new Point(50, 0), new Point(100, 0)] });
256
+ core.scene.addItem('Line', { points: [new Point(75, -50), new Point(75, 50)] });
257
+
258
+ trim.selectedBoundaryItems = [core.scene.entities.get(1)];
259
+ trim.selectedItem = core.scene.entities.get(0);
260
+ core.mouse.setPosFromScenePoint(new Point(90, 0));
261
+ trim.action();
262
+
263
+ // Original polyline removed, new one added, boundary line remains
264
+ expect(core.scene.entities.count()).toBe(2);
265
+
266
+ // Find entities by type
267
+ let boundary;
268
+ let trimmed;
269
+ for (let i = 0; i < core.scene.entities.count(); i++) {
270
+ const entity = core.scene.entities.get(i);
271
+ if (entity.type === 'Line') boundary = entity;
272
+ if (entity.type === 'Polyline') trimmed = entity;
273
+ }
274
+
275
+ // Boundary line unchanged
276
+ expect(boundary.points[0].x).toBe(75);
277
+ expect(boundary.points[0].y).toBe(-50);
278
+
279
+ // Trimmed polyline
280
+ expect(trimmed.points.length).toBe(3);
281
+ expect(trimmed.points[0].x).toBe(0);
282
+ expect(trimmed.points[0].y).toBe(0);
283
+ expect(trimmed.points[1].x).toBe(50);
284
+ expect(trimmed.points[1].y).toBe(0);
285
+ expect(trimmed.points[2].x).toBe(75);
286
+ expect(trimmed.points[2].y).toBe(0);
287
+ });
288
+
289
+ test('Test Trim.action polyline - trim start segment', () => {
290
+ // Polyline from (0,0) to (50,0) to (100,0)
291
+ // Vertical line at x=25 as boundary
292
+ // Mouse near (10,0) - to the left of the boundary
293
+ // Expected: Polyline trimmed to (25,0) (50,0) (100,0)
294
+
295
+ const trim = new Trim();
296
+ core.scene.clear();
297
+
298
+ core.scene.addItem('Lwpolyline', { points: [new Point(0, 0), new Point(50, 0), new Point(100, 0)] });
299
+ core.scene.addItem('Line', { points: [new Point(25, -50), new Point(25, 50)] });
300
+
301
+ trim.selectedBoundaryItems = [core.scene.entities.get(1)];
302
+ trim.selectedItem = core.scene.entities.get(0);
303
+ core.mouse.setPosFromScenePoint(new Point(10, 0));
304
+ trim.action();
305
+
306
+ // Original polyline removed, new one added, boundary line remains
307
+ expect(core.scene.entities.count()).toBe(2);
308
+
309
+ // Find trimmed polyline by type
310
+ let trimmed;
311
+ for (let i = 0; i < core.scene.entities.count(); i++) {
312
+ if (core.scene.entities.get(i).type === 'Polyline') {
313
+ trimmed = core.scene.entities.get(i);
314
+ }
315
+ }
316
+
317
+ expect(trimmed).toBeDefined();
318
+ expect(trimmed.points.length).toBe(3);
319
+ expect(trimmed.points[0].x).toBe(25);
320
+ expect(trimmed.points[0].y).toBe(0);
321
+ expect(trimmed.points[1].x).toBe(50);
322
+ expect(trimmed.points[1].y).toBe(0);
323
+ expect(trimmed.points[2].x).toBe(100);
324
+ expect(trimmed.points[2].y).toBe(0);
325
+ });
326
+
327
+ test('Test Trim.action polyline - trim middle segment', () => {
328
+ // Polyline from (0,0) to (50,0) to (100,0) to (150,0)
329
+ // Vertical line at x=25 and x=75 as boundary
330
+ // Mouse near (50,0) - between the two boundaries
331
+ // Expected: Two polylines: (0,0)-(25,0) and (75,0)-(100,0)-(150,0)
332
+
333
+ const trim = new Trim();
334
+ core.scene.clear();
335
+
336
+ core.scene.addItem('Lwpolyline', { points: [new Point(0, 0), new Point(50, 0), new Point(100, 0), new Point(150, 0)] });
337
+ core.scene.addItem('Line', { points: [new Point(25, -50), new Point(25, 50)] });
338
+ core.scene.addItem('Line', { points: [new Point(75, -50), new Point(75, 50)] });
339
+
340
+ trim.selectedBoundaryItems = [core.scene.entities.get(1), core.scene.entities.get(2)];
341
+ trim.selectedItem = core.scene.entities.get(0);
342
+ core.mouse.setPosFromScenePoint(new Point(50, 0));
343
+ trim.action();
344
+
345
+ // Two boundary lines remain, original polyline removed, two new polylines added
346
+ // Count entities - should have 4 (2 lines + 2 polylines)
347
+ expect(core.scene.entities.count()).toBe(4);
348
+
349
+ // Find the two polylines
350
+ const polylines = [];
351
+ for (let i = 0; i < core.scene.entities.count(); i++) {
352
+ const entity = core.scene.entities.get(i);
353
+ if (entity.type === 'Polyline') {
354
+ polylines.push(entity);
355
+ }
356
+ }
357
+
358
+ expect(polylines.length).toBe(2);
359
+
360
+ // Sort polylines by first point x
361
+ polylines.sort((a, b) => a.points[0].x - b.points[0].x);
362
+
363
+ // First polyline: (0,0) to (25,0)
364
+ expect(polylines[0].points.length).toBe(2);
365
+ expect(polylines[0].points[0].x).toBe(0);
366
+ expect(polylines[0].points[1].x).toBe(25);
367
+
368
+ // Second polyline: (75,0) to (100,0) to (150,0)
369
+ expect(polylines[1].points.length).toBe(3);
370
+ expect(polylines[1].points[0].x).toBe(75);
371
+ expect(polylines[1].points[1].x).toBe(100);
372
+ expect(polylines[1].points[2].x).toBe(150);
373
+ });
374
+
375
+ test('Test Trim.action polyline - trim bulged arc segment', () => {
376
+ // Polyline: (0,0) with bulge=1 -> arc -> (100,0) -> straight -> (200,0)
377
+ // With bulge=1 (CCW) the semicircular arc goes through (50,-50)
378
+ // Arc center: (50,0), radius: 50
379
+ // Boundary: vertical line at x=50
380
+ // Intersection at (50,-50) — bottom of semicircle
381
+ // Mouse at (75,-40) — right of intersection on the arc
382
+ // Expected: polyline trimmed to [(0,0) with partial bulge, (50,-50)]
383
+
384
+ const trim = new Trim();
385
+ core.scene.clear();
386
+
387
+ core.scene.addItem('Lwpolyline', { points: [new Point(0, 0, 1), new Point(100, 0), new Point(200, 0)] });
388
+ core.scene.addItem('Line', { points: [new Point(50, -100), new Point(50, 100)] });
389
+
390
+ trim.selectedBoundaryItems = [core.scene.entities.get(1)];
391
+ trim.selectedItem = core.scene.entities.get(0);
392
+ core.mouse.setPosFromScenePoint(new Point(75, -40));
393
+ trim.action();
394
+
395
+ // Original polyline removed, one new trimmed piece added, boundary remains
396
+ expect(core.scene.entities.count()).toBe(2);
397
+
398
+ // Find the trimmed polyline
399
+ let trimmed;
400
+ for (let i = 0; i < core.scene.entities.count(); i++) {
401
+ if (core.scene.entities.get(i).type === 'Polyline') {
402
+ trimmed = core.scene.entities.get(i);
403
+ }
404
+ }
405
+
406
+ expect(trimmed).toBeDefined();
407
+ expect(trimmed.points.length).toBe(2);
408
+
409
+ // First point: (0,0) with partial bulge for 90° arc
410
+ // Original 180° arc split at midpoint -> 90° arc
411
+ // bulge = tan(includedAngle/4) = tan(π/8) = √2 - 1
412
+ expect(trimmed.points[0].x).toBeCloseTo(0, 5);
413
+ expect(trimmed.points[0].y).toBeCloseTo(0, 5);
414
+ expect(trimmed.points[0].bulge).toBeCloseTo(Math.tan(Math.PI / 8), 5);
415
+
416
+ // Second point: intersection at (50,-50), no bulge
417
+ expect(trimmed.points[1].x).toBeCloseTo(50, 5);
418
+ expect(trimmed.points[1].y).toBeCloseTo(-50, 5);
419
+ expect(trimmed.points[1].bulge).toBe(0);
420
+ });
421
+
422
+ test('Trim.register returns correct metadata', () => {
423
+ const reg = Trim.register();
424
+ expect(reg.command).toBe('Trim');
425
+ expect(reg.shortcut).toBe('TR');
426
+ expect(reg.type).toBe('Tool');
427
+ });
428
+
429
+ test('Trim.preview does not throw', () => {
430
+ const trim = new Trim();
431
+ expect(() => trim.preview()).not.toThrow();
432
+ });
433
+
434
+ test('Trim.execute returns early when boundary input is undefined', async () => {
435
+ core.scene.clear();
436
+ core.scene.selectionManager.reset();
437
+ core.scene.addItem('Line', { points: [new Point(), new Point(10, 0)] });
438
+
439
+ await withMockInput(core.scene, [undefined], async () => {
440
+ const trim = new Trim();
441
+ await trim.execute();
442
+ expect(trim.selectedBoundaryItems).toHaveLength(0);
443
+ });
444
+ });
445
+
446
+ test('Trim.execute exits selection loop when selection input is undefined', async () => {
447
+ core.scene.clear();
448
+ core.scene.selectionManager.reset();
449
+ core.scene.addItem('Line', { points: [new Point(), new Point(10, 0)] });
450
+ core.scene.addItem('Circle', { points: [new Point(), new Point(5, 0)] });
451
+ // preselect first item as boundary
452
+ core.scene.selectionManager.selectionSet.selectionSet.push(0);
453
+
454
+ const executeCommandSpy = jest.fn();
455
+ await withMockInput(core.scene, [undefined], async () => {
456
+ const trim = new Trim();
457
+ await trim.execute();
458
+ expect(trim.selectedBoundaryItems).toHaveLength(1);
459
+ expect(executeCommandSpy).toHaveBeenCalled();
460
+ }, { extraMethods: { executeCommand: () => executeCommandSpy() } });
461
+ });
462
+
463
+ test('Trim.execute calls actionCommand once per trim selection', async () => {
464
+ core.scene.clear();
465
+ core.scene.selectionManager.reset();
466
+ core.scene.addItem('Line', { points: [new Point(0, -10), new Point(0, 10)] });
467
+ core.scene.addItem('Circle', { points: [new Point(-5, 0), new Point(0, 0)] });
468
+ core.scene.addItem('Circle', { points: [new Point(-5, 0), new Point(0, 0)] });
469
+ // preselect line as boundary
470
+ core.scene.selectionManager.selectionSet.selectionSet.push(0);
471
+
472
+ const actionSpy = jest.fn();
473
+ await withMockInput(
474
+ core.scene,
475
+ [new SingleSelection(1, new Point()), new SingleSelection(2, new Point()), undefined],
476
+ async () => {
477
+ const trim = new Trim();
478
+ await trim.execute();
479
+ expect(actionSpy).toHaveBeenCalledTimes(2);
480
+ },
481
+ { extraMethods: { actionCommand: () => actionSpy() } },
482
+ );
483
+ });
484
+
485
+ test('Trim.action does nothing when selectedItem is null', () => {
486
+ core.scene.clear();
487
+ core.scene.addItem('Line', { points: [new Point(), new Point(10, 0)] });
488
+ const trim = new Trim();
489
+ trim.selectedItem = null;
490
+ trim.selectedBoundaryItems = [core.scene.entities.get(0)];
491
+
492
+ expect(() => trim.action()).not.toThrow();
493
+ });
494
+
495
+ test('Trim.action does nothing when selectedBoundaryItems is empty', () => {
496
+ core.scene.clear();
497
+ core.scene.addItem('Line', { points: [new Point(), new Point(10, 0)] });
498
+ const trim = new Trim();
499
+ trim.selectedItem = core.scene.entities.get(0);
500
+ trim.selectedBoundaryItems = [];
501
+
502
+ expect(() => trim.action()).not.toThrow();
503
+ expect(trim.selectedItem).toBeNull();
504
+ });
505
+
506
+ test('Trim.action notifies when no intersection found (parallel lines)', () => {
507
+ core.scene.clear();
508
+ // Two parallel vertical lines - no intersection
509
+ core.scene.addItem('Line', { points: [new Point(0, 0), new Point(0, 100)] });
510
+ core.scene.addItem('Line', { points: [new Point(10, 0), new Point(10, 100)] });
511
+
512
+ const trim = new Trim();
513
+ trim.selectedBoundaryItems = [core.scene.entities.get(0)];
514
+ trim.selectedItem = core.scene.entities.get(1);
515
+
516
+ const notifySpy = jest.spyOn(core, 'notify').mockImplementation(() => {});
517
+ trim.action();
518
+
519
+ expect(notifySpy).toHaveBeenCalledWith(expect.stringContaining(Strings.Message.NOTRIM));
520
+ expect(trim.selectedItem).toBeNull();
521
+ notifySpy.mockRestore();
522
+ });
523
+
524
+ test('Trim.action notifies when boundary item equals selected item', () => {
525
+ core.scene.clear();
526
+ core.scene.addItem('Line', { points: [new Point(0, 0), new Point(100, 0)] });
527
+ const entity = core.scene.entities.get(0);
528
+
529
+ const trim = new Trim();
530
+ trim.selectedBoundaryItems = [entity];
531
+ trim.selectedItem = entity;
532
+
533
+ const notifySpy = jest.spyOn(core, 'notify').mockImplementation(() => {});
534
+ trim.action();
535
+
536
+ expect(notifySpy).toHaveBeenCalledWith(expect.stringContaining(Strings.Message.NOTRIM));
537
+ notifySpy.mockRestore();
203
538
  });