diff --git a/qa/unittests/pcbnew/CMakeLists.txt b/qa/unittests/pcbnew/CMakeLists.txt index 2bc87aa330..ee3ec3cdd2 100644 --- a/qa/unittests/pcbnew/CMakeLists.txt +++ b/qa/unittests/pcbnew/CMakeLists.txt @@ -34,6 +34,7 @@ set( QA_PCBNEW_SRCS test_array_pad_name_provider.cpp test_board_item.cpp test_graphics_import_mgr.cpp + test_fp_shape.cpp test_lset.cpp test_pns_basics.cpp test_pad_numbering.cpp diff --git a/qa/unittests/pcbnew/test_fp_shape.cpp b/qa/unittests/pcbnew/test_fp_shape.cpp new file mode 100644 index 0000000000..d04484b0ef --- /dev/null +++ b/qa/unittests/pcbnew/test_fp_shape.cpp @@ -0,0 +1,177 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2023 KiCad Developers, see AUTHORS.TXT for contributors. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 3 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + + +#include + +#include +#include // For KI_TEST::IsVecWithinTol +#include // For SHAPE_ARC::DefaultAccuracyForPCB() + + +BOOST_AUTO_TEST_SUITE( FpShape ) + +class FP_SHAPE_MOCK : public FP_SHAPE +{ +public: + FP_SHAPE_MOCK( SHAPE_T aShapeType ) : FP_SHAPE( nullptr, aShapeType ){}; + +protected: + EDA_ANGLE getParentOrientation() const override { return ANGLE_0; } + VECTOR2I getParentPosition() const override { return VECTOR2I(); } +}; + + +struct SET_ANGLE_END_CASE +{ + std::string m_CaseName; + VECTOR2I m_Start; + VECTOR2I m_Center; + double m_Angle; + VECTOR2I m_ExpectedEndBeforeSwap; + bool m_ExpectedStartEndSwapped; +}; + + +static const std::vector set_angle_end_cases = +{ + { + "Issue 13626: clockwise semicircle", + {-428880000, 117229160 }, + {-430060565, 113472820 }, + 180.0, + {-431241130, 109716480 }, + false + }, + { + "Issue 13626: anticlockwise arc", + { -431241130, 109716480 }, + { -434923630, 112954230 }, + -138.46654568595355, + { -439827050, 112936200 }, + true + } +}; + + +BOOST_AUTO_TEST_CASE( SetAngleAndEnd ) +{ + for( const auto& c : set_angle_end_cases ) + { + BOOST_TEST_INFO_SCOPE( c.m_CaseName ); + + FP_SHAPE_MOCK shape( SHAPE_T::ARC ); + shape.SetStart0( c.m_Start ); + shape.SetCenter0( c.m_Center ); + + shape.SetArcAngleAndEnd0( EDA_ANGLE( c.m_Angle, DEGREES_T ), true ); + + BOOST_CHECK_EQUAL( shape.EndsSwapped(), c.m_ExpectedStartEndSwapped ); + + const VECTOR2I newEnd = shape.EndsSwapped() ? shape.GetStart0() : shape.GetEnd0(); + + BOOST_CHECK_PREDICATE( + KI_TEST::IsVecWithinTol, + (newEnd) ( c.m_ExpectedEndBeforeSwap ) ( SHAPE_ARC::DefaultAccuracyForPCB() ) ); + } +} + + +struct SET_ARC_GEOMETRY_CASE +{ + std::string m_CaseName; + VECTOR2I m_Start; + VECTOR2I m_Mid; + VECTOR2I m_End; + VECTOR2I m_ExpectedCenter; + int m_ExpectedRadius; + bool m_ExpectedStartEndSwapped; + VECTOR2I m_ExpectedEndAfterSwap; + double m_ExpectedAngleAfterSwapDeg; +}; + +static const std::vector set_arc_geometry_cases = { + { + // Test that when setting an arc by start/mid/end, the winding + // direction is correctly determined (in 15694, this was in FP_SHAPE, + // but the logic has since been merged with EDA_SHAPE). + "Issue 15694: clockwise arc", + { 10000000, 0 }, + { 0, 10000000 }, + { -10000000, 0 }, + { 0, 0 }, + 10000000, + false, + { -10000000, 0 }, // unchanged + 180.0, + }, + { + "Issue 15694: anticlockwise arc", + { -10000000, 0 }, + { 0, 10000000 }, + { 10000000, 0 }, + { 0, 0 }, + 10000000, + true, + { 10000000, 0 }, // the start is the end after swapping + 180.0, // angle is positive after swapping + }, +}; + +BOOST_AUTO_TEST_CASE( SetArcGeometry ) +{ + const double angle_tol = 0.1; + + for( const auto& c : set_arc_geometry_cases ) + { + BOOST_TEST_INFO_SCOPE( c.m_CaseName ); + + FP_SHAPE_MOCK shape( SHAPE_T::ARC ); + + shape.SetArcGeometry0( c.m_Start, c.m_Mid, c.m_End ); + + const VECTOR2I center = shape.GetCenter0(); + + BOOST_CHECK_PREDICATE( + KI_TEST::IsVecWithinTol, + (center) ( c.m_ExpectedCenter ) ( SHAPE_ARC::DefaultAccuracyForPCB() ) ); + + // const int radius = shape.GetRadius(); + + // BOOST_CHECK_PREDICATE( + // KI_TEST::IsWithin, + // (radius) ( c.m_ExpectedRadius ) ( SHAPE_ARC::DefaultAccuracyForPCB() ) ); + + BOOST_CHECK_EQUAL( shape.EndsSwapped(), c.m_ExpectedStartEndSwapped ); + + const VECTOR2I newEnd = shape.EndsSwapped() ? shape.GetStart0() : shape.GetEnd0(); + + BOOST_CHECK_PREDICATE( + KI_TEST::IsVecWithinTol, + (newEnd) ( c.m_ExpectedEndAfterSwap ) ( SHAPE_ARC::DefaultAccuracyForPCB() ) ); + + const EDA_ANGLE angle = shape.GetArcAngle0(); + + BOOST_CHECK_PREDICATE( + KI_TEST::IsWithinWrapped, + ( angle.AsDegrees() )( c.m_ExpectedAngleAfterSwapDeg )( 360.0 )( angle_tol ) ); + + // Check that the centre is still correct + } +} + +BOOST_AUTO_TEST_SUITE_END()