Large rework of BEZIER_POLY

Add direct handling of quadratic beziers to save compute time and number
of points.  Update cubic interpolation to reduce number of points
generated for a given smoothness

Cache data on open and used cached data to avoid multiple re-calcs

Remove minimum line length and number of segments and replace with
standard max error level.  Allows us to specify the tolerance of bezier
interpolation
This commit is contained in:
Seth Hillbrand
2024-06-18 17:55:41 -07:00
parent 0d2c4c91fd
commit bcf6b620a8
31 changed files with 698 additions and 221 deletions
+35 -34
View File
@@ -352,6 +352,7 @@ void EDA_SHAPE::scale( double aScale )
scalePt( m_end );
scalePt( m_bezierC1 );
scalePt( m_bezierC2 );
RebuildBezierToSegmentsPointsList( m_stroke.GetWidth() / 2 );
break;
default:
@@ -488,12 +489,7 @@ void EDA_SHAPE::flip( const VECTOR2I& aCentre, bool aFlipLeftRight )
m_bezierC2.y = aCentre.y - ( m_bezierC2.y - aCentre.y );
}
// Rebuild the poly points shape
{
std::vector<VECTOR2I> ctrlPoints = { m_start, m_bezierC1, m_bezierC2, m_end };
BEZIER_POLY converter( ctrlPoints );
converter.GetPoly( m_bezierPoints, m_stroke.GetWidth() );
}
RebuildBezierToSegmentsPointsList( m_stroke.GetWidth() / 2 );
break;
default:
@@ -503,7 +499,7 @@ void EDA_SHAPE::flip( const VECTOR2I& aCentre, bool aFlipLeftRight )
}
void EDA_SHAPE::RebuildBezierToSegmentsPointsList( int aMinSegLen )
void EDA_SHAPE::RebuildBezierToSegmentsPointsList( int aMaxError )
{
// Has meaning only for SHAPE_T::BEZIER
if( m_shape != SHAPE_T::BEZIER )
@@ -513,30 +509,18 @@ void EDA_SHAPE::RebuildBezierToSegmentsPointsList( int aMinSegLen )
}
// Rebuild the m_BezierPoints vertex list that approximate the Bezier curve
m_bezierPoints = buildBezierToSegmentsPointsList( aMinSegLen );
// Ensure last point respects aMinSegLen parameter
if( m_bezierPoints.size() > 2 )
{
int idx = m_bezierPoints.size() - 1;
if( VECTOR2I( m_bezierPoints[idx] - m_bezierPoints[idx] - 1 ).EuclideanNorm() < aMinSegLen )
{
m_bezierPoints[idx - 1] = m_bezierPoints[idx];
m_bezierPoints.pop_back();
}
}
m_bezierPoints = buildBezierToSegmentsPointsList( aMaxError );
}
const std::vector<VECTOR2I> EDA_SHAPE::buildBezierToSegmentsPointsList( int aMinSegLen ) const
const std::vector<VECTOR2I> EDA_SHAPE::buildBezierToSegmentsPointsList( int aMaxError ) const
{
std::vector<VECTOR2I> bezierPoints;
// Rebuild the m_BezierPoints vertex list that approximate the Bezier curve
std::vector<VECTOR2I> ctrlPoints = { m_start, m_bezierC1, m_bezierC2, m_end };
BEZIER_POLY converter( ctrlPoints );
converter.GetPoly( bezierPoints, aMinSegLen );
converter.GetPoly( bezierPoints, aMaxError );
return bezierPoints;
}
@@ -930,16 +914,25 @@ bool EDA_SHAPE::hitTest( const VECTOR2I& aPosition, int aAccuracy ) const
}
case SHAPE_T::BEZIER:
const_cast<EDA_SHAPE*>( this )->RebuildBezierToSegmentsPointsList( GetWidth() );
{
const std::vector<VECTOR2I>* pts = &m_bezierPoints;
std::vector<VECTOR2I> updatedBezierPoints;
for( unsigned int i= 1; i < m_bezierPoints.size(); i++)
if( m_bezierPoints.empty() )
{
if( TestSegmentHit( aPosition, m_bezierPoints[ i - 1], m_bezierPoints[i], maxdist ) )
BEZIER_POLY converter( m_start, m_bezierC1, m_bezierC2, m_end );
converter.GetPoly( updatedBezierPoints, aAccuracy / 2 );
pts = &updatedBezierPoints;
}
for( unsigned int i = 1; i < pts->size(); i++ )
{
if( TestSegmentHit( aPosition, ( *pts )[i - 1], ( *pts )[i], maxdist ) )
return true;
}
return false;
}
case SHAPE_T::SEGMENT:
return TestSegmentHit( aPosition, GetStart(), GetEnd(), maxdist );
@@ -1132,12 +1125,20 @@ bool EDA_SHAPE::hitTest( const BOX2I& aRect, bool aContained, int aAccuracy ) co
// Account for the width of the line
arect.Inflate( GetWidth() / 2 );
unsigned count = m_bezierPoints.size();
const std::vector<VECTOR2I>* pts = &m_bezierPoints;
std::vector<VECTOR2I> updatedBezierPoints;
for( unsigned ii = 1; ii < count; ii++ )
if( m_bezierPoints.empty() )
{
VECTOR2I vertex = m_bezierPoints[ii - 1];
VECTOR2I vertexNext = m_bezierPoints[ii];
BEZIER_POLY converter( m_start, m_bezierC1, m_bezierC2, m_end );
converter.GetPoly( updatedBezierPoints, aAccuracy / 2 );
pts = &updatedBezierPoints;
}
for( unsigned ii = 1; ii < pts->size(); ii++ )
{
VECTOR2I vertex = ( *pts )[ii - 1];
VECTOR2I vertexNext = ( *pts )[ii];
// Test if the point is within aRect
if( arect.Contains( vertex ) )
@@ -1278,7 +1279,7 @@ std::vector<SHAPE*> EDA_SHAPE::makeEffectiveShapes( bool aEdgeOnly, bool aLineCh
case SHAPE_T::BEZIER:
{
std::vector<VECTOR2I> bezierPoints = buildBezierToSegmentsPointsList( width );
std::vector<VECTOR2I> bezierPoints = buildBezierToSegmentsPointsList( width / 2 );
VECTOR2I start_pt = bezierPoints[0];
for( unsigned int jj = 1; jj < bezierPoints.size(); jj++ )
@@ -1381,7 +1382,7 @@ void EDA_SHAPE::beginEdit( const VECTOR2I& aPosition )
SetBezierC2( aPosition );
m_editState = 1;
RebuildBezierToSegmentsPointsList( GetWidth() );
RebuildBezierToSegmentsPointsList( GetWidth() / 2 );
break;
case SHAPE_T::POLY:
@@ -1463,7 +1464,7 @@ void EDA_SHAPE::calcEdit( const VECTOR2I& aPosition )
case 3: SetBezierC2( aPosition ); break;
}
RebuildBezierToSegmentsPointsList( GetWidth() );
RebuildBezierToSegmentsPointsList( GetWidth() / 2 );
}
break;
@@ -1792,7 +1793,7 @@ void EDA_SHAPE::TransformShapeToPolygon( SHAPE_POLY_SET& aBuffer, int aClearance
std::vector<VECTOR2I> ctrlPts = { GetStart(), GetBezierC1(), GetBezierC2(), GetEnd() };
BEZIER_POLY converter( ctrlPts );
std::vector<VECTOR2I> poly;
converter.GetPoly( poly, GetWidth() );
converter.GetPoly( poly, aError );
for( unsigned ii = 1; ii < poly.size(); ii++ )
TransformOvalToPolygon( aBuffer, poly[ii - 1], poly[ii], width, aError, aErrorLoc );