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