KiCad PCB EDA Suite
dxf_import_plugin.cpp
Go to the documentation of this file.
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) 2019 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 // The DXF reader lib (libdxfrw) comes from dxflib project used in QCAD
26 // See http://www.ribbonsoft.com
27 // Each time a dxf entity is read, a "call back" function is called
28 // like void DXF_IMPORT_PLUGIN::addLine( const DL_LineData& data ) when a line is read.
29 // this function just add the BOARD entity from dxf parameters (start and end point ...)
30 
31 
32 #include "dxf_import_plugin.h"
33 #include <wx/arrstr.h>
34 #include <wx/regex.h>
35 
36 #include <trigo.h>
37 #include <macros.h>
38 #include <class_board.h>
39 #include <class_drawsegment.h>
40 #include <class_edge_mod.h>
41 #include <class_pcb_text.h>
42 #include <class_text_mod.h>
43 #include "common.h"
44 
45 
46 /*
47  * Important note: all DXF coordinates and sizes are converted to mm.
48  * they will be converted to internal units later.
49  */
50 
51 
52 // minimum bulge value before resorting to a line segment;
53 // the value 0.0218 is equivalent to about 5 degrees arc,
54 #define MIN_BULGE 0.0218
55 
56 //#define SCALE_FACTOR(x) millimeter2iu(x) /* no longer used */
57 #define SCALE_FACTOR(x) (x)
58 
59 DXF_IMPORT_PLUGIN::DXF_IMPORT_PLUGIN() : DL_CreationAdapter()
60 {
61  m_xOffset = 0.0; // X coord offset for conversion (in mm)
62  m_yOffset = 0.0; // Y coord offset for conversion (in mm)
63  m_DXF2mm = 1.0; // The scale factor to convert DXF units to mm
64  m_version = 0; // the dxf version, not yet used
65  m_inBlock = false; // Discard blocks
66  m_defaultThickness = 0.2; // default thickness (in mm)
67  m_brdLayer = Dwgs_User; // The default import layer
69  m_minX = m_minY = std::numeric_limits<double>::max();
70  m_maxX = m_maxY = std::numeric_limits<double>::min();
71 }
72 
73 
75 {
76 }
77 
78 
79 bool DXF_IMPORT_PLUGIN::Load( const wxString& aFileName )
80 {
81  return ImportDxfFile( aFileName );
82 }
83 
84 
86 {
87  wxCHECK( m_importer, false );
89 
90  return true;
91 }
92 
93 
95 {
96  return m_maxX - m_minX;
97 }
98 
99 
101 {
102  return m_maxY - m_minY;
103 }
104 
105 
107 {
109 
110  if( m_importer )
112 }
113 
114 
115 double DXF_IMPORT_PLUGIN::mapX( double aDxfCoordX )
116 {
117  return SCALE_FACTOR( m_xOffset + ( aDxfCoordX * m_DXF2mm ) );
118 }
119 
120 
121 double DXF_IMPORT_PLUGIN::mapY( double aDxfCoordY )
122 {
123  return SCALE_FACTOR( m_yOffset - ( aDxfCoordY * m_DXF2mm ) );
124 }
125 
126 
127 double DXF_IMPORT_PLUGIN::mapDim( double aDxfValue )
128 {
129  return SCALE_FACTOR( aDxfValue * m_DXF2mm );
130 }
131 
132 
133 double DXF_IMPORT_PLUGIN::mapWidth( double aDxfWidth )
134 {
135  // Always return the default line width
136 #if 0
137  // mapWidth returns the aDxfValue if aDxfWidth > 0 m_defaultThickness
138  if( aDxfWidth > 0.0 )
139  return SCALE_FACTOR( aDxfWidth * m_DXF2mm );
140 #endif
142 }
143 
144 
145 bool DXF_IMPORT_PLUGIN::ImportDxfFile( const wxString& aFile )
146 {
147  DL_Dxf dxf_reader;
148 
149  // wxFopen takes care of unicode filenames across platforms
150  FILE* fp = wxFopen( aFile, "rt" );
151 
152  if( fp == nullptr )
153  return false;
154 
155  // Note the dxf reader takes care of switching to "C" locale before reading the file
156  // and will close the file after reading
157  bool success = dxf_reader.in( fp, this );
158 
159  return success;
160 }
161 
162 
163 void DXF_IMPORT_PLUGIN::reportMsg( const char* aMessage )
164 {
165  // Add message to keep trace of not handled dxf entities
166  m_messages += aMessage;
167  m_messages += '\n';
168 }
169 
170 
171 void DXF_IMPORT_PLUGIN::addSpline( const DL_SplineData& aData )
172 {
173  if( m_inBlock )
174  return;
175 
176  // Called when starting reading a spline
179  m_curr_entity.m_EntityFlag = aData.flags;
180  m_curr_entity.m_EntityType = DL_ENTITY_SPLINE;
181  m_curr_entity.m_SplineDegree = aData.degree;
182  m_curr_entity.m_SplineTangentStartX = aData.tangentStartX;
183  m_curr_entity.m_SplineTangentStartY = aData.tangentStartY;
184  m_curr_entity.m_SplineTangentEndX = aData.tangentEndX;
185  m_curr_entity.m_SplineTangentEndY = aData.tangentEndY;
186  m_curr_entity.m_SplineKnotsCount = aData.nKnots;
187  m_curr_entity.m_SplineControlCount = aData.nControl;
188  m_curr_entity.m_SplineFitCount = aData.nFit;
189 }
190 
191 
192 void DXF_IMPORT_PLUGIN::addControlPoint( const DL_ControlPointData& aData )
193 {
194  if( m_inBlock )
195  return;
196 
197  // Called for every spline control point, when reading a spline entity
198  m_curr_entity.m_SplineControlPointList.emplace_back( aData.x , aData.y,
199  aData.w );
200 }
201 
202 
203 void DXF_IMPORT_PLUGIN::addFitPoint( const DL_FitPointData& aData )
204 {
205  if( m_inBlock )
206  return;
207 
208  // Called for every spline fit point, when reading a spline entity
209  // we store only the X,Y coord values in a VECTOR2D
210  m_curr_entity.m_SplineFitPointList.emplace_back( aData.x, aData.y );
211 }
212 
213 
214 void DXF_IMPORT_PLUGIN::addKnot( const DL_KnotData& aData)
215 {
216  if( m_inBlock )
217  return;
218 
219  // Called for every spline knot value, when reading a spline entity
220  m_curr_entity.m_SplineKnotsList.push_back( aData.k );
221 }
222 
223 
224 void DXF_IMPORT_PLUGIN::addLayer( const DL_LayerData& aData )
225 {
226  // Not yet useful in Pcbnew.
227 #if 0
228  wxString name = wxString::FromUTF8( aData.name.c_str() );
229  wxLogMessage( name );
230 #endif
231 }
232 
233 
234 void DXF_IMPORT_PLUGIN::addLine( const DL_LineData& aData )
235 {
236  if( m_inBlock )
237  return;
238 
239  VECTOR2D start( mapX( aData.x1 ), mapY( aData.y1 ) );
240  VECTOR2D end( mapX( aData.x2 ), mapY( aData.y2 ) );
241  double lineWidth = mapWidth( attributes.getWidth() );
242 
243  m_internalImporter.AddLine( start, end, lineWidth );
244 
245  updateImageLimits( start );
246  updateImageLimits( end );
247 }
248 
249 
250 void DXF_IMPORT_PLUGIN::addPolyline(const DL_PolylineData& aData )
251 {
252  if( m_inBlock )
253  return;
254 
255  // Convert DXF Polylines into a series of KiCad Lines and Arcs.
256  // A Polyline (as opposed to a LWPolyline) may be a 3D line or
257  // even a 3D Mesh. The only type of Polyline which is guaranteed
258  // to import correctly is a 2D Polyline in X and Y, which is what
259  // we assume of all Polylines. The width used is the width of the Polyline.
260  // per-vertex line widths, if present, are ignored.
263  m_curr_entity.m_EntityFlag = aData.flags;
264  m_curr_entity.m_EntityType = DL_ENTITY_POLYLINE;
265 }
266 
267 
268 void DXF_IMPORT_PLUGIN::addVertex( const DL_VertexData& aData )
269 {
270  if( m_inBlock )
271  return;
272 
274  return; // Error
275 
276  double lineWidth = mapWidth( attributes.getWidth() );
277 
278  const DL_VertexData* vertex = &aData;
279 
280  if( m_curr_entity.m_EntityParseStatus == 1 ) // This is the first vertex of an entity
281  {
285  m_curr_entity.m_BulgeVertex = vertex->bulge;
287  return;
288  }
289 
290  VECTOR2D seg_end( m_xOffset + vertex->x * m_DXF2mm,
291  m_yOffset - vertex->y * m_DXF2mm );
292 
293  if( std::abs( m_curr_entity.m_BulgeVertex ) < MIN_BULGE )
294  insertLine( m_curr_entity.m_LastCoordinate, seg_end, lineWidth );
295  else
297  lineWidth );
298 
300  m_curr_entity.m_BulgeVertex = vertex->bulge;
301 }
302 
303 
305 {
306  if( m_curr_entity.m_EntityType == DL_ENTITY_POLYLINE ||
307  m_curr_entity.m_EntityType == DL_ENTITY_LWPOLYLINE )
308  {
309  // Polyline flags bit 0 indicates closed (1) or open (0) polyline
310  if( m_curr_entity.m_EntityFlag & 1 )
311  {
312  double lineWidth = mapWidth( attributes.getWidth() );
313 
314  if( std::abs( m_curr_entity.m_BulgeVertex ) < MIN_BULGE )
316  lineWidth );
317  else
319  m_curr_entity.m_BulgeVertex, lineWidth );
320  }
321  }
322 
323  if( m_curr_entity.m_EntityType == DL_ENTITY_SPLINE )
324  {
325  double lineWidth = mapWidth( attributes.getWidth() );
326  insertSpline( lineWidth );
327  }
328 
330 }
331 
332 
333 void DXF_IMPORT_PLUGIN::addBlock( const DL_BlockData& aData )
334 {
335  // The DXF blocks are not useful in our import, so we skip them with the exception
336  // of the main block that is shown when editing the file
337  if( aData.name.compare( "*Model_Space") )
338  m_inBlock = true;
339 }
340 
341 
343 {
344  m_inBlock = false;
345 }
346 
347 
348 void DXF_IMPORT_PLUGIN::addCircle( const DL_CircleData& aData )
349 {
350  if( m_inBlock )
351  return;
352 
353  VECTOR2D center( mapX( aData.cx ), mapY( aData.cy ) );
354  double lineWidth = mapWidth( attributes.getWidth() );
355  m_internalImporter.AddCircle( center, mapDim( aData.radius ), lineWidth );
356 
357  VECTOR2D radiusDelta( mapDim( aData.radius ), mapDim( aData.radius ) );
358 
359  updateImageLimits( center + radiusDelta );
360  updateImageLimits( center - radiusDelta );
361 }
362 
363 
364 void DXF_IMPORT_PLUGIN::addArc( const DL_ArcData& aData )
365 {
366  if( m_inBlock )
367  return;
368 
369  // Init arc centre:
370  VECTOR2D center( mapX( aData.cx ), mapY( aData.cy ) );
371 
372  // aData.anglex is in degrees.
373  double startangle = aData.angle1;
374  double endangle = aData.angle2;
375 
376  // Init arc start point
377  VECTOR2D startPoint( aData.radius, 0.0 );
378  startPoint = startPoint.Rotate( startangle * M_PI / 180.0 );
379  VECTOR2D arcStart( mapX( startPoint.x + aData.cx ), mapY( startPoint.y + aData.cy ) );
380 
381  // calculate arc angle (arcs are CCW, and should be < 0 in Pcbnew)
382  double angle = -( endangle - startangle );
383 
384  if( angle > 0.0 )
385  angle -= 360.0;
386 
387  double lineWidth = mapWidth( attributes.getWidth() );
388  m_internalImporter.AddArc( center, arcStart, angle, lineWidth );
389 
390  VECTOR2D radiusDelta( mapDim( aData.radius ), mapDim( aData.radius ) );
391 
392  updateImageLimits( center + radiusDelta );
393  updateImageLimits( center - radiusDelta );
394 }
395 
396 
397 void DXF_IMPORT_PLUGIN::addText( const DL_TextData& aData )
398 {
399  if( m_inBlock )
400  return;
401 
402  VECTOR2D refPoint( mapX( aData.ipx ), mapY( aData.ipy ) );
403  VECTOR2D secPoint( mapX( aData.apx ), mapY( aData.apy ) );
404 
405  if( aData.vJustification != 0 || aData.hJustification != 0 || aData.hJustification == 4 )
406  {
407  if( aData.hJustification != 3 && aData.hJustification != 5 )
408  {
409  VECTOR2D tmp = secPoint;
410  secPoint = refPoint;
411  refPoint = tmp;
412  }
413  }
414 
415  wxString text = toNativeString( wxString::FromUTF8( aData.text.c_str() ) );
416 
417  double textHeight = mapDim( aData.height );
418  // The 0.9 factor gives a better height/width ratio with our font
419  double charWidth = textHeight * 0.9;
420  double textWidth = charWidth * text.length(); // Rough approximation
421  double textThickness = textHeight/8.0; // Use a reasonable line thickness for this text
422 
423  VECTOR2D bottomLeft(0.0, 0.0);
424  VECTOR2D bottomRight(0.0, 0.0);
425  VECTOR2D topLeft(0.0, 0.0);
426  VECTOR2D topRight(0.0, 0.0);
427 
430 
431  switch( aData.vJustification )
432  {
433  case 0: //DRW_Text::VBaseLine:
434  case 1: //DRW_Text::VBottom:
435  vJustify = GR_TEXT_VJUSTIFY_BOTTOM;
436 
437  topLeft.y = textHeight;
438  topRight.y = textHeight;
439  break;
440 
441  case 2: //DRW_Text::VMiddle:
442  vJustify = GR_TEXT_VJUSTIFY_CENTER;
443 
444  bottomRight.y = -textHeight / 2.0;
445  bottomLeft.y = -textHeight / 2.0;
446  topLeft.y = textHeight / 2.0;
447  topRight.y = textHeight / 2.0;
448  break;
449 
450  case 3: //DRW_Text::VTop:
451  vJustify = GR_TEXT_VJUSTIFY_TOP;
452 
453  bottomLeft.y = -textHeight;
454  bottomRight.y = -textHeight;
455  break;
456  }
457 
458  switch( aData.hJustification )
459  {
460  case 0: //DRW_Text::HLeft:
461  case 3: //DRW_Text::HAligned: // no equivalent options in text pcb.
462  case 5: //DRW_Text::HFit: // no equivalent options in text pcb.
463  hJustify = GR_TEXT_HJUSTIFY_LEFT;
464 
465  bottomRight.x = textWidth;
466  topRight.x = textWidth;
467  break;
468 
469  case 1: //DRW_Text::HCenter:
470  case 4: //DRW_Text::HMiddle: // no equivalent options in text pcb.
471  hJustify = GR_TEXT_HJUSTIFY_CENTER;
472 
473  bottomLeft.x = -textWidth / 2.0;
474  topLeft.x = -textWidth / 2.0;
475  bottomRight.x = textWidth / 2.0;
476  topRight.x = textWidth / 2.0;
477  break;
478 
479  case 2: //DRW_Text::HRight:
480  hJustify = GR_TEXT_HJUSTIFY_RIGHT;
481 
482  bottomLeft.x = -textWidth;
483  topLeft.x = -textWidth;
484  break;
485  }
486 
487 #if 0
488  wxString sty = wxString::FromUTF8( aData.style.c_str() );
489  sty = sty.ToLower();
490 
491  if( aData.textgen == 2 )
492  {
493  // Text dir = left to right;
494  } else if( aData.textgen == 4 )
495  {
496  // Text dir = top to bottom;
497  } else
498  {
499  }
500 #endif
501 
502  // dxf_lib imports text angle in radians (although there are no comment about that.
503  // So, for the moment, convert this angle to degrees
504  double angle_degree = aData.angle * 180 / M_PI;
505  // We also need the angle in radians. so convert angle_degree to radians
506  // regardless the aData.angle unit
507  double angleInRads = angle_degree * M_PI / 180.0;
508  double cosine = cos(angleInRads);
509  double sine = sin(angleInRads);
510 
511  m_internalImporter.AddText( refPoint, text, textHeight, charWidth, textThickness,
512  angle_degree, hJustify, vJustify );
513 
514  // Calculate the boundary box and update the image limits:
515  bottomLeft.x = bottomLeft.x * cosine - bottomLeft.y * sine;
516  bottomLeft.y = bottomLeft.x * sine + bottomLeft.y * cosine;
517 
518  bottomRight.x = bottomRight.x * cosine - bottomRight.y * sine;
519  bottomRight.y = bottomRight.x * sine + bottomRight.y * cosine;
520 
521  topLeft.x = topLeft.x * cosine - topLeft.y * sine;
522  topLeft.y = topLeft.x * sine + topLeft.y * cosine;
523 
524  topRight.x = topRight.x * cosine - topRight.y * sine;
525  topRight.y = topRight.x * sine + topRight.y * cosine;
526 
527  bottomLeft += refPoint;
528  bottomRight += refPoint;
529  topLeft += refPoint;
530  topRight += refPoint;
531 
532  updateImageLimits( bottomLeft );
533  updateImageLimits( bottomRight );
534  updateImageLimits( topLeft );
535  updateImageLimits( topRight );
536 
537 }
538 
539 
540 void DXF_IMPORT_PLUGIN::addMText( const DL_MTextData& aData )
541 {
542  if( m_inBlock )
543  return;
544 
545  wxString text = toNativeString( wxString::FromUTF8( aData.text.c_str() ) );
546  wxString attrib, tmp;
547 
548  double textHeight = mapDim( aData.height );
549  // The 0.9 factor gives a better height/width ratio with our font
550  double charWidth = textHeight * 0.9;
551  double textWidth = charWidth * text.length(); // Rough approximation
552  double textThickness = textHeight/8.0; // Use a reasonable line thickness for this text
553 
554  VECTOR2D bottomLeft(0.0, 0.0);
555  VECTOR2D bottomRight(0.0, 0.0);
556  VECTOR2D topLeft(0.0, 0.0);
557  VECTOR2D topRight(0.0, 0.0);
558 
559  /* Some texts start by '\' and have formatting chars (font name, font option...)
560  * ending with ';'
561  * Here are some mtext formatting codes:
562  * Format code Purpose
563  * \0...\o Turns overline on and off
564  * \L...\l Turns underline on and off
565  * \~ Inserts a nonbreaking space
566  \\ Inserts a backslash
567  \\\{...\} Inserts an opening and closing brace
568  \\ \File name; Changes to the specified font file
569  \\ \Hvalue; Changes to the text height specified in drawing units
570  \\ \Hvaluex; Changes the text height to a multiple of the current text height
571  \\ \S...^...; Stacks the subsequent text at the \, #, or ^ symbol
572  \\ \Tvalue; Adjusts the space between characters, from.75 to 4 times
573  \\ \Qangle; Changes oblique angle
574  \\ \Wvalue; Changes width factor to produce wide text
575  \\ \A Sets the alignment value; valid values: 0, 1, 2 (bottom, center, top) while( text.StartsWith( wxT("\\") ) )
576  */
577  while( text.StartsWith( wxT( "\\" ) ) )
578  {
579  attrib << text.BeforeFirst( ';' );
580  tmp = text.AfterFirst( ';' );
581  text = tmp;
582  }
583 
584  VECTOR2D textpos( mapX( aData.ipx ), mapY( aData.ipy ) );
585 
586  // Initialize text justifications:
589 
590  if( aData.attachmentPoint <= 3 )
591  {
592  vJustify = GR_TEXT_VJUSTIFY_TOP;
593 
594  bottomLeft.y = -textHeight;
595  bottomRight.y = -textHeight;
596  }
597  else if( aData.attachmentPoint <= 6 )
598  {
599  vJustify = GR_TEXT_VJUSTIFY_CENTER;
600 
601  bottomRight.y = -textHeight / 2.0;
602  bottomLeft.y = -textHeight / 2.0;
603  topLeft.y = textHeight / 2.0;
604  topRight.y = textHeight / 2.0;
605  }
606  else
607  {
608  vJustify = GR_TEXT_VJUSTIFY_BOTTOM;
609 
610  topLeft.y = textHeight;
611  topRight.y = textHeight;
612  }
613 
614  if( aData.attachmentPoint % 3 == 1 )
615  {
616  hJustify = GR_TEXT_HJUSTIFY_LEFT;
617 
618  bottomRight.x = textWidth;
619  topRight.x = textWidth;
620  }
621  else if( aData.attachmentPoint % 3 == 2 )
622  {
623  hJustify = GR_TEXT_HJUSTIFY_CENTER;
624 
625  bottomLeft.x = -textWidth / 2.0;
626  topLeft.x = -textWidth / 2.0;
627  bottomRight.x = textWidth / 2.0;
628  topRight.x = textWidth / 2.0;
629  }
630  else
631  {
632  hJustify = GR_TEXT_HJUSTIFY_RIGHT;
633 
634  bottomLeft.x = -textWidth;
635  topLeft.x = -textWidth;
636  }
637 
638 #if 0 // These setting have no meaning in Pcbnew
639  if( data.alignH == 1 )
640  {
641  // Text is left to right;
642  }
643  else if( data.alignH == 3 )
644  {
645  // Text is top to bottom;
646  }
647  else
648  {
649  // use ByStyle;
650  }
651 
652  if( aData.alignV == 1 )
653  {
654  // use AtLeast;
655  }
656  else
657  {
658  // useExact;
659  }
660 #endif
661 
662  // dxf_lib imports text angle in radians (although there are no comment about that.
663  // So, for the moment, convert this angle to degrees
664  double angle_degree = aData.angle * 180/M_PI;
665  // We also need the angle in radians. so convert angle_degree to radians
666  // regardless the aData.angle unit
667  double angleInRads = angle_degree * M_PI / 180.0;
668  double cosine = cos(angleInRads);
669  double sine = sin(angleInRads);
670 
671  m_internalImporter.AddText( textpos, text, textHeight, charWidth,
672  textThickness, angle_degree, hJustify, vJustify );
673 
674  bottomLeft.x = bottomLeft.x * cosine - bottomLeft.y * sine;
675  bottomLeft.y = bottomLeft.x * sine + bottomLeft.y * cosine;
676 
677  bottomRight.x = bottomRight.x * cosine - bottomRight.y * sine;
678  bottomRight.y = bottomRight.x * sine + bottomRight.y * cosine;
679 
680  topLeft.x = topLeft.x * cosine - topLeft.y * sine;
681  topLeft.y = topLeft.x * sine + topLeft.y * cosine;
682 
683  topRight.x = topRight.x * cosine - topRight.y * sine;
684  topRight.y = topRight.x * sine + topRight.y * cosine;
685 
686  bottomLeft += textpos;
687  bottomRight += textpos;
688  topLeft += textpos;
689  topRight += textpos;
690 
691  updateImageLimits( bottomLeft );
692  updateImageLimits( bottomRight );
693  updateImageLimits( topLeft );
694  updateImageLimits( topRight );
695 
696 }
697 
698 
699 void DXF_IMPORT_PLUGIN::setVariableInt( const std::string& key, int value, int code )
700 {
701  if( m_inBlock )
702  return;
703 
704  // Called for every int variable in the DXF file (e.g. "$INSUNITS").
705 
706  if( key == "$DWGCODEPAGE" )
707  {
708  m_codePage = value;
709  return;
710  }
711 
712  if( key == "$INSUNITS" ) // Drawing units
713  {
714  switch( value )
715  {
716  case 1: // inches
717  m_DXF2mm = 25.4;
718  break;
719 
720  case 2: // feet
721  m_DXF2mm = 304.8;
722  break;
723 
724  case 4: // mm
725  m_DXF2mm = 1.0;
726  break;
727 
728  case 5: // centimeters
729  m_DXF2mm = 10.0;
730  break;
731 
732  case 6: // meters
733  m_DXF2mm = 1000.0;
734  break;
735 
736  case 8: // microinches
737  m_DXF2mm = 2.54e-5;
738  break;
739 
740  case 9: // mils
741  m_DXF2mm = 0.0254;
742  break;
743 
744  case 10: // yards
745  m_DXF2mm = 914.4;
746  break;
747 
748  case 11: // Angstroms
749  m_DXF2mm = 1.0e-7;
750  break;
751 
752  case 12: // nanometers
753  m_DXF2mm = 1.0e-6;
754  break;
755 
756  case 13: // micrometers
757  m_DXF2mm = 1.0e-3;
758  break;
759 
760  case 14: // decimeters
761  m_DXF2mm = 100.0;
762  break;
763 
764  default:
765  // use the default of 1.0 for:
766  // 0: Unspecified Units
767  // 3: miles
768  // 7: kilometers
769  // 15: decameters
770  // 16: hectometers
771  // 17: gigameters
772  // 18: AU
773  // 19: lightyears
774  // 20: parsecs
775  m_DXF2mm = 1.0;
776  break;
777  }
778 
779  return;
780  }
781 }
782 
783 
784 void DXF_IMPORT_PLUGIN::setVariableString( const std::string& key, const std::string& value,
785  int code )
786 {
787  // Called for every string variable in the DXF file (e.g. "$ACADVER").
788 }
789 
790 
791 wxString DXF_IMPORT_PLUGIN::toDxfString( const wxString& aStr )
792 {
793  wxString res;
794  int j = 0;
795 
796  for( unsigned i = 0; i<aStr.length(); ++i )
797  {
798  int c = aStr[i];
799 
800  if( c > 175 || c < 11 )
801  {
802  res.append( aStr.Mid( j, i - j ) );
803  j = i;
804 
805  switch( c )
806  {
807  case 0x0A:
808  res += wxT( "\\P" );
809  break;
810 
811  // diameter:
812 #ifdef __WINDOWS_
813  // windows, as always, is special.
814  case 0x00D8:
815 #else
816  case 0x2205:
817 #endif
818  res += wxT( "%%C" );
819  break;
820 
821  // degree:
822  case 0x00B0:
823  res += wxT( "%%D" );
824  break;
825 
826  // plus/minus
827  case 0x00B1:
828  res += wxT( "%%P" );
829  break;
830 
831  default:
832  j--;
833  break;
834  }
835 
836  j++;
837  }
838  }
839 
840  res.append( aStr.Mid( j ) );
841  return res;
842 }
843 
844 
845 wxString DXF_IMPORT_PLUGIN::toNativeString( const wxString& aData )
846 {
847  wxString res;
848 
849  // Ignore font tags:
850  int j = 0;
851 
852  for( unsigned i = 0; i < aData.length(); ++i )
853  {
854  if( aData[ i ] == 0x7B ) // is '{' ?
855  {
856  if( aData[ i + 1 ] == 0x5c && aData[ i + 2 ] == 0x66 ) // is "\f" ?
857  {
858  // found font tag, append parsed part
859  res.append( aData.Mid( j, i - j ) );
860 
861  // skip to ';'
862  for( unsigned k = i + 3; k < aData.length(); ++k )
863  {
864  if( aData[ k ] == 0x3B )
865  {
866  i = j = ++k;
867  break;
868  }
869  }
870 
871  // add to '}'
872  for( unsigned k = i; k < aData.length(); ++k )
873  {
874  if( aData[ k ] == 0x7D )
875  {
876  res.append( aData.Mid( i, k - i ) );
877  i = j = ++k;
878  break;
879  }
880  }
881  }
882  }
883  }
884 
885  res.append( aData.Mid( j ) );
886 
887 #if 1
888  wxRegEx regexp;
889  // Line feed:
890  regexp.Compile( wxT( "\\\\P" ) );
891  regexp.Replace( &res, wxT( "\n" ) );
892 
893  // Space:
894  regexp.Compile( wxT( "\\\\~" ) );
895  regexp.Replace( &res, wxT( " " ) );
896 
897  // diameter:
898  regexp.Compile( wxT( "%%[cC]" ) );
899 #ifdef __WINDOWS__
900  // windows, as always, is special.
901  regexp.Replace( &res, wxChar( 0xD8 ) );
902 #else
903  // Empty_set, diameter is 0x2300
904  regexp.Replace( &res, wxChar( 0x2205 ) );
905 #endif
906 
907  // degree:
908  regexp.Compile( wxT( "%%[dD]" ) );
909  regexp.Replace( &res, wxChar( 0x00B0 ) );
910  // plus/minus
911  regexp.Compile( wxT( "%%[pP]" ) );
912  regexp.Replace( &res, wxChar( 0x00B1 ) );
913 #endif
914 
915  return res;
916 }
917 
918 
919 void DXF_IMPORT_PLUGIN::addTextStyle( const DL_StyleData& aData )
920 {
921  // TODO
922 }
923 
924 
926  const VECTOR2D& aSegEnd, int aWidth )
927 {
928  VECTOR2D origin( SCALE_FACTOR( aSegStart.x ), SCALE_FACTOR( aSegStart.y ) );
929  VECTOR2D end( SCALE_FACTOR( aSegEnd.x ), SCALE_FACTOR( aSegEnd.y ) );
930  m_internalImporter.AddLine( origin, end, aWidth );
931 
932  updateImageLimits( origin );
933  updateImageLimits( end );
934 }
935 
936 
937 void DXF_IMPORT_PLUGIN::insertArc( const VECTOR2D& aSegStart, const VECTOR2D& aSegEnd,
938  double aBulge, int aWidth )
939 {
940  VECTOR2D segment_startpoint( SCALE_FACTOR( aSegStart.x ), SCALE_FACTOR( aSegStart.y ) );
941  VECTOR2D segment_endpoint( SCALE_FACTOR( aSegEnd.x ), SCALE_FACTOR( aSegEnd.y ) );
942 
943  // ensure aBulge represents an angle from +/- ( 0 .. approx 359.8 deg )
944  if( aBulge < -2000.0 )
945  aBulge = -2000.0;
946  else if( aBulge > 2000.0 )
947  aBulge = 2000.0;
948 
949  double ang = 4.0 * atan( aBulge );
950 
951  // reflect the Y values to put everything in a RHCS
952  VECTOR2D sp( aSegStart.x, -aSegStart.y );
953  VECTOR2D ep( aSegEnd.x, -aSegEnd.y );
954  // angle from end->start
955  double offAng = atan2( ep.y - sp.y, ep.x - sp.x );
956  // length of subtended segment = 1/2 distance between the 2 points
957  double d = 0.5 * sqrt( (sp.x - ep.x) * (sp.x - ep.x) + (sp.y - ep.y) * (sp.y - ep.y) );
958  // midpoint of the subtended segment
959  double xm = ( sp.x + ep.x ) * 0.5;
960  double ym = ( sp.y + ep.y ) * 0.5;
961  double radius = d / sin( ang * 0.5 );
962 
963  if( radius < 0.0 )
964  radius = -radius;
965 
966  // calculate the height of the triangle with base d and hypotenuse r
967  double dh2 = radius * radius - d * d;
968 
969  // this should only ever happen due to rounding errors when r == d
970  if( dh2 < 0.0 )
971  dh2 = 0.0;
972 
973  double h = sqrt( dh2 );
974 
975  if( ang < 0.0 )
976  offAng -= M_PI_2;
977  else
978  offAng += M_PI_2;
979 
980  // for angles greater than 180 deg we need to flip the
981  // direction in which the arc center is found relative
982  // to the midpoint of the subtended segment.
983  if( ang < -M_PI )
984  offAng += M_PI;
985  else if( ang > M_PI )
986  offAng -= M_PI;
987 
988  // center point
989  double cx = h * cos( offAng ) + xm;
990  double cy = h * sin( offAng ) + ym;
991  VECTOR2D center( SCALE_FACTOR( cx ), SCALE_FACTOR( -cy ) );
992  VECTOR2D arc_start;
993  double angle = RAD2DEG( ang );
994 
995  if( ang < 0.0 )
996  {
997  arc_start = VECTOR2D( SCALE_FACTOR( ep.x ), SCALE_FACTOR( -ep.y ) );
998  }
999  else
1000  {
1001  arc_start = VECTOR2D( SCALE_FACTOR( sp.x ), SCALE_FACTOR( -sp.y ) );
1002  angle = -angle;
1003  }
1004 
1005  m_internalImporter.AddArc( center, arc_start, angle, aWidth );
1006 
1007  VECTOR2D radiusDelta( SCALE_FACTOR( radius ), SCALE_FACTOR( radius ) );
1008 
1009  updateImageLimits( center + radiusDelta );
1010  updateImageLimits( center - radiusDelta );
1011  return;
1012 }
1013 
1014 
1015 #include "tinysplinecpp.h"
1016 
1018 {
1019  #if 0 // Debug only
1020  wxLogMessage("spl deg %d kn %d ctr %d fit %d",
1025  #endif
1026 
1027  unsigned imax = m_curr_entity.m_SplineControlPointList.size();
1028 
1029  if( imax < 2 ) // malformed spline
1030  return;
1031 
1032 #if 0 // set to 1 to approximate the spline by segments between 2 control points
1033  VECTOR2D startpoint( mapX( m_curr_entity.m_SplineControlPointList[0].m_x ),
1035 
1036  for( unsigned int ii = 1; ii < imax; ++ii )
1037  {
1038  VECTOR2D endpoint( mapX( m_curr_entity.m_SplineControlPointList[ii].m_x ),
1040 
1041  if( startpoint != endpoint )
1042  {
1043  m_internalImporter.AddLine( startpoint, endpoint, aWidth );
1044 
1045  updateImageLimits( startpoint );
1046  updateImageLimits( endpoint );
1047 
1048  startpoint = endpoint;
1049  }
1050  }
1051 #else // Use bezier curves, supported by pcbnew, to approximate the spline
1052  tinyspline::BSpline dxfspline( m_curr_entity.m_SplineControlPointList.size(),
1053  /* coord dim */ 2, m_curr_entity.m_SplineDegree );
1054  std::vector<double> ctrlp;
1055 
1056  for( unsigned ii = 0; ii < imax; ++ii )
1057  {
1058  ctrlp.push_back( m_curr_entity.m_SplineControlPointList[ii].m_x );
1059  ctrlp.push_back( m_curr_entity.m_SplineControlPointList[ii].m_y );
1060  }
1061 
1062  dxfspline.setCtrlp( ctrlp );
1063  dxfspline.setKnots( m_curr_entity.m_SplineKnotsList );
1064  tinyspline::BSpline beziers( dxfspline.toBeziers() );
1065 
1066  std::vector<double> coords = beziers.ctrlp();
1067 
1068  // Each Bezier curve uses 4 vertices (a start point, 2 control points and a end point).
1069  // So we can have more than one Bezier curve ( there are one curve each four vertices)
1070  // However, one can have one Bezier curve with end point = ctrl point 2, having only 3
1071  // defined points in list.
1072  for( unsigned ii = 0; ii < coords.size(); ii += 8 )
1073  {
1074  VECTOR2D start( mapX( coords[ii] ), mapY( coords[ii+1] ) );
1075  VECTOR2D bezierControl1( mapX( coords[ii+2] ), mapY( coords[ii+3] ) );
1076  VECTOR2D bezierControl2( mapX( coords[ii+4] ), mapY( coords[ii+5] ) );
1077  VECTOR2D end;
1078 
1079  if( ii+7 < coords.size() )
1080  end = VECTOR2D( mapX( coords[ii+6] ), mapY( coords[ii+7] ) );
1081  else
1082  end = bezierControl2;
1083 
1084  m_internalImporter.AddSpline( start, bezierControl1, bezierControl2, end , aWidth );
1085  }
1086 #endif
1087 }
1088 
1089 
1091 {
1092  m_minX = std::min( aPoint.x, m_minX );
1093  m_maxX = std::max( aPoint.x, m_maxX );
1094 
1095  m_minY = std::min( aPoint.y, m_minY );
1096  m_maxY = std::max( aPoint.y, m_maxY );
1097 }
Interface that creates objects representing shapes for a given data model.
DXF2BRD_ENTITY_DATA m_curr_entity
void insertArc(const VECTOR2D &aSegStart, const VECTOR2D &aSegEnd, double aBulge, int aWidth)
EDA_TEXT_HJUSTIFY_T
Definition: eda_text.h:44
void AddLine(const VECTOR2D &aStart, const VECTOR2D &aEnd, double aWidth) override
Create an object representing a line segment.
double GetImageHeight() const override
Return image height from original imported file.
TEXTE_PCB class definition.
virtual void addTextStyle(const DL_StyleData &aData) override
virtual void addLine(const DL_LineData &aData) override
virtual void addPolyline(const DL_PolylineData &aData) override
virtual void endBlock() override
virtual void addBlock(const DL_BlockData &) override
Called for each BLOCK in the DXF file These are re-usable elements that may be placed into the model ...
static wxString toDxfString(const wxString &aStr)
Convert a native unicode string into a DXF encoded string.
#define MIN_BULGE
unsigned int m_SplineFitCount
double GetImageWidth() const override
Return image width from original imported file.
void AddArc(const VECTOR2D &aCenter, const VECTOR2D &aStart, double aAngle, double aWidth) override
Create an object representing an arc.
#define M_PI_2
Definition: transline.cpp:36
bool Load(const wxString &aFileName) override
Load file for import.
unsigned int m_SplineControlCount
double RAD2DEG(double rad)
Definition: trigo.h:215
double mapX(double aDxfCoordX)
virtual void addFitPoint(const DL_FitPointData &aData) override
Called for every spline fit point.
virtual void addSpline(const DL_SplineData &aData) override
Called for every spline.
std::vector< VECTOR2D > m_SplineFitPointList
void updateImageLimits(const VECTOR2D &aPoint)
void SetDefaultLineWidthMM(double aWidth)
Set the default line width when importing dxf items like lines to Pcbnew.
#define SCALE_FACTOR(x)
This file contains miscellaneous commonly used macros and functions.
virtual void endEntity() override
virtual void setVariableInt(const std::string &key, int value, int code) override
Called for every int variable in the DXF file (e.g.
bool Import() override
Actually imports the file.
Footprint text class description.
void AddSpline(const VECTOR2D &aStart, const VECTOR2D &BezierControl1, const VECTOR2D &BezierControl2, const VECTOR2D &aEnd, double aWidth) override
Create an object representing an arc.
VECTOR2< double > VECTOR2D
Definition: vector2d.h:593
virtual void addVertex(const DL_VertexData &aData) override
Called for every polyline vertex.
unsigned int m_SplineKnotsCount
unsigned int m_SplineDegree
virtual void addKnot(const DL_KnotData &aData) override
Called for every spline knot value.
static wxString toNativeString(const wxString &aData)
Converts a DXF encoded string into a native Unicode string.
void AddCircle(const VECTOR2D &aCenter, double aRadius, double aWidth) override
Create an object representing a circle.
double mapDim(double aDxfValue)
void AddText(const VECTOR2D &aOrigin, const wxString &aText, double aHeight, double aWidth, double aThickness, double aOrientation, EDA_TEXT_HJUSTIFY_T aHJustify, EDA_TEXT_VJUSTIFY_T aVJustify) override
Create an object representing a text.
EDA_TEXT_VJUSTIFY_T
Definition: eda_text.h:51
double mapWidth(double aDxfWidth)
virtual void addControlPoint(const DL_ControlPointData &aData) override
Called for every spline control point.
virtual void SetImporter(GRAPHICS_IMPORTER *aImporter) override
Set the receiver of the imported shapes.
VECTOR2< T > Rotate(double aAngle) const
Function Rotate rotates the vector by a given angle.
Definition: vector2d.h:377
virtual void addMText(const DL_MTextData &aData) override
Class to handle a graphic segment.
const char * name
Definition: DXF_plotter.cpp:60
double GetLineWidthMM() const
Returns the line width used for importing the outlines (in mm).
void insertSpline(int aWidth)
bool ImportDxfFile(const wxString &aFile)
Implementation of the method used for communicate with this filter.
virtual void SetImporter(GRAPHICS_IMPORTER *aImporter)
Set the receiver of the imported shapes.
static DIRECTION_45::AngleType angle(const VECTOR2I &a, const VECTOR2I &b)
void insertLine(const VECTOR2D &aSegStart, const VECTOR2D &aSegEnd, int aWidth)
The common library.
GRAPHICS_IMPORTER * m_importer
Importer used to create objects representing the imported shapes.
virtual void addLayer(const DL_LayerData &aData) override
virtual void addArc(const DL_ArcData &aData) override
double mapY(double aDxfCoordY)
virtual void addText(const DL_TextData &aData) override
GRAPHICS_IMPORTER_BUFFER m_internalImporter
EDGE_MODULE class definition.
std::vector< double > m_SplineKnotsList
void ImportTo(GRAPHICS_IMPORTER &aImporter)
virtual void setVariableString(const std::string &key, const std::string &value, int code) override
Called for every string variable in the DXF file (e.g.
std::vector< SPLINE_CTRL_POINT > m_SplineControlPointList
void reportMsg(const char *aMessage)
virtual void addCircle(const DL_CircleData &aData) override