@design-core/core 0.0.6 → 0.0.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (145) hide show
  1. package/.eslintrc.json +4 -0
  2. package/TODO.md +49 -6
  3. package/core/core/core.js +0 -3
  4. package/core/dimensions/alignedDimension.js +5 -4
  5. package/core/dimensions/angularDimension.js +6 -5
  6. package/core/dimensions/baseCircularDimension.js +3 -2
  7. package/core/dimensions/baseDimension.js +59 -93
  8. package/core/dimensions/baseLinearDimension.js +6 -5
  9. package/core/dimensions/diametricDimension.js +4 -3
  10. package/core/dimensions/dimension.js +21 -14
  11. package/core/dimensions/radialDimension.js +4 -3
  12. package/core/dimensions/rotatedDimension.js +12 -11
  13. package/core/entities/arc.js +112 -145
  14. package/core/entities/arctext.js +212 -174
  15. package/core/entities/basePolyline.js +30 -282
  16. package/core/entities/circle.js +77 -99
  17. package/core/entities/entity.js +114 -93
  18. package/core/entities/hatch.js +322 -249
  19. package/core/entities/insert.js +66 -81
  20. package/core/entities/line.js +21 -142
  21. package/core/entities/lwpolyline.js +16 -11
  22. package/core/entities/polyline.js +4 -4
  23. package/core/entities/rectangle.js +3 -1
  24. package/core/entities/solid.js +6 -15
  25. package/core/entities/text.js +188 -199
  26. package/core/lib/auxiliary/rubberBand.js +32 -0
  27. package/core/lib/auxiliary/selectionWindow.js +63 -0
  28. package/core/lib/auxiliary/snapPoint.js +113 -0
  29. package/core/lib/auxiliary/trackingLine.js +76 -0
  30. package/core/lib/canvas.js +228 -166
  31. package/core/lib/clipboard.js +7 -1
  32. package/core/lib/colour.js +16 -0
  33. package/core/lib/commandLine.js +1 -6
  34. package/core/lib/commandManager.js +18 -8
  35. package/core/lib/constants.js +28 -0
  36. package/core/lib/dxf/dxf.js +4 -3
  37. package/core/lib/entityManager.js +18 -11
  38. package/core/lib/inputManager.js +35 -11
  39. package/core/lib/intersect.js +25 -0
  40. package/core/lib/patterns.js +8 -1
  41. package/core/lib/plotOptions.js +67 -0
  42. package/core/lib/polylineUtils.js +75 -0
  43. package/core/lib/renderers/cairoRenderer.js +371 -0
  44. package/core/lib/renderers/canvasRenderer.js +315 -0
  45. package/core/lib/renderers/pdfRenderer.js +366 -0
  46. package/core/lib/renderers/rendererBase.js +284 -0
  47. package/core/lib/renderers/svgRenderer.js +343 -0
  48. package/core/lib/scene.js +27 -5
  49. package/core/lib/selectionManager.js +2 -2
  50. package/core/lib/settings.js +4 -2
  51. package/core/lib/snapping.js +4 -191
  52. package/core/lib/stateManager.js +30 -1
  53. package/core/lib/utils.js +21 -2
  54. package/core/properties/entityProperties.js +104 -0
  55. package/core/properties/property.js +106 -2
  56. package/core/properties/propertyManager.js +29 -13
  57. package/core/tables/block.js +4 -37
  58. package/core/tables/endblk.js +1 -1
  59. package/core/tables/layer.js +8 -0
  60. package/core/tables/styleManagerBase.js +3 -2
  61. package/core/tables/tableManagerBase.js +8 -3
  62. package/core/tools/chamfer.js +111 -137
  63. package/core/tools/chamferFilletBase.js +135 -34
  64. package/core/tools/copy.js +3 -3
  65. package/core/tools/explode.js +2 -2
  66. package/core/tools/extend.js +132 -19
  67. package/core/tools/fillet.js +86 -183
  68. package/core/tools/matchProp.js +2 -11
  69. package/core/tools/mirror.js +7 -6
  70. package/core/tools/move.js +3 -3
  71. package/core/tools/offset.js +3 -5
  72. package/core/tools/pasteclip.js +1 -1
  73. package/core/tools/rotate.js +6 -3
  74. package/core/tools/scale.js +1 -1
  75. package/core/tools/trim.js +265 -23
  76. package/core/tools/zoom.js +1 -1
  77. package/jest.config.js +6 -0
  78. package/package.json +5 -3
  79. package/test/dimensions/alignedDimension.test.js +14 -7
  80. package/test/dimensions/angularDimension.test.js +6 -6
  81. package/test/dimensions/baseDimension.test.js +69 -8
  82. package/test/dimensions/baseLinearDimension.test.js +62 -0
  83. package/test/dimensions/diametricDimension.test.js +6 -6
  84. package/test/dimensions/radialDimension.test.js +6 -6
  85. package/test/dimensions/rotatedDimension.test.js +14 -7
  86. package/test/entities/arc.test.js +80 -83
  87. package/test/entities/arctext.test.js +180 -53
  88. package/test/entities/basePolyline.test.js +68 -2
  89. package/test/entities/circle.test.js +57 -85
  90. package/test/entities/entity.test.js +42 -8
  91. package/test/entities/hatch.test.js +543 -70
  92. package/test/entities/insert.test.js +96 -1
  93. package/test/entities/line.test.js +15 -41
  94. package/test/entities/lwpolyline.test.js +104 -0
  95. package/test/entities/polyline.test.js +11 -0
  96. package/test/entities/rectangle.test.js +11 -0
  97. package/test/entities/solid.test.js +1 -1
  98. package/test/entities/text.test.js +294 -69
  99. package/test/integration/exportIntegration.reference.dxf +9 -9
  100. package/test/integration/exportIntegration.test.js +2 -2
  101. package/test/integration/intersectionIntegration.test.js +4 -5
  102. package/test/integration/pdfRendererIntegration.reference.pdf +17709 -0
  103. package/test/integration/pdfRendererIntegration.test.js +61 -0
  104. package/test/integration/svgRendererIntegration.reference.svg +3343 -0
  105. package/test/integration/svgRendererIntegration.test.js +61 -0
  106. package/test/integration/userInputIntegration.reference.dxf +1165 -105
  107. package/test/integration/userInputIntegration.test.js +239 -132
  108. package/test/lib/canvas.test.js +742 -123
  109. package/test/lib/constants.test.js +27 -0
  110. package/test/lib/entityManager.test.js +4 -4
  111. package/test/lib/inputManager.test.js +80 -19
  112. package/test/lib/intersect.test.js +83 -0
  113. package/test/lib/plotOptions.test.js +179 -0
  114. package/test/lib/rubberBand.test.js +84 -0
  115. package/test/lib/settings.test.js +5 -6
  116. package/test/lib/snapping.test.js +71 -129
  117. package/test/lib/stateManager.test.js +7 -7
  118. package/test/lib/utils.test.js +36 -2
  119. package/test/properties/entityProperties.test.js +132 -0
  120. package/test/properties/property.test.js +53 -0
  121. package/test/properties/propertyManager.test.js +3 -3
  122. package/test/renderers/canvasRenderer.test.js +317 -0
  123. package/test/renderers/pdfRenderer.test.js +271 -0
  124. package/test/renderers/rendererBase.test.js +213 -0
  125. package/test/renderers/svgRenderer.test.js +326 -0
  126. package/test/tables/block.test.js +31 -1
  127. package/test/tables/endblk.test.js +2 -2
  128. package/test/tables/layer.test.js +30 -0
  129. package/test/test-helpers/hatchGridRenderer.js +180 -0
  130. package/test/test-helpers/test-helpers.js +152 -0
  131. package/test/tools/chamfer.test.js +227 -5
  132. package/test/tools/chamferFilletBase.test.js +98 -9
  133. package/test/tools/copy.test.js +2 -2
  134. package/test/tools/explode.test.js +3 -3
  135. package/test/tools/extend.test.js +331 -0
  136. package/test/tools/fillet.test.js +181 -0
  137. package/test/tools/matchProp.test.js +5 -5
  138. package/test/tools/mirror.test.js +13 -13
  139. package/test/tools/offset.test.js +170 -3
  140. package/test/tools/pasteclip.test.js +5 -5
  141. package/test/tools/rotate.test.js +32 -2
  142. package/test/tools/scale.test.js +5 -5
  143. package/test/tools/trim.test.js +526 -0
  144. package/test/tools/zoom.test.js +7 -7
  145. package/core/lib/selectionWindow.js +0 -83
