mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-09-26 02:11:18 +00:00
Design tab: reference planes at bed centre, slot dims, hole cube handle, real threads
Port of the snaporca CAD work to the mainline fork. - Onshape default planes (XY/XZ/YZ) at the bed centre (transparent, labelled); modeling origin unified to the bed centre (CadDocument::modeling_origin); world-axis triad moved to the bed centre on the Design canvas only. - Datum plane: clickable ghost-plane base pick + draggable offset arrow. - Slot: dims reassessed to inter-centre distance / radius / angle; fixed the duplicate cap-arc radius quote. - Hole: 3D cube move-handle on the face; binds to the face on the first click; decluttered side-distance construction lines. - Thread: derive the M spec (diameter/pitch/depth) from a picked cylindrical surface or circular edge (GeometryEngine::circle_of_edge); fuse the helical ridge onto the existing body; MakePipeShell fixed-binormal sweep (uniform, no twist) + self-intersection/param guards. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BVzKmX6Y1aEteit1HTXG4Q
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
852804450c
commit
71b7a73e86
@@ -1516,3 +1516,90 @@ TEST_CASE("re-edit: multi-type timeline replays all downstream features", "[CadD
|
||||
Vec3d sz1 = doc.display_mesh.bounding_box().size();
|
||||
REQUIRE(sz1.z() > sz0.z() + 1.0);
|
||||
}
|
||||
|
||||
TEST_CASE("datum plane construction methods", "[CadDocument][plane]")
|
||||
{
|
||||
using Catch::Matchers::WithinAbs;
|
||||
|
||||
// Build a doc with a box (sketch rect 40x30 + extrude 20) so bodies[0] has faces/edges.
|
||||
CadDocument doc;
|
||||
int sk = doc.add_sketch(SketchShape::Rectangle, SketchPlane::XY(), 40, 30, 0, "BoxSketch");
|
||||
REQUIRE(sk >= 0);
|
||||
doc.add_extrude(sk, 20.0, false, BooleanMode::New, "Extrude1");
|
||||
REQUIRE(doc.recompute());
|
||||
REQUIRE(doc.error.empty());
|
||||
REQUIRE(doc.bodies.size() == 1);
|
||||
const int n_faces = GeometryEngine::face_count(doc.bodies[0].shape);
|
||||
REQUIRE(n_faces == 6); // a box
|
||||
const int n_edges = GeometryEngine::edge_count(doc.bodies[0].shape);
|
||||
REQUIRE(n_edges == 12);
|
||||
|
||||
// Find top face (normal ~ +Z) and bottom face (normal ~ -Z) by scanning.
|
||||
int top_idx = -1, bot_idx = -1;
|
||||
for (int i = 0; i < n_faces; ++i) {
|
||||
TopoDS_Face fc = GeometryEngine::face_by_index(doc.bodies[0].shape, i);
|
||||
Vec3d n = GeometryEngine::face_normal_world(fc);
|
||||
if (n.z() > 0.9) top_idx = i;
|
||||
if (n.z() < -0.9) bot_idx = i;
|
||||
}
|
||||
REQUIRE(top_idx >= 0);
|
||||
REQUIRE(bot_idx >= 0);
|
||||
|
||||
// --- Offset from base XY by 10 ---
|
||||
auto planes0 = doc.resolve_datum_planes();
|
||||
size_t initial = planes0.size();
|
||||
|
||||
CadFeature f0;
|
||||
f0.type = CadFeatureType::Plane;
|
||||
f0.name = "Offset10";
|
||||
f0.plane_base = 0; // XY
|
||||
f0.plane_type = PlaneType::Offset;
|
||||
f0.plane_offset = 10;
|
||||
doc.features.push_back(f0);
|
||||
|
||||
auto planes = doc.resolve_datum_planes();
|
||||
REQUIRE(planes.size() == initial + 1);
|
||||
CHECK_THAT(planes.back().second.origin.z(), WithinAbs(10.0, 1e-6));
|
||||
CHECK_THAT(planes.back().second.normal.z(), WithinAbs(1.0, 1e-6));
|
||||
|
||||
// --- Coincident to top face ---
|
||||
CadFeature f1;
|
||||
f1.type = CadFeatureType::Plane;
|
||||
f1.name = "CoincidentTop";
|
||||
f1.plane_type = PlaneType::Coincident;
|
||||
f1.plane_face_body = 0;
|
||||
f1.plane_face = top_idx;
|
||||
doc.features.push_back(f1);
|
||||
|
||||
planes = doc.resolve_datum_planes();
|
||||
REQUIRE(planes.size() == initial + 2);
|
||||
CHECK_THAT(planes.back().second.origin.z(), WithinAbs(20.0, 1e-6)); // box height is 20
|
||||
CHECK_THAT(planes.back().second.normal.z(), WithinAbs(1.0, 1e-6));
|
||||
|
||||
// --- Midplane between top and bottom faces ---
|
||||
CadFeature f2;
|
||||
f2.type = CadFeatureType::Plane;
|
||||
f2.name = "Midplane";
|
||||
f2.plane_type = PlaneType::Midplane;
|
||||
f2.plane_face_body = 0;
|
||||
f2.plane_face = top_idx;
|
||||
f2.plane_face2_body = 0;
|
||||
f2.plane_face2 = bot_idx;
|
||||
doc.features.push_back(f2);
|
||||
|
||||
planes = doc.resolve_datum_planes();
|
||||
REQUIRE(planes.size() == initial + 3);
|
||||
CHECK_THAT(planes.back().second.origin.z(), WithinAbs(10.0, 1e-6)); // midway
|
||||
CHECK_THAT(std::abs(planes.back().second.normal.z()), WithinAbs(1.0, 1e-6));
|
||||
|
||||
// --- Orthonormality check on all resolved planes ---
|
||||
for (const auto& [name, sp] : planes) {
|
||||
INFO("Plane: " << name);
|
||||
CHECK_THAT(sp.normal.norm(), WithinAbs(1.0, 1e-6));
|
||||
CHECK_THAT(sp.x_axis.norm(), WithinAbs(1.0, 1e-6));
|
||||
CHECK_THAT(sp.y_axis.norm(), WithinAbs(1.0, 1e-6));
|
||||
CHECK_THAT(std::abs(sp.x_axis.dot(sp.y_axis)), WithinAbs(0.0, 1e-6));
|
||||
CHECK_THAT(std::abs(sp.x_axis.dot(sp.normal)), WithinAbs(0.0, 1e-6));
|
||||
CHECK_THAT(std::abs(sp.y_axis.dot(sp.normal)), WithinAbs(0.0, 1e-6));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user