ShapeToPoly: Fix outline/arc intersection for large expansions
Fixes https://gitlab.com/kicad/code/kicad/-/issues/8820 Adds shortcut code path for 90deg corners Segments are now actually symetrical Refactored and commented the code
This commit is contained in:
parent
a16b85db67
commit
937e4b1d8c
|
@ -254,80 +254,93 @@ void CornerListToPolygon( SHAPE_POLY_SET& outline, std::vector<ROUNDED_CORNER>&
|
||||||
outline.Append( cur.m_position );
|
outline.Append( cur.m_position );
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
VECTOR2I position = cur.m_position;
|
VECTOR2I cornerPosition = cur.m_position;
|
||||||
int radius = cur.m_radius;
|
int endAngle, radius = cur.m_radius;
|
||||||
|
double tanAngle2;
|
||||||
|
|
||||||
|
if( incoming.x == 0 && outgoing.y == 0 || incoming.y == 0 && outgoing.x == 0 )
|
||||||
|
{
|
||||||
|
endAngle = 900;
|
||||||
|
tanAngle2 = 1.0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
double cosNum = (double) incoming.x * outgoing.x + (double) incoming.y * outgoing.y;
|
double cosNum = (double) incoming.x * outgoing.x + (double) incoming.y * outgoing.y;
|
||||||
double cosDen = (double) incoming.EuclideanNorm() * outgoing.EuclideanNorm();
|
double cosDen = (double) incoming.EuclideanNorm() * outgoing.EuclideanNorm();
|
||||||
double angle = acos( cosNum / cosDen );
|
double angle = acos( cosNum / cosDen );
|
||||||
double tanAngle2 = tan( ( M_PI - angle ) / 2 );
|
tanAngle2 = tan( ( M_PI - angle ) / 2 );
|
||||||
|
endAngle = RAD2DECIDEG( angle );
|
||||||
|
}
|
||||||
|
|
||||||
if( aInflate )
|
if( aInflate )
|
||||||
{
|
{
|
||||||
radius += aInflate;
|
radius += aInflate;
|
||||||
position += incoming.Resize( aInflate / tanAngle2 )
|
cornerPosition += incoming.Resize( aInflate / tanAngle2 )
|
||||||
+ incoming.Perpendicular().Resize( -aInflate );
|
+ incoming.Perpendicular().Resize( -aInflate );
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure 16+ segments per 360° and ensure first & last segment are the same size
|
// Ensure 16+ segments per 360deg and ensure first & last segment are the same size
|
||||||
int numSegs = std::max( 16, GetArcToSegmentCount( radius, aError, 360.0 ) );
|
int numSegs = std::max( 16, GetArcToSegmentCount( radius, aError, 360.0 ) );
|
||||||
int angDelta = 3600 / numSegs;
|
int angDelta = 3600 / numSegs;
|
||||||
int targetAngle = RAD2DECIDEG( angle );
|
int lastSegLen = endAngle % angDelta; // or 0 if last seg length is angDelta
|
||||||
int angPos = ( angDelta + ( targetAngle % angDelta ) ) / 2;
|
int angPos = lastSegLen ? ( angDelta + lastSegLen ) / 2 : angDelta;
|
||||||
|
|
||||||
double centerProjection = radius / tanAngle2;
|
double arcTransitionDistance = radius / tanAngle2;
|
||||||
VECTOR2I arcStart = position - incoming.Resize( centerProjection );
|
VECTOR2I arcStart = cornerPosition - incoming.Resize( arcTransitionDistance );
|
||||||
VECTOR2I arcEnd = position + outgoing.Resize( centerProjection );
|
|
||||||
VECTOR2I arcCenter = arcStart + incoming.Perpendicular().Resize( radius );
|
VECTOR2I arcCenter = arcStart + incoming.Perpendicular().Resize( radius );
|
||||||
|
VECTOR2I arcEnd, arcStartOrigin;
|
||||||
|
|
||||||
if( aErrorLoc == ERROR_INSIDE )
|
if( aErrorLoc == ERROR_INSIDE )
|
||||||
{
|
{
|
||||||
|
arcEnd = SEG( cornerPosition, arcCenter ).ReflectPoint( arcStart );
|
||||||
|
arcStartOrigin = arcStart - arcCenter;
|
||||||
outline.Append( arcStart );
|
outline.Append( arcStart );
|
||||||
VECTOR2I zeroRef = arcStart - arcCenter;
|
|
||||||
|
|
||||||
for( ; angPos < targetAngle; angPos += angDelta )
|
|
||||||
{
|
|
||||||
VECTOR2I pt = zeroRef;
|
|
||||||
RotatePoint( pt, -angPos );
|
|
||||||
outline.Append( pt + arcCenter );
|
|
||||||
}
|
|
||||||
|
|
||||||
outline.Append( arcEnd );
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// The outer radius should be radius+aError, recalculate because numSegs is clamped
|
// The outer radius should be radius+aError, recalculate because numSegs is clamped
|
||||||
int actualDeltaRadius = CircleToEndSegmentDeltaRadius( radius, numSegs );
|
int actualDeltaRadius = CircleToEndSegmentDeltaRadius( radius, numSegs );
|
||||||
int radiusExtend = GetCircleToPolyCorrection( actualDeltaRadius );
|
int radiusExtend = GetCircleToPolyCorrection( actualDeltaRadius );
|
||||||
VECTOR2I arcExStart = arcStart + incoming.Perpendicular().Resize( -radiusExtend );
|
arcStart += incoming.Perpendicular().Resize( -radiusExtend );
|
||||||
VECTOR2I arcExEnd = arcEnd + outgoing.Perpendicular().Resize( -radiusExtend );
|
arcStartOrigin = arcStart - arcCenter;
|
||||||
|
|
||||||
// A larger radius will create "ears", so we intersect the first and last segment
|
// To avoid "ears", we only add segments crossing/within the non-rounded outline
|
||||||
// of the rounded corner with the non-rounded outline
|
// Note: outlineIn is short and must be treated as defining an infinite line
|
||||||
SEG inSeg( position - incoming, position );
|
SEG outlineIn( cornerPosition - incoming, cornerPosition );
|
||||||
SEG outSeg( position, position + outgoing );
|
VECTOR2I prevPt = arcStart;
|
||||||
VECTOR2I zeroRef = arcExStart - arcCenter;
|
arcEnd = cornerPosition; // default if no points within the outline are found
|
||||||
VECTOR2I pt = zeroRef;
|
|
||||||
|
|
||||||
|
while( angPos < endAngle )
|
||||||
|
{
|
||||||
|
VECTOR2I pt = arcStartOrigin;
|
||||||
RotatePoint( pt, -angPos );
|
RotatePoint( pt, -angPos );
|
||||||
pt += arcCenter;
|
pt += arcCenter;
|
||||||
OPT<VECTOR2I> intersect = inSeg.Intersect( SEG( arcExStart, pt ) );
|
|
||||||
outline.Append( intersect.is_initialized() ? intersect.get() : arcStart );
|
|
||||||
outline.Append( pt );
|
|
||||||
angPos += angDelta;
|
angPos += angDelta;
|
||||||
|
|
||||||
for( ; angPos < targetAngle; angPos += angDelta )
|
if( outlineIn.Side( pt ) > 0 )
|
||||||
{
|
{
|
||||||
pt = zeroRef;
|
VECTOR2I intersect = outlineIn.IntersectLines( SEG( prevPt, pt ) ).get();
|
||||||
RotatePoint( pt, -angPos );
|
outline.Append( intersect );
|
||||||
pt += arcCenter;
|
|
||||||
outline.Append( pt );
|
outline.Append( pt );
|
||||||
|
arcEnd = SEG( cornerPosition, arcCenter ).ReflectPoint( intersect );
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
intersect = outSeg.Intersect( SEG( pt, arcExEnd ) );
|
endAngle -= angDelta; // if skipping first, also skip last
|
||||||
outline.Append( intersect.is_initialized() ? intersect.get() : arcEnd );
|
prevPt = pt;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for( ; angPos < endAngle; angPos += angDelta )
|
||||||
|
{
|
||||||
|
VECTOR2I pt = arcStartOrigin;
|
||||||
|
RotatePoint( pt, -angPos );
|
||||||
|
outline.Append( pt + arcCenter );
|
||||||
|
}
|
||||||
|
|
||||||
|
outline.Append( arcEnd );
|
||||||
|
}
|
||||||
|
|
||||||
incoming = outgoing;
|
incoming = outgoing;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue