dxf-json 0.0.1
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/.github/FUNDING.yml +1 -0
- package/.github/ISSUE_TEMPLATE/bug_report.md +38 -0
- package/.github/ISSUE_TEMPLATE/custom.md +10 -0
- package/.github/ISSUE_TEMPLATE/feature_request.md +20 -0
- package/.github/pull_request_template.md +23 -0
- package/.github/workflows/npm-publish.yml +55 -0
- package/.swcrc +8 -0
- package/LICENSE +674 -0
- package/README.md +567 -0
- package/jest.config.js +20 -0
- package/package.json +56 -0
- package/src/consts/block.ts +22 -0
- package/src/consts/color.ts +6 -0
- package/src/consts/config.ts +2 -0
- package/src/consts/dimension.ts +69 -0
- package/src/consts/hatch.ts +52 -0
- package/src/consts/header.ts +10 -0
- package/src/consts/index.ts +9 -0
- package/src/consts/lineweight.ts +5 -0
- package/src/consts/measurement.ts +4 -0
- package/src/consts/obscuredLineTypes.ts +21 -0
- package/src/consts/scene.ts +2 -0
- package/src/consts/viewport.ts +62 -0
- package/src/index.ts +6 -0
- package/src/parser/AutoCadColorIndex.ts +265 -0
- package/src/parser/DxfArrayScanner.ts +161 -0
- package/src/parser/DxfParser.ts +108 -0
- package/src/parser/ParseHelpers.ts +124 -0
- package/src/parser/blocks/blocks.test.ts +97 -0
- package/src/parser/blocks/index.ts +88 -0
- package/src/parser/blocks/types.ts +15 -0
- package/src/parser/entities/arc/index.ts +2 -0
- package/src/parser/entities/arc/parser.ts +67 -0
- package/src/parser/entities/arc/types.ts +13 -0
- package/src/parser/entities/attdef/consts.ts +12 -0
- package/src/parser/entities/attdef/index.ts +3 -0
- package/src/parser/entities/attdef/parser.ts +119 -0
- package/src/parser/entities/attdef/types.ts +19 -0
- package/src/parser/entities/attribute/consts.ts +6 -0
- package/src/parser/entities/attribute/index.ts +3 -0
- package/src/parser/entities/attribute/parser.ts +198 -0
- package/src/parser/entities/attribute/types.ts +31 -0
- package/src/parser/entities/circle/index.ts +2 -0
- package/src/parser/entities/circle/parser.ts +58 -0
- package/src/parser/entities/circle/types.ts +11 -0
- package/src/parser/entities/dimension/common.ts +98 -0
- package/src/parser/entities/dimension/index.ts +24 -0
- package/src/parser/entities/dimension/types.ts +65 -0
- package/src/parser/entities/ellipse/index.ts +2 -0
- package/src/parser/entities/ellipse/parser.ts +66 -0
- package/src/parser/entities/ellipse/types.ts +13 -0
- package/src/parser/entities/entities.test.ts +86 -0
- package/src/parser/entities/hatch/boundaryPathData/edge.ts +190 -0
- package/src/parser/entities/hatch/boundaryPathData/index.ts +27 -0
- package/src/parser/entities/hatch/boundaryPathData/polyline.ts +43 -0
- package/src/parser/entities/hatch/boundaryPathData/shared.ts +16 -0
- package/src/parser/entities/hatch/index.ts +189 -0
- package/src/parser/entities/hatch/patternData.ts +57 -0
- package/src/parser/entities/hatch/types/boundaryPath.ts +72 -0
- package/src/parser/entities/hatch/types/definitionLine.ts +14 -0
- package/src/parser/entities/hatch/types/hatch.ts +47 -0
- package/src/parser/entities/hatch/types/index.ts +3 -0
- package/src/parser/entities/index.ts +87 -0
- package/src/parser/entities/insert/index.ts +2 -0
- package/src/parser/entities/insert/parser.ts +106 -0
- package/src/parser/entities/insert/types.ts +19 -0
- package/src/parser/entities/leader/consts.ts +6 -0
- package/src/parser/entities/leader/index.ts +3 -0
- package/src/parser/entities/leader/parser.ts +119 -0
- package/src/parser/entities/leader/types.ts +24 -0
- package/src/parser/entities/line/index.ts +2 -0
- package/src/parser/entities/line/parser.ts +55 -0
- package/src/parser/entities/line/types.ts +11 -0
- package/src/parser/entities/lwpolyline/consts.ts +4 -0
- package/src/parser/entities/lwpolyline/index.ts +3 -0
- package/src/parser/entities/lwpolyline/parser.ts +122 -0
- package/src/parser/entities/lwpolyline/types.ts +21 -0
- package/src/parser/entities/mtext/consts.ts +5 -0
- package/src/parser/entities/mtext/index.ts +3 -0
- package/src/parser/entities/mtext/parser.ts +195 -0
- package/src/parser/entities/mtext/types.ts +34 -0
- package/src/parser/entities/point/index.ts +2 -0
- package/src/parser/entities/point/parser.ts +60 -0
- package/src/parser/entities/point/types.ts +10 -0
- package/src/parser/entities/polyline/consts.ts +17 -0
- package/src/parser/entities/polyline/index.ts +3 -0
- package/src/parser/entities/polyline/parser.ts +126 -0
- package/src/parser/entities/polyline/types.ts +20 -0
- package/src/parser/entities/section/index.ts +2 -0
- package/src/parser/entities/section/parser.ts +104 -0
- package/src/parser/entities/section/types.ts +20 -0
- package/src/parser/entities/shared.ts +166 -0
- package/src/parser/entities/solid/index.ts +2 -0
- package/src/parser/entities/solid/parser.ts +56 -0
- package/src/parser/entities/solid/types.ts +10 -0
- package/src/parser/entities/spline/consts.ts +8 -0
- package/src/parser/entities/spline/index.ts +3 -0
- package/src/parser/entities/spline/parser.ts +121 -0
- package/src/parser/entities/spline/types.ts +22 -0
- package/src/parser/entities/text/consts.ts +21 -0
- package/src/parser/entities/text/index.ts +3 -0
- package/src/parser/entities/text/parser.ts +117 -0
- package/src/parser/entities/text/types.ts +21 -0
- package/src/parser/entities/vertex/consts.ts +10 -0
- package/src/parser/entities/vertex/index.ts +3 -0
- package/src/parser/entities/vertex/parser.ts +90 -0
- package/src/parser/entities/vertex/types.ts +17 -0
- package/src/parser/entities/viewport/index.ts +178 -0
- package/src/parser/entities/viewport/types.ts +58 -0
- package/src/parser/filterDummyBlocks.ts +49 -0
- package/src/parser/header/index.ts +30 -0
- package/src/parser/objects/common.ts +55 -0
- package/src/parser/objects/dictionary.ts +61 -0
- package/src/parser/objects/index.ts +45 -0
- package/src/parser/objects/layout.ts +136 -0
- package/src/parser/objects/plotSettings.ts +231 -0
- package/src/parser/objects/treefy.ts +53 -0
- package/src/parser/objects/types.ts +17 -0
- package/src/parser/shared/index.ts +21 -0
- package/src/parser/shared/parsePoint.ts +37 -0
- package/src/parser/shared/parserGenerator.test.ts +164 -0
- package/src/parser/shared/parserGenerator.ts +180 -0
- package/src/parser/shared/xdata/index.ts +3 -0
- package/src/parser/shared/xdata/interpreter.test.ts +85 -0
- package/src/parser/shared/xdata/interpreter.ts +65 -0
- package/src/parser/shared/xdata/parser.test.ts +66 -0
- package/src/parser/shared/xdata/parser.ts +140 -0
- package/src/parser/shared/xdata/types.ts +22 -0
- package/src/parser/tables/blockRecord/index.ts +2 -0
- package/src/parser/tables/blockRecord/parser.ts +49 -0
- package/src/parser/tables/blockRecord/types.ts +11 -0
- package/src/parser/tables/dimStyle/consts.ts +428 -0
- package/src/parser/tables/dimStyle/index.ts +3 -0
- package/src/parser/tables/dimStyle/parser.ts +37 -0
- package/src/parser/tables/dimStyle/types.ts +164 -0
- package/src/parser/tables/index.ts +2 -0
- package/src/parser/tables/layer/index.ts +2 -0
- package/src/parser/tables/layer/parser.ts +58 -0
- package/src/parser/tables/layer/types.ts +14 -0
- package/src/parser/tables/ltype/consts.ts +6 -0
- package/src/parser/tables/ltype/index.ts +3 -0
- package/src/parser/tables/ltype/parser.ts +110 -0
- package/src/parser/tables/ltype/types.ts +23 -0
- package/src/parser/tables/parser.ts +100 -0
- package/src/parser/tables/shared.ts +32 -0
- package/src/parser/tables/style/index.ts +2 -0
- package/src/parser/tables/style/parser.ts +70 -0
- package/src/parser/tables/style/types.ts +15 -0
- package/src/parser/tables/types.ts +15 -0
- package/src/parser/tables/vport/index.ts +2 -0
- package/src/parser/tables/vport/parser.ts +205 -0
- package/src/parser/tables/vport/types.ts +48 -0
- package/src/parser/types.ts +30 -0
- package/src/types/color.ts +5 -0
- package/src/types/dxfHeader.ts +21 -0
- package/src/types/entity.ts +7 -0
- package/src/types/index.ts +30 -0
- package/src/utils/binarySearch.ts +20 -0
- package/src/utils/disjointSet.ts +52 -0
- package/src/utils/flooding.ts +37 -0
- package/src/utils/functional.ts +124 -0
- package/src/utils/graph.ts +53 -0
- package/src/utils/index.ts +102 -0
- package/src/utils/queue.ts +77 -0
- package/src/utils/triangle.ts +28 -0
- package/tsconfig.json +34 -0
- package/webpack.config.js +46 -0
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
import { DXFParserSnippet, Identity } from '../shared/parserGenerator';
|
|
2
|
+
import type { CommonDXFObject } from './common';
|
|
3
|
+
|
|
4
|
+
export const PlotSettingsSnippets: DXFParserSnippet[] = [
|
|
5
|
+
{
|
|
6
|
+
code: 333,
|
|
7
|
+
name: 'shadePlotId',
|
|
8
|
+
parser: Identity,
|
|
9
|
+
},
|
|
10
|
+
{
|
|
11
|
+
code: 149,
|
|
12
|
+
name: 'imageOriginY',
|
|
13
|
+
parser: Identity,
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
code: 148,
|
|
17
|
+
name: 'imageOriginX',
|
|
18
|
+
parser: Identity,
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
code: 147,
|
|
22
|
+
name: 'scaleFactor',
|
|
23
|
+
parser: Identity,
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
code: 78,
|
|
27
|
+
name: 'shadePlotCustomDPI',
|
|
28
|
+
parser: Identity,
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
code: 77,
|
|
32
|
+
name: 'shadePlotResolution',
|
|
33
|
+
parser: Identity,
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
code: 76,
|
|
37
|
+
name: 'shadePlotMode',
|
|
38
|
+
parser: Identity,
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
code: 75,
|
|
42
|
+
name: 'standardScaleType',
|
|
43
|
+
parser: Identity,
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
code: 7,
|
|
47
|
+
name: 'currentStyleSheet',
|
|
48
|
+
parser: Identity,
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
code: 74,
|
|
52
|
+
name: 'plotType',
|
|
53
|
+
parser: Identity,
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
code: 73,
|
|
57
|
+
name: 'plotRotation',
|
|
58
|
+
parser: Identity,
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
code: 72,
|
|
62
|
+
name: 'paperUnit',
|
|
63
|
+
parser: Identity,
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
code: 70,
|
|
67
|
+
name: 'layoutFlag',
|
|
68
|
+
parser: Identity,
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
code: 143,
|
|
72
|
+
name: 'printScaleDenominator',
|
|
73
|
+
parser: Identity,
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
code: 142,
|
|
77
|
+
name: 'printScaleNominator',
|
|
78
|
+
parser: Identity,
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
code: 141,
|
|
82
|
+
name: 'windowAreaYMax',
|
|
83
|
+
parser: Identity,
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
code: 140,
|
|
87
|
+
name: 'windowAreaYMin',
|
|
88
|
+
parser: Identity,
|
|
89
|
+
},
|
|
90
|
+
{
|
|
91
|
+
code: 49,
|
|
92
|
+
name: 'windowAreaXMax',
|
|
93
|
+
parser: Identity,
|
|
94
|
+
},
|
|
95
|
+
{
|
|
96
|
+
code: 48,
|
|
97
|
+
name: 'windowAreaXMin',
|
|
98
|
+
parser: Identity,
|
|
99
|
+
},
|
|
100
|
+
{
|
|
101
|
+
code: 47,
|
|
102
|
+
name: 'plotOriginY',
|
|
103
|
+
parser: Identity,
|
|
104
|
+
},
|
|
105
|
+
{
|
|
106
|
+
code: 46,
|
|
107
|
+
name: 'plotOriginX',
|
|
108
|
+
parser: Identity,
|
|
109
|
+
},
|
|
110
|
+
{
|
|
111
|
+
code: 45,
|
|
112
|
+
name: 'paperHeight',
|
|
113
|
+
parser: Identity,
|
|
114
|
+
},
|
|
115
|
+
{
|
|
116
|
+
code: 44,
|
|
117
|
+
name: 'paperWidth',
|
|
118
|
+
parser: Identity,
|
|
119
|
+
},
|
|
120
|
+
{
|
|
121
|
+
code: 43,
|
|
122
|
+
name: 'marginTop',
|
|
123
|
+
parser: Identity,
|
|
124
|
+
},
|
|
125
|
+
{
|
|
126
|
+
code: 42,
|
|
127
|
+
name: 'marginRight',
|
|
128
|
+
parser: Identity,
|
|
129
|
+
},
|
|
130
|
+
{
|
|
131
|
+
code: 41,
|
|
132
|
+
name: 'marginBottom',
|
|
133
|
+
parser: Identity,
|
|
134
|
+
},
|
|
135
|
+
{
|
|
136
|
+
code: 40,
|
|
137
|
+
name: 'marginLeft',
|
|
138
|
+
parser: Identity,
|
|
139
|
+
},
|
|
140
|
+
{
|
|
141
|
+
code: 6,
|
|
142
|
+
name: 'plotViewName',
|
|
143
|
+
parser: Identity,
|
|
144
|
+
},
|
|
145
|
+
{
|
|
146
|
+
code: 4,
|
|
147
|
+
name: 'paperSize',
|
|
148
|
+
parser: Identity,
|
|
149
|
+
},
|
|
150
|
+
{
|
|
151
|
+
code: 2,
|
|
152
|
+
name: 'configName',
|
|
153
|
+
parser: Identity,
|
|
154
|
+
},
|
|
155
|
+
{
|
|
156
|
+
code: 1,
|
|
157
|
+
name: 'pageSetupName',
|
|
158
|
+
parser: Identity,
|
|
159
|
+
},
|
|
160
|
+
{
|
|
161
|
+
code: 100,
|
|
162
|
+
name: 'subclassMarker',
|
|
163
|
+
parser: Identity,
|
|
164
|
+
},
|
|
165
|
+
];
|
|
166
|
+
|
|
167
|
+
export enum PlotPaperUnit {
|
|
168
|
+
INCHES = 0,
|
|
169
|
+
MILLIMETERS = 1,
|
|
170
|
+
PIXELS = 2,
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
export enum PlotType {
|
|
174
|
+
LAST_SCREEN_DISPLAY = 0,
|
|
175
|
+
DRAWING_EXTENTS = 1,
|
|
176
|
+
DRAWING_LIMITS = 2,
|
|
177
|
+
VIEW_SPECIFIED = 3, // specified in plotViewName
|
|
178
|
+
WINDOW_SPECIFIED = 4, // specified in plotViewName
|
|
179
|
+
LAYOUT_INFORMATION = 5,
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
export enum ShadePlotMode {
|
|
183
|
+
AS_DISPLAYED = 0,
|
|
184
|
+
WIREFRAME = 1,
|
|
185
|
+
HIDDEN = 2,
|
|
186
|
+
RENDERED = 3,
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
export enum ShadePlotResolution {
|
|
190
|
+
DRAFT = 0,
|
|
191
|
+
PREVIEW = 1,
|
|
192
|
+
NORMAL = 2,
|
|
193
|
+
PRESENTATION = 3,
|
|
194
|
+
MAXIMUM = 4,
|
|
195
|
+
CUSTOM = 5,
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
export interface PlotSettingDXFObject extends CommonDXFObject {
|
|
199
|
+
subclassMarker: 'AcDbPlotSettings7';
|
|
200
|
+
pageSetupName: string;
|
|
201
|
+
configName: string;
|
|
202
|
+
paperSize: string;
|
|
203
|
+
plotViewName: string;
|
|
204
|
+
marginLeft: number;
|
|
205
|
+
marginBottom: number;
|
|
206
|
+
marginRight: number;
|
|
207
|
+
marginTop: number;
|
|
208
|
+
paperWidth: number;
|
|
209
|
+
paperHeight: number;
|
|
210
|
+
plotOriginX: number;
|
|
211
|
+
plotOriginY: number;
|
|
212
|
+
windowAreaXMin: number;
|
|
213
|
+
windowAreaYMin: number;
|
|
214
|
+
windowAreaXMax: number;
|
|
215
|
+
windowAreaYMax: number;
|
|
216
|
+
printScaleNominator: number;
|
|
217
|
+
printScaleDenominator: number;
|
|
218
|
+
layoutFlag: number;
|
|
219
|
+
plotPaperUnit: PlotPaperUnit;
|
|
220
|
+
plotRotation: 0 | 1 | 2 | 3;
|
|
221
|
+
plotType: PlotType;
|
|
222
|
+
currentStyleSheet: string;
|
|
223
|
+
standardScaleType: number; // see https://help.autodesk.com/view/OARX/2023/ENU/?guid=GUID-1113675E-AB07-4567-801A-310CDE0D56E9
|
|
224
|
+
shadePlotMode: ShadePlotMode;
|
|
225
|
+
shadePlotResolution: ShadePlotResolution;
|
|
226
|
+
shadePlotCustomDPI?: number; // 100 ~ 32767
|
|
227
|
+
scaleFactor: number;
|
|
228
|
+
imageOriginX: number;
|
|
229
|
+
imageOriginY: number;
|
|
230
|
+
shadePlotId: string;
|
|
231
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import type { HydratedDxfObject, DxfDictionary, DxfObject } from './types';
|
|
2
|
+
|
|
3
|
+
export function createObjectTree(objects: DxfObject[]) {
|
|
4
|
+
const objectMap = Object.fromEntries(
|
|
5
|
+
objects.map((object) => [object.handle, object as HydratedDxfObject]),
|
|
6
|
+
);
|
|
7
|
+
|
|
8
|
+
for (const object of objects) {
|
|
9
|
+
hydrateParent(object, objectMap);
|
|
10
|
+
hydrateDictionary(object, objectMap);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
return objects[0];
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function hydrateParent(
|
|
17
|
+
object: HydratedDxfObject,
|
|
18
|
+
objectMap: Record<string, HydratedDxfObject>,
|
|
19
|
+
) {
|
|
20
|
+
if (isDictionary(object) && object.ownerDictionaryIdSoft === '0') return;
|
|
21
|
+
|
|
22
|
+
if (object.ownerDictionaryIdSoft) {
|
|
23
|
+
object.ownerDictionarySoft = objectMap[object.ownerDictionaryIdSoft];
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
if (object.ownerDictionaryIdHard) {
|
|
27
|
+
object.ownerDictionaryHard = objectMap[object.ownerDictionaryIdHard];
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
if (object.ownerObjectId) {
|
|
31
|
+
object.ownerObject = objectMap[object.ownerObjectId];
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function hydrateDictionary(
|
|
36
|
+
object: HydratedDxfObject,
|
|
37
|
+
objectMap: Record<string, HydratedDxfObject>,
|
|
38
|
+
) {
|
|
39
|
+
if (!isDictionary(object) || !object.entries) return;
|
|
40
|
+
|
|
41
|
+
object.entries = Object.fromEntries(
|
|
42
|
+
(object.entries as any).map(
|
|
43
|
+
({ name, objectId }: Record<string, string>) => [
|
|
44
|
+
name,
|
|
45
|
+
objectMap[objectId],
|
|
46
|
+
],
|
|
47
|
+
),
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function isDictionary(object: HydratedDxfObject): object is DxfDictionary {
|
|
52
|
+
return (object as any).subclassMarker === 'AcDbDictionary';
|
|
53
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export interface DxfObject {
|
|
2
|
+
name: string;
|
|
3
|
+
handle: string;
|
|
4
|
+
ownerDictionaryIdSoft: string;
|
|
5
|
+
ownerDictionaryIdHard?: string;
|
|
6
|
+
ownerObjectId?: string;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export interface HydratedDxfObject extends DxfObject {
|
|
10
|
+
ownerDictionarySoft?: HydratedDxfObject;
|
|
11
|
+
ownerDictionaryHard?: HydratedDxfObject;
|
|
12
|
+
ownerObject?: HydratedDxfObject;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export interface DxfDictionary extends HydratedDxfObject {
|
|
16
|
+
entries?: Record<string, HydratedDxfObject>;
|
|
17
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { ScannerGroup } from '../DxfArrayScanner';
|
|
2
|
+
|
|
3
|
+
let lastHandle = 0;
|
|
4
|
+
|
|
5
|
+
export function ensureHandle(entity: any) {
|
|
6
|
+
if (!entity) {
|
|
7
|
+
throw new TypeError('entity cannot be undefined or null');
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
if (!entity.handle) {
|
|
11
|
+
entity.handle = lastHandle++;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* group이 `code`와 `value`를 갖는지 확인
|
|
17
|
+
* `value`가 `null`이나 `undefined`인 경우 아무거나 다 매칭한다고 가정한다.
|
|
18
|
+
*/
|
|
19
|
+
export function isMatched(group: ScannerGroup, code: number, value?: any) {
|
|
20
|
+
return group.code === code && (value == null || group.value === value);
|
|
21
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { Point2D, Point3D } from '../../types';
|
|
2
|
+
import type DxfArrayScanner from '../DxfArrayScanner';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Parses the 2D or 3D coordinate, vector, or point. When complete,
|
|
6
|
+
* the scanner remains on the last group of the coordinate.
|
|
7
|
+
*/
|
|
8
|
+
export function parsePoint(scanner: DxfArrayScanner): Point3D | Point2D {
|
|
9
|
+
const point = {} as Point3D;
|
|
10
|
+
|
|
11
|
+
// Reread group for the first coordinate
|
|
12
|
+
scanner.rewind();
|
|
13
|
+
let curr = scanner.next();
|
|
14
|
+
const firstCode = curr.code;
|
|
15
|
+
point.x = curr.value as number;
|
|
16
|
+
|
|
17
|
+
curr = scanner.next();
|
|
18
|
+
if (curr.code !== firstCode + 10)
|
|
19
|
+
throw new Error(
|
|
20
|
+
'Expected code for point value to be ' +
|
|
21
|
+
20 +
|
|
22
|
+
' but got ' +
|
|
23
|
+
curr.code +
|
|
24
|
+
'.',
|
|
25
|
+
);
|
|
26
|
+
point.y = curr.value as number;
|
|
27
|
+
|
|
28
|
+
curr = scanner.next();
|
|
29
|
+
if (curr.code !== firstCode + 20) {
|
|
30
|
+
// Only the x and y are specified. Don't read z.
|
|
31
|
+
scanner.rewind(); // Let the calling code advance off the point
|
|
32
|
+
return point;
|
|
33
|
+
}
|
|
34
|
+
point.z = curr.value as number;
|
|
35
|
+
|
|
36
|
+
return point;
|
|
37
|
+
}
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
import DxfArrayScanner from '../DxfArrayScanner';
|
|
2
|
+
import { createParser, Identity } from './parserGenerator';
|
|
3
|
+
|
|
4
|
+
describe('createParser', () => {
|
|
5
|
+
it('snippet을 소모하고 name이 있으면 object에 넣어야 한다.', () => {
|
|
6
|
+
const parse = createParser([
|
|
7
|
+
{
|
|
8
|
+
code: 1,
|
|
9
|
+
name: 'a',
|
|
10
|
+
parser: Identity,
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
code: 2,
|
|
14
|
+
name: 'b',
|
|
15
|
+
parser: Identity,
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
code: 3,
|
|
19
|
+
name: 'c',
|
|
20
|
+
parser: Identity,
|
|
21
|
+
},
|
|
22
|
+
]);
|
|
23
|
+
|
|
24
|
+
const scanner = new DxfArrayScanner([
|
|
25
|
+
'1',
|
|
26
|
+
'x',
|
|
27
|
+
'2',
|
|
28
|
+
'y',
|
|
29
|
+
'3',
|
|
30
|
+
'z',
|
|
31
|
+
'0',
|
|
32
|
+
'EOF',
|
|
33
|
+
]);
|
|
34
|
+
|
|
35
|
+
const obj = {} as any;
|
|
36
|
+
let curr = scanner.next();
|
|
37
|
+
parse(curr, scanner, obj);
|
|
38
|
+
|
|
39
|
+
expect(obj.a).toBe('x');
|
|
40
|
+
expect(obj.b).toBe('y');
|
|
41
|
+
expect(obj.c).toBe('z');
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
it('정해진 snippet을 소모하고 나면 파서가 종료해야한다.', () => {
|
|
45
|
+
const parse = createParser([
|
|
46
|
+
{
|
|
47
|
+
code: 1,
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
code: 2,
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
code: 3,
|
|
54
|
+
},
|
|
55
|
+
]);
|
|
56
|
+
|
|
57
|
+
const scanner = new DxfArrayScanner([
|
|
58
|
+
'1',
|
|
59
|
+
'',
|
|
60
|
+
'2',
|
|
61
|
+
'',
|
|
62
|
+
'3',
|
|
63
|
+
'',
|
|
64
|
+
'1',
|
|
65
|
+
'',
|
|
66
|
+
'2',
|
|
67
|
+
'',
|
|
68
|
+
'0',
|
|
69
|
+
'EOF',
|
|
70
|
+
]);
|
|
71
|
+
|
|
72
|
+
let curr = scanner.next();
|
|
73
|
+
parse(curr, scanner, {});
|
|
74
|
+
|
|
75
|
+
curr = scanner.next();
|
|
76
|
+
expect(curr.code).toBe(1);
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
it('isMultiple이 걸린 경우 snippet을 소모해선 안된다', () => {
|
|
80
|
+
const parse = createParser([
|
|
81
|
+
{
|
|
82
|
+
code: 1,
|
|
83
|
+
name: 'a',
|
|
84
|
+
isMultiple: true,
|
|
85
|
+
parser: Identity,
|
|
86
|
+
},
|
|
87
|
+
{
|
|
88
|
+
code: 2,
|
|
89
|
+
name: 'b',
|
|
90
|
+
parser: Identity,
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
code: 3,
|
|
94
|
+
name: 'c',
|
|
95
|
+
parser: Identity,
|
|
96
|
+
},
|
|
97
|
+
]);
|
|
98
|
+
|
|
99
|
+
const scanner = new DxfArrayScanner([
|
|
100
|
+
'1',
|
|
101
|
+
'x',
|
|
102
|
+
'2',
|
|
103
|
+
'y',
|
|
104
|
+
'3',
|
|
105
|
+
'z',
|
|
106
|
+
'1',
|
|
107
|
+
'u',
|
|
108
|
+
'2',
|
|
109
|
+
'v',
|
|
110
|
+
'0',
|
|
111
|
+
'EOF',
|
|
112
|
+
]);
|
|
113
|
+
|
|
114
|
+
const obj = {} as any;
|
|
115
|
+
let curr = scanner.next();
|
|
116
|
+
parse(curr, scanner, obj);
|
|
117
|
+
|
|
118
|
+
expect(obj.a instanceof Array).toBeTruthy();
|
|
119
|
+
expect(obj.a[0]).toBe('x');
|
|
120
|
+
expect(obj.a[1]).toBe('u');
|
|
121
|
+
|
|
122
|
+
curr = scanner.next();
|
|
123
|
+
expect(curr.code).toBe(2);
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
it('pushContext가 걸린 경우, 새로 생긴 맥락부터 뒤져본다.', () => {
|
|
127
|
+
const parse = createParser([
|
|
128
|
+
{
|
|
129
|
+
code: 1,
|
|
130
|
+
name: 'a',
|
|
131
|
+
parser: Identity,
|
|
132
|
+
},
|
|
133
|
+
{
|
|
134
|
+
code: 2,
|
|
135
|
+
name: 'b',
|
|
136
|
+
parser: Identity,
|
|
137
|
+
pushContext: true,
|
|
138
|
+
},
|
|
139
|
+
{
|
|
140
|
+
code: 1,
|
|
141
|
+
name: 'c',
|
|
142
|
+
parser: Identity,
|
|
143
|
+
},
|
|
144
|
+
]);
|
|
145
|
+
|
|
146
|
+
const scanner = new DxfArrayScanner([
|
|
147
|
+
'1',
|
|
148
|
+
'x',
|
|
149
|
+
'2',
|
|
150
|
+
'y',
|
|
151
|
+
'1',
|
|
152
|
+
'z',
|
|
153
|
+
'0',
|
|
154
|
+
'EOF',
|
|
155
|
+
]);
|
|
156
|
+
|
|
157
|
+
const obj = {} as any;
|
|
158
|
+
let curr = scanner.next();
|
|
159
|
+
parse(curr, scanner, obj);
|
|
160
|
+
|
|
161
|
+
expect(obj.a).toBe('z');
|
|
162
|
+
expect(obj.c).toBe('x');
|
|
163
|
+
});
|
|
164
|
+
});
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
import { isMatched } from '.';
|
|
2
|
+
import type DxfArrayScanner from '../DxfArrayScanner';
|
|
3
|
+
import { ScannerGroup } from '../DxfArrayScanner';
|
|
4
|
+
import { parsePoint } from './parsePoint';
|
|
5
|
+
|
|
6
|
+
export const Abort = Symbol();
|
|
7
|
+
|
|
8
|
+
// Snippet은 스트림에서 code를 읽었을 때 적절한 원자 파서를
|
|
9
|
+
// 대응해 주는 역할을 한다.
|
|
10
|
+
// Snippet은 스택에 쌓이며, 여러 Snippet이 동일한 code를
|
|
11
|
+
// 소비할 수 있을 때, 가장 위에 쌓인 Snippet이 우선순위를 갖는다.
|
|
12
|
+
export interface DXFParserSnippet {
|
|
13
|
+
code: number | number[]; // 복수의 코드를 다 한 곳으로 몰아넣기
|
|
14
|
+
name?: string; // 파싱한 값을 넣을 오브젝트 속성 명, 없으면 등록 안하고 패스함
|
|
15
|
+
|
|
16
|
+
// 추가로 더 읽어야 할 때만 scanner 사용하고, 새 값은 읽어놓지 말 것
|
|
17
|
+
// 반환되는 값이 entity[name]에 대입되므로 특별한 경우엔 entity 건들지 말고
|
|
18
|
+
// 사용할 경우, 최종값이 반환되도록 잘 건드릴 것
|
|
19
|
+
// 만약 Abort 심볼이 반환될 경우 값에 대입하지 않고 읽은 것을 한 칸 되돌리고
|
|
20
|
+
// 종료함
|
|
21
|
+
parser?(curr: ScannerGroup, scanner: DxfArrayScanner, entity: any): any;
|
|
22
|
+
isMultiple?: boolean; // code가 여러 번 들어올 수 있는 경우, true로 표기
|
|
23
|
+
|
|
24
|
+
// https://github.com/connect-for-you/cadview-front/issues/41
|
|
25
|
+
// 이 스니펫을 기점으로 맥락을 바꿈
|
|
26
|
+
pushContext?: boolean;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// 만약 파서가 어떤 유의미한 snippet도 찾지 못한 경우 전진하지 말고 false 반환
|
|
30
|
+
// 만약 파서가 유의미한 snippet을 찾아서 사용한 경우 반드시 한 칸 전진시키고 true 반환
|
|
31
|
+
// 즉 새로운 애를 하나는 무조건 읽어놓음
|
|
32
|
+
export type DXFParser = (
|
|
33
|
+
curr: ScannerGroup,
|
|
34
|
+
scanner: DxfArrayScanner,
|
|
35
|
+
target: any,
|
|
36
|
+
) => boolean;
|
|
37
|
+
|
|
38
|
+
export function createParser(
|
|
39
|
+
snippets: DXFParserSnippet[],
|
|
40
|
+
defaultObject?: any,
|
|
41
|
+
): DXFParser {
|
|
42
|
+
return (curr, scanner, target) => {
|
|
43
|
+
const snippetMaps = createSnippetMaps(snippets);
|
|
44
|
+
let isReadOnce = false;
|
|
45
|
+
let contextIndex = snippetMaps.length - 1;
|
|
46
|
+
|
|
47
|
+
while (!isMatched(curr, 0, 'EOF')) {
|
|
48
|
+
const snippetMap = findSnippetMap(
|
|
49
|
+
snippetMaps,
|
|
50
|
+
curr.code,
|
|
51
|
+
contextIndex,
|
|
52
|
+
);
|
|
53
|
+
const snippet = snippetMap?.[curr.code].at(-1);
|
|
54
|
+
|
|
55
|
+
if (!snippetMap || !snippet) {
|
|
56
|
+
scanner.rewind();
|
|
57
|
+
break;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if (!snippet.isMultiple) {
|
|
61
|
+
snippetMap[curr.code].pop();
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const { name, parser, isMultiple } = snippet;
|
|
65
|
+
const parsedValue = parser?.(curr, scanner, target);
|
|
66
|
+
|
|
67
|
+
if (parsedValue === Abort) {
|
|
68
|
+
scanner.rewind();
|
|
69
|
+
break;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
if (name) {
|
|
73
|
+
const [leaf, fieldName] = getObjectByPath(target, name);
|
|
74
|
+
|
|
75
|
+
if (isMultiple) {
|
|
76
|
+
// prototype으로 디폴트값 넣어준 경우 nullish coalescing으로 해결 안됨
|
|
77
|
+
// @ts-ignore
|
|
78
|
+
if (!Object.hasOwn(leaf, fieldName)) {
|
|
79
|
+
leaf[fieldName] = [];
|
|
80
|
+
}
|
|
81
|
+
leaf[fieldName].push(parsedValue);
|
|
82
|
+
} else {
|
|
83
|
+
leaf[fieldName] = parsedValue;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
if (snippet.pushContext) {
|
|
88
|
+
contextIndex -= 1;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
isReadOnce = true;
|
|
92
|
+
curr = scanner.next();
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
if (defaultObject) {
|
|
96
|
+
Object.setPrototypeOf(target, defaultObject);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
return isReadOnce;
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function createSnippetMaps(snippets: DXFParserSnippet[]) {
|
|
104
|
+
return snippets.reduce(
|
|
105
|
+
(acc, snippet) => {
|
|
106
|
+
if (snippet.pushContext) {
|
|
107
|
+
acc.push({});
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
const context = acc[acc.length - 1];
|
|
111
|
+
const codes =
|
|
112
|
+
typeof snippet.code === 'number'
|
|
113
|
+
? [snippet.code]
|
|
114
|
+
: snippet.code;
|
|
115
|
+
|
|
116
|
+
for (const code of codes) {
|
|
117
|
+
const bin = (context[code] ??= []);
|
|
118
|
+
|
|
119
|
+
if (snippet.isMultiple && bin.length) {
|
|
120
|
+
console.warn(
|
|
121
|
+
`Snippet ${bin.at(-1)!.name
|
|
122
|
+
} for code(${code}) is shadowed by ${snippet.name}`,
|
|
123
|
+
);
|
|
124
|
+
}
|
|
125
|
+
bin.push(snippet);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
return acc;
|
|
129
|
+
},
|
|
130
|
+
[{}] as Record<number, DXFParserSnippet[]>[],
|
|
131
|
+
);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function findSnippetMap(
|
|
135
|
+
snippetMaps: Record<number, DXFParserSnippet[]>[],
|
|
136
|
+
code: number,
|
|
137
|
+
contextIndex: number,
|
|
138
|
+
) {
|
|
139
|
+
return snippetMaps.find(
|
|
140
|
+
(map, index) => index >= contextIndex && map[code]?.length,
|
|
141
|
+
);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* path를 .으로 나누고, 그 서브패스의 값을 설정할 수 있게 함.
|
|
146
|
+
* 값이 없으면 알아서 만듦
|
|
147
|
+
*
|
|
148
|
+
* ex) a.b -> target[a]를 반환하여 b를 대입할 수 있게 함
|
|
149
|
+
* a.b.c -> target[a][b]를 반환하여 c를 대입할 수 있게 함
|
|
150
|
+
*
|
|
151
|
+
* @param target .으로 구분된 경로
|
|
152
|
+
* @param path
|
|
153
|
+
* @return [finalTargetObject, name]
|
|
154
|
+
*/
|
|
155
|
+
function getObjectByPath(target: any, path: string) {
|
|
156
|
+
const fragments = path.split('.');
|
|
157
|
+
|
|
158
|
+
let currentTarget = target;
|
|
159
|
+
for (let depth = 0; depth < fragments.length - 1; ++depth) {
|
|
160
|
+
const currentName = fragments[depth];
|
|
161
|
+
// @ts-ignore
|
|
162
|
+
if (!Object.hasOwn(currentTarget, currentName)) {
|
|
163
|
+
currentTarget[currentName] = {};
|
|
164
|
+
}
|
|
165
|
+
currentTarget = currentTarget[currentName];
|
|
166
|
+
}
|
|
167
|
+
return [currentTarget, fragments.at(-1)!];
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
export function Identity({ value }: ScannerGroup) {
|
|
171
|
+
return value;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
export function PointParser(_: any, scanner: DxfArrayScanner) {
|
|
175
|
+
return parsePoint(scanner);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
export function ToBoolean({ value }: ScannerGroup) {
|
|
179
|
+
return !!value;
|
|
180
|
+
}
|