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