38#include <boost/test/unit_test.hpp>
58std::map<int, std::vector<VECTOR2D>> ReadBoardOutlineLoops(
const std::filesystem::path& aEmnPath )
60 std::ifstream in( aEmnPath );
61 BOOST_REQUIRE_MESSAGE( in,
"Cannot open exported .emn: " << aEmnPath.string() );
63 std::map<int, std::vector<VECTOR2D>> loops;
65 bool inSection =
false;
67 while( std::getline( in, line ) )
69 if( line.find(
".BOARD_OUTLINE" ) != std::string::npos )
75 if( line.find(
".END_BOARD_OUTLINE" ) != std::string::npos )
81 std::istringstream ss( line );
82 std::vector<std::string> tokens;
84 for( std::string tok; ss >> tok; )
85 tokens.push_back( tok );
89 if( tokens.size() < 4 )
92 const std::string& first = tokens.front();
93 const bool isLoopIndex =
94 std::all_of( first.begin(), first.end(), [](
unsigned char c ) { return std::isdigit( c ); } );
98 loops[std::stoi( first )].emplace_back( std::stod( tokens[1] ),
99 std::stod( tokens[2] ) );
110void CheckLoopContainsPoints(
const std::vector<VECTOR2D>& aLoopPoints,
111 const std::vector<VECTOR2I>& aExpectedIU )
118 const bool found = std::any_of( aLoopPoints.begin(), aLoopPoints.end(),
121 return std::abs( pt.x - expX ) < 1e-3
122 && std::abs( pt.y - expY ) < 1e-3;
126 <<
") missing from exported loop" );
142 cutout =
static_cast<PCB_SHAPE*
>( item );
158 BOOST_REQUIRE_MESSAGE( std::filesystem::exists( sourceBoard ),
"Missing test board " << sourceBoard );
163 const std::filesystem::path outDir =
164 std::filesystem::temp_directory_path() /
"kicad_idf_cutout_test";
165 std::filesystem::create_directories( outDir );
167 const std::filesystem::path emnPath = outDir /
"cutout.emn";
168 std::filesystem::remove( emnPath );
173 const bool ok =
ExportBoardToIDF3( board.get(), wxString::FromUTF8( emnPath.string().c_str() ),
174 false, 0.0, 0.0,
true,
true, &
resolver, &errorMsg );
176 BOOST_REQUIRE_MESSAGE( ok,
"IDF export failed: " << errorMsg.ToStdString() );
179 const std::map<int, std::vector<VECTOR2D>> loops = ReadBoardOutlineLoops( emnPath );
183 BOOST_REQUIRE_MESSAGE( loops.count( 1 ) == 1,
"Footprint cutout (loop 1) missing from .emn" );
187 PCB_SHAPE* cutout = GetCutoutShape( board->Footprints().front() );
193 CheckLoopContainsPoints( loops.at( 1 ),
194 { start, VECTOR2I( end.x, start.y ), end,
195 VECTOR2I( start.x, end.y ) } );
197 std::filesystem::remove_all( outDir );
206 BOOST_REQUIRE_MESSAGE( std::filesystem::exists( sourceBoard ),
"Missing test board " << sourceBoard );
212 FOOTPRINT* footprint = board->Footprints().front();
215 const std::filesystem::path outDir =
216 std::filesystem::temp_directory_path() /
"kicad_idf_poly_cutout_test";
217 std::filesystem::create_directories( outDir );
219 const std::filesystem::path emnPath = outDir /
"poly_cutout.emn";
220 std::filesystem::remove( emnPath );
225 const bool ok =
ExportBoardToIDF3( board.get(), wxString::FromUTF8( emnPath.string().c_str() ),
226 false, 0.0, 0.0,
true,
true, &
resolver, &errorMsg );
228 BOOST_REQUIRE_MESSAGE( ok,
"IDF export failed: " << errorMsg.ToStdString() );
231 const std::map<int, std::vector<VECTOR2D>> loops = ReadBoardOutlineLoops( emnPath );
234 BOOST_REQUIRE_MESSAGE( loops.count( 1 ) == 1,
235 "Rotated polygon cutout (loop 1) missing from .emn" );
238 PCB_SHAPE* cutout = GetCutoutShape( footprint );
241 std::vector<VECTOR2I> corners;
244 for(
int ii = 0; ii <
chain.PointCount(); ++ii )
245 corners.push_back(
chain.CPoint( ii ) );
247 CheckLoopContainsPoints( loops.at( 1 ), corners );
249 std::filesystem::remove_all( outDir );
constexpr EDA_IU_SCALE pcbIUScale
General utilities for PCB file IO for QA programs.
A base class for any item which can be embedded within the BOARD container class, and therefore insta...
SHAPE_POLY_SET & GetPolyShape()
const VECTOR2I & GetEnd() const
Return the ending point of the graphic.
const VECTOR2I & GetStart() const
Return the starting point of the graphic.
Provide an extensible class to resolve 3D model paths.
Represent a polyline containing arcs as well as line segments: A chain of connected line and/or arc s...
const SHAPE_LINE_CHAIN & COutline(int aIndex) const
@ RECTANGLE
Use RECTANGLE instead of RECT to avoid collision in a Windows header.
bool ExportBoardToIDF3(BOARD *aPcb, const wxString &aFullFileName, bool aUseThou, double aXRef, double aYRef, bool aIncludeUnspecified, bool aIncludeDNP, FILENAME_RESOLVER *aResolver, wxString *aErrorMsg)
Generate IDFv3 compliant board (*.emn) and library (*.emp) files representing the user's PCB design.
static FILENAME_RESOLVER * resolver
std::string GetPcbnewTestDataDir()
Utility which returns a path to the data directory where the test board files are stored.
std::unique_ptr< BOARD > ReadBoardFromFileOrStream(const std::string &aFilename, std::istream &aFallback)
Read a board from a file, or another stream, as appropriate.
BOOST_AUTO_TEST_CASE(HorizontalAlignment)
BOOST_AUTO_TEST_SUITE(CadstarPartParser)
BOOST_REQUIRE(intersection.has_value()==c.ExpectedIntersection.has_value())
BOOST_AUTO_TEST_SUITE_END()
BOOST_AUTO_TEST_CASE(FootprintCutoutIsExported)
VECTOR3I expected(15, 30, 45)
BOOST_CHECK_MESSAGE(totalMismatches==0, std::to_string(totalMismatches)+" board(s) with strategy disagreements")
const SHAPE_LINE_CHAIN chain
@ PCB_SHAPE_T
class PCB_SHAPE, a segment not on copper layers
VECTOR2< int32_t > VECTOR2I
VECTOR2< double > VECTOR2D