KiCad PCB EDA Suite
Loading...
Searching...
No Matches
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 The 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#include <geometry/ellipse.h>
36#include <bezier_curves.h>
37
38#include <trigo.h>
39#include <macros.h>
40#include <cmath> // isnan
41#include <board.h>
42#include "common.h"
43
44
45/*
46 * Important notes
47 * 1. All output coordinates of this importer are in mm
48 * 2. DXFs have a concept of world (WCS) and object coordinates (OCS)
49 3. The following objects are world coordinates:
50 - Line
51 - Point
52 - Polyline (3D)
53 - Vertex (3D)
54 - Polymesh
55 - Polyface
56 - Viewport
57 4. The following entities are object coordinates
58 - Circle
59 - Arc
60 - Solid
61 - Trace
62 - Attrib
63 - Shape
64 - Insert
65 - Polyline (2D)
66 - Vertex (2D)
67 - LWPolyline
68 - Hatch
69 - Image
70 - Text
71 * 5. Object coordinates must be run through the arbitrary axis
72 * translation even though they are 2D drawings and most of the time
73 * the import is fine. Sometimes, against all logic, CAD tools like
74 * SolidWorks may randomly insert circles "mirror" that must be unflipped
75 * by following the object to world conversion
76 * 6. Blocks are virtual groups, blocks must be placed by a INSERT entity
77 * 7. Blocks may be repeated multiple times
78 * 8. There is no sane way to make text look perfect like the original CAD.
79 * DXF simply does mpt specifying text/font enough to make it portable.
80 * We however make do try to get it somewhat close/visually appealing.
81 * 9. We silently drop the z coordinate on 3d polylines
82 */
83
84
85// minimum bulge value before resorting to a line segment;
86// the value 0.0218 is equivalent to about 5 degrees arc,
87#define MIN_BULGE 0.0218
88
89#define SCALE_FACTOR(x) (x)
90
91
93{
94 m_xOffset = 0.0; // X coord offset for conversion (in mm)
95 m_yOffset = 0.0; // Y coord offset for conversion (in mm)
96 m_version = 0; // the dxf version, not yet used
97 m_defaultThickness = 0.2; // default thickness (in mm)
98 m_brdLayer = Dwgs_User; // The default import layer
99 m_importAsFPShapes = true;
100 m_minX = m_minY = std::numeric_limits<double>::max();
101 m_maxX = m_maxY = std::numeric_limits<double>::lowest();
103
104 m_importCoordinatePrecision = 4; // initial value per dxf spec
105 m_importAnglePrecision = 0; // initial value per dxf spec
106
107 // placeholder layer so we can fallback to something later
108 auto layer0 = std::make_unique<DXF_IMPORT_LAYER>( "", DXF_IMPORT_LINEWEIGHT_BY_LW_DEFAULT );
109 m_layers.push_back( std::move( layer0 ) );
110
111 m_currentBlock = nullptr;
112}
113
114
118
119
120bool DXF_IMPORT_PLUGIN::Load( const wxString& aFileName )
121{
122 try
123 {
124 return ImportDxfFile( aFileName );
125 }
126 catch( const std::bad_alloc& )
127 {
128 m_layers.clear();
129 m_blocks.clear();
130 m_styles.clear();
131
132 m_internalImporter.ClearShapes();
133
134 ReportMsg( _( "Memory was exhausted trying to load the DXF, it may be too large." ) );
135 return false;
136 }
137}
138
139
140bool DXF_IMPORT_PLUGIN::LoadFromMemory( const wxMemoryBuffer& aMemBuffer )
141{
142 try
143 {
144 return ImportDxfFile( aMemBuffer );
145 }
146 catch( const std::bad_alloc& )
147 {
148 m_layers.clear();
149 m_blocks.clear();
150 m_styles.clear();
151
152 m_internalImporter.ClearShapes();
153
154 ReportMsg( _( "Memory was exhausted trying to load the DXF, it may be too large." ) );
155 return false;
156 }
157}
158
159
161{
162 wxCHECK( m_importer, false );
163 m_internalImporter.ImportTo( *m_importer );
164
165 return true;
166}
167
168
170{
171 return m_maxX - m_minX;
172}
173
174
176{
177 return m_maxY - m_minY;
178}
179
180
182{
183 BOX2D bbox;
184 bbox.SetOrigin( m_minX, m_minY );
185 bbox.SetEnd( m_maxX, m_maxY );
186
187 return bbox;
188}
189
190
192{
194
195 if( m_importer )
196 SetDefaultLineWidthMM( m_importer->GetLineWidthMM() );
197}
198
199
200double DXF_IMPORT_PLUGIN::mapX( double aDxfCoordX )
201{
202 return SCALE_FACTOR( m_xOffset + ( aDxfCoordX * getCurrentUnitScale() ) );
203}
204
205
206double DXF_IMPORT_PLUGIN::mapY( double aDxfCoordY )
207{
208 return SCALE_FACTOR( m_yOffset - ( aDxfCoordY * getCurrentUnitScale() ) );
209}
210
211
212double DXF_IMPORT_PLUGIN::mapDim( double aDxfValue )
213{
214 return SCALE_FACTOR( aDxfValue * getCurrentUnitScale() );
215}
216
217
218bool DXF_IMPORT_PLUGIN::ImportDxfFile( const wxString& aFile )
219{
220 DL_Dxf dxf_reader;
221
222 // wxFopen takes care of unicode filenames across platforms
223 FILE* fp = wxFopen( aFile, wxT( "rt" ) );
224
225 if( fp == nullptr )
226 return false;
227
228 // Note the dxf reader takes care of switching to "C" locale before reading the file
229 // and will close the file after reading
230 bool success = dxf_reader.in( fp, this );
231
232 return success;
233}
234
235
236bool DXF_IMPORT_PLUGIN::ImportDxfFile( const wxMemoryBuffer& aMemBuffer )
237{
238 DL_Dxf dxf_reader;
239
240 std::string str( reinterpret_cast<char*>( aMemBuffer.GetData() ), aMemBuffer.GetDataLen() );
241
242 // Note the dxf reader takes care of switching to "C" locale before reading the file
243 // and will close the file after reading
244 bool success = dxf_reader.in( str, this );
245
246 return success;
247}
248
249
250void DXF_IMPORT_PLUGIN::ReportMsg( const wxString& aMessage )
251{
252 // Add message to keep trace of not handled dxf entities
253 m_messages += aMessage;
254 m_messages += '\n';
255}
256
257
258void DXF_IMPORT_PLUGIN::addSpline( const DL_SplineData& aData )
259{
260 // Called when starting reading a spline
261 m_curr_entity.Clear();
262 m_curr_entity.m_EntityParseStatus = 1;
263 m_curr_entity.m_EntityFlag = aData.flags;
264 m_curr_entity.m_EntityType = DL_ENTITY_SPLINE;
265 m_curr_entity.m_SplineDegree = aData.degree;
266 m_curr_entity.m_SplineTangentStartX = aData.tangentStartX;
267 m_curr_entity.m_SplineTangentStartY = aData.tangentStartY;
268 m_curr_entity.m_SplineTangentEndX = aData.tangentEndX;
269 m_curr_entity.m_SplineTangentEndY = aData.tangentEndY;
270 m_curr_entity.m_SplineKnotsCount = aData.nKnots;
271 m_curr_entity.m_SplineControlCount = aData.nControl;
272 m_curr_entity.m_SplineFitCount = aData.nFit;
273 m_curr_entity.m_LayerName = getDxfLayerName( attributes.getLayer() );
274}
275
276
277void DXF_IMPORT_PLUGIN::addControlPoint( const DL_ControlPointData& aData )
278{
279 // Called for every spline control point, when reading a spline entity
280 m_curr_entity.m_SplineControlPointList.emplace_back( aData.x , aData.y, aData.w );
281}
282
283
284void DXF_IMPORT_PLUGIN::addFitPoint( const DL_FitPointData& aData )
285{
286 // Called for every spline fit point, when reading a spline entity
287 // we store only the X,Y coord values in a VECTOR2D
288 m_curr_entity.m_SplineFitPointList.emplace_back( aData.x, aData.y );
289}
290
291
292void DXF_IMPORT_PLUGIN::addKnot( const DL_KnotData& aData)
293{
294 // Called for every spline knot value, when reading a spline entity
295 m_curr_entity.m_SplineKnotsList.push_back( aData.k );
296}
297
298
299void DXF_IMPORT_PLUGIN::addLayer( const DL_LayerData& aData )
300{
301 wxString name = wxString::FromUTF8( aData.name.c_str() );
302
303 int lw = attributes.getWidth();
304
307
308 std::unique_ptr<DXF_IMPORT_LAYER> layer = std::make_unique<DXF_IMPORT_LAYER>( name, lw );
309
310 m_layers.push_back( std::move( layer ) );
311}
312
313
314void DXF_IMPORT_PLUGIN::addLinetype( const DL_LinetypeData& data )
315{
316#if 0
317 wxString name = From_UTF8( data.name.c_str() );
318 wxString description = From_UTF8( data.description.c_str() );
319#endif
320}
321
322
324{
325 if( lw == DXF_IMPORT_LINEWEIGHT_BY_LAYER && aLayer != nullptr )
326 lw = aLayer->m_lineWeight;
327
328 // All lineweights >= 0 are always in 100ths of mm
329 double mm = m_defaultThickness;
330
331 if( lw >= 0 )
332 mm = lw / 100.0;
333
334 return SCALE_FACTOR( mm );
335}
336
337
339{
340 DXF_IMPORT_LAYER* layer = m_layers.front().get();
341 wxString layerName = wxString::FromUTF8( aLayerName.c_str() );
342
343 if( !layerName.IsEmpty() )
344 {
345 auto resultIt = std::find_if( m_layers.begin(), m_layers.end(),
346 [layerName]( const auto& it )
347 {
348 return it->m_layerName == layerName;
349 } );
350
351 if( resultIt != m_layers.end() )
352 layer = resultIt->get();
353 }
354
355 return layer;
356}
357
358
359wxString DXF_IMPORT_PLUGIN::getDxfLayerName( const std::string& aLayerName ) const
360{
361 wxString layerName = wxString::FromUTF8( aLayerName.c_str() );
362
363 if( layerName.IsEmpty() )
364 layerName = wxS( "0" );
365
366 return layerName;
367}
368
369
371{
372 DXF_IMPORT_BLOCK* block = nullptr;
373 wxString blockName = wxString::FromUTF8( aBlockName.c_str() );
374
375 if( !blockName.IsEmpty() )
376 {
377 auto resultIt = std::find_if( m_blocks.begin(), m_blocks.end(),
378 [blockName]( const auto& it )
379 {
380 return it->m_name == blockName;
381 } );
382
383 if( resultIt != m_blocks.end() )
384 block = resultIt->get();
385 }
386
387 return block;
388}
389
390
392{
393 DXF_IMPORT_STYLE* style = nullptr;
394 wxString styleName = wxString::FromUTF8( aStyleName.c_str() );
395
396 if( !styleName.IsEmpty() )
397 {
398 auto resultIt = std::find_if( m_styles.begin(), m_styles.end(),
399 [styleName]( const auto& it )
400 {
401 return it->m_name == styleName;
402 } );
403
404 if( resultIt != m_styles.end() )
405 style = resultIt->get();
406 }
407
408 return style;
409}
410
411
412void DXF_IMPORT_PLUGIN::addLine( const DL_LineData& aData )
413{
414 DXF_IMPORT_LAYER* layer = getImportLayer( attributes.getLayer() );
415 double lineWidth = lineWeightToWidth( attributes.getWidth(), layer );
416 wxString sourceLayer = getDxfLayerName( attributes.getLayer() );
417
418 VECTOR2D start( mapX( aData.x1 ), mapY( aData.y1 ) );
419 VECTOR2D end( mapX( aData.x2 ), mapY( aData.y2 ) );
420
421 GRAPHICS_IMPORTER_BUFFER* bufferToUse = m_currentBlock ? &m_currentBlock->m_buffer
423 bufferToUse->SetCurrentSourceLayer( sourceLayer );
424 bufferToUse->AddLine( start, end, lineWidth );
425
426 updateImageLimits( start );
428}
429
430
431void DXF_IMPORT_PLUGIN::addPolyline(const DL_PolylineData& aData )
432{
433 // Convert DXF Polylines into a series of KiCad Lines and Arcs.
434 // A Polyline (as opposed to a LWPolyline) may be a 3D line or
435 // even a 3D Mesh. The only type of Polyline which is guaranteed
436 // to import correctly is a 2D Polyline in X and Y, which is what
437 // we assume of all Polylines. The width used is the width of the Polyline.
438 // per-vertex line widths, if present, are ignored.
439 m_curr_entity.Clear();
440 m_curr_entity.m_EntityParseStatus = 1;
441 m_curr_entity.m_EntityFlag = aData.flags;
442 m_curr_entity.m_EntityType = DL_ENTITY_POLYLINE;
443 m_curr_entity.m_LayerName = getDxfLayerName( attributes.getLayer() );
444}
445
446
447void DXF_IMPORT_PLUGIN::addVertex( const DL_VertexData& aData )
448{
449 if( m_curr_entity.m_EntityParseStatus == 0 )
450 return; // Error
451
452 DXF_IMPORT_LAYER* layer = getImportLayer( attributes.getLayer() );
453 double lineWidth = lineWeightToWidth( attributes.getWidth(), layer );
454
455 /* support for per-vertex-encoded linewidth (Cadence uses it) */
456 /* linewidths are scaled by 100 in DXF */
457 if( aData.startWidth > 0.0 )
458 lineWidth = aData.startWidth / 100.0;
459 else if ( aData.endWidth > 0.0 )
460 lineWidth = aData.endWidth / 100.0;
461
462 const DL_VertexData* vertex = &aData;
463
464 MATRIX3x3D arbAxis = getArbitraryAxis( getExtrusion() );
465 VECTOR3D vertexCoords = ocsToWcs( arbAxis, VECTOR3D( vertex->x, vertex->y, vertex->z ) );
466
467 if( m_curr_entity.m_EntityParseStatus == 1 ) // This is the first vertex of an entity
468 {
469 m_curr_entity.m_LastCoordinate.x = mapX( vertexCoords.x );
470 m_curr_entity.m_LastCoordinate.y = mapY( vertexCoords.y );
471 m_curr_entity.m_PolylineStart = m_curr_entity.m_LastCoordinate;
472 m_curr_entity.m_BulgeVertex = vertex->bulge;
473 m_curr_entity.m_EntityParseStatus = 2;
474 return;
475 }
476
477 VECTOR2D seg_end( mapX( vertexCoords.x ), mapY( vertexCoords.y ) );
478
479 if( std::abs( m_curr_entity.m_BulgeVertex ) < MIN_BULGE )
480 insertLine( m_curr_entity.m_LastCoordinate, seg_end, lineWidth );
481 else
482 insertArc( m_curr_entity.m_LastCoordinate, seg_end, m_curr_entity.m_BulgeVertex,
483 lineWidth );
484
485 m_curr_entity.m_LastCoordinate = seg_end;
486 m_curr_entity.m_BulgeVertex = vertex->bulge;
487}
488
489
491{
492 DXF_IMPORT_LAYER* layer = getImportLayer( attributes.getLayer() );
493 double lineWidth = lineWeightToWidth( attributes.getWidth(), layer );
494
495 if( m_curr_entity.m_EntityType == DL_ENTITY_POLYLINE ||
496 m_curr_entity.m_EntityType == DL_ENTITY_LWPOLYLINE )
497 {
498 // Polyline flags bit 0 indicates closed (1) or open (0) polyline
499 if( m_curr_entity.m_EntityFlag & 1 )
500 {
501 if( std::abs( m_curr_entity.m_BulgeVertex ) < MIN_BULGE )
502 {
503 insertLine( m_curr_entity.m_LastCoordinate, m_curr_entity.m_PolylineStart,
504 lineWidth );
505 }
506 else
507 {
508 insertArc( m_curr_entity.m_LastCoordinate, m_curr_entity.m_PolylineStart,
509 m_curr_entity.m_BulgeVertex, lineWidth );
510 }
511 }
512 }
513
514 if( m_curr_entity.m_EntityType == DL_ENTITY_SPLINE )
515 insertSpline( lineWidth );
516
517 m_curr_entity.Clear();
518}
519
520
521void DXF_IMPORT_PLUGIN::addBlock( const DL_BlockData& aData )
522{
523 wxString name = wxString::FromUTF8( aData.name.c_str() );
524
525 std::unique_ptr<DXF_IMPORT_BLOCK> block = std::make_unique<DXF_IMPORT_BLOCK>( name, aData.bpx,
526 aData.bpy );
527
528 m_blocks.push_back( std::move( block ) );
529
530 m_currentBlock = m_blocks.back().get();
531}
532
533
535{
536 m_currentBlock = nullptr;
537}
538
539void DXF_IMPORT_PLUGIN::addInsert( const DL_InsertData& aData )
540{
541 DXF_IMPORT_BLOCK* block = getImportBlock( aData.name );
542
543 if( block == nullptr )
544 return;
545
546 wxString insertLayer = getDxfLayerName( attributes.getLayer() );
547
548 MATRIX3x3D arbAxis = getArbitraryAxis( getExtrusion() );
549
550 MATRIX3x3D rot;
551 rot.SetRotation( DEG2RAD( -aData.angle ) ); // DL_InsertData angle is in degrees
552
554 scale.SetScale( VECTOR2D( aData.sx, aData.sy ) );
555
556 MATRIX3x3D trans = ( arbAxis * rot ) * scale;
557 VECTOR3D insertCoords = ocsToWcs( arbAxis, VECTOR3D( aData.ipx, aData.ipy, aData.ipz ) );
558
559 VECTOR2D translation( mapX( insertCoords.x ), mapY( insertCoords.y ) );
560 translation -= VECTOR2D( mapX( block->m_baseX ), mapY( block->m_baseY ) );
561
562 for( const std::unique_ptr<IMPORTED_SHAPE>& shape : block->m_buffer.GetShapes() )
563 {
564 std::unique_ptr<IMPORTED_SHAPE> newShape = shape->clone();
565
566 newShape->Transform( trans, translation );
567
568 if( newShape->GetSourceLayer().IsEmpty() || newShape->GetSourceLayer() == wxS( "0" ) )
569 newShape->SetSourceLayer( insertLayer );
570
571 m_internalImporter.AddShape( newShape );
572 }
573}
574
575
576void DXF_IMPORT_PLUGIN::addCircle( const DL_CircleData& aData )
577{
578 MATRIX3x3D arbAxis = getArbitraryAxis( getExtrusion() );
579 VECTOR3D centerCoords = ocsToWcs( arbAxis, VECTOR3D( aData.cx, aData.cy, aData.cz ) );
580
581 VECTOR2D center( mapX( centerCoords.x ), mapY( centerCoords.y ) );
582 DXF_IMPORT_LAYER* layer = getImportLayer( attributes.getLayer() );
583 double lineWidth = lineWeightToWidth( attributes.getWidth(), layer );
584 wxString sourceLayer = getDxfLayerName( attributes.getLayer() );
585
586 GRAPHICS_IMPORTER_BUFFER* bufferToUse = m_currentBlock ? &m_currentBlock->m_buffer
588 bufferToUse->SetCurrentSourceLayer( sourceLayer );
589 bufferToUse->AddCircle( center, mapDim( aData.radius ), lineWidth, false );
590
591 VECTOR2D radiusDelta( mapDim( aData.radius ), mapDim( aData.radius ) );
592
593 updateImageLimits( center + radiusDelta );
594 updateImageLimits( center - radiusDelta );
595}
596
597
598void DXF_IMPORT_PLUGIN::addArc( const DL_ArcData& aData )
599{
600 MATRIX3x3D arbAxis = getArbitraryAxis( getExtrusion() );
601 VECTOR3D centerCoords = ocsToWcs( arbAxis, VECTOR3D( aData.cx, aData.cy, aData.cz ) );
602
603 // Init arc centre:
604 VECTOR2D center( mapX( centerCoords.x ), mapY( centerCoords.y ) );
605
606 // aData.anglex is in degrees.
607 EDA_ANGLE startangle( aData.angle1, DEGREES_T );
608 EDA_ANGLE endangle( aData.angle2, DEGREES_T );
609
610 if( ( arbAxis.GetScale().x < 0 ) != ( arbAxis.GetScale().y < 0 ) )
611 {
612 startangle = ANGLE_180 - startangle;
613 endangle = ANGLE_180 - endangle;
614 std::swap( startangle, endangle );
615 }
616
617 // Init arc start point
618 VECTOR2D startPoint( aData.radius, 0.0 );
619 RotatePoint( startPoint, -startangle );
620 VECTOR2D arcStart( mapX( startPoint.x + centerCoords.x ),
621 mapY( startPoint.y + centerCoords.y ) );
622
623 // calculate arc angle (arcs are CCW, and should be < 0 in Pcbnew)
624 EDA_ANGLE angle = -( endangle - startangle );
625
626 if( angle > ANGLE_0 )
627 angle -= ANGLE_360;
628
629 DXF_IMPORT_LAYER* layer = getImportLayer( attributes.getLayer() );
630 double lineWidth = lineWeightToWidth( attributes.getWidth(), layer );
631 wxString sourceLayer = getDxfLayerName( attributes.getLayer() );
632
633 GRAPHICS_IMPORTER_BUFFER* bufferToUse = m_currentBlock ? &m_currentBlock->m_buffer
635 bufferToUse->SetCurrentSourceLayer( sourceLayer );
636 bufferToUse->AddArc( center, arcStart, angle, lineWidth );
637
638 VECTOR2D radiusDelta( mapDim( aData.radius ), mapDim( aData.radius ) );
639
640 updateImageLimits( center + radiusDelta );
641 updateImageLimits( center - radiusDelta );
642}
643
644
645void DXF_IMPORT_PLUGIN::addEllipse( const DL_EllipseData& aData )
646{
647 MATRIX3x3D arbAxis = getArbitraryAxis( getExtrusion() );
648 VECTOR3D centerCoords = ocsToWcs( arbAxis, VECTOR3D( aData.cx, aData.cy, aData.cz ) );
649 VECTOR3D majorCoords = ocsToWcs( arbAxis, VECTOR3D( aData.mx, aData.my, aData.mz ) );
650
651 // DXF ellipses store the minor axis length as a ratio to the major axis.
652 // The major coords are relative to the center point.
653 // For now, we assume ellipses in the XY plane.
654
655 VECTOR2D center( mapX( centerCoords.x ), mapY( centerCoords.y ) );
656 VECTOR2D major( mapX( majorCoords.x ), mapY( majorCoords.y ) );
657
658 // DXF elliptical arcs store their angles in radians (unlike circular arcs which use degrees)
659 // The arcs wind CCW as in KiCad. The end angle must be greater than the start angle, and if
660 // the extrusion direction is negative, the arc winding is CW instead! Note that this is a
661 // simplification that assumes the DXF is representing a 2D drawing, and would need to be
662 // revisited if we want to import true 3D drawings and "flatten" them to the 2D KiCad plane
663 // internally.
664 EDA_ANGLE startAngle( aData.angle1, RADIANS_T );
665 EDA_ANGLE endAngle( aData.angle2, RADIANS_T );
666
667 if( startAngle > endAngle )
668 endAngle += ANGLE_360;
669
670 if( aData.ratio == 1.0 )
671 {
672 double radius = major.EuclideanNorm();
673
674 if( startAngle == endAngle )
675 {
676 DL_CircleData circle( aData.cx, aData.cy, aData.cz, radius );
677 addCircle( circle );
678 return;
679 }
680 else
681 {
682 // Angles are relative to major axis
683 startAngle -= EDA_ANGLE( major );
684 endAngle -= EDA_ANGLE( major );
685
686 DL_ArcData arc( aData.cx, aData.cy, aData.cz, radius, startAngle.AsDegrees(),
687 endAngle.AsDegrees() );
688 addArc( arc );
689 return;
690 }
691 }
692
693 // TODO: testcases for negative extrusion vector; handle it here
694
695 DXF_IMPORT_LAYER* layer = getImportLayer( attributes.getLayer() );
696 double lineWidth = lineWeightToWidth( attributes.getWidth(), layer );
697 wxString sourceLayer = getDxfLayerName( attributes.getLayer() );
698
699 GRAPHICS_IMPORTER_BUFFER* bufferToUse = m_currentBlock ? &m_currentBlock->m_buffer
701 bufferToUse->SetCurrentSourceLayer( sourceLayer );
702
703 double majorRadius = major.EuclideanNorm();
704 double minorRadius = majorRadius * aData.ratio;
705 EDA_ANGLE rotation( major );
706
707 if( startAngle == endAngle || std::abs( ( endAngle - startAngle ).AsDegrees() - 360.0 ) < 1e-9 )
708 {
709 bufferToUse->AddEllipse( center, majorRadius, minorRadius, rotation, lineWidth, false /* aFilled */ );
710 }
711 else
712 {
713 bufferToUse->AddEllipseArc( center, majorRadius, minorRadius, rotation, startAngle, endAngle, lineWidth );
714 }
715
716 // Naive bounding
717 updateImageLimits( center + major );
718 updateImageLimits( center - major );
719}
720
721
722void DXF_IMPORT_PLUGIN::addText( const DL_TextData& aData )
723{
724 MATRIX3x3D arbAxis = getArbitraryAxis( getExtrusion() );
725 VECTOR3D refPointCoords = ocsToWcs( arbAxis, VECTOR3D( aData.ipx, aData.ipy, aData.ipz ) );
726 VECTOR3D secPointCoords =
727 ocsToWcs( arbAxis, VECTOR3D( std::isnan( aData.apx ) ? 0 : aData.apx,
728 std::isnan( aData.apy ) ? 0 : aData.apy,
729 std::isnan( aData.apz ) ? 0 : aData.apz ) );
730
731 VECTOR2D refPoint( mapX( refPointCoords.x ), mapY( refPointCoords.y ) );
732 VECTOR2D secPoint( mapX( secPointCoords.x ), mapY( secPointCoords.y ) );
733
734 if( aData.vJustification != 0 || aData.hJustification != 0 || aData.hJustification == 4 )
735 {
736 if( aData.hJustification != 3 && aData.hJustification != 5 )
737 {
738 VECTOR2D tmp = secPoint;
739 secPoint = refPoint;
740 refPoint = tmp;
741 }
742 }
743
744 wxString text = toNativeString( wxString::FromUTF8( aData.text.c_str() ) );
745
746 DXF_IMPORT_STYLE* style = getImportStyle( aData.style.c_str() );
747
748 double textHeight = mapDim( aData.height );
749
750 // The 0.9 factor gives a better height/width base ratio with our font
751 double charWidth = textHeight * 0.9;
752
753 if( style != nullptr )
754 charWidth *= style->m_widthFactor;
755
756 double textWidth = charWidth * text.length(); // Rough approximation
757 double textThickness = textHeight / 8.0; // Use a reasonable line thickness for this text
758
759 VECTOR2D bottomLeft( 0.0, 0.0 );
760 VECTOR2D bottomRight( 0.0, 0.0 );
761 VECTOR2D topLeft( 0.0, 0.0 );
762 VECTOR2D topRight( 0.0, 0.0 );
763
766
767 switch( aData.vJustification )
768 {
769 case 0: //DRW_Text::VBaseLine:
770 case 1: //DRW_Text::VBottom:
771 vJustify = GR_TEXT_V_ALIGN_BOTTOM;
772
773 topLeft.y = textHeight;
774 topRight.y = textHeight;
775 break;
776
777 case 2: //DRW_Text::VMiddle:
778 vJustify = GR_TEXT_V_ALIGN_CENTER;
779
780 bottomRight.y = -textHeight / 2.0;
781 bottomLeft.y = -textHeight / 2.0;
782 topLeft.y = textHeight / 2.0;
783 topRight.y = textHeight / 2.0;
784 break;
785
786 case 3: //DRW_Text::VTop:
787 vJustify = GR_TEXT_V_ALIGN_TOP;
788
789 bottomLeft.y = -textHeight;
790 bottomRight.y = -textHeight;
791 break;
792 }
793
794 switch( aData.hJustification )
795 {
796 case 0: //DRW_Text::HLeft:
797 case 3: //DRW_Text::HAligned: // no equivalent options in text pcb.
798 case 5: //DRW_Text::HFit: // no equivalent options in text pcb.
799 hJustify = GR_TEXT_H_ALIGN_LEFT;
800
801 bottomRight.x = textWidth;
802 topRight.x = textWidth;
803 break;
804
805 case 1: //DRW_Text::HCenter:
806 case 4: //DRW_Text::HMiddle: // no equivalent options in text pcb.
807 hJustify = GR_TEXT_H_ALIGN_CENTER;
808
809 bottomLeft.x = -textWidth / 2.0;
810 topLeft.x = -textWidth / 2.0;
811 bottomRight.x = textWidth / 2.0;
812 topRight.x = textWidth / 2.0;
813 break;
814
815 case 2: //DRW_Text::HRight:
816 hJustify = GR_TEXT_H_ALIGN_RIGHT;
817
818 bottomLeft.x = -textWidth;
819 topLeft.x = -textWidth;
820 break;
821 }
822
823#if 0
824 wxString sty = wxString::FromUTF8( aData.style.c_str() );
825 sty = sty.ToLower();
826
827 if( aData.textgen == 2 )
828 {
829 // Text dir = left to right;
830 } else if( aData.textgen == 4 )
831 {
832 // Text dir = top to bottom;
833 } else
834 {
835 }
836#endif
837
838 // dxf_lib imports text angle in radians (although there are no comment about that.
839 // So, for the moment, convert this angle to degrees
840 double angle_degree = aData.angle * 180 / M_PI;
841
842 // We also need the angle in radians. so convert angle_degree to radians
843 // regardless the aData.angle unit
844 double angleInRads = angle_degree * M_PI / 180.0;
845 double cosine = cos(angleInRads);
846 double sine = sin(angleInRads);
847
848 GRAPHICS_IMPORTER_BUFFER* bufferToUse = m_currentBlock ? &m_currentBlock->m_buffer
850 bufferToUse->SetCurrentSourceLayer( getDxfLayerName( attributes.getLayer() ) );
851 bufferToUse->AddText( refPoint, text, textHeight, charWidth, textThickness, angle_degree,
852 hJustify, vJustify );
853
854 // Calculate the boundary box and update the image limits:
855 bottomLeft.x = bottomLeft.x * cosine - bottomLeft.y * sine;
856 bottomLeft.y = bottomLeft.x * sine + bottomLeft.y * cosine;
857
858 bottomRight.x = bottomRight.x * cosine - bottomRight.y * sine;
859 bottomRight.y = bottomRight.x * sine + bottomRight.y * cosine;
860
861 topLeft.x = topLeft.x * cosine - topLeft.y * sine;
862 topLeft.y = topLeft.x * sine + topLeft.y * cosine;
863
864 topRight.x = topRight.x * cosine - topRight.y * sine;
865 topRight.y = topRight.x * sine + topRight.y * cosine;
866
867 bottomLeft += refPoint;
868 bottomRight += refPoint;
869 topLeft += refPoint;
870 topRight += refPoint;
871
872 updateImageLimits( bottomLeft );
873 updateImageLimits( bottomRight );
874 updateImageLimits( topLeft );
875 updateImageLimits( topRight );
876}
877
878
879void DXF_IMPORT_PLUGIN::addMTextChunk( const std::string& text )
880{
881 // If the text string is greater than 250 characters, the string is divided into 250-character
882 // chunks, which appear in one or more group 3 codes. If group 3 codes are used, the last group
883 // is a group 1 and has fewer than 250 characters
884
885 m_mtextContent.append( text );
886}
887
888
889void DXF_IMPORT_PLUGIN::addMText( const DL_MTextData& aData )
890{
891 m_mtextContent.append( aData.text );
892
893 // TODO: determine control codes applied to the whole text?
894 wxString text = toNativeString( wxString::FromUTF8( m_mtextContent.c_str() ) );
895
896 DXF_IMPORT_STYLE* style = getImportStyle( aData.style.c_str() );
897 double textHeight = mapDim( aData.height );
898
899 // The 0.9 factor gives a better height/width base ratio with our font
900 double charWidth = textHeight * 0.9;
901
902 if( style != nullptr )
903 charWidth *= style->m_widthFactor;
904
905 double textWidth = charWidth * text.length(); // Rough approximation
906 double textThickness = textHeight/8.0; // Use a reasonable line thickness for this text
907
908 VECTOR2D bottomLeft(0.0, 0.0);
909 VECTOR2D bottomRight(0.0, 0.0);
910 VECTOR2D topLeft(0.0, 0.0);
911 VECTOR2D topRight(0.0, 0.0);
912
913 MATRIX3x3D arbAxis = getArbitraryAxis( getExtrusion() );
914 VECTOR3D textposCoords = ocsToWcs( arbAxis, VECTOR3D( aData.ipx, aData.ipy, aData.ipz ) );
915 VECTOR2D textpos( mapX( textposCoords.x ), mapY( textposCoords.y ) );
916
917 // Initialize text justifications:
920
921 if( aData.attachmentPoint <= 3 )
922 {
923 vJustify = GR_TEXT_V_ALIGN_TOP;
924
925 bottomLeft.y = -textHeight;
926 bottomRight.y = -textHeight;
927 }
928 else if( aData.attachmentPoint <= 6 )
929 {
930 vJustify = GR_TEXT_V_ALIGN_CENTER;
931
932 bottomRight.y = -textHeight / 2.0;
933 bottomLeft.y = -textHeight / 2.0;
934 topLeft.y = textHeight / 2.0;
935 topRight.y = textHeight / 2.0;
936 }
937 else
938 {
939 vJustify = GR_TEXT_V_ALIGN_BOTTOM;
940
941 topLeft.y = textHeight;
942 topRight.y = textHeight;
943 }
944
945 if( aData.attachmentPoint % 3 == 1 )
946 {
947 hJustify = GR_TEXT_H_ALIGN_LEFT;
948
949 bottomRight.x = textWidth;
950 topRight.x = textWidth;
951 }
952 else if( aData.attachmentPoint % 3 == 2 )
953 {
954 hJustify = GR_TEXT_H_ALIGN_CENTER;
955
956 bottomLeft.x = -textWidth / 2.0;
957 topLeft.x = -textWidth / 2.0;
958 bottomRight.x = textWidth / 2.0;
959 topRight.x = textWidth / 2.0;
960 }
961 else
962 {
963 hJustify = GR_TEXT_H_ALIGN_RIGHT;
964
965 bottomLeft.x = -textWidth;
966 topLeft.x = -textWidth;
967 }
968
969#if 0 // These setting have no meaning in Pcbnew
970 if( data.alignH == 1 )
971 {
972 // Text is left to right;
973 }
974 else if( data.alignH == 3 )
975 {
976 // Text is top to bottom;
977 }
978 else
979 {
980 // use ByStyle;
981 }
982
983 if( aData.alignV == 1 )
984 {
985 // use AtLeast;
986 }
987 else
988 {
989 // useExact;
990 }
991#endif
992
993 // dxf_lib imports text angle in radians (although there are no comment about that.
994 // So, for the moment, convert this angle to degrees
995 double angle_degree = aData.angle * 180/M_PI;
996
997 // We also need the angle in radians. so convert angle_degree to radians
998 // regardless the aData.angle unit
999 double angleInRads = angle_degree * M_PI / 180.0;
1000 double cosine = cos(angleInRads);
1001 double sine = sin(angleInRads);
1002
1003
1004 GRAPHICS_IMPORTER_BUFFER* bufferToUse = m_currentBlock ? &m_currentBlock->m_buffer
1006 bufferToUse->SetCurrentSourceLayer( getDxfLayerName( attributes.getLayer() ) );
1007 bufferToUse->AddText( textpos, text, textHeight, charWidth, textThickness, angle_degree,
1008 hJustify, vJustify );
1009
1010 bottomLeft.x = bottomLeft.x * cosine - bottomLeft.y * sine;
1011 bottomLeft.y = bottomLeft.x * sine + bottomLeft.y * cosine;
1012
1013 bottomRight.x = bottomRight.x * cosine - bottomRight.y * sine;
1014 bottomRight.y = bottomRight.x * sine + bottomRight.y * cosine;
1015
1016 topLeft.x = topLeft.x * cosine - topLeft.y * sine;
1017 topLeft.y = topLeft.x * sine + topLeft.y * cosine;
1018
1019 topRight.x = topRight.x * cosine - topRight.y * sine;
1020 topRight.y = topRight.x * sine + topRight.y * cosine;
1021
1022 bottomLeft += textpos;
1023 bottomRight += textpos;
1024 topLeft += textpos;
1025 topRight += textpos;
1026
1027 updateImageLimits( bottomLeft );
1028 updateImageLimits( bottomRight );
1029 updateImageLimits( topLeft );
1030 updateImageLimits( topRight );
1031
1032 m_mtextContent.clear();
1033}
1034
1035
1037{
1038 double scale = 1.0;
1039
1040 switch( m_currentUnit )
1041 {
1042 case DXF_IMPORT_UNITS::INCH: scale = 25.4; break;
1043 case DXF_IMPORT_UNITS::FEET: scale = 304.8; break;
1044 case DXF_IMPORT_UNITS::MM: scale = 1.0; break;
1045 case DXF_IMPORT_UNITS::CM: scale = 10.0; break;
1046 case DXF_IMPORT_UNITS::METERS: scale = 1000.0; break;
1047 case DXF_IMPORT_UNITS::MICROINCHES: scale = 2.54e-5; break;
1048 case DXF_IMPORT_UNITS::MILS: scale = 0.0254; break;
1049 case DXF_IMPORT_UNITS::YARDS: scale = 914.4; break;
1050 case DXF_IMPORT_UNITS::ANGSTROMS: scale = 1.0e-7; break;
1051 case DXF_IMPORT_UNITS::NANOMETERS: scale = 1.0e-6; break;
1052 case DXF_IMPORT_UNITS::MICRONS: scale = 1.0e-3; break;
1053 case DXF_IMPORT_UNITS::DECIMETERS: scale = 100.0; break;
1054
1055 default:
1056 // use the default of 1.0 for:
1057 // 0: Unspecified Units
1058 // 3: miles
1059 // 7: kilometers
1060 // 15: decameters
1061 // 16: hectometers
1062 // 17: gigameters
1063 // 18: AU
1064 // 19: lightyears
1065 // 20: parsecs
1066 break;
1067 }
1068
1069 return scale;
1070}
1071
1072
1073void DXF_IMPORT_PLUGIN::setVariableInt( const std::string& key, int value, int code )
1074{
1075 // Called for every int variable in the DXF file (e.g. "$INSUNITS").
1076
1077 if( key == "$DWGCODEPAGE" )
1078 {
1079 m_codePage = value;
1080 return;
1081 }
1082
1083 if( key == "$AUPREC" )
1084 {
1085 m_importAnglePrecision = value;
1086 return;
1087 }
1088
1089 if( key == "$LUPREC" )
1090 {
1092 return;
1093 }
1094
1095 if( key == "$INSUNITS" ) // Drawing units
1096 {
1098
1099 switch( value )
1100 {
1101 case 1: m_currentUnit = DXF_IMPORT_UNITS::INCH; break;
1102 case 2: m_currentUnit = DXF_IMPORT_UNITS::FEET; break;
1103 case 4: m_currentUnit = DXF_IMPORT_UNITS::MM; break;
1104 case 5: m_currentUnit = DXF_IMPORT_UNITS::CM; break;
1105 case 6: m_currentUnit = DXF_IMPORT_UNITS::METERS; break;
1107 case 9: m_currentUnit = DXF_IMPORT_UNITS::MILS; break;
1108 case 10: m_currentUnit = DXF_IMPORT_UNITS::YARDS; break;
1111 case 13: m_currentUnit = DXF_IMPORT_UNITS::MICRONS; break;
1113
1114 default:
1115 // use the default for:
1116 // 0: Unspecified Units
1117 // 3: miles
1118 // 7: kilometers
1119 // 15: decameters
1120 // 16: hectometers
1121 // 17: gigameters
1122 // 18: AU
1123 // 19: lightyears
1124 // 20: parsecs
1125 break;
1126 }
1127
1128 return;
1129 }
1130}
1131
1132
1133void DXF_IMPORT_PLUGIN::setVariableString( const std::string& key, const std::string& value,
1134 int code )
1135{
1136 // Called for every string variable in the DXF file (e.g. "$ACADVER").
1137}
1138
1139
1140wxString DXF_IMPORT_PLUGIN::toDxfString( const wxString& aStr )
1141{
1142 wxString res;
1143 int j = 0;
1144
1145 for( unsigned i = 0; i<aStr.length(); ++i )
1146 {
1147 int c = aStr[i];
1148
1149 if( c > 175 || c < 11 )
1150 {
1151 res.append( aStr.Mid( j, i - j ) );
1152 j = i;
1153
1154 switch( c )
1155 {
1156 case 0x0A:
1157 res += wxT( "\\P" );
1158 break;
1159
1160 // diameter:
1161#ifdef _WIN32
1162 // windows, as always, is special.
1163 case 0x00D8:
1164#else
1165 case 0x2205:
1166#endif
1167 res += wxT( "%%C" );
1168 break;
1169
1170 // degree:
1171 case 0x00B0:
1172 res += wxT( "%%D" );
1173 break;
1174
1175 // plus/minus
1176 case 0x00B1:
1177 res += wxT( "%%P" );
1178 break;
1179
1180 default:
1181 j--;
1182 break;
1183 }
1184
1185 j++;
1186 }
1187 }
1188
1189 res.append( aStr.Mid( j ) );
1190 return res;
1191}
1192
1193
1194wxString DXF_IMPORT_PLUGIN::toNativeString( const wxString& aData )
1195{
1196 wxString res;
1197 size_t i = 0;
1198 int braces = 0;
1199 int overbarLevel = -1;
1200
1201 // For description, see:
1202 // https://ezdxf.readthedocs.io/en/stable/dxfinternals/entities/mtext.html
1203 // https://www.cadforum.cz/en/text-formatting-codes-in-mtext-objects-tip8640
1204
1205 for( i = 0; i < aData.length(); i++ )
1206 {
1207 switch( (wchar_t) aData[i] )
1208 {
1209 case '{': // Text area influenced by the code
1210 braces++;
1211 break;
1212
1213 case '}':
1214 if( overbarLevel == braces )
1215 {
1216 res << '}';
1217 overbarLevel = -1;
1218 }
1219 braces--;
1220 break;
1221
1222 case '^': // C0 control code
1223 if( ++i >= aData.length() )
1224 break;
1225
1226 switch( (wchar_t) aData[i] )
1227 {
1228 case 'I': res << '\t'; break;
1229 case 'J': res << '\b'; break;
1230 case ' ': res << '^'; break;
1231 default: break;
1232 }
1233 break;
1234
1235 case '\\':
1236 {
1237 if( ++i >= aData.length() )
1238 break;
1239
1240 switch( (wchar_t) aData[i] )
1241 {
1242 case 'P': // New paragraph (new line)
1243 case 'X': // Paragraph wrap on the dimension line (only in dimensions)
1244 res << '\n';
1245 break;
1246
1247 case '~': // Non-wrapping space, hard space
1248 res << L'\u00A0';
1249 break;
1250
1251 case 'U': // Unicode character, e.g. \U+ff08
1252 {
1253 i += 2;
1254 wxString codeHex;
1255
1256 for( ; codeHex.length() < 4 && i < aData.length(); i++ )
1257 codeHex << aData[i];
1258
1259 unsigned long codeVal = 0;
1260
1261 if( codeHex.ToCULong( &codeVal, 16 ) && codeVal != 0 )
1262 res << wxUniChar( codeVal );
1263
1264 i--;
1265 }
1266 break;
1267
1268 case 'S': // Stacking
1269 {
1270 i++;
1271 wxString stacked;
1272
1273 for( ; i < aData.length(); i++ )
1274 {
1275 if( aData[i] == ';' )
1276 break;
1277 else
1278 stacked << aData[i];
1279 }
1280
1281 if( stacked.Contains( wxS( "#" ) ) )
1282 {
1283 res << '^' << '{';
1284 res << stacked.BeforeFirst( '#' );
1285 res << '}' << '/' << '_' << '{';
1286 res << stacked.AfterFirst( '#' );
1287 res << '}';
1288 }
1289 else
1290 {
1291 stacked.Replace( wxS( "^ " ), wxS( "/" ) );
1292 res << stacked;
1293 }
1294 }
1295 break;
1296
1297 case 'O': // Start overstrike
1298 if( overbarLevel == -1 )
1299 {
1300 res << '~' << '{';
1301 overbarLevel = braces;
1302 }
1303 break;
1304 case 'o': // Stop overstrike
1305 if( overbarLevel == braces )
1306 {
1307 res << '}';
1308 overbarLevel = -1;
1309 }
1310 break;
1311
1312 case 'L': // Start underline
1313 case 'l': // Stop underline
1314 case 'K': // Start strike-through
1315 case 'k': // Stop strike-through
1316 case 'N': // New column
1317 // Ignore
1318 break;
1319
1320 case 'p': // Control codes for bullets, numbered paragraphs, tab stops and columns
1321 case 'Q': // Slanting (obliquing) text by angle
1322 case 'H': // Text height
1323 case 'W': // Text width
1324 case 'F': // Font selection
1325 case 'f': // Font selection (alternative)
1326 case 'A': // Alignment
1327 case 'C': // Color change (ACI colors)
1328 case 'c': // Color change (truecolor)
1329 case 'T': // Tracking, char.spacing
1330 // Skip to ;
1331 for( ; i < aData.length(); i++ )
1332 {
1333 if( aData[i] == ';' )
1334 break;
1335 }
1336 break;
1337
1338 default: // Escaped character
1339 if( ++i >= aData.length() )
1340 break;
1341
1342 res << aData[i];
1343 break;
1344 }
1345 }
1346 break;
1347
1348 default: res << aData[i];
1349 }
1350 }
1351
1352 if( overbarLevel != -1 )
1353 {
1354 res << '}';
1355 overbarLevel = -1;
1356 }
1357
1358#if 1
1359 wxRegEx regexp;
1360
1361 // diameter:
1362 regexp.Compile( wxT( "%%[cC]" ) );
1363#ifdef __WINDOWS__
1364 // windows, as always, is special.
1365 regexp.Replace( &res, wxChar( 0xD8 ) );
1366#else
1367 // Empty_set, diameter is 0x2300
1368 regexp.Replace( &res, wxChar( 0x2205 ) );
1369#endif
1370
1371 // degree:
1372 regexp.Compile( wxT( "%%[dD]" ) );
1373 regexp.Replace( &res, wxChar( 0x00B0 ) );
1374
1375 // plus/minus
1376 regexp.Compile( wxT( "%%[pP]" ) );
1377 regexp.Replace( &res, wxChar( 0x00B1 ) );
1378#endif
1379
1380 return res;
1381}
1382
1383
1384void DXF_IMPORT_PLUGIN::addTextStyle( const DL_StyleData& aData )
1385{
1386 wxString name = wxString::FromUTF8( aData.name.c_str() );
1387
1388 auto style = std::make_unique<DXF_IMPORT_STYLE>( name, aData.fixedTextHeight, aData.widthFactor,
1389 aData.bold, aData.italic );
1390
1391 m_styles.push_back( std::move( style ) );
1392}
1393
1394
1395void DXF_IMPORT_PLUGIN::addPoint( const DL_PointData& aData )
1396{
1397 MATRIX3x3D arbAxis = getArbitraryAxis( getExtrusion() );
1398 VECTOR3D centerCoords = ocsToWcs( arbAxis, VECTOR3D( aData.x, aData.y, aData.z ) );
1399 VECTOR2D center( mapX( centerCoords.x ), mapY( centerCoords.y ) );
1400
1401 // we emulate points with filled circles
1402 // set the linewidth to something that even small circles look good with
1403 // thickness is optional for dxf points
1404 // note: we had to modify the dxf library to grab the attribute for thickness
1405 double lineWidth = 0.0001;
1406 double thickness = mapDim( std::max( aData.thickness, 0.01 ) );
1407
1408 GRAPHICS_IMPORTER_BUFFER* bufferToUse = m_currentBlock ? &m_currentBlock->m_buffer
1410 bufferToUse->SetCurrentSourceLayer( getDxfLayerName( attributes.getLayer() ) );
1411 bufferToUse->AddCircle( center, thickness, lineWidth, true );
1412
1413 VECTOR2D radiusDelta( SCALE_FACTOR( thickness ), SCALE_FACTOR( thickness ) );
1414
1415 updateImageLimits( center + radiusDelta );
1416 updateImageLimits( center - radiusDelta );
1417}
1418
1419
1421 const VECTOR2D& aSegEnd, double aWidth )
1422{
1423 VECTOR2D origin( SCALE_FACTOR( aSegStart.x ), SCALE_FACTOR( aSegStart.y ) );
1424 VECTOR2D end( SCALE_FACTOR( aSegEnd.x ), SCALE_FACTOR( aSegEnd.y ) );
1425
1426 GRAPHICS_IMPORTER_BUFFER* bufferToUse = m_currentBlock ? &m_currentBlock->m_buffer
1428 bufferToUse->SetCurrentSourceLayer( m_curr_entity.m_LayerName );
1429 bufferToUse->AddLine( origin, end, aWidth );
1430
1431 updateImageLimits( origin );
1433}
1434
1435
1436void DXF_IMPORT_PLUGIN::insertArc( const VECTOR2D& aSegStart, const VECTOR2D& aSegEnd,
1437 double aBulge, double aWidth )
1438{
1439 VECTOR2D segment_startpoint( SCALE_FACTOR( aSegStart.x ), SCALE_FACTOR( aSegStart.y ) );
1440 VECTOR2D segment_endpoint( SCALE_FACTOR( aSegEnd.x ), SCALE_FACTOR( aSegEnd.y ) );
1441
1442 // ensure aBulge represents an angle from +/- ( 0 .. approx 359.8 deg )
1443 if( aBulge < -2000.0 )
1444 aBulge = -2000.0;
1445 else if( aBulge > 2000.0 )
1446 aBulge = 2000.0;
1447
1448 double ang = 4.0 * atan( aBulge );
1449
1450 // reflect the Y values to put everything in a RHCS
1451 VECTOR2D sp( aSegStart.x, -aSegStart.y );
1452 VECTOR2D ep( aSegEnd.x, -aSegEnd.y );
1453
1454 // angle from end->start
1455 double offAng = atan2( ep.y - sp.y, ep.x - sp.x );
1456
1457 // length of subtended segment = 1/2 distance between the 2 points
1458 double d = 0.5 * sqrt( ( sp.x - ep.x ) * ( sp.x - ep.x ) + ( sp.y - ep.y ) * ( sp.y - ep.y ) );
1459
1460 // midpoint of the subtended segment
1461 double xm = ( sp.x + ep.x ) * 0.5;
1462 double ym = ( sp.y + ep.y ) * 0.5;
1463 double radius = d / sin( ang * 0.5 );
1464
1465 if( radius < 0.0 )
1466 radius = -radius;
1467
1468 // calculate the height of the triangle with base d and hypotenuse r
1469 double dh2 = radius * radius - d * d;
1470
1471 // this should only ever happen due to rounding errors when r == d
1472 if( dh2 < 0.0 )
1473 dh2 = 0.0;
1474
1475 double h = sqrt( dh2 );
1476
1477 if( ang < 0.0 )
1478 offAng -= M_PI_2;
1479 else
1480 offAng += M_PI_2;
1481
1482 // for angles greater than 180 deg we need to flip the
1483 // direction in which the arc center is found relative
1484 // to the midpoint of the subtended segment.
1485 if( ang < -M_PI )
1486 offAng += M_PI;
1487 else if( ang > M_PI )
1488 offAng -= M_PI;
1489
1490 // center point
1491 double cx = h * cos( offAng ) + xm;
1492 double cy = h * sin( offAng ) + ym;
1493 VECTOR2D center( SCALE_FACTOR( cx ), SCALE_FACTOR( -cy ) );
1494 VECTOR2D arc_start;
1495 EDA_ANGLE angle( ang, RADIANS_T );
1496
1497 if( ang < 0.0 )
1498 {
1499 arc_start = VECTOR2D( SCALE_FACTOR( ep.x ), SCALE_FACTOR( -ep.y ) );
1500 }
1501 else
1502 {
1503 arc_start = VECTOR2D( SCALE_FACTOR( sp.x ), SCALE_FACTOR( -sp.y ) );
1504 angle = -angle;
1505 }
1506
1507 GRAPHICS_IMPORTER_BUFFER* bufferToUse = m_currentBlock ? &m_currentBlock->m_buffer
1509 bufferToUse->SetCurrentSourceLayer( m_curr_entity.m_LayerName );
1510 bufferToUse->AddArc( center, arc_start, angle, aWidth );
1511
1512 VECTOR2D radiusDelta( SCALE_FACTOR( radius ), SCALE_FACTOR( radius ) );
1513
1514 updateImageLimits( center + radiusDelta );
1515 updateImageLimits( center - radiusDelta );
1516}
1517
1518
1519#include "tinysplinecxx.h"
1520
1522{
1523#if 0 // Debug only
1524 wxLogMessage( "spl deg %d kn %d ctr %d fit %d",
1525 m_curr_entity.m_SplineDegree,
1526 m_curr_entity.m_SplineKnotsList.size(),
1527 m_curr_entity.m_SplineControlPointList.size(),
1528 m_curr_entity.m_SplineFitPointList.size() );
1529#endif
1530
1531 unsigned imax = m_curr_entity.m_SplineControlPointList.size();
1532
1533 if( imax < 2 ) // malformed spline
1534 return;
1535
1536#if 0 // set to 1 to approximate the spline by segments between 2 control points
1537 VECTOR2D startpoint( mapX( m_curr_entity.m_SplineControlPointList[0].m_x ),
1538 mapY( m_curr_entity.m_SplineControlPointList[0].m_y ) );
1539
1540 for( unsigned int ii = 1; ii < imax; ++ii )
1541 {
1542 VECTOR2D endpoint( mapX( m_curr_entity.m_SplineControlPointList[ii].m_x ),
1543 mapY( m_curr_entity.m_SplineControlPointList[ii].m_y ) );
1544
1545 if( startpoint != endpoint )
1546 {
1547 m_internalImporter.AddLine( startpoint, endpoint, aWidth );
1548
1549 updateImageLimits( startpoint );
1550 updateImageLimits( endpoint );
1551
1552 startpoint = endpoint;
1553 }
1554 }
1555#else // Use bezier curves, supported by pcbnew, to approximate the spline
1556 std::vector<double> ctrlp;
1557
1558 for( unsigned ii = 0; ii < imax; ++ii )
1559 {
1560 ctrlp.push_back( m_curr_entity.m_SplineControlPointList[ii].m_x );
1561 ctrlp.push_back( m_curr_entity.m_SplineControlPointList[ii].m_y );
1562 }
1563
1564 tinyspline::BSpline beziers;
1565 std::vector<double> coords;
1566
1567 try
1568 {
1569 tinyspline::BSpline dxfspline( m_curr_entity.m_SplineControlPointList.size(),
1570 /* coord dim */ 2, m_curr_entity.m_SplineDegree );
1571
1572 dxfspline.setControlPoints( ctrlp );
1573 dxfspline.setKnots( m_curr_entity.m_SplineKnotsList );
1574
1575 if( dxfspline.degree() < 3 )
1576 dxfspline = dxfspline.elevateDegree( 3 - dxfspline.degree() );
1577
1578 beziers = dxfspline.toBeziers();
1579 coords = beziers.controlPoints();
1580 }
1581 catch( const std::runtime_error& ) // tinyspline throws everything including data validation
1582 // as runtime errors
1583 {
1584 // invalid spline definition, drop this block
1585 ReportMsg( _( "Invalid spline definition encountered" ) );
1586 return;
1587 }
1588
1589 size_t order = beziers.order();
1590 size_t dim = beziers.dimension();
1591 size_t numBeziers = ( coords.size() / dim ) / order;
1592
1593 for( size_t i = 0; i < numBeziers; i++ )
1594 {
1595 size_t ii = i * dim * order;
1596 VECTOR2D start( mapX( coords[ ii ] ), mapY( coords[ ii + 1 ] ) );
1597 VECTOR2D bezierControl1( mapX( coords[ii + 2] ), mapY( coords[ii + 3] ) );
1598
1599 // not sure why this happens, but it seems to sometimes slip degree on the final bezier
1600 VECTOR2D bezierControl2;
1601
1602 if( ii + 5 >= coords.size() )
1603 bezierControl2 = bezierControl1;
1604 else
1605 bezierControl2 = VECTOR2D( mapX( coords[ii + 4] ), mapY( coords[ii + 5] ) );
1606
1607 VECTOR2D end;
1608
1609 if( ii + 7 >= coords.size() )
1610 end = bezierControl2;
1611 else
1612 end = VECTOR2D( mapX( coords[ii + 6] ), mapY( coords[ii + 7] ) );
1613
1614 GRAPHICS_IMPORTER_BUFFER* bufferToUse = m_currentBlock ? &m_currentBlock->m_buffer
1616 bufferToUse->SetCurrentSourceLayer( m_curr_entity.m_LayerName );
1617 bufferToUse->AddSpline( start, bezierControl1, bezierControl2, end, aWidth );
1618 }
1619#endif
1620}
1621
1622
1624{
1625 m_minX = std::min( aPoint.x, m_minX );
1626 m_maxX = std::max( aPoint.x, m_maxX );
1627
1628 m_minY = std::min( aPoint.y, m_minY );
1629 m_maxY = std::max( aPoint.y, m_maxY );
1630}
1631
1632
1634{
1635 VECTOR3D arbZ, arbX, arbY;
1636
1637 double direction[3];
1638 aData->getDirection( direction );
1639
1640 arbZ = VECTOR3D( direction[0], direction[1], direction[2] ).Normalize();
1641
1642 if( ( abs( arbZ.x ) < ( 1.0 / 64.0 ) ) && ( abs( arbZ.y ) < ( 1.0 / 64.0 ) ) )
1643 arbX = VECTOR3D( 0, 1, 0 ).Cross( arbZ ).Normalize();
1644 else
1645 arbX = VECTOR3D( 0, 0, 1 ).Cross( arbZ ).Normalize();
1646
1647 arbY = arbZ.Cross( arbX ).Normalize();
1648
1649 return MATRIX3x3D{ arbX, arbY, arbZ };
1650}
1651
1652
1654{
1655 return arbitraryAxis * point;
1656}
1657
1658
1660{
1661 VECTOR3D worldX = wcsToOcs( arbitraryAxis, VECTOR3D( 1, 0, 0 ) );
1662 VECTOR3D worldY = wcsToOcs( arbitraryAxis, VECTOR3D( 0, 1, 0 ) );
1663 VECTOR3D worldZ = wcsToOcs( arbitraryAxis, VECTOR3D( 0, 0, 1 ) );
1664
1665 MATRIX3x3 world( worldX, worldY, worldZ );
1666
1667 return world * point;
1668}
const char * name
BOX2< VECTOR2D > BOX2D
Definition box2.h:923
constexpr void SetOrigin(const Vec &pos)
Definition box2.h:237
constexpr void SetEnd(coord_type x, coord_type y)
Definition box2.h:297
A helper class to hold layer settings temporarily during import.
GRAPHICS_IMPORTER_BUFFER m_buffer
A helper class to hold layer settings temporarily during import.
DXF_IMPORT_UNITS m_currentUnit
virtual void addPolyline(const DL_PolylineData &aData) override
double mapY(double aDxfCoordY)
std::vector< std::unique_ptr< DXF_IMPORT_LAYER > > m_layers
void addEllipse(const DL_EllipseData &aData) override
static wxString toNativeString(const wxString &aData)
Convert a DXF encoded string into a native Unicode string.
virtual void setVariableInt(const std::string &key, int value, int code) override
Called for every int variable in the DXF file (e.g.
bool LoadFromMemory(const wxMemoryBuffer &aMemBuffer) override
Set memory buffer with content for import.
void insertSpline(double aWidth)
virtual void addLine(const DL_LineData &aData) override
VECTOR3D ocsToWcs(const MATRIX3x3D &arbitraryAxis, VECTOR3D point)
Convert a given object coordinate point to world coordinate using the given arbitrary axis vectors.
void insertLine(const VECTOR2D &aSegStart, const VECTOR2D &aSegEnd, double aWidth)
DXF_IMPORT_BLOCK * getImportBlock(const std::string &aBlockName)
Return the import layer block.
virtual void addBlock(const DL_BlockData &) override
Called for each BLOCK in the DXF file.
virtual void addLayer(const DL_LayerData &aData) override
virtual void addCircle(const DL_CircleData &aData) override
virtual void addMText(const DL_MTextData &aData) override
DXF_IMPORT_LAYER * getImportLayer(const std::string &aLayerName)
Return the import layer data.
static wxString toDxfString(const wxString &aStr)
Convert a native Unicode string into a DXF encoded string.
DXF_IMPORT_STYLE * getImportStyle(const std::string &aStyleName)
Return the import style.
virtual void addInsert(const DL_InsertData &aData) override
BOX2D GetImageBBox() const override
Return image bounding box from original imported file.
MATRIX3x3D getArbitraryAxis(DL_Extrusion *aData)
double GetImageWidth() const override
Return image width from original imported file.
double GetImageHeight() const override
Return image height from original imported file.
void updateImageLimits(const VECTOR2D &aPoint)
virtual void addTextStyle(const DL_StyleData &aData) override
virtual void addVertex(const DL_VertexData &aData) override
Called for every polyline vertex.
VECTOR3D wcsToOcs(const MATRIX3x3D &arbitraryAxis, VECTOR3D point)
Convert a given world coordinate point to object coordinate using the given arbitrary axis vectors.
virtual void addSpline(const DL_SplineData &aData) override
Called for every spline.
virtual void addText(const DL_TextData &aData) override
virtual void endBlock() override
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.
virtual void addKnot(const DL_KnotData &aData) override
Called for every spline knot value.
bool ImportDxfFile(const wxString &aFile)
Implementation of the method used for communicate with this filter.
virtual void addArc(const DL_ArcData &aData) override
void SetDefaultLineWidthMM(double aWidth)
Set the default line width when importing dxf items like lines to Pcbnew.
virtual void addFitPoint(const DL_FitPointData &aData) override
Called for every spline fit point.
virtual void addPoint(const DL_PointData &aData) override
double mapDim(double aDxfValue)
void ReportMsg(const wxString &aMessage) override
double mapX(double aDxfCoordX)
double lineWeightToWidth(int lw, DXF_IMPORT_LAYER *aLayer)
void insertArc(const VECTOR2D &aSegStart, const VECTOR2D &aSegEnd, double aBulge, double aWidth)
std::vector< std::unique_ptr< DXF_IMPORT_BLOCK > > m_blocks
bool Import() override
Actually imports the file.
virtual void addMTextChunk(const std::string &text) override
virtual void endEntity() override
virtual void addControlPoint(const DL_ControlPointData &aData) override
Called for every spline control point.
GRAPHICS_IMPORTER_BUFFER m_internalImporter
std::vector< std::unique_ptr< DXF_IMPORT_STYLE > > m_styles
bool Load(const wxString &aFileName) override
Load file for import.
DXF_IMPORT_BLOCK * m_currentBlock
DXF2BRD_ENTITY_DATA m_curr_entity
wxString getDxfLayerName(const std::string &aLayerName) const
virtual void SetImporter(GRAPHICS_IMPORTER *aImporter) override
Set the receiver of the imported shapes.
virtual void addLinetype(const DL_LinetypeData &data) override
A helper class to hold style settings temporarily during import.
double AsDegrees() const
Definition eda_angle.h:116
std::list< std::unique_ptr< IMPORTED_SHAPE > > & GetShapes()
void AddCircle(const VECTOR2D &aCenter, double aRadius, const IMPORTED_STROKE &aStroke, bool aFilled, const COLOR4D &aFillColor=COLOR4D::UNSPECIFIED) override
Create an object representing a circle.
void AddSpline(const VECTOR2D &aStart, const VECTOR2D &aBezierControl1, const VECTOR2D &aBezierControl2, const VECTOR2D &aEnd, const IMPORTED_STROKE &aStroke) override
Create an object representing an arc.
void AddLine(const VECTOR2D &aStart, const VECTOR2D &aEnd, const IMPORTED_STROKE &aStroke) override
Create an object representing a line segment.
void AddEllipse(const VECTOR2D &aCenter, double aMajorRadius, double aMinorRadius, const EDA_ANGLE &aRotation, const IMPORTED_STROKE &aStroke, bool aFilled, const COLOR4D &aFillColor=COLOR4D::UNSPECIFIED) override
Create an object representing a closed ellipse.
void SetCurrentSourceLayer(const wxString &aSourceLayer) override
Set the source layer for the next buffered shape to be imported.
void AddArc(const VECTOR2D &aCenter, const VECTOR2D &aStart, const EDA_ANGLE &aAngle, const IMPORTED_STROKE &aStroke) override
Create an object representing an arc.
void AddText(const VECTOR2D &aOrigin, const wxString &aText, double aHeight, double aWidth, double aThickness, double aOrientation, GR_TEXT_H_ALIGN_T aHJustify, GR_TEXT_V_ALIGN_T aVJustify, const COLOR4D &aColor=COLOR4D::UNSPECIFIED) override
Create an object representing a text.
void AddEllipseArc(const VECTOR2D &aCenter, double aMajorRadius, double aMinorRadius, const EDA_ANGLE &aRotation, const EDA_ANGLE &aStartAngle, const EDA_ANGLE &aEndAngle, const IMPORTED_STROKE &aStroke) override
Create an object representing an elliptical arc.
Interface that creates objects representing shapes for a given data model.
GRAPHICS_IMPORTER * m_importer
Importer used to create objects representing the imported shapes.
virtual void SetImporter(GRAPHICS_IMPORTER *aImporter)
Set the receiver of the imported shapes.
MATRIX3x3 describes a general 3x3 matrix.
Definition matrix3x3.h:63
void SetRotation(T aAngle)
Set the rotation components of the matrix.
Definition matrix3x3.h:275
VECTOR2< T > GetScale() const
Get the scale components of the matrix.
Definition matrix3x3.h:295
T EuclideanNorm() const
Compute the Euclidean norm of the vector, which is defined as sqrt(x ** 2 + y ** 2).
Definition vector2d.h:283
VECTOR3< T > Normalize()
Compute the normalized vector.
Definition vector3.h:160
VECTOR3< T > Cross(const VECTOR3< T > &aVector) const
Compute cross product of self with aVector.
Definition vector3.h:134
The common library.
#define SCALE_FACTOR(x)
#define MIN_BULGE
#define DXF_IMPORT_LINEWEIGHT_BY_LW_DEFAULT
#define DXF_IMPORT_LINEWEIGHT_BY_LAYER
#define _(s)
static constexpr EDA_ANGLE ANGLE_0
Definition eda_angle.h:411
@ RADIANS_T
Definition eda_angle.h:32
@ DEGREES_T
Definition eda_angle.h:31
static constexpr EDA_ANGLE ANGLE_360
Definition eda_angle.h:417
static constexpr EDA_ANGLE ANGLE_180
Definition eda_angle.h:415
@ Dwgs_User
Definition layer_ids.h:107
This file contains miscellaneous commonly used macros and functions.
MATRIX3x3< double > MATRIX3x3D
Definition matrix3x3.h:473
EDA_ANGLE abs(const EDA_ANGLE &aAngle)
Definition eda_angle.h:400
const int scale
wxString From_UTF8(const char *cstring)
VECTOR3I res
VECTOR2I center
int radius
VECTOR2I end
SHAPE_CIRCLE circle(c.m_circle_center, c.m_circle_radius)
GR_TEXT_H_ALIGN_T
This is API surface mapped to common.types.HorizontalAlignment.
@ GR_TEXT_H_ALIGN_CENTER
@ GR_TEXT_H_ALIGN_RIGHT
@ GR_TEXT_H_ALIGN_LEFT
GR_TEXT_V_ALIGN_T
This is API surface mapped to common.types.VertialAlignment.
@ GR_TEXT_V_ALIGN_BOTTOM
@ GR_TEXT_V_ALIGN_CENTER
@ GR_TEXT_V_ALIGN_TOP
#define M_PI
void RotatePoint(int *pX, int *pY, const EDA_ANGLE &aAngle)
Calculate the new point of coord coord pX, pY, for a rotation center 0, 0.
Definition trigo.cpp:229
double DEG2RAD(double deg)
Definition trigo.h:166
VECTOR2< double > VECTOR2D
Definition vector2d.h:686
VECTOR3< double > VECTOR3D
Definition vector3.h:230