mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-09-19 15:03:05 +00:00
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:
@@ -6798,3 +6798,58 @@ TEST_CASE("An entity sketch that forms no wire fails instead of extruding a defa
|
||||
CHECK(doc.error.find("do not form a single closed wire") != std::string::npos);
|
||||
}
|
||||
}
|
||||
|
||||
// Sketching on a picked face is the most common gesture in solid modelling, and it was impossible:
|
||||
// the plane came from a combo of base + datum planes only, so the sole route onto a face was to
|
||||
// build a Coincident datum plane first. plane_of_face is the shared derivation that makes the
|
||||
// viewport selection usable directly. snaporca-3a2.
|
||||
TEST_CASE("plane_of_face gives a sketchable plane for a planar face only", "[CadDocument]")
|
||||
{
|
||||
// 20 x 20 x 20 box on XY, so its top face sits at z = 20 with +Z normal.
|
||||
CadDocument doc;
|
||||
int sk = doc.add_sketch(SketchShape::Rectangle, SketchPlane::XY(), 20, 20, 10, "Sketch");
|
||||
doc.add_extrude(sk, 20.0, false, BooleanMode::New, "Extrude");
|
||||
REQUIRE(doc.recompute());
|
||||
REQUIRE(doc.bodies.size() == 1);
|
||||
|
||||
SECTION("every planar face of the box resolves, and its normal is a unit axis") {
|
||||
int resolved = 0, top = -1;
|
||||
for (int fi = 0; fi < 64; ++fi) {
|
||||
SketchPlane p;
|
||||
if (!doc.plane_of_face(0, fi, p)) continue;
|
||||
++resolved;
|
||||
CHECK_THAT(p.normal.norm(), Catch::Matchers::WithinAbs(1.0, 1e-9));
|
||||
// The frame must be orthonormal or sketch coordinates on it would be skewed.
|
||||
CHECK_THAT(p.x_axis.dot(p.y_axis), Catch::Matchers::WithinAbs(0.0, 1e-9));
|
||||
CHECK_THAT(p.x_axis.dot(p.normal), Catch::Matchers::WithinAbs(0.0, 1e-9));
|
||||
if (p.normal.z() > 0.99) top = fi;
|
||||
}
|
||||
CHECK(resolved == 6); // a box has exactly six planar faces
|
||||
REQUIRE(top >= 0); // and one of them faces +Z
|
||||
SketchPlane p;
|
||||
REQUIRE(doc.plane_of_face(0, top, p));
|
||||
CHECK_THAT(p.origin.z(), Catch::Matchers::WithinAbs(20.0, 1e-6)); // the top, not the base
|
||||
}
|
||||
|
||||
SECTION("bad indices are refused rather than guessed") {
|
||||
SketchPlane p;
|
||||
CHECK_FALSE(doc.plane_of_face(0, -1, p));
|
||||
CHECK_FALSE(doc.plane_of_face(-1, 0, p));
|
||||
CHECK_FALSE(doc.plane_of_face(9, 0, p));
|
||||
CHECK_FALSE(doc.plane_of_face(0, 999, p));
|
||||
}
|
||||
|
||||
SECTION("a cylindrical face is refused — it has no single sketch plane") {
|
||||
CadDocument cyl;
|
||||
int c = cyl.add_sketch(SketchShape::Circle, SketchPlane::XY(), 0, 0, 10.0, "Sketch");
|
||||
cyl.add_extrude(c, 20.0, false, BooleanMode::New, "Extrude");
|
||||
REQUIRE(cyl.recompute());
|
||||
int planar = 0, refused = 0;
|
||||
for (int fi = 0; fi < 16; ++fi) {
|
||||
SketchPlane p;
|
||||
if (cyl.plane_of_face(0, fi, p)) ++planar; else ++refused;
|
||||
}
|
||||
CHECK(planar == 2); // the two flat caps, and NOT the barrel
|
||||
CHECK(refused > 0);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user