Sketch where the user pointed: a picked face is the sketch plane

Selecting a face and sketching on it is the most common gesture in solid
modelling, and it was impossible. The plane came from a combo holding
XY/XZ/YZ plus datums, and plane_from_choice had no face branch at all, so
the only route onto a face was to build a Coincident datum plane on it
first, confirm that, reopen the sketch and find the datum in the
dropdown. Three extra steps and a junk feature in the tree.

The fix is not another combo row. A new sketch now takes its plane from
what is SELECTED IN THE VIEWPORT: a picked planar face wins outright, and
only when nothing is picked does it fall back to the reference plane —
which is itself normally set by clicking one of the ghost planes in 3D,
not by opening the combo. The tool hint names the target ("Circle — click
center, then radius · on the picked face") so the choice is visible on the
geometry side rather than needing a control to read back.

CadDocument::plane_of_face is the shared derivation, so the sketch path
and the Coincident datum method cannot drift apart. It refuses
non-planar faces: face_normal_world evaluates at the mid parameter, which
on a cylinder or a fillet is a tangent plane at one arbitrary point —
fine for offsetting a datum, wrong as a sketch plane, and silently
sketching on a tangent is worse than declining.

Picking the face also CONSUMES it. Leaving the pick live meant the next
Extrude saw a selected face and push/pulled it instead of extruding the
sketch just drawn — the same trap the imported-art path already guards
against.

Verified on :10 end to end with no combo interaction: build a box, click
its top face twice to cycle whole -> face, press S then C, and the circle
is drawn in the plane of that face with its Radius tab on the geometry
(artifacts/shots/f3a2-03-face.png, f3a2-04-sketch-on-face.png,
f3a2-05-circle-drawn.png). Kernel side: 154 cases / 2125 assertions green
on both forks, including that a cylinder resolves exactly its two flat
caps and refuses the barrel.

Still side-panel-shaped and to be dealt with separately: the Plane combo
remains on the card and now merely displays a stale row when a face is
the real target. It should show the actual target or go away.

snaporca-3a2.
This commit is contained in:
Tommaso Bianchi
2026-07-30 13:42:11 +02:00
parent 7f0a8c85ee
commit 6ce20d78c3
5 changed files with 119 additions and 2 deletions
+16
View File
@@ -32,6 +32,8 @@
#include <BRepCheck_Analyzer.hxx>
#include <BRepLib.hxx>
#include <BRepAdaptor_Curve.hxx>
#include <BRepAdaptor_Surface.hxx> // plane_of_face: reject non-planar faces
#include <GeomAbs_SurfaceType.hxx>
#include <GeomAbs_CurveType.hxx>
#include <BRep_Tool.hxx>
#include <Geom_CylindricalSurface.hxx>
@@ -1376,6 +1378,20 @@ static SketchPlane frame_from(const Vec3d& origin, const Vec3d& normal)
return p;
}
bool CadDocument::plane_of_face(int body_idx, int face_idx, SketchPlane& out) const
{
if (face_idx < 0 || body_idx < 0 || body_idx >= int(bodies.size())) return false;
const TopoDS_Face f = GeometryEngine::face_by_index(bodies[body_idx].shape, face_idx);
if (f.IsNull()) return false;
// Planar only. face_normal_world evaluates the normal at the mid parameter, which on a cylinder
// or a fillet is a tangent plane at one arbitrary point — usable for a datum offset, wrong as a
// sketch plane. Refuse rather than sketch somewhere the user did not point at.
BRepAdaptor_Surface surf(f);
if (surf.GetType() != GeomAbs_Plane) return false;
out = frame_from(GeometryEngine::face_centroid_world(f), GeometryEngine::face_normal_world(f));
return true;
}
std::vector<std::pair<std::string, SketchPlane>> CadDocument::resolve_datum_planes() const
{
std::vector<std::pair<std::string, SketchPlane>> out;