Clip the leader lines properly when using circular text frame

Fixes https://gitlab.com/kicad/code/kicad/issues/7810
This commit is contained in:
Mikolaj Wielgus 2021-03-06 00:10:18 +01:00 committed by Jon Evans
parent 720147d272
commit 0f22b54cd8
5 changed files with 110 additions and 24 deletions

View File

@ -76,7 +76,7 @@ public:
*
* Computes the intersection points between this circle and aCircle.
*
* @param aCircle The other circle to intersect with this.
* @param aCircle The other circle to intersect with this
* @return std::vector containing:
* - 0 elements if the circles do not intersect
* - 1 element if the circles are tangent
@ -87,6 +87,16 @@ public:
/**
* Function Intersect()
*
* Computes the intersection points between this circle and aSeg.
*
* @param aSeg The segment to intersect with this circle (end points ignored)
* @return std::vector containing up to two intersection points
*/
std::vector<VECTOR2I> Intersect( const SEG& aSeg ) const;
/**
* Function IntersectLine()
*
* Computes the intersection points between this circle and aLine.
*
* @param aLine The line to intersect with this circle (end points ignored)
@ -95,7 +105,17 @@ public:
* - 1 element if the line is tangent to the circle
* - 2 elements if the line intersects the circle
*/
std::vector<VECTOR2I> Intersect( const SEG& aLine ) const;
std::vector<VECTOR2I> IntersectLine( const SEG& aLine ) const;
/**
* Function Contains()
*
* Checks whether point aP is inside this circle.
*
* @param aP The point to check
* @return true if the point is inside, false otherwise
*/
bool Contains( const VECTOR2I& aP );
};
#endif // __CIRCLE_H

View File

@ -46,6 +46,11 @@ public:
m_circle( aCenter, aRadius )
{}
SHAPE_CIRCLE( const CIRCLE& aCircle ) :
SHAPE( SH_CIRCLE ),
m_circle( aCircle )
{}
SHAPE_CIRCLE( const SHAPE_CIRCLE& aOther ) :
SHAPE( SH_CIRCLE ),
m_circle( aOther.m_circle )

View File

