mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-09-25 09:50:59 +00:00
CLI: --ground-* orientation from the Lay on Face planes, and --inspect-mesh (#15073)
* CLI: --ground-face-* / --lay-flat / --center-on-bed orientation primitives
Adds the CLI counterparts to the GUI's lay-flat / face-pick gizmos.
Scripted / CI / AI pipelines can now set orientation without rendering
a wxWidgets frame; today the only way is a GUI round-trip.
New CLI actions (all operate in the mesh-local frame so they compose
with prior --rotate-* / --orient flags):
--ground-largest-face 1 Auto-detect the largest planar-face
or --lay-flat 1 cluster (area-weighted), rotate so its
normal points -Z. Covers "this part has
one obvious flat side" cases.
--ground-face-normal NX,NY,NZ Pick the face whose mesh-local
normal best matches the given
vector; ground it. e.g.
`--ground-face-normal 1,0,0`
stands a part on its +X side.
--ground-face-point X,Y,Z Find the triangle containing the
given mesh-local point; ground its
face. Disambiguates when several
faces share a normal (largest
containing triangle wins).
--center-on-bed 1 Translate so the XY bounding-box
centroid lands at the bed center
(derived from printable_area).
New file `src/slic3r/Utils/MeshOrient.{hpp,cpp}`:
- collect_triangles_object / compute_face_clusters — quantize
per-triangle normals (0.001, ~0.06°) and area-weighted-average
within clusters. Same clustering logic used by lay-flat.
- apply_ground_rotation — same math as Selection::flattening_rotate
in the GUI (Selection.cpp:1432): world-space quaternion from the
transformed normal to -Z, applied as offset * new_rot * old_no_offset
on every instance of every object, then a per-instance Z-lift so the
grounded face lands at exactly 0 (avoids "No layers were detected"
from FP-error z≈-1e-9).
- ground_face_point uses a top-N cluster search + point-in-triangle
test in local space; largest-area triangle wins on ambiguity.
Rationale: without these, any CLI pipeline that needs a specific
face on the bed must either encode custom rotation math per part or
break out of the pipeline into the GUI. Both are bad for
reproducibility. The --ground-face-* triple + the largest-face
auto-mode cover essentially every orientation intent expressible
in a slicing wizard.
Scope:
- `src/slic3r/Utils/MeshOrient.{hpp,cpp}` — new, ~420 lines
- `src/slic3r/CMakeLists.txt` — 2-line registration
- `src/libslic3r/PrintConfig.cpp` — 5 new CLIMiscConfigDef entries
- `src/OrcaSlicer.cpp` — 58-line handler block + 1 include
No behaviour change when the flags are absent.
(cherry picked from commit c45a9795e1)
* CLI grounding: choose among the Lay on Face planes, per object
Addresses review:
- Move the geometry of GLGizmoFlatten::update_planes() into
libslic3r/LayOnFace and use it from the gizmo and the CLI, so the
--ground-* options pick convex-hull faces per object and instance,
with part transformations (--rotate-x/y) applied.
- Drop --center-on-bed, the --lay-flat alias and MeshOrient; make
--ground-largest-face a coBool.
- Parse --ground-face-normal and --ground-face-point strictly. A point
that only some objects contain grounds those and leaves the others.
- Fold in --inspect-mesh from #14603, reporting the same planes.
- Tests in tests/libslic3r/test_lay_on_face.cpp: bounding boxes before
and after, rotate then ground, two objects, and a ribbed part whose
parallel inner faces outsum its base.
* CLI --inspect-mesh, --ground-face-*: reject missing input and empty values
- Without an input file or --load-assemble-list, --inspect-mesh printed
nothing and exited 0. Reject it up front with CLI_INVALID_PARAMS.
- An explicit empty --ground-face-normal or --ground-face-point was
silently ignored. Only options given on the command line reach the
transforms loop, so an empty value now fails the strict parse like any
other malformed value.
This commit is contained in:
@@ -11956,6 +11956,13 @@ CLIActionsConfigDef::CLIActionsConfigDef()
|
||||
def->tooltip = L("This outputs the model\u2019s information.");
|
||||
def->set_default_value(new ConfigOptionBool(false));
|
||||
|
||||
def = this->add("inspect_mesh", coBool);
|
||||
def->label = L("Inspect mesh (JSON to stdout)");
|
||||
def->tooltip = L("Print a JSON summary of each loaded object to stdout, then exit: its bounding boxes and the "
|
||||
"convex hull faces it can be laid on, with their normals, areas and centers. These are the faces "
|
||||
"the --ground-* options choose from. Machine-readable alternative to --info.");
|
||||
def->set_default_value(new ConfigOptionBool(false));
|
||||
|
||||
def = this->add("export_settings", coString);
|
||||
def->label = L("Export Settings");
|
||||
def->tooltip = L("This exports settings to a file. Use - to write them to stdout.");
|
||||
@@ -12075,6 +12082,34 @@ CLITransformConfigDef::CLITransformConfigDef()
|
||||
def->sidetext = u8"°"; // degrees, don't need translation
|
||||
def->set_default_value(new ConfigOptionFloat(0));
|
||||
|
||||
// The --ground-* options choose from the faces the "Lay on Face" gizmo offers. Like the other
|
||||
// transforms they run in command-line order, so they see the rotations given before them.
|
||||
def = this->add("ground_largest_face", coBool);
|
||||
def->label = L("Ground largest face");
|
||||
def->tooltip = L("Lay each object on the largest face of its convex hull and drop it onto the bed. Of equally large "
|
||||
"faces, the one already facing down is kept. Objects without a face large enough to rest on are left "
|
||||
"as they are. Transforms run in command-line order, so rotations given before this option are respected. "
|
||||
"--orient 1 runs after all transforms and replaces the orientation.");
|
||||
def->set_default_value(new ConfigOptionBool(false));
|
||||
|
||||
def = this->add("ground_face_normal", coString);
|
||||
def->label = L("Ground face by normal");
|
||||
def->tooltip = L("Lay each object on the convex hull face whose outward normal is closest to the direction NX,NY,NZ "
|
||||
"and drop it onto the bed. The direction is in object coordinates, which include the rotations given "
|
||||
"before this option and match the plate axes unless the input file rotates the object. For example, "
|
||||
"1,0,0 stands the object on its +X side. --orient 1 runs after all transforms and replaces the orientation.");
|
||||
def->cli_params = "NX,NY,NZ";
|
||||
def->set_default_value(new ConfigOptionString(""));
|
||||
|
||||
def = this->add("ground_face_point", coString);
|
||||
def->label = L("Ground face at point");
|
||||
def->tooltip = L("Lay each object on the convex hull face that contains the point X,Y,Z and drop it onto the bed. "
|
||||
"The point is in object coordinates, which include the rotations given before this option; "
|
||||
"--inspect-mesh reports face centers in them. Objects without such a face are left as they are, and "
|
||||
"the run fails if no object has one. --orient 1 runs after all transforms and replaces the orientation.");
|
||||
def->cli_params = "X,Y,Z";
|
||||
def->set_default_value(new ConfigOptionString(""));
|
||||
|
||||
def = this->add("scale", coFloat);
|
||||
def->label = L("Scale");
|
||||
def->tooltip = L("Scale the model by a float factor.");
|
||||
|
||||
Reference in New Issue
Block a user