@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
@@ -7,6 +7,8 @@ import { Point } from '../entities/point.js';
7
7
  import { BaseLinearDimension } from './baseLinearDimension.js';
8
8
 
9
9
  import { DesignCore } from '../designCore.js';
10
+ import { Property } from '../properties/property.js';
11
+ import { RubberBand } from '../lib/auxiliary/rubberBand.js';
10
12
 
11
13
 
12
14
  /**
@@ -42,12 +44,12 @@ export class RotatedDimension extends BaseLinearDimension {
42
44
  async execute() {
43
45
  try {
44
46
  DesignCore.Scene.selectionManager.reset();
45
- this.dimensionStyle = DesignCore.DimStyleManager.getCstyle();
47
+ this.setProperty(Property.Names.DIMENSIONSTYLE, DesignCore.DimStyleManager.getCstyle());
46
48
  this.dimType.setDimType(0); // Rotated dimension
47
49
 
48
50
  const opAngle = new PromptOptions(Strings.Input.ROTATION, [Input.Type.NUMBER]);
49
51
  const angleValue = await DesignCore.Scene.inputManager.requestInput(opAngle);
50
- this.linearDimAngle = Number(angleValue) || 0;
52
+ this.setProperty(Property.Names.LINEARDIMANGLE, Number(angleValue) || 0);
51
53
 
52
54
  const op = new PromptOptions(Strings.Input.START, [Input.Type.POINT]);
53
55
  const pt13 = await DesignCore.Scene.inputManager.requestInput(op);
@@ -65,7 +67,7 @@ export class RotatedDimension extends BaseLinearDimension {
65
67
  this.points.push(pt11);
66
68
 
67
69
  const tempLine = new Line({ points: [pt13, pt14] });
68
- this.points = RotatedDimension.getPointsFromSelection([tempLine], pt11, this.linearDimAngle);
70
+ this.points = RotatedDimension.getPointsFromSelection([tempLine], pt11, this.getProperty(Property.Names.LINEARDIMANGLE));
69
71
 
70
72
  DesignCore.Scene.inputManager.executeCommand(this);
71
73
  } catch (err) {
@@ -117,16 +119,15 @@ export class RotatedDimension extends BaseLinearDimension {
117
119
  preview() {
118
120
  if (this.points.length === 1) {
119
121
  const mousePoint = DesignCore.Mouse.pointOnScene();
120
- const points = [this.points.at(0), mousePoint];
121
- DesignCore.Scene.tempEntities.create('Line', { points: points });
122
+ DesignCore.Scene.auxiliaryEntities.add(new RubberBand([this.points.at(0), mousePoint]));
122
123
  }
123
124
 
124
125
  if (this.points.length > 1) {
125
126
  const pt11 = DesignCore.Mouse.pointOnScene();
126
127
  pt11.sequence = 11;
127
128
  const tempLine = new Line({ points: [this.points[0], this.points[1]] });
128
- const points = RotatedDimension.getPointsFromSelection([tempLine], pt11, this.linearDimAngle);
129
- DesignCore.Scene.tempEntities.create(this.type, { points: points, dimensionStyle: this.dimensionStyle });
129
+ const points = RotatedDimension.getPointsFromSelection([tempLine], pt11, this.getProperty(Property.Names.LINEARDIMANGLE));
130
+ DesignCore.Scene.previewEntities.create(this.type, { points: points, dimensionStyle: this.getProperty(Property.Names.DIMENSIONSTYLE) });
130
131
  }
131
132
  }
132
133
 
@@ -141,10 +142,10 @@ export class RotatedDimension extends BaseLinearDimension {
141
142
  const Pt14 = this.getPointBySequence(this.points, 14);
142
143
 
143
144
  file.writeGroupCode('0', 'DIMENSION');
144
- file.writeGroupCode('5', this.handle, DXFFile.Version.R2000); // Handle
145
+ file.writeGroupCode('5', this.getProperty(Property.Names.HANDLE), DXFFile.Version.R2000); // Handle
145
146
  file.writeGroupCode('100', 'AcDbEntity', DXFFile.Version.R2000);
146
147
  file.writeGroupCode('100', 'AcDbDimension', DXFFile.Version.R2000);
147
- file.writeGroupCode('8', this.layer);
148
+ file.writeGroupCode('8', this.getProperty(Property.Names.LAYER));
148
149
  // file.writeGroupCode('2', this.blockName); // Block not required
149
150
  file.writeGroupCode('10', Pt10.x); // X - definition / arrow point
150
151
  file.writeGroupCode('20', Pt10.y); // Y
@@ -153,7 +154,7 @@ export class RotatedDimension extends BaseLinearDimension {
153
154
  file.writeGroupCode('21', Pt11.y); // Y
154
155
  file.writeGroupCode('31', '0.0'); // Z
155
156
  file.writeGroupCode('70', this.dimType.getDimType()); // DIMENSION TYPE
156
- file.writeGroupCode('3', this.dimensionStyle); // DIMENSION STYLE
157
+ file.writeGroupCode('3', this.getProperty(Property.Names.DIMENSIONSTYLE)); // DIMENSION STYLE
157
158
  file.writeGroupCode('100', 'AcDbAlignedDimension', DXFFile.Version.R2000);
158
159
  file.writeGroupCode('13', Pt13.x); // X - start point of first extension line
159
160
  file.writeGroupCode('23', Pt13.y); // Y
@@ -161,7 +162,7 @@ export class RotatedDimension extends BaseLinearDimension {
161
162
  file.writeGroupCode('14', Pt14.x); // X - start point of second extension line
162
163
  file.writeGroupCode('24', Pt14.y); // Y
163
164
  file.writeGroupCode('34', '0.0'); // Z
164
- file.writeGroupCode('50', this.linearDimAngle); // Utils.radians2degrees(Pt13.angle(Pt14))); // Rotation angle
165
+ file.writeGroupCode('50', this.getProperty(Property.Names.LINEARDIMANGLE)); // Utils.radians2degrees(Pt13.angle(Pt14))); // Rotation angle
165
166
  file.writeGroupCode('100', 'AcDbRotatedDimension', DXFFile.Version.R2000);
166
167
  }
167
168
  }
@@ -6,11 +6,12 @@ import { Input, PromptOptions } from '../lib/inputManager.js';
6
6
  import { Logging } from '../lib/logging.js';
7
7
  import { DXFFile } from '../lib/dxf/dxfFile.js';
8
8
  import { BoundingBox } from '../lib/boundingBox.js';
9
- import { AddState, RemoveState } from '../lib/stateManager.js';
9
+
10
10
 
11
11
  import { DesignCore } from '../designCore.js';
12
12
  import { Property } from '../properties/property.js';
13
- import { SnapPoint } from '../lib/snapping.js';
13
+ import { SnapPoint } from '../lib/auxiliary/snapPoint.js';
14
+ import { RubberBand } from '../lib/auxiliary/rubberBand.js';
14
15
 
15
16
  /**
16
17
  * Arc Entity Class
@@ -25,46 +26,32 @@ export class Arc extends Entity {
25
26
  */
