25#include <boost/test/data/test_case.hpp>
65 const double angle_tol_deg = 1.0;
68 const int pos_tol = 1;
104 ( aArc.
BBox() )( aProps.
m_bbox )( pos_tol ) );
122 std::unique_ptr<SHAPE> new_shape{ aArc.
Clone() };
185 "C(0,0) 114 + 360 degree",
200 { { -752587, -752587 }, { 1505174, 1505174 } },
204 "C(0,0) 180 + 360 degree",
219 { { -100, -100 }, { 200, 200 } },
223 "C(0,0) 180 + 90 degree",
238 { { -100, -100 }, { 100, 100 } },
242 "C(100,200) 0 - 30 degree",
257 { { 273, 100 }, { 27, 100 } },
264 "C(0,0) 30 + 120 degree",
280 { { -17320, 10000 }, { 17320 * 2, 10000 } },
285 "C(0,0) 150 + 240 degree",
301 { { -20000, -20000 }, { 40000, 30000 } },
306 "C(0,0) 30 - 300 degree",
322 { { -20000, -20000 }, { 40000, 30000 } },
331 const auto this_arc =
SHAPE_ARC{ c.m_geom.m_center_point, c.m_geom.m_start_point,
366 "90 degree segments intersecting",
381 { { 0, 0 }, { 1000, 1000 } },
385 "45 degree segments intersecting",
388 { 0, 0, 1000, 1000 },
400 { { 0, 1414 }, { 1707, 1000 } },
404 "135 degree segments intersecting",
407 { 0, 0, 1000, -1000 },
419 { { 0, -293 }, { 293, 707 } },
429 for(
int testCase = 0; testCase < 8; ++testCase )
431 SEG seg1 = c.m_geom.m_segment_1;
432 SEG seg2 = c.m_geom.m_segment_2;
438 seg1 = c.m_geom.m_segment_2;
439 seg2 = c.m_geom.m_segment_1;
450 if( ( testCase % 4 ) == 1 || ( testCase % 4 ) == 3 )
458 if( ( testCase % 4 ) == 2 || ( testCase % 4 ) == 3 )
467 c.m_geom.m_radius, c.m_width };
478struct ARC_START_END_CENTER
501 {
"180 deg, clockwise", { { 100, 0 }, { 0, 0 }, { 50, 0 } },
true, { 50, -50 } },
502 {
"180 deg, anticlockwise", { { 100, 0 }, { 0, 0 }, { 50, 0 } },
false, { 50, 50 } },
503 {
"180 deg flipped, clockwise", { { 0, 0 }, { 100, 0 }, { 50, 0 } },
true, { 50, 50 } },
504 {
"180 deg flipped, anticlockwise", { { 0, 0 }, { 100, 0 }, { 50, 0 } },
false, { 50, -50 } },
505 {
"90 deg, clockwise", { { -100, 0 }, { 0, 100 }, { 0, 0 } },
true, { -71, 71 } },
506 {
"90 deg, anticlockwise", { { -100, 0 }, { 0, 100 }, { 0, 0 } },
false, { 71, -71 } },
515 bool cw = c.m_clockwise;
535 {
" 270deg, 0 cl, 0 deg ", { { 0, 0 }, { 100, 0 }, 270.0 }, 0, { 100, 0 },
true, 0 },
536 {
" 270deg, 0 cl, 90 deg ", { { 0, 0 }, { 100, 0 }, 270.0 }, 0, { 0, 100 },
true, 0 },
537 {
" 270deg, 0 cl, 180 deg ", { { 0, 0 }, { 100, 0 }, 270.0 }, 0, { -100, 0 },
true, 0 },
538 {
" 270deg, 0 cl, 270 deg ", { { 0, 0 }, { 100, 0 }, 270.0 }, 0, { 0, -100 },
true, 0 },
539 {
" 270deg, 0 cl, 45 deg ", { { 0, 0 }, { 100, 0 }, 270.0 }, 0, { 71, 71 },
true, 0 },
540 {
" 270deg, 0 cl, -45 deg ", { { 0, 0 }, { 100, 0 }, 270.0 }, 0, { 71, -71 },
false, -1 },
541 {
"-270deg, 0 cl, 0 deg ", { { 0, 0 }, { 100, 0 }, -270.0 }, 0, { 100, 0 },
true, 0 },
542 {
"-270deg, 0 cl, 90 deg ", { { 0, 0 }, { 100, 0 }, -270.0 }, 0, { 0, 100 },
true, 0 },
543 {
"-270deg, 0 cl, 180 deg ", { { 0, 0 }, { 100, 0 }, -270.0 }, 0, { -100, 0 },
true, 0 },
544 {
"-270deg, 0 cl, 270 deg ", { { 0, 0 }, { 100, 0 }, -270.0 }, 0, { 0, -100 },
true, 0 },
545 {
"-270deg, 0 cl, 45 deg ", { { 0, 0 }, { 100, 0 }, -270.0 }, 0, { 71, 71 },
false, -1 },
546 {
"-270deg, 0 cl, -45 deg ", { { 0, 0 }, { 100, 0 }, -270.0 }, 0, { 71, -71 },
true, 0 },
547 {
" 270deg, 5 cl, 0 deg, 5 pos X", { { 0, 0 }, { 100, 0 }, 270.0 }, 5, { 105, 0 },
true, 5 },
548 {
" 270deg, 5 cl, 0 deg, 5 pos Y", { { 0, 0 }, { 100, 0 }, 270.0 }, 5, { 100, -5 },
true, 5 },
549 {
" 270deg, 5 cl, 90 deg, 5 pos", { { 0, 0 }, { 100, 0 }, 270.0 }, 5, { 0, 105 },
true, 5 },
550 {
" 270deg, 5 cl, 180 deg, 5 pos", { { 0, 0 }, { 100, 0 }, 270.0 }, 5, { -105, 0 },
true, 5 },
551 {
" 270deg, 5 cl, 270 deg, 5 pos", { { 0, 0 }, { 100, 0 }, 270.0 }, 5, { 0, -105 },
true, 5 },
552 {
" 270deg, 5 cl, 0 deg, 5 neg", { { 0, 0 }, { 100, 0 }, 270.0 }, 5, { 105, 0 },
true, 5 },
553 {
" 270deg, 5 cl, 90 deg, 5 neg", { { 0, 0 }, { 100, 0 }, 270.0 }, 5, { 0, 105 },
true, 5 },
554 {
" 270deg, 5 cl, 180 deg, 5 neg", { { 0, 0 }, { 100, 0 }, 270.0 }, 5, { -105, 0 },
true, 5 },
555 {
" 270deg, 5 cl, 270 deg, 5 neg", { { 0, 0 }, { 100, 0 }, 270.0 }, 5, { 0, -105 },
true, 5 },
556 {
" 270deg, 5 cl, 45 deg, 5 pos", { { 0, 0 }, { 100, 0 }, 270.0 }, 5, { 74, 75 },
true, 5 },
557 {
" 270deg, 5 cl, -45 deg, 5 pos", { { 0, 0 }, { 100, 0 }, 270.0 }, 5, { 74, -75 },
false, -1 },
558 {
" 270deg, 5 cl, 45 deg, 5 neg", { { 0, 0 }, { 100, 0 }, 270.0 }, 5, { 67, 67 },
true, 5 },
559 {
" 270deg, 5 cl, -45 deg, 5 neg", { { 0, 0 }, { 100, 0 }, 270.0 }, 5, { 67, -67 },
false, -1 },
560 {
" 270deg, 4 cl, 0 deg pos", { { 0, 0 }, { 100, 0 }, 270.0 }, 4, { 105, 0 },
false, -1 },
561 {
" 270deg, 4 cl, 90 deg pos", { { 0, 0 }, { 100, 0 }, 270.0 }, 4, { 0, 105 },
false, -1 },
562 {
" 270deg, 4 cl, 180 deg pos", { { 0, 0 }, { 100, 0 }, 270.0 }, 4, { -105, 0 },
false, -1 },
563 {
" 270deg, 4 cl, 270 deg pos", { { 0, 0 }, { 100, 0 }, 270.0 }, 4, { 0, -105 },
false, -1 },
564 {
" 90deg, 0 cl, 0 deg ", { { 0, 0 }, { 71, -71 }, 90.0 }, 0, { 71, -71 },
true, 0 },
565 {
" 90deg, 0 cl, 45 deg ", { { 0, 0 }, { 71, -71 }, 90.0 }, 0, { 100, 0 },
true, 0 },
566 {
" 90deg, 0 cl, 90 deg ", { { 0, 0 }, { 71, -71 }, 90.0 }, 0, { 71, 71 },
true, 0 },
567 {
" 90deg, 0 cl, 135 deg ", { { 0, 0 }, { 71, -71 }, 90.0 }, 0, { 0, -100 },
false, -1 },
568 {
" 90deg, 0 cl, -45 deg ", { { 0, 0 }, { 71, -71 }, 90.0 }, 0, { 0, 100 },
false, -1 },
569 {
" -90deg, 0 cl, 0 deg ", { { 0, 0 }, { 71, 71 }, -90.0 }, 0, { 71, -71 },
true, 0 },
570 {
" -90deg, 0 cl, 45 deg ", { { 0, 0 }, { 71, 71 }, -90.0 }, 0, { 100, 0 },
true, 0 },
571 {
" -90deg, 0 cl, 90 deg ", { { 0, 0 }, { 71, 71 }, -90.0 }, 0, { 71, 71 },
true, 0 },
572 {
" -90deg, 0 cl, 135 deg ", { { 0, 0 }, { 71, 71 }, -90.0 }, 0, { 0, -100 },
false, -1 },
573 {
" -90deg, 0 cl, -45 deg ", { { 0, 0 }, { 71, 71 }, -90.0 }, 0, { 0, 100 },
false, -1 },
574 {
"issue 11358 collide",
575 { { 119888000, 60452000 }, { 120904000, 60452000 }, 360.0 },
577 { 120395500, 59571830 },
580 {
"issue 11358 dist",
581 { { 119888000, 60452000 }, { 120904000, 60452000 }, 360.0 },
583 { 118872050, 60452000 },
591 SHAPE_ARC arc( c.m_geom.m_center_point, c.m_geom.m_start_point,
607 arc.SetWidth( c.m_arc_clearance * 2 );
628 {
"0 deg ", { { 0, 0 }, { 100, 0 }, 270.0 }, 0, { { 100, 0 }, { 50, 0 } },
true, 0 },
629 {
"90 deg ", { { 0, 0 }, { 100, 0 }, 270.0 }, 0, { { 0, 100 }, { 0, 50 } },
true, 0 },
630 {
"180 deg ", { { 0, 0 }, { 100, 0 }, 270.0 }, 0, { { -100, 0 }, { -50, 0 } },
true, 0 },
631 {
"270 deg ", { { 0, 0 }, { 100, 0 }, 270.0 }, 0, { { 0, -100 }, { 0, -50 } },
true, 0 },
632 {
"45 deg ", { { 0, 0 }, { 100, 0 }, 270.0 }, 0, { { 71, 71 }, { 35, 35 } },
true, 0 },
633 {
"-45 deg ", { { 0, 0 }, { 100, 0 }, 270.0 }, 0, { { 71, -71 }, { 35, -35 } },
false, -1 },
634 {
"seg inside arc start", { { 0, 0 }, { 71, -71 }, 90.0 },
635 10, { { 90, 0 }, { -35, 0 } },
true, 10 },
636 {
"seg inside arc end", { { 0, 0 }, { 71, -71 }, 90.0 },
637 10, { { -35, 0 }, { 90, 0 } },
true, 10 },
638 {
"large diameter arc", { { 172367922, 82282076 }, { 162530000, 92120000 }, -45.0 },
639 433300, { { 162096732, 92331236 }, { 162096732, 78253268 } },
true, 433268 },
645 SHAPE_ARC arc( c.m_geom.m_center_point, c.m_geom.m_start_point,
661 arc.SetWidth( c.m_arc_clearance * 2 );
702 {
"case 1: No intersection",
703 { 73.843527, 74.355869, 71.713528, 72.965869, -76.36664803, 0.2 },
704 { 71.236473, 74.704131, 73.366472, 76.094131, -76.36664803, 0.2 },
707 {
"case 2: No intersection",
708 { 82.542335, 74.825975, 80.413528, 73.435869, -76.4, 0.2 },
709 { 76.491192, 73.839894, 78.619999, 75.23, -76.4, 0.2 },
712 {
"case 3: No intersection",
713 { 89.318807, 74.810106, 87.19, 73.42, -76.4, 0.2 },
714 { 87.045667, 74.632941, 88.826472, 75.794131, -267.9, 0.2 },
717 {
"case 4: Co-centered not intersecting",
718 { 94.665667, 73.772941, 96.446472, 74.934131, -267.9, 0.2 },
719 { 94.665667, 73.772941, 93.6551, 73.025482, -255.5, 0.2 },
722 {
"case 5: Not intersecting, but end points very close",
723 { 72.915251, 80.493054, 73.570159, 81.257692, -260.5, 0.2 },
724 { 73.063537, 82.295989, 71.968628, 81.581351, -255.5, 0.2 },
727 {
"case 6: Coincident centers, colliding due to arc thickness",
728 { 79.279991, 80.67988, 80.3749, 81.394518, -255.5, 0.3 },
729 { 79.279991, 80.67988, 80.3749, 81.694518, -255.5, 0.3 },
732 {
"case 7: Single intersection",
733 { 88.495265, 81.766089, 90.090174, 82.867869, -255.5, 0.2 },
734 { 86.995265, 81.387966, 89.090174, 82.876887, -255.5, 0.2 },
737 {
"case 8: Double intersection",
738 { 96.149734, 81.792126, 94.99, 83.37, -347.2, 0.2 },
739 { 94.857156, 81.240589, 95.91, 83.9, -288.5, 0.2 },
742 {
"case 9: Endpoints within arc width",
743 { 72.915251, 86.493054, 73.970159, 87.257692, -260.5, 0.2 },
744 { 73.063537, 88.295989, 71.968628, 87.581351, -255.5, 0.2 },
747 {
"case 10: Endpoints close, outside, no collision",
748 { 78.915251, 86.393054, 79.970159, 87.157692, 99.5, 0.2 },
749 { 79.063537, 88.295989, 77.968628, 87.581351, -255.5, 0.2 },
752 {
"case 11: Endpoints close, inside, collision due to arc width",
753 { 85.915251, 86.993054, 86.970159, 87.757692, 99.5, 0.2 },
754 { 86.063537, 88.295989, 84.968628, 87.581351, -255.5, 0.2 },
757 {
"case 12: Simulated differential pair length-tuning",
758 { 94.6551, 88.296, 95.6551, 88.296, 90.0, 0.1 },
759 { 94.6551, 88.296, 95.8551, 88.296, 90.0, 0.1 },
762 {
"case 13: One arc fully enclosed in other, non-concentric",
763 { 73.77532, 93.413654, 75.70532, 93.883054, 60.0, 0.1 },
764 { 73.86532, 93.393054, 75.86532, 93.393054, 90.0, 0.3 },
767 {
"case 14: One arc fully enclosed in other, concentric",
768 { 79.87532, 93.413654, 81.64532, 94.113054, 60.0, 0.1 },
769 { 79.87532, 93.413654, 81.86532, 93.393054, 90.0, 0.3 },
772 {
"case 15: Arcs separated by clearance",
773 { 303.7615, 149.9252, 303.695968, 149.925237, 90.0262, 0.065 },
774 { 303.6345, 149.2637, 303.634523, 148.85619, 89.9957, 0.065 },
782 SHAPE_ARC arc1( c.m_arc1.GenerateArc() );
828 VECTOR2I( 197822958, 136722959 ), 250000 );
839 SHAPE* arc_sh = &arc;
880 zoneFill.AddHole( arcBuffer.
Outline( 0 ) );
881 zoneFill.CacheTriangulation(
false );
885 int epsilon = polygonApproximationError / 10;
909 int aRad,
int aTolerance )
911 std::vector<VECTOR2I> points;
913 for(
int i = 0; i < aPolyline.
PointCount(); ++i )
915 points.push_back( aPolyline.
CPoint( i ) );
932 int aRad,
int aTolerance )
934 std::vector<VECTOR2I> points;
936 for(
int i = 0; i < aPolyline.
PointCount() - 1; ++i )
939 points.push_back( mid_pt );
966 "Extremely small semicircle",
975 "Non-round geometry",
1008 int radius = ( c.m_geom.m_center_point - c.m_geom.m_start_point ).EuclideanNorm();
constexpr EDA_IU_SCALE pcbIUScale
constexpr const Vec & GetPosition() const
constexpr const Vec GetEnd() const
constexpr size_type GetWidth() const
EDA_ANGLE GetCentralAngle() const
const VECTOR2I & GetArcMid() const
const BOX2I BBox(int aClearance=0) const override
Compute a bounding box of the shape, with a margin of aClearance a collision.
EDA_ANGLE GetEndAngle() const
SHAPE_ARC & ConstructFromStartEndCenter(const VECTOR2I &aStart, const VECTOR2I &aEnd, const VECTOR2I &aCenter, bool aClockwise=false, double aWidth=0)
Constructs this arc from the given start, end and center.
const VECTOR2I & GetP1() const
bool Collide(const SEG &aSeg, int aClearance=0, int *aActual=nullptr, VECTOR2I *aLocation=nullptr) const override
Check if the boundary of shape (this) lies closer to the segment aSeg than aClearance,...
const SHAPE_LINE_CHAIN ConvertToPolyline(double aAccuracy=DefaultAccuracyForPCB(), double *aEffectiveAccuracy=nullptr) const
Construct a SHAPE_LINE_CHAIN of segments from a given arc.
EDA_ANGLE GetStartAngle() const
static double DefaultAccuracyForPCB()
SHAPE * Clone() const override
Return a dynamically allocated copy of the shape.
const VECTOR2I & GetP0() const
bool IsSolid() const override
const VECTOR2I & GetCenter() const
Represent a polyline containing arcs as well as line segments: A chain of connected line and/or arc s...
int PointCount() const
Return the number of points (vertices) in this line chain.
const VECTOR2I & CPoint(int aIndex) const
Return a reference to a given point in the line chain.
void SetWidth(int aWidth)
Set the width of all segments in the chain.
Represent a set of closed polygons.
int AddOutline(const SHAPE_LINE_CHAIN &aOutline)
Adds a new outline to the set and returns its index.
int HoleCount(int aOutline) const
Returns the number of holes in a given outline.
SHAPE_LINE_CHAIN & Outline(int aIndex)
Return the reference to aIndex-th outline in the set.
int OutlineCount() const
Return the number of outlines in the set.
An abstract shape on 2D plane.
virtual bool Collide(const VECTOR2I &aP, int aClearance=0, int *aActual=nullptr, VECTOR2I *aLocation=nullptr) const
Check if the boundary of shape (this) lies closer to the point aP than aClearance,...
static const int MIN_PRECISION_IU
This is the minimum precision for all the points in a shape.
void TransformArcToPolygon(SHAPE_POLY_SET &aBuffer, const VECTOR2I &aStart, const VECTOR2I &aMid, const VECTOR2I &aEnd, int aWidth, int aError, ERROR_LOC aErrorLoc)
Convert arc to multiple straight segments.
bool ArePointsNearCircle(const std::vector< VECTOR2< T > > &aPoints, const VECTOR2< T > &aCentre, T aRad, T aTol)
Predicate for checking a set of points is within a certain tolerance of a circle.
Numerical test predicates.
Info to set up an arc by centre, start point and angle.
ARC_CENTRE_PT_ANGLE m_geom
Geom of the arc.
ARC_PROPERTIES m_properties
Expected properties.
int m_width
Arc line width.
SHAPE_ARC GenerateArc() const
All properties of an arc (depending on how it's constructed, some of these might be the same as the c...
ARC_CENTRE_PT_ANGLE m_geom
bool m_clockwise
clockwise or anti-clockwise?
ARC_START_END_CENTER m_geom
Geom of the arc.
VECTOR2I m_expected_mid
Expected mid-point of the arc.
ARC_CENTRE_PT_ANGLE m_geom
Info to set up an arc by tangent to two segments and a radius.
ARC_CENTRE_PT_ANGLE m_geom
ARC_PROPERTIES m_properties
Expected properties.
ARC_TAN_TAN_RADIUS m_geom
Geom of the arc.
int m_width
Arc line width.
constexpr int mmToIU(double mm) const
A named data-driven test case.
BOOST_DATA_TEST_CASE(ConvertToKicadUnit, boost::unit_test::data::make(altium_to_kicad_unit), input_value, expected_result)
Test conversation from Altium internal units into KiCad internal units.
BOOST_AUTO_TEST_SUITE(CadstarPartParser)
BOOST_REQUIRE(intersection.has_value()==c.ExpectedIntersection.has_value())
BOOST_AUTO_TEST_SUITE_END()
bool ArePolylineMidPointsNearCircle(const SHAPE_LINE_CHAIN &aPolyline, const VECTOR2I &aCentre, int aRad, int aTolerance)
Predicate for checking a polyline has all the segment mid points on (near) a circle of given centre a...
SHAPE_ARC arc2(c.m_arc2.GenerateArc())
bool result_chain_to_chain
const SHAPE_LINE_CHAIN chain
static const std::vector< ARC_PT_COLLIDE_CASE > arc_pt_collide_cases
static void CheckArcGeom(const SHAPE_ARC &aArc, const ARC_PROPERTIES &aProps, const int aSynErrIU=1)
Check a SHAPE_ARC against a given set of geometric properties.
BOOST_CHECK_PREDICATE(ArePolylineEndPointsNearCircle,(chain)(c.m_geom.m_center_point)(radius)(accuracy+epsilon))
static const std::vector< ARC_SEC_CASE > arc_sec_cases
SHAPE_LINE_CHAIN arc1_slc(c.m_arc1.GenerateArc())
BOOST_CHECK_EQUAL(this_arc.GetArcMid(), c.m_expected_mid)
const std::vector< ARC_TO_POLYLINE_CASE > ArcToPolyline_cases
bool ArePolylineEndPointsNearCircle(const SHAPE_LINE_CHAIN &aPolyline, const VECTOR2I &aCentre, int aRad, int aTolerance)
Predicate for checking a polyline has all the points on (near) a circle of given centre and radius.
static const std::vector< ARC_SEG_COLLIDE_CASE > arc_seg_collide_cases
static void CheckArc(const SHAPE_ARC &aArc, const ARC_PROPERTIES &aProps, const int aSynErrIU=1)
Check an arcs geometry and other class functions.
BOOST_AUTO_TEST_CASE(NullCtor)
Check correct handling of filter strings (as used by WX)
BOOST_TEST_CONTEXT("Test Clearance")
BOOST_TEST_MESSAGE("Polyline has "<< chain.PointCount()<< " points")
static const std::vector< ARC_CPA_CASE > arc_cases
static const std::vector< ARC_TTR_CASE > arc_ttr_cases
SHAPE_LINE_CHAIN arc2_slc(c.m_arc2.GenerateArc())
static const std::vector< ARC_ARC_COLLIDE_CASE > arc_arc_collide_cases
VECTOR2< int32_t > VECTOR2I
VECTOR2< double > VECTOR2D