80 BOOST_REQUIRE_MESSAGE( m_board,
"Failed to load board issue24286" );
84 std::shared_ptr<DRC_ITEM> item;
86 std::vector<PCB_SHAPE> pathShapes;
90 std::vector<ViolationInfo> violations;
93 BOOST_REQUIRE_MESSAGE( bds.
m_DRCEngine,
"DRC engine not initialized" );
101 [&](
const std::shared_ptr<DRC_ITEM>& aItem,
const VECTOR2I& aPos,
int aLayer,
102 const std::function<
void(
PCB_MARKER* )>& aPathGenerator )
115 aPathGenerator( &marker );
116 vi.pathShapes = marker.
GetPath();
119 violations.push_back( vi );
127 (
int) violations.size() ) );
129 for(
const ViolationInfo& vi : violations )
133 vi.pos.x / 1e6, vi.pos.y / 1e6,
134 (
int) vi.pathShapes.size(),
135 vi.item->GetErrorMessage(
false ) ) );
137 for(
size_t j = 0; j < vi.pathShapes.size(); j++ )
144 " [%zu] SEG: (%.4f,%.4f)->(%.4f,%.4f)", j,
151 " [%zu] ARC: (%.4f,%.4f)->(%.4f,%.4f) c=(%.4f,%.4f)", j,
163 for(
FOOTPRINT* fp : m_board->Footprints() )
165 if( fp->GetReference() != wxT(
"C4" ) )
168 for(
PAD* p : fp->Pads() )
170 if( p->GetNumber() == wxT(
"1" ) )
172 else if( p->GetNumber() == wxT(
"2" ) )
177 BOOST_REQUIRE_MESSAGE( pad1 && pad2,
"C4 pads 1 and 2 not found in board" );
181 const SEG directSeg( p1Pos, p2Pos );
182 const double directDist = ( p2Pos - p1Pos ).EuclideanNorm() / 1e6;
185 "C4 pad1 at (%.4f, %.4f) mm, pad2 at (%.4f, %.4f) mm, center-to-center %.4f mm",
186 p1Pos.
x / 1e6, p1Pos.
y / 1e6, p2Pos.
x / 1e6, p2Pos.
y / 1e6, directDist ) );
191 const ViolationInfo* c4Violation =
nullptr;
193 for(
const ViolationInfo& vi : violations )
195 if( vi.layer !=
F_Cu )
198 const KIID idA = vi.item->GetMainItemID();
199 const KIID idB = vi.item->GetAuxItemID();
200 const bool matchA = ( idA == pad1->
m_Uuid || idA == pad2->
m_Uuid );
201 const bool matchB = ( idB == pad1->
m_Uuid || idB == pad2->
m_Uuid );
203 if( matchA && matchB && idA != idB )
210 BOOST_REQUIRE_MESSAGE( c4Violation,
211 "No F.Cu creepage violation reported between C4 pad1 and pad2" );
214 "C4 violation: layer=%d shapes=%d arrow=(%.4f, %.4f) mm",
216 (
int) c4Violation->pathShapes.size(),
217 c4Violation->pos.x / 1e6, c4Violation->pos.y / 1e6 ) );
219 BOOST_REQUIRE_GE( c4Violation->pathShapes.size(), 1u );
222 double pathLen = 0.0;
224 for(
const PCB_SHAPE& s : c4Violation->pathShapes )
238 BOOST_TEST_MESSAGE( wxString::Format(
"C4 path total length: %.4f mm (direct: %.4f mm)",
239 pathLen, directDist ) );
246 bool pathLeavesDirectLine =
false;
248 for(
const PCB_SHAPE& s : c4Violation->pathShapes )
253 if( distStart > 400000 || distEnd > 400000 )
255 pathLeavesDirectLine =
true;
261 "Creepage path between C4 pads stays on the centre line of the NPTH slot, "
262 "indicating it cuts through the slot interior instead of going around it." );
272 wxString::Format(
"Reported creepage path length %.4f mm is shorter than the "
273 "pad-centre to pad-centre distance %.4f mm, which is impossible "
274 "for a path that routes around the NPTH slot.",
275 pathLen, directDist ) );
283 wxString errMsg = c4Violation->item->GetErrorMessage(
false );
284 double reportedActual = 0.0;
285 int actualPos = errMsg.Find( wxT(
"actual " ) );
287 BOOST_REQUIRE_MESSAGE( actualPos != wxNOT_FOUND,
288 wxString::Format(
"Could not find 'actual' in error message: %s",
291 wxString tail = errMsg.Mid( actualPos + 7 );
292 int spacePos = tail.Find(
' ' );
294 if( spacePos != wxNOT_FOUND )
295 tail = tail.Left( spacePos );
297 BOOST_REQUIRE_MESSAGE( tail.ToDouble( &reportedActual ),
298 wxString::Format(
"Could not parse reported actual from '%s'", tail ) );
301 wxString::Format(
"Reported creepage actual %.4f mm exceeds 4 mm; this indicates "
302 "the validator is still falling back to arc-endpoint connections "
303 "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)