26
27
  constructor(data) {
27
28
  super(data);
28
- this.radius = 1;
29
-
30
- // direction: - ccw > 0, cw <= 0
31
- // default to 1 - counter clockwise
32
- Object.defineProperty(this, 'direction', {
33
- value: 1,
34
- writable: true,
35
- });
36
29
 
37
- if (data) {
38
- if (data.hasOwnProperty('points') || data.hasOwnProperty('40')) {
39
- // DXF Groupcode 40 - Radius
40
- // get the radius from the points or the incoming dxf groupcode
41
- const radius = this.points[1] ? this.points[0].distance(this.points[1]) : data[40];
42
-
43
- if (radius !== undefined) {
44
- this.radius = radius;
45
- }
30
+ // If only the center point is present, project start/end from named scalar properties.
31
+ // Full-points data (post-execute or post-fromDxf) skips this block entirely.
32
+ if (data && this.points.length < 2) {
33
+ const radius = data.radius ?? 1;
34
+ if (data.hasOwnProperty('startAngle')) {
35
+ this.points[1] = this.points[0].project(Utils.degrees2radians(data.startAngle), radius);
46
36
  }
47
-
48
- if (data.hasOwnProperty('startAngle') || data.hasOwnProperty('50')) {
49
- // DXF Groupcode 50 - Start Angle
50
-
51
- const angle = Property.loadValue([data.startAngle, data[50]], 0);
52
- const projectionAngle = Utils.degrees2radians(angle);
53
- this.points[1] = this.points[0].project(projectionAngle, this.radius);
37
+ if (data.hasOwnProperty('endAngle')) {
38
+ this.points[2] = this.points[0].project(Utils.degrees2radians(data.endAngle), radius);
54
39
  }
40
+ }
55
41
 
56
- if (data.hasOwnProperty('endAngle') || data.hasOwnProperty('51')) {
57
- // DXF Groupcode 51 - End Angle
58
- const angle = Property.loadValue([data.endAngle, data[51]], 0);
59
- const projectionAngle = Utils.degrees2radians(angle);
60
- this.points[2] = this.points[0].project(projectionAngle, this.radius);
61
- }
42
+ // direction: ccw > 0, cw <= 0
43
+ this.properties.add(Property.Names.DIRECTION, {
44
+ type: Property.Type.NUMBER,
45
+ value: data?.direction ?? 1,
46
+ dxfCode: 73,
47
+ });
62
48
 
63
- if (data.hasOwnProperty('direction') || data.hasOwnProperty('73')) {
64
- // No DXF Groupcode - Arc Direction
65
- this.direction = Property.loadValue([data.direction, data[73]], 1);
66
- }
67
- }
49
+ // radius: computed from points — stored in EntityProperties
50
+ this.properties.add(Property.Names.RADIUS, {
51
+ type: Property.Type.NUMBER,
52
+ get: (entity) => entity.points[1] ? entity.points[0].distance(entity.points[1]) : 1,
53
+ dxfCode: 40,
54
+ });
68
55
  }
69
56
 
70
57
  /**
@@ -79,6 +66,30 @@ export class Arc extends Entity {
79
66
  return command;
80
67
  }
81
68
 
69
+ /**
70
+ * Normalise DXF group codes into the canonical points-based representation.
71
+ * Called by the DXF loader before construction.
72
+ * @param {Object} data
73
+ * @return {Object}
74
+ */
75
+ static fromDxf(data) {
76
+ const center = data.points?.[0];
77
+ if (!center) return data;
78
+ // validate required group codes for points-based arc representation are present
79
+ if (data[40] === undefined || data[50] === undefined || data[51] === undefined) return data;
80
+
81
+ const radius = data[40];
82
+ return {
83
+ ...data,
84
+ points: [
85
+ center,
86
+ center.project(Utils.degrees2radians(data[50] ?? 0), radius),
87
+ center.project(Utils.degrees2radians(data[51] ?? 0), radius),
88
+ ],
89
+ direction: data[73] ?? 1,
90
+ };
91
+ }
92
+
82
93
  /**
83
94
  * Execute method
84
95
  * executes the workflow, requesting input required to create an entity
@@ -90,13 +101,22 @@ export class Arc extends Entity {
90
101
  if (pt === undefined) return;
91
102
  this.points.push(pt);
92
103
 
93
- const op1 = new PromptOptions(Strings.Input.START, [Input.Type.POINT]);
94
- const pt1 = await DesignCore.Scene.inputManager.requestInput(op1);
95
- if (pt1 === undefined) return;
104
+ let pt1;
105
+ while (true) {
106
+ const op1 = new PromptOptions(Strings.Input.START, [Input.Type.POINT]);
107
+ pt1 = await DesignCore.Scene.inputManager.requestInput(op1);
108
+ if (pt1 === undefined) return;
109
+ if (pt1.isSame?.(pt)) {
110
+ DesignCore.Core.notify(`${this.type} - ${Strings.Error.INVALIDPOINT}`);
111
+ continue;
112
+ }
113
+ break;
114
+ }
96
115
  this.points.push(pt1);
97
116
 
98
117
  let pt2;
99
118
  while (true) {
119
+ DesignCore.Scene.inputManager.inputPoint = pt; // polar/ortho tracks from center on each attempt
100
120
  const op2 = new PromptOptions(`${Strings.Input.END} ${Strings.Strings.OR} ${Strings.Input.ANGLE}`, [Input.Type.POINT, Input.Type.NUMBER]);
101
121
  pt2 = await DesignCore.Scene.inputManager.requestInput(op2);
102
122
  if (pt2 === undefined) return;
@@ -109,10 +129,18 @@ export class Arc extends Entity {
109
129
  DesignCore.Core.notify(`${this.type} - ${Strings.Error.INVALIDPOINT}`);
110
130
  continue;
111
131
  }
112
- this.points.push(pt2);
132
+
133
+ const angle = this.points[0].angle(pt2);
134
+ const projectedEnd = this.points[0].project(angle, this.getProperty(Property.Names.RADIUS));
135
+ if (projectedEnd.isSame(this.points[1])) {
136
+ DesignCore.Core.notify(`${this.type} - ${Strings.Error.INVALIDPOINT}`);
137
+ continue;
138
+ }
139
+ this.points.push(projectedEnd);
140
+
113
141
  // Infer arc direction using cross product if we have 3 points
114
142
  if (this.points.length === 3) {
115
- this.direction = Arc.#inferDirection(this.points[0], this.points[1], this.points[2]);
143
+ this.setProperty(Property.Names.DIRECTION, Arc.#inferDirection(this.points[0], this.points[1], this.points[2]));
116
144
  // Check total angle > 1 degree
117
145
  if (Math.abs(this.totalAngle) <= 1) {
118
146
  this.points.pop(); // Remove invalid end point
@@ -134,7 +162,7 @@ export class Arc extends Entity {
134
162
  const point = startPoint.rotate(basePoint, angle);
135
163
  this.points.push(point);
136
164
  // Set direction based on sign of angle
137
- this.direction = angle >= 0 ? 1 : -1;
165
+ this.setProperty(Property.Names.DIRECTION, angle >= 0 ? 1 : -1);
138
166
  // Check total angle > 1 degree
139
167
  if (Math.abs(this.totalAngle) <= 1) {
140
168
  this.points.pop(); // Remove invalid end point
@@ -159,13 +187,16 @@ export class Arc extends Entity {
159
187
  preview() {
160
188
  if (this.points.length >= 1) {
161
189
  const mousePoint = DesignCore.Mouse.pointOnScene();
162
- const points = [this.points.at(0), mousePoint];
163
- DesignCore.Scene.tempEntities.create('Line', { points: points });
190
+ DesignCore.Scene.auxiliaryEntities.add(new RubberBand([this.points.at(0), mousePoint]));
164
191
  }
165
192
 
166
193
  if (this.points.length >= 2) {
167
194
  const mousePoint = DesignCore.Mouse.pointOnScene();
168
- const points = [...this.points, mousePoint];
195
+ // const points = [...this.points, mousePoint];
196
+ const center = this.points[0];
197
+ const endPoint = center.project(center.angle(mousePoint), this.getProperty(Property.Names.RADIUS));
198
+ if (endPoint.isSame(this.points[1])) return;
199
+ const points = [...this.points, endPoint];
169
200
 
170
201
  // Infer direction for preview, so it matches what will be created
171
202
  let direction = 1;
@@ -173,7 +204,7 @@ export class Arc extends Entity {
173
204
  direction = Arc.#inferDirection(points[0], points[1], points[2]);
174
205
  }
175
206
 
176
- DesignCore.Scene.tempEntities.create(this.type, { points: points, direction: direction });
207
+ DesignCore.Scene.previewEntities.create(this.type, { points: points, direction: direction });
177
208
  }
178
209
  }
179
210
 
@@ -228,7 +259,7 @@ export class Arc extends Entity {
228
259
  let endAngle = this.endAngle();
229
260
 
230
261
 
231
- if (this.direction > 0) {
262
+ if (this.getProperty(Property.Names.DIRECTION) > 0) {
232
263
  if (startAngle > endAngle || startAngle === endAngle) {
233
264
  endAngle += (Math.PI * 2);
234
265
  }
@@ -245,7 +276,7 @@ export class Arc extends Entity {
245
276
  * @return {number} - arc radius
246
277
  */
247
278
  getRadius() {
248
- return this.radius;
279
+ return this.getProperty(Property.Names.RADIUS);
249
280
  }
250
281
 
251
282
  /**
@@ -253,9 +284,12 @@ export class Arc extends Entity {
253
284
  * @param {Object} ctx - context
254
285
  * @param {number} scale
255
286
  */
256
- draw(ctx, scale) {
257
- ctx.arc(this.points[0].x, this.points[0].y, this.radius, this.direction > 0 ? this.startAngle() : this.endAngle(), this.direction > 0 ? this.endAngle() : this.startAngle());
258
- ctx.stroke();
287
+ /**
288
+ * Draw the arc
289
+ * @param {Object} renderer
290
+ */
291
+ draw(renderer) {
292
+ renderer.drawShape(this.toPolylinePoints());
259
293
  }
260
294
 
261
295
  /**
@@ -264,19 +298,19 @@ export class Arc extends Entity {
264
298
  */
265
299
  dxf(file) {
266
300
  file.writeGroupCode('0', 'ARC');
267
- file.writeGroupCode('5', this.handle, DXFFile.Version.R2000); // Handle
301
+ file.writeGroupCode('5', this.getProperty(Property.Names.HANDLE), DXFFile.Version.R2000); // Handle
268
302
  file.writeGroupCode('100', 'AcDbEntity', DXFFile.Version.R2000);
269
303
  file.writeGroupCode('100', 'AcDbCircle', DXFFile.Version.R2000);
270
- file.writeGroupCode('8', this.layer); // LAYERNAME
271
- file.writeGroupCode('6', this.lineType); // LINETYPE
304
+ file.writeGroupCode('8', this.getProperty(Property.Names.LAYER)); // LAYERNAME
305
+ file.writeGroupCode('6', this.getProperty(Property.Names.LINETYPE)); // LINETYPE
272
306
  file.writeGroupCode('10', this.points[0].x); // X
273
307
  file.writeGroupCode('20', this.points[0].y); // Y
274
308
  file.writeGroupCode('30', '0.0'); // Z
275
- file.writeGroupCode('39', this.lineWidth); // Line Width
276
- file.writeGroupCode('40', this.radius); // Radius
309
+ file.writeGroupCode('39', this.getProperty(Property.Names.LINEWIDTH)); // Line Width
310
+ file.writeGroupCode('40', this.getProperty(Property.Names.RADIUS)); // Radius
277
311
  file.writeGroupCode('100', 'AcDbArc', DXFFile.Version.R2000);
278
- file.writeGroupCode('50', Utils.radians2degrees(this.direction > 0 ? this.startAngle() : this.endAngle())); // Start Angle
279
- file.writeGroupCode('51', Utils.radians2degrees(this.direction > 0 ? this.endAngle() : this.startAngle())); // End Angle
312
+ file.writeGroupCode('50', Utils.radians2degrees(this.getProperty(Property.Names.DIRECTION) > 0 ? this.startAngle() : this.endAngle())); // Start Angle
313
+ file.writeGroupCode('51', Utils.radians2degrees(this.getProperty(Property.Names.DIRECTION) > 0 ? this.endAngle() : this.startAngle())); // End Angle
280
314
  }
281
315
 
282
316
  /**
@@ -289,101 +323,32 @@ export class Arc extends Entity {
289
323
 
290
324
  // If the arc forms a complete circle
291
325
  // Split into two separate polyline arcs
326
+ const radius = this.getProperty(Property.Names.RADIUS);
292
327
  if (Math.abs(this.totalAngle) === 360) {
293
- const startPoint = this.points[0].project(0, this.radius);
328
+ const startPoint = this.points[0].project(0, radius);
294
329
  startPoint.bulge = 1;
295
- const endPoint = this.points[0].project(0, -this.radius);
330
+ const endPoint = this.points[0].project(0, -radius);
296
331
  endPoint.bulge = 1;
297
- const closurePoint = this.points[0].project(0, this.radius);
332
+ const closurePoint = this.points[0].project(0, radius);
298
333
  return [startPoint, endPoint, closurePoint];
299
334
  }
300
335
 
301
- const startPoint = this.points[0].project(this.startAngle(), this.radius);
336
+ const startPoint = this.points[0].project(this.startAngle(), radius);
302
337
  const bulge = Math.tan(Utils.degrees2radians(-this.totalAngle % 360) / 4);
303
338
  startPoint.bulge = bulge;
304
- const endPoint = this.points[0].project(this.endAngle(), this.radius);
339
+ const endPoint = this.points[0].project(this.endAngle(), radius);
305
340
  return [startPoint, endPoint];
306
341
  }
307
342
 
308
343
  /**
309
344
  * Set entity points from a polyline point representation
310
345
  * @param {Array} points
346
+ * @return {Arc}
311
347
  */
312
348
  fromPolylinePoints(points) {
313
349
  const center = points[0].bulgeCentrePoint(points[1]);
314
- const radius = center.distance(points[0]);
315
- this.points = [center, new Point(points[0].x, points[0].y), new Point(points[1].x, points[1].y)];
316
- this.radius = radius;
317
- }
318
-
319
- /**
320
- * Trim the entity
321
- * @param {Array} intersections
322
- * @return {Array} - array of state changes
323
- */
324
- trim(intersections) {
325
- // array to hold state changes
326
- const stateChanges = [];
327
-
328
- if (intersections?.length === 0 || !intersections) {
329
- return stateChanges;
330
- }
331
-
332
- // get the mouse position
333
- const mousePosition = DesignCore.Mouse.pointOnScene();
334
- // get the point on the arc closest to the mouse
335
- const pointOnArc = mousePosition.closestPointOnArc(this.points[1], this.points[2], this.points[0], this.direction);
336
- // remove any intersections that are at the end points of the arc
337
- intersections = intersections.filter((p) => !p.isSame(this.points[1]) && !p.isSame(this.points[2]));
338
- // sort intersection points by distance to start point
339
- Utils.sortPointsOnArc(intersections, this.points[1], this.points[2], this.points[0], this.direction);
340
- // Add arc points to the start and the end of the point array
341
- const testPoints = [this.points[1], ...intersections, this.points[2]];
342
-
343
- // Test if mouse position is between two intersection points
344
- if (testPoints.length > 1) {
345
- for (let i = 0; i < testPoints.length - 1; i++) {
346
- const startPoint = testPoints[i];
347
- const endPoint = testPoints[i + 1];
348
-
349
- // check if the mouse is between startPoint and endPoint
350
- if (pointOnArc.isOnArc(startPoint, endPoint, this.points[0], this.direction)) {
351
- // Store new entity points
352
- const newPoints = [];
353
-
354
- for (const p of testPoints) {
355
- const inOriginalArc = this.points.indexOf(p) !== -1;
356
- const inIntersections = intersections.indexOf(p) !== -1;
357
-
358
- // Keep points which:
359
- // - The mouse is between are intersections
360
- // - The mouse is not between and in this arc
361
- const isBetween = p.isSame(startPoint) || p.isSame(endPoint);
362
- if ((isBetween && inIntersections) || (!isBetween && inOriginalArc)) {
363
- newPoints.push(p);
364
- }
365
- }
366
-
367
- if (newPoints.length % 2 === 0) {
368
- // Add arcs for each point pair
369
- for (let j = 0; j < newPoints.length; j += 2) {
370
- const arc = Utils.cloneObject(this);
371
- arc.points = [this.points[0], newPoints[j], newPoints[j + 1]];
372
- const addState = new AddState(arc);
373
- stateChanges.push(addState);
374
- }
375
- }
376
-
377
- if (stateChanges.length > 0) {
378
- // Remove the existing arc
379
- const removeState = new RemoveState(this);
380
- stateChanges.push(removeState);
381
- }
382
- }
383
- }
384
- }
385
-
386
- return stateChanges;
350
+ this.points = [center, new Point(points[0].x, points[0].y), new Point(points.at(-1).x, points.at(-1).y)];
351
+ return this;
387
352
  }
388
353
 
389
354
  /**
@@ -417,12 +382,14 @@ export class Arc extends Entity {
417
382
  const angleFromCentreToInput = centre.angle(fromPoint); // angle from centre toward fromPoint
418
383
 
419
384
  // Tangent: only possible when fromPoint is outside the arc radius
420
- if (distanceToCenter > this.radius) {
421
- const tangentHalfAngle = Math.acos(this.radius / distanceToCenter); // half-angle between the two tangent directions
385
+ const radius = this.getProperty(Property.Names.RADIUS);
386
+ const direction = this.getProperty(Property.Names.DIRECTION);
387
+ if (distanceToCenter > radius) {
388
+ const tangentHalfAngle = Math.acos(radius / distanceToCenter); // half-angle between the two tangent directions
422
389
 
423
390
  for (const sign of [1, -1]) {
424
- const tangentPoint = centre.project(angleFromCentreToInput + sign * tangentHalfAngle, this.radius);
425
- if (tangentPoint.isOnArc(this.points[1], this.points[2], centre, this.direction)) {
391
+ const tangentPoint = centre.project(angleFromCentreToInput + sign * tangentHalfAngle, radius);
392
+ if (tangentPoint.isOnArc(this.points[1], this.points[2], centre, direction)) {
426
393
  snaps.push(new SnapPoint(tangentPoint, SnapPoint.Type.TANGENT));
427
394
  }
428
395
  }
@@ -430,8 +397,8 @@ export class Arc extends Entity {
430
397
 
431
398
  // Perpendicular: point on the arc that lies on the line from fromPoint through the centre
432
399
  if (distanceToCenter > 0) {
433
- const perpendicularPoint = centre.project(angleFromCentreToInput, this.radius);
434
- if (perpendicularPoint.isOnArc(this.points[1], this.points[2], centre, this.direction)) {
400
+ const perpendicularPoint = centre.project(angleFromCentreToInput, radius);
401
+ if (perpendicularPoint.isOnArc(this.points[1], this.points[2], centre, direction)) {
435
402
  snaps.push(new SnapPoint(perpendicularPoint, SnapPoint.Type.PERPENDICULAR));
436
403
  }
437
404
  }
@@ -450,7 +417,7 @@ export class Arc extends Entity {
450
417
  const endPoint = this.points[2];
451
418
  const centerPoint = this.points[0];
452
419
 
453
- const pnt = P.closestPointOnArc(startPoint, endPoint, centerPoint, this.direction);
420
+ const pnt = P.closestPointOnArc(startPoint, endPoint, centerPoint, this.getProperty(Property.Names.DIRECTION));
454
421
 
455
422
  if (pnt !== null) {
456
423
  const distance = P.distance(pnt);
@@ -466,6 +433,6 @@ export class Arc extends Entity {
466
433
  * @return {BoundingBox}
467
434
  */
468
435
  boundingBox() {
469
- return BoundingBox.arcBoundingBox(this.points[0], this.points[1], this.points[2], this.direction);
436
+ return BoundingBox.arcBoundingBox(this.points[0], this.points[1], this.points[2], this.getProperty(Property.Names.DIRECTION));
470
437
  }
471
438
  }