@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
@@ -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 centre 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 centre = data.points?.[0];
77
+ if (!centre) 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
+ centre,
86
+ centre.project(Utils.degrees2radians(data[50] ?? 0), radius),
87
+ centre.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,29 +101,46 @@ 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 centre 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;
103
123
 
104
124
  if (Input.getType(pt2) === Input.Type.POINT) {
105
- // Must not be same as center or start
106
- const center = this.points[0];
125
+ // Must not be same as centre or start
126
+ const centre = this.points[0];
107
127
  const start = this.points[1];
108
- if (pt2.isSame?.(center) || pt2.isSame?.(start)) {
128
+ if (pt2.isSame?.(centre) || pt2.isSame?.(start)) {
129
+ DesignCore.Core.notify(`${this.type} - ${Strings.Error.INVALIDPOINT}`);
130
+ continue;
131
+ }
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])) {
109
136
  DesignCore.Core.notify(`${this.type} - ${Strings.Error.INVALIDPOINT}`);
110
137
  continue;
111
138
  }
112
- this.points.push(pt2);
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 centre = this.points[0];
197
+ const endPoint = centre.project(centre.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,28 +204,28 @@ 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
 
180
211
  /**
181
212
  * Infer arc direction from three points using cross product
182
- * @param {Point} center
213
+ * @param {Point} centre
183
214
  * @param {Point} start
184
215
  * @param {Point} end
185
216
  * @return {number} 1 for ccw, -1 for cw
186
217
  */
187
- static #inferDirection(center, start, end) {
218
+ static #inferDirection(centre, start, end) {
188
219
  // All points must be defined and distinct
189
- if (!center || !start || !end) {
220
+ if (!centre || !start || !end) {
190
221
  return 1; // Default to counterclockwise
191
222
  }
192
- if (center.isSame?.(start) || center.isSame?.(end) || start.isSame?.(end)) {
223
+ if (centre.isSame?.(start) || centre.isSame?.(end) || start.isSame?.(end)) {
193
224
  return 1;
194
225
  }
195
226
 
196
- const v1 = start.subtract(center);
197
- const v2 = end.subtract(center);
227
+ const v1 = start.subtract(centre);
228
+ const v2 = end.subtract(centre);
198
229
  const cross = v1.cross(v2);
199
230
  const direction = cross > 0 ? 1 : -1;
200
231
  return direction;
@@ -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
- 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;
349
+ const centre = points[0].bulgeCentrePoint(points[1]);
350
+ this.points = [centre, 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
  }
@@ -448,9 +415,9 @@ export class Arc extends Entity {
448
415
  closestPoint(P) {
449
416
  const startPoint = this.points[1];
450
417
  const endPoint = this.points[2];
451
- const centerPoint = this.points[0];
418
+ const centrePoint = this.points[0];
452
419
 
453
- const pnt = P.closestPointOnArc(startPoint, endPoint, centerPoint, this.direction);
420
+ const pnt = P.closestPointOnArc(startPoint, endPoint, centrePoint, 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
  }