mirror of
https://gitlab.com/kicad/code/kicad.git
synced 2025-09-14 18:23:15 +02:00
Only dogbone inside corners
Fixes https://gitlab.com/kicad/code/kicad/issues/18880
This commit is contained in:
parent
b98621c8c8
commit
ac1b44715b
@ -28,12 +28,16 @@
|
|||||||
#include <geometry/oval.h>
|
#include <geometry/oval.h>
|
||||||
#include <geometry/roundrect.h>
|
#include <geometry/roundrect.h>
|
||||||
#include <geometry/shape_rect.h>
|
#include <geometry/shape_rect.h>
|
||||||
|
#include <geometry/shape_utils.h>
|
||||||
#include <geometry/vector_utils.h>
|
#include <geometry/vector_utils.h>
|
||||||
|
#include <math.h>
|
||||||
|
|
||||||
#include <pad.h>
|
#include <pad.h>
|
||||||
#include <pcb_track.h>
|
#include <pcb_track.h>
|
||||||
#include <tools/pcb_tool_utils.h>
|
#include <tools/pcb_tool_utils.h>
|
||||||
#include <confirm.h>
|
#include <confirm.h>
|
||||||
|
#include <board.h>
|
||||||
|
#include <wx/log.h>
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
@ -293,7 +297,17 @@ std::optional<wxString> DOGBONE_CORNER_ROUTINE::GetStatusMessage( int aSegmentCo
|
|||||||
void DOGBONE_CORNER_ROUTINE::ProcessLinePair( PCB_SHAPE& aLineA, PCB_SHAPE& aLineB )
|
void DOGBONE_CORNER_ROUTINE::ProcessLinePair( PCB_SHAPE& aLineA, PCB_SHAPE& aLineB )
|
||||||
{
|
{
|
||||||
if( aLineA.GetLength() == 0.0 || aLineB.GetLength() == 0.0 )
|
if( aLineA.GetLength() == 0.0 || aLineB.GetLength() == 0.0 )
|
||||||
|
{
|
||||||
|
wxLogTrace( "DOGBONE", "Skip: zero-length line(s) (A len=%f, B len=%f)",
|
||||||
|
aLineA.GetLength(), aLineB.GetLength() );
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( !EnsureBoardOutline() )
|
||||||
|
{
|
||||||
|
wxLogTrace( "DOGBONE", "Skip: board outline unavailable" );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
SEG seg_a( aLineA.GetStart(), aLineA.GetEnd() );
|
SEG seg_a( aLineA.GetStart(), aLineA.GetEnd() );
|
||||||
SEG seg_b( aLineB.GetStart(), aLineB.GetEnd() );
|
SEG seg_b( aLineB.GetStart(), aLineB.GetEnd() );
|
||||||
@ -302,27 +316,74 @@ void DOGBONE_CORNER_ROUTINE::ProcessLinePair( PCB_SHAPE& aLineA, PCB_SHAPE& aLin
|
|||||||
|
|
||||||
if( !a_pt || !b_pt )
|
if( !a_pt || !b_pt )
|
||||||
{
|
{
|
||||||
|
wxLogTrace( "DOGBONE", "Skip: segments do not share endpoint" );
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Cannot handle parallel lines
|
// Cannot handle parallel lines
|
||||||
if( seg_a.Angle( seg_b ).IsHorizontal() )
|
if( seg_a.Angle( seg_b ).IsHorizontal() )
|
||||||
{
|
{
|
||||||
|
wxLogTrace( "DOGBONE", "Skip: parallel segments" );
|
||||||
AddFailure();
|
AddFailure();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Determine if this corner points into the board outline: we construct the bisector
|
||||||
|
// vector (as done in ComputeDogbone) and test a point a small distance in the opposite
|
||||||
|
// direction (i.e. exterior). If that opposite point is INSIDE the board outline, the
|
||||||
|
// corner indentation points inward (needs dogbone). If the opposite test point is
|
||||||
|
// outside, skip.
|
||||||
|
|
||||||
|
const VECTOR2I corner = *a_pt; // shared endpoint
|
||||||
|
// Build vectors from corner toward other ends
|
||||||
|
const VECTOR2I vecA = ( seg_a.A == corner ? seg_a.B - corner : seg_a.A - corner );
|
||||||
|
const VECTOR2I vecB = ( seg_b.A == corner ? seg_b.B - corner : seg_b.A - corner );
|
||||||
|
// Normalize (resize) to common length to form bisector reliably
|
||||||
|
int maxLen = std::max( vecA.EuclideanNorm(), vecB.EuclideanNorm() );
|
||||||
|
if( maxLen == 0 )
|
||||||
|
{
|
||||||
|
wxLogTrace( "DOGBONE", "Skip: degenerate corner (maxLen==0)" );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
VECTOR2I vecAn = vecA.Resize( maxLen );
|
||||||
|
VECTOR2I vecBn = vecB.Resize( maxLen );
|
||||||
|
VECTOR2I bisectorOutward = vecAn + vecBn; // direction inside angle region
|
||||||
|
|
||||||
|
// If vectors are nearly opposite, no meaningful dogbone
|
||||||
|
if( bisectorOutward.EuclideanNorm() == 0 )
|
||||||
|
{
|
||||||
|
wxLogTrace( "DOGBONE", "Skip: bisector zero (vectors opposite)" );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Opposite direction of bisector (points "outside" if angle is convex, or further into
|
||||||
|
// material if reflex). We'll sample a point a small distance along -bisectorOutward.
|
||||||
|
VECTOR2I sampleDir = ( -bisectorOutward ).Resize( std::min( 1000, m_params.DogboneRadiusIU ) );
|
||||||
|
VECTOR2I samplePoint = corner + sampleDir; // test point opposite bisector
|
||||||
|
|
||||||
|
bool oppositeInside = m_boardOutline.Contains( samplePoint );
|
||||||
|
|
||||||
|
if( !oppositeInside )
|
||||||
|
{
|
||||||
|
wxLogTrace( "DOGBONE", "Skip: corner not inward (sample outside polygon)" );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
std::optional<DOGBONE_RESULT> dogbone_result =
|
std::optional<DOGBONE_RESULT> dogbone_result =
|
||||||
ComputeDogbone( seg_a, seg_b, m_params.DogboneRadiusIU, m_params.AddSlots );
|
ComputeDogbone( seg_a, seg_b, m_params.DogboneRadiusIU, m_params.AddSlots );
|
||||||
|
|
||||||
if( !dogbone_result )
|
if( !dogbone_result )
|
||||||
{
|
{
|
||||||
|
wxLogTrace( "DOGBONE", "Skip: ComputeDogbone failed (radius=%d slots=%d)",
|
||||||
|
m_params.DogboneRadiusIU, (int) m_params.AddSlots );
|
||||||
AddFailure();
|
AddFailure();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if( dogbone_result->m_small_arc_mouth )
|
if( dogbone_result->m_small_arc_mouth )
|
||||||
{
|
{
|
||||||
|
wxLogTrace( "DOGBONE", "Info: small arc mouth (slots %s)",
|
||||||
|
m_params.AddSlots ? "enabled" : "disabled" );
|
||||||
// The arc is too small to fit the radius
|
// The arc is too small to fit the radius
|
||||||
m_haveNarrowMouths = true;
|
m_haveNarrowMouths = true;
|
||||||
}
|
}
|
||||||
@ -365,9 +426,36 @@ void DOGBONE_CORNER_ROUTINE::ProcessLinePair( PCB_SHAPE& aLineA, PCB_SHAPE& aLin
|
|||||||
ModifyLineOrDeleteIfZeroLength( aLineA, dogbone_result->m_updated_seg_a );
|
ModifyLineOrDeleteIfZeroLength( aLineA, dogbone_result->m_updated_seg_a );
|
||||||
ModifyLineOrDeleteIfZeroLength( aLineB, dogbone_result->m_updated_seg_b );
|
ModifyLineOrDeleteIfZeroLength( aLineB, dogbone_result->m_updated_seg_b );
|
||||||
|
|
||||||
|
wxLogTrace( "DOGBONE", "Success: dogbone added at (%d,%d)", corner.x, corner.y );
|
||||||
AddSuccess();
|
AddSuccess();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool DOGBONE_CORNER_ROUTINE::EnsureBoardOutline() const
|
||||||
|
{
|
||||||
|
if( m_boardOutlineCached )
|
||||||
|
return !m_boardOutline.IsEmpty();
|
||||||
|
|
||||||
|
m_boardOutlineCached = true;
|
||||||
|
|
||||||
|
BOARD* board = dynamic_cast<BOARD*>( GetBoard() );
|
||||||
|
if( !board )
|
||||||
|
{
|
||||||
|
wxLogTrace( "DOGBONE", "EnsureBoardOutline: board cast failed" );
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build outlines; ignore errors and arcs for this classification
|
||||||
|
if( !board->GetBoardPolygonOutlines( m_boardOutline ) )
|
||||||
|
{
|
||||||
|
wxLogTrace( "DOGBONE", "EnsureBoardOutline: GetBoardPolygonOutlines failed" );
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ok = !m_boardOutline.IsEmpty();
|
||||||
|
wxLogTrace( "DOGBONE", "EnsureBoardOutline: outline %s", ok ? "ready" : "empty" );
|
||||||
|
return ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
wxString LINE_EXTENSION_ROUTINE::GetCommitDescription() const
|
wxString LINE_EXTENSION_ROUTINE::GetCommitDescription() const
|
||||||
{
|
{
|
||||||
|
@ -327,8 +327,13 @@ public:
|
|||||||
void ProcessLinePair( PCB_SHAPE& aLineA, PCB_SHAPE& aLineB ) override;
|
void ProcessLinePair( PCB_SHAPE& aLineA, PCB_SHAPE& aLineB ) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
// Lazily load board outline polygons (outer outline + any holes)
|
||||||
|
bool EnsureBoardOutline() const;
|
||||||
|
|
||||||
PARAMETERS m_params;
|
PARAMETERS m_params;
|
||||||
bool m_haveNarrowMouths;
|
bool m_haveNarrowMouths;
|
||||||
|
mutable bool m_boardOutlineCached = false;
|
||||||
|
mutable SHAPE_POLY_SET m_boardOutline; ///< Cached board outline polygons
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user