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 )
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;
932 holeZsize, bottom - margin, aOrigin ) )
942 holeZsize, bottom - margin, aOrigin ) )
958 double f_pos, f_thickness;
959 double b_pos, b_thickness;
962 double top = std::max( f_pos, f_pos + f_thickness );
963 double bottom = std::min( b_pos, b_pos + b_thickness );
965 TopoDS_Shape plating;
968 ( top - bottom ), bottom, aOrigin ) )
986 static const double c_silkscreenAboveCopper = 0.04;
987 static const double c_soldermaskAboveCopper = 0.015;
995 double f_pos, f_thickness;
997 double top = std::max( f_pos, f_pos + f_thickness );
1000 aZPos = top + c_silkscreenAboveCopper;
1002 aZPos = top + c_soldermaskAboveCopper;
1008 double b_pos, b_thickness;
1010 double bottom = std::min( b_pos, b_pos + b_thickness );
1013 aZPos = bottom - c_silkscreenAboveCopper;
1015 aZPos = bottom - c_soldermaskAboveCopper;
1023 double& aThickness )
1027 bool wasPrepreg =
false;
1032 for(
auto it = materials.rbegin(); it != materials.rend(); ++it )
1038 if( aLayer ==
B_Cu )
1083 double f_pos, f_thickness;
1084 double b_pos, b_thickness;
1087 double top = std::min( f_pos, f_pos + f_thickness );
1088 double bottom = std::max( b_pos, b_pos + b_thickness );
1090 aThickness = ( top - bottom );
1093 wxASSERT( aZPos == 0.0 );
1100 bool success =
true;
1108 double z_pos, thickness;
1119 wxString::Format( wxT(
"Could not add shape (%d points) to copper layer on %s.\n" ),
1133 if( aFileNameUTF8.empty() )
1135 ReportMessage( wxString::Format( wxT(
"No model defined for component %s.\n" ), aRefDes ) );
1139 wxString fileName( wxString::FromUTF8( aFileNameUTF8.c_str() ) );
1140 ReportMessage( wxString::Format( wxT(
"Adding component %s.\n" ), aRefDes ) );
1144 wxString errorMessage;
1146 if( !
getModelLabel( aFileNameUTF8, aScale, lmodel, aSubstituteModels, &errorMessage ) )
1148 if( errorMessage.IsEmpty() )
1149 ReportMessage( wxString::Format( wxT(
"No model for filename '%s'.\n" ), fileName ) );
1157 TopLoc_Location toploc;
1159 if( !
getModelLocation( aBottom, aPosition, aRotation, aOffset, aOrientation, toploc ) )
1162 wxString::Format( wxT(
"No location data for filename '%s'.\n" ), fileName ) );
1167 TDF_Label llabel = m_assy->AddComponent(
m_assy_label, lmodel, toploc );
1169 if( llabel.IsNull() )
1171 ReportMessage( wxString::Format( wxT(
"Could not add component with filename '%s'.\n" ),
1177 TCollection_ExtendedString refdes( aRefDes.c_str() );
1178 TDataStd_Name::Set( llabel, refdes );
1245 double aWidth,
double aThickness,
1246 double aZposition,
const VECTOR2D& aOrigin )
1253 double len = ( aEndPoint - aStartPoint ).EuclideanNorm();
1254 double h_width = aWidth/2.0;
1256 coords[0] =
VECTOR2D{ 0.0, h_width };
1259 coords[1] =
VECTOR2D{ len, h_width };
1262 coords[2] =
VECTOR2D{ len + h_width, 0.0 };
1265 coords[3] =
VECTOR2D{ len, -h_width };
1268 coords[4] =
VECTOR2D{ 0, -h_width };
1271 coords[5] =
VECTOR2D{ -h_width, 0.0 };
1274 EDA_ANGLE seg_angle( aEndPoint - aStartPoint );
1276 for(
int ii = 0; ii < 6; ii++ )
1279 coords[ii] += aStartPoint;
1284 gp_Pnt coords3D[ 6 ];
1286 for(
int ii = 0; ii < 6; ii++ )
1293 BRepBuilderAPI_MakeWire wire;
1294 bool success =
true;
1306 Handle( Geom_Circle ) circle = GC_MakeCircle( coords3D[1],
1311 edge = BRepBuilderAPI_MakeEdge( circle );
1316 edge = BRepBuilderAPI_MakeEdge( coords3D[0], coords3D[1] );
1319 Handle( Geom_TrimmedCurve ) arcOfCircle =
1320 GC_MakeArcOfCircle( coords3D[1],
1324 edge = BRepBuilderAPI_MakeEdge( arcOfCircle );
1327 edge = BRepBuilderAPI_MakeEdge( coords3D[3], coords3D[4] );
1330 Handle( Geom_TrimmedCurve ) arcOfCircle2 =
1331 GC_MakeArcOfCircle( coords3D[4],
1335 edge = BRepBuilderAPI_MakeEdge( arcOfCircle2 );
1339 catch(
const Standard_Failure& e )
1341 ReportMessage( wxString::Format( wxT(
"build shape segment: OCC exception: %s\n" ),
1342 e.GetMessageString() ) );
1346 BRepBuilderAPI_MakeFace face;
1350 gp_Pln plane( coords3D[0], gp::DZ() );
1351 face = BRepBuilderAPI_MakeFace( plane, wire );
1353 catch(
const Standard_Failure& e )
1355 ReportMessage( wxString::Format( wxT(
"MakeShapeThickSegment: OCC exception: %s\n" ),
1356 e.GetMessageString() ) );
1360 if( aThickness != 0.0 )
1362 aShape = BRepPrimAPI_MakePrism( face, gp_Vec( 0, 0, aThickness ) );
1364 if( aShape.IsNull() )
1366 ReportMessage( wxT(
"failed to create a prismatic shape\n" ) );
1384 str <<
"x0: " <<
up.StringFromValue( aBBox.
GetLeft(),
false ) <<
"; ";
1385 str <<
"y0: " <<
up.StringFromValue( aBBox.
GetTop(),
false ) <<
"; ";
1386 str <<
"x1: " <<
up.StringFromValue( aBBox.
GetRight(),
false ) <<
"; ";
1387 str <<
"y1: " <<
up.StringFromValue( aBBox.
GetBottom(),
false );
1394 double aMergeOCCMaxDist,
double aZposition,
const VECTOR2D& aOrigin )
1396 auto toPoint = [&](
const VECTOR2D& aKiCoords ) -> gp_Pnt
1409 gp_Pnt start = toPoint( aPt0 );
1410 gp_Pnt end = toPoint( aPt1 );
1412 BRepBuilderAPI_MakeEdge mkEdge( start, end );
1414 if( !mkEdge.IsDone() || mkEdge.Edge().IsNull() )
1416 ReportMessage( wxString::Format( wxT(
"failed to make segment edge at (%d "
1417 "%d) -> (%d %d), skipping\n" ),
1418 aPt0.
x, aPt0.
y, aPt1.x, aPt1.y ) );
1422 aMkWire.Add( mkEdge.Edge() );
1424 if( aMkWire.Error() != BRepLib_WireDone )
1426 ReportMessage( wxString::Format( wxT(
"failed to add segment edge "
1427 "at (%d %d) -> (%d %d)\n" ),
1428 aPt0.
x, aPt0.
y, aPt1.x, aPt1.y ) );
1439 Handle( Geom_Curve ) curve;
1441 if( aArc.GetCentralAngle() ==
ANGLE_360 )
1443 gp_Ax2 axis = gp::XOY();
1444 axis.SetLocation( toPoint( aArc.GetCenter() ) );
1446 curve = GC_MakeCircle( axis,
pcbIUScale.
IUTomm( aArc.GetRadius() ) ).Value();
1450 curve = GC_MakeArcOfCircle( toPoint( aPt0 ), toPoint( aArc.GetArcMid() ),
1451 toPoint( aArc.GetP1() ) )
1455 if( curve.IsNull() )
1458 aMkWire.Add( BRepBuilderAPI_MakeEdge( curve ) );
1460 if( !aMkWire.IsDone() )
1463 wxT(
"failed to add arc curve from (%d %d), arc p0 "
1464 "(%d %d), mid (%d %d), p1 (%d %d)\n" ),
1465 aPt0.
x, aPt0.
y, aArc.GetP0().x, aArc.GetP0().y, aArc.GetArcMid().x,
1466 aArc.GetArcMid().y, aArc.GetP1().x, aArc.GetP1().y ) );
1475 bool isFirstShape =
true;
1488 if( nextShape != -1 )
1494 lastPt = aChain.
CPoint( i );
1504 firstPt = currentArc.
GetP0();
1509 lastPt = currentArc.
GetP0();
1511 if( addArc( lastPt, currentArc ) )
1512 lastPt = currentArc.
GetP1();
1531 isFirstShape =
false;
1534 if( lastPt != firstPt && !
addSegment( lastPt, firstPt ) )
1537 wxString::Format( wxT(
"** Failed to close wire at %d, %d -> %d, %d **\n" ),
1538 lastPt.
x, lastPt.
y, firstPt.
x, firstPt.
y ) );
1543 catch(
const Standard_Failure& e )
1545 ReportMessage( wxString::Format( wxT(
"makeWireFromChain: OCC exception: %s\n" ),
1546 e.GetMessageString() ) );
1555 double aThickness,
double aZposition,
const VECTOR2D& aOrigin )
1562 if( aConvertToArcs )
1566 for(
size_t polyId = 0; polyId < approximated.
CPolygons().size(); polyId++ )
1570 for(
size_t contId = 0; contId < polygon.size(); contId++ )
1573 polygon[contId] = approxChain;
1577 fallbackPoly = workingPoly;
1578 workingPoly = approximated;
1597 auto toPoint = [&](
const VECTOR2D& aKiCoords ) -> gp_Pnt
1604 gp_Pln basePlane( gp_Pnt( 0.0, 0.0, aZposition ),
1605 std::signbit( aThickness ) ? -gp::DZ() : gp::DZ() );
1607 for(
size_t polyId = 0; polyId < workingPoly.
CPolygons().size(); polyId++ )
1611 auto tryMakeWire = [
this, &aZposition,
1615 BRepLib_MakeWire mkWire;
1619 if( mkWire.IsDone() )
1621 wire = mkWire.Wire();
1626 wxString::Format(
_(
"Wire not done (contour points %d): OCC error %d\n" ),
1627 static_cast<int>( aContour.PointCount() ),
1628 static_cast<int>( mkWire.Error() ) ) );
1630 ReportMessage( wxString::Format(
_(
"z: %g; bounding box: %s\n" ), aZposition,
1634 if( !wire.IsNull() )
1636 BRepAlgoAPI_Check check( wire,
false,
true );
1638 if( !check.IsValid() )
1640 ReportMessage( wxString::Format(
_(
"\nWire self-interference check "
1643 ReportMessage( wxString::Format(
_(
"z: %g; bounding box: %s\n" ), aZposition,
1653 BRepBuilderAPI_MakeFace mkFace;
1655 for(
size_t contId = 0; contId < polygon.size(); contId++ )
1659 TopoDS_Wire wire = tryMakeWire( polygon[contId] );
1661 if( aConvertToArcs && wire.IsNull() )
1663 ReportMessage( wxString::Format(
_(
"Using non-simplified polygon.\n" ) ) );
1666 wire = tryMakeWire( fallbackPoly.
CPolygon( polyId )[contId] );
1671 if( !wire.IsNull() )
1673 if( basePlane.Axis().Direction().Z() < 0 )
1676 mkFace = BRepBuilderAPI_MakeFace( basePlane, wire );
1680 ReportMessage( wxString::Format( wxT(
"\n** Outline skipped **\n" ) ) );
1682 ReportMessage( wxString::Format( wxT(
"z: %g; bounding box: %s\n" ),
1691 if( !wire.IsNull() )
1693 if( basePlane.Axis().Direction().Z() > 0 )
1700 ReportMessage( wxString::Format( wxT(
"\n** Hole skipped **\n" ) ) );
1702 ReportMessage( wxString::Format( wxT(
"z: %g; bounding box: %s\n" ),
1708 catch(
const Standard_Failure& e )
1711 wxString::Format( wxT(
"MakeShapes (contour %d): OCC exception: %s\n" ),
1712 static_cast<int>( contId ), e.GetMessageString() ) );
1717 if( mkFace.IsDone() )
1719 TopoDS_Shape faceShape = mkFace.Shape();
1721 if( aThickness != 0.0 )
1723 TopoDS_Shape prism = BRepPrimAPI_MakePrism( faceShape, gp_Vec( 0, 0, aThickness ) );
1724 aShapes.push_back( prism );
1726 if( prism.IsNull() )
1734 aShapes.push_back( faceShape );
1739 ReportMessage( wxString::Format(
_(
"** Face skipped **\n" ) ) );
1750 {
_HKI(
"Green" ), wxColor( 20, 51, 36 ) },
1751 {
_HKI(
"Red" ), wxColor( 181, 19, 21 ) },
1752 {
_HKI(
"Blue" ), wxColor( 2, 59, 162 ) },
1753 {
_HKI(
"Purple" ), wxColor( 32, 2, 53 ) },
1754 {
_HKI(
"Black" ), wxColor( 11, 11, 11 ) },
1755 {
_HKI(
"White" ), wxColor( 245, 245, 245 ) },
1756 {
_HKI(
"Yellow" ), wxColor( 194, 195, 0 ) },
1757 {
_HKI(
"User defined" ), wxColor( 128, 128, 128 ) }
1767 if( aColorStr.StartsWith( wxT(
"#" ) ) )
1769 aColorOut =
COLOR4D( aColorStr );
1774 const std::vector<FAB_LAYER_COLOR>& colors =
1781 if( fabColor.GetName() == aColorStr )
1783 aColorOut = fabColor.GetColor( aType );
1803 Handle( XCAFDoc_VisMaterialTool ) visMatTool =
1804 XCAFDoc_DocumentTool::VisMaterialTool( m_doc->Main() );
1809 ReportMessage( wxString::Format( wxT(
"Build board outlines (%d outlines) with %d points.\n" ),
1812 double boardThickness;
1827 wxT(
"OCC error creating main outline.\n" ) ) );
1833 for(
size_t contId = 0; contId < polygon.size(); contId++ )
1837 polyset.
Append( contour );
1844 ReportMessage( wxT(
"OCC error creating main outline.\n" ) );
1852 ReportMessage( wxT(
"OCC error creating hole in main outline.\n" ) );
1863 BRepBndLib::Add( brdShape, brdBndBox );
1866 ReportMessage( wxString::Format( wxT(
"Build board cutouts and holes (%d hole(s)).\n" ),
1869 auto buildBSB = [&brdBndBox]( std::vector<TopoDS_Shape>& input, Bnd_BoundSortBox& bsbHoles )
1873 Bnd_Box brdWithHolesBndBox = brdBndBox;
1875 Handle( Bnd_HArray1OfBox ) holeBoxSet =
new Bnd_HArray1OfBox( 0, input.size() - 1 );
1877 for(
size_t i = 0; i < input.size(); i++ )
1880 BRepBndLib::Add( input[i], bbox );
1881 brdWithHolesBndBox.Add( bbox );
1882 ( *holeBoxSet )[i] = bbox;
1885 bsbHoles.Initialize( brdWithHolesBndBox, holeBoxSet );
1888 auto subtractShapes = [](
const wxString& aWhat, std::vector<TopoDS_Shape>& aShapesList,
1889 std::vector<TopoDS_Shape>& aHolesList, Bnd_BoundSortBox& aBSBHoles )
1893 for( TopoDS_Shape& shape : aShapesList )
1896 BRepBndLib::Add( shape, shapeBbox );
1898 const TColStd_ListOfInteger& indices = aBSBHoles.Compare( shapeBbox );
1900 TopTools_ListOfShape holelist;
1902 for(
const Standard_Integer& index : indices )
1903 holelist.Append( aHolesList[index] );
1906 ReportMessage( wxString::Format(
_(
"Build holes for %s\n" ), aWhat ) );
1912 (
int) aShapesList.size(), aWhat ) );
1914 if( holelist.IsEmpty() )
1917 TopTools_ListOfShape cutArgs;
1918 cutArgs.Append( shape );
1920 BRepAlgoAPI_Cut
cut;
1922 cut.SetRunParallel(
true );
1923 cut.SetToFillHistory(
false );
1925 cut.SetArguments( cutArgs );
1926 cut.SetTools( holelist );
1929 if(
cut.HasErrors() ||
cut.HasWarnings() )
1932 _(
"\n** Got problems while cutting %s number %d **\n" ), aWhat, cnt ) );
1935 if(
cut.HasErrors() )
1938 cut.DumpErrors( std::cout );
1941 if(
cut.HasWarnings() )
1944 cut.DumpWarnings( std::cout );
1950 shape =
cut.Shape();
1956 Bnd_BoundSortBox bsbHoles;
1964 Bnd_BoundSortBox bsbHoles;
1975 TopTools_ListOfShape shapesToFuse;
1978 shapesToFuse.Append( shape );
1981 shapesToFuse.Append( shape );
1984 shapesToFuse.Append( shape );
1988 if( !fusedShape.IsNull() )
2008 auto pushToAssembly = [&]( std::vector<TopoDS_Shape>& aShapesList, Quantity_ColorRGBA aColor,
2009 const TDF_Label& aVisMatLabel,
const wxString& aShapeName,
2012 if( aShapesList.empty() )
2015 std::vector<TopoDS_Shape> newList;
2020 newList = aShapesList;
2023 for( TopoDS_Shape& shape : newList )
2025 Handle( TDataStd_TreeNode ) node;
2028 TDF_Label lbl = m_assy->AddComponent(
m_assy_label, shape,
false );
2034 lbl.FindAttribute( XCAFDoc::ShapeRefGUID(), node );
2035 TDF_Label shpLbl = node->Father()->Label();
2036 if( !shpLbl.IsNull() )
2038 if( visMatTool && !aVisMatLabel.IsNull() )
2039 visMatTool->SetShapeMaterial( shpLbl, aVisMatLabel );
2043 if( newList.size() > 1 )
2045 shapeName = wxString::Format( wxT(
"%s_%s_%d" ),
m_pcbName, aShapeName, i );
2049 shapeName = wxString::Format( wxT(
"%s_%s" ),
m_pcbName, aShapeName );
2053 TCollection_ExtendedString partname( shapeName.ToUTF8().data() );
2054 TDataStd_Name::Set( shpLbl, partname );
2061 auto makeMaterial = [&](
const TCollection_AsciiString& aName,
2062 const Quantity_ColorRGBA& aBaseColor,
double aMetallic,
2063 double aRoughness ) -> TDF_Label
2065 Handle( XCAFDoc_VisMaterial ) vismat =
new XCAFDoc_VisMaterial;
2066 XCAFDoc_VisMaterialPBR pbr;
2067 pbr.BaseColor = aBaseColor;
2068 pbr.Metallic = aMetallic;
2069 pbr.Roughness = aRoughness;
2070 vismat->SetPbrMaterial( pbr );
2071 return visMatTool->AddMaterial( vismat, aName );
2078 Quantity_ColorRGBA board_color( 0.3f, 0.3f, 0.3f, 1.0f );
2079 Quantity_ColorRGBA silk_color( 1.0f, 1.0f, 1.0f, 0.9f );
2080 Quantity_ColorRGBA mask_color( 0.08f, 0.2f, 0.14f, 0.83f );
2090 if( item->GetBrdLayerId() ==
F_Mask || item->GetBrdLayerId() ==
B_Mask )
2093 mask_color.SetValues( col.
r, col.
g, col.
b, col.
a );
2096 if( item->GetBrdLayerId() ==
F_SilkS || item->GetBrdLayerId() ==
B_SilkS )
2097 silk_color.SetValues( col.
r, col.
g, col.
b, col.
a );
2100 board_color.SetValues( col.
r, col.
g, col.
b, col.
a );
2105 board_color = mask_color;
2106 board_color.SetAlpha( 1.0 );
2109 TDF_Label mask_mat = makeMaterial(
"soldermask", mask_color, 0.0, 0.6 );
2110 TDF_Label silk_mat = makeMaterial(
"silkscreen", silk_color, 0.0, 0.9 );
2111 TDF_Label copper_mat = makeMaterial(
"copper", copper_color, 1.0, 0.4 );
2112 TDF_Label pad_mat = makeMaterial(
"pad", pad_color, 1.0, 0.4 );
2113 TDF_Label board_mat = makeMaterial(
"board", board_color, 0.0, 0.8 );
2115 pushToAssembly(
m_board_copper, copper_color, copper_mat,
"copper",
true );
2122 if( aPushBoardBody )
2125#if( defined OCC_VERSION_HEX ) && ( OCC_VERSION_HEX > 0x070101 )
2126 m_assy->UpdateAssemblies();
2135bool STEP_PCB_MODEL::WriteIGES(
const wxString& aFileName )
2139 ReportMessage( wxString::Format( wxT(
"No valid PCB assembly; cannot create output file "
2145 m_outFmt = OUTPUT_FORMAT::FMT_OUT_IGES;
2147 wxFileName fn( aFileName );
2148 IGESControl_Controller::Init();
2149 IGESCAFControl_Writer writer;
2150 writer.SetColorMode( Standard_True );
2151 writer.SetNameMode( Standard_True );
2152 IGESData_GlobalSection header = writer.Model()->GlobalSection();
2153 header.SetFileName(
new TCollection_HAsciiString( fn.GetFullName().ToAscii() ) );
2154 header.SetSendName(
new TCollection_HAsciiString(
"KiCad electronic assembly" ) );
2155 header.SetAuthorName(
2156 new TCollection_HAsciiString( Interface_Static::CVal(
"write.iges.header.author" ) ) );
2157 header.SetCompanyName(
2158 new TCollection_HAsciiString( Interface_Static::CVal(
"write.iges.header.company" ) ) );
2159 writer.Model()->SetGlobalSection( header );
2161 if( Standard_False == writer.Perform( m_doc, aFileName.c_str() ) )
2173 ReportMessage( wxString::Format( wxT(
"No valid PCB assembly; cannot create output file "
2179 m_outFmt = OUTPUT_FORMAT::FMT_OUT_STEP;
2181 wxFileName fn( aFileName );
2183 STEPCAFControl_Writer writer;
2184 writer.SetColorMode( Standard_True );
2185 writer.SetNameMode( Standard_True );
2192 if( !Interface_Static::SetCVal(
"write.step.product.name", fn.GetName().ToAscii() ) )
2193 ReportMessage( wxT(
"Failed to set step product name, but will attempt to continue." ) );
2197 if( !Interface_Static::SetIVal(
"write.surfacecurve.mode", aOptimize ? 0 : 1 ) )
2198 ReportMessage( wxT(
"Failed to set surface curve mode, but will attempt to continue." ) );
2200 if( Standard_False == writer.Transfer( m_doc, STEPControl_AsIs ) )
2203 APIHeaderSection_MakeHeader hdr( writer.ChangeWriter().Model() );
2207 hdr.SetName(
new TCollection_HAsciiString( fn.GetFullName().ToAscii() ) );
2210 hdr.SetAuthorValue( 1,
new TCollection_HAsciiString(
"Pcbnew" ) );
2211 hdr.SetOrganizationValue( 1,
new TCollection_HAsciiString(
"Kicad" ) );
2212 hdr.SetOriginatingSystem(
new TCollection_HAsciiString(
"KiCad to STEP converter" ) );
2213 hdr.SetDescriptionValue( 1,
new TCollection_HAsciiString(
"KiCad electronic assembly" ) );
2215 bool success =
true;
2218 wxString currCWD = wxGetCwd();
2219 wxString workCWD = fn.GetPath();
2221 if( !workCWD.IsEmpty() )
2222 wxSetWorkingDirectory( workCWD );
2224 char tmpfname[] =
"$tempfile$.step";
2226 if( Standard_False == writer.Write( tmpfname ) )
2235 if( !wxRenameFile( tmpfname, fn.GetFullName(),
true ) )
2237 ReportMessage( wxString::Format( wxT(
"Cannot rename temporary file '%s' to '%s'.\n" ),
2239 fn.GetFullName() ) );
2244 wxSetWorkingDirectory( currCWD );
2254 ReportMessage( wxString::Format( wxT(
"No valid PCB assembly; cannot create output file "
2260 m_outFmt = OUTPUT_FORMAT::FMT_OUT_BREP;
2263 Handle( XCAFDoc_ShapeTool ) s_assy = XCAFDoc_DocumentTool::ShapeTool( m_doc->Main() );
2268 wxFileName fn( aFileName );
2270 wxFFileOutputStream ffStream( fn.GetFullPath() );
2271 wxStdOutputStream stdStream( ffStream );
2273#if OCC_VERSION_HEX >= 0x070600
2274 BRepTools::Write( shape, stdStream,
false,
false, TopTools_FormatVersion_VERSION_1 );
2276 BRepTools::Write( shape, stdStream );
2285 wxFileName fn( aFileName );
2287 wxFFileOutputStream ffStream( fn.GetFullPath() );
2288 wxStdOutputStream file( ffStream );
2290 if( !ffStream.IsOk() )
2292 ReportMessage( wxString::Format(
"Could not open file '%s'", fn.GetFullPath() ) );
2296 m_outFmt = OUTPUT_FORMAT::FMT_OUT_XAO;
2299 Handle( XCAFDoc_ShapeTool ) s_assy = XCAFDoc_DocumentTool::ShapeTool( m_doc->Main() );
2304 std::map<wxString, std::vector<int>> groups[4];
2305 std::map<wxString, double> groupAreas;
2306 TopExp_Explorer exp;
2309 for( exp.Init( shape, TopAbs_FACE ); exp.More(); exp.Next() )
2311 TopoDS_Shape subShape = exp.Current();
2314 BRepBndLib::Add( subShape, bbox );
2318 const auto& [point, padTestShape] = pair;
2320 if( bbox.IsOut( point ) )
2323 BRepAdaptor_Surface surface( TopoDS::Face( subShape ) );
2325 if( surface.GetType() != GeomAbs_Plane )
2328 BRepExtrema_DistShapeShape dist( padTestShape, subShape );
2331 if( !dist.IsDone() )
2334 if( dist.Value() < Precision::Approximation() )
2337 groups[2][padKey].push_back( faceIndex );
2339 GProp_GProps system;
2340 BRepGProp::SurfaceProperties( subShape, system );
2342 double surfaceArea = system.Mass() / 1e6;
2343 groupAreas[padKey] = surfaceArea;
2351 file <<
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>" << std::endl;
2352 file <<
"<XAO version=\"1.0\" author=\"KiCad\">" << std::endl;
2353 file <<
" <geometry name=\"" << fn.GetName() <<
"\">" << std::endl;
2354 file <<
" <shape format=\"BREP\"><![CDATA[";
2355#if OCC_VERSION_HEX < 0x070600
2356 BRepTools::Write( shape, file );
2358 BRepTools::Write( shape, file, Standard_True, Standard_True, TopTools_FormatVersion_VERSION_1 );
2360 file <<
"]]></shape>" << std::endl;
2361 file <<
" <topology>" << std::endl;
2363 TopTools_IndexedMapOfShape mainMap;
2364 TopExp::MapShapes( shape, mainMap );
2365 std::set<int> topo[4];
2367 static const TopAbs_ShapeEnum c_dimShapeTypes[] = { TopAbs_VERTEX, TopAbs_EDGE, TopAbs_FACE,
2370 static const std::string c_dimLabel[] = {
"vertex",
"edge",
"face",
"solid" };
2371 static const std::string c_dimLabels[] = {
"vertices",
"edges",
"faces",
"solids" };
2373 for(
int dim = 0; dim < 4; dim++ )
2375 for( exp.Init( shape, c_dimShapeTypes[dim] ); exp.More(); exp.Next() )
2377 TopoDS_Shape subShape = exp.Current();
2378 int idx = mainMap.FindIndex( subShape );
2380 if( idx && !topo[dim].count( idx ) )
2381 topo[dim].insert( idx );
2385 for(
int dim = 0; dim <= 3; dim++ )
2387 std::string labels = c_dimLabels[dim];
2388 std::string label = c_dimLabel[dim];
2390 file <<
" <" << labels <<
" count=\"" << topo[dim].size() <<
"\">" << std::endl;
2393 for(
auto p : topo[dim] )
2395 std::string
name(
"" );
2396 file <<
" <" << label <<
" index=\"" << index <<
"\" "
2397 <<
"name=\"" <<
name <<
"\" "
2398 <<
"reference=\"" << p <<
"\"/>" << std::endl;
2402 file <<
" </" << labels <<
">" << std::endl;
2405 file <<
" </topology>" << std::endl;
2406 file <<
" </geometry>" << std::endl;
2407 file <<
" <groups count=\""
2408 << groups[0].size() + groups[1].size() + groups[2].size() + groups[3].size() <<
"\">"
2411 int groupNumber = 1;
2416 for(
int dim = 0; dim <= 3; dim++ )
2418 std::string label = c_dimLabel[dim];
2420 for(
auto g : groups[dim] )
2423 wxString
name = g.first;
2427 std::ostringstream gs;
2428 gs <<
"G_" << dim <<
"D_" << g.first;
2431 file <<
" <group name=\"" <<
name <<
"\" dimension=\"" << label;
2437 file <<
"\" count=\"" << g.second.size() <<
"\">" << std::endl;
2438 for(
auto index : g.second )
2440 file <<
" <element index=\"" << index <<
"\"/>" << std::endl;
2442 file <<
" </group>" << std::endl;
2452 file <<
" </groups>" << std::endl;
2453 file <<
" <fields count=\"0\"/>" << std::endl;
2454 file <<
"</XAO>" << std::endl;
2461 bool aSubstituteModels, wxString* aErrorMessage )
2463 std::string model_key = aFileNameUTF8 +
"_" + std::to_string( aScale.
x )
2464 +
"_" + std::to_string( aScale.
y ) +
"_" + std::to_string( aScale.
z );
2466 MODEL_MAP::const_iterator mm =
m_models.find( model_key );
2470 aLabel = mm->second;
2476 Handle( TDocStd_Document ) doc;
2477 m_app->NewDocument(
"MDTV-XCAF", doc );
2479 wxString fileName( wxString::FromUTF8( aFileNameUTF8.c_str() ) );
2485 if( !
readIGES( doc, aFileNameUTF8.c_str() ) )
2487 ReportMessage( wxString::Format( wxT(
"readIGES() failed on filename '%s'.\n" ),
2494 if( !
readSTEP( doc, aFileNameUTF8.c_str() ) )
2496 ReportMessage( wxString::Format( wxT(
"readSTEP() failed on filename '%s'.\n" ),
2506 wxFFileInputStream ifile( fileName );
2507 wxFileName outFile( fileName );
2509 outFile.SetPath( wxStandardPaths::Get().GetTempDir() );
2510 outFile.SetExt( wxT(
"step" ) );
2511 wxFileOffset size = ifile.GetLength();
2513 if( size == wxInvalidOffset )
2515 ReportMessage( wxString::Format( wxT(
"getModelLabel() failed on filename '%s'.\n" ),
2521 bool success =
false;
2522 wxFFileOutputStream ofile( outFile.GetFullPath() );
2527 char* buffer =
new char[size];
2529 ifile.Read( buffer, size );
2530 std::string expanded;
2534 expanded = gzip::decompress( buffer, size );
2539 ReportMessage( wxString::Format( wxT(
"failed to decompress '%s'.\n" ),
2543 if( expanded.empty() )
2547 wxZipInputStream izipfile( ifile );
2548 std::unique_ptr<wxZipEntry> zip_file( izipfile.GetNextEntry() );
2550 if( zip_file && !zip_file->IsDir() && izipfile.CanRead() )
2552 izipfile.Read( ofile );
2558 ofile.Write( expanded.data(), expanded.size() );
2566 std::string altFileNameUTF8 =
TO_UTF8( outFile.GetFullPath() );
2587 if( aSubstituteModels )
2589 wxFileName wrlName( fileName );
2591 wxString basePath = wrlName.GetPath();
2592 wxString baseName = wrlName.GetName();
2600 alts.Add( wxT(
"stp" ) );
2601 alts.Add( wxT(
"step" ) );
2602 alts.Add( wxT(
"STP" ) );
2603 alts.Add( wxT(
"STEP" ) );
2604 alts.Add( wxT(
"Stp" ) );
2605 alts.Add( wxT(
"Step" ) );
2606 alts.Add( wxT(
"stpz" ) );
2607 alts.Add( wxT(
"stpZ" ) );
2608 alts.Add( wxT(
"STPZ" ) );
2609 alts.Add( wxT(
"step.gz" ) );
2610 alts.Add( wxT(
"stp.gz" ) );
2613 alts.Add( wxT(
"iges" ) );
2614 alts.Add( wxT(
"IGES" ) );
2615 alts.Add( wxT(
"igs" ) );
2616 alts.Add( wxT(
"IGS" ) );
2620 for(
const auto& alt : alts )
2622 wxFileName altFile( basePath, baseName + wxT(
"." ) + alt );
2624 if( altFile.IsOk() && altFile.FileExists() )
2626 std::string altFileNameUTF8 =
TO_UTF8( altFile.GetFullPath() );
2644 if(
m_outFmt == OUTPUT_FORMAT::FMT_OUT_GLTF )
2646 if(
readVRML( doc, aFileNameUTF8.c_str() ) )
2648 Handle( XCAFDoc_ShapeTool ) shapeTool =
2649 XCAFDoc_DocumentTool::ShapeTool( doc->Main() );
2652 TCollection_ExtendedString( baseName.c_str().AsChar() ) );
2656 ReportMessage( wxString::Format( wxT(
"readVRML() failed on filename '%s'.\n" ),
2665 aErrorMessage->Printf( wxT(
"Cannot load any VRML model for this export.\n" ) );
2675 ReportMessage( wxString::Format( wxT(
"Cannot identify actual file type for '%s'.\n" ),
2682 if( aLabel.IsNull() )
2684 ReportMessage( wxString::Format( wxT(
"Could not transfer model data from file '%s'.\n" ),
2691 wxFileName afile( fileName );
2692 std::string pname( afile.GetName().ToUTF8() );
2693 TCollection_ExtendedString partname( pname.c_str() );
2694 TDataStd_Name::Set( aLabel, partname );
2703 TopLoc_Location& aLocation )
2717 lPos.SetTranslation( gp_Vec( aPosition.
x, -aPosition.
y, 0.0 ) );
2722 double boardThickness;
2725 double top = std::max( boardZPos, boardZPos + boardThickness );
2726 double bottom = std::min( boardZPos, boardZPos + boardThickness );
2731 double f_pos, f_thickness;
2735 bottom += f_thickness;
2741 aOffset.
z -= bottom;
2742 lRot.SetRotation( gp_Ax1( gp_Pnt( 0.0, 0.0, 0.0 ), gp_Dir( 0.0, 0.0, 1.0 ) ), aRotation );
2743 lPos.Multiply( lRot );
2744 lRot.SetRotation( gp_Ax1( gp_Pnt( 0.0, 0.0, 0.0 ), gp_Dir( 1.0, 0.0, 0.0 ) ), M_PI );
2745 lPos.Multiply( lRot );
2750 lRot.SetRotation( gp_Ax1( gp_Pnt( 0.0, 0.0, 0.0 ), gp_Dir( 0.0, 0.0, 1.0 ) ), aRotation );
2751 lPos.Multiply( lRot );
2755 lOff.SetTranslation( gp_Vec( aOffset.
x, aOffset.
y, aOffset.
z ) );
2756 lPos.Multiply( lOff );
2759 lOrient.SetRotation( gp_Ax1( gp_Pnt( 0.0, 0.0, 0.0 ), gp_Dir( 0.0, 0.0, 1.0 ) ),
2761 lPos.Multiply( lOrient );
2762 lOrient.SetRotation( gp_Ax1( gp_Pnt( 0.0, 0.0, 0.0 ), gp_Dir( 0.0, 1.0, 0.0 ) ),
2764 lPos.Multiply( lOrient );
2765 lOrient.SetRotation( gp_Ax1( gp_Pnt( 0.0, 0.0, 0.0 ), gp_Dir( 1.0, 0.0, 0.0 ) ),
2767 lPos.Multiply( lOrient );
2769 aLocation = TopLoc_Location( lPos );
2776 IGESControl_Controller::Init();
2777 IGESCAFControl_Reader reader;
2778 IFSelect_ReturnStatus stat = reader.ReadFile( fname );
2780 if( stat != IFSelect_RetDone )
2784 if( !Interface_Static::SetIVal(
"read.precision.mode", 1 ) )
2788 if( !Interface_Static::SetRVal(
"read.precision.val",
USER_PREC ) )
2792 reader.SetColorMode(
true );
2793 reader.SetNameMode(
false );
2794 reader.SetLayerMode(
false );
2796 if( !reader.Transfer( doc ) )
2798 if( doc->CanClose() == CDM_CCS_OK )
2805 if( reader.NbShapes() < 1 )
2807 if( doc->CanClose() == CDM_CCS_OK )
2819 STEPCAFControl_Reader reader;
2820 IFSelect_ReturnStatus stat = reader.ReadFile( fname );
2822 if( stat != IFSelect_RetDone )
2826 if( !Interface_Static::SetIVal(
"read.precision.mode", 1 ) )
2830 if( !Interface_Static::SetRVal(
"read.precision.val",
USER_PREC ) )
2834 reader.SetColorMode(
true );
2835 reader.SetNameMode(
true );
2836 reader.SetLayerMode(
false );
2838 if( !reader.Transfer( doc ) )
2840 if( doc->CanClose() == CDM_CCS_OK )
2847 if( reader.NbRootsForTransfer() < 1 )
2849 if( doc->CanClose() == CDM_CCS_OK )
2861#if OCC_VERSION_HEX >= 0x070700
2862 VrmlAPI_CafReader reader;
2863 RWMesh_CoordinateSystemConverter conv;
2864 conv.SetInputLengthUnit( 2.54 );
2865 reader.SetCoordinateSystemConverter( conv );
2866 reader.SetDocument( doc );
2868 if( !reader.Perform( TCollection_AsciiString( fname ), Message_ProgressRange() ) )
2879 Handle( TDocStd_Document ) & dest,
VECTOR3D aScale )
2883 Handle( XCAFDoc_ShapeTool ) s_assy = XCAFDoc_DocumentTool::ShapeTool( source->Main() );
2886 TDF_LabelSequence frshapes;
2887 s_assy->GetFreeShapes( frshapes );
2890 Handle( XCAFDoc_ShapeTool ) d_assy = XCAFDoc_DocumentTool::ShapeTool( dest->Main() );
2893 TDF_Label d_targetLabel = d_assy->NewShape();
2895 if( frshapes.Size() == 1 )
2897 TDocStd_XLinkTool link;
2898 link.Copy( d_targetLabel, frshapes.First() );
2903 for( TDF_Label& s_shapeLabel : frshapes )
2905 TDF_Label d_component = d_assy->NewShape();
2907 TDocStd_XLinkTool link;
2908 link.Copy( d_component, s_shapeLabel );
2910 d_assy->AddComponent( d_targetLabel, d_component, TopLoc_Location() );
2914 if( aScale.
x != 1.0 || aScale.
y != 1.0 || aScale.
z != 1.0 )
2917 return d_targetLabel;
2923 TDF_LabelSequence freeShapes;
2924 aShapeTool->GetFreeShapes( freeShapes );
2930 for( Standard_Integer i = 1; i <= freeShapes.Length(); ++i )
2932 TDF_Label label = freeShapes.Value( i );
2934 aShapeTool->GetShape( label, shape );
2939 const Standard_Real linearDeflection = 0.14;
2940 const Standard_Real angularDeflection =
DEG2RAD( 30.0 );
2941 BRepMesh_IncrementalMesh mesh( shape, linearDeflection, Standard_False, angularDeflection,
2953 ReportMessage( wxString::Format( wxT(
"No valid PCB assembly; cannot create output file "
2959 m_outFmt = OUTPUT_FORMAT::FMT_OUT_GLTF;
2963 wxFileName fn( aFileName );
2965 const char* tmpGltfname =
"$tempfile$.glb";
2966 RWGltf_CafWriter cafWriter( tmpGltfname,
true );
2968 cafWriter.SetTransformationFormat( RWGltf_WriterTrsfFormat_Compact );
2969 cafWriter.ChangeCoordinateSystemConverter().SetInputLengthUnit( 0.001 );
2970 cafWriter.ChangeCoordinateSystemConverter().SetInputCoordinateSystem(
2971 RWMesh_CoordinateSystem_Zup );
2972#if OCC_VERSION_HEX >= 0x070700
2973 cafWriter.SetParallel(
true );
2975 TColStd_IndexedDataMapOfStringString metadata;
2977 metadata.Add( TCollection_AsciiString(
"pcb_name" ),
2978 TCollection_ExtendedString( fn.GetName().wc_str() ) );
2979 metadata.Add( TCollection_AsciiString(
"source_pcb_file" ),
2980 TCollection_ExtendedString( fn.GetFullName().wc_str() ) );
2981 metadata.Add( TCollection_AsciiString(
"generator" ),
2982 TCollection_AsciiString( wxString::Format( wxS(
"KiCad %s" ),
GetSemanticVersion() ).ToAscii() ) );
2983 metadata.Add( TCollection_AsciiString(
"generated_at" ),
2986 bool success =
true;
2989 wxString currCWD = wxGetCwd();
2990 wxString workCWD = fn.GetPath();
2992 if( !workCWD.IsEmpty() )
2993 wxSetWorkingDirectory( workCWD );
2995 success = cafWriter.Perform( m_doc, metadata, Message_ProgressRange() );
3002 if( !wxRenameFile( tmpGltfname, fn.GetFullName(),
true ) )
3004 ReportMessage( wxString::Format( wxT(
"Cannot rename temporary file '%s' to '%s'.\n" ),
3005 tmpGltfname, fn.GetFullName() ) );
3010 wxSetWorkingDirectory( currCWD );
3018#if OCC_VERSION_HEX < 0x070700
3019#warning "PLY export is not supported before OCCT 7.7.0"
3021 ReportMessage( wxT(
"PLY export is not supported before OCCT 7.7.0\n" ) );
3027 ReportMessage( wxString::Format( wxT(
"No valid PCB assembly; cannot create output file "
3033 m_outFmt = OUTPUT_FORMAT::FMT_OUT_PLY;
3037 wxFileName fn( aFileName );
3039 const char* tmpFname =
"$tempfile$.ply";
3040 RWPly_CafWriter cafWriter( tmpFname );
3042 cafWriter.SetFaceId(
true );
3043 cafWriter.ChangeCoordinateSystemConverter().SetInputLengthUnit( 0.001 );
3044 cafWriter.ChangeCoordinateSystemConverter().SetInputCoordinateSystem(
3045 RWMesh_CoordinateSystem_Zup );
3047 TColStd_IndexedDataMapOfStringString metadata;
3049 metadata.Add( TCollection_AsciiString(
"pcb_name" ),
3050 TCollection_ExtendedString( fn.GetName().wc_str() ) );
3051 metadata.Add( TCollection_AsciiString(
"source_pcb_file" ),
3052 TCollection_ExtendedString( fn.GetFullName().wc_str() ) );
3053 metadata.Add( TCollection_AsciiString(
"generator" ),
3054 TCollection_AsciiString(
3056 metadata.Add( TCollection_AsciiString(
"generated_at" ),
3059 bool success =
true;
3062 wxString currCWD = wxGetCwd();
3063 wxString workCWD = fn.GetPath();
3065 if( !workCWD.IsEmpty() )
3066 wxSetWorkingDirectory( workCWD );
3068 success = cafWriter.Perform( m_doc, metadata, Message_ProgressRange() );
3075 if( !wxRenameFile( tmpFname, fn.GetFullName(),
true ) )
3077 ReportMessage( wxString::Format( wxT(
"Cannot rename temporary file '%s' to '%s'.\n" ),
3078 tmpFname, fn.GetFullName() ) );
3083 wxSetWorkingDirectory( currCWD );
3094 ReportMessage( wxString::Format( wxT(
"No valid PCB assembly; cannot create output file "
3100 m_outFmt = OUTPUT_FORMAT::FMT_OUT_STL;
3104 wxFileName fn( aFileName );
3106 const char* tmpFname =
"$tempfile$.stl";
3109 wxString currCWD = wxGetCwd();
3110 wxString workCWD = fn.GetPath();
3112 if( !workCWD.IsEmpty() )
3113 wxSetWorkingDirectory( workCWD );
3115 bool success = StlAPI_Writer().Write(
getOneShape( m_assy ), tmpFname );
3122 if( !wxRenameFile( tmpFname, fn.GetFullName(),
true ) )
3124 ReportMessage( wxString::Format( wxT(
"Cannot rename temporary file '%s' to '%s'.\n" ),
3125 tmpFname, fn.GetFullName() ) );
3130 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
bool AddHole(const SHAPE_SEGMENT &aShape, int aPlatingThickness, PCB_LAYER_ID aLayerTop, PCB_LAYER_ID aLayerBot, bool aVia, const VECTOR2D &aOrigin)
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 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