@ -97,7 +97,7 @@ CIRCLE& CIRCLE::ConstructFromTanTanPt( const SEG& aLineA, const SEG& aLineB, con
anglebisector = aLineA.ParallelSeg( midPt );
Center = aP; // use this circle as a construction to find the actual centers
std::vector<VECTOR2I> possibleCenters = Intersect( anglebisector );
std::vector<VECTOR2I> possibleCenters = IntersectLine( anglebisector );
wxCHECK_MSG( possibleCenters.size() > 0, *this, "No solutions exist!" );
intersectPoint = aLineA.A; // just for the purpose of deciding which solution to return
@ -138,7 +138,7 @@ CIRCLE& CIRCLE::ConstructFromTanTanPt( const SEG& aLineA, const SEG& aLineB, con
// Find the homothetic image of aP in the construction circle (hSolution)
SEG throughaP( intersectPoint, aP );
std::vector<VECTOR2I> hProjections = hSolution.Intersect( throughaP );
std::vector<VECTOR2I> hProjections = hSolution.IntersectLine( throughaP );
wxCHECK_MSG( hProjections.size() > 0, *this, "No solutions exist!" );
// We want to create a fillet, so the projection of homothetic projection of aP
@ -257,7 +257,33 @@ std::vector<VECTOR2I> CIRCLE::Intersect( const CIRCLE& aCircle ) const
}
std::vector<VECTOR2I> CIRCLE::Intersect( const SEG& aLine ) const
std::vector<VECTOR2I> CIRCLE::Intersect( const SEG& aSeg ) const
{
std::vector<VECTOR2I> retval;
for( VECTOR2I& intersection : IntersectLine( aSeg ) )
{
VECTOR2I delta = aSeg.B - aSeg.A;
if( delta.x > delta.y )
{
if( intersection.x >= std::min( aSeg.A.x, aSeg.B.x )
&& intersection.x <= std::max( aSeg.A.x, aSeg.B.x ) )
retval.push_back( intersection );
}
else
{
if( intersection.y >= std::min( aSeg.A.y, aSeg.B.y )
&& intersection.y <= std::max( aSeg.A.y, aSeg.B.y ) )
retval.push_back( intersection );
}
}
return retval;
}
std::vector<VECTOR2I> CIRCLE::IntersectLine( const SEG& aLine ) const
{
std::vector<VECTOR2I> retval;
@ -311,3 +337,8 @@ std::vector<VECTOR2I> CIRCLE::Intersect( const SEG& aLine ) const
return retval;
}
bool CIRCLE::Contains( const VECTOR2I& aP )
{
return ( aP - Center ).EuclideanNorm() < Radius;
}

View File

@ -476,6 +476,30 @@ OPT_VECTOR2I DIMENSION_BASE::segPolyIntersection( SHAPE_POLY_SET& aPoly, SEG& aS
}
OPT_VECTOR2I DIMENSION_BASE::segCircleIntersection( CIRCLE& aCircle, SEG& aSeg, bool aStart )
{
VECTOR2I start( aStart ? aSeg.A : aSeg.B );
VECTOR2I endpoint( aStart ? aSeg.B : aSeg.A );
if( aCircle.Contains( start ) )
return NULLOPT;
std::vector<VECTOR2I> intersections = aCircle.Intersect( aSeg );
for( VECTOR2I& intersection : aCircle.Intersect( aSeg ) )
{
if( ( intersection - start ).SquaredEuclideanNorm() <
( endpoint - start ).SquaredEuclideanNorm() )
endpoint = intersection;
}
if( start == endpoint )
return NULLOPT;
return OPT_VECTOR2I( endpoint );
}
ALIGNED_DIMENSION::ALIGNED_DIMENSION( BOARD_ITEM* aParent, KICAD_T aType ) :
DIMENSION_BASE( aParent, aType ),
m_height( 0 )
@ -886,13 +910,29 @@ void LEADER::updateGeometry()
VECTOR2I start( m_start );
start += firstLine.Resize( m_extensionOffset );
SEG primarySeg( m_start, m_end );
OPT_VECTOR2I primaryEndpoint = segPolyIntersection( polyBox, primarySeg );
SEG arrowSeg( m_start, m_end );
SEG textSeg( m_end, m_text.GetPosition() );
OPT_VECTOR2I arrowSegEnd, textSegEnd;
if( !primaryEndpoint )
primaryEndpoint = m_end;
if( m_textFrame == DIM_TEXT_FRAME::CIRCLE )
{
double penWidth = m_text.GetEffectiveTextPenWidth() / 2.0;
double radius = ( textBox.GetWidth() / 2.0 ) - penWidth;
CIRCLE circle( textBox.GetCenter(), radius );
m_shapes.emplace_back( new SHAPE_SEGMENT( start, *primaryEndpoint ) );
arrowSegEnd = segCircleIntersection( circle, arrowSeg );
textSegEnd = segCircleIntersection( circle, textSeg );
}
else
{
arrowSegEnd = segPolyIntersection( polyBox, arrowSeg );
textSegEnd = segPolyIntersection( polyBox, textSeg );
}
if( !arrowSegEnd )
arrowSegEnd = m_end;
m_shapes.emplace_back( new SHAPE_SEGMENT( start, *arrowSegEnd ) );
// Add arrows
VECTOR2I arrowEnd( m_arrowLength, 0 );
@ -905,8 +945,6 @@ void LEADER::updateGeometry()
m_shapes.emplace_back( new SHAPE_SEGMENT( start,
start + wxPoint( arrowEnd.Rotate( arrowRotNeg ) ) ) );
SEG textSeg( m_end, m_text.GetPosition() );
OPT_VECTOR2I textEndpoint = segPolyIntersection( polyBox, textSeg );
if( !GetText().IsEmpty() )
{
@ -926,15 +964,6 @@ void LEADER::updateGeometry()
double radius = ( textBox.GetWidth() / 2.0 ) - penWidth;
m_shapes.emplace_back( new SHAPE_CIRCLE( textBox.GetCenter(), radius ) );
// Calculated bbox endpoint won't be right
if( textEndpoint )
{
VECTOR2I totalLength( textBox.GetCenter() - m_end );
VECTOR2I circleEndpoint( *textEndpoint - m_end );
circleEndpoint = circleEndpoint.Resize( totalLength.EuclideanNorm() - radius );
textEndpoint = OPT_VECTOR2I( VECTOR2I( m_end ) + circleEndpoint );
}
break;
}
@ -943,8 +972,8 @@ void LEADER::updateGeometry()
}
}
if( textEndpoint && *primaryEndpoint == m_end )
m_shapes.emplace_back( new SHAPE_SEGMENT( m_end, *textEndpoint ) );
if( textSegEnd && *arrowSegEnd == m_end )
m_shapes.emplace_back( new SHAPE_SEGMENT( m_end, *textSegEnd ) );
}

View File

@ -29,7 +29,7 @@
#include <board_item.h>
#include <pcb_text.h>
#include <geometry/shape.h>
#include <geometry/circle.h>
class LINE_READER;
class MSG_PANEL_ITEM;
@ -272,6 +272,7 @@ protected:
* @return a point on aSeg that collides with aPoly closest to the start, if one exists
*/
static OPT_VECTOR2I segPolyIntersection( SHAPE_POLY_SET& aPoly, SEG& aSeg, bool aStart = true );
static OPT_VECTOR2I segCircleIntersection( CIRCLE& aCircle, SEG& aSeg, bool aStart = true );
// Value format
bool m_overrideTextEnabled; ///< Manually specify the displayed measurement value