Merge pull request #4840 from marioalexis84/draft-wp
Draft: Improve alignToSelection method on WorkingPlane
This commit is contained in:
@@ -34,10 +34,16 @@ YZ, and XZ planes.
|
||||
# in FreeCAD and a couple of utility functions.
|
||||
|
||||
import math
|
||||
import lazy_loader.lazy_loader as lz
|
||||
|
||||
import FreeCAD
|
||||
import DraftVecUtils
|
||||
from FreeCAD import Vector
|
||||
from draftutils.translate import translate
|
||||
|
||||
DraftGeomUtils = lz.LazyLoader("DraftGeomUtils", globals(), "DraftGeomUtils")
|
||||
Part = lz.LazyLoader("Part", globals(), "Part")
|
||||
FreeCADGui = lz.LazyLoader("FreeCADGui", globals(), "FreeCADGui")
|
||||
|
||||
__title__ = "FreeCAD Working Plane utility"
|
||||
__author__ = "Ken Cline"
|
||||
@@ -603,12 +609,6 @@ class Plane:
|
||||
"""Align the plane to a selection if it defines a plane.
|
||||
|
||||
If the selection uniquely defines a plane it will be used.
|
||||
Currently it only works with one object selected, a `'Face'`.
|
||||
It extracts the shape of the object or subobject
|
||||
and then calls `alignToFace(shape, offset)`.
|
||||
|
||||
This method only works when `FreeCAD.GuiUp` is `True`,
|
||||
that is, when the graphical interface is loaded.
|
||||
|
||||
Parameter
|
||||
---------
|
||||
@@ -621,43 +621,101 @@ class Plane:
|
||||
bool
|
||||
`True` if the operation was successful, and `False` otherwise.
|
||||
It returns `False` if the selection has no elements,
|
||||
or if it has more than one element,
|
||||
or if the object is not derived from `'Part::Feature'`
|
||||
or if the object doesn't have a `Shape`.
|
||||
|
||||
To do
|
||||
-----
|
||||
The method returns `False` if the selection list has more than
|
||||
one element.
|
||||
The method should search the list for objects like faces, points,
|
||||
edges, wires, etc., and call the appropriate aligning submethod.
|
||||
|
||||
The method could work for curves (`'Edge'` or `'Wire'`) but
|
||||
`alignToCurve()` isn't fully implemented.
|
||||
|
||||
When the interface is not loaded it should fail and print
|
||||
a message, `FreeCAD.Console.PrintError()`.
|
||||
|
||||
See Also
|
||||
--------
|
||||
alignToFace, alignToCurve
|
||||
"""
|
||||
import FreeCADGui
|
||||
sex = FreeCADGui.Selection.getSelectionEx(FreeCAD.ActiveDocument.Name)
|
||||
if len(sex) == 0:
|
||||
sel_ex = FreeCADGui.Selection.getSelectionEx(FreeCAD.ActiveDocument.Name)
|
||||
if not sel_ex:
|
||||
return False
|
||||
elif len(sex) == 1:
|
||||
if (not sex[0].Object.isDerivedFrom("Part::Feature")
|
||||
or not sex[0].Object.Shape):
|
||||
|
||||
shapes = list()
|
||||
names = list()
|
||||
for obj in sel_ex:
|
||||
# check that the geometric property is a Part.Shape object
|
||||
geom_is_shape = False
|
||||
if isinstance(obj.Object, FreeCAD.GeoFeature):
|
||||
geom = obj.Object.getPropertyOfGeometry()
|
||||
if isinstance(geom, Part.Shape):
|
||||
geom_is_shape = True
|
||||
if not geom_is_shape:
|
||||
FreeCAD.Console.PrintError(translate(
|
||||
"draft",
|
||||
"Object without Part.Shape geometry:'{}'\n".format(
|
||||
obj.ObjectName)))
|
||||
return False
|
||||
return (self.alignToFace(sex[0].Object.Shape, offset)
|
||||
or (len(sex[0].SubObjects) == 1
|
||||
and self.alignToFace(sex[0].SubObjects[0], offset))
|
||||
or self.alignToCurve(sex[0].Object.Shape, offset))
|
||||
if geom.isNull():
|
||||
FreeCAD.Console.PrintError(translate(
|
||||
"draft",
|
||||
"Object with null Part.Shape geometry:'{}'\n".format(
|
||||
obj.ObjectName)))
|
||||
return False
|
||||
if obj.HasSubObjects:
|
||||
shapes.extend(obj.SubObjects)
|
||||
names.extend([obj.ObjectName + "." + n for n in obj.SubElementNames])
|
||||
else:
|
||||
shapes.append(geom)
|
||||
names.append(obj.ObjectName)
|
||||
|
||||
normal = None
|
||||
for n in range(len(shapes)):
|
||||
if not DraftGeomUtils.is_planar(shapes[n]):
|
||||
FreeCAD.Console.PrintError(translate(
|
||||
"draft","'{}' object is not planar\n".format(names[n])))
|
||||
return False
|
||||
if not normal:
|
||||
normal = DraftGeomUtils.get_normal(shapes[n])
|
||||
shape_ref = n
|
||||
|
||||
# test if all shapes are coplanar
|
||||
if normal:
|
||||
for n in range(len(shapes)):
|
||||
if not DraftGeomUtils.are_coplanar(shapes[shape_ref], shapes[n]):
|
||||
FreeCAD.Console.PrintError(translate(
|
||||
"draft","{} and {} aren't coplanar\n".format(
|
||||
names[shape_ref],names[n])))
|
||||
return False
|
||||
else:
|
||||
# len(sex) > 2, look for point and line, three points, etc.
|
||||
# suppose all geometries are straight lines or points
|
||||
points = [vertex.Point for shape in shapes for vertex in shape.Vertexes]
|
||||
if len(points) >= 3:
|
||||
poly = Part.makePolygon(points)
|
||||
if not DraftGeomUtils.is_planar(poly):
|
||||
FreeCAD.Console.PrintError(translate(
|
||||
"draft","All Shapes must be coplanar\n"))
|
||||
return False
|
||||
normal = DraftGeomUtils.get_normal(poly)
|
||||
else:
|
||||
normal = None
|
||||
|
||||
if not normal:
|
||||
FreeCAD.Console.PrintError(translate(
|
||||
"draft","Selected Shapes must define a plane\n"))
|
||||
return False
|
||||
|
||||
# set center of mass
|
||||
ctr_mass = FreeCAD.Vector(0,0,0)
|
||||
ctr_pts = FreeCAD.Vector(0,0,0)
|
||||
mass = 0
|
||||
for shape in shapes:
|
||||
if hasattr(shape, "CenterOfMass"):
|
||||
ctr_mass += shape.CenterOfMass*shape.Mass
|
||||
mass += shape.Mass
|
||||
else:
|
||||
ctr_pts += shape.Point
|
||||
if mass > 0:
|
||||
ctr_mass /= mass
|
||||
# all shapes are vertexes
|
||||
else:
|
||||
ctr_mass = ctr_pts/len(shapes)
|
||||
|
||||
self.alignToPointAndAxis(ctr_mass, normal, offset)
|
||||
|
||||
return True
|
||||
|
||||
def setup(self, direction=None, point=None, upvec=None, force=False):
|
||||
"""Set up the working plane if it exists but is undefined.
|
||||
|
||||
|
||||
@@ -199,6 +199,9 @@ def findDistance(point, edge, strict=False):
|
||||
def get_spline_normal(edge, tol=-1):
|
||||
"""Find the normal of a BSpline edge."""
|
||||
|
||||
if edge.isNull():
|
||||
return None
|
||||
|
||||
if is_straight_line(shape, tol):
|
||||
return None
|
||||
|
||||
@@ -230,9 +233,11 @@ def get_normal(shape, tol=-1):
|
||||
return None
|
||||
|
||||
# for shapes
|
||||
if is_straight_line(shape, tol):
|
||||
if shape.isNull():
|
||||
return None
|
||||
|
||||
if is_straight_line(shape, tol):
|
||||
return None
|
||||
else:
|
||||
plane = find_plane(shape, tol)
|
||||
if plane:
|
||||
@@ -273,6 +278,7 @@ def is_planar(shape, tol=-1):
|
||||
poly = Part.makePolygon(shape)
|
||||
if is_straight_line(poly, tol):
|
||||
return True
|
||||
|
||||
plane = poly.findPlane(tol)
|
||||
if plane:
|
||||
return True
|
||||
@@ -280,6 +286,9 @@ def is_planar(shape, tol=-1):
|
||||
return False
|
||||
|
||||
# for shapes
|
||||
if shape.isNull():
|
||||
return False
|
||||
|
||||
# because Part.Shape.findPlane return None for Vertex and straight edges
|
||||
if shape.ShapeType == "Vertex":
|
||||
return True
|
||||
@@ -300,6 +309,9 @@ def is_straight_line(shape, tol=-1):
|
||||
plane and normal to straight wires creating privileged directions
|
||||
and to deal with straight wires with overlapped edges."""
|
||||
|
||||
if shape.isNull():
|
||||
return False
|
||||
|
||||
if len(shape.Faces) != 0:
|
||||
return False
|
||||
|
||||
@@ -332,6 +344,9 @@ def is_straight_line(shape, tol=-1):
|
||||
def are_coplanar(shape_a, shape_b, tol=-1):
|
||||
"""Return True if exist a plane containing both shapes."""
|
||||
|
||||
if shape_a.isNull() or shape_b.isNull():
|
||||
return False
|
||||
|
||||
if not is_planar(shape_a, tol) or not is_planar(shape_b, tol):
|
||||
return False
|
||||
|
||||
@@ -391,6 +406,9 @@ def get_spline_surface_normal(shape, tol=-1):
|
||||
"""Check if shape formed by BSpline surfaces is planar and get normal.
|
||||
If shape is not planar return None."""
|
||||
|
||||
if shape.isNull():
|
||||
return None
|
||||
|
||||
if len(shape.Faces) == 0:
|
||||
return None
|
||||
|
||||
@@ -431,6 +449,9 @@ def find_plane(shape, tol=-1):
|
||||
Use this function as a workaround due Part.Shape.findPlane
|
||||
fail to find plane on BSpline surfaces."""
|
||||
|
||||
if shape.isNull():
|
||||
return None
|
||||
|
||||
if shape.ShapeType == "Vertex":
|
||||
return None
|
||||
|
||||
|
||||
@@ -75,6 +75,10 @@ class Draft_SelectPlane:
|
||||
|
||||
def Activated(self):
|
||||
"""Execute when the command is called."""
|
||||
# finish active Draft command if any
|
||||
if FreeCAD.activeDraftCommand is not None:
|
||||
FreeCAD.activeDraftCommand.finish()
|
||||
|
||||
# Reset variables
|
||||
self.view = Draft.get3DView()
|
||||
self.wpButton = FreeCADGui.draftToolBar.wplabel
|
||||
@@ -85,9 +89,6 @@ class Draft_SelectPlane:
|
||||
p = FreeCAD.DraftWorkingPlane
|
||||
self.states.append([p.u, p.v, p.axis, p.position])
|
||||
|
||||
m = translate("draft", "Pick a face, 3 vertices or a WP Proxy to define the drawing plane")
|
||||
_msg(m)
|
||||
|
||||
# Create task panel
|
||||
FreeCADGui.Control.closeDialog()
|
||||
self.taskd = task_selectplane.SelectPlaneTaskPanel()
|
||||
@@ -127,18 +128,20 @@ class Draft_SelectPlane:
|
||||
self.taskd.form.fieldSnapRadius.valueChanged.connect(self.onSetSnapRadius)
|
||||
|
||||
# Try to find a WP from the current selection
|
||||
if self.handle():
|
||||
return
|
||||
if FreeCADGui.Selection.getSelectionEx(FreeCAD.ActiveDocument.Name):
|
||||
if self.handle():
|
||||
pass
|
||||
# Try another method
|
||||
elif FreeCAD.DraftWorkingPlane.alignToSelection():
|
||||
FreeCADGui.Selection.clearSelection()
|
||||
self.display(FreeCAD.DraftWorkingPlane.axis)
|
||||
return None
|
||||
|
||||
# Try another method
|
||||
if FreeCAD.DraftWorkingPlane.alignToSelection():
|
||||
FreeCADGui.Selection.clearSelection()
|
||||
self.display(FreeCAD.DraftWorkingPlane.axis)
|
||||
self.finish()
|
||||
return
|
||||
|
||||
# Execute the actual task panel
|
||||
FreeCADGui.Control.showDialog(self.taskd)
|
||||
# Execute the actual task panel delayed to catch posible active Draft command
|
||||
todo.delay(FreeCADGui.Control.showDialog, self.taskd)
|
||||
_msg(translate(
|
||||
"draft",
|
||||
"Pick a face, 3 vertices or a WP Proxy to define the drawing plane"))
|
||||
self.call = self.view.addEventCallback("SoEvent", self.action)
|
||||
|
||||
def finish(self, close=False):
|
||||
|
||||
Reference in New Issue
Block a user