76 BOOST_REQUIRE_MESSAGE( m_board,
"Failed to load board issue24286" );
80 std::shared_ptr<DRC_ITEM> item;
82 std::vector<PCB_SHAPE> pathShapes;
86 std::vector<ViolationInfo> violations;
89 BOOST_REQUIRE_MESSAGE( bds.
m_DRCEngine,
"DRC engine not initialized" );
97 [&](
const std::shared_ptr<DRC_ITEM>& aItem,
const VECTOR2I& aPos,
int aLayer,
98 const std::function<
void(
PCB_MARKER* )>& aPathGenerator )
111 aPathGenerator( &marker );
112 vi.pathShapes = marker.
GetPath();
115 violations.push_back( vi );
123 (
int) violations.size() ) );
125 for(
const ViolationInfo& vi : violations )
129 vi.pos.x / 1e6, vi.pos.y / 1e6,
130 (
int) vi.pathShapes.size(),
131 vi.item->GetErrorMessage(
false ) ) );
133 for(
size_t j = 0; j < vi.pathShapes.size(); j++ )
140 " [%zu] SEG: (%.4f,%.4f)->(%.4f,%.4f)", j,
147 " [%zu] ARC: (%.4f,%.4f)->(%.4f,%.4f) c=(%.4f,%.4f)", j,
159 for(
FOOTPRINT* fp : m_board->Footprints() )
161 if( fp->GetReference() != wxT(
"C4" ) )
164 for(
PAD* p : fp->Pads() )
166 if( p->GetNumber() == wxT(
"1" ) )
168 else if( p->GetNumber() == wxT(
"2" ) )
173 BOOST_REQUIRE_MESSAGE( pad1 && pad2,
"C4 pads 1 and 2 not found in board" );
177 const SEG directSeg( p1Pos, p2Pos );
178 const double directDist = ( p2Pos - p1Pos ).EuclideanNorm() / 1e6;
181 "C4 pad1 at (%.4f, %.4f) mm, pad2 at (%.4f, %.4f) mm, center-to-center %.4f mm",
182 p1Pos.
x / 1e6, p1Pos.
y / 1e6, p2Pos.
x / 1e6, p2Pos.
y / 1e6, directDist ) );
187 const ViolationInfo* c4Violation =
nullptr;
189 for(
const ViolationInfo& vi : violations )
191 if( vi.layer !=
F_Cu )
194 const KIID idA = vi.item->GetMainItemID();
195 const KIID idB = vi.item->GetAuxItemID();
196 const bool matchA = ( idA == pad1->
m_Uuid || idA == pad2->
m_Uuid );
197 const bool matchB = ( idB == pad1->
m_Uuid || idB == pad2->
m_Uuid );
199 if( matchA && matchB && idA != idB )
206 BOOST_REQUIRE_MESSAGE( c4Violation,
207 "No F.Cu creepage violation reported between C4 pad1 and pad2" );
210 "C4 violation: layer=%d shapes=%d arrow=(%.4f, %.4f) mm",
212 (
int) c4Violation->pathShapes.size(),
213 c4Violation->pos.x / 1e6, c4Violation->pos.y / 1e6 ) );
215 BOOST_REQUIRE_GE( c4Violation->pathShapes.size(), 1u );
218 double pathLen = 0.0;
220 for(
const PCB_SHAPE& s : c4Violation->pathShapes )
234 BOOST_TEST_MESSAGE( wxString::Format(
"C4 path total length: %.4f mm (direct: %.4f mm)",
235 pathLen, directDist ) );
242 bool pathLeavesDirectLine =
false;
244 for(
const PCB_SHAPE& s : c4Violation->pathShapes )
249 if( distStart > 400000 || distEnd > 400000 )
251 pathLeavesDirectLine =
true;
257 "Creepage path between C4 pads stays on the centre line of the NPTH slot, "
258 "indicating it cuts through the slot interior instead of going around it." );
268 wxString::Format(
"Reported creepage path length %.4f mm is shorter than the "
269 "pad-centre to pad-centre distance %.4f mm, which is impossible "
270 "for a path that routes around the NPTH slot.",
271 pathLen, directDist ) );
279 wxString errMsg = c4Violation->item->GetErrorMessage(
false );
280 double reportedActual = 0.0;
281 int actualPos = errMsg.Find( wxT(
"actual " ) );
283 BOOST_REQUIRE_MESSAGE( actualPos != wxNOT_FOUND,
284 wxString::Format(
"Could not find 'actual' in error message: %s",
287 wxString tail = errMsg.Mid( actualPos + 7 );
288 int spacePos = tail.Find(
' ' );
290 if( spacePos != wxNOT_FOUND )
291 tail = tail.Left( spacePos );
293 BOOST_REQUIRE_MESSAGE( tail.ToDouble( &reportedActual ),
294 wxString::Format(
"Could not parse reported actual from '%s'", tail ) );
297 wxString::Format(
"Reported creepage actual %.4f mm exceeds 4 mm; this indicates "
298 "the validator is still falling back to arc-endpoint connections "
299 "instead of tangent-on-arc paths.",
SETTINGS_MANAGER m_settingsManager
std::unique_ptr< BOARD > m_board
DRC_CREEPAGE_NPTH_PADS_FIXTURE()=default
BOOST_FIXTURE_TEST_CASE(CreepageNPTHBetweenPadsIssue24286, DRC_CREEPAGE_NPTH_PADS_FIXTURE)