@@ -200,7 +200,7 @@ class State {
200
200
  // capture previous properties for undo
201
201
  const previousProperties = {};
202
202
  for (const prop of Object.getOwnPropertyNames(stateChange.properties)) {
203
- previousProperties[prop] = stateChange.entity[prop];
203
+ previousProperties[prop] = stateChange.entity.getProperty(prop);
204
204
  }
205
205
  const undoStateChange = new UpdateState(stateChange.entity, previousProperties);
206
206
  this.undoStateChanges.push(undoStateChange);
@@ -208,6 +208,15 @@ class State {
208
208
  this.entityManager.update(index, stateChange.properties);
209
209
  }
210
210
  }
211
+
212
+ if (stateChange instanceof ReplaceState) {
213
+ const index = this.entityManager.indexOf(stateChange.entity);
214
+ if (index !== -1) {
215
+ this.entityManager.replace(index, stateChange.replacement);
216
+ const undoStateChange = new ReplaceState(stateChange.replacement, stateChange.entity);
217
+ this.undoStateChanges.push(undoStateChange);
218
+ }
219
+ }
211
220
  }
212
221
  }
213
222
 
@@ -231,6 +240,13 @@ class State {
231
240
  this.entityManager.update(index, stateChange.properties);
232
241
  }
233
242
  }
