@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.
- package/.eslintrc.json +14 -7
- package/.github/workflows/DesignCoreQA.yml +3 -1
- package/README.md +11 -4
- package/TODO.md +19 -0
- package/core/core/core.js +49 -42
- package/core/designCore.js +61 -2
- package/core/dimensions/alignedDimension.js +142 -0
- package/core/dimensions/angularDimension.js +550 -0
- package/core/dimensions/baseCircularDimension.js +244 -0
- package/core/dimensions/baseDimension.js +558 -0
- package/core/dimensions/baseLinearDimension.js +371 -0
- package/core/dimensions/diametricDimension.js +82 -0
- package/core/dimensions/dimension.js +298 -0
- package/core/dimensions/linearDimension.js +29 -0
- package/core/dimensions/radialDimension.js +78 -0
- package/core/dimensions/rotatedDimension.js +167 -0
- package/core/entities/arc.js +314 -60
- package/core/entities/arctext.js +642 -0
- package/core/entities/basePolyline.js +553 -94
- package/core/entities/circle.js +181 -54
- package/core/entities/entity.js +154 -74
- package/core/entities/hatch.js +807 -0
- package/core/entities/insert.js +200 -39
- package/core/entities/line.js +196 -134
- package/core/entities/lwpolyline.js +21 -44
- package/core/entities/point.js +194 -29
- package/core/entities/polyline.js +54 -7
- package/core/entities/rectangle.js +43 -13
- package/core/entities/solid.js +43 -6
- package/core/entities/text.js +324 -181
- package/core/lib/boundingBox.js +73 -13
- package/core/lib/canvas.js +256 -65
- package/core/lib/clipboard.js +198 -0
- package/core/lib/colour.js +144 -0
- package/core/lib/colours.js +367 -301
- package/core/lib/commandLine.js +16 -28
- package/core/lib/commandManager.js +115 -58
- package/core/lib/constants.js +23 -0
- package/core/lib/dxf/dxf.js +249 -34
- package/core/lib/dxf/dxfFile.js +23 -38
- package/core/lib/dxf/dxfIterator.js +17 -5
- package/core/lib/dxf/dxfRead.js +23 -7
- package/core/lib/dxf/dxfWrite.js +39 -129
- package/core/lib/dxf/handleManager.js +97 -0
- package/core/lib/dxf/sections/blocks.js +16 -2
- package/core/lib/dxf/sections/entities.js +17 -3
- package/core/lib/dxf/sections/header.js +10 -1
- package/core/lib/dxf/sections/objects.js +36 -0
- package/core/lib/dxf/sections/section.js +43 -5
- package/core/lib/dxf/sections/tables.js +17 -3
- package/core/lib/entityManager.js +178 -0
- package/core/lib/fileio.js +14 -4
- package/core/lib/headers.js +229 -0
- package/core/lib/input.js +46 -0
- package/core/lib/inputManager.js +199 -194
- package/core/lib/intersect.js +341 -468
- package/core/lib/logging.js +25 -1
- package/core/lib/matrix.js +6 -8
- package/core/lib/mouse.js +24 -79
- package/core/lib/patterns.js +690 -0
- package/core/lib/promptOptions.js +133 -0
- package/core/lib/scene.js +71 -174
- package/core/lib/selectionManager.js +74 -48
- package/core/lib/selectionWindow.js +24 -10
- package/core/lib/settings.js +18 -11
- package/core/lib/snapping.js +129 -21
- package/core/lib/stateManager.js +288 -0
- package/core/lib/strings.js +57 -5
- package/core/lib/utils.js +95 -11
- package/core/objects/dictionary.js +43 -0
- package/core/objects/dictionaryManager.js +80 -0
- package/core/properties/dimType.js +117 -0
- package/core/properties/flags.js +66 -0
- package/core/properties/property.js +26 -0
- package/core/properties/propertyManager.js +65 -19
- package/core/tables/appId.js +31 -0
- package/core/tables/appIdManager.js +44 -0
- package/core/tables/block.js +323 -0
- package/core/tables/blockManager.js +48 -0
- package/core/tables/blockRecord.js +29 -0
- package/core/tables/blockRecordManager.js +37 -0
- package/core/tables/dimStyle.js +627 -0
- package/core/tables/dimStyleManager.js +187 -0
- package/core/tables/endblk.js +28 -0
- package/core/tables/layer.js +219 -0
- package/core/tables/layerManager.js +47 -0
- package/core/tables/ltype.js +85 -0
- package/core/tables/ltypeManager.js +65 -0
- package/core/tables/style.js +145 -0
- package/core/tables/styleManager.js +55 -0
- package/core/tables/styleManagerBase.js +110 -0
- package/core/tables/tableManagerBase.js +354 -0
- package/core/tables/ucs.js +28 -0
- package/core/tables/ucsManager.js +44 -0
- package/core/tables/view.js +28 -0
- package/core/tables/viewManager.js +44 -0
- package/core/tables/vport.js +70 -0
- package/core/tables/vportManager.js +65 -0
- package/core/tools/chamfer.js +343 -0
- package/core/tools/chamferFilletBase.js +162 -0
- package/core/tools/copy.js +68 -40
- package/core/tools/copybase.js +65 -0
- package/core/tools/copyclip.js +59 -0
- package/core/tools/cornerEntity.js +138 -0
- package/core/tools/cutclip.js +72 -0
- package/core/tools/distance.js +37 -9
- package/core/tools/erase.js +43 -13
- package/core/tools/explode.js +117 -0
- package/core/tools/extend.js +71 -32
- package/core/tools/fillet.js +336 -0
- package/core/tools/identify.js +33 -6
- package/core/tools/matchProp.js +106 -0
- package/core/tools/mirror.js +141 -0
- package/core/tools/move.js +65 -32
- package/core/tools/offset.js +321 -0
- package/core/tools/pan.js +54 -0
- package/core/tools/pasteclip.js +93 -0
- package/core/tools/purge.js +90 -0
- package/core/tools/rotate.js +105 -70
- package/core/tools/scale.js +166 -0
- package/core/tools/tool.js +14 -4
- package/core/tools/trim.js +71 -32
- package/core/tools/zoom.js +152 -0
- package/package.json +3 -305
- package/test/dimensions/alignedDimension.test.js +211 -0
- package/test/dimensions/angularDimension.test.js +243 -0
- package/test/dimensions/baseDimension.test.js +239 -0
- package/test/dimensions/diametricDimension.test.js +203 -0
- package/test/dimensions/dimension.test.js +102 -0
- package/test/dimensions/radialDimension.test.js +203 -0
- package/test/dimensions/rotatedDimension.test.js +224 -0
- package/test/entities/arc.test.js +463 -41
- package/test/entities/arctext.test.js +329 -0
- package/test/entities/basePolyline.test.js +459 -10
- package/test/entities/circle.test.js +312 -7
- package/test/entities/entity.test.js +28 -9
- package/test/entities/hatch.test.js +606 -0
- package/test/entities/insert.test.js +90 -0
- package/test/entities/line.test.js +182 -6
- package/test/entities/point.test.js +375 -37
- package/test/entities/solid.test.js +156 -0
- package/test/entities/text.test.js +533 -7
- package/test/integration/exportIntegration.reference.dxf +1706 -0
- package/test/integration/exportIntegration.test.js +186 -0
- package/test/integration/importExportIntegration.test.js +126 -0
- package/test/integration/intersectionIntegration.test.js +880 -0
- package/test/integration/userInputIntegration.reference.dxf +1410 -0
- package/test/integration/userInputIntegration.test.js +269 -0
- package/test/lib/boundingBox.test.js +26 -2
- package/test/lib/canvas.test.js +613 -5
- package/test/lib/clipboard.test.js +70 -0
- package/test/lib/colour.test.js +114 -0
- package/test/lib/colours.test.js +63 -18
- package/test/lib/commandLine.test.js +159 -22
- package/test/lib/commandManager.test.js +4 -4
- package/test/lib/constants.test.js +9 -0
- package/test/lib/dxf/dxfFile.test.js +6 -2
- package/test/lib/dxf/dxfIterator.test.js +1 -1
- package/test/lib/dxf/dxfWrite.test.js +173 -0
- package/test/lib/dxf/handleManager.test.js +137 -0
- package/test/lib/dxf/sections/blocks.test.js +5 -5
- package/test/lib/dxf/sections/entities.test.js +4 -4
- package/test/lib/dxf/sections/header.test.js +18 -4
- package/test/lib/dxf/sections/objects.test.js +132 -0
- package/test/lib/dxf/sections/section.test.js +29 -29
- package/test/lib/dxf/sections/tables.test.js +3 -3
- package/test/lib/entityManager.test.js +163 -0
- package/test/lib/headers.test.js +233 -0
- package/test/lib/inputManager.test.js +1144 -5
- package/test/lib/intersect.test.js +454 -331
- package/test/lib/logging.test.js +1 -1
- package/test/lib/matrix.test.js +1 -1
- package/test/lib/mouse.test.js +327 -4
- package/test/lib/patterns.test.js +105 -0
- package/test/lib/promptOptions.test.js +179 -0
- package/test/lib/scene.test.js +16 -52
- package/test/lib/selectionManager.test.js +17 -5
- package/test/lib/settings.test.js +111 -5
- package/test/lib/snapping.test.js +142 -4
- package/test/lib/stateManager.test.js +155 -0
- package/test/lib/utils.test.js +81 -1
- package/test/objects/dictionary.test.js +81 -0
- package/test/objects/dictionaryManager.test.js +34 -0
- package/test/properties/dimType.test.js +62 -0
- package/test/properties/flags.test.js +164 -0
- package/test/properties/property.test.js +13 -0
- package/test/properties/propertyManager.test.js +10 -11
- package/test/tables/appId.test.js +46 -0
- package/test/tables/appIdManager.test.js +72 -0
- package/test/tables/block.test.js +157 -0
- package/test/tables/blockManager.test.js +52 -0
- package/test/tables/blockRecord.test.js +39 -0
- package/test/tables/blockRecordManager.test.js +60 -0
- package/test/tables/dimstyle.test.js +72 -0
- package/test/tables/endblk.test.js +30 -0
- package/test/tables/layer.test.js +95 -0
- package/test/tables/layerManager.test.js +172 -0
- package/test/{styles → tables}/ltype.test.js +7 -7
- package/test/{styles → tables}/ltypeManager.test.js +2 -2
- package/test/tables/style.test.js +44 -0
- package/test/tables/stylemanagerbase.test.js +220 -0
- package/test/tables/tableManagerBase.test.js +194 -0
- package/test/tables/ucs.test.js +37 -0
- package/test/tables/ucsManager.test.js +48 -0
- package/test/tables/view.test.js +37 -0
- package/test/tables/viewManager.test.js +48 -0
- package/test/tables/vport.test.js +117 -0
- package/test/tables/vportManager.test.js +54 -0
- package/test/test-helpers/test-helpers.js +66 -0
- package/test/tools/chamfer.test.js +970 -0
- package/test/tools/chamferFilletBase.test.js +446 -0
- package/test/tools/copy.test.js +22 -14
- package/test/tools/copybase.test.js +93 -0
- package/test/tools/copyclip.test.js +92 -0
- package/test/tools/cornerEntity.test.js +201 -0
- package/test/tools/cutclip.test.js +94 -0
- package/test/tools/distance.test.js +140 -18
- package/test/tools/erase.test.js +12 -12
- package/test/tools/explode.test.js +45 -0
- package/test/tools/extend.test.js +326 -44
- package/test/tools/fillet.test.js +831 -0
- package/test/tools/identify.test.js +115 -16
- package/test/tools/matchProp.test.js +84 -0
- package/test/tools/mirror.test.js +475 -0
- package/test/tools/move.test.js +75 -19
- package/test/tools/offset.test.js +748 -0
- package/test/tools/pan.test.js +72 -0
- package/test/tools/pasteclip.test.js +100 -0
- package/test/tools/purge.test.js +89 -0
- package/test/tools/rotate.test.js +89 -23
- package/test/tools/scale.test.js +529 -0
- package/test/tools/trim.test.js +419 -84
- package/test/tools/zoom.test.js +504 -0
- package/core/entities/alignedDimension.js +0 -279
- package/core/entities/angularDimension.js +0 -194
- package/core/entities/baseDimension.js +0 -227
- package/core/entities/block.js +0 -247
- package/core/entities/diametricDimension.js +0 -133
- package/core/entities/dimension.js +0 -202
- package/core/entities/radialDimension.js +0 -126
- package/core/styles/dimStyle.js +0 -719
- package/core/styles/dimStyleManager.js +0 -47
- package/core/styles/layer.js +0 -133
- package/core/styles/layerManager.js +0 -40
- package/core/styles/ltype.js +0 -86
- package/core/styles/ltypeManager.js +0 -50
- package/core/styles/style.js +0 -158
- package/core/styles/styleManager.js +0 -43
- package/core/styles/styleManagerBase.js +0 -299
- package/test/entities/block.test.js +0 -22
- package/test/styles/dimstyle.test.js +0 -19
- package/test/styles/layerManager.test.js +0 -150
- package/test/styles/style.test.js +0 -33
- package/test/styles/stylemanagerbase.test.js +0 -219
|
@@ -0,0 +1,807 @@
|
|
|
1
|
+
import { Entity } from './entity.js';
|
|
2
|
+
import { DXFFile } from '../lib/dxf/dxfFile.js';
|
|
3
|
+
import { BoundingBox } from '../lib/boundingBox.js';
|
|
4
|
+
|
|
5
|
+
import { Strings } from '../lib/strings.js';
|
|
6
|
+
import { Input, PromptOptions } from '../lib/inputManager.js';
|
|
7
|
+
import { Logging } from '../lib/logging.js';
|
|
8
|
+
|
|
9
|
+
import { Point } from './point.js';
|
|
10
|
+
import { Flags } from '../properties/flags.js';
|
|
11
|
+
|
|
12
|
+
import { Utils } from '../lib/utils.js';
|
|
13
|
+
import { Patterns } from '../lib/patterns.js';
|
|
14
|
+
import { Intersection } from '../lib/intersect.js';
|
|
15
|
+
|
|
16
|
+
import { DesignCore } from '../designCore.js';
|
|
17
|
+
|
|
18
|
+
import { Line } from './line.js';
|
|
19
|
+
import { Arc } from './arc.js';
|
|
20
|
+
import { Polyline } from './polyline.js';
|
|
21
|
+
import { Property } from '../properties/property.js';
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Hatch Entity Class
|
|
25
|
+
* @extends Entity
|
|
26
|
+
*/
|
|
27
|
+
export class Hatch extends Entity {
|
|
28
|
+
/**
|
|
29
|
+
* Create a Hatch Entity
|
|
30
|
+
* @param {Array} data
|
|
31
|
+
*/
|
|
32
|
+
constructor(data) {
|
|
33
|
+
super(data);
|
|
34
|
+
|
|
35
|
+
// store the boundary shapes
|
|
36
|
+
Object.defineProperty(this, 'childEntities', {
|
|
37
|
+
value: [],
|
|
38
|
+
writable: true,
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
Object.defineProperty(this, 'pattern', {
|
|
42
|
+
value: 'ANSI31',
|
|
43
|
+
writable: true,
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
Object.defineProperty(this, 'solid', {
|
|
47
|
+
value: false,
|
|
48
|
+
writable: true,
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
Object.defineProperty(this, 'patternName', {
|
|
52
|
+
// value: 'ANSI31',
|
|
53
|
+
// writable: true,
|
|
54
|
+
enumerable: true,
|
|
55
|
+
get: this.getPatternName,
|
|
56
|
+
set: this.setPatternName,
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
Object.defineProperty(this, 'angle', {
|
|
60
|
+
value: 0,
|
|
61
|
+
writable: true,
|
|
62
|
+
enumerable: true,
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
Object.defineProperty(this, 'scale', {
|
|
66
|
+
value: 1,
|
|
67
|
+
writable: true,
|
|
68
|
+
enumerable: true,
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
// hide inherited properties
|
|
72
|
+
// needs to be enumerable=false to not appear in the object props
|
|
73
|
+
Object.defineProperty(this, 'lineType', {
|
|
74
|
+
enumerable: false,
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
Object.defineProperty(this, 'lineWidth', {
|
|
78
|
+
enumerable: false,
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
// add a single point to this.points if no other points exist
|
|
82
|
+
if (!this.points.length) {
|
|
83
|
+
this.points.push(new Point());
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// DXF Groupcode 70 - Solid Fill Flag (1 = solid, 0 = pattern)
|
|
87
|
+
this.solid = Boolean(Property.loadValue([data?.solid, data?.[70]], 0));
|
|
88
|
+
// DXF Groupcode 2 - Hatch pattern name
|
|
89
|
+
this.patternName = Property.loadValue([data?.patternName, data?.[2]], 'ANSI31');
|
|
90
|
+
// DXF Groupcode 41 - Hatch pattern scale
|
|
91
|
+
this.scale = Property.loadValue([data?.scale, data?.[41]], 1);
|
|
92
|
+
// DXF Groupcode 52 - Hatch pattern angle
|
|
93
|
+
this.angle = Property.loadValue([data?.angle, data?.[52]], 0);
|
|
94
|
+
|
|
95
|
+
if (data) {
|
|
96
|
+
if (data.hasOwnProperty('childEntities')) {
|
|
97
|
+
if (Array.isArray(data.childEntities)) {
|
|
98
|
+
this.childEntities = data.childEntities;
|
|
99
|
+
}
|
|
100
|
+
} else {
|
|
101
|
+
const shapes = this.processBoundaryData(data);
|
|
102
|
+
if (shapes.length) {
|
|
103
|
+
this.childEntities = shapes;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// ensure first point is 0,0 and last point is 1,1
|
|
109
|
+
if (this.points.at(0).x !== 0 || this.points.at(0).y !== 0) {
|
|
110
|
+
this.points[0] = new Point(0, 0);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
if (this.points.length < 2 || (this.points.at(-1).x !== 1 || this.points.at(-1).y !== 1)) {
|
|
114
|
+
this.points.push(new Point(1, 1));
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Get the hatch pattern name
|
|
120
|
+
* @return {string}
|
|
121
|
+
*/
|
|
122
|
+
getPatternName() {
|
|
123
|
+
return this.pattern;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Set the hatch pattern name
|
|
128
|
+
* @param {string} name
|
|
129
|
+
*/
|
|
130
|
+
setPatternName(name) {
|
|
131
|
+
this.pattern = name.toUpperCase();
|
|
132
|
+
this.solid = this.pattern === 'SOLID';
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Process the dxf data, creating hatch boundaries
|
|
137
|
+
* @param {Array} data
|
|
138
|
+
* @return {Array} - Array of boundary items
|
|
139
|
+
*/
|
|
140
|
+
processBoundaryData(data) {
|
|
141
|
+
if (!data.hasOwnProperty('points')) {
|
|
142
|
+
return [];
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
const childEntities = [];
|
|
146
|
+
|
|
147
|
+
// copy this.points and remove first point
|
|
148
|
+
// First and last points define the hatch, not the hatch boundary
|
|
149
|
+
// Some CAD systems don't include the last point so leave it in place
|
|
150
|
+
const points = data.points.slice(1);
|
|
151
|
+
|
|
152
|
+
// Process the boundary paths into objects
|
|
153
|
+
if (data.hasOwnProperty('91')) {
|
|
154
|
+
// DXF Groupcode 91 - Number of boundary paths (loops)
|
|
155
|
+
const boundaryPathCount = data[91];
|
|
156
|
+
// 92 - Boundary path type flag (bit coded): 0 = Default; 1 = External; 2 = Polyline 4 = Derived; 8 = Textbox; 16 = Outermost
|
|
157
|
+
const boundaryPathType = this.getDataValue(data, 92);
|
|
158
|
+
|
|
159
|
+
for (let i = 0; i < boundaryPathCount; i++) {
|
|
160
|
+
// check if boundary path is polyline
|
|
161
|
+
const boundaryPathTypeFlag = new Flags();
|
|
162
|
+
boundaryPathTypeFlag.setFlagValue(boundaryPathType);
|
|
163
|
+
const isPolyline = boundaryPathTypeFlag.hasFlag(2);
|
|
164
|
+
|
|
165
|
+
if (data.hasOwnProperty('93')) {
|
|
166
|
+
// DXF Groupcode 93 - Number of edges in this boundary path (only if boundary is not a polyline)
|
|
167
|
+
// const edgeCountData = data[93];
|
|
168
|
+
const edgeCount = this.getDataValue(data, 93);
|
|
169
|
+
|
|
170
|
+
const shape = new Polyline();
|
|
171
|
+
|
|
172
|
+
for (let edgeNum = 0; edgeNum < edgeCount; edgeNum++) {
|
|
173
|
+
if (data.hasOwnProperty('72')) {
|
|
174
|
+
// DXF Groupcode 72 - Edge type (only if boundary is not a polyline):
|
|
175
|
+
// 1 = Line; 2 = Circular arc; 3 = Elliptic arc; 4 = Spline
|
|
176
|
+
// const edgeTypeData = data[72];
|
|
177
|
+
const edgeType = this.getDataValue(data, 72);
|
|
178
|
+
|
|
179
|
+
if (isPolyline) {
|
|
180
|
+
// Polyline
|
|
181
|
+
// 72 - Has Bulge Flag; 73 - Is Closed Flag; 93 - Number of vertices; 10 - X; 20 - Y; 42 - Bulge
|
|
182
|
+
const polyPoint = points.shift();
|
|
183
|
+
if (polyPoint.sequence !== undefined && polyPoint.sequence !== 10) {
|
|
184
|
+
const msg = 'Invalid point sequence';
|
|
185
|
+
const err = (`${this.type} - ${msg}`);
|
|
186
|
+
throw Error(err);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
shape.points.push(polyPoint);
|
|
190
|
+
} else if (edgeType === 1) {
|
|
191
|
+
// Line
|
|
192
|
+
// 10 - X; 20 - Y; 11 - X; 21 - Y
|
|
193
|
+
const shapePoints = [];
|
|
194
|
+
const startPoint = points.shift();
|
|
195
|
+
if (startPoint.sequence !== undefined && startPoint.sequence !== 10) {
|
|
196
|
+
const msg = 'Invalid point sequence';
|
|
197
|
+
const err = (`${this.type} - ${msg}`);
|
|
198
|
+
throw Error(err);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
const endPoint = points.shift();
|
|
202
|
+
if (endPoint.sequence !== undefined && endPoint.sequence !== 11) {
|
|
203
|
+
const msg = 'Invalid point sequence';
|
|
204
|
+
const err = (`${this.type} - ${msg}`);
|
|
205
|
+
throw Error(err);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
shapePoints.push(startPoint);
|
|
209
|
+
shapePoints.push(endPoint);
|
|
210
|
+
const line = new Line({ points: shapePoints });
|
|
211
|
+
shape.points.push(...line.toPolylinePoints());
|
|
212
|
+
} else if (edgeType === 2) {
|
|
213
|
+
// ARC
|
|
214
|
+
// 10 - X; 20 - Y; 40 - Radius; 50 - Start Angle; 51 - End Angle; 73 - Is Counter Clockwise
|
|
215
|
+
const shapeData = { points: [] };
|
|
216
|
+
|
|
217
|
+
const centerPoint = points.shift();
|
|
218
|
+
if (centerPoint.sequence !== undefined && centerPoint.sequence !== 10) {
|
|
219
|
+
const msg = 'Invalid point sequence';
|
|
220
|
+
const err = (`${this.type} - ${msg}`);
|
|
221
|
+
throw Error(err);
|
|
222
|
+
}
|
|
223
|
+
shapeData.points.push(centerPoint);
|
|
224
|
+
shapeData[40] = this.getDataValue(data, 40);
|
|
225
|
+
shapeData.startAngle = this.getDataValue(data, 50);
|
|
226
|
+
shapeData.endAngle = this.getDataValue(data, 51);
|
|
227
|
+
// arc direction: - ccw > 0, cw <= 0 default 1
|
|
228
|
+
shapeData.direction = this.getDataValue(data, 73);
|
|
229
|
+
|
|
230
|
+
if (shapeData.direction === 0) {
|
|
231
|
+
const circle = 360;
|
|
232
|
+
shapeData.startAngle = circle - shapeData.startAngle;
|
|
233
|
+
shapeData.endAngle = circle - shapeData.endAngle;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
const arc = new Arc(shapeData);
|
|
237
|
+
shape.points.push(...arc.toPolylinePoints());
|
|
238
|
+
} else if (edge.edgeType === 3) {
|
|
239
|
+
// Ellipse
|
|
240
|
+
// 10 - X; 20 - Y; 11 - X; 21 - Y; 40 - Length of minor axis (percentage of major axis length);
|
|
241
|
+
// 50 - Start angle; 51 - End angle; 73 - Is counterclockwise flag
|
|
242
|
+
const msg = 'Ellipse not supported';
|
|
243
|
+
const err = (`${this.type} - ${msg}`);
|
|
244
|
+
throw Error(err);
|
|
245
|
+
} else if (edgeType === 4) {
|
|
246
|
+
// Spline
|
|
247
|
+
// 94 - Degree; 73 - Rational; 74 - Periodic; 95 - Number of knots; 96 - Number of control points; 40 - Knot values (multiple entries)
|
|
248
|
+
// 10 - X of Control Point; 20 - Y of Control Point; 42 - Weights (optional, default = 1); 97 - Number of fit data
|
|
249
|
+
// 11 - X of Fit datum; 21 - Y of Fit datum; 12 - X of Start Tangent; 22 - Y of Start Tangent
|
|
250
|
+
// 13 - X of End tangent; 23 - Y of End tangent
|
|
251
|
+
const msg = 'Spline not supported';
|
|
252
|
+
const err = (`${this.type} - ${msg}`);
|
|
253
|
+
throw Error(err);
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
childEntities.push(shape);
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
return childEntities;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
|
|
267
|
+
/**
|
|
268
|
+
* Get value from incoming data
|
|
269
|
+
* handle arrays and single values
|
|
270
|
+
* @param {any} data
|
|
271
|
+
* @param {number} dxfCode
|
|
272
|
+
* @return {any}
|
|
273
|
+
*/
|
|
274
|
+
getDataValue(data, dxfCode) {
|
|
275
|
+
let value;
|
|
276
|
+
if (Array.isArray(data[dxfCode])) {
|
|
277
|
+
value = data[dxfCode].shift();
|
|
278
|
+
} else {
|
|
279
|
+
value = data[dxfCode];
|
|
280
|
+
}
|
|
281
|
+
return value;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
/**
|
|
285
|
+
* Register the command
|
|
286
|
+
* @return {Object}
|
|
287
|
+
* command = name of the command
|
|
288
|
+
* shortcut = shortcut for the command
|
|
289
|
+
* type = type to group command in toolbars (omitted if not shown)
|
|
290
|
+
*/
|
|
291
|
+
static register() {
|
|
292
|
+
const command = { command: 'Hatch', shortcut: 'H' };
|
|
293
|
+
return command;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
/**
|
|
297
|
+
* Execute method
|
|
298
|
+
* executes the workflow, requesting input required to create an entity
|
|
299
|
+
*/
|
|
300
|
+
async execute() {
|
|
301
|
+
try {
|
|
302
|
+
const op = new PromptOptions(Strings.Input.SELECTIONSET, [Input.Type.SELECTIONSET]);
|
|
303
|
+
let validBoundary = false;
|
|
304
|
+
|
|
305
|
+
while (!validBoundary) {
|
|
306
|
+
const result = await DesignCore.Scene.inputManager.requestInput(op);
|
|
307
|
+
if (result === undefined) return;
|
|
308
|
+
|
|
309
|
+
const selectedItems = DesignCore.Scene.selectionManager.selectedItems.slice(0);
|
|
310
|
+
const boundary = this.processSelection(selectedItems);
|
|
311
|
+
|
|
312
|
+
if (boundary.length) {
|
|
313
|
+
this.childEntities = boundary;
|
|
314
|
+
validBoundary = true;
|
|
315
|
+
} else {
|
|
316
|
+
// reset selection
|
|
317
|
+
DesignCore.Scene.selectionManager.reset();
|
|
318
|
+
const msg = `${this.type} - ${Strings.Error.SELECTION}`;
|
|
319
|
+
DesignCore.Core.notify(msg);
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
DesignCore.Scene.inputManager.executeCommand(this);
|
|
324
|
+
} catch (err) {
|
|
325
|
+
Logging.instance.error(`${this.type} - ${err}`);
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
/**
|
|
330
|
+
* Preview the entity during creation
|
|
331
|
+
*/
|
|
332
|
+
preview() {
|
|
333
|
+
const selectedItems = DesignCore.Scene.selectionManager.selectedItems.slice(0);
|
|
334
|
+
const shapes = this.processSelection(selectedItems);
|
|
335
|
+
if (shapes.length) {
|
|
336
|
+
DesignCore.Scene.tempEntities.create(this.type, { points: this.points, childEntities: shapes });
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
/**
|
|
341
|
+
* Convert the selection to boundary items
|
|
342
|
+
* @param {Array} selectedItems
|
|
343
|
+
* @return {Array} - Array of boundary items
|
|
344
|
+
*/
|
|
345
|
+
processSelection(selectedItems) {
|
|
346
|
+
const selectedchildEntities = [];
|
|
347
|
+
|
|
348
|
+
let iterationPoints = [];
|
|
349
|
+
|
|
350
|
+
// track the last index count to make sure the loop is closed
|
|
351
|
+
let lastIndexCount = selectedItems.length + 1;
|
|
352
|
+
|
|
353
|
+
while (selectedItems.length) {
|
|
354
|
+
if (selectedItems.length === lastIndexCount) {
|
|
355
|
+
return [];
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
lastIndexCount = selectedItems.length;
|
|
359
|
+
|
|
360
|
+
for (let i = 0; i < selectedItems.length; i++) {
|
|
361
|
+
// no points collected - get the first index from selected items
|
|
362
|
+
|
|
363
|
+
const currentItem = selectedItems[i];
|
|
364
|
+
// if the item can't be converted to polyline points, remove from selected items and go again
|
|
365
|
+
if (typeof currentItem.toPolylinePoints === 'undefined' || currentItem.type === 'Text' || currentItem.type === 'ArcAlignedText') {
|
|
366
|
+
selectedItems.splice(i, 1);
|
|
367
|
+
break;
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
let currentPoints = currentItem.toPolylinePoints();
|
|
371
|
+
|
|
372
|
+
// check if the start or end point of the item are connected to the end of the iteration points
|
|
373
|
+
if (!iterationPoints.length || currentPoints.at(0).isSame(iterationPoints.at(-1)) || currentPoints.at(-1).isSame(iterationPoints.at(-1))) {
|
|
374
|
+
// check if the item is reversed
|
|
375
|
+
if (iterationPoints.length && currentPoints.at(-1).isSame(iterationPoints.at(-1))) {
|
|
376
|
+
currentPoints = currentPoints.reverse();
|
|
377
|
+
|
|
378
|
+
// Reverse the bulge for arcs
|
|
379
|
+
const startBulge = currentPoints.at(0).bulge;
|
|
380
|
+
const endBulge = currentPoints.at(-1).bulge;
|
|
381
|
+
|
|
382
|
+
currentPoints.at(0).bulge = endBulge * -1;
|
|
383
|
+
currentPoints.at(-1).bulge = startBulge * -1;
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
// Merge the shared connection point rather than pushing a duplicate.
|
|
387
|
+
// Arc.toPolylinePoints() stores the outgoing arc bulge on its start
|
|
388
|
+
// point; the previous entity left that slot with bulge=0 (destination
|
|
389
|
+
// only). Transfer the bulge and skip the duplicate so that:
|
|
390
|
+
// - arc geometry is preserved exactly
|
|
391
|
+
// - floating-point trig differences (e.g. Math.cos(3π/2) ≈ -1.8e-16
|
|
392
|
+
// instead of 0) cannot produce a spurious near-zero segment
|
|
393
|
+
if (iterationPoints.length && currentPoints[0].isSame(iterationPoints.at(-1))) {
|
|
394
|
+
if (currentPoints[0].bulge !== 0) {
|
|
395
|
+
iterationPoints.at(-1).bulge = currentPoints[0].bulge;
|
|
396
|
+
}
|
|
397
|
+
iterationPoints.push(...currentPoints.slice(1));
|
|
398
|
+
} else {
|
|
399
|
+
iterationPoints.push(...currentPoints);
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
// remove the index from selected items
|
|
403
|
+
selectedItems = selectedItems.filter((index) => index !== selectedItems[i]);
|
|
404
|
+
|
|
405
|
+
if (iterationPoints.at(0).isSame(iterationPoints.at(-1))) {
|
|
406
|
+
const shape = new Polyline();
|
|
407
|
+
// Drop the redundant closure point — the first point already records
|
|
408
|
+
// the same position; keeping it would create a zero-length segment
|
|
409
|
+
shape.points.push(...iterationPoints.slice(0, -1));
|
|
410
|
+
selectedchildEntities.push(shape);
|
|
411
|
+
iterationPoints = [];
|
|
412
|
+
break;
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
return selectedchildEntities;
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
/**
|
|
422
|
+
* Draw the entity
|
|
423
|
+
* @param {Object} ctx - context
|
|
424
|
+
* @param {number} scale
|
|
425
|
+
*/
|
|
426
|
+
draw(ctx, scale) {
|
|
427
|
+
// ensure the scale is valid
|
|
428
|
+
if (this.scale < 0.01) {
|
|
429
|
+
this.scale = 1;
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
if (!this.childEntities.length) return;
|
|
433
|
+
|
|
434
|
+
ctx.save();
|
|
435
|
+
|
|
436
|
+
// Start a new composite path for all boundary loops
|
|
437
|
+
try {
|
|
438
|
+
ctx.beginPath();
|
|
439
|
+
} catch { // Cairo
|
|
440
|
+
ctx.newPath();
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
// Trace all boundary paths as sub-paths in a single composite path
|
|
444
|
+
// Using even-odd fill rule, islands are automatically handled:
|
|
445
|
+
// areas enclosed by an odd number of boundaries are filled,
|
|
446
|
+
// areas enclosed by an even number are left empty
|
|
447
|
+
for (let i = 0; i < this.childEntities.length; i++) {
|
|
448
|
+
const shape = this.childEntities[i];
|
|
449
|
+
if (!shape.points.length) continue;
|
|
450
|
+
|
|
451
|
+
// Start a new sub-path for this boundary
|
|
452
|
+
ctx.moveTo(shape.points[0].x, shape.points[0].y);
|
|
453
|
+
// Trace the boundary path (without stroking)
|
|
454
|
+
shape.draw(ctx, scale, false);
|
|
455
|
+
// Close the sub-path
|
|
456
|
+
ctx.closePath();
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
// Clip using even-odd rule
|
|
460
|
+
try { // Cairo - clip() takes no arguments, uses the fill rule set above
|
|
461
|
+
ctx.setFillRule(1); // Cairo.FillRule.EVEN_ODD
|
|
462
|
+
ctx.clip();
|
|
463
|
+
} catch { // HTML Canvas
|
|
464
|
+
ctx.clip('evenodd');
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
// Start a new path for the pattern/fill area
|
|
468
|
+
try {
|
|
469
|
+
ctx.beginPath();
|
|
470
|
+
} catch { // Cairo
|
|
471
|
+
ctx.newPath();
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
// Create an oversized rectangle covering all boundaries
|
|
475
|
+
const bb = this.boundingBox();
|
|
476
|
+
try {
|
|
477
|
+
ctx.rect(bb.xMin - bb.xLength, bb.yMin - bb.yLength, bb.xLength * 3, bb.yLength * 3);
|
|
478
|
+
} catch { // Cairo
|
|
479
|
+
ctx.rectangle(bb.xMin - bb.xLength, bb.yMin - bb.yLength, bb.xLength * 3, bb.yLength * 3);
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
// Draw the pattern/fill once over the composite clipped region
|
|
483
|
+
this.createPattern(ctx, scale, bb);
|
|
484
|
+
|
|
485
|
+
ctx.restore();
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
/**
|
|
489
|
+
* Draw the hatch pattern to the context
|
|
490
|
+
* @param {Object} ctx
|
|
491
|
+
* @param {number} scale
|
|
492
|
+
* @param {BoundingBox} boundingbox - bounding box of the hatch boundaries, used to size the pattern
|
|
493
|
+
*/
|
|
494
|
+
createPattern(ctx, scale, boundingbox) {
|
|
495
|
+
if (!Patterns.patternExists(this.patternName) || this.solid) {
|
|
496
|
+
ctx.fill();
|
|
497
|
+
return;
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
const boundingBox = boundingbox;
|
|
501
|
+
const centerPoint = boundingBox.centerPoint;
|
|
502
|
+
const bbXLength = boundingBox.xLength;
|
|
503
|
+
const bbYLength = boundingBox.yLength;
|
|
504
|
+
|
|
505
|
+
const pattern = Patterns.getPattern(this.patternName);
|
|
506
|
+
pattern.forEach((patternLine) => {
|
|
507
|
+
// Each pattern line is considered to be the first member of a line family,
|
|
508
|
+
// Patterns are created by applying the delta offsets in both directions to generate an infinite family of parallel lines.
|
|
509
|
+
// originX and originY values are the offsets of the line from the origin and are applied without rotation
|
|
510
|
+
// The delta-x value indicates the displacement between members of the family in the direction of the line. It is used only for dashed lines.
|
|
511
|
+
// The delta-y value indicates the spacing between members of the family; that is, it is measured perpendicular to the lines.
|
|
512
|
+
// A line is considered to be of infinite length.
|
|
513
|
+
// A dash pattern is superimposed on the line.
|
|
514
|
+
|
|
515
|
+
// define dashlength - i.e. the length of each dash summed
|
|
516
|
+
// where there is no dash defined use half the boundingbox xlength
|
|
517
|
+
let dashLength = bbXLength / 2;
|
|
518
|
+
if (patternLine.dashes.length) {
|
|
519
|
+
dashLength = patternLine.getDashLength();
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
// copy dashes to avoid mutating the pattern line
|
|
523
|
+
let dashes = [...patternLine.dashes];
|
|
524
|
+
let dashOffset = 0;
|
|
525
|
+
|
|
526
|
+
if (dashes[0] < 0) {
|
|
527
|
+
// if the first dash is negative move it to the end
|
|
528
|
+
const firstIndex = dashes.shift();
|
|
529
|
+
dashOffset = Math.abs(firstIndex);
|
|
530
|
+
dashes.push(firstIndex);
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
dashes = dashes.map((x) => Math.abs(x) + 0.00001);
|
|
534
|
+
|
|
535
|
+
try {
|
|
536
|
+
ctx.setLineDash(dashes, 0);
|
|
537
|
+
ctx.lineWidth = (1 / scale / this.scale);
|
|
538
|
+
ctx.beginPath();
|
|
539
|
+
} catch { // Cairo
|
|
540
|
+
ctx.setDash(dashes, 0);
|
|
541
|
+
ctx.setLineWidth(1 / scale / this.scale);
|
|
542
|
+
ctx.newPath();
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
const rotation = Utils.degrees2radians(patternLine.angle + this.angle);
|
|
546
|
+
|
|
547
|
+
// Project the bounding box onto the pattern's rotated local axes
|
|
548
|
+
// to compute tight line counts instead of using the worst-case max dimension.
|
|
549
|
+
const halfW = bbXLength / (2 * this.scale);
|
|
550
|
+
const halfH = bbYLength / (2 * this.scale);
|
|
551
|
+
const sinR = Math.abs(Math.sin(rotation));
|
|
552
|
+
const cosR = Math.abs(Math.cos(rotation));
|
|
553
|
+
|
|
554
|
+
// Half-extent along the line direction (X in local frame)
|
|
555
|
+
const halfX = halfW * cosR + halfH * sinR;
|
|
556
|
+
// Half-extent perpendicular to lines (Y in local frame)
|
|
557
|
+
const halfY = halfW * sinR + halfH * cosR;
|
|
558
|
+
|
|
559
|
+
const xIncrement = Math.ceil(halfX / dashLength);
|
|
560
|
+
const yIncrement = Math.ceil(halfY / Math.abs(patternLine.yDelta));
|
|
561
|
+
|
|
562
|
+
// Set transform once for all lines in this pattern line family
|
|
563
|
+
ctx.save();
|
|
564
|
+
// translate to the center of the shape
|
|
565
|
+
// apply the origin offsets without rotation
|
|
566
|
+
ctx.translate(centerPoint.x + patternLine.xOrigin * this.scale, centerPoint.y + patternLine.yOrigin * this.scale);
|
|
567
|
+
// scale the context
|
|
568
|
+
ctx.scale(this.scale, this.scale);
|
|
569
|
+
// rotate the context
|
|
570
|
+
ctx.rotate(rotation);
|
|
571
|
+
|
|
572
|
+
for (let i = -yIncrement; i < yIncrement; i++) {
|
|
573
|
+
// apply the deltaX offset for odd iterations
|
|
574
|
+
// define offsets for the current iteration
|
|
575
|
+
const xOffset = patternLine.xDelta * Math.abs(i) + dashLength * xIncrement;
|
|
576
|
+
const yOffset = patternLine.yDelta * i;
|
|
577
|
+
|
|
578
|
+
ctx.moveTo(-xOffset + dashOffset, yOffset);
|
|
579
|
+
ctx.lineTo(xOffset, yOffset);
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
ctx.stroke();
|
|
583
|
+
ctx.restore();
|
|
584
|
+
});
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
/**
|
|
588
|
+
* Write the entity to file in the dxf format
|
|
589
|
+
* @param {DXFFile} file
|
|
590
|
+
*/
|
|
591
|
+
dxf(file) {
|
|
592
|
+
// skip if no boundary shapes
|
|
593
|
+
if (!this.childEntities.length) {
|
|
594
|
+
return;
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
file.writeGroupCode('0', 'HATCH');
|
|
598
|
+
file.writeGroupCode('5', this.handle, DXFFile.Version.R2000); // Handle
|
|
599
|
+
file.writeGroupCode('100', 'AcDbEntity', DXFFile.Version.R2000);
|
|
600
|
+
file.writeGroupCode('8', this.layer);
|
|
601
|
+
file.writeGroupCode('100', 'AcDbHatch', DXFFile.Version.R2000);
|
|
602
|
+
file.writeGroupCode('10', '0.0');
|
|
603
|
+
file.writeGroupCode('20', '0.0');
|
|
604
|
+
file.writeGroupCode('30', '0.0');
|
|
605
|
+
|
|
606
|
+
file.writeGroupCode('210', '0.0'); // Extrusion Direction X
|
|
607
|
+
file.writeGroupCode('220', '0.0'); // Extrusion Direction Y
|
|
608
|
+
file.writeGroupCode('230', '1.0'); // Extrusion Direction Z
|
|
609
|
+
|
|
610
|
+
file.writeGroupCode('2', this.patternName); // Hatch pattern name
|
|
611
|
+
file.writeGroupCode('70', this.solid ? 1 : 0); // Solid Fill Flag (1 = solid, 0 = pattern)
|
|
612
|
+
file.writeGroupCode('71', '0'); // Associativity flag (associative = 1; non-associative = 0); for MPolygon, solid-fill flag (has solidfill = 1; lacks solid fill = 0)
|
|
613
|
+
|
|
614
|
+
|
|
615
|
+
file.writeGroupCode('91', this.childEntities.length); // Number of boundary path loops
|
|
616
|
+
|
|
617
|
+
for (let i = 0; i < this.childEntities.length; i++) {
|
|
618
|
+
const shape = this.childEntities[i];
|
|
619
|
+
file.writeGroupCode('92', '7'); // Boundary path type flag (bit coded): 0 = Default; 1 = External; 2 = Polyline 4 = Derived; 8 = Textbox; 16 = Outermost
|
|
620
|
+
file.writeGroupCode('72', '1'); // Edge type (only if boundary is not a polyline): 1 = Line; 2 = Circular arc; 3 = Elliptic arc; 4 = Spline
|
|
621
|
+
file.writeGroupCode('73', '1'); // For MPolygon, boundary annotation flag (boundary is an annotated boundary = 1; boundary is not an annotated boundary = 0)
|
|
622
|
+
file.writeGroupCode('93', shape.points.length); // Number of edges in this boundary path / number of points in polyline
|
|
623
|
+
|
|
624
|
+
|
|
625
|
+
for (let j = 0; j < shape.points.length; j++) {
|
|
626
|
+
file.writeGroupCode('10', shape.points[j].x); // X
|
|
627
|
+
file.writeGroupCode('20', shape.points[j].y); // Y
|
|
628
|
+
file.writeGroupCode('42', shape.points[j].bulge);
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
file.writeGroupCode('97', '0'); // Number of source boundary objects
|
|
632
|
+
}
|
|
633
|
+
file.writeGroupCode('75', '1'); // Hatch style: 0 = Hatch “odd parity” area (Normal style) 1 = Hatch outermost area only (Outer style) 2 = Hatch through entire area (Ignore style)
|
|
634
|
+
file.writeGroupCode('76', '1'); // Hatch pattern type: 0 = User-defined; 1 = Predefined; 2 = Custom
|
|
635
|
+
|
|
636
|
+
if (!this.solid) {
|
|
637
|
+
file.writeGroupCode('52', this.angle); // Hatch Pattern angle
|
|
638
|
+
file.writeGroupCode('41', this.scale); // Hatch Pattern scale
|
|
639
|
+
file.writeGroupCode('77', '0'); // Hatch pattern double flag(pattern fill only): 0 = not double; 1 = double
|
|
640
|
+
file.writeGroupCode('78', Patterns.getPatternLineCount(this.patternName)); // Number of pattern definition lines
|
|
641
|
+
|
|
642
|
+
// Pattern data
|
|
643
|
+
const pattern = Patterns.getPattern(this.patternName);
|
|
644
|
+
pattern.forEach((patternLine) => {
|
|
645
|
+
file.writeGroupCode('53', patternLine.angle + this.angle); // Pattern line angle
|
|
646
|
+
file.writeGroupCode('43', patternLine.xOrigin); // Pattern line base X
|
|
647
|
+
file.writeGroupCode('44', patternLine.yOrigin); // Pattern line base y
|
|
648
|
+
const deltaPoint = new Point(patternLine.xDelta * this.scale, patternLine.yDelta * this.scale);
|
|
649
|
+
const rotatedDelta = deltaPoint.rotate(new Point(), Utils.degrees2radians(patternLine.angle + this.angle));
|
|
650
|
+
file.writeGroupCode('45', rotatedDelta.x); // Pattern line offset x
|
|
651
|
+
file.writeGroupCode('46', rotatedDelta.y); // Pattern line offset y
|
|
652
|
+
file.writeGroupCode('79', patternLine.dashes.length); // Number of dash length items
|
|
653
|
+
patternLine.dashes.forEach((dash) => {
|
|
654
|
+
file.writeGroupCode('49', dash * this.scale); // Dash length
|
|
655
|
+
});
|
|
656
|
+
});
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
file.writeGroupCode('47', '0.5'); // pixel size
|
|
660
|
+
file.writeGroupCode('98', '1'); // Number of seed points
|
|
661
|
+
// seed points
|
|
662
|
+
file.writeGroupCode('10', '1');
|
|
663
|
+
file.writeGroupCode('20', '1');
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
/**
|
|
667
|
+
* Get snap points
|
|
668
|
+
* @param {Point} mousePoint
|
|
669
|
+
* @param {number} delta
|
|
670
|
+
* @return {Array} - array of snap points
|
|
671
|
+
*/
|
|
672
|
+
snaps(mousePoint, delta) {
|
|
673
|
+
const snaps = [];
|
|
674
|
+
return snaps;
|
|
675
|
+
}
|
|
676
|
+
|
|
677
|
+
/**
|
|
678
|
+
* Get closest point on entity
|
|
679
|
+
* @param {Point} P
|
|
680
|
+
* @return {Array} - [Point, distance]
|
|
681
|
+
*/
|
|
682
|
+
closestPoint(P) {
|
|
683
|
+
if (this.isInside(P)) {
|
|
684
|
+
return [P, 0];
|
|
685
|
+
}
|
|
686
|
+
return [P, Infinity];
|
|
687
|
+
}
|
|
688
|
+
|
|
689
|
+
/**
|
|
690
|
+
* Determine if point is inside the hatch
|
|
691
|
+
* @param {Point} P
|
|
692
|
+
* @return {boolean} - true if inside
|
|
693
|
+
*/
|
|
694
|
+
isInside(P) {
|
|
695
|
+
// P = P.subtract(this.points[0]);
|
|
696
|
+
for (let i = 0; i < this.childEntities.length; i++) {
|
|
697
|
+
const shape = this.childEntities[i];
|
|
698
|
+
|
|
699
|
+
if (shape.boundingBox().isInside(P)) {
|
|
700
|
+
const polyline = [...shape.points];
|
|
701
|
+
|
|
702
|
+
// check the polyline is closed
|
|
703
|
+
if (!shape.points.at(0).isSame(shape.points.at(-1))) {
|
|
704
|
+
polyline.push(shape.points.at(0));
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
// create a line from P, twice the length of the bounding box
|
|
708
|
+
const line = [P, new Point(P.x + shape.boundingBox().xLength, P.y)];
|
|
709
|
+
|
|
710
|
+
const intersect = Intersection.intersectPolylinePolyline(polyline, line);
|
|
711
|
+
const intersects = intersect.points.length;
|
|
712
|
+
// P is inside shape if there is a odd number of intersects
|
|
713
|
+
if (Math.abs(intersects % 2) == 1) {
|
|
714
|
+
return true;
|
|
715
|
+
}
|
|
716
|
+
}
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
return false;
|
|
720
|
+
}
|
|
721
|
+
|
|
722
|
+
/**
|
|
723
|
+
* Return boundingbox for entity
|
|
724
|
+
* @return {BoundingBox}
|
|
725
|
+
*/
|
|
726
|
+
boundingBox() {
|
|
727
|
+
if (this.childEntities.length === 0) {
|
|
728
|
+
return new BoundingBox();
|
|
729
|
+
}
|
|
730
|
+
|
|
731
|
+
let xmin = Infinity;
|
|
732
|
+
let xmax = -Infinity;
|
|
733
|
+
let ymin = Infinity;
|
|
734
|
+
let ymax = -Infinity;
|
|
735
|
+
|
|
736
|
+
for (let i = 0; i < this.childEntities.length; i++) {
|
|
737
|
+
const shape = this.childEntities[i];
|
|
738
|
+
|
|
739
|
+
const boundingBox = shape.boundingBox();
|
|
740
|
+
|
|
741
|
+
xmin = Math.min(xmin, boundingBox.xMin);
|
|
742
|
+
xmax = Math.max(xmax, boundingBox.xMax);
|
|
743
|
+
ymin = Math.min(ymin, boundingBox.yMin);
|
|
744
|
+
ymax = Math.max(ymax, boundingBox.yMax);
|
|
745
|
+
}
|
|
746
|
+
|
|
747
|
+
const topLeft = new Point(xmin, ymax);
|
|
748
|
+
const bottomRight = new Point(xmax, ymin);
|
|
749
|
+
|
|
750
|
+
return new BoundingBox(topLeft, bottomRight);
|
|
751
|
+
}
|
|
752
|
+
|
|
753
|
+
/**
|
|
754
|
+
* Determine if the entity is touch the selection window
|
|
755
|
+
* @param {Object} selection - {min: Point, max: Point}
|
|
756
|
+
* @return {boolean} true if touched
|
|
757
|
+
*/
|
|
758
|
+
touched(selection) {
|
|
759
|
+
for (let i = 0; i < this.childEntities.length; i++) {
|
|
760
|
+
if (this.childEntities[i].touched(selection)) {
|
|
761
|
+
return true;
|
|
762
|
+
}
|
|
763
|
+
}
|
|
764
|
+
|
|
765
|
+
return false;
|
|
766
|
+
}
|
|
767
|
+
|
|
768
|
+
/**
|
|
769
|
+
* Set a property if it exists
|
|
770
|
+
* @param {string} property
|
|
771
|
+
* @param {any} value
|
|
772
|
+
*/
|
|
773
|
+
setProperty(property, value) {
|
|
774
|
+
if (this.hasOwnProperty(property)) {
|
|
775
|
+
if (property === 'points') {
|
|
776
|
+
// Special handling for hatch points to move child entities
|
|
777
|
+
// Consider the changes from the hatch points to be an offset and rotation
|
|
778
|
+
const ang = value[0].angle(value.at(-1));
|
|
779
|
+
const theta = ang - this.points[0].angle(this.points.at(-1));
|
|
780
|
+
|
|
781
|
+
if (theta !== 0) {
|
|
782
|
+
// set rotation center
|
|
783
|
+
const center = this.points[0];
|
|
784
|
+
// apply rotation to child entities points
|
|
785
|
+
this.childEntities.forEach((child) => {
|
|
786
|
+
const rotatedPoints = child.points.map((p) => new Point(p.x, p.y, p.bulge, p.sequence).rotate(center, theta));
|
|
787
|
+
child.setProperty('points', rotatedPoints);
|
|
788
|
+
});
|
|
789
|
+
|
|
790
|
+
this.angle+= Utils.radians2degrees(theta);
|
|
791
|
+
}
|
|
792
|
+
|
|
793
|
+
const delta = value[0].subtract(this.points[0]);
|
|
794
|
+
if (delta.x !== 0 || delta.y !== 0) {
|
|
795
|
+
// apply translation to child entities points
|
|
796
|
+
this.childEntities.forEach((child) => {
|
|
797
|
+
const offsetPoints = child.points.map((p) => new Point(p.x, p.y, p.bulge, p.sequence).add(delta));
|
|
798
|
+
child.setProperty('points', offsetPoints);
|
|
799
|
+
});
|
|
800
|
+
}
|
|
801
|
+
}
|
|
802
|
+
|
|
803
|
+
// other properties as normal
|
|
804
|
+
this[property] = value;
|
|
805
|
+
}
|
|
806
|
+
}
|
|
807
|
+
}
|