@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
@@ -0,0 +1,606 @@
1
+ import { Hatch } from '../../core/entities/hatch.js';
2
+ import { Point } from '../../core/entities/point.js';
3
+ import { DesignCore } from '../../core/designCore.js';
4
+
5
+ import { BasePolyline } from '../../core/entities/basePolyline.js';
6
+ import { BoundingBox } from '../../core/lib/boundingBox.js';
7
+ import { Circle } from '../../core/entities/circle.js';
8
+ import { Line } from '../../core/entities/line.js';
9
+ import { Polyline } from '../../core/entities/polyline.js';
10
+ import { Arc } from '../../core/entities/arc.js';
11
+ import { Text } from '../../core/entities/text.js';
12
+
13
+ import { File, withMockInput } from '../test-helpers/test-helpers.js';
14
+
15
+ import { Core } from '../../core/core/core.js';
16
+
17
+ // initialise core
18
+ new Core();
19
+
20
+ const points = [new Point(100, 100, 1), new Point(200, 100, 1)];
21
+ const boundaryShape = new BasePolyline({ points: points });
22
+
23
+ const hatch = new Hatch({ handle: '1' });
24
+ hatch.childEntities = [boundaryShape];
25
+
26
+ const hatchInputScenarios = [
27
+ {
28
+ desc: 'simple rectangle',
29
+ boundaryItems: [new Line({ points: [new Point(0, 0), new Point(10, 0)] }), new Line({ points: [new Point(10, 0), new Point(10, 10)] }),
30
+ new Line({ points: [new Point(10, 10), new Point(0, 10)] }), new Line({ points: [new Point(0, 10), new Point(0, 0)] })],
31
+ pointCount: 4,
32
+ pattern: 'ANSI31',
33
+ scale: 1,
34
+ angle: 0,
35
+ // expectedPoints: 4,
36
+ expectedPattern: 'ANSI31',
37
+ expectedScale: 1,
38
+ expectedAngle: 0,
39
+ },
40
+ {
41
+ desc: 'triangle, custom pattern and scale',
42
+ boundaryItems: [new Line({ points: [new Point(0, 0), new Point(5, 10)] }), new Line({ points: [new Point(5, 10), new Point(10, 0)] }),
43
+ new Line({ points: [new Point(10, 0), new Point(0, 0)] })],
44
+ pointCount: 3,
45
+ pattern: 'SOLID',
46
+ scale: 2,
47
+ angle: 45,
48
+ // expectedPoints: 3,
49
+ expectedPattern: 'SOLID',
50
+ expectedScale: 2,
51
+ expectedAngle: 45,
52
+ },
53
+ ];
54
+
55
+ test.each(hatchInputScenarios)('Hatch.execute handles $desc', async (scenario) => {
56
+ const { boundaryItems, pointCount, pattern, scale, angle, expectedPattern, expectedScale, expectedAngle } = scenario;
57
+
58
+ await withMockInput(DesignCore.Scene, [true], async () => {
59
+ // Manual mock for selected items
60
+ DesignCore.Scene.selectionManager.selectedItems = [];
61
+
62
+ for (let i = 0; i < boundaryItems.length; i++) {
63
+ DesignCore.Scene.selectionManager.selectedItems.push(boundaryItems[i]);
64
+ }
65
+
66
+ const hatch = new Hatch({ patternName: pattern, scale: scale, angle: angle });
67
+
68
+ await hatch.execute();
69
+
70
+ expect(hatch.childEntities.length).toBe(1);
71
+
72
+ expect(hatch.childEntities[0].points.length).toBe(pointCount); // closed polyline adds extra point
73
+
74
+ expect(hatch.points.length).toBe(2);
75
+ // hatch first point should be at 0,0
76
+ expect(hatch.points.at(0).x).toBe(0);
77
+ expect(hatch.points.at(0).y).toBe(0);
78
+ // hatch last point should be at 1,1
79
+ expect(hatch.points.at(-1).x).toBe(1);
80
+ expect(hatch.points.at(-1).y).toBe(1);
81
+ expect(hatch.pattern).toBe(expectedPattern);
82
+ expect(hatch.scale).toBe(expectedScale);
83
+ expect(hatch.angle).toBe(expectedAngle);
84
+ expect(hatch.solid).toBe(expectedPattern === 'SOLID');
85
+ });
86
+ });
87
+
88
+ test('Test Hatch', () => {
89
+ const data =
90
+ {
91
+ '0': 'HATCH',
92
+ '2': 'HONEY', // hatch name
93
+ // '8': '0', // layer name
94
+ '41': 2, // Pattern scale
95
+ // '43': 0, // Pattern line base X
96
+ // '44': 0, // Pattern line base Y
97
+ // '45': -2.245064030267288, // Pattern line offset x
98
+ // '46': 2.245064030267288, // Pattern line offset y
99
+ // '47': 0.5126851563522042, // pixel size
100
+ '52': 45, // pattern angle
101
+ // '53': 45, // Pattern line angle
102
+ '70': 0, // Solid fill flag
103
+ // '71': 1, // Associativity flag
104
+ // '72': [0, 1], // Edge type
105
+ // '73': [1, 1], // Boundary annotation flag
106
+ // '75': 1, // Hatch style
107
+ // '76': 1, // Hatch pattern type
108
+ // '77': 0, // Hatch pattern double flag
109
+ // '78': 1, // Number of pattern definition lines
110
+ // '79': 0, // Number of dash length items
111
+ // '91': 2, // Number of boundary path loops
112
+ // '92': [7, 7], // Boundary path type flag
113
+ // '93': [4, 2], // Number of edges in this boundary path / number of points in polyline
114
+ // '97': [1, 1], // Number of source boundary objects
115
+ // '98': 2, // Number of seed points
116
+ };
117
+
118
+ const newHatch = new Hatch(data);
119
+
120
+ expect(newHatch.patternName).toBe('HONEY');
121
+ expect(newHatch.angle).toBe(45);
122
+ expect(newHatch.scale).toBe(2);
123
+ expect(newHatch.solid).toBe(false);
124
+ });
125
+
126
+ test('Test Hatch.getDataValue', () => {
127
+ const data =
128
+ {
129
+ '2': 'ANSI31', // hatch name
130
+ '72': [0, 1], // Edge type
131
+ };
132
+
133
+ expect(hatch.getDataValue(data, 2)).toBe('ANSI31');
134
+ expect(hatch.getDataValue(data, 72)).toBe(0);
135
+ });
136
+
137
+ test('Test Hatch.getPatternName', () => {
138
+ expect(hatch.getPatternName()).toBe('ANSI31');
139
+ });
140
+
141
+ test('Test Hatch.setPatternName', () => {
142
+ expect(hatch.solid).toBe(false);
143
+ hatch.setPatternName('Solid');
144
+ expect(hatch.pattern).toBe('SOLID');
145
+ expect(hatch.solid).toBe(true);
146
+
147
+ hatch.setPatternName('Ansi31');
148
+ expect(hatch.pattern).toBe('ANSI31');
149
+ expect(hatch.solid).toBe(false);
150
+ });
151
+
152
+ test('Test Hatch.closestPoint', () => {
153
+ // inside
154
+ const point1 = new Point(150, 100);
155
+ const closest1 = hatch.closestPoint(point1);
156
+ expect(closest1[0].x).toBeCloseTo(150);
157
+ expect(closest1[0].y).toBeCloseTo(100);
158
+ expect(closest1[1]).toBe(0);
159
+
160
+ // outside
161
+ const point2 = new Point();
162
+ const closest2 = hatch.closestPoint(point2);
163
+ expect(closest2[0].x).toBeCloseTo(0);
164
+ expect(closest2[0].y).toBeCloseTo(0);
165
+ expect(closest2[1]).toBeCloseTo(Infinity);
166
+ });
167
+
168
+ test('Test Hatch.isInside', () => {
169
+ // Inside Points
170
+ // upper right quad of circle
171
+ expect(hatch.isInside(new Point(165, 115))).toBe(true);
172
+ // upper left quad of circle
173
+ expect(hatch.isInside(new Point(130, 115))).toBe(true);
174
+ // lower left quad of circle
175
+ expect(hatch.isInside(new Point(130, 80))).toBe(true);
176
+ // lower right quad of circle
177
+ expect(hatch.isInside(new Point(165, 80))).toBe(true);
178
+ // middle of circle
179
+ expect(hatch.isInside(new Point(150, 100))).toBe(true);
180
+
181
+ // Outside Points
182
+ // upper right quad of circle
183
+ expect(hatch.isInside(new Point(190, 140))).toBe(false);
184
+ // upper left quad of circle
185
+ expect(hatch.isInside(new Point(110, 140))).toBe(false);
186
+ // lower left quad of circle
187
+ expect(hatch.isInside(new Point(110, 60))).toBe(false);
188
+ // lower right quad of circle
189
+ expect(hatch.isInside(new Point(190, 60))).toBe(false);
190
+ // middle of circle
191
+ expect(hatch.isInside(new Point(205, 100))).toBe(false);
192
+ });
193
+
194
+ test('Test Hatch.boundingBox', () => {
195
+ expect(hatch.boundingBox().xMin).toBe(100);
196
+ expect(hatch.boundingBox().xMax).toBe(200);
197
+ expect(hatch.boundingBox().yMin).toBe(50);
198
+ expect(hatch.boundingBox().yMax).toBe(150);
199
+ });
200
+
201
+ test('Test Hatch.dxf', () => {
202
+ let file = new File();
203
+ hatch.dxf(file);
204
+ // console.log(file.contents);
205
+
206
+ let dxfString = `0
207
+ HATCH
208
+ 5
209
+ 1
210
+ 100
211
+ AcDbEntity
212
+ 8
213
+ 0
214
+ 100
215
+ AcDbHatch
216
+ 10
217
+ 0.0
218
+ 20
219
+ 0.0
220
+ 30
221
+ 0.0
222
+ 210
223
+ 0.0
224
+ 220
225
+ 0.0
226
+ 230
227
+ 1.0
228
+ 2
229
+ ANSI31
230
+ 70
231
+ 0
232
+ 71
233
+ 0
234
+ 91
235
+ 1
236
+ 92
237
+ 7
238
+ 72
239
+ 1
240
+ 73
241
+ 1
242
+ 93
243
+ 2
244
+ 10
245
+ 100
246
+ 20
247
+ 100
248
+ 42
249
+ 1
250
+ 10
251
+ 200
252
+ 20
253
+ 100
254
+ 42
255
+ 1
256
+ 97
257
+ 0
258
+ 75
259
+ 1
260
+ 76
261
+ 1
262
+ 52
263
+ 0
264
+ 41
265
+ 1
266
+ 77
267
+ 0
268
+ 78
269
+ 1
270
+ 53
271
+ 45
272
+ 43
273
+ 0
274
+ 44
275
+ 0
276
+ 45
277
+ -2.245064030267288
278
+ 46
279
+ 2.2450640302672884
280
+ 79
281
+ 0
282
+ 47
283
+ 0.5
284
+ 98
285
+ 1
286
+ 10
287
+ 1
288
+ 20
289
+ 1
290
+ `;
291
+ expect(file.contents).toEqual(dxfString);
292
+
293
+ // create new entity from entity data to ensure all props are loaded
294
+ const newHatch = new Hatch(hatch);
295
+ file = new File();
296
+ newHatch.dxf(file);
297
+
298
+
299
+ // Export rotated and scaled
300
+ const rotatedScaleHatch = new Hatch({ handle: '1' });
301
+ rotatedScaleHatch.angle = 45;
302
+ rotatedScaleHatch.scale = 2;
303
+
304
+ file = new File();
305
+ rotatedScaleHatch.dxf(file);
306
+ // console.log(file.contents);
307
+
308
+ // Don't export hatch if no boundary shapes
309
+ expect(file.contents).toEqual('');
310
+
311
+ const shape = new Circle({ points: [new Point(0, 0), new Point(10, 0)] });
312
+ const boundary = rotatedScaleHatch.processSelection([shape]);
313
+ expect(boundary[0]).toBeInstanceOf(Polyline);
314
+
315
+ rotatedScaleHatch.childEntities = boundary;
316
+ rotatedScaleHatch.dxf(file);
317
+ // console.log(file.contents);
318
+
319
+ dxfString = `0
320
+ HATCH
321
+ 5
322
+ 1
323
+ 100
324
+ AcDbEntity
325
+ 8
326
+ 0
327
+ 100
328
+ AcDbHatch
329
+ 10
330
+ 0.0
331
+ 20
332
+ 0.0
333
+ 30
334
+ 0.0
335
+ 210
336
+ 0.0
337
+ 220
338
+ 0.0
339
+ 230
340
+ 1.0
341
+ 2
342
+ ANSI31
343
+ 70
344
+ 0
345
+ 71
346
+ 0
347
+ 91
348
+ 1
349
+ 92
350
+ 7
351
+ 72
352
+ 1
353
+ 73
354
+ 1
355
+ 93
356
+ 2
357
+ 10
358
+ 10
359
+ 20
360
+ 0
361
+ 42
362
+ 1
363
+ 10
364
+ -10
365
+ 20
366
+ 0
367
+ 42
368
+ 1
369
+ 97
370
+ 0
371
+ 75
372
+ 1
373
+ 76
374
+ 1
375
+ 52
376
+ 45
377
+ 41
378
+ 2
379
+ 77
380
+ 0
381
+ 78
382
+ 1
383
+ 53
384
+ 90
385
+ 43
386
+ 0
387
+ 44
388
+ 0
389
+ 45
390
+ -6.35
391
+ 46
392
+ 3.888253587292846e-16
393
+ 79
394
+ 0
395
+ 47
396
+ 0.5
397
+ 98
398
+ 1
399
+ 10
400
+ 1
401
+ 20
402
+ 1
403
+ `;
404
+ expect(file.contents).toEqual(dxfString);
405
+ });
406
+
407
+ test('Test Hatch.processBoundaryData', () => {
408
+ // create data defining a square and a circle
409
+ const data =
410
+ {
411
+ '0': 'HATCH',
412
+ // '2': 'ANSI31', // hatch name
413
+ // '8': '0', // layer name
414
+ // '41': 1, // Pattern scale
415
+ // '43': 0, // Pattern line base X
416
+ // '44': 0, // Pattern line base Y
417
+ // '45': -2.245064030267288, // Pattern line offset x
418
+ // '46': 2.245064030267288, // Pattern line offset y
419
+ // '47': 0.5126851563522042, // pixel size
420
+ // '52': 0, // pattern angle
421
+ // '53': 45, // Pattern line angle
422
+ // '70': 0, // Solid fill flag
423
+ // '71': 1, // Associativity flag
424
+ '72': [0, 1], // Edge type
425
+ // '73': [1, 1], // Boundary annotation flag
426
+ // '75': 1, // Hatch style
427
+ // '76': 1, // Hatch pattern type
428
+ // '77': 0, // Hatch pattern double flag
429
+ '78': 1, // Number of pattern definition lines
430
+ // '79': 0, // Number of dash length items
431
+ '91': 2, // Number of boundary path loops
432
+ '92': [7, 7], // Boundary path type flag
433
+ '93': [4, 2], // Number of edges in this boundary path / number of points in polyline
434
+ // '97': [1, 1], // Number of source boundary objects
435
+ // '98': 2, // Number of seed points
436
+ // Points 0 and -1 are stripped before processing
437
+ 'points': [new Point(), new Point(200, 200), new Point(100, 200), new Point(100, 100), new Point(200, 100), new Point(350, 300, 1),
438
+ new Point(250, 300, 1), new Point(350, 300, 1)],
439
+ };
440
+
441
+ const hatch = new Hatch();
442
+
443
+ const boundaryData = hatch.processBoundaryData(data);
444
+
445
+ // console.log(boundaryData);
446
+ expect(boundaryData[0].points.length).toEqual(4);
447
+ expect(boundaryData[1].points.length).toEqual(2);
448
+ });
449
+
450
+
451
+ test('Test Hatch.processSelection', () => {
452
+ // create selected items defining a square
453
+ let selectedItems = [];
454
+ selectedItems.push(new Line({ points: [new Point(100, 100), new Point(200, 100)] }));
455
+ selectedItems.push(new Line({ points: [new Point(200, 100), new Point(200, 200)] }));
456
+ selectedItems.push(new Line({ points: [new Point(200, 200), new Point(100, 200)] }));
457
+ selectedItems.push(new Line({ points: [new Point(100, 200), new Point(100, 100)] }));
458
+
459
+ let hatch = new Hatch();
460
+ let boundaryData = hatch.processSelection(selectedItems);
461
+ expect(boundaryData.length).toEqual(1);
462
+ expect(boundaryData[0].points.length).toEqual(4);
463
+
464
+
465
+ // create selected items defining a circle
466
+ selectedItems = [];
467
+ selectedItems.push(new Circle({ points: [new Point(100, 100), new Point(200, 100)] }));
468
+
469
+ hatch = new Hatch();
470
+ boundaryData = hatch.processSelection(selectedItems);
471
+ expect(boundaryData.length).toEqual(1);
472
+ expect(boundaryData[0].points.length).toEqual(2);
473
+
474
+ // create selected items defining a square (polyline)
475
+ selectedItems = [];
476
+ selectedItems.push(new Polyline({
477
+ points: [new Point(100, 100), new Point(200, 100), new Point(200, 200),
478
+ new Point(100, 200), new Point(100, 100)],
479
+ }));
480
+
481
+ hatch = new Hatch();
482
+ boundaryData = hatch.processSelection(selectedItems);
483
+ expect(boundaryData.length).toEqual(1);
484
+ expect(boundaryData[0].points.length).toEqual(4);
485
+
486
+ // create selected items defining a pill shape
487
+ selectedItems = [];
488
+ selectedItems.push(new Line({ points: [new Point(100, 100), new Point(200, 100)] }));
489
+ selectedItems.push(new Arc({ points: [new Point(200, 150), new Point(200, 100), new Point(200, 200)] }));
490
+ selectedItems.push(new Line({ points: [new Point(200, 200), new Point(100, 200)] }));
491
+ selectedItems.push(new Arc({ points: [new Point(100, 150), new Point(100, 200), new Point(100, 100)] }));
492
+
493
+ hatch = new Hatch();
494
+ boundaryData = hatch.processSelection(selectedItems);
495
+ expect(boundaryData.length).toEqual(1);
496
+ // 2 lines + 2 arcs: each shared connection point is merged (not duplicated),
497
+ // so the result is 4 unique vertices, not 5
498
+ expect(boundaryData[0].points.length).toEqual(4);
499
+
500
+ // Test a selection with invalid items
501
+ selectedItems = [];
502
+ selectedItems.push(new Text({ points: [new Point(100, 100), new Point(200, 100)] }));
503
+ selectedItems.push(new Circle({ points: [new Point(100, 100), new Point(200, 100)] }));
504
+
505
+ hatch = new Hatch();
506
+ boundaryData = hatch.processSelection(selectedItems);
507
+ expect(boundaryData.length).toEqual(1);
508
+ expect(boundaryData[0].points.length).toEqual(2);
509
+ });
510
+
511
+ // Minimal mock canvas context that records moveTo calls and supports try/catch split
512
+ /**
513
+ * Create a minimal mock canvas context
514
+ * @return {Object} mock context
515
+ */
516
+ function makeMockCtx() {
517
+ let moveCount = 0;
518
+ let fillCalled = false;
519
+ return {
520
+ save: () => {},
521
+ restore: () => {},
522
+ translate: () => {},
523
+ scale: () => {},
524
+ rotate: () => {},
525
+ beginPath: () => {},
526
+ setLineDash: () => {},
527
+ lineWidth: 0,
528
+ moveTo: () => {
529
+ moveCount++;
530
+ },
531
+ lineTo: () => {},
532
+ stroke: () => {},
533
+ fill: () => {
534
+ fillCalled = true;
535
+ },
536
+ getMoveCalls: () => moveCount,
537
+ getFillCalled: () => fillCalled,
538
+ };
539
+ }
540
+
541
+ test('Test Hatch.createPattern solid fill calls ctx.fill', () => {
542
+ const solidHatch = new Hatch({ patternName: 'SOLID' });
543
+ solidHatch.childEntities = [new BasePolyline({ points: [new Point(0, 0), new Point(10, 0)] })];
544
+
545
+ const ctx = makeMockCtx();
546
+ solidHatch.createPattern(ctx, 1, solidHatch.boundingBox());
547
+
548
+ expect(ctx.getFillCalled()).toBe(true);
549
+ expect(ctx.getMoveCalls()).toBe(0);
550
+ });
551
+
552
+ test('Test Hatch.createPattern unknown pattern calls ctx.fill', () => {
553
+ const unknownHatch = new Hatch({ patternName: 'ANSI31' });
554
+ unknownHatch.childEntities = [new BasePolyline({ points: [new Point(0, 0), new Point(10, 0)] })];
555
+ // Override pattern to a nonexistent name without going through setPatternName
556
+ unknownHatch.pattern = 'NOTAPATTERN';
557
+
558
+ const ctx = makeMockCtx();
559
+ unknownHatch.createPattern(ctx, 1, unknownHatch.boundingBox());
560
+
561
+ expect(ctx.getFillCalled()).toBe(true);
562
+ expect(ctx.getMoveCalls()).toBe(0);
563
+ });
564
+
565
+ test('Test Hatch.createPattern tight bound line count - square boundary', () => {
566
+ // ANSI31: angle=45, yDelta=3.175, no dashes (dashLength = bbXLength/2)
567
+ // 100x100 square: halfW=halfH=50, sin45=cos45=√2/2
568
+ // halfY = 50*(√2/2) + 50*(√2/2) ≈ 70.71
569
+ // yIncrement = ceil(70.71 / 3.175) = 23 → loop i=-23..22 = 46 lines
570
+ const squareBB = new BoundingBox(new Point(0, 100), new Point(100, 0));
571
+ const squareHatch = new Hatch({ patternName: 'ANSI31' });
572
+ const ctx = makeMockCtx();
573
+ squareHatch.createPattern(ctx, 1, squareBB);
574
+ expect(ctx.getMoveCalls()).toBe(46);
575
+ });
576
+
577
+ test('Test Hatch.createPattern tight bound line count - flat boundary', () => {
578
+ // ANSI31: angle=45, yDelta=3.175, no dashes
579
+ // 100x10 flat box: halfW=50, halfH=5, sin45=cos45=√2/2
580
+ // halfY = (50+5)*(√2/2) ≈ 38.89
581
+ // yIncrement = ceil(38.89 / 3.175) = 13 → loop i=-13..12 = 26 lines
582
+ const flatBB = new BoundingBox(new Point(0, 10), new Point(100, 0));
583
+ const flatHatch = new Hatch({ patternName: 'ANSI31' });
584
+ const ctx = makeMockCtx();
585
+ flatHatch.createPattern(ctx, 1, flatBB);
586
+ expect(ctx.getMoveCalls()).toBe(26);
587
+ });
588
+
589
+ test('Test Hatch.createPattern tight bound produces fewer lines for flat boundary', () => {
590
+ // A flat boundary (100x10) should produce fewer lines than a square (100x100).
591
+ // This verifies that the tight projection avoids over-generating lines for
592
+ // non-square shapes, unlike the old Math.max(bbX, bbY) approach.
593
+ const squareBB = new BoundingBox(new Point(0, 100), new Point(100, 0));
594
+ const flatBB = new BoundingBox(new Point(0, 10), new Point(100, 0));
595
+
596
+ const squareHatch = new Hatch({ patternName: 'ANSI31' });
597
+ const flatHatch = new Hatch({ patternName: 'ANSI31' });
598
+
599
+ const squareCtx = makeMockCtx();
600
+ squareHatch.createPattern(squareCtx, 1, squareBB);
601
+
602
+ const flatCtx = makeMockCtx();
603
+ flatHatch.createPattern(flatCtx, 1, flatBB);
604
+
605
+ expect(flatCtx.getMoveCalls()).toBeLessThan(squareCtx.getMoveCalls());
606
+ });
@@ -0,0 +1,90 @@
1
+ import { Core } from '../../core/core/core.js';
2
+ import { Insert } from '../../core/entities/insert.js';
3
+ import { Point } from '../../core/entities/point.js';
4
+ import { Line } from '../../core/entities/line.js';
5
+
6
+ import { File } from '../test-helpers/test-helpers.js';
7
+
8
+ // initialise core
9
+ new Core();
10
+
11
+
12
+ const insert = new Insert({ handle: '1', points: [new Point()] });
13
+ const line = new Line({ points: [new Point(101, 102), new Point(201, 202)] });
14
+ insert.block.addItem(line);
15
+
16
+ const rotatedInsert = new Insert({ points: [new Point()], rotation: 45 });
17
+ rotatedInsert.block.addItem(line);
18
+
19
+ test('Test Insert.snaps', () => {
20
+ const point = new Point(100, 100);
21
+ const snaps = insert.snaps(point, 1);
22
+ expect(snaps[0].x).toBeCloseTo(101);
23
+ expect(snaps[0].y).toBeCloseTo(102);
24
+
25
+ // Test snaps for a rotated block
26
+ const rotatedSnaps = rotatedInsert.snaps(point);
27
+ expect(rotatedSnaps[0].x).toBeCloseTo(-0.7071);
28
+ expect(rotatedSnaps[0].y).toBeCloseTo(143.5426);
29
+ });
30
+
31
+ test('Test Insert.closestPoint', () => {
32
+ const point = new Point(100, 100);
33
+ const closest = insert.closestPoint(point);
34
+ expect(closest[0].x).toBeCloseTo(101);
35
+ expect(closest[0].y).toBeCloseTo(102);
36
+
37
+ // Test closest for a rotated block
38
+ const rotatedClosest = rotatedInsert.closestPoint(point);
39
+ expect(rotatedClosest[0].x).toBeCloseTo(101);
40
+ expect(rotatedClosest[0].y).toBeCloseTo(102);
41
+ });
42
+
43
+ test('Test Insert.boundingBox', () => {
44
+ expect(insert.boundingBox().xMin).toBeCloseTo(101);
45
+ expect(insert.boundingBox().xMax).toBeCloseTo(201);
46
+ expect(insert.boundingBox().yMin).toBeCloseTo(102);
47
+ expect(insert.boundingBox().yMax).toBeCloseTo(202);
48
+
49
+ const line2 = new Line({ points: [new Point(1001, 1002), new Point(2001, 2002)] });
50
+ insert.block.addItem(line2);
51
+
52
+ expect(insert.boundingBox().xMin).toBeCloseTo(101);
53
+ expect(insert.boundingBox().xMax).toBeCloseTo(2001);
54
+ expect(insert.boundingBox().yMin).toBeCloseTo(102);
55
+ expect(insert.boundingBox().yMax).toBeCloseTo(2002);
56
+ });
57
+
58
+ test('Test Insert.dxf', () => {
59
+ let file = new File();
60
+ insert.dxf(file);
61
+ // console.log(file.contents);
62
+
63
+ const dxfString = `0
64
+ INSERT
65
+ 5
66
+ 1
67
+ 100
68
+ AcDbEntity
69
+ 8
70
+ 0
71
+ 100
72
+ AcDbBlockReference
73
+ 2
74
+
75
+ 10
76
+ 0
77
+ 20
78
+ 0
79
+ 30
80
+ 0.0
81
+ `;
82
+
83
+ expect(file.contents).toEqual(dxfString);
84
+
85
+ // create new entity from entity data to ensure all props are loaded
86
+ const newInsert = new Insert(insert);
87
+ file = new File();
88
+ newInsert.dxf(file);
89
+ expect(file.contents).toEqual(dxfString);
90
+ });