@design-core/core 0.0.5 → 0.0.7

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 (145) hide show
  1. package/.eslintrc.json +4 -0
  2. package/TODO.md +49 -6
  3. package/core/core/core.js +0 -3
  4. package/core/dimensions/alignedDimension.js +5 -4
  5. package/core/dimensions/angularDimension.js +6 -5
  6. package/core/dimensions/baseCircularDimension.js +3 -2
  7. package/core/dimensions/baseDimension.js +59 -93
  8. package/core/dimensions/baseLinearDimension.js +6 -5
  9. package/core/dimensions/diametricDimension.js +4 -3
  10. package/core/dimensions/dimension.js +21 -14
  11. package/core/dimensions/radialDimension.js +4 -3
  12. package/core/dimensions/rotatedDimension.js +12 -11
  13. package/core/entities/arc.js +112 -145
  14. package/core/entities/arctext.js +212 -174
  15. package/core/entities/basePolyline.js +33 -284
  16. package/core/entities/circle.js +77 -99
  17. package/core/entities/entity.js +114 -93
  18. package/core/entities/hatch.js +322 -249
  19. package/core/entities/insert.js +66 -81
  20. package/core/entities/line.js +21 -142
  21. package/core/entities/lwpolyline.js +16 -11
  22. package/core/entities/polyline.js +4 -4
  23. package/core/entities/rectangle.js +3 -1
  24. package/core/entities/solid.js +6 -15
  25. package/core/entities/text.js +188 -199
  26. package/core/lib/auxiliary/rubberBand.js +32 -0
  27. package/core/lib/auxiliary/selectionWindow.js +63 -0
  28. package/core/lib/auxiliary/snapPoint.js +113 -0
  29. package/core/lib/auxiliary/trackingLine.js +76 -0
  30. package/core/lib/canvas.js +228 -166
  31. package/core/lib/clipboard.js +7 -1
  32. package/core/lib/colour.js +16 -0
  33. package/core/lib/commandLine.js +1 -6
  34. package/core/lib/commandManager.js +18 -8
  35. package/core/lib/constants.js +28 -0
  36. package/core/lib/dxf/dxf.js +4 -3
  37. package/core/lib/entityManager.js +18 -11
  38. package/core/lib/inputManager.js +36 -13
  39. package/core/lib/intersect.js +25 -0
  40. package/core/lib/patterns.js +8 -1
  41. package/core/lib/plotOptions.js +67 -0
  42. package/core/lib/polylineUtils.js +75 -0
  43. package/core/lib/renderers/cairoRenderer.js +371 -0
  44. package/core/lib/renderers/canvasRenderer.js +315 -0
  45. package/core/lib/renderers/pdfRenderer.js +366 -0
  46. package/core/lib/renderers/rendererBase.js +284 -0
  47. package/core/lib/renderers/svgRenderer.js +343 -0
  48. package/core/lib/scene.js +27 -5
  49. package/core/lib/selectionManager.js +2 -2
  50. package/core/lib/settings.js +4 -2
  51. package/core/lib/snapping.js +4 -191
  52. package/core/lib/stateManager.js +30 -1
  53. package/core/lib/utils.js +21 -2
  54. package/core/properties/entityProperties.js +104 -0
  55. package/core/properties/property.js +106 -2
  56. package/core/properties/propertyManager.js +29 -13
  57. package/core/tables/block.js +4 -37
  58. package/core/tables/endblk.js +1 -1
  59. package/core/tables/layer.js +8 -0
  60. package/core/tables/styleManagerBase.js +3 -2
  61. package/core/tables/tableManagerBase.js +8 -3
  62. package/core/tools/chamfer.js +111 -137
  63. package/core/tools/chamferFilletBase.js +135 -34
  64. package/core/tools/copy.js +3 -3
  65. package/core/tools/explode.js +2 -2
  66. package/core/tools/extend.js +132 -19
  67. package/core/tools/fillet.js +86 -183
  68. package/core/tools/matchProp.js +2 -11
  69. package/core/tools/mirror.js +7 -6
  70. package/core/tools/move.js +3 -3
  71. package/core/tools/offset.js +3 -5
  72. package/core/tools/pasteclip.js +1 -1
  73. package/core/tools/rotate.js +6 -3
  74. package/core/tools/scale.js +1 -1
  75. package/core/tools/trim.js +265 -23
  76. package/core/tools/zoom.js +1 -1
  77. package/jest.config.js +6 -0
  78. package/package.json +5 -3
  79. package/test/dimensions/alignedDimension.test.js +14 -7
  80. package/test/dimensions/angularDimension.test.js +6 -6
  81. package/test/dimensions/baseDimension.test.js +69 -8
  82. package/test/dimensions/baseLinearDimension.test.js +62 -0
  83. package/test/dimensions/diametricDimension.test.js +6 -6
  84. package/test/dimensions/radialDimension.test.js +6 -6
  85. package/test/dimensions/rotatedDimension.test.js +14 -7
  86. package/test/entities/arc.test.js +80 -83
  87. package/test/entities/arctext.test.js +180 -53
  88. package/test/entities/basePolyline.test.js +129 -2
  89. package/test/entities/circle.test.js +57 -85
  90. package/test/entities/entity.test.js +42 -8
  91. package/test/entities/hatch.test.js +543 -70
  92. package/test/entities/insert.test.js +96 -1
  93. package/test/entities/line.test.js +15 -41
  94. package/test/entities/lwpolyline.test.js +104 -0
  95. package/test/entities/polyline.test.js +11 -0
  96. package/test/entities/rectangle.test.js +11 -0
  97. package/test/entities/solid.test.js +1 -1
  98. package/test/entities/text.test.js +294 -69
  99. package/test/integration/exportIntegration.reference.dxf +9 -9
  100. package/test/integration/exportIntegration.test.js +2 -2
  101. package/test/integration/intersectionIntegration.test.js +4 -5
  102. package/test/integration/pdfRendererIntegration.reference.pdf +17709 -0
  103. package/test/integration/pdfRendererIntegration.test.js +61 -0
  104. package/test/integration/svgRendererIntegration.reference.svg +3343 -0
  105. package/test/integration/svgRendererIntegration.test.js +61 -0
  106. package/test/integration/userInputIntegration.reference.dxf +1165 -105
  107. package/test/integration/userInputIntegration.test.js +239 -132
  108. package/test/lib/canvas.test.js +742 -123
  109. package/test/lib/constants.test.js +27 -0
  110. package/test/lib/entityManager.test.js +4 -4
  111. package/test/lib/inputManager.test.js +80 -19
  112. package/test/lib/intersect.test.js +83 -0
  113. package/test/lib/plotOptions.test.js +179 -0
  114. package/test/lib/rubberBand.test.js +84 -0
  115. package/test/lib/settings.test.js +5 -6
  116. package/test/lib/snapping.test.js +71 -129
  117. package/test/lib/stateManager.test.js +7 -7
  118. package/test/lib/utils.test.js +36 -2
  119. package/test/properties/entityProperties.test.js +132 -0
  120. package/test/properties/property.test.js +53 -0
  121. package/test/properties/propertyManager.test.js +3 -3
  122. package/test/renderers/canvasRenderer.test.js +317 -0
  123. package/test/renderers/pdfRenderer.test.js +271 -0
  124. package/test/renderers/rendererBase.test.js +213 -0
  125. package/test/renderers/svgRenderer.test.js +326 -0
  126. package/test/tables/block.test.js +31 -1
  127. package/test/tables/endblk.test.js +2 -2
  128. package/test/tables/layer.test.js +30 -0
  129. package/test/test-helpers/hatchGridRenderer.js +180 -0
  130. package/test/test-helpers/test-helpers.js +152 -0
  131. package/test/tools/chamfer.test.js +227 -5
  132. package/test/tools/chamferFilletBase.test.js +98 -9
  133. package/test/tools/copy.test.js +2 -2
  134. package/test/tools/explode.test.js +3 -3
  135. package/test/tools/extend.test.js +331 -0
  136. package/test/tools/fillet.test.js +296 -0
  137. package/test/tools/matchProp.test.js +5 -5
  138. package/test/tools/mirror.test.js +13 -13
  139. package/test/tools/offset.test.js +170 -3
  140. package/test/tools/pasteclip.test.js +5 -5
  141. package/test/tools/rotate.test.js +32 -2
  142. package/test/tools/scale.test.js +5 -5
  143. package/test/tools/trim.test.js +526 -0
  144. package/test/tools/zoom.test.js +7 -7
  145. package/core/lib/selectionWindow.js +0 -83
