const SHAPE_LINE_CHAIN SegmentHull ( const SHAPE_SEGMENT& aSeg, int aClearance, int aWalkaroundThickness ) { int d = aSeg.GetWidth() / 2 + aClearance + aWalkaroundThickness / 2 + HULL_MARGIN; int x = (int)( 2.0 / ( 1.0 + M_SQRT2 ) * d ); const VECTOR2I a = aSeg.GetSeg().A; const VECTOR2I b = aSeg.GetSeg().B; VECTOR2I dir = b - a; VECTOR2I p0 = dir.Perpendicular().Resize( d ); VECTOR2I ds = dir.Perpendicular().Resize( x / 2 ); VECTOR2I pd = dir.Resize( x / 2 ); VECTOR2I dp = dir.Resize( d ); SHAPE_LINE_CHAIN s; s.SetClosed( true ); s.Append( b + p0 + pd ); s.Append( b + dp + ds ); s.Append( b + dp - ds ); s.Append( b - p0 + pd ); s.Append( a - p0 - pd ); s.Append( a - dp - ds ); s.Append( a - dp + ds ); s.Append( a + p0 - pd ); // make sure the hull outline is always clockwise if( s.CSegment( 0 ).Side( a ) < 0 ) return s.Reverse(); else return s; }
SHAPE_LINE_CHAIN PNS_MEANDER_SHAPE::circleQuad( VECTOR2D aP, VECTOR2D aDir, bool aSide ) { SHAPE_LINE_CHAIN lc; if( aDir.EuclideanNorm( ) == 0.0f ) { lc.Append( aP ); return lc; } VECTOR2D dir_u( aDir ); VECTOR2D dir_v( aDir.Perpendicular( ) ); const int ArcSegments = Settings().m_cornerArcSegments; for( int i = ArcSegments - 1; i >= 0; i-- ) { VECTOR2D p; double alpha = (double) i / (double) ( ArcSegments - 1 ) * M_PI / 2.0; p = aP + dir_u * cos( alpha ) + dir_v * ( aSide ? -1.0 : 1.0 ) * ( 1.0 - sin( alpha ) ); lc.Append( ( int ) p.x, ( int ) p.y ); } return lc; }
static void drawGw( VECTOR2I p, int color ) { SHAPE_LINE_CHAIN l; l.Append( p - VECTOR2I( -50000, -50000 ) ); l.Append( p + VECTOR2I( -50000, -50000 ) ); l.Clear(); l.Append( p - VECTOR2I( 50000, -50000 ) ); l.Append( p + VECTOR2I( 50000, -50000 ) ); }
void DrawDebugBox( BOX2I aB, int aColor ) { SHAPE_LINE_CHAIN l; VECTOR2I o = aB.GetOrigin(); VECTOR2I s = aB.GetSize(); l.Append( o ); l.Append( o.x + s.x, o.y ); l.Append( o.x + s.x, o.y + s.y ); l.Append( o.x, o.y + s.y ); l.Append( o ); ROUTER::GetInstance()->DisplayDebugLine( l, aColor, 10000 ); }
void DrawDebugPoint( VECTOR2I aP, int aColor ) { SHAPE_LINE_CHAIN l; l.Append( aP - VECTOR2I( -50000, -50000 ) ); l.Append( aP + VECTOR2I( -50000, -50000 ) ); ROUTER::GetInstance()->DisplayDebugLine ( l, aColor, 10000 ); l.Clear(); l.Append( aP - VECTOR2I( 50000, -50000 ) ); l.Append( aP + VECTOR2I( 50000, -50000 ) ); ROUTER::GetInstance()->DisplayDebugLine( l, aColor, 10000 ); }
void AddPoint( VECTOR2I aP, int aColor ) { SHAPE_LINE_CHAIN l; l.Append( aP - VECTOR2I( -50000, -50000 ) ); l.Append( aP + VECTOR2I( -50000, -50000 ) ); AddLine ( l, aColor, 10000 ); l.Clear(); l.Append( aP - VECTOR2I( 50000, -50000 ) ); l.Append( aP + VECTOR2I( 50000, -50000 ) ); AddLine( l, aColor, 10000 ); }
void AddBox( BOX2I aB, int aColor ) { SHAPE_LINE_CHAIN l; VECTOR2I o = aB.GetOrigin(); VECTOR2I s = aB.GetSize(); l.Append( o ); l.Append( o.x + s.x, o.y ); l.Append( o.x + s.x, o.y + s.y ); l.Append( o.x, o.y + s.y ); l.Append( o ); AddLine( l, aColor, 10000 ); }
const SHAPE_LINE_CHAIN SHAPE_ARC::ConvertToPolyline( double aAccuracy ) const { SHAPE_LINE_CHAIN rv; double r = GetRadius(); double sa = GetStartAngle(); auto c = GetCenter(); int n; if( r == 0.0 ) { n = 0; } else { n = GetArcToSegmentCount( r, From_User_Unit( MILLIMETRES, aAccuracy ), m_centralAngle ); } for( int i = 0; i <= n ; i++ ) { double a = sa + m_centralAngle * (double) i / (double) n; double x = c.x + r * cos( a * M_PI / 180.0 ); double y = c.y + r * sin( a * M_PI / 180.0 ); rv.Append( (int) x, (int) y ); } return rv; }
SHAPE_LINE_CHAIN dragCornerInternal( const SHAPE_LINE_CHAIN& aOrigin, const VECTOR2I& aP ) { optional<SHAPE_LINE_CHAIN> picked; int i; int d = 2; 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; } return DIRECTION_45().BuildInitialTrace( aOrigin.CPoint( 0 ), aP, true ); }
SHAPE_LINE_CHAIN PNS_MEANDER_SHAPE::circleQuad( VECTOR2D aP, VECTOR2D aDir, bool aSide ) { SHAPE_LINE_CHAIN lc; if( aDir.EuclideanNorm( ) == 0.0f ) { lc.Append( aP ); return lc; } VECTOR2D dir_u( aDir ); VECTOR2D dir_v( aDir.Perpendicular( ) ); const int ArcSegments = Settings().m_cornerArcSegments; double radius = (double) aDir.EuclideanNorm(); double angleStep = M_PI / 2.0 / (double) ArcSegments; double correction = 12.0 * radius * ( 1.0 - cos( angleStep / 2.0 ) ); if( !m_dual ) correction = 0.0; else if( radius < m_meanCornerRadius ) correction = 0.0; VECTOR2D p = aP; lc.Append( ( int ) p.x, ( int ) p.y ); VECTOR2D dir_uu = dir_u.Resize( radius - correction ); VECTOR2D dir_vv = dir_v.Resize( radius - correction ); VECTOR2D shift = dir_u.Resize( correction ); for( int i = ArcSegments - 1; i >= 0; i-- ) { double alpha = (double) i / (double) ( ArcSegments - 1 ) * M_PI / 2.0; p = aP + shift + dir_uu * cos( alpha ) + dir_vv * ( aSide ? -1.0 : 1.0 ) * ( 1.0 - sin( alpha ) ); lc.Append( ( int ) p.x, ( int ) p.y ); } p = aP + dir_u + dir_v * ( aSide ? -1.0 : 1.0 ); lc.Append( ( int ) p.x, ( int ) p.y ); return lc; }
void DrawDebugSeg( SEG aS, int aColor ) { SHAPE_LINE_CHAIN l; l.Append( aS.A ); l.Append( aS.B ); ROUTER::GetInstance()->DisplayDebugLine( l, aColor, 10000 ); }
void AddSegment( SEG aS, int aColor ) { SHAPE_LINE_CHAIN l; l.Append( aS.A ); l.Append( aS.B ); AddLine( l, aColor, 10000 ); }
const SHAPE_LINE_CHAIN SHAPE_POLY_SET::convertFromClipper( const Path& aPath ) { SHAPE_LINE_CHAIN lc; for( unsigned int i = 0; i < aPath.size(); i++ ) lc.Append( aPath[i].X, aPath[i].Y ); return lc; }
const SHAPE_LINE_CHAIN OctagonalHull( const VECTOR2I& aP0, const VECTOR2I& aSize, int aClearance, int aChamfer ) { SHAPE_LINE_CHAIN s; s.SetClosed( true ); s.Append( aP0.x - aClearance, aP0.y - aClearance + aChamfer ); s.Append( aP0.x - aClearance + aChamfer, aP0.y - aClearance ); s.Append( aP0.x + aSize.x + aClearance - aChamfer, aP0.y - aClearance ); s.Append( aP0.x + aSize.x + aClearance, aP0.y - aClearance + aChamfer ); s.Append( aP0.x + aSize.x + aClearance, aP0.y + aSize.y + aClearance - aChamfer ); s.Append( aP0.x + aSize.x + aClearance - aChamfer, aP0.y + aSize.y + aClearance ); s.Append( aP0.x - aClearance + aChamfer, aP0.y + aSize.y + aClearance ); s.Append( aP0.x - aClearance, aP0.y + aSize.y + aClearance - aChamfer ); return s; }
const SHAPE_LINE_CHAIN ConvexHull( const SHAPE_CONVEX& aConvex, int aClearance ) { // this defines the horizontal and vertical lines in the hull octagon BOX2I box = aConvex.BBox( aClearance + HULL_MARGIN ); box.Normalize(); SEG topline = SEG( VECTOR2I( box.GetX(), box.GetY() + box.GetHeight() ), VECTOR2I( box.GetX() + box.GetWidth(), box.GetY() + box.GetHeight() ) ); SEG rightline = SEG( VECTOR2I( box.GetX() + box.GetWidth(), box.GetY() + box.GetHeight() ), VECTOR2I( box.GetX() + box.GetWidth(), box.GetY() ) ); SEG bottomline = SEG( VECTOR2I( box.GetX() + box.GetWidth(), box.GetY() ), box.GetOrigin() ); SEG leftline = SEG( box.GetOrigin(), VECTOR2I( box.GetX(), box.GetY() + box.GetHeight() ) ); const SHAPE_LINE_CHAIN& vertices = aConvex.Vertices(); // top right diagonal VECTOR2I corner = box.GetOrigin() + box.GetSize(); SEG toprightline = SEG( corner, corner + VECTOR2I( box.GetHeight(), -box.GetHeight() ) ); MoveDiagonal( toprightline, vertices, aClearance ); // bottom right diagonal corner = box.GetOrigin() + VECTOR2I( box.GetWidth(), 0 ); SEG bottomrightline = SEG( corner + VECTOR2I( box.GetHeight(), box.GetHeight() ), corner ); MoveDiagonal( bottomrightline, vertices, aClearance ); // bottom left diagonal corner = box.GetOrigin(); SEG bottomleftline = SEG( corner, corner + VECTOR2I( -box.GetHeight(), box.GetHeight() ) ); MoveDiagonal( bottomleftline, vertices, aClearance ); // top left diagonal corner = box.GetOrigin() + VECTOR2I( 0, box.GetHeight() ); SEG topleftline = SEG( corner + VECTOR2I( -box.GetHeight(), -box.GetHeight() ), corner ); MoveDiagonal( topleftline, vertices, aClearance ); SHAPE_LINE_CHAIN octagon; octagon.SetClosed( true ); octagon.Append( *leftline.IntersectLines( bottomleftline ) ); octagon.Append( *bottomline.IntersectLines( bottomleftline ) ); octagon.Append( *bottomline.IntersectLines( bottomrightline ) ); octagon.Append( *rightline.IntersectLines( bottomrightline ) ); octagon.Append( *rightline.IntersectLines( toprightline ) ); octagon.Append( *topline.IntersectLines( toprightline ) ); octagon.Append( *topline.IntersectLines( topleftline ) ); octagon.Append( *leftline.IntersectLines( topleftline ) ); return octagon; }
bool SHAPE_POLY_SET::Parse( std::stringstream& aStream ) { std::string tmp; aStream >> tmp; if( tmp != "polyset" ) return false; aStream >> tmp; int n_polys = atoi( tmp.c_str() ); if( n_polys < 0 ) return false; for( int i = 0; i < n_polys; i++ ) { POLYGON paths; aStream >> tmp; if( tmp != "poly" ) return false; aStream >> tmp; int n_outlines = atoi( tmp.c_str() ); if( n_outlines < 0 ) return false; for( int j = 0; j < n_outlines; j++ ) { SHAPE_LINE_CHAIN outline; outline.SetClosed( true ); aStream >> tmp; int n_vertices = atoi( tmp.c_str() ); for( int v = 0; v < n_vertices; v++ ) { VECTOR2I p; aStream >> tmp; p.x = atoi( tmp.c_str() ); aStream >> tmp; p.y = atoi( tmp.c_str() ); outline.Append( p ); } paths.push_back( outline ); } m_polys.push_back( paths ); } 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(); }
bool PNS_TOPOLOGY::LeadingRatLine( const PNS_LINE* aTrack, SHAPE_LINE_CHAIN& aRatLine ) { PNS_LINE track( *aTrack ); VECTOR2I end; if( !track.PointCount() ) return false; std::unique_ptr<PNS_NODE> tmpNode( m_world->Branch() ); tmpNode->Add( &track ); PNS_JOINT* jt = tmpNode->FindJoint( track.CPoint( -1 ), &track ); if( !jt ) return false; if( ( !track.EndsWithVia() && jt->LinkCount() >= 2 ) || ( track.EndsWithVia() && jt->LinkCount() >= 3 ) ) // we got something connected { end = jt->Pos(); } else { int anchor; PNS_TOPOLOGY topo( tmpNode.get() ); PNS_ITEM* it = topo.NearestUnconnectedItem( jt, &anchor ); if( !it ) return false; end = it->Anchor( anchor ); } aRatLine.Clear(); aRatLine.Append( track.CPoint( -1 ) ); aRatLine.Append( end ); return true; }
const SHAPE_LINE_CHAIN SHAPE_LINE_CHAIN::Slice( int aStartIndex, int aEndIndex ) const { SHAPE_LINE_CHAIN rv; if( aEndIndex < 0 ) aEndIndex += PointCount(); if( aStartIndex < 0 ) aStartIndex += PointCount(); for( int i = aStartIndex; i <= aEndIndex; i++ ) rv.Append( m_points[i] ); return rv; }
// This is the same function as in board_items_to_polygon_shape_transform.cpp // but it adds the rect/trapezoid shapes with a different winding void CINFO3D_VISU::buildPadShapePolygon( const D_PAD* aPad, SHAPE_POLY_SET& aCornerBuffer, wxSize aInflateValue, int aSegmentsPerCircle, double aCorrectionFactor ) const { wxPoint corners[4]; wxPoint PadShapePos = aPad->ShapePos(); /* Note: for pad having a shape offset, * the pad position is NOT the shape position */ switch( aPad->GetShape() ) { case PAD_SHAPE_CIRCLE: case PAD_SHAPE_OVAL: case PAD_SHAPE_ROUNDRECT: aPad->TransformShapeWithClearanceToPolygon( aCornerBuffer, aInflateValue.x, aSegmentsPerCircle, aCorrectionFactor ); break; case PAD_SHAPE_TRAPEZOID: case PAD_SHAPE_RECT: { SHAPE_LINE_CHAIN aLineChain; aPad->BuildPadPolygon( corners, aInflateValue, aPad->GetOrientation() ); for( int ii = 0; ii < 4; ++ii ) { corners[3-ii] += PadShapePos; // Shift origin to position aLineChain.Append( corners[3-ii].x, corners[3-ii].y ); } aLineChain.SetClosed( true ); aCornerBuffer.AddOutline( aLineChain ); } break; default: wxFAIL_MSG( wxT( "CINFO3D_VISU::buildPadShapePolygon: found a not implemented pad shape (new shape?)" ) ); break; } }
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; }
PNS_WALKAROUND::WalkaroundStatus PNS_WALKAROUND::singleStep( PNS_LINE& aPath, bool aWindingDirection ) { optional<PNS_OBSTACLE>& current_obs = aWindingDirection ? m_currentObstacle[0] : m_currentObstacle[1]; bool& prev_recursive = aWindingDirection ? m_recursiveCollision[0] : m_recursiveCollision[1]; if( !current_obs ) return DONE; SHAPE_LINE_CHAIN path_pre[2], path_walk[2], path_post[2]; VECTOR2I last = aPath.GetCLine().CPoint( -1 ); if( ( current_obs->hull ).PointInside( last ) ) { m_recursiveBlockageCount++; if( m_recursiveBlockageCount < 3 ) aPath.GetLine().Append( current_obs->hull.NearestPoint( last ) ); else { aPath = aPath.ClipToNearestObstacle( m_world ); return STUCK; } } aPath.NewWalkaround( current_obs->hull, path_pre[0], path_walk[0], path_post[0], aWindingDirection ); aPath.NewWalkaround( current_obs->hull, path_pre[1], path_walk[1], path_post[1], !aWindingDirection ); int len_pre = path_walk[0].Length(); int len_alt = path_walk[1].Length(); PNS_LINE walk_path( aPath, path_walk[1] ); bool alt_collides = m_world->CheckColliding( &walk_path, m_solids_only ? PNS_ITEM::SOLID : PNS_ITEM::ANY ); SHAPE_LINE_CHAIN pnew; if( !m_forceSingleDirection && len_alt < len_pre && !alt_collides && !prev_recursive ) { pnew = path_pre[1]; pnew.Append( path_walk[1] ); pnew.Append( path_post[1] ); current_obs = nearestObstacle( PNS_LINE( aPath, path_post[1] ) ); prev_recursive = false; } else { pnew = path_pre[0]; pnew.Append( path_walk[0] ); pnew.Append( path_post[0] ); current_obs = nearestObstacle( PNS_LINE( aPath, path_walk[0] ) ); if( !current_obs ) { prev_recursive = false; current_obs = nearestObstacle( PNS_LINE( aPath, path_post[0] ) ); } else prev_recursive = true; } pnew.Simplify(); aPath.SetShape( pnew ); return IN_PROGRESS; }
PNS_WALKAROUND::WalkaroundStatus PNS_WALKAROUND::Route( const PNS_LINE& aInitialPath, PNS_LINE& aWalkPath, bool aOptimize ) { PNS_LINE path_cw( aInitialPath ), path_ccw( aInitialPath ); WalkaroundStatus s_cw = IN_PROGRESS, s_ccw = IN_PROGRESS; SHAPE_LINE_CHAIN best_path; start( aInitialPath ); m_currentObstacle[0] = m_currentObstacle[1] = nearestObstacle( aInitialPath ); m_recursiveBlockageCount = 0; aWalkPath = aInitialPath; while( m_iteration < m_iteration_limit ) { if( s_cw != STUCK ) s_cw = singleStep( path_cw, true ); if( s_ccw != STUCK ) s_ccw = singleStep( path_ccw, false ); if( ( s_cw == DONE && s_ccw == DONE ) || ( s_cw == STUCK && s_ccw == STUCK ) ) { int len_cw = path_cw.GetCLine().Length(); int len_ccw = path_ccw.GetCLine().Length(); if( m_forceLongerPath ) aWalkPath = (len_cw > len_ccw ? path_cw : path_ccw); else aWalkPath = (len_cw < len_ccw ? path_cw : path_ccw); break; } else if( s_cw == DONE && !m_forceLongerPath ) { aWalkPath = path_cw; break; } else if( s_ccw == DONE && !m_forceLongerPath ) { aWalkPath = path_ccw; break; } m_iteration++; } if( m_iteration == m_iteration_limit ) { int len_cw = path_cw.GetCLine().Length(); int len_ccw = path_ccw.GetCLine().Length(); if( m_forceLongerPath ) aWalkPath = (len_cw > len_ccw ? path_cw : path_ccw); else aWalkPath = (len_cw < len_ccw ? path_cw : path_ccw); } if( m_cursorApproachMode ) { // int len_cw = path_cw.GetCLine().Length(); // int len_ccw = path_ccw.GetCLine().Length(); bool found = false; SHAPE_LINE_CHAIN l = aWalkPath.GetCLine(); for( int i = 0; i < l.SegmentCount(); i++ ) { const SEG s = l.Segment( i ); VECTOR2I nearest = s.NearestPoint( m_cursorPos ); VECTOR2I::extended_type dist_a = ( s.A - m_cursorPos ).SquaredEuclideanNorm(); VECTOR2I::extended_type dist_b = ( s.B - m_cursorPos ).SquaredEuclideanNorm(); VECTOR2I::extended_type dist_n = ( nearest - m_cursorPos ).SquaredEuclideanNorm(); if( dist_n <= dist_a && dist_n < dist_b ) { // PNSDisplayDebugLine( l, 3 ); l.Remove( i + 1, -1 ); l.Append( nearest ); l.Simplify(); found = true; break; } } if( found ) { aWalkPath = aInitialPath; aWalkPath.SetShape( l ); } } aWalkPath.SetWorld( m_world ); aWalkPath.GetLine().Simplify(); WalkaroundStatus st = s_ccw == DONE || s_cw == DONE ? DONE : STUCK; if( aOptimize && st == DONE ) PNS_OPTIMIZER::Optimize( &aWalkPath, PNS_OPTIMIZER::MERGE_OBTUSE, m_world ); return st; }
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 ); 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 ); int i = nearest.their.Index(); 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 ); 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::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(); }
SHAPE_LINE_CHAIN PNS_MEANDER_SHAPE::genMeanderShape( VECTOR2D aP, VECTOR2D aDir, bool aSide, PNS_MEANDER_TYPE aType, int aAmpl, int aBaselineOffset ) { const PNS_MEANDER_SETTINGS& st = Settings(); int cr = cornerRadius(); int offset = aBaselineOffset; int spc = spacing(); if( aSide ) offset *= -1; VECTOR2D dir_u_b( aDir.Resize( offset ) ); VECTOR2D dir_v_b( dir_u_b.Perpendicular() ); if( 2 * cr > aAmpl ) { cr = aAmpl / 2; } if( 2 * cr > spc ) { cr = spc / 2; } SHAPE_LINE_CHAIN lc; start( &lc, aP + dir_v_b, aDir ); switch( aType ) { case MT_EMPTY: { lc.Append( aP + dir_v_b + aDir ); break; } case MT_START: { arc( cr - offset, false ); uShape( aAmpl - 2 * cr + std::abs( offset ), cr + offset, spc - 2 * cr ); forward( std::min( cr - offset, cr + offset ) ); forward( std::abs( offset ) ); break; } case MT_FINISH: { start( &lc, aP - dir_u_b, aDir ); turn ( 90 ); forward( std::min( cr - offset, cr + offset ) ); forward( std::abs( offset ) ); uShape( aAmpl - 2 * cr + std::abs( offset ), cr + offset, spc - 2 * cr ); arc( cr - offset, false ); break; } case MT_TURN: { start( &lc, aP - dir_u_b, aDir ); turn( 90 ); forward( std::abs( offset ) ); uShape ( aAmpl - cr, cr + offset, spc - 2 * cr ); forward( std::abs( offset ) ); break; } case MT_SINGLE: { arc( cr - offset, false ); uShape( aAmpl - 2 * cr + std::abs( offset ), cr + offset, spc - 2 * cr ); arc( cr - offset, false ); lc.Append( aP + dir_v_b + aDir.Resize ( 2 * st.m_spacing ) ); break; } default: break; } if( aSide ) { SEG axis ( aP, aP + aDir ); for( int i = 0; i < lc.PointCount(); i++ ) lc.Point( i ) = reflect( lc.CPoint( i ), axis ); } return lc; }
void GRID_HELPER::computeAnchors( BOARD_ITEM* aItem, const VECTOR2I& aRefPos ) { VECTOR2I origin; switch( aItem->Type() ) { case PCB_MODULE_T: { MODULE* mod = static_cast<MODULE*>( aItem ); addAnchor( mod->GetPosition(), ORIGIN | SNAPPABLE, mod ); for( D_PAD* pad = mod->Pads(); pad; pad = pad->Next() ) addAnchor( pad->GetPosition(), CORNER | SNAPPABLE, pad ); break; } case PCB_PAD_T: { D_PAD* pad = static_cast<D_PAD*>( aItem ); addAnchor( pad->GetPosition(), CORNER | SNAPPABLE, pad ); break; } case PCB_MODULE_EDGE_T: case PCB_LINE_T: { DRAWSEGMENT* dseg = static_cast<DRAWSEGMENT*>( aItem ); VECTOR2I start = dseg->GetStart(); VECTOR2I end = dseg->GetEnd(); //LAYER_ID layer = dseg->GetLayer(); switch( dseg->GetShape() ) { case S_CIRCLE: { int r = ( start - end ).EuclideanNorm(); addAnchor( start, ORIGIN | SNAPPABLE, dseg ); addAnchor( start + VECTOR2I( -r, 0 ), OUTLINE | SNAPPABLE, dseg ); addAnchor( start + VECTOR2I( r, 0 ), OUTLINE | SNAPPABLE, dseg ); addAnchor( start + VECTOR2I( 0, -r ), OUTLINE | SNAPPABLE, dseg ); addAnchor( start + VECTOR2I( 0, r ), OUTLINE | SNAPPABLE, dseg ); break; } case S_ARC: { origin = dseg->GetCenter(); addAnchor( dseg->GetArcStart(), CORNER | SNAPPABLE, dseg ); addAnchor( dseg->GetArcEnd(), CORNER | SNAPPABLE, dseg ); addAnchor( origin, ORIGIN | SNAPPABLE, dseg ); break; } case S_SEGMENT: { origin.x = start.x + ( start.x - end.x ) / 2; origin.y = start.y + ( start.y - end.y ) / 2; addAnchor( start, CORNER | SNAPPABLE, dseg ); addAnchor( end, CORNER | SNAPPABLE, dseg ); addAnchor( origin, ORIGIN, dseg ); break; } default: { origin = dseg->GetStart(); addAnchor( origin, ORIGIN | SNAPPABLE, dseg ); break; } } break; } case PCB_TRACE_T: { TRACK* track = static_cast<TRACK*>( aItem ); VECTOR2I start = track->GetStart(); VECTOR2I end = track->GetEnd(); origin.x = start.x + ( start.x - end.x ) / 2; origin.y = start.y + ( start.y - end.y ) / 2; addAnchor( start, CORNER | SNAPPABLE, track ); addAnchor( end, CORNER | SNAPPABLE, track ); addAnchor( origin, ORIGIN, track); break; } case PCB_VIA_T: addAnchor( aItem->GetPosition(), CORNER | SNAPPABLE, aItem ); break; case PCB_ZONE_AREA_T: { const CPolyLine* outline = static_cast<const ZONE_CONTAINER*>( aItem )->Outline(); int cornersCount = outline->GetCornersCount(); SHAPE_LINE_CHAIN lc; lc.SetClosed( true ); for( int i = 0; i < cornersCount; ++i ) { const VECTOR2I p ( outline->GetPos( i ) ); addAnchor( p, CORNER, aItem ); lc.Append( p ); } addAnchor( lc.NearestPoint( aRefPos ), OUTLINE, aItem ); break; } case PCB_MODULE_TEXT_T: case PCB_TEXT_T: addAnchor( aItem->GetPosition(), ORIGIN, aItem ); default: break; } }
WALKAROUND::WALKAROUND_STATUS WALKAROUND::singleStep( LINE& aPath, bool aWindingDirection ) { optional<OBSTACLE>& current_obs = aWindingDirection ? m_currentObstacle[0] : m_currentObstacle[1]; bool& prev_recursive = aWindingDirection ? m_recursiveCollision[0] : m_recursiveCollision[1]; if( !current_obs ) return DONE; SHAPE_LINE_CHAIN path_pre[2], path_walk[2], path_post[2]; VECTOR2I last = aPath.CPoint( -1 ); if( ( current_obs->m_hull ).PointInside( last ) || ( current_obs->m_hull ).PointOnEdge( last ) ) { m_recursiveBlockageCount++; if( m_recursiveBlockageCount < 3 ) aPath.Line().Append( current_obs->m_hull.NearestPoint( last ) ); else { aPath = aPath.ClipToNearestObstacle( m_world ); return DONE; } } aPath.Walkaround( current_obs->m_hull, path_pre[0], path_walk[0], path_post[0], aWindingDirection ); aPath.Walkaround( current_obs->m_hull, path_pre[1], path_walk[1], path_post[1], !aWindingDirection ); #ifdef DEBUG m_logger.NewGroup( aWindingDirection ? "walk-cw" : "walk-ccw", m_iteration ); m_logger.Log( &path_walk[0], 0, "path-walk" ); m_logger.Log( &path_pre[0], 1, "path-pre" ); m_logger.Log( &path_post[0], 4, "path-post" ); m_logger.Log( ¤t_obs->m_hull, 2, "hull" ); m_logger.Log( current_obs->m_item, 3, "item" ); #endif int len_pre = path_walk[0].Length(); int len_alt = path_walk[1].Length(); LINE walk_path( aPath, path_walk[1] ); bool alt_collides = static_cast<bool>( m_world->CheckColliding( &walk_path, m_itemMask ) ); SHAPE_LINE_CHAIN pnew; if( !m_forceLongerPath && len_alt < len_pre && !alt_collides && !prev_recursive ) { pnew = path_pre[1]; pnew.Append( path_walk[1] ); pnew.Append( path_post[1] ); if( !path_post[1].PointCount() || !path_walk[1].PointCount() ) current_obs = nearestObstacle( LINE( aPath, path_pre[1] ) ); else current_obs = nearestObstacle( LINE( aPath, path_post[1] ) ); prev_recursive = false; } else { pnew = path_pre[0]; pnew.Append( path_walk[0] ); pnew.Append( path_post[0] ); if( !path_post[0].PointCount() || !path_walk[0].PointCount() ) current_obs = nearestObstacle( LINE( aPath, path_pre[0] ) ); else current_obs = nearestObstacle( LINE( aPath, path_walk[0] ) ); if( !current_obs ) { prev_recursive = false; current_obs = nearestObstacle( LINE( aPath, path_post[0] ) ); } else prev_recursive = true; } pnew.Simplify(); aPath.SetShape( pnew ); return IN_PROGRESS; }
WALKAROUND::WALKAROUND_STATUS WALKAROUND::Route( const LINE& aInitialPath, LINE& aWalkPath, bool aOptimize ) { LINE path_cw( aInitialPath ), path_ccw( aInitialPath ); WALKAROUND_STATUS s_cw = IN_PROGRESS, s_ccw = IN_PROGRESS; SHAPE_LINE_CHAIN best_path; // special case for via-in-the-middle-of-track placement if( aInitialPath.PointCount() <= 1 ) { if( aInitialPath.EndsWithVia() && m_world->CheckColliding( &aInitialPath.Via(), m_itemMask ) ) return STUCK; aWalkPath = aInitialPath; return DONE; } start( aInitialPath ); m_currentObstacle[0] = m_currentObstacle[1] = nearestObstacle( aInitialPath ); m_recursiveBlockageCount = 0; aWalkPath = aInitialPath; if( m_forceWinding ) { s_cw = m_forceCw ? IN_PROGRESS : STUCK; s_ccw = m_forceCw ? STUCK : IN_PROGRESS; m_forceSingleDirection = true; } else { m_forceSingleDirection = false; } while( m_iteration < m_iterationLimit ) { if( s_cw != STUCK ) s_cw = singleStep( path_cw, true ); if( s_ccw != STUCK ) s_ccw = singleStep( path_ccw, false ); if( ( s_cw == DONE && s_ccw == DONE ) || ( s_cw == STUCK && s_ccw == STUCK ) ) { int len_cw = path_cw.CLine().Length(); int len_ccw = path_ccw.CLine().Length(); if( m_forceLongerPath ) aWalkPath = ( len_cw > len_ccw ? path_cw : path_ccw ); else aWalkPath = ( len_cw < len_ccw ? path_cw : path_ccw ); break; } else if( s_cw == DONE && !m_forceLongerPath ) { aWalkPath = path_cw; break; } else if( s_ccw == DONE && !m_forceLongerPath ) { aWalkPath = path_ccw; break; } m_iteration++; } if( m_iteration == m_iterationLimit ) { int len_cw = path_cw.CLine().Length(); int len_ccw = path_ccw.CLine().Length(); if( m_forceLongerPath ) aWalkPath = ( len_cw > len_ccw ? path_cw : path_ccw ); else aWalkPath = ( len_cw < len_ccw ? path_cw : path_ccw ); } if( m_cursorApproachMode ) { // int len_cw = path_cw.GetCLine().Length(); // int len_ccw = path_ccw.GetCLine().Length(); bool found = false; SHAPE_LINE_CHAIN l = aWalkPath.CLine(); for( int i = 0; i < l.SegmentCount(); i++ ) { const SEG s = l.Segment( i ); VECTOR2I nearest = s.NearestPoint( m_cursorPos ); VECTOR2I::extended_type dist_a = ( s.A - m_cursorPos ).SquaredEuclideanNorm(); VECTOR2I::extended_type dist_b = ( s.B - m_cursorPos ).SquaredEuclideanNorm(); VECTOR2I::extended_type dist_n = ( nearest - m_cursorPos ).SquaredEuclideanNorm(); if( dist_n <= dist_a && dist_n < dist_b ) { l.Remove( i + 1, -1 ); l.Append( nearest ); l.Simplify(); found = true; break; } } if( found ) { aWalkPath = aInitialPath; aWalkPath.SetShape( l ); } } aWalkPath.Line().Simplify(); if( aWalkPath.SegmentCount() < 1 ) return STUCK; if( aWalkPath.CPoint( -1 ) != aInitialPath.CPoint( -1 ) ) return STUCK; if( aWalkPath.CPoint( 0 ) != aInitialPath.CPoint( 0 ) ) return STUCK; WALKAROUND_STATUS st = s_ccw == DONE || s_cw == DONE ? DONE : STUCK; if( st == DONE ) { if( aOptimize ) OPTIMIZER::Optimize( &aWalkPath, OPTIMIZER::MERGE_OBTUSE, m_world ); } return st; }
void ZONE_FILLER::buildUnconnectedThermalStubsPolygonList( SHAPE_POLY_SET& aCornerBuffer, const ZONE_CONTAINER* aZone, const SHAPE_POLY_SET& aRawFilledArea, double aArcCorrection, double aRoundPadThermalRotation ) const { SHAPE_LINE_CHAIN spokes; BOX2I itemBB; VECTOR2I ptTest[4]; auto zoneBB = aRawFilledArea.BBox(); int zone_clearance = aZone->GetZoneClearance(); int biggest_clearance = m_board->GetDesignSettings().GetBiggestClearanceValue(); biggest_clearance = std::max( biggest_clearance, zone_clearance ); zoneBB.Inflate( biggest_clearance ); // half size of the pen used to draw/plot zones outlines int pen_radius = aZone->GetMinThickness() / 2; for( auto module : m_board->Modules() ) { for( auto pad : module->Pads() ) { // Rejects non-standard pads with tht-only thermal reliefs if( aZone->GetPadConnection( pad ) == PAD_ZONE_CONN_THT_THERMAL && pad->GetAttribute() != PAD_ATTRIB_STANDARD ) continue; if( aZone->GetPadConnection( pad ) != PAD_ZONE_CONN_THERMAL && aZone->GetPadConnection( pad ) != PAD_ZONE_CONN_THT_THERMAL ) continue; if( !pad->IsOnLayer( aZone->GetLayer() ) ) continue; if( pad->GetNetCode() != aZone->GetNetCode() ) continue; // Calculate thermal bridge half width int thermalBridgeWidth = aZone->GetThermalReliefCopperBridge( pad ) - aZone->GetMinThickness(); if( thermalBridgeWidth <= 0 ) continue; // we need the thermal bridge half width // with a small extra size to be sure we create a stub // slightly larger than the actual stub thermalBridgeWidth = ( thermalBridgeWidth + 4 ) / 2; int thermalReliefGap = aZone->GetThermalReliefGap( pad ); itemBB = pad->GetBoundingBox(); itemBB.Inflate( thermalReliefGap ); if( !( itemBB.Intersects( zoneBB ) ) ) continue; // Thermal bridges are like a segment from a starting point inside the pad // to an ending point outside the pad // calculate the ending point of the thermal pad, outside the pad VECTOR2I endpoint; endpoint.x = ( pad->GetSize().x / 2 ) + thermalReliefGap; endpoint.y = ( pad->GetSize().y / 2 ) + thermalReliefGap; // Calculate the starting point of the thermal stub // inside the pad VECTOR2I startpoint; int copperThickness = aZone->GetThermalReliefCopperBridge( pad ) - aZone->GetMinThickness(); if( copperThickness < 0 ) copperThickness = 0; // Leave a small extra size to the copper area inside to pad copperThickness += KiROUND( IU_PER_MM * 0.04 ); startpoint.x = std::min( pad->GetSize().x, copperThickness ); startpoint.y = std::min( pad->GetSize().y, copperThickness ); startpoint.x /= 2; startpoint.y /= 2; // This is a CIRCLE pad tweak // for circle pads, the thermal stubs orientation is 45 deg double fAngle = pad->GetOrientation(); if( pad->GetShape() == PAD_SHAPE_CIRCLE ) { endpoint.x = KiROUND( endpoint.x * aArcCorrection ); endpoint.y = endpoint.x; fAngle = aRoundPadThermalRotation; } // contour line width has to be taken into calculation to avoid "thermal stub bleed" endpoint.x += pen_radius; endpoint.y += pen_radius; // compute north, south, west and east points for zone connection. ptTest[0] = VECTOR2I( 0, endpoint.y ); // lower point ptTest[1] = VECTOR2I( 0, -endpoint.y ); // upper point ptTest[2] = VECTOR2I( endpoint.x, 0 ); // right point ptTest[3] = VECTOR2I( -endpoint.x, 0 ); // left point // Test all sides for( int i = 0; i < 4; i++ ) { // rotate point RotatePoint( ptTest[i], fAngle ); // translate point ptTest[i] += pad->ShapePos(); if( aRawFilledArea.Contains( ptTest[i] ) ) continue; spokes.Clear(); // polygons are rectangles with width of copper bridge value switch( i ) { case 0: // lower stub spokes.Append( -thermalBridgeWidth, endpoint.y ); spokes.Append( +thermalBridgeWidth, endpoint.y ); spokes.Append( +thermalBridgeWidth, startpoint.y ); spokes.Append( -thermalBridgeWidth, startpoint.y ); break; case 1: // upper stub spokes.Append( -thermalBridgeWidth, -endpoint.y ); spokes.Append( +thermalBridgeWidth, -endpoint.y ); spokes.Append( +thermalBridgeWidth, -startpoint.y ); spokes.Append( -thermalBridgeWidth, -startpoint.y ); break; case 2: // right stub spokes.Append( endpoint.x, -thermalBridgeWidth ); spokes.Append( endpoint.x, thermalBridgeWidth ); spokes.Append( +startpoint.x, thermalBridgeWidth ); spokes.Append( +startpoint.x, -thermalBridgeWidth ); break; case 3: // left stub spokes.Append( -endpoint.x, -thermalBridgeWidth ); spokes.Append( -endpoint.x, thermalBridgeWidth ); spokes.Append( -startpoint.x, thermalBridgeWidth ); spokes.Append( -startpoint.x, -thermalBridgeWidth ); break; } aCornerBuffer.NewOutline(); // add computed polygon to list for( int ic = 0; ic < spokes.PointCount(); ic++ ) { auto cpos = spokes.CPoint( ic ); RotatePoint( cpos, fAngle ); // Rotate according to module orientation cpos += pad->ShapePos(); // Shift origin to position aCornerBuffer.Append( cpos ); } } } } }