summaryrefslogtreecommitdiff
path: root/pcbnew/router/pns_line.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'pcbnew/router/pns_line.cpp')
-rw-r--r--pcbnew/router/pns_line.cpp889
1 files changed, 889 insertions, 0 deletions
diff --git a/pcbnew/router/pns_line.cpp b/pcbnew/router/pns_line.cpp
new file mode 100644
index 0000000..39d5f5c
--- /dev/null
+++ b/pcbnew/router/pns_line.cpp
@@ -0,0 +1,889 @@
+/*
+ * KiRouter - a push-and-(sometimes-)shove PCB router
+ *
+ * Copyright (C) 2013-2014 CERN
+ * Author: Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation, either version 3 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <boost/foreach.hpp>
+#include <boost/optional.hpp>
+
+#include <math/vector2d.h>
+
+#include "pns_line.h"
+#include "pns_node.h"
+#include "pns_via.h"
+#include "pns_utils.h"
+#include "pns_router.h"
+
+#include <geometry/shape_rect.h>
+
+using boost::optional;
+
+PNS_LINE::PNS_LINE( const PNS_LINE& aOther ) :
+ PNS_ITEM( aOther ),
+ m_line( aOther.m_line ),
+ m_width( aOther.m_width )
+{
+ m_net = aOther.m_net;
+ m_movable = aOther.m_movable;
+ m_layers = aOther.m_layers;
+ m_via = aOther.m_via;
+ m_hasVia = aOther.m_hasVia;
+ m_marker = aOther.m_marker;
+ m_rank = aOther.m_rank;
+
+ copyLinks( &aOther );
+}
+
+
+PNS_LINE::~PNS_LINE()
+{
+ delete m_segmentRefs;
+}
+
+
+const PNS_LINE& PNS_LINE::operator=( const PNS_LINE& aOther )
+{
+ m_line = aOther.m_line;
+ m_width = aOther.m_width;
+ m_net = aOther.m_net;
+ m_movable = aOther.m_movable;
+ m_layers = aOther.m_layers;
+ m_via = aOther.m_via;
+ m_hasVia = aOther.m_hasVia;
+ m_marker = aOther.m_marker;
+ m_rank = aOther.m_rank;
+
+ copyLinks( &aOther );
+
+ return *this;
+}
+
+
+PNS_LINE* PNS_LINE::Clone() const
+{
+ PNS_LINE* l = new PNS_LINE( *this );
+
+ return l;
+}
+
+
+void PNS_LINE::Mark( int aMarker )
+{
+ m_marker = aMarker;
+
+ if( m_segmentRefs )
+ {
+ BOOST_FOREACH( PNS_SEGMENT* s, *m_segmentRefs )
+ s->Mark( aMarker );
+ }
+}
+
+
+void PNS_LINE::Unmark()
+{
+ if( m_segmentRefs )
+ {
+ BOOST_FOREACH( PNS_SEGMENT* s, *m_segmentRefs )
+ s->Unmark();
+ }
+
+ m_marker = 0;
+}
+
+
+int PNS_LINE::Marker() const
+{
+ int marker = m_marker;
+
+ if( m_segmentRefs )
+ {
+ BOOST_FOREACH( PNS_SEGMENT* s, *m_segmentRefs )
+ {
+ marker |= s->Marker();
+ }
+ }
+
+ return marker;
+}
+
+
+void PNS_LINE::copyLinks( const PNS_LINE* aParent )
+{
+ if( aParent->m_segmentRefs == NULL )
+ {
+ m_segmentRefs = NULL;
+ return;
+ }
+
+ m_segmentRefs = new SEGMENT_REFS();
+ *m_segmentRefs = *aParent->m_segmentRefs;
+}
+
+
+PNS_SEGMENT* PNS_SEGMENT::Clone() const
+{
+ PNS_SEGMENT* s = new PNS_SEGMENT;
+
+ s->m_seg = m_seg;
+ s->m_net = m_net;
+ s->m_layers = m_layers;
+ s->m_marker = m_marker;
+ s->m_rank = m_rank;
+
+ return s;
+}
+
+
+int PNS_LINE::CountCorners( int aAngles )
+{
+ int count = 0;
+
+ for( int i = 0; i < m_line.SegmentCount() - 1; i++ )
+ {
+ const SEG seg1 = m_line.CSegment( i );
+ const SEG seg2 = m_line.CSegment( i + 1 );
+
+ const DIRECTION_45 dir1( seg1 );
+ const DIRECTION_45 dir2( seg2 );
+
+ DIRECTION_45::AngleType a = dir1.Angle( dir2 );
+
+ if( a & aAngles )
+ count++;
+ }
+
+ return count;
+}
+
+
+bool PNS_LINE::Walkaround( SHAPE_LINE_CHAIN aObstacle, SHAPE_LINE_CHAIN& aPre,
+ SHAPE_LINE_CHAIN& aWalk, SHAPE_LINE_CHAIN& aPost, bool aCw ) const
+{
+ const SHAPE_LINE_CHAIN& line( CLine() );
+ VECTOR2I ip_start;
+ VECTOR2I ip_end;
+
+ if( line.SegmentCount() < 1 )
+ return false;
+
+ if( aObstacle.PointInside( line.CPoint( 0 ) ) || aObstacle.PointInside( line.CPoint( -1 ) ) )
+ return false;
+
+ SHAPE_LINE_CHAIN::INTERSECTIONS ips, ips2;
+
+ line.Intersect( aObstacle, ips );
+
+ aWalk.Clear();
+ aPost.Clear();
+
+ int nearest_dist = INT_MAX;
+ int farthest_dist = 0;
+
+ SHAPE_LINE_CHAIN::INTERSECTION nearest, farthest;
+
+ for( int i = 0; i < (int) ips.size(); i++ )
+ {
+ const VECTOR2I p = ips[i].p;
+ int dist = line.PathLength( p );
+
+ if( dist < 0 )
+ return false;
+
+ if( dist <= nearest_dist )
+ {
+ nearest_dist = dist;
+ nearest = ips[i];
+ }
+
+ if( dist >= farthest_dist )
+ {
+ farthest_dist = dist;
+ farthest = ips[i];
+ }
+ }
+
+ if( ips.size() <= 1 || nearest.p == farthest.p )
+ {
+ aPre = line;
+ return true;
+ }
+
+ aPre = line.Slice( 0, nearest.our.Index() );
+ aPre.Append( nearest.p );
+ aPre.Simplify();
+
+ aWalk.Clear();
+ aWalk.SetClosed( false );
+ aWalk.Append( nearest.p );
+
+ assert( nearest.their.Index() >= 0 );
+ assert( farthest.their.Index() >= 0 );
+
+ assert( nearest_dist <= farthest_dist );
+
+ aObstacle.Split( nearest.p );
+ aObstacle.Split( farthest.p );
+
+ int i_first = aObstacle.Find( nearest.p );
+ int i_last = aObstacle.Find( farthest.p );
+
+ int i = i_first;
+
+ while( i != i_last )
+ {
+ aWalk.Append( aObstacle.CPoint( i ) );
+ i += ( aCw ? 1 : -1 );
+
+ if( i < 0 )
+ i = aObstacle.PointCount() - 1;
+ else if( i == aObstacle.PointCount() )
+ i = 0;
+ }
+
+ aWalk.Append( farthest.p );
+ aWalk.Simplify();
+
+ aPost.Clear();
+ aPost.Append( farthest.p );
+ aPost.Append( line.Slice( farthest.our.Index() + 1, -1 ) );
+ aPost.Simplify();
+
+ return true;
+}
+
+
+void PNS_LINE::Walkaround( const SHAPE_LINE_CHAIN& aObstacle,
+ SHAPE_LINE_CHAIN& aPath,
+ bool aCw ) const
+{
+ SHAPE_LINE_CHAIN walk, post;
+
+ Walkaround( aObstacle, aPath, walk, post, aCw );
+ aPath.Append( walk );
+ aPath.Append( post );
+ aPath.Simplify();
+}
+
+
+const SHAPE_LINE_CHAIN PNS_SEGMENT::Hull( int aClearance, int aWalkaroundThickness ) const
+{
+ return SegmentHull ( m_seg, aClearance, aWalkaroundThickness );
+}
+
+
+bool PNS_LINE::Is45Degree()
+{
+ for( int i = 0; i < m_line.SegmentCount(); i++ )
+ {
+ const SEG& s = m_line.CSegment( i );
+
+ if( s.Length() < 10 )
+ continue;
+
+ double angle = 180.0 / M_PI *
+ atan2( (double) s.B.y - (double) s.A.y,
+ (double) s.B.x - (double) s.A.x );
+
+ if( angle < 0 )
+ angle += 360.0;
+
+ double angle_a = fabs( fmod( angle, 45.0 ) );
+
+ if( angle_a > 1.0 && angle_a < 44.0 )
+ return false;
+ }
+
+ return true;
+}
+
+
+const PNS_LINE PNS_LINE::ClipToNearestObstacle( PNS_NODE* aNode ) const
+{
+ const int IterationLimit = 5;
+ int i;
+ PNS_LINE l( *this );
+
+ for( i = 0; i < IterationLimit; i++ )
+ {
+ PNS_NODE::OPT_OBSTACLE obs = aNode->NearestObstacle( &l );
+
+ if( obs )
+ {
+ l.RemoveVia();
+ int p = l.Line().Split( obs->m_ipFirst );
+ l.Line().Remove( p + 1, -1 );
+ } else
+ break;
+ }
+
+ if( i == IterationLimit )
+ l.Line().Clear();
+
+ return l;
+}
+
+
+void PNS_LINE::ShowLinks()
+{
+ if( !m_segmentRefs )
+ {
+ printf( "line %p: no links\n", this );
+ return;
+ }
+
+ printf( "line %p: %d linked segs\n", this, (int) m_segmentRefs->size() );
+
+ for( int i = 0; i < (int) m_segmentRefs->size(); i++ )
+ printf( "seg %d: %p\n", i, (*m_segmentRefs)[i] );
+}
+
+SHAPE_LINE_CHAIN dragCornerInternal( const SHAPE_LINE_CHAIN& aOrigin, const VECTOR2I& aP )
+{
+ optional<SHAPE_LINE_CHAIN> picked;
+ int i;
+ int d = 2;
+
+ if( aOrigin.SegmentCount() == 1)
+ {
+ DIRECTION_45 dir( aOrigin.CPoint( 0 ) - aOrigin.CPoint( 1 ) );
+
+ return DIRECTION_45().BuildInitialTrace( aOrigin.CPoint( 0 ), aP, dir.IsDiagonal() );
+ }
+
+ if( aOrigin.CSegment( -1 ).Length() > 100000 * 30 ) // fixme: constant/parameter?
+ d = 1;
+
+ for( i = aOrigin.SegmentCount() - d; i >= 0; i-- )
+ {
+ DIRECTION_45 d_start ( aOrigin.CSegment( i ) );
+ VECTOR2I p_start = aOrigin.CPoint( i );
+ SHAPE_LINE_CHAIN paths[2];
+ DIRECTION_45 dirs[2];
+ DIRECTION_45 d_prev = ( i > 0 ? DIRECTION_45( aOrigin.CSegment( i - 1 ) ) : DIRECTION_45() );
+
+ for( int j = 0; j < 2; j++ )
+ {
+ paths[j] = d_start.BuildInitialTrace( p_start, aP, j );
+ dirs[j] = DIRECTION_45( paths[j].CSegment( 0 ) );
+ }
+
+ for( int j = 0; j < 2; j++ )
+ {
+ if( dirs[j] == d_start )
+ {
+ picked = paths[j];
+ break;
+ }
+ }
+
+ if( picked )
+ break;
+
+ for( int j = 0; j < 2; j++ )
+ {
+ if( dirs[j].IsObtuse( d_prev ) )
+ {
+ picked = paths[j];
+ break;
+ }
+ }
+
+ if( picked )
+ break;
+ }
+
+ if( picked )
+ {
+ SHAPE_LINE_CHAIN path = aOrigin.Slice( 0, i );
+ path.Append( *picked );
+
+ return path;
+ }
+
+ DIRECTION_45 dir( aOrigin.CPoint( -1 ) - aOrigin.CPoint( -2 ) );
+
+ return DIRECTION_45().BuildInitialTrace( aOrigin.CPoint( 0 ), aP, dir.IsDiagonal() );
+}
+
+
+void PNS_LINE::DragCorner ( const VECTOR2I& aP, int aIndex, int aSnappingThreshold )
+{
+ SHAPE_LINE_CHAIN path;
+
+ VECTOR2I snapped = snapDraggedCorner( m_line, aP, aIndex, aSnappingThreshold );
+
+ if( aIndex == 0 )
+ path = dragCornerInternal( m_line.Reverse(), snapped ).Reverse();
+ else if( aIndex == m_line.SegmentCount() )
+ path = dragCornerInternal( m_line, snapped );
+ else
+ {
+ // fixme: awkward behaviour for "outwards" drags
+ path = dragCornerInternal( m_line.Slice( 0, aIndex ), snapped );
+ SHAPE_LINE_CHAIN path_rev = dragCornerInternal( m_line.Slice( aIndex, -1 ).Reverse(),
+ snapped ).Reverse();
+ path.Append( path_rev );
+ }
+
+ path.Simplify();
+ m_line = path;
+}
+
+
+VECTOR2I PNS_LINE::snapDraggedCorner( const SHAPE_LINE_CHAIN& aPath, const VECTOR2I& aP,
+ int aIndex, int aThreshold ) const
+{
+ int s_start = std::max( aIndex - 2, 0 );
+ int s_end = std::min( aIndex + 2, aPath.SegmentCount() - 1 );
+
+ int i, j;
+ int best_dist = INT_MAX;
+ VECTOR2I best_snap = aP;
+
+ if( aThreshold <= 0 )
+ return aP;
+
+ for( i = s_start; i <= s_end; i++ )
+ {
+ const SEG& a = aPath.CSegment( i );
+
+ for( j = s_start; j < i; j++ )
+ {
+ const SEG& b = aPath.CSegment( j );
+
+ if( !( DIRECTION_45( a ).IsObtuse(DIRECTION_45( b ) ) ) )
+ continue;
+
+ OPT_VECTOR2I ip = a.IntersectLines(b);
+
+ if( ip )
+ {
+ int dist = ( *ip - aP ).EuclideanNorm();
+
+ if( dist < aThreshold && dist < best_dist )
+ {
+ best_dist = dist;
+ best_snap = *ip;
+ }
+ }
+ }
+ }
+
+ return best_snap;
+}
+
+VECTOR2I PNS_LINE::snapToNeighbourSegments( const SHAPE_LINE_CHAIN& aPath, const VECTOR2I &aP,
+ int aIndex, int aThreshold ) const
+{
+ VECTOR2I snap_p[2];
+ DIRECTION_45 dragDir( aPath.CSegment( aIndex ) );
+ int snap_d[2] = { -1, -1 };
+
+ if( aThreshold == 0 )
+ return aP;
+
+ if( aIndex >= 2 )
+ {
+ SEG s = aPath.CSegment( aIndex - 2 );
+
+ if( DIRECTION_45( s ) == dragDir )
+ snap_d[0] = s.LineDistance( aP );
+
+ snap_p[0] = s.A;
+ }
+
+ if( aIndex < aPath.SegmentCount() - 2 )
+ {
+ SEG s = aPath.CSegment( aIndex + 2 );
+
+ if( DIRECTION_45( s ) == dragDir )
+ snap_d[1] = s.LineDistance(aP);
+
+ snap_p[1] = s.A;
+ }
+
+ VECTOR2I best = aP;
+ int minDist = INT_MAX;
+
+ for( int i = 0; i < 2; i++ )
+ {
+ if( snap_d[i] >= 0 && snap_d[i] < minDist && snap_d[i] <= aThreshold )
+ {
+ minDist = snap_d[i];
+ best = snap_p[i];
+ }
+ }
+
+ return best;
+}
+
+
+void PNS_LINE::DragSegment ( const VECTOR2I& aP, int aIndex, int aSnappingThreshold )
+{
+ SHAPE_LINE_CHAIN path( m_line );
+ VECTOR2I target( aP );
+
+ SEG guideA[2], guideB[2];
+ int index = aIndex;
+
+ target = snapToNeighbourSegments( path, aP, aIndex, aSnappingThreshold );
+
+ if( index == 0 )
+ {
+ path.Insert( 0, path.CPoint( 0 ) );
+ index++;
+ }
+
+ if( index == path.SegmentCount() - 1 )
+ {
+ path.Insert( path.PointCount() - 1, path.CPoint( -1 ) );
+ }
+
+ SEG dragged = path.CSegment( index );
+ DIRECTION_45 drag_dir( dragged );
+
+ SEG s_prev = path.CSegment( index - 1 );
+ SEG s_next = path.CSegment( index + 1 );
+
+ DIRECTION_45 dir_prev( s_prev );
+ DIRECTION_45 dir_next( s_next );
+
+ if( dir_prev == drag_dir )
+ {
+ dir_prev = dir_prev.Left();
+ path.Insert( index, path.CPoint( index ) );
+ index++;
+ }
+
+ if( dir_next == drag_dir )
+ {
+ dir_next = dir_next.Right();
+ path.Insert( index + 1, path.CPoint( index + 1 ) );
+ }
+
+ s_prev = path.CSegment( index - 1 );
+ s_next = path.CSegment( index + 1 );
+ dragged = path.CSegment( index );
+
+ bool lockEndpointA = true;
+ bool lockEndpointB = true;
+
+ if( aIndex == 0 )
+ {
+ if( !lockEndpointA )
+ guideA[0] = guideA[1] = SEG( dragged.A, dragged.A + drag_dir.Right().Right().ToVector() );
+ else
+ {
+ guideA[0] = SEG( dragged.A, dragged.A + drag_dir.Right().ToVector() );
+ guideA[1] = SEG( dragged.A, dragged.A + drag_dir.Left().ToVector() );
+ }
+ }
+ else
+ {
+ if( dir_prev.IsObtuse(drag_dir ) )
+ {
+ guideA[0] = SEG( s_prev.A, s_prev.A + drag_dir.Left().ToVector() );
+ guideA[1] = SEG( s_prev.A, s_prev.A + drag_dir.Right().ToVector() );
+ }
+ else
+ guideA[0] = guideA[1] = SEG( dragged.A, dragged.A + dir_prev.ToVector() );
+ }
+
+ if( aIndex == m_line.SegmentCount() - 1 )
+ {
+ if( !lockEndpointB )
+ guideB[0] = guideB[1] = SEG( dragged.B, dragged.B + drag_dir.Right().Right().ToVector() );
+ else
+ {
+ guideB[0] = SEG( dragged.B, dragged.B + drag_dir.Right().ToVector() );
+ guideB[1] = SEG( dragged.B, dragged.B + drag_dir.Left().ToVector() );
+ }
+ }
+ else
+ {
+ if( dir_next.IsObtuse( drag_dir ) )
+ {
+ guideB[0] = SEG( s_next.B, s_next.B + drag_dir.Left().ToVector() );
+ guideB[1] = SEG( s_next.B, s_next.B + drag_dir.Right().ToVector() );
+ }
+ else
+ guideB[0] = guideB[1] = SEG( dragged.B, dragged.B + dir_next.ToVector() );
+ }
+
+ SEG s_current( target, target + drag_dir.ToVector() );
+
+ int best_len = INT_MAX;
+ SHAPE_LINE_CHAIN best;
+
+ for( int i = 0; i < 2; i++ )
+ {
+ for( int j = 0; j < 2; j++ )
+ {
+ OPT_VECTOR2I ip1 = s_current.IntersectLines( guideA[i] );
+ OPT_VECTOR2I ip2 = s_current.IntersectLines( guideB[j] );
+
+ SHAPE_LINE_CHAIN np;
+
+ if( !ip1 || !ip2 )
+ continue;
+
+ SEG s1( s_prev.A, *ip1 );
+ SEG s2( *ip1, *ip2 );
+ SEG s3( *ip2, s_next.B );
+
+ OPT_VECTOR2I ip;
+
+ if( (ip = s1.Intersect( s_next )) )
+ {
+ np.Append ( s1.A );
+ np.Append ( *ip );
+ np.Append ( s_next.B );
+ }
+ else if( (ip = s3.Intersect( s_prev )) )
+ {
+ np.Append ( s_prev.A );
+ np.Append ( *ip );
+ np.Append ( s3.B );
+ }
+ else if( (ip = s1.Intersect( s3 )) )
+ {
+ np.Append( s_prev.A );
+ np.Append( *ip );
+ np.Append( s_next.B );
+ }
+ else
+ {
+ np.Append( s_prev.A );
+ np.Append( *ip1 );
+ np.Append( *ip2 );
+ np.Append( s_next.B );
+ }
+
+ if( np.Length() < best_len )
+ {
+ best_len = np.Length();
+ best = np;
+ }
+ }
+ }
+
+ if( !lockEndpointA && aIndex == 0 )
+ best.Remove( 0, 0 );
+ if( !lockEndpointB && aIndex == m_line.SegmentCount() - 1 )
+ best.Remove( -1, -1 );
+
+ if( m_line.PointCount() == 1 )
+ m_line = best;
+ else if( aIndex == 0 )
+ m_line.Replace( 0, 1, best );
+ else if( aIndex == m_line.SegmentCount() - 1 )
+ m_line.Replace( -2, -1, best );
+ else
+ m_line.Replace( aIndex, aIndex + 1, best );
+
+ m_line.Simplify();
+}
+
+
+bool PNS_LINE::CompareGeometry( const PNS_LINE& aOther )
+{
+ return m_line.CompareGeometry( aOther.m_line );
+}
+
+
+void PNS_LINE::Reverse()
+{
+ m_line = m_line.Reverse();
+
+ if( m_segmentRefs )
+ std::reverse( m_segmentRefs->begin(), m_segmentRefs->end() );
+}
+
+
+void PNS_LINE::AppendVia( const PNS_VIA& aVia )
+{
+ if( m_line.PointCount() > 1 && aVia.Pos() == m_line.CPoint( 0 ) )
+ {
+ Reverse();
+ }
+
+ m_hasVia = true;
+ m_via = aVia;
+ m_via.SetNet( m_net );
+}
+
+
+void PNS_LINE::SetRank( int aRank )
+{
+ m_rank = aRank;
+
+ if( m_segmentRefs )
+ {
+ BOOST_FOREACH( PNS_SEGMENT* s, *m_segmentRefs )
+ s->SetRank( aRank );
+ }
+}
+
+
+int PNS_LINE::Rank() const
+{
+ int min_rank = INT_MAX;
+ int rank;
+
+ if( m_segmentRefs )
+ {
+ BOOST_FOREACH( PNS_SEGMENT *s, *m_segmentRefs )
+ min_rank = std::min( min_rank, s->Rank() );
+ rank = ( min_rank == INT_MAX ) ? -1 : min_rank;
+ }
+ else
+ {
+ rank = m_rank;
+ }
+
+ return rank;
+}
+
+
+void PNS_LINE::ClipVertexRange( int aStart, int aEnd )
+{
+ m_line = m_line.Slice( aStart, aEnd );
+
+ if( m_segmentRefs )
+ {
+ SEGMENT_REFS* snew = new SEGMENT_REFS( m_segmentRefs->begin() + aStart,
+ m_segmentRefs->begin() + aEnd );
+
+ delete m_segmentRefs;
+ m_segmentRefs = snew;
+ }
+}
+
+
+bool PNS_LINE::HasLoops() const
+{
+ for( int i = 0; i < PointCount(); i++ )
+ {
+ for( int j = 0; j < PointCount(); j++ )
+ {
+ if( ( std::abs( i - j ) > 1 ) && CPoint( i ) == CPoint( j ) )
+ return true;
+ }
+ }
+
+ return false;
+}
+
+
+void PNS_LINE::ClearSegmentLinks()
+{
+ if( m_segmentRefs )
+ delete m_segmentRefs;
+
+ m_segmentRefs = NULL;
+}
+
+
+static void extendBox( BOX2I& aBox, bool& aDefined, const VECTOR2I& aP )
+{
+ if( aDefined )
+ aBox.Merge ( aP );
+ else {
+ aBox = BOX2I( aP, VECTOR2I( 0, 0 ) );
+ aDefined = true;
+ }
+}
+
+
+OPT_BOX2I PNS_LINE::ChangedArea( const PNS_LINE* aOther ) const
+{
+ BOX2I area;
+ bool areaDefined = false;
+
+ int i_start = -1;
+ int i_end_self = -1, i_end_other = -1;
+
+ SHAPE_LINE_CHAIN self( m_line );
+ self.Simplify();
+ SHAPE_LINE_CHAIN other( aOther->m_line );
+ other.Simplify();
+
+ int np_self = self.PointCount();
+ int np_other = other.PointCount();
+
+ int n = std::min( np_self, np_other );
+
+ for( int i = 0; i < n; i++ )
+ {
+ const VECTOR2I p1 = self.CPoint( i );
+ const VECTOR2I p2 = other.CPoint( i );
+
+ if( p1 != p2 )
+ {
+ if( i != n - 1 )
+ {
+ SEG s = self.CSegment( i );
+
+ if( !s.Contains( p2 ) )
+ {
+ i_start = i;
+ break;
+ }
+ } else {
+ i_start = i;
+ break;
+ }
+ }
+ }
+
+ for( int i = 0; i < n; i++ )
+ {
+ const VECTOR2I p1 = self.CPoint( np_self - 1 - i );
+ const VECTOR2I p2 = other.CPoint( np_other - 1 - i );
+
+ if( p1 != p2 )
+ {
+ i_end_self = np_self - 1 - i;
+ i_end_other = np_other - 1 - i;
+ break;
+ }
+ }
+
+ if( i_start < 0 )
+ i_start = n;
+
+ if( i_end_self < 0 )
+ i_end_self = np_self - 1;
+
+ if( i_end_other < 0 )
+ i_end_other = np_other - 1;
+
+ for( int i = i_start; i <= i_end_self; i++ )
+ extendBox( area, areaDefined, self.CPoint( i ) );
+
+ for( int i = i_start; i <= i_end_other; i++ )
+ extendBox( area, areaDefined, other.CPoint( i ) );
+
+ if( areaDefined )
+ {
+ area.Inflate( std::max( Width(), aOther->Width() ) );
+ return area;
+ }
+
+ return OPT_BOX2I();
+}