@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,186 @@
1
+ import { Core } from '../../core/core/core.js';
2
+ import { DesignCore } from '../../core/designCore.js';
3
+ import { Point } from '../../core/entities/point.js';
4
+ import { Line } from '../../core/entities/line.js';
5
+ import { Circle } from '../../core/entities/circle.js';
6
+ import { Arc } from '../../core/entities/arc.js';
7
+ import { Text } from '../../core/entities/text.js';
8
+ import { Polyline } from '../../core/entities/polyline.js';
9
+ import { Lwpolyline } from '../../core/entities/lwpolyline.js';
10
+ // import { Solid } from '../../core/entities/solid.js';
11
+ import { Hatch } from '../../core/entities/hatch.js';
12
+ import { Insert } from '../../core/entities/insert.js';
13
+ // import { ArcAlignedText } from '../../core/entities/arctext.js';
14
+ import { BasePolyline } from '../../core/entities/basePolyline.js';
15
+
16
+ import { readFileSync } from 'fs';
17
+ import { fileURLToPath } from 'url';
18
+ import { dirname, join } from 'path';
19
+
20
+ const __filename = fileURLToPath(import.meta.url);
21
+ const __dirname = dirname(__filename);
22
+
23
+ let core;
24
+
25
+ beforeAll(() => {
26
+ core = new Core();
27
+
28
+ // Entities are arranged in a 3x3 grid of 100x100 cells:
29
+ //
30
+ // Row 2 (y=200-300): | Hatch | Insert |
31
+ // Row 1 (y=100-200): | Text | Polyline | Lwpolyline |
32
+ // Row 0 (y=0-100): | Line | Circle | Arc |
33
+
34
+ // Add layers for each entity type
35
+ DesignCore.LayerManager.addItem({ name: 'LINE_LAYER' });
36
+ DesignCore.LayerManager.addItem({ name: 'CIRCLE_LAYER' });
37
+ DesignCore.LayerManager.addItem({ name: 'ARC_LAYER' });
38
+ DesignCore.LayerManager.addItem({ name: 'TEXT_LAYER' });
39
+ DesignCore.LayerManager.addItem({ name: 'POLYLINE_LAYER' });
40
+ DesignCore.LayerManager.addItem({ name: 'LWPOLYLINE_LAYER' });
41
+ // DesignCore.LayerManager.addItem({ name: 'SOLID_LAYER' });
42
+ DesignCore.LayerManager.addItem({ name: 'HATCH_LAYER' });
43
+ DesignCore.LayerManager.addItem({ name: 'INSERT_LAYER' });
44
+ // DesignCore.LayerManager.addItem({ name: 'ARCTEXT_LAYER' });
45
+
46
+ // Row 0, Col 0: Line
47
+ const line = new Line({
48
+ points: [new Point(20, 25), new Point(80, 85)],
49
+ layer: 'LINE_LAYER',
50
+ });
51
+ core.scene.entities.add(line);
52
+ core.scene.entities.add(new Text({ points: [new Point(15, 5)], string: 'Line', height: 4 }));
53
+
54
+ // Row 0, Col 1: Circle
55
+ const circle = new Circle({
56
+ points: [new Point(150, 55)],
57
+ radius: 25,
58
+ layer: 'CIRCLE_LAYER',
59
+ });
60
+ core.scene.entities.add(circle);
61
+ core.scene.entities.add(new Text({ points: [new Point(115, 5)], string: 'Circle', height: 4 }));
62
+
63
+ // Row 0, Col 2: Arc
64
+ const arc = new Arc({
65
+ points: [new Point(250, 55)],
66
+ radius: 25,
67
+ startAngle: 0,
68
+ endAngle: 270,
69
+ layer: 'ARC_LAYER',
70
+ });
71
+ core.scene.entities.add(arc);
72
+ core.scene.entities.add(new Text({ points: [new Point(215, 5)], string: 'Arc', height: 4 }));
73
+
74
+ // Row 1, Col 0: Text
75
+ const text = new Text({
76
+ points: [new Point(25, 150)],
77
+ string: 'Hello',
78
+ height: 8,
79
+ layer: 'TEXT_LAYER',
80
+ });
81
+ core.scene.entities.add(text);
82
+ core.scene.entities.add(new Text({ points: [new Point(15, 105)], string: 'Text', height: 4 }));
83
+
84
+ // Row 1, Col 1: Polyline
85
+ const polyline = new Polyline({
86
+ points: [new Point(130, 130), new Point(170, 130), new Point(170, 175), new Point(130, 175)],
87
+ layer: 'POLYLINE_LAYER',
88
+ });
89
+ core.scene.entities.add(polyline);
90
+ core.scene.entities.add(new Text({ points: [new Point(115, 105)], string: 'Polyline', height: 4 }));
91
+
92
+ // Row 1, Col 2: Lwpolyline
93
+ const lwpolyline = new Lwpolyline({
94
+ points: [new Point(230, 130), new Point(270, 130), new Point(270, 175), new Point(230, 175)],
95
+ layer: 'LWPOLYLINE_LAYER',
96
+ });
97
+ core.scene.entities.add(lwpolyline);
98
+ core.scene.entities.add(new Text({ points: [new Point(215, 105)], string: 'Lwpolyline', height: 4 }));
99
+
100
+ // Solid - not registered as a command, cannot be imported
101
+ // const solid = new Solid({
102
+ // points: [new Point(20, 225), new Point(80, 225), new Point(50, 280)],
103
+ // layer: 'SOLID_LAYER',
104
+ // });
105
+ // core.scene.entities.add(solid);
106
+ // core.scene.entities.add(new Text({ points: [new Point(15, 205)], string: 'Solid', height: 4 }));
107
+
108
+ // Row 2, Col 0: Hatch
109
+ const boundary = new BasePolyline({
110
+ points: [new Point(25, 230), new Point(75, 230), new Point(75, 280), new Point(25, 280)],
111
+ });
112
+ const hatch = new Hatch({
113
+ patternName: 'ANSI31',
114
+ layer: 'HATCH_LAYER',
115
+ });
116
+ hatch.childEntities = [boundary];
117
+ core.scene.entities.add(hatch);
118
+ core.scene.entities.add(new Text({ points: [new Point(15, 205)], string: 'Hatch', height: 4 }));
119
+
120
+ // Row 2, Col 1: Insert
121
+ const blockLine = new Line({ points: [new Point(0, 0), new Point(40, 40)] });
122
+ blockLine.handle = DesignCore.HandleManager.next();
123
+ const block = DesignCore.Scene.blockManager.addItem({
124
+ name: 'TEST_BLOCK',
125
+ items: [blockLine],
126
+ });
127
+ const insert = new Insert({
128
+ points: [new Point(130, 235)],
129
+ layer: 'INSERT_LAYER',
130
+ });
131
+ insert.block = block;
132
+ core.scene.entities.add(insert);
133
+ core.scene.entities.add(new Text({ points: [new Point(115, 205)], string: 'Insert', height: 4 }));
134
+
135
+ // ArcAlignedText - omitted
136
+ // const arctext = new ArcAlignedText({
137
+ // points: [new Point(250, 255)],
138
+ // radius: 25,
139
+ // string: 'ArcText',
140
+ // height: 5,
141
+ // startAngle: 0,
142
+ // endAngle: 180,
143
+ // layer: 'ARCTEXT_LAYER',
144
+ // });
145
+ // core.scene.entities.add(arctext);
146
+ // core.scene.entities.add(new Text({ points: [new Point(215, 205)], string: 'ArcText', height: 4 }));
147
+ });
148
+
149
+ let dxfOutput;
150
+
151
+ beforeAll(() => {
152
+ dxfOutput = core.saveFile('R2018');
153
+ });
154
+
155
+ test('Test DXF output contains all entities', () => {
156
+ expect(dxfOutput).toContain('LINE');
157
+ expect(dxfOutput).toContain('CIRCLE');
158
+ expect(dxfOutput).toContain('ARC');
159
+ expect(dxfOutput).toContain('TEXT');
160
+ expect(dxfOutput).toContain('POLYLINE');
161
+ expect(dxfOutput).toContain('LWPOLYLINE');
162
+ // expect(dxfOutput).toContain('SOLID');
163
+ expect(dxfOutput).toContain('HATCH');
164
+ expect(dxfOutput).toContain('INSERT');
165
+ // expect(dxfOutput).toContain('ARCALIGNEDTEXT');
166
+ });
167
+
168
+ test('Test DXF output contains all layers', () => {
169
+ expect(dxfOutput).toContain('LINE_LAYER');
170
+ expect(dxfOutput).toContain('CIRCLE_LAYER');
171
+ expect(dxfOutput).toContain('ARC_LAYER');
172
+ expect(dxfOutput).toContain('TEXT_LAYER');
173
+ expect(dxfOutput).toContain('POLYLINE_LAYER');
174
+ expect(dxfOutput).toContain('LWPOLYLINE_LAYER');
175
+ // expect(dxfOutput).toContain('SOLID_LAYER');
176
+ expect(dxfOutput).toContain('HATCH_LAYER');
177
+ expect(dxfOutput).toContain('INSERT_LAYER');
178
+ // expect(dxfOutput).toContain('ARCTEXT_LAYER');
179
+ });
180
+
181
+ test('Test DXF output matches reference file', () => {
182
+ const referencePath = join(__dirname, 'exportIntegration.reference.dxf');
183
+ const reference = readFileSync(referencePath, 'utf8');
184
+
185
+ expect(dxfOutput).toBe(reference);
186
+ });
@@ -0,0 +1,126 @@
1
+ import { Core } from '../../core/core/core.js';
2
+ import { DXF } from '../../core/lib/dxf/dxf.js';
3
+
4
+ import { readFileSync } from 'fs';
5
+ import { fileURLToPath } from 'url';
6
+ import { dirname, join } from 'path';
7
+
8
+ const __filename = fileURLToPath(import.meta.url);
9
+ const __dirname = dirname(__filename);
10
+
11
+ /**
12
+ * Strip handle values from a DXF string so that comparison
13
+ * is insensitive to handle allocation order.
14
+ * Only group codes that contain hex handles are stripped.
15
+ * Values are validated as hex before stripping.
16
+ * @param {string} dxfString
17
+ * @return {{stripped: string, invalidHandles: Array<{line: number, code: number, value: string}>}}
18
+ */
19
+ function stripHandles(dxfString) {
20
+ const lines = dxfString.split('\n');
21
+ const invalidHandles = [];
22
+ let inDimStyle = false;
23
+
24
+ // DXF lines alternate: group code, value, group code, value...
25
+ for (let i = 0; i < lines.length - 1; i += 2) {
26
+ const code = parseInt(lines[i].trim(), 10);
27
+ if (isNaN(code)) continue;
28
+
29
+ const value = lines[i + 1].trim();
30
+
31
+ // Track DIMSTYLE context - groupcode 5 is an arrowhead
32
+ // block name in DIMSTYLE, not a handle (DIMSTYLE uses 105)
33
+ if (code === 0) {
34
+ inDimStyle = value === 'DIMSTYLE';
35
+ }
36
+
37
+ // Groupcode 5 = entity/table handle (except in DIMSTYLE)
38
+ // Groupcode 105 = dimstyle handle
39
+ // Groupcode 350 = dictionary entry handle
40
+ // Groupcode 390 = plotStyleHandle
41
+ const isHandleCode = (code === 5 && !inDimStyle) ||
42
+ code === 105 || code === 350 || code === 390;
43
+
44
+ if (isHandleCode) {
45
+ if (/^[0-9A-Fa-f]+$/.test(value)) {
46
+ lines[i + 1] = '0';
47
+ } else {
48
+ invalidHandles.push({ line: i + 2, code, value });
49
+ }
50
+ }
51
+ }
52
+
53
+ return { stripped: lines.join('\n'), invalidHandles };
54
+ }
55
+
56
+ test('Test DXF round-trip: read reference and re-output matches', () => {
57
+ const core = new Core();
58
+
59
+ const referencePath = join(__dirname, 'exportIntegration.reference.dxf');
60
+ const reference = readFileSync(referencePath, 'utf8');
61
+
62
+ // Load the reference DXF
63
+ const dxf = new DXF();
64
+ dxf.loadDxf(reference);
65
+
66
+ // Output a new DXF
67
+ const output = core.saveFile('R2018');
68
+
69
+ const strippedOutput = stripHandles(output);
70
+ const strippedReference = stripHandles(reference);
71
+
72
+ expect(strippedOutput.invalidHandles).toEqual([]);
73
+ expect(strippedReference.invalidHandles).toEqual([]);
74
+ expect(strippedOutput.stripped).toBe(strippedReference.stripped);
75
+ });
76
+
77
+ test('Test DXF output has no duplicate handles', () => {
78
+ const core = new Core();
79
+
80
+ const referencePath = join(__dirname, 'exportIntegration.reference.dxf');
81
+ const reference = readFileSync(referencePath, 'utf8');
82
+
83
+ // Load the reference DXF
84
+ const dxf = new DXF();
85
+ dxf.loadDxf(reference);
86
+
87
+ // Output a new DXF
88
+ const output = core.saveFile('R2018');
89
+ const lines = output.split('\n');
90
+
91
+ const handles = new Map();
92
+ let inDimStyle = false;
93
+ let isHandseed = false;
94
+
95
+ for (let i = 0; i < lines.length - 1; i += 2) {
96
+ const code = parseInt(lines[i].trim(), 10);
97
+ if (isNaN(code)) continue;
98
+
99
+ const value = lines[i + 1].trim();
100
+
101
+ if (code === 0) {
102
+ inDimStyle = value === 'DIMSTYLE';
103
+ }
104
+
105
+ // $HANDSEED uses groupcode 5 for its value, not as a handle
106
+ if (code === 9 && value === '$HANDSEED') {
107
+ isHandseed = true;
108
+ continue;
109
+ }
110
+
111
+ // Only check groupcode 5 handles (entity/table handles)
112
+ // Skip DIMSTYLE where code 5 is arrowhead block name
113
+ // Skip $HANDSEED value
114
+ if (code === 5 && !inDimStyle && !isHandseed && /^[0-9A-Fa-f]+$/.test(value)) {
115
+ const lineNum = i + 2;
116
+ if (handles.has(value)) {
117
+ fail(`Duplicate handle ${value} at line ${lineNum} (first seen at line ${handles.get(value)})`);
118
+ }
119
+ handles.set(value, lineNum);
120
+ }
121
+
122
+ isHandseed = false;
123
+ }
124
+
125
+ expect(handles.size).toBeGreaterThan(0);
126
+ });