@things-factory/board-import 10.0.0-beta.108

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (137) hide show
  1. package/config/config.development.js +44 -0
  2. package/config/config.production.js +18 -0
  3. package/dist-server/index.d.ts +7 -0
  4. package/dist-server/index.js +11 -0
  5. package/dist-server/index.js.map +1 -0
  6. package/dist-server/service/adapters/dxf-adapter.d.ts +8 -0
  7. package/dist-server/service/adapters/dxf-adapter.js +220 -0
  8. package/dist-server/service/adapters/dxf-adapter.js.map +1 -0
  9. package/dist-server/service/adapters/image-adapter.d.ts +32 -0
  10. package/dist-server/service/adapters/image-adapter.js +420 -0
  11. package/dist-server/service/adapters/image-adapter.js.map +1 -0
  12. package/dist-server/service/ai/types.d.ts +185 -0
  13. package/dist-server/service/ai/types.js +378 -0
  14. package/dist-server/service/ai/types.js.map +1 -0
  15. package/dist-server/service/converters/generic-to.d.ts +31 -0
  16. package/dist-server/service/converters/generic-to.js +84 -0
  17. package/dist-server/service/converters/generic-to.js.map +1 -0
  18. package/dist-server/service/extraction/contracts.d.ts +112 -0
  19. package/dist-server/service/extraction/contracts.js +3 -0
  20. package/dist-server/service/extraction/contracts.js.map +1 -0
  21. package/dist-server/service/extraction/index.d.ts +17 -0
  22. package/dist-server/service/extraction/index.js +62 -0
  23. package/dist-server/service/extraction/index.js.map +1 -0
  24. package/dist-server/service/extraction/label-scale-calibrator.d.ts +5 -0
  25. package/dist-server/service/extraction/label-scale-calibrator.js +111 -0
  26. package/dist-server/service/extraction/label-scale-calibrator.js.map +1 -0
  27. package/dist-server/service/extraction/layout-refiner.d.ts +16 -0
  28. package/dist-server/service/extraction/layout-refiner.js +88 -0
  29. package/dist-server/service/extraction/layout-refiner.js.map +1 -0
  30. package/dist-server/service/extraction/pipeline.d.ts +11 -0
  31. package/dist-server/service/extraction/pipeline.js +124 -0
  32. package/dist-server/service/extraction/pipeline.js.map +1 -0
  33. package/dist-server/service/extraction/source-router.d.ts +17 -0
  34. package/dist-server/service/extraction/source-router.js +40 -0
  35. package/dist-server/service/extraction/source-router.js.map +1 -0
  36. package/dist-server/service/extraction/stubs.d.ts +18 -0
  37. package/dist-server/service/extraction/stubs.js +38 -0
  38. package/dist-server/service/extraction/stubs.js.map +1 -0
  39. package/dist-server/service/extraction/vector-geometry.d.ts +7 -0
  40. package/dist-server/service/extraction/vector-geometry.js +21 -0
  41. package/dist-server/service/extraction/vector-geometry.js.map +1 -0
  42. package/dist-server/service/extraction/vlm-recognizer.d.ts +18 -0
  43. package/dist-server/service/extraction/vlm-recognizer.js +78 -0
  44. package/dist-server/service/extraction/vlm-recognizer.js.map +1 -0
  45. package/dist-server/service/import-session/import-actions.d.ts +79 -0
  46. package/dist-server/service/import-session/import-actions.js +98 -0
  47. package/dist-server/service/import-session/import-actions.js.map +1 -0
  48. package/dist-server/service/import-session/import-session-resolver.d.ts +39 -0
  49. package/dist-server/service/import-session/import-session-resolver.js +263 -0
  50. package/dist-server/service/import-session/import-session-resolver.js.map +1 -0
  51. package/dist-server/service/import-session/import-session.d.ts +28 -0
  52. package/dist-server/service/import-session/import-session.js +152 -0
  53. package/dist-server/service/import-session/import-session.js.map +1 -0
  54. package/dist-server/service/import-session/import-worker.d.ts +1 -0
  55. package/dist-server/service/import-session/import-worker.js +121 -0
  56. package/dist-server/service/import-session/import-worker.js.map +1 -0
  57. package/dist-server/service/import-session/index.d.ts +8 -0
  58. package/dist-server/service/import-session/index.js +19 -0
  59. package/dist-server/service/import-session/index.js.map +1 -0
  60. package/dist-server/service/import-session/materialize-from-session.d.ts +66 -0
  61. package/dist-server/service/import-session/materialize-from-session.js +104 -0
  62. package/dist-server/service/import-session/materialize-from-session.js.map +1 -0
  63. package/dist-server/service/import-session/suggest-board-name.d.ts +80 -0
  64. package/dist-server/service/import-session/suggest-board-name.js +195 -0
  65. package/dist-server/service/import-session/suggest-board-name.js.map +1 -0
  66. package/dist-server/service/import-tools.d.ts +10 -0
  67. package/dist-server/service/import-tools.js +227 -0
  68. package/dist-server/service/import-tools.js.map +1 -0
  69. package/dist-server/service/index.d.ts +26 -0
  70. package/dist-server/service/index.js +60 -0
  71. package/dist-server/service/index.js.map +1 -0
  72. package/dist-server/service/pipeline/index.d.ts +66 -0
  73. package/dist-server/service/pipeline/index.js +127 -0
  74. package/dist-server/service/pipeline/index.js.map +1 -0
  75. package/dist-server/service/pipeline/stage2-mapping.d.ts +29 -0
  76. package/dist-server/service/pipeline/stage2-mapping.js +231 -0
  77. package/dist-server/service/pipeline/stage2-mapping.js.map +1 -0
  78. package/dist-server/service/pipeline/stage3-board.d.ts +52 -0
  79. package/dist-server/service/pipeline/stage3-board.js +108 -0
  80. package/dist-server/service/pipeline/stage3-board.js.map +1 -0
  81. package/dist-server/service/pipeline/stage4-binding.d.ts +9 -0
  82. package/dist-server/service/pipeline/stage4-binding.js +28 -0
  83. package/dist-server/service/pipeline/stage4-binding.js.map +1 -0
  84. package/dist-server/service/registry/index.d.ts +27 -0
  85. package/dist-server/service/registry/index.js +108 -0
  86. package/dist-server/service/registry/index.js.map +1 -0
  87. package/dist-server/service/types/index.d.ts +152 -0
  88. package/dist-server/service/types/index.js +3 -0
  89. package/dist-server/service/types/index.js.map +1 -0
  90. package/dist-server/service/types/registry.d.ts +175 -0
  91. package/dist-server/service/types/registry.js +3 -0
  92. package/dist-server/service/types/registry.js.map +1 -0
  93. package/dist-server/tsconfig.tsbuildinfo +1 -0
  94. package/package.json +35 -0
  95. package/probe/gcp-vision-probe.mjs +114 -0
  96. package/probe/opencv-geometry-probe.py +98 -0
  97. package/server/index.ts +7 -0
  98. package/server/service/adapters/dxf-adapter.ts +230 -0
  99. package/server/service/adapters/image-adapter.test.ts +545 -0
  100. package/server/service/adapters/image-adapter.ts +464 -0
  101. package/server/service/ai/types.ts +619 -0
  102. package/server/service/converters/generic-to.test.ts +105 -0
  103. package/server/service/converters/generic-to.ts +121 -0
  104. package/server/service/extraction/contracts.ts +142 -0
  105. package/server/service/extraction/extraction.test.ts +374 -0
  106. package/server/service/extraction/index.ts +51 -0
  107. package/server/service/extraction/label-scale-calibrator.ts +124 -0
  108. package/server/service/extraction/layout-refiner.ts +104 -0
  109. package/server/service/extraction/pipeline.ts +163 -0
  110. package/server/service/extraction/source-router.ts +45 -0
  111. package/server/service/extraction/stubs.ts +46 -0
  112. package/server/service/extraction/vector-geometry.ts +26 -0
  113. package/server/service/extraction/vlm-recognizer.ts +87 -0
  114. package/server/service/import-session/import-actions.test.ts +185 -0
  115. package/server/service/import-session/import-actions.ts +164 -0
  116. package/server/service/import-session/import-session-resolver.ts +245 -0
  117. package/server/service/import-session/import-session.ts +141 -0
  118. package/server/service/import-session/import-worker.ts +132 -0
  119. package/server/service/import-session/index.ts +24 -0
  120. package/server/service/import-session/materialize-from-session.test.ts +274 -0
  121. package/server/service/import-session/materialize-from-session.ts +158 -0
  122. package/server/service/import-session/suggest-board-name.test.ts +271 -0
  123. package/server/service/import-session/suggest-board-name.ts +279 -0
  124. package/server/service/import-tools.test.ts +137 -0
  125. package/server/service/import-tools.ts +255 -0
  126. package/server/service/index.ts +117 -0
  127. package/server/service/pipeline/index.ts +184 -0
  128. package/server/service/pipeline/stage2-mapping.test.ts +204 -0
  129. package/server/service/pipeline/stage2-mapping.ts +270 -0
  130. package/server/service/pipeline/stage3-board.test.ts +133 -0
  131. package/server/service/pipeline/stage3-board.ts +179 -0
  132. package/server/service/pipeline/stage4-binding.ts +31 -0
  133. package/server/service/registry/index.ts +127 -0
  134. package/server/service/types/index.ts +169 -0
  135. package/server/service/types/registry.ts +186 -0
  136. package/things-factory.config.js +1 -0
  137. package/tsconfig.json +9 -0
