Refactor SHAPE_LINE_CHAIN to allow two shapes per point

m_shapes now has two possible indices. The first one is populated if
the point is associated with an arc and the second index is populated
if the point is shared between two arcs.
This commit is contained in:
Roberto Fernandez Bautista 2021-05-06 23:10:39 +01:00 committed by Jon Evans
parent e54a44e1d3
commit c3051ba48a
9 changed files with 406 additions and 162 deletions

View File

@ -2,7 +2,7 @@
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2019 CERN
* 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
@ -26,9 +26,26 @@
#define INCLUDE_CORE_KICAD_ALGO_H_
#include <algorithm>
#include <functional> // std::function
#include <utility> // std::pair
namespace alg
{
/**
* @brief Apply a function to the first and second element of a std::pair
* @param __pair A pair of elements (both elements must be the same type).
* @param __f A unary function object.
*
* Applies the function object @p __f to @p __pair.first and @p __pair.second
* If @p __f has a return value it is ignored.
*/
template <typename _Type, typename _Function>
void run_on_pair( std::pair<_Type, _Type>& __pair, _Function __f )
{
__f( __pair.first );
__f( __pair.second );
}
/**
* @brief Apply a function to every sequential pair of elements of a sequence.
* @param __first An input iterator.
@ -82,6 +99,22 @@ bool contains( const _Container& __container, _Value __value )
{
return std::find( __container.begin(), __container.end(), __value ) != __container.end();
}
/**
* @brief Returns true if either of the elements in an std::pair contains the given value
*
* @param __pair A pair of elements (both elements must be the same type).
* @param __value A value to test
* @return true if @p __value is contained in @p __pair
*/
template <typename _Type, typename _Value>
bool pair_contains( const std::pair<_Type, _Type> __pair, _Value __value )
{
return __pair.first == static_cast<_Type>( __value )
|| __pair.second == static_cast<_Type>( __value );
}
} // namespace alg
#endif /* INCLUDE_CORE_KICAD_ALGO_H_ */

View File

@ -132,14 +132,14 @@ 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 ) );
m_shapes = std::vector<std::pair<ssize_t, ssize_t>>( aV.size(), SHAPES_ARE_PT );
}
SHAPE_LINE_CHAIN( const std::vector<VECTOR2I>& aV, bool aClosed = false )
: SHAPE_LINE_CHAIN_BASE( 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 ) );
m_shapes = std::vector<std::pair<ssize_t, ssize_t>>( aV.size(), SHAPES_ARE_PT );
}
SHAPE_LINE_CHAIN( const SHAPE_ARC& aArc, bool aClosed = false )
@ -149,7 +149,7 @@ public:
{
m_points = aArc.ConvertToPolyline().CPoints();
m_arcs.emplace_back( aArc );
m_shapes = std::vector<ssize_t>( m_points.size(), 0 );
m_shapes = std::vector<std::pair<ssize_t, ssize_t>>( m_points.size(), SHAPES_ARE_PT );
}
SHAPE_LINE_CHAIN( const ClipperLib::Path& aPath );
@ -305,18 +305,7 @@ public:
* @param aIndex is the index of the point to move.
* @param aPos is the new absolute location of the point.
*/
void SetPoint( int aIndex, const VECTOR2I& aPos )
{
if( aIndex < 0 )
aIndex += PointCount();
else if( aIndex >= PointCount() )
aIndex -= PointCount();
m_points[aIndex] = aPos;
if( m_shapes[aIndex] != SHAPE_IS_PT )
convertArc( m_shapes[aIndex] );
}
void SetPoint( int aIndex, const VECTOR2I& aPos );
/**
* Return a reference to a given point in the line chain.
@ -344,7 +333,7 @@ public:
*/
const VECTOR2I& CLastPoint() const
{
return m_points[PointCount() - 1];
return m_points[static_cast<size_t>( PointCount() ) - 1];
}
/**
@ -358,7 +347,7 @@ public:
/**
* @return the vector of values indicating shape type and location.
*/
const std::vector<ssize_t>& CShapes() const
const std::vector<std::pair<ssize_t, ssize_t>>& CShapes() const
{
return m_shapes;
}
@ -441,7 +430,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_shapes.push_back( SHAPES_ARE_PT );
m_bbox.Merge( aP );
}
}
@ -716,12 +705,15 @@ public:
return m_arcs.size();
}
/**
* Return the arc index for the given segment index.
*/
ssize_t ArcIndex( size_t aSegment ) const
{
if( aSegment >= m_shapes.size() )
return SHAPE_IS_PT;
return m_shapes[aSegment];
if( IsSharedPt( aSegment ) )
return m_shapes[aSegment].second;
else
return m_shapes[aSegment].first;
}
const SHAPE_ARC& Arc( size_t aArc ) const
@ -729,16 +721,58 @@ public:
return m_arcs[aArc];
}
bool isArc( size_t aSegment ) const
/**
* Test if a point is shared between multiple shapes
* @param aIndex
* @return
*/
bool IsSharedPt( size_t aIndex ) const
{
return aIndex < m_shapes.size() - 1
&& m_shapes[aIndex].first != SHAPE_IS_PT
&& m_shapes[aIndex].second != SHAPE_IS_PT;
}
bool IsPtOnArc( size_t aPtIndex ) const
{
return aPtIndex < m_shapes.size() && m_shapes[aPtIndex] != SHAPES_ARE_PT;
}
bool IsArcSegment( size_t aSegment ) const
{
/*
* A segment is part of an arc except in the special case of two arcs next to each other
* but without a shared vertex. Here there is a segment between the end of the first arc
* and the start of the second arc.
*/
return ( aSegment < m_shapes.size() - 1
&& m_shapes[aSegment] != SHAPE_IS_PT
&& m_shapes[aSegment] == m_shapes[aSegment + 1] );
size_t nextIdx = aSegment + 1;
if( nextIdx > m_shapes.size() - 1 )
return false; // Always false, even if the shape is closed
return ( IsPtOnArc( aSegment )
&& ( IsSharedPt( aSegment )
|| m_shapes[aSegment].first == m_shapes[nextIdx].first ) );
}
bool IsArcStart( size_t aIndex ) const
{
if( aIndex == 0 )
return IsPtOnArc( aIndex );
return ( IsSharedPt( aIndex ) || ( IsPtOnArc( aIndex ) && !IsArcSegment( aIndex - 1 ) ) );
}
bool IsArcEnd( size_t aIndex ) const
{
if( aIndex == static_cast<size_t>( PointCount() ) - 1 )
return IsPtOnArc( aIndex );
return ( IsSharedPt( aIndex ) || ( IsPtOnArc( aIndex ) && !IsArcSegment( aIndex ) ) );
}
virtual const VECTOR2I GetPoint( int aIndex ) const override { return CPoint(aIndex); }
@ -763,7 +797,9 @@ protected:
private:
constexpr static ssize_t SHAPE_IS_PT = -1;
static const ssize_t SHAPE_IS_PT;
static const std::pair<ssize_t, ssize_t> SHAPES_ARE_PT;
/// array of vertices
std::vector<VECTOR2I> m_points;
@ -772,8 +808,17 @@ private:
* 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.
*
* There can be up to two shapes associated with a single point (e.g. the end point of
* one arc might be the start point of another).
*
* Generally speaking only the first element of the pair will be populated (i.e. with a value
* not equal to SHAPE_IS_PT), unless the point is shared between two arc shapes. If the point
* is shared, then both the first and second element of the pair should be populated.
*
* The second element must always be SHAPE_IS_PT if the first element is SHAPE_IS_PT.
*/
std::vector<ssize_t> m_shapes;
std::vector<std::pair<ssize_t, ssize_t>> m_shapes;
std::vector<SHAPE_ARC> m_arcs;

