def _extractPathWire(self, obj, base, flatWire, cutShp): PathLog.debug('_extractPathWire()') subLoops = list() rtnWIRES = list() osWrIdxs = list() subDistFactor = 1.0 # Raise to include sub wires at greater distance from original fdv = obj.FinalDepth.Value wire = flatWire lstVrtIdx = len(wire.Vertexes) - 1 lstVrt = wire.Vertexes[lstVrtIdx] frstVrt = wire.Vertexes[0] cent0 = FreeCAD.Vector(frstVrt.X, frstVrt.Y, fdv) cent1 = FreeCAD.Vector(lstVrt.X, lstVrt.Y, fdv) pl = FreeCAD.Placement() pl.Rotation = FreeCAD.Rotation(FreeCAD.Vector(0, 0, 1), 0) pl.Base = FreeCAD.Vector(0, 0, 0) # Calculate offset shape, containing cut region ofstShp = self._extractFaceOffset(obj, cutShp, False) # CHECK for ZERO area of offset shape try: osArea = ofstShp.Area except Exception as ee: PathLog.error('No area to offset shape returned.') return False if PathLog.getLevel(PathLog.thisModule()) == 4: os = FreeCAD.ActiveDocument.addObject('Part::Feature', 'tmpOffsetShape') os.Shape = ofstShp os.recompute() os.purgeTouched() self.tmpGrp.addObject(os) numOSWires = len(ofstShp.Wires) for w in range(0, numOSWires): osWrIdxs.append(w) # Identify two vertexes for dividing offset loop NEAR0 = self._findNearestVertex(ofstShp, cent0) min0i = 0 min0 = NEAR0[0][4] for n in range(0, len(NEAR0)): N = NEAR0[n] if N[4] < min0: min0 = N[4] min0i = n (w0, vi0, pnt0, vrt0, d0) = NEAR0[0] # min0i if PathLog.getLevel(PathLog.thisModule()) == 4: near0 = FreeCAD.ActiveDocument.addObject('Part::Feature', 'tmpNear0') near0.Shape = Part.makeLine(cent0, pnt0) near0.recompute() near0.purgeTouched() self.tmpGrp.addObject(near0) NEAR1 = self._findNearestVertex(ofstShp, cent1) min1i = 0 min1 = NEAR1[0][4] for n in range(0, len(NEAR1)): N = NEAR1[n] if N[4] < min1: min1 = N[4] min1i = n (w1, vi1, pnt1, vrt1, d1) = NEAR1[0] # min1i if PathLog.getLevel(PathLog.thisModule()) == 4: near1 = FreeCAD.ActiveDocument.addObject('Part::Feature', 'tmpNear1') near1.Shape = Part.makeLine(cent1, pnt1) near1.recompute() near1.purgeTouched() self.tmpGrp.addObject(near1) if w0 != w1: PathLog.warning( 'Offset wire endpoint indexes are not equal - w0, w1: {}, {}'. format(w0, w1)) if PathLog.getLevel(PathLog.thisModule()) == 4: PathLog.debug('min0i is {}.'.format(min0i)) PathLog.debug('min1i is {}.'.format(min1i)) PathLog.debug('NEAR0[{}] is {}.'.format(w0, NEAR0[w0])) PathLog.debug('NEAR1[{}] is {}.'.format(w1, NEAR1[w1])) PathLog.debug('NEAR0 is {}.'.format(NEAR0)) PathLog.debug('NEAR1 is {}.'.format(NEAR1)) mainWire = ofstShp.Wires[w0] # Check for additional closed loops in offset wire by checking distance to iTAG or eTAG elements if numOSWires > 1: # check all wires for proximity(children) to intersection tags tagsComList = list() for T in self.cutSideTags.Faces: tcom = T.CenterOfMass tv = FreeCAD.Vector(tcom.x, tcom.y, 0.0) tagsComList.append(tv) subDist = self.ofstRadius * subDistFactor for w in osWrIdxs: if w != w0: cutSub = False VTXS = ofstShp.Wires[w].Vertexes for V in VTXS: v = FreeCAD.Vector(V.X, V.Y, 0.0) for t in tagsComList: if t.sub(v).Length < subDist: cutSub = True break if cutSub is True: break if cutSub is True: sub = Part.Wire( Part.__sortEdges__(ofstShp.Wires[w].Edges)) subLoops.append(sub) # Eif # Break offset loop into two wires - one of which is the desired profile path wire. (edgeIdxs0, edgeIdxs1) = self._separateWireAtVertexes(mainWire, mainWire.Vertexes[vi0], mainWire.Vertexes[vi1]) edgs0 = list() edgs1 = list() for e in edgeIdxs0: edgs0.append(mainWire.Edges[e]) for e in edgeIdxs1: edgs1.append(mainWire.Edges[e]) part0 = Part.Wire(Part.__sortEdges__(edgs0)) part1 = Part.Wire(Part.__sortEdges__(edgs1)) # Determine which part is nearest original edge(s) distToPart0 = self._distMidToMid(wire.Wires[0], part0.Wires[0]) distToPart1 = self._distMidToMid(wire.Wires[0], part1.Wires[0]) if distToPart0 < distToPart1: rtnWIRES.append(part0) else: rtnWIRES.append(part1) rtnWIRES.extend(subLoops) return rtnWIRES
def useTempJobClones(self, cloneName): '''useTempJobClones(cloneName) Manage use of temporary model clones for rotational operation calculations. Clones are stored in 'rotJobClones' group.''' if FreeCAD.ActiveDocument.getObject('rotJobClones'): if cloneName == 'Start': if PathLog.getLevel(PathLog.thisModule()) < 4: for cln in FreeCAD.ActiveDocument.getObject( 'rotJobClones').Group: FreeCAD.ActiveDocument.removeObject(cln.Name) elif cloneName == 'Delete': if PathLog.getLevel(PathLog.thisModule()) < 4: for cln in FreeCAD.ActiveDocument.getObject( 'rotJobClones').Group: FreeCAD.ActiveDocument.removeObject(cln.Name) FreeCAD.ActiveDocument.removeObject('rotJobClones') else: FreeCAD.ActiveDocument.getObject( 'rotJobClones').purgeTouched() else: FreeCAD.ActiveDocument.addObject("App::DocumentObjectGroup", "rotJobClones") if FreeCAD.GuiUp: FreeCADGui.ActiveDocument.getObject( 'rotJobClones').Visibility = False if cloneName != 'Start' and cloneName != 'Delete': FreeCAD.ActiveDocument.getObject('rotJobClones').addObject( FreeCAD.ActiveDocument.getObject(cloneName)) if FreeCAD.GuiUp: FreeCADGui.ActiveDocument.getObject( cloneName).Visibility = False
def test13(self): """Verify setting other modul's log level doesn't change this one's.""" # if this test fails then most likely the global RESET is broken PathLog.setLevel(PathLog.Level.DEBUG, 'SomeOtherModule') self.assertEqual(PathLog.getLevel(), PathLog.Level.NOTICE) self.assertEqual(PathLog.getLevel(self.MODULE), PathLog.Level.NOTICE)
def test14(self): """Verify resetting log level for module falls back to global level.""" PathLog.setLevel(PathLog.Level.DEBUG, self.MODULE) self.assertEqual(PathLog.getLevel(self.MODULE), PathLog.Level.DEBUG) # changing global log level does not affect module PathLog.setLevel(PathLog.Level.ERROR) self.assertEqual(PathLog.getLevel(self.MODULE), PathLog.Level.DEBUG) # resetting module log level restores global log level for module PathLog.setLevel(PathLog.Level.RESET, self.MODULE) self.assertEqual(PathLog.getLevel(self.MODULE), PathLog.Level.ERROR) # changing the global log level will also change the module log level PathLog.setLevel(PathLog.Level.DEBUG) self.assertEqual(PathLog.getLevel(self.MODULE), PathLog.Level.DEBUG)
def getEnvelope(partshape, subshape=None, depthparams=None): ''' getEnvelope(partshape, stockheight=None) returns a shape corresponding to the partshape silhouette extruded to height. if stockheight is given, the returned shape is extruded to that height otherwise the returned shape is the height of the original shape boundbox partshape = solid object stockheight = float - Absolute Z height of the top of material before cutting. ''' PathLog.track(partshape, subshape, depthparams) # if partshape.Volume == 0.0: #Not a 3D object # return None zShift = 0 if subshape is not None: if isinstance(subshape, Part.Face): PathLog.debug('processing a face') sec = Part.makeCompound([subshape]) else: area = Path.Area(Fill=2, Coplanar=0).add(subshape) area.setPlane(makeWorkplane(partshape)) PathLog.debug("About to section with params: {}".format( area.getParams())) sec = area.makeSections(heights=[0.0], project=True)[0].getShape() # zShift = partshape.BoundBox.ZMin - subshape.BoundBox.ZMin PathLog.debug('partshapeZmin: {}, subshapeZMin: {}, zShift: {}'.format( partshape.BoundBox.ZMin, subshape.BoundBox.ZMin, zShift)) else: area = Path.Area(Fill=2, Coplanar=0).add(partshape) area.setPlane(makeWorkplane(partshape)) sec = area.makeSections(heights=[0.0], project=True)[0].getShape() # If depthparams are passed, use it to calculate bottom and height of # envelope if depthparams is not None: # eLength = float(stockheight)-partshape.BoundBox.ZMin eLength = depthparams.safe_height - depthparams.final_depth #envelopeshape = sec.extrude(FreeCAD.Vector(0, 0, eLength)) zShift = depthparams.final_depth - sec.BoundBox.ZMin PathLog.debug('boundbox zMIN: {} elength: {} zShift {}'.format( partshape.BoundBox.ZMin, eLength, zShift)) else: eLength = partshape.BoundBox.ZLength - sec.BoundBox.ZMin # Shift the section based on selection and depthparams. newPlace = FreeCAD.Placement(FreeCAD.Vector(0, 0, zShift), sec.Placement.Rotation) sec.Placement = newPlace # Extrude the section to top of Boundbox or desired height envelopeshape = sec.extrude(FreeCAD.Vector(0, 0, eLength)) if PathLog.getLevel(PathLog.thisModule()) == PathLog.Level.DEBUG: removalshape = FreeCAD.ActiveDocument.addObject( "Part::Feature", "Envelope") removalshape.Shape = envelopeshape return envelopeshape
def debugMarker(vector, label, color=None, radius=0.5): if PathLog.getLevel(LOG_MODULE) == PathLog.Level.DEBUG: obj = FreeCAD.ActiveDocument.addObject("Part::Sphere", label) obj.Label = label obj.Radius = radius obj.Placement = FreeCAD.Placement(vector, FreeCAD.Rotation(FreeCAD.Vector(0, 0, 1), 0)) if color: obj.ViewObject.ShapeColor = color
def debugMarker(vector, label, color=None, radius=0.5): if PathLog.getLevel(PathLog.thisModule()) == PathLog.Level.DEBUG: obj = FreeCAD.ActiveDocument.addObject("Part::Sphere", label) obj.Label = label obj.Radius = radius obj.Placement = FreeCAD.Placement(vector, FreeCAD.Rotation(FreeCAD.Vector(0, 0, 1), 0)) if color: obj.ViewObject.ShapeColor = color
def debugEdge(edge, prefix, force=False): if force or PathLog.getLevel(PathLog.thisModule()) == PathLog.Level.DEBUG: pf = edge.valueAt(edge.FirstParameter) pl = edge.valueAt(edge.LastParameter) if type(edge.Curve) == Part.Line or type(edge.Curve) == Part.LineSegment: print("%s %s((%.2f, %.2f, %.2f) - (%.2f, %.2f, %.2f))" % (prefix, type(edge.Curve), pf.x, pf.y, pf.z, pl.x, pl.y, pl.z)) else: pm = edge.valueAt((edge.FirstParameter+edge.LastParameter)/2) print("%s %s((%.2f, %.2f, %.2f) - (%.2f, %.2f, %.2f) - (%.2f, %.2f, %.2f))" % (prefix, type(edge.Curve), pf.x, pf.y, pf.z, pm.x, pm.y, pm.z, pl.x, pl.y, pl.z))
def debugCircle(vector, r, label, color=None): if PathLog.getLevel(LOG_MODULE) == PathLog.Level.DEBUG: obj = FreeCAD.ActiveDocument.addObject("Part::Cylinder", label) obj.Label = label obj.Radius = r obj.Height = 1 obj.Placement = FreeCAD.Placement(vector, FreeCAD.Rotation(FreeCAD.Vector(0, 0, 1), 0)) obj.ViewObject.Transparency = 90 if color: obj.ViewObject.ShapeColor = color
def debugCylinder(vector, r, height, label, color=None): if PathLog.getLevel(PathLog.thisModule()) == PathLog.Level.DEBUG: obj = FreeCAD.ActiveDocument.addObject("Part::Cylinder", label) obj.Label = label obj.Radius = r obj.Height = height obj.Placement = FreeCAD.Placement(vector, FreeCAD.Rotation(FreeCAD.Vector(0, 0, 1), 0)) obj.ViewObject.Transparency = 90 if color: obj.ViewObject.ShapeColor = color
def getEnvelope(partshape, subshape=None, depthparams=None): ''' getEnvelope(partshape, stockheight=None) returns a shape corresponding to the partshape silhouette extruded to height. if stockheight is given, the returned shape is extruded to that height otherwise the returned shape is the height of the original shape boundbox partshape = solid object stockheight = float - Absolute Z height of the top of material before cutting. ''' PathLog.track(partshape, subshape, depthparams) # if partshape.Volume == 0.0: #Not a 3D object # return None zShift = 0 if subshape is not None: if isinstance(subshape, Part.Face): PathLog.debug('processing a face') sec = Part.makeCompound([subshape]) else: area = Path.Area(Fill=2, Coplanar=0).add(subshape) area.setPlane(makeWorkplane(partshape)) PathLog.debug("About to section with params: {}".format(area.getParams())) sec = area.makeSections(heights=[0.0], project=True)[0].getShape() # zShift = partshape.BoundBox.ZMin - subshape.BoundBox.ZMin PathLog.debug('partshapeZmin: {}, subshapeZMin: {}, zShift: {}'.format(partshape.BoundBox.ZMin, subshape.BoundBox.ZMin, zShift)) else: area = Path.Area(Fill=2, Coplanar=0).add(partshape) area.setPlane(makeWorkplane(partshape)) sec = area.makeSections(heights=[0.0], project=True)[0].getShape() # If depthparams are passed, use it to calculate bottom and height of # envelope if depthparams is not None: # eLength = float(stockheight)-partshape.BoundBox.ZMin eLength = depthparams.safe_height - depthparams.final_depth #envelopeshape = sec.extrude(FreeCAD.Vector(0, 0, eLength)) zShift = depthparams.final_depth - sec.BoundBox.ZMin PathLog.debug('boundbox zMIN: {} elength: {} zShift {}'.format(partshape.BoundBox.ZMin, eLength, zShift)) else: eLength = partshape.BoundBox.ZLength - sec.BoundBox.ZMin # Shift the section based on selection and depthparams. newPlace = FreeCAD.Placement(FreeCAD.Vector(0, 0, zShift), sec.Placement.Rotation) sec.Placement = newPlace # Extrude the section to top of Boundbox or desired height envelopeshape = sec.extrude(FreeCAD.Vector(0, 0, eLength)) if PathLog.getLevel(PathLog.thisModule()) == PathLog.Level.DEBUG: removalshape = FreeCAD.ActiveDocument.addObject("Part::Feature", "Envelope") removalshape.Shape = envelopeshape return envelopeshape
def debugCone(vector, r1, r2, height, label, color=None): if PathLog.getLevel(PathLog.thisModule()) == PathLog.Level.DEBUG: obj = FreeCAD.ActiveDocument.addObject("Part::Cone", label) obj.Label = label obj.Radius1 = r1 obj.Radius2 = r2 obj.Height = height obj.Placement = FreeCAD.Placement(vector, FreeCAD.Rotation(FreeCAD.Vector(0, 0, 1), 0)) obj.ViewObject.Transparency = 90 if color: obj.ViewObject.ShapeColor = color
def processTags(self, obj): tagID = 0 if PathLog.getLevel(PathLog.thisModule()) == PathLog.Level.DEBUG: for tag in self.tags: tagID += 1 if tag.enabled: PathLog.debug("x=%s, y=%s, z=%s" % (tag.x, tag.y, self.pathData.minZ)) # debugMarker(FreeCAD.Vector(tag.x, tag.y, self.pathData.minZ), "tag-%02d" % tagID , (1.0, 0.0, 1.0), 0.5) # if tag.angle != 90: # debugCone(tag.originAt(self.pathData.minZ), tag.r1, tag.r2, tag.actualHeight, "tag-%02d" % tagID) # else: # debugCylinder(tag.originAt(self.pathData.minZ), tag.fullWidth()/2, tag.actualHeight, "tag-%02d" % tagID) obj.Path = self.createPath(obj, self.pathData, self.tags)
def updateUI(self): if PathLog.getLevel(LOG_MODULE) == PathLog.Level.DEBUG: for obj in FreeCAD.ActiveDocument.Objects: if obj.Name.startswith("Shape"): FreeCAD.ActiveDocument.removeObject(obj.Name) print("object name %s" % self.obj.Name) if hasattr(self.obj.Proxy, "shapes"): PathLog.info("showing shapes attribute") for shapes in self.obj.Proxy.shapes.itervalues(): for shape in shapes: Part.show(shape) else: PathLog.info("no shapes attribute found")
def processTags(self, obj): tagID = 0 if PathLog.getLevel(PathLog.thisModule()) == PathLog.Level.DEBUG: for tag in self.tags: tagID += 1 if tag.enabled: PathLog.debug("x=%s, y=%s, z=%s" % (tag.x, tag.y, self.pathData.minZ)) #debugMarker(FreeCAD.Vector(tag.x, tag.y, self.pathData.minZ), "tag-%02d" % tagID , (1.0, 0.0, 1.0), 0.5) #if tag.angle != 90: # debugCone(tag.originAt(self.pathData.minZ), tag.r1, tag.r2, tag.actualHeight, "tag-%02d" % tagID) #else: # debugCylinder(tag.originAt(self.pathData.minZ), tag.fullWidth()/2, tag.actualHeight, "tag-%02d" % tagID) obj.Path = self.createPath(obj, self.pathData, self.tags)
def processTags(self, obj): global failures # pylint: disable=global-statement failures = [] tagID = 0 if PathLog.getLevel(PathLog.thisModule()) == PathLog.Level.DEBUG: for tag in self.tags: tagID += 1 if tag.enabled: PathLog.debug("x=%s, y=%s, z=%s" % (tag.x, tag.y, self.pathData.minZ)) # debugMarker(FreeCAD.Vector(tag.x, tag.y, self.pathData.minZ), "tag-%02d" % tagID , (1.0, 0.0, 1.0), 0.5) # if not PathGeom.isRoughly(90, tag.angle): # debugCone(tag.originAt(self.pathData.minZ), tag.r1, tag.r2, tag.actualHeight, "tag-%02d" % tagID) # else: # debugCylinder(tag.originAt(self.pathData.minZ), tag.fullWidth()/2, tag.actualHeight, "tag-%02d" % tagID) obj.Path = self.createPath(obj, self.pathData, self.tags)
def insertBone(self, bone): PathLog.debug(">----------------------------------- %d --------------------------------------" % bone.boneId) self.boneShapes = [] blacklisted, inaccessible = self.boneIsBlacklisted(bone) enabled = not blacklisted self.bones.append((bone.boneId, bone.location(), enabled, inaccessible)) self.boneId = bone.boneId if False and PathLog.getLevel(LOG_MODULE) == PathLog.Level.DEBUG and bone.boneId > 2: commands = self.boneCommands(bone, False) else: commands = self.boneCommands(bone, enabled) bone.commands = commands self.shapes[bone.boneId] = self.boneShapes PathLog.debug("<----------------------------------- %d --------------------------------------" % bone.boneId) return commands
def updateUI(self): customSelected = self.obj.Incision == Incision.Custom self.form.custom.setEnabled(customSelected) self.form.customLabel.setEnabled(customSelected) self.updateBoneList() if PathLog.getLevel(LOG_MODULE) == PathLog.Level.DEBUG: for obj in FreeCAD.ActiveDocument.Objects: if obj.Name.startswith('Shape'): FreeCAD.ActiveDocument.removeObject(obj.Name) PathLog.info('object name %s' % self.obj.Name) if hasattr(self.obj.Proxy, "shapes"): PathLog.info("showing shapes attribute") for shapes in self.obj.Proxy.shapes.values(): for shape in shapes: Part.show(shape) else: PathLog.info("no shapes attribute found")
def updateUI(self): customSelected = self.obj.Incision == Incision.Custom self.form.custom.setEnabled(customSelected) self.form.customLabel.setEnabled(customSelected) self.updateBoneList() if PathLog.getLevel(LOG_MODULE) == PathLog.Level.DEBUG: for obj in FreeCAD.ActiveDocument.Objects: if obj.Name.startswith('Shape'): FreeCAD.ActiveDocument.removeObject(obj.Name) print('object name %s' % self.obj.Name) if hasattr(self.obj.Proxy, "shapes"): PathLog.info("showing shapes attribute") for shapes in self.obj.Proxy.shapes.itervalues(): for shape in shapes: Part.show(shape) else: PathLog.info("no shapes attribute found")
def __init__(self, op, obj, feature, sub, length, direction): PathLog.debug("Extension(%s, %s, %s, %.2f, %s" % (obj.Label, feature, sub, length, direction)) self.op = op self.obj = obj self.feature = feature self.sub = sub self.length = length self.direction = direction self.extFaces = None self.isDebug = True if PathLog.getLevel( PathLog.thisModule()) == 4 else False self.avoid = False if sub.startswith("Avoid_"): self.avoid = True self.wire = None
def _separateWireAtVertexes(self, wire, VV1, VV2): PathLog.debug('_separateWireAtVertexes()') tolerance = self.JOB.GeometryTolerance.Value grps = [[], []] wireIdxs = [[], []] V1 = FreeCAD.Vector(VV1.X, VV1.Y, VV1.Z) V2 = FreeCAD.Vector(VV2.X, VV2.Y, VV2.Z) lenE = len(wire.Edges) FLGS = list() for e in range(0, lenE): FLGS.append(0) chk4 = False for e in range(0, lenE): v = 0 E = wire.Edges[e] fv0 = FreeCAD.Vector(E.Vertexes[0].X, E.Vertexes[0].Y, E.Vertexes[0].Z) fv1 = FreeCAD.Vector(E.Vertexes[1].X, E.Vertexes[1].Y, E.Vertexes[1].Z) if fv0.sub(V1).Length < tolerance: v = 1 if fv1.sub(V2).Length < tolerance: v += 3 chk4 = True elif fv1.sub(V1).Length < tolerance: v = 1 if fv0.sub(V2).Length < tolerance: v += 3 chk4 = True if fv0.sub(V2).Length < tolerance: v = 3 if fv1.sub(V1).Length < tolerance: v += 1 chk4 = True elif fv1.sub(V2).Length < tolerance: v = 3 if fv0.sub(V1).Length < tolerance: v += 1 chk4 = True FLGS[e] += v # Efor PathLog.debug('_separateWireAtVertexes() FLGS: \n{}'.format(FLGS)) PRE = list() POST = list() IDXS = list() IDX1 = list() IDX2 = list() for e in range(0, lenE): f = FLGS[e] PRE.append(f) POST.append(f) IDXS.append(e) IDX1.append(e) IDX2.append(e) PRE.extend(FLGS) PRE.extend(POST) lenFULL = len(PRE) IDXS.extend(IDX1) IDXS.extend(IDX2) if chk4 is True: # find beginning 1 edge begIdx = None begFlg = False for e in range(0, lenFULL): f = PRE[e] i = IDXS[e] if f == 4: begIdx = e grps[0].append(f) wireIdxs[0].append(i) break # find first 3 edge endIdx = None for e in range(begIdx + 1, lenE + begIdx): f = PRE[e] i = IDXS[e] grps[1].append(f) wireIdxs[1].append(i) else: # find beginning 1 edge begIdx = None begFlg = False for e in range(0, lenFULL): f = PRE[e] if f == 1: if begFlg is False: begFlg = True else: begIdx = e break # find first 3 edge and group all first wire edges endIdx = None for e in range(begIdx, lenE + begIdx): f = PRE[e] i = IDXS[e] if f == 3: grps[0].append(f) wireIdxs[0].append(i) endIdx = e break else: grps[0].append(f) wireIdxs[0].append(i) # Collect remaining edges for e in range(endIdx + 1, lenFULL): f = PRE[e] i = IDXS[e] if f == 1: grps[1].append(f) wireIdxs[1].append(i) break else: wireIdxs[1].append(i) grps[1].append(f) # Efor # Eif if PathLog.getLevel(PathLog.thisModule()) != 4: PathLog.debug('grps[0]: {}'.format(grps[0])) PathLog.debug('grps[1]: {}'.format(grps[1])) PathLog.debug('wireIdxs[0]: {}'.format(wireIdxs[0])) PathLog.debug('wireIdxs[1]: {}'.format(wireIdxs[1])) PathLog.debug('PRE: {}'.format(PRE)) PathLog.debug('IDXS: {}'.format(IDXS)) return (wireIdxs[0], wireIdxs[1])
def areaOpShapes(self, obj): '''areaOpShapes(obj) ... return shapes representing the solids to be removed.''' PathLog.track() PathLog.debug("----- areaOpShapes() in PathPocketShape.py") self.isDebug = True if PathLog.getLevel(PathLog.thisModule()) == 4 else False baseSubsTuples = [] allTuples = [] subCount = 0 if obj.Base: PathLog.debug('Processing obj.Base') self.removalshapes = [] # pylint: disable=attribute-defined-outside-init if obj.EnableRotation == 'Off': stock = PathUtils.findParentJob(obj).Stock for (base, subList) in obj.Base: tup = (base, subList, 0.0, 'X', stock) baseSubsTuples.append(tup) else: PathLog.debug('... Rotation is active') # method call here for p in range(0, len(obj.Base)): (bst, at) = self.process_base_geometry_with_rotation(obj, p, subCount) allTuples.extend(at) baseSubsTuples.extend(bst) for o in baseSubsTuples: self.horiz = [] # pylint: disable=attribute-defined-outside-init self.vert = [] # pylint: disable=attribute-defined-outside-init subBase = o[0] subsList = o[1] angle = o[2] axis = o[3] # stock = o[4] for sub in subsList: if 'Face' in sub: if not self.clasifySub(subBase, sub): PathLog.error(translate('PathPocket', 'Pocket does not support shape %s.%s') % (subBase.Label, sub)) if obj.EnableRotation != 'Off': PathLog.warning(translate('PathPocket', 'Face might not be within rotation accessibility limits.')) # Determine final depth as highest value of bottom boundbox of vertical face, # in case of uneven faces on bottom if len(self.vert) > 0: vFinDep = self.vert[0].BoundBox.ZMin for vFace in self.vert: if vFace.BoundBox.ZMin > vFinDep: vFinDep = vFace.BoundBox.ZMin # Determine if vertical faces for a loop: Extract planar loop wire as new horizontal face. self.vertical = PathGeom.combineConnectedShapes(self.vert) # pylint: disable=attribute-defined-outside-init self.vWires = [TechDraw.findShapeOutline(shape, 1, FreeCAD.Vector(0, 0, 1)) for shape in self.vertical] # pylint: disable=attribute-defined-outside-init for wire in self.vWires: w = PathGeom.removeDuplicateEdges(wire) face = Part.Face(w) # face.tessellate(0.1) if PathGeom.isRoughly(face.Area, 0): msg = translate('PathPocket', 'Vertical faces do not form a loop - ignoring') PathLog.error(msg) else: face.translate(FreeCAD.Vector(0, 0, vFinDep - face.BoundBox.ZMin)) self.horiz.append(face) # add faces for extensions self.exts = [] # pylint: disable=attribute-defined-outside-init for ext in self.getExtensions(obj): wire = ext.getWire() if wire: face = Part.Face(wire) self.horiz.append(face) self.exts.append(face) # check all faces and see if they are touching/overlapping and combine those into a compound self.horizontal = [] # pylint: disable=attribute-defined-outside-init for shape in PathGeom.combineConnectedShapes(self.horiz): shape.sewShape() # shape.tessellate(0.1) shpZMin = shape.BoundBox.ZMin PathLog.debug('PathGeom.combineConnectedShapes shape.BoundBox.ZMin: {}'.format(shape.BoundBox.ZMin)) if obj.UseOutline: wire = TechDraw.findShapeOutline(shape, 1, FreeCAD.Vector(0, 0, 1)) wFace = Part.Face(wire) if wFace.BoundBox.ZMin != shpZMin: wFace.translate(FreeCAD.Vector(0, 0, shpZMin - wFace.BoundBox.ZMin)) self.horizontal.append(wFace) PathLog.debug('PathGeom.combineConnectedShapes shape.BoundBox.ZMin: {}'.format(wFace.BoundBox.ZMin)) else: self.horizontal.append(shape) # move all horizontal faces to FinalDepth # extrude all faces up to StartDepth and those are the removal shapes start_dep = obj.StartDepth.Value clrnc = 0.5 # self._addDebugObject('subBase', subBase.Shape) for face in self.horizontal: isFaceUp = True invZ = 0.0 useAngle = angle faceZMin = face.BoundBox.ZMin adj_final_dep = obj.FinalDepth.Value trans = obj.FinalDepth.Value - face.BoundBox.ZMin PathLog.debug('face.BoundBox.ZMin: {}'.format(face.BoundBox.ZMin)) if obj.EnableRotation != 'Off': PathLog.debug('... running isFaceUp()') isFaceUp = self.isFaceUp(subBase, face) # Determine if face is really oriented toward Z+ (rotational purposes) # ignore for cylindrical faces if not isFaceUp: PathLog.debug('... NOT isFaceUp') useAngle += 180.0 invZ = (-2 * face.BoundBox.ZMin) face.translate(FreeCAD.Vector(0.0, 0.0, invZ)) faceZMin = face.BoundBox.ZMin # reset faceZMin PathLog.debug('... face.BoundBox.ZMin: {}'.format(face.BoundBox.ZMin)) else: PathLog.debug('... isFaceUp') if useAngle > 180.0: useAngle -= 360.0 # Apply LimitDepthToFace property for rotational operations if obj.LimitDepthToFace: if obj.FinalDepth.Value < face.BoundBox.ZMin: PathLog.debug('obj.FinalDepth.Value < face.BoundBox.ZMin') # Raise FinalDepth to face depth adj_final_dep = faceZMin # face.BoundBox.ZMin # faceZMin # Ensure StartDepth is above FinalDepth if start_dep <= adj_final_dep: start_dep = adj_final_dep + 1.0 msg = translate('PathPocketShape', 'Start Depth is lower than face depth. Setting to:') PathLog.warning(msg + ' {} mm.'.format(start_dep)) PathLog.debug('LimitDepthToFace adj_final_dep: {}'.format(adj_final_dep)) # Eif face.translate(FreeCAD.Vector(0.0, 0.0, adj_final_dep - faceZMin - clrnc)) zExtVal = start_dep - adj_final_dep + (2 * clrnc) extShp = face.removeSplitter().extrude(FreeCAD.Vector(0, 0, zExtVal)) self.removalshapes.append((extShp, False, 'pathPocketShape', useAngle, axis, start_dep, adj_final_dep)) PathLog.debug("Extent values are strDep: {}, finDep: {}, extrd: {}".format(start_dep, adj_final_dep, zExtVal)) # Efor face # Efor else: # process the job base object as a whole PathLog.debug(translate("Path", 'Processing model as a whole ...')) finDep = obj.FinalDepth.Value strDep = obj.StartDepth.Value self.outlines = [Part.Face(TechDraw.findShapeOutline(base.Shape, 1, FreeCAD.Vector(0, 0, 1))) for base in self.model] # pylint: disable=attribute-defined-outside-init stockBB = self.stock.Shape.BoundBox self.removalshapes = [] # pylint: disable=attribute-defined-outside-init self.bodies = [] # pylint: disable=attribute-defined-outside-init for outline in self.outlines: outline.translate(FreeCAD.Vector(0, 0, stockBB.ZMin - 1)) body = outline.extrude(FreeCAD.Vector(0, 0, stockBB.ZLength + 2)) self.bodies.append(body) self.removalshapes.append((self.stock.Shape.cut(body), False, 'pathPocketShape', 0.0, 'X', strDep, finDep)) for (shape, hole, sub, angle, axis, strDep, finDep) in self.removalshapes: # pylint: disable=unused-variable shape.tessellate(0.05) # originally 0.1 if self.removalshapes: obj.removalshape = self.removalshapes[0][0] return self.removalshapes
def test11(self): """Verify setting global log level.""" PathLog.setLevel(PathLog.Level.DEBUG) self.assertEqual(PathLog.getLevel(), PathLog.Level.DEBUG) self.assertEqual(PathLog.getLevel(self.MODULE), PathLog.Level.DEBUG)
def addDebugDisplay(): return PathLog.getLevel(PathLog.thisModule()) == PathLog.Level.DEBUG
def areaOpShapes(self, obj): '''areaOpShapes(obj) ... returns envelope for all wires formed by the base edges.''' PathLog.track() self.tmpGrp = FreeCAD.ActiveDocument.addObject( 'App::DocumentObjectGroup', 'tmpDebugGrp') tmpGrpNm = self.tmpGrp.Name self.JOB = PathUtils.findParentJob(obj) self.offsetExtra = abs(obj.OffsetExtra.Value) if obj.UseComp: self.useComp = True self.ofstRadius = self.radius + self.offsetExtra self.commandlist.append( Path.Command("(Compensated Tool Path. Diameter: " + str(self.radius * 2) + ")")) else: self.useComp = False self.ofstRadius = self.offsetExtra self.commandlist.append(Path.Command("(Uncompensated Tool Path)")) shapes = [] if obj.Base: basewires = [] zMin = None for b in obj.Base: edgelist = [] for sub in b[1]: edgelist.append(getattr(b[0].Shape, sub)) basewires.append((b[0], DraftGeomUtils.findWires(edgelist))) if zMin is None or b[0].Shape.BoundBox.ZMin < zMin: zMin = b[0].Shape.BoundBox.ZMin PathLog.debug( 'PathProfileEdges areaOpShapes():: len(basewires) is {}'. format(len(basewires))) for base, wires in basewires: for wire in wires: if wire.isClosed() is True: # f = Part.makeFace(wire, 'Part::FaceMakerSimple') # if planar error, Comment out previous line, uncomment the next two (origWire, flatWire) = self._flattenWire(obj, wire, obj.FinalDepth.Value) f = origWire.Shape.Wires[0] if f is not False: # shift the compound to the bottom of the base object for proper sectioning zShift = zMin - f.BoundBox.ZMin newPlace = FreeCAD.Placement( FreeCAD.Vector(0, 0, zShift), f.Placement.Rotation) f.Placement = newPlace env = PathUtils.getEnvelope( base.Shape, subshape=f, depthparams=self.depthparams) shapes.append((env, False)) else: PathLog.error( translate( 'PathProfileEdges', 'The selected edge(s) are inaccessible.')) else: if self.JOB.GeometryTolerance.Value == 0.0: msg = self.JOB.Label + '.GeometryTolerance = 0.0.' msg += translate( 'PathProfileEdges', 'Please set to an acceptable value greater than zero.' ) PathLog.error(msg) else: cutWireObjs = False (origWire, flatWire) = self._flattenWire( obj, wire, obj.FinalDepth.Value) cutShp = self._getCutAreaCrossSection( obj, base, origWire, flatWire) if cutShp is not False: cutWireObjs = self._extractPathWire( obj, base, flatWire, cutShp) if cutWireObjs is not False: for cW in cutWireObjs: shapes.append((cW, False)) self.profileEdgesIsOpen = True else: PathLog.error( translate( 'PathProfileEdges', 'The selected edge(s) are inaccessible.' )) # Delete the temporary objects if PathLog.getLevel(PathLog.thisModule()) != 4: for to in self.tmpGrp.Group: FreeCAD.ActiveDocument.removeObject(to.Name) FreeCAD.ActiveDocument.removeObject(tmpGrpNm) else: if FreeCAD.GuiUp: import FreeCADGui FreeCADGui.ActiveDocument.getObject( tmpGrpNm).Visibility = False return shapes
def opExecute(self, obj, getsim=False): # pylint: disable=arguments-differ '''opExecute(obj, getsim=False) ... implementation of Path.Area ops. determines the parameters for _buildPathArea(). Do not overwrite, implement areaOpAreaParams(obj, isHole) ... op specific area param dictionary areaOpPathParams(obj, isHole) ... op specific path param dictionary areaOpShapes(obj) ... the shape for path area to process areaOpUseProjection(obj) ... return true if operation can use projection instead.''' PathLog.track() # Instantiate class variables for operation reference self.endVector = None # pylint: disable=attribute-defined-outside-init self.rotateFlag = False # pylint: disable=attribute-defined-outside-init self.leadIn = 2.0 # pylint: disable=attribute-defined-outside-init self.cloneNames = [] # pylint: disable=attribute-defined-outside-init self.guiMsgs = [] # pylint: disable=attribute-defined-outside-init self.tempObjectNames = [] # pylint: disable=attribute-defined-outside-init self.stockBB = PathUtils.findParentJob(obj).Stock.Shape.BoundBox # pylint: disable=attribute-defined-outside-init self.useTempJobClones( 'Delete') # Clear temporary group and recreate for temp job clones self.rotStartDepth = None # pylint: disable=attribute-defined-outside-init if obj.EnableRotation != 'Off': # Calculate operation heights based upon rotation radii opHeights = self.opDetermineRotationRadii(obj) (self.xRotRad, self.yRotRad, self.zRotRad) = opHeights[0] # pylint: disable=attribute-defined-outside-init (self.clrOfset, self.safOfst) = opHeights[1] # pylint: disable=attribute-defined-outside-init # Set clearance and safe heights based upon rotation radii if obj.EnableRotation == 'A(x)': strDep = self.xRotRad elif obj.EnableRotation == 'B(y)': strDep = self.yRotRad else: strDep = max(self.xRotRad, self.yRotRad) finDep = -1 * strDep self.rotStartDepth = strDep obj.ClearanceHeight.Value = strDep + self.clrOfset obj.SafeHeight.Value = strDep + self.safOfst # Create visual axes when debugging. if PathLog.getLevel(PathLog.thisModule()) == 4: self.visualAxis() else: strDep = obj.StartDepth.Value finDep = obj.FinalDepth.Value # Set axial feed rates based upon horizontal feed rates safeCircum = 2 * math.pi * obj.SafeHeight.Value self.axialFeed = 360 / safeCircum * self.horizFeed # pylint: disable=attribute-defined-outside-init self.axialRapid = 360 / safeCircum * self.horizRapid # pylint: disable=attribute-defined-outside-init # Initiate depthparams and calculate operation heights for rotational operation self.depthparams = self._customDepthParams(obj, obj.StartDepth.Value, obj.FinalDepth.Value) # Set start point if PathOp.FeatureStartPoint & self.opFeatures( obj) and obj.UseStartPoint: start = obj.StartPoint else: start = None aOS = self.areaOpShapes(obj) # pylint: disable=assignment-from-no-return # Adjust tuples length received from other PathWB tools/operations beside PathPocketShape shapes = [] for shp in aOS: if len(shp) == 2: (fc, iH) = shp # fc, iH, sub, angle, axis, strtDep, finDep tup = fc, iH, 'otherOp', 0.0, 'S', obj.StartDepth.Value, obj.FinalDepth.Value shapes.append(tup) else: shapes.append(shp) if len(shapes) > 1: jobs = list() for s in shapes: if s[2] == 'OpenEdge': shp = Part.makeCompound(s[0]) else: shp = s[0] jobs.append({ 'x': shp.BoundBox.XMax, 'y': shp.BoundBox.YMax, 'shape': s }) jobs = PathUtils.sort_jobs(jobs, ['x', 'y']) shapes = [j['shape'] for j in jobs] sims = [] numShapes = len(shapes) for ns in range(0, numShapes): profileEdgesIsOpen = False (shape, isHole, sub, angle, axis, strDep, finDep) = shapes[ns] # pylint: disable=unused-variable if sub == 'OpenEdge': profileEdgesIsOpen = True if PathOp.FeatureStartPoint & self.opFeatures( obj) and obj.UseStartPoint: osp = obj.StartPoint self.commandlist.append( Path.Command('G0', { 'X': osp.x, 'Y': osp.y, 'F': self.horizRapid })) if ns < numShapes - 1: nextAxis = shapes[ns + 1][4] else: nextAxis = 'L' self.depthparams = self._customDepthParams(obj, strDep, finDep) try: if profileEdgesIsOpen: (pp, sim) = self._buildProfileOpenEdges( obj, shape, isHole, start, getsim) else: (pp, sim) = self._buildPathArea(obj, shape, isHole, start, getsim) except Exception as e: # pylint: disable=broad-except FreeCAD.Console.PrintError(e) FreeCAD.Console.PrintError( "Something unexpected happened. Check project and tool config." ) else: if profileEdgesIsOpen: ppCmds = pp else: ppCmds = pp.Commands if obj.EnableRotation != 'Off' and self.rotateFlag is True: # Rotate model to index for cut if axis == 'X': axisOfRot = 'A' elif axis == 'Y': axisOfRot = 'B' elif axis == 'Z': axisOfRot = 'C' else: axisOfRot = 'A' # Rotate Model to correct angle ppCmds.insert( 0, Path.Command('G0', { axisOfRot: angle, 'F': self.axialRapid })) # Raise cutter to safe height ppCmds.insert( 0, Path.Command('G0', { 'Z': obj.SafeHeight.Value, 'F': self.vertRapid })) # Return index to starting position if axis of rotation changes. if numShapes > 1: if ns != numShapes - 1: if axis != nextAxis: ppCmds.append( Path.Command('G0', { axisOfRot: 0.0, 'F': self.axialRapid })) # Eif # Save gcode commands to object command list self.commandlist.extend(ppCmds) sims.append(sim) # Eif if self.areaOpRetractTool( obj) and self.endVector is not None and len( self.commandlist) > 1: self.endVector[2] = obj.ClearanceHeight.Value self.commandlist.append( Path.Command('G0', { 'Z': obj.ClearanceHeight.Value, 'F': self.vertRapid })) # Raise cutter to safe height and rotate back to original orientation # based on next rotational operation in job if self.rotateFlag is True: resetAxis = False lastJobOp = None nextJobOp = None opIdx = 0 JOB = PathUtils.findParentJob(obj) jobOps = JOB.Operations.Group numJobOps = len(jobOps) for joi in range(0, numJobOps): jo = jobOps[joi] if jo.Name == obj.Name: opIdx = joi lastOpIdx = opIdx - 1 nextOpIdx = opIdx + 1 if lastOpIdx > -1: lastJobOp = jobOps[lastOpIdx] if nextOpIdx < numJobOps: nextJobOp = jobOps[nextOpIdx] if lastJobOp is not None: if hasattr(lastJobOp, 'EnableRotation'): PathLog.debug( 'Last Op, {}, has `EnableRotation` set to {}'.format( lastJobOp.Label, lastJobOp.EnableRotation)) if lastJobOp.EnableRotation != obj.EnableRotation: resetAxis = True # if ns == numShapes - 1: # If last shape, check next op EnableRotation setting if nextJobOp is not None: if hasattr(nextJobOp, 'EnableRotation'): PathLog.debug( 'Next Op, {}, has `EnableRotation` set to {}'.format( nextJobOp.Label, nextJobOp.EnableRotation)) if nextJobOp.EnableRotation != obj.EnableRotation: resetAxis = True # Raise to safe height if rotation activated self.commandlist.append( Path.Command('G0', { 'Z': obj.SafeHeight.Value, 'F': self.vertRapid })) # reset rotational axes if necessary if resetAxis is True: self.commandlist.append( Path.Command('G0', { 'A': 0.0, 'F': self.axialRapid })) self.commandlist.append( Path.Command('G0', { 'B': 0.0, 'F': self.axialRapid })) self.useTempJobClones( 'Delete') # Delete temp job clone group and contents self.guiMessage('title', None, show=True) # Process GUI messages to user for ton in self.tempObjectNames: # remove temporary objects by name FreeCAD.ActiveDocument.removeObject(ton) PathLog.debug("obj.Name: " + str(obj.Name) + "\n\n") return sims
def _makeStop(self, sType, pA, pB, lbl): rad = self.radius ofstRad = self.ofstRadius extra = self.radius / 10 pl = FreeCAD.Placement() pl.Rotation = FreeCAD.Rotation(FreeCAD.Vector(0, 0, 1), 0) pl.Base = FreeCAD.Vector(0, 0, 0) E = FreeCAD.Vector(pB.x, pB.y, 0) # endpoint C = FreeCAD.Vector(pA.x, pA.y, 0) # checkpoint lenEC = E.sub(C).Length if self.useComp is True or (self.useComp is False and self.offsetExtra != 0): # 'L' stop shape and edge legend # --1-- # | | # 2 6 # | | # | ----5----| # | 4 # -----3-------| # positive dist in _makePerp2DVector() is CCW rotation p1 = E if sType == 'BEG': p2 = self._makePerp2DVector(C, E, -0.25) # E1 p3 = self._makePerp2DVector(p1, p2, ofstRad + 1 + extra) # E2 p4 = self._makePerp2DVector(p2, p3, 0.25 + ofstRad + extra) # E3 p5 = self._makePerp2DVector(p3, p4, 1 + extra) # E4 p6 = self._makePerp2DVector(p4, p5, ofstRad + extra) # E5 elif sType == 'END': p2 = self._makePerp2DVector(C, E, 0.25) # E1 p3 = self._makePerp2DVector(p1, p2, -1 * (ofstRad + 1 + extra)) # E2 p4 = self._makePerp2DVector(p2, p3, -1 * (0.25 + ofstRad + extra)) # E3 p5 = self._makePerp2DVector(p3, p4, -1 * (1 + extra)) # E4 p6 = self._makePerp2DVector(p4, p5, -1 * (ofstRad + extra)) # E5 p7 = E # E6 L1 = Part.makeLine(p1, p2) L2 = Part.makeLine(p2, p3) L3 = Part.makeLine(p3, p4) L4 = Part.makeLine(p4, p5) L5 = Part.makeLine(p5, p6) L6 = Part.makeLine(p6, p7) wire = Part.Wire([L1, L2, L3, L4, L5, L6]) else: # 'L' stop shape and edge legend # : # |----2-------| # 3 1 # |-----4------| # positive dist in _makePerp2DVector() is CCW rotation p1 = E if sType == 'BEG': p2 = self._makePerp2DVector( C, E, -1 * (0.25 + abs(self.offsetExtra))) # left, 0.25 p3 = self._makePerp2DVector(p1, p2, 0.25 + abs(self.offsetExtra)) p4 = self._makePerp2DVector( p2, p3, (0.5 + abs(self.offsetExtra))) # FIRST POINT p5 = self._makePerp2DVector( p3, p4, 0.25 + abs(self.offsetExtra)) # E1 SECOND elif sType == 'END': p2 = self._makePerp2DVector( C, E, (0.25 + abs(self.offsetExtra))) # left, 0.25 p3 = self._makePerp2DVector( p1, p2, -1 * (0.25 + abs(self.offsetExtra))) p4 = self._makePerp2DVector( p2, p3, -1 * (0.5 + abs(self.offsetExtra))) # FIRST POINT p5 = self._makePerp2DVector( p3, p4, -1 * (0.25 + abs(self.offsetExtra))) # E1 SECOND p6 = p1 # E4 L1 = Part.makeLine(p1, p2) L2 = Part.makeLine(p2, p3) L3 = Part.makeLine(p3, p4) L4 = Part.makeLine(p4, p5) L5 = Part.makeLine(p5, p6) wire = Part.Wire([L1, L2, L3, L4, L5]) # Eif face = Part.Face(wire) if PathLog.getLevel(PathLog.thisModule()) == 4: os = FreeCAD.ActiveDocument.addObject('Part::Feature', 'tmp' + lbl) os.Shape = face os.recompute() os.purgeTouched() self.tmpGrp.addObject(os) return face
def _getCutAreaCrossSection(self, obj, base, origWire, flatWire): PathLog.debug('_getCutAreaCrossSection()') FCAD = FreeCAD.ActiveDocument tolerance = self.JOB.GeometryTolerance.Value toolDiam = 2 * self.radius # self.radius defined in PathAreaOp or PathProfileBase modules minBfr = toolDiam * 1.25 bbBfr = (self.ofstRadius * 2) * 1.25 if bbBfr < minBfr: bbBfr = minBfr fwBB = flatWire.BoundBox wBB = origWire.BoundBox minArea = (self.ofstRadius - tolerance)**2 * math.pi useWire = origWire.Wires[0] numOrigEdges = len(useWire.Edges) sdv = wBB.ZMax fdv = obj.FinalDepth.Value extLenFwd = sdv - fdv WIRE = flatWire.Wires[0] numEdges = len(WIRE.Edges) # Identify first/last edges and first/last vertex on wire begE = WIRE.Edges[0] # beginning edge endE = WIRE.Edges[numEdges - 1] # ending edge blen = begE.Length elen = endE.Length Vb = begE.Vertexes[0] # first vertex of wire Ve = endE.Vertexes[1] # last vertex of wire pb = FreeCAD.Vector(Vb.X, Vb.Y, fdv) pe = FreeCAD.Vector(Ve.X, Ve.Y, fdv) # Identify endpoints connecting circle center and diameter vectDist = pe.sub(pb) diam = vectDist.Length cntr = vectDist.multiply(0.5).add(pb) R = diam / 2 pl = FreeCAD.Placement() pl.Rotation = FreeCAD.Rotation(FreeCAD.Vector(0, 0, 1), 0) pl.Base = FreeCAD.Vector(0, 0, 0) # Obtain beginning point perpendicular points if blen > 0.1: bcp = begE.valueAt(begE.getParameterByLength( 0.1)) # point returned 0.1 mm along edge else: bcp = FreeCAD.Vector(begE.Vertexes[1].X, begE.Vertexes[1].Y, fdv) if elen > 0.1: ecp = endE.valueAt(endE.getParameterByLength( elen - 0.1)) # point returned 0.1 mm along edge else: ecp = FreeCAD.Vector(endE.Vertexes[1].X, endE.Vertexes[1].Y, fdv) # Create intersection tags for determining which side of wire to cut (begInt, begExt, iTAG, eTAG) = self._makeIntersectionTags(useWire, numOrigEdges, fdv) if not begInt or not begExt: return False self.iTAG = iTAG self.eTAG = eTAG # Create extended wire boundbox, and extrude extBndbox = self._makeExtendedBoundBox(wBB, bbBfr, fdv) extBndboxEXT = extBndbox.extrude(FreeCAD.Vector(0, 0, extLenFwd)) # Cut model(selected edges) from extended edges boundbox cutArea = extBndboxEXT.cut(base.Shape) if PathLog.getLevel(PathLog.thisModule()) == 4: CA = FCAD.addObject('Part::Feature', 'tmpCutArea') CA.Shape = cutArea CA.recompute() CA.purgeTouched() self.tmpGrp.addObject(CA) # Get top and bottom faces of cut area (CA), and combine faces when necessary topFc = list() botFc = list() bbZMax = cutArea.BoundBox.ZMax bbZMin = cutArea.BoundBox.ZMin for f in range(0, len(cutArea.Faces)): FcBB = cutArea.Faces[f].BoundBox if abs(FcBB.ZMax - bbZMax) < tolerance and abs(FcBB.ZMin - bbZMax) < tolerance: topFc.append(f) if abs(FcBB.ZMax - bbZMin) < tolerance and abs(FcBB.ZMin - bbZMin) < tolerance: botFc.append(f) if len(topFc) == 0: PathLog.error('Failed to identify top faces of cut area.') return False topComp = Part.makeCompound([cutArea.Faces[f] for f in topFc]) topComp.translate(FreeCAD.Vector( 0, 0, fdv - topComp.BoundBox.ZMin)) # Translate face to final depth if len(botFc) > 1: PathLog.debug('len(botFc) > 1') bndboxFace = Part.Face(extBndbox.Wires[0]) tmpFace = Part.Face(extBndbox.Wires[0]) for f in botFc: Q = tmpFace.cut(cutArea.Faces[f]) tmpFace = Q botComp = bndboxFace.cut(tmpFace) else: botComp = Part.makeCompound([ cutArea.Faces[f] for f in botFc ]) # Part.makeCompound([CA.Shape.Faces[f] for f in botFc]) botComp.translate(FreeCAD.Vector( 0, 0, fdv - botComp.BoundBox.ZMin)) # Translate face to final depth # Make common of the two comFC = topComp.common(botComp) # Determine with which set of intersection tags the model intersects (cmnIntArea, cmnExtArea) = self._checkTagIntersection(iTAG, eTAG, 'QRY', comFC) if cmnExtArea > cmnIntArea: PathLog.debug('Cutting on Ext side.') self.cutSide = 'E' self.cutSideTags = eTAG tagCOM = begExt.CenterOfMass else: PathLog.debug('Cutting on Int side.') self.cutSide = 'I' self.cutSideTags = iTAG tagCOM = begInt.CenterOfMass # Make two beginning style(oriented) 'L' shape stops begStop = self._makeStop('BEG', bcp, pb, 'BegStop') altBegStop = self._makeStop('END', bcp, pb, 'BegStop') # Identify to which style 'L' stop the beginning intersection tag is closest, # and create partner end 'L' stop geometry, and save for application later lenBS_extETag = begStop.CenterOfMass.sub(tagCOM).Length lenABS_extETag = altBegStop.CenterOfMass.sub(tagCOM).Length if lenBS_extETag < lenABS_extETag: endStop = self._makeStop('END', ecp, pe, 'EndStop') pathStops = Part.makeCompound([begStop, endStop]) else: altEndStop = self._makeStop('BEG', ecp, pe, 'EndStop') pathStops = Part.makeCompound([altBegStop, altEndStop]) pathStops.translate(FreeCAD.Vector(0, 0, fdv - pathStops.BoundBox.ZMin)) # Identify closed wire in cross-section that corresponds to user-selected edge(s) workShp = comFC fcShp = workShp wire = origWire WS = workShp.Wires lenWS = len(WS) if lenWS < 3: wi = 0 else: wi = None for wvt in wire.Vertexes: for w in range(0, lenWS): twr = WS[w] for v in range(0, len(twr.Vertexes)): V = twr.Vertexes[v] if abs(V.X - wvt.X) < tolerance: if abs(V.Y - wvt.Y) < tolerance: # Same vertex found. This wire to be used for offset wi = w break # Efor if wi is None: PathLog.error( 'The cut area cross-section wire does not coincide with selected edge. Wires[] index is None.' ) return False else: PathLog.debug('Cross-section Wires[] index is {}.'.format(wi)) nWire = Part.Wire(Part.__sortEdges__(workShp.Wires[wi].Edges)) fcShp = Part.Face(nWire) fcShp.translate(FreeCAD.Vector(0, 0, fdv - workShp.BoundBox.ZMin)) # Eif # verify that wire chosen is not inside the physical model if wi > 0: # and isInterior is False: PathLog.debug( 'Multiple wires in cut area. First choice is not 0. Testing.') testArea = fcShp.cut(base.Shape) isReady = self._checkTagIntersection(iTAG, eTAG, self.cutSide, testArea) PathLog.debug('isReady {}.'.format(isReady)) if isReady is False: PathLog.debug('Using wire index {}.'.format(wi - 1)) pWire = Part.Wire( Part.__sortEdges__(workShp.Wires[wi - 1].Edges)) pfcShp = Part.Face(pWire) pfcShp.translate( FreeCAD.Vector(0, 0, fdv - workShp.BoundBox.ZMin)) workShp = pfcShp.cut(fcShp) if testArea.Area < minArea: PathLog.debug( 'offset area is less than minArea of {}.'.format(minArea)) PathLog.debug('Using wire index {}.'.format(wi - 1)) pWire = Part.Wire( Part.__sortEdges__(workShp.Wires[wi - 1].Edges)) pfcShp = Part.Face(pWire) pfcShp.translate( FreeCAD.Vector(0, 0, fdv - workShp.BoundBox.ZMin)) workShp = pfcShp.cut(fcShp) # Eif # Add path stops at ends of wire cutShp = workShp.cut(pathStops) return cutShp
def test10(self): """Verify default log levels is NOTICE.""" self.assertEqual(PathLog.getLevel(), PathLog.Level.NOTICE) self.assertEqual(PathLog.getLevel(self.MODULE), PathLog.Level.NOTICE)
def opExecute(self, obj, getsim=False): # pylint: disable=arguments-differ '''opExecute(obj, getsim=False) ... implementation of Path.Area ops. determines the parameters for _buildPathArea(). Do not overwrite, implement areaOpAreaParams(obj, isHole) ... op specific area param dictionary areaOpPathParams(obj, isHole) ... op specific path param dictionary areaOpShapes(obj) ... the shape for path area to process areaOpUseProjection(obj) ... return true if operation can use projection instead.''' PathLog.track() # Instantiate class variables for operation reference self.endVector = None # pylint: disable=attribute-defined-outside-init self.rotateFlag = False # pylint: disable=attribute-defined-outside-init self.leadIn = 2.0 # pylint: disable=attribute-defined-outside-init self.cloneNames = [] # pylint: disable=attribute-defined-outside-init self.guiMsgs = [] # pylint: disable=attribute-defined-outside-init self.stockBB = PathUtils.findParentJob(obj).Stock.Shape.BoundBox # pylint: disable=attribute-defined-outside-init self.useTempJobClones( 'Delete') # Clear temporary group and recreate for temp job clones # Import OpFinalDepth from pre-existing operation for recompute() scenarios if self.defValsSet is True: PathLog.debug("self.defValsSet is True.") if self.initOpStartDepth is not None: if self.initOpStartDepth != obj.OpStartDepth.Value: obj.OpStartDepth.Value = self.initOpStartDepth obj.StartDepth.Value = self.initOpStartDepth if self.initOpFinalDepth is not None: if self.initOpFinalDepth != obj.OpFinalDepth.Value: obj.OpFinalDepth.Value = self.initOpFinalDepth obj.FinalDepth.Value = self.initOpFinalDepth self.defValsSet = False if obj.EnableRotation != 'Off': # Calculate operation heights based upon rotation radii opHeights = self.opDetermineRotationRadii(obj) (self.xRotRad, self.yRotRad, self.zRotRad) = opHeights[0] # pylint: disable=attribute-defined-outside-init (self.clrOfset, self.safOfst) = opHeights[1] # pylint: disable=attribute-defined-outside-init # Set clearnance and safe heights based upon rotation radii if obj.EnableRotation == 'A(x)': strDep = self.xRotRad elif obj.EnableRotation == 'B(y)': strDep = self.yRotRad else: strDep = max(self.xRotRad, self.yRotRad) finDep = -1 * strDep obj.ClearanceHeight.Value = strDep + self.clrOfset obj.SafeHeight.Value = strDep + self.safOfst if self.initWithRotation is False: if obj.FinalDepth.Value == obj.OpFinalDepth.Value: obj.FinalDepth.Value = finDep if obj.StartDepth.Value == obj.OpStartDepth.Value: obj.StartDepth.Value = strDep # Create visual axes when debugging. if PathLog.getLevel(PathLog.thisModule()) == 4: self.visualAxis() else: strDep = obj.StartDepth.Value finDep = obj.FinalDepth.Value # Set axial feed rates based upon horizontal feed rates safeCircum = 2 * math.pi * obj.SafeHeight.Value self.axialFeed = 360 / safeCircum * self.horizFeed # pylint: disable=attribute-defined-outside-init self.axialRapid = 360 / safeCircum * self.horizRapid # pylint: disable=attribute-defined-outside-init # Initiate depthparams and calculate operation heights for rotational operation finish_step = obj.FinishDepth.Value if hasattr(obj, "FinishDepth") else 0.0 self.depthparams = PathUtils.depth_params( # pylint: disable=attribute-defined-outside-init clearance_height=obj.ClearanceHeight.Value, safe_height=obj.SafeHeight.Value, start_depth=obj.StartDepth.Value, step_down=obj.StepDown.Value, z_finish_step=finish_step, final_depth=obj.FinalDepth.Value, user_depths=None) # Set start point if PathOp.FeatureStartPoint & self.opFeatures( obj) and obj.UseStartPoint: start = obj.StartPoint else: start = None aOS = self.areaOpShapes(obj) # pylint: disable=assignment-from-no-return # Adjust tuples length received from other PathWB tools/operations beside PathPocketShape shapes = [] for shp in aOS: if len(shp) == 2: (fc, iH) = shp # fc, iH, sub, angle, axis tup = fc, iH, 'otherOp', 0.0, 'S', obj.StartDepth.Value, obj.FinalDepth.Value shapes.append(tup) else: shapes.append(shp) if len(shapes) > 1: jobs = [{ 'x': s[0].BoundBox.XMax, 'y': s[0].BoundBox.YMax, 'shape': s } for s in shapes] jobs = PathUtils.sort_jobs(jobs, ['x', 'y']) shapes = [j['shape'] for j in jobs] # PathLog.debug("Pre_path depths are Start: {}, and Final: {}".format(obj.StartDepth.Value, obj.FinalDepth.Value)) sims = [] numShapes = len(shapes) # if numShapes == 1: # nextAxis = shapes[0][4] # elif numShapes > 1: # nextAxis = shapes[1][4] # else: # nextAxis = 'L' for ns in range(0, numShapes): (shape, isHole, sub, angle, axis, strDep, finDep) = shapes[ns] # pylint: disable=unused-variable if ns < numShapes - 1: nextAxis = shapes[ns + 1][4] else: nextAxis = 'L' finish_step = obj.FinishDepth.Value if hasattr( obj, "FinishDepth") else 0.0 self.depthparams = PathUtils.depth_params( # pylint: disable=attribute-defined-outside-init clearance_height=obj.ClearanceHeight.Value, safe_height=obj.SafeHeight.Value, start_depth=strDep, # obj.StartDepth.Value, step_down=obj.StepDown.Value, z_finish_step=finish_step, final_depth=finDep, # obj.FinalDepth.Value, user_depths=None) try: (pp, sim) = self._buildPathArea(obj, shape, isHole, start, getsim) except Exception as e: # pylint: disable=broad-except FreeCAD.Console.PrintError(e) FreeCAD.Console.PrintError( "Something unexpected happened. Check project and tool config." ) else: ppCmds = pp.Commands if obj.EnableRotation != 'Off' and self.rotateFlag is True: # Rotate model to index for cut if axis == 'X': axisOfRot = 'A' elif axis == 'Y': axisOfRot = 'B' # Reverse angle temporarily to match model. Error in FreeCAD render of B axis rotations if obj.B_AxisErrorOverride is True: angle = -1 * angle elif axis == 'Z': axisOfRot = 'C' else: axisOfRot = 'A' # Rotate Model to correct angle ppCmds.insert( 0, Path.Command('G1', { axisOfRot: angle, 'F': self.axialFeed })) ppCmds.insert(0, Path.Command('N100', {})) # Raise cutter to safe depth and return index to starting position ppCmds.append(Path.Command('N200', {})) ppCmds.append( Path.Command('G0', { 'Z': obj.SafeHeight.Value, 'F': self.vertRapid })) if axis != nextAxis: ppCmds.append( Path.Command('G0', { axisOfRot: 0.0, 'F': self.axialRapid })) # Eif # Save gcode commands to object command list self.commandlist.extend(ppCmds) sims.append(sim) # Eif if self.areaOpRetractTool(obj): self.endVector = None # pylint: disable=attribute-defined-outside-init # Raise cutter to safe height and rotate back to original orientation if self.rotateFlag is True: self.commandlist.append( Path.Command('G0', { 'Z': obj.SafeHeight.Value, 'F': self.vertRapid })) self.commandlist.append( Path.Command('G0', { 'A': 0.0, 'F': self.axialRapid })) self.commandlist.append( Path.Command('G0', { 'B': 0.0, 'F': self.axialRapid })) self.useTempJobClones( 'Delete') # Delete temp job clone group and contents self.guiMessage('title', None, show=True) # Process GUI messages to user PathLog.debug("obj.Name: " + str(obj.Name) + "\n\n") return sims
def execute(self, obj, getsim=False): PathLog.track() commandlist = [] simlist = [] commandlist.append(Path.Command("(" + obj.Label + ")")) if not obj.Active: path = Path.Path("(inactive operation)") obj.Path = path obj.ViewObject.Visibility = False return parentJob = PathUtils.findParentJob(obj) if parentJob is None: return baseobject = parentJob.Base if baseobject is None: return self.depthparams = depth_params( clearance_height=obj.ClearanceHeight.Value, safe_height=obj.SafeHeight.Value, start_depth=obj.StartDepth.Value, step_down=obj.StepDown.Value, z_finish_step=0.0, final_depth=obj.FinalDepth.Value, user_depths=None) toolLoad = obj.ToolController if toolLoad is None or toolLoad.ToolNumber == 0: FreeCAD.Console.PrintError( "No Tool Controller is selected. We need a tool to build a Path." ) else: self.vertFeed = toolLoad.VertFeed.Value self.horizFeed = toolLoad.HorizFeed.Value self.vertRapid = toolLoad.VertRapid.Value self.horizRapid = toolLoad.HorizRapid.Value tool = toolLoad.Proxy.getTool(toolLoad) if not tool or tool.Diameter == 0: FreeCAD.Console.PrintError( "No Tool found or diameter is zero. We need a tool to build a Path." ) return else: self.radius = tool.Diameter / 2 if obj.Base: PathLog.debug("base items exist. Processing...") for b in obj.Base: PathLog.debug("Base item: {}".format(b)) for sub in b[1]: if "Face" in sub: shape = Part.makeCompound([getattr(b[0].Shape, sub)]) #shape = getattr(b[0].Shape, sub) else: edges = [getattr(b[0].Shape, sub) for sub in b[1]] shape = Part.makeFace(edges, 'Part::FaceMakerSimple') env = PathUtils.getEnvelope(baseobject.Shape, subshape=shape, depthparams=self.depthparams) removal = env.cut(baseobject.Shape) if PathLog.getLevel( PathLog.thisModule()) == PathLog.Level.DEBUG: removalshape = FreeCAD.ActiveDocument.addObject( "Part::Feature", "removalshape") removalshape.Shape = removal try: (pp, sim) = self._buildPathArea(obj, removal, getsim=getsim) if sim is not None: simlist.append(sim) commandlist.extend(pp.Commands) except Exception as e: FreeCAD.Console.PrintError(e) FreeCAD.Console.PrintError( "Something unexpected happened. Unable to generate a pocket path. Check project and tool config." ) else: # process the job base object as a whole PathLog.debug("processing the whole job base object") env = PathUtils.getEnvelope(baseobject.Shape, subshape=None, depthparams=self.depthparams) removal = env.cut(baseobject.Shape) if PathLog.getLevel(PathLog.thisModule()) == PathLog.Level.DEBUG: removalshape = FreeCAD.ActiveDocument.addObject( "Part::Feature", "removalshape") removalshape.Shape = removal try: (pp, sim) = self._buildPathArea(obj, removal, getsim=getsim) commandlist.extend(pp.Commands) if sim is not None: simlist.append(sim) #commandlist.extend(self._buildPathArea(obj, env.cut(baseobject.Shape)).Commands) except Exception as e: FreeCAD.Console.PrintError(e) FreeCAD.Console.PrintError( "Something unexpected happened. Unable to generate a pocket path. Check project and tool config." ) # Let's finish by rapid to clearance...just for safety commandlist.append(Path.Command("G0", {"Z": obj.ClearanceHeight.Value})) path = Path.Path(commandlist) obj.Path = path obj.ViewObject.Visibility = True PathLog.debug(simlist) simshape = None if len(simlist) > 1: simshape = simlist[0].fuse(simlist[1:]) elif len(simlist) == 1: simshape = simlist[0] if simshape is not None and PathLog.getLevel( PathLog.thisModule()) == PathLog.Level.DEBUG: sim = FreeCAD.ActiveDocument.addObject("Part::Feature", "simshape") sim.Shape = simshape return simshape
def opExecute(self, obj): '''opExecute(obj) ... processes all Base features and Locations and collects them in a list of positions and radii which is then passed to circularHoleExecute(obj, holes). If no Base geometries and no Locations are present, the job's Base is inspected and all drillable features are added to Base. In this case appropriate values for depths are also calculated and assigned. Do not overwrite, implement circularHoleExecute(obj, holes) instead.''' PathLog.track() holes = [] baseSubsTuples = [] subCount = 0 allTuples = [] self.cloneNames = [] # pylint: disable=attribute-defined-outside-init self.guiMsgs = [] # pylint: disable=attribute-defined-outside-init self.rotateFlag = False # pylint: disable=attribute-defined-outside-init self.useTempJobClones('Delete') # pylint: disable=attribute-defined-outside-init self.stockBB = PathUtils.findParentJob(obj).Stock.Shape.BoundBox # pylint: disable=attribute-defined-outside-init self.clearHeight = obj.ClearanceHeight.Value # pylint: disable=attribute-defined-outside-init self.safeHeight = obj.SafeHeight.Value # pylint: disable=attribute-defined-outside-init self.axialFeed = 0.0 # pylint: disable=attribute-defined-outside-init self.axialRapid = 0.0 # pylint: disable=attribute-defined-outside-init trgtDep = None def haveLocations(self, obj): if PathOp.FeatureLocations & self.opFeatures(obj): return len(obj.Locations) != 0 return False if obj.EnableRotation == 'Off': strDep = obj.StartDepth.Value finDep = obj.FinalDepth.Value else: # Calculate operation heights based upon rotation radii opHeights = self.opDetermineRotationRadii(obj) (self.xRotRad, self.yRotRad, self.zRotRad) = opHeights[0] # pylint: disable=attribute-defined-outside-init (clrOfset, safOfst) = opHeights[1] PathLog.debug("Exec. opHeights[0]: " + str(opHeights[0])) PathLog.debug("Exec. opHeights[1]: " + str(opHeights[1])) # Set clearance and safe heights based upon rotation radii if obj.EnableRotation == 'A(x)': strDep = self.xRotRad elif obj.EnableRotation == 'B(y)': strDep = self.yRotRad else: strDep = max(self.xRotRad, self.yRotRad) finDep = -1 * strDep obj.ClearanceHeight.Value = strDep + clrOfset obj.SafeHeight.Value = strDep + safOfst # Create visual axes when debugging. if PathLog.getLevel(PathLog.thisModule()) == 4: self.visualAxis() # Set axial feed rates based upon horizontal feed rates safeCircum = 2 * math.pi * obj.SafeHeight.Value self.axialFeed = 360 / safeCircum * self.horizFeed # pylint: disable=attribute-defined-outside-init self.axialRapid = 360 / safeCircum * self.horizRapid # pylint: disable=attribute-defined-outside-init # Complete rotational analysis and temp clone creation as needed if obj.EnableRotation == 'Off': PathLog.debug("Enable Rotation setting is 'Off' for {}.".format( obj.Name)) stock = PathUtils.findParentJob(obj).Stock for (base, subList) in obj.Base: baseSubsTuples.append((base, subList, 0.0, 'A', stock)) else: for p in range(0, len(obj.Base)): (base, subsList) = obj.Base[p] for sub in subsList: if self.isHoleEnabled(obj, base, sub): shape = getattr(base.Shape, sub) rtn = False (norm, surf) = self.getFaceNormAndSurf(shape) (rtn, angle, axis, praInfo) = self.faceRotationAnalysis(obj, norm, surf) # pylint: disable=unused-variable if rtn is True: (clnBase, angle, clnStock, tag) = self.applyRotationalAnalysis( obj, base, angle, axis, subCount) # Verify faces are correctly oriented - InverseAngle might be necessary PathLog.debug( "Verifying {} orientation: running faceRotationAnalysis() again." .format(sub)) faceIA = getattr(clnBase.Shape, sub) (norm, surf) = self.getFaceNormAndSurf(faceIA) (rtn, praAngle, praAxis, praInfo) = self.faceRotationAnalysis( obj, norm, surf) # pylint: disable=unused-variable if rtn is True: msg = obj.Name + ":: " msg += translate( "Path", "{} might be misaligned after initial rotation." .format(sub)) + " " if obj.AttemptInverseAngle is True and obj.InverseAngle is False: (clnBase, clnStock, angle) = self.applyInverseAngle( obj, clnBase, clnStock, axis, angle) msg += translate( "Path", "Rotated to 'InverseAngle' to attempt access." ) else: if len(subsList) == 1: msg += translate( "Path", "Consider toggling the 'InverseAngle' property and recomputing." ) else: msg += translate( "Path", "Consider transferring '{}' to independent operation." .format(sub)) PathLog.warning(msg) # title = translate("Path", 'Rotation Warning') # self.guiMessage(title, msg, False) else: PathLog.debug( "Face appears to be oriented correctly.") cmnt = "{}: {} @ {}; ".format( sub, axis, str(round(angle, 5))) if cmnt not in obj.Comment: obj.Comment += cmnt tup = clnBase, sub, tag, angle, axis, clnStock allTuples.append(tup) else: if self.warnDisabledAxis(obj, axis, sub) is True: pass # Skip drill feature due to access issue else: PathLog.debug(str(sub) + ": No rotation used") axis = 'X' angle = 0.0 tag = base.Name + '_' + axis + str( angle).replace('.', '_') stock = PathUtils.findParentJob(obj).Stock tup = base, sub, tag, angle, axis, stock allTuples.append(tup) # Eif # Eif subCount += 1 # Efor # Efor (Tags, Grps) = self.sortTuplesByIndex(allTuples, 2) # return (TagList, GroupList) subList = [] for o in range(0, len(Tags)): PathLog.debug('hTag: {}'.format(Tags[o])) subList = [] for (base, sub, tag, angle, axis, stock) in Grps[o]: subList.append(sub) pair = base, subList, angle, axis, stock baseSubsTuples.append(pair) # Efor for base, subs, angle, axis, stock in baseSubsTuples: for sub in subs: if self.isHoleEnabled(obj, base, sub): pos = self.holePosition(obj, base, sub) if pos: # Default is treat selection as 'Face' shape finDep = base.Shape.getElement(sub).BoundBox.ZMin if base.Shape.getElement(sub).ShapeType == 'Edge': msg = translate( "Path", "Verify Final Depth of holes based on edges. {} depth is: {} mm" .format(sub, round(finDep, 4))) + " " msg += translate( "Path", "Always select the bottom edge of the hole when using an edge." ) PathLog.warning(msg) # If user has not adjusted Final Depth value, attempt to determine from sub trgtDep = obj.FinalDepth.Value if obj.OpFinalDepth.Value == obj.FinalDepth.Value: trgtDep = finDep if obj.FinalDepth.Value < finDep: msg = translate( "Path", "Final Depth setting is below the hole bottom for {}." .format(sub)) PathLog.warning(msg) holes.append({ 'x': pos.x, 'y': pos.y, 'r': self.holeDiameter(obj, base, sub), 'angle': angle, 'axis': axis, 'trgtDep': trgtDep, 'stkTop': stock.Shape.BoundBox.ZMax }) if haveLocations(self, obj): for location in obj.Locations: # holes.append({'x': location.x, 'y': location.y, 'r': 0, 'angle': 0.0, 'axis': 'X', 'finDep': obj.FinalDepth.Value}) trgtDep = obj.FinalDepth.Value holes.append({ 'x': location.x, 'y': location.y, 'r': 0, 'angle': 0.0, 'axis': 'X', 'trgtDep': trgtDep, 'stkTop': PathUtils.findParentJob(obj).stock.Shape.BoundBox.ZMax }) # If all holes based upon edges, set post-operation Final Depth to highest edge height if obj.OpFinalDepth.Value == obj.FinalDepth.Value: if len(holes) == 1: PathLog.info( translate( 'Path', "Single-hole operation. Saving Final Depth determined from hole base." )) obj.FinalDepth.Value = trgtDep if len(holes) > 0: self.circularHoleExecute( obj, holes) # circularHoleExecute() located in PathDrilling.py self.useTempJobClones( 'Delete') # Delete temp job clone group and contents self.guiMessage('title', None, show=True) # Process GUI messages to user PathLog.debug("obj.Name: " + str(obj.Name))
def opExecute(self, obj): '''opExecute(obj) ... processes all Base features and Locations and collects them in a list of positions and radii which is then passed to circularHoleExecute(obj, holes). If no Base geometries and no Locations are present, the job's Base is inspected and all drillable features are added to Base. In this case appropriate values for depths are also calculated and assigned. Do not overwrite, implement circularHoleExecute(obj, holes) instead.''' PathLog.track() holes = [] baseSubsTuples = [] subCount = 0 allTuples = [] self.cloneNames = [] # pylint: disable=attribute-defined-outside-init self.guiMsgs = [] # pylint: disable=attribute-defined-outside-init self.rotateFlag = False # pylint: disable=attribute-defined-outside-init self.useTempJobClones('Delete') # pylint: disable=attribute-defined-outside-init self.stockBB = PathUtils.findParentJob(obj).Stock.Shape.BoundBox # pylint: disable=attribute-defined-outside-init self.clearHeight = obj.ClearanceHeight.Value # pylint: disable=attribute-defined-outside-init self.safeHeight = obj.SafeHeight.Value # pylint: disable=attribute-defined-outside-init self.axialFeed = 0.0 # pylint: disable=attribute-defined-outside-init self.axialRapid = 0.0 # pylint: disable=attribute-defined-outside-init def haveLocations(self, obj): if PathOp.FeatureLocations & self.opFeatures(obj): return len(obj.Locations) != 0 return False if obj.EnableRotation == 'Off': strDep = obj.StartDepth.Value finDep = obj.FinalDepth.Value else: # Calculate operation heights based upon rotation radii opHeights = self.opDetermineRotationRadii(obj) (self.xRotRad, self.yRotRad, self.zRotRad) = opHeights[0] # pylint: disable=attribute-defined-outside-init (clrOfset, safOfst) = opHeights[1] PathLog.debug("Exec. opHeights[0]: " + str(opHeights[0])) PathLog.debug("Exec. opHeights[1]: " + str(opHeights[1])) # Set clearance and safe heights based upon rotation radii if obj.EnableRotation == 'A(x)': strDep = self.xRotRad elif obj.EnableRotation == 'B(y)': strDep = self.yRotRad else: strDep = max(self.xRotRad, self.yRotRad) finDep = -1 * strDep obj.ClearanceHeight.Value = strDep + clrOfset obj.SafeHeight.Value = strDep + safOfst # Create visual axes when debugging. if PathLog.getLevel(PathLog.thisModule()) == 4: self.visualAxis() # Set axial feed rates based upon horizontal feed rates safeCircum = 2 * math.pi * obj.SafeHeight.Value self.axialFeed = 360 / safeCircum * self.horizFeed # pylint: disable=attribute-defined-outside-init self.axialRapid = 360 / safeCircum * self.horizRapid # pylint: disable=attribute-defined-outside-init # Complete rotational analysis and temp clone creation as needed if obj.EnableRotation == 'Off': PathLog.debug("Enable Rotation setting is 'Off' for {}.".format( obj.Name)) stock = PathUtils.findParentJob(obj).Stock for (base, subList) in obj.Base: baseSubsTuples.append((base, subList, 0.0, 'A', stock)) else: for p in range(0, len(obj.Base)): (bst, at) = self.process_base_geometry_with_rotation( obj, p, subCount) allTuples.extend(at) baseSubsTuples.extend(bst) for base, subs, angle, axis, stock in baseSubsTuples: # rotate shorter angle in opposite direction if angle > 180: angle -= 360 elif angle < -180: angle += 360 # Re-analyze rotated model for drillable holes if obj.EnableRotation != 'Off': rotated_features = self.findHoles(obj, base) for sub in subs: PathLog.debug('sub, angle, axis: {}, {}, {}'.format( sub, angle, axis)) if self.isHoleEnabled(obj, base, sub): pos = self.holePosition(obj, base, sub) if pos: # Identify face to which edge belongs sub_shape = base.Shape.getElement(sub) # Default is to treat selection as 'Face' shape holeBtm = sub_shape.BoundBox.ZMin if obj.EnableRotation != 'Off': # Update Start and Final depths due to rotation, if auto defaults are active parent_face = self._find_parent_face_of_edge( rotated_features, sub_shape) if parent_face: PathLog.debug('parent_face found') holeBtm = parent_face.BoundBox.ZMin if obj.OpStartDepth == obj.StartDepth: obj.StartDepth.Value = parent_face.BoundBox.ZMax PathLog.debug('new StartDepth: {}'.format( obj.StartDepth.Value)) if obj.OpFinalDepth == obj.FinalDepth: obj.FinalDepth.Value = holeBtm PathLog.debug( 'new FinalDepth: {}'.format(holeBtm)) else: PathLog.debug('NO parent_face identified') if base.Shape.getElement(sub).ShapeType == 'Edge': msg = translate( "Path", "Verify Final Depth of holes based on edges. {} depth is: {} mm" .format(sub, round(holeBtm, 4))) + " " msg += translate( "Path", "Always select the bottom edge of the hole when using an edge." ) PathLog.warning(msg) # Warn user if Final Depth set lower than bottom of hole if obj.FinalDepth.Value < holeBtm: msg = translate( "Path", "Final Depth setting is below the hole bottom for {}." .format(sub)) + ' ' msg += translate( "Path", "{} depth is calculated at {} mm".format( sub, round(holeBtm, 4))) PathLog.warning(msg) holes.append({ 'x': pos.x, 'y': pos.y, 'r': self.holeDiameter(obj, base, sub), 'angle': angle, 'axis': axis, 'trgtDep': obj.FinalDepth.Value, 'stkTop': stock.Shape.BoundBox.ZMax }) # haveLocations are populated from user-provided (x, y) coordinates # provided by the user in the Base Locations tab of the Task Editor window if haveLocations(self, obj): for location in obj.Locations: # holes.append({'x': location.x, 'y': location.y, 'r': 0, 'angle': 0.0, 'axis': 'X', 'holeBtm': obj.FinalDepth.Value}) holes.append({ 'x': location.x, 'y': location.y, 'r': 0, 'angle': 0.0, 'axis': 'X', 'trgtDep': obj.FinalDepth.Value, 'stkTop': PathUtils.findParentJob(obj).Stock.Shape.BoundBox.ZMax }) if len(holes) > 0: self.circularHoleExecute( obj, holes) # circularHoleExecute() located in PathDrilling.py self.useTempJobClones( 'Delete') # Delete temp job clone group and contents self.guiMessage('title', None, show=True) # Process GUI messages to user PathLog.debug("obj.Name: " + str(obj.Name))
def test12(self): """Verify setting module log level.""" PathLog.setLevel(PathLog.Level.DEBUG, self.MODULE) self.assertEqual(PathLog.getLevel(), PathLog.Level.NOTICE) self.assertEqual(PathLog.getLevel(self.MODULE), PathLog.Level.DEBUG)