|
1 | | -import { toast } from 'react-toastify'; |
2 | | -import { v4 as uuidv4 } from 'uuid'; |
3 | | -import type { Point, LineEntity, CircleEntity, Entity } from '../../App.types'; |
4 | | -import { EntityType } from '../../App.types'; |
5 | | -import { getEntities, setEntities, getActiveLayerId, getActiveLineColor, getActiveLineWidth } from '../../state'; |
| 1 | +import {Point} from '@flatten-js/core'; |
| 2 | +import type {Entities as DxfEntities, Helper} from 'dxf'; |
| 3 | +import {isNil, uniqBy} from 'es-toolkit'; |
| 4 | +import {toast} from 'react-toastify'; |
| 5 | +import {CircleEntity} from '../../entities/CircleEntity.ts'; |
| 6 | +import type {Entity} from '../../entities/Entity.ts'; |
| 7 | +import {LineEntity} from '../../entities/LineEntity.ts'; |
| 8 | +import {getActiveLayerId, getActiveLineColor, getActiveLineWidth, getEntities, setEntities,} from '../../state'; |
| 9 | +import {toHex} from '../rgb-to-hex-color.ts'; |
| 10 | + |
| 11 | +function getDxfLineColor(dxfColor: [number, number, number] | undefined): string { |
| 12 | + if (isNil(dxfColor)) { |
| 13 | + return getActiveLineColor(); |
| 14 | + } |
| 15 | + return toHex(dxfColor[0], dxfColor[1], dxfColor[2], 1); |
| 16 | +} |
6 | 17 |
|
7 | 18 | /** |
8 | 19 | * Imports entities from a DXF file. |
@@ -30,72 +41,64 @@ export const importEntitiesFromDxfFile = async (file?: File): Promise<void> => { |
30 | 41 | console.log('DXF file content loaded, attempting to parse...'); |
31 | 42 | // Dynamically import the dxf library |
32 | 43 | const dxf = await import('dxf'); |
33 | | - const parser = new dxf.DxfParser(); |
34 | | - const parsedDxf = parser.parseSync(fileContent); // Use parseSync as parseString is not available in the version I expect |
| 44 | + const parsedDxf = new dxf.Helper(fileContent) as Helper; |
35 | 45 |
|
36 | | - if (!parsedDxf || !parsedDxf.entities) { |
| 46 | + if (!parsedDxf || !parsedDxf.denormalised) { |
37 | 47 | toast.error('Failed to parse DXF file. No entities found or invalid format.'); |
38 | 48 | console.error('Parsed DXF data is invalid or contains no entities:', parsedDxf); |
39 | 49 | return; |
40 | 50 | } |
41 | 51 |
|
42 | | - console.log(`Successfully parsed DXF. Found ${parsedDxf.entities.length} entities.`); |
| 52 | + console.log(`Successfully parsed DXF. Found ${parsedDxf.denormalised.length} entities.`); |
43 | 53 | const newEntities: Entity[] = []; |
44 | 54 | const currentLayerId = getActiveLayerId(); |
45 | | - const defaultColor = getActiveLineColor(); |
46 | 55 | const defaultWidth = getActiveLineWidth(); |
47 | 56 |
|
48 | | - for (const entity of parsedDxf.entities) { |
| 57 | + for (const entity of parsedDxf.denormalised) { |
49 | 58 | // TODO: Coordinate transformation for Y-axis (DXF Y is usually up, canvas Y is down) |
50 | 59 | // For now, we assume positive Y is down for simplicity matching canvas. |
51 | 60 | // If DXF typically has Y up, then Y coordinates from DXF might need to be negated or subtracted from canvas height. |
52 | 61 | if (entity.type === 'LINE') { |
53 | | - if (entity.vertices && entity.vertices.length >= 2) { |
54 | | - const startPoint: Point = { x: entity.vertices[0].x, y: entity.vertices[0].y }; |
55 | | - const endPoint: Point = { x: entity.vertices[1].x, y: entity.vertices[1].y }; |
56 | | - const line: LineEntity = { |
57 | | - id: uuidv4(), |
58 | | - type: EntityType.LINE, |
59 | | - layerId: currentLayerId, |
60 | | - start: startPoint, |
61 | | - end: endPoint, |
62 | | - color: (entity.colorNumber !== undefined && entity.colorNumber !== 256) ? `#${entity.colorNumber.toString(16).padStart(6, '0')}` : defaultColor, // TODO: Map DXF color index to HEX |
63 | | - width: defaultWidth, // TODO: Potentially use layer lineweight or entity thickness |
64 | | - }; |
| 62 | + const dxfLine = entity as DxfEntities.Line; |
| 63 | + if (dxfLine.start && dxfLine.end) { |
| 64 | + const startPoint: Point = new Point(dxfLine.start.x, dxfLine.start.y); |
| 65 | + const endPoint: Point = new Point(dxfLine.end.x, dxfLine.end.y); |
| 66 | + const line = new LineEntity(currentLayerId, startPoint, endPoint); |
| 67 | + line.lineColor = getDxfLineColor(dxfLine.colorNumber); |
| 68 | + line.lineWidth = dxfLine.thickness || defaultWidth; |
| 69 | + line.lineDash = undefined; |
65 | 70 | newEntities.push(line); |
66 | | - console.log('Converted DXF LINE to LineEntity:', line); |
67 | 71 | } else { |
68 | | - console.warn('Skipping DXF LINE entity due to missing or insufficient vertices:', entity); |
| 72 | + console.warn('Skipping DXF LINE due to missing or insufficient vertices:', dxfLine); |
69 | 73 | } |
70 | 74 | } else if (entity.type === 'CIRCLE') { |
71 | | - if (entity.center && typeof entity.radius === 'number') { |
72 | | - const centerPoint: Point = { x: entity.center.x, y: entity.center.y }; |
73 | | - const circle: CircleEntity = { |
74 | | - id: uuidv4(), |
75 | | - type: EntityType.CIRCLE, |
76 | | - layerId: currentLayerId, |
77 | | - center: centerPoint, |
78 | | - radius: entity.radius, |
79 | | - color: (entity.colorNumber !== undefined && entity.colorNumber !== 256) ? `#${entity.colorNumber.toString(16).padStart(6, '0')}` : defaultColor, // TODO: Map DXF color index to HEX |
80 | | - width: defaultWidth, // TODO: Potentially use layer lineweight or entity thickness |
81 | | - }; |
| 75 | + const dxfCircle = entity as DxfEntities.Circle; |
| 76 | + if (dxfCircle.x && dxfCircle.y && dxfCircle.r) { |
| 77 | + const centerPoint = new Point(dxfCircle.x, dxfCircle.y); |
| 78 | + const circle = new CircleEntity(currentLayerId, centerPoint, dxfCircle.r); |
| 79 | + circle.lineColor = getDxfLineColor(dxfCircle.colorNumber); |
| 80 | + circle.lineWidth = defaultWidth; |
| 81 | + circle.lineDash = undefined; |
82 | 82 | newEntities.push(circle); |
83 | | - console.log('Converted DXF CIRCLE to CircleEntity:', circle); |
84 | 83 | } else { |
85 | | - console.warn('Skipping DXF CIRCLE entity due to missing center or radius:', entity); |
| 84 | + console.warn('Skipping DXF CIRCLE due to missing center or radius:', dxfCircle); |
86 | 85 | } |
87 | 86 | } else { |
88 | | - console.log(`Unsupported DXF entity type: ${entity.type}. Skipping.`); |
| 87 | + console.log(`Unsupported DXF type: ${entity.type}. Skipping.`); |
89 | 88 | } |
90 | 89 | } |
91 | 90 |
|
92 | 91 | if (newEntities.length > 0) { |
93 | | - setEntities([...getEntities(), ...newEntities]); |
94 | | - toast.success(`${newEntities.length} entities imported successfully from DXF!`); |
| 92 | + const uniqueEntities = uniqBy( |
| 93 | + newEntities, |
| 94 | + (entity) => |
| 95 | + `${JSON.stringify(entity.getShape())}|${entity.lineColor}|${entity.lineWidth}|${entity.lineDash}` |
| 96 | + ); |
| 97 | + setEntities([...getEntities(), ...uniqueEntities]); |
| 98 | + toast.success(`${uniqueEntities.length} entities imported successfully from DXF!`); |
95 | 99 | } else { |
96 | 100 | toast.info('No supported entities found in the DXF file.'); |
97 | 101 | } |
98 | | - |
99 | 102 | } catch (error) { |
100 | 103 | console.error('Error parsing DXF file:', error); |
101 | 104 | toast.error('An error occurred while parsing the DXF file. See console for details.'); |
|
0 commit comments