SHAPE_LINE_CHAIN: Add ability to insert arcs

Creates new substructure for arc management.  Existing functions provide
wrapper to use SHAPE_LINE_CHAIN as normal, without arcs while also
permitting the addition of arcs into the chain
This commit is contained in:
Seth Hillbrand 2019-03-25 19:55:59 -07:00
parent 83057a48e1
commit c52b3ce0e4
3 changed files with 313 additions and 43 deletions

View File

@ -98,6 +98,41 @@ public:
m_pc += aVector;
}
/**
* Function Rotate
* rotates the arc by a given angle about a point
* @param aCenter is the rotation center
* @param aAngle rotation angle in radians
*/
void Rotate( double aAngle, const VECTOR2I& aCenter )
{
m_p0 -= aCenter;
m_pc -= aCenter;
m_p0.Rotate( aAngle );
m_pc.Rotate( aAngle );
m_pc += aCenter;
m_p0 += aCenter;
}
void Mirror( bool aX = true, bool aY = false, const VECTOR2I& aVector = { 0, 0 } )
{
if( aX )
{
m_p0.x = -m_p0.x + 2 * aVector.x;
m_pc.x = -m_pc.x + 2 * aVector.x;
m_centralAngle = - m_centralAngle;
}
if( aY )
{
m_p0.y = -m_p0.y + 2 * aVector.y;
m_pc.y = -m_pc.y + 2 * aVector.y;
m_centralAngle = - m_centralAngle;
}
}
int GetRadius() const;
SEG GetChord() const

View File

