From a89eda2428c41dce44c8c57ec90a6cff7988fb65 Mon Sep 17 00:00:00 2001 From: vocx-fc Date: Tue, 16 Jun 2020 16:48:43 -0500 Subject: [PATCH] Draft: clean up ViewProviderLinearDimension class This class handles linear dimensions of edges, or radial dimensions of arcs and circles. Various improvements in style, PEP8, spacing, docstrings. In particular use more readable variable names. The modules `Part` and `DraftGeomUtils` have been delayed in import. The `updataData` method is modified to return immediately if the Proxy property modified is different from `Start`, `End`, `Dimline` or `Direction`. --- .../draftviewproviders/view_dimension.py | 669 +++++++++++------- 1 file changed, 394 insertions(+), 275 deletions(-) diff --git a/src/Mod/Draft/draftviewproviders/view_dimension.py b/src/Mod/Draft/draftviewproviders/view_dimension.py index af2d42f427..0646b33cb9 100644 --- a/src/Mod/Draft/draftviewproviders/view_dimension.py +++ b/src/Mod/Draft/draftviewproviders/view_dimension.py @@ -32,17 +32,22 @@ They inherit their behavior from the base Annotation viewprovider. # \brief Provides the Draft Dimension viewprovider classes. import pivy.coin as coin +import lazy_loader.lazy_loader as lz from PySide.QtCore import QT_TRANSLATE_NOOP import FreeCAD as App import DraftVecUtils -import DraftGeomUtils +import draftutils.units as units import draftutils.utils as utils import draftutils.gui_utils as gui_utils from draftviewproviders.view_draft_annotation \ import ViewProviderDraftAnnotation +# Delay import of module until first use because it is heavy +Part = lz.LazyLoader("Part", globals(), "Part") +DraftGeomUtils = lz.LazyLoader("DraftGeomUtils", globals(), "DraftGeomUtils") + class ViewProviderDimensionBase(ViewProviderDraftAnnotation): """The base viewprovider for the Draft Dimensions object. @@ -330,9 +335,12 @@ class ViewProviderDimensionBase(ViewProviderDraftAnnotation): class ViewProviderLinearDimension(ViewProviderDimensionBase): + """The viewprovider for the Linear Dimension objects. + + This includes straight edge measurement, as well as measurement + of circular edges, and circumferences. """ - A View Provider for the Draft Linear Dimension object - """ + def __init__(self, vobj): super(ViewProviderLinearDimension, self).__init__(vobj) super(ViewProviderLinearDimension, self).set_properties(vobj) @@ -340,29 +348,36 @@ class ViewProviderLinearDimension(ViewProviderDimensionBase): self.Object = vobj.Object vobj.Proxy = self - def attach(self, vobj): - '''Setup the scene sub-graph of the view provider''' + """Set up the scene sub-graph of the viewprovider.""" self.Object = vobj.Object + self.color = coin.SoBaseColor() self.font = coin.SoFont() self.font3d = coin.SoFont() - self.text = coin.SoAsciiText() - self.text3d = coin.SoText2() - self.text.string = "d" # some versions of coin crash if string is not set + self.text = coin.SoAsciiText() # Can be oriented in 3D space + self.text3d = coin.SoText2() # Faces the camera always + + # The text string needs to be initialized to something, + # otherwise it may cause a crash of the system + self.text.string = "d" self.text3d.string = "d" self.textpos = coin.SoTransform() - self.text.justification = self.text3d.justification = coin.SoAsciiText.CENTER + self.text.justification = coin.SoAsciiText.CENTER + self.text3d.justification = coin.SoAsciiText.CENTER + label = coin.SoSeparator() label.addChild(self.textpos) label.addChild(self.color) label.addChild(self.font) label.addChild(self.text) + label3d = coin.SoSeparator() label3d.addChild(self.textpos) label3d.addChild(self.color) label3d.addChild(self.font3d) label3d.addChild(self.text3d) + self.coord1 = coin.SoCoordinate3() self.trans1 = coin.SoTransform() self.coord2 = coin.SoCoordinate3() @@ -371,12 +386,14 @@ class ViewProviderLinearDimension(ViewProviderDimensionBase): self.transDimOvershoot2 = coin.SoTransform() self.transExtOvershoot1 = coin.SoTransform() self.transExtOvershoot2 = coin.SoTransform() + self.marks = coin.SoSeparator() self.marksDimOvershoot = coin.SoSeparator() self.marksExtOvershoot = coin.SoSeparator() self.drawstyle = coin.SoDrawStyle() self.line = coin.SoType.fromName("SoBrepEdgeSet").createInstance() self.coords = coin.SoCoordinate3() + self.node = coin.SoGroup() self.node.addChild(self.color) self.node.addChild(self.drawstyle) @@ -389,6 +406,7 @@ class ViewProviderLinearDimension(ViewProviderDimensionBase): self.lineswitch2.addChild(self.marksDimOvershoot) self.lineswitch2.addChild(self.marksExtOvershoot) self.node.addChild(label) + self.node3d = coin.SoGroup() self.node3d.addChild(self.color) self.node3d.addChild(self.drawstyle) @@ -401,254 +419,346 @@ class ViewProviderLinearDimension(ViewProviderDimensionBase): self.lineswitch3.addChild(self.marksDimOvershoot) self.lineswitch3.addChild(self.marksExtOvershoot) self.node3d.addChild(label3d) - vobj.addDisplayMode(self.node,"2D") - vobj.addDisplayMode(self.node3d,"3D") - self.updateData(vobj.Object,"Start") - self.onChanged(vobj,"FontSize") - self.onChanged(vobj,"FontName") - self.onChanged(vobj,"ArrowType") - self.onChanged(vobj,"LineColor") - self.onChanged(vobj,"DimOvershoot") - self.onChanged(vobj,"ExtOvershoot") + + vobj.addDisplayMode(self.node, "2D") + vobj.addDisplayMode(self.node3d, "3D") + self.updateData(vobj.Object, "Start") + self.onChanged(vobj, "FontSize") + self.onChanged(vobj, "FontName") + self.onChanged(vobj, "ArrowType") + self.onChanged(vobj, "LineColor") + self.onChanged(vobj, "DimOvershoot") + self.onChanged(vobj, "ExtOvershoot") def updateData(self, obj, prop): - """called when the base object is changed""" - import DraftGui - if prop in ["Start","End","Dimline","Direction"]: + """Execute when a property from the Proxy class is changed. - if obj.Start == obj.End: - return + It only runs if `Start`, `End`, `Dimline`, or `Direction` changed. + """ + if prop not in ("Start", "End", "Dimline", "Direction"): + return - if not hasattr(self,"node"): - return + if obj.Start == obj.End: + return - import Part, DraftGeomUtils - from pivy import coin + if not hasattr(self, "node"): + return - # 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) + 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: - self.p2 = self.p1.add(proj.negative()) - self.p3 = self.p4.add(proj.negative()) - else: - self.p2 = self.p1 - self.p3 = self.p4 + 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: - 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)) + self.p2 = self.p1 + proj.negative() + self.p3 = self.p4 + proj.negative() else: - proj = (self.p3.sub(self.p2)).cross(App.Vector(0,0,1)) + self.p2 = self.p1 + self.p3 = self.p4 - # 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() + 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: - 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) + 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)) - # 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)) + # 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)) def onChanged(self, vobj, prop): """Execute when a view property is changed.""" super(ViewProviderLinearDimension, self).onChanged(vobj, prop) - if prop == "ScaleMultiplier" and hasattr(vobj, "ScaleMultiplier"): - # update all dimension values - if hasattr(self,"font"): + obj = vobj.Object + properties = vobj.PropertiesList + + if prop == "ScaleMultiplier" and "ScaleMultiplier" in properties: + # Update all dimension values + if hasattr(self, "font"): self.font.size = vobj.FontSize.Value * vobj.ScaleMultiplier - if hasattr(self,"font3d"): - self.font3d.size = vobj.FontSize.Value * 100 * vobj.ScaleMultiplier - if hasattr(self,"node") and hasattr(self,"p2") and hasattr(vobj,"ArrowSize"): + if hasattr(self, "font3d"): + self.font3d.size = \ + vobj.FontSize.Value * 10 * vobj.ScaleMultiplier + if (hasattr(self, "node") and hasattr(self, "p2") + and "ArrowSize" in properties): self.remove_dim_arrows() self.draw_dim_arrows(vobj) - if hasattr(vobj,"DimOvershoot"): + if "DimOvershoot" in properties: self.remove_dim_overshoot() self.draw_dim_overshoot(vobj) - if hasattr(vobj,"ExtOvershoot"): + if "ExtOvershoot" in properties: self.remove_ext_overshoot() self.draw_ext_overshoot(vobj) - self.updateData(vobj.Object,"Start") - vobj.Object.touch() - - elif (prop == "FontSize") and hasattr(vobj,"FontSize") and hasattr(vobj, "ScaleMultiplier"): - if hasattr(self,"font"): + + self.updateData(obj, "Start") + obj.touch() + + elif (prop == "FontSize" and "FontSize" in properties + and "ScaleMultiplier" in properties): + if hasattr(self, "font"): self.font.size = vobj.FontSize.Value * vobj.ScaleMultiplier - if hasattr(self,"font3d"): - self.font3d.size = vobj.FontSize.Value * 100 * vobj.ScaleMultiplier - vobj.Object.touch() - - elif (prop == "FontName") and hasattr(vobj,"FontName"): - if hasattr(self,"font") and hasattr(self,"font3d"): - self.font.name = self.font3d.name = str(vobj.FontName) - vobj.Object.touch() - - elif (prop == "LineColor") and hasattr(vobj,"LineColor"): - if hasattr(self,"color"): - c = vobj.LineColor - self.color.rgb.setValue(c[0],c[1],c[2]) - - elif (prop == "LineWidth") and hasattr(vobj,"LineWidth"): - if hasattr(self,"drawstyle"): - self.drawstyle.lineWidth = vobj.LineWidth - - elif (prop in ["ArrowSize","ArrowType"]) and hasattr(vobj,"ArrowSize") and hasattr(vobj, "ScaleMultiplier"): - if hasattr(self,"node") and hasattr(self,"p2"): - self.remove_dim_arrows() - self.draw_dim_arrows(vobj) - vobj.Object.touch() - - elif (prop == "DimOvershoot") and hasattr(vobj,"DimOvershoot") and hasattr(vobj, "ScaleMultiplier"): + if hasattr(self, "font3d"): + self.font3d.size = \ + vobj.FontSize.Value * 10 * vobj.ScaleMultiplier + obj.touch() + + elif (prop == "FontName" and "FontName" in properties + and hasattr(self, "font") and hasattr(self, "font3d")): + self.font.name = str(vobj.FontName) + self.font3d.name = str(vobj.FontName) + obj.touch() + + elif (prop == "LineColor" and "LineColor" in properties + and hasattr(self, "color")): + col = vobj.LineColor + self.color.rgb.setValue(col[0], col[1], col[2]) + + elif (prop == "LineWidth" and "LineWidth" in properties + and hasattr(self, "drawstyle")): + self.drawstyle.lineWidth = vobj.LineWidth + + elif (prop in ("ArrowSize", "ArrowType") + and "ArrowSize" in properties + and "ScaleMultiplier" in properties + and hasattr(self, "node") and hasattr(self, "p2")): + self.remove_dim_arrows() + self.draw_dim_arrows(vobj) + obj.touch() + + elif (prop == "DimOvershoot" + and "DimOvershoot" in properties + and "ScaleMultiplier" in properties): self.remove_dim_overshoot() self.draw_dim_overshoot(vobj) - vobj.Object.touch() - - elif (prop == "ExtOvershoot") and hasattr(vobj,"ExtOvershoot") and hasattr(vobj, "ScaleMultiplier"): + obj.touch() + + elif (prop == "ExtOvershoot" + and "ExtOvershoot" in properties + and "ScaleMultiplier" in properties): self.remove_ext_overshoot() self.draw_ext_overshoot(vobj) - vobj.Object.touch() - - elif (prop == "ShowLine") and hasattr(vobj,"ShowLine"): + obj.touch() + + elif prop == "ShowLine" and "ShowLine" in properties: if vobj.ShowLine: self.lineswitch2.whichChild = -3 self.lineswitch3.whichChild = -3 @@ -656,17 +766,19 @@ class ViewProviderLinearDimension(ViewProviderDimensionBase): self.lineswitch2.whichChild = -1 self.lineswitch3.whichChild = -1 else: - self.updateData(vobj.Object,"Start") - + self.updateData(obj, "Start") + def remove_dim_arrows(self): - # remove existing nodes + """Remove dimension arrows in the dimension lines. + + Remove the existing nodes. + """ self.node.removeChild(self.marks) self.node3d.removeChild(self.marks) def draw_dim_arrows(self, vobj): - from pivy import coin - - if not hasattr(vobj,"ArrowType"): + """Draw dimension arrows.""" + if not hasattr(vobj, "ArrowType"): return if self.p3.x < self.p2.x: @@ -674,105 +786,112 @@ class ViewProviderLinearDimension(ViewProviderDimensionBase): else: inv = True - # set scale + # Set scale symbol = utils.ARROW_TYPES.index(vobj.ArrowType) s = vobj.ArrowSize.Value * vobj.ScaleMultiplier - self.trans1.scaleFactor.setValue((s,s,s)) - self.trans2.scaleFactor.setValue((s,s,s)) + self.trans1.scaleFactor.setValue((s, s, s)) + self.trans2.scaleFactor.setValue((s, s, s)) - - # set new nodes + # Set new nodes self.marks = coin.SoSeparator() self.marks.addChild(self.color) + s1 = coin.SoSeparator() if symbol == "Circle": s1.addChild(self.coord1) else: s1.addChild(self.trans1) - s1.addChild(gui_utils.dim_symbol(symbol,invert=not(inv))) + + s1.addChild(gui_utils.dim_symbol(symbol, invert=not inv)) self.marks.addChild(s1) + s2 = coin.SoSeparator() if symbol == "Circle": s2.addChild(self.coord2) else: s2.addChild(self.trans2) - s2.addChild(gui_utils.dim_symbol(symbol,invert=inv)) + + s2.addChild(gui_utils.dim_symbol(symbol, invert=inv)) self.marks.addChild(s2) - self.node.insertChild(self.marks,2) - self.node3d.insertChild(self.marks,2) + + self.node.insertChild(self.marks, 2) + self.node3d.insertChild(self.marks, 2) def remove_dim_overshoot(self): + """Remove the dimension overshoot lines.""" self.node.removeChild(self.marksDimOvershoot) self.node3d.removeChild(self.marksDimOvershoot) - def draw_dim_overshoot(self, vobj): - from pivy import coin - - # set scale + """Draw dimension overshoot lines.""" + # Set scale s = vobj.DimOvershoot.Value * vobj.ScaleMultiplier - self.transDimOvershoot1.scaleFactor.setValue((s,s,s)) - self.transDimOvershoot2.scaleFactor.setValue((s,s,s)) + self.transDimOvershoot1.scaleFactor.setValue((s, s, s)) + self.transDimOvershoot2.scaleFactor.setValue((s, s, s)) - # remove existing nodes - - # set new nodes + # Remove existing nodes, and set new nodes self.marksDimOvershoot = coin.SoSeparator() if vobj.DimOvershoot.Value: self.marksDimOvershoot.addChild(self.color) + s1 = coin.SoSeparator() s1.addChild(self.transDimOvershoot1) - s1.addChild(gui_utils.dimDash((-1,0,0),(0,0,0))) + s1.addChild(gui_utils.dimDash((-1, 0, 0), (0, 0, 0))) self.marksDimOvershoot.addChild(s1) + s2 = coin.SoSeparator() s2.addChild(self.transDimOvershoot2) - s2.addChild(gui_utils.dimDash((0,0,0),(1,0,0))) + s2.addChild(gui_utils.dimDash((0, 0, 0), (1, 0, 0))) self.marksDimOvershoot.addChild(s2) - self.node.insertChild(self.marksDimOvershoot,2) - self.node3d.insertChild(self.marksDimOvershoot,2) - + self.node.insertChild(self.marksDimOvershoot, 2) + self.node3d.insertChild(self.marksDimOvershoot, 2) + def remove_ext_overshoot(self): + """Remove dimension extension overshoot lines.""" self.node.removeChild(self.marksExtOvershoot) self.node3d.removeChild(self.marksExtOvershoot) - def draw_ext_overshoot(self, vobj): - from pivy import coin - - # set scale + """Draw dimension extension overshoot lines.""" + # Set scale s = vobj.ExtOvershoot.Value * vobj.ScaleMultiplier - self.transExtOvershoot1.scaleFactor.setValue((s,s,s)) - self.transExtOvershoot2.scaleFactor.setValue((s,s,s)) + self.transExtOvershoot1.scaleFactor.setValue((s, s, s)) + self.transExtOvershoot2.scaleFactor.setValue((s, s, s)) - # set new nodes + # Set new nodes self.marksExtOvershoot = coin.SoSeparator() if vobj.ExtOvershoot.Value: self.marksExtOvershoot.addChild(self.color) s1 = coin.SoSeparator() s1.addChild(self.transExtOvershoot1) - s1.addChild(gui_utils.dimDash((0,0,0),(-1,0,0))) + s1.addChild(gui_utils.dimDash((0, 0, 0), (-1, 0, 0))) self.marksExtOvershoot.addChild(s1) + s2 = coin.SoSeparator() s2.addChild(self.transExtOvershoot2) - s2.addChild(gui_utils.dimDash((0,0,0),(-1,0,0))) + s2.addChild(gui_utils.dimDash((0, 0, 0), (-1, 0, 0))) self.marksExtOvershoot.addChild(s2) - self.node.insertChild(self.marksExtOvershoot,2) - self.node3d.insertChild(self.marksExtOvershoot,2) + + self.node.insertChild(self.marksExtOvershoot, 2) + self.node3d.insertChild(self.marksExtOvershoot, 2) def is_linked_to_circle(self): - _obj = self.Object - if _obj.LinkedGeometry and len(_obj.LinkedGeometry) == 1: - lobj = _obj.LinkedGeometry[0][0] - lsub = _obj.LinkedGeometry[0][1] - if len(lsub) == 1 and "Edge" in lsub[0]: - n = int(lsub[0][4:]) - 1 - edge = lobj.Shape.Edges[n] + """Return true if the dimension measures a circular edge.""" + obj = self.Object + if obj.LinkedGeometry and len(obj.LinkedGeometry) == 1: + linked_obj = obj.LinkedGeometry[0][0] + subelements = obj.LinkedGeometry[0][1] + if len(subelements) == 1 and "Edge" in subelements[0]: + sub = subelements[0] + index = int(sub[4:]) - 1 + edge = linked_obj.Shape.Edges[index] if DraftGeomUtils.geomType(edge) == "Circle": return True return False def getIcon(self): + """Return the path to the icon used by the viewprovider.""" if self.is_linked_to_circle(): return ":/icons/Draft_DimensionRadius.svg" return ":/icons/Draft_Dimension_Tree.svg"