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 scale(objectslist, scale=App.Vector(1, 1, 1), center=App.Vector(0, 0, 0), copy=False): """scale(objects, scale, [center], copy) Scales the objects contained in objects (that can be a list of objects or an object) of the given around given center. Parameters ---------- objectlist : list scale : Base.Vector Scale factors defined by a given vector (in X, Y, Z directions). objectlist : Base.Vector Center of the scale operation. copy : bool If copy is True, the actual objects are not scaled, but copies are created instead. Return ---------- The objects (or their copies) are returned. """ if not isinstance(objectslist, list): objectslist = [objectslist] newobjlist = [] for obj in objectslist: if copy: newobj = make_copy.make_copy(obj) else: newobj = obj if hasattr(obj, 'Shape'): scaled_shape = obj.Shape.copy() m = App.Matrix() m.move(center.negative()) m.scale(scale.x, scale.y, scale.z) m.move(center) scaled_shape = scaled_shape.transformGeometry(m) if utils.get_type(obj) == "Rectangle": p = [] for v in scaled_shape.Vertexes: p.append(v.Point) 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 < 0: l = -nb.Length else: l = nb.Length if obj.Height < 0: h = -nh.Length else: h = nh.Length newobj.Length = l newobj.Height = h tr = p[0].sub(obj.Shape.Vertexes[0].Point) # unused? newobj.Placement = pl elif utils.get_type(obj) == "Wire" or utils.get_type(obj) == "BSpline": for index, point in enumerate(newobj.Points): scale_vertex(newobj, index, scale, center) elif hasattr(obj, 'Shape'): newobj.Shape = scaled_shape elif (obj.TypeId == "App::Annotation"): factor = scale.y * obj.ViewObject.FontSize newobj.ViewObject.FontSize = factor d = obj.Position.sub(center) newobj.Position = center.add( App.Vector(d.x * scale.x, d.y * scale.y, d.z * scale.z)) if copy: gui_utils.format_object(newobj, obj) newobjlist.append(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 heal(objlist=None, delete=True, reparent=True): """heal([objlist],[delete],[reparent]) Recreate Draft objects that are damaged, for example if created from an earlier version. If ran without arguments, all the objects in the document will be healed if they are damaged. Parameters ---------- objlist : list delete : Base.Vector or list of Base.Vector If delete is True, the damaged objects are deleted (default). reparent : bool If reparent is True (default), new objects go at the very same place in the tree than their original. """ auto = False if not objlist: objlist = App.ActiveDocument.Objects print("Automatic mode: Healing whole document...") auto = True else: print("Manual mode: Force-healing selected objects...") if not isinstance(objlist, list): objlist = [objlist] dellist = [] got = False for obj in objlist: dtype = utils.get_type(obj) ftype = obj.TypeId if ftype in [ "Part::FeaturePython", "App::FeaturePython", "Part::Part2DObjectPython", "Drawing::FeatureViewPython" ]: proxy = obj.Proxy if hasattr(obj, "ViewObject"): if hasattr(obj.ViewObject, "Proxy"): proxy = obj.ViewObject.Proxy if (proxy == 1) or (dtype in ["Unknown", "Part"]) or (not auto): got = True dellist.append(obj.Name) props = obj.PropertiesList if ("Dimline" in props) and ("Start" in props): print("Healing " + obj.Name + " of type Dimension") nobj = make_copy(obj, force="Dimension", reparent=reparent) elif ("Height" in props) and ("Length" in props): print("Healing " + obj.Name + " of type Rectangle") nobj = make_copy(obj, force="Rectangle", reparent=reparent) elif ("Points" in props) and ("Closed" in props): if "BSpline" in obj.Name: print("Healing " + obj.Name + " of type BSpline") nobj = make_copy(obj, force="BSpline", reparent=reparent) else: print("Healing " + obj.Name + " of type Wire") nobj = make_copy(obj, force="Wire", reparent=reparent) elif ("Radius" in props) and ("FirstAngle" in props): print("Healing " + obj.Name + " of type Circle") nobj = make_copy(obj, force="Circle", reparent=reparent) elif ("DrawMode" in props) and ("FacesNumber" in props): print("Healing " + obj.Name + " of type Polygon") nobj = make_copy(obj, force="Polygon", reparent=reparent) elif ("FillStyle" in props) and ("FontSize" in props): nobj = make_copy(obj, force="DrawingView", reparent=reparent) else: dellist.pop() print("Object " + obj.Name + " is not healable") if not got: print("No object seems to need healing") else: print("Healed ", len(dellist), " objects") if dellist and delete: for n in dellist: App.ActiveDocument.removeObject(n)
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 # TODO: Make Move work also with App::Link 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": if copy: newobj = make_copy(obj) else: newobj = obj newobj.X = obj.X + real_vector.x newobj.Y = obj.Y + real_vector.y newobj.Z = obj.Z + real_vector.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 = make_copy(obj) else: newobj = obj newobj.Position = obj.Position.add(real_vector) elif utils.get_type(obj) == "Text": if copy: newobj = make_copy(obj) else: newobj = obj newobj.Placement.Base = obj.Placement.Base.add(real_vector) elif utils.get_type(obj) in ["Dimension", "LinearDimension"]: if copy: newobj = make_copy(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) elif utils.get_type(obj) in ["AngularDimension"]: if copy: newobj = make_copy(obj) else: newobj = obj newobj.Center = obj.Start.add(real_vector) elif "Placement" in obj.PropertiesList: if copy: newobj = make_copy(obj) else: newobj = obj 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 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 ---------- objectslist : list angle : rotation angle (in degrees) 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) if copy: doc = App.ActiveDocument for obj in objectslist: if obj.isDerivedFrom("App::DocumentObjectGroup") \ and obj.Name not in newgroups.keys(): newgroups[obj.Name] = doc.addObject( obj.TypeId, utils.get_real_name(obj.Name)) 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 obj.isDerivedFrom("App::Annotation"): # TODO: this is very different from how move handle annotations # maybe we can uniform the two methods if copy: newobj = make_copy.make_copy(obj) else: newobj = obj 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": if copy: newobj = make_copy.make_copy(obj) else: newobj = obj v = App.Vector(newobj.X, newobj.Y, newobj.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"): if copy: newobj = newgroups[obj.Name] else: newobj = obj elif hasattr(obj, "Placement"): # App.Console.PrintMessage("placement rotation\n") if copy: newobj = make_copy.make_copy(obj) else: newobj = obj # Workaround for `faulty` implementation of Base.Placement.rotate(center, axis, angle). # See: https://forum.freecadweb.org/viewtopic.php?p=613196#p613196 offset_rotation = App.Placement(App.Vector(0, 0, 0), App.Rotation(real_axis, angle), real_center) newobj.Placement = offset_rotation * newobj.Placement elif hasattr(obj, "Shape"): if copy: newobj = make_copy.make_copy(obj) else: newobj = obj shape = newobj.Shape.copy() shape.rotate(real_center, real_axis, angle) newobj.Shape = shape if newobj is not None: newobjlist.append(newobj) if copy: for parent in obj.InList: if parent.isDerivedFrom("App::DocumentObjectGroup") \ and (parent in objectslist): newgroups[parent.Name].addObject(newobj) if utils.get_type(parent) == "Layer": parent.Proxy.addObject(parent, 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