31#include <wx/filename.h>
33#include <wx/stdpaths.h>
34#include <wx/wfstream.h>
35#include <wx/zipstrm.h>
36#include <wx/stdstream.h>
38#include <decompress.hpp>
54#include <IGESCAFControl_Reader.hxx>
55#include <IGESCAFControl_Writer.hxx>
56#include <IGESControl_Controller.hxx>
57#include <IGESData_GlobalSection.hxx>
58#include <IGESData_IGESModel.hxx>
59#include <Interface_Static.hxx>
60#include <Quantity_Color.hxx>
61#include <STEPCAFControl_Reader.hxx>
62#include <STEPCAFControl_Writer.hxx>
63#include <APIHeaderSection_MakeHeader.hxx>
64#include <Standard_Failure.hxx>
65#include <Standard_Handle.hxx>
66#include <Standard_Version.hxx>
67#include <TCollection_ExtendedString.hxx>
68#include <TDocStd_Document.hxx>
69#include <TDocStd_XLinkTool.hxx>
70#include <TDataStd_Name.hxx>
71#include <TDataStd_TreeNode.hxx>
72#include <TDF_LabelSequence.hxx>
73#include <TDF_Tool.hxx>
74#include <TopExp_Explorer.hxx>
76#include <XCAFApp_Application.hxx>
78#include <XCAFDoc_DocumentTool.hxx>
79#include <XCAFDoc_ColorTool.hxx>
80#include <XCAFDoc_ShapeTool.hxx>
81#include <XCAFDoc_VisMaterialTool.hxx>
82#include <XCAFDoc_Area.hxx>
83#include <XCAFDoc_Centroid.hxx>
84#include <XCAFDoc_Location.hxx>
85#include <XCAFDoc_Volume.hxx>
87#include "KI_XCAFDoc_AssemblyGraph.hxx"
89#include <BRep_Tool.hxx>
90#include <BRepMesh_IncrementalMesh.hxx>
91#include <BRepBuilderAPI_GTransform.hxx>
92#include <BRepBuilderAPI_MakeEdge.hxx>
93#include <BRepBuilderAPI_MakeWire.hxx>
94#include <BRepBuilderAPI_MakeFace.hxx>
95#include <BRepExtrema_DistShapeShape.hxx>
96#include <BRepPrimAPI_MakePrism.hxx>
97#include <BRepTools.hxx>
98#include <BRepLib_MakeWire.hxx>
99#include <BRepAdaptor_Surface.hxx>
100#include <BRepAlgoAPI_Check.hxx>
101#include <BRepAlgoAPI_Cut.hxx>
102#include <BRepAlgoAPI_Fuse.hxx>
103#include <ShapeUpgrade_UnifySameDomain.hxx>
105#include <BRepBndLib.hxx>
106#include <Bnd_BoundSortBox.hxx>
107#include <GProp_GProps.hxx>
108#include <BRepGProp.hxx>
110#include <Geom_Curve.hxx>
111#include <Geom_TrimmedCurve.hxx>
116#include <GC_MakeArcOfCircle.hxx>
117#include <GC_MakeCircle.hxx>
119#include <RWGltf_CafWriter.hxx>
120#include <StlAPI_Writer.hxx>
122#if OCC_VERSION_HEX >= 0x070700
123#include <VrmlAPI_CafReader.hxx>
124#include <RWPly_CafWriter.hxx>
151 wxFileName lfile( wxString::FromUTF8Unchecked( aFileName ) );
153 if( !lfile.FileExists() )
156 msg.Printf( wxT(
" * fileType(): no such file: %s\n" ),
157 wxString::FromUTF8Unchecked( aFileName ) );
163 wxString ext = lfile.GetExt().Lower();
165 if( ext == wxT(
"wrl" ) )
168 if( ext == wxT(
"wrz" ) )
171 if( ext == wxT(
"idf" ) )
174 if( ext == wxT(
"emn" ) )
177 if( ext == wxT(
"stpz" ) || ext == wxT(
"gz" ) )
191 const int max_line_count = 3;
193 for(
int ii = 0; ii < max_line_count; ii++ )
195 memset( iline, 0, 82 );
196 ifile.getline( iline, 82 );
202 if( !strncmp( iline,
"ISO-10303-21;", 13 ) )
208 std::string fstr = iline;
212 if( fstr.find(
"urn:oid:1.0.10303." ) != std::string::npos )
221 if( iline[72] ==
'S' && ( iline[80] == 0 || iline[80] == 13 || iline[80] == 10 ) )
228 if( strncmp( iline,
"/*", 2 ) != 0 )
246 double bc = ( b.
x * b.
x + b.
y * b.
y ) / 2.0;
247 double cd = ( -d.
x * d.
x - d.
y * d.
y ) / 2.0;
248 double det = -b.
x * d.
y + d.
x * b.
y;
252 center.
x = ( -bc * d.
y - cd * b.
y ) * det;
253 center.
y = ( b.
x * cd + d.
x * bc ) * det;
260#define APPROX_DBG( stmt )
268 static const double c_radiusDeviation = 1000.0;
269 static const double c_arcCenterDeviation = 1000.0;
270 static const double c_relLengthDeviation = 0.8;
271 static const int c_last_none = -1000;
291 int last = c_last_none;
297 APPROX_DBG( std::cout << i <<
" " << aSrc.
CPoint( i ) <<
" " << ( i - 3 ) <<
" "
299 << ( i - 1 ) <<
" " <<
VECTOR2I( p2 ) << std::endl );
304 bool defective =
false;
310 defective |=
std::abs( d01 - d12 ) > ( std::max( d01, d12 ) * c_relLengthDeviation );
318 double a_diff = ( a01 - a12 ).Normalize180().AsDegrees();
319 defective |=
std::abs( a_diff ) < 0.1;
322 double maxAngleDiff = std::max( d01, d12 ) < c_smallSize ? 46.0 : 30.0;
323 defective |=
std::abs( a_diff ) >= maxAngleDiff;
330 double radius = ( p0 - center ).EuclideanNorm();
334 for(
int j = i; j <= jEndIdx; j++ )
339 double rad_test = ( p_test - center ).EuclideanNorm();
340 double d_tl = ( p_test - p_prev ).EuclideanNorm();
341 double rad_dev =
std::abs( radius - rad_test );
344 << int64_t( rad_test ) <<
" ref " << int64_t( radius )
347 if( rad_dev > c_radiusDeviation )
350 <<
" Radius deviation too large: " << int64_t( rad_dev )
351 <<
" > " << c_radiusDeviation << std::endl );
356 double maxAngleDiff =
357 std::max( std::max( d01, d12 ), d_tl ) < c_smallSize ? 46.0 : 30.0;
359 double a_diff_test = ( a_prev - a_test ).Normalize180().AsDegrees();
360 if(
std::abs( a_diff_test ) >= maxAngleDiff )
362 APPROX_DBG( std::cout <<
" " << j <<
" Angles differ too much " << a_diff_test
367 if(
std::abs( d_tl - d01 ) > ( std::max( d_tl, d01 ) * c_relLengthDeviation ) )
369 APPROX_DBG( std::cout <<
" " << j <<
" Lengths differ too much " << d_tl
370 <<
"; " << d01 << std::endl );
380 if( last != c_last_none )
389 int toRemove = last - ( aSrc.
PointCount() - 3 );
419 APPROX_DBG( std::cout <<
" Self-intersection check failed" << std::endl );
423 if( last == c_last_none )
440 if( iarc0 != -1 && iarc1 != -1 )
442 APPROX_DBG( std::cout <<
"Final arcs " << iarc0 <<
" " << iarc1 << std::endl );
452 if( ( p1 - p0 ).EuclideanNorm() < c_circleCloseGap )
470 if(
std::abs( ar0 - ar1 ) <= c_radiusDeviation
471 && ( ac0 - ac1 ).EuclideanNorm() <= c_arcCenterDeviation )
487static TopoDS_Shape
getOneShape( Handle( XCAFDoc_ShapeTool ) aShapeTool )
489 TDF_LabelSequence theLabels;
490 aShapeTool->GetFreeShapes( theLabels );
494 if( theLabels.Length() == 1 )
495 return aShapeTool->GetShape( theLabels.Value( 1 ) );
497 TopoDS_Compound aCompound;
498 BRep_Builder aBuilder;
499 aBuilder.MakeCompound( aCompound );
501 for( TDF_LabelSequence::Iterator anIt( theLabels ); anIt.More(); anIt.Next() )
503 TopoDS_Shape aFreeShape;
505 if( !aShapeTool->GetShape( anIt.Value(), aFreeShape ) )
508 aBuilder.Add( aCompound, aFreeShape );
511 if( aCompound.NbChildren() > 0 )
520static Standard_Boolean
rescaleShapes(
const TDF_Label& theLabel,
const gp_XYZ& aScale )
522 if( theLabel.IsNull() )
524 Message::SendFail(
"Null label." );
525 return Standard_False;
528 if( Abs( aScale.X() ) <= gp::Resolution() || Abs( aScale.Y() ) <= gp::Resolution()
529 || Abs( aScale.Z() ) <= gp::Resolution() )
531 Message::SendFail(
"Scale factor is too small." );
532 return Standard_False;
535 Handle( XCAFDoc_ShapeTool ) aShapeTool = XCAFDoc_DocumentTool::ShapeTool( theLabel );
536 if( aShapeTool.IsNull() )
538 Message::SendFail(
"Couldn't find XCAFDoc_ShapeTool attribute." );
539 return Standard_False;
542 Handle( KI_XCAFDoc_AssemblyGraph ) aG =
new KI_XCAFDoc_AssemblyGraph( theLabel );
545 Message::SendFail(
"Couldn't create assembly graph." );
546 return Standard_False;
549 Standard_Boolean anIsDone = Standard_True;
553 aGTrsf.SetVectorialPart( gp_Mat( aScale.X(), 0, 0,
555 0, 0, aScale.Z() ) );
558 BRepBuilderAPI_GTransform aBRepTrsf( aGTrsf );
560 for( Standard_Integer idx = 1; idx <= aG->NbNodes(); idx++ )
562 const KI_XCAFDoc_AssemblyGraph::NodeType aNodeType = aG->GetNodeType( idx );
564 if( ( aNodeType != KI_XCAFDoc_AssemblyGraph::NodeType_Part )
565 && ( aNodeType != KI_XCAFDoc_AssemblyGraph::NodeType_Occurrence ) )
570 const TDF_Label& aLabel = aG->GetNode( idx );
572 if( aNodeType == KI_XCAFDoc_AssemblyGraph::NodeType_Part )
574 const TopoDS_Shape aShape = aShapeTool->GetShape( aLabel );
575 aBRepTrsf.Perform( aShape, Standard_True );
576 if( !aBRepTrsf.IsDone() )
578 Standard_SStream aSS;
579 TCollection_AsciiString anEntry;
580 TDF_Tool::Entry( aLabel, anEntry );
581 aSS <<
"Shape " << anEntry <<
" is not scaled!";
582 Message::SendFail( aSS.str().c_str() );
583 anIsDone = Standard_False;
584 return Standard_False;
586 TopoDS_Shape aScaledShape = aBRepTrsf.Shape();
587 aShapeTool->SetShape( aLabel, aScaledShape );
590 TDF_LabelSequence aSubshapes;
591 aShapeTool->GetSubShapes( aLabel, aSubshapes );
592 for( TDF_LabelSequence::Iterator anItSs( aSubshapes ); anItSs.More(); anItSs.Next() )
594 const TDF_Label& aLSs = anItSs.Value();
595 const TopoDS_Shape aSs = aShapeTool->GetShape( aLSs );
596 const TopoDS_Shape aSs1 = aBRepTrsf.ModifiedShape( aSs );
597 aShapeTool->SetShape( aLSs, aSs1 );
601 aLabel.ForgetAttribute( XCAFDoc_Area::GetID() );
602 aLabel.ForgetAttribute( XCAFDoc_Centroid::GetID() );
603 aLabel.ForgetAttribute( XCAFDoc_Volume::GetID() );
605 else if( aNodeType == KI_XCAFDoc_AssemblyGraph::NodeType_Occurrence )
607 TopLoc_Location aLoc = aShapeTool->GetLocation( aLabel );
608 gp_Trsf aTrsf = aLoc.Transformation();
609 aTrsf.SetTranslationPart( aTrsf.TranslationPart().Multiplied( aScale ) );
610 XCAFDoc_Location::Set( aLabel, aTrsf );
616 return Standard_False;
619 aShapeTool->UpdateAssemblies();
625static bool fuseShapes(
auto& aInputShapes, TopoDS_Shape& aOutShape )
627 BRepAlgoAPI_Fuse mkFuse;
628 TopTools_ListOfShape shapeArguments, shapeTools;
630 for( TopoDS_Shape& sh : aInputShapes )
635 if( shapeArguments.IsEmpty() )
636 shapeArguments.Append( sh );
638 shapeTools.Append( sh );
641 mkFuse.SetRunParallel(
true );
642 mkFuse.SetToFillHistory(
false );
643 mkFuse.SetArguments( shapeArguments );
644 mkFuse.SetTools( shapeTools );
647 if( mkFuse.HasErrors() || mkFuse.HasWarnings() )
651 if( mkFuse.HasErrors() )
654 mkFuse.DumpErrors( std::cout );
657 if( mkFuse.HasWarnings() )
660 mkFuse.DumpWarnings( std::cout );
666 if( mkFuse.IsDone() )
668 TopoDS_Shape fusedShape = mkFuse.Shape();
670 ShapeUpgrade_UnifySameDomain unify( fusedShape,
true,
true,
false );
671 unify.History() =
nullptr;
674 TopoDS_Shape unifiedShapes = unify.Shape();
676 if( unifiedShapes.IsNull() )
678 ReportMessage(
_(
"** ShapeUpgrade_UnifySameDomain produced a null shape **\n" ) );
682 aOutShape = unifiedShapes;
693 TopoDS_Compound compound;
694 BRep_Builder builder;
695 builder.MakeCompound( compound );
697 for( TopoDS_Shape& shape : aInputShapes )
698 builder.Add( compound, shape );
707 TopoDS_Shape outShape;
709 if( aInputShapes.Size() == 1 )
710 return aInputShapes.First();
721 const TCollection_ExtendedString& aPrefix )
723 Handle( KI_XCAFDoc_AssemblyGraph ) aG =
new KI_XCAFDoc_AssemblyGraph( aLabel );
727 Message::SendFail(
"Couldn't create assembly graph." );
728 return Standard_False;
731 Standard_Boolean anIsDone = Standard_True;
733 for( Standard_Integer idx = 1; idx <= aG->NbNodes(); idx++ )
735 const TDF_Label& lbl = aG->GetNode( idx );
736 Handle( TDataStd_Name ) nameHandle;
738 if( lbl.FindAttribute( TDataStd_Name::GetID(), nameHandle ) )
740 TCollection_ExtendedString
name;
744 name += nameHandle->Get();
747 TDataStd_Name::Set( lbl,
name );
751 TDataStd_Name::Set( lbl, aPrefix );
761 m_app = XCAFApp_Application::GetApplication();
762 m_app->NewDocument(
"MDTV-XCAF", m_doc );
763 m_assy = XCAFDoc_DocumentTool::ShapeTool( m_doc->Main() );
775 m_outFmt = OUTPUT_FORMAT::FMT_OUT_UNKNOWN;
781 if( m_doc->CanClose() == CDM_CCS_OK )
789 std::vector<TopoDS_Shape> padShapes;
802 double Zpos, thickness;
808 if( pcb_layer ==
F_Cu )
810 else if( pcb_layer ==
B_Cu )
814 TopoDS_Shape testShape;
822 if( testShape.IsNull() )
824 std::vector<TopoDS_Shape> testShapes;
828 if( testShapes.size() > 0 )
829 testShape = testShapes.front();
832 if( !aVia && !testShape.IsNull() )
834 if( pcb_layer ==
F_Cu || pcb_layer ==
B_Cu )
840 if( pcb_layer ==
F_Cu )
842 else if( pcb_layer ==
B_Cu )
859 double f_pos, f_thickness;
860 double b_pos, b_thickness;
863 double top = std::max( f_pos, f_pos + f_thickness );
864 double bottom = std::min( b_pos, b_pos + b_thickness );
866 TopoDS_Shape plating;
872 ( top - bottom ), bottom, aOrigin ) )
874 padShapes.push_back( plating );
883 ReportMessage( wxT(
"OCC error adding pad/via polygon.\n" ) );
888 TopTools_ListOfShape padShapesList;
890 for(
const TopoDS_Shape& shape : padShapes )
891 padShapesList.Append( shape );
897 for(
const TopoDS_Shape& shape : padShapes )
907 const VECTOR2D& aOrigin,
bool aCutCopper,
bool aCutBody )
909 double margin = 0.001;
917 double f_pos, f_thickness;
918 double b_pos, b_thickness;
921 double top = std::max( f_pos, f_pos + f_thickness );
922 double bottom = std::min( b_pos, b_pos + b_thickness );
924 double holeZsize = ( top - bottom ) + ( margin * 2 );
926 double boardDrill = aShape.
GetWidth();
927 double copperDrill = boardDrill - aPlatingThickness * 2;
929 TopoDS_Shape copperHole, boardHole;
934 holeZsize, bottom - margin, aOrigin ) )
947 holeZsize, bottom - margin, aOrigin ) )
964 double f_pos, f_thickness;
965 double b_pos, b_thickness;
968 double top = std::max( f_pos, f_pos + f_thickness );
969 double bottom = std::min( b_pos, b_pos + b_thickness );
971 TopoDS_Shape plating;
974 ( top - bottom ), bottom, aOrigin ) )
992 static const double c_silkscreenAboveCopper = 0.04;
993 static const double c_soldermaskAboveCopper = 0.015;
1001 double f_pos, f_thickness;
1003 double top = std::max( f_pos, f_pos + f_thickness );
1006 aZPos = top + c_silkscreenAboveCopper;
1008 aZPos = top + c_soldermaskAboveCopper;
1014 double b_pos, b_thickness;
1016 double bottom = std::min( b_pos, b_pos + b_thickness );
1019 aZPos = bottom - c_silkscreenAboveCopper;
1021 aZPos = bottom - c_soldermaskAboveCopper;
1029 double& aThickness )
1033 bool wasPrepreg =
false;
1038 for(
auto it = materials.rbegin(); it != materials.rend(); ++it )
1044 if( aLayer ==
B_Cu )
1089 double f_pos, f_thickness;
1090 double b_pos, b_thickness;
1093 double top = std::min( f_pos, f_pos + f_thickness );
1094 double bottom = std::max( b_pos, b_pos + b_thickness );
1096 aThickness = ( top - bottom );
1099 wxASSERT( aZPos == 0.0 );
1106 bool success =
true;
1114 double z_pos, thickness;
1125 wxString::Format( wxT(
"Could not add shape (%d points) to copper layer on %s.\n" ),
1139 if( aFileNameUTF8.empty() )
1141 ReportMessage( wxString::Format( wxT(
"No model defined for component %s.\n" ), aRefDes ) );
1145 wxString fileName( wxString::FromUTF8( aFileNameUTF8.c_str() ) );
1146 ReportMessage( wxString::Format( wxT(
"Adding component %s.\n" ), aRefDes ) );
1150 wxString errorMessage;
1152 if( !
getModelLabel( aFileNameUTF8, aScale, lmodel, aSubstituteModels, &errorMessage ) )
1154 if( errorMessage.IsEmpty() )
1155 ReportMessage( wxString::Format( wxT(
"No model for filename '%s'.\n" ), fileName ) );
1163 TopLoc_Location toploc;
1165 if( !
getModelLocation( aBottom, aPosition, aRotation, aOffset, aOrientation, toploc ) )
1168 wxString::Format( wxT(
"No location data for filename '%s'.\n" ), fileName ) );
1173 TDF_Label llabel = m_assy->AddComponent(
m_assy_label, lmodel, toploc );
1175 if( llabel.IsNull() )
1177 ReportMessage( wxString::Format( wxT(
"Could not add component with filename '%s'.\n" ),
1183 TCollection_ExtendedString refdes( aRefDes.c_str() );
1184 TDataStd_Name::Set( llabel, refdes );
1251 double aWidth,
double aThickness,
1252 double aZposition,
const VECTOR2D& aOrigin )
1259 double len = ( aEndPoint - aStartPoint ).EuclideanNorm();
1260 double h_width = aWidth/2.0;
1262 coords[0] =
VECTOR2D{ 0.0, h_width };
1265 coords[1] =
VECTOR2D{ len, h_width };
1268 coords[2] =
VECTOR2D{ len + h_width, 0.0 };
1271 coords[3] =
VECTOR2D{ len, -h_width };
1274 coords[4] =
VECTOR2D{ 0, -h_width };
1277 coords[5] =
VECTOR2D{ -h_width, 0.0 };
1280 EDA_ANGLE seg_angle( aEndPoint - aStartPoint );
1282 for(
int ii = 0; ii < 6; ii++ )
1285 coords[ii] += aStartPoint;
1290 gp_Pnt coords3D[ 6 ];
1292 for(
int ii = 0; ii < 6; ii++ )
1299 BRepBuilderAPI_MakeWire wire;
1300 bool success =
true;
1312 Handle( Geom_Circle ) circle = GC_MakeCircle( coords3D[1],
1317 edge = BRepBuilderAPI_MakeEdge( circle );
1322 edge = BRepBuilderAPI_MakeEdge( coords3D[0], coords3D[1] );
1325 Handle( Geom_TrimmedCurve ) arcOfCircle =
1326 GC_MakeArcOfCircle( coords3D[1],
1330 edge = BRepBuilderAPI_MakeEdge( arcOfCircle );
1333 edge = BRepBuilderAPI_MakeEdge( coords3D[3], coords3D[4] );
1336 Handle( Geom_TrimmedCurve ) arcOfCircle2 =
1337 GC_MakeArcOfCircle( coords3D[4],
1341 edge = BRepBuilderAPI_MakeEdge( arcOfCircle2 );
1345 catch(
const Standard_Failure& e )
1347 ReportMessage( wxString::Format( wxT(
"build shape segment: OCC exception: %s\n" ),
1348 e.GetMessageString() ) );
1352 BRepBuilderAPI_MakeFace face;
1356 gp_Pln plane( coords3D[0], gp::DZ() );
1357 face = BRepBuilderAPI_MakeFace( plane, wire );
1359 catch(
const Standard_Failure& e )
1361 ReportMessage( wxString::Format( wxT(
"MakeShapeThickSegment: OCC exception: %s\n" ),
1362 e.GetMessageString() ) );
1366 if( aThickness != 0.0 )
1368 aShape = BRepPrimAPI_MakePrism( face, gp_Vec( 0, 0, aThickness ) );
1370 if( aShape.IsNull() )
1372 ReportMessage( wxT(
"failed to create a prismatic shape\n" ) );
1390 str <<
"x0: " <<
up.StringFromValue( aBBox.
GetLeft(),
false ) <<
"; ";
1391 str <<
"y0: " <<
up.StringFromValue( aBBox.
GetTop(),
false ) <<
"; ";
1392 str <<
"x1: " <<
up.StringFromValue( aBBox.
GetRight(),
false ) <<
"; ";
1393 str <<
"y1: " <<
up.StringFromValue( aBBox.
GetBottom(),
false );
1400 double aMergeOCCMaxDist,
double aZposition,
const VECTOR2D& aOrigin )
1402 auto toPoint = [&](
const VECTOR2D& aKiCoords ) -> gp_Pnt
1415 gp_Pnt start = toPoint( aPt0 );
1416 gp_Pnt end = toPoint( aPt1 );
1418 BRepBuilderAPI_MakeEdge mkEdge( start, end );
1420 if( !mkEdge.IsDone() || mkEdge.Edge().IsNull() )
1422 ReportMessage( wxString::Format( wxT(
"failed to make segment edge at (%d "
1423 "%d) -> (%d %d), skipping\n" ),
1424 aPt0.
x, aPt0.
y, aPt1.x, aPt1.y ) );
1428 aMkWire.Add( mkEdge.Edge() );
1430 if( aMkWire.Error() != BRepLib_WireDone )
1432 ReportMessage( wxString::Format( wxT(
"failed to add segment edge "
1433 "at (%d %d) -> (%d %d)\n" ),
1434 aPt0.
x, aPt0.
y, aPt1.x, aPt1.y ) );
1445 Handle( Geom_Curve ) curve;
1447 if( aArc.GetCentralAngle() ==
ANGLE_360 )
1449 gp_Ax2 axis = gp::XOY();
1450 axis.SetLocation( toPoint( aArc.GetCenter() ) );
1452 curve = GC_MakeCircle( axis,
pcbIUScale.
IUTomm( aArc.GetRadius() ) ).Value();
1456 curve = GC_MakeArcOfCircle( toPoint( aPt0 ), toPoint( aArc.GetArcMid() ),
1457 toPoint( aArc.GetP1() ) )
1461 if( curve.IsNull() )
1464 aMkWire.Add( BRepBuilderAPI_MakeEdge( curve ) );
1466 if( !aMkWire.IsDone() )
1469 wxT(
"failed to add arc curve from (%d %d), arc p0 "
1470 "(%d %d), mid (%d %d), p1 (%d %d)\n" ),
1471 aPt0.
x, aPt0.
y, aArc.GetP0().x, aArc.GetP0().y, aArc.GetArcMid().x,
1472 aArc.GetArcMid().y, aArc.GetP1().x, aArc.GetP1().y ) );
1481 bool isFirstShape =
true;
1494 if( nextShape != -1 )
1500 lastPt = aChain.
CPoint( i );
1510 firstPt = currentArc.
GetP0();
1515 lastPt = currentArc.
GetP0();
1517 if( addArc( lastPt, currentArc ) )
1518 lastPt = currentArc.
GetP1();
1537 isFirstShape =
false;
1540 if( lastPt != firstPt && !
addSegment( lastPt, firstPt ) )
1543 wxString::Format( wxT(
"** Failed to close wire at %d, %d -> %d, %d **\n" ),
1544 lastPt.
x, lastPt.
y, firstPt.
x, firstPt.
y ) );
1549 catch(
const Standard_Failure& e )
1551 ReportMessage( wxString::Format( wxT(
"makeWireFromChain: OCC exception: %s\n" ),
1552 e.GetMessageString() ) );
1561 double aThickness,
double aZposition,
const VECTOR2D& aOrigin )
1568 if( aConvertToArcs )
1572 for(
size_t polyId = 0; polyId < approximated.
CPolygons().size(); polyId++ )
1576 for(
size_t contId = 0; contId < polygon.size(); contId++ )
1579 polygon[contId] = approxChain;
1583 fallbackPoly = workingPoly;
1584 workingPoly = approximated;
1603 auto toPoint = [&](
const VECTOR2D& aKiCoords ) -> gp_Pnt
1610 gp_Pln basePlane( gp_Pnt( 0.0, 0.0, aZposition ),
1611 std::signbit( aThickness ) ? -gp::DZ() : gp::DZ() );
1613 for(
size_t polyId = 0; polyId < workingPoly.
CPolygons().size(); polyId++ )
1617 auto tryMakeWire = [
this, &aZposition,
1621 BRepLib_MakeWire mkWire;
1625 if( mkWire.IsDone() )
1627 wire = mkWire.Wire();
1632 wxString::Format(
_(
"Wire not done (contour points %d): OCC error %d\n" ),
1633 static_cast<int>( aContour.PointCount() ),
1634 static_cast<int>( mkWire.Error() ) ) );
1636 ReportMessage( wxString::Format(
_(
"z: %g; bounding box: %s\n" ), aZposition,
1640 if( !wire.IsNull() )
1642 BRepAlgoAPI_Check check( wire,
false,
true );
1644 if( !check.IsValid() )
1646 ReportMessage( wxString::Format(
_(
"\nWire self-interference check "
1649 ReportMessage( wxString::Format(
_(
"z: %g; bounding box: %s\n" ), aZposition,
1659 BRepBuilderAPI_MakeFace mkFace;
1661 for(
size_t contId = 0; contId < polygon.size(); contId++ )
1665 TopoDS_Wire wire = tryMakeWire( polygon[contId] );
1667 if( aConvertToArcs && wire.IsNull() )
1669 ReportMessage( wxString::Format(
_(
"Using non-simplified polygon.\n" ) ) );
1672 wire = tryMakeWire( fallbackPoly.
CPolygon( polyId )[contId] );
1677 if( !wire.IsNull() )
1679 if( basePlane.Axis().Direction().Z() < 0 )
1682 mkFace = BRepBuilderAPI_MakeFace( basePlane, wire );
1686 ReportMessage( wxString::Format( wxT(
"\n** Outline skipped **\n" ) ) );
1688 ReportMessage( wxString::Format( wxT(
"z: %g; bounding box: %s\n" ),
1697 if( !wire.IsNull() )
1699 if( basePlane.Axis().Direction().Z() > 0 )
1706 ReportMessage( wxString::Format( wxT(
"\n** Hole skipped **\n" ) ) );
1708 ReportMessage( wxString::Format( wxT(
"z: %g; bounding box: %s\n" ),
1714 catch(
const Standard_Failure& e )
1717 wxString::Format( wxT(
"MakeShapes (contour %d): OCC exception: %s\n" ),
1718 static_cast<int>( contId ), e.GetMessageString() ) );
1723 if( mkFace.IsDone() )
1725 TopoDS_Shape faceShape = mkFace.Shape();
1727 if( aThickness != 0.0 )
1729 TopoDS_Shape prism = BRepPrimAPI_MakePrism( faceShape, gp_Vec( 0, 0, aThickness ) );
1730 aShapes.push_back( prism );
1732 if( prism.IsNull() )
1740 aShapes.push_back( faceShape );
1745 ReportMessage( wxString::Format(
_(
"** Face skipped **\n" ) ) );
1756 {
_HKI(
"Green" ), wxColor( 20, 51, 36 ) },
1757 {
_HKI(
"Red" ), wxColor( 181, 19, 21 ) },
1758 {
_HKI(
"Blue" ), wxColor( 2, 59, 162 ) },
1759 {
_HKI(
"Purple" ), wxColor( 32, 2, 53 ) },
1760 {
_HKI(
"Black" ), wxColor( 11, 11, 11 ) },
1761 {
_HKI(
"White" ), wxColor( 245, 245, 245 ) },
1762 {
_HKI(
"Yellow" ), wxColor( 194, 195, 0 ) },
1763 {
_HKI(
"User defined" ), wxColor( 128, 128, 128 ) }
1773 if( aColorStr.StartsWith( wxT(
"#" ) ) )
1775 aColorOut =
COLOR4D( aColorStr );
1780 const std::vector<FAB_LAYER_COLOR>& colors =
1787 if( fabColor.GetName() == aColorStr )
1789 aColorOut = fabColor.GetColor( aType );
1809 Handle( XCAFDoc_VisMaterialTool ) visMatTool =
1810 XCAFDoc_DocumentTool::VisMaterialTool( m_doc->Main() );
1815 ReportMessage( wxString::Format( wxT(
"Build board outlines (%d outlines) with %d points.\n" ),
1818 double boardThickness;
1833 wxT(
"OCC error creating main outline.\n" ) ) );
1839 for(
size_t contId = 0; contId < polygon.size(); contId++ )
1843 polyset.
Append( contour );
1850 ReportMessage( wxT(
"OCC error creating main outline.\n" ) );
1858 ReportMessage( wxT(
"OCC error creating hole in main outline.\n" ) );
1869 BRepBndLib::Add( brdShape, brdBndBox );
1872 ReportMessage( wxString::Format( wxT(
"Build board cutouts and holes (%d hole(s)).\n" ),
1875 auto buildBSB = [&brdBndBox]( std::vector<TopoDS_Shape>& input, Bnd_BoundSortBox& bsbHoles )
1879 Bnd_Box brdWithHolesBndBox = brdBndBox;
1881 Handle( Bnd_HArray1OfBox ) holeBoxSet =
new Bnd_HArray1OfBox( 0, input.size() - 1 );
1883 for(
size_t i = 0; i < input.size(); i++ )
1886 BRepBndLib::Add( input[i], bbox );
1887 brdWithHolesBndBox.Add( bbox );
1888 ( *holeBoxSet )[i] = bbox;
1891 bsbHoles.Initialize( brdWithHolesBndBox, holeBoxSet );
1894 auto subtractShapes = [](
const wxString& aWhat, std::vector<TopoDS_Shape>& aShapesList,
1895 std::vector<TopoDS_Shape>& aHolesList, Bnd_BoundSortBox& aBSBHoles )
1899 for( TopoDS_Shape& shape : aShapesList )
1902 BRepBndLib::Add( shape, shapeBbox );
1904 const TColStd_ListOfInteger& indices = aBSBHoles.Compare( shapeBbox );
1906 TopTools_ListOfShape holelist;
1908 for(
const Standard_Integer& index : indices )
1909 holelist.Append( aHolesList[index] );
1912 ReportMessage( wxString::Format(
_(
"Build holes for %s\n" ), aWhat ) );
1918 (
int) aShapesList.size(), aWhat ) );
1920 if( holelist.IsEmpty() )
1923 TopTools_ListOfShape cutArgs;
1924 cutArgs.Append( shape );
1926 BRepAlgoAPI_Cut
cut;
1928 cut.SetRunParallel(
true );
1929 cut.SetToFillHistory(
false );
1931 cut.SetArguments( cutArgs );
1932 cut.SetTools( holelist );
1935 if(
cut.HasErrors() ||
cut.HasWarnings() )
1938 _(
"\n** Got problems while cutting %s number %d **\n" ), aWhat, cnt ) );
1941 if(
cut.HasErrors() )
1944 cut.DumpErrors( std::cout );
1947 if(
cut.HasWarnings() )
1950 cut.DumpWarnings( std::cout );
1956 shape =
cut.Shape();
1962 Bnd_BoundSortBox bsbHoles;
1970 Bnd_BoundSortBox bsbHoles;
1981 TopTools_ListOfShape shapesToFuse;
1984 shapesToFuse.Append( shape );
1987 shapesToFuse.Append( shape );
1990 shapesToFuse.Append( shape );
1994 if( !fusedShape.IsNull() )
2014 auto pushToAssembly = [&]( std::vector<TopoDS_Shape>& aShapesList, Quantity_ColorRGBA aColor,
2015 const TDF_Label& aVisMatLabel,
const wxString& aShapeName,
2018 if( aShapesList.empty() )
2021 std::vector<TopoDS_Shape> newList;
2026 newList = aShapesList;
2029 for( TopoDS_Shape& shape : newList )
2031 Handle( TDataStd_TreeNode ) node;
2034 TDF_Label lbl = m_assy->AddComponent(
m_assy_label, shape,
false );
2040 lbl.FindAttribute( XCAFDoc::ShapeRefGUID(), node );
2041 TDF_Label shpLbl = node->Father()->Label();
2042 if( !shpLbl.IsNull() )
2044 if( visMatTool && !aVisMatLabel.IsNull() )
2045 visMatTool->SetShapeMaterial( shpLbl, aVisMatLabel );
2049 if( newList.size() > 1 )
2051 shapeName = wxString::Format( wxT(
"%s_%s_%d" ),
m_pcbName, aShapeName, i );
2055 shapeName = wxString::Format( wxT(
"%s_%s" ),
m_pcbName, aShapeName );
2059 TCollection_ExtendedString partname( shapeName.ToUTF8().data() );
2060 TDataStd_Name::Set( shpLbl, partname );
2067 auto makeMaterial = [&](
const TCollection_AsciiString& aName,
2068 const Quantity_ColorRGBA& aBaseColor,
double aMetallic,
2069 double aRoughness ) -> TDF_Label
2071 Handle( XCAFDoc_VisMaterial ) vismat =
new XCAFDoc_VisMaterial;
2072 XCAFDoc_VisMaterialPBR pbr;
2073 pbr.BaseColor = aBaseColor;
2074 pbr.Metallic = aMetallic;
2075 pbr.Roughness = aRoughness;
2076 vismat->SetPbrMaterial( pbr );
2077 return visMatTool->AddMaterial( vismat, aName );
2084 Quantity_ColorRGBA board_color( 0.3f, 0.3f, 0.3f, 1.0f );
2085 Quantity_ColorRGBA silk_color( 1.0f, 1.0f, 1.0f, 0.9f );
2086 Quantity_ColorRGBA mask_color( 0.08f, 0.2f, 0.14f, 0.83f );
2096 if( item->GetBrdLayerId() ==
F_Mask || item->GetBrdLayerId() ==
B_Mask )
2099 mask_color.SetValues( col.
r, col.
g, col.
b, col.
a );
2102 if( item->GetBrdLayerId() ==
F_SilkS || item->GetBrdLayerId() ==
B_SilkS )
2103 silk_color.SetValues( col.
r, col.
g, col.
b, col.
a );
2106 board_color.SetValues( col.
r, col.
g, col.
b, col.
a );
2111 board_color = mask_color;
2112 board_color.SetAlpha( 1.0 );
2115 TDF_Label mask_mat = makeMaterial(
"soldermask", mask_color, 0.0, 0.6 );
2116 TDF_Label silk_mat = makeMaterial(
"silkscreen", silk_color, 0.0, 0.9 );
2117 TDF_Label copper_mat = makeMaterial(
"copper", copper_color, 1.0, 0.4 );
2118 TDF_Label pad_mat = makeMaterial(
"pad", pad_color, 1.0, 0.4 );
2119 TDF_Label board_mat = makeMaterial(
"board", board_color, 0.0, 0.8 );
2121 pushToAssembly(
m_board_copper, copper_color, copper_mat,
"copper",
true );
2128 if( aPushBoardBody )
2131#if( defined OCC_VERSION_HEX ) && ( OCC_VERSION_HEX > 0x070101 )
2132 m_assy->UpdateAssemblies();
2141bool STEP_PCB_MODEL::WriteIGES(
const wxString& aFileName )
2145 ReportMessage( wxString::Format( wxT(
"No valid PCB assembly; cannot create output file "
2151 m_outFmt = OUTPUT_FORMAT::FMT_OUT_IGES;
2153 wxFileName fn( aFileName );
2154 IGESControl_Controller::Init();
2155 IGESCAFControl_Writer writer;
2156 writer.SetColorMode( Standard_True );
2157 writer.SetNameMode( Standard_True );
2158 IGESData_GlobalSection header = writer.Model()->GlobalSection();
2159 header.SetFileName(
new TCollection_HAsciiString( fn.GetFullName().ToAscii() ) );
2160 header.SetSendName(
new TCollection_HAsciiString(
"KiCad electronic assembly" ) );
2161 header.SetAuthorName(
2162 new TCollection_HAsciiString( Interface_Static::CVal(
"write.iges.header.author" ) ) );
2163 header.SetCompanyName(
2164 new TCollection_HAsciiString( Interface_Static::CVal(
"write.iges.header.company" ) ) );
2165 writer.Model()->SetGlobalSection( header );
2167 if( Standard_False == writer.Perform( m_doc, aFileName.c_str() ) )
2179 ReportMessage( wxString::Format( wxT(
"No valid PCB assembly; cannot create output file "
2185 m_outFmt = OUTPUT_FORMAT::FMT_OUT_STEP;
2187 wxFileName fn( aFileName );
2189 STEPCAFControl_Writer writer;
2190 writer.SetColorMode( Standard_True );
2191 writer.SetNameMode( Standard_True );
2198 if( !Interface_Static::SetCVal(
"write.step.product.name", fn.GetName().ToAscii() ) )
2199 ReportMessage( wxT(
"Failed to set step product name, but will attempt to continue." ) );
2203 if( !Interface_Static::SetIVal(
"write.surfacecurve.mode", aOptimize ? 0 : 1 ) )
2204 ReportMessage( wxT(
"Failed to set surface curve mode, but will attempt to continue." ) );
2206 if( Standard_False == writer.Transfer( m_doc, STEPControl_AsIs ) )
2209 APIHeaderSection_MakeHeader hdr( writer.ChangeWriter().Model() );
2213 hdr.SetName(
new TCollection_HAsciiString( fn.GetFullName().ToAscii() ) );
2216 hdr.SetAuthorValue( 1,
new TCollection_HAsciiString(
"Pcbnew" ) );
2217 hdr.SetOrganizationValue( 1,
new TCollection_HAsciiString(
"Kicad" ) );
2218 hdr.SetOriginatingSystem(
new TCollection_HAsciiString(
"KiCad to STEP converter" ) );
2219 hdr.SetDescriptionValue( 1,
new TCollection_HAsciiString(
"KiCad electronic assembly" ) );
2221 bool success =
true;
2224 wxString currCWD = wxGetCwd();
2225 wxString workCWD = fn.GetPath();
2227 if( !workCWD.IsEmpty() )
2228 wxSetWorkingDirectory( workCWD );
2230 char tmpfname[] =
"$tempfile$.step";
2232 if( Standard_False == writer.Write( tmpfname ) )
2241 if( !wxRenameFile( tmpfname, fn.GetFullName(),
true ) )
2243 ReportMessage( wxString::Format( wxT(
"Cannot rename temporary file '%s' to '%s'.\n" ),
2245 fn.GetFullName() ) );
2250 wxSetWorkingDirectory( currCWD );
2260 ReportMessage( wxString::Format( wxT(
"No valid PCB assembly; cannot create output file "
2266 m_outFmt = OUTPUT_FORMAT::FMT_OUT_BREP;
2269 Handle( XCAFDoc_ShapeTool ) s_assy = XCAFDoc_DocumentTool::ShapeTool( m_doc->Main() );
2274 wxFileName fn( aFileName );
2276 wxFFileOutputStream ffStream( fn.GetFullPath() );
2277 wxStdOutputStream stdStream( ffStream );
2279#if OCC_VERSION_HEX >= 0x070600
2280 BRepTools::Write( shape, stdStream,
false,
false, TopTools_FormatVersion_VERSION_1 );
2282 BRepTools::Write( shape, stdStream );
2291 wxFileName fn( aFileName );
2293 wxFFileOutputStream ffStream( fn.GetFullPath() );
2294 wxStdOutputStream file( ffStream );
2296 if( !ffStream.IsOk() )
2298 ReportMessage( wxString::Format(
"Could not open file '%s'", fn.GetFullPath() ) );
2302 m_outFmt = OUTPUT_FORMAT::FMT_OUT_XAO;
2305 Handle( XCAFDoc_ShapeTool ) s_assy = XCAFDoc_DocumentTool::ShapeTool( m_doc->Main() );
2310 std::map<wxString, std::vector<int>> groups[4];
2311 std::map<wxString, double> groupAreas;
2312 TopExp_Explorer exp;
2315 for( exp.Init( shape, TopAbs_FACE ); exp.More(); exp.Next() )
2317 TopoDS_Shape subShape = exp.Current();
2320 BRepBndLib::Add( subShape, bbox );
2324 const auto& [point, padTestShape] = pair;
2326 if( bbox.IsOut( point ) )
2329 BRepAdaptor_Surface surface( TopoDS::Face( subShape ) );
2331 if( surface.GetType() != GeomAbs_Plane )
2334 BRepExtrema_DistShapeShape dist( padTestShape, subShape );
2337 if( !dist.IsDone() )
2340 if( dist.Value() < Precision::Approximation() )
2343 groups[2][padKey].push_back( faceIndex );
2345 GProp_GProps system;
2346 BRepGProp::SurfaceProperties( subShape, system );
2348 double surfaceArea = system.Mass() / 1e6;
2349 groupAreas[padKey] = surfaceArea;
2357 file <<
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>" << std::endl;
2358 file <<
"<XAO version=\"1.0\" author=\"KiCad\">" << std::endl;
2359 file <<
" <geometry name=\"" << fn.GetName() <<
"\">" << std::endl;
2360 file <<
" <shape format=\"BREP\"><![CDATA[";
2361#if OCC_VERSION_HEX < 0x070600
2362 BRepTools::Write( shape, file );
2364 BRepTools::Write( shape, file, Standard_True, Standard_True, TopTools_FormatVersion_VERSION_1 );
2366 file <<
"]]></shape>" << std::endl;
2367 file <<
" <topology>" << std::endl;
2369 TopTools_IndexedMapOfShape mainMap;
2370 TopExp::MapShapes( shape, mainMap );
2371 std::set<int> topo[4];
2373 static const TopAbs_ShapeEnum c_dimShapeTypes[] = { TopAbs_VERTEX, TopAbs_EDGE, TopAbs_FACE,
2376 static const std::string c_dimLabel[] = {
"vertex",
"edge",
"face",
"solid" };
2377 static const std::string c_dimLabels[] = {
"vertices",
"edges",
"faces",
"solids" };
2379 for(
int dim = 0; dim < 4; dim++ )
2381 for( exp.Init( shape, c_dimShapeTypes[dim] ); exp.More(); exp.Next() )
2383 TopoDS_Shape subShape = exp.Current();
2384 int idx = mainMap.FindIndex( subShape );
2386 if( idx && !topo[dim].count( idx ) )
2387 topo[dim].insert( idx );
2391 for(
int dim = 0; dim <= 3; dim++ )
2393 std::string labels = c_dimLabels[dim];
2394 std::string label = c_dimLabel[dim];
2396 file <<
" <" << labels <<
" count=\"" << topo[dim].size() <<
"\">" << std::endl;
2399 for(
auto p : topo[dim] )
2401 std::string
name(
"" );
2402 file <<
" <" << label <<
" index=\"" << index <<
"\" "
2403 <<
"name=\"" <<
name <<
"\" "
2404 <<
"reference=\"" << p <<
"\"/>" << std::endl;
2408 file <<
" </" << labels <<
">" << std::endl;
2411 file <<
" </topology>" << std::endl;
2412 file <<
" </geometry>" << std::endl;
2413 file <<
" <groups count=\""
2414 << groups[0].size() + groups[1].size() + groups[2].size() + groups[3].size() <<
"\">"
2417 int groupNumber = 1;
2422 for(
int dim = 0; dim <= 3; dim++ )
2424 std::string label = c_dimLabel[dim];
2426 for(
auto g : groups[dim] )
2429 wxString
name = g.first;
2433 std::ostringstream gs;
2434 gs <<
"G_" << dim <<
"D_" << g.first;
2437 file <<
" <group name=\"" <<
name <<
"\" dimension=\"" << label;
2443 file <<
"\" count=\"" << g.second.size() <<
"\">" << std::endl;
2444 for(
auto index : g.second )
2446 file <<
" <element index=\"" << index <<
"\"/>" << std::endl;
2448 file <<
" </group>" << std::endl;
2458 file <<
" </groups>" << std::endl;
2459 file <<
" <fields count=\"0\"/>" << std::endl;
2460 file <<
"</XAO>" << std::endl;
2467 bool aSubstituteModels, wxString* aErrorMessage )
2469 std::string model_key = aFileNameUTF8 +
"_" + std::to_string( aScale.
x )
2470 +
"_" + std::to_string( aScale.
y ) +
"_" + std::to_string( aScale.
z );
2472 MODEL_MAP::const_iterator mm =
m_models.find( model_key );
2476 aLabel = mm->second;
2482 Handle( TDocStd_Document ) doc;
2483 m_app->NewDocument(
"MDTV-XCAF", doc );
2485 wxString fileName( wxString::FromUTF8( aFileNameUTF8.c_str() ) );
2491 if( !
readIGES( doc, aFileNameUTF8.c_str() ) )
2493 ReportMessage( wxString::Format( wxT(
"readIGES() failed on filename '%s'.\n" ),
2500 if( !
readSTEP( doc, aFileNameUTF8.c_str() ) )
2502 ReportMessage( wxString::Format( wxT(
"readSTEP() failed on filename '%s'.\n" ),
2512 wxFFileInputStream ifile( fileName );
2513 wxFileName outFile( fileName );
2515 outFile.SetPath( wxStandardPaths::Get().GetTempDir() );
2516 outFile.SetExt( wxT(
"step" ) );
2517 wxFileOffset size = ifile.GetLength();
2519 if( size == wxInvalidOffset )
2521 ReportMessage( wxString::Format( wxT(
"getModelLabel() failed on filename '%s'.\n" ),
2527 bool success =
false;
2528 wxFFileOutputStream ofile( outFile.GetFullPath() );
2533 char* buffer =
new char[size];
2535 ifile.Read( buffer, size );
2536 std::string expanded;
2540 expanded = gzip::decompress( buffer, size );
2545 ReportMessage( wxString::Format( wxT(
"failed to decompress '%s'.\n" ),
2549 if( expanded.empty() )
2553 wxZipInputStream izipfile( ifile );
2554 std::unique_ptr<wxZipEntry> zip_file( izipfile.GetNextEntry() );
2556 if( zip_file && !zip_file->IsDir() && izipfile.CanRead() )
2558 izipfile.Read( ofile );
2564 ofile.Write( expanded.data(), expanded.size() );
2572 std::string altFileNameUTF8 =
TO_UTF8( outFile.GetFullPath() );
2593 if( aSubstituteModels )
2595 wxFileName wrlName( fileName );
2597 wxString basePath = wrlName.GetPath();
2598 wxString baseName = wrlName.GetName();
2606 alts.Add( wxT(
"stp" ) );
2607 alts.Add( wxT(
"step" ) );
2608 alts.Add( wxT(
"STP" ) );
2609 alts.Add( wxT(
"STEP" ) );
2610 alts.Add( wxT(
"Stp" ) );
2611 alts.Add( wxT(
"Step" ) );
2612 alts.Add( wxT(
"stpz" ) );
2613 alts.Add( wxT(
"stpZ" ) );
2614 alts.Add( wxT(
"STPZ" ) );
2615 alts.Add( wxT(
"step.gz" ) );
2616 alts.Add( wxT(
"stp.gz" ) );
2619 alts.Add( wxT(
"iges" ) );
2620 alts.Add( wxT(
"IGES" ) );
2621 alts.Add( wxT(
"igs" ) );
2622 alts.Add( wxT(
"IGS" ) );
2626 for(
const auto& alt : alts )
2628 wxFileName altFile( basePath, baseName + wxT(
"." ) + alt );
2630 if( altFile.IsOk() && altFile.FileExists() )
2632 std::string altFileNameUTF8 =
TO_UTF8( altFile.GetFullPath() );
2650 if(
m_outFmt == OUTPUT_FORMAT::FMT_OUT_GLTF )
2652 if(
readVRML( doc, aFileNameUTF8.c_str() ) )
2654 Handle( XCAFDoc_ShapeTool ) shapeTool =
2655 XCAFDoc_DocumentTool::ShapeTool( doc->Main() );
2658 TCollection_ExtendedString( baseName.c_str().AsChar() ) );
2662 ReportMessage( wxString::Format( wxT(
"readVRML() failed on filename '%s'.\n" ),
2671 aErrorMessage->Printf( wxT(
"Cannot load any VRML model for this export.\n" ) );
2681 ReportMessage( wxString::Format( wxT(
"Cannot identify actual file type for '%s'.\n" ),
2688 if( aLabel.IsNull() )
2690 ReportMessage( wxString::Format( wxT(
"Could not transfer model data from file '%s'.\n" ),
2697 wxFileName afile( fileName );
2698 std::string pname( afile.GetName().ToUTF8() );
2699 TCollection_ExtendedString partname( pname.c_str() );
2700 TDataStd_Name::Set( aLabel, partname );
2709 TopLoc_Location& aLocation )
2723 lPos.SetTranslation( gp_Vec( aPosition.
x, -aPosition.
y, 0.0 ) );
2728 double boardThickness;
2731 double top = std::max( boardZPos, boardZPos + boardThickness );
2732 double bottom = std::min( boardZPos, boardZPos + boardThickness );
2737 double f_pos, f_thickness;
2741 bottom += f_thickness;
2747 aOffset.
z -= bottom;
2748 lRot.SetRotation( gp_Ax1( gp_Pnt( 0.0, 0.0, 0.0 ), gp_Dir( 0.0, 0.0, 1.0 ) ), aRotation );
2749 lPos.Multiply( lRot );
2750 lRot.SetRotation( gp_Ax1( gp_Pnt( 0.0, 0.0, 0.0 ), gp_Dir( 1.0, 0.0, 0.0 ) ), M_PI );
2751 lPos.Multiply( lRot );
2756 lRot.SetRotation( gp_Ax1( gp_Pnt( 0.0, 0.0, 0.0 ), gp_Dir( 0.0, 0.0, 1.0 ) ), aRotation );
2757 lPos.Multiply( lRot );
2761 lOff.SetTranslation( gp_Vec( aOffset.
x, aOffset.
y, aOffset.
z ) );
2762 lPos.Multiply( lOff );
2765 lOrient.SetRotation( gp_Ax1( gp_Pnt( 0.0, 0.0, 0.0 ), gp_Dir( 0.0, 0.0, 1.0 ) ),
2767 lPos.Multiply( lOrient );
2768 lOrient.SetRotation( gp_Ax1( gp_Pnt( 0.0, 0.0, 0.0 ), gp_Dir( 0.0, 1.0, 0.0 ) ),
2770 lPos.Multiply( lOrient );
2771 lOrient.SetRotation( gp_Ax1( gp_Pnt( 0.0, 0.0, 0.0 ), gp_Dir( 1.0, 0.0, 0.0 ) ),
2773 lPos.Multiply( lOrient );
2775 aLocation = TopLoc_Location( lPos );
2782 IGESControl_Controller::Init();
2783 IGESCAFControl_Reader reader;
2784 IFSelect_ReturnStatus stat = reader.ReadFile( fname );
2786 if( stat != IFSelect_RetDone )
2790 if( !Interface_Static::SetIVal(
"read.precision.mode", 1 ) )
2794 if( !Interface_Static::SetRVal(
"read.precision.val",
USER_PREC ) )
2798 reader.SetColorMode(
true );
2799 reader.SetNameMode(
false );
2800 reader.SetLayerMode(
false );
2802 if( !reader.Transfer( doc ) )
2804 if( doc->CanClose() == CDM_CCS_OK )
2811 if( reader.NbShapes() < 1 )
2813 if( doc->CanClose() == CDM_CCS_OK )
2825 STEPCAFControl_Reader reader;
2826 IFSelect_ReturnStatus stat = reader.ReadFile( fname );
2828 if( stat != IFSelect_RetDone )
2832 if( !Interface_Static::SetIVal(
"read.precision.mode", 1 ) )
2836 if( !Interface_Static::SetRVal(
"read.precision.val",
USER_PREC ) )
2840 reader.SetColorMode(
true );
2841 reader.SetNameMode(
true );
2842 reader.SetLayerMode(
false );
2844 if( !reader.Transfer( doc ) )
2846 if( doc->CanClose() == CDM_CCS_OK )
2853 if( reader.NbRootsForTransfer() < 1 )
2855 if( doc->CanClose() == CDM_CCS_OK )
2867#if OCC_VERSION_HEX >= 0x070700
2868 VrmlAPI_CafReader reader;
2869 RWMesh_CoordinateSystemConverter conv;
2870 conv.SetInputLengthUnit( 2.54 );
2871 reader.SetCoordinateSystemConverter( conv );
2872 reader.SetDocument( doc );
2874 if( !reader.Perform( TCollection_AsciiString( fname ), Message_ProgressRange() ) )
2885 Handle( TDocStd_Document ) & dest,
VECTOR3D aScale )
2889 Handle( XCAFDoc_ShapeTool ) s_assy = XCAFDoc_DocumentTool::ShapeTool( source->Main() );
2892 TDF_LabelSequence frshapes;
2893 s_assy->GetFreeShapes( frshapes );
2896 Handle( XCAFDoc_ShapeTool ) d_assy = XCAFDoc_DocumentTool::ShapeTool( dest->Main() );
2899 TDF_Label d_targetLabel = d_assy->NewShape();
2901 if( frshapes.Size() == 1 )
2903 TDocStd_XLinkTool link;
2904 link.Copy( d_targetLabel, frshapes.First() );
2909 for( TDF_Label& s_shapeLabel : frshapes )
2911 TDF_Label d_component = d_assy->NewShape();
2913 TDocStd_XLinkTool link;
2914 link.Copy( d_component, s_shapeLabel );
2916 d_assy->AddComponent( d_targetLabel, d_component, TopLoc_Location() );
2920 if( aScale.
x != 1.0 || aScale.
y != 1.0 || aScale.
z != 1.0 )
2923 return d_targetLabel;
2929 TDF_LabelSequence freeShapes;
2930 aShapeTool->GetFreeShapes( freeShapes );
2936 for( Standard_Integer i = 1; i <= freeShapes.Length(); ++i )
2938 TDF_Label label = freeShapes.Value( i );
2940 aShapeTool->GetShape( label, shape );
2945 const Standard_Real linearDeflection = 0.14;
2946 const Standard_Real angularDeflection =
DEG2RAD( 30.0 );
2947 BRepMesh_IncrementalMesh mesh( shape, linearDeflection, Standard_False, angularDeflection,
2959 ReportMessage( wxString::Format( wxT(
"No valid PCB assembly; cannot create output file "
2965 m_outFmt = OUTPUT_FORMAT::FMT_OUT_GLTF;
2969 wxFileName fn( aFileName );
2971 const char* tmpGltfname =
"$tempfile$.glb";
2972 RWGltf_CafWriter cafWriter( tmpGltfname,
true );
2974 cafWriter.SetTransformationFormat( RWGltf_WriterTrsfFormat_Compact );
2975 cafWriter.ChangeCoordinateSystemConverter().SetInputLengthUnit( 0.001 );
2976 cafWriter.ChangeCoordinateSystemConverter().SetInputCoordinateSystem(
2977 RWMesh_CoordinateSystem_Zup );
2978#if OCC_VERSION_HEX >= 0x070700
2979 cafWriter.SetParallel(
true );
2981 TColStd_IndexedDataMapOfStringString metadata;
2983 metadata.Add( TCollection_AsciiString(
"pcb_name" ),
2984 TCollection_ExtendedString( fn.GetName().wc_str() ) );
2985 metadata.Add( TCollection_AsciiString(
"source_pcb_file" ),
2986 TCollection_ExtendedString( fn.GetFullName().wc_str() ) );
2987 metadata.Add( TCollection_AsciiString(
"generator" ),
2988 TCollection_AsciiString( wxString::Format( wxS(
"KiCad %s" ),
GetSemanticVersion() ).ToAscii() ) );
2989 metadata.Add( TCollection_AsciiString(
"generated_at" ),
2992 bool success =
true;
2995 wxString currCWD = wxGetCwd();
2996 wxString workCWD = fn.GetPath();
2998 if( !workCWD.IsEmpty() )
2999 wxSetWorkingDirectory( workCWD );
3001 success = cafWriter.Perform( m_doc, metadata, Message_ProgressRange() );
3008 if( !wxRenameFile( tmpGltfname, fn.GetFullName(),
true ) )
3010 ReportMessage( wxString::Format( wxT(
"Cannot rename temporary file '%s' to '%s'.\n" ),
3011 tmpGltfname, fn.GetFullName() ) );
3016 wxSetWorkingDirectory( currCWD );
3024#if OCC_VERSION_HEX < 0x070700
3025 ReportMessage( wxT(
"PLY export is not supported before OCCT 7.7.0\n" ) );
3031 ReportMessage( wxString::Format( wxT(
"No valid PCB assembly; cannot create output file "
3037 m_outFmt = OUTPUT_FORMAT::FMT_OUT_PLY;
3041 wxFileName fn( aFileName );
3043 const char* tmpFname =
"$tempfile$.ply";
3044 RWPly_CafWriter cafWriter( tmpFname );
3046 cafWriter.SetFaceId(
true );
3047 cafWriter.ChangeCoordinateSystemConverter().SetInputLengthUnit( 0.001 );
3048 cafWriter.ChangeCoordinateSystemConverter().SetInputCoordinateSystem(
3049 RWMesh_CoordinateSystem_Zup );
3051 TColStd_IndexedDataMapOfStringString metadata;
3053 metadata.Add( TCollection_AsciiString(
"pcb_name" ),
3054 TCollection_ExtendedString( fn.GetName().wc_str() ) );
3055 metadata.Add( TCollection_AsciiString(
"source_pcb_file" ),
3056 TCollection_ExtendedString( fn.GetFullName().wc_str() ) );
3057 metadata.Add( TCollection_AsciiString(
"generator" ),
3058 TCollection_AsciiString(
3060 metadata.Add( TCollection_AsciiString(
"generated_at" ),
3063 bool success =
true;
3066 wxString currCWD = wxGetCwd();
3067 wxString workCWD = fn.GetPath();
3069 if( !workCWD.IsEmpty() )
3070 wxSetWorkingDirectory( workCWD );
3072 success = cafWriter.Perform( m_doc, metadata, Message_ProgressRange() );
3079 if( !wxRenameFile( tmpFname, fn.GetFullName(),
true ) )
3081 ReportMessage( wxString::Format( wxT(
"Cannot rename temporary file '%s' to '%s'.\n" ),
3082 tmpFname, fn.GetFullName() ) );
3087 wxSetWorkingDirectory( currCWD );
3098 ReportMessage( wxString::Format( wxT(
"No valid PCB assembly; cannot create output file "
3104 m_outFmt = OUTPUT_FORMAT::FMT_OUT_STL;
3108 wxFileName fn( aFileName );
3110 const char* tmpFname =
"$tempfile$.stl";
3113 wxString currCWD = wxGetCwd();
3114 wxString workCWD = fn.GetPath();
3116 if( !workCWD.IsEmpty() )
3117 wxSetWorkingDirectory( workCWD );
3119 bool success = StlAPI_Writer().Write(
getOneShape( m_assy ), tmpFname );
3126 if( !wxRenameFile( tmpFname, fn.GetFullName(),
true ) )
3128 ReportMessage( wxString::Format( wxT(
"Cannot rename temporary file '%s' to '%s'.\n" ),
3129 tmpFname, fn.GetFullName() ) );
3134 wxSetWorkingDirectory( currCWD );
constexpr int ARC_HIGH_DEF
constexpr EDA_IU_SCALE pcbIUScale
bool IsPrmSpecified(const wxString &aPrmValue)
@ BS_ITEM_TYPE_SILKSCREEN
@ BS_ITEM_TYPE_DIELECTRIC
@ BS_ITEM_TYPE_SOLDERMASK
wxString GetSemanticVersion()
Get the semantic version string for KiCad defined inside the KiCadVersion.cmake file in the variable ...
const wxString & GetShortNetname() const
FOOTPRINT * GetParentFootprint() const
Manage one layer needed to make a physical board.
wxString GetTypeName() const
int GetSublayersCount() const
PCB_LAYER_ID GetBrdLayerId() const
int GetThickness(int aDielectricSubLayer=0) const
BOARD_STACKUP_ITEM_TYPE GetType() const
Manage layers needed to make a physical board.
const std::vector< BOARD_STACKUP_ITEM * > & GetList() const
constexpr coord_type GetLeft() const
constexpr coord_type GetRight() const
constexpr coord_type GetTop() const
constexpr coord_type GetBottom() const
A color representation with 4 components: red, green, blue, alpha.
COLOR4D & Darken(double aFactor)
Makes the color darker by a given factor.
LSET is a set of PCB_LAYER_IDs.
LSEQ Seq(const LSEQ &aSequence) const
Return an LSEQ from the union of this LSET and a desired sequence.
bool Contains(PCB_LAYER_ID aLayer) const
See if the layer set contains a PCB layer.
LSET GetLayerSet() const override
Return a std::bitset of all layers on which the item physically resides.
bool FlashLayer(int aLayer, bool aOnlyCheckIfPermitted=false) const
Check to see whether the pad should be flashed on the specific layer.
bool IsOnLayer(PCB_LAYER_ID aLayer) const override
Test to see if this object is on the given layer.
const VECTOR2I & GetDrillSize() const
PAD_ATTRIB GetAttribute() const
const wxString & GetNumber() const
void TransformShapeToPolygon(SHAPE_POLY_SET &aBuffer, PCB_LAYER_ID aLayer, int aClearance, int aMaxError, ERROR_LOC aErrorLoc=ERROR_INSIDE, bool ignoreLineWidth=false) const override
Convert the pad shape to a closed polygon.
std::shared_ptr< SHAPE_SEGMENT > GetEffectiveHoleShape() const override
Return a SHAPE_SEGMENT object representing the pad's hole.
const VECTOR2I & GetArcMid() const
const VECTOR2I & GetP1() const
const VECTOR2I & GetP0() const
const VECTOR2I & GetCenter() const
Represent a polyline containing arcs as well as line segments: A chain of connected line and/or arc s...
const SHAPE_ARC & Arc(size_t aArc) const
bool IsClosed() const override
void SetClosed(bool aClosed)
Mark the line chain as closed (i.e.
int PointCount() const
Return the number of points (vertices) in this line chain.
ssize_t ArcIndex(size_t aSegment) const
Return the arc index for the given segment index.
void Clear()
Remove all points from the line chain.
const SHAPE_LINE_CHAIN Slice(int aStartIndex, int aEndIndex=-1) const
Return a subset of this line chain containing the [start_index, end_index] range of points.
const std::optional< INTERSECTION > SelfIntersectingWithArcs() const
Check if the line chain is self-intersecting.
int NextShape(int aPointIndex) const
Return the vertex index of the next shape in the chain, or -1 if aPointIndex is the last shape.
void Append(int aX, int aY, bool aAllowDuplication=false)
Append a new point at the end of the line chain.
const VECTOR2I & CPoint(int aIndex) const
Return a reference to a given point in the line chain.
virtual size_t GetSegmentCount() const override
const SEG CSegment(int aIndex) const
Return a constant copy of the aIndex segment in the line chain.
bool IsArcSegment(size_t aSegment) const
void RemoveShape(int aPointIndex)
Remove the shape at the given index from the line chain.
bool IsArcStart(size_t aIndex) const
Represent a set of closed polygons.
bool IsEmpty() const
Return true if the set is empty (no polygons at all)
POLYGON & Polygon(int aIndex)
Return the aIndex-th subpolygon in the set.
int FullPointCount() const
Return the number of points in the shape poly set.
int Append(int x, int y, int aOutline=-1, int aHole=-1, bool aAllowDuplication=false)
Appends a vertex at the end of the given outline/hole (default: the last outline)
std::vector< SHAPE_LINE_CHAIN > POLYGON
represents a single polygon outline with holes.
void Simplify(POLYGON_MODE aFastMode)
Simplify the polyset (merges overlapping polys, eliminates degeneracy/self-intersections) For aFastMo...
int OutlineCount() const
Return the number of outlines in the set.
const POLYGON & CPolygon(int aIndex) const
const std::vector< POLYGON > & CPolygons() const
const SEG & GetSeg() const
OUTPUT_FORMAT m_outFmt
The current output format for created file.
void SetCopperColor(double r, double g, double b)
bool isBoardOutlineValid()
std::vector< TopoDS_Shape > m_board_silkscreen
bool CreatePCB(SHAPE_POLY_SET &aOutline, VECTOR2D aOrigin, bool aPushBoardBody)
bool MakeShapeAsThickSegment(TopoDS_Shape &aShape, VECTOR2D aStartPoint, VECTOR2D aEndPoint, double aWidth, double aThickness, double aZposition, const VECTOR2D &aOrigin)
Make a segment shape based on start and end point.
bool WritePLY(const wxString &aFileName)
std::vector< TopoDS_Shape > m_board_copper_vias
bool AddComponent(const std::string &aFileName, const std::string &aRefDes, bool aBottom, VECTOR2D aPosition, double aRotation, VECTOR3D aOffset, VECTOR3D aOrientation, VECTOR3D aScale, bool aSubstituteModels=true)
wxString m_pcbName
Name of the PCB, which will most likely be the file name of the path.
std::vector< TopoDS_Shape > m_boardCutouts
std::vector< TopoDS_Shape > m_board_soldermask
void getBoardBodyZPlacement(double &aZPos, double &aThickness)
std::vector< TopoDS_Shape > m_board_copper_fused
std::vector< TopoDS_Shape > m_board_copper
void SetFuseShapes(bool aValue)
bool WriteXAO(const wxString &aFileName)
bool WriteGLTF(const wxString &aFileName)
Write the assembly in binary GLTF Format.
std::vector< TDF_Label > m_pcb_labels
void getLayerZPlacement(const PCB_LAYER_ID aLayer, double &aZPos, double &aThickness)
STEP_PCB_MODEL(const wxString &aPcbName)
bool AddBarrel(const SHAPE_SEGMENT &aShape, PCB_LAYER_ID aLayerTop, PCB_LAYER_ID aLayerBot, bool aVia, const VECTOR2D &aOrigin)
std::vector< TopoDS_Shape > m_board_outlines
void SetSimplifyShapes(bool aValue)
bool readVRML(Handle(TDocStd_Document) &aDoc, const char *aFname)
void SetPadColor(double r, double g, double b)
void SetEnabledLayers(const LSET &aLayers)
bool performMeshing(Handle(XCAFDoc_ShapeTool) &aShapeTool)
bool readSTEP(Handle(TDocStd_Document) &aDoc, const char *aFname)
Handle(XCAFApp_Application) m_app
bool getModelLocation(bool aBottom, VECTOR2D aPosition, double aRotation, VECTOR3D aOffset, VECTOR3D aOrientation, TopLoc_Location &aLocation)
virtual ~STEP_PCB_MODEL()
bool WriteSTL(const wxString &aFileName)
void SetStackup(const BOARD_STACKUP &aStackup)
void SetNetFilter(const wxString &aFilter)
bool AddPadShape(const PAD *aPad, const VECTOR2D &aOrigin, bool aVia)
bool MakeShapes(std::vector< TopoDS_Shape > &aShapes, const SHAPE_POLY_SET &aPolySet, bool aConvertToArcs, double aThickness, double aZposition, const VECTOR2D &aOrigin)
Convert a SHAPE_POLY_SET to TopoDS_Shape's (polygonal vertical prisms, or flat faces)
bool getModelLabel(const std::string &aFileNameUTF8, VECTOR3D aScale, TDF_Label &aLabel, bool aSubstituteModels, wxString *aErrorMessage=nullptr)
Load a 3D model data.
bool readIGES(Handle(TDocStd_Document) &aDoc, const char *aFname)
bool AddHole(const SHAPE_SEGMENT &aShape, int aPlatingThickness, PCB_LAYER_ID aLayerTop, PCB_LAYER_ID aLayerBot, bool aVia, const VECTOR2D &aOrigin, bool aCutCopper, bool aCutBody)
bool AddPolygonShapes(const SHAPE_POLY_SET *aPolyShapes, PCB_LAYER_ID aLayer, const VECTOR2D &aOrigin)
bool WriteSTEP(const wxString &aFileName, bool aOptimize)
std::vector< TopoDS_Shape > m_copperCutouts
std::vector< TopoDS_Shape > m_board_copper_pads
std::map< wxString, std::pair< gp_Pnt, TopoDS_Shape > > m_pad_points
void getCopperLayerZPlacement(const PCB_LAYER_ID aLayer, double &aZPos, double &aThickness)
TDF_Label transferModel(Handle(TDocStd_Document)&source, Handle(TDocStd_Document) &dest, VECTOR3D aScale)
void OCCSetMergeMaxDistance(double aDistance=OCC_MAX_DISTANCE_TO_MERGE_POINTS)
bool WriteBREP(const wxString &aFileName)
T EuclideanNorm() const
Compute the Euclidean norm of the vector, which is defined as sqrt(x ** 2 + y ** 2).
static constexpr EDA_ANGLE ANGLE_360
void ReportMessage(const wxString &aMessage)
wxString LayerName(int aLayer)
Returns the default display name for a given layer.
bool IsFrontLayer(PCB_LAYER_ID aLayerId)
Layer classification: check if it's a front layer.
bool IsBackLayer(PCB_LAYER_ID aLayerId)
Layer classification: check if it's a back layer.
bool IsCopperLayer(int aLayerId)
Tests whether a layer is a copper layer.
PCB_LAYER_ID
A quick note on layer IDs:
This file contains miscellaneous commonly used macros and functions.
EDA_ANGLE abs(const EDA_ANGLE &aAngle)
static bool addSegment(VRML_LAYER &model, IDF_SEGMENT *seg, int icont, int iseg)
const std::vector< FAB_LAYER_COLOR > & GetStandardColors(BOARD_STACKUP_ITEM_TYPE aType)
wxString NotSpecifiedPrm()
static bool colorFromStackup(BOARD_STACKUP_ITEM_TYPE aType, const wxString &aColorStr, COLOR4D &aColorOut)
static TopoDS_Compound makeCompound(auto &aInputShapes)
static Standard_Boolean prefixNames(const TDF_Label &aLabel, const TCollection_ExtendedString &aPrefix)
static Standard_Boolean rescaleShapes(const TDF_Label &theLabel, const gp_XYZ &aScale)
static constexpr double BOARD_OFFSET
static wxString formatBBox(const BOX2I &aBBox)
static bool fuseShapes(auto &aInputShapes, TopoDS_Shape &aOutShape)
static std::vector< FAB_LAYER_COLOR > s_soldermaskColors
static TopoDS_Shape fuseShapesOrCompound(TopTools_ListOfShape &aInputShapes)
MODEL3D_FORMAT_TYPE fileType(const char *aFileName)
static bool makeWireFromChain(BRepLib_MakeWire &aMkWire, const SHAPE_LINE_CHAIN &aChain, double aMergeOCCMaxDist, double aZposition, const VECTOR2D &aOrigin)
static VECTOR2D CircleCenterFrom3Points(const VECTOR2D &p1, const VECTOR2D &p2, const VECTOR2D &p3)
static SHAPE_LINE_CHAIN approximateLineChainWithArcs(const SHAPE_LINE_CHAIN &aSrc)
static constexpr double USER_ANGLE_PREC
static constexpr double USER_PREC
static TopoDS_Shape getOneShape(Handle(XCAFDoc_ShapeTool) aShapeTool)
static constexpr double OCC_MAX_DISTANCE_TO_MERGE_POINTS
Default distance between points to treat them as separate ones (mm) 0.001 mm or less is a reasonable ...
std::pair< std::string, TDF_Label > MODEL_DATUM
static constexpr double ARC_TO_SEGMENT_MAX_ERROR_MM
void ReportMessage(const wxString &aMessage)
#define CLOSE_STREAM(var)
#define OPEN_ISTREAM(var, name)
wxString GetISO8601CurrentDateTime()
#define TO_UTF8(wxstring)
Convert a wxString to a UTF8 encoded C string for all wxWidgets build modes.
constexpr double IUTomm(int iu) const
constexpr int mmToIU(double mm) const
void RotatePoint(int *pX, int *pY, const EDA_ANGLE &aAngle)
Calculate the new point of coord coord pX, pY, for a rotation center 0, 0.
double DEG2RAD(double deg)
VECTOR2< int32_t > VECTOR2I
VECTOR3< double > VECTOR3D