diff --git a/libs/kimath/include/geometry/shape_line_chain.h b/libs/kimath/include/geometry/shape_line_chain.h index dc266c3d9f..68cbb9b003 100644 --- a/libs/kimath/include/geometry/shape_line_chain.h +++ b/libs/kimath/include/geometry/shape_line_chain.h @@ -628,9 +628,10 @@ public: * Function Simplify() * * Simplifies the line chain by removing colinear adjacent segments and duplicate vertices. + * @param aRemoveColinear controsl the removal of colinear adjacent segments * @return reference to self. */ - SHAPE_LINE_CHAIN& Simplify(); + SHAPE_LINE_CHAIN& Simplify( bool aRemoveColinear = true ); /** * Converts an arc to only a point chain by removing the arc and references @@ -747,7 +748,14 @@ public: bool isArc( size_t aSegment ) const { - return aSegment < m_shapes.size() && m_shapes[aSegment] != SHAPE_IS_PT; + /** + * 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] ); } virtual const VECTOR2I GetPoint( int aIndex ) const override { return CPoint(aIndex); } diff --git a/libs/kimath/src/geometry/shape_line_chain.cpp b/libs/kimath/src/geometry/shape_line_chain.cpp index f5ce62450c..fb45c43fc2 100644 --- a/libs/kimath/src/geometry/shape_line_chain.cpp +++ b/libs/kimath/src/geometry/shape_line_chain.cpp @@ -936,7 +936,7 @@ const OPT SHAPE_LINE_CHAIN::SelfIntersecting() c } -SHAPE_LINE_CHAIN& SHAPE_LINE_CHAIN::Simplify() +SHAPE_LINE_CHAIN& SHAPE_LINE_CHAIN::Simplify( bool aRemoveColinear ) { std::vector pts_unique; std::vector shapes_unique; @@ -961,11 +961,25 @@ SHAPE_LINE_CHAIN& SHAPE_LINE_CHAIN::Simplify() { int j = i + 1; - while( j < np && m_points[i] == m_points[j] && m_shapes[i] == m_shapes[j] ) + // We can eliminate duplicate vertices as long as they are part of the same shape, OR if + // 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 ) ) + { j++; + } + + int shapeToKeep = m_shapes[i]; + + if( shapeToKeep == SHAPE_IS_PT ) + shapeToKeep = m_shapes[j - 1]; + + wxASSERT( shapeToKeep < static_cast( m_arcs.size() ) ); pts_unique.push_back( CPoint( i ) ); - shapes_unique.push_back( m_shapes[i] ); + shapes_unique.push_back( shapeToKeep ); i = j; } @@ -976,17 +990,20 @@ SHAPE_LINE_CHAIN& SHAPE_LINE_CHAIN::Simplify() i = 0; - // stage 1: eliminate collinear segments + // stage 2: eliminate colinear segments while( i < np - 2 ) { const VECTOR2I p0 = pts_unique[i]; const VECTOR2I p1 = pts_unique[i + 1]; int n = i; - while( n < np - 2 - && ( SEG( p0, p1 ).LineDistance( pts_unique[n + 2] ) <= 1 - || SEG( p0, p1 ).Collinear( SEG( p1, pts_unique[n + 2] ) ) ) ) - n++; + if( aRemoveColinear ) + { + while( n < np - 2 + && ( SEG( p0, p1 ).LineDistance( pts_unique[n + 2] ) <= 1 + || SEG( p0, p1 ).Collinear( SEG( p1, pts_unique[n + 2] ) ) ) ) + n++; + } m_points.push_back( p0 ); m_shapes.push_back( shapes_unique[i] ); diff --git a/pcbnew/router/pns_line_placer.cpp b/pcbnew/router/pns_line_placer.cpp index 1f9ad4dddf..31ce47d8e9 100644 --- a/pcbnew/router/pns_line_placer.cpp +++ b/pcbnew/router/pns_line_placer.cpp @@ -1111,9 +1111,10 @@ bool LINE_PLACER::FixRoute( const VECTOR2I& aP, ITEM* aEndItem, bool aForceFinis else lastV = std::max( 1, l.SegmentCount() - 1 ); - SEGMENT seg; - SEGMENT* lastSeg = nullptr; - int lastArc = -1; + ARC arc; + SEGMENT seg; + LINKED_ITEM* lastItem = nullptr; + int lastArc = -1; for( int i = 0; i < lastV; i++ ) { @@ -1126,27 +1127,28 @@ bool LINE_PLACER::FixRoute( const VECTOR2I& aP, ITEM* aEndItem, bool aForceFinis seg.SetLayer( m_currentLayer ); if( m_lastNode->Add( std::make_unique( seg ) ) ) - lastSeg = &seg; + lastItem = &seg; } else { if( arcIndex == lastArc ) continue; - std::unique_ptr arc = std::make_unique( l.Arc( arcIndex ), m_currentNet ); - arc->SetWidth( pl.Width() ); - arc->SetLayer( m_currentLayer ); - m_lastNode->Add( std::move( arc ) ); - lastSeg = nullptr; - lastArc = arcIndex; + arc = ARC( l.Arc( arcIndex ), m_currentNet ); + arc.SetWidth( pl.Width() ); + arc.SetLayer( m_currentLayer ); + + m_lastNode->Add( std::make_unique( arc ) ); + lastItem = &arc; + lastArc = arcIndex; } } if( pl.EndsWithVia() ) m_lastNode->Add( Clone( pl.Via() ) ); - if( realEnd && lastSeg ) - simplifyNewLine( m_lastNode, lastSeg ); + if( realEnd && lastItem ) + simplifyNewLine( m_lastNode, lastItem ); if( !realEnd ) { @@ -1297,8 +1299,9 @@ void LINE_PLACER::removeLoops( NODE* aNode, LINE& aLatest ) } -void LINE_PLACER::simplifyNewLine( NODE* aNode, SEGMENT* aLatest ) +void LINE_PLACER::simplifyNewLine( NODE* aNode, LINKED_ITEM* aLatest ) { + wxASSERT( aLatest->OfKind( ITEM::SEGMENT_T | ITEM::ARC_T ) ); LINE l = aNode->AssembleLine( aLatest ); bool optimized = OPTIMIZER::Optimize( &l, OPTIMIZER::MERGE_COLINEAR, aNode ); diff --git a/pcbnew/router/pns_line_placer.h b/pcbnew/router/pns_line_placer.h index d4eaddb669..15215d9f5f 100644 --- a/pcbnew/router/pns_line_placer.h +++ b/pcbnew/router/pns_line_placer.h @@ -320,13 +320,11 @@ private: void removeLoops( NODE* aNode, LINE& aLatest ); /** - * Function simplifyNewLine() - * - * Assembles a line starting from segment aLatest, removes collinear segments + * Assembles a line starting from segment or arc aLatest, removes collinear segments * and redundant vertexes. If a simplification bhas been found, replaces the * old line with the simplified one in aNode. */ - void simplifyNewLine( NODE* aNode, SEGMENT* aLatest ); + void simplifyNewLine( NODE* aNode, LINKED_ITEM* aLatest ); /** * Function handleSelfIntersections() diff --git a/pcbnew/router/pns_node.cpp b/pcbnew/router/pns_node.cpp index 9d32f21226..09d4dac373 100644 --- a/pcbnew/router/pns_node.cpp +++ b/pcbnew/router/pns_node.cpp @@ -958,7 +958,8 @@ const LINE NODE::AssembleLine( LINKED_ITEM* aSeg, int* aOriginSegmentIndex, prev_seg = li; } - pl.Line().Simplify(); + // Remove duplicate verts, but do NOT remove colinear segments here! + pl.Line().Simplify( false ); assert( pl.SegmentCount() != 0 );