Go back to previous arc midpoint editing routine.

It avoids erratic behaviour near the chord or end-points.

Fixes https://gitlab.com/kicad/code/kicad/issues/7135
This commit is contained in:
Jeff Young 2021-01-19 23:07:31 +00:00
parent 23b11f0090
commit 0ded846130
4 changed files with 59 additions and 94 deletions

View File

@ -441,6 +441,29 @@ double PCB_SHAPE::GetArcAngleEnd() const
}
void PCB_SHAPE::SetArcGeometry( const wxPoint& aStart, const wxPoint& aMid, const wxPoint& aEnd )
{
SetArcStart( aStart );
SetArcEnd( aEnd );
// Sadly we currently store center and angle rather than mid. So we have to calculate
// those.
wxPoint center = GetArcCenter( aStart, aMid, aEnd );
VECTOR2D startLine = aStart - center;
VECTOR2D endLine = aEnd - center;
bool clockwise = GetAngle() > 0;
double angle = RAD2DECIDEG( endLine.Angle() - startLine.Angle() );
if( clockwise && angle < 0.0 )
angle += 3600.0;
else if( !clockwise && angle > 0.0 )
angle -= 3600.0;
SetAngle( angle, false );
SetCenter( center );
}
void PCB_SHAPE::SetAngle( double aAngle, bool aUpdateEnd )
{
// m_Angle must be >= -360 and <= +360 degrees

View File

@ -227,6 +227,14 @@ public:
*/
void SetCenter( const wxPoint& aCenterPoint ) { m_start = aCenterPoint; }
/**
* Set the three controlling points for an arc.
*
* NB: these are NOT what's currently stored, so we have to do some calculations behind
* the scenes. However, they are what SHOULD be stored.
*/
void SetArcGeometry( const wxPoint& aStart, const wxPoint& aMid, const wxPoint& aEnd );
const wxPoint GetFocusPosition() const override
{
return GetCenter();

View File

@ -209,15 +209,6 @@ std::shared_ptr<EDIT_POINTS> PCB_POINT_EDITOR::makePoints( EDA_ITEM* aItem )
points->AddPoint( shape->GetArcMid() );
points->AddPoint( shape->GetArcEnd() );
// Set constraints
// Arc end has to stay at the same radius as the start
points->Point( ARC_END ).SetConstraint( new EC_CIRCLE( points->Point( ARC_END ),
points->Point( ARC_CENTER ),
points->Point( ARC_START ) ) );
points->Point( ARC_MID ).SetConstraint( new EC_LINE( points->Point( ARC_MID ),
points->Point( ARC_CENTER ) ) );
points->Point( ARC_MID ).SetGridConstraint( IGNORE_GRID );
points->Point( ARC_START ).SetGridConstraint( SNAP_TO_GRID );
points->Point( ARC_CENTER ).SetGridConstraint( SNAP_BY_GRID );
@ -998,83 +989,20 @@ void PCB_POINT_EDITOR::editArcMidKeepCenter( PCB_SHAPE* aArc, VECTOR2I aCenter,
}
void PCB_POINT_EDITOR::editArcMidKeepEndpoints( PCB_SHAPE* aArc, VECTOR2I aCenter, VECTOR2I aStart,
VECTOR2I aMid, VECTOR2I aEnd,
void PCB_POINT_EDITOR::editArcMidKeepEndpoints( PCB_SHAPE* aArc, VECTOR2I aStart, VECTOR2I aEnd,
const VECTOR2I aCursor ) const
{
bool clockwise;
VECTOR2I oldCenter = aArc->GetCenter();
// Let 'm' be the middle point of the chord between the start and end points
VECTOR2I m = ( aStart + aEnd ) / 2;
// Legal midpoints lie on a vector starting just off the chord midpoint and extending out
// past the existing midpoint. We do not allow arc inflection while point editing.
const int JUST_OFF = ( aStart - aEnd ).EuclideanNorm() / 100;
VECTOR2I v = (VECTOR2I) aArc->GetArcMid() - m;
SEG legal( m + v.Resize( JUST_OFF ), m + v.Resize( INT_MAX / 2 ) );
VECTOR2I mid = legal.NearestPoint( aCursor );
// 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 );
// 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 );
}
else
{
aArc->SetAngle( newAngle, false );
}
// 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;
}
aArc->SetArcGeometry( (wxPoint) aStart, (wxPoint) mid, (wxPoint) aEnd );
}
@ -1096,12 +1024,15 @@ void PCB_POINT_EDITOR::updateItem() const
{
case S_SEGMENT:
if( isModified( m_editPoints->Point( SEG_START ) ) )
{
shape->SetStart( wxPoint( m_editPoints->Point( SEG_START ).GetPosition().x,
m_editPoints->Point( SEG_START ).GetPosition().y ) );
}
else if( isModified( m_editPoints->Point( SEG_END ) ) )
{
shape->SetEnd( wxPoint( m_editPoints->Point( SEG_END ).GetPosition().x,
m_editPoints->Point( SEG_END ).GetPosition().y ) );
}
break;
@ -1169,7 +1100,7 @@ void PCB_POINT_EDITOR::updateItem() const
if( m_altEditMethod )
editArcMidKeepCenter( shape, center, start, mid, end, cursorPos );
else
editArcMidKeepEndpoints( shape, center, start, mid, end, cursorPos );
editArcMidKeepEndpoints( shape, start, end, cursorPos );
}
else if( isModified( m_editPoints->Point( ARC_START ) )
|| isModified( m_editPoints->Point( ARC_END ) ) )
@ -1970,10 +1901,13 @@ bool PCB_POINT_EDITOR::removeCornerCondition( const SELECTION& )
if( !item )
return false;
if( !( item->Type() == PCB_ZONE_T || item->Type() == PCB_FP_ZONE_T
|| ( ( item->Type() == PCB_FP_SHAPE_T || item->Type() == PCB_SHAPE_T ) &&
static_cast<PCB_SHAPE*>( item )->GetShape() == S_POLYGON ) ) )
if( !( item->Type() == PCB_ZONE_T
|| item->Type() == PCB_FP_ZONE_T
|| ( ( item->Type() == PCB_FP_SHAPE_T || item->Type() == PCB_SHAPE_T )
&& static_cast<PCB_SHAPE*>( item )->GetShape() == S_POLYGON ) ) )
{
return false;
}
SHAPE_POLY_SET *polyset;
@ -2044,8 +1978,8 @@ int PCB_POINT_EDITOR::addCorner( const TOOL_EVENT& aEvent )
// and therefore break this segment into two segments
// Object to iterate through the corners of the outlines (main contour and its holes)
SHAPE_POLY_SET::ITERATOR iterator = zoneOutline->Iterate( 0,
zoneOutline->OutlineCount()-1, /* IterateHoles */ true );
SHAPE_POLY_SET::ITERATOR iterator = zoneOutline->Iterate( 0, zoneOutline->OutlineCount()-1,
/* IterateHoles */ true );
int curr_idx = 0;
// Iterate through all the corners of the outlines and search the best segment
@ -2155,10 +2089,10 @@ int PCB_POINT_EDITOR::removeCorner( const TOOL_EVENT& aEvent )
}
else if( item->Type() == PCB_FP_SHAPE_T || item->Type() == PCB_SHAPE_T )
{
auto ds = static_cast<PCB_SHAPE*>( item );
PCB_SHAPE* shape = static_cast<PCB_SHAPE*>( item );
if( ds->GetShape() == S_POLYGON )
polygon = &ds->GetPolyShape();
if( shape->GetShape() == S_POLYGON )
polygon = &shape->GetPolyShape();
}
if( !polygon )

View File

@ -146,8 +146,8 @@ private:
/**
* Move the mid point of the arc, while keeping the two endpoints.
*/
void editArcMidKeepEndpoints( PCB_SHAPE* aArc, VECTOR2I aCenter, VECTOR2I aStart,
VECTOR2I aMid, VECTOR2I aEnd, const VECTOR2I aCursor ) const;
void editArcMidKeepEndpoints( PCB_SHAPE* aArc, VECTOR2I aStart, VECTOR2I aEnd,
const VECTOR2I aCursor ) const;
/**
* Move the mid point of the arc, while keeping the angle.