From 72d15972010617a5c0b7d44af31685594a2d7c58 Mon Sep 17 00:00:00 2001 From: jean-pierre charras Date: Sat, 7 Jul 2018 13:04:01 +0200 Subject: [PATCH] DXF import: add import of DXF splines that are converted to Bezier curves. Fix also a lot of bugs related to Bezier curves (S_CURVE shape in DRAW_SEGMENT class) in Pcbnew code. Add missing code to handle these Bezier curves --- CMakeLists.txt | 2 +- common/bezier_curves.cpp | 3 +- include/bezier_curves.h | 19 +- pcbnew/CMakeLists.txt | 2 +- pcbnew/block_footprint_editor.cpp | 5 +- ...board_items_to_polygon_shape_transform.cpp | 15 +- pcbnew/class_drawsegment.cpp | 119 +++- pcbnew/class_drawsegment.h | 10 + pcbnew/class_edge_mod.cpp | 62 +- pcbnew/class_edge_mod.h | 12 +- pcbnew/edgemod.cpp | 6 +- pcbnew/editedge.cpp | 3 +- pcbnew/import_dxf/dialog_dxf_import.cpp | 10 +- pcbnew/import_dxf/dxf2brd_items.cpp | 535 +++++++++++------- pcbnew/import_dxf/dxf2brd_items.h | 274 ++++++--- pcbnew/kicad_plugin.cpp | 4 +- pcbnew/pcb_painter.cpp | 5 +- pcbnew/pcb_parser.cpp | 4 +- pcbnew/tools/drawing_tool.cpp | 11 +- utils/idftools/CMakeLists.txt | 7 +- 20 files changed, 760 insertions(+), 348 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index f2ea761f95..fbc438c78c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -941,7 +941,7 @@ add_subdirectory( 3d-viewer ) add_subdirectory( cvpcb ) add_subdirectory( eeschema ) add_subdirectory( gerbview ) -add_subdirectory( lib_dxf ) +add_subdirectory( dxflib_qcad ) add_subdirectory( pcbnew ) add_subdirectory( polygon ) add_subdirectory( pagelayout_editor ) diff --git a/common/bezier_curves.cpp b/common/bezier_curves.cpp index 8b66185b7f..8d7f588478 100644 --- a/common/bezier_curves.cpp +++ b/common/bezier_curves.cpp @@ -45,9 +45,10 @@ static inline double sqrt_len( int dx, int dy ) } -void BEZIER_POLY::GetPoly( std::vector& aOutput ) +void BEZIER_POLY::GetPoly( std::vector& aOutput, int aMinSegLen ) { wxCHECK( !m_ctrlPts.empty(), /* void */ ); + m_minSegLen = std::max( 1, aMinSegLen ); m_output = &aOutput; m_output->clear(); m_output->push_back( wxPoint( m_ctrlPts.front() ) ); diff --git a/include/bezier_curves.h b/include/bezier_curves.h index c4dabb3fb7..7fb63a139d 100644 --- a/include/bezier_curves.h +++ b/include/bezier_curves.h @@ -30,18 +30,22 @@ /** * Bezier curves to polygon converter. + * Only quadratic and cubic Bezier curves are handled */ class BEZIER_POLY { public: + /** cubic Bezier curve */ BEZIER_POLY( int x1, int y1, int x2, int y2, int x3, int y3 ) { m_ctrlPts.emplace_back( x1, y1 ); m_ctrlPts.emplace_back( x2, y2 ); m_ctrlPts.emplace_back( x3, y3 ); m_output = nullptr; + m_minSegLen = 0; } + /** Quadratic and cubic Bezier curve */ BEZIER_POLY( int x1, int y1, int x2, int y2, int x3, int y3, int x4, int y4 ) { m_ctrlPts.emplace_back( x1, y1 ); @@ -49,21 +53,28 @@ public: m_ctrlPts.emplace_back( x3, y3 ); m_ctrlPts.emplace_back( x4, y4 ); m_output = nullptr; + m_minSegLen = 0; } BEZIER_POLY( const std::vector& aControlPoints ) : m_ctrlPts( aControlPoints ) { m_output = nullptr; + m_minSegLen = 0; } /** * Converts Bezier curve to a polygon. * @param aOutput will be used as an output vector storing polygon points. + * @param aMinSegLen is the min dist between 2 successve points. + * It can be used to reduce the number of points. + * (the last point is always generated) */ - void GetPoly( std::vector& aOutput ); + void GetPoly( std::vector& aOutput, int aMinSegLen = 0 ); private: + int m_minSegLen; + ///> Control points std::vector m_ctrlPts; @@ -72,7 +83,11 @@ private: void addSegment( const wxPoint& aSegment ) { - if( m_output->back() != aSegment ) + int seglen = std::abs( m_output->back().x - aSegment.x ) + + std::abs( m_output->back().y - aSegment.y ); + + // m_minSegLen is always > 0, so never store a 0 len segment + if( seglen >= m_minSegLen ) m_output->push_back( aSegment ); } diff --git a/pcbnew/CMakeLists.txt b/pcbnew/CMakeLists.txt index 0c7e1e7f63..f890e6179e 100644 --- a/pcbnew/CMakeLists.txt +++ b/pcbnew/CMakeLists.txt @@ -42,7 +42,7 @@ include_directories( ../polygon ../common/dialogs ./exporters - ../lib_dxf + ../dxflib_qcad ./import_dxf ../utils/idftools ${GLM_INCLUDE_DIR} diff --git a/pcbnew/block_footprint_editor.cpp b/pcbnew/block_footprint_editor.cpp index add517487f..11ac957c1c 100644 --- a/pcbnew/block_footprint_editor.cpp +++ b/pcbnew/block_footprint_editor.cpp @@ -533,10 +533,11 @@ void MoveMarkedItems( MODULE* module, wxPoint offset ) case PCB_MODULE_EDGE_T: { EDGE_MODULE* em = (EDGE_MODULE*) item; - em->SetStart( em->GetStart() + offset ); - em->SetEnd( em->GetEnd() + offset ); + em->Move( offset ); em->SetStart0( em->GetStart0() + offset ); em->SetEnd0( em->GetEnd0() + offset ); + em->SetBezier0_C1( em->GetBezier0_C1() + offset ); + em->SetBezier0_C2( em->GetBezier0_C2() + offset ); } break; diff --git a/pcbnew/board_items_to_polygon_shape_transform.cpp b/pcbnew/board_items_to_polygon_shape_transform.cpp index 9b94f08c4a..ffdab7d2da 100644 --- a/pcbnew/board_items_to_polygon_shape_transform.cpp +++ b/pcbnew/board_items_to_polygon_shape_transform.cpp @@ -33,6 +33,7 @@ #include #include +#include #include // for IU_PER_MM #include #include @@ -602,7 +603,19 @@ void DRAWSEGMENT::TransformShapeWithClearanceToPolygon( SHAPE_POLY_SET& aCornerB } break; - case S_CURVE: // Bezier curve (TODO: not yet in use) + case S_CURVE: // Bezier curve + { + std::vector ctrlPoints = { m_Start, m_BezierC1, m_BezierC2, m_End }; + BEZIER_POLY converter( ctrlPoints ); + std::vector< wxPoint> poly; + converter.GetPoly( poly, m_Width ); + + for( unsigned ii = 1; ii < poly.size(); ii++ ) + { + TransformRoundedEndsSegmentToPolygon( aCornerBuffer, + poly[ii-1], poly[ii], aCircleToSegmentsCount, linewidth ); + } + } break; default: diff --git a/pcbnew/class_drawsegment.cpp b/pcbnew/class_drawsegment.cpp index 3a2989dc49..75c1292a37 100644 --- a/pcbnew/class_drawsegment.cpp +++ b/pcbnew/class_drawsegment.cpp @@ -91,6 +91,18 @@ void DRAWSEGMENT::Move( const wxPoint& aMoveVector ) (*iter) += VECTOR2I( aMoveVector ); } break; + + case S_CURVE: + m_BezierC1 += aMoveVector; + m_BezierC2 += aMoveVector; + + for( unsigned int ii = 0; ii < m_BezierPoints.size(); ii++ ) + { + m_BezierPoints[ii] += aMoveVector; + } + + break; + default: break; } @@ -119,6 +131,8 @@ void DRAWSEGMENT::Rotate( const wxPoint& aRotCentre, double aAngle ) case S_CURVE: RotatePoint( &m_Start, aRotCentre, aAngle); RotatePoint( &m_End, aRotCentre, aAngle); + RotatePoint( &m_BezierC1, aRotCentre, aAngle); + RotatePoint( &m_BezierC2, aRotCentre, aAngle); for( unsigned int ii = 0; ii < m_BezierPoints.size(); ii++ ) { @@ -145,21 +159,51 @@ void DRAWSEGMENT::Flip( const wxPoint& aCentre ) case S_ARC: m_Angle = -m_Angle; break; + case S_POLYGON: for( auto iter = m_Poly.Iterate(); iter; iter++ ) { iter->y = aCentre.y - (iter->y - aCentre.y); } break; + + case S_CURVE: + { + m_BezierC1.y = aCentre.y - (m_BezierC1.y - aCentre.y); + m_BezierC2.y = aCentre.y - (m_BezierC2.y - aCentre.y); + + // Rebuild the poly points shape + std::vector ctrlPoints = { m_Start, m_BezierC1, m_BezierC2, m_End }; + BEZIER_POLY converter( ctrlPoints ); + converter.GetPoly( m_BezierPoints, m_Width ); + } + break; + default: break; } // DRAWSEGMENT items are not allowed on copper layers, so - // copper layers count is not taken in accoun in Flip transform + // copper layers count is not taken in account in Flip transform SetLayer( FlipLayer( GetLayer() ) ); } + +void DRAWSEGMENT::RebuildBezierToSegmentsPointsList( int aMinSegLen ) +{ + // Has meaning only for S_CURVE DRAW_SEGMENT shape + if( m_Shape != S_CURVE ) + { + m_BezierPoints.clear(); + return; + } + // Rebuild the m_BezierPoints vertex list that approximate the Bezier curve + std::vector ctrlPoints = { m_Start, m_BezierC1, m_BezierC2, m_End }; + BEZIER_POLY converter( ctrlPoints ); + converter.GetPoly( m_BezierPoints, aMinSegLen ); +} + + const wxPoint DRAWSEGMENT::GetCenter() const { wxPoint c; @@ -340,29 +384,24 @@ void DRAWSEGMENT::Draw( EDA_DRAW_PANEL* panel, wxDC* DC, GR_DRAWMODE draw_mode, case S_CURVE: { - std::vector ctrlPoints = { m_Start, m_BezierC1, m_BezierC2, m_End }; - BEZIER_POLY converter( ctrlPoints ); - converter.GetPoly( m_BezierPoints ); - } + RebuildBezierToSegmentsPointsList( m_Width ); - for( unsigned int i=1; i < m_BezierPoints.size(); i++ ) - { - if( filled ) + wxPoint& startp = m_BezierPoints[0]; + + for( unsigned int i = 1; i < m_BezierPoints.size(); i++ ) { - GRFillCSegm( panel->GetClipBox(), DC, - m_BezierPoints[i].x, m_BezierPoints[i].y, - m_BezierPoints[i-1].x, m_BezierPoints[i-1].y, - m_Width, color ); - } - else - { - GRCSegm( panel->GetClipBox(), DC, - m_BezierPoints[i].x, m_BezierPoints[i].y, - m_BezierPoints[i-1].x, m_BezierPoints[i-1].y, - m_Width, color ); + wxPoint& endp = m_BezierPoints[i]; + + if( filled ) + GRFilledSegment( panel->GetClipBox(), DC, + startp+aOffset, endp+aOffset, m_Width, color ); + else + GRCSegm( panel->GetClipBox(), DC, + startp+aOffset, endp+aOffset, m_Width, color ); + + startp = m_BezierPoints[i]; } } - break; case S_POLYGON: @@ -518,6 +557,15 @@ const EDA_RECT DRAWSEGMENT::GetBoundingBox() const bbox.SetEnd( p_end ); break; } + + case S_CURVE: + // Rebuild the poly points shape + ((DRAWSEGMENT*)this)->RebuildBezierToSegmentsPointsList( m_Width ); + + for( unsigned ii = 0; ii < m_BezierPoints.size(); ++ii ) + bbox.Merge( m_BezierPoints[ii] ); + break; + default: break; } @@ -578,6 +626,8 @@ bool DRAWSEGMENT::HitTest( const wxPoint& aPosition ) const break; case S_CURVE: + ((DRAWSEGMENT*)this)->RebuildBezierToSegmentsPointsList( m_Width ); + for( unsigned int i= 1; i < m_BezierPoints.size(); i++) { if( TestSegmentHit( aPosition, m_BezierPoints[i-1], m_BezierPoints[i-1], m_Width / 2 ) ) @@ -711,6 +761,35 @@ bool DRAWSEGMENT::HitTest( const EDA_RECT& aRect, bool aContained, int aAccuracy break; case S_CURVE: // not yet handled + if( aContained ) + { + return arect.Contains( bb ); + } + else + { + // Fast test: if aRect is outside the polygon bounding box, + // rectangles cannot intersect + if( !arect.Intersects( bb ) ) + return false; + + // Account for the width of the line + arect.Inflate( GetWidth() / 2 ); + unsigned count = m_BezierPoints.size(); + + for( unsigned ii = 1; ii < count; ii++ ) + { + wxPoint vertex = m_BezierPoints[ii-1]; + wxPoint vertexNext = m_BezierPoints[ii]; + + // Test if the point is within aRect + if( arect.Contains( ( wxPoint ) vertex ) ) + return true; + + // Test if this edge intersects aRect + if( arect.Intersects( vertex, vertexNext ) ) + return true; + } + } break; diff --git a/pcbnew/class_drawsegment.h b/pcbnew/class_drawsegment.h index 0b6dff4fbc..ee740aaebc 100644 --- a/pcbnew/class_drawsegment.h +++ b/pcbnew/class_drawsegment.h @@ -194,6 +194,16 @@ public: m_BezierPoints = aPoints; } + /** Rebuild the m_BezierPoints vertex list that approximate the Bezier curve + * by a list of segments + * Has meaning only for S_CURVE DRAW_SEGMENT shape + * @param aMinSegLen is the min length of segments approximating the shape. + * the last segment can be shorter + * This param avoid having too many very short segment in list. + * a good value is m_Width/2 to m_Width + */ + void RebuildBezierToSegmentsPointsList( int aMinSegLen ); + void SetPolyPoints( const std::vector& aPoints ); void Draw( EDA_DRAW_PANEL* panel, wxDC* DC, diff --git a/pcbnew/class_edge_mod.cpp b/pcbnew/class_edge_mod.cpp index feef53a7b3..d16bc64829 100644 --- a/pcbnew/class_edge_mod.cpp +++ b/pcbnew/class_edge_mod.cpp @@ -75,14 +75,19 @@ void EDGE_MODULE::SetLocalCoord() { m_Start0 = m_Start; m_End0 = m_End; + m_Bezier0_C1 = m_BezierC1; + m_Bezier0_C2 = m_BezierC2; return; } m_Start0 = m_Start - module->GetPosition(); m_End0 = m_End - module->GetPosition(); + m_Bezier0_C1 = m_BezierC1 - module->GetPosition(); + m_Bezier0_C2 = m_BezierC2 - module->GetPosition(); double angle = module->GetOrientation(); RotatePoint( &m_Start0.x, &m_Start0.y, -angle ); RotatePoint( &m_End0.x, &m_End0.y, -angle ); + RotatePoint( &m_Bezier0_C1.x, &m_Bezier0_C1.y, -angle ); } @@ -92,15 +97,23 @@ void EDGE_MODULE::SetDrawCoord() m_Start = m_Start0; m_End = m_End0; + m_BezierC1 = m_Bezier0_C1; + m_BezierC2 = m_Bezier0_C2; if( module ) { RotatePoint( &m_Start.x, &m_Start.y, module->GetOrientation() ); - RotatePoint( &m_End.x, &m_End.y, module->GetOrientation() ); + RotatePoint( &m_End.x, &m_End.y, module->GetOrientation() ); + RotatePoint( &m_BezierC1.x, &m_BezierC1.y, module->GetOrientation() ); + RotatePoint( &m_BezierC2.x, &m_BezierC2.y, module->GetOrientation() ); m_Start += module->GetPosition(); m_End += module->GetPosition(); + m_BezierC1 += module->GetPosition(); + m_BezierC2 += module->GetPosition(); } + + RebuildBezierToSegmentsPointsList( m_Width ); } @@ -225,6 +238,28 @@ void EDGE_MODULE::Draw( EDA_DRAW_PANEL* panel, wxDC* DC, GR_DRAWMODE draw_mode, } break; + case S_CURVE: + { + RebuildBezierToSegmentsPointsList( m_Width ); + + wxPoint& startp = m_BezierPoints[0]; + + for( unsigned int i = 1; i < m_BezierPoints.size(); i++ ) + { + wxPoint& endp = m_BezierPoints[i]; + + if( filled ) + GRFilledSegment( panel->GetClipBox(), DC, + startp-offset, endp-offset, m_Width, color ); + else + GRCSegm( panel->GetClipBox(), DC, + startp-offset, endp-offset, m_Width, color ); + + startp = m_BezierPoints[i]; + } + } + break; + default: break; } @@ -290,6 +325,7 @@ void EDGE_MODULE::Flip( const wxPoint& aCentre ) //Fall through default: case S_SEGMENT: + case S_CURVE: pt = GetStart(); MIRROR( pt.y, aCentre.y ); SetStart( pt ); @@ -298,8 +334,14 @@ void EDGE_MODULE::Flip( const wxPoint& aCentre ) MIRROR( pt.y, aCentre.y ); SetEnd( pt ); + MIRROR( m_BezierC1.y, aCentre.y ); + MIRROR( m_BezierC2.y, aCentre.y ); + MIRROR( m_Start0.y, 0 ); MIRROR( m_End0.y, 0 ); + MIRROR( m_Bezier0_C1.y, 0 ); + MIRROR( m_Bezier0_C2.y, 0 ); + RebuildBezierToSegmentsPointsList( m_Width ); break; case S_POLYGON: @@ -336,18 +378,32 @@ void EDGE_MODULE::Mirror( wxPoint aCentre, bool aMirrorAroundXAxis ) SetAngle( -GetAngle() ); //Fall through default: + case S_CURVE: case S_SEGMENT: if( aMirrorAroundXAxis ) { MIRROR( m_Start0.y, aCentre.y ); MIRROR( m_End0.y, aCentre.y ); + MIRROR( m_Bezier0_C1.y, aCentre.y ); + MIRROR( m_Bezier0_C2.y, aCentre.y ); } else { MIRROR( m_Start0.x, aCentre.x ); MIRROR( m_End0.x, aCentre.x ); + MIRROR( m_Bezier0_C1.x, aCentre.x ); + MIRROR( m_Bezier0_C2.x, aCentre.x ); } - break; + + for( unsigned ii = 0; ii < m_BezierPoints.size(); ii++ ) + { + if( aMirrorAroundXAxis ) + MIRROR( m_BezierPoints[ii].y, aCentre.y ); + else + MIRROR( m_BezierPoints[ii].x, aCentre.x ); + } + + break; case S_POLYGON: // polygon corners coordinates are always relative to the @@ -383,6 +439,8 @@ void EDGE_MODULE::Move( const wxPoint& aMoveVector ) // This is a footprint shape modification. m_Start0 += aMoveVector; m_End0 += aMoveVector; + m_Bezier0_C1 += aMoveVector; + m_Bezier0_C2 += aMoveVector; switch( GetShape() ) { diff --git a/pcbnew/class_edge_mod.h b/pcbnew/class_edge_mod.h index 07b766543f..7cfd0520e4 100644 --- a/pcbnew/class_edge_mod.h +++ b/pcbnew/class_edge_mod.h @@ -96,6 +96,12 @@ public: void SetEnd0( const wxPoint& aPoint ) { m_End0 = aPoint; } const wxPoint& GetEnd0() const { return m_End0; } + void SetBezier0_C1( const wxPoint& aPoint ) { m_Bezier0_C1 = aPoint; } + const wxPoint& GetBezier0_C1() const { return m_Bezier0_C1; } + + void SetBezier0_C2( const wxPoint& aPoint ) { m_Bezier0_C2 = aPoint; } + const wxPoint& GetBezier0_C2() const { return m_Bezier0_C2; } + /** * Set relative coordinates from draw coordinates. * Call in only when the geometry ov the footprint is modified @@ -134,8 +140,10 @@ public: void Show( int nestLevel, std::ostream& os ) const override { ShowDummy( os ); } #endif - wxPoint m_Start0; // Start point or center, relative to module origin, orient 0. - wxPoint m_End0; // End point, relative to module origin, orient 0. + wxPoint m_Start0; ///< Start point or center, relative to module origin, orient 0. + wxPoint m_End0; ///< End point, relative to module origin, orient 0. + wxPoint m_Bezier0_C1; ///< Bezier Control Point 1, relative to module origin, orient 0. + wxPoint m_Bezier0_C2; ///< Bezier Control Point 2, relative to module origin, orient 0. }; #endif // CLASS_EDGE_MOD_H_ diff --git a/pcbnew/edgemod.cpp b/pcbnew/edgemod.cpp index 5efc65d7f9..25d22e6455 100644 --- a/pcbnew/edgemod.cpp +++ b/pcbnew/edgemod.cpp @@ -79,11 +79,7 @@ void FOOTPRINT_EDIT_FRAME::Place_EdgeMod( EDGE_MODULE* aEdge ) if( aEdge == NULL ) return; - aEdge->SetStart( aEdge->GetStart() - MoveVector ); - aEdge->SetEnd( aEdge->GetEnd() - MoveVector ); - - aEdge->SetStart0( aEdge->GetStart0() - MoveVector ); - aEdge->SetEnd0( aEdge->GetEnd0() - MoveVector ); + aEdge->Move( -MoveVector ); aEdge->ClearFlags(); m_canvas->SetMouseCapture( NULL, NULL ); diff --git a/pcbnew/editedge.cpp b/pcbnew/editedge.cpp index 763513788f..fa5454b96e 100644 --- a/pcbnew/editedge.cpp +++ b/pcbnew/editedge.cpp @@ -101,8 +101,7 @@ static void Move_Segment( EDA_DRAW_PANEL* aPanel, wxDC* aDC, const wxPoint& aPos wxPoint delta; delta = aPanel->GetParent()->GetCrossHairPosition() - s_LastPosition; - segment->SetStart( segment->GetStart() + delta ); - segment->SetEnd( segment->GetEnd() + delta ); + segment->Move( delta ); s_LastPosition = aPanel->GetParent()->GetCrossHairPosition(); diff --git a/pcbnew/import_dxf/dialog_dxf_import.cpp b/pcbnew/import_dxf/dialog_dxf_import.cpp index 3fb210c2cb..c46458aa39 100644 --- a/pcbnew/import_dxf/dialog_dxf_import.cpp +++ b/pcbnew/import_dxf/dialog_dxf_import.cpp @@ -300,7 +300,15 @@ bool DIALOG_DXF_IMPORT::TransferDataFromWindow() // Read dxf file: m_dxfImporter.ImportDxfFile( m_dxfFilename ); - return true; + // Get messages: + std::string& messages = m_dxfImporter.GetMessages(); + + if( messages.empty() ) + return true; + + // Show messages (list of net handled dxf items + wxMessageBox( messages.c_str(), _( "Not Handled DXF Items" ) ); + return true; } diff --git a/pcbnew/import_dxf/dxf2brd_items.cpp b/pcbnew/import_dxf/dxf2brd_items.cpp index b02438630c..960b98b985 100644 --- a/pcbnew/import_dxf/dxf2brd_items.cpp +++ b/pcbnew/import_dxf/dxf2brd_items.cpp @@ -1,8 +1,8 @@ /* * This program source code file is part of KiCad, a free EDA CAD application. * - * Copyright (C) 2013 Jean-Pierre Charras, jp.charras at wanadoo.fr - * Copyright (C) 1992-2016 KiCad Developers, see AUTHORS.txt for contributors. + * Copyright (C) 2018 Jean-Pierre Charras, jp.charras at wanadoo.fr + * Copyright (C) 1992-2018 KiCad Developers, see AUTHORS.txt for contributors. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -35,7 +35,6 @@ // this function just add the BOARD entity from dxf parameters (start and end point ...) -#include "libdxfrw.h" #include "dxf2brd_items.h" #include #include @@ -48,13 +47,12 @@ #include #include #include "common.h" -#include // minimum bulge value before resorting to a line segment; // the value 0.0218 is equivalent to about 5 degrees arc, #define MIN_BULGE 0.0218 -DXF2BRD_CONVERTER::DXF2BRD_CONVERTER() : DRW_Interface() +DXF2BRD_CONVERTER::DXF2BRD_CONVERTER() : DL_CreationAdapter() { m_xOffset = 0.0; // X coord offset for conversion (in mm) m_yOffset = 0.0; // Y coord offset for conversion (in mm) @@ -92,26 +90,78 @@ int DXF2BRD_CONVERTER::mapDim( double aDxfValue ) int DXF2BRD_CONVERTER::mapWidth( double aDxfWidth ) { + // Always return the default line width +#if 0 // mapWidth returns the aDxfValue if aDxfWidth > 0 m_defaultThickness if( aDxfWidth > 0.0 ) return Millimeter2iu( aDxfWidth * m_DXF2mm ); - +#endif return Millimeter2iu( m_defaultThickness ); } bool DXF2BRD_CONVERTER::ImportDxfFile( const wxString& aFile ) { LOCALE_IO locale; - dxfRW* dxf = new dxfRW( aFile.ToUTF8() ); - bool success = dxf->read( this, true ); - delete dxf; + DL_Dxf dxf_reader; + std::string filename = TO_UTF8( aFile ); + bool success = true; + + if( !dxf_reader.in( filename, this ) ) // if file open failed + success = false; return success; } -void DXF2BRD_CONVERTER::addLayer( const DRW_Layer& aData ) +void DXF2BRD_CONVERTER::reportMsg( const char* aMessage ) +{ + // Add message to keep trace of not handled dxf entities + m_messages += aMessage; + m_messages += '\n'; +} + + +void DXF2BRD_CONVERTER::addSpline( const DL_SplineData& aData ) +{ + // Called when starting reading a spline + m_curr_entity.Clear(); + m_curr_entity.m_EntityParseStatus = 1; + m_curr_entity.m_EntityFlag = aData.flags; + m_curr_entity.m_EntityType = DL_ENTITY_SPLINE; + m_curr_entity.m_SplineDegree = aData.degree; + m_curr_entity.m_SplineTangentStartX = aData.tangentStartX; + m_curr_entity.m_SplineTangentStartY = aData.tangentStartY; + m_curr_entity.m_SplineTangentEndX = aData.tangentEndX; + m_curr_entity.m_SplineTangentEndY = aData.tangentEndY; + m_curr_entity.m_SplineKnotsCount = aData.nKnots; + m_curr_entity.m_SplineControlCount = aData.nControl; + m_curr_entity.m_SplineFitCount = aData.nFit; +} + + +void DXF2BRD_CONVERTER::addControlPoint( const DL_ControlPointData& aData ) +{ + // Called for every spline control point, when reading a spline entity + m_curr_entity.m_SplineControlPointList.push_back( SPLINE_CTRL_POINT( aData.x , aData.y, aData.w ) ); +} + +void DXF2BRD_CONVERTER::addFitPoint( const DL_FitPointData& aData ) +{ + // Called for every spline fit point, when reading a spline entity + // we store only the X,Y coord values in a wxRealPoint + m_curr_entity.m_SplineFitPointList.push_back( wxRealPoint( aData.x, aData.y ) ); +} + + +void DXF2BRD_CONVERTER::addKnot( const DL_KnotData& aData) +{ + // Called for every spline knot value, when reading a spline entity + m_curr_entity.m_SplineKnotsList.push_back( aData.k ); +} + + +void DXF2BRD_CONVERTER::addLayer( const DL_LayerData& aData ) { // Not yet useful in Pcbnew. #if 0 @@ -121,129 +171,110 @@ void DXF2BRD_CONVERTER::addLayer( const DRW_Layer& aData ) } -void DXF2BRD_CONVERTER::addLine( const DRW_Line& aData ) +void DXF2BRD_CONVERTER::addLine( const DL_LineData& aData ) { DRAWSEGMENT* segm = ( m_importAsfootprintGraphicItems ) ? static_cast< DRAWSEGMENT* >( new EDGE_MODULE( NULL ) ) : new DRAWSEGMENT; segm->SetLayer( ToLAYER_ID( m_brdLayer ) ); - wxPoint start( mapX( aData.basePoint.x ), mapY( aData.basePoint.y ) ); + wxPoint start( mapX( aData.x1 ), mapY( aData.y1 ) ); segm->SetStart( start ); - wxPoint end( mapX( aData.secPoint.x ), mapY( aData.secPoint.y ) ); + wxPoint end( mapX( aData.x2 ), mapY( aData.y2 ) ); segm->SetEnd( end ); - segm->SetWidth( mapWidth( aData.thickness ) ); + segm->SetWidth( mapWidth( attributes.getWidth() ) ); m_newItemsList.push_back( segm ); } -void DXF2BRD_CONVERTER::addPolyline(const DRW_Polyline& aData ) + +void DXF2BRD_CONVERTER::addPolyline(const DL_PolylineData& aData ) { // Convert DXF Polylines into a series of KiCad Lines and Arcs. // A Polyline (as opposed to a LWPolyline) may be a 3D line or // even a 3D Mesh. The only type of Polyline which is guaranteed // to import correctly is a 2D Polyline in X and Y, which is what - // we assume of all Polylines. The width used is the width of the - // Polyline; per-vertex line widths, if present, are ignored. + // we assume of all Polylines. The width used is the width of the Polyline. + // per-vertex line widths, if present, are ignored. - wxRealPoint seg_start; - wxRealPoint poly_start; - double bulge = 0.0; - int lineWidth = mapWidth( aData.thickness ); - - for( unsigned ii = 0; ii < aData.vertlist.size(); ii++ ) - { - DRW_Vertex* vertex = aData.vertlist[ii]; - - if( ii == 0 ) - { - seg_start.x = m_xOffset + vertex->basePoint.x * m_DXF2mm; - seg_start.y = m_yOffset - vertex->basePoint.y * m_DXF2mm; - bulge = vertex->bulge; - poly_start = seg_start; - continue; - } - - wxRealPoint seg_end( m_xOffset + vertex->basePoint.x * m_DXF2mm, - m_yOffset - vertex->basePoint.y * m_DXF2mm ); - - if( std::abs( bulge ) < MIN_BULGE ) - insertLine( seg_start, seg_end, lineWidth ); - else - insertArc( seg_start, seg_end, bulge, lineWidth ); - - bulge = vertex->bulge; - seg_start = seg_end; - } - - // LWPolyline flags bit 0 indicates closed (1) or open (0) polyline - if( aData.flags & 1 ) - { - if( std::abs( bulge ) < MIN_BULGE ) - insertLine( seg_start, poly_start, lineWidth ); - else - insertArc( seg_start, poly_start, bulge, lineWidth ); - } + m_curr_entity.Clear(); + m_curr_entity.m_EntityParseStatus = 1; + m_curr_entity.m_EntityFlag = aData.flags; + m_curr_entity.m_EntityType = DL_ENTITY_POLYLINE; } -void DXF2BRD_CONVERTER::addLWPolyline(const DRW_LWPolyline& aData ) +void DXF2BRD_CONVERTER::addVertex( const DL_VertexData& aData ) { - // Currently, Pcbnew does not know polylines, for boards. - // So we have to convert a polyline to a set of segments. - // The import is a simplified import: the width of segment is - // (obviously constant and is the width of the DRW_LWPolyline. - // the variable width of each vertex (when exists) is not used. - wxRealPoint seg_start; - wxRealPoint poly_start; - double bulge = 0.0; - int lineWidth = mapWidth( aData.thickness ); + if( m_curr_entity.m_EntityParseStatus == 0 ) + return; // Error - for( unsigned ii = 0; ii < aData.vertlist.size(); ii++ ) + int lineWidth = mapWidth( attributes.getWidth() ); + + const DL_VertexData* vertex = &aData; + + if( m_curr_entity.m_EntityParseStatus == 1 ) // This is the first vertex of an entity { - DRW_Vertex2D* vertex = aData.vertlist[ii]; - - if( ii == 0 ) - { - seg_start.x = m_xOffset + vertex->x * m_DXF2mm; - seg_start.y = m_yOffset - vertex->y * m_DXF2mm; - bulge = vertex->bulge; - poly_start = seg_start; - continue; - } - - wxRealPoint seg_end( m_xOffset + vertex->x * m_DXF2mm, m_yOffset - vertex->y * m_DXF2mm ); - - if( std::abs( bulge ) < MIN_BULGE ) - insertLine( seg_start, seg_end, lineWidth ); - else - insertArc( seg_start, seg_end, bulge, lineWidth ); - - bulge = vertex->bulge; - seg_start = seg_end; + m_curr_entity.m_LastCoordinate.x = m_xOffset + vertex->x * m_DXF2mm; + m_curr_entity.m_LastCoordinate.y = m_yOffset - vertex->y * m_DXF2mm; + m_curr_entity.m_PolylineStart = m_curr_entity.m_LastCoordinate; + m_curr_entity.m_BulgeVertex = vertex->bulge; + m_curr_entity.m_EntityParseStatus = 2; + return; } - // LWPolyline flags bit 0 indicates closed (1) or open (0) polyline - if( aData.flags & 1 ) - { - if( std::abs( bulge ) < MIN_BULGE ) - insertLine( seg_start, poly_start, lineWidth ); - else - insertArc( seg_start, poly_start, bulge, lineWidth ); - } + + wxRealPoint seg_end( m_xOffset + vertex->x * m_DXF2mm, + m_yOffset - vertex->y * m_DXF2mm ); + + if( std::abs( m_curr_entity.m_BulgeVertex ) < MIN_BULGE ) + insertLine( m_curr_entity.m_LastCoordinate, seg_end, lineWidth ); + else + insertArc( m_curr_entity.m_LastCoordinate, seg_end, m_curr_entity.m_BulgeVertex, lineWidth ); + + m_curr_entity.m_LastCoordinate = seg_end; + m_curr_entity.m_BulgeVertex = vertex->bulge; } -void DXF2BRD_CONVERTER::addCircle( const DRW_Circle& aData ) +void DXF2BRD_CONVERTER::endEntity() +{ + if( m_curr_entity.m_EntityType == DL_ENTITY_POLYLINE || + m_curr_entity.m_EntityType == DL_ENTITY_LWPOLYLINE ) + { + // Polyline flags bit 0 indicates closed (1) or open (0) polyline + if( m_curr_entity.m_EntityFlag & 1 ) + { + int lineWidth = mapWidth( attributes.getWidth() ); + + if( std::abs( m_curr_entity.m_BulgeVertex ) < MIN_BULGE ) + insertLine( m_curr_entity.m_LastCoordinate, m_curr_entity.m_PolylineStart, lineWidth ); + else + insertArc( m_curr_entity.m_LastCoordinate, m_curr_entity.m_PolylineStart, + m_curr_entity.m_BulgeVertex, lineWidth ); + } + } + + if( m_curr_entity.m_EntityType == DL_ENTITY_SPLINE ) + { + int lineWidth = mapWidth( attributes.getWidth() ); + insertSpline( lineWidth ); + } + + m_curr_entity.Clear(); +} + + +void DXF2BRD_CONVERTER::addCircle( const DL_CircleData& aData ) { DRAWSEGMENT* segm = ( m_importAsfootprintGraphicItems ) ? static_cast< DRAWSEGMENT* >( new EDGE_MODULE( NULL ) ) : new DRAWSEGMENT; segm->SetLayer( ToLAYER_ID( m_brdLayer ) ); segm->SetShape( S_CIRCLE ); - wxPoint center( mapX( aData.basePoint.x ), mapY( aData.basePoint.y ) ); + wxPoint center( mapX( aData.cx ), mapY( aData.cy ) ); segm->SetCenter( center ); - wxPoint circle_start( mapX( aData.basePoint.x + aData.radious ), mapY( aData.basePoint.y ) ); + wxPoint circle_start( mapX( aData.cx + aData.radius ), mapY( aData.cy ) ); segm->SetArcStart( circle_start ); - segm->SetWidth( mapWidth( aData.thickness ) ); + segm->SetWidth( mapWidth( attributes.getWidth() ) ); m_newItemsList.push_back( segm ); } @@ -251,7 +282,7 @@ void DXF2BRD_CONVERTER::addCircle( const DRW_Circle& aData ) /* * Import Arc entities. */ -void DXF2BRD_CONVERTER::addArc( const DRW_Arc& data ) +void DXF2BRD_CONVERTER::addArc( const DL_ArcData& data ) { DRAWSEGMENT* segm = ( m_importAsfootprintGraphicItems ) ? static_cast< DRAWSEGMENT* >( new EDGE_MODULE( NULL ) ) : new DRAWSEGMENT; @@ -260,18 +291,18 @@ void DXF2BRD_CONVERTER::addArc( const DRW_Arc& data ) segm->SetShape( S_ARC ); // Init arc centre: - wxPoint center( mapX( data.basePoint.x ), mapY( data.basePoint.y ) ); + wxPoint center( mapX( data.cx ), mapY( data.cy ) ); segm->SetCenter( center ); // Init arc start point - double arcStartx = data.radious; + double arcStartx = data.radius; double arcStarty = 0; - double startangle = data.staangle; - double endangle = data.endangle; + double startangle = data.angle1; + double endangle = data.angle2; RotatePoint( &arcStartx, &arcStarty, -RAD2DECIDEG( startangle ) ); - wxPoint arcStart( mapX( arcStartx + data.basePoint.x ), - mapY( arcStarty + data.basePoint.y ) ); + wxPoint arcStart( mapX( arcStartx + data.cx ), + mapY( arcStarty + data.cy ) ); segm->SetArcStart( arcStart ); // calculate arc angle (arcs are CCW, and should be < 0 in Pcbnew) @@ -282,12 +313,12 @@ void DXF2BRD_CONVERTER::addArc( const DRW_Arc& data ) segm->SetAngle( angle ); - segm->SetWidth( mapWidth( data.thickness ) ); + segm->SetWidth( mapWidth( attributes.getWidth() ) ); m_newItemsList.push_back( segm ); } -void DXF2BRD_CONVERTER::addText( const DRW_Text& aData ) +void DXF2BRD_CONVERTER::addText( const DL_TextData& aData ) { BOARD_ITEM* brdItem; EDA_TEXT* textItem; @@ -307,12 +338,12 @@ void DXF2BRD_CONVERTER::addText( const DRW_Text& aData ) brdItem->SetLayer( ToLAYER_ID( m_brdLayer ) ); - wxPoint refPoint( mapX( aData.basePoint.x ), mapY( aData.basePoint.y ) ); - wxPoint secPoint( mapX( aData.secPoint.x ), mapY( aData.secPoint.y ) ); + wxPoint refPoint( mapX( aData.ipx ), mapY( aData.ipy ) ); + wxPoint secPoint( mapX( aData.apx ), mapY( aData.apy ) ); - if( aData.alignV != 0 || aData.alignH != 0 || aData.alignH == DRW_Text::HMiddle ) + if( aData.vJustification != 0 || aData.hJustification != 0 || aData.hJustification == 4 ) { - if( aData.alignH != DRW_Text::HAligned && aData.alignH != DRW_Text::HFit ) + if( aData.hJustification != 3 && aData.hJustification != 5 ) { wxPoint tmp = secPoint; secPoint = refPoint; @@ -320,50 +351,50 @@ void DXF2BRD_CONVERTER::addText( const DRW_Text& aData ) } } - switch( aData.alignV ) + switch( aData.vJustification ) { - case DRW_Text::VBaseLine: + case 0: //DRW_Text::VBaseLine: textItem->SetVertJustify( GR_TEXT_VJUSTIFY_BOTTOM ); break; - case DRW_Text::VBottom: + case 1: //DRW_Text::VBottom: textItem->SetVertJustify( GR_TEXT_VJUSTIFY_BOTTOM ); break; - case DRW_Text::VMiddle: + case 2: //DRW_Text::VMiddle: textItem->SetVertJustify( GR_TEXT_VJUSTIFY_CENTER ); break; - case DRW_Text::VTop: + case 3: //DRW_Text::VTop: textItem->SetVertJustify( GR_TEXT_VJUSTIFY_TOP ); break; } - switch( aData.alignH ) + switch( aData.hJustification ) { - case DRW_Text::HLeft: + case 0: //DRW_Text::HLeft: textItem->SetHorizJustify( GR_TEXT_HJUSTIFY_LEFT ); break; - case DRW_Text::HCenter: + case 1: //DRW_Text::HCenter: textItem->SetHorizJustify( GR_TEXT_HJUSTIFY_CENTER ); break; - case DRW_Text::HRight: + case 2: //DRW_Text::HRight: textItem->SetHorizJustify( GR_TEXT_HJUSTIFY_RIGHT ); break; - case DRW_Text::HAligned: + case 3: //DRW_Text::HAligned: // no equivalent options in text pcb. textItem->SetHorizJustify( GR_TEXT_HJUSTIFY_LEFT ); break; - case DRW_Text::HMiddle: + case 4: //DRW_Text::HMiddle: // no equivalent options in text pcb. textItem->SetHorizJustify( GR_TEXT_HJUSTIFY_CENTER ); break; - case DRW_Text::HFit: + case 5: //DRW_Text::HFit: // no equivalent options in text pcb. textItem->SetHorizJustify( GR_TEXT_HJUSTIFY_LEFT ); break; @@ -392,14 +423,14 @@ void DXF2BRD_CONVERTER::addText( const DRW_Text& aData ) // The 0.8 factor gives a better height/width ratio with our font textItem->SetTextWidth( mapDim( aData.height * 0.8 ) ); textItem->SetTextHeight( mapDim( aData.height ) ); - textItem->SetThickness( mapWidth( aData.thickness ) ); + textItem->SetThickness( mapWidth( aData.height * 0.15 ) ); // Gives a reasonable text thickness textItem->SetText( text ); m_newItemsList.push_back( static_cast< BOARD_ITEM* >( brdItem ) ); } -void DXF2BRD_CONVERTER::addMText( const DRW_MText& aData ) +void DXF2BRD_CONVERTER::addMText( const DL_MTextData& aData ) { wxString text = toNativeString( wxString::FromUTF8( aData.text.c_str() ) ); wxString attrib, tmp; @@ -446,7 +477,7 @@ void DXF2BRD_CONVERTER::addMText( const DRW_MText& aData ) } brdItem->SetLayer( ToLAYER_ID( m_brdLayer ) ); - wxPoint textpos( mapX( aData.basePoint.x ), mapY( aData.basePoint.y ) ); + wxPoint textpos( mapX( aData.ipx ), mapY( aData.ipy ) ); textItem->SetTextPos( textpos ); textItem->SetTextAngle( aData.angle * 10 ); @@ -454,15 +485,15 @@ void DXF2BRD_CONVERTER::addMText( const DRW_MText& aData ) // The 0.8 factor gives a better height/width ratio with our font textItem->SetTextWidth( mapDim( aData.height * 0.8 ) ); textItem->SetTextHeight( mapDim( aData.height ) ); - textItem->SetThickness( mapWidth( aData.thickness ) ); + textItem->SetThickness( mapWidth( aData.height * 0.15 ) ); // Gives a reasonable text thickness textItem->SetText( text ); // Initialize text justifications: - if( aData.textgen <= 3 ) + if( aData.attachmentPoint <= 3 ) { textItem->SetVertJustify( GR_TEXT_VJUSTIFY_TOP ); } - else if( aData.textgen <= 6 ) + else if( aData.attachmentPoint <= 6 ) { textItem->SetVertJustify( GR_TEXT_VJUSTIFY_CENTER ); } @@ -471,11 +502,11 @@ void DXF2BRD_CONVERTER::addMText( const DRW_MText& aData ) textItem->SetVertJustify( GR_TEXT_VJUSTIFY_BOTTOM ); } - if( aData.textgen % 3 == 1 ) + if( aData.attachmentPoint % 3 == 1 ) { textItem->SetHorizJustify( GR_TEXT_HJUSTIFY_LEFT ); } - else if( aData.textgen % 3 == 2 ) + else if( aData.attachmentPoint % 3 == 2 ) { textItem->SetHorizJustify( GR_TEXT_HJUSTIFY_CENTER ); } @@ -484,7 +515,7 @@ void DXF2BRD_CONVERTER::addMText( const DRW_MText& aData ) textItem->SetHorizJustify( GR_TEXT_HJUSTIFY_RIGHT ); } -#if 0 // These setting have no mening in Pcbnew +#if 0 // These setting have no meaning in Pcbnew if( data.alignH == 1 ) { // Text is left to right; @@ -512,86 +543,92 @@ void DXF2BRD_CONVERTER::addMText( const DRW_MText& aData ) } -void DXF2BRD_CONVERTER::addHeader( const DRW_Header* data ) +void DXF2BRD_CONVERTER::setVariableInt( const std::string& key, int value, int code ) { - std::map::const_iterator it; - m_DXF2mm = 1.0; // assume no scale factor + // Called for every int variable in the DXF file (e.g. "$INSUNITS"). - for( it = data->vars.begin(); it != data->vars.end(); ++it ) + if( key == "$DWGCODEPAGE" ) { - std::string key = ( (*it).first ).c_str(); - - if( key == "$DWGCODEPAGE" ) - { - DRW_Variant* var = (*it).second; - m_codePage = ( *var->content.s ); - } - else if( key == "$INSUNITS" ) - { - DRW_Variant* var = (*it).second; - - switch( var->content.i ) - { - case 1: // inches - m_DXF2mm = 25.4; - break; - - case 2: // feet - m_DXF2mm = 304.8; - break; - - case 5: // centimeters - m_DXF2mm = 10.0; - break; - - case 6: // meters - m_DXF2mm = 1000.0; - break; - - case 8: // microinches - m_DXF2mm = 2.54e-5; - break; - - case 9: // mils - m_DXF2mm = 0.0254; - break; - - case 10: // yards - m_DXF2mm = 914.4; - break; - - case 11: // Angstroms - m_DXF2mm = 1.0e-7; - break; - - case 12: // nanometers - m_DXF2mm = 1.0e-6; - break; - - case 13: // micrometers - m_DXF2mm = 1.0e-3; - break; - - case 14: // decimeters - m_DXF2mm = 100.0; - break; - - default: - // use the default of 1.0 for: - // 0: Unspecified Units - // 4: mm - // 3: miles - // 7: kilometers - // 15: decameters - // 16: hectometers - // 17: gigameters - // 18: AU - // 19: lightyears - // 20: parsecs - break; - } - } + m_codePage = value; + return; } + + if( key == "$INSUNITS" ) // Drawing units + { + switch( value ) + { + case 1: // inches + m_DXF2mm = 25.4; + break; + + case 2: // feet + m_DXF2mm = 304.8; + break; + + case 4: // mm + m_DXF2mm = 1.0; + break; + + case 5: // centimeters + m_DXF2mm = 10.0; + break; + + case 6: // meters + m_DXF2mm = 1000.0; + break; + + case 8: // microinches + m_DXF2mm = 2.54e-5; + break; + + case 9: // mils + m_DXF2mm = 0.0254; + break; + + case 10: // yards + m_DXF2mm = 914.4; + break; + + case 11: // Angstroms + m_DXF2mm = 1.0e-7; + break; + + case 12: // nanometers + m_DXF2mm = 1.0e-6; + break; + + case 13: // micrometers + m_DXF2mm = 1.0e-3; + break; + + case 14: // decimeters + m_DXF2mm = 100.0; + break; + + default: + // use the default of 1.0 for: + // 0: Unspecified Units + // 3: miles + // 7: kilometers + // 15: decameters + // 16: hectometers + // 17: gigameters + // 18: AU + // 19: lightyears + // 20: parsecs + m_DXF2mm = 1.0; + break; + } + + return; + } +} + + +void DXF2BRD_CONVERTER::setVariableString( const std::string& key, const std::string& value, + int code ) +{ + // Called for every string variable in the DXF file (e.g. "$ACADVER"). } @@ -723,7 +760,7 @@ wxString DXF2BRD_CONVERTER::toNativeString( const wxString& aData ) } -void DXF2BRD_CONVERTER::addTextStyle( const DRW_Textstyle& aData ) +void DXF2BRD_CONVERTER::addTextStyle( const DL_StyleData& aData ) { // TODO } @@ -743,7 +780,6 @@ void DXF2BRD_CONVERTER::insertLine( const wxRealPoint& aSegStart, segm->SetWidth( aWidth ); m_newItemsList.push_back( segm ); - return; } @@ -825,3 +861,82 @@ void DXF2BRD_CONVERTER::insertArc( const wxRealPoint& aSegStart, const wxRealPoi m_newItemsList.push_back( segm ); return; } + + +#include "tinyspline_lib/tinysplinecpp.h" + +void DXF2BRD_CONVERTER::insertSpline( int aWidth ) +{ + #if 0 // Debug only + wxLogMessage("spl deg %d kn %d ctr %d fit %d", + m_curr_entity.m_SplineDegree, + m_curr_entity.m_SplineKnotsList.size(), + m_curr_entity.m_SplineControlPointList.size(), + m_curr_entity.m_SplineFitPointList.size() ); + #endif + + // Very basic conversion to segments + unsigned imax = m_curr_entity.m_SplineControlPointList.size(); + + if( imax < 2 ) // malformed spline + return; + +#if 0 // set to 1 to approximate the spline by segments between 2 control points + wxPoint startpoint( mapX( m_curr_entity.m_SplineControlPointList[0].m_x ), + mapY( m_curr_entity.m_SplineControlPointList[0].m_y ) ); + + for( unsigned int ii = 1; ii < imax; ++ii ) + { + wxPoint endpoint( mapX( m_curr_entity.m_SplineControlPointList[ii].m_x ), + mapY( m_curr_entity.m_SplineControlPointList[ii].m_y ) ); + + if( startpoint != endpoint ) + { + DRAWSEGMENT* segm = ( m_importAsfootprintGraphicItems ) ? + static_cast< DRAWSEGMENT* >( new EDGE_MODULE( NULL ) ) : + new DRAWSEGMENT; + segm->SetLayer( ToLAYER_ID( m_brdLayer ) ); + segm->SetStart( startpoint ); + segm->SetEnd( endpoint ); + segm->SetWidth( aWidth ); + m_newItemsList.push_back( segm ); + startpoint = endpoint; + } + } +#else // Use bezier curves, supported by pcbnew, to approximate the spline + tinyspline::BSpline dxfspline( m_curr_entity.m_SplineControlPointList.size(), + /* coord dim */ 2, m_curr_entity.m_SplineDegree ); + std::vector ctrlp; + + for( unsigned ii = 0; ii < imax; ++ii ) + { + ctrlp.push_back( m_curr_entity.m_SplineControlPointList[ii].m_x ); + ctrlp.push_back( m_curr_entity.m_SplineControlPointList[ii].m_y ); + } + + dxfspline.setCtrlp( ctrlp ); + dxfspline.setKnots( m_curr_entity.m_SplineKnotsList ); + tinyspline::BSpline beziers( dxfspline.toBeziers() ); + + std::vector coords = beziers.ctrlp(); + + // Each Bezier curve uses 4 vertices (a start point, 2 control points and a end point). + // So we can have more than one Bezier curve ( there are one curve each four vertices) + for( unsigned ii = 0; ii < coords.size(); ii += 8 ) + { + DRAWSEGMENT* segm = ( m_importAsfootprintGraphicItems ) ? + static_cast< DRAWSEGMENT* >( new EDGE_MODULE( NULL ) ) : + new DRAWSEGMENT; + segm->SetLayer( ToLAYER_ID( m_brdLayer ) ); + segm->SetShape( S_CURVE ); + segm->SetStart( wxPoint( mapX( coords[ii] ), mapY( coords[ii+1] ) ) ); + segm->SetBezControl1( wxPoint( mapX( coords[ii+2] ), mapY( coords[ii+3] ) ) ); + segm->SetBezControl2( wxPoint( mapX( coords[ii+4] ), mapY( coords[ii+5] ) ) ); + segm->SetEnd( wxPoint( mapX( coords[ii+6] ), mapY( coords[ii+7] ) ) ); + segm->SetWidth( aWidth ); + segm->RebuildBezierToSegmentsPointsList( aWidth ); + m_newItemsList.push_back( segm ); + } +#endif +} + diff --git a/pcbnew/import_dxf/dxf2brd_items.h b/pcbnew/import_dxf/dxf2brd_items.h index bee2cf5677..1874bb26ec 100644 --- a/pcbnew/import_dxf/dxf2brd_items.h +++ b/pcbnew/import_dxf/dxf2brd_items.h @@ -1,32 +1,33 @@ /**************************************************************************** -** -** This file comes from the LibreCAD project, a 2D CAD program -** -** Copyright (C) 2011 Rallaz, rallazz@gmail.com -** Copyright (C) 2010 R. van Twisk (librecad@rvt.dds.nl) -** -** -** This file may be distributed and/or modified under the terms of the -** GNU General Public License as published by the Free Software -** Foundation either version 2 of the License, or (at your option) -** any later version. -** -** This program is distributed in the hope that it will be useful, -** but WITHOUT ANY WARRANTY; without even the implied warranty of -** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -** GNU General Public License for more details. -** -** You should have received a copy of the GNU General Public License -** along with this program; if not, write to the Free Software -** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA -** + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2018 Jean-Pierre Charras, jp.charras at wanadoo.fr + * Copyright (C) 1992-2018 KiCad Developers, see AUTHORS.txt for contributors. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, you may find one here: + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * or you may search the http://www.gnu.org website for the version 2 license, + * or you may write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA **********************************************************************/ -#ifndef FILTERDXFRW_H -#define FILTERDXFRW_H +#ifndef DXF2BRD_ITEMS_H +#define DXF2BRD_ITEMS_H -#include "drw_interface.h" +#include "dl_dxf.h" +#include "dl_creationadapter.h" #include "wx/wx.h" #include @@ -34,12 +35,81 @@ class BOARD; class BOARD_ITEM; /** - * This format filter class can import and export DXF files. - * It depends on the dxflib library. - * - * @author Rallaz + * A helper class to store a spline control point (in X,Y plane only) */ -class DXF2BRD_CONVERTER : public DRW_Interface +struct SPLINE_CTRL_POINT +{ + double m_x; + double m_y; + double m_weight; + + SPLINE_CTRL_POINT( double a_x, double a_y, double a_weight ) + : m_x( a_x ), m_y( a_y ), m_weight( a_weight ) + {} +}; + +/** + * A helper class to parse a DXF entity (polyline and spline) + */ +class DXF2BRD_ENTITY_DATA +{ +public: + int m_EntityType; // the DXF type of entity + int m_EntityParseStatus; // Inside a entity: status od parsing: + // 0 = no entity + // 1 = first item of entity + // 2 = entity in progress + int m_EntityFlag; // a info flag to parse entities + + wxRealPoint m_LastCoordinate; // the last vertex coordinate read (unit = mm) + wxRealPoint m_PolylineStart; // The first point of the polyline entity, when reading a polyline (unit = mm) + double m_BulgeVertex; // the last vertex bulge value read + + // for spline parsing: parameters + unsigned int m_SplineDegree; + unsigned int m_SplineKnotsCount; + unsigned int m_SplineControlCount; + unsigned int m_SplineFitCount; + double m_SplineTangentStartX; // tangeant dir X for the start point + double m_SplineTangentStartY; // tangeant dir Y for the start point + double m_SplineTangentEndX; // tangeant dir X for the end point + double m_SplineTangentEndY; // tangeant dir Y for the end point + + // for spline parsing: buffers to store control points, fit points and knot + std::vector m_SplineKnotsList; // knots list, code 40 + // control points list coordinates, code 10, 20 & 30 (only X and Y cood and Weight) + std::vector m_SplineControlPointList; + // fit points list, code 11, 21 & 31 (only X and Y cood) + std::vector m_SplineFitPointList; + + DXF2BRD_ENTITY_DATA() { Clear(); }; + + // Reset the entity parameters + void Clear() + { + m_EntityType = DL_UNKNOWN; + m_EntityParseStatus = 0; + m_EntityFlag = 0; + m_SplineDegree = 1; + m_SplineKnotsCount = 0; + m_SplineControlCount = 0; + m_SplineFitCount = 0; + m_SplineTangentStartX = 0.0; + m_SplineTangentStartY = 0.0; + m_SplineTangentEndX = 0.0; + m_SplineTangentEndY = 0.0; + m_SplineKnotsList.clear(); + m_SplineControlPointList.clear(); + m_SplineFitPointList.clear(); + } +}; + + +/** + * This class import DXF ASCII files and convert basic entities to board entities. + * It depends on the dxflib library. + */ +class DXF2BRD_CONVERTER : public DL_CreationAdapter { private: std::list m_newItemsList; // The list of new items added to the board @@ -52,6 +122,10 @@ private: std::string m_codePage; // The code page, not used here bool m_importAsfootprintGraphicItems; // Use module items instead of board items when true. // true when the items are imported in the footprint editor + std::string m_messages; // messages generated during dxf file parsing. + // Each message ends by '\n' + DXF2BRD_ENTITY_DATA m_curr_entity; // the current entity parameters when parsing a DXF entity + public: DXF2BRD_CONVERTER(); @@ -115,7 +189,15 @@ public: return m_newItemsList; } + /** + * @return the list of messages in one string. Each message ends by '\n' + */ + std::string& GetMessages() { return m_messages; } + private: + // report message to keep trace of not supported dxf entities: + void reportMsg( const char* aMessage ); + // coordinate conversions from dxf to internal units int mapX( double aDxfCoordX ); int mapY( double aDxfCoordY ); @@ -124,59 +206,88 @@ private: // or m_defaultThickness int mapWidth( double aDxfWidth ); - // Functions to aid in the creation of a LWPolyline + // Functions to aid in the creation of a Polyline void insertLine( const wxRealPoint& aSegStart, const wxRealPoint& aSegEnd, int aWidth ); void insertArc( const wxRealPoint& aSegStart, const wxRealPoint& aSegEnd, double aBulge, int aWidth ); + // Add a dxf spline (stored in m_curr_entity) to the board, after conversion to segments + void insertSpline( int aWidth ); - // Methods from DRW_CreationInterface: - // They are "call back" fonctions, called when the corresponding object - // is read in dxf file - // Depending of the application, they can do something or not - virtual void addHeader( const DRW_Header* aData ) override; - virtual void addLType( const DRW_LType& aData ) override {} - virtual void addLayer( const DRW_Layer& aData ) override; - virtual void addDimStyle( const DRW_Dimstyle& aData ) override {} - virtual void addBlock( const DRW_Block& aData ) override {} - virtual void endBlock() override {} - virtual void addPoint( const DRW_Point& aData ) override {} - virtual void addLine( const DRW_Line& aData) override; - virtual void addRay( const DRW_Ray& aData ) override {} - virtual void addXline( const DRW_Xline& aData ) override {} - virtual void addCircle( const DRW_Circle& aData ) override; - virtual void addArc( const DRW_Arc& aData ) override; - virtual void addEllipse( const DRW_Ellipse& aData ) override {} - virtual void addLWPolyline( const DRW_LWPolyline& aData ) override; - virtual void addText( const DRW_Text& aData ) override; - virtual void addPolyline( const DRW_Polyline& aData ) override; - virtual void addSpline( const DRW_Spline* aData ) override {} - virtual void addKnot( const DRW_Entity&) override {} - virtual void addInsert( const DRW_Insert& aData ) override {} - virtual void addTrace( const DRW_Trace& aData ) override {} - virtual void addSolid( const DRW_Solid& aData ) override {} - virtual void addMText( const DRW_MText& aData) override; - virtual void addDimAlign( const DRW_DimAligned* aData ) override {} - virtual void addDimLinear( const DRW_DimLinear* aData ) override {} - virtual void addDimRadial( const DRW_DimRadial* aData ) override {} - virtual void addDimDiametric( const DRW_DimDiametric* aData ) override {} - virtual void addDimAngular( const DRW_DimAngular* aData ) override {} - virtual void addDimAngular3P( const DRW_DimAngular3p* aData ) override {} - virtual void addDimOrdinate( const DRW_DimOrdinate* aData ) override {} - virtual void addLeader( const DRW_Leader* aData ) override {} - virtual void addHatch( const DRW_Hatch* aData ) override {} - virtual void addImage( const DRW_Image* aData ) override {} - virtual void linkImage( const DRW_ImageDef* aData ) override {} + // Methods from DL_CreationAdapter: + // They are something like"call back" fonctions, + // called when the corresponding object is read in dxf file - virtual void add3dFace( const DRW_3Dface& aData ) override {} - virtual void addComment( const char*) override {} + /** + * Called for every string variable in the DXF file (e.g. "$ACADVER"). + */ + virtual void setVariableString( const std::string& key, const std::string& value, + int code ) override; - virtual void addVport( const DRW_Vport& aData ) override {} + /** + * Called for every int variable in the DXF file (e.g. "$ACADMAINTVER"). + */ + virtual void setVariableInt( const std::string& key, int value, int code ) override; - virtual void addTextStyle( const DRW_Textstyle& aData ) override; + /** + * Called for every double variable in the DXF file (e.g. "$DIMEXO"). + */ + virtual void setVariableDouble( const std::string& key, double value, int code ) override {} - virtual void addViewport( const DRW_Viewport& aData ) override {} + virtual void addLayer( const DL_LayerData& aData ) override; + virtual void addLine( const DL_LineData& aData) override; + virtual void addCircle( const DL_CircleData& aData ) override; + virtual void addArc( const DL_ArcData& aData ) override; + //virtual void addLWPolyline( const DRW_LWPolyline& aData ) override; + virtual void addText( const DL_TextData& aData ) override; + virtual void addPolyline( const DL_PolylineData& aData ) override; - virtual void setBlock( const int aHandle ) override {} + /** Called for every polyline vertex */ + virtual void addVertex( const DL_VertexData& aData ) override; + virtual void addMText( const DL_MTextData& aData) override; + virtual void addTextStyle( const DL_StyleData& aData ) override; + + virtual void endEntity() override; + + /** Called for every spline */ + virtual void addSpline( const DL_SplineData& aData ) override; + + /** Called for every spline control point */ + virtual void addControlPoint( const DL_ControlPointData& aData ) override; + + /** Called for every spline fit point */ + virtual void addFitPoint( const DL_FitPointData& aData ) override; + + /** Called for every spline knot value */ + virtual void addKnot( const DL_KnotData& aData ) override; + + // Not yet handled DXF entities: + virtual void addDimAlign( const DL_DimensionData&, + const DL_DimAlignedData& ) override { reportMsg( "DL_Dimension not managed" ); } + virtual void addDimLinear( const DL_DimensionData&, + const DL_DimLinearData& ) override { reportMsg( "DL_Dimension not managed" ); } + virtual void addDimRadial( const DL_DimensionData&, + const DL_DimRadialData& ) override { reportMsg( "DL_Dimension not managed" ); } + virtual void addDimDiametric( const DL_DimensionData&, + const DL_DimDiametricData& ) override { reportMsg( "DL_Dimension not managed" ); } + virtual void addDimAngular( const DL_DimensionData&, + const DL_DimAngularData& ) override { reportMsg( "DL_Dimension not managed" ); } + virtual void addDimAngular3P( const DL_DimensionData&, + const DL_DimAngular3PData& ) override { reportMsg( "DL_Dimension not managed" ); } + virtual void addDimOrdinate( const DL_DimensionData&, + const DL_DimOrdinateData& ) override { reportMsg( "DL_Dimension not managed" ); } + virtual void addLeader( const DL_LeaderData& ) override { reportMsg( "DL_Leader not managed" ); } + virtual void addLeaderVertex( const DL_LeaderVertexData& ) override { reportMsg( "DL_LeaderVertex not managed" ); } + + virtual void addHatch( const DL_HatchData& ) override { reportMsg( "DL_Hatch not managed" ); } + + virtual void addTrace( const DL_TraceData& ) override { reportMsg( "DL_Trace not managed" ); } + virtual void add3dFace( const DL_3dFaceData& ) override { reportMsg( "DL_3dFace not managed" ); } + virtual void addSolid( const DL_SolidData& ) override { reportMsg( "DL_Solid not managed" ); } + + virtual void addImage( const DL_ImageData& ) override { reportMsg( "DL_ImageDa not managed" ); } + virtual void linkImage( const DL_ImageDefData& ) override { reportMsg( "DL_ImageDef not managed" ); } + virtual void addHatchLoop( const DL_HatchLoopData& ) override { reportMsg( "DL_HatchLoop not managed" ); } + virtual void addHatchEdge( const DL_HatchEdgeData& ) override { reportMsg( "DL_HatchEdge not managed" ); } /** * Converts a native unicode string into a DXF encoded string. @@ -193,23 +304,8 @@ private: */ static wxString toNativeString( const wxString& aData ); - // These functions are not used in Kicad. - // But because they are virtual pure in DRW_Interface, they should be defined - virtual void writeTextstyles() override {} - virtual void writeVports() override {} - virtual void writeHeader( DRW_Header& aData ) override {} - virtual void writeEntities() override {} - virtual void writeLTypes() override {} - virtual void writeLayers() override {} - virtual void writeBlockRecords() override {} - virtual void writeBlocks() override {} - virtual void writeDimstyles() override {} - void writeLine(); void writeMtext(); - - virtual void addAppId( const DRW_AppId& data ) override {} - virtual void writeAppId() override {} }; -#endif // FILTERDXFRW_H +#endif // DXF2BRD_ITEMS_H diff --git a/pcbnew/kicad_plugin.cpp b/pcbnew/kicad_plugin.cpp index c5855dbcca..a6bad50ac9 100644 --- a/pcbnew/kicad_plugin.cpp +++ b/pcbnew/kicad_plugin.cpp @@ -984,8 +984,8 @@ void PCB_IO::format( EDGE_MODULE* aModuleDrawing, int aNestLevel ) const case S_CURVE: // Bezier curve m_out->Print( aNestLevel, "(fp_curve (pts (xy %s) (xy %s) (xy %s) (xy %s))", FMT_IU( aModuleDrawing->GetStart0() ).c_str(), - FMT_IU( aModuleDrawing->GetBezControl1() ).c_str(), - FMT_IU( aModuleDrawing->GetBezControl2() ).c_str(), + FMT_IU( aModuleDrawing->GetBezier0_C1() ).c_str(), + FMT_IU( aModuleDrawing->GetBezier0_C2() ).c_str(), FMT_IU( aModuleDrawing->GetEnd0() ).c_str() ); break; diff --git a/pcbnew/pcb_painter.cpp b/pcbnew/pcb_painter.cpp index 3898d2614a..a4c0f2d9d1 100644 --- a/pcbnew/pcb_painter.cpp +++ b/pcbnew/pcb_painter.cpp @@ -961,7 +961,7 @@ void PCB_PAINTER::draw( const DRAWSEGMENT* aSegment, int aLayer ) std::copy( points.begin(), points.end(), std::back_inserter( pointsList ) ); pointsList.push_back( points[0] ); - m_gal->SetLineWidth( aSegment->GetWidth() ); + m_gal->SetLineWidth( thickness ); m_gal->SetIsFill( true ); m_gal->SetIsStroke( true ); m_gal->DrawPolygon( pointsList ); @@ -971,6 +971,9 @@ void PCB_PAINTER::draw( const DRAWSEGMENT* aSegment, int aLayer ) } case S_CURVE: + m_gal->SetIsFill( false ); + m_gal->SetIsStroke( true ); + m_gal->SetLineWidth( thickness ); m_gal->DrawCurve( VECTOR2D( aSegment->GetStart() ), VECTOR2D( aSegment->GetBezControl1() ), VECTOR2D( aSegment->GetBezControl2() ), diff --git a/pcbnew/pcb_parser.cpp b/pcbnew/pcb_parser.cpp index 6bfcf352b6..72b0a4dd14 100644 --- a/pcbnew/pcb_parser.cpp +++ b/pcbnew/pcb_parser.cpp @@ -2209,8 +2209,8 @@ EDGE_MODULE* PCB_PARSER::parseEDGE_MODULE() Expecting( T_pts ); segment->SetStart0( parseXY() ); - segment->SetBezControl1( parseXY() ); - segment->SetBezControl2( parseXY() ); + segment->SetBezier0_C1( parseXY() ); + segment->SetBezier0_C2( parseXY() ); segment->SetEnd0( parseXY() ); NeedRIGHT(); break; diff --git a/pcbnew/tools/drawing_tool.cpp b/pcbnew/tools/drawing_tool.cpp index ef13d6175e..89d67128f4 100644 --- a/pcbnew/tools/drawing_tool.cpp +++ b/pcbnew/tools/drawing_tool.cpp @@ -2,7 +2,7 @@ * This program source code file is part of KiCad, a free EDA CAD application. * * Copyright (C) 2014-2017 CERN - * Copyright (C) 2016 KiCad Developers, see AUTHORS.txt for contributors. + * Copyright (C) 2018 KiCad Developers, see AUTHORS.txt for contributors. * @author Maciej Suminski * * This program is free software; you can redistribute it and/or @@ -762,6 +762,15 @@ int DRAWING_TOOL::PlaceDXF( const TOOL_EVENT& aEvent ) SCOPED_DRAW_MODE scopedDrawMode( m_mode, MODE::DXF ); + // Now move the new items to the current cursor position: + cursorPos = m_controls->GetCursorPosition(); + delta = cursorPos - firstItem->GetPosition(); + + for( auto item : preview ) + static_cast( item )->Move( wxPoint( delta.x, delta.y ) ); + + m_view->Update( &preview ); + Activate(); // Main loop: keep receiving events diff --git a/utils/idftools/CMakeLists.txt b/utils/idftools/CMakeLists.txt index 8315a9be0d..8eefaca0f9 100644 --- a/utils/idftools/CMakeLists.txt +++ b/utils/idftools/CMakeLists.txt @@ -26,10 +26,10 @@ add_library( idf3 STATIC ${IDF3_FILES} ) add_executable( idfcyl idf_cylinder.cpp ) add_executable( idfrect idf_rect.cpp ) -add_executable( dxf2idf dxf2idfmain.cpp dxf2idf.cpp ) +#add_executable( dxf2idf dxf2idfmain.cpp dxf2idf.cpp ) add_executable( idf2vrml idf2vrml.cpp ) -target_link_libraries( dxf2idf lib_dxf idf3 ${wxWidgets_LIBRARIES} ) +#target_link_libraries( dxf2idf lib_dxf idf3 ${wxWidgets_LIBRARIES} ) target_link_libraries( idf2vrml idf3 common ${OPENGL_LIBRARIES} ${wxWidgets_LIBRARIES} ) @@ -39,7 +39,8 @@ if( APPLE ) RUNTIME_OUTPUT_DIRECTORY ${OSX_BUNDLE_BUILD_BIN_DIR} ) else() - install( TARGETS idfcyl idfrect dxf2idf idf2vrml + install( TARGETS idfcyl idfrect idf2vrml + #dxf2idf DESTINATION ${KICAD_BIN} COMPONENT binary ) endif()