| |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| """Provides the viewprovider code for the Dimension objects. |
| |
| These include linear dimensions, including radius and diameter, |
| as well as angular dimensions. |
| They inherit their behavior from the base Annotation viewprovider. |
| """ |
| |
| |
| |
|
|
| import math |
| 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 |
| from draftutils import gui_utils |
| from draftutils import params |
| from draftutils import units |
| from draftutils import utils |
| from draftviewproviders.view_draft_annotation import ViewProviderDraftAnnotation |
|
|
| |
| Part = lz.LazyLoader("Part", globals(), "Part") |
| DraftGeomUtils = lz.LazyLoader("DraftGeomUtils", globals(), "DraftGeomUtils") |
|
|
| |
| |
|
|
|
|
| class ViewProviderDimensionBase(ViewProviderDraftAnnotation): |
| """The base viewprovider for the Draft Dimensions object. |
| |
| This class is not used directly, but inherited by dimension |
| viewproviders like linear, radial, and angular. |
| |
| Dimension nomeclature |
| --------------------- |
| The dimension object depends on various variables to draw the lines |
| that are drawn on 3D view. |
| :: |
| | txt | e |
| ----b---------a----------------------b---- |
| | | |
| | | d |
| | | |
| |
| c (t) (t) c |
| |
| From the object class |
| |
| * `a`, `Dimline`, point through which the dimension line goes through |
| |
| From the viewprovider class |
| |
| * `b`, `ArrowTypeStart`, `ArrowTypeEnd`, `ArrowSizeStart`, `ArrowSizeEnd`, the symbol shown on the endpoints |
| * `c`, `DimOvershoot`, extension to the dimension line going through `a` |
| * `d`, `ExtLines`, distance to target `(t)` |
| * `e`, `ExtOvershoot`, extension in the opposite direction to `(t)` |
| * `txt`, text label showing the value of the measurement |
| |
| Coin object structure |
| --------------------- |
| The scenegraph is set from two main nodes. |
| :: |
| vobj.node_wld.linecolor |
| .drawstyle |
| .lineswitch_wld.coords |
| .line |
| .marks |
| .marksDimOvershoot |
| .marksExtOvershoot |
| .label_wld.textpos |
| .textcolor |
| .font |
| .text_wld |
| |
| vobj.node_scr.linecolor |
| .drawstyle |
| .lineswitch_scr.coords |
| .line |
| .marks |
| .marksDimOvershoot |
| .marksExtOvershoot |
| .label_scr.textpos |
| .textcolor |
| .font |
| .text_scr |
| """ |
|
|
| def set_text_properties(self, vobj, properties): |
| """Set text properties only if they don't already exist.""" |
| super().set_text_properties(vobj, properties) |
|
|
| if "TextSpacing" not in properties: |
| _tip = QT_TRANSLATE_NOOP("App::Property", "Spacing between text and dimension line") |
| vobj.addProperty("App::PropertyLength", "TextSpacing", "Text", _tip, locked=True) |
| vobj.TextSpacing = params.get_param("dimspacing") |
|
|
| if "FlipText" not in properties: |
| _tip = QT_TRANSLATE_NOOP("App::Property", "Rotate the dimension text 180 degrees") |
| vobj.addProperty("App::PropertyBool", "FlipText", "Text", _tip, locked=True) |
| vobj.FlipText = False |
|
|
| if "TextPosition" not in properties: |
| _tip = QT_TRANSLATE_NOOP( |
| "App::Property", "Text Position.\n" "Leave '(0,0,0)' for automatic position" |
| ) |
| vobj.addProperty( |
| "App::PropertyVectorDistance", "TextPosition", "Text", _tip, locked=True |
| ) |
| vobj.TextPosition = App.Vector(0, 0, 0) |
|
|
| if "Override" not in properties: |
| _tip = QT_TRANSLATE_NOOP( |
| "App::Property", |
| "Text override.\n" |
| "Write '$dim' so that it is replaced by " |
| "the dimension length.", |
| ) |
| vobj.addProperty("App::PropertyString", "Override", "Text", _tip, locked=True) |
| vobj.Override = "" |
|
|
| def set_units_properties(self, vobj, properties): |
| """Set unit properties only if they don't already exist.""" |
| super().set_units_properties(vobj, properties) |
|
|
| if "Decimals" not in properties: |
| _tip = QT_TRANSLATE_NOOP("App::Property", "The number of decimals to show") |
| vobj.addProperty("App::PropertyInteger", "Decimals", "Units", _tip, locked=True) |
| vobj.Decimals = params.get_param("dimPrecision") |
|
|
| if "ShowUnit" not in properties: |
| _tip = QT_TRANSLATE_NOOP("App::Property", "Show the unit suffix") |
| vobj.addProperty("App::PropertyBool", "ShowUnit", "Units", _tip, locked=True) |
| vobj.ShowUnit = params.get_param("showUnit") |
|
|
| if "UnitOverride" not in properties: |
| _tip = QT_TRANSLATE_NOOP( |
| "App::Property", |
| "A unit to express the measurement.\n" |
| "Leave blank for system default.\n" |
| "Use 'arch' to force US arch notation", |
| ) |
| vobj.addProperty("App::PropertyString", "UnitOverride", "Units", _tip, locked=True) |
| vobj.UnitOverride = params.get_param("overrideUnit") |
|
|
| def set_graphics_properties(self, vobj, properties): |
| """Set graphics properties only if they don't already exist.""" |
| super().set_graphics_properties(vobj, properties) |
|
|
| if "FlipArrows" not in properties: |
| _tip = QT_TRANSLATE_NOOP("App::Property", "Rotate the dimension arrows 180 degrees") |
| vobj.addProperty("App::PropertyBool", "FlipArrows", "Graphics", _tip, locked=True) |
| vobj.FlipArrows = False |
|
|
| if "DimOvershoot" not in properties: |
| _tip = QT_TRANSLATE_NOOP( |
| "App::Property", |
| "The distance the dimension line " "is extended\n" "past the extension lines", |
| ) |
| vobj.addProperty("App::PropertyDistance", "DimOvershoot", "Graphics", _tip, locked=True) |
| vobj.DimOvershoot = params.get_param("dimovershoot") |
|
|
| if "ExtLines" not in properties: |
| _tip = QT_TRANSLATE_NOOP("App::Property", "Length of the extension lines") |
| vobj.addProperty("App::PropertyDistance", "ExtLines", "Graphics", _tip, locked=True) |
| vobj.ExtLines = params.get_param("extlines") |
|
|
| if "ExtOvershoot" not in properties: |
| _tip = QT_TRANSLATE_NOOP( |
| "App::Property", "Length of the extension line\n" "beyond the dimension line" |
| ) |
| vobj.addProperty("App::PropertyDistance", "ExtOvershoot", "Graphics", _tip, locked=True) |
| vobj.ExtOvershoot = params.get_param("extovershoot") |
|
|
| if "ShowLine" not in properties: |
| _tip = QT_TRANSLATE_NOOP("App::Property", "Shows the dimension line and arrows") |
| vobj.addProperty("App::PropertyBool", "ShowLine", "Graphics", _tip, locked=True) |
| vobj.ShowLine = params.get_param("DimShowLine") |
|
|
| def getIcon(self): |
| """Return the path to the icon used by the viewprovider.""" |
| return ":/icons/Draft_Dimension_Tree.svg" |
|
|
|
|
| class ViewProviderLinearDimension(ViewProviderDimensionBase): |
| """The viewprovider for the Linear Dimension objects. |
| |
| This includes straight edge measurement, as well as measurement |
| of circular edges, and circumferences. |
| """ |
|
|
| def set_graphics_properties(self, vobj, properties): |
| """Set graphics properties only if they don't already exist.""" |
| super().set_graphics_properties(vobj, properties) |
|
|
| self.Object = vobj.Object |
| if vobj.Object.Diameter or not self.is_linked_to_circle(): |
| vobj.ArrowTypeStart = params.get_param("dimsymbolstart") |
| else: |
| vobj.ArrowTypeStart = "None" |
| vobj.ArrowTypeEnd = params.get_param("dimsymbolend") |
|
|
| def attach(self, vobj): |
| """Set up the scene sub-graph of the viewprovider.""" |
| self.Object = vobj.Object |
|
|
| self.textpos = coin.SoTransform() |
| self.textcolor = coin.SoBaseColor() |
| self.font = coin.SoFont() |
| self.text_wld = coin.SoAsciiText() |
| self.text_scr = coin.SoText2() |
|
|
| |
| |
| self.text_wld.string = "d" |
| self.text_scr.string = "d" |
| self.text_wld.justification = coin.SoAsciiText.CENTER |
| self.text_scr.justification = coin.SoAsciiText.CENTER |
|
|
| label_wld = coin.SoSeparator() |
| label_wld.addChild(self.textpos) |
| label_wld.addChild(self.textcolor) |
| label_wld.addChild(self.font) |
| label_wld.addChild(self.text_wld) |
|
|
| label_scr = coin.SoSeparator() |
| label_scr.addChild(self.textpos) |
| label_scr.addChild(self.textcolor) |
| label_scr.addChild(self.font) |
| label_scr.addChild(self.text_scr) |
|
|
| self.coord1 = coin.SoCoordinate3() |
| self.trans1 = coin.SoTransform() |
| self.coord2 = coin.SoCoordinate3() |
| self.trans2 = coin.SoTransform() |
| self.transDimOvershoot1 = coin.SoTransform() |
| self.transDimOvershoot2 = coin.SoTransform() |
| self.transExtOvershoot1 = coin.SoTransform() |
| self.transExtOvershoot2 = coin.SoTransform() |
|
|
| self.linecolor = coin.SoBaseColor() |
| self.drawstyle = coin.SoDrawStyle() |
| self.coords = coin.SoCoordinate3() |
| import PartGui |
|
|
| self.line = coin.SoType.fromName("SoBrepEdgeSet").createInstance() |
| self.marks = coin.SoSeparator() |
| self.marksDimOvershoot = coin.SoSeparator() |
| self.marksExtOvershoot = coin.SoSeparator() |
|
|
| self.node_wld = coin.SoGroup() |
| self.node_wld.addChild(self.linecolor) |
| self.node_wld.addChild(self.drawstyle) |
| self.lineswitch_wld = coin.SoSwitch() |
| self.lineswitch_wld.whichChild = -3 |
| self.node_wld.addChild(self.lineswitch_wld) |
| self.lineswitch_wld.addChild(self.coords) |
| self.lineswitch_wld.addChild(self.line) |
| self.lineswitch_wld.addChild(self.marks) |
| self.lineswitch_wld.addChild(self.marksDimOvershoot) |
| self.lineswitch_wld.addChild(self.marksExtOvershoot) |
| self.node_wld.addChild(label_wld) |
|
|
| self.node_scr = coin.SoGroup() |
| self.node_scr.addChild(self.linecolor) |
| self.node_scr.addChild(self.drawstyle) |
| self.lineswitch_scr = coin.SoSwitch() |
| self.lineswitch_scr.whichChild = -3 |
| self.node_scr.addChild(self.lineswitch_scr) |
| self.lineswitch_scr.addChild(self.coords) |
| self.lineswitch_scr.addChild(self.line) |
| self.lineswitch_scr.addChild(self.marks) |
| self.lineswitch_scr.addChild(self.marksDimOvershoot) |
| self.lineswitch_scr.addChild(self.marksExtOvershoot) |
| self.node_scr.addChild(label_scr) |
|
|
| vobj.addDisplayMode(self.node_wld, "World") |
| vobj.addDisplayMode(self.node_scr, "Screen") |
| self.updateData(vobj.Object, "Start") |
| self.onChanged(vobj, "FontSize") |
| self.onChanged(vobj, "FontName") |
| self.onChanged(vobj, "TextColor") |
| self.onChanged(vobj, "ArrowTypeStart") |
| self.onChanged(vobj, "ArrowTypeEnd") |
| self.onChanged(vobj, "LineColor") |
| self.onChanged(vobj, "DimOvershoot") |
| self.onChanged(vobj, "ExtOvershoot") |
| self.onChanged(vobj, "ShowLine") |
| self.onChanged(vobj, "LineWidth") |
|
|
| 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", "Diameter"): |
| return |
|
|
| if obj.Start == obj.End: |
| return |
|
|
| if not hasattr(self, "node_wld"): |
| return |
|
|
| vobj = obj.ViewObject |
|
|
| if prop == "Diameter": |
| if getattr(vobj, "Override", False): |
| if obj.Diameter: |
| vobj.Override = vobj.Override.replace("R $dim", "Ø $dim") |
| else: |
| vobj.Override = vobj.Override.replace("Ø $dim", "R $dim") |
|
|
| self.onChanged(vobj, "ArrowTypeStart") |
| self.onChanged(vobj, "ArrowTypeEnd") |
| return |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| 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"): |
| |
| |
| |
| 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)) |
|
|
| |
| 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) |
|
|
| |
| self.transDimOvershoot1.translation.setValue(p2) |
| self.transDimOvershoot2.translation.setValue(p3) |
| self.transExtOvershoot1.translation.setValue(p2) |
| self.transExtOvershoot2.translation.setValue(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 hasattr(obj, "Normal") and not DraftVecUtils.isNull(obj.Normal): |
| norm = App.Vector(obj.Normal) |
|
|
| if not DraftVecUtils.isNull(norm): |
| norm.normalize() |
|
|
| |
| v1 = norm.cross(u) |
| _plane_rot = DraftVecUtils.getPlaneRotation(u, v1, norm) |
| if _plane_rot is not None: |
| 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])) |
| self.trot = rot1 |
| else: |
| self.trot = (0, 0, 0, 1) |
|
|
| if getattr(vobj, "FlipArrows", False): |
| u = u.negative() |
|
|
| v2 = norm.cross(u) |
| _plane_rot = DraftVecUtils.getPlaneRotation(u, v2) |
| if _plane_rot is not None: |
| 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) |
| if _plane_rot is not None: |
| 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])) |
|
|
| |
| |
| 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) |
|
|
| if getattr(vobj, "FlipText", False): |
| _rott = App.Rotation(self.trot[0], self.trot[1], self.trot[2], self.trot[3]) |
| self.trot = _rott.multiply(App.Rotation(App.Vector(0, 0, 1), 180)).Q |
| offset = offset.negative() |
|
|
| |
| |
| |
| try: |
| m = vobj.DisplayMode |
| except AssertionError: |
| m = ["World", "Screen"][params.get_param("DefaultAnnoDisplayMode")] |
|
|
| if m == "Screen": |
| offset = offset.negative() |
|
|
| |
| |
| |
| if hasattr(vobj, "TextPosition") and not DraftVecUtils.isNull(vobj.TextPosition): |
| self.tbase = vobj.TextPosition |
| else: |
| |
| |
| |
| 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( |
| self.trot[0], self.trot[1], self.trot[2], self.trot[3] |
| ) |
|
|
| show_unit = True |
| if hasattr(vobj, "ShowUnit"): |
| show_unit = vobj.ShowUnit |
|
|
| |
| length = (self.p3 - self.p2).Length |
| unit = None |
|
|
| if hasattr(vobj, "UnitOverride"): |
| unit = vobj.UnitOverride |
|
|
| |
| doc = obj.Document |
| if (not unit and doc.UnitSystem == doc.getEnumerationsOfProperty("UnitSystem")[5]) or ( |
| unit == "arch" |
| ): |
| self.string = App.Units.Quantity(length, App.Units.Length).UserString |
| if self.string.count('"') > 1: |
| |
| self.string = self.string.replace('"', "", self.string.count('"') - 1) |
| sep = params.get_param("FeetSeparator") |
| |
| self.string = self.string.replace("' ", "'" + sep) |
| self.string = self.string.replace("+", " ") |
| self.string = self.string.replace(" ", " ") |
| self.string = self.string.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 getattr(vobj, "Override", False): |
| self.string = vobj.Override.replace("$dim", self.string) |
|
|
| self.text_wld.string = utils.string_encode_coin(self.string) |
| self.text_scr.string = utils.string_encode_coin(self.string) |
|
|
| |
| if m == "Screen": |
| |
| 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.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.coordIndex.setValues(0, 4, (0, 1, 2, 3)) |
|
|
| def onChanged(self, vobj, prop): |
| """Execute when a view property is changed.""" |
| super().onChanged(vobj, prop) |
|
|
| obj = vobj.Object |
| properties = vobj.PropertiesList |
|
|
| if prop == "ScaleMultiplier" and "ScaleMultiplier" in properties: |
| |
| if hasattr(self, "font"): |
| self.font.size = vobj.FontSize.Value * vobj.ScaleMultiplier |
| if ( |
| hasattr(self, "node_wld") |
| and hasattr(self, "p2") |
| and "ArrowSizeStart" in properties |
| and "ArrowSizeEnd" in properties |
| ): |
| self.remove_dim_arrows() |
| self.draw_dim_arrows(vobj) |
| if "DimOvershoot" in properties: |
| self.remove_dim_overshoot() |
| self.draw_dim_overshoot(vobj) |
| if "ExtOvershoot" in properties: |
| self.remove_ext_overshoot() |
| self.draw_ext_overshoot(vobj) |
|
|
| self.updateData(obj, "Start") |
|
|
| elif prop == "FontSize" and "FontSize" in properties and "ScaleMultiplier" in properties: |
| if hasattr(self, "font"): |
| self.font.size = vobj.FontSize.Value * vobj.ScaleMultiplier |
|
|
| elif prop == "FontName" and "FontName" in properties and hasattr(self, "font"): |
| self.font.name = str(vobj.FontName) |
|
|
| elif prop == "TextColor" and "TextColor" in properties and hasattr(self, "textcolor"): |
| col = vobj.TextColor |
| self.textcolor.rgb.setValue(col[0], col[1], col[2]) |
|
|
| elif prop == "LineColor" and "LineColor" in properties and hasattr(self, "linecolor"): |
| col = vobj.LineColor |
| self.linecolor.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 ("ArrowSizeStart", "ArrowSizeEnd", "ArrowTypeStart", "ArrowTypeEnd") |
| and "ArrowSizeStart" in properties |
| and "ArrowSizeEnd" in properties |
| and "ScaleMultiplier" in properties |
| and hasattr(self, "node_wld") |
| and hasattr(self, "p2") |
| ): |
| self.remove_dim_arrows() |
| self.draw_dim_arrows(vobj) |
|
|
| elif ( |
| prop == "DimOvershoot" |
| and "DimOvershoot" in properties |
| and "ScaleMultiplier" in properties |
| ): |
| self.remove_dim_overshoot() |
| self.draw_dim_overshoot(vobj) |
|
|
| elif ( |
| prop == "ExtOvershoot" |
| and "ExtOvershoot" in properties |
| and "ScaleMultiplier" in properties |
| ): |
| self.remove_ext_overshoot() |
| self.draw_ext_overshoot(vobj) |
|
|
| elif prop == "ShowLine" and "ShowLine" in properties: |
| if vobj.ShowLine: |
| self.lineswitch_wld.whichChild = -3 |
| self.lineswitch_scr.whichChild = -3 |
| else: |
| self.lineswitch_wld.whichChild = -1 |
| self.lineswitch_scr.whichChild = -1 |
| else: |
| self.updateData(obj, "Start") |
|
|
| def remove_dim_arrows(self): |
| """Remove dimension arrows in the dimension lines. |
| |
| Remove the existing nodes. |
| """ |
| self.node_wld.removeChild(self.marks) |
| self.node_scr.removeChild(self.marks) |
|
|
| def draw_dim_arrows(self, vobj): |
| """Draw dimension arrows.""" |
| if not ( |
| hasattr(vobj, "ArrowTypeStart") |
| and hasattr(vobj, "ArrowTypeEnd") |
| and hasattr(vobj, "ArrowSizeStart") |
| and hasattr(vobj, "ArrowSizeEnd") |
| ): |
| return |
|
|
| |
| symbol_sta = utils.ARROW_TYPES.index(vobj.ArrowTypeStart) |
| symbol_end = utils.ARROW_TYPES.index(vobj.ArrowTypeEnd) |
| scale_sta = vobj.ArrowSizeStart.Value * vobj.ScaleMultiplier |
| scale_end = vobj.ArrowSizeEnd.Value * vobj.ScaleMultiplier |
| self.trans1.scaleFactor.setValue((scale_sta, scale_sta, scale_sta)) |
| self.trans2.scaleFactor.setValue((scale_end, scale_end, scale_end)) |
|
|
| |
| self.marks = coin.SoSeparator() |
| self.marks.addChild(self.linecolor) |
|
|
| s1 = coin.SoSeparator() |
| if symbol_sta == "Circle": |
| s1.addChild(self.coord1) |
| else: |
| s1.addChild(self.trans1) |
|
|
| s1.addChild(gui_utils.dim_symbol(symbol_sta, invert=False)) |
| self.marks.addChild(s1) |
|
|
| s2 = coin.SoSeparator() |
| if symbol_end == "Circle": |
| s2.addChild(self.coord2) |
| else: |
| s2.addChild(self.trans2) |
|
|
| s2.addChild(gui_utils.dim_symbol(symbol_end, invert=True)) |
| self.marks.addChild(s2) |
|
|
| self.node_wld.insertChild(self.marks, 2) |
| self.node_scr.insertChild(self.marks, 2) |
|
|
| def remove_dim_overshoot(self): |
| """Remove the dimension overshoot lines.""" |
| self.node_wld.removeChild(self.marksDimOvershoot) |
| self.node_scr.removeChild(self.marksDimOvershoot) |
|
|
| def draw_dim_overshoot(self, vobj): |
| """Draw dimension overshoot lines.""" |
| |
| s = vobj.DimOvershoot.Value * vobj.ScaleMultiplier |
| self.transDimOvershoot1.scaleFactor.setValue((s, s, s)) |
| self.transDimOvershoot2.scaleFactor.setValue((s, s, s)) |
|
|
| |
| self.marksDimOvershoot = coin.SoSeparator() |
| if vobj.DimOvershoot.Value: |
| self.marksDimOvershoot.addChild(self.linecolor) |
|
|
| s1 = coin.SoSeparator() |
| s1.addChild(self.transDimOvershoot1) |
| 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))) |
| self.marksDimOvershoot.addChild(s2) |
|
|
| self.node_wld.insertChild(self.marksDimOvershoot, 2) |
| self.node_scr.insertChild(self.marksDimOvershoot, 2) |
|
|
| def remove_ext_overshoot(self): |
| """Remove dimension extension overshoot lines.""" |
| self.node_wld.removeChild(self.marksExtOvershoot) |
| self.node_scr.removeChild(self.marksExtOvershoot) |
|
|
| def draw_ext_overshoot(self, vobj): |
| """Draw dimension extension overshoot lines.""" |
| |
| s = vobj.ExtOvershoot.Value * vobj.ScaleMultiplier |
| self.transExtOvershoot1.scaleFactor.setValue((s, s, s)) |
| self.transExtOvershoot2.scaleFactor.setValue((s, s, s)) |
|
|
| |
| self.marksExtOvershoot = coin.SoSeparator() |
| if vobj.ExtOvershoot.Value: |
| self.marksExtOvershoot.addChild(self.linecolor) |
| s1 = coin.SoSeparator() |
| s1.addChild(self.transExtOvershoot1) |
| 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))) |
| self.marksExtOvershoot.addChild(s2) |
|
|
| self.node_wld.insertChild(self.marksExtOvershoot, 2) |
| self.node_scr.insertChild(self.marksExtOvershoot, 2) |
|
|
| def is_linked_to_circle(self): |
| """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" |
|
|
|
|
| |
| _ViewProviderDimension = ViewProviderLinearDimension |
|
|
|
|
| class ViewProviderAngularDimension(ViewProviderDimensionBase): |
| """Viewprovider for the Angular dimension object.""" |
|
|
| def set_graphics_properties(self, vobj, properties): |
| """Set graphics properties only if they don't already exist.""" |
| super().set_graphics_properties(vobj, properties) |
|
|
| vobj.ArrowTypeStart = params.get_param("dimsymbolstart") |
| vobj.ArrowTypeEnd = params.get_param("dimsymbolend") |
|
|
| def attach(self, vobj): |
| """Set up the scene sub-graph of the viewprovider.""" |
| self.Object = vobj.Object |
|
|
| self.textpos = coin.SoTransform() |
| self.textcolor = coin.SoBaseColor() |
| self.font = coin.SoFont() |
| self.text_wld = coin.SoAsciiText() |
| self.text_scr = coin.SoText2() |
|
|
| |
| |
| self.text_wld.string = "d" |
| self.text_scr.string = "d" |
| self.text_wld.justification = coin.SoAsciiText.CENTER |
| self.text_scr.justification = coin.SoAsciiText.CENTER |
|
|
| label_wld = coin.SoSeparator() |
| label_wld.addChild(self.textpos) |
| label_wld.addChild(self.textcolor) |
| label_wld.addChild(self.font) |
| label_wld.addChild(self.text_wld) |
|
|
| label_scr = coin.SoSeparator() |
| label_scr.addChild(self.textpos) |
| label_scr.addChild(self.textcolor) |
| label_scr.addChild(self.font) |
| label_scr.addChild(self.text_scr) |
|
|
| self.coord1 = coin.SoCoordinate3() |
| self.trans1 = coin.SoTransform() |
| self.coord2 = coin.SoCoordinate3() |
| self.trans2 = coin.SoTransform() |
|
|
| self.linecolor = coin.SoBaseColor() |
| self.drawstyle = coin.SoDrawStyle() |
| self.coords = coin.SoCoordinate3() |
| import PartGui |
|
|
| self.arc = coin.SoType.fromName("SoBrepEdgeSet").createInstance() |
| self.marks = coin.SoSeparator() |
|
|
| self.node_wld = coin.SoGroup() |
| self.node_wld.addChild(self.linecolor) |
| self.node_wld.addChild(self.drawstyle) |
| self.node_wld.addChild(self.coords) |
| self.node_wld.addChild(self.arc) |
| self.node_wld.addChild(self.marks) |
| self.node_wld.addChild(label_wld) |
|
|
| self.node_scr = coin.SoGroup() |
| self.node_scr.addChild(self.linecolor) |
| self.node_scr.addChild(self.drawstyle) |
| self.node_scr.addChild(self.coords) |
| self.node_scr.addChild(self.arc) |
| self.node_scr.addChild(self.marks) |
| self.node_scr.addChild(label_scr) |
|
|
| vobj.addDisplayMode(self.node_wld, "World") |
| vobj.addDisplayMode(self.node_scr, "Screen") |
| self.updateData(vobj.Object, None) |
| self.onChanged(vobj, "FontSize") |
| self.onChanged(vobj, "FontName") |
| self.onChanged(vobj, "TextColor") |
| self.onChanged(vobj, "ArrowTypeStart") |
| self.onChanged(vobj, "ArrowTypeEnd") |
| self.onChanged(vobj, "LineColor") |
|
|
| 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 |
|
|
| |
| |
| 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) |
| ray = midp - obj.Center |
|
|
| |
| 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_wld.string = utils.string_encode_coin(self.string) |
| self.text_scr.string = utils.string_encode_coin(self.string) |
|
|
| |
| |
| |
| try: |
| m = vobj.DisplayMode |
| except AssertionError: |
| m = ["World", "Screen"][params.get_param("DefaultAnnoDisplayMode")] |
|
|
| |
| first = self.circle.FirstParameter |
| last = self.circle.LastParameter |
|
|
| if m == "Screen": |
| |
| 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))) |
|
|
| |
| 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) |
|
|
| |
| if ( |
| hasattr(vobj, "ArrowSizeStart") |
| and hasattr(vobj, "ArrowSizeEnd") |
| and hasattr(vobj, "ScaleMultiplier") |
| and hasattr(vobj, "FlipArrows") |
| and vobj.ArrowSizeStart.Value != 0 |
| and vobj.ArrowSizeEnd.Value != 0 |
| and vobj.ScaleMultiplier != 0 |
| ): |
| half_arrow_length_sta = 2 * vobj.ArrowSizeStart.Value * vobj.ScaleMultiplier |
| arrow_angle_sta = 2 * math.asin(min(1, half_arrow_length_sta / radius)) |
| half_arrow_length_end = 2 * vobj.ArrowSizeEnd.Value * vobj.ScaleMultiplier |
| arrow_angle_end = 2 * math.asin(min(1, half_arrow_length_end / radius)) |
| if vobj.FlipArrows: |
| arrow_angle_sta = -arrow_angle_sta |
| arrow_angle_end = -arrow_angle_end |
|
|
| u1 = ( |
| self.circle.valueAt(first + arrow_angle_sta) - self.circle.valueAt(first) |
| ).normalize() |
| u2 = ( |
| self.circle.valueAt(last) - self.circle.valueAt(last - arrow_angle_end) |
| ).normalize() |
|
|
| w2 = self.circle.Curve.Axis |
| w1 = w2.negative() |
|
|
| v1 = w1.cross(u1) |
| v2 = w2.cross(u2) |
| _plane_rot_1 = DraftVecUtils.getPlaneRotation(u1, v1) |
| _plane_rot_2 = DraftVecUtils.getPlaneRotation(u2, v2) |
| 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])) |
|
|
| |
| 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) |
| r = App.Placement(_plane_rot_3).Rotation |
| offset = r.multVec(App.Vector(0, 1, 0)) |
|
|
| if hasattr(vobj, "TextSpacing") and hasattr(vobj, "ScaleMultiplier"): |
| ts = vobj.TextSpacing.Value * vobj.ScaleMultiplier |
| offset = DraftVecUtils.scaleTo(offset, ts) |
| else: |
| offset = DraftVecUtils.scaleTo(offset, 0.05) |
|
|
| if getattr(vobj, "FlipText", False): |
| r = r.multiply(App.Rotation(App.Vector(0, 0, 1), 180)) |
| offset = offset.negative() |
|
|
| if m == "Screen": |
| 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]) |
|
|
| |
| _round_1 = round(obj.Angle, utils.precision()) |
| _round_2 = round(angle, utils.precision()) |
| if _round_1 != _round_2: |
| obj.Angle = angle |
|
|
| def onChanged(self, vobj, prop): |
| """Execute when a view property is changed.""" |
| super().onChanged(vobj, prop) |
|
|
| obj = vobj.Object |
| properties = vobj.PropertiesList |
|
|
| if "ScaleMultiplier" in properties and vobj.ScaleMultiplier == 0: |
| return |
|
|
| if prop == "ScaleMultiplier" and "ScaleMultiplier" in properties: |
| if hasattr(self, "font"): |
| self.font.size = vobj.FontSize.Value * vobj.ScaleMultiplier |
| if ( |
| hasattr(self, "node_wld") |
| and hasattr(self, "p2") |
| and "ArrowSizeStart" in properties |
| and "ArrowSizeEnd" in properties |
| ): |
| self.remove_dim_arrows() |
| self.draw_dim_arrows(vobj) |
|
|
| self.updateData(obj, None) |
|
|
| elif prop == "FontSize" and "ScaleMultiplier" in properties: |
| if hasattr(self, "font"): |
| self.font.size = vobj.FontSize.Value * vobj.ScaleMultiplier |
|
|
| elif prop == "FontName" and hasattr(self, "font"): |
| self.font.name = str(vobj.FontName) |
|
|
| elif prop == "TextColor" and "TextColor" in properties and hasattr(self, "textcolor"): |
| col = vobj.TextColor |
| self.textcolor.rgb.setValue(col[0], col[1], col[2]) |
|
|
| elif prop == "LineColor" and "LineColor" in properties and hasattr(self, "linecolor"): |
| col = vobj.LineColor |
| self.linecolor.rgb.setValue(col[0], col[1], col[2]) |
|
|
| elif prop == "LineWidth" and hasattr(self, "drawstyle"): |
| self.drawstyle.lineWidth = vobj.LineWidth |
|
|
| elif ( |
| prop in ("ArrowSizeStart", "ArrowTypeStart", "ArrowSizeEnd", "ArrowTypeEnd") |
| and "ScaleMultiplier" in properties |
| and hasattr(self, "node_wld") |
| and hasattr(self, "p2") |
| ): |
| self.updateData(obj, None) |
| self.remove_dim_arrows() |
| self.draw_dim_arrows(vobj) |
|
|
| else: |
| self.updateData(obj, None) |
|
|
| def remove_dim_arrows(self): |
| """Remove dimension arrows in the dimension lines. |
| |
| Remove the existing nodes. |
| """ |
| self.node_wld.removeChild(self.marks) |
| self.node_scr.removeChild(self.marks) |
|
|
| def draw_dim_arrows(self, vobj): |
| """Draw dimension arrows.""" |
| if not ( |
| hasattr(vobj, "ArrowTypeStart") |
| and hasattr(vobj, "ArrowTypeEnd") |
| and hasattr(vobj, "ArrowSizeStart") |
| and hasattr(vobj, "ArrowSizeEnd") |
| ): |
| return |
|
|
| |
| symbol_sta = utils.ARROW_TYPES.index(vobj.ArrowTypeStart) |
| symbol_end = utils.ARROW_TYPES.index(vobj.ArrowTypeEnd) |
| scale_sta = vobj.ArrowSizeStart.Value * vobj.ScaleMultiplier |
| scale_end = vobj.ArrowSizeEnd.Value * vobj.ScaleMultiplier |
| self.trans1.scaleFactor.setValue((scale_sta, scale_sta, scale_sta)) |
| self.trans2.scaleFactor.setValue((scale_end, scale_end, scale_end)) |
|
|
| |
| self.marks = coin.SoSeparator() |
| self.marks.addChild(self.linecolor) |
|
|
| s1 = coin.SoSeparator() |
| if symbol_sta == "Circle": |
| s1.addChild(self.coord1) |
| else: |
| s1.addChild(self.trans1) |
| s1.addChild(gui_utils.dim_symbol(symbol_sta, invert=False)) |
| self.marks.addChild(s1) |
|
|
| s2 = coin.SoSeparator() |
| if symbol_end == "Circle": |
| s2.addChild(self.coord2) |
| else: |
| s2.addChild(self.trans2) |
| s2.addChild(gui_utils.dim_symbol(symbol_end, invert=True)) |
| self.marks.addChild(s2) |
|
|
| self.node_wld.insertChild(self.marks, 2) |
| self.node_scr.insertChild(self.marks, 2) |
|
|
| def getIcon(self): |
| """Return the path to the icon used by the viewprovider.""" |
| return ":/icons/Draft_DimensionAngular.svg" |
|
|
|
|
| |
| _ViewProviderAngularDimension = ViewProviderAngularDimension |
|
|
| |
|
|