mirror of
https://gitlab.com/kicad/code/kicad.git
synced 2025-04-21 00:21:25 +00:00
Pcbnew: add bezier editing tool
Adds the initial implementation of bezier overlay (assistant) and geometry manager. This is only implemented in Pcbnew - the code is common, but eeschema doesn't currently use it for any shape. Relates-To: https://gitlab.com/kicad/code/kicad/-/issues/8828
This commit is contained in:
parent
336c87b4c0
commit
b3248095e8
common
include/preview_items
pcbnew
@ -459,6 +459,8 @@ set( COMMON_PREVIEW_ITEMS_SRCS
|
||||
preview_items/anchor_debug.cpp
|
||||
preview_items/arc_assistant.cpp
|
||||
preview_items/arc_geom_manager.cpp
|
||||
preview_items/bezier_assistant.cpp
|
||||
preview_items/bezier_geom_manager.cpp
|
||||
preview_items/centreline_rect_item.cpp
|
||||
preview_items/construction_geom.cpp
|
||||
preview_items/draw_context.cpp
|
||||
|
110
common/preview_items/bezier_assistant.cpp
Normal file
110
common/preview_items/bezier_assistant.cpp
Normal file
@ -0,0 +1,110 @@
|
||||
/*
|
||||
* This program source code file is part of KiCad, a free EDA CAD application.
|
||||
*
|
||||
* Copyright (C) 2017-2022 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
|
||||
*/
|
||||
|
||||
#include "preview_items/bezier_assistant.h"
|
||||
|
||||
#include <preview_items/draw_context.h>
|
||||
#include <preview_items/preview_utils.h>
|
||||
|
||||
#include <gal/graphics_abstraction_layer.h>
|
||||
#include <view/view.h>
|
||||
|
||||
#include <base_units.h>
|
||||
#include <trigo.h>
|
||||
|
||||
using namespace KIGFX::PREVIEW;
|
||||
|
||||
BEZIER_ASSISTANT::BEZIER_ASSISTANT( const BEZIER_GEOM_MANAGER& aManager,
|
||||
const EDA_IU_SCALE& aIuScale, EDA_UNITS aUnits ) :
|
||||
EDA_ITEM( NOT_USED ), m_constructMan( aManager ), m_iuScale( aIuScale ), m_units( aUnits )
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
const BOX2I BEZIER_ASSISTANT::ViewBBox() const
|
||||
{
|
||||
BOX2I tmp;
|
||||
|
||||
// no bounding box when no graphic shown
|
||||
if( m_constructMan.IsReset() )
|
||||
return tmp;
|
||||
|
||||
// this is an edit-time artefact; no reason to try and be smart with the bounding box
|
||||
// (besides, we can't tell the text extents without a view to know what the scale is)
|
||||
tmp.SetMaximum();
|
||||
return tmp;
|
||||
}
|
||||
|
||||
|
||||
void BEZIER_ASSISTANT::ViewDraw( int aLayer, KIGFX::VIEW* aView ) const
|
||||
{
|
||||
KIGFX::GAL& gal = *aView->GetGAL();
|
||||
|
||||
// not in a position to draw anything
|
||||
if( m_constructMan.IsReset() )
|
||||
return;
|
||||
|
||||
gal.ResetTextAttributes();
|
||||
|
||||
const VECTOR2I start = m_constructMan.GetStart();
|
||||
const BEZIER_GEOM_MANAGER::BEZIER_STEPS step = m_constructMan.GetStep();
|
||||
|
||||
KIGFX::PREVIEW::DRAW_CONTEXT preview_ctx( *aView );
|
||||
|
||||
int dashSize = KiROUND( aView->ToWorld( 12 ) );
|
||||
|
||||
if( step >= BEZIER_GEOM_MANAGER::BEZIER_STEPS::SET_CONTROL1 )
|
||||
{
|
||||
// Draw the first control point control line
|
||||
preview_ctx.DrawLineDashed( start, m_constructMan.GetControlC1(), dashSize, dashSize / 2,
|
||||
false );
|
||||
}
|
||||
|
||||
if( step >= BEZIER_GEOM_MANAGER::BEZIER_STEPS::SET_CONTROL2 )
|
||||
{
|
||||
const VECTOR2I c2vec = m_constructMan.GetControlC2() - m_constructMan.GetEnd();
|
||||
// Draw the second control point control line as a double length line
|
||||
// centred on the end point
|
||||
preview_ctx.DrawLineDashed( m_constructMan.GetEnd() - c2vec, m_constructMan.GetControlC2(),
|
||||
dashSize, dashSize / 2, false );
|
||||
}
|
||||
|
||||
std::vector<wxString> cursorStrings;
|
||||
|
||||
if( step >= BEZIER_GEOM_MANAGER::BEZIER_STEPS::SET_END )
|
||||
{
|
||||
// Going to need a better way to get a length here
|
||||
// const int length = m_constructMan.GetBezierLength();
|
||||
// Have enough points to report a bezier length
|
||||
// cursorStrings.push_back( DimensionLabel( wxString::FromUTF8( "L" ), 12300000,
|
||||
// m_iuScale, m_units ) );
|
||||
}
|
||||
|
||||
if( !cursorStrings.empty() )
|
||||
{
|
||||
// place the text next to cursor, on opposite side from radius
|
||||
DrawTextNextToCursor( aView, m_constructMan.GetLastPoint(),
|
||||
start - m_constructMan.GetLastPoint(), cursorStrings,
|
||||
aLayer == LAYER_SELECT_OVERLAY );
|
||||
}
|
||||
}
|
98
common/preview_items/bezier_geom_manager.cpp
Normal file
98
common/preview_items/bezier_geom_manager.cpp
Normal file
@ -0,0 +1,98 @@
|
||||
/*
|
||||
* This program source code file is part of KiCad, a free EDA CAD application.
|
||||
*
|
||||
* Copyright (C) 2024 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
|
||||
*/
|
||||
|
||||
#include "preview_items/bezier_geom_manager.h"
|
||||
|
||||
using namespace KIGFX::PREVIEW;
|
||||
|
||||
|
||||
bool BEZIER_GEOM_MANAGER::acceptPoint( const VECTOR2I& aPt )
|
||||
{
|
||||
switch( getStep() )
|
||||
{
|
||||
case SET_START: return setStart( aPt );
|
||||
case SET_CONTROL1: return setControlC1( aPt );
|
||||
case SET_END: return setEnd( aPt );
|
||||
case SET_CONTROL2: return setControlC2( aPt );
|
||||
case COMPLETE: return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
VECTOR2I BEZIER_GEOM_MANAGER::GetStart() const
|
||||
{
|
||||
return m_start;
|
||||
}
|
||||
|
||||
VECTOR2I BEZIER_GEOM_MANAGER::GetControlC1() const
|
||||
{
|
||||
return m_controlC1;
|
||||
}
|
||||
|
||||
VECTOR2I BEZIER_GEOM_MANAGER::GetEnd() const
|
||||
{
|
||||
return m_end;
|
||||
}
|
||||
|
||||
VECTOR2I BEZIER_GEOM_MANAGER::GetControlC2() const
|
||||
{
|
||||
// The actual bezier C2 point is the reflection over the end point
|
||||
// so that the cursor will be on the C1 point of the next bezier.
|
||||
return m_end - ( m_controlC2 - m_end );
|
||||
}
|
||||
|
||||
bool BEZIER_GEOM_MANAGER::setStart( const VECTOR2I& aStart )
|
||||
{
|
||||
m_start = aStart;
|
||||
// Prevents wierd-looking loops if the control points aren't initialized
|
||||
m_end = aStart;
|
||||
m_controlC1 = aStart;
|
||||
m_controlC2 = aStart;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool BEZIER_GEOM_MANAGER::setControlC1( const VECTOR2I& aControlC1 )
|
||||
{
|
||||
m_controlC1 = aControlC1;
|
||||
m_end = m_controlC1;
|
||||
m_controlC2 = m_controlC1;
|
||||
// It's possible to set the control 1 point to the same as the start point
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BEZIER_GEOM_MANAGER::setEnd( const VECTOR2I& aEnd )
|
||||
{
|
||||
m_end = aEnd;
|
||||
m_controlC2 = m_end;
|
||||
return m_end != m_start;
|
||||
}
|
||||
|
||||
bool BEZIER_GEOM_MANAGER::setControlC2( const VECTOR2I& aControlC2 )
|
||||
{
|
||||
m_controlC2 = aControlC2;
|
||||
// It's possible to set the control 2 point to the same as the end point
|
||||
return true;
|
||||
}
|
71
include/preview_items/bezier_assistant.h
Normal file
71
include/preview_items/bezier_assistant.h
Normal file
@ -0,0 +1,71 @@
|
||||
/*
|
||||
* This program source code file is part of KiCad, a free EDA CAD application.
|
||||
*
|
||||
* Copyright (C) 2024 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
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <eda_item.h>
|
||||
#include <preview_items/bezier_geom_manager.h>
|
||||
#include <layer_ids.h>
|
||||
|
||||
namespace KIGFX
|
||||
{
|
||||
namespace PREVIEW
|
||||
{
|
||||
/**
|
||||
* Represents an assistant draw when interactively drawing a bezier on a canvas.
|
||||
*/
|
||||
class BEZIER_ASSISTANT : public EDA_ITEM
|
||||
{
|
||||
public:
|
||||
BEZIER_ASSISTANT( const BEZIER_GEOM_MANAGER& aManager, const EDA_IU_SCALE& aIuScale,
|
||||
EDA_UNITS aUnits );
|
||||
|
||||
const BOX2I ViewBBox() const override;
|
||||
|
||||
void ViewGetLayers( int aLayers[], int& aCount ) const override
|
||||
{
|
||||
aLayers[0] = LAYER_SELECT_OVERLAY; // Assistant graphics
|
||||
aLayers[1] = LAYER_GP_OVERLAY; // Drop shadows
|
||||
aCount = 2;
|
||||
}
|
||||
|
||||
/**
|
||||
* Draw the assistance (with reference to the construction manager
|
||||
*/
|
||||
void ViewDraw( int aLayer, KIGFX::VIEW* aView ) const override final;
|
||||
|
||||
#if defined( DEBUG )
|
||||
void Show( int x, std::ostream& st ) const override {}
|
||||
#endif
|
||||
|
||||
wxString GetClass() const override { return "BEZIER_ASSISTANT"; }
|
||||
|
||||
void SetUnits( EDA_UNITS aUnits ) { m_units = aUnits; }
|
||||
|
||||
private:
|
||||
const BEZIER_GEOM_MANAGER& m_constructMan;
|
||||
const EDA_IU_SCALE& m_iuScale;
|
||||
EDA_UNITS m_units;
|
||||
};
|
||||
} // namespace PREVIEW
|
||||
} // namespace KIGFX
|
102
include/preview_items/bezier_geom_manager.h
Normal file
102
include/preview_items/bezier_geom_manager.h
Normal file
@ -0,0 +1,102 @@
|
||||
/*
|
||||
* This program source code file is part of KiCad, a free EDA CAD application.
|
||||
*
|
||||
* Copyright (C) 2024 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
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <preview_items/multistep_geom_manager.h>
|
||||
#include <geometry/eda_angle.h>
|
||||
#include <geometry/seg.h>
|
||||
|
||||
namespace KIGFX
|
||||
{
|
||||
namespace PREVIEW
|
||||
{
|
||||
|
||||
|
||||
/**
|
||||
* Manage the construction of a bezier through a series of steps.
|
||||
*
|
||||
* See also @ref KIGFX::PREVIEW::ARC_GEOM_MANAGER.
|
||||
*
|
||||
* Interfaces are provided to return both arc geometry (can be used to set up real beziers on
|
||||
* PCBs, for example) as well as important control points for informational overlays.
|
||||
*/
|
||||
class BEZIER_GEOM_MANAGER : public MULTISTEP_GEOM_MANAGER
|
||||
{
|
||||
public:
|
||||
BEZIER_GEOM_MANAGER() {}
|
||||
|
||||
enum BEZIER_STEPS
|
||||
{
|
||||
SET_START = 0, ///< Waiting to lock in the start point
|
||||
SET_CONTROL1, ///< Waiting to lock in the first control point
|
||||
SET_END, ///< Waiting to lock in the end point
|
||||
SET_CONTROL2, ///< Waiting to lock in the second control point
|
||||
COMPLETE
|
||||
};
|
||||
|
||||
int getMaxStep() const override { return COMPLETE; }
|
||||
|
||||
/**
|
||||
* Get the current step the manager is on (useful when drawing
|
||||
* something depends on the current state)
|
||||
*/
|
||||
BEZIER_STEPS GetStep() const { return static_cast<BEZIER_STEPS>( getStep() ); }
|
||||
|
||||
bool acceptPoint( const VECTOR2I& aPt ) override;
|
||||
|
||||
/*
|
||||
* Geometry query interface - used by clients of the manager
|
||||
*/
|
||||
|
||||
///< Get the center point of the arc (valid when state > SET_ORIGIN)
|
||||
VECTOR2I GetStart() const;
|
||||
|
||||
///< Get the coordinates of the arc start
|
||||
VECTOR2I GetControlC1() const;
|
||||
VECTOR2I GetControlC2() const;
|
||||
|
||||
///< Get the coordinates of the arc end point
|
||||
VECTOR2I GetEnd() const;
|
||||
|
||||
private:
|
||||
/*
|
||||
* Point acceptor functions
|
||||
*/
|
||||
|
||||
///< Set the center point of the arc
|
||||
bool setStart( const VECTOR2I& aOrigin );
|
||||
bool setControlC1( const VECTOR2I& aControl );
|
||||
bool setEnd( const VECTOR2I& aCursor );
|
||||
bool setControlC2( const VECTOR2I& aControl );
|
||||
|
||||
/*
|
||||
* Bezier geometry
|
||||
*/
|
||||
VECTOR2I m_start;
|
||||
VECTOR2I m_controlC1;
|
||||
VECTOR2I m_end;
|
||||
VECTOR2I m_controlC2;
|
||||
};
|
||||
} // namespace PREVIEW
|
||||
} // namespace KIGFX
|
@ -171,6 +171,7 @@ void FOOTPRINT_EDIT_FRAME::doReCreateMenuBar()
|
||||
placeMenu->Add( PCB_ACTIONS::drawRectangle );
|
||||
placeMenu->Add( PCB_ACTIONS::drawCircle );
|
||||
placeMenu->Add( PCB_ACTIONS::drawPolygon );
|
||||
placeMenu->Add( PCB_ACTIONS::drawBezier );
|
||||
placeMenu->Add( PCB_ACTIONS::placeReferenceImage );
|
||||
placeMenu->Add( PCB_ACTIONS::placeText );
|
||||
placeMenu->Add( PCB_ACTIONS::drawTextBox );
|
||||
|
@ -316,6 +316,7 @@ void PCB_EDIT_FRAME::doReCreateMenuBar()
|
||||
placeMenu->Add( PCB_ACTIONS::drawRectangle );
|
||||
placeMenu->Add( PCB_ACTIONS::drawCircle );
|
||||
placeMenu->Add( PCB_ACTIONS::drawPolygon );
|
||||
placeMenu->Add( PCB_ACTIONS::drawBezier );
|
||||
placeMenu->Add( PCB_ACTIONS::placeReferenceImage );
|
||||
placeMenu->Add( PCB_ACTIONS::placeText );
|
||||
placeMenu->Add( PCB_ACTIONS::drawTextBox );
|
||||
|
@ -1044,6 +1044,7 @@ void PCB_EDIT_FRAME::setupUIConditions()
|
||||
CURRENT_EDIT_TOOL( PCB_ACTIONS::drawCircle );
|
||||
CURRENT_EDIT_TOOL( PCB_ACTIONS::drawArc );
|
||||
CURRENT_EDIT_TOOL( PCB_ACTIONS::drawPolygon );
|
||||
CURRENT_EDIT_TOOL( PCB_ACTIONS::drawBezier );
|
||||
CURRENT_EDIT_TOOL( PCB_ACTIONS::placeReferenceImage );
|
||||
CURRENT_EDIT_TOOL( PCB_ACTIONS::placeText );
|
||||
CURRENT_EDIT_TOOL( PCB_ACTIONS::drawTextBox );
|
||||
|
@ -173,6 +173,7 @@ void FOOTPRINT_EDIT_FRAME::ReCreateVToolbar()
|
||||
&PCB_ACTIONS::drawLeader } );
|
||||
}
|
||||
|
||||
// clang-format off
|
||||
m_drawToolBar->Add( ACTIONS::selectionTool, ACTION_TOOLBAR::TOGGLE );
|
||||
|
||||
m_drawToolBar->AddScaledSeparator( this );
|
||||
@ -185,6 +186,7 @@ void FOOTPRINT_EDIT_FRAME::ReCreateVToolbar()
|
||||
m_drawToolBar->Add( PCB_ACTIONS::drawRectangle, ACTION_TOOLBAR::TOGGLE );
|
||||
m_drawToolBar->Add( PCB_ACTIONS::drawCircle, ACTION_TOOLBAR::TOGGLE );
|
||||
m_drawToolBar->Add( PCB_ACTIONS::drawPolygon, ACTION_TOOLBAR::TOGGLE );
|
||||
m_drawToolBar->Add( PCB_ACTIONS::drawBezier, ACTION_TOOLBAR::TOGGLE );
|
||||
m_drawToolBar->Add( PCB_ACTIONS::placeReferenceImage, ACTION_TOOLBAR::TOGGLE );
|
||||
m_drawToolBar->Add( PCB_ACTIONS::placeText, ACTION_TOOLBAR::TOGGLE );
|
||||
m_drawToolBar->Add( PCB_ACTIONS::drawTextBox, ACTION_TOOLBAR::TOGGLE );
|
||||
@ -196,6 +198,7 @@ void FOOTPRINT_EDIT_FRAME::ReCreateVToolbar()
|
||||
m_drawToolBar->Add( PCB_ACTIONS::setAnchor, ACTION_TOOLBAR::TOGGLE );
|
||||
m_drawToolBar->Add( PCB_ACTIONS::gridSetOrigin, ACTION_TOOLBAR::TOGGLE );
|
||||
m_drawToolBar->Add( ACTIONS::measureTool, ACTION_TOOLBAR::TOGGLE );
|
||||
// clang-format on
|
||||
|
||||
PCB_SELECTION_TOOL* selTool = m_toolManager->GetTool<PCB_SELECTION_TOOL>();
|
||||
|
||||
|
@ -356,6 +356,7 @@ void PCB_EDIT_FRAME::ReCreateVToolbar()
|
||||
&PCB_ACTIONS::tuneSkew } );
|
||||
}
|
||||
|
||||
// clang-format off
|
||||
m_drawToolBar->Add( ACTIONS::selectionTool, ACTION_TOOLBAR::TOGGLE );
|
||||
m_drawToolBar->Add( PCB_ACTIONS::localRatsnestTool, ACTION_TOOLBAR::TOGGLE );
|
||||
|
||||
@ -373,6 +374,7 @@ void PCB_EDIT_FRAME::ReCreateVToolbar()
|
||||
m_drawToolBar->Add( PCB_ACTIONS::drawRectangle, ACTION_TOOLBAR::TOGGLE );
|
||||
m_drawToolBar->Add( PCB_ACTIONS::drawCircle, ACTION_TOOLBAR::TOGGLE );
|
||||
m_drawToolBar->Add( PCB_ACTIONS::drawPolygon, ACTION_TOOLBAR::TOGGLE );
|
||||
m_drawToolBar->Add( PCB_ACTIONS::drawBezier, ACTION_TOOLBAR::TOGGLE );
|
||||
m_drawToolBar->Add( PCB_ACTIONS::placeReferenceImage, ACTION_TOOLBAR::TOGGLE );
|
||||
m_drawToolBar->Add( PCB_ACTIONS::placeText, ACTION_TOOLBAR::TOGGLE );
|
||||
m_drawToolBar->Add( PCB_ACTIONS::drawTextBox, ACTION_TOOLBAR::TOGGLE );
|
||||
@ -383,6 +385,7 @@ void PCB_EDIT_FRAME::ReCreateVToolbar()
|
||||
m_drawToolBar->AddScaledSeparator( this );
|
||||
m_drawToolBar->AddGroup( originGroup, ACTION_TOOLBAR::TOGGLE );
|
||||
m_drawToolBar->Add( ACTIONS::measureTool, ACTION_TOOLBAR::TOGGLE );
|
||||
// clang-format on
|
||||
|
||||
PCB_SELECTION_TOOL* selTool = m_toolManager->GetTool<PCB_SELECTION_TOOL>();
|
||||
|
||||
|
@ -37,6 +37,8 @@
|
||||
#include <geometry/geometry_utils.h>
|
||||
#include <geometry/shape_segment.h>
|
||||
#include <import_gfx/dialog_import_graphics.h>
|
||||
#include <preview_items/arc_assistant.h>
|
||||
#include <preview_items/bezier_assistant.h>
|
||||
#include <preview_items/two_point_assistant.h>
|
||||
#include <preview_items/two_point_geom_manager.h>
|
||||
#include <ratsnest/ratsnest_data.h>
|
||||
@ -72,7 +74,6 @@
|
||||
#include <pcb_tablecell.h>
|
||||
#include <pcb_dimension.h>
|
||||
#include <pcbnew_id.h>
|
||||
#include <preview_items/arc_assistant.h>
|
||||
#include <scoped_set_reset.h>
|
||||
#include <string_utils.h>
|
||||
#include <zone.h>
|
||||
@ -543,6 +544,45 @@ int DRAWING_TOOL::DrawArc( const TOOL_EVENT& aEvent )
|
||||
}
|
||||
|
||||
|
||||
int DRAWING_TOOL::DrawBezier( const TOOL_EVENT& aEvent )
|
||||
{
|
||||
if( m_isFootprintEditor && !m_frame->GetModel() )
|
||||
return 0;
|
||||
|
||||
if( m_inDrawingTool )
|
||||
return 0;
|
||||
|
||||
REENTRANCY_GUARD guard( &m_inDrawingTool );
|
||||
|
||||
BOARD_COMMIT commit( m_frame );
|
||||
SCOPED_DRAW_MODE scopedDrawMode( m_mode, MODE::BEZIER );
|
||||
OPT_VECTOR2I startingPoint, startingC1;
|
||||
|
||||
m_frame->PushTool( aEvent );
|
||||
Activate();
|
||||
|
||||
if( aEvent.HasPosition() )
|
||||
startingPoint = aEvent.Position();
|
||||
|
||||
while( std::unique_ptr<PCB_SHAPE> bezier = drawOneBezier( aEvent, startingPoint, startingC1 ) )
|
||||
{
|
||||
PCB_SHAPE& bezierRef = *bezier;
|
||||
commit.Add( bezier.release() );
|
||||
commit.Push( _( "Draw Bezier" ) );
|
||||
|
||||
startingPoint = bezierRef.GetEnd();
|
||||
|
||||
// Mirror the control point across the starting point to get
|
||||
// a tangent control point
|
||||
startingC1 = *startingPoint - ( bezierRef.GetBezierC2() - *startingPoint );
|
||||
|
||||
m_toolMgr->RunAction<EDA_ITEM*>( PCB_ACTIONS::selectItem, &bezierRef );
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int DRAWING_TOOL::PlaceReferenceImage( const TOOL_EVENT& aEvent )
|
||||
{
|
||||
if( m_inDrawingTool )
|
||||
@ -2746,6 +2786,333 @@ bool DRAWING_TOOL::drawArc( const TOOL_EVENT& aTool, PCB_SHAPE** aGraphic,
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Update a bezier PCB_SHAPE from the current state of a Bezier Geometry Manager.
|
||||
*/
|
||||
static void updateBezierFromConstructionMgr( const KIGFX::PREVIEW::BEZIER_GEOM_MANAGER& aMgr,
|
||||
PCB_SHAPE& aBezier )
|
||||
{
|
||||
VECTOR2I vec = aMgr.GetStart();
|
||||
|
||||
aBezier.SetStart( vec );
|
||||
aBezier.SetBezierC1( aMgr.GetControlC1() );
|
||||
aBezier.SetEnd( aMgr.GetEnd() );
|
||||
aBezier.SetBezierC2( aMgr.GetControlC2() );
|
||||
|
||||
// Need this for the length preview to work
|
||||
aBezier.RebuildBezierToSegmentsPointsList( ARC_HIGH_DEF );
|
||||
}
|
||||
|
||||
|
||||
std::unique_ptr<PCB_SHAPE> DRAWING_TOOL::drawOneBezier( const TOOL_EVENT& aTool,
|
||||
const OPT_VECTOR2I& aStartingPoint,
|
||||
const OPT_VECTOR2I& aStartingControl1Point )
|
||||
{
|
||||
std::unique_ptr<PCB_SHAPE> bezier = std::make_unique<PCB_SHAPE>( m_frame->GetModel() );
|
||||
bezier->SetShape( SHAPE_T::BEZIER );
|
||||
bezier->SetFlags( IS_NEW );
|
||||
|
||||
if( m_layer != m_frame->GetActiveLayer() )
|
||||
{
|
||||
m_layer = m_frame->GetActiveLayer();
|
||||
m_stroke.SetWidth( m_frame->GetDesignSettings().GetLineThickness( m_layer ) );
|
||||
m_stroke.SetLineStyle( LINE_STYLE::DEFAULT );
|
||||
m_stroke.SetColor( COLOR4D::UNSPECIFIED );
|
||||
}
|
||||
|
||||
// Turn shapes on if they are off, so that the created object will be visible after completion
|
||||
m_frame->SetObjectVisible( LAYER_SHAPES );
|
||||
|
||||
// Arc geometric construction manager
|
||||
KIGFX::PREVIEW::BEZIER_GEOM_MANAGER bezierManager;
|
||||
|
||||
// Arc drawing assistant overlay
|
||||
KIGFX::PREVIEW::BEZIER_ASSISTANT bezierAsst( bezierManager, pcbIUScale,
|
||||
m_frame->GetUserUnits() );
|
||||
|
||||
// Add a VIEW_GROUP that serves as a preview for the new item
|
||||
PCB_SELECTION preview;
|
||||
m_view->Add( &preview );
|
||||
m_view->Add( &bezierAsst );
|
||||
PCB_GRID_HELPER grid( m_toolMgr, m_frame->GetMagneticItemsSettings() );
|
||||
|
||||
auto setCursor = [&]()
|
||||
{
|
||||
m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::PENCIL );
|
||||
};
|
||||
|
||||
auto cleanup = [&]()
|
||||
{
|
||||
preview.Clear();
|
||||
bezier.reset();
|
||||
};
|
||||
|
||||
m_controls->ShowCursor( true );
|
||||
m_controls->ForceCursorPosition( false );
|
||||
// Set initial cursor
|
||||
setCursor();
|
||||
|
||||
// We need to know when the bezier manager has started actually adding points
|
||||
// but which point it started with depends on whether we were passed a starting point
|
||||
KIGFX::PREVIEW::BEZIER_GEOM_MANAGER::BEZIER_STEPS startedAfterStep =
|
||||
KIGFX::PREVIEW::BEZIER_GEOM_MANAGER::SET_START;
|
||||
const auto started = [&]()
|
||||
{
|
||||
return bezierManager.GetStep() > startedAfterStep;
|
||||
};
|
||||
bool cancelled = false;
|
||||
bool priming = false;
|
||||
|
||||
m_toolMgr->PostAction( ACTIONS::refreshPreview );
|
||||
|
||||
// Load in one or two points if they were passed in
|
||||
if( aStartingPoint )
|
||||
{
|
||||
priming = true;
|
||||
if( aStartingControl1Point )
|
||||
{
|
||||
bezierManager.AddPoint( *aStartingPoint, true );
|
||||
bezierManager.AddPoint( *aStartingControl1Point, true );
|
||||
m_toolMgr->PrimeTool( *aStartingControl1Point );
|
||||
startedAfterStep = KIGFX::PREVIEW::BEZIER_GEOM_MANAGER::SET_END;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_toolMgr->PrimeTool( *aStartingPoint );
|
||||
startedAfterStep = KIGFX::PREVIEW::BEZIER_GEOM_MANAGER::SET_CONTROL1;
|
||||
}
|
||||
}
|
||||
|
||||
// Main loop: keep receiving events
|
||||
while( TOOL_EVENT* evt = Wait() )
|
||||
{
|
||||
if( started() )
|
||||
m_frame->SetMsgPanel( bezier.get() );
|
||||
|
||||
setCursor();
|
||||
|
||||
bezier->SetLayer( m_layer );
|
||||
|
||||
grid.SetSnap( !evt->Modifier( MD_SHIFT ) );
|
||||
grid.SetUseGrid( getView()->GetGAL()->GetGridSnapping() && !evt->DisableGridSnapping() );
|
||||
VECTOR2I cursorPos = GetClampedCoords(
|
||||
grid.BestSnapAnchor( m_controls->GetMousePosition(), bezier.get(), GRID_GRAPHICS ),
|
||||
COORDS_PADDING );
|
||||
m_controls->ForceCursorPosition( true, cursorPos );
|
||||
|
||||
if( evt->IsCancelInteractive() || ( started() && evt->IsAction( &ACTIONS::undo ) ) )
|
||||
{
|
||||
cleanup();
|
||||
|
||||
if( !started() )
|
||||
{
|
||||
// We've handled the cancel event. Don't cancel other tools
|
||||
evt->SetPassEvent( false );
|
||||
m_frame->PopTool( aTool );
|
||||
cancelled = true;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
else if( evt->IsActivate() )
|
||||
{
|
||||
if( evt->IsPointEditor() )
|
||||
{
|
||||
// don't exit (the point editor runs in the background)
|
||||
}
|
||||
else if( evt->IsMoveTool() )
|
||||
{
|
||||
cleanup();
|
||||
// leave ourselves on the stack so we come back after the move
|
||||
cancelled = true;
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
cleanup();
|
||||
m_frame->PopTool( aTool );
|
||||
cancelled = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if( evt->IsClick( BUT_LEFT ) )
|
||||
{
|
||||
if( !started() )
|
||||
{
|
||||
m_toolMgr->RunAction( PCB_ACTIONS::selectionClear );
|
||||
|
||||
m_controls->SetAutoPan( true );
|
||||
m_controls->CaptureCursor( true );
|
||||
|
||||
// Init the new item attributes
|
||||
// (non-geometric, those are handled by the manager)
|
||||
bezier->SetShape( SHAPE_T::BEZIER );
|
||||
bezier->SetStroke( m_stroke );
|
||||
|
||||
if( !m_view->IsLayerVisible( m_layer ) )
|
||||
{
|
||||
m_frame->GetAppearancePanel()->SetLayerVisible( m_layer, true );
|
||||
m_frame->GetCanvas()->Refresh();
|
||||
}
|
||||
|
||||
frame()->SetMsgPanel( bezier.get() );
|
||||
}
|
||||
|
||||
if( !priming )
|
||||
bezierManager.AddPoint( cursorPos, true );
|
||||
else
|
||||
priming = false;
|
||||
|
||||
if( bezierManager.GetStep() == KIGFX::PREVIEW::BEZIER_GEOM_MANAGER::SET_END )
|
||||
{
|
||||
preview.Add( bezier.get() );
|
||||
}
|
||||
}
|
||||
else if( evt->IsAction( &PCB_ACTIONS::deleteLastPoint ) )
|
||||
{
|
||||
bezierManager.RemoveLastPoint();
|
||||
|
||||
if( bezierManager.GetStep() < KIGFX::PREVIEW::BEZIER_GEOM_MANAGER::SET_END )
|
||||
{
|
||||
preview.Remove( bezier.get() );
|
||||
}
|
||||
}
|
||||
else if( evt->IsMotion() )
|
||||
{
|
||||
// set angle snap
|
||||
// bezierManager.SetAngleSnap( Is45Limited() );
|
||||
|
||||
// update, but don't step the manager state
|
||||
bezierManager.AddPoint( cursorPos, false );
|
||||
}
|
||||
else if( evt->IsAction( &PCB_ACTIONS::layerChanged ) )
|
||||
{
|
||||
if( m_layer != m_frame->GetActiveLayer() )
|
||||
{
|
||||
m_layer = m_frame->GetActiveLayer();
|
||||
m_stroke.SetWidth( m_frame->GetDesignSettings().GetLineThickness( m_layer ) );
|
||||
m_stroke.SetLineStyle( LINE_STYLE::DEFAULT );
|
||||
m_stroke.SetColor( COLOR4D::UNSPECIFIED );
|
||||
}
|
||||
|
||||
if( bezier )
|
||||
{
|
||||
if( !m_view->IsLayerVisible( m_layer ) )
|
||||
{
|
||||
m_frame->GetAppearancePanel()->SetLayerVisible( m_layer, true );
|
||||
m_frame->GetCanvas()->Refresh();
|
||||
}
|
||||
|
||||
bezier->SetLayer( m_layer );
|
||||
bezier->SetStroke( m_stroke );
|
||||
m_view->Update( &preview );
|
||||
frame()->SetMsgPanel( bezier.get() );
|
||||
}
|
||||
else
|
||||
{
|
||||
evt->SetPassEvent();
|
||||
}
|
||||
}
|
||||
else if( evt->IsAction( &PCB_ACTIONS::properties ) )
|
||||
{
|
||||
// Don't show the edit panel if we can't represent the arc with it
|
||||
if( ( bezierManager.GetStep() >= KIGFX::PREVIEW::BEZIER_GEOM_MANAGER::SET_END ) )
|
||||
{
|
||||
frame()->OnEditItemRequest( bezier.get() );
|
||||
m_view->Update( &preview );
|
||||
frame()->SetMsgPanel( bezier.get() );
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
evt->SetPassEvent();
|
||||
}
|
||||
}
|
||||
else if( evt->IsClick( BUT_RIGHT ) )
|
||||
{
|
||||
if( !bezier )
|
||||
m_toolMgr->VetoContextMenuMouseWarp();
|
||||
|
||||
m_menu->ShowContextMenu( selection() );
|
||||
}
|
||||
else if( evt->IsAction( &PCB_ACTIONS::incWidth ) )
|
||||
{
|
||||
m_stroke.SetWidth( m_stroke.GetWidth() + WIDTH_STEP );
|
||||
|
||||
if( bezier )
|
||||
{
|
||||
bezier->SetStroke( m_stroke );
|
||||
m_view->Update( &preview );
|
||||
frame()->SetMsgPanel( bezier.get() );
|
||||
}
|
||||
}
|
||||
else if( evt->IsAction( &PCB_ACTIONS::decWidth ) )
|
||||
{
|
||||
if( (unsigned) m_stroke.GetWidth() > WIDTH_STEP )
|
||||
{
|
||||
m_stroke.SetWidth( m_stroke.GetWidth() - WIDTH_STEP );
|
||||
|
||||
if( bezier )
|
||||
{
|
||||
bezier->SetStroke( m_stroke );
|
||||
m_view->Update( &preview );
|
||||
frame()->SetMsgPanel( bezier.get() );
|
||||
}
|
||||
}
|
||||
}
|
||||
else if( evt->IsAction( &ACTIONS::updateUnits ) )
|
||||
{
|
||||
bezierAsst.SetUnits( frame()->GetUserUnits() );
|
||||
m_view->Update( &bezierAsst );
|
||||
evt->SetPassEvent();
|
||||
}
|
||||
else if( started()
|
||||
&& ( ZONE_FILLER_TOOL::IsZoneFillAction( evt )
|
||||
|| evt->IsAction( &ACTIONS::redo ) ) )
|
||||
{
|
||||
wxBell();
|
||||
}
|
||||
else
|
||||
{
|
||||
evt->SetPassEvent();
|
||||
}
|
||||
|
||||
if( bezierManager.IsComplete() )
|
||||
{
|
||||
break;
|
||||
}
|
||||
else if( bezierManager.HasGeometryChanged() )
|
||||
{
|
||||
updateBezierFromConstructionMgr( bezierManager, *bezier );
|
||||
m_view->Update( &preview );
|
||||
m_view->Update( &bezierAsst );
|
||||
|
||||
// Once we are receiving end points, we can show the bezier in the preview
|
||||
if( bezierManager.GetStep() >= KIGFX::PREVIEW::BEZIER_GEOM_MANAGER::SET_END )
|
||||
frame()->SetMsgPanel( bezier.get() );
|
||||
else
|
||||
frame()->SetMsgPanel( board() );
|
||||
}
|
||||
}
|
||||
|
||||
preview.Remove( bezier.get() );
|
||||
m_view->Remove( &bezierAsst );
|
||||
m_view->Remove( &preview );
|
||||
|
||||
if( selection().Empty() )
|
||||
m_frame->SetMsgPanel( board() );
|
||||
|
||||
m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW );
|
||||
m_controls->SetAutoPan( false );
|
||||
m_controls->CaptureCursor( false );
|
||||
m_controls->ForceCursorPosition( false );
|
||||
|
||||
if( cancelled )
|
||||
return nullptr;
|
||||
|
||||
return bezier;
|
||||
};
|
||||
|
||||
bool DRAWING_TOOL::getSourceZoneForAction( ZONE_MODE aMode, ZONE** aZone )
|
||||
{
|
||||
bool clearSelection = false;
|
||||
@ -3681,7 +4048,7 @@ const unsigned int DRAWING_TOOL::WIDTH_STEP = pcbIUScale.mmToIU( 0.1 );
|
||||
|
||||
void DRAWING_TOOL::setTransitions()
|
||||
{
|
||||
|
||||
// clang-format off
|
||||
Go( &DRAWING_TOOL::PlaceStackup, PCB_ACTIONS::placeStackup.MakeEvent() );
|
||||
Go( &DRAWING_TOOL::PlaceCharacteristics, PCB_ACTIONS::placeCharacteristics.MakeEvent() );
|
||||
Go( &DRAWING_TOOL::DrawLine, PCB_ACTIONS::drawLine.MakeEvent() );
|
||||
@ -3689,6 +4056,7 @@ void DRAWING_TOOL::setTransitions()
|
||||
Go( &DRAWING_TOOL::DrawRectangle, PCB_ACTIONS::drawRectangle.MakeEvent() );
|
||||
Go( &DRAWING_TOOL::DrawCircle, PCB_ACTIONS::drawCircle.MakeEvent() );
|
||||
Go( &DRAWING_TOOL::DrawArc, PCB_ACTIONS::drawArc.MakeEvent() );
|
||||
Go( &DRAWING_TOOL::DrawBezier, PCB_ACTIONS::drawBezier.MakeEvent() );
|
||||
Go( &DRAWING_TOOL::DrawDimension, PCB_ACTIONS::drawAlignedDimension.MakeEvent() );
|
||||
Go( &DRAWING_TOOL::DrawDimension, PCB_ACTIONS::drawOrthogonalDimension.MakeEvent() );
|
||||
Go( &DRAWING_TOOL::DrawDimension, PCB_ACTIONS::drawCenterDimension.MakeEvent() );
|
||||
@ -3711,4 +4079,5 @@ void DRAWING_TOOL::setTransitions()
|
||||
Go( &DRAWING_TOOL::PlaceTuningPattern, PCB_ACTIONS::tuneSingleTrack.MakeEvent() );
|
||||
Go( &DRAWING_TOOL::PlaceTuningPattern, PCB_ACTIONS::tuneDiffPair.MakeEvent() );
|
||||
Go( &DRAWING_TOOL::PlaceTuningPattern, PCB_ACTIONS::tuneSkew.MakeEvent() );
|
||||
// clang-format on
|
||||
}
|
||||
|
@ -71,6 +71,7 @@ public:
|
||||
RECTANGLE,
|
||||
CIRCLE,
|
||||
ARC,
|
||||
BEZIER,
|
||||
IMAGE,
|
||||
TEXT,
|
||||
ANCHOR,
|
||||
@ -145,6 +146,13 @@ public:
|
||||
*/
|
||||
int DrawArc( const TOOL_EVENT& aEvent );
|
||||
|
||||
/**
|
||||
* Start interactively drawing a bezier curve.
|
||||
*
|
||||
* An interactive geometry manager will handle adding/editing the control points.
|
||||
*/
|
||||
int DrawBezier( const TOOL_EVENT& aEvent );
|
||||
|
||||
/**
|
||||
* Display a dialog that allows one to select a reference image and then decide where to
|
||||
* place the image in the editor.
|
||||
@ -258,6 +266,22 @@ private:
|
||||
bool drawArc( const TOOL_EVENT& aTool, PCB_SHAPE** aGraphic,
|
||||
std::optional<VECTOR2D> aStartingPoint );
|
||||
|
||||
/**
|
||||
* Draw a bezier curve.
|
||||
*
|
||||
* @param aTool is the event that triggered the drawing.
|
||||
* @param aStartingPoint is the starting point of the curve (e.g. the end point of the
|
||||
* previous curve).
|
||||
* @param aStartingControl1Point is the previous control point of the curve (which can
|
||||
* be used to create a smooth transition between two curves).
|
||||
*
|
||||
* @return A new PCB_SHAPE object representing the bezier curve, or nullptr if
|
||||
* the tool was canceled.
|
||||
*/
|
||||
std::unique_ptr<PCB_SHAPE> drawOneBezier( const TOOL_EVENT& aTool,
|
||||
const OPT_VECTOR2I& aStartingPoint,
|
||||
const OPT_VECTOR2I& aStartingControl1Point );
|
||||
|
||||
/**
|
||||
* Draw a polygon, that is added as a zone or a keepout area.
|
||||
*
|
||||
|
@ -142,6 +142,14 @@ TOOL_ACTION PCB_ACTIONS::drawArc( TOOL_ACTION_ARGS()
|
||||
.Icon( BITMAPS::add_arc )
|
||||
.Flags( AF_ACTIVATE ) );
|
||||
|
||||
TOOL_ACTION PCB_ACTIONS::drawBezier( TOOL_ACTION_ARGS()
|
||||
.Name( "pcbnew.InteractiveDrawing.bezier" )
|
||||
.Scope( AS_GLOBAL )
|
||||
.DefaultHotkey( MD_SHIFT + MD_CTRL + 'B' )
|
||||
.FriendlyName( _( "Draw Bezier Curve" ) )
|
||||
.Icon( BITMAPS::add_bezier )
|
||||
.Flags( AF_ACTIVATE ) );
|
||||
|
||||
TOOL_ACTION PCB_ACTIONS::placeCharacteristics( TOOL_ACTION_ARGS()
|
||||
.Name( "pcbnew.InteractiveDrawing.placeCharacteristics" )
|
||||
.Scope( AS_GLOBAL )
|
||||
|
@ -203,6 +203,7 @@ public:
|
||||
static TOOL_ACTION drawRectangle;
|
||||
static TOOL_ACTION drawCircle;
|
||||
static TOOL_ACTION drawArc;
|
||||
static TOOL_ACTION drawBezier;
|
||||
static TOOL_ACTION placeReferenceImage;
|
||||
static TOOL_ACTION placeText;
|
||||
static TOOL_ACTION drawTextBox;
|
||||
|
Loading…
Reference in New Issue
Block a user