@ -37,6 +37,7 @@
#include <clipper.hpp>
#include <geometry/seg.h>
#include <geometry/shape.h>
#include <geometry/shape_arc.h>
#include <math/box2.h> // for BOX2I
#include <math/vector2d.h>
@ -85,10 +86,14 @@ public:
/**
* Copy Constructor
*/
SHAPE_LINE_CHAIN( const SHAPE_LINE_CHAIN& aShape )
: SHAPE( SH_LINE_CHAIN ),
m_points( aShape.m_points ),
m_shapes( aShape.m_shapes ),
m_arcs( aShape.m_arcs ),
m_closed( aShape.m_closed ),
m_bbox( aShape.m_bbox ),
m_width( aShape.m_width )
{}
@ -99,18 +104,22 @@ public:
for( auto pt : aV )
m_points.emplace_back( pt.x, pt.y );
m_shapes = std::vector<ssize_t>( aV.size(), ssize_t( SHAPE_IS_PT ) );
}
SHAPE_LINE_CHAIN( const std::vector<VECTOR2I>& aV, bool aClosed = false )
: SHAPE( SH_LINE_CHAIN ), m_closed( aClosed ), m_width( 0 )
{
m_points = aV;
m_shapes = std::vector<ssize_t>( aV.size(), ssize_t( SHAPE_IS_PT ) );
}
SHAPE_LINE_CHAIN( const ClipperLib::Path& aPath )
: SHAPE( SH_LINE_CHAIN ), m_closed( true ), m_width( 0 )
{
m_points.reserve( aPath.size() );
m_shapes = std::vector<ssize_t>( aPath.size(), ssize_t( SHAPE_IS_PT ) );
for( const auto& point : aPath )
m_points.emplace_back( point.X, point.Y );
@ -130,6 +139,8 @@ public:
void Clear()
{
m_points.clear();
m_arcs.clear();
m_shapes.clear();
m_closed = false;
}
@ -252,6 +263,9 @@ public:
aIndex -= PointCount();
m_points[aIndex] = aPos;
if( m_shapes[aIndex] != SHAPE_IS_PT )
convertArc( m_shapes[aIndex] );
}
/**
@ -382,6 +396,7 @@ public:
if( m_points.size() == 0 || aAllowDuplication || CPoint( -1 ) != aP )
{
m_points.push_back( aP );
m_shapes.push_back( ssize_t( SHAPE_IS_PT ) );
m_bbox.Merge( aP );
}
}
@ -392,30 +407,13 @@ public:
* Appends another line chain at the end.
* @param aOtherLine the line chain to be appended.
*/
void Append( const SHAPE_LINE_CHAIN& aOtherLine )
{
if( aOtherLine.PointCount() == 0 )
return;
void Append( const SHAPE_LINE_CHAIN& aOtherLine );
else if( PointCount() == 0 || aOtherLine.CPoint( 0 ) != CPoint( -1 ) )
{
const VECTOR2I p = aOtherLine.CPoint( 0 );
m_points.push_back( p );
m_bbox.Merge( p );
}
void Append( const SHAPE_ARC& aArc );
for( int i = 1; i < aOtherLine.PointCount(); i++ )
{
const VECTOR2I p = aOtherLine.CPoint( i );
m_points.push_back( p );
m_bbox.Merge( p );
}
}
void Insert( size_t aVertex, const VECTOR2I& aP );
void Insert( int aVertex, const VECTOR2I& aP )
{
m_points.insert( m_points.begin() + aVertex, aP );
}
void Insert( size_t aVertex, const SHAPE_ARC& aArc );
/**
* Function Replace()
@ -601,6 +599,13 @@ public:
*/
SHAPE_LINE_CHAIN& Simplify();
/**
* Converts an arc to only a point chain by removing the arc and references
*
* @param aArcIndex index of the arc to convert to points
*/
void convertArc( ssize_t aArcIndex );
/**
* Creates a new Clipper path from the SHAPE_LINE_CHAIN in a given orientation
*
@ -658,8 +663,11 @@ public:
void Move( const VECTOR2I& aVector ) override
{
for( std::vector<VECTOR2I>::iterator i = m_points.begin(); i != m_points.end(); ++i )
(*i) += aVector;
for( auto& pt : m_points )
pt += aVector;
for( auto& arc : m_arcs )
arc.Move( aVector );
}
/**
@ -668,17 +676,7 @@ public:
* @param aY If true, mirror about the x axis (flip Y coordinate)
* @param aRef sets the reference point about which to mirror
*/
void Mirror( bool aX = true, bool aY = false, const VECTOR2I& aRef = { 0, 0 } )
{
for( auto& pt : m_points )
{
if( aX )
pt.x = -pt.x + 2 * aRef.x;
if( aY )
pt.y = -pt.y + 2 * aRef.y;
}
}
void Mirror( bool aX = true, bool aY = false, const VECTOR2I& aRef = { 0, 0 } );
/**
* Function Rotate
@ -701,6 +699,16 @@ private:
/// array of vertices
std::vector<VECTOR2I> m_points;
/**
* Array of indices that refer to the index of the shape if the point
* is part of a larger shape, e.g. arc or spline.
* If the value is -1, the point is just a point
*/
std::vector<ssize_t> m_shapes;
constexpr static ssize_t SHAPE_IS_PT = -1;
std::vector<SHAPE_ARC> m_arcs;
/// is the line chain closed?
bool m_closed;
@ -714,4 +722,5 @@ private:
BOX2I m_bbox;
};
#endif // __SHAPE_LINE_CHAIN

View File

