diff options
Diffstat (limited to 'pcbnew/import_dxf/dxf2brd_items.cpp')
-rw-r--r-- | pcbnew/import_dxf/dxf2brd_items.cpp | 824 |
1 files changed, 824 insertions, 0 deletions
diff --git a/pcbnew/import_dxf/dxf2brd_items.cpp b/pcbnew/import_dxf/dxf2brd_items.cpp new file mode 100644 index 0000000..a48f591 --- /dev/null +++ b/pcbnew/import_dxf/dxf2brd_items.cpp @@ -0,0 +1,824 @@ +/* + * 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. + * + * 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 + */ + +// The DXF reader lib (libdxfrw) comes from LibreCAD project, a 2D CAD program +// libdxfrw can be found on http://sourceforge.net/projects/libdxfrw/ +// or (latest sources) on +// https://github.com/LibreCAD/LibreCAD/tree/master/libraries/libdxfrw/src +// +// There is no doc to use it, but have a look to +// https://github.com/LibreCAD/LibreCAD/blob/master/librecad/src/lib/filters/rs_filterdxf.cpp +// and https://github.com/LibreCAD/LibreCAD/blob/master/librecad/src/lib/filters/rs_filterdxf.h +// Each time a dxf entity is read, a "call back" fuction is called +// like void DXF2BRD_CONVERTER::addLine( const DRW_Line& data ) when a line is read. +// this function just add the BOARD entity from dxf parameters (start and end point ...) + + +#include "libdxfrw.h" +#include "dxf2brd_items.h" +#include <wx/arrstr.h> +#include <wx/regex.h> + +#include <trigo.h> +#include <macros.h> +#include <class_board.h> +#include <class_drawsegment.h> +#include <class_edge_mod.h> +#include <class_pcb_text.h> +#include <convert_from_iu.h> +#include <class_text_mod.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; +} + + +DXF2BRD_CONVERTER::~DXF2BRD_CONVERTER() +{ +} + + +// coordinate conversions from dxf to internal units +int DXF2BRD_CONVERTER::mapX( double aDxfCoordX ) +{ + return Millimeter2iu( m_xOffset + ( aDxfCoordX * m_DXF2mm ) ); +} + + +int DXF2BRD_CONVERTER::mapY( double aDxfCoordY ) +{ + return Millimeter2iu( m_yOffset - ( aDxfCoordY * m_DXF2mm ) ); +} + + +int DXF2BRD_CONVERTER::mapDim( double aDxfValue ) +{ + return Millimeter2iu( aDxfValue * m_DXF2mm ); +} + + +bool DXF2BRD_CONVERTER::ImportDxfFile( const wxString& aFile ) +{ + dxfRW* dxf = new dxfRW( aFile.ToUTF8() ); + bool success = dxf->read( this, true ); + + delete dxf; + + return success; +} + + +void DXF2BRD_CONVERTER::addLayer( const DRW_Layer& aData ) +{ + // Not yet useful in Pcbnew. +#if 0 + wxString name = wxString::FromUTF8( aData.name.c_str() ); + wxLogMessage( name ); +#endif +} + + +void DXF2BRD_CONVERTER::addLine( const DRW_Line& aData ) +{ + DRAWSEGMENT* segm = ( m_useModuleItems ) ? + 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 ) ); + segm->SetStart( start ); + wxPoint end( mapX( aData.secPoint.x ), mapY( aData.secPoint.y ) ); + segm->SetEnd( end ); + segm->SetWidth( mapDim( aData.thickness == 0 ? m_defaultThickness / m_DXF2mm + : aData.thickness ) ); + m_newItemsList.push_back( segm ); +} + +void DXF2BRD_CONVERTER::addPolyline(const DRW_Polyline& aData ) +{ + // Currently, Pcbnew does not know polylines, for boards. + // So we have to convert a polyline to a set of segments. + // Obviously, the z coordinate is ignored + + wxPoint polyline_startpoint; + wxPoint segment_startpoint; + + for( unsigned ii = 0; ii < aData.vertlist.size(); ii++ ) + { + DRW_Vertex* vertex = aData.vertlist[ii]; + + if( ii == 0 ) + { + segment_startpoint.x = mapX( vertex->basePoint.x ); + segment_startpoint.y = mapY( vertex->basePoint.y ); + polyline_startpoint = segment_startpoint; + continue; + } + + DRAWSEGMENT* segm = ( m_useModuleItems ) ? + static_cast< DRAWSEGMENT* >( new EDGE_MODULE( NULL ) ) : + new DRAWSEGMENT; + + segm->SetLayer( ToLAYER_ID( m_brdLayer ) ); + segm->SetStart( segment_startpoint ); + wxPoint segment_endpoint( mapX( vertex->basePoint.x ), mapY( vertex->basePoint.y ) ); + segm->SetEnd( segment_endpoint ); + segm->SetWidth( mapDim( aData.thickness == 0 ? m_defaultThickness / m_DXF2mm + : aData.thickness ) ); + m_newItemsList.push_back( segm ); + segment_startpoint = segment_endpoint; + } + + // Polyline flags bit 0 indicates closed (1) or open (0) polyline + if( aData.flags & 1 ) + { + DRAWSEGMENT* closing_segm = ( m_useModuleItems ) ? + static_cast< DRAWSEGMENT* >( new EDGE_MODULE( NULL ) ) : + new DRAWSEGMENT; + + closing_segm->SetLayer( ToLAYER_ID( m_brdLayer ) ); + closing_segm->SetStart( segment_startpoint ); + closing_segm->SetEnd( polyline_startpoint ); + closing_segm->SetWidth( mapDim( aData.thickness == 0 ? m_defaultThickness / m_DXF2mm + : aData.thickness ) ); + m_newItemsList.push_back( closing_segm ); + } +} + + +void DXF2BRD_CONVERTER::addLWPolyline(const DRW_LWPolyline& 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 = mapDim( aData.thickness == 0 ? m_defaultThickness / m_DXF2mm + : aData.thickness ); + + for( unsigned ii = 0; ii < aData.vertlist.size(); ii++ ) + { + 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; + } + + // 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 ); + } +} + + +void DXF2BRD_CONVERTER::addCircle( const DRW_Circle& aData ) +{ + DRAWSEGMENT* segm = ( m_useModuleItems ) ? + 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 ) ); + segm->SetCenter( center ); + wxPoint circle_start( mapX( aData.basePoint.x + aData.radious ), mapY( aData.basePoint.y ) ); + segm->SetArcStart( circle_start ); + segm->SetWidth( mapDim( aData.thickness == 0 ? m_defaultThickness / m_DXF2mm + : aData.thickness ) ); + m_newItemsList.push_back( segm ); +} + + +/* + * Import Arc entities. + */ +void DXF2BRD_CONVERTER::addArc( const DRW_Arc& data ) +{ + DRAWSEGMENT* segm = ( m_useModuleItems ) ? + static_cast< DRAWSEGMENT* >( new EDGE_MODULE( NULL ) ) : new DRAWSEGMENT; + + segm->SetLayer( ToLAYER_ID( m_brdLayer ) ); + segm->SetShape( S_ARC ); + + // Init arc centre: + wxPoint center( mapX( data.basePoint.x ), mapY( data.basePoint.y ) ); + segm->SetCenter( center ); + + // Init arc start point + double arcStartx = data.radious; + double arcStarty = 0; + double startangle = data.staangle; + double endangle = data.endangle; + + RotatePoint( &arcStartx, &arcStarty, -RAD2DECIDEG( startangle ) ); + wxPoint arcStart( mapX( arcStartx + data.basePoint.x ), + mapY( arcStarty + data.basePoint.y ) ); + segm->SetArcStart( arcStart ); + + // calculate arc angle (arcs are CCW, and should be < 0 in Pcbnew) + double angle = -RAD2DECIDEG( endangle - startangle ); + + if( angle > 0.0 ) + angle -= 3600.0; + + segm->SetAngle( angle ); + + segm->SetWidth( mapDim( data.thickness == 0 ? m_defaultThickness / m_DXF2mm + : data.thickness ) ); + m_newItemsList.push_back( segm ); +} + + +void DXF2BRD_CONVERTER::addText( const DRW_Text& aData ) +{ + BOARD_ITEM* brdItem; + EDA_TEXT* textItem; + + if( m_useModuleItems ) + { + TEXTE_MODULE* modText = new TEXTE_MODULE( NULL ); + brdItem = static_cast< BOARD_ITEM* >( modText ); + textItem = static_cast< EDA_TEXT* >( modText ); + } + else + { + TEXTE_PCB* pcbText = new TEXTE_PCB( NULL ); + brdItem = static_cast< BOARD_ITEM* >( pcbText ); + textItem = static_cast< EDA_TEXT* >( pcbText ); + } + + 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 ) ); + + if( aData.alignV != 0 || aData.alignH != 0 || aData.alignH == DRW_Text::HMiddle ) + { + if( aData.alignH != DRW_Text::HAligned && aData.alignH != DRW_Text::HFit ) + { + wxPoint tmp = secPoint; + secPoint = refPoint; + refPoint = tmp; + } + } + + switch( aData.alignV ) + { + case DRW_Text::VBaseLine: + textItem->SetVertJustify( GR_TEXT_VJUSTIFY_BOTTOM ); + break; + + case DRW_Text::VBottom: + textItem->SetVertJustify( GR_TEXT_VJUSTIFY_BOTTOM ); + break; + + case DRW_Text::VMiddle: + textItem->SetVertJustify( GR_TEXT_VJUSTIFY_CENTER ); + break; + + case DRW_Text::VTop: + textItem->SetVertJustify( GR_TEXT_VJUSTIFY_TOP ); + break; + } + + switch( aData.alignH ) + { + case DRW_Text::HLeft: + textItem->SetHorizJustify( GR_TEXT_HJUSTIFY_LEFT ); + break; + + case DRW_Text::HCenter: + textItem->SetHorizJustify( GR_TEXT_HJUSTIFY_CENTER ); + break; + + case DRW_Text::HRight: + textItem->SetHorizJustify( GR_TEXT_HJUSTIFY_RIGHT ); + break; + + case DRW_Text::HAligned: + // no equivalent options in text pcb. + textItem->SetHorizJustify( GR_TEXT_HJUSTIFY_LEFT ); + break; + + case DRW_Text::HMiddle: + // no equivalent options in text pcb. + textItem->SetHorizJustify( GR_TEXT_HJUSTIFY_CENTER ); + break; + + case DRW_Text::HFit: + // no equivalent options in text pcb. + textItem->SetHorizJustify( GR_TEXT_HJUSTIFY_LEFT ); + break; + } + +#if 0 + wxString sty = wxString::FromUTF8( aData.style.c_str() ); + sty = sty.ToLower(); + + if( aData.textgen == 2 ) + { + // Text dir = left to right; + } else if( aData.textgen == 4 ) + { + // Text dir = top to bottom; + } else + { + } +#endif + + wxString text = toNativeString( wxString::FromUTF8( aData.text.c_str() ) ); + + textItem->SetTextPosition( refPoint ); + textItem->SetOrientation( aData.angle * 10 ); + + // The 0.8 factor gives a better height/width ratio with our font + textItem->SetWidth( mapDim( aData.height * 0.8 ) ); + textItem->SetHeight( mapDim( aData.height ) ); + textItem->SetThickness( mapDim( aData.thickness == 0 ? m_defaultThickness / m_DXF2mm + : aData.thickness ) ); + textItem->SetText( text ); + + m_newItemsList.push_back( static_cast< BOARD_ITEM* >( brdItem ) ); +} + + +void DXF2BRD_CONVERTER::addMText( const DRW_MText& aData ) +{ + wxString text = toNativeString( wxString::FromUTF8( aData.text.c_str() ) ); + wxString attrib, tmp; + + /* Some texts start by '\' and have formating chars (font name, font option...) + * ending with ';' + * Here are some mtext formatting codes: + * Format code Purpose + * \0...\o Turns overline on and off + * \L...\l Turns underline on and off + * \~ Inserts a nonbreaking space + \\ Inserts a backslash + \\\{...\} Inserts an opening and closing brace + \\ \File name; Changes to the specified font file + \\ \Hvalue; Changes to the text height specified in drawing units + \\ \Hvaluex; Changes the text height to a multiple of the current text height + \\ \S...^...; Stacks the subsequent text at the \, #, or ^ symbol + \\ \Tvalue; Adjusts the space between characters, from.75 to 4 times + \\ \Qangle; Changes obliquing angle + \\ \Wvalue; Changes width factor to produce wide text + \\ \A Sets the alignment value; valid values: 0, 1, 2 (bottom, center, top) while( text.StartsWith( wxT("\\") ) ) + */ + while( text.StartsWith( wxT( "\\" ) ) ) + { + attrib << text.BeforeFirst( ';' ); + tmp = text.AfterFirst( ';' ); + text = tmp; + } + + BOARD_ITEM* brdItem; + EDA_TEXT* textItem; + + if( m_useModuleItems ) + { + TEXTE_MODULE* modText = new TEXTE_MODULE( NULL ); + brdItem = static_cast< BOARD_ITEM* >( modText ); + textItem = static_cast< EDA_TEXT* >( modText ); + } + else + { + TEXTE_PCB* pcbText = new TEXTE_PCB( NULL ); + brdItem = static_cast< BOARD_ITEM* >( pcbText ); + textItem = static_cast< EDA_TEXT* >( pcbText ); + } + + brdItem->SetLayer( ToLAYER_ID( m_brdLayer ) ); + wxPoint textpos( mapX( aData.basePoint.x ), mapY( aData.basePoint.y ) ); + + textItem->SetTextPosition( textpos ); + textItem->SetOrientation( aData.angle * 10 ); + + // The 0.8 factor gives a better height/width ratio with our font + textItem->SetWidth( mapDim( aData.height * 0.8 ) ); + textItem->SetHeight( mapDim( aData.height ) ); + textItem->SetThickness( mapDim( aData.thickness == 0 ? m_defaultThickness / m_DXF2mm + : aData.thickness ) ); + textItem->SetText( text ); + + // Initialize text justifications: + if( aData.textgen <= 3 ) + { + textItem->SetVertJustify( GR_TEXT_VJUSTIFY_TOP ); + } + else if( aData.textgen <= 6 ) + { + textItem->SetVertJustify( GR_TEXT_VJUSTIFY_CENTER ); + } + else + { + textItem->SetVertJustify( GR_TEXT_VJUSTIFY_BOTTOM ); + } + + if( aData.textgen % 3 == 1 ) + { + textItem->SetHorizJustify( GR_TEXT_HJUSTIFY_LEFT ); + } + else if( aData.textgen % 3 == 2 ) + { + textItem->SetHorizJustify( GR_TEXT_HJUSTIFY_CENTER ); + } + else + { + textItem->SetHorizJustify( GR_TEXT_HJUSTIFY_RIGHT ); + } + +#if 0 // These setting have no mening in Pcbnew + if( data.alignH == 1 ) + { + // Text is left to right; + } + else if( data.alignH == 3 ) + { + // Text is top to bottom; + } + else + { + // use ByStyle; + } + + if( aData.alignV == 1 ) + { + // use AtLeast; + } + else + { + // useExact; + } +#endif + + m_newItemsList.push_back( static_cast< BOARD_ITEM* >( brdItem ) ); +} + + +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 ) + { + 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; + } + } + } +} + + +wxString DXF2BRD_CONVERTER::toDxfString( const wxString& aStr ) +{ + wxString res; + int j = 0; + + for( unsigned i = 0; i<aStr.length(); ++i ) + { + int c = aStr[i]; + + if( c > 175 || c < 11 ) + { + res.append( aStr.Mid( j, i - j ) ); + j = i; + + switch( c ) + { + case 0x0A: + res += wxT( "\\P" ); + break; + + // diameter: +#ifdef __WINDOWS_ + // windows, as always, is special. + case 0x00D8: +#else + case 0x2205: +#endif + res += wxT( "%%C" ); + break; + + // degree: + case 0x00B0: + res += wxT( "%%D" ); + break; + + // plus/minus + case 0x00B1: + res += wxT( "%%P" ); + break; + + default: + j--; + break; + } + + j++; + } + } + + res.append( aStr.Mid( j ) ); + return res; +} + + +wxString DXF2BRD_CONVERTER::toNativeString( const wxString& aData ) +{ + wxString res; + + // Ignore font tags: + int j = 0; + + for( unsigned i = 0; i < aData.length(); ++i ) + { + if( aData[ i ] == 0x7B ) // is '{' ? + { + if( aData[ i + 1 ] == 0x5c && aData[ i + 2 ] == 0x66 ) // is "\f" ? + { + // found font tag, append parsed part + res.append( aData.Mid( j, i - j ) ); + + // skip to ';' + for( unsigned k = i + 3; k < aData.length(); ++k ) + { + if( aData[ k ] == 0x3B ) + { + i = j = ++k; + break; + } + } + + // add to '}' + for( unsigned k = i; k < aData.length(); ++k ) + { + if( aData[ k ] == 0x7D ) + { + res.append( aData.Mid( i, k - i ) ); + i = j = ++k; + break; + } + } + } + } + } + + res.append( aData.Mid( j ) ); + +#if 1 + wxRegEx regexp; + // Line feed: + regexp.Compile( wxT( "\\\\P" ) ); + regexp.Replace( &res, wxT( "\n" ) ); + + // Space: + regexp.Compile( wxT( "\\\\~" ) ); + regexp.Replace( &res, wxT( " " ) ); + + // diameter: + regexp.Compile( wxT( "%%[cC]" ) ); +#ifdef __WINDOWS__ + // windows, as always, is special. + regexp.Replace( &res, wxChar( 0xD8 ) ); +#else + // Empty_set, diameter is 0x2300 + regexp.Replace( &res, wxChar( 0x2205 ) ); +#endif + + // degree: + regexp.Compile( wxT( "%%[dD]" ) ); + regexp.Replace( &res, wxChar( 0x00B0 ) ); + // plus/minus + regexp.Compile( wxT( "%%[pP]" ) ); + regexp.Replace( &res, wxChar( 0x00B1 ) ); +#endif + + return res; +} + + +void DXF2BRD_CONVERTER::addTextStyle( const DRW_Textstyle& aData ) +{ + // TODO +} + + +void DXF2BRD_CONVERTER::insertLine( const wxRealPoint& aSegStart, + const wxRealPoint& aSegEnd, int aWidth ) +{ + DRAWSEGMENT* segm = ( m_useModuleItems ) ? + static_cast< DRAWSEGMENT* >( new EDGE_MODULE( NULL ) ) : new DRAWSEGMENT; + 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 = ( m_useModuleItems ) ? + static_cast< DRAWSEGMENT* >( new EDGE_MODULE( NULL ) ) : new DRAWSEGMENT; + + 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; +} |