Fix polygon point editor behavior to avoid hard limits

When dragging a vertex, use the 45/90 limits as guides and display the
full guidelines.  When dragging a midpoint, do not use these limits.
Keep the standard converging limit
This commit is contained in:
Seth Hillbrand
2025-10-10 13:31:49 -07:00
parent cc245475c5
commit 3a67dceab2
7 changed files with 146 additions and 8 deletions
+13 -2
View File
@@ -26,6 +26,7 @@
#include <layer_ids.h>
#include <utility>
#include <gal/graphics_abstraction_layer.h>
#include <geometry/line.h>
#include <geometry/shape_utils.h>
#include <preview_items/item_drawing_utils.h>
#include <view/view.h>
@@ -97,7 +98,7 @@ void CONSTRUCTION_GEOM::ViewDraw( int aLayer, VIEW* aView ) const
for( const DRAWABLE_INFO& drawable : m_drawables )
{
gal.SetStrokeColor( drawable.IsPersistent ? m_persistentColor : m_color );
gal.SetLineWidth( drawable.LineWidth );
gal.SetLineWidth( drawable.LineWidth / gal.GetWorldScale() );
std::visit(
[&]( const auto& visited )
@@ -149,9 +150,19 @@ void CONSTRUCTION_GEOM::ViewDraw( int aLayer, VIEW* aView ) const
for( const SNAP_GUIDE& guide : m_snapGuides )
{
const SEG& segment = guide.Segment;
if( segment.A == segment.B )
continue;
std::optional<SEG> clipped = KIGEOM::ClipLineToBox( LINE( segment ), viewport );
if( !clipped )
continue;
gal.SetStrokeColor( guide.Color );
gal.SetLineWidth( guide.LineWidth );
gal.DrawLine( guide.Segment.A, guide.Segment.B );
gal.DrawLine( clipped->A, clipped->B );
}
if( haveSnapLine )
+1 -1
View File
@@ -681,7 +681,7 @@ void SNAP_MANAGER::UpdateSnapGuides()
if( activeDirection && *activeDirection == static_cast<int>( ii ) )
{
guide.LineWidth = 2;
guide.LineWidth = 5;
guide.Color = m_snapGuideHighlightColor;
}
else
+4
View File
@@ -142,6 +142,10 @@ void GRID_HELPER::SetSnapLineOrigin( const VECTOR2I& aOrigin )
m_snapManager.GetSnapLineManager().SetSnapLineOrigin( aOrigin );
}
void GRID_HELPER::SetSnapLineEnd( const std::optional<VECTOR2I>& aEnd )
{
m_snapManager.GetSnapLineManager().SetSnapLineEnd( aEnd );
}
void GRID_HELPER::ClearSnapLine()
{
+3 -3
View File
@@ -52,7 +52,7 @@ void POLYGON_POINT_EDIT_BEHAVIOR::BuildForPolyOutline( EDIT_POINTS& aPo
else
aPoints.AddLine( aPoints.Point( i ), aPoints.Point( i + 1 ) );
aPoints.Line( i ).SetConstraint( new EC_PERPLINE( aPoints.Line( i ) ) );
aPoints.Line( i ).SetConstraint( new EC_CONVERGING( aPoints.Line( i ), aPoints ) );
}
// The last missing line, connecting the last and the first polygon point
@@ -60,7 +60,7 @@ void POLYGON_POINT_EDIT_BEHAVIOR::BuildForPolyOutline( EDIT_POINTS& aPo
aPoints.Point( aPoints.GetContourStartIdx( cornersCount - 1 ) ) );
aPoints.Line( aPoints.LinesSize() - 1 )
.SetConstraint( new EC_PERPLINE( aPoints.Line( aPoints.LinesSize() - 1 ) ) );
.SetConstraint( new EC_CONVERGING( aPoints.Line( aPoints.LinesSize() - 1 ), aPoints ) );
}
@@ -94,7 +94,7 @@ void POLYGON_POINT_EDIT_BEHAVIOR::UpdateOutlineFromPoints( SHAPE_POLY_SET& aOu
for( unsigned i = 0; i < aPoints.LinesSize(); ++i )
{
if( !isModified( aEditedPoint, aPoints.Line( i ) ) )
aPoints.Line( i ).SetConstraint( new EC_PERPLINE( aPoints.Line( i ) ) );
aPoints.Line( i ).SetConstraint( new EC_CONVERGING( aPoints.Line( i ), aPoints ) );
}
}
+3
View File
@@ -194,6 +194,9 @@ public:
/// @copydoc EDIT_CONSTRAINT::Apply()
virtual void Apply( EDIT_POINT& aHandle, const GRID_HELPER& aGrid ) override;
const EDIT_POINT& GetConstrainer() const { return m_constrainer; }
VECTOR2I GetLineVector() const { return m_line; }
private:
const EDIT_POINT& m_constrainer; ///< Point that imposes the constraint.
VECTOR2I m_line; ///< Vector representing the constraining line.
+2
View File
@@ -25,6 +25,7 @@
#define GRID_HELPER_H
#include <vector>
#include <optional>
#include <geometry/point_types.h>
#include <math/vector2d.h>
@@ -126,6 +127,7 @@ public:
void SetSnapLine( bool aSnap ) { m_enableSnapLine = aSnap; }
void SetSnapLineDirections( const std::vector<VECTOR2I>& aDirections );
void SetSnapLineOrigin( const VECTOR2I& aOrigin );
void SetSnapLineEnd( const std::optional<VECTOR2I>& aEnd );
void ClearSnapLine();
std::optional<VECTOR2I> SnapToConstructionLines( const VECTOR2I& aPoint,
const VECTOR2I& aNearestGrid,
+120 -2
View File
@@ -70,6 +70,47 @@ using namespace std::placeholders;
const unsigned int PCB_POINT_EDITOR::COORDS_PADDING = pcbIUScale.mmToIU( 20 );
static void appendDirection( std::vector<VECTOR2I>& aDirections, const VECTOR2I& aDirection )
{
if( aDirection.x != 0 || aDirection.y != 0 )
aDirections.push_back( aDirection );
}
static std::vector<VECTOR2I> getConstraintDirections( EDIT_CONSTRAINT<EDIT_POINT>* aConstraint )
{
std::vector<VECTOR2I> directions;
if( !aConstraint )
return directions;
if( dynamic_cast<EC_90DEGREE*>( aConstraint ) )
{
appendDirection( directions, VECTOR2I( 1, 0 ) );
appendDirection( directions, VECTOR2I( 0, 1 ) );
}
else if( dynamic_cast<EC_45DEGREE*>( aConstraint ) )
{
appendDirection( directions, VECTOR2I( 1, 0 ) );
appendDirection( directions, VECTOR2I( 0, 1 ) );
appendDirection( directions, VECTOR2I( 1, 1 ) );
appendDirection( directions, VECTOR2I( 1, -1 ) );
}
else if( dynamic_cast<EC_VERTICAL*>( aConstraint ) )
{
appendDirection( directions, VECTOR2I( 0, 1 ) );
}
else if( dynamic_cast<EC_HORIZONTAL*>( aConstraint ) )
{
appendDirection( directions, VECTOR2I( 1, 0 ) );
}
else if( EC_LINE* lineConstraint = dynamic_cast<EC_LINE*>( aConstraint ) )
{
appendDirection( directions, lineConstraint->GetLineVector() );
}
return directions;
}
// Few constants to avoid using bare numbers for point indices
enum RECT_POINTS
{
@@ -2158,6 +2199,39 @@ int PCB_POINT_EDITOR::OnSelectionChange( const TOOL_EVENT& aEvent )
updateEditedPoint( aEvent );
bool inDrag = false;
bool isConstrained = false;
bool haveSnapLineDirections = false;
auto updateSnapLineDirections =
[&]()
{
std::vector<VECTOR2I> directions;
if( inDrag && m_editedPoint )
{
EDIT_CONSTRAINT<EDIT_POINT>* constraint = nullptr;
if( m_altConstraint )
constraint = m_altConstraint.get();
else if( m_editedPoint->IsConstrained() )
constraint = m_editedPoint->GetConstraint();
directions = getConstraintDirections( constraint );
}
if( directions.empty() )
{
grid.SetSnapLineDirections( {} );
grid.SetSnapLineEnd( std::nullopt );
haveSnapLineDirections = false;
}
else
{
grid.SetSnapLineDirections( directions );
grid.SetSnapLineOrigin( m_original.GetPosition() );
grid.SetSnapLineEnd( std::nullopt );
haveSnapLineDirections = true;
}
};
BOARD_COMMIT commit( editFrame );
@@ -2235,6 +2309,8 @@ int PCB_POINT_EDITOR::OnSelectionChange( const TOOL_EVENT& aEvent )
clones.emplace_back( clone );
grid.AddConstructionItems( { clone }, false, true );
updateSnapLineDirections();
}
bool need_constraint = Is45Limited() || Is90Limited();
@@ -2243,6 +2319,7 @@ int PCB_POINT_EDITOR::OnSelectionChange( const TOOL_EVENT& aEvent )
{
setAltConstraint( need_constraint );
isConstrained = need_constraint;
updateSnapLineDirections();
}
// Keep point inside of limits with some padding
@@ -2319,16 +2396,20 @@ int PCB_POINT_EDITOR::OnSelectionChange( const TOOL_EVENT& aEvent )
}
}
bool constraintSnapped = false;
// Apply 45 degree or other constraints
if( !m_angleSnapActive && m_altConstraint )
{
m_editedPoint->SetPosition( pos );
m_altConstraint->Apply( grid );
constraintSnapped = true;
}
else if( !m_angleSnapActive && m_editedPoint->IsConstrained() )
{
m_editedPoint->SetPosition( pos );
m_editedPoint->ApplyConstraint( grid );
constraintSnapped = true;
}
else if( !m_angleSnapActive && m_editedPoint->GetGridConstraint() == SNAP_TO_GRID )
{
@@ -2340,6 +2421,14 @@ int PCB_POINT_EDITOR::OnSelectionChange( const TOOL_EVENT& aEvent )
m_editedPoint->SetPosition( pos );
}
if( haveSnapLineDirections )
{
if( constraintSnapped )
grid.SetSnapLineEnd( m_editedPoint->GetPosition() );
else
grid.SetSnapLineEnd( std::nullopt );
}
updateItem( commit );
getViewControls()->ForceCursorPosition( true, m_editedPoint->GetPosition() );
updatePoints();
@@ -2398,6 +2487,7 @@ int PCB_POINT_EDITOR::OnSelectionChange( const TOOL_EVENT& aEvent )
getViewControls()->SetAutoPan( false );
setAltConstraint( false );
updateSnapLineDirections();
if( m_editorBehavior )
m_editorBehavior->FinalizeItem( *m_editPoints, commit );
@@ -2428,6 +2518,7 @@ int PCB_POINT_EDITOR::OnSelectionChange( const TOOL_EVENT& aEvent )
inDrag = false;
frame()->UndoRedoBlock( false );
updateSnapLineDirections();
m_toolMgr->PostAction<EDA_ITEM*>( ACTIONS::reselectItem, item ); // FIXME: Needed for generators
}
@@ -2451,6 +2542,7 @@ int PCB_POINT_EDITOR::OnSelectionChange( const TOOL_EVENT& aEvent )
inDrag = false;
frame()->UndoRedoBlock( false );
updateSnapLineDirections();
}
// Only cancel point editor when activating a new tool
@@ -2511,6 +2603,7 @@ int PCB_POINT_EDITOR::OnSelectionChange( const TOOL_EVENT& aEvent )
}
m_editedPoint = nullptr;
grid.SetSnapLineDirections( {} );
return 0;
}
@@ -2640,9 +2733,11 @@ void PCB_POINT_EDITOR::updatePoints()
return;
int editedIndex = -1;
bool editingLine = false;
if( m_editedPoint )
{
// Check if we're editing a point (vertex)
for( unsigned ii = 0; ii < m_editPoints->PointsSize(); ++ii )
{
if( &m_editPoints->Point( ii ) == m_editedPoint )
@@ -2651,6 +2746,20 @@ void PCB_POINT_EDITOR::updatePoints()
break;
}
}
// If not found in points, check if we're editing a line (midpoint)
if( editedIndex == -1 )
{
for( unsigned ii = 0; ii < m_editPoints->LinesSize(); ++ii )
{
if( &m_editPoints->Line( ii ) == m_editedPoint )
{
editedIndex = ii;
editingLine = true;
break;
}
}
}
}
if( !m_editorBehavior->UpdatePoints( *m_editPoints ) )
@@ -2660,10 +2769,19 @@ void PCB_POINT_EDITOR::updatePoints()
getView()->Add( m_editPoints.get() );
}
if( editedIndex >= 0 && editedIndex < (int) m_editPoints->PointsSize())
m_editedPoint = &m_editPoints->Point( editedIndex );
if( editedIndex >= 0 )
{
if( editingLine && editedIndex < (int) m_editPoints->LinesSize() )
m_editedPoint = &m_editPoints->Line( editedIndex );
else if( !editingLine && editedIndex < (int) m_editPoints->PointsSize() )
m_editedPoint = &m_editPoints->Point( editedIndex );
else
m_editedPoint = nullptr;
}
else
{
m_editedPoint = nullptr;
}
getView()->Update( m_editPoints.get() );