SHAPE_LINE_CHAIN splitArc on Insert, Remove and Replace
This ensures that the arc shapes remain correct after removing a point belonging to an arc or inserting a point in the middle of an arc. Simplify implementation of Replace( ..., aP ). Now a Remove operation followed by an Insert operation. Improve QA test for SHAPE_LINE_CHAIN Append, Insert and Replace Implement SHAPE_LINE_CHAIN::splitArc to break up an arc into two Implement SHAPE_ARC::ConstructFromStartEndCenter and add qa test
This commit is contained in:
parent
2f069c0b19
commit
a9a8aa8243
|
@ -95,6 +95,19 @@ public:
|
|||
SHAPE_ARC& ConstructFromStartEndAngle( const VECTOR2I& aStart, const VECTOR2I& aEnd,
|
||||
double aAngle, double aWidth = 0 );
|
||||
|
||||
/**
|
||||
* Constructs this arc from the given start, end and center.
|
||||
* @param aStart is the arc starting point
|
||||
* @param aEnd is the arc endpoint
|
||||
* @param aCenter is the arc center
|
||||
* @param aClockwise determines which of the two solutions to construct
|
||||
* @param aWidth is the arc line thickness
|
||||
* @return *this
|
||||
*/
|
||||
SHAPE_ARC& ConstructFromStartEndCenter( const VECTOR2I& aStart, const VECTOR2I& aEnd,
|
||||
const VECTOR2I& aCenter, bool aClockwise = false,
|
||||
double aWidth = 0 );
|
||||
|
||||
const VECTOR2I& GetP0() const { return m_start; }
|
||||
const VECTOR2I& GetP1() const { return m_end; }
|
||||
const VECTOR2I& GetArcMid() const { return m_mid; }
|
||||
|
@ -107,6 +120,8 @@ public:
|
|||
bool Collide( const VECTOR2I& aP, int aClearance = 0, int* aActual = nullptr,
|
||||
VECTOR2I* aLocation = nullptr ) const override;
|
||||
|
||||
bool IsClockwise() const;
|
||||
|
||||
void SetWidth( int aWidth )
|
||||
{
|
||||
m_width = aWidth;
|
||||
|
|
|
@ -790,6 +790,23 @@ protected:
|
|||
*/
|
||||
void convertArc( ssize_t aArcIndex );
|
||||
|
||||
/**
|
||||
* Splits an arc into two arcs at aPtIndex. Parameter \p aCoincident controls whether the two
|
||||
* arcs are to be coincident at aPtIndex or whether a short straight segment should be created
|
||||
* instead
|
||||
*
|
||||
* @param aPtIndex index of the point in the chain in which to split the arc
|
||||
* @param aCoincident If true, the end point of the first arc will be coincident with the start
|
||||
point of the second arc at aPtIndex.
|
||||
If false, the end point of the first arc will be at aPtIndex-1 and the
|
||||
start point of the second arc will be at aPtIndex, resulting in a short
|
||||
straight line segment between aPtIndex-1 and aPtIndex.
|
||||
*/
|
||||
void splitArc( ssize_t aPtIndex, bool aCoincident = false );
|
||||
|
||||
|
||||
void ammendArc( size_t aArcIndex, VECTOR2I aNewStart, VECTOR2I aNewEnd );
|
||||
|
||||
/**
|
||||
* Create a new Clipper path from the SHAPE_LINE_CHAIN in a given orientation
|
||||
*/
|
||||
|
|
|
@ -196,6 +196,34 @@ SHAPE_ARC& SHAPE_ARC::ConstructFromStartEndAngle( const VECTOR2I& aStart, const
|
|||
}
|
||||
|
||||
|
||||
SHAPE_ARC& SHAPE_ARC::ConstructFromStartEndCenter( const VECTOR2I& aStart, const VECTOR2I& aEnd,
|
||||
const VECTOR2I& aCenter, bool aClockwise,
|
||||
double aWidth )
|
||||
{
|
||||
VECTOR2I startLine = aStart - aCenter;
|
||||
VECTOR2I endLine = aEnd - aCenter;
|
||||
|
||||
double startangle = NormalizeAnglePos(RAD2DECIDEG( startLine.Angle() ));
|
||||
double endangle = NormalizeAnglePos(RAD2DECIDEG( endLine.Angle() ));
|
||||
double angle = endangle - startangle;
|
||||
|
||||
if( aClockwise )
|
||||
angle = NormalizeAngleNeg( angle );
|
||||
else
|
||||
angle = NormalizeAnglePos( angle );
|
||||
|
||||
m_start = aStart;
|
||||
m_end = aEnd;
|
||||
m_mid = aStart;
|
||||
|
||||
RotatePoint( m_mid, aCenter, -angle / 2.0 );
|
||||
|
||||
update_bbox();
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
bool SHAPE_ARC::Collide( const SEG& aSeg, int aClearance, int* aActual, VECTOR2I* aLocation ) const
|
||||
{
|
||||
if( aSeg.A == aSeg.B )
|
||||
|
@ -321,6 +349,12 @@ const BOX2I SHAPE_ARC::BBox( int aClearance ) const
|
|||
}
|
||||
|
||||
|
||||
bool SHAPE_ARC::IsClockwise() const
|
||||
{
|
||||
return GetCentralAngle() < 0;
|
||||
}
|
||||
|
||||
|
||||
bool SHAPE_ARC::Collide( const VECTOR2I& aP, int aClearance, int* aActual,
|
||||
VECTOR2I* aLocation ) const
|
||||
{
|
||||
|
|
|
@ -115,6 +115,97 @@ void SHAPE_LINE_CHAIN::convertArc( ssize_t aArcIndex )
|
|||
}
|
||||
|
||||
|
||||
void SHAPE_LINE_CHAIN::ammendArc( size_t aArcIndex, VECTOR2I aNewStart, VECTOR2I aNewEnd )
|
||||
{
|
||||
wxCHECK_MSG( aArcIndex < m_arcs.size(), /* void */,
|
||||
"Invalid arc index requested." );
|
||||
|
||||
SHAPE_ARC& theArc = m_arcs[aArcIndex];
|
||||
|
||||
// Try to preseve the centre of the original arc
|
||||
SHAPE_ARC newArc;
|
||||
newArc.ConstructFromStartEndCenter( aNewStart, aNewEnd, theArc.GetCenter(),
|
||||
theArc.IsClockwise() );
|
||||
|
||||
m_arcs[aArcIndex] = newArc;
|
||||
}
|
||||
|
||||
|
||||
void SHAPE_LINE_CHAIN::splitArc( ssize_t aPtIndex, bool aCoincident )
|
||||
{
|
||||
if( aPtIndex < 0 )
|
||||
aPtIndex += m_shapes.size();
|
||||
|
||||
if( !IsSharedPt( aPtIndex ) && IsArcStart( aPtIndex ) )
|
||||
return; // Nothing to do
|
||||
|
||||
wxCHECK_MSG( aPtIndex < static_cast<ssize_t>( m_shapes.size() ), /* void */,
|
||||
"Invalid point index requested." );
|
||||
|
||||
if( IsSharedPt( aPtIndex ) || IsArcEnd( aPtIndex ) )
|
||||
{
|
||||
if( aCoincident || aPtIndex == 0 )
|
||||
return; // nothing to do
|
||||
|
||||
ssize_t firstArcIndex = m_shapes[aPtIndex].first;
|
||||
|
||||
const VECTOR2I& newStart = m_arcs[firstArcIndex].GetP0(); //don't ammend the start
|
||||
const VECTOR2I& newEnd = m_points[aPtIndex - 1];
|
||||
ammendArc( firstArcIndex, newStart, newEnd );
|
||||
|
||||
if( IsSharedPt( aPtIndex ) )
|
||||
{
|
||||
m_shapes[aPtIndex].first = m_shapes[aPtIndex].second;
|
||||
m_shapes[aPtIndex].second = SHAPE_IS_PT;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_shapes[aPtIndex] = SHAPES_ARE_PT;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
ssize_t currArcIdx = ArcIndex( aPtIndex );
|
||||
SHAPE_ARC& currentArc = m_arcs[currArcIdx];
|
||||
|
||||
SHAPE_ARC newArc1;
|
||||
SHAPE_ARC newArc2;
|
||||
|
||||
VECTOR2I arc1End = ( aCoincident ) ? m_points[aPtIndex] : m_points[aPtIndex - 1];
|
||||
VECTOR2I arc2Start = m_points[aPtIndex];
|
||||
|
||||
newArc1.ConstructFromStartEndCenter( currentArc.GetP0(), arc1End, currentArc.GetCenter(),
|
||||
currentArc.IsClockwise() );
|
||||
|
||||
newArc2.ConstructFromStartEndCenter( arc2Start, currentArc.GetP1(), currentArc.GetCenter(),
|
||||
currentArc.IsClockwise() );
|
||||
|
||||
if( !aCoincident && ArcIndex( aPtIndex - 1 ) != currArcIdx )
|
||||
{
|
||||
//Ignore newArc1 as it has zero points
|
||||
m_arcs[currArcIdx] = newArc2;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_arcs[currArcIdx] = newArc1;
|
||||
m_arcs.insert( m_arcs.begin() + currArcIdx + 1, newArc2 );
|
||||
|
||||
if( aCoincident )
|
||||
m_shapes[aPtIndex].second = currArcIdx + 1;
|
||||
|
||||
// Only change the arc indices for the second half of the point range
|
||||
for( int i = aPtIndex; i < PointCount(); i++ )
|
||||
{
|
||||
alg::run_on_pair( m_shapes[i], [&]( ssize_t& aIndex ) {
|
||||
if( aIndex != SHAPE_IS_PT )
|
||||
aIndex++;
|
||||
} );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool SHAPE_LINE_CHAIN_BASE::Collide( const VECTOR2I& aP, int aClearance, int* aActual,
|
||||
VECTOR2I* aLocation ) const
|
||||
{
|
||||
|
@ -433,36 +524,8 @@ void SHAPE_LINE_CHAIN::Mirror( const SEG& axis )
|
|||
|
||||
void SHAPE_LINE_CHAIN::Replace( int aStartIndex, int aEndIndex, const VECTOR2I& aP )
|
||||
{
|
||||
if( aEndIndex < 0 )
|
||||
aEndIndex += PointCount();
|
||||
|
||||
if( aStartIndex < 0 )
|
||||
aStartIndex += PointCount();
|
||||
|
||||
aEndIndex = std::min( aEndIndex, PointCount() - 1 );
|
||||
|
||||
// N.B. This works because convertArc changes m_shapes on the first run
|
||||
for( int ind = aStartIndex; ind <= aEndIndex; ind++ )
|
||||
{
|
||||
if( m_shapes[ind] != SHAPES_ARE_PT )
|
||||
alg::run_on_pair( m_shapes[ind], [&]( size_t aIndex )
|
||||
{
|
||||
convertArc( aIndex );
|
||||
} );
|
||||
}
|
||||
|
||||
if( aStartIndex == aEndIndex )
|
||||
{
|
||||
m_points[aStartIndex] = aP;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_points.erase( m_points.begin() + aStartIndex + 1, m_points.begin() + aEndIndex + 1 );
|
||||
m_points[aStartIndex] = aP;
|
||||
|
||||
m_shapes.erase( m_shapes.begin() + aStartIndex + 1, m_shapes.begin() + aEndIndex + 1 );
|
||||
}
|
||||
|
||||
Remove( aStartIndex, aEndIndex );
|
||||
Insert( aStartIndex, aP );
|
||||
assert( m_shapes.size() == m_points.size() );
|
||||
}
|
||||
|
||||
|
@ -481,33 +544,22 @@ void SHAPE_LINE_CHAIN::Replace( int aStartIndex, int aEndIndex, const SHAPE_LINE
|
|||
|
||||
SHAPE_LINE_CHAIN newLine = aLine;
|
||||
|
||||
// It's possible that the start or end lands on the end of an arc. If so, we'd better have a
|
||||
// replacement line that matches up to the same coordinates, as we can't break the arc(s).
|
||||
|
||||
// @todo fixme: need to handle the case when there are two arcs associated with one point
|
||||
ssize_t startShape = m_shapes[aStartIndex].first;
|
||||
ssize_t endShape = m_shapes[aEndIndex].first;
|
||||
|
||||
if( startShape >= 0 )
|
||||
// Remove coincident points in the new line
|
||||
if( newLine.m_points.front() == m_points[aStartIndex] )
|
||||
{
|
||||
wxASSERT( !newLine.PointCount() ||
|
||||
( newLine.m_points.front() == m_points[aStartIndex] &&
|
||||
aStartIndex < m_points.size() - 1 ) );
|
||||
aStartIndex++;
|
||||
newLine.Remove( 0 );
|
||||
}
|
||||
|
||||
if( endShape >= 0 )
|
||||
if( newLine.m_points.back() == m_points[aEndIndex] && aEndIndex > 0 )
|
||||
{
|
||||
wxASSERT( !newLine.PointCount() ||
|
||||
( newLine.m_points.back() == m_points[aEndIndex] && aEndIndex > 0 ) );
|
||||
aEndIndex--;
|
||||
newLine.Remove( -1 );
|
||||
}
|
||||
|
||||
Remove( aStartIndex, aEndIndex );
|
||||
|
||||
if( !aLine.PointCount() )
|
||||
if( !newLine.PointCount() )
|
||||
return;
|
||||
|
||||
// The total new arcs index is added to the new arc indices
|
||||
|
@ -516,14 +568,12 @@ void SHAPE_LINE_CHAIN::Replace( int aStartIndex, int aEndIndex, const SHAPE_LINE
|
|||
|
||||
for( std::pair<ssize_t, ssize_t>& shape_pair : new_shapes )
|
||||
{
|
||||
if( shape_pair != SHAPES_ARE_PT )
|
||||
{
|
||||
if( shape_pair.first != SHAPE_IS_PT )
|
||||
shape_pair.first += prev_arc_count;
|
||||
|
||||
if( shape_pair.second != SHAPE_IS_PT )
|
||||
shape_pair.second += prev_arc_count;
|
||||
}
|
||||
alg::run_on_pair( shape_pair,
|
||||
[&]( ssize_t& aShape )
|
||||
{
|
||||
if( aShape != SHAPE_IS_PT )
|
||||
aShape += prev_arc_count;
|
||||
} );
|
||||
}
|
||||
|
||||
m_shapes.insert( m_shapes.begin() + aStartIndex, new_shapes.begin(), new_shapes.end() );
|
||||
|
@ -549,6 +599,16 @@ void SHAPE_LINE_CHAIN::Remove( int aStartIndex, int aEndIndex )
|
|||
return;
|
||||
|
||||
aEndIndex = std::min( aEndIndex, PointCount() - 1 );
|
||||
|
||||
// Split arcs at start index and end just after the end index
|
||||
if( aStartIndex < m_points.size() && m_shapes[aStartIndex] != SHAPES_ARE_PT )
|
||||
splitArc( aStartIndex );
|
||||
|
||||
size_t nextIndex = static_cast<size_t>( aEndIndex ) + 1;
|
||||
|
||||
if( nextIndex < m_points.size() && m_shapes[aEndIndex] != SHAPES_ARE_PT )
|
||||
splitArc( nextIndex );
|
||||
|
||||
std::set<size_t> extra_arcs;
|
||||
auto logArcIdxRemoval = [&]( ssize_t& aShapeIndex )
|
||||
{
|
||||
|
@ -975,8 +1035,10 @@ void SHAPE_LINE_CHAIN::Append( const SHAPE_ARC& aArc )
|
|||
|
||||
void SHAPE_LINE_CHAIN::Insert( size_t aVertex, const VECTOR2I& aP )
|
||||
{
|
||||
if( aVertex < m_points.size() && m_shapes[aVertex] != SHAPES_ARE_PT )
|
||||
convertArc( aVertex );
|
||||
wxCHECK( aVertex < m_points.size(), /* void */ );
|
||||
|
||||
if( aVertex > 0 && IsPtOnArc( aVertex ) )
|
||||
splitArc( aVertex );
|
||||
|
||||
//@todo need to check we aren't creating duplicate points
|
||||
m_points.insert( m_points.begin() + aVertex, aP );
|
||||
|
@ -988,8 +1050,10 @@ void SHAPE_LINE_CHAIN::Insert( size_t aVertex, const VECTOR2I& aP )
|
|||
|
||||
void SHAPE_LINE_CHAIN::Insert( size_t aVertex, const SHAPE_ARC& aArc )
|
||||
{
|
||||
if( aVertex < m_points.size() && m_shapes[aVertex] != SHAPES_ARE_PT )
|
||||
convertArc( aVertex );
|
||||
wxCHECK( aVertex < m_points.size(), /* void */ );
|
||||
|
||||
if( aVertex > 0 && IsPtOnArc( aVertex ) )
|
||||
splitArc( aVertex );
|
||||
|
||||
/// Step 1: Find the position for the new arc in the existing arc vector
|
||||
ssize_t arc_pos = m_arcs.size();
|
||||
|
|
|
@ -479,6 +479,64 @@ BOOST_AUTO_TEST_CASE( BasicTTRGeom )
|
|||
}
|
||||
|
||||
|
||||
/**
|
||||
* Info to set up an arc start, end and center
|
||||
*/
|
||||
struct ARC_START_END_CENTER
|
||||
{
|
||||
VECTOR2I m_start;
|
||||
VECTOR2I m_end;
|
||||
VECTOR2I m_center;
|
||||
};
|
||||
|
||||
|
||||
struct ARC_SEC_CASE
|
||||
{
|
||||
/// The text context name
|
||||
std::string m_ctx_name;
|
||||
|
||||
/// Geom of the arc
|
||||
ARC_START_END_CENTER m_geom;
|
||||
|
||||
/// clockwise or anti-clockwise?
|
||||
bool m_clockwise;
|
||||
|
||||
/// Expected mid-point of the arc
|
||||
VECTOR2I m_expected_mid;
|
||||
};
|
||||
|
||||
|
||||
|
||||
static const std::vector<ARC_SEC_CASE> arc_sec_cases = {
|
||||
{ "180 deg, clockwise", { { 100, 0 }, { 0, 0 }, { 50, 0 } }, true, { 50, -50 } },
|
||||
{ "180 deg, anticlockwise", { { 100, 0 }, { 0, 0 }, { 50, 0 } }, false, { 50, 50 } },
|
||||
{ "180 deg flipped, clockwise", { { 0, 0 }, { 100, 0 }, { 50, 0 } }, true, { 50, 50 } },
|
||||
{ "180 deg flipped, anticlockwise", { { 0, 0 }, { 100, 0 }, { 50, 0 } }, false, { 50, -50 } },
|
||||
{ "90 deg, clockwise", { { -100, 0 }, { 0, 100 }, { 0, 0 } }, true, { -71, 71 } },
|
||||
{ "90 deg, anticlockwise", { { -100, 0 }, { 0, 100 }, { 0, 0 } }, false, { 71, -71 } },
|
||||
};
|
||||
|
||||
|
||||
BOOST_AUTO_TEST_CASE( BasicSECGeom )
|
||||
{
|
||||
for( const auto& c : arc_sec_cases )
|
||||
{
|
||||
BOOST_TEST_CONTEXT( c.m_ctx_name )
|
||||
{
|
||||
VECTOR2I start = c.m_geom.m_start;
|
||||
VECTOR2I end = c.m_geom.m_end;
|
||||
VECTOR2I center = c.m_geom.m_center;
|
||||
bool cw = c.m_clockwise;
|
||||
|
||||
SHAPE_ARC this_arc;
|
||||
this_arc.ConstructFromStartEndCenter( start, end, center, cw );
|
||||
|
||||
BOOST_CHECK_EQUAL( this_arc.GetArcMid(), c.m_expected_mid );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
struct ARC_PT_COLLIDE_CASE
|
||||
{
|
||||
std::string m_ctx_name;
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/*
|
||||
* This program source code file is part of KiCad, a free EDA CAD application.
|
||||
*
|
||||
* Copyright (C) 2019 KiCad Developers, see CHANGELOG.TXT for contributors.
|
||||
* Copyright (C) 2019-2021 KiCad Developers, see CHANGELOG.TXT for contributors.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
|
@ -43,23 +43,61 @@ BOOST_AUTO_TEST_CASE( ArcToPolyline )
|
|||
VECTOR2I( 1500, 0 ),
|
||||
} );
|
||||
|
||||
SHAPE_LINE_CHAIN arc_insert1( SHAPE_ARC( VECTOR2I( 0, -100 ), VECTOR2I( 0, -200 ), 1800 ) );
|
||||
SHAPE_LINE_CHAIN arc_insert1( SHAPE_ARC( VECTOR2I( 0, -100 ), VECTOR2I( 0, -200 ), 180.0 ) );
|
||||
|
||||
SHAPE_LINE_CHAIN arc_insert2( SHAPE_ARC( VECTOR2I( 0, 500 ), VECTOR2I( 0, 400 ), 1800 ) );
|
||||
SHAPE_LINE_CHAIN arc_insert2( SHAPE_ARC( VECTOR2I( 0, 500 ), VECTOR2I( 0, 400 ), 180.0 ) );
|
||||
|
||||
BOOST_CHECK_EQUAL( base_chain.CShapes().size(), base_chain.CPoints().size() );
|
||||
BOOST_CHECK_EQUAL( arc_insert1.CShapes().size(), arc_insert1.CPoints().size() );
|
||||
BOOST_CHECK_EQUAL( arc_insert2.CShapes().size(), arc_insert2.CPoints().size() );
|
||||
|
||||
base_chain.Insert( 0, SHAPE_ARC( VECTOR2I( 0, -100 ), VECTOR2I( 0, -200 ), 1800 ) );
|
||||
BOOST_CHECK( GEOM_TEST::IsOutlineValid( base_chain ) );
|
||||
BOOST_CHECK( GEOM_TEST::IsOutlineValid( arc_insert1 ) );
|
||||
BOOST_CHECK( GEOM_TEST::IsOutlineValid( arc_insert2 ) );
|
||||
|
||||
base_chain.Insert( 0, SHAPE_ARC( VECTOR2I( 0, -100 ), VECTOR2I( 0, -200 ), 1800 ) );
|
||||
BOOST_CHECK( GEOM_TEST::IsOutlineValid( base_chain ) );
|
||||
BOOST_CHECK_EQUAL( base_chain.CShapes().size(), base_chain.CPoints().size() );
|
||||
|
||||
base_chain.Replace( 0, 2, chain_insert );
|
||||
BOOST_CHECK( GEOM_TEST::IsOutlineValid( base_chain ) );
|
||||
BOOST_CHECK_EQUAL( base_chain.CShapes().size(), base_chain.CPoints().size() );
|
||||
}
|
||||
|
||||
|
||||
// Similar test to above but with larger coordinates, so we have more than one point per arc
|
||||
BOOST_AUTO_TEST_CASE( ArcToPolylineLargeCoords )
|
||||
{
|
||||
SHAPE_LINE_CHAIN base_chain( { VECTOR2I( 0, 0 ), VECTOR2I( 0, 100000 ), VECTOR2I( 100000, 0 ) } );
|
||||
|
||||
SHAPE_LINE_CHAIN chain_insert( {
|
||||
VECTOR2I( 0, 1500000 ),
|
||||
VECTOR2I( 1500000, 1500000 ),
|
||||
VECTOR2I( 1500000, 0 ),
|
||||
} );
|
||||
|
||||
base_chain.Append( SHAPE_ARC( VECTOR2I( 200000, 0 ), VECTOR2I( 300000, 100000 ), 180.0 ) );
|
||||
|
||||
BOOST_CHECK( GEOM_TEST::IsOutlineValid( base_chain ) );
|
||||
BOOST_CHECK_EQUAL( base_chain.PointCount(), 11 );
|
||||
|
||||
base_chain.Insert( 9, VECTOR2I( 250000, 0 ) );
|
||||
BOOST_CHECK( GEOM_TEST::IsOutlineValid( base_chain ) );
|
||||
BOOST_CHECK_EQUAL( base_chain.PointCount(), 12 );
|
||||
BOOST_CHECK_EQUAL( base_chain.ArcCount(), 2 ); // Should have two arcs after the split
|
||||
|
||||
base_chain.Replace( 5, 6, chain_insert );
|
||||
BOOST_CHECK( GEOM_TEST::IsOutlineValid( base_chain ) );
|
||||
BOOST_CHECK_EQUAL( base_chain.PointCount(), 13 ); // Adding 3 points, removing 2
|
||||
BOOST_CHECK_EQUAL( base_chain.ArcCount(), 3 ); // Should have three arcs after the split
|
||||
|
||||
base_chain.Replace( 4, 6, VECTOR2I( 550000, 0 ) );
|
||||
BOOST_CHECK( GEOM_TEST::IsOutlineValid( base_chain ) );
|
||||
BOOST_CHECK_EQUAL( base_chain.PointCount(), 11 ); // Adding 1 point, removing 3
|
||||
BOOST_CHECK_EQUAL( base_chain.ArcCount(), 3 ); // Should still have three arcs
|
||||
}
|
||||
|
||||
|
||||
struct ARC_FOUND_PROPERTIES
|
||||
{
|
||||
VECTOR2I m_Start;
|
||||
|
|
Loading…
Reference in New Issue