@ -53,6 +53,29 @@ ClipperLib::Path SHAPE_LINE_CHAIN::convertToClipper( bool aRequiredOrientation )
}
//TODO(SH): Adjust this into two functions: one to convert and one to split the arc into two arcs
void SHAPE_LINE_CHAIN::convertArc( ssize_t aArcIndex )
{
if( aArcIndex < 0 )
aArcIndex += m_arcs.size();
if( aArcIndex >= static_cast<ssize_t>( m_arcs.size() ) )
return;
// Clear the shapes references
for( auto& sh : m_shapes )
{
if( sh == aArcIndex )
sh = SHAPE_IS_PT;
if( sh > aArcIndex )
--sh;
}
m_arcs.erase( m_arcs.begin() + aArcIndex );
}
bool SHAPE_LINE_CHAIN::Collide( const VECTOR2I& aP, int aClearance ) const
{
// fixme: ugly!
@ -63,12 +86,15 @@ bool SHAPE_LINE_CHAIN::Collide( const VECTOR2I& aP, int aClearance ) const
void SHAPE_LINE_CHAIN::Rotate( double aAngle, const VECTOR2I& aCenter )
{
for( std::vector<VECTOR2I>::iterator i = m_points.begin(); i != m_points.end(); ++i )
for( auto& pt : m_points )
{
(*i) -= aCenter;
(*i) = (*i).Rotate( aAngle );
(*i) += aCenter;
pt -= aCenter;
pt = pt.Rotate( aAngle );
pt += aCenter;
}
for( auto& arc : m_arcs )
arc.Rotate( aAngle, aCenter );
}
@ -100,6 +126,16 @@ const SHAPE_LINE_CHAIN SHAPE_LINE_CHAIN::Reverse() const
SHAPE_LINE_CHAIN a( *this );
reverse( a.m_points.begin(), a.m_points.end() );
reverse( a.m_shapes.begin(), a.m_shapes.end() );
reverse( a.m_arcs.begin(), a.m_arcs.end() );
for( auto& sh : a.m_shapes )
{
if( sh != SHAPE_IS_PT )
sh = a.m_arcs.size() - sh - 1;
}
a.m_closed = m_closed;
return a;
@ -117,6 +153,22 @@ long long int SHAPE_LINE_CHAIN::Length() const
}
void SHAPE_LINE_CHAIN::Mirror( bool aX, bool aY, const VECTOR2I& aRef )
{
for( auto& pt : m_points )
{
if( aX )
pt.x = -pt.x + 2 * aRef.x;
if( aY )
pt.y = -pt.y + 2 * aRef.y;
}
for( auto& arc : m_arcs )
arc.Mirror( aX, aY, aRef );
}
void SHAPE_LINE_CHAIN::Replace( int aStartIndex, int aEndIndex, const VECTOR2I& aP )
{
if( aEndIndex < 0 )
@ -125,6 +177,15 @@ void SHAPE_LINE_CHAIN::Replace( int aStartIndex, int aEndIndex, const VECTOR2I&
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] != SHAPE_IS_PT )
convertArc( ind );
}
if( aStartIndex == aEndIndex )
m_points[aStartIndex] = aP;
else
@ -143,6 +204,40 @@ void SHAPE_LINE_CHAIN::Replace( int aStartIndex, int aEndIndex, const SHAPE_LINE
if( aStartIndex < 0 )
aStartIndex += PointCount();
aEndIndex = std::min( aEndIndex, PointCount() - 1 );
ssize_t arc_index = -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] != SHAPE_IS_PT )
{
if( arc_index == -1 )
arc_index = m_shapes[ind];
convertArc( ind );
}
}
for( auto remaining_it = m_shapes.erase( m_shapes.begin() + aStartIndex,
m_shapes.begin() + aEndIndex + 1 );
remaining_it != m_shapes.end(); remaining_it++ )
{
if( *remaining_it != SHAPE_IS_PT )
*remaining_it += aLine.m_arcs.size();
}
m_shapes.insert( m_shapes.begin() + aStartIndex, aLine.m_shapes.begin(), aLine.m_shapes.end() );
for( auto new_it = m_shapes.begin() + aStartIndex;
new_it != m_shapes.begin() + aStartIndex + aLine.m_shapes.size() + 1; new_it++ )
{
if( *new_it != SHAPE_IS_PT )
*new_it += arc_index;
}
m_arcs.insert( m_arcs.begin() + arc_index, aLine.m_arcs.begin(), aLine.m_arcs.end() );
m_points.erase( m_points.begin() + aStartIndex, m_points.begin() + aEndIndex + 1 );
m_points.insert( m_points.begin() + aStartIndex, aLine.m_points.begin(), aLine.m_points.end() );
}
@ -156,6 +251,26 @@ void SHAPE_LINE_CHAIN::Remove( int aStartIndex, int aEndIndex )
if( aStartIndex < 0 )
aStartIndex += PointCount();
if( aStartIndex >= PointCount() )
return;
aEndIndex = std::min( aEndIndex, PointCount() );
std::vector<size_t> extra_arcs;
ssize_t last_arc = -1;
for( ssize_t i = aStartIndex; i < aEndIndex; i++)
{
if( m_shapes[i] != SHAPE_IS_PT && m_shapes[i] != last_arc )
extra_arcs.emplace_back( m_shapes[i] );
}
// Reverse the sort order to ensure we maintain valid indices
std::sort( extra_arcs.begin(), extra_arcs.end(), std::greater<size_t>() );
for( auto arc : extra_arcs )
convertArc( arc );
m_shapes.erase( m_shapes.begin() + aStartIndex, m_shapes.begin() + aEndIndex + 1 );
m_points.erase( m_points.begin() + aStartIndex, m_points.begin() + aEndIndex + 1 );
}
@ -204,6 +319,7 @@ int SHAPE_LINE_CHAIN::Split( const VECTOR2I& aP )
if( ii >= 0 )
{
m_points.insert( m_points.begin() + ii + 1, aP );
m_shapes.insert( m_shapes.begin() + ii + 1, ssize_t( SHAPE_IS_PT ) );
return ii + 1;
}
@ -249,6 +365,81 @@ const SHAPE_LINE_CHAIN SHAPE_LINE_CHAIN::Slice( int aStartIndex, int aEndIndex )
}
void SHAPE_LINE_CHAIN::Append( const SHAPE_LINE_CHAIN& aOtherLine )
{
if( aOtherLine.PointCount() == 0 )
return;
else if( PointCount() == 0 || aOtherLine.CPoint( 0 ) != CPoint( -1 ) )
{
const VECTOR2I p = aOtherLine.CPoint( 0 );
m_points.push_back( p );
m_shapes.push_back( ssize_t( SHAPE_IS_PT ) );
m_bbox.Merge( p );
}
for( int i = 1; i < aOtherLine.PointCount(); i++ )
{
const VECTOR2I p = aOtherLine.CPoint( i );
m_points.push_back( p );
m_shapes.push_back( ssize_t( SHAPE_IS_PT ) );
m_bbox.Merge( p );
}
}
void SHAPE_LINE_CHAIN::Append( const SHAPE_ARC& aArc )
{
auto& chain = aArc.ConvertToPolyline();
m_arcs.push_back( aArc );
for( auto& pt : chain.CPoints() )
{
m_points.push_back( pt );
m_shapes.push_back( m_arcs.size() );
}
}
void SHAPE_LINE_CHAIN::Insert( size_t aVertex, const VECTOR2I& aP )
{
if( m_shapes[aVertex] != SHAPE_IS_PT )
convertArc( aVertex );
m_points.insert( m_points.begin() + aVertex, aP );
m_shapes.insert( m_shapes.begin() + aVertex, ssize_t( SHAPE_IS_PT ) );
}
void SHAPE_LINE_CHAIN::Insert( size_t aVertex, const SHAPE_ARC& aArc )
{
if( m_shapes[aVertex] != SHAPE_IS_PT )
convertArc( aVertex );
/// Step 1: Find the position for the new arc in the existing arc vector
size_t arc_pos = m_arcs.size();
for( auto arc_it = m_shapes.rbegin() ;
arc_it != m_shapes.rend() + aVertex;
arc_it++ )
{
if( *arc_it != SHAPE_IS_PT )
arc_pos = ( *arc_it )++;
}
m_arcs.insert( m_arcs.begin() + arc_pos, aArc );
/// Step 2: Add the arc polyline points to the chain
auto& chain = aArc.ConvertToPolyline();
m_points.insert( m_points.begin() + aVertex,
chain.CPoints().begin(), chain.CPoints().end() );
/// Step 3: Add the vector of indices to the shape vector
std::vector<size_t> new_points( chain.PointCount(), arc_pos );
m_shapes.insert( m_shapes.begin() + aVertex, new_points.begin(), new_points.end() );
}
struct compareOriginDistance
{
compareOriginDistance( VECTOR2I& aOrigin ) :
@ -550,6 +741,7 @@ SHAPE_LINE_CHAIN& SHAPE_LINE_CHAIN::Simplify()
i = 0;
// stage 1: eliminate collinear segments
// TODO(sh): handle arcs Maybe segment-wise?
while( i < np - 2 )
{
const VECTOR2I p0 = pts_unique[i];
@ -562,6 +754,7 @@ SHAPE_LINE_CHAIN& SHAPE_LINE_CHAIN::Simplify()
n++;
m_points.push_back( p0 );
m_shapes.push_back( ssize_t( SHAPE_IS_PT ) );
if( n > i )
i = n;
@ -569,6 +762,7 @@ SHAPE_LINE_CHAIN& SHAPE_LINE_CHAIN::Simplify()
if( n == np )
{
m_points.push_back( pts_unique[n - 1] );
m_shapes.push_back( ssize_t( SHAPE_IS_PT ) );
return *this;
}
@ -576,9 +770,13 @@ SHAPE_LINE_CHAIN& SHAPE_LINE_CHAIN::Simplify()
}
if( np > 1 )
{
m_points.push_back( pts_unique[np - 2] );
m_shapes.push_back( ssize_t( SHAPE_IS_PT ) );
}
m_points.push_back( pts_unique[np - 1] );
m_shapes.push_back( ssize_t( SHAPE_IS_PT ) );
return *this;
}
@ -648,10 +846,15 @@ const std::string SHAPE_LINE_CHAIN::Format() const
{
std::stringstream ss;
ss << m_points.size() << " " << ( m_closed ? 1 : 0 ) << " ";
ss << m_points.size() << " " << ( m_closed ? 1 : 0 ) << " " << m_arcs.size() << " ";
for( int i = 0; i < PointCount(); i++ )
ss << m_points[i].x << " " << m_points[i].y << " "; // Format() << " ";
ss << m_points[i].x << " " << m_points[i].y << " " << m_shapes[i];
for( size_t i = 0; i < m_arcs.size(); i++ )
ss << m_arcs[i].GetCenter().x << " " << m_arcs[i].GetCenter().y << " "
<< m_arcs[i].GetP0().x << " " << m_arcs[i].GetP0().y << " "
<< m_arcs[i].GetCentralAngle();
return ss.str();
}
@ -687,23 +890,46 @@ SHAPE* SHAPE_LINE_CHAIN::Clone() const
bool SHAPE_LINE_CHAIN::Parse( std::stringstream& aStream )
{
int n_pts;
size_t n_pts;
size_t n_arcs;
m_points.clear();
aStream >> n_pts;
// Rough sanity check, just make sure the loop bounds aren't absolutely outlandish
if( n_pts < 0 || n_pts > int( aStream.str().size() ) )
if( n_pts > aStream.str().size() )
return false;
aStream >> m_closed;
aStream >> n_arcs;
for( int i = 0; i < n_pts; i++ )
if( n_arcs > aStream.str().size() )
return false;
for( size_t i = 0; i < n_pts; i++ )
{
int x, y;
ssize_t ind;
aStream >> x;
aStream >> y;
m_points.emplace_back( x, y );
aStream >> ind;
m_shapes.push_back( ind );
}
for( size_t i = 0; i < n_arcs; i++ )
{
VECTOR2I p0, pc;
double angle;
aStream >> pc.x;
aStream >> pc.y;
aStream >> p0.x;
aStream >> p0.y;
aStream >> angle;
m_arcs.emplace_back( pc, p0, angle );
}
return true;