@design-core/core 0.0.6 → 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 +30 -282
  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 +35 -11
  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 +68 -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 +181 -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
@@ -5,11 +5,11 @@ import { Block } from '../tables/block.js';
5
5
  import { BoundingBox } from '../lib/boundingBox.js';
6
6
  import { Point } from './point.js';
7
7
  import { Utils } from '../lib/utils.js';
8
- import { Property } from '../properties/property.js';
9
8
  import { Logging } from '../lib/logging.js';
9
+ import { Property } from '../properties/property.js';
10
10
 
11
11
  import { DesignCore } from '../designCore.js';
12
- import { SnapPoint } from '../lib/snapping.js';
12
+ import { SnapPoint } from '../lib/auxiliary/snapPoint.js';
13
13
 
14
14
 
15
15
  /**
@@ -34,59 +34,29 @@ export class Insert extends Entity {
34
34
  writable: true,
35
35
  });
36
36
 
37
- // add rotation property with getter and setter
38
- // needs to be enumerable to appear in the object props
39
- Object.defineProperty(this, 'rotation', {
40
- get: this.getRotation,
41
- set: this.setRotation,
42
- enumerable: true,
37
+ this.properties.add(Property.Names.ROTATION, {
38
+ type: Property.Type.NUMBER,
39
+ dxfCode: 50,
40
+ get: (entity) => entity.getRotation(),
41
+ set: (entity, value) => entity.setRotation(value),
43
42
  });
44
43
 
45
- if (data) {
46
- if (data.hasOwnProperty('blockName') || data.hasOwnProperty('2')) {
47
- // DXF Groupcode 2 - Block name
44
+ this.properties.add(Property.Names.BLOCKNAME, {
45
+ type: Property.Type.LABEL,
46
+ readOnly: true,
47
+ get: (entity) => entity.block?.name ?? '',
48
+ });
48
49
 
49
- const blockName = data.blockName || data[2];
50
- const block = DesignCore.Scene.blockManager.getItemByName(blockName);
50
+ if (data) {
51
+ // DXF Groupcode 2 - Block name
52
+ if (data.blockName !== undefined) {
53
+ const block = DesignCore.Scene.blockManager.getItemByName(data.blockName);
51
54
  this.block = block;
52
55
  }
53
56
 
54
- if (data.hasOwnProperty('41')) {
55
- // DXF Groupcode 41 - X Scale Factor (optional, default = 1)
56
- const err = 'Groupcode 41 not implemented';
57
- Logging.instance.warn(`${this.type} - ${err}`);
58
- }
59
-
60
- if (data.hasOwnProperty('42')) {
61
- // DXF Groupcode 42 - Y Scale Factor (optional, default = 1)
62
- const err = 'Groupcode 42 not implemented';
63
- Logging.instance.warn(`${this.type} - ${err}`);
64
- }
65
-
66
- if (data.hasOwnProperty('43')) {
67
- // DXF Groupcode 43 - Z Scale Factor (optional, default = 1)
68
- const err = 'Groupcode 43 not implemented';
69
- Logging.instance.warn(`${this.type} - ${err}`);
70
- }
71
-
72
- if (data.hasOwnProperty('44')) {
73
- // DXF Groupcode 44 - Column Spacing (optional, default = 0)
74
- const err = 'Groupcode 44 not implemented';
75
- Logging.instance.warn(`${this.type} - ${err}`);
76
- }
77
-
78
- if (data.hasOwnProperty('45')) {
79
- // DXF Groupcode 45 - Row Spacing (optional, default = 0)
80
- const err = 'Groupcode 45 not implemented';
81
- Logging.instance.warn(`${this.type} - ${err}`);
82
- }
83
-
84
- if (data.hasOwnProperty('rotation') || data.hasOwnProperty('50')) {
85
- // DXF Groupcode 50 - Text Rotation, angle in degrees
86
- // if we get rotation data store this as a point[1] at an angle from point[0]
87
- // this allows all the entities to be rotated by rotating the points i.e. not all entities have a rotation property
88
-
89
- this.setRotation(Property.loadValue([data.rotation, data[50]], 0));
57
+ // Named scalar rotation (internal API); DXF code 50 is handled by fromDxf
58
+ if (data.rotation !== undefined) {
59
+ this.setRotation(data.rotation);
90
60
  } else {
91
61
  // create points[1] used to determine the rotation
92
62
  if (this.points[0] === undefined) {
@@ -95,18 +65,6 @@ export class Insert extends Entity {
95
65
 
96
66
  this.points[1] = this.points[0].add(new Point(10, 0));
97
67
  }
98
-
99
- if (data.hasOwnProperty('70')) {
100
- // DXF Groupcode 70 - Column Count (optional, default = 1)
101
- const err = 'Groupcode 70 not implemented';
102
- Logging.instance.warn(`${this.type} - ${err}`);
103
- }
104
-
105
- if (data.hasOwnProperty('71')) {
106
- // DXF Groupcode 71 - Row Count(optional, default = 1)
107
- const err = 'Groupcode 71 not implemented';
108
- Logging.instance.warn(`${this.type} - ${err}`);
109
- }
110
68
  }
111
69
  }
112
70
 
@@ -122,6 +80,40 @@ export class Insert extends Entity {
122
80
  return command;
123
81
  }
124
82
 
83
+ /**
84
+ * Normalise raw DXF data before construction
85
+ * @param {Object} data - raw DXF group code object
86
+ * @return {Object} normalised data with blockName and rotation encoded as points[1]
87
+ *
88
+ * Handles:
89
+ * - Group code 2: block name
90
+ * - Group code 50: rotation angle (degrees) encoded into points[1]
91
+ * - Group codes 41–45, 70, 71: unimplemented — logged as warnings
92
+ */
93
+ static fromDxf(data) {
94
+ const normalised = { ...data };
95
+
96
+ // DXF group code 2 - Block name
97
+ if (data[2] !== undefined) {
98
+ normalised.blockName = data[2];
99
+ }
100
+
101
+ // DXF group code 50 - rotation angle in degrees; encode into points
102
+ if (data[50] !== undefined) {
103
+ normalised.rotation = data[50];
104
+ }
105
+
106
+ // Unimplemented group codes
107
+ const unimplemented = { 41: 'X Scale Factor', 42: 'Y Scale Factor', 43: 'Z Scale Factor', 44: 'Column Spacing', 45: 'Row Spacing', 70: 'Column Count', 71: 'Row Count' };
108
+ for (const [code, label] of Object.entries(unimplemented)) {
109
+ if (data[code] !== undefined) {
110
+ Logging.instance.warn(`Insert - Groupcode ${code} (${label}) not implemented`);
111
+ }
112
+ }
113
+
114
+ return normalised;
115
+ }
116
+
125
117
  /**
126
118
  * Execute method
127
119
  * executes the workflow, requesting input required to create an entity
@@ -144,37 +136,30 @@ export class Insert extends Entity {
144
136
  */
