kicad-source/common/import_gfx/dxf_import_plugin.cpp
Seth Hillbrand a724b3d8b1 Handle DXF files with out of bounds values
First, ensure that we calculate the bbox of the imported elements.

If the total BBOX is outside our allowed value, refused to import.  This
needs to be scaled or addressed in an external editor

If the bbox fits, then make sure that we clamp it such that the largest
element is still within our bounds

Fixes https://gitlab.com/kicad/code/kicad/-/issues/18523
2024-08-14 13:06:18 -07:00

1621 lines
50 KiB
C++

/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2019 Jean-Pierre Charras, jp.charras at wanadoo.fr
* Copyright (C) 2019-2024 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 dxflib project used in QCAD
// See http://www.ribbonsoft.com
// Each time a dxf entity is read, a "call back" function is called
// like void DXF_IMPORT_PLUGIN::addLine( const DL_LineData& data ) when a line is read.
// this function just add the BOARD entity from dxf parameters (start and end point ...)
#include "dxf_import_plugin.h"
#include <wx/arrstr.h>
#include <wx/regex.h>
#include <geometry/ellipse.h>
#include <bezier_curves.h>
#include <trigo.h>
#include <macros.h>
#include <cmath> // isnan
#include <board.h>
#include "common.h"
/*
* Important notes
* 1. All output coordinates of this importer are in mm
* 2. DXFs have a concept of world (WCS) and object coordinates (OCS)
3. The following objects are world coordinates:
- Line
- Point
- Polyline (3D)
- Vertex (3D)
- Polymesh
- Polyface
- Viewport
4. The following entities are object coordinates
- Circle
- Arc
- Solid
- Trace
- Attrib
- Shape
- Insert
- Polyline (2D)
- Vertex (2D)
- LWPolyline
- Hatch
- Image
- Text
* 5. Object coordinates must be run through the arbitrary axis
* translation even though they are 2D drawings and most of the time
* the import is fine. Sometimes, against all logic, CAD tools like
* SolidWorks may randomly insert circles "mirror" that must be unflipped
* by following the object to world conversion
* 6. Blocks are virtual groups, blocks must be placed by a INSERT entity
* 7. Blocks may be repeated multiple times
* 8. There is no sane way to make text look perfect like the original CAD.
* DXF simply does mpt secifying text/font enough to make it portable.
* We however make do try to get it somewhat close/visually appealing.
* 9. We silently drop the z coordinate on 3d polylines
*/
// 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
#define SCALE_FACTOR(x) (x)
DXF_IMPORT_PLUGIN::DXF_IMPORT_PLUGIN() : DL_CreationAdapter()
{
m_xOffset = 0.0; // X coord offset for conversion (in mm)
m_yOffset = 0.0; // Y coord offset for conversion (in mm)
m_version = 0; // the dxf version, not yet used
m_defaultThickness = 0.2; // default thickness (in mm)
m_brdLayer = Dwgs_User; // The default import layer
m_importAsFPShapes = true;
m_minX = m_minY = std::numeric_limits<double>::max();
m_maxX = m_maxY = std::numeric_limits<double>::min();
m_currentUnit = DXF_IMPORT_UNITS::DEFAULT;
m_importCoordinatePrecision = 4; // initial value per dxf spec
m_importAnglePrecision = 0; // initial value per dxf spec
// placeholder layer so we can fallback to something later
auto layer0 = std::make_unique<DXF_IMPORT_LAYER>( "", DXF_IMPORT_LINEWEIGHT_BY_LW_DEFAULT );
m_layers.push_back( std::move( layer0 ) );
m_currentBlock = nullptr;
}
DXF_IMPORT_PLUGIN::~DXF_IMPORT_PLUGIN()
{
}
bool DXF_IMPORT_PLUGIN::Load( const wxString& aFileName )
{
try
{
return ImportDxfFile( aFileName );
}
catch( const std::bad_alloc& )
{
m_layers.clear();
m_blocks.clear();
m_styles.clear();
m_internalImporter.ClearShapes();
ReportMsg( _( "Memory was exhausted trying to load the DXF, it may be too large." ) );
return false;
}
}
bool DXF_IMPORT_PLUGIN::LoadFromMemory( const wxMemoryBuffer& aMemBuffer )
{
try
{
return ImportDxfFile( aMemBuffer );
}
catch( const std::bad_alloc& )
{
m_layers.clear();
m_blocks.clear();
m_styles.clear();
m_internalImporter.ClearShapes();
ReportMsg( _( "Memory was exhausted trying to load the DXF, it may be too large." ) );
return false;
}
}
bool DXF_IMPORT_PLUGIN::Import()
{
wxCHECK( m_importer, false );
m_internalImporter.ImportTo( *m_importer );
return true;
}
double DXF_IMPORT_PLUGIN::GetImageWidth() const
{
return m_maxX - m_minX;
}
double DXF_IMPORT_PLUGIN::GetImageHeight() const
{
return m_maxY - m_minY;
}
BOX2D DXF_IMPORT_PLUGIN::GetImageBBox() const
{
BOX2D bbox;
bbox.SetOrigin( m_minX, m_minY );
bbox.SetEnd( m_maxX, m_maxY );
return bbox;
}
void DXF_IMPORT_PLUGIN::SetImporter( GRAPHICS_IMPORTER* aImporter )
{
GRAPHICS_IMPORT_PLUGIN::SetImporter( aImporter );
if( m_importer )
SetDefaultLineWidthMM( m_importer->GetLineWidthMM() );
}
double DXF_IMPORT_PLUGIN::mapX( double aDxfCoordX )
{
return SCALE_FACTOR( m_xOffset + ( aDxfCoordX * getCurrentUnitScale() ) );
}
double DXF_IMPORT_PLUGIN::mapY( double aDxfCoordY )
{
return SCALE_FACTOR( m_yOffset - ( aDxfCoordY * getCurrentUnitScale() ) );
}
double DXF_IMPORT_PLUGIN::mapDim( double aDxfValue )
{
return SCALE_FACTOR( aDxfValue * getCurrentUnitScale() );
}
bool DXF_IMPORT_PLUGIN::ImportDxfFile( const wxString& aFile )
{
DL_Dxf dxf_reader;
// wxFopen takes care of unicode filenames across platforms
FILE* fp = wxFopen( aFile, wxT( "rt" ) );
if( fp == nullptr )
return false;
// Note the dxf reader takes care of switching to "C" locale before reading the file
// and will close the file after reading
bool success = dxf_reader.in( fp, this );
return success;
}
bool DXF_IMPORT_PLUGIN::ImportDxfFile( const wxMemoryBuffer& aMemBuffer )
{
DL_Dxf dxf_reader;
std::string str( reinterpret_cast<char*>( aMemBuffer.GetData() ), aMemBuffer.GetDataLen() );
// Note the dxf reader takes care of switching to "C" locale before reading the file
// and will close the file after reading
bool success = dxf_reader.in( str, this );
return success;
}
void DXF_IMPORT_PLUGIN::ReportMsg( const wxString& aMessage )
{
// Add message to keep trace of not handled dxf entities
m_messages += aMessage;
m_messages += '\n';
}
void DXF_IMPORT_PLUGIN::addSpline( const DL_SplineData& aData )
{
// Called when starting reading a spline
m_curr_entity.Clear();
m_curr_entity.m_EntityParseStatus = 1;
m_curr_entity.m_EntityFlag = aData.flags;
m_curr_entity.m_EntityType = DL_ENTITY_SPLINE;
m_curr_entity.m_SplineDegree = aData.degree;
m_curr_entity.m_SplineTangentStartX = aData.tangentStartX;
m_curr_entity.m_SplineTangentStartY = aData.tangentStartY;
m_curr_entity.m_SplineTangentEndX = aData.tangentEndX;
m_curr_entity.m_SplineTangentEndY = aData.tangentEndY;
m_curr_entity.m_SplineKnotsCount = aData.nKnots;
m_curr_entity.m_SplineControlCount = aData.nControl;
m_curr_entity.m_SplineFitCount = aData.nFit;
}
void DXF_IMPORT_PLUGIN::addControlPoint( const DL_ControlPointData& aData )
{
// Called for every spline control point, when reading a spline entity
m_curr_entity.m_SplineControlPointList.emplace_back( aData.x , aData.y, aData.w );
}
void DXF_IMPORT_PLUGIN::addFitPoint( const DL_FitPointData& aData )
{
// Called for every spline fit point, when reading a spline entity
// we store only the X,Y coord values in a VECTOR2D
m_curr_entity.m_SplineFitPointList.emplace_back( aData.x, aData.y );
}
void DXF_IMPORT_PLUGIN::addKnot( const DL_KnotData& aData)
{
// Called for every spline knot value, when reading a spline entity
m_curr_entity.m_SplineKnotsList.push_back( aData.k );
}
void DXF_IMPORT_PLUGIN::addLayer( const DL_LayerData& aData )
{
wxString name = wxString::FromUTF8( aData.name.c_str() );
int lw = attributes.getWidth();
if( lw == DXF_IMPORT_LINEWEIGHT_BY_LAYER )
lw = DXF_IMPORT_LINEWEIGHT_BY_LW_DEFAULT;
std::unique_ptr<DXF_IMPORT_LAYER> layer = std::make_unique<DXF_IMPORT_LAYER>( name, lw );
m_layers.push_back( std::move( layer ) );
}
void DXF_IMPORT_PLUGIN::addLinetype( const DL_LinetypeData& data )
{
#if 0
wxString name = From_UTF8( data.name.c_str() );
wxString description = From_UTF8( data.description.c_str() );
#endif
}
double DXF_IMPORT_PLUGIN::lineWeightToWidth( int lw, DXF_IMPORT_LAYER* aLayer )
{
if( lw == DXF_IMPORT_LINEWEIGHT_BY_LAYER && aLayer != nullptr )
lw = aLayer->m_lineWeight;
// All lineweights >= 0 are always in 100ths of mm
double mm = m_defaultThickness;
if( lw >= 0 )
mm = lw / 100.0;
return SCALE_FACTOR( mm );
}
DXF_IMPORT_LAYER* DXF_IMPORT_PLUGIN::getImportLayer( const std::string& aLayerName )
{
DXF_IMPORT_LAYER* layer = m_layers.front().get();
wxString layerName = wxString::FromUTF8( aLayerName.c_str() );
if( !layerName.IsEmpty() )
{
auto resultIt = std::find_if( m_layers.begin(), m_layers.end(),
[layerName]( const auto& it )
{
return it->m_layerName == layerName;
} );
if( resultIt != m_layers.end() )
layer = resultIt->get();
}
return layer;
}
DXF_IMPORT_BLOCK* DXF_IMPORT_PLUGIN::getImportBlock( const std::string& aBlockName )
{
DXF_IMPORT_BLOCK* block = nullptr;
wxString blockName = wxString::FromUTF8( aBlockName.c_str() );
if( !blockName.IsEmpty() )
{
auto resultIt = std::find_if( m_blocks.begin(), m_blocks.end(),
[blockName]( const auto& it )
{
return it->m_name == blockName;
} );
if( resultIt != m_blocks.end() )
block = resultIt->get();
}
return block;
}
DXF_IMPORT_STYLE* DXF_IMPORT_PLUGIN::getImportStyle( const std::string& aStyleName )
{
DXF_IMPORT_STYLE* style = nullptr;
wxString styleName = wxString::FromUTF8( aStyleName.c_str() );
if( !styleName.IsEmpty() )
{
auto resultIt = std::find_if( m_styles.begin(), m_styles.end(),
[styleName]( const auto& it )
{
return it->m_name == styleName;
} );
if( resultIt != m_styles.end() )
style = resultIt->get();
}
return style;
}
void DXF_IMPORT_PLUGIN::addLine( const DL_LineData& aData )
{
DXF_IMPORT_LAYER* layer = getImportLayer( attributes.getLayer() );
double lineWidth = lineWeightToWidth( attributes.getWidth(), layer );
VECTOR2D start( mapX( aData.x1 ), mapY( aData.y1 ) );
VECTOR2D end( mapX( aData.x2 ), mapY( aData.y2 ) );
GRAPHICS_IMPORTER_BUFFER* bufferToUse = m_currentBlock ? &m_currentBlock->m_buffer
: &m_internalImporter;
bufferToUse->AddLine( start, end, lineWidth );
updateImageLimits( start );
updateImageLimits( end );
}
void DXF_IMPORT_PLUGIN::addPolyline(const DL_PolylineData& aData )
{
// Convert DXF Polylines into a series of KiCad Lines and Arcs.
// A Polyline (as opposed to a LWPolyline) may be a 3D line or
// even a 3D Mesh. The only type of Polyline which is guaranteed
// to import correctly is a 2D Polyline in X and Y, which is what
// we assume of all Polylines. The width used is the width of the Polyline.
// per-vertex line widths, if present, are ignored.
m_curr_entity.Clear();
m_curr_entity.m_EntityParseStatus = 1;
m_curr_entity.m_EntityFlag = aData.flags;
m_curr_entity.m_EntityType = DL_ENTITY_POLYLINE;
}
void DXF_IMPORT_PLUGIN::addVertex( const DL_VertexData& aData )
{
if( m_curr_entity.m_EntityParseStatus == 0 )
return; // Error
DXF_IMPORT_LAYER* layer = getImportLayer( attributes.getLayer() );
double lineWidth = lineWeightToWidth( attributes.getWidth(), layer );
/* support for per-vertex-encoded linewidth (Cadence uses it) */
/* linewidths are scaled by 100 in DXF */
if( aData.startWidth > 0.0 )
lineWidth = aData.startWidth / 100.0;
else if ( aData.endWidth > 0.0 )
lineWidth = aData.endWidth / 100.0;
const DL_VertexData* vertex = &aData;
MATRIX3x3D arbAxis = getArbitraryAxis( getExtrusion() );
VECTOR3D vertexCoords = ocsToWcs( arbAxis, VECTOR3D( vertex->x, vertex->y, vertex->z ) );
if( m_curr_entity.m_EntityParseStatus == 1 ) // This is the first vertex of an entity
{
m_curr_entity.m_LastCoordinate.x = mapX( vertexCoords.x );
m_curr_entity.m_LastCoordinate.y = mapY( vertexCoords.y );
m_curr_entity.m_PolylineStart = m_curr_entity.m_LastCoordinate;
m_curr_entity.m_BulgeVertex = vertex->bulge;
m_curr_entity.m_EntityParseStatus = 2;
return;
}
VECTOR2D seg_end( mapX( vertexCoords.x ), mapY( vertexCoords.y ) );
if( std::abs( m_curr_entity.m_BulgeVertex ) < MIN_BULGE )
insertLine( m_curr_entity.m_LastCoordinate, seg_end, lineWidth );
else
insertArc( m_curr_entity.m_LastCoordinate, seg_end, m_curr_entity.m_BulgeVertex, lineWidth );
m_curr_entity.m_LastCoordinate = seg_end;
m_curr_entity.m_BulgeVertex = vertex->bulge;
}
void DXF_IMPORT_PLUGIN::endEntity()
{
DXF_IMPORT_LAYER* layer = getImportLayer( attributes.getLayer() );
double lineWidth = lineWeightToWidth( attributes.getWidth(), layer );
if( m_curr_entity.m_EntityType == DL_ENTITY_POLYLINE ||
m_curr_entity.m_EntityType == DL_ENTITY_LWPOLYLINE )
{
// Polyline flags bit 0 indicates closed (1) or open (0) polyline
if( m_curr_entity.m_EntityFlag & 1 )
{
if( std::abs( m_curr_entity.m_BulgeVertex ) < MIN_BULGE )
{
insertLine( m_curr_entity.m_LastCoordinate, m_curr_entity.m_PolylineStart,
lineWidth );
}
else
{
insertArc( m_curr_entity.m_LastCoordinate, m_curr_entity.m_PolylineStart,
m_curr_entity.m_BulgeVertex, lineWidth );
}
}
}
if( m_curr_entity.m_EntityType == DL_ENTITY_SPLINE )
insertSpline( lineWidth );
m_curr_entity.Clear();
}
void DXF_IMPORT_PLUGIN::addBlock( const DL_BlockData& aData )
{
wxString name = wxString::FromUTF8( aData.name.c_str() );
std::unique_ptr<DXF_IMPORT_BLOCK> block = std::make_unique<DXF_IMPORT_BLOCK>( name, aData.bpx,
aData.bpy );
m_blocks.push_back( std::move( block ) );
m_currentBlock = m_blocks.back().get();
}
void DXF_IMPORT_PLUGIN::endBlock()
{
m_currentBlock = nullptr;
}
void DXF_IMPORT_PLUGIN::addInsert( const DL_InsertData& aData )
{
DXF_IMPORT_BLOCK* block = getImportBlock( aData.name );
if( block == nullptr )
return;
MATRIX3x3D arbAxis = getArbitraryAxis( getExtrusion() );
MATRIX3x3D rot;
rot.SetRotation( DEG2RAD( -aData.angle ) ); // DL_InsertData angle is in degrees
MATRIX3x3D scale;
scale.SetScale( VECTOR2D( aData.sx, aData.sy ) );
MATRIX3x3D trans = ( arbAxis * rot ) * scale;
VECTOR3D insertCoords = ocsToWcs( arbAxis, VECTOR3D( aData.ipx, aData.ipy, aData.ipz ) );
VECTOR2D translation( mapX( insertCoords.x ), mapY( insertCoords.y ) );
translation -= VECTOR2D( mapX( block->m_baseX ), mapY( block->m_baseY ) );
for( const std::unique_ptr<IMPORTED_SHAPE>& shape : block->m_buffer.GetShapes() )
{
std::unique_ptr<IMPORTED_SHAPE> newShape = shape->clone();
newShape->Transform( trans, translation );
m_internalImporter.AddShape( newShape );
}
}
void DXF_IMPORT_PLUGIN::addCircle( const DL_CircleData& aData )
{
MATRIX3x3D arbAxis = getArbitraryAxis( getExtrusion() );
VECTOR3D centerCoords = ocsToWcs( arbAxis, VECTOR3D( aData.cx, aData.cy, aData.cz ) );
VECTOR2D center( mapX( centerCoords.x ), mapY( centerCoords.y ) );
DXF_IMPORT_LAYER* layer = getImportLayer( attributes.getLayer() );
double lineWidth = lineWeightToWidth( attributes.getWidth(), layer );
GRAPHICS_IMPORTER_BUFFER* bufferToUse = m_currentBlock ? &m_currentBlock->m_buffer
: &m_internalImporter;
bufferToUse->AddCircle( center, mapDim( aData.radius ), lineWidth, false );
VECTOR2D radiusDelta( mapDim( aData.radius ), mapDim( aData.radius ) );
updateImageLimits( center + radiusDelta );
updateImageLimits( center - radiusDelta );
}
void DXF_IMPORT_PLUGIN::addArc( const DL_ArcData& aData )
{
MATRIX3x3D arbAxis = getArbitraryAxis( getExtrusion() );
VECTOR3D centerCoords = ocsToWcs( arbAxis, VECTOR3D( aData.cx, aData.cy, aData.cz ) );
// Init arc centre:
VECTOR2D center( mapX( centerCoords.x ), mapY( centerCoords.y ) );
// aData.anglex is in degrees.
EDA_ANGLE startangle( aData.angle1, DEGREES_T );
EDA_ANGLE endangle( aData.angle2, DEGREES_T );
if( ( arbAxis.GetScale().x < 0 ) != ( arbAxis.GetScale().y < 0 ) )
{
startangle = ANGLE_180 - startangle;
endangle = ANGLE_180 - endangle;
std::swap( startangle, endangle );
}
// Init arc start point
VECTOR2D startPoint( aData.radius, 0.0 );
RotatePoint( startPoint, -startangle );
VECTOR2D arcStart( mapX( startPoint.x + centerCoords.x ),
mapY( startPoint.y + centerCoords.y ) );
// calculate arc angle (arcs are CCW, and should be < 0 in Pcbnew)
EDA_ANGLE angle = -( endangle - startangle );
if( angle > ANGLE_0 )
angle -= ANGLE_360;
DXF_IMPORT_LAYER* layer = getImportLayer( attributes.getLayer() );
double lineWidth = lineWeightToWidth( attributes.getWidth(), layer );
GRAPHICS_IMPORTER_BUFFER* bufferToUse = m_currentBlock ? &m_currentBlock->m_buffer
: &m_internalImporter;
bufferToUse->AddArc( center, arcStart, angle, lineWidth );
VECTOR2D radiusDelta( mapDim( aData.radius ), mapDim( aData.radius ) );
updateImageLimits( center + radiusDelta );
updateImageLimits( center - radiusDelta );
}
void DXF_IMPORT_PLUGIN::addEllipse( const DL_EllipseData& aData )
{
MATRIX3x3D arbAxis = getArbitraryAxis( getExtrusion() );
VECTOR3D centerCoords = ocsToWcs( arbAxis, VECTOR3D( aData.cx, aData.cy, aData.cz ) );
VECTOR3D majorCoords = ocsToWcs( arbAxis, VECTOR3D( aData.mx, aData.my, aData.mz ) );
// DXF ellipses store the minor axis length as a ratio to the major axis.
// The major coords are relative to the center point.
// For now, we assume ellipses in the XY plane.
VECTOR2D center( mapX( centerCoords.x ), mapY( centerCoords.y ) );
VECTOR2D major( mapX( majorCoords.x ), mapY( majorCoords.y ) );
// DXF elliptical arcs store their angles in radians (unlike circular arcs which use degrees)
// The arcs wind CCW as in KiCad. The end angle must be greater than the start angle, and if
// the extrusion direction is negative, the arc winding is CW instead! Note that this is a
// simplification that assumes the DXF is representing a 2D drawing, and would need to be
// revisited if we want to import true 3D drawings and "flatten" them to the 2D KiCad plane
// internally.
EDA_ANGLE startAngle( aData.angle1, RADIANS_T );
EDA_ANGLE endAngle( aData.angle2, RADIANS_T );
if( startAngle > endAngle )
endAngle += ANGLE_360;
if( aData.ratio == 1.0 )
{
double radius = major.EuclideanNorm();
if( startAngle == endAngle )
{
DL_CircleData circle( aData.cx, aData.cy, aData.cz, radius );
addCircle( circle );
return;
}
else
{
// Angles are relative to major axis
startAngle -= EDA_ANGLE( major );
endAngle -= EDA_ANGLE( major );
DL_ArcData arc( aData.cx, aData.cy, aData.cz, radius, startAngle.AsDegrees(),
endAngle.AsDegrees() );
addArc( arc );
return;
}
}
// TODO: testcases for negative extrusion vector; handle it here
std::vector<BEZIER<double>> splines;
ELLIPSE<double> ellipse( center, major, aData.ratio, startAngle, endAngle );
TransformEllipseToBeziers( ellipse, splines );
DXF_IMPORT_LAYER* layer = getImportLayer( attributes.getLayer() );
double lineWidth = lineWeightToWidth( attributes.getWidth(), layer );
GRAPHICS_IMPORTER_BUFFER* bufferToUse = m_currentBlock ? &m_currentBlock->m_buffer
: &m_internalImporter;
for( const BEZIER<double>& b : splines )
bufferToUse->AddSpline( b.Start, b.C1, b.C2, b.End, lineWidth );
// Naive bounding
updateImageLimits( center + major );
updateImageLimits( center - major );
}
void DXF_IMPORT_PLUGIN::addText( const DL_TextData& aData )
{
MATRIX3x3D arbAxis = getArbitraryAxis( getExtrusion() );
VECTOR3D refPointCoords = ocsToWcs( arbAxis, VECTOR3D( aData.ipx, aData.ipy, aData.ipz ) );
VECTOR3D secPointCoords = ocsToWcs( arbAxis, VECTOR3D( std::isnan( aData.apx ) ? 0 : aData.apx,
std::isnan( aData.apy ) ? 0 : aData.apy,
std::isnan( aData.apz ) ? 0 : aData.apz ) );
VECTOR2D refPoint( mapX( refPointCoords.x ), mapY( refPointCoords.y ) );
VECTOR2D secPoint( mapX( secPointCoords.x ), mapY( secPointCoords.y ) );
if( aData.vJustification != 0 || aData.hJustification != 0 || aData.hJustification == 4 )
{
if( aData.hJustification != 3 && aData.hJustification != 5 )
{
VECTOR2D tmp = secPoint;
secPoint = refPoint;
refPoint = tmp;
}
}
wxString text = toNativeString( wxString::FromUTF8( aData.text.c_str() ) );
DXF_IMPORT_STYLE* style = getImportStyle( aData.style.c_str() );
double textHeight = mapDim( aData.height );
// The 0.9 factor gives a better height/width base ratio with our font
double charWidth = textHeight * 0.9;
if( style != nullptr )
charWidth *= style->m_widthFactor;
double textWidth = charWidth * text.length(); // Rough approximation
double textThickness = textHeight/8.0; // Use a reasonable line thickness for this text
VECTOR2D bottomLeft(0.0, 0.0);
VECTOR2D bottomRight(0.0, 0.0);
VECTOR2D topLeft(0.0, 0.0);
VECTOR2D topRight(0.0, 0.0);
GR_TEXT_H_ALIGN_T hJustify = GR_TEXT_H_ALIGN_LEFT;
GR_TEXT_V_ALIGN_T vJustify = GR_TEXT_V_ALIGN_BOTTOM;
switch( aData.vJustification )
{
case 0: //DRW_Text::VBaseLine:
case 1: //DRW_Text::VBottom:
vJustify = GR_TEXT_V_ALIGN_BOTTOM;
topLeft.y = textHeight;
topRight.y = textHeight;
break;
case 2: //DRW_Text::VMiddle:
vJustify = GR_TEXT_V_ALIGN_CENTER;
bottomRight.y = -textHeight / 2.0;
bottomLeft.y = -textHeight / 2.0;
topLeft.y = textHeight / 2.0;
topRight.y = textHeight / 2.0;
break;
case 3: //DRW_Text::VTop:
vJustify = GR_TEXT_V_ALIGN_TOP;
bottomLeft.y = -textHeight;
bottomRight.y = -textHeight;
break;
}
switch( aData.hJustification )
{
case 0: //DRW_Text::HLeft:
case 3: //DRW_Text::HAligned: // no equivalent options in text pcb.
case 5: //DRW_Text::HFit: // no equivalent options in text pcb.
hJustify = GR_TEXT_H_ALIGN_LEFT;
bottomRight.x = textWidth;
topRight.x = textWidth;
break;
case 1: //DRW_Text::HCenter:
case 4: //DRW_Text::HMiddle: // no equivalent options in text pcb.
hJustify = GR_TEXT_H_ALIGN_CENTER;
bottomLeft.x = -textWidth / 2.0;
topLeft.x = -textWidth / 2.0;
bottomRight.x = textWidth / 2.0;
topRight.x = textWidth / 2.0;
break;
case 2: //DRW_Text::HRight:
hJustify = GR_TEXT_H_ALIGN_RIGHT;
bottomLeft.x = -textWidth;
topLeft.x = -textWidth;
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
// dxf_lib imports text angle in radians (although there are no comment about that.
// So, for the moment, convert this angle to degrees
double angle_degree = aData.angle * 180 / M_PI;
// We also need the angle in radians. so convert angle_degree to radians
// regardless the aData.angle unit
double angleInRads = angle_degree * M_PI / 180.0;
double cosine = cos(angleInRads);
double sine = sin(angleInRads);
GRAPHICS_IMPORTER_BUFFER* bufferToUse = m_currentBlock ? &m_currentBlock->m_buffer
: &m_internalImporter;
bufferToUse->AddText( refPoint, text, textHeight, charWidth, textThickness, angle_degree,
hJustify, vJustify );
// Calculate the boundary box and update the image limits:
bottomLeft.x = bottomLeft.x * cosine - bottomLeft.y * sine;
bottomLeft.y = bottomLeft.x * sine + bottomLeft.y * cosine;
bottomRight.x = bottomRight.x * cosine - bottomRight.y * sine;
bottomRight.y = bottomRight.x * sine + bottomRight.y * cosine;
topLeft.x = topLeft.x * cosine - topLeft.y * sine;
topLeft.y = topLeft.x * sine + topLeft.y * cosine;
topRight.x = topRight.x * cosine - topRight.y * sine;
topRight.y = topRight.x * sine + topRight.y * cosine;
bottomLeft += refPoint;
bottomRight += refPoint;
topLeft += refPoint;
topRight += refPoint;
updateImageLimits( bottomLeft );
updateImageLimits( bottomRight );
updateImageLimits( topLeft );
updateImageLimits( topRight );
}
void DXF_IMPORT_PLUGIN::addMTextChunk( const std::string& text )
{
// If the text string is greater than 250 characters, the string is divided into 250-character
// chunks, which appear in one or more group 3 codes. If group 3 codes are used, the last group
// is a group 1 and has fewer than 250 characters
m_mtextContent.append( text );
}
void DXF_IMPORT_PLUGIN::addMText( const DL_MTextData& aData )
{
m_mtextContent.append( aData.text );
// TODO: determine control codes applied to the whole text?
wxString text = toNativeString( wxString::FromUTF8( m_mtextContent.c_str() ) );
DXF_IMPORT_STYLE* style = getImportStyle( aData.style.c_str() );
double textHeight = mapDim( aData.height );
// The 0.9 factor gives a better height/width base ratio with our font
double charWidth = textHeight * 0.9;
if( style != nullptr )
charWidth *= style->m_widthFactor;
double textWidth = charWidth * text.length(); // Rough approximation
double textThickness = textHeight/8.0; // Use a reasonable line thickness for this text
VECTOR2D bottomLeft(0.0, 0.0);
VECTOR2D bottomRight(0.0, 0.0);
VECTOR2D topLeft(0.0, 0.0);
VECTOR2D topRight(0.0, 0.0);
MATRIX3x3D arbAxis = getArbitraryAxis( getExtrusion() );
VECTOR3D textposCoords = ocsToWcs( arbAxis, VECTOR3D( aData.ipx, aData.ipy, aData.ipz ) );
VECTOR2D textpos( mapX( textposCoords.x ), mapY( textposCoords.y ) );
// Initialize text justifications:
GR_TEXT_H_ALIGN_T hJustify = GR_TEXT_H_ALIGN_LEFT;
GR_TEXT_V_ALIGN_T vJustify = GR_TEXT_V_ALIGN_BOTTOM;
if( aData.attachmentPoint <= 3 )
{
vJustify = GR_TEXT_V_ALIGN_TOP;
bottomLeft.y = -textHeight;
bottomRight.y = -textHeight;
}
else if( aData.attachmentPoint <= 6 )
{
vJustify = GR_TEXT_V_ALIGN_CENTER;
bottomRight.y = -textHeight / 2.0;
bottomLeft.y = -textHeight / 2.0;
topLeft.y = textHeight / 2.0;
topRight.y = textHeight / 2.0;
}
else
{
vJustify = GR_TEXT_V_ALIGN_BOTTOM;
topLeft.y = textHeight;
topRight.y = textHeight;
}
if( aData.attachmentPoint % 3 == 1 )
{
hJustify = GR_TEXT_H_ALIGN_LEFT;
bottomRight.x = textWidth;
topRight.x = textWidth;
}
else if( aData.attachmentPoint % 3 == 2 )
{
hJustify = GR_TEXT_H_ALIGN_CENTER;
bottomLeft.x = -textWidth / 2.0;
topLeft.x = -textWidth / 2.0;
bottomRight.x = textWidth / 2.0;
topRight.x = textWidth / 2.0;
}
else
{
hJustify = GR_TEXT_H_ALIGN_RIGHT;
bottomLeft.x = -textWidth;
topLeft.x = -textWidth;
}
#if 0 // These setting have no meaning 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
// dxf_lib imports text angle in radians (although there are no comment about that.
// So, for the moment, convert this angle to degrees
double angle_degree = aData.angle * 180/M_PI;
// We also need the angle in radians. so convert angle_degree to radians
// regardless the aData.angle unit
double angleInRads = angle_degree * M_PI / 180.0;
double cosine = cos(angleInRads);
double sine = sin(angleInRads);
GRAPHICS_IMPORTER_BUFFER* bufferToUse = m_currentBlock ? &m_currentBlock->m_buffer
: &m_internalImporter;
bufferToUse->AddText( textpos, text, textHeight, charWidth, textThickness, angle_degree,
hJustify, vJustify );
bottomLeft.x = bottomLeft.x * cosine - bottomLeft.y * sine;
bottomLeft.y = bottomLeft.x * sine + bottomLeft.y * cosine;
bottomRight.x = bottomRight.x * cosine - bottomRight.y * sine;
bottomRight.y = bottomRight.x * sine + bottomRight.y * cosine;
topLeft.x = topLeft.x * cosine - topLeft.y * sine;
topLeft.y = topLeft.x * sine + topLeft.y * cosine;
topRight.x = topRight.x * cosine - topRight.y * sine;
topRight.y = topRight.x * sine + topRight.y * cosine;
bottomLeft += textpos;
bottomRight += textpos;
topLeft += textpos;
topRight += textpos;
updateImageLimits( bottomLeft );
updateImageLimits( bottomRight );
updateImageLimits( topLeft );
updateImageLimits( topRight );
m_mtextContent.clear();
}
double DXF_IMPORT_PLUGIN::getCurrentUnitScale()
{
double scale = 1.0;
switch( m_currentUnit )
{
case DXF_IMPORT_UNITS::INCHES: scale = 25.4; break;
case DXF_IMPORT_UNITS::FEET: scale = 304.8; break;
case DXF_IMPORT_UNITS::MILLIMETERS: scale = 1.0; break;
case DXF_IMPORT_UNITS::CENTIMETERS: scale = 10.0; break;
case DXF_IMPORT_UNITS::METERS: scale = 1000.0; break;
case DXF_IMPORT_UNITS::MICROINCHES: scale = 2.54e-5; break;
case DXF_IMPORT_UNITS::MILS: scale = 0.0254; break;
case DXF_IMPORT_UNITS::YARDS: scale = 914.4; break;
case DXF_IMPORT_UNITS::ANGSTROMS: scale = 1.0e-7; break;
case DXF_IMPORT_UNITS::NANOMETERS: scale = 1.0e-6; break;
case DXF_IMPORT_UNITS::MICRONS: scale = 1.0e-3; break;
case DXF_IMPORT_UNITS::DECIMETERS: scale = 100.0; break;
default:
// use the default of 1.0 for:
// 0: Unspecified Units
// 3: miles
// 7: kilometers
// 15: decameters
// 16: hectometers
// 17: gigameters
// 18: AU
// 19: lightyears
// 20: parsecs
break;
}
return scale;
}
void DXF_IMPORT_PLUGIN::setVariableInt( const std::string& key, int value, int code )
{
// Called for every int variable in the DXF file (e.g. "$INSUNITS").
if( key == "$DWGCODEPAGE" )
{
m_codePage = value;
return;
}
if( key == "$AUPREC" )
{
m_importAnglePrecision = value;
return;
}
if( key == "$LUPREC" )
{
m_importCoordinatePrecision = value;
return;
}
if( key == "$INSUNITS" ) // Drawing units
{
m_currentUnit = DXF_IMPORT_UNITS::DEFAULT;
switch( value )
{
case 1: m_currentUnit = DXF_IMPORT_UNITS::INCHES; break;
case 2: m_currentUnit = DXF_IMPORT_UNITS::FEET; break;
case 4: m_currentUnit = DXF_IMPORT_UNITS::MILLIMETERS; break;
case 5: m_currentUnit = DXF_IMPORT_UNITS::CENTIMETERS; break;
case 6: m_currentUnit = DXF_IMPORT_UNITS::METERS; break;
case 8: m_currentUnit = DXF_IMPORT_UNITS::MICROINCHES; break;
case 9: m_currentUnit = DXF_IMPORT_UNITS::MILS; break;
case 10: m_currentUnit = DXF_IMPORT_UNITS::YARDS; break;
case 11: m_currentUnit = DXF_IMPORT_UNITS::ANGSTROMS; break;
case 12: m_currentUnit = DXF_IMPORT_UNITS::NANOMETERS; break;
case 13: m_currentUnit = DXF_IMPORT_UNITS::MICRONS; break;
case 14: m_currentUnit = DXF_IMPORT_UNITS::DECIMETERS; break;
default:
// use the default for:
// 0: Unspecified Units
// 3: miles
// 7: kilometers
// 15: decameters
// 16: hectometers
// 17: gigameters
// 18: AU
// 19: lightyears
// 20: parsecs
break;
}
return;
}
}
void DXF_IMPORT_PLUGIN::setVariableString( const std::string& key, const std::string& value,
int code )
{
// Called for every string variable in the DXF file (e.g. "$ACADVER").
}
wxString DXF_IMPORT_PLUGIN::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 _WIN32
// 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 DXF_IMPORT_PLUGIN::toNativeString( const wxString& aData )
{
wxString res;
size_t i = 0;
int braces = 0;
int overbarLevel = -1;
// For description, see:
// https://ezdxf.readthedocs.io/en/stable/dxfinternals/entities/mtext.html
// https://www.cadforum.cz/en/text-formatting-codes-in-mtext-objects-tip8640
for( i = 0; i < aData.length(); i++ )
{
switch( (wchar_t) aData[i] )
{
case '{': // Text area influenced by the code
braces++;
break;
case '}':
if( overbarLevel == braces )
{
res << '}';
overbarLevel = -1;
}
braces--;
break;
case '^': // C0 control code
if( ++i >= aData.length() )
break;
switch( (wchar_t) aData[i] )
{
case 'I': res << '\t'; break;
case 'J': res << '\b'; break;
case ' ': res << '^'; break;
default: break;
}
break;
case '\\':
{
if( ++i >= aData.length() )
break;
switch( (wchar_t) aData[i] )
{
case 'P': // New paragraph (new line)
case 'X': // Paragraph wrap on the dimension line (only in dimensions)
res << '\n';
break;
case '~': // Non-wrapping space, hard space
res << L'\u00A0';
break;
case 'U': // Unicode character, e.g. \U+ff08
{
i += 2;
wxString codeHex;
for( ; codeHex.length() < 4 && i < aData.length(); i++ )
codeHex << aData[i];
unsigned long codeVal = 0;
if( codeHex.ToCULong( &codeVal, 16 ) && codeVal != 0 )
res << wxUniChar( codeVal );
i--;
}
break;
case 'S': // Stacking
{
i++;
wxString stacked;
for( ; i < aData.length(); i++ )
{
if( aData[i] == ';' )
break;
else
stacked << aData[i];
}
if( stacked.Contains( wxS( "#" ) ) )
{
res << '^' << '{';
res << stacked.BeforeFirst( '#' );
res << '}' << '/' << '_' << '{';
res << stacked.AfterFirst( '#' );
res << '}';
}
else
{
stacked.Replace( wxS( "^ " ), wxS( "/" ) );
res << stacked;
}
}
break;
case 'O': // Start overstrike
if( overbarLevel == -1 )
{
res << '~' << '{';
overbarLevel = braces;
}
break;
case 'o': // Stop overstrike
if( overbarLevel == braces )
{
res << '}';
overbarLevel = -1;
}
break;
case 'L': // Start underline
case 'l': // Stop underline
case 'K': // Start strike-through
case 'k': // Stop strike-through
case 'N': // New column
// Ignore
break;
case 'p': // Control codes for bullets, numbered paragraphs, tab stops and columns
case 'Q': // Slanting (obliquing) text by angle
case 'H': // Text height
case 'W': // Text width
case 'F': // Font selection
case 'f': // Font selection (alternative)
case 'A': // Alignment
case 'C': // Color change (ACI colors)
case 'c': // Color change (truecolor)
case 'T': // Tracking, char.spacing
// Skip to ;
for( ; i < aData.length(); i++ )
{
if( aData[i] == ';' )
break;
}
break;
default: // Escaped character
if( ++i >= aData.length() )
break;
res << aData[i];
break;
}
}
break;
default: res << aData[i];
}
}
if( overbarLevel != -1 )
{
res << '}';
overbarLevel = -1;
}
#if 1
wxRegEx regexp;
// 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 DXF_IMPORT_PLUGIN::addTextStyle( const DL_StyleData& aData )
{
wxString name = wxString::FromUTF8( aData.name.c_str() );
auto style = std::make_unique<DXF_IMPORT_STYLE>( name, aData.fixedTextHeight, aData.widthFactor,
aData.bold, aData.italic );
m_styles.push_back( std::move( style ) );
}
void DXF_IMPORT_PLUGIN::addPoint( const DL_PointData& aData )
{
MATRIX3x3D arbAxis = getArbitraryAxis( getExtrusion() );
VECTOR3D centerCoords = ocsToWcs( arbAxis, VECTOR3D( aData.x, aData.y, aData.z ) );
VECTOR2D center( mapX( centerCoords.x ), mapY( centerCoords.y ) );
// we emulate points with filled circles
// set the linewidth to something that even small circles look good with
// thickness is optional for dxf points
// note: we had to modify the dxf library to grab the attribute for thickness
double lineWidth = 0.0001;
double thickness = mapDim( std::max( aData.thickness, 0.01 ) );
GRAPHICS_IMPORTER_BUFFER* bufferToUse = m_currentBlock ? &m_currentBlock->m_buffer
: &m_internalImporter;
bufferToUse->AddCircle( center, thickness, lineWidth, true );
VECTOR2D radiusDelta( SCALE_FACTOR( thickness ), SCALE_FACTOR( thickness ) );
updateImageLimits( center + radiusDelta );
updateImageLimits( center - radiusDelta );
}
void DXF_IMPORT_PLUGIN::insertLine( const VECTOR2D& aSegStart,
const VECTOR2D& aSegEnd, double aWidth )
{
VECTOR2D origin( SCALE_FACTOR( aSegStart.x ), SCALE_FACTOR( aSegStart.y ) );
VECTOR2D end( SCALE_FACTOR( aSegEnd.x ), SCALE_FACTOR( aSegEnd.y ) );
GRAPHICS_IMPORTER_BUFFER* bufferToUse = m_currentBlock ? &m_currentBlock->m_buffer
: &m_internalImporter;
bufferToUse->AddLine( origin, end, aWidth );
updateImageLimits( origin );
updateImageLimits( end );
}
void DXF_IMPORT_PLUGIN::insertArc( const VECTOR2D& aSegStart, const VECTOR2D& aSegEnd,
double aBulge, double aWidth )
{
VECTOR2D segment_startpoint( SCALE_FACTOR( aSegStart.x ), SCALE_FACTOR( aSegStart.y ) );
VECTOR2D segment_endpoint( SCALE_FACTOR( aSegEnd.x ), SCALE_FACTOR( 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
VECTOR2D sp( aSegStart.x, -aSegStart.y );
VECTOR2D 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;
VECTOR2D center( SCALE_FACTOR( cx ), SCALE_FACTOR( -cy ) );
VECTOR2D arc_start;
EDA_ANGLE angle( ang, RADIANS_T );
if( ang < 0.0 )
{
arc_start = VECTOR2D( SCALE_FACTOR( ep.x ), SCALE_FACTOR( -ep.y ) );
}
else
{
arc_start = VECTOR2D( SCALE_FACTOR( sp.x ), SCALE_FACTOR( -sp.y ) );
angle = -angle;
}
GRAPHICS_IMPORTER_BUFFER* bufferToUse = m_currentBlock ? &m_currentBlock->m_buffer
: &m_internalImporter;
bufferToUse->AddArc( center, arc_start, angle, aWidth );
VECTOR2D radiusDelta( SCALE_FACTOR( radius ), SCALE_FACTOR( radius ) );
updateImageLimits( center + radiusDelta );
updateImageLimits( center - radiusDelta );
}
#include "tinysplinecxx.h"
void DXF_IMPORT_PLUGIN::insertSpline( double aWidth )
{
#if 0 // Debug only
wxLogMessage("spl deg %d kn %d ctr %d fit %d",
m_curr_entity.m_SplineDegree,
m_curr_entity.m_SplineKnotsList.size(),
m_curr_entity.m_SplineControlPointList.size(),
m_curr_entity.m_SplineFitPointList.size() );
#endif
unsigned imax = m_curr_entity.m_SplineControlPointList.size();
if( imax < 2 ) // malformed spline
return;
#if 0 // set to 1 to approximate the spline by segments between 2 control points
VECTOR2D startpoint( mapX( m_curr_entity.m_SplineControlPointList[0].m_x ),
mapY( m_curr_entity.m_SplineControlPointList[0].m_y ) );
for( unsigned int ii = 1; ii < imax; ++ii )
{
VECTOR2D endpoint( mapX( m_curr_entity.m_SplineControlPointList[ii].m_x ),
mapY( m_curr_entity.m_SplineControlPointList[ii].m_y ) );
if( startpoint != endpoint )
{
m_internalImporter.AddLine( startpoint, endpoint, aWidth );
updateImageLimits( startpoint );
updateImageLimits( endpoint );
startpoint = endpoint;
}
}
#else // Use bezier curves, supported by pcbnew, to approximate the spline
std::vector<double> ctrlp;
for( unsigned ii = 0; ii < imax; ++ii )
{
ctrlp.push_back( m_curr_entity.m_SplineControlPointList[ii].m_x );
ctrlp.push_back( m_curr_entity.m_SplineControlPointList[ii].m_y );
}
std::vector<double> coords;
tinyspline::BSpline beziers;
try
{
tinyspline::BSpline dxfspline( m_curr_entity.m_SplineControlPointList.size(),
/* coord dim */ 2, m_curr_entity.m_SplineDegree );
dxfspline.setControlPoints( ctrlp );
dxfspline.setKnots( m_curr_entity.m_SplineKnotsList );
beziers = tinyspline::BSpline( dxfspline.toBeziers() );
coords = beziers.controlPoints();
}
catch( const std::runtime_error& ) // tinyspline throws everything including data validation
// as runtime errors
{
// invalid spline definition, drop this block
ReportMsg( _( "Invalid spline definition encountered" ) );
return;
}
size_t order = beziers.order();
size_t dim = beziers.dimension();
size_t numBeziers = ( coords.size() / dim ) / order;
for( size_t i = 0; i < numBeziers; i++ )
{
size_t ii = i * dim * order;
VECTOR2D start( mapX( coords[ ii ] ), mapY( coords[ ii + 1 ] ) );
VECTOR2D bezierControl1( mapX( coords[ii + 2] ), mapY( coords[ii + 3] ) );
// not sure why this happens, but it seems to sometimes slip degree on the final bezier
VECTOR2D bezierControl2;
if( ii + 4 >= coords.size() )
bezierControl2 = bezierControl1;
else
bezierControl2 = VECTOR2D( mapX( coords[ii + 4] ), mapY( coords[ii + 5] ) );
VECTOR2D end;
if( ii + 6 >= coords.size() )
end = bezierControl2;
else
end = VECTOR2D( mapX( coords[ii + 6] ), mapY( coords[ii + 7] ) );
GRAPHICS_IMPORTER_BUFFER* bufferToUse = m_currentBlock ? &m_currentBlock->m_buffer
: &m_internalImporter;
bufferToUse->AddSpline( start, bezierControl1, bezierControl2, end, aWidth );
}
#endif
}
void DXF_IMPORT_PLUGIN::updateImageLimits( const VECTOR2D& aPoint )
{
m_minX = std::min( aPoint.x, m_minX );
m_maxX = std::max( aPoint.x, m_maxX );
m_minY = std::min( aPoint.y, m_minY );
m_maxY = std::max( aPoint.y, m_maxY );
}
MATRIX3x3D DXF_IMPORT_PLUGIN::getArbitraryAxis( DL_Extrusion* aData )
{
VECTOR3D arbZ, arbX, arbY;
double direction[3];
aData->getDirection( direction );
arbZ = VECTOR3D( direction[0], direction[1], direction[2] ).Normalize();
if( ( abs( arbZ.x ) < ( 1.0 / 64.0 ) ) && ( abs( arbZ.y ) < ( 1.0 / 64.0 ) ) )
arbX = VECTOR3D( 0, 1, 0 ).Cross( arbZ ).Normalize();
else
arbX = VECTOR3D( 0, 0, 1 ).Cross( arbZ ).Normalize();
arbY = arbZ.Cross( arbX ).Normalize();
return MATRIX3x3D{ arbX, arbY, arbZ };
}
VECTOR3D DXF_IMPORT_PLUGIN::wcsToOcs( const MATRIX3x3D& arbitraryAxis, VECTOR3D point )
{
return arbitraryAxis * point;
}
VECTOR3D DXF_IMPORT_PLUGIN::ocsToWcs( const MATRIX3x3D& arbitraryAxis, VECTOR3D point )
{
VECTOR3D worldX = wcsToOcs( arbitraryAxis, VECTOR3D( 1, 0, 0 ) );
VECTOR3D worldY = wcsToOcs( arbitraryAxis, VECTOR3D( 0, 1, 0 ) );
VECTOR3D worldZ = wcsToOcs( arbitraryAxis, VECTOR3D( 0, 0, 1 ) );
MATRIX3x3 world( worldX, worldY, worldZ );
return world * point;
}