def TDPyTest(): path = os.path.dirname(os.path.abspath(__file__)) print('TDPy path: ' + path) FreeCAD.newDocument("TDPy") FreeCAD.setActiveDocument("TDPy") FreeCAD.ActiveDocument = FreeCAD.getDocument("TDPy") direction = FreeCAD.Vector(0.0, 1.0, 0.0) box = FreeCAD.ActiveDocument.addObject("Part::Box", "Box") result = TechDraw.project( box.Shape, direction) #visible hard & smooth, hidden hard & smooth print("project result: {0}".format(result)) # Part.show(result[0]) result = TechDraw.projectEx( box.Shape, direction) #visible & hidden hard, smooth, seam, outline, iso print("projectEx result: {0}".format(result)) # Part.show(result[0]) SVGResult = TechDraw.projectToSVG(box.Shape, direction, "ShowHiddenLines", 0.10) #SVG string print("SVG result: {0}".format(SVGResult)) result = TechDraw.projectToDXF(box.Shape, direction) #DXF string print("DXF result: {0}".format(result)) result = TechDraw.removeSvgTags(SVGResult) print("remove tags result: {0}".format(result))
def execute(self, obj): import Part # math #DraftGeomUtils output = "" toolLoad = PathUtils.getLastToolLoad(obj) if toolLoad is None or toolLoad.ToolNumber == 0: self.vertFeed = 100 self.horizFeed = 100 self.vertRapid = 100 self.horizRapid = 100 self.radius = 0.25 obj.ToolNumber = 0 obj.ToolDescription = "UNDEFINED" else: self.vertFeed = toolLoad.VertFeed.Value self.horizFeed = toolLoad.HorizFeed.Value self.vertRapid = toolLoad.VertRapid.Value self.horizRapid = toolLoad.HorizRapid.Value tool = PathUtils.getTool(obj, toolLoad.ToolNumber) if not tool or tool.Diameter == 0: self.radius = 0.25 else: self.radius = tool.Diameter/2 obj.ToolNumber = toolLoad.ToolNumber obj.ToolDescription = toolLoad.Name self.setLabel(obj) output += "(" + obj.Label + ")" if not obj.UseComp: output += "(Compensated Tool Path. Diameter: " + str(self.radius * 2) + ")" else: output += "(Uncompensated Tool Path)" parentJob = PathUtils.findParentJob(obj) if parentJob is None: return baseobject = parentJob.Base if baseobject is None: return contourwire = TechDraw.findShapeOutline(baseobject.Shape,1, Vector(0,0,1)) edgelist = contourwire.Edges edgelist = Part.__sortEdges__(edgelist) try: output += self._buildPathLibarea(obj, edgelist) except: FreeCAD.Console.PrintError("Something unexpected happened. Unable to generate a contour path. Check project and tool config.") if obj.Active: path = Path.Path(output) obj.Path = path if obj.ViewObject: obj.ViewObject.Visibility = True else: path = Path.Path("(inactive operation)") obj.Path = path obj.ViewObject.Visibility = False
def horizontalFaceLoop(obj, face, faceList=None): '''horizontalFaceLoop(obj, face, faceList=None) ... returns a list of face names which form the walls of a vertical hole face is a part of. All face names listed in faceList must be part of the hole for the solution to be returned.''' wires = [horizontalEdgeLoop(obj, e) for e in face.Edges] # Not sure if sorting by Area is a premature optimization - but it seems # the loop we're looking for is typically the biggest of the them all. wires = sorted([w for w in wires if w], key=lambda w: Part.Face(w).Area) for wire in wires: hashes = [e.hashCode() for e in wire.Edges] # find all faces that share a an edge with the wire and are vertical faces = [ "Face%d" % (i + 1) for i, f in enumerate(obj.Shape.Faces) if any(e.hashCode() in hashes for e in f.Edges) and PathGeom.isVertical(f) ] if faceList and not all(f in faces for f in faceList): continue # verify they form a valid hole by getting the outline and comparing # the resulting XY footprint with that of the faces comp = Part.makeCompound([obj.Shape.getElement(f) for f in faces]) outline = TechDraw.findShapeOutline(comp, 1, FreeCAD.Vector(0, 0, 1)) # findShapeOutline always returns closed wires, by removing the # trace-backs single edge spikes don't contriubte to the bound box uniqueEdges = [] for edge in outline.Edges: if any(PathGeom.edgesMatch(edge, e) for e in uniqueEdges): continue uniqueEdges.append(edge) w = Part.Wire(uniqueEdges) # if the faces really form the walls of a hole then the resulting # wire is still closed and it still has the same footprint bb1 = comp.BoundBox bb2 = w.BoundBox if w.isClosed() and PathGeom.isRoughly( bb1.XMin, bb2.XMin) and PathGeom.isRoughly( bb1.XMax, bb2.XMax) and PathGeom.isRoughly( bb1.YMin, bb2.YMin) and PathGeom.isRoughly( bb1.YMax, bb2.YMax): return faces return None
def execute(self, obj): import Part import TechDraw if not obj.Base: return if not obj.File: return if not obj.Pattern: return if not obj.Scale: return if not obj.Pattern in self.getPatterns(obj.File): return if not obj.Base.isDerivedFrom("Part::Feature"): return if not obj.Base.Shape.Faces: return pla = obj.Placement shapes = [] for face in obj.Base.Shape.Faces: face = face.copy() if obj.Translate: bpoint = face.CenterOfMass norm = face.normalAt(0, 0) fpla = FreeCAD.Placement( bpoint, FreeCAD.Rotation(FreeCAD.Vector(0, 0, 1), norm)) face.Placement = face.Placement.multiply(fpla.inverse()) if obj.Rotation: face.rotate(FreeCAD.Vector(), FreeCAD.Vector(0, 0, 1), obj.Rotation) shape = TechDraw.makeGeomHatch(face, obj.Scale, obj.Pattern, obj.File) if obj.Rotation: shape.rotate(FreeCAD.Vector(), FreeCAD.Vector(0, 0, 1), -obj.Rotation) if obj.Translate: shape.Placement = shape.Placement.multiply(fpla) shapes.append(shape) if shapes: obj.Shape = Part.makeCompound(shapes) obj.Placement = pla
def getProjected(self,obj,shape,direction): "returns projected edges from a shape and a direction" import Part, TechDraw, DraftGeomUtils edges = [] _groups = TechDraw.projectEx(shape, direction) for g in _groups[0:5]: if g: edges.append(g) if hasattr(obj,"HiddenLines"): if obj.HiddenLines: for g in _groups[5:]: edges.append(g) edges = self.cleanExcluded(obj,edges) #return Part.makeCompound(edges) if hasattr(obj,"Tessellation") and obj.Tessellation: return DraftGeomUtils.cleanProjection(Part.makeCompound(edges), obj.Tessellation, obj.SegmentLength) else: return Part.makeCompound(edges)
def horizontalFaceLoop(obj, face, faceList=None): '''horizontalFaceLoop(obj, face, faceList=None) ... returns a list of face names which form the walls of a vertical hole face is a part of. All face names listed in faceList must be part of the hole for the solution to be returned.''' wires = [horizontalEdgeLoop(obj, e) for e in face.Edges] # Not sure if sorting by Area is a premature optimization - but it seems # the loop we're looking for is typically the biggest of the them all. wires = sorted([w for w in wires if w], key=lambda w: Part.Face(w).Area) for wire in wires: hashes = [e.hashCode() for e in wire.Edges] #find all faces that share a an edge with the wire and are vertical faces = ["Face%d"%(i+1) for i,f in enumerate(obj.Shape.Faces) if any(e.hashCode() in hashes for e in f.Edges) and PathGeom.isVertical(f)] if faceList and not all(f in faces for f in faceList): continue # verify they form a valid hole by getting the outline and comparing # the resulting XY footprint with that of the faces comp = Part.makeCompound([obj.Shape.getElement(f) for f in faces]) outline = TechDraw.findShapeOutline(comp, 1, FreeCAD.Vector(0,0,1)) # findShapeOutline always returns closed wires, by removing the # trace-backs single edge spikes don't contriubte to the bound box uniqueEdges = [] for edge in outline.Edges: if any(PathGeom.edgesMatch(edge, e) for e in uniqueEdges): continue uniqueEdges.append(edge) w = Part.Wire(uniqueEdges) # if the faces really form the walls of a hole then the resulting # wire is still closed and it still has the same footprint bb1 = comp.BoundBox bb2 = w.BoundBox if w.isClosed() and PathGeom.isRoughly(bb1.XMin, bb2.XMin) and PathGeom.isRoughly(bb1.XMax, bb2.XMax) and PathGeom.isRoughly(bb1.YMin, bb2.YMin) and PathGeom.isRoughly(bb1.YMax, bb2.YMax): return faces return None
def areaOpShapes(self, obj): '''areaOpShapes(obj) ... return shapes representing the solids to be removed.''' PathLog.track() PathLog.debug("----- areaOpShapes() in PathPocketShape.py") baseSubsTuples = [] subCount = 0 allTuples = [] finalDepths = [] def planarFaceFromExtrusionEdges(face, trans): useFace = 'useFaceName' minArea = 0.0 fCnt = 0 clsd = [] planar = False # Identify closed edges for edg in face.Edges: if edg.isClosed(): PathLog.debug(' -e.isClosed()') clsd.append(edg) planar = True # Attempt to create planar faces and select that with smallest area for use as pocket base if planar is True: planar = False for edg in clsd: fCnt += 1 fName = sub + '_face_' + str(fCnt) # Create planar face from edge mFF = Part.Face(Part.Wire(Part.__sortEdges__([edg]))) if mFF.isNull(): PathLog.debug('Face(Part.Wire()) failed') else: if trans is True: mFF.translate( FreeCAD.Vector( 0, 0, face.BoundBox.ZMin - mFF.BoundBox.ZMin)) if FreeCAD.ActiveDocument.getObject(fName): FreeCAD.ActiveDocument.removeObject(fName) tmpFace = FreeCAD.ActiveDocument.addObject( 'Part::Feature', fName).Shape = mFF tmpFace = FreeCAD.ActiveDocument.getObject(fName) tmpFace.purgeTouched() if minArea == 0.0: minArea = tmpFace.Shape.Face1.Area useFace = fName planar = True elif tmpFace.Shape.Face1.Area < minArea: minArea = tmpFace.Shape.Face1.Area FreeCAD.ActiveDocument.removeObject(useFace) useFace = fName else: FreeCAD.ActiveDocument.removeObject(fName) if useFace != 'useFaceName': self.useTempJobClones(useFace) return (planar, useFace) def clasifySub(self, bs, sub): face = bs.Shape.getElement(sub) if type(face.Surface) == Part.Plane: PathLog.debug('type() == Part.Plane') if PathGeom.isVertical(face.Surface.Axis): PathLog.debug(' -isVertical()') # it's a flat horizontal face self.horiz.append(face) return True elif PathGeom.isHorizontal(face.Surface.Axis): PathLog.debug(' -isHorizontal()') self.vert.append(face) return True else: return False elif type(face.Surface) == Part.Cylinder and PathGeom.isVertical( face.Surface.Axis): PathLog.debug('type() == Part.Cylinder') # vertical cylinder wall if any(e.isClosed() for e in face.Edges): PathLog.debug(' -e.isClosed()') # complete cylinder circle = Part.makeCircle(face.Surface.Radius, face.Surface.Center) disk = Part.Face(Part.Wire(circle)) disk.translate( FreeCAD.Vector(0, 0, face.BoundBox.ZMin - disk.BoundBox.ZMin)) self.horiz.append(disk) return True else: PathLog.debug(' -none isClosed()') # partial cylinder wall self.vert.append(face) return True elif type(face.Surface) == Part.SurfaceOfExtrusion: # extrusion wall PathLog.debug('type() == Part.SurfaceOfExtrusion') # Attempt to extract planar face from surface of extrusion (planar, useFace) = planarFaceFromExtrusionEdges(face, trans=True) # Save face object to self.horiz for processing or display error if planar is True: uFace = FreeCAD.ActiveDocument.getObject(useFace) self.horiz.append(uFace.Shape.Faces[0]) msg = translate( 'Path', "<b>Verify depth of pocket for '{}'.</b>".format(sub)) msg += translate( 'Path', "\n<br>Pocket is based on extruded surface.") msg += translate( 'Path', "\n<br>Bottom of pocket might be non-planar and/or not normal to spindle axis." ) msg += translate( 'Path', "\n<br>\n<br><i>3D pocket bottom is NOT available in this operation</i>." ) PathLog.info(msg) title = translate('Path', 'Depth Warning') self.guiMessage(title, msg, False) else: PathLog.error( translate( "Path", "Failed to create a planar face from edges in {}.". format(sub))) else: PathLog.debug(' -type(face.Surface): {}'.format( type(face.Surface))) return False 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: baseSubsTuples.append((base, subList, 0.0, 'X', stock)) else: for p in range(0, len(obj.Base)): (base, subsList) = obj.Base[p] isLoop = False # First, check all subs collectively for loop of faces if len(subsList) > 2: (isLoop, norm, surf) = self.checkForFacesLoop(base, subsList) if isLoop is True: PathLog.info( "Common Surface.Axis or normalAt() value found for loop faces." ) rtn = False subCount += 1 (rtn, angle, axis, praInfo) = self.faceRotationAnalysis(obj, norm, surf) # pylint: disable=unused-variable PathLog.info("angle: {}; axis: {}".format( angle, axis)) if rtn is True: faceNums = "" for f in subsList: faceNums += '_' + f.replace('Face', '') (clnBase, angle, clnStock, tag) = self.applyRotationalAnalysis( obj, base, angle, axis, faceNums) # pylint: disable=unused-variable # Verify faces are correctly oriented - InverseAngle might be necessary PathLog.debug( "Checking if faces are oriented correctly after rotation..." ) for sub in subsList: face = clnBase.Shape.getElement(sub) if type(face.Surface) == Part.Plane: if not PathGeom.isHorizontal( face.Surface.Axis): rtn = False break if rtn is False: if obj.AttemptInverseAngle is True and obj.InverseAngle is False: (clnBase, clnStock, angle) = self.applyInverseAngle( obj, clnBase, clnStock, axis, angle) else: PathLog.info( translate( "Path", "Consider toggling the InverseAngle property and recomputing the operation." )) tup = clnBase, subsList, angle, axis, clnStock else: if self.warnDisabledAxis(obj, axis) is False: PathLog.debug("No rotation used") axis = 'X' angle = 0.0 stock = PathUtils.findParentJob(obj).Stock tup = base, subsList, angle, axis, stock # Eif allTuples.append(tup) baseSubsTuples.append(tup) # Eif if isLoop is False: PathLog.debug( translate('Path', "Processing subs individually ...")) for sub in subsList: subCount += 1 if 'Face' in sub: rtn = False face = base.Shape.getElement(sub) if type(face.Surface ) == Part.SurfaceOfExtrusion: # extrusion wall PathLog.debug( 'analyzing type() == Part.SurfaceOfExtrusion' ) # Attempt to extract planar face from surface of extrusion (planar, useFace) = planarFaceFromExtrusionEdges( face, trans=False) # Save face object to self.horiz for processing or display error if planar is True: base = FreeCAD.ActiveDocument.getObject( useFace) sub = 'Face1' PathLog.debug( ' -successful face created: {}'. format(useFace)) else: PathLog.error( translate( "Path", "Failed to create a planar face from edges in {}." .format(sub))) (norm, surf) = self.getFaceNormAndSurf(face) (rtn, angle, axis, praInfo) = self.faceRotationAnalysis( obj, norm, surf) # pylint: disable=unused-variable if rtn is True: faceNum = sub.replace('Face', '') (clnBase, angle, clnStock, tag) = self.applyRotationalAnalysis( obj, base, angle, axis, faceNum) # Verify faces are correctly oriented - InverseAngle might be necessary faceIA = clnBase.Shape.getElement(sub) (norm, surf) = self.getFaceNormAndSurf(faceIA) (rtn, praAngle, praAxis, praInfo) = self.faceRotationAnalysis( obj, norm, surf) # pylint: disable=unused-variable if rtn is True: PathLog.debug( "Face not aligned after initial rotation." ) if obj.AttemptInverseAngle is True and obj.InverseAngle is False: (clnBase, clnStock, angle) = self.applyInverseAngle( obj, clnBase, clnStock, axis, angle) else: PathLog.info( translate( "Path", "Consider toggling the InverseAngle property and recomputing the operation." )) else: PathLog.debug( "Face appears to be oriented correctly." ) tup = clnBase, [sub], angle, axis, clnStock else: if self.warnDisabledAxis(obj, axis) is False: PathLog.debug( str(sub) + ": No rotation used") axis = 'X' angle = 0.0 stock = PathUtils.findParentJob(obj).Stock tup = base, [sub], angle, axis, stock # Eif allTuples.append(tup) baseSubsTuples.append(tup) else: ignoreSub = base.Name + '.' + sub PathLog.error( translate( 'Path', "Selected feature is not a Face. Ignoring: {}" .format(ignoreSub))) 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 clasifySub(self, subBase, sub) is False: PathLog.error( translate( 'PathPocket', 'Pocket does not support shape %s.%s') % (subBase.Label, sub)) if obj.EnableRotation != 'Off': PathLog.info( 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) # title = translate("Path", "Face Selection Warning") # self.guiMessage(title, msg, True) else: face.translate( FreeCAD.Vector(0, 0, vFinDep - face.BoundBox.ZMin)) self.horiz.append(face) msg = translate( 'Path', 'Verify final depth of pocket shaped by vertical faces.' ) PathLog.error(msg) title = translate('Path', 'Depth Warning') self.guiMessage(title, msg, False) # 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) # move all horizontal faces to FinalDepth for f in self.horiz: finDep = max(obj.FinalDepth.Value, f.BoundBox.ZMin) f.translate(FreeCAD.Vector(0, 0, finDep - f.BoundBox.ZMin)) # 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) if obj.UseOutline: wire = TechDraw.findShapeOutline( shape, 1, FreeCAD.Vector(0, 0, 1)) wire.translate( FreeCAD.Vector( 0, 0, obj.FinalDepth.Value - wire.BoundBox.ZMin)) self.horizontal.append(Part.Face(wire)) else: self.horizontal.append(shape) for face in self.horizontal: # extrude all faces up to StartDepth and those are the removal shapes (strDep, finDep) = self.calculateStartFinalDepths( obj, face, stock) finalDepths.append(finDep) extent = FreeCAD.Vector(0, 0, strDep - finDep) self.removalshapes.append( (face.removeSplitter().extrude(extent), False, 'pathPocketShape', angle, axis, strDep, finDep)) PathLog.debug( "Extent depths are str: {}, and fin: {}".format( strDep, finDep)) # Efor face # Adjust obj.FinalDepth.Value as needed. if len(finalDepths) > 0: finalDep = min(finalDepths) if subCount == 1: obj.FinalDepth.Value = finalDep 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 execute(self, obj): import Part # math #DraftGeomUtils output = "" toolLoad = PathUtils.getLastToolLoad(obj) if toolLoad is None or toolLoad.ToolNumber == 0: self.vertFeed = 100 self.horizFeed = 100 self.vertRapid = 100 self.horizRapid = 100 self.radius = 0.25 obj.ToolNumber = 0 obj.ToolDescription = "UNDEFINED" else: self.vertFeed = toolLoad.VertFeed.Value self.horizFeed = toolLoad.HorizFeed.Value self.vertRapid = toolLoad.VertRapid.Value self.horizRapid = toolLoad.HorizRapid.Value tool = PathUtils.getTool(obj, toolLoad.ToolNumber) if not tool or tool.Diameter == 0: self.radius = 0.25 else: self.radius = tool.Diameter / 2 obj.ToolNumber = toolLoad.ToolNumber obj.ToolDescription = toolLoad.Name self.setLabel(obj) output += "(" + obj.Label + ")" if not obj.UseComp: output += "(Compensated Tool Path. Diameter: " + str( self.radius * 2) + ")" else: output += "(Uncompensated Tool Path)" parentJob = PathUtils.findParentJob(obj) if parentJob is None: return baseobject = parentJob.Base if baseobject is None: return contourwire = TechDraw.findShapeOutline(baseobject.Shape, 1, Vector(0, 0, 1)) edgelist = contourwire.Edges edgelist = Part.__sortEdges__(edgelist) try: output += self._buildPathLibarea(obj, edgelist) except: FreeCAD.Console.PrintError( "Something unexpected happened. Unable to generate a contour path. Check project and tool config." ) if obj.Active: path = Path.Path(output) obj.Path = path if obj.ViewObject: obj.ViewObject.Visibility = True else: path = Path.Path("(inactive operation)") obj.Path = path obj.ViewObject.Visibility = False
def execute(self, obj): import Part # math #DraftGeomUtils output = "" 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 output += "(" + obj.Label + ")" if obj.UseComp: output += "(Compensated Tool Path. Diameter: " + str( self.radius * 2) + ")" else: output += "(Uncompensated Tool Path)" if obj.Base: 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: print( "found a base object which is not a face. Can't continue." ) return profileshape = Part.makeCompound(faces) profilewire = TechDraw.findShapeOutline(profileshape, 1, Vector(0, 0, 1)) if obj.processHoles: for wire in holes: edgelist = wire.Edges edgelist = Part.__sortEdges__(edgelist) output += self._buildPathLibarea(obj, edgelist, True) if obj.processPerimeter: edgelist = profilewire.Edges edgelist = Part.__sortEdges__(edgelist) output += self._buildPathLibarea(obj, edgelist, False) else: #Try to build targets frorm the job base parentJob = PathUtils.findParentJob(obj) if parentJob is None: return baseobject = parentJob.Base if baseobject is None: return 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: edgelist = wire.Edges edgelist = Part.__sortEdges__(edgelist) PathLog.debug( "Processing panel perimeter. edges found: {}" .format(len(edgelist))) try: output += self._buildPathLibarea(obj, edgelist, isHole=False) except: 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): edgelist = wire.Edges edgelist = Part.__sortEdges__(edgelist) try: output += self._buildPathLibarea( obj, edgelist, isHole=True) except: FreeCAD.Console.PrintError( "Something unexpected happened. Unable to generate a contour path. Check project and tool config." ) if obj.Active: path = Path.Path(output) obj.Path = path obj.ViewObject.Visibility = True else: path = Path.Path("(inactive operation)") obj.Path = path obj.ViewObject.Visibility = False
def execute(self, obj): if not obj.Active: path = Path.Path("(inactive operation)") obj.Path = path obj.ViewObject.Visibility = False return #Tool may have changed. Refresh data toolLoad = PathUtils.getLastToolLoad(obj) if toolLoad is None or toolLoad.ToolNumber == 0: self.vertFeed = 100 self.horizFeed = 100 self.vertRapid = 100 self.horiRrapid = 100 self.radius = 0.25 obj.ToolNumber = 0 obj.ToolDescription = "UNDEFINED" else: self.vertFeed = toolLoad.VertFeed.Value self.horizFeed = toolLoad.HorizFeed.Value self.vertRapid = toolLoad.VertRapid.Value self.horizRapid = toolLoad.HorizRapid.Value tool = PathUtils.getTool(obj, toolLoad.ToolNumber) if tool.Diameter == 0: self.radius = 0.25 else: self.radius = tool.Diameter/2 obj.ToolNumber = toolLoad.ToolNumber obj.ToolDescription = toolLoad.Name #Build preliminary comments output = "" output += "(" + obj.Label + ")" if obj.UserLabel == "": obj.Label = obj.Name + " :" + obj.ToolDescription else: obj.Label = obj.UserLabel + " :" + obj.ToolDescription #Facing is done either against base objects if 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: print ('falling out') return planeshape = Part.makeCompound(faces) #If no base object, do planing of top surface of entire model else: parentJob = PathUtils.findParentJob(obj) if parentJob is None: return baseobject = parentJob.Base if baseobject is None: return planeshape = baseobject.Shape #if user wants the boundbox, calculate that if obj.BoundaryShape == 'Boundbox': bb = planeshape.BoundBox bbperim = Part.makeBox(bb.XLength, bb.YLength, 1, Vector(bb.XMin, bb.YMin, bb.ZMin), Vector(0,0,1)) contourwire = TechDraw.findShapeOutline(bbperim, 1, Vector(0,0,1)) else: contourwire = TechDraw.findShapeOutline(planeshape, 1, Vector(0,0,1)) edgelist = contourwire.Edges edgelist = Part.__sortEdges__(edgelist) #use libarea to build the pattern a = area.Area() c = PathScripts.PathKurveUtils.makeAreaCurve(edgelist, 'CW') a.append(c) a.Reorder() output += self.buildpathlibarea(obj, a) path = Path.Path(output) obj.Path = path obj.ViewObject.Visibility = True
def execute(self, obj): import Part # math #DraftGeomUtils output = "" 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 output += "(" + obj.Label + ")" if obj.UseComp: output += "(Compensated Tool Path. Diameter: " + str(self.radius * 2) + ")" else: output += "(Uncompensated Tool Path)" if obj.Base: 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: print ("found a base object which is not a face. Can't continue.") return profileshape = Part.makeCompound(faces) profilewire = TechDraw.findShapeOutline(profileshape, 1, Vector(0, 0, 1)) if obj.processHoles: for wire in holes: edgelist = wire.Edges edgelist = Part.__sortEdges__(edgelist) output += self._buildPathLibarea(obj, edgelist, True) if obj.processPerimeter: edgelist = profilewire.Edges edgelist = Part.__sortEdges__(edgelist) output += self._buildPathLibarea(obj, edgelist, False) else: #Try to build targets frorm the job base parentJob = PathUtils.findParentJob(obj) if parentJob is None: return baseobject = parentJob.Base if baseobject is None: return 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: edgelist = wire.Edges edgelist = Part.__sortEdges__(edgelist) PathLog.debug("Processing panel perimeter. edges found: {}".format(len(edgelist))) try: output += self._buildPathLibarea(obj, edgelist, isHole=False) except: 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): edgelist = wire.Edges edgelist = Part.__sortEdges__(edgelist) try: output += self._buildPathLibarea(obj, edgelist, isHole=True) except: FreeCAD.Console.PrintError("Something unexpected happened. Unable to generate a contour path. Check project and tool config.") if obj.Active: path = Path.Path(output) obj.Path = path obj.ViewObject.Visibility = True else: path = Path.Path("(inactive operation)") obj.Path = path obj.ViewObject.Visibility = False
def silhouette(obj): from FreeCAD import Vector s = getProjected(obj.Shape, Vector(0, 0, 1)) print s w = TechDraw.findOuterWire(s.Edges) return w
def execute(self, obj): PathLog.track() import Part # math #DraftGeomUtils output = "" 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) #PathUtils.getTool(obj, toolLoad.ToolNumber) 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 #self.radius = 0.25 else: self.radius = tool.Diameter/2 output += "(" + obj.Label + ")" if not obj.UseComp: output += "(Compensated Tool Path. Diameter: " + str(self.radius * 2) + ")" else: output += "(Uncompensated Tool Path)" parentJob = PathUtils.findParentJob(obj) if parentJob is None: return baseobject = parentJob.Base if baseobject is None: return if hasattr(baseobject, "Proxy"): if isinstance(baseobject.Proxy, ArchPanel.PanelSheet): # process the sheet baseobject.Proxy.execute(baseobject) for subobj in baseobject.Group: # process the group of panels if isinstance(subobj.Proxy, ArchPanel.PanelCut): shapes = baseobject.Proxy.getOutlines(baseobject, transform=True) for shape in shapes: for wire in shape.Wires: edgelist = wire.Edges edgelist = Part.__sortEdges__(edgelist) PathLog.debug("Processing panel perimeter. edges found: {}".format(len(edgelist))) # subobj.Proxy.execute(subobj) try: output += self._buildPathLibarea(obj, edgelist) except: FreeCAD.Console.PrintError("Something unexpected happened. Unable to generate a contour path. Check project and tool config.") else: contourwire = TechDraw.findShapeOutline(baseobject.Shape, 1, Vector(0, 0, 1)) edgelist = contourwire.Edges edgelist = Part.__sortEdges__(edgelist) try: output += self._buildPathLibarea(obj, edgelist) except: FreeCAD.Console.PrintError("Something unexpected happened. Unable to generate a contour path. Check project and tool config.") if obj.Active: path = Path.Path(output) obj.Path = path if obj.ViewObject: obj.ViewObject.Visibility = True else: path = Path.Path("(inactive operation)") obj.Path = path obj.ViewObject.Visibility = False
def silhouette(obj): w = TechDraw.findOuterWire(obj.Shape.Edges) return w
def execute(self, obj): PathLog.track() output = "" 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 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 # Build preliminary comments output = "" output += "(" + 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)) # if user wants the boundbox, calculate that PathLog.info("Boundary Shape: {}".format(obj.BoundaryShape)) if obj.BoundaryShape == 'Boundbox': bb = planeshape.BoundBox bbperim = Part.makeBox(bb.XLength, bb.YLength, 1, Vector(bb.XMin, bb.YMin, bb.ZMin), Vector(0, 0, 1)) contourwire = TechDraw.findShapeOutline(bbperim, 1, Vector(0, 0, 1)) else: contourwire = TechDraw.findShapeOutline(planeshape, 1, Vector(0, 0, 1)) # pocket = Path.Area(PocketMode=4,SectionCount=-1,SectionMode=1,Stepdown=0.499) # pocket.setParams(PocketExtraOffset = obj.PassExtension.Value, ToolRadius = self.radius) # pocket.add(planeshape, op=1) # #Part.show(contourwire) # path = Path.fromShapes(pocket.getShape()) edgelist = contourwire.Edges edgelist = Part.__sortEdges__(edgelist) # use libarea to build the pattern a = area.Area() c = PathScripts.PathKurveUtils.makeAreaCurve(edgelist, 'CW') PathLog.debug(c.text()) a.append(c) a.Reorder() output += self.buildpathlibarea(obj, a) path = Path.Path(output) if len(path.Commands) == 0: FreeCAD.Console.PrintMessage(translate("PathMillFace", "The selected settings did not produce a valid path.\n")) obj.Path = path obj.ViewObject.Visibility = True
def execute(self, obj): import Part import TechDraw if not obj.Base: return if not obj.File: return if not obj.Pattern: return if not obj.Scale: return if not obj.Pattern in self.getPatterns(obj.File): return if not obj.Base.isDerivedFrom("Part::Feature"): return if not obj.Base.Shape.Faces: return shapes = [] for face in obj.Base.Shape.Faces: if face.findPlane(): # Only planar faces. face = face.copy() if obj.Translate: mtx = None w = face.normalAt(0, 0) # Try to base a matrix on the first straight edge with # a reasonable length (> 0.001): for e in face.Edges: if geomType(e) == "Line": sta = e.firstVertex().Point end = e.lastVertex().Point u = end.sub(sta) if u.Length > 0.001: u = u.normalize() v = w.cross(u) mtx = App.Matrix(u.x, v.x, w.x, sta.x, u.y, v.y, w.y, sta.y, u.z, v.z, w.z, sta.z, 0.0, 0.0, 0.0, 1.0) break # If no suitable straight edge was found use a default matrix: if not mtx: cen = face.CenterOfMass rot = App.Rotation(App.Vector(0, 0, 1), w) mtx = App.Placement(cen, rot).Matrix face = face.transformGeometry(mtx.inverse()).Faces[0] if obj.Rotation.Value: face.rotate(App.Vector(), App.Vector(0, 0, 1), -obj.Rotation) shape = TechDraw.makeGeomHatch(face, obj.Scale, obj.Pattern, obj.File) if obj.Rotation.Value: shape.rotate(App.Vector(), App.Vector(0, 0, 1), obj.Rotation) if obj.Translate: shape = shape.transformGeometry(mtx) shapes.append(shape) if shapes: obj.Shape = Part.makeCompound(shapes)
def silhouette(obj): from FreeCAD import Vector s = getProjected(obj.Shape, Vector(0,0,1)) print s w = TechDraw.findOuterWire(s.Edges) return w
def execute(self, obj): import Part # math #DraftGeomUtils output = "" toolLoad = PathUtils.getLastToolLoad(obj) if toolLoad is None or toolLoad.ToolNumber == 0: self.vertFeed = 100 self.horizFeed = 100 self.vertRapid = 100 self.horizRapid = 100 self.radius = 0.25 obj.ToolNumber = 0 obj.ToolDescription = "UNDEFINED" else: self.vertFeed = toolLoad.VertFeed.Value self.horizFeed = toolLoad.HorizFeed.Value self.vertRapid = toolLoad.VertRapid.Value self.horizRapid = toolLoad.HorizRapid.Value tool = PathUtils.getTool(obj, toolLoad.ToolNumber) if tool.Diameter == 0: self.radius = 0.25 else: self.radius = tool.Diameter/2 obj.ToolNumber = toolLoad.ToolNumber obj.ToolDescription = toolLoad.Name if obj.UserLabel == "": obj.Label = obj.Name + " :" + obj.ToolDescription else: obj.Label = obj.UserLabel + " :" + obj.ToolDescription output += "(" + obj.Label + ")" if obj.Side != "On": output += "(Compensated Tool Path. Diameter: " + str(self.radius * 2) + ")" else: output += "(Uncompensated Tool Path)" if obj.Base: 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: print ("found a base object which is not a face. Can't continue.") return profileshape = Part.makeCompound(faces) profilewire = TechDraw.findShapeOutline(profileshape, 1, Vector(0,0,1)) if obj.processHoles: for wire in holes: edgelist = wire.Edges edgelist = Part.__sortEdges__(edgelist) output += self._buildPathLibarea(obj, edgelist, True) if obj.processPerimeter: edgelist = profilewire.Edges edgelist = Part.__sortEdges__(edgelist) output += self._buildPathLibarea(obj, edgelist, False) if obj.Active: path = Path.Path(output) obj.Path = path obj.ViewObject.Visibility = True else: path = Path.Path("(inactive operation)") obj.Path = path obj.ViewObject.Visibility = False
def execute(self, obj): if not obj.Active: path = Path.Path("(inactive operation)") obj.Path = path obj.ViewObject.Visibility = False return #Tool may have changed. Refresh data toolLoad = PathUtils.getLastToolLoad(obj) if toolLoad is None or toolLoad.ToolNumber == 0: self.vertFeed = 100 self.horizFeed = 100 self.vertRapid = 100 self.horiRrapid = 100 self.radius = 0.25 obj.ToolNumber = 0 obj.ToolDescription = "UNDEFINED" else: self.vertFeed = toolLoad.VertFeed.Value self.horizFeed = toolLoad.HorizFeed.Value self.vertRapid = toolLoad.VertRapid.Value self.horizRapid = toolLoad.HorizRapid.Value tool = PathUtils.getTool(obj, toolLoad.ToolNumber) if tool.Diameter == 0: self.radius = 0.25 else: self.radius = tool.Diameter / 2 obj.ToolNumber = toolLoad.ToolNumber obj.ToolDescription = toolLoad.Name #Build preliminary comments output = "" output += "(" + obj.Label + ")" if obj.UserLabel == "": obj.Label = obj.Name + " :" + obj.ToolDescription else: obj.Label = obj.UserLabel + " :" + obj.ToolDescription #Facing is done either against base objects if 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: print('falling out') return planeshape = Part.makeCompound(faces) #If no base object, do planing of top surface of entire model else: parentJob = PathUtils.findParentJob(obj) if parentJob is None: return baseobject = parentJob.Base if baseobject is None: return planeshape = baseobject.Shape #if user wants the boundbox, calculate that if obj.BoundaryShape == 'Boundbox': bb = planeshape.BoundBox bbperim = Part.makeBox(bb.XLength, bb.YLength, 1, Vector(bb.XMin, bb.YMin, bb.ZMin), Vector(0, 0, 1)) contourwire = TechDraw.findShapeOutline(bbperim, 1, Vector(0, 0, 1)) else: contourwire = TechDraw.findShapeOutline(planeshape, 1, Vector(0, 0, 1)) edgelist = contourwire.Edges edgelist = Part.__sortEdges__(edgelist) #use libarea to build the pattern a = area.Area() c = PathScripts.PathKurveUtils.makeAreaCurve(edgelist, 'CW') a.append(c) a.Reorder() output += self.buildpathlibarea(obj, a) path = Path.Path(output) obj.Path = path obj.ViewObject.Visibility = True
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...") self.removalshapes = [] self.horiz = [] vertical = [] for o in obj.Base: PathLog.debug("Base item: {}".format(o)) base = o[0] for sub in o[1]: if "Face" in sub: face = base.Shape.getElement(sub) if type(face.Surface) == Part.Plane and PathGeom.isVertical(face.Surface.Axis): # it's a flat horizontal face self.horiz.append(face) elif type(face.Surface) == Part.Cylinder and PathGeom.isVertical(face.Surface.Axis): # vertical cylinder wall if any(e.isClosed() for e in face.Edges): # complete cylinder circle = Part.makeCircle(face.Surface.Radius, face.Surface.Center) disk = Part.Face(Part.Wire(circle)) self.horiz.append(disk) else: # partial cylinder wall vertical.append(face) elif type(face.Surface) == Part.Plane and PathGeom.isHorizontal(face.Surface.Axis): vertical.append(face) else: PathLog.error(translate('PathPocket', "Pocket does not support shape %s.%s") % (base.Label, sub)) self.vertical = PathGeom.combineConnectedShapes(vertical) self.vWires = [TechDraw.findShapeOutline(shape, 1, FreeCAD.Vector(0, 0, 1)) for shape in self.vertical] for wire in self.vWires: w = PathGeom.removeDuplicateEdges(wire) face = Part.Face(w) face.tessellate(0.1) if PathGeom.isRoughly(face.Area, 0): PathLog.error(translate('PathPocket', 'Vertical faces do not form a loop - ignoring')) else: self.horiz.append(face) # move all horizontal faces to FinalDepth for f in self.horiz: f.translate(FreeCAD.Vector(0, 0, obj.FinalDepth.Value - f.BoundBox.ZMin)) # check all faces and see if they are touching/overlapping and combine those into a compound self.horizontal = [] for shape in PathGeom.combineConnectedShapes(self.horiz): shape.sewShape() shape.tessellate(0.1) if obj.UseOutline: wire = TechDraw.findShapeOutline(shape, 1, FreeCAD.Vector(0, 0, 1)) wire.translate(FreeCAD.Vector(0, 0, obj.FinalDepth.Value - wire.BoundBox.ZMin)) self.horizontal.append(Part.Face(wire)) else: self.horizontal.append(shape) # extrude all faces up to StartDepth and those are the removal shapes extent = FreeCAD.Vector(0, 0, obj.StartDepth.Value - obj.FinalDepth.Value) self.removalshapes = [(face.extrude(extent), False) for face in self.horizontal] else: # process the job base object as a whole PathLog.debug("processing the whole job base object") self.outlines = [Part.Face(TechDraw.findShapeOutline(base.Shape, 1, FreeCAD.Vector(0, 0, 1))) for base in self.model] stockBB = self.stock.Shape.BoundBox self.removalshapes = [] self.bodies = [] 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)) for (shape,hole) in self.removalshapes: shape.tessellate(0.1) if self.removalshapes: obj.removalshape = self.removalshapes[0][0] return self.removalshapes
def areaOpShapes(self, obj): '''areaOpShapes(obj) ... return shapes representing the solids to be removed.''' PathLog.track() PathLog.debug("areaOpShapes() in PathPocketShape.py") def judgeFinalDepth(obj, fD): if obj.FinalDepth.Value >= fD: return obj.FinalDepth.Value else: return fD def judgeStartDepth(obj, sD): if obj.StartDepth.Value >= sD: return obj.StartDepth.Value else: return sD def analyzeVerticalFaces(self, obj, vertTuples): hT = [] # base = FreeCAD.ActiveDocument.getObject(self.modelName) # Separate elements, regroup by orientation (axis_angle combination) vTags = ['X34.2'] vGrps = [[(2.3, 3.4, 'X')]] for tup in vertTuples: (face, sub, angle, axis, tag, strDep, finDep, trans) = tup if tag in vTags: # Determine index of found string i = 0 for orn in vTags: if orn == tag: break i += 1 vGrps[i].append(tup) else: vTags.append(tag) # add orientation entry vGrps.append([tup]) # add orientation entry # Remove temp elements vTags.pop(0) vGrps.pop(0) # check all faces in each axis_angle group shpList = [] zmaxH = 0.0 for o in range(0, len(vTags)): shpList = [] zmaxH = vGrps[o][0].BoundBox.ZMax for (face, sub, angle, axis, tag, strDep, finDep, trans) in vGrps[o]: shpList.append(face) # Identify tallest face to use as zMax if face.BoundBox.ZMax > zmaxH: zmaxH = face.BoundBox.ZMax # check all faces and see if they are touching/overlapping and combine those into a compound # Original Code in For loop self.vertical = PathGeom.combineConnectedShapes(shpList) self.vWires = [ TechDraw.findShapeOutline(shape, 1, FreeCAD.Vector(0, 0, 1)) for shape in self.vertical ] for wire in self.vWires: w = PathGeom.removeDuplicateEdges(wire) face = Part.Face(w) face.tessellate(0.05) if PathGeom.isRoughly(face.Area, 0): PathLog.error( translate( 'PathPocket', 'Vertical faces do not form a loop - ignoring') ) else: strDep = zmaxH + self.leadIn # base.Shape.BoundBox.ZMax finDep = judgeFinalDepth(obj, face.BoundBox.ZMin) tup = face, sub, angle, axis, tag, strDep, finDep, trans hT.append(tup) # Eol return hT if obj.Base: PathLog.debug('base items exist. Processing...') self.removalshapes = [] self.horiz = [] vertical = [] horizTuples = [] vertTuples = [] axis = 'X' angle = 0.0 reset = False resetPlacement = None trans = FreeCAD.Vector(0.0, 0.0, 0.0) for o in obj.Base: PathLog.debug('Base item: {}'.format(o)) base = o[0] # Limit sub faces to children of single Model object. if self.modelName is None: self.modelName = base.Name else: if base.Name != self.modelName: for sub in o[1]: PathLog.error(sub + " is not a part of Model object: " + self.modelName) o[1] = [] PathLog.error( "Only processing faces on a single Model object per operation." ) PathLog.error( "You will need to separate faces per Model object within the Job." ) startBase = FreeCAD.Vector(base.Placement.Base.x, base.Placement.Base.y, base.Placement.Base.z) startAngle = base.Placement.Rotation.Angle startAxis = base.Placement.Rotation.Axis startRotation = FreeCAD.Rotation(startAxis, startAngle) resetPlacement = FreeCAD.Placement(startBase, startRotation) for sub in o[1]: if 'Face' in sub: PathLog.debug('sub: {}'.format(sub)) # Determine angle of rotation needed to make normal vector = (0,0,1) strDep = obj.StartDepth.Value finDep = obj.FinalDepth.Value trans = FreeCAD.Vector(0.0, 0.0, 0.0) rtn = False if obj.UseRotation != 'Off': (rtn, angle, axis) = self.pocketRotationAnalysis(obj, base, sub, prnt=True) if rtn is True: reset = True PathLog.debug( str(sub) + ": rotating model to make face normal at (0,0,1) ..." ) if axis == 'X': bX = 0.0 bY = 0.0 bZ = math.sin(math.radians( angle)) * base.Placement.Base.y vect = FreeCAD.Vector(1, 0, 0) elif axis == 'Y': bX = 0.0 bY = 0.0 bZ = math.sin(math.radians( angle)) * base.Placement.Base.x if obj.B_AxisErrorOverride is True: bZ = -1 * bZ vect = FreeCAD.Vector(0, 1, 0) # Rotate base to such that Surface.Axis of pocket bottom is Z=1 base.Placement.Rotation = FreeCAD.Rotation( vect, angle) base.recompute() trans = FreeCAD.Vector(bX, bY, bZ) else: axis = 'X' angle = 0.0 tag = axis + str(round(angle, 7)) face = base.Shape.getElement(sub) if type(face.Surface ) == Part.Plane and PathGeom.isVertical( face.Surface.Axis): # it's a flat horizontal face PathLog.debug(" == Part.Plane: isVertical") # Adjust start and finish depths for pocket strDep = base.Shape.BoundBox.ZMax + self.leadIn finDep = judgeFinalDepth(obj, face.BoundBox.ZMin) # Over-write default final depth value, leaves manual override by user obj.StartDepth.Value = trans.z + strDep obj.FinalDepth.Value = trans.z + finDep tup = face, sub, angle, axis, tag, strDep, finDep, trans horizTuples.append(tup) elif type(face.Surface ) == Part.Cylinder and PathGeom.isVertical( face.Surface.Axis): PathLog.debug("== Part.Cylinder") # vertical cylinder wall if any(e.isClosed() for e in face.Edges): PathLog.debug("e.isClosed()") # complete cylinder circle = Part.makeCircle( face.Surface.Radius, face.Surface.Center) disk = Part.Face(Part.Wire(circle)) # Adjust start and finish depths for pocket strDep = face.BoundBox.ZMax + self.leadIn # base.Shape.BoundBox.ZMax + self.leadIn finDep = judgeFinalDepth( obj, face.BoundBox.ZMin) # Over-write default final depth value, leaves manual override by user obj.StartDepth.Value = trans.z + strDep obj.FinalDepth.Value = trans.z + finDep tup = disk, sub, angle, axis, tag, strDep, finDep, trans horizTuples.append(tup) else: # partial cylinder wall vertical.append(face) # Adjust start and finish depths for pocket strDep = face.BoundBox.ZMax + self.leadIn # base.Shape.BoundBox.ZMax + self.leadIn finDep = judgeFinalDepth( obj, face.BoundBox.ZMin) # Over-write default final depth value, leaves manual override by user obj.StartDepth.Value = trans.z + strDep obj.FinalDepth.Value = trans.z + finDep tup = face, sub, angle, axis, tag, strDep, finDep, trans vertTuples.append(tup) PathLog.debug(sub + "is vertical after rotation.") elif type(face.Surface ) == Part.Plane and PathGeom.isHorizontal( face.Surface.Axis): vertical.append(face) # Adjust start and finish depths for pocket strDep = face.BoundBox.ZMax + self.leadIn # base.Shape.BoundBox.ZMax + self.leadIn finDep = judgeFinalDepth(obj, face.BoundBox.ZMin) # Over-write default final depth value, leaves manual override by user obj.StartDepth.Value = trans.z + strDep obj.FinalDepth.Value = trans.z + finDep tup = face, sub, angle, axis, tag, strDep, finDep, trans vertTuples.append(tup) PathLog.debug(sub + "is vertical after rotation.") else: PathLog.error( translate( 'PathPocket', 'Pocket does not support shape %s.%s') % (base.Label, sub)) if reset is True: base.Placement.Rotation = startRotation base.recompute() reset = False # End IF # End FOR base.Placement = resetPlacement base.recompute() # End FOR # Analyze vertical faces via PathGeom.combineConnectedShapes() # hT = analyzeVerticalFaces(self, obj, vertTuples) # horizTuples.extend(hT) # This section will be replaced analyzeVerticalFaces(self, obj, vertTuples) above self.vertical = PathGeom.combineConnectedShapes(vertical) self.vWires = [ TechDraw.findShapeOutline(shape, 1, FreeCAD.Vector(0.0, 0.0, 1.0)) for shape in self.vertical ] for wire in self.vWires: w = PathGeom.removeDuplicateEdges(wire) face = Part.Face(w) face.tessellate(0.05) if PathGeom.isRoughly(face.Area, 0): PathLog.error( translate( 'PathPocket', 'Vertical faces do not form a loop - ignoring')) else: # self.horiz.append(face) strDep = base.Shape.BoundBox.ZMax + self.leadIn finDep = judgeFinalDepth(obj, face.BoundBox.ZMin) tup = face, 'vertFace', 0.0, 'X', 'X0.0', strDep, finDep, FreeCAD.Vector( 0.0, 0.0, 0.0) horizTuples.append(tup) # add faces for extensions self.exts = [] for ext in self.getExtensions(obj): wire = Part.Face(ext.getWire()) if wire: face = Part.Face(wire) # self.horiz.append(face) strDep = base.Shape.BoundBox.ZMax + self.leadIn finDep = judgeFinalDepth(obj, face.BoundBox.ZMin) tup = face, 'vertFace', 0.0, 'X', 'X0.0', strDep, finDep, FreeCAD.Vector( 0.0, 0.0, 0.0) horizTuples.append(tup) self.exts.append(face) # move all horizontal faces to FinalDepth for (face, sub, angle, axis, tag, strDep, finDep, trans) in horizTuples: # face.translate(FreeCAD.Vector(0, 0, obj.FinalDepth.Value - face.BoundBox.ZMin)) if angle <= 0.0: if axis == 'X': face.translate( FreeCAD.Vector( 0, trans.z, trans.z + finDep - face.BoundBox.ZMin)) elif axis == 'Y': face.translate( FreeCAD.Vector( -1 * trans.z, 0, trans.z + finDep - face.BoundBox.ZMin)) else: if axis == 'X': face.translate( FreeCAD.Vector( 0, -1 * trans.z, trans.z + finDep - face.BoundBox.ZMin)) elif axis == 'Y': face.translate( FreeCAD.Vector( trans.z, 0, trans.z + finDep - face.BoundBox.ZMin)) # Separate elements, regroup by orientation (axis_angle combination) hTags = ['X34.2'] hGrps = [[(2.3, 3.4, 'X')]] for tup in horizTuples: (face, sub, angle, axis, tag, strDep, finDep, trans) = tup if tag in hTags: # Determine index of found string i = 0 for orn in hTags: if orn == tag: break i += 1 hGrps[i].append(tup) else: hTags.append(tag) # add orientation entry hGrps.append([tup]) # add orientation entry # Remove temp elements hTags.pop(0) hGrps.pop(0) # check all faces in each axis_angle group self.horizontal = [] shpList = [] for o in range(0, len(hTags)): PathLog.debug('hTag: {}'.format(hTags[o])) shpList = [] for (face, sub, angle, axis, tag, strDep, finDep, trans) in hGrps[o]: shpList.append(face) # check all faces and see if they are touching/overlapping and combine those into a compound # Original Code in For loop for shape in PathGeom.combineConnectedShapes(shpList): shape.sewShape() # shape.tessellate(0.05) # Russ4262 0.1 original if obj.UseOutline: wire = TechDraw.findShapeOutline( shape, 1, FreeCAD.Vector(0, 0, 1)) wire.translate( FreeCAD.Vector( 0, 0, obj.FinalDepth.Value - wire.BoundBox.ZMin)) PathLog.debug( " -obj.UseOutline: obj.FinalDepth.Value" + str(obj.FinalDepth.Value)) PathLog.debug(" -obj.UseOutline: wire.BoundBox.ZMin" + str(wire.BoundBox.ZMin)) # shape.tessellate(0.05) # Russ4262 0.1 original face = Part.Face(wire) tup = face, sub, angle, axis, tag, strDep, finDep, trans self.horizontal.append(tup) else: # Re-pair shape to tuple set for (face, sub, angle, axis, tag, strDep, finDep, trans) in hGrps[o]: if shape is face: tup = face, sub, angle, axis, tag, strDep, finDep, trans self.horizontal.append(tup) break # Eol # extrude all faces up to StartDepth and those are the removal shapes for (face, sub, angle, axis, tag, strDep, finDep, trans) in self.horizontal: # extent = FreeCAD.Vector(0, 0, obj.StartDepth.Value - obj.FinalDepth.Value) extent = FreeCAD.Vector(0, 0, strDep - finDep) shp = face.removeSplitter().extrude(extent) # tup = shp, False, sub, angle, axis, tag, strDep, finDep, trans tup = shp, False, sub, angle, axis # shape, isHole, sub, angle, axis self.removalshapes.append(tup) else: # process the job base object as a whole PathLog.debug("processing the whole job base object") self.outlines = [ Part.Face( TechDraw.findShapeOutline(base.Shape, 1, FreeCAD.Vector(0, 0, 1))) for base in self.model ] stockBB = self.stock.Shape.BoundBox PathLog.debug(" -Using outlines; no obj.Base") self.removalshapes = [] self.bodies = [] 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, 'outline', 0.0, 'X')) for (shape, isHole, sub, angle, axis) in self.removalshapes: shape.tessellate(0.05) if self.removalshapes: obj.removalshape = self.removalshapes[0][0] return self.removalshapes
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...") self.removalshapes = [] self.horiz = [] vertical = [] for o in obj.Base: PathLog.debug("Base item: {}".format(o)) base = o[0] for sub in o[1]: if "Face" in sub: face = base.Shape.getElement(sub) if type(face.Surface) == Part.Plane and PathGeom.isVertical(face.Surface.Axis): # it's a flat horizontal face self.horiz.append(face) elif type(face.Surface) == Part.Cylinder and PathGeom.isVertical(face.Surface.Axis): # vertical cylinder wall if any(e.isClosed() for e in face.Edges): # complete cylinder circle = Part.makeCircle(face.Surface.Radius, face.Surface.Center) disk = Part.Face(Part.Wire(circle)) self.horiz.append(disk) else: # partial cylinder wall vertical.append(face) elif type(face.Surface) == Part.Plane and PathGeom.isHorizontal(face.Surface.Axis): vertical.append(face) else: PathLog.error(translate('PathPocket', "Pocket does not support shape %s.%s") % (base.Label, sub)) self.vertical = PathGeom.combineConnectedShapes(vertical) self.vWires = [TechDraw.findShapeOutline(shape, 1, FreeCAD.Vector(0, 0, 1)) for shape in self.vertical] for wire in self.vWires: w = PathGeom.removeDuplicateEdges(wire) face = Part.Face(w) face.tessellate(0.1) if PathGeom.isRoughly(face.Area, 0): PathLog.error(translate('PathPocket', 'Vertical faces do not form a loop - ignoring')) else: self.horiz.append(face) # move all horizontal faces to FinalDepth for f in self.horiz: f.translate(FreeCAD.Vector(0, 0, obj.FinalDepth.Value - f.BoundBox.ZMin)) # check all faces and see if they are touching/overlapping and combine those into a compound self.horizontal = [] for shape in PathGeom.combineConnectedShapes(self.horiz): shape.sewShape() shape.tessellate(0.1) if obj.UseOutline: wire = TechDraw.findShapeOutline(shape, 1, FreeCAD.Vector(0, 0, 1)) wire.translate(FreeCAD.Vector(0, 0, obj.FinalDepth.Value - wire.BoundBox.ZMin)) self.horizontal.append(Part.Face(wire)) else: self.horizontal.append(shape) # extrude all faces up to StartDepth and those are the removal shapes extent = FreeCAD.Vector(0, 0, obj.StartDepth.Value - obj.FinalDepth.Value) self.removalshapes = [(face.extrude(extent), False) for face in self.horizontal] else: # process the job base object as a whole PathLog.debug("processing the whole job base object") self.outline = Part.Face(TechDraw.findShapeOutline(self.baseobject.Shape, 1, FreeCAD.Vector(0, 0, 1))) stockBB = self.stock.Shape.BoundBox self.outline.translate(FreeCAD.Vector(0, 0, stockBB.ZMin - 1)) self.body = self.outline.extrude(FreeCAD.Vector(0, 0, stockBB.ZLength + 2)) self.removalshapes = [(self.stock.Shape.cut(self.body), False)] for (shape,hole) in self.removalshapes: shape.tessellate(0.1) if self.removalshapes: obj.removalshape = self.removalshapes[0][0] return self.removalshapes
def opExecute(self, obj): '''opExecute(obj) ... process engraving operation''' PathLog.track() zValues = [] if obj.StepDown.Value != 0: z = obj.StartDepth.Value - obj.StepDown.Value while z > obj.FinalDepth.Value: zValues.append(z) z -= obj.StepDown.Value zValues.append(obj.FinalDepth.Value) self.zValues = zValues output = '' try: if self.baseobject.isDerivedFrom('Sketcher::SketchObject') or \ self.baseobject.isDerivedFrom('Part::Part2DObject') or \ hasattr(self.baseobject, 'ArrayType'): output += "G0 Z" + PathUtils.fmt( obj.ClearanceHeight.Value) + "F " + PathUtils.fmt( self.vertRapid) + "\n" self.commandlist.append( Path.Command('G0', { 'Z': obj.ClearanceHeight.Value, 'F': self.vertRapid })) # we only consider the outer wire if this is a Face wires = [] for w in self.baseobject.Shape.Wires: tempedges = PathUtils.cleanedges(w.Edges, 0.5) wires.append(Part.Wire(tempedges)) output += self.buildpathocc(obj, wires, zValues) self.wires = wires elif isinstance(self.baseobject.Proxy, ArchPanel.PanelSheet): # process the sheet wires = [] for tag in self.baseobject.Proxy.getTags(self.baseobject, transform=True): output += "G0 Z" + PathUtils.fmt( obj.ClearanceHeight.Value) + "F " + PathUtils.fmt( self.vertRapid) + "\n" self.commandlist.append( Path.Command('G0', { 'Z': obj.ClearanceHeight.Value, 'F': self.vertRapid })) tagWires = [] for w in tag.Wires: tempedges = PathUtils.cleanedges(w.Edges, 0.5) tagWires.append(Part.Wire(tempedges)) output += self.buildpathocc(obj, tagWires, zValues) wires.extend(tagWires) self.wires = wires elif obj.Base: wires = [] for base, subs in obj.Base: edges = [] for sub in subs: edges.extend(base.Shape.getElement(sub).Edges) shapeWires = adjustWirePlacement( obj, base, TechDraw.edgeWalker(edges)) wires.extend(shapeWires) output += self.buildpathocc(obj, wires, zValues) self.wires = wires elif not obj.BaseShapes: raise ValueError( translate('PathEngrave', "Unknown baseobject type for engraving (%s)") % (obj.Base)) if obj.BaseShapes: wires = [] for shape in obj.BaseShapes: shapeWires = adjustWirePlacement(obj, shape, shape.Shape.Wires) output += self.buildpathocc(obj, shapeWires, zValues) wires.extend(shapeWires) self.wires = wires output += "G0 Z" + PathUtils.fmt( obj.ClearanceHeight.Value) + "F " + PathUtils.fmt( self.vertRapid) + "\n" self.commandlist.append( Path.Command('G0', { 'Z': obj.ClearanceHeight.Value, 'F': self.vertRapid })) except Exception as e: PathLog.error(e) traceback.print_exc() PathLog.error( translate( 'PathEngrave', 'The Job Base Object has no engraveable element. Engraving operation will produce no output.' ))
def analyzeVerticalFaces(self, obj, vertTuples): hT = [] # base = FreeCAD.ActiveDocument.getObject(self.modelName) # Separate elements, regroup by orientation (axis_angle combination) vTags = ['X34.2'] vGrps = [[(2.3, 3.4, 'X')]] for tup in vertTuples: (face, sub, angle, axis, tag, strDep, finDep, trans) = tup if tag in vTags: # Determine index of found string i = 0 for orn in vTags: if orn == tag: break i += 1 vGrps[i].append(tup) else: vTags.append(tag) # add orientation entry vGrps.append([tup]) # add orientation entry # Remove temp elements vTags.pop(0) vGrps.pop(0) # check all faces in each axis_angle group shpList = [] zmaxH = 0.0 for o in range(0, len(vTags)): shpList = [] zmaxH = vGrps[o][0].BoundBox.ZMax for (face, sub, angle, axis, tag, strDep, finDep, trans) in vGrps[o]: shpList.append(face) # Identify tallest face to use as zMax if face.BoundBox.ZMax > zmaxH: zmaxH = face.BoundBox.ZMax # check all faces and see if they are touching/overlapping and combine those into a compound # Original Code in For loop self.vertical = PathGeom.combineConnectedShapes(shpList) self.vWires = [ TechDraw.findShapeOutline(shape, 1, FreeCAD.Vector(0, 0, 1)) for shape in self.vertical ] for wire in self.vWires: w = PathGeom.removeDuplicateEdges(wire) face = Part.Face(w) face.tessellate(0.05) if PathGeom.isRoughly(face.Area, 0): PathLog.error( translate( 'PathPocket', 'Vertical faces do not form a loop - ignoring') ) else: strDep = zmaxH + self.leadIn # base.Shape.BoundBox.ZMax finDep = judgeFinalDepth(obj, face.BoundBox.ZMin) tup = face, sub, angle, axis, tag, strDep, finDep, trans hT.append(tup) # Eol return hT
def computeAreas(self,obj): """Compute the area, perimeter length, and volume of the terrain shape. Compute the area of the terrain projected onto an XY hyperplane, IE: the area of the terrain if viewed from a birds eye view. Compute the length of the perimeter of this birds eye view area. Compute the volume of the terrain that needs to be subtracted and added on account of the Additions and Subtractions to the site. Assign these values to their respective site properties. """ if not obj.Shape: return if obj.Shape.isNull(): return if not obj.Shape.isValid(): return if not obj.Shape.Faces: return if not hasattr(obj,"Perimeter"): # check we have a latest version site return if not obj.Terrain: return # compute area fset = [] for f in obj.Shape.Faces: if f.normalAt(0,0).getAngle(FreeCAD.Vector(0,0,1)) < 1.5707: fset.append(f) if fset: import TechDraw, Part pset = [] for f in fset: try: pf = Part.Face(Part.Wire(TechDraw.project(f,FreeCAD.Vector(0,0,1))[0].Edges)) except Part.OCCError: # error in computing the area. Better set it to zero than show a wrong value if obj.ProjectedArea.Value != 0: print("Error computing areas for ",obj.Label) obj.ProjectedArea = 0 else: pset.append(pf) if pset: self.flatarea = pset.pop() for f in pset: self.flatarea = self.flatarea.fuse(f) self.flatarea = self.flatarea.removeSplitter() if obj.ProjectedArea.Value != self.flatarea.Area: obj.ProjectedArea = self.flatarea.Area # compute perimeter lut = {} for e in obj.Shape.Edges: lut.setdefault(e.hashCode(),[]).append(e) l = 0 for e in lut.values(): if len(e) == 1: # keep only border edges l += e[0].Length if l: if obj.Perimeter.Value != l: obj.Perimeter = l # compute volumes if obj.Terrain.Shape.Solids: shapesolid = obj.Terrain.Shape.copy() else: shapesolid = obj.Terrain.Shape.extrude(obj.ExtrusionVector) addvol = 0 subvol = 0 for sub in obj.Subtractions: subvol += sub.Shape.common(shapesolid).Volume for sub in obj.Additions: addvol += sub.Shape.cut(shapesolid).Volume if obj.SubtractionVolume.Value != subvol: obj.SubtractionVolume = subvol if obj.AdditionVolume.Value != addvol: obj.AdditionVolume = addvol