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.
This commit is contained in:
Cirilo Bernardo 2016-03-20 21:33:53 -04:00 committed by Chris Pavlina
parent 30f113c2de
commit a4ba01f42d
2 changed files with 199 additions and 33 deletions

View File

@ -46,11 +46,17 @@
#include <class_drawsegment.h>
#include <class_pcb_text.h>
#include <convert_from_iu.h>
#include <drw_base.h>
// 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<std::string, DRW_Variant*>::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;
}

View File

@ -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