32#include <wx/filename.h>
34#include <wx/sstream.h>
35#include <wx/stdpaths.h>
38#include <wx/zstream.h>
39#include <wx/wfstream.h>
40#include <wx/zipstrm.h>
41#include <wx/stdstream.h>
44#include <decompress.hpp>
70#include <IGESCAFControl_Reader.hxx>
71#include <IGESCAFControl_Writer.hxx>
72#include <IGESControl_Controller.hxx>
73#include <IGESData_GlobalSection.hxx>
74#include <IGESData_IGESModel.hxx>
75#include <Interface_Static.hxx>
76#include <Quantity_Color.hxx>
77#include <STEPCAFControl_Reader.hxx>
78#include <STEPCAFControl_Writer.hxx>
79#include <APIHeaderSection_MakeHeader.hxx>
80#include <Standard_Failure.hxx>
81#include <Standard_Handle.hxx>
82#include <Standard_Version.hxx>
83#include <TCollection_ExtendedString.hxx>
84#include <TDocStd_Document.hxx>
85#include <TDocStd_XLinkTool.hxx>
86#include <TDataStd_Name.hxx>
87#include <TDataStd_TreeNode.hxx>
88#include <TDF_LabelSequence.hxx>
89#include <TDF_Tool.hxx>
90#include <TopExp_Explorer.hxx>
92#include <XCAFApp_Application.hxx>
94#include <XCAFDoc_DocumentTool.hxx>
95#include <XCAFDoc_ColorTool.hxx>
96#include <XCAFDoc_ShapeTool.hxx>
97#include <XCAFDoc_VisMaterialTool.hxx>
98#include <XCAFDoc_Area.hxx>
99#include <XCAFDoc_Centroid.hxx>
100#include <XCAFDoc_Location.hxx>
101#include <XCAFDoc_Volume.hxx>
104#include "KI_XCAFDoc_AssemblyGraph.hxx"
106#include <BRep_Tool.hxx>
107#include <BRepMesh_IncrementalMesh.hxx>
108#include <BRepBuilderAPI_GTransform.hxx>
109#include <BRepBuilderAPI_MakeEdge.hxx>
110#include <BRepBuilderAPI_MakeWire.hxx>
111#include <BRepBuilderAPI_MakeFace.hxx>
112#include <BRepExtrema_DistShapeShape.hxx>
113#include <BRepPrimAPI_MakeCone.hxx>
114#include <BRepPrimAPI_MakeCylinder.hxx>
115#include <BRepPrimAPI_MakePrism.hxx>
116#include <BRepTools.hxx>
117#include <BRepLib_MakeWire.hxx>
118#include <BRepAdaptor_Surface.hxx>
119#include <BRepAlgoAPI_Check.hxx>
120#include <BRepAlgoAPI_Cut.hxx>
121#include <BRepAlgoAPI_Fuse.hxx>
122#include <ShapeUpgrade_UnifySameDomain.hxx>
124#include <BRepBndLib.hxx>
125#include <Bnd_BoundSortBox.hxx>
126#include <GProp_GProps.hxx>
127#include <BRepGProp.hxx>
129#include <Geom_Curve.hxx>
130#include <Geom_TrimmedCurve.hxx>
135#include <GC_MakeArcOfCircle.hxx>
136#include <GC_MakeCircle.hxx>
138#include <RWGltf_CafWriter.hxx>
139#include <StlAPI_Writer.hxx>
141#if OCC_VERSION_HEX >= 0x070700
142#include <VrmlAPI_CafReader.hxx>
143#include <RWPly_CafWriter.hxx>
171 wxFileName lfile( wxString::FromUTF8Unchecked( aFileName ) );
173 if( !lfile.FileExists() )
176 wxString ext = lfile.GetExt().Lower();
178 if( ext == wxT(
"wrl" ) )
181 if( ext == wxT(
"wrz" ) )
184 if( ext == wxT(
"idf" ) )
187 if( ext == wxT(
"emn" ) )
190 if( ext == wxT(
"stpz" ) || ext == wxT(
"gz" ) )
204 const int max_line_count = 3;
206 for(
int ii = 0; ii < max_line_count; ii++ )
208 memset( iline, 0, 82 );
209 ifile.getline( iline, 82 );
215 if( !strncmp( iline,
"ISO-10303-21;", 13 ) )
221 std::string fstr = iline;
225 if( fstr.find(
"urn:oid:1.0.10303." ) != std::string::npos )
234 if( iline[72] ==
'S' && ( iline[80] == 0 || iline[80] == 13 || iline[80] == 10 ) )
241 if( strncmp( iline,
"/*", 2 ) != 0 )
259 double bc = ( b.
x * b.
x + b.
y * b.
y ) / 2.0;
260 double cd = ( -d.
x * d.
x - d.
y * d.
y ) / 2.0;
261 double det = -b.
x * d.
y + d.
x * b.
y;
265 center.x = ( -bc * d.
y - cd * b.
y ) * det;
266 center.y = ( b.
x * cd + d.
x * bc ) * det;
273#define APPROX_DBG( stmt )
281 static const double c_radiusDeviation = 1000.0;
282 static const double c_arcCenterDeviation = 1000.0;
283 static const double c_relLengthDeviation = 0.8;
284 static const int c_last_none = -1000;
286 static const double c_smallSize =
pcbIUScale.mmToIU( 0.1 );
287 static const double c_circleCloseGap =
pcbIUScale.mmToIU( 1.0 );
304 int last = c_last_none;
310 APPROX_DBG( std::cout << i <<
" " << aSrc.
CPoint( i ) <<
" " << ( i - 3 ) <<
" "
312 << ( i - 1 ) <<
" " <<
VECTOR2I( p2 ) << std::endl );
317 bool defective =
false;
323 defective |=
std::abs( d01 - d12 ) > ( std::max( d01, d12 ) * c_relLengthDeviation );
331 double a_diff = ( a01 - a12 ).Normalize180().AsDegrees();
332 defective |=
std::abs( a_diff ) < 0.1;
335 double maxAngleDiff = std::max( d01, d12 ) < c_smallSize ? 46.0 : 30.0;
336 defective |=
std::abs( a_diff ) >= maxAngleDiff;
347 for(
int j = i; j <= jEndIdx; j++ )
352 double rad_test = ( p_test -
center ).EuclideanNorm();
353 double d_tl = ( p_test - p_prev ).EuclideanNorm();
357 << int64_t( rad_test ) <<
" ref " << int64_t(
radius )
360 if( rad_dev > c_radiusDeviation )
363 <<
" Radius deviation too large: " << int64_t( rad_dev )
364 <<
" > " << c_radiusDeviation << std::endl );
369 double maxAngleDiff =
370 std::max( std::max( d01, d12 ), d_tl ) < c_smallSize ? 46.0 : 30.0;
372 double a_diff_test = ( a_prev - a_test ).Normalize180().AsDegrees();
373 if(
std::abs( a_diff_test ) >= maxAngleDiff )
375 APPROX_DBG( std::cout <<
" " << j <<
" Angles differ too much " << a_diff_test
380 if(
std::abs( d_tl - d01 ) > ( std::max( d_tl, d01 ) * c_relLengthDeviation ) )
382 APPROX_DBG( std::cout <<
" " << j <<
" Lengths differ too much " << d_tl
383 <<
"; " << d01 << std::endl );
393 if( last != c_last_none )
402 int toRemove = last - ( aSrc.
PointCount() - 3 );
432 APPROX_DBG( std::cout <<
" Self-intersection check failed" << std::endl );
436 if( last == c_last_none )
453 if( iarc0 != -1 && iarc1 != -1 )
455 APPROX_DBG( std::cout <<
"Final arcs " << iarc0 <<
" " << iarc1 << std::endl );
465 if( ( p1 - p0 ).EuclideanNorm() < c_circleCloseGap )
483 if(
std::abs( ar0 - ar1 ) <= c_radiusDeviation
484 && ( ac0 - ac1 ).EuclideanNorm() <= c_arcCenterDeviation )
502 TDF_LabelSequence theLabels;
503 aShapeTool->GetFreeShapes( theLabels );
507 if( theLabels.Length() == 1 )
508 return aShapeTool->GetShape( theLabels.Value( 1 ) );
510 TopoDS_Compound aCompound;
511 BRep_Builder aBuilder;
512 aBuilder.MakeCompound( aCompound );
514 for( TDF_LabelSequence::Iterator anIt( theLabels ); anIt.More(); anIt.Next() )
516 TopoDS_Shape aFreeShape;
518 if( !aShapeTool->GetShape( anIt.Value(), aFreeShape ) )
521 aBuilder.Add( aCompound, aFreeShape );
524 if( aCompound.NbChildren() > 0 )
533static Standard_Boolean
rescaleShapes(
const TDF_Label& theLabel,
const gp_XYZ& aScale )
535 if( theLabel.IsNull() )
537 Message::SendFail(
"Null label." );
538 return Standard_False;
541 if( Abs( aScale.X() ) <= gp::Resolution() || Abs( aScale.Y() ) <= gp::Resolution()
542 || Abs( aScale.Z() ) <= gp::Resolution() )
544 Message::SendFail(
"Scale factor is too small." );
545 return Standard_False;
548 Handle( XCAFDoc_ShapeTool ) aShapeTool = XCAFDoc_DocumentTool::ShapeTool( theLabel );
550 if( aShapeTool.IsNull() )
552 Message::SendFail(
"Couldn't find XCAFDoc_ShapeTool attribute." );
553 return Standard_False;
556 Handle( KI_XCAFDoc_AssemblyGraph ) aG =
new KI_XCAFDoc_AssemblyGraph( theLabel );
560 Message::SendFail(
"Couldn't create assembly graph." );
561 return Standard_False;
564 Standard_Boolean anIsDone = Standard_True;
568 aGTrsf.SetVectorialPart( gp_Mat( aScale.X(), 0, 0,
570 0, 0, aScale.Z() ) );
573 BRepBuilderAPI_GTransform aBRepTrsf( aGTrsf );
575 for( Standard_Integer idx = 1; idx <= aG->NbNodes(); idx++ )
577 const KI_XCAFDoc_AssemblyGraph::NodeType aNodeType = aG->GetNodeType( idx );
579 if( ( aNodeType != KI_XCAFDoc_AssemblyGraph::NodeType_Part )
580 && ( aNodeType != KI_XCAFDoc_AssemblyGraph::NodeType_Occurrence ) )
585 const TDF_Label& aLabel = aG->GetNode( idx );
587 if( aNodeType == KI_XCAFDoc_AssemblyGraph::NodeType_Part )
589 const TopoDS_Shape aShape = aShapeTool->GetShape( aLabel );
590 aBRepTrsf.Perform( aShape, Standard_True );
591 if( !aBRepTrsf.IsDone() )
593 Standard_SStream aSS;
594 TCollection_AsciiString anEntry;
595 TDF_Tool::Entry( aLabel, anEntry );
596 aSS <<
"Shape " << anEntry <<
" is not scaled!";
597 Message::SendFail( aSS.str().c_str() );
598 anIsDone = Standard_False;
599 return Standard_False;
601 TopoDS_Shape aScaledShape = aBRepTrsf.Shape();
602 aShapeTool->SetShape( aLabel, aScaledShape );
605 TDF_LabelSequence aSubshapes;
606 aShapeTool->GetSubShapes( aLabel, aSubshapes );
607 for( TDF_LabelSequence::Iterator anItSs( aSubshapes ); anItSs.More(); anItSs.Next() )
609 const TDF_Label& aLSs = anItSs.Value();
610 const TopoDS_Shape aSs = aShapeTool->GetShape( aLSs );
611 const TopoDS_Shape aSs1 = aBRepTrsf.ModifiedShape( aSs );
612 aShapeTool->SetShape( aLSs, aSs1 );
616 aLabel.ForgetAttribute( XCAFDoc_Area::GetID() );
617 aLabel.ForgetAttribute( XCAFDoc_Centroid::GetID() );
618 aLabel.ForgetAttribute( XCAFDoc_Volume::GetID() );
620 else if( aNodeType == KI_XCAFDoc_AssemblyGraph::NodeType_Occurrence )
622 TopLoc_Location aLoc = aShapeTool->GetLocation( aLabel );
623 gp_Trsf aTrsf = aLoc.Transformation();
624 aTrsf.SetTranslationPart( aTrsf.TranslationPart().Multiplied( aScale ) );
625 XCAFDoc_Location::Set( aLabel, aTrsf );
631 return Standard_False;
634 aShapeTool->UpdateAssemblies();
642 BRepAlgoAPI_Fuse mkFuse;
643 TopTools_ListOfShape shapeArguments, shapeTools;
645 for( TopoDS_Shape& sh : aInputShapes )
650 if( shapeArguments.IsEmpty() )
651 shapeArguments.Append( sh );
653 shapeTools.Append( sh );
656 mkFuse.SetRunParallel(
true );
657 mkFuse.SetToFillHistory(
false );
658 mkFuse.SetArguments( shapeArguments );
659 mkFuse.SetTools( shapeTools );
662 if( mkFuse.HasErrors() || mkFuse.HasWarnings() )
666 if( mkFuse.HasErrors() )
668 wxString msg =
_(
"Errors:\n" );
669 wxStringOutputStream os_stream( &msg );
670 wxStdOutputStream out( os_stream );
672 mkFuse.DumpErrors( out );
676 if( mkFuse.HasWarnings() )
678 wxString msg =
_(
"Warnings:\n" );
679 wxStringOutputStream os_stream( &msg );
680 wxStdOutputStream out( os_stream );
682 mkFuse.DumpWarnings( out );
687 if( mkFuse.IsDone() )
689 TopoDS_Shape fusedShape = mkFuse.Shape();
691 ShapeUpgrade_UnifySameDomain unify( fusedShape,
true,
true,
false );
692 unify.History() =
nullptr;
695 TopoDS_Shape unifiedShapes = unify.Shape();
697 if( unifiedShapes.IsNull() )
699 aReporter->
Report(
_(
"** ShapeUpgrade_UnifySameDomain produced a null shape **" ),
704 aOutShape = unifiedShapes;
715 TopoDS_Compound compound;
716 BRep_Builder builder;
717 builder.MakeCompound( compound );
719 for(
const TopoDS_Shape& shape : aInputShapes )
720 builder.Add( compound, shape );
730 TopoDS_Shape outShape;
732 if( aInputShapes.Size() == 1 )
733 return aInputShapes.First();
735 if(
fuseShapes( aInputShapes, outShape, aReporter ) )
744 const TCollection_ExtendedString& aPrefix )
746 Handle( KI_XCAFDoc_AssemblyGraph ) aG =
new KI_XCAFDoc_AssemblyGraph( aLabel );
750 Message::SendFail(
"Couldn't create assembly graph." );
751 return Standard_False;
754 Standard_Boolean anIsDone = Standard_True;
756 for( Standard_Integer idx = 1; idx <= aG->NbNodes(); idx++ )
758 const TDF_Label& lbl = aG->GetNode( idx );
759 Handle( TDataStd_Name ) nameHandle;
761 if( lbl.FindAttribute( TDataStd_Name::GetID(), nameHandle ) )
763 TCollection_ExtendedString
name;
767 name += nameHandle->Get();
770 TDataStd_Name::Set( lbl,
name );
774 TDataStd_Name::Set( lbl, aPrefix );
785 m_app = XCAFApp_Application::GetApplication();
786 m_app->NewDocument(
"MDTV-XCAF", m_doc );
787 m_assy = XCAFDoc_DocumentTool::ShapeTool( m_doc->Main() );
805 if( m_doc->CanClose() == CDM_CCS_OK )
813 const double c_padExtraThickness = 0.005;
815 std::vector<TopoDS_Shape> padShapes;
829 double Zpos, thickness;
835 if( pcb_layer ==
F_Cu )
836 thickness += c_padExtraThickness;
837 else if( pcb_layer ==
B_Cu )
838 thickness -= c_padExtraThickness;
841 TopoDS_Shape testShape;
855 if( testShape.IsNull() )
857 std::vector<TopoDS_Shape> testShapes;
861 if( testShapes.size() > 0 )
862 testShape = testShapes.front();
867 if( pcb_layer ==
F_Cu || pcb_layer ==
B_Cu )
873 if( pcb_layer ==
F_Cu )
875 else if( pcb_layer ==
B_Cu )
891 double f_pos, f_thickness;
892 double b_pos, b_thickness;
899 f_thickness += c_padExtraThickness;
900 b_thickness -= c_padExtraThickness;
903 double top = std::max( f_pos, f_pos + f_thickness );
904 double bottom = std::min( b_pos, b_pos + b_thickness );
905 double hole_height =
top - bottom;
907 TopoDS_Shape plating;
915 hole_height, bottom, aOrigin ) )
917 padShapes.push_back( plating );
931 if( seg_hole->GetSeg().A == seg_hole->GetSeg().B )
948 padShapes.push_back( plating );
960 if( !padShapes.empty() )
965 TopTools_ListOfShape padShapesList;
967 for(
const TopoDS_Shape& shape : padShapes )
968 padShapesList.Append( shape );
974 for(
const TopoDS_Shape& shape : padShapes )
985 const VECTOR2D& aOrigin,
bool aCutCopper,
bool aCutBody )
987 double margin = 0.001;
995 double f_pos, f_thickness;
996 double b_pos, b_thickness;
999 double top = std::max( f_pos, f_pos + f_thickness );
1000 double bottom = std::min( b_pos, b_pos + b_thickness );
1002 double holeZsize = (
top - bottom ) + ( margin * 2 );
1004 double boardDrill = aShape.
GetWidth();
1005 double copperDrill = boardDrill - aPlatingThickness * 2;
1007 TopoDS_Shape copperHole, boardHole;
1012 holeZsize, bottom - margin, aOrigin ) )
1025 holeZsize, bottom - margin, aOrigin ) )
1041 const wxString& aNetname )
1043 double f_pos, f_thickness;
1044 double b_pos, b_thickness;
1047 double top = std::max( f_pos, f_pos + f_thickness );
1048 double bottom = std::min( b_pos, b_pos + b_thickness );
1050 TopoDS_Shape plating;
1053 (
top - bottom ), bottom, aOrigin ) )
1075 double margin = 0.001;
1079 double copperMargin = 0.5;
1081 double start_pos, start_thickness;
1082 double end_pos, end_thickness;
1087 double top = std::max( { start_pos, start_pos + start_thickness,
1088 end_pos, end_pos + end_thickness } );
1089 double bottom = std::min( { start_pos, start_pos + start_thickness,
1090 end_pos, end_pos + end_thickness } );
1093 if( aLayerStart ==
F_Cu || aLayerEnd ==
F_Cu )
1094 top += copperMargin;
1095 if( aLayerStart ==
B_Cu || aLayerEnd ==
B_Cu )
1096 bottom -= copperMargin;
1098 double holeZsize = (
top - bottom ) + ( margin * 2 );
1099 double holeZpos = bottom - margin;
1101 double backdrillDiameter = aShape.
GetWidth();
1103 TopoDS_Shape backdrillHole;
1107 backdrillDiameter, holeZsize, holeZpos, aOrigin ) )
1124 bool aFrontSide,
const VECTOR2D& aOrigin )
1126 wxLogTrace(
traceKiCad2Step, wxT(
"AddCounterbore: pos=(%d,%d) diameter=%d depth=%d frontSide=%d origin=(%f,%f)" ),
1127 aPosition.
x, aPosition.
y, aDiameter, aDepth, aFrontSide ? 1 : 0, aOrigin.
x, aOrigin.
y );
1130 if( aDiameter <= 0 || aDepth <= 0 )
1132 wxLogTrace(
traceKiCad2Step, wxT(
"AddCounterbore: REJECTED - invalid diameter=%d or depth=%d" ),
1133 aDiameter, aDepth );
1137 double margin = 0.001;
1140 double copperMargin = 0.5;
1143 double boardZpos, boardThickness;
1147 double f_pos, f_thickness, b_pos, b_thickness;
1154 double topOuterSurface = std::max( f_pos, f_pos + f_thickness );
1155 double bottomOuterSurface = std::min( b_pos, b_pos + b_thickness );
1157 wxLogTrace(
traceKiCad2Step, wxT(
"AddCounterbore: boardZpos=%f boardThickness=%f f_pos=%f f_thickness=%f topOuter=%f bottomOuter=%f" ),
1158 boardZpos, boardThickness, f_pos, f_thickness, topOuterSurface, bottomOuterSurface );
1161 double diameter_mm =
pcbIUScale.IUTomm( aDiameter );
1162 double depth_mm =
pcbIUScale.IUTomm( aDepth );
1163 double radius_mm = diameter_mm / 2.0;
1165 wxLogTrace(
traceKiCad2Step, wxT(
"AddCounterbore: diameter_mm=%f depth_mm=%f radius_mm=%f" ),
1166 diameter_mm, depth_mm, radius_mm );
1170 double cylinderZpos;
1171 double cylinderHeight;
1177 cylinderZpos = topOuterSurface - depth_mm - margin;
1178 cylinderHeight = depth_mm + copperMargin + 2 * margin;
1184 cylinderZpos = bottomOuterSurface - copperMargin - margin;
1185 cylinderHeight = depth_mm + copperMargin + 2 * margin;
1189 double posX_mm =
pcbIUScale.IUTomm( aPosition.
x - aOrigin.
x );
1190 double posY_mm = -
pcbIUScale.IUTomm( aPosition.
y - aOrigin.
y );
1192 wxLogTrace(
traceKiCad2Step, wxT(
"AddCounterbore: posX_mm=%f posY_mm=%f cylinderZpos=%f cylinderHeight=%f" ),
1193 posX_mm, posY_mm, cylinderZpos, cylinderHeight );
1199 gp_Ax2 axis( gp_Pnt( posX_mm, posY_mm, cylinderZpos ), gp::DZ() );
1201 TopoDS_Shape cylinder = BRepPrimAPI_MakeCylinder( axis, radius_mm, cylinderHeight );
1203 if( cylinder.IsNull() )
1205 wxLogTrace(
traceKiCad2Step, wxT(
"AddCounterbore: FAILED - cylinder shape is null" ) );
1206 m_reporter->Report(
_(
"Failed to create counterbore cylinder shape" ),
1215 wxLogTrace(
traceKiCad2Step, wxT(
"AddCounterbore: SUCCESS - added cylinder. boardCutouts=%zu copperCutouts=%zu" ),
1218 catch(
const Standard_Failure& e )
1220 wxLogTrace(
traceKiCad2Step, wxT(
"AddCounterbore: EXCEPTION - %s" ), e.GetMessageString() );
1221 m_reporter->Report( wxString::Format(
_(
"OCC exception creating counterbore: %s" ),
1222 e.GetMessageString() ),
1232 int aAngle,
bool aFrontSide,
const VECTOR2D& aOrigin )
1234 wxLogTrace(
traceKiCad2Step, wxT(
"AddCountersink: pos=(%d,%d) diameter=%d depth=%d angle=%d frontSide=%d origin=(%f,%f)" ),
1235 aPosition.
x, aPosition.
y, aDiameter, aDepth, aAngle, aFrontSide ? 1 : 0, aOrigin.
x, aOrigin.
y );
1240 if( aDiameter <= 0 || aAngle <= 0 )
1242 wxLogTrace(
traceKiCad2Step, wxT(
"AddCountersink: REJECTED - invalid diameter=%d or angle=%d" ),
1243 aDiameter, aAngle );
1247 double margin = 0.001;
1250 double copperMargin = 0.5;
1253 double boardZpos, boardThickness;
1257 double f_pos, f_thickness, b_pos, b_thickness;
1262 double topOuterSurface = std::max( f_pos, f_pos + f_thickness );
1263 double bottomOuterSurface = std::min( b_pos, b_pos + b_thickness );
1265 wxLogTrace(
traceKiCad2Step, wxT(
"AddCountersink: boardZpos=%f boardThickness=%f f_pos=%f f_thickness=%f topOuter=%f bottomOuter=%f" ),
1266 boardZpos, boardThickness, f_pos, f_thickness, topOuterSurface, bottomOuterSurface );
1269 double diameter_mm =
pcbIUScale.IUTomm( aDiameter );
1270 double radius_mm = diameter_mm / 2.0;
1274 double halfAngleRad = ( aAngle / 10.0 ) *
M_PI / 180.0 / 2.0;
1282 depth_mm = radius_mm / tan( halfAngleRad );
1283 wxLogTrace(
traceKiCad2Step, wxT(
"AddCountersink: depth not specified, calculated depth_mm=%f from radius=%f and angle" ),
1284 depth_mm, radius_mm );
1291 wxLogTrace(
traceKiCad2Step, wxT(
"AddCountersink: diameter_mm=%f depth_mm=%f radius_mm=%f halfAngleRad=%f (deg=%f)" ),
1292 diameter_mm, depth_mm, radius_mm, halfAngleRad, halfAngleRad * 180.0 /
M_PI );
1303 double bottomRadius_mm = radius_mm - depth_mm * tan( halfAngleRad );
1305 wxLogTrace(
traceKiCad2Step, wxT(
"AddCountersink: bottomRadius_mm=%f (before clamp), tan(halfAngle)=%f" ),
1306 bottomRadius_mm, tan( halfAngleRad ) );
1308 if( bottomRadius_mm < 0 )
1309 bottomRadius_mm = 0;
1314 double coneHeight = depth_mm + copperMargin + margin;
1318 double posX_mm =
pcbIUScale.IUTomm( aPosition.
x - aOrigin.
x );
1319 double posY_mm = -
pcbIUScale.IUTomm( aPosition.
y - aOrigin.
y );
1330 coneZpos = topOuterSurface - depth_mm - margin;
1331 r1 = bottomRadius_mm;
1333 r2 = radius_mm + ( copperMargin + margin ) * tan( halfAngleRad );
1335 wxLogTrace(
traceKiCad2Step, wxT(
"AddCountersink: FRONT - coneZpos=%f r1=%f r2=%f coneHeight=%f" ),
1336 coneZpos, r1, r2, coneHeight );
1338 gp_Ax2 axis( gp_Pnt( posX_mm, posY_mm, coneZpos ), gp::DZ() );
1339 cone = BRepPrimAPI_MakeCone( axis, r1, r2, coneHeight );
1346 coneZpos = bottomOuterSurface - copperMargin - margin;
1348 r1 = radius_mm + ( copperMargin + margin ) * tan( halfAngleRad );
1349 r2 = bottomRadius_mm;
1351 wxLogTrace(
traceKiCad2Step, wxT(
"AddCountersink: BACK - coneZpos=%f r1=%f r2=%f coneHeight=%f" ),
1352 coneZpos, r1, r2, coneHeight );
1354 gp_Ax2 axis( gp_Pnt( posX_mm, posY_mm, coneZpos ), gp::DZ() );
1355 cone = BRepPrimAPI_MakeCone( axis, r1, r2, coneHeight );
1360 wxLogTrace(
traceKiCad2Step, wxT(
"AddCountersink: FAILED - cone shape is null" ) );
1361 m_reporter->Report(
_(
"Failed to create countersink cone shape" ),
1370 wxLogTrace(
traceKiCad2Step, wxT(
"AddCountersink: SUCCESS - added cone. boardCutouts=%zu copperCutouts=%zu" ),
1373 catch(
const Standard_Failure& e )
1375 wxLogTrace(
traceKiCad2Step, wxT(
"AddCountersink: EXCEPTION - %s" ), e.GetMessageString() );
1376 m_reporter->Report( wxString::Format(
_(
"OCC exception creating countersink: %s" ),
1377 e.GetMessageString() ),
1387 int aAngle,
bool aFrontSide )
1389 std::map<PCB_LAYER_ID, int> knockouts;
1392 double f_pos, f_thickness, b_pos, b_thickness;
1396 double topOuterSurface = std::max( f_pos, f_pos + f_thickness );
1397 double bottomOuterSurface = std::min( b_pos, b_pos + b_thickness );
1400 double diameter_mm =
pcbIUScale.IUTomm( aDiameter );
1401 double radius_mm = diameter_mm / 2.0;
1405 double halfAngleRad = 0.0;
1410 halfAngleRad = ( aAngle / 10.0 ) *
M_PI / 180.0 / 2.0;
1414 depth_mm = radius_mm / tan( halfAngleRad );
1425 double featureTop, featureBottom;
1429 featureTop = topOuterSurface;
1430 featureBottom = topOuterSurface - depth_mm;
1434 featureBottom = bottomOuterSurface;
1435 featureTop = bottomOuterSurface + depth_mm;
1438 wxLogTrace(
traceKiCad2Step, wxT(
"GetCopperLayerKnockouts: featureTop=%f featureBottom=%f depth_mm=%f frontSide=%d" ),
1439 featureTop, featureBottom, depth_mm, aFrontSide ? 1 : 0 );
1448 double layerZ, layerThickness;
1452 double layerTop = std::max( layerZ, layerZ + layerThickness );
1453 double layerBottom = std::min( layerZ, layerZ + layerThickness );
1457 bool layerInRange = ( layerTop >= featureBottom && layerBottom <= featureTop );
1459 wxLogTrace(
traceKiCad2Step, wxT(
"GetCopperLayerKnockouts: layer %d Z=[%f, %f] feature=[%f, %f] inRange=%d" ),
1460 static_cast<int>( layer ), layerBottom, layerTop, featureBottom, featureTop, layerInRange ? 1 : 0 );
1465 int knockoutDiameter;
1471 double layerSurfaceZ;
1475 layerSurfaceZ = layerTop;
1480 layerSurfaceZ = layerBottom;
1484 double distanceFromSurface;
1486 distanceFromSurface = topOuterSurface - layerSurfaceZ;
1488 distanceFromSurface = layerSurfaceZ - bottomOuterSurface;
1491 double radiusAtLayer_mm = radius_mm - distanceFromSurface * tan( halfAngleRad );
1493 if( radiusAtLayer_mm <= 0 )
1495 wxLogTrace(
traceKiCad2Step, wxT(
"GetCopperLayerKnockouts: layer %d - countersink tapers to point before this layer" ),
1496 static_cast<int>( layer ) );
1500 knockoutDiameter =
pcbIUScale.mmToIU( radiusAtLayer_mm * 2.0 );
1501 wxLogTrace(
traceKiCad2Step, wxT(
"GetCopperLayerKnockouts: layer %d (countersink) - distFromSurface=%f radiusAtLayer=%f diameter=%d" ),
1502 static_cast<int>( layer ), distanceFromSurface, radiusAtLayer_mm, knockoutDiameter );
1507 knockoutDiameter = aDiameter;
1508 wxLogTrace(
traceKiCad2Step, wxT(
"GetCopperLayerKnockouts: layer %d (counterbore) - diameter=%d" ),
1509 static_cast<int>( layer ), knockoutDiameter );
1512 knockouts[layer] = knockoutDiameter;
1520 double& aThickness )
1523 static const double c_silkscreenAboveCopper = 0.04;
1524 static const double c_soldermaskAboveCopper = 0.015;
1532 double f_pos, f_thickness;
1534 double top = std::max( f_pos, f_pos + f_thickness );
1537 aZPos =
top + c_silkscreenAboveCopper;
1539 aZPos =
top + c_soldermaskAboveCopper;
1545 double b_pos, b_thickness;
1547 double bottom = std::min( b_pos, b_pos + b_thickness );
1550 aZPos = bottom - c_silkscreenAboveCopper;
1552 aZPos = bottom - c_soldermaskAboveCopper;
1560 double& aThickness )
1564 bool wasPrepreg =
false;
1566 const std::vector<BOARD_STACKUP_ITEM*>& materials =
m_stackup.GetList();
1569 for(
auto it = materials.rbegin(); it != materials.rend(); ++it )
1575 if( aLayer ==
B_Cu )
1620 double f_pos, f_thickness;
1621 double b_pos, b_thickness;
1624 double top = std::min( f_pos, f_pos + f_thickness );
1625 double bottom = std::max( b_pos, b_pos + b_thickness );
1627 aThickness = (
top - bottom );
1630 wxASSERT( aZPos == 0.0 );
1635 const VECTOR2D& aOrigin,
const wxString& aNetname )
1637 bool success =
true;
1645 double z_pos, thickness;
1648 std::vector<TopoDS_Shape>* targetVec =
nullptr;
1656 else if( aLayer ==
F_Mask )
1663 m_reporter->Report( wxString::Format(
_(
"Could not add shape (%d points) to copper layer %s." ),
1676 bool aBottom,
const VECTOR2D& aPosition,
double aRotation,
const VECTOR3D& aOffset,
1677 const VECTOR3D& aOrientation,
const VECTOR3D& aScale,
bool aSubstituteModels )
1679 if( aFileNameUTF8.empty() )
1681 m_reporter->Report( wxString::Format(
_(
"No model defined for %s." ), aRefDes ),
1686 wxString fileName( wxString::FromUTF8( aFileNameUTF8.c_str() ) );
1691 wxString errorMessage;
1693 if( !
getModelLabel( aFileNameUTF8, aScale, lmodel, aSubstituteModels, &errorMessage ) )
1695 if( errorMessage.IsEmpty() )
1696 errorMessage.Printf(
_(
"No model for filename '%s'." ), fileName );
1703 TopLoc_Location toploc;
1705 if( !
getModelLocation( aBottom, aPosition, aRotation, aOffset, aOrientation, toploc ) )
1707 m_reporter->Report( wxString::Format(
_(
"No location data for filename '%s'." ), fileName ),
1713 TDF_Label llabel = m_assy->AddComponent(
m_assy_label, lmodel, toploc );
1715 if( llabel.IsNull() )
1717 m_reporter->Report( wxString::Format(
_(
"Could not add component with filename '%s'." ), fileName ),
1723 TCollection_ExtendedString refdes( aRefDes.c_str() );
1724 TDataStd_Name::Set( llabel, refdes );
1798 const VECTOR2D& aEndPoint,
double aWidth,
double aThickness,
1799 double aZposition,
const VECTOR2D& aOrigin )
1806 double len = ( aEndPoint - aStartPoint ).EuclideanNorm();
1807 double h_width = aWidth/2.0;
1809 coords[0] =
VECTOR2D{ 0.0, h_width };
1812 coords[1] =
VECTOR2D{ len, h_width };
1815 coords[2] =
VECTOR2D{ len + h_width, 0.0 };
1818 coords[3] =
VECTOR2D{ len, -h_width };
1821 coords[4] =
VECTOR2D{ 0, -h_width };
1824 coords[5] =
VECTOR2D{ -h_width, 0.0 };
1827 EDA_ANGLE seg_angle( aEndPoint - aStartPoint );
1829 for(
int ii = 0; ii < 6; ii++ )
1832 coords[ii] += aStartPoint;
1837 gp_Pnt coords3D[ 6 ];
1839 for(
int ii = 0; ii < 6; ii++ )
1841 coords3D[ii] = gp_Pnt(
pcbIUScale.IUTomm( coords[ii].
x - aOrigin.
x ),
1842 -
pcbIUScale.IUTomm( coords[ii].
y - aOrigin.
y ), aZposition );
1846 BRepBuilderAPI_MakeWire wire;
1847 bool success =
true;
1859 Handle( Geom_Circle )
circle = GC_MakeCircle( coords3D[1],
1864 edge = BRepBuilderAPI_MakeEdge(
circle );
1869 edge = BRepBuilderAPI_MakeEdge( coords3D[0], coords3D[1] );
1872 Handle( Geom_TrimmedCurve ) arcOfCircle =
1873 GC_MakeArcOfCircle( coords3D[1],
1877 edge = BRepBuilderAPI_MakeEdge( arcOfCircle );
1880 edge = BRepBuilderAPI_MakeEdge( coords3D[3], coords3D[4] );
1883 Handle( Geom_TrimmedCurve ) arcOfCircle2 =
1884 GC_MakeArcOfCircle( coords3D[4],
1888 edge = BRepBuilderAPI_MakeEdge( arcOfCircle2 );
1892 catch(
const Standard_Failure& e )
1894 m_reporter->Report( wxString::Format(
_(
"OCC exception building shape segment: %s" ),
1895 e.GetMessageString() ),
1900 BRepBuilderAPI_MakeFace face;
1904 gp_Pln plane( coords3D[0], gp::DZ() );
1905 face = BRepBuilderAPI_MakeFace( plane, wire );
1907 catch(
const Standard_Failure& e )
1909 m_reporter->Report( wxString::Format(
_(
"OCC exception building face: %s" ),
1910 e.GetMessageString() ),
1915 if( aThickness != 0.0 )
1917 aShape = BRepPrimAPI_MakePrism( face, gp_Vec( 0, 0, aThickness ) );
1919 if( aShape.IsNull() )
1921 m_reporter->Report(
_(
"Failed to create a prismatic shape" ),
1938 double aZposition,
const VECTOR2D& aOrigin )
1940 std::vector<TopoDS_Shape> testShapes;
1943 aHeight, aZposition, aOrigin );
1945 if( testShapes.size() > 0 )
1946 aShape = testShapes.front();
1969 double aMergeOCCMaxDist,
double aZposition,
const VECTOR2D& aOrigin,
1973 [&](
const VECTOR2D& aKiCoords ) -> gp_Pnt
1975 return gp_Pnt(
pcbIUScale.IUTomm( aKiCoords.x - aOrigin.
x ),
1976 -
pcbIUScale.IUTomm( aKiCoords.y - aOrigin.
y ), aZposition );
1986 gp_Pnt start = toPoint( aPt0 );
1987 gp_Pnt
end = toPoint( aPt1 );
1989 BRepBuilderAPI_MakeEdge mkEdge( start,
end );
1991 if( !mkEdge.IsDone() || mkEdge.Edge().IsNull() )
1993 aReporter->
Report( wxString::Format(
_(
"Failed to make segment edge (%d %d) -> (%d %d), "
2001 aMkWire.Add( mkEdge.Edge() );
2003 if( aMkWire.Error() != BRepLib_WireDone )
2005 aReporter->
Report( wxString::Format(
_(
"Failed to add segment edge (%d %d) -> (%d %d)" ),
2019 Handle( Geom_Curve ) curve;
2021 if( aArc.GetCentralAngle() ==
ANGLE_360 )
2023 gp_Ax2 axis = gp::XOY();
2024 axis.SetLocation( toPoint( aArc.GetCenter() ) );
2026 curve = GC_MakeCircle( axis,
pcbIUScale.IUTomm( aArc.GetRadius() ) ).Value();
2030 curve = GC_MakeArcOfCircle( toPoint( aPt0 ), toPoint( aArc.GetArcMid() ),
2031 toPoint( aArc.GetP1() ) ).Value();
2034 if( curve.IsNull() )
2037 aMkWire.Add( BRepBuilderAPI_MakeEdge( curve ) );
2039 if( !aMkWire.IsDone() )
2041 aReporter->
Report( wxString::Format(
_(
"Failed to add arc curve from (%d %d), arc p0 "
2042 "(%d %d), mid (%d %d), p1 (%d %d)" ),
2044 aArc.GetP0().x, aArc.GetP0().y,
2045 aArc.GetArcMid().x, aArc.GetArcMid().y,
2046 aArc.GetP1().x, aArc.GetP1().y ),
2056 bool isFirstShape =
true;
2069 if( nextShape != -1 )
2075 lastPt = aChain.
CPoint( i );
2085 firstPt = currentArc.
GetP0();
2090 lastPt = currentArc.
GetP0();
2092 if( addArc( lastPt, currentArc ) )
2093 lastPt = currentArc.
GetP1();
2112 isFirstShape =
false;
2115 if( lastPt != firstPt && !
addSegment( lastPt, firstPt ) )
2117 aReporter->
Report( wxString::Format(
_(
"Failed to close wire at %d, %d -> %d, %d **" ),
2119 firstPt.
x, firstPt.
y ),
2125 catch(
const Standard_Failure& e )
2127 aReporter->
Report( wxString::Format(
_(
"OCC exception creating wire: %s" ),
2128 e.GetMessageString() ),
2138 bool aConvertToArcs,
double aThickness,
double aZposition,
2146 if( aConvertToArcs )
2150 for(
size_t polyId = 0; polyId < approximated.
CPolygons().size(); polyId++ )
2154 for(
size_t contId = 0; contId < polygon.size(); contId++ )
2158 fallbackPoly = workingPoly;
2159 workingPoly = approximated;
2178 auto toPoint = [&](
const VECTOR2D& aKiCoords ) -> gp_Pnt
2180 return gp_Pnt(
pcbIUScale.IUTomm( aKiCoords.x - aOrigin.
x ),
2181 -
pcbIUScale.IUTomm( aKiCoords.y - aOrigin.
y ), aZposition );
2185 gp_Pln basePlane( gp_Pnt( 0.0, 0.0, aZposition ),
2186 std::signbit( aThickness ) ? -gp::DZ() : gp::DZ() );
2188 for(
size_t polyId = 0; polyId < workingPoly.
CPolygons().size(); polyId++ )
2192 auto tryMakeWire = [
this, &aZposition,
2193 &aOrigin](
const SHAPE_LINE_CHAIN& aContour,
bool aAllowRetry ) -> TopoDS_Wire
2196 BRepLib_MakeWire mkWire;
2200 if( mkWire.IsDone() )
2202 wire = mkWire.Wire();
2207 wxString::Format(
_(
"Wire not done (contour points %d): OCC error %d\n"
2208 "z: %g; bounding box: %s" ),
2209 static_cast<int>( aContour.PointCount() ),
2210 static_cast<int>( mkWire.Error() ),
2215 if( !wire.IsNull() )
2217 BRepAlgoAPI_Check check( wire,
false,
true );
2219 if( !check.IsValid() )
2221 m_reporter->Report( wxString::Format(
_(
"Wire self-interference check failed\n"
2222 "z: %g; bounding box: %s" ),
2234 BRepBuilderAPI_MakeFace mkFace;
2236 for(
size_t contId = 0; contId < polygon.size(); contId++ )
2242 bool allow_retry = aConvertToArcs ? true :
false;
2244 TopoDS_Wire wire = tryMakeWire( polygon[contId], allow_retry );
2246 if( aConvertToArcs && wire.IsNull() )
2248 m_reporter->Report( wxString::Format(
_(
"Using non-simplified polygon." ) ),
2252 allow_retry =
false;
2253 wire = tryMakeWire( fallbackPoly.
CPolygon( polyId )[contId], allow_retry );
2258 if( !wire.IsNull() )
2260 if( basePlane.Axis().Direction().Z() < 0 )
2263 mkFace = BRepBuilderAPI_MakeFace( basePlane, wire );
2267 m_reporter->Report( wxString::Format( wxT(
"** Outline skipped **\n"
2268 "z: %g; bounding box: %s" ),
2277 if( !wire.IsNull() )
2279 if( basePlane.Axis().Direction().Z() > 0 )
2286 m_reporter->Report( wxString::Format( wxT(
"** Hole skipped **\n"
2287 "z: %g; bounding box: %s" ),
2294 catch(
const Standard_Failure& e )
2296 m_reporter->Report( wxString::Format(
_(
"OCC exception creating contour %d: %s" ),
2297 static_cast<int>( contId ),
2298 e.GetMessageString() ),
2304 if( mkFace.IsDone() )
2306 TopoDS_Shape faceShape = mkFace.Shape();
2308 if( aThickness != 0.0 )
2310 TopoDS_Shape prism = BRepPrimAPI_MakePrism( faceShape, gp_Vec( 0, 0, aThickness ) );
2311 aShapes.push_back( prism );
2313 if( prism.IsNull() )
2321 aShapes.push_back( faceShape );
2337 {
_HKI(
"Green" ), wxColor( 20, 51, 36 ) },
2338 {
_HKI(
"Red" ), wxColor( 181, 19, 21 ) },
2339 {
_HKI(
"Blue" ), wxColor( 2, 59, 162 ) },
2340 {
_HKI(
"Purple" ), wxColor( 32, 2, 53 ) },
2341 {
_HKI(
"Black" ), wxColor( 11, 11, 11 ) },
2342 {
_HKI(
"White" ), wxColor( 245, 245, 245 ) },
2343 {
_HKI(
"Yellow" ), wxColor( 194, 195, 0 ) },
2344 {
_HKI(
"User defined" ), wxColor( 128, 128, 128 ) }
2354 if( aColorStr.StartsWith( wxT(
"#" ) ) )
2356 aColorOut =
COLOR4D( aColorStr );
2361 const std::vector<FAB_LAYER_COLOR>& colors =
2368 if( fabColor.GetName() == aColorStr )
2370 aColorOut = fabColor.GetColor( aType );
2392 Handle( XCAFDoc_VisMaterialTool ) visMatTool = XCAFDoc_DocumentTool::VisMaterialTool( m_doc->Main() );
2397 m_reporter->Report( wxString::Format( wxT(
"Build board outlines (%d outlines) with %d points." ),
2402 double boardThickness;
2422 for(
size_t contId = 0; contId < polygon.size(); contId++ )
2426 polyset.
Append( contour );
2433 m_reporter->Report(
_(
"OCC error creating main outline." ),
2442 m_reporter->Report(
_(
"OCC error creating hole in main outline." ),
2454 BRepBndLib::Add( brdShape, brdBndBox );
2457 m_reporter->Report( wxString::Format( wxT(
"Build board cutouts and holes (%d hole(s))." ),
2462 [&brdBndBox]( std::vector<TopoDS_Shape>& input, Bnd_BoundSortBox& bsbHoles )
2466 Bnd_Box brdWithHolesBndBox = brdBndBox;
2468 Handle( Bnd_HArray1OfBox ) holeBoxSet =
new Bnd_HArray1OfBox( 0, input.size() - 1 );
2470 for(
size_t i = 0; i < input.size(); i++ )
2473 BRepBndLib::Add( input[i], bbox );
2474 brdWithHolesBndBox.Add( bbox );
2475 ( *holeBoxSet )[i] = bbox;
2478 bsbHoles.Initialize( brdWithHolesBndBox, holeBoxSet );
2481 auto subtractShapesMap =
2482 [&
tp,
this](
const wxString& aWhat, std::map<wxString, std::vector<TopoDS_Shape>>& aShapesMap,
2483 std::vector<TopoDS_Shape>& aHolesList, Bnd_BoundSortBox& aBSBHoles )
2485 m_reporter->Report( wxString::Format(
_(
"Subtracting holes for %s" ), aWhat ),
2488 for(
auto& [netname, vec] : aShapesMap )
2492 auto subtractLoopFn = [&](
const int shapeId )
2494 TopoDS_Shape& shape = vec[shapeId];
2497 BRepBndLib::Add( shape, shapeBbox );
2499 TopTools_ListOfShape holelist;
2502 std::unique_lock lock( mutex );
2504 const TColStd_ListOfInteger& indices = aBSBHoles.Compare( shapeBbox );
2506 for(
const Standard_Integer&
index : indices )
2507 holelist.Append( aHolesList[
index] );
2510 if( holelist.IsEmpty() )
2513 TopTools_ListOfShape cutArgs;
2514 cutArgs.Append( shape );
2516 BRepAlgoAPI_Cut
cut;
2518 cut.SetRunParallel(
true );
2519 cut.SetToFillHistory(
false );
2521 cut.SetArguments( cutArgs );
2522 cut.SetTools( holelist );
2525 if(
cut.HasErrors() ||
cut.HasWarnings() )
2527 m_reporter->Report( wxString::Format(
_(
"** Got problems while cutting "
2534 if(
cut.HasErrors() )
2536 wxString msg =
_(
"Errors:\n" );
2537 wxStringOutputStream os_stream( &msg );
2538 wxStdOutputStream out( os_stream );
2540 cut.DumpErrors( out );
2544 if(
cut.HasWarnings() )
2546 wxString msg =
_(
"Warnings:\n" );
2547 wxStringOutputStream os_stream( &msg );
2548 wxStdOutputStream out( os_stream );
2550 cut.DumpWarnings( out );
2555 shape =
cut.Shape();
2558 tp.submit_loop( 0, vec.size(), subtractLoopFn ).wait();
2562 auto subtractShapes =
2563 [subtractShapesMap](
const wxString& aWhat, std::vector<TopoDS_Shape>& aShapesList,
2564 std::vector<TopoDS_Shape>& aHolesList, Bnd_BoundSortBox& aBSBHoles )
2566 std::map<wxString, std::vector<TopoDS_Shape>> aShapesMap{ { wxEmptyString, aShapesList } };
2568 subtractShapesMap( aWhat, aShapesMap, aHolesList, aBSBHoles );
2569 aShapesList = aShapesMap[wxEmptyString];
2575 Bnd_BoundSortBox bsbHoles;
2583 Bnd_BoundSortBox bsbHoles;
2592 std::map<wxString, TopTools_ListOfShape> shapesToFuseMap;
2594 auto addShapes = [&shapesToFuseMap](
const wxString& aNetname,
2595 const std::vector<TopoDS_Shape>& aShapes )
2597 for(
const TopoDS_Shape& shape : aShapes )
2598 shapesToFuseMap[aNetname].Append( shape );
2602 addShapes( netname, shapes );
2605 addShapes( netname, shapes );
2608 addShapes( netname, shapes );
2615 auto fuseLoopFn = [&](
const wxString& aNetname )
2617 auto& toFuse = shapesToFuseMap[aNetname];
2620 if( !fusedShape.IsNull() )
2622 std::unique_lock lock( mutex );
2632 BS::multi_future<void> mf;
2634 for(
const auto& [netname,
_] : shapesToFuseMap )
2635 mf.push_back(
tp.submit_task( [&, netname]() { fuseLoopFn( netname ); } ) );
2652 auto pushToAssemblyMap =
2653 [&](
const std::map<wxString, std::vector<TopoDS_Shape>>& aShapesMap,
2654 const TDF_Label& aVisMatLabel,
const wxString& aShapeName,
bool aCompoundNets,
2655 bool aCompoundAll,
const wxString& aNiceName )
2657 std::map<wxString, std::vector<TopoDS_Shape>> shapesMap;
2661 std::vector<TopoDS_Shape> allShapes;
2663 for(
const auto& [netname, shapesList] : aShapesMap )
2664 allShapes.insert( allShapes.end(), shapesList.begin(), shapesList.end() );
2666 if( !allShapes.empty() )
2667 shapesMap[wxEmptyString].emplace_back(
makeCompound( allShapes ) );
2671 shapesMap = aShapesMap;
2674 for(
const auto& [netname, shapesList] : shapesMap )
2676 std::vector<TopoDS_Shape> newList;
2681 newList = shapesList;
2685 for( TopoDS_Shape& shape : newList )
2687 Handle( TDataStd_TreeNode ) node;
2690 TDF_Label lbl = m_assy->AddComponent(
m_assy_label, shape,
false );
2697 lbl.FindAttribute( XCAFDoc::ShapeRefGUID(), node );
2698 TDF_Label shpLbl = node->Father()->Label();
2700 if( !shpLbl.IsNull() )
2702 if( visMatTool && !aVisMatLabel.IsNull() )
2703 visMatTool->SetShapeMaterial( shpLbl, aVisMatLabel );
2709 shapeName << aShapeName;
2711 if( !netname.empty() )
2714 shapeName << netname;
2717 if( newList.size() > 1 )
2723 TCollection_ExtendedString partname( shapeName.ToUTF8().data() );
2724 TDataStd_Name::Set( shpLbl, partname );
2732 auto pushToAssembly =
2733 [&](
const std::vector<TopoDS_Shape>& aShapesList,
const TDF_Label& aVisMatLabel,
2734 const wxString& aShapeName,
bool aCompound,
const wxString& aNiceName )
2736 const std::map<wxString, std::vector<TopoDS_Shape>> shapesMap{ { wxEmptyString, aShapesList } };
2738 pushToAssemblyMap( shapesMap, aVisMatLabel, aShapeName, aCompound, aCompound, aNiceName );
2742 [&](
const TCollection_AsciiString& aName,
const Quantity_ColorRGBA& aBaseColor,
2743 double aMetallic,
double aRoughness ) -> TDF_Label
2745 Handle( XCAFDoc_VisMaterial ) vismat =
new XCAFDoc_VisMaterial;
2746 XCAFDoc_VisMaterialPBR pbr;
2747 pbr.BaseColor = aBaseColor;
2748 pbr.Metallic = aMetallic;
2749 pbr.Roughness = aRoughness;
2750 vismat->SetPbrMaterial( pbr );
2751 return visMatTool->AddMaterial( vismat, aName );
2758 Quantity_ColorRGBA board_color( 0.42f, 0.45f, 0.29f, 0.98f );
2759 Quantity_ColorRGBA front_silk_color( 1.0f, 1.0f, 1.0f, 0.9f );
2760 Quantity_ColorRGBA back_silk_color = front_silk_color;
2761 Quantity_ColorRGBA front_mask_color( 0.08f, 0.2f, 0.14f, 0.83f );
2762 Quantity_ColorRGBA back_mask_color = front_mask_color;
2772 if( item->GetBrdLayerId() ==
F_Mask || item->GetBrdLayerId() ==
B_Mask )
2776 if( item->GetBrdLayerId() ==
F_Mask )
2777 front_mask_color.SetValues( col.
r, col.
g, col.
b, col.
a );
2779 back_mask_color.SetValues( col.
r, col.
g, col.
b, col.
a );
2782 if( item->GetBrdLayerId() ==
F_SilkS )
2783 front_silk_color.SetValues( col.
r, col.
g, col.
b, col.
a );
2784 else if( item->GetBrdLayerId() ==
B_SilkS )
2785 back_silk_color.SetValues( col.
r, col.
g, col.
b, col.
a );
2788 board_color.SetValues( col.
r, col.
g, col.
b, col.
a );
2794 board_color = front_mask_color;
2795 board_color.SetAlpha( 1.0 );
2798 TDF_Label front_mask_mat = makeMaterial(
"soldermask", front_mask_color, 0.0, 0.6 );
2799 TDF_Label back_mask_mat = makeMaterial(
"soldermask", back_mask_color, 0.0, 0.6 );
2800 TDF_Label front_silk_mat = makeMaterial(
"silkscreen", front_silk_color, 0.0, 0.9 );
2801 TDF_Label back_silk_mat = makeMaterial(
"silkscreen", back_silk_color, 0.0, 0.9 );
2802 TDF_Label copper_mat = makeMaterial(
"copper", copper_color, 1.0, 0.4 );
2803 TDF_Label pad_mat = makeMaterial(
"pad", pad_color, 1.0, 0.4 );
2804 TDF_Label board_mat = makeMaterial(
"board", board_color, 0.0, 0.8 );
2806 pushToAssemblyMap(
m_board_copper, copper_mat,
"copper",
true,
true,
"Copper" );
2810 pushToAssembly(
m_board_front_silk, front_silk_mat,
"silkscreen",
true,
"Top Silkscreen" );
2811 pushToAssembly(
m_board_back_silk, back_silk_mat,
"silkscreen",
true,
"Bottom Silkscreen" );
2812 pushToAssembly(
m_board_front_mask, front_mask_mat,
"soldermask",
true,
"Top Soldermask" );
2813 pushToAssembly(
m_board_back_mask, back_mask_mat,
"soldermask",
true,
"Bottom Soldermask" );
2815 if( aPushBoardBody )
2818#if( defined OCC_VERSION_HEX ) && ( OCC_VERSION_HEX > 0x070101 )
2819 m_assy->UpdateAssemblies();
2828bool STEP_PCB_MODEL::WriteIGES(
const wxString& aFileName )
2832 m_reporter->
Report( wxString::Format(
_(
"No valid PCB assembly; cannot create output file '%s'." ),
2840 wxFileName fn( aFileName );
2841 IGESControl_Controller::Init();
2842 IGESCAFControl_Writer writer;
2843 writer.SetColorMode( Standard_True );
2844 writer.SetNameMode( Standard_True );
2845 IGESData_GlobalSection header = writer.Model()->GlobalSection();
2846 header.SetFileName(
new TCollection_HAsciiString( fn.GetFullName().ToAscii() ) );
2847 header.SetSendName(
new TCollection_HAsciiString(
"KiCad electronic assembly" ) );
2848 header.SetAuthorName(
new TCollection_HAsciiString( Interface_Static::CVal(
"write.iges.header.author" ) ) );
2849 header.SetCompanyName(
new TCollection_HAsciiString( Interface_Static::CVal(
"write.iges.header.company" ) ) );
2850 writer.Model()->SetGlobalSection( header );
2852 if( Standard_False == writer.Perform( m_doc, aFileName.c_str() ) )
2861 wxFileInputStream input( inputFile );
2862 wxFileOutputStream output( outputFile );
2866 m_reporter->Report( wxString::Format(
_(
"Cannot create input stream '%s'.\n" ), inputFile ) );
2870 if( !output.IsOk() )
2872 m_reporter->Report( wxString::Format(
_(
"Cannot create output stream '%s'.\n" ), outputFile ) );
2876 wxZlibOutputStream zlibStream( output, -1, wxZLIB_GZIP );
2878 if( !zlibStream.IsOk() )
2880 m_reporter->Report(
_(
"Impossible create compress stream" ) );
2884 input.Read( zlibStream );
2886 if( input.LastRead() == 0 || zlibStream.LastWrite() == 0 )
2888 m_reporter->Report(
_(
"Compress read or write error" ) );
2902 m_reporter->Report( wxString::Format(
_(
"No valid PCB assembly; cannot create output file '%s'." ),
2910 wxFileName fn( aFileName );
2912 STEPCAFControl_Writer writer;
2913 writer.SetColorMode( Standard_True );
2914 writer.SetNameMode( Standard_True );
2921 if( !Interface_Static::SetCVal(
"write.step.product.name", fn.GetName().ToAscii() ) )
2923 m_reporter->Report(
_(
"Failed to set STEP product name, but will attempt to continue." ),
2929 if( !Interface_Static::SetIVal(
"write.surfacecurve.mode", aOptimize ? 0 : 1 ) )
2931 m_reporter->Report(
_(
"Failed to set surface curve mode, but will attempt to continue." ),
2935 if( Standard_False == writer.Transfer( m_doc, STEPControl_AsIs ) )
2938 APIHeaderSection_MakeHeader hdr( writer.ChangeWriter().Model() );
2942 hdr.SetName(
new TCollection_HAsciiString( fn.GetFullName().ToAscii() ) );
2945 hdr.SetAuthorValue( 1,
new TCollection_HAsciiString(
"Pcbnew" ) );
2946 hdr.SetOrganizationValue( 1,
new TCollection_HAsciiString(
"Kicad" ) );
2947 hdr.SetOriginatingSystem(
new TCollection_HAsciiString(
"KiCad to STEP converter" ) );
2948 hdr.SetDescriptionValue( 1,
new TCollection_HAsciiString(
"KiCad electronic assembly" ) );
2950 bool success =
true;
2953 wxString currCWD = wxGetCwd();
2954 wxString workCWD = fn.GetPath();
2956 if( !workCWD.IsEmpty() )
2957 wxSetWorkingDirectory( workCWD );
2959 wxString tmpfname(
"$tempfile$.step" );
2961 if( Standard_False == writer.Write( tmpfname.c_str() ) )
2964 if( compress && success )
2966 wxString srcTmp( tmpfname );
2967 wxString dstTmp(
"$tempfile$.stpz" );
2970 wxRemoveFile( srcTmp );
2981 if( !wxRenameFile( tmpfname, fn.GetFullName(),
true ) )
2983 m_reporter->Report( wxString::Format(
_(
"Cannot rename temporary file '%s' to '%s'." ),
2991 wxSetWorkingDirectory( currCWD );
3001 m_reporter->Report( wxString::Format(
_(
"No valid PCB assembly; cannot create output file '%s'." ),
3010 Handle( XCAFDoc_ShapeTool ) s_assy = XCAFDoc_DocumentTool::ShapeTool( m_doc->Main() );
3015 wxFileName fn( aFileName );
3017 wxFFileOutputStream ffStream( fn.GetFullPath() );
3018 wxStdOutputStream stdStream( ffStream );
3020#if OCC_VERSION_HEX >= 0x070600
3021 BRepTools::Write( shape, stdStream,
false,
false, TopTools_FormatVersion_VERSION_1 );
3023 BRepTools::Write( shape, stdStream );
3032 wxFileName fn( aFileName );
3034 wxFFileOutputStream ffStream( fn.GetFullPath() );
3035 wxStdOutputStream file( ffStream );
3037 if( !ffStream.IsOk() )
3039 m_reporter->Report( wxString::Format(
"Could not open file '%s'", fn.GetFullPath() ),
3047 Handle( XCAFDoc_ShapeTool ) s_assy = XCAFDoc_DocumentTool::ShapeTool( m_doc->Main() );
3052 std::map<wxString, std::vector<int>> groups[4];
3053 std::map<wxString, double> groupAreas;
3054 TopExp_Explorer exp;
3057 for( exp.Init( shape, TopAbs_FACE ); exp.More(); exp.Next() )
3059 TopoDS_Shape subShape = exp.Current();
3062 BRepBndLib::Add( subShape, bbox );
3066 for(
const auto& pair : pairs )
3068 const auto& [point, padTestShape] = pair;
3070 if( bbox.IsOut( point ) )
3073 BRepAdaptor_Surface surface( TopoDS::Face( subShape ) );
3075 if( surface.GetType() != GeomAbs_Plane )
3078 BRepExtrema_DistShapeShape dist( padTestShape, subShape );
3081 if( !dist.IsDone() )
3084 if( dist.Value() < Precision::Approximation() )
3087 groups[2][padKey].push_back( faceIndex );
3089 GProp_GProps system;
3090 BRepGProp::SurfaceProperties( subShape, system );
3092 double surfaceArea = system.Mass() / 1e6;
3093 groupAreas[padKey] += surfaceArea;
3102 file <<
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>" << std::endl;
3103 file <<
"<XAO version=\"1.0\" author=\"KiCad\">" << std::endl;
3104 file <<
" <geometry name=\"" << fn.GetName() <<
"\">" << std::endl;
3105 file <<
" <shape format=\"BREP\"><![CDATA[";
3106#if OCC_VERSION_HEX < 0x070600
3107 BRepTools::Write( shape, file );
3109 BRepTools::Write( shape, file, Standard_True, Standard_True, TopTools_FormatVersion_VERSION_1 );
3111 file <<
"]]></shape>" << std::endl;
3112 file <<
" <topology>" << std::endl;
3114 TopTools_IndexedMapOfShape mainMap;
3115 TopExp::MapShapes( shape, mainMap );
3116 std::set<int> topo[4];
3118 static const TopAbs_ShapeEnum c_dimShapeTypes[] = { TopAbs_VERTEX, TopAbs_EDGE, TopAbs_FACE,
3121 static const std::string c_dimLabel[] = {
"vertex",
"edge",
"face",
"solid" };
3122 static const std::string c_dimLabels[] = {
"vertices",
"edges",
"faces",
"solids" };
3124 for(
int dim = 0; dim < 4; dim++ )
3126 for( exp.Init( shape, c_dimShapeTypes[dim] ); exp.More(); exp.Next() )
3128 TopoDS_Shape subShape = exp.Current();
3129 int idx = mainMap.FindIndex( subShape );
3131 if( idx && !topo[dim].count( idx ) )
3132 topo[dim].insert( idx );
3136 for(
int dim = 0; dim <= 3; dim++ )
3138 std::string labels = c_dimLabels[dim];
3139 std::string label = c_dimLabel[dim];
3141 file <<
" <" << labels <<
" count=\"" << topo[dim].size() <<
"\">" << std::endl;
3144 for(
auto p : topo[dim] )
3146 std::string
name(
"" );
3147 file <<
" <" << label <<
" index=\"" <<
index <<
"\" "
3148 <<
"name=\"" <<
name <<
"\" "
3149 <<
"reference=\"" << p <<
"\"/>" << std::endl;
3153 file <<
" </" << labels <<
">" << std::endl;
3156 file <<
" </topology>" << std::endl;
3157 file <<
" </geometry>" << std::endl;
3158 file <<
" <groups count=\""
3159 << groups[0].size() + groups[1].size() + groups[2].size() + groups[3].size() <<
"\">"
3162 int groupNumber = 1;
3167 for(
int dim = 0; dim <= 3; dim++ )
3169 std::string label = c_dimLabel[dim];
3171 for(
auto g : groups[dim] )
3174 wxString
name = g.first;
3178 std::ostringstream gs;
3179 gs <<
"G_" << dim <<
"D_" << g.first;
3182 file <<
" <group name=\"" <<
name <<
"\" dimension=\"" << label;
3188 file <<
"\" count=\"" << g.second.size() <<
"\">" << std::endl;
3190 for(
auto index : g.second )
3191 file <<
" <element index=\"" <<
index <<
"\"/>" << std::endl;
3193 file <<
" </group>" << std::endl;
3195 m_reporter->Report( wxString::Format(
"%d\t%s\t%g",
3207 file <<
" </groups>" << std::endl;
3208 file <<
" <fields count=\"0\"/>" << std::endl;
3209 file <<
"</XAO>" << std::endl;
3216 bool aSubstituteModels, wxString* aErrorMessage )
3218 std::string model_key = aFileNameUTF8 +
"_" + std::to_string( aScale.
x )
3219 +
"_" + std::to_string( aScale.
y ) +
"_" + std::to_string( aScale.
z );
3221 MODEL_MAP::const_iterator mm =
m_models.find( model_key );
3225 aLabel = mm->second;
3231 Handle( TDocStd_Document ) doc;
3232 m_app->NewDocument(
"MDTV-XCAF", doc );
3234 wxString fileName( wxString::FromUTF8( aFileNameUTF8.c_str() ) );
3237 wxFileName modelFile( fileName );
3238 std::string pname( modelFile.GetName().ToUTF8() );
3239 TCollection_ExtendedString partname( pname.c_str() );
3244 if( !
readIGES( doc, aFileNameUTF8.c_str() ) )
3246 m_reporter->Report( wxString::Format( wxT(
"readIGES() failed on filename '%s'." ),
3254 if( !
readSTEP( doc, aFileNameUTF8.c_str() ) )
3256 m_reporter->Report( wxString::Format( wxT(
"readSTEP() failed on filename '%s'." ),
3267 wxFFileInputStream ifile( fileName );
3268 wxFileName outFile( fileName );
3270 outFile.SetPath( wxStandardPaths::Get().GetTempDir() );
3271 outFile.SetExt( wxT(
"step" ) );
3272 wxFileOffset size = ifile.GetLength();
3274 if( size == wxInvalidOffset )
3276 m_reporter->Report( wxString::Format( wxT(
"getModelLabel() failed on filename '%s'." ),
3283 bool success =
false;
3284 wxFFileOutputStream ofile( outFile.GetFullPath() );
3289 char* buffer =
new char[size];
3291 ifile.Read( buffer, size );
3292 std::string expanded;
3296 expanded = gzip::decompress( buffer, size );
3301 m_reporter->Report( wxString::Format( wxT(
"failed to decompress '%s'." ),
3306 if( expanded.empty() )
3310 wxZipInputStream izipfile( ifile );
3311 std::unique_ptr<wxZipEntry> zip_file( izipfile.GetNextEntry() );
3313 if( zip_file && !zip_file->IsDir() && izipfile.CanRead() )
3315 izipfile.Read( ofile );
3321 ofile.Write( expanded.data(), expanded.size() );
3329 std::string altFileNameUTF8 =
TO_UTF8( outFile.GetFullPath() );
3349 if( aSubstituteModels )
3351 wxFileName wrlName( fileName );
3353 wxString basePath = wrlName.GetPath();
3354 wxString baseName = wrlName.GetName();
3362 alts.Add( wxT(
"stp" ) );
3363 alts.Add( wxT(
"step" ) );
3364 alts.Add( wxT(
"STP" ) );
3365 alts.Add( wxT(
"STEP" ) );
3366 alts.Add( wxT(
"Stp" ) );
3367 alts.Add( wxT(
"Step" ) );
3368 alts.Add( wxT(
"stpz" ) );
3369 alts.Add( wxT(
"stpZ" ) );
3370 alts.Add( wxT(
"STPZ" ) );
3371 alts.Add( wxT(
"step.gz" ) );
3372 alts.Add( wxT(
"stp.gz" ) );
3375 alts.Add( wxT(
"iges" ) );
3376 alts.Add( wxT(
"IGES" ) );
3377 alts.Add( wxT(
"igs" ) );
3378 alts.Add( wxT(
"IGS" ) );
3382 for(
const auto& alt : alts )
3384 wxFileName altFile( basePath, baseName + wxT(
"." ) + alt );
3386 if( altFile.IsOk() && altFile.FileExists() )
3388 std::string altFileNameUTF8 =
TO_UTF8( altFile.GetFullPath() );
3411 if(
readVRML( doc, aFileNameUTF8.c_str() ) )
3413 Handle( XCAFDoc_ShapeTool ) shapeTool = XCAFDoc_DocumentTool::ShapeTool( doc->Main() );
3419 m_reporter->Report( wxString::Format( wxT(
"readVRML() failed on filename '%s'." ), fileName ),
3427 aErrorMessage->Printf(
_(
"Cannot use VRML models when exporting to non-mesh formats." ) );
3437 m_reporter->Report( wxString::Format(
_(
"Cannot identify actual file type for '%s'." ),
3445 if( aLabel.IsNull() )
3447 m_reporter->Report( wxString::Format(
_(
"Could not transfer model data from file '%s'." ),
3455 TDataStd_Name::Set( aLabel, partname );
3465 TopLoc_Location& aLocation )
3479 lPos.SetTranslation( gp_Vec( aPosition.
x, -aPosition.
y, 0.0 ) );
3485 double boardThickness;
3488 double top = std::max( boardZPos, boardZPos + boardThickness );
3489 double bottom = std::min( boardZPos, boardZPos + boardThickness );
3494 double f_pos, f_thickness;
3498 bottom += f_thickness;
3505 lRot.SetRotation( gp_Ax1( gp_Pnt( 0.0, 0.0, 0.0 ), gp_Dir( 0.0, 0.0, 1.0 ) ), aRotation );
3506 lPos.Multiply( lRot );
3507 lRot.SetRotation( gp_Ax1( gp_Pnt( 0.0, 0.0, 0.0 ), gp_Dir( 1.0, 0.0, 0.0 ) ),
M_PI );
3508 lPos.Multiply( lRot );
3513 lRot.SetRotation( gp_Ax1( gp_Pnt( 0.0, 0.0, 0.0 ), gp_Dir( 0.0, 0.0, 1.0 ) ), aRotation );
3514 lPos.Multiply( lRot );
3518 lOff.SetTranslation( gp_Vec( offset.
x, offset.
y, offset.
z ) );
3519 lPos.Multiply( lOff );
3522 lOrient.SetRotation( gp_Ax1( gp_Pnt( 0.0, 0.0, 0.0 ), gp_Dir( 0.0, 0.0, 1.0 ) ), -aOrientation.
z );
3523 lPos.Multiply( lOrient );
3524 lOrient.SetRotation( gp_Ax1( gp_Pnt( 0.0, 0.0, 0.0 ), gp_Dir( 0.0, 1.0, 0.0 ) ), -aOrientation.
y );
3525 lPos.Multiply( lOrient );
3526 lOrient.SetRotation( gp_Ax1( gp_Pnt( 0.0, 0.0, 0.0 ), gp_Dir( 1.0, 0.0, 0.0 ) ), -aOrientation.
x );
3527 lPos.Multiply( lOrient );
3529 aLocation = TopLoc_Location( lPos );
3536 IGESControl_Controller::Init();
3537 IGESCAFControl_Reader reader;
3538 IFSelect_ReturnStatus stat = reader.ReadFile( fname );
3540 if( stat != IFSelect_RetDone )
3544 if( !Interface_Static::SetIVal(
"read.precision.mode", 1 ) )
3548 if( !Interface_Static::SetRVal(
"read.precision.val",
USER_PREC ) )
3552 reader.SetColorMode(
true );
3553 reader.SetNameMode(
false );
3554 reader.SetLayerMode(
false );
3556 if( !reader.Transfer( doc ) )
3558 if( doc->CanClose() == CDM_CCS_OK )
3565 if( reader.NbShapes() < 1 )
3567 if( doc->CanClose() == CDM_CCS_OK )
3579 STEPCAFControl_Reader reader;
3580 IFSelect_ReturnStatus stat = reader.ReadFile( fname );
3582 if( stat != IFSelect_RetDone )
3586 if( !Interface_Static::SetIVal(
"read.precision.mode", 1 ) )
3590 if( !Interface_Static::SetRVal(
"read.precision.val",
USER_PREC ) )
3594 reader.SetColorMode(
true );
3595 reader.SetNameMode(
true );
3596 reader.SetLayerMode(
false );
3598 if( !reader.Transfer( doc ) )
3600 if( doc->CanClose() == CDM_CCS_OK )
3607 if( reader.NbRootsForTransfer() < 1 )
3609 if( doc->CanClose() == CDM_CCS_OK )
3621#if OCC_VERSION_HEX >= 0x070700
3622 VrmlAPI_CafReader reader;
3623 RWMesh_CoordinateSystemConverter conv;
3624 conv.SetInputLengthUnit( 2.54 );
3625 reader.SetCoordinateSystemConverter( conv );
3626 reader.SetDocument( doc );
3628 if( !reader.Perform( TCollection_AsciiString( fname ), Message_ProgressRange() ) )
3643 Handle( XCAFDoc_ShapeTool ) s_assy = XCAFDoc_DocumentTool::ShapeTool( source->Main() );
3646 TDF_LabelSequence frshapes;
3647 s_assy->GetFreeShapes( frshapes );
3650 Handle( XCAFDoc_ShapeTool ) d_assy = XCAFDoc_DocumentTool::ShapeTool( dest->Main() );
3653 TDF_Label d_targetLabel = d_assy->NewShape();
3655 if( frshapes.Size() == 1 )
3657 TDocStd_XLinkTool link;
3658 link.Copy( d_targetLabel, frshapes.First() );
3663 for( TDF_Label& s_shapeLabel : frshapes )
3665 TDF_Label d_component = d_assy->NewShape();
3667 TDocStd_XLinkTool link;
3668 link.Copy( d_component, s_shapeLabel );
3670 d_assy->AddComponent( d_targetLabel, d_component, TopLoc_Location() );
3674 if( aScale.
x != 1.0 || aScale.
y != 1.0 || aScale.
z != 1.0 )
3677 return d_targetLabel;
3683 TDF_LabelSequence freeShapes;
3684 aShapeTool->GetFreeShapes( freeShapes );
3690 for( Standard_Integer i = 1; i <= freeShapes.Length(); ++i )
3692 TDF_Label label = freeShapes.Value( i );
3694 aShapeTool->GetShape( label, shape );
3699 const Standard_Real linearDeflection = 0.14;
3700 const Standard_Real angularDeflection =
DEG2RAD( 30.0 );
3701 BRepMesh_IncrementalMesh mesh( shape, linearDeflection, Standard_False, angularDeflection,
3723 wxFileName fn( aFileName );
3725 const char* tmpGltfname =
"$tempfile$.glb";
3726 RWGltf_CafWriter cafWriter( tmpGltfname,
true );
3728 cafWriter.SetTransformationFormat( RWGltf_WriterTrsfFormat_Compact );
3729 cafWriter.ChangeCoordinateSystemConverter().SetInputLengthUnit( 0.001 );
3730 cafWriter.ChangeCoordinateSystemConverter().SetInputCoordinateSystem(
3731 RWMesh_CoordinateSystem_Zup );
3732#if OCC_VERSION_HEX >= 0x070700
3733 cafWriter.SetParallel(
true );
3735 TColStd_IndexedDataMapOfStringString metadata;
3737 metadata.Add( TCollection_AsciiString(
"pcb_name" ),
3738 TCollection_ExtendedString( fn.GetName().wc_str() ) );
3739 metadata.Add( TCollection_AsciiString(
"source_pcb_file" ),
3740 TCollection_ExtendedString( fn.GetFullName().wc_str() ) );
3741 metadata.Add( TCollection_AsciiString(
"generator" ),
3742 TCollection_AsciiString( wxString::Format( wxS(
"KiCad %s" ),
GetSemanticVersion() ).ToAscii() ) );
3743 metadata.Add( TCollection_AsciiString(
"generated_at" ),
3746 bool success =
true;
3749 wxString currCWD = wxGetCwd();
3750 wxString workCWD = fn.GetPath();
3752 if( !workCWD.IsEmpty() )
3753 wxSetWorkingDirectory( workCWD );
3755 success = cafWriter.Perform( m_doc, metadata, Message_ProgressRange() );
3762 if( !wxRenameFile( tmpGltfname, fn.GetFullName(),
true ) )
3764 m_reporter->Report( wxString::Format(
_(
"Cannot rename temporary file '%s' to '%s'." ),
3772 wxSetWorkingDirectory( currCWD );
3780#if OCC_VERSION_HEX < 0x070700
3787 m_reporter->Report( wxString::Format(
_(
"No valid PCB assembly; cannot create output file '%s'." ),
3797 wxFileName fn( aFileName );
3799 const char* tmpFname =
"$tempfile$.ply";
3800 RWPly_CafWriter cafWriter( tmpFname );
3802 cafWriter.SetFaceId(
true );
3803 cafWriter.ChangeCoordinateSystemConverter().SetInputLengthUnit( 0.001 );
3804 cafWriter.ChangeCoordinateSystemConverter().SetInputCoordinateSystem( RWMesh_CoordinateSystem_Zup );
3806 TColStd_IndexedDataMapOfStringString metadata;
3808 metadata.Add( TCollection_AsciiString(
"pcb_name" ),
3809 TCollection_ExtendedString( fn.GetName().wc_str() ) );
3810 metadata.Add( TCollection_AsciiString(
"source_pcb_file" ),
3811 TCollection_ExtendedString( fn.GetFullName().wc_str() ) );
3812 metadata.Add( TCollection_AsciiString(
"generator" ),
3813 TCollection_AsciiString( wxString::Format( wxS(
"KiCad %s" ),
3815 metadata.Add( TCollection_AsciiString(
"generated_at" ),
3818 bool success =
true;
3821 wxString currCWD = wxGetCwd();
3822 wxString workCWD = fn.GetPath();
3824 if( !workCWD.IsEmpty() )
3825 wxSetWorkingDirectory( workCWD );
3827 success = cafWriter.Perform( m_doc, metadata, Message_ProgressRange() );
3834 if( !wxRenameFile( tmpFname, fn.GetFullName(),
true ) )
3836 m_reporter->Report( wxString::Format(
_(
"Cannot rename temporary file '%s' to '%s'." ),
3844 wxSetWorkingDirectory( currCWD );
3855 m_reporter->Report( wxString::Format(
_(
"No valid PCB assembly; cannot create output file '%s'." ),
3865 wxFileName fn( aFileName );
3867 const char* tmpFname =
"$tempfile$.stl";
3870 wxString currCWD = wxGetCwd();
3871 wxString workCWD = fn.GetPath();
3873 if( !workCWD.IsEmpty() )
3874 wxSetWorkingDirectory( workCWD );
3876 bool success = StlAPI_Writer().Write(
getOneShape( m_assy ), tmpFname );
3883 if( !wxRenameFile( tmpFname, fn.GetFullName(),
true ) )
3885 m_reporter->Report( wxString::Format(
_(
"Cannot rename temporary file '%s' to '%s'." ),
3893 wxSetWorkingDirectory( currCWD );
3905 wxString::Format(
_(
"No valid PCB assembly; cannot create output file '%s'.\n" ), aFileName ),
3914 wxFileName fn( aFileName );
3916 const char* tmpFname =
"$tempfile$.u3d";
3919 wxString currCWD = wxGetCwd();
3920 wxString workCWD = fn.GetPath();
3922 if( !workCWD.IsEmpty() )
3923 wxSetWorkingDirectory( workCWD );
3926 bool success = writer.
Perform( m_doc );
3932 if( !wxRenameFile( tmpFname, fn.GetFullName(),
true ) )
3934 m_reporter->Report( wxString::Format( wxT(
"Cannot rename temporary file '%s' to '%s'.\n" ), tmpFname,
3941 wxSetWorkingDirectory( currCWD );
3952 wxString::Format(
_(
"No valid PCB assembly; cannot create output file '%s'.\n" ), aFileName ),
3961 wxFileName fn( aFileName );
3963 wxFileName u3dTmpfn = wxFileName::CreateTempFileName(
"" );
3964 wxFileName pdfTmpfn = wxFileName::CreateTempFileName(
"" );
3966 U3D::WRITER writer( u3dTmpfn.GetFullPath().ToStdString() );
3967 bool success = writer.
Perform( m_doc );
3970 std::unique_ptr<PDF_PLOTTER> plotter = std::make_unique<PDF_PLOTTER>();
3972 plotter->SetColorMode(
true );
3973 plotter->Set3DExport(
true );
3974 plotter->SetCreator( wxT(
"Mark's awesome 3d exporter" ) );
3976 plotter->SetRenderSettings( &renderSettings );
3978 if( !plotter->OpenFile( pdfTmpfn.GetFullPath() ) )
3980 m_reporter->Report( wxString::Format( wxT(
"Cannot open temporary file '%s'.\n" ), pdfTmpfn.GetFullPath() ),
3986 plotter->StartPlot(
"1",
"3D Model" );
3987 double fov_degrees = 16.5f;
3992 std::vector<PDF_3D_VIEW> views;
3997 std::vector<float> c2wMatrix =
4001 .m_name =
"Default",
4002 .m_cameraMatrix = c2wMatrix,
4003 .m_cameraCenter = (float)
distance,
4004 .m_fov = (
float) fov_degrees,
4013 .m_cameraMatrix = c2wMatrix,
4014 .m_cameraCenter = (float)
distance,
4015 .m_fov = (
float) fov_degrees,
4024 .m_cameraMatrix = c2wMatrix,
4025 .m_cameraCenter = (float)
distance,
4026 .m_fov = (
float) fov_degrees,
4035 .m_cameraMatrix = c2wMatrix,
4036 .m_cameraCenter = (float)
distance,
4037 .m_fov = (
float) fov_degrees,
4040 plotter->Plot3DModel( u3dTmpfn.GetFullPath(), views );
4049 if( !wxRenameFile( pdfTmpfn.GetFullPath(), fn.GetFullPath(),
true ) )
4051 m_reporter->Report( wxString::Format( wxT(
"Cannot rename temporary file '%s' to '%s'.\n" ),
4052 pdfTmpfn.GetFullPath(), fn.GetFullPath() ),
4058 wxRemoveFile( u3dTmpfn.GetFullPath() );
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 ...
wxString GetNetname() const
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.
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.
PCB specific render settings.
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.
PAD_PROP GetProperty() const
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.
static std::vector< float > CreateC2WMatrixFromAngles(const VECTOR3D &aTargetPosition, float aCameraDistance, float aYawDegrees, float aPitchDegrees, float aRollDegrees)
Generates the camera to world matrix for use with a 3D View.
A pure virtual class used to derive REPORTER objects from.
virtual REPORTER & Report(const wxString &aText, SEVERITY aSeverity=RPT_SEVERITY_UNDEFINED)
Report a string with a given severity.
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 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.
const SHAPE_LINE_CHAIN Slice(int aStartIndex, int aEndIndex) const
Return a subset of this line chain containing the [start_index, end_index] range of points.
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.
void ClearArcs()
Removes all arc references from all the outlines and holes in the polyset.
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)
void Simplify()
Simplify the polyset (merges overlapping polys, eliminates degeneracy/self-intersections)
std::vector< SHAPE_LINE_CHAIN > POLYGON
represents a single polygon outline with holes.
void BooleanIntersection(const SHAPE_POLY_SET &b)
Perform boolean polyset intersection.
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
int GetWidth() const override
bool MakeShapeAsThickSegment(TopoDS_Shape &aShape, const VECTOR2D &aStartPoint, const VECTOR2D &aEndPoint, double aWidth, double aThickness, double aZposition, const VECTOR2D &aOrigin)
Make a segment shape based on start and end point.
OUTPUT_FORMAT m_outFmt
The current output format for created file.
void SetCopperColor(double r, double g, double b)
bool isBoardOutlineValid()
bool WritePLY(const wxString &aFileName)
bool AddCountersink(const VECTOR2I &aPosition, int aDiameter, int aDepth, int aAngle, bool aFrontSide, const VECTOR2D &aOrigin)
Add a countersink shape to remove board material from the top or bottom of a hole.
std::map< wxString, std::vector< TopoDS_Shape > > m_board_copper_vias
std::map< wxString, std::vector< TopoDS_Shape > > m_board_copper_pads
bool WriteSTEP(const wxString &aFileName, bool aOptimize, bool compress)
std::vector< TopoDS_Shape > m_board_back_mask
wxString m_pcbName
Name of the PCB, which will most likely be the file name of the path.
std::vector< TopoDS_Shape > m_boardCutouts
bool AddPolygonShapes(const SHAPE_POLY_SET *aPolyShapes, PCB_LAYER_ID aLayer, const VECTOR2D &aOrigin, const wxString &aNetname)
bool CreatePCB(SHAPE_POLY_SET &aOutline, const VECTOR2D &aOrigin, bool aPushBoardBody)
void getBoardBodyZPlacement(double &aZPos, double &aThickness)
TDF_Label transferModel(Handle(TDocStd_Document)&source, Handle(TDocStd_Document) &dest, const VECTOR3D &aScale)
bool getModelLocation(bool aBottom, const VECTOR2D &aPosition, double aRotation, const VECTOR3D &aOffset, const VECTOR3D &aOrientation, TopLoc_Location &aLocation)
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
STEP_PCB_MODEL(const wxString &aPcbName, REPORTER *aReporter)
bool AddBackdrill(const SHAPE_SEGMENT &aShape, PCB_LAYER_ID aLayerStart, PCB_LAYER_ID aLayerEnd, const VECTOR2D &aOrigin)
Add a backdrill hole shape to remove board material and copper plating.
void getLayerZPlacement(PCB_LAYER_ID aLayer, double &aZPos, double &aThickness)
std::vector< TopoDS_Shape > m_board_outlines
void SetSimplifyShapes(bool aValue)
bool WriteU3D(const wxString &aFileName)
bool readVRML(Handle(TDocStd_Document) &aDoc, const char *aFname)
void SetPadColor(double r, double g, double b)
bool AddCounterbore(const VECTOR2I &aPosition, int aDiameter, int aDepth, bool aFrontSide, const VECTOR2D &aOrigin)
Add a counterbore shape to remove board material from the top or bottom of a hole.
void SetEnabledLayers(const LSET &aLayers)
void SetExtraPadThickness(bool aValue)
bool performMeshing(Handle(XCAFDoc_ShapeTool) &aShapeTool)
bool MakePolygonAsWall(TopoDS_Shape &aShape, SHAPE_POLY_SET &aPolySet, double aHeight, double aZposition, const VECTOR2D &aOrigin)
Make a polygonal shape to create a vertical wall.
bool readSTEP(Handle(TDocStd_Document) &aDoc, const char *aFname)
std::vector< TopoDS_Shape > m_board_front_silk
Handle(XCAFApp_Application) m_app
bool WritePDF(const wxString &aFileName)
virtual ~STEP_PCB_MODEL()
std::vector< TopoDS_Shape > m_board_front_mask
bool WriteSTL(const wxString &aFileName)
void SetStackup(const BOARD_STACKUP &aStackup)
void SetNetFilter(const wxString &aFilter)
std::map< wxString, std::vector< TopoDS_Shape > > m_board_copper
std::map< PCB_LAYER_ID, int > GetCopperLayerKnockouts(int aDiameter, int aDepth, int aAngle, bool aFrontSide)
Get the knockout diameters for copper layers that a counterbore or countersink crosses.
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 AddBarrel(const SHAPE_SEGMENT &aShape, PCB_LAYER_ID aLayerTop, PCB_LAYER_ID aLayerBot, bool aVia, const VECTOR2D &aOrigin, const wxString &aNetname)
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 getModelLabel(const std::string &aFileNameUTF8, const VECTOR3D &aScale, TDF_Label &aLabel, bool aSubstituteModels, wxString *aErrorMessage=nullptr)
Load a 3D model data.
std::vector< TopoDS_Shape > m_copperCutouts
bool CompressSTEP(wxString &inputFile, wxString &outputFile)
std::vector< TopoDS_Shape > m_board_back_silk
void getCopperLayerZPlacement(PCB_LAYER_ID aLayer, double &aZPos, double &aThickness)
std::map< wxString, std::vector< std::pair< gp_Pnt, TopoDS_Shape > > > m_pad_points
bool AddPadShape(const PAD *aPad, const VECTOR2D &aOrigin, bool aVia, SHAPE_POLY_SET *aClipPolygon=nullptr)
bool AddComponent(const std::string &aFileName, const std::string &aRefDes, bool aBottom, const VECTOR2D &aPosition, double aRotation, const VECTOR3D &aOffset, const VECTOR3D &aOrientation, const VECTOR3D &aScale, bool aSubstituteModels=true)
std::map< wxString, std::vector< TopoDS_Shape > > m_board_copper_fused
void OCCSetMergeMaxDistance(double aDistance=OCC_MAX_DISTANCE_TO_MERGE_POINTS)
bool WriteBREP(const wxString &aFileName)
bool Perform(const Handle(TDocStd_Document) &aDocument)
const VECTOR3D & GetCenter() const
const Bnd_Box & GetMeshBoundingBox() const
wxString StringFromValue(double aValue, bool aAddUnitLabel=false, EDA_DATA_TYPE aType=EDA_DATA_TYPE::DISTANCE) const
Converts aValue in internal units into a united string.
T EuclideanNorm() const
Compute the Euclidean norm of the vector, which is defined as sqrt(x ** 2 + y ** 2).
void TransformCircleToPolygon(SHAPE_LINE_CHAIN &aBuffer, const VECTOR2I &aCenter, int aRadius, int aError, ERROR_LOC aErrorLoc, int aMinSegCount=0)
Convert a circle to a polygon, using multiple straight lines.
void TransformOvalToPolygon(SHAPE_POLY_SET &aBuffer, const VECTOR2I &aStart, const VECTOR2I &aEnd, int aWidth, int aError, ERROR_LOC aErrorLoc, int aMinSegCount=0)
Convert a oblong shape to a polygon, using multiple segments.
static constexpr EDA_ANGLE ANGLE_360
const wxChar *const traceKiCad2Step
Flag to enable KiCad2Step debug tracing.
Handle(KICAD3D_INFO) KICAD3D_INFO
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)
Test 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)
@ PTH
Plated through hole pad.
@ CASTELLATED
a pad with a castellated through hole
Plotting engines similar to ps (PostScript, Gerber, svg)
static float distance(const SFVEC2UI &a, const SFVEC2UI &b)
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 TopoDS_Shape fuseShapesOrCompound(const TopTools_ListOfShape &aInputShapes, REPORTER *aReporter)
static bool colorFromStackup(BOARD_STACKUP_ITEM_TYPE aType, const wxString &aColorStr, COLOR4D &aColorOut)
static bool makeWireFromChain(BRepLib_MakeWire &aMkWire, const SHAPE_LINE_CHAIN &aChain, double aMergeOCCMaxDist, double aZposition, const VECTOR2D &aOrigin, REPORTER *aReporter)
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, REPORTER *aReporter)
static TopoDS_Compound makeCompound(const auto &aInputShapes)
static std::vector< FAB_LAYER_COLOR > s_soldermaskColors
MODEL3D_FORMAT_TYPE fileType(const char *aFileName)
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 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
#define CLOSE_STREAM(var)
#define OPEN_ISTREAM(var, name)
wxString UnescapeString(const wxString &aSource)
wxString GetISO8601CurrentDateTime()
#define TO_UTF8(wxstring)
Convert a wxString to a UTF8 encoded C string for all wxWidgets build modes.
KIBIS top(path, &reporter)
SHAPE_CIRCLE circle(c.m_circle_center, c.m_circle_radius)
thread_pool & GetKiCadThreadPool()
Get a reference to the current thread pool.
BS::priority_thread_pool thread_pool
wxLogTrace helper definitions.
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
VECTOR2< double > VECTOR2D
VECTOR3< double > VECTOR3D