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