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_drawsegment.h>
|
||||||
#include <class_pcb_text.h>
|
#include <class_pcb_text.h>
|
||||||
#include <convert_from_iu.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()
|
DXF2BRD_CONVERTER::DXF2BRD_CONVERTER() : DRW_Interface()
|
||||||
{
|
{
|
||||||
m_xOffset = 0.0; // X coord offset for conversion (in mm)
|
m_xOffset = 0.0; // X coord offset for conversion (in mm)
|
||||||
m_yOffset = 0.0; // Y 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_version = 0;
|
||||||
m_defaultThickness = 0.1;
|
m_defaultThickness = 0.1;
|
||||||
m_brdLayer = Dwgs_User;
|
m_brdLayer = Dwgs_User;
|
||||||
|
@ -65,19 +71,19 @@ DXF2BRD_CONVERTER::~DXF2BRD_CONVERTER()
|
||||||
// coordinate conversions from dxf to internal units
|
// coordinate conversions from dxf to internal units
|
||||||
int DXF2BRD_CONVERTER::mapX( double aDxfCoordX )
|
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 )
|
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 )
|
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
|
// The import is a simplified import: the width of segment is
|
||||||
// (obviously constant and is the width of the DRW_LWPolyline.
|
// (obviously constant and is the width of the DRW_LWPolyline.
|
||||||
// the variable width of each vertex (when exists) is not used.
|
// the variable width of each vertex (when exists) is not used.
|
||||||
wxPoint lwpolyline_startpoint;
|
wxRealPoint seg_start;
|
||||||
wxPoint segment_startpoint;
|
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++ )
|
for( unsigned ii = 0; ii < aData.vertlist.size(); ii++ )
|
||||||
{
|
{
|
||||||
|
@ -184,35 +193,31 @@ void DXF2BRD_CONVERTER::addLWPolyline(const DRW_LWPolyline& aData )
|
||||||
|
|
||||||
if( ii == 0 )
|
if( ii == 0 )
|
||||||
{
|
{
|
||||||
segment_startpoint.x = mapX( vertex->x );
|
seg_start.x = m_xOffset + vertex->x * m_DXF2mm;
|
||||||
segment_startpoint.y = mapY( vertex->y );
|
seg_start.y = m_yOffset - vertex->y * m_DXF2mm;
|
||||||
lwpolyline_startpoint = segment_startpoint;
|
bulge = vertex->bulge;
|
||||||
|
poly_start = seg_start;
|
||||||
continue;
|
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 ) );
|
if( std::abs( bulge ) < MIN_BULGE )
|
||||||
segm->SetStart( segment_startpoint );
|
insertLine( seg_start, seg_end, lineWidth );
|
||||||
wxPoint segment_endpoint( mapX( vertex->x ), mapY( vertex->y ) );
|
else
|
||||||
segm->SetEnd( segment_endpoint );
|
insertArc( seg_start, seg_end, bulge, lineWidth );
|
||||||
segm->SetWidth( mapDim( aData.thickness == 0 ? m_defaultThickness
|
|
||||||
: aData.thickness ) );
|
bulge = vertex->bulge;
|
||||||
m_newItemsList.push_back( segm );
|
seg_start = seg_end;
|
||||||
segment_startpoint = segment_endpoint;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// LWPolyline flags bit 0 indicates closed (1) or open (0) polyline
|
// LWPolyline flags bit 0 indicates closed (1) or open (0) polyline
|
||||||
if( aData.flags & 1 )
|
if( aData.flags & 1 )
|
||||||
{
|
{
|
||||||
DRAWSEGMENT* closing_segm = new DRAWSEGMENT( NULL );
|
if( std::abs( bulge ) < MIN_BULGE )
|
||||||
|
insertLine( seg_start, poly_start, lineWidth );
|
||||||
closing_segm->SetLayer( ToLAYER_ID( m_brdLayer ) );
|
else
|
||||||
closing_segm->SetStart( segment_startpoint );
|
insertArc( seg_start, poly_start, bulge, lineWidth );
|
||||||
closing_segm->SetEnd( lwpolyline_startpoint );
|
|
||||||
closing_segm->SetWidth( mapDim( aData.thickness == 0 ? m_defaultThickness
|
|
||||||
: aData.thickness ) );
|
|
||||||
m_newItemsList.push_back( closing_segm );
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -475,6 +480,7 @@ void DXF2BRD_CONVERTER::addMText( const DRW_MText& aData )
|
||||||
void DXF2BRD_CONVERTER::addHeader( const DRW_Header* data )
|
void DXF2BRD_CONVERTER::addHeader( const DRW_Header* data )
|
||||||
{
|
{
|
||||||
std::map<std::string, DRW_Variant*>::const_iterator it;
|
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 )
|
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;
|
DRW_Variant* var = (*it).second;
|
||||||
m_codePage = ( *var->content.s );
|
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
|
// 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;
|
||||||
class BOARD_ITEM;
|
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.
|
* This format filter class can import and export DXF files.
|
||||||
* It depends on the dxflib library.
|
* It depends on the dxflib library.
|
||||||
|
@ -53,7 +46,8 @@ private:
|
||||||
double m_xOffset; // X coord offset for conversion (in mm)
|
double m_xOffset; // X coord offset for conversion (in mm)
|
||||||
double m_yOffset; // Y 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)
|
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
|
int m_version; // the dxf version, not used here
|
||||||
std::string m_codePage; // The code page, not used here
|
std::string m_codePage; // The code page, not used here
|
||||||
|
|
||||||
|
@ -103,6 +97,11 @@ private:
|
||||||
int mapY( double aDxfCoordY );
|
int mapY( double aDxfCoordY );
|
||||||
int mapDim( double aDxfValue );
|
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:
|
// Methods from DRW_CreationInterface:
|
||||||
// They are "call back" fonctions, called when the corresponding object
|
// They are "call back" fonctions, called when the corresponding object
|
||||||
// is read in dxf file
|
// is read in dxf file
|
||||||
|
|
Loading…
Reference in New Issue