@design-core/core 0.0.1 → 0.0.3

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 (254) hide show
  1. package/.eslintrc.json +14 -7
  2. package/.github/workflows/DesignCoreQA.yml +3 -1
  3. package/README.md +11 -4
  4. package/TODO.md +19 -0
  5. package/core/core/core.js +49 -42
  6. package/core/designCore.js +61 -2
  7. package/core/dimensions/alignedDimension.js +142 -0
  8. package/core/dimensions/angularDimension.js +550 -0
  9. package/core/dimensions/baseCircularDimension.js +244 -0
  10. package/core/dimensions/baseDimension.js +558 -0
  11. package/core/dimensions/baseLinearDimension.js +371 -0
  12. package/core/dimensions/diametricDimension.js +82 -0
  13. package/core/dimensions/dimension.js +298 -0
  14. package/core/dimensions/linearDimension.js +29 -0
  15. package/core/dimensions/radialDimension.js +78 -0
  16. package/core/dimensions/rotatedDimension.js +167 -0
  17. package/core/entities/arc.js +314 -60
  18. package/core/entities/arctext.js +642 -0
  19. package/core/entities/basePolyline.js +553 -94
  20. package/core/entities/circle.js +181 -54
  21. package/core/entities/entity.js +154 -74
  22. package/core/entities/hatch.js +807 -0
  23. package/core/entities/insert.js +200 -39
  24. package/core/entities/line.js +196 -134
  25. package/core/entities/lwpolyline.js +21 -44
  26. package/core/entities/point.js +194 -29
  27. package/core/entities/polyline.js +54 -7
  28. package/core/entities/rectangle.js +43 -13
  29. package/core/entities/solid.js +43 -6
  30. package/core/entities/text.js +324 -181
  31. package/core/lib/boundingBox.js +73 -13
  32. package/core/lib/canvas.js +256 -65
  33. package/core/lib/clipboard.js +198 -0
  34. package/core/lib/colour.js +144 -0
  35. package/core/lib/colours.js +367 -301
  36. package/core/lib/commandLine.js +16 -28
  37. package/core/lib/commandManager.js +115 -58
  38. package/core/lib/constants.js +23 -0
  39. package/core/lib/dxf/dxf.js +249 -34
  40. package/core/lib/dxf/dxfFile.js +23 -38
  41. package/core/lib/dxf/dxfIterator.js +17 -5
  42. package/core/lib/dxf/dxfRead.js +23 -7
  43. package/core/lib/dxf/dxfWrite.js +39 -129
  44. package/core/lib/dxf/handleManager.js +97 -0
  45. package/core/lib/dxf/sections/blocks.js +16 -2
  46. package/core/lib/dxf/sections/entities.js +17 -3
  47. package/core/lib/dxf/sections/header.js +10 -1
  48. package/core/lib/dxf/sections/objects.js +36 -0
  49. package/core/lib/dxf/sections/section.js +43 -5
  50. package/core/lib/dxf/sections/tables.js +17 -3
  51. package/core/lib/entityManager.js +178 -0
  52. package/core/lib/fileio.js +14 -4
  53. package/core/lib/headers.js +229 -0
  54. package/core/lib/input.js +46 -0
  55. package/core/lib/inputManager.js +199 -194
  56. package/core/lib/intersect.js +341 -468
  57. package/core/lib/logging.js +25 -1
  58. package/core/lib/matrix.js +6 -8
  59. package/core/lib/mouse.js +24 -79
  60. package/core/lib/patterns.js +690 -0
  61. package/core/lib/promptOptions.js +133 -0
  62. package/core/lib/scene.js +71 -174
  63. package/core/lib/selectionManager.js +74 -48
  64. package/core/lib/selectionWindow.js +24 -10
  65. package/core/lib/settings.js +18 -11
  66. package/core/lib/snapping.js +129 -21
  67. package/core/lib/stateManager.js +288 -0
  68. package/core/lib/strings.js +57 -5
  69. package/core/lib/utils.js +95 -11
  70. package/core/objects/dictionary.js +43 -0
  71. package/core/objects/dictionaryManager.js +80 -0
  72. package/core/properties/dimType.js +117 -0
  73. package/core/properties/flags.js +66 -0
  74. package/core/properties/property.js +26 -0
  75. package/core/properties/propertyManager.js +65 -19
  76. package/core/tables/appId.js +31 -0
  77. package/core/tables/appIdManager.js +44 -0
  78. package/core/tables/block.js +323 -0
  79. package/core/tables/blockManager.js +48 -0
  80. package/core/tables/blockRecord.js +29 -0
  81. package/core/tables/blockRecordManager.js +37 -0
  82. package/core/tables/dimStyle.js +627 -0
  83. package/core/tables/dimStyleManager.js +187 -0
  84. package/core/tables/endblk.js +28 -0
  85. package/core/tables/layer.js +219 -0
  86. package/core/tables/layerManager.js +47 -0
  87. package/core/tables/ltype.js +85 -0
  88. package/core/tables/ltypeManager.js +65 -0
  89. package/core/tables/style.js +145 -0
  90. package/core/tables/styleManager.js +55 -0
  91. package/core/tables/styleManagerBase.js +110 -0
  92. package/core/tables/tableManagerBase.js +354 -0
  93. package/core/tables/ucs.js +28 -0
  94. package/core/tables/ucsManager.js +44 -0
  95. package/core/tables/view.js +28 -0
  96. package/core/tables/viewManager.js +44 -0
  97. package/core/tables/vport.js +70 -0
  98. package/core/tables/vportManager.js +65 -0
  99. package/core/tools/chamfer.js +343 -0
  100. package/core/tools/chamferFilletBase.js +162 -0
  101. package/core/tools/copy.js +68 -40
  102. package/core/tools/copybase.js +65 -0
  103. package/core/tools/copyclip.js +59 -0
  104. package/core/tools/cornerEntity.js +138 -0
  105. package/core/tools/cutclip.js +72 -0
  106. package/core/tools/distance.js +37 -9
  107. package/core/tools/erase.js +43 -13
  108. package/core/tools/explode.js +117 -0
  109. package/core/tools/extend.js +71 -32
  110. package/core/tools/fillet.js +336 -0
  111. package/core/tools/identify.js +33 -6
  112. package/core/tools/matchProp.js +106 -0
  113. package/core/tools/mirror.js +141 -0
  114. package/core/tools/move.js +65 -32
  115. package/core/tools/offset.js +321 -0
  116. package/core/tools/pan.js +54 -0
  117. package/core/tools/pasteclip.js +93 -0
  118. package/core/tools/purge.js +90 -0
  119. package/core/tools/rotate.js +105 -70
  120. package/core/tools/scale.js +166 -0
  121. package/core/tools/tool.js +14 -4
  122. package/core/tools/trim.js +71 -32
  123. package/core/tools/zoom.js +152 -0
  124. package/package.json +3 -305
  125. package/test/dimensions/alignedDimension.test.js +211 -0
  126. package/test/dimensions/angularDimension.test.js +243 -0
  127. package/test/dimensions/baseDimension.test.js +239 -0
  128. package/test/dimensions/diametricDimension.test.js +203 -0
  129. package/test/dimensions/dimension.test.js +102 -0
  130. package/test/dimensions/radialDimension.test.js +203 -0
  131. package/test/dimensions/rotatedDimension.test.js +224 -0
  132. package/test/entities/arc.test.js +463 -41
  133. package/test/entities/arctext.test.js +329 -0
  134. package/test/entities/basePolyline.test.js +459 -10
  135. package/test/entities/circle.test.js +312 -7
  136. package/test/entities/entity.test.js +28 -9
  137. package/test/entities/hatch.test.js +606 -0
  138. package/test/entities/insert.test.js +90 -0
  139. package/test/entities/line.test.js +182 -6
  140. package/test/entities/point.test.js +375 -37
  141. package/test/entities/solid.test.js +156 -0
  142. package/test/entities/text.test.js +533 -7
  143. package/test/integration/exportIntegration.reference.dxf +1706 -0
  144. package/test/integration/exportIntegration.test.js +186 -0
  145. package/test/integration/importExportIntegration.test.js +126 -0
  146. package/test/integration/intersectionIntegration.test.js +880 -0
  147. package/test/integration/userInputIntegration.reference.dxf +1410 -0
  148. package/test/integration/userInputIntegration.test.js +269 -0
  149. package/test/lib/boundingBox.test.js +26 -2
  150. package/test/lib/canvas.test.js +613 -5
  151. package/test/lib/clipboard.test.js +70 -0
  152. package/test/lib/colour.test.js +114 -0
  153. package/test/lib/colours.test.js +63 -18
  154. package/test/lib/commandLine.test.js +159 -22
  155. package/test/lib/commandManager.test.js +4 -4
  156. package/test/lib/constants.test.js +9 -0
  157. package/test/lib/dxf/dxfFile.test.js +6 -2
  158. package/test/lib/dxf/dxfIterator.test.js +1 -1
  159. package/test/lib/dxf/dxfWrite.test.js +173 -0
  160. package/test/lib/dxf/handleManager.test.js +137 -0
  161. package/test/lib/dxf/sections/blocks.test.js +5 -5
  162. package/test/lib/dxf/sections/entities.test.js +4 -4
  163. package/test/lib/dxf/sections/header.test.js +18 -4
  164. package/test/lib/dxf/sections/objects.test.js +132 -0
  165. package/test/lib/dxf/sections/section.test.js +29 -29
  166. package/test/lib/dxf/sections/tables.test.js +3 -3
  167. package/test/lib/entityManager.test.js +163 -0
  168. package/test/lib/headers.test.js +233 -0
  169. package/test/lib/inputManager.test.js +1144 -5
  170. package/test/lib/intersect.test.js +454 -331
  171. package/test/lib/logging.test.js +1 -1
  172. package/test/lib/matrix.test.js +1 -1
  173. package/test/lib/mouse.test.js +327 -4
  174. package/test/lib/patterns.test.js +105 -0
  175. package/test/lib/promptOptions.test.js +179 -0
  176. package/test/lib/scene.test.js +16 -52
  177. package/test/lib/selectionManager.test.js +17 -5
  178. package/test/lib/settings.test.js +111 -5
  179. package/test/lib/snapping.test.js +142 -4
  180. package/test/lib/stateManager.test.js +155 -0
  181. package/test/lib/utils.test.js +81 -1
  182. package/test/objects/dictionary.test.js +81 -0
  183. package/test/objects/dictionaryManager.test.js +34 -0
  184. package/test/properties/dimType.test.js +62 -0
  185. package/test/properties/flags.test.js +164 -0
  186. package/test/properties/property.test.js +13 -0
  187. package/test/properties/propertyManager.test.js +10 -11
  188. package/test/tables/appId.test.js +46 -0
  189. package/test/tables/appIdManager.test.js +72 -0
  190. package/test/tables/block.test.js +157 -0
  191. package/test/tables/blockManager.test.js +52 -0
  192. package/test/tables/blockRecord.test.js +39 -0
  193. package/test/tables/blockRecordManager.test.js +60 -0
  194. package/test/tables/dimstyle.test.js +72 -0
  195. package/test/tables/endblk.test.js +30 -0
  196. package/test/tables/layer.test.js +95 -0
  197. package/test/tables/layerManager.test.js +172 -0
  198. package/test/{styles → tables}/ltype.test.js +7 -7
  199. package/test/{styles → tables}/ltypeManager.test.js +2 -2
  200. package/test/tables/style.test.js +44 -0
  201. package/test/tables/stylemanagerbase.test.js +220 -0
  202. package/test/tables/tableManagerBase.test.js +194 -0
  203. package/test/tables/ucs.test.js +37 -0
  204. package/test/tables/ucsManager.test.js +48 -0
  205. package/test/tables/view.test.js +37 -0
  206. package/test/tables/viewManager.test.js +48 -0
  207. package/test/tables/vport.test.js +117 -0
  208. package/test/tables/vportManager.test.js +54 -0
  209. package/test/test-helpers/test-helpers.js +66 -0
  210. package/test/tools/chamfer.test.js +970 -0
  211. package/test/tools/chamferFilletBase.test.js +446 -0
  212. package/test/tools/copy.test.js +22 -14
  213. package/test/tools/copybase.test.js +93 -0
  214. package/test/tools/copyclip.test.js +92 -0
  215. package/test/tools/cornerEntity.test.js +201 -0
  216. package/test/tools/cutclip.test.js +94 -0
  217. package/test/tools/distance.test.js +140 -18
  218. package/test/tools/erase.test.js +12 -12
  219. package/test/tools/explode.test.js +45 -0
  220. package/test/tools/extend.test.js +326 -44
  221. package/test/tools/fillet.test.js +831 -0
  222. package/test/tools/identify.test.js +115 -16
  223. package/test/tools/matchProp.test.js +84 -0
  224. package/test/tools/mirror.test.js +475 -0
  225. package/test/tools/move.test.js +75 -19
  226. package/test/tools/offset.test.js +748 -0
  227. package/test/tools/pan.test.js +72 -0
  228. package/test/tools/pasteclip.test.js +100 -0
  229. package/test/tools/purge.test.js +89 -0
  230. package/test/tools/rotate.test.js +89 -23
  231. package/test/tools/scale.test.js +529 -0
  232. package/test/tools/trim.test.js +419 -84
  233. package/test/tools/zoom.test.js +504 -0
  234. package/core/entities/alignedDimension.js +0 -279
  235. package/core/entities/angularDimension.js +0 -194
  236. package/core/entities/baseDimension.js +0 -227
  237. package/core/entities/block.js +0 -247
  238. package/core/entities/diametricDimension.js +0 -133
  239. package/core/entities/dimension.js +0 -202
  240. package/core/entities/radialDimension.js +0 -126
  241. package/core/styles/dimStyle.js +0 -719
  242. package/core/styles/dimStyleManager.js +0 -47
  243. package/core/styles/layer.js +0 -133
  244. package/core/styles/layerManager.js +0 -40
  245. package/core/styles/ltype.js +0 -86
  246. package/core/styles/ltypeManager.js +0 -50
  247. package/core/styles/style.js +0 -158
  248. package/core/styles/styleManager.js +0 -43
  249. package/core/styles/styleManagerBase.js +0 -299
  250. package/test/entities/block.test.js +0 -22
  251. package/test/styles/dimstyle.test.js +0 -19
  252. package/test/styles/layerManager.test.js +0 -150
  253. package/test/styles/style.test.js +0 -33
  254. package/test/styles/stylemanagerbase.test.js +0 -219
