def updateData(self, obj, prop): """Execute when a property from the Proxy class is changed.""" if not hasattr(self, "arc"): return arcsegs = 24 vobj = obj.ViewObject # Determine the orientation of the text by using a normal direction. # Also calculate the arc data. if DraftVecUtils.isNull(obj.Normal): norm = App.Vector(0, 0, 1) else: norm = obj.Normal radius = (obj.Dimline - obj.Center).Length self.circle = Part.makeCircle(radius, obj.Center, norm, obj.FirstAngle.Value, obj.LastAngle.Value) self.p2 = self.circle.Vertexes[0].Point self.p3 = self.circle.Vertexes[-1].Point midp = DraftGeomUtils.findMidpoint(self.circle.Edges[0]) ray = midp - obj.Center # Set text value if obj.LastAngle.Value > obj.FirstAngle.Value: angle = obj.LastAngle.Value - obj.FirstAngle.Value else: angle = (360 - obj.FirstAngle.Value) + obj.LastAngle.Value show_unit = True if hasattr(vobj, "ShowUnit"): show_unit = vobj.ShowUnit if hasattr(vobj, "Decimals"): self.string = units.display_external(angle, vobj.Decimals, 'Angle', show_unit) else: self.string = units.display_external(angle, None, 'Angle', show_unit) if vobj.Override: self.string = vobj.Override.replace("$dim", self.string) self.text.string = utils.string_encode_coin(self.string) self.text3d.string = utils.string_encode_coin(self.string) # On first run the `DisplayMode` enumeration is not set, so we trap # the exception and set the display mode using the value # in the parameter database try: m = vobj.DisplayMode except AssertionError: m = ["2D", "3D"][utils.get_param("dimstyle", 0)] # Set the arc first = self.circle.FirstParameter last = self.circle.LastParameter if m == "3D": # Calculate the spacing of the text spacing = len(self.string) * vobj.FontSize.Value / 8.0 pts1 = [] cut = None pts2 = [] for i in range(arcsegs + 1): p = self.circle.valueAt(first + (last - first) / arcsegs * i) if (p - midp).Length <= spacing: if cut is None: cut = i else: if cut is None: pts1.append([p.x, p.y, p.z]) else: pts2.append([p.x, p.y, p.z]) self.coords.point.setValues(pts1 + pts2) pts1_num = len(pts1) pts2_num = len(pts2) i1 = pts1_num i2 = i1 + pts2_num self.arc.coordIndex.setValues( 0, pts1_num + pts2_num + 1, list(range(pts1_num)) + [-1] + list(range(i1, i2))) if pts1_num >= 3 and pts2_num >= 3: self.circle1 = Part.Arc( App.Vector(pts1[0][0], pts1[0][1], pts1[0][2]), App.Vector(pts1[1][0], pts1[1][1], pts1[1][2]), App.Vector(pts1[-1][0], pts1[-1][1], pts1[-1][2])).toShape() self.circle2 = Part.Arc( App.Vector(pts2[0][0], pts2[0][1], pts2[0][2]), App.Vector(pts2[1][0], pts2[1][1], pts2[1][2]), App.Vector(pts2[-1][0], pts2[-1][1], pts2[-1][2])).toShape() else: pts = [] for i in range(arcsegs + 1): p = self.circle.valueAt(first + (last - first) / arcsegs * i) pts.append([p.x, p.y, p.z]) self.coords.point.setValues(pts) self.arc.coordIndex.setValues(0, arcsegs + 1, list(range(arcsegs + 1))) # Set the arrow coords and rotation p2 = (self.p2.x, self.p2.y, self.p2.z) p3 = (self.p3.x, self.p3.y, self.p3.z) self.trans1.translation.setValue(p2) self.coord1.point.setValue(p2) self.trans2.translation.setValue(p3) self.coord2.point.setValue(p3) # Calculate small chords to make arrows look better arrowlength = 4 * vobj.ArrowSize.Value u1 = (self.circle.valueAt(first + arrowlength) - self.circle.valueAt(first)).normalize() u2 = (self.circle.valueAt(last) - self.circle.valueAt(last - arrowlength)).normalize() if hasattr(vobj, "FlipArrows") and vobj.FlipArrows: u1 = u1.negative() u2 = u2.negative() w2 = self.circle.Curve.Axis w1 = w2.negative() v1 = w1.cross(u1) v2 = w2.cross(u2) _plane_rot_1 = DraftVecUtils.getPlaneRotation(u1, v1, w1) _plane_rot_2 = DraftVecUtils.getPlaneRotation(u2, v2, w2) q1 = App.Placement(_plane_rot_1).Rotation.Q q2 = App.Placement(_plane_rot_2).Rotation.Q self.trans1.rotation.setValue((q1[0], q1[1], q1[2], q1[3])) self.trans2.rotation.setValue((q2[0], q2[1], q2[2], q2[3])) # Set text position and rotation self.tbase = midp if (hasattr(vobj, "TextPosition") and not DraftVecUtils.isNull(vobj.TextPosition)): self.tbase = vobj.TextPosition u3 = ray.cross(norm).normalize() v3 = norm.cross(u3) _plane_rot_3 = DraftVecUtils.getPlaneRotation(u3, v3, norm) r = App.Placement(_plane_rot_3).Rotation offset = r.multVec(App.Vector(0, 1, 0)) if hasattr(vobj, "TextSpacing"): offset = DraftVecUtils.scaleTo(offset, vobj.TextSpacing.Value) else: offset = DraftVecUtils.scaleTo(offset, 0.05) if m == "3D": offset = offset.negative() self.tbase = self.tbase.add(offset) q = r.Q self.textpos.translation.setValue( [self.tbase.x, self.tbase.y, self.tbase.z]) self.textpos.rotation = coin.SbRotation(q[0], q[1], q[2], q[3]) # Set the angle property _round_1 = round(obj.Angle, utils.precision()) _round_2 = round(angle, utils.precision()) if _round_1 != _round_2: obj.Angle = angle
def updateData(self, obj, prop): """Execute when a property from the Proxy class is changed. It only runs if `Start`, `End`, `Dimline`, or `Direction` changed. """ if prop not in ("Start", "End", "Dimline", "Direction"): return if obj.Start == obj.End: return if not hasattr(self, "node"): return vobj = obj.ViewObject # Calculate the 4 points # # | d | # ---p2-------------c----p3---- c # | | # | | # p1 p4 # # - `c` is the `Dimline`, a point that lies on the dimension line # or on its extension. # - The line itself between `p2` to `p3` is the `base`. # - The distance between `p2` (`base`) to `p1` is `proj`, an extension # line from the dimension to the measured object. # - If the `proj` distance is zero, `p1` and `p2` are the same point, # and same with `p3` and `p4`. # self.p1 = obj.Start self.p4 = obj.End base = None if (hasattr(obj, "Direction") and not DraftVecUtils.isNull(obj.Direction)): v2 = self.p1 - obj.Dimline v3 = self.p4 - obj.Dimline v2 = DraftVecUtils.project(v2, obj.Direction) v3 = DraftVecUtils.project(v3, obj.Direction) self.p2 = obj.Dimline + v2 self.p3 = obj.Dimline + v3 if DraftVecUtils.equals(self.p2, self.p3): base = None proj = None else: base = Part.LineSegment(self.p2, self.p3).toShape() proj = DraftGeomUtils.findDistance(self.p1, base) if proj: proj = proj.negative() if not base: if DraftVecUtils.equals(self.p1, self.p4): base = None proj = None else: base = Part.LineSegment(self.p1, self.p4).toShape() proj = DraftGeomUtils.findDistance(obj.Dimline, base) if proj: self.p2 = self.p1 + proj.negative() self.p3 = self.p4 + proj.negative() else: self.p2 = self.p1 self.p3 = self.p4 if proj: if hasattr(vobj, "ExtLines") and hasattr(vobj, "ScaleMultiplier"): # The scale multiplier also affects the value # of the extension line; this makes sure a maximum length # is used if the calculated value is larger than it. dmax = vobj.ExtLines.Value * vobj.ScaleMultiplier if dmax and proj.Length > dmax: if dmax > 0: self.p1 = self.p2 + DraftVecUtils.scaleTo(proj, dmax) self.p4 = self.p3 + DraftVecUtils.scaleTo(proj, dmax) else: rest = proj.Length + dmax self.p1 = self.p2 + DraftVecUtils.scaleTo(proj, rest) self.p4 = self.p3 + DraftVecUtils.scaleTo(proj, rest) else: proj = (self.p3 - self.p2).cross(App.Vector(0, 0, 1)) # Calculate the arrow positions p2 = (self.p2.x, self.p2.y, self.p2.z) p3 = (self.p3.x, self.p3.y, self.p3.z) self.trans1.translation.setValue(p2) self.coord1.point.setValue(p2) self.trans2.translation.setValue(p3) self.coord2.point.setValue(p3) # Calculate dimension and extension lines overshoots positions self.transDimOvershoot1.translation.setValue(p2) self.transDimOvershoot2.translation.setValue(p3) self.transExtOvershoot1.translation.setValue(p2) self.transExtOvershoot2.translation.setValue(p3) # Determine the orientation of the text by using a normal direction. # By default the value of +Z will be used, or a calculated value # from p2 and p3. So the text will lie on the XY plane # or a plane coplanar with p2 and p3. u = self.p3 - self.p2 u.normalize() if proj: _norm = u.cross(proj) norm = _norm.negative() else: norm = App.Vector(0, 0, 1) # If `Normal` exists and is different from the default `(0,0,0)`, # it will be used. if hasattr(obj, "Normal") and not DraftVecUtils.isNull(obj.Normal): norm = App.Vector(obj.Normal) if not DraftVecUtils.isNull(norm): norm.normalize() # Calculate the position of the arrows and extension lines v1 = norm.cross(u) _plane_rot = DraftVecUtils.getPlaneRotation(u, v1, norm) rot1 = App.Placement(_plane_rot).Rotation.Q self.transDimOvershoot1.rotation.setValue( (rot1[0], rot1[1], rot1[2], rot1[3])) self.transDimOvershoot2.rotation.setValue( (rot1[0], rot1[1], rot1[2], rot1[3])) if hasattr(vobj, "FlipArrows") and vobj.FlipArrows: u = u.negative() v2 = norm.cross(u) _plane_rot = DraftVecUtils.getPlaneRotation(u, v2, norm) rot2 = App.Placement(_plane_rot).Rotation.Q self.trans1.rotation.setValue((rot2[0], rot2[1], rot2[2], rot2[3])) self.trans2.rotation.setValue((rot2[0], rot2[1], rot2[2], rot2[3])) if self.p1 != self.p2: u3 = self.p1 - self.p2 u3.normalize() v3 = norm.cross(u3) _plane_rot = DraftVecUtils.getPlaneRotation(u3, v3, norm) rot3 = App.Placement(_plane_rot).Rotation.Q self.transExtOvershoot1.rotation.setValue( (rot3[0], rot3[1], rot3[2], rot3[3])) self.transExtOvershoot2.rotation.setValue( (rot3[0], rot3[1], rot3[2], rot3[3])) # Offset is the distance from the dimension line to the textual # element that displays the value of the measurement if hasattr(vobj, "TextSpacing") and hasattr(vobj, "ScaleMultiplier"): ts = vobj.TextSpacing.Value * vobj.ScaleMultiplier offset = DraftVecUtils.scaleTo(v1, ts) else: offset = DraftVecUtils.scaleTo(v1, 0.05) rott = rot1 if hasattr(vobj, "FlipText") and vobj.FlipText: _rott = App.Rotation(rott[0], rott[1], rott[2], rott[3]) rott = _rott.multiply(App.Rotation(norm, 180)).Q offset = offset.negative() # On first run the `DisplayMode` enumeration is not set, so we trap # the exception and set the display mode using the value # in the parameter database try: m = vobj.DisplayMode except AssertionError: m = ["2D", "3D"][utils.get_param("dimstyle", 0)] if m == "3D": offset = offset.negative() # The position of the text element in the dimension is provided # in absolute coordinates by the value of `TextPosition`, # if it is different from the default `(0,0,0)` if (hasattr(vobj, "TextPosition") and not DraftVecUtils.isNull(vobj.TextPosition)): self.tbase = vobj.TextPosition else: # Otherwise the position is calculated from the end points # of the dimension line, and the offset that depends # on `TextSpacing` center = self.p2 + (self.p3 - self.p2).multiply(0.5) self.tbase = center + offset self.textpos.translation.setValue( [self.tbase.x, self.tbase.y, self.tbase.z]) self.textpos.rotation = coin.SbRotation(rott[0], rott[1], rott[2], rott[3]) show_unit = True if hasattr(vobj, "ShowUnit"): show_unit = vobj.ShowUnit # Set text element showing the value of the dimension length = (self.p3 - self.p2).Length unit = None if hasattr(vobj, "UnitOverride"): unit = vobj.UnitOverride # Special representation if we use 'Building US' scheme u_params = App.ParamGet("User parameter:BaseApp/Preferences/Units") if u_params.GetInt("UserSchema", 0) == 5: s = App.Units.Quantity(length, App.Units.Length).UserString self.string = s.replace("' ", "'- ") # feet self.string = s.replace("+", " ") elif hasattr(vobj, "Decimals"): self.string = units.display_external(length, vobj.Decimals, 'Length', show_unit, unit) else: self.string = units.display_external(length, None, 'Length', show_unit, unit) if hasattr(vobj, "Override") and vobj.Override: self.string = vobj.Override.replace("$dim", self.string) self.text.string = utils.string_encode_coin(self.string) self.text3d.string = utils.string_encode_coin(self.string) # Set the lines if m == "3D": # Calculate the spacing of the text textsize = len(self.string) * vobj.FontSize.Value / 4.0 spacing = (self.p3 - self.p2).Length / 2.0 - textsize self.p2a = self.p2 + DraftVecUtils.scaleTo(self.p3 - self.p2, spacing) self.p2b = self.p3 + DraftVecUtils.scaleTo(self.p2 - self.p3, spacing) self.coords.point.setValues([[self.p1.x, self.p1.y, self.p1.z], [self.p2.x, self.p2.y, self.p2.z], [self.p2a.x, self.p2a.y, self.p2a.z], [self.p2b.x, self.p2b.y, self.p2b.z], [self.p3.x, self.p3.y, self.p3.z], [self.p4.x, self.p4.y, self.p4.z]]) # self.line.numVertices.setValues([3, 3]) self.line.coordIndex.setValues(0, 7, (0, 1, 2, -1, 3, 4, 5)) else: self.coords.point.setValues([[self.p1.x, self.p1.y, self.p1.z], [self.p2.x, self.p2.y, self.p2.z], [self.p3.x, self.p3.y, self.p3.z], [self.p4.x, self.p4.y, self.p4.z]]) # self.line.numVertices.setValue(4) self.line.coordIndex.setValues(0, 4, (0, 1, 2, 3))