145
137
  dxf(file) {
146
138
  file.writeGroupCode('0', 'INSERT');
147
- file.writeGroupCode('5', this.handle, DXFFile.Version.R2000); // Handle
139
+ file.writeGroupCode('5', this.getProperty(Property.Names.HANDLE), DXFFile.Version.R2000); // Handle
148
140
  file.writeGroupCode('100', 'AcDbEntity', DXFFile.Version.R2000);
149
- file.writeGroupCode('8', this.layer);
141
+ file.writeGroupCode('8', this.getProperty(Property.Names.LAYER));
150
142
  this.writeDxfColour(file);
151
143
  file.writeGroupCode('100', 'AcDbBlockReference', DXFFile.Version.R2000);
152
144
  file.writeGroupCode('2', this.block.name);
153
145
  file.writeGroupCode('10', this.points[0].x);
154
146
  file.writeGroupCode('20', this.points[0].y);
155
147
  file.writeGroupCode('30', '0.0');
156
- if (this.rotation) {
157
- file.writeGroupCode('50', this.rotation);
148
+ if (this.getProperty(Property.Names.ROTATION)) {
149
+ file.writeGroupCode('50', this.getProperty(Property.Names.ROTATION));
158
150
  }
159
151
  }
160
152
 
161
153
  /**
162
154
  * Draw the entity
163
- * @param {Object} ctx - context
164
- * @param {number} scale
155
+ * @param {Object} renderer
156
+ * @return {Array} block items for the canvas to render recursively
165
157
  */
166
- draw(ctx, scale) {
158
+ draw(renderer) {
167
159
  // blocks are associated with an insert point.
168
- // translate ctx by the insert location
169
- // this allows the block items to be draw without knowing the insert location
170
-
171
- ctx.save();
172
- ctx.translate(this.points[0].x, this.points[0].y);
173
- const rotation = Utils.degrees2radians(this.rotation);
174
- ctx.rotate(rotation);
175
- // pass *this* to the block to allow colour ByBlock
176
- this.block.draw(ctx, scale, this);
177
- ctx.restore();
160
+ // Apply the insert location and rotation so block items draw correctly.
161
+ renderer.applyTransform({ x: this.points[0].x, y: this.points[0].y, rotation: Utils.degrees2radians(this.getRotation()) });
162
+ return this.block.items;
178
163
  }
179
164
 
180
165
  /**
@@ -222,7 +207,7 @@ export class Insert extends Entity {
222
207
  // offset the item snap point by the block insert location
223
208
  const rotatedPoint = sp.snapPoint.add(this.points[0]);
224
209
  // rotate the snap point to match the item positions
225
- const adjustedPoint = rotatedPoint.rotate(this.points[0], Utils.degrees2radians(this.rotation));
210
+ const adjustedPoint = rotatedPoint.rotate(this.points[0], Utils.degrees2radians(this.getRotation()));
226
211
  snaps.push(new SnapPoint(adjustedPoint, sp.type));
227
212
  }
228
213
 
@@ -250,7 +235,7 @@ export class Insert extends Entity {
250
235
  closestPoint(P) {
251
236
  // get the closest point from the blocks entities
252
237
  // rotate P to match the block rotation
253
- const rotatedPoint = P.rotate(this.points[0], Utils.degrees2radians(-this.rotation));
238
+ const rotatedPoint = P.rotate(this.points[0], Utils.degrees2radians(-this.getRotation()));
254
239
  // adjust P by the insert position
255
240
  const adjustedPoint = rotatedPoint.subtract(this.points[0]);
256
241
  return this.block.closestPoint(adjustedPoint);
@@ -273,7 +258,7 @@ export class Insert extends Entity {
273
258
  * @return {boolean} true if touched
274
259
  */
275
260
  touched(selection) {
276
- const layer = DesignCore.LayerManager.getItemByName(this.layer);
261
+ const layer = DesignCore.LayerManager.getItemByName(this.getProperty(Property.Names.LAYER));
277
262
 
278
263
  if (!layer?.isSelectable) {
279
264
  return;
@@ -5,11 +5,11 @@ import { Input, PromptOptions } from '../lib/inputManager.js';
5
5
  import { Logging } from '../lib/logging.js';
6
6
  import { DXFFile } from '../lib/dxf/dxfFile.js';
7
7
  import { BoundingBox } from '../lib/boundingBox.js';
8
- import { AddState, RemoveState, UpdateState } from '../lib/stateManager.js';
8
+ import { Property } from '../properties/property.js';
9
9
 
10
10
  import { DesignCore } from '../designCore.js';
11
- import { Utils } from '../lib/utils.js';
12
- import { SnapPoint } from '../lib/snapping.js';
11
+
12
+ import { SnapPoint } from '../lib/auxiliary/snapPoint.js';
13
13
 
14
14
 
15
15
  /**
@@ -70,13 +70,18 @@ export class Line extends Entity {
70
70
  if (pt2 === undefined) break;
71
71
 
72
72
  if (Input.getType(pt2) === Input.Type.STRING && pt2 === 'Close') {
73
- this.points.push(pt1);
74
- DesignCore.Scene.inputManager.executeCommand(this);
73
+ // Commit a 2-point segment from the last accumulated point back to pt1
74
+ const segment = new Line({ points: [this.points.at(-1), pt1] });
75
+ DesignCore.Scene.inputManager.executeCommand(segment);
75
76
  return;
76
77
  }
77
78
 
79
+ // Commit a fresh 2-point segment; keep this.points as the accumulator
80
+ // so preview() always draws from the last confirmed point to the mouse.
81
+ const segStart = this.points.at(-1);
78
82
  this.points.push(pt2);
79
- DesignCore.Scene.inputManager.actionCommand(this);
83
+ const segment = new Line({ points: [segStart, pt2] });
84
+ DesignCore.Scene.inputManager.actionCommand(segment);
80
85
  }
81
86
  } catch (err) {
82
87
  Logging.instance.error(`${this.type} - ${err}`);
@@ -90,19 +95,16 @@ export class Line extends Entity {
90
95
  if (this.points.length >= 1) {
91
96
  const mousePoint = DesignCore.Mouse.pointOnScene();
92
97
  const points = [this.points.at(-1), mousePoint];
93
- DesignCore.Scene.tempEntities.create(this.type, { points: points });
98
+ DesignCore.Scene.previewEntities.create(this.type, { points: points });
94
99
  }
95
100
  }
96
101
 
97
102
  /**
98
103
  * Draw the entity
99
- * @param {Object} ctx - context
100
- * @param {number} scale
104
+ * @param {Object} renderer
101
105
  */
102
- draw(ctx, scale) {
103
- ctx.moveTo(this.points[0].x, this.points[0].y);
104
- ctx.lineTo(this.points[1].x, this.points[1].y);
105
- ctx.stroke();
106
+ draw(renderer) {
107
+ renderer.drawShape(this.toPolylinePoints());
106
108
  }
107
109
 
108
110
  /**
@@ -111,18 +113,18 @@ export class Line extends Entity {
111
113
  */
112
114
  dxf(file) {
113
115
  file.writeGroupCode('0', 'LINE');
114
- file.writeGroupCode('5', this.handle, DXFFile.Version.R2000); // Handle
116
+ file.writeGroupCode('5', this.getProperty(Property.Names.HANDLE), DXFFile.Version.R2000); // Handle
115
117
  file.writeGroupCode('100', 'AcDbEntity', DXFFile.Version.R2000);
116
118
  file.writeGroupCode('100', 'AcDbLine', DXFFile.Version.R2000);
117
- file.writeGroupCode('8', this.layer);
118
- file.writeGroupCode('6', this.lineType);
119
+ file.writeGroupCode('8', this.getProperty(Property.Names.LAYER));
120
+ file.writeGroupCode('6', this.getProperty(Property.Names.LINETYPE));
119
121
  file.writeGroupCode('10', this.points[0].x);
120
122
  file.writeGroupCode('20', this.points[0].y);
121
123
  file.writeGroupCode('30', '0.0');
122
124
  file.writeGroupCode('11', this.points[1].x);
123
125
  file.writeGroupCode('21', this.points[1].y);
124
126
  file.writeGroupCode('31', '0.0');
125
- file.writeGroupCode('39', this.lineWidth);
127
+ file.writeGroupCode('39', this.getProperty(Property.Names.LINEWIDTH));
126
128
  }
127
129
 
128
130
  /**
@@ -136,134 +138,11 @@ export class Line extends Entity {
136
138
  /**
137
139
  * Set entity points from a polyline point representation
138
140
  * @param {Array} points
141
+ * @return {Line}
139
142
  */
140
143
  fromPolylinePoints(points) {
141
144
  this.points = [new Point(points[0].x, points[0].y), new Point(points[1].x, points[1].y)];
142
- }
143
-
144
- /**
145
- * Trim the entity
146
- * @param {Array} intersections
147
- * @return {Array} - array of state changes
148
- */
149
- trim(intersections) {
150
- // array to hold state changes
151
- const stateChanges = [];
152
-
153
- if (intersections?.length === 0 || !intersections) {
154
- return stateChanges;
155
- }
156
-
157
- // get the mouse position
158
- const mousePosition = DesignCore.Mouse.pointOnScene();
159
- // get the point on the line closest to the mouse
160
- const pointOnLine = mousePosition.closestPointOnLine(this.points[0], this.points[1]);
161
- // remove any intersections that are at the end points of the line
162
- intersections = intersections.filter((p) => !p.isSame(this.points[0]) && !p.isSame(this.points[1]));
163
-
164
- Utils.sortPointsByDistance(intersections, this.points[0]);
165
-
166
- // Add line points to the start and the end of the point array
167
- const testPoints = [this.points[0], ...intersections, this.points[1]];
168
-
169
- // Test if mouse position is between two intersection points
170
- if (testPoints.length > 1) {
171
- for (let i = 0; i < testPoints.length - 1; i++) {
172
- const startPoint = testPoints[i];
173
- const endPoint = testPoints[i + 1];
174
-
175
- // check if the mouse is between startPoint and endPoint
176
- if (pointOnLine.isOnLine(startPoint, endPoint)) {
177
- const newPoints = [];
178
-
179
- for (const p of testPoints) {
180
- const inOriginalLine = this.points.indexOf(p) !== -1;
181
- const inIntersections = intersections.indexOf(p) !== -1;
182
-
183
- // Keep points which:
184
- // - The mouse is between are intersections
185
- // - The mouse is not between and in this line
186
- const isBetween = p.isSame(startPoint) || p.isSame(endPoint);
187
- if ((isBetween && inIntersections) || (!isBetween && inOriginalLine)) {
188
- newPoints.push(p);
189
- }
190
- }
191
-
192
- if (newPoints.length % 2 === 0) {
193
- // Add lines for each point pair
194
- for (let j = 0; j < newPoints.length; j += 2) {
195
- const line = Utils.cloneObject(this);
196
- line.points = [newPoints[j], newPoints[j + 1]];
197
- const addState = new AddState(line);
198
- stateChanges.push(addState);
199
- }
200
- }
201
- // Remove the existing line
202
- const removeState = new RemoveState(this);
203
- stateChanges.push(removeState);
204
- }
205
- }
206
- }
207
-
208
- return stateChanges;
209
- }
210
-
211
- /**
212
- * Extend the entity
213
- * @param {Array} intersections
214
- * @return {Array} - array of state changes
215
- */
216
- extend(intersections) {
217
- // array to hold state changes
218
- const stateChanges = [];
219
-
220
- if (intersections?.length === 0 || !intersections) {
221
- return stateChanges;
222
- }
223
-
224
- let originPoint;
225
- // Find which end is closer to the mouse
226
- if (this.points[0].distance(DesignCore.Mouse.pointOnScene()) < this.points[1].distance(DesignCore.Mouse.pointOnScene())) {
227
- originPoint = 0;
228
- } else {
229
- originPoint = 1;
230
- }
231
-
232
- Utils.sortPointsByDistance(intersections, this.points[originPoint]);
233
-
234
- const endPoint = this.points[originPoint];
235
- const adjacentPoint = this.points[1 - originPoint];
236
-
237
- // Find the closest intersection that lies beyond the endpoint
238
- const direction = endPoint.subtract(adjacentPoint);
239
- const newEndPoint = intersections.find((p) => {
240
- if (p.isSame(endPoint)) return false;
241
- if (p.subtract(endPoint).dot(direction) <= 0) return false;
242
- if (adjacentPoint.distance(p) <= adjacentPoint.distance(endPoint)) return false;
243
- return true;
244
- });
245
-
246
- if (!newEndPoint) {
247
- return stateChanges;
248
- }
249
-
250
- const newPoints = [];
251
- if (originPoint === 0) {
252
- newPoints.push(newEndPoint, this.points[1]);
253
- } else {
254
- newPoints.push(this.points[0], newEndPoint);
255
- }
256
-
257
- if (newPoints[0].isSame(this.points[0]) && newPoints[1].isSame(this.points[1])) {
258
- // No change
259
- return stateChanges;
260
- }
261
-
262
- // update the line
263
- const addState = new UpdateState(this, { points: newPoints });
264
- stateChanges.push(addState);
265
-
266
- return stateChanges;
145
+ return this;
267
146
  }
268
147
 
269
148
  /**
@@ -1,12 +1,13 @@
1
1
  import { Logging } from '../lib/logging.js';
2
- import { BasePolyline } from './basePolyline.js';
3
2
  import { Polyline } from './polyline.js';
4
3
 
5
4
  /**
6
5
  * LWPolyline Entity Class
7
- * @extends BasePolyline
6
+ * LWPolyline is a DXF import-only entity. The constructor pre-processes the
7
+ * raw DXF data and returns a {@link Polyline} instance. It is never
8
+ * instantiated as a standalone entity within the application.
8
9
  */
9
- export class Lwpolyline extends BasePolyline {
10
+ export class Lwpolyline {
10
11
  static type = 'Lwpolyline';
11
12
 
12
13
  /**
@@ -14,16 +15,20 @@ export class Lwpolyline extends BasePolyline {
14
15
  * @param {Array} data
15
16
  */
16
17
  constructor(data) {
17
- super(data);
18
-
19
- if (data) {
20
- if (data.hasOwnProperty('90')) {
21
- // DXF Groupcode 90 - Number of vertices
22
- if (this.points.length !== data[90]) {
23
- Logging.instance.error(`LWPOLYLINE Vertices Count`);
24
- }
18
+ if (data?.hasOwnProperty('90')) {
19
+ // DXF Groupcode 90 - Number of vertices
20
+ if (!Array.isArray(data.points)) {
21
+ Logging.instance.error(`LWPOLYLINE: group code 90 specifies ${data[90]} vertices but points array is missing or invalid`);
22
+ } else if (data.points.length !== data[90]) {
23
+ Logging.instance.error(`LWPOLYLINE: group code 90 specifies ${data[90]} vertices but ${data.points.length} were parsed`);
25
24
  }
26
25
  }
26
+
27
+ if (data?.points?.length >= 4 && data.points[0].isSame(data.points.at(-1))) {
28
+ data.points.pop();
29
+ data[70] = (data[70] ?? 0) | 1;
30
+ }
31
+
27
32
  return new Polyline(data);
28
33
  }
29
34
 
@@ -47,14 +47,14 @@ export class Polyline extends BasePolyline {
47
47
  if (file.version < DXFFile.Version.R2000) {
48
48
  // POLYLINE/VERTEX/SEQEND format for R12
49
49
  file.writeGroupCode('0', 'POLYLINE');
50
- file.writeGroupCode('8', this.layer);
51
- file.writeGroupCode('6', this.lineType);
50
+ file.writeGroupCode('8', this.getProperty('layer'));
51
+ file.writeGroupCode('6', this.getProperty('lineType'));
52
52
  file.writeGroupCode('66', 1); // vertices-follow flag
53
53
  file.writeGroupCode('70', this.flags.getFlagValue());
54
54
 
55
55
  for (let i = 0; i < this.points.length; i++) {
56
56
  file.writeGroupCode('0', 'VERTEX');
57
- file.writeGroupCode('8', this.layer);
57
+ file.writeGroupCode('8', this.getProperty('layer'));
58
58
  file.writeGroupCode('10', this.points[i].x);
59
59
  file.writeGroupCode('20', this.points[i].y);
60
60
  file.writeGroupCode('30', '0.0');
@@ -62,7 +62,7 @@ export class Polyline extends BasePolyline {
62
62
  }
63
63
 
64
64
  file.writeGroupCode('0', 'SEQEND');
65
- file.writeGroupCode('8', this.layer);
65
+ file.writeGroupCode('8', this.getProperty('layer'));
66
66
  return;
67
67
  }
68
68
 
@@ -4,6 +4,7 @@ import { Entity } from './entity.js';
4
4
  import { Input, PromptOptions } from '../lib/inputManager.js';
5
5
  import { Logging } from '../lib/logging.js';
6
6
  import { Polyline } from './polyline.js';
7
+ import { RubberBand } from '../lib/auxiliary/rubberBand.js';
7
8
 
8
9
  import { DesignCore } from '../designCore.js';
9
10
 
@@ -67,8 +68,9 @@ export class Rectangle extends Entity {
67
68
  preview() {
68
69
  if (this.points.length >= 1) {
69
70
  const mousePoint = DesignCore.Mouse.pointOnScene();
71
+ DesignCore.Scene.auxiliaryEntities.add(new RubberBand([this.points.at(-1), mousePoint]));
70
72
  const points = this.rectPoints(this.points.at(-1), mousePoint);
71
- DesignCore.Scene.tempEntities.create('Polyline', { points: points });
73
+ DesignCore.Scene.previewEntities.create('Polyline', { points: points });
72
74
  }
73
75
  }
74
76
 
@@ -1,6 +1,7 @@
1
1
  import { Entity } from './entity.js';
2
2
  import { DXFFile } from '../lib/dxf/dxfFile.js';
3
3
  import { BoundingBox } from '../lib/boundingBox.js';
4
+ import { Property } from '../properties/property.js';
4
5
 
5
6
  /**
6
7
  * Solid Entity Class
@@ -19,20 +20,10 @@ export class Solid extends Entity {
19
20
 
20
21
  /**
21
22
  * Draw the entity
22
- * @param {Object} ctx - context
23
- * @param {number} scale
23
+ * @param {Object} renderer
24
24
  */
25
- draw(ctx, scale) {
26
- this.points.forEach((point, index) => {
27
- if (index == 0) {
28
- ctx.moveTo(point.x, point.y);
29
- } else {
30
- ctx.lineTo(point.x, point.y);
31
- }
32
- });
33
-
34
- ctx.closePath();
35
- ctx.fill();
25
+ draw(renderer) {
26
+ renderer.drawShape(this.points, { closed: true, fill: true, stroke: false });
36
27
  }
37
28
 
38
29
  /**
@@ -41,10 +32,10 @@ export class Solid extends Entity {
41
32
  */
42
33
  dxf(file) {
43
34
  file.writeGroupCode('0', 'SOLID');
44
- file.writeGroupCode('5', this.handle, DXFFile.Version.R2000); // Handle
35
+ file.writeGroupCode('5', this.getProperty(Property.Names.HANDLE), DXFFile.Version.R2000); // Handle
45
36
  file.writeGroupCode('100', 'AcDbEntity', DXFFile.Version.R2000);
46
37
  file.writeGroupCode('100', 'AcDbTrace', DXFFile.Version.R2000);
47
- file.writeGroupCode('8', this.layer);
38
+ file.writeGroupCode('8', this.getProperty(Property.Names.LAYER));
48
39
  file.writeGroupCode('10', this.points[0].x);
49
40
  file.writeGroupCode('20', this.points[0].y);
50
41
  file.writeGroupCode('30', '0.0');