KiCad PCB EDA Suite
Loading...
Searching...
No Matches
board_constraint_adapter.h
Go to the documentation of this file.
1/*
2 * This program source code file is part of KiCad, a free EDA CAD application.
3 *
4 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 2
9 * of the License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <https://www.gnu.org/licenses/>.
18 */
19
20#ifndef BOARD_CONSTRAINT_ADAPTER_H_
21#define BOARD_CONSTRAINT_ADAPTER_H_
22
23#include <deque>
24#include <functional>
25#include <map>
26#include <memory>
27#include <optional>
28#include <set>
29#include <vector>
30
31#include <kiid.h>
32#include <math/vector2d.h>
33
35
36class BOARD;
37class BOARD_ITEM;
40class PCB_SHAPE;
41
42// planegcs is an Eigen-heavy implementation detail; keep its header out of this one (PIMPL).
43namespace GCS
44{
45class System;
46}
47
48
53{
54 bool solved = false;
55 int freeDof = -1;
56 std::vector<KIID> conflicting;
57 std::vector<KIID> redundant;
58
59 bool IsWellConstrained() const { return solved && freeDof == 0; }
60 bool IsUnderConstrained() const { return freeDof > 0; }
61 bool IsOverConstrained() const { return !conflicting.empty(); }
62};
63
64
73
74
80{
81 int totalFreeDof = 0;
82 std::map<KIID, CONSTRAINT_STATE> shapeStates;
83 std::vector<KIID> conflicting;
84 std::vector<KIID> redundant;
85 std::vector<KIID> errored;
87};
88
89
106{
107public:
110
113
125 bool Build( const std::vector<PCB_SHAPE*>& aShapes, const std::vector<PCB_CONSTRAINT*>& aConstraints,
126 const std::set<KIID>* aFixedShapes = nullptr,
127 const std::vector<PCB_DIMENSION_BASE*>& aDimensions = {} );
128
132 const std::vector<KIID>& UnmappedConstraints() const { return m_unmapped; }
133
148 bool Solve( const CONSTRAINT_MEMBER& aDragged, const VECTOR2I& aCursor, bool aStabilize = false,
149 const std::set<KIID>& aEdited = {},
150 const std::optional<std::pair<CONSTRAINT_MEMBER, VECTOR2I>>& aCoDragged = std::nullopt );
151
153 bool Solve( bool aStabilize = false );
154
157 bool SolveAfterResize( const KIID& aResizedShape );
158
168 std::vector<PCB_SHAPE*> Apply( const std::function<void( BOARD_ITEM* )>& aBeforeWrite = {} );
169
178 void ApplyReferenceValues( const std::function<void( BOARD_ITEM* )>& aBeforeWrite = {} );
179
183 CONSTRAINT_DIAGNOSIS Diagnose();
184
185private:
200
207 {
208 PCB_SHAPE* shape = nullptr;
211 int startX = -1;
212 int endX = -1;
213 int radius = -1;
214 int focusX = -1;
215 int arcStartX = -1;
216 int arcEndX = -1;
217 int startAngle = -1;
218 int endAngle = -1;
220
222 int vertexCount = 0;
223
226 bool startIsLeft = true;
227 bool startIsTop = true;
228 };
229
233 {
234 int x = -1;
235 int y = -1;
236
237 bool IsValid() const { return x >= 0 && y >= 0; }
238 };
239
241 int pushParam( double aValue );
242
245 void recordReferenceValue( PCB_CONSTRAINT* aConstraint );
246
249 void holdFreeSegmentLengths( int aTag, const std::set<KIID>& aShapes );
250
253 void holdFreeArcRadii( int aTag, const std::set<KIID>& aShapes );
254
256 void holdArcRadius( const SHAPE_VARS& aVars, int aTag );
257
260 void softPinPoint( const ANCHOR_PARAMS& aPoint, int aTag, std::optional<double> aWeight = std::nullopt );
261
263 void softPinPoint( int aPointX, int aTag, std::optional<double> aWeight = std::nullopt );
264
267 void holdPolygonVertices( const std::set<KIID>& aShapes, int aTag );
268
271 void pinDraggedShapeRest( const CONSTRAINT_MEMBER& aDragged, int aTag,
272 const CONSTRAINT_MEMBER* aCoDragged = nullptr );
273
276 void pinUneditedShapes( const std::set<KIID>& aEdited, int aTag );
277
280 bool solveSucceeded( int aSolveResult );
281
284 ANCHOR_PARAMS anchorParams( const CONSTRAINT_MEMBER& aMember ) const;
285
288 PCB_DIM_ORTHOGONAL* orthogonalDimensionForMembers( const std::vector<CONSTRAINT_MEMBER>& aMembers ) const;
289
291 double normalizeX( int aIU ) const { return ( aIU - m_originX ) * m_invScale; }
292 double normalizeY( int aIU ) const { return ( aIU - m_originY ) * m_invScale; }
293 double denormalizeX( double aNorm ) const { return aNorm * m_scale + m_originX; }
294 double denormalizeY( double aNorm ) const { return aNorm * m_scale + m_originY; }
295
296 std::unique_ptr<GCS::System> m_gcs;
297
298 // Pointer-stable backing store for every solver double (see class comment).
299 std::deque<double> m_params;
300
301 std::map<KIID, SHAPE_VARS> m_shapeVars;
302 std::map<int, KIID> m_tagToConstraint;
303 std::set<int> m_nonDrivingTags;
304 std::map<int, std::vector<KIID>> m_tagMembers;
305 std::vector<PCB_CONSTRAINT*> m_referenceConstraints;
306 std::vector<KIID> m_unmapped;
307
311
312 double m_originX = 0.0;
313 double m_originY = 0.0;
314 double m_scale = 1.0;
315 double m_invScale = 1.0;
316
317 int m_dragTargetX = -1;
318 int m_dragTargetY = -1;
321
322 // Hold tags reserved by Build() just past the mapped constraints' tags, so a hold can never
323 // collide with (and clear) a real constraint on a large cluster.
326
327 bool m_built = false;
328};
329
330
355CONSTRAINT_DIAGNOSIS SolveCluster( BOARD* aBoard, const CONSTRAINT_MEMBER& aDragged, const VECTOR2I& aCursor,
356 std::vector<PCB_SHAPE*>* aModified = nullptr,
357 const std::function<void( BOARD_ITEM* )>& aBeforeModify = {},
358 bool aIncludeDragged = false, bool aStabilize = false,
359 const std::set<KIID>& aEdited = {},
360 const std::optional<std::pair<CONSTRAINT_MEMBER, VECTOR2I>>& aCoDragged =
361 std::nullopt );
362
363
374 BOARD* aBoard, const PCB_CONSTRAINT* aConstraint,
375 std::vector<PCB_SHAPE*>* aModified = nullptr,
376 const std::function<void( BOARD_ITEM* )>& aBeforeModify = {} );
377
378
381bool BoardHasConstraints( BOARD* aBoard );
382
383
388bool ConstraintItemIsLocked( const BOARD_ITEM* aItem );
389
390
393void ReSolveShapeClusters( BOARD* aBoard, const std::vector<PCB_SHAPE*>& aShapes,
394 std::vector<PCB_SHAPE*>* aModified = nullptr,
395 const std::function<void( BOARD_ITEM* )>& aBeforeModify = {} );
396
397
400void ReSolveShapeClustersHoldingEdited( BOARD* aBoard, const std::vector<PCB_SHAPE*>& aEditedShapes,
401 std::vector<PCB_SHAPE*>* aModified = nullptr,
402 const std::function<void( BOARD_ITEM* )>& aBeforeModify = {} );
403
404
406void ReSolveAfterShapeResize( BOARD* aBoard, PCB_SHAPE* aShape, std::vector<PCB_SHAPE*>* aModified = nullptr,
407 const std::function<void( BOARD_ITEM* )>& aBeforeModify = {} );
408
409
416
417
423{
425 std::vector<KIID> shapeIds;
426 std::vector<KIID> dimensionIds;
427 int freeDof = 0;
428 std::vector<KIID> conflicting;
429 std::vector<KIID> redundant;
430 std::vector<KIID> erroredUnmapped;
431};
432
433
441{
442public:
445
447 void Clear();
448
450 std::size_t SolveCount() const { return m_solveCount; }
451
452private:
454 {
455 std::size_t hash = 0;
457 };
458
459 // Keyed by the cluster sorted constraint-id set which is stable across a geometry or value
460 // edit and changes only when a constraint is added to or removed from the cluster
461 std::map<std::vector<KIID>, CACHE_ENTRY> m_cache;
462 std::size_t m_solveCount = 0;
463};
464
465#endif // BOARD_CONSTRAINT_ADAPTER_H_
CONSTRAINT_STATE
The constraint state of a shape, for the diagnostics overlay tint.
@ UNCONSTRAINED
Not referenced by any constraint.
@ 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).
void ReSolveShapeClustersHoldingEdited(BOARD *aBoard, const std::vector< PCB_SHAPE * > &aEditedShapes, std::vector< PCB_SHAPE * > *aModified=nullptr, const std::function< void(BOARD_ITEM *)> &aBeforeModify={})
Re-solve clusters whose new geometry is authoritative holding every edited shape fully fixed so only ...
bool BoardHasConstraints(BOARD *aBoard)
True if the board or any of its footprints carries at least one geometric constraint.
CONSTRAINT_DIAGNOSIS ApplyConstraintImmediately(BOARD *aBoard, const PCB_CONSTRAINT *aConstraint, std::vector< PCB_SHAPE * > *aModified=nullptr, const std::function< void(BOARD_ITEM *)> &aBeforeModify={})
Solve a just-created constraint's cluster so the geometry snaps to satisfy it (SolidWorks-style),...
CONSTRAINT_DIAGNOSIS SolveCluster(BOARD *aBoard, const CONSTRAINT_MEMBER &aDragged, const VECTOR2I &aCursor, std::vector< PCB_SHAPE * > *aModified=nullptr, const std::function< void(BOARD_ITEM *)> &aBeforeModify={}, bool aIncludeDragged=false, bool aStabilize=false, const std::set< KIID > &aEdited={}, const std::optional< std::pair< CONSTRAINT_MEMBER, VECTOR2I > > &aCoDragged=std::nullopt)
Gather the cluster of shapes transitively constrained with the dragged shape, solve with the dragged ...
bool ConstraintItemIsLocked(const BOARD_ITEM *aItem)
True when the solver must treat aItem as immovable, either locked itself or living inside a locked fo...
void ReSolveShapeClusters(BOARD *aBoard, const std::vector< PCB_SHAPE * > &aShapes, std::vector< PCB_SHAPE * > *aModified=nullptr, const std::function< void(BOARD_ITEM *)> &aBeforeModify={})
Re-solve the clusters of shapes edited outside the solver, e.g.
BOARD_CONSTRAINT_DIAGNOSTICS DiagnoseBoardConstraints(BOARD *aBoard)
Diagnose every constraint cluster on the board (validate only – geometry is not changed) and return t...
void ReSolveAfterShapeResize(BOARD *aBoard, PCB_SHAPE *aShape, std::vector< PCB_SHAPE * > *aModified=nullptr, 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.
void recordReferenceValue(PCB_CONSTRAINT *aConstraint)
Note a non-driving valued constraint so its measured value can be read back after a solve.
BOARD_CONSTRAINT_ADAPTER(const BOARD_CONSTRAINT_ADAPTER &)=delete
BOARD_CONSTRAINT_ADAPTER & operator=(const BOARD_CONSTRAINT_ADAPTER &)=delete
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...
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...
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...
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.
@ 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).
An incremental DiagnoseBoardConstraints for the interactive edit path.
std::size_t SolveCount() const
Clusters actually re-solved across this diagnoser lifetime for testing cache isolation.
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...
Definition board_item.h:83
Information pertinent to a Pcbnew printed circuit board.
Definition board.h:373
A geometric constraint between board items (issue #2329).
Abstract dimension API.
An orthogonal dimension is like an aligned dimension, but the extension lines are locked to the X or ...
Param indices of an anchor coordinates a rect corner aliases mixed start end params so y is not alway...
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 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).
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.
CLUSTER_DIAGNOSIS result
std::size_t hash
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).
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.
std::vector< KIID > conflicting
Constraints the solver reports as over-constraining.
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...
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:683