mirror of
https://gitlab.com/kicad/code/kicad.git
synced 2025-09-14 02:03:12 +02:00
Make BuildWireWithHopShape() a member of SCH_LINE
BuildWireWithHopShape(): fix incorrect code, and plot Hop Over shapes
This commit is contained in:
parent
f650999004
commit
57bbc96ab2
@ -232,6 +232,20 @@ void SCH_COMMIT::pushSchEdit( const wxString& aMessage, int aCommitFlags )
|
||||
if( enteredGroup )
|
||||
Modify( enteredGroup );
|
||||
|
||||
// Handle wires with Hop Over shapes:
|
||||
for( COMMIT_LINE& ent : m_changes )
|
||||
{
|
||||
SCH_ITEM* schCopyItem = dynamic_cast<SCH_ITEM*>( ent.m_copy );
|
||||
SCH_ITEM* schItem = dynamic_cast<SCH_ITEM*>( ent.m_item );
|
||||
|
||||
if( schCopyItem && schCopyItem->Type() == SCH_LINE_T )
|
||||
frame->UpdateHopOveredWires( schCopyItem );
|
||||
|
||||
if( schItem && schItem->Type() == SCH_LINE_T )
|
||||
frame->UpdateHopOveredWires( schItem );
|
||||
}
|
||||
|
||||
|
||||
for( COMMIT_LINE& ent : m_changes )
|
||||
{
|
||||
SCH_ITEM* schItem = dynamic_cast<SCH_ITEM*>( ent.m_item );
|
||||
@ -463,20 +477,6 @@ void SCH_COMMIT::pushSchEdit( const wxString& aMessage, int aCommitFlags )
|
||||
|
||||
void SCH_COMMIT::Push( const wxString& aMessage, int aCommitFlags )
|
||||
{
|
||||
SCH_EDIT_FRAME* frame = static_cast<SCH_EDIT_FRAME*>( m_toolMgr->GetToolHolder() );
|
||||
|
||||
for( COMMIT_LINE& ent : m_changes )
|
||||
{
|
||||
SCH_ITEM* schCopyItem = dynamic_cast<SCH_ITEM*>( ent.m_copy );
|
||||
SCH_ITEM* schItem = dynamic_cast<SCH_ITEM*>( ent.m_item );
|
||||
|
||||
if( schCopyItem && schCopyItem->Type() == SCH_LINE_T )
|
||||
frame->UpdateHopOveredWires( schCopyItem );
|
||||
|
||||
if( schItem && schItem->Type() == SCH_LINE_T )
|
||||
frame->UpdateHopOveredWires( schItem );
|
||||
}
|
||||
|
||||
if( m_isLibEditor )
|
||||
pushLibEdit( aMessage, aCommitFlags );
|
||||
else
|
||||
|
@ -1032,6 +1032,140 @@ bool SCH_LINE::ShouldHopOver( const SCH_LINE* aLine ) const
|
||||
}
|
||||
|
||||
|
||||
std::vector<VECTOR3I> SCH_LINE::BuildWireWithHopShape( const SCH_SCREEN* aScreen,
|
||||
double aArcRadius ) const
|
||||
{
|
||||
// Note: Points are VECTOR3D, with Z coord used as flag
|
||||
// for segments: start point and end point have the Z coord = 0
|
||||
// for arcs: start point middle point and end point have the Z coord = 1
|
||||
|
||||
std::vector<VECTOR3I> wire_shape; // List of coordinates:
|
||||
// 2 points for a segment, 3 points for an arc
|
||||
|
||||
if( !IsWire() )
|
||||
{
|
||||
wire_shape.emplace_back( GetStartPoint().x,GetStartPoint().y, 0 );
|
||||
wire_shape.emplace_back( GetEndPoint().x, GetEndPoint().y, 0 );
|
||||
return wire_shape;
|
||||
}
|
||||
|
||||
std::vector<SCH_LINE*> existingWires; // wires to test (candidates)
|
||||
std::vector<VECTOR2I> intersections;
|
||||
|
||||
for( SCH_ITEM* item : aScreen->Items().Overlapping( SCH_LINE_T, GetBoundingBox() ) )
|
||||
{
|
||||
SCH_LINE* line = static_cast<SCH_LINE*>( item );
|
||||
|
||||
if( line->IsWire() )
|
||||
existingWires.push_back( line );
|
||||
}
|
||||
|
||||
VECTOR2I currentLineStartPoint = GetStartPoint();
|
||||
VECTOR2I currentLineEndPoint = GetEndPoint();
|
||||
|
||||
for( SCH_LINE* existingLine : existingWires )
|
||||
{
|
||||
VECTOR2I extLineStartPoint = existingLine->GetStartPoint();
|
||||
VECTOR2I extLineEndPoint = existingLine->GetEndPoint();
|
||||
|
||||
if( extLineStartPoint == currentLineStartPoint && extLineEndPoint == currentLineEndPoint )
|
||||
continue;
|
||||
|
||||
if( !ShouldHopOver( existingLine ) )
|
||||
continue;
|
||||
|
||||
SEG currentSegment = SEG( currentLineStartPoint, currentLineEndPoint );
|
||||
SEG existingSegment = SEG( extLineStartPoint, extLineEndPoint );
|
||||
|
||||
if( OPT_VECTOR2I intersect = currentSegment.Intersect( existingSegment, true, false ) )
|
||||
{
|
||||
if( IsEndPoint( *intersect ) || existingLine->IsEndPoint( *intersect ) )
|
||||
continue;
|
||||
|
||||
intersections.push_back( *intersect );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if( intersections.empty() )
|
||||
{
|
||||
wire_shape.emplace_back( currentLineStartPoint.x, currentLineStartPoint.y, 0 );
|
||||
wire_shape.emplace_back( currentLineEndPoint.x, currentLineEndPoint.y, 0 );
|
||||
}
|
||||
else
|
||||
{
|
||||
auto getDistance = []( const VECTOR2I& a, const VECTOR2I& b ) -> double
|
||||
{
|
||||
return std::sqrt( std::pow( a.x - b.x, 2 ) + std::pow( a.y - b.y, 2 ) );
|
||||
};
|
||||
|
||||
std::sort( intersections.begin(), intersections.end(),
|
||||
[&]( const VECTOR2I& a, const VECTOR2I& b )
|
||||
{
|
||||
return getDistance( GetStartPoint(), a ) < getDistance( GetStartPoint(), b );
|
||||
} );
|
||||
|
||||
VECTOR2I currentStart = GetStartPoint();
|
||||
double arcRadius = aArcRadius;
|
||||
|
||||
for( const VECTOR2I& hopMid : intersections )
|
||||
{
|
||||
// Calculate the angle of the line from start point to end point in radians
|
||||
double lineAngle = std::atan2( GetEndPoint().y - GetStartPoint().y,
|
||||
GetEndPoint().x - GetStartPoint().x );
|
||||
|
||||
// Convert the angle from radians to degrees
|
||||
double lineAngleDeg = lineAngle * ( 180.0f / M_PI );
|
||||
|
||||
// Normalize the angle to be between 0 and 360 degrees
|
||||
if( lineAngleDeg < 0 )
|
||||
lineAngleDeg += 360;
|
||||
|
||||
double startAngle = lineAngleDeg;
|
||||
double endAngle = startAngle + 180.0f;
|
||||
|
||||
// Adjust the end angle if it exceeds 360 degrees
|
||||
if( endAngle >= 360.0 )
|
||||
endAngle -= 360.0;
|
||||
|
||||
// Convert start and end angles from degrees to radians
|
||||
double startAngleRad = startAngle * ( M_PI / 180.0f );
|
||||
double endAngleRad = endAngle * ( M_PI / 180.0f );
|
||||
|
||||
VECTOR2I arcMidPoint = {
|
||||
hopMid.x + static_cast<int>( arcRadius
|
||||
* cos( ( startAngleRad + endAngleRad ) / 2.0f ) ),
|
||||
hopMid.y + static_cast<int>( arcRadius
|
||||
* sin( ( startAngleRad + endAngleRad ) / 2.0f ) )
|
||||
};
|
||||
|
||||
VECTOR2I beforeHop = hopMid - VECTOR2I( arcRadius * std::cos( lineAngle ),
|
||||
arcRadius * std::sin( lineAngle ) );
|
||||
VECTOR2I afterHop = hopMid + VECTOR2I( arcRadius * std::cos( lineAngle ),
|
||||
arcRadius * std::sin( lineAngle ) );
|
||||
|
||||
// Draw the line from the current start point to the before-hop point
|
||||
wire_shape.emplace_back( currentStart.x, currentStart.y, 0 );
|
||||
wire_shape.emplace_back( beforeHop.x, beforeHop.y, 0 );
|
||||
|
||||
// Create an arc object
|
||||
SHAPE_ARC arc( beforeHop, arcMidPoint, afterHop, 0 );
|
||||
wire_shape.emplace_back( beforeHop.x, beforeHop.y, 1 );
|
||||
wire_shape.emplace_back( arcMidPoint.x, arcMidPoint.y, 1 );
|
||||
wire_shape.emplace_back( afterHop.x, afterHop.y, 1 );
|
||||
|
||||
currentStart = afterHop;
|
||||
}
|
||||
|
||||
// Draw the final line from the current start point to the end point of the original line
|
||||
wire_shape.emplace_back( currentStart. x,currentStart.y, 0 );
|
||||
wire_shape.emplace_back( GetEndPoint().x, GetEndPoint().y, 0 );
|
||||
}
|
||||
|
||||
return wire_shape;
|
||||
}
|
||||
|
||||
|
||||
static struct SCH_LINE_DESC
|
||||
{
|
||||
SCH_LINE_DESC()
|
||||
|
@ -29,6 +29,7 @@
|
||||
#include <wx/pen.h> // for wxPenStyle
|
||||
#include <list> // for std::list
|
||||
#include <geometry/seg.h>
|
||||
#include <math/vector3.h>
|
||||
|
||||
class NETLIST_OBJECT_LIST;
|
||||
|
||||
@ -260,8 +261,23 @@ public:
|
||||
|
||||
bool IsParallel( const SCH_LINE* aLine ) const;
|
||||
|
||||
/**
|
||||
* For wires only: @return true if a wire can accept a hop over arc shape
|
||||
* (when 2 wires are crossing, only one must accept the hpo over)
|
||||
*/
|
||||
bool ShouldHopOver( const SCH_LINE* aLine ) const;
|
||||
|
||||
/**
|
||||
* For wires only: build the list of points to draw the shape using segments and 180 deg arcs
|
||||
* Points are VECTOR3D, with Z coord used as flag:
|
||||
* for segments: start point and end point have the Z coord = 0
|
||||
* for arcs (hop over): start point middle point and end point have the Z coord = 1
|
||||
* @return the list of points
|
||||
* @param aScreen is the current screen to draw/plot
|
||||
* @param aArcRadius is the radius of the hop over arc
|
||||
*/
|
||||
std::vector<VECTOR3I> BuildWireWithHopShape( const SCH_SCREEN* aScreen, double aArcRadius ) const;
|
||||
|
||||
void GetEndPoints( std::vector<DANGLING_END_ITEM>& aItemList ) override;
|
||||
|
||||
bool UpdateDanglingState( std::vector<DANGLING_END_ITEM>& aItemListByType,
|
||||
|
@ -1480,140 +1480,46 @@ void SCH_PAINTER::draw( const SCH_LINE* aLine, int aLayer )
|
||||
m_gal->SetStrokeColor( color );
|
||||
m_gal->SetLineWidth( width );
|
||||
|
||||
VECTOR2I currentLineStartPoint = aLine->GetStartPoint();
|
||||
VECTOR2I currentLineEndPoint = aLine->GetEndPoint();
|
||||
double lineWidth = getLineWidth( aLine, drawingShadows, drawingNetColorHighlights );
|
||||
double arcRadius = lineWidth * ADVANCED_CFG::GetCfg().m_hopOverArcRadius;
|
||||
std::vector<VECTOR3I> curr_wire_shape = aLine->BuildWireWithHopShape( m_schematic->GetCurrentScreen(),
|
||||
arcRadius );
|
||||
|
||||
std::vector<SCH_LINE*> existingLines;
|
||||
existingLines.clear();
|
||||
|
||||
if( aLine->IsWire() )
|
||||
for( size_t ii = 1; ii < curr_wire_shape.size(); ii++ )
|
||||
{
|
||||
SCH_SCREEN* curr_screen = m_schematic->GetCurrentScreen();
|
||||
VECTOR2I start( curr_wire_shape[ii-1].x, curr_wire_shape[ii-1].y );
|
||||
|
||||
for( SCH_ITEM* item : curr_screen->Items().Overlapping( SCH_LINE_T, aLine->GetBoundingBox() ) )
|
||||
if( curr_wire_shape[ii-1].z == 0 ) // This is the start point of a segment
|
||||
// there are always 2 points in list for a segment
|
||||
{
|
||||
SCH_LINE* line = static_cast<SCH_LINE*>( item );
|
||||
|
||||
if( line->IsWire() )
|
||||
existingLines.push_back( line );
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<VECTOR2I> intersections;
|
||||
|
||||
for( SCH_LINE* existingLine : existingLines )
|
||||
{
|
||||
VECTOR2I extLineStartPoint = existingLine->GetStartPoint();
|
||||
VECTOR2I extLineEndPoint = existingLine->GetEndPoint();
|
||||
|
||||
if( extLineStartPoint == currentLineStartPoint && extLineEndPoint == currentLineEndPoint )
|
||||
continue;
|
||||
|
||||
if( !aLine->ShouldHopOver( existingLine ) )
|
||||
continue;
|
||||
|
||||
SEG currentSegment = SEG( currentLineStartPoint, currentLineEndPoint );
|
||||
SEG existingSegment = SEG( extLineStartPoint, extLineEndPoint );
|
||||
|
||||
if( OPT_VECTOR2I intersect = currentSegment.Intersect( existingSegment, true, false ) )
|
||||
{
|
||||
if( aLine->IsEndPoint( *intersect ) || existingLine->IsEndPoint( *intersect ) )
|
||||
continue;
|
||||
|
||||
intersections.push_back( *intersect );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if( intersections.empty() )
|
||||
{
|
||||
drawLine( currentLineStartPoint, currentLineEndPoint, lineStyle,
|
||||
VECTOR2I end( curr_wire_shape[ii].x, curr_wire_shape[ii].y );
|
||||
drawLine( start, end, lineStyle,
|
||||
( lineStyle <= LINE_STYLE::FIRST_TYPE || drawingShadows ), width );
|
||||
}
|
||||
else
|
||||
else // This is the start point of a arc. there are always 3 points in list for an arc
|
||||
{
|
||||
auto getDistance = []( const VECTOR2I& a, const VECTOR2I& b ) -> double
|
||||
{
|
||||
return std::sqrt( std::pow( a.x - b.x, 2 ) + std::pow( a.y - b.y, 2 ) );
|
||||
};
|
||||
// Hop are a small arc, so use a solid line style gives best results
|
||||
VECTOR2I arc_middle( curr_wire_shape[ii].x, curr_wire_shape[ii].y );
|
||||
ii++;
|
||||
VECTOR2I arc_end( curr_wire_shape[ii].x, curr_wire_shape[ii].y );
|
||||
ii++;
|
||||
|
||||
std::sort( intersections.begin(), intersections.end(),
|
||||
[&]( const VECTOR2I& a, const VECTOR2I& b )
|
||||
{
|
||||
return getDistance( aLine->GetStartPoint(), a )
|
||||
< getDistance( aLine->GetStartPoint(), b );
|
||||
} );
|
||||
VECTOR2D dstart = start;
|
||||
VECTOR2D dmid = arc_middle;
|
||||
VECTOR2D dend = arc_end;
|
||||
VECTOR2D center = CalcArcCenter( dstart, dmid, dend );
|
||||
|
||||
VECTOR2I currentStart = aLine->GetStartPoint();
|
||||
float lineWidth = getLineWidth( aLine, drawingShadows, drawingNetColorHighlights );
|
||||
float arcRadius = lineWidth * ADVANCED_CFG::GetCfg().m_hopOverArcRadius;
|
||||
EDA_ANGLE startAngle( dstart - center );
|
||||
EDA_ANGLE midAngle( dmid - center );
|
||||
EDA_ANGLE endAngle( dend - center );
|
||||
|
||||
for( const VECTOR2I& hopMid : intersections )
|
||||
{
|
||||
// Calculate the angle of the line from start point to end point in radians
|
||||
double lineAngle = std::atan2( aLine->GetEndPoint().y - aLine->GetStartPoint().y,
|
||||
aLine->GetEndPoint().x - aLine->GetStartPoint().x );
|
||||
EDA_ANGLE angle1 = midAngle - startAngle;
|
||||
EDA_ANGLE angle2 = endAngle - midAngle;
|
||||
|
||||
// Convert the angle from radians to degrees
|
||||
double lineAngleDeg = lineAngle * ( 180.0f / M_PI );
|
||||
EDA_ANGLE angle = angle1.Normalize180() + angle2.Normalize180();
|
||||
|
||||
// Normalize the angle to be between 0 and 360 degrees
|
||||
if( lineAngleDeg < 0 )
|
||||
lineAngleDeg += 360;
|
||||
|
||||
double startAngle = lineAngleDeg;
|
||||
double endAngle = startAngle + 180.0f;
|
||||
|
||||
// Adjust the end angle if it exceeds 360 degrees
|
||||
if( endAngle >= 360.0 )
|
||||
endAngle -= 360.0;
|
||||
|
||||
// Convert start and end angles from degrees to radians
|
||||
double startAngleRad = startAngle * ( M_PI / 180.0f );
|
||||
double endAngleRad = endAngle * ( M_PI / 180.0f );
|
||||
|
||||
VECTOR2I arcStartPoint = {
|
||||
hopMid.x + static_cast<int>( arcRadius * cos( startAngleRad ) ),
|
||||
hopMid.y + static_cast<int>( arcRadius * sin( startAngleRad ) )
|
||||
};
|
||||
|
||||
VECTOR2I arcEndPoint = { hopMid.x + static_cast<int>( arcRadius * cos( endAngleRad ) ),
|
||||
hopMid.y
|
||||
+ static_cast<int>( arcRadius * sin( endAngleRad ) ) };
|
||||
|
||||
VECTOR2I arcMidPoint = {
|
||||
hopMid.x + static_cast<int>( arcRadius
|
||||
* cos( ( startAngleRad + endAngleRad ) / 2.0f ) ),
|
||||
hopMid.y + static_cast<int>( arcRadius
|
||||
* sin( ( startAngleRad + endAngleRad ) / 2.0f ) )
|
||||
};
|
||||
|
||||
VECTOR2I beforeHop = hopMid - VECTOR2I( arcRadius * std::cos( lineAngle ),
|
||||
arcRadius * std::sin( lineAngle ) );
|
||||
VECTOR2I afterHop = hopMid + VECTOR2I( arcRadius * std::cos( lineAngle ),
|
||||
arcRadius * std::sin( lineAngle ) );
|
||||
|
||||
// Draw the line from the current start point to the before-hop point
|
||||
drawLine( currentStart, beforeHop, lineStyle,
|
||||
( lineStyle <= LINE_STYLE::FIRST_TYPE || drawingShadows ), width );
|
||||
|
||||
// Create an arc object and draw it using the stroke function
|
||||
SHAPE_ARC arc( arcStartPoint, arcMidPoint, arcEndPoint, lineWidth );
|
||||
STROKE_PARAMS::Stroke( &arc, LINE_STYLE::DASH, KiROUND( width ), &m_schSettings,
|
||||
[&]( const VECTOR2I& start, const VECTOR2I& end )
|
||||
{
|
||||
if( start == end )
|
||||
m_gal->DrawLine( start + 1, end );
|
||||
else
|
||||
m_gal->DrawLine( start, end );
|
||||
} );
|
||||
|
||||
currentStart = afterHop;
|
||||
m_gal->DrawArc( center, ( dstart - center ).EuclideanNorm(), startAngle, angle );
|
||||
}
|
||||
|
||||
// Draw the final line from the current start point to the end point of the original line
|
||||
drawLine( currentStart, aLine->GetEndPoint(), lineStyle,
|
||||
( lineStyle <= LINE_STYLE::FIRST_TYPE || drawingShadows ), width );
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -61,10 +61,12 @@
|
||||
#include <locale_io.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <math/vector3.h>
|
||||
|
||||
// TODO(JE) Debugging only
|
||||
#include <core/profile.h>
|
||||
#include "sch_bus_entry.h"
|
||||
#include "sch_shape.h"
|
||||
|
||||
/**
|
||||
* Flag to enable profiling of the TestDanglingEnds() function.
|
||||
@ -913,8 +915,53 @@ void SCH_SCREEN::Plot( PLOTTER* aPlotter, const SCH_PLOT_OPTS& aPlotOpts ) const
|
||||
// Plot the foreground items
|
||||
for( SCH_ITEM* item : other )
|
||||
{
|
||||
aPlotter->SetCurrentLineWidth( item->GetEffectivePenWidth( renderSettings ) );
|
||||
double lineWidth = item->GetEffectivePenWidth( renderSettings );
|
||||
aPlotter->SetCurrentLineWidth( lineWidth );
|
||||
|
||||
if( item->Type() != SCH_LINE_T )
|
||||
item->Plot( aPlotter, !background, aPlotOpts, 0, 0, { 0, 0 }, false );
|
||||
else
|
||||
{
|
||||
SCH_LINE* aLine = static_cast<SCH_LINE*>( item );
|
||||
|
||||
if( !aLine->IsWire() )
|
||||
item->Plot( aPlotter, !background, aPlotOpts, 0, 0, { 0, 0 }, false );
|
||||
else
|
||||
{
|
||||
double arcRadius = lineWidth * ADVANCED_CFG::GetCfg().m_hopOverArcRadius;
|
||||
std::vector<VECTOR3I> curr_wire_shape = aLine->BuildWireWithHopShape( this, arcRadius );
|
||||
|
||||
for( size_t ii = 1; ii < curr_wire_shape.size(); ii++ )
|
||||
{
|
||||
VECTOR2I start( curr_wire_shape[ii-1].x, curr_wire_shape[ii-1].y );
|
||||
|
||||
if( curr_wire_shape[ii-1].z == 0 ) // This is the start point of a segment
|
||||
// there are always 2 points in list for a segment
|
||||
{
|
||||
VECTOR2I end( curr_wire_shape[ii].x, curr_wire_shape[ii].y );
|
||||
|
||||
SCH_LINE curr_line( *aLine );
|
||||
curr_line.SetStartPoint( start );
|
||||
curr_line.SetEndPoint( end );
|
||||
curr_line.Plot( aPlotter, !background, aPlotOpts, 0, 0, { 0, 0 }, false );
|
||||
}
|
||||
else // This is the start point of a arc. there are always 3 points in list for an arc
|
||||
{
|
||||
VECTOR2I arc_middle( curr_wire_shape[ii].x, curr_wire_shape[ii].y );
|
||||
ii++;
|
||||
VECTOR2I arc_end( curr_wire_shape[ii].x, curr_wire_shape[ii].y );
|
||||
ii++;
|
||||
|
||||
SCH_SHAPE arc( SHAPE_T::ARC, aLine->GetLayer(), lineWidth );
|
||||
|
||||
arc.SetArcGeometry( start, arc_middle, arc_end );
|
||||
// Hop are a small arc, so use a solid line style gives best results
|
||||
arc.SetLineStyle( LINE_STYLE::SOLID );
|
||||
arc.Plot( aPlotter, !background, aPlotOpts, 0, 0, { 0, 0 }, false );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// After plotting the symbols as a group above (in `other`), we need to overplot the pins
|
||||
|
Loading…
x
Reference in New Issue
Block a user