/* * This program source code file is part of KiCad, a free EDA CAD application. * * Copyright (C) 2023 KiCad Developers, see AUTHORS.txt for contributors. * * 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 */ #ifndef ITEM_MODIFICATION_ROUTINE_H_ #define ITEM_MODIFICATION_ROUTINE_H_ #include #include #include #include #include #include #include /** * @brief An object that has the ability to modify items on a board * * For example, such an object could take pairs of lines and fillet them, * or produce other modification to items. * * Deliberately not called a "tool" to distinguish it from true * tools that are used interactively by the user. */ class ITEM_MODIFICATION_ROUTINE { public: /* * Handlers for receiving changes from the tool * * These are used to allow the tool's caller to make changes to * affected board items using extra information that the tool * does not have access to (e.g. is this an FP editor, was * the line created from a rectangle and needs to be added, not * modified, etc). * * We can't store them up until the end, because modifications * need the old state to be known, so this allows the caller to * inject the dependencies for how to handle the changes. */ class CHANGE_HANDLER { public: virtual ~CHANGE_HANDLER() = default; /** * @brief Report that the tools wants to add a new item to the board * * @param aItem the new item */ virtual void AddNewItem( std::unique_ptr aItem ) = 0; /** * @brief Report that the tool has modified an item on the board * * @param aItem the modified item */ virtual void MarkItemModified( PCB_SHAPE& aItem ) = 0; /** * @brief Report that the tool has deleted an item on the board * * @param aItem the deleted item */ virtual void DeleteItem( PCB_SHAPE& aItem ) = 0; }; /** * @brief A handler that is based on a set of callbacks provided * by the user of the ITEM_MODIFICATION_ROUTINE */ class CALLABLE_BASED_HANDLER : public CHANGE_HANDLER { public: /** * Handler for creating a new item on the board * * @param PCB_SHAPE& the shape to add */ using CREATION_HANDLER = std::function )>; /** * Handler for modifying or deleting an existing item on the board * * @param PCB_SHAPE& the shape to modify */ using MODIFICATION_HANDLER = std::function; /** * Handler for modifying or deleting an existing item on the board * * @param PCB_SHAPE& the shape to delete */ using DELETION_HANDLER = std::function; CALLABLE_BASED_HANDLER( CREATION_HANDLER aCreationHandler, MODIFICATION_HANDLER aModificationHandler, DELETION_HANDLER aDeletionHandler ) : m_creationHandler( aCreationHandler ), m_modificationHandler( aModificationHandler ), m_deletionHandler( aDeletionHandler ) { } /** * @brief Report that the tools wants to add a new item to the board * * @param aItem the new item */ void AddNewItem( std::unique_ptr aItem ) override { m_creationHandler( std::move( aItem ) ); } /** * @brief Report that the tool has modified an item on the board * * @param aItem the modified item */ void MarkItemModified( PCB_SHAPE& aItem ) override { m_modificationHandler( aItem ); } /** * @brief Report that the tool has deleted an item on the board * * @param aItem the deleted item */ void DeleteItem( PCB_SHAPE& aItem ) override { m_deletionHandler( aItem ); } CREATION_HANDLER m_creationHandler; MODIFICATION_HANDLER m_modificationHandler; DELETION_HANDLER m_deletionHandler; }; ITEM_MODIFICATION_ROUTINE( BOARD_ITEM* aBoard, CHANGE_HANDLER& aHandler ) : m_board( aBoard ), m_handler( aHandler ), m_numSuccesses( 0 ), m_numFailures( 0 ) { } virtual ~ITEM_MODIFICATION_ROUTINE() = default; unsigned GetSuccesses() const { return m_numSuccesses; } unsigned GetFailures() const { return m_numFailures; } virtual wxString GetCommitDescription() const = 0; /** * @brief Get a status message to show when the routine is complete * * Usually this will be an error or nothing. */ virtual std::optional GetStatusMessage() const = 0; protected: /** * The BOARD used when creating new shapes */ BOARD_ITEM* GetBoard() const { return m_board; } /** * Mark that one of the actions succeeded. */ void AddSuccess() { ++m_numSuccesses; } /** * Mark that one of the actions failed. */ void AddFailure() { ++m_numFailures; } /** * @brief Helper function useful for multiple tools: modify a line or delete * it if it has zero length * * @param aItem the line to modify * @param aSeg the new line geometry */ bool ModifyLineOrDeleteIfZeroLength( PCB_SHAPE& aItem, const SEG& aSeg ); /** * @brief Access the handler for making changes to the board */ CHANGE_HANDLER& GetHandler() { return m_handler; } private: BOARD_ITEM* m_board; CHANGE_HANDLER& m_handler; unsigned m_numSuccesses; unsigned m_numFailures; }; /** * A tool that acts on a pair of lines. For example, fillets, chamfers, extensions, etc */ class PAIRWISE_LINE_ROUTINE : public ITEM_MODIFICATION_ROUTINE { public: PAIRWISE_LINE_ROUTINE( BOARD_ITEM* aBoard, CHANGE_HANDLER& aHandler ) : ITEM_MODIFICATION_ROUTINE( aBoard, aHandler ) { } /** * @brief Perform the action on the pair of lines given * * The routine will be called repeatedly with all possible pairs of lines * in the selection. The tools should handle the ones it's interested in. * This means that the same line can appear multiple times with different * partners. * * The routine can skip lines that it's not interested in by returning without * adding to the success or failure count. * * @param aLineA the first line * @param aLineB the second line * @return did the action succeed */ virtual void ProcessLinePair( PCB_SHAPE& aLineA, PCB_SHAPE& aLineB ) = 0; }; /** * Pairwise line tool that adds a fillet to the lines. */ class LINE_FILLET_ROUTINE : public PAIRWISE_LINE_ROUTINE { public: LINE_FILLET_ROUTINE( BOARD_ITEM* aBoard, CHANGE_HANDLER& aHandler, int filletRadiusIU ) : PAIRWISE_LINE_ROUTINE( aBoard, aHandler ), m_filletRadiusIU( filletRadiusIU ) { } wxString GetCommitDescription() const override; std::optional GetStatusMessage() const override; void ProcessLinePair( PCB_SHAPE& aLineA, PCB_SHAPE& aLineB ) override; private: int m_filletRadiusIU; }; /** * Pairwise line tool that adds a chamfer between the lines. */ class LINE_CHAMFER_ROUTINE : public PAIRWISE_LINE_ROUTINE { public: LINE_CHAMFER_ROUTINE( BOARD_ITEM* aBoard, CHANGE_HANDLER& aHandler, CHAMFER_PARAMS aChamferParams ) : PAIRWISE_LINE_ROUTINE( aBoard, aHandler ), m_chamferParams( std::move( aChamferParams ) ) { } wxString GetCommitDescription() const override; std::optional GetStatusMessage() const override; void ProcessLinePair( PCB_SHAPE& aLineA, PCB_SHAPE& aLineB ) override; private: const CHAMFER_PARAMS m_chamferParams; }; /** * Pairwise extend to corner or meeting tool */ class LINE_EXTENSION_ROUTINE : public PAIRWISE_LINE_ROUTINE { public: LINE_EXTENSION_ROUTINE( BOARD_ITEM* aBoard, CHANGE_HANDLER& aHandler ) : PAIRWISE_LINE_ROUTINE( aBoard, aHandler ) { } wxString GetCommitDescription() const override; std::optional GetStatusMessage() const override; void ProcessLinePair( PCB_SHAPE& aLineA, PCB_SHAPE& aLineB ) override; }; /** * A routine that modifies polygons using boolean operations */ class POLYGON_BOOLEAN_ROUTINE : public ITEM_MODIFICATION_ROUTINE { public: POLYGON_BOOLEAN_ROUTINE( BOARD_ITEM* aBoard, CHANGE_HANDLER& aHandler ) : ITEM_MODIFICATION_ROUTINE( aBoard, aHandler ), m_workingPolygon( nullptr ) { } void ProcessShape( PCB_SHAPE& aPcbShape ); protected: PCB_SHAPE* GetWorkingPolygon() const { return m_workingPolygon; } virtual bool ProcessSubsequentPolygon( const SHAPE_POLY_SET& aPolygon ) = 0; private: PCB_SHAPE* m_workingPolygon; }; class POLYGON_MERGE_ROUTINE : public POLYGON_BOOLEAN_ROUTINE { public: POLYGON_MERGE_ROUTINE( BOARD_ITEM* aBoard, CHANGE_HANDLER& aHandler ) : POLYGON_BOOLEAN_ROUTINE( aBoard, aHandler ) { } wxString GetCommitDescription() const override; std::optional GetStatusMessage() const override; private: bool ProcessSubsequentPolygon( const SHAPE_POLY_SET& aPolygon ) override; }; class POLYGON_SUBTRACT_ROUTINE : public POLYGON_BOOLEAN_ROUTINE { public: POLYGON_SUBTRACT_ROUTINE( BOARD_ITEM* aBoard, CHANGE_HANDLER& aHandler ) : POLYGON_BOOLEAN_ROUTINE( aBoard, aHandler ) { } wxString GetCommitDescription() const override; std::optional GetStatusMessage() const override; private: bool ProcessSubsequentPolygon( const SHAPE_POLY_SET& aPolygon ) override; }; class POLYGON_INTERSECT_ROUTINE : public POLYGON_BOOLEAN_ROUTINE { public: POLYGON_INTERSECT_ROUTINE( BOARD_ITEM* aBoard, CHANGE_HANDLER& aHandler ) : POLYGON_BOOLEAN_ROUTINE( aBoard, aHandler ) { } wxString GetCommitDescription() const override; std::optional GetStatusMessage() const override; private: bool ProcessSubsequentPolygon( const SHAPE_POLY_SET& aPolygon ) override; }; #endif /* ITEM_MODIFICATION_ROUTINE_H_ */