From 954b265839cff7b518ba10e705891bee37848566 Mon Sep 17 00:00:00 2001 From: Seth Hillbrand Date: Thu, 6 Jul 2023 19:23:28 -0700 Subject: [PATCH] Check for zone-zone overlap Instead of just checking for the zone outline, we adjust to check the full fill area of the zones for intersection and overlaps --- libs/kimath/include/geometry/seg.h | 8 + libs/kimath/include/math/box2.h | 5 + .../drc_test_provider_copper_clearance.cpp | 233 ++++---- qa/data/pcbnew/intersectingzones.kicad_pcb | 524 ++++++++++++++++++ qa/data/pcbnew/intersectingzones.kicad_pro | 232 ++++++++ qa/tests/pcbnew/drc/test_drc_regressions.cpp | 60 ++ 6 files changed, 968 insertions(+), 94 deletions(-) create mode 100644 qa/data/pcbnew/intersectingzones.kicad_pcb create mode 100644 qa/data/pcbnew/intersectingzones.kicad_pro diff --git a/libs/kimath/include/geometry/seg.h b/libs/kimath/include/geometry/seg.h index f88d80d9cb..f5cbf8c41a 100644 --- a/libs/kimath/include/geometry/seg.h +++ b/libs/kimath/include/geometry/seg.h @@ -364,6 +364,14 @@ public: return A + ( B - A ) / 2; } + bool operator<( const SEG& aSeg ) const + { + if( A == aSeg.A ) + return B < aSeg.B; + + return A < aSeg.A; + } + private: bool ccw( const VECTOR2I& aA, const VECTOR2I& aB, const VECTOR2I &aC ) const; diff --git a/libs/kimath/include/math/box2.h b/libs/kimath/include/math/box2.h index 5939eab82f..258f226d8c 100644 --- a/libs/kimath/include/math/box2.h +++ b/libs/kimath/include/math/box2.h @@ -837,6 +837,11 @@ public: return ( t1.m_Pos != t2.m_Pos || t1.m_Size != t2.m_Size ); } + const bool IsValid() const + { + return m_init; + } + private: Vec m_Pos; // Rectangle Origin Vec m_Size; // Rectangle Size diff --git a/pcbnew/drc/drc_test_provider_copper_clearance.cpp b/pcbnew/drc/drc_test_provider_copper_clearance.cpp index 5097771bab..053655ce03 100644 --- a/pcbnew/drc/drc_test_provider_copper_clearance.cpp +++ b/pcbnew/drc/drc_test_provider_copper_clearance.cpp @@ -28,6 +28,7 @@ #include #include #include +#include #include #include @@ -41,6 +42,8 @@ #include #include +#include + /* Copper clearance test. Checks all copper items (pads, vias, tracks, drawings, zones) for their electrical clearance. @@ -1028,19 +1031,81 @@ void DRC_TEST_PROVIDER_COPPER_CLEARANCE::testZonesToZones() bool testClearance = !m_drcEngine->IsErrorLimitExceeded( DRCE_CLEARANCE ); bool testIntersects = !m_drcEngine->IsErrorLimitExceeded( DRCE_ZONES_INTERSECT ); - SHAPE_POLY_SET buffer; - SHAPE_POLY_SET* boardOutline = nullptr; DRC_CONSTRAINT constraint; - int zone2zoneClearance; + bool cancelled = false; + std::vector>> poly_segments; - if( m_board->GetBoardPolygonOutlines( buffer ) ) - boardOutline = &buffer; + poly_segments.resize( m_board->m_DRCCopperZones.size() ); + + // Contains the index for zoneA, zoneB, the conflict point, the actual clearance, the required clearance, and the layer + using report_data = std::tuple; + const int invalid_zone = -1; + + std::vector> futures; + thread_pool& tp = GetKiCadThreadPool(); + + auto checkZones = [testClearance, testIntersects, &poly_segments, &cancelled, invalid_zone] + ( int zoneA, int zoneB, + int zone2zoneClearance, PCB_LAYER_ID layer ) -> report_data + { + // Iterate through all the segments of refSmoothedPoly + std::map conflictPoints; + + std::vector& refSegments = poly_segments[zoneA][layer]; + std::vector& testSegments = poly_segments[zoneB][layer]; + bool reported = false; + auto invalid_result = std::make_tuple( invalid_zone, invalid_zone, VECTOR2I(), 0, 0, F_Cu ); + + for( SEG& refSegment : refSegments ) + { + int ax1 = refSegment.A.x; + int ay1 = refSegment.A.y; + int ax2 = refSegment.B.x; + int ay2 = refSegment.B.y; + + // Iterate through all the segments in smoothed_polys[ia2] + for( SEG& testSegment : testSegments ) + { + // Build test segment + VECTOR2I pt; + + int bx1 = testSegment.A.x; + int by1 = testSegment.A.y; + int bx2 = testSegment.B.x; + int by2 = testSegment.B.y; + + // We have ensured that the A segment starts before the B segment, so if the + // A segment ends before the B segment starts, we can skip to the next A + if( ax2 < bx1 ) + break; + + int d = GetClearanceBetweenSegments( bx1, by1, bx2, by2, 0, + ax1, ay1, ax2, ay2, 0, + zone2zoneClearance, &pt.x, &pt.y ); + + if( d < zone2zoneClearance ) + { + if( d == 0 && testIntersects ) + reported = true; + else if( testClearance ) + reported = true; + + if( reported ) + return std::make_tuple( zoneA, zoneB, pt, d, zone2zoneClearance, layer ); + } + + if( cancelled ) + return invalid_result; + } + } + + return invalid_result; + }; for( int layer_id = F_Cu; layer_id <= B_Cu; ++layer_id ) { PCB_LAYER_ID layer = static_cast( layer_id ); - std::vector smoothed_polys; - smoothed_polys.resize( m_board->m_DRCCopperZones.size() ); + int zone2zoneClearance; // Skip over layers not used on the current board if( !m_board->IsLayerEnabled( layer ) ) @@ -1050,12 +1115,30 @@ void DRC_TEST_PROVIDER_COPPER_CLEARANCE::testZonesToZones() { if( m_board->m_DRCCopperZones[ii]->IsOnLayer( layer ) ) { - m_board->m_DRCCopperZones[ii]->BuildSmoothedPoly( smoothed_polys[ii], layer, - boardOutline ); + SHAPE_POLY_SET poly = + *m_board->m_DRCCopperZones[ii]->GetFilledPolysList( layer ); + std::vector& poly_segs = poly_segments[ii][layer]; + + poly.Fracture( SHAPE_POLY_SET::PM_FAST ); + poly.BuildBBoxCaches(); + poly_segs.reserve( poly.FullPointCount() ); + + for( auto it = poly.IterateSegmentsWithHoles(); it; it++ ) + { + SEG seg = *it; + + if( seg.A.x > seg.B.x ) + seg.Reverse(); + + poly_segs.push_back( seg ); + } + + std::sort( poly_segs.begin(), poly_segs.end() ); } } - // iterate through all areas + std::vector> zonePairs; + for( size_t ia = 0; ia < m_board->m_DRCCopperZones.size(); ia++ ) { if( !reportProgress( layer_id * m_board->m_DRCCopperZones.size() + ia, @@ -1081,100 +1164,62 @@ void DRC_TEST_PROVIDER_COPPER_CLEARANCE::testZonesToZones() if( zoneA->GetNetCode() == zoneB->GetNetCode() && zoneA->GetNetCode() >= 0 ) continue; - // test for different priorities - if( zoneA->GetAssignedPriority() != zoneB->GetAssignedPriority() ) - continue; - // rule areas may overlap at will if( zoneA->GetIsRuleArea() || zoneB->GetIsRuleArea() ) continue; // Examine a candidate zone: compare zoneB to zoneA + SHAPE_POLY_SET* polyA = m_board->m_DRCCopperZones[ia]->GetFill( layer ); + SHAPE_POLY_SET* polyB = m_board->m_DRCCopperZones[ia2]->GetFill( layer ); + + if( !polyA->BBoxFromCaches().Intersects( polyB->BBoxFromCaches() ) ) + continue; // Get clearance used in zone to zone test. constraint = m_drcEngine->EvalRules( CLEARANCE_CONSTRAINT, zoneA, zoneB, layer ); zone2zoneClearance = constraint.GetValue().Min(); - if( constraint.GetSeverity() == RPT_SEVERITY_IGNORE ) + if( constraint.GetSeverity() == RPT_SEVERITY_IGNORE || zone2zoneClearance <= 0 ) continue; - if( testIntersects ) + futures.push_back( tp.submit( checkZones, ia, ia2, zone2zoneClearance, layer ) ); + } + } + } + + for( auto& task : futures ) + { + if( !task.valid() ) + continue; + + std::future_status result; + + while( true ) + { + result = task.wait_for( std::chrono::milliseconds( 200 ) ); + + if( m_drcEngine->IsCancelled() ) + { + cancelled = true; + break; + } + + if( result == std::future_status::ready ) + { + report_data data = task.get(); + int zoneA_id = std::get<0>( data ); + int zoneB_id = std::get<1>( data ); + VECTOR2I pt = std::get<2>( data ); + int actual = std::get<3>( data ); + int required = std::get<4>( data ); + PCB_LAYER_ID layer = std::get<5>( data ); + + if( zoneA_id != invalid_zone ) { - // test for some corners of zoneA inside zoneB - for( auto it = smoothed_polys[ia].IterateWithHoles(); it; it++ ) - { - VECTOR2I currentVertex = *it; + ZONE* zoneA = m_board->m_DRCCopperZones[zoneA_id]; + ZONE* zoneB = m_board->m_DRCCopperZones[zoneB_id]; - if( smoothed_polys[ia2].Contains( currentVertex ) ) - { - std::shared_ptr drce = DRC_ITEM::Create( DRCE_ZONES_INTERSECT ); - drce->SetItems( zoneA, zoneB ); - drce->SetViolatingRule( constraint.GetParentRule() ); - - reportViolation( drce, currentVertex, layer ); - } - } - - // test for some corners of zoneB inside zoneA - for( auto it = smoothed_polys[ia2].IterateWithHoles(); it; it++ ) - { - VECTOR2I currentVertex = *it; - - if( smoothed_polys[ia].Contains( currentVertex ) ) - { - std::shared_ptr drce = DRC_ITEM::Create( DRCE_ZONES_INTERSECT ); - drce->SetItems( zoneB, zoneA ); - drce->SetViolatingRule( constraint.GetParentRule() ); - - reportViolation( drce, currentVertex, layer ); - } - } - } - - // Iterate through all the segments of refSmoothedPoly - std::map conflictPoints; - - for( auto refIt = smoothed_polys[ia].IterateSegmentsWithHoles(); refIt; refIt++ ) - { - // Build ref segment - SEG refSegment = *refIt; - - // Iterate through all the segments in smoothed_polys[ia2] - for( auto it = smoothed_polys[ia2].IterateSegmentsWithHoles(); it; it++ ) - { - // Build test segment - SEG testSegment = *it; - VECTOR2I pt; - - int ax1, ay1, ax2, ay2; - ax1 = refSegment.A.x; - ay1 = refSegment.A.y; - ax2 = refSegment.B.x; - ay2 = refSegment.B.y; - - int bx1, by1, bx2, by2; - bx1 = testSegment.A.x; - by1 = testSegment.A.y; - bx2 = testSegment.B.x; - by2 = testSegment.B.y; - - int d = GetClearanceBetweenSegments( bx1, by1, bx2, by2, 0, - ax1, ay1, ax2, ay2, 0, - zone2zoneClearance, &pt.x, &pt.y ); - - if( d < zone2zoneClearance ) - { - if( conflictPoints.count( pt ) ) - conflictPoints[ pt ] = std::min( conflictPoints[ pt ], d ); - else - conflictPoints[ pt ] = d; - } - } - } - - for( const std::pair& conflict : conflictPoints ) - { - int actual = conflict.second; + constraint = m_drcEngine->EvalRules( CLEARANCE_CONSTRAINT, zoneA, zoneB, layer ); std::shared_ptr drce; if( actual <= 0 && testIntersects ) @@ -1186,7 +1231,7 @@ void DRC_TEST_PROVIDER_COPPER_CLEARANCE::testZonesToZones() drce = DRC_ITEM::Create( DRCE_CLEARANCE ); wxString msg = formatMsg( _( "(%s clearance %s; actual %s)" ), constraint.GetName(), - zone2zoneClearance, + required, std::max( actual, 0 ) ); drce->SetErrorMessage( drce->GetErrorText() + wxS( " " ) + msg ); @@ -1197,13 +1242,13 @@ void DRC_TEST_PROVIDER_COPPER_CLEARANCE::testZonesToZones() drce->SetItems( zoneA, zoneB ); drce->SetViolatingRule( constraint.GetParentRule() ); - reportViolation( drce, conflict.first, layer ); + reportViolation( drce, pt, layer ); } } - if( m_drcEngine->IsCancelled() ) - return; + break; } + } } } diff --git a/qa/data/pcbnew/intersectingzones.kicad_pcb b/qa/data/pcbnew/intersectingzones.kicad_pcb new file mode 100644 index 0000000000..20d663fd28 --- /dev/null +++ b/qa/data/pcbnew/intersectingzones.kicad_pcb @@ -0,0 +1,524 @@ +(kicad_pcb (version 20230620) (generator pcbnew) + + (general + (thickness 1.6) + ) + + (paper "A4") + (layers + (0 "F.Cu" signal) + (31 "B.Cu" signal) + (32 "B.Adhes" user "B.Adhesive") + (33 "F.Adhes" user "F.Adhesive") + (34 "B.Paste" user) + (35 "F.Paste" user) + (36 "B.SilkS" user "B.Silkscreen") + (37 "F.SilkS" user "F.Silkscreen") + (38 "B.Mask" user) + (39 "F.Mask" user) + (40 "Dwgs.User" user "User.Drawings") + (41 "Cmts.User" user "User.Comments") + (42 "Eco1.User" user "User.Eco1") + (43 "Eco2.User" user "User.Eco2") + (44 "Edge.Cuts" user) + (45 "Margin" user) + (46 "B.CrtYd" user "B.Courtyard") + (47 "F.CrtYd" user "F.Courtyard") + (48 "B.Fab" user) + (49 "F.Fab" user) + (50 "User.1" user) + (51 "User.2" user) + (52 "User.3" user) + (53 "User.4" user) + (54 "User.5" user) + (55 "User.6" user) + (56 "User.7" user) + (57 "User.8" user) + (58 "User.9" user) + ) + + (setup + (pad_to_mask_clearance 0) + (pcbplotparams + (layerselection 0x00010fc_ffffffff) + (plot_on_all_layers_selection 0x0000000_00000000) + (disableapertmacros false) + (usegerberextensions false) + (usegerberattributes true) + (usegerberadvancedattributes true) + (creategerberjobfile true) + (dashed_line_dash_ratio 12.000000) + (dashed_line_gap_ratio 3.000000) + (svgprecision 4) + (plotframeref false) + (viasonmask false) + (mode 1) + (useauxorigin false) + (hpglpennumber 1) + (hpglpenspeed 20) + (hpglpendiameter 15.000000) + (pdf_front_fp_property_popups true) + (pdf_back_fp_property_popups true) + (dxfpolygonmode true) + (dxfimperialunits true) + (dxfusepcbnewfont true) + (psnegative false) + (psa4output false) + (plotreference true) + (plotvalue true) + (plotinvisibletext false) + (sketchpadsonfab false) + (subtractmaskfromsilk false) + (outputformat 1) + (mirror false) + (drillshape 1) + (scaleselection 1) + (outputdirectory "") + ) + ) + + (net 0 "") + (net 1 "A") + (net 2 "B") + + (footprint "TestPoint:TestPoint_Pad_1.0x1.0mm" (layer "F.Cu") + (tstamp 0662cfad-e2af-4a28-8e06-54487b2d060f) + (at 126.383886 85.958221) + (descr "SMD rectangular pad as test Point, square 1.0mm side length") + (tags "test point SMD pad rectangle square") + (property "Reference" "REF**" (at 0 -1.448 0 unlocked) (layer "F.SilkS") (tstamp a9c377d9-d9a8-4957-9ef2-653acbec4dad) + (effects (font (size 1 1) (thickness 0.1))) + ) + (property "Value" "TestPoint_Pad_1.0x1.0mm" (at 0 1.55 0 unlocked) (layer "F.Fab") (tstamp 8722d65f-d023-4ca1-9653-ede49a60bc5e) + (effects (font (size 1 1) (thickness 0.15))) + ) + (property "Footprint" "TestPoint:TestPoint_Pad_1.0x1.0mm" (at 0 0 0 unlocked) (layer "F.Fab") hide (tstamp 1f52001b-ae94-49e9-b2b5-6673a1b966af) + (effects (font (size 1 1) (thickness 0.15))) + ) + (property "Datasheet" "" (at 0 0 0 unlocked) (layer "F.Fab") hide (tstamp 2cc1d293-3904-4185-9d78-14af283b2a39) + (effects (font (size 1 1) (thickness 0.15))) + ) + (property "Description" "" (at 0 0 0 unlocked) (layer "F.Fab") hide (tstamp 8d2540c1-292c-4170-86dd-0959865683a2) + (effects (font (size 1 1) (thickness 0.15))) + ) + (attr exclude_from_pos_files exclude_from_bom) + (fp_line (start -0.7 -0.7) (end 0.7 -0.7) + (stroke (width 0.12) (type solid)) (layer "F.SilkS") (tstamp de5cbed9-338f-4fba-aa6e-c8a10dd46598)) + (fp_line (start -0.7 0.7) (end -0.7 -0.7) + (stroke (width 0.12) (type solid)) (layer "F.SilkS") (tstamp 0004140c-4466-41de-a8de-f1da559892df)) + (fp_line (start 0.7 -0.7) (end 0.7 0.7) + (stroke (width 0.12) (type solid)) (layer "F.SilkS") (tstamp 6d11ed56-9675-4b43-9e47-1493d54e3ac3)) + (fp_line (start 0.7 0.7) (end -0.7 0.7) + (stroke (width 0.12) (type solid)) (layer "F.SilkS") (tstamp 20542c66-768b-43a3-a7a6-10c5c62bb528)) + (fp_line (start -1 -1) (end -1 1) + (stroke (width 0.05) (type solid)) (layer "F.CrtYd") (tstamp 78bcf185-2b20-4fc4-9289-1b0fa982c740)) + (fp_line (start -1 -1) (end 1 -1) + (stroke (width 0.05) (type solid)) (layer "F.CrtYd") (tstamp 058a4358-6342-4f80-80a8-3dbc570a4ca7)) + (fp_line (start 1 1) (end -1 1) + (stroke (width 0.05) (type solid)) (layer "F.CrtYd") (tstamp 1b5c24fb-0c0f-45c4-a0b6-2789970f4a7a)) + (fp_line (start 1 1) (end 1 -1) + (stroke (width 0.05) (type solid)) (layer "F.CrtYd") (tstamp 2a79ab99-848f-4e4a-9a6a-b6833477e773)) + (fp_text user "${REFERENCE}" (at 0 -1.45 0) (layer "F.Fab") (tstamp a858b269-27f4-4723-ba2d-fa8cdd0a9779) + (effects (font (size 1 1) (thickness 0.15))) + ) + (pad "1" smd rect (at 0 0) (size 1 1) (layers "F.Cu" "F.Mask") + (net 1 "A") + (tstamp ff356f6c-417c-45fd-8338-965175a521e8) + ) + ) + + (footprint "TestPoint:TestPoint_Pad_1.0x1.0mm" (layer "B.Cu") + (tstamp 42ed20a1-6256-4072-835a-a4bd28a9f9a8) + (at 128.622292 85.304594 180) + (descr "SMD rectangular pad as test Point, square 1.0mm side length") + (tags "test point SMD pad rectangle square") + (property "Reference" "REF**" (at 0 1.448 0 unlocked) (layer "B.SilkS") (tstamp 3467d764-46ee-4a7d-803e-52e5934180a2) + (effects (font (size 1 1) (thickness 0.1)) (justify mirror)) + ) + (property "Value" "TestPoint_Pad_1.0x1.0mm" (at 0 -1.55 0 unlocked) (layer "B.Fab") (tstamp a61e917b-111f-4d17-9565-6ae0b5832d32) + (effects (font (size 1 1) (thickness 0.15)) (justify mirror)) + ) + (property "Footprint" "TestPoint:TestPoint_Pad_1.0x1.0mm" (at 0 0 0 unlocked) (layer "B.Fab") hide (tstamp dd971483-3b14-41dd-be84-dd65b929cd2b) + (effects (font (size 1 1) (thickness 0.15)) (justify mirror)) + ) + (property "Datasheet" "" (at 0 0 0 unlocked) (layer "B.Fab") hide (tstamp 093cf8e6-f50b-4ee6-900c-63d6c741c2dd) + (effects (font (size 1 1) (thickness 0.15)) (justify mirror)) + ) + (property "Description" "" (at 0 0 0 unlocked) (layer "B.Fab") hide (tstamp ed3395ab-2567-4809-9ce8-0af0ac4a06cd) + (effects (font (size 1 1) (thickness 0.15)) (justify mirror)) + ) + (attr exclude_from_pos_files exclude_from_bom) + (fp_line (start 0.7 0.7) (end -0.7 0.7) + (stroke (width 0.12) (type solid)) (layer "B.SilkS") (tstamp 477ea467-832b-4e6a-9280-640c8709aedd)) + (fp_line (start 0.7 -0.7) (end 0.7 0.7) + (stroke (width 0.12) (type solid)) (layer "B.SilkS") (tstamp 6f8c6994-cad2-429a-a1c5-43413d1196e8)) + (fp_line (start -0.7 0.7) (end -0.7 -0.7) + (stroke (width 0.12) (type solid)) (layer "B.SilkS") (tstamp 585c91fb-6706-4683-bcd7-6beb6cecefd8)) + (fp_line (start -0.7 -0.7) (end 0.7 -0.7) + (stroke (width 0.12) (type solid)) (layer "B.SilkS") (tstamp 30b86a99-e8ac-45bd-b9f8-3f1f188d9521)) + (fp_line (start 1 1) (end 1 -1) + (stroke (width 0.05) (type solid)) (layer "B.CrtYd") (tstamp fcf86568-6d87-4044-8c2f-405cf9649f0e)) + (fp_line (start 1 1) (end -1 1) + (stroke (width 0.05) (type solid)) (layer "B.CrtYd") (tstamp bdcd4fd5-3c51-46b9-952d-edfef4ece6f5)) + (fp_line (start -1 -1) (end 1 -1) + (stroke (width 0.05) (type solid)) (layer "B.CrtYd") (tstamp d8a94b9c-85fe-4c07-80d1-074ae82400dd)) + (fp_line (start -1 -1) (end -1 1) + (stroke (width 0.05) (type solid)) (layer "B.CrtYd") (tstamp 2bdb98a7-177c-4399-b91b-0e73fdfb39e3)) + (fp_text user "${REFERENCE}" (at 0 1.45 180) (layer "B.Fab") (tstamp 3361ae7f-450d-488d-bc3e-4c7d82969fd2) + (effects (font (size 1 1) (thickness 0.15)) (justify mirror)) + ) + (pad "1" smd rect (at 0 0 180) (size 1 1) (layers "B.Cu" "B.Mask") + (net 2 "B") + (tstamp 8285774c-eb9a-4524-aae8-d1d472cd8c36) + ) + ) + + (gr_rect (start 120.188564 80.549407) (end 134.184964 91.096551) + (stroke (width 0.05) (type default)) (fill none) (layer "Edge.Cuts") (tstamp 24026c1a-e66c-43d9-874c-745b91ba08d9)) + (gr_text "Without refilling, \nthe inner zone \nis located too close" (at 122.336593 82.233874 0) (layer "Cmts.User") (tstamp 5bc9367f-80d0-476c-b99a-6c82dbf59fbc) + (effects (font (size 0.5 0.5) (thickness 0.125)) (justify left bottom)) + ) + + (via (at 131.802386 81.865308) (size 0.6) (drill 0.3) (layers "F.Cu" "B.Cu") (free) (net 2) (tstamp 2b81f30e-99a8-4345-9760-67513bc8c623)) + + (zone (net 1) (net_name "A") (layer "F.Cu") (tstamp fd8091a8-712f-4b09-81f9-cc203a1012a5) (hatch edge 0.5) + (priority 1) + (connect_pads (clearance 0.5)) + (min_thickness 0.25) (filled_areas_thickness no) + (fill yes (thermal_gap 0.5) (thermal_bridge_width 0.5)) + (polygon + (pts + (xy 122.70769 82.894624) + (xy 127.682194 82.784966) + (xy 127.742008 89.07537) + (xy 122.647877 88.736426) + ) + ) + (filled_polygon + (layer "F.Cu") + (pts + (xy 127.624117 82.805935) + (xy 127.671025 82.857718) + (xy 127.683387 82.910524) + (xy 127.706143 85.303649) + (xy 127.687097 85.370873) + (xy 127.634731 85.417127) + (xy 127.56567 85.427728) + (xy 127.501841 85.399309) + (xy 127.465967 85.348161) + (xy 127.413944 85.20868) + (xy 127.41394 85.208673) + (xy 127.32778 85.093579) + (xy 127.327777 85.093576) + (xy 127.212683 85.007416) + (xy 127.212676 85.007412) + (xy 127.077969 84.95717) + (xy 127.077962 84.957168) + (xy 127.018434 84.950767) + (xy 126.72059 84.950767) + (xy 126.72059 86.950767) + (xy 127.018418 86.950767) + (xy 127.018434 86.950766) + (xy 127.077962 86.944365) + (xy 127.077969 86.944363) + (xy 127.212676 86.894121) + (xy 127.212683 86.894117) + (xy 127.327777 86.807957) + (xy 127.32778 86.807954) + (xy 127.41394 86.69286) + (xy 127.413944 86.692853) + (xy 127.464186 86.558146) + (xy 127.464188 86.558139) + (xy 127.470358 86.500757) + (xy 127.497096 86.436205) + (xy 127.554488 86.396357) + (xy 127.624313 86.393864) + (xy 127.684402 86.429517) + (xy 127.715677 86.491996) + (xy 127.717641 86.512833) + (xy 127.740735 88.941582) + (xy 127.721689 89.008806) + (xy 127.669323 89.05506) + (xy 127.608509 89.066487) + (xy 122.764839 88.744208) + (xy 122.699254 88.720117) + (xy 122.657106 88.664391) + (xy 122.649077 88.619212) + (xy 122.66616 86.950766) + (xy 122.673839 86.200767) + (xy 125.47059 86.200767) + (xy 125.47059 86.498611) + (xy 125.476991 86.558139) + (xy 125.476993 86.558146) + (xy 125.527235 86.692853) + (xy 125.527239 86.69286) + (xy 125.613399 86.807954) + (xy 125.613402 86.807957) + (xy 125.728496 86.894117) + (xy 125.728503 86.894121) + (xy 125.86321 86.944363) + (xy 125.863217 86.944365) + (xy 125.922745 86.950766) + (xy 125.922762 86.950767) + (xy 126.220589 86.950767) + (xy 126.22059 86.200767) + (xy 125.47059 86.200767) + (xy 122.673839 86.200767) + (xy 122.678958 85.700767) + (xy 125.47059 85.700767) + (xy 126.220589 85.700767) + (xy 126.22059 84.950767) + (xy 125.922745 84.950767) + (xy 125.863217 84.957168) + (xy 125.86321 84.95717) + (xy 125.728503 85.007412) + (xy 125.728496 85.007416) + (xy 125.613402 85.093576) + (xy 125.613399 85.093579) + (xy 125.527239 85.208673) + (xy 125.527235 85.20868) + (xy 125.476993 85.343387) + (xy 125.476991 85.343394) + (xy 125.47059 85.402922) + (xy 125.47059 85.700767) + (xy 122.678958 85.700767) + (xy 122.70646 83.014675) + (xy 122.72683 82.947844) + (xy 122.7801 82.902632) + (xy 122.82772 82.891978) + (xy 127.55666 82.787733) + ) + ) + ) + (zone (net 2) (net_name "B") (layers "F&B.Cu") (tstamp cbf3ce30-83db-44ed-be70-44935fea7e57) (hatch edge 0.5) + (connect_pads (clearance 0.5)) + (min_thickness 0.25) (filled_areas_thickness no) + (fill yes (thermal_gap 0.5) (thermal_bridge_width 0.5)) + (polygon + (pts + (xy 118.583563 79.871518) + (xy 118.513781 92.023664) + (xy 135.590585 92.063539) + (xy 135.002417 80.190525) + ) + ) + (filled_polygon + (layer "F.Cu") + (pts + (xy 133.627503 81.069592) + (xy 133.673258 81.122396) + (xy 133.684464 81.173907) + (xy 133.684464 90.472051) + (xy 133.664779 90.53909) + (xy 133.611975 90.584845) + (xy 133.560464 90.596051) + (xy 120.813064 90.596051) + (xy 120.746025 90.576366) + (xy 120.70027 90.523562) + (xy 120.689064 90.472051) + (xy 120.689064 88.6215) + (xy 122.056899 88.6215) + (xy 122.064671 88.715117) + (xy 122.072702 88.760304) + (xy 122.097652 88.850836) + (xy 122.097653 88.850838) + (xy 122.097654 88.850841) + (xy 122.167233 88.976779) + (xy 122.167235 88.976782) + (xy 122.167237 88.976785) + (xy 122.189915 89.006769) + (xy 122.209381 89.032505) + (xy 122.209388 89.032515) + (xy 122.311632 89.133746) + (xy 122.438254 89.202072) + (xy 122.503839 89.226163) + (xy 122.644575 89.256047) + (xy 127.488245 89.578326) + (xy 127.488246 89.578325) + (xy 127.488248 89.578326) + (xy 127.522856 89.576259) + (xy 127.615155 89.570747) + (xy 127.675969 89.55932) + (xy 127.796972 89.520317) + (xy 127.917267 89.441382) + (xy 127.940871 89.420532) + (xy 127.969625 89.395136) + (xy 127.969636 89.395125) + (xy 128.062819 89.285496) + (xy 128.121342 89.154055) + (xy 128.140388 89.086831) + (xy 128.159508 88.94423) + (xy 128.157329 88.715115) + (xy 128.136313 86.504804) + (xy 128.135208 86.483479) + (xy 128.132242 86.452014) + (xy 128.132241 86.452014) + (xy 128.126441 86.409695) + (xy 128.081003 86.273178) + (xy 128.049728 86.210699) + (xy 128.049724 86.210693) + (xy 127.967674 86.092518) + (xy 127.96767 86.092514) + (xy 127.864369 86.009269) + (xy 127.824521 85.951877) + (xy 127.822027 85.882052) + (xy 127.857679 85.821963) + (xy 127.874151 85.809042) + (xy 127.882675 85.803449) + (xy 127.909751 85.779533) + (xy 127.935033 85.757203) + (xy 127.935044 85.757192) + (xy 128.028227 85.647563) + (xy 128.08675 85.516122) + (xy 128.105796 85.448898) + (xy 128.124916 85.306296) + (xy 128.10216 82.913171) + (xy 128.088876 82.802755) + (xy 128.076514 82.749949) + (xy 128.0394 82.645096) + (xy 127.958963 82.5258) + (xy 127.954746 82.521145) + (xy 127.924494 82.487748) + (xy 127.919398 82.4801) + (xy 127.801268 82.382215) + (xy 127.801267 82.382214) + (xy 127.669103 82.325344) + (xy 127.669102 82.325343) + (xy 127.669096 82.325341) + (xy 127.669094 82.32534) + (xy 127.601645 82.307141) + (xy 127.458816 82.28981) + (xy 127.458815 82.28981) + (xy 123.267027 82.382214) + (xy 122.729875 82.394055) + (xy 122.729861 82.394056) + (xy 122.630671 82.406123) + (xy 122.630635 82.406129) + (xy 122.583048 82.416776) + (xy 122.583034 82.416779) + (xy 122.583029 82.416781) + (xy 122.583022 82.416783) + (xy 122.583023 82.416783) + (xy 122.488125 82.44814) + (xy 122.366292 82.524685) + (xy 122.313028 82.569892) + (xy 122.31301 82.569909) + (xy 122.217699 82.677654) + (xy 122.217693 82.677663) + (xy 122.15659 82.807909) + (xy 122.156587 82.807916) + (xy 122.156588 82.807916) + (xy 122.136218 82.874747) + (xy 122.136216 82.874755) + (xy 122.12896 82.921791) + (xy 122.114282 83.016951) + (xy 122.08678 85.703046) + (xy 122.085536 85.824418) + (xy 122.081661 86.203046) + (xy 122.056899 88.621498) + (xy 122.056899 88.6215) + (xy 120.689064 88.6215) + (xy 120.689064 81.173907) + (xy 120.708749 81.106868) + (xy 120.761553 81.061113) + (xy 120.813064 81.049907) + (xy 133.560464 81.049907) + ) + ) + (filled_polygon + (layer "B.Cu") + (pts + (xy 133.627503 81.069592) + (xy 133.673258 81.122396) + (xy 133.684464 81.173907) + (xy 133.684464 90.472051) + (xy 133.664779 90.53909) + (xy 133.611975 90.584845) + (xy 133.560464 90.596051) + (xy 120.813064 90.596051) + (xy 120.746025 90.576366) + (xy 120.70027 90.523562) + (xy 120.689064 90.472051) + (xy 120.689064 85.554594) + (xy 127.622292 85.554594) + (xy 127.622292 85.852438) + (xy 127.628693 85.911966) + (xy 127.628695 85.911973) + (xy 127.678937 86.04668) + (xy 127.678941 86.046687) + (xy 127.765101 86.161781) + (xy 127.765104 86.161784) + (xy 127.880198 86.247944) + (xy 127.880205 86.247948) + (xy 128.014912 86.29819) + (xy 128.014919 86.298192) + (xy 128.074447 86.304593) + (xy 128.074464 86.304594) + (xy 128.372292 86.304594) + (xy 128.372292 85.554594) + (xy 128.872292 85.554594) + (xy 128.872292 86.304594) + (xy 129.17012 86.304594) + (xy 129.170136 86.304593) + (xy 129.229664 86.298192) + (xy 129.229671 86.29819) + (xy 129.364378 86.247948) + (xy 129.364385 86.247944) + (xy 129.479479 86.161784) + (xy 129.479482 86.161781) + (xy 129.565642 86.046687) + (xy 129.565646 86.04668) + (xy 129.615888 85.911973) + (xy 129.61589 85.911966) + (xy 129.622291 85.852438) + (xy 129.622292 85.852421) + (xy 129.622292 85.554594) + (xy 128.872292 85.554594) + (xy 128.372292 85.554594) + (xy 127.622292 85.554594) + (xy 120.689064 85.554594) + (xy 120.689064 85.054594) + (xy 127.622292 85.054594) + (xy 128.372292 85.054594) + (xy 128.372292 84.304594) + (xy 128.872292 84.304594) + (xy 128.872292 85.054594) + (xy 129.622292 85.054594) + (xy 129.622292 84.756766) + (xy 129.622291 84.756749) + (xy 129.61589 84.697221) + (xy 129.615888 84.697214) + (xy 129.565646 84.562507) + (xy 129.565642 84.5625) + (xy 129.479482 84.447406) + (xy 129.479479 84.447403) + (xy 129.364385 84.361243) + (xy 129.364378 84.361239) + (xy 129.229671 84.310997) + (xy 129.229664 84.310995) + (xy 129.170136 84.304594) + (xy 128.872292 84.304594) + (xy 128.372292 84.304594) + (xy 128.074447 84.304594) + (xy 128.014919 84.310995) + (xy 128.014912 84.310997) + (xy 127.880205 84.361239) + (xy 127.880198 84.361243) + (xy 127.765104 84.447403) + (xy 127.765101 84.447406) + (xy 127.678941 84.5625) + (xy 127.678937 84.562507) + (xy 127.628695 84.697214) + (xy 127.628693 84.697221) + (xy 127.622292 84.756749) + (xy 127.622292 85.054594) + (xy 120.689064 85.054594) + (xy 120.689064 81.173907) + (xy 120.708749 81.106868) + (xy 120.761553 81.061113) + (xy 120.813064 81.049907) + (xy 133.560464 81.049907) + ) + ) + ) +) diff --git a/qa/data/pcbnew/intersectingzones.kicad_pro b/qa/data/pcbnew/intersectingzones.kicad_pro new file mode 100644 index 0000000000..fe82e52951 --- /dev/null +++ b/qa/data/pcbnew/intersectingzones.kicad_pro @@ -0,0 +1,232 @@ +{ + "board": { + "3dviewports": [], + "design_settings": { + "defaults": { + "board_outline_line_width": 0.049999999999999996, + "copper_line_width": 0.19999999999999998, + "copper_text_italic": false, + "copper_text_size_h": 1.5, + "copper_text_size_v": 1.5, + "copper_text_thickness": 0.3, + "copper_text_upright": false, + "courtyard_line_width": 0.049999999999999996, + "dimension_precision": 4, + "dimension_units": 3, + "dimensions": { + "arrow_length": 1270000, + "extension_offset": 500000, + "keep_text_aligned": true, + "suppress_zeroes": false, + "text_position": 0, + "units_format": 1 + }, + "fab_line_width": 0.09999999999999999, + "fab_text_italic": false, + "fab_text_size_h": 1.0, + "fab_text_size_v": 1.0, + "fab_text_thickness": 0.15, + "fab_text_upright": false, + "other_line_width": 0.09999999999999999, + "other_text_italic": false, + "other_text_size_h": 1.0, + "other_text_size_v": 1.0, + "other_text_thickness": 0.15, + "other_text_upright": false, + "pads": { + "drill": 0.0, + "height": 1.0, + "width": 1.0 + }, + "silk_line_width": 0.09999999999999999, + "silk_text_italic": false, + "silk_text_size_h": 1.0, + "silk_text_size_v": 1.0, + "silk_text_thickness": 0.09999999999999999, + "silk_text_upright": false, + "zones": { + "min_clearance": 0.5 + } + }, + "diff_pair_dimensions": [], + "drc_exclusions": [], + "meta": { + "version": 2 + }, + "rule_severities": { + "annular_width": "error", + "clearance": "error", + "connection_width": "warning", + "copper_edge_clearance": "error", + "copper_sliver": "warning", + "courtyards_overlap": "error", + "diff_pair_gap_out_of_range": "error", + "diff_pair_uncoupled_length_too_long": "error", + "drill_out_of_range": "error", + "duplicate_footprints": "warning", + "extra_footprint": "warning", + "footprint": "error", + "footprint_type_mismatch": "ignore", + "hole_clearance": "error", + "hole_near_hole": "error", + "invalid_outline": "error", + "isolated_copper": "warning", + "item_on_disabled_layer": "error", + "items_not_allowed": "error", + "length_out_of_range": "error", + "lib_footprint_issues": "warning", + "lib_footprint_mismatch": "warning", + "malformed_courtyard": "error", + "microvia_drill_out_of_range": "error", + "missing_courtyard": "ignore", + "missing_footprint": "warning", + "net_conflict": "warning", + "npth_inside_courtyard": "ignore", + "padstack": "warning", + "pth_inside_courtyard": "ignore", + "shorting_items": "error", + "silk_edge_clearance": "warning", + "silk_over_copper": "warning", + "silk_overlap": "warning", + "skew_out_of_range": "error", + "solder_mask_bridge": "error", + "starved_thermal": "error", + "text_height": "warning", + "text_thickness": "warning", + "through_hole_pad_without_hole": "error", + "too_many_vias": "error", + "track_dangling": "warning", + "track_width": "error", + "tracks_crossing": "error", + "unconnected_items": "error", + "unresolved_variable": "error", + "via_dangling": "warning", + "zones_intersect": "error" + }, + "rules": { + "max_error": 0.005, + "min_clearance": 0.0, + "min_connection": 0.0, + "min_copper_edge_clearance": 0.5, + "min_hole_clearance": 0.25, + "min_hole_to_hole": 0.25, + "min_microvia_diameter": 0.19999999999999998, + "min_microvia_drill": 0.09999999999999999, + "min_resolved_spokes": 2, + "min_silk_clearance": 0.0, + "min_text_height": 0.7999999999999999, + "min_text_thickness": 0.08, + "min_through_hole_diameter": 0.3, + "min_track_width": 0.0, + "min_via_annular_width": 0.09999999999999999, + "min_via_diameter": 0.5, + "solder_mask_to_copper_clearance": 0.0, + "use_height_for_length_calcs": true + }, + "teardrop_options": [ + { + "td_onpadsmd": true, + "td_onroundshapesonly": false, + "td_ontrackend": false, + "td_onviapad": true + } + ], + "teardrop_parameters": [ + { + "td_allow_use_two_tracks": true, + "td_curve_segcount": 0, + "td_height_ratio": 1.0, + "td_length_ratio": 0.5, + "td_maxheight": 2.0, + "td_maxlen": 1.0, + "td_on_pad_in_zone": false, + "td_target_name": "td_round_shape", + "td_width_to_size_filter_ratio": 0.9 + }, + { + "td_allow_use_two_tracks": true, + "td_curve_segcount": 0, + "td_height_ratio": 1.0, + "td_length_ratio": 0.5, + "td_maxheight": 2.0, + "td_maxlen": 1.0, + "td_on_pad_in_zone": false, + "td_target_name": "td_rect_shape", + "td_width_to_size_filter_ratio": 0.9 + }, + { + "td_allow_use_two_tracks": true, + "td_curve_segcount": 0, + "td_height_ratio": 1.0, + "td_length_ratio": 0.5, + "td_maxheight": 2.0, + "td_maxlen": 1.0, + "td_on_pad_in_zone": false, + "td_target_name": "td_track_end", + "td_width_to_size_filter_ratio": 0.9 + } + ], + "track_widths": [], + "via_dimensions": [], + "zones_allow_external_fillets": false + }, + "layer_presets": [], + "viewports": [] + }, + "boards": [], + "cvpcb": { + "equivalence_files": [] + }, + "libraries": { + "pinned_footprint_libs": [], + "pinned_symbol_libs": [] + }, + "meta": { + "filename": "intersectingzones.kicad_pro", + "version": 1 + }, + "net_settings": { + "classes": [ + { + "bus_width": 12, + "clearance": 0.2, + "diff_pair_gap": 0.25, + "diff_pair_via_gap": 0.25, + "diff_pair_width": 0.2, + "line_style": 0, + "microvia_diameter": 0.3, + "microvia_drill": 0.1, + "name": "Default", + "pcb_color": "rgba(0, 0, 0, 0.000)", + "schematic_color": "rgba(0, 0, 0, 0.000)", + "track_width": 0.2, + "via_diameter": 0.6, + "via_drill": 0.3, + "wire_width": 6 + } + ], + "meta": { + "version": 3 + }, + "net_colors": null, + "netclass_assignments": null, + "netclass_patterns": [] + }, + "pcbnew": { + "last_paths": { + "gencad": "", + "idf": "", + "netlist": "", + "specctra_dsn": "", + "step": "", + "vrml": "" + }, + "page_layout_descr_file": "" + }, + "schematic": { + "legacy_lib_dir": "", + "legacy_lib_list": [] + }, + "sheets": [], + "text_variables": {} +} diff --git a/qa/tests/pcbnew/drc/test_drc_regressions.cpp b/qa/tests/pcbnew/drc/test_drc_regressions.cpp index 67f6b328fa..d2f2a96d26 100644 --- a/qa/tests/pcbnew/drc/test_drc_regressions.cpp +++ b/qa/tests/pcbnew/drc/test_drc_regressions.cpp @@ -187,3 +187,63 @@ BOOST_FIXTURE_TEST_CASE( DRCFalseNegativeRegressions, DRC_REGRESSION_TEST_FIXTUR } } } + + +BOOST_FIXTURE_TEST_CASE( DRCFalseNegativeRegressionsNoFill, DRC_REGRESSION_TEST_FIXTURE ) +{ + // These documents at one time failed to catch DRC errors that they should have but only + // when the zones were filled. In other words, refilling the zone will fix the error, but + // DRC should have caught it regardless + + std::vector< std::pair > tests = + { + { "intersectingzones", 1 } // zones are too close to each other + }; + + for( const std::pair& entry : tests ) + { + KI_TEST::LoadBoard( m_settingsManager, entry.first, m_board ); + + std::vector violations; + BOARD_DESIGN_SETTINGS& bds = m_board->GetDesignSettings(); + + // Disable DRC tests not useful in this testcase + bds.m_DRCSeverities[ DRCE_COPPER_SLIVER ] = SEVERITY::RPT_SEVERITY_IGNORE; + bds.m_DRCSeverities[ DRCE_LIB_FOOTPRINT_ISSUES ] = SEVERITY::RPT_SEVERITY_IGNORE; + bds.m_DRCSeverities[ DRCE_LIB_FOOTPRINT_MISMATCH ] = SEVERITY::RPT_SEVERITY_IGNORE; + + bds.m_DRCEngine->SetViolationHandler( + [&]( const std::shared_ptr& aItem, VECTOR2I aPos, int aLayer ) + { + PCB_MARKER temp( aItem, aPos ); + + if( bds.m_DrcExclusions.find( temp.Serialize() ) == bds.m_DrcExclusions.end() ) + violations.push_back( *aItem ); + } ); + + bds.m_DRCEngine->RunTests( EDA_UNITS::MILLIMETRES, true, false ); + + if( violations.size() == entry.second ) + { + BOOST_CHECK_EQUAL( 1, 1 ); // quiet "did not check any assertions" warning + BOOST_TEST_MESSAGE( wxString::Format( "DRC regression: %s, passed", entry.first ) ); + } + else + { + BOOST_CHECK_EQUAL( violations.size(), entry.second ); + + UNITS_PROVIDER unitsProvider( pcbIUScale, EDA_UNITS::INCHES ); + + std::map itemMap; + m_board->FillItemMap( itemMap ); + + for( const DRC_ITEM& item : violations ) + { + BOOST_TEST_MESSAGE( item.ShowReport( &unitsProvider, RPT_SEVERITY_ERROR, + itemMap ) ); + } + + BOOST_ERROR( wxString::Format( "DRC regression: %s, failed", entry.first ) ); + } + } +}