@@ -1,28 +1,38 @@
1
- import {Point} from './point.js';
2
- import {Utils} from '../lib/utils.js';
3
- import {Strings} from '../lib/strings.js';
4
- import {Entity} from './entity.js';
5
- import {Input, PromptOptions} from '../lib/inputManager.js';
6
- import {Logging} from '../lib/logging.js';
7
- import {DXFFile} from '../lib/dxf/dxfFile.js';
8
- import {BoundingBox} from '../lib/boundingBox.js';
9
-
10
- import {DesignCore} from '../designCore.js';
11
-
12
-
1
+ import { Point } from './point.js';
2
+ import { Utils } from '../lib/utils.js';
3
+ import { Strings } from '../lib/strings.js';
4
+ import { Entity } from './entity.js';
5
+ import { Input, PromptOptions } from '../lib/inputManager.js';
6
+ import { Logging } from '../lib/logging.js';
7
+ import { DXFFile } from '../lib/dxf/dxfFile.js';
8
+ import { BoundingBox } from '../lib/boundingBox.js';
9
+ import { AddState, RemoveState } from '../lib/stateManager.js';
10
+
11
+ import { DesignCore } from '../designCore.js';
12
+ import { Property } from '../properties/property.js';
13
+
14
+ /**
15
+ * Arc Entity Class
16
+ * @extends Entity
17
+ * */
13
18
  export class Arc extends Entity {
19
+ /**
20
+ * Create an Arc Entity
21
+ * @param {Array} data
22
+ */
14
23
  constructor(data) {
15
24
  super(data);
16
25
  this.radius = 1;
17
26
 
18
- // direction: - ccw > 0, cw < 0
27
+ // direction: - ccw > 0, cw <= 0
28
+ // default to 1 - counter clockwise
19
29
  Object.defineProperty(this, 'direction', {
20
30
  value: 1,
21
31
  writable: true,
22
32
  });
23
33
 
24
34
  if (data) {
25
- if (data.points || data[40]) {
35
+ if (data.hasOwnProperty('points') || data.hasOwnProperty('40')) {
26
36
  // DXF Groupcode 40 - Radius
27
37
  // get the radius from the points or the incoming dxf groupcode
28
38
  const radius = this.points[1] ? this.points[0].distance(this.points[1]) : data[40];
@@ -32,52 +42,106 @@ export class Arc extends Entity {
32
42
  }
33
43
  }
34
44
 
35
- if (data.hasOwnProperty('startAngle') || data[50]) {
45
+ if (data.hasOwnProperty('startAngle') || data.hasOwnProperty('50')) {
36
46
  // DXF Groupcode 50 - Start Angle
37
- const angle = Utils.degrees2radians(data.startAngle || data[50]);
38
- this.points[1] = this.points[0].project(angle, this.radius);
47
+
48
+ const angle = Property.loadValue([data.startAngle, data[50]], 0);
49
+ const projectionAngle = Utils.degrees2radians(angle);
50
+ this.points[1] = this.points[0].project(projectionAngle, this.radius);
39
51
  }
40
52
 
41
- if (data.hasOwnProperty('endAngle') || data[51]) {
53
+ if (data.hasOwnProperty('endAngle') || data.hasOwnProperty('51')) {
42
54
  // DXF Groupcode 51 - End Angle
43
- const angle = Utils.degrees2radians(data.endAngle || data[51]);
44
- this.points[2] = this.points[0].project(angle, this.radius);
55
+ const angle = Property.loadValue([data.endAngle, data[51]], 0);
56
+ const projectionAngle = Utils.degrees2radians(angle);
57
+ this.points[2] = this.points[0].project(projectionAngle, this.radius);
45
58
  }
46
59
 
47
- if (data.hasOwnProperty('direction')) {
60
+ if (data.hasOwnProperty('direction') || data.hasOwnProperty('73')) {
48
61
  // No DXF Groupcode - Arc Direction
49
- const direction = data.direction;
50
- this.direction = direction;
62
+ this.direction = Property.loadValue([data.direction, data[73]], 1);
51
63
  }
52
64
  }
53
65
  }
54
66
 
67
+ /**
68
+ * Register the command
69
+ * @return {Object}
70
+ * command = name of the command
71
+ * shortcut = shortcut for the command
72
+ * type = type to group command in toolbars (omitted if not shown)
73
+ */
55
74
  static register() {
56
- const command = {command: 'Arc', shortcut: 'A', type: 'Entity'};
75
+ const command = { command: 'Arc', shortcut: 'A', type: 'Entity' };
57
76
  return command;
58
77
  }
59
78
 
79
+ /**
80
+ * Execute method
81
+ * executes the workflow, requesting input required to create an entity
82
+ */
60
83
  async execute() {
61
84
  try {
62
85
  const op = new PromptOptions(Strings.Input.CENTER, [Input.Type.POINT]);
63
86
  const pt = await DesignCore.Scene.inputManager.requestInput(op);
87
+ if (pt === undefined) return;
64
88
  this.points.push(pt);
65
89
 
66
90
  const op1 = new PromptOptions(Strings.Input.START, [Input.Type.POINT]);
67
91
  const pt1 = await DesignCore.Scene.inputManager.requestInput(op1);
92
+ if (pt1 === undefined) return;
68
93
  this.points.push(pt1);
69
94
 
70
- const op2 = new PromptOptions(Strings.Input.ANGLE, [Input.Type.POINT, Input.Type.NUMBER]);
71
- const pt2 = await DesignCore.Scene.inputManager.requestInput(op2);
72
-
73
- if (Input.getType(pt2) === Input.Type.POINT) {
74
- this.points.push(pt2);
75
- } else if (Input.getType(pt2) === Input.Type.NUMBER) {
76
- const basePoint = this.points.at(0);
77
- const startPoint = this.points.at(1);
78
- const angle = Utils.degrees2radians(pt2);
79
- const point = startPoint.rotate(basePoint, angle);
80
- this.points.push(point);
95
+ let pt2;
96
+ while (true) {
97
+ const op2 = new PromptOptions(`${Strings.Input.END} ${Strings.Strings.OR} ${Strings.Input.ANGLE}`, [Input.Type.POINT, Input.Type.NUMBER]);
98
+ pt2 = await DesignCore.Scene.inputManager.requestInput(op2);
99
+ if (pt2 === undefined) return;
100
+
101
+ if (Input.getType(pt2) === Input.Type.POINT) {
102
+ // Must not be same as center or start
103
+ const center = this.points[0];
104
+ const start = this.points[1];
105
+ if (pt2.isSame?.(center) || pt2.isSame?.(start)) {
106
+ DesignCore.Core.notify(`${this.type} - ${Strings.Error.INVALIDPOINT}`);
107
+ continue;
108
+ }
109
+ this.points.push(pt2);
110
+ // Infer arc direction using cross product if we have 3 points
111
+ if (this.points.length === 3) {
112
+ this.direction = Arc.#inferDirection(this.points[0], this.points[1], this.points[2]);
113
+ // Check total angle > 1 degree
114
+ if (Math.abs(this.totalAngle) <= 1) {
115
+ this.points.pop(); // Remove invalid end point
116
+ DesignCore.Core.notify(`${this.type} - ${Strings.Error.INVALIDNUMBER}: ${Strings.Error.MINVALUE}`);
117
+ continue;
118
+ }
119
+ }
120
+ break;
121
+ } else if (Input.getType(pt2) === Input.Type.NUMBER) {
122
+ // Must be a valid angle in (0, 360]
123
+ const angleValue = Number(pt2);
124
+ if (!Number.isFinite(angleValue) || Math.abs(angleValue) <= 0 || Math.abs(angleValue) > 360) {
125
+ DesignCore.Core.notify(`${this.type} - ${Strings.Error.INVALIDNUMBER}`);
126
+ continue;
127
+ }
128
+ const basePoint = this.points.at(0);
129
+ const startPoint = this.points.at(1);
130
+ const angle = Utils.degrees2radians(angleValue);
131
+ const point = startPoint.rotate(basePoint, angle);
132
+ this.points.push(point);
133
+ // Set direction based on sign of angle
134
+ this.direction = angle >= 0 ? 1 : -1;
135
+ // Check total angle > 1 degree
136
+ if (Math.abs(this.totalAngle) <= 1) {
137
+ this.points.pop(); // Remove invalid end point
138
+ DesignCore.Core.notify(`${this.type} - ${Strings.Error.INVALIDNUMBER}: ${Strings.Error.MINVALUE}`);
139
+ continue;
140
+ }
141
+ break;
142
+ } else {
143
+ DesignCore.Core.notify(`${this.type} - ${Strings.Error.INPUT}`);
144
+ }
81
145
  }
82
146
 
83
147
  DesignCore.Scene.inputManager.executeCommand(this);
@@ -86,71 +150,258 @@ export class Arc extends Entity {
86
150
  }
87
151
  }
88
152
 
153
+ /**
154
+ * Preview the entity during creation
155
+ */
89
156
  preview() {
90
157
  if (this.points.length >= 1) {
91
158
  const mousePoint = DesignCore.Mouse.pointOnScene();
92
159
  const points = [this.points.at(0), mousePoint];
93
- DesignCore.Scene.createTempItem('Line', {points: points});
160
+ DesignCore.Scene.tempEntities.create('Line', { points: points });
94
161
  }
95
162
 
96
163
  if (this.points.length >= 2) {
97
164
  const mousePoint = DesignCore.Mouse.pointOnScene();
98
165
  const points = [...this.points, mousePoint];
99
- DesignCore.Scene.createTempItem(this.type, {points: points});
166
+
167
+ // Infer direction for preview, so it matches what will be created
168
+ let direction = 1;
169
+ if (points.length === 3) {
170
+ direction = Arc.#inferDirection(points[0], points[1], points[2]);
171
+ }
172
+
173
+ DesignCore.Scene.tempEntities.create(this.type, { points: points, direction: direction });
174
+ }
175
+ }
176
+
177
+ /**
178
+ * Infer arc direction from three points using cross product
179
+ * @param {Point} center
180
+ * @param {Point} start
181
+ * @param {Point} end
182
+ * @return {number} 1 for ccw, -1 for cw
183
+ */
184
+ static #inferDirection(center, start, end) {
185
+ // All points must be defined and distinct
186
+ if (!center || !start || !end) {
187
+ return 1; // Default to counterclockwise
188
+ }
189
+ if (center.isSame?.(start) || center.isSame?.(end) || start.isSame?.(end)) {
190
+ return 1;
100
191
  }
192
+
193
+ const v1 = start.subtract(center);
194
+ const v2 = end.subtract(center);
195
+ const cross = v1.cross(v2);
196
+ const direction = cross > 0 ? 1 : -1;
197
+ return direction;
101
198
  }
102
199
 
200
+ /**
201
+ * Arc start angle
202
+ * @return {number} start angle in radians
203
+ */
103
204
  startAngle() {
104
205
  return this.points[0].angle(this.points[1]);
105
206
  }
106
207
 
208
+ /**
209
+ * Arc end angle
210
+ * @return {number} end angle in radians
211
+ */
107
212
  endAngle() {
108
- return this.points[0].angle(this.points[2]);
213
+ const endAngle = this.points[0].angle(this.points[2]);
214
+ return endAngle;
109
215
  }
110
216
 
217
+ /**
218
+ * Calculate the angle between the start and end of the arc
219
+ * Clockwise returns positive angle
220
+ * Counter clockwise returns negative
221
+ * @return {number} angle in degrees
222
+ */
223
+ get totalAngle() {
224
+ let startAngle = this.startAngle();
225
+ let endAngle = this.endAngle();
226
+
227
+
228
+ if (this.direction > 0) {
229
+ if (startAngle > endAngle || startAngle === endAngle) {
230
+ endAngle += (Math.PI * 2);
231
+ }
232
+ } else {
233
+ startAngle += (Math.PI * 2);
234
+ }
235
+
236
+ const totalAngle = (startAngle - endAngle);
237
+ return Utils.radians2degrees(totalAngle);
238
+ }
239
+
240
+ /**
241
+ * Get Arc radius
242
+ * @return {number} - arc radius
243
+ */
111
244
  getRadius() {
112
245
  return this.radius;
113
246
  }
114
247
 
248
+ /**
249
+ * Draw the entity
250
+ * @param {Object} ctx - context
251
+ * @param {number} scale
252
+ */
115
253
  draw(ctx, scale) {
116
- ctx.arc(this.points[0].x, this.points[0].y, this.radius, this.startAngle(), this.endAngle());
254
+ 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());
117
255
  ctx.stroke();
118
256
  }
119
257
 
258
+ /**
259
+ * Write the entity to file in the dxf format
260
+ * @param {DXFFile} file
261
+ */
120
262
  dxf(file) {
121
263
  file.writeGroupCode('0', 'ARC');
122
- file.writeGroupCode('5', file.nextHandle(), DXFFile.Version.R2000); // Handle
264
+ file.writeGroupCode('5', this.handle, DXFFile.Version.R2000); // Handle
123
265
  file.writeGroupCode('100', 'AcDbEntity', DXFFile.Version.R2000);
124
266
  file.writeGroupCode('100', 'AcDbCircle', DXFFile.Version.R2000);
125
267
  file.writeGroupCode('8', this.layer); // LAYERNAME
268
+ file.writeGroupCode('6', this.lineType); // LINETYPE
126
269
  file.writeGroupCode('10', this.points[0].x); // X
127
270
  file.writeGroupCode('20', this.points[0].y); // Y
128
271
  file.writeGroupCode('30', '0.0'); // Z
272
+ file.writeGroupCode('39', this.lineWidth); // Line Width
129
273
  file.writeGroupCode('40', this.radius); // Radius
130
274
  file.writeGroupCode('100', 'AcDbArc', DXFFile.Version.R2000);
131
- file.writeGroupCode('50', Utils.radians2degrees(this.startAngle())); // Start Angle
132
- file.writeGroupCode('51', Utils.radians2degrees(this.endAngle())); // End Angle
275
+ file.writeGroupCode('50', Utils.radians2degrees(this.direction > 0 ? this.startAngle() : this.endAngle())); // Start Angle
276
+ file.writeGroupCode('51', Utils.radians2degrees(this.direction > 0 ? this.endAngle() : this.startAngle())); // End Angle
133
277
  }
134
278
 
135
- intersectPoints() {
136
- return {
137
- centre: this.points[0],
138
- startPoint: this.points[1],
139
- endPoint: this.points[2],
140
- radius: this.radius,
141
- direction: this.direction,
142
- };
279
+ /**
280
+ * Return a list of points representing a polyline version of this entity
281
+ * @return {Array}
282
+ */
283
+ toPolylinePoints() {
284
+ // counter clockwise bulge = +ve, clockwise bulge = -ve,
285
+ // ccw arc = 0, clockwise arc = 1
286
+
287
+ // If the arc forms a complete circle
288
+ // Split into two separate polyline arcs
289
+ if (Math.abs(this.totalAngle) === 360) {
290
+ const startPoint = this.points[0].project(0, this.radius);
291
+ startPoint.bulge = 1;
292
+ const endPoint = this.points[0].project(0, -this.radius);
293
+ endPoint.bulge = 1;
294
+ const closurePoint = this.points[0].project(0, this.radius);
295
+ return [startPoint, endPoint, closurePoint];
296
+ }
297
+
298
+ const startPoint = this.points[0].project(this.startAngle(), this.radius);
299
+ const bulge = Math.tan(Utils.degrees2radians(-this.totalAngle % 360) / 4);
300
+ startPoint.bulge = bulge;
301
+ const endPoint = this.points[0].project(this.endAngle(), this.radius);
302
+ return [startPoint, endPoint];
303
+ }
304
+
305
+ /**
306
+ * Set entity points from a polyline point representation
307
+ * @param {Array} points
308
+ */
309
+ fromPolylinePoints(points) {
310
+ const center = points[0].bulgeCentrePoint(points[1]);
311
+ const radius = center.distance(points[0]);
312
+ this.points = [center, new Point(points[0].x, points[0].y), new Point(points[1].x, points[1].y)];
313
+ this.radius = radius;
143
314
  }
144
315
 
316
+ /**
317
+ * Trim the entity
318
+ * @param {Array} intersections
319
+ * @return {Array} - array of state changes
320
+ */
321
+ trim(intersections) {
322
+ // array to hold state changes
323
+ const stateChanges = [];
324
+
325
+ if (intersections?.length === 0 || !intersections) {
326
+ return stateChanges;
327
+ }
328
+
329
+ // get the mouse position
330
+ const mousePosition = DesignCore.Mouse.pointOnScene();
331
+ // get the point on the arc closest to the mouse
332
+ const pointOnArc = mousePosition.closestPointOnArc(this.points[1], this.points[2], this.points[0], this.direction);
333
+ // remove any intersections that are at the end points of the arc
334
+ intersections = intersections.filter((p) => !p.isSame(this.points[1]) && !p.isSame(this.points[2]));
335
+ // sort intersection points by distance to start point
336
+ Utils.sortPointsOnArc(intersections, this.points[1], this.points[2], this.points[0], this.direction);
337
+ // Add arc points to the start and the end of the point array
338
+ const testPoints = [this.points[1], ...intersections, this.points[2]];
339
+
340
+ // Test if mouse position is between two intersection points
341
+ if (testPoints.length > 1) {
342
+ for (let i = 0; i < testPoints.length - 1; i++) {
343
+ const startPoint = testPoints[i];
344
+ const endPoint = testPoints[i + 1];
345
+
346
+ // check if the mouse is between startPoint and endPoint
347
+ if (pointOnArc.isOnArc(startPoint, endPoint, this.points[0], this.direction)) {
348
+ // Store new entity points
349
+ const newPoints = [];
350
+
351
+ for (const p of testPoints) {
352
+ const inOriginalArc = this.points.indexOf(p) !== -1;
353
+ const inIntersections = intersections.indexOf(p) !== -1;
354
+
355
+ // Keep points which:
356
+ // - The mouse is between are intersections
357
+ // - The mouse is not between and in this arc
358
+ const isBetween = p.isSame(startPoint) || p.isSame(endPoint);
359
+ if ((isBetween && inIntersections) || (!isBetween && inOriginalArc)) {
360
+ newPoints.push(p);
361
+ }
362
+ }
363
+
364
+ if (newPoints.length % 2 === 0) {
365
+ // Add arcs for each point pair
366
+ for (let j = 0; j < newPoints.length; j += 2) {
367
+ const arc = Utils.cloneObject(this);
368
+ arc.points = [this.points[0], newPoints[j], newPoints[j + 1]];
369
+ const addState = new AddState(arc);
370
+ stateChanges.push(addState);
371
+ }
372
+ }
373
+
374
+ if (stateChanges.length > 0) {
375
+ // Remove the existing arc
376
+ const removeState = new RemoveState(this);
377
+ stateChanges.push(removeState);
378
+ }
379
+ }
380
+ }
381
+ }
382
+
383
+ return stateChanges;
384
+ }
385
+
386
+ /**
387
+ * Get snap points
388
+ * @param {Point} mousePoint
389
+ * @param {number} delta
390
+ * @return {Array} - array of snap points
391
+ */
145
392
  snaps(mousePoint, delta) {
146
393
  const snaps = [];
147
394
 
148
395
  if (DesignCore.Settings.endsnap) {
149
396
  // Speed this up by generating the proper start and end points when the arc is initialised
150
- const startPoint = new Point(this.points[0].x + (this.radius * Math.cos(this.startAngle())),
151
- this.points[0].y + (this.radius * Math.sin(this.startAngle())));
152
- const endPoint = new Point(this.points[0].x + (this.radius * Math.cos(this.endAngle())),
153
- this.points[0].y + (this.radius * Math.sin(this.endAngle())));
397
+ const startPoint = new Point(
398
+ this.points[0].x + (this.radius * Math.cos(this.startAngle())),
399
+ this.points[0].y + (this.radius * Math.sin(this.startAngle())),
400
+ );
401
+ const endPoint = new Point(
402
+ this.points[0].x + (this.radius * Math.cos(this.endAngle())),
403
+ this.points[0].y + (this.radius * Math.sin(this.endAngle())),
404
+ );
154
405
 
155
406
  snaps.push(startPoint, endPoint);
156
407
  }
@@ -169,17 +420,20 @@ export class Arc extends Entity {
169
420
  }
170
421
  }
171
422
 
172
-
173
423
  return snaps;
174
424
  }
175
425
 
426
+ /**
427
+ * Get closest point on entity
428
+ * @param {Point} P
429
+ * @return {Array} - [Point, distance]
430
+ */
176
431
  closestPoint(P) {
177
432
  const startPoint = this.points[1];
178
433
  const endPoint = this.points[2];
179
434
  const centerPoint = this.points[0];
180
- // TODO: enable defining clockwise arcs
181
- const direction = 1;
182
- const pnt = P.closestPointOnArc(startPoint, endPoint, centerPoint, direction);
435
+
436
+ const pnt = P.closestPointOnArc(startPoint, endPoint, centerPoint, this.direction);
183
437
 
184
438
  if (pnt !== null) {
185
439
  const distance = P.distance(pnt);
@@ -191,8 +445,8 @@ export class Arc extends Entity {
191
445
  }
192
446
 
193
447
  /**
194
- * Return boundingbox
195
- * @returns BoundingBox
448
+ * Return boundingbox for entity
449
+ * @return {BoundingBox}
196
450
  */
197
451
  boundingBox() {
198
452
  return BoundingBox.arcBoundingBox(this.points[0], this.points[1], this.points[2], this.direction);