pcbnew: Allow arc editing via midpoint

This permits changing the radius of the arc by adjusting the center
point of the arc.  The arc end points are fixed in this operation.
This commit is contained in:
Seth Hillbrand 2018-12-08 07:26:47 -08:00
parent 4a9f82109a
commit 24bf838392
5 changed files with 116 additions and 8 deletions

View File

@ -344,3 +344,67 @@ void RotatePoint( double* pX, double* pY, double angle )
*pY = fpy;
}
}
const VECTOR2I GetArcCenter( const VECTOR2I& aStart, const VECTOR2I& aMid, const VECTOR2I& aEnd )
{
VECTOR2I center;
double yDelta_21 = aMid.y - aStart.y;
double xDelta_21 = aMid.x - aStart.x;
double yDelta_32 = aEnd.y - aMid.y;
double xDelta_32 = aEnd.x - aMid.x;
// This is a special case for aMid as the half-way point when aSlope = 0 and bSlope = inf
// or the other way around. In that case, the center lies in a straight line between
// aStart and aEnd
if( ( ( xDelta_21 == 0.0 ) && ( yDelta_32 == 0.0 ) ) ||
( ( yDelta_21 == 0.0 ) && ( xDelta_32 == 0.0 ) ) )
{
center.x = KiROUND( ( aStart.x + aEnd.x ) / 2.0 );
center.y = KiROUND( ( aStart.y + aEnd.y ) / 2.0 );
return center;
}
// Prevent div=0 errors
if( xDelta_21 == 0.0 )
xDelta_21 = std::numeric_limits<double>::epsilon();
if( xDelta_32 == 0.0 )
xDelta_32 = -std::numeric_limits<double>::epsilon();
double aSlope = yDelta_21 / xDelta_21;
double bSlope = yDelta_32 / xDelta_32;
// If the points are colinear, the center is at infinity, so offset
// the slope by a minimal amount
// Warning: This will induce a small error in the center location
if( yDelta_32 * xDelta_21 == yDelta_21 * xDelta_32 )
{
aSlope += std::numeric_limits<double>::epsilon();
bSlope -= std::numeric_limits<double>::epsilon();
}
if( aSlope == 0.0 )
aSlope = std::numeric_limits<double>::epsilon();
if( bSlope == 0.0 )
bSlope = -std::numeric_limits<double>::epsilon();
double result = ( aSlope * bSlope * ( aStart.y - aEnd.y ) +
bSlope * ( aStart.x + aMid.x ) -
aSlope * ( aMid.x + aEnd.x ) ) / ( 2 * ( bSlope - aSlope ) );
center.x = KiROUND( Clamp<double>( double( std::numeric_limits<int>::min() / 2 ),
result,
double( std::numeric_limits<int>::max() / 2 ) ) );
result = ( ( ( aStart.x + aMid.x ) / 2.0 - center.x ) / aSlope +
( aStart.y + aMid.y ) / 2.0 );
center.y = KiROUND( Clamp<double>( double( std::numeric_limits<int>::min() / 2 ),
result,
double( std::numeric_limits<int>::max() / 2 ) ) );
return center;
}

View File

