From a4ba01f42d36aec1076fc0055628d53eca21a8f1 Mon Sep 17 00:00:00 2001 From: Cirilo Bernardo Date: Sun, 20 Mar 2016 21:33:53 -0400 Subject: [PATCH] Various fixes to DXF import The attached patch makes the following changes to import DXF: 1. correctly implements scaling based on DXF $INSUNITS - at least where INSUNITS is sensible. I ignore units like miles, gigameters, AU, lightyear, parsec ... 2. correctly implements LWPolylines, adding an ARC segment rather than a line segment when bulge != 0. A number of users have been complaining about getting the wrong board shape (chamfered corners rather than rounded corners) on DXF Import, now they shouldn't complain. --- pcbnew/import_dxf/dxf2brd_items.cpp | 217 ++++++++++++++++++++++++---- pcbnew/import_dxf/dxf2brd_items.h | 15 +- 2 files changed, 199 insertions(+), 33 deletions(-) diff --git a/pcbnew/import_dxf/dxf2brd_items.cpp b/pcbnew/import_dxf/dxf2brd_items.cpp index fece4b618a..8efc051449 100644 --- a/pcbnew/import_dxf/dxf2brd_items.cpp +++ b/pcbnew/import_dxf/dxf2brd_items.cpp @@ -46,11 +46,17 @@ #include #include #include +#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() { m_xOffset = 0.0; // X coord offset for conversion (in mm) m_yOffset = 0.0; // Y coord offset for conversion (in mm) + m_DXF2mm = 1.0; // The scale factor to convert DXF units to mm m_version = 0; m_defaultThickness = 0.1; m_brdLayer = Dwgs_User; @@ -65,19 +71,19 @@ DXF2BRD_CONVERTER::~DXF2BRD_CONVERTER() // coordinate conversions from dxf to internal units int DXF2BRD_CONVERTER::mapX( double aDxfCoordX ) { - return Millimeter2iu( m_xOffset + ( aDxfCoordX * DXF_UNITS_PER_MM ) ); + return Millimeter2iu( m_xOffset + ( aDxfCoordX * m_DXF2mm ) ); } int DXF2BRD_CONVERTER::mapY( double aDxfCoordY ) { - return Millimeter2iu( m_yOffset - ( aDxfCoordY * DXF_UNITS_PER_MM ) ); + return Millimeter2iu( m_yOffset - ( aDxfCoordY * m_DXF2mm ) ); } int DXF2BRD_CONVERTER::mapDim( double aDxfValue ) { - return Millimeter2iu( aDxfValue * DXF_UNITS_PER_MM ); + return Millimeter2iu( aDxfValue * m_DXF2mm ); } @@ -175,8 +181,11 @@ void DXF2BRD_CONVERTER::addLWPolyline(const DRW_LWPolyline& aData ) // 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. - wxPoint lwpolyline_startpoint; - wxPoint segment_startpoint; + wxRealPoint seg_start; + wxRealPoint poly_start; + double bulge = 0.0; + int lineWidth = mapDim( aData.thickness == 0 ? m_defaultThickness + : aData.thickness ); for( unsigned ii = 0; ii < aData.vertlist.size(); ii++ ) { @@ -184,35 +193,31 @@ void DXF2BRD_CONVERTER::addLWPolyline(const DRW_LWPolyline& aData ) if( ii == 0 ) { - segment_startpoint.x = mapX( vertex->x ); - segment_startpoint.y = mapY( vertex->y ); - lwpolyline_startpoint = segment_startpoint; + 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; } - DRAWSEGMENT* segm = new DRAWSEGMENT( NULL ); + wxRealPoint seg_end( m_xOffset + vertex->x * m_DXF2mm, m_yOffset - vertex->y * m_DXF2mm ); - segm->SetLayer( ToLAYER_ID( m_brdLayer ) ); - segm->SetStart( segment_startpoint ); - wxPoint segment_endpoint( mapX( vertex->x ), mapY( vertex->y ) ); - segm->SetEnd( segment_endpoint ); - segm->SetWidth( mapDim( aData.thickness == 0 ? m_defaultThickness - : aData.thickness ) ); - m_newItemsList.push_back( segm ); - segment_startpoint = segment_endpoint; + 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 ) { - DRAWSEGMENT* closing_segm = new DRAWSEGMENT( NULL ); - - closing_segm->SetLayer( ToLAYER_ID( m_brdLayer ) ); - closing_segm->SetStart( segment_startpoint ); - closing_segm->SetEnd( lwpolyline_startpoint ); - closing_segm->SetWidth( mapDim( aData.thickness == 0 ? m_defaultThickness - : aData.thickness ) ); - m_newItemsList.push_back( closing_segm ); + if( std::abs( bulge ) < MIN_BULGE ) + insertLine( seg_start, poly_start, lineWidth ); + else + insertArc( seg_start, poly_start, bulge, lineWidth ); } } @@ -475,6 +480,7 @@ void DXF2BRD_CONVERTER::addMText( const DRW_MText& aData ) void DXF2BRD_CONVERTER::addHeader( const DRW_Header* data ) { std::map::const_iterator it; + m_DXF2mm = 1.0; // assume no scale factor for( it = data->vars.begin(); it != data->vars.end(); ++it ) { @@ -485,6 +491,71 @@ void DXF2BRD_CONVERTER::addHeader( const DRW_Header* data ) 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; + } + } } } @@ -632,3 +703,99 @@ void DXF2BRD_CONVERTER::addTextStyle( const DRW_Textstyle& aData ) { // TODO } + + +void DXF2BRD_CONVERTER::insertLine( const wxRealPoint& aSegStart, + const wxRealPoint& aSegEnd, int aWidth ) +{ + DRAWSEGMENT* segm = new DRAWSEGMENT( NULL ); + wxPoint segment_startpoint( Millimeter2iu( aSegStart.x ), Millimeter2iu( aSegStart.y ) ); + wxPoint segment_endpoint( Millimeter2iu( aSegEnd.x ), Millimeter2iu( aSegEnd.y ) ); + + segm->SetLayer( ToLAYER_ID( m_brdLayer ) ); + segm->SetStart( segment_startpoint ); + segm->SetEnd( segment_endpoint ); + segm->SetWidth( aWidth ); + + m_newItemsList.push_back( segm ); + return; +} + + +void DXF2BRD_CONVERTER::insertArc( const wxRealPoint& aSegStart, const wxRealPoint& aSegEnd, + double aBulge, int aWidth ) +{ + DRAWSEGMENT* segm = new DRAWSEGMENT( NULL ); + + wxPoint segment_startpoint( Millimeter2iu( aSegStart.x ), Millimeter2iu( aSegStart.y ) ); + wxPoint segment_endpoint( Millimeter2iu( aSegEnd.x ), Millimeter2iu( aSegEnd.y ) ); + + // ensure aBulge represents an angle from +/- ( 0 .. approx 359.8 deg ) + if( aBulge < -2000.0 ) + aBulge = -2000.0; + else if( aBulge > 2000.0 ) + aBulge = 2000.0; + + double ang = 4.0 * atan( aBulge ); + + // reflect the Y values to put everything in a RHCS + wxRealPoint sp( aSegStart.x, -aSegStart.y ); + wxRealPoint ep( aSegEnd.x, -aSegEnd.y ); + // angle from end->start + double offAng = atan2( ep.y - sp.y, ep.x - sp.x ); + // length of subtended segment = 1/2 distance between the 2 points + double d = 0.5 * sqrt( (sp.x - ep.x) * (sp.x - ep.x) + (sp.y - ep.y) * (sp.y - ep.y) ); + // midpoint of the subtended segment + double xm = ( sp.x + ep.x ) * 0.5; + double ym = ( sp.y + ep.y ) * 0.5; + double radius = d / sin( ang * 0.5 ); + + if( radius < 0.0 ) + radius = -radius; + + // calculate the height of the triangle with base d and hypotenuse r + double dh2 = radius * radius - d * d; + + // this should only ever happen due to rounding errors when r == d + if( dh2 < 0.0 ) + dh2 = 0.0; + + double h = sqrt( dh2 ); + + if( ang < 0.0 ) + offAng -= M_PI_2; + else + offAng += M_PI_2; + + // for angles greater than 180 deg we need to flip the + // direction in which the arc center is found relative + // to the midpoint of the subtended segment. + if( ang < -M_PI ) + offAng += M_PI; + else if( ang > M_PI ) + offAng -= M_PI; + + // center point + double cx = h * cos( offAng ) + xm; + double cy = h * sin( offAng ) + ym; + + segm->SetLayer( ToLAYER_ID( m_brdLayer ) ); + segm->SetShape( S_ARC ); + segm->SetCenter( wxPoint( Millimeter2iu( cx ), Millimeter2iu( -cy ) ) ); + + if( ang < 0.0 ) + { + segm->SetArcStart( wxPoint( Millimeter2iu( ep.x ), Millimeter2iu( -ep.y ) ) ); + segm->SetAngle( RAD2DECIDEG( ang ) ); + } + else + { + segm->SetArcStart( wxPoint( Millimeter2iu( sp.x ), Millimeter2iu( -sp.y ) ) ); + segm->SetAngle( RAD2DECIDEG( -ang ) ); + } + + segm->SetWidth( aWidth ); + + m_newItemsList.push_back( segm ); + return; +} diff --git a/pcbnew/import_dxf/dxf2brd_items.h b/pcbnew/import_dxf/dxf2brd_items.h index 88285c7507..7951f39900 100644 --- a/pcbnew/import_dxf/dxf2brd_items.h +++ b/pcbnew/import_dxf/dxf2brd_items.h @@ -33,13 +33,6 @@ class BOARD; class BOARD_ITEM; -/** - * Conversion factor for DXF units to millimeters - * It seems DRW_Interface always converts DXF coordinates in mm - * (to be confirmed) - */ -static const double DXF_UNITS_PER_MM = 1.0; - /** * This format filter class can import and export DXF files. * It depends on the dxflib library. @@ -53,7 +46,8 @@ private: double m_xOffset; // X coord offset for conversion (in mm) double m_yOffset; // Y coord offset for conversion (in mm) double m_defaultThickness; // default line thickness for conversion (in mm) - int m_brdLayer; // The board layer to place imported dfx items + double m_DXF2mm; // The scale factor to convert DXF units to mm + int m_brdLayer; // The board layer to place imported DXF items int m_version; // the dxf version, not used here std::string m_codePage; // The code page, not used here @@ -103,6 +97,11 @@ private: int mapY( double aDxfCoordY ); int mapDim( double aDxfValue ); + // Functions to aid in the creation of a LWPolyline + void insertLine( const wxRealPoint& aSegStart, const wxRealPoint& aSegEnd, int aWidth ); + void insertArc( const wxRealPoint& aSegStart, const wxRealPoint& aSegEnd, + double aBulge, int aWidth ); + // Methods from DRW_CreationInterface: // They are "call back" fonctions, called when the corresponding object // is read in dxf file