def make_point_array(base, ptlst): """make_point_array(base,pointlist) Make a Draft PointArray object. Parameters ---------- base : TODO: describe plist : TODO: describe """ obj = App.ActiveDocument.addObject("Part::FeaturePython", "PointArray") PointArray(obj, base, ptlst) obj.Base = base obj.PointList = ptlst if App.GuiUp: ViewProviderDraftArray(obj.ViewObject) base.ViewObject.hide() gui_utils.formatObject(obj, obj.Base) if len(obj.Base.ViewObject.DiffuseColor) > 1: obj.ViewObject.Proxy.resetColors(obj.ViewObject) gui_utils.select(obj) return obj
def rotate(objectslist, angle, center=App.Vector(0,0,0), axis=App.Vector(0,0,1), copy=False): """rotate(objects,angle,[center,axis,copy]) Rotates the objects contained in objects (that can be a list of objects or an object) of the given angle (in degrees) around the center, using axis as a rotation axis. Parameters ---------- objectlist : list angle : list center : Base.Vector axis : Base.Vector If axis is omitted, the rotation will be around the vertical Z axis. copy : bool If copy is True, the actual objects are not moved, but copies are created instead. Return ---------- The objects (or their copies) are returned. """ import Part utils.type_check([(copy,bool)], "rotate") if not isinstance(objectslist,list): objectslist = [objectslist] objectslist.extend(groups.get_movable_children(objectslist)) newobjlist = [] newgroups = {} objectslist = utils.filter_objects_for_modifiers(objectslist, copy) for obj in objectslist: newobj = None # real_center and real_axis are introduced to take into account # the possibility that object is inside an App::Part if hasattr(obj, "getGlobalPlacement"): ci = obj.getGlobalPlacement().inverse().multVec(center) real_center = obj.Placement.multVec(ci) ai = obj.getGlobalPlacement().inverse().Rotation.multVec(axis) real_axis = obj.Placement.Rotation.multVec(ai) else: real_center = center real_axis = axis if copy: newobj = make_copy.make_copy(obj) else: newobj = obj if obj.isDerivedFrom("App::Annotation"): # TODO: this is very different from how move handle annotations # maybe we can uniform the two methods if axis.normalize() == App.Vector(1,0,0): newobj.ViewObject.RotationAxis = "X" newobj.ViewObject.Rotation = angle elif axis.normalize() == App.Vector(0,1,0): newobj.ViewObject.RotationAxis = "Y" newobj.ViewObject.Rotation = angle elif axis.normalize() == App.Vector(0,-1,0): newobj.ViewObject.RotationAxis = "Y" newobj.ViewObject.Rotation = -angle elif axis.normalize() == App.Vector(0,0,1): newobj.ViewObject.RotationAxis = "Z" newobj.ViewObject.Rotation = angle elif axis.normalize() == App.Vector(0,0,-1): newobj.ViewObject.RotationAxis = "Z" newobj.ViewObject.Rotation = -angle elif utils.get_type(obj) == "Point": v = App.Vector(obj.X,obj.Y,obj.Z) rv = v.sub(real_center) rv = DraftVecUtils.rotate(rv, math.radians(angle), real_axis) v = real_center.add(rv) newobj.X = v.x newobj.Y = v.y newobj.Z = v.z elif obj.isDerivedFrom("App::DocumentObjectGroup"): pass elif hasattr(obj,"Placement"): #FreeCAD.Console.PrintMessage("placement rotation\n") shape = Part.Shape() shape.Placement = obj.Placement shape.rotate(DraftVecUtils.tup(real_center), DraftVecUtils.tup(real_axis), angle) newobj.Placement = shape.Placement elif hasattr(obj,'Shape') and (utils.get_type(obj) not in ["WorkingPlaneProxy","BuildingPart"]): #think it make more sense to try first to rotate placement and later to try with shape. no? shape = obj.Shape.copy() shape.rotate(DraftVecUtils.tup(real_center), DraftVecUtils.tup(real_axis), angle) newobj.Shape = shape if copy: gui_utils.formatObject(newobj,obj) if newobj is not None: newobjlist.append(newobj) if copy: for p in obj.InList: if p.isDerivedFrom("App::DocumentObjectGroup") and (p in objectslist): g = newgroups.setdefault(p.Name, App.ActiveDocument.addObject(p.TypeId, p.Name)) g.addObject(newobj) break gui_utils.select(newobjlist) if len(newobjlist) == 1: return newobjlist[0] return newobjlist
def offset(obj, delta, copy=False, bind=False, sym=False, occ=False): """offset(object,delta,[copymode],[bind]) Offset the given wire by applying the given delta Vector to its first vertex. Parameters ---------- obj : delta : Base.Vector or list of Base.Vector If offsetting a BSpline, the delta must not be a Vector but a list of Vectors, one for each node of the spline. copy : bool If copymode is True, another object is created, otherwise the same object gets offsetted. copy : bool If bind is True, and provided the wire is open, the original and the offset wires will be bound by their endpoints, forming a face. sym : bool if sym is True, bind must be true too, and the offset is made on both sides, the total width being the given delta length. """ import Part import DraftGeomUtils newwire = None delete = None if utils.get_type(obj).startswith("Part::") or utils.get_type( obj).startswith("Sketcher::"): copy = True print( "the offset tool is currently unable to offset a non-Draft object directly - Creating a copy" ) def getRect(p, obj): """returns length,height,placement""" pl = obj.Placement.copy() pl.Base = p[0] diag = p[2].sub(p[0]) bb = p[1].sub(p[0]) bh = p[3].sub(p[0]) nb = DraftVecUtils.project(diag, bb) nh = DraftVecUtils.project(diag, bh) if obj.Length.Value < 0: l = -nb.Length else: l = nb.Length if obj.Height.Value < 0: h = -nh.Length else: h = nh.Length return l, h, pl def getRadius(obj, delta): """returns a new radius for a regular polygon""" an = math.pi / obj.FacesNumber nr = DraftVecUtils.rotate(delta, -an) nr.multiply(1 / math.cos(an)) nr = obj.Shape.Vertexes[0].Point.add(nr) nr = nr.sub(obj.Placement.Base) nr = nr.Length if obj.DrawMode == "inscribed": return nr else: return nr * math.cos(math.pi / obj.FacesNumber) newwire = None if utils.get_type(obj) == "Circle": pass elif utils.get_type(obj) == "BSpline": pass else: if sym: d1 = App.Vector(delta).multiply(0.5) d2 = d1.negative() n1 = DraftGeomUtils.offsetWire(obj.Shape, d1) n2 = DraftGeomUtils.offsetWire(obj.Shape, d2) else: if isinstance(delta, float) and (len(obj.Shape.Edges) == 1): # circle c = obj.Shape.Edges[0].Curve nc = Part.Circle(c.Center, c.Axis, delta) if len(obj.Shape.Vertexes) > 1: nc = Part.ArcOfCircle(nc, obj.Shape.Edges[0].FirstParameter, obj.Shape.Edges[0].LastParameter) newwire = Part.Wire(nc.toShape()) p = [] else: newwire = DraftGeomUtils.offsetWire(obj.Shape, delta) if DraftGeomUtils.hasCurves(newwire) and copy: p = [] else: p = DraftGeomUtils.getVerts(newwire) if occ: newobj = App.ActiveDocument.addObject("Part::Feature", "Offset") newobj.Shape = DraftGeomUtils.offsetWire(obj.Shape, delta, occ=True) gui_utils.formatObject(newobj, obj) if not copy: delete = obj.Name elif bind: if not DraftGeomUtils.isReallyClosed(obj.Shape): if sym: s1 = n1 s2 = n2 else: s1 = obj.Shape s2 = newwire if s1 and s2: w1 = s1.Edges w2 = s2.Edges w3 = Part.LineSegment(s1.Vertexes[0].Point, s2.Vertexes[0].Point).toShape() w4 = Part.LineSegment(s1.Vertexes[-1].Point, s2.Vertexes[-1].Point).toShape() newobj = App.ActiveDocument.addObject("Part::Feature", "Offset") newobj.Shape = Part.Face(Part.Wire(w1 + [w3] + w2 + [w4])) else: print("Draft.offset: Unable to bind wires") else: newobj = App.ActiveDocument.addObject("Part::Feature", "Offset") newobj.Shape = Part.Face(obj.Shape.Wires[0]) if not copy: delete = obj.Name elif copy: newobj = None if sym: return None if utils.get_type(obj) == "Wire": if p: newobj = make_wire(p) newobj.Closed = obj.Closed elif newwire: newobj = App.ActiveDocument.addObject("Part::Feature", "Offset") newobj.Shape = newwire else: print("Draft.offset: Unable to duplicate this object") elif utils.get_type(obj) == "Rectangle": if p: length, height, plac = getRect(p, obj) newobj = make_rectangle(length, height, plac) elif newwire: newobj = App.ActiveDocument.addObject("Part::Feature", "Offset") newobj.Shape = newwire else: print("Draft.offset: Unable to duplicate this object") elif utils.get_type(obj) == "Circle": pl = obj.Placement newobj = make_circle(delta) newobj.FirstAngle = obj.FirstAngle newobj.LastAngle = obj.LastAngle newobj.Placement = pl elif utils.get_type(obj) == "Polygon": pl = obj.Placement newobj = make_polygon(obj.FacesNumber) newobj.Radius = getRadius(obj, delta) newobj.DrawMode = obj.DrawMode newobj.Placement = pl elif utils.get_type(obj) == "BSpline": newobj = make_bspline(delta) newobj.Closed = obj.Closed else: # try to offset anyway try: if p: newobj = make_wire(p) newobj.Closed = obj.Shape.isClosed() except Part.OCCError: pass if (not newobj) and newwire: newobj = App.ActiveDocument.addObject("Part::Feature", "Offset") newobj.Shape = newwire if not newobj: print("Draft.offset: Unable to create an offset") if newobj: gui_utils.formatObject(newobj, obj) else: newobj = None if sym: return None if utils.get_type(obj) == "Wire": if obj.Base or obj.Tool: App.Console.PrintWarning("Warning: object history removed\n") obj.Base = None obj.Tool = None obj.Placement = App.Placement( ) # p points are in the global coordinate system obj.Points = p elif utils.get_type(obj) == "BSpline": #print(delta) obj.Points = delta #print("done") elif utils.get_type(obj) == "Rectangle": length, height, plac = getRect(p, obj) obj.Placement = plac obj.Length = length obj.Height = height elif utils.get_type(obj) == "Circle": obj.Radius = delta elif utils.get_type(obj) == "Polygon": obj.Radius = getRadius(obj, delta) elif utils.get_type(obj) == 'Part': print("unsupported object") # TODO newobj = obj if copy and utils.get_param("selectBaseObjects", False): gui_utils.select(newobj) else: gui_utils.select(obj) if delete: App.ActiveDocument.removeObject(delete) return newobj
def make_path_array(base_object, path_object, count=4, extra=App.Vector(0, 0, 0), subelements=None, align=False, align_mode="Original", tan_vector=App.Vector(1, 0, 0), force_vertical=False, vertical_vector=App.Vector(0, 0, 1), use_link=True): """Make a Draft PathArray object. Distribute copies of a `base_object` along `path_object` or `subelements` from `path_object`. Parameters ---------- base_object: Part::Feature or str Any of object that has a `Part::TopoShape` that can be duplicated. This means most 2D and 3D objects produced with any workbench. 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. path_object: Part::Feature or str Path object like a polyline, B-Spline, or bezier curve that should contain edges. Just like `base_object` it can also be `Label`. count: int, float, optional It defaults to 4. Number of copies to create along the `path_object`. It must be at least 2. If a `float` is provided, it will be truncated by `int(count)`. extra: Base.Vector3, optional It defaults to `App.Vector(0, 0, 0)`. It translates each copy by the value of `extra`. This is useful to adjust for the difference between shape centre and shape reference point. subelements: list or tuple of str, optional It defaults to `None`. It should be a list of names of edges that must exist in `path_object`. Then the path array will be created along these edges only, and not the entire `path_object`. :: subelements = ['Edge1', 'Edge2'] The edges must be contiguous, meaning that it is not allowed to input `'Edge1'` and `'Edge3'` if they do not touch each other. A single string value is also allowed. :: subelements = 'Edge1' align: bool, optional It defaults to `False`. If it is `True` it will align `base_object` to tangent, normal, or binormal to the `path_object`, depending on the value of `tan_vector`. align_mode: str, optional It defaults to `'Original'` which is the traditional alignment. It can also be `'Frenet'` or `'Tangent'`. - Original. It does not calculate curve normal. `X` is curve tangent, `Y` is normal parameter, Z is the cross product `X` x `Y`. - Frenet. It defines a local coordinate system along the path. `X` is tangent to curve, `Y` is curve normal, `Z` is curve binormal. If normal cannot be computed, for example, in a straight path, a default is used. - Tangent. It is similar to `'Original'` but includes a pre-rotation to align the base object's `X` to the value of `tan_vector`, then `X` follows curve tangent. tan_vector: Base::Vector3, optional It defaults to `App.Vector(1, 0, 0)` or the +X axis. It aligns the tangent of the path to this local unit vector of the object. force_vertical: Base::Vector3, optional It defaults to `False`. If it is `True`, the value of `vertical_vector` will be used when `align_mode` is `'Original'` or `'Tangent'`. vertical_vector: Base::Vector3, optional It defaults to `App.Vector(0, 0, 1)` or the +Z axis. It will force this vector to be the vertical direction when `force_vertical` is `True`. use_link: bool, optional It defaults to `True`, in which case the copies are `App::Link` elements. Otherwise, the copies are shape copies which makes the resulting array heavier. Returns ------- Part::FeaturePython The scripted object of type `'PathArray'`. Its `Shape` is a compound of the copies of the original object. None If there is a problem it will return `None`. """ _name = "make_path_array" utils.print_header(_name, "Path array") found, doc = utils.find_doc(App.activeDocument()) if not found: _err(_tr("No active document. Aborting.")) return None if isinstance(base_object, str): base_object_str = base_object found, base_object = utils.find_object(base_object, doc) if not found: _msg("base_object: {}".format(base_object_str)) _err(_tr("Wrong input: object not in document.")) return None _msg("base_object: {}".format(base_object.Label)) if isinstance(path_object, str): path_object_str = path_object found, path_object = utils.find_object(path_object, doc) if not found: _msg("path_object: {}".format(path_object_str)) _err(_tr("Wrong input: object not in document.")) return None _msg("path_object: {}".format(path_object.Label)) _msg("count: {}".format(count)) try: utils.type_check([(count, (int, float))], name=_name) except TypeError: _err(_tr("Wrong input: must be a number.")) return None count = int(count) _msg("extra: {}".format(extra)) try: utils.type_check([(extra, App.Vector)], name=_name) except TypeError: _err(_tr("Wrong input: must be a vector.")) return None _msg("subelements: {}".format(subelements)) if subelements: try: # Make a list if isinstance(subelements, str): subelements = [subelements] utils.type_check([(subelements, (list, tuple, str))], name=_name) except TypeError: _err( _tr("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 LinkSubList, which includes the path_object. # Old style: [(path_object, "Edge1"), (path_object, "Edge2")] # New style: [(path_object, ("Edge1", "Edge2"))] # # If a simple list is given ["a", "b"], this will create an old-style # SubList. # If a nested list is given [["a", "b"]], this will create a new-style # SubList. # In any case, the property of the object accepts both styles. # # If the old style is deprecated then this code should be updated # to create new style lists exclusively. sub_list = list() for sub in subelements: sub_list.append((path_object, sub)) else: sub_list = None align = bool(align) _msg("align: {}".format(align)) _msg("align_mode: {}".format(align_mode)) try: utils.type_check([(align_mode, str)], name=_name) if align_mode not in ("Original", "Frenet", "Tangent"): raise TypeError except TypeError: _err(_tr("Wrong input: must be " "'Original', 'Frenet', or 'Tangent'.")) return None _msg("tan_vector: {}".format(tan_vector)) try: utils.type_check([(tan_vector, App.Vector)], name=_name) except TypeError: _err(_tr("Wrong input: must be a vector.")) return None force_vertical = bool(force_vertical) _msg("force_vertical: {}".format(force_vertical)) _msg("vertical_vector: {}".format(vertical_vector)) try: utils.type_check([(vertical_vector, App.Vector)], name=_name) except TypeError: _err(_tr("Wrong input: must be a vector.")) return None use_link = bool(use_link) _msg("use_link: {}".format(use_link)) if use_link: # The PathArray class must be called in this special way # to make it a PathLinkArray new_obj = doc.addObject("Part::FeaturePython", "PathArray", PathArray(None), None, True) else: new_obj = doc.addObject("Part::FeaturePython", "PathArray") PathArray(new_obj) new_obj.Base = base_object new_obj.PathObject = path_object new_obj.Count = count new_obj.ExtraTranslation = extra new_obj.PathSubelements = sub_list new_obj.Align = align new_obj.AlignMode = align_mode new_obj.TangentVector = tan_vector new_obj.ForceVertical = force_vertical new_obj.VerticalVector = vertical_vector if App.GuiUp: if use_link: ViewProviderDraftLink(new_obj.ViewObject) else: ViewProviderDraftArray(new_obj.ViewObject) gui_utils.formatObject(new_obj, new_obj.Base) if hasattr(new_obj.Base.ViewObject, "DiffuseColor"): if len(new_obj.Base.ViewObject.DiffuseColor) > 1: new_obj.ViewObject.Proxy.resetColors(new_obj.ViewObject) new_obj.Base.ViewObject.hide() gui_utils.select(new_obj) return new_obj
def make_path_twisted_array(base_object, path_object, count=15, rot_factor=0.25, use_link=True): """Create a Path twisted array.""" _name = "make_path_twisted_array" utils.print_header(_name, "Path twisted array") found, doc = utils.find_doc(App.activeDocument()) if not found: _err(_tr("No active document. Aborting.")) return None if isinstance(base_object, str): base_object_str = base_object found, base_object = utils.find_object(base_object, doc) if not found: _msg("base_object: {}".format(base_object_str)) _err(_tr("Wrong input: object not in document.")) return None _msg("base_object: {}".format(base_object.Label)) if isinstance(path_object, str): path_object_str = path_object found, path_object = utils.find_object(path_object, doc) if not found: _msg("path_object: {}".format(path_object_str)) _err(_tr("Wrong input: object not in document.")) return None _msg("path_object: {}".format(path_object.Label)) _msg("count: {}".format(count)) try: utils.type_check([(count, (int, float))], name=_name) except TypeError: _err(_tr("Wrong input: must be a number.")) return None count = int(count) use_link = bool(use_link) _msg("use_link: {}".format(use_link)) if use_link: # The PathTwistedArray class must be called in this special way # to make it a PathTwistLinkArray new_obj = doc.addObject("Part::FeaturePython", "PathTwistedArray", PathTwistedArray(None), None, True) else: new_obj = doc.addObject("Part::FeaturePython", "PathTwistedArray") PathTwistedArray(new_obj) new_obj.Base = base_object new_obj.PathObject = path_object new_obj.Count = count new_obj.RotationFactor = rot_factor if App.GuiUp: if use_link: ViewProviderDraftLink(new_obj.ViewObject) else: ViewProviderDraftArray(new_obj.ViewObject) gui_utils.formatObject(new_obj, new_obj.Base) if hasattr(new_obj.Base.ViewObject, "DiffuseColor"): if len(new_obj.Base.ViewObject.DiffuseColor) > 1: new_obj.ViewObject.Proxy.resetColors(new_obj.ViewObject) new_obj.Base.ViewObject.hide() gui_utils.select(new_obj) return new_obj
def make_point_array(base_object, point_object, extra=None): """Make a Draft PointArray object. Distribute copies of a `base_object` in the points defined by `point_object`. Parameters ---------- base_object: Part::Feature or str Any of object that has a `Part::TopoShape` that can be duplicated. This means most 2D and 3D objects produced with any workbench. 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. point_object: Part::Feature or str An object that is a type of container for holding points. This object must have one of the following properties `Geometry`, `Links`, or `Components`, which themselves must contain objects with `X`, `Y`, and `Z` properties. This object could be: - A `Sketcher::SketchObject`, as it has a `Geometry` property. The sketch can contain different elements but it must contain at least one `Part::GeomPoint`. - A `Part::Compound`, as it has a `Links` property. The compound can contain different elements but it must contain at least one object that has `X`, `Y`, and `Z` properties, like a `Draft Point` or a `Part::Vertex`. - A `Draft Block`, as it has a `Components` property. This `Block` behaves essentially the same as a `Part::Compound`. It must contain at least a point or vertex object. extra: Base::Placement, Base::Vector3, or Base::Rotation, optional It defaults to `None`. If it is provided, it is an additional placement that is applied to each copy of the array. The input could be a full placement, just a vector indicating the additional translation, or just a rotation. Returns ------- Part::FeaturePython A scripted object of type `'PointArray'`. Its `Shape` is a compound of the copies of the original object. None If there is a problem it will return `None`. """ _name = "make_point_array" utils.print_header(_name, "Point array") found, doc = utils.find_doc(App.activeDocument()) if not found: _err(_tr("No active document. Aborting.")) return None if isinstance(base_object, str): base_object_str = base_object found, base_object = utils.find_object(base_object, doc) if not found: _msg("base_object: {}".format(base_object_str)) _err(_tr("Wrong input: object not in document.")) return None _msg("base_object: {}".format(base_object.Label)) if isinstance(point_object, str): point_object_str = point_object found, point_object = utils.find_object(point_object, doc) if not found: _msg("point_object: {}".format(point_object_str)) _err(_tr("Wrong input: object not in document.")) return None _msg("point_object: {}".format(point_object.Label)) if (not hasattr(point_object, "Geometry") and not hasattr(point_object, "Links") and not hasattr(point_object, "Components")): _err( _tr("Wrong input: point object doesn't have " "'Geometry', 'Links', or 'Components'.")) return None _msg("extra: {}".format(extra)) if not extra: extra = App.Placement() try: utils.type_check([(extra, (App.Placement, App.Vector, App.Rotation))], name=_name) except TypeError: _err( _tr("Wrong input: must be a placement, a vector, " "or a rotation.")) return None # Convert the vector or rotation to a full placement if isinstance(extra, App.Vector): extra = App.Placement(extra, App.Rotation()) elif isinstance(extra, App.Rotation): extra = App.Placement(App.Vector(), extra) new_obj = doc.addObject("Part::FeaturePython", "PointArray") PointArray(new_obj) new_obj.Base = base_object new_obj.PointObject = point_object new_obj.ExtraPlacement = extra if App.GuiUp: ViewProviderDraftArray(new_obj.ViewObject) gui_utils.formatObject(new_obj, new_obj.Base) if hasattr(new_obj.Base.ViewObject, "DiffuseColor"): if len(new_obj.Base.ViewObject.DiffuseColor) > 1: new_obj.ViewObject.Proxy.resetColors(new_obj.ViewObject) new_obj.Base.ViewObject.hide() gui_utils.select(new_obj) return new_obj
def move(objectslist, vector, copy=False): """move(objects,vector,[copy]) Move the objects contained in objects (that can be an object or a list of objects) in the direction and distance indicated by the given vector. Parameters ---------- objectslist : list vector : Base.Vector Delta Vector to move the clone from the original position. copy : bool If copy is True, the actual objects are not moved, but copies are created instead. Return ---------- The objects (or their copies) are returned. """ utils.type_check([(vector, App.Vector), (copy,bool)], "move") if not isinstance(objectslist, list): objectslist = [objectslist] objectslist.extend(utils.get_movable_children(objectslist)) newobjlist = [] newgroups = {} objectslist = utils.filter_objects_for_modifiers(objectslist, copy) for obj in objectslist: newobj = None # real_vector have been introduced to take into account # the possibility that object is inside an App::Part if hasattr(obj, "getGlobalPlacement"): v_minus_global = obj.getGlobalPlacement().inverse().Rotation.multVec(vector) real_vector = obj.Placement.Rotation.multVec(v_minus_global) else: real_vector = vector if utils.get_type(obj) == "Point": v = App.Vector(obj.X,obj.Y,obj.Z) v = v.add(real_vector) if copy: newobj = make_copy(obj) else: newobj = obj newobj.X = v.x newobj.Y = v.y newobj.Z = v.z elif obj.isDerivedFrom("App::DocumentObjectGroup"): pass elif hasattr(obj,'Shape'): if copy: newobj = make_copy(obj) else: newobj = obj pla = newobj.Placement pla.move(real_vector) elif utils.get_type(obj) == "Annotation": if copy: newobj = App.ActiveDocument.addObject("App::Annotation", utils.getRealName(obj.Name)) newobj.LabelText = obj.LabelText if App.GuiUp: gui_utils.formatObject(newobj,obj) else: newobj = obj newobj.Position = obj.Position.add(real_vector) elif utils.get_type(obj) == "Text": if copy: # TODO: Why make_copy do not handle Text object?? newobj = App.ActiveDocument.addObject("App::FeaturePython", utils.getRealName(obj.Name)) Text(newobj) if App.GuiUp: ViewProviderText(newobj.ViewObject) gui_utils.formatObject(newobj,obj) newobj.Text = obj.Text newobj.Placement = obj.Placement if App.GuiUp: gui_utils.formatObject(newobj,obj) else: newobj = obj newobj.Placement.Base = obj.Placement.Base.add(real_vector) elif utils.get_type(obj) in ["Dimension","LinearDimension"]: if copy: # TODO: Why make_copy do not handle Dimension object?? # TODO: Support also Label and Angular dimension newobj = App.ActiveDocument.addObject("App::FeaturePython", utils.getRealName(obj.Name)) LinearDimension(newobj) if App.GuiUp: ViewProviderLinearDimension(newobj.ViewObject) gui_utils.formatObject(newobj,obj) else: newobj = obj newobj.Start = obj.Start.add(real_vector) newobj.End = obj.End.add(real_vector) newobj.Dimline = obj.Dimline.add(real_vector) else: if copy and obj.isDerivedFrom("Mesh::Feature"): print("Mesh copy not supported at the moment") # TODO newobj = obj if "Placement" in obj.PropertiesList: pla = obj.Placement pla.move(real_vector) if newobj is not None: newobjlist.append(newobj) if copy: for p in obj.InList: if p.isDerivedFrom("App::DocumentObjectGroup") and (p in objectslist): g = newgroups.setdefault(p.Name,App.ActiveDocument.addObject(p.TypeId,p.Name)) g.addObject(newobj) break if utils.get_type(p) == "Layer": p.Proxy.addObject(p,newobj) if copy and utils.get_param("selectBaseObjects",False): gui_utils.select(objectslist) else: gui_utils.select(newobjlist) if len(newobjlist) == 1: return newobjlist[0] return newobjlist
def make_path_array(baseobject,pathobject,count,xlate=None,align=False,pathobjsubs=[],use_link=False): """make_path_array(docobj, path, count, xlate, align, pathobjsubs, use_link) Make a Draft PathArray object. Distribute count copies of a document baseobject along a pathobject or subobjects of a pathobject. Parameters ---------- docobj : Object to array path : Path object pathobjsubs : TODO: Complete documentation align : Optionally aligns baseobject to tangent/normal/binormal of path. TODO: verify count : TODO: Complete documentation xlate : Base.Vector Optionally translates each copy by FreeCAD.Vector xlate direction and distance to adjust for difference in shape centre vs shape reference point. use_link : TODO: Complete documentation """ if not App.ActiveDocument: App.Console.PrintError("No active document. Aborting\n") return if use_link: obj = App.ActiveDocument.addObject("Part::FeaturePython","PathArray", PathArray(None), None, True) else: obj = App.ActiveDocument.addObject("Part::FeaturePython","PathArray") PathArray(obj) obj.Base = baseobject obj.PathObj = pathobject if pathobjsubs: sl = [] for sub in pathobjsubs: sl.append((obj.PathObj,sub)) obj.PathSubs = list(sl) if count > 1: obj.Count = count if xlate: obj.Xlate = xlate obj.Align = align if App.GuiUp: if use_link: ViewProviderDraftLink(obj.ViewObject) else: ViewProviderDraftArray(obj.ViewObject) gui_utils.formatObject(obj,obj.Base) if hasattr(obj.Base.ViewObject, "DiffuseColor"): if len(obj.Base.ViewObject.DiffuseColor) > 1: obj.ViewObject.Proxy.resetColors(obj.ViewObject) baseobject.ViewObject.hide() gui_utils.select(obj) return obj