@@ -2,8 +2,9 @@ import { Colours } from '../lib/colours.js';
2
2
  import { EntityColour } from '../lib/colour.js';
3
3
  import { Intersection } from '../lib/intersect.js';
4
4
  import { Point } from './point.js';
5
- import { Strings } from '../lib/strings.js';
5
+
6
6
  import { Property } from '../properties/property.js';
7
+ import { EntityProperties } from '../properties/entityProperties.js';
7
8
 
8
9
 
9
10
  import { DesignCore } from '../designCore.js';
@@ -23,80 +24,130 @@ export class Entity {
23
24
  writable: true,
24
25
  });
25
26
 
26
- Object.defineProperty(this, 'points', {
27
- value: [],
27
+ Object.defineProperty(this, 'entityColour', {
28
+ value: new EntityColour(),
28
29
  writable: true,
29
30
  });
30
31
 
31
- Object.defineProperty(this, 'colour', {
32
- get: this.getColour,
33
- set: this.setColour,
34
- });
32
+ // DXF Groupcode 62 (normalised to colourACI by Entity.fromDxf) - ACI colour index
33
+ // DXF Groupcode 420 (normalised to trueColour by Entity.fromDxf) - 24-bit true colour
34
+ if (data?.colourACI !== undefined) {
35
+ this.entityColour.setColourFromACI(data.colourACI);
36
+ }
35
37
 
36
- Object.defineProperty(this, 'entityColour', {
37
- value: new EntityColour(),
38
+ if (data?.trueColour !== undefined) {
39
+ const trueColour = Colours.trueColourToRGB(data.trueColour);
40
+ if (trueColour) this.entityColour.setColour(trueColour);
41
+ }
42
+
43
+ // Typed property store — non-enumerable so it doesn't appear in serialisation
44
+ Object.defineProperty(this, 'properties', {
45
+ value: new EntityProperties(),
38
46
  writable: true,
39
47
  });
40
48
 
41
- this.lineWidth = 2;
42
- this.lineType = 'ByLayer';
43
- this.layer = '0';
44
-
45
49
  // DXF Groupcode 5 - Handle
46
- Object.defineProperty(this, 'handle', {
50
+ this.properties.add(Property.Names.HANDLE, {
51
+ type: Property.Type.STRING,
47
52
  value: Property.loadValue([data?.handle, data?.[5]]),
48
- writable: true,
53
+ visible: false,
54
+ dxfCode: 5,
55
+ });
56
+ // DXF Groupcode 8 - Layer name
57
+ this.properties.add(Property.Names.LAYER, {
58
+ type: Property.Type.LIST,
59
+ value: Property.loadValue([data?.layer, data?.[8]], '0'),
60
+ dxfCode: 8,
61
+ options: () => DesignCore.LayerManager.getItems().map((l) => ({ display: l.name, value: l.name })),
62
+ });
63
+ // DXF Groupcode 6 - Line type
64
+ this.properties.add(Property.Names.LINETYPE, {
65
+ type: Property.Type.LIST,
66
+ value: Property.loadValue([data?.lineType, data?.[6]], 'ByLayer'),
67
+ dxfCode: 6,
68
+ options: () => DesignCore.LTypeManager.getItems().map((s) => ({ display: s.name, value: s.name })),
49
69
  });
50
- // DXF Groupcode 6 - lineType
51
- this.lineType = Property.loadValue([data?.lineType, data?.[6]], 'ByLayer');
52
70
  // DXF Groupcode 39 - Thickness (lineWidth)
53
- this.lineWidth = Property.loadValue([data?.lineWidth, data?.[39]], 2);
54
- // DXF Groupcode 8 - layername
55
- this.layer = Property.loadValue([data?.layer, data?.[8]], '0');
56
-
57
- if (data) {
58
- if (data.hasOwnProperty('points')) {
59
- this.points = data.points;
60
- }
61
-
62
- if (data.hasOwnProperty('colour') || data.hasOwnProperty('62')) {
63
- // DXF Groupcode 62 - Color Number (present if not BYLAYER)(optional);
64
- // zero indicates BYBLOCK
65
- // 256 indicates BYLAYER;
66
- // a negative value indicates that the layer is turned off
67
- if (data.colour) {
68
- if (Colours.isRGB(data.colour)) {
69
- this.colour = data.colour;
70
- }
71
- } else if (data.hasOwnProperty('62')) {
72
- this.entityColour.setColourFromACI(Math.abs(data[62]));
73
- }
74
- }
75
-
76
- if (data.hasOwnProperty('trueColour') || data.hasOwnProperty('420')) {
77
- // DXF Groupcode 420 - true color
78
- // A 24-bit color value that should be dealt with in terms of bytes with values
79
- // of 0 to 255. The lowest byte is the blue value, the middle byte is the
80
- // green value, and the third byte is the red value. The top byte is always
81
- // 0. The group code cannot be used by custom entities for their own data
82
- // because the group code is reserved for AcDbEntity, class-level color data
83
- // and AcDbEntity, class-level transparency data
84
-
85
- const trueColour = Colours.trueColourToRGB(data.trueColour || data[420]);
86
- if (trueColour) {
87
- this.colour = trueColour;
88
- }
89
- }
71
+ this.properties.add(Property.Names.LINEWIDTH, {
72
+ type: Property.Type.NUMBER,
73
+ value: Property.loadValue([data?.lineWidth, data?.[39]], 1),
74
+ dxfCode: 39,
75
+ });
76
+ // DXF Groupcode 62/420 - colour (computed, backed by entityColour)
77
+ this.properties.add(Property.Names.COLOUR, {
78
+ type: Property.Type.COLOUR,
79
+ get: (entity) => entity.entityColour.getColour(),
80
+ set: (entity, value) => entity.entityColour.setColour(value),
81
+ dxfCode: 62,
82
+ });
83
+
84
+ // Apply colour from data if RGB was passed directly
85
+ if (data?.colour && Colours.isRGB(data.colour)) {
86
+ this.entityColour.setColour(data.colour);
90
87
  }
88
+
89
+ // points — stored value in EntityProperties
90
+ this.properties.add(Property.Names.POINTS, {
91
+ visible: false,
92
+ value: data?.points ?? [],
93
+ });
91
94
  }
92
95
 
96
+ /**
97
+ * Get the entity's geometry points.
98
+ * @return {Array<Point>}
99
+ */
100
+ get points() {
101
+ return this.properties.get(Property.Names.POINTS, this);
102
+ }
103
+
104
+ /**
105
+ * Set the entity's geometry points.
106
+ * @param {Array<Point>} value
107
+ */
108
+ set points(value) {
109
+ this.properties.set(Property.Names.POINTS, value, this);
110
+ }
111
+
112
+ /**
113
+ * Normalise entity-level DXF group codes before construction.
114
+ * Subclasses override this for geometric transformation; they receive data
115
+ * that has already passed through this method via CommandManager.
116
+ * @param {Object} data - raw DXF group code object
117
+ * @return {Object} normalised data
118
+ *
119
+ * Handles:
120
+ * - Group code 62: ACI colour index → colourACI (absolute value)
121
+ * - Group code 420: 24-bit true colour integer → trueColour
122
+ */
123
+ static fromDxf(data) {
124
+ const normalised = { ...data };
125
+ // DXF group code 62 - Color Number (ACI)
126
+ if (data[62] !== undefined) {
127
+ normalised.colourACI = Math.abs(data[62]);
128
+ }
129
+ // DXF group code 420 - True Color (24-bit integer)
130
+ if (data[420] !== undefined) {
131
+ normalised.trueColour = data[420];
132
+ }
133
+ return normalised;
134
+ }
135
+
136
+ /**
137
+ * Get a property value by name.
138
+ * @param {string} name
139
+ * @return {any}
140
+ */
141
+ getProperty(name) {
142
+ return this.properties.get(name, this);
143
+ }
93
144
 
94
145
  /**
95
146
  * get rgb colour
96
147
  * @return {number} rgb colour object
97
148
  */
98
149
  getColour() {
99
- return this.entityColour.getColour(); ;
150
+ return this.entityColour.getColour();
100
151
  }
101
152
 
102
153
  /**
@@ -107,30 +158,22 @@ export class Entity {
107
158
  let rgb = this.getColour();
108
159
 
109
160
  if (this.entityColour.byLayer) {
110
- const layer = DesignCore.LayerManager.getItemByName(this.layer);
161
+ const layer = DesignCore.LayerManager.getItemByName(this.getProperty(Property.Names.LAYER));
111
162
  rgb = layer?.colour;
112
163
  }
113
164
 
114
165
  return rgb;
115
166
  }
116
167
 
117
- /**
118
- * Set the entity colour
119
- * @param {Object} rgb
120
- */
121
- setColour(rgb) {
122
- this.entityColour.setColour(rgb);
123
- }
124
-
125
168
  /**
126
169
  * Get the line type
127
170
  * @return {LType}
128
171
  */
129
172
  getLineType() {
130
- let lineTypeName = this.lineType;
173
+ let lineTypeName = this.getProperty(Property.Names.LINETYPE) ?? 'ByLayer';
131
174
 
132
175
  if (lineTypeName.toUpperCase() === 'BYLAYER') {
133
- const layer = DesignCore.LayerManager.getItemByName(this.layer);
176
+ const layer = DesignCore.LayerManager.getItemByName(this.getProperty(Property.Names.LAYER));
134
177
  lineTypeName = layer?.lineType;
135
178
  }
136
179
 
@@ -148,7 +191,7 @@ export class Entity {
148
191
  file.writeGroupCode('62', this.entityColour.aci);
149
192
  }
150
193
  if (this.entityColour.isTrueColour) {
151
- file.writeGroupCode('420', Colours.rgbToTrueColour(this.colour));
194
+ file.writeGroupCode('420', Colours.rgbToTrueColour(this.getColour()));
152
195
  }
153
196
  }
154
197
 
@@ -158,7 +201,7 @@ export class Entity {
158
201
  * @return {boolean} true if within
159
202
  */
160
203
  within(selection) {
161
- const layer = DesignCore.LayerManager.getItemByName(this.layer);
204
+ const layer = DesignCore.LayerManager.getItemByName(this.getProperty(Property.Names.LAYER));
162
205
 
163
206
  if (!layer?.isSelectable) {
164
207
  return;
@@ -182,7 +225,7 @@ export class Entity {
182
225
  * @return {boolean} true if touched
183
226
  */
184
227
  touched(selection) {
185
- const layer = DesignCore.LayerManager.getItemByName(this.layer);
228
+ const layer = DesignCore.LayerManager.getItemByName(this.getProperty(Property.Names.LAYER));
186
229
 
187
230
  if (!layer?.isSelectable) {
188
231
  return;
@@ -205,36 +248,14 @@ export class Entity {
205
248
  return false;
206
249
  }
207
250
 
208
- /**
209
- * Extend the entity
210
- * @param {Array} points
211
- * @return {Array} - array of state changes
212
- */
213
- extend(points) {
214
- // extend function to be overridden by implementation
215
- DesignCore.Core.notify(`${this.type} - ${Strings.Message.NOEXTEND}`);
216
- return [];
217
- }
218
-
219
- /**
220
- * Trim the entity
221
- * @param {Array} points
222
- * @return {Array} - array of state changes
223
- */
224
- trim(points) {
225
- // trim function to be overridden by implementation
226
- DesignCore.Core.notify(`${this.type} - ${Strings.Message.NOTRIM}`);
227
- return [];
228
- }
229
-
230
251
  /**
231
252
  * Set a property if it exists
232
253
  * @param {string} property
233
254
  * @param {any} value
234
255
  */
235
256
  setProperty(property, value) {
236
- if (this.hasOwnProperty(property)) {
237
- this[property] = value;
257
+ if (this.properties.has(property)) {
258
+ this.properties.set(property, value, this);
238
259
  }
239
260
  }
240
261
  }