7
mirror of https://gitlab.com/kicad/code/kicad.git synced 2025-02-16 12:48:56 +00:00
kicad/include/tool/construction_manager.h
John Beard c120254aa4 Pcbnew: snap to object faster if no objects activated
Otherwise it's annoying to snap to the first item you get to
if you have to wait every time.
2025-01-25 23:57:38 +08:00

288 lines
9.8 KiB
C++

/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright The KiCad Developers, see AUTHORS.txt for contributors.
*
* @author Maciej Suminski <maciej.suminski@cern.ch>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, you may find one here:
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
* or you may search the http://www.gnu.org website for the version 2 license,
* or you may write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#pragma once
#include <deque>
#include <memory>
#include <mutex>
#include <vector>
#include <preview_items/construction_geom.h>
template <typename T>
class ACTIVATION_HELPER;
class EDA_ITEM;
/**
* Interface wrapper for the construction geometry preview with a callback to signal the
* view owner that the view needs to be updated.
*/
class CONSTRUCTION_VIEW_HANDLER
{
public:
CONSTRUCTION_VIEW_HANDLER( KIGFX::CONSTRUCTION_GEOM& aHelper ) :
m_constructionGeomPreview( aHelper )
{
}
virtual void updateView() = 0;
KIGFX::CONSTRUCTION_GEOM& GetViewItem() { return m_constructionGeomPreview; }
private:
// An (external) construction helper view item, that this manager adds/removes
// construction objects to/from.
KIGFX::CONSTRUCTION_GEOM& m_constructionGeomPreview;
};
/**
* A class that manages the geometry of a "snap line".
*
* This is a line that has a start point (the "snap origin") and an end point (the "snap end").
* The end can only be set if the origin is set. If the origin is set, the end will be unset.
*/
class SNAP_LINE_MANAGER
{
public:
SNAP_LINE_MANAGER( CONSTRUCTION_VIEW_HANDLER& aViewHandler );
/**
* The snap point is a special point that is located at the last point the cursor
* snapped to.
*
* If it is set, the construction manager may add extra construction geometry to the helper
* extending from the snap point origin to the cursor, which is the 'snap line'.
*/
void SetSnapLineOrigin( const VECTOR2I& aOrigin );
/**
* Set the end point of the snap line.
*
* Passing std::nullopt will unset the end point, but keep the origin.
*/
void SetSnapLineEnd( const OPT_VECTOR2I& aSnapPoint );
/**
* Clear the snap line origin and end points.
*/
void ClearSnapLine();
const OPT_VECTOR2I& GetSnapLineOrigin() const { return m_snapLineOrigin; }
bool HasCompleteSnapLine() const { return m_snapLineOrigin && m_snapLineEnd; }
/**
* Inform this manager that an anchor snap has been made.
*
* This will also update the start or end of the snap line as appropriate.
*/
void SetSnappedAnchor( const VECTOR2I& aAnchorPos );
/**
* If the snap line is active, return the best snap point that is closest to the cursor.
*
* If there's no active snap line, return std::nullopt.
*
* If there's a snap very near, use that otherwise, use the grid point.
* With this point, snap to it on an H/V axis.
*
* Then, if there's a grid point near, snap to it on an H/V axis.
*
* @param aCursor The cursor position.
* @param aNearestGrid The nearest grid point to the cursor.
* @param aDistToNearest The distance to the nearest non-grid snap point, if any.
* @param snapRange The snap range.
*/
OPT_VECTOR2I GetNearestSnapLinePoint( const VECTOR2I& aCursor, const VECTOR2I& aNearestGrid,
std::optional<int> aDistToNearest, int snapRange ) const;
private:
// If a snap point is "active", extra construction geometry is added to the helper
// extending from the snap point to the cursor.
OPT_VECTOR2I m_snapLineOrigin;
OPT_VECTOR2I m_snapLineEnd;
// The view handler to update when the snap line changes
CONSTRUCTION_VIEW_HANDLER& m_viewHandler;
};
/**
* A class that manages "construction" objects and geometry.
*
* These are things like line extensions, arc centers, etc.
*/
class CONSTRUCTION_MANAGER
{
public:
CONSTRUCTION_MANAGER( CONSTRUCTION_VIEW_HANDLER& aViewHandler );
~CONSTRUCTION_MANAGER();
enum class SOURCE
{
FROM_ITEMS,
FROM_SNAP_LINE,
};
/**
* Items to be used for the construction of "virtual" anchors, for example, when snapping to
* a point involving an _extension_ of an existing line or arc.
*
* One item can have multiple construction items (e.g. an arc can have a circle and centre
* point).
*/
struct CONSTRUCTION_ITEM
{
SOURCE Source;
EDA_ITEM* Item;
std::vector<KIGFX::CONSTRUCTION_GEOM::DRAWABLE> Constructions;
};
// A single batch of construction items. Once batch contains all the items (and associated
// construction geometry) that should be shown for one point of interest.
// More than one batch may be shown on the screen at the same time.
using CONSTRUCTION_ITEM_BATCH = std::vector<CONSTRUCTION_ITEM>;
/**
* Add a batch of construction items to the helper.
*
* @param aBatch The batch of construction items to add.
* @param aIsPersistent If true, the batch is considered "persistent" and will always be shown
* (and it will replace any previous persistent batch).
* If false, the batch is temporary and may be pushed out by other batches.
*/
void ProposeConstructionItems( std::unique_ptr<CONSTRUCTION_ITEM_BATCH> aBatch,
bool aIsPersistent );
/**
* Cancel outstanding proposals for new geometry.
*/
void CancelProposal();
/**
* Check if all 'real' (non-null = constructed) the items in the batch are in the list of items
* currently 'involved' in an active construction.
*/
bool InvolvesAllGivenRealItems( const std::vector<EDA_ITEM*>& aItems ) const;
/**
* Get the list of additional geometry items that should be considered.
*/
void GetConstructionItems( std::vector<CONSTRUCTION_ITEM_BATCH>& aToExtend ) const;
bool HasActiveConstruction() const;
private:
struct PENDING_BATCH;
void acceptConstructionItems( std::unique_ptr<PENDING_BATCH> aAcceptedBatchHash );
/**
* How many batches of temporary construction items can be active at once.
*
* This is to prevent too much clutter.
*/
unsigned getMaxTemporaryBatches() const;
CONSTRUCTION_VIEW_HANDLER& m_viewHandler;
/// Within one "operation", there is one set of construction items that are
/// "persistent", and are always shown. Usually the original item and any
/// extensions.
std::optional<CONSTRUCTION_ITEM_BATCH> m_persistentConstructionBatch;
/// Temporary construction items are added and removed as needed.
std::deque<CONSTRUCTION_ITEM_BATCH> m_temporaryConstructionBatches;
/// Set of all items for which construction geometry has been added.
std::set<EDA_ITEM*> m_involvedItems;
std::unique_ptr<ACTIVATION_HELPER<std::unique_ptr<PENDING_BATCH>>> m_activationHelper;
/// Protects the persistent and temporary construction batches.
mutable std::mutex m_batchesMutex;
};
/**
* A SNAP_MANAGER glues together the snap line manager and construction manager.,
* along with some other state. It provides information for generating snap
* anchors based on this state, as well as keeping the state of visible
* construction geometry involved in that process.
*
* Probably only used by GRID_HELPERs, but it's neater to keep it separate,
* as there's quite a bit of state to manage.
*
* This is also where you may wish to add other 'virtual' snapping state,
* such as 'equal-space' snapping, etc.
*/
class SNAP_MANAGER : public CONSTRUCTION_VIEW_HANDLER
{
public:
using GFX_UPDATE_CALLBACK = std::function<void( bool aShowAnything )>;
SNAP_MANAGER( KIGFX::CONSTRUCTION_GEOM& aHelper );
/**
* Set the callback to call when the construction geometry changes and a view may need updating.
*/
void SetUpdateCallback( GFX_UPDATE_CALLBACK aCallback ) { m_updateCallback = aCallback; }
SNAP_LINE_MANAGER& GetSnapLineManager() { return m_snapLineManager; }
CONSTRUCTION_MANAGER& GetConstructionManager() { return m_constructionManager; }
/**
* Set the reference-only points - these are points that are not snapped to, but can still
* be used for connection to the snap line.
*/
void SetReferenceOnlyPoints( std::vector<VECTOR2I> aPoints )
{
m_referenceOnlyPoints = std::move( aPoints );
}
const std::vector<VECTOR2I>& GetReferenceOnlyPoints() const { return m_referenceOnlyPoints; }
/**
* Get a list of all the active construction geometry, computed from
* the combined state of the snap line and construction manager.
*
* This can be combined with other external geometry to compute snap anchors.
*/
std::vector<CONSTRUCTION_MANAGER::CONSTRUCTION_ITEM_BATCH> GetConstructionItems() const;
public:
void updateView() override;
GFX_UPDATE_CALLBACK m_updateCallback;
SNAP_LINE_MANAGER m_snapLineManager;
CONSTRUCTION_MANAGER m_constructionManager;
std::vector<VECTOR2I> m_referenceOnlyPoints;
};