KiCad PCB EDA Suite
zones_test_and_combine_areas.cpp
Go to the documentation of this file.
1 
7 /*
8  * This program source code file is part of KiCad, a free EDA CAD application.
9  *
10  * Copyright (C) 2012 Jean-Pierre Charras, jean-pierre.charras@ujf-grenoble.fr
11  * Copyright (C) 1992-2012 KiCad Developers, see AUTHORS.txt for contributors.
12  *
13  * Some code comes from FreePCB.
14  *
15  * This program is free software; you can redistribute it and/or
16  * modify it under the terms of the GNU General Public License
17  * as published by the Free Software Foundation; either version 2
18  * of the License, or (at your option) any later version.
19  *
20  * This program is distributed in the hope that it will be useful,
21  * but WITHOUT ANY WARRANTY; without even the implied warranty of
22  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23  * GNU General Public License for more details.
24  *
25  * You should have received a copy of the GNU General Public License
26  * along with this program; if not, you may find one here:
27  * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
28  * or you may search the http://www.gnu.org website for the version 2 license,
29  * or you may write to the Free Software Foundation, Inc.,
30  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
31  */
32 
33 #include <fctsys.h>
34 #include <common.h>
35 #include <confirm.h>
37 
38 #include <class_board.h>
39 #include <class_zone.h>
40 #include <class_marker_pcb.h>
41 
42 #include <pcbnew.h>
43 #include <drc_stuff.h>
44 #include <math_for_graphics.h>
45 
46 #define STRAIGHT 0 // To be remove after math_for_graphics code cleanup
47 
48 
50  ZONE_CONTAINER* modified_area )
51 {
52  // clip polygon against itself
53  bool modified = NormalizeAreaPolygon( aModifiedZonesList, modified_area );
54 
55  // now see if we need to clip against other areas
56  LAYER_NUM layer = modified_area->GetLayer();
57  bool bCheckAllAreas = TestAreaIntersections( modified_area );
58 
59  if( bCheckAllAreas )
60  {
61  modified = true;
62  CombineAllAreasInNet( aModifiedZonesList, modified_area->GetNetCode(), true );
63  }
64 
65  if( !IsCopperLayer( layer ) ) // Refill non copper zones on this layer
66  {
67  for( unsigned ia = 0; ia < m_ZoneDescriptorList.size(); ia++ )
68  if( m_ZoneDescriptorList[ia]->GetLayer() == layer )
69  m_ZoneDescriptorList[ia]->BuildFilledSolidAreasPolygons( this );
70  }
71 
72  // Test for bad areas: all zones must have more than 2 corners:
73  // Note: should not happen, but just in case.
74  for( unsigned ii = 0; ii < m_ZoneDescriptorList.size(); )
75  {
77 
78  if( zone->GetNumCorners() >= 3 )
79  ii++;
80  else // Remove zone because it is incorrect:
81  RemoveArea( aModifiedZonesList, zone );
82  }
83 
84  return modified;
85 }
86 
87 
88 bool BOARD::CombineAllAreasInNet( PICKED_ITEMS_LIST* aDeletedList, int aNetCode,
89  bool aUseLocalFlags )
90 {
91  if( m_ZoneDescriptorList.size() <= 1 )
92  return false;
93 
94  bool modified = false;
95 
96  // Loop through all combinations
97  for( unsigned ia1 = 0; ia1 < m_ZoneDescriptorList.size() - 1; ia1++ )
98  {
99  ZONE_CONTAINER* curr_area = m_ZoneDescriptorList[ia1];
100 
101  if( curr_area->GetNetCode() != aNetCode )
102  continue;
103 
104  // legal polygon
105  BOX2I b1 = curr_area->Outline()->BBox();
106  bool mod_ia1 = false;
107 
108  for( unsigned ia2 = m_ZoneDescriptorList.size() - 1; ia2 > ia1; ia2-- )
109  {
110  ZONE_CONTAINER* area2 = m_ZoneDescriptorList[ia2];
111 
112  if( area2->GetNetCode() != aNetCode )
113  continue;
114 
115  if( curr_area->GetPriority() != area2->GetPriority() )
116  continue;
117 
118  if( curr_area->GetIsKeepout() != area2->GetIsKeepout() )
119  continue;
120 
121  if( curr_area->GetLayer() != area2->GetLayer() )
122  continue;
123 
124  BOX2I b2 = area2->Outline()->BBox();
125 
126  if( b1.Intersects( b2 ) )
127  {
128  // check area2 against curr_area
129  if( curr_area->GetLocalFlags() || area2->GetLocalFlags()
130  || aUseLocalFlags == false )
131  {
132  bool ret = TestAreaIntersection( curr_area, area2 );
133 
134  if( ret )
135  ret = CombineAreas( aDeletedList, curr_area, area2 );
136 
137  if( ret )
138  {
139  mod_ia1 = true;
140  modified = true;
141  }
142  }
143  }
144  }
145 
146  if( mod_ia1 )
147  ia1--; // if modified, we need to check it again
148  }
149 
150  return modified;
151 }
152 
153 
155 {
156  for( unsigned ia2 = 0; ia2 < m_ZoneDescriptorList.size(); ia2++ )
157  {
158  ZONE_CONTAINER* area2 = m_ZoneDescriptorList[ia2];
159 
160  if( area_to_test->GetNetCode() != area2->GetNetCode() )
161  continue;
162 
163  if( area_to_test == area2 )
164  continue;
165 
166  // see if areas are on same layer
167  if( area_to_test->GetLayer() != area2->GetLayer() )
168  continue;
169 
170  // test for different priorities
171  if( area_to_test->GetPriority() != area2->GetPriority() )
172  continue;
173 
174  // test for different types
175  if( area_to_test->GetIsKeepout() != area2->GetIsKeepout() )
176  continue;
177 
178  if( TestAreaIntersection( area_to_test, area2 ) )
179  return true;
180  }
181 
182  return false;
183 }
184 
185 
187 {
188  // see if areas are on same layer
189  if( area_ref->GetLayer() != area_to_test->GetLayer() )
190  return false;
191 
192  SHAPE_POLY_SET* poly1 = area_ref->Outline();
193  SHAPE_POLY_SET* poly2 = area_to_test->Outline();
194 
195  // test bounding rects
196  BOX2I b1 = poly1->BBox();
197  BOX2I b2 = poly2->BBox();
198 
199  if( ! b1.Intersects( b2 ) )
200  return false;
201 
202  // Now test for intersecting segments
203  for( auto segIterator1 = poly1->IterateSegmentsWithHoles(); segIterator1; segIterator1++ )
204  {
205  // Build segment
206  SEG firstSegment = *segIterator1;
207 
208  for( auto segIterator2 = poly2->IterateSegmentsWithHoles(); segIterator2; segIterator2++ )
209  {
210  // Build second segment
211  SEG secondSegment = *segIterator2;
212 
213  // Check whether the two segments built collide
214  if( firstSegment.Collide( secondSegment, 0 ) )
215  return true;
216  }
217  }
218 
219  // If a contour is inside another contour, no segments intersects, but the zones
220  // can be combined if a corner is inside an outline (only one corner is enough)
221  for( auto iter = poly2->IterateWithHoles(); iter; iter++ )
222  {
223  if( poly1->Contains( *iter ) )
224  return true;
225  }
226 
227  for( auto iter = poly1->IterateWithHoles(); iter; iter++ )
228  {
229  if( poly2->Contains( *iter ) )
230  return true;
231  }
232 
233  return false;
234 }
235 
236 
237 bool BOARD::CombineAreas( PICKED_ITEMS_LIST* aDeletedList, ZONE_CONTAINER* area_ref,
238  ZONE_CONTAINER* area_to_combine )
239 {
240  if( area_ref == area_to_combine )
241  {
242  wxASSERT( 0 );
243  return false;
244  }
245 
246  SHAPE_POLY_SET mergedOutlines = *area_ref->Outline();
247  SHAPE_POLY_SET areaToMergePoly = *area_to_combine->Outline();
248 
249  mergedOutlines.BooleanAdd( areaToMergePoly, SHAPE_POLY_SET::PM_FAST );
250  mergedOutlines.Simplify( SHAPE_POLY_SET::PM_FAST );
251 
252  // We should have one polygon with hole
253  // We can have 2 polygons with hole, if the 2 initial polygons have only one common corner
254  // and therefore cannot be merged (they are dectected as intersecting)
255  // but we should never have more than 2 polys
256  if( mergedOutlines.OutlineCount() > 2 )
257  {
258  wxLogMessage(wxT("BOARD::CombineAreas error: more than 2 polys after merging") );
259  return false;
260  }
261 
262  if( mergedOutlines.OutlineCount() > 1 )
263  return false;
264 
265  // Update the area with the new merged outline
266  delete area_ref->Outline();
267  area_ref->SetOutline( new SHAPE_POLY_SET( mergedOutlines ) );
268 
269  RemoveArea( aDeletedList, area_to_combine );
270 
271  area_ref->SetLocalFlags( 1 );
272  area_ref->Hatch();
273 
274  return true;
275 }
276 
277 
279  bool aCreate_Markers )
280 {
281  int nerrors = 0;
282 
283  // iterate through all areas
284  for( int ia = 0; ia < GetAreaCount(); ia++ )
285  {
286  ZONE_CONTAINER* Area_Ref = GetArea( ia );
287  SHAPE_POLY_SET* refSmoothedPoly = Area_Ref->GetSmoothedPoly();
288 
289  if( !Area_Ref->IsOnCopperLayer() )
290  continue;
291 
292  // When testing only a single area, skip all others
293  if( aArea_To_Examine && (aArea_To_Examine != Area_Ref) )
294  continue;
295 
296  for( int ia2 = 0; ia2 < GetAreaCount(); ia2++ )
297  {
298  ZONE_CONTAINER* area_to_test = GetArea( ia2 );
299  SHAPE_POLY_SET* testSmoothedPoly = area_to_test->GetSmoothedPoly();
300 
301  if( Area_Ref == area_to_test )
302  continue;
303 
304  // test for same layer
305  if( Area_Ref->GetLayer() != area_to_test->GetLayer() )
306  continue;
307 
308  // Test for same net
309  if( Area_Ref->GetNetCode() == area_to_test->GetNetCode() && Area_Ref->GetNetCode() >= 0 )
310  continue;
311 
312  // test for different priorities
313  if( Area_Ref->GetPriority() != area_to_test->GetPriority() )
314  continue;
315 
316  // test for different types
317  if( Area_Ref->GetIsKeepout() != area_to_test->GetIsKeepout() )
318  continue;
319 
320  // Examine a candidate zone: compare area_to_test to Area_Ref
321 
322  // Get clearance used in zone to zone test. The policy used to
323  // obtain that value is now part of the zone object itself by way of
324  // ZONE_CONTAINER::GetClearance().
325  int zone2zoneClearance = Area_Ref->GetClearance( area_to_test );
326 
327  // Keepout areas have no clearance, so set zone2zoneClearance to 1
328  // ( zone2zoneClearance = 0 can create problems in test functions)
329  if( Area_Ref->GetIsKeepout() )
330  zone2zoneClearance = 1;
331 
332  // test for some corners of Area_Ref inside area_to_test
333  for( auto iterator = refSmoothedPoly->IterateWithHoles(); iterator; iterator++ )
334  {
335  VECTOR2I currentVertex = *iterator;
336 
337  if( testSmoothedPoly->Contains( currentVertex ) )
338  {
339  // COPPERAREA_COPPERAREA error: copper area ref corner inside copper area
340  if( aCreate_Markers )
341  {
342  int x = currentVertex.x;
343  int y = currentVertex.y;
344 
345  wxString msg1 = Area_Ref->GetSelectMenuText();
346  wxString msg2 = area_to_test->GetSelectMenuText();
348  wxPoint( x, y ),
349  msg1, wxPoint( x, y ),
350  msg2, wxPoint( x, y ) );
351  Add( marker );
352  }
353 
354  nerrors++;
355  }
356  }
357 
358  // test for some corners of area_to_test inside Area_Ref
359  for( auto iterator = testSmoothedPoly->IterateWithHoles(); iterator; iterator++ )
360  {
361  VECTOR2I currentVertex = *iterator;
362 
363  if( refSmoothedPoly->Contains( currentVertex ) )
364  {
365  // COPPERAREA_COPPERAREA error: copper area corner inside copper area ref
366  if( aCreate_Markers )
367  {
368  int x = currentVertex.x;
369  int y = currentVertex.y;
370 
371  wxString msg1 = area_to_test->GetSelectMenuText();
372  wxString msg2 = Area_Ref->GetSelectMenuText();
374  wxPoint( x, y ),
375  msg1, wxPoint( x, y ),
376  msg2, wxPoint( x, y ) );
377  Add( marker );
378  }
379 
380  nerrors++;
381  }
382  }
383 
384 
385  // Iterate through all the segments of refSmoothedPoly
386  for( auto refIt = refSmoothedPoly->IterateSegmentsWithHoles(); refIt; refIt++ )
387  {
388  // Build ref segment
389  SEG refSegment = *refIt;
390 
391  // Iterate through all the segments in testSmoothedPoly
392  for( auto testIt = testSmoothedPoly->IterateSegmentsWithHoles(); testIt; testIt++ )
393  {
394  // Build test segment
395  SEG testSegment = *testIt;
396 
397  int x, y;
398 
399  int ax1, ay1, ax2, ay2;
400  ax1 = refSegment.A.x;
401  ay1 = refSegment.A.y;
402  ax2 = refSegment.B.x;
403  ay2 = refSegment.B.y;
404 
405  int bx1, by1, bx2, by2;
406  bx1 = testSegment.A.x;
407  by1 = testSegment.A.y;
408  bx2 = testSegment.B.x;
409  by2 = testSegment.B.y;
410 
411  int d = GetClearanceBetweenSegments( bx1, by1, bx2, by2,
412  0,
413  ax1, ay1, ax2, ay2,
414  0,
415  zone2zoneClearance,
416  &x, &y );
417 
418  if( d < zone2zoneClearance )
419  {
420  // COPPERAREA_COPPERAREA error : intersect or too close
421  if( aCreate_Markers )
422  {
423  wxString msg1 = Area_Ref->GetSelectMenuText();
424  wxString msg2 = area_to_test->GetSelectMenuText();
426  wxPoint( x, y ),
427  msg1, wxPoint( x, y ),
428  msg2, wxPoint( x, y ) );
429  Add( marker );
430  }
431 
432  nerrors++;
433  }
434  }
435  }
436  }
437  }
438 
439  return nerrors;
440 }
441 
442 
443 bool DRC::doEdgeZoneDrc( ZONE_CONTAINER* aArea, int aCornerIndex )
444 {
445  if( !aArea->IsOnCopperLayer() ) // Cannot have a Drc error if not on copper layer
446  return true;
447  // Get polygon, contour and vertex index.
449 
450  // If the vertex does not exist, there is no conflict
451  if( !aArea->Outline()->GetRelativeIndices( aCornerIndex, &index ) )
452  return true;
453 
454  // Retrieve the selected contour
455  SHAPE_LINE_CHAIN contour;
456  contour = aArea->Outline()->Polygon( index.m_polygon )[index.m_contour];
457 
458  // Retrieve the segment that starts at aCornerIndex-th corner.
459  SEG selectedSegment = contour.Segment( index.m_vertex );
460 
461  VECTOR2I start = selectedSegment.A;
462  VECTOR2I end = selectedSegment.B;
463 
464  // iterate through all areas
465  for( int ia2 = 0; ia2 < m_pcb->GetAreaCount(); ia2++ )
466  {
467  ZONE_CONTAINER* area_to_test = m_pcb->GetArea( ia2 );
468  int zone_clearance = std::max( area_to_test->GetZoneClearance(),
469  aArea->GetZoneClearance() );
470 
471  // test for same layer
472  if( area_to_test->GetLayer() != aArea->GetLayer() )
473  continue;
474 
475  // Test for same net
476  if( ( aArea->GetNetCode() == area_to_test->GetNetCode() ) && (aArea->GetNetCode() >= 0) )
477  continue;
478 
479  // test for same priority
480  if( area_to_test->GetPriority() != aArea->GetPriority() )
481  continue;
482 
483  // test for same type
484  if( area_to_test->GetIsKeepout() != aArea->GetIsKeepout() )
485  continue;
486 
487  // For keepout, there is no clearance, so use a minimal value for it
488  // use 1, not 0 as value to avoid some issues in tests
489  if( area_to_test->GetIsKeepout() )
490  zone_clearance = 1;
491 
492  // test for ending line inside area_to_test
493  if( area_to_test->Outline()->Contains( end ) )
494  {
495  // COPPERAREA_COPPERAREA error: corner inside copper area
496  m_currentMarker = fillMarker( aArea, static_cast<wxPoint>( end ),
498  m_currentMarker );
499  return false;
500  }
501 
502  // now test spacing between areas
503  int ax1 = start.x;
504  int ay1 = start.y;
505  int ax2 = end.x;
506  int ay2 = end.y;
507 
508  // Iterate through all edges in the polygon.
510  for( iterator = area_to_test->Outline()->IterateSegmentsWithHoles(); iterator; iterator++ )
511  {
512  SEG segment = *iterator;
513 
514  int bx1 = segment.A.x;
515  int by1 = segment.A.y;
516  int bx2 = segment.B.x;
517  int by2 = segment.B.y;
518 
519  int x, y; // variables containing the intersecting point coordinates
520  int d = GetClearanceBetweenSegments( bx1, by1, bx2, by2,
521  0,
522  ax1, ay1, ax2, ay2,
523  0,
524  zone_clearance,
525  &x, &y );
526 
527  if( d < zone_clearance )
528  {
529  // COPPERAREA_COPPERAREA error : edge intersect or too close
530  m_currentMarker = fillMarker( aArea, wxPoint( x, y ),
532  m_currentMarker );
533  return false;
534  }
535 
536  }
537  }
538 
539  return true;
540 }
Class ZONE_CONTAINER handles a list of polygons defining a copper zone.
Definition: class_zone.h:78
int GetNumCorners(void) const
Access to m_Poly parameters.
Definition: class_zone.h:480
wxString GetSelectMenuText() const override
Function GetSelectMenuText returns the text to display to be used in the selection clarification cont...
SEGMENT_ITERATOR IterateSegmentsWithHoles()
Returns an iterator object, for all outlines in the set (with holes)
MARKER_PCB * fillMarker(const TRACK *aTrack, BOARD_ITEM *aItem, int aErrorCode, MARKER_PCB *fillMe)
Function fillMarker optionally creates a marker and fills it in with information, but does not add it...
void BooleanAdd(const SHAPE_POLY_SET &b, POLYGON_MODE aFastMode)
Performs boolean polyset union For aFastMode meaning, see function booleanOp
This file is part of the common library.
bool OnAreaPolygonModified(PICKED_ITEMS_LIST *aModifiedZonesList, ZONE_CONTAINER *modified_area)
Function OnAreaPolygonModified Process an area that has been modified, by normalizing its polygon and...
virtual PCB_LAYER_ID GetLayer() const
Function GetLayer returns the primary layer this item is on.
Class BOARD to handle a board.
virtual PCB_LAYER_ID GetLayer() const override
Function GetLayer returns the primary layer this item is on.
Definition: class_zone.cpp:182
SHAPE_POLY_SET * Outline()
Definition: class_zone.h:262
#define COPPERAREA_CLOSE_TO_COPPERAREA
copper area outlines are too close
Definition: drc_stuff.h:62
void RemoveArea(PICKED_ITEMS_LIST *aDeletedList, ZONE_CONTAINER *area_to_remove)
Function RemoveArea remove copper area from net, and put it in a deleted list (if exists) ...
Struct VERTEX_INDEX.
Classes to handle copper zones.
Class SEGMENT_ITERATOR_TEMPLATE.
int GetClearanceBetweenSegments(int x1i, int y1i, int x1f, int y1f, int w1, int x2i, int y2i, int x2f, int y2f, int w2, int max_cl, int *x, int *y)
int OutlineCount() const
Returns the number of outlines in the set
bool CombineAreas(PICKED_ITEMS_LIST *aDeletedList, ZONE_CONTAINER *area_ref, ZONE_CONTAINER *area_to_combine)
Function CombineAreas If possible, combine 2 copper areas.
bool TestAreaIntersection(ZONE_CONTAINER *area_ref, ZONE_CONTAINER *area_to_test)
Function TestAreaIntersection Test for intersection of 2 copper areas area_to_test must be after area...
void SetOutline(SHAPE_POLY_SET *aOutline)
Definition: class_zone.h:265
int GetClearance(BOARD_CONNECTED_ITEM *aItem=NULL) const override
Function GetClearance returns the clearance in internal units.
Definition: class_zone.cpp:787
BOARD * m_pcb
Definition: drc_stuff.h:205
bool GetIsKeepout() const
Accessors to parameters used in Keepout zones:
Definition: class_zone.h:672
Markers used to show a drc problem on boards.
bool NormalizeAreaPolygon(PICKED_ITEMS_LIST *aNewZonesList, ZONE_CONTAINER *aCurrArea)
Function NormalizeAreaPolygon Process an area that has been modified, by normalizing its polygon agai...
ITERATOR IterateWithHoles(int aOutline)
Function IterateWithHoles.
Class SHAPE_POLY_SET.
bool GetRelativeIndices(int aGlobalIdx, VERTEX_INDEX *aRelativeIndices) const
Function GetRelativeIndices.
void Add(BOARD_ITEM *aItem, ADD_MODE aMode=ADD_INSERT) override
Adds an item to the container.
bool Intersects(const BOX2< Vec > &aRect) const
Function Intersects.
Definition: box2.h:224
void Simplify(POLYGON_MODE aFastMode)
Simplifies the polyset (merges overlapping polys, eliminates degeneracy/self-intersections) For aFast...
int GetAreaCount() const
Function GetAreaCount.
Definition: class_board.h:1011
Class PICKED_ITEMS_LIST is a holder to handle information on schematic or board items.
SHAPE_POLY_SET * GetSmoothedPoly() const
Function GetSmoothedPoly returns a pointer to the corner-smoothed version of m_Poly if it exists...
Definition: class_zone.h:620
bool doEdgeZoneDrc(ZONE_CONTAINER *aArea, int aCornerIndex)
Function doEdgeZoneDrc tests a segment in ZONE_CONTAINER * aArea: Test Edge inside other areas Test E...
int LAYER_NUM
Type LAYER_NUM can be replaced with int and removed.
int GetNetCode() const
Function GetNetCode.
MARKER_PCB * m_currentMarker
Definition: drc_stuff.h:176
Definition: seg.h:36
SEG Segment(int aIndex)
Function Segment()
bool IsOnCopperLayer() const
Function IsOnCopperLayer.
Definition: class_zone.cpp:188
ZONE_CONTAINER * GetArea(int index) const
Function GetArea returns the Area (Zone Container) at a given index.
Definition: class_board.h:982
int GetLocalFlags() const
Definition: class_zone.h:256
#define max(a, b)
Definition: auxiliary.h:86
Class SHAPE_LINE_CHAIN.
ZONE_CONTAINERS m_ZoneDescriptorList
edge zone descriptors, owned by pointer.
Definition: class_board.h:181
VECTOR2I A
Definition: seg.h:46
void Hatch()
Function Hatch computes the hatch lines depending on the hatch parameters and stores it in the zone's...
void SetLocalFlags(int aFlags)
Definition: class_zone.h:257
The common library.
bool IsCopperLayer(LAYER_NUM aLayerId)
Function IsCopperLayer tests whether a layer is a copper layer.
POLYGON & Polygon(int aIndex)
Returns the aIndex-th subpolygon in the set
bool Collide(const SEG &aSeg, int aClearance) const
Definition: seg.cpp:134
int Test_Drc_Areas_Outlines_To_Areas_Outlines(ZONE_CONTAINER *aArea_To_Examine, bool aCreate_Markers)
Function Test_Drc_Areas_Outlines_To_Areas_Outlines tests area outlines for DRC: Tests areas inside ot...
unsigned GetPriority() const
Function GetPriority.
Definition: class_zone.h:119
bool CombineAllAreasInNet(PICKED_ITEMS_LIST *aDeletedList, int aNetCode, bool aUseLocalFlags)
Function CombineAllAreasInNet Checks all copper areas in net for intersections, combining them if fou...
const BOX2I BBox(int aClearance=0) const override
Function BBox()
bool TestAreaIntersections(ZONE_CONTAINER *area_to_test)
Function TestAreaIntersections Check for intersection of a given copper area with other areas in same...
int GetZoneClearance() const
Definition: class_zone.h:218
bool Contains(const VECTOR2I &aP, int aSubpolyIndex=-1) const
Returns true if a given subpolygon contains the point aP.
#define COPPERAREA_INSIDE_COPPERAREA
copper area outlines intersect
Definition: drc_stuff.h:61
VECTOR2I B
Definition: seg.h:47