Enable converting segments to arcs

Fixes https://gitlab.com/kicad/code/kicad/-/issues/1957
This commit is contained in:
Jon Evans 2020-08-29 12:12:40 -04:00
parent a390b19f97
commit fa8655511e
4 changed files with 60 additions and 34 deletions

View File

@ -112,6 +112,11 @@ const VECTOR2I GetArcCenter( const VECTOR2I& aStart, const VECTOR2I& aMid, const
const VECTOR2D GetArcCenter( const VECTOR2D& aStart, const VECTOR2D& aMid, const VECTOR2D& aEnd );
const wxPoint GetArcCenter( const wxPoint& aStart, const wxPoint& aMid, const wxPoint& aEnd );
/**
* Returns the subtended angle for a given arc
*/
double GetArcAngle( 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

@ -442,3 +442,32 @@ const wxPoint GetArcCenter( const wxPoint& aStart, const wxPoint& aMid, const wx
return iCenter;
}
double GetArcAngle( const VECTOR2I& aStart, const VECTOR2I& aMid, const VECTOR2I& aEnd )
{
VECTOR2I center = GetArcCenter( aStart, aMid, aEnd );
// Check if the new arc is CW or CCW
VECTOR2D startLine = aStart - center;
VECTOR2D endLine = aEnd - center;
double angle = 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 );
bool clockwise = ( ( v1.Angle() - v2.Angle() ) > 0 );
// Normalize the angle
if( clockwise && angle < 0.0 )
angle += 3600.0;
else if( !clockwise && angle > 0.0 )
angle -= 3600.0;
return angle;
}

View File

@ -73,7 +73,7 @@ bool CONVERT_TOOL::Init()
// Create a context menu and make it available through selection tool
m_menu = new CONDITIONAL_MENU( this );
m_menu->SetIcon( refresh_xpm );
m_menu->SetTitle( _( "Convert" ) );
m_menu->SetTitle( _( "Convert..." ) );
static KICAD_T convertableTracks[] = { PCB_TRACE_T, PCB_ARC_T, EOT };
@ -87,15 +87,17 @@ bool CONVERT_TOOL::Init()
auto anyPolys = ( S_C::OnlyType( PCB_ZONE_AREA_T ) ||
P_S_C::OnlyGraphicShapeTypes( { S_POLYGON } ) );
auto showConvert = anyPolys || anyLines;
auto lineToArc = P_S_C::OnlyGraphicShapeTypes( { S_SEGMENT } ) ||
S_C::OnlyType( PCB_TRACE_T );
auto showConvert = anyPolys || anyLines || lineToArc;
m_menu->AddItem( PCB_ACTIONS::convertToPoly, anyLines );
m_menu->AddItem( PCB_ACTIONS::convertToZone, anyLines );
m_menu->AddItem( PCB_ACTIONS::convertToKeepout, anyLines );
m_menu->AddItem( PCB_ACTIONS::convertToLines, anyPolys );
m_menu->AddItem( PCB_ACTIONS::convertToTracks, anyPolys );
// Not yet
// m_menu->AddItem( PCB_ACTIONS::convertToArc, lineToArc );
m_menu->AddItem( PCB_ACTIONS::convertToArc, lineToArc );
for( std::shared_ptr<ACTION_MENU>& subMenu : m_selectionTool->GetToolMenu().GetSubMenus() )
{
@ -491,13 +493,18 @@ int CONVERT_TOOL::SegmentToArc( const TOOL_EVENT& aEvent )
} );
EDA_ITEM* source = selection.Front();
wxPoint start, end, mid;
VECTOR2I start, end, mid;
if( OPT<SEG> optSeg = getStartEndPoints( source ) )
// Offset the midpoint along the normal a little bit so that it's more obviously an arc
const double offsetRatio = 0.1;
if( OPT<SEG> seg = getStartEndPoints( source ) )
{
start = wxPoint( optSeg->A );
end = wxPoint( optSeg->B );
mid = wxPoint( optSeg->Center() );
start = seg->A;
end = seg->B;
VECTOR2I normal = ( seg->B - seg->A ).Perpendicular().Resize( offsetRatio * seg->Length() );
mid = seg->Center() + normal;
}
else
return -1;
@ -517,16 +524,17 @@ int CONVERT_TOOL::SegmentToArc( const TOOL_EVENT& aEvent )
DRAWSEGMENT* line = static_cast<DRAWSEGMENT*>( source );
DRAWSEGMENT* arc = new DRAWSEGMENT( parent );
wxPoint center = GetArcCenter( start, mid, end );
VECTOR2I center = GetArcCenter( start, mid, end );
arc->SetShape( S_ARC );
arc->SetLayer( layer );
arc->SetWidth( line->GetWidth() );
arc->SetArcStart( start );
arc->SetCenter( center );
// TODO(JE): once !325 is merged
//arc->SetArcEnd( end );
arc->SetCenter( wxPoint( center ) );
arc->SetArcStart( wxPoint( start ) );
arc->SetAngle( GetArcAngle( start, mid, end ) );
arc->SetArcEnd( wxPoint( end ) );
commit.Add( arc );
}
else
@ -537,9 +545,9 @@ int CONVERT_TOOL::SegmentToArc( const TOOL_EVENT& aEvent )
arc->SetLayer( layer );
arc->SetWidth( line->GetWidth() );
arc->SetStart( start );
arc->SetMid( mid );
arc->SetEnd( end );
arc->SetStart( wxPoint( start ) );
arc->SetMid( wxPoint( mid ) );
arc->SetEnd( wxPoint( end ) );
commit.Add( arc );
}

View File

@ -657,23 +657,7 @@ void POINT_EDITOR::updateItem() const
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;
newAngle = GetArcAngle( start, mid, end );
// Accuracy test
// First, get the angle