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 the block id
336  // because Pcbnew has no support to group a set of graphic items inside an entity
337  m_inBlock = false;
338 }
339 
340 
342 {
343  m_inBlock = false;
344 }
345 
346 
347 void DXF_IMPORT_PLUGIN::addCircle( const DL_CircleData& aData )
348 {
349  if( m_inBlock )
350  return;
351 
352  VECTOR2D center( mapX( aData.cx ), mapY( aData.cy ) );
353  double lineWidth = mapWidth( attributes.getWidth() );
354  m_internalImporter.AddCircle( center, mapDim( aData.radius ), lineWidth );
355 
356  VECTOR2D radiusDelta( mapDim( aData.radius ), mapDim( aData.radius ) );
357 
358  updateImageLimits( center + radiusDelta );
359  updateImageLimits( center - radiusDelta );
360 }
361 
362 
363 void DXF_IMPORT_PLUGIN::addArc( const DL_ArcData& aData )
364 {
365  if( m_inBlock )
366  return;
367 
368  // Init arc centre:
369  VECTOR2D center( mapX( aData.cx ), mapY( aData.cy ) );
370 
371  // aData.anglex is in degrees.
372  double startangle = aData.angle1;
373  double endangle = aData.angle2;
374 
375  // Init arc start point
376  VECTOR2D startPoint( aData.radius, 0.0 );
377  startPoint = startPoint.Rotate( startangle * M_PI / 180.0 );
378  VECTOR2D arcStart( mapX( startPoint.x + aData.cx ), mapY( startPoint.y + aData.cy ) );
379 
380  // calculate arc angle (arcs are CCW, and should be < 0 in Pcbnew)
381  double angle = -( endangle - startangle );
382 
383  if( angle > 0.0 )
384  angle -= 360.0;
385 
386  double lineWidth = mapWidth( attributes.getWidth() );
387  m_internalImporter.AddArc( center, arcStart, angle, lineWidth );
388 
389  VECTOR2D radiusDelta( mapDim( aData.radius ), mapDim( aData.radius ) );
390 
391  updateImageLimits( center + radiusDelta );
392  updateImageLimits( center - radiusDelta );
393 }
394 
395 
396 void DXF_IMPORT_PLUGIN::addText( const DL_TextData& aData )
397 {
398  if( m_inBlock )
399  return;
400 
401  VECTOR2D refPoint( mapX( aData.ipx ), mapY( aData.ipy ) );
402  VECTOR2D secPoint( mapX( aData.apx ), mapY( aData.apy ) );
403 
404  if( aData.vJustification != 0 || aData.hJustification != 0 || aData.hJustification == 4 )
405  {
406  if( aData.hJustification != 3 && aData.hJustification != 5 )
407  {
408  VECTOR2D tmp = secPoint;
409  secPoint = refPoint;
410  refPoint = tmp;
411  }
412  }
413 
414  wxString text = toNativeString( wxString::FromUTF8( aData.text.c_str() ) );
415 
416  double textHeight = mapDim( aData.height );
417  // The 0.9 factor gives a better height/width ratio with our font
418  double charWidth = textHeight * 0.9;
419  double textWidth = charWidth * text.length(); // Rough approximation
420  double textThickness = textHeight/8.0; // Use a reasonable line thickness for this text
421 
422  VECTOR2D bottomLeft(0.0, 0.0);
423  VECTOR2D bottomRight(0.0, 0.0);
424  VECTOR2D topLeft(0.0, 0.0);
425  VECTOR2D topRight(0.0, 0.0);
426 
429 
430  switch( aData.vJustification )
431  {
432  case 0: //DRW_Text::VBaseLine:
433  case 1: //DRW_Text::VBottom:
434  vJustify = GR_TEXT_VJUSTIFY_BOTTOM;
435 
436  topLeft.y = textHeight;
437  topRight.y = textHeight;
438  break;
439 
440  case 2: //DRW_Text::VMiddle:
441  vJustify = GR_TEXT_VJUSTIFY_CENTER;
442 
443  bottomRight.y = -textHeight / 2.0;
444  bottomLeft.y = -textHeight / 2.0;
445  topLeft.y = textHeight / 2.0;
446  topRight.y = textHeight / 2.0;
447  break;
448 
449  case 3: //DRW_Text::VTop:
450  vJustify = GR_TEXT_VJUSTIFY_TOP;
451 
452  bottomLeft.y = -textHeight;
453  bottomRight.y = -textHeight;
454  break;
455  }
456 
457  switch( aData.hJustification )
458  {
459  case 0: //DRW_Text::HLeft:
460  case 3: //DRW_Text::HAligned: // no equivalent options in text pcb.
461  case 5: //DRW_Text::HFit: // no equivalent options in text pcb.
462  hJustify = GR_TEXT_HJUSTIFY_LEFT;
463 
464  bottomRight.x = textWidth;
465  topRight.x = textWidth;
466  break;
467 
468  case 1: //DRW_Text::HCenter:
469  case 4: //DRW_Text::HMiddle: // no equivalent options in text pcb.
470  hJustify = GR_TEXT_HJUSTIFY_CENTER;
471 
472  bottomLeft.x = -textWidth / 2.0;
473  topLeft.x = -textWidth / 2.0;
474  bottomRight.x = textWidth / 2.0;
475  topRight.x = textWidth / 2.0;
476  break;
477 
478  case 2: //DRW_Text::HRight:
479  hJustify = GR_TEXT_HJUSTIFY_RIGHT;
480 
481  bottomLeft.x = -textWidth;
482  topLeft.x = -textWidth;
483  break;
484  }
485 
486 #if 0
487  wxString sty = wxString::FromUTF8( aData.style.c_str() );
488  sty = sty.ToLower();
489 
490  if( aData.textgen == 2 )
491  {
492  // Text dir = left to right;
493  } else if( aData.textgen == 4 )
494  {
495  // Text dir = top to bottom;
496  } else
497  {
498  }
499 #endif
500 
501  // dxf_lib imports text angle in radians (although there are no comment about that.
502  // So, for the moment, convert this angle to degrees
503  double angle_degree = aData.angle * 180 / M_PI;
504  // We also need the angle in radians. so convert angle_degree to radians
505  // regardless the aData.angle unit
506  double angleInRads = angle_degree * M_PI / 180.0;
507  double cosine = cos(angleInRads);
508  double sine = sin(angleInRads);
509 
510  m_internalImporter.AddText( refPoint, text, textHeight, charWidth, textThickness,
511  angle_degree, hJustify, vJustify );
512 
513  // Calculate the boundary box and update the image limits:
514  bottomLeft.x = bottomLeft.x * cosine - bottomLeft.y * sine;
515  bottomLeft.y = bottomLeft.x * sine + bottomLeft.y * cosine;
516 
517  bottomRight.x = bottomRight.x * cosine - bottomRight.y * sine;
518  bottomRight.y = bottomRight.x * sine + bottomRight.y * cosine;
519 
520  topLeft.x = topLeft.x * cosine - topLeft.y * sine;
521  topLeft.y = topLeft.x * sine + topLeft.y * cosine;
522 
523  topRight.x = topRight.x * cosine - topRight.y * sine;
524  topRight.y = topRight.x * sine + topRight.y * cosine;
525 
526  bottomLeft += refPoint;
527  bottomRight += refPoint;
528  topLeft += refPoint;
529  topRight += refPoint;
530 
531  updateImageLimits( bottomLeft );
532  updateImageLimits( bottomRight );
533  updateImageLimits( topLeft );
534  updateImageLimits( topRight );
535 
536 }
537 
538 
539 void DXF_IMPORT_PLUGIN::addMText( const DL_MTextData& aData )
540 {
541  if( m_inBlock )
542  return;
543 
544  wxString text = toNativeString( wxString::FromUTF8( aData.text.c_str() ) );
545  wxString attrib, tmp;
546 
547  double textHeight = mapDim( aData.height );
548  // The 0.9 factor gives a better height/width ratio with our font
549  double charWidth = textHeight * 0.9;
550  double textWidth = charWidth * text.length(); // Rough approximation
551  double textThickness = textHeight/8.0; // Use a reasonable line thickness for this text
552 
553  VECTOR2D bottomLeft(0.0, 0.0);
554  VECTOR2D bottomRight(0.0, 0.0);
555  VECTOR2D topLeft(0.0, 0.0);
556  VECTOR2D topRight(0.0, 0.0);
557 
558  /* Some texts start by '\' and have formatting chars (font name, font option...)
559  * ending with ';'
560  * Here are some mtext formatting codes:
561  * Format code Purpose
562  * \0...\o Turns overline on and off
563  * \L...\l Turns underline on and off
564  * \~ Inserts a nonbreaking space
565  \\ Inserts a backslash
566  \\\{...\} Inserts an opening and closing brace
567  \\ \File name; Changes to the specified font file
568  \\ \Hvalue; Changes to the text height specified in drawing units
569  \\ \Hvaluex; Changes the text height to a multiple of the current text height
570  \\ \S...^...; Stacks the subsequent text at the \, #, or ^ symbol
571  \\ \Tvalue; Adjusts the space between characters, from.75 to 4 times
572  \\ \Qangle; Changes oblique angle
573  \\ \Wvalue; Changes width factor to produce wide text
574  \\ \A Sets the alignment value; valid values: 0, 1, 2 (bottom, center, top) while( text.StartsWith( wxT("\\") ) )
575  */
576  while( text.StartsWith( wxT( "\\" ) ) )
577  {
578  attrib << text.BeforeFirst( ';' );
579  tmp = text.AfterFirst( ';' );
580  text = tmp;
581  }
582 
583  VECTOR2D textpos( mapX( aData.ipx ), mapY( aData.ipy ) );
584 
585  // Initialize text justifications:
588 
589  if( aData.attachmentPoint <= 3 )
590  {
591  vJustify = GR_TEXT_VJUSTIFY_TOP;
592 
593  bottomLeft.y = -textHeight;
594  bottomRight.y = -textHeight;
595  }
596  else if( aData.attachmentPoint <= 6 )
597  {
598  vJustify = GR_TEXT_VJUSTIFY_CENTER;
599 
600  bottomRight.y = -textHeight / 2.0;
601  bottomLeft.y = -textHeight / 2.0;
602  topLeft.y = textHeight / 2.0;
603  topRight.y = textHeight / 2.0;
604  }
605  else
606  {
607  vJustify = GR_TEXT_VJUSTIFY_BOTTOM;
608 
609  topLeft.y = textHeight;
610  topRight.y = textHeight;
611  }
612 
613  if( aData.attachmentPoint % 3 == 1 )
614  {
615  hJustify = GR_TEXT_HJUSTIFY_LEFT;
616 
617  bottomRight.x = textWidth;
618  topRight.x = textWidth;
619  }
620  else if( aData.attachmentPoint % 3 == 2 )
621  {
622  hJustify = GR_TEXT_HJUSTIFY_CENTER;
623 
624  bottomLeft.x = -textWidth / 2.0;
625  topLeft.x = -textWidth / 2.0;
626  bottomRight.x = textWidth / 2.0;
627  topRight.x = textWidth / 2.0;
628  }
629  else
630  {
631  hJustify = GR_TEXT_HJUSTIFY_RIGHT;
632 
633  bottomLeft.x = -textWidth;
634  topLeft.x = -textWidth;
635  }
636 
637 #if 0 // These setting have no meaning in Pcbnew
638  if( data.alignH == 1 )
639  {
640  // Text is left to right;
641  }
642  else if( data.alignH == 3 )
643  {
644  // Text is top to bottom;
645  }
646  else
647  {
648  // use ByStyle;
649  }
650 
651  if( aData.alignV == 1 )
652  {
653  // use AtLeast;
654  }
655  else
656  {
657  // useExact;
658  }
659 #endif
660 
661  // dxf_lib imports text angle in radians (although there are no comment about that.
662  // So, for the moment, convert this angle to degrees
663  double angle_degree = aData.angle * 180/M_PI;
664  // We also need the angle in radians. so convert angle_degree to radians
665  // regardless the aData.angle unit
666  double angleInRads = angle_degree * M_PI / 180.0;
667  double cosine = cos(angleInRads);
668  double sine = sin(angleInRads);
669 
670  m_internalImporter.AddText( textpos, text, textHeight, charWidth,
671  textThickness, angle_degree, hJustify, vJustify );
672 
673  bottomLeft.x = bottomLeft.x * cosine - bottomLeft.y * sine;
674  bottomLeft.y = bottomLeft.x * sine + bottomLeft.y * cosine;
675 
676  bottomRight.x = bottomRight.x * cosine - bottomRight.y * sine;
677  bottomRight.y = bottomRight.x * sine + bottomRight.y * cosine;
678 
679  topLeft.x = topLeft.x * cosine - topLeft.y * sine;
680  topLeft.y = topLeft.x * sine + topLeft.y * cosine;
681 
682  topRight.x = topRight.x * cosine - topRight.y * sine;
683  topRight.y = topRight.x * sine + topRight.y * cosine;
684 
685  bottomLeft += textpos;
686  bottomRight += textpos;
687  topLeft += textpos;
688  topRight += textpos;
689 
690  updateImageLimits( bottomLeft );
691  updateImageLimits( bottomRight );
692  updateImageLimits( topLeft );
693  updateImageLimits( topRight );
694 
695 }
696 
697 
698 void DXF_IMPORT_PLUGIN::setVariableInt( const std::string& key, int value, int code )
699 {
700  if( m_inBlock )
701  return;
702 
703  // Called for every int variable in the DXF file (e.g. "$INSUNITS").
704 
705  if( key == "$DWGCODEPAGE" )
706  {
707  m_codePage = value;
708  return;
709  }
710 
711  if( key == "$INSUNITS" ) // Drawing units
712  {
713  switch( value )
714  {
715  case 1: // inches
716  m_DXF2mm = 25.4;
717  break;
718 
719  case 2: // feet
720  m_DXF2mm = 304.8;
721  break;
722 
723  case 4: // mm
724  m_DXF2mm = 1.0;
725  break;
726 
727  case 5: // centimeters
728  m_DXF2mm = 10.0;
729  break;
730 
731  case 6: // meters
732  m_DXF2mm = 1000.0;
733  break;
734 
735  case 8: // microinches
736  m_DXF2mm = 2.54e-5;
737  break;
738 
739  case 9: // mils
740  m_DXF2mm = 0.0254;
741  break;
742 
743  case 10: // yards
744  m_DXF2mm = 914.4;
745  break;
746 
747  case 11: // Angstroms
748  m_DXF2mm = 1.0e-7;
749  break;
750 
751  case 12: // nanometers
752  m_DXF2mm = 1.0e-6;
753  break;
754 
755  case 13: // micrometers
756  m_DXF2mm = 1.0e-3;
757  break;
758 
759  case 14: // decimeters
760  m_DXF2mm = 100.0;
761  break;
762 
763  default:
764  // use the default of 1.0 for:
765  // 0: Unspecified Units
766  // 3: miles
767  // 7: kilometers
768  // 15: decameters
769  // 16: hectometers
770  // 17: gigameters
771  // 18: AU
772  // 19: lightyears
773  // 20: parsecs
774  m_DXF2mm = 1.0;
775  break;
776  }
777 
778  return;
779  }
780 }
781 
782 
783 void DXF_IMPORT_PLUGIN::setVariableString( const std::string& key, const std::string& value,
784  int code )
785 {
786  // Called for every string variable in the DXF file (e.g. "$ACADVER").
787 }
788 
789 
790 wxString DXF_IMPORT_PLUGIN::toDxfString( const wxString& aStr )
791 {
792  wxString res;
793  int j = 0;
794 
795  for( unsigned i = 0; i<aStr.length(); ++i )
796  {
797  int c = aStr[i];
798 
799  if( c > 175 || c < 11 )
800  {
801  res.append( aStr.Mid( j, i - j ) );
802  j = i;
803 
804  switch( c )
805  {
806  case 0x0A:
807  res += wxT( "\\P" );
808  break;
809 
810  // diameter:
811 #ifdef __WINDOWS_
812  // windows, as always, is special.
813  case 0x00D8:
814 #else
815  case 0x2205:
816 #endif
817  res += wxT( "%%C" );
818  break;
819 
820  // degree:
821  case 0x00B0:
822  res += wxT( "%%D" );
823  break;
824 
825  // plus/minus
826  case 0x00B1:
827  res += wxT( "%%P" );
828  break;
829 
830  default:
831  j--;
832  break;
833  }
834 
835  j++;
836  }
837  }
838 
839  res.append( aStr.Mid( j ) );
840  return res;
841 }
842 
843 
844 wxString DXF_IMPORT_PLUGIN::toNativeString( const wxString& aData )
845 {
846  wxString res;
847 
848  // Ignore font tags:
849  int j = 0;
850 
851  for( unsigned i = 0; i < aData.length(); ++i )
852  {
853  if( aData[ i ] == 0x7B ) // is '{' ?
854  {
855  if( aData[ i + 1 ] == 0x5c && aData[ i + 2 ] == 0x66 ) // is "\f" ?
856  {
857  // found font tag, append parsed part
858  res.append( aData.Mid( j, i - j ) );
859 
860  // skip to ';'
861  for( unsigned k = i + 3; k < aData.length(); ++k )
862  {
863  if( aData[ k ] == 0x3B )
864  {
865  i = j = ++k;
866  break;
867  }
868  }
869 
870  // add to '}'
871  for( unsigned k = i; k < aData.length(); ++k )
872  {
873  if( aData[ k ] == 0x7D )
874  {
875  res.append( aData.Mid( i, k - i ) );
876  i = j = ++k;
877  break;
878  }
879  }
880  }
881  }
882  }
883 
884  res.append( aData.Mid( j ) );
885 
886 #if 1
887  wxRegEx regexp;
888  // Line feed:
889  regexp.Compile( wxT( "\\\\P" ) );
890  regexp.Replace( &res, wxT( "\n" ) );
891 
892  // Space:
893  regexp.Compile( wxT( "\\\\~" ) );
894  regexp.Replace( &res, wxT( " " ) );
895 
896  // diameter:
897  regexp.Compile( wxT( "%%[cC]" ) );
898 #ifdef __WINDOWS__
899  // windows, as always, is special.
900  regexp.Replace( &res, wxChar( 0xD8 ) );
901 #else
902  // Empty_set, diameter is 0x2300
903  regexp.Replace( &res, wxChar( 0x2205 ) );
904 #endif
905 
906  // degree:
907  regexp.Compile( wxT( "%%[dD]" ) );
908  regexp.Replace( &res, wxChar( 0x00B0 ) );
909  // plus/minus
910  regexp.Compile( wxT( "%%[pP]" ) );
911  regexp.Replace( &res, wxChar( 0x00B1 ) );
912 #endif
913 
914  return res;
915 }
916 
917 
918 void DXF_IMPORT_PLUGIN::addTextStyle( const DL_StyleData& aData )
919 {
920  // TODO
921 }
922 
923 
925  const VECTOR2D& aSegEnd, int aWidth )
926 {
927  VECTOR2D origin( SCALE_FACTOR( aSegStart.x ), SCALE_FACTOR( aSegStart.y ) );
928  VECTOR2D end( SCALE_FACTOR( aSegEnd.x ), SCALE_FACTOR( aSegEnd.y ) );
929  m_internalImporter.AddLine( origin, end, aWidth );
930 
931  updateImageLimits( origin );
932  updateImageLimits( end );
933 }
934 
935 
936 void DXF_IMPORT_PLUGIN::insertArc( const VECTOR2D& aSegStart, const VECTOR2D& aSegEnd,
937  double aBulge, int aWidth )
938 {
939  VECTOR2D segment_startpoint( SCALE_FACTOR( aSegStart.x ), SCALE_FACTOR( aSegStart.y ) );
940  VECTOR2D segment_endpoint( SCALE_FACTOR( aSegEnd.x ), SCALE_FACTOR( aSegEnd.y ) );
941 
942  // ensure aBulge represents an angle from +/- ( 0 .. approx 359.8 deg )
943  if( aBulge < -2000.0 )
944  aBulge = -2000.0;
945  else if( aBulge > 2000.0 )
946  aBulge = 2000.0;
947 
948  double ang = 4.0 * atan( aBulge );
949 
950  // reflect the Y values to put everything in a RHCS
951  VECTOR2D sp( aSegStart.x, -aSegStart.y );
952  VECTOR2D ep( aSegEnd.x, -aSegEnd.y );
953  // angle from end->start
954  double offAng = atan2( ep.y - sp.y, ep.x - sp.x );
955  // length of subtended segment = 1/2 distance between the 2 points
956  double d = 0.5 * sqrt( (sp.x - ep.x) * (sp.x - ep.x) + (sp.y - ep.y) * (sp.y - ep.y) );
957  // midpoint of the subtended segment
958  double xm = ( sp.x + ep.x ) * 0.5;
959  double ym = ( sp.y + ep.y ) * 0.5;
960  double radius = d / sin( ang * 0.5 );
961 
962  if( radius < 0.0 )
963  radius = -radius;
964 
965  // calculate the height of the triangle with base d and hypotenuse r
966  double dh2 = radius * radius - d * d;
967 
968  // this should only ever happen due to rounding errors when r == d
969  if( dh2 < 0.0 )
970  dh2 = 0.0;
971 
972  double h = sqrt( dh2 );
973 
974  if( ang < 0.0 )
975  offAng -= M_PI_2;
976  else
977  offAng += M_PI_2;
978 
979  // for angles greater than 180 deg we need to flip the
980  // direction in which the arc center is found relative
981  // to the midpoint of the subtended segment.
982  if( ang < -M_PI )
983  offAng += M_PI;
984  else if( ang > M_PI )
985  offAng -= M_PI;
986 
987  // center point
988  double cx = h * cos( offAng ) + xm;
989  double cy = h * sin( offAng ) + ym;
990  VECTOR2D center( SCALE_FACTOR( cx ), SCALE_FACTOR( -cy ) );
991  VECTOR2D arc_start;
992  double angle = RAD2DEG( ang );
993 
994  if( ang < 0.0 )
995  {
996  arc_start = VECTOR2D( SCALE_FACTOR( ep.x ), SCALE_FACTOR( -ep.y ) );
997  }
998  else
999  {
1000  arc_start = VECTOR2D( SCALE_FACTOR( sp.x ), SCALE_FACTOR( -sp.y ) );
1001  angle = -angle;
1002  }
1003 
1004  m_internalImporter.AddArc( center, arc_start, angle, aWidth );
1005 
1006  VECTOR2D radiusDelta( SCALE_FACTOR( radius ), SCALE_FACTOR( radius ) );
1007 
1008  updateImageLimits( center + radiusDelta );
1009  updateImageLimits( center - radiusDelta );
1010  return;
1011 }
1012 
1013 
1014 #include "tinysplinecpp.h"
1015 
1017 {
1018  #if 0 // Debug only
1019  wxLogMessage("spl deg %d kn %d ctr %d fit %d",
1024  #endif
1025 
1026  unsigned imax = m_curr_entity.m_SplineControlPointList.size();
1027 
1028  if( imax < 2 ) // malformed spline
1029  return;
1030 
1031 #if 0 // set to 1 to approximate the spline by segments between 2 control points
1032  VECTOR2D startpoint( mapX( m_curr_entity.m_SplineControlPointList[0].m_x ),
1034 
1035  for( unsigned int ii = 1; ii < imax; ++ii )
1036  {
1037  VECTOR2D endpoint( mapX( m_curr_entity.m_SplineControlPointList[ii].m_x ),
1039 
1040  if( startpoint != endpoint )
1041  {
1042  m_internalImporter.AddLine( startpoint, endpoint, aWidth );
1043 
1044  updateImageLimits( startpoint );
1045  updateImageLimits( endpoint );
1046 
1047  startpoint = endpoint;
1048  }
1049  }
1050 #else // Use bezier curves, supported by pcbnew, to approximate the spline
1051  tinyspline::BSpline dxfspline( m_curr_entity.m_SplineControlPointList.size(),
1052  /* coord dim */ 2, m_curr_entity.m_SplineDegree );
1053  std::vector<double> ctrlp;
1054 
1055  for( unsigned ii = 0; ii < imax; ++ii )
1056  {
1057  ctrlp.push_back( m_curr_entity.m_SplineControlPointList[ii].m_x );
1058  ctrlp.push_back( m_curr_entity.m_SplineControlPointList[ii].m_y );
1059  }
1060 
1061  dxfspline.setCtrlp( ctrlp );
1062  dxfspline.setKnots( m_curr_entity.m_SplineKnotsList );
1063  tinyspline::BSpline beziers( dxfspline.toBeziers() );
1064 
1065  std::vector<double> coords = beziers.ctrlp();
1066 
1067  // Each Bezier curve uses 4 vertices (a start point, 2 control points and a end point).
1068  // So we can have more than one Bezier curve ( there are one curve each four vertices)
1069  // However, one can have one Bezier curve with end point = ctrl point 2, having only 3
1070  // defined points in list.
1071  for( unsigned ii = 0; ii < coords.size(); ii += 8 )
1072  {
1073  VECTOR2D start( mapX( coords[ii] ), mapY( coords[ii+1] ) );
1074  VECTOR2D bezierControl1( mapX( coords[ii+2] ), mapY( coords[ii+3] ) );
1075  VECTOR2D bezierControl2( mapX( coords[ii+4] ), mapY( coords[ii+5] ) );
1076  VECTOR2D end;
1077 
1078  if( ii+7 < coords.size() )
1079  end = VECTOR2D( mapX( coords[ii+6] ), mapY( coords[ii+7] ) );
1080  else
1081  end = bezierControl2;
1082 
1083  m_internalImporter.AddSpline( start, bezierControl1, bezierControl2, end , aWidth );
1084  }
1085 #endif
1086 }
1087 
1088 
1090 {
1091  m_minX = std::min( aPoint.x, m_minX );
1092  m_maxX = std::max( aPoint.x, m_maxX );
1093 
1094  m_minY = std::min( aPoint.y, m_minY );
1095  m_maxY = std::max( aPoint.y, m_maxY );
1096 }
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:47
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:54
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