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