31#include <unordered_map>
32#include <unordered_set>
68 all.insert( all.end(), footprint->Constraints().begin(), footprint->Constraints().end() );
82 return std::ranges::any_of( aBoard->
Footprints(),
84 { return !aFootprint->Constraints().empty(); } );
105 return parent && parent->
IsLocked();
113static std::unordered_map<KIID, std::vector<PCB_CONSTRAINT*>>
115 std::vector<KIID>* aErrored =
nullptr )
117 std::unordered_map<KIID, std::vector<PCB_CONSTRAINT*>> map;
121 bool errored = constraint->GetMembers().empty();
125 map[member.m_item].push_back( constraint );
133 if( errored && aErrored )
134 aErrored->push_back( constraint->m_Uuid );
144 const std::unordered_map<
KIID, std::vector<PCB_CONSTRAINT*>>& aMap,
const KIID& aSeed,
145 std::unordered_set<KIID>& aClusterShapes, std::vector<PCB_CONSTRAINT*>& aClusterConstraints,
146 std::set<KIID>* aVisited =
nullptr )
149 std::vector<KIID> frontier{ aSeed };
150 aClusterShapes.insert( aSeed );
152 while( !frontier.empty() )
154 KIID shapeId = frontier.back();
158 aVisited->insert( shapeId );
160 auto it = aMap.find( shapeId );
162 if( it == aMap.end() )
167 if( used.insert( constraint->
m_Uuid ).second )
168 aClusterConstraints.push_back( constraint );
172 if( aClusterShapes.insert( member.m_item ).second )
173 frontier.push_back( member.m_item );
186 std::vector<T*> items;
188 for(
const KIID&
id : aIds )
190 if(
T* item =
dynamic_cast<T*
>( aBoard->
ResolveItem(
id,
true ) ) )
191 items.push_back( item );
205 const std::unordered_set<KIID>& aIds )
225 return std::hypot( *aLine.p2.x - *aLine.p1.x, *aLine.p2.y - *aLine.p1.y ) < 1e-9;
231 const double ax[2] = { *aL1.p1.x, *aL1.p2.x };
232 const double ay[2] = { *aL1.p1.y, *aL1.p2.y };
233 const double bx[2] = { *aL2.p1.x, *aL2.p2.x };
234 const double by[2] = { *aL2.p1.y, *aL2.p2.y };
237 double best = std::numeric_limits<double>::max();
239 for(
int i = 0; i < 2; ++i )
241 for(
int j = 0; j < 2; ++j )
243 double dist = std::hypot( ax[i] - bx[j], ay[i] - by[j] );
254 double d1x = ax[1] - ax[0], d1y = ay[1] - ay[0];
255 double d2x = bx[1] - bx[0], d2y = by[1] - by[0];
256 double theta = std::atan2( d1x * d2y - d1y * d2x, d1x * d2x + d1y * d2y );
260 double alpha =
std::abs( std::remainder( aCornerDeg *
M_PI / 180.0, 2.0 *
M_PI ) );
261 double shift = ( vB -
vA ) *
M_PI;
262 double c1 = alpha - shift;
263 double c2 = -alpha - shift;
265 double d1 =
std::abs( std::remainder( theta - c1, 2.0 *
M_PI ) );
266 double d2 =
std::abs( std::remainder( theta - c2, 2.0 *
M_PI ) );
268 return d1 <= d2 ? c1 : c2;
278static double arcSweepTarget(
double aStartAngle,
double aEndAngle,
double aSweepDeg )
280 double current = aEndAngle - aStartAngle;
283 return current - std::remainder( current - alpha, 2.0 *
M_PI );
305 return static_cast<int>(
m_params.size() ) - 1;
311 if( aConstraint && !aConstraint->
IsDriving() )
323 const std::vector<CONSTRAINT_MEMBER>& members = constraint->GetMembers();
325 if( members.empty() )
328 auto it =
m_shapeVars.find( members.front().m_item );
333 const PCB_SHAPE* shape = it->second.shape;
334 std::optional<double> value;
339 switch( constraint->GetConstraintType() )
345 if( members.size() == 2 )
347 auto anchorPos = [&](
const CONSTRAINT_MEMBER& aMember ) -> std::optional<VECTOR2I>
358 std::optional<VECTOR2I> pa = anchorPos( members[0] );
359 std::optional<VECTOR2I> pb = anchorPos( members[1] );
372 value = ( *pb - *pa ).EuclideanNorm();
389 if( members.size() != 2 )
392 auto other =
m_shapeVars.find( members[1].m_item );
397 const PCB_SHAPE* shapeB = other->second.shape;
416 std::optional<double> current = constraint->GetValue();
418 if( current &&
std::abs( *value - *current ) <= tol )
422 aBeforeWrite( constraint );
424 constraint->SetValue( value );
464 case 0:
return { leftX, topY };
465 case 1:
return { rightX, topY };
466 case 2:
return { rightX, botY };
467 default:
return { leftX, botY };
505 const std::vector<CONSTRAINT_MEMBER>& aMembers )
const
507 if( aMembers.size() != 2 || aMembers[0].m_item != aMembers[1].m_item )
532 const std::vector<PCB_CONSTRAINT*>& aConstraints,
533 const std::set<KIID>* aFixedShapes,
534 const std::vector<PCB_DIMENSION_BASE*>& aDimensions )
547 if( aShapes.empty() && aDimensions.empty() )
552 VECTOR2I origin = !aShapes.empty() ? aShapes.front()->GetStart() : aDimensions.front()->GetStart();
560 std::vector<std::pair<int, int>> lockedRanges;
563 std::vector<int> ellipseOffsetParams;
569 int firstParam =
static_cast<int>(
m_params.size() );
584 vars.
startIsLeft = shape->GetStart().x <= shape->GetEnd().x;
585 vars.
startIsTop = shape->GetStart().y <= shape->GetEnd().y;
605 for(
int i = 1; i < outline.
PointCount(); ++i )
660 m_gcs->addConstraintArcRules( arc );
667 double major = shape->GetEllipseMajorRadius();
668 double minor = shape->GetEllipseMinorRadius();
669 double phi = shape->GetEllipseRotation().AsRadians();
672 double focal = major > minor ? std::sqrt( major * major - minor * minor ) : 0.0;
686 ellipseOffsetParams.push_back( offX );
687 ellipseOffsetParams.push_back( offY );
704 GCS::ArcOfEllipse arc;
712 m_gcs->addConstraintArcOfEllipseRules( arc );
723 lockedRanges.emplace_back( firstParam,
static_cast<int>(
m_params.size() ) );
740 switch( dimension->Type() )
758 std::set<int> fixedParams;
760 for(
const auto& [first, last] : lockedRanges )
762 for(
int i = first; i < last; ++i )
763 fixedParams.insert( i );
766 for(
int i : ellipseOffsetParams )
767 fixedParams.insert( i );
769 auto pointAt = [&](
int aXIndex ) -> GCS::Point
776 auto pointFor = [&](
const ANCHOR_PARAMS& aParams ) -> GCS::Point
791 aLine.p1 = pointAt( it->second.startX );
792 aLine.p2 = pointAt( it->second.endX );
798 auto circleFor = [&](
const CONSTRAINT_MEMBER& aMember, GCS::Circle& aCircle ) ->
bool
808 aCircle.center = pointAt( it->second.startX );
809 aCircle.rad = &
m_params[it->second.radius];
815 auto ellipseFor = [&](
const CONSTRAINT_MEMBER& aMember, GCS::Ellipse& aEllipse ) ->
bool
825 aEllipse.center = pointAt( it->second.startX );
826 aEllipse.focus1 = pointAt( it->second.focusX );
827 aEllipse.radmin = &
m_params[it->second.radius];
832 auto pushConstant = [&](
double aIU ) ->
int
835 fixedParams.insert( idx );
843 const std::vector<CONSTRAINT_MEMBER>& members = constraint->GetMembers();
846 switch( constraint->GetConstraintType() )
852 if( members.size() == 2 && lineFor( members[0], l1 ) && lineFor( members[1], l2 ) )
854 m_gcs->addConstraintParallel( l1, l2, tag );
865 if( members.size() == 1 )
869 if( lineFor( members[0], l ) )
871 m_gcs->addConstraintHorizontal( l, tag );
875 else if( members.size() == 2 )
882 GCS::Point p1 = pointFor( a );
883 GCS::Point p2 = pointFor( b );
884 m_gcs->addConstraintHorizontal( p1, p2, tag );
894 if( members.size() == 1 )
898 if( lineFor( members[0], l ) )
900 m_gcs->addConstraintVertical( l, tag );
904 else if( members.size() == 2 )
911 GCS::Point p1 = pointFor( a );
912 GCS::Point p2 = pointFor( b );
913 m_gcs->addConstraintVertical( p1, p2, tag );
928 GCS::Point p1 = pointFor( a );
929 GCS::Point p2 = pointFor( b );
930 m_gcs->addConstraintP2PCoincident( p1, p2, tag );
943 fixedParams.insert( a.
x );
944 fixedParams.insert( a.
y );
955 if( members.size() == 2 && lineFor( members[0], l1 ) && lineFor( members[1], l2 ) )
957 m_gcs->addConstraintPerpendicular( l1, l2, tag );
968 if( members.size() == 2 && lineFor( members[0], l1 ) && lineFor( members[1], l2 ) )
970 m_gcs->addConstraintEqualLength( l1, l2, tag );
984 if( p.
IsValid() && lineFor( members[1], l ) )
986 GCS::Point point = pointFor( p );
987 m_gcs->addConstraintPointOnLine( point, l, tag );
990 else if( p.
IsValid() && circleFor( members[1], circ ) )
993 GCS::Point point = pointFor( p );
994 m_gcs->addConstraintPointOnCircle( point, circ, tag );
997 else if( p.
IsValid() && ellipseFor( members[1], ell ) )
999 GCS::Point point = pointFor( p );
1000 m_gcs->addConstraintPointOnEllipse( point, ell, tag );
1013 if( p.
IsValid() && lineFor( members[1], seg ) )
1015 GCS::Point mid = pointFor( p );
1016 m_gcs->addConstraintP2PSymmetric( seg.p1, seg.p2, mid, tag );
1028 if( members.size() == 2 && lineFor( members[0], l1 ) && lineFor( members[1], l2 ) )
1030 m_gcs->addConstraintPointOnLine( l2.p1, l1, tag );
1031 m_gcs->addConstraintPointOnLine( l2.p2, l1, tag );
1046 GCS::Point pa = pointFor( a );
1047 GCS::Point pb = pointFor( b );
1048 m_gcs->addConstraintP2PSymmetric( pa, pb, axis, tag );
1059 if( members.size() == 1 && constraint->HasValue() && lineFor( members[0], l ) )
1061 int len = pushConstant( *constraint->GetValue() );
1062 m_gcs->addConstraintP2PDistance( l.p1, l.p2, &
m_params[len], tag, constraint->IsDriving() );
1065 if( constraint->IsDriving() )
1068 it->second.fixedLengthParam = len;
1075 else if( members.size() == 2 && constraint->HasValue() )
1089 int aAxis = horiz ? a.
x : a.
y;
1090 int bAxis = horiz ? b.
x : b.
y;
1092 double sign = gap >= 0.0 ? 1.0 : -1.0;
1093 int len = pushConstant(
sign * *constraint->GetValue() );
1096 &
m_params[len], tag, constraint->IsDriving() );
1100 GCS::Point pa = pointFor( a );
1101 GCS::Point pb = pointFor( b );
1102 int len = pushConstant( *constraint->GetValue() );
1103 m_gcs->addConstraintP2PDistance( pa, pb, &
m_params[len], tag,
1104 constraint->IsDriving() );
1119 if( members.size() == 1 && constraint->HasValue() && circleFor( members[0], c ) )
1121 int rad = pushConstant( *constraint->GetValue() );
1122 m_gcs->addConstraintCircleRadius( c, &
m_params[rad], tag, constraint->IsDriving() );
1134 if( members.size() == 2 && circleFor( members[0], c1 ) && circleFor( members[1], c2 ) )
1136 m_gcs->addConstraintEqualRadius( c1, c2, tag );
1146 auto centerOf = [&](
const CONSTRAINT_MEMBER& aMember, GCS::Point& aOut ) ->
bool
1151 if( circleFor( aMember, c ) )
1157 if( ellipseFor( aMember, e ) )
1168 if( members.size() == 2 && centerOf( members[0], p1 ) && centerOf( members[1], p2 ) )
1170 m_gcs->addConstraintP2PCoincident( p1, p2, tag );
1181 if( members.size() == 2 && constraint->HasValue() && lineFor( members[0], l1 )
1185 fixedParams.insert( angle );
1186 m_gcs->addConstraintL2LAngle( l1, l2, &
m_params[angle], tag, constraint->IsDriving() );
1200 bool validSweep = constraint->HasValue() && *constraint->GetValue() > 0.0
1201 && *constraint->GetValue() < 360.0;
1207 *constraint->GetValue() );
1209 fixedParams.insert( tgt );
1211 &
m_params[tgt], tag, constraint->IsDriving() );
1221 if( members.size() != 2 )
1229 int lineIdx = lineFor( members[0], l ) ? 0 : ( lineFor( members[1], l ) ? 1 : -1 );
1235 if( circleFor( other, c1 ) )
1238 double dx = *l.p2.x - *l.p1.x;
1239 double dy = *l.p2.y - *l.p1.y;
1240 double cross = dx * ( *c1.center.y - *l.p1.y ) - dy * ( *c1.center.x - *l.p1.x );
1242 m_gcs->addConstraintTangent( l, c1, cross > 0.0, tag );
1245 else if( ellipseFor( other, ell ) )
1247 m_gcs->addConstraintTangent( l, ell, tag );
1251 else if( circleFor( members[0], c1 ) && circleFor( members[1], c2 ) )
1253 m_gcs->addConstraintTangent( c1, c2, tag );
1275 if( !constraint->IsDriving() )
1283 switch( constraint->GetConstraintType() )
1290 if( constraint->GetMembers().size() != 1 )
1321 std::set<int> referencedDimParams;
1325 if( unmappedConstraints.contains( constraint->m_Uuid ) )
1335 referencedDimParams.insert(
anchor.x );
1347 for(
int pointX : { vars.startX, vars.endX } )
1349 if( pointX >= 0 && (
locked || !referencedDimParams.contains( pointX ) ) )
1351 fixedParams.insert( pointX );
1352 fixedParams.insert( pointX + 1 );
1358 GCS::VEC_pD unknowns;
1360 for(
int i = 0; i < static_cast<int>(
m_params.size() ); ++i )
1362 if( !fixedParams.contains( i ) )
1363 unknowns.push_back( &
m_params[i] );
1366 m_gcs->declareUnknowns( unknowns );
1367 m_gcs->initSolution();
1391 m_gcs->initSolution();
1392 int ret =
m_gcs->solve();
1393 m_gcs->applySolution();
1397 m_gcs->clearByTag( GCS::DefaultTemporaryConstraint );
1416 if( vars.radius < 0 )
1421 c.center = GCS::Point{ &
m_params[vars.startX], &
m_params[vars.startX + 1] };
1424 if( kiid == aResizedShape )
1432 m_gcs->addConstraintCoordinateX( c.center, &
m_params[cx], GCS::DefaultTemporaryConstraint );
1433 m_gcs->addConstraintCoordinateY( c.center, &
m_params[cy], GCS::DefaultTemporaryConstraint );
1438 m_gcs->addConstraintCircleRadius( c, &
m_params[target], GCS::DefaultTemporaryConstraint );
1450 m_gcs->initSolution();
1451 int ret =
m_gcs->solve();
1452 m_gcs->applySolution();
1456 m_gcs->clearByTag( GCS::DefaultTemporaryConstraint );
1464 const std::set<KIID>& aEdited,
1465 const std::optional<std::pair<CONSTRAINT_MEMBER, VECTOR2I>>& aCoDragged )
1496 double len = std::hypot( dx, dy );
1508 && it->second.fixedLengthParam >= 0
1514 double dx = targetX -
m_params[farX];
1515 double dy = targetY -
m_params[farX + 1];
1516 double len = std::hypot( dx, dy );
1518 if( len > 1e-9 && segLen > 1e-9 )
1520 targetX =
m_params[farX] + dx * segLen / len;
1521 targetY =
m_params[farX + 1] + dy * segLen / len;
1545 coDragged = &aCoDragged->first;
1578 std::set<KIID> editedShapes = aEdited;
1579 editedShapes.insert( aDragged.
m_item );
1582 editedShapes.insert( coDragged->
m_item );
1588 std::set<KIID> heldPolygons = editedShapes;
1591 heldPolygons.erase( aDragged.
m_item );
1595 m_gcs->initSolution();
1596 int ret =
m_gcs->solve();
1597 m_gcs->applySolution();
1601 m_gcs->clearByTag( GCS::DefaultTemporaryConstraint );
1609 if( aSolveResult == GCS::Success || aSolveResult == GCS::Converged )
1615 const double residualTol = 1e-3;
1619 double structuralErr =
m_gcs->calculateConstraintErrorByTag( 0 );
1621 if( std::isfinite( structuralErr ) &&
std::abs( structuralErr ) > residualTol )
1630 double err =
m_gcs->calculateConstraintErrorByTag( tag );
1632 if( !std::isfinite( err ) ||
std::abs( err ) > residualTol )
1643 wxASSERT( !aWeight || *aWeight > 0 );
1652 int cx =
m_gcs->addConstraintCoordinateX( point, &
m_params[pinX], aTag );
1653 int cy =
m_gcs->addConstraintCoordinateY( point, &
m_params[pinY], aTag );
1657 m_gcs->rescaleConstraint( cx, *aWeight );
1658 m_gcs->rescaleConstraint( cy, *aWeight );
1711 ( aDragged.
m_index + 2 ) % 4 ) ),
1738 auto holdRadius = [&](
const SHAPE_VARS& aVars )
1750 if( aEdited.contains( kiid ) )
1789 for(
int i = 0; i < vars.vertexCount; ++i )
1805 if( !aShapes.contains( kiid ) )
1808 for(
int i = 0; i < vars.vertexCount; ++i )
1822 if( !aShapes.contains( kiid ) )
1829 int len =
pushParam( std::hypot( dx, dy ) );
1830 m_gcs->addConstraintP2PDistance( p1, p2, &
m_params[len], aTag );
1855 if( !aShapes.contains( kiid ) )
1865 std::vector<PCB_SHAPE*> changed;
1871 auto pointAt = [&](
int aX )
1880 VECTOR2I start = pointAt( vars.startX );
1883 VECTOR2I end = vars.endX >= 0 ? pointAt( vars.endX ) : vars.dimension->GetEnd();
1885 if( start == vars.dimension->GetStart() &&
end == vars.dimension->GetEnd() )
1889 aBeforeWrite( vars.dimension );
1891 vars.dimension->SetStart( start );
1892 vars.dimension->SetEnd(
end );
1893 vars.dimension->Update();
1902 if(
center == vars.shape->GetCenter() &&
radius == vars.shape->GetRadius() )
1906 aBeforeWrite( vars.shape );
1908 vars.shape->SetCenter(
center );
1909 vars.shape->SetRadius(
radius );
1910 changed.push_back( vars.shape );
1922 VECTOR2I start = pointAt( vars.arcStartX );
1925 if( start == vars.shape->GetStart() &&
end == vars.shape->GetEnd()
1926 &&
center == vars.shape->GetCenter() )
1932 aBeforeWrite( vars.shape );
1937 double sa = std::atan2( start.
y -
center.y, start.
x -
center.x );
1940 if( vars.shape->IsClockwiseArc() )
1950 double midAngle = 0.5 * ( sa + ea );
1954 vars.shape->SetArcGeometry( start, mid,
end );
1955 changed.push_back( vars.shape );
1966 double focal = std::hypot( fx, fy );
1968 double major = std::sqrt( focal * focal + minor * minor );
1973 focal > 1.0 ?
EDA_ANGLE( std::atan2( fy, fx ),
RADIANS_T ) : vars.shape->GetEllipseRotation();
1975 EDA_ANGLE startAngle = vars.shape->GetEllipseStartAngle();
1976 EDA_ANGLE endAngle = vars.shape->GetEllipseEndAngle();
1986 return std::abs( ( aA - aB ).Normalize180().AsDegrees() ) < 1e-6;
1989 if(
center == vars.shape->GetEllipseCenter() &&
KiROUND( major ) == vars.shape->GetEllipseMajorRadius()
1990 &&
KiROUND( minor ) == vars.shape->GetEllipseMinorRadius()
1991 && sameAngle( rotation, vars.shape->GetEllipseRotation() )
1992 && sameAngle( startAngle, vars.shape->GetEllipseStartAngle() )
1993 && sameAngle( endAngle, vars.shape->GetEllipseEndAngle() ) )
1999 aBeforeWrite( vars.shape );
2001 vars.shape->SetEllipseCenter(
center );
2002 vars.shape->SetEllipseRotation( rotation );
2003 vars.shape->SetEllipseMajorRadius(
KiROUND( major ) );
2004 vars.shape->SetEllipseMinorRadius(
KiROUND( minor ) );
2008 vars.shape->SetEllipseStartAngle( startAngle );
2009 vars.shape->SetEllipseEndAngle( endAngle );
2012 changed.push_back( vars.shape );
2018 VECTOR2I start = pointAt( vars.startX );
2021 if( start == vars.shape->GetStart() &&
end == vars.shape->GetEnd() )
2025 aBeforeWrite( vars.shape );
2029 VECTOR2I startDelta = start - vars.shape->GetStart();
2032 vars.shape->SetBezierC1( vars.shape->GetBezierC1() + startDelta );
2033 vars.shape->SetBezierC2( vars.shape->GetBezierC2() + endDelta );
2034 vars.shape->SetStart( start );
2035 vars.shape->SetEnd(
end );
2036 vars.shape->RebuildBezierToSegmentsPointsList();
2037 changed.push_back( vars.shape );
2053 std::vector<VECTOR2I> points;
2054 points.reserve( vars.vertexCount );
2056 for(
int i = 0; i < vars.vertexCount; ++i )
2057 points.push_back( pointAt( vars.startX + 2 * i ) );
2062 for(
int i = 0; i < vars.vertexCount; ++i )
2064 if( points[i] != outline.
CPoint( i ) )
2075 aBeforeWrite( vars.shape );
2079 vars.shape->SetPolyPoints( points );
2080 changed.push_back( vars.shape );
2086 VECTOR2I start = pointAt( vars.startX );
2089 if( start == vars.shape->GetStart() &&
end == vars.shape->GetEnd() )
2094 const int collapseFloor = 1000;
2097 int curWidth =
std::abs( vars.shape->GetEnd().x - vars.shape->GetStart().x );
2098 int curHeight =
std::abs( vars.shape->GetEnd().y - vars.shape->GetStart().y );
2100 if( ( newWidth < collapseFloor && curWidth >= collapseFloor )
2101 || ( newHeight < collapseFloor && curHeight >= collapseFloor ) )
2107 aBeforeWrite( vars.shape );
2111 int cornerRadius = vars.shape->GetCornerRadius();
2113 vars.shape->SetStart( start );
2114 vars.shape->SetEnd(
end );
2116 if( cornerRadius > 0 )
2117 vars.shape->SetCornerRadius( cornerRadius );
2119 changed.push_back( vars.shape );
2123 VECTOR2I start = pointAt( vars.startX );
2126 if( start == vars.shape->GetStart() &&
end == vars.shape->GetEnd() )
2131 const double collapseFloor = 1000.0;
2132 double newLen = (
end - start ).EuclideanNorm();
2133 double curLen = ( vars.shape->GetEnd() - vars.shape->GetStart() ).EuclideanNorm();
2135 if( newLen < collapseFloor && curLen >= collapseFloor )
2139 aBeforeWrite( vars.shape );
2141 vars.shape->SetStart( start );
2142 vars.shape->SetEnd(
end );
2143 changed.push_back( vars.shape );
2160 GCS::VEC_I conflictingTags;
2161 GCS::VEC_I redundantTags;
2162 m_gcs->getConflicting( conflictingTags );
2163 m_gcs->getRedundant( redundantTags );
2165 auto tagsToKiids = [&](
const GCS::VEC_I& aTags, std::vector<KIID>& aOut )
2167 for(
int t : aTags )
2172 aOut.push_back( it->second );
2177 tagsToKiids( redundantTags, diag.
redundant );
2179 auto flagConflict = [&](
const KIID& aKiid )
2186 const double residualTol = 1e-3;
2195 double err =
m_gcs->calculateConstraintErrorByTag( tag );
2198 if( !std::isfinite( err ) ||
std::abs( err ) <= residualTol )
2201 flagConflict( kiid );
2206 const double normFloor = 1e-3;
2207 std::set<KIID> collapsedShapes;
2214 double solvedLen = std::hypot(
m_params[vars.endX] -
m_params[vars.startX],
2216 double origLen = ( vars.shape->GetEnd() - vars.shape->GetStart() ).EuclideanNorm() *
m_invScale;
2218 if( origLen > normFloor && solvedLen < normFloor )
2219 collapsedShapes.insert( k );
2222 if( !collapsedShapes.empty() )
2224 bool attributed =
false;
2237 bool incident = std::any_of( members->second.begin(), members->second.end(),
2238 [&](
const KIID& aMember )
2240 return collapsedShapes.contains( aMember );
2245 flagConflict( kiid );
2257 flagConflict( kiid );
2268 std::vector<PCB_SHAPE*>* aModified,
2269 const std::function<
void(
BOARD_ITEM* )>& aBeforeModify,
bool aIncludeDragged,
2270 bool aStabilize,
const std::set<KIID>& aEdited,
2271 const std::optional<std::pair<CONSTRAINT_MEMBER, VECTOR2I>>& aCoDragged )
2282 std::unordered_set<KIID> clusterShapes;
2283 std::vector<PCB_CONSTRAINT*> clusterConstraints;
2289 if( ( shapes.empty() && dimensions.empty() ) || clusterConstraints.empty() )
2294 if( !adapter.
Build( shapes, clusterConstraints,
nullptr, dimensions ) )
2297 bool solved = adapter.
Solve( aDragged, aCursor, aStabilize, aEdited, aCoDragged );
2306 std::vector<PCB_SHAPE*> changed = adapter.
Apply(
2309 if( ( aIncludeDragged || aItem != draggedShape ) && aBeforeModify )
2310 aBeforeModify( aItem );
2320 std::ranges::copy_if( changed, std::back_inserter( *aModified ),
2322 {
return aIncludeDragged || aShape != draggedShape; } );
2330 std::vector<PCB_SHAPE*>* aModified,
2331 const std::function<
void(
BOARD_ITEM* )>& aBeforeModify )
2333 if( !aBoard || !aConstraint || aConstraint->
GetMembers().empty() )
2350 if( anchors.empty() )
2353 pin.m_anchor = anchors.front().anchor;
2354 pin.m_index = anchors.front().index;
2364 return SolveCluster( aBoard,
pin, *pos, aModified, aBeforeModify,
true,
2370 const std::function<
void(
BOARD_ITEM* )>& aBeforeModify )
2378 std::set<KIID> visited;
2382 if( !shape || visited.contains( shape->m_Uuid ) || !shapeToConstraints.contains( shape->m_Uuid ) )
2385 std::unordered_set<KIID> clusterShapes;
2386 std::vector<PCB_CONSTRAINT*> clusterConstraints;
2391 if( anchors.empty() )
2396 std::set<KIID> edited;
2400 if( other && clusterShapes.contains( other->m_Uuid ) )
2401 edited.insert( other->m_Uuid );
2404 SolveCluster( aBoard, { shape->m_Uuid, anchors.front().anchor, anchors.front().index },
2405 anchors.front().pos, aModified, aBeforeModify,
2406 true,
false, edited );
2412 std::vector<PCB_SHAPE*>* aModified,
2413 const std::function<
void(
BOARD_ITEM* )>& aBeforeModify )
2419 std::set<KIID> visited;
2423 if( !
seed || visited.contains(
seed->m_Uuid ) || !shapeToConstraints.contains(
seed->m_Uuid ) )
2426 std::unordered_set<KIID> clusterShapes;
2427 std::vector<PCB_CONSTRAINT*> clusterConstraints;
2433 if( shapes.empty() || clusterConstraints.empty() )
2438 std::set<KIID> fixed;
2440 for(
PCB_SHAPE* edited : aEditedShapes )
2442 if( edited && clusterShapes.contains( edited->m_Uuid ) )
2443 fixed.insert( edited->m_Uuid );
2448 if( !adapter.
Build( shapes, clusterConstraints, &fixed, dimensions ) || !adapter.
Solve(
true ) )
2451 std::vector<PCB_SHAPE*> changed = adapter.
Apply( aBeforeModify );
2458 aModified->insert( aModified->end(), changed.begin(), changed.end() );
2464 const std::function<
void(
BOARD_ITEM* )>& aBeforeModify )
2466 if( !aBoard || !aShape )
2471 if( !shapeToConstraints.contains( aShape->
m_Uuid ) )
2474 std::unordered_set<KIID> clusterShapes;
2475 std::vector<PCB_CONSTRAINT*> clusterConstraints;
2481 if( shapes.empty() || clusterConstraints.empty() )
2486 if( !adapter.
Build( shapes, clusterConstraints,
nullptr, dimensions )
2492 std::vector<PCB_SHAPE*> changed = adapter.
Apply(
2496 aBeforeModify( aChanged );
2502 aModified->insert( aModified->end(), changed.begin(), changed.end() );
2509 const std::unordered_set<KIID>& aClusterShapes,
2510 const std::vector<PCB_CONSTRAINT*>& aClusterConstraints )
2519 if( ( shapes.empty() && dimensions.empty() ) || aClusterConstraints.empty()
2520 || !adapter.
Build( shapes, aClusterConstraints,
nullptr, dimensions ) )
2531 adapter.
Solve(
true );
2543 result.shapeIds.push_back( shape->m_Uuid );
2548 result.dimensionIds.push_back( dimension->m_Uuid );
2600 std::unordered_map<KIID, std::vector<PCB_CONSTRAINT*>> shapeToConstraints =
2603 std::set<KIID> visitedShapes;
2605 for(
const auto& [seedShape, seedConstraints] : shapeToConstraints )
2607 if( visitedShapes.contains( seedShape ) )
2610 std::unordered_set<KIID> clusterShapes;
2611 std::vector<PCB_CONSTRAINT*> clusterConstraints;
2628 aSeed ^= aValue + 0x9e3779b97f4a7c15ULL + ( aSeed << 6 ) + ( aSeed >> 2 );
2632static void hashInt( std::size_t& aSeed,
long long aValue )
2634 hashCombine( aSeed,
static_cast<std::size_t
>( aValue ) );
2642 std::uint64_t bits = 0;
2643 static_assert(
sizeof( bits ) ==
sizeof( aValue ) );
2644 std::memcpy( &bits, &aValue,
sizeof( bits ) );
2645 hashCombine( aSeed,
static_cast<std::size_t
>( bits & 0xFFFFFFFFULL ) );
2646 hashCombine( aSeed,
static_cast<std::size_t
>( bits >> 32 ) );
2709 for(
int i = 0; i < outline.
PointCount(); ++i )
2751 hashInt( aSeed,
static_cast<int>( aDimension->
Type() ) );
2759 hashInt( aSeed,
static_cast<int>(
2778 hashInt( aSeed,
static_cast<int>( member.m_anchor ) );
2779 hashInt( aSeed, member.m_index );
2787 const std::vector<PCB_CONSTRAINT*>& aClusterConstraints )
2789 std::size_t
seed = 0;
2791 std::vector<KIID> ids( aClusterShapes.begin(), aClusterShapes.end() );
2792 std::sort( ids.begin(), ids.end() );
2794 for(
const KIID&
id : ids )
2804 std::vector<PCB_CONSTRAINT*> constraints = aClusterConstraints;
2805 std::sort( constraints.begin(), constraints.end(),
2807 { return aLhs->m_Uuid < aRhs->m_Uuid; } );
2836 std::unordered_map<KIID, std::vector<PCB_CONSTRAINT*>> shapeToConstraints =
2839 std::set<KIID> visitedShapes;
2840 std::set<std::vector<KIID>> seenKeys;
2842 for(
const auto& [seedShape, seedConstraints] : shapeToConstraints )
2844 if( visitedShapes.contains( seedShape ) )
2847 std::unordered_set<KIID> clusterShapes;
2848 std::vector<PCB_CONSTRAINT*> clusterConstraints;
2854 std::vector<KIID> key;
2857 key.push_back( constraint->m_Uuid );
2859 std::sort( key.begin(), key.end() );
2861 std::size_t hash =
hashCluster( aBoard, clusterShapes, clusterConstraints );
2862 auto it =
m_cache.find( key );
2864 if( it ==
m_cache.end() || it->second.hash != hash )
2869 it =
m_cache.insert_or_assign( key,
CACHE_ENTRY{ hash, std::move( cluster ) } ).first;
2873 seenKeys.insert( key );
2880 if( seenKeys.contains( it->first ) )
static CLUSTER_DIAGNOSIS diagnoseSingleCluster(BOARD *aBoard, const std::unordered_set< KIID > &aClusterShapes, const std::vector< PCB_CONSTRAINT * > &aClusterConstraints)
static std::vector< PCB_CONSTRAINT * > collectAllConstraints(BOARD *aBoard)
static std::vector< PCB_SHAPE * > resolveClusterShapes(BOARD *aBoard, const std::unordered_set< KIID > &aIds)
static void hashCombine(std::size_t &aSeed, std::size_t aValue)
static void hashConstraint(std::size_t &aSeed, const PCB_CONSTRAINT *aConstraint)
static constexpr double STAY_PUT_WEIGHT
static void hashKiid(std::size_t &aSeed, const KIID &aId)
static void hashShape(std::size_t &aSeed, const PCB_SHAPE *aShape)
void ReSolveAfterShapeResize(BOARD *aBoard, PCB_SHAPE *aShape, std::vector< PCB_SHAPE * > *aModified, const std::function< void(BOARD_ITEM *)> &aBeforeModify)
Re-solve after a resize, e.g. a circle radius edit. Holds aShape fixed so its neighbors adjust.
bool BoardHasConstraints(BOARD *aBoard)
True if the board or any of its footprints carries at least one geometric constraint.
static void assembleClusterInto(BOARD_CONSTRAINT_DIAGNOSTICS &aResult, const CLUSTER_DIAGNOSIS &aCluster)
static void collectConstraintCluster(const std::unordered_map< KIID, std::vector< PCB_CONSTRAINT * > > &aMap, const KIID &aSeed, std::unordered_set< KIID > &aClusterShapes, std::vector< PCB_CONSTRAINT * > &aClusterConstraints, std::set< KIID > *aVisited=nullptr)
static constexpr double IU_PER_NORM_UNIT
static std::unordered_map< KIID, std::vector< PCB_CONSTRAINT * > > buildShapeConstraintMap(BOARD *aBoard, const std::vector< PCB_CONSTRAINT * > &aConstraints, std::vector< KIID > *aErrored=nullptr)
static void hashPoint(std::size_t &aSeed, const VECTOR2I &aPoint)
static std::size_t hashCluster(BOARD *aBoard, const std::unordered_set< KIID > &aClusterShapes, const std::vector< PCB_CONSTRAINT * > &aClusterConstraints)
void ReSolveShapeClustersHoldingEdited(BOARD *aBoard, const std::vector< PCB_SHAPE * > &aEditedShapes, std::vector< PCB_SHAPE * > *aModified, const std::function< void(BOARD_ITEM *)> &aBeforeModify)
Re-solve clusters whose new geometry is authoritative holding every edited shape fully fixed so only ...
CONSTRAINT_DIAGNOSIS ApplyConstraintImmediately(BOARD *aBoard, const PCB_CONSTRAINT *aConstraint, std::vector< PCB_SHAPE * > *aModified, const std::function< void(BOARD_ITEM *)> &aBeforeModify)
Solve a just-created constraint's cluster so the geometry snaps to satisfy it (SolidWorks-style),...
static void hashDimension(std::size_t &aSeed, const PCB_DIMENSION_BASE *aDimension)
static double arcSweepTarget(double aStartAngle, double aEndAngle, double aSweepDeg)
static double directedAngleForCorner(const GCS::Line &aL1, const GCS::Line &aL2, double aCornerDeg)
void ReSolveShapeClusters(BOARD *aBoard, const std::vector< PCB_SHAPE * > &aShapes, std::vector< PCB_SHAPE * > *aModified, const std::function< void(BOARD_ITEM *)> &aBeforeModify)
Re-solve the clusters of shapes edited outside the solver, e.g.
static void hashInt(std::size_t &aSeed, long long aValue)
bool ConstraintItemIsLocked(const BOARD_ITEM *aItem)
True when the solver must treat aItem as immovable, either locked itself or living inside a locked fo...
static void dedupErrored(BOARD_CONSTRAINT_DIAGNOSTICS &aResult)
CONSTRAINT_DIAGNOSIS SolveCluster(BOARD *aBoard, const CONSTRAINT_MEMBER &aDragged, const VECTOR2I &aCursor, std::vector< PCB_SHAPE * > *aModified, const std::function< void(BOARD_ITEM *)> &aBeforeModify, bool aIncludeDragged, bool aStabilize, const std::set< KIID > &aEdited, const std::optional< std::pair< CONSTRAINT_MEMBER, VECTOR2I > > &aCoDragged)
Gather the cluster of shapes transitively constrained with the dragged shape, solve with the dragged ...
BOARD_CONSTRAINT_DIAGNOSTICS DiagnoseBoardConstraints(BOARD *aBoard)
Diagnose every constraint cluster on the board (validate only – geometry is not changed) and return t...
static std::vector< PCB_DIMENSION_BASE * > resolveClusterDimensions(BOARD *aBoard, const std::unordered_set< KIID > &aIds)
static std::vector< T * > resolveClusterItems(BOARD *aBoard, const std::unordered_set< KIID > &aIds)
static constexpr int MAX_SOLVE_ITERATIONS
static bool isDegenerateLine(const GCS::Line &aLine)
static void hashDouble(std::size_t &aSeed, double aValue)
@ OVER_CONSTRAINED
In a cluster the solver reports as conflicting.
@ UNDER_CONSTRAINED
In a cluster with remaining free degrees of freedom.
@ WELL_CONSTRAINED
In a fully-determined cluster (zero free DOF).
bool ConstraintItemIsLocked(const BOARD_ITEM *aItem)
True when the solver must treat aItem as immovable, either locked itself or living inside a locked fo...
constexpr BOX2I KiROUND(const BOX2D &aBoxD)
Translates KiCad board geometry to and from the planegcs solver (issue #2329).
void recordReferenceValue(PCB_CONSTRAINT *aConstraint)
Note a non-driving valued constraint so its measured value can be read back after a solve.
double normalizeY(int aIU) const
bool Solve(const CONSTRAINT_MEMBER &aDragged, const VECTOR2I &aCursor, bool aStabilize=false, const std::set< KIID > &aEdited={}, const std::optional< std::pair< CONSTRAINT_MEMBER, VECTOR2I > > &aCoDragged=std::nullopt)
Solve the system, pinning a dragged anchor to a cursor position.
int m_coDragTargetY
Backing slot for the co-dragged pin y target.
PCB_DIM_ORTHOGONAL * orthogonalDimensionForMembers(const std::vector< CONSTRAINT_MEMBER > &aMembers) const
The orthogonal dimension a two-point length constraint drives or nullptr requires both members to be ...
std::vector< KIID > m_unmapped
Constraints Build() could not map (not enforced).
double denormalizeX(double aNorm) const
void softPinPoint(const ANCHOR_PARAMS &aPoint, int aTag, std::optional< double > aWeight=std::nullopt)
Pin the point at aPoint where it sits tagged aTag with aWeight rescaling the tier or the default weig...
BOARD_CONSTRAINT_ADAPTER()
void pinUneditedShapes(const std::set< KIID > &aEdited, int aTag)
Soft-pin every cluster shape not in aEdited at its current geometry tagged aTag for a minimal-movemen...
double m_originX
IU offset subtracted from x before scaling (cluster anchor).
int m_dragTargetX
Stable backing slot for the drag pin's x target (-1 = unset).
void holdFreeSegmentLengths(int aTag, const std::set< KIID > &aShapes)
Length hold on the free segments in aShapes tagged aTag so only those shapes are protected while a me...
std::vector< PCB_CONSTRAINT * > m_referenceConstraints
Non-driving valued, read back after a solve.
bool solveSucceeded(int aSolveResult)
Decide whether a solve reached a usable result a raw Success or Converged always qualifies while a Fa...
double m_invScale
1 / m_scale.
const std::vector< KIID > & UnmappedConstraints() const
Constraints from the last Build() that could not be mapped onto a solver primitive (wrong member coun...
ANCHOR_PARAMS anchorParams(const CONSTRAINT_MEMBER &aMember) const
Indices into m_params of the coordinates an anchor maps to invalid if the shape has no such anchor fo...
std::map< KIID, SHAPE_VARS > m_shapeVars
bool SolveAfterResize(const KIID &aResizedShape)
Solve after a resize.
int m_coDragTargetX
Backing slot for the co-dragged pin x target.
double denormalizeY(double aNorm) const
void pinDraggedShapeRest(const CONSTRAINT_MEMBER &aDragged, int aTag, const CONSTRAINT_MEMBER *aCoDragged=nullptr)
Hold the parts of the dragged shape meant to stay put tagged aTag a segment holds its far endpoint an...
std::map< int, std::vector< KIID > > m_tagMembers
Member items per tag, for collapse attribution.
int m_dragTargetY
Stable backing slot for the drag pin's y target.
double m_scale
IU per normalized unit.
std::map< int, KIID > m_tagToConstraint
std::set< KIID > m_angleConstrainedShapes
Shapes a direction or angle constraint could collapse to a point only these get a stabilize length or...
void holdFreeArcRadii(int aTag, const std::set< KIID > &aShapes)
Radius hold on the free arcs in aShapes tagged aTag so an angle change rotates an endpoint instead of...
std::deque< double > m_params
bool Build(const std::vector< PCB_SHAPE * > &aShapes, const std::vector< PCB_CONSTRAINT * > &aConstraints, const std::set< KIID > *aFixedShapes=nullptr, const std::vector< PCB_DIMENSION_BASE * > &aDimensions={})
Translate a cluster into a planegcs system.
void holdPolygonVertices(const std::set< KIID > &aShapes, int aTag)
Soft-pin every vertex of each POLYGON in aShapes tagged aTag for edited shapes pinUneditedShapes excl...
std::vector< PCB_SHAPE * > Apply(const std::function< void(BOARD_ITEM *)> &aBeforeWrite={})
Write the solved coordinates back into the shapes, de-normalized to IU.
std::unique_ptr< GCS::System > m_gcs
int pushParam(double aValue)
Append a normalized coordinate to the backing store, returning its stable index.
CONSTRAINT_DIAGNOSIS Diagnose()
Report degrees of freedom and conflicting/redundant constraints.
double m_originY
IU offset subtracted from y before scaling.
std::set< int > m_nonDrivingTags
Measurement-only; excluded from conflict residuals.
double normalizeX(int aIU) const
IU <-> normalized (millimetre, cluster-centred) frame, per axis.
~BOARD_CONSTRAINT_ADAPTER()
@ RECT
An axis-aligned rectangle whose four corners alias the two stored corners params so rectness holds by...
@ POINT_PAIR
A dimension's two feature points (start + end); no line/curve geometry.
@ POLYGON
A single hole-free outline with one free param pair per vertex since write-back rebuilds one outline ...
@ BEZIER
A cubic bezier only its start and end endpoints are exposed as free points.
void ApplyReferenceValues(const std::function< void(BOARD_ITEM *)> &aBeforeWrite={})
Propagate solved reference (non-driving) constraint values back into their m_value so a reference dim...
void holdArcRadius(const SHAPE_VARS &aVars, int aTag)
Hold aVars's arc at its current radius (tagged aTag).
BOARD_CONSTRAINT_DIAGNOSTICS Diagnose(BOARD *aBoard)
Diagnose every cluster reusing cached per-cluster results whose solve inputs are unchanged.
std::map< std::vector< KIID >, CACHE_ENTRY > m_cache
void Clear()
Drop the cache call when the board or view reloads and item-identity assumptions break.
A base class for any item which can be embedded within the BOARD container class, and therefore insta...
bool IsLocked() const override
virtual const BOARD * GetBoard() const
Return the BOARD in which this BOARD_ITEM resides, or NULL if none.
FOOTPRINT * GetParentFootprint() const
Information pertinent to a Pcbnew printed circuit board.
BOARD_USE GetBoardUse() const
Get what the board use is.
const FOOTPRINTS & Footprints() const
const CONSTRAINTS & Constraints() const
Geometric constraints (#2329) owned by this board.
BOARD_ITEM * ResolveItem(const KIID &aID, bool aAllowNullptrReturn=false) const
KICAD_T Type() const
Returns the type of object.
EDA_ANGLE GetArcAngle() const
int GetEllipseMinorRadius() const
const VECTOR2I & GetBezierC2() const
const VECTOR2I & GetEllipseCenter() const
EDA_ANGLE GetEllipseEndAngle() const
int GetEllipseMajorRadius() const
SHAPE_POLY_SET & GetPolyShape()
EDA_ANGLE GetEllipseRotation() const
const VECTOR2I & GetEnd() const
Return the ending point of the graphic.
const VECTOR2I & GetStart() const
Return the starting point of the graphic.
EDA_ANGLE GetEllipseStartAngle() const
const VECTOR2I & GetBezierC1() const
A geometric constraint between board items (issue #2329).
const std::vector< CONSTRAINT_MEMBER > & GetMembers() const
std::optional< double > GetValue() const
bool IsDriving() const
A driving constraint forces its value; a reference (non-driving) one only measures it.
PCB_CONSTRAINT_TYPE GetConstraintType() const
virtual VECTOR2I GetEnd() const
virtual VECTOR2I GetStart() const
The dimension's origin is the first feature point for the dimension.
An orthogonal dimension is like an aligned dimension, but the extension lines are locked to the X or ...
VECTOR2I GetCenter() const override
This defaults to the center of the bounding box if not overridden.
Represent a polyline containing arcs as well as line segments: A chain of connected line and/or arc s...
int PointCount() const
Return the number of points (vertices) in this line chain.
const VECTOR2I & CPoint(int aIndex) const
Return a reference to a given point in the line chain.
Represent a set of closed polygons.
int HoleCount(int aOutline) const
Returns the number of holes in a given outline.
int OutlineCount() const
Return the number of outlines in the set.
const SHAPE_LINE_CHAIN & COutline(int aIndex) const
EDA_ANGLE MeasureCornerAngle(const SEG &aA, const SEG &aB)
The corner angle between two segments, in the closed range [0, 180] degrees.
std::vector< CONSTRAINT_ANCHOR_POINT > ConstraintShapeAnchors(const PCB_SHAPE *aShape)
Enumerate a shape constraint anchors with positions segment and arc endpoints arc centre circle centr...
std::optional< VECTOR2I > ConstraintAnchorPosition(BOARD *aBoard, const CONSTRAINT_MEMBER &aMember)
Current location of a constraint member's anchor (its shape's START/END/CENTER, or a dimension's feat...
bool ConstraintPolygonIsModelable(const PCB_SHAPE *aShape)
True when polygon has one non empty hole free arc free outline making it solver eligible Shared by ad...
BOARD_ITEM * ResolveConstrainableItem(BOARD *aBoard, const KIID &aId)
The board item a constraint may reference: a PCB_SHAPE or a dimension, or nullptr for anything else (...
@ RECTANGLE
Use RECTANGLE instead of RECT to avoid collision in a Windows header.
EDA_ANGLE abs(const EDA_ANGLE &aAngle)
CONSTRAINT_ANCHOR
Which feature of a referenced board item participates in a constraint.
@ VERTEX
An indexed rectangle corner or polygon outline vertex; pairs with CONSTRAINT_MEMBER::m_index.
@ WHOLE
The item as a whole (a segment as a line, a circle).
@ START
First endpoint of a segment or arc.
@ END
Second endpoint of a segment or arc.
@ CENTER
Center of an arc or circle.
@ CONCENTRIC
Two arcs/circles share a center.
@ SYMMETRIC
Two points are mirror images about an axis.
@ FIXED_POSITION
A point is locked at its current location.
@ VERTICAL
A segment (or two points) is vertical.
@ TANGENT
A line and a curve, or two curves, touch tangentially.
@ COINCIDENT
Two points are made to coincide.
@ PERPENDICULAR
Two segments are perpendicular.
@ FIXED_RADIUS
An arc/circle has a driving radius value.
@ HORIZONTAL
A segment (or two points) is horizontal.
@ EQUAL_RADIUS
Two arcs/circles have equal radius.
@ MIDPOINT
A point is the midpoint of a segment.
@ POINT_ON_LINE
A point lies on a segment's supporting line.
@ FIXED_LENGTH
A segment has a driving length value.
@ ANGULAR_DIMENSION
An angle between members (driving or reference).
@ COLLINEAR
Two segments lie on the same line.
@ ARC_ANGLE
An arc has a driving or reference swept-angle value.
@ PARALLEL
Two segments are parallel.
@ EQUAL_LENGTH
Two segments have equal length.
Param indices of an anchor coordinates a rect corner aliases mixed start end params so y is not alway...
Per-shape indices into m_params.
int radius
radius scalar (circle, arc) / minor radius (ellipse).
bool startIsLeft
Rect corner roles frozen at Build so VERTEX 0 to 3 as TL TR BR BL bind the same physical corners what...
int arcStartX
arc start-point.x.
int fixedLengthParam
param index of a driving fixed-length target, or -1.
int focusX
first focus.x (ellipse kinds only).
int startX
start.x (segment) / center.x (circle, arc, ellipse).
int endAngle
arc end angle (radians).
int startAngle
arc start angle (radians).
int arcEndX
arc end-point.x.
int endX
end.x (segment only).
PCB_DIMENSION_BASE * dimension
set instead of shape for a POINT_PAIR
int vertexCount
Outline-0 vertex count of a POLYGON vertex i x param is startX plus 2 times i.
Board-wide diagnostics for the constraint overlay and info bar.
std::map< KIID, CONSTRAINT_STATE > shapeStates
std::vector< KIID > errored
Invalid constraints (member missing, deleted, or of a kind incompatible with the type).
std::vector< KIID > redundant
std::vector< KIID > conflicting
One cluster's diagnosis, the unit DiagnoseBoardConstraints assembles the board-wide result from and B...
int freeDof
Remaining free DOF folded into the board total.
std::vector< KIID > dimensionIds
Cluster dimensions.
std::vector< KIID > conflicting
std::vector< KIID > erroredUnmapped
Constraints Build could not map and so not enforced.
std::vector< KIID > shapeIds
Cluster shapes in the order the state is written.
std::vector< KIID > redundant
The outcome of a constraint solve, in plain data so callers need not know planegcs.
bool solved
Solver reached Success or Converged.
bool IsOverConstrained() const
std::vector< KIID > conflicting
Constraints the solver reports as over-constraining.
bool IsUnderConstrained() const
std::vector< KIID > redundant
Constraints the solver reports as redundant.
int freeDof
Remaining degrees of freedom (-1 if not diagnosed).
One participant in a constraint: a referenced board item plus the feature of that item that participa...
KIID m_item
Referenced board item, usually a PCB_SHAPE.
CONSTRAINT_ANCHOR m_anchor
Which feature of that item participates.
int m_index
Vertex ordinal; only meaningful for the VERTEX anchor.
SHAPE_CIRCLE circle(c.m_circle_center, c.m_circle_radius)
wxString result
Test unit parsing edge cases and error handling.
@ PCB_DIM_ORTHOGONAL_T
class PCB_DIM_ORTHOGONAL, a linear dimension constrained to x/y
@ PCB_DIM_ALIGNED_T
class PCB_DIM_ALIGNED, a linear dimension (graphic item)
@ PCB_DIM_RADIAL_T
class PCB_DIM_RADIAL, a radius or diameter dimension
constexpr int sign(T val)
VECTOR2< int32_t > VECTOR2I