pcbnew: Add an alternate edit method for arcs

The alternate edit methods keeps the radius constant

Fixes https://gitlab.com/kicad/code/kicad/-/issues/5369
This commit is contained in:
Fabien Corona 2020-08-29 22:59:11 +00:00 committed by Seth Hillbrand
parent cc64709407
commit 4a0d6297ab
5 changed files with 469 additions and 243 deletions

View File

@ -194,6 +194,9 @@ TOOL_ACTION ACTIONS::deleteTool( "common.Interactive.deleteTool",
TOOL_ACTION ACTIONS::activatePointEditor( "common.Control.activatePointEditor",
AS_GLOBAL );
TOOL_ACTION ACTIONS::changeEditMethod( "common.Interactive.changeEditMethod", AS_GLOBAL,
MD_CTRL + ' ', "", _( "Change Edit Method" ), _( "Change edit method constraints" ) );
TOOL_ACTION ACTIONS::find( "common.Interactive.find",
AS_GLOBAL,
MD_CTRL + 'F', LEGACY_HK_NAME( "Find" ),

View File

@ -163,6 +163,7 @@ public:
// Internal
static TOOL_ACTION updateMenu;
static TOOL_ACTION activatePointEditor;
static TOOL_ACTION changeEditMethod;
// Suite
static TOOL_ACTION configurePaths;

View File

@ -1220,6 +1220,7 @@ void DRAWSEGMENT::SwapData( BOARD_ITEM* aImage )
std::swap( m_Width, image->m_Width );
std::swap( m_Start, image->m_Start );
std::swap( m_End, image->m_End );
std::swap( m_ThirdPoint, image->m_ThirdPoint );
std::swap( m_Shape, image->m_Shape );
std::swap( m_Type, image->m_Type );
std::swap( m_Angle, image->m_Angle );

View File

@ -469,6 +469,177 @@ int POINT_EDITOR::OnSelectionChange( const TOOL_EVENT& aEvent )
return 0;
}
void POINT_EDITOR::editArcEndpointKeepTangent( DRAWSEGMENT* aArc, VECTOR2I aCenter, VECTOR2I aStart,
VECTOR2I aMid, VECTOR2I aEnd,
const VECTOR2I aCursor ) const
{
VECTOR2D startLine = aStart - aCenter;
VECTOR2D endLine = aEnd - aCenter;
double newAngle = RAD2DECIDEG( endLine.Angle() - startLine.Angle() );
bool clockwise;
bool movingStart;
bool arcValid = true;
VECTOR2I *p1, *p2, *p3;
// p1 does not move, p2 does.
if( aStart != aArc->GetArcStart() )
{
aStart = aCursor;
p1 = &aEnd;
p2 = &aStart;
p3 = &aMid;
movingStart = true;
}
else
{
aEnd = aCursor;
p1 = &aStart;
p2 = &aEnd;
p3 = &aMid;
movingStart = false;
}
VECTOR2D v1, v2, v3, v4;
// Move the coordinate system
v1 = *p1 - aCenter;
v2 = *p2 - aCenter;
v3 = *p3 - aCenter;
VECTOR2D u1, u2, u3;
// A point cannot be both the center and on the arc.
if( ( v1.EuclideanNorm() == 0 ) || ( v2.EuclideanNorm() == 0 ) )
return;
u1 = v1 / v1.EuclideanNorm();
u2 = v3 - ( u1.x * v3.x + u1.y * v3.y ) * u1;
u2 = u2 / u2.EuclideanNorm();
// [ u1, u3 ] is a base centered on the circle with:
// u1 : unit vector toward the point that does not move
// u2 : unit vector toward the mid point.
// Get vectors v1, and v2 in that coordinate system.
double det = u1.x * u2.y - u2.x * u1.y;
// u1 and u2 are unit vectors, and perpendicular.
// det should not be 0. In case it is, do not change the arc.
if( det == 0 )
return;
double tmpx = v1.x * u2.y - v1.y * u2.x;
double tmpy = -v1.x * u1.y + v1.y * u1.x;
v1.x = tmpx;
v1.y = tmpy;
v1 = v1 / det;
tmpx = v2.x * u2.y - v2.y * u2.x;
tmpy = -v2.x * u1.y + v2.y * u1.x;
v2.x = tmpx;
v2.y = tmpy;
v2 = v2 / det;
double R = v1.EuclideanNorm();
bool transformCircle = false;
/* p2
* X***
* ** <---- This is the arc
* y ^ **
* | R *
* | <-----------> *
* x------x------>--------x p1
* C' <----> C x
* delta
*
* p1 does not move, and the tangent at p1 remains the same.
* => The new center, C', will be on the C-p1 axis.
* p2 moves
*
* The radius of the new circle is delta + R
*
* || C' p2 || = || C' P1 ||
* is the same as :
* ( delta + p2.x ) ^ 2 + p2.y ^ 2 = ( R + delta ) ^ 2
*
* delta = ( R^2 - p2.x ^ 2 - p2.y ^2 ) / ( 2 * p2.x - 2 * R )
*
* We can use this equation for any point p2 with p2.x < R
*/
if( v2.x == R )
{
// Straight line, do nothing
}
else
{
if( v2.x > R )
{
// If we need to invert the curvature.
// We modify the input so we can use the same equation
transformCircle = true;
v2.x = 2 * R - v2.x;
}
// We can keep the tangent constraint.
double delta = ( R * R - v2.x * v2.x - v2.y * v2.y ) / ( 2 * v2.x - 2 * R );
// This is just to limit the radius, so nothing overflows later when drawing.
if( abs( v2.y / ( R - v2.x ) ) > ADVANCED_CFG::GetCfg().m_drawArcCenterMaxAngle )
{
arcValid = false;
}
// Never recorded a problem, but still checking.
if( !std::isfinite( delta ) )
{
arcValid = false;
}
// v4 is the new center
v4 = ( !transformCircle ) ? VECTOR2D( -delta, 0 ) : VECTOR2D( 2 * R + delta, 0 );
clockwise = aArc->GetAngle() > 0;
if( transformCircle )
clockwise = !clockwise;
tmpx = v4.x * u1.x + v4.y * u2.x;
tmpy = v4.x * u1.y + v4.y * u2.y;
v4.x = tmpx;
v4.y = tmpy;
aCenter = v4 + aCenter;
startLine = aStart - aCenter;
endLine = aEnd - aCenter;
newAngle = RAD2DECIDEG( endLine.Angle() - startLine.Angle() );
if( clockwise && newAngle < 0.0 )
newAngle += 3600.0;
else if( !clockwise && newAngle > 0.0 )
newAngle -= 3600.0;
if( arcValid )
{
aArc->SetAngle( newAngle );
aArc->SetCenter( wxPoint( aCenter.x, aCenter.y ) );
if( movingStart )
{
aArc->SetArcStart( wxPoint( aStart.x, aStart.y ) );
// Set angle computes the end point, so re-force it now.
aArc->SetArcEnd( wxPoint( aEnd.x, aEnd.y ) );
}
else
{
aArc->SetArcEnd( wxPoint( aEnd.x, aEnd.y ) );
}
}
}
}
/**
* Update the coordinates of 4 corners of a rectangle, according to pad constraints and the
@ -572,6 +743,227 @@ static void pinEditedCorner( int aEditedPointIndex, int aMinWidth, int aMinHeigh
}
void POINT_EDITOR::editArcEndpointKeepCenter( DRAWSEGMENT* aArc, VECTOR2I aCenter, VECTOR2I aStart,
VECTOR2I aMid, VECTOR2I aEnd,
const VECTOR2I aCursor ) const
{
bool clockwise;
bool movingStart;
VECTOR2I *p1, *p2;
VECTOR2I target;
// p1 does not move, p2 does.
if( aStart != aArc->GetArcStart() )
{
p1 = &aEnd;
p2 = &aStart;
movingStart = true;
}
else
{
p1 = &aStart;
p2 = &aEnd;
movingStart = false;
}
target = *p2 - aCenter;
double sqRadius = ( *p1 - aCenter ).SquaredEuclideanNorm();
*p1 = *p1 - aCenter;
*p2 = *p2 - aCenter;
// Circle : x^2 + y^2 = R ^ 2
// In this coordinate system, the angular position of the cursor is (r, theta)
// The line coming from the center of the circle is y = start.y / start.x * x
// The intersection fulfills : x^2 = R^2 / ( 1 + ( start.y / start.x ) ^ 2 )
if( target.x == 0 )
{
p2->x = 0;
p2->y = ( target.y > 0 ) ? sqrt( sqRadius ) : -sqrt( sqRadius );
}
else
{
double tan = target.y / static_cast<double>( target.x );
// The divider is always greater than 1 ( cannot be 0 )
double tmp = sqrt( sqRadius / ( 1.0 + tan * tan ) );
// Move to the correct quadrant
tmp = target.x > 0 ? tmp : -tmp;
p2->y = target.y / static_cast<double>( target.x ) * tmp;
p2->x = tmp;
}
*p1 = *p1 + aCenter;
*p2 = *p2 + aCenter;
clockwise = aArc->GetAngle() > 0;
VECTOR2D startLine = aStart - aCenter;
VECTOR2D endLine = aEnd - aCenter;
double newAngle = RAD2DECIDEG( endLine.Angle() - startLine.Angle() );
if( clockwise && newAngle < 0.0 )
newAngle += 3600.0;
else if( !clockwise && newAngle > 0.0 )
newAngle -= 3600.0;
aArc->SetAngle( newAngle );
aArc->SetCenter( wxPoint( aCenter.x, aCenter.y ) );
if( movingStart )
{
aArc->SetArcStart( wxPoint( aStart.x, aStart.y ) );
// Set angle computes the end point, so re-force it now.
aArc->SetArcEnd( wxPoint( aEnd.x, aEnd.y ) );
}
else
{
aArc->SetArcEnd( wxPoint( aEnd.x, aEnd.y ) );
}
}
void POINT_EDITOR::editArcMidKeepCenter( DRAWSEGMENT* aArc, VECTOR2I aCenter, VECTOR2I aStart,
VECTOR2I aMid, VECTOR2I aEnd, const VECTOR2I aCursor ) const
{
// Now, update the edit point position
// Express the point in a cercle-centered coordinate system.
aStart = aStart - aCenter;
aEnd = aEnd - aCenter;
double sqRadius = ( aCursor - aCenter ).SquaredEuclideanNorm();
// Special case, because the tangent would lead to +/- infinity
if( aStart.x == 0 )
{
aStart.y = aCursor.y > 0 ? sqrt( sqRadius ) : -sqrt( sqRadius );
}
else
{
// Circle : x^2 + y^2 = R ^ 2
// In this coordinate system, the angular position of the cursor is (r, theta)
// The line coming from the center of the circle is y = start.y / start.x * x
// The intersection fulfills : x^2 = R^2 / ( 1 + ( start.y / start.x ) ^ 2 )
double tan = aStart.y / static_cast<double>( aStart.x );
double tmp = sqrt( sqRadius / ( 1.0 + tan * tan ) );
// Move to the correct quadrant
tmp = aStart.x > 0 ? tmp : -tmp;
aStart.y = aStart.y / static_cast<double>( aStart.x ) * tmp;
aStart.x = tmp;
}
// Special case, because the tangent would lead to +/- infinity
if( aEnd.x == 0 )
{
aEnd.y = aMid.y > 0 ? sqrt( sqRadius ) : -sqrt( sqRadius );
}
else
{
// Circle : x^2 + y^2 = R ^ 2
// In this coordinate system, the angular position of the cursor is (r, theta)
// The line coming from the center of the circle is y = start.y / start.x * x
// The intersection fulfills : x^2 = R^2 / ( 1 + ( start.y / start.x ) ^ 2 )
double tan = aEnd.y / static_cast<double>( aEnd.x );
double tmp = sqrt( sqRadius / ( 1.0 + tan * tan ) );
// Move to the correct quadrant
tmp = aEnd.x > 0 ? tmp : -tmp;
aEnd.y = aEnd.y / static_cast<double>( aEnd.x ) * tmp;
aEnd.x = tmp;
}
aStart = aStart + aCenter;
aEnd = aEnd + aCenter;
aArc->SetArcStart( wxPoint( aStart.x, aStart.y ) );
aArc->SetArcEnd( wxPoint( aEnd.x, aEnd.y ) );
}
void POINT_EDITOR::editArcMidKeepEnpoints( DRAWSEGMENT* aArc, VECTOR2I aCenter, VECTOR2I aStart,
VECTOR2I aMid, VECTOR2I aEnd, const VECTOR2I aCursor ) const
{
bool clockwise;
VECTOR2I oldCenter = aArc->GetCenter();
// This allows the user to go on the sides of the arc
aMid = aCursor;
// Find the new center
aCenter = GetArcCenter( aStart, aMid, aEnd );
aArc->SetCenter( wxPoint( aCenter.x, aCenter.y ) );
// Check if the new arc is CW or CCW
VECTOR2D startLine = aStart - aCenter;
VECTOR2D endLine = aEnd - aCenter;
double newAngle = RAD2DECIDEG( endLine.Angle() - startLine.Angle() );
VECTOR2D v1, v2;
v1 = aStart - aMid;
v2 = aEnd - aMid;
double theta = RAD2DECIDEG( v1.Angle() );
RotatePoint( &( v1.x ), &( v1.y ), theta );
RotatePoint( &( v2.x ), &( v2.y ), theta );
clockwise = ( ( v1.Angle() - v2.Angle() ) > 0 );
// Normalize the angle
if( clockwise && newAngle < 0.0 )
newAngle += 3600.0;
else if( !clockwise && newAngle > 0.0 )
newAngle -= 3600.0;
// Accuracy test
// First, get the angle
VECTOR2I endTest = aStart;
RotatePoint( &( endTest.x ), &( endTest.y ), aCenter.x, aCenter.y, -newAngle );
double distance = ( endTest - aEnd ).SquaredEuclideanNorm();
if( distance > ADVANCED_CFG::GetCfg().m_drawArcAccuracy )
{
// Cancel Everything
// If the accuracy is low, we can't draw precisely the arc.
// It may happen when the radius is *high*
aArc->SetCenter( wxPoint( oldCenter.x, oldCenter.y ) );
}
else
{
aArc->SetAngle( newAngle );
aArc->SetArcEnd( wxPoint( aEnd.x, aEnd.y ) );
}
// Now, update the edit point position
// Express the point in a cercle-centered coordinate system.
aMid = aCursor - aCenter;
double sqRadius = ( aEnd - aCenter ).SquaredEuclideanNorm();
// Special case, because the tangent would lead to +/- infinity
if( aMid.x == 0 )
{
aMid.y = aMid.y > 0 ? sqrt( sqRadius ) : -sqrt( sqRadius );
}
else
{
// Circle : x^2 + y^2 = R ^ 2
// In this coordinate system, the angular position of the cursor is (r, theta)
// The line coming from the center of the circle is y = start.y / start.x * x
// The intersection fulfills : x^2 = R^2 / ( 1 + ( start.y / start.x ) ^ 2 )
double tan = aMid.y / static_cast<double>( aMid.x );
double tmp = sqrt( sqRadius / ( 1.0 + tan * tan ) );
// Move to the correct quadrant
tmp = aMid.x > 0 ? tmp : -tmp;
aMid.y = aMid.y / static_cast<double>( aMid.x ) * tmp;
aMid.x = tmp;
}
}
void POINT_EDITOR::updateItem() const
{
EDA_ITEM* item = m_editPoints->GetParent();
@ -631,241 +1023,34 @@ void POINT_EDITOR::updateItem() const
VECTOR2I start = m_editPoints->Point( ARC_START ).GetPosition();
VECTOR2I end = m_editPoints->Point( ARC_END ).GetPosition();
if( center != segment->GetCenter() )
const VECTOR2I& cursorPos = getViewControls()->GetCursorPosition();
if( isModified( m_editPoints->Point( ARC_CENTER ) ) )
{
wxPoint moveVector = wxPoint( center.x, center.y ) - segment->GetCenter();
segment->Move( moveVector );
m_editPoints->Point( ARC_START ).SetPosition( segment->GetArcStart() );
m_editPoints->Point( ARC_END ).SetPosition( segment->GetArcEnd() );
m_editPoints->Point( ARC_MID ).SetPosition( segment->GetArcMid() );
}
else
else if( isModified( m_editPoints->Point( ARC_MID ) ) )
{
const VECTOR2I& cursorPos = getViewControls()->GetCursorPosition();
VECTOR2I oldCenter = segment->GetCenter();
double newAngle;
bool clockwise;
if( mid != segment->GetArcMid() )
if( m_altEditMethod )
{
// This allows the user to go on the sides of the arc
mid = cursorPos;
// Find the new center
center = GetArcCenter( start, mid, end );
segment->SetCenter( wxPoint( center.x, center.y ) );
m_editPoints->Point( ARC_CENTER ).SetPosition( center );
newAngle = GetArcAngle( start, mid, end );
// Accuracy test
// First, get the angle
VECTOR2I endTest = start;
RotatePoint( &( endTest.x ), &( endTest.y ), center.x, center.y, -newAngle );
double distance = ( endTest - end ).SquaredEuclideanNorm();
if( distance > ADVANCED_CFG::GetCfg().m_drawArcAccuracy )
{
// Cancel Everything
// If the accuracy is low, we can't draw precisely the arc.
// It may happen when the radius is *high*
segment->SetCenter( wxPoint( oldCenter.x, oldCenter.y ) );
}
else
{
segment->SetAngle( newAngle );
segment->SetArcEnd( wxPoint( end.x, end.y ) );
}
// Now, update the edit point position
// Express the point in a cercle-centered coordinate system.
mid = cursorPos - center;
double sqRadius = ( end - center ).SquaredEuclideanNorm();
// Special case, because the tangent would lead to +/- infinity
if( mid.x == 0 )
{
mid.y = mid.y > 0 ? sqrt( sqRadius ) : -sqrt( sqRadius );
}
else
{
// Circle : x^2 + y^2 = R ^ 2
// In this coordinate system, the angular position of the cursor is (r, theta)
// The line coming from the center of the circle is y = start.y / start.x * x
// The intersection fulfills : x^2 = R^2 / ( 1 + ( start.y / start.x ) ^ 2 )
double tmp = sqrt( sqRadius /
( ( 1.0 + mid.y / static_cast<double>( mid.x ) * mid.y
/ static_cast<double>( mid.x ) ) ) );
// Move to the correct quadrant
tmp = mid.x > 0 ? tmp : -tmp;
mid.y = mid.y / static_cast<double>( mid.x ) * tmp;
mid.x = tmp;
}
// Go back to the main coordinate system
mid = mid + center;
m_editPoints->Point( ARC_MID ).SetPosition( mid );
editArcMidKeepCenter( segment, center, start, mid, end, cursorPos );
}
else if( ( start != segment->GetArcStart() ) || ( end != segment->GetArcEnd() ) )
else
{
VECTOR2D startLine = start - center;
VECTOR2D endLine = end - center;
newAngle = RAD2DECIDEG( endLine.Angle() - startLine.Angle() );
VECTOR2I *p1, *p2, *p3;
// p1 does not move, p2 does.
bool movingStart;
bool arcValid = true;
if( start != segment->GetArcStart() )
{
start = cursorPos;
p1 = &end;
p2 = &start;
p3 = &mid;
movingStart = true;
}
else
{
end = cursorPos;
p1 = &start;
p2 = &end;
p3 = &mid;
movingStart = false;
}
VECTOR2D v1, v2, v3, v4;
// Move the coordinate system
v1 = *p1 - center;
v2 = *p2 - center;
v3 = *p3 - center;
VECTOR2D u1, u2, u3;
u1 = v1 / v1.EuclideanNorm();
u2 = v3 - ( u1.x * v3.x + u1.y * v3.y ) * u1;
u2 = u2 / u2.EuclideanNorm();
// [ u1, u3 ] is a base centered on the circle with:
// u1 : unit vector toward the point that does not move
// u2 : unit vector toward the mid point.
// Get vectors v1, and v2 in that coordinate system.
double det = u1.x * u2.y - u2.x * u1.y;
double tmpx = v1.x * u2.y - v1.y * u2.x;
double tmpy = -v1.x * u1.y + v1.y * u1.x;
v1.x = tmpx;
v1.y = tmpy;
v1 = v1 / det;
tmpx = v2.x * u2.y - v2.y * u2.x;
tmpy = -v2.x * u1.y + v2.y * u1.x;
v2.x = tmpx;
v2.y = tmpy;
v2 = v2 / det;
double R = v1.EuclideanNorm();
bool transformCircle = false;
/* p2
* X***
* ** <---- This is the arc
* y ^ **
* | R *
* | <-----------> *
* x------x------>--------x p1
* C' <----> C x
* delta
*
* p1 does not move, and the tangent at p1 remains the same.
* => The new center, C', will be on the C-p1 axis.
* p2 moves
*
* The radius of the new circle is delta + R
*
* || C' p2 || = || C' P1 ||
* is the same as :
* ( delta + p2.x ) ^ 2 + p2.y ^ 2 = ( R + delta ) ^ 2
*
* delta = ( R^2 - p2.x ^ 2 - p2.y ^2 ) / ( 2 * p2.x - 2 * R )
*
* We can use this equation for any point p2 with p2.x < R
*/
if( v2.x == R )
{
// Straight line, do nothing
}
else
{
if( v2.x > R )
{
// If we need to invert the curvature.
// We modify the input so we can use the same equation
transformCircle = true;
v2.x = 2 * R - v2.x;
}
// We can keep the tangent constraint.
double delta = ( R * R - v2.x * v2.x - v2.y * v2.y ) / ( 2 * v2.x - 2 * R );
// This is just to limit the radius, so nothing overflows later when drawing.
if( abs( v2.y / ( R - v2.x ) )
> ADVANCED_CFG::GetCfg().m_drawArcCenterMaxAngle )
{
arcValid = false;
}
// v4 is the new center
v4 = ( !transformCircle ) ? VECTOR2D( -delta, 0 ) :
VECTOR2D( 2 * R + delta, 0 );
clockwise = segment->GetAngle() > 0;
if( transformCircle )
clockwise = !clockwise;
tmpx = v4.x * u1.x + v4.y * u2.x;
tmpy = v4.x * u1.y + v4.y * u2.y;
v4.x = tmpx;
v4.y = tmpy;
center = v4 + center;
startLine = start - center;
endLine = end - center;
newAngle = RAD2DECIDEG( endLine.Angle() - startLine.Angle() );
if( clockwise && newAngle < 0.0 )
newAngle += 3600.0;
else if( !clockwise && newAngle > 0.0 )
newAngle -= 3600.0;
if( arcValid )
{
segment->SetAngle( newAngle );
segment->SetCenter( wxPoint( center.x, center.y ) );
if( movingStart )
{
segment->SetArcStart( wxPoint( start.x, start.y ) );
// Set angle computes the end point, so re-force it now.
segment->SetArcEnd( wxPoint( end.x, end.y ) );
m_editPoints->Point( ARC_START ).SetPosition( start );
}
else
{
segment->SetArcEnd( wxPoint( end.x, end.y ) );
m_editPoints->Point( ARC_END ).SetPosition( end );
}
m_editPoints->Point( ARC_CENTER ).SetPosition( center );
}
}
editArcMidKeepEnpoints( segment, center, start, mid, end, cursorPos );
}
}
else if( isModified( m_editPoints->Point( ARC_START ) )
|| isModified( m_editPoints->Point( ARC_END ) ) )
{
if( m_altEditMethod )
{
editArcEndpointKeepCenter( segment, center, start, mid, end, cursorPos );
}
else
{
editArcEndpointKeepTangent( segment, center, start, mid, end, cursorPos );
}
}
}
@ -982,17 +1167,17 @@ void POINT_EDITOR::updateItem() const
if( isModified( m_editPoints->Point( RECT_TOP_LEFT ) )
|| isModified( m_editPoints->Point( RECT_BOT_RIGHT ) ) )
{
dist[0] = center.x - topLeft.x;
dist[1] = center.y - topLeft.y;
dist[2] = botRight.x - center.x;
dist[3] = botRight.y - center.y;
dist[0] = center.x - m_editPoints->Point( RECT_TOP_LEFT ).GetPosition().x;
dist[1] = center.y - m_editPoints->Point( RECT_TOP_LEFT ).GetPosition().y;
dist[2] = m_editPoints->Point( RECT_BOT_RIGHT ).GetPosition().x - center.x;
dist[3] = m_editPoints->Point( RECT_BOT_RIGHT ).GetPosition().y - center.y;
}
else
{
dist[0] = center.x - botLeft.x;
dist[1] = center.y - topRight.y;
dist[2] = topRight.x - center.x;
dist[3] = botLeft.y - center.y;
dist[0] = center.x - m_editPoints->Point( RECT_BOT_LEFT ).GetPosition().x;
dist[1] = center.y - m_editPoints->Point( RECT_TOP_RIGHT ).GetPosition().y;
dist[2] = m_editPoints->Point( RECT_TOP_RIGHT ).GetPosition().x - center.x;
dist[3] = m_editPoints->Point( RECT_BOT_LEFT ).GetPosition().y - center.y;
}
wxSize padSize( dist[0] + dist[2], dist[1] + dist[3] );
@ -1015,17 +1200,17 @@ void POINT_EDITOR::updateItem() const
if( isModified( m_editPoints->Point( RECT_TOP_LEFT ) )
|| isModified( m_editPoints->Point( RECT_BOT_RIGHT ) ) )
{
left = topLeft.x;
top = topLeft.y;
right = botRight.x;
bottom = botRight.y;
left = m_editPoints->Point( RECT_TOP_LEFT ).GetPosition().x;
top = m_editPoints->Point( RECT_TOP_LEFT ).GetPosition().y;
right = m_editPoints->Point( RECT_BOT_RIGHT ).GetPosition().x;
bottom = m_editPoints->Point( RECT_BOT_RIGHT ).GetPosition().y;
}
else
{
left = botLeft.x;
top = topRight.y;
right = topRight.x;
bottom = botLeft.y;
left = m_editPoints->Point( RECT_BOT_LEFT ).GetPosition().x;
top = m_editPoints->Point( RECT_TOP_RIGHT ).GetPosition().y;
right = m_editPoints->Point( RECT_TOP_RIGHT ).GetPosition().x;
bottom = m_editPoints->Point( RECT_BOT_LEFT ).GetPosition().y;
}
wxSize padSize( abs( right - left ), abs( bottom - top ) );
@ -1754,6 +1939,11 @@ int POINT_EDITOR::modifiedSelection( const TOOL_EVENT& aEvent )
return 0;
}
int POINT_EDITOR::changeEditMethod( const TOOL_EVENT& aEvent )
{
m_altEditMethod = !m_altEditMethod;
return 0;
}
void POINT_EDITOR::setTransitions()
{
@ -1763,4 +1953,5 @@ void POINT_EDITOR::setTransitions()
Go( &POINT_EDITOR::modifiedSelection, EVENTS::SelectedItemsModified );
Go( &POINT_EDITOR::OnSelectionChange, EVENTS::SelectedEvent );
Go( &POINT_EDITOR::OnSelectionChange, EVENTS::UnselectedEvent );
Go( &POINT_EDITOR::changeEditMethod, ACTIONS::changeEditMethod.MakeEvent() );
}

