67 std::unique_lock<std::mutex> lock(
m_mutex );
77 std::unique_lock<std::mutex> lock(
m_mutex );
95 if( aAcceptImmediately )
109 std::lock_guard<std::mutex> lock(
m_mutex );
114 template <
typename Func>
117 std::lock_guard<std::mutex> lock(
m_mutex );
135 std::unique_lock<std::mutex> lock(
m_mutex );
187 const std::chrono::milliseconds acceptanceTimeout(
190 m_activationHelper = std::make_unique<ACTIVATION_HELPER<std::unique_ptr<PENDING_BATCH>>>(
192 [
this]( std::unique_ptr<PENDING_BATCH>&& aAccepted )
196 wxCHECK_MSG( aAccepted !=
nullptr,
void(),
"Null proposal accepted" );
215 std::size_t hash =
hash_val( aIsPersistent );
226 std::unique_ptr<CONSTRUCTION_ITEM_BATCH> aBatch,
bool aIsPersistent )
228 if( aBatch->empty() )
236 std::make_unique<PENDING_BATCH>(
PENDING_BATCH{ std::move( *aBatch ), aIsPersistent } );
240 m_activationHelper->ProposeActivation( std::move( pendingBatch ), hash, aIsPersistent );
274 std::vector<CONSTRUCTION_ITEM_BATCH> persistentBatches, temporaryBatches;
278 if( aAcceptedBatch->IsPersistent )
285 bool anyNewItems =
false;
320 getInvolved( batch );
321 temporaryBatches.push_back( batch );
328 const auto addDrawables =
329 [&](
const std::vector<CONSTRUCTION_ITEM_BATCH>& aBatches,
bool aIsPersistent )
343 addDrawables( persistentBatches,
true );
344 addDrawables( temporaryBatches,
false );
366 std::vector<CONSTRUCTION_ITEM_BATCH>& aToExtend )
const
376 aToExtend.push_back( batch );
384 [&](
const std::unique_ptr<PENDING_BATCH>& aPending )
387 aToExtend.push_back( aPending->Batch );
409 if( aDir.
x == 0 && aDir.
y == 0 )
423 if( dx < 0 || ( dx == 0 && dy < 0 ) )
438 if( normalized.
x == 0 && normalized.
y == 0 )
441 for(
size_t i = 0; i < aDirections.size(); ++i )
443 if( aDirections[i] == normalized )
444 return static_cast<int>( i );
453 std::vector<VECTOR2I> uniqueDirections;
454 uniqueDirections.reserve( aDirections.size() );
456 for(
const VECTOR2I& direction : aDirections )
460 if( normalized.
x == 0 && normalized.
y == 0 )
463 if( std::find( uniqueDirections.begin(), uniqueDirections.end(), normalized )
464 == uniqueDirections.end() )
466 uniqueDirections.push_back( normalized );
564 std::optional<int> aDistToNearest,
569 wxLogTrace(
traceSnap,
"GetNearestSnapLinePoint: cursor=(%d, %d), nearestGrid=(%d, %d), distToNearest=%s, snapRange=%d",
570 aCursor.
x, aCursor.
y, aNearestGrid.
x, aNearestGrid.
y,
571 aDistToNearest ? wxString::Format(
"%d", *aDistToNearest ) : wxString(
"none" ), aSnapRange );
575 wxLogTrace(
traceSnap,
" No snap line origin or no directions, returning nullopt" );
579 const bool gridBetterThanNearest = !aDistToNearest || *aDistToNearest > aSnapRange;
580 const bool gridActive = aGridSize.
x > 0 && aGridSize.
y > 0;
582 wxLogTrace(
traceSnap,
" snapLineOrigin=(%d, %d), directions count=%zu, gridBetterThanNearest=%d, gridActive=%d",
585 if( !gridBetterThanNearest )
587 wxLogTrace(
traceSnap,
" Grid not better than nearest, returning nullopt" );
591 const int escapeRange = 2 * aSnapRange;
594 wxLogTrace(
traceSnap,
" escapeRange=%d, longRangeEscapeAngle=%.1f deg",
595 escapeRange, longRangeEscapeAngle.
AsDegrees() );
601 double bestPerpDistance = std::numeric_limits<double>::max();
602 std::optional<VECTOR2I> bestSnapPoint;
610 if( dirLength == 0.0 )
612 wxLogTrace(
traceSnap,
" Direction %zu: zero length, skipping", ii );
616 VECTOR2D dirUnit = dirVector / dirLength;
618 double distanceAlong =
delta.Dot( dirUnit );
619 VECTOR2D projection = origin + dirUnit * distanceAlong;
623 wxLogTrace(
traceSnap,
" Direction %zu: dir=(%d, %d), perpDist=%.1f, distAlong=%.1f",
624 ii, direction.
x, direction.
y, perpDistance, distanceAlong );
626 if( perpDistance > aSnapRange )
628 wxLogTrace(
traceSnap,
" perpDistance > snapRange, skipping" );
632 bool escaped =
false;
634 if( perpDistance >= escapeRange )
638 double angleDiff = ( deltaAngle - directionAngle ).Normalize180().AsDegrees();
640 wxLogTrace(
traceSnap,
" In escape range: deltaAngle=%.1f, dirAngle=%.1f, angleDiff=%.1f",
646 wxLogTrace(
traceSnap,
" ESCAPED (angle diff too large)" );
652 wxLogTrace(
traceSnap,
" Not updating (escaped)" );
662 if( direction.
x == 0 && direction.
y != 0 )
665 snapPoint.
x = origin.
x;
666 snapPoint.
y = aNearestGrid.
y;
667 wxLogTrace(
traceSnap,
" Vertical line: snapping to grid Y, snapPoint=(%.1f, %.1f)",
668 snapPoint.
x, snapPoint.
y );
670 else if( direction.
y == 0 && direction.
x != 0 )
673 snapPoint.
x = aNearestGrid.
x;
674 snapPoint.
y = origin.
y;
675 wxLogTrace(
traceSnap,
" Horizontal line: snapping to grid X, snapPoint=(%.1f, %.1f)",
676 snapPoint.
x, snapPoint.
y );
681 VECTOR2D gridOriginD( aGridOrigin );
682 VECTOR2D relProjection = projection - gridOriginD;
685 double bestGridScore = std::numeric_limits<double>::max();
686 VECTOR2D bestGridPoint = projection;
688 for(
int dx = -1; dx <= 1; ++dx )
690 for(
int dy = -1; dy <= 1; ++dy )
692 double gridX = std::round( relProjection.
x / aGridSize.
x ) * aGridSize.
x + dx * aGridSize.
x;
693 double gridY = std::round( relProjection.
y / aGridSize.
y ) * aGridSize.
y + dy * aGridSize.
y;
694 VECTOR2D gridPt( gridX + gridOriginD.
x, gridY + gridOriginD.
y );
697 VECTOR2D gridDelta = gridPt - origin;
698 double gridDistAlong = gridDelta.
Dot( dirUnit );
699 VECTOR2D gridProjection = origin + dirUnit * gridDistAlong;
700 double gridPerpDist = ( gridPt - gridProjection ).EuclideanNorm();
703 double distFromCursor = ( gridPt -
cursor ).EuclideanNorm();
706 double score = gridPerpDist + distFromCursor * 0.1;
708 if( score < bestGridScore )
710 bestGridScore = score;
711 bestGridPoint = gridPt;
716 snapPoint = bestGridPoint;
717 wxLogTrace(
traceSnap,
" Diagonal line: snapping to grid intersection, snapPoint=(%.1f, %.1f)",
718 snapPoint.
x, snapPoint.
y );
723 wxLogTrace(
traceSnap,
" Grid not active, using projection" );
726 if( perpDistance < bestPerpDistance )
728 bestPerpDistance = perpDistance;
729 bestSnapPoint =
KiROUND( snapPoint );
730 wxLogTrace(
traceSnap,
" NEW BEST: perpDist=%.1f, snapPoint=(%d, %d)",
731 bestPerpDistance, bestSnapPoint->x, bestSnapPoint->y );
735 wxLogTrace(
traceSnap,
" Not updating (perpDist=%.1f >= bestPerp=%.1f)",
736 perpDistance, bestPerpDistance );
742 wxLogTrace(
traceSnap,
" RETURNING bestSnapPoint=(%d, %d)", bestSnapPoint->x, bestSnapPoint->y );
743 return *bestSnapPoint;
746 wxLogTrace(
traceSnap,
" RETURNING nullopt (no valid snap found)" );
777 if( aBrackets ==
GetViewItem().DimensionBrackets() )
795 std::vector<KIGFX::CONSTRUCTION_GEOM::SNAP_GUIDE> guides;
800 if( origin && !directions.empty() )
802 const std::optional<int> activeDirection =
m_snapLineManager.GetActiveDirection();
803 const int guideLength = 500000;
805 for(
size_t ii = 0; ii < directions.size(); ++ii )
807 const VECTOR2I& direction = directions[ii];
809 if( direction.
x == 0 && direction.
y == 0 )
812 VECTOR2I scaled = direction * guideLength;
815 guide.
Segment =
SEG( *origin - scaled, *origin + scaled );
817 if( activeDirection && *activeDirection ==
static_cast<int>( ii ) )
828 guides.push_back( guide );
844std::vector<CONSTRUCTION_MANAGER::CONSTRUCTION_ITEM_BATCH>
847 std::vector<CONSTRUCTION_MANAGER::CONSTRUCTION_ITEM_BATCH> batches;
852 snapLineOrigin.has_value() )
864 const std::optional<int> activeDirection =
m_snapLineManager.GetActiveDirection();
866 for(
size_t ii = 0; ii < directions.size(); ++ii )
868 const VECTOR2I& direction = directions[ii];
870 VECTOR2I scaledDirection = direction * 100000;
873 entry.
Drawable =
LINE{ *snapLineOrigin, *snapLineOrigin + scaledDirection };
874 entry.
LineWidth = ( activeDirection && *activeDirection ==
static_cast<int>( ii ) ) ? 2 : 1;
880 batches.push_back( std::move( batch ) );
constexpr BOX2I KiROUND(const BOX2D &aBoxD)
void onTimerExpiry(wxTimerEvent &aEvent)
Timer expiry callback in the UI thread.
void acceptPendingProposal()
ACTIVATION_HELPER(std::chrono::milliseconds aTimeout, ACTIVATION_CALLBACK aCallback)
void InspectPendingProposal(Func &&aFunc) const
std::optional< std::size_t > m_lastAcceptedProposalTag
The last proposal that was accepted.
ACTIVATION_CALLBACK m_callback
Callback to call when the proposal is accepted.
std::chrono::milliseconds m_timeout
Activation timeout in milliseconds.
void ProposeActivation(T &&aProposal, std::size_t aProposalTag, bool aAcceptImmediately)
std::optional< std::size_t > m_pendingProposalTag
The last proposal tag that was made.
T m_lastProposal
The most recently-proposed item.
std::function< void(T &&)> ACTIVATION_CALLBACK
static const ADVANCED_CFG & GetCfg()
Get the singleton instance's config, which is shared by all consumers.
A color representation with 4 components: red, green, blue, alpha.
void GetPendingConstructionItems(std::vector< CONSTRUCTION_ITEM_BATCH > &aToExtend) const
Get the construction proposal awaiting activation.
void GetConstructionItems(std::vector< CONSTRUCTION_ITEM_BATCH > &aToExtend) const
Get the list of additional geometry items that should be considered.
void ProposeConstructionItems(std::unique_ptr< CONSTRUCTION_ITEM_BATCH > aBatch, bool aIsPersistent)
Add a batch of construction items to the helper.
CONSTRUCTION_VIEW_HANDLER & m_viewHandler
bool HasActiveConstruction() const
CONSTRUCTION_MANAGER(CONSTRUCTION_VIEW_HANDLER &aViewHandler)
void CancelProposal()
Cancel outstanding proposals for new geometry.
std::deque< CONSTRUCTION_ITEM_BATCH > m_temporaryConstructionBatches
Temporary construction items are added and removed as needed.
void Clear()
Clear all construction items.
std::vector< CONSTRUCTION_ITEM > CONSTRUCTION_ITEM_BATCH
std::optional< CONSTRUCTION_ITEM_BATCH > m_persistentConstructionBatch
Within one "operation", there is one set of construction items that are "persistent",...
std::unique_ptr< ACTIVATION_HELPER< std::unique_ptr< PENDING_BATCH > > > m_activationHelper
unsigned getMaxTemporaryBatches() const
How many batches of temporary construction items can be active at once.
std::mutex m_batchesMutex
Protects the persistent and temporary construction batches.
bool InvolvesAllGivenRealItems(const std::vector< EDA_ITEM * > &aItems) const
Check if all 'real' (non-null = constructed) the items in the batch are in the list of items currentl...
void acceptConstructionItems(std::unique_ptr< PENDING_BATCH > aAcceptedBatchHash)
std::set< EDA_ITEM * > m_involvedItems
Set of all items for which construction geometry has been added.
Interface wrapper for the construction geometry preview with a callback to signal the view owner that...
CONSTRUCTION_VIEW_HANDLER(KIGFX::CONSTRUCTION_GEOM &aHelper)
KIGFX::CONSTRUCTION_GEOM & GetViewItem()
A base class for most all the KiCad significant classes used in schematics and boards.
A color representation with 4 components: red, green, blue, alpha.
Shows construction geometry for things like line extensions, arc centers, etc.
void AddDrawable(const DRAWABLE &aItem, bool aIsPersistent, int aLineWidth=1)
void SetSnapGuides(std::vector< SNAP_GUIDE > aGuides)
void SetDimensionBrackets(std::vector< SEG > aBrackets)
bool HasDimensionBrackets() const
OPT_VECTOR2I GetNearestSnapLinePoint(const VECTOR2I &aCursor, const VECTOR2I &aNearestGrid, std::optional< int > aDistToNearest, int snapRange, const VECTOR2D &aGridSize=VECTOR2D(0, 0), const VECTOR2I &aGridOrigin=VECTOR2I(0, 0)) const
If the snap line is active, return the best snap point that is closest to the cursor.
OPT_VECTOR2I m_snapLineEnd
void SetDirections(const std::vector< VECTOR2I > &aDirections)
void SetSnappedAnchor(const VECTOR2I &aAnchorPos)
Inform this manager that an anchor snap has been made.
CONSTRUCTION_VIEW_HANDLER & m_viewHandler
void ClearSnapLine()
Clear the snap line origin and end points.
std::vector< VECTOR2I > m_directions
SNAP_MANAGER * m_snapManager
SNAP_LINE_MANAGER(CONSTRUCTION_VIEW_HANDLER &aViewHandler)
void SetSnapLineOrigin(const VECTOR2I &aOrigin)
The snap point is a special point that is located at the last point the cursor snapped to.
std::optional< int > m_activeDirection
void SetSnapLineEnd(const OPT_VECTOR2I &aSnapPoint)
Set the end point of the snap line.
OPT_VECTOR2I m_snapLineOrigin
A SNAP_MANAGER glues together the snap line manager and construction manager., along with some other ...
void SetSnapGuideColors(const KIGFX::COLOR4D &aBase, const KIGFX::COLOR4D &aHighlight)
KIGFX::COLOR4D m_snapGuideColor
KIGFX::COLOR4D m_snapGuideHighlightColor
GFX_UPDATE_CALLBACK m_updateCallback
CONSTRUCTION_MANAGER m_constructionManager
std::vector< CONSTRUCTION_MANAGER::CONSTRUCTION_ITEM_BATCH > GetConstructionItems() const
Get a list of all the active construction geometry, computed from the combined state of the snap line...
void updateView() override
SNAP_LINE_MANAGER m_snapLineManager
SNAP_MANAGER(KIGFX::CONSTRUCTION_GEOM &aHelper)
void SetDimensionBrackets(std::vector< SEG > aBrackets)
T EuclideanNorm() const
Compute the Euclidean norm of the vector, which is defined as sqrt(x ** 2 + y ** 2).
constexpr extended_type Dot(const VECTOR2< T > &aVector) const
Compute dot product of self with aVector.
static std::size_t HashConstructionBatchSources(const CONSTRUCTION_MANAGER::CONSTRUCTION_ITEM_BATCH &aBatch, bool aIsPersistent)
Construct a hash based on the sources of the items in the batch.
static VECTOR2I normalizeDirection(const VECTOR2I &aDir)
static std::optional< int > findDirectionIndex(const std::vector< VECTOR2I > &aDirections, const VECTOR2I &aDelta)
const wxChar *const traceSnap
Flag to enable snap/grid helper debug tracing.
static constexpr void hash_combine(std::size_t &seed)
This is a dummy function to take the final case of hash_combine below.
static constexpr std::size_t hash_val(const Types &... args)
The Cairo implementation of the graphics abstraction layer.
EDA_ANGLE abs(const EDA_ANGLE &aAngle)
std::optional< VECTOR2I > OPT_VECTOR2I
KIGFX::CONSTRUCTION_GEOM::DRAWABLE Drawable
Items to be used for the construction of "virtual" anchors, for example, when snapping to a point inv...
std::vector< DRAWABLE_ENTRY > Constructions
CONSTRUCTION_ITEM_BATCH Batch
wxLogTrace helper definitions.
VECTOR2< int32_t > VECTOR2I
VECTOR2< double > VECTOR2D