def setup(doc=None, solvertype="ccxtools"): # setup model if doc is None: doc = init_doc() # geometry objects # TODO turn circle of upper tube to have the line on the other side # make a boolean fragment of them to be sure there is a mesh point on remesh # but as long as we do not remesh it works without the boolean fragment too # tubes tube_radius = 25 tube_length = 500 sh_lower_circle = Part.Wire(Part.makeCircle(tube_radius)) sh_lower_tube = sh_lower_circle.extrude(FreeCAD.Vector(0, 0, tube_length)) sh_lower_tube.reverse() lower_tube = doc.addObject("Part::Feature", "Lower_tube") lower_tube.Shape = sh_lower_tube sh_upper_circle = Part.Wire(Part.makeCircle(tube_radius)) sh_upper_tube = sh_upper_circle.extrude(FreeCAD.Vector(0, 0, tube_length)) sh_upper_tube.reverse() upper_tube = doc.addObject("Part::Feature", "Upper_tube") upper_tube.Shape = sh_upper_tube upper_tube.Placement = FreeCAD.Placement( FreeCAD.Vector(-25, 51, 475), FreeCAD.Rotation(90, 0, 90), FreeCAD.Vector(0, 0, 0), ) # point for load v_force_pt = FreeCAD.Vector(0, 76, 475) sh_force_point = Part.Vertex(v_force_pt) force_point = doc.addObject("Part::Feature", "Load_place_point") force_point.Shape = sh_force_point if FreeCAD.GuiUp: force_point.ViewObject.PointSize = 10.0 force_point.ViewObject.PointColor = (1.0, 0.0, 0.0) # boolean fragment of upper tubo and force point boolfrag = SplitFeatures.makeBooleanFragments(name='BooleanFragments') boolfrag.Objects = [upper_tube, force_point] if FreeCAD.GuiUp: upper_tube.ViewObject.hide() # compound out of bool frag and lower tube geom_obj = doc.addObject("Part::Compound", "AllGeomCompound") geom_obj.Links = [boolfrag, lower_tube] # line for load direction sh_load_line = Part.makeLine(v_force_pt, FreeCAD.Vector(0, 150, 475)) load_line = doc.addObject("Part::Feature", "Load_direction_line") load_line.Shape = sh_load_line if FreeCAD.GuiUp: load_line.ViewObject.LineWidth = 5.0 load_line.ViewObject.LineColor = (1.0, 0.0, 0.0) doc.recompute() if FreeCAD.GuiUp: geom_obj.ViewObject.Document.activeView().viewAxonometric() geom_obj.ViewObject.Document.activeView().fitAll() # analysis analysis = ObjectsFem.makeAnalysis(doc, "Analysis") # solver if solvertype == "calculix": solver_object = analysis.addObject( ObjectsFem.makeSolverCalculix(doc, "SolverCalculiX"))[0] elif solvertype == "ccxtools": solver_object = analysis.addObject( ObjectsFem.makeSolverCalculixCcxTools(doc, "CalculiXccxTools"))[0] solver_object.WorkingDir = u"" if solvertype == "calculix" or solvertype == "ccxtools": solver_object.AnalysisType = "static" solver_object.BeamShellResultOutput3D = True solver_object.GeometricalNonlinearity = "linear" # really? # TODO iterations parameter !!! solver_object.ThermoMechSteadyState = False solver_object.MatrixSolverType = "default" solver_object.IterationsControlParameterTimeUse = False solver_object.SplitInputWriter = False # shell thickness analysis.addObject( ObjectsFem.makeElementGeometry2D(doc, 0.5, 'ShellThickness')) # material material_obj = analysis.addObject( ObjectsFem.makeMaterialSolid(doc, "MechanicalMaterial"))[0] mat = material_obj.Material mat["Name"] = "AlCuMgPb" mat["YoungsModulus"] = "72000 MPa" mat["PoissonRatio"] = "0.30" material_obj.Material = mat analysis.addObject(material_obj) # fixed_constraint fixed_constraint = analysis.addObject( ObjectsFem.makeConstraintFixed(doc, "ConstraintFixed"))[0] fixed_constraint.References = [ (lower_tube, "Edge2"), (upper_tube, "Edge3"), ] # force_constraint force_constraint = doc.Analysis.addObject( ObjectsFem.makeConstraintForce(doc, name="ConstraintForce"))[0] # TODO use point of tube boolean fragment force_constraint.References = [(force_point, "Vertex1")] force_constraint.Force = 5000.0 force_constraint.Direction = (load_line, ["Edge1"]) force_constraint.Reversed = True # contact constraint contact_constraint = doc.Analysis.addObject( ObjectsFem.makeConstraintContact(doc, name="ConstraintContact"))[0] contact_constraint.References = [ (lower_tube, "Face1"), (upper_tube, "Face1"), ] contact_constraint.Friction = 0.0 # contact_constrsh_aint.Slope = "1000000.0 kg/(mm*s^2)" # contact stiffness contact_constraint.Slope = 1000000.0 # should be 1000000.0 kg/(mm*s^2) # mesh from .meshes.mesh_contact_tube_tube_tria3 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 superWireReverse(debuglist, closed=False): '''superWireReverse(debuglist,[closed]): forces a wire between edges that don't necessarily have coincident endpoints. If closed=True, wire will always be closed. debuglist has a tuple for every edge.The first entry is the edge, the second is the flag 'does not need to be inverted' ''' #taken from draftlibs def median(v1, v2): vd = v2.sub(v1) vd.scale(.5, .5, .5) return v1.add(vd) try: from DraftGeomUtils import findMidpoint except ImportError: #workaround for Version 0.12 from draftlibs.fcgeo import findMidpoint #workaround for Version 0.12 import Part #edges = sortEdges(edgeslist) print(debuglist) newedges = [] for i in range(len(debuglist)): curr = debuglist[i] if i == 0: if closed: prev = debuglist[-1] else: prev = None else: prev = debuglist[i - 1] if i == (len(debuglist) - 1): if closed: nexte = debuglist[0] else: nexte = None else: nexte = debuglist[i + 1] print(i, prev, curr, nexte) if prev: if curr[0].Vertexes[-1*(not curr[1])].Point == \ prev[0].Vertexes[-1*prev[1]].Point: p1 = curr[0].Vertexes[-1 * (not curr[1])].Point else: p1 = median(curr[0].Vertexes[-1*(not curr[1])].Point,\ prev[0].Vertexes[-1*prev[1]].Point) else: p1 = curr[0].Vertexes[-1 * (not curr[1])].Point if nexte: if curr[0].Vertexes[-1*curr[1]].Point == \ nexte[0].Vertexes[-1*(not nexte[1])].Point: p2 = nexte[0].Vertexes[-1 * (not nexte[1])].Point else: p2 = median(curr[0].Vertexes[-1*(curr[1])].Point,\ nexte[0].Vertexes[-1*(not nexte[1])].Point) else: p2 = curr[0].Vertexes[-1 * (curr[1])].Point if isinstance(curr[0].Curve, (Part.LineSegment, Part.Line)): print("line", p1, p2) newedges.append(Part.LineSegment(p1, p2).toShape()) elif isinstance(curr[0].Curve, Part.Circle): p3 = findMidpoint(curr[0]) print("arc", p1, p3, p2) newedges.append(Part.Arc(p1, p3, p2).toShape()) else: print("Cannot superWire edges that are not lines or arcs") return None print(newedges) return Part.Wire(newedges)
def execute(self,obj): if self.clone(obj): return if not obj.Base: return if not obj.Base.Shape: return if not obj.Base.Shape.Wires: return if not obj.Diameter.Value: return if not obj.Amount: return father = obj.Host fathershape = None if not father: # support for old-style rebars if obj.InList: if hasattr(obj.InList[0],"Armatures"): if obj in obj.InList[0].Armatures: father = obj.InList[0] if father: if father.isDerivedFrom("Part::Feature"): fathershape = father.Shape wire = obj.Base.Shape.Wires[0] if hasattr(obj,"Rounding"): #print(obj.Rounding) if obj.Rounding: radius = obj.Rounding * obj.Diameter.Value import DraftGeomUtils wire = DraftGeomUtils.filletWire(wire,radius) bpoint, bvec = self.getBaseAndAxis(wire) if not bpoint: return axis = obj.Base.Placement.Rotation.multVec(FreeCAD.Vector(0,0,-1)) if fathershape: size = (ArchCommands.projectToVector(fathershape.copy(),axis)).Length else: size = 1 if hasattr(obj,"Direction"): if not DraftVecUtils.isNull(obj.Direction): axis = FreeCAD.Vector(obj.Direction) axis.normalize() if fathershape: size = (ArchCommands.projectToVector(fathershape.copy(),axis)).Length else: size = 1 if hasattr(obj,"Distance"): if obj.Distance.Value: size = obj.Distance.Value #print(axis) #print(size) spacinglist = None if hasattr(obj, "CustomSpacing"): if obj.CustomSpacing: spacinglist = strprocessOfCustomSpacing(obj.CustomSpacing) influenceArea = sum(spacinglist) - spacinglist[0] / 2 - spacinglist[-1] / 2 if (obj.OffsetStart.Value + obj.OffsetEnd.Value) > size: return # all tests ok! if hasattr(obj, "Length"): length = getLengthOfRebar(obj) if length: obj.Length = length pl = obj.Placement import Part circle = Part.makeCircle(obj.Diameter.Value/2,bpoint,bvec) circle = Part.Wire(circle) try: bar = wire.makePipeShell([circle],True,False,2) basewire = wire.copy() except Part.OCCError: print("Arch: error sweeping rebar profile along the base sketch") return # building final shape shapes = [] placementlist = [] self.wires = [] if father: rot = father.Placement.Rotation else: rot = FreeCAD.Rotation() if obj.Amount == 1: barplacement = CalculatePlacement(obj.Amount, 1, size, axis, rot, obj.OffsetStart.Value, obj.OffsetEnd.Value) placementlist.append(barplacement) if hasattr(obj,"Spacing"): obj.Spacing = 0 else: if obj.OffsetStart.Value: baseoffset = DraftVecUtils.scaleTo(axis,obj.OffsetStart.Value) else: baseoffset = None interval = size - (obj.OffsetStart.Value + obj.OffsetEnd.Value) interval = interval / (obj.Amount - 1) for i in range(obj.Amount): barplacement = CalculatePlacement(obj.Amount, i+1, size, axis, rot, obj.OffsetStart.Value, obj.OffsetEnd.Value) placementlist.append(barplacement) if hasattr(obj,"Spacing"): obj.Spacing = interval # Calculate placement of bars from custom spacing. if spacinglist: placementlist[:] = [] reqInfluenceArea = size - (obj.OffsetStart.Value + obj.OffsetEnd.Value) # Avoid unnecessary checks to pass like. For eg.: when we have values # like influenceArea is 100.00001 and reqInflueneArea is 100 if round(influenceArea) > round(reqInfluenceArea): return FreeCAD.Console.PrintError("Influence area of rebars is greater than "+ str(reqInfluenceArea) + ".\n") elif round(influenceArea) < round(reqInfluenceArea): FreeCAD.Console.PrintWarning("Last span is greater that end offset.\n") for i in range(len(spacinglist)): if i == 0: barplacement = CustomSpacingPlacement(spacinglist, 1, axis, father.Placement.Rotation, obj.OffsetStart.Value, obj.OffsetEnd.Value) placementlist.append(barplacement) else: barplacement = CustomSpacingPlacement(spacinglist, i+1, axis, father.Placement.Rotation, obj.OffsetStart.Value, obj.OffsetEnd.Value) placementlist.append(barplacement) obj.Amount = len(spacinglist) obj.Spacing = 0 obj.PlacementList = placementlist for i in range(len(obj.PlacementList)): if i == 0: bar.Placement = obj.PlacementList[i] shapes.append(bar) basewire.Placement = obj.PlacementList[i] self.wires.append(basewire) else: bar = bar.copy() bar.Placement = obj.PlacementList[i] shapes.append(bar) w = basewire.copy() w.Placement = obj.PlacementList[i] self.wires.append(w) if shapes: obj.Shape = Part.makeCompound(shapes) obj.Placement = pl obj.TotalLength = obj.Length * len(obj.PlacementList)
def build(self, delta=0.25, delta_phi=math.radians(2)): exit_radius = math.sqrt(throat_radius * throat_radius * self.nozzle_expansion_ratio) inflection_angle = math.radians(self.inflection_angle_deg) exit_angle = math.radians(self.exit_angle_deg) diverging_radius = diverging_radius_koef * throat_radius inflection_point = circle_point(inflection_angle, diverging_radius, 0, (1 + diverging_radius_koef) * throat_radius) edges = [] # converging arc converging_radius = throat_radius * converging_radius_koef converging_circle_center = 0, throat_radius + converging_radius x = 0 y = converging_circle_center[1] px = -converging_radius py = 0 phi = math.radians(180) while phi <= math.radians(270): x = math.cos(phi) * converging_radius + converging_circle_center[0] y = math.sin(phi) * converging_radius + converging_circle_center[1] edges.append(Part.makeLine((px, py, 0), (x, y, 0))) px = x py = y phi = phi + delta_phi # diverging arc diverging_circle_center = 0, throat_radius + diverging_radius while y < inflection_point[1]: x = math.cos(phi) * diverging_radius + diverging_circle_center[0] y = math.sin(phi) * diverging_radius + diverging_circle_center[1] edges.append(Part.makeLine((px, py, 0), (x, y, 0))) px = x py = y phi = phi + delta_phi # diverging parabola abc = parabola_koef(inflection_angle, exit_angle, exit_radius, inflection_point) while y <= exit_radius: y = py + delta ny = min(y, exit_radius) x = abc[0] * sqr(ny) + abc[1] * ny + abc[2] edges.append(Part.makeLine((px, py, 0), (x, ny, 0))) px = x py = ny # back to the axis edges.append(Part.makeLine((px, py, 0), (px, 0, 0))) edges.append(Part.makeLine((px, 0, 0), (-converging_radius, 0, 0))) nozzle_contour = Part.Wire(edges) nozzle_face = Part.Face(nozzle_contour) self.solid = nozzle_face.revolve(App.Vector(1, 0, 0), App.Vector(360, 0, 0)) self.converging_length = converging_radius self.diverging_length = px self.exit_radius = exit_radius
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 execute(self, obj): output = "" toolLoad = obj.ToolController if toolLoad is None or toolLoad.ToolNumber == 0: FreeCAD.Console.PrintError( "No Tool Controller is selected. We need a tool to build a Path." ) else: self.vertFeed = toolLoad.VertFeed.Value self.horizFeed = toolLoad.HorizFeed.Value self.vertRapid = toolLoad.VertRapid.Value self.horizRapid = toolLoad.HorizRapid.Value tool = toolLoad.Proxy.getTool(toolLoad) if not tool or tool.Diameter == 0: FreeCAD.Console.PrintError( "No Tool found or diameter is zero. We need a tool to build a Path." ) return else: self.radius = tool.Diameter / 2 if obj.Base: for b in obj.Base: for sub in b[1]: import Part import PathScripts.PathKurveUtils if "Face" in sub: shape = getattr(b[0].Shape, sub) wire = shape.OuterWire edges = wire.Edges else: edges = [getattr(b[0].Shape, sub) for sub in b[1]] wire = Part.Wire(edges) shape = None # output = "" if obj.Algorithm == "OCC Native": if shape is None: shape = wire output += self.buildpathocc(obj, shape) else: try: import area except: FreeCAD.Console.PrintError( translate( "PathKurve", "libarea needs to be installed for this command to work.\n" )) return a = area.Area() if shape is None: c = PathScripts.PathKurveUtils.makeAreaCurve( wire.Edges, 'CW') a.append(c) else: for w in shape.Wires: c = PathScripts.PathKurveUtils.makeAreaCurve( w.Edges, 'CW') a.append(c) a.Reorder() output += self.buildpathlibarea(obj, a) if obj.Active: path = Path.Path(output) obj.Path = path obj.ViewObject.Visibility = True else: path = Path.Path("(inactive operation)") obj.Path = path obj.ViewObject.Visibility = False
def zbeam(params, document): # key = params['type'] h = params["h"] c1 = params["c1"] tw = params["tw"] tf = params["tf"] le = params["l"] name = params["name"] rf = tf / 2.0 rw = tw # points, starting at the left upper corner, going counter-clockwise V1 = Vector(-0.5 * tw, 0, 0) V2 = Vector(-0.5 * tw + c1, 0, 0) V3 = Vector(-0.5 * tw + c1, tf - rf, 0) V4 = Vector(-0.5 * tw + c1 - rf, tf, 0) V5 = Vector(0.5 * tw + rw, tf, 0) V6 = Vector(0.5 * tw, tf + rw, 0) V7 = Vector(0.5 * tw, h, 0) V8 = Vector(0.5 * tw - c1, h, 0) V9 = Vector(0.5 * tw - c1, h - tf + rf, 0) V10 = Vector(0.5 * tw - c1 + rf, h - tf, 0) V11 = Vector(-0.5 * tw - rw, h - tf, 0) V12 = Vector(-0.5 * tw, h - tf - rw, 0) # circle center of the fillets, starting right bottom, going counter-clockwise Vc1 = Vector(-0.5 * tw + c1 - rf, tf - rf, 0) Vc2 = Vector(0.5 * tw + rw, tf + rw, 0) Vc3 = Vector(0.5 * tw - c1 + rf, h - tf + rf, 0) Vc4 = Vector(-0.5 * tw - rw, h - tf - rw, 0) normal = Vector(0, 0, 1) # edges E1 = makeLine(V1, V2) E2 = makeLine(V2, V3) E3 = makeCircle(rf, Vc1, normal, 0, 90) E4 = makeLine(V4, V5) E5 = makeCircle(rw, Vc2, normal, 180, 270) E6 = makeLine(V6, V7) E7 = makeLine(V7, V8) E8 = makeLine(V8, V9) E9 = makeCircle(rf, Vc3, normal, 180, 270) E10 = makeLine(V10, V11) E11 = makeCircle(rw, Vc4, normal, 0, 90) E12 = makeLine(V12, V1) # putting the segments together make a wire, a face and extrude it W = Part.Wire([E1, E2, E3, E4, E5, E6, E7, E8, E9, E10, E11, E12]) F = Part.Face(W) if params["arch"]: from ArchStructure import makeStructure part = makeStructure(name=name) prof = document.addObject("Part::Feature", "Profile") prof.Shape = F part.Base = prof part.Height = le else: part = document.addObject("Part::Feature", "BOLTS_part") part.Label = name beam = F.extrude(Vector(0, 0, le)) part.Shape = beam
def getProfiles(self,obj,noplacement=False): "Returns the base profile(s) of this component, if applicable" wires = [] n,l,w,h = self.getDefaultValues(obj) if obj.Base: if obj.Base.isDerivedFrom("Part::Feature"): if obj.Base.Shape: base = obj.Base.Shape.copy() if noplacement: base.Placement = FreeCAD.Placement() if not base.Solids: if base.Faces: import DraftGeomUtils if not DraftGeomUtils.isCoplanar(base.Faces): return [] return [base] basewires = [] if not base.Wires: if len(base.Edges) == 1: import Part basewires = [Part.Wire(base.Edges)] else: basewires = base.Wires if basewires: import DraftGeomUtils,DraftVecUtils,Part for wire in basewires: e = wire.Edges[0] if isinstance(e.Curve,Part.Circle): dvec = e.Vertexes[0].Point.sub(e.Curve.Center) else: dvec = DraftGeomUtils.vec(wire.Edges[0]).cross(n) if not DraftVecUtils.isNull(dvec): dvec.normalize() sh = None if hasattr(obj,"Align"): if obj.Align == "Left": dvec.multiply(w) if hasattr(obj,"Offset"): if obj.Offset.Value: dvec2 = DraftVecUtils.scaleTo(dvec,obj.Offset.Value) wire = DraftGeomUtils.offsetWire(wire,dvec2) w2 = DraftGeomUtils.offsetWire(wire,dvec) w1 = Part.Wire(DraftGeomUtils.sortEdges(wire.Edges)) sh = DraftGeomUtils.bind(w1,w2) elif obj.Align == "Right": dvec.multiply(w) dvec = dvec.negative() if hasattr(obj,"Offset"): if obj.Offset.Value: dvec2 = DraftVecUtils.scaleTo(dvec,obj.Offset.Value) wire = DraftGeomUtils.offsetWire(wire,dvec2) w2 = DraftGeomUtils.offsetWire(wire,dvec) w1 = Part.Wire(DraftGeomUtils.sortEdges(wire.Edges)) sh = DraftGeomUtils.bind(w1,w2) elif obj.Align == "Center": dvec.multiply(w/2) w1 = DraftGeomUtils.offsetWire(wire,dvec) dvec = dvec.negative() w2 = DraftGeomUtils.offsetWire(wire,dvec) sh = DraftGeomUtils.bind(w1,w2) if sh: wires.append(sh) else: wires.append(wire) elif Draft.getType(obj) in ["Wall","Structure"]: if (Draft.getType(obj) == "Structure") and (l > h): if noplacement: h2 = h/2 or 0.5 w2 = w/2 or 0.5 v1 = Vector(-h2,-w2,0) v2 = Vector(h2,-w2,0) v3 = Vector(h2,w2,0) v4 = Vector(-h2,w2,0) else: h2 = h/2 or 0.5 w2 = w/2 or 0.5 v1 = Vector(0,-w2,-h2) v2 = Vector(0,-w2,h2) v3 = Vector(0,w2,h2) v4 = Vector(0,w2,-h2) else: l2 = l/2 or 0.5 w2 = w/2 or 0.5 v1 = Vector(-l2,-w2,0) v2 = Vector(l2,-w2,0) v3 = Vector(l2,w2,0) v4 = Vector(-l2,w2,0) import Part base = Part.makePolygon([v1,v2,v3,v4,v1]) return [base] return wires
def figure_to_freecad_25d_part(ai_figure, ai_extrude_height): """ the first outline of the figure ai_figure is the outer line of the part the other outlines are holes in the part If one outline is not closed, only wire (not face) are extruded the height of the extrusion is ai_extrude_height It returns a FreeCAD Part object If you want to make more complex fuse and cut combinations, you need to work at the outline level (not figure level) """ # extra length to remove skin during 3D cut operation remove_skin_extra = 10.0 # check the number of outlines outline_nb = len(ai_figure) if (outline_nb < 1): print("ERR876: Error, the figure doesn't contain any outlines!") sys.exit(2) # check if one outline is not closed face_nwire = True for oli in range(len(ai_figure)): ol = ai_figure[oli] if (isinstance( ol[0], (list, tuple))): # it's a general outline (not a circle) #print("dbg663: outline with {:d} segments".format(len(ol)-1)) if ((ol[0][0] != ol[-1][-2]) or (ol[0][1] != ol[-1][-1])): face_nwire = False print( "WARN504: Warning, the outline {:d} is not closed! Only wire can be extruded." .format(oli + 1)) # create the FreeCAD part if (face_nwire): # generate a real solid part outer_face = Part.Face( Part.Wire(outline_arc_line(ai_figure[0], 'freecad').Edges)) outer_solid = outer_face.extrude(Base.Vector( 0, 0, ai_extrude_height)) # straight linear extrusion if (outline_nb > 1): # holes need to be cut from outer_solid inner_solid = [] for i in range(outline_nb - 1): inner_face = Part.Face( Part.Wire( outline_arc_line(ai_figure[i + 1], 'freecad').Edges)) inner_solid.append( inner_face.extrude( Base.Vector( 0, 0, ai_extrude_height + 2 * remove_skin_extra))) # straight linear extrusion #inner_hole = Part.makeCompound(inner_solid) # not satisfying result with overlap holes inner_hole = inner_solid[0] for i in range(outline_nb - 2): inner_hole = inner_hole.fuse(inner_solid[i + 1]) inner_hole.translate(Base.Vector(0, 0, -remove_skin_extra)) r_part = outer_solid.cut(inner_hole) else: r_part = outer_solid else: # generate a simple extrusion of wires wire_part = [] for i in range(outline_nb): wire = Part.Wire(outline_arc_line(ai_figure[i], 'freecad').Edges) wire_part.append(wire.extrude(Base.Vector(0, 0, ai_extrude_height))) r_part = Part.makeCompound(wire_part) # return return (r_part)
def trimObject(self): """Trim the actual object.""" import Part if self.extrudeMode: delta = self.extrude(self.shift, real=True) # print("delta", delta) self.doc.openTransaction("Extrude") Gui.addModule("Draft") obj = Draft.extrude(self.obj, delta, solid=True) self.doc.commitTransaction() self.obj = obj else: edges = self.redraw(self.point, self.snapped, self.shift, self.alt, real=True) newshape = Part.Wire(edges) self.doc.openTransaction("Trim/extend") if utils.getType(self.obj) in ["Wire", "BSpline"]: p = [] if self.placement: invpl = self.placement.inverse() for v in newshape.Vertexes: np = v.Point if self.placement: np = invpl.multVec(np) p.append(np) self.obj.Points = p elif utils.getType(self.obj) == "Part::Line": p = [] if self.placement: invpl = self.placement.inverse() for v in newshape.Vertexes: np = v.Point if self.placement: np = invpl.multVec(np) p.append(np) if ((p[0].x == self.obj.X1) and (p[0].y == self.obj.Y1) and (p[0].z == self.obj.Z1)): self.obj.X2 = p[-1].x self.obj.Y2 = p[-1].y self.obj.Z2 = p[-1].z elif ((p[-1].x == self.obj.X1) and (p[-1].y == self.obj.Y1) and (p[-1].z == self.obj.Z1)): self.obj.X2 = p[0].x self.obj.Y2 = p[0].y self.obj.Z2 = p[0].z elif ((p[0].x == self.obj.X2) and (p[0].y == self.obj.Y2) and (p[0].z == self.obj.Z2)): self.obj.X1 = p[-1].x self.obj.Y1 = p[-1].y self.obj.Z1 = p[-1].z else: self.obj.X1 = p[0].x self.obj.Y1 = p[0].y self.obj.Z1 = p[0].z elif utils.getType(self.obj) == "Circle": angles = self.ghost[0].getAngles() # print("original", self.obj.FirstAngle," ",self.obj.LastAngle) # print("new", angles) if angles[0] > angles[1]: angles = (angles[1], angles[0]) self.obj.FirstAngle = angles[0] self.obj.LastAngle = angles[1] else: self.obj.Shape = newshape self.doc.commitTransaction() self.doc.recompute() for g in self.ghost: g.off()
def trimObjects(self, objectslist): """Attempt to trim two objects together.""" import Part import DraftGeomUtils wires = [] for obj in objectslist: if not utils.getType(obj) in ["Wire", "Circle"]: _err( translate( "draft", "Unable to trim these objects, " "only Draft wires and arcs are supported.")) return if len(obj.Shape.Wires) > 1: _err( translate( "draft", "Unable to trim these objects, " "too many wires")) return if len(obj.Shape.Wires) == 1: wires.append(obj.Shape.Wires[0]) else: wires.append(Part.Wire(obj.Shape.Edges)) ints = [] edge1 = None edge2 = None for i1, e1 in enumerate(wires[0].Edges): for i2, e2 in enumerate(wires[1].Edges): i = DraftGeomUtils.findIntersection(e1, e2, dts=False) if len(i) == 1: ints.append(i[0]) edge1 = i1 edge2 = i2 if not ints: _err(translate("draft", "These objects don't intersect.")) return if len(ints) != 1: _err(translate("draft", "Too many intersection points.")) return v11 = wires[0].Vertexes[0].Point v12 = wires[0].Vertexes[-1].Point v21 = wires[1].Vertexes[0].Point v22 = wires[1].Vertexes[-1].Point if DraftVecUtils.closest(ints[0], [v11, v12]) == 1: last1 = True else: last1 = False if DraftVecUtils.closest(ints[0], [v21, v22]) == 1: last2 = True else: last2 = False for i, obj in enumerate(objectslist): if i == 0: ed = edge1 la = last1 else: ed = edge2 la = last2 if utils.getType(obj) == "Wire": if la: pts = obj.Points[:ed + 1] + ints else: pts = ints + obj.Points[ed + 1:] obj.Points = pts else: vec = ints[0].sub(obj.Placement.Base) vec = obj.Placement.inverse().Rotation.multVec(vec) _x = App.Vector(1, 0, 0) _ang = -DraftVecUtils.angle(vec, obj.Placement.Rotation.multVec(_x), obj.Shape.Edges[0].Curve.Axis) ang = math.degrees(_ang) if la: obj.LastAngle = ang else: obj.FirstAngle = ang self.doc.recompute()
def execute(self, fp): if fp.Base: import Part #fp.Shape=fp.Base.Shape.Wires[0] fp.Shape = Part.Wire( fp.Base.Shape.Wires[0]) # works with 0.13 stable
def Activated(self): # check that the selection contains exactly what we want selection = FreeCADGui.Selection.getSelectionEx() if len(selection) != 1: FreeCAD.Console.PrintError( translate( "Path_Pocket", "Please select an edges loop from one object, or a single face\n" )) return if len(selection[0].SubObjects) == 0: FreeCAD.Console.PrintError( translate( "Path_Pocket", "Please select an edges loop from one object, or a single face\n" )) return for s in selection[0].SubObjects: if s.ShapeType != "Edge": if (s.ShapeType != "Face") or (len(selection[0].SubObjects) != 1): FreeCAD.Console.PrintError( translate( "Path_Pocket", "Please select only edges or a single face\n")) return if selection[0].SubObjects[0].ShapeType == "Edge": try: import Part w = Part.Wire(selection[0].SubObjects) except: FreeCAD.Console.PrintError( translate("Path_Pocket", "The selected edges don't form a loop\n")) return # if everything is ok, execute and register the transaction in the undo/redo stack FreeCAD.ActiveDocument.openTransaction( translate("Path_Pocket", "Create Pocket")) FreeCADGui.addModule("PathScripts.PathPocket") FreeCADGui.doCommand('prjexists = False') FreeCADGui.doCommand( 'obj = FreeCAD.ActiveDocument.addObject("Path::FeaturePython","Pocket")' ) FreeCADGui.doCommand('PathScripts.PathPocket.ObjectPocket(obj)') FreeCADGui.doCommand( 'PathScripts.PathPocket.ViewProviderPocket(obj.ViewObject)') subs = "[" for s in selection[0].SubElementNames: subs += '"' + s + '",' subs += "]" FreeCADGui.doCommand('obj.Base = (FreeCAD.ActiveDocument.' + selection[0].ObjectName + ',' + subs + ')') FreeCADGui.doCommand('obj.Active = True') snippet = ''' from PathScripts import PathUtils PathUtils.addToProject(obj) ZMax = obj.Base[0].Shape.BoundBox.ZMax ZMin = obj.Base[0].Shape.BoundBox.ZMin obj.StepDown = 1.0 obj.StartDepth = ZMax obj.FinalDepth = ZMin obj.ClearanceHeight = ZMax + 5.0 ''' FreeCADGui.doCommand(snippet) FreeCAD.ActiveDocument.commitTransaction() FreeCAD.ActiveDocument.recompute()
def execute(self, obj): if obj.Base: tool = PathUtils.getLastTool(obj) if tool: radius = tool.Diameter / 2 if radius < 0: # safe guard radius -= radius else: # temporary value, to be taken from the properties later on radius = 1 import Part, DraftGeomUtils if "Face" in obj.Base[1][0]: shape = getattr(obj.Base[0].Shape, obj.Base[1][0]) else: edges = [ getattr(obj.Base[0].Shape, sub) for sub in obj.Base[1] ] shape = Part.Wire(edges) print len(edges) # absolute coords, millimeters, cancel offsets output = "G90\nG21\nG40\n" # save tool if obj.ToolNumber > 0 and tool.ToolNumber != obj.ToolNumber: output += "M06 T" + str(tool.ToolNumber) + "\n" # build offsets offsets = [] nextradius = radius result = DraftGeomUtils.pocket2d(shape, nextradius) while result: #print "Adding " + str(len(result)) + " wires" offsets.extend(result) nextradius += radius result = DraftGeomUtils.pocket2d(shape, nextradius) # first move will be rapid, subsequent will be at feed rate first = True startPoint = None fastZPos = max(obj.StartDepth + 2, obj.RetractHeight) # revert the list so we start with the outer wires if obj.StartAt != 'Edge': offsets.reverse() # print "startDepth: " + str(obj.StartDepth) # print "finalDepth: " + str(obj.FinalDepth) # print "stepDown: " + str(obj.StepDown) # print "finishDepth" + str(obj.FinishDepth) # print "offsets:", len(offsets) def prnt(vlu): return str("%.4f" % round(vlu, 4)) #Fraction of tool radius our plunge helix is to be #FIXME: This should be configurable plungeR = 0.75 #(minimum) Fraction of tool DIAMETER to go back and forth while ramp-plunging #FIXME: This should be configurable #FIXME: The ramp plunging should maybe even be limited to this distance; I don't know what's best rampD = 0.75 #Total offset from the desired pocket edge is tool radius plus the plunge helix radius #Any point on these curves could be the center of a plunge helixBounds = DraftGeomUtils.pocket2d( shape, tool.Diameter / 2. * (1 + plungeR)) #Try to find a location to nicely plunge, starting with a helix, then ramp #Can't do it without knowledge of a tool plungePos = None rampEdge = None if not tool: raise Error("Ramp plunge location-finding requires a tool") return else: #Since we're going to start machining either the inner-most #edge or the outer (depending on StartAt setting), try to #plunge near that location if helixBounds: #Edge is easy- pick a point on helixBounds and go with it if obj.StartAt == 'Edge': plungePos = helixBounds[0].Edges[0].Vertexes[0].Point #Center is harder- use a point from the first offset, check if it works else: plungePos = offsets[0].Edges[0].Vertexes[0].Point #If it turns out this is invalid for some reason, nuke plungePos [perp, idx] = DraftGeomUtils.findPerpendicular( plungePos, shape.Edges) if not perp or perp.Length < tool.Diameter / 2. * ( 1 + plungeR): plungePos = None #FIXME: Really need to do a point-in-polygon operation to make sure this is within helixBounds #Or some math to prove that it has to be (doubt that's true) #Maybe reverse helixBounds and pick off that? #If we didn't find a place to helix, how about a ramp? if not plungePos: #Check first edge of our offsets if (offsets[0].Edges[0].Length >= tool.Diameter * rampD) and not (isinstance( offsets[0].Edges[0].Curve, Part.Circle)): rampEdge = offsets[0].Edges[0] #The last edge also connects with the starting location- try that elif (offsets[0].Edges[-1].Length >= tool.Diameter * rampD) and not (isinstance( offsets[0].Edges[-1].Curve, Part.Circle)): rampEdge = offsets[0].Edges[-1] else: print "Neither edge works: " + str( offsets[0].Edges[0]) + ", " + str( offsets[0].Edges[-1]) #FIXME: There's got to be a smarter way to find a place to ramp #Returns gcode to perform a rapid move def rapid(x=None, y=None, z=None): retstr = "G00" if (x != None) or (y != None) or (z != None): if (x != None): retstr += " X" + str("%.4f" % x) if (y != None): retstr += " Y" + str("%.4f" % y) if (z != None): retstr += " Z" + str("%.4f" % z) else: return "" return retstr + "\n" #Returns gcode to perform a linear feed def feed(x=None, y=None, z=None): global feedxy retstr = "G01 F" if (x == None) and (y == None): retstr += str("%.4f" % obj.HorizFeed) else: retstr += str("%.4f" % obj.VertFeed) if (x != None) or (y != None) or (z != None): if (x != None): retstr += " X" + str("%.4f" % x) if (y != None): retstr += " Y" + str("%.4f" % y) if (z != None): retstr += " Z" + str("%.4f" % z) else: return "" return retstr + "\n" #Returns gcode to perform an arc #Assumes XY plane or helix around Z #Don't worry about starting Z- assume that's dealt with elsewhere def arc(cx, cy, sx, sy, ex, ey, ez=None, ccw=False): #If start/end radii aren't within eps, abort eps = 0.01 if (math.sqrt((cx - sx)**2 + (cy - sy)**2) - math.sqrt((cx - ex)**2 + (cy - ey)**2)) >= eps: print "ERROR: Illegal arc: Stand and end radii not equal" return "" #Set [C]CW and feed retstr = "" if ccw: retstr += "G03 F" else: retstr += "G02 F" retstr += str(obj.HorizFeed) #End location retstr += " X" + str("%.4f" % ex) + " Y" + str("%.4f" % ey) #Helix if requested if ez != None: retstr += " Z" + str("%.4f" % ez) #Append center offsets retstr += " I" + str("%.4f" % (cx - sx)) + " J" + str("%.4f" % (cy - sy)) return retstr + "\n" #Returns gcode to helically plunge #destZ is the milling level #startZ is the height we can safely feed down to before helix-ing def helicalPlunge(plungePos, rampangle, destZ, startZ): helixCmds = "(START HELICAL PLUNGE)\n" if (plungePos == None): raise Error("Helical plunging requires a position!") return None if (not tool): raise Error("Helical plunging requires a tool!") return None helixX = plungePos.x + tool.Diameter / 2. * plungeR helixY = plungePos.y helixCirc = math.pi * tool.Diameter * plungeR dzPerRev = math.sin(rampangle / 180. * math.pi) * helixCirc #Go to the start of the helix position helixCmds += rapid(helixX, helixY) helixCmds += rapid(z=startZ) #Helix as required to get to the requested depth lastZ = startZ curZ = max(startZ - dzPerRev, destZ) done = False while not done: done = (curZ == destZ) #NOTE: FreeCAD doesn't render this, but at least LinuxCNC considers it valid #helixCmds += arc(plungePos.x, plungePos.y, helixX, helixY, helixX, helixY, ez = curZ, ccw=True) #Use two half-helixes; FreeCAD renders that correctly, #and it fits with the other code breaking up 360-degree arcs helixCmds += arc(plungePos.x, plungePos.y, helixX, helixY, helixX - tool.Diameter * plungeR, helixY, ez=(curZ + lastZ) / 2., ccw=True) helixCmds += arc(plungePos.x, plungePos.y, helixX - tool.Diameter * plungeR, helixY, helixX, helixY, ez=curZ, ccw=True) lastZ = curZ curZ = max(curZ - dzPerRev, destZ) return helixCmds #Returns commands to linearly ramp into a cut #FIXME: This ramps along the first edge, assuming it's long #enough, NOT just wiggling back and forth by ~0.75 * toolD. #Not sure if that's any worse, but it's simpler #FIXME: This code is untested def rampPlunge(edge, rampangle, destZ, startZ): rampCmds = "(START RAMP PLUNGE)\n" if (edge == None): raise Error("Ramp plunging requires an edge!") return None if (not tool): raise Error("Ramp plunging requires a tool!") sPoint = edge.Vertexes[0].Point ePoint = edge.Vertexes[1].Point #Evidently edges can get flipped- pick the right one in this case #FIXME: This is iffy code, based on what already existed in the "for vpos ..." loop below if ePoint == sPoint: #print "FLIP" ePoint = edge.Vertexes[-1].Point #print "Start: " + str(sPoint) + " End: " + str(ePoint) + " Zhigh: " + prnt(startZ) + " ZLow: " + prnt(destZ) rampDist = edge.Length rampDZ = math.sin(rampangle / 180. * math.pi) * rampDist rampCmds += rapid(sPoint.x, sPoint.y) rampCmds += rapid(z=startZ) #Ramp down to the requested depth #FIXME: This might be an arc, so handle that as well lastZ = startZ curZ = max(startZ - rampDZ, destZ) done = False while not done: done = (curZ == destZ) #If it's an arc, handle it! if isinstance(edge.Curve, Part.Circle): raise Error( "rampPlunge: Screw it, not handling an arc.") #Straight feed! Easy! else: rampCmds += feed(ePoint.x, ePoint.y, curZ) rampCmds += feed(sPoint.x, sPoint.y) lastZ = curZ curZ = max(curZ - rampDZ, destZ) return rampCmds #For helix-ing/ramping, know where we were last time #FIXME: Can probably get this from the "machine"? lastZ = fastZPos for vpos in frange(obj.StartDepth, obj.FinalDepth, obj.StepDown, obj.FinishDepth): # print "vpos: " + str(vpos) #Every for every depth we should helix down first = True # loop over successive wires for currentWire in offsets: # print "new line (offset)" last = None for edge in currentWire.Edges: # print "new edge" if not last: # we set the base GO to our fast move to our starting pos if first: #If we can helix, do so if plungePos: output += helicalPlunge( plungePos, 3, vpos, lastZ) #print output lastZ = vpos #Otherwise, see if we can ramp #FIXME: This could be a LOT smarter (eg, searching for a longer leg of the edge to ramp along) elif rampEdge: output += rampPlunge( rampEdge, 3, vpos, lastZ) lastZ = vpos #Otherwise, straight plunge... Don't want to, but sometimes you might not have a choice. #FIXME: At least not with the lazy ramp programming above... else: print "WARNING: Straight-plunging... probably not good, but we didn't find a place to helix or ramp" startPoint = edge.Vertexes[0].Point output += "G0 X" + prnt(startPoint.x) + " Y" + prnt(startPoint.y) +\ " Z" + prnt(fastZPos) + "\n" first = False #then move slow down to our starting point for our profile last = edge.Vertexes[0].Point output += "G1 X" + prnt(last.x) + " Y" + prnt( last.y) + " Z" + prnt(vpos) + "\n" #if isinstance(edge.Curve,Part.Circle): if DraftGeomUtils.geomType(edge) == "Circle": point = edge.Vertexes[-1].Point if point == last: # edges can come flipped point = edge.Vertexes[0].Point # print "flipped" center = edge.Curve.Center relcenter = center.sub(last) v1 = last.sub(center) v2 = point.sub(center) if v1.cross(v2).z < 0: output += "G2" else: output += "G3" output += " X" + prnt(point.x) + " Y" + prnt( point.y) + " Z" + prnt(vpos) output += " I" + prnt(relcenter.x) + " J" + prnt( relcenter.y) + " K" + prnt(relcenter.z) output += "\n" last = point else: point = edge.Vertexes[-1].Point if point == last: # edges can come flipped point = edge.Vertexes[0].Point output += "G1 X" + prnt(point.x) + " Y" + prnt( point.y) + " Z" + prnt(vpos) + "\n" last = point #move back up output += "G0 Z" + prnt(fastZPos) + "\n" # print output # path = Path.Path(output) # obj.Path = path if obj.Active: path = Path.Path(output) obj.Path = path obj.ViewObject.Visibility = True else: path = Path.Path("(inactive operation)") obj.Path = path obj.ViewObject.Visibility = False
def setup(doc=None, solvertype="ccxtools"): # init FreeCAD document if doc is None: doc = init_doc() # explanation object # just keep the following line and change text string in get_explanation method manager.add_explanation_obj( doc, get_explanation(manager.get_header(get_information()))) # geometric object v1 = vec(-200, -100, 0) v2 = vec(200, -100, 0) v3 = vec(200, 100, 0) v4 = vec(-200, 100, 0) l1 = ln(v1, v2) l2 = ln(v2, v3) l3 = ln(v3, v4) l4 = ln(v4, v1) v5 = vec(0, 0, 0) c1 = ci(50, v5) face = Part.makeFace([Part.Wire([l1, l2, l3, l4]), c1], "Part::FaceMakerBullseye") geom_obj = doc.addObject("Part::Feature", "Hole_Plate") geom_obj.Shape = face.extrude(vec(0, 0, 10)) doc.recompute() if FreeCAD.GuiUp: geom_obj.ViewObject.Document.activeView().viewAxonometric() geom_obj.ViewObject.Document.activeView().fitAll() # analysis analysis = ObjectsFem.makeAnalysis(doc, "Analysis") # solver if solvertype == "calculix": solver_obj = ObjectsFem.makeSolverCalculix(doc, "SolverCalculiX") elif solvertype == "ccxtools": solver_obj = ObjectsFem.makeSolverCalculixCcxTools( doc, "CalculiXccxTools") solver_obj.WorkingDir = u"" else: FreeCAD.Console.PrintWarning( "Not known or not supported solver type: {}. " "No solver object was created.\n".format(solvertype)) if solvertype == "calculix" or solvertype == "ccxtools": solver_obj.SplitInputWriter = False solver_obj.AnalysisType = "static" solver_obj.GeometricalNonlinearity = "linear" solver_obj.ThermoMechSteadyState = False solver_obj.MatrixSolverType = "default" solver_obj.IterationsControlParameterTimeUse = False solver_obj.GeometricalNonlinearity = 'nonlinear' solver_obj.MaterialNonlinearity = 'nonlinear' analysis.addObject(solver_obj) # linear material material_obj = ObjectsFem.makeMaterialSolid(doc, "Material_lin") matprop = material_obj.Material matprop["Name"] = "CalculiX-Steel" matprop["YoungsModulus"] = "210000 MPa" matprop["PoissonRatio"] = "0.30" material_obj.Material = matprop analysis.addObject(material_obj) # nonlinear material name_nlm = "Material_nonlin" nonlinear_mat = ObjectsFem.makeMaterialMechanicalNonlinear( doc, material_obj, name_nlm) nonlinear_mat.YieldPoints = ['240.0, 0.0', '270.0, 0.025'] analysis.addObject(nonlinear_mat) # check solver attributes, Nonlinearity needs to be set to nonlinear # constraint fixed con_fixed = ObjectsFem.makeConstraintFixed(doc, "ConstraintFixed") con_fixed.References = [(geom_obj, "Face4")] analysis.addObject(con_fixed) # pressure constraint con_pressure = ObjectsFem.makeConstraintPressure(doc, "ConstraintPressure") con_pressure.References = [(geom_obj, "Face2")] con_pressure.Pressure = 130.0 con_pressure.Reversed = True analysis.addObject(con_pressure) # mesh from .meshes.mesh_platewithhole_tetra10 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( ObjectsFem.makeMeshGmsh(doc, get_meshname()))[0] femmesh_obj.FemMesh = fem_mesh femmesh_obj.Part = geom_obj femmesh_obj.SecondOrderLinear = False doc.recompute() return doc
def outline_arc_line_test1(): """ test the functions outline_arc_line and outline_circle. """ l_ol1 = [[0, 0], [20, 0], [20, 20], [0, 20], [0, 0]] l_ol2 = [[110, 0], [120, 0], [130, 0, 130, 10], [130, 20], [130, 30, 120, 30], [110, 30], [100, 30, 100, 20], [100, 10], [100, 0, 110, 0]] l_ol3 = [[210, 0], [220, 0], [230, 0, 230, 10], [230, 20], [230, 30, 220, 30], [210, 30], [200, 30, 200, 20], [200, 10]] #[200,0, 210,0]] # check CC (clock wise) l_ol4 = [[300, 10], [300, 20], [300, 30, 310, 30], [320, 30], [330, 30, 330, 20], [330, 10], [330, 0, 320, 0], [310, 0]] l_ol5 = [[0, 100], [100, 150], [110, 155, 120, 150], [150, 110], [160, 100, 170, 105], [200, 200], [0, 200], [0, 100]] l_ols = [l_ol1, l_ol2, l_ol3, l_ol4, l_ol5] #l_ols = [l_ol2] # circle l_circle_center = [200, 200] l_circle_radius = 150 # backend freecad print("dbg701: test1 backend freecad") for i_ol in l_ols: r_ol = outline_arc_line(i_ol, 'freecad') #Part.show(r_ol) l_test_face = Part.Face(Part.Wire(r_ol.Edges)) r_test_solid = l_test_face.extrude(Base.Vector( 0, 0, 1)) # straight linear extrusion Part.show(r_test_solid) r_ol = outline_circle(l_circle_center, l_circle_radius, 'freecad') l_test_face = Part.Face(Part.Wire(r_ol.Edges)) r_test_solid = l_test_face.extrude(Base.Vector( 0, 0, 1)) # straight linear extrusion Part.show(r_test_solid) # create the output directory l_output_dir = "test_output" print("Create the output directory: {:s}".format(l_output_dir)) design_help.mkdir_p(l_output_dir) # backend svgwrite print("dbg702: test1 backend svgwrite") output_svg_file_name = "{:s}/outline_arc_line_test1_00.svg".format( l_output_dir) object_svg = svgwrite.Drawing(filename=output_svg_file_name) #output_file_idx = 0 for i_ol in l_ols: #output_file_idx += 1 #output_svg_file_name = "outline_arc_line_test1_{:02d}.svg".format(output_file_idx) #object_svg = svgwrite.Drawing(filename = output_svg_file_name) svg_outline = outline_arc_line(i_ol, 'svgwrite') for one_line_or_arc in svg_outline: object_svg.add(one_line_or_arc) #object_svg.save() one_circle = outline_circle(l_circle_center, l_circle_radius, 'svgwrite') object_svg.add(one_circle[0]) object_svg.save() # backend dxfwrite print("dbg703: test1 backend dxfwrite") output_dxf_file_name = "{:s}/outline_arc_line_test1_00.dxf".format( l_output_dir) object_dxf = DXFEngine.drawing(output_dxf_file_name) #object_dxf.add_layer(default_dxf_layer_name) for i_ol in l_ols: dxf_outline = outline_arc_line(i_ol, 'dxfwrite') for one_line_or_arc in dxf_outline: object_dxf.add(one_line_or_arc) one_circle = outline_circle(l_circle_center, l_circle_radius, 'dxfwrite') object_dxf.add(one_circle[0]) object_dxf.save() # backend tkinter print("dbg704: test1 backend tkinter") tk_root = Tkinter.Tk() #my_canvas = display_backend.Two_Canvas(tk_root) my_canvas = Two_Canvas(tk_root) # callback function for display_backend def sub_canvas_graphics(ai_rotation_direction, ai_angle_position): # angle position l_angle_position = float(ai_angle_position) / 100 # r_canvas_graphics = [] for i_ol in l_ols: r_canvas_graphics.append( ('graphic_lines', outline_arc_line(i_ol, 'tkinter'), 'red', 2)) r_canvas_graphics.append( ('graphic_lines', outline_circle(l_circle_center, l_circle_radius, 'tkinter'), 'blue', 2)) return (r_canvas_graphics) # end of callback function my_canvas.add_canvas_graphic_function(sub_canvas_graphics) tk_root.mainloop() del (my_canvas, tk_root ) # because Tkinter will be used again later in this script #time.sleep(0.3) ### test the figure-level functions wfl_outer_rectangle_B = [[-60, -40], [60, -40], [60, 40], [-60, 40], [-60, -40]] wfl_inner_square_B = [[-10, -10], [10, -10], [10, 10], [-10, 10], [-10, -10]] wfl_inner_circle1 = [30, 0, 15] wfl_inner_circle2 = [40, 0, 10] wfl_figure = [ wfl_outer_rectangle_B, wfl_inner_square_B, wfl_inner_circle1, wfl_inner_circle2 ] # display the figure figure_simple_display(wfl_figure) wfl_extrude_height = 20.0 # create a FreeCAD part wfl_part = figure_to_freecad_25d_part(wfl_figure, wfl_extrude_height) # output file with mozman print("Generate {:s}/obt1_with_mozman.svg".format(l_output_dir)) write_figure_in_svg(wfl_figure, "{:s}/obt1_with_mozman.svg".format(l_output_dir)) print("Generate {:s}/obt1_with_mozman.dxf".format(l_output_dir)) write_figure_in_dxf(wfl_figure, "{:s}/obt1_with_mozman.dxf".format(l_output_dir)) # wfl_part in 3D BRep print("Generate {:s}/obt1_part.brep".format(l_output_dir)) wfl_part.exportBrep("{:s}/obt1_part.brep".format(l_output_dir)) # wfl_part in 2D DXF print("Generate {:s}/obt1_part.dxf".format(l_output_dir)) export_2d.export_to_dxf( wfl_part, Base.Vector(0, 0, 1), wfl_extrude_height / 2, "{:s}/obt1_part.dxf".format(l_output_dir) ) # slice wfl_part in the XY plan at a height of wfl_extrude_height/2 # r_test = 1 return (r_test)
def getExtrusionData(self, obj): """returns (shape,extrusion vector,placement) or None""" import Part, DraftGeomUtils data = ArchComponent.Component.getExtrusionData(self, obj) if data: if not isinstance(data[0], list): # multifuses not considered here return data length = obj.Length.Value width = obj.Width.Value height = obj.Height.Value if not height: for p in obj.InList: if Draft.getType(p) == "Floor": if p.Height.Value: height = p.Height.Value if obj.Normal == Vector(0, 0, 0): normal = Vector(0, 0, 1) else: normal = Vector(obj.Normal) base = None placement = None basewires = None # build wall layers layers = [] if hasattr(obj, "Material"): if obj.Material: if hasattr(obj.Material, "Materials"): varwidth = 0 restwidth = width - sum(obj.Material.Thicknesses) if restwidth > 0: varwidth = [ t for t in obj.Material.Thicknesses if t == 0 ] if varwidth: varwidth = restwidth / len(varwidth) for t in obj.Material.Thicknesses: if t: layers.append(t) elif varwidth: layers.append(varwidth) if obj.Base: if obj.Base.isDerivedFrom("Part::Feature"): if obj.Base.Shape: if obj.Base.Shape.Solids: return None elif obj.Face > 0: if len(obj.Base.Shape.Faces) >= obj.Face: face = obj.Base.Shape.Faces[obj.Face - 1] # this wall is based on a specific face of its base object normal = face.normalAt(0, 0) if normal.getAngle(Vector(0, 0, 1)) > math.pi / 4: normal.multiply(width) base = face.extrude(normal) if obj.Align == "Center": base.translate( normal.negative().multiply(0.5)) elif obj.Align == "Right": base.translate(normal.negative()) else: normal.multiply(height) base = face.extrude(normal) base, placement = self.rebase(base) return (base, normal, placement) elif obj.Base.Shape.Faces: if not DraftGeomUtils.isCoplanar(obj.Base.Shape.Faces): return None else: base, placement = self.rebase(obj.Base.Shape) elif obj.Base.Shape.Wires: basewires = obj.Base.Shape.Wires elif len(obj.Base.Shape.Edges) == 1: basewires = [Part.Wire(obj.Base.Shape.Edges)] if basewires and width: if (len(basewires) == 1) and layers: basewires = [basewires[0] for l in layers] layeroffset = 0 baseface = None for i, wire in enumerate(basewires): e = wire.Edges[0] if isinstance(e.Curve, Part.Circle): dvec = e.Vertexes[0].Point.sub(e.Curve.Center) else: dvec = DraftGeomUtils.vec( wire.Edges[0]).cross(normal) if not DraftVecUtils.isNull(dvec): dvec.normalize() sh = None if obj.Align == "Left": off = obj.Offset.Value if layers: off = off + layeroffset dvec.multiply(layers[i]) layeroffset += layers[i] else: dvec.multiply(width) if off: dvec2 = DraftVecUtils.scaleTo(dvec, off) wire = DraftGeomUtils.offsetWire( wire, dvec2) w2 = DraftGeomUtils.offsetWire(wire, dvec) w1 = Part.Wire(Part.__sortEdges__(wire.Edges)) sh = DraftGeomUtils.bind(w1, w2) elif obj.Align == "Right": dvec = dvec.negative() off = obj.Offset.Value if layers: off = off + layeroffset dvec.multiply(layers[i]) layeroffset += layers[i] else: dvec.multiply(width) if off: dvec2 = DraftVecUtils.scaleTo(dvec, off) wire = DraftGeomUtils.offsetWire( wire, dvec2) w2 = DraftGeomUtils.offsetWire(wire, dvec) w1 = Part.Wire(Part.__sortEdges__(wire.Edges)) sh = DraftGeomUtils.bind(w1, w2) elif obj.Align == "Center": if layers: off = width / 2 - layeroffset d1 = Vector(dvec).multiply(off) w1 = DraftGeomUtils.offsetWire(wire, d1) layeroffset += layers[i] off = width / 2 - layeroffset d1 = Vector(dvec).multiply(off) w2 = DraftGeomUtils.offsetWire(wire, d1) else: dvec.multiply(width / 2) w1 = DraftGeomUtils.offsetWire(wire, dvec) dvec = dvec.negative() w2 = DraftGeomUtils.offsetWire(wire, dvec) sh = DraftGeomUtils.bind(w1, w2) if sh: sh.fix(0.1, 0, 1) # fixes self-intersecting wires f = Part.Face(sh) if baseface: if layers: baseface.append(f) else: baseface = baseface.fuse(f) else: if layers: baseface = [f] else: baseface = f if baseface: base, placement = self.rebase(baseface) else: if layers: totalwidth = sum(layers) offset = 0 base = [] for l in layers: l2 = length / 2 or 0.5 w1 = -totalwidth / 2 + offset w2 = w1 + l v1 = Vector(-l2, w1, 0) v2 = Vector(l2, w1, 0) v3 = Vector(l2, w2, 0) v4 = Vector(-l2, w2, 0) base.append( Part.Face(Part.makePolygon([v1, v2, v3, v4, v1]))) offset += l else: 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.Face(Part.makePolygon([v1, v2, v3, v4, v1])) placement = FreeCAD.Placement() if base and placement: extrusion = normal.multiply(height) return (base, extrusion, placement) return None
rot = FreeCAD.Rotation(FreeCAD.Vector(0,0,1),alfa) centre = FreeCAD.Vector(0,0,0) pos = obj.Placement.Base newplace = FreeCAD.Placement(pos,rot,centre) obj.Placement = newplace App.getDocument('TOBERA').recompute() ##################### CARAS ENTRADA y SALIDA ############ Part.Face(Part.Wire(Part.__sortEdges__([App.ActiveDocument.TUBO.Shape.Edge13, ]))) App.ActiveDocument.addObject('Part::Feature','ENTRADA').Shape=_ Part.Face(Part.Wire(Part.__sortEdges__([App.ActiveDocument.TOBERA.Shape.Edge3, ]))) App.ActiveDocument.addObject('Part::Feature','SALIDA').Shape=_ ###################### MALLADO ##################### __doc__=FreeCAD.getDocument("TOBERA") __mesh__=__doc__.addObject("Mesh::Feature","tubo_mesh") __mesh__.Mesh=MeshPart.meshFromShape(Shape=__doc__.getObject("TUBO").Shape,MaxLength=m_tubo)
def execute(self, obj): import Part plm = obj.Placement if obj.Base and (not obj.Tool): if obj.Base.isDerivedFrom("Sketcher::SketchObject"): shape = obj.Base.Shape.copy() if obj.Base.Shape.isClosed(): if getattr(obj,"MakeFace",True): shape = Part.Face(shape) obj.Shape = shape elif obj.Base and obj.Tool: if hasattr(obj.Base,'Shape') and hasattr(obj.Tool,'Shape'): if (not obj.Base.Shape.isNull()) and (not obj.Tool.Shape.isNull()): sh1 = obj.Base.Shape.copy() sh2 = obj.Tool.Shape.copy() shape = sh1.fuse(sh2) if DraftGeomUtils.isCoplanar(shape.Faces): shape = DraftGeomUtils.concatenate(shape) obj.Shape = shape p = [] for v in shape.Vertexes: p.append(v.Point) if obj.Points != p: obj.Points = p elif obj.Points: if obj.Points[0] == obj.Points[-1]: if not obj.Closed: obj.Closed = True obj.Points.pop() if obj.Closed and (len(obj.Points) > 2): pts = obj.Points if getattr(obj,"Subdivisions",0) > 0: npts = [] for i in range(len(pts)): p1 = pts[i] npts.append(pts[i]) if i == len(pts)-1: p2 = pts[0] else: p2 = pts[i+1] v = p2.sub(p1) v = DraftVecUtils.scaleTo(v,v.Length/(obj.Subdivisions+1)) for j in range(obj.Subdivisions): npts.append(p1.add(App.Vector(v).multiply(j+1))) pts = npts shape = Part.makePolygon(pts+[pts[0]]) if "ChamferSize" in obj.PropertiesList: if obj.ChamferSize.Value != 0: w = DraftGeomUtils.filletWire(shape,obj.ChamferSize.Value,chamfer=True) if w: shape = w if "FilletRadius" in obj.PropertiesList: if obj.FilletRadius.Value != 0: w = DraftGeomUtils.filletWire(shape,obj.FilletRadius.Value) if w: shape = w try: if getattr(obj,"MakeFace",True): shape = Part.Face(shape) except Part.OCCError: pass else: edges = [] pts = obj.Points[1:] lp = obj.Points[0] for p in pts: if not DraftVecUtils.equals(lp,p): if getattr(obj,"Subdivisions",0) > 0: npts = [] v = p.sub(lp) v = DraftVecUtils.scaleTo(v,v.Length/(obj.Subdivisions+1)) edges.append(Part.LineSegment(lp,lp.add(v)).toShape()) lv = lp.add(v) for j in range(obj.Subdivisions): edges.append(Part.LineSegment(lv,lv.add(v)).toShape()) lv = lv.add(v) else: edges.append(Part.LineSegment(lp,p).toShape()) lp = p try: shape = Part.Wire(edges) except Part.OCCError: print("Error wiring edges") shape = None if "ChamferSize" in obj.PropertiesList: if obj.ChamferSize.Value != 0: w = DraftGeomUtils.filletWire(shape,obj.ChamferSize.Value,chamfer=True) if w: shape = w if "FilletRadius" in obj.PropertiesList: if obj.FilletRadius.Value != 0: w = DraftGeomUtils.filletWire(shape,obj.FilletRadius.Value) if w: shape = w if shape: obj.Shape = shape if hasattr(obj,"Area") and hasattr(shape,"Area"): obj.Area = shape.Area if hasattr(obj,"Length"): obj.Length = shape.Length obj.Placement = plm obj.positionBySupport() self.onChanged(obj,"Placement")
def getExtrusionData(self,obj): """returns (shape,extrusion vector,placement) or None""" import Part,DraftGeomUtils data = ArchComponent.Component.getExtrusionData(self,obj) if data: if not isinstance(data[0],list): # multifuses not considered here return data length = obj.Length.Value width = obj.Width.Value height = obj.Height.Value normal = None if not height: for p in obj.InList: if Draft.getType(p) == "Floor": if p.Height.Value: height = p.Height.Value base = None placement = None if obj.Base: if obj.Base.isDerivedFrom("Part::Feature"): if obj.Base.Shape: if obj.Base.Shape.Solids: return None elif obj.Base.Shape.Faces: if not DraftGeomUtils.isCoplanar(obj.Base.Shape.Faces): return None else: base,placement = self.rebase(obj.Base.Shape) normal = obj.Base.Shape.Faces[0].normalAt(0,0) normal = placement.inverse().Rotation.multVec(normal) elif obj.Base.Shape.Wires: baseface = None if hasattr(obj,"FaceMaker"): if obj.FaceMaker != "None": try: baseface = Part.makeFace(obj.Base.Shape.Wires,"Part::FaceMaker"+str(obj.FaceMaker)) except: FreeCAD.Console.PrintError(translate("Arch","Facemaker returned an error")+"\n") return None if len(baseface.Faces) > 1: baseface = baseface.Faces[0] normal = baseface.normalAt(0,0) normal = placement.inverse().Rotation.multVec(normal) if not baseface: for w in obj.Base.Shape.Wires: w.fix(0.1,0,1) # fixes self-intersecting wires f = Part.Face(w) if baseface: baseface = baseface.fuse(f) else: baseface = f normal = f.normalAt(0,0) base,placement = self.rebase(baseface) elif (len(obj.Base.Shape.Edges) == 1) and (len(obj.Base.Shape.Vertexes) == 1): # closed edge w = Part.Wire(obj.Base.Shape.Edges[0]) baseface = Part.Face(w) base,placement = self.rebase(baseface) elif length and width and height: if (length > height) and (obj.Role != "Slab"): h2 = height/2 or 0.5 w2 = width/2 or 0.5 v1 = Vector(0,-w2,-h2) v2 = Vector(0,-w2,h2) v3 = Vector(0,w2,h2) v4 = Vector(0,w2,-h2) else: 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) import Part baseface = Part.Face(Part.makePolygon([v1,v2,v3,v4,v1])) base,placement = self.rebase(baseface) if base and placement: if obj.Normal == Vector(0,0,0): if not normal: normal = Vector(0,0,1) else: normal = Vector(obj.Normal) if (length > height) and (obj.Role != "Slab"): extrusion = normal.multiply(length) else: extrusion = normal.multiply(height) return (base,extrusion,placement) return None
def to_wire(self): return Part.Wire(self.edges)
def process(doc, filename): # The common airfoil dat format has many flavors # This code should work with almost every dialect # Regex to identify data rows and throw away unused metadata regex = re.compile( r'^\s*(?P<xval>(\-|\d*)\.\d+(E\-?\d+)?)\,?\s*(?P<yval>\-?\s*\d*\.\d+(E\-?\d+)?)\s*$' ) afile = pythonopen(filename, 'r') # read the airfoil name which is always at the first line airfoilname = afile.readline().strip() coords = [] upside = True last_x = None # Collect the data for the upper and the lower side separately if possible for lin in afile: curdat = regex.match(lin) if curdat != None: x = float(curdat.group("xval")) y = float(curdat.group("yval")) # the normal processing coords.append(Vector(x, y, 0)) # End of if curdat != None # End of for lin in file afile.close() if len(coords) < 3: print('Did not find enough coordinates\n') return # sometimes coords are divided in upper an lower side # so that x-coordinate begin new from leading or trailing edge # check for start coordinates in the middle of list if coords[0:-1].count(coords[0]) > 1: flippoint = coords.index(coords[0], 1) upper = coords[0:flippoint] lower = coords[flippoint + 1:] lower.reverse() for i in lower: upper.append(i) coords = upper # do we use the parametric Draft Wire? if useDraftWire: obj = makeWire(coords, True) #obj.label = airfoilname else: # alternate solution, uses common Part Faces lines = [] first_v = None last_v = None for v in coords: if first_v == None: first_v = v # End of if first_v == None # Line between v and last_v if they're not equal if (last_v != None) and (last_v != v): lines.append(Part.makeLine(last_v, v)) # End of if (last_v != None) and (last_v != v) # The new last_v last_v = v # End of for v in upper # close the wire if needed if last_v != first_v: lines.append(Part.makeLine(last_v, first_v)) # End of if last_v != first_v wire = Part.Wire(lines) face = Part.Face(wire) obj = FreeCAD.ActiveDocument.addObject('Part::Feature', airfoilname) obj.Shape = face doc.recompute()
def getObjectData(obj, wireframeMode=wireframeStyle): """returns the geometry data of an object as three.js snippet. wireframeMode can be multimaterial, faceloop or None""" result = "" wires = [] if obj.isDerivedFrom("Part::Feature"): fcmesh = obj.Shape.tessellate(0.1) result = "var geom = new THREE.Geometry();\n" # adding vertices data for i in range(len(fcmesh[0])): v = fcmesh[0][i] result += tab + "var v" + str(i) + " = new THREE.Vector3(" + str( v.x) + "," + str(v.y) + "," + str(v.z) + ");\n" result += tab + "console.log(geom.vertices)\n" for i in range(len(fcmesh[0])): result += tab + "geom.vertices.push(v" + str(i) + ");\n" # adding facets data for f in fcmesh[1]: result += tab + "geom.faces.push( new THREE.Face3" + str( f) + " );\n" for f in obj.Shape.Faces: for w in f.Wires: wo = Part.Wire(Part.__sortEdges__(w.Edges)) wires.append(wo.discretize(QuasiDeflection=0.1)) elif obj.isDerivedFrom("Mesh::Feature"): mesh = obj.Mesh result = "var geom = new THREE.Geometry();\n" # adding vertices data for p in mesh.Points: v = p.Vector i = p.Index result += tab + "var v" + str(i) + " = new THREE.Vector3(" + str( v.x) + "," + str(v.y) + "," + str(v.z) + ");\n" result += tab + "console.log(geom.vertices)\n" for p in mesh.Points: result += tab + "geom.vertices.push(v" + str(p.Index) + ");\n" # adding facets data for f in mesh.Facets: pointIndices = tuple([int(i) for i in f.PointIndices]) result += tab + "geom.faces.push( new THREE.Face3" + str( pointIndices) + " );\n" if result: # adding a base material if FreeCADGui: col = obj.ViewObject.ShapeColor rgb = Draft.getrgb(col, testbw=False) else: rgb = "#888888" # test color result += tab + "var basematerial = new THREE.MeshBasicMaterial( { color: 0x" + str( rgb)[1:] + " } );\n" #result += tab+"var basematerial = new THREE.MeshLambertMaterial( { color: 0x"+str(rgb)[1:]+" } );\n" if wireframeMode == "faceloop": # adding the mesh to the scene with a wireframe copy result += tab + "var mesh = new THREE.Mesh( geom, basematerial );\n" result += tab + "scene.add( mesh );\n" result += tab + "var linematerial = new THREE.LineBasicMaterial({linewidth: %d, color: 0x000000,});\n" % linewidth for w in wires: result += tab + "var wire = new THREE.Geometry();\n" for p in w: result += tab + "wire.vertices.push(new THREE.Vector3(" result += str(p.x) + ", " + str(p.y) + ", " + str( p.z) + "));\n" result += tab + "var line = new THREE.Line(wire, linematerial);\n" result += tab + "scene.add(line);\n" elif wireframeMode == "multimaterial": # adding a wireframe material result += tab + "var wireframe = new THREE.MeshBasicMaterial( { color: " result += "0x000000, wireframe: true, transparent: true } );\n" result += tab + "var material = [ basematerial, wireframe ];\n" result += tab + "var mesh = new THREE.SceneUtils.createMultiMaterialObject( geom, material );\n" result += tab + "scene.add( mesh );\n" + tab else: # adding the mesh to the scene with simple material result += tab + "var mesh = new THREE.Mesh( geom, basematerial );\n" result += tab + "scene.add( mesh );\n" + tab return result
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 setup(doc=None, solvertype="ccxtools"): if doc is None: doc = init_doc() # geometry object # name is important because the other method in this module use obj name l1 = Part.makeLine((-142.5, -142.5, 0), (142.5, -142.5, 0)) l2 = Part.makeLine((142.5, -142.5, 0), (142.5, 142.5, 0)) l3 = Part.makeLine((142.5, 142.5, 0), (-142.5, 142.5, 0)) l4 = Part.makeLine((-142.5, 142.5, 0), (-142.5, -142.5, 0)) wire = Part.Wire([l1, l2, l3, l4]) shape = wire.extrude(Vector(0, 0, 1000)) geom_obj = doc.addObject('Part::Feature', 'SquareTube') geom_obj.Shape = shape points_forces = [] points_forces.append(Part.Vertex(-142.5, 142.5, 0.0)) points_forces.append(Part.Vertex(-142.5, -142.5, 0.0)) points_forces.append(Part.Vertex(-142.5, 95.0, 0.0)) points_forces.append(Part.Vertex(-142.5, 47.5, 0.0)) points_forces.append(Part.Vertex(-142.5, 0.0, 0.0)) points_forces.append(Part.Vertex(-142.5, -47.5, 0.0)) points_forces.append(Part.Vertex(-142.5, -95.0, 0.0)) points_forces.append(Part.Vertex(142.5, -142.5, 0.0)) points_forces.append(Part.Vertex(-95.0, -142.5, 0.0)) points_forces.append(Part.Vertex(-47.5, -142.5, 0.0)) points_forces.append(Part.Vertex(0.0, -142.5, 0.0)) points_forces.append(Part.Vertex(47.5, -142.5, 0.0)) points_forces.append(Part.Vertex(95.0, -142.5, 0.0)) points_forces.append(Part.Vertex(142.5, 142.5, 0.0)) points_forces.append(Part.Vertex(142.5, -95.0, 0.0)) points_forces.append(Part.Vertex(142.5, -47.5, 0.0)) points_forces.append(Part.Vertex(142.5, 0.0, 0.0)) points_forces.append(Part.Vertex(142.5, 47.5, 0.0)) points_forces.append(Part.Vertex(142.5, 95.0, 0.0)) points_forces.append(Part.Vertex(95.0, 142.5, 0.0)) points_forces.append(Part.Vertex(47.5, 142.5, 0.0)) points_forces.append(Part.Vertex(0.0, 142.5, 0.0)) points_forces.append(Part.Vertex(-47.5, 142.5, 0.0)) points_forces.append(Part.Vertex(-95.0, 142.5, 0.0)) points_forces.append(Part.Vertex(-142.5, 118.75, 0.0)) points_forces.append(Part.Vertex(-142.5, -118.75, 0.0)) points_forces.append(Part.Vertex(-142.5, 71.25, 0.0)) points_forces.append(Part.Vertex(-142.5, 23.75, 0.0)) points_forces.append(Part.Vertex(-142.5, -23.75, 0.0)) points_forces.append(Part.Vertex(-142.5, -71.25, 0.0)) points_forces.append(Part.Vertex(118.75, -142.5, 0.0)) points_forces.append(Part.Vertex(-71.25, -142.5, 0.0)) points_forces.append(Part.Vertex(-118.75, -142.5, 0.0)) points_forces.append(Part.Vertex(-23.75, -142.5, 0.0)) points_forces.append(Part.Vertex(23.75, -142.5, 0.0)) points_forces.append(Part.Vertex(71.25, -142.5, 0.0)) points_forces.append(Part.Vertex(142.5, 118.75, 0.0)) points_forces.append(Part.Vertex(142.5, -71.25, 0.0)) points_forces.append(Part.Vertex(142.5, -118.75, 0.0)) points_forces.append(Part.Vertex(142.5, -23.75, 0.0)) points_forces.append(Part.Vertex(142.5, 23.75, 0.0)) points_forces.append(Part.Vertex(142.5, 71.25, 0.0)) points_forces.append(Part.Vertex(71.25, 142.5, 0.0)) points_forces.append(Part.Vertex(118.75, 142.5, 0.0)) points_forces.append(Part.Vertex(23.75, 142.5, 0.0)) points_forces.append(Part.Vertex(-23.75, 142.5, 0.0)) points_forces.append(Part.Vertex(-71.25, 142.5, 0.0)) points_forces.append(Part.Vertex(-118.75, 142.5, 0.0)) points_fixes = [] points_fixes.append(Part.Vertex(-142.5, 142.5, 1000.0)) points_fixes.append(Part.Vertex(-142.5, -142.5, 1000.0)) points_fixes.append(Part.Vertex(-142.5, 95.0, 1000.0)) points_fixes.append(Part.Vertex(-142.5, 47.5, 1000.0)) points_fixes.append(Part.Vertex(-142.5, 0.0, 1000.0)) points_fixes.append(Part.Vertex(-142.5, -47.5, 1000.0)) points_fixes.append(Part.Vertex(-142.5, -95.0, 1000.0)) points_fixes.append(Part.Vertex(142.5, -142.5, 1000.0)) points_fixes.append(Part.Vertex(-95.0, -142.5, 1000.0)) points_fixes.append(Part.Vertex(-47.5, -142.5, 1000.0)) points_fixes.append(Part.Vertex(0.0, -142.5, 1000.0)) points_fixes.append(Part.Vertex(47.5, -142.5, 1000.0)) points_fixes.append(Part.Vertex(95.0, -142.5, 1000.0)) points_fixes.append(Part.Vertex(142.5, 142.5, 1000.0)) points_fixes.append(Part.Vertex(142.5, -95.0, 1000.0)) points_fixes.append(Part.Vertex(142.5, -47.5, 1000.0)) points_fixes.append(Part.Vertex(142.5, 0.0, 1000.0)) points_fixes.append(Part.Vertex(142.5, 47.5, 1000.0)) points_fixes.append(Part.Vertex(142.5, 95.0, 1000.0)) points_fixes.append(Part.Vertex(95.0, 142.5, 1000.0)) points_fixes.append(Part.Vertex(47.5, 142.5, 1000.0)) points_fixes.append(Part.Vertex(0.0, 142.5, 1000.0)) points_fixes.append(Part.Vertex(-47.5, 142.5, 1000.0)) points_fixes.append(Part.Vertex(-95.0, 142.5, 1000.0)) points_fixes.append(Part.Vertex(-142.5, 118.75, 1000.0)) points_fixes.append(Part.Vertex(-142.5, -118.75, 1000.0)) points_fixes.append(Part.Vertex(-142.5, 71.25, 1000.0)) points_fixes.append(Part.Vertex(-142.5, 23.75, 1000.0)) points_fixes.append(Part.Vertex(-142.5, -23.75, 1000.0)) points_fixes.append(Part.Vertex(-142.5, -71.25, 1000.0)) points_fixes.append(Part.Vertex(118.75, -142.5, 1000.0)) points_fixes.append(Part.Vertex(-71.25, -142.5, 1000.0)) points_fixes.append(Part.Vertex(-118.75, -142.5, 1000.0)) points_fixes.append(Part.Vertex(-23.75, -142.5, 1000.0)) points_fixes.append(Part.Vertex(23.75, -142.5, 1000.0)) points_fixes.append(Part.Vertex(71.25, -142.5, 1000.0)) points_fixes.append(Part.Vertex(142.5, 118.75, 1000.0)) points_fixes.append(Part.Vertex(142.5, -71.25, 1000.0)) points_fixes.append(Part.Vertex(142.5, -118.75, 1000.0)) points_fixes.append(Part.Vertex(142.5, -23.75, 1000.0)) points_fixes.append(Part.Vertex(142.5, 23.75, 1000.0)) points_fixes.append(Part.Vertex(142.5, 71.25, 1000.0)) points_fixes.append(Part.Vertex(71.25, 142.5, 1000.0)) points_fixes.append(Part.Vertex(118.75, 142.5, 1000.0)) points_fixes.append(Part.Vertex(23.75, 142.5, 1000.0)) points_fixes.append(Part.Vertex(-23.75, 142.5, 1000.0)) points_fixes.append(Part.Vertex(-71.25, 142.5, 1000.0)) points_fixes.append(Part.Vertex(-118.75, 142.5, 1000.0)) forces_obj = doc.addObject('Part::Feature', 'Forces') forces_obj.Shape = Part.makeCompound(points_forces) fixes_obj = doc.addObject('Part::Feature', 'Fixes') fixes_obj.Shape = Part.makeCompound(points_fixes) doc.recompute() if FreeCAD.GuiUp: forces_obj.ViewObject.PointColor = (1.0, 0.0, 0.0, 0.0) forces_obj.ViewObject.PointSize = 10.0 fixes_obj.ViewObject.PointColor = (1.0, 0.0, 0.0, 0.0) fixes_obj.ViewObject.PointSize = 10.0 geom_obj.ViewObject.Document.activeView().viewAxonometric() geom_obj.ViewObject.Document.activeView().fitAll() # analysis analysis = ObjectsFem.makeAnalysis(doc, "Analysis") # solver if solvertype == "calculix": solver_object = analysis.addObject( ObjectsFem.makeSolverCalculix(doc, "SolverCalculiX") )[0] elif solvertype == "ccxtools": solver_object = analysis.addObject( ObjectsFem.makeSolverCalculixCcxTools(doc, "CalculiXccxTools") )[0] solver_object.WorkingDir = u"" else: FreeCAD.Console.PrintWarning( "Not known or not supported solver type: {}. " "No solver object was created.\n".format(solvertype) ) if solvertype == "calculix" or solvertype == "ccxtools": solver_object.SplitInputWriter = False solver_object.AnalysisType = "static" solver_object.GeometricalNonlinearity = "linear" solver_object.ThermoMechSteadyState = False solver_object.MatrixSolverType = "default" solver_object.IterationsControlParameterTimeUse = False # shell thickness thickness = analysis.addObject( ObjectsFem.makeElementGeometry2D(doc, 0, "ShellThickness") )[0] thickness.Thickness = 15.0 # material material_object = analysis.addObject( ObjectsFem.makeMaterialSolid(doc, "FemMaterial") )[0] mat = material_object.Material mat["Name"] = "Steel-Generic" mat["YoungsModulus"] = "200000 MPa" mat["PoissonRatio"] = "0.30" mat["Density"] = "7900 kg/m^3" material_object.Material = mat # fixed_constraint fixed_constraint = analysis.addObject( ObjectsFem.makeConstraintFixed(doc, name="ConstraintFixed"))[0] fixed_constraint.References = [ (doc.Fixes, 'Vertex6'), (doc.Fixes, 'Vertex15'), (doc.Fixes, 'Vertex5'), (doc.Fixes, 'Vertex29'), (doc.Fixes, 'Vertex42'), (doc.Fixes, 'Vertex30'), (doc.Fixes, 'Vertex9'), (doc.Fixes, 'Vertex31'), (doc.Fixes, 'Vertex33'), (doc.Fixes, 'Vertex32'), (doc.Fixes, 'Vertex3'), (doc.Fixes, 'Vertex34'), (doc.Fixes, 'Vertex46'), (doc.Fixes, 'Vertex1'), (doc.Fixes, 'Vertex36'), (doc.Fixes, 'Vertex11'), (doc.Fixes, 'Vertex38'), (doc.Fixes, 'Vertex12'), (doc.Fixes, 'Vertex39'), (doc.Fixes, 'Vertex13'), (doc.Fixes, 'Vertex40'), (doc.Fixes, 'Vertex16'), (doc.Fixes, 'Vertex35'), (doc.Fixes, 'Vertex14'), (doc.Fixes, 'Vertex47'), (doc.Fixes, 'Vertex20'), (doc.Fixes, 'Vertex37'), (doc.Fixes, 'Vertex18'), (doc.Fixes, 'Vertex41'), (doc.Fixes, 'Vertex17'), (doc.Fixes, 'Vertex10'), (doc.Fixes, 'Vertex26'), (doc.Fixes, 'Vertex43'), (doc.Fixes, 'Vertex21'), (doc.Fixes, 'Vertex44'), (doc.Fixes, 'Vertex19'), (doc.Fixes, 'Vertex4'), (doc.Fixes, 'Vertex28'), (doc.Fixes, 'Vertex48'), (doc.Fixes, 'Vertex22'), (doc.Fixes, 'Vertex8'), (doc.Fixes, 'Vertex23'), (doc.Fixes, 'Vertex7'), (doc.Fixes, 'Vertex24'), (doc.Fixes, 'Vertex45'), (doc.Fixes, 'Vertex27'), (doc.Fixes, 'Vertex2'), (doc.Fixes, 'Vertex25')] # force_constraint1 force_constraint1 = analysis.addObject( ObjectsFem.makeConstraintForce(doc, name="ConstraintForce1"))[0] force_constraint1.References = [(forces_obj, 'Vertex1'), (forces_obj, 'Vertex14')] force_constraint1.Force = 5555.56 force_constraint1.Direction = (doc.SquareTube, ["Edge9"]) force_constraint1.Reversed = False # force_constraint2 force_constraint2 = analysis.addObject( ObjectsFem.makeConstraintForce(doc, name="ConstraintForce2"))[0] force_constraint2.References = [(forces_obj, 'Vertex2'), (forces_obj, 'Vertex8')] force_constraint2.Force = 5555.56 force_constraint2.Direction = (doc.SquareTube, ["Edge3"]) force_constraint2.Reversed = False # force_constraint3 force_constraint3 = analysis.addObject( ObjectsFem.makeConstraintForce(doc, name="ConstraintForce3"))[0] force_constraint3.References = [ (forces_obj, 'Vertex20'), (forces_obj, 'Vertex21'), (forces_obj, 'Vertex22'), (forces_obj, 'Vertex23'), (forces_obj, 'Vertex24'), ] force_constraint3.Force = 27777.78 force_constraint3.Direction = (doc.SquareTube, ["Edge9"]) force_constraint3.Reversed = False # force_constraint4 force_constraint4 = analysis.addObject( ObjectsFem.makeConstraintForce(doc, name="ConstraintForce4"))[0] force_constraint4.References = [ (forces_obj, 'Vertex9'), (forces_obj, 'Vertex10'), (forces_obj, 'Vertex11'), (forces_obj, 'Vertex12'), (forces_obj, 'Vertex13'), ] force_constraint4.Force = 27777.78 force_constraint4.Direction = (doc.SquareTube, ["Edge3"]) force_constraint4.Reversed = False # force_constraint5 force_constraint5 = analysis.addObject( ObjectsFem.makeConstraintForce(doc, name="ConstraintForce5"))[0] force_constraint5.References = [ (forces_obj, 'Vertex43'), (forces_obj, 'Vertex44'), (forces_obj, 'Vertex45'), (forces_obj, 'Vertex46'), (forces_obj, 'Vertex47'), (forces_obj, 'Vertex48'), ] force_constraint5.Force = 66666.67 force_constraint5.Direction = (doc.SquareTube, ["Edge9"]) force_constraint5.Reversed = False # force_constraint6 force_constraint6 = analysis.addObject( ObjectsFem.makeConstraintForce(doc, name="ConstraintForce6"))[0] force_constraint6.References = [ (forces_obj, 'Vertex31'), (forces_obj, 'Vertex32'), (forces_obj, 'Vertex33'), (forces_obj, 'Vertex34'), (forces_obj, 'Vertex35'), (forces_obj, 'Vertex36'), ] force_constraint6.Force = 66666.67 force_constraint6.Direction = (doc.SquareTube, ["Edge3"]) force_constraint6.Reversed = False # force_constraint7 force_constraint7 = analysis.addObject( ObjectsFem.makeConstraintForce(doc, name="ConstraintForce7"))[0] force_constraint7.References = [(forces_obj, 'Vertex1'), (forces_obj, 'Vertex2')] force_constraint7.Force = 5555.56 force_constraint7.Direction = (doc.SquareTube, ["Edge11"]) force_constraint7.Reversed = False # force_constraint8 force_constraint8 = analysis.addObject( ObjectsFem.makeConstraintForce(doc, name="ConstraintForce8"))[0] force_constraint8.References = [(forces_obj, 'Vertex8'), (forces_obj, 'Vertex14')] force_constraint8.Force = 5555.56 force_constraint8.Direction = (doc.SquareTube, ["Edge6"]) force_constraint8.Reversed = False # force_constraint9 force_constraint9 = analysis.addObject( ObjectsFem.makeConstraintForce(doc, name="ConstraintForce9"))[0] force_constraint9.References = [ (forces_obj, 'Vertex3'), (forces_obj, 'Vertex4'), (forces_obj, 'Vertex5'), (forces_obj, 'Vertex6'), (forces_obj, 'Vertex7'), ] force_constraint9.Force = 27777.78 force_constraint9.Direction = (doc.SquareTube, ["Edge11"]) force_constraint9.Reversed = False # force_constraint10 force_constraint10 = analysis.addObject( ObjectsFem.makeConstraintForce(doc, name="ConstraintForce10"))[0] force_constraint10.References = [ (forces_obj, 'Vertex15'), (forces_obj, 'Vertex16'), (forces_obj, 'Vertex17'), (forces_obj, 'Vertex18'), (forces_obj, 'Vertex19'), ] force_constraint10.Force = 27777.78 force_constraint10.Direction = (doc.SquareTube, ["Edge6"]) force_constraint10.Reversed = False # force_constraint11 force_constraint11 = analysis.addObject( ObjectsFem.makeConstraintForce(doc, name="ConstraintForce11"))[0] force_constraint11.References = [ (forces_obj, 'Vertex25'), (forces_obj, 'Vertex26'), (forces_obj, 'Vertex27'), (forces_obj, 'Vertex28'), (forces_obj, 'Vertex29'), (forces_obj, 'Vertex30'), ] force_constraint11.Force = 66666.67 force_constraint11.Direction = (doc.SquareTube, ["Edge11"]) force_constraint11.Reversed = False # force_constraint12 force_constraint12 = analysis.addObject( ObjectsFem.makeConstraintForce(doc, name="ConstraintForce12"))[0] force_constraint12.References = [ (forces_obj, 'Vertex37'), (forces_obj, 'Vertex38'), (forces_obj, 'Vertex39'), (forces_obj, 'Vertex40'), (forces_obj, 'Vertex41'), (forces_obj, 'Vertex42'), ] force_constraint12.Force = 66666.67 force_constraint12.Direction = (doc.SquareTube, ["Edge6"]) force_constraint12.Reversed = False # mesh from .meshes.mesh_square_pipe_end_twisted_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( ObjectsFem.makeMeshGmsh(doc, mesh_name) )[0] femmesh_obj.FemMesh = fem_mesh femmesh_obj.Part = geom_obj femmesh_obj.SecondOrderLinear = False doc.recompute() return doc
def offset(obj, delta, copy=False, bind=False, sym=False, occ=False): """offset(object,delta,[copymode],[bind]) Offset the given wire by applying the given delta Vector to its first vertex. Parameters ---------- obj : delta : Base.Vector or list of Base.Vector If offsetting a BSpline, the delta must not be a Vector but a list of Vectors, one for each node of the spline. copy : bool If copymode is True, another object is created, otherwise the same object gets offsetted. copy : bool If bind is True, and provided the wire is open, the original and the offset wires will be bound by their endpoints, forming a face. sym : bool if sym is True, bind must be true too, and the offset is made on both sides, the total width being the given delta length. """ import Part import DraftGeomUtils newwire = None delete = None if utils.get_type(obj) in ["Sketch", "Part"]: copy = True print( "the offset tool is currently unable to offset a non-Draft object directly - Creating a copy" ) def getRect(p, obj): """returns length,height,placement""" pl = obj.Placement.copy() pl.Base = p[0] diag = p[2].sub(p[0]) bb = p[1].sub(p[0]) bh = p[3].sub(p[0]) nb = DraftVecUtils.project(diag, bb) nh = DraftVecUtils.project(diag, bh) if obj.Length.Value < 0: l = -nb.Length else: l = nb.Length if obj.Height.Value < 0: h = -nh.Length else: h = nh.Length return l, h, pl def getRadius(obj, delta): """returns a new radius for a regular polygon""" an = math.pi / obj.FacesNumber nr = DraftVecUtils.rotate(delta, -an) nr.multiply(1 / math.cos(an)) nr = obj.Shape.Vertexes[0].Point.add(nr) nr = nr.sub(obj.Placement.Base) nr = nr.Length if obj.DrawMode == "inscribed": return nr else: return nr * math.cos(math.pi / obj.FacesNumber) newwire = None if utils.get_type(obj) == "Circle": pass elif utils.get_type(obj) == "BSpline": pass else: if sym: d1 = App.Vector(delta).multiply(0.5) d2 = d1.negative() n1 = DraftGeomUtils.offsetWire(obj.Shape, d1) n2 = DraftGeomUtils.offsetWire(obj.Shape, d2) else: if isinstance(delta, float) and (len(obj.Shape.Edges) == 1): # circle c = obj.Shape.Edges[0].Curve nc = Part.Circle(c.Center, c.Axis, delta) if len(obj.Shape.Vertexes) > 1: nc = Part.ArcOfCircle(nc, obj.Shape.Edges[0].FirstParameter, obj.Shape.Edges[0].LastParameter) newwire = Part.Wire(nc.toShape()) p = [] else: newwire = DraftGeomUtils.offsetWire(obj.Shape, delta) if DraftGeomUtils.hasCurves(newwire) and copy: p = [] else: p = DraftGeomUtils.getVerts(newwire) if occ: newobj = App.ActiveDocument.addObject("Part::Feature", "Offset") newobj.Shape = DraftGeomUtils.offsetWire(obj.Shape, delta, occ=True) gui_utils.formatObject(newobj, obj) if not copy: delete = obj.Name elif bind: if not DraftGeomUtils.isReallyClosed(obj.Shape): if sym: s1 = n1 s2 = n2 else: s1 = obj.Shape s2 = newwire if s1 and s2: w1 = s1.Edges w2 = s2.Edges w3 = Part.LineSegment(s1.Vertexes[0].Point, s2.Vertexes[0].Point).toShape() w4 = Part.LineSegment(s1.Vertexes[-1].Point, s2.Vertexes[-1].Point).toShape() newobj = App.ActiveDocument.addObject("Part::Feature", "Offset") newobj.Shape = Part.Face(Part.Wire(w1 + [w3] + w2 + [w4])) else: print("Draft.offset: Unable to bind wires") else: newobj = App.ActiveDocument.addObject("Part::Feature", "Offset") newobj.Shape = Part.Face(obj.Shape.Wires[0]) if not copy: delete = obj.Name elif copy: newobj = None if sym: return None if utils.get_type(obj) == "Wire": if p: newobj = makeWire(p) newobj.Closed = obj.Closed elif newwire: newobj = App.ActiveDocument.addObject("Part::Feature", "Offset") newobj.Shape = newwire else: print("Draft.offset: Unable to duplicate this object") elif utils.get_type(obj) == "Rectangle": if p: length, height, plac = getRect(p, obj) newobj = makeRectangle(length, height, plac) elif newwire: newobj = App.ActiveDocument.addObject("Part::Feature", "Offset") newobj.Shape = newwire else: print("Draft.offset: Unable to duplicate this object") elif utils.get_type(obj) == "Circle": pl = obj.Placement newobj = makeCircle(delta) newobj.FirstAngle = obj.FirstAngle newobj.LastAngle = obj.LastAngle newobj.Placement = pl elif utils.get_type(obj) == "Polygon": pl = obj.Placement newobj = makePolygon(obj.FacesNumber) newobj.Radius = getRadius(obj, delta) newobj.DrawMode = obj.DrawMode newobj.Placement = pl elif utils.get_type(obj) == "BSpline": newobj = makeBSpline(delta) newobj.Closed = obj.Closed else: # try to offset anyway try: if p: newobj = makeWire(p) newobj.Closed = obj.Shape.isClosed() except Part.OCCError: pass if not (newobj) and newwire: newobj = App.ActiveDocument.addObject("Part::Feature", "Offset") newobj.Shape = newwire else: print("Draft.offset: Unable to create an offset") if newobj: gui_utils.formatObject(newobj, obj) else: newobj = None if sym: return None if utils.get_type(obj) == "Wire": if obj.Base or obj.Tool: App.Console.PrintWarning("Warning: object history removed\n") obj.Base = None obj.Tool = None obj.Points = p elif utils.get_type(obj) == "BSpline": #print(delta) obj.Points = delta #print("done") elif utils.get_type(obj) == "Rectangle": length, height, plac = getRect(p, obj) obj.Placement = plac obj.Length = length obj.Height = height elif utils.get_type(obj) == "Circle": obj.Radius = delta elif utils.get_type(obj) == "Polygon": obj.Radius = getRadius(obj, delta) elif utils.get_type(obj) == 'Part': print("unsupported object") # TODO newobj = obj if copy and utils.get_param("selectBaseObjects", False): gui_utils.select(newobj) else: gui_utils.select(obj) if delete: App.ActiveDocument.removeObject(delete) return newobj
def execute(self, obj): if self.clone(obj): return if len(obj.InList) != 1: return if Draft.getType(obj.InList[0]) != "Structure": return if not obj.InList[0].Shape: return if not obj.Base: return if not obj.Base.Shape: return if not obj.Base.Shape.Wires: return if not obj.Diameter.Value: return if not obj.Amount: return father = obj.InList[0] wire = obj.Base.Shape.Wires[0] if hasattr(obj, "Rounding"): #print(obj.Rounding) if obj.Rounding: radius = obj.Rounding * obj.Diameter.Value import DraftGeomUtils wire = DraftGeomUtils.filletWire(wire, radius) bpoint, bvec = self.getBaseAndAxis(wire) if not bpoint: return axis = obj.Base.Placement.Rotation.multVec(FreeCAD.Vector(0, 0, -1)) size = (ArchCommands.projectToVector(father.Shape.copy(), axis)).Length if hasattr(obj, "Direction"): if not DraftVecUtils.isNull(obj.Direction): axis = FreeCAD.Vector(obj.Direction) #.normalize() # don't normalize so the vector can also be used to determine the distance size = axis.Length #print(axis) #print(size) if (obj.OffsetStart.Value + obj.OffsetEnd.Value) > size: return # all tests ok! pl = obj.Placement import Part circle = Part.makeCircle(obj.Diameter.Value / 2, bpoint, bvec) circle = Part.Wire(circle) try: bar = wire.makePipeShell([circle], True, False, 2) except Part.OCCError: print("Arch: error sweeping rebar profile along the base sketch") return # building final shape shapes = [] if obj.Amount == 1: offset = DraftVecUtils.scaleTo(axis, size / 2) bar.translate(offset) shapes.append(bar) if hasattr(obj, "Spacing"): obj.Spacing = 0 else: if obj.OffsetStart.Value: baseoffset = DraftVecUtils.scaleTo(axis, obj.OffsetStart.Value) else: baseoffset = None interval = size - (obj.OffsetStart.Value + obj.OffsetEnd.Value) interval = interval / (obj.Amount - 1) vinterval = DraftVecUtils.scaleTo(axis, interval) for i in range(obj.Amount): if i == 0: if baseoffset: bar.translate(baseoffset) shapes.append(bar) else: bar = bar.copy() bar.translate(vinterval) shapes.append(bar) if hasattr(obj, "Spacing"): obj.Spacing = interval if shapes: obj.Shape = Part.makeCompound(shapes) obj.Placement = pl
def compute(): QtGui.qApp.setOverrideCursor(QtCore.Qt.WaitCursor) if FreeCAD.ActiveDocument == None: FreeCAD.newDocument("Gear") oldDocumentObjects = App.ActiveDocument.Objects try: N = int(l1.text()) p = float(l2.text()) alfa = int(l3.text()) y = float( l4.text()) #standard value y<1 for gear drives y>1 for Gear pumps m = p / math.pi #standard value 0.06, 0.12, 0.25, 0.5, 1, 2, 4, 8, 16, 32, 60 (polish norm) c = float(l5.text()) * m #standard value 0,1*m - 0,3*m j = float(l6.text()) * m #standard value 0,015 - 0,04*m width = float(l7.text()) #gear width except ValueError: FreeCAD.Console.PrintError("Wrong input! Only numbers allowed...\n") #tooth height h = 2 * y * m + c #pitch diameter d = N * m #root diameter df = d - 2 * y * m - 2 * c #df=d-2hf where and hf=y*m+c #addendum diameter da = d + 2 * y * m #da=d+2ha where ha=y*m #base diameter for involute db = d * math.cos(math.radians(alfa)) #Base circle baseCircle = FreeCAD.ActiveDocument.addObject("Part::FeaturePython", "BaseCircle") Draft._Circle(baseCircle) Draft._ViewProviderDraft(baseCircle.ViewObject) baseCircle.Radius = db / 2 baseCircle.FirstAngle = 0.0 baseCircle.LastAngle = 0.0 #Root circle rootCircle = FreeCAD.ActiveDocument.addObject("Part::FeaturePython", "RootCircle") Draft._Circle(rootCircle) Draft._ViewProviderDraft(rootCircle.ViewObject) rootCircle.Radius = df / 2 rootCircle.FirstAngle = 0.0 rootCircle.LastAngle = 0.0 #Addendum circle addendumCircle = FreeCAD.ActiveDocument.addObject("Part::FeaturePython", "AddendumCircle") Draft._Circle(addendumCircle) Draft._ViewProviderDraft(addendumCircle.ViewObject) addendumCircle.Radius = da / 2 addendumCircle.FirstAngle = 0.0 addendumCircle.LastAngle = 0.0 #Pitch circle pitchCircle = FreeCAD.ActiveDocument.addObject("Part::FeaturePython", "PitchCircle") Draft._Circle(pitchCircle) Draft._ViewProviderDraft(pitchCircle.ViewObject) pitchCircle.Radius = d / 2 pitchCircle.FirstAngle = 0.0 pitchCircle.LastAngle = 0.0 #************ Calculating right sides of teeth #Involute of base circle involute = [] involutee = [] involutesav = [] for t in range(0, 60, 1): x = db / 2 * (math.cos(math.radians(t)) + math.radians(t) * math.sin(math.radians(t))) y = db / 2 * (math.sin(math.radians(t)) - math.radians(t) * math.cos(math.radians(t))) involute.append(Part.Vertex(x, y, 0).Point) #************ Drawing rigth sides of teeth involutesav.extend(involute) involutee.extend(involute) for angle in range(1, N + 1, 1): involuteobj = FreeCAD.ActiveDocument.addObject( "Part::Feature", "InvoluteL" + str(angle)) involutee.insert(0, (0, 0, 0)) involuteshape = Part.makePolygon(involutee) involuteobj.Shape = involuteshape involutee = [] for num in range(0, 60, 1): point = involute.pop() pointt = Part.Vertex( point.x * math.cos(math.radians(angle * 360 / N)) - point.y * math.sin(math.radians(angle * 360 / N)), point.x * math.sin(math.radians(angle * 360 / N)) + point.y * math.cos(math.radians(angle * 360 / N)), 0).Point involutee.insert(0, pointt) involute.extend(involutesav) involutee = [] #************ Calculating difference between tooth spacing on BaseCircle and PitchCircle pc = App.ActiveDocument.getObject("PitchCircle") inv = App.ActiveDocument.getObject("InvoluteL1") cut = inv.Shape.cut(pc.Shape) # FreeCAD.ActiveDocument.addObject("Part::Feature","CutInv").Shape=cut invPoint = cut.Vertexes[0].Point diff = invPoint.y * 2 # instead of making axial symmetry and calculating point distance. anglediff = 2 * math.asin(diff / d) #************ Calculating left sides of teeth #************ Inversing Involute for num in range(0, 60, 1): point = involute.pop() pointt = Part.Vertex(point.x, point.y * -1, 0).Point involutee.insert(0, pointt) involute.extend(involutee) involutee = [] #Normal tooth size calculated as: 0,5* p - j j=m * 0,1 below are calculations # 0,5* p - m * 0,1 # 0,5* p - p /pi * 0,1 # 0,5*360/N - ((360/N)/pi)* 0,1 # 0,5*360/N - (360/N)*((1/pi)*0,1) j=(p/pi)*0,1 # 0,5*360/N - (360/N)*((p/pi)*0,1)/p # 0,5*360/N - (360/N)*( j )/p for num in range(0, 60, 1): point = involute.pop() pointt = Part.Vertex( point.x * math.cos(math.radians(180 / N - (360 / N) * (j / p)) + anglediff) - point.y * math.sin(math.radians(180 / N - (360 / N) * (j / p)) + anglediff), point.x * math.sin(math.radians(180 / N - (360 / N) * (j / p)) + anglediff) + point.y * math.cos(math.radians(180 / N - (360 / N) * (j / p)) + anglediff), 0).Point involutee.insert(0, pointt) involute.extend(involutee) involutesav = [] involutesav.extend(involute) #************ Drawing left sides of teeth for angle in range(1, N + 1, 1): involuteobj = FreeCAD.ActiveDocument.addObject( "Part::Feature", "InvoluteR" + str(angle)) involutee.insert(0, (0, 0, 0)) involuteshape = Part.makePolygon(involutee) involuteobj.Shape = involuteshape involutee = [] for num in range(0, 60, 1): point = involute.pop() pointt = Part.Vertex( point.x * math.cos(math.radians(angle * 360 / N)) - point.y * math.sin(math.radians(angle * 360 / N)), point.x * math.sin(math.radians(angle * 360 / N)) + point.y * math.cos(math.radians(angle * 360 / N)), 0).Point involutee.insert(0, pointt) involute.extend(involutesav) Gui.SendMsgToActiveView("ViewFit") #************ Forming teeth cutCircle = FreeCAD.ActiveDocument.addObject("Part::FeaturePython", "CutCircle") Draft._Circle(cutCircle) Draft._ViewProviderDraft(cutCircle.ViewObject) cutCircle.Radius = da # da because must be bigger than addendumCircle and bigger than whole construction da is right for this but it not has to be. cutCircle.FirstAngle = 0.0 cutCircle.LastAngle = 0.0 cutTool = cutCircle.Shape.cut(addendumCircle.Shape) #cutshape=Part.show(cutTool) gearShape = rootCircle.Shape for invNum in range(1, N + 1, 1): invL = App.ActiveDocument.getObject("InvoluteL" + str(invNum)) invR = App.ActiveDocument.getObject("InvoluteR" + str(invNum)) cutL = invL.Shape.cut(cutTool) cutR = invR.Shape.cut(cutTool) pointL = cutL.Vertexes.pop().Point pointR = cutR.Vertexes.pop().Point faceEdge = Part.makeLine(pointL, pointR) toothWhole = cutL.fuse(cutR) toothWhole = toothWhole.fuse(faceEdge) toothWire = Part.Wire(toothWhole.Edges) toothShape = Part.Face(toothWire) # tooth=App.ActiveDocument.addObject("Part::Feature","Tooth"+str(invNum)) # tooth.Shape=toothShape gearShape = gearShape.fuse(toothShape) for o in App.ActiveDocument.Objects: if oldDocumentObjects.count(o) == 0: App.ActiveDocument.removeObject(o.Name) gearFlat = App.ActiveDocument.addObject("Part::Feature", "GearFlat") gearFlat.Shape = gearShape Gui.ActiveDocument.getObject(gearFlat.Name).Visibility = False gear = App.ActiveDocument.addObject("Part::Extrusion", "Gear3D") gear.Base = gearFlat gear.Dir = (0, 0, width) App.ActiveDocument.recompute() if c1.isChecked() == True: gearMesh = App.ActiveDocument.addObject("Mesh::Feature", "Gear3D-mesh") faces = [] triangles = gear.Shape.tessellate( 1) # the number represents the precision of the tessellation) for tri in triangles[1]: face = [] for i in range(3): vindex = tri[i] face.append(triangles[0][vindex]) faces.append(face) mesh = Mesh.Mesh(faces) gearMesh.Mesh = mesh App.ActiveDocument.removeObject(gear.Name) App.ActiveDocument.removeObject(gearFlat.Name) App.ActiveDocument.recompute() Gui.SendMsgToActiveView("ViewFit") QtGui.qApp.restoreOverrideCursor() hide()
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.LineSegment(V1, V2) # and the second one L2 = Part.LineSegment(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 execute(self, obj): if self.clone(obj): return if not obj.Base: FreeCAD.Console.PrintError( "No Base, return without a rebar shape for {}.\n".format( obj.Name)) return if not obj.Base.Shape: FreeCAD.Console.PrintError( "No Shape in Base, return without a rebar shape for {}.\n". format(obj.Name)) return if obj.Base.Shape.Faces: FreeCAD.Console.PrintError( "Faces in Shape of Base, return without a rebar shape for {}.\n" .format(obj.Name)) return if not obj.Base.Shape.Edges: FreeCAD.Console.PrintError( "No Edges in Shape of Base, return without a rebar shape for {}.\n" .format(obj.Name)) return if not obj.Diameter.Value: FreeCAD.Console.PrintError( "No Diameter Value, return without a rebar shape for {}.\n". format(obj.Name)) return if not obj.Amount: FreeCAD.Console.PrintError( "No Amount, return without a rebar shape for {}.\n".format( obj.Name)) return father = obj.Host fathershape = None if not father: # support for old-style rebars if obj.InList: if hasattr(obj.InList[0], "Armatures"): if obj in obj.InList[0].Armatures: father = obj.InList[0] if father: if hasattr(father, 'Shape'): fathershape = father.Shape import Part # corner cases: # compound from more Wires # compound without Wires but with multiple Edges # Does they make sense? If yes handle them. # Does it makes sense to handle Shapes with Faces or even Solids? if not obj.Base.Shape.Wires and len(obj.Base.Shape.Edges) == 1: wire = Part.Wire(obj.Base.Shape.Edges[0]) else: wire = obj.Base.Shape.Wires[0] if hasattr(obj, "Rounding"): #print(obj.Rounding) if obj.Rounding: radius = obj.Rounding * obj.Diameter.Value from DraftGeomUtils import filletWire wire = filletWire(wire, radius) bpoint, bvec = self.getBaseAndAxis(wire) if not bpoint: return axis = obj.Base.Placement.Rotation.multVec(FreeCAD.Vector(0, 0, -1)) if fathershape: size = (ArchCommands.projectToVector(fathershape.copy(), axis)).Length else: size = 1 if hasattr(obj, "Direction"): if not DraftVecUtils.isNull(obj.Direction): axis = FreeCAD.Vector(obj.Direction) axis.normalize() if fathershape: size = (ArchCommands.projectToVector( fathershape.copy(), axis)).Length else: size = 1 if hasattr(obj, "Distance"): if obj.Distance.Value: size = obj.Distance.Value spacinglist = None if hasattr(obj, "CustomSpacing"): if obj.CustomSpacing: spacinglist = strprocessOfCustomSpacing(obj.CustomSpacing) influenceArea = sum( spacinglist) - spacinglist[0] / 2 - spacinglist[-1] / 2 # Drop this check to solve issue as discussed here: https://github.com/FreeCAD/FreeCAD/pull/2550 # if (obj.OffsetStart.Value + obj.OffsetEnd.Value) > size: # return # all tests ok! if hasattr(obj, "Length"): length = getLengthOfRebar(obj) if length: obj.Length = length pl = obj.Placement circle = Part.makeCircle(obj.Diameter.Value / 2, bpoint, bvec) circle = Part.Wire(circle) try: bar = wire.makePipeShell([circle], True, False, 2) basewire = wire.copy() except Part.OCCError: print("Arch: error sweeping rebar profile along the base sketch") return # building final shape shapes = [] placementlist = [] self.wires = [] rot = FreeCAD.Rotation() if obj.Amount == 1: if hasattr(obj, "RebarShape"): barplacement = CalculatePlacement( obj.Amount, 1, obj.Diameter.Value, size, axis, rot, obj.OffsetStart.Value, obj.OffsetEnd.Value, obj.RebarShape) else: barplacement = CalculatePlacement(obj.Amount, 1, obj.Diameter.Value, size, axis, rot, obj.OffsetStart.Value, obj.OffsetEnd.Value) placementlist.append(barplacement) if hasattr(obj, "Spacing"): obj.Spacing = 0 else: if obj.OffsetStart.Value: baseoffset = DraftVecUtils.scaleTo(axis, obj.OffsetStart.Value) else: baseoffset = None if hasattr(obj, "RebarShape") and obj.RebarShape == "Stirrup": interval = size - (obj.OffsetStart.Value + obj.OffsetEnd.Value + obj.Diameter.Value) else: interval = size - (obj.OffsetStart.Value + obj.OffsetEnd.Value) interval = interval / (obj.Amount - 1) for i in range(obj.Amount): if hasattr(obj, "RebarShape"): barplacement = CalculatePlacement(obj.Amount, i + 1, obj.Diameter.Value, size, axis, rot, obj.OffsetStart.Value, obj.OffsetEnd.Value, obj.RebarShape) else: barplacement = CalculatePlacement(obj.Amount, i + 1, obj.Diameter.Value, size, axis, rot, obj.OffsetStart.Value, obj.OffsetEnd.Value) placementlist.append(barplacement) if hasattr(obj, "Spacing"): obj.Spacing = interval # Calculate placement of bars from custom spacing. if spacinglist: placementlist[:] = [] if hasattr(obj, "RebarShape") and obj.RebarShape == "Stirrup": reqInfluenceArea = size - (obj.OffsetStart.Value + obj.OffsetEnd.Value + obj.Diameter.Value) else: reqInfluenceArea = size - (obj.OffsetStart.Value + obj.OffsetEnd.Value) # Avoid unnecessary checks to pass like. For eg.: when we have values # like influenceArea is 100.00001 and reqInflueneArea is 100 if round(influenceArea) > round(reqInfluenceArea): FreeCAD.Console.PrintWarning( "Influence area of rebars is greater than " + str(reqInfluenceArea) + ".\n") elif round(influenceArea) < round(reqInfluenceArea): FreeCAD.Console.PrintWarning( "Last span is greater that end offset.\n") for i in range(len(spacinglist)): if i == 0: barplacement = CustomSpacingPlacement( spacinglist, 1, axis, father.Placement.Rotation, obj.OffsetStart.Value, obj.OffsetEnd.Value) placementlist.append(barplacement) else: barplacement = CustomSpacingPlacement( spacinglist, i + 1, axis, father.Placement.Rotation, obj.OffsetStart.Value, obj.OffsetEnd.Value) placementlist.append(barplacement) obj.Amount = len(spacinglist) obj.Spacing = 0 obj.PlacementList = placementlist for i in range(len(obj.PlacementList)): if i == 0: bar.Placement = obj.PlacementList[i] shapes.append(bar) basewire.Placement = obj.PlacementList[i] self.wires.append(basewire) else: bar = bar.copy() bar.Placement = obj.PlacementList[i] shapes.append(bar) w = basewire.copy() w.Placement = obj.PlacementList[i] self.wires.append(w) if shapes: obj.Shape = Part.makeCompound(shapes) obj.Placement = pl obj.TotalLength = obj.Length * len(obj.PlacementList)