def proceed(self): """Proceed with the command if one object was selected.""" if self.call: self.view.removeEventCallback("SoEvent", self.call) self.sel = Gui.Selection.getSelection()[0] if not self.sel.isDerivedFrom("Part::Feature"): _wrn(translate("draft", "Cannot offset this object type")) self.finish() else: self.step = 0 self.dvec = None self.npts = None self.constrainSeg = None self.ui.offsetUi() occmode = utils.param.GetBool("Offset_OCC", False) self.ui.occOffset.setChecked(occmode) self.linetrack = trackers.lineTracker() self.faces = False self.shape = self.sel.Shape self.mode = None if utils.getType(self.sel) in ("Circle", "Arc"): self.ghost = trackers.arcTracker() self.mode = "Circle" self.center = self.shape.Edges[0].Curve.Center self.ghost.setCenter(self.center) self.ghost.setStartAngle(math.radians(self.sel.FirstAngle)) self.ghost.setEndAngle(math.radians(self.sel.LastAngle)) elif utils.getType(self.sel) == "BSpline": self.ghost = trackers.bsplineTracker(points=self.sel.Points) self.mode = "BSpline" elif utils.getType(self.sel) == "BezCurve": _wrn( translate( "draft", "Offset of Bezier curves " "is currently not supported")) self.finish() return else: if len(self.sel.Shape.Edges) == 1: import Part if isinstance(self.sel.Shape.Edges[0].Curve, Part.Circle): self.ghost = trackers.arcTracker() self.mode = "Circle" self.center = self.shape.Edges[0].Curve.Center self.ghost.setCenter(self.center) if len(self.sel.Shape.Vertexes) > 1: _edge = self.sel.Shape.Edges[0] self.ghost.setStartAngle(_edge.FirstParameter) self.ghost.setEndAngle(_edge.LastParameter) if not self.ghost: self.ghost = trackers.wireTracker(self.shape) self.mode = "Wire" self.call = self.view.addEventCallback("SoEvent", self.action) _msg(translate("draft", "Pick distance")) if self.planetrack: self.planetrack.set(self.shape.Vertexes[0].Point) self.running = True
def Activated(self): """Execute when the command is called.""" if self.running: self.finish() # TODO: iterate over all selected items to transform # many objects. As it is right now, it only works on the first object # in the selection. # Also, it is recommended to use the `self.commit` function # in order to properly open a transaction and commit it. selection = Gui.Selection.getSelection() if selection: if utils.getType(selection[0]) in ['Wire', 'BSpline']: super(WireToBSpline, self).Activated(name=_tr("Convert polyline/B-spline")) if self.doc: self.obj = Gui.Selection.getSelection() if self.obj: self.obj = self.obj[0] self.pl = None if "Placement" in self.obj.PropertiesList: self.pl = self.obj.Placement self.Points = self.obj.Points self.closed = self.obj.Closed n = None if utils.getType(self.obj) == 'Wire': n = Draft.makeBSpline(self.Points, closed=self.closed, placement=self.pl) elif utils.getType(self.obj) == 'BSpline': self.bs2wire = True n = Draft.makeWire(self.Points, closed=self.closed, placement=self.pl, face=None, support=None, bs2wire=self.bs2wire) if n: Draft.formatObject(n, self.obj) self.doc.recompute() else: self.finish()
def is_scalable(self, obj): """Return True only for the supported objects. Currently it only supports `Rectangle`, `Wire`, `Annotation` and `BSpline`. """ t = utils.getType(obj) if t in ["Rectangle", "Wire", "Annotation", "BSpline","Image::ImagePlane"]: # TODO: support more types in Draft.scale return True else: return False
def proceed(self): """Proceed with execution of the command after proper selection.""" if self.call: self.view.removeEventCallback("SoEvent", self.call) supported = ["Rectangle", "Wire", "BSpline", "BezCurve", "Sketch"] self.sel = [] for obj in Gui.Selection.getSelection(): if utils.getType(obj) in supported: self.sel.append([obj, App.Placement()]) elif hasattr(obj, "Base"): if obj.Base: if utils.getType(obj.Base) in supported: self.sel.append([obj.Base, obj.Placement]) elif utils.getType(obj.Base) in ["Offset2D", "Array"]: base = None if hasattr(obj.Base, "Source") and obj.Base.Source: base = obj.Base.Source elif hasattr(obj.Base, "Base") and obj.Base.Base: base = obj.Base.Base if base: if utils.getType(base) in supported: self.sel.append([ base, obj.Placement.multiply(obj.Base.Placement) ]) elif hasattr(obj.Base, "Base"): if obj.Base.Base: if utils.getType(obj.Base.Base) in supported: self.sel.append([ obj.Base.Base, obj.Placement.multiply(obj.Base.Placement) ]) elif utils.getType(obj) in ["Offset2D", "Array"]: base = None if hasattr(obj, "Source") and obj.Source: base = obj.Source elif hasattr(obj, "Base") and obj.Base: base = obj.Base if base: if utils.getType(base) in supported: self.sel.append([base, obj.Placement]) if self.ui and self.sel: self.step = 1 self.refpoint = None self.ui.pointUi(title=translate("draft", self.featureName), icon="Draft_Stretch") self.call = self.view.addEventCallback("SoEvent", self.action) self.rectracker = trackers.rectangleTracker(dotted=True, scolor=(0.0, 0.0, 1.0), swidth=2) self.nodetracker = [] self.displacement = None _msg(translate("draft", "Pick first point of selection rectangle"))
def doStretch(self): """Do the actual stretching once the points are selected.""" commitops = [] if self.displacement: if self.displacement.Length > 0: _doc = "FreeCAD.ActiveDocument." # print("displacement: ", self.displacement) # TODO: break this section into individual functions # depending on the type of object (wire, curve, sketch, # rectangle, etc.) that is selected, and use variables # with common strings to avoid repeating # the same information every time, for example, the `_doc` # variable. # This is necessary to reduce the number of indentation levels # and make the code easier to read. for ops in self.ops: tp = utils.getType(ops[0]) _rot = ops[0].Placement.Rotation localdisp = _rot.inverted().multVec(self.displacement) if tp in ["Wire", "BSpline", "BezCurve"]: pts = [] for i in range(len(ops[1])): if ops[1][i] is False: pts.append(ops[0].Points[i]) else: pts.append(ops[0].Points[i].add(localdisp)) pts = str(pts).replace("Vector ", "FreeCAD.Vector") _cmd = _doc + ops[0].Name + ".Points=" + pts commitops.append(_cmd) elif tp in ["Sketch"]: baseverts = [ ops[0].Shape.Vertexes[i].Point for i in range(len(ops[1])) if ops[1][i] ] for i in range(ops[0].GeometryCount): j = 0 while True: try: p = ops[0].getPoint(i, j) except ValueError: break else: p = ops[0].Placement.multVec(p) r = None for bv in baseverts: if DraftVecUtils.isNull(p.sub(bv)): _cmd = _doc _cmd += ops[0].Name _cmd += ".movePoint" _cmd += "(" _cmd += str(i) + ", " _cmd += str(j) + ", " _cmd += "FreeCAD." + str( localdisp) + ", " _cmd += "True" _cmd += ")" commitops.append(_cmd) r = bv break if r: baseverts.remove(r) j += 1 elif tp in ["Rectangle"]: p1 = App.Vector(0, 0, 0) p2 = App.Vector(ops[0].Length.Value, 0, 0) p3 = App.Vector(ops[0].Length.Value, ops[0].Height.Value, 0) p4 = App.Vector(0, ops[0].Height.Value, 0) if ops[1] == [False, True, True, False]: optype = 1 elif ops[1] == [False, False, True, True]: optype = 2 elif ops[1] == [True, False, False, True]: optype = 3 elif ops[1] == [True, True, False, False]: optype = 4 else: optype = 0 # print("length:", ops[0].Length, # "height:", ops[0].Height, # " - ", ops[1], # " - ", self.displacement) done = False if optype > 0: v1 = ops[0].Placement.multVec(p2).sub( ops[0].Placement.multVec(p1)) a1 = round(self.displacement.getAngle(v1), 4) v2 = ops[0].Placement.multVec(p4).sub( ops[0].Placement.multVec(p1)) a2 = round(self.displacement.getAngle(v2), 4) # check if the displacement is along one # of the rectangle directions if a1 == 0: # 0 degrees if optype == 1: if ops[0].Length.Value >= 0: d = ops[ 0].Length.Value + self.displacement.Length else: d = ops[ 0].Length.Value - self.displacement.Length _cmd = _doc _cmd += ops[0].Name + ".Length=" + str(d) commitops.append(_cmd) done = True elif optype == 3: if ops[0].Length.Value >= 0: d = ops[ 0].Length.Value - self.displacement.Length else: d = ops[ 0].Length.Value + self.displacement.Length _cmd = _doc + ops[0].Name _cmd += ".Length=" + str(d) _pl = _doc + ops[0].Name _pl += ".Placement.Base=FreeCAD." _pl += str(ops[0].Placement.Base.add( self.displacement)) commitops.append(_cmd) commitops.append(_pl) done = True elif a1 == 3.1416: # pi radians, 180 degrees if optype == 1: if ops[0].Length.Value >= 0: d = ops[ 0].Length.Value - self.displacement.Length else: d = ops[ 0].Length.Value + self.displacement.Length _cmd = _doc + ops[0].Name _cmd += ".Length=" + str(d) commitops.append(_cmd) done = True elif optype == 3: if ops[0].Length.Value >= 0: d = ops[ 0].Length.Value + self.displacement.Length else: d = ops[ 0].Length.Value - self.displacement.Length _cmd = _doc + ops[0].Name _cmd += ".Length=" + str(d) _pl = _doc + ops[0].Name _pl += ".Placement.Base=FreeCAD." _pl += str(ops[0].Placement.Base.add( self.displacement)) commitops.append(_cmd) commitops.append(_pl) done = True elif a2 == 0: # 0 degrees if optype == 2: if ops[0].Height.Value >= 0: d = ops[ 0].Height.Value + self.displacement.Length else: d = ops[ 0].Height.Value - self.displacement.Length _cmd = _doc + ops[0].Name _cmd += ".Height=" + str(d) commitops.append(_cmd) done = True elif optype == 4: if ops[0].Height.Value >= 0: d = ops[ 0].Height.Value - self.displacement.Length else: d = ops[ 0].Height.Value + self.displacement.Length _cmd = _doc + ops[0].Name _cmd += ".Height=" + str(d) _pl = _doc + ops[0].Name _pl += ".Placement.Base=FreeCAD." _pl += str(ops[0].Placement.Base.add( self.displacement)) commitops.append(_cmd) commitops.append(_pl) done = True elif a2 == 3.1416: # pi radians, 180 degrees if optype == 2: if ops[0].Height.Value >= 0: d = ops[ 0].Height.Value - self.displacement.Length else: d = ops[ 0].Height.Value + self.displacement.Length _cmd = _doc + ops[0].Name _cmd += ".Height=" + str(d) commitops.append(_cmd) done = True elif optype == 4: if ops[0].Height.Value >= 0: d = ops[ 0].Height.Value + self.displacement.Length else: d = ops[ 0].Height.Value - self.displacement.Length _cmd = _doc + ops[0].Name _cmd += ".Height=" + str(d) _pl = _doc + ops[0].Name _pl += ".Placement.Base=FreeCAD." _pl += str(ops[0].Placement.Base.add( self.displacement)) commitops.append(_cmd) commitops.append(_pl) done = True if not done: # otherwise create a wire copy and stretch it instead _msg( translate("draft", "Turning one Rectangle into a Wire")) pts = [] vts = ops[0].Shape.Vertexes for i in range(4): if ops[1][i] == False: pts.append(vts[i].Point) else: pts.append(vts[i].Point.add( self.displacement)) pts = str(pts).replace("Vector ", "FreeCAD.Vector") _cmd = "Draft.make_wire" _cmd += "(" + pts + ", closed=True, " _cmd += "face=" + str(ops[0].MakeFace) _cmd += ")" _format = "Draft.formatObject" _format += "(w, " _format += _doc + ops[0].Name _format += ")" _hide = _doc + ops[0].Name + ".ViewObject.hide()" commitops.append("w = " + _cmd) commitops.append(_format) commitops.append(_hide) else: _pl = _doc + ops[0].Name _pl += ".Placement.Base=FreeCAD." _pl += str(ops[0].Placement.Base.add( self.displacement)) commitops.append(_pl) if commitops: commitops.append("FreeCAD.ActiveDocument.recompute()") Gui.addModule("Draft") self.commit(translate("draft", "Stretch"), commitops) self.finish()
def addPoint(self, point): """Add point to defined selection rectangle.""" if self.step == 1: # first rctangle point _msg( translate("draft", "Pick opposite point " "of selection rectangle")) self.ui.setRelative() self.rectracker.setorigin(point) self.rectracker.on() if self.planetrack: self.planetrack.set(point) self.step = 2 elif self.step == 2: # second rectangle point _msg(translate("draft", "Pick start point of displacement")) self.rectracker.off() nodes = [] self.ops = [] for sel in self.sel: o = sel[0] vispla = sel[1] tp = utils.getType(o) if tp in ["Wire", "BSpline", "BezCurve"]: np = [] iso = False for p in o.Points: p = o.Placement.multVec(p) p = vispla.multVec(p) isi = self.rectracker.isInside(p) np.append(isi) if isi: iso = True nodes.append(p) if iso: self.ops.append([o, np]) elif tp in ["Rectangle"]: p1 = App.Vector(0, 0, 0) p2 = App.Vector(o.Length.Value, 0, 0) p3 = App.Vector(o.Length.Value, o.Height.Value, 0) p4 = App.Vector(0, o.Height.Value, 0) np = [] iso = False for p in [p1, p2, p3, p4]: p = o.Placement.multVec(p) p = vispla.multVec(p) isi = self.rectracker.isInside(p) np.append(isi) if isi: iso = True nodes.append(p) if iso: self.ops.append([o, np]) elif tp in ["Sketch"]: np = [] iso = False for p in o.Shape.Vertexes: p = vispla.multVec(p.Point) isi = self.rectracker.isInside(p) np.append(isi) if isi: iso = True nodes.append(p) if iso: self.ops.append([o, np]) else: p = o.Placement.Base p = vispla.multVec(p) if self.rectracker.isInside(p): self.ops.append([o]) nodes.append(p) for n in nodes: nt = trackers.editTracker(n, inactive=True) nt.on() self.nodetracker.append(nt) self.step = 3 elif self.step == 3: # first point of displacement line _msg(translate("draft", "Pick end point of displacement")) self.displacement = point # print("first point:", point) self.node = [point] self.step = 4 elif self.step == 4: # print("second point:", point) self.displacement = point.sub(self.displacement) self.doStretch() if self.point: self.ui.redraw()
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(utils.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(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 if copy and utils.getType( "selectBaseObjects" ): # was utils.getType("selectBaseObjects", False) gui_utils.select(objectslist) else: gui_utils.select(newobjlist) if len(newobjlist) == 1: return newobjlist[0] return newobjlist
def trimObjects(self, objectslist): """Attempt to trim two objects together.""" import Part import DraftGeomUtils wires = [] for obj in objectslist: if not utils.getType(obj) in ["Wire", "Circle"]: _err( translate( "draft", "Unable to trim these objects, " "only Draft wires and arcs are supported.")) return if len(obj.Shape.Wires) > 1: _err( translate( "draft", "Unable to trim these objects, " "too many wires")) return if len(obj.Shape.Wires) == 1: wires.append(obj.Shape.Wires[0]) else: wires.append(Part.Wire(obj.Shape.Edges)) ints = [] edge1 = None edge2 = None for i1, e1 in enumerate(wires[0].Edges): for i2, e2 in enumerate(wires[1].Edges): i = DraftGeomUtils.findIntersection(e1, e2, dts=False) if len(i) == 1: ints.append(i[0]) edge1 = i1 edge2 = i2 if not ints: _err(translate("draft", "These objects don't intersect.")) return if len(ints) != 1: _err(translate("draft", "Too many intersection points.")) return v11 = wires[0].Vertexes[0].Point v12 = wires[0].Vertexes[-1].Point v21 = wires[1].Vertexes[0].Point v22 = wires[1].Vertexes[-1].Point if DraftVecUtils.closest(ints[0], [v11, v12]) == 1: last1 = True else: last1 = False if DraftVecUtils.closest(ints[0], [v21, v22]) == 1: last2 = True else: last2 = False for i, obj in enumerate(objectslist): if i == 0: ed = edge1 la = last1 else: ed = edge2 la = last2 if utils.getType(obj) == "Wire": if la: pts = obj.Points[:ed + 1] + ints else: pts = ints + obj.Points[ed + 1:] obj.Points = pts else: vec = ints[0].sub(obj.Placement.Base) vec = obj.Placement.inverse().Rotation.multVec(vec) _x = App.Vector(1, 0, 0) _ang = -DraftVecUtils.angle(vec, obj.Placement.Rotation.multVec(_x), obj.Shape.Edges[0].Curve.Axis) ang = math.degrees(_ang) if la: obj.LastAngle = ang else: obj.FirstAngle = ang self.doc.recompute()
def trimObject(self): """Trim the actual object.""" import Part if self.extrudeMode: delta = self.extrude(self.shift, real=True) # print("delta", delta) self.doc.openTransaction("Extrude") Gui.addModule("Draft") obj = Draft.extrude(self.obj, delta, solid=True) self.doc.commitTransaction() self.obj = obj else: edges = self.redraw(self.point, self.snapped, self.shift, self.alt, real=True) newshape = Part.Wire(edges) self.doc.openTransaction("Trim/extend") if utils.getType(self.obj) in ["Wire", "BSpline"]: p = [] if self.placement: invpl = self.placement.inverse() for v in newshape.Vertexes: np = v.Point if self.placement: np = invpl.multVec(np) p.append(np) self.obj.Points = p elif utils.getType(self.obj) == "Part::Line": p = [] if self.placement: invpl = self.placement.inverse() for v in newshape.Vertexes: np = v.Point if self.placement: np = invpl.multVec(np) p.append(np) if ((p[0].x == self.obj.X1) and (p[0].y == self.obj.Y1) and (p[0].z == self.obj.Z1)): self.obj.X2 = p[-1].x self.obj.Y2 = p[-1].y self.obj.Z2 = p[-1].z elif ((p[-1].x == self.obj.X1) and (p[-1].y == self.obj.Y1) and (p[-1].z == self.obj.Z1)): self.obj.X2 = p[0].x self.obj.Y2 = p[0].y self.obj.Z2 = p[0].z elif ((p[0].x == self.obj.X2) and (p[0].y == self.obj.Y2) and (p[0].z == self.obj.Z2)): self.obj.X1 = p[-1].x self.obj.Y1 = p[-1].y self.obj.Z1 = p[-1].z else: self.obj.X1 = p[0].x self.obj.Y1 = p[0].y self.obj.Z1 = p[0].z elif utils.getType(self.obj) == "Circle": angles = self.ghost[0].getAngles() # print("original", self.obj.FirstAngle," ",self.obj.LastAngle) # print("new", angles) if angles[0] > angles[1]: angles = (angles[1], angles[0]) self.obj.FirstAngle = angles[0] self.obj.LastAngle = angles[1] else: self.obj.Shape = newshape self.doc.commitTransaction() self.doc.recompute() for g in self.ghost: g.off()