@ -96,6 +96,15 @@ void RotatePoint( double *pX, double *pY, double angle );
void RotatePoint( double *pX, double *pY, double cx, double cy, double angle );
/**
* Determine the center of an arc/circle, given three points on its circumference
* @param aStart The starting point of the circle (equivalent to aEnd)
* @param aMid The point on the arc, half-way between aStart and aEnd
* @param aEnd The ending point of the circle (equivalent to aStart)
* @return The center of the circle
*/
const VECTOR2I GetArcCenter( const VECTOR2I& aStart, const VECTOR2I& aMid, const VECTOR2I& aEnd );
/* Return the arc tangent of 0.1 degrees coord vector dx, dy
* between -1800 and 1800
* Equivalent to atan2 (but faster for calculations if

View File

@ -236,7 +236,7 @@ const wxPoint DRAWSEGMENT::GetCenter() const
const wxPoint DRAWSEGMENT::GetArcEnd() const
{
wxPoint endPoint; // start of arc
wxPoint endPoint( m_End ); // start of arc
switch( m_Shape )
{
@ -255,6 +255,27 @@ const wxPoint DRAWSEGMENT::GetArcEnd() const
return endPoint; // after rotation, the end of the arc.
}
const wxPoint DRAWSEGMENT::GetArcMid() const
{
wxPoint endPoint( m_End );
switch( m_Shape )
{
case S_ARC:
// rotate the starting point of the arc, given by m_End, through half
// the angle m_Angle to get the middle of the arc.
// m_Start is the arc centre
endPoint = m_End; // m_End = start point of arc
RotatePoint( &endPoint, m_Start, -m_Angle / 2.0 );
break;
default:
break;
}
return endPoint; // after rotation, the end of the arc.
}
double DRAWSEGMENT::GetArcAngleStart() const
{
// due to the Y axis orient atan2 needs - y value

View File

@ -131,6 +131,7 @@ public:
const wxPoint GetCenter() const override;
const wxPoint& GetArcStart() const { return m_End; }
const wxPoint GetArcEnd() const;
const wxPoint GetArcMid() const;
/**
* function GetArcAngleStart()

View File

@ -68,7 +68,7 @@ enum SEG_POINTS
enum ARC_POINTS
{
ARC_CENTER, ARC_START, ARC_END
ARC_CENTER, ARC_START, ARC_MID, ARC_END
};
enum CIRCLE_POINTS
@ -163,6 +163,7 @@ public:
case S_ARC:
points->AddPoint( segment->GetCenter() );
points->AddPoint( segment->GetArcStart() );
points->AddPoint( segment->GetArcMid() );
points->AddPoint( segment->GetArcEnd() );
// Set constraints
@ -170,6 +171,9 @@ public:
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 ) ) );
break;
case S_CIRCLE:
@ -462,9 +466,10 @@ void POINT_EDITOR::updateItem() const
case S_ARC:
{
const VECTOR2I& center = m_editPoints->Point( ARC_CENTER ).GetPosition();
const VECTOR2I& start = m_editPoints->Point( ARC_START ).GetPosition();
const VECTOR2I& end = m_editPoints->Point( ARC_END ).GetPosition();
VECTOR2I center = m_editPoints->Point( ARC_CENTER ).GetPosition();
VECTOR2I mid = m_editPoints->Point( ARC_MID ).GetPosition();
VECTOR2I start = m_editPoints->Point( ARC_START ).GetPosition();
VECTOR2I end = m_editPoints->Point( ARC_END ).GetPosition();
if( center != segment->GetCenter() )
{
@ -473,14 +478,21 @@ void POINT_EDITOR::updateItem() const
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
{
if( mid != segment->GetArcMid() )
{
center = GetArcCenter( start, mid, end );
segment->SetCenter( wxPoint( center.x, center.y ) );
m_editPoints->Point( ARC_CENTER ).SetPosition( center );
}
segment->SetArcStart( wxPoint( start.x, start.y ) );
VECTOR2D startLine = start - center;
VECTOR2I endLine = end - center;
VECTOR2D endLine = end - center;
double newAngle = RAD2DECIDEG( endLine.Angle() - startLine.Angle() );
// Adjust the new angle to (counter)clockwise setting
@ -710,7 +722,8 @@ void POINT_EDITOR::updatePoints()
case S_ARC:
m_editPoints->Point( ARC_CENTER ).SetPosition( segment->GetCenter() );
m_editPoints->Point( ARC_START).SetPosition( segment->GetArcStart() );
m_editPoints->Point( ARC_START ).SetPosition( segment->GetArcStart() );
m_editPoints->Point( ARC_MID ).SetPosition( segment->GetArcMid() );
m_editPoints->Point( ARC_END ).SetPosition( segment->GetArcEnd() );
break;