def areaOpShapes(self, obj): '''areaOpShapes(obj) ... return top face''' # Facing is done either against base objects if obj.Base: PathLog.debug("obj.Base: {}".format(obj.Base)) faces = [] for b in obj.Base: for sub in b[1]: shape = getattr(b[0].Shape, sub) if isinstance(shape, Part.Face): faces.append(shape) else: PathLog.debug('The base subobject is not a face') return planeshape = Part.makeCompound(faces) PathLog.debug("Working on a collection of faces {}".format(faces)) # If no base object, do planing of top surface of entire model else: planeshape = self.baseobject.Shape PathLog.debug("Working on a shape {}".format(self.baseobject.Name)) # Find the correct shape depending on Boundary shape. PathLog.debug("Boundary Shape: {}".format(obj.BoundaryShape)) bb = planeshape.BoundBox if obj.BoundaryShape == 'Boundbox': bbperim = Part.makeBox(bb.XLength, bb.YLength, 1, FreeCAD.Vector(bb.XMin, bb.YMin, bb.ZMin), FreeCAD.Vector(0, 0, 1)) env = PathUtils.getEnvelope(partshape=bbperim, depthparams=self.depthparams) elif obj.BoundaryShape == 'Stock': stock = PathUtils.findParentJob(obj).Stock.Shape env = stock else: env = PathUtils.getEnvelope(partshape=planeshape, depthparams=self.depthparams) return [(env, False)]
def areaOpShapes(self, obj): '''areaOpShapes(obj) ... return shapes representing the solids to be removed.''' PathLog.track() if obj.Base: PathLog.debug("base items exist. Processing...") removalshapes = [] 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)]) else: edges = [getattr(b[0].Shape, sub) for sub in b[1]] shape = Part.makeFace(edges, 'Part::FaceMakerSimple') env = PathUtils.getEnvelope(self.baseobject.Shape, subshape=shape, depthparams=self.depthparams) obj.removalshape = env.cut(self.baseobject.Shape) removalshapes.append((obj.removalshape, False)) else: # process the job base object as a whole PathLog.debug("processing the whole job base object") env = PathUtils.getEnvelope(self.baseobject.Shape, subshape=None, depthparams=self.depthparams) obj.removalshape = env.cut(self.baseobject.Shape) removalshapes = [(obj.removalshape, False)] return removalshapes
def areaOpShapes(self, obj): '''areaOpShapes(obj) ... return top face''' # Facing is done either against base objects if obj.Base: PathLog.debug("obj.Base: {}".format(obj.Base)) faces = [] for b in obj.Base: for sub in b[1]: shape = getattr(b[0].Shape, sub) if isinstance(shape, Part.Face): faces.append(shape) else: PathLog.debug('The base subobject is not a face') return planeshape = Part.makeCompound(faces) PathLog.debug("Working on a collection of faces {}".format(faces)) # If no base object, do planing of top surface of entire model else: planeshape = Part.makeCompound([base.Shape for base in self.model]) PathLog.debug("Working on a shape {}".format(obj.Label)) # Find the correct shape depending on Boundary shape. PathLog.debug("Boundary Shape: {}".format(obj.BoundaryShape)) bb = planeshape.BoundBox if obj.BoundaryShape == 'Boundbox': bbperim = Part.makeBox(bb.XLength, bb.YLength, 1, FreeCAD.Vector(bb.XMin, bb.YMin, bb.ZMin), FreeCAD.Vector(0, 0, 1)) env = PathUtils.getEnvelope(partshape=bbperim, depthparams=self.depthparams) elif obj.BoundaryShape == 'Stock': stock = PathUtils.findParentJob(obj).Stock.Shape env = stock else: env = PathUtils.getEnvelope(partshape=planeshape, depthparams=self.depthparams) return [(env, False)]
def areaOpShapes(self, obj): '''areaOpShapes(obj) ... returns envelope for all base shapes or wires for Arch.Panels.''' if obj.UseComp: self.commandlist.append(Path.Command("(Compensated Tool Path. Diameter: " + str(self.radius * 2) + ")")) else: self.commandlist.append(Path.Command("(Uncompensated Tool Path)")) shapes = [] if obj.Base: # The user has selected subobjects from the base. Process each. holes = [] faces = [] for b in obj.Base: for sub in b[1]: shape = getattr(b[0].Shape, sub) if isinstance(shape, Part.Face): faces.append(shape) if numpy.isclose(abs(shape.normalAt(0, 0).z), 1): # horizontal face holes += shape.Wires[1:] else: FreeCAD.Console.PrintWarning("found a base object which is not a face. Can't continue.") return for wire in holes: f = Part.makeFace(wire, 'Part::FaceMakerSimple') drillable = PathUtils.isDrillable(self.baseobject.Shape, wire) if (drillable and obj.processCircles) or (not drillable and obj.processHoles): env = PathUtils.getEnvelope(self.baseobject.Shape, subshape=f, depthparams=self.depthparams) shapes.append((env, True)) if len(faces) > 0: profileshape = Part.makeCompound(faces) if obj.processPerimeter: env = PathUtils.getEnvelope(self.baseobject.Shape, subshape=profileshape, depthparams=self.depthparams) shapes.append((env, False)) else: # Try to build targets from the job base if hasattr(self.baseobject, "Proxy"): if isinstance(self.baseobject.Proxy, ArchPanel.PanelSheet): # process the sheet if obj.processCircles or obj.processHoles: for shape in self.baseobject.Proxy.getHoles(self.baseobject, transform=True): for wire in shape.Wires: drillable = PathUtils.isDrillable(self.baseobject.Proxy, wire) if (drillable and obj.processCircles) or (not drillable and obj.processHoles): f = Part.makeFace(wire, 'Part::FaceMakerSimple') env = PathUtils.getEnvelope(self.baseobject.Shape, subshape=f, depthparams=self.depthparams) shapes.append((env, True)) if obj.processPerimeter: for shape in self.baseobject.Proxy.getOutlines(self.baseobject, transform=True): for wire in shape.Wires: f = Part.makeFace(wire, 'Part::FaceMakerSimple') env = PathUtils.getEnvelope(self.baseobject.Shape, subshape=f, depthparams=self.depthparams) shapes.append((env, False)) PathLog.debug("%d shapes" % len(shapes)) return shapes
def areaOpShapes(self, obj): '''areaOpShapes(obj) ... return shapes representing the solids to be removed.''' PathLog.track() removalshapes = [] if obj.Base: PathLog.debug("base items exist. Processing...") for base in obj.Base: PathLog.debug("Base item: {}".format(base)) # Check if all subs are faces allFaceSubs = True Faces = [] for sub in base[1]: if "Face" in sub: Faces.append(getattr(base[0].Shape, sub)) else: allFaceSubs = False break if allFaceSubs is True and obj.HandleMultipleFeatures == 'Collectively': shape = Part.makeCompound(Faces) env = PathUtils.getEnvelope(base[0].Shape, subshape=shape, depthparams=self.depthparams) obj.removalshape = env.cut(base[0].Shape) obj.removalshape.tessellate(0.1) removalshapes.append((obj.removalshape, False)) else: for sub in base[1]: if "Face" in sub: shape = Part.makeCompound( [getattr(base[0].Shape, sub)]) else: edges = [ getattr(base[0].Shape, sub) for sub in base[1] ] shape = Part.makeFace(edges, 'Part::FaceMakerSimple') env = PathUtils.getEnvelope( base[0].Shape, subshape=shape, depthparams=self.depthparams) obj.removalshape = env.cut(base[0].Shape) obj.removalshape.tessellate(0.1) removalshapes.append((obj.removalshape, False)) else: # process the job base object as a whole PathLog.debug("processing the whole job base object") for base in self.model: env = PathUtils.getEnvelope(base.Shape, subshape=None, depthparams=self.depthparams) obj.removalshape = env.cut(base.Shape) obj.removalshape.tessellate(0.1) removalshapes.append((obj.removalshape, False)) return removalshapes
def areaOpShapes(self, obj): '''areaOpShapes(obj) ... return envelope over the job's Base.Shape or all Arch.Panel shapes.''' if obj.UseComp: self.commandlist.append( Path.Command("(Compensated Tool Path. Diameter: " + str(self.radius * 2) + ")")) else: self.commandlist.append(Path.Command("(Uncompensated Tool Path)")) isPanel = False if 1 == len(self.model) and hasattr(self.model[0], "Proxy"): if isinstance(self.model[0].Proxy, ArchPanel.PanelSheet): # process the sheet panel = self.model[0] isPanel = True panel.Proxy.execute(panel) shapes = panel.Proxy.getOutlines(panel, transform=True) for shape in shapes: f = Part.makeFace([shape], 'Part::FaceMakerSimple') thickness = panel.Group[0].Source.Thickness return [(f.extrude(FreeCAD.Vector(0, 0, thickness)), False)] if not isPanel: return [(PathUtils.getEnvelope(partshape=base.Shape, subshape=None, depthparams=self.depthparams), False) for base in self.model if hasattr(base, 'Shape')]
def areaOpShapes(self, obj): '''areaOpShapes(obj) ... returns envelope for all wires formed by the base edges.''' PathLog.track() if obj.UseComp: self.commandlist.append( Path.Command("(Compensated Tool Path. Diameter: " + str(self.radius * 2) + ")")) else: self.commandlist.append(Path.Command("(Uncompensated Tool Path)")) shapes = [] if obj.Base: wires = [] for b in obj.Base: edgelist = [] for sub in b[1]: edgelist.append(getattr(b[0].Shape, sub)) wires.extend(findWires(edgelist)) for wire in wires: f = Part.makeFace(wire, 'Part::FaceMakerSimple') # shift the compound to the bottom of the base object for # proper sectioning zShift = b[0].Shape.BoundBox.ZMin - f.BoundBox.ZMin newPlace = FreeCAD.Placement(FreeCAD.Vector(0, 0, zShift), f.Placement.Rotation) f.Placement = newPlace env = PathUtils.getEnvelope(self.baseobject.Shape, subshape=f, depthparams=self.depthparams) shapes.append((env, False)) return shapes
def areaOpShapes(self, obj): '''areaOpShapes(obj) ... returns envelope for all wires formed by the base edges.''' PathLog.track() if obj.UseComp: self.commandlist.append(Path.Command("(Compensated Tool Path. Diameter: " + str(self.radius * 2) + ")")) else: self.commandlist.append(Path.Command("(Uncompensated Tool Path)")) shapes = [] if obj.Base: basewires = [] for b in obj.Base: edgelist = [] for sub in b[1]: edgelist.append(getattr(b[0].Shape, sub)) basewires.append((b[0], findWires(edgelist))) for base,wires in basewires: for wire in wires: f = Part.makeFace(wire, 'Part::FaceMakerSimple') # shift the compound to the bottom of the base object for # proper sectioning zShift = b[0].Shape.BoundBox.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)) return shapes
def areaOpShapes(self, obj): '''areaOpShapes(obj) ... return top face''' # Facing is done either against base objects if obj.Base: PathLog.debug("obj.Base: {}".format(obj.Base)) faces = [] for b in obj.Base: for sub in b[1]: shape = getattr(b[0].Shape, sub) if isinstance(shape, Part.Face): faces.append(shape) else: PathLog.debug('The base subobject is not a face') return planeshape = Part.makeCompound(faces) PathLog.debug("Working on a collection of faces {}".format(faces)) # If no base object, do planing of top surface of entire model else: planeshape = self.baseobject.Shape PathLog.debug("Working on a shape {}".format(self.baseobject.Name)) # if user wants the boundbox, calculate that PathLog.debug("Boundary Shape: {}".format(obj.BoundaryShape)) bb = planeshape.BoundBox if obj.BoundaryShape == 'Boundbox': bbperim = Part.makeBox(bb.XLength, bb.YLength, 1, FreeCAD.Vector(bb.XMin, bb.YMin, bb.ZMin), FreeCAD.Vector(0, 0, 1)) env = PathUtils.getEnvelope(partshape=bbperim, depthparams=self.depthparams) else: env = PathUtils.getEnvelope(partshape=planeshape, depthparams=self.depthparams) return [(env, False)]
def areaOpShapes(self, obj): '''areaOpShapes(obj) ... return envelope over the job's Base.Shape or all Arch.Panel shapes.''' if obj.UseComp: self.commandlist.append(Path.Command("(Compensated Tool Path. Diameter: " + str(self.radius * 2) + ")")) else: self.commandlist.append(Path.Command("(Uncompensated Tool Path)")) isPanel = False if hasattr(self.baseobject, "Proxy"): if isinstance(self.baseobject.Proxy, ArchPanel.PanelSheet): # process the sheet isPanel = True self.baseobject.Proxy.execute(self.baseobject) shapes = self.baseobject.Proxy.getOutlines(self.baseobject, transform=True) for shape in shapes: f = Part.makeFace([shape], 'Part::FaceMakerSimple') thickness = self.baseobject.Group[0].Source.Thickness return [(f.extrude(FreeCAD.Vector(0, 0, thickness)), False)] if hasattr(self.baseobject, "Shape") and not isPanel: return [(PathUtils.getEnvelope(partshape=self.baseobject.Shape, subshape=None, depthparams=self.depthparams), False)]
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 execute(self, obj): PathLog.track() if not obj.Active: path = Path.Path("(inactive operation)") obj.Path = path obj.ViewObject.Visibility = False return commandlist = [] toolLoad = obj.ToolController self.depthparams = depth_params( clearance_height=obj.ClearanceHeight.Value, safe_height=obj.SafeHeight.Value, start_depth=obj.StartDepth.Value, step_down=obj.StepDown, z_finish_step=obj.FinishDepth.Value, final_depth=obj.FinalDepth.Value, user_depths=None) if toolLoad is None or toolLoad.ToolNumber == 0: FreeCAD.Console.PrintError( "No Tool Controller is selected. We need a tool to build a Path." ) return 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 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 commandlist.append(Path.Command("(" + obj.Label + ")")) # Facing is done either against base objects if obj.Base: PathLog.debug("obj.Base: {}".format(obj.Base)) faces = [] for b in obj.Base: for sub in b[1]: shape = getattr(b[0].Shape, sub) if isinstance(shape, Part.Face): faces.append(shape) else: PathLog.debug('The base subobject is not a face') return planeshape = Part.makeCompound(faces) PathLog.info("Working on a collection of faces {}".format(faces)) # If no base object, do planing of top surface of entire model else: parentJob = PathUtils.findParentJob(obj) if parentJob is None: PathLog.debug("No base object. No parent job found") return baseobject = parentJob.Base if baseobject is None: PathLog.debug("Parent job exists but no Base Object") return planeshape = baseobject.Shape PathLog.info("Working on a shape {}".format(baseobject.Name)) # Let's start by rapid to clearance...just for safety commandlist.append(Path.Command("G0", {"Z": obj.ClearanceHeight.Value})) # if user wants the boundbox, calculate that PathLog.info("Boundary Shape: {}".format(obj.BoundaryShape)) bb = planeshape.BoundBox if obj.BoundaryShape == 'Boundbox': bbperim = Part.makeBox(bb.XLength, bb.YLength, 1, FreeCAD.Vector(bb.XMin, bb.YMin, bb.ZMin), FreeCAD.Vector(0, 0, 1)) env = PathUtils.getEnvelope(partshape=bbperim, depthparams=self.depthparams) else: env = PathUtils.getEnvelope(partshape=planeshape, depthparams=self.depthparams) try: commandlist.extend(self._buildPathArea(obj, env).Commands) except Exception as e: FreeCAD.Console.PrintError(e) FreeCAD.Console.PrintError( translate( "Path_MillFace", "The selected settings did not produce a valid path.\n")) # 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
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 execute(self, obj, getsim=False): commandlist = [] sim = None 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 commandlist.append(Path.Command("(" + obj.Label + ")")) if obj.UseComp: commandlist.append( Path.Command("(Compensated Tool Path. Diameter: " + str(self.radius * 2) + ")")) else: commandlist.append(Path.Command("(Uncompensated Tool Path)")) if obj.Base: wires = [] for b in obj.Base: edgelist = [] for sub in b[1]: edgelist.append(getattr(b[0].Shape, sub)) wires.extend(findWires(edgelist)) for wire in wires: f = Part.makeFace(wire, 'Part::FaceMakerSimple') # shift the compound to the bottom of the base object for # proper sectioning zShift = b[0].Shape.BoundBox.ZMin - f.BoundBox.ZMin newPlace = FreeCAD.Placement(FreeCAD.Vector(0, 0, zShift), f.Placement.Rotation) f.Placement = newPlace env = PathUtils.getEnvelope(baseobject.Shape, subshape=f, depthparams=self.depthparams) try: (pp, sim) = self._buildPathArea(obj, baseobject=env, start=obj.StartPoint, getsim=getsim) commandlist.extend(pp.Commands) except Exception as e: FreeCAD.Console.PrintError(e) FreeCAD.Console.PrintError( "Something unexpected happened. Unable to generate a contour 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 return sim
def areaOpShapes(self, obj): """areaOpShapes(obj) ... return shapes representing the solids to be removed.""" PathLog.track() subObjTups = [] removalshapes = [] if obj.Base: PathLog.debug("base items exist. Processing... ") for base in obj.Base: PathLog.debug("obj.Base item: {}".format(base)) # Check if all subs are faces allSubsFaceType = True Faces = [] for sub in base[1]: if "Face" in sub: face = getattr(base[0].Shape, sub) Faces.append(face) subObjTups.append((sub, face)) else: allSubsFaceType = False break if len(Faces) == 0: allSubsFaceType = False if (allSubsFaceType is True and obj.HandleMultipleFeatures == "Collectively"): (fzmin, fzmax) = self.getMinMaxOfFaces(Faces) if obj.FinalDepth.Value < fzmin: PathLog.warning( translate( "PathPocket", "Final depth set below ZMin of face(s) selected.", )) if (obj.AdaptivePocketStart is True or obj.AdaptivePocketFinish is True): pocketTup = self.calculateAdaptivePocket( obj, base, subObjTups) if pocketTup is not False: obj.removalshape = pocketTup[0] removalshapes.append( pocketTup) # (shape, isHole, detail) else: shape = Part.makeCompound(Faces) env = PathUtils.getEnvelope( base[0].Shape, subshape=shape, depthparams=self.depthparams) obj.removalshape = env.cut(base[0].Shape) # obj.removalshape.tessellate(0.1) removalshapes.append( (obj.removalshape, False, "3DPocket")) # (shape, isHole, detail) else: for sub in base[1]: if "Face" in sub: shape = Part.makeCompound( [getattr(base[0].Shape, sub)]) else: edges = [ getattr(base[0].Shape, sub) for sub in base[1] ] shape = Part.makeFace(edges, "Part::FaceMakerSimple") env = PathUtils.getEnvelope( base[0].Shape, subshape=shape, depthparams=self.depthparams) obj.removalshape = env.cut(base[0].Shape) # obj.removalshape.tessellate(0.1) removalshapes.append( (obj.removalshape, False, "3DPocket")) else: # process the job base object as a whole PathLog.debug("processing the whole job base object") for base in self.model: if obj.ProcessStockArea is True: job = PathUtils.findParentJob(obj) stockEnvShape = PathUtils.getEnvelope( job.Stock.Shape, subshape=None, depthparams=self.depthparams) obj.removalshape = stockEnvShape.cut(base.Shape) # obj.removalshape.tessellate(0.1) else: env = PathUtils.getEnvelope(base.Shape, subshape=None, depthparams=self.depthparams) obj.removalshape = env.cut(base.Shape) # obj.removalshape.tessellate(0.1) removalshapes.append((obj.removalshape, False, "3DPocket")) return removalshapes
def areaOpShapes(self, obj): """areaOpShapes(obj) ... return top face""" # Facing is done either against base objects self.removalshapes = [] holeShape = None PathLog.debug("depthparams: {}".format([i for i in self.depthparams])) if obj.Base: PathLog.debug("obj.Base: {}".format(obj.Base)) faces = [] holes = [] holeEnvs = [] oneBase = [obj.Base[0][0], True] sub0 = getattr(obj.Base[0][0].Shape, obj.Base[0][1][0]) minHeight = sub0.BoundBox.ZMax for b in obj.Base: for sub in b[1]: shape = getattr(b[0].Shape, sub) if isinstance(shape, Part.Face): faces.append(shape) if shape.BoundBox.ZMin < minHeight: minHeight = shape.BoundBox.ZMin # Limit to one model base per operation if oneBase[0] is not b[0]: oneBase[1] = False if numpy.isclose(abs(shape.normalAt(0, 0).z), 1): # horizontal face # Analyze internal closed wires to determine if raised or a recess for wire in shape.Wires[1:]: if obj.ExcludeRaisedAreas: ip = self.isPocket(b[0], shape, wire) if ip is False: holes.append((b[0].Shape, wire)) else: holes.append((b[0].Shape, wire)) else: PathLog.warning( 'The base subobject, "{0}," is not a face. Ignoring "{0}."' .format(sub)) if obj.ExcludeRaisedAreas and len(holes) > 0: for shape, wire in holes: f = Part.makeFace(wire, "Part::FaceMakerSimple") env = PathUtils.getEnvelope(shape, subshape=f, depthparams=self.depthparams) holeEnvs.append(env) holeShape = Part.makeCompound(holeEnvs) PathLog.debug("Working on a collection of faces {}".format(faces)) planeshape = Part.makeCompound(faces) # If no base object, do planing of top surface of entire model else: planeshape = Part.makeCompound([base.Shape for base in self.model]) PathLog.debug("Working on a shape {}".format(obj.Label)) # Find the correct shape depending on Boundary shape. PathLog.debug("Boundary Shape: {}".format(obj.BoundaryShape)) bb = planeshape.BoundBox # Apply offset for clearing edges offset = 0 if obj.ClearEdges: offset = self.radius + 0.1 bb.XMin = bb.XMin - offset bb.YMin = bb.YMin - offset bb.XMax = bb.XMax + offset bb.YMax = bb.YMax + offset if obj.BoundaryShape == "Boundbox": bbperim = Part.makeBox( bb.XLength, bb.YLength, 1, FreeCAD.Vector(bb.XMin, bb.YMin, bb.ZMin), FreeCAD.Vector(0, 0, 1), ) env = PathUtils.getEnvelope(partshape=bbperim, depthparams=self.depthparams) if obj.ExcludeRaisedAreas and oneBase[1]: includedFaces = self.getAllIncludedFaces(oneBase[0], env, faceZ=minHeight) if len(includedFaces) > 0: includedShape = Part.makeCompound(includedFaces) includedEnv = PathUtils.getEnvelope( oneBase[0].Shape, subshape=includedShape, depthparams=self.depthparams, ) env = env.cut(includedEnv) elif obj.BoundaryShape == "Stock": stock = PathUtils.findParentJob(obj).Stock.Shape env = stock if obj.ExcludeRaisedAreas and oneBase[1]: includedFaces = self.getAllIncludedFaces(oneBase[0], stock, faceZ=minHeight) if len(includedFaces) > 0: stockEnv = PathUtils.getEnvelope( partshape=stock, depthparams=self.depthparams) includedShape = Part.makeCompound(includedFaces) includedEnv = PathUtils.getEnvelope( oneBase[0].Shape, subshape=includedShape, depthparams=self.depthparams, ) env = stockEnv.cut(includedEnv) elif obj.BoundaryShape == "Perimeter": if obj.ClearEdges: psZMin = planeshape.BoundBox.ZMin ofstShape = PathUtils.getOffsetArea(planeshape, self.radius * 1.25, plane=planeshape) ofstShape.translate( FreeCAD.Vector(0.0, 0.0, psZMin - ofstShape.BoundBox.ZMin)) env = PathUtils.getEnvelope(partshape=ofstShape, depthparams=self.depthparams) else: env = PathUtils.getEnvelope(partshape=planeshape, depthparams=self.depthparams) elif obj.BoundaryShape == "Face Region": baseShape = planeshape # oneBase[0].Shape psZMin = planeshape.BoundBox.ZMin ofst = 0.0 if obj.ClearEdges: ofst = self.tool.Diameter * 0.51 ofstShape = PathUtils.getOffsetArea(planeshape, ofst, plane=planeshape) ofstShape.translate( FreeCAD.Vector(0.0, 0.0, psZMin - ofstShape.BoundBox.ZMin)) # Calculate custom depth params for removal shape envelope, with start and final depth buffers custDepthparams = self._customDepthParams( obj, obj.StartDepth.Value + 0.2, obj.FinalDepth.Value - 0.1) # only an envelope ofstShapeEnv = PathUtils.getEnvelope(partshape=ofstShape, depthparams=custDepthparams) if obj.ExcludeRaisedAreas: env = ofstShapeEnv.cut(baseShape) env.translate(FreeCAD.Vector( 0.0, 0.0, -0.00001)) # lower removal shape into buffer zone else: env = ofstShapeEnv if holeShape: PathLog.debug("Processing holes and face ...") holeEnv = PathUtils.getEnvelope(partshape=holeShape, depthparams=self.depthparams) newEnv = env.cut(holeEnv) tup = newEnv, False, "pathMillFace" else: PathLog.debug("Processing solid face ...") tup = env, False, "pathMillFace" self.removalshapes.append(tup) obj.removalshape = self.removalshapes[0][0] # save removal shape return self.removalshapes
def areaOpShapes(self, obj): '''areaOpShapes(obj) ... return shapes representing the solids to be removed.''' PathLog.track() subObjTups = [] removalshapes = [] if obj.Base: PathLog.debug("base items exist. Processing... ") for base in obj.Base: PathLog.debug("obj.Base item: {}".format(base)) # Check if all subs are faces allSubsFaceType = True Faces = [] for sub in base[1]: if "Face" in sub: face = getattr(base[0].Shape, sub) Faces.append(face) subObjTups.append((sub, face)) else: allSubsFaceType = False break if len(Faces) == 0: allSubsFaceType = False if allSubsFaceType is True and obj.HandleMultipleFeatures == 'Collectively': if obj.OpFinalDepth == obj.FinalDepth: (fzmin, fzmax) = self.getMinMaxOfFaces(Faces) obj.FinalDepth.Value = fzmin finish_step = obj.FinishDepth.Value if hasattr( obj, "FinishDepth") else 0.0 self.depthparams = PathUtils.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=finish_step, final_depth=fzmin, user_depths=None) PathLog.info( "Updated obj.FinalDepth.Value and self.depthparams to zmin: {}" .format(fzmin)) if obj.AdaptivePocketStart is True or obj.AdaptivePocketFinish is True: pocketTup = self.calculateAdaptivePocket( obj, base, subObjTups) if pocketTup is not False: removalshapes.append( pocketTup ) # (shape, isHole, sub, angle, axis, strDep, finDep) else: strDep = obj.StartDepth.Value finDep = obj.FinalDepth.Value shape = Part.makeCompound(Faces) env = PathUtils.getEnvelope( base[0].Shape, subshape=shape, depthparams=self.depthparams) obj.removalshape = env.cut(base[0].Shape) obj.removalshape.tessellate(0.1) # (shape, isHole, sub, angle, axis, strDep, finDep) removalshapes.append( (obj.removalshape, False, '3DPocket', 0.0, 'X', strDep, finDep)) else: for sub in base[1]: if "Face" in sub: shape = Part.makeCompound( [getattr(base[0].Shape, sub)]) else: edges = [ getattr(base[0].Shape, sub) for sub in base[1] ] shape = Part.makeFace(edges, 'Part::FaceMakerSimple') env = PathUtils.getEnvelope( base[0].Shape, subshape=shape, depthparams=self.depthparams) obj.removalshape = env.cut(base[0].Shape) obj.removalshape.tessellate(0.1) removalshapes.append((obj.removalshape, False)) else: # process the job base object as a whole PathLog.debug("processing the whole job base object") strDep = obj.StartDepth.Value finDep = obj.FinalDepth.Value recomputeDepthparams = False for base in self.model: if obj.OpFinalDepth == obj.FinalDepth: if base.Shape.BoundBox.ZMin < obj.FinalDepth.Value: obj.FinalDepth.Value = base.Shape.BoundBox.ZMin finDep = base.Shape.BoundBox.ZMin recomputeDepthparams = True PathLog.info( "Updated obj.FinalDepth.Value to {}".format( finDep)) if obj.OpStartDepth == obj.StartDepth: if base.Shape.BoundBox.ZMax > obj.StartDepth.Value: obj.StartDepth.Value = base.Shape.BoundBox.ZMax finDep = base.Shape.BoundBox.ZMax recomputeDepthparams = True PathLog.info( "Updated obj.StartDepth.Value to {}".format( strDep)) if recomputeDepthparams is True: finish_step = obj.FinishDepth.Value if hasattr( obj, "FinishDepth") else 0.0 self.depthparams = PathUtils.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=finish_step, final_depth=obj.FinalDepth.Value, user_depths=None) recomputeDepthparams = False if obj.ProcessStockArea is True: job = PathUtils.findParentJob(obj) finish_step = obj.FinishDepth.Value if hasattr( obj, "FinishDepth") else 0.0 depthparams = PathUtils.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=finish_step, final_depth=base.Shape.BoundBox.ZMin, user_depths=None) stockEnvShape = PathUtils.getEnvelope( job.Stock.Shape, subshape=None, depthparams=depthparams) obj.removalshape = stockEnvShape.cut(base.Shape) obj.removalshape.tessellate(0.1) else: env = PathUtils.getEnvelope(base.Shape, subshape=None, depthparams=self.depthparams) obj.removalshape = env.cut(base.Shape) obj.removalshape.tessellate(0.1) removalshapes.append((obj.removalshape, False, '3DPocket', 0.0, 'X', strDep, finDep)) return removalshapes
def execute(self, obj, getsim=False): import Part if not obj.Active: path = Path.Path("(inactive operation)") obj.Path = path obj.ViewObject.Visibility = False 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) commandlist = [] 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.") return 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 commandlist.append(Path.Command("(" + obj.Label + ")")) if obj.UseComp: commandlist.append(Path.Command("(Compensated Tool Path. Diameter: " + str(self.radius * 2) + ")")) else: commandlist.append(Path.Command("(Uncompensated Tool Path)")) parentJob = PathUtils.findParentJob(obj) if parentJob is None: return baseobject = parentJob.Base if baseobject is None: return if obj.Base: # The user has selected subobjects from the base. Process each. holes = [] faces = [] for b in obj.Base: for sub in b[1]: shape = getattr(b[0].Shape, sub) if isinstance(shape, Part.Face): faces.append(shape) if numpy.isclose(abs(shape.normalAt(0, 0).z), 1): # horizontal face holes += shape.Wires[1:] else: FreeCAD.Console.PrintWarning("found a base object which is not a face. Can't continue.") return for wire in holes: f = Part.makeFace(wire, 'Part::FaceMakerSimple') drillable = PathUtils.isDrillable(baseobject.Shape, wire) if (drillable and obj.processCircles) or (not drillable and obj.processHoles): env = PathUtils.getEnvelope(baseobject.Shape, subshape=f, depthparams=self.depthparams) try: (pp, sim) = self._buildPathArea(obj, baseobject=env, isHole=True, start=None, getsim=getsim) commandlist.extend(pp.Commands) except Exception as e: FreeCAD.Console.PrintError(e) FreeCAD.Console.PrintError("Something unexpected happened. Unable to generate a contour path. Check project and tool config.") if len(faces) > 0: profileshape = Part.makeCompound(faces) if obj.processPerimeter: env = PathUtils.getEnvelope(baseobject.Shape, subshape=profileshape, depthparams=self.depthparams) try: (pp, sim) = self._buildPathArea(obj, baseobject=env, start=None, getsim=getsim) commandlist.extend(pp.Commands) except Exception as e: FreeCAD.Console.PrintError(e) FreeCAD.Console.PrintError("Something unexpected happened. Unable to generate a contour path. Check project and tool config.") else: # Try to build targets from the job base if hasattr(baseobject, "Proxy"): if isinstance(baseobject.Proxy, ArchPanel.PanelSheet): # process the sheet if obj.processPerimeter: shapes = baseobject.Proxy.getOutlines(baseobject, transform=True) for shape in shapes: for wire in shape.Wires: f = Part.makeFace(wire, 'Part::FaceMakerSimple') env = PathUtils.getEnvelope(baseobject.Shape, subshape=f, depthparams=self.depthparams) try: (pp, sim) = self._buildPathArea(obj, baseobject=env, isHole=False, start=None, getsim=getsim) commandlist.extend(pp.Commands) except Exception as e: FreeCAD.Console.PrintError(e) FreeCAD.Console.PrintError("Something unexpected happened. Unable to generate a contour path. Check project and tool config.") shapes = baseobject.Proxy.getHoles(baseobject, transform=True) for shape in shapes: for wire in shape.Wires: drillable = PathUtils.isDrillable(baseobject.Proxy, wire) if (drillable and obj.processCircles) or (not drillable and obj.processHoles): f = Part.makeFace(wire, 'Part::FaceMakerSimple') env = PathUtils.getEnvelope(baseobject.Shape, subshape=f, depthparams=self.depthparams) try: (pp, sim) = self._buildPathArea(obj, baseobject=env, isHole=True, start=None, getsim=getsim) commandlist.extend(pp.Commands) except Exception as e: FreeCAD.Console.PrintError(e) FreeCAD.Console.PrintError("Something unexpected happened. Unable to generate a contour 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
def calculateAdaptivePocket(self, obj, base, subObjTups): '''calculateAdaptivePocket(obj, base, subObjTups) Orient multiple faces around common facial center of mass. Identify edges that are connections for adjacent faces. Attempt to separate unconnected edges into top and bottom loops of the pocket. Trim the top and bottom of the pocket if available and requested. return: tuple with pocket shape information''' low = [] high = [] removeList = [] Faces = [] allEdges = [] makeHighFace = 0 tryNonPlanar = False isHighFacePlanar = True isLowFacePlanar = True faceType = 0 for (sub, face) in subObjTups: Faces.append(face) # identify max and min face heights for top loop (zmin, zmax) = self.getMinMaxOfFaces(Faces) # Order faces around common center of mass subObjTups = self.orderFacesAroundCenterOfMass(subObjTups) # find connected edges and map to edge names of base (connectedEdges, touching) = self.findSharedEdges(subObjTups) (low, high) = self.identifyUnconnectedEdges(subObjTups, touching) if len(high) > 0 and obj.AdaptivePocketStart is True: # attempt planar face with top edges of pocket allEdges = [] makeHighFace = 0 tryNonPlanar = False for (sub, face, ei) in high: allEdges.append(face.Edges[ei]) (hzmin, hzmax) = self.getMinMaxOfFaces(allEdges) try: highFaceShape = Part.Face( Part.Wire(Part.__sortEdges__(allEdges))) except Exception as ee: PathLog.warning(ee) PathLog.error( translate( "Path", "A planar adaptive start is unavailable. The non-planar will be attempted." )) tryNonPlanar = True else: makeHighFace = 1 if tryNonPlanar is True: try: highFaceShape = Part.makeFilledFace( Part.__sortEdges__(allEdges)) # NON-planar face method except Exception as eee: PathLog.warning(eee) PathLog.error( translate( "Path", "The non-planar adaptive start is also unavailable." ) + "(1)") isHighFacePlanar = False else: makeHighFace = 2 if makeHighFace > 0: FreeCAD.ActiveDocument.addObject('Part::Feature', 'topEdgeFace') highFace = FreeCAD.ActiveDocument.ActiveObject highFace.Shape = highFaceShape removeList.append(highFace.Name) # verify non-planar face is within high edge loop Z-boundaries if makeHighFace == 2: mx = hzmax + obj.StepDown.Value mn = hzmin - obj.StepDown.Value if highFace.Shape.BoundBox.ZMax > mx or highFace.Shape.BoundBox.ZMin < mn: PathLog.warning("ZMaxDiff: {}; ZMinDiff: {}".format( highFace.Shape.BoundBox.ZMax - mx, highFace.Shape.BoundBox.ZMin - mn)) PathLog.error( translate( "Path", "The non-planar adaptive start is also unavailable." ) + "(2)") isHighFacePlanar = False makeHighFace = 0 else: isHighFacePlanar = False if len(low) > 0 and obj.AdaptivePocketFinish is True: # attempt planar face with bottom edges of pocket allEdges = [] for (sub, face, ei) in low: allEdges.append(face.Edges[ei]) # (lzmin, lzmax) = self.getMinMaxOfFaces(allEdges) try: lowFaceShape = Part.Face( Part.Wire(Part.__sortEdges__(allEdges))) # lowFaceShape = Part.makeFilledFace(Part.__sortEdges__(allEdges)) # NON-planar face method except Exception as ee: PathLog.error(ee) PathLog.error("An adaptive finish is unavailable.") isLowFacePlanar = False else: FreeCAD.ActiveDocument.addObject('Part::Feature', 'bottomEdgeFace') lowFace = FreeCAD.ActiveDocument.ActiveObject lowFace.Shape = lowFaceShape removeList.append(lowFace.Name) else: isLowFacePlanar = False # Start with a regular pocket envelope strDep = obj.StartDepth.Value finDep = obj.FinalDepth.Value cuts = [] starts = [] finals = [] starts.append(obj.StartDepth.Value) finals.append(zmin) if obj.AdaptivePocketStart is True or len(subObjTups) == 1: strDep = zmax + obj.StepDown.Value starts.append(zmax + obj.StepDown.Value) finish_step = obj.FinishDepth.Value if hasattr(obj, "FinishDepth") else 0.0 depthparams = PathUtils.depth_params( clearance_height=obj.ClearanceHeight.Value, safe_height=obj.SafeHeight.Value, start_depth=strDep, step_down=obj.StepDown.Value, z_finish_step=finish_step, final_depth=finDep, user_depths=None) shape = Part.makeCompound(Faces) env = PathUtils.getEnvelope(base[0].Shape, subshape=shape, depthparams=depthparams) cuts.append(env.cut(base[0].Shape)) # Might need to change to .cut(job.Stock.Shape) if pocket has no bottom # job = PathUtils.findParentJob(obj) # envBody = env.cut(job.Stock.Shape) if isHighFacePlanar is True and len(subObjTups) > 1: starts.append(hzmax + obj.StepDown.Value) # make shape to trim top of reg pocket strDep1 = obj.StartDepth.Value + (hzmax - hzmin) if makeHighFace == 1: # Planar face finDep1 = highFace.Shape.BoundBox.ZMin + obj.StepDown.Value else: # Non-Planar face finDep1 = hzmin + obj.StepDown.Value depthparams1 = PathUtils.depth_params( clearance_height=obj.ClearanceHeight.Value, safe_height=obj.SafeHeight.Value, start_depth=strDep1, step_down=obj.StepDown.Value, z_finish_step=finish_step, final_depth=finDep1, user_depths=None) envTop = PathUtils.getEnvelope(base[0].Shape, subshape=highFace.Shape, depthparams=depthparams1) cbi = len(cuts) - 1 cuts.append(cuts[cbi].cut(envTop)) if isLowFacePlanar is True and len(subObjTups) > 1: # make shape to trim top of pocket if makeHighFace == 1: # Planar face strDep2 = lowFace.Shape.BoundBox.ZMax else: # Non-Planar face strDep2 = hzmax finDep2 = obj.FinalDepth.Value depthparams2 = PathUtils.depth_params( clearance_height=obj.ClearanceHeight.Value, safe_height=obj.SafeHeight.Value, start_depth=strDep2, step_down=obj.StepDown.Value, z_finish_step=finish_step, final_depth=finDep2, user_depths=None) envBottom = PathUtils.getEnvelope(base[0].Shape, subshape=lowFace.Shape, depthparams=depthparams2) cbi = len(cuts) - 1 cuts.append(cuts[cbi].cut(envBottom)) # package pocket details into tuple sdi = len(starts) - 1 fdi = len(finals) - 1 cbi = len(cuts) - 1 pocket = (cuts[cbi], False, '3DPocket', 0.0, 'X', starts[sdi], finals[fdi]) if FreeCAD.GuiUp: import FreeCADGui for rn in removeList: FreeCADGui.ActiveDocument.getObject(rn).Visibility = False for rn in removeList: FreeCAD.ActiveDocument.getObject(rn).purgeTouched() self.tempObjectNames.append(rn) return pocket
def areaOpShapes(self, obj): '''areaOpShapes(obj) ... returns envelope for all base shapes or wires for Arch.Panels.''' PathLog.track() if obj.UseComp: self.commandlist.append( Path.Command("(Compensated Tool Path. Diameter: " + str(self.radius * 2) + ")")) else: self.commandlist.append(Path.Command("(Uncompensated Tool Path)")) shapes = [] self.profileshape = [] # pylint: disable=attribute-defined-outside-init baseSubsTuples = [] subCount = 0 allTuples = [] if obj.Base: # The user has selected subobjects from the base. Process each. if obj.EnableRotation != 'Off': for p in range(0, len(obj.Base)): (base, subsList) = obj.Base[p] for sub in subsList: subCount += 1 shape = getattr(base.Shape, sub) if isinstance(shape, Part.Face): 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 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: PathLog.error( translate( "Path", "Face appears misaligned after initial rotation." )) if obj.AttemptInverseAngle is True and obj.InverseAngle is False: (clnBase, clnStock, angle) = self.applyInverseAngle( obj, clnBase, clnStock, axis, angle) else: msg = translate( "Path", "Consider toggling the 'InverseAngle' property and recomputing." ) PathLog.error(msg) else: PathLog.debug( "Face appears to be oriented correctly." ) tup = clnBase, sub, tag, angle, axis, clnStock else: if self.warnDisabledAxis(obj, axis) is False: 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) if subCount > 1: msg = translate('Path', "Multiple faces in Base Geometry.") + " " msg += translate( 'Path', "Depth settings will be applied to all faces.") PathLog.warning(msg) (Tags, Grps) = self.sortTuplesByIndex( allTuples, 2) # return (TagList, GroupList) subList = [] for o in range(0, len(Tags)): subList = [] for (base, sub, tag, angle, axis, stock) in Grps[o]: subList.append(sub) pair = base, subList, angle, axis, stock baseSubsTuples.append(pair) # Efor else: PathLog.debug( translate("Path", "EnableRotation property is 'Off'.")) stock = PathUtils.findParentJob(obj).Stock for (base, subList) in obj.Base: baseSubsTuples.append((base, subList, 0.0, 'X', stock)) # for base in obj.Base: finish_step = obj.FinishDepth.Value if hasattr( obj, "FinishDepth") else 0.0 for (base, subsList, angle, axis, stock) in baseSubsTuples: holes = [] faces = [] faceDepths = [] startDepths = [] for sub in subsList: shape = getattr(base.Shape, sub) if isinstance(shape, Part.Face): faces.append(shape) if numpy.isclose(abs(shape.normalAt(0, 0).z), 1): # horizontal face for wire in shape.Wires[1:]: holes.append((base.Shape, wire)) # Add face depth to list faceDepths.append(shape.BoundBox.ZMin) else: ignoreSub = base.Name + '.' + sub msg = translate( 'Path', "Found a selected object which is not a face. Ignoring: {}" .format(ignoreSub)) PathLog.error(msg) FreeCAD.Console.PrintWarning(msg) # Set initial Start and Final Depths and recalculate depthparams finDep = obj.FinalDepth.Value strDep = obj.StartDepth.Value if strDep > stock.Shape.BoundBox.ZMax: strDep = stock.Shape.BoundBox.ZMax startDepths.append(strDep) self.depthparams = self._customDepthParams(obj, strDep, finDep) for shape, wire in holes: f = Part.makeFace(wire, 'Part::FaceMakerSimple') drillable = PathUtils.isDrillable(shape, wire) if (drillable and obj.processCircles) or (not drillable and obj.processHoles): env = PathUtils.getEnvelope( shape, subshape=f, depthparams=self.depthparams) tup = env, True, 'pathProfileFaces', angle, axis, strDep, finDep shapes.append(tup) if len(faces) > 0: profileshape = Part.makeCompound(faces) self.profileshape.append(profileshape) if obj.processPerimeter: if obj.HandleMultipleFeatures == 'Collectively': custDepthparams = self.depthparams if obj.LimitDepthToFace is True and obj.EnableRotation != 'Off': if profileshape.BoundBox.ZMin > obj.FinalDepth.Value: finDep = profileshape.BoundBox.ZMin custDepthparams = self._customDepthParams( obj, strDep, finDep - 0.5) # only an envelope try: env = PathUtils.getEnvelope( base.Shape, subshape=profileshape, depthparams=custDepthparams) except Exception: # pylint: disable=broad-except # PathUtils.getEnvelope() failed to return an object. PathLog.error( translate( 'Path', 'Unable to create path for face(s).')) else: tup = env, False, 'pathProfileFaces', angle, axis, strDep, finDep shapes.append(tup) elif obj.HandleMultipleFeatures == 'Individually': for shape in faces: profShape = Part.makeCompound([shape]) finalDep = obj.FinalDepth.Value custDepthparams = self.depthparams if obj.Side == 'Inside': if finalDep < shape.BoundBox.ZMin: # Recalculate depthparams finalDep = shape.BoundBox.ZMin custDepthparams = self._customDepthParams( obj, strDep, finalDep - 0.5) env = PathUtils.getEnvelope( base.Shape, subshape=profShape, depthparams=custDepthparams) tup = env, False, 'pathProfileFaces', angle, axis, strDep, finalDep shapes.append(tup) # Lower high Start Depth to top of Stock startDepth = max(startDepths) if obj.StartDepth.Value > startDepth: obj.StartDepth.Value = startDepth else: # Try to build targets from the job base if 1 == len(self.model): if hasattr(self.model[0], "Proxy"): PathLog.info("hasattr() Proxy") if isinstance(self.model[0].Proxy, ArchPanel.PanelSheet): # process the sheet if obj.processCircles or obj.processHoles: for shape in self.model[0].Proxy.getHoles( self.model[0], transform=True): for wire in shape.Wires: drillable = PathUtils.isDrillable( self.model[0].Proxy, wire) if (drillable and obj.processCircles) or ( not drillable and obj.processHoles): f = Part.makeFace( wire, 'Part::FaceMakerSimple') env = PathUtils.getEnvelope( self.model[0].Shape, subshape=f, depthparams=self.depthparams) tup = env, True, 'pathProfileFaces', 0.0, 'X', obj.StartDepth.Value, obj.FinalDepth.Value shapes.append(tup) if obj.processPerimeter: for shape in self.model[0].Proxy.getOutlines( self.model[0], transform=True): for wire in shape.Wires: f = Part.makeFace(wire, 'Part::FaceMakerSimple') env = PathUtils.getEnvelope( self.model[0].Shape, subshape=f, depthparams=self.depthparams) tup = env, False, 'pathProfileFaces', 0.0, 'X', obj.StartDepth.Value, obj.FinalDepth.Value shapes.append(tup) self.removalshapes = shapes # pylint: disable=attribute-defined-outside-init PathLog.debug("%d shapes" % len(shapes)) return shapes
def execute(self, obj, getsim=False): PathLog.track() self.endVector = None if not obj.Active: path = Path.Path("(inactive operation)") obj.Path = path obj.ViewObject.Visibility = False return commandlist = [] toolLoad = obj.ToolController 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) if toolLoad is None or toolLoad.ToolNumber == 0: FreeCAD.Console.PrintError( "No Tool Controller is selected. We need a tool to build a Path." ) return 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 commandlist.append(Path.Command("(" + obj.Label + ")")) if obj.UseComp: commandlist.append( Path.Command("(Compensated Tool Path. Diameter: " + str(self.radius * 2) + ")")) else: commandlist.append(Path.Command("(Uncompensated Tool Path)")) parentJob = PathUtils.findParentJob(obj) if parentJob is None: return baseobject = parentJob.Base if baseobject is None: return # Let's always start by rapid to clearance...just for safety commandlist.append(Path.Command("G0", {"Z": obj.ClearanceHeight.Value})) isPanel = False if hasattr(baseobject, "Proxy"): if isinstance(baseobject.Proxy, ArchPanel.PanelSheet): # process the sheet isPanel = True baseobject.Proxy.execute(baseobject) shapes = baseobject.Proxy.getOutlines(baseobject, transform=True) for shape in shapes: f = Part.makeFace([shape], 'Part::FaceMakerSimple') thickness = baseobject.Group[0].Source.Thickness contourshape = f.extrude(FreeCAD.Vector(0, 0, thickness)) try: (pp, sim) = self._buildPathArea(obj, contourshape, start=obj.StartPoint, getsim=getsim) commandlist.extend(pp.Commands) except Exception as e: FreeCAD.Console.PrintError(e) FreeCAD.Console.PrintError( "Something unexpected happened. Unable to generate a contour path. Check project and tool config." ) if hasattr(baseobject, "Shape") and not isPanel: #bb = baseobject.Shape.BoundBox env = PathUtils.getEnvelope(partshape=baseobject.Shape, subshape=None, depthparams=self.depthparams) try: (pp, sim) = self._buildPathArea(obj, env, start=obj.StartPoint, getsim=getsim) commandlist.extend(pp.Commands) except Exception as e: FreeCAD.Console.PrintError(e) FreeCAD.Console.PrintError( "Something unexpected happened. Unable to generate a contour 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 return sim
def areaOpShapes(self, obj): '''areaOpShapes(obj) ... return top face''' # Facing is done either against base objects holeShape = None if obj.Base: PathLog.debug("obj.Base: {}".format(obj.Base)) faces = [] holes = [] holeEnvs = [] oneBase = [obj.Base[0][0], True] sub0 = getattr(obj.Base[0][0].Shape, obj.Base[0][1][0]) minHeight = sub0.BoundBox.ZMax for b in obj.Base: for sub in b[1]: shape = getattr(b[0].Shape, sub) if isinstance(shape, Part.Face): faces.append(shape) if shape.BoundBox.ZMin < minHeight: minHeight = shape.BoundBox.ZMin if oneBase[0] is not b[0]: oneBase[1] = False if numpy.isclose(abs(shape.normalAt(0, 0).z), 1): # horizontal face for wire in shape.Wires[1:]: if obj.ExcludeRaisedAreas is True: ip = self.isPocket(b[0], shape, wire) if ip is False: holes.append((b[0].Shape, wire)) else: holes.append((b[0].Shape, wire)) else: PathLog.error('The base subobject, "{}," is not a face. Ignoring "{}."'.format(sub, sub)) if obj.ExcludeRaisedAreas is True and len(holes) > 0: for shape, wire in holes: f = Part.makeFace(wire, 'Part::FaceMakerSimple') env = PathUtils.getEnvelope(shape, subshape=f, depthparams=self.depthparams) holeEnvs.append(env) holeShape = Part.makeCompound(holeEnvs) PathLog.debug("Working on a collection of faces {}".format(faces)) planeshape = Part.makeCompound(faces) # If no base object, do planing of top surface of entire model else: planeshape = Part.makeCompound([base.Shape for base in self.model]) PathLog.debug("Working on a shape {}".format(obj.Label)) # Find the correct shape depending on Boundary shape. PathLog.debug("Boundary Shape: {}".format(obj.BoundaryShape)) bb = planeshape.BoundBox if obj.BoundaryShape == 'Boundbox': bbperim = Part.makeBox(bb.XLength, bb.YLength, 1, FreeCAD.Vector(bb.XMin, bb.YMin, bb.ZMin), FreeCAD.Vector(0, 0, 1)) env = PathUtils.getEnvelope(partshape=bbperim, depthparams=self.depthparams) if obj.ExcludeRaisedAreas is True and oneBase[1] is True: includedFaces = self.getAllIncludedFaces(oneBase[0], env, faceZ=minHeight) if len(includedFaces) > 0: includedShape = Part.makeCompound(includedFaces) includedEnv = PathUtils.getEnvelope(oneBase[0].Shape, subshape=includedShape, depthparams=self.depthparams) env = env.cut(includedEnv) elif obj.BoundaryShape == 'Stock': stock = PathUtils.findParentJob(obj).Stock.Shape env = stock if obj.ExcludeRaisedAreas is True and oneBase[1] is True: includedFaces = self.getAllIncludedFaces(oneBase[0], stock, faceZ=minHeight) if len(includedFaces) > 0: stockEnv = PathUtils.getEnvelope(partshape=stock, depthparams=self.depthparams) includedShape = Part.makeCompound(includedFaces) includedEnv = PathUtils.getEnvelope(oneBase[0].Shape, subshape=includedShape, depthparams=self.depthparams) env = stockEnv.cut(includedEnv) else: env = PathUtils.getEnvelope(partshape=planeshape, depthparams=self.depthparams) if holeShape is not None: PathLog.info("Processing holes...") holeEnv = PathUtils.getEnvelope(partshape=holeShape, depthparams=self.depthparams) newEnv = env.cut(holeEnv) return [(newEnv, False)] else: return [(env, False)]
def areaOpShapes(self, obj): '''areaOpShapes(obj) ... returns envelope for all base shapes or wires for Arch.Panels.''' PathLog.track() PathLog.debug("----- areaOpShapes() in PathProfileFaces.py") if obj.UseComp: self.commandlist.append( Path.Command("(Compensated Tool Path. Diameter: " + str(self.radius * 2) + ")")) else: self.commandlist.append(Path.Command("(Uncompensated Tool Path)")) shapes = [] self.profileshape = [] finalDepths = [] baseSubsTuples = [] subCount = 0 allTuples = [] if obj.Base: # The user has selected subobjects from the base. Process each. if obj.EnableRotation != 'Off': for p in range(0, len(obj.Base)): (base, subsList) = obj.Base[p] for sub in subsList: subCount += 1 shape = getattr(base.Shape, sub) if isinstance(shape, Part.Face): rtn = False (norm, surf) = self.getFaceNormAndSurf(shape) (rtn, angle, axis, praInfo) = self.faceRotationAnalysis( obj, norm, surf) if rtn is True: (clnBase, angle, clnStock, tag) = self.applyRotationalAnalysis( obj, base, angle, axis, subCount) # Verify faces are correctly oriented - InverseAngle might be necessary faceIA = getattr(clnBase.Shape, sub) (norm, surf) = self.getFaceNormAndSurf(faceIA) (rtn, praAngle, praAxis, praInfo) = self.faceRotationAnalysis( obj, norm, surf) if rtn is True: PathLog.error( translate( "Path", "Face appears misaligned after initial rotation." )) if obj.AttemptInverseAngle is True and obj.InverseAngle is False: (clnBase, clnStock, angle) = self.applyInverseAngle( obj, clnBase, clnStock, axis, angle) else: msg = translate( "Path", "Consider toggling the 'InverseAngle' property and recomputing." ) PathLog.error(msg) # title = translate("Path", 'Rotation Warning') # self.guiMessage(title, msg, False) else: PathLog.debug( "Face appears to be oriented correctly." ) tup = clnBase, sub, tag, angle, axis, clnStock else: if self.warnDisabledAxis(obj, axis) is False: 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 # Eif allTuples.append(tup) # Eif # Efor # Efor if subCount > 1: msg = translate('Path', "Multiple faces in Base Geometry.") + " " msg += translate( 'Path', "Depth settings will be applied to all faces.") PathLog.warning(msg) # title = translate("Path", "Depth Warning") # self.guiMessage(title, msg) (Tags, Grps) = self.sortTuplesByIndex( allTuples, 2) # return (TagList, GroupList) subList = [] for o in range(0, len(Tags)): subList = [] for (base, sub, tag, angle, axis, stock) in Grps[o]: subList.append(sub) pair = base, subList, angle, axis, stock baseSubsTuples.append(pair) # Efor else: PathLog.info( translate("Path", "EnableRotation property is 'Off'.")) stock = PathUtils.findParentJob(obj).Stock for (base, subList) in obj.Base: baseSubsTuples.append((base, subList, 0.0, 'X', stock)) # for base in obj.Base: for (base, subsList, angle, axis, stock) in baseSubsTuples: holes = [] faces = [] for sub in subsList: shape = getattr(base.Shape, sub) if isinstance(shape, Part.Face): faces.append(shape) if numpy.isclose(abs(shape.normalAt(0, 0).z), 1): # horizontal face for wire in shape.Wires[1:]: holes.append((base.Shape, wire)) else: ignoreSub = base.Name + '.' + sub msg = translate( 'Path', "Found a selected object which is not a face. Ignoring: {}" .format(ignoreSub)) PathLog.error(msg) FreeCAD.Console.PrintWarning(msg) # return for shape, wire in holes: f = Part.makeFace(wire, 'Part::FaceMakerSimple') drillable = PathUtils.isDrillable(shape, wire) if (drillable and obj.processCircles) or (not drillable and obj.processHoles): PathLog.track() # Recalculate depthparams (strDep, finDep) = self.calculateStartFinalDepths( obj, shape, stock) finalDepths.append(finDep) PathLog.debug( "Adjusted face depths strDep: {}, and finDep: {}". format(self.strDep, self.finDep)) finish_step = obj.FinishDepth.Value if hasattr( obj, "FinishDepth") else 0.0 self.depthparams = PathUtils.depth_params( 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) env = PathUtils.getEnvelope( shape, subshape=f, depthparams=self.depthparams) # shapes.append((env, True)) tup = env, True, 'pathProfileFaces', angle, axis, strDep, finDep shapes.append(tup) if len(faces) > 0: profileshape = Part.makeCompound(faces) self.profileshape.append(profileshape) if obj.processPerimeter: PathLog.track() if profileshape: # Recalculate depthparams (strDep, finDep) = self.calculateStartFinalDepths( obj, profileshape, stock) finalDepths.append(finDep) PathLog.debug( "Adjusted face depths strDep: {}, and finDep: {}". format(self.strDep, self.finDep)) finish_step = obj.FinishDepth.Value if hasattr( obj, "FinishDepth") else 0.0 self.depthparams = PathUtils.depth_params( 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) else: strDep = obj.StartDepth.Value finDep = obj.FinalDepth.Value try: env = PathUtils.getEnvelope( base.Shape, subshape=profileshape, depthparams=self.depthparams) except Exception: # PathUtils.getEnvelope() failed to return an object. PathLog.error( translate('Path', 'Unable to create path for face(s).')) else: # shapes.append((env, False)) tup = env, False, 'pathProfileFaces', angle, axis, strDep, finDep shapes.append(tup) else: for shape in faces: # Recalculate depthparams (strDep, finDep) = self.calculateStartFinalDepths( obj, shape, stock) finalDepths.append(finDep) finish_step = obj.FinishDepth.Value if hasattr( obj, "FinishDepth") else 0.0 self.depthparams = PathUtils.depth_params( 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) env = PathUtils.getEnvelope( base.Shape, subshape=shape, depthparams=self.depthparams) tup = env, False, 'pathProfileFaces', angle, axis, strDep, finDep shapes.append(tup) # Eif # adjust FinalDepth as needed finalDepth = min(finalDepths) if obj.FinalDepth.Value < finalDepth: obj.FinalDepth.Value = finalDepth else: # Try to build targets from the job base if 1 == len(self.model) and hasattr(self.model[0], "Proxy"): if isinstance(self.model[0].Proxy, ArchPanel.PanelSheet): # process the sheet if obj.processCircles or obj.processHoles: for shape in self.model[0].Proxy.getHoles( self.model[0], transform=True): for wire in shape.Wires: drillable = PathUtils.isDrillable( self.model[0].Proxy, wire) if (drillable and obj.processCircles) or ( not drillable and obj.processHoles): f = Part.makeFace(wire, 'Part::FaceMakerSimple') env = PathUtils.getEnvelope( self.model[0].Shape, subshape=f, depthparams=self.depthparams) # shapes.append((env, True)) tup = env, True, 'pathProfileFaces', 0.0, 'X', obj.StartDepth.Value, obj.FinalDepth.Value shapes.append(tup) if obj.processPerimeter: for shape in self.model[0].Proxy.getOutlines( self.model[0], transform=True): for wire in shape.Wires: f = Part.makeFace(wire, 'Part::FaceMakerSimple') env = PathUtils.getEnvelope( self.model[0].Shape, subshape=f, depthparams=self.depthparams) # shapes.append((env, False)) tup = env, False, 'pathProfileFaces', 0.0, 'X', obj.StartDepth.Value, obj.FinalDepth.Value shapes.append(tup) self.removalshapes = shapes PathLog.debug("%d shapes" % len(shapes)) return shapes
def areaOpShapes(self, obj): """areaOpShapes(obj) ... return shapes representing the solids to be removed.""" PathLog.track() subObjTups = [] removalshapes = [] if obj.Base: PathLog.debug("base items exist. Processing... ") for base in obj.Base: PathLog.debug("obj.Base item: {}".format(base)) # Check if all subs are faces allSubsFaceType = True Faces = [] for sub in base[1]: if "Face" in sub: face = getattr(base[0].Shape, sub) Faces.append(face) subObjTups.append((sub, face)) else: allSubsFaceType = False break if len(Faces) == 0: allSubsFaceType = False if (allSubsFaceType is True and obj.HandleMultipleFeatures == "Collectively"): (fzmin, fzmax) = self.getMinMaxOfFaces(Faces) if obj.FinalDepth.Value < fzmin: PathLog.warning( translate( "PathPocket", "Final depth set below ZMin of face(s) selected.", )) if (obj.AdaptivePocketStart is True or obj.AdaptivePocketFinish is True): pocketTup = self.calculateAdaptivePocket( obj, base, subObjTups) if pocketTup is not False: obj.removalshape = pocketTup[0] removalshapes.append( pocketTup) # (shape, isHole, detail) else: shape = Part.makeCompound(Faces) env = PathUtils.getEnvelope( base[0].Shape, subshape=shape, depthparams=self.depthparams) rawRemovalShape = env.cut(base[0].Shape) faceExtrusions = [ f.extrude(FreeCAD.Vector(0.0, 0.0, 1.0)) for f in Faces ] obj.removalshape = _identifyRemovalSolids( rawRemovalShape, faceExtrusions) removalshapes.append( (obj.removalshape, False, "3DPocket")) # (shape, isHole, detail) else: for sub in base[1]: if "Face" in sub: shape = Part.makeCompound( [getattr(base[0].Shape, sub)]) else: edges = [ getattr(base[0].Shape, sub) for sub in base[1] ] shape = Part.makeFace(edges, "Part::FaceMakerSimple") env = PathUtils.getEnvelope( base[0].Shape, subshape=shape, depthparams=self.depthparams) rawRemovalShape = env.cut(base[0].Shape) faceExtrusions = [ shape.extrude(FreeCAD.Vector(0.0, 0.0, 1.0)) ] obj.removalshape = _identifyRemovalSolids( rawRemovalShape, faceExtrusions) removalshapes.append( (obj.removalshape, False, "3DPocket")) else: # process the job base object as a whole PathLog.debug("processing the whole job base object") for base in self.model: if obj.ProcessStockArea is True: job = PathUtils.findParentJob(obj) stockEnvShape = PathUtils.getEnvelope( job.Stock.Shape, subshape=None, depthparams=self.depthparams) rawRemovalShape = stockEnvShape.cut(base.Shape) else: env = PathUtils.getEnvelope(base.Shape, subshape=None, depthparams=self.depthparams) rawRemovalShape = env.cut(base.Shape) # Identify target removal shapes after cutting envelope with base shape removalSolids = [ s for s in rawRemovalShape.Solids if PathGeom.isRoughly( s.BoundBox.ZMax, rawRemovalShape.BoundBox.ZMax) ] # Fuse multiple solids if len(removalSolids) > 1: seed = removalSolids[0] for tt in removalSolids[1:]: fusion = seed.fuse(tt) seed = fusion removalShape = seed else: removalShape = removalSolids[0] obj.removalshape = removalShape removalshapes.append((obj.removalshape, False, "3DPocket")) return removalshapes