ADDED: Angle preview for polygon mod

Shows angle for the current and adjacent corners and whether they are
congruent
This commit is contained in:
Seth Hillbrand 2025-08-31 10:37:13 -07:00
parent c6c8e19e39
commit bbc762a546
10 changed files with 308 additions and 0 deletions

View File

@ -542,6 +542,7 @@ set( COMMON_DRAWING_SHEET_SRCS
set( COMMON_PREVIEW_ITEMS_SRCS
preview_items/anchor_debug.cpp
preview_items/angle_item.cpp
preview_items/arc_assistant.cpp
preview_items/arc_geom_manager.cpp
preview_items/bezier_assistant.cpp

View File

@ -0,0 +1,208 @@
/*
* This program source code file is part of KICAD, a free EDA CAD application.
*
* Copyright (C) 2024 The KiCad Developers, see AUTHORS.txt for contributors.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, you may find one here:
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
* or you may search the http://www.gnu.org website for the version 2 license,
* or you may write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include <preview_items/angle_item.h>
#include <tool/edit_points.h>
#include <gal/graphics_abstraction_layer.h>
#include <gal/painter.h>
#include <view/view.h>
#include <geometry/seg.h>
#include <preview_items/preview_utils.h>
#include <font/font.h>
#include <wx/string.h>
#include <algorithm>
#include <map>
using namespace KIGFX::PREVIEW;
ANGLE_ITEM::ANGLE_ITEM( EDIT_POINTS* aPoints ) :
SIMPLE_OVERLAY_ITEM(),
m_points( aPoints )
{
}
const BOX2I ANGLE_ITEM::ViewBBox() const
{
if( m_points )
return m_points->ViewBBox();
else
return BOX2I();
}
void ANGLE_ITEM::drawPreviewShape( KIGFX::VIEW* aView ) const
{
if( !m_points )
return;
KIGFX::GAL* gal = aView->GetGAL();
KIGFX::RENDER_SETTINGS* settings = aView->GetPainter()->GetSettings();
KIGFX::COLOR4D drawColor = settings->GetLayerColor( LAYER_AUX_ITEMS );
double size = aView->ToWorld( EDIT_POINT::POINT_SIZE ) / 2.0;
double borderSize = aView->ToWorld( EDIT_POINT::BORDER_SIZE );
double radius = size * 10.0;
gal->SetStrokeColor( drawColor );
gal->SetFillColor( drawColor );
gal->SetIsFill( false );
gal->SetLineWidth( borderSize * 2.0 );
gal->SetGlyphSize( VECTOR2I( radius / 2, radius / 2 ) );
std::vector<const EDIT_POINT*> anglePoints;
for( unsigned i = 0; i < m_points->PointsSize(); ++i )
{
const EDIT_POINT& point = m_points->Point( i );
if( point.IsActive() || point.IsHover() )
{
anglePoints.push_back( &point );
EDIT_POINT* prev = m_points->Previous( point );
EDIT_POINT* next = m_points->Next( point );
if( prev )
anglePoints.push_back( prev );
if( next )
anglePoints.push_back( next );
}
}
std::sort( anglePoints.begin(), anglePoints.end() );
anglePoints.erase( std::unique( anglePoints.begin(), anglePoints.end() ), anglePoints.end() );
// First pass: collect all angles and identify congruent ones
struct AngleInfo
{
const EDIT_POINT* point;
EDA_ANGLE angle;
VECTOR2D center;
VECTOR2D v1, v2;
EDA_ANGLE start;
EDA_ANGLE sweep;
EDA_ANGLE mid;
bool isRightAngle;
};
std::vector<AngleInfo> angles;
std::map<int, int> angleCount; // Map angle (in tenths of degree) to count
for( const EDIT_POINT* pt : anglePoints )
{
EDIT_POINT* prev = m_points->Previous( *pt );
EDIT_POINT* next = m_points->Next( *pt );
if( !( prev && next ) )
continue;
SEG seg1( pt->GetPosition(), prev->GetPosition() );
SEG seg2( pt->GetPosition(), next->GetPosition() );
// Calculate the interior angle (0-180 degrees) instead of the smallest angle
VECTOR2I c = pt->GetPosition();
VECTOR2I v1 = prev->GetPosition() - c;
VECTOR2I v2 = next->GetPosition() - c;
EDA_ANGLE angle = seg2.Angle( seg1 );
EDA_ANGLE start = EDA_ANGLE( VECTOR2D( v1 ) );
EDA_ANGLE sweep = ( EDA_ANGLE( VECTOR2D( v2 ) ) - start ).Normalize180();
EDA_ANGLE mid = start + sweep / 2.0;
VECTOR2D center( c );
AngleInfo info;
info.point = pt;
info.angle = angle;
info.center = center;
info.v1 = v1;
info.v2 = v2;
info.start = start;
info.sweep = sweep;
info.mid = mid;
info.isRightAngle = ( angle.AsTenthsOfADegree() == 900 );
angles.push_back( info );
angleCount[angle.AsTenthsOfADegree()]++;
}
// Second pass: draw the angles with congruence markings
for( const AngleInfo& angleInfo : angles )
{
bool isCongruent = angleCount[angleInfo.angle.AsTenthsOfADegree()] > 1;
if( angleInfo.isRightAngle )
{
VECTOR2D u1 = VECTOR2D( angleInfo.v1 ).Resize( radius );
VECTOR2D u2 = VECTOR2D( angleInfo.v2 ).Resize( radius );
VECTOR2D p1 = angleInfo.center + u1;
VECTOR2D p2 = angleInfo.center + u2;
VECTOR2D corner = angleInfo.center + u1 + u2;
// Draw the primary right angle marker
gal->DrawLine( p1, corner );
gal->DrawLine( p2, corner );
// Draw congruence marking for right angles
if( isCongruent )
{
double innerRadius = radius * 0.6;
VECTOR2D u1_inner = VECTOR2D( angleInfo.v1 ).Resize( innerRadius );
VECTOR2D u2_inner = VECTOR2D( angleInfo.v2 ).Resize( innerRadius );
VECTOR2D p1_inner = angleInfo.center + u1_inner;
VECTOR2D p2_inner = angleInfo.center + u2_inner;
VECTOR2D corner_inner = angleInfo.center + u1_inner + u2_inner;
gal->DrawLine( p1_inner, corner_inner );
gal->DrawLine( p2_inner, corner_inner );
}
}
else
{
// Draw the primary arc
gal->DrawArc( angleInfo.center, radius, angleInfo.start, angleInfo.sweep );
// Draw congruence marking for non-right angles
if( isCongruent )
{
double innerRadius = radius * 0.7;
gal->DrawArc( angleInfo.center, innerRadius, angleInfo.start, angleInfo.sweep );
}
}
VECTOR2D textDir( angleInfo.mid.Cos(), angleInfo.mid.Sin() );
wxString label = wxString::Format( wxT( "%.1f°" ), angleInfo.angle.AsDegrees() );
// Calculate actual text dimensions to ensure proper clearance
KIFONT::FONT* font = KIFONT::FONT::GetFont();
VECTOR2I textSize = font->StringBoundaryLimits( label, gal->GetGlyphSize(), 0, false, false,
KIFONT::METRICS::Default() );
// Calculate offset based on text direction - use width for horizontal, height for vertical
double absX = std::abs( textDir.x );
double absY = std::abs( textDir.y );
double textClearance = ( absX * textSize.x + absY * textSize.y ) / 2.0;
double textOffset = radius + borderSize + textClearance;
VECTOR2I textPos = angleInfo.center + VECTOR2I( textDir * textOffset );
gal->BitmapText( label, textPos, ANGLE_HORIZONTAL );
}
}

View File

@ -27,6 +27,10 @@
#include <gal/color4d.h>
#include <gal/painter.h>
#include <math/util.h> // for KiROUND
#include <geometry/seg.h>
#include <wx/string.h>
#include <vector>
#include <algorithm>
#include "tool/edit_points.h"
@ -331,4 +335,5 @@ void EDIT_POINTS::ViewDraw( int aLayer, KIGFX::VIEW* aView ) const
gal->DrawLine( line.GetOrigin().GetPosition(), line.GetEnd().GetPosition() );
}
}
}

View File

@ -32,6 +32,7 @@
#include <gal/graphics_abstraction_layer.h>
#include <geometry/seg.h>
#include <geometry/shape_utils.h>
#include <preview_items/angle_item.h>
#include <tool/point_editor_behavior.h>
#include <tools/sch_actions.h>
#include <tools/sch_selection_tool.h>
@ -1068,7 +1069,9 @@ int SCH_POINT_EDITOR::Main( const TOOL_EVENT& aEvent )
controls->ShowCursor( true );
makePointsAndBehavior( item );
m_angleItem = std::make_unique<KIGFX::PREVIEW::ANGLE_ITEM>( m_editPoints.get() );
view->Add( m_editPoints.get() );
view->Add( m_angleItem.get() );
setEditedPoint( nullptr );
updateEditedPoint( aEvent );
bool inDrag = false;
@ -1188,8 +1191,10 @@ int SCH_POINT_EDITOR::Main( const TOOL_EVENT& aEvent )
if( m_editPoints )
{
view->Remove( m_editPoints.get() );
view->Remove( m_angleItem.get() );
m_editPoints.reset();
m_angleItem.reset();
m_frame->GetCanvas()->Refresh();
}
@ -1226,6 +1231,7 @@ void SCH_POINT_EDITOR::updatePoints()
m_editBehavior->UpdatePoints( *m_editPoints );
getView()->Update( m_editPoints.get() );
getView()->Update( m_angleItem.get() );
}

View File

@ -29,6 +29,8 @@
#include <tool/edit_points.h>
#include <tool/selection.h>
namespace KIGFX { namespace PREVIEW { class ANGLE_ITEM; } }
class SCH_SELECTION_TOOL;
class POINT_EDIT_BEHAVIOR;
class SCH_BASE_FRAME;
@ -121,6 +123,7 @@ private:
///< Currently available edit points.
std::shared_ptr<EDIT_POINTS> m_editPoints;
std::unique_ptr<KIGFX::PREVIEW::ANGLE_ITEM> m_angleItem;
///< Current item-specific edit behavior.
std::unique_ptr<POINT_EDIT_BEHAVIOR> m_editBehavior;

View File

@ -0,0 +1,57 @@
/*
* This program source code file is part of KICAD, a free EDA CAD application.
*
* Copyright (C) 2024 The KiCad Developers, see AUTHORS.txt for contributors.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, you may find one here:
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
* or you may search the http://www.gnu.org website for the version 2 license,
* or you may write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#ifndef PREVIEW_ANGLE_ITEM_H
#define PREVIEW_ANGLE_ITEM_H
#include <preview_items/simple_overlay_item.h>
class EDIT_POINTS;
namespace KIGFX
{
namespace PREVIEW
{
class ANGLE_ITEM : public SIMPLE_OVERLAY_ITEM
{
public:
ANGLE_ITEM( EDIT_POINTS* aPoints );
const BOX2I ViewBBox() const override;
void SetEditPoints( EDIT_POINTS* aPoints )
{
m_points = aPoints;
}
private:
void drawPreviewShape( KIGFX::VIEW* aView ) const override;
EDIT_POINTS* m_points;
};
} // PREVIEW
} // KIGFX
#endif

View File

@ -32,6 +32,7 @@ using namespace std::placeholders;
#include <tool/actions.h>
#include <view/view_controls.h>
#include <gal/graphics_abstraction_layer.h>
#include <preview_items/angle_item.h>
#include <confirm.h>
#include <bitmaps.h>
#include <status_popup.h>
@ -174,7 +175,10 @@ int PL_POINT_EDITOR::Main( const TOOL_EVENT& aEvent )
if( !m_editPoints )
return 0;
m_angleItem = std::make_unique<KIGFX::PREVIEW::ANGLE_ITEM>( m_editPoints.get() );
getView()->Add( m_editPoints.get() );
getView()->Add( m_angleItem.get() );
setEditedPoint( nullptr );
updateEditedPoint( aEvent );
bool inDrag = false;
@ -252,11 +256,13 @@ int PL_POINT_EDITOR::Main( const TOOL_EVENT& aEvent )
if( m_editPoints )
{
getView()->Remove( m_editPoints.get() );
getView()->Remove( m_angleItem.get() );
if( modified )
m_frame->OnModify();
m_editPoints.reset();
m_angleItem.reset();
m_frame->GetCanvas()->Refresh();
}
@ -462,6 +468,7 @@ void PL_POINT_EDITOR::updatePoints()
}
getView()->Update( m_editPoints.get() );
getView()->Update( m_angleItem.get() );
}

View File

@ -30,6 +30,8 @@
#include <tool/selection.h>
#include <tool/tool_menu.h>
namespace KIGFX { namespace PREVIEW { class ANGLE_ITEM; } }
class PL_SELECTION_TOOL;
class PL_EDITOR_FRAME;
@ -99,6 +101,7 @@ private:
///< Currently available edit points.
std::shared_ptr<EDIT_POINTS> m_editPoints;
std::unique_ptr<KIGFX::PREVIEW::ANGLE_ITEM> m_angleItem;
};
#endif // PL_POINT_EDITOR_H

View File

@ -41,6 +41,7 @@ using namespace std::placeholders;
#include <tool/tool_manager.h>
#include <tool/point_editor_behavior.h>
#include <tool/selection_conditions.h>
#include <preview_items/angle_item.h>
#include <tools/pcb_actions.h>
#include <tools/pcb_selection_tool.h>
#include <tools/pcb_point_editor.h>
@ -2011,6 +2012,8 @@ int PCB_POINT_EDITOR::OnSelectionChange( const TOOL_EVENT& aEvent )
if( !m_editPoints )
return 0;
m_angleItem = std::make_unique<KIGFX::PREVIEW::ANGLE_ITEM>( m_editPoints.get() );
m_preview.FreeItems();
getView()->Add( &m_preview );
@ -2019,6 +2022,7 @@ int PCB_POINT_EDITOR::OnSelectionChange( const TOOL_EVENT& aEvent )
m_preview.Add( radiusHelper );
getView()->Add( m_editPoints.get() );
getView()->Add( m_angleItem.get() );
setEditedPoint( nullptr );
updateEditedPoint( aEvent );
bool inDrag = false;
@ -2047,7 +2051,10 @@ int PCB_POINT_EDITOR::OnSelectionChange( const TOOL_EVENT& aEvent )
updateEditedPoint( *evt );
if( prevHover != m_hoveredPoint )
{
getView()->Update( m_editPoints.get() );
getView()->Update( m_angleItem.get() );
}
if( evt->IsDrag( BUT_LEFT ) && m_editedPoint )
{
@ -2191,6 +2198,7 @@ int PCB_POINT_EDITOR::OnSelectionChange( const TOOL_EVENT& aEvent )
}
getView()->Update( m_editPoints.get() );
getView()->Update( m_angleItem.get() );
}
else if( inDrag && evt->IsMouseUp( BUT_LEFT ) )
{
@ -2198,6 +2206,7 @@ int PCB_POINT_EDITOR::OnSelectionChange( const TOOL_EVENT& aEvent )
{
m_editedPoint->SetActive( false );
getView()->Update( m_editPoints.get() );
getView()->Update( m_angleItem.get() );
}
radiusHelper->Hide();
@ -2269,8 +2278,11 @@ int PCB_POINT_EDITOR::OnSelectionChange( const TOOL_EVENT& aEvent )
if( item->Type() == PCB_PAD_T && m_isFootprintEditor )
{
getView()->Remove( m_editPoints.get() );
getView()->Remove( m_angleItem.get() );
m_editPoints = makePoints( item );
m_angleItem->SetEditPoints( m_editPoints.get() );
getView()->Add( m_editPoints.get() );
getView()->Add( m_angleItem.get() );
}
}
else if( evt->Action() == TA_UNDO_REDO_POST )
@ -2295,7 +2307,9 @@ int PCB_POINT_EDITOR::OnSelectionChange( const TOOL_EVENT& aEvent )
if( m_editPoints )
{
getView()->Remove( m_editPoints.get() );
getView()->Remove( m_angleItem.get() );
m_editPoints.reset();
m_angleItem.reset();
}
m_editedPoint = nullptr;
@ -2431,6 +2445,7 @@ void PCB_POINT_EDITOR::updatePoints()
m_editorBehavior->UpdatePoints( *m_editPoints );
getView()->Update( m_editPoints.get() );
getView()->Update( m_angleItem.get() );
}

View File

@ -34,6 +34,8 @@
#include <memory>
namespace KIGFX { namespace PREVIEW { class ANGLE_ITEM; } }
class PCB_SELECTION_TOOL;
class POINT_EDIT_BEHAVIOR;
@ -125,6 +127,7 @@ private:
PCB_BASE_FRAME* m_frame;
PCB_SELECTION_TOOL* m_selectionTool;
std::shared_ptr<EDIT_POINTS> m_editPoints;
std::unique_ptr<KIGFX::PREVIEW::ANGLE_ITEM> m_angleItem;
EDIT_POINT* m_editedPoint;
EDIT_POINT* m_hoveredPoint;