mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-09-16 13:32:44 +00:00
* 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.
49 lines
2.4 KiB
C++
49 lines
2.4 KiB
C++
#pragma once
|
|
|
|
#include "Point.hpp"
|
|
|
|
#include <vector>
|
|
|
|
namespace Slic3r {
|
|
|
|
class ModelObject;
|
|
|
|
// A face of an object's convex hull that the object can rest on. These are the faces the
|
|
// "Lay on Face" gizmo offers and the ones the CLI --ground-* options choose from.
|
|
//
|
|
// Frames: "object" coordinates have the volume transformations applied but not the instance
|
|
// transformation. "Instance" coordinates additionally have the instance rotation, scale and
|
|
// mirror applied, but not its offset.
|
|
struct LayOnFacePlane
|
|
{
|
|
Vec3d normal; // outward unit normal, object coordinates
|
|
Vec3d center; // centroid of the outline, object coordinates; on the face's mean plane
|
|
float area; // mm², instance coordinates
|
|
Pointf3s outline; // convex outline in the plane frame, where the face is horizontal
|
|
Transform3d to_plane_frame; // rotation from instance coordinates to the plane frame
|
|
};
|
|
|
|
// Candidate faces of the object's model parts, largest first. The instance transformation
|
|
// (without offset) is applied before measuring, so faces too small to rest on are dropped
|
|
// by their printed size: under 5 mm², a side under 1 mm, or an inner angle under 1°.
|
|
std::vector<LayOnFacePlane> lay_on_face_planes(const ModelObject &object, const Transform3d &instance_matrix_no_offset);
|
|
|
|
// Index of the largest plane, or -1 if `planes` is empty. Of planes with the same area, such as
|
|
// the top and bottom of a box, the one already facing down the most wins, so flat parts stay put.
|
|
int find_largest_plane(const std::vector<LayOnFacePlane> &planes);
|
|
|
|
// Index of the plane whose normal is closest to `direction` (object coordinates),
|
|
// or -1 if `planes` is empty.
|
|
int find_plane_by_normal(const std::vector<LayOnFacePlane> &planes, const Vec3d &direction);
|
|
|
|
// Index of the plane whose face contains `point` (object coordinates) within `tolerance` mm, or -1
|
|
// if there is none. `instance_matrix_no_offset` is the one the planes were computed with.
|
|
int find_plane_at_point(const std::vector<LayOnFacePlane> &planes, const Transform3d &instance_matrix_no_offset,
|
|
const Vec3d &point, double tolerance);
|
|
|
|
// Rotates the instance so that `normal` (object coordinates) points down, the same rotation as
|
|
// the gizmo applies, then drops the instance so its lowest point is at z = 0.
|
|
void lay_on_face(ModelObject &object, size_t instance_idx, const Vec3d &normal);
|
|
|
|
} // namespace Slic3r
|