Bounds-awareness in Move, Rotate, point editor, drawing tool
This commit is contained in:
parent
6d739ca9da
commit
405be8d15f
|
@ -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 );
|
||||
|
||||
|
|
|
@ -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
|
||||
};
|
||||
|
|
|
@ -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 )
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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() )
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue