Skip to content

Commit

Permalink
Draft: new make_linear_dimension functions for more precision
Browse files Browse the repository at this point in the history
A single `make_dimension` handles three types of dimensions,
(1) simple linear, (2) linear linked to an object, and (3) linked
to a circular edge.

So, we provide two new functions, `make_linear_dimension`
and `make_linear_dimension_obj`, to handle the first two cases.
In this way we can check the input parameters much better.

We adjust the `Draft_Dimension` Gui Command accordingly.
  • Loading branch information
vocx-fc authored and yorikvanhavre committed Jun 17, 2020
1 parent e81ca58 commit 984de3b
Show file tree
Hide file tree
Showing 5 changed files with 419 additions and 135 deletions.
2 changes: 2 additions & 0 deletions src/Mod/Draft/Draft.py
Expand Up @@ -390,6 +390,8 @@

from draftmake.make_dimension import (make_dimension,
makeDimension,
make_linear_dimension,
make_linear_dimension_obj,
make_angular_dimension,
makeAngularDimension)

Expand Down
287 changes: 162 additions & 125 deletions src/Mod/Draft/draftguitools/gui_dimensions.py
Expand Up @@ -46,6 +46,7 @@
import draftguitools.gui_base_original as gui_base_original
import draftguitools.gui_tool_utils as gui_tool_utils
import draftguitools.gui_trackers as trackers

from draftutils.translate import translate
from draftutils.messages import _msg

Expand Down Expand Up @@ -102,11 +103,11 @@ def Activated(self):
name = translate("draft", "Dimension")
if self.cont:
self.finish()
elif self.hasMeasures():
elif self.selected_app_measure():
super(Dimension, self).Activated(name)
self.dimtrack = trackers.dimTracker()
self.arctrack = trackers.arcTracker()
self.createOnMeasures()
self.create_with_app_measure()
self.finish()
else:
super(Dimension, self).Activated(name)
Expand All @@ -129,40 +130,52 @@ def Activated(self):
self.force = None
self.info = None
self.selectmode = False
self.setFromSelection()
self.set_selection()
_msg(translate("draft", "Pick first point"))
Gui.draftToolBar.show()

def setFromSelection(self):
def set_selection(self):
"""Fill the nodes according to the selected geometry."""
sel = Gui.Selection.getSelectionEx()
if len(sel) == 1:
if len(sel[0].SubElementNames) == 1:
if "Edge" in sel[0].SubElementNames[0]:
edge = sel[0].SubObjects[0]
n = int(sel[0].SubElementNames[0].lstrip("Edge")) - 1
self.indices.append(n)
if DraftGeomUtils.geomType(edge) == "Line":
self.node.extend([edge.Vertexes[0].Point,
edge.Vertexes[1].Point])
v1 = None
v2 = None
for i, v in enumerate(sel[0].Object.Shape.Vertexes):
if v.Point == edge.Vertexes[0].Point:
v1 = i
if v.Point == edge.Vertexes[1].Point:
v2 = i
if (v1 is not None) and (v2 is not None):
self.link = [sel[0].Object, v1, v2]
elif DraftGeomUtils.geomType(edge) == "Circle":
self.node.extend([edge.Curve.Center,
edge.Vertexes[0].Point])
self.edges = [edge]
self.arcmode = "diameter"
self.link = [sel[0].Object, n]

def hasMeasures(self):
"""Check if measurement objects are selected."""
if (len(sel) == 1
and len(sel[0].SubElementNames) == 1
and "Edge" in sel[0].SubElementNames[0]):
# The selection is just a single `Edge`
sel_object = sel[0]
edge = sel_object.SubObjects[0]

# `n` is the edge number starting from 0 not from 1.
# The reason is lists in Python start from 0, although
# in the object's `Shape`, they start from 1
n = int(sel_object.SubElementNames[0].lstrip("Edge")) - 1
self.indices.append(n)

