@@ -559,6 +559,7 @@ def save(self, reconstructArcs: bool=False, refillAllZones: bool=False,
559559 for e in boardsEdges :
560560 e .SetWidth (edgeWidth )
561561
562+ self ._validateVCuts ()
562563 vcuts = self ._renderVCutH () + self ._renderVCutV ()
563564 keepouts = []
564565 for cut , clearanceArea in vcuts :
@@ -1207,6 +1208,32 @@ def _setVCutLabelStyle(self, label, origin, position):
12071208 label .SetTextSize (toKiCADPoint ((self .vCutSettings .textSize , self .vCutSettings .textSize )))
12081209 label .SetHorizJustify (EDA_TEXT_HJUSTIFY_T .GR_TEXT_HJUSTIFY_LEFT )
12091210
1211+ def _validateVCuts (self , tolerance = fromMm (1 )):
1212+ """
1213+ Validates V-cuts for cuttitng the PCBs. Renders the violations into the
1214+ PCB as a side effect.
1215+ """
1216+ if len (self .hVCuts ) == 0 and len (self .vVCuts ) == 0 :
1217+ return
1218+
1219+ collisionPolygons = shapely .ops .unary_union ([x .substrates .buffer (- tolerance ) for x in self .substrates ])
1220+ minx , miny , maxx , maxy = self .panelBBox ()
1221+
1222+ lines = \
1223+ [LineString ([(minx , y ), (maxx , y )]) for y in self .hVCuts ] + \
1224+ [LineString ([(x , miny ), (x , maxy )]) for x in self .vVCuts ]
1225+
1226+ error_message = "V-Cut cuts the original PCBs. You should:\n "
1227+ error_message += "- either reconsider your tab placement,\n "
1228+ error_message += "- or use different cut type – e.g., mouse bites."
1229+ for line in lines :
1230+ for geom in listGeometries (collisionPolygons .intersection (line )):
1231+ if geom .is_empty :
1232+ continue
1233+ annotationPos = sorted (geom .coords , key = lambda p : - p [1 ])[0 ]
1234+ self ._renderLines ([geom ], Layer .Margin )
1235+ self .reportError (toKiCADPoint (annotationPos ), error_message )
1236+
12101237 def _renderVCutV (self ):
12111238 """ return list of PCB_SHAPE V-Cuts """
12121239 bBox = self .boardSubstrate .boundingBox ()
0 commit comments