Merge pull request #4840 from marioalexis84/draft-wp

Draft: Improve alignToSelection method on WorkingPlane
This commit is contained in:
Yorik van Havre
2021-06-09 12:43:58 +02:00
committed by GitHub
3 changed files with 128 additions and 46 deletions

View File

@@ -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.

View File

@@ -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

View File

@@ -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):