diff --git a/src/Mod/Draft/WorkingPlane.py b/src/Mod/Draft/WorkingPlane.py index 5fa9329ba5..1cebf2ad86 100644 --- a/src/Mod/Draft/WorkingPlane.py +++ b/src/Mod/Draft/WorkingPlane.py @@ -243,6 +243,87 @@ class PlaneBase: self.position = pos + (self.axis * offset) return True + def align_to_straight_edge(self, edge, offset=0): + """Align the WP to a straight edge. + + Used when a single straight edge is selected and does not define a + plane. The edge direction becomes the WP `u` axis. All three axes are + recomputed using XZY priority so the result is always a valid + orthonormal frame. + + Parameters + ---------- + edge: Part.Edge + A straight edge. + offset: float, optional + Defaults to zero. + Offset along the WP `axis`. + + Returns + ------- + `True`/`False` + `True` if successful. + """ + tol = 1e-7 + if edge.Length <= tol: + return False + u = edge.derivative1At(edge.FirstParameter).normalize() + rot = FreeCAD.Rotation(u, self.v, self.axis, "XZY") + self.u, self.v, self.axis = self._axes_from_rotation(rot) + self.position = edge.Vertexes[0].Point + (self.axis * offset) + return True + + def align_to_vertex(self, vertex, offset=0): + """Move the WP origin to a vertex, keeping the current orientation. + + Parameters + ---------- + vertex: Part.Vertex + A vertex. + offset: float, optional + Defaults to zero. + Offset along the WP `axis`. + + Returns + ------- + `True` + Always successful. + """ + self.position = vertex.Point + (self.axis * offset) + return True + + def align_to_two_vertices(self, v1, v2, offset=0): + """Align the WP to two vertices. + + The direction from `v1` to `v2` defines the WP `u` axis. The `v` and + `axis` vectors are recomputed using XZY priority. The WP origin is set + to `v1`. + + Parameters + ---------- + v1: Part.Vertex + First vertex (WP origin). + v2: Part.Vertex + Second vertex (defines u axis direction). + offset: float, optional + Defaults to zero. + Offset along the WP `axis`. + + Returns + ------- + `True`/`False` + `True` if successful. + """ + tol = 1e-7 + u = v2.Point - v1.Point + if u.Length <= tol: + return False + u.normalize() + rot = FreeCAD.Rotation(u, self.v, self.axis, "XZY") + self.u, self.v, self.axis = self._axes_from_rotation(rot) + self.position = v1.Point + (self.axis * offset) + return True + def align_to_face(self, face, offset=0): """Align the WP to a face with an optional offset. @@ -1286,7 +1367,13 @@ class PlaneGui(PlaneBase): def align_to_selection(self, offset=0, _hist_add=True): """Align the WP to a selection with an optional offset. - The selection must define a plane. + For a single face the WP is aligned to that face. For a single curved + edge or wire the WP is aligned to the plane of that shape. For a + single straight edge the WP u-axis is aligned along the edge while the + WP axis (normal) is preserved. For a single vertex the WP origin is + moved to that point while the orientation is preserved. For two + vertices the direction from the first to the second defines the WP + u-axis; selection order matters. Parameter --------- @@ -1313,7 +1400,11 @@ class PlaneGui(PlaneBase): if len(objs) != 1: ret = False - if all( + if len(objs) == 2 and all( + obj[0].isNull() is False and obj[0].ShapeType == "Vertex" for obj in objs + ): + ret = self.align_to_two_vertices(objs[0][0], objs[1][0], offset, _hist_add) + elif all( [ obj[0].isNull() is False and obj[0].ShapeType in ["Edge", "Vertex"] for obj in objs @@ -1351,8 +1442,12 @@ class PlaneGui(PlaneBase): ret = self.align_to_face(shape, offset, _hist_add) elif shape.ShapeType == "Edge": ret = self.align_to_edge_or_wire(shape, offset, _hist_add) + if ret is False: + ret = self.align_to_straight_edge(shape, offset, _hist_add) elif shape.Solids: ret = self.align_to_obj_placement(obj, offset, place, _hist_add) + elif shape.ShapeType == "Vertex": + ret = self.align_to_vertex(shape, offset, _hist_add) else: ret = self.align_to_edges_vertexes(shape.Vertexes, offset, _hist_add) @@ -1388,6 +1483,26 @@ class PlaneGui(PlaneBase): self._handle_custom(_hist_add) return True + def align_to_straight_edge(self, edge, offset=0, _hist_add=True): + """See PlaneBase.align_to_straight_edge.""" + if super().align_to_straight_edge(edge, offset) is False: + return False + self._handle_custom(_hist_add) + return True + + def align_to_vertex(self, vertex, offset=0, _hist_add=True): + """See PlaneBase.align_to_vertex.""" + super().align_to_vertex(vertex, offset) + self._handle_custom(_hist_add) + return True + + def align_to_two_vertices(self, v1, v2, offset=0, _hist_add=True): + """See PlaneBase.align_to_two_vertices.""" + if super().align_to_two_vertices(v1, v2, offset) is False: + return False + self._handle_custom(_hist_add) + return True + def align_to_face(self, face, offset=0, _hist_add=True): """See PlaneBase.align_to_face.""" if super().align_to_face(face, offset) is False: