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:
parent
30f113c2de
commit
a4ba01f42d
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue