Cache read arc data for stability

Arcs can be altered by the process of changing from on-disk
representation to in-memory representation.  Saving back to disk without
modifying the arc should not modify the calculated values.

This stores a copy of the on-disk representation that is only used to
save back to disk in the event that the arc is not modified during
editing.

Fixes https://gitlab.com/kicad/code/kicad/issues/10442
This commit is contained in:
Seth Hillbrand 2022-03-04 15:18:34 -08:00
parent 2200c1319d
commit cd7141fd10
5 changed files with 60 additions and 8 deletions

View File

@ -437,6 +437,12 @@ void EDA_SHAPE::SetCenter( const VECTOR2I& aCenter )
VECTOR2I EDA_SHAPE::GetArcMid() const
{
// If none of the input data have changed since we loaded the arc,
// keep the original mid point data to minimize churn
if( m_arcMidData.start == m_start && m_arcMidData.end == m_end
&& m_arcMidData.center == m_arcCenter )
return m_arcMidData.mid;
VECTOR2I mid = m_start;
RotatePoint( mid, m_arcCenter, -GetArcAngle() / 2.0 );
return mid;
@ -487,19 +493,34 @@ int EDA_SHAPE::GetRadius() const
}
void EDA_SHAPE::SetCachedArcData( const VECTOR2I& aStart, const VECTOR2I& aMid, const VECTOR2I& aEnd, const VECTOR2I& aCenter )
{
m_arcMidData.start = aStart;
m_arcMidData.end = aEnd;
m_arcMidData.center = aCenter;
m_arcMidData.mid = aMid;
}
void EDA_SHAPE::SetArcGeometry( const VECTOR2I& aStart, const VECTOR2I& aMid, const VECTOR2I& aEnd )
{
m_arcMidData = {};
m_start = aStart;
m_end = aEnd;
m_arcCenter = CalcArcCenter( aStart, aMid, aEnd );
VECTOR2I new_mid = GetArcMid();
m_endsSwapped = false;
// Watch the ordering here. GetArcMid above needs to be called prior to initializing the
// m_arcMidData structure in order to ensure we get the calculated variant, not the cached
SetCachedArcData( aStart, aMid, aEnd, m_arcCenter );
/*
* If the input winding doesn't match our internal winding, the calculated midpoint will end
* up on the other side of the arc. In this case, we need to flip the start/end points and
* flag this change for the system.
*/
VECTOR2I new_mid = GetArcMid();
VECTOR2D dist( new_mid - aMid );
VECTOR2D dist2( new_mid - m_arcCenter );

View File

@ -1000,12 +1000,9 @@ LIB_SHAPE* SCH_SEXPR_PARSER::parseArc()
}
}
arc->SetStart( startPoint );
arc->SetEnd( endPoint );
if( hasMidPoint )
{
arc->SetCenter( CalcArcCenter( arc->GetStart(), midPoint, arc->GetEnd() ) );
arc->SetArcGeometry( startPoint, midPoint, endPoint);
#if 1 // This code will be removed if the current code in Eeschema is modified to allow
// full support of arcs >= 180 deg without issue.
@ -1030,6 +1027,7 @@ LIB_SHAPE* SCH_SEXPR_PARSER::parseArc()
VECTOR2I new_center = CalcArcCenter( arc->GetStart(), arc->GetEnd(),
EDA_ANGLE( 179.5, DEGREES_T ) );
arc->SetCenter( new_center );
arc->SetCachedArcData( startPoint, midPoint, endPoint, new_center );
}
#endif
}
@ -1041,9 +1039,8 @@ LIB_SHAPE* SCH_SEXPR_PARSER::parseArc()
* between LibEdit and PCBNew. Since we now use a common class (EDA_SHAPE) for both we
* need to flip one of them. LibEdit drew the short straw.
*/
VECTOR2I temp = arc->GetStart();
arc->SetStart( arc->GetEnd() );
arc->SetEnd( temp );
arc->SetStart( endPoint );
arc->SetEnd( startPoint );
}
else
{

View File

@ -61,6 +61,15 @@ enum class FILL_T : int
};
// Holding struct to keep originating midpoint
struct ARC_MID
{
VECTOR2I mid;
VECTOR2I start;
VECTOR2I end;
VECTOR2I center;
};
class EDA_SHAPE
{
public:
@ -193,6 +202,17 @@ public:
*/
void SetArcGeometry( const VECTOR2I& aStart, const VECTOR2I& aMid, const VECTOR2I& aEnd );
/**
* Set the data used for mid point caching. If the controlling points remain constant, then
* we keep the midpoint the same as it was when read in. This minimizes VCS churn
*
* @param aStart Cached start point
* @param aMid Cached mid point
* @param aEnd Cached end point
* @param aCenter Calculated center point using the preceeding three
*/
void SetCachedArcData( const VECTOR2I& aStart, const VECTOR2I& aMid, const VECTOR2I& aEnd, const VECTOR2I& aCenter );
const std::vector<VECTOR2I>& GetBezierPoints() const { return m_bezierPoints; }
/**
@ -314,6 +334,7 @@ protected:
VECTOR2I m_end; // Line end point or Circle 3 o'clock point
VECTOR2I m_arcCenter; // Used only for Arcs: arc end point
ARC_MID m_arcMidData; // Used to store originating data
VECTOR2I m_bezierC1; // Bezier Control Point 1
VECTOR2I m_bezierC2; // Bezier Control Point 2

View File

@ -178,6 +178,12 @@ void FP_SHAPE::SetCenter0( const VECTOR2I& aCenter )
VECTOR2I FP_SHAPE::GetArcMid0() const
{
// If none of the input data have changed since we loaded the arc,
// keep the original mid point data to minimize churn
if( m_arcMidData_0.start == m_start && m_arcMidData_0.end == m_end
&& m_arcMidData_0.center == m_arcCenter )
return m_arcMidData_0.mid;
VECTOR2I mid0 = m_start0;
RotatePoint( mid0, m_arcCenter0, -GetArcAngle() / 2.0 );
return mid0;
@ -202,6 +208,11 @@ void FP_SHAPE::SetArcGeometry0( const VECTOR2I& aStart0, const VECTOR2I& aMid0,
m_start0 = aStart0;
m_end0 = aEnd0;
m_arcCenter0 = CalcArcCenter( aStart0, aMid0, aEnd0 );
m_arcMidData_0.center = m_arcCenter0;
m_arcMidData_0.end = m_end0;
m_arcMidData_0.mid = aMid0;
m_arcMidData_0.start = m_start0;
}

View File

@ -164,6 +164,8 @@ protected:
VECTOR2I m_arcCenter0; ///< Center of arc, relative to footprint origin, orient 0.
VECTOR2I m_bezierC1_0; ///< Bezier Control Point 1, relative to footprint origin, orient 0.
VECTOR2I m_bezierC2_0; ///< Bezier Control Point 2, relative to footprint origin, orient 0.
ARC_MID m_arcMidData_0; ///< Originating Arc data, orient 0
};
#endif // FP_SHAPE_H