The Design tab gets a grid a modeller can read, centred on the origin

In CAD the centre IS the sketch origin -- GLCanvas3D already moves the axis
triad there for exactly that reason. The grid under it did not agree: it comes
from PartPlate::calc_gridlines, generated from m_origin, the plate's front-left
corner, with an adaptive step meant for a print bed.

Measured on a screenshot from the user's machine: the nearest grid line was 10 px
from the origin in a 23 px pitch. The origin floated mid-cell, in both axes.

Corner-origin is CORRECT for Prepare -- a print bed starts at a corner -- and the
plate list is SHARED with the plater, so re-centring it there would change the
bed for every user of the app to fix one tab. The seam used instead already
existed: _render_platelist takes show_grid, and m_axes_at_bed_center is already
the "this is the Design canvas" flag. The Design canvas suppresses the plate's
grid and draws its own.

Minor every 10 mm, major every 50 mm, both generated from bed_center() so a line
passes exactly THROUGH the origin in each axis. Two GLModels, rebuilt only when
the bed shape changes, not per frame.

White majors, grey minors, the SAME in both themes. There is no white bed to
vanish against: the plate is dark grey either way (DEFAULT_MODEL_COLOR
{0.326,0.337,0.337} light, DEFAULT_MODEL_COLOR_DARK {0.255,0.255,0.283} dark),
a difference of 0.07. An earlier draft inverted the palette on the light theme;
that was a branch buying nothing. For contrast with what this replaces: the
plate's grid draws BOTH its thin and bold families in one 0.43 grey, which is
most of why the stock grid reads as a flat mesh with no scale to it.

z = -0.26, the same value as PartPlate::GROUND_Z_GRIDLINE -- below the bed fill
at -0.03, above the bed model at -0.41, so no z-fighting. Matched by
construction, since that constant is file-static in another TU.

Known limit, commented: the grid is clipped to the bed's BOUNDING BOX, not its
polygon. Identical on a rectangular bed; on a circular one it would spill past
the round edge. The target printers are rectangular.

Verified on the rig, not just compiled: white majors over a fine grey mesh, and
a white line through the origin in both axes.

