Finish arc rework and push out to file formats.

This commit is contained in:
Jeff Young 2021-10-15 00:39:31 +01:00
parent b52529521e
commit f9861b4a6c
23 changed files with 755 additions and 686 deletions

View File

@ -33,12 +33,13 @@
#include <macros.h>
#include <math/util.h> // for KiROUND
#include <eda_shape.h>
#include <plotters/plotter.h>
EDA_SHAPE::EDA_SHAPE( SHAPE_T aType, int aLineWidth, FILL_T aFill ) :
m_shape( aType ),
m_width( aLineWidth ),
m_fill( aFill )
m_fill( aFill ),
m_editState( 0 )
{
}
@ -432,38 +433,29 @@ void EDA_SHAPE::SetCenter( const wxPoint& aCenter )
wxPoint EDA_SHAPE::GetArcMid() const
{
wxPoint mid = m_start;
RotatePoint( &mid, m_arcCenter, -m_arcAngle / 2.0 );
RotatePoint( &mid, m_arcCenter, -GetArcAngle() / 2.0 );
return mid;
}
double EDA_SHAPE::GetArcAngleStart() const
void EDA_SHAPE::CalcArcAngles( double& aStartAngle, double& aEndAngle ) const
{
wxPoint arcStart = GetStart();
wxPoint center = getCenter();
double angleStart = ArcTangente( arcStart.y - center.y, arcStart.x - center.x );
VECTOR2D startRadial( GetStart() - getCenter() );
VECTOR2D endRadial( GetEnd() - getCenter() );
// 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 );
aStartAngle = 180.0 / M_PI * atan2( startRadial.y, startRadial.x );
aEndAngle = 180.0 / M_PI * atan2( endRadial.y, endRadial.x );
return angleStart;
}
if( aEndAngle == aStartAngle )
aEndAngle = aStartAngle + 360.0; // ring, not null
double EDA_SHAPE::GetArcAngleEnd() const
{
wxPoint arcEnd = GetEnd();
wxPoint center = getCenter();
double angleStart = ArcTangente( arcEnd.y - center.y, arcEnd.x - center.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;
if( aStartAngle > aEndAngle )
{
if( aEndAngle < 0 )
aEndAngle = NormalizeAngleDegrees( aEndAngle, 0.0, 360.0 );
else
aStartAngle = NormalizeAngleDegrees( aStartAngle, -360.0, 0.0 );
}
}
@ -495,34 +487,30 @@ void EDA_SHAPE::SetArcGeometry( const wxPoint& aStart, const wxPoint& aMid, cons
m_start = aStart;
m_end = aEnd;
m_arcCenter = CalcArcCenter( aStart, aMid, aEnd );
}
double EDA_SHAPE::GetArcAngle() const
{
VECTOR2D startLine = m_start - m_arcCenter;
VECTOR2D endLine = aEnd - m_arcCenter;
bool clockwise = m_arcAngle > 0;
VECTOR2D endLine = m_end - m_arcCenter;
m_arcAngle = RAD2DECIDEG( endLine.Angle() - startLine.Angle() );
double arcAngle = RAD2DECIDEG( endLine.Angle() - startLine.Angle() );
if( clockwise && m_arcAngle < 0.0 )
m_arcAngle += 3600.0;
else if( !clockwise && m_arcAngle > 0.0 )
m_arcAngle -= 3600.0;
if( arcAngle < 0.0 )
arcAngle += 3600.0;
return arcAngle;
}
void EDA_SHAPE::SetArcAngle( double aAngle )
void EDA_SHAPE::SetArcAngleAndEnd( double aAngle, bool aCheckNegativeAngle )
{
// m_Angle must be >= -360 and <= +360 degrees
m_arcAngle = NormalizeAngle360Max( aAngle );
}
void EDA_SHAPE::SetArcAngleAndEnd( double aAngle )
{
// m_Angle must be >= -360 and <= +360 degrees
m_arcAngle = NormalizeAngle360Max( aAngle );
m_end = m_start;
RotatePoint( &m_end, m_arcCenter, -m_arcAngle );
RotatePoint( &m_end, m_arcCenter, -NormalizeAngle360Max( aAngle ) );
if( aCheckNegativeAngle && aAngle < 0 )
std::swap( m_start, m_end );
}
@ -684,39 +672,28 @@ bool EDA_SHAPE::hitTest( const wxPoint& aPosition, int aAccuracy ) const
case SHAPE_T::ARC:
{
wxPoint relPos = aPosition - getCenter();
int radius = GetRadius();
int dist = KiROUND( EuclideanNorm( relPos ) );
int radius = GetRadius();
int dist = KiROUND( EuclideanNorm( relPos ) );
if( abs( radius - dist ) <= maxdist )
{
// For arcs, the test point angle must be >= arc angle start
// and <= arc angle end
// However angle values > 360 deg are not easy to handle
// so we calculate the relative angle between arc start point and teast point
// this relative arc should be < arc angle if arc angle > 0 (CW arc)
// and > arc angle if arc angle < 0 (CCW arc)
double arc_angle_start = GetArcAngleStart(); // Always 0.0 ... 360 deg, in 0.1 deg
double arc_swept_angle = GetArcAngleEnd() - arc_angle_start;
double startAngle;
double endAngle;
CalcArcAngles( startAngle, endAngle );
double arc_hittest = ArcTangente( relPos.y, relPos.x );
double relPosAngle = 180.0 / M_PI * atan2( relPos.y, relPos.x );
// Calculate relative angle between the starting point of the arc, and the test point
arc_hittest -= arc_angle_start;
if( relPosAngle >= startAngle && relPosAngle <= endAngle )
return true;
// Normalise arc_hittest between 0 ... 360 deg
NORMALIZE_ANGLE_POS( arc_hittest );
relPosAngle = NormalizeAngleDegrees( relPosAngle, 0.0, 360.0 );
if( relPosAngle >= startAngle && relPosAngle <= endAngle )
return true;
// Check angle: inside the arc angle when it is > 0 and outside the not drawn arc when
// it is < 0
if( arc_swept_angle >= 0.0 )
return arc_hittest <= arc_swept_angle;
else
return arc_hittest >= ( 3600.0 + arc_swept_angle );
}
else
{
return false;
}
return false;
}
case SHAPE_T::BEZIER:
@ -1012,16 +989,12 @@ void EDA_SHAPE::computeArcBBox( EDA_RECT& aBBox ) const
quarter = 1;
}
int radius = GetRadius();
int angle = (int) GetArcAngleStart() % 900 + m_arcAngle;
bool directionCW = ( m_arcAngle > 0 ); // Is the direction of arc clockwise?
// Make the angle positive, so we go clockwise and merge points belonging to the arc
if( !directionCW )
{
angle = 900 - angle;
quarter = ( quarter + 3 ) % 4; // -1 modulo arithmetic
}
int radius = GetRadius();
VECTOR2I startRadial = GetStart() - getCenter();
VECTOR2I endRadial = GetEnd() - getCenter();
double angleStart = ArcTangente( startRadial.y, startRadial.x );
double arcAngle = RAD2DECIDEG( endRadial.Angle() - startRadial.Angle() );
int angle = (int) NormalizeAnglePos( angleStart ) % 900 + NormalizeAnglePos( arcAngle );
while( angle > 900 )
{
@ -1033,18 +1006,13 @@ void EDA_SHAPE::computeArcBBox( EDA_RECT& aBBox ) const
case 3: aBBox.Merge( wxPoint( m_arcCenter.x + radius, m_arcCenter.y ) ); break; // right
}
if( directionCW )
++quarter;
else
quarter += 3; // -1 modulo arithmetic
quarter %= 4;
++quarter %= 4;
angle -= 900;
}
aBBox.Inflate( m_width ); // Technically m_width / 2, but it doesn't hurt to have the
// bounding box a bit large to account for drawing clearances,
// etc.
aBBox.Inflate( GetWidth() ); // Technically m_width / 2, but it doesn't hurt to have the
// bounding box a bit large to account for drawing clearances,
// etc.
}
@ -1065,7 +1033,7 @@ std::vector<SHAPE*> EDA_SHAPE::MakeEffectiveShapes() const
switch( m_shape )
{
case SHAPE_T::ARC:
effectiveShapes.emplace_back( new SHAPE_ARC( m_arcCenter, m_start, m_arcAngle / 10.0,
effectiveShapes.emplace_back( new SHAPE_ARC( m_arcCenter, m_start, GetArcAngle() / 10.0,
m_width ) );
break;
@ -1193,6 +1161,221 @@ int EDA_SHAPE::GetPointCount() const
}
void EDA_SHAPE::beginEdit( const wxPoint& aPosition )
{
switch( GetShape() )
{
case SHAPE_T::SEGMENT:
case SHAPE_T::CIRCLE:
case SHAPE_T::RECT:
SetStart( aPosition );
SetEnd( aPosition );
break;
case SHAPE_T::ARC:
SetArcGeometry( aPosition, aPosition, aPosition );
m_editState = 1;
break;
case SHAPE_T::POLY:
m_poly.NewOutline();
m_poly.Outline( 0 ).SetClosed( false );
// Start and end of the first segment (co-located for now)
m_poly.Outline( 0 ).Append( aPosition );
m_poly.Outline( 0 ).Append( aPosition, true );
break;
default:
UNIMPLEMENTED_FOR( SHAPE_T_asString() );
}
}
bool EDA_SHAPE::continueEdit( const wxPoint& aPosition )
{
switch( GetShape() )
{
case SHAPE_T::ARC:
case SHAPE_T::SEGMENT:
case SHAPE_T::CIRCLE:
case SHAPE_T::RECT:
return false;
case SHAPE_T::POLY:
{
SHAPE_LINE_CHAIN& poly = m_poly.Outline( 0 );
// do not add zero-length segments
if( poly.CPoint( poly.GetPointCount() - 2 ) != poly.CLastPoint() )
poly.Append( aPosition, true );
}
return true;
default:
UNIMPLEMENTED_FOR( SHAPE_T_asString() );
return false;
}
}
void EDA_SHAPE::calcEdit( const wxPoint& aPosition )
{
#define sq( x ) pow( x, 2 )
switch( GetShape() )
{
case SHAPE_T::SEGMENT:
case SHAPE_T::CIRCLE:
case SHAPE_T::RECT:
SetEnd( aPosition );
break;
case SHAPE_T::ARC:
{
int radius = GetRadius();
// Edit state 0: drawing: place start
// Edit state 1: drawing: place end (center calculated for 90-degree subtended angle)
// Edit state 2: point edit: move start (center calculated for invariant subtended angle)
// Edit state 3: point edit: move end (center calculated for invariant subtended angle)
// Edit state 4: point edit: move center
// Edit state 5: point edit: move arc-mid-point
switch( m_editState )
{
case 0:
SetArcGeometry( aPosition, aPosition, aPosition );
return;
case 1:
m_end = aPosition;
radius = KiROUND( sqrt( sq( GetLineLength( m_start, m_end ) ) / 2.0 ) );
break;
case 2:
case 3:
{
wxPoint v = m_start - m_end;
double chordBefore = sq( v.x ) + sq( v.y );
if( m_editState == 2 )
m_start = aPosition;
else
m_end = aPosition;
v = m_start - m_end;
double chordAfter = sq( v.x ) + sq( v.y );
double ratio = chordAfter / chordBefore;
if( ratio != 0 )
{
radius = std::max( int( sqrt( sq( radius ) * ratio ) ) + 1,
int( sqrt( chordAfter ) / 2 ) + 1 );
}
}
break;
case 4:
{
double chordA = GetLineLength( m_start, aPosition );
double chordB = GetLineLength( m_end, aPosition );
radius = int( ( chordA + chordB ) / 2.0 ) + 1;
}
break;
case 5:
SetArcGeometry( GetStart(), aPosition, GetEnd() );
return;
}
// Calculate center based on start, end, and radius
//
// Let 'l' be the length of the chord and 'm' the middle point of the chord
double l = GetLineLength( m_start, m_end );
wxPoint m = ( m_start + m_end ) / 2;
// Calculate 'd', the vector from the chord midpoint to the center
wxPoint d;
d.x = KiROUND( sqrt( sq( radius ) - sq( l/2 ) ) * ( m_start.y - m_end.y ) / l );
d.y = KiROUND( sqrt( sq( radius ) - sq( l/2 ) ) * ( m_end.x - m_start.x ) / l );
wxPoint c1 = m + d;
wxPoint c2 = m - d;
// Solution gives us 2 centers; we need to pick one:
switch( m_editState )
{
case 1:
{
// Keep center clockwise from chord while drawing
wxPoint chordVector = m_end - m_start;
double chordAngle = ArcTangente( chordVector.y, chordVector.x );
NORMALIZE_ANGLE_POS( chordAngle );
wxPoint c1Test = c1;
RotatePoint( &c1Test, m_start, -chordAngle );
m_arcCenter = c1Test.x > 0 ? c2 : c1;
}
break;
case 2:
case 3:
// Pick the one closer to the old center
m_arcCenter = GetLineLength( c1, m_arcCenter ) < GetLineLength( c2, m_arcCenter ) ? c1 : c2;
break;
case 4:
// Pick the one closer to the mouse position
m_arcCenter = GetLineLength( c1, aPosition ) < GetLineLength( c2, aPosition ) ? c1 : c2;
break;
}
}
break;
case SHAPE_T::POLY:
m_poly.Outline( 0 ).SetPoint( m_poly.Outline( 0 ).GetPointCount() - 1, aPosition );
break;
default:
UNIMPLEMENTED_FOR( SHAPE_T_asString() );
}
}
void EDA_SHAPE::endEdit()
{
switch( GetShape() )
{
case SHAPE_T::ARC:
case SHAPE_T::SEGMENT:
case SHAPE_T::CIRCLE:
case SHAPE_T::RECT:
break;
case SHAPE_T::POLY:
{
SHAPE_LINE_CHAIN& poly = m_poly.Outline( 0 );
// do not include last point twice
if( poly.GetPointCount() > 2 )
{
if( poly.CPoint( poly.GetPointCount() - 2 ) == poly.CLastPoint() )
{
poly.SetClosed( true );
poly.Remove( poly.GetPointCount() - 1 );
}
}
}
break;
default:
UNIMPLEMENTED_FOR( SHAPE_T_asString() );
}
}
void EDA_SHAPE::SwapShape( EDA_SHAPE* aImage )
{
EDA_SHAPE* image = dynamic_cast<EDA_SHAPE*>( aImage );
@ -1203,7 +1386,6 @@ void EDA_SHAPE::SwapShape( EDA_SHAPE* aImage )
std::swap( m_end, image->m_end );
std::swap( m_arcCenter, image->m_arcCenter );
std::swap( m_shape, image->m_shape );
std::swap( m_arcAngle, image->m_arcAngle );
std::swap( m_bezierC1, image->m_bezierC1 );
std::swap( m_bezierC2, image->m_bezierC2 );
std::swap( m_bezierPoints, image->m_bezierPoints );
@ -1242,4 +1424,44 @@ int EDA_SHAPE::Compare( const EDA_SHAPE* aOther ) const
TEST( (int) m_fill, (int) aOther->m_fill );
return 0;
}
}
static struct EDA_SHAPE_DESC
{
EDA_SHAPE_DESC()
{
ENUM_MAP<SHAPE_T>::Instance()
.Map( SHAPE_T::SEGMENT, _HKI( "Segment" ) )
.Map( SHAPE_T::RECT, _HKI( "Rectangle" ) )
.Map( SHAPE_T::ARC, _HKI( "Arc" ) )
.Map( SHAPE_T::CIRCLE, _HKI( "Circle" ) )
.Map( SHAPE_T::POLY, _HKI( "Polygon" ) )
.Map( SHAPE_T::BEZIER, _HKI( "Bezier" ) );
ENUM_MAP<PLOT_DASH_TYPE>::Instance()
.Map( PLOT_DASH_TYPE::DEFAULT, _HKI( "Default" ) )
.Map( PLOT_DASH_TYPE::SOLID, _HKI( "Solid" ) )
.Map( PLOT_DASH_TYPE::DASH, _HKI( "Dashed" ) )
.Map( PLOT_DASH_TYPE::DOT, _HKI( "Dotted" ) )
.Map( PLOT_DASH_TYPE::DASHDOT, _HKI( "Dash-Dot" ) );
PROPERTY_MANAGER& propMgr = PROPERTY_MANAGER::Instance();
REGISTER_TYPE( EDA_SHAPE );
propMgr.AddProperty( new PROPERTY_ENUM<EDA_SHAPE, SHAPE_T>( _HKI( "Shape" ),
&EDA_SHAPE::SetShape, &EDA_SHAPE::GetShape ) );
propMgr.AddProperty( new PROPERTY<EDA_SHAPE, int>( _HKI( "Start X" ),
&EDA_SHAPE::SetStartX, &EDA_SHAPE::GetStartX ) );
propMgr.AddProperty( new PROPERTY<EDA_SHAPE, int>( _HKI( "Start Y" ),
&EDA_SHAPE::SetStartY, &EDA_SHAPE::GetStartY ) );
propMgr.AddProperty( new PROPERTY<EDA_SHAPE, int>( _HKI( "End X" ),
&EDA_SHAPE::SetEndX, &EDA_SHAPE::GetEndX ) );
propMgr.AddProperty( new PROPERTY<EDA_SHAPE, int>( _HKI( "End Y" ),
&EDA_SHAPE::SetEndY, &EDA_SHAPE::GetEndY ) );
// TODO: m_arcCenter, m_bezierC1, m_bezierC2, m_poly
propMgr.AddProperty( new PROPERTY<EDA_SHAPE, int>( _HKI( "Line Width" ),
&EDA_SHAPE::SetWidth, &EDA_SHAPE::GetWidth ) );
}
} _EDA_SHAPE_DESC;
ENUM_TO_WXANY( SHAPE_T )
ENUM_TO_WXANY( PLOT_DASH_TYPE )

View File

@ -35,8 +35,7 @@ const int fill_tab[3] = { 'N', 'F', 'f' };
LIB_ITEM::LIB_ITEM( KICAD_T aType, LIB_SYMBOL* aSymbol, int aUnit, int aConvert ) :
EDA_ITEM( aSymbol, aType ),
m_unit( aUnit ),
m_convert( aConvert ),
m_editState( 0 )
m_convert( aConvert )
{
}

View File

@ -126,12 +126,6 @@ public:
*/
virtual void CalcEdit( const wxPoint& aPosition ) {}
/**
* For use by more complex editing routines that need to indicate what phase they are in.
* @param aState
*/
virtual void SetEditState( int aState ) { m_editState = aState; }
/**
* Draw an item
*
@ -320,8 +314,6 @@ protected:
* body styles. This is typially used for representing DeMorgan variants in KiCad.
*/
int m_convert;
int m_editState;
};

