def trim(self, trimPoly): if len(self.polygons) > 0 and len(trimPoly.polygons) > 0: polytrim = pyclipper.Pyclipper() #Pyclipper polytrim.AddPaths( [[[int(self.scaling * p[0]), int(self.scaling * p[1])] for p in pat] for pat in trimPoly.polygons], poly_type=pyclipper.PT_CLIP, closed=True) polytrim.AddPaths( [[[int(self.scaling * p[0]), int(self.scaling * p[1])] for p in pat] for pat in self.polygons], poly_type=pyclipper.PT_SUBJECT, closed=True) try: trimmed = polytrim.Execute(pyclipper.CT_INTERSECTION, pyclipper.PFT_EVENODD, pyclipper.PFT_EVENODD) trimmed = pyclipper.SimplifyPolygons(trimmed) self.polygons = [[[ float(x[0]) / self.scaling, float(x[1]) / self.scaling, self.zlevel ] for x in poly] for poly in trimmed] except: print("clipping intersection error")
def clean_points(pts): """ Clean the polygon by getting rid of numerical artifacts. This is required to overcome Gmsh parsing errors. """ sc = constants.CLIPPER_SCALE spl_pts = pyclipper.SimplifyPolygons(pts) cln_pts = pyclipper.CleanPolygons(spl_pts) pts = sf(cln_pts, sc) return np.array(pts)
def offset(self, radius=0, rounding=0.0): offset = [] clip = pyclipper.PyclipperOffset() #Pyclipper polyclipper = pyclipper.Pyclipper() #Pyclipper for pat in self.polygons: outPoly = [[int(self.scaling * p[0]), int(self.scaling * p[1])] for p in pat] #Pyclipper outPoly = pyclipper.SimplifyPolygons([outPoly]) try: polyclipper.AddPaths(outPoly, poly_type=pyclipper.PT_SUBJECT, closed=True) except: None #print "path invalid", outPoly poly = polyclipper.Execute(pyclipper.CT_UNION, pyclipper.PFT_EVENODD, pyclipper.PFT_EVENODD) clip.AddPaths(poly, pyclipper.JT_ROUND, pyclipper.ET_CLOSEDPOLYGON) offset = clip.Execute(-int((radius + rounding) * self.scaling)) offset = pyclipper.SimplifyPolygons(offset) if rounding > 0.0: roundclipper = pyclipper.PyclipperOffset() roundclipper.AddPaths(offset, pyclipper.JT_ROUND, pyclipper.ET_CLOSEDPOLYGON) offset = roundclipper.Execute(int(rounding * self.scaling)) offset = pyclipper.CleanPolygons(offset, distance=self.scaling * self.precision) #print (len(offset)) result = PolygonGroup(scaling=self.scaling, precision=self.precision, zlevel=self.zlevel) result.polygons = [] for poly in offset: if len(poly) > 0: output_poly = [[ float(x[0]) / self.scaling, float(x[1]) / self.scaling, self.zlevel ] for x in poly] result.addPolygon(output_poly) return result
def convertToClipper(pathArray, SCALE_FACTOR): clipperPaths = [] for path in pathArray: points = [] for edge in path: for pts in edge: #points points.append([pts.x, pts.y]) if len(points) > 0: scaled = pyclipper.scale_to_clipper(points, SCALE_FACTOR) clipperPaths.append(scaled) return pyclipper.SimplifyPolygons(clipperPaths)
def bool_operation(subj, clip=None, method=None, closed=True): """ Angusj clipping library """ from spira.gdsii.elemental.polygons import PolygonAbstract scale = 1 pc = pyclipper.Pyclipper() if issubclass(type(subj), PolygonAbstract): subj = subj.polygons if issubclass(type(clip), PolygonAbstract): clip = clip.polygons if clip is not None: pc.AddPaths(st(clip, scale), pyclipper.PT_CLIP, True) pc.AddPaths(st(subj, scale), pyclipper.PT_SUBJECT, closed) subj = None if method == 'difference': subj = pc.Execute(pyclipper.CT_DIFFERENCE, pyclipper.PFT_NONZERO, pyclipper.PFT_NONZERO) elif method == 'union': subj = pc.Execute(pyclipper.CT_UNION, pyclipper.PFT_NONZERO, pyclipper.PFT_NONZERO) elif method == 'intersection': subj = pc.Execute(pyclipper.CT_INTERSECTION, pyclipper.PFT_NONZERO, pyclipper.PFT_NONZERO) elif method == 'exclusive': subj = pc.Execute(pyclipper.CT_XOR, pyclipper.PFT_NONZERO, pyclipper.PFT_NONZERO) else: raise ValueError('Please specify a clipping method') sp = pyclipper.SimplifyPolygons(subj) # cp = pyclipper.CleanPolygons(sf(sp, scale)) return sp
def test_simplify_polygons(self): solution = pyclipper.SimplifyPolygons([PATH_SUBJ_1]) solution_single = pyclipper.SimplifyPolygon(PATH_SUBJ_1) self.assertEqual(len(solution), 1) self.assertEqual(len(solution), len(solution_single)) _do_solutions_match(solution, solution_single)
def getPolysAtSlice(self, objectToCut, plane, depth): fc = FreeCAD.ActiveDocument obj = fc.getObjectsByLabel(objectToCut)[0] shape = obj.Shape if hasattr(self.obj, "MaximumError") == False: error = .1 else: error = self.obj.MaximumError.Value FreeCADGui.ActiveDocument.getObject(obj.Name).Deviation = error / 3. wires = list() for i in shape.slice(Base.Vector(0, 0, 1), self.zOff - depth): wires.append(i) polys = [] for wire in wires: segList = [] for edge in wire.OrderedEdges: segList.append(edge.discretize(QuasiDeflection=error / 3.)) newWire = segList.pop() while len(segList) > 0: for seg in segList: if self.dtp(seg[0], newWire[len(newWire) - 1]) < 0.00001: newWire = newWire + seg[1:] segList.remove(seg) break elif self.dtp(seg[len(seg) - 1], newWire[len(newWire) - 1]) < 0.00001: temp = [] i = len(seg) - 2 while i >= 0: temp.append(seg[i]) i = i - 1 newWire = newWire + temp segList.remove(seg) break poly = [] for v in newWire: poly.append([v[0], v[1]]) polys.append(poly) if hasattr(self, "lastSlice"): if self.lastSlice != None: scaledLastSlice = pyclipper.SimplifyPolygons( self.scaleToClipper(self.lastSlice, 100000.)) scaledPolys = pyclipper.SimplifyPolygons( self.scaleToClipper(simplePolys, 100000.)) pc = pyclipper.Pyclipper() pc.AddPath(scaledPolys, pyclipper.PT_CLIP, True) pc.AddPaths([scaledLastSlice], pyclipper.PT_SUBJECT, True) solution = pc.Execute(pyclipper.CT_UNION, pyclipper.PFT_EVENODD, pyclipper.PFT_EVENODD) simplePolys = self.scaleFromClipper( pyclipper.SimplifyPolygons(solutions, 1 / 100000.)) else: simplePolys = self.scaleFromClipper( pyclipper.SimplifyPolygons( self.scaleToClipper(polys, 100000.)), 1 / 100000.) return simplePolys else: return self.scaleFromClipper( pyclipper.SimplifyPolygons(self.scaleToClipper(polys, 100000.)), 1 / 100000.)
def offsetPathOutIn(self, recursive=True): max_iterations = self.sliceIter.getValue() scaling = 1000.0 output = [] #define bounding box with tool radius and offset added radius = self.tool.getValue( ).diameter.value / 2.0 + self.radialOffset.value bound_min = [ self.stockMinX.getValue() - radius, self.stockMinY.getValue() - radius ] bound_max = [ self.stockMinX.getValue() + self.stockSizeX.getValue() + radius, self.stockMinY.getValue() + self.stockSizeY.getValue() + radius ] # sort patterns by slice levels patternLevels = dict() for p in self.patterns: patternLevels[p[0][2]] = [] for p in self.patterns: patternLevels[p[0][2]].append(p) for sliceLevel in sorted(patternLevels.keys(), reverse=True): #define bounding box bb = [[bound_min[0], bound_min[1], sliceLevel], [bound_min[0], bound_max[1], sliceLevel], [bound_max[0], bound_max[1], sliceLevel], [bound_max[0], bound_min[1], sliceLevel]] bbpoly = [[int(scaling * p[0]), int(scaling * p[1])] for p in bb] #Pyclipper print("Slice Level: ", sliceLevel) radius = self.tool.getValue( ).diameter.value / 2.0 + self.radialOffset.value iterations = max_iterations if iterations <= 0: iterations = 1 input = patternLevels[sliceLevel] offsetOutput = [] while len(input) > 0 and (iterations > 0): offset = [] clip = pyclipper.PyclipperOffset() #Pyclipper polyclipper = pyclipper.Pyclipper() #Pyclipper for pat in input: outPoly = [[int(scaling * p[0]), int(scaling * p[1])] for p in pat] #Pyclipper outPoly = pyclipper.SimplifyPolygons([outPoly]) try: polyclipper.AddPaths(outPoly, poly_type=pyclipper.PT_SUBJECT, closed=True) except: None #print "path invalid", outPoly poly = polyclipper.Execute(pyclipper.CT_UNION, pyclipper.PFT_EVENODD, pyclipper.PFT_EVENODD) clip.AddPaths(poly, pyclipper.JT_ROUND, pyclipper.ET_CLOSEDPOLYGON) rounding = self.pathRounding.getValue() offset = clip.Execute(int((radius + rounding) * scaling)) offset = pyclipper.SimplifyPolygons(offset) if rounding > 0.0: roundclipper = pyclipper.PyclipperOffset() roundclipper.AddPaths(offset, pyclipper.JT_ROUND, pyclipper.ET_CLOSEDPOLYGON) offset = roundclipper.Execute(int(-rounding * scaling)) offset = pyclipper.CleanPolygons(offset, distance=scaling * self.precision.getValue()) # trim to outline polytrim = pyclipper.Pyclipper() #Pyclipper polytrim.AddPath(bbpoly, poly_type=pyclipper.PT_CLIP, closed=True) polytrim.AddPaths(offset, poly_type=pyclipper.PT_SUBJECT, closed=True) try: offset = polytrim.Execute(pyclipper.CT_INTERSECTION, pyclipper.PFT_EVENODD, pyclipper.PFT_EVENODD) except: print("clipping intersection error") #print (len(offset)) input = [] for poly in offset: offsetOutput.append( [[x[0] / scaling, x[1] / scaling, sliceLevel] for x in reversed(poly)]) if recursive: input.append( [[x[0] / scaling, x[1] / scaling, sliceLevel] for x in poly]) radius = self.sideStep.value iterations -= 1 #self.patterns = input #offsetOutput.reverse() lastpoint = None for p in offsetOutput: closest_point_index = 0 path_closed = True remainder_path = [ ] # append the start of a boundary path to the first boundary point to the end on_boundary = False opt_path = [] for i in range(0, len(p)): #check if point lies on boundary bdist, bpoint, bindex = closest_point_on_polygon(p[i], bb) if bdist < 0.001 and False: # (TURNED OFF, buggy) point lies on boundary; skip this point # if this is the first boundary point of the path, append the start to the end if path_closed: remainder_path = opt_path remainder_path.append(p[i]) opt_path = [] path_closed = False else: if not on_boundary: # if it is the first point on boundary, add it #flush path up to here to output opt_path.append(p[i]) if len(opt_path) > 0: output.append(opt_path) lastpoint = opt_path[-1] opt_path = [] on_boundary = True else: if on_boundary and i > 0: opt_path.append(p[i - 1]) on_boundary = False opt_path.append(p[i]) opt_path += remainder_path if lastpoint is not None and path_closed: for i in range(0, len(p)): # find closest point from last position if dist(lastpoint, p[i]) < dist( lastpoint, opt_path[closest_point_index]): closest_point_index = i opt_path = opt_path[ closest_point_index:] + opt_path[:closest_point_index] # last point same as first point on closed paths (explicitly add it here) opt_path.append(opt_path[0]) if len(opt_path) > 0: lastpoint = opt_path[-1] output.append(opt_path) return output
def LoadGerberFile(self, evt): dlg = wx.FileDialog(self, "Open Gerber file", "", "", "Gerber Files (*.gbl;*.gtl;*.gbr;*.cmp)|*.gbl;*.gtl;*.gbr;*.cmp|All Files (*.*)|*", wx.FD_OPEN | wx.FD_FILE_MUST_EXIST) if dlg.ShowModal() == wx.ID_CANCEL: self.SetStatusText("Load Gerber file cancelled by user", 0) return filename = dlg.GetPath() dlg.Destroy() self.SetStatusText("Loading Gerber: " + filename + "...", 0) [data, tracks] = load_file(filename) self.data = data xmin = 1E99 xmax = -1E99 ymin = 1E99 ymax = -1E99 sum1 = 0 sumReg = 0 sumBound = 0 sumTracks = 0 sumPads = 0 cbounds = pyclipper.Pyclipper() boundarys = [] pcb_edges = [] layers = list(data.layers) for gl in layers: if gl.type == GerberLayer.TYPE_PCBEDGE: data.layers.remove(gl) pcb_edges.extend(gl.points) for segment in gl.points: sum1 += len(segment) for vertex in segment: x = vertex[0] y = vertex[1] if x < xmin: xmin = x if x > xmax: xmax = x if y < ymin: ymin = y if y > ymax: ymax = y continue if gl.type == GerberLayer.TYPE_REGION: sumReg += len(gl.points) # regions.extend(gl.points) continue if gl.type == GerberLayer.TYPE_TRACK: sumTracks += len(gl.points) continue if gl.type == GerberLayer.TYPE_PAD: sumPads += len(gl.points) continue if gl.type == GerberLayer.TYPE_BOUNDARY: # if gl.isDark: # # boundarys.extend(gl.points) # # if len(boundarys) == 0: # boundarys.extend(gl.points) # # else: # # cbounds.AddPaths(boundarys, pyclipper.PT_SUBJECT) # # cbounds.AddPaths(gl.points, pyclipper.PT_SUBJECT) # # boundarys = cbounds.Execute(pyclipper.CT_UNION, pyclipper.PFT_EVENODD, pyclipper.PFT_EVENODD) # # cbounds.Clear() # else: # cbounds.AddPaths(boundarys, pyclipper.PT_SUBJECT) # cbounds.AddPaths(gl.points, pyclipper.PT_CLIP) # boundarys = cbounds.Execute(pyclipper.CT_DIFFERENCE, pyclipper.PFT_EVENODD, pyclipper.PFT_EVENODD) # cbounds.Clear() if gl.isDark: boundarys.extend(gl.points) else: cbounds.AddPaths(boundarys, pyclipper.PT_SUBJECT) cbounds.AddPaths(gl.points, pyclipper.PT_CLIP) boundarys = cbounds.Execute(pyclipper.CT_DIFFERENCE, pyclipper.PFT_NONZERO, pyclipper.PFT_NONZERO) cbounds.Clear() sumBound += len(gl.points) for segment in gl.points: sum1 += len(segment) for vertex in segment: x = vertex[0] y = vertex[1] if x < xmin: xmin = x if x > xmax: xmax = x if y < ymin: ymin = y if y > ymax: ymax = y continue if gl.type == GerberLayer.TYPE_MERGEDCOPPER: data.layers.remove(gl) continue print " fraction = ",self.data.fraction print " found", sumBound, "polygons,", sum1, "vertices" print " found", sumReg, "pours" print " found", sumTracks, "tracks" print " found", sumPads, "pads" print " found", len(pcb_edges), "edge segments" print " xmin: %0.3g " % xmin, "xmax: %0.3g " % xmax, "dx: %0.3g " % (xmax - xmin) print " ymin: %0.3g " % ymin, "ymax: %0.3g " % ymax, "dy: %0.3g " % (ymax - ymin) data.xmin2 = xmin data.xmax2 = xmax data.ymin2 = ymin data.ymax2 = ymax if len(pcb_edges) == 0: outer_offset = (1 if data.units == 0 else 0.03937) * 10**data.fraction # 1 mm # outer_offset = 0.01 * 10**data.fraction xmin -= outer_offset ymin -= outer_offset xmax += outer_offset ymax += outer_offset pcb_edge = [[xmax, ymax], [xmax, ymin], [xmin, ymin], [xmin, ymax], [xmax, ymax]] pcb_edges.append(pcb_edge) self.pcb_edges = pcb_edges self.boundarys = boundarys = pyclipper.SimplifyPolygons(boundarys, pyclipper.PFT_NONZERO) # boundarys = GerberReader3.replace_holes_with_seams(boundarys) GerberReader3.closeOffPolys(boundarys) data.layers.append(GerberLayer(True, "PCB Edge", pcb_edges, True, False, "blue", GerberLayer.TYPE_PCBEDGE)) data.layers.append(GerberLayer(True, "Merged Copper", boundarys, False, color="brown", type=GerberLayer.TYPE_MERGEDCOPPER)) # PCB bounds data.xmin = xmin data.xmax = xmax data.ymin = ymin data.ymax = ymax # View bounds # Includes the origin if xmin > 0: xmin = 0 if xmax < 0: xmax = 0 if ymin > 0: ymin = 0 if ymax < 0: ymax = 0 # Add margin ww = (xmax - xmin)*0.1 hh = (ymax - ymin)*0.1 xmin -= ww xmax += ww ymin -= hh ymax += hh self.contours = [] self.layersPanel.loadLayersPanel(data, self.NotifyDataChange) self.canvas.loadData2(self.data, xmin, xmax, ymin, ymax) self.SetStatusText("Load Gerber file completed successfully", 0) self.origincombo.SetSelection(0) self.nativeLabel.SetLabelText("(File unit: %s, Dec. places: %0d)" % ("mm" if data.units == 0 else "in", data.fraction))