if DraftGeomUtils.geomType(edge) == "Line":
self.node.extend([edge.Vertexes[0].Point,
edge.Vertexes[1].Point])

# Iterate over the vertices of the parent `Object`;
# when the vertices match those of the selected `edge`
# save the index of vertex in the parent object
v1 = None
v2 = None
for i, v in enumerate(sel_object.Object.Shape.Vertexes):
if v.Point == edge.Vertexes[0].Point:
v1 = i
if v.Point == edge.Vertexes[1].Point:
v2 = i

if v1 and v2:
self.link = [sel_object.Object, v1, v2]
elif DraftGeomUtils.geomType(edge) == "Circle":
self.node.extend([edge.Curve.Center,
edge.Vertexes[0].Point])
self.edges = [edge]
self.arcmode = "diameter"
self.link = [sel_object.Object, n]

def selected_app_measure(self):
"""Check if App::MeasureDistance objects are selected."""
sel = Gui.Selection.getSelection()
if not sel:
return False
Expand All @@ -180,21 +193,28 @@ def finish(self, closed=False):
self.dimtrack.finalize()
self.arctrack.finalize()

def createOnMeasures(self):
"""Create on measurement objects."""
def create_with_app_measure(self):
"""Create on measurement objects.
This is used when the selection is an `'App::MeasureDistance'`,
which is created with the basic tool `Std_MeasureDistance`.
This object is removed and in its place a `Draft Dimension`
is created.
"""
for o in Gui.Selection.getSelection():
p1 = o.P1
p2 = o.P2
_root = o.ViewObject.RootNode
_ch = _root.getChildren()[1].getChildren()[0].getChildren()[0]
pt = _ch.getChildren()[3]
p3 = App.Vector(pt.point.getValues()[2].getValue())