View File

@ -254,7 +254,9 @@ void LIB_SHAPE::print( const RENDER_SETTINGS* aSettings, const wxPoint& aOffset,
c = aTransform.TransformCoordinate( getCenter() ) + aOffset;
CalcArcAngles( t1, t2 );
aTransform.MapAngles( &t1, &t2 );
if( t1 > t2 )
aTransform.MapAngles( &t1, &t2 );
}
if( forceNoFill || GetFillType() == FILL_T::NO_FILL )
@ -264,7 +266,7 @@ void LIB_SHAPE::print( const RENDER_SETTINGS* aSettings, const wxPoint& aOffset,
switch( GetShape() )
{
case SHAPE_T::ARC:
GRArc1( nullptr, DC, pt2.x, pt2.y, pt1.x, pt1.y, c.x, c.y, penWidth, color );
GRArc1( nullptr, DC, pt1.x, pt1.y, pt2.x, pt2.y, c.x, c.y, penWidth, color );
break;
case SHAPE_T::CIRCLE:
@ -295,7 +297,7 @@ void LIB_SHAPE::print( const RENDER_SETTINGS* aSettings, const wxPoint& aOffset,
switch( GetShape() )
{
case SHAPE_T::ARC:
GRFilledArc( nullptr, DC, c.x, c.y, t1, t2, GetRadius(), penWidth, color, color );
GRFilledArc( nullptr, DC, c.x, c.y, -t2, -t1, GetRadius(), penWidth, color, color );
break;
case SHAPE_T::CIRCLE:
@ -333,7 +335,7 @@ const EDA_RECT LIB_SHAPE::GetBoundingBox() const
}
void LIB_SHAPE::GetMsgPanelInfo( EDA_DRAW_FRAME* aFrame, MSG_PANEL_ITEMS& aList )
void LIB_SHAPE::GetMsgPanelInfo( EDA_DRAW_FRAME* aFrame, std::vector<MSG_PANEL_ITEM>& aList )
{
LIB_ITEM::GetMsgPanelInfo( aFrame, aList );
@ -390,225 +392,6 @@ BITMAPS LIB_SHAPE::GetMenuImage() const
}
void LIB_SHAPE::BeginEdit( const wxPoint& aPosition )
{
switch( GetShape() )
{
case SHAPE_T::SEGMENT:
case SHAPE_T::CIRCLE:
case SHAPE_T::RECT:
SetPosition( aPosition );
SetEnd( aPosition );
break;
case SHAPE_T::ARC:
SetArcGeometry( aPosition, aPosition, aPosition );
SetEditState( 1 );
break;
case SHAPE_T::POLY:
m_poly.NewOutline();
m_poly.Outline( 0 ).SetClosed( false );
// Start and end of the first segment (co-located for now)
m_poly.Outline( 0 ).Append( aPosition );
m_poly.Outline( 0 ).Append( aPosition, true );
break;
default:
UNIMPLEMENTED_FOR( SHAPE_T_asString() );
}
}
bool LIB_SHAPE::ContinueEdit( const wxPoint& aPosition )
{
switch( GetShape() )
{
case SHAPE_T::ARC:
case SHAPE_T::SEGMENT:
case SHAPE_T::CIRCLE:
case SHAPE_T::RECT:
return false;
case SHAPE_T::POLY:
{
SHAPE_LINE_CHAIN& poly = m_poly.Outline( 0 );
// do not add zero-length segments
if( poly.CPoint( poly.GetPointCount() - 2 ) != poly.CLastPoint() )
poly.Append( aPosition, true );
}
return true;
default:
UNIMPLEMENTED_FOR( SHAPE_T_asString() );
return false;
}
}
void LIB_SHAPE::CalcEdit( const wxPoint& aPosition )
{
#define sq( x ) pow( x, 2 )
switch( GetShape() )
{
case SHAPE_T::SEGMENT:
case SHAPE_T::CIRCLE:
case SHAPE_T::RECT:
SetEnd( aPosition );
break;
case SHAPE_T::ARC:
{
int radius = GetRadius();
// Edit state 0: drawing: place start
// Edit state 1: drawing: place end (center calculated for 90-degree subtended angle)
// Edit state 2: point edit: move start (center calculated for invariant subtended angle)
// Edit state 3: point edit: move end (center calculated for invariant subtended angle)
// Edit state 4: point edit: move center
// Edit state 5: point edit: move arc-mid-point
switch( m_editState )
{
case 0:
SetArcGeometry( aPosition, aPosition, aPosition );
return;
case 1:
m_end = aPosition;
radius = KiROUND( sqrt( sq( GetLineLength( m_start, m_end ) ) / 2.0 ) );
break;
break;
case 2:
case 3:
{
wxPoint v = m_start - m_end;
double chordBefore = sq( v.x ) + sq( v.y );
if( m_editState == 2 )
m_start = aPosition;
else
m_end = aPosition;
v = m_start - m_end;
double chordAfter = sq( v.x ) + sq( v.y );
double ratio = chordAfter / chordBefore;
if( ratio != 0 )
{
radius = std::max( int( sqrt( sq( radius ) * ratio ) ) + 1,
int( sqrt( chordAfter ) / 2 ) + 1 );
}
}
break;
case 4:
{
double chordA = GetLineLength( m_start, aPosition );
double chordB = GetLineLength( m_end, aPosition );
radius = int( ( chordA + chordB ) / 2.0 ) + 1;
}
break;
case 5:
SetArcGeometry( GetStart(), aPosition, GetEnd() );
return;
}
// Calculate center based on start, end, and radius
//
// Let 'l' be the length of the chord and 'm' the middle point of the chord
double l = GetLineLength( m_start, m_end );
wxPoint m = ( m_start + m_end ) / 2;
// Calculate 'd', the vector from the chord midpoint to the center
wxPoint d;
d.x = KiROUND( sqrt( sq( radius ) - sq( l/2 ) ) * ( m_start.y - m_end.y ) / l );
d.y = KiROUND( sqrt( sq( radius ) - sq( l/2 ) ) * ( m_end.x - m_start.x ) / l );
wxPoint c1 = m + d;
wxPoint c2 = m - d;
// Solution gives us 2 centers; we need to pick one:
switch( m_editState )
{
case 1:
{
// Keep center clockwise from chord while drawing
wxPoint chordVector = m_end - m_start;
double chordAngle = ArcTangente( chordVector.y, chordVector.x );
NORMALIZE_ANGLE_POS( chordAngle );
wxPoint c1Test = c1;
RotatePoint( &c1Test, m_start, -chordAngle );
m_arcCenter = c1Test.x > 0 ? c2 : c1;
}
break;
case 2:
case 3:
// Pick the one closer to the old center
m_arcCenter = GetLineLength( c1, m_arcCenter ) < GetLineLength( c2, m_arcCenter ) ? c1 : c2;
break;
case 4:
// Pick the one closer to the mouse position
m_arcCenter = GetLineLength( c1, aPosition ) < GetLineLength( c2, aPosition ) ? c1 : c2;
break;
}
}
break;
case SHAPE_T::POLY:
m_poly.Outline( 0 ).SetPoint( m_poly.Outline( 0 ).GetPointCount() - 1, aPosition );
break;
default:
UNIMPLEMENTED_FOR( SHAPE_T_asString() );
}
}
void LIB_SHAPE::EndEdit()
{
switch( GetShape() )
{
case SHAPE_T::ARC:
case SHAPE_T::SEGMENT:
case SHAPE_T::CIRCLE:
case SHAPE_T::RECT:
break;
case SHAPE_T::POLY:
{
SHAPE_LINE_CHAIN& poly = m_poly.Outline( 0 );
if( poly.GetPointCount() > 2 )
{
if( poly.CPoint( poly.GetPointCount() - 2 ) == poly.CLastPoint() )
{
poly.SetClosed( true );
poly.Remove( poly.GetPointCount() - 1 );
}
else
{
poly.SetClosed( false );
}
}
}
break;
default:
wxFAIL_MSG( "LIB_SHAPE::EndEdit not implemented for " + SHAPE_T_asString() );
}
}
void LIB_SHAPE::AddPoint( const wxPoint& aPosition )
{
if( GetShape() == SHAPE_T::POLY )
@ -625,30 +408,6 @@ void LIB_SHAPE::AddPoint( const wxPoint& aPosition )
}
double LIB_SHAPE::GetArcAngleStart() const
{
int startAngle, endAngle;
CalcArcAngles( startAngle, endAngle );
if( startAngle > endAngle )
TRANSFORM().MapAngles( &startAngle, &endAngle );
return startAngle;
}
double LIB_SHAPE::GetArcAngleEnd() const
{
int startAngle, endAngle;
CalcArcAngles( startAngle, endAngle );
if( startAngle > endAngle )
TRANSFORM().MapAngles( &startAngle, &endAngle );
return endAngle;
}
void LIB_SHAPE::CalcArcAngles( int& aStartAngle, int& aEndAngle ) const
{
wxPoint centerStartVector = GetStart() - GetCenter();

View File

@ -71,10 +71,11 @@ public:
void GetMsgPanelInfo( EDA_DRAW_FRAME* aFrame, std::vector<MSG_PANEL_ITEM>& aList ) override;
void BeginEdit( const wxPoint& aStartPoint ) override;
bool ContinueEdit( const wxPoint& aPosition ) override;
void CalcEdit( const wxPoint& aPosition ) override;
void EndEdit() override;
void BeginEdit( const wxPoint& aStartPoint ) override { beginEdit( aStartPoint ); }
bool ContinueEdit( const wxPoint& aPosition ) override { return continueEdit( aPosition ); }
void CalcEdit( const wxPoint& aPosition ) override { calcEdit( aPosition ); }
void EndEdit() override { endEdit(); }
void SetEditState( int aState ) { setEditState( aState ); }
void AddPoint( const wxPoint& aPosition );
@ -82,13 +83,11 @@ public:
void MoveTo( const wxPoint& aPosition ) override;
wxPoint GetPosition() const override { return getPosition(); }
wxPoint GetPosition() const override { return getPosition(); }
void SetPosition( const wxPoint& aPosition ) override { setPosition( aPosition ); }
wxPoint GetCenter() const { return getCenter(); }
double GetArcAngleStart() const override;
double GetArcAngleEnd() const override;
void CalcArcAngles( int& aStartAngle, int& aEndAngle ) const;
void MirrorHorizontal( const wxPoint& aCenter ) override;

View File

@ -39,7 +39,8 @@
//#define SEXPR_SYMBOL_LIB_FILE_VERSION 20200827 // Remove host tag.
//#define SEXPR_SYMBOL_LIB_FILE_VERSION 20200908 // Add include in BOM and on board support.
//#define SEXPR_SYMBOL_LIB_FILE_VERSION 20201005 // Separate ki_fp_filters by spaces.
#define SEXPR_SYMBOL_LIB_FILE_VERSION 20210619 // Change pin overbar syntax from `~...~` to `~{...}`.
//#define SEXPR_SYMBOL_LIB_FILE_VERSION 20210619 // Change pin overbar syntax from `~...~` to `~{...}`.
#define SEXPR_SYMBOL_LIB_FILE_VERSION 20211014 // Arc formatting.
/**

View File

@ -1018,7 +1018,6 @@ LIB_SHAPE* SCH_SEXPR_PARSER::parseArc()
}
else if( hasAngles )
{
arc->SetCenter( pos );
/**
* This accounts for an oddity in the old library format, where the symbol is overdefined.
* The previous draw (based on wxwidgets) used start point and end point and always drew
@ -1032,9 +1031,12 @@ LIB_SHAPE* SCH_SEXPR_PARSER::parseArc()
arc->SetStart( arc->GetEnd() );
arc->SetEnd( temp );
}
arc->SetCenter( pos );
}
else
{
wxFAIL_MSG( "Setting arc without either midpoint or angles not implemented." );
}
return arc.release();
}

View File

@ -73,11 +73,12 @@ static const char* emptyString = "";
/**
* Fill token formatting helper.
*/
static void formatFill( const LIB_SHAPE* aItem, OUTPUTFORMATTER& aFormatter, int aNestLevel )
static void formatFill( OUTPUTFORMATTER* aFormatter, int aNestLevel, FILL_T aFillMode,
const COLOR4D& aFillColor )
{
const char* fillType;
switch( aItem->GetFillType() )
switch( aFillMode )
{
default:
case FILL_T::NO_FILL: fillType = "none"; break;
@ -85,7 +86,7 @@ static void formatFill( const LIB_SHAPE* aItem, OUTPUTFORMATTER& aFormatter, int
case FILL_T::FILLED_WITH_BG_BODYCOLOR: fillType = "background"; break;
}
aFormatter.Print( aNestLevel, "(fill (type %s))", fillType );
aFormatter->Print( aNestLevel, "(fill (type %s))", fillType );
}
@ -211,10 +212,10 @@ static double getSheetPinAngle( SHEET_SIDE aSide )
{
case SHEET_SIDE::UNDEFINED:
case SHEET_SIDE::LEFT: retv = 180.0; break;
case SHEET_SIDE::RIGHT: retv = 0.0; break;
case SHEET_SIDE::TOP: retv = 90.0; break;
case SHEET_SIDE::RIGHT: retv = 0.0; break;
case SHEET_SIDE::TOP: retv = 90.0; break;
case SHEET_SIDE::BOTTOM: retv = 270.0; break;
default: wxFAIL; retv = 0.0; break;
default: wxFAIL; retv = 0.0; break;
}
return retv;
@ -278,6 +279,156 @@ static void formatStroke( OUTPUTFORMATTER* aFormatter, int aNestLevel,
}
static void formatArc( OUTPUTFORMATTER* aFormatter, int aNestLevel, EDA_SHAPE* aArc, int x1, int x2,
const STROKE_PARAMS& aStroke, FILL_T aFillMode, const COLOR4D& aFillColor,
KIID aUuid = niluuid )
{
if( x1 > 1800 )
x1 -= 3600;
if( x2 > 1800 )
x2 -= 3600;
aFormatter->Print( aNestLevel, "(arc (start %s) (mid %s) (end %s)\n",
FormatInternalUnits( aArc->GetStart() ).c_str(),
FormatInternalUnits( aArc->GetArcMid() ).c_str(),
FormatInternalUnits( aArc->GetEnd() ).c_str() );
formatStroke( aFormatter, aNestLevel + 1, aStroke );
aFormatter->Print( 0, "\n" );
formatFill( aFormatter, aNestLevel + 1, aFillMode, aFillColor );
aFormatter->Print( 0, "\n" );
if( aUuid != niluuid )
aFormatter->Print( aNestLevel + 1, "(uuid %s)\n", TO_UTF8( aUuid.AsString() ) );
aFormatter->Print( aNestLevel, ")\n" );
}
static void formatCircle( OUTPUTFORMATTER* aFormatter, int aNestLevel, EDA_SHAPE* aCircle,
const STROKE_PARAMS& aStroke, FILL_T aFillMode, const COLOR4D& aFillColor,
KIID aUuid = niluuid )
{
aFormatter->Print( aNestLevel, "(circle (center %s %s) (radius %s) (stroke (width %s)) ",
FormatInternalUnits( aCircle->GetStart().x ).c_str(),
FormatInternalUnits( aCircle->GetStart().y ).c_str(),
FormatInternalUnits( aCircle->GetRadius() ).c_str(),
FormatInternalUnits( aCircle->GetWidth() ).c_str() );
formatStroke( aFormatter, aNestLevel + 1, aStroke );
aFormatter->Print( 0, "\n" );
formatFill( aFormatter, aNestLevel + 1, aFillMode, aFillColor );
aFormatter->Print( 0, "\n" );
if( aUuid != niluuid )
aFormatter->Print( aNestLevel + 1, "(uuid %s)\n", TO_UTF8( aUuid.AsString() ) );
aFormatter->Print( aNestLevel, ")\n" );
}
static void formatRect( OUTPUTFORMATTER* aFormatter, int aNestLevel, EDA_SHAPE* aRect,
const STROKE_PARAMS& aStroke, FILL_T aFillMode, const COLOR4D& aFillColor,
KIID aUuid = niluuid )
{
aFormatter->Print( aNestLevel, "(rectangle (start %s %s) (end %s %s)\n",
FormatInternalUnits( aRect->GetStart().x ).c_str(),
FormatInternalUnits( aRect->GetStart().y ).c_str(),
FormatInternalUnits( aRect->GetEnd().x ).c_str(),
FormatInternalUnits( aRect->GetEnd().y ).c_str() );
formatStroke( aFormatter, aNestLevel + 1, aStroke );
aFormatter->Print( 0, "\n" );
formatFill( aFormatter, aNestLevel + 1, aFillMode, aFillColor );
aFormatter->Print( 0, "\n" );
if( aUuid != niluuid )
aFormatter->Print( aNestLevel + 1, "(uuid %s)\n", TO_UTF8( aUuid.AsString() ) );
aFormatter->Print( aNestLevel, ")\n" );
}
static void formatBezier( OUTPUTFORMATTER* aFormatter, int aNestLevel, EDA_SHAPE* aBezier,
const STROKE_PARAMS& aStroke, FILL_T aFillMode, const COLOR4D& aFillColor,
KIID aUuid = niluuid )
{
aFormatter->Print( aNestLevel, "(bezier (pts " );
for( const wxPoint& pt : { aBezier->GetStart(), aBezier->GetBezierC1(),
aBezier->GetBezierC2(), aBezier->GetEnd() } )
{
aFormatter->Print( 0, " (xy %s %s)",
FormatInternalUnits( pt.x ).c_str(),
FormatInternalUnits( pt.y ).c_str() );
}
aFormatter->Print( 0, ")\n" ); // Closes pts token on same line.
formatStroke( aFormatter, aNestLevel + 1, aStroke );
aFormatter->Print( 0, "\n" );
formatFill( aFormatter, aNestLevel + 1, aFillMode, aFillColor );
aFormatter->Print( 0, "\n" );
if( aUuid != niluuid )
aFormatter->Print( aNestLevel + 1, "(uuid %s)\n", TO_UTF8( aUuid.AsString() ) );
aFormatter->Print( aNestLevel, ")\n" );
}
static void formatPoly( OUTPUTFORMATTER* aFormatter, int aNestLevel, EDA_SHAPE* aPolyLine,
const STROKE_PARAMS& aStroke, FILL_T aFillMode, const COLOR4D& aFillColor,
KIID aUuid = niluuid )
{
int newLine = 0;
int lineCount = 1;
aFormatter->Print( aNestLevel, "(polyline\n" );
aFormatter->Print( aNestLevel + 1, "(pts" );
for( const VECTOR2I& pt : aPolyLine->GetPolyShape().Outline( 0 ).CPoints() )
{
if( newLine == 4 || !ADVANCED_CFG::GetCfg().m_CompactSave )
{
aFormatter->Print( 0, "\n" );
aFormatter->Print( aNestLevel + 2, "(xy %s %s)",
FormatInternalUnits( pt.x ).c_str(),
FormatInternalUnits( pt.y ).c_str() );
newLine = 0;
lineCount += 1;
}
else
{
aFormatter->Print( 0, " (xy %s %s)",
FormatInternalUnits( pt.x ).c_str(),
FormatInternalUnits( pt.y ).c_str() );
}
newLine += 1;
}
if( lineCount == 1 )
{
aFormatter->Print( 0, ")\n" ); // Closes pts token on same line.
}
else
{
aFormatter->Print( 0, "\n" );
aFormatter->Print( aNestLevel + 1, ")\n" ); // Closes pts token with multiple lines.
}
formatStroke( aFormatter, aNestLevel + 1, aStroke );
aFormatter->Print( 0, "\n" );
formatFill( aFormatter, aNestLevel + 1, aFillMode, aFillColor );
aFormatter->Print( 0, "\n" );
if( aUuid != niluuid )
aFormatter->Print( aNestLevel + 1, "(uuid %s)\n", TO_UTF8( aUuid.AsString() ) );
aFormatter->Print( aNestLevel, ")\n" );
}
/**
* A cache assistant for the symbol library portion of the #SCH_PLUGIN API, and only for the
* #SCH_SEXPR_PLUGIN, so therefore is private to this implementation file, i.e. not placed
@ -300,14 +451,9 @@ class SCH_SEXPR_PLUGIN_CACHE
static void saveSymbolDrawItem( LIB_ITEM* aItem, OUTPUTFORMATTER& aFormatter,
int aNestLevel );
static void saveArc( LIB_SHAPE* aArc, OUTPUTFORMATTER& aFormatter, int aNestLevel = 0 );
static void saveBezier( LIB_SHAPE* aBezier, OUTPUTFORMATTER& aFormatter, int aNestLevel = 0 );
static void saveCircle( LIB_SHAPE* aCircle, OUTPUTFORMATTER& aFormatter, int aNestLevel = 0 );
static void saveField( LIB_FIELD* aField, OUTPUTFORMATTER& aFormatter, int& aNextFreeFieldId,
int aNestLevel );
static void savePin( LIB_PIN* aPin, OUTPUTFORMATTER& aFormatter, int aNestLevel = 0 );
static void savePolyLine( LIB_SHAPE* aPolyLine, OUTPUTFORMATTER& aFormatter, int aNestLevel = 0 );
static void saveRectangle( LIB_SHAPE* aRect, OUTPUTFORMATTER& aFormatter, int aNestLevel = 0 );
static void saveText( LIB_TEXT* aText, OUTPUTFORMATTER& aFormatter, int aNestLevel = 0 );
static void saveDcmInfoAsFields( LIB_SYMBOL* aSymbol, OUTPUTFORMATTER& aFormatter,
@ -1763,15 +1909,39 @@ void SCH_SEXPR_PLUGIN_CACHE::saveSymbolDrawItem( LIB_ITEM* aItem, OUTPUTFORMATTE
{
case LIB_SHAPE_T:
{
LIB_SHAPE* shape = static_cast<LIB_SHAPE*>( aItem );
LIB_SHAPE* shape = static_cast<LIB_SHAPE*>( aItem );
STROKE_PARAMS stroke;
FILL_T fillMode = shape->GetFillType();
stroke.SetWidth( shape->GetWidth() );
switch( shape->GetShape() )
{
case SHAPE_T::ARC: saveArc( shape, aFormatter, aNestLevel ); break;
case SHAPE_T::CIRCLE: saveCircle( shape, aFormatter, aNestLevel ); break;
case SHAPE_T::RECT: saveRectangle( shape, aFormatter, aNestLevel ); break;
case SHAPE_T::BEZIER: saveBezier( shape, aFormatter, aNestLevel ); break;
case SHAPE_T::POLY: savePolyLine( shape, aFormatter, aNestLevel ); break;
case SHAPE_T::ARC:
int x1;
int x2;
shape->CalcArcAngles( x1, x2 );
formatArc( &aFormatter, aNestLevel, shape, x1, x2, stroke, fillMode, COLOR4D() );
break;
case SHAPE_T::CIRCLE:
formatCircle( &aFormatter, aNestLevel, shape, stroke, fillMode, COLOR4D() );
break;
case SHAPE_T::RECT:
formatRect( &aFormatter, aNestLevel, shape, stroke, fillMode, COLOR4D() );
break;
case SHAPE_T::BEZIER:
formatBezier(&aFormatter, aNestLevel, shape, stroke, fillMode, COLOR4D() );
break;
case SHAPE_T::POLY:
formatPoly( &aFormatter, aNestLevel, shape, stroke, fillMode, COLOR4D() );
break;
default:
UNIMPLEMENTED_FOR( shape->SHAPE_T_asString() );
}
@ -1793,81 +1963,6 @@ void SCH_SEXPR_PLUGIN_CACHE::saveSymbolDrawItem( LIB_ITEM* aItem, OUTPUTFORMATTE
}
void SCH_SEXPR_PLUGIN_CACHE::saveArc( LIB_SHAPE* aArc, OUTPUTFORMATTER& aFormatter, int aNestLevel )
{
int x1;
int x2;
aArc->CalcArcAngles( x1, x2 );
if( x1 > 1800 )
x1 -= 3600;
if( x2 > 1800 )
x2 -= 3600;
aFormatter.Print( aNestLevel,
"(arc (start %s %s) (end %s %s) (radius (at %s %s) (length %s) "
"(angles %g %g))",
FormatInternalUnits( aArc->GetStart().x ).c_str(),
FormatInternalUnits( aArc->GetStart().y ).c_str(),
FormatInternalUnits( aArc->GetEnd().x ).c_str(),
FormatInternalUnits( aArc->GetEnd().y ).c_str(),
FormatInternalUnits( aArc->GetCenter().x ).c_str(),
FormatInternalUnits( aArc->GetCenter().y ).c_str(),
FormatInternalUnits( aArc->GetRadius() ).c_str(),
static_cast<double>( x1 ) / 10.0,
static_cast<double>( x2 ) / 10.0 );
aFormatter.Print( 0, "\n" );
aFormatter.Print( aNestLevel + 1, "(stroke (width %s)) ",
FormatInternalUnits( aArc->GetWidth() ).c_str() );
formatFill( aArc, aFormatter, 0 );
aFormatter.Print( 0, "\n" );
aFormatter.Print( aNestLevel, ")\n" );
}
void SCH_SEXPR_PLUGIN_CACHE::saveBezier( LIB_SHAPE* aBezier, OUTPUTFORMATTER& aFormatter,
int aNestLevel )
{
aFormatter.Print( aNestLevel, "(bezier\n" );
aFormatter.Print( aNestLevel + 1, "(pts " );
for( const wxPoint& pt : { aBezier->GetStart(), aBezier->GetBezierC1(),
aBezier->GetBezierC2(), aBezier->GetEnd() } )
{
aFormatter.Print( 0, " (xy %s %s)",
FormatInternalUnits( pt.x ).c_str(),
FormatInternalUnits( pt.y ).c_str() );
}
aFormatter.Print( 0, ")\n" ); // Closes pts token on same line.
aFormatter.Print( aNestLevel + 1, "(stroke (width %s)) ",
FormatInternalUnits( aBezier->GetWidth() ).c_str() );
formatFill( aBezier, aFormatter, 0 );
aFormatter.Print( 0, "\n" );
aFormatter.Print( aNestLevel, ")\n" );
}
void SCH_SEXPR_PLUGIN_CACHE::saveCircle( LIB_SHAPE* aCircle, OUTPUTFORMATTER& aFormatter,
int aNestLevel )
{
aFormatter.Print( aNestLevel, "(circle (center %s %s) (radius %s) (stroke (width %s)) ",
FormatInternalUnits( aCircle->GetPosition().x ).c_str(),
FormatInternalUnits( aCircle->GetPosition().y ).c_str(),
FormatInternalUnits( aCircle->GetRadius() ).c_str(),
FormatInternalUnits( aCircle->GetWidth() ).c_str() );
formatFill( aCircle, aFormatter, 0 );
aFormatter.Print( 0, ")\n" );
}
void SCH_SEXPR_PLUGIN_CACHE::saveField( LIB_FIELD* aField, OUTPUTFORMATTER& aFormatter,
int& aNextFreeFieldId, int aNestLevel )
{
@ -1944,69 +2039,6 @@ void SCH_SEXPR_PLUGIN_CACHE::savePin( LIB_PIN* aPin, OUTPUTFORMATTER& aFormatter
}
void SCH_SEXPR_PLUGIN_CACHE::savePolyLine( LIB_SHAPE* aPolyLine, OUTPUTFORMATTER& aFormatter,
int aNestLevel )
{
int newLine = 0;
int lineCount = 1;
aFormatter.Print( aNestLevel, "(polyline\n" );
aFormatter.Print( aNestLevel + 1, "(pts" );
for( const VECTOR2I& pt : aPolyLine->GetPolyShape().Outline( 0 ).CPoints() )
{
if( newLine == 4 || !ADVANCED_CFG::GetCfg().m_CompactSave )
{
aFormatter.Print( 0, "\n" );
aFormatter.Print( aNestLevel + 2, "(xy %s %s)",
FormatInternalUnits( pt.x ).c_str(),
FormatInternalUnits( pt.y ).c_str() );
newLine = 0;
lineCount += 1;
}
else
{
aFormatter.Print( 0, " (xy %s %s)",
FormatInternalUnits( pt.x ).c_str(),
FormatInternalUnits( pt.y ).c_str() );
}
newLine += 1;
}
if( lineCount == 1 )
{
aFormatter.Print( 0, ")\n" ); // Closes pts token on same line.
}
else
{
aFormatter.Print( 0, "\n" );
aFormatter.Print( aNestLevel + 1, ")\n" ); // Closes pts token with multiple lines.
}
aFormatter.Print( aNestLevel + 1, "(stroke (width %s)) ",
FormatInternalUnits( aPolyLine->GetWidth() ).c_str() );
formatFill( aPolyLine, aFormatter, 0 );
aFormatter.Print( 0, "\n" );
aFormatter.Print( aNestLevel, ")\n" );
}
void SCH_SEXPR_PLUGIN_CACHE::saveRectangle( LIB_SHAPE* aRect, OUTPUTFORMATTER& aFormatter,
int aNestLevel )
{
aFormatter.Print( aNestLevel, "(rectangle (start %s %s) (end %s %s)\n",
FormatInternalUnits( aRect->GetPosition().x ).c_str(),
FormatInternalUnits( aRect->GetPosition().y ).c_str(),
FormatInternalUnits( aRect->GetEnd().x ).c_str(),
FormatInternalUnits( aRect->GetEnd().y ).c_str() );
aFormatter.Print( aNestLevel + 1, "(stroke (width %s)) ",
FormatInternalUnits( aRect->GetWidth() ).c_str() );
formatFill( aRect, aFormatter, 0 );
aFormatter.Print( 0, "\n" );
aFormatter.Print( aNestLevel, ")\n" );
}
void SCH_SEXPR_PLUGIN_CACHE::saveText( LIB_TEXT* aText, OUTPUTFORMATTER& aFormatter,
int aNestLevel )
{

View File

@ -326,14 +326,11 @@ int EE_POINT_EDITOR::Main( const TOOL_EVENT& aEvent )
modified = true;
}
bool snap = !evt->DisableGridSnapping();
bool snap = !evt->DisableGridSnapping();
EDA_SHAPE* shape = dynamic_cast<EDA_SHAPE*>( item );
if( item->Type() == LIB_SHAPE_T
&& static_cast<LIB_SHAPE*>( item )->GetShape() == SHAPE_T::ARC
&& getEditedPointIndex() == ARC_CENTER )
{
if( shape && shape->GetShape() == SHAPE_T::ARC && getEditedPointIndex() == ARC_CENTER )
snap = false;
}
m_editedPoint->SetPosition( controls->GetCursorPosition( snap ) );

View File

@ -120,13 +120,13 @@ public:
void SetCenter( const wxPoint& aCenter );
/**
* Set the angle for arcs, and normalizes it within the range 0 - 360 degrees.
* Set the end point from the angle center and start.
*
* @param aAngle is tenths of degrees, but will soon be degrees.
* @param aAngle is tenths of degrees.
*/
void SetArcAngle( double aAngle );
void SetArcAngleAndEnd( double aAngle );
double GetArcAngle() const { return m_arcAngle; }
void SetArcAngleAndEnd( double aAngle, bool aCheckNegativeAngle = false );
double GetArcAngle() const;
// Some attributes are read only, since they are derived from m_Start, m_End, and m_Angle.
// No Set...() function for these attributes.
@ -135,14 +135,10 @@ public:
std::vector<wxPoint> GetRectCorners() const;
/**
* @return the angle of the starting point of this arc, between 0 and 3600 in 0.1 deg.
* Calc arc start and end angles such that aStartAngle < aEndAngle. Each may be between
* -360.0 and 360.0.
*/
virtual double GetArcAngleStart() const;
/**
* @return the angle of the ending point of this arc, between 0 and 3600 in 0.1 deg.
*/
virtual double GetArcAngleEnd() const;
void CalcArcAngles( double& aStartAngle, double& aEndAngle ) const;
int GetRadius() const;
@ -252,6 +248,12 @@ protected:
const std::vector<wxPoint> buildBezierToSegmentsPointsList( int aMinSegLen ) const;
void beginEdit( const wxPoint& aStartPoint );
bool continueEdit( const wxPoint& aPosition );
void calcEdit( const wxPoint& aPosition );
void endEdit();
void setEditState( int aState ) { m_editState = aState; }
protected:
SHAPE_T m_shape; // Shape: line, Circle, Arc
int m_width; // thickness of lines ...
@ -260,13 +262,14 @@ protected:
wxPoint m_end; // Line end point or Circle 3 o'clock point
wxPoint m_arcCenter; // Used only for Arcs: arc end point
double m_arcAngle; // Used only for Arcs: Arc angle in 1/10 deg
wxPoint m_bezierC1; // Bezier Control Point 1
wxPoint m_bezierC2; // Bezier Control Point 2
std::vector<wxPoint> m_bezierPoints;
SHAPE_POLY_SET m_poly; // Stores the S_POLYGON shape
int m_editState;
};
#endif // EDA_SHAPE_H

View File

@ -316,10 +316,7 @@ bool DIALOG_GRAPHIC_ITEM_PROPERTIES::TransferDataFromWindow()
}
if( m_item->GetShape() == SHAPE_T::ARC )
{
m_item->SetCenter( CalcArcCenter( m_item->GetStart(), m_item->GetEnd(), m_AngleValue ) );
m_item->SetArcAngle( m_AngleValue * 10.0 );
}
if( m_fp_item )
{

View File

@ -55,22 +55,23 @@ void FP_SHAPE::SetLocalCoord()
if( fp == NULL )
{
m_start0 = m_start;
m_end0 = m_end;
m_start0 = m_start;
m_end0 = m_end;
m_arcCenter0 = m_arcCenter;
m_bezierC1_0 = m_bezierC1;
m_bezierC2_0 = m_bezierC2;
return;
}
m_start0 = m_start - fp->GetPosition();
m_end0 = m_end - fp->GetPosition();
m_start0 = m_start - fp->GetPosition();
m_end0 = m_end - fp->GetPosition();
m_arcCenter0 = m_arcCenter - fp->GetPosition();
m_bezierC1_0 = m_bezierC1 - fp->GetPosition();
m_bezierC2_0 = m_bezierC2 - fp->GetPosition();
m_bezierC1_0 = m_bezierC1 - fp->GetPosition();
m_bezierC2_0 = m_bezierC2 - fp->GetPosition();
double angle = fp->GetOrientation();
RotatePoint( &m_start0.x, &m_start0.y, -angle );
RotatePoint( &m_end0.x, &m_end0.y, -angle );
RotatePoint( &m_start0.x, &m_start0.y, -angle );
RotatePoint( &m_end0.x, &m_end0.y, -angle );
RotatePoint( &m_arcCenter0.x, &m_arcCenter0.y, -angle );
RotatePoint( &m_bezierC1_0.x, &m_bezierC1_0.y, -angle );
RotatePoint( &m_bezierC2_0.x, &m_bezierC2_0.y, -angle );
@ -81,11 +82,11 @@ void FP_SHAPE::SetDrawCoord()
{
FOOTPRINT* fp = static_cast<FOOTPRINT*>( m_parent );
m_start = m_start0;
m_end = m_end0;
m_start = m_start0;
m_end = m_end0;
m_arcCenter = m_arcCenter0;
m_bezierC1 = m_bezierC1_0;
m_bezierC2 = m_bezierC2_0;
m_bezierC1 = m_bezierC1_0;
m_bezierC2 = m_bezierC2_0;
if( fp )
{
@ -95,11 +96,11 @@ void FP_SHAPE::SetDrawCoord()
RotatePoint( &m_bezierC1.x, &m_bezierC1.y, fp->GetOrientation() );
RotatePoint( &m_bezierC2.x, &m_bezierC2.y, fp->GetOrientation() );
m_start += fp->GetPosition();
m_end += fp->GetPosition();
m_start += fp->GetPosition();
m_end += fp->GetPosition();
m_arcCenter += fp->GetPosition();
m_bezierC1 += fp->GetPosition();
m_bezierC2 += fp->GetPosition();
m_bezierC1 += fp->GetPosition();
m_bezierC2 += fp->GetPosition();
}
RebuildBezierToSegmentsPointsList( m_width );
@ -172,13 +173,29 @@ void FP_SHAPE::SetCenter0( const wxPoint& aCenter )
}
void FP_SHAPE::SetArcAngleAndEnd0( double aAngle )
wxPoint FP_SHAPE::GetArcMid0() const
{
PCB_SHAPE::SetArcAngle( aAngle );
wxPoint mid0 = m_start0;
RotatePoint( &mid0, m_arcCenter0, -GetArcAngle() / 2.0 );
return mid0;
}
wxPoint end = GetStart0();
RotatePoint( &end, GetCenter0(), -m_arcAngle );
SetEnd0( end );
void FP_SHAPE::SetArcAngleAndEnd0( double aAngle, bool aCheckNegativeAngle )
{
m_end0 = m_start0;
RotatePoint( &m_end0, m_arcCenter0, -NormalizeAngle360Max( aAngle ) );
if( aCheckNegativeAngle && aAngle < 0 )
std::swap( m_start0, m_end0 );
}
void FP_SHAPE::SetArcGeometry0( const wxPoint& aStart0, const wxPoint& aMid0, const wxPoint& aEnd0 )
{
m_start0 = aStart0;
m_end0 = aEnd0;
m_arcCenter0 = CalcArcCenter( aStart0, aMid0, aEnd0 );
}
@ -367,6 +384,11 @@ static struct FP_SHAPE_DESC
{
PROPERTY_MANAGER& propMgr = PROPERTY_MANAGER::Instance();
REGISTER_TYPE( FP_SHAPE );
propMgr.AddTypeCast( new TYPE_CAST<FP_SHAPE, BOARD_ITEM> );
propMgr.AddTypeCast( new TYPE_CAST<FP_SHAPE, EDA_SHAPE> );
propMgr.AddTypeCast( new TYPE_CAST<FP_SHAPE, PCB_SHAPE> );
propMgr.InheritsAfter( TYPE_HASH( FP_SHAPE ), TYPE_HASH( BOARD_ITEM ) );
propMgr.InheritsAfter( TYPE_HASH( FP_SHAPE ), TYPE_HASH( EDA_SHAPE ) );
propMgr.InheritsAfter( TYPE_HASH( FP_SHAPE ), TYPE_HASH( PCB_SHAPE ) );
propMgr.AddProperty( new PROPERTY<FP_SHAPE, wxString>( _HKI( "Parent" ),

View File

@ -71,7 +71,9 @@ public:
* Sets the angle for arcs, and normalizes it within the range 0 - 360 degrees.
* @param aAngle is tenths of degrees, but will soon be degrees.
*/
void SetArcAngleAndEnd0( double aAngle );
void SetArcAngleAndEnd0( double aAngle, bool aCheckNegativeAngle = false );
void SetArcGeometry0( const wxPoint& aStart, const wxPoint& aMid, const wxPoint& aEnd );
/**
* Move an edge of the footprint.
@ -121,6 +123,8 @@ public:
wxPoint GetCenter0() const;
void SetCenter0( const wxPoint& aPt );
wxPoint GetArcMid0() const;
/**
* Set relative coordinates from draw coordinates.
* Call in only when the geometry or the footprint is modified and therefore the relative

View File

@ -1408,12 +1408,16 @@ void PCB_PAINTER::draw( const PCB_SHAPE* aShape, int aLayer )
}
case SHAPE_T::ARC:
{
double startAngle;
double endAngle;
aShape->CalcArcAngles( startAngle, endAngle );
if( sketch )
{
m_gal->DrawArcSegment( aShape->GetCenter(), aShape->GetRadius(),
DECIDEG2RAD( aShape->GetArcAngleStart() ),
DECIDEG2RAD( aShape->GetArcAngleStart() + aShape->GetArcAngle() ), // Change this
thickness, m_maxError );
DEG2RAD( startAngle ), DEG2RAD( endAngle ), thickness,
m_maxError );
}
else
{
@ -1421,11 +1425,11 @@ void PCB_PAINTER::draw( const PCB_SHAPE* aShape, int aLayer )
m_gal->SetIsStroke( false );
m_gal->DrawArcSegment( aShape->GetCenter(), aShape->GetRadius(),
DECIDEG2RAD( aShape->GetArcAngleStart() ),
DECIDEG2RAD( aShape->GetArcAngleStart() + aShape->GetArcAngle() ), // Change this
thickness, m_maxError );
DEG2RAD( startAngle ), DEG2RAD( endAngle ), thickness,
m_maxError );
}
break;
}
case SHAPE_T::CIRCLE:
if( sketch )

View File

@ -820,11 +820,11 @@ void PCB_IO::format( const PCB_SHAPE* aShape, int aNestLevel ) const
break;
case SHAPE_T::ARC:
m_out->Print( aNestLevel, "(gr_arc%s (start %s) (end %s) (angle %s)",
m_out->Print( aNestLevel, "(gr_arc%s (start %s) (mid %s) (end %s)",
locked.c_str(),
FormatInternalUnits( aShape->GetCenter() ).c_str(),
FormatInternalUnits( aShape->GetStart() ).c_str(),
FormatAngle( aShape->GetArcAngle() ).c_str() );
FormatInternalUnits( aShape->GetArcMid() ).c_str(),
FormatInternalUnits( aShape->GetEnd() ).c_str() );
break;
case SHAPE_T::POLY:
@ -954,11 +954,11 @@ void PCB_IO::format( const FP_SHAPE* aFPShape, int aNestLevel ) const
break;
case SHAPE_T::ARC:
m_out->Print( aNestLevel, "(fp_arc%s (start %s) (end %s) (angle %s)",
m_out->Print( aNestLevel, "(fp_arc%s (start %s) (mid %s) (end %s)",
locked.c_str(),
FormatInternalUnits( aFPShape->GetCenter0() ).c_str(),
FormatInternalUnits( aFPShape->GetStart0() ).c_str(),
FormatAngle( aFPShape->GetArcAngle() ).c_str() );
FormatInternalUnits( aFPShape->GetArcMid0() ).c_str(),
FormatInternalUnits( aFPShape->GetEnd0() ).c_str() );
break;
case SHAPE_T::POLY:
@ -1630,10 +1630,10 @@ void PCB_IO::format( const PAD* aPad, int aNestLevel ) const
break;
case SHAPE_T::ARC:
m_out->Print( nested_level, "(gr_arc (start %s) (end %s) (angle %s)",
FormatInternalUnits( primitive->GetCenter() ).c_str(),
m_out->Print( aNestLevel, "(gr_arc (start %s) (mid %s) (end %s)",
FormatInternalUnits( primitive->GetStart() ).c_str(),
FormatAngle( primitive->GetArcAngle() ).c_str() );
FormatInternalUnits( primitive->GetArcMid() ).c_str(),
FormatInternalUnits( primitive->GetEnd() ).c_str() );
break;
case SHAPE_T::CIRCLE:

View File

@ -101,9 +101,11 @@ class PCB_TEXT;
//#define SEXPR_BOARD_FILE_VERSION 20210623 // Add support for reading/writing arcs in polygons
//#define SEXPR_BOARD_FILE_VERSION 20210722 // Reading/writing group locked flags
//#define SEXPR_BOARD_FILE_VERSION 20210824 // Opacity in 3D colors
#define SEXPR_BOARD_FILE_VERSION 20210925 // Locked flag for fp_text
//#define SEXPR_BOARD_FILE_VERSION 20210925 // Locked flag for fp_text
#define SEXPR_BOARD_FILE_VERSION 20211014 // Arc formatting
#define BOARD_FILE_HOST_VERSION 20200825 ///< Earlier files than this include the host tag
#define LEGACY_ARC_FORMATTING 20210925 ///< These were the last to use old arc formatting
#define CTL_OMIT_PAD_NETS (1 << 1) ///< Omit pads net names (useless in library)
#define CTL_OMIT_TSTAMPS (1 << 2) ///< Omit component time stamp (useless in library)

View File

@ -2385,25 +2385,60 @@ PCB_SHAPE* PCB_PARSER::parsePCB_SHAPE()
token = NextTok();
// the start keyword actually gives the arc center
// Allows also T_center for future change
if( token != T_start && token != T_center )
Expecting( T_start );
if( m_requiredVersion <= LEGACY_ARC_FORMATTING )
{
// In legacy files the start keyword actually gives the arc center...
if( token != T_start )
Expecting( T_start );
pt.x = parseBoardUnits( "X coordinate" );
pt.y = parseBoardUnits( "Y coordinate" );
shape->SetCenter( pt );
NeedRIGHT();
NeedLEFT();
token = NextTok();
pt.x = parseBoardUnits( "X coordinate" );
pt.y = parseBoardUnits( "Y coordinate" );
shape->SetCenter( pt );
NeedRIGHT();
NeedLEFT();
token = NextTok();
if( token != T_end ) // the end keyword actually gives the starting point of the arc
Expecting( T_end );
// ... and the end keyword gives the start point of the arc
if( token != T_end )
Expecting( T_end );
pt.x = parseBoardUnits( "X coordinate" );
pt.y = parseBoardUnits( "Y coordinate" );
shape->SetStart( pt );
NeedRIGHT();
}
else
{
wxPoint start, mid, end;
if( token != T_start )
Expecting( T_start );
start.x = parseBoardUnits( "X coordinate" );
start.y = parseBoardUnits( "Y coordinate" );
NeedRIGHT();
NeedLEFT();
token = NextTok();
if( token != T_mid )
Expecting( T_mid );
mid.x = parseBoardUnits( "X coordinate" );
mid.y = parseBoardUnits( "Y coordinate" );
NeedRIGHT();
NeedLEFT();
token = NextTok();
if( token != T_end )
Expecting( T_end );
end.x = parseBoardUnits( "X coordinate" );
end.y = parseBoardUnits( "Y coordinate" );
NeedRIGHT();
shape->SetArcGeometry( start, mid, end );
}
pt.x = parseBoardUnits( "X coordinate" );
pt.y = parseBoardUnits( "Y coordinate" );
shape->SetStart( pt );
NeedRIGHT();
break;
case T_gr_circle:
@ -2583,12 +2618,20 @@ PCB_SHAPE* PCB_PARSER::parsePCB_SHAPE()
switch( token )
{
case T_angle:
angle = parseDouble( "segment angle" ) * 10.0;
if( m_requiredVersion <= LEGACY_ARC_FORMATTING )
{
angle = parseAngle( "arc angle" );
if( shape->GetShape() == SHAPE_T::ARC )
shape->SetArcAngleAndEnd( angle );
if( shape->GetShape() == SHAPE_T::ARC )
shape->SetArcAngleAndEnd( angle, true );
NeedRIGHT();
}
else
{
Unexpected( T_angle );
}
NeedRIGHT();
break;
case T_layer:
@ -2707,7 +2750,7 @@ PCB_TEXT* PCB_PARSER::parsePCB_TEXT()
if( token == T_NUMBER )
{
text->SetTextAngle( parseDouble() * 10.0 );
text->SetTextAngle( parseAngle() );
NeedRIGHT();
}
else if( token != T_RIGHT )
@ -3272,7 +3315,7 @@ FOOTPRINT* PCB_PARSER::parseFOOTPRINT_unchecked( wxArrayString* aInitialComments
if( token == T_NUMBER )
{
footprint->SetOrientation( parseDouble() * 10.0 );
footprint->SetOrientation( parseAngle() );
NeedRIGHT();
}
else if( token != T_RIGHT )
@ -3420,24 +3463,6 @@ FOOTPRINT* PCB_PARSER::parseFOOTPRINT_unchecked( wxArrayString* aInitialComments
}
case T_fp_arc:
{
FP_SHAPE* shape = parseFP_SHAPE();
// Drop 0 and NaN angles as these can corrupt/crash the schematic
if( std::isnormal( shape->GetArcAngle() ) )
{
shape->SetParent( footprint.get() );
shape->SetDrawCoord();
footprint->Add( shape, ADD_MODE::APPEND );
}
else
{
delete shape;
}
break;
}
case T_fp_circle:
case T_fp_curve:
case T_fp_rect:
@ -3574,7 +3599,7 @@ FP_TEXT* PCB_PARSER::parseFP_TEXT()
if( CurTok() == T_NUMBER )
{
text->SetTextAngle( parseDouble() * 10.0 );
text->SetTextAngle( parseAngle() );
NextTok();
}
@ -3652,33 +3677,68 @@ FP_SHAPE* PCB_PARSER::parseFP_SHAPE()
token = NextTok();
// the start keyword actually gives the arc center
// Allows also T_center for future change
if( token != T_start && token != T_center )
Expecting( T_start );
if( m_requiredVersion <= LEGACY_ARC_FORMATTING )
{
// In legacy files the start keyword actually gives the arc center...
if( token != T_start )
Expecting( T_start );
pt.x = parseBoardUnits( "X coordinate" );
pt.y = parseBoardUnits( "Y coordinate" );
shape->SetCenter0( pt );
NeedRIGHT();
NeedLEFT();
token = NextTok();
pt.x = parseBoardUnits( "X coordinate" );
pt.y = parseBoardUnits( "Y coordinate" );
shape->SetCenter0( pt );
NeedRIGHT();
NeedLEFT();
token = NextTok();
if( token != T_end ) // end keyword actually gives the starting point of the arc
Expecting( T_end );
// ... and the end keyword gives the start point of the arc
if( token != T_end )
Expecting( T_end );
pt.x = parseBoardUnits( "X coordinate" );
pt.y = parseBoardUnits( "Y coordinate" );
shape->SetStart0( pt );
NeedRIGHT();
NeedLEFT();
token = NextTok();
pt.x = parseBoardUnits( "X coordinate" );
pt.y = parseBoardUnits( "Y coordinate" );
shape->SetStart0( pt );
NeedRIGHT();
NeedLEFT();
token = NextTok();
if( token != T_angle )
Expecting( T_angle );
if( token != T_angle )
Expecting( T_angle );
shape->SetArcAngleAndEnd0( parseAngle( "segment angle" ), true );
NeedRIGHT();
}
else
{
wxPoint start, mid, end;
if( token != T_start )
Expecting( T_start );
start.x = parseBoardUnits( "X coordinate" );
start.y = parseBoardUnits( "Y coordinate" );
NeedRIGHT();
NeedLEFT();
token = NextTok();
if( token != T_mid )
Expecting( T_mid );
mid.x = parseBoardUnits( "X coordinate" );
mid.y = parseBoardUnits( "Y coordinate" );
NeedRIGHT();
NeedLEFT();
token = NextTok();
if( token != T_end )
Expecting( T_end );
end.x = parseBoardUnits( "X coordinate" );
end.y = parseBoardUnits( "Y coordinate" );
NeedRIGHT();
shape->SetArcGeometry0( start, mid, end );
}
shape->SetArcAngleAndEnd0( parseDouble( "segment angle" ) * 10.0 );
NeedRIGHT();
break;
case T_fp_circle:
@ -4053,7 +4113,7 @@ PAD* PCB_PARSER::parsePAD( FOOTPRINT* aParent )
if( token == T_NUMBER )
{
pad->SetOrientation( parseDouble() * 10.0 );
pad->SetOrientation( parseAngle() );
NeedRIGHT();
}
else if( token != T_RIGHT )

View File

@ -311,6 +311,16 @@ private:
return parseDouble( GetTokenText( aToken ) );
}
/**
* Parse angles into deci-degrees.
*/
double parseAngle() { return parseDouble() * 10.0; }
inline double parseAngle( const char* aExpected )
{
return parseDouble( aExpected ) * 10.0;
}
int parseBoardUnits();
int parseBoardUnits( const char* aExpected );

View File

@ -804,15 +804,21 @@ IMAGE* SPECCTRA_DB::makeIMAGE( BOARD* aBoard, FOOTPRINT* aFootprint )
wxPoint arc_centre = graphic->GetCenter0();
double radius = graphic->GetRadius() + graphic->GetWidth()/2;
double arcStartDeg = graphic->GetArcAngleStart() / 10.0;
double arcAngleDeg = graphic->GetArcAngle() / 10.0;
VECTOR2I startRadial = graphic->GetStart() - graphic->GetCenter();
double arcStartDeg = ArcTangente( startRadial.y, startRadial.x ) / 10;
NORMALIZE_ANGLE_DEGREES_POS( arcStartDeg );
// For some obscure reason, FreeRouter does not show the same polygonal
// shape for polygons CW and CCW. So used only the order of corners
// giving the best look.
if( arcAngleDeg < 0 )
{
arcStartDeg = graphic->GetArcAngleEnd() / 10.0;
VECTOR2I endRadial = graphic->GetEnd() - graphic->GetCenter();
arcStartDeg = ArcTangente( endRadial.y, endRadial.x ) / 10;
NORMALIZE_ANGLE_DEGREES_POS( arcStartDeg );
arcAngleDeg = -arcAngleDeg;
}

View File

@ -815,7 +815,6 @@ int CONVERT_TOOL::SegmentToArc( const TOOL_EVENT& aEvent )
arc->SetCenter( wxPoint( center ) );
arc->SetStart( wxPoint( start ) );
arc->SetEnd( wxPoint( end ) );
arc->SetArcAngle( CalcArcAngle( start, mid, end ) );
commit.Add( arc );
}

View File

@ -1721,8 +1721,6 @@ static void updateArcFromConstructionMgr( const KIGFX::PREVIEW::ARC_GEOM_MANAGER
aArc.SetStart( (wxPoint) vec );
vec = aMgr.GetEndRadiusEnd();
aArc.SetEnd( (wxPoint) vec );
aArc.SetArcAngle( RAD2DECIDEG( -aMgr.GetSubtended() ) );
}

View File

@ -627,7 +627,6 @@ PCB_LAYER_ID PAD_TOOL::explodePad( PAD* aPad )
shape->SetStart( primitive->GetStart() );
shape->SetEnd( primitive->GetEnd() );
shape->SetCenter( primitive->GetCenter() );
shape->SetArcAngle( primitive->GetArcAngle() );
break;
case SHAPE_T::BEZIER:
@ -766,7 +765,6 @@ void PAD_TOOL::recombinePad( PAD* aPad )
pcbShape->SetStart( fpShape->GetStart() );
pcbShape->SetEnd( fpShape->GetEnd() );
pcbShape->SetCenter( fpShape->GetCenter() );
pcbShape->SetArcAngle( fpShape->GetArcAngle() );
break;
case SHAPE_T::BEZIER:

View File

@ -602,13 +602,8 @@ void PCB_POINT_EDITOR::editArcEndpointKeepTangent( PCB_SHAPE* aArc, const VECTOR
VECTOR2I start = aStart;
VECTOR2I end = aEnd;
VECTOR2I center = aCenter;
VECTOR2D startLine = aStart - aCenter;
VECTOR2D endLine = aEnd - aCenter;
double newAngle = RAD2DECIDEG( endLine.Angle() - startLine.Angle() );
bool clockwise;
bool movingStart;
bool arcValid = true;
bool movingStart;
bool arcValid = true;
VECTOR2I p1, p2, p3;
// p1 does not move, p2 does.
@ -723,24 +718,15 @@ void PCB_POINT_EDITOR::editArcEndpointKeepTangent( PCB_SHAPE* aArc, const VECTOR
// 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->GetArcAngle() > 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;
@ -748,24 +734,14 @@ void PCB_POINT_EDITOR::editArcEndpointKeepTangent( PCB_SHAPE* aArc, const VECTOR
center = v4 + aCenter;
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 )
{
aArc->SetCenter( (wxPoint) aCenter );
aArc->SetArcAngle( newAngle );
aArc->SetCenter( (wxPoint) center );
if( movingStart )
aArc->SetStart( (wxPoint) aStart );
aArc->SetStart( (wxPoint) start );
else
aArc->SetEnd( (wxPoint) aEnd );
aArc->SetEnd( (wxPoint) end );
}
}
}
@ -878,7 +854,6 @@ void PCB_POINT_EDITOR::editArcEndpointKeepCenter( PCB_SHAPE* aArc, const VECTOR2
const VECTOR2I& aEnd,
const VECTOR2I& aCursor ) const
{
bool clockwise;
bool movingStart;
VECTOR2I p1, p2;
@ -932,19 +907,7 @@ void PCB_POINT_EDITOR::editArcEndpointKeepCenter( PCB_SHAPE* aArc, const VECTOR2
p1 = p1 + aCenter;
p2 = p2 + aCenter;
clockwise = aArc->GetArcAngle() > 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->SetCenter( (wxPoint) aCenter );
aArc->SetArcAngle( newAngle );
if( movingStart )
aArc->SetStart( (wxPoint) aStart );