def arc(self, end, center, layer=None): if self.enable: r = xyDist(end, center) if self.svg is not None: self.path.push_arc(self.scaleOffset(end), 0, r, \ large_arc=True, angle_dir='+', \ absolute=True) if self.d is not None: if layer is None: layer = self.lPath else: if not layer in self.definedLayers: self.definedLayers[layer] = True self.d.add_layer(layer, color=self.color, lineweight=0) p0 = self.last p1 = end if xyDist(p0, p1) < MIN_DIST: self.d.add(dxf.circle(r, center, layer=layer)) else: # dprt("p0 (%7.4f, %7.4f) p1 (%7.4f, %7.4f)" % \ # (p0[0], p0[1], p1[0], p1[1])) # if orientation(p0, center, p1) == CCW: # (p0, p1) = (p1, p0) a0 = degrees(calcAngle(center, p0)) a1 = degrees(calcAngle(center, p1)) if a1 == 0.0: a1 = 360.0 # dprt("a0 %5.1f a1 %5.1f" % (a0, a1)) self.d.add(dxf.arc(r, center, a0, a1, layer=layer)) self.last = end
def trim(self, path): dprt("trim start %d trimX %7.4f trimy %7.4f xPlus %5s yPlus %5s" % \ (self.layerNum, self.trimX, self.trimY, self.xPlus, self.yPlus)) if self.layerNum == 3: dprt("break") rtnPath = [] for l in path: if l.index == INDEX_MARKER: continue if l.type == ARC: if xyDist((self.trimX, self.trimY), l.c) < l.r: continue # elif l.type == LINE: # rtnPath.append(l) # continue dprt() l.prt() dprt("horz trim") l1 = l.horizontalTrim(self.trimY, self.yPlus) if l1 != None: l1.prt() dprt("vert trim") l1 = l.verticalTrim(self.trimX, self.xPlus) if l1 != None: l1.prt() rtnPath.append(l1) else: dprt("vert returned None") else: dprt("horz returned None") dprt("\ntrim done") return (rtnPath)
def pointsArc(self, p0, p1, p2, dbg=False): if dbg: dprt("p0 (%7.4f %7.4f) p1 (%7.4f %7.4f) "\ "p2 (%7.4f %7.4f) %7.4f %7.4f" % \ (p0[0], p0[1], p1[0], p1[1], p2[0], p2[1], \ xyDist(p0, p1), xyDist(p1, p2))) (eqType0, m0, b0) = self.lineMid90(p0, p1, dbg) (eqType1, m1, b1) = self.lineMid90(p1, p2, dbg) if dbg: dprt("eqType0 %5s m0 %7.4f b0 %7.4f "\ "eqType1 %5s m1 %7.4f b1 %7.4f" % \ (eqType0, m0, b0, eqType1, m1, b1)) if eqType0: if eqType1: # y = m0*x + b0 # y = m1*x + b1 # m0*x + b0 = m1*x + b1 # x*(m0 - m1) = b1 - b0 x = (b1 - b0) / (m0 - m1) y = m0 * x + b0 else: # y = m0*x + b0 # x = m1*y + b1 # y = m0 * (m1*y + b1) + b0 # y = m0*m1*y + m0*b1 + b0 # y - m0*m1*y = m0*b1 + b0 # y * (1 - m0*m1) = m0*b1 + b0 # y = (m0*b1 + b0) / (1 - m0*m1) y = (m0*b1 + b0) / (1 - m0*m1) x = m1 * y + b1 else: if eqType1: x = (m0*b1 + b0) / (1 - m0*m1) y = m1 * x + b1 else: y = (b1 - b0) / (m0 - m1) x = m0 * y + b0 c = (x, y) r0 = xyDist(c, p0) if dbg: r1 = xyDist(c, p1) r2 = xyDist(c, p2) dprt("c (%7.4f %7.4f) r0 %7.4f r1 %7.4f r2 %7.4f" % \ (c[0], c[1], r0, r1, r2)) return((c, r0))
def closest(self, path): p0 = self.last minD = MAX_VALUE for (i, l) in enumerate(path): dist = xyDist(p0, l.p0) if dist < minD: minD = dist index = i path = path[index:] + path[:index] self.last = path[0].p0 return(path)
def pocket(self, args, dbg=None): if dbg is None: dbg = self.dbg layer = args[1] cfg = self.cfg # dir = CCW # if cfg.dir is not None and cfg.dir == 'CW': # dir = CW stepOver = cfg.endMillSize * self.stepOver cfg.ncInit() segments = cfg.dxfInput.getPath(layer) pco = PyclipperOffset() mp = cfg.getMillPath() self.last = cfg.mill.last for seg in segments: self.pDir = pathDir(seg) pco.Clear() mainPath = [] for (i, l) in enumerate(seg): l.draw() if dbg: l.prt() if l.type == LINE: mainPath.append(intScale(l.p0)) elif l.type == ARC: self.arcLines(mainPath, l, dbg=dbg) if dbg: dprt() for (i, p) in enumerate(mainPath): (x, y) = floatScale(p) dprt("%3d (%7.4f %7.4f)" % (i, x, y)) dprt() pco.AddPath(mainPath, pyclipper.JT_ROUND, \ pyclipper.ET_CLOSEDPOLYGON) offset = cfg.endMillSize / 2.0 + cfg.finishAllowance step = 0 offsetPaths = [] while True: result = pco.Execute(-int(offset * SCALE)) if dbg: dprt("%2d offset %7.4f results %d" % \ (step, offset, len(result))) if len(result) == 0: break rData = [] for (rNum, r) in enumerate(result): # convert from list of points to lines if self.arcs: pLast = floatScale(r[-1]) index = 0 maxDist = -1 for (i, p) in enumerate(r): p = floatScale(p) dist = xyDist(p, pLast) if dbg: dprt("%3d (%7.4f %7.4f) dist %9.6f" % \ (i, p[0], p[1], dist)) if dist > maxDist: maxDist = dist index = i r[i] = p pLast = p r = r[index:] + r[:index] if dbg: dprt("index %d maxDist %7.4f" % (index, maxDist)) pLast = r[-1] dprt("pLast (%7.4f %7.4f)" % (pLast[0], pLast[1])) for (i, p) in enumerate(r): dprt("%3d (%7.4f %7.4f) dist %9.6f" % \ (i, p[0], p[1], xyDist(p, pLast))) pLast = p dprt() path = self.makePath(r, step, rNum, dbg) else: pLast = floatScale(r[-1]) path = [] for (i, p) in enumerate(r): p = floatScale(p) if dbg: dprt("%3d (%7.4f %7.4f)" % (i, p[0], p[1])) l = Line(pLast, p, i) txt = "s %d r %d i %d" % (step, rNum, i) l.label(txt) path.append(l) pLast = p result[rNum] = path if dbg: dprt() for l in path: l.prt() dprt() # find shortest distance to each path oData = [] for (oNum, oPath) in enumerate(offsetPaths): lEnd = oPath[-1] pEnd = lEnd.p1 minDist = MAX_VALUE index = None for (i, l) in enumerate(path): dist = xyDist(pEnd, l.p0) if dist < minDist: minDist = dist index = i oData.append((index, minDist)) rData.append(oData) # connect to nearest path if dbg and len(result) > 1: dprt("multiple results") oConnect = [False] * len(offsetPaths) while True: minDist = MAX_VALUE index = None rPath = None for (rNum, oData) in enumerate(rData): if oData == None: continue rPath = rNum for (oNum, (i, dist)) in enumerate(oData): if dbg: dprt("step %d rNum %d Onum %d index %3d "\ "dist %9.6f" % \ (step, rNum, oNum, i, dist)) if not oConnect[oNum] and dist < minDist: minDist = dist index = i rIndex = rNum oIndex = oNum if rPath is None: break if index is not None: # connect path if dbg: dprt("connect rIndex %d index %3d to "\ "oIndex %d dist %9.6f" % \ (rIndex, index, oIndex, minDist)) path = result[rIndex] rData[rIndex] = None oConnect[oIndex] = True oPath = offsetPaths[oIndex] oPath.append(Line(oPath[-1].p1, path[index].p0)) oPath += path[index:] + path[:index] else: # add new path if dbg: dprt("add rPath %d oNum %d" % \ (rPath, len(offsetPaths))) rData[rPath] = None path = result[rPath] path = self.closest(path) offsetPaths.append(path) offset += stepOver step += 1 if step > 99: break for path in offsetPaths: mp.millPath(path, closed=False, minDist=False)
def makePath(self, points, step, rNum, dbg=False): if False: r = 1 for i in range(4): a0 = float(i) * pi / 2 + pi / 4 a = a0 p0 = (r * cos(a), r * sin(a)) for j in range(2): tmp = (pi, -pi)[j] a = a0 + tmp / 2 p1 = (r * cos(a), r * sin(a)) a = a0 + tmp / 1 p2 = (r * cos(a), r * sin(a)) self.pointsArc(p0, p1, p2) dprt() sys.exit() numPoints = len(points) pLast = points[-1] i = 0 path = [] # points.append(points[0]) o0 = orientation(pLast, points[0], points[1]) if dbg: dprt("path orientation %s" % oStr(o0)) while i < numPoints: p0 = points[i] if dbg: dprt("i %3d (%7.4f %7.4f)" % (i, p0[0], p0[1])) d0 = xyDist(p0, pLast) j = i + 1 pa = p0 while j < numPoints - 1: pj = points[j] dist = xyDist(pa, pj) if abs(dist - d0) > 0.001: if dbg: dprt("dist %9.6f d0 %9.6f" % (dist, d0)) break j += 1 pa = pj delta = j - i if delta < 4: l = Line(pLast, p0, i) txt = "s %d r %d i %d" % (step, rNum, i) l.label(txt) i += 1 pLast = p0 else: p1 = points[i + delta / 2] (c, r) = self.pointsArc(pLast, p1, pa) o = orientation(pLast, p1, pa) if o != o0: (pLast, pa) = (pa, pLast) a0 = degAtan2(pLast[1] - c[1], pLast[0] - c[0]) a1 = degAtan2(pa[1] - c[1], pa[0] - c[0]) l = Arc(c, r, a0, a1, direction=o) if dbg: dprt("arc %s %2d i %d (%7.4f %7.4f) %8.3f "\ " %d (%7.4f %7.4f) %8.3f" % \ (oStr(o), delta, i, pLast[0], pLast[1], a0, \ j, pa[0], pa[1], a1)) i = j pLast = pa if dbg: l.prt() path.append(l) return(path)
def enlargeHole(self, args): outerLayer = args[1] try: innerD = self.cfg.evalFloatArg(args[2]) innerLayer = None except ValueError: innerLayer = args[2] cfg = self.cfg direction = CCW if cfg.dir is not None and cfg.dir == 'CW': direction = CW cfg.ncInit() # endAngle = self.endAngle # swapped = False outer = cfg.dxfInput.getCircles(outerLayer) if innerLayer is not None: inner = cfg.dxfInput.getCircles(innerLayer) for (c, outerD) in outer: if innerLayer is not None: found = False for (c0, innerD) in inner: if xyDist(c, c0) < MIN_DIST: found = True break if not found: continue draw = cfg.draw if draw is not None: draw.circle(c, innerD / 2.0) draw.circle(c, outerD / 2.0) outerD -= 2 * cfg.finishAllowance totalDist = (outerD - innerD) / 2 passes = int(round(totalDist / self.distPass)) distPass = totalDist / passes endMillRadius = cfg.endMillSize / 2.0 (xC, yC) = c self.path = [] self.index = 0 r0 = innerD / 2.0 - endMillRadius if self.leadRadius != 0: leadCenter = (r0 - self.leadRadius + xC, yC) if direction == CCW: self.addSeg( Arc(leadCenter, self.leadRadius, 270, 0, direction=CCW)) else: self.addSeg( Arc(leadCenter, self.leadRadius, 0, 90, direction=CW)) for _ in range(passes): r1 = r0 + distPass y1 = sqrt(r1 * r1 - r0 * r0) p0 = (xC + r0, yC) if direction == CCW: p1 = (xC + r0, yC + y1) else: p1 = (xC + r0, yC - y1) self.addSeg(Line(p0, p1)) a0 = degrees(atan2(y1, r0)) if direction == CCW: self.addSeg(Arc(c, r1, a0, 0, direction=CCW)) else: self.addSeg(Arc(c, r1, 0, a0, direction=CW)) r0 = r1 self.addSeg(Arc(c, r1, 0, a0, direction=direction)) if self.leadRadius != 0: r2 = r1 - self.leadRadius a0R = radians(a0) leadCenter = (r2 * cos(a0R) + xC, r2 * sin(a0R) + yC) if direction == CCW: self.addSeg( Arc(leadCenter, self.leadRadius, a0, a0 + 90, direction=CCW)) else: self.addSeg( Arc(leadCenter, self.leadRadius, a0 - 90, a0, direction=CW)) self.millPath()
def rectSlot(self, args): layer = args[1] cfg = self.cfg cfg.ncInit() segments = cfg.dxfInput.getPath(layer) millSeg = [] for seg in segments: if len(seg) != 4: ePrint("rectPocket wrong number of sides") continue vert = [] horiz = [] arc = [] for l in seg: if l.type == LINE: if abs(l.p0[0] - l.p1[0]) < MIN_DIST: vert.append(l) elif abs(l.p0[1] - l.p1[1]) < MIN_DIST: horiz.append(l) elif l.type == ARC: arc.append(l) if len(arc) == 2: if abs(arc[0].r - cfg.endMillSize / 2.0) < MIN_DIST: path = [] path.append(Line(arc[0].c, arc[1].c)) millSeg.append(path) else: a0 = arc[0] a1 = arc[1] if len(vert) == 2: x = a0.p0.x if a0.c.y > a1.c.y: (a0, a1) = (a1, a0) y = a0.c.y + (a1.c.y - a0.c.y) / 2 w = abs(a0.p0.x - a0.p1.x) h = abs(a0.c.y - a1.c.y) self.pocket(x, y, w, h) elif len(horiz) == 2: if a0.c.x > a1.c.y: (a0, a1) = (a1, a0) x = a0.c.x + (a1.c.x - a0.c.x) / 2 y = a0.p0.y w = abs(a0.c.x - a1.c.x) h = abs(a0.p0.y - a1.p1.y) self.pocket(x, y, w, h) last = cfg.mill.last mp = cfg.getMillPath() while len(millSeg): minDist = MAX_VALUE index = None for i, path in enumerate(millSeg): dist0 = xyDist(last, path[0].p0) dist1 = xyDist(last, path[-1].p1) if dist0 < dist1: dist = dist0 swap = False else: dist = dist1 swap = True if dist < minDist: minDist = dist minSwap = swap index = i path = millSeg.pop(index) if swap: path = reverseSeg(path, False) mp.millPath(path, closed=False)