@@ -0,0 +1,44 @@
1
+ module.exports = {
2
+ /*
3
+ Board Import Configuration
4
+
5
+ Settings for drawing → board conversion pipeline (DXF / IFC / image).
6
+ Override in your application's config to tune performance limits.
7
+ */
8
+ boardImport: {
9
+ /*
10
+ Maximum entities parsed from a single drawing. Performance guard.
11
+ Drawings with more entities are truncated; warning is recorded in
12
+ MappingResult.warnings.
13
+ */
14
+ maxEntities: 100000,
15
+
16
+ /*
17
+ Maximum concurrent ImportSession workers within a single process.
18
+ Higher = faster batch import, more memory.
19
+
20
+ Recommended: number of CPU cores.
21
+ */
22
+ maxConcurrentJobs: 4,
23
+
24
+ /*
25
+ Default scope(s) to use when ImportSession options.scopes is empty.
26
+ Application typically registers ImportRules under a domain scope (e.g. 'fmsim').
27
+
28
+ Example:
29
+ defaultScopes: ['fmsim']
30
+ */
31
+ // defaultScopes: undefined,
32
+
33
+ /*
34
+ Default geometry handling for assembled BoardModel.
35
+ These are applied when the corresponding ImportSession option is undefined.
36
+ */
37
+ defaults: {
38
+ normalizeOrigin: true, // shift bbox so minX,minY = 0
39
+ flipY: true, // CAD Y-up → scene Y-down
40
+ scale: 1, // 1mm = 1 scene unit (CAD standard)
41
+ applyBinding: true // run Stage 4 (subscription auto-mapping)
42
+ }
43
+ }
44
+ }
@@ -0,0 +1,18 @@
1
+ module.exports = {
2
+ /*
3
+ Board Import Configuration — Production
4
+
5
+ See config.development.js for the full reference. Production typically tunes
6
+ `maxConcurrentJobs` based on host capacity.
7
+ */
8
+ boardImport: {
9
+ maxEntities: 100000,
10
+ maxConcurrentJobs: 4,
11
+ defaults: {
12
+ normalizeOrigin: true,
13
+ flipY: true,
14
+ scale: 1,
15
+ applyBinding: true
16
+ }
17
+ }
18
+ }
@@ -0,0 +1,7 @@
1
+ /**
2
+ * @things-factory/board-import — 도면(CAD/이미지/IFC) → Board Model 백엔드 코어.
3
+ *
4
+ * Phase 1: DXF 결정적 파이프라인 (AI 미사용).
5
+ * Phase 2+: GraphQL/REST 엔드포인트, AI 통합.
6
+ */
7
+ export * from './service/index';
@@ -0,0 +1,11 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const tslib_1 = require("tslib");
4
+ /**
5
+ * @things-factory/board-import — 도면(CAD/이미지/IFC) → Board Model 백엔드 코어.
6
+ *
7
+ * Phase 1: DXF 결정적 파이프라인 (AI 미사용).
8
+ * Phase 2+: GraphQL/REST 엔드포인트, AI 통합.
9
+ */
10
+ tslib_1.__exportStar(require("./service/index"), exports);
11
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../server/index.ts"],"names":[],"mappings":";;;AAAA;;;;;GAKG;AACH,0DAA+B","sourcesContent":["/**\n * @things-factory/board-import — 도면(CAD/이미지/IFC) → Board Model 백엔드 코어.\n *\n * Phase 1: DXF 결정적 파이프라인 (AI 미사용).\n * Phase 2+: GraphQL/REST 엔드포인트, AI 통합.\n */\nexport * from './service/index'\n"]}
@@ -0,0 +1,8 @@
1
+ import type { AdapterCapabilities, AdapterInput, FormatAdapter, ParseOptions, ParseResult } from '../types/index';
2
+ export declare class DxfAdapter implements FormatAdapter {
3
+ readonly format = "dxf";
4
+ readonly supportedExtensions: string[];
5
+ readonly capabilities: AdapterCapabilities;
6
+ detect(file: AdapterInput, filename?: string): Promise<boolean>;
7
+ parse(file: AdapterInput, options?: ParseOptions): Promise<ParseResult>;
8
+ }
@@ -0,0 +1,220 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.DxfAdapter = void 0;
4
+ const tslib_1 = require("tslib");
5
+ /**
6
+ * DXF Format Adapter — DXF 텍스트 파일을 UniversalEntity 목록으로 정규화.
7
+ *
8
+ * 백엔드 환경(Node.js)에서 동작. 입력은 string / Buffer / ArrayBuffer / Uint8Array 모두 허용.
9
+ *
10
+ * 구현 범위 (Phase 1):
11
+ * - INSERT (블록 참조) → kind='shape', attribs.name 추출
12
+ * - LWPOLYLINE / POLYLINE → kind='path'
13
+ * - CIRCLE → kind='shape', attribs.shape='circle'
14
+ * - LINE → kind='path' (단일 선분)
15
+ * - TEXT / MTEXT → kind='text'
16
+ * - 레이어, 좌표, 회전 추출
17
+ * - $INSUNITS 단위 추출
18
+ *
19
+ * 보류 (Phase 2+):
20
+ * - HATCH, SPLINE, ELLIPSE 의 정밀 변환
21
+ * - 블록 정의 안의 entities 깊이 펼치기
22
+ * - 외부 참조 (XREF)
23
+ */
24
+ const dxf_parser_1 = tslib_1.__importDefault(require("dxf-parser"));
25
+ const DXF_INSUNITS_TO_UNITS = {
26
+ 0: 'unknown',
27
+ 1: 'inch',
28
+ 4: 'mm',
29
+ 5: 'cm',
30
+ 6: 'm'
31
+ };
32
+ class DxfAdapter {
33
+ constructor() {
34
+ this.format = 'dxf';
35
+ this.supportedExtensions = ['.dxf'];
36
+ this.capabilities = {
37
+ has2D: true,
38
+ has3D: false,
39
+ hasSemanticLabels: false,
40
+ hasTextLabels: true,
41
+ hasAttributes: true
42
+ };
43
+ }
44
+ async detect(file, filename) {
45
+ if (filename) {
46
+ const lower = filename.toLowerCase();
47
+ if (lower.endsWith('.dxf'))
48
+ return true;
49
+ // 명백한 non-DXF 확장자는 content peek 자체를 시도하지 않음.
50
+ // 잘못된 입력 타입(Promise/Stream 등) 이 흘러도 detect 가 throw 하지 않게 가드.
51
+ if (/\.(jpe?g|png|gif|webp|bmp|svg|pdf|ifc|gltf|glb|json|xml|csv|xlsx?|docx?)$/.test(lower)) {
52
+ return false;
53
+ }
54
+ }
55
+ try {
56
+ const head = readHead(file, 256);
57
+ return /\b0\s*\n\s*SECTION\b/.test(head) || /^\s*999/.test(head);
58
+ }
59
+ catch {
60
+ // 입력 타입을 텍스트로 못 읽으면 DXF 가 아니다 — false 로 흘려보내 다음 어댑터에 양보.
61
+ return false;
62
+ }
63
+ }
64
+ async parse(file, options = {}) {
65
+ const text = readText(file);
66
+ const parser = new dxf_parser_1.default();
67
+ const dxf = parser.parseSync(text);
68
+ if (!dxf)
69
+ throw new Error('DXF 파싱 실패');
70
+ const entities = [];
71
+ let uidCounter = 0;
72
+ const nextUid = () => `dxf-${++uidCounter}`;
73
+ const excludeLayers = new Set((options.excludeLayers || []).map(l => l.toLowerCase()));
74
+ const maxEntities = options.maxEntities ?? 100000;
75
+ for (const e of dxf.entities || []) {
76
+ if (entities.length >= maxEntities)
77
+ break;
78
+ const layer = e.layer || '';
79
+ if (excludeLayers.has(layer.toLowerCase()))
80
+ continue;
81
+ const ue = entityToUniversal(e, layer, nextUid());
82
+ if (ue)
83
+ entities.push(ue);
84
+ }
85
+ const bbox = computeBoundingBox(entities);
86
+ const units = DXF_INSUNITS_TO_UNITS[dxf.header?.$INSUNITS ?? 0];
87
+ return {
88
+ entities,
89
+ metadata: {
90
+ format: 'dxf',
91
+ formatVersion: dxf.header?.$ACADVER,
92
+ ...(dxf.header || {})
93
+ },
94
+ bbox,
95
+ units
96
+ };
97
+ }
98
+ }
99
+ exports.DxfAdapter = DxfAdapter;
100
+ // ── DXF entity → UniversalEntity ─────────────────────────────────────
101
+ function entityToUniversal(e, layer, uid) {
102
+ const type = e.type;
103
+ switch (type) {
104
+ case 'INSERT': {
105
+ const x = e.position?.x ?? 0;
106
+ const y = e.position?.y ?? 0;
107
+ const width = (e.xScale ?? 1) * 1;
108
+ const height = (e.yScale ?? 1) * 1;
109
+ return {
110
+ uid,
111
+ kind: 'shape',
112
+ position: { x, y },
113
+ size: { width, height },
114
+ rotation: e.rotation ?? 0,
115
+ bbox: { min: { x, y }, max: { x: x + width, y: y + height } },
116
+ attribs: {
117
+ name: e.name,
118
+ layer,
119
+ shape: 'rect'
120
+ }
121
+ };
122
+ }
123
+ case 'CIRCLE': {
124
+ const cx = e.center?.x ?? 0;
125
+ const cy = e.center?.y ?? 0;
126
+ const r = e.radius ?? 0;
127
+ return {
128
+ uid,
129
+ kind: 'shape',
130
+ position: { x: cx - r, y: cy - r },
131
+ size: { width: 2 * r, height: 2 * r },
132
+ bbox: { min: { x: cx - r, y: cy - r }, max: { x: cx + r, y: cy + r } },
133
+ attribs: { layer, shape: 'circle' }
134
+ };
135
+ }
136
+ case 'LWPOLYLINE':
137
+ case 'POLYLINE': {
138
+ const verts = (e.vertices || []).map((v) => ({ x: v.x, y: v.y }));
139
+ if (verts.length === 0)
140
+ return null;
141
+ const xs = verts.map(v => v.x);
142
+ const ys = verts.map(v => v.y);
143
+ const min = { x: Math.min(...xs), y: Math.min(...ys) };
144
+ const max = { x: Math.max(...xs), y: Math.max(...ys) };
145
+ return {
146
+ uid,
147
+ kind: 'path',
148
+ position: { x: min.x, y: min.y },
149
+ size: { width: max.x - min.x, height: max.y - min.y },
150
+ bbox: { min, max },
151
+ attribs: { layer, shape: 'polyline', vertices: verts, closed: !!e.shape }
152
+ };
153
+ }
154
+ case 'LINE': {
155
+ const x1 = e.vertices?.[0]?.x ?? 0;
156
+ const y1 = e.vertices?.[0]?.y ?? 0;
157
+ const x2 = e.vertices?.[1]?.x ?? 0;
158
+ const y2 = e.vertices?.[1]?.y ?? 0;
159
+ const min = { x: Math.min(x1, x2), y: Math.min(y1, y2) };
160
+ const max = { x: Math.max(x1, x2), y: Math.max(y1, y2) };
161
+ return {
162
+ uid,
163
+ kind: 'path',
164
+ position: { x: min.x, y: min.y },
165
+ size: { width: max.x - min.x, height: max.y - min.y },
166
+ bbox: { min, max },
167
+ attribs: { layer, shape: 'polyline', vertices: [{ x: x1, y: y1 }, { x: x2, y: y2 }] }
168
+ };
169
+ }
170
+ case 'TEXT':
171
+ case 'MTEXT': {
172
+ const x = e.position?.x ?? e.startPoint?.x ?? 0;
173
+ const y = e.position?.y ?? e.startPoint?.y ?? 0;
174
+ const text = e.text ?? e.string ?? '';
175
+ const h = e.textHeight ?? e.height ?? 1;
176
+ return {
177
+ uid,
178
+ kind: 'text',
179
+ position: { x, y },
180
+ bbox: { min: { x, y }, max: { x: x + h * (text.length || 1), y: y + h } },
181
+ attribs: { layer, text }
182
+ };
183
+ }
184
+ default:
185
+ return null;
186
+ }
187
+ }
188
+ // ── 유틸 ─────────────────────────────────────────────────────────────
189
+ function computeBoundingBox(entities) {
190
+ if (entities.length === 0)
191
+ return { min: { x: 0, y: 0 }, max: { x: 0, y: 0 } };
192
+ let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
193
+ for (const e of entities) {
194
+ if (e.bbox.min.x < minX)
195
+ minX = e.bbox.min.x;
196
+ if (e.bbox.min.y < minY)
197
+ minY = e.bbox.min.y;
198
+ if (e.bbox.max.x > maxX)
199
+ maxX = e.bbox.max.x;
200
+ if (e.bbox.max.y > maxY)
201
+ maxY = e.bbox.max.y;
202
+ }
203
+ return { min: { x: minX, y: minY }, max: { x: maxX, y: maxY } };
204
+ }
205
+ function readText(file) {
206
+ if (typeof file === 'string')
207
+ return file;
208
+ if (Buffer.isBuffer(file))
209
+ return file.toString('utf-8');
210
+ if (file instanceof Uint8Array)
211
+ return Buffer.from(file).toString('utf-8');
212
+ if (file instanceof ArrayBuffer)
213
+ return Buffer.from(new Uint8Array(file)).toString('utf-8');
214
+ throw new Error('Unsupported DXF input type');
215
+ }
216
+ function readHead(file, maxBytes) {
217
+ const text = readText(file);
218
+ return text.substring(0, maxBytes);
219
+ }
220
+ //# sourceMappingURL=dxf-adapter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dxf-adapter.js","sourceRoot":"","sources":["../../../server/service/adapters/dxf-adapter.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * DXF Format Adapter — DXF 텍스트 파일을 UniversalEntity 목록으로 정규화.\n *\n * 백엔드 환경(Node.js)에서 동작. 입력은 string / Buffer / ArrayBuffer / Uint8Array 모두 허용.\n *\n * 구현 범위 (Phase 1):\n * - INSERT (블록 참조) → kind='shape', attribs.name 추출\n * - LWPOLYLINE / POLYLINE → kind='path'\n * - CIRCLE → kind='shape', attribs.shape='circle'\n * - LINE → kind='path' (단일 선분)\n * - TEXT / MTEXT → kind='text'\n * - 레이어, 좌표, 회전 추출\n * - $INSUNITS 단위 추출\n *\n * 보류 (Phase 2+):\n * - HATCH, SPLINE, ELLIPSE 의 정밀 변환\n * - 블록 정의 안의 entities 깊이 펼치기\n * - 외부 참조 (XREF)\n */\nimport DxfParser from 'dxf-parser'\n\nimport type {\n AdapterCapabilities,\n AdapterInput,\n BoundingBox,\n FormatAdapter,\n ParseOptions,\n ParseResult,\n UniversalEntity\n} from '../types/index'\n\nconst DXF_INSUNITS_TO_UNITS: Record<number, ParseResult['units']> = {\n 0: 'unknown',\n 1: 'inch',\n 4: 'mm',\n 5: 'cm',\n 6: 'm'\n}\n\nexport class DxfAdapter implements FormatAdapter {\n readonly format = 'dxf'\n readonly supportedExtensions = ['.dxf']\n readonly capabilities: AdapterCapabilities = {\n has2D: true,\n has3D: false,\n hasSemanticLabels: false,\n hasTextLabels: true,\n hasAttributes: true\n }\n\n async detect(file: AdapterInput, filename?: string): Promise<boolean> {\n if (filename) {\n const lower = filename.toLowerCase()\n if (lower.endsWith('.dxf')) return true\n // 명백한 non-DXF 확장자는 content peek 자체를 시도하지 않음.\n // 잘못된 입력 타입(Promise/Stream 등) 이 흘러도 detect 가 throw 하지 않게 가드.\n if (/\\.(jpe?g|png|gif|webp|bmp|svg|pdf|ifc|gltf|glb|json|xml|csv|xlsx?|docx?)$/.test(lower)) {\n return false\n }\n }\n try {\n const head = readHead(file, 256)\n return /\\b0\\s*\\n\\s*SECTION\\b/.test(head) || /^\\s*999/.test(head)\n } catch {\n // 입력 타입을 텍스트로 못 읽으면 DXF 가 아니다 — false 로 흘려보내 다음 어댑터에 양보.\n return false\n }\n }\n\n async parse(file: AdapterInput, options: ParseOptions = {}): Promise<ParseResult> {\n const text = readText(file)\n const parser = new DxfParser()\n const dxf = parser.parseSync(text)\n\n if (!dxf) throw new Error('DXF 파싱 실패')\n\n const entities: UniversalEntity[] = []\n let uidCounter = 0\n const nextUid = () => `dxf-${++uidCounter}`\n\n const excludeLayers = new Set((options.excludeLayers || []).map(l => l.toLowerCase()))\n const maxEntities = options.maxEntities ?? 100000\n\n for (const e of dxf.entities || []) {\n if (entities.length >= maxEntities) break\n\n const layer = (e as any).layer || ''\n if (excludeLayers.has(layer.toLowerCase())) continue\n\n const ue = entityToUniversal(e, layer, nextUid())\n if (ue) entities.push(ue)\n }\n\n const bbox = computeBoundingBox(entities)\n const units = DXF_INSUNITS_TO_UNITS[(dxf.header as any)?.$INSUNITS ?? 0]\n\n return {\n entities,\n metadata: {\n format: 'dxf',\n formatVersion: (dxf.header as any)?.$ACADVER,\n ...((dxf.header as any) || {})\n },\n bbox,\n units\n }\n }\n}\n\n// ── DXF entity → UniversalEntity ─────────────────────────────────────\n\nfunction entityToUniversal(e: any, layer: string, uid: string): UniversalEntity | null {\n const type = e.type as string\n\n switch (type) {\n case 'INSERT': {\n const x = e.position?.x ?? 0\n const y = e.position?.y ?? 0\n const width = (e.xScale ?? 1) * 1\n const height = (e.yScale ?? 1) * 1\n return {\n uid,\n kind: 'shape',\n position: { x, y },\n size: { width, height },\n rotation: e.rotation ?? 0,\n bbox: { min: { x, y }, max: { x: x + width, y: y + height } },\n attribs: {\n name: e.name,\n layer,\n shape: 'rect'\n }\n }\n }\n\n case 'CIRCLE': {\n const cx = e.center?.x ?? 0\n const cy = e.center?.y ?? 0\n const r = e.radius ?? 0\n return {\n uid,\n kind: 'shape',\n position: { x: cx - r, y: cy - r },\n size: { width: 2 * r, height: 2 * r },\n bbox: { min: { x: cx - r, y: cy - r }, max: { x: cx + r, y: cy + r } },\n attribs: { layer, shape: 'circle' }\n }\n }\n\n case 'LWPOLYLINE':\n case 'POLYLINE': {\n const verts: { x: number; y: number }[] = (e.vertices || []).map((v: any) => ({ x: v.x, y: v.y }))\n if (verts.length === 0) return null\n const xs = verts.map(v => v.x)\n const ys = verts.map(v => v.y)\n const min = { x: Math.min(...xs), y: Math.min(...ys) }\n const max = { x: Math.max(...xs), y: Math.max(...ys) }\n return {\n uid,\n kind: 'path',\n position: { x: min.x, y: min.y },\n size: { width: max.x - min.x, height: max.y - min.y },\n bbox: { min, max },\n attribs: { layer, shape: 'polyline', vertices: verts, closed: !!e.shape }\n }\n }\n\n case 'LINE': {\n const x1 = e.vertices?.[0]?.x ?? 0\n const y1 = e.vertices?.[0]?.y ?? 0\n const x2 = e.vertices?.[1]?.x ?? 0\n const y2 = e.vertices?.[1]?.y ?? 0\n const min = { x: Math.min(x1, x2), y: Math.min(y1, y2) }\n const max = { x: Math.max(x1, x2), y: Math.max(y1, y2) }\n return {\n uid,\n kind: 'path',\n position: { x: min.x, y: min.y },\n size: { width: max.x - min.x, height: max.y - min.y },\n bbox: { min, max },\n attribs: { layer, shape: 'polyline', vertices: [{ x: x1, y: y1 }, { x: x2, y: y2 }] }\n }\n }\n\n case 'TEXT':\n case 'MTEXT': {\n const x = e.position?.x ?? e.startPoint?.x ?? 0\n const y = e.position?.y ?? e.startPoint?.y ?? 0\n const text = e.text ?? e.string ?? ''\n const h = e.textHeight ?? e.height ?? 1\n return {\n uid,\n kind: 'text',\n position: { x, y },\n bbox: { min: { x, y }, max: { x: x + h * (text.length || 1), y: y + h } },\n attribs: { layer, text }\n }\n }\n\n default:\n return null\n }\n}\n\n// ── 유틸 ─────────────────────────────────────────────────────────────\n\nfunction computeBoundingBox(entities: UniversalEntity[]): BoundingBox {\n if (entities.length === 0) return { min: { x: 0, y: 0 }, max: { x: 0, y: 0 } }\n let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity\n for (const e of entities) {\n if (e.bbox.min.x < minX) minX = e.bbox.min.x\n if (e.bbox.min.y < minY) minY = e.bbox.min.y\n if (e.bbox.max.x > maxX) maxX = e.bbox.max.x\n if (e.bbox.max.y > maxY) maxY = e.bbox.max.y\n }\n return { min: { x: minX, y: minY }, max: { x: maxX, y: maxY } }\n}\n\nfunction readText(file: AdapterInput): string {\n if (typeof file === 'string') return file\n if (Buffer.isBuffer(file)) return file.toString('utf-8')\n if (file instanceof Uint8Array) return Buffer.from(file).toString('utf-8')\n if (file instanceof ArrayBuffer) return Buffer.from(new Uint8Array(file)).toString('utf-8')\n throw new Error('Unsupported DXF input type')\n}\n\nfunction readHead(file: AdapterInput, maxBytes: number): string {\n const text = readText(file)\n return text.substring(0, maxBytes)\n}\n"]}
@@ -0,0 +1,32 @@
1
+ import type { AdapterCapabilities, AdapterInput, FormatAdapter, ParseOptions, ParseResult } from '../types/index';
2
+ export interface ImageParseOptions extends ParseOptions {
3
+ /** 자연어 컨텍스트 — VLM 에 함께 전달. 예: "factory floor plan with AGV paths and stockers" */
4
+ context?: string;
5
+ /** 후보 카테고리 — VLM 이 그 안에서 라벨링하도록 가이드 */
6
+ categories?: string[];
7
+ /**
8
+ * 이미지 dimension — VLM 프롬프트가 픽셀 좌표계를 명시하기 위해 사용. ImageAdapter 가
9
+ * 헤더에서 읽어 자동 주입한다. 호출자가 직접 부를 때도 명시 가능.
10
+ */
11
+ imageWidth?: number;
12
+ imageHeight?: number;
13
+ /**
14
+ * 사용자 자유서술 hint — 도면 설명, 강조 부분, 카테고리 가이드 등.
15
+ * VLM 의 view type 분류 + entity 추출 양쪽에 추가 컨텍스트로 합류.
16
+ */
17
+ userPrompt?: string;
18
+ /**
19
+ * View type 명시 (강제). 미지정 시 ImageAdapter 가 1-pass classify 로 자동 결정.
20
+ * - 'top-down' : orthographic 도면 / 평면도 (픽셀 좌표 그대로)
21
+ * - 'perspective-3d' : 3D 렌더 / 조감도 / 등각투상 (floor plan 추론)
22
+ * - 'photo' : 실사진 (floor plan 추론)
23
+ */
24
+ viewType?: 'top-down' | 'perspective-3d' | 'photo';
25
+ }
26
+ export declare class ImageAdapter implements FormatAdapter {
27
+ readonly format = "image";
28
+ readonly supportedExtensions: string[];
29
+ readonly capabilities: AdapterCapabilities;
30
+ detect(file: AdapterInput, filename?: string): Promise<boolean>;
31
+ parse(file: AdapterInput, options?: ImageParseOptions): Promise<ParseResult>;
32
+ }