Add a 90°-only mode for polygon creation

Sometimes, you don't want those 45° corners in your nice, clean zones.
This adds an additional mode that can be selected, cycling through
free-angle, 45° angle and 90° angle constraints
This commit is contained in:
Seth Hillbrand 2025-08-12 16:48:36 -07:00
parent 5375e27415
commit 188ffda029
26 changed files with 231 additions and 94 deletions

View File

@ -228,6 +228,20 @@ static SHAPE_LINE_CHAIN build45DegLeader( const VECTOR2I& aEndPoint, const SHAPE
return SHAPE_LINE_CHAIN( std::vector<VECTOR2I>{ lastPt, midInt, aEndPoint } );
}
static SHAPE_LINE_CHAIN build90DegLeader( const VECTOR2I& aEndPoint, const SHAPE_LINE_CHAIN& aLastPoints )
{
if( aLastPoints.PointCount() < 1 )
return SHAPE_LINE_CHAIN();
const VECTOR2I lastPt = aLastPoints.CLastPoint();
if( lastPt.x == aEndPoint.x || lastPt.y == aEndPoint.y )
return SHAPE_LINE_CHAIN( std::vector<VECTOR2I>{ lastPt, aEndPoint } );
VECTOR2I mid( aEndPoint.x, lastPt.y );
return SHAPE_LINE_CHAIN( std::vector<VECTOR2I>{ lastPt, mid, aEndPoint } );
}
void POLYGON_GEOM_MANAGER::updateTemporaryLines( const VECTOR2I& aEndPoint, LEADER_MODE aModifier )
{
@ -242,6 +256,14 @@ void POLYGON_GEOM_MANAGER::updateTemporaryLines( const VECTOR2I& aEndPoint, LEAD
m_loopPts = build45DegLeader( aEndPoint, m_lockedPoints.Reverse() ).Reverse();
}
}
else if( m_leaderMode == LEADER_MODE::DEG90 || aModifier == LEADER_MODE::DEG90 )
{
if( m_lockedPoints.PointCount() > 0 )
{
m_leaderPts = build90DegLeader( aEndPoint, m_lockedPoints );
m_loopPts = build90DegLeader( aEndPoint, m_lockedPoints.Reverse() ).Reverse();
}
}
else
{
// direct segment

View File

@ -24,6 +24,7 @@
#include <settings/cvpcb_settings.h>
#include <settings/parameters.h>
#include <wx/config.h>
#include <geometry/geometry_utils.h>
///! Update the schema version whenever a migration is required
@ -54,8 +55,9 @@ CVPCB_SETTINGS::CVPCB_SETTINGS() :
m_params.emplace_back( new PARAM<bool>( "footprint_viewer.autozoom",
&m_FootprintViewerAutoZoomOnSelect, true ) );
m_params.emplace_back( new PARAM<bool>( "footprint_viewer.use_45_limit",
&m_ViewersDisplay.m_Use45Limit, true ) );
m_params.emplace_back( new PARAM<int>( "footprint_viewer.angle_snap_mode",
reinterpret_cast<int*>( &m_ViewersDisplay.m_AngleSnapMode ),
static_cast<int>( LEADER_MODE::DEG45 ) ) );
m_params.emplace_back( new PARAM<bool>( "footprint_viewer.show_pad_fill",
&m_ViewersDisplay.m_DisplayPadFill, true ) );

View File

@ -93,7 +93,7 @@ bool RULE_AREA_CREATE_HELPER::OnFirstPoint( POLYGON_GEOM_MANAGER& aMgr )
m_parentView.SetVisible( &m_previewItem, true );
aMgr.SetLeaderMode( POLYGON_GEOM_MANAGER::LEADER_MODE::DEG45 );
aMgr.SetLeaderMode( LEADER_MODE::DEG45 );
}
return m_rule_area != nullptr;
@ -138,7 +138,7 @@ void RULE_AREA_CREATE_HELPER::OnComplete( const POLYGON_GEOM_MANAGER& aMgr )
// In DEG45 mode, we may have intermediate points in the leader that should be included
// as they are shown in the preview. These typically maintain the 45 constraint
if( aMgr.GetLeaderMode() == POLYGON_GEOM_MANAGER::LEADER_MODE::DEG45 )
if( aMgr.GetLeaderMode() == LEADER_MODE::DEG45 || aMgr.GetLeaderMode() == LEADER_MODE::DEG90 )
{
const SHAPE_LINE_CHAIN leaderPts = aMgr.GetLeaderLinePoints();
for( int i = 1; i < leaderPts.PointCount(); i++ )

View File

@ -2617,8 +2617,8 @@ int SCH_DRAWING_TOOLS::DrawRuleArea( const TOOL_EVENT& aEvent )
controls->ForceCursorPosition( true, cursorPos );
polyGeomMgr.SetLeaderMode( m_frame->eeconfig()->m_Drawing.line_mode == LINE_MODE_FREE
? POLYGON_GEOM_MANAGER::LEADER_MODE::DIRECT
: POLYGON_GEOM_MANAGER::LEADER_MODE::DEG45 );
? LEADER_MODE::DIRECT
: LEADER_MODE::DEG45 );
if( evt->IsCancelInteractive() )
{

View File

@ -286,7 +286,8 @@ int GERBVIEW_INSPECTION_TOOL::MeasureTool( const TOOL_EVENT& aEvent )
else if( originSet && ( evt->IsMotion() || evt->IsDrag( BUT_LEFT ) ) )
{
// move or drag when origin set updates rules
twoPtMgr.SetAngleSnap( evt->Modifier( MD_SHIFT ) );
twoPtMgr.SetAngleSnap( evt->Modifier( MD_SHIFT ) ? LEADER_MODE::DEG45
: LEADER_MODE::DIRECT );
twoPtMgr.SetEnd( cursorPos );
getView()->SetVisible( &ruler, true );

View File

@ -74,7 +74,7 @@ public:
EDA_ANGLE m_RotationAngle;
bool m_Use45Limit;
LEADER_MODE m_AngleSnapMode;
ARC_EDIT_MODE m_ArcEditMode;

View File

@ -25,6 +25,7 @@
#define PREVIEW_POLYGON_GEOM_MANAGER__H_
#include <geometry/shape_line_chain.h>
#include <geometry/geometry_utils.h>
/**
* Class that handles the drawing of a polygon, including management of last corner deletion
@ -63,15 +64,6 @@ public:
}
};
/**
* The kind of the leader line
*/
enum class LEADER_MODE
{
DIRECT, ///< Unconstrained point-to-point
DEG45, ///< 45 Degree only
};
/**
* @param aClient is the client to pass the results onto
*/

View File

@ -40,7 +40,6 @@ namespace PREVIEW
class TWO_POINT_GEOMETRY_MANAGER
{
public:
///< Set the origin of the ruler (the fixed end)
void SetOrigin( const VECTOR2I& aOrigin )
{
@ -58,10 +57,12 @@ public:
*/
void SetEnd( const VECTOR2I& aEnd )
{
if( m_angleSnap )
m_end = GetVectorSnapped45( aEnd - m_origin ) + m_origin;
else
m_end = aEnd;
switch( m_angleSnap )
{
case LEADER_MODE::DEG45: m_end = GetVectorSnapped45( aEnd - m_origin ) + m_origin; break;
case LEADER_MODE::DEG90: m_end = GetVectorSnapped90( aEnd - m_origin ) + m_origin; break;
default: m_end = aEnd; break;
}
}
VECTOR2I GetEnd() const
@ -69,15 +70,9 @@ public:
return m_end;
}
void SetAngleSnap( bool aSnap )
{
m_angleSnap = aSnap;
}
void SetAngleSnap( LEADER_MODE aSnap ) { m_angleSnap = aSnap; }
bool GetAngleSnap() const
{
return m_angleSnap;
}
LEADER_MODE GetAngleSnap() const { return m_angleSnap; }
/**
* @return true if the manager is in the initial state
@ -101,11 +96,10 @@ public:
}
private:
VECTOR2I m_origin;
VECTOR2I m_end;
bool m_angleSnap = false;
bool m_originSet = false;
VECTOR2I m_origin;
VECTOR2I m_end;
LEADER_MODE m_angleSnap = LEADER_MODE::DIRECT;
bool m_originSet = false;
};
} // PREVIEW

View File

@ -39,6 +39,16 @@
#include <geometry/shape_simple.h>
#include <geometry/shape_compound.h>
/**
* The kind of the leader line
*/
enum class LEADER_MODE
{
DIRECT, ///< Unconstrained point-to-point
DEG45, ///< 45 Degree only
DEG90 ///< 90 Degree only
};
/**
* @return the number of segments to approximate a arc by segments
* with a given max error (this number is >= 1)
@ -131,6 +141,30 @@ VECTOR2<T> GetVectorSnapped45( const VECTOR2<T>& aVec, bool only45 = false )
}
/**
* Snap a vector onto the nearest horizontal or vertical line.
*
* The magnitude of the vector is NOT kept; instead one of the coordinates is
* set to zero as needed. If the starting vector is on a square grid, the
* resulting snapped vector will remain on the same grid.
*
* @param aVec vector to be snapped
* @return the snapped vector
*/
template <typename T>
VECTOR2<T> GetVectorSnapped90( const VECTOR2<T>& aVec )
{
auto newVec = aVec;
const VECTOR2<T> absVec{ std::abs( aVec.x ), std::abs( aVec.y ) };
if( absVec.x >= absVec.y )
newVec.y = 0;
else
newVec.x = 0;
return newVec;
}
/**
* Clamps a vector to values that can be negated, respecting numeric limits
* of coordinates data type with specified padding.

View File

@ -28,6 +28,7 @@
#include <pcbnew_settings.h>
#include <footprint_editor_settings.h>
#include <panel_edit_options.h>
#include <geometry/geometry_utils.h>
PANEL_EDIT_OPTIONS::PANEL_EDIT_OPTIONS( wxWindow* aParent, UNITS_PROVIDER* aUnitsProvider,
@ -104,7 +105,7 @@ static ARC_EDIT_MODE arcEditModeToEnum( int aIndex )
void PANEL_EDIT_OPTIONS::loadPCBSettings( PCBNEW_SETTINGS* aCfg )
{
m_cbConstrainHV45Mode->SetValue( aCfg->m_Use45DegreeLimit );
m_cbConstrainHV45Mode->SetValue( aCfg->m_AngleSnapMode != LEADER_MODE::DIRECT );
m_rotationAngle.SetAngleValue( aCfg->m_RotationAngle );
m_arcEditMode->SetSelection( arcEditModeToComboIndex( aCfg->m_ArcEditMode ) );
m_trackMouseDragCtrl->SetSelection( (int) aCfg->m_TrackDragAction );
@ -150,7 +151,7 @@ void PANEL_EDIT_OPTIONS::loadFPSettings( FOOTPRINT_EDITOR_SETTINGS* aCfg )
m_rotationAngle.SetAngleValue( aCfg->m_RotationAngle );
m_magneticPads->SetValue( aCfg->m_MagneticItems.pads == MAGNETIC_OPTIONS::CAPTURE_ALWAYS );
m_magneticGraphics->SetValue( aCfg->m_MagneticItems.graphics );
m_cbConstrainHV45Mode->SetValue( aCfg->m_Use45Limit );
m_cbConstrainHV45Mode->SetValue( aCfg->m_AngleSnapMode != LEADER_MODE::DIRECT );
m_arcEditMode->SetSelection( arcEditModeToComboIndex( aCfg->m_ArcEditMode ) );
}
@ -178,7 +179,8 @@ bool PANEL_EDIT_OPTIONS::TransferDataFromWindow()
: MAGNETIC_OPTIONS::NO_EFFECT;
cfg->m_MagneticItems.graphics = m_magneticGraphics->GetValue();
cfg->m_Use45Limit = m_cbConstrainHV45Mode->GetValue();
cfg->m_AngleSnapMode = m_cbConstrainHV45Mode->GetValue() ? LEADER_MODE::DEG45
: LEADER_MODE::DIRECT;
cfg->m_ArcEditMode = arcEditModeToEnum( m_arcEditMode->GetSelection() );
}
}
@ -190,7 +192,8 @@ bool PANEL_EDIT_OPTIONS::TransferDataFromWindow()
cfg->m_Display.m_ShowModuleRatsnest = m_showSelectedRatsnest->GetValue();
cfg->m_Display.m_RatsnestThickness = m_ratsnestThickness->GetValue();
cfg->m_Use45DegreeLimit = m_cbConstrainHV45Mode->GetValue();
cfg->m_AngleSnapMode = m_cbConstrainHV45Mode->GetValue() ? LEADER_MODE::DEG45
: LEADER_MODE::DIRECT;
cfg->m_RotationAngle = m_rotationAngle.GetAngleValue();
cfg->m_ArcEditMode = arcEditModeToEnum( m_arcEditMode->GetSelection() );
cfg->m_TrackDragAction = (TRACK_DRAG_ACTION) m_trackMouseDragCtrl->GetSelection();

View File

@ -30,6 +30,7 @@
#include "tools/pcb_actions.h"
#include "tools/pcb_control.h"
#include "tools/pcb_picker_tool.h"
#include <geometry/geometry_utils.h>
#include "tools/align_distribute_tool.h"
#include "tools/pcb_point_editor.h"
#include "tools/pcb_selection_tool.h"
@ -1337,7 +1338,7 @@ void FOOTPRINT_EDIT_FRAME::setupUIConditions()
auto constrainedDrawingModeCond =
[this]( const SELECTION& )
{
return GetSettings()->m_Use45Limit;
return GetSettings()->m_AngleSnapMode != LEADER_MODE::DIRECT;
};
auto highContrastCond =

View File

@ -50,7 +50,7 @@ FOOTPRINT_EDITOR_SETTINGS::FOOTPRINT_EDITOR_SETTINGS() :
m_DisplayInvertXAxis( false ),
m_DisplayInvertYAxis( false ),
m_RotationAngle( ANGLE_90 ),
m_Use45Limit( true ),
m_AngleSnapMode( LEADER_MODE::DEG45 ),
m_ArcEditMode( ARC_EDIT_MODE::KEEP_CENTER_ADJUST_ANGLE_RADIUS ),
m_LibWidth( 250 ),
m_LastExportPath(),
@ -138,8 +138,9 @@ FOOTPRINT_EDITOR_SETTINGS::FOOTPRINT_EDITOR_SETTINGS() :
},
900 ) );
m_params.emplace_back( new PARAM<bool>( "editing.fp_use_45_degree_limit",
&m_Use45Limit, false ) );
m_params.emplace_back( new PARAM<int>( "editing.fp_angle_snap_mode",
reinterpret_cast<int*>( &m_AngleSnapMode ),
static_cast<int>( LEADER_MODE::DEG45 ) ) );
m_params.emplace_back( new PARAM_LAYER_PRESET( "pcb_display.layer_presets", &m_LayerPresets ) );

View File

@ -209,7 +209,7 @@ int MICROWAVE_TOOL::drawMicrowaveInductor( const TOOL_EVENT& aEvent )
// the end point
else if( originSet && ( evt->IsMotion() || evt->IsDrag( BUT_LEFT ) ) )
{
tpGeomMgr.SetAngleSnap( Is45Limited() );
tpGeomMgr.SetAngleSnap( GetAngleSnapMode() );
tpGeomMgr.SetEnd( cursorPos );
view.SetVisible( &previewRect, true );

View File

@ -30,6 +30,7 @@
#include <3d_viewer/eda_3d_viewer_frame.h>
#include <api/api_plugin_manager.h>
#include <fp_lib_table.h>
#include <geometry/geometry_utils.h>
#include <bitmaps.h>
#include <confirm.h>
#include <lset.h>
@ -888,7 +889,7 @@ void PCB_EDIT_FRAME::setupUIConditions()
auto constrainedDrawingModeCond =
[this]( const SELECTION& )
{
return GetPcbNewSettings()->m_Use45DegreeLimit;
return GetPcbNewSettings()->m_AngleSnapMode != LEADER_MODE::DIRECT;
};
auto boardFlippedCond =

View File

@ -58,7 +58,7 @@ PCBNEW_SETTINGS::PCBNEW_SETTINGS()
m_TrackDragAction( TRACK_DRAG_ACTION::DRAG ),
m_ArcEditMode( ARC_EDIT_MODE::KEEP_CENTER_ADJUST_ANGLE_RADIUS ),
m_CtrlClickHighlight( false ),
m_Use45DegreeLimit( false ),
m_AngleSnapMode( LEADER_MODE::DIRECT ),
m_FlipDirection( FLIP_DIRECTION::TOP_BOTTOM ),
m_ESCClearsNetHighlight( true ),
m_PolarCoords( false ),
@ -190,8 +190,9 @@ PCBNEW_SETTINGS::PCBNEW_SETTINGS()
m_params.emplace_back( new PARAM<bool>( "editing.ctrl_click_highlight",
&m_CtrlClickHighlight, false ) );
m_params.emplace_back( new PARAM<bool>( "editing.pcb_use_45_degree_limit",
&m_Use45DegreeLimit, false ) );
m_params.emplace_back( new PARAM<int>( "editing.pcb_angle_snap_mode",
reinterpret_cast<int*>( &m_AngleSnapMode ),
static_cast<int>( LEADER_MODE::DIRECT ) ) );
m_params.emplace_back( new PARAM<bool>( "editing.auto_fill_zones",
&m_AutoRefillZones, false ) );

View File

@ -22,6 +22,7 @@
#include <core/mirror.h> // for FLIP_DIRECTION
#include <geometry/eda_angle.h>
#include <geometry/geometry_utils.h>
#include <settings/app_settings.h>
#include <pcb_display_options.h>
@ -108,7 +109,7 @@ class PCB_VIEWERS_SETTINGS_BASE : public APP_SETTINGS_BASE
public:
struct VIEWERS_DISPLAY_OPTIONS
{
bool m_Use45Limit;
LEADER_MODE m_AngleSnapMode;
bool m_DisplayGraphicsFill;
bool m_DisplayTextFill;
bool m_DisplayPadNumbers;
@ -125,7 +126,7 @@ public:
m_FootprintViewerZoom( 1.0 ),
m_FootprintViewerAutoZoomOnSelect( true )
{
m_ViewersDisplay.m_Use45Limit = false;
m_ViewersDisplay.m_AngleSnapMode = LEADER_MODE::DIRECT;
m_ViewersDisplay.m_DisplayGraphicsFill = true;
m_ViewersDisplay.m_DisplayTextFill = true;
m_ViewersDisplay.m_DisplayPadNumbers = true;
@ -246,7 +247,7 @@ public:
bool m_CtrlClickHighlight;
bool m_Use45DegreeLimit; // Constrain tool actions to horizontal, vertical and 45deg
LEADER_MODE m_AngleSnapMode; // Constrain tool actions to horizontal/vertical or 45°/90°
FLIP_DIRECTION m_FlipDirection;
bool m_ESCClearsNetHighlight;

View File

@ -333,7 +333,18 @@ DRAWING_TOOL::MODE DRAWING_TOOL::GetDrawingMode() const
void DRAWING_TOOL::UpdateStatusBar() const
{
if( m_frame )
m_frame->DisplayConstraintsMsg( Is45Limited() ? _( "Constrain to H, V, 45" ) : wxString( "" ) );
{
switch( GetAngleSnapMode() )
{
case LEADER_MODE::DEG45:
m_frame->DisplayConstraintsMsg( _( "Constrain to H, V, 45" ) );
break;
case LEADER_MODE::DEG90:
m_frame->DisplayConstraintsMsg( _( "Constrain to H, V" ) );
break;
default: m_frame->DisplayConstraintsMsg( wxString( "" ) ); break;
}
}
}
@ -1407,7 +1418,10 @@ int DRAWING_TOOL::DrawDimension( const TOOL_EVENT& aEvent )
setCursor();
grid.SetSnap( !evt->Modifier( MD_SHIFT ) );
bool is45Limited = Is45Limited() && !evt->Modifier( MD_CTRL );
auto angleSnap = GetAngleSnapMode();
if( evt->Modifier( MD_CTRL ) )
angleSnap = LEADER_MODE::DIRECT;
bool constrained = angleSnap != LEADER_MODE::DIRECT;
grid.SetUseGrid( getView()->GetGAL()->GetGridSnapping() && !evt->DisableGridSnapping() );
if( step == SET_HEIGHT && t != PCB_DIM_ORTHOGONAL_T )
@ -1615,7 +1629,7 @@ int DRAWING_TOOL::DrawDimension( const TOOL_EVENT& aEvent )
case SET_END:
dimension->SetEnd( cursorPos );
if( is45Limited || t == PCB_DIM_CENTER_T )
if( constrained || t == PCB_DIM_CENTER_T )
constrainDimension( dimension );
if( t == PCB_DIM_ORTHOGONAL_T )
@ -2187,11 +2201,13 @@ bool DRAWING_TOOL::drawShape( const TOOL_EVENT& aTool, PCB_SHAPE** aGraphic,
m_frame->SetMsgPanel( graphic );
grid.SetSnap( !evt->Modifier( MD_SHIFT ) );
bool is45Limited = Is45Limited() && !evt->Modifier( MD_CTRL );
auto angleSnap = GetAngleSnapMode();
if( evt->Modifier( MD_CTRL ) )
angleSnap = LEADER_MODE::DIRECT;
// Rectangular shapes never get 45-degree snapping
// Rectangular shapes never get diagonal snapping
if( shape == SHAPE_T::RECTANGLE )
is45Limited = false;
angleSnap = LEADER_MODE::DIRECT;
grid.SetUseGrid( getView()->GetGAL()->GetGridSnapping() && !evt->DisableGridSnapping() );
cursorPos = GetClampedCoords(
@ -2370,21 +2386,25 @@ bool DRAWING_TOOL::drawShape( const TOOL_EVENT& aTool, PCB_SHAPE** aGraphic,
else
clampedCursorPos = getClampedDifferenceEnd( twoPointMgr.GetOrigin(), cursorPos );
// 45 degree lines
if( started && is45Limited )
// constrained lines
if( started && angleSnap != LEADER_MODE::DIRECT )
{
const VECTOR2I lineVector( clampedCursorPos - VECTOR2I( twoPointMgr.GetOrigin() ) );
// get a restricted 45/H/V line from the last fixed point to the cursor
VECTOR2I newEnd = GetVectorSnapped45( lineVector, ( shape == SHAPE_T::RECTANGLE ) );
VECTOR2I newEnd;
if( angleSnap == LEADER_MODE::DEG90 )
newEnd = GetVectorSnapped90( lineVector );
else
newEnd = GetVectorSnapped45( lineVector, ( shape == SHAPE_T::RECTANGLE ) );
m_controls->ForceCursorPosition( true, VECTOR2I( twoPointMgr.GetEnd() ) );
twoPointMgr.SetEnd( twoPointMgr.GetOrigin() + newEnd );
twoPointMgr.SetAngleSnap( true );
twoPointMgr.SetAngleSnap( angleSnap );
}
else
{
twoPointMgr.SetEnd( clampedCursorPos );
twoPointMgr.SetAngleSnap( false );
twoPointMgr.SetAngleSnap( LEADER_MODE::DIRECT );
}
updateSegmentFromGeometryMgr( twoPointMgr, graphic );
@ -2580,7 +2600,9 @@ bool DRAWING_TOOL::drawArc( const TOOL_EVENT& aTool, PCB_SHAPE** aGraphic,
graphic->SetLayer( m_layer );
grid.SetSnap( !evt->Modifier( MD_SHIFT ) );
bool is45Limited = Is45Limited() && !evt->Modifier( MD_CTRL );
auto angleSnap = GetAngleSnapMode();
if( evt->Modifier( MD_CTRL ) )
angleSnap = LEADER_MODE::DIRECT;
grid.SetUseGrid( getView()->GetGAL()->GetGridSnapping() && !evt->DisableGridSnapping() );
VECTOR2I cursorPos = GetClampedCoords(
grid.BestSnapAnchor( m_controls->GetMousePosition(), graphic, GRID_GRAPHICS ),
@ -2656,7 +2678,7 @@ bool DRAWING_TOOL::drawArc( const TOOL_EVENT& aTool, PCB_SHAPE** aGraphic,
else if( evt->IsMotion() )
{
// set angle snap
arcManager.SetAngleSnap( is45Limited );
arcManager.SetAngleSnap( angleSnap != LEADER_MODE::DIRECT );
// update, but don't step the manager state
arcManager.AddPoint( cursorPos, false );
@ -3230,7 +3252,9 @@ int DRAWING_TOOL::DrawZone( const TOOL_EVENT& aEvent )
LSET layers( { m_frame->GetActiveLayer() } );
grid.SetSnap( !evt->Modifier( MD_SHIFT ) );
bool is45Limited = Is45Limited() && !evt->Modifier( MD_CTRL );
auto angleSnap = GetAngleSnapMode();
if( evt->Modifier( MD_CTRL ) )
angleSnap = LEADER_MODE::DIRECT;
grid.SetUseGrid( getView()->GetGAL()->GetGridSnapping() && !evt->DisableGridSnapping() );
VECTOR2I cursorPos = evt->HasPosition() ? evt->Position() : m_controls->GetMousePosition();
@ -3239,8 +3263,7 @@ int DRAWING_TOOL::DrawZone( const TOOL_EVENT& aEvent )
m_controls->ForceCursorPosition( true, cursorPos );
polyGeomMgr.SetLeaderMode( is45Limited ? POLYGON_GEOM_MANAGER::LEADER_MODE::DEG45
: POLYGON_GEOM_MANAGER::LEADER_MODE::DIRECT );
polyGeomMgr.SetLeaderMode( angleSnap );
if( evt->IsCancelInteractive() )
{

View File

@ -1399,8 +1399,8 @@ TOOL_ACTION PCB_ACTIONS::toggleHV45Mode( TOOL_ACTION_ARGS()
.Name( "pcbnew.EditorControl.toggle45" )
.Scope( AS_GLOBAL )
.DefaultHotkey( MD_SHIFT + ' ' )
.FriendlyName( _( "Constrain to H, V, 45" ) )
.Tooltip( _( "Limit actions to horizontal, vertical, or 45 degrees from the starting point" ) )
.FriendlyName( _( "Cycle H/V/45 constraint" ) )
.Tooltip( _( "Cycle between no angle constraint, 45 degrees, or horizontal/vertical only" ) )
.ToolbarState( TOOLBAR_STATE::TOGGLE )
.Icon( BITMAPS::hv45mode ) );

View File

@ -31,6 +31,7 @@
#include <pcb_base_frame.h>
#include <tool/selection.h>
#include <tools/pcb_editor_conditions.h>
#include <geometry/geometry_utils.h>
#include <functional>
#include <wx/debug.h>
@ -200,11 +201,11 @@ bool PCB_EDITOR_CONDITIONS::zoneDisplayModeFunc( const SELECTION& aSelection, PC
bool PCB_EDITOR_CONDITIONS::get45degModeFunc( const SELECTION& aSelection, PCB_BASE_FRAME* aFrame )
{
if( aFrame->IsType( FRAME_PCB_EDITOR ) )
return GetAppSettings<PCBNEW_SETTINGS>( "pcbnew" )->m_Use45DegreeLimit;
return GetAppSettings<PCBNEW_SETTINGS>( "pcbnew" )->m_AngleSnapMode != LEADER_MODE::DIRECT;
else if( aFrame->IsType( FRAME_FOOTPRINT_EDITOR ) )
return GetAppSettings<FOOTPRINT_EDITOR_SETTINGS>( "fpedit" )->m_Use45Limit;
return GetAppSettings<FOOTPRINT_EDITOR_SETTINGS>( "fpedit" )->m_AngleSnapMode != LEADER_MODE::DIRECT;
else
return aFrame->GetViewerSettingsBase()->m_ViewersDisplay.m_Use45Limit;
return aFrame->GetViewerSettingsBase()->m_ViewersDisplay.m_AngleSnapMode != LEADER_MODE::DIRECT;
}

View File

@ -103,9 +103,9 @@ public:
SELECTION_CONDITION ZoneDisplayMode( ZONE_DISPLAY_MODE aMode );
/**
* Create a functor that tests whether only 45 degree lines should be allowed
* Create a functor that tests whether angle constraints are enabled
*
* @return Functor returning true if only 45 degree lines should be allowed
* @return Functor returning true if angle constraints are enabled
*/
SELECTION_CONDITION Get45degMode();
@ -142,7 +142,7 @@ protected:
static bool zoneDisplayModeFunc( const SELECTION& aSelection, PCB_BASE_FRAME* aFrame,
ZONE_DISPLAY_MODE aMode );
///< Helper function used by Line45degMode()
///< Helper function used by Get45degMode()
static bool get45degModeFunc( const SELECTION& aSelection, PCB_BASE_FRAME* aFrame );
/// Helper function used by FootprintViewerAutoZoom()

View File

@ -329,11 +329,21 @@ PCB_SELECTION& PCB_TOOL_BASE::selection()
bool PCB_TOOL_BASE::Is45Limited() const
{
return GetAngleSnapMode() != LEADER_MODE::DIRECT;
}
bool PCB_TOOL_BASE::Is90Limited() const
{
return GetAngleSnapMode() == LEADER_MODE::DEG90;
}
LEADER_MODE PCB_TOOL_BASE::GetAngleSnapMode() const
{
if( frame<PCB_BASE_FRAME>()->IsType( FRAME_PCB_EDITOR ) )
return GetAppSettings<PCBNEW_SETTINGS>( "pcbnew" )->m_Use45DegreeLimit;
return GetAppSettings<PCBNEW_SETTINGS>( "pcbnew" )->m_AngleSnapMode;
else
return GetAppSettings<FOOTPRINT_EDITOR_SETTINGS>( "fpedit" )->m_Use45Limit;
return GetAppSettings<FOOTPRINT_EDITOR_SETTINGS>( "fpedit" )->m_AngleSnapMode;
}

View File

@ -35,6 +35,7 @@
#include <pcb_view.h>
#include <pcb_draw_panel_gal.h>
#include <pcbnew_settings.h>
#include <preview_items/two_point_geom_manager.h>
#include <functional>
#include <tool/tool_menu.h>
@ -114,6 +115,16 @@ public:
*/
virtual bool Is45Limited() const;
/**
* Should the tool limit drawing to horizontal and vertical only?
*/
virtual bool Is90Limited() const;
/**
* Get the current angle snapping mode.
*/
LEADER_MODE GetAngleSnapMode() const;
protected:
/**
* Options for placing items interactively.

View File

@ -31,6 +31,7 @@
#include <kiplatform/ui.h>
#include <pcb_base_frame.h>
#include <preview_items/ruler_item.h>
#include <preview_items/two_point_geom_manager.h>
#include <pgm_base.h>
#include <settings/settings_manager.h>
#include <tool/actions.h>
@ -108,11 +109,38 @@ template<class T> void Flip( T& aValue )
int PCB_VIEWER_TOOLS::ToggleHV45Mode( const TOOL_EVENT& toolEvent )
{
if( frame()->IsType( FRAME_PCB_EDITOR ) )
Flip( GetAppSettings<PCBNEW_SETTINGS>( "pcbnew" )->m_Use45DegreeLimit );
{
PCBNEW_SETTINGS* settings = GetAppSettings<PCBNEW_SETTINGS>( "pcbnew" );
switch( settings->m_AngleSnapMode )
{
case LEADER_MODE::DIRECT: settings->m_AngleSnapMode = LEADER_MODE::DEG45; break;
case LEADER_MODE::DEG45: settings->m_AngleSnapMode = LEADER_MODE::DEG90; break;
default: settings->m_AngleSnapMode = LEADER_MODE::DIRECT; break;
}
}
else if( frame()->IsType( FRAME_FOOTPRINT_EDITOR ) )
Flip( GetAppSettings<FOOTPRINT_EDITOR_SETTINGS>( "fpedit" )->m_Use45Limit );
{
FOOTPRINT_EDITOR_SETTINGS* settings = GetAppSettings<FOOTPRINT_EDITOR_SETTINGS>( "fpedit" );
switch( settings->m_AngleSnapMode )
{
case LEADER_MODE::DIRECT: settings->m_AngleSnapMode = LEADER_MODE::DEG45; break;
case LEADER_MODE::DEG45: settings->m_AngleSnapMode = LEADER_MODE::DEG90; break;
default: settings->m_AngleSnapMode = LEADER_MODE::DIRECT; break;
}
}
else
Flip( frame()->GetViewerSettingsBase()->m_ViewersDisplay.m_Use45Limit );
{
LEADER_MODE& mode = frame()->GetViewerSettingsBase()->m_ViewersDisplay.m_AngleSnapMode;
switch( mode )
{
case LEADER_MODE::DIRECT: mode = LEADER_MODE::DEG45; break;
case LEADER_MODE::DEG45: mode = LEADER_MODE::DEG90; break;
default: mode = LEADER_MODE::DIRECT; break;
}
}
frame()->UpdateStatusBar();
@ -340,16 +368,22 @@ int PCB_VIEWER_TOOLS::MeasureTool( const TOOL_EVENT& aEvent )
// move or drag when origin set updates rules
else if( originSet && ( evt->IsMotion() || evt->IsDrag( BUT_LEFT ) ) )
{
bool force45Deg;
auto snap = LEADER_MODE::DIRECT;
if( frame()->IsType( FRAME_PCB_EDITOR ) )
force45Deg = GetAppSettings<PCBNEW_SETTINGS>( "pcbnew" )->m_Use45DegreeLimit;
{
snap = GetAppSettings<PCBNEW_SETTINGS>( "pcbnew" )->m_AngleSnapMode;
}
else if( frame()->IsType( FRAME_FOOTPRINT_EDITOR ) )
force45Deg = GetAppSettings<FOOTPRINT_EDITOR_SETTINGS>( "fpedit" )->m_Use45Limit;
{
snap = GetAppSettings<FOOTPRINT_EDITOR_SETTINGS>( "fpedit" )->m_AngleSnapMode;
}
else
force45Deg = frame()->GetViewerSettingsBase()->m_ViewersDisplay.m_Use45Limit;
{
snap = frame()->GetViewerSettingsBase()->m_ViewersDisplay.m_AngleSnapMode;
}
twoPtMgr.SetAngleSnap( force45Deg );
twoPtMgr.SetAngleSnap( snap );
twoPtMgr.SetEnd( cursorPos );
view.SetVisible( &ruler, true );

View File

@ -348,14 +348,18 @@ int POSITION_RELATIVE_TOOL::PositionRelativeInteractively( const TOOL_EVENT& aEv
// move or drag when origin set updates rules
else if( originSet && ( evt->IsMotion() || evt->IsDrag( BUT_LEFT ) ) )
{
bool force45Deg;
auto snap = LEADER_MODE::DIRECT;
if( frame()->IsType( FRAME_PCB_EDITOR ) )
force45Deg = GetAppSettings<PCBNEW_SETTINGS>( "pcbnew" )->m_Use45DegreeLimit;
{
snap = GetAppSettings<PCBNEW_SETTINGS>( "pcbnew" )->m_AngleSnapMode;
}
else
force45Deg = GetAppSettings<FOOTPRINT_EDITOR_SETTINGS>( "fpedit" )->m_Use45Limit;
{
snap = GetAppSettings<FOOTPRINT_EDITOR_SETTINGS>( "fpedit" )->m_AngleSnapMode;
}
twoPtMgr.SetAngleSnap( force45Deg );
twoPtMgr.SetAngleSnap( snap );
twoPtMgr.SetEnd( cursorPos );
view.SetVisible( &ruler, true );

View File

@ -281,8 +281,9 @@ bool ZONE_CREATE_HELPER::OnFirstPoint( POLYGON_GEOM_MANAGER& aMgr )
m_parentView.SetVisible( &m_previewItem, true );
aMgr.SetLeaderMode( m_tool.Is45Limited() ? POLYGON_GEOM_MANAGER::LEADER_MODE::DEG45
: POLYGON_GEOM_MANAGER::LEADER_MODE::DIRECT );
LEADER_MODE mode = m_tool.GetAngleSnapMode();
aMgr.SetLeaderMode( mode );
}
}
@ -328,7 +329,7 @@ void ZONE_CREATE_HELPER::OnComplete( const POLYGON_GEOM_MANAGER& aMgr )
// In DEG45 mode, we may have intermediate points in the leader that should be included
// as they are shown in the preview. These typically maintain the 45 constraint
if( aMgr.GetLeaderMode() == POLYGON_GEOM_MANAGER::LEADER_MODE::DEG45 )
if( aMgr.GetLeaderMode() == LEADER_MODE::DEG45 || aMgr.GetLeaderMode() == LEADER_MODE::DEG90 )
{
const SHAPE_LINE_CHAIN leaderPts = aMgr.GetLeaderLinePoints();

View File

@ -60,7 +60,7 @@ public:
ZONE* m_sourceZone;
///< Zone leader mode
POLYGON_GEOM_MANAGER::LEADER_MODE m_leaderMode;
LEADER_MODE m_leaderMode;
};
/**