View File

@ -89,6 +89,9 @@ private:
// Flag indicating whether the selected zone needs to be refilled
bool m_refill;
// Flag indicating whether the alternative edit method is enabled.
bool m_altEditMethod;
std::unique_ptr<STATUS_TEXT_POPUP> m_statusPopup;
///> Updates item's points with edit points.
@ -149,6 +152,33 @@ private:
int addCorner( const TOOL_EVENT& aEvent );
int removeCorner( const TOOL_EVENT& aEvent );
int modifiedSelection( const TOOL_EVENT& aEvent );
/** Move an end point of the arc, while keeping the tangent at the other endpoint.
*
*/
void editArcEndpointKeepTangent( DRAWSEGMENT* aArc, VECTOR2I aCenter, VECTOR2I aStart,
VECTOR2I aMid, VECTOR2I aEnd, const VECTOR2I aCursor ) const;
/** Move an end point of the arc, while keeping radius, and the other point position.
*
*/
void editArcEndpointKeepCenter( DRAWSEGMENT* aArc, VECTOR2I aCenter, VECTOR2I aStart,
VECTOR2I aMid, VECTOR2I aEnd, const VECTOR2I aCursor ) const;
/** Move the mid point of the arc, while keeping the two endpoints.
*
*/
void editArcMidKeepEnpoints( DRAWSEGMENT* aArc, VECTOR2I aCenter, VECTOR2I aStart,
VECTOR2I aMid, VECTOR2I aEnd, const VECTOR2I aCursor ) const;
/** Move the mid point of the arc, while keeping the angle.
*
*/
void editArcMidKeepCenter( DRAWSEGMENT* aArc, VECTOR2I aCenter, VECTOR2I aStart, VECTOR2I aMid,
VECTOR2I aEnd, const VECTOR2I aCursor ) const;
///> Change the edit method to an alternative method ( currently, arcs only )
int changeEditMethod( const TOOL_EVENT& aEvent );
};
#endif