Gui.addModule("Draft")
_cmd = 'Draft.make_dimension'
_cmd = 'Draft.make_linear_dimension'
_cmd += '('
_cmd += DraftVecUtils.toString(p1) + ', '
_cmd += DraftVecUtils.toString(p2) + ', '
_cmd += DraftVecUtils.toString(p3)
_cmd += 'dim_line=' + DraftVecUtils.toString(p3)
_cmd += ')'
_rem = 'FreeCAD.ActiveDocument.removeObject("' + o.Name + '")'
_cmd_list = ['_dim_ = ' + _cmd,
Expand All @@ -204,106 +224,121 @@ def createOnMeasures(self):
self.commit(translate("draft", "Create Dimension"),
_cmd_list)

def create_angle_dimension(self):
"""Create an angular dimension from a center and two angles."""
normal_str = "None"
if len(self.edges) == 2:
v1 = DraftGeomUtils.vec(self.edges[0])
v2 = DraftGeomUtils.vec(self.edges[1])
norm = v1.cross(v2)
norm.normalize()
normal_str = DraftVecUtils.toString(norm)

ang1 = math.degrees(self.angledata[1])
ang2 = math.degrees(self.angledata[0])

if ang1 > 360:
ang1 = ang1 - 360
if ang2 > 360:
ang2 = ang2 - 360

_cmd = 'Draft.make_angular_dimension'
_cmd += '('
_cmd += 'center=' + DraftVecUtils.toString(self.center) + ', '
_cmd += 'angles='
_cmd += '['
_cmd += str(ang1) + ', '
_cmd += str(ang2)
_cmd += '], '
_cmd += 'dim_line=' + DraftVecUtils.toString(self.node[-1]) + ', '
_cmd += 'normal=' + normal_str
_cmd += ')'
_cmd_list = ['_dim_ = ' + _cmd,
'Draft.autogroup(_dim_)',
'FreeCAD.ActiveDocument.recompute()']
self.commit(translate("draft", "Create Dimension"),
_cmd_list)

def create_linear_dimension(self):
"""Create a simple linear dimension, not linked to an edge."""
_cmd = 'Draft.make_linear_dimension'
_cmd += '('
_cmd += DraftVecUtils.toString(self.node[0]) + ', '
_cmd += DraftVecUtils.toString(self.node[1]) + ', '
_cmd += 'dim_line=' + DraftVecUtils.toString(self.node[2])
_cmd += ')'
_cmd_list = ['_dim_ = ' + _cmd,
'Draft.autogroup(_dim_)',
'FreeCAD.ActiveDocument.recompute()']
self.commit(translate("draft", "Create Dimension"),
_cmd_list)

def create_linear_dimension_obj(self, direction=None):
"""Create a linear dimension linked to an edge.
The `link` attribute has indices of vertices as they appear
in the list `Shape.Vertexes`, so they start as zero 0.
The `LinearDimension` class, created by `make_linear_dimension_obj`,
considers the vertices of a `Shape` which are numbered to start
with 1, that is, `Vertex1`.
Therefore the value in `link` has to be incremented by 1.
"""
_cmd = 'Draft.make_linear_dimension_obj'
_cmd += '('
_cmd += 'FreeCAD.ActiveDocument.' + self.link[0].Name + ', '
_cmd += 'i1=' + str(self.link[1] + 1) + ', '
_cmd += 'i2=' + str(self.link[2] + 1) + ', '
_cmd += 'dim_line=' + DraftVecUtils.toString(self.node[2])
_cmd += ')'
_cmd_list = ['_dim_ = ' + _cmd]

if direction == "X":
_cmd_list += ['_dim_.Direction = FreeCAD.Vector(1, 0, 0)']
elif direction == "Y":
_cmd_list += ['_dim_.Direction = FreeCAD.Vector(0, 1, 0)']

_cmd_list += ['Draft.autogroup(_dim_)',
'FreeCAD.ActiveDocument.recompute()']
self.commit(translate("draft", "Create Dimension"),
_cmd_list)

def create_radial_dimension_obj(self):
"""Create a radial dimension linked to a circular edge."""
_cmd = 'Draft.make_dimension'
_cmd += '('
_cmd += 'FreeCAD.ActiveDocument.' + self.link[0].Name + ', '
_cmd += str(self.link[1]) + ', '
_cmd += '"' + str(self.arcmode) + '", '
_cmd += DraftVecUtils.toString(self.node[2])
_cmd += ')'
_cmd_list = ['_dim_ = ' + _cmd,
'Draft.autogroup(_dim_)',
'FreeCAD.ActiveDocument.recompute()']
self.commit(translate("draft", "Create Dimension (radial)"),
_cmd_list)

def createObject(self):
"""Create the actual object in the current document."""
Gui.addModule("Draft")

if self.angledata:
normal = "None"
if len(self.edges) == 2:
v1 = DraftGeomUtils.vec(self.edges[0])
v2 = DraftGeomUtils.vec(self.edges[1])
normal = DraftVecUtils.toString((v1.cross(v2)).normalize())

_cmd = 'Draft.make_angular_dimension'
_cmd += '('
_cmd += 'center=' + DraftVecUtils.toString(self.center) + ', '
_cmd += 'angles='
_cmd += '['
_cmd += str(math.degrees(self.angledata[1])) + ', '
_cmd += str(math.degrees(self.angledata[0]))
_cmd += '], '
_cmd += 'dim_line=' + DraftVecUtils.toString(self.node[-1]) + ', '
_cmd += 'normal=' + normal
_cmd += ')'
_cmd_list = ['_dim_ = ' + _cmd,
'Draft.autogroup(_dim_)',
'FreeCAD.ActiveDocument.recompute()']
self.commit(translate("draft", "Create Dimension"),
_cmd_list)

# Angle dimension, with two angles provided
self.create_angle_dimension()
elif self.link and not self.arcmode:
# Linear dimension, linked
# Linear dimension, linked to a straight edge
if self.force == 1:
_cmd = 'Draft.make_dimension'
_cmd += '('
_cmd += 'FreeCAD.ActiveDocument.' + self.link[0].Name + ', '
_cmd += str(self.link[1]) + ', '
_cmd += str(self.link[2]) + ', '
_cmd += DraftVecUtils.toString(self.node[2])
_cmd += ')'
_cmd_list = ['_dim_ = ' + _cmd,
'_dim_.Direction = FreeCAD.Vector(0, 1, 0)',
'Draft.autogroup(_dim_)',
'FreeCAD.ActiveDocument.recompute()']
self.commit(translate("draft", "Create Dimension"),
_cmd_list)
self.create_linear_dimension_obj("Y")
elif self.force == 2:
_cmd = 'Draft.make_dimension'
_cmd += '('
_cmd += 'FreeCAD.ActiveDocument.' + self.link[0].Name + ', '
_cmd += str(self.link[1]) + ', '
_cmd += str(self.link[2]) + ', '
_cmd += DraftVecUtils.toString(self.node[2])
_cmd += ')'
_cmd_list = ['_dim_ = ' + _cmd,
'_dim_.Direction = FreeCAD.Vector(1, 0, 0)',
'Draft.autogroup(_dim_)',
'FreeCAD.ActiveDocument.recompute()']
self.commit(translate("draft", "Create Dimension"),
_cmd_list)
self.create_linear_dimension_obj("X")
else:
_cmd = 'Draft.make_dimension'
_cmd += '('
_cmd += 'FreeCAD.ActiveDocument.' + self.link[0].Name + ', '
_cmd += str(self.link[1]) + ', '
_cmd += str(self.link[2]) + ', '
_cmd += DraftVecUtils.toString(self.node[2])
_cmd += ')'
_cmd_list = ['_dim_ = ' + _cmd,
'Draft.autogroup(_dim_)',
'FreeCAD.ActiveDocument.recompute()']
self.commit(translate("draft", "Create Dimension"),
_cmd_list)

self.create_linear_dimension_obj()
elif self.arcmode:
# Radius or dimeter dimension, linked
_cmd = 'Draft.make_dimension'
_cmd += '('
_cmd += 'FreeCAD.ActiveDocument.' + self.link[0].Name + ', '
_cmd += str(self.link[1]) + ', '
_cmd += '"' + str(self.arcmode) + '", '
_cmd += DraftVecUtils.toString(self.node[2])
_cmd += ')'
_cmd_list = ['_dim_ = ' + _cmd,
'Draft.autogroup(_dim_)',
'FreeCAD.ActiveDocument.recompute()']
self.commit(translate("draft", "Create Dimension"),
_cmd_list)

# Radius or dimeter dimension, linked to a circular edge
self.create_radial_dimension_obj()
else:
# Linear dimension, non-linked
_cmd = 'Draft.make_dimension'
_cmd += '('
_cmd += DraftVecUtils.toString(self.node[0]) + ', '
_cmd += DraftVecUtils.toString(self.node[1]) + ', '
_cmd += DraftVecUtils.toString(self.node[2])
_cmd += ')'
_cmd_list = ['_dim_ = ' + _cmd,
'Draft.autogroup(_dim_)',
'FreeCAD.ActiveDocument.recompute()']
self.commit(translate("draft", "Create Dimension"),
_cmd_list)
# Linear dimension, not linked to any edge
self.create_linear_dimension()

if self.ui.continueMode:
self.cont = self.node[2]
Expand All @@ -314,12 +349,14 @@ def createObject(self):
self.dir = v2.sub(v1)
else:
self.dir = self.node[1].sub(self.node[0])

self.node = [self.node[1]]

self.link = None

def selectEdge(self):
"""Toggle the select mode to the opposite state."""
self.selectmode = not(self.selectmode)
self.selectmode = not self.selectmode

def action(self, arg):
"""Handle the 3D scene events.
Expand Down

0 comments on commit 984de3b

Please sign in to comment.