mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-10-11 01:41:03 +00:00
Show the Design tab's reference planes as separate labelled squares around the origin
The XY, XZ and YZ planes no longer cross through the bed. Each is a small square in its axis colour, set off from the axes into the corner that faces the default front view, with its name written in the plane. Dash-dot axes run between the squares and replace the bed's axis triad while they show. Hovering a plane greys it and selecting one makes it solid, and picking a solid face now clears a previously picked plane.
This commit is contained in:
@@ -59,6 +59,17 @@ void show_two_sketches(DesignSketchTool& tool)
|
||||
// The Design tab's base planes, all through one origin, as DesignPanel shows them.
|
||||
std::vector<SketchPlane> base_planes() { return { SketchPlane::XY(), SketchPlane::XZ(), SketchPlane::YZ() }; }
|
||||
|
||||
// The squares the base planes are drawn as, through a modeling origin at `origin`.
|
||||
std::vector<SketchPlane> reference_squares(const Vec3d& origin, double half)
|
||||
{
|
||||
std::vector<SketchPlane> squares;
|
||||
for (SketchPlane plane : base_planes()) {
|
||||
plane.origin += origin;
|
||||
squares.push_back(reference_square(plane, int(squares.size()), half));
|
||||
}
|
||||
return squares;
|
||||
}
|
||||
|
||||
struct View
|
||||
{
|
||||
Vec3d eye;
|
||||
@@ -108,25 +119,6 @@ std::optional<double> hit_distance(const PlanePiece& piece, const SketchPlane& p
|
||||
return t;
|
||||
}
|
||||
|
||||
// Whether x lies on one of the segments the pieces are outlined with.
|
||||
bool outlined(const std::vector<PlanePiece>& pieces, const Vec3d& x)
|
||||
{
|
||||
for (const PlanePiece& piece : pieces)
|
||||
for (const auto& [a, b] : piece.lines) {
|
||||
const Vec3d ab = b - a;
|
||||
const double t = std::clamp((x - a).dot(ab) / ab.squaredNorm(), 0., 1.);
|
||||
if ((a + ab * t - x).norm() < 1e-6)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<PlanePiece> pieces_of(const std::vector<SketchPlane>& planes)
|
||||
{
|
||||
const View& view = kViews[0];
|
||||
return planes_back_to_front(planes, 75., view.eye, view.forward, view.perspective);
|
||||
}
|
||||
|
||||
struct SightLines
|
||||
{
|
||||
int overlapping = 0; // sight lines through two or more pieces, where draw order matters
|
||||
@@ -134,15 +126,15 @@ struct SightLines
|
||||
};
|
||||
|
||||
// Translucent pieces blend correctly only if, along every line of sight, each piece is drawn after
|
||||
// every piece behind it.
|
||||
SightLines sight_lines(const std::vector<SketchPlane>& planes, double half, const View& view)
|
||||
// every piece behind it. The lines of sight aim at points of the box [lo, hi].
|
||||
SightLines sight_lines(const std::vector<SketchPlane>& planes, double half, const View& view, const Vec3d& lo, const Vec3d& hi)
|
||||
{
|
||||
const std::vector<PlanePiece> pieces = planes_back_to_front(planes, half, view.eye, view.forward, view.perspective);
|
||||
std::mt19937 rng(7);
|
||||
std::uniform_real_distribution<double> coord(-0.95 * half, 0.95 * half);
|
||||
std::uniform_real_distribution<double> unit(0., 1.);
|
||||
SightLines seen;
|
||||
for (int r = 0; r < 500; ++r) {
|
||||
const Vec3d target(coord(rng), coord(rng), coord(rng));
|
||||
const Vec3d target = lo + (hi - lo).cwiseProduct(Vec3d(unit(rng), unit(rng), unit(rng)));
|
||||
const Vec3d from = view.perspective ? view.eye : Vec3d(target - view.forward * (10. * half));
|
||||
const Vec3d dir = (target - from).normalized();
|
||||
double last = std::numeric_limits<double>::max();
|
||||
@@ -231,7 +223,7 @@ TEST_CASE("Fit frames nothing when the Design tab shows nothing", "[DesignSketch
|
||||
TEST_CASE("Crossing base planes are drawn back to front from any viewpoint", "[DesignSketchTool]")
|
||||
{
|
||||
const View& view = kViews[GENERATE(range(0, int(std::size(kViews))))];
|
||||
const SightLines seen = sight_lines(base_planes(), 75., view);
|
||||
const SightLines seen = sight_lines(base_planes(), 75., view, Vec3d::Constant(-0.95 * 75.), Vec3d::Constant(0.95 * 75.));
|
||||
CHECK(seen.overlapping >= 200); // of the 500: most lines of sight into the planes cross two
|
||||
CHECK(seen.out_of_order == 0);
|
||||
}
|
||||
@@ -250,51 +242,11 @@ TEST_CASE("Datum planes are drawn back to front among the base planes", "[Design
|
||||
planes.push_back(tilted);
|
||||
|
||||
const View& view = kViews[GENERATE(range(0, int(std::size(kViews))))];
|
||||
const SightLines seen = sight_lines(planes, 75., view);
|
||||
const SightLines seen = sight_lines(planes, 75., view, Vec3d::Constant(-0.95 * 75.), Vec3d::Constant(0.95 * 75.));
|
||||
CHECK(seen.overlapping >= 200);
|
||||
CHECK(seen.out_of_order == 0);
|
||||
}
|
||||
|
||||
TEST_CASE("Base planes are outlined along every line where they cross", "[DesignSketchTool]")
|
||||
{
|
||||
// XY, XZ and YZ cross along the three axes, all through the middle of each 75 mm half-square.
|
||||
const std::vector<PlanePiece> pieces = pieces_of(base_planes());
|
||||
int missed = 0;
|
||||
for (double t = -70.; t <= 70.; t += 10.)
|
||||
for (const Vec3d& axis : { Vec3d(1., 0., 0.), Vec3d(0., 1., 0.), Vec3d(0., 0., 1.) })
|
||||
missed += outlined(pieces, axis * t) ? 0 : 1;
|
||||
CHECK(missed == 0);
|
||||
}
|
||||
|
||||
TEST_CASE("A datum clear of the base planes gets no lines across it", "[DesignSketchTool]")
|
||||
{
|
||||
// Parallel to YZ at x = 100, past the 75 mm half-squares of XY and XZ: the infinite XY and XZ
|
||||
// planes still cut it, along z = 0 and y = 0, but the squares never meet.
|
||||
std::vector<SketchPlane> planes = base_planes();
|
||||
SketchPlane beyond = SketchPlane::YZ();
|
||||
beyond.origin = Vec3d(100., 0., 0.);
|
||||
planes.push_back(beyond);
|
||||
|
||||
const std::vector<PlanePiece> pieces = pieces_of(planes);
|
||||
CHECK_FALSE(outlined(pieces, Vec3d(100., 30., 0.)));
|
||||
CHECK_FALSE(outlined(pieces, Vec3d(100., 0., 30.)));
|
||||
}
|
||||
|
||||
TEST_CASE("A line where two squares cross stops where either square ends", "[DesignSketchTool]")
|
||||
{
|
||||
// Parallel to XY 30 mm up and moved 60 mm along X, so it spans x = -15..135. It meets XZ along
|
||||
// y = 0, z = 30, but XZ's square only reaches x = 75.
|
||||
std::vector<SketchPlane> planes = base_planes();
|
||||
SketchPlane raised = SketchPlane::XY();
|
||||
raised.origin = Vec3d(60., 0., 30.);
|
||||
planes.push_back(raised);
|
||||
|
||||
const std::vector<PlanePiece> pieces = pieces_of(planes);
|
||||
CHECK(outlined(pieces, Vec3d(0., 0., 30.)));
|
||||
CHECK(outlined(pieces, Vec3d(70., 0., 30.)));
|
||||
CHECK_FALSE(outlined(pieces, Vec3d(100., 0., 30.)));
|
||||
}
|
||||
|
||||
TEST_CASE("Cutting the base planes along each other keeps every plane whole", "[DesignSketchTool]")
|
||||
{
|
||||
const std::vector<SketchPlane> planes = base_planes();
|
||||
@@ -307,6 +259,98 @@ TEST_CASE("Cutting the base planes along each other keeps every plane whole", "[
|
||||
CHECK_THAT(a, WithinRel(4. * half * half, 1e-9));
|
||||
}
|
||||
|
||||
TEST_CASE("Base reference planes sit in the octant their names face, clear of the axes", "[DesignSketchTool]")
|
||||
{
|
||||
// Each square is flat in one coordinate and at least `gap` out along the other two, toward
|
||||
// (+X, -Y, +Z), so no two of them meet: a point of XY has z = 0, every point of XZ and YZ has
|
||||
// z >= gap.
|
||||
const Vec3d origin(128., 128., 0.), octant(1., -1., 1.);
|
||||
const double half = 32., gap = reference_square_gap(half);
|
||||
const std::vector<SketchPlane> squares = reference_squares(origin, half);
|
||||
const int flat[] = { 2, 1, 0 }; // XY in z, XZ in y, YZ in x
|
||||
for (int i = 0; i < 3; ++i) {
|
||||
// The side a name reads from faces into the same octant.
|
||||
CHECK(squares[i].x_axis.cross(squares[i].y_axis).dot(octant) > 0.);
|
||||
for (const Vec2d& corner : { Vec2d(-half, -half), Vec2d(half, -half), Vec2d(half, half), Vec2d(-half, half) }) {
|
||||
const Vec3d x = squares[i].to_world(corner) - origin;
|
||||
for (int k = 0; k < 3; ++k)
|
||||
if (k == flat[i])
|
||||
CHECK_THAT(x[k], WithinAbs(0., 1e-9));
|
||||
else {
|
||||
CHECK(octant[k] * x[k] >= gap - 1e-9);
|
||||
CHECK(octant[k] * x[k] <= gap + 2. * half + 1e-9);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("From the front no base reference plane stands behind another", "[DesignSketchTool]")
|
||||
{
|
||||
// Looking from the front and above, as the Design tab opens: XY lies below the X axis on screen
|
||||
// and XZ above it, and YZ is seen edge-on, so no line of sight crosses two squares.
|
||||
const double half = 32., e = reference_square_gap(half) + 2. * half;
|
||||
const std::vector<SketchPlane> squares = reference_squares(Vec3d::Zero(), half);
|
||||
const double elevation = M_PI / 180. * GENERATE(20., 45., 70.);
|
||||
const View front{ Vec3d::Zero(), Vec3d(0., std::cos(elevation), -std::sin(elevation)), false };
|
||||
CHECK(sight_lines(squares, half, front, Vec3d(0., -e, 0.), Vec3d(e, 0., e)).overlapping == 0);
|
||||
}
|
||||
|
||||
TEST_CASE("A datum's reference square stays centred on its origin", "[DesignSketchTool]")
|
||||
{
|
||||
SketchPlane datum = SketchPlane::XY();
|
||||
datum.origin = Vec3d(10., 20., 30.);
|
||||
CHECK((reference_square(datum, 3, 32.).origin - datum.origin).norm() < 1e-12);
|
||||
}
|
||||
|
||||
TEST_CASE("Base reference planes are drawn back to front without being cut", "[DesignSketchTool]")
|
||||
{
|
||||
const double half = 32.;
|
||||
const std::vector<SketchPlane> squares = reference_squares(Vec3d::Zero(), half);
|
||||
int overlapping = 0;
|
||||
for (const View& view : kViews) {
|
||||
const std::vector<PlanePiece> pieces = planes_back_to_front(squares, half, view.eye, view.forward, view.perspective);
|
||||
CHECK(pieces.size() == 3);
|
||||
for (const PlanePiece& piece : pieces)
|
||||
CHECK(piece.corners.size() == 4);
|
||||
const double e = reference_square_gap(half) + 2. * half;
|
||||
const SightLines seen = sight_lines(squares, half, view, Vec3d(0., -e, 0.), Vec3d(e, 0., e));
|
||||
CHECK(seen.out_of_order == 0);
|
||||
overlapping += seen.overlapping;
|
||||
}
|
||||
// These views do see squares behind one another, so the order is actually put to the test.
|
||||
CHECK(overlapping >= 50);
|
||||
}
|
||||
|
||||
TEST_CASE("A click takes the nearest reference square along the ray", "[DesignSketchTool]")
|
||||
{
|
||||
const double half = 32.; // the squares span 8..72 out along both their axes
|
||||
const std::vector<SketchPlane> squares = reference_squares(Vec3d::Zero(), half);
|
||||
// From the front left through YZ (x = 0) at (0, -20, 40), then on through XZ (y = 0) at (20, 0, 40).
|
||||
const Vec3d along(1., 1., 0.);
|
||||
CHECK(pick_reference_square(squares, half, Vec3d(-40., -60., 40.), along) == 2);
|
||||
// The same line walked the other way meets XZ first.
|
||||
CHECK(pick_reference_square(squares, half, Vec3d(60., 40., 40.), -along) == 1);
|
||||
// Straight down onto XY, and down the gap beside it, which holds nothing.
|
||||
CHECK(pick_reference_square(squares, half, Vec3d(40., -40., 100.), Vec3d(0., 0., -1.)) == 0);
|
||||
CHECK(pick_reference_square(squares, half, Vec3d(4., -40., 100.), Vec3d(0., 0., -1.)) == -1);
|
||||
// Looking away from the squares.
|
||||
CHECK(pick_reference_square(squares, half, Vec3d(40., -40., 100.), Vec3d(0., 0., 1.)) == -1);
|
||||
}
|
||||
|
||||
TEST_CASE("A base plane's name lies inside its own square", "[DesignSketchTool]")
|
||||
{
|
||||
const double half = 32.;
|
||||
const std::vector<SketchPlane> squares = reference_squares(Vec3d(128., 128., 0.), half);
|
||||
const char* names[] = { "XY", "XZ", "YZ" };
|
||||
for (int i = 0; i < 3; ++i)
|
||||
for (const Vec3d& corner : reference_label_box(squares[i], half, names[i])) {
|
||||
const Vec3d d = corner - squares[i].origin;
|
||||
CHECK_THAT(d.dot(squares[i].x_axis.cross(squares[i].y_axis)), WithinAbs(0., 1e-9));
|
||||
CHECK(std::abs(d.dot(squares[i].x_axis)) < half);
|
||||
CHECK(std::abs(d.dot(squares[i].y_axis)) < half);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("A move arrow drag moves the body by the cursor's travel, wherever the arrow is grabbed", "[DesignSketchTool]")
|
||||
{
|
||||
DesignSketchTool tool;
|
||||
|
||||
Reference in New Issue
Block a user