From fa8655511e27be8e54f458727610d52728ebe956 Mon Sep 17 00:00:00 2001 From: Jon Evans Date: Sat, 29 Aug 2020 12:12:40 -0400 Subject: [PATCH] Enable converting segments to arcs Fixes https://gitlab.com/kicad/code/kicad/-/issues/1957 --- libs/kimath/include/trigo.h | 5 +++++ libs/kimath/src/trigo.cpp | 29 ++++++++++++++++++++++++ pcbnew/tools/convert_tool.cpp | 42 +++++++++++++++++++++-------------- pcbnew/tools/point_editor.cpp | 18 +-------------- 4 files changed, 60 insertions(+), 34 deletions(-) diff --git a/libs/kimath/include/trigo.h b/libs/kimath/include/trigo.h index fa319993f8..50285866a1 100644 --- a/libs/kimath/include/trigo.h +++ b/libs/kimath/include/trigo.h @@ -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 diff --git a/libs/kimath/src/trigo.cpp b/libs/kimath/src/trigo.cpp index 5ee1e13095..5e93e0718f 100644 --- a/libs/kimath/src/trigo.cpp +++ b/libs/kimath/src/trigo.cpp @@ -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; +} diff --git a/pcbnew/tools/convert_tool.cpp b/pcbnew/tools/convert_tool.cpp index e9b49a0c25..480ffd05e2 100644 --- a/pcbnew/tools/convert_tool.cpp +++ b/pcbnew/tools/convert_tool.cpp @@ -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& 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 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 = 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( 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 ); } diff --git a/pcbnew/tools/point_editor.cpp b/pcbnew/tools/point_editor.cpp index 977ece06f7..fa5d27ace3 100644 --- a/pcbnew/tools/point_editor.cpp +++ b/pcbnew/tools/point_editor.cpp @@ -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