mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-09-26 10:21:00 +00:00
Merge main
This commit is contained in:
@@ -1,5 +1,8 @@
|
||||
#include "libslic3r/libslic3r.h"
|
||||
#include "GLCanvas3D.hpp"
|
||||
#ifdef SLIC3R_CAD
|
||||
#include "slic3r/GUI/CAD/DesignSketchTool.hpp" // Design tab: interactive 2D sketch tool
|
||||
#endif
|
||||
|
||||
#include <igl/unproject.h>
|
||||
|
||||
@@ -1826,6 +1829,16 @@ void GLCanvas3D::enable_separator_toolbar(bool enable)
|
||||
m_separator_toolbar.set_enabled(enable);
|
||||
}
|
||||
|
||||
void GLCanvas3D::enable_collapse_toolbar(bool enable)
|
||||
{
|
||||
m_collapse_toolbar_enabled = enable;
|
||||
}
|
||||
|
||||
void GLCanvas3D::enable_plate_chrome(bool enable)
|
||||
{
|
||||
m_plate_chrome_enabled = enable;
|
||||
}
|
||||
|
||||
bool GLCanvas3D::has_mouse_capture() const {
|
||||
return m_canvas != nullptr && m_canvas->HasCapture();
|
||||
}
|
||||
@@ -2047,14 +2060,24 @@ 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;
|
||||
if (m_canvas_type == ECanvasType::CanvasView3D) {
|
||||
if (!no_partplate)
|
||||
// m_show_bed gates the plate list too: hiding the bed but leaving its grid and outline
|
||||
// floating would read as a rendering fault rather than a deliberate view option.
|
||||
if (!no_partplate && m_show_bed)
|
||||
_render_bed(camera.get_view_matrix(), camera.get_projection_matrix(), !camera.is_looking_downward(), m_show_world_axes);
|
||||
if (!no_partplate) //BBS: add outline logic
|
||||
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.
|
||||
@@ -2120,6 +2143,13 @@ void GLCanvas3D::render(bool only_init)
|
||||
if (_is_fxaa_enabled())
|
||||
_render_fxaa_pass(static_cast<unsigned int>(cnv_size.get_width()), static_cast<unsigned int>(cnv_size.get_height()));
|
||||
|
||||
// Design tab: interactive 2D sketch overlay, drawn over the scene but
|
||||
// beneath the UI overlays (toolbars, labels).
|
||||
#ifdef SLIC3R_CAD
|
||||
if (m_design_sketch_tool != nullptr && m_design_sketch_tool->has_display())
|
||||
m_design_sketch_tool->render(*this);
|
||||
#endif
|
||||
|
||||
// draw overlays
|
||||
_render_overlays();
|
||||
|
||||
@@ -3202,7 +3232,11 @@ void GLCanvas3D::on_idle(wxIdleEvent& evt)
|
||||
// BBS
|
||||
//m_dirty |= wxGetApp().plater()->get_view_toolbar().update_items_state();
|
||||
m_dirty |= wxGetApp().plater()->get_collapse_toolbar().update_items_state();
|
||||
bool mouse3d_controller_applied = wxGetApp().plater()->get_mouse3d_controller().apply(wxGetApp().plater()->get_camera());
|
||||
// apply() DRAINS the 3D-mouse queue, so only the canvas actually on screen may call it: a
|
||||
// hidden canvas renders nothing, so the motion it swallowed moves the shared camera without
|
||||
// ever being drawn and the next visible frame jumps several states at once.
|
||||
bool mouse3d_controller_applied = _is_shown_on_screen()
|
||||
&& wxGetApp().plater()->get_mouse3d_controller().apply(wxGetApp().plater()->get_camera());
|
||||
m_dirty |= mouse3d_controller_applied;
|
||||
m_dirty |= wxGetApp().plater()->get_notification_manager()->update_notifications(*this);
|
||||
auto gizmo = wxGetApp().plater()->get_view3D_canvas3D()->get_gizmos_manager().get_current();
|
||||
@@ -3270,6 +3304,64 @@ void GLCanvas3D::on_char(wxKeyEvent& evt)
|
||||
return;
|
||||
}
|
||||
|
||||
// Design tab: Delete/Backspace removes the selected sketch entities while a
|
||||
// sketch tool is active and the canvas has focus (dialog text fields are separate
|
||||
// wx controls, so this never eats their editing keys).
|
||||
#ifdef SLIC3R_CAD
|
||||
if (m_design_sketch_tool != nullptr && m_design_sketch_tool->is_active()
|
||||
&& (keyCode == WXK_DELETE || keyCode == WXK_BACK)
|
||||
&& !m_design_sketch_tool->selection().empty()) {
|
||||
m_design_sketch_tool->delete_selected();
|
||||
m_dirty = true;
|
||||
render();
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Esc exits the active sketch tool (Onshape-like, layered: abort in-progress entity ->
|
||||
// drop to Select -> exit the session back to Feature mode).
|
||||
#ifdef SLIC3R_CAD
|
||||
if (m_design_sketch_tool != nullptr && m_design_sketch_tool->is_active()
|
||||
&& keyCode == WXK_ESCAPE) {
|
||||
m_design_sketch_tool->request_exit();
|
||||
m_dirty = true;
|
||||
render();
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Design tab: Ctrl+Z / Ctrl+Shift+Z (and Ctrl+Y) undo/redo the Design feature
|
||||
// history. Scoped by m_design_sketch_tool — only the Design canvas owns one — so the
|
||||
// main 3D editor's undo/redo (the CanvasView3D-gated cases further below) is untouched.
|
||||
// Handled here, before the generic Ctrl block, so it takes precedence and early-returns.
|
||||
#ifdef SLIC3R_CAD
|
||||
if (m_design_sketch_tool != nullptr && (evt.GetModifiers() & ctrlMask) != 0) {
|
||||
const bool is_z = (keyCode == 'z' || keyCode == 'Z' || keyCode == WXK_CONTROL_Z);
|
||||
const bool is_y = (keyCode == 'y' || keyCode == 'Y' || keyCode == WXK_CONTROL_Y);
|
||||
if (is_z || is_y) {
|
||||
const bool redo = is_y || ((evt.GetModifiers() & shiftMask) != 0);
|
||||
m_design_sketch_tool->request_undo_redo(redo);
|
||||
m_dirty = true;
|
||||
render();
|
||||
return;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// Design tab: F = Place on Face (Prepare's lay-flat), when the Design viewport is up
|
||||
// and a body face is selected. The tool forwards to DesignPanel::place_on_face; it returns
|
||||
// false (no face picked) so F falls through to the default handler below.
|
||||
#ifdef SLIC3R_CAD
|
||||
if (m_design_sketch_tool != nullptr && m_design_sketch_tool->has_display()
|
||||
&& (keyCode == 'f' || keyCode == 'F') && (evt.GetModifiers() & ctrlMask) == 0) {
|
||||
if (m_design_sketch_tool->request_place_on_face()) {
|
||||
m_dirty = true;
|
||||
render();
|
||||
return;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
bool is_in_painting_mode = false;
|
||||
GLGizmoPainterBase *current_gizmo_painter = dynamic_cast<GLGizmoPainterBase *>(get_gizmos_manager().get_current());
|
||||
if (current_gizmo_painter != nullptr) {
|
||||
@@ -3642,6 +3734,20 @@ public:
|
||||
|
||||
void GLCanvas3D::on_key(wxKeyEvent& evt)
|
||||
{
|
||||
// Design tab: Delete/Backspace removes selected sketch entities. GTK delivers
|
||||
// these as KEY_DOWN rather than CHAR, so handle it here too.
|
||||
#ifdef SLIC3R_CAD
|
||||
if (evt.GetEventType() == wxEVT_KEY_DOWN
|
||||
&& m_design_sketch_tool != nullptr && m_design_sketch_tool->is_active()
|
||||
&& (evt.GetKeyCode() == WXK_DELETE || evt.GetKeyCode() == WXK_BACK)
|
||||
&& !m_design_sketch_tool->selection().empty()) {
|
||||
m_design_sketch_tool->delete_selected();
|
||||
m_dirty = true;
|
||||
render();
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
static GLCanvas3D const * thiz = nullptr;
|
||||
static TranslationProcessor translationProcessor(nullptr, nullptr);
|
||||
if (thiz != this) {
|
||||
@@ -4206,6 +4312,23 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
|
||||
return;
|
||||
}
|
||||
|
||||
// Design tab: the interactive sketch tool owns the mouse whenever it has
|
||||
// something on screen — an active session OR committed sketch overlays that the user
|
||||
// can click to select. It runs after ImGui (so dialogs still work) but before
|
||||
// camera/toolbar/gizmo handling; on_mouse returns false for events it doesn't consume
|
||||
// (drag/orbit/wheel) so the camera keeps working over the display-only plate.
|
||||
#ifdef SLIC3R_CAD
|
||||
if (m_design_sketch_tool != nullptr && m_design_sketch_tool->has_display()) {
|
||||
if (evt.LeftDown() && m_canvas != nullptr)
|
||||
m_canvas->SetFocus(); // grab keyboard focus so Delete/keys reach this canvas
|
||||
if (m_design_sketch_tool->on_mouse(evt, *this)) {
|
||||
m_dirty = true;
|
||||
render(); // force an immediate redraw so the sketch overlay updates live
|
||||
return;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef __WXMSW__
|
||||
bool on_enter_workaround = false;
|
||||
if (! evt.Entering() && ! evt.Leaving() && m_mouse.position.x() == -1.0) {
|
||||
@@ -4310,6 +4433,9 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
|
||||
if (can_sequential_clearance_show_in_gizmo())
|
||||
update_sequential_clearance();
|
||||
} else {
|
||||
// Orca: by-layer counterpart, for a prime tower compacted by "No sparse layers".
|
||||
if (current_printer_technology() == ptFFF && can_sequential_clearance_show_in_gizmo())
|
||||
update_compacted_wipe_tower_clearance();
|
||||
if (c == GLGizmosManager::EType::Move ||
|
||||
c == GLGizmosManager::EType::Scale ||
|
||||
c == GLGizmosManager::EType::Rotate)
|
||||
@@ -4543,8 +4669,12 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
|
||||
TransformationType trafo_type;
|
||||
trafo_type.set_relative();
|
||||
m_selection.translate(cur_pos - m_mouse.drag.start_position_3D, trafo_type);
|
||||
if (current_printer_technology() == ptFFF && (fff_print()->config().print_sequence == PrintSequence::ByObject))
|
||||
update_sequential_clearance();
|
||||
if (current_printer_technology() == ptFFF) {
|
||||
if (fff_print()->config().print_sequence == PrintSequence::ByObject)
|
||||
update_sequential_clearance();
|
||||
else
|
||||
update_compacted_wipe_tower_clearance();
|
||||
}
|
||||
// BBS
|
||||
//wxGetApp().obj_manipul()->set_dirty();
|
||||
m_dirty = true;
|
||||
@@ -4910,6 +5040,8 @@ bool GLCanvas3D::is_camera_rotate(const wxMouseEvent& evt, const std::map<MouseB
|
||||
{
|
||||
if (m_is_touchpad_navigation) {
|
||||
return evt.Moving() && evt.AltDown() && !evt.ShiftDown();
|
||||
} else if (m_cad_navigation) {
|
||||
return evt.Dragging() && evt.MiddleIsDown(); // left-drag is the selection rubber band
|
||||
} else {
|
||||
return evt.Dragging() && clicked_button_matches_action(evt, MouseAction::Rotation, mappings);
|
||||
}
|
||||
@@ -4919,6 +5051,8 @@ bool GLCanvas3D::is_camera_pan(const wxMouseEvent& evt, const std::map<MouseButt
|
||||
{
|
||||
if (m_is_touchpad_navigation) {
|
||||
return evt.Moving() && evt.ShiftDown() && !evt.AltDown();
|
||||
} else if (m_cad_navigation) {
|
||||
return evt.Dragging() && evt.RightIsDown(); // middle now orbits, so pan is right only
|
||||
} else {
|
||||
return evt.Dragging() && clicked_button_matches_action(evt, MouseAction::Pan, mappings);
|
||||
;
|
||||
@@ -5608,6 +5742,101 @@ bool GLCanvas3D::can_sequential_clearance_show_in_gizmo() {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Live preview of the compacted prime tower clearance, the by-layer counterpart of
|
||||
// update_sequential_clearance(). Called while the user drags a volume / gizmo; idle visibility
|
||||
// matches sequential print (hidden when valid, filled when Print::validate reports a collision).
|
||||
// Print::compacted_wipe_tower_clearance_valid() answers the same question authoritatively, but it
|
||||
// reads the tower position from the config, which only catches up once do_move() writes it back on
|
||||
// mouse release. Recomputing from the volumes here is what makes the keep-out zone follow the tower
|
||||
// while it is still under the cursor.
|
||||
void GLCanvas3D::update_compacted_wipe_tower_clearance()
|
||||
{
|
||||
if (current_printer_technology() != ptFFF)
|
||||
return;
|
||||
const Print *print = fff_print();
|
||||
if (print == nullptr)
|
||||
return;
|
||||
const PrintConfig &config = print->config();
|
||||
if (config.print_sequence != PrintSequence::ByLayer || ! wipe_tower_sparse_layers_skipped(config) || ! print->has_wipe_tower())
|
||||
return;
|
||||
|
||||
PartPlateList &plate_list = wxGetApp().plater()->get_partplate_list();
|
||||
PartPlate *plate = plate_list.get_curr_plate();
|
||||
if (plate == nullptr)
|
||||
return;
|
||||
const int plate_id = plate_list.get_curr_plate_index();
|
||||
|
||||
// Once the tower has been generated the scene shows its real mesh with the brim merged in,
|
||||
// otherwise it is a bare estimated cube with no brim at all. Only the latter needs the brim added
|
||||
// here, and the width comes from WipeTowerData, the same source the preview box is sized from, so
|
||||
// the zone cannot be padded against a brim the preview was not built with.
|
||||
const bool preview_carries_brim = print->is_step_done(psWipeTower) && print->wipe_tower_data().wipe_tower_mesh_data.has_value();
|
||||
const double brim = preview_carries_brim ? 0. : double(print->wipe_tower_data(print->extruders().size()).brim_width);
|
||||
const double padding = compacted_tower_footprint_padding(config, brim);
|
||||
|
||||
// Tower footprint straight from the volume the user sees, so that dragging either the tower or an
|
||||
// object updates the zone on the very next frame.
|
||||
Polygon tower_footprint;
|
||||
for (const GLVolume *v : m_volumes.volumes) {
|
||||
if (! v->is_wipe_tower || v->object_idx() - 1000 != plate_id)
|
||||
continue;
|
||||
const BoundingBoxf3 bbox = v->transformed_convex_hull_bounding_box();
|
||||
tower_footprint = Polygon({ Point(scale_(bbox.min.x() - padding), scale_(bbox.min.y() - padding)),
|
||||
Point(scale_(bbox.max.x() + padding), scale_(bbox.min.y() - padding)),
|
||||
Point(scale_(bbox.max.x() + padding), scale_(bbox.max.y() + padding)),
|
||||
Point(scale_(bbox.min.x() - padding), scale_(bbox.max.y() + padding)) });
|
||||
break;
|
||||
}
|
||||
|
||||
const CompactedTowerZone zone = compacted_wipe_tower_zone(config, tower_footprint);
|
||||
if (zone.empty()) {
|
||||
reset_sequential_print_clearance();
|
||||
return;
|
||||
}
|
||||
|
||||
// While dragging, outline every on-plate instance next to the tower ring, the way sequential print
|
||||
// outlines every object. Both carry half of the clearance, so the two outlines meeting is precisely
|
||||
// the moment that object goes over its limit - which is what makes the pair worth drawing at all.
|
||||
// The tier is per object, so a short object gets the narrow nozzle outline rather than the wide
|
||||
// body one it is not subject to; without that, a 3 mm object parked beside the tower would be drawn
|
||||
// deep inside the keep-out ring while passing the check. Only the instances that already exceed
|
||||
// allowed_rise also get a height limit plane.
|
||||
Polygons outlines;
|
||||
std::vector<std::pair<Polygon, float>> height_polygons;
|
||||
bool body_tier_used = false;
|
||||
const BoundingBox plate_bb = plate->get_bounding_box_crd();
|
||||
for (const ModelObject *model_object : m_model->objects) {
|
||||
for (size_t i = 0; i < model_object->instances.size(); ++i) {
|
||||
Geometry::Transformation trafo(model_object->instances[i]->get_transformation());
|
||||
const Vec3d offset = trafo.get_offset();
|
||||
trafo.set_offset(Vec3d(offset.x(), offset.y(), 0.0));
|
||||
const Polygon inst_hull = model_object->convex_hull_2d(trafo.get_matrix());
|
||||
if (inst_hull.points.empty() || ! plate_bb.overlap(inst_hull.bounding_box()))
|
||||
continue;
|
||||
|
||||
// Same tiers and the same rise measured from the plate as
|
||||
// Print::compacted_wipe_tower_clearance_valid(), so that the preview and the validation
|
||||
// that follows it never contradict each other.
|
||||
const double object_top = model_object->get_instance_max_z(i);
|
||||
const CompactedTowerClearance clearance = compacted_wipe_tower_clearance(config, zone, inst_hull, object_top);
|
||||
body_tier_used = body_tier_used || compacted_tower_body_tier(clearance);
|
||||
|
||||
const Polygon outline = compacted_wipe_tower_offender_outline(inst_hull, clearance.body_clearance);
|
||||
outlines.emplace_back(outline);
|
||||
if (object_top <= clearance.allowed_rise + EPSILON)
|
||||
continue;
|
||||
height_polygons.emplace_back(outline, float(clearance.allowed_rise));
|
||||
}
|
||||
}
|
||||
|
||||
Polygons polygons = compacted_wipe_tower_rings(zone, body_tier_used);
|
||||
append(polygons, outlines);
|
||||
|
||||
set_sequential_print_clearance_visible(true);
|
||||
set_sequential_print_clearance_render_fill(false);
|
||||
set_sequential_print_clearance_polygons(polygons, height_polygons);
|
||||
}
|
||||
|
||||
void GLCanvas3D::update_sequential_clearance()
|
||||
{
|
||||
if (current_printer_technology() != ptFFF || (fff_print()->config().print_sequence == PrintSequence::ByLayer))
|
||||
@@ -7917,13 +8146,113 @@ void GLCanvas3D::_render_bed(const Transform3d& view_matrix, const Transform3d&
|
||||
*/
|
||||
//bool show_texture = true;
|
||||
//BBS set axes mode
|
||||
m_bed.set_axes_mode(m_main_toolbar.is_enabled());
|
||||
if (m_axes_at_bed_center) {
|
||||
// Design tab: triad at the bed centre = modeling origin (set every frame because
|
||||
// set_shape/set_axes_mode otherwise reset it to the bed corner).
|
||||
const Vec2d bc = m_bed.build_volume().bed_center();
|
||||
m_bed.set_axes_origin(Vec3d(bc.x(), bc.y(), 0.0));
|
||||
} else {
|
||||
m_bed.set_axes_mode(m_main_toolbar.is_enabled());
|
||||
}
|
||||
m_bed.render(*this, view_matrix, projection_matrix, bottom, scale_factor, show_axes);
|
||||
}
|
||||
|
||||
void GLCanvas3D::_render_platelist(const Transform3d& view_matrix, const Transform3d& projection_matrix, bool bottom, bool only_current, bool only_body, int hover_id, bool render_cali, bool show_grid)
|
||||
{
|
||||
wxGetApp().plater()->get_partplate_list().render(view_matrix, projection_matrix, bottom, only_current, only_body, hover_id, render_cali, show_grid);
|
||||
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, ¢er, &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)
|
||||
@@ -9596,6 +9925,9 @@ void GLCanvas3D::_render_separator_toolbar_left() const
|
||||
|
||||
void GLCanvas3D::_render_collapse_toolbar() const
|
||||
{
|
||||
if (!m_collapse_toolbar_enabled)
|
||||
return;
|
||||
|
||||
auto& plater = *wxGetApp().plater();
|
||||
const auto sidebar_docking_dir = plater.get_sidebar_docking_state();
|
||||
if (sidebar_docking_dir == Sidebar::None) {
|
||||
|
||||
Reference in New Issue
Block a user