@design-core/core 0.0.6 → 50.1.0

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 (173) hide show
  1. package/.eslintrc.json +4 -0
  2. package/.github/workflows/DesignCoreQA.yml +3 -0
  3. package/TODO.md +49 -6
  4. package/core/core/core.js +0 -3
  5. package/core/designCore.js +1 -1
  6. package/core/dimensions/alignedDimension.js +12 -11
  7. package/core/dimensions/angularDimension.js +38 -37
  8. package/core/dimensions/{baseCircularDimension.js → circularDimensionBase.js} +22 -21
  9. package/core/dimensions/diametricDimension.js +14 -13
  10. package/core/dimensions/dimension.js +62 -55
  11. package/core/dimensions/{baseDimension.js → dimensionBase.js} +73 -107
  12. package/core/dimensions/{baseLinearDimension.js → linearDimensionBase.js} +15 -14
  13. package/core/dimensions/radialDimension.js +12 -11
  14. package/core/dimensions/rotatedDimension.js +20 -19
  15. package/core/entities/arc.js +123 -156
  16. package/core/entities/arctext.js +222 -184
  17. package/core/entities/circle.js +77 -99
  18. package/core/entities/entity.js +114 -93
  19. package/core/entities/hatch.js +346 -273
  20. package/core/entities/insert.js +68 -83
  21. package/core/entities/line.js +21 -142
  22. package/core/entities/lwpolyline.js +16 -11
  23. package/core/entities/point.js +19 -19
  24. package/core/entities/polyline.js +7 -7
  25. package/core/entities/{basePolyline.js → polylineBase.js} +47 -299
  26. package/core/entities/rectangle.js +3 -1
  27. package/core/entities/solid.js +6 -15
  28. package/core/entities/text.js +190 -201
  29. package/core/lib/auxiliary/rubberBand.js +32 -0
  30. package/core/lib/auxiliary/selectionWindow.js +63 -0
  31. package/core/lib/auxiliary/snapPoint.js +113 -0
  32. package/core/lib/auxiliary/trackingLine.js +76 -0
  33. package/core/lib/boundingBox.js +11 -11
  34. package/core/lib/canvas.js +232 -168
  35. package/core/lib/clipboard.js +9 -3
  36. package/core/lib/colour.js +16 -0
  37. package/core/lib/commandLine.js +1 -6
  38. package/core/lib/commandManager.js +18 -8
  39. package/core/lib/constants.js +28 -0
  40. package/core/lib/dxf/dxf.js +15 -14
  41. package/core/lib/dxf/dxfIterator.js +2 -2
  42. package/core/lib/entityManager.js +33 -26
  43. package/core/lib/inputManager.js +36 -12
  44. package/core/lib/intersect.js +25 -0
  45. package/core/lib/matrix.js +1 -1
  46. package/core/lib/patterns.js +9 -2
  47. package/core/lib/plotOptions.js +67 -0
  48. package/core/lib/polylineUtils.js +75 -0
  49. package/core/lib/renderers/cairoRenderer.js +371 -0
  50. package/core/lib/renderers/canvasRenderer.js +315 -0
  51. package/core/lib/renderers/pdfRenderer.js +366 -0
  52. package/core/lib/renderers/rendererBase.js +284 -0
  53. package/core/lib/renderers/svgRenderer.js +343 -0
  54. package/core/lib/scene.js +28 -6
  55. package/core/lib/selectionManager.js +20 -20
  56. package/core/lib/settings.js +4 -2
  57. package/core/lib/snapping.js +4 -191
  58. package/core/lib/stateManager.js +30 -1
  59. package/core/lib/utils.js +26 -7
  60. package/core/properties/entityProperties.js +104 -0
  61. package/core/properties/property.js +106 -2
  62. package/core/properties/propertyManager.js +60 -44
  63. package/core/tables/block.js +50 -81
  64. package/core/tables/dimStyle.js +5 -5
  65. package/core/tables/endblk.js +1 -1
  66. package/core/tables/layer.js +8 -0
  67. package/core/tables/ltypeManager.js +1 -1
  68. package/core/tables/styleManagerBase.js +3 -2
  69. package/core/tables/tableManagerBase.js +8 -3
  70. package/core/tables/vport.js +2 -2
  71. package/core/tools/chamfer.js +117 -143
  72. package/core/tools/chamferFilletBase.js +138 -37
  73. package/core/tools/copy.js +11 -11
  74. package/core/tools/copybase.js +3 -3
  75. package/core/tools/copyclip.js +3 -3
  76. package/core/tools/cornerEntity.js +2 -2
  77. package/core/tools/cutclip.js +3 -3
  78. package/core/tools/explode.js +7 -7
  79. package/core/tools/extend.js +138 -25
  80. package/core/tools/fillet.js +92 -189
  81. package/core/tools/matchProp.js +3 -12
  82. package/core/tools/mirror.js +15 -14
  83. package/core/tools/move.js +8 -8
  84. package/core/tools/offset.js +31 -33
  85. package/core/tools/pasteclip.js +1 -1
  86. package/core/tools/purge.js +10 -10
  87. package/core/tools/rotate.js +19 -16
  88. package/core/tools/scale.js +7 -7
  89. package/core/tools/trim.js +271 -29
  90. package/core/tools/zoom.js +1 -1
  91. package/cspell.json +321 -0
  92. package/jest.config.js +6 -0
  93. package/package.json +5 -3
  94. package/test/dimensions/alignedDimension.test.js +17 -10
  95. package/test/dimensions/angularDimension.test.js +13 -13
  96. package/test/dimensions/diametricDimension.test.js +15 -15
  97. package/test/dimensions/dimension.test.js +12 -12
  98. package/test/dimensions/{baseDimension.test.js → dimensionBase.test.js} +73 -12
  99. package/test/dimensions/linearDimensionBase.test.js +62 -0
  100. package/test/dimensions/radialDimension.test.js +15 -15
  101. package/test/dimensions/rotatedDimension.test.js +18 -11
  102. package/test/entities/arc.test.js +83 -86
  103. package/test/entities/arctext.test.js +184 -57
  104. package/test/entities/circle.test.js +68 -96
  105. package/test/entities/entity.test.js +42 -8
  106. package/test/entities/hatch.test.js +569 -96
  107. package/test/entities/insert.test.js +99 -4
  108. package/test/entities/line.test.js +15 -41
  109. package/test/entities/lwpolyline.test.js +104 -0
  110. package/test/entities/point.test.js +3 -3
  111. package/test/entities/polyline.test.js +11 -0
  112. package/test/entities/{basePolyline.test.js → polylineBase.test.js} +192 -57
  113. package/test/entities/rectangle.test.js +11 -0
  114. package/test/entities/solid.test.js +1 -1
  115. package/test/entities/text.test.js +302 -77
  116. package/test/integration/exportIntegration.reference.dxf +9 -9
  117. package/test/integration/exportIntegration.test.js +5 -5
  118. package/test/integration/intersectionIntegration.test.js +4 -5
  119. package/test/integration/pdfRendererIntegration.reference.pdf +17709 -0
  120. package/test/integration/pdfRendererIntegration.test.js +61 -0
  121. package/test/integration/svgRendererIntegration.reference.svg +3343 -0
  122. package/test/integration/svgRendererIntegration.test.js +61 -0
  123. package/test/integration/userInputIntegration.reference.dxf +1165 -105
  124. package/test/integration/userInputIntegration.test.js +239 -132
  125. package/test/lib/boundingBox.test.js +3 -3
  126. package/test/lib/canvas.test.js +744 -125
  127. package/test/lib/constants.test.js +27 -0
  128. package/test/lib/entityManager.test.js +4 -4
  129. package/test/lib/inputManager.test.js +81 -20
  130. package/test/lib/intersect.test.js +83 -0
  131. package/test/lib/plotOptions.test.js +179 -0
  132. package/test/lib/rubberBand.test.js +84 -0
  133. package/test/lib/scene.test.js +9 -9
  134. package/test/lib/selectionManager.test.js +3 -3
  135. package/test/lib/settings.test.js +5 -6
  136. package/test/lib/snapping.test.js +72 -130
  137. package/test/lib/stateManager.test.js +7 -7
  138. package/test/lib/utils.test.js +36 -2
  139. package/test/properties/entityProperties.test.js +132 -0
  140. package/test/properties/property.test.js +53 -0
  141. package/test/properties/propertyManager.test.js +25 -25
  142. package/test/renderers/canvasRenderer.test.js +317 -0
  143. package/test/renderers/pdfRenderer.test.js +271 -0
  144. package/test/renderers/rendererBase.test.js +213 -0
  145. package/test/renderers/svgRenderer.test.js +326 -0
  146. package/test/tables/block.test.js +92 -13
  147. package/test/tables/endblk.test.js +2 -2
  148. package/test/tables/layer.test.js +30 -0
  149. package/test/tables/layerManager.test.js +2 -2
  150. package/test/test-helpers/hatchGridRenderer.js +180 -0
  151. package/test/test-helpers/test-helpers.js +158 -6
  152. package/test/tools/chamfer.test.js +273 -51
  153. package/test/tools/chamferFilletBase.test.js +125 -36
  154. package/test/tools/copy.test.js +8 -8
  155. package/test/tools/copybase.test.js +2 -2
  156. package/test/tools/copyclip.test.js +2 -2
  157. package/test/tools/cornerEntity.test.js +8 -8
  158. package/test/tools/cutclip.test.js +2 -2
  159. package/test/tools/erase.test.js +6 -6
  160. package/test/tools/explode.test.js +5 -5
  161. package/test/tools/extend.test.js +373 -42
  162. package/test/tools/fillet.test.js +230 -49
  163. package/test/tools/matchProp.test.js +11 -11
  164. package/test/tools/mirror.test.js +39 -39
  165. package/test/tools/move.test.js +7 -7
  166. package/test/tools/offset.test.js +222 -55
  167. package/test/tools/pasteclip.test.js +5 -5
  168. package/test/tools/purge.test.js +4 -4
  169. package/test/tools/rotate.test.js +39 -9
  170. package/test/tools/scale.test.js +33 -33
  171. package/test/tools/trim.test.js +579 -53
  172. package/test/tools/zoom.test.js +12 -12
  173. package/core/lib/selectionWindow.js +0 -83
