Ejemplo n.º 1
0
    def updateData(self, obj, prop):
        if hasattr(self, "arc"):
            from pivy import coin
            import Part, DraftGeomUtils
            import DraftGui
            arcsegs = 24

            # calculate the arc data
            if DraftVecUtils.isNull(obj.Normal):
                norm = App.Vector(0, 0, 1)
            else:
                norm = obj.Normal
            radius = (obj.Dimline.sub(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
            mp = DraftGeomUtils.findMidpoint(self.circle.Edges[0])
            ray = mp.sub(obj.Center)

            # set text value
            if obj.LastAngle.Value > obj.FirstAngle.Value:
                a = obj.LastAngle.Value - obj.FirstAngle.Value
            else:
                a = (360 - obj.FirstAngle.Value) + obj.LastAngle.Value
            su = True
            if hasattr(obj.ViewObject, "ShowUnit"):
                su = obj.ViewObject.ShowUnit
            if hasattr(obj.ViewObject, "Decimals"):
                self.string = DraftGui.displayExternal(a,
                                                       obj.ViewObject.Decimals,
                                                       'Angle', su)
            else:
                self.string = DraftGui.displayExternal(a, None, 'Angle', su)
            if obj.ViewObject.Override:
                self.string = obj.ViewObject.Override.replace("$dim",\
                    self.string)
            self.text.string = self.text3d.string = utils.string_encode_coin(
                self.string)

            # check display mode
            try:
                m = obj.ViewObject.DisplayMode
            except:  # swallow all exceptions here since it always fails on first run (Displaymode enum no set yet)
                m = ["2D", "3D"][utils.get_param("dimstyle", 0)]

            # set the arc
            if m == "3D":
                # calculate the spacing of the text
                spacing = (len(self.string) *
                           obj.ViewObject.FontSize.Value) / 8.0
                pts1 = []
                cut = None
                pts2 = []
                for i in range(arcsegs + 1):
                    p = self.circle.valueAt(self.circle.FirstParameter + (
                        (self.circle.LastParameter -
                         self.circle.FirstParameter) / arcsegs) * i)
                    if (p.sub(mp)).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)
                i1 = len(pts1)
                i2 = i1 + len(pts2)
                self.arc.coordIndex.setValues(
                    0,
                    len(pts1) + len(pts2) + 1,
                    list(range(len(pts1))) + [-1] + list(range(i1, i2)))
                if (len(pts1) >= 3) and (len(pts2) >= 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(self.circle.FirstParameter + (
                        (self.circle.LastParameter -
                         self.circle.FirstParameter) / 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
            self.trans1.translation.setValue((self.p2.x, self.p2.y, self.p2.z))
            self.coord1.point.setValue((self.p2.x, self.p2.y, self.p2.z))
            self.trans2.translation.setValue((self.p3.x, self.p3.y, self.p3.z))
            self.coord2.point.setValue((self.p3.x, self.p3.y, self.p3.z))
            # calculate small chords to make arrows look better
            arrowlength = 4 * obj.ViewObject.ArrowSize.Value
            u1 = (self.circle.valueAt(self.circle.FirstParameter + arrowlength)
                  ).sub(self.circle.valueAt(
                      self.circle.FirstParameter)).normalize()
            u2 = (self.circle.valueAt(self.circle.LastParameter)).sub(
                self.circle.valueAt(self.circle.LastParameter -
                                    arrowlength)).normalize()
            if hasattr(obj.ViewObject, "FlipArrows"):
                if obj.ViewObject.FlipArrows:
                    u1 = u1.negative()
                    u2 = u2.negative()
            w2 = self.circle.Curve.Axis
            w1 = w2.negative()
            v1 = w1.cross(u1)
            v2 = w2.cross(u2)
            q1 = App.Placement(DraftVecUtils.getPlaneRotation(u1, v1,
                                                              w1)).Rotation.Q
            q2 = App.Placement(DraftVecUtils.getPlaneRotation(u2, v2,
                                                              w2)).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]))

            # setting text pos & rot
            self.tbase = mp
            if hasattr(obj.ViewObject, "TextPosition"):
                if not DraftVecUtils.isNull(obj.ViewObject.TextPosition):
                    self.tbase = obj.ViewObject.TextPosition

            u3 = ray.cross(norm).normalize()
            v3 = norm.cross(u3)
            r = App.Placement(DraftVecUtils.getPlaneRotation(u3, v3,
                                                             norm)).Rotation
            offset = r.multVec(App.Vector(0, 1, 0))

            if hasattr(obj.ViewObject, "TextSpacing"):
                offset = DraftVecUtils.scaleTo(
                    offset, obj.ViewObject.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
            if round(obj.Angle, utils.precision()) != round(
                    a, utils.precision()):
                obj.Angle = a
Ejemplo n.º 2
0
    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
Ejemplo n.º 3
0
    def updateData(self, obj, prop):
        """called when the base object is changed"""
        import DraftGui
        if prop in ["Start", "End", "Dimline", "Direction"]:

            if obj.Start == obj.End:
                return

            if not hasattr(self, "node"):
                return

            import Part, DraftGeomUtils
            from pivy import coin

            # calculate the 4 points
            self.p1 = obj.Start
            self.p4 = obj.End
            base = None
            if hasattr(obj, "Direction"):
                if not DraftVecUtils.isNull(obj.Direction):
                    v2 = self.p1.sub(obj.Dimline)
                    v3 = self.p4.sub(obj.Dimline)
                    v2 = DraftVecUtils.project(v2, obj.Direction)
                    v3 = DraftVecUtils.project(v3, obj.Direction)
                    self.p2 = obj.Dimline.add(v2)
                    self.p3 = obj.Dimline.add(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.add(proj.negative())
                    self.p3 = self.p4.add(proj.negative())
                else:
                    self.p2 = self.p1
                    self.p3 = self.p4
            if proj:
                if hasattr(obj.ViewObject, "ExtLines") and hasattr(
                        obj.ViewObject, "ScaleMultiplier"):
                    dmax = obj.ViewObject.ExtLines.Value * obj.ViewObject.ScaleMultiplier
                    if dmax and (proj.Length > dmax):
                        if (dmax > 0):
                            self.p1 = self.p2.add(
                                DraftVecUtils.scaleTo(proj, dmax))
                            self.p4 = self.p3.add(
                                DraftVecUtils.scaleTo(proj, dmax))
                        else:
                            rest = proj.Length + dmax
                            self.p1 = self.p2.add(
                                DraftVecUtils.scaleTo(proj, rest))
                            self.p4 = self.p3.add(
                                DraftVecUtils.scaleTo(proj, rest))
            else:
                proj = (self.p3.sub(self.p2)).cross(App.Vector(0, 0, 1))

            # calculate the arrows positions
            self.trans1.translation.setValue((self.p2.x, self.p2.y, self.p2.z))
            self.coord1.point.setValue((self.p2.x, self.p2.y, self.p2.z))
            self.trans2.translation.setValue((self.p3.x, self.p3.y, self.p3.z))
            self.coord2.point.setValue((self.p3.x, self.p3.y, self.p3.z))

            # calculate dimension and extension lines overshoots positions
            self.transDimOvershoot1.translation.setValue(
                (self.p2.x, self.p2.y, self.p2.z))
            self.transDimOvershoot2.translation.setValue(
                (self.p3.x, self.p3.y, self.p3.z))
            self.transExtOvershoot1.translation.setValue(
                (self.p2.x, self.p2.y, self.p2.z))
            self.transExtOvershoot2.translation.setValue(
                (self.p3.x, self.p3.y, self.p3.z))

            # calculate the text position and orientation
            if hasattr(obj, "Normal"):
                if DraftVecUtils.isNull(obj.Normal):
                    if proj:
                        norm = (self.p3.sub(self.p2).cross(proj)).negative()
                    else:
                        norm = App.Vector(0, 0, 1)
                else:
                    norm = App.Vector(obj.Normal)
            else:
                if proj:
                    norm = (self.p3.sub(self.p2).cross(proj)).negative()
                else:
                    norm = App.Vector(0, 0, 1)
            if not DraftVecUtils.isNull(norm):
                norm.normalize()
            u = self.p3.sub(self.p2)
            u.normalize()
            v1 = norm.cross(u)
            rot1 = App.Placement(DraftVecUtils.getPlaneRotation(
                u, v1, norm)).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(obj.ViewObject, "FlipArrows"):
                if obj.ViewObject.FlipArrows:
                    u = u.negative()
            v2 = norm.cross(u)
            rot2 = App.Placement(DraftVecUtils.getPlaneRotation(
                u, v2, norm)).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.sub(self.p2)
                u3.normalize()
                v3 = norm.cross(u3)
                rot3 = App.Placement(
                    DraftVecUtils.getPlaneRotation(u3, v3, norm)).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]))
            if hasattr(obj.ViewObject, "TextSpacing") and hasattr(
                    obj.ViewObject, "ScaleMultiplier"):
                ts = obj.ViewObject.TextSpacing.Value * obj.ViewObject.ScaleMultiplier
                offset = DraftVecUtils.scaleTo(v1, ts)
            else:
                offset = DraftVecUtils.scaleTo(v1, 0.05)
            rott = rot1
            if hasattr(obj.ViewObject, "FlipText"):
                if obj.ViewObject.FlipText:
                    rott = App.Rotation(*rott).multiply(App.Rotation(
                        norm, 180)).Q
                    offset = offset.negative()
            # setting text
            try:
                m = obj.ViewObject.DisplayMode
            except:  # swallow all exceptions here since it always fails on first run (Displaymode enum no set yet)
                m = ["2D", "3D"][utils.get_param("dimstyle", 0)]
            if m == "3D":
                offset = offset.negative()
            self.tbase = (self.p2.add(
                (self.p3.sub(self.p2).multiply(0.5)))).add(offset)
            if hasattr(obj.ViewObject, "TextPosition"):
                if not DraftVecUtils.isNull(obj.ViewObject.TextPosition):
                    self.tbase = obj.ViewObject.TextPosition
            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])
            su = True
            if hasattr(obj.ViewObject, "ShowUnit"):
                su = obj.ViewObject.ShowUnit
            # set text value
            l = self.p3.sub(self.p2).Length
            unit = None
            if hasattr(obj.ViewObject, "UnitOverride"):
                unit = obj.ViewObject.UnitOverride
            # special representation if "Building US" scheme
            if App.ParamGet("User parameter:BaseApp/Preferences/Units").GetInt(
                    "UserSchema", 0) == 5:
                s = App.Units.Quantity(l, App.Units.Length).UserString
                self.string = s.replace("' ", "'- ")
                self.string = s.replace("+", " ")
            elif hasattr(obj.ViewObject, "Decimals"):
                self.string = DraftGui.displayExternal(l,
                                                       obj.ViewObject.Decimals,
                                                       'Length', su, unit)
            else:
                self.string = DraftGui.displayExternal(l, None, 'Length', su,
                                                       unit)
            if hasattr(obj.ViewObject, "Override"):
                if obj.ViewObject.Override:
                    self.string = obj.ViewObject.Override.replace("$dim",\
                            self.string)
            self.text.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) *
                            obj.ViewObject.FontSize.Value) / 4.0
                spacing = ((self.p3.sub(self.p2)).Length / 2.0) - textsize
                self.p2a = self.p2.add(
                    DraftVecUtils.scaleTo(self.p3.sub(self.p2), spacing))
                self.p2b = self.p3.add(
                    DraftVecUtils.scaleTo(self.p2.sub(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))
Ejemplo n.º 4
0
    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))