def ungroup(obj): """Remove the object from any group to which it belongs. A "group" is any object returned by `get_group_names`. Parameters ---------- obj: App::DocumentObject or str Any type of object. 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. """ if isinstance(obj, str): obj_str = obj found, obj = utils.find_object(obj, doc=App.activeDocument()) if not found: _msg("obj: {}".format(obj_str)) _err(_tr("Wrong input: object not in document.")) return None doc = obj.Document for name in get_group_names(): group = doc.getObject(name) if obj in group.Group: # The list of objects cannot be modified directly, # so a new list is created, this new list is modified, # and then it is assigned over the older list. objects = group.Group objects.remove(obj) group.Group = objects
def get_group_names(doc=None): """Return a list of names of existing groups in the document. Parameters ---------- doc: App::Document, optional It defaults to `None`. A document on which to search group names. It if is `None` it will search the current document. Returns ------- list of str A list of names of objects that are "groups". These are objects derived from `App::DocumentObjectGroup` or which are of types `'Floor'`, `'Building'`, or `'Site'` from the Arch Workbench. Otherwise, return an empty list. """ if not doc: found, doc = utils.find_doc(App.activeDocument()) if not found: _err(_tr("No active document. Aborting.")) return [] glist = [] for obj in doc.Objects: if (obj.isDerivedFrom("App::DocumentObjectGroup") or utils.get_type(obj) in ("Floor", "Building", "Site")): glist.append(obj.Name) return glist
def _are_integers(n_x, n_y, n_z=None, name="Unknown"): """Check that the numbers are integers, with minimum value of 1.""" _msg("n_x: {}".format(n_x)) _msg("n_y: {}".format(n_y)) if n_z: _msg("n_z: {}".format(n_z)) try: if n_z: utils.type_check([(n_x, int), (n_y, int), (n_z, int)], name=name) else: utils.type_check([(n_x, int), (n_y, int)], name=name) except TypeError: _err(_tr("Wrong input: must be an integer number.")) return False, n_x, n_y, n_z _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 and n_z < 1: _wrn(_tr(_text)) n_z = 1 return True, n_x, n_y, n_z
def _are_vectors(v_x, v_y, v_z=None, name="Unknown"): """Check that the vectors are numbers.""" _msg("v_x: {}".format(v_x)) _msg("v_y: {}".format(v_y)) if v_z: _msg("v_z: {}".format(v_z)) try: if v_z: utils.type_check([(v_x, (int, float, App.Vector)), (v_y, (int, float, App.Vector)), (v_z, (int, float, App.Vector))], name=name) else: 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 False, v_x, v_y, v_z _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 v_z and not isinstance(v_z, App.Vector): v_z = App.Vector(0, 0, v_z) _wrn(_tr(_text)) return True, v_x, v_y, v_z
def cut(object1, object2): """Return a cut object made from the difference of the 2 given objects. Parameters ---------- object1: Part::Feature Any object with a `Part::TopoShape`. object2: Part::Feature Any object with a `Part::TopoShape`. Returns ------- Part::Cut The resulting cut object. None If there is a problem and the new object can't be created. """ if not App.activeDocument(): _err(_tr("No active document. Aborting.")) return obj = App.activeDocument().addObject("Part::Cut", "Cut") obj.Base = object1 obj.Tool = object2 if App.GuiUp: gui_utils.format_object(obj, object1) gui_utils.select(obj) object1.ViewObject.Visibility = False object2.ViewObject.Visibility = False return obj
def get_group_names(doc=None): """Return a list of names of existing groups in the document. Parameters ---------- doc: App::Document, optional It defaults to `None`. A document on which to search group names. It if is `None` it will search the current document. Returns ------- list of str A list of names of objects that are considered groups. See the is_group function. Otherwise returns an empty list. """ if not doc: found, doc = utils.find_doc(App.activeDocument()) if not found: _err(_tr("No active document. Aborting.")) return [] glist = [] for obj in doc.Objects: if is_group(obj): glist.append(obj.Name) return glist
def proceed(self): """Proceed with the command if one object was selected.""" sel = Gui.Selection.getSelectionEx() if len(sel) != 2: _err( _tr("Please select exactly two objects, " "the base object and the path object, " "before calling this command.")) else: base_object = sel[0].Object path_object = sel[1].Object count = 15 rot_factor = 0.25 use_link = self.use_link Gui.addModule("Draft") _cmd = "Draft.make_path_twisted_array" _cmd += "(" _cmd += "App.ActiveDocument." + base_object.Name + ", " _cmd += "App.ActiveDocument." + path_object.Name + ", " _cmd += "count=" + str(count) + ", " _cmd += "rot_factor=" + str(rot_factor) + ", " _cmd += "use_link=" + str(use_link) _cmd += ")" _cmd_list = [ "_obj_ = " + _cmd, "Draft.autogroup(_obj_)", "App.ActiveDocument.recompute()" ] self.commit(_tr(self.name), _cmd_list) # Commit the transaction and execute the commands # through the parent class self.finish()
def createObject(self): """Create the actual object in the current document.""" # print("debug: D_T ShapeString.createObject type(self.SString):" # + str(type(self.SString))) dquote = '"' String = dquote + self.SString + dquote # Size and tracking are numbers; # they are ASCII so this conversion should always work Size = str(self.SSSize) Tracking = str(self.SSTrack) FFile = dquote + self.FFile + dquote try: qr, sup, points, fil = self.getStrings() Gui.addModule("Draft") _cmd = 'Draft.make_shapestring' _cmd += '(' _cmd += 'String=' + String + ', ' _cmd += 'FontFile=' + FFile + ', ' _cmd += 'Size=' + Size + ', ' _cmd += 'Tracking=' + Tracking _cmd += ')' _cmd_list = [ 'ss = ' + _cmd, 'plm = FreeCAD.Placement()', 'plm.Base = ' + DraftVecUtils.toString(self.ssBase), 'plm.Rotation.Q = ' + qr, 'ss.Placement = plm', 'ss.Support = ' + sup, 'Draft.autogroup(ss)', 'FreeCAD.ActiveDocument.recompute()' ] self.commit(translate("draft", "Create ShapeString"), _cmd_list) except Exception: _err("Draft_ShapeString: error delaying commit") self.finish()
def get_layer_container(): """Return a group object to put layers in. Returns ------- App::DocumentObjectGroupPython The existing group object named `'LayerContainer'` of type `LayerContainer`. If it doesn't exist it will create it with this default Name. """ found, doc = utils.find_doc(App.activeDocument()) if not found: _err(translate("draft", "No active document. Aborting.")) return None for obj in doc.Objects: if obj.Name == "LayerContainer": return obj new_obj = doc.addObject("App::DocumentObjectGroupPython", "LayerContainer") new_obj.Label = translate("draft", "Layers") LayerContainer(new_obj) if App.GuiUp: ViewProviderLayerContainer(new_obj.ViewObject) return new_obj
def createObject(self): """Create object in the current document.""" dquote = '"' if sys.version_info.major < 3: # Python3: no more unicode String = 'u' + dquote + str( self.task.leString.text().encode('unicode_escape')) + dquote else: String = dquote + self.task.leString.text() + dquote FFile = dquote + str(self.fileSpec) + dquote Size = str(_Quantity(self.task.sbHeight.text()).Value) Tracking = str(0.0) x = _Quantity(self.task.sbX.text()).Value y = _Quantity(self.task.sbY.text()).Value z = _Quantity(self.task.sbZ.text()).Value ssBase = App.Vector(x, y, z) # this try block is almost identical to the one in DraftTools try: qr, sup, points, fil = self.sourceCmd.getStrings() Gui.addModule("Draft") self.sourceCmd.commit(translate("draft", "Create ShapeString"), [ 'ss=Draft.makeShapeString(String=' + String + ',FontFile=' + FFile + ',Size=' + Size + ',Tracking=' + Tracking + ')', 'plm=FreeCAD.Placement()', 'plm.Base=' + DraftVecUtils.toString(ssBase), 'plm.Rotation.Q=' + qr, 'ss.Placement=plm', 'ss.Support=' + sup, 'Draft.autogroup(ss)' ]) except Exception: _err("Draft_ShapeString: error delaying commit\n")
def build_copies(base_object, pt_list=None, placement=App.Placement()): """Build a compound of copies from the base object and list of points. Returns ------- Part::TopoShape The compound shape created by `Part.makeCompound`. """ if not pt_list: _err( translate( "Draft", "Point object doesn't have a discrete point, " "it cannot be used for an array.")) shape = base_object.Shape.copy() return shape copies = list() for pla in build_copies(base_object, pt_list, placement): new_shape = base_object.Shape.copy() new_shape.Placement = pla copies.append(new_shape) shape = Part.makeCompound(copies) return shape
def execute(self, obj): """Execute when the object is created or recomputed.""" if not obj.Base or not obj.PathObject: return # placement of entire PathArray object array_placement = obj.Placement w = self.get_wires(obj.PathObject, obj.PathSubelements) if not w: _err(obj.PathObject.Label + translate("draft", ", path object doesn't have 'Edges'.")) return base_rotation = obj.Base.Shape.Placement.Rotation final_rotation = base_rotation if (obj.Align and obj.AlignMode == "Tangent" and hasattr(obj, "TangentVector")): Xaxis = App.Vector(1.0, 0.0, 0.0) # default TangentVector if not DraftVecUtils.equals(Xaxis, obj.TangentVector): # make rotation from TangentVector to X pre_rotation = App.Rotation(obj.TangentVector, Xaxis) final_rotation = base_rotation.multiply(pre_rotation) copy_placements = placements_on_path(final_rotation, w, obj.Count, obj.ExtraTranslation, obj.Align, obj.AlignMode, obj.ForceVertical, obj.VerticalVector) return super(PathArray, self).buildShape(obj, array_placement, copy_placements)
def createObject(self): """Create object in the current document.""" dquote = '"' String = dquote + self.form.leString.text() + dquote FFile = dquote + str(self.fileSpec) + dquote Size = str(App.Units.Quantity(self.form.sbHeight.text()).Value) Tracking = str(0.0) x = App.Units.Quantity(self.form.sbX.text()).Value y = App.Units.Quantity(self.form.sbY.text()).Value z = App.Units.Quantity(self.form.sbZ.text()).Value ssBase = App.Vector(x, y, z) # this try block is almost identical to the one in DraftTools try: qr, sup, points, fil = self.sourceCmd.getStrings() Gui.addModule("Draft") self.sourceCmd.commit(translate("draft", "Create ShapeString"), [ 'ss=Draft.make_shapestring(String=' + String + ', FontFile=' + FFile + ', Size=' + Size + ', Tracking=' + Tracking + ')', 'plm=FreeCAD.Placement()', 'plm.Base=' + toString(ssBase), 'plm.Rotation.Q=' + qr, 'ss.Placement=plm', 'ss.Support=' + sup, 'Draft.autogroup(ss)' ]) except Exception: _err("Draft_ShapeString: error delaying commit\n")
def proceed(self): """Proceed with the command if one object was selected.""" if self.call: self.view.removeEventCallback("SoEvent", self.call) sel = Gui.Selection.getSelectionEx() if len(sel) != 2: _err( _tr("Please select exactly two objects, " "the base object and the point object, " "before calling this command.")) else: base_object = sel[0].Object point_object = sel[1].Object extra = None Gui.addModule('Draft') _cmd = "Draft.make_point_array" _cmd += "(" _cmd += "App.ActiveDocument." + base_object.Name + ", " _cmd += "App.ActiveDocument." + point_object.Name + ", " _cmd += "extra=" + str(extra) + ", " _cmd += 'use_link=' + str(self.use_link) _cmd += ")" _cmd_list = [ "_obj_ = " + _cmd, "Draft.autogroup(_obj_)", "App.ActiveDocument.recompute()" ] self.commit(_tr(self.name), _cmd_list) # Commit the transaction and execute the commands # through the parent class self.finish()
def build_placements(base_object, pt_list=None, placement=App.Placement()): """Build a placements from the base object and list of points. Returns ------- list(App.Placement) """ if not pt_list: _err( translate( "Draft", "Point object doesn't have a discrete point, " "it cannot be used for an array.")) return [] pls = list() for point in pt_list: new_pla = base_object.Placement.copy() original_rotation = new_pla.Rotation # Reset the position of the copy, and combine the original rotation # with the provided rotation. Two rotations (quaternions) # are combined by multiplying them. new_pla.Base = placement.Base new_pla.Rotation = original_rotation * placement.Rotation if point.TypeId == "Part::Vertex": # For this object the final position is the value of the Placement # plus the value of the X, Y, Z properties place = App.Vector(point.X, point.Y, point.Z) + point.Placement.Base elif hasattr(point, 'Placement'): # If the point object has a placement (Draft Point), use it # to displace the copy of the shape place = point.Placement.Base # The following old code doesn't make much sense because it uses # the rotation value of the auxiliary point. # Even if the point does have a rotation property as part of its # placement, rotating a point by itself is a strange workflow. # We want to use the position of the point but not its rotation, # which will probably be zero anyway. # Old code: # place = point.Placement # new_shape.rotate(place.Base, # place.Rotation.Axis, # math.degrees(place.Rotation.Angle)) else: # In other cases (Sketch with points) # translate by the X, Y, Z coordinates place = App.Vector(point.X, point.Y, point.Z) new_pla.translate(place) pls.append(new_pla) return pls
def createObject(self): """Create the actual object in the current document.""" plane = App.DraftWorkingPlane p1 = self.node[0] p3 = self.node[-1] diagonal = p3.sub(p1) halfdiag = App.Vector(diagonal).multiply(0.5) center = p1.add(halfdiag) p2 = p1.add(DraftVecUtils.project(diagonal, plane.v)) p4 = p1.add(DraftVecUtils.project(diagonal, plane.u)) r1 = (p4.sub(p1).Length) / 2 r2 = (p2.sub(p1).Length) / 2 try: # The command to run is built as a series of text strings # to be committed through the `draftutils.todo.ToDo` class. rot, sup, pts, fil = self.getStrings() if r2 > r1: r1, r2 = r2, r1 m = App.Matrix() m.rotateZ(math.pi / 2) rot1 = App.Rotation() rot1.Q = eval(rot) rot2 = App.Placement(m) rot2 = rot2.Rotation rot = str((rot1.multiply(rot2)).Q) if utils.getParam("UsePartPrimitives", False): # Insert a Part::Primitive object Gui.addModule("Part") _cmd = 'FreeCAD.ActiveDocument.' _cmd += 'addObject("Part::Ellipse", "Ellipse")' _cmd_list = [ 'ellipse = ' + _cmd, 'ellipse.MajorRadius = ' + str(r1), 'ellipse.MinorRadius = ' + str(r2), 'pl = FreeCAD.Placement()', 'pl.Rotation.Q= ' + rot, 'pl.Base = ' + DraftVecUtils.toString(center), 'ellipse.Placement = pl', 'Draft.autogroup(ellipse)', 'FreeCAD.ActiveDocument.recompute()' ] self.commit(translate("draft", "Create Ellipse"), _cmd_list) else: # Insert a Draft ellipse Gui.addModule("Draft") _cmd = 'Draft.makeEllipse' _cmd += '(' _cmd += str(r1) + ', ' + str(r2) + ', ' _cmd += 'placement=pl, ' _cmd += 'face=' + fil + ', ' _cmd += 'support=' + sup _cmd += ')' _cmd_list = [ 'pl = FreeCAD.Placement()', 'pl.Rotation.Q = ' + rot, 'pl.Base = ' + DraftVecUtils.toString(center), 'ellipse = ' + _cmd, 'Draft.autogroup(ellipse)', 'FreeCAD.ActiveDocument.recompute()' ] self.commit(translate("draft", "Create Ellipse"), _cmd_list) except Exception: _err("Draft: Error: Unable to create object.") self.finish(cont=True)
def createObject(self): """Create the final object in the current document.""" plane = App.DraftWorkingPlane p1 = self.node[0] p3 = self.node[-1] diagonal = p3.sub(p1) p2 = p1.add(DraftVecUtils.project(diagonal, plane.v)) p4 = p1.add(DraftVecUtils.project(diagonal, plane.u)) length = p4.sub(p1).Length if abs(DraftVecUtils.angle(p4.sub(p1), plane.u, plane.axis)) > 1: length = -length height = p2.sub(p1).Length if abs(DraftVecUtils.angle(p2.sub(p1), plane.v, plane.axis)) > 1: height = -height try: # The command to run is built as a series of text strings # to be committed through the `draftutils.todo.ToDo` class. rot, sup, pts, fil = self.getStrings() base = p1 if length < 0: length = -length base = base.add((p1.sub(p4)).negative()) if height < 0: height = -height base = base.add((p1.sub(p2)).negative()) Gui.addModule("Draft") if utils.getParam("UsePartPrimitives", False): # Insert a Part::Primitive object _cmd = 'FreeCAD.ActiveDocument.' _cmd += 'addObject("Part::Plane", "Plane")' _cmd_list = [ 'plane = ' + _cmd, 'plane.Length = ' + str(length), 'plane.Width = ' + str(height), 'pl = FreeCAD.Placement()', 'pl.Rotation.Q=' + rot, 'pl.Base = ' + DraftVecUtils.toString(base), 'plane.Placement = pl', 'Draft.autogroup(plane)', 'FreeCAD.ActiveDocument.recompute()' ] self.commit(translate("draft", "Create Plane"), _cmd_list) else: _cmd = 'Draft.makeRectangle' _cmd += '(' _cmd += 'length=' + str(length) + ', ' _cmd += 'height=' + str(height) + ', ' _cmd += 'placement=pl, ' _cmd += 'face=' + fil + ', ' _cmd += 'support=' + sup _cmd += ')' _cmd_list = [ 'pl = FreeCAD.Placement()', 'pl.Rotation.Q = ' + rot, 'pl.Base = ' + DraftVecUtils.toString(base), 'rec = ' + _cmd, 'Draft.autogroup(rec)', 'FreeCAD.ActiveDocument.recompute()' ] self.commit(translate("draft", "Create Rectangle"), _cmd_list) except Exception: _err("Draft: error delaying commit") self.finish(cont=True)
def validate_input(self, selection, r_distance, tan_distance, number, symmetry, axis, center): """Check that the input is valid. Some values may not need to be checked because the interface may not allow to input wrong data. """ if not selection: _err(translate("draft", "At least one element must be selected.")) return False if number < 2: _err(translate("draft", "Number of layers must be at least 2.")) return False # TODO: this should handle multiple objects. # Each of the elements of the selection should be tested. obj = selection[0] if obj.isDerivedFrom("App::FeaturePython"): _err(translate("draft", "Selection is not suitable for array.")) _err( translate("draft", "Object:") + " {}".format(selection[0].Label)) return False if r_distance == 0: _wrn( translate( "draft", "Radial distance is zero. Resulting array may not look correct." )) elif r_distance < 0: _wrn( translate( "draft", "Radial distance is negative. It is made positive to proceed." )) self.r_distance = abs(r_distance) if tan_distance == 0: _err(translate("draft", "Tangential distance cannot be zero.")) return False elif tan_distance < 0: _wrn( translate( "draft", "Tangential distance is negative. It is made positive to proceed." )) self.tan_distance = abs(tan_distance) # The other arguments are not tested but they should be present. if symmetry and axis and center: pass self.fuse = self.form.checkbox_fuse.isChecked() self.use_link = self.form.checkbox_link.isChecked() return True
def proceed(self): """Proceed with the command if one object was selected.""" if self.call: self.view.removeEventCallback("SoEvent", self.call) sel = Gui.Selection.getSelectionEx() if len(sel) != 2: _err( _tr("Please select exactly two objects, " "the base object and the path object, " "before calling this command.")) else: base_object = sel[0].Object path_object = sel[1].Object count = 4 extra = App.Vector(0, 0, 0) subelements = list(sel[1].SubElementNames) align = False align_mode = "Original" tan_vector = App.Vector(1, 0, 0) force_vertical = False vertical_vector = App.Vector(0, 0, 1) use_link = self.use_link _edge_list_str = list() _edge_list_str = ["'" + edge + "'" for edge in subelements] _sub_str = ", ".join(_edge_list_str) subelements_list_str = "[" + _sub_str + "]" vertical_vector_str = DraftVecUtils.toString(vertical_vector) Gui.addModule("Draft") _cmd = "Draft.make_path_array" _cmd += "(" _cmd += "App.ActiveDocument." + base_object.Name + ", " _cmd += "App.ActiveDocument." + path_object.Name + ", " _cmd += "count=" + str(count) + ", " _cmd += "extra=" + DraftVecUtils.toString(extra) + ", " _cmd += "subelements=" + subelements_list_str + ", " _cmd += "align=" + str(align) + ", " _cmd += "align_mode=" + "'" + align_mode + "', " _cmd += "tan_vector=" + DraftVecUtils.toString(tan_vector) + ", " _cmd += "force_vertical=" + str(force_vertical) + ", " _cmd += "vertical_vector=" + vertical_vector_str + ", " _cmd += "use_link=" + str(use_link) _cmd += ")" _cmd_list = [ "_obj_ = " + _cmd, "Draft.autogroup(_obj_)", "App.ActiveDocument.recompute()" ] self.commit(_tr(self.name), _cmd_list) # Commit the transaction and execute the commands # through the parent class self.finish()
def numericRadius(self, rad): """Validate the radius entry field in the user interface. This function is called by the toolbar or taskpanel interface when a valid radius has been entered in the input field. """ # print("dvec:", self.dvec) # print("rad:", rad) if self.dvec: if isinstance(self.dvec, float): if self.mode == "Circle": r1 = self.shape.Edges[0].Curve.Radius r2 = self.ghost.getRadius() if r2 >= r1: rad = r1 + rad else: rad = r1 - rad delta = str(rad) else: _err("Draft.Offset error: Unhandled case") # to offset bspline elif self.mode == "BSpline": new_points = [] for old_point, new_point in zip(self.sel.Points, self.npts): diff_direction = new_point.sub(old_point).normalize() new_points.append(old_point.add(diff_direction * rad)) delta = DraftVecUtils.toString(new_points) else: self.dvec.normalize() self.dvec.multiply(rad) delta = DraftVecUtils.toString(self.dvec) copymode = False occmode = self.ui.occOffset.isChecked() utils.param.SetBool("Offset_OCC", occmode) if self.ui.isCopy.isChecked(): copymode = True Gui.addModule("Draft") _cmd = 'Draft.offset' _cmd += '(' _cmd += 'FreeCAD.ActiveDocument.' _cmd += self.sel.Name + ', ' _cmd += delta + ', ' _cmd += 'copy=' + str(copymode) + ', ' _cmd += 'occ=' + str(occmode) _cmd += ')' _cmd_list = [ 'offst = ' + _cmd, 'FreeCAD.ActiveDocument.recompute()' ] self.commit(translate("draft", "Offset"), _cmd_list) self.finish() else: _err( translate( "Draft", "Offset direction is not defined. " "Please move the mouse on either side " "of the object first to indicate a direction"))
def make_rect_array2d(obj, d_x=10, d_y=10, n_x=2, n_y=2, use_link=True): """Create a 2D rectangular array from the given object. This function wraps around `make_ortho_array2d` to produce strictly rectangular arrays, in which the displacement vectors `v_x` and `v_y` only have their respective components in X and Y. 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. d_x, d_y: Base::Vector3, optional Displacement of elements in the corresponding X and Y directions. n_x, n_y: int, optional Number of elements in the corresponding X and Y directions. 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_ortho_array2d, make_rect_array """ _name = "make_rect_array2d" utils.print_header(_name, _tr("Rectangular array 2D")) _msg("d_x: {}".format(d_x)) _msg("d_y: {}".format(d_y)) try: utils.type_check([(d_x, (int, float)), (d_y, (int, float))], name=_name) except TypeError: _err(_tr("Wrong input: must be a number.")) return None new_obj = make_ortho_array2d(obj, v_x=App.Vector(d_x, 0, 0), v_y=App.Vector(0, d_y, 0), n_x=n_x, n_y=n_y, use_link=use_link) return new_obj
def convert_draft_texts(textslist=None): """Convert the given Annotation to a Draft text. In the past, the `Draft Text` object didn't exist; text objects were of type `App::Annotation`. This function was introduced to convert those older objects to a `Draft Text` scripted object. This function was already present at splitting time during v0.19. Parameters ---------- textslist: list of objects, optional It defaults to `None`. A list containing `App::Annotation` objects or a single of these objects. If it is `None` it will convert all objects in the current document. """ _name = "convert_draft_texts" utils.print_header(_name, "Convert Draft texts") found, doc = utils.find_doc(App.activeDocument()) if not found: _err(_tr("No active document. Aborting.")) return None if not textslist: textslist = list() for obj in doc.Objects: if obj.TypeId == "App::Annotation": textslist.append(obj) if not isinstance(textslist, list): textslist = [textslist] to_delete = [] for obj in textslist: label = obj.Label obj.Label = label + ".old" # Create a new Draft Text object new_obj = make_text(obj.LabelText, placement=obj.Position) new_obj.Label = label to_delete.append(obj) # Move the new object to the group which contained the old object for in_obj in obj.InList: if in_obj.isDerivedFrom("App::DocumentObjectGroup"): if obj in in_obj.Group: group = in_obj.Group group.append(new_obj) in_obj.Group = group for obj in to_delete: doc.removeObject(obj.Name)
def rotate_subelements(self, is_copy): """Rotate the subelements.""" try: if is_copy: self.commit(translate("draft", "Copy"), self.build_copy_subelements_command()) else: self.commit(translate("draft", "Rotate"), self.build_rotate_subelements_command()) except Exception: _err(translate("draft", "Some subelements could not be moved."))
def finish(self, closed=False, cont=False): """Terminate the operation and close the curve if asked. Parameters ---------- closed: bool, optional Close the line if `True`. """ if self.ui: if hasattr(self, "bezcurvetrack"): self.bezcurvetrack.finalize() if not utils.getParam("UiMode", 1): Gui.Control.closeDialog() if self.obj: # remove temporary object, if any old = self.obj.Name todo.ToDo.delay(self.doc.removeObject, old) if closed is False: cleannd = (len(self.node) - 1) % self.degree if cleannd == 0: self.node = self.node[0:-3] if cleannd > 0: self.node = self.node[0:-cleannd] if len(self.node) > 1: try: # The command to run is built as a series of text strings # to be committed through the `draftutils.todo.ToDo` class. rot, sup, pts, fil = self.getStrings() Gui.addModule("Draft") _cmd = 'Draft.makeBezCurve' _cmd += '(' _cmd += 'points, ' _cmd += 'closed=' + str(closed) + ', ' _cmd += 'support=' + sup + ', ' _cmd += 'degree=' + str(self.degree) _cmd += ')' _cmd_list = ['points = ' + pts, 'bez = ' + _cmd, 'Draft.autogroup(bez)', 'FreeCAD.ActiveDocument.recompute()'] self.commit(translate("draft", "Create BezCurve"), _cmd_list) except Exception: _err("Draft: error delaying commit") # `Creator` is the grandfather class, the parent of `Line`; # we need to call it to perform final cleanup tasks. # # Calling it directly like this is a bit messy; maybe we need # another method that performs cleanup (superfinish) # that is not re-implemented by any of the child classes. gui_base_original.Creator.finish(self) if self.ui and self.ui.continueMode: self.Activated()
def move_subelements(self): """Move the subelements.""" try: if self.ui.isCopy.isChecked(): self.commit(translate("draft", "Copy"), self.build_copy_subelements_command()) else: self.commit(translate("draft", "Move"), self.build_move_subelements_command()) except Exception: _err(translate("draft", "Some subelements could not be moved."))
def build_copies(base_object, pt_list=None, placement=App.Placement()): """Build a compound of copies from the base object and list of points. Returns ------- Part::TopoShape The compound shape created by `Part.makeCompound`. """ if not pt_list: _err( translate( "Draft", "Point object doesn't have a discrete point, " "it cannot be used for an array.")) shape = base_object.Shape.copy() return shape copies = list() for point in pt_list: new_shape = base_object.Shape.copy() original_rotation = new_shape.Placement.Rotation # Reset placement of the copy, and combine the original rotation # with the provided placement. Two rotations (quaternions) # are combined by multiplying them. new_shape.Placement.Base = placement.Base new_shape.Placement.Rotation = original_rotation * placement.Rotation # If the point object has a placement, use it # to displace the copy of the shape. Otherwise # translate by the X, Y, Z coordinates. if hasattr(point, 'Placement'): place = point.Placement new_shape.translate(place.Base) # The following old code doesn't make much sense because it uses # the rotation value of the auxiliary point. # Even if the point does have a rotation property as part of its # placement, rotating a point by itself is a strange workflow. # We want to use the position of the point but not its rotation, # which will probably be zero anyway. # Old code: # new_shape.rotate(place.Base, # place.Rotation.Axis, # math.degrees(place.Rotation.Angle)) else: new_shape.translate(App.Vector(point.X, point.Y, point.Z)) copies.append(new_shape) shape = Part.makeCompound(copies) return shape
def move_subelements(self, is_copy): """Move the subelements.""" Gui.addModule("Draft") try: if is_copy: self.commit(translate("draft", "Copy"), self.build_copy_subelements_command()) else: self.commit(translate("draft", "Move"), self.build_move_subelements_command()) except Exception: _err(translate("draft", "Some subelements could not be moved."))
def finish(self, closed=False, cont=False): """Terminate the operation and close the spline if asked. Parameters ---------- closed: bool, optional Close the line if `True`. """ # if self.ui: # self.linetrack.finalize() if not utils.getParam("UiMode", 1): Gui.Control.closeDialog() if self.obj: # Remove temporary object, if any old = self.obj.Name todo.ToDo.delay(self.doc.removeObject, old) if len(self.node) > 1: # The command to run is built as a series of text strings # to be committed through the `draftutils.todo.ToDo` class. try: # rot, sup, pts, fil = self.getStrings() cmd_list = [ 'from FreeCAD import Vector', 'from safe.punch.beam import make_beam', 'from safe.punch.base_foundation import make_base_foundation', 'beams = []', ] for p1, p2 in zip(self.node[:-1], self.node[1:]): cmd_list.append(f'beam = make_beam({p1}, {p2})') cmd_list.append(f'beams.append(beam)') hide_beams = self.hide_beams_checkbox.isChecked() cmd_list.append( f'make_base_foundation(beams, "{self.layer}", {self.bf_width}, {self.bf_height}, {self.bf_soil_modulus}, "{self.bf_align}", {self.bf_left_width}, {self.bf_right_width}, {hide_beams} )' ) self.commit(translate("civil", "Create Base Foundation"), cmd_list) FreeCAD.ParamGet( "User parameter:BaseApp/Preferences/Mod/OSAFE").SetBool( "base_foundation_hide_beams", hide_beams) except Exception: _err("Draft: error delaying commit") # `Creator` is the grandfather class, the parent of `Line`; # we need to call it to perform final cleanup tasks. # # Calling it directly like this is a bit messy; maybe we need # another method that performs cleanup (superfinish) # that is not re-implemented by any of the child classes. gui_base_original.Creator.finish(self) if self.ui and self.ui.continueMode: self.Activated()
def Activated(self): """Execute when the command is called.""" import Part # If there is a selection, and this selection contains various # two-point lines, their shapes are extracted, and we attempt # to join them into a single Wire (polyline), # then the old lines are removed. if len(Gui.Selection.getSelection()) > 1: edges = [] for o in Gui.Selection.getSelection(): if utils.get_type(o) != "Wire": edges = [] break edges.extend(o.Shape.Edges) if edges: try: w = Part.Wire(edges) except Exception: _err( translate( "draft", "Unable to create a Wire " "from selected objects")) else: # Points of the new fused Wire in string form # 'FreeCAD.Vector(x,y,z), FreeCAD.Vector(x1,y1,z1), ...' pts = ", ".join([str(v.Point) for v in w.Vertexes]) pts = pts.replace("Vector ", "FreeCAD.Vector") # List of commands to remove the old objects rems = list() for o in Gui.Selection.getSelection(): rems.append('FreeCAD.ActiveDocument.' 'removeObject("' + o.Name + '")') Gui.addModule("Draft") # The command to run is built as a series of text strings # to be committed through the `draftutils.todo.ToDo` class _cmd_list = ['wire = Draft.makeWire([' + pts + '])'] _cmd_list.extend(rems) _cmd_list.append('Draft.autogroup(wire)') _cmd_list.append('FreeCAD.ActiveDocument.recompute()') _op_name = translate("draft", "Convert to Wire") todo.ToDo.delayCommit([(_op_name, _cmd_list)]) return # If there was no selection or the selection was just one object # then we proceed with the normal line creation functions, # only this time we will be able to input more than two points super(Wire, self).Activated(name=translate("draft", "Polyline"), icon="Draft_Wire")
def execute(self, obj): """Run when the object is created or recomputed.""" if not hasattr(obj.Base, 'Shape'): _err(_tr("Base object doesn't have a 'Shape', " "it cannot be used for an array.")) obj.Count = 0 return pt_list, count = get_point_list(obj.PointObject) shape = build_copies(obj.Base, pt_list, obj.ExtraPlacement) obj.Shape = shape obj.Count = count