STEP: export arcs in board contours as curves, not polygons.
Fixes https://gitlab.com/kicad/code/kicad/-/issues/13825
This commit is contained in:
parent
c7abbccfac
commit
f842e6ac7a
|
@ -326,40 +326,36 @@ bool ConvertOutlineToPolygon( std::vector<PCB_SHAPE*>& aShapeList, SHAPE_POLY_SE
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SHAPE_T::ARC:
|
case SHAPE_T::ARC:
|
||||||
// We do not support arcs in polygons, so approximate an arc with a series of
|
|
||||||
// short lines and put those line segments into the !same! PATH.
|
|
||||||
{
|
{
|
||||||
VECTOR2I pstart = graphic->GetStart();
|
VECTOR2I pstart = graphic->GetStart();
|
||||||
VECTOR2I pend = graphic->GetEnd();
|
VECTOR2I pmid = graphic->GetArcMid();
|
||||||
|
VECTOR2I pend = graphic->GetEnd();
|
||||||
VECTOR2I pcenter = graphic->GetCenter();
|
VECTOR2I pcenter = graphic->GetCenter();
|
||||||
EDA_ANGLE angle = -graphic->GetArcAngle();
|
|
||||||
int radius = graphic->GetRadius();
|
|
||||||
int steps = GetArcToSegmentCount( radius, aErrorMax, angle );
|
|
||||||
|
|
||||||
if( !close_enough( prevPt, pstart, aChainingEpsilon ) )
|
if( !close_enough( prevPt, pstart, aChainingEpsilon ) )
|
||||||
{
|
{
|
||||||
wxASSERT( close_enough( prevPt, graphic->GetEnd(), aChainingEpsilon ) );
|
wxASSERT( close_enough( prevPt, graphic->GetEnd(), aChainingEpsilon ) );
|
||||||
|
|
||||||
angle = -angle;
|
|
||||||
std::swap( pstart, pend );
|
std::swap( pstart, pend );
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create intermediate points between start and end:
|
SHAPE_ARC sarc( pstart, pmid, pend, 0 );
|
||||||
for( int step = 1; step < steps; ++step )
|
|
||||||
|
SHAPE_LINE_CHAIN arcChain;
|
||||||
|
arcChain.Append( sarc, aErrorMax );
|
||||||
|
|
||||||
|
if( !aAllowUseArcsInPolygons )
|
||||||
|
arcChain.ClearArcs();
|
||||||
|
|
||||||
|
// set shapeOwners for arcChain points created by appending the sarc:
|
||||||
|
for( int ii = 1; ii < arcChain.PointCount(); ++ii )
|
||||||
{
|
{
|
||||||
EDA_ANGLE rotation = ( angle * step ) / steps;
|
shapeOwners[std::make_pair( arcChain.CPoint( ii - 1 ),
|
||||||
VECTOR2I pt = pstart;
|
arcChain.CPoint( ii ) )] = graphic;
|
||||||
|
|
||||||
RotatePoint( pt, pcenter, rotation );
|
|
||||||
|
|
||||||
currContour.Append( pt );
|
|
||||||
shapeOwners[ std::make_pair( prevPt, pt ) ] = graphic;
|
|
||||||
prevPt = pt;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Append the last arc end point
|
currContour.Append( arcChain );
|
||||||
currContour.Append( pend );
|
|
||||||
shapeOwners[ std::make_pair( prevPt, pend ) ] = graphic;
|
|
||||||
prevPt = pend;
|
prevPt = pend;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -620,67 +620,140 @@ bool STEP_PCB_MODEL::MakeShapes( std::vector<TopoDS_Shape>& aShapes, const SHAPE
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
// a SHAPE_LINE_CHAIN that is in fact a circle (one 360deg arc) is exported as cylinder
|
auto addSegment = [&]( const VECTOR2D& aPt0, const VECTOR2D& aPt1 ) -> bool
|
||||||
if( IsChainCircle( aChain ) )
|
|
||||||
{
|
{
|
||||||
const SHAPE_ARC& arc = aChain.Arc( 0 );
|
gp_Pnt start = toPoint( aPt0 );
|
||||||
|
gp_Pnt end = toPoint( aPt1 );
|
||||||
Handle( Geom_Curve ) curve;
|
|
||||||
|
|
||||||
if( arc.GetCentralAngle() == ANGLE_360 )
|
|
||||||
{
|
|
||||||
gp_Ax2 axis = gp::XOY();
|
|
||||||
axis.SetLocation( toPoint( arc.GetCenter() ) );
|
|
||||||
|
|
||||||
curve = GC_MakeCircle( axis, pcbIUScale.IUTomm( arc.GetRadius() ) ).Value();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
curve = GC_MakeArcOfCircle( toPoint( arc.GetP0() ),
|
|
||||||
toPoint( arc.GetArcMid() ),
|
|
||||||
toPoint( arc.GetP1() ) )
|
|
||||||
.Value();
|
|
||||||
}
|
|
||||||
|
|
||||||
if( !curve.IsNull() )
|
|
||||||
{
|
|
||||||
aMkWire.Add( BRepBuilderAPI_MakeEdge( curve ) );
|
|
||||||
|
|
||||||
if( aMkWire.Error() != BRepLib_WireDone )
|
|
||||||
{
|
|
||||||
ReportMessage( wxT( "failed to add curve\n" ) );
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
gp_Pnt start = toPoint( aChain.CPoint( 0 ) );
|
|
||||||
|
|
||||||
for( int j = 0; j < aChain.PointCount(); j++ )
|
|
||||||
{
|
|
||||||
int next = ( j + 1 ) % aChain.PointCount();
|
|
||||||
gp_Pnt end = toPoint( aChain.CPoint( next ) );
|
|
||||||
|
|
||||||
// Do not export too short segments: they create broken shape because OCC thinks
|
// Do not export too short segments: they create broken shape because OCC thinks
|
||||||
// start point and end point are at the same place
|
// start point and end point are at the same place
|
||||||
double seg_len = std::hypot( end.X() - start.X(), end.Y() - start.Y() );
|
double seg_len = std::hypot( end.X() - start.X(), end.Y() - start.Y() );
|
||||||
|
|
||||||
if( seg_len <= m_mergeOCCMaxDist )
|
if( seg_len <= m_mergeOCCMaxDist )
|
||||||
continue;
|
return false;
|
||||||
|
|
||||||
BRepBuilderAPI_MakeEdge mkEdge( start, end );
|
BRepBuilderAPI_MakeEdge mkEdge( start, end );
|
||||||
aMkWire.Add( mkEdge );
|
|
||||||
|
|
||||||
if( aMkWire.Error() != BRepLib_WireDone )
|
if( !mkEdge.IsDone() || mkEdge.Edge().IsNull() )
|
||||||
|
{
|
||||||
|
ReportMessage( wxT( "failed to make edge, skipping\n" ) );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
aMkWire.Add( mkEdge.Edge() );
|
||||||
|
|
||||||
|
if( aMkWire.Error() != BRepLib_WireDone )
|
||||||
|
{
|
||||||
|
ReportMessage( wxT( "failed to add edge to wire\n" ) );
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
auto addArc = [&]( const SHAPE_ARC& aArc ) -> bool
|
||||||
|
{
|
||||||
|
// Do not export too short segments: they create broken shape because OCC thinks
|
||||||
|
Handle( Geom_Curve ) curve;
|
||||||
|
|
||||||
|
if( aArc.GetCentralAngle() == ANGLE_360 )
|
||||||
|
{
|
||||||
|
gp_Ax2 axis = gp::XOY();
|
||||||
|
axis.SetLocation( toPoint( aArc.GetCenter() ) );
|
||||||
|
|
||||||
|
curve = GC_MakeCircle( axis, pcbIUScale.IUTomm( aArc.GetRadius() ) )
|
||||||
|
.Value();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
curve = GC_MakeArcOfCircle( toPoint( aArc.GetP0() ),
|
||||||
|
toPoint( aArc.GetArcMid() ),
|
||||||
|
toPoint( aArc.GetP1() ) )
|
||||||
|
.Value();
|
||||||
|
}
|
||||||
|
|
||||||
|
if( curve.IsNull() )
|
||||||
|
return false;
|
||||||
|
|
||||||
|
aMkWire.Add( BRepBuilderAPI_MakeEdge( curve ) );
|
||||||
|
|
||||||
|
if( !aMkWire.IsDone() )
|
||||||
{
|
{
|
||||||
ReportMessage( wxT( "failed to add curve\n" ) );
|
ReportMessage( wxT( "failed to add curve\n" ) );
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
start = end;
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
VECTOR2I firstPt;
|
||||||
|
VECTOR2I lastPt;
|
||||||
|
bool isFirstShape = true;
|
||||||
|
|
||||||
|
for( int i = 0; i <= aChain.PointCount() && i != -1; i = aChain.NextShape( i ) )
|
||||||
|
{
|
||||||
|
if( i == 0 )
|
||||||
|
{
|
||||||
|
if( aChain.IsArcSegment( 0 )
|
||||||
|
&& aChain.IsArcSegment( aChain.PointCount() - 1 )
|
||||||
|
&& aChain.ArcIndex( 0 ) == aChain.ArcIndex( aChain.PointCount() - 1 ) )
|
||||||
|
{
|
||||||
|
std::cout << "Skip looping arc" << std::endl;
|
||||||
|
|
||||||
|
// Skip first arc (we should encounter it later)
|
||||||
|
int nextShape = aChain.NextShape( i );
|
||||||
|
|
||||||
|
// If nextShape points to the end, then we have a circle.
|
||||||
|
if( nextShape != aChain.PointCount() - 1 )
|
||||||
|
i = nextShape;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if( isFirstShape )
|
||||||
|
lastPt = aChain.CPoint( i );
|
||||||
|
|
||||||
|
bool isArc = aChain.IsArcSegment( i );
|
||||||
|
int arcindex = isArc ? aChain.ArcIndex( i ) : -1;
|
||||||
|
|
||||||
|
if( aChain.IsArcStart( i ) )
|
||||||
|
{
|
||||||
|
const SHAPE_ARC& currentArc = aChain.Arc( aChain.ArcIndex( i ) );
|
||||||
|
int nextShape = aChain.NextShape( i );
|
||||||
|
bool isLastShape = nextShape < 0;
|
||||||
|
|
||||||
|
if( isFirstShape )
|
||||||
|
{
|
||||||
|
firstPt = currentArc.GetP0();
|
||||||
|
lastPt = firstPt;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( lastPt != currentArc.GetP0() )
|
||||||
|
addSegment( lastPt, currentArc.GetP0() );
|
||||||
|
|
||||||
|
addArc( currentArc );
|
||||||
|
|
||||||
|
lastPt = currentArc.GetP1();
|
||||||
|
}
|
||||||
|
else if( !isArc )
|
||||||
|
{
|
||||||
|
const SEG& seg = aChain.CSegment( i );
|
||||||
|
|
||||||
|
if( isFirstShape )
|
||||||
|
{
|
||||||
|
firstPt = seg.A;
|
||||||
|
lastPt = firstPt;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( addSegment( lastPt, seg.B ) )
|
||||||
|
lastPt = seg.B;
|
||||||
|
}
|
||||||
|
|
||||||
|
isFirstShape = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if( lastPt != firstPt )
|
||||||
|
addSegment( lastPt, firstPt );
|
||||||
}
|
}
|
||||||
catch( const Standard_Failure& e )
|
catch( const Standard_Failure& e )
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in New Issue