@@ -10,17 +10,18 @@ import { Line } from './line.js';
10
10
  import { Arc } from './arc.js';
11
11
  import { Flags } from '../properties/flags.js';
12
12
  import { Property } from '../properties/property.js';
13
- import { AddState, RemoveState, UpdateState } from '../lib/stateManager.js';
13
+
14
14
 
15
15
  import { DesignCore } from '../designCore.js';
16
- import { SnapPoint } from '../lib/snapping.js';
16
+ import { SnapPoint } from '../lib/auxiliary/snapPoint.js';
17
+ import { RubberBand } from '../lib/auxiliary/rubberBand.js';
17
18
 
18
19
  /**
19
20
  * Base Polyline Entity Class
20
21
  * @extends Entity
21
22
  */
22
- export class BasePolyline extends Entity {
23
- static type = 'BasePolyline';
23
+ export class PolylineBase extends Entity {
24
+ static type = 'PolylineBase';
24
25
 
25
26
  /**
26
27
  * Create a Base Polyline
@@ -75,6 +76,14 @@ export class BasePolyline extends Entity {
75
76
 
76
77
  this.flags.setFlagValue(Property.loadValue([data?.flags, data?.[70]], 0));
77
78
 
79
+ // DXF Groupcode 70 bit 1 - closed flag exposed via EntityProperties
80
+ this.properties.add(Property.Names.CLOSED, {
81
+ type: Property.Type.BOOLEAN,
82
+ get: (entity) => entity.flags.hasFlag(1),
83
+ set: (entity, value) => value ? entity.flags.addValue(1) : entity.flags.removeValue(1),
84
+ dxfCode: 70,
85
+ });
86
+
78
87
  // If the first and last points are the same the polyline should be considered closed.
79
88
  // Require at least 4 points so that after removing the duplicate endpoint at least 3 remain.
80
89
  if (this.points.length >= 4 && this.points[0].isSame(this.points.at(-1))) {
@@ -161,54 +170,25 @@ export class BasePolyline extends Entity {
161
170
 
162
171
  if (this.points.length >= 1) {
163
172
  const points = [...this.points, mousePoint];
164
- DesignCore.Scene.tempEntities.create(this.type, { points: points });
165
- }
166
-
167
- if (this.inputMode === this.modes.ARC) {
168
- const arcpoints = Utils.cloneObject(this.points);
169
- arcpoints.at(-1).bulge = this.getBulgeFromSegment(mousePoint);
170
- const points = [...arcpoints, mousePoint];
171
- DesignCore.Scene.tempEntities.create(this.type, { points: points });
173
+ if (this.inputMode === this.modes.ARC) {
174
+ // Straight segment is not the final form — show as rubber-band indicator
175
+ DesignCore.Scene.auxiliaryEntities.add(new RubberBand([this.points.at(-1), mousePoint]));
176
+ // Arc segment is the final form — draw on the current layer
177
+ const arcpoints = Utils.cloneObject(this.points);
178
+ arcpoints.at(-1).bulge = this.getBulgeFromSegment(mousePoint);
179
+ DesignCore.Scene.previewEntities.create(this.type, { points: [...arcpoints, mousePoint] });
180
+ } else {
181
+ DesignCore.Scene.previewEntities.create(this.type, { points: points });
182
+ }
172
183
  }
173
184
  }
174
185
 
175
186
  /**
176
187
  * Draw the polyline
177
- * @param {Object} ctx
178
- * @param {number} scale
179
- * @param {boolean} stroke - don't stroke hatch boundary shapes
188
+ * @param {Object} renderer
180
189
  */
181
- draw(ctx, scale, stroke = true) {
182
- for (let i = 0; i < this.points.length; i++) {
183
- if (this.points[i].bulge === 0) {
184
- ctx.lineTo(this.points[i].x, this.points[i].y);
185
- } else {
186
- // define the next point or the first point for closed shapes
187
- const nextPoint = this.points[i + 1] || this.points[0];
188
- const centerPoint = this.points[i].bulgeCentrePoint(nextPoint);
189
- const radius = this.points[i].bulgeRadius(nextPoint);
190
-
191
- if (this.points[i].bulge > 0) {
192
- // TODO: make this work with canvas
193
- ctx.arc(centerPoint.x, centerPoint.y, radius, centerPoint.angle(this.points[i]), centerPoint.angle(nextPoint));
194
- } else {
195
- try { // HTML
196
- ctx.arc(centerPoint.x, centerPoint.y, radius, centerPoint.angle(this.points[i]), centerPoint.angle(nextPoint), true);
197
- } catch { // Cairo
198
- ctx.arcNegative(centerPoint.x, centerPoint.y, radius, centerPoint.angle(this.points[i]), centerPoint.angle(nextPoint));
199
- }
200
- }
201
- }
202
- }
203
-
204
- // handle a closed shape
205
- if (this.flags.hasFlag(1)) {
206
- ctx.lineTo(this.points[0].x, this.points[0].y);
207
- }
208
-
209
- if (stroke) {
210
- ctx.stroke();
211
- }
190
+ draw(renderer) {
191
+ renderer.drawShape(this.toPolylinePoints());
212
192
  }
213
193
 
214
194
  /**
@@ -217,12 +197,12 @@ export class BasePolyline extends Entity {
217
197
  */
218
198
  dxf(file) {
219
199
  file.writeGroupCode('0', 'LWPOLYLINE');
220
- file.writeGroupCode('5', this.handle, DXFFile.Version.R2000); // Handle
200
+ file.writeGroupCode('5', this.getProperty(Property.Names.HANDLE), DXFFile.Version.R2000); // Handle
221
201
  file.writeGroupCode('100', 'AcDbEntity', DXFFile.Version.R2000);
222
202
  file.writeGroupCode('100', 'AcDbPolyline', DXFFile.Version.R2000);
223
- file.writeGroupCode('8', this.layer); // LAYERNAME
224
- file.writeGroupCode('6', this.lineType);
225
- file.writeGroupCode('39', this.lineWidth);
203
+ file.writeGroupCode('8', this.getProperty(Property.Names.LAYER)); // LAYERNAME
204
+ file.writeGroupCode('6', this.getProperty(Property.Names.LINETYPE));
205
+ file.writeGroupCode('39', this.getProperty(Property.Names.LINEWIDTH));
226
206
  file.writeGroupCode('90', this.points.length);
227
207
  file.writeGroupCode('70', this.flags.getFlagValue());
228
208
  for (let i = 0; i < this.points.length; i++) {
@@ -253,6 +233,7 @@ export class BasePolyline extends Entity {
253
233
  /**
254
234
  * Set entity points from a polyline point representation
255
235
  * @param {Array} points
236
+ * @return {PolylineBase}
256
237
  */
257
238
  fromPolylinePoints(points) {
258
239
  const pts = points.map((p) => new Point(p.x, p.y, p.bulge));
@@ -260,6 +241,7 @@ export class BasePolyline extends Entity {
260
241
  pts.pop();
261
242
  }
262
243
  this.points = pts;
244
+ return this;
263
245
  }
264
246
 
265
247
  /**
@@ -329,10 +311,10 @@ export class BasePolyline extends Entity {
329
311
  let closestPoint;
330
312
  let candidateSegment;
331
313
  if (A.bulge !== 0) {
332
- const center = A.bulgeCentrePoint(B);
314
+ const centre = A.bulgeCentrePoint(B);
333
315
  const direction = A.bulge > 0 ? 1 : -1;
334
- closestPoint = P.closestPointOnArc(A, B, center, direction);
335
- candidateSegment = new Arc({ points: [center, A, B], direction });
316
+ closestPoint = P.closestPointOnArc(A, B, centre, direction);
317
+ candidateSegment = new Arc({ points: [centre, A, B], direction });
336
318
  } else {
337
319
  closestPoint = P.closestPointOnLine(A, B);
338
320
  candidateSegment = new Line({ points: [A, B] });
@@ -438,9 +420,9 @@ export class BasePolyline extends Entity {
438
420
  */
439
421
  isPointOnSegment(point, A, B) {
440
422
  if (A.bulge !== 0) {
441
- const center = A.bulgeCentrePoint(B);
423
+ const centre = A.bulgeCentrePoint(B);
442
424
  const direction = A.bulge > 0 ? 1 : -1;
443
- return point.isOnArc(A, B, center, direction);
425
+ return point.isOnArc(A, B, centre, direction);
444
426
  }
445
427
  return point.isOnLine(A, B);
446
428
  }
@@ -456,9 +438,9 @@ export class BasePolyline extends Entity {
456
438
  const B = this.points[segmentIndex % this.points.length];
457
439
 
458
440
  if (A.bulge !== 0) {
459
- const center = A.bulgeCentrePoint(B);
441
+ const centre = A.bulgeCentrePoint(B);
460
442
  const direction = A.bulge > 0 ? 1 : -1;
461
- const closest = point.closestPointOnArc(A, B, center, direction);
443
+ const closest = point.closestPointOnArc(A, B, centre, direction);
462
444
 
463
445
  if (closest) {
464
446
  return closest;
@@ -482,250 +464,16 @@ export class BasePolyline extends Entity {
482
464
  const B = this.points[segmentIndex % this.points.length];
483
465
 
484
466
  if (A.bulge !== 0) {
485
- const center = A.bulgeCentrePoint(B);
467
+ const centre = A.bulgeCentrePoint(B);
486
468
  const direction = A.bulge > 0 ? 1 : -1;
487
- const startAngle = center.angle(A);
488
- const pointAngle = center.angle(point);
469
+ const startAngle = centre.angle(A);
470
+ const pointAngle = centre.angle(point);
489
471
  return ((pointAngle - startAngle) * direction + 4 * Math.PI) % (2 * Math.PI);
490
472
  }
491
473
 
492
474
  return A.distance(point);
493
475
  }
494
476
 
495
- /**
496
- * Extend the entity
497
- * Only extends when the end segment closest to the mouse is a line segment (bulge === 0)
498
- * @param {Array} intersections - array of intersection points
499
- * @return {Array} - array of state changes
500
- */
501
- extend(intersections) {
502
- const stateChanges = [];
503
-
504
- if (!intersections?.length) {
505
- return stateChanges;
506
- }
507
-
508
- const mousePosition = DesignCore.Mouse.pointOnScene();
509
- const lastIndex = this.points.length - 1;
510
-
511
- // Determine which end segment is closer to the mouse
512
- const closestOnFirst = this.closestPointOnSegment(mousePosition, 1);
513
- const closestOnLast = this.closestPointOnSegment(mousePosition, lastIndex);
514
- const distToFirst = mousePosition.distance(closestOnFirst);
515
- const distToLast = mousePosition.distance(closestOnLast);
516
-
517
- // Identify the selected end: the endpoint index and the bulge of its segment
518
- let endPointIndex;
519
- let endSegmentBulge;
520
-
521
- if (distToFirst < distToLast) {
522
- endPointIndex = 0;
523
- endSegmentBulge = this.points[0].bulge;
524
- } else if (distToLast < distToFirst) {
525
- endPointIndex = lastIndex;
526
- endSegmentBulge = this.points[lastIndex - 1].bulge;
527
- } else {
528
- // Tie (includes 2-point polyline where both segments are the same)
529
- // Break tie by distance to the actual endpoints
530
- if (mousePosition.distance(this.points[0]) <= mousePosition.distance(this.points[lastIndex])) {
531
- endPointIndex = 0;
532
- endSegmentBulge = this.points[0].bulge;
533
- } else {
534
- endPointIndex = lastIndex;
535
- endSegmentBulge = this.points[lastIndex - 1].bulge;
536
- }
537
- }
538
-
539
- // Only allow extending line segments (not arcs)
540
- if (endSegmentBulge !== 0) {
541
- DesignCore.Core.notify(`${this.type} - ${Strings.Message.NOEXTEND}`);
542
- return stateChanges;
543
- }
544
-
545
- const endPoint = this.points[endPointIndex];
546
- const adjacentPoint = endPointIndex === 0 ? this.points[1] : this.points[lastIndex - 1];
547
-
548
- // Sort intersections by distance from the end point
549
- Utils.sortPointsByDistance(intersections, endPoint);
550
-
551
- // Find the closest intersection that lies beyond the endpoint
552
- const direction = endPoint.subtract(adjacentPoint);
553
- const newEndPoint = intersections.find((p) => {
554
- if (p.isSame(endPoint)) return false;
555
- if (p.subtract(endPoint).dot(direction) <= 0) return false;
556
- if (adjacentPoint.distance(p) <= adjacentPoint.distance(endPoint)) return false;
557
- return true;
558
- });
559
-
560
- if (!newEndPoint) {
561
- return stateChanges;
562
- }
563
-
564
- // Build the new points array
565
- const newPoints = this.points.map((p) => p.clone());
566
- newPoints[endPointIndex] = newEndPoint;
567
-
568
- if (newPoints[endPointIndex].isSame(this.points[endPointIndex])) {
569
- return stateChanges;
570
- }
571
-
572
- stateChanges.push(new UpdateState(this, { points: newPoints }));
573
- return stateChanges;
574
- }
575
-
576
- /**
577
- * Trim the entity
578
- * @param {Array} intersections - array of intersection points
579
- * @return {Array} - array of state changes
580
- */
581
- trim(intersections) {
582
- const stateChanges = [];
583
-
584
- if (!intersections?.length) {
585
- return stateChanges;
586
- }
587
-
588
- const mousePosition = DesignCore.Mouse.pointOnScene();
589
-
590
- // Find the segment index closest to the mouse
591
- let mouseSegmentIndex = 0;
592
- let minDistance = Infinity;
593
-
594
- for (let i = 1; i < this.points.length; i++) {
595
- const closestPoint = this.closestPointOnSegment(mousePosition, i);
596
- if (closestPoint) {
597
- const dist = mousePosition.distance(closestPoint);
598
- if (dist < minDistance) {
599
- minDistance = dist;
600
- mouseSegmentIndex = i;
601
- }
602
- }
603
- }
604
-
605
- // Filter out intersections that coincide with existing polyline vertices
606
- const filteredIntersections = intersections.filter((point) => !this.points.some((vertex) => vertex.isSame(point)));
607
-
608
- if (!filteredIntersections.length) {
609
- return stateChanges;
610
- }
611
-
612
- // Locate each intersection on its segment with an ordering value
613
- const locatedIntersections = [];
614
-
615
- for (const point of filteredIntersections) {
616
- for (let i = 1; i < this.points.length; i++) {
617
- if (this.isPointOnSegment(point, this.points[i - 1], this.points[i])) {
618
- locatedIntersections.push({
619
- segmentIndex: i,
620
- point: point,
621
- positionAlongSegment: this.positionOnSegment(point, i),
622
- });
623
- break;
624
- }
625
- }
626
- }
627
-
628
- if (!locatedIntersections.length) {
629
- return stateChanges;
630
- }
631
-
632
- // Sort by segment index, then by position along segment
633
- locatedIntersections.sort((a, b) => {
634
- if (a.segmentIndex !== b.segmentIndex) return a.segmentIndex - b.segmentIndex;
635
- return a.positionAlongSegment - b.positionAlongSegment;
636
- });
637
-
638
- // Precompute the mouse position along its segment
639
- const mouseClosest = this.closestPointOnSegment(mousePosition, mouseSegmentIndex);
640
- const mousePos = mouseClosest ? this.positionOnSegment(mouseClosest, mouseSegmentIndex) : 0;
641
-
642
- // Find the nearest intersections that bracket the mouse segment
643
- // - trimBefore: the last intersection at or before the mouse
644
- // - trimAfter: the first intersection at or after the mouse
645
- let trimBefore = null;
646
- let trimAfter = null;
647
-
648
- for (const loc of locatedIntersections) {
649
- if (loc.segmentIndex < mouseSegmentIndex) {
650
- trimBefore = loc;
651
- } else if (loc.segmentIndex === mouseSegmentIndex) {
652
- if (loc.positionAlongSegment <= mousePos) {
653
- trimBefore = loc;
654
- } else if (!trimAfter) {
655
- trimAfter = loc;
656
- }
657
- } else if (!trimAfter) {
658
- trimAfter = loc;
659
- }
660
- }
661
-
662
- // Build new polyline(s) from the remaining portions
663
- // Portion 1: start of polyline to trimBefore point
664
- if (trimBefore) {
665
- const points = [];
666
-
667
- for (let i = 0; i < trimBefore.segmentIndex - 1; i++) {
668
- points.push(this.points[i].clone());
669
- }
670
-
671
- // Add the segment start point (with adjusted bulge for arcs)
672
- const segStart = this.points[trimBefore.segmentIndex - 1];
673
- const segStartClone = segStart.clone();
674
- if (segStart.bulge !== 0) {
675
- segStartClone.bulge = segStart.partialBulge(this.points[trimBefore.segmentIndex], trimBefore.point);
676
- }
677
- points.push(segStartClone);
678
-
679
- // Add the trim point (skip if it coincides with the last added point)
680
- const trimPoint = trimBefore.point.clone();
681
- if (!points.at(-1).isSame(trimPoint)) {
682
- points.push(trimPoint);
683
- }
684
-
685
- if (points.length >= 2) {
686
- const polyline = Utils.cloneObject(this);
687
- polyline.points = points;
688
- polyline.flags.removeValue(1); // clear closed bit
689
- stateChanges.push(new AddState(polyline));
690
- }
691
- }
692
-
693
- // Portion 2: trimAfter point to end of polyline
694
- if (trimAfter) {
695
- const points = [];
696
- const trimPoint = trimAfter.point.clone();
697
-
698
- // If the segment is an arc, set remaining arc bulge
699
- const segStart = this.points[trimAfter.segmentIndex - 1];
700
- if (segStart.bulge !== 0) {
701
- trimPoint.bulge = segStart.partialBulge(this.points[trimAfter.segmentIndex], trimPoint, true);
702
- }
703
-
704
- points.push(trimPoint);
705
-
706
- // Add remaining points (skip if coincides with the trim point)
707
- for (let i = trimAfter.segmentIndex; i < this.points.length; i++) {
708
- const nextPoint = this.points[i].clone();
709
- if (!points.at(-1).isSame(nextPoint)) {
710
- points.push(nextPoint);
711
- }
712
- }
713
-
714
- if (points.length >= 2) {
715
- const polyline = Utils.cloneObject(this);
716
- polyline.points = points;
717
- polyline.flags.removeValue(1); // clear closed bit
718
- stateChanges.push(new AddState(polyline));
719
- }
720
- }
721
-
722
- if (trimBefore || trimAfter) {
723
- stateChanges.push(new RemoveState(this));
724
- }
725
-
726
- return stateChanges;
727
- }
728
-
729
477
  /**
730
478
  * Return boundingbox for entity
731
479
  * @return {BoundingBox}
@@ -742,8 +490,8 @@ export class BasePolyline extends Entity {
742
490
  let boundingBox = BoundingBox.lineBoundingBox(this.points[i], nextPoint);
743
491
 
744
492
  if (this.points[i].bulge !== 0) {
745
- const centerPoint = this.points[i].bulgeCentrePoint(nextPoint);
746
- boundingBox = BoundingBox.arcBoundingBox(centerPoint, this.points[i], nextPoint, this.points[i].bulge);
493
+ const centrePoint = this.points[i].bulgeCentrePoint(nextPoint);
494
+ boundingBox = BoundingBox.arcBoundingBox(centrePoint, this.points[i], nextPoint, this.points[i].bulge);
747
495
  }
748
496
 
749
497
  xmin = Math.min(xmin, boundingBox.xMin);
@@ -771,9 +519,9 @@ export class BasePolyline extends Entity {
771
519
  // get the angle at the end of the previous segment
772
520
  let lastSegAngle;
773
521
  if (lastSegIsArc) {
774
- const centerPoint = this.points.at(-2).bulgeCentrePoint(this.points.at(-1));
775
- // angle is perpendicular to the center-to-end point ray, in the direction of the bulge
776
- lastSegAngle = centerPoint.angle(this.points.at(-1)) + (Math.PI / 2) * Math.sign(lastSegBulge);
522
+ const centrePoint = this.points.at(-2).bulgeCentrePoint(this.points.at(-1));
523
+ // angle is perpendicular to the centre-to-end point ray, in the direction of the bulge
524
+ lastSegAngle = centrePoint.angle(this.points.at(-1)) + (Math.PI / 2) * Math.sign(lastSegBulge);
777
525
  } else {
778
526
  // line segment angle is the angle from point 0 to point 1
779
527
  lastSegAngle = this.points.at(-2).angle(this.points.at(-1));
@@ -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');