def update_object_from_edit_points(self, obj, node_idx, v, alt_edit_mode=0): """Update the object from modified Draft_Edit point. No need to recompute at the end. Parameters: obj: the object node_idx: number of the edited node v: target vector of the node in object local coordinates system alt_edit_mode: alternative edit mode to perform different operations (usually can be selected from the Draft_Edit context menu) default = 0 """ if alt_edit_mode == 0: # trim/extend endpoint if node_idx == 0: obj.Proxy.set_first_point(obj, v, local=True) elif node_idx == 1: obj.Proxy.set_last_point(obj, v, local=True) elif alt_edit_mode == 1: # rotate wall on the opposite endpoint (context menu "align") import Draft global_v = obj.getGlobalPlacement().multVec(v) p1 = obj.Proxy.get_first_point(obj) p2 = obj.Proxy.get_last_point(obj) if node_idx == 0: current_angle = DraftVecUtils.angle(App.Vector(1,0,0), p1.sub(p2)) new_angle = DraftVecUtils.angle(App.Vector(1,0,0), global_v.sub(p2)) Draft.rotate(obj, math.degrees(new_angle - current_angle), p2) # obj.Proxy.set_first_point(obj, global_v) # this causes frequent hard crashes, probably to delay elif node_idx == 1: current_angle = DraftVecUtils.angle(App.Vector(1,0,0), p2.sub(p1)) new_angle = DraftVecUtils.angle(App.Vector(1,0,0), global_v.sub(p1)) Draft.rotate(obj, math.degrees(new_angle - current_angle), p1)
def createObject(self): """Create the final object in the current document.""" plane = App.DraftWorkingPlane p1 = self.node[0] p3 = self.node[-1] diagonal = p3.sub(p1) p2 = p1.add(DraftVecUtils.project(diagonal, plane.v)) p4 = p1.add(DraftVecUtils.project(diagonal, plane.u)) length = p4.sub(p1).Length if abs(DraftVecUtils.angle(p4.sub(p1), plane.u, plane.axis)) > 1: length = -length height = p2.sub(p1).Length if abs(DraftVecUtils.angle(p2.sub(p1), plane.v, plane.axis)) > 1: height = -height try: # The command to run is built as a series of text strings # to be committed through the `draftutils.todo.ToDo` class. rot, sup, pts, fil = self.getStrings() base = p1 if length < 0: length = -length base = base.add((p1.sub(p4)).negative()) if height < 0: height = -height base = base.add((p1.sub(p2)).negative()) Gui.addModule("Draft") if utils.getParam("UsePartPrimitives", False): # Insert a Part::Primitive object _cmd = 'FreeCAD.ActiveDocument.' _cmd += 'addObject("Part::Plane", "Plane")' _cmd_list = [ 'plane = ' + _cmd, 'plane.Length = ' + str(length), 'plane.Width = ' + str(height), 'pl = FreeCAD.Placement()', 'pl.Rotation.Q=' + rot, 'pl.Base = ' + DraftVecUtils.toString(base), 'plane.Placement = pl', 'Draft.autogroup(plane)', 'FreeCAD.ActiveDocument.recompute()' ] self.commit(translate("draft", "Create Plane"), _cmd_list) else: _cmd = 'Draft.makeRectangle' _cmd += '(' _cmd += 'length=' + str(length) + ', ' _cmd += 'height=' + str(height) + ', ' _cmd += 'placement=pl, ' _cmd += 'face=' + fil + ', ' _cmd += 'support=' + sup _cmd += ')' _cmd_list = [ 'pl = FreeCAD.Placement()', 'pl.Rotation.Q = ' + rot, 'pl.Base = ' + DraftVecUtils.toString(base), 'rec = ' + _cmd, 'Draft.autogroup(rec)', 'FreeCAD.ActiveDocument.recompute()' ] self.commit(translate("draft", "Create Rectangle"), _cmd_list) except Exception: _err("Draft: error delaying commit") self.finish(cont=True)
def create(self): """Create the actual object.""" if len(self.node) == 3: targetpoint = self.node[0] basepoint = self.node[2] v = self.node[2].sub(self.node[1]) dist = v.Length if hasattr(App, "DraftWorkingPlane"): h = App.DraftWorkingPlane.u n = App.DraftWorkingPlane.axis r = App.DraftWorkingPlane.getRotation().Rotation else: h = App.Vector(1, 0, 0) n = App.Vector(0, 0, 1) r = App.Rotation() if abs(DraftVecUtils.angle(v, h, n)) <= math.pi / 4: direction = "Horizontal" dist = -dist elif abs(DraftVecUtils.angle(v, h, n)) >= math.pi * 3 / 4: direction = "Horizontal" elif DraftVecUtils.angle(v, h, n) > 0: direction = "Vertical" else: direction = "Vertical" dist = -dist tp = "targetpoint=FreeCAD." + str(targetpoint) + ", " sel = "" if self.sel: if self.sel.SubElementNames: sub = "'" + self.sel.SubElementNames[0] + "'" else: sub = "()" sel = "target=" sel += "(" sel += "FreeCAD.ActiveDocument." + self.sel.Object.Name + ", " sel += sub sel += ")," pl = "placement=FreeCAD.Placement" pl += "(" pl += "FreeCAD." + str(basepoint) + ", " pl += "FreeCAD.Rotation" + str(r.Q) pl += ")" App.ActiveDocument.openTransaction("Create Label") Gui.addModule("Draft") _cmd = "Draft.makeLabel" _cmd += "(" _cmd += tp _cmd += sel _cmd += "direction='" + direction + "', " _cmd += "distance=" + str(dist) + ", " _cmd += "labeltype='" + self.labeltype + "', " _cmd += pl _cmd += ")" Gui.doCommand("l = " + _cmd) Gui.doCommand("Draft.autogroup(l)") App.ActiveDocument.recompute() App.ActiveDocument.commitTransaction() self.finish()
def measure_two_obj_angles(link_sub_1, link_sub_2): """Measure two edges from two different objects to measure the angle. This function is a prototype because it does not determine all possible starting and ending angles that could be used to draw the dimension line, which is a circular arc. Parameters ---------- link_sub_1: tuple A tuple containing one object and a list of subelement strings, which may be empty. Only the first subelement is considered, which must be an edge. :: link_sub_1 = (obj1, ['EdgeN', ...]) link_sub_2: tuple Same. """ start = 0 end = 0 obj1 = link_sub_1[0] lsub1 = link_sub_1[1] obj2 = link_sub_2[0] lsub2 = link_sub_2[1] # The subelement list may be empty so we test it first # and pick only the first item if lsub1 and lsub2: subelement1 = lsub1[0] subelement2 = lsub2[0] if "Edge" in subelement1 and "Edge" in subelement2: n1 = int(subelement1[4:]) - 1 n2 = int(subelement2[4:]) - 1 start = obj1.Shape.Edges[n1].Curve.Direction end = obj2.Shape.Edges[n2].Curve.Direction # We get the angle from the direction of the line to the U axis # of the working plane; we should be able to also use the V axis start_r = DraftVecUtils.angle(start, App.DraftWorkingPlane.u) end_r = DraftVecUtils.angle(end, App.DraftWorkingPlane.u) start = math.degrees(start_r) end = math.degrees(end_r) # We make the angle positive because when tested, some errors # were produced in the code that calculates the 'Angle'. # This code is actually inside the viewprovider. if start < 0: start = abs(start) if end < 0: end = abs(end) return start, end
def getCurveSet(ent): result = [] if ent.is_a() in ["IfcGeometricCurveSet", "IfcGeometricSet"]: elts = ent.Elements elif ent.is_a() in [ "IfcLine", "IfcPolyline", "IfcCircle", "IfcTrimmedCurve" ]: elts = [ent] for el in elts: if el.is_a("IfcPolyline"): result.append(getPolyline(el)) elif el.is_a("IfcLine"): result.append(getLine(el)) elif el.is_a("IfcCircle"): result.append(getCircle(el)) elif el.is_a("IfcTrimmedCurve"): base = el.BasisCurve t1 = el.Trim1[0].wrappedValue t2 = el.Trim2[0].wrappedValue if not el.SenseAgreement: t1, t2 = t2, t1 if base.is_a("IfcPolyline"): bc = getPolyline(base) result.append(bc) elif base.is_a("IfcCircle"): bc = getCircle(base) e = Part.ArcOfCircle(bc.Curve, math.radians(t1), math.radians(t2)).toShape() d = base.Position.RefDirection.DirectionRatios v = FreeCAD.Vector(d[0], d[1], d[2] if len(d) > 2 else 0) a = -DraftVecUtils.angle(v) e.rotate(bc.Curve.Center, FreeCAD.Vector(0, 0, 1), math.degrees(a)) result.append(e) elif el.is_a("IfcCompositeCurve"): for base in el.Segments: if base.ParentCurve.is_a("IfcPolyline"): bc = getPolyline(base.ParentCurve) result.append(bc) elif base.ParentCurve.is_a("IfcCircle"): bc = getCircle(base.ParentCurve) e = Part.ArcOfCircle(bc.Curve, math.radians(t1), math.radians(t2)).toShape() d = base.Position.RefDirection.DirectionRatios v = FreeCAD.Vector(d[0], d[1], d[2] if len(d) > 2 else 0) a = -DraftVecUtils.angle(v) e.rotate(bc.Curve.Center, FreeCAD.Vector(0, 0, 1), math.degrees(a)) result.append(e) return result
def get_perpendicular_tuples(faces): listeCouple = [] lenfaces = len(faces) faces.append(faces[0]) for n in range(lenfaces): norm2 = faces[n + 1].normalAt(0, 0) norm1 = faces[n].normalAt(0, 0) norm0 = faces[n - 1].normalAt(0, 0) if abs(round(math.degrees(DraftVecUtils.angle(norm1, norm0)))) == 90.0: listeCouple.append([faces[n], faces[n - 1]]) if abs(round(math.degrees(DraftVecUtils.angle(norm1, norm2)))) == 90.0: listeCouple.append([faces[n], faces[n + 1]]) return listeCouple
def getCoupleFacesEquerre(self, faces): listeCouple = [] lenfaces = len(faces) faces.append(faces[0]) for n in range(lenfaces): norm2 = faces[n+1].normalAt(0,0) norm1 = faces[n].normalAt(0,0) norm0 = faces[n-1].normalAt(0,0) if abs(round(math.degrees(DraftVecUtils.angle(norm1,norm0)))) == 90.: listeCouple.append([faces[n],faces[n-1]]) if abs(round(math.degrees(DraftVecUtils.angle(norm1,norm2)))) == 90.: listeCouple.append([faces[n],faces[n+1]]) return listeCouple
def handle_mouse_move_event(self, arg): """Handle the mouse when moving.""" plane = App.DraftWorkingPlane for ghost in self.ghosts: ghost.off() self.point, ctrlPoint, info = gui_tool_utils.getPoint(self, arg) # this is to make sure radius is what you see on screen if self.center and DraftVecUtils.dist(self.point, self.center): viewdelta = DraftVecUtils.project(self.point.sub(self.center), plane.axis) if not DraftVecUtils.isNull(viewdelta): self.point = self.point.add(viewdelta.negative()) if self.extendedCopy: if not gui_tool_utils.hasMod(arg, gui_tool_utils.MODALT): self.step = 3 self.finish() if self.step == 0: pass elif self.step == 1: currentrad = DraftVecUtils.dist(self.point, self.center) if currentrad != 0: angle = DraftVecUtils.angle(plane.u, self.point.sub(self.center), plane.axis) else: angle = 0 self.ui.setRadiusValue(math.degrees(angle), unit="Angle") self.firstangle = angle self.ui.radiusValue.setFocus() self.ui.radiusValue.selectAll() elif self.step == 2: currentrad = DraftVecUtils.dist(self.point, self.center) if currentrad != 0: angle = DraftVecUtils.angle(plane.u, self.point.sub(self.center), plane.axis) else: angle = 0 if angle < self.firstangle: sweep = (2 * math.pi - self.firstangle) + angle else: sweep = angle - self.firstangle self.arctrack.setApertureAngle(sweep) for ghost in self.ghosts: ghost.rotate(plane.axis, sweep) ghost.on() self.ui.setRadiusValue(math.degrees(sweep), 'Angle') self.ui.radiusValue.setFocus() self.ui.radiusValue.selectAll() gui_tool_utils.redraw3DView()
def T_join(self, wall, target, idx): """ Compute wall angles according to given parameters """ print("--------\n"+"T_Join "+wall.Name + " with " +target.Name+ "\n") if idx == 0: w1 = wall.Proxy.get_first_point(wall) w2 = wall.Proxy.get_last_point(wall) elif idx == 1: w1 = wall.Proxy.get_last_point(wall) w2 = wall.Proxy.get_first_point(wall) t1 = target.Proxy.get_first_point(target) t2 = target.Proxy.get_last_point(target) angle = math.degrees(DraftVecUtils.angle(w2-w1,t2-t1)) # print(angle) # identify if the function have to join the first or the end of the wall if idx == 0: wall.FirstCoreInnerAngle = angle wall.FirstCoreOuterAngle = -angle wall.FirstCoreOffset = - abs(target.Width / 2 / math.cos(math.pi / 2 - math.radians(angle))) elif idx == 1: wall.LastCoreInnerAngle = -angle wall.LastCoreOuterAngle = angle wall.LastCoreOffset = - abs(target.Width / 2 / math.cos(math.pi / 2 - math.radians(angle))) if not wall.Name in target.IncomingTJoins: target_list = target.IncomingTJoins target_list.append(wall.Name) target.IncomingTJoins = target_list
def getIncidentAngle(self, queue): global currLocation '''returns in the incident angle in radians between the current and previous moves''' # get the vector of the last move if queue[1].Name in arccommands: print queue print currLocation arcLoc = FreeCAD.Base.Vector(queue[2].X + queue[1].I, queue[2].Y + queue[1].J, currLocation['Z']) radvector = queue[1].Placement.Base.sub(arcLoc) # vector of chord from center to point # vector of line perp to chord. v1 = radvector.cross(FreeCAD.Base.Vector(0, 0, 1)) else: v1 = queue[1].Placement.Base.sub(queue[2].Placement.Base) # get the vector of the current move if queue[0].Name in arccommands: arcLoc = FreeCAD.Base.Vector((queue[1].x + queue[0].I), (queue[1].y + queue[0].J), currLocation['Z']) radvector = queue[1].Placement.Base.sub(arcLoc) # calculate arcangle v2 = radvector.cross(FreeCAD.Base.Vector(0, 0, 1)) # if switching between G2 and G3, reverse orientation if queue[1].Name in arccommands: if queue[0].Name != queue[1].Name: v2 = D.rotate2D(v2, math.radians(180)) else: v2 = queue[0].Placement.Base.sub(queue[1].Placement.Base) incident_angle = D.angle(v1, v2, FreeCAD.Base.Vector(0, 0, -1)) return incident_angle
def orientEdge(edge, normal=None, make_arc=False): """Re-orient the edge such that it is in the XY plane. Re-orients `edge` such that it is in the XY plane. If `normal` is passed, this is used as the basis for the rotation, otherwise the placement of `edge` is used. """ # This 'normalizes' the placement to the xy plane edge = edge.copy() xyDir = App.Vector(0, 0, 1) base = App.Vector(0, 0, 0) if normal: angle = DraftVecUtils.angle(normal, xyDir) * App.Units.Radian axis = normal.cross(xyDir) else: axis = edge.Placement.Rotation.Axis angle = -1 * edge.Placement.Rotation.Angle * App.Units.Radian if axis == App.Vector(0.0, 0.0, 0.0): axis = App.Vector(0.0, 0.0, 1.0) if angle: edge.rotate(base, axis, angle) if isinstance(edge.Curve, Part.Line): return Part.LineSegment(edge.Curve, edge.FirstParameter, edge.LastParameter) elif make_arc and isinstance(edge.Curve, Part.Circle) and not edge.Closed: return Part.ArcOfCircle(edge.Curve, edge.FirstParameter, edge.LastParameter, edge.Curve.Axis.z > 0) elif make_arc and isinstance(edge.Curve, Part.Ellipse) and not edge.Closed: return Part.ArcOfEllipse(edge.Curve, edge.FirstParameter, edge.LastParameter, edge.Curve.Axis.z > 0) return edge.Curve
def getAngle(self, pt): "returns the angle of a given vector" c = self.trans.translation.getValue() center = Vector(c[0], c[1], c[2]) base = FreeCAD.DraftWorkingPlane.u rad = pt.sub(center) return (DraftVecUtils.angle(rad, base, FreeCAD.DraftWorkingPlane.axis))
def getDeviation(self): """Return the angle between the u axis and the horizontal plane. It defines a projection of `u` on the horizontal plane (without a Z component), and then measures the angle between this projection and `u`. It also considers the cross product of the projection and `u` to determine the sign of the angle. Returns ------- float Angle between the `u` vector, and a projected vector on the global horizontal plane. See also -------- DraftVecUtils.angle """ proj = Vector(self.u.x, self.u.y, 0) if self.u.getAngle(proj) == 0: return 0 else: norm = proj.cross(self.u) return DraftVecUtils.angle(self.u, proj, norm)
def makeWallFromPoints(p1, p2, width=None, height=None, align="Center", name="Wall"): '''makeWall([obj],[length],[width],[height],[align],[face],[name]): creates a wall based on the given object, which can be a sketch, a draft object, a face or a solid, or no object at all, then you must provide length, width and height. Align can be "Center","Left" or "Right", face can be an index number of a face in the base object to base the wall on.''' if not App.ActiveDocument: App.Console.PrintError("No active document. Aborting\n") return # Add a Wall object to the docume # TODO: verify if it's necessary to split this in 2 to have the 'if App.GuiUp:' # guard for the viewprovider obj = App.ActiveDocument.addObject('Part::FeaturePython', 'Wall', Wall(), ViewProviderWall(), True) # Align the wall to the given point obj.Placement.Base = p1 length = p1.distanceToPoint(p2) angle = DraftVecUtils.angle(p2 - p1, App.Vector(1, 0, 0)) obj.Placement.Rotation.Angle = -angle obj.AxisLastPointX = length # Set the wall properties obj.Width = width obj.Height = height App.ActiveDocument.recompute() return obj
def getRoundEdgeSVG(edge, view_plane, stroke_width, stroke_color): """getRoundEdgeSVG(Edge, ViewPlane, StrokeWidth, StrokeColor): Returns round corner edge svg with given radius. """ p1 = getProjectionToSVGPlane(edge.Vertexes[0].Point, view_plane) p2 = getProjectionToSVGPlane(edge.Vertexes[1].Point, view_plane) t1 = edge.tangentAt(edge.FirstParameter) t2 = edge.tangentAt(edge.FirstParameter + (edge.LastParameter - edge.FirstParameter) / 10) flag_sweep = int(DraftVecUtils.angle(t1, t2, view_plane.axis) < 0) radius = edge.Curve.Radius svg = ElementTree.Element("path") svg.set("style", "stroke:{};fill:none".format(stroke_color)) svg.set( "d", "M{x1} {y1} A{radius} {radius} 0 0 {flag_sweep} {x2} {y2}".format( x1=round(p1.x), y1=round(p1.y), x2=round(p2.x), y2=round(p2.y), radius=round(radius), flag_sweep=flag_sweep, ), ) svg.set("stroke-width", str(stroke_width)) svg.set("stroke", stroke_color) return svg
def isClockwise(edge, ref=None): """Return True if a circle-based edge has a clockwise direction.""" if not geomType(edge) == "Circle": return True v1 = edge.Curve.tangent(edge.ParameterRange[0])[0] if DraftVecUtils.isNull(v1): return True # we take an arbitrary other point on the edge that has little chances # to be aligned with the first one v2 = edge.Curve.tangent(edge.ParameterRange[0] + 0.01)[0] n = edge.Curve.Axis # if that axis points "the wrong way" from the reference, we invert it if not ref: ref = FreeCAD.Vector(0, 0, 1) if n.getAngle(ref) > math.pi / 2: n = n.negative() if DraftVecUtils.angle(v1, v2, n) < 0: return False if n.z < 0: return False return True
def getAngle(self,pt): "returns the angle of a given vector" c = self.trans.translation.getValue() center = Vector(c[0],c[1],c[2]) base = FreeCAD.DraftWorkingPlane.u rad = pt.sub(center) return(DraftVecUtils.angle(rad,base,FreeCAD.DraftWorkingPlane.axis))
def angleBisection(edge1, edge2): """Return an edge that bisects the angle between the 2 straight edges.""" if geomType(edge1) != "Line" or geomType(edge2) != "Line": return None p1 = edge1.Vertexes[0].Point p2 = edge1.Vertexes[-1].Point p3 = edge2.Vertexes[0].Point p4 = edge2.Vertexes[-1].Point intersect = findIntersection(edge1, edge2, True, True) if intersect: line1Dir = p2.sub(p1) angleDiff = DraftVecUtils.angle(line1Dir, p4.sub(p3)) ang = angleDiff * 0.5 origin = intersect[0] line1Dir.normalize() direction = DraftVecUtils.rotate(line1Dir, ang) else: diff = p3.sub(p1) origin = p1.add(diff.multiply(0.5)) direction = p2.sub(p1) direction.normalize() return Part.LineSegment(origin, origin.add(direction)).toShape()
def calcEdgeGeometry(self, edges, i): self.profilsDico[i]["edge"] = edges[i] vec = edges[i].Vertexes[-1].Point.sub(edges[i].Vertexes[0].Point) self.profilsDico[i]["vec"] = vec rot = math.degrees(DraftVecUtils.angle(vec)) self.profilsDico[i]["rot"] = rot
def isRoundCornerInSVG(edge, radius, view_plane, svg): """isRoundCornerInSVG(Edge, Radius, ViewPlane. SVG): Returns True if svg corresponding to round corner edge is present in SVG element, False otherwise. """ p1 = getProjectionToSVGPlane(edge.Vertexes[0].Point, view_plane) p2 = getProjectionToSVGPlane(edge.Vertexes[1].Point, view_plane) t1 = edge.tangentAt(edge.FirstParameter) t2 = edge.tangentAt(edge.FirstParameter + (edge.LastParameter - edge.FirstParameter) / 10) flag_sweep = int(DraftVecUtils.angle(t1, t2, view_plane.axis) < 0) if (svg.find( './/path[@d="M{x1} {y1} A{radius} {radius} 0 0 {flag_sweep} {x2} ' '{y2}"]'.format( x1=round(p1.x), y1=round(p1.y), x2=round(p2.x), y2=round(p2.y), radius=round(radius), flag_sweep=flag_sweep, )) is not None): return True elif (svg.find( './/path[@d="M{x1} {y1} A{radius} {radius} 0 0 {flag_sweep} {x2} ' '{y2}"]'.format( x1=round(p2.x), y1=round(p2.y), x2=round(p1.x), y2=round(p1.y), radius=round(radius), flag_sweep=not flag_sweep, )) is not None): return True else: return False
def segmentAngleXY(self, prevCommand, currCommand, endpos=False, currentZ=0): '''returns in the starting angle in radians for a Path command. requires the previous command in order to calculate arcs correctly if endpos = True, return the angle at the end of the segment.''' global arccommands if currCommand.Name in arccommands: arcLoc = FreeCAD.Vector((prevCommand.x + currCommand.I), (prevCommand.y + currCommand.J), currentZ) if endpos is True: radvector = arcLoc.sub(currCommand.Placement.Base ) #Calculate vector at start of arc else: radvector = arcLoc.sub(prevCommand.Placement.Base ) #Calculate vector at end of arc v1 = radvector.cross(FreeCAD.Vector(0, 0, 1)) if currCommand.Name in ["G2", "G02"]: v1 = D.rotate2D(v1, math.radians(180)) else: v1 = currCommand.Placement.Base.sub( prevCommand.Placement.Base) #Straight segments are easy myAngle = D.angle(v1, FreeCAD.Base.Vector(1, 0, 0), FreeCAD.Base.Vector(0, 0, -1)) return myAngle
def rotateSel(sel, n): p = sel.Object.Placement loc = sel.Object.Placement.Base r = axis.cross(n) # rotation axis a = DraftVecUtils.angle(n, axis, r) * 180 / math.pi PathLog.debug("oh boy: (%.2f, %.2f, %.2f) -> %.2f" % (r.x, r.y, r.z, a)) Draft.rotate(sel.Object, a, axis=r)
def circleFrom2LinesRadius(edge1, edge2, radius): """Retun a list of circles from two edges and one radius. It calculates 4 centers. """ intsec = findIntersection(edge1, edge2, True, True) if not intsec: return None intsec = intsec[0] bis12 = angleBisection(edge1, edge2) bis21 = Part.LineSegment(bis12.Vertexes[0].Point, DraftVecUtils.rotate(vec(bis12), math.pi/2.0)) ang12 = abs(DraftVecUtils.angle(vec(edge1), vec(edge2))) ang21 = math.pi - ang12 dist12 = radius / math.sin(ang12 * 0.5) dist21 = radius / math.sin(ang21 * 0.5) circles = [] cen = App.Vector.add(intsec, vec(bis12).multiply(dist12)) circles.append(Part.Circle(cen, NORM, radius)) cen = App.Vector.add(intsec, vec(bis12).multiply(-dist12)) circles.append(Part.Circle(cen, NORM, radius)) cen = App.Vector.add(intsec, vec(bis21).multiply(dist21)) circles.append(Part.Circle(cen, NORM, radius)) cen = App.Vector.add(intsec, vec(bis21).multiply(-dist21)) circles.append(Part.Circle(cen, NORM, radius)) return circles
def calcEdgeGeometry(self, i, edge): profilCurr = self.profilsDico[i] profilCurr["edge"] = edge vec = edge.Vertexes[1].Point.sub(edge.Vertexes[0].Point) profilCurr["vec"] = vec rot = math.degrees(DraftVecUtils.angle(vec)) profilCurr["rot"] = rot
def lineExtension(self, obj, queue): '''returns gcode for line extension''' global currLocation offset = float(obj.offset) results = [] v1 = queue[1].Placement.Base.sub(queue[2].Placement.Base) # extend the current segment to comp for offset segAngle = D.angle(v1, FreeCAD.Base.Vector(1, 0, 0), FreeCAD.Base.Vector(0, 0, -1)) xoffset = math.cos(segAngle) * offset yoffset = math.sin(segAngle) * offset newX = currLocation["X"] + xoffset newY = currLocation["Y"] + yoffset extendcommand = Path.Command('G1', {"X": newX, "Y": newY}) results.append(extendcommand) currLocation.update(extendcommand.Parameters) replace = None return (results, replace)
def FSMoveToObject(ScrewObj_m, attachToObject, invert, offset): Pnt1 = None Axis1 = None Axis2 = None s = attachToObject if hasattr(s, "Curve"): if hasattr(s.Curve, "Center"): Pnt1 = s.Curve.Center Axis1 = s.Curve.Axis if hasattr(s, 'Surface'): #print 'the object is a face!' if hasattr(s.Surface, 'Axis'): Axis1 = s.Surface.Axis if hasattr(s, 'Point'): FreeCAD.Console.PrintLog("the object seems to be a vertex! " + str(s.Point) + "\n") Pnt1 = s.Point if (Axis1 != None): if invert: Axis1 = Base.Vector(0, 0, 0) - Axis1 Pnt1 = Pnt1 + Axis1 * offset #FreeCAD.Console.PrintLog( "Got Axis1: " + str(Axis1) + "\n") Axis2 = Base.Vector(0.0, 0.0, 1.0) Axis2_minus = Base.Vector(0.0, 0.0, -1.0) # Calculate angle if Axis1 == Axis2: normvec = Base.Vector(1.0, 0.0, 0.0) result = 0.0 else: if Axis1 == Axis2_minus: normvec = Base.Vector(1.0, 0.0, 0.0) result = math.pi else: normvec = Axis1.cross( Axis2) # Berechne Achse der Drehung = normvec normvec.normalize() # Normalisieren fuer Quaternionenrechnung #normvec_rot = normvec result = DraftVecUtils.angle(Axis1, Axis2, normvec) # Winkelberechnung sin_res = math.sin(result / 2.0) cos_res = math.cos(result / 2.0) normvec.multiply(-sin_res) # Berechnung der Quaternionen-Elemente #FreeCAD.Console.PrintLog( "Winkel = "+ str(math.degrees(result)) + "\n") #FreeCAD.Console.PrintLog("Normalvektor: "+ str(normvec) + "\n") pl = FreeCAD.Placement() pl.Rotation = (normvec.x, normvec.y, normvec.z, cos_res ) #Drehungs-Quaternion #FreeCAD.Console.PrintLog("pl mit Rot: "+ str(pl) + "\n") ScrewObj_m.Placement = FreeCAD.Placement() ScrewObj_m.Placement.Rotation = pl.Rotation.multiply( ScrewObj_m.Placement.Rotation) ScrewObj_m.Placement.move(Pnt1)
def getDeviation(self): "returns the deviation angle between the u axis and the horizontal plane" proj = Vector(self.u.x,self.u.y,0) if self.u.getAngle(proj) == 0: return 0 else: norm = proj.cross(self.u) return DraftVecUtils.angle(self.u,proj,norm)
def getAngle(self,pt): "returns the angle of a given vector" c = self.trans.translation.getValue() center = Vector(c[0],c[1],c[2]) rad = pt.sub(center) a = DraftVecUtils.angle(rad,self.basevector,self.normal) #print(a) return(a)
def getDeviation(self): "returns the deviation angle between the u axis and the horizontal plane" proj = Vector(self.u.x, self.u.y, 0) if self.u.getAngle(proj) == 0: return 0 else: norm = proj.cross(self.u) return DraftVecUtils.angle(self.u, proj, norm)
def addCircle(radius, placement=None, face=None, startangle=None, endangle=None, support=None): import Part, DraftGeomUtils if placement: typecheck([(placement, FreeCAD.Placement)], "makeCircle") if startangle != endangle: n = "Arc" else: n = "Circle" obj = FreeCAD.ActiveDocument.addObject("Part::Part2DObjectPython", n) _Circle(obj) if face != None: obj.MakeFace = face if isinstance(radius, Part.Edge): edge = radius if DraftGeomUtils.geomType(edge) == "Circle": obj.Radius = edge.Curve.Radius placement = FreeCAD.Placement(edge.Placement) delta = edge.Curve.Center.sub(placement.Base) placement.move(delta) if len(edge.Vertexes) > 1: ref = placement.multVec(FreeCAD.Vector(1, 0, 0)) v1 = (edge.Vertexes[0].Point).sub(edge.Curve.Center) v2 = (edge.Vertexes[-1].Point).sub(edge.Curve.Center) a1 = -math.degrees(DraftVecUtils.angle(v1, ref)) a2 = -math.degrees(DraftVecUtils.angle(v2, ref)) obj.FirstAngle = a1 obj.LastAngle = a2 else: obj.Radius = radius if (startangle != None) and (endangle != None): if startangle == -0: startangle = 0 obj.FirstAngle = startangle obj.LastAngle = endangle obj.Support = support if placement: obj.Placement = placement if gui: _ViewProviderDraft(obj.ViewObject) formatObject(obj) select(obj) FreeCAD.ActiveDocument.recompute() return obj
def getCubicDimensions(shape): """getCubicDimensions(shape): returns a list containing the placement, the length, the width and the height of a cubic shape. If not cubic, nothing is returned. The placement point is the lowest corner of the shape.""" if not isCubic(shape): return None # determine lowest face, which will be our base z = [10, 1000000000000] for i in range(len(shape.Faces)): if shape.Faces[i].CenterOfMass.z < z[1]: z = [i, shape.Faces[i].CenterOfMass.z] if z[0] > 5: return None base = shape.Faces[z[0]] basepoint = base.Edges[0].Vertexes[0].Point plpoint = base.CenterOfMass basenorm = base.normalAt(0.5, 0.5) # getting length and width vx = vec(base.Edges[0]) vy = vec(base.Edges[1]) # getting rotations rotZ = DraftVecUtils.angle(vx) rotY = DraftVecUtils.angle(vx, FreeCAD.Vector(vx.x, vx.y, 0)) rotX = DraftVecUtils.angle(vy, FreeCAD.Vector(vy.x, vy.y, 0)) # getting height vz = None rpi = round(math.pi / 2, precision()) for i in range(1, 6): for e in shape.Faces[i].Edges: if basepoint in [e.Vertexes[0].Point, e.Vertexes[1].Point]: vtemp = vec(e) # print(vtemp) if round(vtemp.getAngle(vx), precision()) == rpi: if round(vtemp.getAngle(vy), precision()) == rpi: vz = vtemp if not vz: return None mat = FreeCAD.Matrix() mat.move(plpoint) mat.rotateX(rotX) mat.rotateY(rotY) mat.rotateZ(rotZ) return [ FreeCAD.Placement(mat), round(vx.Length, precision()), round(vy.Length, precision()), round(vz.Length, precision()) ]
def FSMoveToObject(ScrewObj_m, attachToObject, invert, offset): Pnt1 = None Axis1 = None Axis2 = None s = attachToObject if hasattr(s,"Curve"): if hasattr(s.Curve,"Center"): Pnt1 = s.Curve.Center Axis1 = s.Curve.Axis if hasattr(s,'Surface'): #print 'the object is a face!' if hasattr(s.Surface,'Axis'): Axis1 = s.Surface.Axis if hasattr(s,'Point'): FreeCAD.Console.PrintLog( "the object seems to be a vertex! "+ str(s.Point) + "\n") Pnt1 = s.Point if (Axis1 != None): if invert: Axis1 = Base.Vector(0,0,0) - Axis1 Pnt1 = Pnt1 + Axis1 * offset #FreeCAD.Console.PrintLog( "Got Axis1: " + str(Axis1) + "\n") Axis2 = Base.Vector(0.0,0.0,1.0) Axis2_minus = Base.Vector(0.0,0.0,-1.0) # Calculate angle if Axis1 == Axis2: normvec = Base.Vector(1.0,0.0,0.0) result = 0.0 else: if Axis1 == Axis2_minus: normvec = Base.Vector(1.0,0.0,0.0) result = math.pi else: normvec = Axis1.cross(Axis2) # Berechne Achse der Drehung = normvec normvec.normalize() # Normalisieren fuer Quaternionenrechnung #normvec_rot = normvec result = DraftVecUtils.angle(Axis1, Axis2, normvec) # Winkelberechnung sin_res = math.sin(result/2.0) cos_res = math.cos(result/2.0) normvec.multiply(-sin_res) # Berechnung der Quaternionen-Elemente #FreeCAD.Console.PrintLog( "Winkel = "+ str(math.degrees(result)) + "\n") #FreeCAD.Console.PrintLog("Normalvektor: "+ str(normvec) + "\n") pl = FreeCAD.Placement() pl.Rotation = (normvec.x,normvec.y,normvec.z,cos_res) #Drehungs-Quaternion #FreeCAD.Console.PrintLog("pl mit Rot: "+ str(pl) + "\n") #neuPlatz = Part2.Object.Placement.multiply(pl) ScrewObj_m.Placement = FreeCAD.Placement() neuPlatz = ScrewObj_m.Placement #FreeCAD.Console.PrintLog("die Position "+ str(neuPlatz) + "\n") neuPlatz.Rotation = pl.Rotation.multiply(ScrewObj_m.Placement.Rotation) neuPlatz.move(Pnt1)
def lineTwist(self, obj, queue, lastXY, twistCW=False): '''returns gcode to do an arc move toward a line to perform a corner action twist. Includes lifting and plungeing the knife''' global currLocation pivotheight = obj.pivotheight offset = obj.offset results = [] # set the correct twist command if twistCW is False: arcdir = "G3" else: arcdir = "G2" # move to pivot height zdepth = currLocation["Z"] retract = Path.Command("G0", {"Z": pivotheight}) results.append(retract) currLocation.update(retract.Parameters) C = queue[1].Placement.Base # get the vectors between endpoints to calculate twist v2 = queue[0].Placement.Base.sub(queue[1].Placement.Base) # calc arc endpoints to twist to segAngle = D.angle(v2, FreeCAD.Base.Vector(1, 0, 0), FreeCAD.Base.Vector(0, 0, -1)) xoffset = math.cos(segAngle) * offset yoffset = math.sin(segAngle) * offset newX = queue[1].x + xoffset newY = queue[1].y + yoffset offsetvector = C.sub(lastXY) I = offsetvector.x J = offsetvector.y # add the arc move arcmove = Path.Command(arcdir, { "X": newX, "Y": newY, "I": I, "J": J }) # add G2/G3 move results.append(arcmove) currLocation.update(arcmove.Parameters) # plunge back to depth plunge = Path.Command("G1", {"Z": zdepth}) results.append(plunge) currLocation.update(plunge.Parameters) replace = None return (results, replace)
def getRotation(v1, v2=App.Vector(0, 0, 1)): """Get the rotation Quaternion between 2 vectors.""" if (v1.dot(v2) > 0.999999) or (v1.dot(v2) < -0.999999): # vectors are opposite return None axis = v1.cross(v2) axis.normalize() # angle = math.degrees(math.sqrt(v1.Length^2 * v2.Length^2) + v1.dot(v2)) angle = math.degrees(DraftVecUtils.angle(v1, v2, axis)) return App.Rotation(axis, angle)
def shapeAnalyse(self, name, shape): ## Create a new object with the shape of the current arch object ## His placment is set to 0,0,0 obj = FreeCAD.ActiveDocument.addObject('Part::Feature', name) obj.Shape = shape obj.Placement.Base = FreeCAD.Vector(0.0, 0.0, 0.0) obj.Placement.Rotation = FreeCAD.Rotation( FreeCAD.Vector(0.0, 0.0, 1.0), 0.0) FreeCAD.ActiveDocument.recompute() ## Get the face to align with XY plane faces = obj.Shape.Faces facesMax = self.getFacesMax(faces) coupleEquerre = self.getCoupleFacesEquerre(facesMax) ## Get the normal of this face nv1 = coupleEquerre[0][0].normalAt(0, 0) ## Get the goal normal vector zv = Vector(0, 0, 1) ## Find and apply a rotation to the object to align face pla = obj.Placement rot = pla.Rotation rot1 = Rotation(nv1, zv) newrot = rot.multiply(rot1) pla.Rotation = newrot ## Get the face to align with XY plane faces = obj.Shape.Faces facesMax = self.getFacesMax(faces) coupleEquerre = self.getCoupleFacesEquerre(facesMax) ## Get the longest edge from aligned face maxLength = 0. for e in coupleEquerre[0][0].Edges: if e.Length > maxLength: maxLength = e.Length edgeMax = e ## Get the angle between edge and X axis and rotate object vec = DraftGeomUtils.vec(edgeMax) vecZ = FreeCAD.Vector(vec[0], vec[1], 0.0) pos2 = obj.Placement.Base rotZ = math.degrees( DraftVecUtils.angle(vecZ, FreeCAD.Vector(1.0, 0.0, 0.0), zv)) Draft.rotate([obj], rotZ, pos2, axis=zv, copy=False) bb = obj.Shape.BoundBox movex = bb.XMin * -1 movey = bb.YMin * -1 movez = bb.ZMin * -1 Draft.move([obj], FreeCAD.Vector(movex, movey, movez)) FreeCAD.ActiveDocument.recompute() ## Get the boundbox analyse = [ obj.Shape.BoundBox.YLength, obj.Shape.BoundBox.ZLength, obj.Shape.BoundBox.XLength ] if not "Shape" in self.export: FreeCAD.ActiveDocument.removeObject(name) return analyse
def lineTwist(self, obj, queue, lastXY, twistCW=False): '''returns gcode to do an arc move toward a line to perform a corner action twist. Includes lifting and plungeing the knife''' global currLocation pivotheight = obj.pivotheight offset = obj.offset results = [] # set the correct twist command if twistCW is False: arcdir = "G3" else: arcdir = "G2" # move to pivot height zdepth = currLocation["Z"] retract = Path.Command("G0", {"Z": pivotheight}) results.append(retract) currLocation.update(retract.Parameters) C = queue[1].Placement.Base # get the vectors between endpoints to calculate twist v2 = queue[0].Placement.Base.sub(queue[1].Placement.Base) # calc arc endpoints to twist to segAngle = D.angle(v2, FreeCAD.Base.Vector(1, 0, 0), FreeCAD.Base.Vector(0, 0, -1)) xoffset = math.cos(segAngle) * offset yoffset = math.sin(segAngle) * offset newX = queue[1].x + xoffset newY = queue[1].y + yoffset offsetvector = C.sub(lastXY) I = offsetvector.x J = offsetvector.y # add the arc move arcmove = Path.Command( arcdir, {"X": newX, "Y": newY, "I": I, "J": J}) # add G2/G3 move results.append(arcmove) currLocation.update(arcmove.Parameters) # plunge back to depth plunge = Path.Command("G1", {"Z": zdepth}) results.append(plunge) currLocation.update(plunge.Parameters) replace = None return (results, replace)
def shapeAnalyse(self, name, shape): ## Create a new object with the shape of the current arch object ## His placment is set to 0,0,0 obj = FreeCAD.ActiveDocument.addObject('Part::Feature',name) obj.Shape=shape obj.Placement.Base = FreeCAD.Vector(0.0,0.0,0.0) obj.Placement.Rotation = FreeCAD.Rotation(FreeCAD.Vector(0.0,0.0,1.0),0.0) FreeCAD.ActiveDocument.recompute() ## Get the face to align with XY plane faces = obj.Shape.Faces facesMax = self.getFacesMax(faces) coupleEquerre = self.getCoupleFacesEquerre(facesMax) ## Get the normal of this face nv1 = coupleEquerre[0][0].normalAt(0,0) ## Get the goal normal vector zv = Vector(0,0,1) ## Find and apply a rotation to the object to align face pla = obj.Placement rot = pla.Rotation rot1 = Rotation(nv1, zv) newrot = rot.multiply(rot1) pla.Rotation = newrot ## Get the face to align with XY plane faces = obj.Shape.Faces facesMax = self.getFacesMax(faces) coupleEquerre = self.getCoupleFacesEquerre(facesMax) ##Â Get the longest edge from aligned face maxLength = 0. for e in coupleEquerre[0][0].Edges: if e.Length > maxLength: maxLength = e.Length edgeMax = e ## Get the angle between edge and X axis and rotate object vec = DraftGeomUtils.vec(edgeMax) vecZ = FreeCAD.Vector(vec[0],vec[1],0.0) pos2 = obj.Placement.Base rotZ = math.degrees(DraftVecUtils.angle(vecZ,FreeCAD.Vector(1.0,0.0,0.0),zv)) Draft.rotate([obj],rotZ,pos2,axis=zv,copy=False) bb = obj.Shape.BoundBox movex = bb.XMin*-1 movey = bb.YMin*-1 movez = bb.ZMin*-1 Draft.move([obj], FreeCAD.Vector(movex, movey, movez)) FreeCAD.ActiveDocument.recompute() ## Get the boundbox analyse = [obj.Shape.BoundBox.YLength, obj.Shape.BoundBox.ZLength, obj.Shape.BoundBox.XLength] if not "Shape" in self.export : FreeCAD.ActiveDocument.removeObject(name) return analyse
def accept(self): self.recupererDonnees() distancesListe = [] if self.objetDebut: distancesListe.append(self.decalageDebut) if self.plEspaceRestant == 0: distancesListe.append(self.espaceRestant) if self.plEspaceRestant == 1: distancesListe.append(self.ecartementRegulier-self.decalageDebut) if self.plEspaceRestant == 2: distancesListe.append(self.espaceRestant/2-self.decalageDebut) for i in range(self.qteEcartement-2): distancesListe.append(self.ecartementRegulier) if self.objetFin: if self.plEspaceRestant == 0: distancesListe.append(self.ecartementRegulier-self.decalageFin-self.decalageDebut) if self.plEspaceRestant == 1: distancesListe.append(self.espaceRestant-self.decalageFin) if self.plEspaceRestant == 2: distancesListe.append(self.ecartementRegulier) distancesListe.append((self.espaceRestant/2)-self.decalageFin) repartition = Arch.makeAxis(num=len(distancesListe), name="Repartition") repartition.Length = 1000.00 repartition.Distances= distancesListe self.sel = FreeCADGui.Selection.getSelection() if self.sel: edges = DraftGeomUtils.sortEdges(self.sel[0].Shape.Wires[0].Edges) vec1 = edges[0].Vertexes[-1].Point.sub(edges[0].Vertexes[0].Point) point1 = edges[0].Vertexes[0].Point rot = math.degrees(DraftVecUtils.angle(vec1))*-1 repartition.Placement = FreeCAD.Placement(FreeCAD.Vector(point1),FreeCAD.Rotation(FreeCAD.Vector(0.0,0.0,1.0),rot)) FreeCAD.ActiveDocument.recompute() else: repartition.Placement = FreeCAD.Placement(FreeCAD.Vector(0.0,0.0,0.0),FreeCAD.Rotation(FreeCAD.Vector(0.0,0.0,1.0),0)) m = FreeCADGui.getMainWindow() w = m.findChild(QtGui.QDockWidget,"PartsLibrary") if w: if w.isVisible(): index = w.folder_view.selectedIndexes()[0] path = w.dirmodel.filePath(index) if path.lower().endswith(".stp") or path.lower().endswith(".step") or path.lower().endswith(".brep"): objetRepartit = Part.show(Part.read(path)) else: objetRepartit = FreeCADGui.ActiveDocument.mergeProject(path) repartitionStructurel = Arch.makeStructuralSystem([FreeCAD.ActiveDocument.Objects[-1],],[repartition,], name="RepartitionStructurelle") return True
def setRepresentation(representation): """Returns a shape from a 2D IfcShapeRepresentation""" def getPolyline(ent): pts = [] for p in ent.Points: c = p.Coordinates pts.append(FreeCAD.Vector(c[0],c[1],c[2] if len(c) > 2 else 0)) return Part.makePolygon(pts) def getCircle(ent): c = ent.Position.Location.Coordinates c = FreeCAD.Vector(c[0],c[1],c[2] if len(c) > 2 else 0) r = ent.Radius return Part.makeCircle(r,c) result = [] if representation.is_a("IfcShapeRepresentation"): for item in representation.Items: if item.is_a("IfcGeometricCurveSet"): for el in item.Elements: if el.is_a("IfcPolyline"): result.append(getPolyline(el)) elif el.is_a("IfcCircle"): result.append(getCircle(el)) elif el.is_a("IfcTrimmedCurve"): base = el.BasisCurve t1 = el.Trim1[0].wrappedValue t2 = el.Trim2[0].wrappedValue if not el.SenseAgreement: t1,t2 = t2,t1 if base.is_a("IfcPolyline"): bc = getPolyline(base) result.append(bc) elif base.is_a("IfcCircle"): bc = getCircle(base) e = Part.ArcOfCircle(bc.Curve,math.radians(t1),math.radians(t2)).toShape() d = base.Position.RefDirection.DirectionRatios v = FreeCAD.Vector(d[0],d[1],d[2] if len(d) > 2 else 0) a = -DraftVecUtils.angle(v) e.rotate(bc.Curve.Center,FreeCAD.Vector(0,0,1),math.degrees(a)) result.append(e) return result
def segmentAngleXY(self, prevCommand, currCommand, endpos=False, currentZ=0): '''returns in the starting angle in radians for a Path command. requires the previous command in order to calculate arcs correctly if endpos = True, return the angle at the end of the segment.''' global arccommands if currCommand.Name in arccommands: arcLoc = FreeCAD.Vector((prevCommand.x + currCommand.I), (prevCommand.y + currCommand.J), currentZ) if endpos is True: radvector = arcLoc.sub(currCommand.Placement.Base) # Calculate vector at start of arc else: radvector = arcLoc.sub(prevCommand.Placement.Base) # Calculate vector at end of arc v1 = radvector.cross(FreeCAD.Vector(0, 0, 1)) if currCommand.Name in ["G2", "G02"]: v1 = D.rotate2D(v1, math.radians(180)) else: v1 = currCommand.Placement.Base.sub(prevCommand.Placement.Base) # Straight segments are easy myAngle = D.angle(v1, FreeCAD.Base.Vector(1, 0, 0), FreeCAD.Base.Vector(0, 0, -1)) return myAngle
def getTuples(data,scale=1,placement=None,normal=None,close=True): """getTuples(data,[scale,placement,normal,close]): returns a tuple or a list of tuples from a vector or from the vertices of a shape. Scale can indicate a scale factor""" import Part if isinstance(data,FreeCAD.Vector): if placement: data = placement.multVec(data) data = DraftVecUtils.rounded(data) return (data.x*scale,data.y*scale,data.z*scale) elif isinstance(data,Part.Shape): t = [] if len(data.Wires) == 1: import Part,DraftGeomUtils data = Part.Wire(DraftGeomUtils.sortEdges(data.Wires[0].Edges)) verts = data.Vertexes try: c = data.CenterOfMass v1 = verts[0].Point.sub(c) v2 = verts[1].Point.sub(c) if DraftVecUtils.angle(v2,v1,normal) >= 0: # inverting verts order if the direction is couterclockwise verts.reverse() except: pass for v in verts: pt = v.Point if placement: if not placement.isNull(): pt = placement.multVec(pt) pt = DraftVecUtils.rounded(pt) t.append((pt.x*scale,pt.y*scale,pt.z*scale)) if close: # faceloops must not be closed, but ifc profiles must. t.append(t[0]) else: print "Arch.getTuples(): Wrong profile data" return t
def getRepresentation(ifcfile,context,obj,forcebrep=False,subtraction=False,tessellation=1): """returns an IfcShapeRepresentation object or None""" import Part,math,DraftGeomUtils,DraftVecUtils shapes = [] placement = None productdef = None shapetype = "no shape" if not forcebrep: profile = None if hasattr(obj,"Proxy"): if hasattr(obj.Proxy,"getProfiles"): p = obj.Proxy.getProfiles(obj,noplacement=True) extrusionv = obj.Proxy.getExtrusionVector(obj,noplacement=True) if (len(p) == 1) and extrusionv: p = p[0] r = obj.Proxy.getPlacement(obj) if len(p.Edges) == 1: pxvc = ifcfile.createIfcDirection((1.0,0.0)) povc = ifcfile.createIfcCartesianPoint((0.0,0.0)) pt = ifcfile.createIfcAxis2Placement2D(povc,pxvc) # extruded circle if isinstance(p.Edges[0].Curve,Part.Circle): profile = ifcfile.createIfcCircleProfileDef("AREA",None,pt, p.Edges[0].Curve.Radius) # extruded ellipse elif isinstance(p.Edges[0].Curve,Part.Ellipse): profile = ifcfile.createIfcEllipseProfileDef("AREA",None,pt, p.Edges[0].Curve.MajorRadius, p.Edges[0].Curve.MinorRadius) else: curves = False for e in p.Edges: if isinstance(e.Curve,Part.Circle): curves = True # extruded polyline if not curves: w = Part.Wire(DraftGeomUtils.sortEdges(p.Edges)) pts = [ifcfile.createIfcCartesianPoint(tuple(v.Point)[:2]) for v in w.Vertexes+[w.Vertexes[0]]] pol = ifcfile.createIfcPolyline(pts) # extruded composite curve else: segments = [] last = None edges = DraftGeomUtils.sortEdges(p.Edges) for e in edges: if isinstance(e.Curve,Part.Circle): follow = True if last: if not DraftVecUtils.equals(last,e.Vertexes[0].Point): follow = False last = e.Vertexes[0].Point else: last = e.Vertexes[-1].Point else: last = e.Vertexes[-1].Point p1 = math.degrees(-DraftVecUtils.angle(e.Vertexes[0].Point.sub(e.Curve.Center))) p2 = math.degrees(-DraftVecUtils.angle(e.Vertexes[-1].Point.sub(e.Curve.Center))) da = DraftVecUtils.angle(e.valueAt(e.FirstParameter+0.1).sub(e.Curve.Center),e.Vertexes[0].Point.sub(e.Curve.Center)) if p1 < 0: p1 = 360 + p1 if p2 < 0: p2 = 360 + p2 if da > 0: follow = not(follow) xvc = ifcfile.createIfcDirection((1.0,0.0)) ovc = ifcfile.createIfcCartesianPoint(tuple(e.Curve.Center)[:2]) plc = ifcfile.createIfcAxis2Placement2D(ovc,xvc) cir = ifcfile.createIfcCircle(plc,e.Curve.Radius) curve = ifcfile.createIfcTrimmedCurve(cir,[ifcfile.create_entity("IfcParameterValue",p1)],[ifcfile.create_entity("IfcParameterValue",p2)],follow,"PARAMETER") else: verts = [vertex.Point for vertex in e.Vertexes] if last: if not DraftVecUtils.equals(last,verts[0]): verts.reverse() last = e.Vertexes[0].Point else: last = e.Vertexes[-1].Point else: last = e.Vertexes[-1].Point pts = [ifcfile.createIfcCartesianPoint(tuple(v)[:2]) for v in verts] curve = ifcfile.createIfcPolyline(pts) segment = ifcfile.createIfcCompositeCurveSegment("CONTINUOUS",True,curve) segments.append(segment) pol = ifcfile.createIfcCompositeCurve(segments,False) profile = ifcfile.createIfcArbitraryClosedProfileDef("AREA",None,pol) if profile: xvc = ifcfile.createIfcDirection(tuple(r.Rotation.multVec(FreeCAD.Vector(1,0,0)))) zvc = ifcfile.createIfcDirection(tuple(r.Rotation.multVec(FreeCAD.Vector(0,0,1)))) ovc = ifcfile.createIfcCartesianPoint(tuple(r.Base)) lpl = ifcfile.createIfcAxis2Placement3D(ovc,zvc,xvc) edir = ifcfile.createIfcDirection(tuple(FreeCAD.Vector(extrusionv).normalize())) shape = ifcfile.createIfcExtrudedAreaSolid(profile,lpl,edir,extrusionv.Length) shapes.append(shape) solidType = "SweptSolid" shapetype = "extrusion" if not shapes: # brep representation fcshape = None solidType = "Brep" if subtraction: if hasattr(obj,"Proxy"): if hasattr(obj.Proxy,"getSubVolume"): fcshape = obj.Proxy.getSubVolume(obj) if not fcshape: if hasattr(obj,"Shape"): if obj.Shape: if not obj.Shape.isNull(): fcshape = obj.Shape elif hasattr(obj,"Terrain"): if obj.Terrain: if hasattr(obj.Terrain,"Shape"): if obj.Terrain.Shape: if not obj.Terrain.Shape.isNull(): fcshape = obj.Terrain.Shape if fcshape: solids = [] if fcshape.Solids: dataset = fcshape.Solids else: dataset = fcshape.Shells print "Warning! object contains no solids" for fcsolid in dataset: faces = [] curves = False for fcface in fcsolid.Faces: for e in fcface.Edges: if not isinstance(e.Curve,Part.Line): curves = True if curves: tris = fcsolid.tessellate(tessellation) for tri in tris[1]: pts = [ifcfile.createIfcCartesianPoint(tuple(tris[0][i])) for i in tri] loop = ifcfile.createIfcPolyLoop(pts) bound = ifcfile.createIfcFaceOuterBound(loop,True) face = ifcfile.createIfcFace([bound]) faces.append(face) else: for fcface in fcsolid.Faces: loops = [] verts = [v.Point for v in Part.Wire(DraftGeomUtils.sortEdges(fcface.OuterWire.Edges)).Vertexes] c = fcface.CenterOfMass v1 = verts[0].sub(c) v2 = verts[1].sub(c) n = fcface.normalAt(0,0) if DraftVecUtils.angle(v2,v1,n) >= 0: verts.reverse() # inverting verts order if the direction is couterclockwise pts = [ifcfile.createIfcCartesianPoint(tuple(v)) for v in verts] loop = ifcfile.createIfcPolyLoop(pts) bound = ifcfile.createIfcFaceOuterBound(loop,True) loops.append(bound) for wire in fcface.Wires: if wire.hashCode() != fcface.OuterWire.hashCode(): verts = [v.Point for v in Part.Wire(DraftGeomUtils.sortEdges(wire.Edges)).Vertexes] v1 = verts[0].sub(c) v2 = verts[1].sub(c) if DraftVecUtils.angle(v2,v1,DraftVecUtils.neg(n)) >= 0: verts.reverse() pts = [ifcfile.createIfcCartesianPoint(tuple(v)) for v in verts] loop = ifcfile.createIfcPolyLoop(pts) bound = ifcfile.createIfcFaceBound(loop,True) loops.append(bound) face = ifcfile.createIfcFace(loops) faces.append(face) shell = ifcfile.createIfcClosedShell(faces) shape = ifcfile.createIfcFacetedBrep(shell) shapes.append(shape) shapetype = "brep" if shapes: if FreeCAD.GuiUp and (not subtraction) and hasattr(obj.ViewObject,"ShapeColor"): rgb = obj.ViewObject.ShapeColor col = ifcfile.createIfcColourRgb(None,rgb[0],rgb[1],rgb[2]) ssr = ifcfile.createIfcSurfaceStyleRendering(col,None,None,None,None,None,None,None,"FLAT") iss = ifcfile.createIfcSurfaceStyle(None,"BOTH",[ssr]) psa = ifcfile.createIfcPresentationStyleAssignment([iss]) for shape in shapes: isi = ifcfile.createIfcStyledItem(shape,[psa],None) xvc = ifcfile.createIfcDirection((1.0,0.0,0.0)) zvc = ifcfile.createIfcDirection((0.0,0.0,1.0)) ovc = ifcfile.createIfcCartesianPoint((0.0,0.0,0.0)) gpl = ifcfile.createIfcAxis2Placement3D(ovc,zvc,xvc) placement = ifcfile.createIfcLocalPlacement(None,gpl) representation = ifcfile.createIfcShapeRepresentation(context,'Body',solidType,shapes) productdef = ifcfile.createIfcProductDefinitionShape(None,None,[representation]) return productdef,placement,shapetype
def arcTwist(self, obj, queue, lastXY, twistCW=False): '''returns gcode to do an arc move toward an arc to perform a corner action twist. Includes lifting and plungeing the knife''' global currLocation pivotheight = obj.pivotheight offset = obj.offset results = [] # set the correct twist command if twistCW is False: arcdir = "G3" else: arcdir = "G2" # move to the pivot height zdepth = currLocation["Z"] retract = Path.Command("G0", {"Z": pivotheight}) results.append(retract) currLocation.update(retract.Parameters) # get the center of the destination arc arccenter = FreeCAD.Base.Vector(queue[1].x + queue[0].I, queue[1].y + queue[0].J, currLocation["Z"]) # The center of the twist arc is the old line end point. C = queue[1].Placement.Base # Find radius of old arc R = math.hypot(queue[0].I, queue[0].J) # find angle of original center to startpoint v1 = queue[1].Placement.Base.sub(arccenter) segAngle = D.angle(v1, FreeCAD.Base.Vector(1, 0, 0), FreeCAD.Base.Vector(0, 0, -1)) # Find angle subtended by the offset theta = offset / R # add or subtract theta depending on direction if queue[1].Name in ["G2", "G02"]: newangle = segAngle + theta else: newangle = segAngle - theta # calculate endpoints Bx = arccenter.x + R * math.cos(newangle) By = arccenter.y + R * math.sin(newangle) endpointvector = FreeCAD.Base.Vector(Bx, By, currLocation['Z']) # calculate IJ offsets of twist arc from current position. offsetvector = C.sub(lastXY) # add G2/G3 move arcmove = Path.Command( arcdir, {"X": endpointvector.x, "Y": endpointvector.y, "I": offsetvector.x, "J": offsetvector.y}) results.append(arcmove) currLocation.update(arcmove.Parameters) # plunge back to depth plunge = Path.Command("G1", {"Z": zdepth}) results.append(plunge) currLocation.update(plunge.Parameters) # The old arc move won't work so calculate a replacement command offsetv = arccenter.sub(endpointvector) replace = Path.Command( queue[0].Name, {"X": queue[0].X, "Y": queue[0].Y, "I": offsetv.x, "J": offsetv.y}) return (results, replace)
def getSVG(obj,scale=1,linewidth=0.35,fontsize=12,fillstyle="shape color",direction=None,linestyle=None,color=None,linespacing=None,techdraw=False,rotation=0): '''getSVG(object,[scale], [linewidth],[fontsize],[fillstyle],[direction],[linestyle],[color],[linespacing]): returns a string containing a SVG representation of the given object, with the given linewidth and fontsize (used if the given object contains any text). You can also supply an arbitrary projection vector. the scale parameter allows to scale linewidths down, so they are resolution-independant.''' # if this is a group, gather all the svg views of its children if hasattr(obj,"isDerivedFrom"): if obj.isDerivedFrom("App::DocumentObjectGroup"): svg = "" for child in obj.Group: svg += getSVG(child,scale,linewidth,fontsize,fillstyle,direction,linestyle,color,linespacing,techdraw) return svg pathdata = [] svg = "" linewidth = float(linewidth)/scale fontsize = (float(fontsize)/scale)/2 if linespacing: linespacing = float(linespacing)/scale else: linespacing = 0.5 #print obj.Label," line spacing ",linespacing,"scale ",scale pointratio = .75 # the number of times the dots are smaller than the arrow size plane = None if direction: if isinstance(direction,FreeCAD.Vector): if direction != Vector(0,0,0): plane = WorkingPlane.plane() plane.alignToPointAndAxis_SVG(Vector(0,0,0),direction.negative().negative(),0) elif isinstance(direction,WorkingPlane.plane): plane = direction stroke = "#000000" if color: if "#" in color: stroke = color else: stroke = getrgb(color) elif gui: if hasattr(obj,"ViewObject"): if hasattr(obj.ViewObject,"LineColor"): stroke = getrgb(obj.ViewObject.LineColor) def getPath(edges=[],wires=[],pathname=None): import Part,DraftGeomUtils svg = "<path " if pathname is None: svg += 'id="%s" ' % obj.Name elif pathname != "": svg += 'id="%s" ' % pathname svg += ' d="' if not wires: egroups = Part.sortEdges(edges) else: egroups = [] for w in wires: w1=w.copy() w1.fixWire() egroups.append(Part.__sortEdges__(w1.Edges)) for egroupindex, edges in enumerate(egroups): edata = "" vs=() #skipped for the first edge for edgeindex,e in enumerate(edges): previousvs = vs # vertexes of an edge (reversed if needed) vs = e.Vertexes if previousvs: if (vs[0].Point-previousvs[-1].Point).Length > 1e-6: vs.reverse() if edgeindex == 0: v = getProj(vs[0].Point, plane) edata += 'M '+ str(v.x) +' '+ str(v.y) + ' ' else: if (vs[0].Point-previousvs[-1].Point).Length > 1e-6: raise ValueError('edges not ordered') iscircle = DraftGeomUtils.geomType(e) == "Circle" isellipse = DraftGeomUtils.geomType(e) == "Ellipse" if iscircle or isellipse: import math if hasattr(FreeCAD,"DraftWorkingPlane"): drawing_plane_normal = FreeCAD.DraftWorkingPlane.axis else: drawing_plane_normal = FreeCAD.Vector(0,0,1) if plane: drawing_plane_normal = plane.axis c = e.Curve if round(c.Axis.getAngle(drawing_plane_normal),2) in [0,3.14]: occversion = Part.OCC_VERSION.split(".") done = False if (int(occversion[0]) >= 7) and (int(occversion[1]) >= 1): # if using occ >= 7.1, use HLR algorithm import Drawing snip = Drawing.projectToSVG(e,drawing_plane_normal) if snip: try: a = "A " + snip.split("path d=\"")[1].split("\"")[0].split("A")[1] except: pass else: edata += a done = True if not done: if len(e.Vertexes) == 1 and iscircle: #complete curve svg = getCircle(e) return svg elif len(e.Vertexes) == 1 and isellipse: #svg = getEllipse(e) #return svg endpoints = (getProj(c.value((c.LastParameter-\ c.FirstParameter)/2.0), plane), \ getProj(vs[-1].Point, plane)) else: endpoints = (getProj(vs[-1].Point), plane) # arc if iscircle: rx = ry = c.Radius rot = 0 else: #ellipse rx = c.MajorRadius ry = c.MinorRadius rot = math.degrees(c.AngleXU * (c.Axis * \ FreeCAD.Vector(0,0,1))) if rot > 90: rot -=180 if rot < -90: rot += 180 #be careful with the sweep flag flag_large_arc = (((e.ParameterRange[1] - \ e.ParameterRange[0]) / math.pi) % 2) > 1 #flag_sweep = (c.Axis * drawing_plane_normal >= 0) \ # == (e.LastParameter > e.FirstParameter) # == (e.Orientation == "Forward") # other method: check the direction of the angle between tangents t1 = e.tangentAt(e.FirstParameter) t2 = e.tangentAt(e.FirstParameter + (e.LastParameter-e.FirstParameter)/10) flag_sweep = (DraftVecUtils.angle(t1,t2,drawing_plane_normal) < 0) for v in endpoints: edata += 'A %s %s %s %s %s %s %s ' % \ (str(rx),str(ry),str(rot),\ str(int(flag_large_arc)),\ str(int(flag_sweep)),str(v.x),str(v.y)) else: edata += getDiscretized(e, plane) elif DraftGeomUtils.geomType(e) == "Line": v = getProj(vs[-1].Point, plane) edata += 'L '+ str(v.x) +' '+ str(v.y) + ' ' else: bspline=e.Curve.toBSpline(e.FirstParameter,e.LastParameter) if bspline.Degree > 3 or bspline.isRational(): try: bspline=bspline.approximateBSpline(0.05,50, 3,'C0') except RuntimeError: print("Debug: unable to approximate bspline") if bspline.Degree <= 3 and not bspline.isRational(): for bezierseg in bspline.toBezier(): if bezierseg.Degree>3: #should not happen raise AssertionError elif bezierseg.Degree==1: edata +='L ' elif bezierseg.Degree==2: edata +='Q ' elif bezierseg.Degree==3: edata +='C ' for pole in bezierseg.getPoles()[1:]: v = getProj(pole, plane) edata += str(v.x) +' '+ str(v.y) + ' ' else: print("Debug: one edge (hash ",e.hashCode(),\ ") has been discretized with parameter 0.1") for linepoint in bspline.discretize(0.1)[1:]: v = getProj(linepoint, plane) edata += 'L '+ str(v.x) +' '+ str(v.y) + ' ' if fill != 'none': edata += 'Z ' if edata in pathdata: # do not draw a path on another identical path return "" else: svg += edata pathdata.append(edata) svg += '" ' svg += 'stroke="' + stroke + '" ' svg += 'stroke-width="' + str(linewidth) + ' px" ' svg += 'style="stroke-width:'+ str(linewidth) svg += ';stroke-miterlimit:4' svg += ';stroke-dasharray:' + lstyle svg += ';fill:' + fill try: svg += ';fill-opacity:' + str(fill_opacity) except NameError: pass svg += ';fill-rule: evenodd "' svg += '/>\n' return svg def getCircle(edge): cen = getProj(edge.Curve.Center, plane) rad = edge.Curve.Radius if hasattr(FreeCAD,"DraftWorkingPlane"): drawing_plane_normal = FreeCAD.DraftWorkingPlane.axis else: drawing_plane_normal = FreeCAD.Vector(0,0,1) if plane: drawing_plane_normal = plane.axis if round(edge.Curve.Axis.getAngle(drawing_plane_normal),2) == 0: # perpendicular projection: circle svg = '<circle cx="' + str(cen.x) svg += '" cy="' + str(cen.y) svg += '" r="' + str(rad)+'" ' else: # any other projection: ellipse svg = '<path d="' svg += getDiscretized(edge, plane) svg += '" ' svg += 'stroke="' + stroke + '" ' svg += 'stroke-width="' + str(linewidth) + ' px" ' svg += 'style="stroke-width:'+ str(linewidth) svg += ';stroke-miterlimit:4' svg += ';stroke-dasharray:' + lstyle svg += ';fill:' + fill + '"' svg += '/>\n' return svg def getEllipse(edge): cen = getProj(edge.Curve.Center, plane) mir = edge.Curve.MinorRadius mar = edge.Curve.MajorRadius svg = '<ellipse cx="' + str(cen.x) svg += '" cy="' + str(cen.y) svg += '" rx="' + str(mar) svg += '" ry="' + str(mir)+'" ' svg += 'stroke="' + stroke + '" ' svg += 'stroke-width="' + str(linewidth) + ' px" ' svg += 'style="stroke-width:'+ str(linewidth) svg += ';stroke-miterlimit:4' svg += ';stroke-dasharray:' + lstyle svg += ';fill:' + fill + '"' svg += '/>\n' return svg def getArrow(arrowtype,point,arrowsize,color,linewidth,angle=0): svg = "" if gui: if not obj.ViewObject: return svg if obj.ViewObject.ArrowType == "Circle": svg += '<circle cx="'+str(point.x)+'" cy="'+str(point.y) svg += '" r="'+str(arrowsize)+'" ' svg += 'fill="none" stroke="'+ color + '" ' svg += 'style="stroke-width:'+ str(linewidth) + ';stroke-miterlimit:4;stroke-dasharray:none" ' svg += 'freecad:skip="1"' svg += '/>\n' elif obj.ViewObject.ArrowType == "Dot": svg += '<circle cx="'+str(point.x)+'" cy="'+str(point.y) svg += '" r="'+str(arrowsize)+'" ' svg += 'fill="'+ color +'" stroke="none" ' svg += 'style="stroke-miterlimit:4;stroke-dasharray:none" ' svg += 'freecad:skip="1"' svg += '/>\n' elif obj.ViewObject.ArrowType == "Arrow": svg += '<path transform="rotate('+str(math.degrees(angle)) svg += ','+ str(point.x) + ',' + str(point.y) + ') ' svg += 'translate(' + str(point.x) + ',' + str(point.y) + ') ' svg += 'scale('+str(arrowsize)+','+str(arrowsize)+')" freecad:skip="1" ' svg += 'fill="'+ color +'" stroke="none" ' svg += 'style="stroke-miterlimit:4;stroke-dasharray:none" ' svg += 'd="M 0 0 L 4 1 L 4 -1 Z"/>\n' elif obj.ViewObject.ArrowType == "Tick": svg += '<path transform="rotate('+str(math.degrees(angle)) svg += ','+ str(point.x) + ',' + str(point.y) + ') ' svg += 'translate(' + str(point.x) + ',' + str(point.y) + ') ' svg += 'scale('+str(arrowsize)+','+str(arrowsize)+')" freecad:skip="1" ' svg += 'fill="'+ color +'" stroke="none" ' svg += 'style="stroke-miterlimit:4;stroke-dasharray:none" ' svg += 'd="M -1 -2 L 0 2 L 1 2 L 0 -2 Z"/>\n' elif obj.ViewObject.ArrowType == "Tick-2": svg += '<line transform="rotate('+str(math.degrees(angle)+45) svg += ','+ str(point.x) + ',' + str(point.y) + ') ' svg += 'translate(' + str(point.x) + ',' + str(point.y) + ') ' svg += '" freecad:skip="1" ' svg += 'fill="none" stroke="'+ color +'" ' svg += 'style="stroke-dasharray:none;stroke-linecap:square;' svg += 'stroke-width:'+ str(linewidth) +'" ' svg += 'x1="-'+ str(arrowsize*2) +'" y1="0" ' svg += 'x2="' + str(arrowsize*2) +'" y2="0" />\n' else: print("getSVG: arrow type not implemented") return svg def getOvershoot(point,shootsize,color,linewidth,angle=0): svg = '<line transform="rotate('+str(math.degrees(angle)) svg += ','+ str(point.x) + ',' + str(point.y) + ') ' svg += 'translate(' + str(point.x) + ',' + str(point.y) + ') ' svg += '" freecad:skip="1" ' svg += 'fill="none" stroke="'+ color +'" ' svg += 'style="stroke-dasharray:none;stroke-linecap:square;' svg += 'stroke-width:'+ str(linewidth) +'" ' svg += 'x1="0" y1="0" ' svg += 'x2="'+ str(shootsize*-1) +'" y2="0" />\n' return svg def getText(color,fontsize,fontname,angle,base,text,linespacing=0.5,align="center",flip=True): if isinstance(angle,FreeCAD.Rotation): if not plane: angle = angle.Angle else: if plane.axis.getAngle(angle.Axis) < 0.001: angle = angle.Angle elif abs(plane.axis.getAngle(angle.Axis)-math.pi) < 0.001: return "" # text is perpendicular to view, so it shouldn't appear else: angle = 0 #TODO maybe there is something better to do here? if not isinstance(text,list): text = text.split("\n") if align.lower() == "center": anchor = "middle" elif align.lower() == "left": anchor = "start" else: anchor = "end" if techdraw: svg = "" for i in range(len(text)): t = text[i] if sys.version_info.major < 3 and (not isinstance(t,unicode)): t = t.decode("utf8") # possible workaround if UTF8 is unsupported # import unicodedata # t = u"".join([c for c in unicodedata.normalize("NFKD",t) if not unicodedata.combining(c)]).encode("utf8") svg += '<text fill="' + color +'" font-size="' + str(fontsize) + '" ' svg += 'style="text-anchor:'+anchor+';text-align:'+align.lower()+';' svg += 'font-family:'+ fontname +'" ' svg += 'transform="rotate('+str(math.degrees(angle)) svg += ','+ str(base.x) + ',' + str(base.y-linespacing*i) + ') ' svg += 'translate(' + str(base.x) + ',' + str(base.y-linespacing*i) + ') ' svg += 'scale(1,-1)" ' #svg += '" freecad:skip="1"' svg += '>\n' + t + '</text>\n' else: svg = '<text fill="' svg += color +'" font-size="' svg += str(fontsize) + '" ' svg += 'style="text-anchor:'+anchor+';text-align:'+align.lower()+';' svg += 'font-family:'+ fontname +'" ' svg += 'transform="rotate('+str(math.degrees(angle)) svg += ','+ str(base.x) + ',' + str(base.y) + ') ' if flip: svg += 'translate(' + str(base.x) + ',' + str(base.y) + ')' else: svg += 'translate(' + str(base.x) + ',' + str(-base.y) + ')' #svg += 'scale('+str(tmod/2000)+',-'+str(tmod/2000)+') ' if flip: svg += ' scale(1,-1) ' else: svg += ' scale(1,1) ' svg += '" freecad:skip="1"' svg += '>\n' if len(text) == 1: try: svg += text[0] except: svg += text[0].decode("utf8") else: for i in range(len(text)): if i == 0: svg += '<tspan>' else: svg += '<tspan x="0" dy="'+str(linespacing)+'">' try: svg += text[i] except: svg += text[i].decode("utf8") svg += '</tspan>\n' svg += '</text>\n' return svg if not obj: pass elif isinstance(obj,Part.Shape): if "#" in fillstyle: fill = fillstyle elif fillstyle == "shape color": fill = "#888888" else: fill = 'url(#'+fillstyle+')' lstyle = getLineStyle(linestyle, scale) svg += getPath(obj.Edges,pathname="") elif getType(obj) == "Dimension": if gui: if not obj.ViewObject: print ("export of dimensions to SVG is only available in GUI mode") elif obj.ViewObject.Proxy: if hasattr(obj.ViewObject.Proxy,"p1"): prx = obj.ViewObject.Proxy ts = (len(prx.string)*obj.ViewObject.FontSize.Value)/4.0 rm = ((prx.p3.sub(prx.p2)).Length/2.0)-ts p2a = getProj(prx.p2.add(DraftVecUtils.scaleTo(prx.p3.sub(prx.p2),rm)), plane) p2b = getProj(prx.p3.add(DraftVecUtils.scaleTo(prx.p2.sub(prx.p3),rm)), plane) p1 = getProj(prx.p1, plane) p2 = getProj(prx.p2, plane) p3 = getProj(prx.p3, plane) p4 = getProj(prx.p4, plane) tbase = getProj(prx.tbase, plane) r = prx.textpos.rotation.getValue().getValue() rv = FreeCAD.Rotation(r[0],r[1],r[2],r[3]).multVec(FreeCAD.Vector(1,0,0)) angle = -DraftVecUtils.angle(getProj(rv, plane)) #angle = -DraftVecUtils.angle(p3.sub(p2)) # drawing lines svg = '<path ' if obj.ViewObject.DisplayMode == "2D": tangle = angle if tangle > math.pi/2: tangle = tangle-math.pi #elif (tangle <= -math.pi/2) or (tangle > math.pi/2): # tangle = tangle+math.pi #tbase = tbase.add(DraftVecUtils.rotate(Vector(0,2/scale,0),tangle)) if rotation != 0: #print "dim: tangle:",tangle," rot: ",rotation," text: ",prx.string if abs(tangle+math.radians(rotation)) < 0.0001: tangle += math.pi tbase = tbase.add(DraftVecUtils.rotate(Vector(0,2/scale,0),tangle)) svg += 'd="M '+str(p1.x)+' '+str(p1.y)+' ' svg += 'L '+str(p2.x)+' '+str(p2.y)+' ' svg += 'L '+str(p3.x)+' '+str(p3.y)+' ' svg += 'L '+str(p4.x)+' '+str(p4.y)+'" ' else: tangle = 0 if rotation != 0: tangle = -math.radians(rotation) tbase = tbase.add(Vector(0,-2.0/scale,0)) svg += 'd="M '+str(p1.x)+' '+str(p1.y)+' ' svg += 'L '+str(p2.x)+' '+str(p2.y)+' ' svg += 'L '+str(p2a.x)+' '+str(p2a.y)+' ' svg += 'M '+str(p2b.x)+' '+str(p2b.y)+' ' svg += 'L '+str(p3.x)+' '+str(p3.y)+' ' svg += 'L '+str(p4.x)+' '+str(p4.y)+'" ' svg += 'fill="none" stroke="' svg += stroke + '" ' svg += 'stroke-width="' + str(linewidth) + ' px" ' svg += 'style="stroke-width:'+ str(linewidth) svg += ';stroke-miterlimit:4;stroke-dasharray:none" ' svg += 'freecad:basepoint1="'+str(p1.x)+' '+str(p1.y)+'" ' svg += 'freecad:basepoint2="'+str(p4.x)+' '+str(p4.y)+'" ' svg += 'freecad:dimpoint="'+str(p2.x)+' '+str(p2.y)+'"' svg += '/>\n' # drawing dimension and extension lines overshoots if hasattr(obj.ViewObject,"DimOvershoot") and obj.ViewObject.DimOvershoot.Value: shootsize = obj.ViewObject.DimOvershoot.Value/pointratio svg += getOvershoot(p2,shootsize,stroke,linewidth,angle) svg += getOvershoot(p3,shootsize,stroke,linewidth,angle+math.pi) if hasattr(obj.ViewObject,"ExtOvershoot") and obj.ViewObject.ExtOvershoot.Value: shootsize = obj.ViewObject.ExtOvershoot.Value/pointratio shootangle = -DraftVecUtils.angle(p1.sub(p2)) svg += getOvershoot(p2,shootsize,stroke,linewidth,shootangle) svg += getOvershoot(p3,shootsize,stroke,linewidth,shootangle) # drawing arrows if hasattr(obj.ViewObject,"ArrowType"): arrowsize = obj.ViewObject.ArrowSize.Value/pointratio if hasattr(obj.ViewObject,"FlipArrows"): if obj.ViewObject.FlipArrows: angle = angle+math.pi svg += getArrow(obj.ViewObject.ArrowType,p2,arrowsize,stroke,linewidth,angle) svg += getArrow(obj.ViewObject.ArrowType,p3,arrowsize,stroke,linewidth,angle+math.pi) # drawing text svg += getText(stroke,fontsize,obj.ViewObject.FontName,tangle,tbase,prx.string) elif getType(obj) == "AngularDimension": if gui: if not obj.ViewObject: print ("export of dimensions to SVG is only available in GUI mode") elif obj.ViewObject.Proxy: if hasattr(obj.ViewObject.Proxy,"circle"): prx = obj.ViewObject.Proxy # drawing arc fill= "none" lstyle = getLineStyle(linestyle, scale) if obj.ViewObject.DisplayMode == "2D": svg += getPath([prx.circle]) else: if hasattr(prx,"circle1"): svg += getPath([prx.circle1]) svg += getPath([prx.circle2]) else: svg += getPath([prx.circle]) # drawing arrows if hasattr(obj.ViewObject,"ArrowType"): p2 = getProj(prx.p2, plane) p3 = getProj(prx.p3, plane) arrowsize = obj.ViewObject.ArrowSize.Value/pointratio arrowlength = 4*obj.ViewObject.ArrowSize.Value u1 = getProj((prx.circle.valueAt(prx.circle.FirstParameter+arrowlength)).sub(prx.circle.valueAt(prx.circle.FirstParameter)), plane) u2 = getProj((prx.circle.valueAt(prx.circle.LastParameter-arrowlength)).sub(prx.circle.valueAt(prx.circle.LastParameter)), plane) angle1 = -DraftVecUtils.angle(u1) angle2 = -DraftVecUtils.angle(u2) if hasattr(obj.ViewObject,"FlipArrows"): if obj.ViewObject.FlipArrows: angle1 = angle1+math.pi angle2 = angle2+math.pi svg += getArrow(obj.ViewObject.ArrowType,p2,arrowsize,stroke,linewidth,angle1) svg += getArrow(obj.ViewObject.ArrowType,p3,arrowsize,stroke,linewidth,angle2) # drawing text if obj.ViewObject.DisplayMode == "2D": t = prx.circle.tangentAt(prx.circle.FirstParameter+(prx.circle.LastParameter-prx.circle.FirstParameter)/2.0) t = getProj(t, plane) tangle = DraftVecUtils.angle(t) if (tangle <= -math.pi/2) or (tangle > math.pi/2): tangle = tangle + math.pi tbase = getProj(prx.circle.valueAt(prx.circle.FirstParameter+(prx.circle.LastParameter-prx.circle.FirstParameter)/2.0), plane) tbase = tbase.add(DraftVecUtils.rotate(Vector(0,2.0/scale,0),tangle)) #print(tbase) else: tangle = 0 tbase = getProj(prx.tbase, plane) svg += getText(stroke,fontsize,obj.ViewObject.FontName,tangle,tbase,prx.string) elif getType(obj) == "Label": if getattr(obj.ViewObject, "Line", True): # some Labels may have no Line property def format_point(coords, action='L'): return "{action}{x},{y}".format( x=coords.x, y=coords.y, action=action ) # Draw multisegment line proj_points = list(map(lambda x: getProj(x, plane), obj.Points)) path_dir_list = [format_point(proj_points[0], action='M')] path_dir_list += map(format_point, proj_points[1:]) path_dir_str = " ".join(path_dir_list) svg_path = '<path fill="none" stroke="{stroke}" stroke-width="{linewidth}" d="{directions}"/>'.format( stroke=stroke, linewidth=linewidth, directions=path_dir_str ) svg += svg_path # Draw arrow. # We are different here from 3D view # if Line is set to 'off', no arrow is drawn if hasattr(obj.ViewObject, "ArrowType") and len(obj.Points) >= 2: last_segment = FreeCAD.Vector(obj.Points[-1] - obj.Points[-2]) angle = -DraftVecUtils.angle(getProj(last_segment, plane)) + math.pi svg += getArrow( arrowtype=obj.ViewObject.ArrowType, point=proj_points[-1], arrowsize=obj.ViewObject.ArrowSize.Value/pointratio, color=stroke, linewidth=linewidth, angle=angle ) # print text if gui: if not obj.ViewObject: print("export of texts to SVG is only available in GUI mode") else: fontname = obj.ViewObject.TextFont position = getProj(obj.Placement.Base, plane) rotation = obj.Placement.Rotation justification = obj.ViewObject.TextAlignment text = obj.Text svg += getText(stroke, fontsize, fontname, rotation, position, text, linespacing, justification) elif getType(obj) in ["Annotation","DraftText"]: "returns an svg representation of a document annotation" if gui: if not obj.ViewObject: print ("export of texts to SVG is only available in GUI mode") else: n = obj.ViewObject.FontName if getType(obj) == "Annotation": p = getProj(obj.Position, plane) r = obj.ViewObject.Rotation.getValueAs("rad") t = obj.LabelText else: # DraftText p = getProj(obj.Placement.Base, plane) r = obj.Placement.Rotation t = obj.Text j = obj.ViewObject.Justification svg += getText(stroke,fontsize,n,r,p,t,linespacing,j) elif getType(obj) == "Axis": "returns the SVG representation of an Arch Axis system" if gui: if not obj.ViewObject: print ("export of axes to SVG is only available in GUI mode") else: vobj = obj.ViewObject lorig = getLineStyle(linestyle, scale) fill = 'none' rad = vobj.BubbleSize.Value/2 n = 0 for e in obj.Shape.Edges: lstyle = lorig svg += getPath([e]) lstyle = "none" pos = ["Start"] if hasattr(vobj,"BubblePosition"): if vobj.BubblePosition == "Both": pos = ["Start","End"] else: pos = [vobj.BubblePosition] for p in pos: if p == "Start": p1 = e.Vertexes[0].Point p2 = e.Vertexes[1].Point else: p1 = e.Vertexes[1].Point p2 = e.Vertexes[0].Point dv = p2.sub(p1) dv.normalize() center = p2.add(dv.scale(rad,rad,rad)) svg += getCircle(Part.makeCircle(rad,center)) if hasattr(vobj.Proxy,"bubbletexts"): if len (vobj.Proxy.bubbletexts) >= n: svg += '<text fill="' + stroke + '" ' svg += 'font-size="' + str(rad) + '" ' svg += 'style="text-anchor:middle;' svg += 'text-align:center;' svg += 'font-family: sans;" ' svg += 'transform="translate(' + str(center.x+rad/4.0) + ',' + str(center.y-rad/3.0) + ') ' svg += 'scale(1,-1)"> ' svg += '<tspan>' + obj.ViewObject.Proxy.bubbletexts[n].string.getValues()[0] + '</tspan>\n' svg += '</text>\n' n += 1 elif getType(obj) == "Pipe": fill = stroke lstyle = getLineStyle(linestyle, scale) if obj.Base and obj.Diameter: svg += getPath(obj.Base.Shape.Edges) for f in obj.Shape.Faces: if len(f.Edges) == 1: if isinstance(f.Edges[0].Curve,Part.Circle): svg += getCircle(f.Edges[0]) elif getType(obj) == "Rebar": fill = "none" lstyle = getLineStyle(linestyle, scale) if obj.Proxy: if not hasattr(obj.Proxy,"wires"): obj.Proxy.execute(obj) if hasattr(obj.Proxy,"wires"): svg += getPath(wires=obj.Proxy.wires) elif getType(obj) == "PipeConnector": pass elif getType(obj) == "Space": "returns an SVG fragment for the text of a space" if gui: if not obj.ViewObject: print ("export of spaces to SVG is only available in GUI mode") else: c = getrgb(obj.ViewObject.TextColor) n = obj.ViewObject.FontName a = 0 if rotation != 0: a = math.radians(rotation) t1 = obj.ViewObject.Proxy.text1.string.getValues() t2 = obj.ViewObject.Proxy.text2.string.getValues() scale = obj.ViewObject.FirstLine.Value/obj.ViewObject.FontSize.Value f1 = fontsize*scale p2 = FreeCAD.Vector(obj.ViewObject.Proxy.coords.translation.getValue().getValue()) lspc = FreeCAD.Vector(obj.ViewObject.Proxy.header.translation.getValue().getValue()) p1 = p2.add(lspc) j = obj.ViewObject.TextAlign svg += getText(c,f1,n,a,getProj(p1, plane),t1,linespacing,j,flip=True) if t2: ofs = FreeCAD.Vector(0,lspc.Length,0) if a: ofs = FreeCAD.Rotation(FreeCAD.Vector(0,0,1),-rotation).multVec(ofs) svg += getText(c,fontsize,n,a,getProj(p1, plane).add(ofs),t2,linespacing,j,flip=True) elif obj.isDerivedFrom('Part::Feature'): if obj.Shape.isNull(): return '' # setting fill if obj.Shape.Faces: if gui: try: m = obj.ViewObject.DisplayMode except AttributeError: m = None if (m != "Wireframe"): if fillstyle == "shape color": fill = getrgb(obj.ViewObject.ShapeColor,testbw=False) fill_opacity = 1 - (obj.ViewObject.Transparency / 100.0) else: fill = 'url(#'+fillstyle+')' svg += getPattern(fillstyle) else: fill = "none" else: fill = "#888888" else: fill = 'none' lstyle = getLineStyle(linestyle, scale) if len(obj.Shape.Vertexes) > 1: wiredEdges = [] if obj.Shape.Faces: for i,f in enumerate(obj.Shape.Faces): svg += getPath(wires=f.Wires,pathname='%s_f%04d' % \ (obj.Name,i)) wiredEdges.extend(f.Edges) else: for i,w in enumerate(obj.Shape.Wires): svg += getPath(w.Edges,pathname='%s_w%04d' % \ (obj.Name,i)) wiredEdges.extend(w.Edges) if len(wiredEdges) != len(obj.Shape.Edges): for i,e in enumerate(obj.Shape.Edges): if (DraftGeomUtils.findEdge(e,wiredEdges) == None): svg += getPath([e],pathname='%s_nwe%04d' % \ (obj.Name,i)) else: # closed circle or spline if obj.Shape.Edges: if isinstance(obj.Shape.Edges[0].Curve,Part.Circle): svg = getCircle(obj.Shape.Edges[0]) else: svg = getPath(obj.Shape.Edges) if FreeCAD.GuiUp: if hasattr(obj.ViewObject,"EndArrow") and hasattr(obj.ViewObject,"ArrowType") and (len(obj.Shape.Vertexes) > 1): if obj.ViewObject.EndArrow: p1 = getProj(obj.Shape.Vertexes[-2].Point, plane) p2 = getProj(obj.Shape.Vertexes[-1].Point, plane) angle = -DraftVecUtils.angle(p2.sub(p1)) arrowsize = obj.ViewObject.ArrowSize.Value/pointratio svg += getArrow(obj.ViewObject.ArrowType,p2,arrowsize,stroke,linewidth,angle) # techdraw expects bottom-to-top coordinates if techdraw: svg = '<g transform ="scale(1,-1)">'+svg+'</g>' return svg
def execute(self,obj): import Part, math, DraftGeomUtils pl = obj.Placement self.baseface = None base = None if obj.Base and obj.Angles: w = None if obj.Base.isDerivedFrom("Part::Feature"): if (obj.Base.Shape.Faces and obj.Face): w = obj.Base.Shape.Faces[obj.Face-1].Wires[0] elif obj.Base.Shape.Wires: w = obj.Base.Shape.Wires[0] if w: if w.isClosed(): self.profilsDico = [] self.shps = [] self.subVolshps = [] heights = [] edges = DraftGeomUtils.sortEdges(w.Edges) l = len(edges) print("le contour contient "+str(l)+" aretes") for i in range(l): self.makeRoofProfilsDic(i, obj.Angles[i], obj.Runs[i], obj.IdRel[i], obj.Overhang[i], obj.Thickness[i]) for i in range(l): self.calcMissingData(i) for p in self.profilsDico: heights.append(p["height"]) obj.Heights = heights for i in range(l): edgesForward = edges[:] edgesForward.append(edges[0]) ptsPaneProject=[] profil0 =self.profilsDico[i-1] profil1 =self.profilsDico[i] if i == l-1: profil2 =self.profilsDico[0] else: profil2 =self.profilsDico[i+1] vec0 = edges[i-1].Vertexes[-1].Point.sub(edges[i-1].Vertexes[0].Point) vec1 = edges[i].Vertexes[-1].Point.sub(edges[i].Vertexes[0].Point) vec2 = edgesForward[i+1].Vertexes[-1].Point.sub(edgesForward[i+1].Vertexes[0].Point) rotEdge0 = math.degrees(DraftVecUtils.angle(vec0)) rotEdge1 = math.degrees(DraftVecUtils.angle(vec1)) rotEdge2 = math.degrees(DraftVecUtils.angle(vec2)) edgeEave0 = DraftGeomUtils.offset(edges[i-1],self.getPerpendicular(vec0,rotEdge0,profil0["overhang"]).negative()) edgeEave1 = DraftGeomUtils.offset(edges[i],self.getPerpendicular(vec1,rotEdge1,profil1["overhang"]).negative()) edgeEave2 = DraftGeomUtils.offset(edgesForward[i+1],self.getPerpendicular(vec2,rotEdge2,profil2["overhang"]).negative()) pt0Eave1 = DraftGeomUtils.findIntersection(edgeEave0,edgeEave1,infinite1=True,infinite2=True,) pt1Eave1 = DraftGeomUtils.findIntersection(edgeEave1,edgeEave2,infinite1=True,infinite2=True,) edgeEave1 = DraftGeomUtils.edg(FreeCAD.Vector(pt0Eave1[0]),FreeCAD.Vector(pt1Eave1[0])) edgeRidge0 = DraftGeomUtils.offset(edges[i-1],self.getPerpendicular(vec0,rotEdge0,profil0["run"])) edgeRidge1 = DraftGeomUtils.offset(edges[i],self.getPerpendicular(vec1,rotEdge1,profil1["run"])) edgeRidge2 = DraftGeomUtils.offset(edgesForward[i+1],self.getPerpendicular(vec2,rotEdge2,profil2["run"])) midpoint = DraftGeomUtils.findMidpoint(edges[i]) pt0Edge1 = edges[i].Vertexes[0].Point pt1Edge1 = edges[i].Vertexes[-1].Point print("Analyse profil " + str(i)) if profil1["angle"] != 90.: if profil2["angle"] == 90. : print("situation a droite : pignon") ptsPaneProject.append(FreeCAD.Vector(pt1Eave1[0])) point = DraftGeomUtils.findIntersection(edgeRidge1,edgeEave2,infinite1=True,infinite2=True,) ptsPaneProject.append(FreeCAD.Vector(point[0])) elif profil1["height"] == profil2["height"] : print("situation a droite : ht1 = ht2") ptInterRidges = DraftGeomUtils.findIntersection(edgeRidge1,edgeRidge2,infinite1=True,infinite2=True,) edgeHip = DraftGeomUtils.edg(FreeCAD.Vector(ptInterRidges[0]),pt1Edge1) ptInterHipEave1 = DraftGeomUtils.findIntersection(edgeHip,edgeEave1,infinite1=True,infinite2=False,) if ptInterHipEave1: ptsPaneProject.append(FreeCAD.Vector(ptInterHipEave1[0])) else: ptInterHipEave2 = DraftGeomUtils.findIntersection(edgeHip,edgeEave2,infinite1=True,infinite2=True,) ptsPaneProject.append(FreeCAD.Vector(pt1Eave1[0])) ptsPaneProject.append(FreeCAD.Vector(ptInterHipEave2[0])) ptsPaneProject.append(FreeCAD.Vector(ptInterRidges[0])) elif profil1["height"] > profil2["height"]: print("situation a droite : ht1 > ht2") dec = profil2["height"]/math.tan(math.radians(profil1["angle"])) edgeRidge2OnPane = DraftGeomUtils.offset(edges[i],self.getPerpendicular(vec1,rotEdge1,dec)) ptInter1 = DraftGeomUtils.findIntersection(edgeRidge2,edgeRidge2OnPane,infinite1=True,infinite2=True,) edgeHip = DraftGeomUtils.edg(FreeCAD.Vector(ptInter1[0]),pt1Edge1) ptInterHipEave1 = DraftGeomUtils.findIntersection(edgeHip,edgeEave1,infinite1=True,infinite2=False,) if ptInterHipEave1: ptsPaneProject.append(FreeCAD.Vector(ptInterHipEave1[0])) else: ptInterHipEave2 = DraftGeomUtils.findIntersection(edgeHip,edgeEave2,infinite1=True,infinite2=True,) ptsPaneProject.append(FreeCAD.Vector(pt1Eave1[0])) ptsPaneProject.append(FreeCAD.Vector(ptInterHipEave2[0])) ptsPaneProject.append(FreeCAD.Vector(ptInter1[0])) ptInter2 = edgeHip.Vertexes[0].Point vecInterRidges = DraftGeomUtils.findPerpendicular(ptInter2, [edgeRidge1.Edges[0],], force=0) ptInterRidges = ptInter2.add(vecInterRidges[0]) ptsPaneProject.append(FreeCAD.Vector(ptInterRidges)) elif profil1["height"] < profil2["height"]: print("situation a droite : ht1 < ht2") dec = profil1["height"]/math.tan(math.radians(profil2["angle"])) edgeRidge2OnPane = DraftGeomUtils.offset(edgesForward[i+1],self.getPerpendicular(vec2,rotEdge2,dec)) ptInter1 = DraftGeomUtils.findIntersection(edgeRidge1,edgeRidge2OnPane,infinite1=True,infinite2=True,) edgeHip = DraftGeomUtils.edg(FreeCAD.Vector(ptInter1[0]),pt1Edge1) ptInterHipEave1 = DraftGeomUtils.findIntersection(edgeHip,edgeEave1,infinite1=True,infinite2=False,) if ptInterHipEave1: ptsPaneProject.append(FreeCAD.Vector(ptInterHipEave1[0])) else: ptInterHipEave2 = DraftGeomUtils.findIntersection(edgeHip,edgeEave2,infinite1=True,infinite2=True,) ptsPaneProject.append(FreeCAD.Vector(pt1Eave1[0])) ptsPaneProject.append(FreeCAD.Vector(ptInterHipEave2[0])) ptsPaneProject.append(FreeCAD.Vector(ptInter1[0])) ptInterRidges = DraftGeomUtils.findIntersection(edgeRidge1,edgeRidge2,infinite1=True,infinite2=True,) ptsPaneProject.append(FreeCAD.Vector(ptInterRidges[0])) else: print("Cas de figure non pris en charge") if profil0["angle"] == 90. : print("situation a gauche : pignon") point = DraftGeomUtils.findIntersection(edgeRidge1,edgeEave0,infinite1=True,infinite2=True,) ptsPaneProject.append(FreeCAD.Vector(point[0])) ptsPaneProject.append(FreeCAD.Vector(pt0Eave1[0])) elif profil0["height"] == profil1["height"]: print("situation a gauche : ht1 = ht0") edgeRidge0 = DraftGeomUtils.offset(edges[i-1],self.getPerpendicular(vec0,rotEdge0,profil0["run"])) ptInterRidges = DraftGeomUtils.findIntersection(edgeRidge1,edgeRidge0,infinite1=True,infinite2=True,) ptsPaneProject.append(FreeCAD.Vector(ptInterRidges[0])) edgeHip = DraftGeomUtils.edg(FreeCAD.Vector(ptInterRidges[0]),pt0Edge1) ptInterHipEave3 = DraftGeomUtils.findIntersection(edgeHip,edgeEave1,infinite1=True,infinite2=False,) if ptInterHipEave3: ptsPaneProject.append(FreeCAD.Vector(ptInterHipEave3[0])) else: ptInterHipEave4 = DraftGeomUtils.findIntersection(edgeHip,edgeEave0,infinite1=True,infinite2=True,) ptsPaneProject.append(FreeCAD.Vector(ptInterHipEave4[0])) ptsPaneProject.append(FreeCAD.Vector(pt0Eave1[0])) elif profil1["height"] > profil0["height"]: print("situation a gauche : ht1 > ht0") dec = profil0["height"]/math.tan(math.radians(profil1["angle"])) edgeRidge0OnPane = DraftGeomUtils.offset(edges[i],self.getPerpendicular(vec1,rotEdge1,dec)) ptInter1 = DraftGeomUtils.findIntersection(edgeRidge0OnPane,edgeRidge0,infinite1=True,infinite2=True,) edgeHip = DraftGeomUtils.edg(FreeCAD.Vector(ptInter1[0]),pt0Edge1) ptInter2 = edgeHip.Vertexes[0].Point vecInterRidges = DraftGeomUtils.findPerpendicular(ptInter2, [edgeRidge1.Edges[0],], force=0) ptInterRidges = ptInter2.add(vecInterRidges[0]) ptsPaneProject.append(FreeCAD.Vector(ptInterRidges)) ptsPaneProject.append(FreeCAD.Vector(ptInter1[0])) ptInterHipEave3 = DraftGeomUtils.findIntersection(edgeHip,edgeEave1,infinite1=True,infinite2=False,) if ptInterHipEave3: ptsPaneProject.append(FreeCAD.Vector(ptInterHipEave3[0])) else: ptInterHipEave4 = DraftGeomUtils.findIntersection(edgeHip,edgeEave0,infinite1=True,infinite2=True,) ptsPaneProject.append(FreeCAD.Vector(ptInterHipEave4[0])) ptsPaneProject.append(FreeCAD.Vector(pt0Eave1[0])) elif profil1["height"] < profil0["height"]: print("situation a gauche : ht1 < ht0") dec = profil1["height"]/math.tan(math.radians(profil0["angle"])) edgeRidge0OnPane = DraftGeomUtils.offset(edges[i-1],self.getPerpendicular(vec0,rotEdge0,dec)) ptInterRidges = DraftGeomUtils.findIntersection(edgeRidge0OnPane,edgeRidge1,infinite1=True,infinite2=True,) ptsPaneProject.append(FreeCAD.Vector(ptInterRidges[0])) edgeHip = DraftGeomUtils.edg(FreeCAD.Vector(ptInterRidges[0]),pt0Edge1) ptInterHipEave3 = DraftGeomUtils.findIntersection(edgeHip,edgeEave1,infinite1=True,infinite2=False,) if ptInterHipEave3: ptsPaneProject.append(FreeCAD.Vector(ptInterHipEave3[0])) else: ptInterHipEave4 = DraftGeomUtils.findIntersection(edgeHip,edgeEave0,infinite1=True,infinite2=True,) ptsPaneProject.append(FreeCAD.Vector(ptInterHipEave4[0])) ptsPaneProject.append(FreeCAD.Vector(pt0Eave1[0])) else: print("Cas de figure non pris en charge") ptsPaneProject = DraftVecUtils.removeDoubles(ptsPaneProject) print("ptsPaneProject",ptsPaneProject) print("Fin Analyse profil " + str(i)) self.profilsDico[i]["points"] = ptsPaneProject lp = len(ptsPaneProject) ptsPaneProject.append(ptsPaneProject[0]) edgesWire = [] for i in range(lp): edge = Part.makeLine(ptsPaneProject[i],ptsPaneProject[i+1]) edgesWire.append(edge) wire = Part.Wire(edgesWire) d = wire.BoundBox.DiagonalLength thicknessV = profil1["thickness"]/(math.cos(math.radians(profil1["angle"]))) overhangV = profil1["overhang"]*math.tan(math.radians(profil1["angle"])) if wire.isClosed(): f = Part.Face(wire) f = f.extrude(FreeCAD.Vector(0,0,profil1["height"]+2*thicknessV+2*overhangV)) f.translate(FreeCAD.Vector(0.0,0.0,-2*overhangV)) ptsPaneProfil=[FreeCAD.Vector(-profil1["overhang"],-overhangV,0.0),FreeCAD.Vector(profil1["run"],profil1["height"],0.0),FreeCAD.Vector(profil1["run"],profil1["height"]+thicknessV,0.0),FreeCAD.Vector(-profil1["overhang"],-overhangV+thicknessV,0.0)] self.createProfilShape (ptsPaneProfil, midpoint, rotEdge1, vec1, profil1["run"], d, self.shps, f) ## subVolume shape ptsSubVolumeProfil=[FreeCAD.Vector(-profil1["overhang"],-overhangV,0.0),FreeCAD.Vector(profil1["run"],profil1["height"],0.0),FreeCAD.Vector(profil1["run"],profil1["height"]+10000,0.0),FreeCAD.Vector(0.0,profil1["height"]+10000,0.0)] self.createProfilShape (ptsSubVolumeProfil, midpoint, rotEdge1, vec1, profil1["run"], d, self.subVolshps, f) else: #TODO PIGNON pass ## SubVolume self.sub = self.subVolshps.pop() for s in self.subVolshps: self.sub = self.sub.fuse(s) self.sub = self.sub.removeSplitter() if not self.sub.isNull(): if not DraftGeomUtils.isNull(pl): self.sub.Placement = pl ## BaseVolume base = Part.makeCompound(self.shps) if not base.isNull(): if not DraftGeomUtils.isNull(pl): base.Placement = pl base = self.processSubShapes(obj,base) if base: if not base.isNull(): obj.Shape = base
def getPath(edges=[],wires=[],pathname=None): import Part,DraftGeomUtils svg = "<path " if pathname is None: svg += 'id="%s" ' % obj.Name elif pathname != "": svg += 'id="%s" ' % pathname svg += ' d="' if not wires: egroups = Part.sortEdges(edges) else: egroups = [] for w in wires: w1=w.copy() w1.fixWire() egroups.append(Part.__sortEdges__(w1.Edges)) for egroupindex, edges in enumerate(egroups): edata = "" vs=() #skipped for the first edge for edgeindex,e in enumerate(edges): previousvs = vs # vertexes of an edge (reversed if needed) vs = e.Vertexes if previousvs: if (vs[0].Point-previousvs[-1].Point).Length > 1e-6: vs.reverse() if edgeindex == 0: v = getProj(vs[0].Point, plane) edata += 'M '+ str(v.x) +' '+ str(v.y) + ' ' else: if (vs[0].Point-previousvs[-1].Point).Length > 1e-6: raise ValueError('edges not ordered') iscircle = DraftGeomUtils.geomType(e) == "Circle" isellipse = DraftGeomUtils.geomType(e) == "Ellipse" if iscircle or isellipse: import math if hasattr(FreeCAD,"DraftWorkingPlane"): drawing_plane_normal = FreeCAD.DraftWorkingPlane.axis else: drawing_plane_normal = FreeCAD.Vector(0,0,1) if plane: drawing_plane_normal = plane.axis c = e.Curve if round(c.Axis.getAngle(drawing_plane_normal),2) in [0,3.14]: occversion = Part.OCC_VERSION.split(".") done = False if (int(occversion[0]) >= 7) and (int(occversion[1]) >= 1): # if using occ >= 7.1, use HLR algorithm import Drawing snip = Drawing.projectToSVG(e,drawing_plane_normal) if snip: try: a = "A " + snip.split("path d=\"")[1].split("\"")[0].split("A")[1] except: pass else: edata += a done = True if not done: if len(e.Vertexes) == 1 and iscircle: #complete curve svg = getCircle(e) return svg elif len(e.Vertexes) == 1 and isellipse: #svg = getEllipse(e) #return svg endpoints = (getProj(c.value((c.LastParameter-\ c.FirstParameter)/2.0), plane), \ getProj(vs[-1].Point, plane)) else: endpoints = (getProj(vs[-1].Point), plane) # arc if iscircle: rx = ry = c.Radius rot = 0 else: #ellipse rx = c.MajorRadius ry = c.MinorRadius rot = math.degrees(c.AngleXU * (c.Axis * \ FreeCAD.Vector(0,0,1))) if rot > 90: rot -=180 if rot < -90: rot += 180 #be careful with the sweep flag flag_large_arc = (((e.ParameterRange[1] - \ e.ParameterRange[0]) / math.pi) % 2) > 1 #flag_sweep = (c.Axis * drawing_plane_normal >= 0) \ # == (e.LastParameter > e.FirstParameter) # == (e.Orientation == "Forward") # other method: check the direction of the angle between tangents t1 = e.tangentAt(e.FirstParameter) t2 = e.tangentAt(e.FirstParameter + (e.LastParameter-e.FirstParameter)/10) flag_sweep = (DraftVecUtils.angle(t1,t2,drawing_plane_normal) < 0) for v in endpoints: edata += 'A %s %s %s %s %s %s %s ' % \ (str(rx),str(ry),str(rot),\ str(int(flag_large_arc)),\ str(int(flag_sweep)),str(v.x),str(v.y)) else: edata += getDiscretized(e, plane) elif DraftGeomUtils.geomType(e) == "Line": v = getProj(vs[-1].Point, plane) edata += 'L '+ str(v.x) +' '+ str(v.y) + ' ' else: bspline=e.Curve.toBSpline(e.FirstParameter,e.LastParameter) if bspline.Degree > 3 or bspline.isRational(): try: bspline=bspline.approximateBSpline(0.05,50, 3,'C0') except RuntimeError: print("Debug: unable to approximate bspline") if bspline.Degree <= 3 and not bspline.isRational(): for bezierseg in bspline.toBezier(): if bezierseg.Degree>3: #should not happen raise AssertionError elif bezierseg.Degree==1: edata +='L ' elif bezierseg.Degree==2: edata +='Q ' elif bezierseg.Degree==3: edata +='C ' for pole in bezierseg.getPoles()[1:]: v = getProj(pole, plane) edata += str(v.x) +' '+ str(v.y) + ' ' else: print("Debug: one edge (hash ",e.hashCode(),\ ") has been discretized with parameter 0.1") for linepoint in bspline.discretize(0.1)[1:]: v = getProj(linepoint, plane) edata += 'L '+ str(v.x) +' '+ str(v.y) + ' ' if fill != 'none': edata += 'Z ' if edata in pathdata: # do not draw a path on another identical path return "" else: svg += edata pathdata.append(edata) svg += '" ' svg += 'stroke="' + stroke + '" ' svg += 'stroke-width="' + str(linewidth) + ' px" ' svg += 'style="stroke-width:'+ str(linewidth) svg += ';stroke-miterlimit:4' svg += ';stroke-dasharray:' + lstyle svg += ';fill:' + fill try: svg += ';fill-opacity:' + str(fill_opacity) except NameError: pass svg += ';fill-rule: evenodd "' svg += '/>\n' return svg