snaporca-kha0
This commit is contained in:
Tommaso Bianchi
2026-09-01 04:49:09 +02:00
parent 8b9ff36685
commit 741682f874
2 changed files with 115 additions and 0 deletions
+101
View File
@@ -2081,6 +2081,11 @@ void GLCanvas3D::render(bool only_init)
no_partplate = true;
else if (gizmo_type == GLGizmosManager::BrimEars && !camera.is_looking_downward())
show_grid = false;
if (m_axes_at_bed_center)
// Design tab: the plate grid is generated from the plate's front-left corner, so it
// floats mid-cell under the modeling-origin triad. Suppress it here; a CAD grid centred
// on the origin is rendered in its place (see _render_cad_grid).
show_grid = false;
/* view3D render*/
int hover_id = (m_hover_plate_idxs.size() > 0)?m_hover_plate_idxs.front():-1;
@@ -2091,6 +2096,9 @@ void GLCanvas3D::render(bool only_init)
_render_bed(camera.get_view_matrix(), camera.get_projection_matrix(), !camera.is_looking_downward(), m_show_world_axes);
if (!no_partplate && m_show_bed) //BBS: add outline logic
_render_platelist(camera.get_view_matrix(), camera.get_projection_matrix(), !camera.is_looking_downward(), only_current, only_body, hover_id, true, show_grid);
if (m_axes_at_bed_center && m_show_bed && !no_partplate)
// Design tab: replace the plate's corner-origin grid with the origin-centred CAD grid.
_render_cad_grid(camera.get_view_matrix(), camera.get_projection_matrix());
//BBS: add outline logic
// Depth pass for object-on-object and self shadows; consumed by the gouraud shader below.
@@ -8084,6 +8092,99 @@ void GLCanvas3D::_render_platelist(const Transform3d& view_matrix, const Transfo
wxGetApp().plater()->get_partplate_list().render(view_matrix, projection_matrix, bottom, only_current, only_body, hover_id, render_cali, show_grid, !m_plate_chrome_enabled);
}
// Design tab: CAD grid on the bed plane, drawn in place of the plate's corner-origin grid.
// Generated from the bed centre (= modeling origin) so a grid line passes exactly through the
// triad in both axes. Minor lines every 10 mm, major every 50 mm; the two GLModels are built
// once and rebuilt only when the bed shape changes, not per frame.
void GLCanvas3D::_render_cad_grid(const Transform3d& view_matrix, const Transform3d& projection_matrix)
{
const BuildVolume& build_volume = m_bed.build_volume();
if (!build_volume.valid())
return;
const Vec2d center = build_volume.bed_center();
const BoundingBoxf bb = build_volume.bounding_volume2d();
if (!m_cad_grid_valid || m_cad_grid_center != center || m_cad_grid_bb != bb) {
m_cad_grid_center = center;
m_cad_grid_bb = bb;
m_cad_grid_valid = true;
// Same z as PartPlate::GROUND_Z_GRIDLINE (-0.26f): just below the bed fill (GROUND_Z =
// -0.03f, which is drawn with the depth mask disabled) and above the physical bed model
// (offset z = -0.41), so the grid never z-fights the bed quad. Chosen by construction,
// not by magic number: it is the exact z the plate grid already uses on the shared bed.
const float z = -0.26f;
auto build_grid = [&z, &center, &bb](double step, GLModel& model) {
std::vector<std::pair<Vec2d, Vec2d>> segs;
// Constant-x (vertical on screen) lines, both directions from the centre so the
// centre column itself is always present. Clipped to the bed bounding box so nothing
// spills past the bed quad.
for (double x = center.x(); x >= bb.min.x(); x -= step)
segs.emplace_back(Vec2d(x, bb.min.y()), Vec2d(x, bb.max.y()));
for (double x = center.x() + step; x <= bb.max.x(); x += step)
segs.emplace_back(Vec2d(x, bb.min.y()), Vec2d(x, bb.max.y()));
// Constant-y (horizontal on screen) lines, same centre-first convention.
for (double y = center.y(); y >= bb.min.y(); y -= step)
segs.emplace_back(Vec2d(bb.min.x(), y), Vec2d(bb.max.x(), y));
for (double y = center.y() + step; y <= bb.max.y(); y += step)
segs.emplace_back(Vec2d(bb.min.x(), y), Vec2d(bb.max.x(), y));
GLModel::Geometry data;
data.format = { GLModel::Geometry::EPrimitiveType::Lines, GLModel::Geometry::EVertexLayout::P3 };
data.reserve_vertices(2 * segs.size());
data.reserve_indices(2 * segs.size());
for (const auto& s : segs) {
data.add_vertex(Vec3f(float(s.first.x()), float(s.first.y()), z));
data.add_vertex(Vec3f(float(s.second.x()), float(s.second.y()), z));
const unsigned int vc = static_cast<unsigned int>(data.vertices_count());
data.add_line(vc - 2, vc - 1);
}
model.init_from(std::move(data));
};
m_cad_grid_minor.reset();
m_cad_grid_major.reset();
build_grid(10.0, m_cad_grid_minor);
build_grid(50.0, m_cad_grid_major);
}
if (!m_cad_grid_minor.is_initialized() || !m_cad_grid_major.is_initialized())
return;
GLShaderProgram* shader = wxGetApp().get_shader("flat");
if (shader == nullptr)
return;
shader->start_using();
glsafe(::glEnable(GL_BLEND));
glsafe(::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA));
shader->set_uniform("view_model_matrix", view_matrix);
shader->set_uniform("projection_matrix", projection_matrix);
// White every 5 cm, grey every 1 cm — the SAME in both themes, deliberately. There is no
// "white bed" to vanish against: the plate is dark grey either way, DEFAULT_MODEL_COLOR
// {0.326,0.337,0.337} on light and DEFAULT_MODEL_COLOR_DARK {0.255,0.255,0.283} on dark
// (3DBed.cpp:185-186), a difference of 0.07. A per-theme palette here would be a branch
// that buys nothing and one more thing to keep in step.
//
// For contrast with what this replaces: the plate's own grid uses LINE_TOP_DARK_COLOR, a
// 0.43 grey, for BOTH its thin and its bold family — which is most of why the stock grid
// reads as a flat mesh with no scale to it.
const ColorRGBA minor_color(0.40f, 0.40f, 0.42f, 1.0f);
const ColorRGBA major_color(0.90f, 0.90f, 0.90f, 1.0f);
glsafe(::glLineWidth(1.0f));
m_cad_grid_minor.set_color(minor_color);
m_cad_grid_minor.render();
glsafe(::glLineWidth(2.0f));
m_cad_grid_major.set_color(major_color);
m_cad_grid_major.render();
glsafe(::glDisable(GL_BLEND));
}
void GLCanvas3D::_render_shadows(const Transform3d& view_matrix, const Transform3d& projection_matrix)
{
if (wxGetApp().app_config == nullptr)
+14
View File
@@ -555,6 +555,16 @@ private:
// Design tab: draw the printer bed and its plate grid at all. Default true, so the
// main editor is untouched; the Design tab lets the user hide it to model without a bed.
bool m_show_bed{true};
// Design tab: CAD grid drawn on the bed plane in place of the plate's corner-origin grid.
// Two GLModels (10 mm minor / 50 mm major) generated from the bed centre so a line passes
// exactly through the modeling origin; built once and rebuilt only when the bed shape changes.
GLModel m_cad_grid_minor;
GLModel m_cad_grid_major;
// Geometry the CAD grid models were last built from, so they are rebuilt on bed-shape change
// rather than every frame.
BoundingBoxf m_cad_grid_bb;
Vec2d m_cad_grid_center;
bool m_cad_grid_valid{false};
#ifdef SLIC3R_CAD
DesignSketchTool* m_design_sketch_tool{nullptr};
#endif
@@ -1297,6 +1307,10 @@ private:
void _render_shadows(const Transform3d& view_matrix, const Transform3d& projection_matrix);
//BBS: add part plate related logic
void _render_platelist(const Transform3d& view_matrix, const Transform3d& projection_matrix, bool bottom, bool only_current, bool only_body = false, int hover_id = -1, bool render_cali = false, bool show_grid = true);
// Design tab: draw the CAD grid (minor 10 mm + major 50 mm) in place of the plate's
// corner-origin grid when the axes sit at the bed centre (modeling origin). Rebuilds its
// GLModels lazily, only when the bed shape changed.
void _render_cad_grid(const Transform3d& view_matrix, const Transform3d& projection_matrix);
//BBS: add outline drawing logic
void _render_objects(GLVolumeCollection::ERenderType type, bool with_outline = true);
void _render_wireframe_overlay();