243
+
244
+ if (stateChange instanceof ReplaceState) {
245
+ const index = this.entityManager.indexOf(stateChange.entity);
246
+ if (index !== -1) {
247
+ this.entityManager.replace(index, stateChange.replacement);
248
+ }
249
+ }
234
250
  }
235
251
  }
236
252
  }
@@ -286,3 +302,16 @@ export class UpdateState extends StateChange {
286
302
  super(entity, properties);
287
303
  }
288
304
  }
305
+
306
+ /** Replace State Class */
307
+ export class ReplaceState {
308
+ /**
309
+ * Create Replace State
310
+ * @param {object} entity - the entity to be replaced (used to locate it by reference)
311
+ * @param {object} replacement - the entity to put in its place
312
+ */
313
+ constructor(entity, replacement) {
314
+ this.entity = entity;
315
+ this.replacement = replacement;
316
+ }
317
+ }
package/core/lib/utils.js CHANGED
@@ -1,6 +1,7 @@
1
1
 
2
2
  import { Logging } from './logging.js';
3
3
  import { Constants } from './constants.js';
4
+ import { Property } from '../properties/property.js';
4
5
 
5
6
  /** Utils Class */
6
7
  export class Utils {
@@ -114,13 +115,31 @@ export class Utils {
114
115
  }
115
116
 
116
117
  // Clear handle so a fresh unique handle is assigned when added to the scene
117
- if (clone.handle !== undefined) {
118
- clone.handle = undefined;
118
+ if (clone.getProperty?.(Property.Names.HANDLE) !== undefined) {
119
+ clone.setProperty?.(Property.Names.HANDLE, undefined);
119
120
  }
120
121
 
121
122
  return clone;
122
123
  }
123
124
 
125
+ /**
126
+ * Copy display properties from a source entity into a plain data object.
127
+ * Useful when creating a new entity that should inherit the visual properties
128
+ * of an existing one (e.g. the arc produced by Fillet).
129
+ * @param {Entity} entity - source entity
130
+ * @param {Array<string>} properties - property names to copy
131
+ * @return {Object} plain object containing the copied properties
132
+ */
133
+ static cloneProperties(entity, properties = ['colour', 'layer', 'lineType', 'lineWidth', 'height', 'styleName', 'dimensionStyle', 'patternName', 'scale']) {
134
+ const result = {};
135
+ for (const prop of properties) {
136
+ if (entity.properties?.has(prop)) {
137
+ result[prop] = entity.getProperty(prop);
138
+ }
139
+ }
140
+ return result;
141
+ }
142
+
124
143
  /**
125
144
  * Calculate area of triangle given 3 points
126
145
  * @param {Point} p1
@@ -0,0 +1,104 @@
1
+ import { Property } from './property.js';
2
+
3
+ /**
4
+ * EntityProperties — typed property store for an entity.
5
+ *
6
+ * Holds a plain-object store of named Property descriptors.
7
+ * Each descriptor owns its value.
8
+ * Using a plain object (rather than a private Map) ensures the store is deep-cloneable by Utils.cloneObject.
9
+ *
10
+ * Computed properties may supply get(entity) and set(entity, value) functions.
11
+ * These are stateless functions (not bound methods) so they survive cloneObject.
12
+ */
13
+ export class EntityProperties {
14
+ /** @type {Object.<string, Property>} */
15
+ // Store is a plain object to ensure it is deep-cloneable by Utils.cloneObject.
16
+ _store = {};
17
+
18
+ /**
19
+ * Register a property.
20
+ * @param {string} name - property name
21
+ * @param {Object} definition - passed directly to the Property constructor
22
+ * @param {string} definition.type - Property.Type constant
23
+ * @param {any} definition.value - initial value
24
+ * @param {boolean} [definition.readOnly=false]
25
+ * @param {boolean} [definition.visible=true]
26
+ * @param {number} [definition.dxfCode]
27
+ * @param {Function} [definition.get] - computed getter: (entity) => value
28
+ * @param {Function} [definition.set] - computed setter: (entity, value) => void
29
+ */
30
+ add(name, definition) {
31
+ this._store[name] = new Property(definition);
32
+ }
33
+
34
+ /**
35
+ * Get the current value of a property.
36
+ * For computed properties, invokes get(entity).
37
+ * For stored properties, returns prop.value directly.
38
+ * @param {string} name
39
+ * @param {Object} [entity] - passed to the computed getter when prop.get is defined
40
+ * @return {any} value, or undefined if not registered
41
+ */
42
+ get(name, entity) {
43
+ const prop = this._store[name];
44
+ if (!prop) return undefined;
45
+ if (prop.get) return prop.get(entity);
46
+ return prop.value;
47
+ }
48
+
49
+ /**
50
+ * Set the value of a property.
51
+ * For computed properties, invokes set(entity, value).
52
+ * For stored properties, updates prop.value directly.
53
+ * No-op if the property is not registered or is readOnly.
54
+ * @param {string} name
55
+ * @param {any} value
56
+ * @param {Object} [entity] - passed to the computed setter when prop.set is defined
57
+ */
58
+ set(name, value, entity) {
59
+ const prop = this._store[name];
60
+ if (!prop) return;
61
+ if (prop.readOnly) return;
62
+ if (prop.set) {
63
+ prop.set(entity, value);
64
+ } else {
65
+ prop.value = value;
66
+ }
67
+ }
68
+
69
+ /**
70
+ * Check whether a property is registered.
71
+ * @param {string} name
72
+ * @return {boolean}
73
+ */
74
+ has(name) {
75
+ return name in this._store;
76
+ }
77
+
78
+ /**
79
+ * Remove a property from the store.
80
+ * @param {string} name
81
+ */
82
+ remove(name) {
83
+ delete this._store[name];
84
+ }
85
+
86
+ /**
87
+ * Return names of all visible properties in registration order.
88
+ * @return {string[]}
89
+ */
90
+ list() {
91
+ return Object.entries(this._store)
92
+ .filter(([, prop]) => prop.visible)
93
+ .map(([name]) => name);
94
+ }
95
+
96
+ /**
97
+ * Return the full Property descriptor for a named property.
98
+ * @param {string} name
99
+ * @return {Property|undefined}
100
+ */
101
+ definition(name) {
102
+ return this._store[name];
103
+ }
104
+ }
@@ -2,8 +2,112 @@ import { Flags } from './flags.js';
2
2
 
3
3
  /** Property Class */
4
4
  export class Property {
5
- /** Create property */
6
- constructor() { }
5
+ /**
6
+ * Create a Property descriptor
7
+ * @param {Object} [options]
8
+ * @param {string} options.type - Property.Type constant describing the value kind
9
+ * @param {any} options.value - initial value
10
+ * @param {boolean} [options.readOnly=false] - when true, set() is a no-op
11
+ * @param {boolean} [options.visible=true] - controls whether the property appears in listings
12
+ * @param {number} [options.dxfCode] - associated DXF group code (informational)
13
+ * @param {Function} [options.get] - computed getter: (entity) => value
14
+ * @param {Function} [options.set] - computed setter: (entity, value) => void
15
+ */
16
+ constructor({ type, value, readOnly = false, visible = true, dxfCode, get, set, options } = {}) {
17
+ this.type = type;
18
+ this._value = value;
19
+ this.readOnly = readOnly;
20
+ this.visible = visible;
21
+ this.dxfCode = dxfCode;
22
+ this.get = get;
23
+ this.set = set;
24
+ this.options = options;
25
+ }
26
+
27
+ /** @return {any} current value */
28
+ get value() {
29
+ return this._value;
30
+ }
31
+
32
+ /** @param {any} val - new value; ignored when readOnly */
33
+ set value(val) {
34
+ if (!this.readOnly) {
35
+ this._value = val;
36
+ }
37
+ }
38
+
39
+ /**
40
+ * Property type constants
41
+ * Used to describe the type of a property value for UI rendering and validation.
42
+ * - NUMBER: numeric value (integer or float)
43
+ * - STRING: text value
44
+ * - BOOLEAN: true/false toggle
45
+ * - LIST: selection from a named set (layer, lineType, hatch pattern, etc.)
46
+ * - COLOUR: RGB colour object
47
+ * - LABEL: read-only display value (no editing)
48
+ */
49
+ static Type = {
50
+ NUMBER: 'NUMBER',
51
+ STRING: 'STRING',
52
+ BOOLEAN: 'BOOLEAN',
53
+ LIST: 'LIST',
54
+ COLOUR: 'COLOUR',
55
+ LABEL: 'LABEL',
56
+ ENTITIES: 'ENTITIES',
57
+ };
58
+
59
+ /**
60
+ * Canonical property name constants.
61
+ * Use these instead of raw strings when calling getProperty() / setProperty().
62
+ */
63
+ static Names = Object.freeze({
64
+ // ── Base entity (Entity class) ────────────────────────────────────────────
65
+ HANDLE: 'handle',
66
+ LAYER: 'layer',
67
+ LINETYPE: 'lineType',
68
+ LINEWIDTH: 'lineWidth',
69
+ COLOUR: 'colour',
70
+ POINTS: 'points',
71
+ // ── Shared (multiple entities) ────────────────────────────────────────────
72
+ ANGLE: 'angle',
73
+ BLOCKNAME: 'blockName',
74
+ DIRECTION: 'direction',
75
+ HEIGHT: 'height',
76
+ RADIUS: 'radius',
77
+ ROTATION: 'rotation',
78
+ SCALE: 'scale',
79
+ STRING: 'string',
80
+ STYLENAME: 'styleName',
81
+ // ── BasePolyline ──────────────────────────────────────────────────────────
82
+ CLOSED: 'closed',
83
+ // ── Hatch ─────────────────────────────────────────────────────────────────
84
+ CHILDENTITIES: 'childEntities',
85
+ PATTERNNAME: 'patternName',
86
+ // ── Text ──────────────────────────────────────────────────────────────────
87
+ BACKWARDS: 'backwards',
88
+ HORIZONTALALIGNMENT: 'horizontalAlignment',
89
+ UPSIDEDOWN: 'upsideDown',
90
+ VERTICALALIGNMENT: 'verticalAlignment',
91
+ // ── ArcAlignedText ────────────────────────────────────────────────────────
92
+ ARCSIDE: 'arcSide',
93
+ BOLD: 'bold',
94
+ CHARACTERSPACING: 'characterSpacing',
95
+ FONTNAME: 'fontName',
96
+ ITALIC: 'italic',
97
+ OFFSETFROMARC: 'offsetFromArc',
98
+ OFFSETFROMLEFT: 'offsetFromLeft',
99
+ OFFSETFROMRIGHT: 'offsetFromRight',
100
+ TEXTALIGNMENT: 'textAlignment',
101
+ TEXTORIENTATION: 'textOrientation',
102
+ TEXTREVERSED: 'textReversed',
103
+ UNDERLINE: 'underline',
104
+ WIDTHFACTOR: 'widthFactor',
105
+ // ── Dimensions ────────────────────────────────────────────────────────────
106
+ DIMENSIONSTYLE: 'dimensionStyle',
107
+ LEADERLENGTH: 'leaderLength',
108
+ LINEARDIMANGLE: 'linearDimAngle',
109
+ TEXTOVERRIDE: 'textOverride',
110
+ });
7
111
 
8
112
  /**
9
113
  * Parse the input values and return a value
@@ -46,11 +46,13 @@ export class PropertyManager {
46
46
  }
47
47
 
48
48
  // check if the item has the selected property
49
- if (!item.hasOwnProperty(property)) {
49
+ if (!item.properties?.has(property)) {
50
50
  continue;
51
51
  }
52
52
 
53
- if (typeof (item[property]) !== typeof (newPropertyValue)) {
53
+ const currentValue = item.getProperty(property);
54
+
55
+ if (typeof (currentValue) !== typeof (newPropertyValue)) {
54
56
  DesignCore.Core.notify(Strings.Error.INPUT);
55
57
  } else {
56
58
  // update the item property
@@ -119,22 +121,36 @@ export class PropertyManager {
119
121
  const propertiesList = [];
120
122
 
121
123
  subset.forEach((el) => {
122
- // get list of keys
123
- Object.keys(el).forEach((key) => {
124
- // check if the key is already in the propertiesList
125
- if (propertiesList.includes(key) === false) {
126
- // check if all subset items contain the key and the key is enumberable
127
- if (subset.every((el) => el.hasOwnProperty(key) && Object.getOwnPropertyDescriptor(el, key).enumerable)) {
128
- propertiesList.push(key);
124
+ if (el.properties) {
125
+ el.properties.list().forEach((key) => {
126
+ if (propertiesList.includes(key) === false) {
127
+ if (subset.every((sub) => sub.properties?.has(key))) {
128
+ propertiesList.push(key);
129
+ }
129
130
  }
130
- }
131
- });
131
+ });
132
+ }
132
133
  });
133
134
 
134
135
  return propertiesList;
135
136
  }
136
137
 
137
138
 
139
+ /**
140
+ * Get the Property descriptor for a named property from a representative selected entity.
141
+ * @param {string} itemType
142
+ * @param {string} property
143
+ * @return {Property|undefined}
144
+ */
145
+ getItemPropertyDefinition(itemType, property) {
146
+ const index = DesignCore.Scene.selectionManager.selectionSet.selectionSet.find((i) => {
147
+ const item = DesignCore.Scene.entities.get(i);
148
+ return (itemType === 'All' || item.type === itemType) && item.properties?.has(property);
149
+ });
150
+ if (index === undefined) return undefined;
151
+ return DesignCore.Scene.entities.get(index).properties.definition(property);
152
+ }
153
+
138
154
  /**
139
155
  * Get a list the property values
140
156
  * @param {string} itemType
@@ -147,8 +163,8 @@ export class PropertyManager {
147
163
  if (DesignCore.Scene.selectionManager.selectionSet.selectionSet.length > 0) {
148
164
  for (let i = 0; i < DesignCore.Scene.selectionManager.selectionSet.selectionSet.length; i++) {
149
165
  if (DesignCore.Scene.entities.get(DesignCore.Scene.selectionManager.selectionSet.selectionSet[i]).type === itemType || itemType === 'All') {
150
- const prop = DesignCore.Scene.entities.get(DesignCore.Scene.selectionManager.selectionSet.selectionSet[i])[property];
151
- propertiesValueList.push(prop);
166
+ const entity = DesignCore.Scene.entities.get(DesignCore.Scene.selectionManager.selectionSet.selectionSet[i]);
167
+ propertiesValueList.push(entity.getProperty(property));
152
168
  }
153
169
  }
154
170
  }
@@ -119,6 +119,8 @@ export class Block extends Entity {
119
119
  const item = DesignCore.Scene.entities.get(index);
120
120
  // remove item from scene
121
121
  const copyofitem = Utils.cloneObject(item);
122
+ // assign a fresh handle — the block item is a new entity distinct from the scene entity
123
+ copyofitem.setProperty('handle', DesignCore.HandleManager.next());
122
124
  // adjust the items points to reflect the insert point
123
125
  const delta = new Point(-insertPoint.x, -insertPoint.y);
124
126
  if (item.hasOwnProperty('points')) {
@@ -161,9 +163,9 @@ export class Block extends Entity {
161
163
  */
162
164
  dxf(file) {
163
165
  file.writeGroupCode('0', 'BLOCK');
164
- file.writeGroupCode('5', this.handle, DXFFile.Version.R2000); // Handle
166
+ file.writeGroupCode('5', this.getProperty('handle'), DXFFile.Version.R2000); // Handle
165
167
  file.writeGroupCode('100', 'AcDbEntity', DXFFile.Version.R2000);
166
- file.writeGroupCode('8', this.layer);
168
+ file.writeGroupCode('8', this.getProperty('layer'));
167
169
  file.writeGroupCode('100', 'AcDbBlockBegin', DXFFile.Version.R2000);
168
170
  file.writeGroupCode('2', this.name);
169
171
  file.writeGroupCode('70', this.flags.getFlagValue());
@@ -196,41 +198,6 @@ export class Block extends Entity {
196
198
  this.items.push(item);
197
199
  }
198
200
 
199
- /**
200
- * Draw the entity
201
- * @param {Object} ctx - context
202
- * @param {number} scale
203
- * @param {Object} insert - insert entity
204
- */
205
- draw(ctx, scale, insert = undefined) {
206
- if (!this.items.length) {
207
- // nothing to draw
208
- return;
209
- }
210
-
211
- this.items.forEach((item) => {
212
- ctx.save();
213
- // Use the current item and block insert to set the context
214
- // insert required for colour ByBlock
215
- DesignCore.Canvas.setContext(item, ctx, insert);
216
-
217
- item.draw(ctx, scale);
218
- ctx.restore();
219
- });
220
-
221
- /*
222
- //////////////////////////////////////////
223
- // draw test point for location
224
- ctx.strokeStyle = Colours.rgbToString(colour);
225
- ctx.lineWidth = 1 / scale;
226
- ctx.beginPath()
227
- ctx.moveTo(this.points[0].x, this.points[0].y);
228
- ctx.arc(this.points[0].x, this.points[0].y, 5 / scale, radians2degrees(0), radians2degrees(360), false);
229
- ctx.stroke();
230
- //////////////////////////////////////////
231
- */
232
- }
233
-
234
201
  /**
235
202
  * Get snap points
236
203
  * @param {Point} mousePoint
@@ -20,7 +20,7 @@ export class EndBlock extends Entity {
20
20
  */
21
21
  dxf(file) {
22
22
  file.writeGroupCode('0', 'ENDBLK');
23
- file.writeGroupCode('5', this.handle, DXFFile.Version.R2000); // Handle
23
+ file.writeGroupCode('5', this.getProperty('handle'), DXFFile.Version.R2000); // Handle
24
24
  file.writeGroupCode('100', 'AcDbEntity', DXFFile.Version.R2000);
25
25
  // file.writeGroupCode('8', this.layer);
26
26
  file.writeGroupCode('100', 'AcDbBlockEnd', DXFFile.Version.R2000);
@@ -166,6 +166,14 @@ export class Layer {
166
166
  return false;
167
167
  }
168
168
 
169
+ /**
170
+ * Determine if layer is plottable (visible and plotting flag set)
171
+ * @return {boolean}
172
+ */
173
+ get isPlottable() {
174
+ return this.isVisible && this.plotting;
175
+ }
176
+
169
177
  /**
170
178
  * Determine if layer is selectable
171
179
  * @return {boolean}
@@ -40,7 +40,7 @@ export class StyleManagerBase extends TableManagerBase {
40
40
  const selectionSet = [];
41
41
 
42
42
  for (let i = 0; i < DesignCore.Scene.entities.count(); i++) {
43
- if (DesignCore.Scene.entities.get(i)[this.itemProperty] === style) {
43
+ if ((DesignCore.Scene.entities.get(i).getProperty?.(this.itemProperty) ?? DesignCore.Scene.entities.get(i)[this.itemProperty]) === style) {
44
44
  selectionSet.push(i);
45
45
  }
46
46
  }
@@ -79,7 +79,8 @@ export class StyleManagerBase extends TableManagerBase {
79
79
  this.addStandardItems();
80
80
 
81
81
  for (let i = 0; i < DesignCore.Scene.entities.count(); i++) {
82
- const style = (DesignCore.Scene.entities.get(i)[this.itemProperty]);
82
+ const entity = DesignCore.Scene.entities.get(i);
83
+ const style = entity.getProperty?.(this.itemProperty) ?? entity[this.itemProperty];
83
84
  this.addItem({
84
85
  'name': style,
85
86
  });
@@ -122,10 +122,15 @@ export class TableManagerBase {
122
122
  return this.getItemByName(newItemName);
123
123
  }
124
124
 
125
- if (newItem.handle === undefined) {
126
- newItem.handle = DesignCore.HandleManager.next();
125
+ const existingHandle = newItem.getProperty?.('handle') ?? newItem.handle;
126
+ if (existingHandle === undefined) {
127
+ if (newItem.setProperty) {
128
+ newItem.setProperty('handle', DesignCore.HandleManager.next());
129
+ } else {
130
+ newItem.handle = DesignCore.HandleManager.next();
131
+ }
127
132
  } else {
128
- DesignCore.HandleManager.checkHandle(newItem.handle);
133
+ DesignCore.HandleManager.checkHandle(existingHandle);
129
134
  }
130
135
 
131
136
  if (!exists) {