1 /*
2  * This program source code file is part of KiCad, a free EDA CAD application.
3  *
4  * Copyright (C) 2019 Jean-Pierre Charras, jp.charras at wanadoo.fr
5  * Copyright (C) 1992-2021 KiCad Developers, see AUTHORS.txt for contributors.
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 2
10  * of the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, you may find one here:
19  * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
20  * or you may search the http://www.gnu.org website for the version 2 license,
21  * or you may write to the Free Software Foundation, Inc.,
22  * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
23  */
24 
25 #ifndef DXF2BRD_ITEMS_H
26 #define DXF2BRD_ITEMS_H
27 
28 #include "graphics_import_plugin.h"
29 #include "graphics_importer_buffer.h"
30 
31 #include <dl_creationadapter.h>
32 #include <dl_dxf.h>
33 #include <math/vector3.h>
34 #include <wildcards_and_files_ext.h>
35 
36 #include <list>
37 
38 class BOARD;
39 class BOARD_ITEM;
40 
41 /**
42  * A helper class to store a spline control point (in X,Y plane only)
43  */
44 struct SPLINE_CTRL_POINT
45 {
46     double m_x;
47     double m_y;
48     double m_weight;
49 
SPLINE_CTRL_POINTSPLINE_CTRL_POINT50     SPLINE_CTRL_POINT( double a_x, double a_y, double a_weight )
51                     : m_x( a_x ), m_y( a_y ), m_weight( a_weight )
52     {}
53 };
54 
55 /**
56  * A helper class to parse a DXF entity (polyline and spline)
57  */
58 class DXF2BRD_ENTITY_DATA
59 {
60 public:
DXF2BRD_ENTITY_DATA()61     DXF2BRD_ENTITY_DATA() { Clear(); };
62 
63     // Reset the entity parameters
Clear()64     void Clear()
65     {
66         m_EntityType = DL_UNKNOWN;
67         m_EntityParseStatus = 0;
68         m_EntityFlag = 0;
69         m_SplineDegree = 1;
70         m_SplineKnotsCount = 0;
71         m_SplineControlCount = 0;
72         m_SplineFitCount = 0;
73         m_SplineTangentStartX = 0.0;
74         m_SplineTangentStartY = 0.0;
75         m_SplineTangentEndX = 0.0;
76         m_SplineTangentEndY = 0.0;
77         m_BulgeVertex = 0.0;
78         m_SplineKnotsList.clear();
79         m_SplineControlPointList.clear();
80         m_SplineFitPointList.clear();
81     }
82 
83     int m_EntityType;           // the DXF type of entity
84     int m_EntityParseStatus;    // Inside a entity: status of parsing:
85                                 // 0 = no entity
86                                 // 1 = first item of entity
87                                 // 2 = entity in progress
88     int m_EntityFlag;           // a info flag to parse entities
89 
90     VECTOR2D m_LastCoordinate;  // the last vertex coordinate read (unit = mm)
91     VECTOR2D m_PolylineStart;   // The first point of the polyline entity, when reading a
92                                 // polyline (unit = mm)
93     double m_BulgeVertex;       // the last vertex bulge value read
94 
95     // for spline parsing: parameters
96     unsigned int m_SplineDegree;
97     unsigned int m_SplineKnotsCount;
98     unsigned int m_SplineControlCount;
99     unsigned int m_SplineFitCount;
100     double m_SplineTangentStartX;   // tangent dir X for the start point
101     double m_SplineTangentStartY;   // tangent dir Y for the start point
102     double m_SplineTangentEndX;     // tangent dir X for the end point
103     double m_SplineTangentEndY;     // tangent dir Y for the end point
104 
105     // for spline parsing: buffers to store control points, fit points and knot
106     std::vector<double> m_SplineKnotsList;          // knots list, code 40
107     // control points list coordinates, code 10, 20 & 30 (only X and Y cood and Weight)
108     std::vector<SPLINE_CTRL_POINT> m_SplineControlPointList;
109     // fit points list, code 11, 21 & 31 (only X and Y cood)
110     std::vector<VECTOR2D> m_SplineFitPointList;
111 };
112 
113 // Magic constants as defined by dxf specification for line weight
114 #define DXF_IMPORT_LINEWEIGHT_BY_LAYER -1
115 #define DXF_IMPORT_LINEWEIGHT_BY_BLOCK -2
116 #define DXF_IMPORT_LINEWEIGHT_BY_LW_DEFAULT -3
117 
118 /**
119  * A helper class to hold layer settings temporarily during import
120  */
121 class DXF_IMPORT_LAYER
122 {
123 public:
124     wxString m_layerName;
125     int      m_lineWeight;
126 
DXF_IMPORT_LAYER(const wxString & aName,int aLineWeight)127     DXF_IMPORT_LAYER( const wxString& aName, int aLineWeight )
128     {
129         m_layerName  = aName;
130         m_lineWeight = aLineWeight;
131     }
132 };
133 
134 /**
135  * A helper class to hold layer settings temporarily during import
136  */
137 class DXF_IMPORT_BLOCK
138 {
139 public:
140     wxString m_name;
141     double m_baseX, m_baseY;
142 
143     GRAPHICS_IMPORTER_BUFFER m_buffer;
144 
DXF_IMPORT_BLOCK(const wxString & aName,double aX,double aY)145     DXF_IMPORT_BLOCK( const wxString& aName, double aX, double aY )
146     {
147         m_name = aName;
148         m_baseX = aX;
149         m_baseY = aY;
150     }
151 };
152 
153 /**
154  * A helper class to hold style settings temporarily during import
155  */
156 class DXF_IMPORT_STYLE
157 {
158 public:
159     wxString m_name;
160     double m_textHeight;
161     double m_widthFactor;
162     bool m_bold;
163     bool m_italic;
164 
DXF_IMPORT_STYLE(const wxString & aName,double aTextHeight,double aWidthFactor,bool aBold,bool aItalic)165     DXF_IMPORT_STYLE( const wxString& aName, double aTextHeight, double aWidthFactor, bool aBold,
166                       bool aItalic )
167     {
168         m_name = aName;
169         m_textHeight = aTextHeight;
170         m_widthFactor = aWidthFactor;
171         m_bold = aBold;
172         m_italic = aItalic;
173     }
174 };
175 
176 
177 /**
178  * DXF Units enum with values as specified in DXF 2012 Specification
179  */
180 enum class DXF_IMPORT_UNITS
181 {
182     DEFAULT = 0,
183     INCHES = 1,
184     FEET = 2,
185     MILLIMETERS = 4,
186     CENTIMETERS = 5,
187     METERS = 6,
188     MICROINCHES = 8,
189     MILS = 9,
190     YARDS = 10,
191     ANGSTROMS = 11,
192     NANOMETERS = 12,
193     MICRONS = 13,
194     DECIMETERS = 14,
195     DECAMETERS = 15,
196     HECTOMETERS = 16,
197     GIGAMETERS = 17,
198     ASTRONOMICAL = 18,
199     LIGHTYEARS = 19,
200     PARSECS = 20
201 };
202 
203 /**
204  * Helper class representing the DXF specification's "arbitrary axis"
205  */
206 struct DXF_ARBITRARY_AXIS
207 {
208     VECTOR3D vecX;
209     VECTOR3D vecY;
210     VECTOR3D vecZ;
211 };
212 
213 /**
214  * This class import DXF ASCII files and convert basic entities to board entities.
215  * It depends on the dxflib library.
216  */
217 #if 0 //defined(DEBUG)
218 // For dxf import debug:
219 #define ON_UNSUPPORTED( error_msg ) wxLogMessage( error_msg )
220 #else
221 #define ON_UNSUPPORTED( error_msg )
222 #endif
223 
224 class DXF_IMPORT_PLUGIN : public GRAPHICS_IMPORT_PLUGIN, public DL_CreationAdapter
225 {
226 public:
227     DXF_IMPORT_PLUGIN();
228     ~DXF_IMPORT_PLUGIN();
229 
GetName()230     const wxString GetName() const override
231     {
232         return "AutoCAD DXF";
233     }
234 
GetFileExtensions()235     const std::vector<std::string> GetFileExtensions() const override
236     {
237         static std::vector<std::string> exts = { "dxf" };
238         return exts;
239     }
240 
241     bool Load( const wxString& aFileName ) override;
242     bool Import() override;
243 
244     double GetImageWidth() const override;
245     double GetImageHeight() const override;
246 
247     void updateImageLimits( const VECTOR2D& aPoint );
248 
249     virtual void SetImporter( GRAPHICS_IMPORTER* aImporter ) override;
250 
251     /**
252      * Allow the import DXF items converted to board graphic items or footprint graphic items.
253      *
254      * @param aImportAsFootprintGraphic use true to import in a footprint or false to import on
255      *                                  a board.
256      */
ImportAsFootprintGraphic(bool aImportAsFootprintGraphic)257     void ImportAsFootprintGraphic( bool aImportAsFootprintGraphic )
258     {
259         m_importAsFPShapes = aImportAsFootprintGraphic;
260     }
261 
262     /**
263      * Set the default units when importing DXFs.
264      *
265      * DXFs can lack units by design which requires the importing software to make the decision.
266      *
267      * @param aUnits is the default unit of the DXF to assume.
268      */
SetUnit(DXF_IMPORT_UNITS aUnit)269     void SetUnit( DXF_IMPORT_UNITS aUnit )
270     {
271         m_currentUnit = aUnit;
272     }
273 
274     /**
275      * Set the default line width when importing dxf items like lines to Pcbnew.
276      *
277      * DXF files have no line width explicit parameter, it will be most of time the line width
278      * of imported lines.
279      *f
280      * @param aWidth is the line width in mm.
281      */
SetDefaultLineWidthMM(double aWidth)282     void SetDefaultLineWidthMM( double aWidth )
283     {
284         m_defaultThickness = aWidth;
285     }
286 
SetLineWidthMM(double aWidth)287     void SetLineWidthMM( double aWidth ) override { SetDefaultLineWidthMM( aWidth ); }
288 
289     /**
290      * Set the coordinate offset between the imported dxf items and Pcbnew.
291      *
292      * DXF files have the Y axis from bottom to top aOffsetX = 0, and aOffsetY = - vertical
293      * page size to import a full page.
294      *
295      * @param aOffsetX is the X offset in mm.
296      * @param aOffsetY is the Y offset in mm.
297      */
SetOffset(double aOffsetX,double aOffsetY)298     void SetOffset( double aOffsetX, double aOffsetY )
299     {
300         m_xOffset = aOffsetX;
301         m_yOffset = aOffsetY;
302     }
303 
304     /**
305      * Set the layer number to import dxf items.
306      *
307      * The layer should be a technical layer, not a copper layer.
308      */
SetBrdLayer(int aBrdLayer)309     void SetBrdLayer( int aBrdLayer ) { m_brdLayer = aBrdLayer; }
310 
311     /**
312      * Implementation of the method used for communicate with this filter.
313      *
314      * @param aFile is the full filename.
315      */
316     bool ImportDxfFile( const wxString& aFile );
317 
318     /**
319      * @return the list of messages in one string. Each message ends by '\n'
320      */
GetMessages()321     const wxString& GetMessages() const override
322     {
323         return m_messages;
324     }
325 
326 private:
327     // report message to keep trace of not supported dxf entities:
328     void reportMsg( const wxString& aMessage );
329 
330     // coordinate conversions from dxf file to mm
331     double mapX( double aDxfCoordX );
332     double mapY( double aDxfCoordY );
333     double mapDim( double aDxfValue );
334     double lineWeightToWidth( int lw, DXF_IMPORT_LAYER* aLayer );
335     double getCurrentUnitScale();
336 
337     DXF_ARBITRARY_AXIS getArbitraryAxis( DL_Extrusion* aData );
338 
339     /**
340      * Converts a given world coordinate point to object coordinate using the given arbitrary
341      * axis vectors.
342      */
343     VECTOR3D wcsToOcs( const DXF_ARBITRARY_AXIS& arbitraryAxis, VECTOR3D point );
344 
345     /**
346      * Converts a given object coordinate point to world coordinate using the given arbitrary
347      * axis vectors.
348      */
349     VECTOR3D ocsToWcs( const DXF_ARBITRARY_AXIS& arbitraryAxis, VECTOR3D point );
350 
351     /**
352      * Return the import layer data.
353      *
354      * @param aLayerName is the raw string from dxflib getLayer().
355      * @returns The given layer by name or the placeholder layer inserted in the constructor.
356      */
357     DXF_IMPORT_LAYER* getImportLayer( const std::string& aLayerName );
358 
359     /**
360      * Return the import layer block.
361      *
362      * @param aBlockName is the raw string from dxflib.
363      * @return The given block by name or nullptr if not found.
364      */
365     DXF_IMPORT_BLOCK* getImportBlock( const std::string& aBlockName );
366 
367     /**
368      * Return the import style.
369      *
370      * @param aStyleName is the raw string from dxflib.
371      * @return The given style by name or nullptr if not found.
372      */
373     DXF_IMPORT_STYLE* getImportStyle( const std::string& aStyleName );
374 
375     // Functions to aid in the creation of a Polyline.
376     void insertLine( const VECTOR2D& aSegStart, const VECTOR2D& aSegEnd, double aWidth );
377     void insertArc( const VECTOR2D& aSegStart, const VECTOR2D& aSegEnd,
378                     double aBulge, double aWidth );
379 
380     // Add a dxf spline (stored in m_curr_entity) to the board, after conversion to segments.
381     void insertSpline( double aWidth );
382 
383     // Methods from DL_CreationAdapter:
384     // They are something like"call back" functions,
385     // called when the corresponding object is read in dxf file
386 
387     /**
388      * Called for every string variable in the DXF file (e.g. "$ACADVER").
389      */
390     virtual void setVariableString( const std::string& key, const std::string& value,
391                                     int code ) override;
392 
393     /**
394      * Called for every int variable in the DXF file (e.g. "$ACADMAINTVER").
395      */
396     virtual void setVariableInt( const std::string& key, int value, int code ) override;
397 
398     /**
399      * Called for every double variable in the DXF file (e.g. "$DIMEXO").
400      */
setVariableDouble(const std::string & key,double value,int code)401     virtual void setVariableDouble( const std::string& key, double value, int code ) override {}
402 
403     virtual void addLayer( const DL_LayerData& aData ) override;
404     virtual void addLine( const DL_LineData& aData ) override;
405     virtual void addLinetype( const DL_LinetypeData& data ) override;
406 
407     /**
408      * Called for each BLOCK in the DXF file.
409      *
410      * These are re-usable elements that may be placed into the model space.  The elements
411      * are dereferenced to the model, so we just need to skip the re-parsing for the block
412      * elements.
413      */
414     virtual void addBlock( const DL_BlockData& ) override;
415     virtual void endBlock() override;
416     virtual void addTextStyle( const DL_StyleData& aData ) override;
417     virtual void addPoint( const DL_PointData& aData ) override;
418 
419     virtual void addCircle( const DL_CircleData& aData ) override;
420     virtual void addArc( const DL_ArcData& aData ) override;
421     //virtual void addLWPolyline( const DRW_LWPolyline& aData ) override;
422     virtual void addText( const DL_TextData& aData ) override;
423     virtual void addPolyline( const DL_PolylineData& aData ) override;
424 
425     /* Inserts blocks where specified by insert data */
426     virtual void addInsert( const DL_InsertData& aData ) override;
427 
428     /**
429      * Called for every polyline vertex.
430      */
431     virtual void addVertex( const DL_VertexData& aData ) override;
432     virtual void addMText( const DL_MTextData& aData) override;
433 
434     virtual void endEntity() override;
435 
436     /**
437      * Called for every spline.
438      * */
439     virtual void addSpline( const DL_SplineData& aData ) override;
440 
441     /**
442      * Called for every spline control point.
443      */
444     virtual void addControlPoint( const DL_ControlPointData& aData ) override;
445 
446     /**
447      * Called for every spline fit point.
448      */
449     virtual void addFitPoint( const DL_FitPointData& aData ) override;
450 
451     /**
452      * Called for every spline knot value.
453      */
454     virtual void addKnot( const DL_KnotData& aData ) override;
455 
456     // Not yet handled DXF entities:
addXLine(const DL_XLineData &)457     virtual void addXLine( const DL_XLineData& ) override { ON_UNSUPPORTED( "addXLine" ); }
458 
addRay(const DL_RayData &)459     virtual void addRay( const DL_RayData& ) override { ON_UNSUPPORTED( "addRay" ); }
460 
addArcAlignedText(const DL_ArcAlignedTextData &)461     virtual void addArcAlignedText( const DL_ArcAlignedTextData& ) override
462                 { ON_UNSUPPORTED( "addArcAlignedText" ); }
463 
addAttribute(const DL_AttributeData &)464     virtual void addAttribute( const DL_AttributeData& ) override
465                 { ON_UNSUPPORTED( "addAttribute" ); }
466 
addDimAlign(const DL_DimensionData &,const DL_DimAlignedData &)467     virtual void addDimAlign( const DL_DimensionData&,
468             const DL_DimAlignedData& ) override { ON_UNSUPPORTED( "addDimAlign" ); }
addDimLinear(const DL_DimensionData &,const DL_DimLinearData &)469     virtual void addDimLinear( const DL_DimensionData&,
470             const DL_DimLinearData& ) override { ON_UNSUPPORTED( "addDimLinear" ); }
addDimRadial(const DL_DimensionData &,const DL_DimRadialData &)471     virtual void addDimRadial( const DL_DimensionData&,
472             const DL_DimRadialData& ) override { ON_UNSUPPORTED( "addDimRadial" ); }
addDimDiametric(const DL_DimensionData &,const DL_DimDiametricData &)473     virtual void addDimDiametric( const DL_DimensionData&,
474             const DL_DimDiametricData& ) override { ON_UNSUPPORTED( "addDimDiametric" ); }
addDimAngular(const DL_DimensionData &,const DL_DimAngular2LData &)475     virtual void addDimAngular( const DL_DimensionData&,
476             const DL_DimAngular2LData& ) override { ON_UNSUPPORTED( "addDimAngular" ); }
addDimAngular3P(const DL_DimensionData &,const DL_DimAngular3PData &)477     virtual void addDimAngular3P( const DL_DimensionData&,
478             const DL_DimAngular3PData& ) override { ON_UNSUPPORTED( "addDimAngular3P" ); }
addDimOrdinate(const DL_DimensionData &,const DL_DimOrdinateData &)479     virtual void addDimOrdinate( const DL_DimensionData&,
480             const DL_DimOrdinateData& ) override { ON_UNSUPPORTED( "addDimOrdinate" ); }
481 
addLeader(const DL_LeaderData &)482     virtual void addLeader( const DL_LeaderData& ) override
483                         { ON_UNSUPPORTED( "addLeader" ); }
484 
addLeaderVertex(const DL_LeaderVertexData &)485     virtual void addLeaderVertex( const DL_LeaderVertexData& ) override
486                         { ON_UNSUPPORTED( "addLeaderVertex" ); }
487 
addHatch(const DL_HatchData &)488     virtual void addHatch( const DL_HatchData& ) override { ON_UNSUPPORTED( "addHatch" ); }
489 
addTrace(const DL_TraceData &)490     virtual void addTrace( const DL_TraceData& ) override { ON_UNSUPPORTED( "addTrace" ); }
add3dFace(const DL_3dFaceData &)491     virtual void add3dFace( const DL_3dFaceData& ) override { ON_UNSUPPORTED( "add3dFace" ); }
492 
addSolid(const DL_SolidData &)493     virtual void addSolid( const DL_SolidData& ) override { ON_UNSUPPORTED( "addSolid" ); }
494 
addImage(const DL_ImageData &)495     virtual void addImage( const DL_ImageData& ) override { ON_UNSUPPORTED( "addImage" ); }
linkImage(const DL_ImageDefData &)496     virtual void linkImage( const DL_ImageDefData& ) override {}
497 
addHatchLoop(const DL_HatchLoopData &)498     virtual void addHatchLoop( const DL_HatchLoopData& ) override
499     {
500         ON_UNSUPPORTED( "addHatchLoop" );
501     }
502 
addHatchEdge(const DL_HatchEdgeData &)503     virtual void addHatchEdge( const DL_HatchEdgeData& ) override
504     {
505         ON_UNSUPPORTED( "addHatchEdge" );
506     }
507 
addXRecord(const std::string &)508     virtual void addXRecord( const std::string& ) override { ON_UNSUPPORTED( "addXRecord" ); }
509 
addXRecordString(int,const std::string &)510     virtual void addXRecordString( int, const std::string& ) override
511     {
512         ON_UNSUPPORTED( "addXRecordString" );
513     }
514 
addXRecordReal(int,double)515     virtual void addXRecordReal( int, double ) override { ON_UNSUPPORTED( "addXRecordReal" ); }
addXRecordInt(int,int)516     virtual void addXRecordInt( int, int ) override { ON_UNSUPPORTED( "addXRecordInt" ); }
addXRecordBool(int,bool)517     virtual void addXRecordBool( int, bool ) override { ON_UNSUPPORTED( "addXRecordBool" ); }
518 
addXDataApp(const std::string &)519     virtual void addXDataApp( const std::string& ) override { ON_UNSUPPORTED( "addXDataApp" ); }
addXDataString(int,const std::string &)520     virtual void addXDataString( int, const std::string& ) override
521     {
522         ON_UNSUPPORTED( "addXDataString" );
523     }
524 
addXDataReal(int,double)525     virtual void addXDataReal( int, double ) override { ON_UNSUPPORTED( "addXDataReal" ); }
addXDataInt(int,int)526     virtual void addXDataInt( int, int ) override { ON_UNSUPPORTED( "addXDataInt" ); }
527 
528     /**
529      * Convert a native Unicode string into a DXF encoded string.
530      *
531      * DXF encoding includes the following special sequences:
532      * - %%%c for a diameter sign
533      * - %%%d for a degree sign
534      * - %%%p for a plus/minus sign
535      */
536     static wxString toDxfString( const wxString& aStr );
537 
538     /**
539      * Convert a DXF encoded string into a native Unicode string.
540      */
541     static wxString toNativeString( const wxString& aData );
542 
543     void writeLine();
544     void writeMtext();
545 
546 private:
547     double      m_xOffset;           // X coord offset for conversion (in mm)
548     double      m_yOffset;           // Y coord offset for conversion (in mm)
549     double      m_defaultThickness;  // default line thickness for conversion (in mm)
550     int         m_brdLayer;          // The board layer to place imported DXF items
551     int         m_version;           // the dxf version, not used here
552     std::string m_codePage;          // The code page, not used here
553     bool        m_importAsFPShapes;  // Use footprint items instead of board items when true.
554                                      // true when the items are imported in the footprint editor
555     wxString    m_messages;          // messages generated during dxf file parsing.
556                                      // Each message ends by '\n'
557     DXF2BRD_ENTITY_DATA m_curr_entity;  // the current entity parameters when parsing a DXF entity
558 
559     double      m_minX, m_maxX;      // handles image size in mm
560     double      m_minY, m_maxY;      // handles image size in mm
561 
562     DXF_IMPORT_UNITS m_currentUnit;               // current unit during import
563     int              m_importCoordinatePrecision; // current precision for linear units (points)
564     int              m_importAnglePrecision;      // current precision for angles
565 
566     GRAPHICS_IMPORTER_BUFFER m_internalImporter;
567 
568     // List of layers as we import, used just to grab props for objects.
569     std::vector<std::unique_ptr<DXF_IMPORT_LAYER>> m_layers;
570     std::vector<std::unique_ptr<DXF_IMPORT_BLOCK>> m_blocks;    // List of blocks as we import
571     std::vector<std::unique_ptr<DXF_IMPORT_STYLE>> m_styles;    // List of blocks as we import
572     DXF_IMPORT_BLOCK* m_currentBlock;
573 };
574 
575 #endif  // DXF2BRD_ITEMS_H
576