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:
parent
4a9f82109a
commit
24bf838392
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
Loading…
Reference in New Issue