Fix SHAPE_LINE_CHAIN::Slice(), splitting arcs when required

This commit is contained in:
Roberto Fernandez Bautista 2021-08-08 18:28:52 +01:00
parent fb3a6ef2db
commit 1edb96cc5b
2 changed files with 101 additions and 31 deletions

View File

@ -321,16 +321,17 @@ public:
}
/**
* Return the vertex index of the next shape in the chain, or -1 if \a aPoint is in the last
* shape.
* Return the vertex index of the next shape in the chain, or -1 if \a aPointIndex is the
* last shape.
*
* If \a aPoint is the start of a segment, this will be ( aPoint + 1 ). If \a aPoint is
* part of an arc, this will be the index of the start of the next shape after the arc,
* in other words, the last point of the arc.
* If \a aPointIndex is the start of a segment, this will be ( aPointIndex + 1 ). If
* \a aPointIndex is part of an arc, this will be the index of the start of the next shape after
* the arc, in other words, the last point of the arc.
*
* @param aPointIndex is a vertex in the chain.
* @param aForwards is true if the next shape is desired, false for previous shape.
* @return the vertex index of the start of the next shape after aPoint's shape.
* @return the vertex index of the start of the next shape after aPoint's shape or -1 if
* the end was reached.
*/
int NextShape( int aPointIndex, bool aForwards = true ) const;

View File

@ -228,7 +228,10 @@ void SHAPE_LINE_CHAIN::splitArc( ssize_t aPtIndex, bool aCoincident )
m_arcs.insert( m_arcs.begin() + currArcIdx + 1, newArc2 );
if( aCoincident )
{
m_shapes[aPtIndex].second = currArcIdx + 1;
aPtIndex++;
}
// Only change the arc indices for the second half of the point range
for( int i = aPtIndex; i < PointCount(); i++ )
@ -736,11 +739,13 @@ int SHAPE_LINE_CHAIN::NextShape( int aPointIndex, bool aForwards ) const
if( aPointIndex < 0 )
aPointIndex += PointCount();
int lastIndex = PointCount() - 1;
// First or last point?
if( ( aForwards && aPointIndex == PointCount() - 1 ) ||
if( ( aForwards && aPointIndex == lastIndex ) ||
( !aForwards && aPointIndex == 0 ) )
{
return -1;
return -1; // we don't want to wrap around
}
int delta = aForwards ? 1 : -1;
@ -760,12 +765,19 @@ int SHAPE_LINE_CHAIN::NextShape( int aPointIndex, bool aForwards ) const
if( arcIndex == SHAPE_IS_PT || !aForwards )
arcIndex = m_shapes[aPointIndex].first; // Not a shared point or we are going backwards
int numPoints = static_cast<int>( m_shapes.size() );
// Now skip the rest of the arc
while( aPointIndex < numPoints && aPointIndex >= 0 && m_shapes[aPointIndex].first == arcIndex )
while( aPointIndex < lastIndex && aPointIndex >= 0 && m_shapes[aPointIndex].first == arcIndex )
aPointIndex += delta;
if( aPointIndex == lastIndex )
{
if( !m_closed )
return -1;
else
return lastIndex; // Segment between last point and the start
}
bool indexStillOnArc = alg::pair_contains( m_shapes[aPointIndex], arcIndex );
// We want the last vertex of the arc if the initial point was the start of one
@ -843,39 +855,96 @@ const SHAPE_LINE_CHAIN SHAPE_LINE_CHAIN::Slice( int aStartIndex, int aEndIndex )
int numPoints = static_cast<int>( m_points.size() );
if( IsArcSegment( aStartIndex ) && !IsArcStart( aStartIndex ) )
{
// Cutting in middle of an arc, lets split it
ssize_t arcIndex = ArcIndex( aStartIndex );
const SHAPE_ARC& currentArc = Arc( arcIndex );
// Copy the points as arc points
for( size_t i = aStartIndex; arcIndex == ArcIndex( i ); i++ )
{
rv.m_points.push_back( m_points[i] );
rv.m_shapes.push_back( { rv.m_arcs.size(), SHAPE_IS_PT } );
rv.m_bbox.Merge( m_points[i] );
}
// Create a new arc from the existing one, with different start point.
SHAPE_ARC newArc;
VECTOR2I newArcStart = m_points[aStartIndex];
newArc.ConstructFromStartEndCenter( newArcStart, currentArc.GetP1(),
currentArc.GetCenter(),
currentArc.IsClockwise() );
rv.m_arcs.push_back( newArc );
aStartIndex += rv.PointCount();
}
for( int i = aStartIndex; i <= aEndIndex && i < numPoints; i++ )
{
if( m_shapes[i] != SHAPES_ARE_PT )
if( IsArcStart( i ) )
{
int arcIdx = ArcIndex( i );
bool wholeArc = true;
int arcStart = i;
size_t prevIdx = static_cast<size_t>( i ) - 1;
const SHAPE_ARC &currentArc = Arc( ArcIndex( i ) );
int nextShape = NextShape( i );
bool isLastShape = nextShape < 0;
if( i > 0 && IsArcSegment( prevIdx ) && ArcIndex( prevIdx ) != arcIdx )
wholeArc = false;
if( ( isLastShape && aEndIndex != ( numPoints - 1 ) )
|| ( nextShape > aEndIndex ) )
{
if( i == aEndIndex )
{
// Single point on an arc, just append the single point
rv.Append( m_points[i] );
return rv;
}
while( i < numPoints && ArcIndex( i ) == arcIdx )
i++;
// Cutting in middle of an arc, lets split it
ssize_t arcIndex = ArcIndex( i );
const SHAPE_ARC& currentArc = Arc( arcIndex );
// Copy the points as arc points
for( ; i <= aEndIndex; i++ )
{
rv.m_points.push_back( m_points[i] );
rv.m_shapes.push_back( { rv.m_arcs.size(), SHAPE_IS_PT } );
rv.m_bbox.Merge( m_points[i] );
}
// Create a new arc from the existing one, with different end point.
SHAPE_ARC newArc;
VECTOR2I newArcEnd = m_points[aEndIndex];
newArc.ConstructFromStartEndCenter( currentArc.GetP0(), newArcEnd,
currentArc.GetCenter(),
currentArc.IsClockwise() );
rv.m_arcs.push_back( newArc );
return rv;
}
else
{
// append the whole arc
rv.Append( currentArc );
}
if( isLastShape )
return rv;
i = nextShape;
i--;
if( i > aEndIndex )
wholeArc = false;
if( wholeArc )
{
rv.Append( m_arcs[arcIdx] );
}
else
{
//@todo need to split up the arc
rv.Append( m_points[arcStart] );
i = arcStart;
}
}
else
{
wxASSERT_MSG( !IsArcSegment( i ), "Still on an arc segment, we missed something..." );
rv.Append( m_points[i] );
}
}