def projectPointOld(self, p, direction=None): """Project a point onto the plane. OBSOLETE. Parameters ---------- p : Base::Vector3 The point to project. direction : Base::Vector3, optional The unit vector that indicates the direction of projection. It defaults to `None`, which then uses the `plane.axis` (normal) value, meaning that the point is projected perpendicularly to the plane. Returns ------- Base::Vector3 The projected point, or the original point if the angle between the `direction` and the `plane.axis` is 90 degrees. """ if not direction: direction = self.axis t = Vector(direction) # t.normalize() a = round(t.getAngle(self.axis), DraftVecUtils.precision()) pp = round((math.pi)/2, DraftVecUtils.precision()) if a == pp: return p t.multiply(self.offsetToPoint(p, direction)) return p.add(t)
def alignToPointAndAxis_SVG(self, point, axis, offset): # based on cases table self.doc = FreeCAD.ActiveDocument self.axis = axis self.axis.normalize() ref_vec = Vector(0.0, 1.0, 0.0) if ((abs(axis.x) > abs(axis.y)) and (abs(axis.y) > abs(axis.z))): ref_vec = Vector(0.0, 0., 1.0) self.u = axis.negative().cross(ref_vec) self.u.normalize() self.v = DraftVecUtils.rotate(self.u, math.pi / 2, self.axis) #projcase = "Case new" elif ((abs(axis.y) > abs(axis.z)) and (abs(axis.z) >= abs(axis.x))): ref_vec = Vector(1.0, 0.0, 0.0) self.u = axis.negative().cross(ref_vec) self.u.normalize() self.v = DraftVecUtils.rotate(self.u, math.pi / 2, self.axis) #projcase = "Y>Z, View Y" elif ((abs(axis.y) >= abs(axis.x)) and (abs(axis.x) > abs(axis.z))): ref_vec = Vector(0.0, 0., 1.0) self.u = axis.cross(ref_vec) self.u.normalize() self.v = DraftVecUtils.rotate(self.u, math.pi / 2, self.axis) #projcase = "ehem. XY, Case XY" elif ((abs(axis.x) > abs(axis.z)) and (abs(axis.z) >= abs(axis.y))): self.u = axis.cross(ref_vec) self.u.normalize() self.v = DraftVecUtils.rotate(self.u, math.pi / 2, self.axis) #projcase = "X>Z, View X" elif ((abs(axis.z) >= abs(axis.y)) and (abs(axis.y) > abs(axis.x))): ref_vec = Vector(1.0, 0., 0.0) self.u = axis.cross(ref_vec) self.u.normalize() self.v = DraftVecUtils.rotate(self.u, math.pi / 2, self.axis) #projcase = "Y>X, Case YZ" else: self.u = axis.negative().cross(ref_vec) self.u.normalize() self.v = DraftVecUtils.rotate(self.u, math.pi / 2, self.axis) #projcase = "else" #spat_vec = self.u.cross(self.v) #spat_res = spat_vec.dot(axis) #FreeCAD.Console.PrintMessage(projcase + " spat Prod = " + str(spat_res) + "\n") offsetVector = Vector(axis) offsetVector.multiply(offset) self.position = point.add(offsetVector) self.weak = False
def alignToPointAndAxis_SVG(self, point, axis, offset): # based on cases table self.doc = FreeCAD.ActiveDocument self.axis = axis; self.axis.normalize() ref_vec = Vector(0.0, 1.0, 0.0) if ((abs(axis.x) > abs(axis.y)) and (abs(axis.y) > abs(axis.z))): ref_vec = Vector(0.0, 0., 1.0) self.u = axis.negative().cross(ref_vec) self.u.normalize() self.v = DraftVecUtils.rotate(self.u, math.pi/2, self.axis) #projcase = "Case new" elif ((abs(axis.y) > abs(axis.z)) and (abs(axis.z) >= abs(axis.x))): ref_vec = Vector(1.0, 0.0, 0.0) self.u = axis.negative().cross(ref_vec) self.u.normalize() self.v = DraftVecUtils.rotate(self.u, math.pi/2, self.axis) #projcase = "Y>Z, View Y" elif ((abs(axis.y) >= abs(axis.x)) and (abs(axis.x) > abs(axis.z))): ref_vec = Vector(0.0, 0., 1.0) self.u = axis.cross(ref_vec) self.u.normalize() self.v = DraftVecUtils.rotate(self.u, math.pi/2, self.axis) #projcase = "ehem. XY, Case XY" elif ((abs(axis.x) > abs(axis.z)) and (abs(axis.z) >= abs(axis.y))): self.u = axis.cross(ref_vec) self.u.normalize() self.v = DraftVecUtils.rotate(self.u, math.pi/2, self.axis) #projcase = "X>Z, View X" elif ((abs(axis.z) >= abs(axis.y)) and (abs(axis.y) > abs(axis.x))): ref_vec = Vector(1.0, 0., 0.0) self.u = axis.cross(ref_vec) self.u.normalize() self.v = DraftVecUtils.rotate(self.u, math.pi/2, self.axis) #projcase = "Y>X, Case YZ" else: self.u = axis.negative().cross(ref_vec) self.u.normalize() self.v = DraftVecUtils.rotate(self.u, math.pi/2, self.axis) #projcase = "else" #spat_vec = self.u.cross(self.v) #spat_res = spat_vec.dot(axis) #FreeCAD.Console.PrintMessage(projcase + " spat Prod = " + str(spat_res) + "\n") offsetVector = Vector(axis); offsetVector.multiply(offset) self.position = point.add(offsetVector) self.weak = False
def projectPointOld(self, p, direction=None): '''project point onto plane, default direction is orthogonal. Obsolete''' if not direction: direction = self.axis t = Vector(direction) #t.normalize() a = round(t.getAngle(self.axis),DraftVecUtils.precision()) pp = round((math.pi)/2,DraftVecUtils.precision()) if a == pp: return p t.multiply(self.offsetToPoint(p, direction)) return p.add(t)
def projectPointOld(self, p, direction=None): '''project point onto plane, default direction is orthogonal. Obsolete''' if not direction: direction = self.axis t = Vector(direction) #t.normalize() a = round(t.getAngle(self.axis), DraftVecUtils.precision()) pp = round((math.pi) / 2, DraftVecUtils.precision()) if a == pp: return p t.multiply(self.offsetToPoint(p, direction)) return p.add(t)
def alignToPointAndAxis(self, point, axis, offset=0, upvec=None): """Align the working plane to a point and an axis (vector). Set `v` as the cross product of `axis` with `(1, 0, 0)` or `+X`, and `u` as `v` rotated -90 degrees around the `axis`. Also set `weak` to `False`. Parameters ---------- point : Base::Vector3 The new `position` of the plane, adjusted by the `offset`. axis : Base::Vector3 A vector whose unit vector will be used as the new `axis` of the plane. If it is very close to the `X` or `-X` axes, it will use this axis exactly, and will adjust `u` and `v` to `+Y` and `+Z`, or `-Y` and `+Z`, respectively. offset : float, optional Defaults to zero. A value which will be used to offset the plane in the direction of its `axis`. upvec : Base::Vector3, optional Defaults to `None`. If it exists, its unit vector will be used as `v`, and will set `u` as the cross product of `v` with `axis`. """ self.doc = FreeCAD.ActiveDocument self.axis = axis self.axis.normalize() if axis.getAngle(Vector(1, 0, 0)) < 0.00001: self.axis = Vector(1, 0, 0) self.u = Vector(0, 1, 0) self.v = Vector(0, 0, 1) elif axis.getAngle(Vector(-1, 0, 0)) < 0.00001: self.axis = Vector(-1, 0, 0) self.u = Vector(0, -1, 0) self.v = Vector(0, 0, 1) elif upvec: self.v = upvec self.v.normalize() self.u = self.v.cross(self.axis) else: self.v = axis.cross(Vector(1, 0, 0)) self.v.normalize() self.u = DraftVecUtils.rotate(self.v, -math.pi/2, self.axis) offsetVector = Vector(axis) offsetVector.multiply(offset) self.position = point.add(offsetVector) self.weak = False
def GetOrthoVector(self, line, distance, side=''): """ Return the orthogonal vector pointing toward the indicated side at the provided position. Defaults to left-hand side """ _dir = 1.0 _side = side.lower() if _side in ['r', 'rt', 'right']: _dir = -1.0 start = line.Start end = line.End if (start is None) or (end is None): return None, None _delta = end.sub(start).normalize() _left = Vector(-_delta.y, _delta.x, 0.0) _coord = start.add(_delta.multiply(distance)) return _coord, _left.multiply(_dir)
def get_ortho_vector(line_dict, distance, side=''): """ Return the orthogonal vector pointing toward the indicated side at the provided position. Defaults to left-hand side """ _dir = 1.0 _side = side.lower() if _side in ['r', 'rt', 'right']: _dir = -1.0 start = line_dict['Start'] end = line_dict['End'] if (start is None) or (end is None): return None, None _delta = end.sub(start).normalize() _left = Vector(-_delta.y, _delta.x, 0.0) _coord = get_coordinate(line_dict['Start'], line_dict['BearingIn'], distance) return _coord, _left.multiply(_dir)
def Activated(self): sel = FreeCADGui.Selection.getSelection() if not sel: FreeCAD.Console.PrintMessage("Error: no meshes selected\n") return for obj in FreeCADGui.Selection.getSelection(): if "Mesh" not in obj.PropertiesList: FreeCAD.Console.PrintMessage( "Error: " + obj.Name + " is not an object of type 'Mesh::Feature'\n") continue # export facets arrows = [] condName = obj.Label.replace(" ", "_") for facet in obj.Mesh.Facets: center = Vector(0.0, 0.0, 0.0) avgSideLen = 0.0 for j, point in enumerate(facet.Points): # 'point' is a tuple, transform in vector center = center + Vector(point) # get side length side = Vector(facet.Points[(j + 1) % 3]) - Vector(point) avgSideLen += side.Length # calculate the reference point # (there should be a better way to divide a vector by a scalar..) center.multiply(1.0 / len(facet.Points)) # and now move along the normal, proportional to the average facet dimension scaledNormal = Vector(facet.Normal) scaledNormal.multiply(avgSideLen / len(facet.Points)) refpoint = center + scaledNormal arrows.append(self.make_arrow__(center, refpoint)) # add the vector normals visualization to the view # Note: could also use Part.show(normals) but in this case we could # not give the (permanent) name to the object, only change the label afterwards if len(arrows) > 40: step = len(arrows) / float(40) arrows = [arrows[int(x * step)] for x in range(40)] normals = Part.makeCompound(arrows) normalobj = FreeCAD.ActiveDocument.addObject( "Part::Feature", obj.Name + "_normals") normalobj.Shape = normals
def alignToPointAndAxis(self, point, axis, offset, upvec=None): self.doc = FreeCAD.ActiveDocument self.axis = axis; self.axis.normalize() if (DraftVecUtils.equals(axis, Vector(1,0,0))): self.u = Vector(0,1,0) self.v = Vector(0,0,1) elif (DraftVecUtils.equals(axis, Vector(-1,0,0))): self.u = Vector(0,-1,0) self.v = Vector(0,0,1) elif upvec: self.v = upvec self.v.normalize() self.u = self.v.cross(self.axis) else: self.v = axis.cross(Vector(1,0,0)) self.v.normalize() self.u = DraftVecUtils.rotate(self.v, -math.pi/2, self.axis) offsetVector = Vector(axis); offsetVector.multiply(offset) self.position = point.add(offsetVector) self.weak = False
def alignToPointAndAxis(self, point, axis, offset=0, upvec=None): self.doc = FreeCAD.ActiveDocument self.axis = axis; self.axis.normalize() if (DraftVecUtils.equals(axis, Vector(1,0,0))): self.u = Vector(0,1,0) self.v = Vector(0,0,1) elif (DraftVecUtils.equals(axis, Vector(-1,0,0))): self.u = Vector(0,-1,0) self.v = Vector(0,0,1) elif upvec: self.v = upvec self.v.normalize() self.u = self.v.cross(self.axis) else: self.v = axis.cross(Vector(1,0,0)) self.v.normalize() self.u = DraftVecUtils.rotate(self.v, -math.pi/2, self.axis) offsetVector = Vector(axis); offsetVector.multiply(offset) self.position = point.add(offsetVector) self.weak = False
def makePortShape(self,n1,n2): ''' Compute a port shape given: 'n1': start node position (FreeCAD.Vector) 'n2': end node position (FreeCAD.Vector) ''' # do not accept coincident nodes if (n2-n1).Length < EMFHPORT_LENTOL: return None line = Part.makeLine(n1, n2) # calculate arrow head base direction = n1 - n2 length = direction.Length base = Vector(direction) base.normalize() base.multiply(length * 0.8) base = n2 + base # radius2 is calculated for a fixed arrow head angle tan(15deg)=0.27 cone = Part.makeCone(0.2 * length * 0.27, 0.0, 0.2 * length, base, direction, 360) # add the compound representing the arrow arrow = Part.makeCompound([line, cone]) return arrow
def mouse_event(self, arg): """ SoLocation2Event callback """ #force refresh the view matrix if dragging self.mouse.update(arg, ViewState().view.getCursorPos()) if MouseState().shiftDown: _dist = MouseState().vector.Length if not _dist: return _vec = Vector(MouseState().vector).normalize() MouseState().set_mouse_position(MouseState().last_coord.add( _vec.multiply(_dist * 0.10)))
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 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: baseface = None 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(normal) if not DraftVecUtils.isNull(dvec): dvec.normalize() sh = None if obj.Align == "Left": dvec.multiply(width) if obj.Offset.Value: dvec2 = DraftVecUtils.scaleTo(dvec,obj.Offset.Value) 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.multiply(width) dvec = dvec.negative() if obj.Offset.Value: dvec2 = DraftVecUtils.scaleTo(dvec,obj.Offset.Value) 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": 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: baseface = baseface.fuse(f) else: baseface = f if baseface: base,placement = self.rebase(baseface) 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
def getExtrusionData(self,obj): """returns (shape,extrusion vector,placement) or None""" import Part,DraftGeomUtils data = ArchComponent.Component.getExtrusionData(self,obj) if data: 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) 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) 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 makeSegShape(n1, n2, width, height, ww): ''' Compute a segment shape given: 'n1': start node position (Vector) 'n2': end node position (Vector) 'width': segment width 'height': segment height 'ww': cross-section direction (along width) Returns the created Shape ''' # do not accept coincident nodes if (n2 - n1).Length < EMFHSEGMENT_LENTOL: return None # vector along length wl = n2 - n1 # calculate the vector along the height wh = (ww.cross(wl)) # if cross-section is not defined, by default the width vector # is assumed to lie in x-y plane perpendicular to the length. # If the length direction is parallel to the z-axis, then # the width is assumed along the x-axis. # The same is done if 'ww' has been defined parallel to 'wl' if ww.Length < EMFHSEGMENT_LENTOL or wh.Length < EMFHSEGMENT_LENTOL: # if length parallel to the z-axis (note that getAngle() always returns a value # between 0 and 180) angle = wl.getAngle(Vector(0, 0, 1)) * FreeCAD.Units.Radian if angle < EMFHSEGMENT_PARTOL or angle > 180 - EMFHSEGMENT_PARTOL: ww = Vector(1, 0, 0) else: ww = (wl.cross(Vector(0, 0, 1))).normalize() # and re-calculate 'wh' since we changed 'ww' wh = (ww.cross(wl)) # normalize the freshly calculated 'wh' wh.normalize() # copy ww as the multiply() method changes the vector on which is called wwHalf = Vector(ww) # must normalize. We don't want to touch 'ww', as this is user's defined wwHalf.normalize() wwHalf.multiply(width / 2) # copy wh as the multiply() method changes the vector on which is called whHalf = Vector(wh) whHalf.multiply(height / 2) # calculate the vertexes v11 = n1 - wwHalf - whHalf v12 = n1 + wwHalf - whHalf v13 = n1 + wwHalf + whHalf v14 = n1 - wwHalf + whHalf v21 = n2 - wwHalf - whHalf v22 = n2 + wwHalf - whHalf v23 = n2 + wwHalf + whHalf v24 = n2 - wwHalf + whHalf # now make faces # front poly = Part.makePolygon([v11, v12, v13, v14, v11]) face1 = Part.Face(poly) # back poly = Part.makePolygon([v21, v24, v23, v22, v21]) face2 = Part.Face(poly) # left poly = Part.makePolygon([v11, v14, v24, v21, v11]) face3 = Part.Face(poly) # right poly = Part.makePolygon([v12, v22, v23, v13, v12]) face4 = Part.Face(poly) # top poly = Part.makePolygon([v14, v13, v23, v24, v14]) face5 = Part.Face(poly) # bottom poly = Part.makePolygon([v11, v21, v22, v12, v11]) face6 = Part.Face(poly) # create a shell. Does not need to be solid. segShell = Part.makeShell([face1, face2, face3, face4, face5, face6]) return segShell
def getExtrusionData(self,obj): """returns (shape,extrusion vector,placement) or None""" if hasattr(obj,"IfcRole"): role = obj.IfcRole else: role = obj.Role 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) in ["Floor","BuildingPart"]: 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) if not baseface: for w in obj.Base.Shape.Wires: if not w.isClosed(): p0 = w.OrderedVertexes[0].Point p1 = w.OrderedVertexes[-1].Point if p0 != p1: e = Part.Line(p0,p1).toShape() w.add(e) 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) normal = placement.inverse().Rotation.multVec(normal) 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 (role != "Slab"): h2 = height/2 or 0.5 w2 = width/2 or 0.5 v1 = Vector(0,-w2,-h2) v4 = Vector(0,-w2,h2) v3 = Vector(0,w2,h2) v2 = 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.Length: normal = Vector(obj.Normal) normal = placement.inverse().Rotation.multVec(normal) if not normal: normal = Vector(0,0,1) if not normal.Length: normal = Vector(0,0,1) extrusion = normal if (length > height) and (role != "Slab"): if length: extrusion = normal.multiply(length) else: if height: extrusion = normal.multiply(height) return (base,extrusion,placement) return None
def execute(self, obj): "creates the structure shape" import Part, DraftGeomUtils # getting default values length = 1 width = 1 height = 1 if hasattr(obj, "Length"): if obj.Length.Value: length = obj.Length.Value if hasattr(obj, "Width"): if obj.Width.Value: width = obj.Width.Value if hasattr(obj, "Height"): if obj.Height.Value: height = obj.Height.Value else: for p in obj.InList: if Draft.getType(p) == "Floor": if p.Height.Value: height = p.Height.Value # creating base shape pl = obj.Placement base = None if obj.Base: if obj.Base.isDerivedFrom("Part::Feature"): if obj.Base.Shape.isNull(): return if hasattr(obj, "Tool"): if obj.Tool: try: base = obj.Tool.Shape.copy().makePipe( obj.Base.Shape.copy()) except: FreeCAD.Console.PrintError( translate( "Arch", "Error: The base shape couldn't be extruded along this tool object" )) return if not base: if obj.Normal == Vector(0, 0, 0): p = FreeCAD.Placement(obj.Base.Placement) normal = p.Rotation.multVec(Vector(0, 0, 1)) else: normal = Vector(obj.Normal) normal = normal.multiply(height) base = obj.Base.Shape.copy() if base.Solids: pass elif base.Faces: self.BaseProfile = base self.ExtrusionVector = normal base = base.extrude(normal) elif (len(base.Wires) == 1): if base.Wires[0].isClosed(): base = Part.Face(base.Wires[0]) self.BaseProfile = base self.ExtrusionVector = normal base = base.extrude(normal) elif obj.Base.isDerivedFrom("Mesh::Feature"): if obj.Base.Mesh.isSolid(): if obj.Base.Mesh.countComponents() == 1: sh = ArchCommands.getShapeFromMesh(obj.Base.Mesh) if sh.isClosed() and sh.isValid() and sh.Solids: base = sh else: if obj.Normal == Vector(0, 0, 0): if length > height: normal = Vector(1, 0, 0).multiply(length) else: normal = Vector(0, 0, 1).multiply(height) else: normal = Vector(obj.Normal).multiply(height) self.ExtrusionVector = normal if length > height: 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) base = Part.makePolygon([v1, v2, v3, v4, v1]) base = Part.Face(base) self.BaseProfile = base base = base.extrude(self.ExtrusionVector) base = self.processSubShapes(obj, base, pl) if base: # applying axes pts = self.getAxisPoints(obj) apl = self.getAxisPlacement(obj) if pts: fsh = [] for i in range(len(pts)): if hasattr(obj, "Exclude"): if i in obj.Exclude: continue sh = base.copy() if apl: sh.Placement.Rotation = apl.Rotation sh.translate(pts[i]) fsh.append(sh) obj.Shape = Part.makeCompound(fsh) # finalizing else: if base: if not base.isNull(): if base.isValid() and base.Solids: if base.Volume < 0: base.reverse() if base.Volume < 0: FreeCAD.Console.PrintError( translate("Arch", "Couldn't compute a shape")) return base = base.removeSplitter() obj.Shape = base if not pl.isNull(): obj.Placement = pl
def execute(self,obj): "creates the structure shape" import Part, DraftGeomUtils # getting default values length = 1 width = 1 height = 1 if hasattr(obj,"Length"): if obj.Length.Value: length = obj.Length.Value if hasattr(obj,"Width"): if obj.Width.Value: width = obj.Width.Value if hasattr(obj,"Height"): if obj.Height.Value: height = obj.Height.Value else: for p in obj.InList: if Draft.getType(p) == "Floor": if p.Height.Value: height = p.Height.Value # creating base shape pl = obj.Placement base = None if obj.Base: if obj.Base.isDerivedFrom("Part::Feature"): if obj.Base.Shape.isNull(): return if hasattr(obj,"Tool"): if obj.Tool: try: base = obj.Tool.Shape.copy().makePipe(obj.Base.Shape.copy()) except: FreeCAD.Console.PrintError(translate("Arch","Error: The base shape couldn't be extruded along this tool object")) return if not base: if obj.Normal == Vector(0,0,0): p = FreeCAD.Placement(obj.Base.Placement) normal = p.Rotation.multVec(Vector(0,0,1)) else: normal = Vector(obj.Normal) normal = normal.multiply(height) base = obj.Base.Shape.copy() if base.Solids: pass elif base.Faces: self.BaseProfile = base self.ExtrusionVector = normal base = base.extrude(normal) elif (len(base.Wires) == 1): if base.Wires[0].isClosed(): base = Part.Face(base.Wires[0]) self.BaseProfile = base self.ExtrusionVector = normal base = base.extrude(normal) elif obj.Base.isDerivedFrom("Mesh::Feature"): if obj.Base.Mesh.isSolid(): if obj.Base.Mesh.countComponents() == 1: sh = ArchCommands.getShapeFromMesh(obj.Base.Mesh) if sh.isClosed() and sh.isValid() and sh.Solids: base = sh else: if obj.Normal == Vector(0,0,0): if length > height: normal = Vector(1,0,0).multiply(length) else: normal = Vector(0,0,1).multiply(height) else: normal = Vector(obj.Normal).multiply(height) self.ExtrusionVector = normal if length > height: 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) base = Part.makePolygon([v1,v2,v3,v4,v1]) base = Part.Face(base) self.BaseProfile = base base = base.extrude(self.ExtrusionVector) base = self.processSubShapes(obj,base,pl) if base: # applying axes pts = self.getAxisPoints(obj) apl = self.getAxisPlacement(obj) if pts: fsh = [] for i in range(len(pts)): if hasattr(obj,"Exclude"): if i in obj.Exclude: continue sh = base.copy() if apl: sh.Placement.Rotation = apl.Rotation sh.translate(pts[i]) fsh.append(sh) obj.Shape = Part.makeCompound(fsh) # finalizing else: if base: if not base.isNull(): if base.isValid() and base.Solids: if base.Volume < 0: base.reverse() if base.Volume < 0: FreeCAD.Console.PrintError(translate("Arch","Couldn't compute a shape")) return base = base.removeSplitter() obj.Shape = base if not pl.isNull(): obj.Placement = pl
def execute(self,obj): "creates the structure shape" import Part, DraftGeomUtils if self.clone(obj): return normal,length,width,height = self.getDefaultValues(obj) # creating base shape pl = obj.Placement base = None if obj.Base: if obj.Base.isDerivedFrom("Part::Feature"): if obj.Base.Shape.isNull(): return if not obj.Base.Shape.isValid(): if not obj.Base.Shape.Solids: # let pass invalid objects if they have solids... return if hasattr(obj,"Tool"): if obj.Tool: try: base = obj.Tool.Shape.copy().makePipe(obj.Base.Shape.copy()) except Part.OCCError: FreeCAD.Console.PrintError(translate("Arch","Error: The base shape couldn't be extruded along this tool object")) return if not base: if not height: return if obj.Normal == Vector(0,0,0): p = FreeCAD.Placement(obj.Base.Placement) normal = p.Rotation.multVec(normal) else: normal = Vector(obj.Normal) normal = normal.multiply(height) base = obj.Base.Shape.copy() if base.Solids: pass elif base.Faces: base = base.extrude(normal) elif (len(base.Wires) == 1): if base.Wires[0].isClosed(): try: base = Part.Face(base.Wires[0]) base = base.extrude(normal) except Part.OCCError: FreeCAD.Console.PrintError(obj.Label+" : "+str(translate("Arch","Unable to extrude the base shape\n"))) return elif obj.Base.isDerivedFrom("Mesh::Feature"): if obj.Base.Mesh.isSolid(): if obj.Base.Mesh.countComponents() == 1: sh = ArchCommands.getShapeFromMesh(obj.Base.Mesh) if sh.isClosed() and sh.isValid() and sh.Solids and (not sh.isNull()): base = sh else: FreeCAD.Console.PrintWarning(str(translate("Arch","This mesh is an invalid solid"))) obj.Base.ViewObject.show() else: base = self.getProfiles(obj) if base: if length > height: normal = normal.multiply(length) else: normal = normal.multiply(height) base = Part.Face(base[0]) base = base.extrude(normal) base = self.processSubShapes(obj,base,pl) self.applyShape(obj,base,pl)
def createGeometry(self, obj): import Part, DraftGeomUtils # getting default values height = width = length = 1 if hasattr(obj, "Length"): if obj.Length: length = obj.Length if hasattr(obj, "Width"): if obj.Width: width = obj.Width if hasattr(obj, "Height"): if obj.Height: height = obj.Height # creating base shape pl = obj.Placement base = None if obj.Base: if obj.Base.isDerivedFrom("Part::Feature"): if obj.Normal == Vector(0, 0, 0): p = FreeCAD.Placement(obj.Base.Placement) normal = p.Rotation.multVec(Vector(0, 0, 1)) else: normal = Vector(obj.Normal) normal = normal.multiply(height) base = obj.Base.Shape.copy() if base.Solids: pass elif base.Faces: base = base.extrude(normal) elif (len(base.Wires) == 1): if base.Wires[0].isClosed(): base = Part.Face(base.Wires[0]) base = base.extrude(normal) elif obj.Base.isDerivedFrom("Mesh::Feature"): if obj.Base.Mesh.isSolid(): if obj.Base.Mesh.countComponents() == 1: sh = ArchCommands.getShapeFromMesh(obj.Base.Mesh) if sh.isClosed() and sh.isValid() and sh.Solids: base = sh else: if obj.Normal == Vector(0, 0, 0): normal = Vector(0, 0, 1) else: normal = Vector(obj.Normal) normal = normal.multiply(height) l2 = length / 2 or 0.5 w2 = width / 2 or 0.5 v1 = Vector(-l2, -w2, 0) v2 = Vector(l2, -w2, 0) v3 = Vector(l2, w2, 0) v4 = Vector(-l2, w2, 0) base = Part.makePolygon([v1, v2, v3, v4, v1]) base = Part.Face(base) base = base.extrude(normal) base = self.processSubShapes(obj, base) if base: # applying axes pts = self.getAxisPoints(obj) apl = self.getAxisPlacement(obj) if pts: fsh = [] for i in range(len(pts)): if hasattr(obj, "Exclude"): if i in obj.Exclude: continue sh = base.copy() if apl: sh.Placement.Rotation = apl.Rotation sh.translate(pts[i]) fsh.append(sh) obj.Shape = Part.makeCompound(fsh) # finalizing else: if base: if not base.isNull(): if base.isValid() and base.Solids: if base.Volume < 0: base.reverse() if base.Volume < 0: FreeCAD.Console.PrintError( str( translate( "Arch", "Couldn't compute the wall shape")) ) return base = base.removeSplitter() obj.Shape = base if not DraftGeomUtils.isNull(pl): obj.Placement = pl
def createGeometry(self, obj): import Part from draftlibs import fcgeo flat = False if hasattr(obj.ViewObject, "DisplayMode"): flat = (obj.ViewObject.DisplayMode == "Flat 2D") def getbase(wire): "returns a full shape from a base wire" dvec = fcgeo.vec(wire.Edges[0]).cross(normal) dvec.normalize() if obj.Align == "Left": dvec = dvec.multiply(obj.Width) w2 = fcgeo.offsetWire(wire, dvec) sh = fcgeo.bind(wire, w2) elif obj.Align == "Right": dvec = dvec.multiply(obj.Width) dvec = fcvec.neg(dvec) w2 = fcgeo.offsetWire(wire, dvec) sh = fcgeo.bind(wire, w2) elif obj.Align == "Center": dvec = dvec.multiply(obj.Width / 2) w1 = fcgeo.offsetWire(wire, dvec) dvec = fcvec.neg(dvec) w2 = fcgeo.offsetWire(wire, dvec) sh = fcgeo.bind(w1, w2) # fixing self-intersections sh.fix(0.1, 0, 1) if height and (not flat): norm = Vector(normal).multiply(height) sh = sh.extrude(norm) return sh pl = obj.Placement # getting default values height = normal = None if obj.Height: height = obj.Height else: for p in obj.InList: if Draft.getType(p) == "Floor": height = p.Height if not height: height = 1 if obj.Normal == Vector(0, 0, 0): normal = Vector(0, 0, 1) else: normal = Vector(obj.Normal) # computing shape if obj.Base: if obj.Base.isDerivedFrom("Part::Feature"): base = obj.Base.Shape.copy() if base.Solids: pass elif base.Faces: if height: norm = normal.multiply(height) base = base.extrude(norm) elif base.Wires: temp = None for wire in obj.Base.Shape.Wires: sh = getbase(wire) if temp: temp = temp.oldFuse(sh) else: temp = sh base = temp else: if obj.Length == 0: return v1 = Vector(0, 0, 0) v2 = Vector(obj.Length, 0, 0) w = Part.Wire(Part.Line(v1, v2).toShape()) base = getbase(w) for app in obj.Additions: base = base.oldFuse(app.Shape) app.ViewObject.hide() #to be removed for hole in obj.Subtractions: if Draft.getType(hole) == "Window": # window if hole.Base and obj.Width: f = self.getSubVolume(hole.Base, obj.Width) base = base.cut(f) elif Draft.isClone(hole, "Window"): if hole.Objects[0].Base and obj.Width: f = self.getSubVolume(hole.Objects[0].Base, obj.Width, hole.Placement.Base) base = base.cut(f) elif hasattr(obj, "Shape"): base = base.cut(hole.Shape) hole.ViewObject.hide() # to be removed obj.Shape = base if not fcgeo.isNull(pl): obj.Placement = pl
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) in ["Floor","BuildingPart"]: if p.Height.Value: height = p.Height.Value if not height: return None if obj.Normal == Vector(0,0,0): normal = Vector(0,0,1) else: normal = Vector(obj.Normal) base = None placement = None self.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 len(obj.Base.Shape.Edges) == 1: self.basewires = [Part.Wire(obj.Base.Shape.Edges)] else: # self.basewires = obj.Base.Shape.Wires self.basewires = [] for cluster in Part.getSortedClusters(obj.Base.Shape.Edges): for c in Part.sortEdges(cluster): self.basewires.append(Part.Wire(c)) if self.basewires and width: if (len(self.basewires) == 1) and layers: self.basewires = [self.basewires[0] for l in layers] layeroffset = 0 baseface = None for i,wire in enumerate(self.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) # baseface = baseface.removeSplitter() s = DraftGeomUtils.removeSplitter(baseface) if s: baseface = s 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
def projectPoint(self, p, direction=None): '''project point onto plane, default direction is orthogonal''' if not direction: direction = self.axis t = Vector(direction) t.multiply(self.offsetToPoint(p, direction)) return p.add(t)
def export_mesh(filename, meshobj=None, isDiel=False, showNormals=False, folder=DEF_FOLDER): '''Export mesh in FasterCap format as conductor or dielectric interface 'filename' is the name of the export file 'meshobj' must be a Mesh::Feature object 'isDiel' specifies if the mesh is a dielectric, so the function will add a reference point to each panel to indicate which is the external side (outside) 'showNormals' will add a compound object composed by a set of arrows showing the normal direction for each panel 'folder' is the folder in which 'filename' will be saved Example: mymeshGui = Gui.ActiveDocument.Mesh mymeshObj = mymeshGui.Object export_mesh("mymesh.txt", meshobj=mymeshObj, folder="C:/temp") ''' # if no valid mesh was passed if meshobj == None: return elif meshobj.TypeId != "Mesh::Feature": FreeCAD.Console.PrintMessage( "Error: 'meshobj' is not an object of type 'Mesh::Feature'") return if not os.path.isdir(folder): os.mkdir(folder) with open(folder + os.sep + filename, 'w') as fid: # write the preamble if isDiel == True: fid.write("0 dielectric definition file for mesh '" + meshobj.Label) else: fid.write("0 conductor definition file for mesh '" + meshobj.Label) fid.write("' created using FreeCAD's ElectroMagnetic workbench\n") fid.write( "* see http://www.freecad.org and http://www.fastfieldsolvers.com\n" ) fid.write("\n") # export facets arrows = [] condName = meshobj.Label.replace(" ", "_") for facet in meshobj.Mesh.Facets: if len(facet.Points) == 3: fid.write("T " + condName) elif len(facet.Points) == 4: fid.write("Q " + condName) else: FreeCAD.Console.PrintMessage( "Unforeseen number of mesh facet points: " + len(facet.Points) + ", skipping facet") continue center = Vector(0.0, 0.0, 0.0) avgSideLen = 0.0 for j, point in enumerate(facet.Points): fid.write(" ") for i in range(3): fid.write(" " + str(point[i])) if isDiel == True or showNormals == True: # 'point' is a tuple, transform in vector center = center + Vector(point) # get side length side = Vector(facet.Points[(j + 1) % 3]) - Vector(point) avgSideLen += side.Length if isDiel == True or showNormals == True: # calculate the reference point # (there should be a better way to divide a vector by a scalar..) center.multiply(1.0 / len(facet.Points)) # and now move along the normal, proportional to the average facet dimension scaledNormal = Vector(facet.Normal) scaledNormal.multiply(avgSideLen / len(facet.Points)) refpoint = center + scaledNormal if isDiel == True: fid.write(" ") for i in range(3): fid.write(" " + str(refpoint[i])) fid.write("\n") if showNormals == True: arrows.append(make_arrow(center, refpoint)) if showNormals == True: # add the vector normals visualization to the view # Note: could also use Part.show(normals) but in this case we could # not give the (permanent) name to the object, only change the label afterwards normals = Part.makeCompound(arrows) normalobj = FreeCAD.ActiveDocument.addObject( "Part::Feature", "Normals") normalobj.Shape = normals fid.closed
def export_faces(filename, isDiel=False, name="", showNormals=False, forceMesh=False, folder=DEF_FOLDER): '''Export faces in FasterCap format as conductor or dielectric interface The function operates on the selection. The selection can be a face, a compound or a solid. 'filename' is the name of the export file 'isDiel' specifies if the mesh is a dielectric, so the function will add a reference point to each panel to indicate which is the external side (outside) 'name' is the name of the conductor created in the file. If not specified, defaults to the label of the first element in the selection set 'forceMesh' force the meshing of all faces, even if they could be exported non-meshed (triangular or quadrilateral faces). 'showNormals' will add a compound object composed by a set of arrows showing the normal direction for each panel 'folder' is the folder in which 'filename' will be saved Example: export_faces("mymesh.txt", folder="C:/temp") ''' # get selection sel = FreeCADGui.Selection.getSelection() # if no valid selection was passed if sel == None: return if name == "": condName = sel[0].Label.replace(" ", "_") else: condName = name # scan objects in selection and extract all faces faces = [] facets = [] for obj in sel: if obj.TypeId == "Mesh::Feature": facets.extend(obj.Mesh.Facets) else: if obj.Shape.ShapeType == "Face": faces.append(obj.Shape) elif obj.Shape.ShapeType == "Compound" or obj.Shape.ShapeType == "Solid": faces.extend(obj.Shape.Faces) # scan faces and find out which faces have more than 4 vertexes # TBD warning: should mesh also curve faces if forceMesh == False: facesComplex = [x for x in faces if len(x.Vertexes) >= 5] facesSimple = [x for x in faces if len(x.Vertexes) < 5] else: facesComplex = faces facesSimple = [] # mesh complex faces doc = FreeCAD.ActiveDocument for face in facesComplex: mesh = doc.addObject("Mesh::Feature", "Mesh") mesh.Mesh = MeshPart.meshFromShape(Shape=face, Fineness=0, SecondOrder=0, Optimize=1, AllowQuad=0) facets.extend(mesh.Mesh.Facets) # now we have faces and facets. Uniform all panels = [] for face in facesSimple: sortEdges = Part.__sortEdges__(face.Edges) # Point of a Vertex is a Vector, as well as Face.normalAt() points = [x.firstVertex().Point for x in sortEdges] panels.append([points, face.normalAt(0, 0)]) for facet in facets: points = [Vector(x) for x in facet.Points] panels.append([points, Vector(facet.Normal)]) if not os.path.isdir(folder): os.mkdir(folder) with open(folder + os.sep + filename, 'w') as fid: # write the preamble if isDiel == True: fid.write( "0 dielectric definition file for the following objects\n") else: fid.write( "0 conductor definition file for the following objects\n") for obj in sel: fid.write("* - " + obj.Label + "\n") fid.write("* created using FreeCAD's ElectroMagnetic workbench\n") fid.write( "* see http://www.freecad.org and http://www.fastfieldsolvers.com\n\n" ) arrows = [] # export faces for panel in panels: pointsNum = len(panel[0]) if pointsNum == 3: fid.write("T " + condName) elif pointsNum == 4: fid.write("Q " + condName) else: FreeCAD.Console.PrintMessage( "Unforeseen number of panel vertexes: " + pointsNum + ", skipping panel") continue center = Vector(0.0, 0.0, 0.0) avgSideLen = 0.0 for j, vertex in enumerate(panel[0]): fid.write(" ") for i in range(3): fid.write(" " + str(vertex[i])) if isDiel == True or showNormals == True: # 'point' is a tuple, transform in vector center = center + vertex # get side length side = panel[0][(j + 1) % 3] - vertex avgSideLen += side.Length if isDiel == True or showNormals == True: # calculate the reference point # (there should be a better way to divide a vector by a scalar..) center.multiply(1.0 / pointsNum) # and now move along the normal, proportional to the average facet dimension scaledNormal = panel[1] scaledNormal.multiply(avgSideLen / pointsNum) refpoint = center + scaledNormal if isDiel == True: fid.write(" ") for i in range(3): fid.write(" " + str(refpoint[i])) fid.write("\n") if showNormals == True: arrows.append(make_arrow(center, refpoint)) fid.closed if showNormals == True: # add the vector normals visualization to the view # Note: could also use Part.show(normals) but in this case we could # not give the (permanent) name to the object, only change the label afterwards normals = Part.makeCompound(arrows) normalobj = FreeCAD.ActiveDocument.addObject("Part::Feature", "Normals") normalobj.Shape = normals
def createGeometry(self,obj): import Part, DraftGeomUtils # getting default values height = width = length = 1 if hasattr(obj,"Length"): if obj.Length: length = obj.Length if hasattr(obj,"Width"): if obj.Width: width = obj.Width if hasattr(obj,"Height"): if obj.Height: height = obj.Height # creating base shape pl = obj.Placement base = None if obj.Base: if obj.Base.isDerivedFrom("Part::Feature"): if obj.Normal == Vector(0,0,0): p = FreeCAD.Placement(obj.Base.Placement) normal = p.Rotation.multVec(Vector(0,0,1)) else: normal = Vector(obj.Normal) normal = normal.multiply(height) base = obj.Base.Shape.copy() if base.Solids: pass elif base.Faces: base = base.extrude(normal) elif (len(base.Wires) == 1): if base.Wires[0].isClosed(): base = Part.Face(base.Wires[0]) base = base.extrude(normal) else: if obj.Normal == Vector(0,0,0): normal = Vector(0,0,1) else: normal = Vector(obj.Normal) normal = normal.multiply(height) l2 = length/2 or 0.5 w2 = width/2 or 0.5 v1 = Vector(-l2,-w2,0) v2 = Vector(l2,-w2,0) v3 = Vector(l2,w2,0) v4 = Vector(-l2,w2,0) base = Part.makePolygon([v1,v2,v3,v4,v1]) base = Part.Face(base) base = base.extrude(normal) if base: # applying adds and subs if not base.isNull(): for app in obj.Additions: if hasattr(app,"Shape"): if not app.Shape.isNull(): base = base.fuse(app.Shape) app.ViewObject.hide() # to be removed for hole in obj.Subtractions: if hasattr(hole,"Shape"): if not hole.Shape.isNull(): base = base.cut(hole.Shape) hole.ViewObject.hide() # to be removed # applying axes pts = self.getAxisPoints(obj) apl = self.getAxisPlacement(obj) if pts: fsh = [] for i in range(len(pts)): if hasattr(obj,"Exclude"): if i in obj.Exclude: continue sh = base.copy() if apl: sh.Placement.Rotation = apl.Rotation sh.translate(pts[i]) fsh.append(sh) obj.Shape = Part.makeCompound(fsh) # finalizing else: if base: if not base.isNull(): base = base.removeSplitter() obj.Shape = base if not DraftGeomUtils.isNull(pl): obj.Placement = pl
def createGeometry(self, obj): "builds the wall shape" if not obj.Base: return import Part, DraftGeomUtils flat = False if hasattr(obj.ViewObject, "DisplayMode"): flat = (obj.ViewObject.DisplayMode == "Flat 2D") width = 1.0 if hasattr(obj, "Width"): if obj.Width: width = obj.Width def getbase(wire): "returns a full shape from a base wire" dvec = DraftGeomUtils.vec(wire.Edges[0]).cross(normal) dvec.normalize() if obj.Align == "Left": dvec = dvec.multiply(width) w2 = DraftGeomUtils.offsetWire(wire, dvec) w1 = Part.Wire(DraftGeomUtils.sortEdges(wire.Edges)) sh = DraftGeomUtils.bind(w1, w2) elif obj.Align == "Right": dvec = dvec.multiply(width) dvec = DraftVecUtils.neg(dvec) w2 = DraftGeomUtils.offsetWire(wire, dvec) w1 = Part.Wire(DraftGeomUtils.sortEdges(wire.Edges)) sh = DraftGeomUtils.bind(w1, w2) elif obj.Align == "Center": dvec = dvec.multiply(width / 2) w1 = DraftGeomUtils.offsetWire(wire, dvec) dvec = DraftVecUtils.neg(dvec) w2 = DraftGeomUtils.offsetWire(wire, dvec) sh = DraftGeomUtils.bind(w1, w2) # fixing self-intersections sh.fix(0.1, 0, 1) if height and (not flat): norm = Vector(normal).multiply(height) sh = sh.extrude(norm) return sh pl = obj.Placement # getting default values height = normal = None if obj.Height: height = obj.Height else: for p in obj.InList: if Draft.getType(p) == "Floor": height = p.Height if not height: height = 1 if obj.Normal == Vector(0, 0, 0): normal = Vector(0, 0, 1) else: normal = Vector(obj.Normal) # computing shape base = None if obj.Base.isDerivedFrom("Part::Feature"): if not obj.Base.Shape.isNull(): if obj.Base.Shape.isValid(): base = obj.Base.Shape.copy() if base.Solids: pass elif base.Faces and (not obj.ForceWire): if height: norm = normal.multiply(height) base = base.extrude(norm) elif base.Wires: temp = None for wire in obj.Base.Shape.Wires: sh = getbase(wire) if temp: temp = temp.fuse(sh) else: temp = sh base = temp elif base.Edges: wire = Part.Wire(base.Edges) sh = getbase(wire) if sh: base = sh else: base = None FreeCAD.Console.PrintError( str(translate("Arch", "Error: Invalid base object"))) elif obj.Base.isDerivedFrom("Mesh::Feature"): if obj.Base.Mesh.isSolid(): if obj.Base.Mesh.countComponents() == 1: sh = ArchCommands.getShapeFromMesh(obj.Base.Mesh) if sh.isClosed() and sh.isValid() and sh.Solids and ( not sh.isNull()): base = sh else: FreeCAD.Console.PrintWarning( str( translate("Arch", "This mesh is an invalid solid"))) obj.Base.ViewObject.show() if base: for app in obj.Additions: if Draft.getType(app) == "Window": # window if app.Base and obj.Width: f = self.getSubVolume(app.Base, width) if f: base = base.cut(f) elif Draft.isClone(app, "Window"): if app.Objects[0].Base and width: f = self.getSubVolume(app.Objects[0].Base, width, app.Placement) if f: base = base.cut(f) elif app.isDerivedFrom("Part::Feature"): if app.Shape: if not app.Shape.isNull(): base = base.fuse(app.Shape) app.ViewObject.hide() #to be removed for hole in obj.Subtractions: if Draft.getType(hole) == "Window": # window if hole.Base and obj.Width: f = self.getSubVolume(hole.Base, width) if f: base = base.cut(f) elif Draft.isClone(hole, "Window"): if hole.Objects[0].Base and width: f = self.getSubVolume(hole.Objects[0].Base, width, hole.Placement) if f: base = base.cut(f) elif hole.isDerivedFrom("Part::Feature"): if hole.Shape: if not hole.Shape.isNull(): base = base.cut(hole.Shape) hole.ViewObject.hide() # to be removed if not base.isNull(): if base.isValid() and base.Solids: if base.Volume < 0: base.reverse() if base.Volume < 0: FreeCAD.Console.PrintError( str( translate("Arch", "Couldn't compute the wall shape"))) return try: base = base.removeSplitter() except: FreeCAD.Console.PrintError( str( translate( "Arch", "Error removing splitter from wall shape")) ) obj.Shape = base if not DraftGeomUtils.isNull(pl): obj.Placement = pl
def execute(self, obj): "creates the panel shape" if self.clone(obj): return import Part, DraftGeomUtils # base tests if obj.Base: if obj.Base.isDerivedFrom("Part::Feature"): if obj.Base.Shape.isNull(): return elif obj.Base.isDerivedFrom("Mesh::Feature"): if not obj.Base.Mesh.isSolid(): return else: if obj.Length.Value: length = obj.Length.Value else: return if obj.Width.Value: width = obj.Width.Value else: return if obj.Thickness.Value: thickness = obj.Thickness.Value else: if not obj.Base: return elif obj.Base.isDerivedFrom("Part::Feature"): if not obj.Base.Shape.Solids: return # creating base shape pl = obj.Placement base = None normal = None if hasattr(obj, "Normal"): if obj.Normal.Length > 0: normal = Vector(obj.Normal) normal.normalize() normal.multiply(thickness) baseprofile = None if obj.Base: base = obj.Base.Shape.copy() if not base.Solids: p = FreeCAD.Placement(obj.Base.Placement) if base.Faces: baseprofile = base if not normal: normal = baseprofile.Faces[0].normalAt( 0, 0).multiply(thickness) base = base.extrude(normal) elif base.Wires: fm = False if hasattr(obj, "FaceMaker"): if obj.FaceMaker != "None": try: base = Part.makeFace( base.Wires, "Part::FaceMaker" + str(obj.FaceMaker)) fm = True except: FreeCAD.Console.PrintError( translate("Arch", "Facemaker returned an error") + "\n") return if not fm: closed = True for w in base.Wires: if not w.isClosed(): closed = False if closed: baseprofile = ArchCommands.makeFace(base.Wires) if not normal: normal = baseprofile.normalAt( 0, 0).multiply(thickness) base = baseprofile.extrude(normal) elif obj.Base.isDerivedFrom("Mesh::Feature"): if obj.Base.Mesh.isSolid(): if obj.Base.Mesh.countComponents() == 1: sh = ArchCommands.getShapeFromMesh(obj.Base.Mesh) if sh.isClosed() and sh.isValid() and sh.Solids: base = sh else: if not normal: normal = Vector(0, 0, 1).multiply(thickness) l2 = length / 2 or 0.5 w2 = width / 2 or 0.5 v1 = Vector(-l2, -w2, 0) v2 = Vector(l2, -w2, 0) v3 = Vector(l2, w2, 0) v4 = Vector(-l2, w2, 0) base = Part.makePolygon([v1, v2, v3, v4, v1]) baseprofile = Part.Face(base) base = baseprofile.extrude(normal) if hasattr(obj, "Area"): if baseprofile: obj.Area = baseprofile.Area if hasattr(obj, "WaveLength"): if baseprofile and obj.WaveLength.Value and obj.WaveHeight.Value: # corrugated element bb = baseprofile.BoundBox bb.enlarge(bb.DiagonalLength) p1 = Vector(bb.getPoint(0).x, bb.getPoint(0).y, bb.Center.z) if obj.WaveType == "Curved": p2 = p1.add( Vector(obj.WaveLength.Value / 2, 0, obj.WaveHeight.Value)) p3 = p2.add( Vector(obj.WaveLength.Value / 2, 0, -obj.WaveHeight.Value)) e1 = Part.Arc(p1, p2, p3).toShape() p4 = p3.add( Vector(obj.WaveLength.Value / 2, 0, -obj.WaveHeight.Value)) p5 = p4.add( Vector(obj.WaveLength.Value / 2, 0, obj.WaveHeight.Value)) e2 = Part.Arc(p3, p4, p5).toShape() else: if obj.WaveHeight.Value < obj.WaveLength.Value: p2 = p1.add( Vector(obj.WaveHeight.Value, 0, obj.WaveHeight.Value)) p3 = p2.add( Vector( obj.WaveLength.Value - 2 * obj.WaveHeight.Value, 0, 0)) p4 = p3.add( Vector(obj.WaveHeight.Value, 0, -obj.WaveHeight.Value)) e1 = Part.makePolygon([p1, p2, p3, p4]) p5 = p4.add( Vector(obj.WaveHeight.Value, 0, -obj.WaveHeight.Value)) p6 = p5.add( Vector( obj.WaveLength.Value - 2 * obj.WaveHeight.Value, 0, 0)) p7 = p6.add( Vector(obj.WaveHeight.Value, 0, obj.WaveHeight.Value)) e2 = Part.makePolygon([p4, p5, p6, p7]) else: p2 = p1.add( Vector(obj.WaveLength.Value / 2, 0, obj.WaveHeight.Value)) p3 = p2.add( Vector(obj.WaveLength.Value / 2, 0, -obj.WaveHeight.Value)) e1 = Part.makePolygon([p1, p2, p3]) p4 = p3.add( Vector(obj.WaveLength.Value / 2, 0, -obj.WaveHeight.Value)) p5 = p4.add( Vector(obj.WaveLength.Value / 2, 0, obj.WaveHeight.Value)) e2 = Part.makePolygon([p3, p4, p5]) edges = [e1, e2] for i in range(int(bb.XLength / (obj.WaveLength.Value * 2))): e1 = e1.copy() e1.translate(Vector(obj.WaveLength.Value * 2, 0, 0)) e2 = e2.copy() e2.translate(Vector(obj.WaveLength.Value * 2, 0, 0)) edges.extend([e1, e2]) basewire = Part.Wire(edges) baseface = basewire.extrude(Vector(0, bb.YLength, 0)) base = baseface.extrude(Vector(0, 0, thickness)) rot = FreeCAD.Rotation(FreeCAD.Vector(0, 0, 1), normal) base.rotate(bb.Center, rot.Axis, math.degrees(rot.Angle)) if obj.WaveDirection.Value: base.rotate(bb.Center, normal, obj.WaveDirection.Value) n1 = normal.negative().normalize().multiply( obj.WaveHeight.Value * 2) self.vol = baseprofile.copy() self.vol.translate(n1) self.vol = self.vol.extrude(n1.negative().multiply(2)) base = self.vol.common(base) base = base.removeSplitter() if not base: FreeCAD.Console.PrintError( transpate("Arch", "Error computing shape of ") + obj.Label + "\n") return False if base and (obj.Sheets > 1) and normal and thickness: bases = [base] for i in range(1, obj.Sheets): n = FreeCAD.Vector(normal).normalize().multiply(i * thickness) b = base.copy() b.translate(n) bases.append(b) base = Part.makeCompound(bases) if base and normal and hasattr(obj, "Offset"): if obj.Offset.Value: v = DraftVecUtils.scaleTo(normal, obj.Offset.Value) base.translate(v) # process subshapes base = self.processSubShapes(obj, base, pl) # applying if base: if not base.isNull(): if base.isValid() and base.Solids: if base.Volume < 0: base.reverse() if base.Volume < 0: FreeCAD.Console.PrintError( translate("Arch", "Couldn't compute a shape")) return base = base.removeSplitter() obj.Shape = base if not pl.isNull(): obj.Placement = pl
def export_mesh(filename, meshobj=None, isDiel=False, folder=DEF_FOLDER, groupName=None): # check input in caller # add qui extension filename = filename + ".qui" if not os.path.isdir(folder): os.mkdir(folder) with open(os.path.join(folder, filename), 'w') as fid: if isDiel == True: fid.write("0 dielectric definition file for mesh '" + meshobj.Label) else: fid.write("0 conductor definition file for mesh '" + meshobj.Label) fid.write( "' created using FreeCAD's Electromagnetic workbench script\n") fid.write("\n") # export faces # for dielectric, right now, assumes that any old normal surface vector can be chosen # as the reference vector, should be more mathematically rigorous than that refvectors = [] if groupName is None: condName = meshobj.Label.replace(" ", "_") else: condName = groupName.replace(" ", "_") for facet in meshobj.Mesh.Facets: if len(facet.Points) == 3: fid.write("T " + condName) elif len(facet.Points) == 4: fid.write("Q " + condName) else: FreeCAD.Console.PrintError( "Error: Unforseen number of mesh facet points: " + len(facet.Points) + ", skipping facet\n") continue center = Vector(0.0, 0.0, 0.0) avgSideLen = 0.0 for j, point in enumerate(facet.Points): fid.write(" ") fid.write(" ".join(map(str, point))) if isDiel == True: for i in range(3): center = center + Vector(point) side = Vector( facet.Points[(j + 1) % 3]) - Vector(point) avgSideLen += side.Length center.multiply(1.0 / len(facet.Points)) scaledNormal = Vector(facet.Normal) scaledNormal.multiply(avgSideLen / len(facet.Points)) refpoint = center + scaledNormal refvectors.append((refpoint.x, refpoint.y, refpoint.z)) fid.write("\n") fid.close if isDiel == True: return random.choice(refvectors) else: return None
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) in ["Floor","BuildingPart"]: if p.Height.Value: height = p.Height.Value if not height: return None if obj.Normal == Vector(0,0,0): normal = Vector(0,0,1) else: normal = Vector(obj.Normal) base = None placement = None self.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 if obj.Normal != Vector(0,0,0): 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 len(obj.Base.Shape.Edges) == 1: self.basewires = [Part.Wire(obj.Base.Shape.Edges)] else: # self.basewires = obj.Base.Shape.Wires self.basewires = [] for cluster in Part.getSortedClusters(obj.Base.Shape.Edges): for c in Part.sortEdges(cluster): self.basewires.append(Part.Wire(c)) if self.basewires and width: if (len(self.basewires) == 1) and layers: self.basewires = [self.basewires[0] for l in layers] layeroffset = 0 baseface = None for i,wire in enumerate(self.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) # baseface = baseface.removeSplitter() s = DraftGeomUtils.removeSplitter(baseface) if s: baseface = s 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) if placement.Rotation.Angle > 0: extrusion = placement.inverse().Rotation.multVec(extrusion) return (base,extrusion,placement) return None
def createGeometry(self,obj): import Part from draftlibs import fcgeo # getting default values height = normal = None if obj.Length: length = obj.Length else: length = 1 width = 1 if hasattr(obj,"Width"): if obj.Width: width = obj.Width if obj.Height: height = obj.Height else: for p in obj.InList: if Draft.getType(p) == "Floor": height = p.Height if not height: height = 1 if obj.Normal == Vector(0,0,0): normal = Vector(0,0,1) else: normal = Vector(obj.Normal) # creating shape pl = obj.Placement norm = normal.multiply(height) base = None if obj.Base: if obj.Base.isDerivedFrom("Part::Feature"): base = obj.Base.Shape.copy() if base.Solids: pass elif base.Faces: base = base.extrude(normal) elif (len(base.Wires) == 1) and base.Wires[0].isClosed(): base = Part.Face(base.Wires[0]) base = base.extrude(normal) 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.makePolygon([v1,v2,v3,v4,v1]) base = Part.Face(base) base = base.extrude(normal) for app in obj.Additions: base = base.oldFuse(app.Shape) app.ViewObject.hide() # to be removed for hole in obj.Subtractions: cut = False if hasattr(hole,"Proxy"): if hasattr(hole.Proxy,"Subvolume"): if hole.Proxy.Subvolume: print "cutting subvolume",hole.Proxy.Subvolume base = base.cut(hole.Proxy.Subvolume) cut = True if not cut: if hasattr(obj,"Shape"): base = base.cut(hole.Shape) hole.ViewObject.hide() # to be removed if base: pts = self.getAxisPoints(obj) if pts: fsh = [] for p in pts: sh = base.copy() sh.translate(p) fsh.append(sh) obj.Shape = Part.makeCompound(fsh) else: obj.Shape = base if not fcgeo.isNull(pl): obj.Placement = pl
def createGeometry(self,obj): import Part, DraftGeomUtils # getting default values height = width = length = 1 if hasattr(obj,"Length"): if obj.Length: length = obj.Length if hasattr(obj,"Width"): if obj.Width: width = obj.Width if hasattr(obj,"Height"): if obj.Height: height = obj.Height # creating base shape pl = obj.Placement base = None if obj.Base: if obj.Base.isDerivedFrom("Part::Feature"): if obj.Normal == Vector(0,0,0): p = FreeCAD.Placement(obj.Base.Placement) normal = p.Rotation.multVec(Vector(0,0,1)) else: normal = Vector(obj.Normal) normal = normal.multiply(height) base = obj.Base.Shape.copy() if base.Solids: pass elif base.Faces: base = base.extrude(normal) elif (len(base.Wires) == 1): if base.Wires[0].isClosed(): base = Part.Face(base.Wires[0]) base = base.extrude(normal) else: if obj.Normal == Vector(0,0,0): normal = Vector(0,0,1) else: normal = Vector(obj.Normal) normal = normal.multiply(height) l2 = length/2 or 0.5 w2 = width/2 or 0.5 v1 = Vector(-l2,-w2,0) v2 = Vector(l2,-w2,0) v3 = Vector(l2,w2,0) v4 = Vector(-l2,w2,0) base = Part.makePolygon([v1,v2,v3,v4,v1]) base = Part.Face(base) base = base.extrude(normal) if base: # applying adds and subs if not base.isNull(): for app in obj.Additions: if hasattr(app,"Shape"): if not app.Shape.isNull(): base = base.fuse(app.Shape) app.ViewObject.hide() # to be removed for hole in obj.Subtractions: if hasattr(hole,"Shape"): if not hole.Shape.isNull(): base = base.cut(hole.Shape) hole.ViewObject.hide() # to be removed pts = self.getAxisPoints(obj) if pts: fsh = [] for p in pts: sh = base.copy() sh.translate(p) fsh.append(sh) obj.Shape = Part.makeCompound(fsh) else: if base: if not base.isNull(): base = base.removeSplitter() obj.Shape = base if not DraftGeomUtils.isNull(pl): obj.Placement = pl
def projectPoint(self, p, direction=None): '''project point onto plane, default direction is orthogonal''' if not direction: direction = self.axis t = Vector(direction) t.multiply(self.offsetToPoint(p, direction)) return p.add(t)
def createGeometry(self,obj): "builds the wall shape" if not obj.Base: return import Part, DraftGeomUtils flat = False if hasattr(obj.ViewObject,"DisplayMode"): flat = (obj.ViewObject.DisplayMode == "Flat 2D") width = 1.0 if hasattr(obj,"Width"): if obj.Width: width = obj.Width def getbase(wire): "returns a full shape from a base wire" dvec = DraftGeomUtils.vec(wire.Edges[0]).cross(normal) dvec.normalize() if obj.Align == "Left": dvec = dvec.multiply(width) w2 = DraftGeomUtils.offsetWire(wire,dvec) w1 = Part.Wire(DraftGeomUtils.sortEdges(wire.Edges)) sh = DraftGeomUtils.bind(w1,w2) elif obj.Align == "Right": dvec = dvec.multiply(width) dvec = DraftVecUtils.neg(dvec) w2 = DraftGeomUtils.offsetWire(wire,dvec) w1 = Part.Wire(DraftGeomUtils.sortEdges(wire.Edges)) sh = DraftGeomUtils.bind(w1,w2) elif obj.Align == "Center": dvec = dvec.multiply(width/2) w1 = DraftGeomUtils.offsetWire(wire,dvec) dvec = DraftVecUtils.neg(dvec) w2 = DraftGeomUtils.offsetWire(wire,dvec) sh = DraftGeomUtils.bind(w1,w2) # fixing self-intersections sh.fix(0.1,0,1) if height and (not flat): norm = Vector(normal).multiply(height) sh = sh.extrude(norm) return sh pl = obj.Placement # getting default values height = normal = None if obj.Height: height = obj.Height else: for p in obj.InList: if Draft.getType(p) == "Floor": height = p.Height if not height: height = 1 if obj.Normal == Vector(0,0,0): normal = Vector(0,0,1) else: normal = Vector(obj.Normal) # computing shape base = None if obj.Base.isDerivedFrom("Part::Feature"): if not obj.Base.Shape.isNull(): if obj.Base.Shape.isValid(): base = obj.Base.Shape.copy() if base.Solids: pass elif base.Faces and (not obj.ForceWire): if height: norm = normal.multiply(height) base = base.extrude(norm) elif base.Wires: temp = None for wire in obj.Base.Shape.Wires: sh = getbase(wire) if temp: temp = temp.fuse(sh) else: temp = sh base = temp elif base.Edges: wire = Part.Wire(base.Edges) sh = getbase(wire) if sh: base = sh else: base = None FreeCAD.Console.PrintError(str(translate("Arch","Error: Invalid base object"))) elif obj.Base.isDerivedFrom("Mesh::Feature"): if obj.Base.Mesh.isSolid(): if obj.Base.Mesh.countComponents() == 1: sh = ArchCommands.getShapeFromMesh(obj.Base.Mesh) if sh.isClosed() and sh.isValid() and sh.Solids and (not sh.isNull()): base = sh else: FreeCAD.Console.PrintWarning(str(translate("Arch","This mesh is an invalid solid"))) obj.Base.ViewObject.show() if base: for app in obj.Additions: if Draft.getType(app) == "Window": # window if app.Base and obj.Width: f = self.getSubVolume(app.Base,width) if f: base = base.cut(f) elif Draft.isClone(app,"Window"): if app.Objects[0].Base and width: f = self.getSubVolume(app.Objects[0].Base,width,app.Placement) if f: base = base.cut(f) elif app.isDerivedFrom("Part::Feature"): if app.Shape: if not app.Shape.isNull(): base = base.fuse(app.Shape) app.ViewObject.hide() #to be removed for hole in obj.Subtractions: if Draft.getType(hole) == "Window": # window if hole.Base and obj.Width: f = self.getSubVolume(hole.Base,width) if f: base = base.cut(f) elif Draft.isClone(hole,"Window"): if hole.Objects[0].Base and width: f = self.getSubVolume(hole.Objects[0].Base,width,hole.Placement) if f: base = base.cut(f) elif hole.isDerivedFrom("Part::Feature"): if hole.Shape: if not hole.Shape.isNull(): base = base.cut(hole.Shape) hole.ViewObject.hide() # to be removed if not base.isNull(): if base.isValid() and base.Solids: if base.Volume < 0: base.reverse() if base.Volume < 0: FreeCAD.Console.PrintError(str(translate("Arch","Couldn't compute the wall shape"))) return try: base = base.removeSplitter() except: FreeCAD.Console.PrintError(str(translate("Arch","Error removing splitter from wall shape"))) obj.Shape = base if not DraftGeomUtils.isNull(pl): obj.Placement = pl
def createGeometry(self, obj): import Part from draftlibs import fcgeo # getting default values height = normal = None if obj.Length: length = obj.Length else: length = 1 if obj.Width: width = obj.Width else: width = 1 if obj.Height: height = obj.Height else: for p in obj.InList: if Draft.getType(p) == "Floor": height = p.Height if not height: height = 1 if obj.Normal == Vector(0, 0, 0): normal = Vector(0, 0, 1) else: normal = Vector(obj.Normal) # creating shape pl = obj.Placement norm = normal.multiply(height) base = None if obj.Base: if obj.Base.isDerivedFrom("Part::Feature"): base = obj.Base.Shape.copy() if base.Solids: pass elif base.Faces: base = base.extrude(normal) elif (len(base.Wires) == 1) and base.Wires[0].isClosed(): base = Part.Face(base.Wires[0]) base = base.extrude(normal) 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.makePolygon([v1, v2, v3, v4, v1]) base = Part.Face(base) base = base.extrude(normal) for app in obj.Additions: base = base.oldFuse(app.Shape) app.ViewObject.hide() # to be removed for hole in obj.Subtractions: cut = False if hasattr(hole, "Proxy"): if hasattr(hole.Proxy, "Subvolume"): if hole.Proxy.Subvolume: print "cutting subvolume", hole.Proxy.Subvolume base = base.cut(hole.Proxy.Subvolume) cut = True if not cut: if hasattr(obj, "Shape"): base = base.cut(hole.Shape) hole.ViewObject.hide() # to be removed if base: pts = self.getAxisPoints(obj) if pts: fsh = [] for p in pts: sh = base.copy() sh.translate(p) fsh.append(sh) obj.Shape = Part.makeCompound(fsh) else: obj.Shape = base if not fcgeo.isNull(pl): obj.Placement = pl
def alignToPointAndAxis_SVG(self, point, axis, offset=0): """Align the working plane to a point and an axis (vector). It aligns `u` and `v` based on the magnitude of the components of `axis`. Also set `weak` to `False`. Parameters ---------- point : Base::Vector3 The new `position` of the plane, adjusted by the `offset`. axis : Base::Vector3 A vector whose unit vector will be used as the new `axis` of the plane. The magnitudes of the `x`, `y`, `z` components of the axis determine the orientation of `u` and `v` of the plane. offset : float, optional Defaults to zero. A value which will be used to offset the plane in the direction of its `axis`. Cases ----- The `u` and `v` are always calculated the same * `u` is the cross product of the positive or negative of `axis` with a `reference vector`. :: u = [+1|-1] axis.cross(ref_vec) * `v` is `u` rotated 90 degrees around `axis`. Whether the `axis` is positive or negative, and which reference vector is used, depends on the absolute values of the `x`, `y`, `z` components of the `axis` unit vector. #. If `x > y`, and `y > z` The reference vector is +Z :: u = -1 axis.cross(+Z) #. If `y > z`, and `z >= x` The reference vector is +X. :: u = -1 axis.cross(+X) #. If `y >= x`, and `x > z` The reference vector is +Z. :: u = +1 axis.cross(+Z) #. If `x > z`, and `z >= y` The reference vector is +Y. :: u = +1 axis.cross(+Y) #. If `z >= y`, and `y > x` The reference vector is +X. :: u = +1 axis.cross(+X) #. otherwise The reference vector is +Y. :: u = -1 axis.cross(+Y) """ self.doc = FreeCAD.ActiveDocument self.axis = axis self.axis.normalize() ref_vec = Vector(0.0, 1.0, 0.0) if ((abs(axis.x) > abs(axis.y)) and (abs(axis.y) > abs(axis.z))): ref_vec = Vector(0.0, 0., 1.0) self.u = axis.negative().cross(ref_vec) self.u.normalize() self.v = DraftVecUtils.rotate(self.u, math.pi/2, self.axis) # projcase = "Case new" elif ((abs(axis.y) > abs(axis.z)) and (abs(axis.z) >= abs(axis.x))): ref_vec = Vector(1.0, 0.0, 0.0) self.u = axis.negative().cross(ref_vec) self.u.normalize() self.v = DraftVecUtils.rotate(self.u, math.pi/2, self.axis) # projcase = "Y>Z, View Y" elif ((abs(axis.y) >= abs(axis.x)) and (abs(axis.x) > abs(axis.z))): ref_vec = Vector(0.0, 0., 1.0) self.u = axis.cross(ref_vec) self.u.normalize() self.v = DraftVecUtils.rotate(self.u, math.pi/2, self.axis) # projcase = "ehem. XY, Case XY" elif ((abs(axis.x) > abs(axis.z)) and (abs(axis.z) >= abs(axis.y))): self.u = axis.cross(ref_vec) self.u.normalize() self.v = DraftVecUtils.rotate(self.u, math.pi/2, self.axis) # projcase = "X>Z, View X" elif ((abs(axis.z) >= abs(axis.y)) and (abs(axis.y) > abs(axis.x))): ref_vec = Vector(1.0, 0., 0.0) self.u = axis.cross(ref_vec) self.u.normalize() self.v = DraftVecUtils.rotate(self.u, math.pi/2, self.axis) # projcase = "Y>X, Case YZ" else: self.u = axis.negative().cross(ref_vec) self.u.normalize() self.v = DraftVecUtils.rotate(self.u, math.pi/2, self.axis) # projcase = "else" # spat_vec = self.u.cross(self.v) # spat_res = spat_vec.dot(axis) # FCC.PrintMessage(projcase + " spat Prod = " + str(spat_res) + "\n") offsetVector = Vector(axis) offsetVector.multiply(offset) self.position = point.add(offsetVector) self.weak = False
def execute(self,obj): "creates the panel shape" if self.clone(obj): return import Part, DraftGeomUtils # base tests if obj.Base: if obj.Base.isDerivedFrom("Part::Feature"): if obj.Base.Shape.isNull(): return elif obj.Base.isDerivedFrom("Mesh::Feature"): if not obj.Base.Mesh.isSolid(): return else: if obj.Length.Value: length = obj.Length.Value else: return if obj.Width.Value: width = obj.Width.Value else: return if obj.Thickness.Value: thickness = obj.Thickness.Value else: if not obj.Base: return elif obj.Base.isDerivedFrom("Part::Feature"): if not obj.Base.Shape.Solids: return # creating base shape pl = obj.Placement base = None normal = None if hasattr(obj,"Normal"): if obj.Normal.Length > 0: normal = Vector(obj.Normal) normal.normalize() normal.multiply(thickness) baseprofile = None if obj.Base: base = obj.Base.Shape.copy() if not base.Solids: p = FreeCAD.Placement(obj.Base.Placement) if base.Faces: baseprofile = base if not normal: normal = baseprofile.Faces[0].normalAt(0,0).multiply(thickness) base = base.extrude(normal) elif base.Wires: fm = False if hasattr(obj,"FaceMaker"): if obj.FaceMaker != "None": try: base = Part.makeFace(base.Wires,"Part::FaceMaker"+str(obj.FaceMaker)) fm = True except: FreeCAD.Console.PrintError(translate("Arch","Facemaker returned an error")+"\n") return if not fm: closed = True for w in base.Wires: if not w.isClosed(): closed = False if closed: baseprofile = ArchCommands.makeFace(base.Wires) if not normal: normal = baseprofile.normalAt(0,0).multiply(thickness) base = baseprofile.extrude(normal) elif obj.Base.isDerivedFrom("Mesh::Feature"): if obj.Base.Mesh.isSolid(): if obj.Base.Mesh.countComponents() == 1: sh = ArchCommands.getShapeFromMesh(obj.Base.Mesh) if sh.isClosed() and sh.isValid() and sh.Solids: base = sh else: if not normal: normal = Vector(0,0,1).multiply(thickness) l2 = length/2 or 0.5 w2 = width/2 or 0.5 v1 = Vector(-l2,-w2,0) v2 = Vector(l2,-w2,0) v3 = Vector(l2,w2,0) v4 = Vector(-l2,w2,0) base = Part.makePolygon([v1,v2,v3,v4,v1]) baseprofile = Part.Face(base) base = baseprofile.extrude(normal) if hasattr(obj,"Area"): if baseprofile: obj.Area = baseprofile.Area if hasattr(obj,"WaveLength"): if baseprofile and obj.WaveLength.Value and obj.WaveHeight.Value: # corrugated element bb = baseprofile.BoundBox bb.enlarge(bb.DiagonalLength) p1 = Vector(bb.getPoint(0).x,bb.getPoint(0).y,bb.Center.z) if obj.WaveType == "Curved": p2 = p1.add(Vector(obj.WaveLength.Value/2,0,obj.WaveHeight.Value)) p3 = p2.add(Vector(obj.WaveLength.Value/2,0,-obj.WaveHeight.Value)) e1 = Part.Arc(p1,p2,p3).toShape() p4 = p3.add(Vector(obj.WaveLength.Value/2,0,-obj.WaveHeight.Value)) p5 = p4.add(Vector(obj.WaveLength.Value/2,0,obj.WaveHeight.Value)) e2 = Part.Arc(p3,p4,p5).toShape() else: if obj.WaveHeight.Value < obj.WaveLength.Value: p2 = p1.add(Vector(obj.WaveHeight.Value,0,obj.WaveHeight.Value)) p3 = p2.add(Vector(obj.WaveLength.Value-2*obj.WaveHeight.Value,0,0)) p4 = p3.add(Vector(obj.WaveHeight.Value,0,-obj.WaveHeight.Value)) e1 = Part.makePolygon([p1,p2,p3,p4]) p5 = p4.add(Vector(obj.WaveHeight.Value,0,-obj.WaveHeight.Value)) p6 = p5.add(Vector(obj.WaveLength.Value-2*obj.WaveHeight.Value,0,0)) p7 = p6.add(Vector(obj.WaveHeight.Value,0,obj.WaveHeight.Value)) e2 = Part.makePolygon([p4,p5,p6,p7]) else: p2 = p1.add(Vector(obj.WaveLength.Value/2,0,obj.WaveHeight.Value)) p3 = p2.add(Vector(obj.WaveLength.Value/2,0,-obj.WaveHeight.Value)) e1 = Part.makePolygon([p1,p2,p3]) p4 = p3.add(Vector(obj.WaveLength.Value/2,0,-obj.WaveHeight.Value)) p5 = p4.add(Vector(obj.WaveLength.Value/2,0,obj.WaveHeight.Value)) e2 = Part.makePolygon([p3,p4,p5]) edges = [e1,e2] for i in range(int(bb.XLength/(obj.WaveLength.Value*2))): e1 = e1.copy() e1.translate(Vector(obj.WaveLength.Value*2,0,0)) e2 = e2.copy() e2.translate(Vector(obj.WaveLength.Value*2,0,0)) edges.extend([e1,e2]) basewire = Part.Wire(edges) baseface = basewire.extrude(Vector(0,bb.YLength,0)) base = baseface.extrude(Vector(0,0,thickness)) rot = FreeCAD.Rotation(FreeCAD.Vector(0,0,1),normal) base.rotate(bb.Center,rot.Axis,math.degrees(rot.Angle)) if obj.WaveDirection.Value: base.rotate(bb.Center,normal,obj.WaveDirection.Value) n1 = normal.negative().normalize().multiply(obj.WaveHeight.Value*2) self.vol = baseprofile.copy() self.vol.translate(n1) self.vol = self.vol.extrude(n1.negative().multiply(2)) base = self.vol.common(base) base = base.removeSplitter() if not base: FreeCAD.Console.PrintError(transpate("Arch","Error computing shape of ")+obj.Label+"\n") return False if base and (obj.Sheets > 1) and normal and thickness: bases = [base] for i in range(1,obj.Sheets): n = FreeCAD.Vector(normal).normalize().multiply(i*thickness) b = base.copy() b.translate(n) bases.append(b) base = Part.makeCompound(bases) if base and normal and hasattr(obj,"Offset"): if obj.Offset.Value: v = DraftVecUtils.scaleTo(normal,obj.Offset.Value) base.translate(v) # process subshapes base = self.processSubShapes(obj,base,pl) # applying if base: if not base.isNull(): if base.isValid() and base.Solids: if base.Volume < 0: base.reverse() if base.Volume < 0: FreeCAD.Console.PrintError(translate("Arch","Couldn't compute a shape")) return base = base.removeSplitter() obj.Shape = base if not pl.isNull(): obj.Placement = pl
def execute(self, obj): "creates the structure shape" import Part, DraftGeomUtils if self.clone(obj): return normal, length, width, height = self.getDefaultValues(obj) # creating base shape pl = obj.Placement base = None if obj.Base: if obj.Base.isDerivedFrom("Part::Feature"): if obj.Base.Shape.isNull(): return if not obj.Base.Shape.isValid(): if not obj.Base.Shape.Solids: # let pass invalid objects if they have solids... return if hasattr(obj, "Tool"): if obj.Tool: try: base = obj.Tool.Shape.copy().makePipe( obj.Base.Shape.copy()) except Part.OCCError: FreeCAD.Console.PrintError( translate( "Arch", "Error: The base shape couldn't be extruded along this tool object" )) return if not base: if not height: return if obj.Normal == Vector(0, 0, 0): if len(obj.Base.Shape.Faces) > 0: normal = obj.Base.Shape.Faces[0].normalAt(.5, .5) else: normal = DraftGeomUtils.getNormal(obj.Base.Shape) if not normal: normal = FreeCAD.Vector(0, 0, 1) #p = FreeCAD.Placement(obj.Base.Placement) #normal = p.Rotation.multVec(normal) else: normal = Vector(obj.Normal) normal = normal.multiply(height) base = obj.Base.Shape.copy() if base.Solids: pass elif base.Faces: base = base.extrude(normal) elif (len(base.Wires) == 1): if base.Wires[0].isClosed(): try: base = Part.Face(base.Wires[0]) base = base.extrude(normal) except Part.OCCError: FreeCAD.Console.PrintError( obj.Label + " : " + str( translate( "Arch", "Unable to extrude the base shape\n" ))) return elif obj.Base.isDerivedFrom("Mesh::Feature"): if obj.Base.Mesh.isSolid(): if obj.Base.Mesh.countComponents() == 1: sh = ArchCommands.getShapeFromMesh(obj.Base.Mesh) if sh.isClosed() and sh.isValid() and sh.Solids and ( not sh.isNull()): base = sh else: FreeCAD.Console.PrintWarning( str( translate( "Arch", "This mesh is an invalid solid"))) obj.Base.ViewObject.show() else: base = self.getProfiles(obj) if base: if length > height: normal = normal.multiply(length) else: normal = normal.multiply(height) base = Part.Face(base[0]) base = base.extrude(normal) base = self.processSubShapes(obj, base, pl) self.applyShape(obj, base, pl)
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) 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) 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 createGeometry(self,obj): import Part, DraftGeomUtils # getting default values height = width = length = 1 if hasattr(obj,"Length"): if obj.Length: length = obj.Length if hasattr(obj,"Width"): if obj.Width: width = obj.Width if hasattr(obj,"Height"): if obj.Height: height = obj.Height # creating base shape pl = obj.Placement base = None if obj.Base: if obj.Base.isDerivedFrom("Part::Feature"): if obj.Normal == Vector(0,0,0): p = FreeCAD.Placement(obj.Base.Placement) normal = p.Rotation.multVec(Vector(0,0,1)) else: normal = Vector(obj.Normal) normal = normal.multiply(height) base = obj.Base.Shape.copy() if base.Solids: pass elif base.Faces: base = base.extrude(normal) elif (len(base.Wires) == 1): if base.Wires[0].isClosed(): base = Part.Face(base.Wires[0]) base = base.extrude(normal) elif obj.Base.isDerivedFrom("Mesh::Feature"): if obj.Base.Mesh.isSolid(): if obj.Base.Mesh.countComponents() == 1: sh = ArchCommands.getShapeFromMesh(obj.Base.Mesh) if sh.isClosed() and sh.isValid() and sh.Solids: base = sh else: if obj.Normal == Vector(0,0,0): normal = Vector(0,0,1) else: normal = Vector(obj.Normal) normal = normal.multiply(height) l2 = length/2 or 0.5 w2 = width/2 or 0.5 v1 = Vector(-l2,-w2,0) v2 = Vector(l2,-w2,0) v3 = Vector(l2,w2,0) v4 = Vector(-l2,w2,0) base = Part.makePolygon([v1,v2,v3,v4,v1]) base = Part.Face(base) base = base.extrude(normal) base = self.processSubShapes(obj,base) if base: # applying axes pts = self.getAxisPoints(obj) apl = self.getAxisPlacement(obj) if pts: fsh = [] for i in range(len(pts)): if hasattr(obj,"Exclude"): if i in obj.Exclude: continue sh = base.copy() if apl: sh.Placement.Rotation = apl.Rotation sh.translate(pts[i]) fsh.append(sh) obj.Shape = Part.makeCompound(fsh) # finalizing else: if base: if not base.isNull(): if base.isValid() and base.Solids: if base.Volume < 0: base.reverse() if base.Volume < 0: FreeCAD.Console.PrintError(str(translate("Arch","Couldn't compute the wall shape"))) return base = base.removeSplitter() obj.Shape = base if not DraftGeomUtils.isNull(pl): obj.Placement = pl
def createGeometry(self,obj): "builds the wall shape" if not obj.Base: return import Part from draftlibs import fcgeo flat = False if hasattr(obj.ViewObject,"DisplayMode"): flat = (obj.ViewObject.DisplayMode == "Flat 2D") width = 1.0 if hasattr(obj,"Width"): if obj.Width: width = obj.Width def getbase(wire): "returns a full shape from a base wire" dvec = fcgeo.vec(wire.Edges[0]).cross(normal) dvec.normalize() if obj.Align == "Left": dvec = dvec.multiply(width) w2 = fcgeo.offsetWire(wire,dvec) w1 = Part.Wire(fcgeo.sortEdges(wire.Edges)) sh = fcgeo.bind(w1,w2) elif obj.Align == "Right": dvec = dvec.multiply(width) dvec = fcvec.neg(dvec) w2 = fcgeo.offsetWire(wire,dvec) w1 = Part.Wire(fcgeo.sortEdges(wire.Edges)) sh = fcgeo.bind(w1,w2) elif obj.Align == "Center": dvec = dvec.multiply(width/2) w1 = fcgeo.offsetWire(wire,dvec) dvec = fcvec.neg(dvec) w2 = fcgeo.offsetWire(wire,dvec) sh = fcgeo.bind(w1,w2) # fixing self-intersections sh.fix(0.1,0,1) if height and (not flat): norm = Vector(normal).multiply(height) sh = sh.extrude(norm) return sh pl = obj.Placement # getting default values height = normal = None if obj.Height: height = obj.Height else: for p in obj.InList: if Draft.getType(p) == "Floor": height = p.Height if not height: height = 1 if obj.Normal == Vector(0,0,0): normal = Vector(0,0,1) else: normal = Vector(obj.Normal) # computing shape base = None if obj.Base.isDerivedFrom("Part::Feature"): if not obj.Base.Shape.isNull(): base = obj.Base.Shape.copy() if base.Solids: pass elif base.Faces: if height: norm = normal.multiply(height) base = base.extrude(norm) elif base.Wires: temp = None for wire in obj.Base.Shape.Wires: sh = getbase(wire) if temp: temp = temp.fuse(sh) else: temp = sh base = temp base = base.removeSplitter() for app in obj.Additions: base = base.oldFuse(app.Shape) app.ViewObject.hide() #to be removed for hole in obj.Subtractions: if Draft.getType(hole) == "Window": # window if hole.Base and obj.Width: f = self.getSubVolume(hole.Base,width) base = base.cut(f) elif Draft.isClone(hole,"Window"): if hole.Objects[0].Base and width: f = self.getSubVolume(hole.Objects[0].Base,width,hole.Placement.Base) base = base.cut(f) elif hasattr(hole,"Shape"): if not hole.Shape.isNull(): base = base.cut(hole.Shape) hole.ViewObject.hide() # to be removed if base: obj.Shape = base if not fcgeo.isNull(pl): obj.Placement = pl