/* * Copyright The KiCad Developers. * Copyright (C) 2024 Fabien Corona f.coronalaposte.net * * 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 2 * 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, you may find one here: * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html * or you may search the http://www.gnu.org website for the version 2 license, * or you may write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ #include #include #include extern bool segmentIntersectsArc( const VECTOR2I& p1, const VECTOR2I& p2, const VECTOR2I& center, double radius, EDA_ANGLE startAngle, EDA_ANGLE endAngle, std::vector* aIntersectPoints ); //Check if line segments 'p1q1' and 'p2q2' intersect, excluding endpoint overlap bool segments_intersect( VECTOR2I p1, VECTOR2I q1, VECTOR2I p2, VECTOR2I q2, std::vector* aIntersectPoints ) { if( p1 == p2 || p1 == q2 || q1 == p2 || q1 == q2 ) return false; SEG segment1( p1, q1 ); SEG segment2( p2, q2 ); std::vector intersectionPoints; INTERSECTABLE_GEOM geom1 = segment1; INTERSECTABLE_GEOM geom2 = segment2; INTERSECTION_VISITOR visitor( geom2, intersectionPoints ); std::visit( visitor, geom1 ); if( aIntersectPoints ) { for( VECTOR2I& point : intersectionPoints ) aIntersectPoints->push_back( point ); } return intersectionPoints.size() > 0; } bool compareShapes( const CREEP_SHAPE* a, const CREEP_SHAPE* b ) { if( !a ) return true; if( !b ) return false; if( a->GetType() != b->GetType() ) { return a->GetType() < b->GetType(); } if( a->GetType() == CREEP_SHAPE::TYPE::UNDEFINED ) return true; auto posA = a->GetPos(); auto posB = b->GetPos(); if( posA != posB ) { return posA < posB; } if( a->GetType() == CREEP_SHAPE::TYPE::CIRCLE ) { return a->GetRadius() < b->GetRadius(); } return false; } bool areEquivalent( const CREEP_SHAPE* a, const CREEP_SHAPE* b ) { if( !a && !b ) { return true; } if( ( !a && b ) || ( a && !b ) ) { return false; } if( a->GetType() != b->GetType() ) { return false; } if( a->GetType() == CREEP_SHAPE::TYPE::POINT ) { return a->GetPos() == b->GetPos(); } if( a->GetType() == CREEP_SHAPE::TYPE::CIRCLE ) { return a->GetPos() == b->GetPos() && ( a->GetRadius() == b->GetRadius() ); } return false; } std::vector BE_SHAPE_POINT::Paths( const BE_SHAPE_POINT& aS2, double aMaxWeight, double aMaxSquaredWeight ) const { std::vector result; double weight = ( this->GetPos() - aS2.GetPos() ).SquaredEuclideanNorm(); if( weight > aMaxSquaredWeight ) return result; PATH_CONNECTION pc; pc.a1 = this->GetPos(); pc.a2 = aS2.GetPos(); pc.weight = sqrt( weight ); result.push_back( pc ); return result; } std::vector BE_SHAPE_POINT::Paths( const BE_SHAPE_CIRCLE& aS2, double aMaxWeight, double aMaxSquaredWeight ) const { std::vector result; int radius = aS2.GetRadius(); VECTOR2I pointPos = this->GetPos(); VECTOR2I circleCenter = aS2.GetPos(); if( radius <= 0 ) { return result; } double pointToCenterDistanceSquared = ( pointPos - circleCenter ).SquaredEuclideanNorm(); double weightSquared = pointToCenterDistanceSquared - (float) radius * (float) radius; if( weightSquared > aMaxSquaredWeight ) return result; VECTOR2D direction1 = VECTOR2D( pointPos.x - circleCenter.x, pointPos.y - circleCenter.y ); direction1 = direction1.Resize( 1 ); VECTOR2D direction2 = direction1.Perpendicular(); double radiusSquared = double( radius ) * double( radius ); double distance = sqrt( pointToCenterDistanceSquared ); double value1 = radiusSquared / distance; double value2 = sqrt( radiusSquared - value1 * value1 ); VECTOR2D resultPoint; PATH_CONNECTION pc; pc.a1 = pointPos; pc.weight = sqrt( weightSquared ); resultPoint = direction1 * value1 + direction2 * value2 + circleCenter; pc.a2.x = int( resultPoint.x ); pc.a2.y = int( resultPoint.y ); result.push_back( pc ); resultPoint = direction1 * value1 - direction2 * value2 + circleCenter; pc.a2.x = int( resultPoint.x ); pc.a2.y = int( resultPoint.y ); result.push_back( pc ); return result; } std::pair BE_SHAPE_ARC::IsThereATangentPassingThroughPoint( const BE_SHAPE_POINT aPoint ) const { std::pair result; double R = m_radius; VECTOR2I newPoint = aPoint.GetPos() - m_pos; if( newPoint.SquaredEuclideanNorm() <= R * R ) { // If the point is inside the arc result.first = false; result.second = false; return result; } EDA_ANGLE testAngle = AngleBetweenStartAndEnd( aPoint.GetPos() ); double startAngle = m_startAngle.AsRadians(); double endAngle = m_endAngle.AsRadians(); double pointAngle = testAngle.AsRadians(); bool greaterThan180 = ( m_endAngle - m_startAngle ) > EDA_ANGLE( 180 ); bool connectToEndPoint; connectToEndPoint = ( cos( startAngle ) * newPoint.x + sin( startAngle ) * newPoint.y >= R ); if( greaterThan180 ) connectToEndPoint &= ( cos( endAngle ) * newPoint.x + sin( endAngle ) * newPoint.y <= R ); connectToEndPoint |= ( cos( endAngle ) * newPoint.x + sin( endAngle ) * newPoint.y <= R ) && ( pointAngle >= endAngle || pointAngle <= startAngle ); result.first = !connectToEndPoint; connectToEndPoint = ( cos( endAngle ) * newPoint.x + sin( endAngle ) * newPoint.y >= R ); if( greaterThan180 ) connectToEndPoint &= ( cos( startAngle ) * newPoint.x + sin( startAngle ) * newPoint.y <= R ); connectToEndPoint |= ( cos( startAngle ) * newPoint.x + sin( startAngle ) * newPoint.y <= R ) && ( pointAngle >= endAngle || pointAngle <= startAngle ); result.second = !connectToEndPoint; return result; } std::vector BE_SHAPE_POINT::Paths( const BE_SHAPE_ARC& aS2, double aMaxWeight, double aMaxSquaredWeight ) const { std::vector result; VECTOR2I center = aS2.GetPos(); double radius = aS2.GetRadius(); // First path tries to connect to start point // Second path tries to connect to end point std::pair behavesLikeCircle; behavesLikeCircle = aS2.IsThereATangentPassingThroughPoint( *this ); if( behavesLikeCircle.first && behavesLikeCircle.second ) { BE_SHAPE_CIRCLE csc( center, radius ); return this->Paths( csc, aMaxWeight, aMaxSquaredWeight ); } if( behavesLikeCircle.first ) { BE_SHAPE_CIRCLE csc( center, radius ); std::vector paths = this->Paths( csc, aMaxWeight, aMaxSquaredWeight ); if( paths.size() > 1 ) // Point to circle creates either 0 or 2 connections result.push_back( paths[1] ); } else { BE_SHAPE_POINT csp1( aS2.GetStartPoint() ); for( const PATH_CONNECTION& pc : this->Paths( csp1, aMaxWeight, aMaxSquaredWeight ) ) result.push_back( pc ); } if( behavesLikeCircle.second ) { BE_SHAPE_CIRCLE csc( center, radius ); std::vector paths = this->Paths( csc, aMaxWeight, aMaxSquaredWeight ); if( paths.size() > 1 ) // Point to circle creates either 0 or 2 connections { result.push_back( paths[0] ); } } else { BE_SHAPE_POINT csp1( aS2.GetEndPoint() ); for( const PATH_CONNECTION& pc : this->Paths( csp1, aMaxWeight, aMaxSquaredWeight ) ) result.push_back( pc ); } return result; } std::vector BE_SHAPE_CIRCLE::Paths( const BE_SHAPE_ARC& aS2, double aMaxWeight, double aMaxSquaredWeight ) const { std::vector result; VECTOR2I circleCenter = this->GetPos(); double circleRadius = this->GetRadius(); VECTOR2I arcCenter = aS2.GetPos(); double arcRadius = aS2.GetRadius(); EDA_ANGLE arcStartAngle = aS2.GetStartAngle(); EDA_ANGLE arcEndAngle = aS2.GetEndAngle(); double centerDistance = ( circleCenter - arcCenter ).EuclideanNorm(); if( centerDistance + arcRadius < circleRadius ) { // The arc is inside the circle return result; } BE_SHAPE_POINT csp1( aS2.GetStartPoint() ); BE_SHAPE_POINT csp2( aS2.GetEndPoint() ); BE_SHAPE_CIRCLE csc( arcCenter, arcRadius ); for( const PATH_CONNECTION& pc : this->Paths( csc, aMaxWeight, aMaxSquaredWeight ) ) { EDA_ANGLE pointAngle = aS2.AngleBetweenStartAndEnd( pc.a2 - arcCenter ); if( pointAngle <= aS2.GetEndAngle() ) result.push_back( pc ); } if( result.size() == 4 ) { // It behaved as a circle return result; } for( const BE_SHAPE_POINT& csp : { csp1, csp2 } ) { for( PATH_CONNECTION pc : this->Paths( csp, aMaxWeight, aMaxSquaredWeight ) ) { if( !segmentIntersectsArc( pc.a1, pc.a2, arcCenter, arcRadius, arcStartAngle, arcEndAngle, nullptr ) ) { result.push_back( pc ); } } } return result; } std::vector BE_SHAPE_ARC::Paths( const BE_SHAPE_ARC& aS2, double aMaxWeight, double aMaxSquaredWeight ) const { std::vector result; VECTOR2I circleCenter = this->GetPos(); double circleRadius = this->GetRadius(); VECTOR2I arcCenter = aS2.GetPos(); double arcRadius = aS2.GetRadius(); double centerDistance = ( circleCenter - arcCenter ).EuclideanNorm(); if( centerDistance + arcRadius < circleRadius ) { // The arc is inside the circle return result; } BE_SHAPE_POINT csp1( aS2.GetStartPoint() ); BE_SHAPE_POINT csp2( aS2.GetEndPoint() ); BE_SHAPE_CIRCLE csc( arcCenter, arcRadius ); for( const PATH_CONNECTION& pc : this->Paths( BE_SHAPE_CIRCLE( aS2.GetPos(), aS2.GetRadius() ), aMaxWeight, aMaxSquaredWeight ) ) { EDA_ANGLE pointAngle = aS2.AngleBetweenStartAndEnd( pc.a2 - arcCenter ); if( pointAngle <= aS2.GetEndAngle() ) result.push_back( pc ); } for( const PATH_CONNECTION& pc : BE_SHAPE_CIRCLE( this->GetPos(), this->GetRadius() ) .Paths( aS2, aMaxWeight, aMaxSquaredWeight ) ) { EDA_ANGLE pointAngle = this->AngleBetweenStartAndEnd( pc.a2 - arcCenter ); if( pointAngle <= this->GetEndAngle() ) result.push_back( pc ); } return result; } std::vector BE_SHAPE_CIRCLE::Paths( const BE_SHAPE_CIRCLE& aS2, double aMaxWeight, double aMaxSquaredWeight ) const { std::vector result; VECTOR2I p1 = this->GetPos(); VECTOR2I p2 = aS2.GetPos(); VECTOR2D distSquared( double( ( p2 - p1 ).x ), double( ( p2 - p1 ).y ) ); double weightSquared = distSquared.SquaredEuclideanNorm(); double R1 = this->GetRadius(); double R2 = aS2.GetRadius(); double Rdiff = abs( R1 - R2 ); double Rsum = R1 + R2; // "Straight" paths double weightSquared1 = weightSquared - Rdiff * Rdiff; // "Crossed" paths double weightSquared2 = weightSquared - Rsum * Rsum; if( weightSquared1 <= aMaxSquaredWeight ) { VECTOR2D direction1 = VECTOR2D( p2.x - p1.x, p2.y - p1.y ); direction1 = direction1.Resize( 1 ); VECTOR2D direction2 = direction1.Perpendicular(); double D = sqrt( weightSquared ); double ratio1 = ( R1 - R2 ) / D; double ratio2 = sqrt( 1 - ratio1 * ratio1 ); PATH_CONNECTION pc; pc.weight = sqrt( weightSquared1 ); pc.a1 = p1 + direction1 * R1 * ratio1 + direction2 * R1 * ratio2; pc.a2 = p2 + direction1 * R2 * ratio1 + direction2 * R2 * ratio2; result.push_back( pc ); pc.a1 = p1 + direction1 * R1 * ratio1 - direction2 * R1 * ratio2; pc.a2 = p2 + direction1 * R2 * ratio1 - direction2 * R2 * ratio2; result.push_back( pc ); } if( weightSquared2 <= aMaxSquaredWeight ) { VECTOR2D direction1 = VECTOR2D( p2.x - p1.x, p2.y - p1.y ); direction1 = direction1.Resize( 1 ); VECTOR2D direction2 = direction1.Perpendicular(); double D = sqrt( weightSquared ); double ratio1 = ( R1 + R2 ) / D; double ratio2 = sqrt( 1 - ratio1 * ratio1 ); PATH_CONNECTION pc; pc.weight = sqrt( weightSquared2 ); pc.a1 = p1 + direction1 * R1 * ratio1 + direction2 * R1 * ratio2; pc.a2 = p2 - direction1 * R2 * ratio1 - direction2 * R2 * ratio2; result.push_back( pc ); pc.a1 = p1 + direction1 * R1 * ratio1 - direction2 * R1 * ratio2; pc.a2 = p2 - direction1 * R2 * ratio1 + direction2 * R2 * ratio2; result.push_back( pc ); } return result; } void CREEPAGE_GRAPH::TransformCreepShapesToNodes( std::vector& aShapes ) { for( CREEP_SHAPE* p1 : aShapes ) { if( !p1 ) continue; switch( p1->GetType() ) { case CREEP_SHAPE::TYPE::POINT: AddNode( GRAPH_NODE::TYPE::POINT, p1, p1->GetPos() ); break; case CREEP_SHAPE::TYPE::CIRCLE: AddNode( GRAPH_NODE::TYPE::CIRCLE, p1, p1->GetPos() ); break; case CREEP_SHAPE::TYPE::ARC: AddNode( GRAPH_NODE::TYPE::ARC, p1, p1->GetPos() ); break; default: break; } } } void CREEPAGE_GRAPH::RemoveDuplicatedShapes() { // Sort the vector sort( m_shapeCollection.begin(), m_shapeCollection.end(), compareShapes ); std::vector newVector; size_t i = 0; for( i = 0; i < m_shapeCollection.size() - 1; i++ ) { if( m_shapeCollection[i] == nullptr ) continue; if( areEquivalent( m_shapeCollection[i], m_shapeCollection[i + 1] ) ) { delete m_shapeCollection[i]; m_shapeCollection[i] = nullptr; } else { newVector.push_back( m_shapeCollection[i] ); } } if( m_shapeCollection[i] ) newVector.push_back( m_shapeCollection[i] ); std::swap( m_shapeCollection, newVector ); } void CREEPAGE_GRAPH::TransformEdgeToCreepShapes() { for( BOARD_ITEM* drawing : m_boardEdge ) { PCB_SHAPE* d = dynamic_cast( drawing ); if( !d ) continue; switch( d->GetShape() ) { case SHAPE_T::SEGMENT: { BE_SHAPE_POINT* a = new BE_SHAPE_POINT( d->GetStart() ); m_shapeCollection.push_back( a ); a = new BE_SHAPE_POINT( d->GetEnd() ); m_shapeCollection.push_back( a ); break; } case SHAPE_T::RECTANGLE: { BE_SHAPE_POINT* a = new BE_SHAPE_POINT( d->GetStart() ); m_shapeCollection.push_back( a ); a = new BE_SHAPE_POINT( d->GetEnd() ); m_shapeCollection.push_back( a ); a = new BE_SHAPE_POINT( VECTOR2I( d->GetEnd().x, d->GetStart().y ) ); m_shapeCollection.push_back( a ); a = new BE_SHAPE_POINT( VECTOR2I( d->GetStart().x, d->GetEnd().y ) ); m_shapeCollection.push_back( a ); break; } case SHAPE_T::POLY: { std::vector points; d->DupPolyPointsList( points ); for( auto p : points ) { BE_SHAPE_POINT* a = new BE_SHAPE_POINT( p ); m_shapeCollection.push_back( a ); } break; } case SHAPE_T::CIRCLE: { BE_SHAPE_CIRCLE* a = new BE_SHAPE_CIRCLE( d->GetCenter(), d->GetRadius() ); a->SetParent( d ); m_shapeCollection.push_back( a ); break; } case SHAPE_T::ARC: { // If the arc is not locally convex, only use the endpoints double tolerance = 10; VECTOR2D center( double( d->GetCenter().x ), double( d->GetCenter().y ) ); VECTOR2D mid( double( d->GetArcMid().x ), double( d->GetArcMid().y ) ); VECTOR2D dir( mid - center ); dir = dir / d->GetRadius() * ( d->GetRadius() - tolerance ); EDA_ANGLE alpha, beta; d->CalcArcAngles( alpha, beta ); BE_SHAPE_ARC* a = new BE_SHAPE_ARC( d->GetCenter(), d->GetRadius(), alpha, beta, d->GetStart(), d->GetEnd() ); a->SetParent( d ); m_shapeCollection.push_back( a ); break; } default: break; } } } std::vector GRAPH_CONNECTION::GetShapes() { std::vector shapes = std::vector(); int lineWidth = 0; if( !m_path.m_show ) return shapes; if( !n1 || !n2 ) return shapes; if( n1->m_type == GRAPH_NODE::TYPE::VIRTUAL || n2->m_type == GRAPH_NODE::TYPE::VIRTUAL ) { return shapes; } if( !m_forceStraightLine && n1->m_parent && ( n1->m_parent == n2->m_parent ) && ( n1->m_parent->GetType() == CREEP_SHAPE::TYPE::CIRCLE ) ) { VECTOR2I center = n1->m_parent->GetPos(); VECTOR2I R1 = n1->m_pos - center; VECTOR2I R2 = n2->m_pos - center; PCB_SHAPE s( nullptr, SHAPE_T::ARC ); if( R1.Cross( R2 ) > 0 ) { s.SetStart( n1->m_pos ); s.SetEnd( n2->m_pos ); } else { s.SetStart( n2->m_pos ); s.SetEnd( n1->m_pos ); } s.SetCenter( center ); s.SetWidth( lineWidth ); s.SetLayer( Eco1_User ); shapes.push_back( s ); return shapes; } else if( !m_forceStraightLine && n1->m_parent && ( n1->m_parent == n2->m_parent ) && n1->m_parent->GetType() == CREEP_SHAPE::TYPE::ARC ) { BE_SHAPE_ARC* arc = dynamic_cast( n1->m_parent ); if( !arc ) { PCB_SHAPE s; s.SetStart( m_path.a1 ); s.SetEnd( m_path.a2 ); s.SetWidth( lineWidth ); s.SetLayer( Eco1_User ); shapes.push_back( s ); return shapes; } VECTOR2I center = arc->GetPos(); VECTOR2I R1 = n1->m_pos - center; VECTOR2I R2 = n2->m_pos - center; PCB_SHAPE s( nullptr, SHAPE_T::ARC ); if( R1.Cross( R2 ) > 0 ) { s.SetStart( n1->m_pos ); s.SetEnd( n2->m_pos ); } else { s.SetStart( n2->m_pos ); s.SetEnd( n1->m_pos ); } s.SetCenter( center ); //Check that we are on the correct side of the arc. VECTOR2I mid = s.GetArcMid(); EDA_ANGLE midAngle = arc->AngleBetweenStartAndEnd( mid ); if( midAngle > arc->GetEndAngle() ) { VECTOR2I tmp; tmp = s.GetStart(); s.SetStart( s.GetEnd() ); s.SetEnd( tmp ); s.SetCenter( center ); } s.SetWidth( lineWidth ); s.SetLayer( Eco1_User ); shapes.push_back( s ); return shapes; } PCB_SHAPE s; s.SetStart( m_path.a1 ); s.SetEnd( m_path.a2 ); s.SetWidth( lineWidth ); shapes.push_back( s ); return shapes; } void CREEP_SHAPE::ConnectChildren( std::shared_ptr& a1, std::shared_ptr&, CREEPAGE_GRAPH& aG ) const { } void BE_SHAPE_POINT::ConnectChildren( std::shared_ptr& a1, std::shared_ptr&, CREEPAGE_GRAPH& aG ) const { } void BE_SHAPE_CIRCLE::ShortenChildDueToGV( std::shared_ptr& a1, std::shared_ptr& a2, CREEPAGE_GRAPH& aG, double aNormalWeight ) const { EDA_ANGLE angle1 = EDA_ANGLE( a1->m_pos - m_pos ); EDA_ANGLE angle2 = EDA_ANGLE( a2->m_pos - m_pos ); while( angle1 < ANGLE_0 ) angle1 += ANGLE_360; while( angle2 < ANGLE_0 ) angle2 += ANGLE_360; while( angle1 > ANGLE_360 ) angle1 -= ANGLE_360; while( angle2 > ANGLE_360 ) angle2 -= ANGLE_360; EDA_ANGLE maxAngle = angle1 > angle2 ? angle1 : angle2; EDA_ANGLE skipAngle = EDA_ANGLE( asin( float( aG.m_minGrooveWidth ) / ( 2 * m_radius ) ), RADIANS_T ); skipAngle += skipAngle; // Cannot multiply EDA_ANGLE by scalar, but this really is angle *2 EDA_ANGLE pointAngle = maxAngle - skipAngle; VECTOR2I skipPoint = m_pos; skipPoint.x += m_radius * cos( pointAngle.AsRadians() ); skipPoint.y += m_radius * sin( pointAngle.AsRadians() ); std::shared_ptr gnt = aG.AddNode( GRAPH_NODE::POINT, a1->m_parent, skipPoint ); PATH_CONNECTION pc; pc.a1 = maxAngle == angle2 ? a1->m_pos : a2->m_pos; pc.a2 = skipPoint; pc.weight = aNormalWeight - aG.m_minGrooveWidth; aG.AddConnection( maxAngle == angle2 ? a1 : a2, gnt, pc ); pc.a1 = skipPoint; pc.a2 = maxAngle == angle2 ? a2->m_pos : a1->m_pos; pc.weight = aG.m_minGrooveWidth; std::shared_ptr gc = aG.AddConnection( gnt, maxAngle == angle2 ? a2 : a1, pc ); if( gc ) gc->m_forceStraightLine = true; } void BE_SHAPE_CIRCLE::ConnectChildren( std::shared_ptr& a1, std::shared_ptr& a2, CREEPAGE_GRAPH& aG ) const { if( !a1 || !a2 ) return; if( m_radius == 0 ) return; VECTOR2D distI( a1->m_pos - a2->m_pos ); VECTOR2D distD( double( distI.x ), double( distI.y ) ); double weight = m_radius * 2 * asin( distD.EuclideanNorm() / ( 2.0 * m_radius ) ); if( ( weight > aG.GetTarget() ) ) return; if( aG.m_minGrooveWidth <= 0 ) { PATH_CONNECTION pc; pc.a1 = a1->m_pos; pc.a2 = a2->m_pos; pc.weight = std::max( weight, 0.0 ); aG.AddConnection( a1, a2, pc ); return; } if( weight > aG.m_minGrooveWidth ) ShortenChildDueToGV( a1, a2, aG, weight ); // Else well.. this paths will be "shorted" by another one } void BE_SHAPE_ARC::ConnectChildren( std::shared_ptr& a1, std::shared_ptr& a2, CREEPAGE_GRAPH& aG ) const { if( !a1 || !a2 ) return; EDA_ANGLE angle1 = AngleBetweenStartAndEnd( a1->m_pos ); EDA_ANGLE angle2 = AngleBetweenStartAndEnd( a2->m_pos ); double weight = abs( m_radius * ( angle2 - angle1 ).AsRadians() ); if( true || aG.m_minGrooveWidth <= 0 ) { if( ( weight > aG.GetTarget() ) ) return; PATH_CONNECTION pc; pc.a1 = a1->m_pos; pc.a2 = a2->m_pos; pc.weight = weight; aG.AddConnection( a1, a2, pc ); return; } if( weight > aG.m_minGrooveWidth ) ShortenChildDueToGV( a1, a2, aG, weight ); } void CREEPAGE_GRAPH::SetTarget( double aTarget ) { m_creepageTarget = aTarget; m_creepageTargetSquared = aTarget * aTarget; } bool segmentIntersectsArc( const VECTOR2I& p1, const VECTOR2I& p2, const VECTOR2I& center, double radius, EDA_ANGLE startAngle, EDA_ANGLE endAngle, std::vector* aIntersectPoints ) { SEG segment( p1, p2 ); VECTOR2I startPoint( radius * cos( startAngle.AsRadians() ), radius * sin( startAngle.AsRadians() ) ); startPoint += center; SHAPE_ARC arc( center, startPoint, endAngle - startAngle ); std::vector intersectionPoints; INTERSECTABLE_GEOM geom1 = segment; INTERSECTABLE_GEOM geom2 = arc; INTERSECTION_VISITOR visitor( geom2, intersectionPoints ); std::visit( visitor, geom1 ); if( aIntersectPoints ) { for( VECTOR2I& point : intersectionPoints ) aIntersectPoints->push_back( point ); } return intersectionPoints.size() > 0; } std::vector CU_SHAPE_SEGMENT::Paths( const BE_SHAPE_POINT& aS2, double aMaxWeight, double aMaxSquaredWeight ) const { std::vector result; VECTOR2I start = this->GetStart(); VECTOR2I end = this->GetEnd(); double halfWidth = this->GetWidth() / 2; EDA_ANGLE trackAngle( end - start ); VECTOR2I pointPos = aS2.GetPos(); double length = ( start - end ).EuclideanNorm(); double projectedPos = cos( trackAngle.AsRadians() ) * ( pointPos.x - start.x ) + sin( trackAngle.AsRadians() ) * ( pointPos.y - start.y ); VECTOR2I newPoint; if( projectedPos <= 0 ) { newPoint = start + ( pointPos - start ).Resize( halfWidth ); } else if( projectedPos >= length ) { newPoint = end + ( pointPos - end ).Resize( halfWidth ); } else { double posOnSegment = ( start - pointPos ).SquaredEuclideanNorm() - ( end - pointPos ).SquaredEuclideanNorm(); posOnSegment = posOnSegment / ( 2 * length ) + length / 2; newPoint = start + ( end - start ).Resize( posOnSegment ); newPoint += ( pointPos - newPoint ).Resize( halfWidth ); } double weightSquared = ( pointPos - newPoint ).SquaredEuclideanNorm(); if( weightSquared > aMaxSquaredWeight ) return result; PATH_CONNECTION pc; pc.a1 = newPoint; pc.a2 = pointPos; pc.weight = sqrt( weightSquared ); result.push_back( pc ); return result; } std::vector CU_SHAPE_SEGMENT::Paths( const BE_SHAPE_CIRCLE& aS2, double aMaxWeight, double aMaxSquaredWeight ) const { std::vector result; VECTOR2I start = this->GetStart(); VECTOR2I end = this->GetEnd(); double halfWidth = this->GetWidth() / 2; double circleRadius = aS2.GetRadius(); VECTOR2I circleCenter = aS2.GetPos(); double length = ( start - end ).EuclideanNorm(); EDA_ANGLE trackAngle( end - start ); double weightSquared = std::numeric_limits::infinity(); VECTOR2I PointOnTrack, PointOnCircle; // There are two possible paths // First the one on the side of the start of the track. double projectedPos1 = cos( trackAngle.AsRadians() ) * ( circleCenter.x - start.x ) + sin( trackAngle.AsRadians() ) * ( circleCenter.y - start.y ); double projectedPos2 = projectedPos1 + circleRadius; projectedPos1 = projectedPos1 - circleRadius; double trackSide = ( end - start ).Cross( circleCenter - start ) > 0 ? 1 : -1; if( ( projectedPos1 < 0 && projectedPos2 < 0 ) ) { CU_SHAPE_CIRCLE csc( start, halfWidth ); for( PATH_CONNECTION pc : csc.Paths( aS2, aMaxWeight, aMaxSquaredWeight ) ) { result.push_back( pc ); } } else if( ( projectedPos1 > length && projectedPos2 > length ) ) { CU_SHAPE_CIRCLE csc( end, halfWidth ); for( const PATH_CONNECTION& pc : csc.Paths( aS2, aMaxWeight, aMaxSquaredWeight ) ) result.push_back( pc ); } else if( ( projectedPos1 >= 0 ) && ( projectedPos1 <= length ) && ( projectedPos2 >= 0 ) && ( projectedPos2 <= length ) ) { // Both point connects to the segment part of the track PointOnTrack = start; PointOnTrack += ( end - start ).Resize( projectedPos1 ); PointOnTrack += ( end - start ).Perpendicular().Resize( halfWidth ) * trackSide; PointOnCircle = circleCenter - ( end - start ).Resize( circleRadius ); weightSquared = ( PointOnCircle - PointOnTrack ).SquaredEuclideanNorm(); if( weightSquared < aMaxSquaredWeight ) { PATH_CONNECTION pc; pc.a1 = PointOnTrack; pc.a2 = PointOnCircle; pc.weight = sqrt( weightSquared ); result.push_back( pc ); PointOnTrack = start; PointOnTrack += ( end - start ).Resize( projectedPos2 ); PointOnTrack += ( end - start ).Perpendicular().Resize( halfWidth ) * trackSide; PointOnCircle = circleCenter + ( end - start ).Resize( circleRadius ); pc.a1 = PointOnTrack; pc.a2 = PointOnCircle; result.push_back( pc ); } } else if( ( ( projectedPos1 >= 0 ) && ( projectedPos1 <= length ) ) && ( ( projectedPos2 > length ) || projectedPos2 < 0 ) ) { CU_SHAPE_CIRCLE csc( end, halfWidth ); std::vector pcs = csc.Paths( aS2, aMaxWeight, aMaxSquaredWeight ); if( pcs.size() < 2 ) return result; result.push_back( pcs.at( trackSide == 1 ? 1 : 0 ) ); PointOnTrack = start; PointOnTrack += ( end - start ).Resize( projectedPos1 ); PointOnTrack += ( end - start ).Perpendicular().Resize( halfWidth ) * trackSide; PointOnCircle = circleCenter - ( end - start ).Resize( circleRadius ); weightSquared = ( PointOnCircle - PointOnTrack ).SquaredEuclideanNorm(); if( weightSquared < aMaxSquaredWeight ) { PATH_CONNECTION pc; pc.a1 = PointOnTrack; pc.a2 = PointOnCircle; pc.weight = sqrt( weightSquared ); result.push_back( pc ); } } else if( ( ( projectedPos2 >= 0 ) && ( projectedPos2 <= length ) ) && ( ( projectedPos1 > length ) || projectedPos1 < 0 ) ) { CU_SHAPE_CIRCLE csc( start, halfWidth ); std::vector pcs = csc.Paths( aS2, aMaxWeight, aMaxSquaredWeight ); if( pcs.size() < 2 ) return result; result.push_back( pcs.at( trackSide == 1 ? 0 : 1 ) ); PointOnTrack = start; PointOnTrack += ( end - start ).Resize( projectedPos2 ); PointOnTrack += ( end - start ).Perpendicular().Resize( halfWidth ) * trackSide; PointOnCircle = circleCenter + ( end - start ).Resize( circleRadius ); weightSquared = ( PointOnCircle - PointOnTrack ).SquaredEuclideanNorm(); if( weightSquared < aMaxSquaredWeight ) { PATH_CONNECTION pc; pc.a1 = PointOnTrack; pc.a2 = PointOnCircle; pc.weight = sqrt( weightSquared ); result.push_back( pc ); } } return result; } std::vector CU_SHAPE_SEGMENT::Paths( const BE_SHAPE_ARC& aS2, double aMaxWeight, double aMaxSquaredWeight ) const { std::vector result; BE_SHAPE_CIRCLE bsc( aS2.GetPos(), aS2.GetRadius() ); for( const PATH_CONNECTION& pc : this->Paths( bsc, aMaxWeight, aMaxSquaredWeight ) ) { EDA_ANGLE testAngle = aS2.AngleBetweenStartAndEnd( pc.a2 ); if( testAngle < aS2.GetEndAngle() ) result.push_back( pc ); } if( result.size() < 2 ) { BE_SHAPE_POINT bsp1( aS2.GetStartPoint() ); BE_SHAPE_POINT bsp2( aS2.GetEndPoint() ); VECTOR2I beArcPos = aS2.GetPos(); int beArcRadius = aS2.GetRadius(); EDA_ANGLE beArcStartAngle = aS2.GetStartAngle(); EDA_ANGLE beArcEndAngle = aS2.GetEndAngle(); for( const PATH_CONNECTION& pc : this->Paths( bsp1, aMaxWeight, aMaxSquaredWeight ) ) { if( !segmentIntersectsArc( pc.a1, pc.a2, beArcPos, beArcRadius, beArcStartAngle, beArcEndAngle, nullptr ) ) { result.push_back( pc ); } } for( const PATH_CONNECTION& pc : this->Paths( bsp2, aMaxWeight, aMaxSquaredWeight ) ) { if( !segmentIntersectsArc( pc.a1, pc.a2, beArcPos, beArcRadius, beArcStartAngle, beArcEndAngle, nullptr ) ) { result.push_back( pc ); } } } return result; } std::vector CU_SHAPE_CIRCLE::Paths( const BE_SHAPE_ARC& aS2, double aMaxWeight, double aMaxSquaredWeight ) const { std::vector result; VECTOR2I beArcPos = aS2.GetPos(); int beArcRadius = aS2.GetRadius(); EDA_ANGLE beArcStartAngle = aS2.GetStartAngle(); EDA_ANGLE beArcEndAngle = aS2.GetEndAngle(); BE_SHAPE_CIRCLE bsc( beArcPos, beArcRadius ); for( const PATH_CONNECTION& pc : this->Paths( bsc, aMaxWeight, aMaxSquaredWeight ) ) { EDA_ANGLE testAngle = aS2.AngleBetweenStartAndEnd( pc.a2 ); if( testAngle < aS2.GetEndAngle() ) result.push_back( pc ); } if( result.size() < 2 ) { BE_SHAPE_POINT bsp1( aS2.GetStartPoint() ); BE_SHAPE_POINT bsp2( aS2.GetEndPoint() ); for( const PATH_CONNECTION& pc : this->Paths( bsp1, aMaxWeight, aMaxSquaredWeight ) ) { if( !segmentIntersectsArc( pc.a1, pc.a2, beArcPos, beArcRadius, beArcStartAngle, beArcEndAngle, nullptr ) ) { result.push_back( pc ); } } for( const PATH_CONNECTION& pc : this->Paths( bsp2, aMaxWeight, aMaxSquaredWeight ) ) { if( !segmentIntersectsArc( pc.a1, pc.a2, beArcPos, beArcRadius, beArcStartAngle, beArcEndAngle, nullptr ) ) { result.push_back( pc ); } } } return result; } std::vector CU_SHAPE_ARC::Paths( const BE_SHAPE_CIRCLE& aS2, double aMaxWeight, double aMaxSquaredWeight ) const { std::vector result; CU_SHAPE_CIRCLE csc( this->GetPos(), this->GetRadius() + this->GetWidth() / 2 ); for( const PATH_CONNECTION& pc : this->Paths( csc, aMaxWeight, aMaxSquaredWeight ) ) { EDA_ANGLE testAngle = this->AngleBetweenStartAndEnd( pc.a2 ); if( testAngle < this->GetEndAngle() ) result.push_back( pc ); } if( result.size() < 2 ) { CU_SHAPE_CIRCLE csc1( this->GetStartPoint(), this->GetWidth() / 2 ); CU_SHAPE_CIRCLE csc2( this->GetEndPoint(), this->GetWidth() / 2 ); for( const PATH_CONNECTION& pc : this->Paths( csc1, aMaxWeight, aMaxSquaredWeight ) ) result.push_back( pc ); for( const PATH_CONNECTION& pc : this->Paths( csc2, aMaxWeight, aMaxSquaredWeight ) ) result.push_back( pc ); } return result; } std::vector CU_SHAPE_ARC::Paths( const BE_SHAPE_ARC& aS2, double aMaxWeight, double aMaxSquaredWeight ) const { std::vector result; VECTOR2I beArcPos = aS2.GetPos(); int beArcRadius = aS2.GetRadius(); EDA_ANGLE beArcStartAngle = aS2.GetStartAngle(); EDA_ANGLE beArcEndAngle = aS2.GetEndAngle(); BE_SHAPE_CIRCLE bsc( aS2.GetPos(), aS2.GetRadius() ); for( const PATH_CONNECTION& pc : this->Paths( bsc, aMaxWeight, aMaxSquaredWeight ) ) { EDA_ANGLE testAngle = aS2.AngleBetweenStartAndEnd( pc.a2 ); if( testAngle < aS2.GetEndAngle() ) result.push_back( pc ); } if( result.size() < 2 ) { BE_SHAPE_POINT bsp1( aS2.GetStartPoint() ); BE_SHAPE_POINT bsp2( aS2.GetEndPoint() ); for( const PATH_CONNECTION& pc : this->Paths( bsp1, aMaxWeight, aMaxSquaredWeight ) ) { if( !segmentIntersectsArc( pc.a1, pc.a2, beArcPos, beArcRadius, beArcStartAngle, beArcEndAngle, nullptr ) ) { result.push_back( pc ); } } for( const PATH_CONNECTION& pc : this->Paths( bsp2, aMaxWeight, aMaxSquaredWeight ) ) { if( !segmentIntersectsArc( pc.a1, pc.a2, beArcPos, beArcRadius, beArcStartAngle, beArcEndAngle, nullptr ) ) { result.push_back( pc ); } } } return result; } std::vector CU_SHAPE_CIRCLE::Paths( const BE_SHAPE_POINT& aS2, double aMaxWeight, double aMaxSquaredWeight ) const { std::vector result; double R = this->GetRadius(); VECTOR2I center = this->GetPos(); VECTOR2I point = aS2.GetPos(); double weight = ( center - point ).EuclideanNorm() - R; if( weight > aMaxWeight ) return result; PATH_CONNECTION pc; pc.weight = std::max( weight, 0.0 ); pc.a2 = point; pc.a1 = center + ( point - center ).Resize( R ); result.push_back( pc ); return result; } std::vector CU_SHAPE_CIRCLE::Paths( const CU_SHAPE_CIRCLE& aS2, double aMaxWeight, double aMaxSquaredWeight ) const { std::vector result; double R1 = this->GetRadius(); double R2 = aS2.GetRadius(); VECTOR2I C1 = this->GetPos(); VECTOR2I C2 = aS2.GetPos(); if( ( C1 - C2 ).SquaredEuclideanNorm() < ( R1 - R2 ) * ( R1 - R2 ) ) { // One of the circles is inside the other return result; } double weight = ( C1 - C2 ).EuclideanNorm() - R1 - R2; if( weight > aMaxWeight || weight < 0 ) return result; PATH_CONNECTION pc; pc.weight = std::max( weight, 0.0 ); pc.a1 = ( C2 - C1 ).Resize( R1 ) + C1; pc.a2 = ( C1 - C2 ).Resize( R2 ) + C2; result.push_back( pc ); return result; } std::vector CU_SHAPE_SEGMENT::Paths( const CU_SHAPE_CIRCLE& aS2, double aMaxWeight, double aMaxSquaredWeight ) const { std::vector result; VECTOR2I s_start = this->GetStart(); VECTOR2I s_end = this->GetEnd(); double halfWidth = this->GetWidth() / 2; EDA_ANGLE trackAngle( s_end - s_start ); VECTOR2I pointPos = aS2.GetPos(); double length = ( s_start - s_end ).EuclideanNorm(); double projectedPos = cos( trackAngle.AsRadians() ) * ( pointPos.x - s_start.x ) + sin( trackAngle.AsRadians() ) * ( pointPos.y - s_start.y ); if( ( projectedPos <= 0 ) || ( s_start == s_end ) ) { CU_SHAPE_CIRCLE csc( s_start, halfWidth ); return csc.Paths( aS2, aMaxWeight, aMaxSquaredWeight ); } if( projectedPos >= length ) { CU_SHAPE_CIRCLE csc( s_end, halfWidth ); return csc.Paths( aS2, aMaxWeight, aMaxSquaredWeight ); } double radius = aS2.GetRadius(); double trackSide = ( s_end - s_start ).Cross( pointPos - s_start ) > 0 ? 1 : -1; PATH_CONNECTION pc; pc.a1 = s_start + ( s_end - s_start ).Resize( projectedPos ) + ( s_end - s_start ).Perpendicular().Resize( halfWidth ) * trackSide; pc.a2 = ( pc.a1 - pointPos ).Resize( radius ) + pointPos; pc.weight = ( pc.a2 - pc.a1 ).SquaredEuclideanNorm(); if( pc.weight <= aMaxSquaredWeight ) { pc.weight = sqrt( pc.weight ); result.push_back( pc ); } return result; } std::vector CU_SHAPE_CIRCLE::Paths( const CU_SHAPE_ARC& aS2, double aMaxWeight, double aMaxSquaredWeight ) const { std::vector result; VECTOR2I circlePos = this->GetPos(); VECTOR2I arcPos = aS2.GetPos(); double circleRadius = this->GetRadius(); double arcRadius = aS2.GetRadius(); VECTOR2I startPoint = aS2.GetStartPoint(); VECTOR2I endPoint = aS2.GetEndPoint(); CU_SHAPE_CIRCLE csc( arcPos, arcRadius + aS2.GetWidth() / 2 ); if( ( circlePos - arcPos ).EuclideanNorm() > arcRadius + circleRadius ) { const std::vector& pcs = this->Paths( csc, aMaxWeight, aMaxSquaredWeight ); if( pcs.size() == 1 ) { EDA_ANGLE testAngle = aS2.AngleBetweenStartAndEnd( pcs[0].a2 ); if( testAngle < aS2.GetEndAngle() ) { result.push_back( pcs[0] ); return result; } } } CU_SHAPE_CIRCLE csc1( startPoint, aS2.GetWidth() / 2 ); CU_SHAPE_CIRCLE csc2( endPoint, aS2.GetWidth() / 2 ); PATH_CONNECTION* bestPath = nullptr; std::vector pcs1 = this->Paths( csc1, aMaxWeight, aMaxSquaredWeight ); std::vector pcs2 = this->Paths( csc2, aMaxWeight, aMaxSquaredWeight ); for( PATH_CONNECTION& pc : pcs1 ) { if( !bestPath || ( ( bestPath->weight > pc.weight ) && ( pc.weight > 0 ) ) ) bestPath = &pc; } for( PATH_CONNECTION& pc : pcs2 ) { if( !bestPath || ( ( bestPath->weight > pc.weight ) && ( pc.weight > 0 ) ) ) bestPath = &pc; } // If the circle center is insde the arc ring PATH_CONNECTION pc3; if( ( circlePos - arcPos ).SquaredEuclideanNorm() < arcRadius * arcRadius ) { if( circlePos != arcPos ) // The best path is already found otherwise { EDA_ANGLE testAngle = aS2.AngleBetweenStartAndEnd( circlePos ); if( testAngle < aS2.GetEndAngle() ) { pc3.weight = std::max( arcRadius - ( circlePos - arcPos ).EuclideanNorm() - circleRadius, 0.0 ); pc3.a1 = circlePos + ( circlePos - arcPos ).Resize( circleRadius ); pc3.a2 = arcPos + ( circlePos - arcPos ).Resize( arcRadius - aS2.GetWidth() / 2 ); if( !bestPath || ( ( bestPath->weight > pc3.weight ) && ( pc3.weight > 0 ) ) ) bestPath = &pc3; } } } if( bestPath && bestPath->weight > 0 ) { result.push_back( *bestPath ); } return result; } std::vector CU_SHAPE_SEGMENT::Paths( const CU_SHAPE_ARC& aS2, double aMaxWeight, double aMaxSquaredWeight ) const { std::vector result; VECTOR2I s_start = this->GetStart(); VECTOR2I s_end = this->GetEnd(); double halfWidth1 = this->GetWidth() / 2; VECTOR2I arcPos = aS2.GetPos(); double arcRadius = aS2.GetRadius(); double halfWidth2 = aS2.GetWidth() / 2; CU_SHAPE_CIRCLE csc( arcPos, arcRadius + halfWidth2 ); std::vector pcs; pcs = this->Paths( csc, aMaxWeight, aMaxSquaredWeight ); if( pcs.size() < 1 ) return result; VECTOR2I circlePoint; EDA_ANGLE testAngle; if( pcs.size() > 0 ) { circlePoint = pcs[0].a1; testAngle = ( aS2.AngleBetweenStartAndEnd( pcs[0].a1 ) ); } if( testAngle < aS2.GetEndAngle() && pcs.size() > 0 ) { result.push_back( pcs[0] ); return result; } CU_SHAPE_CIRCLE csc1( aS2.GetStartPoint(), halfWidth2 ); CU_SHAPE_CIRCLE csc2( aS2.GetEndPoint(), halfWidth2 ); PATH_CONNECTION* bestPath = nullptr; for( PATH_CONNECTION& pc : this->Paths( csc1, aMaxWeight, aMaxSquaredWeight ) ) { if( !bestPath || ( bestPath->weight > pc.weight ) ) bestPath = &pc; } for( PATH_CONNECTION& pc : this->Paths( csc2, aMaxWeight, aMaxSquaredWeight ) ) { if( !bestPath || ( bestPath->weight > pc.weight ) ) bestPath = &pc; } CU_SHAPE_CIRCLE csc3( s_start, halfWidth1 ); CU_SHAPE_CIRCLE csc4( s_end, halfWidth1 ); for( PATH_CONNECTION& pc : csc3.Paths( aS2, aMaxWeight, aMaxSquaredWeight ) ) { if( !bestPath || ( bestPath->weight > pc.weight ) ) bestPath = &pc; } for( PATH_CONNECTION& pc : csc4.Paths( aS2, aMaxWeight, aMaxSquaredWeight ) ) { if( !bestPath || ( bestPath->weight > pc.weight ) ) bestPath = &pc; } if( bestPath ) result.push_back( *bestPath ); return result; } // Function to compute the projection of point P onto the line segment AB VECTOR2I closestPointOnSegment( const VECTOR2I& A, const VECTOR2I& B, const VECTOR2I& P ) { if( A == B ) return A; if( A == P ) return A; VECTOR2I AB = B - A; VECTOR2I AP = P - A; double t = float( AB.Dot( AP ) ) / float( AB.SquaredEuclideanNorm() ); // Clamp t to the range [0, 1] to restrict the projection to the segment t = std::max( 0.0, std::min( 1.0, t ) ); return A + ( AB * t ); } std::vector CU_SHAPE_SEGMENT::Paths( const CU_SHAPE_SEGMENT& aS2, double aMaxWeight, double aMaxSquaredWeight ) const { std::vector result; VECTOR2I A( this->GetStart() ); VECTOR2I B( this->GetEnd() ); double halfWidth1 = this->GetWidth() / 2; VECTOR2I C( aS2.GetStart() ); VECTOR2I D( aS2.GetEnd() ); double halfWidth2 = aS2.GetWidth() / 2; VECTOR2I P1 = closestPointOnSegment( A, B, C ); VECTOR2I P2 = closestPointOnSegment( A, B, D ); VECTOR2I P3 = closestPointOnSegment( C, D, A ); VECTOR2I P4 = closestPointOnSegment( C, D, B ); // Calculate all possible squared distances between the segments double dist1 = ( P1 - C ).SquaredEuclideanNorm(); double dist2 = ( P2 - D ).SquaredEuclideanNorm(); double dist3 = ( P3 - A ).SquaredEuclideanNorm(); double dist4 = ( P4 - B ).SquaredEuclideanNorm(); // Find the minimum squared distance and update closest points double min_dist = dist1; VECTOR2I closest1 = P1; VECTOR2I closest2 = C; if( dist2 < min_dist ) { min_dist = dist2; closest1 = P2; closest2 = D; } if( dist3 < min_dist ) { min_dist = dist3; closest1 = A; closest2 = P3; } if( dist4 < min_dist ) { min_dist = dist4; closest1 = B; closest2 = P4; } PATH_CONNECTION pc; pc.a1 = closest1 + ( closest2 - closest1 ).Resize( halfWidth1 ); pc.a2 = closest2 + ( closest1 - closest2 ).Resize( halfWidth2 ); pc.weight = std::max( sqrt( min_dist ) - halfWidth1 - halfWidth2, 0.0 ); if( pc.weight <= aMaxWeight ) result.push_back( pc ); return result; } std::vector CU_SHAPE_CIRCLE::Paths( const BE_SHAPE_CIRCLE& aS2, double aMaxWeight, double aMaxSquaredWeight ) const { std::vector result; double R1 = this->GetRadius(); double R2 = aS2.GetRadius(); VECTOR2I center1 = this->GetPos(); VECTOR2I center2 = aS2.GetPos(); double dist = ( center1 - center2 ).EuclideanNorm(); if( dist > aMaxWeight || dist == 0 ) return result; double weight = sqrt( dist * dist - R2 * R2 ) - R1; double theta = asin( R2 / dist ); double psi = acos( R2 / dist ); if( weight > aMaxWeight ) return result; PATH_CONNECTION pc; pc.weight = std::max( weight, 0.0 ); double circleAngle = EDA_ANGLE( center2 - center1 ).AsRadians(); VECTOR2I pStart; VECTOR2I pEnd; pStart = VECTOR2I( R1 * cos( theta + circleAngle ), R1 * sin( theta + circleAngle ) ); pStart += center1; pEnd = VECTOR2I( -R2 * cos( psi - circleAngle ), R2 * sin( psi - circleAngle ) ); pEnd += center2; pc.a1 = pStart; pc.a2 = pEnd; result.push_back( pc ); pStart = VECTOR2I( R1 * cos( -theta + circleAngle ), R1 * sin( -theta + circleAngle ) ); pStart += center1; pEnd = VECTOR2I( -R2 * cos( -psi - circleAngle ), R2 * sin( -psi - circleAngle ) ); pEnd += center2; pc.a1 = pStart; pc.a2 = pEnd; result.push_back( pc ); return result; } std::vector CU_SHAPE_ARC::Paths( const BE_SHAPE_POINT& aS2, double aMaxWeight, double aMaxSquaredWeight ) const { std::vector result; VECTOR2I point = aS2.GetPos(); VECTOR2I arcCenter = this->GetPos(); double radius = this->GetRadius(); double width = this->GetWidth(); EDA_ANGLE angle( point - arcCenter ); while( angle < this->GetStartAngle() ) angle += ANGLE_360; while( angle > this->GetEndAngle() + ANGLE_360 ) angle -= ANGLE_360; if( angle < this->GetEndAngle() ) { if( ( point - arcCenter ).SquaredEuclideanNorm() > radius * radius ) { CU_SHAPE_CIRCLE circle( arcCenter, radius + width / 2 ); return circle.Paths( aS2, aMaxWeight, aMaxSquaredWeight ); } else { PATH_CONNECTION pc; pc.weight = std::max( ( radius - width / 2 ) - ( point - arcCenter ).EuclideanNorm(), 0.0 ); pc.a1 = ( point - arcCenter ).Resize( radius - width / 2 ) + arcCenter; pc.a2 = point; if( pc.weight > 0 && pc.weight < aMaxWeight ) result.push_back( pc ); return result; } } else { VECTOR2I nearestPoint; if( ( point - this->GetStartPoint() ).SquaredEuclideanNorm() > ( point - this->GetEndPoint() ).SquaredEuclideanNorm() ) { nearestPoint = this->GetEndPoint(); } else { nearestPoint = this->GetStartPoint(); } CU_SHAPE_CIRCLE circle( nearestPoint, width / 2 ); return circle.Paths( aS2, aMaxWeight, aMaxSquaredWeight ); } return result; } std::vector CU_SHAPE_ARC::Paths( const CU_SHAPE_ARC& aS2, double aMaxWeight, double aMaxSquaredWeight ) const { std::vector result; double R1 = this->GetRadius(); double R2 = aS2.GetRadius(); VECTOR2I C1 = this->GetPos(); VECTOR2I C2 = aS2.GetPos(); PATH_CONNECTION bestPath; bestPath.weight = std::numeric_limits::infinity(); CU_SHAPE_CIRCLE csc1( C1, R1 + this->GetWidth() / 2 ); CU_SHAPE_CIRCLE csc2( C2, R2 + aS2.GetWidth() / 2 ); CU_SHAPE_CIRCLE csc3( this->GetStartPoint(), this->GetWidth() / 2 ); CU_SHAPE_CIRCLE csc4( this->GetEndPoint(), this->GetWidth() / 2 ); CU_SHAPE_CIRCLE csc5( aS2.GetStartPoint(), aS2.GetWidth() / 2 ); CU_SHAPE_CIRCLE csc6( aS2.GetEndPoint(), aS2.GetWidth() / 2 ); for( const std::vector& pcs : { csc1.Paths( csc2, aMaxWeight, aMaxSquaredWeight ), this->Paths( csc2, aMaxWeight, aMaxSquaredWeight ), csc1.Paths( aS2, aMaxWeight, aMaxSquaredWeight ) } ) { for( const PATH_CONNECTION& pc : pcs ) { EDA_ANGLE testAngle1 = this->AngleBetweenStartAndEnd( pc.a1 ); EDA_ANGLE testAngle2 = aS2.AngleBetweenStartAndEnd( pc.a2 ); if( testAngle1 < this->GetEndAngle() && testAngle2 < aS2.GetEndAngle() && bestPath.weight > pc.weight ) bestPath = pc; } } for( const std::vector& pcs : { this->Paths( csc5, aMaxWeight, aMaxSquaredWeight ), this->Paths( csc6, aMaxWeight, aMaxSquaredWeight ), csc3.Paths( aS2, aMaxWeight, aMaxSquaredWeight ), csc4.Paths( aS2, aMaxWeight, aMaxSquaredWeight ) } ) { for( const PATH_CONNECTION& pc : pcs ) { if( bestPath.weight > pc.weight ) bestPath = pc; } } if( bestPath.weight != std::numeric_limits::infinity() ) result.push_back( bestPath ); return result; } bool segmentIntersectsCircle( const VECTOR2I& p1, const VECTOR2I& p2, const VECTOR2I& center, double radius, std::vector* aIntersectPoints ) { SEG segment( p1, p2 ); CIRCLE circle( center, radius ); std::vector intersectionPoints; INTERSECTABLE_GEOM geom1 = segment; INTERSECTABLE_GEOM geom2 = circle; INTERSECTION_VISITOR visitor( geom2, intersectionPoints ); std::visit( visitor, geom1 ); if( aIntersectPoints ) { for( VECTOR2I& point : intersectionPoints ) aIntersectPoints->push_back( point ); } return intersectionPoints.size() > 0; } bool SegmentIntersectsBoard( const VECTOR2I& aP1, const VECTOR2I& aP2, const std::vector& aBe, const std::vector& aDontTestAgainst, int aMinGrooveWidth ) { std::vector intersectionPoints; bool TestGrooveWidth = aMinGrooveWidth > 0; for( BOARD_ITEM* be : aBe ) { if( count( aDontTestAgainst.begin(), aDontTestAgainst.end(), be ) > 0 ) continue; PCB_SHAPE* d = static_cast( be ); if( !d ) continue; switch( d->GetShape() ) { case SHAPE_T::SEGMENT: { bool intersects = segments_intersect( aP1, aP2, d->GetStart(), d->GetEnd(), &intersectionPoints ); if( intersects && !TestGrooveWidth ) return false; break; } case SHAPE_T::RECTANGLE: { VECTOR2I c1 = d->GetStart(); VECTOR2I c2( d->GetStart().x, d->GetEnd().y ); VECTOR2I c3 = d->GetEnd(); VECTOR2I c4( d->GetEnd().x, d->GetStart().y ); bool intersects = false; intersects |= segments_intersect( aP1, aP2, c1, c2, &intersectionPoints ); intersects |= segments_intersect( aP1, aP2, c2, c3, &intersectionPoints ); intersects |= segments_intersect( aP1, aP2, c3, c4, &intersectionPoints ); intersects |= segments_intersect( aP1, aP2, c4, c1, &intersectionPoints ); if( intersects && !TestGrooveWidth ) return false; break; } case SHAPE_T::POLY: { std::vector points; d->DupPolyPointsList( points ); if( points.size() < 2 ) break; VECTOR2I prevPoint = points.back(); bool intersects = false; for( const VECTOR2I& p : points ) { intersects |= segments_intersect( aP1, aP2, prevPoint, p, &intersectionPoints ); prevPoint = p; } if( intersects && !TestGrooveWidth ) return false; break; } case SHAPE_T::CIRCLE: { VECTOR2I center = d->GetCenter(); double radius = d->GetRadius(); bool intersects = segmentIntersectsCircle( aP1, aP2, center, radius, &intersectionPoints ); if( intersects && !TestGrooveWidth ) return false; break; } case SHAPE_T::ARC: { VECTOR2I center = d->GetCenter(); double radius = d->GetRadius(); EDA_ANGLE A, B; d->CalcArcAngles( A, B ); bool intersects = segmentIntersectsArc( aP1, aP2, center, radius, A, B, &intersectionPoints ); if( intersects && !TestGrooveWidth ) return false; break; } default: break; } } if( intersectionPoints.size() <= 0 ) return true; if( intersectionPoints.size() % 2 != 0 ) return false; // Should not happen if the start and end are both on the board int minx = intersectionPoints[0].x; int maxx = intersectionPoints[0].x; int miny = intersectionPoints[0].y; int maxy = intersectionPoints[0].y; for( const VECTOR2I& v : intersectionPoints ) { minx = v.x < minx ? v.x : minx; maxx = v.x > maxx ? v.x : maxx; miny = v.x < miny ? v.x : miny; maxy = v.x > maxy ? v.x : maxy; } if( abs( maxx - minx ) > abs( maxy - miny ) ) { std::sort( intersectionPoints.begin(), intersectionPoints.end(), []( const VECTOR2I& a, const VECTOR2I& b ) { return a.x > b.x; } ); } else { std::sort( intersectionPoints.begin(), intersectionPoints.end(), []( const VECTOR2I& a, const VECTOR2I& b ) { return a.y > b.y; } ); } int GVSquared = aMinGrooveWidth * aMinGrooveWidth; for( size_t i = 0; i < intersectionPoints.size(); i += 2 ) { if( intersectionPoints[i].SquaredDistance( intersectionPoints[i + 1] ) > GVSquared ) return false; } return true; } std::vector GetPaths( CREEP_SHAPE* aS1, CREEP_SHAPE* aS2, double aMaxWeight ) { double maxWeight = aMaxWeight; double maxWeightSquared = maxWeight * maxWeight; std::vector result; CU_SHAPE_SEGMENT* cusegment1 = dynamic_cast( aS1 ); CU_SHAPE_SEGMENT* cusegment2 = dynamic_cast( aS2 ); CU_SHAPE_CIRCLE* cucircle1 = dynamic_cast( aS1 ); CU_SHAPE_CIRCLE* cucircle2 = dynamic_cast( aS2 ); CU_SHAPE_ARC* cuarc1 = dynamic_cast( aS1 ); CU_SHAPE_ARC* cuarc2 = dynamic_cast( aS2 ); BE_SHAPE_POINT* bepoint1 = dynamic_cast( aS1 ); BE_SHAPE_POINT* bepoint2 = dynamic_cast( aS2 ); BE_SHAPE_CIRCLE* becircle1 = dynamic_cast( aS1 ); BE_SHAPE_CIRCLE* becircle2 = dynamic_cast( aS2 ); BE_SHAPE_ARC* bearc1 = dynamic_cast( aS1 ); BE_SHAPE_ARC* bearc2 = dynamic_cast( aS2 ); // Cu to Cu if( cuarc1 && cuarc2 ) return cuarc1->Paths( *cuarc2, maxWeight, maxWeightSquared ); if( cuarc1 && cucircle2 ) return cuarc1->Paths( *cucircle2, maxWeight, maxWeightSquared ); if( cuarc1 && cusegment2 ) return cuarc1->Paths( *cusegment2, maxWeight, maxWeightSquared ); if( cucircle1 && cuarc2 ) return cucircle1->Paths( *cuarc2, maxWeight, maxWeightSquared ); if( cucircle1 && cucircle2 ) return cucircle1->Paths( *cucircle2, maxWeight, maxWeightSquared ); if( cucircle1 && cusegment2 ) return cucircle1->Paths( *cusegment2, maxWeight, maxWeightSquared ); if( cusegment1 && cuarc2 ) return cusegment1->Paths( *cuarc2, maxWeight, maxWeightSquared ); if( cusegment1 && cucircle2 ) return cusegment1->Paths( *cucircle2, maxWeight, maxWeightSquared ); if( cusegment1 && cusegment2 ) return cusegment1->Paths( *cusegment2, maxWeight, maxWeightSquared ); // Cu to Be if( cuarc1 && bearc2 ) return cuarc1->Paths( *bearc2, maxWeight, maxWeightSquared ); if( cuarc1 && becircle2 ) return cuarc1->Paths( *becircle2, maxWeight, maxWeightSquared ); if( cuarc1 && bepoint2 ) return cuarc1->Paths( *bepoint2, maxWeight, maxWeightSquared ); if( cucircle1 && bearc2 ) return cucircle1->Paths( *bearc2, maxWeight, maxWeightSquared ); if( cucircle1 && becircle2 ) return cucircle1->Paths( *becircle2, maxWeight, maxWeightSquared ); if( cucircle1 && bepoint2 ) return cucircle1->Paths( *bepoint2, maxWeight, maxWeightSquared ); if( cusegment1 && bearc2 ) return cusegment1->Paths( *bearc2, maxWeight, maxWeightSquared ); if( cusegment1 && becircle2 ) return cusegment1->Paths( *becircle2, maxWeight, maxWeightSquared ); if( cusegment1 && bepoint2 ) return cusegment1->Paths( *bepoint2, maxWeight, maxWeightSquared ); // Reversed if( cuarc2 && bearc1 ) return bearc1->Paths( *cuarc2, maxWeight, maxWeightSquared ); if( cuarc2 && becircle1 ) return becircle1->Paths( *cuarc2, maxWeight, maxWeightSquared ); if( cuarc2 && bepoint1 ) return bepoint1->Paths( *cuarc2, maxWeight, maxWeightSquared ); if( cucircle2 && bearc1 ) return bearc1->Paths( *cucircle2, maxWeight, maxWeightSquared ); if( cucircle2 && becircle1 ) return becircle1->Paths( *cucircle2, maxWeight, maxWeightSquared ); if( cucircle2 && bepoint1 ) return bepoint1->Paths( *cucircle2, maxWeight, maxWeightSquared ); if( cusegment2 && bearc1 ) return bearc1->Paths( *cusegment2, maxWeight, maxWeightSquared ); if( cusegment2 && becircle1 ) return becircle1->Paths( *cusegment2, maxWeight, maxWeightSquared ); if( cusegment2 && bepoint1 ) return bepoint1->Paths( *cusegment2, maxWeight, maxWeightSquared ); // Be to Be if( bearc1 && bearc2 ) return bearc1->Paths( *bearc2, maxWeight, maxWeightSquared ); if( bearc1 && becircle2 ) return bearc1->Paths( *becircle2, maxWeight, maxWeightSquared ); if( bearc1 && bepoint2 ) return bearc1->Paths( *bepoint2, maxWeight, maxWeightSquared ); if( becircle1 && bearc2 ) return becircle1->Paths( *bearc2, maxWeight, maxWeightSquared ); if( becircle1 && becircle2 ) return becircle1->Paths( *becircle2, maxWeight, maxWeightSquared ); if( becircle1 && bepoint2 ) return becircle1->Paths( *bepoint2, maxWeight, maxWeightSquared ); if( bepoint1 && bearc2 ) return bepoint1->Paths( *bearc2, maxWeight, maxWeightSquared ); if( bepoint1 && becircle2 ) return bepoint1->Paths( *becircle2, maxWeight, maxWeightSquared ); if( bepoint1 && bepoint2 ) return bepoint1->Paths( *bepoint2, maxWeight, maxWeightSquared ); return result; } double CREEPAGE_GRAPH::Solve( std::shared_ptr& aFrom, std::shared_ptr& aTo, std::vector>& aResult ) // Change to vector of pointers { if( !aFrom || !aTo ) return 0; if( aFrom == aTo ) return 0; // Dijkstra's algorithm for shortest path std::unordered_map distances; std::unordered_map previous; auto cmp = [&distances]( GRAPH_NODE* left, GRAPH_NODE* right ) { double distLeft = distances[left]; double distRight = distances[right]; if( distLeft == distRight ) return left > right; // Compare addresses to avoid ties. return distLeft > distRight; }; std::priority_queue, decltype( cmp )> pq( cmp ); // Initialize distances to infinity for all nodes except the starting node for( const std::shared_ptr& node : m_nodes ) { if( node != nullptr ) distances[node.get()] = std::numeric_limits::infinity(); // Set to infinity } distances[aFrom.get()] = 0.0; distances[aTo.get()] = std::numeric_limits::infinity(); pq.push( aFrom.get() ); // Dijkstra's main loop while( !pq.empty() ) { GRAPH_NODE* current = pq.top(); pq.pop(); if( current == aTo.get() ) { break; // Shortest path found } // Traverse neighbors for( const std::shared_ptr& connection : current->m_node_conns ) { GRAPH_NODE* neighbor = ( connection->n1 ).get() == current ? ( connection->n2 ).get() : ( connection->n1 ).get(); if( !neighbor ) continue; // Ignore connections with negative weights as Dijkstra doesn't support them. if( connection->m_path.weight < 0.0 ) { wxLogTrace( "CREEPAGE", "Negative weight connection found. Ignoring connection." ); continue; } double alt = distances[current] + connection->m_path.weight; // Calculate alternative path cost if( alt < distances[neighbor] ) { distances[neighbor] = alt; previous[neighbor] = current; pq.push( neighbor ); } } } double pathWeight = distances[aTo.get()]; // If aTo is unreachable, return infinity if( pathWeight == std::numeric_limits::infinity() ) return std::numeric_limits::infinity(); // Trace back the path from aTo to aFrom GRAPH_NODE* step = aTo.get(); while( step != aFrom.get() ) { GRAPH_NODE* prevNode = previous[step]; for( const std::shared_ptr& node_conn : step->m_node_conns ) { if( ( ( node_conn->n1 ).get() == prevNode && ( node_conn->n2 ).get() == step ) || ( ( node_conn->n1 ).get() == step && ( node_conn->n2 ).get() == prevNode ) ) { aResult.push_back( node_conn ); break; } } step = prevNode; } return pathWeight; } void CREEPAGE_GRAPH::Addshape( const SHAPE& aShape, std::shared_ptr& aConnectTo, BOARD_ITEM* aParent ) { CREEP_SHAPE* newshape = nullptr; if( !aConnectTo ) return; switch( aShape.Type() ) { case SH_SEGMENT: { const SHAPE_SEGMENT& segment = dynamic_cast( aShape ); CU_SHAPE_SEGMENT* cuseg = new CU_SHAPE_SEGMENT( segment.GetSeg().A, segment.GetSeg().B, segment.GetWidth() ); newshape = dynamic_cast( cuseg ); break; } case SH_CIRCLE: { const SHAPE_CIRCLE& circle = dynamic_cast( aShape ); CU_SHAPE_CIRCLE* cucircle = new CU_SHAPE_CIRCLE( circle.GetCenter(), circle.GetRadius() ); newshape = dynamic_cast( cucircle ); break; } case SH_ARC: { const SHAPE_ARC& arc = dynamic_cast( aShape ); EDA_ANGLE alpha, beta; VECTOR2I start, end; EDA_SHAPE edaArc( SHAPE_T::ARC, 0, FILL_T::NO_FILL ); if( arc.IsClockwise() ) { edaArc.SetArcGeometry( arc.GetP0(), arc.GetArcMid(), arc.GetP1() ); start = arc.GetP0(); end = arc.GetP1(); } else { edaArc.SetArcGeometry( arc.GetP1(), arc.GetArcMid(), arc.GetP0() ); start = arc.GetP1(); end = arc.GetP0(); } edaArc.CalcArcAngles( alpha, beta ); CU_SHAPE_ARC* cuarc = new CU_SHAPE_ARC( edaArc.getCenter(), edaArc.GetRadius(), alpha, beta, arc.GetP0(), arc.GetP1() ); cuarc->SetWidth( arc.GetWidth() ); newshape = dynamic_cast( cuarc ); break; } case SH_COMPOUND: { int nbShapes = static_cast( &aShape )->Shapes().size(); for( const SHAPE* subshape : ( static_cast( &aShape )->Shapes() ) ) { if( subshape ) { // We don't want to add shape for the inner rectangle of rounded rectangles if( !( ( subshape->Type() == SH_RECT ) && ( nbShapes == 5 ) ) ) Addshape( *subshape, aConnectTo, aParent ); } } break; } case SH_POLY_SET: { const SHAPE_POLY_SET& polySet = dynamic_cast( aShape ); for( auto it = polySet.CIterateSegmentsWithHoles(); it; it++ ) { const SEG object = *it; SHAPE_SEGMENT segment( object.A, object.B ); Addshape( segment, aConnectTo, aParent ); } break; } case SH_LINE_CHAIN: { const SHAPE_LINE_CHAIN& lineChain = dynamic_cast( aShape ); VECTOR2I prevPoint = lineChain.CLastPoint(); for( const VECTOR2I& point : lineChain.CPoints() ) { SHAPE_SEGMENT segment( point, prevPoint ); prevPoint = point; Addshape( segment, aConnectTo, aParent ); } break; } case SH_RECT: { const SHAPE_RECT& rect = dynamic_cast( aShape ); VECTOR2I point0 = rect.GetPosition(); VECTOR2I point1 = rect.GetPosition() + VECTOR2I( rect.GetSize().x, 0 ); VECTOR2I point2 = rect.GetPosition() + rect.GetSize(); VECTOR2I point3 = rect.GetPosition() + VECTOR2I( 0, rect.GetSize().y ); Addshape( SHAPE_SEGMENT( point0, point1 ), aConnectTo, aParent ); Addshape( SHAPE_SEGMENT( point1, point2 ), aConnectTo, aParent ); Addshape( SHAPE_SEGMENT( point2, point3 ), aConnectTo, aParent ); Addshape( SHAPE_SEGMENT( point3, point0 ), aConnectTo, aParent ); break; } default: break; } if( !newshape ) return; std::shared_ptr gnShape = nullptr; newshape->SetParent( aParent ); switch( aShape.Type() ) { case SH_SEGMENT: gnShape = AddNode( GRAPH_NODE::SEGMENT, newshape, newshape->GetPos() ); break; case SH_CIRCLE: gnShape = AddNode( GRAPH_NODE::CIRCLE, newshape, newshape->GetPos() ); break; case SH_ARC: gnShape = AddNode( GRAPH_NODE::ARC, newshape, newshape->GetPos() ); break; default: break; } if( gnShape ) { m_shapeCollection.push_back( newshape ); gnShape->m_net = aConnectTo->m_net; std::shared_ptr gc = AddConnection( gnShape, aConnectTo ); if( gc ) gc->m_path.m_show = false; } else { delete newshape; newshape = nullptr; } } void CREEPAGE_GRAPH::GeneratePaths( double aMaxWeight, PCB_LAYER_ID aLayer ) { std::vector> nodes; std::mutex nodes_lock; thread_pool& tp = GetKiCadThreadPool(); std::copy_if( m_nodes.begin(), m_nodes.end(), std::back_inserter( nodes ), [&]( const std::shared_ptr& gn ) { return !!gn && gn->m_parent && gn->m_connectDirectly && ( gn->m_type != GRAPH_NODE::TYPE::VIRTUAL ); } ); std::sort( nodes.begin(), nodes.end(), []( const std::shared_ptr& gn1, const std::shared_ptr& gn2 ) { return ( gn1->m_parent < gn2->m_parent ) || ( gn1->m_parent == gn2->m_parent && gn1->m_net < gn2->m_net ); } ); // Build parent -> net -> nodes mapping for efficient filtering std::unordered_map>>> parent_net_groups; std::vector parent_keys; for( const auto& gn : nodes ) { const BOARD_ITEM* parent = gn->m_parent->GetParent(); if( parent_net_groups[parent].empty() ) parent_keys.push_back( parent ); parent_net_groups[parent][gn->m_net].push_back( gn ); } // Generate work items: compare nodes between different parents only struct WorkItem { std::shared_ptr node1, node2; }; std::vector work_items; for( size_t i = 0; i < parent_keys.size(); ++i ) { for( size_t j = i + 1; j < parent_keys.size(); ++j ) { const auto& group1_nets = parent_net_groups[parent_keys[i]]; const auto& group2_nets = parent_net_groups[parent_keys[j]]; for( const auto& [net1, nodes1] : group1_nets ) { for( const auto& [net2, nodes2] : group2_nets ) { // Skip if same net and both nets have only conductive nodes if( net1 == net2 ) { bool all_conductive_1 = std::all_of( nodes1.begin(), nodes1.end(), []( const auto& n ) { return n->m_parent->IsConductive(); } ); bool all_conductive_2 = std::all_of( nodes2.begin(), nodes2.end(), []( const auto& n ) { return n->m_parent->IsConductive(); } ); if( all_conductive_1 && all_conductive_2 ) continue; } // Add all node pairs from these net groups for( const auto& gn1 : nodes1 ) for( const auto& gn2 : nodes2 ) work_items.push_back( { gn1, gn2 } ); } } } } auto processWorkItems = [&]( size_t idx ) -> bool { auto [gn1, gn2] = work_items[idx]; for( PATH_CONNECTION pc : GetPaths( gn1->m_parent, gn2->m_parent, aMaxWeight ) ) { std::vector IgnoreForTest = { gn1->m_parent->GetParent(), gn2->m_parent->GetParent() }; if( !pc.isValid( m_board, aLayer, m_boardEdge, IgnoreForTest, m_boardOutline, { false, true }, m_minGrooveWidth ) ) continue; std::shared_ptr connect1 = gn1, connect2 = gn2; std::lock_guard lock( nodes_lock ); // Handle non-point node1 if( gn1->m_parent->GetType() != CREEP_SHAPE::TYPE::POINT ) { auto gnt1 = AddNode( GRAPH_NODE::POINT, gn1->m_parent, pc.a1 ); gnt1->m_connectDirectly = false; connect1 = gnt1; if( gn1->m_parent->IsConductive() ) { if( auto gc = AddConnection( gn1, gnt1 ) ) gc->m_path.m_show = false; } } // Handle non-point node2 if( gn2->m_parent->GetType() != CREEP_SHAPE::TYPE::POINT ) { auto gnt2 = AddNode( GRAPH_NODE::POINT, gn2->m_parent, pc.a2 ); gnt2->m_connectDirectly = false; connect2 = gnt2; if( gn2->m_parent->IsConductive() ) { if( auto gc = AddConnection( gn2, gnt2 ) ) gc->m_path.m_show = false; } } AddConnection( connect1, connect2, pc ); } return true; }; // If the number of tasks is high enough, this indicates that the calling process // has already parallelized the work, so we can process all items in one go. if( tp.get_tasks_total() >= tp.get_thread_count() - 4 ) { for( size_t ii = 0; ii < work_items.size(); ii++ ) processWorkItems( ii ); } else { auto ret = tp.submit_loop( 0, work_items.size(), processWorkItems ); for( size_t ii = 0; ii < ret.size(); ii++ ) { auto& r = ret[ii]; if( !r.valid() ) continue; while( r.wait_for( std::chrono::milliseconds( 100 ) ) != std::future_status::ready ){} } } } void CREEPAGE_GRAPH::Trim( double aWeightLimit ) { std::vector> toRemove; // Collect connections to remove for( std::shared_ptr& gc : m_connections ) { if( gc && ( gc->m_path.weight > aWeightLimit ) ) toRemove.push_back( gc ); } // Remove collected connections for( const std::shared_ptr& gc : toRemove ) RemoveConnection( gc ); } void CREEPAGE_GRAPH::RemoveConnection( const std::shared_ptr& aGc, bool aDelete ) { if( !aGc ) return; for( std::shared_ptr gn : { aGc->n1, aGc->n2 } ) { if( gn ) { gn->m_node_conns.erase( aGc ); if( gn->m_node_conns.empty() && aDelete ) { auto it = std::find_if( m_nodes.begin(), m_nodes.end(), [&gn]( const std::shared_ptr& node ) { return node.get() == gn.get(); } ); if( it != m_nodes.end() ) m_nodes.erase( it ); m_nodeset.erase( gn ); } } } if( aDelete ) { // Remove the connection from the graph's connections m_connections.erase( std::remove( m_connections.begin(), m_connections.end(), aGc ), m_connections.end() ); } } std::shared_ptr CREEPAGE_GRAPH::AddNode( GRAPH_NODE::TYPE aType, CREEP_SHAPE* parent, const VECTOR2I& pos ) { std::shared_ptr gn = FindNode( aType, parent, pos ); if( gn ) return gn; gn = std::make_shared( aType, parent, pos ); m_nodes.push_back( gn ); m_nodeset.insert( gn ); return gn; } std::shared_ptr CREEPAGE_GRAPH::AddNodeVirtual() { //Virtual nodes are always unique, do not try to find them std::shared_ptr gn = std::make_shared( GRAPH_NODE::TYPE::VIRTUAL, nullptr ); m_nodes.push_back( gn ); m_nodeset.insert( gn ); return gn; } std::shared_ptr CREEPAGE_GRAPH::AddConnection( std::shared_ptr& aN1, std::shared_ptr& aN2, const PATH_CONNECTION& aPc ) { if( !aN1 || !aN2 ) return nullptr; wxASSERT_MSG( ( aN1 != aN2 ), "Creepage: a connection connects a node to itself" ); std::shared_ptr gc = std::make_shared( aN1, aN2, aPc ); m_connections.push_back( gc ); aN1->m_node_conns.insert( gc ); aN2->m_node_conns.insert( gc ); return gc; } std::shared_ptr CREEPAGE_GRAPH::AddConnection( std::shared_ptr& aN1, std::shared_ptr& aN2 ) { if( !aN1 || !aN2 ) return nullptr; PATH_CONNECTION pc; pc.a1 = aN1->m_pos; pc.a2 = aN2->m_pos; pc.weight = 0; return AddConnection( aN1, aN2, pc ); } std::shared_ptr CREEPAGE_GRAPH::FindNode( GRAPH_NODE::TYPE aType, CREEP_SHAPE* aParent, const VECTOR2I& aPos ) { auto it = m_nodeset.find( std::make_shared( aType, aParent, aPos ) ); if( it != m_nodeset.end() ) return *it; return nullptr; } std::shared_ptr CREEPAGE_GRAPH::AddNetElements( int aNetCode, PCB_LAYER_ID aLayer, int aMaxCreepage ) { std::shared_ptr virtualNode = AddNodeVirtual(); virtualNode->m_net = aNetCode; for( FOOTPRINT* footprint : m_board.Footprints() ) { for( PAD* pad : footprint->Pads() ) { if( pad->GetNetCode() != aNetCode || !pad->GetLayerSet().test( aLayer ) ) continue; std::shared_ptr padShape = pad->GetEffectiveShape( aLayer ); if( padShape ) { Addshape( *padShape, virtualNode, pad ); } } } for( PCB_TRACK* track : m_board.Tracks() ) { if( !track ) continue; if( track->GetNetCode() != aNetCode ) continue; if( !track->IsOnLayer( aLayer ) ) continue; if( track->GetEffectiveShape() == nullptr ) continue; Addshape( *( track->GetEffectiveShape() ), virtualNode, track ); } for( ZONE* zone : m_board.Zones() ) { if( !zone ) continue; if( zone->GetNetCode() != aNetCode ) continue; if( zone->GetEffectiveShape( aLayer ) == nullptr ) continue; Addshape( *( zone->GetEffectiveShape( aLayer ) ), virtualNode, zone ); } const DRAWINGS drawings = m_board.Drawings(); for( BOARD_ITEM* drawing : drawings ) { if( !drawing ) continue; if( !drawing->IsConnected() ) continue; BOARD_CONNECTED_ITEM* bci = dynamic_cast( drawing ); if( !bci ) continue; if( bci->GetNetCode() != aNetCode ) continue; if( bci->IsOnLayer( aLayer ) ) { Addshape( *( bci->GetEffectiveShape() ), virtualNode, bci ); } } return virtualNode; }