mirror of
https://gitlab.com/kicad/code/kicad.git
synced 2025-09-13 17:53:11 +02:00
ADDED: Angle preview for polygon mod
Shows angle for the current and adjacent corners and whether they are congruent
This commit is contained in:
parent
c6c8e19e39
commit
bbc762a546
@ -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
|
||||
|
208
common/preview_items/angle_item.cpp
Normal file
208
common/preview_items/angle_item.cpp
Normal 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 );
|
||||
}
|
||||
}
|
@ -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() );
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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() );
|
||||
}
|
||||
|
||||
|
||||
|
@ -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;
|
||||
|
57
include/preview_items/angle_item.h
Normal file
57
include/preview_items/angle_item.h
Normal 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
|
@ -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() );
|
||||
}
|
||||
|
||||
|
||||
|
@ -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
|
||||
|
@ -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() );
|
||||
}
|
||||
|
||||
|
||||
|
@ -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;
|
||||
|
Loading…
x
Reference in New Issue
Block a user