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