def material(self, xSize, ySize): if self.svg is not None: self.offset = 0.0 path = self.materialPath if path is None: self.materialPath = Path(stroke_width=.5, stroke='red', \ fill='none') path = self.materialPath path.push('M', (self.scaleOffset((0, 0)))) path.push('L', (self.scaleOffset((xSize, 0)))) path.push('L', (self.scaleOffset((xSize, ySize)))) path.push('L', (self.scaleOffset((0, ySize)))) path.push('L', (self.scaleOffset((0, 0)))) self.path.push('M', (self.scaleOffset((0, 0)))) # dwg = svgwrite.Drawing(name, (svg_size_width, svg_size_height), \ # debug=True) cfg = self.cfg if self.d is not None: orientation = cfg.orientation if orientation == O_UPPER_LEFT: p0 = (0.0, 0.0) p1 = (xSize, 0.0) p2 = (xSize, -ySize) p3 = (0.0, -ySize) elif orientation == O_LOWER_LEFT: p0 = (0.0, 0.0) p1 = (xSize, 0.0) p2 = (xSize, ySize) p3 = (0.0, ySize) elif orientation == O_UPPER_RIGHT: p0 = (0.0, 0.0) p1 = (-xSize, 0.0) p2 = (-xSize, -ySize) p3 = (0.0, -ySize) elif orientation == O_LOWER_RIGHT: p0 = (0.0, 0.0) p1 = (-xSize, 0.0) p2 = (-xSize, ySize) p3 = (0.0, ySize) elif orientation == O_CENTER: p0 = (-xSize / 2, -ySize / 2) p1 = (xSize / 2, -ySize / 2) p2 = (xSize / 2, ySize / 2) p3 = (-xSize / 2, ySize / 2) elif orientation == O_POINT: dxfInput = cfg.dxfInput p0 = (dxfInput.xMin, dxfInput.yMin) p1 = (dxfInput.xMin, dxfInput.yMax) p2 = (dxfInput.xMax, dxfInput.yMax) p3 = (dxfInput.xMax, dxfInput.yMin) else: ePrint("invalid orientation") self.d.add(dxf.line(p0, p1, layer=self.lBorder)) self.d.add(dxf.line(p1, p2, layer=self.lBorder)) self.d.add(dxf.line(p2, p3, layer=self.lBorder)) self.d.add(dxf.line(p3, p0, layer=self.lBorder))
def setQuadrant(self, args): val = args[1].lower() self.quadrant = None for (x, i) in self.quadrantValues: if val == x: self.quadrant = i break if self.quadrant is None: ePrint("invalid quadrant %s" % val) raise ValueError("invalid quadrant" % val)
def readFont(self, fontFile): inp = open(fontFile, 'rb') c = ord(' ') while True: minVal = 99 maxVal = -99 val = inp.read(5) if len(val) == 0: break index = int(val.decode()) val = inp.read(3) length = int(val.decode()) l = ord(inp.read(1)) - ord('R') r = ord(inp.read(1)) - ord('R') chArray = [] move = True for i in range(0, length - 1): while True: x = inp.read(1) if ord(x) >= ord(' '): break y = inp.read(1) if ord(x) == ord(' ') and ord(y) == ord('R'): move = True else: x = ord(x) - ord('R') y = ord(y) - ord('R') if y > maxVal: maxVal = y if y < minVal: minVal = y chArray.append([x + abs(l), y, move]) move = False self.letter.append(Letter(r, l, chArray)) val = inp.read(1) if ord(val) != 10: ePrint("error") if self.dbg: dprt("%3d '%1c' length %2d index %4d len %2d l %3d r %3d "\ "max %3d min %3d" % \ (c, c, length, index, len(chArray), l, r, maxVal, minVal)) j = 0 for i, (x, y, move) in enumerate(chArray): dprt("(%2d %3d %3d %5s)" % (i, x, y, move), end=" ") j += 1 if j & 3 == 0: dprt() if j & 3 != 0: dprt() c += 1 if maxVal > self.max: self.max = maxVal if minVal < self.min: self.min = minVal
def setup(self): (xGrid, yGrid) = self.grid (xOffset, yOffset) = self.offset (xSpace, ySpace) = self.spacing self.xSize = 2 * xOffset + (xGrid - 1) * xSpace self.ySize = 2 * yOffset + (yGrid - 1) * ySpace orientation = self.cfg.orientation if orientation == O_UPPER_LEFT: if self.top: y = -yOffset ySpace = -ySpace else: y = -self.ySize + yOffset elif orientation == O_LOWER_LEFT: if self.top: y = self.ySize - yOffset ySpace = -ySpace else: y = yOffset else: ePrint("drillHolder invalid orientation") index = 0 self.holeInfo = [] for _ in range(yGrid): x = xOffset for _ in range(xGrid): (size, label) = self.holes[index] self.holeInfo.append((x, y, size, label)) x += xSpace index += 1 y += ySpace (xMountOffset, xMountGrid) = self.xMount (yMountOffset, yMountGrid) = self.yMount xMountSpace = (self.xSize - 2 * xMountOffset) / (xMountGrid - 1) yMountSpace = (self.ySize - 2 * yMountOffset) / (yMountGrid - 1) self.mountInfo = [] y = yMountOffset for _ in range(yMountGrid): x = xMountOffset for _ in range(xMountGrid): self.mountInfo.append((x, y)) x += xMountSpace y += yMountSpace
def open(self, inFile, drawDxf=True, drawSvg=True): if drawSvg and self.svg is None: svgFile = inFile + ".svg" try: self.svg = Drawing(svgFile, profile='full', fill='black') self.path = Path(stroke_width=.5, stroke='black', fill='none') except IOError: self.svg = None self.path = None ePrint("svg file open error %s" % (svgFile)) if drawDxf and self.d is None: dxfFile = inFile + "_ngc.dxf" try: self.d = dxf.drawing(dxfFile) self.layerIndex = 0 self.d.add_layer('0', color=self.color, lineweight=0) self.setupLayers() except IOError: self.d = None ePrint("dxf file open error %s" % (dxfFile))
def labelHoles(self, _): cfg = self.cfg if cfg.probe: cfg.probeInit() prb = cfg.prb (x, y) = self.holeInfo[0][0:2] prb.write("g0 x %7.4f y %7.4f\n" % (x, y + self.textOffset)) prb.write("g1 z%6.4f\n" % (cfg.retract)) prb.blankLine() prb.write("g38.2 z%6.4f f%3.1f(reference probe)\n" % (cfg.probeDepth, cfg.probeFeed)) prb.write("g10 L20 P0 z0.000 (zero z)\n") prb.write("g0 z%6.4f\n" % (cfg.retract)) prb.blankLine() if cfg.level: try: inp = open(cfg.probeData, "r") except IOError: ePrint("unable to open level data %s" % (cfg.probeData)) cfg.level = False return (x0, y0, zRef) = inp.readline().split(" ")[:3] #zRef = float(zRef) x0 = float(x0) y0 = float(y0) zRef = 0.0 levelData = [] for probeData in inp: (x, y, z) = probeData.split(" ")[:3] levelData.append((float(x), float(y), float(z) - zRef)) levelIndex = 0 inp.close() font = cfg.font font.setHeight(self.letterHeight) offset = self.textOffset if offset < 0: offset -= self.letterHeight / 2 else: offset += self.letterHeight / 2 m = self.cfg.mill m.safeZ() zOffset = 0.0 if cfg.level: m.write("\nm5 (stop spindle to probe)\n") m.write("g4 p3\n") m.write("g0 x %7.4f y %7.4f\n" % (x0, y0)) m.retract() m.write("g38.2 z%6.4f f%3.1f (probe reference point)\n" % (cfg.probeDepth, cfg.probeFeed)) m.write("g10 L20 P0 z0.000 (zero z)\n") m.retract() m.write("m3 (start spindle)\n") m.write("g4 p3\n") for (x, y, _, text) in self.holeInfo: if len(text) != 0: y += offset if cfg.probe: prb.write("g0 x %7.4f y %7.4f\n" % (x, y)) prb.write("g38.2 z%6.4f f%3.1f(reference probe)\n" % (cfg.probeDepth, cfg.probeFeed)) prb.write("g0 z%6.4f\n" % (cfg.retract)) prb.blankLine() if cfg.level: found = False if levelIndex < len(levelData): (xProbe, yProbe, zOffset) = levelData[levelIndex] if abs(x - xProbe) < MIN_DIST and \ abs(y - yProbe) < MIN_DIST: found = True levelIndex += 1 if not found: for levelIndex, (xProbe, yProbe, zOffset) in \ enumerate(levelData): if abs(x - xProbe) < MIN_DIST and \ abs(y - yProbe) < MIN_DIST: found = True levelIndex += 1 if not found: zOffset = 0.0 ePrint("level data not found") font.setZOffset(zOffset) m.write("\n(x %7.4f y %7.4f zOffset %6.4f %s)\n" % \ (x, y, zOffset, text)) font.mill((x, y), text, center=True)
def rectPocket(self, args): layer = args[1] cfg = self.cfg cfg.ncInit() segments = cfg.dxfInput.getPath(layer) for seg in segments: if len(seg) != 4: ePrint("rectPocket wrong number if sides") continue vert = [] horiz = [] 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) else: ePrint("rectPocket line not horizontal or vertical") continue else: ePrint("rectPocket segment not a line") continue if len(vert) != 2 or len(horiz) != 2: ePrint("rectPocket incorrect number of sides") continue hl0 = horiz[0] vl0 = vert[0] w = abs(hl0.p0[0] - hl0.p1[0]) # width h = abs(vl0.p0[1] - vl0.p1[1]) # height x = min(hl0.p0[0], hl0.p1[0]) y = min(vl0.p0[1], vl0.p1[1]) self.drawRect(x, y, w, h) stepOver = self.getStepOver() self.path = [] self.index = 0 prev = None offset = cfg.endMillSize / 2.0 + cfg.finishAllowance if self.spiral: dist = min(h, w) / 2.0 - offset if dist < 0: ePrint("rectPocket rectangle too small for end mill") continue passes = int(ceil(dist / stepOver)) stepOver = dist / passes dprt("x %7.4f y %7.4f w %7.4f h %7.4f" % (x, y, w, h)) dprt("passes %2d stepOver %7.4f dist %7.4f" % \ (passes, stepOver, dist)) x0 = x + offset y0 = y + offset w -= 2.0 * offset h -= 2.0 * offset for i in range(passes + 1): dprt("pass %2d x0 %7.4f y0 %7.4f w %7.4f h %7.4f" % \ (i, x0, y0, w, h)) prev = self.millRect(prev, x0, y0, w, h, cfg.dir) x0 += stepOver y0 += stepOver w -= 2 * stepOver h -= 2 * stepOver self.millPath() else: if min(h, w) < cfg.endMillSize + cfg.finishAllowance * 2.0: ePrint("rectPocket rectangle too small for end mill") continue dist = h - 4 * offset + (1 - self.getStepOver()) passes = int(ceil(dist / stepOver)) stepOver = dist / passes r = cfg.endMillSize / 2.0 s = stepOver / 2.0 d = sqrt(r*r - s*s) dprt("x %7.4f y %7.4f w %7.4f h %7.4f" % (x, y, w, h)) dprt("passes %2d offset %7.4f dist %7.4f stepOver %7.4f" \ "d %7.4f" % \ (passes, offset, dist, stepOver, d)) x0 = x + offset y0 = y + offset w -= 2.0 * offset h -= 2.0 * offset prev = self.millRect(None, x0, y0, w, h, cfg.dir) self.drawRect(x0 + offset, y0 + offset, \ w - 2*offset, h - 2*offset) x0 += offset + d y0 += stepOver w -= (offset + d) * 2 h -= offset * 2 if h > 0 and w > 0: sign = 1 for i in range(passes): dprt("pass %2d x0 %7.4f y0 %7.4f w %7.4f h %7.4f" % \ (i, x0, y0, w, h)) x1 = x0 + w if sign > 0: p0 = (x0, y0) p1 = (x1, y0) else: p0 = (x1, y0) p1 = (x0, y0) sign = - sign if prev is not None: self.addLineSeg(prev, p0) self.addLineSeg(p0, p1) self.drawMill(p0) self.drawMill(p1) prev = p1 y0 += stepOver 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)
def corner(self, args, dbg=True): cfg = self.cfg # direction = CCW # if cfg.dir is not None and cfg.dir == 'CW': # direction = CW cfg.ncInit() if SIMPLE_BOX: dxf = cfg.dxfInput xMin = dxf.xMin xMax = dxf.xMax yMin = dxf.yMin yMax = dxf.yMax q = self.quadrant if q == XPLUS_YPLUS: xMin = (xMax + xMin) / 2 yMin = (yMax + yMin) / 2 elif q == XMINUS_YPLUS: xMax = (xMax + xMin) / 2 yMin = (yMax + yMin) / 2 elif q == XMINUS_YMINUS: xMax = (xMax + xMin) / 2 yMax = (yMax + yMin) / 2 elif q == XPLUS_YMINUS: xMin = (xMax + xMin) / 2 yMax = (yMax + yMin) / 2 p0 = (xMin, yMax) p1 = (xMax, yMax) p2 = (xMax, yMin) p3 = (xMin, yMin) box = [] box.append(Line(p0, p1)) box.append(Line(p1, p2)) box.append(Line(p2, p3)) box.append(Line(p3, p0)) dprt("\nsimple box") boxLayer = "%02d-00-a-box" % self.layerNum for l in box: l.prt() l.draw(layer=boxLayer) dprt() layer = args[1] segments = cfg.dxfInput.getPath(layer) if len(args) > 2: if args[2] == "break": ePrint("break") # for seg in segments: # for l in seg: # l.prt() # l.draw() # dprt() mp = cfg.getMillPath() for seg in segments: splitSeg = splitArcs(seg) xMax = yMax = MIN_VALUE xMin = yMin = MAX_VALUE for l in splitSeg: l.prt() for (x, y) in (l.p0, l.p1): if x > xMax: # check x xMax = x if x < xMin: xMin = x if y > yMax: # check y yMax = y if y < yMin: yMin = y (self.xMin, self.yMin) = (xMin, yMin) (self.xMax, self.yMax) = (xMax, yMax) dprt("xMin %7.4f xMax %7.4f ymin %7.4f yMax %7.4f" % \ (self.xMin, self.xMax, self.yMin, self.yMax)) dxf = self.cfg.dxfInput dprt("xMin %7.4f xMax %7.4f ymin %7.4f yMax %7.4f" % \ (dxf.xMin, dxf.xMax, dxf.yMin, dxf.yMax)) # for l in splitSeg: # l.draw() if not SIMPLE_BOX: box = self.createBox(splitSeg) if box is None: continue dprt("\ngeneral box") boxLayer = "%02d-00-a-box" % self.layerNum for l in box: l.prt() l.draw(layer=boxLayer) dprt() seg1 = [] for l in splitSeg: (x0, y0) = l.p0 (x1, y1) = l.p1 if l.type == ARC: p = ((x0 + x1) / 2, (y0 + y1) / 2) if (inside(p, box) & 1) == 0: continue elif l.type == LINE: if (inside(l.p0, box) & 1) == 0 and \ (inside(l.p1, box) & 1) == 0: continue seg1.append(l) dprt("seg len %d" % (len(seg1))) if len(seg1) == 0: continue # for l in seg1: # l.prt() # l.draw() dprt("closePath") self.closePath(seg1) closeLayer = "%02d-00-b-close" % self.layerNum for l in seg1: l.prt() l.draw(layer=closeLayer) if self.quadrant <= XPLUS_YMINUS: self.setTrim() offset = cfg.endMillSize / 2.0 + cfg.finishAllowance passes = self.maxPasses # passOffset = self.passOffset if passes == 0: dprt("\nfind max dist") maxD = MIN_VALUE for l in seg1: if l.index <= INDEX_MARKER: continue d = l.pointDistance((self.trimX, self.trimY)) if d is not None: l.prt() dprt("d %7.4f" % (d)) maxD = max(maxD, d) total = maxD - offset passes = int(round(total / self.passOffset)) # passOffset = total / passes dprt("maxD %7.4f total %7.4f passes %d passOffset %7.4f" % \ (maxD, total, passes, self.passOffset)) path = [] for i in range(passes): dprt("\npass %2d offset %7.4f\n" % (i, offset)) seg2 = createPath(seg1, offset, outside=True, keepIndex=True, split=False, dbg=True)[0] pathLayer = "%02d-%02d-c-path" % (self.layerNum, i) for l in seg2: l.draw(layer=pathLayer) if self.quadrant <= XPLUS_YMINUS: dprt() seg3 = self.trim(seg2) trimLayer = "%02d-%02d-d-trim" % (self.layerNum, i) for l in seg3: l.draw(layer=trimLayer) l.label(layer=trimLayer) else: seg3 = [] for l in seg2: if l.index <= INDEX_MARKER: continue seg3.append(l) print("seg3Len %d" % (len(seg3))) if len(seg3) == 0: break path.append(seg3) # dprt() # for l in seg3: # l.prt() # l.draw() offset += self.passOffset cfg.mill.write("(corner %s)\n" % \ (self.quadrantValues[self.quadrant][0])) if self.alternate: finalPath = [] lastPoint = None for (i, seg) in enumerate(path): if (i & 1) != 0: seg = reverseSeg(seg) if lastPoint is not None: finalPath.append(Line(lastPoint, seg[0].p0)) finalPath += seg lastPoint = finalPath[-1].p1 finalPath = reverseSeg(finalPath, makeCopy=False) if self.quadrant <= XPLUS_YMINUS: self.addEntry(finalPath) self.addExit(finalPath) else: self.addEntry1(finalPath) self.addExit1(finalPath) dprt() finalLayer = "%02d-%02d-e-final" % (self.layerNum, i) for l in finalPath: l.prt() l.draw(layer=finalLayer) mp.millPath(finalPath, closed=False, minDist=False) else: for seg in reversed(path): if self.quadrant <= XPLUS_YMINUS: self.addEntry(seg) self.addExit(seg) else: self.addEntry1(seg) self.addExit1(seg) dprt() finalLayer = "%02d-%02d-e-final" % (self.layerNum, i) for l in seg: l.prt() l.draw(layer=finalLayer) mp.millPath(seg, closed=False, minDist=False) self.layerNum += 1
def addExit(self, path): l = path[-1] (x, y) = l.p1 dx = x - l.p0[0] dy = y - l.p0[1] r = self.leadRadius if abs(x - self.trimX) < MIN_DIST: if self.xPlus: x += r if dy > 0: a0 = 90 a1 = 180 direction = CW ex = 0 else: a0 = 180 a1 = 270 direction = CCW ex = 1 else: x -= r if dy < 0: a0 = 270 a1 = 360 direction = CW ex = 2 else: a0 = 0 a1 = 90 direction = CCW ex = 3 elif abs(y - self.trimY) < MIN_DIST: if self.yPlus: y += r if dx < 0: a0 = 180 a1 = 270 direction = CW ex = 4 else: a0 = 270 a1 = 360 direction = CCW ex = 5 else: y -= r if dx > 0: a0 = 0 a1 = 90 direction = CW ex = 6 else: a0 = 90 a1 = 180 direction = CCW ex = 7 else: ePrint("error") return dprt("exit %d direction %s" % (ex, oStr(self.cfg.dir))) l = Arc((x, y), r, a0, a1, direction=direction) path.append(l)
def addEntry(self, path): l = path[0] (x, y) = l.p0 dx = x - l.p1[0] dy = y - l.p1[1] r = self.leadRadius if abs(x - self.trimX) < MIN_DIST: if self.xPlus: x += r if dy > 0: a0 = 90 a1 = 180 direction = CCW en = 0 else: a0 = 180 a1 = 270 direction = CW en = 1 else: x -= r if dy < 0: a0 = 270 a1 = 360 direction = CCW en = 2 else: a0 = 0 a1 = 90 direction = CW en = 3 elif abs(y - self.trimY) < MIN_DIST: if self.yPlus: y += r if dx < 0: a0 = 180 a1 = 270 direction = CCW en = 4 else: a0 = 270 a1 = 360 direction = CW en = 5 else: y -= r if dx > 0: a0 = 0 a1 = 90 direction = CCW en = 6 else: a0 = 90 a1 = 180 direction = CW en = 7 else: ePrint("addEntry error x %7.4f xTrim %7.4f y %7.4f yTrim %7.4f" % \ (x, self.trimX, y, self.trimY)) return dprt("entry %d direction %s" % (en, oStr(self.cfg.dir))) l = Arc((x, y), r, a0, a1, direction=direction) path.insert(0, l)