Pcbnew fix arc edition
This commit is contained in:
parent
abc64f228b
commit
133d082cc5
|
@ -91,6 +91,23 @@ static const wxChar ShowRouterDebugGraphics[] = wxT( "ShowRouterDebugGraphics" )
|
|||
*/
|
||||
static const wxChar CompactFileSave[] = wxT( "CompactSave" );
|
||||
|
||||
/**
|
||||
* For drawsegments - arcs.
|
||||
* Distance from an arc end point and the estimated end point,
|
||||
* when rotating from the start point to the end point.
|
||||
* 0 will not allow any approximate result, and the arc will not show.
|
||||
* Squared value for performances, in system unit.
|
||||
*/
|
||||
static const wxChar DrawArcAccuracy[] = wxT( "DrawArcAccuracy" );
|
||||
|
||||
/**
|
||||
* For drawsegments - arcs.
|
||||
* When drawing an arc, the angle ( center - start ) - ( start - end ),
|
||||
* can be limited to avoid extremely high radii.
|
||||
* The value is the tan( angle )
|
||||
*/
|
||||
static const wxChar DrawArcCenterStartEndMaxAngle[] = wxT( "DrawArcCenterStartEndMaxAngle" );
|
||||
|
||||
} // namespace KEYS
|
||||
|
||||
|
||||
|
@ -168,9 +185,11 @@ ADVANCED_CFG::ADVANCED_CFG()
|
|||
|
||||
// Init defaults - this is done in case the config doesn't exist,
|
||||
// then the values will remain as set here.
|
||||
m_realTimeConnectivity = true;
|
||||
m_coroutineStackSize = AC_STACK::default_stack;
|
||||
m_ShowRouterDebugGraphics = false;
|
||||
m_realTimeConnectivity = true;
|
||||
m_coroutineStackSize = AC_STACK::default_stack;
|
||||
m_ShowRouterDebugGraphics = false;
|
||||
m_drawArcAccuracy = 10.0;
|
||||
m_drawArcCenterStartEndMaxAngle = 50.0;
|
||||
|
||||
loadFromConfigFile();
|
||||
}
|
||||
|
@ -220,6 +239,12 @@ void ADVANCED_CFG::loadSettings( wxConfigBase& aCfg )
|
|||
configParams.push_back( new PARAM_CFG_BOOL( true, AC_KEYS::CompactFileSave,
|
||||
&m_CompactSave, false ) );
|
||||
|
||||
configParams.push_back( new PARAM_CFG_DOUBLE(
|
||||
true, AC_KEYS::DrawArcAccuracy, &m_drawArcAccuracy, 10.0, 0.0, 100000.0 ) );
|
||||
|
||||
configParams.push_back( new PARAM_CFG_DOUBLE( true, AC_KEYS::DrawArcCenterStartEndMaxAngle,
|
||||
&m_drawArcCenterStartEndMaxAngle, 50.0, 0.0, 100000.0 ) );
|
||||
|
||||
wxConfigLoadSetups( &aCfg, configParams );
|
||||
|
||||
for( auto param : configParams )
|
||||
|
|
|
@ -68,6 +68,20 @@ public:
|
|||
*/
|
||||
static const ADVANCED_CFG& GetCfg();
|
||||
|
||||
/**
|
||||
* For drawsegments - arcs.
|
||||
* Distance from an arc end point and the estimated end point,
|
||||
* when rotating from the start point to the end point.
|
||||
*/
|
||||
double m_drawArcAccuracy;
|
||||
|
||||
/**
|
||||
* For drawsegments - arcs.
|
||||
* When drawing an arc, the angle ( center - start ) - ( start - end )
|
||||
* can be limited to avoid extremely high radii.
|
||||
*/
|
||||
double m_drawArcCenterStartEndMaxAngle;
|
||||
|
||||
/**
|
||||
* Extra fill clearance for zone fills
|
||||
*/
|
||||
|
|
|
@ -115,6 +115,10 @@ void DRAWSEGMENT::Move( const wxPoint& aMoveVector )
|
|||
m_Poly.Move( VECTOR2I( aMoveVector ) );
|
||||
break;
|
||||
|
||||
case S_ARC:
|
||||
m_ThirdPoint += aMoveVector;
|
||||
break;
|
||||
|
||||
case S_CURVE:
|
||||
m_BezierC1 += aMoveVector;
|
||||
m_BezierC2 += aMoveVector;
|
||||
|
@ -352,11 +356,7 @@ wxPoint DRAWSEGMENT::GetArcEnd() const
|
|||
switch( m_Shape )
|
||||
{
|
||||
case S_ARC:
|
||||
// rotate the starting point of the arc, given by m_End, through the
|
||||
// angle m_Angle to get the ending point of the arc.
|
||||
// m_Start is the arc centre
|
||||
endPoint = m_End; // m_End = start point of arc
|
||||
RotatePoint( &endPoint, m_Start, -m_Angle );
|
||||
endPoint = m_ThirdPoint;
|
||||
break;
|
||||
|
||||
default:
|
||||
|
@ -403,11 +403,29 @@ double DRAWSEGMENT::GetArcAngleStart() const
|
|||
return angleStart;
|
||||
}
|
||||
|
||||
double DRAWSEGMENT::GetArcAngleEnd() const
|
||||
{
|
||||
// due to the Y axis orient atan2 needs - y value
|
||||
double angleStart = ArcTangente( GetArcEnd().y - GetCenter().y,
|
||||
GetArcEnd().x - GetCenter().x );
|
||||
|
||||
// Normalize it to 0 ... 360 deg, to avoid discontinuity for angles near 180 deg
|
||||
// because 180 deg and -180 are very near angles when ampping betewwen -180 ... 180 deg.
|
||||
// and this is not easy to handle in calculations
|
||||
NORMALIZE_ANGLE_POS( angleStart );
|
||||
|
||||
return angleStart;
|
||||
}
|
||||
|
||||
|
||||
void DRAWSEGMENT::SetAngle( double aAngle )
|
||||
{
|
||||
// Mark as depreciated.
|
||||
// m_Angle does not define the arc anymore
|
||||
// m_Angle must be >= -360 and <= +360 degrees
|
||||
m_Angle = NormalizeAngle360Max( aAngle );
|
||||
m_ThirdPoint = m_End;
|
||||
RotatePoint( &m_ThirdPoint, m_Start, -m_Angle );
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -51,6 +51,7 @@ protected:
|
|||
int m_Width; ///< thickness of lines ...
|
||||
wxPoint m_Start; ///< Line start point or Circle and Arc center
|
||||
wxPoint m_End; ///< Line end point or circle and arc start point
|
||||
wxPoint m_ThirdPoint; ///< Used only for Arcs: arc end point
|
||||
|
||||
STROKE_T m_Shape; ///< Shape: line, Circle, Arc
|
||||
int m_Type; ///< Used in complex associations ( Dimensions.. )
|
||||
|
@ -148,6 +149,17 @@ public:
|
|||
void SetEndY( int y ) { m_End.y = y; }
|
||||
void SetEndX( int x ) { m_End.x = x; }
|
||||
|
||||
/**
|
||||
* Function GetThirdPoint
|
||||
* returns the third point point of the graphic
|
||||
*/
|
||||
const wxPoint& GetThirdPoint() const { return m_ThirdPoint; }
|
||||
int GetThirdPointY() { return m_ThirdPoint.y; }
|
||||
int GetThirdPointX() { return m_ThirdPoint.x; }
|
||||
void SetThirdPoint( const wxPoint& aPoint ) { m_ThirdPoint = aPoint; }
|
||||
void SetThirdPointY( int y ) { m_ThirdPoint.y = y; }
|
||||
void SetThirdPointX( int x ) { m_ThirdPoint.x = x; }
|
||||
|
||||
// Some attributes are read only, since they are "calculated" from
|
||||
// m_Start, m_End, and m_Angle.
|
||||
// No Set...() function for these attributes.
|
||||
|
@ -164,6 +176,12 @@ public:
|
|||
*/
|
||||
double GetArcAngleStart() const;
|
||||
|
||||
/**
|
||||
* function GetArcAngleEnd()
|
||||
* @return the angle of the ending point of this arc, between 0 and 3600 in 0.1 deg
|
||||
*/
|
||||
double GetArcAngleEnd() const;
|
||||
|
||||
/**
|
||||
* Function GetRadius
|
||||
* returns the radius of this item
|
||||
|
@ -184,6 +202,13 @@ public:
|
|||
void SetArcStart( const wxPoint& aArcStartPoint )
|
||||
{ m_End = aArcStartPoint; }
|
||||
|
||||
/**
|
||||
* Initialize the start arc point. can be used for circles
|
||||
* to initialize one point of the cicumference
|
||||
*/
|
||||
void SetArcEnd( const wxPoint& aArcEndPoint )
|
||||
{ m_ThirdPoint = aArcEndPoint; }
|
||||
|
||||
/** For arcs and circles:
|
||||
*/
|
||||
void SetCenter( const wxPoint& aCenterPoint ) { m_Start = aCenterPoint; }
|
||||
|
|
|
@ -967,9 +967,9 @@ void PCB_PAINTER::draw( const DRAWSEGMENT* aSegment, int aLayer )
|
|||
|
||||
case S_ARC:
|
||||
m_gal->DrawArcSegment( start, aSegment->GetRadius(),
|
||||
DECIDEG2RAD( aSegment->GetArcAngleStart() ),
|
||||
DECIDEG2RAD( aSegment->GetArcAngleStart() + aSegment->GetAngle() ),
|
||||
thickness );
|
||||
DECIDEG2RAD( aSegment->GetArcAngleStart() ),
|
||||
DECIDEG2RAD( aSegment->GetArcAngleStart() + aSegment->GetAngle() ), // Change this
|
||||
thickness );
|
||||
break;
|
||||
|
||||
case S_CIRCLE:
|
||||
|
|
|
@ -1258,6 +1258,9 @@ static void updateArcFromConstructionMgr( const KIGFX::PREVIEW::ARC_GEOM_MANAGER
|
|||
aArc.SetArcStart( { vec.x, vec.y } );
|
||||
|
||||
aArc.SetAngle( RAD2DECIDEG( -aMgr.GetSubtended() ) );
|
||||
|
||||
vec = aMgr.GetEndRadiusEnd();
|
||||
aArc.SetArcEnd( { vec.x, vec.y } );
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
#include <functional>
|
||||
#include <memory>
|
||||
using namespace std::placeholders;
|
||||
#include <advanced_config.h>
|
||||
#include <tool/tool_manager.h>
|
||||
#include <view/view_controls.h>
|
||||
#include <gal/graphics_abstraction_layer.h>
|
||||
|
@ -541,28 +542,251 @@ void POINT_EDITOR::updateItem() const
|
|||
}
|
||||
else
|
||||
{
|
||||
const VECTOR2I& cursorPos = getViewControls()->GetCursorPosition();
|
||||
VECTOR2I oldCenter = segment->GetCenter();
|
||||
double newAngle;
|
||||
bool clockwise;
|
||||
|
||||
if( mid != segment->GetArcMid() )
|
||||
{
|
||||
// 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 );
|
||||
|
||||
// Check if the new arc is CW or CCW
|
||||
VECTOR2D startLine = start - center;
|
||||
VECTOR2D endLine = end - center;
|
||||
newAngle = RAD2DECIDEG( endLine.Angle() - startLine.Angle() );
|
||||
VECTOR2D v1, v2;
|
||||
v1 = start - mid;
|
||||
v2 = end - mid;
|
||||
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 = 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
|
||||
{
|
||||
double tan = mid.y / static_cast<double>( mid.x );
|
||||
double tmp = mid.x;
|
||||
// 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 )
|
||||
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 );
|
||||
}
|
||||
else if( ( start != segment->GetArcStart() ) || ( end != segment->GetArcEnd() ) )
|
||||
{
|
||||
|
||||
segment->SetArcStart( wxPoint( start.x, start.y ) );
|
||||
VECTOR2D startLine = start - center;
|
||||
VECTOR2D endLine = end - center;
|
||||
newAngle = RAD2DECIDEG( endLine.Angle() - startLine.Angle() );
|
||||
|
||||
VECTOR2D startLine = start - center;
|
||||
VECTOR2D endLine = end - center;
|
||||
double newAngle = RAD2DECIDEG( endLine.Angle() - startLine.Angle() );
|
||||
VECTOR2I *p1, *p2, *p3;
|
||||
|
||||
// Adjust the new angle to (counter)clockwise setting
|
||||
bool clockwise = ( segment->GetAngle() > 0 );
|
||||
// p1 does not move, p2 does.
|
||||
bool movingStart;
|
||||
bool arcValid = true;
|
||||
|
||||
if( clockwise && newAngle < 0.0 )
|
||||
newAngle += 3600.0;
|
||||
else if( !clockwise && newAngle > 0.0 )
|
||||
newAngle -= 3600.0;
|
||||
if( start != segment->GetArcStart() )
|
||||
{
|
||||
start = cursorPos;
|
||||
p1 = &end;
|
||||
p2 = &start;
|
||||
p3 = ∣
|
||||
movingStart = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
end = cursorPos;
|
||||
p1 = &start;
|
||||
p2 = &end;
|
||||
p3 = ∣
|
||||
movingStart = false;
|
||||
}
|
||||
|
||||
segment->SetAngle( newAngle );
|
||||
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;
|
||||
bool invertY = ( v2.y < 0 );
|
||||
|
||||
/* 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_drawArcCenterStartEndMaxAngle )
|
||||
{
|
||||
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 );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
|
Loading…
Reference in New Issue