@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,642 @@
|
|
|
1
|
+
import { Arc } from './arc.js';
|
|
2
|
+
import { Point } from './point.js';
|
|
3
|
+
import { Utils } from '../lib/utils.js';
|
|
4
|
+
import { Strings } from '../lib/strings.js';
|
|
5
|
+
import { Entity } from './entity.js';
|
|
6
|
+
import { Input, PromptOptions } from '../lib/inputManager.js';
|
|
7
|
+
import { Logging } from '../lib/logging.js';
|
|
8
|
+
import { DXFFile } from '../lib/dxf/dxfFile.js';
|
|
9
|
+
import { BoundingBox } from '../lib/boundingBox.js';
|
|
10
|
+
import { Property } from '../properties/property.js';
|
|
11
|
+
import { Text } from './text.js';
|
|
12
|
+
|
|
13
|
+
import { DesignCore } from '../designCore.js';
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Arc Aligned Character Class
|
|
17
|
+
*/
|
|
18
|
+
export class ArcAlignedCharacter {
|
|
19
|
+
#character;
|
|
20
|
+
#position;
|
|
21
|
+
#angle;
|
|
22
|
+
#height;
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* @param {string} character - text character
|
|
26
|
+
* @param {Point} position - center mid point of character
|
|
27
|
+
* @param {number} angle - in radians
|
|
28
|
+
* @param {number} height - character height
|
|
29
|
+
*/
|
|
30
|
+
constructor(character, position, angle, height = 1) {
|
|
31
|
+
this.#character = character;
|
|
32
|
+
this.#position = position;
|
|
33
|
+
this.#angle = angle;
|
|
34
|
+
this.#height = height;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Get character
|
|
39
|
+
* @return {string}
|
|
40
|
+
*/
|
|
41
|
+
get character() {
|
|
42
|
+
return this.#character;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Get position
|
|
47
|
+
* @return {Point}
|
|
48
|
+
*/
|
|
49
|
+
get position() {
|
|
50
|
+
return this.#position;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Get angle
|
|
55
|
+
* @return {number}
|
|
56
|
+
*/
|
|
57
|
+
get angle() {
|
|
58
|
+
return this.#angle;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Get height
|
|
63
|
+
* @return {number}
|
|
64
|
+
*/
|
|
65
|
+
get height() {
|
|
66
|
+
return this.#height;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Get baseline point
|
|
71
|
+
* @return {Point}
|
|
72
|
+
*/
|
|
73
|
+
get baseline() {
|
|
74
|
+
const bb = this.boundingBox;
|
|
75
|
+
const baselinePoint = new Point(this.#position.x - bb.xLength*0.5, this.#position.y - bb.yLength*0.5).rotate(this.#position, this.#angle);
|
|
76
|
+
return baselinePoint;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Get bounding box
|
|
81
|
+
* @return {BoundingBox}
|
|
82
|
+
*/
|
|
83
|
+
get boundingBox() {
|
|
84
|
+
const halfHeight = this.#height / 2;
|
|
85
|
+
const halfWidth = halfHeight * 0.6; // approximate width as half height
|
|
86
|
+
const pt1 = new Point(this.position.x - halfWidth, this.position.y - halfHeight);
|
|
87
|
+
const pt2 = new Point(this.position.x + halfWidth, this.position.y + halfHeight);
|
|
88
|
+
return new BoundingBox(pt1, pt2);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Text Entity Class
|
|
95
|
+
* @extends Entity
|
|
96
|
+
*/
|
|
97
|
+
export class ArcAlignedText extends Entity {
|
|
98
|
+
/**
|
|
99
|
+
* Create a Text Entity
|
|
100
|
+
* @param {Array} data
|
|
101
|
+
*/
|
|
102
|
+
constructor(data) {
|
|
103
|
+
super(data);
|
|
104
|
+
|
|
105
|
+
// hide inherited properties
|
|
106
|
+
// needs to be enumerable=false to not appear in the object props
|
|
107
|
+
Object.defineProperty(this, 'lineType', {
|
|
108
|
+
enumerable: false,
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
Object.defineProperty(this, 'lineWidth', {
|
|
112
|
+
enumerable: false,
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
// DXF Groupcode 1 - Text String
|
|
116
|
+
this.string = (Property.loadValue([data?.string, data?.[1]], ''));
|
|
117
|
+
|
|
118
|
+
// DXF Groupcode 2 - Font name
|
|
119
|
+
// not implemented - enumerable=false to not appear in the object props
|
|
120
|
+
// get font from selected style: this.styleName.font
|
|
121
|
+
Object.defineProperty(this, 'fontName', {
|
|
122
|
+
value: Property.loadValue([data?.fontName, data?.[2]], 'Arial'),
|
|
123
|
+
writable: true,
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
// DXF Groupcode 3
|
|
127
|
+
// not implemented - always empty in acad output
|
|
128
|
+
|
|
129
|
+
// DXF Groupcode 7 - Text style name
|
|
130
|
+
this.styleName = (Property.loadValue([data?.styleName, data?.[7]], 'STANDARD'));
|
|
131
|
+
|
|
132
|
+
// DXF Groupcode 40 - Radius
|
|
133
|
+
this.radius = Property.loadValue([data?.radius, data?.[40]], 10);
|
|
134
|
+
|
|
135
|
+
// DXF Groupcode 41 - Width Factor
|
|
136
|
+
// not implemented - enumerable=false to not appear in the object props
|
|
137
|
+
Object.defineProperty(this, 'widthFactor', {
|
|
138
|
+
value: Property.loadValue([data?.widthFactor, data?.[41]], 1),
|
|
139
|
+
writable: true,
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
// DXF Groupcode 42 - Text Height
|
|
143
|
+
this.height = Property.loadValue([data?.height, data?.[42]], 2.5);
|
|
144
|
+
// DXF Groupcode 43 - Character Spacing
|
|
145
|
+
this.characterSpacing = Property.loadValue([data?.characterSpacing, data?.[43]], 0.095);
|
|
146
|
+
// DXF Groupcode 44 - Offset from Arc
|
|
147
|
+
this.offsetFromArc = Property.loadValue([data?.offsetFromArc, data?.[44]], 0);
|
|
148
|
+
// DXF Groupcode 45 - Offset from right
|
|
149
|
+
this.offsetFromRight = Property.loadValue([data?.offsetFromRight, data?.[45]], 0);
|
|
150
|
+
// DXF Groupcode 46 - Offset from left
|
|
151
|
+
this.offsetFromLeft = Property.loadValue([data?.offsetFromLeft, data?.[46]], 0);
|
|
152
|
+
|
|
153
|
+
// ensure points array has at least one point - the arc center
|
|
154
|
+
if (!this.points.length) {
|
|
155
|
+
this.points = [];
|
|
156
|
+
this.points.push(new Point(0, 0)); // center point
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// DXF Groupcode 50 - Start Angle in degrees
|
|
160
|
+
const startAngle = Utils.round(Property.loadValue([data?.startAngle, data?.[50]], 0));
|
|
161
|
+
if (this.points[1] === undefined) {
|
|
162
|
+
this.points[1] = this.points[0].project(Utils.degrees2radians(startAngle), this.radius);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// DXF Groupcode 51 - End Angle in degrees
|
|
166
|
+
const endAngle = Utils.round(Property.loadValue([data?.endAngle, data?.[51]], 180));
|
|
167
|
+
if (this.points[2] === undefined) {
|
|
168
|
+
this.points[2] = this.points[0].project(Utils.degrees2radians(endAngle), this.radius);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// DXF Groupcode 70 - Text Direction 0 = forward, 1 = reversed
|
|
172
|
+
// not implemented - enumerable=false to not appear in the object props
|
|
173
|
+
Object.defineProperty(this, 'textReversed', {
|
|
174
|
+
value: Property.loadValue([data?.textReversed, Boolean(parseInt(data?.[70]))], false),
|
|
175
|
+
writable: true,
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
// DXF Groupcode 71 - 1 = outward, 2 = inward
|
|
179
|
+
this.textOrientation = Property.loadValue([data?.textOrientation, data?.[71]], 1);
|
|
180
|
+
// DXF Groupcode 72 - 1 = fit to arc, 2 = left align, 3 = right align, 4 = center
|
|
181
|
+
this.textAlignment = Property.loadValue([data?.textAlignment, data?.[72]], 4);
|
|
182
|
+
// DXF Groupcode 73 - Arc Side: convex = 1, concave = 2
|
|
183
|
+
this.arcSide = Property.loadValue([data?.arcSide, data?.[73]], 1);
|
|
184
|
+
|
|
185
|
+
// DXF Groupcode 74 - Bold 0 = off, 1 = on
|
|
186
|
+
// not implemented - enumerable=false to not appear in the object props
|
|
187
|
+
Object.defineProperty(this, 'bold', {
|
|
188
|
+
value: Property.loadValue([data?.bold, Boolean(parseInt(data?.[74]))], false),
|
|
189
|
+
writable: true,
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
// DXF Groupcode 75 - Italic 0 = off, 1 = on
|
|
193
|
+
// not implemented - enumerable=false to not appear in the object props
|
|
194
|
+
Object.defineProperty(this, 'italic', {
|
|
195
|
+
value: Property.loadValue([data?.italic, Boolean(parseInt(data?.[75]))], false),
|
|
196
|
+
writable: true,
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
// DXF Groupcode 76 - Underline 0 = off, 1 = on
|
|
200
|
+
// not implemented - enumerable=false to not appear in the object props
|
|
201
|
+
Object.defineProperty(this, 'underline', {
|
|
202
|
+
value: Property.loadValue([data?.underline, Boolean(parseInt(data?.[76]))], false),
|
|
203
|
+
writable: true,
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
// Not Implemented:
|
|
207
|
+
// DXF Groupcode 77 -
|
|
208
|
+
// DXF Groupcode 78 -
|
|
209
|
+
// DXF Groupcode 79 -
|
|
210
|
+
// DXF Groupcode 90 -
|
|
211
|
+
// DXF Groupcode 210 -
|
|
212
|
+
// DXF Groupcode 220 -
|
|
213
|
+
// DXF Groupcode 230 -
|
|
214
|
+
// DXF Groupcode 280 -
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* Register the command
|
|
219
|
+
* @return {Object}
|
|
220
|
+
* command = name of the command
|
|
221
|
+
* shortcut = shortcut for the command
|
|
222
|
+
* type = type to group command in toolbars (omitted if not shown)
|
|
223
|
+
*/
|
|
224
|
+
static register() {
|
|
225
|
+
const command = { command: 'ArcAlignedText', shortcut: 'ArcText' };
|
|
226
|
+
return command;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* Execute method
|
|
231
|
+
* executes the workflow, requesting input required to create an entity
|
|
232
|
+
*/
|
|
233
|
+
async execute() {
|
|
234
|
+
try {
|
|
235
|
+
const op = new PromptOptions(Strings.Input.SELECT, [Input.Type.SINGLESELECTION]);
|
|
236
|
+
|
|
237
|
+
let selectedArc = null;
|
|
238
|
+
while (selectedArc instanceof Arc !== true) {
|
|
239
|
+
// reset selection
|
|
240
|
+
DesignCore.Scene.selectionManager.reset();
|
|
241
|
+
|
|
242
|
+
const selection = await DesignCore.Scene.inputManager.requestInput(op);
|
|
243
|
+
if (selection === undefined) return;
|
|
244
|
+
selectedArc = DesignCore.Scene.entities.get(selection.selectedItemIndex);
|
|
245
|
+
|
|
246
|
+
if (selectedArc instanceof Arc === false) {
|
|
247
|
+
const msg = `${this.type} - ${Strings.Error.INVALIDTYPE}: ${selectedArc.type}`;
|
|
248
|
+
DesignCore.Core.notify(msg);
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
// Get the arc properties
|
|
253
|
+
// direction: - ccw > 0, cw <= 0
|
|
254
|
+
this.radius = selectedArc.radius;
|
|
255
|
+
const startPoint = selectedArc.direction > 0 ? selectedArc.points[1] : selectedArc.points[2];
|
|
256
|
+
const endPoint = selectedArc.direction > 0 ? selectedArc.points[2] : selectedArc.points[1];
|
|
257
|
+
|
|
258
|
+
// set the points
|
|
259
|
+
this.points[0] = new Point(selectedArc.points[0].x, selectedArc.points[0].y);
|
|
260
|
+
this.points[1] = new Point(startPoint.x, startPoint.y);
|
|
261
|
+
this.points[2] = new Point(endPoint.x, endPoint.y);
|
|
262
|
+
|
|
263
|
+
// set the text style to the current style
|
|
264
|
+
const currentStyle = DesignCore.StyleManager.getCstyle();
|
|
265
|
+
this.styleName = currentStyle;
|
|
266
|
+
|
|
267
|
+
// get properties from style
|
|
268
|
+
const style = DesignCore.StyleManager.getItemByName(this.styleName);
|
|
269
|
+
if (style?.textHeight) {
|
|
270
|
+
this.height = style.textHeight;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
// Get the font size when standard style is used
|
|
274
|
+
if (this.styleName.toUpperCase() === 'STANDARD') {
|
|
275
|
+
const op2 = new PromptOptions(Strings.Input.HEIGHT, [Input.Type.NUMBER], [], this.height);
|
|
276
|
+
const height = await DesignCore.Scene.inputManager.requestInput(op2);
|
|
277
|
+
if (height === undefined) return;
|
|
278
|
+
this.height = height;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
const op4 = new PromptOptions(Strings.Input.STRING, [Input.Type.STRING, Input.Type.NUMBER]);
|
|
282
|
+
const string = await DesignCore.Scene.inputManager.requestInput(op4);
|
|
283
|
+
if (string === undefined) return;
|
|
284
|
+
this.string = String(string);
|
|
285
|
+
|
|
286
|
+
DesignCore.Scene.inputManager.executeCommand(this);
|
|
287
|
+
} catch (err) {
|
|
288
|
+
Logging.instance.error(`${this.type} - ${err}`);
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
/**
|
|
293
|
+
* Preview the entity during creation
|
|
294
|
+
*/
|
|
295
|
+
preview() {
|
|
296
|
+
if (this.points.length >= 1) {
|
|
297
|
+
if (DesignCore.Scene.inputManager.promptOption.types.includes(Input.Type.STRING)) {
|
|
298
|
+
const data = {
|
|
299
|
+
points: this.points,
|
|
300
|
+
height: this.height,
|
|
301
|
+
rotation: this.rotation,
|
|
302
|
+
startAngle: Utils.radians2degrees(this.startAngle()),
|
|
303
|
+
endAngle: Utils.radians2degrees(this.endAngle()),
|
|
304
|
+
radius: this.radius,
|
|
305
|
+
string: DesignCore.CommandLine.command,
|
|
306
|
+
};
|
|
307
|
+
|
|
308
|
+
DesignCore.Scene.tempEntities.create(this.type, data);
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
/**
|
|
314
|
+
* Get start angle in radians
|
|
315
|
+
* @return {number}
|
|
316
|
+
*/
|
|
317
|
+
startAngle() {
|
|
318
|
+
return this.points[0].angle(this.points[1]);
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
/**
|
|
322
|
+
* Get end angle in radians
|
|
323
|
+
* @return {number}
|
|
324
|
+
*/
|
|
325
|
+
endAngle() {
|
|
326
|
+
return this.points[0].angle(this.points[2]);
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
/**
|
|
330
|
+
* Convert linear length to angular length on arc
|
|
331
|
+
* @param {number} length - linear length
|
|
332
|
+
* @param {number} radius - radius of arc
|
|
333
|
+
* @return {number} angle in radians
|
|
334
|
+
*/
|
|
335
|
+
linearToAngular(length, radius) {
|
|
336
|
+
if (radius === 0) {
|
|
337
|
+
return 0;
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
return 2 * Math.atan((length)/Math.abs(radius));
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
/**
|
|
344
|
+
* Get mid angle of arc
|
|
345
|
+
* @param {number} startAngle - in radians
|
|
346
|
+
* @param {number} endAngle - in radians
|
|
347
|
+
* @return {number} mid angle in radians
|
|
348
|
+
*/
|
|
349
|
+
arcMidAngle(startAngle, endAngle) {
|
|
350
|
+
let startAng = startAngle % (Math.PI*2);
|
|
351
|
+
let endAng = endAngle % (Math.PI*2);
|
|
352
|
+
|
|
353
|
+
if (startAng < 0) startAng += Math.PI*2;
|
|
354
|
+
if (endAng < 0) endAng += Math.PI*2;
|
|
355
|
+
|
|
356
|
+
let midAngle;
|
|
357
|
+
if (endAng < startAng) {
|
|
358
|
+
midAngle = ((endAng + Math.PI*2) + startAng) / 2;
|
|
359
|
+
if (midAngle >= Math.PI*2) midAngle -= Math.PI*2;
|
|
360
|
+
} else {
|
|
361
|
+
midAngle = (startAng + endAng) / 2;
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
return midAngle;
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
/**
|
|
368
|
+
* Get characters aligned on arc
|
|
369
|
+
* @return {Array} - array of ArcAlignedCharacter
|
|
370
|
+
*/
|
|
371
|
+
getArcAlignedCharacters() {
|
|
372
|
+
// character positions on arc
|
|
373
|
+
const arcAlignedCharacters = [];
|
|
374
|
+
|
|
375
|
+
if (this.string.length === 0) {
|
|
376
|
+
return arcAlignedCharacters;
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
// calculate the radial distance - Arc Side: convex = 1, concave = 2
|
|
380
|
+
const radialDistance = this.arcSide === 2 ? this.radius - this.offsetFromArc - this.height * 0.5 : this.radius + this.offsetFromArc + this.height * 0.5;
|
|
381
|
+
// calculate character width
|
|
382
|
+
const charWidth = Text.getApproximateWidth(this.string[0], this.height);
|
|
383
|
+
// calculate total char width including additional spacing
|
|
384
|
+
const totalCharWidth = charWidth + this.characterSpacing;
|
|
385
|
+
// convert the linear width of a character as an angle on the arc
|
|
386
|
+
const charWidthAsAngle = this.linearToAngular(charWidth*0.5, radialDistance);
|
|
387
|
+
// get total charwidth as angle
|
|
388
|
+
let totalCharWidthAsAngle = this.linearToAngular(totalCharWidth*0.5, radialDistance);
|
|
389
|
+
|
|
390
|
+
// calculate start and end offsets
|
|
391
|
+
const startOffsetAngle = this.linearToAngular(this.offsetFromRight, radialDistance) + charWidthAsAngle * 0.5;
|
|
392
|
+
const endOffsetAngle = this.linearToAngular(this.offsetFromLeft, radialDistance) + charWidthAsAngle * 0.5;
|
|
393
|
+
|
|
394
|
+
// total arc angle
|
|
395
|
+
const totalArcAngle = Math.abs(this.endAngle() - this.startAngle()) - startOffsetAngle - endOffsetAngle;
|
|
396
|
+
|
|
397
|
+
// defined positions - Start and end angles +/- half char width
|
|
398
|
+
const startPosition = this.points[0].project(this.startAngle() + startOffsetAngle, radialDistance);
|
|
399
|
+
const endPosition = this.points[0].project(this.endAngle() - endOffsetAngle, radialDistance);
|
|
400
|
+
|
|
401
|
+
// default to the arc end position as the string start
|
|
402
|
+
let stringStartPoint = endPosition;
|
|
403
|
+
// direction: - ccw > 0, cw <= 0
|
|
404
|
+
let direction = 1; // default to ccw
|
|
405
|
+
|
|
406
|
+
let string = this.string.slice(); // make a copy of the string
|
|
407
|
+
|
|
408
|
+
// 1 = fit to arc, 2 = left align, 3 = right align, 4 = center
|
|
409
|
+
if (this.textAlignment === 1) { // fit to arc
|
|
410
|
+
totalCharWidthAsAngle = totalArcAngle / (this.string.length - 1);
|
|
411
|
+
}
|
|
412
|
+
if (this.textAlignment === 2) { // left align
|
|
413
|
+
// default
|
|
414
|
+
}
|
|
415
|
+
if (this.textAlignment === 3) { // right align
|
|
416
|
+
// start at the arc start position and create the text cw around the arc
|
|
417
|
+
stringStartPoint = startPosition;
|
|
418
|
+
direction = -1;
|
|
419
|
+
string = string.split('').reverse().join('');
|
|
420
|
+
}
|
|
421
|
+
if (this.textAlignment === 4) { // center
|
|
422
|
+
const arcMidPoint = this.points[0].project(this.arcMidAngle(this.startAngle(), this.endAngle()), radialDistance);
|
|
423
|
+
stringStartPoint = arcMidPoint.rotate(this.points[0], 0.5 * totalCharWidthAsAngle * (string.length - 1));
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
// calculate the text rotation angle: 1 = outward, 2 = inward
|
|
427
|
+
const textRotationAngle = this.textOrientation === 2 ? -Math.PI*0.5 : Math.PI*0.5;
|
|
428
|
+
|
|
429
|
+
if (this.textOrientation === 2) {
|
|
430
|
+
// direction = -direction;
|
|
431
|
+
string = string.split('').reverse().join('');
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
/*
|
|
435
|
+
if (this.textReversed === 1) {
|
|
436
|
+
// reverse the string
|
|
437
|
+
string = string.split('').reverse().join('');
|
|
438
|
+
// reverse the direction
|
|
439
|
+
direction = -direction;
|
|
440
|
+
}
|
|
441
|
+
*/
|
|
442
|
+
|
|
443
|
+
// loop through string and calculate position and angle of each character
|
|
444
|
+
for (let index = 0; index < string.length; index++) {
|
|
445
|
+
const charPosition = stringStartPoint.rotate(this.points[0], -totalCharWidthAsAngle*((index) * direction));
|
|
446
|
+
const charAngle = this.points[0].angle(charPosition) - textRotationAngle;
|
|
447
|
+
const arcChar = new ArcAlignedCharacter(string[index], charPosition, charAngle, this.height);
|
|
448
|
+
arcAlignedCharacters.push(arcChar);
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
return arcAlignedCharacters;
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
/**
|
|
455
|
+
* Draw the entity
|
|
456
|
+
* @param {Object} ctx - context
|
|
457
|
+
* @param {number} scale
|
|
458
|
+
*/
|
|
459
|
+
draw(ctx, scale) {
|
|
460
|
+
ctx.save(); // save current context before scale and translate
|
|
461
|
+
ctx.scale(1, -1);
|
|
462
|
+
|
|
463
|
+
const style = DesignCore.StyleManager.getItemByName(this.styleName);
|
|
464
|
+
|
|
465
|
+
// Cairo
|
|
466
|
+
ctx.setFontSize(this.height);
|
|
467
|
+
ctx.selectFontFace(style?.font, null, null); // (FontName, cairo.FONT_SLANT_NORMAL, cairo.FONT_WEIGHT_NORMAL);
|
|
468
|
+
|
|
469
|
+
const ArcAlignedCharacters = this.getArcAlignedCharacters();
|
|
470
|
+
|
|
471
|
+
for (const arcAlignedChar of ArcAlignedCharacters) {
|
|
472
|
+
ctx.save(); // save current context
|
|
473
|
+
ctx.translate(arcAlignedChar.baseline.x, -arcAlignedChar.baseline.y);
|
|
474
|
+
ctx.rotate(-arcAlignedChar.angle);
|
|
475
|
+
ctx.showText(String(arcAlignedChar.character));
|
|
476
|
+
|
|
477
|
+
ctx.stroke();
|
|
478
|
+
ctx.restore(); // restore context
|
|
479
|
+
|
|
480
|
+
/*
|
|
481
|
+
// debug draw character bounding box
|
|
482
|
+
const bb = arcAlignedChar.boundingBox;
|
|
483
|
+
const center = arcAlignedChar.position;
|
|
484
|
+
|
|
485
|
+
const posOne = new Point(center.x - bb.xLength*0.5, center.y - bb.yLength*0.5).rotate(arcAlignedChar.position, arcAlignedChar.angle);
|
|
486
|
+
const posTwo = new Point(center.x + bb.xLength*0.5, center.y - bb.yLength*0.5).rotate(arcAlignedChar.position, arcAlignedChar.angle);
|
|
487
|
+
const posThree = new Point(center.x + bb.xLength*0.5, center.y + bb.yLength*0.5).rotate(arcAlignedChar.position, arcAlignedChar.angle);
|
|
488
|
+
const posFour = new Point(center.x - bb.xLength*0.5, center.y + bb.yLength*0.5).rotate(arcAlignedChar.position, arcAlignedChar.angle);
|
|
489
|
+
|
|
490
|
+
ctx.moveTo(posOne.x, -posOne.y);
|
|
491
|
+
ctx.lineTo(posTwo.x, -posTwo.y);
|
|
492
|
+
ctx.lineTo(posThree.x, -posThree.y);
|
|
493
|
+
ctx.lineTo(posFour.x, -posFour.y);
|
|
494
|
+
ctx.lineTo(posOne.x, -posOne.y);
|
|
495
|
+
ctx.stroke();
|
|
496
|
+
*/
|
|
497
|
+
|
|
498
|
+
/*
|
|
499
|
+
// debug draw the arcText bounding box
|
|
500
|
+
const bb = this.boundingBox();
|
|
501
|
+
ctx.moveTo(bb.xMin, -bb.yMin);
|
|
502
|
+
ctx.lineTo(bb.xMax, -bb.yMin);
|
|
503
|
+
ctx.lineTo(bb.xMax, -bb.yMax);
|
|
504
|
+
ctx.lineTo(bb.xMin, -bb.yMax);
|
|
505
|
+
ctx.lineTo(bb.xMin, -bb.yMin);
|
|
506
|
+
ctx.stroke();
|
|
507
|
+
*/
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
ctx.stroke();
|
|
511
|
+
ctx.restore(); // restore context before scale and translate
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
/**
|
|
515
|
+
* Write the entity to file in the dxf format
|
|
516
|
+
* @param {DXFFile} file
|
|
517
|
+
*/
|
|
518
|
+
dxf(file) {
|
|
519
|
+
const style = DesignCore.StyleManager.getItemByName(this.styleName);
|
|
520
|
+
|
|
521
|
+
file.writeGroupCode('0', 'ARCALIGNEDTEXT');
|
|
522
|
+
file.writeGroupCode('5', this.handle, DXFFile.Version.R2000); // Handle
|
|
523
|
+
file.writeGroupCode('100', 'AcDbEntity', DXFFile.Version.R2000);
|
|
524
|
+
file.writeGroupCode('8', this.layer);
|
|
525
|
+
file.writeGroupCode('100', 'AcDbArcAlignedText', DXFFile.Version.R2000);
|
|
526
|
+
file.writeGroupCode('1', this.string);
|
|
527
|
+
file.writeGroupCode('2', style?.font);
|
|
528
|
+
file.writeGroupCode('3', '');
|
|
529
|
+
file.writeGroupCode('7', this.styleName); // Test style name
|
|
530
|
+
file.writeGroupCode('10', this.points[0].x); // x of arc center
|
|
531
|
+
file.writeGroupCode('20', this.points[0].y); // y of arc center
|
|
532
|
+
file.writeGroupCode('30', '0.0'); // z of arc center
|
|
533
|
+
file.writeGroupCode('40', this.radius);
|
|
534
|
+
file.writeGroupCode('41', this.widthFactor);
|
|
535
|
+
file.writeGroupCode('42', this.height);
|
|
536
|
+
file.writeGroupCode('43', this.characterSpacing);
|
|
537
|
+
file.writeGroupCode('44', this.offsetFromArc);
|
|
538
|
+
file.writeGroupCode('45', this.offsetFromRight);
|
|
539
|
+
file.writeGroupCode('46', this.offsetFromLeft);
|
|
540
|
+
file.writeGroupCode('50', Utils.radians2degrees(this.startAngle()));
|
|
541
|
+
file.writeGroupCode('51', Utils.radians2degrees(this.endAngle()));
|
|
542
|
+
file.writeGroupCode('70', this.textReversed ? 1 : 0); // Text direction
|
|
543
|
+
file.writeGroupCode('71', this.textOrientation); // Text orientation
|
|
544
|
+
file.writeGroupCode('72', this.textAlignment); // Text alignment
|
|
545
|
+
file.writeGroupCode('73', this.arcSide); // Arc side
|
|
546
|
+
file.writeGroupCode('74', this.bold ? 1 : 0);
|
|
547
|
+
file.writeGroupCode('75', this.italic ? 1 : 0);
|
|
548
|
+
file.writeGroupCode('76', this.underline ? 1 : 0);
|
|
549
|
+
file.writeGroupCode('77', 0);
|
|
550
|
+
file.writeGroupCode('78', 34);
|
|
551
|
+
file.writeGroupCode('79', 0);
|
|
552
|
+
file.writeGroupCode('90', 256);
|
|
553
|
+
file.writeGroupCode('210', 0); // X extrusion
|
|
554
|
+
file.writeGroupCode('220', 0); // Y extrusion
|
|
555
|
+
file.writeGroupCode('230', 1); // Z extrusion
|
|
556
|
+
file.writeGroupCode('280', 1);
|
|
557
|
+
file.writeGroupCode('330', ''); // Handle of arc object?
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
/**
|
|
561
|
+
* Get snap points
|
|
562
|
+
* @param {Point} mousePoint
|
|
563
|
+
* @param {number} delta
|
|
564
|
+
* @return {Array} - array of snap points
|
|
565
|
+
*/
|
|
566
|
+
snaps(mousePoint, delta) {
|
|
567
|
+
const snaps = [];
|
|
568
|
+
|
|
569
|
+
const ArcAlignedCharacters = this.getArcAlignedCharacters();
|
|
570
|
+
|
|
571
|
+
for (const arcAlignedChar of ArcAlignedCharacters) {
|
|
572
|
+
// Don't snap to space characters
|
|
573
|
+
if (arcAlignedChar.character.trim() === '') continue;
|
|
574
|
+
snaps.push(arcAlignedChar.position);
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
snaps.push(this.points[0]); // arc center
|
|
578
|
+
|
|
579
|
+
return snaps;
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
/**
|
|
583
|
+
* Get closest point on entity
|
|
584
|
+
* @param {Point} P
|
|
585
|
+
* @return {Array} - [Point, distance]
|
|
586
|
+
*/
|
|
587
|
+
closestPoint(P) {
|
|
588
|
+
let distance = Infinity;
|
|
589
|
+
let minPnt = P;
|
|
590
|
+
|
|
591
|
+
const ArcAlignedCharacters = this.getArcAlignedCharacters();
|
|
592
|
+
|
|
593
|
+
for (const arcAlignedChar of ArcAlignedCharacters) {
|
|
594
|
+
let pntDist = P.distance(arcAlignedChar.position);
|
|
595
|
+
|
|
596
|
+
if (pntDist < arcAlignedChar.height * 0.35) {
|
|
597
|
+
pntDist = 0.1; // this is a hack to make selecting easier
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
if (pntDist < distance) {
|
|
601
|
+
distance = pntDist;
|
|
602
|
+
minPnt = arcAlignedChar.position;
|
|
603
|
+
}
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
return [minPnt, distance];
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
/**
|
|
610
|
+
* Return boundingbox for entity
|
|
611
|
+
* @return {BoundingBox}
|
|
612
|
+
*/
|
|
613
|
+
boundingBox() {
|
|
614
|
+
const ArcAlignedCharacters = this.getArcAlignedCharacters();
|
|
615
|
+
const points = [];
|
|
616
|
+
|
|
617
|
+
// get all corner points from each character bounding box
|
|
618
|
+
ArcAlignedCharacters.forEach((char) => {
|
|
619
|
+
const bb = char.boundingBox;
|
|
620
|
+
points.push(new Point(bb.xMin, bb.yMin));
|
|
621
|
+
points.push(new Point(bb.xMax, bb.yMin));
|
|
622
|
+
points.push(new Point(bb.xMin, bb.yMax));
|
|
623
|
+
points.push(new Point(bb.xMax, bb.yMax));
|
|
624
|
+
});
|
|
625
|
+
|
|
626
|
+
// create bounding box from points
|
|
627
|
+
if (points.length > 0) {
|
|
628
|
+
return BoundingBox.fromPoints(points);
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
return new BoundingBox();
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
/**
|
|
635
|
+
* Return a list of points representing a polyline version of this entity
|
|
636
|
+
* @return {Array}
|
|
637
|
+
*/
|
|
638
|
+
toPolylinePoints() {
|
|
639
|
+
const characters = this.getArcAlignedCharacters();
|
|
640
|
+
return characters.map((char) => char.position);
|
|
641
|
+
}
|
|
642
|
+
}
|