View File

@ -31,6 +31,7 @@
#include <string> // for basic_string
#include <clipper.hpp>
#include <core/kicad_algo.h> // for alg::run_on_pair
#include <geometry/seg.h> // for SEG, OPT_VECTOR2I
#include <geometry/circle.h> // for CIRCLE
#include <geometry/shape_line_chain.h>
@ -41,8 +42,10 @@
class SHAPE;
const ssize_t SHAPE_LINE_CHAIN::SHAPE_IS_PT = -1;
const std::pair<ssize_t, ssize_t> SHAPE_LINE_CHAIN::SHAPES_ARE_PT = { SHAPE_IS_PT, SHAPE_IS_PT };
SHAPE_LINE_CHAIN::SHAPE_LINE_CHAIN( const std::vector<int>& aV )
SHAPE_LINE_CHAIN::SHAPE_LINE_CHAIN( const std::vector<int>& aV)
: SHAPE_LINE_CHAIN_BASE( SH_LINE_CHAIN ), m_closed( false ), m_width( 0 )
{
for(size_t i = 0; i < aV.size(); i+= 2 )
@ -94,11 +97,18 @@ void SHAPE_LINE_CHAIN::convertArc( ssize_t aArcIndex )
// Clear the shapes references
for( auto& sh : m_shapes )
{
if( sh == aArcIndex )
sh = SHAPE_IS_PT;
alg::run_on_pair( sh,
[&]( ssize_t& aShapeIndex )
{
if( aShapeIndex == aArcIndex )
aShapeIndex = SHAPE_IS_PT;
if( sh > aArcIndex )
--sh;
if( aShapeIndex > aArcIndex )
--aShapeIndex;
} );
if( sh.second != SHAPE_IS_PT && sh.first == SHAPE_IS_PT )
std::swap( sh.first, sh.second );
}
m_arcs.erase( m_arcs.begin() + aArcIndex );
@ -230,7 +240,7 @@ void SHAPE_LINE_CHAIN::DetectArcs( const std::vector<SHAPE_ARC>& aArcs, int aMar
{
// Remove all arcs
m_arcs.clear();
std::fill( m_shapes.begin(), m_shapes.end(), ssize_t( SHAPE_IS_PT ) );
std::fill( m_shapes.begin(), m_shapes.end(), SHAPES_ARE_PT );
auto emplaceArc =
[&]( point_iter aStart, point_iter aEnd, SHAPE_ARC aReplacementArc )
@ -239,7 +249,19 @@ void SHAPE_LINE_CHAIN::DetectArcs( const std::vector<SHAPE_ARC>& aArcs, int aMar
size_t endIndex = aEnd - m_points.begin();
for( int ii = startIndex; ii <= endIndex; ++ii )
m_shapes[ii] = m_arcs.size();
{
if( m_shapes[ii].first == SHAPE_IS_PT )
{
m_shapes[ii].first = m_arcs.size();
}
else
{
// The point can't be shared with more than two arcs
assert( m_shapes[ii].second == SHAPE_IS_PT );
m_shapes[ii].second = m_arcs.size();
}
}
m_arcs.push_back( aReplacementArc );
};
@ -336,8 +358,24 @@ const SHAPE_LINE_CHAIN SHAPE_LINE_CHAIN::Reverse() const
for( auto& sh : a.m_shapes )
{
if( sh != SHAPE_IS_PT )
sh = a.m_arcs.size() - sh - 1;
if( sh != SHAPES_ARE_PT )
{
alg::run_on_pair( sh,
[&]( ssize_t& aShapeIndex )
{
if( aShapeIndex != SHAPE_IS_PT )
aShapeIndex = a.m_arcs.size() - aShapeIndex - 1;
} );
if( sh.second != SHAPE_IS_PT )
{
// If the second element is populated, the first one should be too!
assert( sh.first != SHAPE_IS_PT );
// Switch round first and second in shared points, as part of reversing the chain
std::swap( sh.first, sh.second );
}
}
}
for( SHAPE_ARC& arc : a.m_arcs )
@ -356,11 +394,8 @@ long long int SHAPE_LINE_CHAIN::Length() const
for( int i = 0; i < SegmentCount(); i++ )
{
// Only include segments that aren't part of arc shapes
if( m_shapes[i] == SHAPE_IS_PT || m_shapes[i + 1] == SHAPE_IS_PT ||
( m_shapes[i] != m_shapes[i + 1] ) )
{
if( !IsArcSegment(i) )
l += CSegment( i ).Length();
}
}
for( int i = 0; i < ArcCount(); i++ )
@ -409,8 +444,11 @@ void SHAPE_LINE_CHAIN::Replace( int aStartIndex, int aEndIndex, const VECTOR2I&
// 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( m_shapes[ind] != SHAPES_ARE_PT )
alg::run_on_pair( m_shapes[ind], [&]( size_t aIndex )
{
convertArc( aIndex );
} );
}
if( aStartIndex == aEndIndex )
@ -445,8 +483,10 @@ void SHAPE_LINE_CHAIN::Replace( int aStartIndex, int aEndIndex, const SHAPE_LINE
// 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).
ssize_t startShape = m_shapes[aStartIndex];
ssize_t endShape = m_shapes[aEndIndex];
// @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 )
{
@ -471,13 +511,19 @@ void SHAPE_LINE_CHAIN::Replace( int aStartIndex, int aEndIndex, const SHAPE_LINE
return;
// The total new arcs index is added to the new arc indices
size_t prev_arc_count = m_arcs.size();
std::vector<ssize_t> new_shapes = newLine.m_shapes;
size_t prev_arc_count = m_arcs.size();
std::vector<std::pair<ssize_t, ssize_t>> new_shapes = newLine.m_shapes;
for( ssize_t& shape : new_shapes )
for( std::pair<ssize_t, ssize_t>& shape_pair : new_shapes )
{
if( shape >= 0 )
shape += prev_arc_count;
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;
}
}
m_shapes.insert( m_shapes.begin() + aStartIndex, new_shapes.begin(), new_shapes.end() );
@ -502,14 +548,41 @@ void SHAPE_LINE_CHAIN::Remove( int aStartIndex, int aEndIndex )
if( aStartIndex >= PointCount() )
return;
aEndIndex = std::min( aEndIndex, PointCount() );
aEndIndex = std::min( aEndIndex, PointCount() - 1 );
std::set<size_t> extra_arcs;
auto logArcIdxRemoval = [&]( ssize_t& aShapeIndex )
{
if( aShapeIndex != SHAPE_IS_PT )
extra_arcs.insert( aShapeIndex );
};
// Remove any overlapping arcs in the point range
for( int i = aStartIndex; i < aEndIndex; i++ )
for( int i = aStartIndex; i <= aEndIndex; i++ )
{
if( m_shapes[i] != SHAPE_IS_PT )
extra_arcs.insert( m_shapes[i] );
if( IsSharedPt( i ) )
{
if( i == aStartIndex )
{
logArcIdxRemoval( m_shapes[i].second ); // Only remove the arc on the second index
// Ensure that m_shapes has been built correctly.
assert( i < aEndIndex || m_shapes[i + 1].first == m_shapes[i].second );
continue;
}
else if( i == aEndIndex )
{
logArcIdxRemoval( m_shapes[i].first ); // Only remove the arc on the first index
// Ensure that m_shapes has been built correctly.
assert( i > aStartIndex || IsSharedPt( i - 1 )
? m_shapes[i - 1].second == m_shapes[i].first
: m_shapes[i - 1].first == m_shapes[i].first );
continue;
}
}
alg::run_on_pair( m_shapes[i], logArcIdxRemoval );
}
for( auto arc : extra_arcs )
@ -572,11 +645,11 @@ int SHAPE_LINE_CHAIN::Split( const VECTOR2I& aP )
{
// Are we splitting at the beginning of an arc? If so, let's split right before so that
// the shape is preserved
if( ii < PointCount() - 1 && m_shapes[ii] >= 0 && m_shapes[ii] == m_shapes[ii + 1] )
if( IsArcSegment( ii ) )
ii--;
m_points.insert( m_points.begin() + ( ii + 1 ), aP );
m_shapes.insert( m_shapes.begin() + ( ii + 1 ), ssize_t( SHAPE_IS_PT ) );
m_shapes.insert( m_shapes.begin() + ( ii + 1 ), SHAPES_ARE_PT );
return ii + 1;
}
@ -628,17 +701,29 @@ int SHAPE_LINE_CHAIN::ShapeCount() const
for( int i = 0; i < m_points.size() - 1; i++ )
{
if( m_shapes[i] == SHAPE_IS_PT )
if( m_shapes[i] == SHAPES_ARE_PT )
{
numShapes++;
}
else
{
arcIdx = m_shapes[i];
// Expect that the second index only gets populated when the point is shared between
// two shapes. Otherwise, the shape index should always go on the first element of
// the pair.
assert( m_shapes[i].first != SHAPE_IS_PT );
// Start assuming the point is shared with the previous arc
// If so, the new/next arc index should be located at the second
// element in the pair
arcIdx = m_shapes[i].second;
if( arcIdx == SHAPE_IS_PT )
arcIdx = m_shapes[i].first; // Not a shared point
numShapes++;
// Now skip the rest of the arc
while( i < numPoints && m_shapes[i] == arcIdx )
while( i < numPoints && m_shapes[i].first == arcIdx )
i++;
// Add the "hidden" segment at the end of the arc, if it exists
@ -669,43 +754,86 @@ int SHAPE_LINE_CHAIN::NextShape( int aPointIndex, bool aForwards ) const
int delta = aForwards ? 1 : -1;
if( m_shapes[aPointIndex] == SHAPE_IS_PT )
if( m_shapes[aPointIndex] == SHAPES_ARE_PT )
return aPointIndex + delta;
int arcIndex = m_shapes[aPointIndex];
int arcStart = aPointIndex;
while( aPointIndex < static_cast<int>( m_shapes.size() ) && m_shapes[aPointIndex] == arcIndex )
// The second element should only get populated when the point is shared between two shapes.
// If not a shared point, then the index should always go on the first element.
assert( m_shapes[aPointIndex].first != SHAPE_IS_PT );
// Start with the assumption the point is shared
int arcIndex = m_shapes[aPointIndex].second;
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 )
aPointIndex += delta;
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
// Well-formed arcs should generate more than one point to travel above
if( aPointIndex - arcStart > 1 )
if( aPointIndex - arcStart > 1 && !indexStillOnArc )
aPointIndex -= delta;
return aPointIndex;
}
void SHAPE_LINE_CHAIN::SetPoint( int aIndex, const VECTOR2I& aPos )
{
if( aIndex < 0 )
aIndex += PointCount();
else if( aIndex >= PointCount() )
aIndex -= PointCount();
m_points[aIndex] = aPos;
alg::run_on_pair( m_shapes[aIndex],
[&]( ssize_t& aIdx )
{
if( aIdx != SHAPE_IS_PT )
convertArc( aIdx );
} );
}
void SHAPE_LINE_CHAIN::RemoveShape( int aPointIndex )
{
if( aPointIndex < 0 )
aPointIndex += PointCount();
if( m_shapes[aPointIndex] == SHAPE_IS_PT )
if( m_shapes[aPointIndex] == SHAPES_ARE_PT )
{
Remove( aPointIndex );
return;
}
//@todo should this be replaced to use NextShape() / PrevShape()?
int start = aPointIndex;
int end = aPointIndex;
int arcIdx = m_shapes[aPointIndex];
int arcIdx = ArcIndex( aPointIndex );
while( start >= 0 && m_shapes[start] == arcIdx )
start--;
if( !IsSharedPt( aPointIndex ) )
{
// aPointIndex is not a shared point, so iterate backwards to find the start of the arc
while( start >= 0 && m_shapes[start].first == arcIdx )
start--;
while( end < static_cast<int>( m_shapes.size() ) - 1 && m_shapes[end] == arcIdx )
// Check if the previous point might be a shared point and decrement 'start' if so
if( start >= 1 && m_shapes[static_cast<ssize_t>( start ) - 1].second == arcIdx )
start--;
}
// For the end point we only need to check the first element in m_shapes (the second one is only
// populated if there is an arc after the current one sharing the same point).
while( end < static_cast<int>( m_shapes.size() ) - 1 && m_shapes[end].first == arcIdx )
end++;
Remove( start, end );
@ -726,16 +854,17 @@ const SHAPE_LINE_CHAIN SHAPE_LINE_CHAIN::Slice( int aStartIndex, int aEndIndex )
for( int i = aStartIndex; i <= aEndIndex && i < numPoints; i++ )
{
if( m_shapes[i] != SHAPE_IS_PT )
if( m_shapes[i] != SHAPES_ARE_PT )
{
int arcIdx = m_shapes[i];
int arcIdx = ArcIndex( i );
bool wholeArc = true;
int arcStart = i;
size_t prevIdx = static_cast<size_t>( i ) - 1;
if( i > 0 && m_shapes[i - 1] >= 0 && m_shapes[i - 1] != arcIdx )
if( i > 0 && IsArcSegment( prevIdx ) && ArcIndex( prevIdx ) != arcIdx )
wholeArc = false;
while( i < numPoints && m_shapes[i] == arcIdx )
while( i < numPoints && ArcIndex( i ) == arcIdx )
i++;
i--;
@ -749,6 +878,7 @@ const SHAPE_LINE_CHAIN SHAPE_LINE_CHAIN::Slice( int aStartIndex, int aEndIndex )
}
else
{
//@todo need to split up the arc
rv.Append( m_points[arcStart] );
i = arcStart;
}
@ -772,16 +902,39 @@ void SHAPE_LINE_CHAIN::Append( const SHAPE_LINE_CHAIN& aOtherLine )
return;
}
else if( PointCount() == 0 || aOtherLine.CPoint( 0 ) != CPoint( -1 ) )
size_t num_arcs = m_arcs.size();
m_arcs.insert( m_arcs.end(), aOtherLine.m_arcs.begin(), aOtherLine.m_arcs.end() );
auto fixShapeIndices =
[&]( const std::pair<ssize_t, ssize_t>& aShapeIndices ) -> std::pair<ssize_t, ssize_t>
{
std::pair<ssize_t, ssize_t> retval = aShapeIndices;
alg::run_on_pair( retval, [&]( ssize_t& aIndex )
{
if( aIndex != SHAPE_IS_PT )
aIndex = aIndex + num_arcs;
} );
return retval;
};
if( PointCount() == 0 || aOtherLine.CPoint( 0 ) != CPoint( -1 ) )
{
const VECTOR2I p = aOtherLine.CPoint( 0 );
m_points.push_back( p );
m_shapes.push_back( aOtherLine.CShapes()[0] );
m_shapes.push_back( fixShapeIndices( aOtherLine.CShapes()[0] ) );
m_bbox.Merge( p );
}
else if( aOtherLine.IsArcSegment( 0 ) )
{
// Associate the new arc shape with the last point of this chain
if( m_shapes.back() == SHAPES_ARE_PT )
m_shapes.back().first = aOtherLine.CShapes()[0].first + num_arcs;
else
m_shapes.back().second = aOtherLine.CShapes()[0].first + num_arcs;
}
size_t num_arcs = m_arcs.size();
m_arcs.insert( m_arcs.end(), aOtherLine.m_arcs.begin(), aOtherLine.m_arcs.end() );
for( int i = 1; i < aOtherLine.PointCount(); i++ )
{
@ -791,9 +944,11 @@ void SHAPE_LINE_CHAIN::Append( const SHAPE_LINE_CHAIN& aOtherLine )
ssize_t arcIndex = aOtherLine.ArcIndex( i );
if( arcIndex != ssize_t( SHAPE_IS_PT ) )
m_shapes.push_back( num_arcs + arcIndex );
{
m_shapes.push_back( fixShapeIndices( aOtherLine.m_shapes[i] ) );
}
else
m_shapes.push_back( ssize_t( SHAPE_IS_PT ) );
m_shapes.push_back( SHAPES_ARE_PT );
m_bbox.Merge( p );
}
@ -804,15 +959,15 @@ void SHAPE_LINE_CHAIN::Append( const SHAPE_LINE_CHAIN& aOtherLine )
void SHAPE_LINE_CHAIN::Append( const SHAPE_ARC& aArc )
{
auto& chain = aArc.ConvertToPolyline();
SHAPE_LINE_CHAIN chain = aArc.ConvertToPolyline();
for( auto& pt : chain.CPoints() )
{
m_points.push_back( pt );
m_shapes.push_back( m_arcs.size() );
}
// @todo should the below 4 LOC be moved to SHAPE_ARC::ConvertToPolyline ?
chain.m_arcs.push_back( aArc );
m_arcs.push_back( aArc );
for( auto& sh : chain.m_shapes )
sh.first = 0;
Append( chain );
assert( m_shapes.size() == m_points.size() );
}
@ -820,11 +975,12 @@ 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] != SHAPE_IS_PT )
if( aVertex < m_points.size() && m_shapes[aVertex] != SHAPES_ARE_PT )
convertArc( aVertex );
//@todo need to check we aren't creating duplicate points
m_points.insert( m_points.begin() + aVertex, aP );
m_shapes.insert( m_shapes.begin() + aVertex, ssize_t( SHAPE_IS_PT ) );
m_shapes.insert( m_shapes.begin() + aVertex, SHAPES_ARE_PT );
assert( m_shapes.size() == m_points.size() );
}
@ -832,28 +988,46 @@ void SHAPE_LINE_CHAIN::Insert( size_t aVertex, const VECTOR2I& aP )
void SHAPE_LINE_CHAIN::Insert( size_t aVertex, const SHAPE_ARC& aArc )
{
if( m_shapes[aVertex] != SHAPE_IS_PT )
if( aVertex < m_points.size() && m_shapes[aVertex] != SHAPES_ARE_PT )
convertArc( aVertex );
/// Step 1: Find the position for the new arc in the existing arc vector
size_t arc_pos = m_arcs.size();
ssize_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 )++;
if( *arc_it != SHAPES_ARE_PT )
{
arc_pos = std::max( ( *arc_it ).first, ( *arc_it ).second );
arc_pos++;
}
}
//Increment all arc indices before inserting the new arc
for( auto& sh : m_shapes )
{
alg::run_on_pair( sh,
[&]( ssize_t& aIndex )
{
if( aIndex >= arc_pos )
aIndex++;
} );
}
m_arcs.insert( m_arcs.begin() + arc_pos, aArc );
/// Step 2: Add the arc polyline points to the chain
//@todo need to check we aren't creating duplicate points at start or end
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 );
//@todo need to check we aren't creating duplicate points at start or end
std::vector<std::pair<ssize_t, ssize_t>> new_points( chain.PointCount(),
{ arc_pos, SHAPE_IS_PT } );
m_shapes.insert( m_shapes.begin() + aVertex, new_points.begin(), new_points.end() );
assert( m_shapes.size() == m_points.size() );
}
@ -1210,7 +1384,7 @@ const OPT<SHAPE_LINE_CHAIN::INTERSECTION> SHAPE_LINE_CHAIN::SelfIntersecting() c
SHAPE_LINE_CHAIN& SHAPE_LINE_CHAIN::Simplify( bool aRemoveColinear )
{
std::vector<VECTOR2I> pts_unique;
std::vector<ssize_t> shapes_unique;
std::vector<std::pair<ssize_t, ssize_t>> shapes_unique;
if( PointCount() < 2 )
{
@ -1236,18 +1410,19 @@ SHAPE_LINE_CHAIN& SHAPE_LINE_CHAIN::Simplify( bool aRemoveColinear )
// one of them is part of a shape and one is not.
while( j < np && m_points[i] == m_points[j] &&
( m_shapes[i] == m_shapes[j] ||
m_shapes[i] == SHAPE_IS_PT ||
m_shapes[j] == SHAPE_IS_PT ) )
m_shapes[i] == SHAPES_ARE_PT ||
m_shapes[j] == SHAPES_ARE_PT ) )
{
j++;
}
int shapeToKeep = m_shapes[i];
std::pair<ssize_t,ssize_t> shapeToKeep = m_shapes[i];
if( shapeToKeep == SHAPE_IS_PT )
if( shapeToKeep == SHAPES_ARE_PT )
shapeToKeep = m_shapes[j - 1];
wxASSERT( shapeToKeep < static_cast<int>( m_arcs.size() ) );
assert( shapeToKeep.first < static_cast<int>( m_arcs.size() ) );
assert( shapeToKeep.second < static_cast<int>( m_arcs.size() ) );
pts_unique.push_back( CPoint( i ) );
shapes_unique.push_back( shapeToKeep );
@ -1268,7 +1443,8 @@ SHAPE_LINE_CHAIN& SHAPE_LINE_CHAIN::Simplify( bool aRemoveColinear )
const VECTOR2I p1 = pts_unique[i + 1];
int n = i;
if( aRemoveColinear && shapes_unique[i] < 0 && shapes_unique[i + 1] < 0 )
if( aRemoveColinear && shapes_unique[i] == SHAPES_ARE_PT
&& shapes_unique[i + 1] == SHAPES_ARE_PT )
{
while( n < np - 2
&& ( SEG( p0, p1 ).LineDistance( pts_unique[n + 2] ) <= 1
@ -1321,9 +1497,9 @@ const VECTOR2I SHAPE_LINE_CHAIN::NearestPoint( const VECTOR2I& aP,
// An internal shape point here is everything after the start of an arc and before the
// second-to-last vertex of the arc, because we are looking at segments here!
if( i > 0 && i < SegmentCount() - 1 && m_shapes[i] >= 0 &&
( ( m_shapes[i - 1] >= 0 && m_shapes[i - 1] == m_shapes[i] ) &&
( m_shapes[i + 2] >= 0 && m_shapes[i + 2] == m_shapes[i] ) ) )
if( i > 0 && i < SegmentCount() - 1 && m_shapes[i] != SHAPES_ARE_PT
&& ( ( m_shapes[i - 1] != SHAPES_ARE_PT && m_shapes[i - 1] == m_shapes[i] )
&& ( m_shapes[i + 2] != SHAPES_ARE_PT && m_shapes[i + 2] == m_shapes[i] ) ) )
{
isInternalShapePoint = true;
}
@ -1335,19 +1511,12 @@ const VECTOR2I SHAPE_LINE_CHAIN::NearestPoint( const VECTOR2I& aP,
}
}
// Is this the start of an arc? If so, return it directly
if( !aAllowInternalShapePoints &&
( ( nearest == 0 && m_shapes[nearest] >= 0 ) ||
( m_shapes[nearest] >= 0 && m_shapes[nearest] != m_shapes[nearest - 1] ) ) )
// Is this the start or end of an arc? If so, return it directly
if( !aAllowInternalShapePoints && ( IsArcStart( nearest ) || IsArcEnd( nearest ) ) )
{
//@todo should we calculate the nearest point to the "true" arc?
return m_points[nearest];
}
else if( !aAllowInternalShapePoints && nearest < SegmentCount() &&
m_shapes[nearest] >= 0 && m_shapes[nearest + 1] == m_shapes[nearest] )
{
// If the nearest segment is the last of the arc, just return the arc endpoint
return m_points[nearest + 1];
}
return CSegment( nearest ).NearestPoint( aP );
}
@ -1482,7 +1651,7 @@ bool SHAPE_LINE_CHAIN::Parse( std::stringstream& aStream )
m_points.emplace_back( x, y );
aStream >> ind;
m_shapes.push_back( ind );
m_shapes.emplace_back( std::make_pair( ind, SHAPE_IS_PT ) );
}
for( size_t i = 0; i < n_arcs; i++ )

View File

@ -137,7 +137,7 @@ bool DRAGGER::startDragSegment( const VECTOR2D& aP, SEGMENT* aSeg )
{
if( distB < distA &&
( m_draggedSegmentIndex < m_draggedLine.PointCount() - 2 ) &&
( m_draggedLine.CLine().CShapes()[ m_draggedSegmentIndex + 1 ] < 0 ) )
( !m_draggedLine.CLine().IsPtOnArc( static_cast<size_t>(m_draggedSegmentIndex) + 1 ) ) )
{
m_draggedSegmentIndex++;
}

View File

@ -540,7 +540,7 @@ bool LINE::Is45Degree() const
{
const SEG& s = m_line.CSegment( i );
if( m_line.isArc( i ) )
if( m_line.IsArcSegment( i ) )
continue;
if( s.Length() < 10 )
@ -687,7 +687,7 @@ void LINE::dragCorner45( const VECTOR2I& aP, int aIndex )
else
{
// Are we next to an arc? Insert a new point so we slice correctly
if( m_line.CShapes()[aIndex + 1] >= 0 )
if( m_line.IsPtOnArc( static_cast<size_t>( aIndex ) + 1 ) )
m_line.Insert( aIndex + 1, m_line.CPoint( aIndex + 1 ) );
// fixme: awkward behaviour for "outwards" drags
@ -705,23 +705,28 @@ void LINE::dragCorner45( const VECTOR2I& aP, int aIndex )
void LINE::dragCornerFree( const VECTOR2I& aP, int aIndex )
{
const std::vector<ssize_t>& shapes = m_line.CShapes();
ssize_t idx = static_cast<ssize_t>( aIndex );
ssize_t numpts = static_cast<ssize_t>( m_line.PointCount() );
// If we're asked to drag the end of an arc, insert a new vertex to drag instead
if( shapes[aIndex] >= 0 )
if( m_line.IsPtOnArc( idx ) )
{
if( aIndex > 0 && shapes[aIndex - 1] == -1 )
m_line.Insert( aIndex, m_line.GetPoint( aIndex ) );
else if( aIndex < shapes.size() - 1 && shapes[aIndex + 1] != shapes[aIndex] )
if( idx == 0 || ( idx > 0 && !m_line.IsPtOnArc( idx - 1 ) ) )
{
aIndex++;
m_line.Insert( aIndex, m_line.GetPoint( aIndex ) );
m_line.Insert( idx, m_line.GetPoint( idx ) );
}
else if( ( idx == numpts - 1 ) || ( idx < numpts - 1 && !m_line.IsArcSegment( idx ) ) )
{
idx++;
m_line.Insert( idx, m_line.GetPoint( idx ) );
}
else
{
wxASSERT_MSG( false, "Attempt to dragCornerFree in the middle of an arc!" );
}
}
m_line.SetPoint( aIndex, aP );
m_line.SetPoint( idx, aP );
m_line.Simplify();
}
@ -850,14 +855,12 @@ void LINE::dragSegment45( const VECTOR2I& aP, int aIndex )
target = snapToNeighbourSegments( path, aP, aIndex );
const std::vector<ssize_t>& shapes = path.CShapes();
// We require a valid s_prev and s_next. If we are at the start or end of the line, we insert
// a new point at the start or end so there is a zero-length segment for prev or next (we will
// resize it as part of the drag operation). If we are next to an arc, we do this also, as we
// cannot drag away one of the arc's points.
if( index == 0 || shapes[index] >= 0 )
if( index == 0 || path.IsPtOnArc( index ) )
{
path.Insert( index > 0 ? index + 1 : 0, path.CPoint( index ) );
index++;
@ -867,7 +870,7 @@ void LINE::dragSegment45( const VECTOR2I& aP, int aIndex )
{
path.Insert( path.PointCount() - 1, path.CPoint( -1 ) );
}
else if( shapes[index + 1] >= 0 )
else if( path.IsPtOnArc( index + 1 ) )
{
path.Insert( index + 1, path.CPoint( index + 1 ) );
}
@ -1079,24 +1082,24 @@ void LINE::ClipVertexRange( int aStart, int aEnd )
int arcIdx = -1;
int linkIdx = 0;
auto shapes = m_line.CShapes();
int numPoints = static_cast<int>( shapes.size() );
int numPoints = static_cast<int>( m_line.PointCount() );
for( int i = 0; i < m_line.PointCount(); i++ )
{
if( i <= aStart )
firstLink = linkIdx;
if( shapes[i] >= 0 )
//@todo RFB Should this be replaced by m_line.NextShape( i ); ?
if( m_line.IsPtOnArc( i ) )
{
// Account for "hidden segments" between two arcs
if( i > aStart && ( shapes[i - 1] >= 0 ) && ( shapes[i - 1] != shapes[i] ) )
if( i > aStart && ( m_line.IsArcSegment( static_cast<size_t>( i ) - 1 ) ) )
linkIdx++;
arcIdx = shapes[i];
arcIdx = m_line.ArcIndex(i);
// Skip over the rest of the arc vertices
while( i < numPoints && shapes[i] == arcIdx )
while( i < numPoints && m_line.ArcIndex( i ) == arcIdx )
i++;
// Back up two vertices to restart at the segment coincident with the end of the arc

View File

@ -194,22 +194,19 @@ bool LINE_PLACER::handlePullback()
DIRECTION_45 first_head, last_tail;
const std::vector<ssize_t>& headShapes = head.CShapes();
const std::vector<ssize_t>& tailShapes = tail.CShapes();
wxASSERT( tail.PointCount() >= 2 );
if( headShapes[0] == -1 )
if( !head.IsPtOnArc( 0 ) )
first_head = DIRECTION_45( head.CSegment( 0 ) );
else
first_head = DIRECTION_45( head.CArcs()[ headShapes[0] ] );
first_head = DIRECTION_45( head.CArcs()[head.ArcIndex(0)] );
int lastSegIdx = tail.PointCount() - 2;
if( tailShapes[lastSegIdx] == -1 )
if( !tail.IsPtOnArc( lastSegIdx ) )
last_tail = DIRECTION_45( tail.CSegment( lastSegIdx ) );
else
last_tail = DIRECTION_45( tail.CArcs()[tailShapes[lastSegIdx]] );
last_tail = DIRECTION_45( tail.CArcs()[tail.ArcIndex(lastSegIdx)] );
DIRECTION_45::AngleType angle = first_head.Angle( last_tail );
@ -226,7 +223,7 @@ bool LINE_PLACER::handlePullback()
{
lastSegIdx = tail.PrevShape( -1 );
if( tailShapes[lastSegIdx] == -1 )
if( !tail.IsPtOnArc( lastSegIdx ) )
{
const SEG& seg = tail.CSegment( lastSegIdx );
m_direction = DIRECTION_45( seg );
@ -234,7 +231,7 @@ bool LINE_PLACER::handlePullback()
}
else
{
const SHAPE_ARC& arc = tail.CArcs()[tailShapes[lastSegIdx]];
const SHAPE_ARC& arc = tail.CArcs()[tail.ArcIndex( lastSegIdx )];
m_direction = DIRECTION_45( arc );
m_p_start = arc.GetP0();
}
@ -357,23 +354,20 @@ bool LINE_PLACER::mergeHead()
DIRECTION_45 dir_tail, dir_head;
const std::vector<ssize_t>& headShapes = head.CShapes();
const std::vector<ssize_t>& tailShapes = tail.CShapes();
if( headShapes[0] == -1 )
if( !head.IsPtOnArc( 0 ) )
dir_head = DIRECTION_45( head.CSegment( 0 ) );
else
dir_head = DIRECTION_45( head.CArcs()[ headShapes[0] ] );
dir_head = DIRECTION_45( head.CArcs()[head.ArcIndex( 0 )] );
if( n_tail )
{
wxASSERT( tail.PointCount() >= 2 );
int lastSegIdx = tail.PointCount() - 2;
if( tailShapes[lastSegIdx] == -1 )
if( !tail.IsPtOnArc( lastSegIdx ) )
dir_tail = DIRECTION_45( tail.CSegment( -1 ) );
else
dir_tail = DIRECTION_45( tail.CArcs()[ tailShapes[lastSegIdx] ] );
dir_tail = DIRECTION_45( tail.CArcs()[tail.ArcIndex( lastSegIdx )] );
if( dir_head.Angle( dir_tail ) & ForbiddenAngles )
return false;
@ -388,10 +382,10 @@ bool LINE_PLACER::mergeHead()
int lastSegIdx = tail.PointCount() - 2;
if( tailShapes[lastSegIdx] == -1 )
if( !tail.IsArcSegment( lastSegIdx ) )
m_direction = DIRECTION_45( tail.CSegment( -1 ) );
else
m_direction = DIRECTION_45( tail.CArcs()[ tailShapes[lastSegIdx] ] );
m_direction = DIRECTION_45( tail.CArcs()[tail.ArcIndex( lastSegIdx )] );
head.Remove( 0, -1 );
@ -1343,7 +1337,7 @@ bool LINE_PLACER::FixRoute( const VECTOR2I& aP, ITEM* aEndItem, bool aForceFinis
{
ssize_t arcIndex = l.ArcIndex( i );
if( arcIndex < 0 || ( lastArc >= 0 && i == lastV - 1 && l.CShapes()[lastV] == -1 ) )
if( arcIndex < 0 || ( lastArc >= 0 && i == lastV - 1 && !l.IsPtOnArc( lastV ) ) )
{
seg = SEGMENT( pl.CSegment( i ), m_currentNet );
seg.SetWidth( pl.Width() );

View File

@ -122,11 +122,9 @@ bool MEANDER_PLACER::doMove( const VECTOR2I& aP, ITEM* aEndItem, long long int a
m_result.SetWidth( m_originLine.Width() );
m_result.SetBaselineOffset( 0 );
const std::vector<ssize_t>& tunedShapes = tuned.CShapes();
for( int i = 0; i < tuned.SegmentCount(); i++ )
{
if( tunedShapes[i] >= 0 )
if( tuned.IsArcSegment( i ) )
continue;
const SEG s = tuned.CSegment( i );

View File

@ -583,7 +583,7 @@ void NODE::Add( LINE& aLine, bool aAllowRedundant )
for( int i = 0; i < l.SegmentCount(); i++ )
{
if( l.isArc( i ) )
if( l.IsArcSegment( i ) )
continue;
SEG s = l.CSegment( i );
@ -972,7 +972,7 @@ const LINE NODE::AssembleLine( LINKED_ITEM* aSeg, int* aOriginSegmentIndex,
int nSegs = line.PointCount();
VECTOR2I last = nSegs ? line.CPoint( -1 ) : VECTOR2I();
ssize_t lastShape = nSegs ? line.CShapes()[nSegs - 1] : -1;
ssize_t lastShape = nSegs ? line.ArcIndex( static_cast<ssize_t>( nSegs ) - 1 ) : -1;
line.Append( arcReversed[i] ? sa->Reversed() : *sa );

View File

@ -576,7 +576,6 @@ bool OPTIMIZER::mergeFull( LINE* aLine )
bool OPTIMIZER::mergeColinear( LINE* aLine )
{
SHAPE_LINE_CHAIN& line = aLine->Line();
const std::vector<ssize_t> shapes = line.CShapes();
int nSegs = line.SegmentCount();
@ -592,7 +591,7 @@ bool OPTIMIZER::mergeColinear( LINE* aLine )
if( s1.Collinear( s2 ) )
{
// We should not see a collinear vertex inside an arc
wxASSERT( shapes[segIdx + 1] < 0 );
wxASSERT( !line.IsPtOnArc( segIdx + 1 ) );
line.Remove( segIdx + 1 );
}
}
@ -696,8 +695,11 @@ bool OPTIMIZER::mergeStep( LINE* aLine, SHAPE_LINE_CHAIN& aCurrentPath, int step
for( int n = 0; n < n_segs - step; n++ )
{
// Do not attempt to merge false segments that are part of an arc
if( aCurrentPath.isArc( n ) || aCurrentPath.isArc( static_cast<std::size_t>( n ) + step ) )
if( aCurrentPath.IsArcSegment( n )
|| aCurrentPath.IsArcSegment( static_cast<std::size_t>( n ) + step ) )
{
continue;
}
const SEG s1 = aCurrentPath.CSegment( n );
const SEG s2 = aCurrentPath.CSegment( n + step );