68std::optional<KIID> nearestOutline(
BOARD* aBoard,
const VECTOR2I& aPos,
double aMaxDist,
bool aAllowCircle )
70 double best = aMaxDist;
71 std::optional<KIID>
result;
75 const SHAPE_T shapeType = shape->GetShape();
80 dist =
SEG( shape->GetStart(), shape->GetEnd() ).
Distance( aPos );
84 dist =
std::abs( ( aPos - shape->GetCenter() ).EuclideanNorm() - shape->GetRadius() );
90 double a = shape->GetEllipseMajorRadius();
91 double b = shape->GetEllipseMinorRadius();
92 double phi = shape->GetEllipseRotation().AsRadians();
94 double lx = d.
x * std::cos( phi ) + d.
y * std::sin( phi );
95 double ly = -d.
x * std::sin( phi ) + d.
y * std::cos( phi );
96 double r = std::hypot( lx, ly );
98 if( a <= 0 || b <= 0 )
101 double theta = std::atan2( ly, lx );
102 double re = a * b / std::hypot( b * std::cos( theta ), a * std::sin( theta ) );
158 m_overlay->SetVisibilityMode(
frame()->GetPcbNewSettings()->m_Display.m_ShowConstraints
192 const std::vector<CONSTRAINT_BADGE>& badges =
m_overlay->Badges();
196 for(
size_t i = 0; i < badges.size(); ++i )
198 double dist = ( layout[i] -
VECTOR2D( aPos ) ).EuclideanNorm();
227 panel->SelectConstraint( aConstraint->
m_Uuid );
240 if( !aConstraint || !
board() )
250 std::vector<MSG_PANEL_ITEM> items;
258 if( !members.IsEmpty() )
259 members += wxT(
", " );
264 items.emplace_back(
_(
"Items" ), members );
267 wxString state =
_(
"OK" );
270 state =
_(
"Error (missing item)" );
272 state =
_(
"Over-constrained" );
274 state =
_(
"Redundant" );
276 items.emplace_back(
_(
"State" ), state );
278 frame()->SetMsgPanel( items );
344 panel = pcbFrame->GetConstraintsPanel();
346 bool panelShown = panel && panel->IsShownOnScreen();
354 if( !overlayActive && !panelShown )
392 std::vector<PCB_SHAPE*> shapes;
401 if( ids.insert( m.m_item ).second )
404 dynamic_cast<PCB_SHAPE*
>(
board()->ResolveItem( m.m_item,
true ) ) )
406 shapes.push_back( shape );
415 collect(
board()->Constraints() );
451 bool overBadge = std::ranges::any_of( layout,
453 {
return ( aPos -
VECTOR2D(
cursor ) ).EuclideanNorm() <= badgeTol; } );
487 std::unordered_set<KIID> constrained;
490 constrained.insert( entry.first );
507 std::unordered_set<KIID> members;
512 members.insert( member.m_item );
524 const std::unordered_set<KIID>& aNew )
537 for(
const KIID&
id : aOld )
539 if( !aNew.contains(
id ) )
543 for(
const KIID&
id : aNew )
545 if( !aOld.contains(
id ) )
561 if(
PANEL_CONSTRAINTS* panel = pcbFrame->GetConstraintsPanel(); panel && panel->IsShownOnScreen() )
562 panel->RefreshList( aDiag );
570 std::vector<PCB_SHAPE*> members;
575 members.push_back( shape );
579 commit.
Remove( aConstraint );
580 commit.
Push(
_(
"Remove Geometric Constraint" ) );
657 const std::vector<CONSTRAINT_MEMBER>& members = constraint->
GetMembers();
660 bool highlightOne = aMemberIndex >= 0 && aMemberIndex < static_cast<int>( members.size() );
669 selectMember( members[aMemberIndex] );
671 std::ranges::for_each( members, selectMember );
688 return std::ranges::all_of( aConstraint->
GetMembers(),
691 BOARD_ITEM* item = board()->ResolveItem( aMember.m_item, true );
692 return item && ConstraintItemIsLocked( item );
699 if( !aConstraint || !
board() )
704 return std::ranges::any_of( aList,
711 if( scan(
board()->Constraints() ) )
719 const std::vector<PCB_CONSTRAINT*>& aAdded )
721 aCommit.
Push(
_(
"Add Geometric Constraint" ) );
724 if( !aAdded.empty() )
729 if( aAdded.empty() || !
frame() )
737 frame()->ShowInfoBarWarning(
_(
"All items referenced by this constraint are locked, so the constraint cannot "
738 "move any geometry." ),
752 frame()->ShowInfoBarWarning(
_(
"The new geometric constraint conflicts with existing constraints and "
753 "could not be satisfied." ),
760 frame()->ShowInfoBarWarning(
_(
"The new geometric constraint could not be applied to the referenced "
817 std::vector<PCB_SHAPE*> modified;
823 if( !commit.
Empty() )
834 std::vector<PCB_SHAPE*> modified;
842 if( !commit.
Empty() )
855 std::vector<PCB_SHAPE*> modified;
863 if( !commit.
Empty() )
883 bool overConstrained = std::ranges::any_of( aShapes,
890 if( overConstrained &&
frame() )
891 frame()->ShowInfoBarWarning(
_(
"A geometric constraint could not be satisfied by this edit." ),
true );
912 frame()->GetPcbNewSettings()->m_Display.m_ShowConstraints = always;
926 pcbFrame->ToggleConstraintsPanel();
980 return static_cast<const PCB_SHAPE*
>( aItem )->GetShape();
984 auto isRadial = [](
SHAPE_T aShape )
989 auto isCentered = [isRadial](
SHAPE_T aShape )
994 auto oneRadial = [kindOf, isRadial](
const SELECTION& aSel )
996 return aSel.Size() == 1 && isRadial( kindOf( aSel[0] ) );
999 auto oneArc = [kindOf](
const SELECTION& aSel )
1001 return aSel.Size() == 1 && kindOf( aSel[0] ) ==
SHAPE_T::ARC;
1004 auto twoRadial = [kindOf, isRadial](
const SELECTION& aSel )
1006 return aSel.Size() == 2 && isRadial( kindOf( aSel[0] ) ) && isRadial( kindOf( aSel[1] ) );
1009 auto twoCentered = [kindOf, isCentered](
const SELECTION& aSel )
1011 return aSel.Size() == 2 && isCentered( kindOf( aSel[0] ) ) && isCentered( kindOf( aSel[1] ) );
1015 auto tangentPair = [kindOf, isCentered, isRadial](
const SELECTION& aSel )
1017 if( aSel.Size() != 2 )
1020 SHAPE_T a = kindOf( aSel[0] );
1021 SHAPE_T b = kindOf( aSel[1] );
1024 || ( isRadial( a ) && isRadial( b ) );
1028 auto selectionConstrained = [
this](
const SELECTION& aSel ) ->
bool
1030 if( aSel.Empty() || !
board() )
1036 ids.insert( item->m_Uuid );
1040 return std::ranges::any_of( aList,
1048 if( anyMember(
board()->Constraints() ) )
1052 && anyMember(
board()->GetFirstFootprint()->Constraints() );
1060 m_menu->SetUntranslatedTitle(
_HKI(
"Constraints" ) );
1066 const std::map<const TOOL_ACTION*, SELECTION_CONDITION> gate = {
1082 bool pointGroupSeparated =
false;
1086 if(
auto it = gate.find( action ); it != gate.end() )
1088 m_menu->AddItem( *action, it->second );
1092 if( !pointGroupSeparated )
1095 pointGroupSeparated =
true;
1106 auto overlayShown = [
this](
const SELECTION& )
1110 auto overlayHidden = [
this](
const SELECTION& )
1133 std::vector<BOARD_ITEM*> items;
1137 if( item->IsBOARD_ITEM() )
1138 items.push_back(
static_cast<BOARD_ITEM*
>( item ) );
1141 std::unique_ptr<PCB_CONSTRAINT> constraint =
1169 frame()->ShowInfoBarWarning(
_(
"An identical geometric constraint already exists." ) );
1176 if( aConstraint->HasValue() )
1184 bool driving = aConstraint->IsDriving();
1187 driving = it->second;
1204 commit.
Add( aConstraint.release() );
1214 bool allowCircle =
false;
1222 allowCircle =
false;
1237 allowCircle =
false;
1246 std::vector<KIID> picked;
1247 const double snapTol =
pcbIUScale.mmToIU( 1.0 );
1255 [&](
const VECTOR2D& aPoint ) ->
bool
1258 std::optional<KIID> target = nearestOutline(
board(), pos, snapTol, allowCircle );
1263 picked.push_back( *target );
1265 if(
static_cast<int>( picked.size() ) < count )
1275 std::vector<BOARD_ITEM*> items;
1277 for(
const KIID&
id : picked )
1280 items.push_back( item );
1283 std::unique_ptr<PCB_CONSTRAINT> constraint =
1286 if( !constraint &&
frame() )
1288 frame()->ShowInfoBarWarning( wxString::Format(
_(
"Cannot form a %s constraint from those items." ),
1305 std::optional<KIID> target = nearestOutline(
board(), pos, snapTol, allowCircle );
1312 m_overlay->SetPickPreview( target.value_or(
niluuid ),
true, std::nullopt );
1328 evt->SetPassEvent();
1350 std::vector<CONSTRAINT_MEMBER> members;
1351 const double snapTol =
pcbIUScale.mmToIU( 1.0 );
1358 auto commitPointPair = [&]()
1360 std::unique_ptr<PCB_CONSTRAINT> constraint =
1364 constraint->AddMember( member.m_item, member.m_anchor, member.m_index );
1370 [&](
const VECTOR2D& aPoint ) ->
bool
1376 std::optional<CONSTRAINT_MEMBER>
anchor =
1381 members.push_back( *
anchor );
1383 if( members.size() < 2 )
1398 if( members.empty() )
1400 if( std::optional<KIID> target = nearestOutline(
board(), pos, snapTol,
false ) )
1402 std::unique_ptr<PCB_CONSTRAINT> constraint =
1421 if( std::optional<CONSTRAINT_MEMBER>
anchor =
1427 else if( members.empty() )
1429 if( std::optional<KIID> target = nearestOutline(
board(), pos, snapTol,
false ) )
1430 m_overlay->SetPickPreview( *target,
true, std::nullopt );
1453 evt->SetPassEvent();
1472 std::vector<bool> plan;
1488 std::vector<CONSTRAINT_MEMBER> members;
1489 const double snapTol =
pcbIUScale.mmToIU( 1.0 );
1497 [&](
const VECTOR2D& aPoint ) ->
bool
1501 if( plan[members.size()] )
1505 std::optional<CONSTRAINT_MEMBER>
anchor =
1511 members.push_back( *
anchor );
1515 std::optional<KIID> target =
1524 if( members.size() < plan.size() )
1534 std::unique_ptr<PCB_CONSTRAINT> constraint =
1538 constraint->AddMember( member.m_item, member.m_anchor, member.m_index );
1543 frame()->ShowInfoBarWarning(
_(
"An identical geometric constraint already exists." ) );
1551 commit.
Add( constraint.release() );
1561 if( !
m_overlay || members.size() >= plan.size() )
1568 std::optional<VECTOR2I> anchorPos;
1570 if( plan[members.size()] )
1572 if( std::optional<CONSTRAINT_MEMBER>
anchor =
1575 element =
anchor->m_item;
1580 else if( std::optional<KIID> target = nearestOutline(
board(), pos, snapTol,
1586 m_overlay->SetPickPreview( element, whole, anchorPos );
1592 [&](
const int& aFinalState )
1603 evt->SetPassEvent();
1620 std::set<KIID> selectedIds;
1623 selectedIds.insert( item->m_Uuid );
1625 if( selectedIds.empty() )
1631 auto removeReferencing =
1636 bool referenced = std::ranges::any_of( constraint->GetMembers(),
1638 { return selectedIds.contains( aMember.m_item ); } );
1642 commit.
Remove( constraint );
1650 removeReferencing(
board()->Constraints() );
1653 removeReferencing(
board()->GetFirstFootprint()->Constraints() );
1657 commit.
Push(
_(
"Remove Geometric Constraints" ) );
constexpr EDA_IU_SCALE pcbIUScale
bool BoardHasConstraints(BOARD *aBoard)
True if the board or any of its footprints carries at least one geometric constraint.
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),...
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.
@ OVER_CONSTRAINED
In a cluster the solver reports as conflicting.
constexpr BOX2I KiROUND(const BOX2D &aBoxD)
static TOOL_ACTION pickerTool
static TOOL_ACTION zoomFitSelection
virtual void Push(const wxString &aMessage=wxEmptyString, int aCommitFlags=0) override
Execute the changes.
A base class for any item which can be embedded within the BOARD container class, and therefore insta...
Information pertinent to a Pcbnew printed circuit board.
FOOTPRINT * GetFirstFootprint() const
Get the first footprint on the board or nullptr.
BOARD_ITEM * ResolveItem(const KIID &aID, bool aAllowNullptrReturn=false) const
COMMIT & Remove(EDA_ITEM *aItem, BASE_SCREEN *aScreen=nullptr)
Remove a new item from the model.
COMMIT & Modify(EDA_ITEM *aItem, BASE_SCREEN *aScreen=nullptr, RECURSE_MODE aRecurse=RECURSE_MODE::NO_RECURSE)
Modify a given item in the model.
COMMIT & Add(EDA_ITEM *aItem, BASE_SCREEN *aScreen=nullptr)
Add a new item to the model.
static double BadgeHitRadius()
Click hit radius in screen pixels.
static double BadgeWorldPerPixel(double aWorldScale)
World units per screen pixel for badge sizing, capped at far zoom-out.
static std::vector< VECTOR2D > LayoutBadges(const std::vector< CONSTRAINT_BADGE > &aBadges, double aWorldPerPx)
The on-screen draw position (world units) of each badge at the given scale: the anchor offset by Badg...
Lists every geometric constraint on the board with its type and diagnostic state, the way the canvas ...
Value entry for a dimensional geometric constraint (issue #2329): a UNIT_BINDER for the length (or an...
bool GetDriving() const
True if the constraint should drive (lock) the geometry; false for a reference dimension.
double GetConstraintValue()
The entered value, in IU for a length/radius or in degrees for an angle.
A base class for most all the KiCad significant classes used in schematics and boards.
static const TOOL_EVENT ClearedEvent
static const TOOL_EVENT SelectedEvent
static const TOOL_EVENT UndoRedoPostEvent
static const TOOL_EVENT UnselectedEvent
virtual RENDER_SETTINGS * GetSettings()=0
Return a pointer to current settings that are going to be used when drawing items.
PCB specific render settings.
void SetHighlightedConstraintMembers(std::unordered_set< KIID > aItems)
void SetConstrainedItems(std::unordered_set< KIID > aItems)
const std::unordered_set< KIID > & GetHighlightedConstraintMembers() const
const std::unordered_set< KIID > & GetConstrainedItems() const
virtual VECTOR2D GetMousePosition(bool aWorldCoordinates=true) const =0
Return the current mouse pointer position.
virtual void Update(const VIEW_ITEM *aItem, int aUpdateFlags) const
For dynamic VIEWs, inform the associated VIEW that the graphical representation of this item has chan...
VECTOR2D ToWorld(const VECTOR2D &aCoord, bool aAbsolute=true) const
Converts a screen space point/vector to a point/vector in world space coordinates.
PAINTER * GetPainter() const
Return the painter object used by the view for drawing #VIEW_ITEMS.
Dockable panel listing the board's geometric constraints (issue #2329).
static TOOL_ACTION hideConstraints
Same toggle, shown while the overlay is visible.
static TOOL_ACTION addConstraintVertical
static TOOL_ACTION addConstraintPerpendicular
static TOOL_ACTION addConstraintAngular
static TOOL_ACTION addConstraintArcAngle
static TOOL_ACTION manageConstraints
Open the constraint list dialog.
static TOOL_ACTION addConstraintHorizontal
static const std::vector< const TOOL_ACTION * > & ConstraintAddActions()
Canonical ordered list of the geometric-constraint "add" actions, shared by the context submenu (gate...
static TOOL_ACTION addConstraintCollinear
static TOOL_ACTION addConstraintEqualRadius
static TOOL_ACTION addConstraintTangent
static TOOL_ACTION addConstraintFixedLength
static TOOL_ACTION addConstraintSymmetric
static TOOL_ACTION addConstraintPointOnLine
static TOOL_ACTION addConstraintCoincident
static TOOL_ACTION showConstraints
Toggle the constraint diagnostics overlay.
static TOOL_ACTION addConstraintParallel
static TOOL_ACTION addConstraintFixedRadius
static TOOL_ACTION addConstraintEqualLength
static TOOL_ACTION removeConstraints
static TOOL_ACTION addConstraintMidpoint
static TOOL_ACTION addConstraintConcentric
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
The main frame for Pcbnew.
int Distance(const SEG &aSeg) const
Compute minimum Euclidean distance to segment aSeg.
Class that groups generic conditions for selected items.
static SELECTION_CONDITION Count(int aNumber)
Create a functor that tests if the number of selected items is equal to the value given as parameter.
static bool ShowAlways(const SELECTION &aSelection)
The default condition function (always returns true).
static SELECTION_CONDITION OnlyTypes(std::vector< KICAD_T > aTypes)
Create a functor that tests if the selected items are only of given types.
A type-safe container of any type.
std::vector< PCB_SHAPE * > CollectConstraintShapes(BOARD *aBoard)
Every PCB_SHAPE on the board (drawings plus footprint graphics) – the candidates constraints can refe...
double InitialConstraintValue(PCB_CONSTRAINT_TYPE aType, double aMeasured, const std::map< PCB_CONSTRAINT_TYPE, double > &aRemembered)
Value a freshly authored constraint dialog should open with.
std::optional< KIID > NearestConstrainedShape(const std::vector< PCB_SHAPE * > &aCandidates, const VECTOR2I &aPos, int aMaxDist)
The candidate shape whose outline aPos hits within aMaxDist, or std::nullopt.
std::unique_ptr< PCB_CONSTRAINT > BuildConstraintFromItems(BOARD_ITEM *aParent, PCB_CONSTRAINT_TYPE aType, const std::vector< BOARD_ITEM * > &aItems)
Build a constraint of aType from a set of selected board items, or nullptr if the selection does not ...
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...
std::optional< CONSTRAINT_MEMBER > NearestConstraintAnchor(BOARD *aBoard, const VECTOR2I &aPos, double aMaxDist, const std::vector< CONSTRAINT_MEMBER > &aExclude)
Find the constrainable-item anchor (a shape's segment/arc endpoint or centre, or a dimension's featur...
bool EditConstraintValue(PCB_BASE_FRAME *aFrame, PCB_CONSTRAINT *aConstraint, BOARD_COMMIT &aCommit)
Show the value dialog for a valued constraint and stage the change in aCommit (the caller pushes).
Message panel definition file.
@ ALL
All except INITIAL_ADD.
bool contains(const _Container &__container, _Value __value)
Returns true if the container contains the given value.
EDA_ANGLE abs(const EDA_ANGLE &aAngle)
bool ConstraintsAreDuplicate(const PCB_CONSTRAINT &aA, const PCB_CONSTRAINT &aB)
True if two constraints express the same relation, meaning the same type and the same members compare...
wxString ConstraintTypeLabel(PCB_CONSTRAINT_TYPE aType)
Human-readable name of a constraint type (e.g. "Parallel"), for menus and lists.
wxString ConstraintMemberLabel(BOARD_ITEM *aItem, const CONSTRAINT_MEMBER &aMember, UNITS_PROVIDER *aUnitsProvider)
Label for one constrained item in a list combining the item description with its anchored feature suc...
wxString ConstraintDisplayLabel(const PCB_CONSTRAINT &aConstraint, EDA_UNITS aUnits)
Display label for a constraint in lists and menus.
@ WHOLE
The item as a whole (a segment as a line, a circle).
PCB_CONSTRAINT_TYPE
The geometric relationship a PCB_CONSTRAINT enforces between its members.
@ CONCENTRIC
Two arcs/circles share a center.
@ SYMMETRIC
Two points are mirror images about an axis.
@ 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.
@ 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.
@ ARC_ANGLE
An arc has a driving or reference swept-angle value.
std::deque< PCB_CONSTRAINT * > CONSTRAINTS
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 participant in a constraint: a referenced board item plus the feature of that item that participa...
wxString result
Test unit parsing edge cases and error handling.
@ PCB_SHAPE_T
class PCB_SHAPE, a segment not on copper layers
@ PCB_SHAPE_LOCATE_SEGMENT_T
VECTOR2< int32_t > VECTOR2I
VECTOR2< double > VECTOR2D