def GetFace(self): self.edges.append(Part.makeLine(self.lastPoint, self.firstPoint)) w = Part.Wire(self.edges) return Part.Face(w)
def make_face(v1, v2, v3): wire = Part.makePolygon([v1, v2, v3, v1]) face = Part.Face(wire) return face
def __init__( self, depth, aluprof_dict, xtr_d=0, xtr_nd=0, axis_d=VX, axis_w=VY, axis_h=V0, pos_d=0, pos_w=0, pos_h=0, pos=V0, model_type=1, # dimensional model name=None): width = aluprof_dict['w'] depth = depth thick = aluprof_dict['t'] slot = aluprof_dict['slot'] insquare = aluprof_dict['insq'] indiam = aluprof_dict['indiam'] # either axis_w or axis_h can be V0, but not both if (axis_w is None) or (axis_w == V0): if (axis_h is None) or (axis_h == V0): logger.error('Either axis_w or axis_h must be defined') logger.warning('getting a random perpendicular verctor') axis_w = fcfun.get_fc_perpend1(axis_d) else: axis_w = axis_h.cross(axis_d) if (axis_h is None) or (axis_h == V0): axis_h = axis_d.cross(axis_w) if name == None: name = ('aluprof_w' + str(int(aluprof_dict['w'])) + 'l_' + str(int(xtr_nd + depth + xtr_d))) Obj3D.__init__(self, axis_d, axis_w, axis_h, name=name) # save the arguments as attributes: frame = inspect.currentframe() args, _, _, values = inspect.getargvalues(frame) for i in args: if not hasattr(self, i): setattr(self, i, values[i]) self.pos = FreeCAD.Vector(0, 0, 0) self.position = pos self.d0_cen = 0 self.w0_cen = 1 # symmetric self.h0_cen = 1 # symmetric # total length (depth) self.tot_d = xtr_nd + depth + xtr_d # vectors from the origin to the points along axis_d: self.d_o[0] = V0 # origin self.d_o[1] = self.vec_d(xtr_nd) #if xtr_nd= 0: same as d_o[0] # middle point, not considering xtr_nd and xtr_d self.d_o[2] = self.vec_d(xtr_nd + depth / 2.) # middle point considering xtr_nd and xtr_d self.d_o[3] = self.vec_d(self.tot_d / 2.) self.d_o[4] = self.vec_d(xtr_nd + depth) self.d_o[5] = self.vec_d(self.tot_d) # vectors from the origin to the points along axis_w: # symmetric: negative self.w_o[0] = V0 # center: origin self.w_o[1] = self.vec_w(-insquare / 2.) self.w_o[2] = self.vec_w(-(width / 2. - thick)) self.w_o[3] = self.vec_w(-width / 2.) # vectors from the origin to the points along axis_h: # symmetric: negative self.h_o[0] = V0 # center: origin self.h_o[1] = self.vec_h(-insquare / 2.) self.h_o[2] = self.vec_h(-(width / 2. - thick)) self.h_o[3] = self.vec_h(-width / 2.) # calculates the position of the origin, and keeps it in attribute pos_o self.set_pos_o() shp_alu_wire = fcfun.shp_aluwire_dir( width, thick, slot, insquare, fc_axis_x=self.axis_w, fc_axis_y=self.axis_h, ref_x=1, # pos_o is centered ref_y=1, # pos_o is centered pos=self.pos_o) # make a face of the wire shp_alu_face = Part.Face(shp_alu_wire) # inner hole if indiam > 0: hole = Part.makeCircle( indiam / 2., # Radius self.pos_o, # Position self.axis_d) # direction wire_hole = Part.Wire(hole) face_hole = Part.Face(wire_hole) shp_alu_face = shp_alu_face.cut(face_hole) # extrude it dir_extrud = DraftVecUtils.scaleTo(self.axis_d, self.tot_d) shp_aluprof = shp_alu_face.extrude(dir_extrud) self.shp = shp_aluprof super().create_fco() # Need to set first in (0,0,0) and after that set the real placement. # This enable to do rotations without any issue self.fco.Placement.Base = FreeCAD.Vector(0, 0, 0) self.fco.Placement.Base = self.position
def areas(ship, n, draft=None, roll=Units.parseQuantity("0 deg"), trim=Units.parseQuantity("0 deg")): """Compute the ship transversal areas Position arguments: ship -- Ship object (see createShip) n -- Number of points to compute Keyword arguments: draft -- Ship draft (Design ship draft by default) roll -- Roll angle (0 degrees by default) trim -- Trim angle (0 degrees by default) Returned value: List of sections, each section contains 2 values, the x longitudinal coordinate, and the transversal area. If n < 2, an empty list will be returned. """ if n < 2: return [] if draft is None: draft = ship.Draft shape, _ = placeShipShape(ship.Shape.copy(), draft, roll, trim) shape = getUnderwaterSide(shape) # Sections distance computation bbox = shape.BoundBox xmin = bbox.XMin xmax = bbox.XMax dx = (xmax - xmin) / (n - 1.0) # Since we are computing the sections in the total length (not in the # length between perpendiculars), we can grant that the starting and # ending sections have null area areas = [(Units.Quantity(xmin, Units.Length), Units.Quantity(0.0, Units.Area))] # And since we just need to compute areas we will create boxes with its # front face at the desired transversal area position, computing the # common solid part, dividing it by faces, and getting only the desired # ones. App.Console.PrintMessage("Computing transversal areas...\n") App.Console.PrintMessage("Some Inventor representation errors can be" " shown, please ignore them.\n") for i in range(1, n - 1): App.Console.PrintMessage("{0} / {1}\n".format(i, n - 2)) x = xmin + i * dx try: f = Part.Face(shape.slice(Vector(1, 0, 0), x)) except Part.OCCError: msg = QtGui.QApplication.translate( "ship_console", "Part.OCCError: Transversal area computation failed", None) App.Console.PrintError(msg + '\n') areas.append((Units.Quantity(x, Units.Length), Units.Quantity(0.0, Units.Area))) continue # It is a valid face, so we can add this area areas.append( (Units.Quantity(x, Units.Length), Units.Quantity(f.Area, Units.Area))) # Last area is equal to zero (due to the total length usage) areas.append( (Units.Quantity(xmax, Units.Length), Units.Quantity(0.0, Units.Area))) App.Console.PrintMessage("Done!\n") return areas
def createMeshView(obj, direction=FreeCAD.Vector(0, 0, -1), outeronly=False, largestonly=False): """createMeshView(obj,[direction,outeronly,largestonly]): creates a flat shape that is the projection of the given mesh object in the given direction (default = on the XY plane). If outeronly is True, only the outer contour is taken into consideration, discarding the inner holes. If largestonly is True, only the largest segment of the given mesh will be used.""" import Mesh, math, Part, DraftGeomUtils if not obj.isDerivedFrom("Mesh::Feature"): return mesh = obj.Mesh # 1. Flattening the mesh proj = [] for f in mesh.Facets: nf = [] for v in f.Points: v = FreeCAD.Vector(v) a = v.negative().getAngle(direction) l = math.cos(a) * v.Length p = v.add(FreeCAD.Vector(direction).multiply(l)) p = DraftVecUtils.rounded(p) nf.append(p) proj.append(nf) flatmesh = Mesh.Mesh(proj) # 2. Removing wrong faces facets = [] for f in flatmesh.Facets: if f.Normal.getAngle(direction) < math.pi: facets.append(f) cleanmesh = Mesh.Mesh(facets) #Mesh.show(cleanmesh) # 3. Getting the bigger mesh from the planar segments if largestonly: c = cleanmesh.getSeparateComponents() #print(c) cleanmesh = c[0] segs = cleanmesh.getPlanarSegments(1) meshes = [] for s in segs: f = [cleanmesh.Facets[i] for i in s] meshes.append(Mesh.Mesh(f)) a = 0 for m in meshes: if m.Area > a: boundarymesh = m a = m.Area #Mesh.show(boundarymesh) cleanmesh = boundarymesh # 4. Creating a Part and getting the contour shape = None for f in cleanmesh.Facets: p = Part.makePolygon(f.Points + [f.Points[0]]) #print(p,len(p.Vertexes),p.isClosed()) try: p = Part.Face(p) if shape: shape = shape.fuse(p) else: shape = p except Part.OCCError: pass shape = shape.removeSplitter() # 5. Extracting the largest wire if outeronly: count = 0 largest = None for w in shape.Wires: if len(w.Vertexes) > count: count = len(w.Vertexes) largest = w if largest: try: f = Part.Face(w) except Part.OCCError: print("Unable to produce a face from the outer wire.") else: shape = f return shape
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 = [] 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.warning(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.warning(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 = obj.FinalDepth.Value # 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) # extrude all faces up to StartDepth and those are the removal shapes sD = obj.StartDepth.Value fD = obj.FinalDepth.Value extent = FreeCAD.Vector(0, 0, sD - fD) for face in self.horizontal: self.removalshapes.append( (face.removeSplitter().extrude(extent), False, 'pathPocketShape', angle, axis, sD, fD)) PathLog.debug( "Extent depths are str: {}, and fin: {}".format( sD, fD)) # Efor face # Efor else: # process the job base object as a whole PathLog.debug(translate("Path", 'Processing model as a whole ...')) finDep = obj.FinalDepth.Value strDep = obj.StartDepth.Value self.outlines = [ Part.Face( TechDraw.findShapeOutline(base.Shape, 1, FreeCAD.Vector(0, 0, 1))) for base in self.model ] # pylint: disable=attribute-defined-outside-init stockBB = self.stock.Shape.BoundBox self.removalshapes = [] # pylint: disable=attribute-defined-outside-init self.bodies = [] # pylint: disable=attribute-defined-outside-init for outline in self.outlines: outline.translate(FreeCAD.Vector(0, 0, stockBB.ZMin - 1)) body = outline.extrude( FreeCAD.Vector(0, 0, stockBB.ZLength + 2)) self.bodies.append(body) self.removalshapes.append( (self.stock.Shape.cut(body), False, 'pathPocketShape', 0.0, 'X', strDep, finDep)) for (shape, hole, sub, angle, axis, strDep, finDep) in self.removalshapes: # pylint: disable=unused-variable shape.tessellate(0.05) # originally 0.1 if self.removalshapes: obj.removalshape = self.removalshapes[0][0] return self.removalshapes
def __init__(self, name, origin, side='L'): self.name = name if not isinstance(origin, Base.Vector): raise RuntimeError("origin is not a Vector!") self.origin = origin ox = origin.x oy = origin.y oz = origin.z bracketPoly = list() bracketPoly.append(Base.Vector(ox, oy, oz)) self.bracketmh = dict() if side == 'L': x = ox y = oy + self.AngleLength() bracketPoly.append(Base.Vector(x, y, oz)) x -= self.AngleWidth() bracketPoly.append(Base.Vector(x, y, oz)) y -= self.AngleNotchDY2() bracketPoly.append(Base.Vector(x, y, oz)) x += self.AngleNotchDX() bracketPoly.append(Base.Vector(x, y, oz)) y -= (self.AngleNotchDY1() - self.AngleNotchDY2()) bracketPoly.append(Base.Vector(x, y, oz)) x -= self.AngleNotchDX() bracketPoly.append(Base.Vector(x, y, oz)) y -= (self.AngleLength() - self.AngleNotchDY1()) bracketPoly.append(Base.Vector(x, y, oz)) bracketPoly.append(Base.Vector(ox, oy, oz)) angle_a = Part.Face(Part.Wire(Part.makePolygon(bracketPoly))) self.bracketmh[1] = origin.add( Base.Vector(-self.BRACKET_z(), self.M1_y(), 0)) self.bracketmh[2] = origin.add( Base.Vector(-self.BRACKET_z(), self.M2_y(), 0)) self.bracketmh[3] = origin.add( Base.Vector(-self.BRACKET_z(), self.M3_y(), 0)) self.bracketmh[4] = origin.add( Base.Vector(-self.BRACKET_z(), self.M4_y(), 0)) extrude_b = Base.Vector(-self.AngleThickness(), 0, 0) elif side == 'R': x = ox y = oy + self.AngleLength() bracketPoly.append(Base.Vector(x, y, oz)) x += self.AngleWidth() bracketPoly.append(Base.Vector(x, y, oz)) y -= self.AngleNotchDY2() bracketPoly.append(Base.Vector(x, y, oz)) x -= self.AngleNotchDX() bracketPoly.append(Base.Vector(x, y, oz)) y -= (self.AngleNotchDY1() - self.AngleNotchDY2()) bracketPoly.append(Base.Vector(x, y, oz)) x += self.AngleNotchDX() bracketPoly.append(Base.Vector(x, y, oz)) y -= (self.AngleLength() - self.AngleNotchDY1()) bracketPoly.append(Base.Vector(x, y, oz)) bracketPoly.append(Base.Vector(ox, oy, oz)) angle_a = Part.Face(Part.Wire(Part.makePolygon(bracketPoly))) self.bracketmh[1] = origin.add( Base.Vector(self.BRACKET_z(), self.M1_y(), 0)) self.bracketmh[2] = origin.add( Base.Vector(self.BRACKET_z(), self.M2_y(), 0)) self.bracketmh[3] = origin.add( Base.Vector(self.BRACKET_z(), self.M3_y(), 0)) self.bracketmh[4] = origin.add( Base.Vector(self.BRACKET_z(), self.M4_y(), 0)) extrude_b = Base.Vector(self.AngleThickness(), 0, 0) else: raise RuntimeError("side must be L or R!") self.bracketPoly = bracketPoly borig = origin.add( Base.Vector(0, self.AngleLength(), -self.AngleHeight())) angle_b = Part.makePlane(self.AngleHeight(), self.AngleLength(), borig, Base.Vector(1, 0, 0)) self.lcdmh = dict() self.lcdmh[1] = origin.add(Base.Vector(0, self.M1_y(), -self.M_x())) self.lcdmh[2] = origin.add(Base.Vector(0, self.M2_y(), -self.M_x())) self.lcdmh[3] = origin.add(Base.Vector(0, self.M3_y(), -self.M_x())) self.lcdmh[4] = origin.add(Base.Vector(0, self.M4_y(), -self.M_x())) #self.mhFaces = dict() for i in [1, 2, 3, 4]: bmhFace = Part.Face( Part.Wire(Part.makeCircle(self.BRACKET_r(), self.bracketmh[i]))) angle_a = angle_a.cut(bmhFace) mhFace = Part.Face( Part.Wire( Part.makeCircle(self.M_r(), self.lcdmh[i], Base.Vector(1, 0, 0)))) angle_b = angle_b.cut(mhFace) #self.mhFaces[i] = mhFace.extrude(extrude_b) extrude_a = Base.Vector(0, 0, -self.AngleThickness()) self.bracket = angle_a.extrude(extrude_a) self.bracket = self.bracket.fuse(angle_b.extrude(extrude_b)) self._mapPolyVerts() self._mapBMHoleEdges() self._mapLCDMHoleEdges()
def setup_rcwall2d(doc=None, solver="ccxtools"): # setup reinfoced wall in 2D if doc is None: doc = init_doc() # part from FreeCAD import Vector as vec import Part from Part import makeLine as ln v1 = vec(0, -2000, 0) v2 = vec(500, -2000, 0) v3 = vec(500, 0, 0) v4 = vec(3500, 0, 0) v5 = vec(3500, -2000, 0) v6 = vec(4000, -2000, 0) v7 = vec(4000, 2000, 0) v8 = vec(0, 2000, 0) l1 = ln(v1, v2) l2 = ln(v2, v3) l3 = ln(v3, v4) l4 = ln(v4, v5) l5 = ln(v5, v6) l6 = ln(v6, v7) l7 = ln(v7, v8) l8 = ln(v8, v1) rcwall = doc.addObject("Part::Feature", "FIB_Wall") rcwall.Shape = Part.Face(Part.Wire([l1, l2, l3, l4, l5, l6, l7, l8])) # analysis analysis = ObjectsFem.makeAnalysis(doc, "Analysis") # solver # TODO How to pass multiple solver for one analysis in one doc if solver == "calculix": solver = analysis.addObject( ObjectsFem.makeSolverCalculix(doc, "SolverCalculiX"))[0] solver.AnalysisType = "static" solver.GeometricalNonlinearity = "linear" solver.ThermoMechSteadyState = False solver.MatrixSolverType = "default" solver.IterationsControlParameterTimeUse = False elif solver == "ccxtools": solver = analysis.addObject( ObjectsFem.makeSolverCalculixCcxTools(doc, "CalculiXccxTools"))[0] solver.AnalysisType = "static" solver.GeometricalNonlinearity = "linear" solver.ThermoMechSteadyState = False solver.MatrixSolverType = "default" solver.IterationsControlParameterTimeUse = False solver.WorkingDir = u"" # shell thickness thickness = analysis.addObject( ObjectsFem.makeElementGeometry2D(doc, 0, "ShellThickness"))[0] thickness.Thickness = 150.0 # material matrixprop = {} matrixprop["Name"] = "Concrete-EN-C35/45" matrixprop["YoungsModulus"] = "32000 MPa" matrixprop["PoissonRatio"] = "0.17" matrixprop["CompressiveStrength"] = "15.75 MPa" # make some hint on the possible angle units in material system matrixprop["AngleOfFriction"] = "30 deg" matrixprop["Density"] = "2500 kg/m^3" reinfoprop = {} reinfoprop["Name"] = "Reinforcement-FIB-B500" reinfoprop["YieldStrength"] = "315 MPa" # not an official FreeCAD material property reinfoprop["ReinforcementRatio"] = "0.0" material_reinforced = analysis.addObject( ObjectsFem.makeMaterialReinforced(doc, "MaterialReinforced"))[0] material_reinforced.Material = matrixprop material_reinforced.Reinforcement = reinfoprop # fixed_constraint fixed_constraint = analysis.addObject( ObjectsFem.makeConstraintFixed(doc, name="ConstraintFixed"))[0] fixed_constraint.References = [(rcwall, "Edge1"), (rcwall, "Edge5")] # force constraint force_constraint = doc.Analysis.addObject( ObjectsFem.makeConstraintForce(doc, name="ConstraintForce"))[0] force_constraint.References = [(rcwall, "Edge7")] force_constraint.Force = 1000000.0 force_constraint.Direction = (rcwall, ["Edge8"]) force_constraint.Reversed = False # displacement_constraint displacement_constraint = doc.Analysis.addObject( ObjectsFem.makeConstraintDisplacement( doc, name="ConstraintDisplacmentPrescribed"))[0] displacement_constraint.References = [(rcwall, "Face1")] displacement_constraint.zFix = True # mesh from femexamples.meshes.mesh_rc_wall_2d_tria6 import create_nodes, create_elements fem_mesh = Fem.FemMesh() control = create_nodes(fem_mesh) if not control: FreeCAD.Console.PrintError("Error on creating nodes.\n") control = create_elements(fem_mesh) if not control: FreeCAD.Console.PrintError("Error on creating elements.\n") femmesh_obj = analysis.addObject( doc.addObject("Fem::FemMeshObject", mesh_name))[0] femmesh_obj.FemMesh = fem_mesh doc.recompute() return doc
def smCornerR(reliefsketch="Circle", size=3.0, ratio=1.0, xoffset=0.0, yoffset=0.0, kfactor=0.5, sketch='', flipped=False, selEdgeNames='', MainObject=None): resultSolid = MainObject.Shape.copy() REdgelist = [] for selEdgeName in selEdgeNames: REdge = resultSolid.getElement(selEdgeName) REdgelist.append(REdge) DetailList = getBendDetail(resultSolid, REdgelist[0], REdgelist[1], kfactor) cornerPoint, centerPoint, LargeFace, thk, unfoldLength, neutralRadius = DetailList normal = LargeFace.normalAt(0, 0) SplitLineVector = centerPoint - cornerPoint if "Scaled" in reliefsketch: size = ratio * abs(SplitLineVector.Length) SplitLineVector.normalize() #print([centerPoint, cornerPoint, SplitLineVector] ) SplitLine = Part.makeLine(centerPoint + SplitLineVector * size * 3, cornerPoint + SplitLineVector * -size * 3) #Part.show(SplitLine,"SplitLine") if reliefsketch != "Sketch": sketch = makeSketch(reliefsketch, size, ratio, centerPoint, normal, SplitLineVector) reliefFace = Part.Face(sketch) else: reliefFace = Part.Face(sketch.Shape.Wires[0]) #Part.show(reliefFace,'reliefFace') #To check face direction coeff = normal.dot(reliefFace.Faces[0].normalAt(0, 0)) if coeff < 0: reliefFace.reverse() # to get top face cut First_face = LargeFace.common(reliefFace) #Part.show(First_face,'First_face') Balance_Face = reliefFace.cut(First_face) #Part.show(Balance_Face,'Balance_Face') #To get bend solid face SplitFaces = BOPTools.SplitAPI.slice(Balance_Face.Faces[0], SplitLine.Edges, "Standard", 0.0) #Part.show(SplitFaces,"SplitFaces") #To get top face normal, flatsolid solidlist = [] if First_face.Faces: Flatsolid = First_face.extrude(normal * -thk) #Part.show(Flatsolid,"Flatsolid") solidlist.append(Flatsolid) if SplitFaces.Faces: for BalanceFace in SplitFaces.Faces: #Part.show(BalanceFace,"BalanceFace") TopFace = LargeFace #Part.show(TopFace,"TopFace") while BalanceFace.Faces: BendEdgelist = smGetEdgelist(BalanceFace, TopFace) for BendEdge in BendEdgelist: #Part.show(BendEdge,"BendEdge") edge_facelist = resultSolid.ancestorsOfType( BendEdge, Part.Face) for cyl_face in edge_facelist: #print(type(cyl_face.Surface)) if issubclass(type(cyl_face.Surface), Part.Cylinder): break if issubclass(type(cyl_face.Surface), Part.Cylinder): break #Part.show(BendEdge,"BendEdge") facelist = resultSolid.ancestorsOfType(BendEdge, Part.Face) #To get bend radius, bend angle for cylface in facelist: if issubclass(type(cylface.Surface), Part.Cylinder): break if not (issubclass(type(cylface.Surface), Part.Cylinder)): break #Part.show(cylface,"cylface") for planeface in facelist: if issubclass(type(planeface.Surface), Part.Plane): break #Part.show(planeface,"planeface") normal = planeface.normalAt(0, 0) revAxisV = cylface.Surface.Axis revAxisP = cylface.Surface.Center bendA = bendAngle(cylface, revAxisP) #print([bendA, revAxisV, revAxisP, cylface.Orientation]) #To check bend direction offsetface = cylface.makeOffsetShape(-thk, 0.0, fill=False) #Part.show(offsetface,"offsetface") if offsetface.Area < cylface.Area: bendR = cylface.Surface.Radius - thk flipped = True else: bendR = cylface.Surface.Radius flipped = False #To arrive unfold Length, neutralRadius unfoldLength = (bendR + kfactor * thk) * abs(bendA) * math.pi / 180.0 neutralRadius = (bendR + kfactor * thk) #print([unfoldLength,neutralRadius]) #To get faceNormal, bend face faceNormal = normal.cross(revAxisV).normalize() #print(faceNormal) if bendR < cylface.Surface.Radius: offsetSolid = cylface.makeOffsetShape(bendR / 2.0, 0.0, fill=True) else: offsetSolid = cylface.makeOffsetShape(-bendR / 2.0, 0.0, fill=True) #Part.show(offsetSolid,"offsetSolid") tool = BendEdge.copy() FaceArea = tool.extrude(faceNormal * -unfoldLength) #Part.show(FaceArea,"FaceArea") #Part.show(BalanceFace,"BalanceFace") SolidFace = offsetSolid.common(FaceArea) if not (SolidFace.Faces): faceNormal = faceNormal * -1 FaceArea = tool.extrude(faceNormal * -unfoldLength) BendSolidFace = BalanceFace.common(FaceArea) #Part.show(FaceArea,"FaceArea") #Part.show(BendSolidFace,"BendSolidFace") #print([bendR, bendA, revAxisV, revAxisP, normal, flipped, BendSolidFace.Faces[0].normalAt(0,0)]) bendsolid = SheetMetalBendSolid.BendSolid( BendSolidFace.Faces[0], BendEdge, bendR, thk, neutralRadius, revAxisV, flipped) #Part.show(bendsolid,"bendsolid") solidlist.append(bendsolid) if flipped == True: bendA = -bendA if not (SolidFace.Faces): revAxisV = revAxisV * -1 sketch_face = BalanceFace.cut(BendSolidFace) sketch_face.translate(faceNormal * unfoldLength) #Part.show(sketch_face,"sketch_face") sketch_face.rotate(revAxisP, -revAxisV, bendA) #Part.show(sketch_face,"Rsketch_face") CylEdge = smGetEdge(sketch_face, cylface) #Part.show(CylEdge,"CylEdge") edgefacelist = resultSolid.ancestorsOfType(CylEdge, Part.Face) for TopFace in edgefacelist: if not (issubclass(type(TopFace.Surface), Part.Cylinder)): break #Part.show(TopFace,"TopFace") #To get top face normal, flatsolid normal = TopFace.normalAt(0, 0) Flatface = sketch_face.common(TopFace) BalanceFace = sketch_face.cut(Flatface) #Part.show(BalanceFace,"BalanceFace") if Flatface.Faces: BalanceFace = sketch_face.cut(Flatface) #Part.show(BalanceFace,"BalanceFace") Flatsolid = Flatface.extrude(normal * -thk) #Part.show(Flatsolid,"Flatsolid") solidlist.append(Flatsolid) else: BalanceFace = sketch_face #To get relief Solid fused if len(solidlist) > 1: SMSolid = solidlist[0].multiFuse(solidlist[1:]) #Part.show(SMSolid,"SMSolid") SMSolid = SMSolid.removeSplitter() else: SMSolid = solidlist[0] #Part.show(SMSolid,"SMSolid") resultSolid = resultSolid.cut(SMSolid) return resultSolid
def execute(self, obj): if hasattr(obj, "VisibleOnly"): if obj.VisibleOnly: if obj.ViewObject: if obj.ViewObject.Visibility == False: return False import Part, DraftGeomUtils obj.positionBySupport() pl = obj.Placement if obj.Base: if utils.get_type(obj.Base) in ["BuildingPart", "SectionPlane"]: objs = [] if utils.get_type(obj.Base) == "SectionPlane": objs = obj.Base.Objects cutplane = obj.Base.Shape else: objs = obj.Base.Group cutplane = Part.makePlane(1000, 1000, App.Vector(-500, -500, 0)) m = 1 if obj.Base.ViewObject and hasattr(obj.Base.ViewObject, "CutMargin"): m = obj.Base.ViewObject.CutMargin.Value cutplane.translate(App.Vector(0, 0, m)) cutplane.Placement = cutplane.Placement.multiply( obj.Base.Placement) if objs: onlysolids = True if hasattr(obj.Base, "OnlySolids"): onlysolids = obj.Base.OnlySolids import Arch, Part, Drawing objs = utils.get_group_contents(objs, walls=True) objs = gui_utils.remove_hidden(objs) shapes = [] if hasattr(obj, "FuseArch") and obj.FuseArch: shtypes = {} for o in objs: if utils.get_type(o) in ["Wall", "Structure"]: if onlysolids: shtypes.setdefault( o.Material.Name if (hasattr(o, "Material") and o.Material) else "None", []).extend(o.Shape.Solids) else: shtypes.setdefault( o.Material.Name if (hasattr(o, "Material") and o.Material) else "None", []).append(o.Shape.copy()) elif hasattr(o, 'Shape'): if onlysolids: shapes.extend(o.Shape.Solids) else: shapes.append(o.Shape.copy()) for k, v in shtypes.items(): v1 = v.pop() if v: v1 = v1.multiFuse(v) v1 = v1.removeSplitter() if v1.Solids: shapes.extend(v1.Solids) else: print( "Shape2DView: Fusing Arch objects produced non-solid results" ) shapes.append(v1) else: for o in objs: if hasattr(o, 'Shape'): if onlysolids: shapes.extend(o.Shape.Solids) else: shapes.append(o.Shape.copy()) clip = False if hasattr(obj.Base, "Clip"): clip = obj.Base.Clip cutp, cutv, iv = Arch.getCutVolume(cutplane, shapes, clip) cuts = [] opl = App.Placement(obj.Base.Placement) proj = opl.Rotation.multVec(App.Vector(0, 0, 1)) if obj.ProjectionMode == "Solid": for sh in shapes: if cutv: if sh.Volume < 0: sh.reverse() #if cutv.BoundBox.intersect(sh.BoundBox): # c = sh.cut(cutv) #else: # c = sh.copy() c = sh.cut(cutv) if onlysolids: cuts.extend(c.Solids) else: cuts.append(c) else: if onlysolids: cuts.extend(sh.Solids) else: cuts.append(sh.copy()) comp = Part.makeCompound(cuts) obj.Shape = self.getProjected(obj, comp, proj) elif obj.ProjectionMode in ["Cutlines", "Cutfaces"]: for sh in shapes: if sh.Volume < 0: sh.reverse() c = sh.section(cutp) faces = [] if (obj.ProjectionMode == "Cutfaces") and (sh.ShapeType == "Solid"): if hasattr(obj, "InPlace"): if not obj.InPlace: c = self.getProjected(obj, c, proj) wires = DraftGeomUtils.findWires(c.Edges) for w in wires: if w.isClosed(): faces.append(Part.Face(w)) if faces: cuts.extend(faces) else: cuts.append(c) comp = Part.makeCompound(cuts) opl = App.Placement(obj.Base.Placement) comp.Placement = opl.inverse() if comp: obj.Shape = comp elif obj.Base.isDerivedFrom("App::DocumentObjectGroup"): shapes = [] objs = utils.get_group_contents(obj.Base) for o in objs: if hasattr(o, 'Shape'): if o.Shape: if not o.Shape.isNull(): shapes.append(o.Shape) if shapes: import Part comp = Part.makeCompound(shapes) obj.Shape = self.getProjected(obj, comp, obj.Projection) elif hasattr(obj.Base, 'Shape'): if not DraftVecUtils.isNull(obj.Projection): if obj.ProjectionMode == "Solid": obj.Shape = self.getProjected(obj, obj.Base.Shape, obj.Projection) elif obj.ProjectionMode == "Individual Faces": import Part if obj.FaceNumbers: faces = [] for i in obj.FaceNumbers: if len(obj.Base.Shape.Faces) > i: faces.append(obj.Base.Shape.Faces[i]) views = [] for f in faces: views.append( self.getProjected(obj, f, obj.Projection)) if views: obj.Shape = Part.makeCompound(views) else: App.Console.PrintWarning(obj.ProjectionMode + " mode not implemented\n") if not DraftGeomUtils.isNull(pl): obj.Placement = pl
def onChanged(self, obj, prop): if prop in ["Source", "RenderingMode", "ShowCut"]: import Part, DraftGeomUtils if hasattr(obj, "Source"): if obj.Source: if obj.Source.Objects: objs = Draft.getGroupContents(obj.Source.Objects, walls=True, addgroups=True) objs = Draft.removeHidden(objs) # separate spaces self.spaces = [] os = [] for o in objs: if Draft.getType(o) == "Space": self.spaces.append(o) else: os.append(o) objs = os self.svg = '' fillpattern = '<pattern id="sectionfill" patternUnits="userSpaceOnUse" patternTransform="matrix(5,0,0,5,0,0)"' fillpattern += ' x="0" y="0" width="10" height="10">' fillpattern += '<g>' fillpattern += '<rect width="10" height="10" style="stroke:none; fill:#ffffff" /><path style="stroke:#000000; stroke-width:1" d="M0,0 l10,10" /></g></pattern>' # generating SVG if obj.RenderingMode == "Solid": # render using the Arch Vector Renderer import ArchVRM, WorkingPlane wp = WorkingPlane.plane() wp.setFromPlacement(obj.Source.Placement) #wp.inverse() render = ArchVRM.Renderer() render.setWorkingPlane(wp) render.addObjects(objs) if hasattr(obj, "ShowCut"): render.cut(obj.Source.Shape, obj.ShowCut) else: render.cut(obj.Source.Shape) self.svg += '<g transform="scale(1,-1)">\n' self.svg += render.getViewSVG( linewidth="LWPlaceholder") self.svg += fillpattern self.svg += render.getSectionSVG( linewidth="SWPlaceholder", fillpattern="sectionfill") if hasattr(obj, "ShowCut"): if obj.ShowCut: self.svg += render.getHiddenSVG( linewidth="LWPlaceholder") self.svg += '</g>\n' # print render.info() else: # render using the Drawing module import Drawing, Part shapes = [] hshapes = [] sshapes = [] p = FreeCAD.Placement(obj.Source.Placement) self.direction = p.Rotation.multVec( FreeCAD.Vector(0, 0, 1)) for o in objs: if o.isDerivedFrom("Part::Feature"): if o.Shape.isNull(): pass #FreeCAD.Console.PrintWarning(translate("Arch","Skipping empty object: ")+o.Name) elif o.Shape.isValid(): if hasattr(obj.Source, "OnlySolids"): if obj.Source.OnlySolids: shapes.extend(o.Shape.Solids) else: shapes.append(o.Shape) else: shapes.extend(o.Shape.Solids) else: FreeCAD.Console.PrintWarning( translate( "Arch", "Skipping invalid object: ") + o.Name) cutface, cutvolume, invcutvolume = ArchCommands.getCutVolume( obj.Source.Shape.copy(), shapes) if cutvolume: nsh = [] for sh in shapes: for sol in sh.Solids: if sol.Volume < 0: sol.reverse() c = sol.cut(cutvolume) s = sol.section(cutface) try: wires = DraftGeomUtils.findWires( s.Edges) for w in wires: f = Part.Face(w) sshapes.append(f) #s = Part.Wire(s.Edges) #s = Part.Face(s) except Part.OCCError: #print "ArchDrawingView: unable to get a face" sshapes.append(s) nsh.extend(c.Solids) #sshapes.append(s) if hasattr(obj, "ShowCut"): if obj.ShowCut: c = sol.cut(invcutvolume) hshapes.append(c) shapes = nsh if shapes: self.shapes = shapes self.baseshape = Part.makeCompound(shapes) svgf = Drawing.projectToSVG( self.baseshape, self.direction) if svgf: svgf = svgf.replace( 'stroke-width="0.35"', 'stroke-width="LWPlaceholder"') svgf = svgf.replace( 'stroke-width="1"', 'stroke-width="LWPlaceholder"') svgf = svgf.replace( 'stroke-width:0.01', 'stroke-width:LWPlaceholder') self.svg += svgf if hshapes: hshapes = Part.makeCompound(hshapes) self.hiddenshape = hshapes svgh = Drawing.projectToSVG( hshapes, self.direction) if svgh: svgh = svgh.replace( 'stroke-width="0.35"', 'stroke-width="LWPlaceholder"') svgh = svgh.replace( 'stroke-width="1"', 'stroke-width="LWPlaceholder"') svgh = svgh.replace( 'stroke-width:0.01', 'stroke-width:LWPlaceholder') svgh = svgh.replace( 'fill="none"', 'fill="none"\nstroke-dasharray="DAPlaceholder"' ) self.svg += svgh if sshapes: svgs = "" if hasattr(obj, "ShowFill"): if obj.ShowFill: svgs += fillpattern svgs += '<g transform="rotate(180)">\n' for s in sshapes: if s.Edges: f = Draft.getSVG( s, direction=self.direction. negative(), linewidth=0, fillstyle="sectionfill", color=(0, 0, 0)) svgs += f svgs += "</g>\n" sshapes = Part.makeCompound(sshapes) self.sectionshape = sshapes svgs += Drawing.projectToSVG( sshapes, self.direction) if svgs: svgs = svgs.replace( 'stroke-width="0.35"', 'stroke-width="SWPlaceholder"') svgs = svgs.replace( 'stroke-width="1"', 'stroke-width="SWPlaceholder"') svgs = svgs.replace( 'stroke-width:0.01', 'stroke-width:SWPlaceholder') svgs = svgs.replace( 'stroke-width="0.35 px"', 'stroke-width="SWPlaceholder"') svgs = svgs.replace( 'stroke-width:0.35', 'stroke-width:SWPlaceholder') self.svg += svgs
def getCutShapes(objs,section,showHidden,groupSshapesByObject=False): import Part,DraftGeomUtils shapes = [] hshapes = [] sshapes = [] objectShapes = [] objectSshapes = [] for o in objs: if o.isDerivedFrom("Part::Feature"): if o.Shape.isNull(): pass elif section.OnlySolids: if o.Shape.isValid(): solids = [] solids.extend(o.Shape.Solids) shapes.extend(solids) objectShapes.append((o, solids)) else: print(section.Label,": Skipping invalid object:",o.Label) else: shapes.append(o.Shape) objectShapes.append((o, [o.Shape])) cutface,cutvolume,invcutvolume = ArchCommands.getCutVolume(section.Shape.copy(),shapes) if cutvolume: for o, shapeList in objectShapes: tmpSshapes = [] for sh in shapeList: for sol in sh.Solids: if sol.Volume < 0: sol.reverse() c = sol.cut(cutvolume) s = sol.section(cutface) try: wires = DraftGeomUtils.findWires(s.Edges) for w in wires: f = Part.Face(w) tmpSshapes.append(f) #s = Part.Wire(s.Edges) #s = Part.Face(s) except Part.OCCError: #print "ArchDrawingView: unable to get a face" tmpSshapes.append(s) shapes.extend(c.Solids) #sshapes.append(s) if showHidden: c = sol.cut(invcutvolume) hshapes.append(c) if len(tmpSshapes) > 0: sshapes.extend(tmpSshapes) if groupSshapesByObject: objectSshapes.append((o, tmpSshapes)) if groupSshapesByObject: return shapes,hshapes,sshapes,cutface,cutvolume,invcutvolume,objectSshapes else: return shapes,hshapes,sshapes,cutface,cutvolume,invcutvolume
def createGeometry(self, obj): import Part, DraftGeomUtils # getting default values height = width = length = 1 if hasattr(obj, "Length"): if obj.Length: length = obj.Length if hasattr(obj, "Width"): if obj.Width: width = obj.Width if hasattr(obj, "Height"): if obj.Height: height = obj.Height # creating base shape pl = obj.Placement base = None if obj.Base: if obj.Base.isDerivedFrom("Part::Feature"): if obj.Normal == Vector(0, 0, 0): p = FreeCAD.Placement(obj.Base.Placement) normal = p.Rotation.multVec(Vector(0, 0, 1)) else: normal = Vector(obj.Normal) normal = normal.multiply(height) base = obj.Base.Shape.copy() if base.Solids: pass elif base.Faces: base = base.extrude(normal) elif (len(base.Wires) == 1): if base.Wires[0].isClosed(): base = Part.Face(base.Wires[0]) base = base.extrude(normal) elif obj.Base.isDerivedFrom("Mesh::Feature"): if obj.Base.Mesh.isSolid(): if obj.Base.Mesh.countComponents() == 1: sh = ArchCommands.getShapeFromMesh(obj.Base.Mesh) if sh.isClosed() and sh.isValid() and sh.Solids: base = sh else: if obj.Normal == Vector(0, 0, 0): normal = Vector(0, 0, 1) else: normal = Vector(obj.Normal) normal = normal.multiply(height) l2 = length / 2 or 0.5 w2 = width / 2 or 0.5 v1 = Vector(-l2, -w2, 0) v2 = Vector(l2, -w2, 0) v3 = Vector(l2, w2, 0) v4 = Vector(-l2, w2, 0) base = Part.makePolygon([v1, v2, v3, v4, v1]) base = Part.Face(base) base = base.extrude(normal) base = self.processSubShapes(obj, base) if base: # applying axes pts = self.getAxisPoints(obj) apl = self.getAxisPlacement(obj) if pts: fsh = [] for i in range(len(pts)): if hasattr(obj, "Exclude"): if i in obj.Exclude: continue sh = base.copy() if apl: sh.Placement.Rotation = apl.Rotation sh.translate(pts[i]) fsh.append(sh) obj.Shape = Part.makeCompound(fsh) # finalizing else: if base: if not base.isNull(): if base.isValid() and base.Solids: if base.Volume < 0: base.reverse() if base.Volume < 0: FreeCAD.Console.PrintError( str( translate( "Arch", "Couldn't compute the wall shape")) ) return base = base.removeSplitter() obj.Shape = base if not DraftGeomUtils.isNull(pl): obj.Placement = pl
def computeAreas(self, obj): "computes the area properties" if not obj.Shape: return if obj.Shape.isNull(): return if not obj.Shape.isValid(): return if not obj.Shape.Faces: return import Drawing, Part a = 0 fset = [] for i, f in enumerate(obj.Shape.Faces): try: ang = f.normalAt(0, 0).getAngle(FreeCAD.Vector(0, 0, 1)) except Part.OCCError: print("Debug: Error computing areas for ", obj.Label, ": normalAt() Face ", i) return else: if (ang > 1.57) and (ang < 1.571): a += f.Area if ang < 1.5707: fset.append(f) if a and hasattr(obj, "VerticalArea"): if obj.VerticalArea.Value != a: obj.VerticalArea = a if fset and hasattr(obj, "HorizontalArea"): pset = [] for f in fset: if f.normalAt(0, 0).getAngle(FreeCAD.Vector(0, 0, 1)) < 0.00001: # already horizontal pset.append(f) else: try: pf = Part.Face( Part.Wire( Drawing.project(f, FreeCAD.Vector(0, 0, 1))[0].Edges)) except Part.OCCError: # error in computing the areas. Better set them to zero than show a wrong value if obj.HorizontalArea.Value != 0: print("Debug: Error computing areas for ", obj.Label, ": unable to project face: ", str([v.Point for v in f.Vertexes]), " (face normal:", f.normalAt(0, 0), ")") obj.HorizontalArea = 0 if hasattr(obj, "PerimeterLength"): if obj.PerimeterLength.Value != 0: obj.PerimeterLength = 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.HorizontalArea.Value != self.flatarea.Area: obj.HorizontalArea = self.flatarea.Area if hasattr(obj, "PerimeterLength") and (len( self.flatarea.Faces) == 1): if obj.PerimeterLength.Value != self.flatarea.Faces[ 0].OuterWire.Length: obj.PerimeterLength = self.flatarea.Faces[ 0].OuterWire.Length
def _getRegularWire(self): """_getRegularWire()... Private method to retrieve the extension area, pertaining to the feature and sub element provided at class instantiation, as a closed wire. If no closed wire is possible, a `None` value is returned.""" PathLog.track() length = self.length.Value if PathGeom.isRoughly(0, length) or not self.sub: PathLog.debug("no extension, length=%.2f, sub=%s" % (length, self.sub)) return None feature = self.obj.Shape.getElement(self.feature) edges = self._getEdges() sub = Part.Wire(Part.sortEdges(edges)[0]) if 1 == len(edges): PathLog.debug("Extending single edge wire") edge = edges[0] if Part.Circle == type(edge.Curve): circle = edge.Curve # for a circle we have to figure out if it's a hole or a cylinder p0 = edge.valueAt(edge.FirstParameter) normal = (edge.Curve.Center - p0).normalize() direction = self._getDirectedNormal(p0, normal) if direction is None: return None if PathGeom.pointsCoincide(normal, direction): r = circle.Radius - length else: r = circle.Radius + length # assuming the offset produces a valid circle - go for it if r > 0: e3 = Part.makeCircle( r, circle.Center, circle.Axis, edge.FirstParameter * 180 / math.pi, edge.LastParameter * 180 / math.pi, ) if endPoints(edge): # need to construct the arc slice e0 = Part.makeLine( edge.valueAt(edge.FirstParameter), e3.valueAt(e3.FirstParameter), ) e2 = Part.makeLine( edge.valueAt(edge.LastParameter), e3.valueAt(e3.LastParameter), ) return Part.Wire([e0, edge, e2, e3]) extWire = Part.Wire([e3]) self.extFaces = [self._makeCircularExtFace(edge, extWire)] return extWire # the extension is bigger than the hole - so let's just cover the whole hole if endPoints(edge): # if the resulting arc is smaller than the radius, create a pie slice PathLog.track() center = circle.Center e0 = Part.makeLine(center, edge.valueAt(edge.FirstParameter)) e2 = Part.makeLine(edge.valueAt(edge.LastParameter), center) return Part.Wire([e0, edge, e2]) PathLog.track() return Part.Wire([edge]) else: PathLog.track(self.feature, self.sub, type(edge.Curve), endPoints(edge)) direction = self._getDirection(sub) if direction is None: return None # return self._extendEdge(feature, edge, direction) return self._extendEdge(feature, edges[0], direction) elif sub.isClosed(): PathLog.debug("Extending multi-edge closed wire") subFace = Part.Face(sub) featFace = Part.Face(feature.Wires[0]) isOutside = True if not PathGeom.isRoughly(featFace.Area, subFace.Area): length = -1.0 * length isOutside = False try: off2D = sub.makeOffset2D(length) except FreeCAD.Base.FreeCADError as ee: PathLog.debug(ee) return None if isOutside: self.extFaces = [Part.Face(off2D).cut(featFace)] else: self.extFaces = [subFace.cut(Part.Face(off2D))] return off2D PathLog.debug("Extending multi-edge open wire") return extendWire(feature, sub, length)
def addtofreecad(self, doc=None, fcpar=None): def center(obj, x, y, z): obj.Placement = FreeCAD.Placement(\ FreeCAD.Vector(-x/2.0,-y/2.0,-z/2.0),\ FreeCAD.Rotation(0,0,0,1)) import FreeCAD, Part if not doc: doc = FreeCAD.newDocument() namel = self.name.lower() multifeature = { 'union': "Part::MultiFuse", 'imp_union': "Part::MultiFuse", 'intersection': "Part::MultiCommon" } if namel in multifeature: if len(self.children) > 1: obj = doc.addObject(multifeature[namel], namel) subobjs = [ child.addtofreecad(doc, obj) for child in self.children ] obj.Shapes = subobjs for subobj in subobjs: subobj.ViewObject.hide() elif len(self.children) == 1: obj = self.children[0].addtofreecad(doc, fcpar or True) else: obj = fcpar elif namel == 'difference': if len(self.children) == 1: obj = self.children[0].addtofreecad(doc, fcpar or True) else: obj = doc.addObject("Part::Cut", namel) base = self.children[0].addtofreecad(doc, obj) if len(self.children) == 2: tool = self.children[1].addtofreecad(doc, obj) else: tool = Node(name='imp_union',\ children=self.children[1:]).addtofreecad(doc,obj) obj.Base = base obj.Tool = tool base.ViewObject.hide() tool.ViewObject.hide() elif namel == 'cube': obj = doc.addObject('Part::Box', namel) x, y, z = self.arguments['size'] obj.Length = x obj.Width = y obj.Height = z if self.arguments['center']: center(obj, x, y, z) elif namel == 'sphere': obj = doc.addObject("Part::Sphere", namel) obj.Radius = self.arguments['r'] elif namel == 'cylinder': h = self.arguments['h'] r1, r2 = self.arguments['r1'], self.arguments['r2'] if '$fn' in self.arguments and self.arguments['$fn'] > 2 \ and self.arguments['$fn']<=Node.fnmin: # polygonal if r1 == r2: # prismatic obj = doc.addObject("Part::Prism", "prism") obj.Polygon = int(self.arguments['$fn']) obj.Circumradius = r1 obj.Height = h if self.arguments['center']: center(obj, 0, 0, h) #base.ViewObject.hide() elif False: #use Frustum Feature with makeRuledSurface obj = doc.addObject("Part::FeaturePython", 'frustum') Frustum(obj, r1, r2, int(self.arguments['$fn']), h) ViewProviderTree(obj.ViewObject) if self.arguments['center']: center(obj, 0, 0, h) else: #Use Part::Loft and GetWire Feature obj = doc.addObject('Part::Loft', 'frustum') import Draft p1 = Draft.makePolygon(int(self.arguments['$fn']), r1) p2 = Draft.makePolygon(int(self.arguments['$fn']), r2) if self.arguments['center']: p1.Placement = FreeCAD.Placement(\ FreeCAD.Vector(0.0,0.0,-h/2.0),FreeCAD.Rotation()) p2.Placement = FreeCAD.Placement(\ FreeCAD.Vector(0.0,0.0,h/2.0),FreeCAD.Rotation()) else: p2.Placement = FreeCAD.Placement(\ FreeCAD.Vector(0.0,0.0,h),FreeCAD.Rotation()) w1 = doc.addObject("Part::FeaturePython", 'polygonwire1') w2 = doc.addObject("Part::FeaturePython", 'polygonwire2') GetWire(w1, p1) GetWire(w2, p2) ViewProviderTree(w1.ViewObject) ViewProviderTree(w2.ViewObject) obj.Sections = [w1, w2] obj.Solid = True obj.Ruled = True p1.ViewObject.hide() p2.ViewObject.hide() w1.ViewObject.hide() w2.ViewObject.hide() else: if r1 == r2: obj = doc.addObject("Part::Cylinder", namel) obj.Height = h obj.Radius = r1 else: obj = doc.addObject("Part::Cone", 'cone') obj.Height = h obj.Radius1, obj.Radius2 = r1, r2 if self.arguments['center']: center(obj, 0, 0, h) elif namel == 'polyhedron': obj = doc.addObject("Part::Feature", namel) points = self.arguments['points'] faces = self.arguments['triangles'] shell=Part.Shell([Part.Face(Part.makePolygon(\ [tuple(points[pointindex]) for pointindex in \ (face+face[0:1])])) for face in faces]) # obj.Shape=Part.Solid(shell).removeSplitter() solid = Part.Solid(shell).removeSplitter() if solid.Volume < 0: # solid.complement() solid.reverse() obj.Shape = solid #.removeSplitter() elif namel == 'polygon': obj = doc.addObject("Part::Feature", namel) points = self.arguments['points'] paths = self.arguments.get('paths') if not paths: faces = [ Part.Face( Part.makePolygon([(x, y, 0) for x, y in points + points[0:1]])) ] else: faces= [Part.Face(Part.makePolygon([(points[pointindex][0],points[pointindex][1],0) for \ pointindex in (path+path[0:1])])) for path in paths] obj.Shape = subtractfaces(faces) elif namel == 'square': obj = doc.addObject("Part::Plane", namel) x, y = self.arguments['size'] obj.Length = x obj.Width = y if self.arguments['center']: center(obj, x, y, 0) elif namel == 'circle': r = self.arguments['r'] import Draft if '$fn' in self.arguments and self.arguments['$fn'] != 0 \ and self.arguments['$fn']<=Node.fnmin: obj = Draft.makePolygon(int(self.arguments['$fn']), r) else: obj = Draft.makeCircle(r) # create a Face #obj = doc.addObject("Part::Circle",namel);obj.Radius = r elif namel == 'color': if len(self.children) == 1: obj = self.children[0].addtofreecad(doc, fcpar or True) else: obj = Node(name='imp_union',\ children=self.children).addtofreecad(doc,fcpar or True) obj.ViewObject.ShapeColor = tuple( [float(p) for p in self.arguments[:3]]) #RGB transp = 100 - int(math.floor(100 * self.arguments[3])) #Alpha obj.ViewObject.Transparency = transp elif namel == 'multmatrix': assert (len(self.children) > 0) m1l = [round(f, 12) for f in sum(self.arguments, []) ] #Thats the original matrix m1 = FreeCAD.Matrix(*tuple(m1l)) #Thats the original matrix if isspecialorthogonalpython(fcsubmatrix( m1)): #a Placement can represent the transformation if len(self.children) == 1: obj = self.children[0].addtofreecad(doc, fcpar or True) else: obj = Node(name='imp_union',\ children=self.children).addtofreecad(doc,fcpar or True) #FreeCAD.Console.PrintMessage('obj %s\nmat %s/n' % (obj.Placement,m1)) obj.Placement = FreeCAD.Placement(m1).multiply(obj.Placement) else: #we need to apply the matrix transformation to the Shape using a custom PythonFeature obj = doc.addObject("Part::FeaturePython", namel) if len(self.children) == 1: child = self.children[0].addtofreecad(doc, obj) else: child = Node(name='imp_union',\ children=self.children).addtofreecad(doc,obj) MatrixTransform( obj, m1, child) #This object is not mutable from the GUI ViewProviderTree(obj.ViewObject) #elif namel == 'import': pass #Custom Feature elif namel == 'linear_extrude': height = self.arguments['height'] twist = self.arguments.get('twist') if not twist: obj = doc.addObject("Part::Extrusion", namel) else: #twist obj = doc.addObject("Part::FeaturePython", 'twist_extrude') if len(self.children) == 0: base = Node('import', self.arguments).addtofreecad(doc, obj) elif len(self.children) == 1: base = self.children[0].addtofreecad(doc, obj) else: base = Node(name='imp_union',\ children=self.children).addtofreecad(doc,obj) if False and base.isDerivedFrom('Part::MultiFuse'): #does not solve all the problems newobj = doc.addObject("Part::FeaturePython", 'refine') RefineShape(newobj, base) ViewProviderTree(newobj.ViewObject) base.ViewObject.hide() base = newobj if not twist: obj.Base = base obj.Dir = (0, 0, height) else: #twist Twist(obj, base, height, -twist) ViewProviderTree(obj.ViewObject) if self.arguments['center']: center(obj, 0, 0, height) base.ViewObject.hide() elif namel == 'rotate_extrude': obj = doc.addObject("Part::Revolution", namel) if len(self.children) == 0: base = Node('import', self.arguments).addtofreecad(doc, obj) elif len(self.children) == 1: base = self.children[0].addtofreecad(doc, obj) else: base = Node(name='imp_union',\ children=self.children).addtofreecad(doc,obj) if False and base.isDerivedFrom('Part::MultiFuse'): #creates 'Axe and meridian are confused' Errors newobj = doc.addObject("Part::FeaturePython", 'refine') RefineShape(newobj, base) ViewProviderTree(newobj.ViewObject) base.ViewObject.hide() base = newobj obj.Source = base obj.Axis = (0.00, 1.00, 0.00) obj.Base = (0.00, 0.00, 0.00) obj.Angle = 360.00 base.ViewObject.hide() obj.Placement = FreeCAD.Placement(FreeCAD.Vector(), FreeCAD.Rotation(0, 0, 90)) elif namel == 'projection': if self.arguments['cut']: planename = 'xy_plane_used_for_project_cut' obj = doc.addObject('Part::MultiCommon', 'projection_cut') plane = doc.getObject(planename) if not plane: plane = doc.addObject("Part::Plane", planename) plane.Length = Node.planedim * 2 plane.Width = Node.planedim * 2 plane.Placement = FreeCAD.Placement(FreeCAD.Vector(\ -Node.planedim,-Node.planedim,0),FreeCAD.Rotation(0,0,0,1)) #plane.ViewObject.hide() subobjs = [ child.addtofreecad(doc, obj) for child in self.children ] subobjs.append(plane) obj.Shapes = subobjs for subobj in subobjs: subobj.ViewObject.hide() else: #Do a proper projection raise (NotImplementedError) elif namel == 'import': filename = self.arguments.get('file') scale = self.arguments.get('scale') origin = self.arguments.get('origin') if filename: import os docname = os.path.split(filename)[1] objname, extension = docname.split('.', 1) if not os.path.isabs(filename): try: global lastimportpath filename = os.path.join(lastimportpath, filename) except: raise #no path given # Check for a mesh fileformat support by the Mesh mddule if extension.lower() in reverseimporttypes()['Mesh']: import Mesh mesh1 = doc.getObject(objname) #reuse imported object if not mesh1: Mesh.insert(filename) mesh1 = doc.getObject(objname) mesh1.ViewObject.hide() sh = Part.Shape() sh.makeShapeFromMesh(mesh1.Mesh.Topology, 0.1) solid = Part.Solid(sh) obj = doc.addObject("Part::FeaturePython", 'import_%s_%s' % (extension, objname)) #obj=doc.addObject('Part::Feature',) ImportObject( obj, mesh1) #This object is not mutable from the GUI ViewProviderTree(obj.ViewObject) solid = solid.removeSplitter() if solid.Volume < 0: #sh.reverse() #sh = sh.copy() solid.complement() obj.Shape = solid #.removeSplitter() elif extension in ['dxf']: layera = self.arguments.get('layer') featname = 'import_dxf_%s_%s' % (objname, layera) # reusing an allready imported object does not work if the #shape in not yet calculated import importDXF global dxfcache layers = dxfcache.get(id(doc), []) if layers: groupobj = [ go for go in layers if (not layera) or go.Label == layera ] else: groupobj = None if not groupobj: groupname = objname layers = importDXF.processdxf( doc, filename) or importDXF.layers dxfcache[id(doc)] = layers[:] for l in layers: for o in l.Group: o.ViewObject.hide() l.ViewObject.hide() groupobj = [ go for go in layers if (not layera) or go.Label == layera ] edges = [] for shapeobj in groupobj[0].Group: edges.extend(shapeobj.Shape.Edges) try: f = edgestofaces(edges) except Part.OCCError: FreeCAD.Console.PrintError(\ 'processing of dxf import faild\nPlease rework \'%s\' manualy\n' % layera) f = Part.Shape() #empty Shape obj = doc.addObject("Part::FeaturePython", 'import_dxf_%s_%s' % (objname, layera)) #obj=doc.addObject('Part::Feature',) ImportObject( obj, groupobj[0]) #This object is not mutable from the GUI ViewProviderTree(obj.ViewObject) obj.Shape = f else: FreeCAD.Console.ErrorMessage(\ 'Filetype of %s not supported\n' % (filename)) raise (NotImplementedError) if obj: #handle origin and scale if scale is not None and scale != 1: if origin is not None and any([c != 0 for c in origin]): raise (NotImplementedError ) # order of transformations unkown child = obj m1 = FreeCAD.Matrix() m1.scale(scale, scale, scale) obj = doc.addObject("Part::FeaturePython", 'scale_import') MatrixTransform( obj, m1, child) #This object is not mutable from the GUI ViewProviderTree(obj.ViewObject) elif origin is not None and any([c != 0 for c in origin]): placement = FreeCAD.Placement( FreeCAD.Vector(*[-c for c in origin]), FreeCAD.Rotation()) obj.Placement = placement.multiply(obj.Placement) else: FreeCAD.Console.ErrorMessage('Import of %s failed\n' % (filename)) elif namel == 'minkowski': childrennames = [child.name.lower() for child in self.children] if len(self.children) == 2 and \ childrennames.count('cube')==1 and \ (childrennames.count('sphere') + \ childrennames.count('cylinder')) == 1: if self.children[0].name.lower() == 'cube': cube = self.children[0] roundobj = self.children[1] elif self.children[1].name.lower() == 'cube': cube = self.children[1] roundobj = self.children[0] roundobjname = roundobj.name.lower() issphere = roundobjname == 'sphere' cubeobj = doc.addObject('Part::Box', 'roundedcube') x, y, z = cube.arguments['size'] r=roundobj.arguments.get('r') or \ roundobj.arguments.get('r1') cubeobj.Length = x + 2 * r cubeobj.Width = y + 2 * r cubeobj.Height = z + 2 * r * issphere obj = doc.addObject("Part::Fillet", "%s_%s" % (namel, roundobjname)) obj.Base = cubeobj cubeobj.ViewObject.hide() if issphere: obj.Edges = [(i, r, r) for i in range(1, 13)] else: #cylinder obj.Edges = [(i, r, r) for i in [1, 3, 5, 7]] if cube.arguments['center']: center(cubeobj, x + 2 * r, y + 2 * r, z + 2 * r * issphere) else: #htandle a rotated cylinder #OffsetShape raise (NotImplementedError) elif childrennames.count('sphere') == 1: sphereindex = childrennames.index('sphere') sphere = self.children[sphereindex] offset = sphere.arguments['r'] nonsphere=self.children[0:sphereindex]+\ self.sphere[sphereindex+1:] obj = doc.addObject("Part::FeaturePython", 'Offset') if len(nonsphere) == 1: child = nonsphere[0].addtofreecad(doc, obj) else: child = Node(name='imp_union',\ children=nonsphere).addtofreecad(doc,obj) OffsetShape(obj, child, offset) ViewProviderTree(obj.ViewObject) elif False: raise (NotImplementedError) pass # handle rotated cylinders and select edges that #radius = radius0 * m1.multiply(FreeCAD.Vector(0,0,1)).dot(edge.Curve.tangent(0)[0]) else: raise (NotImplementedError) elif namel == 'surface': obj = doc.addObject("Part::Feature", namel) #include filename? obj.Shape, xoff, yoff = makeSurfaceVolume(self.arguments['file']) if self.arguments['center']: center(obj, xoff, yoff, 0.0) return obj #import os #scadstr = 'surface(file = "%s", center = %s );' % \ # (self.arguments['file'], 'true' if self.arguments['center'] else 'false') #docname=os.path.split(self.arguments['file'])[1] #objname,extension = docname.split('.',1) #obj = openscadmesh(doc,scadstr,objname) elif namel in ['glide', 'hull']: raise (NotImplementedError) elif namel in ['render', 'subdiv'] or True: lenchld = len(self.children) if lenchld == 1: FreeCAD.Console.PrintMessage('Not recognized %s\n' % (self)) obj = self.children[0].addtofreecad(doc, fcpar) elif lenchld > 1: obj = Node(name='imp_union',\ children=self.children).addtofreecad(doc,fcpar or True) else: obj = doc.addObject("Part::Feature", 'Not_Impl_%s' % namel) if fcpar == True: #We are the last real object, our parent is not rendered. return obj if fcpar: try: obj.ViewObject.hide() except: raise if True: #never refine the Shape, as it itroduces crashes return obj else: #refine Shape import Draft if obj.Type =='Part::Extrusion' and obj.Base.Type == 'Part::Part2DObjectPython' and \ isinstance(obj.Base.Proxy,Draft._Polygon) or \ (not obj.isDerivedFrom('Part::Extrusion') and \ not obj.isDerivedFrom('Part::Boolean') and \ not obj.isDerivedFrom('Part::Cut') and \ not obj.isDerivedFrom('Part::MultiCommon') and \ not obj.isDerivedFrom('Part::MultiFuse') and \ not obj.isDerivedFrom('Part::Revolution') ) \ or (obj.isDerivedFrom('Part::FeaturePython') and isinstance(obj.Proxy,RefineShape)): return obj else: newobj = doc.addObject("Part::FeaturePython", 'refine') RefineShape(newobj, obj) ViewProviderTree(newobj.ViewObject) obj.ViewObject.hide() return newobj else: doc.recompute()
def execute(self, obj): import Part, math, DraftGeomUtils pl = obj.Placement self.baseface = None base = None if obj.Base and obj.Angles: w = None if obj.Base.isDerivedFrom("Part::Feature"): if (obj.Base.Shape.Faces and obj.Face): w = obj.Base.Shape.Faces[obj.Face - 1].Wires[0] elif obj.Base.Shape.Wires: w = obj.Base.Shape.Wires[0] if w: if w.isClosed(): self.profilsDico = [] self.shps = [] self.subVolshps = [] heights = [] edges = DraftGeomUtils.sortEdges(w.Edges) l = len(edges) print("le contour contient " + str(l) + " aretes") for i in range(l): self.makeRoofProfilsDic(i, obj.Angles[i], obj.Runs[i], obj.IdRel[i], obj.Overhang[i], obj.Thickness[i]) for i in range(l): self.calcMissingData(i) for i in range(l): self.calcEdgeGeometry(edges, i) for i in range(l): self.calcDraftEdges(i) for i in range(l): self.calcEave(i) for p in self.profilsDico: heights.append(p["height"]) obj.Heights = heights for i in range(l): self.getRoofPaneProject(i) profilCurrent = self.findProfil(i) midpoint = DraftGeomUtils.findMidpoint( profilCurrent["edge"]) ptsPaneProject = profilCurrent["points"] lp = len(ptsPaneProject) if lp != 0: #print FreeCAD.Vector(ptsPaneProject[0]) ptsPaneProject.append(ptsPaneProject[0]) edgesWire = [] for p in range(lp): edge = Part.makeLine(ptsPaneProject[p], ptsPaneProject[p + 1]) edgesWire.append(edge) wire = Part.Wire(edgesWire) d = wire.BoundBox.DiagonalLength thicknessV = profilCurrent["thickness"] / ( math.cos(math.radians(profilCurrent["angle"]))) overhangV = profilCurrent["overhang"] * math.tan( math.radians(profilCurrent["angle"])) if wire.isClosed(): f = Part.Face(wire) #Part.show(f) f = f.extrude( FreeCAD.Vector( 0, 0, profilCurrent["height"] + 2 * thicknessV + 2 * overhangV)) f.translate( FreeCAD.Vector(0.0, 0.0, -2 * overhangV)) ptsPaneProfil = [ FreeCAD.Vector(-profilCurrent["overhang"], -overhangV, 0.0), FreeCAD.Vector(profilCurrent["run"], profilCurrent["height"], 0.0), FreeCAD.Vector( profilCurrent["run"], profilCurrent["height"] + thicknessV, 0.0), FreeCAD.Vector(-profilCurrent["overhang"], -overhangV + thicknessV, 0.0) ] self.createProfilShape(ptsPaneProfil, midpoint, profilCurrent["rot"], profilCurrent["vec"], profilCurrent["run"], d, self.shps, f) ## subVolume shape ptsSubVolumeProfil = [ FreeCAD.Vector(-profilCurrent["overhang"], -overhangV, 0.0), FreeCAD.Vector(profilCurrent["run"], profilCurrent["height"], 0.0), FreeCAD.Vector(profilCurrent["run"], profilCurrent["height"] + 10000, 0.0), FreeCAD.Vector(0.0, profilCurrent["height"] + 10000, 0.0) ] self.createProfilShape(ptsSubVolumeProfil, midpoint, profilCurrent["rot"], profilCurrent["vec"], profilCurrent["run"], d, self.subVolshps, f) ## SubVolume self.sub = self.subVolshps.pop() for s in self.subVolshps: self.sub = self.sub.fuse(s) self.sub = self.sub.removeSplitter() if not self.sub.isNull(): if not DraftGeomUtils.isNull(pl): self.sub.Placement = pl ## BaseVolume base = Part.makeCompound(self.shps) if not base.isNull(): if not DraftGeomUtils.isNull(pl): base.Placement = pl base = self.processSubShapes(obj, base) if base: if not base.isNull(): obj.Shape = base
def run(self): """run(): Runs a nesting operation. Returns a list of lists of shapes, each primary list being one filled container, or None if the operation failed.""" # reset abort mechanism and variables self.running = True self.progress = 0 starttime = datetime.now() # general conformity tests print("Executing conformity tests ... ", end="") if not self.container: print("Empty container. Aborting") return if not self.shapes: print("Empty shapes. Aborting") return if not isinstance(self.container, Part.Face): print("Container is not a face. Aborting") return normal = self.container.normalAt(0, 0) for s in self.shapes: if not self.update(): return if len(s.Faces) != 1: print( "One of the shapes does not contain exactly one face. Aborting" ) return # check if all faces correctly oriented (same normal) if s.Faces[0].normalAt(0, 0).getAngle(normal) > TOLERANCE: # let pass faces with inverted normal if s.Faces[0].normalAt( 0, 0).getAngle(normal) - math.pi > TOLERANCE: print( "One of the face doesn't have the same orientation as the container. Aborting" ) return # TODO # allow to use a non-rectangular container # manage margins/paddings # allow to prevent or force specific rotations for a piece # LONG-TERM TODO # add genetic algo to swap pieces, and check if the result is better # track progresses step = 100.0 / (len(self.shapes) * len(ROTATIONS)) # store hashCode together with the face so we can change the order # and still identify the original face, so we can calculate a transform afterwards self.indexedfaces = [[shape.hashCode(), shape] for shape in self.shapes] # build a clean copy so we don't touch the original faces = list(self.indexedfaces) # replace shapes by their face faces = [[f[0], f[1].Faces[0]] for f in faces] # order by area faces = sorted(faces, key=lambda face: face[1].Area) # discretize non-linear edges and remove holes nfaces = [] for face in faces: if not self.update(): return nedges = [] allLines = True for edge in face[1].OuterWire.OrderedEdges: if isinstance(edge.Curve, (Part.LineSegment, Part.Line)): nedges.append(edge) else: allLines = False last = edge.Vertexes[0].Point for i in range(DISCRETIZE): s = float(i + 1) / DISCRETIZE par = edge.FirstParameter + (edge.LastParameter - edge.FirstParameter) * s new = edge.valueAt(par) nedges.append(Part.LineSegment(last, new).toShape()) last = new f = Part.Face(Part.Wire(nedges)) if not f.isValid(): if allLines: print("Invalid face found in set. Aborting") else: print("Face distretizing failed. Aborting") return nfaces.append([face[0], f]) faces = nfaces # container for sheets with a first, empty sheet sheets = [[]] print("Everything OK (", datetime.now() - starttime, ")") # main loop facenumber = 1 facesnumber = len(faces) #print("Vertices per face:",[len(face[1].Vertexes) for face in faces]) while faces: print("Placing piece", facenumber, "/", facesnumber, "Area:", FreeCAD.Units.Quantity( faces[-1][1].Area, FreeCAD.Units.Area).getUserPreferred()[0], ": ", end="") face = faces.pop() boc = self.container.BoundBox # this stores the available solutions for each rotation of a piece # contains [sheetnumber,face,xlength] lists, # face being [hascode,transformed face] and xlength # the X size of all boundboxes of placed pieces available = [] # this stores the possible positions on a blank # sheet, in case we need to create a new one initials = [] # this checks if the piece don't fit in the container unfit = True for rotation in ROTATIONS: if not self.update(): return self.progress += step print(rotation, ", ", end="") hashcode = face[0] rotface = face[1].copy() if rotation: rotface.rotate(rotface.CenterOfMass, normal, rotation) bof = rotface.BoundBox rotverts = self.order(rotface) #for i,v in enumerate(rotverts): # Draft.makeText([str(i)],point=v) basepoint = rotverts[0] # leftmost point of the rotated face basecorner = boc.getPoint( 0) # lower left corner of the container # See if the piece fits in the container dimensions if (bof.XLength < boc.XLength) and (bof.YLength < boc.YLength): unfit = False # Get the fit polygon of the container # that is, the polygon inside which basepoint can # circulate, and the face still be fully inside the container v1 = basecorner.add(basepoint.sub(bof.getPoint(0))) v2 = v1.add(FreeCAD.Vector(0, boc.YLength - bof.YLength, 0)) v3 = v2.add(FreeCAD.Vector(boc.XLength - bof.XLength, 0, 0)) v4 = v3.add(FreeCAD.Vector(0, -(boc.YLength - bof.YLength), 0)) binpol = Part.Face(Part.makePolygon([v1, v2, v3, v4, v1])) initials.append([binpol, [hashcode, rotface], basepoint]) # check for available space on each existing sheet for sheetnumber, sheet in enumerate(sheets): # Get the no-fit polygon for each already placed face in # current sheet. That is, a polygon in which basepoint # cannot be, if we want our face to not overlap with the # placed face. # To do this, we "circulate" the face around the placed face if not self.update(): return nofitpol = [] for placed in sheet: pts = [] pi = 0 for placedvert in self.order(placed[1], right=True): fpts = [] for i, rotvert in enumerate(rotverts): if not self.update(): return facecopy = rotface.copy() facecopy.translate(placedvert.sub(rotvert)) # test if all the points of the face are outside the # placed face (except the base point, which is coincident) outside = True faceverts = self.order(facecopy) for vert in faceverts: if (vert.sub(placedvert) ).Length > TOLERANCE: if placed[1].isInside( vert, TOLERANCE, True): outside = False break # also need to test for edge intersection, because even # if all vertices are outside, the pieces could still # overlap if outside: for e1 in facecopy.OuterWire.Edges: for e2 in placed[1].OuterWire.Edges: if not self.update(): return if True: # Draft code (SLOW) p = DraftGeomUtils.findIntersection( e1, e2) if p: p = p[0] p1 = e1.Vertexes[0].Point p2 = e1.Vertexes[1].Point p3 = e2.Vertexes[0].Point p4 = e2.Vertexes[1].Point if (p.sub(p1).Length > TOLERANCE) and (p.sub(p2).Length > TOLERANCE) \ and (p.sub(p3).Length > TOLERANCE) and (p.sub(p4).Length > TOLERANCE): outside = False break else: # alt code: using distToShape (EVEN SLOWER!) p = e1.distToShape(e2) if p: if p[0] < TOLERANCE: # allow vertex-to-vertex intersection if (p[2][0][0] != "Vertex" ) or (p[2][0][3] != "Vertex"): outside = False break if outside: fpts.append([faceverts[0], i]) #Draft.makeText([str(i)],point=faceverts[0]) # reorder available solutions around a same point if needed # ensure they are in the correct order idxs = [p[1] for p in fpts] if (0 in idxs) and (len(faceverts) - 1 in idxs): slicepoint = len(fpts) last = len(faceverts) for p in reversed(fpts): if p[1] == last - 1: slicepoint -= 1 last -= 1 else: break fpts = fpts[slicepoint:] + fpts[:slicepoint] #print(fpts) pts.extend(fpts) # create the polygon if len(pts) < 3: print( "Error calculating a no-fit polygon. Aborting") return pts = [p[0] for p in pts] pol = Part.Face(Part.makePolygon(pts + [pts[0]])) if not pol.isValid(): # fix overlapping edges overlap = True while overlap: overlap = False for i in range(len(pol.OuterWire.Edges) - 1): if not self.update(): return v1 = DraftGeomUtils.vec( pol.OuterWire.OrderedEdges[i]) v2 = DraftGeomUtils.vec( pol.OuterWire.OrderedEdges[i + 1]) if abs(v1.getAngle(v2) - math.pi) <= TOLERANCE: overlap = True ne = Part.LineSegment( pol.OuterWire.OrderedEdges[i]. Vertexes[0].Point, pol.OuterWire.OrderedEdges[i + 1]. Vertexes[-1].Point).toShape() pol = Part.Face( Part.Wire(pol.OuterWire. OrderedEdges[:i] + [ne] + pol.OuterWire. OrderedEdges[i + 2:])) break if not pol.isValid(): # trying basic OCC fix pol.fix(0, 0, 0) if pol.isValid(): if pol.ShapeType == "Face": pol = Part.Face( pol.OuterWire ) # discard possible inner holes elif pol.Faces: # several faces after the fix, keep the biggest one a = 0 ff = None for f in pol.Faces: if f.Area > a: a = f.Area ff = f if ff: pol = ff else: print( "Unable to fix invalid no-fit polygon. Aborting" ) Part.show(pol) return if not pol.isValid(): # none of the fixes worked. Epic fail. print("Invalid no-fit polygon. Aborting") Part.show(pol.OuterWire) for p in sheet: Part.show(p[1]) Part.show(facecopy) #for i,p in enumerate(faceverts): # Draft.makeText([str(i)],point=p) return if pol.isValid(): nofitpol.append(pol) #Part.show(pol) # Union all the no-fit pols into one if len(nofitpol) == 1: nofitpol = nofitpol[0] elif len(nofitpol) > 1: b = nofitpol.pop() for n in nofitpol: if not self.update(): return b = b.fuse(n) nofitpol = b # remove internal edges (discard edges shared by 2 faces) lut = {} for f in fitpol.Faces: for e in f.Edges: h = e.hashCode() if h in lut: lut[h].append(e) else: lut[h] = [e] edges = [e[0] for e in lut.values() if len(e) == 1] try: pol = Part.Face(Part.Wire(edges)) except Exception: # above method can fail sometimes. Try a slower method w = DraftGeomUtils.findWires(edges) if len(w) == 1: if w[0].isClosed(): try: pol = Part.Face(w[0]) except Exception: print( "Error merging polygons. Aborting") try: Part.show(Part.Wire(edges)) except Exception: for e in edges: Part.show(e) return # subtract the no-fit polygon from the container's fit polygon # we then have the zone where the face can be placed if nofitpol: fitpol = binpol.cut(nofitpol) else: fitpol = binpol.copy() # check that we have some space on this sheet if (fitpol.Area > 0) and fitpol.Vertexes: # order the fitpol vertexes by smallest X # and try to place the piece, making sure it doesn't # intersect with already placed pieces fitverts = sorted([v.Point for v in fitpol.Vertexes], key=lambda v: v.x) for p in fitverts: if not self.update(): return trface = rotface.copy() trface.translate(p.sub(basepoint)) ok = True for placed in sheet: if ok: for vert in trface.Vertexes: if placed[1].isInside( vert.Point, TOLERANCE, False): ok = False break if ok: for e1 in trface.OuterWire.Edges: for e2 in placed[1].OuterWire.Edges: p = DraftGeomUtils.findIntersection( e1, e2) if p: p = p[0] p1 = e1.Vertexes[0].Point p2 = e1.Vertexes[1].Point p3 = e2.Vertexes[0].Point p4 = e2.Vertexes[1].Point if (p.sub(p1).Length > TOLERANCE) and (p.sub(p2).Length > TOLERANCE) \ and (p.sub(p3).Length > TOLERANCE) and (p.sub(p4).Length > TOLERANCE): ok = False break if not ok: break if ok: rotface = trface break else: print( "Couldn't determine location on sheet. Aborting" ) return # check the X space occupied by this solution bb = rotface.BoundBox for placed in sheet: bb.add(placed[1].BoundBox) available.append([ sheetnumber, [hashcode, rotface], bb.XMax, fitpol ]) if unfit: print("One face doesn't fit in the container. Aborting") return if available: # order by smallest X size and take the first one available = sorted(available, key=lambda sol: sol[2]) print("Adding piece to sheet", available[0][0] + 1) sheets[available[0][0]].append(available[0][1]) #Part.show(available[0][3]) else: # adding to the leftmost vertex of the binpol sheet = [] print("Creating new sheet, adding piece to sheet", len(sheets)) # order initial positions by smallest X size initials = sorted(initials, key=lambda sol: sol[1][1].BoundBox.XLength) hashcode = initials[0][1][0] face = initials[0][1][1] # order binpol vertexes by X coord verts = sorted([v.Point for v in initials[0][0].Vertexes], key=lambda v: v.x) face.translate(verts[0].sub(initials[0][2])) sheet.append([hashcode, face]) sheets.append(sheet) facenumber += 1 print("Run time:", datetime.now() - starttime) self.results.append(sheets) return sheets
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.warning(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
def makeStraightLanding(self, obj, edge, numberofsteps=None): "builds a landing from a straight edge" # general data if not numberofsteps: numberofsteps = obj.NumberOfSteps import Part, DraftGeomUtils v = DraftGeomUtils.vec(edge) vLength = Vector(v.x, v.y, 0) vWidth = vWidth = DraftVecUtils.scaleTo(vLength.cross(Vector(0, 0, 1)), obj.Width) vBase = edge.Vertexes[0].Point vNose = DraftVecUtils.scaleTo(vLength, -abs(obj.Nosing)) h = obj.Height l = obj.Length if obj.Base: if obj.Base.isDerivedFrom("Part::Feature"): l = obj.Base.Shape.Length if obj.Base.Shape.BoundBox.ZLength: h = obj.Base.Shape.BoundBox.ZLength fLength = float(l - obj.Width) / (numberofsteps - 2) fHeight = float(h) / numberofsteps a = math.atan(fHeight / fLength) print "landing data:", fLength, ":", fHeight # step p1 = self.align(vBase, obj.Align, vWidth) p1 = p1.add(vNose).add(Vector(0, 0, -abs(obj.TreadThickness))) p2 = p1.add(DraftVecUtils.neg(vNose)).add(vLength) p3 = p2.add(vWidth) p4 = p3.add(DraftVecUtils.neg(vLength)).add(vNose) step = Part.Face(Part.makePolygon([p1, p2, p3, p4, p1])) if obj.TreadThickness: step = step.extrude(Vector(0, 0, abs(obj.TreadThickness))) self.steps.append(step) # structure lProfile = [] struct = None p7 = None p1 = p1.add(DraftVecUtils.neg(vNose)) p2 = p1.add(Vector(0, 0, -fHeight)).add( Vector(0, 0, -obj.StructureThickness / math.cos(a))) resheight = p1.sub(p2).Length - obj.StructureThickness reslength = resheight / math.tan(a) p3 = p2.add(DraftVecUtils.scaleTo(vLength, reslength)).add( Vector(0, 0, resheight)) p6 = p1.add(vLength) if obj.TreadThickness: p7 = p6.add(Vector(0, 0, obj.TreadThickness)) reslength = fLength + (obj.StructureThickness / math.sin(a) - (fHeight - obj.TreadThickness) / math.tan(a)) if p7: p5 = p7.add(DraftVecUtils.scaleTo(vLength, reslength)) else: p5 = p6.add(DraftVecUtils.scaleTo(vLength, reslength)) resheight = obj.StructureThickness + obj.TreadThickness reslength = resheight / math.tan(a) p4 = p5.add(DraftVecUtils.scaleTo(vLength, -reslength)).add( Vector(0, 0, -resheight)) if obj.Structure == "Massive": if obj.StructureThickness: if p7: struct = Part.Face( Part.makePolygon([p1, p2, p3, p4, p5, p7, p6, p1])) else: struct = Part.Face( Part.makePolygon([p1, p2, p3, p4, p5, p6, p1])) evec = vWidth if obj.StructureOffset: mvec = DraftVecUtils.scaleTo(vWidth, obj.StructureOffset) struct.translate(mvec) evec = DraftVecUtils.scaleTo( evec, evec.Length - (2 * mvec.Length)) struct = struct.extrude(evec) elif obj.Structure in ["One stringer", "Two stringers"]: if obj.StringerWidth and obj.StructureThickness: p1b = p1.add(Vector(0, 0, -fHeight)) reslength = fHeight / math.tan(a) p1c = p1.add(DraftVecUtils.scaleTo(vLength, reslength)) p5b = None p5c = None if obj.TreadThickness: reslength = obj.StructureThickness / math.sin(a) p5b = p5.add(DraftVecUtils.scaleTo(vLength, -reslength)) reslength = obj.TreadThickness / math.tan(a) p5c = p5b.add(DraftVecUtils.scaleTo( vLength, -reslength)).add(Vector(0, 0, -obj.TreadThickness)) pol = Part.Face( Part.makePolygon( [p1c, p1b, p2, p3, p4, p5, p5b, p5c, p1c])) else: pol = Part.Face( Part.makePolygon([p1c, p1b, p2, p3, p4, p5, p1c])) evec = DraftVecUtils.scaleTo(vWidth, obj.StringerWidth) if obj.Structure == "One stringer": if obj.StructureOffset: mvec = DraftVecUtils.scaleTo(vWidth, obj.StructureOffset) else: mvec = DraftVecUtils.scaleTo(vWidth, (vWidth.Length / 2) - obj.StringerWidth / 2) pol.translate(mvec) struct = pol.extrude(evec) elif obj.Structure == "Two stringers": pol2 = pol.copy() if obj.StructureOffset: mvec = DraftVecUtils.scaleTo(vWidth, obj.StructureOffset) pol.translate(mvec) mvec = vWidth.add(mvec.negative()) pol2.translate(mvec) else: pol2.translate(vWidth) s1 = pol.extrude(evec) s2 = pol2.extrude(evec.negative()) struct = Part.makeCompound([s1, s2]) if struct: self.structures.append(struct)
def computeAreas(self,obj): 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 Drawing,Part pset = [] for f in fset: try: pf = Part.Face(Part.Wire(Drawing.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
def makeStraightStairs(self, obj, edge, numberofsteps=None): "builds a simple, straight staircase from a straight edge" # general data import Part, DraftGeomUtils if not numberofsteps: numberofsteps = obj.NumberOfSteps v = DraftGeomUtils.vec(edge) vLength = DraftVecUtils.scaleTo( v, float(edge.Length) / (numberofsteps - 1)) vLength = Vector(vLength.x, vLength.y, 0) if round(v.z, Draft.precision()) != 0: h = v.z else: h = obj.Height vHeight = Vector(0, 0, float(h) / numberofsteps) vWidth = DraftVecUtils.scaleTo(vLength.cross(Vector(0, 0, 1)), obj.Width) vBase = edge.Vertexes[0].Point vNose = DraftVecUtils.scaleTo(vLength, -abs(obj.Nosing)) a = math.atan(vHeight.Length / vLength.Length) print "stair data:", vLength.Length, ":", vHeight.Length # steps for i in range(numberofsteps - 1): p1 = vBase.add((Vector(vLength).multiply(i)).add( Vector(vHeight).multiply(i + 1))) p1 = self.align(p1, obj.Align, vWidth) p1 = p1.add(vNose).add(Vector(0, 0, -abs(obj.TreadThickness))) p2 = p1.add(DraftVecUtils.neg(vNose)).add(vLength) p3 = p2.add(vWidth) p4 = p3.add(DraftVecUtils.neg(vLength)).add(vNose) step = Part.Face(Part.makePolygon([p1, p2, p3, p4, p1])) if obj.TreadThickness: step = step.extrude(Vector(0, 0, abs(obj.TreadThickness))) self.steps.append(step) # structure lProfile = [] struct = None if obj.Structure == "Massive": if obj.StructureThickness: for i in range(numberofsteps - 1): if not lProfile: lProfile.append(vBase) last = lProfile[-1] if len(lProfile) == 1: last = last.add(Vector(0, 0, -abs(obj.TreadThickness))) lProfile.append(last.add(vHeight)) lProfile.append(lProfile[-1].add(vLength)) resHeight1 = obj.StructureThickness / math.cos(a) lProfile.append(lProfile[-1].add(Vector(0, 0, -resHeight1))) resHeight2 = ((numberofsteps - 1) * vHeight.Length) - ( resHeight1 + obj.TreadThickness) resLength = (vLength.Length / vHeight.Length) * resHeight2 h = DraftVecUtils.scaleTo(vLength, -resLength) lProfile.append(lProfile[-1].add(Vector(h.x, h.y, -resHeight2))) lProfile.append(vBase) #print lProfile pol = Part.makePolygon(lProfile) struct = Part.Face(pol) evec = vWidth if obj.StructureOffset: mvec = DraftVecUtils.scaleTo(vWidth, obj.StructureOffset) struct.translate(mvec) evec = DraftVecUtils.scaleTo( evec, evec.Length - (2 * mvec.Length)) struct = struct.extrude(evec) elif obj.Structure in ["One stringer", "Two stringers"]: if obj.StringerWidth and obj.StructureThickness: hyp = math.sqrt(vHeight.Length**2 + vLength.Length**2) l1 = Vector(vLength).multiply(numberofsteps - 1) h1 = Vector(vHeight).multiply(numberofsteps - 1).add( Vector(0, 0, -abs(obj.TreadThickness))) p1 = vBase.add(l1).add(h1) p1 = self.align(p1, obj.Align, vWidth) lProfile.append(p1) h2 = (obj.StructureThickness / vLength.Length) * hyp lProfile.append(lProfile[-1].add(Vector(0, 0, -abs(h2)))) h3 = lProfile[-1].z - vBase.z l3 = (h3 / vHeight.Length) * vLength.Length v3 = DraftVecUtils.scaleTo(vLength, -l3) lProfile.append(lProfile[-1].add(Vector(0, 0, -abs(h3))).add(v3)) l4 = (obj.StructureThickness / vHeight.Length) * hyp v4 = DraftVecUtils.scaleTo(vLength, -l4) lProfile.append(lProfile[-1].add(v4)) lProfile.append(lProfile[0]) #print lProfile pol = Part.makePolygon(lProfile) pol = Part.Face(pol) evec = DraftVecUtils.scaleTo(vWidth, obj.StringerWidth) if obj.Structure == "One stringer": if obj.StructureOffset: mvec = DraftVecUtils.scaleTo(vWidth, obj.StructureOffset) else: mvec = DraftVecUtils.scaleTo(vWidth, (vWidth.Length / 2) - obj.StringerWidth / 2) pol.translate(mvec) struct = pol.extrude(evec) elif obj.Structure == "Two stringers": pol2 = pol.copy() if obj.StructureOffset: mvec = DraftVecUtils.scaleTo(vWidth, obj.StructureOffset) pol.translate(mvec) mvec = vWidth.add(mvec.negative()) pol2.translate(mvec) else: pol2.translate(vWidth) s1 = pol.extrude(evec) s2 = pol2.extrude(evec.negative()) struct = Part.makeCompound([s1, s2]) if struct: self.structures.append(struct)
def makeBoreHole(): # create a document if needed if App.ActiveDocument == None: App.newDocument("Solid") Group = App.ActiveDocument.addObject("App::DocumentObjectGroup","Group") Group.Label="Bore hole" V1 = Base.Vector(0,10,0) V2 = Base.Vector(30,10,0) V3 = Base.Vector(30,-10,0) V4 = Base.Vector(0,-10,0) VC1 = Base.Vector(-10,0,0) C1 = Part.Arc(V1,VC1,V4) # and the second one VC2 = Base.Vector(40,0,0) C2 = Part.Arc(V2,VC2,V3) L1 = Part.Line(V1,V2) # and the second one L2 = Part.Line(V4,V3) S1 = Part.Shape([C1,C2,L1,L2]) W=Part.Wire(S1.Edges) F=Part.Face(W) P=F.extrude(Base.Vector(0,0,5)) # add objects with the shape Wire=Group.newObject("Part::Feature","Wire") Wire.Shape=W Face=Group.newObject("Part::Feature","Face") Face.Shape=F Prism=Group.newObject("Part::Feature","Extrude") Prism.Shape=P c=Part.Circle(Base.Vector(0,0,-1),Base.Vector(0,0,1),2.0) w=Part.Wire(c.toShape()) f=Part.Face(w) p=f.extrude(Base.Vector(0,0,7)) P=P.cut(p) # add first borer Bore1=Group.newObject("Part::Feature","Borer_1") Bore1.Shape=p Hole1=Group.newObject("Part::Feature","Borer_Hole1") Hole1.Shape=P c=Part.Circle(Base.Vector(0,-11,2.5),Base.Vector(0,1,0),1.0) w=Part.Wire(c.toShape()) f=Part.Face(w) p=f.extrude(Base.Vector(0,22,0)) P=P.cut(p) # add second borer Bore2=Group.newObject("Part::Feature","Borer_2") Bore2.Shape=p Hole2=Group.newObject("Part::Feature","Borer_Hole2") Hole2.Shape=P App.ActiveDocument.recompute() # hide all objets except of the final one Gui.ActiveDocument.getObject(Wire.Name).hide() Gui.ActiveDocument.getObject(Face.Name).hide() Gui.ActiveDocument.getObject(Prism.Name).hide() Gui.ActiveDocument.getObject(Bore1.Name).hide() Gui.ActiveDocument.getObject(Hole1.Name).hide() Gui.ActiveDocument.getObject(Bore2.Name).hide() Gui.ActiveDocument.ActiveView.fitAll()
def faceArray(tool, tool_face, base_faces, skip_edges, offset, align): tool_list = [] for base_face in base_faces: skiplist = [] # generate hashcode for skiped edges for skip_edge in skip_edges: skiplist.append(skip_edge.hashCode()) # print(skiplist) wire_list = [] # remove wires with skip edge specified based on hashcode for i, wire in enumerate(base_face.Wires): if i != 0: for edge in wire.Edges: if edge.hashCode() in skiplist: break if edge.hashCode() not in skiplist: wire_list.append(wire) for wire in wire_list: tool_copy = tool.copy() make_face = Part.Face(wire) dir, point = face_direction(make_face) offsetPoint = point - base_face.CenterOfMass + dir * offset # Aligned, if align option specified if align: # Alignment Based on Largest edge of both faces size = 0.0 for medge in make_face.Edges: if medge.Length > size and issubclass(type(medge.Curve), (Part.Line)): base_face_edge = medge size = medge.Length # print(size) #Part.show(base_face_edge, "base_face_edge") base_vec = base_face_edge.valueAt( base_face_edge.LastParameter) - base_face_edge.valueAt( base_face_edge.FirstParameter) size = 0.0 for nedge in tool_face.Edges: if nedge.Length > size and issubclass(type(nedge.Curve), (Part.Line)): tool_face_edge = nedge size = nedge.Length # print(size) #Part.show(tool_face_edge, "tool_face_edge") tool_vec = tool_face_edge.valueAt( tool_face_edge.LastParameter) - tool_face_edge.valueAt( tool_face_edge.FirstParameter) # # Alignment Based on boundbox (useful, if oriented boundbox available) # base_vec = FreeCAD.Vector (make_face.BoundBox.XMax, make_face.BoundBox.YMax, make_face.BoundBox.ZMax) # - FreeCAD.Vector (make_face.BoundBox.XMin, make_face.BoundBox.YMin, make_face.BoundBox.ZMin) # tool_vec = FreeCAD.Vector (tool_face.BoundBox.XMax, tool_face.BoundBox.YMax, tool_face.BoundBox.ZMax) # - FreeCAD.Vector (tool_face.BoundBox.XMin, tool_face.BoundBox.YMin, tool_face.BoundBox.ZMin) angle = angleBetween(base_vec, tool_vec) # print(angle) tool_tran = transform_tool(tool_copy, base_face, tool_face, offsetPoint, angle) else: tool_tran = transform_tool(tool_copy, base_face, tool_face, offsetPoint) #Part.show(tool_tran, "tool_tran") tool_list.append(tool_tran) return tool_list
def execute(self, obj): if self.clone(obj): return import Part, DraftGeomUtils pl = obj.Placement # test properties if not obj.Base: FreeCAD.Console.PrintLog(obj.Label + ": no base\n") return if not hasattr(obj.Base, "Shape"): FreeCAD.Console.PrintLog(obj.Label + ": invalid base\n") return if obj.VerticalMullionProfile: if not hasattr(obj.VerticalMullionProfile, "Shape"): FreeCAD.Console.PrintLog( obj.Label + ": invalid vertical mullion profile\n") return if obj.HorizontalMullionProfile: if not hasattr(obj.HorizontalMullionProfile, "Shape"): FreeCAD.Console.PrintLog( obj.Label + ": invalid horizontal mullion profile\n") return if obj.DiagonalMullionProfile: if not hasattr(obj.DiagonalMullionProfile, "Shape"): FreeCAD.Console.PrintLog( obj.Label + ": invalid diagonal mullion profile\n") return facets = [] faces = [] if obj.Base.Shape.Faces: faces = obj.Base.Shape.Faces elif obj.Height.Value and obj.VerticalDirection.Length: ext = FreeCAD.Vector(obj.VerticalDirection) ext.normalize() ext = ext.multiply(obj.Height.Value) faces = [edge.extrude(ext) for edge in obj.Base.Shape.Edges] if not faces: FreeCAD.Console.PrintLog(obj.Label + ": unable to build base faces\n") return # subdivide the faces into quads for face in faces: fp = face.ParameterRange # guessing horizontal/vertical directions vdir = obj.VerticalDirection if not vdir.Length: vdir = FreeCAD.Vector(0, 0, 1) vdir.normalize() basevector = face.valueAt(fp[1], fp[3]).sub(face.valueAt(fp[0], fp[2])) a = basevector.getAngle(vdir) if (a <= math.pi / 2 + ANGLETOLERANCE) and ( a >= math.pi / 2 - ANGLETOLERANCE): facedir = True vertsec = obj.VerticalSections horizsec = obj.HorizontalSections else: facedir = False vertsec = obj.HorizontalSections horizsec = obj.VerticalSections hstep = (fp[1] - fp[0]) if vertsec: hstep = hstep / vertsec vstep = (fp[3] - fp[2]) if horizsec: vstep = vstep / horizsec # construct facets for i in range(vertsec or 1): for j in range(horizsec or 1): p0 = face.valueAt(fp[0] + i * hstep, fp[2] + j * vstep) p1 = face.valueAt(fp[0] + (i + 1) * hstep, fp[2] + j * vstep) p2 = face.valueAt(fp[0] + (i + 1) * hstep, fp[2] + (j + 1) * vstep) p3 = face.valueAt(fp[0] + i * hstep, fp[2] + (j + 1) * vstep) facet = Part.Face(Part.makePolygon([p0, p1, p2, p3, p0])) facets.append(facet) if not facets: FreeCAD.Console.PrintLog(obj.Label + ": failed to subdivide shape\n") return baseshape = Part.makeShell(facets) # make edge/normal relation table edgetable = {} for face in baseshape.Faces: for edge in face.Edges: ec = edge.hashCode() if ec in edgetable: edgetable[ec].append(face) else: edgetable[ec] = [face] self.edgenormals = {} for ec, faces in edgetable.items(): if len(faces) == 1: self.edgenormals[ec] = faces[0].normalAt(0, 0) else: n = faces[0].normalAt(0, 0).add(faces[1].normalAt(0, 0)) if n.Length > 0.001: n.normalize() else: # adjacent faces have same normals n = faces[0].normalAt(0, 0) self.edgenormals[ec] = n # sort edges between vertical/horizontal hedges = [] vedges = [] for edge in baseshape.Edges: v = edge.Vertexes[-1].Point.sub(edge.Vertexes[0].Point) a = v.getAngle(vdir) if (a <= math.pi / 2 + ANGLETOLERANCE) and ( a >= math.pi / 2 - ANGLETOLERANCE): hedges.append(edge) else: vedges.append(edge) # construct vertical mullions vmullions = [] vprofile = self.getMullionProfile(obj, "Vertical") if vprofile and vertsec: for vedge in vedges: vn = self.edgenormals[vedge.hashCode()] if (vn.x != 0) or (vn.y != 0): avn = FreeCAD.Vector(vn.x, vn.y, 0) rot = FreeCAD.Rotation(FreeCAD.Vector(0, -1, 0), avn) else: rot = FreeCAD.Rotation() if obj.VerticalMullionAlignment: ev = vedge.Vertexes[-1].Point.sub(vedge.Vertexes[0].Point) rot = FreeCAD.Rotation(FreeCAD.Vector(1, 0, 0), ev).multiply(rot) vmullions.append( self.makeMullion(vedge, vprofile, rot, obj.CenterProfiles)) # construct horizontal mullions hmullions = [] hprofile = self.getMullionProfile(obj, "Horizontal") if hprofile and horizsec: for hedge in hedges: rot = FreeCAD.Rotation(FreeCAD.Vector(0, 1, 0), -90) vn = self.edgenormals[hedge.hashCode()] if (vn.x != 0) or (vn.y != 0): avn = FreeCAD.Vector(vn.x, vn.y, 0) rot = FreeCAD.Rotation(FreeCAD.Vector(0, -1, 0), avn).multiply(rot) if obj.HorizontalMullionAlignment: rot = FreeCAD.Rotation(avn, vn).multiply(rot) hmullions.append( self.makeMullion(hedge, hprofile, rot, obj.CenterProfiles)) # construct panels panels = [] dedges = [] if obj.PanelThickness.Value: for face in baseshape.Faces: verts = [v.Point for v in face.OuterWire.OrderedVertexes] if len(verts) == 4: if DraftGeomUtils.isPlanar(verts): panel = self.makePanel(verts, obj.PanelThickness.Value) panels.append(panel) else: verts1 = [verts[0], verts[1], verts[2]] panel = self.makePanel(verts1, obj.PanelThickness.Value) panels.append(panel) verts2 = [verts[0], verts[2], verts[3]] panel = self.makePanel(verts2, obj.PanelThickness.Value) panels.append(panel) dedges.append(Part.makeLine(verts[0], verts[2])) # construct diagonal mullions dmullions = [] if dedges: n = (dedges[0].Vertexes[-1].Point.sub(dedges[0].Point)) dprofile = self.getMullionProfile(obj, "Diagonal") if dprofile: for dedge in dedges: rot = FreeCAD.Rotation( FreeCAD.Vector(0, 0, 1), dedge.Vertexes[-1].Point.sub(dedge.Vertexes[0].Point)) dmullions.append( self.makeMullion(dedge, dprofile, rot, obj.CenterProfiles)) # perform subtractions if obj.Refine: subvmullion = None subhmullion = None subdmullion = None if vmullions: subvmullion = vmullions[0].copy() for m in vmullions[1:]: subvmullion = subvmullion.fuse(m) if hmullions: subhmullion = hmullions[0].copy() for m in hmullions[1:]: subhmullion = subhmullion.fuse(m) if dmullions: subdmullion = dmullions[0].copy() for m in dmullions[1:]: subdmullion = subdmullion.fuse(m) if subvmullion: hmullions = [m.cut(subvmullion) for m in hmullions] if subhmullion: dmullions = [m.cut(subvmullion) for m in dmullions] dmullions = [m.cut(subhmullion) for m in dmullions] panels = [m.cut(subvmullion) for m in panels] panels = [m.cut(subhmullion) for m in panels] if subdmullion: panels = [m.cut(subdmullion) for m in panels] # mount shape obj.VerticalMullionNumber = len(vmullions) obj.HorizontalMullionNumber = len(hmullions) obj.DiagonalMullionNumber = len(dmullions) obj.PanelNumber = len(panels) shape = Part.makeCompound(vmullions + hmullions + dmullions + panels) shape = self.processSubShapes(obj, shape, pl) self.applyShape(obj, shape, pl)
def execute(self, obj): if not obj.Base: return if not obj.Base.Shape: return if not obj.Base.Shape.Wires: return pl = obj.Placement if obj.Base.Shape.Solids: obj.Shape = obj.Base.Shape.copy() if not pl.isNull(): obj.Placement = obj.Shape.Placement.multiply(pl) else: if not obj.Profile: return if not obj.Profile.isDerivedFrom("Part::Part2DObject"): return if not obj.Profile.Shape: return if not obj.Profile.Shape.Wires: return if not obj.Profile.Shape.Faces: for w in obj.Profile.Shape.Wires: if not w.isClosed(): return import DraftGeomUtils, Part, math baseprofile = obj.Profile.Shape.copy() if not baseprofile.Faces: f = [] for w in baseprofile.Wires: f.append(Part.Face(w)) if len(f) == 1: baseprofile = f[0] else: baseprofile = Part.makeCompound(f) shapes = [] normal = DraftGeomUtils.getNormal(obj.Base.Shape) #for wire in obj.Base.Shape.Wires: for e in obj.Base.Shape.Edges: #e = wire.Edges[0] bvec = DraftGeomUtils.vec(e) bpoint = e.Vertexes[0].Point profile = baseprofile.copy() #basepoint = profile.Placement.Base basepoint = profile.CenterOfMass profile.translate(bpoint.sub(basepoint)) if obj.Align: axis = profile.Placement.Rotation.multVec( FreeCAD.Vector(0, 0, 1)) angle = bvec.getAngle(axis) if round(angle, Draft.precision()) != 0: if round(angle, Draft.precision()) != round( math.pi, Draft.precision()): rotaxis = axis.cross(bvec) profile.rotate(DraftVecUtils.tup(bpoint), DraftVecUtils.tup(rotaxis), math.degrees(angle)) if obj.Rotation: profile.rotate( DraftVecUtils.tup(bpoint), DraftVecUtils.tup(FreeCAD.Vector(bvec).normalize()), obj.Rotation) #profile = wire.makePipeShell([profile],True,False,2) TODO buggy profile = profile.extrude(bvec) if obj.Offset: if not DraftVecUtils.isNull(obj.Offset): profile.translate(obj.Offset) shapes.append(profile) if shapes: obj.Shape = Part.makeCompound(shapes) obj.Placement = pl
def execute(self, obj): "creates the panel shape" import Part, DraftGeomUtils # base tests if obj.Base: if obj.Base.isDerivedFrom("Part::Feature"): if obj.Base.Shape.isNull(): return elif obj.Base.isDerivedFrom("Mesh::Feature"): if not obj.Base.Mesh.isSolid(): return else: if obj.Length.Value: length = obj.Length.Value else: return if obj.Width.Value: width = obj.Width.Value else: return if obj.Thickness.Value: thickness = obj.Thickness.Value else: if not obj.Base: return elif obj.Base.isDerivedFrom("Part::Feature"): if not obj.Base.Solids: return # creating base shape pl = obj.Placement base = None normal = None if obj.Base: p = FreeCAD.Placement(obj.Base.Placement) normal = p.Rotation.multVec(Vector(0, 0, 1)) normal = normal.multiply(thickness) base = obj.Base.Shape.copy() if base.Solids: pass elif base.Faces: self.BaseProfile = base self.ExtrusionVector = normal base = base.extrude(normal) elif base.Wires: closed = True for w in base.Wires: if not w.isClosed(): closed = False if closed: base = ArchCommands.makeFace(base.Wires) self.BaseProfile = base self.ExtrusionVector = normal base = base.extrude(normal) elif obj.Base.isDerivedFrom("Mesh::Feature"): if obj.Base.Mesh.isSolid(): if obj.Base.Mesh.countComponents() == 1: sh = ArchCommands.getShapeFromMesh(obj.Base.Mesh) if sh.isClosed() and sh.isValid() and sh.Solids: base = sh else: normal = Vector(0, 0, 1).multiply(thickness) self.ExtrusionVector = normal l2 = length / 2 or 0.5 w2 = width / 2 or 0.5 v1 = Vector(-l2, -w2, 0) v2 = Vector(l2, -w2, 0) v3 = Vector(l2, w2, 0) v4 = Vector(-l2, w2, 0) base = Part.makePolygon([v1, v2, v3, v4, v1]) base = Part.Face(base) self.BaseProfile = base base = base.extrude(self.ExtrusionVector) if base and (obj.Sheets > 1) and normal and thickness: bases = [base] for i in range(1, obj.Sheets): n = FreeCAD.Vector(normal).normalize().multiply(i * thickness) b = base.copy() b.translate(n) bases.append(b) base = Part.makeCompound(bases) if base and normal and hasattr(obj, "Offset"): if obj.Offset.Value: v = DraftVecUtils.scaleTo(normal, obj.Offset.Value) base.translate(v) # process subshapes base = self.processSubShapes(obj, base, pl) # applying if base: if not base.isNull(): if base.isValid() and base.Solids: if base.Volume < 0: base.reverse() if base.Volume < 0: FreeCAD.Console.PrintError( translate("Arch", "Couldn't compute a shape")) return base = base.removeSplitter() obj.Shape = base if not pl.isNull(): obj.Placement = pl
def Create(doc, constraint, solver, rigid1, rigid2): c = constraint if c.Type == "pointIdentity": dep1 = DependencyPointIdentity(c, "point") dep2 = DependencyPointIdentity(c, "point") ob1 = doc.getObject(c.Object1) ob2 = doc.getObject(c.Object2) vert1 = getObjectVertexFromName(ob1, c.SubElement1) vert2 = getObjectVertexFromName(ob2, c.SubElement2) dep1.refPoint = vert1.Point dep2.refPoint = vert2.Point elif c.Type == "sphereCenterIdent": dep1 = DependencyPointIdentity(c, "point") dep2 = DependencyPointIdentity(c, "point") ob1 = doc.getObject(c.Object1) ob2 = doc.getObject(c.Object2) vert1 = getPos(ob1, c.SubElement1) vert2 = getPos(ob2, c.SubElement2) dep1.refPoint = vert1 dep2.refPoint = vert2 elif c.Type == "pointOnLine": dep1 = DependencyPointOnLine(c, "point") dep2 = DependencyPointOnLine(c, "pointAxis") ob1 = doc.getObject(c.Object1) ob2 = doc.getObject(c.Object2) #vert1 = getObjectVertexFromName(ob1, c.SubElement1) #dep1.refPoint = vert1.Point dep1.refPoint = getPos(ob1, c.SubElement1) dep2.refPoint = getPos(ob2, c.SubElement2) axis2 = getAxis(ob2, c.SubElement2) dep2.refAxisEnd = dep2.refPoint.add(axis2) elif c.Type == "pointOnPlane": dep1 = DependencyPointOnPlane(c, "point") dep2 = DependencyPointOnPlane(c, "plane") ob1 = doc.getObject(c.Object1) ob2 = doc.getObject(c.Object2) #vert1 = getObjectVertexFromName(ob1, c.SubElement1) dep1.refPoint = getPos(ob1, c.SubElement1) plane2 = getObjectFaceFromName(ob2, c.SubElement2) dep2.refPoint = plane2.Faces[0].BoundBox.Center normal2 = a2plib.getPlaneNormal(plane2.Surface) #shift refPoint of plane by offset try: offs = c.offset except: offs = 0.0 offsetVector = Base.Vector(normal2) offsetVector.multiply(offs) dep2.refPoint = dep2.refPoint.add(offsetVector) dep2.refAxisEnd = dep2.refPoint.add(normal2) elif c.Type == "circularEdge": dep1 = DependencyCircularEdge(c, "pointAxis") dep2 = DependencyCircularEdge(c, "pointAxis") ob1 = doc.getObject(c.Object1) ob2 = doc.getObject(c.Object2) circleEdge1 = getObjectEdgeFromName(ob1, c.SubElement1) circleEdge2 = getObjectEdgeFromName(ob2, c.SubElement2) dep1.refPoint = circleEdge1.Curve.Center dep2.refPoint = circleEdge2.Curve.Center axis1 = circleEdge1.Curve.Axis axis2 = circleEdge2.Curve.Axis if dep2.direction == "opposed": axis2.multiply(-1.0) dep1.refAxisEnd = dep1.refPoint.add(axis1) dep2.refAxisEnd = dep2.refPoint.add(axis2) # if abs(dep2.offset) > solver.mySOLVER_SPIN_ACCURACY * 1e-1: offsetAdjustVec = Base.Vector(axis2.x, axis2.y, axis2.z) offsetAdjustVec.multiply(dep2.offset) dep2.refPoint = dep2.refPoint.add(offsetAdjustVec) dep2.refAxisEnd = dep2.refAxisEnd.add(offsetAdjustVec) elif c.Type == "planesParallel": dep1 = DependencyParallelPlanes(c, "pointNormal") dep2 = DependencyParallelPlanes(c, "pointNormal") ob1 = doc.getObject(c.Object1) ob2 = doc.getObject(c.Object2) plane1 = getObjectFaceFromName(ob1, c.SubElement1) plane2 = getObjectFaceFromName(ob2, c.SubElement2) dep1.refPoint = plane1.Faces[0].BoundBox.Center dep2.refPoint = plane2.Faces[0].BoundBox.Center normal1 = a2plib.getPlaneNormal(plane1.Surface) normal2 = a2plib.getPlaneNormal(plane2.Surface) if dep2.direction == "opposed": normal2.multiply(-1.0) dep1.refAxisEnd = dep1.refPoint.add(normal1) dep2.refAxisEnd = dep2.refPoint.add(normal2) elif c.Type == "angledPlanes": dep1 = DependencyAngledPlanes(c, "pointNormal") dep2 = DependencyAngledPlanes(c, "pointNormal") ob1 = doc.getObject(c.Object1) ob2 = doc.getObject(c.Object2) plane1 = getObjectFaceFromName(ob1, c.SubElement1) plane2 = getObjectFaceFromName(ob2, c.SubElement2) dep1.refPoint = plane1.Faces[0].BoundBox.Center dep2.refPoint = plane2.Faces[0].BoundBox.Center normal1 = a2plib.getPlaneNormal(plane1.Surface) normal2 = a2plib.getPlaneNormal(plane2.Surface) dep1.refAxisEnd = dep1.refPoint.add(normal1) dep2.refAxisEnd = dep2.refPoint.add(normal2) elif c.Type == "plane": dep1 = DependencyPlane(c, "pointNormal") dep2 = DependencyPlane(c, "pointNormal") ob1 = doc.getObject(c.Object1) ob2 = doc.getObject(c.Object2) plane1 = getObjectFaceFromName(ob1, c.SubElement1) plane2 = getObjectFaceFromName(ob2, c.SubElement2) dep1.refPoint = plane1.Faces[0].BoundBox.Center dep2.refPoint = plane2.Faces[0].BoundBox.Center normal1 = a2plib.getPlaneNormal(plane1.Surface) normal2 = a2plib.getPlaneNormal(plane2.Surface) if dep2.direction == "opposed": normal2.multiply(-1.0) dep1.refAxisEnd = dep1.refPoint.add(normal1) dep2.refAxisEnd = dep2.refPoint.add(normal2) # if abs(dep2.offset) > solver.mySOLVER_SPIN_ACCURACY * 1e-1: offsetAdjustVec = Base.Vector(normal2.x, normal2.y, normal2.z) offsetAdjustVec.multiply(dep2.offset) dep2.refPoint = dep2.refPoint.add(offsetAdjustVec) dep2.refAxisEnd = dep2.refAxisEnd.add(offsetAdjustVec) elif c.Type == "axial": dep1 = DependencyAxial(c, "pointAxis") dep2 = DependencyAxial(c, "pointAxis") ob1 = doc.getObject(c.Object1) ob2 = doc.getObject(c.Object2) dep1.refPoint = getPos(ob1, c.SubElement1) dep2.refPoint = getPos(ob2, c.SubElement2) axis1 = getAxis(ob1, c.SubElement1) axis2 = getAxis(ob2, c.SubElement2) if dep2.direction == "opposed": axis2.multiply(-1.0) dep1.refPoint = dep1.adjustRefPoints(ob1, c.SubElement1, dep1.refPoint, axis1) dep2.refPoint = dep2.adjustRefPoints(ob2, c.SubElement2, dep2.refPoint, axis2) dep1.refAxisEnd = dep1.refPoint.add(axis1) dep2.refAxisEnd = dep2.refPoint.add(axis2) elif c.Type == "axisParallel": dep1 = DependencyAxisParallel(c, "pointAxis") dep2 = DependencyAxisParallel(c, "pointAxis") ob1 = doc.getObject(c.Object1) ob2 = doc.getObject(c.Object2) dep1.refPoint = getPos(ob1, c.SubElement1) dep2.refPoint = getPos(ob2, c.SubElement2) axis1 = getAxis(ob1, c.SubElement1) axis2 = getAxis(ob2, c.SubElement2) if dep2.direction == "opposed": axis2.multiply(-1.0) dep1.refAxisEnd = dep1.refPoint.add(axis1) dep2.refAxisEnd = dep2.refPoint.add(axis2) elif c.Type == "axisPlaneParallel": dep1 = DependencyAxisPlaneParallel(c, "pointAxis") dep2 = DependencyAxisPlaneParallel(c, "pointNormal") ob1 = doc.getObject(c.Object1) ob2 = doc.getObject(c.Object2) axis1 = getAxis(ob1, c.SubElement1) plane2 = getObjectFaceFromName(ob2, c.SubElement2) dep1.refPoint = getPos(ob1, c.SubElement1) dep2.refPoint = plane2.Faces[0].BoundBox.Center axis1Normalized = Base.Vector(axis1) axis1Normalized.normalize() dep1.refAxisEnd = dep1.refPoint.add(axis1Normalized) normal2 = a2plib.getPlaneNormal(plane2.Surface) dep2.refAxisEnd = dep2.refPoint.add(normal2) elif c.Type == "axisPlaneVertical" or c.Type == "axisPlaneNormal": # axisPlaneVertical for compat. dep1 = DependencyAxisPlaneNormal(c, "pointAxis") dep2 = DependencyAxisPlaneNormal(c, "pointNormal") ob1 = doc.getObject(c.Object1) ob2 = doc.getObject(c.Object2) axis1 = getAxis(ob1, c.SubElement1) plane2 = getObjectFaceFromName(ob2, c.SubElement2) dep1.refPoint = getPos(ob1, c.SubElement1) dep2.refPoint = plane2.Faces[0].BoundBox.Center axis1Normalized = Base.Vector(axis1) axis1Normalized.normalize() dep1.refAxisEnd = dep1.refPoint.add(axis1Normalized) normal2 = a2plib.getPlaneNormal(plane2.Surface) if dep2.direction == "opposed": normal2.multiply(-1.0) dep2.refAxisEnd = dep2.refPoint.add(normal2) elif c.Type == "CenterOfMass": dep1 = DependencyCenterOfMass(c, "point") dep2 = DependencyCenterOfMass(c, "point") ob1 = doc.getObject(c.Object1) ob2 = doc.getObject(c.Object2) if c.SubElement1.startswith('Face'): plane1 = getObjectFaceFromName(ob1, c.SubElement1) dep1.refPoint = plane1.Faces[0].CenterOfMass elif c.SubElement1.startswith('Edge'): plane1 = Part.Face( Part.Wire(getObjectEdgeFromName(ob1, c.SubElement1))) dep1.refPoint = plane1.CenterOfMass if c.SubElement2.startswith('Face'): plane2 = getObjectFaceFromName(ob2, c.SubElement2) dep2.refPoint = plane2.Faces[0].CenterOfMass elif c.SubElement2.startswith('Edge'): plane2 = Part.Face( Part.Wire(getObjectEdgeFromName(ob2, c.SubElement2))) dep2.refPoint = plane2.CenterOfMass normal1 = a2plib.getPlaneNormal(plane1.Surface) normal2 = a2plib.getPlaneNormal(plane2.Surface) if dep2.direction == "opposed": normal2.multiply(-1.0) dep1.refAxisEnd = dep1.refPoint.add(normal1) dep2.refAxisEnd = dep2.refPoint.add(normal2) # to be improved: toggle direction even if offset == 0.0 if abs(dep2.offset) > solver.mySOLVER_SPIN_ACCURACY * 1e-1: offsetAdjustVec = Base.Vector(normal2.x, normal2.y, normal2.z) offsetAdjustVec.multiply(dep2.offset) dep2.refPoint = dep2.refPoint.add(offsetAdjustVec) dep2.refAxisEnd = dep2.refAxisEnd.add(offsetAdjustVec) else: raise NotImplementedError( "Constraint type {} was not implemented!".format(c.Type)) # Assignments dep1.currentRigid = rigid1 dep1.dependedRigid = rigid2 dep1.foreignDependency = dep2 dep2.currentRigid = rigid2 dep2.dependedRigid = rigid1 dep2.foreignDependency = dep1 rigid1.dependencies.append(dep1) rigid2.dependencies.append(dep2)
def __init__( self, block_dict, rail_dict, axis_d=VX, axis_w=V0, axis_h=VZ, pos_d=0, pos_w=0, pos_h=0, pos=V0, model_type=1, # dimensional model name=None): self.pos = FreeCAD.Vector(0, 0, 0) self.position = pos if name == None: self.name = block_dict['name'] + '_block' if rail_dict is None: self.rail_h = 0 self.rail_w = 0 else: self.rail_h = rail_dict['rh'] self.rail_w = rail_dict['rw'] if (axis_w is None) or (axis_w == V0): axis_w = axis_h.cross(axis_d) Obj3D.__init__(self, axis_d, axis_w, axis_h, self.name) self.block_d = block_dict['bl'] self.block_ds = block_dict['bls'] self.block_w = block_dict['bw'] self.block_ws = block_dict['bws'] self.block_h = block_dict['bh'] self.linguide_h = block_dict['lh'] self.bolt_dsep = block_dict['boltlsep'] self.bolt_wsep = block_dict['boltwsep'] self.bolt_d = block_dict['boltd'] self.bolt_l = block_dict['boltl'] linguide_h = block_dict['lh'] # save the arguments as attributes: frame = inspect.currentframe() args, _, _, values = inspect.getargvalues(frame) for i in args: if not hasattr(self, i): setattr(self, i, values[i]) self.d0_cen = 1 # symmetric self.w0_cen = 1 # symmetric self.h0_cen = 0 if self.bolt_l == 0: # thruhole self.bolt_l = self.block_h self.thruhole = 1 else: self.thruhole = 0 if self.rail_h == 0 or linguide_h == 0: self.rail_h = 0 self.linguide_h = 0 self.rail_ins_h = 0 self.rail_bot_h = 0 else: self.rail_ins_h = self.block_h - (self.linguide_h - self.rail_h) self.rail_bot_h = self.rail_h - self.rail_ins_h # vectors from the origin to the points along axis_d: self.d_o[0] = V0 # Origin (center symmetric) self.d_o[1] = self.vec_d(-self.bolt_dsep / 2.) self.d_o[2] = self.vec_d(-self.block_ds / 2.) self.d_o[3] = self.vec_d(-self.block_d / 2.) # vectors from the origin to the points along axis_w: self.w_o[0] = V0 # Origin (center symmetric) self.w_o[1] = self.vec_w(-self.rail_w / 2.) self.w_o[2] = self.vec_w(-self.bolt_wsep / 2.) self.w_o[3] = self.vec_w(-self.block_ws / 2.) self.w_o[4] = self.vec_w(-self.block_w / 2.) # vectors from the origin to the points along axis_h: # could make more sense to have the origin at the top self.h_o[0] = V0 # Origin at the bottom self.h_o[1] = self.vec_h(self.rail_ins_h) self.h_o[2] = self.vec_h(self.block_h - self.bolt_l) self.h_o[3] = self.vec_h(self.block_h) self.h_o[4] = self.vec_h(-self.rail_bot_h) # calculates the position of the origin, and keeps it in attribute pos_o self.set_pos_o() # the main block shp_mblock = fcfun.shp_box_dir(box_w=self.block_w, box_d=self.block_ds, box_h=self.block_h, fc_axis_w=self.axis_w, fc_axis_d=self.axis_d, fc_axis_h=self.axis_h, cw=1, cd=1, ch=0, pos=self.pos_o) # the extra block shp_exblock = fcfun.shp_box_dir(box_w=self.block_ws, box_d=self.block_d, box_h=self.block_h, fc_axis_w=self.axis_w, fc_axis_d=self.axis_d, fc_axis_h=self.axis_h, cw=1, cd=1, ch=0, pos=self.pos_o) # fusion of these blocks shp_block = shp_mblock.fuse(shp_exblock) holes_list = [] # rail hole: if self.rail_h > 0 and self.rail_w > 0: wire_rail = fcfun.wire_lgrail(rail_w=self.rail_w, rail_h=self.rail_h, axis_w=self.axis_w, axis_h=self.axis_h, pos_w=0, pos_h=0, pos=self.get_pos_h(4)) face_rail = Part.Face(wire_rail) shp_rail = fcfun.shp_extrud_face(face=face_rail, length=self.block_d + 2, vec_extr_axis=self.axis_d, centered=1) #Part.show(shp_rail) holes_list.append(shp_rail) # bolt holes: for d_i in (-1, 1): # positions of the holes along axis_d for w_i in (-2, 2): # positions of the holes along axis_w shp_bolt = fcfun.shp_cylcenxtr(r=self.bolt_d / 2., h=self.bolt_l, normal=axis_h, ch=0, xtr_top=1, xtr_bot=self.thruhole, pos=self.get_pos_dwh( d_i, w_i, 2)) holes_list.append(shp_bolt) shp_holes = fcfun.fuseshplist(holes_list) shp_block = shp_block.cut(shp_holes) shp_block = shp_block.removeSplitter() self.shp = shp_block super().create_fco(self.name) # Need to set first in (0,0,0) and after that set the real placement. # This enable to do rotations without any issue self.fco.Placement.Base = FreeCAD.Vector(0, 0, 0) self.fco.Placement.Base = self.position
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