def doTasks(): """Execute the commands stored in the lists. The lists are `itinerary`, `commitlist` and `afteritinerary`. """ if _DEBUG: _msg("Debug: doing delayed tasks.\n" "itinerary: {0}\n" "commitlist: {1}\n" "afteritinerary: {2}\n".format(todo.itinerary, todo.commitlist, todo.afteritinerary)) try: for f, arg in todo.itinerary: try: if _DEBUG_inner: _msg("Debug: executing.\n" "function: {}\n".format(f)) if arg or (arg is False): f(arg) else: f() except Exception: _log(traceback.format_exc()) wrn = ("ToDo.doTasks, Unexpected error:\n" "{0}\n" "in {1}({2})".format(sys.exc_info()[0], f, arg)) _wrn(wrn) except ReferenceError: _wrn("Debug: ToDo.doTasks: " "queue contains a deleted object, skipping") todo.itinerary = [] if todo.commitlist: for name, func in todo.commitlist: if six.PY2: if isinstance(name, six.text_type): name = name.encode("utf8") if _DEBUG_inner: _msg("Debug: committing.\n" "name: {}\n".format(name)) try: name = str(name) FreeCAD.ActiveDocument.openTransaction(name) if isinstance(func, list): for string in func: FreeCADGui.doCommand(string) else: func() FreeCAD.ActiveDocument.commitTransaction() except Exception: _log(traceback.format_exc()) wrn = ("ToDo.doTasks, Unexpected error:\n" "{0}\n" "in {1}".format(sys.exc_info()[0], func)) _wrn(wrn) # Restack Draft screen widgets after creation if hasattr(FreeCADGui, "Snapper"): FreeCADGui.Snapper.restack() todo.commitlist = [] for f, arg in todo.afteritinerary: try: if _DEBUG_inner: _msg("Debug: executing after.\n" "function: {}\n".format(f)) if arg: f(arg) else: f() except Exception: _log(traceback.format_exc()) wrn = ("ToDo.doTasks, Unexpected error:\n" "{0}\n" "in {1}({2})".format(sys.exc_info()[0], f, arg)) _wrn(wrn) todo.afteritinerary = []
def get_svg(obj, scale=1, linewidth=0.35, fontsize=12, fillstyle="shape color", direction=None, linestyle=None, color=None, linespacing=None, techdraw=False, rotation=0, fillspaces=False, override=True): """Return a string containing an SVG representation of the object. Paramaeters ----------- scale: float, optional It defaults to 1. It allows scaling line widths down, so they are resolution-independent. linewidth: float, optional It defaults to 0.35. fontsize: float, optional It defaults to 12, which is interpreted as `pt` unit (points). It is used if the given object contains any text. fillstyle: str, optional It defaults to 'shape color'. direction: Base::Vector3, optional It defaults to `None`. It is an arbitrary projection vector or a `WorkingPlane.Plane` instance. linestyle: optional It defaults to `None`. color: optional It defaults to `None`. linespacing: float, optional It defaults to `None`. techdraw: bool, optional It defaults to `False`. If it is `True`, it sets some options for generating SVG strings for displaying inside TechDraw. rotation: float, optional It defaults to 0. fillspaces: bool, optional It defaults to `False`. override: bool, optional It defaults to `True`. """ # If this is a group, recursively call this function to gather # all the SVG strings from the contents of the group if hasattr(obj, "isDerivedFrom"): if (obj.isDerivedFrom("App::DocumentObjectGroup") or utils.get_type(obj) == "Layer"): svg = "" for child in obj.Group: svg += get_svg(child, scale, linewidth, fontsize, fillstyle, direction, linestyle, color, linespacing, techdraw, rotation, fillspaces, override) return svg pathdata = [] svg = "" linewidth = float(linewidth)/scale if not override: if hasattr(obj, "ViewObject") and hasattr(obj.ViewObject, "LineWidth"): if hasattr(obj.ViewObject.LineWidth, "Value"): lw = obj.ViewObject.LineWidth.Value else: lw = obj.ViewObject.LineWidth linewidth = lw * linewidth fontsize = (float(fontsize)/scale)/2 if linespacing: linespacing = float(linespacing)/scale else: linespacing = 0.5 # print(obj.Label, "line spacing", linespacing, "scale", scale) # The number of times the dots are smaller than the arrow size pointratio = 0.75 plane = None if direction: if isinstance(direction, App.Vector): if direction != App.Vector(0, 0, 0): plane = WorkingPlane.plane() plane.alignToPointAndAxis_SVG(App.Vector(0, 0, 0), direction.negative().negative(), 0) else: raise ValueError("'direction' cannot be: Vector(0, 0, 0)") elif isinstance(direction, WorkingPlane.plane): plane = direction stroke = "#000000" if color and override: if "#" in color: stroke = color else: stroke = utils.get_rgb(color) elif App.GuiUp: # find print color pc = get_print_color(obj) if pc: stroke = utils.get_rgb(pc) # get line color elif hasattr(obj, "ViewObject"): if hasattr(obj.ViewObject, "LineColor"): stroke = utils.get_rgb(obj.ViewObject.LineColor) elif hasattr(obj.ViewObject, "TextColor"): stroke = utils.get_rgb(obj.ViewObject.TextColor) lstyle = "none" if override: lstyle = get_line_style(linestyle, scale) else: if hasattr(obj, "ViewObject") and hasattr(obj.ViewObject, "DrawStyle"): lstyle = get_line_style(obj.ViewObject.DrawStyle, scale) if not obj: pass elif isinstance(obj, Part.Shape): svg = _svg_shape(svg, obj, plane, fillstyle, pathdata, stroke, linewidth, lstyle) elif utils.get_type(obj) in ["Dimension", "LinearDimension"]: svg = _svg_dimension(obj, plane, scale, linewidth, fontsize, stroke, pointratio, techdraw, rotation) elif utils.get_type(obj) == "AngularDimension": if not App.GuiUp: _wrn("Export of dimensions to SVG is only available in GUI mode") if App.GuiUp: if obj.ViewObject.Proxy: if hasattr(obj.ViewObject.Proxy, "circle"): prx = obj.ViewObject.Proxy # drawing arc fill = "none" if obj.ViewObject.DisplayMode == "2D": svg += get_path(obj, plane, fill, pathdata, stroke, linewidth, lstyle, fill_opacity=None, edges=[prx.circle]) else: if hasattr(prx, "circle1"): svg += get_path(obj, plane, fill, pathdata, stroke, linewidth, lstyle, fill_opacity=None, edges=[prx.circle1]) svg += get_path(obj, plane, fill, pathdata, stroke, linewidth, lstyle, fill_opacity=None, edges=[prx.circle2]) else: svg += get_path(obj, plane, fill, pathdata, stroke, linewidth, lstyle, fill_opacity=None, edges=[prx.circle]) # drawing arrows if hasattr(obj.ViewObject, "ArrowType"): p2 = get_proj(prx.p2, plane) p3 = get_proj(prx.p3, plane) arrowsize = obj.ViewObject.ArrowSize.Value/pointratio arrowlength = 4*obj.ViewObject.ArrowSize.Value _v1a = prx.circle.valueAt(prx.circle.FirstParameter + arrowlength) _v1b = prx.circle.valueAt(prx.circle.FirstParameter) _v2a = prx.circle.valueAt(prx.circle.LastParameter - arrowlength) _v2b = prx.circle.valueAt(prx.circle.LastParameter) u1 = get_proj(_v1a - _v1b, plane) u2 = get_proj(_v2a - _v2b, 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 += get_arrow(obj, obj.ViewObject.ArrowType, p2, arrowsize, stroke, linewidth, angle1) svg += get_arrow(obj, obj.ViewObject.ArrowType, p3, arrowsize, stroke, linewidth, angle2) # drawing text if obj.ViewObject.DisplayMode == "2D": _diff = (prx.circle.LastParameter - prx.circle.FirstParameter) t = prx.circle.tangentAt(prx.circle.FirstParameter + _diff/2.0) t = get_proj(t, plane) tangle = DraftVecUtils.angle(t) if (tangle <= -math.pi/2) or (tangle > math.pi/2): tangle = tangle + math.pi _diff = (prx.circle.LastParameter - prx.circle.FirstParameter) _va = prx.circle.valueAt(prx.circle.FirstParameter + _diff/2.0) tbase = get_proj(_va, plane) _v = App.Vector(0, 2.0/scale, 0) tbase = tbase + DraftVecUtils.rotate(_v, tangle) # print(tbase) else: tangle = 0 tbase = get_proj(prx.tbase, plane) svg += svgtext.get_text(plane, techdraw, stroke, fontsize, obj.ViewObject.FontName, tangle, tbase, prx.string) elif utils.get_type(obj) == "Label": if getattr(obj.ViewObject, "Line", True): # Some Labels may have no Line property # Draw multisegment line proj_points = list(map(lambda x: get_proj(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 ' svg_path += 'fill="none" ' svg_path += 'stroke="{}" '.format(stroke) svg_path += 'stroke-width="{}" '.format(linewidth) svg_path += 'd="{}"'.format(path_dir_str) svg_path += '/>' 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 = App.Vector(obj.Points[-1] - obj.Points[-2]) _v = get_proj(last_segment, plane) angle = -DraftVecUtils.angle(_v) + math.pi svg += get_arrow(obj, obj.ViewObject.ArrowType, proj_points[-1], obj.ViewObject.ArrowSize.Value/pointratio, stroke, linewidth, angle) if not App.GuiUp: _wrn("Export of texts to SVG is only available in GUI mode") # print text if App.GuiUp: fontname = obj.ViewObject.TextFont position = get_proj(obj.Placement.Base, plane) rotation = obj.Placement.Rotation justification = obj.ViewObject.TextAlignment text = obj.Text svg += svgtext.get_text(plane, techdraw, stroke, fontsize, fontname, rotation, position, text, linespacing, justification) elif utils.get_type(obj) in ["Annotation", "DraftText", "Text"]: # returns an svg representation of a document annotation if not App.GuiUp: _wrn("Export of texts to SVG is only available in GUI mode") if App.GuiUp: n = obj.ViewObject.FontName if utils.get_type(obj) == "Annotation": p = get_proj(obj.Position, plane) r = obj.ViewObject.Rotation.getValueAs("rad") t = obj.LabelText else: # DraftText (old) or Text (new, 0.19) p = get_proj(obj.Placement.Base, plane) r = obj.Placement.Rotation t = obj.Text j = obj.ViewObject.Justification svg += svgtext.get_text(plane, techdraw, stroke, fontsize, n, r, p, t, linespacing, j) elif utils.get_type(obj) == "Axis": # returns the SVG representation of an Arch Axis system if not App.GuiUp: _wrn("Export of axes to SVG is only available in GUI mode") if App.GuiUp: vobj = obj.ViewObject lorig = lstyle fill = 'none' rad = vobj.BubbleSize.Value/2 n = 0 for e in obj.Shape.Edges: lstyle = lorig svg += get_path(obj, plane, fill, pathdata, stroke, linewidth, lstyle, fill_opacity=None, edges=[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 += get_circle(plane, fill, stroke, linewidth, lstyle, Part.makeCircle(rad, center)) if (hasattr(vobj.Proxy, "bubbletexts") and len(vobj.Proxy.bubbletexts) >= n): bubb = vobj.Proxy.bubbletexts svg += '<text ' svg += 'fill="{}" '.format(stroke) svg += 'font-size="{}" '.format(rad) svg += 'style="text-anchor:middle;' svg += 'text-align:center;' svg += 'font-family: sans;" ' svg += 'transform="' svg += 'translate({},{}) '.format(center.x + rad/4.0, center.y - rad/3.0) svg += 'scale(1,-1)"> ' svg += '<tspan>' svg += bubb[n].string.getValues()[0] svg += '</tspan>\n' svg += '</text>\n' n += 1 lstyle = lorig elif utils.get_type(obj) == "Pipe": fill = stroke if obj.Base and obj.Diameter: svg += get_path(obj, plane, fill, pathdata, stroke, linewidth, lstyle, fill_opacity=None, edges=obj.Base.Shape.Edges) for f in obj.Shape.Faces: if len(f.Edges) == 1: if isinstance(f.Edges[0].Curve, Part.Circle): svg += get_circle(plane, fill, stroke, linewidth, lstyle, f.Edges[0]) elif utils.get_type(obj) == "Rebar": fill = "none" basewire = obj.Base.Shape.Wires[0].copy() # Not applying rounding because the results are not correct # if hasattr(obj, "Rounding") and obj.Rounding: # basewire = DraftGeomUtils.filletWire( # basewire, obj.Rounding * obj.Diameter.Value # ) wires = [] for placement in obj.PlacementList: wire = basewire.copy() wire.Placement = placement.multiply(basewire.Placement) wires.append(wire) svg += get_path(obj, plane, fill, pathdata, stroke, linewidth, lstyle, fill_opacity=None, wires=wires) elif utils.get_type(obj) == "PipeConnector": pass elif utils.get_type(obj) == "Space": fill_opacity = 1 # returns an SVG fragment for the text of a space if not App.GuiUp: _wrn("Export of spaces to SVG is only available in GUI mode") if App.GuiUp: vobj = obj.ViewObject if fillspaces and hasattr(obj, "Proxy"): if not hasattr(obj.Proxy, "face"): obj.Proxy.getArea(obj, notouch=True) if hasattr(obj.Proxy, "face"): # setting fill if App.GuiUp: fill = utils.get_rgb(vobj.ShapeColor, testbw=False) fill_opacity = 1 - vobj.Transparency / 100.0 else: fill = "#888888" svg += get_path(obj, plane, fill, pathdata, stroke, linewidth, lstyle, fill_opacity=fill_opacity, wires=[obj.Proxy.face.OuterWire]) c = utils.get_rgb(vobj.TextColor) n = vobj.FontName a = 0 if rotation != 0: a = math.radians(rotation) t1 = vobj.Proxy.text1.string.getValues() t2 = vobj.Proxy.text2.string.getValues() scale = vobj.FirstLine.Value/vobj.FontSize.Value f1 = fontsize * scale if round(plane.axis.getAngle(App.Vector(0,0,1)),2) not in [0,3.14]: # if not in XY view, place the label at center p2 = obj.Shape.CenterOfMass else: _v = vobj.Proxy.coords.translation.getValue().getValue() p2 = obj.Placement.multVec(App.Vector(_v)) _h = vobj.Proxy.header.translation.getValue().getValue() lspc = App.Vector(_h) p1 = p2 + lspc j = vobj.TextAlign t3 = svgtext.get_text(plane, techdraw, c, f1, n, a, get_proj(p1, plane), t1, linespacing, j, flip=True) svg += t3 if t2: ofs = App.Vector(0, -lspc.Length, 0) if a: Z = App.Vector(0, 0, 1) ofs = App.Rotation(Z, -rotation).multVec(ofs) t4 = svgtext.get_text(plane, techdraw, c, fontsize, n, a, get_proj(p1, plane).add(ofs), t2, linespacing, j, flip=True) svg += t4 elif hasattr(obj, 'Shape'): # In the past we tested for a Part Feature # elif obj.isDerivedFrom('Part::Feature'): # # however, this doesn't work for App::Links; instead we # test for a 'Shape'. All Part::Features should have a Shape, # and App::Links can have one as well. if obj.Shape.isNull(): return '' fill_opacity = 1 # setting fill if obj.Shape.Faces: if App.GuiUp: try: m = obj.ViewObject.DisplayMode except AttributeError: m = None vobj = obj.ViewObject if m != "Wireframe": if fillstyle == "shape color": fill = utils.get_rgb(vobj.ShapeColor, testbw=False) fill_opacity = 1 - vobj.Transparency / 100.0 elif fillstyle in ("none",None): fill = "none" else: fill = 'url(#'+fillstyle+')' svg += get_pattern(fillstyle) else: fill = "none" else: fill = "#888888" else: fill = 'none' if len(obj.Shape.Vertexes) > 1: wiredEdges = [] if obj.Shape.Faces: for i, f in enumerate(obj.Shape.Faces): # place outer wire first wires = [f.OuterWire] wires.extend([w for w in f.Wires if w.hashCode() != f.OuterWire.hashCode()]) svg += get_path(obj, plane, fill, pathdata, stroke, linewidth, lstyle, fill_opacity=fill_opacity, wires=f.Wires, pathname='%s_f%04d' % (obj.Name, i)) wiredEdges.extend(f.Edges) else: for i, w in enumerate(obj.Shape.Wires): svg += get_path(obj, plane, fill, pathdata, stroke, linewidth, lstyle, fill_opacity=fill_opacity, edges=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) is None: svg += get_path(obj, plane, fill, pathdata, stroke, linewidth, lstyle, fill_opacity=fill_opacity, edges=[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 = get_circle(plane, fill, stroke, linewidth, lstyle, obj.Shape.Edges[0]) else: svg = get_path(obj, plane, fill, pathdata, stroke, linewidth, lstyle, fill_opacity=fill_opacity, edges=obj.Shape.Edges) if (App.GuiUp and hasattr(obj.ViewObject, "EndArrow") and obj.ViewObject.EndArrow and hasattr(obj.ViewObject, "ArrowType") and len(obj.Shape.Vertexes) > 1): p1 = get_proj(obj.Shape.Vertexes[-1].Point, plane) p2 = get_proj(obj.Shape.Vertexes[-2].Point, plane) angle = -DraftVecUtils.angle(p2 - p1) arrowsize = obj.ViewObject.ArrowSize.Value/pointratio svg += get_arrow(obj, obj.ViewObject.ArrowType, p1, arrowsize, stroke, linewidth, angle) # techdraw expects bottom-to-top coordinates if techdraw: svg = '<g transform ="scale(1,-1)">\n ' + svg + '</g>\n' return svg
def _create_objects(doc=None, font_file="/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf" ): """Create the objects of the test file. Parameters ---------- doc: App::Document, optional It defaults to `None`, which then defaults to the current active document, or creates a new document. """ if not doc: doc = App.activeDocument() if not doc: doc = App.newDocument() # Line, wire, and fillet _msg(16 * "-") _msg("Line") Draft.make_line(Vector(0, 0, 0), Vector(500, 500, 0)) t_xpos = -50 t_ypos = -200 _set_text(["Line"], Vector(t_xpos, t_ypos, 0)) _msg(16 * "-") _msg("Wire") Draft.make_wire( [Vector(500, 0, 0), Vector(1000, 500, 0), Vector(1000, 1000, 0)]) t_xpos += 500 _set_text(["Wire"], Vector(t_xpos, t_ypos, 0)) _msg(16 * "-") _msg("Fillet") line_h_1 = Draft.make_line(Vector(1500, 0, 0), Vector(1500, 500, 0)) line_h_2 = Draft.make_line(Vector(1500, 500, 0), Vector(2000, 500, 0)) if App.GuiUp: line_h_1.ViewObject.DrawStyle = "Dotted" line_h_2.ViewObject.DrawStyle = "Dotted" doc.recompute() Draft.make_fillet([line_h_1, line_h_2], 400) t_xpos += 900 _set_text(["Fillet"], Vector(t_xpos, t_ypos, 0)) # Circle, arc, arc by 3 points _msg(16 * "-") _msg("Circle") circle = Draft.make_circle(350) circle.Placement.Base = Vector(2500, 500, 0) t_xpos += 1050 _set_text(["Circle"], Vector(t_xpos, t_ypos, 0)) _msg(16 * "-") _msg("Circular arc") arc = Draft.make_circle(350, startangle=0, endangle=100) arc.Placement.Base = Vector(3200, 500, 0) t_xpos += 800 _set_text(["Circular arc"], Vector(t_xpos, t_ypos, 0)) _msg(16 * "-") _msg("Circular arc 3 points") Draft.make_arc_3points( [Vector(4600, 0, 0), Vector(4600, 800, 0), Vector(4000, 1000, 0)]) t_xpos += 600 _set_text(["Circular arc 3 points"], Vector(t_xpos, t_ypos, 0)) # Ellipse, polygon, rectangle _msg(16 * "-") _msg("Ellipse") ellipse = Draft.make_ellipse(500, 300) ellipse.Placement.Base = Vector(5500, 250, 0) t_xpos += 1600 _set_text(["Ellipse"], Vector(t_xpos, t_ypos, 0)) _msg(16 * "-") _msg("Polygon") polygon = Draft.make_polygon(5, 250) polygon.Placement.Base = Vector(6500, 500, 0) t_xpos += 950 _set_text(["Polygon"], Vector(t_xpos, t_ypos, 0)) _msg(16 * "-") _msg("Rectangle") rectangle = Draft.make_rectangle(500, 1000, 0) rectangle.Placement.Base = Vector(7000, 0, 0) t_xpos += 650 _set_text(["Rectangle"], Vector(t_xpos, t_ypos, 0)) # Text _msg(16 * "-") _msg("Text") text = Draft.make_text(["Testing", "text"], Vector(7700, 500, 0)) if App.GuiUp: text.ViewObject.FontSize = 100 t_xpos += 700 _set_text(["Text"], Vector(t_xpos, t_ypos, 0)) # Linear dimension _msg(16 * "-") _msg("Linear dimension") line = Draft.make_wire([Vector(8700, 200, 0), Vector(8700, 1200, 0)]) dimension = Draft.make_linear_dimension(Vector(8600, 200, 0), Vector(8600, 1000, 0), Vector(8400, 750, 0)) if App.GuiUp: dimension.ViewObject.ArrowSize = 15 dimension.ViewObject.ExtLines = 1000 dimension.ViewObject.ExtOvershoot = 100 dimension.ViewObject.DimOvershoot = 50 dimension.ViewObject.FontSize = 100 dimension.ViewObject.ShowUnit = False doc.recompute() dim_obj = Draft.make_linear_dimension_obj(line, 1, 2, Vector(9000, 750, 0)) if App.GuiUp: dim_obj.ViewObject.ArrowSize = 15 dim_obj.ViewObject.ArrowType = "Arrow" dim_obj.ViewObject.ExtLines = 100 dim_obj.ViewObject.ExtOvershoot = 100 dim_obj.ViewObject.DimOvershoot = 50 dim_obj.ViewObject.FontSize = 100 dim_obj.ViewObject.ShowUnit = False t_xpos += 680 _set_text(["Dimension"], Vector(t_xpos, t_ypos, 0)) # Radius and diameter dimension _msg(16 * "-") _msg("Radius and diameter dimension") arc_h = Draft.make_circle(500, startangle=0, endangle=90) arc_h.Placement.Base = Vector(9500, 0, 0) doc.recompute() dimension_r = Draft.make_radial_dimension_obj(arc_h, 1, "radius", Vector(9750, 200, 0)) if App.GuiUp: dimension_r.ViewObject.ArrowSize = 15 dimension_r.ViewObject.FontSize = 100 dimension_r.ViewObject.ShowUnit = False arc_h2 = Draft.make_circle(450, startangle=-120, endangle=80) arc_h2.Placement.Base = Vector(10000, 1000, 0) doc.recompute() dimension_d = Draft.make_radial_dimension_obj(arc_h2, 1, "diameter", Vector(10750, 900, 0)) if App.GuiUp: dimension_d.ViewObject.ArrowSize = 15 dimension_d.ViewObject.FontSize = 100 dimension_d.ViewObject.ShowUnit = False t_xpos += 950 _set_text(["Radius dimension", "Diameter dimension"], Vector(t_xpos, t_ypos, 0)) # Angular dimension _msg(16 * "-") _msg("Angular dimension") Draft.make_line(Vector(10500, 300, 0), Vector(11500, 1000, 0)) Draft.make_line(Vector(10500, 300, 0), Vector(11500, 0, 0)) angle1 = -20 angle2 = 40 dimension_a = Draft.make_angular_dimension(Vector(10500, 300, 0), [angle1, angle2], Vector(11500, 300, 0)) if App.GuiUp: dimension_a.ViewObject.ArrowSize = 15 dimension_a.ViewObject.FontSize = 100 t_xpos += 1700 _set_text(["Angle dimension"], Vector(t_xpos, t_ypos, 0)) # BSpline _msg(16 * "-") _msg("BSpline") Draft.make_bspline([ Vector(12500, 0, 0), Vector(12500, 500, 0), Vector(13000, 500, 0), Vector(13000, 1000, 0) ]) t_xpos += 1500 _set_text(["BSpline"], Vector(t_xpos, t_ypos, 0)) # Point _msg(16 * "-") _msg("Point") point = Draft.make_point(13500, 500, 0) if App.GuiUp: point.ViewObject.PointSize = 10 t_xpos += 900 _set_text(["Point"], Vector(t_xpos, t_ypos, 0)) # Shapestring _msg(16 * "-") _msg("Shapestring") try: shape_string = Draft.make_shapestring("Testing", font_file, 100) shape_string.Placement.Base = Vector(14000, 500) except Exception: _wrn("Shapestring could not be created") _wrn("Possible cause: the font file may not exist") _wrn(font_file) rect = Draft.make_rectangle(500, 100) rect.Placement.Base = Vector(14000, 500) t_xpos += 600 _set_text(["Shapestring"], Vector(t_xpos, t_ypos, 0)) # Facebinder _msg(16 * "-") _msg("Facebinder") box = doc.addObject("Part::Box", "Cube") box.Length = 200 box.Width = 500 box.Height = 100 box.Placement.Base = Vector(15000, 0, 0) if App.GuiUp: box.ViewObject.Visibility = False facebinder = Draft.make_facebinder([(box, ("Face1", "Face3", "Face6"))]) facebinder.Extrusion = 10 t_xpos += 780 _set_text(["Facebinder"], Vector(t_xpos, t_ypos, 0)) # Cubic bezier curve, n-degree bezier curve _msg(16 * "-") _msg("Cubic bezier") Draft.make_bezcurve([ Vector(15500, 0, 0), Vector(15578, 485, 0), Vector(15879, 154, 0), Vector(15975, 400, 0), Vector(16070, 668, 0), Vector(16423, 925, 0), Vector(16500, 500, 0) ], degree=3) t_xpos += 680 _set_text(["Cubic bezier"], Vector(t_xpos, t_ypos, 0)) _msg(16 * "-") _msg("N-degree bezier") Draft.make_bezcurve([ Vector(16500, 0, 0), Vector(17000, 500, 0), Vector(17500, 500, 0), Vector(17500, 1000, 0), Vector(17000, 1000, 0), Vector(17063, 1256, 0), Vector(17732, 1227, 0), Vector(17790, 720, 0), Vector(17702, 242, 0) ]) t_xpos += 1200 _set_text(["n-Bezier"], Vector(t_xpos, t_ypos, 0)) # Label _msg(16 * "-") _msg("Label") place = App.Placement(Vector(18500, 500, 0), App.Rotation()) label = Draft.make_label(target_point=Vector(18000, 0, 0), placement=place, custom_text="Example label", distance=-250) label.Text = "Testing" if App.GuiUp: label.ViewObject.ArrowSize = 15 label.ViewObject.TextSize = 100 doc.recompute() t_xpos += 1200 _set_text(["Label"], Vector(t_xpos, t_ypos, 0)) # Orthogonal array and orthogonal link array _msg(16 * "-") _msg("Orthogonal array") rect_h = Draft.make_rectangle(500, 500) rect_h.Placement.Base = Vector(1500, 2500, 0) if App.GuiUp: rect_h.ViewObject.Visibility = False Draft.make_ortho_array(rect_h, Vector(600, 0, 0), Vector(0, 600, 0), Vector(0, 0, 0), 3, 2, 1, use_link=False) t_xpos = 1700 t_ypos = 2200 _set_text(["Array"], Vector(t_xpos, t_ypos, 0)) rect_h_2 = Draft.make_rectangle(500, 100) rect_h_2.Placement.Base = Vector(1500, 5000, 0) if App.GuiUp: rect_h_2.ViewObject.Visibility = False _msg(16 * "-") _msg("Orthogonal link array") Draft.make_ortho_array(rect_h_2, Vector(800, 0, 0), Vector(0, 500, 0), Vector(0, 0, 0), 2, 4, 1, use_link=True) t_ypos += 2600 _set_text(["Link array"], Vector(t_xpos, t_ypos, 0)) # Polar array and polar link array _msg(16 * "-") _msg("Polar array") wire_h = Draft.make_wire([ Vector(5500, 3000, 0), Vector(6000, 3500, 0), Vector(6000, 3200, 0), Vector(5800, 3200, 0) ]) if App.GuiUp: wire_h.ViewObject.Visibility = False Draft.make_polar_array(wire_h, 8, 200, Vector(5000, 3000, 0), use_link=False) t_xpos = 4600 t_ypos = 2200 _set_text(["Polar array"], Vector(t_xpos, t_ypos, 0)) _msg(16 * "-") _msg("Polar link array") wire_h_2 = Draft.make_wire([ Vector(5500, 6000, 0), Vector(6000, 6000, 0), Vector(5800, 5700, 0), Vector(5800, 5750, 0) ]) if App.GuiUp: wire_h_2.ViewObject.Visibility = False Draft.make_polar_array(wire_h_2, 8, 200, Vector(5000, 6000, 0), use_link=True) t_ypos += 3200 _set_text(["Polar link array"], Vector(t_xpos, t_ypos, 0)) # Circular array and circular link array _msg(16 * "-") _msg("Circular array") poly_h = Draft.make_polygon(5, 200) poly_h.Placement.Base = Vector(8000, 3000, 0) if App.GuiUp: poly_h.ViewObject.Visibility = False Draft.make_circular_array(poly_h, 500, 600, 3, 1, Vector(0, 0, 1), Vector(0, 0, 0), use_link=False) t_xpos = 7700 t_ypos = 1700 _set_text(["Circular array"], Vector(t_xpos, t_ypos, 0)) _msg(16 * "-") _msg("Circular link array") poly_h_2 = Draft.make_polygon(6, 150) poly_h_2.Placement.Base = Vector(8000, 6250, 0) if App.GuiUp: poly_h_2.ViewObject.Visibility = False Draft.make_circular_array(poly_h_2, 550, 450, 3, 1, Vector(0, 0, 1), Vector(0, 0, 0), use_link=True) t_ypos += 3100 _set_text(["Circular link array"], Vector(t_xpos, t_ypos, 0)) # Path array and path link array _msg(16 * "-") _msg("Path array") poly_h = Draft.make_polygon(3, 250) poly_h.Placement.Base = Vector(10000, 3000, 0) if App.GuiUp: poly_h.ViewObject.Visibility = False bspline_path = Draft.make_bspline([ Vector(10500, 2500, 0), Vector(11000, 3000, 0), Vector(11500, 3200, 0), Vector(12000, 4000, 0) ]) Draft.make_path_array(poly_h, bspline_path, 5, use_link=False) t_xpos = 10400 t_ypos = 2200 _set_text(["Path array"], Vector(t_xpos, t_ypos, 0)) _msg(16 * "-") _msg("Path link array") poly_h_2 = Draft.make_polygon(4, 200) poly_h_2.Placement.Base = Vector(10000, 5000, 0) if App.GuiUp: poly_h_2.ViewObject.Visibility = False bspline_path_2 = Draft.make_bspline([ Vector(10500, 4500, 0), Vector(11000, 6800, 0), Vector(11500, 6000, 0), Vector(12000, 5200, 0) ]) Draft.make_path_array(poly_h_2, bspline_path_2, 6, use_link=True) t_ypos += 2000 _set_text(["Path link array"], Vector(t_xpos, t_ypos, 0)) # Point array _msg(16 * "-") _msg("Point array") poly_h = Draft.make_polygon(3, 250) poly_h.Placement.Base = Vector(12500, 2500, 0) point_1 = Draft.make_point(13000, 3000, 0) point_2 = Draft.make_point(13000, 3500, 0) point_3 = Draft.make_point(14000, 2500, 0) point_4 = Draft.make_point(14000, 3000, 0) add_list, delete_list = Draft.upgrade([point_1, point_2, point_3, point_4]) compound = add_list[0] if App.GuiUp: compound.ViewObject.PointSize = 5 Draft.make_point_array(poly_h, compound) t_xpos = 13000 t_ypos = 2200 _set_text(["Point array"], Vector(t_xpos, t_ypos, 0)) # Clone and mirror _msg(16 * "-") _msg("Clone") wire_h = Draft.make_wire([ Vector(15000, 2500, 0), Vector(15200, 3000, 0), Vector(15500, 2500, 0), Vector(15200, 2300, 0) ]) Draft.make_clone(wire_h, Vector(0, 1000, 0)) t_xpos = 15000 t_ypos = 2100 _set_text(["Clone"], Vector(t_xpos, t_ypos, 0)) _msg(16 * "-") _msg("Mirror") wire_h = Draft.make_wire([ Vector(17000, 2500, 0), Vector(16500, 4000, 0), Vector(16000, 2700, 0), Vector(16500, 2500, 0), Vector(16700, 2700, 0) ]) Draft.mirror(wire_h, Vector(17100, 2000, 0), Vector(17100, 4000, 0)) t_xpos = 17000 t_ypos = 2200 _set_text(["Mirror"], Vector(t_xpos, t_ypos, 0)) _msg(16 * "-") _msg("Layer") layer = Draft.make_layer("Custom layer", line_color=(0.33, 0.0, 0.49), shape_color=(0.56, 0.89, 0.56), line_width=4, transparency=50) cube = doc.addObject('Part::Box') cube.Length = 350 cube.Width = 300 cube.Height = 250 cube.Placement.Base = Vector(14000, 5500, 0) cone = doc.addObject('Part::Cone') cone.Radius1 = 400 cone.Height = 600 cone.Angle = 270 cone.Placement.Base = Vector(15000, 6000, 0) sphere = doc.addObject('Part::Sphere') sphere.Radius = 450 sphere.Angle1 = -45 sphere.Angle2 = 45 sphere.Angle3 = 300 sphere.Placement.Base = Vector(14000, 7000, 0) layer.Proxy.addObject(layer, cube) layer.Proxy.addObject(layer, cone) layer.Proxy.addObject(layer, sphere) t_xpos = 14000 t_ypos = 5000 _set_text(["Layer"], Vector(t_xpos, t_ypos, 0)) doc.recompute()
def get_arrow(obj, arrowtype, point, arrowsize, color, linewidth, angle=0): """Get the SVG representation from an arrow.""" svg = "" if not App.GuiUp or not obj.ViewObject: return svg _cx_cy_r = 'cx="{}" cy="{}" r="{}"'.format(point.x, point.y, arrowsize) _rotate = 'rotate({},{},{})'.format(math.degrees(angle), point.x, point.y) _transl = 'translate({},{})'.format(point.x, point.y) _scale = 'scale({size},{size})'.format(size=arrowsize) _style = 'style="stroke-miterlimit:4;stroke-dasharray:none"' if obj.ViewObject.ArrowType == "Circle": svg += '<circle ' svg += _cx_cy_r + ' ' svg += 'fill="{}" stroke="{}" '.format("none", color) svg += 'style="stroke-width:{};'.format(linewidth) svg += 'stroke-miterlimit:4;stroke-dasharray:none" ' svg += 'freecad:skip="1"' svg += '/>\n' elif obj.ViewObject.ArrowType == "Dot": svg += '<circle ' svg += _cx_cy_r + ' ' svg += 'fill="{}" stroke="{}" '.format(color, "none") svg += _style + ' ' svg += 'freecad:skip="1"' svg += '/>\n' elif obj.ViewObject.ArrowType == "Arrow": svg += '<path ' svg += 'transform="' svg += _rotate + ' ' svg += _transl + ' ' svg += _scale + '" ' svg += 'freecad:skip="1" ' svg += 'fill="{}" stroke="{}" '.format(color, "none") svg += _style + ' ' svg += 'd="M 0 0 L 4 1 L 4 -1 Z"' svg += '/>\n' elif obj.ViewObject.ArrowType == "Tick": svg += '<path ' svg += 'transform="' svg += _rotate + ' ' svg += _transl + ' ' svg += _scale + '" ' svg += 'freecad:skip="1" ' svg += 'fill="{}" stroke="{}" '.format(color, "none") svg += _style + ' ' svg += 'd="M -1 -2 L 0 2 L 1 2 L 0 -2 Z"' svg += '/>\n' elif obj.ViewObject.ArrowType == "Tick-2": svg += '<line ' svg += 'transform="' svg += 'rotate({},{},{}) '.format(math.degrees(angle) + 45, point.x, point.y) svg += _transl + '" ' svg += 'freecad:skip="1" ' svg += 'fill="{}" stroke="{}" '.format("none", color) svg += 'style="stroke-dasharray:none;stroke-linecap:square;' svg += 'stroke-width:{}" '.format(linewidth) svg += 'x1="-{}" y1="0" '.format(2 * arrowsize) svg += 'x2="{}" y2="0"'.format(2 * arrowsize) svg += '/>\n' else: _wrn("getSVG: arrow type not implemented") return svg
def _svg_dimension(obj, plane, scale, linewidth, fontsize, stroke, pointratio, techdraw, rotation): """Return the SVG representation of a linear dimension.""" if not App.GuiUp: _wrn("'{}': SVG can only be generated " "in GUI mode".format(obj.Label)) return "" if not hasattr(obj.ViewObject, "Proxy") or not obj.ViewObject.Proxy: _err("'{}': doesn't have Proxy, " "SVG cannot be generated".format(obj.Label)) return "" vobj = obj.ViewObject prx = vobj.Proxy if not hasattr(prx, "p1"): _err("'{}': doesn't have points, " "SVG cannot be generated".format(obj.Label)) return "" ts = len(prx.string) * vobj.FontSize.Value / 4.0 rm = (prx.p3 - prx.p2).Length/2.0 - ts _diff32 = prx.p3 - prx.p2 _diff23 = prx.p2 - prx.p3 _v32 = DraftVecUtils.scaleTo(_diff32, rm) _v23 = DraftVecUtils.scaleTo(_diff23, rm) p2a = get_proj(prx.p2 + _v32, plane) p2b = get_proj(prx.p3 + _v23, plane) p1 = get_proj(prx.p1, plane) p2 = get_proj(prx.p2, plane) p3 = get_proj(prx.p3, plane) p4 = get_proj(prx.p4, plane) tbase = get_proj(prx.tbase, plane) r = prx.textpos.rotation.getValue().getValue() _rv = App.Rotation(r[0], r[1], r[2], r[3]) rv = _rv.multVec(App.Vector(1, 0, 0)) angle = -DraftVecUtils.angle(get_proj(rv, plane)) # angle = -DraftVecUtils.angle(p3.sub(p2)) svg = '' nolines = False if hasattr(vobj, "ShowLine"): if not vobj.ShowLine: nolines = True # drawing lines if not nolines: svg += '<path ' if vobj.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 if rotation != 0: # print("dim: tangle:", tangle, # " rot: ", rotation, # " text: ", prx.string) if abs(tangle + math.radians(rotation)) < 0.0001: tangle += math.pi _v = App.Vector(0, 2.0/scale, 0) _rot = DraftVecUtils.rotate(_v, tangle) tbase = tbase + _rot if not nolines: 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 + App.Vector(0, -2.0/scale, 0) if not nolines: 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) + '" ' if not nolines: 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(vobj, "DimOvershoot") and vobj.DimOvershoot.Value: shootsize = vobj.DimOvershoot.Value/pointratio svg += get_overshoot(p2, shootsize, stroke, linewidth, angle) svg += get_overshoot(p3, shootsize, stroke, linewidth, angle + math.pi) if hasattr(vobj, "ExtOvershoot") and vobj.ExtOvershoot.Value: shootsize = vobj.ExtOvershoot.Value/pointratio shootangle = -DraftVecUtils.angle(p1 - p2) svg += get_overshoot(p2, shootsize, stroke, linewidth, shootangle) svg += get_overshoot(p3, shootsize, stroke, linewidth, shootangle) # drawing arrows if hasattr(vobj, "ArrowType"): arrowsize = vobj.ArrowSize.Value/pointratio if hasattr(vobj, "FlipArrows"): if vobj.FlipArrows: angle = angle + math.pi svg += get_arrow(obj, vobj.ArrowType, p2, arrowsize, stroke, linewidth, angle) svg += get_arrow(obj, vobj.ArrowType, p3, arrowsize, stroke, linewidth, angle + math.pi) # drawing text svg += svgtext.get_text(plane, techdraw, stroke, fontsize, vobj.FontName, tangle, tbase, prx.string) return svg
def dim_symbol(symbol=None, invert=False): """Return the specified dimension symbol. Parameters ---------- symbol: int, optional It defaults to `None`, in which it gets the value from the parameter database, `get_param("dimsymbol", 0)`. A numerical value defines different markers * 0, `SoSphere` * 1, `SoMarkerSet` with a circle * 2, `SoSeparator` with a `soCone` * 3, `SoSeparator` with a `SoFaceSet` * 4, `SoSeparator` with a `SoLineSet`, calling `dim_dash` * Otherwise, `SoSphere` invert: bool, optional It defaults to `False`. If it is `True` and `symbol=2`, the cone will be rotated -90 degrees around the Z axis, otherwise the rotation is positive, +90 degrees. Returns ------- Coin.SoNode A `Coin.SoSphere`, or `Coin.SoMarkerSet` (circle), or `Coin.SoSeparator` (cone, face, line) that will be used as a dimension symbol. """ if symbol is None: symbol = utils.get_param("dimsymbol", 0) if symbol == 0: # marker = coin.SoMarkerSet() # marker.markerIndex = 80 # Returning a sphere means that the bounding box will # be 3-dimensional; a marker will always be planar seen from any # orientation but it currently doesn't work correctly marker = coin.SoSphere() return marker elif symbol == 1: marker = coin.SoMarkerSet() # Should be the same as # marker.markerIndex = 10 marker.markerIndex = Gui.getMarkerIndex("circle", 9) return marker elif symbol == 2: marker = coin.SoSeparator() t = coin.SoTransform() t.translation.setValue((0, -2, 0)) t.center.setValue((0, 2, 0)) if invert: t.rotation.setValue(coin.SbVec3f((0, 0, 1)), -math.pi / 2) else: t.rotation.setValue(coin.SbVec3f((0, 0, 1)), math.pi / 2) c = coin.SoCone() c.height.setValue(4) marker.addChild(t) marker.addChild(c) return marker elif symbol == 3: marker = coin.SoSeparator() c = coin.SoCoordinate3() c.point.setValues([(-1, -2, 0), (0, 2, 0), (1, 2, 0), (0, -2, 0)]) f = coin.SoFaceSet() marker.addChild(c) marker.addChild(f) return marker elif symbol == 4: return dimDash((-1.5, -1.5, 0), (1.5, 1.5, 0)) else: _wrn(_tr("Symbol not implemented. Use a default symbol.")) return coin.SoSphere()
def make_linear_dimension_obj(edge_object, i1=1, i2=2, dim_line=None): """Create a linear dimension from an object. Parameters ---------- edge_object: Part::Feature The object which has an edge which will be measured. It must have a `Part::TopoShape`, and at least one element in `Shape.Vertexes`, to be able to measure a distance. i1: int, optional It defaults to `1`. It is the index of the first vertex in `edge_object` from which the measurement will be taken. The minimum value should be `1`, which will be interpreted as `'Vertex1'`. If the value is below `1`, it will be set to `1`. i2: int, optional It defaults to `2`, which will be converted to `'Vertex2'`. It is the index of the second vertex in `edge_object` that determines the endpoint of the measurement. If it is the same value as `i1`, the resulting measurement will be made from the origin `(0, 0, 0)` to the vertex indicated by `i1`. If the value is below `1`, it will be set to the last vertex in `edge_object`. Then to measure the first and last, this could be used :: make_linear_dimension_obj(edge_object, i1=1, i2=-1) dim_line: Base::Vector3 It defaults to `None`. This is a point through which the extension of the dimension line will pass. This point controls how close or how far the dimension line is positioned from the measured segment in `edge_object`. If it is `None`, this point will be calculated from the intermediate distance betwwen the vertices defined by `i1` and `i2`. Returns ------- App::FeaturePython A scripted object of type `'LinearDimension'`. This object does not have a `Shape` attribute, as the text and lines are created on screen by Coin (pivy). None If there is a problem it will return `None`. """ _name = "make_linear_dimension_obj" utils.print_header(_name, "Linear dimension") found, doc = utils.find_doc(App.activeDocument()) if not found: _err(_tr("No active document. Aborting.")) return None if isinstance(edge_object, str): edge_object_str = edge_object if isinstance(edge_object, (list, tuple)): _msg("edge_object: {}".format(edge_object)) _err(_tr("Wrong input: object must not be a list.")) return None found, edge_object = utils.find_object(edge_object, doc) if not found: _msg("edge_object: {}".format(edge_object_str)) _err(_tr("Wrong input: object not in document.")) return None _msg("edge_object: {}".format(edge_object.Label)) if not hasattr(edge_object, "Shape"): _err(_tr("Wrong input: object doesn't have a 'Shape' to measure.")) return None if (not hasattr(edge_object.Shape, "Vertexes") or len(edge_object.Shape.Vertexes) < 1): _err(_tr("Wrong input: object doesn't have at least one element " "in 'Vertexes' to use for measuring.")) return None _msg("i1: {}".format(i1)) try: utils.type_check([(i1, int)], name=_name) except TypeError: _err(_tr("Wrong input: must be an integer.")) return None if i1 < 1: i1 = 1 _wrn(_tr("i1: values below 1 are not allowed; will be set to 1.")) vx1 = edge_object.getSubObject("Vertex" + str(i1)) if not vx1: _err(_tr("Wrong input: vertex not in object.")) return None _msg("i2: {}".format(i2)) try: utils.type_check([(i2, int)], name=_name) except TypeError: _err(_tr("Wrong input: must be a vector.")) return None if i2 < 1: i2 = len(edge_object.Shape.Vertexes) _wrn(_tr("i2: values below 1 are not allowed; " "will be set to the last vertex in the object.")) vx2 = edge_object.getSubObject("Vertex" + str(i2)) if not vx2: _err(_tr("Wrong input: vertex not in object.")) return None _msg("dim_line: {}".format(dim_line)) if dim_line: try: utils.type_check([(dim_line, App.Vector)], name=_name) except TypeError: _err(_tr("Wrong input: must be a vector.")) return None else: diff = vx2.Point.sub(vx1.Point) diff.multiply(0.5) dim_line = vx1.Point.add(diff) # TODO: the internal function expects an index starting with 0 # so we need to decrease the value here. # This should be changed in the future in the internal function. i1 -= 1 i2 -= 1 new_obj = make_dimension(edge_object, i1, i2, dim_line) return new_obj
def make_label(target_point=App.Vector(0, 0, 0), placement=App.Vector(30, 30, 0), target_object=None, subelements=None, label_type="Custom", custom_text="Label", direction="Horizontal", distance=-10, points=None): """Create a Label object containing different types of information. The current color and text height and font specified in preferences are used. Parameters ---------- target_point: Base::Vector3, optional It defaults to the origin `App.Vector(0, 0, 0)`. This is the point which is pointed to by the label's leader line. This point can be adorned with a marker like an arrow or circle. placement: Base::Placement, Base::Vector3, or Base::Rotation, optional It defaults to `App.Vector(30, 30, 0)`. If it is provided, it defines the base point of the textual label. The input could be a full placement, just a vector indicating the translation, or just a rotation. target_object: Part::Feature or str, optional It defaults to `None`. If it exists it should be an object which will be used to provide information to the label, as long as `label_type` is different from `'Custom'`. If it is a string, it must be the `Label` of that object. Since a `Label` is not guaranteed to be unique in a document, it will use the first object found with this `Label`. subelements: str, optional It defaults to `None`. If `subelements` is provided, `target_object` should be provided as well, otherwise it is ignored. It should be a string indicating a subelement name, either `'VertexN'`, `'EdgeN'`, or `'FaceN'` which should exist within `target_object`. In this case `'N'` is an integer that indicates the specific number of vertex, edge, or face in `target_object`. Both `target_object` and `subelements` are used to link the label to a particular object, or to the particular vertex, edge, or face, and get information from them. :: make_label(..., target_object=App.ActiveDocument.Box) make_label(..., target_object="My box", subelements="Face3") These two parameters can be can be obtained from the `Gui::Selection` module. :: sel_object = Gui.Selection.getSelectionEx()[0] target_object = sel_object.Object subelements = sel_object.SubElementNames[0] label_type: str, optional It defaults to `'Custom'`. It indicates the type of information that will be shown in the label. See the get_label_types function in label.py for supported types. Only `'Custom'` allows you to manually set the text by defining `custom_text`. The other types take their information from the object included in `target`. - `'Position'` will show the base position of the target object, or of the indicated `'VertexN'` in `target`. - `'Length'` will show the `Length` of the target object's `Shape`, or of the indicated `'EdgeN'` in `target`. - `'Area'` will show the `Area` of the target object's `Shape`, or of the indicated `'FaceN'` in `target`. custom_text: str, or list of str, optional It defaults to `'Label'`. If it is a list, each element in the list represents a new text line. It is the text that will be displayed by the label when `label_type` is `'Custom'`. direction: str, optional It defaults to `'Horizontal'`. It can be `'Horizontal'`, `'Vertical'`, or `'Custom'`. It indicates the direction of the straight segment of the leader line that ends up next to the textual label. If `'Custom'` is selected, the leader line can be manually drawn by specifying the value of `points`. Normally, the leader line has only three points, but with `'Custom'` you can specify as many points as needed. distance: int, float, Base::Quantity, optional It defaults to -10. It indicates the length of the horizontal or vertical segment of the leader line. The leader line is composed of two segments, the first segment is inclined, while the second segment is either horizontal or vertical depending on the value of `direction`. :: T | | o------- L text The `oL` segment's length is defined by `distance` while the `oT` segment is automatically calculated depending on the values of `placement` (L) and `distance` (o). This `distance` is oriented, meaning that if it is positive the segment will be to the right and above of the textual label, depending on if `direction` is `'Horizontal'` or `'Vertical'`, respectively. If it is negative, the segment will be to the left and below of the text. points: list of Base::Vector3, optional It defaults to `None`. It is a list of vectors defining the shape of the leader line; the list must have at least two points. This argument must be used together with `direction='Custom'` to display this custom leader. However, notice that if the Label's `StraightDirection` property is later changed to `'Horizontal'` or `'Vertical'`, the custom point list will be overwritten with a new, automatically calculated three-point list. For the object to use custom points, `StraightDirection` must remain `'Custom'`, and then the `Points` property can be overwritten by a suitable list of points. Returns ------- App::FeaturePython A scripted object of type `'Label'`. This object does not have a `Shape` attribute, as the text and lines are created on screen by Coin (pivy). None If there is a problem it will return `None`. """ _name = "make_label" utils.print_header(_name, "Label") found, doc = utils.find_doc(App.activeDocument()) if not found: _err(translate("draft", "No active document. Aborting.")) return None _msg("target_point: {}".format(target_point)) if not target_point: target_point = App.Vector(0, 0, 0) try: utils.type_check([(target_point, App.Vector)], name=_name) except TypeError: _err(translate("draft", "Wrong input: must be a vector.")) return None _msg("placement: {}".format(placement)) if not placement: placement = App.Placement() try: utils.type_check([(placement, (App.Placement, App.Vector, App.Rotation))], name=_name) except TypeError: _err( translate( "draft", "Wrong input: must be a placement, a vector, or a rotation.")) return None # Convert the vector or rotation to a full placement if isinstance(placement, App.Vector): placement = App.Placement(placement, App.Rotation()) elif isinstance(placement, App.Rotation): placement = App.Placement(App.Vector(), placement) if isinstance(target_object, str): target_object_str = target_object if target_object: if isinstance(target_object, (list, tuple)): _msg("target_object: {}".format(target_object)) _err(translate("draft", "Wrong input: object must not be a list.")) return None found, target_object = utils.find_object(target_object, doc) if not found: _msg("target_object: {}".format(target_object_str)) _err(translate("draft", "Wrong input: object not in document.")) return None _msg("target_object: {}".format(target_object.Label)) if target_object and subelements: _msg("subelements: {}".format(subelements)) try: # Make a list if isinstance(subelements, str): subelements = [subelements] utils.type_check([(subelements, (list, tuple, str))], name=_name) except TypeError: _err( translate( "draft", "Wrong input: must be a list or tuple of strings, or a single string." )) return None # The subelements list is used to build a special list # called a LinkSub, which includes the target_object # and the subelements. # Single: (target_object, "Edge1") # Multiple: (target_object, ("Edge1", "Edge2")) for sub in subelements: _sub = target_object.getSubObject(sub) if not _sub: _err("subelement: {}".format(sub)) _err( translate("draft", "Wrong input: subelement not in object.")) return None _msg("label_type: {}".format(label_type)) if not label_type: label_type = "Custom" try: utils.type_check([(label_type, str)], name=_name) except TypeError: _err(translate("draft", "Wrong input: label_type must be a string.")) return None types = label.get_label_types() if label_type not in types: _err( translate( "draft", "Wrong input: label_type must be one of the following: ") + str(types).strip("[]")) return None _msg("custom_text: {}".format(custom_text)) if not custom_text: custom_text = "Label" try: utils.type_check([(custom_text, (str, list))], name=_name) except TypeError: _err( translate( "draft", "Wrong input: must be a list of strings or a single string.")) return None if (type(custom_text) is list and not all(isinstance(element, str) for element in custom_text)): _err( translate( "draft", "Wrong input: must be a list of strings or a single string.")) return None _msg("direction: {}".format(direction)) if not direction: direction = "Horizontal" try: utils.type_check([(direction, str)], name=_name) except TypeError: _err( translate( "draft", "Wrong input: must be a string, 'Horizontal', 'Vertical', or 'Custom'." )) return None if direction not in ("Horizontal", "Vertical", "Custom"): _err( translate( "draft", "Wrong input: must be a string, 'Horizontal', 'Vertical', or 'Custom'." )) return None _msg("distance: {}".format(distance)) if not distance: distance = 1 try: utils.type_check([(distance, (int, float))], name=_name) except TypeError: _err(translate("draft", "Wrong input: must be a number.")) return None if points: _msg("points: {}".format(points)) _err_msg = translate( "draft", "Wrong input: must be a list of at least two vectors.") try: utils.type_check([(points, (tuple, list))], name=_name) except TypeError: _err(_err_msg) return None if len(points) < 2: _err(_err_msg) return None if not all(isinstance(p, App.Vector) for p in points): _err(_err_msg) return None new_obj = doc.addObject("App::FeaturePython", "dLabel") label.Label(new_obj) new_obj.TargetPoint = target_point new_obj.Placement = placement if target_object: if subelements: new_obj.Target = [target_object, subelements] else: new_obj.Target = [target_object, []] new_obj.LabelType = label_type new_obj.CustomText = custom_text new_obj.StraightDirection = direction new_obj.StraightDistance = distance if points: if direction != "Custom": _wrn( translate("draft", "Direction is not 'Custom'; points won't be used.")) new_obj.Points = points if App.GuiUp: ViewProviderLabel(new_obj.ViewObject) h = utils.get_param("textheight", 0.20) new_obj.ViewObject.TextSize = h gui_utils.format_object(new_obj) gui_utils.select(new_obj) return new_obj
def get_dxf(obj, direction=None): """Return a DXF entity from the given object. If direction is given, the object is projected in 2D. """ plane = None result = "" if (obj.isDerivedFrom("Drawing::View") or obj.isDerivedFrom("TechDraw::DrawView")): if obj.Source.isDerivedFrom("App::DocumentObjectGroup"): for o in obj.Source.Group: result += get_dxf(o, obj.Direction) else: result += get_dxf(obj.Source, obj.Direction) return result if direction and isinstance(direction, App.Vector): if direction != App.Vector(0, 0, 0): plane = WorkingPlane.Plane() plane.alignToPointAndAxis(App.Vector(0, 0, 0), direction) if utils.get_type(obj) in ("Dimension", "LinearDimension"): p1 = _get_proj(obj.Start, plane=plane) p2 = _get_proj(obj.End, plane=plane) p3 = _get_proj(obj.Dimline, plane=plane) result += "0\nDIMENSION\n8\n0\n62\n0\n3\nStandard\n70\n1\n" result += "10\n"+str(p3.x)+"\n20\n"+str(p3.y)+"\n30\n"+str(p3.z)+"\n" result += "13\n"+str(p1.x)+"\n23\n"+str(p1.y)+"\n33\n"+str(p1.z)+"\n" result += "14\n"+str(p2.x)+"\n24\n"+str(p2.y)+"\n34\n"+str(p2.z)+"\n" elif utils.get_type(obj) == "Annotation": # Only for App::Annotation p = _get_proj(obj.Position, plane=plane) count = 0 for t in obj.LabeLtext: result += "0\nTEXT\n8\n0\n62\n0\n" result += "10\n" result += str(p.x) + "\n20\n" result += str(p.y + count) + "\n30\n" result += str(p.z) + "\n" result += "40\n1\n" result += "1\n" + str(t) + "\n" result += "7\nSTANDARD\n" count += 1 elif hasattr(obj, 'Shape'): # TODO do this the Draft way, for ex. using polylines and rectangles if not direction: direction = App.Vector(0, 0, -1) if DraftVecUtils.isNull(direction): direction = App.Vector(0, 0, -1) try: d = TechDraw.projectToDXF(obj.Shape, direction) except Exception: # TODO: trap only specific exception. # Impossible to generate DXF from Shape? Which exception is throw? _wrn("get_dxf: " "unable to project '{}' to {}".format(obj.Label, direction)) else: result += d else: _wrn("get_dxf: unsupported object, '{}'".format(obj.Label)) return result
def make_ortho_array(obj, v_x=App.Vector(10, 0, 0), v_y=App.Vector(0, 10, 0), v_z=App.Vector(0, 0, 10), n_x=2, n_y=2, n_z=1, use_link=True): """Create an orthogonal array from the given object. Parameters ---------- obj: Part::Feature Any type of object that has a `Part::TopoShape` that can be duplicated. This means most 2D and 3D objects produced with any workbench. v_x, v_y, v_z: Base::Vector3, optional The vector indicating the vector displacement between two elements in the specified orthogonal direction X, Y, Z. By default: :: v_x = App.Vector(10, 0, 0) v_y = App.Vector(0, 10, 0) v_z = App.Vector(0, 0, 10) Given that this is a vectorial displacement the next object can appear displaced in one, two or three axes at the same time. For example :: v_x = App.Vector(10, 5, 0) means that the next element in the X direction will be displaced 10 mm in X, 5 mm in Y, and 0 mm in Z. A traditional "rectangular" array is obtained when the displacement vector only has its corresponding component, like in the default case. If these values are entered as single numbers instead of vectors, the single value is expanded into a vector of the corresponding direction, and the other components are assumed to be zero. For example :: v_x = 15 v_y = 10 v_z = 1 becomes :: v_x = App.Vector(15, 0, 0) v_y = App.Vector(0, 10, 0) v_z = App.Vector(0, 0, 1) n_x, n_y, n_z: int, optional The number of copies in the specified orthogonal direction X, Y, Z. This number includes the original object, therefore, it must be at least 1. The values of `n_x` and `n_y` default to 2, while `n_z` defaults to 1. This means the array by default is a planar array. use_link: bool, optional It defaults to `True`. If it is `True` the produced copies are not `Part::TopoShape` copies, but rather `App::Link` objects. The Links repeat the shape of the original `obj` exactly, and therefore the resulting array is more memory efficient. Also, when `use_link` is `True`, the `Fuse` property of the resulting array does not work; the array doesn't contain separate shapes, it only has the original shape repeated many times, so there is nothing to fuse together. If `use_link` is `False` the original shape is copied many times. In this case the `Fuse` property is able to fuse all copies into a single object, if they touch each other. Returns ------- Part::FeaturePython A scripted object with `Proxy.Type='Array'`. Its `Shape` is a compound of the copies of the original object. See Also -------- make_ortho_array2d, make_rect_array, make_rect_array2d """ _name = "make_ortho_array" utils.print_header(_name, _tr("Orthogonal array")) _msg("v_x: {}".format(v_x)) _msg("v_y: {}".format(v_y)) _msg("v_z: {}".format(v_z)) try: utils.type_check([(v_x, (int, float, App.Vector)), (v_y, (int, float, App.Vector)), (v_z, (int, float, App.Vector))], name=_name) except TypeError: _err(_tr("Wrong input: must be a number or vector.")) return None _text = "Input: single value expanded to vector." if not isinstance(v_x, App.Vector): v_x = App.Vector(v_x, 0, 0) _wrn(_tr(_text)) if not isinstance(v_y, App.Vector): v_y = App.Vector(0, v_y, 0) _wrn(_tr(_text)) if not isinstance(v_z, App.Vector): v_z = App.Vector(0, 0, v_z) _wrn(_tr(_text)) _msg("n_x: {}".format(n_x)) _msg("n_y: {}".format(n_y)) _msg("n_z: {}".format(n_z)) try: utils.type_check([(n_x, int), (n_y, int), (n_z, int)], name=_name) except TypeError: _err(_tr("Wrong input: must be an integer number.")) return None _text = ("Input: number of elements must be at least 1. " "It is set to 1.") if n_x < 1: _wrn(_tr(_text)) n_x = 1 if n_y < 1: _wrn(_tr(_text)) n_y = 1 if n_z < 1: _wrn(_tr(_text)) n_z = 1 _msg("use_link: {}".format(bool(use_link))) # new_obj = make_array.make_array() new_obj = Draft.makeArray(obj, arg1=v_x, arg2=v_y, arg3=v_z, arg4=n_x, arg5=n_y, arg6=n_z, use_link=use_link) return new_obj
def getColorFromStyledItem(styled_item): """Get color from the IfcStyledItem. Returns ------- float, float, float, int A tuple with the red, green, blue, and transparency values. If the `IfcStyledItem` is a `IfcDraughtingPreDefinedColour` the transparency is set to 0. The first three values range from 0 to 1.0, while the transparency varies from 0 to 100. None Return `None` if `styled_item` is not of type `'IfcStyledItem'` or if there is any other problem getting a color. """ if styled_item.is_a("IfcStyledRepresentation"): styled_item = styled_item.Items[0] if not styled_item.is_a("IfcStyledItem"): return None rgb_color = None transparency = None col = None # The `IfcStyledItem` holds presentation style information for products, # either explicitly for an `IfcGeometricRepresentationItem` being part of # an `IfcShapeRepresentation` assigned to a product, or by assigning # presentation information to `IfcMaterial` being assigned # as other representation for a product. # In current IFC release (IFC2x3) only one presentation style # assignment shall be assigned. # In IFC4 `IfcPresentationStyleAssignment` is deprecated # In IFC4 multiple styles are assigned to style in 'IfcStyleItem' instead # print(ifcfile[p]) # print(styled_item) # print(styled_item.Styles) if len(styled_item.Styles) == 0: # IN IFC2x3, only one element in `Styles` should be available. _wrn("No 'Style' in 'IfcStyleItem', do nothing.") # ca 100x in 210_King_Merged.ifc # Empty styles, #4952778=IfcStyledItem(#4952779,(),$) # this is an error in the IFC file in my opinion else: # never seen an ifc with more than one Styles in IfcStyledItem # the above seams to only apply for IFC2x3, IFC4 can have them # see https://forum.freecadweb.org/viewtopic.php?f=39&t=33560&p=437056#p437056 # Get the `IfcPresentationStyleAssignment`, there should only be one, if styled_item.Styles[0].is_a('IfcPresentationStyleAssignment'): assign_style = styled_item.Styles[0] else: # `IfcPresentationStyleAssignment` is deprecated in IFC4, # in favor of `IfcStyleAssignmentSelect` assign_style = styled_item # print(assign_style) # IfcPresentationStyleAssignment # `IfcPresentationStyleAssignment` can hold various kinds and counts # of styles, see `IfcPresentationStyleSelect` if assign_style.Styles[0].is_a("IfcSurfaceStyle"): _style = assign_style.Styles[0] # Schependomlaan and Nova and others # `IfcSurfaceStyleRendering` # print(_style.Styles[0]) # `IfcColourRgb` rgb_color = _style.Styles[0].SurfaceColour # print(rgb_color) if (_style.Styles[0].is_a('IfcSurfaceStyleShading') and hasattr(_style.Styles[0], 'Transparency') and _style.Styles[0].Transparency): transparency = _style.Styles[0].Transparency * 100 elif assign_style.Styles[0].is_a("IfcCurveStyle"): if (len(assign_style.Styles) == 2 and assign_style.Styles[1].is_a("IfcSurfaceStyle")): # Allplan, new IFC export started in 2017 # `IfcDraughtingPreDefinedColour` # print(assign_style.Styles[0].CurveColour) # TODO: check this; on index 1, is this what we need?! rgb_color = assign_style.Styles[1].Styles[0].SurfaceColour # print(rgb_color) else: # 2x Annotations in 210_King_Merged.ifc # print(ifcfile[p]) # print(assign_style.Styles[0]) # print(assign_style.Styles[0].CurveColour) rgb_color = assign_style.Styles[0].CurveColour if rgb_color: if rgb_color.is_a('IfcDraughtingPreDefinedColour'): if DEBUG_prod_colors: _msg(" '{}'= ".format(rgb_color.Name)) col = predefined_to_rgb(rgb_color) if col: col = col + (0, ) else: col = (rgb_color.Red, rgb_color.Green, rgb_color.Blue, int(transparency) if transparency else 0) else: col = None if DEBUG_prod_colors: _msg(" {}".format(col)) return col
def make_ortho_array2d(obj, v_x=App.Vector(10, 0, 0), v_y=App.Vector(0, 10, 0), n_x=2, n_y=2, use_link=True): """Create a 2D orthogonal array from the given object. This works the same as `make_ortho_array`. The Z component is ignored so it only considers vector displacements in X and Y directions. Parameters ---------- obj: Part::Feature Any type of object that has a `Part::TopoShape` that can be duplicated. This means most 2D and 3D objects produced with any workbench. v_x, v_y: Base::Vector3, optional Vectorial displacement of elements in the corresponding X and Y directions. See `make_ortho_array`. n_x, n_y: int, optional Number of elements in the corresponding X and Y directions. See `make_ortho_array`. use_link: bool, optional If it is `True`, create `App::Link` array. See `make_ortho_array`. Returns ------- Part::FeaturePython A scripted object with `Proxy.Type='Array'`. Its `Shape` is a compound of the copies of the original object. See Also -------- make_ortho_array, make_rect_array, make_rect_array2d """ _name = "make_ortho_array2d" utils.print_header(_name, _tr("Orthogonal array 2D")) _msg("v_x: {}".format(v_x)) _msg("v_y: {}".format(v_y)) try: utils.type_check([(v_x, (int, float, App.Vector)), (v_y, (int, float, App.Vector))], name=_name) except TypeError: _err(_tr("Wrong input: must be a number or vector.")) return None _text = "Input: single value expanded to vector." if not isinstance(v_x, App.Vector): v_x = App.Vector(v_x, 0, 0) _wrn(_tr(_text)) if not isinstance(v_y, App.Vector): v_y = App.Vector(0, v_y, 0) _wrn(_tr(_text)) _msg("n_x: {}".format(n_x)) _msg("n_y: {}".format(n_y)) try: utils.type_check([(n_x, int), (n_y, int)], name=_name) except TypeError: _err(_tr("Wrong input: must be an integer number.")) return None _text = ("Input: number of elements must be at least 1. " "It is set to 1.") if n_x < 1: _wrn(_tr(_text)) n_x = 1 if n_y < 1: _wrn(_tr(_text)) n_y = 1 _msg("use_link: {}".format(bool(use_link))) # new_obj = make_array.make_array() new_obj = Draft.makeArray(obj, arg1=v_x, arg2=v_y, arg3=n_x, arg4=n_y, use_link=use_link) return new_obj
def _get_path_circ_ellipse(plane, edge, vertex, edata, iscircle, isellipse, fill, stroke, linewidth, lstyle): """Get the edge data from a path that is a circle or ellipse.""" if hasattr(App, "DraftWorkingPlane"): drawing_plane_normal = App.DraftWorkingPlane.axis else: drawing_plane_normal = App.Vector(0, 0, 1) if plane: drawing_plane_normal = plane.axis center = edge.Curve ax = center.Axis # The angle between the curve axis and the plane is not 0 nor 180 degrees _angle = math.degrees(ax.getAngle(drawing_plane_normal)) if round(_angle, 2) not in (0, 180): edata += get_discretized(edge, plane) return "edata", edata # The angle is 0 or 180, coplanar 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 snip = Drawing.projectToSVG(edge, drawing_plane_normal) if snip: try: _a = snip.split('path d="')[1] _a = _a.split('"')[0] _a = _a.split("A")[1] A = "A " + _a except IndexError: # TODO: trap only specific exception. # Check the problem. Split didn't produce a two element list? _wrn("Circle or ellipse: " "cannot split the projection snip " "obtained by 'projectToSVG', " "continue manually.") else: edata += A done = True if not done: if len(edge.Vertexes) == 1 and iscircle: # Complete circle not only arc svg = get_circle(plane, fill, stroke, linewidth, lstyle, edge) # If it's a circle we will return the final SVG string, # otherwise it will process the `edata` further return "svg", svg elif len(edge.Vertexes) == 1 and isellipse: # Complete ellipse not only arc # svg = get_ellipse(plane, # fill, stroke, linewidth, # lstyle, edge) # return svg # Difference in angles _diff = (center.LastParameter - center.FirstParameter) / 2.0 endpoints = [ get_proj(center.value(_diff), plane), get_proj(vertex[-1].Point, plane) ] else: endpoints = [get_proj(vertex[-1].Point, plane)] # Arc with more than one vertex if iscircle: rx = ry = center.Radius rot = 0 else: # ellipse rx = center.MajorRadius ry = center.MinorRadius _rot = center.AngleXU * center.Axis * App.Vector(0, 0, 1) rot = math.degrees(_rot) if rot > 90: rot -= 180 if rot < -90: rot += 180 # Be careful with the sweep flag _diff = edge.ParameterRange[1] - edge.ParameterRange[0] _diff = _diff / math.pi flag_large_arc = (_diff % 2) > 1 # flag_sweep = (center.Axis * drawing_plane_normal >= 0) \ # == (edge.LastParameter > edge.FirstParameter) # == (edge.Orientation == "Forward") # Another method: check the direction of the angle # between tangents _diff = edge.LastParameter - edge.FirstParameter t1 = edge.tangentAt(edge.FirstParameter) t2 = edge.tangentAt(edge.FirstParameter + _diff / 10) flag_sweep = DraftVecUtils.angle(t1, t2, drawing_plane_normal) < 0 for v in endpoints: edata += ('A {} {} {} ' '{} {} ' '{} {} '.format(rx, ry, rot, int(flag_large_arc), int(flag_sweep), v.x, v.y)) return "edata", edata
def make_radial_dimension_obj(edge_object, index=1, mode="radius", dim_line=None): """Create a radial or diameter dimension from an arc object. Parameters ---------- edge_object: Part::Feature The object which has a circular edge which will be measured. It must have a `Part::TopoShape`, and at least one element must be a circular edge in `Shape.Edges` to be able to measure its radius. index: int, optional It defaults to `1`. It is the index of the edge in `edge_object` which is going to be measured. The minimum value should be `1`, which will be interpreted as `'Edge1'`. If the value is below `1`, it will be set to `1`. mode: str, optional It defaults to `'radius'`; the other option is `'diameter'`. It determines whether the dimension will be shown as a radius or as a diameter. dim_line: Base::Vector3, optional It defaults to `None`. This is a point through which the extension of the dimension line will pass. The dimension line will be a radius or diameter of the measured arc, extending from the center to the arc itself. If it is `None`, this point will be set to one unit to the right of the center of the arc, which will create a dimension line that is horizontal, that is, parallel to the +X axis. Returns ------- App::FeaturePython A scripted object of type `'LinearDimension'`. This object does not have a `Shape` attribute, as the text and lines are created on screen by Coin (pivy). None If there is a problem it will return `None`. """ _name = "make_radial_dimension_obj" utils.print_header(_name, "Radial dimension") found, doc = utils.find_doc(App.activeDocument()) if not found: _err(_tr("No active document. Aborting.")) return None if isinstance(edge_object, str): edge_object_str = edge_object found, edge_object = utils.find_object(edge_object, doc) if not found: _msg("edge_object: {}".format(edge_object_str)) _err(_tr("Wrong input: object not in document.")) return None _msg("edge_object: {}".format(edge_object.Label)) if not hasattr(edge_object, "Shape"): _err(_tr("Wrong input: object doesn't have a 'Shape' to measure.")) return None if (not hasattr(edge_object.Shape, "Edges") or len(edge_object.Shape.Edges) < 1): _err(_tr("Wrong input: object doesn't have at least one element " "in 'Edges' to use for measuring.")) return None _msg("index: {}".format(index)) try: utils.type_check([(index, int)], name=_name) except TypeError: _err(_tr("Wrong input: must be an integer.")) return None if index < 1: index = 1 _wrn(_tr("index: values below 1 are not allowed; will be set to 1.")) edge = edge_object.getSubObject("Edge" + str(index)) if not edge: _err(_tr("Wrong input: index doesn't correspond to an edge " "in the object.")) return None if not hasattr(edge, "Curve") or edge.Curve.TypeId != 'Part::GeomCircle': _err(_tr("Wrong input: index doesn't correspond to a circular edge.")) return None _msg("mode: {}".format(mode)) try: utils.type_check([(mode, str)], name=_name) except TypeError: _err(_tr("Wrong input: must be a string, 'radius' or 'diameter'.")) return None if mode not in ("radius", "diameter"): _err(_tr("Wrong input: must be a string, 'radius' or 'diameter'.")) return None _msg("dim_line: {}".format(dim_line)) if dim_line: try: utils.type_check([(dim_line, App.Vector)], name=_name) except TypeError: _err(_tr("Wrong input: must be a vector.")) return None else: center = edge_object.Shape.Edges[index - 1].Curve.Center dim_line = center + App.Vector(1, 0, 0) # TODO: the internal function expects an index starting with 0 # so we need to decrease the value here. # This should be changed in the future in the internal function. index -= 1 new_obj = make_dimension(edge_object, index, mode, dim_line) return new_obj
def create_test_file( file_name="draft_test_objects", file_path=os.environ["HOME"], save=False, font_file="/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf"): """Create a complete test file of Draft objects. It draws a frame with information on the software used to create the test document, and fills it with every object that can be created. Parameters ---------- file_name: str, optional It defaults to `'draft_test_objects'`. It is the name of the document that is created. The `file_name` will be appended to `file_path` to determine the actual path to save. The extension `.FCStd` will be added automatically. file_path: str, optional It defaults to the value of `os.environ['HOME']` which in Linux is usually `'/home/user'`. If it is the empty string `''` it will use the value returned by `App.getUserAppDataDir()`, for example, `'/home/user/.FreeCAD/'`. save: bool, optional It defaults to `False`. If it is `True` the new document will be saved to disk after creating all objects. font_file: str, optional It defaults to `'/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf'`. It is the full path of a font in the system to be used to create a `Draft ShapeString`. If the font is not found, this object is not created. Returns ------- App::Document A reference to the test document that was created. To Do ----- Find a reliable way of getting a default font to be able to create the `Draft ShapeString`. """ doc = App.newDocument(file_name) _msg(16 * "-") _msg("Filename: {}".format(file_name)) _msg("If the units tests fail, this script may fail as well") _create_frame() # Line, wire, and fillet _msg(16 * "-") _msg("Line") Draft.make_line(Vector(0, 0, 0), Vector(500, 500, 0)) t_xpos = -50 t_ypos = -200 _t = Draft.make_text(["Line"], Vector(t_xpos, t_ypos, 0)) _set_text(_t) _msg(16 * "-") _msg("Wire") Draft.make_wire( [Vector(500, 0, 0), Vector(1000, 500, 0), Vector(1000, 1000, 0)]) t_xpos += 500 _t = Draft.make_text(["Wire"], Vector(t_xpos, t_ypos, 0)) _set_text(_t) _msg(16 * "-") _msg("Fillet") line_h_1 = Draft.make_line(Vector(1500, 0, 0), Vector(1500, 500, 0)) line_h_2 = Draft.make_line(Vector(1500, 500, 0), Vector(2000, 500, 0)) if App.GuiUp: line_h_1.ViewObject.DrawStyle = "Dotted" line_h_2.ViewObject.DrawStyle = "Dotted" doc.recompute() Draft.make_fillet([line_h_1, line_h_2], 400) t_xpos += 900 _t = Draft.make_text(["Fillet"], Vector(t_xpos, t_ypos, 0)) _set_text(_t) # Circle, arc, arc by 3 points _msg(16 * "-") _msg("Circle") circle = Draft.make_circle(350) circle.Placement.Base = Vector(2500, 500, 0) t_xpos += 1050 _t = Draft.make_text(["Circle"], Vector(t_xpos, t_ypos, 0)) _set_text(_t) _msg(16 * "-") _msg("Circular arc") arc = Draft.make_circle(350, startangle=0, endangle=100) arc.Placement.Base = Vector(3200, 500, 0) t_xpos += 800 _t = Draft.make_text(["Circular arc"], Vector(t_xpos, t_ypos, 0)) _set_text(_t) _msg(16 * "-") _msg("Circular arc 3 points") Draft.make_arc_3points( [Vector(4600, 0, 0), Vector(4600, 800, 0), Vector(4000, 1000, 0)]) t_xpos += 600 _t = Draft.make_text(["Circular arc 3 points"], Vector(t_xpos, t_ypos, 0)) _set_text(_t) # Ellipse, polygon, rectangle _msg(16 * "-") _msg("Ellipse") ellipse = Draft.make_ellipse(500, 300) ellipse.Placement.Base = Vector(5500, 250, 0) t_xpos += 1600 _t = Draft.make_text(["Ellipse"], Vector(t_xpos, t_ypos, 0)) _set_text(_t) _msg(16 * "-") _msg("Polygon") polygon = Draft.make_polygon(5, 250) polygon.Placement.Base = Vector(6500, 500, 0) t_xpos += 950 _t = Draft.make_text(["Polygon"], Vector(t_xpos, t_ypos, 0)) _set_text(_t) _msg(16 * "-") _msg("Rectangle") rectangle = Draft.make_rectangle(500, 1000, 0) rectangle.Placement.Base = Vector(7000, 0, 0) t_xpos += 650 _t = Draft.make_text(["Rectangle"], Vector(t_xpos, t_ypos, 0)) _set_text(_t) # Text _msg(16 * "-") _msg("Text") text = Draft.make_text(["Testing"], Vector(7700, 500, 0)) if App.GuiUp: text.ViewObject.FontSize = 100 t_xpos += 700 _t = Draft.make_text(["Text"], Vector(t_xpos, t_ypos, 0)) _set_text(_t) # Linear dimension _msg(16 * "-") _msg("Linear dimension") dimension = Draft.make_dimension(Vector(8500, 500, 0), Vector(8500, 1000, 0), Vector(9000, 750, 0)) if App.GuiUp: dimension.ViewObject.ArrowSize = 15 dimension.ViewObject.ExtLines = 1000 dimension.ViewObject.ExtOvershoot = 100 dimension.ViewObject.FontSize = 100 dimension.ViewObject.ShowUnit = False t_xpos += 680 _t = Draft.make_text(["Dimension"], Vector(t_xpos, t_ypos, 0)) _set_text(_t) # Radius and diameter dimension _msg(16 * "-") _msg("Radius and diameter dimension") arc_h = Draft.make_circle(500, startangle=0, endangle=90) arc_h.Placement.Base = Vector(9500, 0, 0) doc.recompute() dimension_r = Draft.make_dimension(arc_h, 0, "radius", Vector(9750, 200, 0)) if App.GuiUp: dimension_r.ViewObject.ArrowSize = 15 dimension_r.ViewObject.FontSize = 100 dimension_r.ViewObject.ShowUnit = False arc_h2 = Draft.make_circle(450, startangle=-120, endangle=80) arc_h2.Placement.Base = Vector(10000, 1000, 0) doc.recompute() dimension_d = Draft.make_dimension(arc_h2, 0, "diameter", Vector(10750, 900, 0)) if App.GuiUp: dimension_d.ViewObject.ArrowSize = 15 dimension_d.ViewObject.FontSize = 100 dimension_d.ViewObject.ShowUnit = False t_xpos += 950 _t = Draft.make_text(["Radius dimension", "Diameter dimension"], Vector(t_xpos, t_ypos, 0)) _set_text(_t) # Angular dimension _msg(16 * "-") _msg("Angular dimension") Draft.make_line(Vector(10500, 300, 0), Vector(11500, 1000, 0)) Draft.make_line(Vector(10500, 300, 0), Vector(11500, 0, 0)) angle1 = math.radians(40) angle2 = math.radians(-20) dimension_a = Draft.make_angular_dimension(Vector(10500, 300, 0), [angle1, angle2], Vector(11500, 300, 0)) if App.GuiUp: dimension_a.ViewObject.ArrowSize = 15 dimension_a.ViewObject.FontSize = 100 t_xpos += 1700 _t = Draft.make_text(["Angle dimension"], Vector(t_xpos, t_ypos, 0)) _set_text(_t) # BSpline _msg(16 * "-") _msg("BSpline") Draft.make_bspline([ Vector(12500, 0, 0), Vector(12500, 500, 0), Vector(13000, 500, 0), Vector(13000, 1000, 0) ]) t_xpos += 1500 _t = Draft.make_text(["BSpline"], Vector(t_xpos, t_ypos, 0)) _set_text(_t) # Point _msg(16 * "-") _msg("Point") point = Draft.make_point(13500, 500, 0) if App.GuiUp: point.ViewObject.PointSize = 10 t_xpos += 900 _t = Draft.make_text(["Point"], Vector(t_xpos, t_ypos, 0)) _set_text(_t) # Shapestring _msg(16 * "-") _msg("Shapestring") try: shape_string = Draft.make_shapestring("Testing", font_file, 100) shape_string.Placement.Base = Vector(14000, 500) except Exception: _wrn("Shapestring could not be created") _wrn("Possible cause: the font file may not exist") _wrn(font_file) rect = Draft.make_rectangle(500, 100) rect.Placement.Base = Vector(14000, 500) t_xpos += 600 _t = Draft.make_text(["Shapestring"], Vector(t_xpos, t_ypos, 0)) _set_text(_t) # Facebinder _msg(16 * "-") _msg("Facebinder") box = doc.addObject("Part::Box", "Cube") box.Length = 200 box.Width = 500 box.Height = 100 box.Placement.Base = Vector(15000, 0, 0) if App.GuiUp: box.ViewObject.Visibility = False facebinder = Draft.make_facebinder([(box, ("Face1", "Face3", "Face6"))]) facebinder.Extrusion = 10 t_xpos += 780 _t = Draft.make_text(["Facebinder"], Vector(t_xpos, t_ypos, 0)) _set_text(_t) # Cubic bezier curve, n-degree bezier curve _msg(16 * "-") _msg("Cubic bezier") Draft.make_bezcurve([ Vector(15500, 0, 0), Vector(15578, 485, 0), Vector(15879, 154, 0), Vector(15975, 400, 0), Vector(16070, 668, 0), Vector(16423, 925, 0), Vector(16500, 500, 0) ], degree=3) t_xpos += 680 _t = Draft.make_text(["Cubic bezier"], Vector(t_xpos, t_ypos, 0)) _set_text(_t) _msg(16 * "-") _msg("N-degree bezier") Draft.make_bezcurve([ Vector(16500, 0, 0), Vector(17000, 500, 0), Vector(17500, 500, 0), Vector(17500, 1000, 0), Vector(17000, 1000, 0), Vector(17063, 1256, 0), Vector(17732, 1227, 0), Vector(17790, 720, 0), Vector(17702, 242, 0) ]) t_xpos += 1200 _t = Draft.make_text(["n-Bezier"], Vector(t_xpos, t_ypos, 0)) _set_text(_t) # Label _msg(16 * "-") _msg("Label") place = App.Placement(Vector(18500, 500, 0), App.Rotation()) label = Draft.make_label(targetpoint=Vector(18000, 0, 0), distance=-250, placement=place) label.Text = "Testing" if App.GuiUp: label.ViewObject.ArrowSize = 15 label.ViewObject.TextSize = 100 doc.recompute() t_xpos += 1200 _t = Draft.make_text(["Label"], Vector(t_xpos, t_ypos, 0)) _set_text(_t) # Orthogonal array and orthogonal link array _msg(16 * "-") _msg("Orthogonal array") rect_h = Draft.make_rectangle(500, 500) rect_h.Placement.Base = Vector(1500, 2500, 0) if App.GuiUp: rect_h.ViewObject.Visibility = False Draft.makeArray(rect_h, Vector(600, 0, 0), Vector(0, 600, 0), Vector(0, 0, 0), 3, 2, 1) t_xpos = 1700 t_ypos = 2200 _t = Draft.make_text(["Array"], Vector(t_xpos, t_ypos, 0)) _set_text(_t) rect_h_2 = Draft.make_rectangle(500, 100) rect_h_2.Placement.Base = Vector(1500, 5000, 0) if App.GuiUp: rect_h_2.ViewObject.Visibility = False _msg(16 * "-") _msg("Orthogonal link array") Draft.makeArray(rect_h_2, Vector(800, 0, 0), Vector(0, 500, 0), Vector(0, 0, 0), 2, 4, 1, use_link=True) t_ypos += 2600 _t = Draft.make_text(["Link array"], Vector(t_xpos, t_ypos, 0)) _set_text(_t) # Polar array and polar link array _msg(16 * "-") _msg("Polar array") wire_h = Draft.make_wire([ Vector(5500, 3000, 0), Vector(6000, 3500, 0), Vector(6000, 3200, 0), Vector(5800, 3200, 0) ]) if App.GuiUp: wire_h.ViewObject.Visibility = False Draft.makeArray(wire_h, Vector(5000, 3000, 0), 200, 8) t_xpos = 4600 t_ypos = 2200 _t = Draft.make_text(["Polar array"], Vector(t_xpos, t_ypos, 0)) _set_text(_t) _msg(16 * "-") _msg("Polar link array") wire_h_2 = Draft.make_wire([ Vector(5500, 6000, 0), Vector(6000, 6000, 0), Vector(5800, 5700, 0), Vector(5800, 5750, 0) ]) if App.GuiUp: wire_h_2.ViewObject.Visibility = False Draft.makeArray(wire_h_2, Vector(5000, 6000, 0), 200, 8, use_link=True) t_ypos += 3200 _t = Draft.make_text(["Polar link array"], Vector(t_xpos, t_ypos, 0)) _set_text(_t) # Circular array and circular link array _msg(16 * "-") _msg("Circular array") poly_h = Draft.make_polygon(5, 200) poly_h.Placement.Base = Vector(8000, 3000, 0) if App.GuiUp: poly_h.ViewObject.Visibility = False Draft.makeArray(poly_h, 500, 600, Vector(0, 0, 1), Vector(0, 0, 0), 3, 1) t_xpos = 7700 t_ypos = 1700 _t = Draft.make_text(["Circular array"], Vector(t_xpos, t_ypos, 0)) _set_text(_t) _msg(16 * "-") _msg("Circular link array") poly_h_2 = Draft.make_polygon(6, 150) poly_h_2.Placement.Base = Vector(8000, 6250, 0) if App.GuiUp: poly_h_2.ViewObject.Visibility = False Draft.makeArray(poly_h_2, 550, 450, Vector(0, 0, 1), Vector(0, 0, 0), 3, 1, use_link=True) t_ypos += 3100 _t = Draft.make_text(["Circular link array"], Vector(t_xpos, t_ypos, 0)) _set_text(_t) # Path array and path link array _msg(16 * "-") _msg("Path array") poly_h = Draft.make_polygon(3, 250) poly_h.Placement.Base = Vector(10500, 3000, 0) if App.GuiUp: poly_h.ViewObject.Visibility = False bspline_path = Draft.make_bspline([ Vector(10500, 2500, 0), Vector(11000, 3000, 0), Vector(11500, 3200, 0), Vector(12000, 4000, 0) ]) Draft.makePathArray(poly_h, bspline_path, 5) t_xpos = 10400 t_ypos = 2200 _t = Draft.make_text(["Path array"], Vector(t_xpos, t_ypos, 0)) _set_text(_t) _msg(16 * "-") _msg("Path link array") poly_h_2 = Draft.make_polygon(4, 200) poly_h_2.Placement.Base = Vector(10500, 5000, 0) if App.GuiUp: poly_h_2.ViewObject.Visibility = False bspline_path_2 = Draft.make_bspline([ Vector(10500, 4500, 0), Vector(11000, 6800, 0), Vector(11500, 6000, 0), Vector(12000, 5200, 0) ]) Draft.makePathArray(poly_h_2, bspline_path_2, 6, use_link=True) t_ypos += 2000 _t = Draft.make_text(["Path link array"], Vector(t_xpos, t_ypos, 0)) _set_text(_t) # Point array _msg(16 * "-") _msg("Point array") poly_h = Draft.make_polygon(3, 250) point_1 = Draft.make_point(13000, 3000, 0) point_2 = Draft.make_point(13000, 3500, 0) point_3 = Draft.make_point(14000, 2500, 0) point_4 = Draft.make_point(14000, 3000, 0) add_list, delete_list = Draft.upgrade([point_1, point_2, point_3, point_4]) compound = add_list[0] if App.GuiUp: compound.ViewObject.PointSize = 5 Draft.makePointArray(poly_h, compound) t_xpos = 13000 t_ypos = 2200 _t = Draft.make_text(["Point array"], Vector(t_xpos, t_ypos, 0)) _set_text(_t) # Clone and mirror _msg(16 * "-") _msg("Clone") wire_h = Draft.make_wire([ Vector(15000, 2500, 0), Vector(15200, 3000, 0), Vector(15500, 2500, 0), Vector(15200, 2300, 0) ]) Draft.clone(wire_h, Vector(0, 1000, 0)) t_xpos = 15000 t_ypos = 2100 _t = Draft.make_text(["Clone"], Vector(t_xpos, t_ypos, 0)) _set_text(_t) _msg(16 * "-") _msg("Mirror") wire_h = Draft.make_wire([ Vector(17000, 2500, 0), Vector(16500, 4000, 0), Vector(16000, 2700, 0), Vector(16500, 2500, 0), Vector(16700, 2700, 0) ]) Draft.mirror(wire_h, Vector(17100, 2000, 0), Vector(17100, 4000, 0)) t_xpos = 17000 t_ypos = 2200 _t = Draft.make_text(["Mirror"], Vector(t_xpos, t_ypos, 0)) _set_text(_t) doc.recompute() if App.GuiUp: Gui.runCommand("Std_ViewFitAll") # Export if not file_path: file_path = App.getUserAppDataDir() out_name = os.path.join(file_path, file_name + ".FCStd") doc.FileName = out_name if save: doc.save() _msg(16 * "-") _msg("Saved: {}".format(out_name)) return doc
def dim_symbol(symbol=None, invert=False): """Return the specified dimension symbol. Parameters ---------- symbol: int, optional It defaults to `None`, in which it gets the value from the parameter database, `get_param("dimsymbol", 0)`. A numerical value defines different markers * 0, `SoSphere` * 1, `SoSeparator` with a `SoLineSet`, a circle (in fact a 24 sided polygon) * 2, `SoSeparator` with a `soCone` * 3, `SoSeparator` with a `SoFaceSet` * 4, `SoSeparator` with a `SoLineSet`, calling `dim_dash` * Otherwise, `SoSphere` invert: bool, optional It defaults to `False`. If it is `True` and `symbol=2`, the cone will be rotated -90 degrees around the Z axis, otherwise the rotation is positive, +90 degrees. Returns ------- Coin.SoNode A `Coin.SoSphere`, or `Coin.SoSeparator` (circle, cone, face, line) that will be used as a dimension symbol. """ if symbol is None: symbol = utils.get_param("dimsymbol", 0) if symbol == 0: # marker = coin.SoMarkerSet() # marker.markerIndex = 80 # Returning a sphere means that the bounding box will # be 3-dimensional; a marker will always be planar seen from any # orientation but it currently doesn't work correctly marker = coin.SoSphere() return marker elif symbol == 1: marker = coin.SoSeparator() v = coin.SoVertexProperty() for i in range(25): ang = math.radians(i * 15) v.vertex.set1Value(i, (math.sin(ang), math.cos(ang), 0)) p = coin.SoLineSet() p.vertexProperty = v marker.addChild(p) return marker elif symbol == 2: marker = coin.SoSeparator() t = coin.SoTransform() t.translation.setValue((0, -2, 0)) t.center.setValue((0, 2, 0)) if invert: t.rotation.setValue(coin.SbVec3f((0, 0, 1)), -math.pi / 2) else: t.rotation.setValue(coin.SbVec3f((0, 0, 1)), math.pi / 2) c = coin.SoCone() c.height.setValue(4) marker.addChild(t) marker.addChild(c) return marker elif symbol == 3: marker = coin.SoSeparator() # hints are required otherwise only the bottom of the face is colored h = coin.SoShapeHints() h.vertexOrdering = h.COUNTERCLOCKWISE c = coin.SoCoordinate3() c.point.setValues([(-1, -2, 0), (0, 2, 0), (1, 2, 0), (0, -2, 0)]) f = coin.SoFaceSet() marker.addChild(h) marker.addChild(c) marker.addChild(f) return marker elif symbol == 4: return dim_dash((-1.5, -1.5, 0), (1.5, 1.5, 0)) else: _wrn( translate("draft", "Symbol not implemented. Using a default symbol.")) return coin.SoSphere()
def calculate_placement(globalRotation, edge, offset, RefPt, xlate, align, normal=None, mode='Original', overrideNormal=False): """Orient shape to a local coordinate system (tangent, normal, binormal). Orient shape at parameter offset, normally length. http://en.wikipedia.org/wiki/Euler_angles (previous version) http://en.wikipedia.org/wiki/Quaternions """ # Start with a null Placement so the translation goes to the right place. # Then apply the global orientation. placement = App.Placement() placement.Rotation = globalRotation placement.move(RefPt + xlate) if not align: return placement nullv = App.Vector(0, 0, 0) defNormal = App.Vector(0.0, 0.0, 1.0) if normal: defNormal = normal try: t = edge.tangentAt(get_parameter_from_v0(edge, offset)) t.normalize() except: _wrn( translate("draft", "Cannot calculate path tangent. Copy not aligned.")) return placement if mode in ('Original', 'Tangent'): if normal is None: n = defNormal else: n = normal n.normalize() try: b = t.cross(n) b.normalize() except Exception: # weird special case, tangent and normal parallel b = nullv _wrn( translate( "draft", "Tangent and normal are parallel. Copy not aligned.")) return placement if overrideNormal: priority = "XZY" newRot = App.Rotation(t, b, n, priority) # t/x, b/y, n/z else: # must follow X, try to follow Z, Y is what it is priority = "XZY" newRot = App.Rotation(t, n, b, priority) elif mode == 'Frenet': try: n = edge.normalAt(get_parameter_from_v0(edge, offset)) n.normalize() except App.Base.FreeCADError: # no/infinite normals here n = defNormal _msg( translate("draft", "Cannot calculate path normal, using default.")) try: b = t.cross(n) b.normalize() except Exception: b = nullv _wrn( translate("draft", "Cannot calculate path binormal. Copy not aligned.")) return placement priority = "XZY" newRot = App.Rotation(t, n, b, priority) # t/x, n/y, b/z else: _msg( translate("draft", "AlignMode {} is not implemented").format(mode)) return placement # Have valid tangent, normal, binormal newGRot = newRot.multiply(globalRotation) placement.Rotation = newGRot return placement
def redraw_3d_view(): """Force a redraw of 3D view or do nothing if it fails.""" try: Gui.ActiveDocument.ActiveView.redraw() except AttributeError as err: _wrn(err)
def load_texture(filename, size=None, gui=App.GuiUp): """Return a Coin.SoSFImage to use as a texture for a 2D plane. This function only works if the graphical interface is available as the visual properties that can be applied to a shape are attributes of the view provider (`obj.ViewObject`). Parameters ---------- filename: str A path to a pixel image file (PNG) that can be used as a texture on the face of an object. size: tuple of two int, or a single int, optional It defaults to `None`. If a tuple is given, the two values define the width and height in pixels to which the loaded image will be scaled. If it is a single value, it is used for both dimensions. If it is `None`, the size will be determined from the `QImage` created from `filename`. CURRENTLY the input `size` parameter IS NOT USED. It always uses the `QImage` to determine this information. gui: bool, optional It defaults to the value of `App.GuiUp`, which is `True` when the interface exists, and `False` otherwise. This value can be set to `False` to simulate when the interface is not available. Returns ------- coin.SoSFImage An image object with the appropriate size, number of components (grayscale, grayscale and transparency, color, color and transparency), and byte data. It returns `None` if the interface is not available, or if there is a problem creating the image. """ if gui: # from pivy import coin # from PySide import QtGui, QtSvg try: p = QtGui.QImage(filename) if p.isNull(): _wrn("load_texture: " + _tr("image is Null")) if not os.path.exists(filename): raise FileNotFoundError( -1, _tr("filename does not exist " "on the system or " "on the resource file"), filename) # This is buggy so it was de-activated. # # TODO: allow SVGs to use resolutions # if size and (".svg" in filename.lower()): # # this is a pattern, not a texture # if isinstance(size, int): # size = (size, size) # svgr = QtSvg.QSvgRenderer(filename) # p = QtGui.QImage(size[0], size[1], # QtGui.QImage.Format_ARGB32) # pa = QtGui.QPainter() # pa.begin(p) # svgr.render(pa) # pa.end() # else: # p = QtGui.QImage(filename) size = coin.SbVec2s(p.width(), p.height()) buffersize = p.byteCount() width = size[0] height = size[1] numcomponents = int(buffersize / (width * height)) img = coin.SoSFImage() byteList = bytearray() # The SoSFImage needs to be filled with bytes. # The pixel information is converted into a Qt color, gray, # red, green, blue, or transparency (alpha), # depending on the input image. for y in range(height): # line = width*numcomponents*(height-(y)); for x in range(width): rgba = p.pixel(x, y) if numcomponents <= 2: byteList.append(QtGui.qGray(rgba)) if numcomponents == 2: byteList.append(QtGui.qAlpha(rgba)) elif numcomponents <= 4: byteList.append(QtGui.qRed(rgba)) byteList.append(QtGui.qGreen(rgba)) byteList.append(QtGui.qBlue(rgba)) if numcomponents == 4: byteList.append(QtGui.qAlpha(rgba)) # line += numcomponents _bytes = bytes(byteList) img.setValue(size, numcomponents, _bytes) except FileNotFoundError as exc: _wrn("load_texture: {0}, {1}".format(exc.strerror, exc.filename)) return None except Exception as exc: _wrn(str(exc)) _wrn("load_texture: " + _tr("unable to load texture")) return None else: return img return None
def _create_objects(doc=None, font_file=None, hatch_file=None, hatch_name=None): """Create the objects of the test file.""" if not doc: doc = App.activeDocument() if not doc: doc = App.newDocument() # Drafting ############################################################## # Line _msg(16 * "-") _msg("Line") Draft.make_line(Vector(0, 0, 0), Vector(500, 500, 0)) _set_text(["Line"], Vector(0, -200, 0)) # Wire _msg(16 * "-") _msg("Wire") Draft.make_wire( [Vector(1000, 0, 0), Vector(1500, 250, 0), Vector(1500, 500, 0)]) _set_text(["Wire"], Vector(1000, -200, 0)) # Fillet _msg(16 * "-") _msg("Fillet") line_1 = Draft.make_line(Vector(2000, 0, 0), Vector(2000, 500, 0)) line_2 = Draft.make_line(Vector(2000, 500, 0), Vector(2500, 500, 0)) if App.GuiUp: line_1.ViewObject.DrawStyle = "Dotted" line_2.ViewObject.DrawStyle = "Dotted" doc.recompute() Draft.make_fillet([line_1, line_2], 400) _set_text(["Fillet"], Vector(2000, -200, 0)) # Circular arc _msg(16 * "-") _msg("Circular arc") arc = Draft.make_circle(250, startangle=90, endangle=270) arc.Placement.Base = Vector(3250, 250, 0) _set_text(["Circular arc"], Vector(3000, -200, 0)) # Circular arc 3 points _msg(16 * "-") _msg("Circular arc 3 points") Draft.make_arc_3points( [Vector(4250, 0, 0), Vector(4000, 250, 0), Vector(4250, 500, 0)]) _set_text(["Circular arc 3 points"], Vector(4000, -200, 0)) # Circle _msg(16 * "-") _msg("Circle") circle = Draft.make_circle(250) circle.Placement.Base = Vector(5250, 250, 0) _set_text(["Circle"], Vector(5000, -200, 0)) # Ellipse _msg(16 * "-") _msg("Ellipse") ellipse = Draft.make_ellipse(250, 150) ellipse.Placement.Base = Vector(6250, 150, 0) _set_text(["Ellipse"], Vector(6000, -200, 0)) # Rectangle _msg(16 * "-") _msg("Rectangle") rectangle = Draft.make_rectangle(500, 300, 0) rectangle.Placement.Base = Vector(7000, 0, 0) _set_text(["Rectangle"], Vector(7000, -200, 0)) # Polygon _msg(16 * "-") _msg("Polygon") polygon = Draft.make_polygon(5, 250) polygon.Placement.Base = Vector(8250, 250, 0) _set_text(["Polygon"], Vector(8000, -200, 0)) # BSpline _msg(16 * "-") _msg("BSpline") Draft.make_bspline([ Vector(9000, 0, 0), Vector(9100, 200, 0), Vector(9400, 300, 0), Vector(9500, 500, 0) ]) _set_text(["BSpline"], Vector(9000, -200, 0)) # Cubic bezier _msg(16 * "-") _msg("Cubic bezier") Draft.make_bezcurve([ Vector(10000, 0, 0), Vector(10000, 500, 0), Vector(10500, 0, 0), Vector(10500, 500, 0) ], degree=3) _set_text(["Cubic bezier"], Vector(10000, -200, 0)) # N-degree bezier _msg(16 * "-") _msg("N-degree bezier") Draft.make_bezcurve([ Vector(11000, 0, 0), Vector(11100, 400, 0), Vector(11250, 250, 0), Vector(11400, 100, 0), Vector(11500, 500, 0) ]) _set_text(["N-degree bezier"], Vector(11000, -200, 0)) # Point _msg(16 * "-") _msg("Point") point = Draft.make_point(12000, 0, 0) if App.GuiUp: point.ViewObject.PointSize = 10 _set_text(["Point"], Vector(12000, -200, 0)) # Facebinder _msg(16 * "-") _msg("Facebinder") box = doc.addObject("Part::Box", "Box") box.Length = 200 box.Width = 500 box.Height = 100 box.Placement.Base = Vector(13000, 0, 0) if App.GuiUp: box.ViewObject.Visibility = False facebinder = Draft.make_facebinder([(box, ("Face1", "Face3", "Face6"))]) facebinder.Extrusion = 10 _set_text(["Facebinder"], Vector(13000, -200, 0)) # Shapestring _msg(16 * "-") _msg("Shapestring") try: shape_string = Draft.make_shapestring("Testing", font_file, 100) shape_string.Placement.Base = Vector(14000, 0) except Exception: _wrn("Shapestring could not be created") _wrn("Possible cause: the font file may not exist") _wrn(font_file) _set_text(["Shapestring"], Vector(14000, -200, 0)) # Hatch _msg(16 * "-") _msg("Hatch") rectangle = Draft.make_rectangle(500, 300, 0) rectangle.Placement.Base = Vector(15000, 0, 0) rectangle.MakeFace = True if App.GuiUp: rectangle.ViewObject.Visibility = False try: Draft.make_hatch(rectangle, hatch_file, hatch_name, scale=10, rotation=45) except Exception: _wrn("Hatch could not be created") _wrn("Possible cause: the hatch file may not exist") _wrn(hatch_file) _set_text(["Hatch"], Vector(15000, -200, 0)) # Annotation ############################################################ # Text _msg(16 * "-") _msg("Text") text = Draft.make_text(["Testing", "text"], Vector(0, 2100, 0)) if App.GuiUp: text.ViewObject.FontSize = 100 _set_text(["Text"], Vector(0, 1800, 0)) # Linear dimension _msg(16 * "-") _msg("Linear dimension") dimension = Draft.make_linear_dimension(Vector(1500, 2000, 0), Vector(1500, 2400, 0), Vector(1000, 2200, 0)) if App.GuiUp: dimension.ViewObject.ArrowSize = 15 dimension.ViewObject.ExtLines = 0 dimension.ViewObject.ExtOvershoot = 50 dimension.ViewObject.DimOvershoot = 25 dimension.ViewObject.FontSize = 50 dimension.ViewObject.Decimals = 1 dimension.ViewObject.ShowUnit = False line = Draft.make_wire([Vector(1500, 2600, 0), Vector(1500, 3000, 0)]) doc.recompute() dimension = Draft.make_linear_dimension_obj(line, 1, 2, Vector(1000, 2800, 0)) if App.GuiUp: dimension.ViewObject.ArrowSize = 15 dimension.ViewObject.ArrowType = "Arrow" dimension.ViewObject.ExtLines = -50 dimension.ViewObject.ExtOvershoot = 50 dimension.ViewObject.DimOvershoot = 25 dimension.ViewObject.FontSize = 50 dimension.ViewObject.Decimals = 1 dimension.ViewObject.ShowUnit = False _set_text(["Dimension"], Vector(1000, 1800, 0)) # Radius and diameter dimension _msg(16 * "-") _msg("Radius and diameter dimension") circle = Draft.make_circle(200) circle.Placement.Base = Vector(2200, 2200, 0) circle.MakeFace = False doc.recompute() dimension = Draft.make_radial_dimension_obj(circle, 1, "radius", Vector(2300, 2300, 0)) if App.GuiUp: dimension.ViewObject.ArrowSize = 15 dimension.ViewObject.FontSize = 50 dimension.ViewObject.Decimals = 1 dimension.ViewObject.ShowUnit = False circle = Draft.make_circle(200) circle.Placement.Base = Vector(2200, 2800, 0) circle.MakeFace = False doc.recompute() dimension = Draft.make_radial_dimension_obj(circle, 1, "diameter", Vector(2300, 2900, 0)) if App.GuiUp: dimension.ViewObject.ArrowSize = 15 dimension.ViewObject.FontSize = 50 dimension.ViewObject.Decimals = 1 dimension.ViewObject.ShowUnit = False _set_text(["Radius dimension", "Diameter dimension"], Vector(2000, 1800, 0)) # Angular dimension _msg(16 * "-") _msg("Angular dimension") Draft.make_line(Vector(3000, 2000, 0), Vector(3500, 2000, 0)) Draft.make_line(Vector(3000, 2000, 0), Vector(3500, 2500, 0)) dimension = Draft.make_angular_dimension(Vector(3000, 2000, 0), [0, 45], Vector(3250, 2250, 0)) if App.GuiUp: dimension.ViewObject.ArrowSize = 15 dimension.ViewObject.FontSize = 50 dimension.ViewObject.Decimals = 1 _set_text(["Angle dimension"], Vector(3000, 1800, 0)) # Label _msg(16 * "-") _msg("Label") place = App.Placement(Vector(4250, 2250, 0), App.Rotation()) label = Draft.make_label(target_point=Vector(4000, 2000, 0), placement=place, custom_text="Example label", distance=-100) label.Text = "Testing" if App.GuiUp: label.ViewObject.ArrowSize = 15 label.ViewObject.TextSize = 50 doc.recompute() _set_text(["Label"], Vector(4000, 1800, 0)) # Array ################################################################# # Orthogonal array _msg(16 * "-") _msg("Orthogonal array") rectangle = Draft.make_rectangle(100, 50) rectangle.Placement.Base = Vector(0, 4000, 0) if App.GuiUp: rectangle.ViewObject.Visibility = False Draft.make_ortho_array(rectangle, Vector(200, 0, 0), Vector(0, 150, 0), Vector(0, 0, 0), 3, 2, 1, use_link=False) _set_text(["Orthogonal array"], Vector(0, 3800, 0)) # Orthogonal link array _msg(16 * "-") _msg("Orthogonal link array") rectangle = Draft.make_rectangle(50, 50) rectangle.Placement.Base = Vector(1000, 4000, 0) if App.GuiUp: rectangle.ViewObject.Visibility = False Draft.make_ortho_array(rectangle, Vector(200, 0, 0), Vector(0, 150, 0), Vector(0, 0, 0), 3, 2, 1, use_link=True) _set_text(["Orthogonal link array"], Vector(1000, 3800, 0)) # Polar array _msg(16 * "-") _msg("Polar array") wire = Draft.make_wire( [Vector(2000, 4050, 0), Vector(2000, 4000, 0), Vector(2100, 4000, 0)]) if App.GuiUp: wire.ViewObject.Visibility = False Draft.make_polar_array(wire, 4, 90, Vector(2000, 4250, 0), use_link=False) _set_text(["Polar array"], Vector(2000, 3800, 0)) # Polar link array _msg(16 * "-") _msg("Polar link array") wire = Draft.make_wire( [Vector(3000, 4050, 0), Vector(3000, 4000, 0), Vector(3050, 4000, 0)]) if App.GuiUp: wire.ViewObject.Visibility = False Draft.make_polar_array(wire, 4, 90, Vector(3000, 4250, 0), use_link=True) _set_text(["Polar link array"], Vector(3000, 3800, 0)) # Circular array _msg(16 * "-") _msg("Circular array") polygon = Draft.make_polygon(5, 30) polygon.Placement.Base = Vector(4250, 4250, 0) if App.GuiUp: polygon.ViewObject.Visibility = False Draft.make_circular_array(polygon, 110, 100, 3, 1, Vector(0, 0, 1), Vector(0, 0, 0), use_link=False) _set_text(["Circular array"], Vector(4000, 3800, 0)) # Circular link array _msg(16 * "-") _msg("Circular link array") polygon = Draft.make_polygon(6, 30) polygon.Placement.Base = Vector(5250, 4250, 0) if App.GuiUp: polygon.ViewObject.Visibility = False Draft.make_circular_array(polygon, 110, 100, 3, 1, Vector(0, 0, 1), Vector(0, 0, 0), use_link=True) _set_text(["Circular link array"], Vector(5000, 3800, 0)) # Path array _msg(16 * "-") _msg("Path array") polygon = Draft.make_polygon(3, 30) polygon.Placement.Base = Vector(6000, 4000, 0) if App.GuiUp: polygon.ViewObject.Visibility = False spline = Draft.make_bspline([ Vector(6000, 4000, 0), Vector(6100, 4200, 0), Vector(6400, 4300, 0), Vector(6500, 4500, 0) ]) Draft.make_path_array(polygon, spline, 5, use_link=False) _set_text(["Path array"], Vector(6000, 3800, 0)) # Path link array _msg(16 * "-") _msg("Path link array") polygon = Draft.make_polygon(4, 30) polygon.Placement.Base = Vector(7000, 4000, 0) if App.GuiUp: polygon.ViewObject.Visibility = False spline = Draft.make_bspline([ Vector(7000, 4000, 0), Vector(7100, 4200, 0), Vector(7400, 4300, 0), Vector(7500, 4500, 0) ]) Draft.make_path_array(polygon, spline, 5, use_link=True) _set_text(["Path link array"], Vector(7000, 3800, 0)) # Point array _msg(16 * "-") _msg("Point array") polygon = Draft.make_polygon(3, 30) polygon.Placement.Base = Vector(8000, 4000, 0) point_1 = Draft.make_point(8030, 4030, 0) point_2 = Draft.make_point(8030, 4250, 0) point_3 = Draft.make_point(8470, 4250, 0) point_4 = Draft.make_point(8470, 4470, 0) add_list, delete_list = Draft.upgrade([point_1, point_2, point_3, point_4]) compound = add_list[0] if App.GuiUp: compound.ViewObject.PointSize = 5 Draft.make_point_array(polygon, compound, use_link=False) _set_text(["Point array"], Vector(8000, 3800, 0)) # Point link array _msg(16 * "-") _msg("Point link array") polygon = Draft.make_polygon(4, 30) polygon.Placement.Base = Vector(9000, 4000, 0) point_1 = Draft.make_point(9030, 4030, 0) point_2 = Draft.make_point(9030, 4250, 0) point_3 = Draft.make_point(9470, 4250, 0) point_4 = Draft.make_point(9470, 4470, 0) add_list, delete_list = Draft.upgrade([point_1, point_2, point_3, point_4]) compound = add_list[0] if App.GuiUp: compound.ViewObject.PointSize = 5 Draft.make_point_array(polygon, compound, use_link=True) _set_text(["Point link array"], Vector(9000, 3800, 0)) # Miscellaneous ######################################################### # Mirror _msg(16 * "-") _msg("Mirror") wire = Draft.make_wire( [Vector(0, 6000, 0), Vector(150, 6200, 0), Vector(500, 6000, 0)]) Draft.mirror(wire, Vector(0, 6250, 0), Vector(500, 6250, 0)) _set_text(["Mirror"], Vector(0, 5800, 0)) # Clone _msg(16 * "-") _msg("Clone") wire = Draft.make_wire( [Vector(1000, 6000, 0), Vector(1150, 6200, 0), Vector(1500, 6000, 0)]) Draft.make_clone(wire, Vector(0, 300, 0)) _set_text(["Clone"], Vector(1000, 5800, 0)) # Shape2DView _msg(16 * "-") _msg("Shape2DView") place = App.Placement(Vector(2000, 6000, 0), App.Rotation(Vector(0, 0, 1), Vector(1, 2, 3))) box = doc.addObject("Part::Box", "Box") box.Length = 200 box.Width = 500 box.Height = 100 box.Placement = place if App.GuiUp: box.ViewObject.Visibility = False Draft.make_shape2dview(box) _set_text(["Shape2DView"], Vector(2000, 5800, 0)) # WorkingPlaneProxy _msg(16 * "-") _msg("WorkingPlaneProxy") place = App.Placement(Vector(3250, 6250, 0), App.Rotation()) proxy = Draft.make_workingplaneproxy(place) if App.GuiUp: proxy.ViewObject.DisplaySize = 500 proxy.ViewObject.ArrowSize = 50 _set_text(["WorkingPlaneProxy"], Vector(3000, 5800, 0)) # Layer _msg(16 * "-") _msg("Layer") layer = Draft.make_layer("Custom layer", line_color=(0.33, 0.0, 0.49), shape_color=(0.56, 0.89, 0.56), line_width=4, transparency=50) box = doc.addObject("Part::Box", "Box") box.Length = 200 box.Width = 500 box.Height = 100 box.Placement.Base = Vector(4000, 6000, 0) sphere = doc.addObject("Part::Sphere", "Sphere") sphere.Radius = 100 sphere.Placement.Base = Vector(4400, 6250, 0) layer.Proxy.addObject(layer, box) layer.Proxy.addObject(layer, sphere) _set_text(["Layer"], Vector(4000, 5800, 0)) doc.recompute()