Bounds-awareness in Move, Rotate, point editor, drawing tool

This commit is contained in:
Alex 2021-12-12 02:57:51 +03:00 committed by Seth Hillbrand
parent 6d739ca9da
commit 405be8d15f
7 changed files with 213 additions and 34 deletions

View File

@ -73,6 +73,7 @@
#include <string_utils.h>
#include <zone.h>
const unsigned int DRAWING_TOOL::COORDS_PADDING = Millimeter2iu( 20 );
using SCOPED_DRAW_MODE = SCOPED_SET_RESET<DRAWING_TOOL::MODE>;
@ -1115,8 +1116,8 @@ int DRAWING_TOOL::DrawDimension( const TOOL_EVENT& aEvent )
}
VECTOR2I cursorPos = evt->HasPosition() ? evt->Position() : m_controls->GetMousePosition();
cursorPos = GetClampedCoords( grid.BestSnapAnchor( cursorPos, nullptr ), COORDS_PADDING );
cursorPos = grid.BestSnapAnchor( cursorPos, nullptr );
m_controls->ForceCursorPosition( true, cursorPos );
if( evt->IsCancelInteractive() )
@ -1844,7 +1845,9 @@ bool DRAWING_TOOL::drawShape( const std::string& aTool, PCB_SHAPE** aGraphic,
grid.SetSnap( !evt->Modifier( MD_SHIFT ) );
grid.SetUseGrid( getView()->GetGAL()->GetGridSnapping() && !evt->DisableGridSnapping() );
cursorPos = grid.BestSnapAnchor( m_controls->GetMousePosition(), m_layer );
cursorPos = GetClampedCoords(
grid.BestSnapAnchor( m_controls->GetMousePosition(), m_layer ),
COORDS_PADDING );
m_controls->ForceCursorPosition( true, cursorPos );
if( evt->IsCancelInteractive() )
@ -2029,14 +2032,27 @@ bool DRAWING_TOOL::drawShape( const std::string& aTool, PCB_SHAPE** aGraphic,
break;
}
twoPointManager.SetEnd( cursorPos );
twoPointManager.SetEnd( GetClampedCoords( cursorPos ) );
}
else if( evt->IsMotion() )
{
VECTOR2I clampedCursorPos = cursorPos;
if( shape == SHAPE_T::CIRCLE || shape == SHAPE_T::ARC )
{
clampedCursorPos = getClampedRadiusEnd( twoPointManager.GetOrigin(), cursorPos );
}
else
{
clampedCursorPos =
getClampedDifferenceEnd( twoPointManager.GetOrigin(), cursorPos );
}
// 45 degree lines
if( started && Is45Limited() )
{
const VECTOR2I lineVector( cursorPos - VECTOR2I( twoPointManager.GetOrigin() ) );
const VECTOR2I lineVector( clampedCursorPos
- VECTOR2I( twoPointManager.GetOrigin() ) );
// get a restricted 45/H/V line from the last fixed point to the cursor
VECTOR2I newEnd = GetVectorSnapped45( lineVector, ( shape == SHAPE_T::RECT ) );
@ -2046,7 +2062,7 @@ bool DRAWING_TOOL::drawShape( const std::string& aTool, PCB_SHAPE** aGraphic,
}
else
{
twoPointManager.SetEnd( cursorPos );
twoPointManager.SetEnd( clampedCursorPos );
twoPointManager.SetAngleSnap( false );
}
@ -2191,7 +2207,8 @@ bool DRAWING_TOOL::drawArc( const std::string& aTool, PCB_SHAPE** aGraphic,
grid.SetSnap( !evt->Modifier( MD_SHIFT ) );
grid.SetUseGrid( getView()->GetGAL()->GetGridSnapping() && !evt->DisableGridSnapping() );
VECTOR2I cursorPos = grid.BestSnapAnchor( m_controls->GetMousePosition(), graphic );
VECTOR2I cursorPos = GetClampedCoords(
grid.BestSnapAnchor( m_controls->GetMousePosition(), graphic ), COORDS_PADDING );
m_controls->ForceCursorPosition( true, cursorPos );
if( evt->IsCancelInteractive() )
@ -2511,7 +2528,7 @@ int DRAWING_TOOL::DrawZone( const TOOL_EVENT& aEvent )
grid.SetUseGrid( getView()->GetGAL()->GetGridSnapping() && !evt->DisableGridSnapping() );
VECTOR2I cursorPos = evt->HasPosition() ? evt->Position() : m_controls->GetMousePosition();
cursorPos = grid.BestSnapAnchor( cursorPos, layers );
cursorPos = GetClampedCoords( grid.BestSnapAnchor( cursorPos, layers ), COORDS_PADDING );
m_controls->ForceCursorPosition( true, cursorPos );

View File

@ -272,6 +272,65 @@ private:
*/
void constrainDimension( PCB_DIMENSION_BASE* aDim );
/**
* Clamps the end vector to respect numeric limits of difference representation
*
* @param aOrigin - the origin vector.
* @param aEnd - the end vector.
* @return clamped end vector.
*/
VECTOR2I getClampedDifferenceEnd( const VECTOR2I& aOrigin, const VECTOR2I& aEnd )
{
typedef std::numeric_limits<int> coord_limits;
const int guardValue = 1;
long maxDiff = coord_limits::max() - guardValue;
long xDiff = long( aEnd.x ) - aOrigin.x;
long yDiff = long( aEnd.y ) - aOrigin.y;
if( xDiff > maxDiff )
xDiff = maxDiff;
if( yDiff > maxDiff )
yDiff = maxDiff;
if( xDiff < -maxDiff )
xDiff = -maxDiff;
if( yDiff < -maxDiff )
yDiff = -maxDiff;
return aOrigin + VECTOR2I( int( xDiff ), int( yDiff ) );
}
/**
* Clamps the end vector to respect numeric limits of radius representation
*
* @param aOrigin - the origin vector.
* @param aEnd - the end vector.
* @return clamped end vector.
*/
VECTOR2I getClampedRadiusEnd( const VECTOR2I& aOrigin, const VECTOR2I& aEnd )
{
typedef std::numeric_limits<int> coord_limits;
const int guardValue = 10;
long xDiff = long( aEnd.x ) - aOrigin.x;
long yDiff = long( aEnd.y ) - aOrigin.y;
double maxRadius = coord_limits::max() / 2 - guardValue;
double radius = std::hypot( xDiff, yDiff );
if( radius > maxRadius )
{
double scaleFactor = maxRadius / radius;
xDiff = KiROUND<double, int>( xDiff * scaleFactor );
yDiff = KiROUND<double, int>( yDiff * scaleFactor );
}
return aOrigin + VECTOR2I( int( xDiff ), int( yDiff ) );
}
///< Return the appropriate width for a segment depending on the settings.
int getSegmentWidth( PCB_LAYER_ID aLayer ) const;
@ -287,6 +346,8 @@ private:
TEXT_ATTRIBUTES m_textAttrs;
static const unsigned int WIDTH_STEP; // Amount of width change for one -/+ key press
static const unsigned int COORDS_PADDING; // Padding from coordinates limits for this tool
friend class ZONE_CREATE_HELPER; // give internal access to helper classes
};

View File

@ -32,6 +32,7 @@
#include <fp_shape.h>
#include <fp_textbox.h>
#include <collectors.h>
#include <convert_to_biu.h>
#include <pcb_edit_frame.h>
#include <drawing_sheet/ds_proxy_view_item.h>
#include <kiway.h>
@ -64,6 +65,7 @@ using namespace std::placeholders;
#include <pcb_target.h>
#include <zone_filler.h>
const unsigned int EDIT_TOOL::COORDS_PADDING = Millimeter2iu( 20 );
EDIT_TOOL::EDIT_TOOL() :
PCB_TOOL_BASE( "pcbnew.InteractiveEdit" ),
@ -1061,40 +1063,64 @@ int EDIT_TOOL::Rotate( const TOOL_EVENT& aEvent )
VECTOR2I refPt = selection.GetReferencePoint();
EDA_ANGLE rotateAngle = TOOL_EVT_UTILS::GetEventRotationAngle( *editFrame, aEvent );
// When editing footprints, all items have the same parent
if( IsFootprintEditor() )
m_commit->Modify( selection.Front() );
// Calculate view bounding box
BOX2I viewBBox = selection.Front()->ViewBBox();
for( EDA_ITEM* item : selection )
{
if( !item->IsNew() && !IsFootprintEditor() )
{
m_commit->Modify( item );
viewBBox.Merge( item->ViewBBox() );
// If rotating a group, record position of all the descendants for undo
if( item->Type() == PCB_GROUP_T )
// Check if the view bounding box will go out of bounds
VECTOR2D rotPos = viewBBox.GetPosition();
VECTOR2D rotEnd = viewBBox.GetEnd();
RotatePoint( &rotPos.x, &rotPos.y, refPt.x, refPt.y, rotateAngle );
RotatePoint( &rotEnd.x, &rotEnd.y, refPt.x, refPt.y, rotateAngle );
typedef std::numeric_limits<int> coord_limits;
long max = coord_limits::max() - COORDS_PADDING;
long min = -max;
bool outOfBounds = rotPos.x < min || rotPos.x > max || rotPos.y < min || rotPos.y > max
|| rotEnd.x < min || rotEnd.x > max || rotEnd.y < min || rotEnd.y > max;
if( !outOfBounds )
{
// When editing footprints, all items have the same parent
if( IsFootprintEditor() )
m_commit->Modify( selection.Front() );
for( EDA_ITEM* item : selection )
{
if( !item->IsNew() && !IsFootprintEditor() )
{
static_cast<PCB_GROUP*>( item )->RunOnDescendants( [&]( BOARD_ITEM* bItem )
{
m_commit->Modify( bItem );
});
m_commit->Modify( item );
// If rotating a group, record position of all the descendants for undo
if( item->Type() == PCB_GROUP_T )
{
static_cast<PCB_GROUP*>( item )->RunOnDescendants( [&]( BOARD_ITEM* bItem )
{
m_commit->Modify( bItem );
});
}
}
static_cast<BOARD_ITEM*>( item )->Rotate( refPt, rotateAngle );
}
static_cast<BOARD_ITEM*>( item )->Rotate( refPt, rotateAngle );
if( !m_dragging )
m_commit->Push( _( "Rotate" ) );
if( is_hover && !m_dragging )
m_toolMgr->RunAction( PCB_ACTIONS::selectionClear, true );
m_toolMgr->ProcessEvent( EVENTS::SelectedItemsModified );
if( m_dragging )
m_toolMgr->RunAction( PCB_ACTIONS::updateLocalRatsnest, false );
}
if( !m_dragging )
m_commit->Push( _( "Rotate" ) );
if( is_hover && !m_dragging )
m_toolMgr->RunAction( PCB_ACTIONS::selectionClear, true );
m_toolMgr->ProcessEvent( EVENTS::SelectedItemsModified );
if( m_dragging )
m_toolMgr->RunAction( PCB_ACTIONS::updateLocalRatsnest, false );
// Restore the old reference so any mouse dragging that occurs doesn't make the selection jump
// to this now invalid reference
if( oldRefPt )

View File

@ -182,6 +182,9 @@ private:
int doMoveSelection( TOOL_EVENT aEvent, bool aPickReference = false );
VECTOR2I getSafeMovement( const VECTOR2I& aMovement, const BOX2I& aSourceBBox,
const VECTOR2D& aBBoxOffset );
bool pickReferencePoint( const wxString& aTooltip, const wxString& aSuccessMessage,
const wxString& aCanceledMessage, VECTOR2I& aReferencePoint );
@ -192,6 +195,8 @@ private:
VECTOR2I m_cursor; // Last cursor position (so getModificationPoint()
// can avoid changes of edit reference point).
std::unique_ptr<STATUS_TEXT_POPUP> m_statusPopup;
static const unsigned int COORDS_PADDING; // Padding from coordinates limits for this tool
};
#endif

View File

@ -294,6 +294,41 @@ int EDIT_TOOL::MoveWithReference( const TOOL_EVENT& aEvent )
}
VECTOR2I EDIT_TOOL::getSafeMovement( const VECTOR2I& aMovement, const BOX2I& aSourceBBox,
const VECTOR2D& aBBoxOffset )
{
typedef std::numeric_limits<int> coord_limits;
long max = coord_limits::max();
long min = -max;
double left = aBBoxOffset.x + aSourceBBox.GetPosition().x;
double top = aBBoxOffset.y + aSourceBBox.GetPosition().y;
double right = left + aSourceBBox.GetSize().x;
double bottom = top + aSourceBBox.GetSize().y;
// Do not restrict movement if bounding box is already out of bounds
if( left < min || top < min || right > max || bottom > max )
return aMovement;
// Constrain moving bounding box to coordinates limits
VECTOR2D tryMovement( aMovement );
VECTOR2D clampedBBoxOrigin = GetClampedCoords(
VECTOR2D( aSourceBBox.GetPosition() ) + aBBoxOffset + tryMovement, COORDS_PADDING );
tryMovement = clampedBBoxOrigin - aBBoxOffset - aSourceBBox.GetPosition();
VECTOR2D clampedBBoxEnd = GetClampedCoords(
VECTOR2D( aSourceBBox.GetEnd() ) + aBBoxOffset + tryMovement, COORDS_PADDING );
tryMovement = clampedBBoxEnd - aBBoxOffset - aSourceBBox.GetEnd();
return GetClampedCoords<double, int>( tryMovement );
}
// Note: aEvent MUST NOT be const&; the source will get de-allocated if we go into the picker's
// event loop.
int EDIT_TOOL::doMoveSelection( TOOL_EVENT aEvent, bool aPickReference )
@ -397,6 +432,9 @@ int EDIT_TOOL::doMoveSelection( TOOL_EVENT aEvent, bool aPickReference )
bool restore_state = false;
VECTOR2I originalPos;
VECTOR2I totalMovement;
VECTOR2D bboxMovement;
BOX2I originalBBox;
bool updateBBox = true;
PCB_GRID_HELPER grid( m_toolMgr, editFrame->GetMagneticItemsSettings() );
TOOL_EVENT* evt = &aEvent;
VECTOR2I prevPos;
@ -467,12 +505,36 @@ int EDIT_TOOL::doMoveSelection( TOOL_EVENT aEvent, bool aPickReference )
m_cursor = originalPos + GetVectorSnapped45( moveVector );
}
if( updateBBox )
{
originalBBox = BOX2I();
bboxMovement = VECTOR2D();
for( EDA_ITEM* item : sel_items )
{
BOX2I viewBBOX = item->ViewBBox();
if( originalBBox.GetWidth() == 0 && originalBBox.GetHeight() == 0 )
originalBBox = viewBBOX;
else
originalBBox.Merge( viewBBOX );
}
updateBBox = false;
}
// Constrain selection bounding box to coordinates limits
movement = getSafeMovement( m_cursor - prevPos, originalBBox, bboxMovement );
// Apply constrained movement
m_cursor = prevPos + movement;
controls->ForceCursorPosition( true, m_cursor );
selection.SetReferencePoint( m_cursor );
movement = m_cursor - prevPos;
prevPos = m_cursor;
totalMovement += movement;
bboxMovement += movement;
// Drag items to the current cursor position
for( EDA_ITEM* item : sel_items )
@ -637,6 +699,9 @@ int EDIT_TOOL::doMoveSelection( TOOL_EVENT aEvent, bool aPickReference )
originalPos = m_cursor;
}
// Update variables for bounding box collision calculations
updateBBox = true;
controls->SetCursorPosition( m_cursor, false );
prevPos = m_cursor;
@ -675,6 +740,7 @@ int EDIT_TOOL::doMoveSelection( TOOL_EVENT aEvent, bool aPickReference )
|| evt->IsAction( &PCB_ACTIONS::flip )
|| evt->IsAction( &PCB_ACTIONS::mirror ) )
{
updateBBox = true;
eatFirstMouseUp = false;
evt->SetPassEvent();
}

View File

@ -49,6 +49,7 @@ using namespace std::placeholders;
#include <connectivity/connectivity_data.h>
#include <progress_reporter.h>
const unsigned int PCB_POINT_EDITOR::COORDS_PADDING = Millimeter2iu( 20 );
// Few constants to avoid using bare numbers for point indices
enum SEG_POINTS
@ -543,7 +544,8 @@ int PCB_POINT_EDITOR::OnSelectionChange( const TOOL_EVENT& aEvent )
}
}
VECTOR2I pos = evt->Position();
// Keep point inside of limits with some padding
VECTOR2I pos = GetClampedCoords<double, int>( evt->Position(), COORDS_PADDING );
LSET snapLayers;
switch( m_editedPoint->GetSnapConstraint() )

View File

@ -177,6 +177,8 @@ private:
// Alternative constraint, enabled while a modifier key is held
std::shared_ptr<EDIT_CONSTRAINT<EDIT_POINT>> m_altConstraint;
EDIT_POINT m_altConstrainer;
static const unsigned int COORDS_PADDING; // Padding from coordinates limits for this tool
};
#endif