Merge upstream, including section view and a faster preview

Five commits: a section view for the 3D canvas, a faster G-code preview, user
preset values kept on extruder variants they don't list, debug build CMake
fixes, and a 30 minute timeout on macOS notarization.

Two conflicts, both in GLCanvas3D. The header's were neighboring
declarations, kept from both sides. In the transparent pass, the section view
removed the assemble view's painter block that followed our ghost render;
the ghost render stays where it was, at the end of that pass.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Clifford Garwood
2026-10-02 15:14:03 -04:00
co-authored by Claude Opus 5.5
63 changed files with 2509 additions and 927 deletions
+36
View File
@@ -83,6 +83,7 @@
#include <imgui/imgui_internal.h>
#include <glad/gl.h>
#include <boost/functional/hash.hpp>
#include <boost/log/trivial.hpp>
#include <boost/algorithm/string/split.hpp>
#include <boost/nowide/cstdio.hpp>
@@ -2220,6 +2221,26 @@ void GCodeViewer::render_scene(int canvas_width, int canvas_height)
}
}
size_t GCodeViewer::shadow_casters_signature() const
{
size_t hash = m_viewer.get_vertices_count();
const libvgcode::Interval& visible = m_viewer.get_view_visible_range();
const libvgcode::Interval& layers = m_viewer.get_layers_view_range();
for (size_t value : { size_t(visible[0]), size_t(visible[1]), size_t(layers[0]), size_t(layers[1]) })
boost::hash_combine(hash, value);
for (double value : { m_paths_bounding_box.min.x(), m_paths_bounding_box.min.y(), m_paths_bounding_box.min.z(),
m_paths_bounding_box.max.x(), m_paths_bounding_box.max.y(), m_paths_bounding_box.max.z() })
boost::hash_combine(hash, value);
boost::hash_combine(hash, m_viewer.is_top_layer_only_view_range());
for (size_t i = 0; i < libvgcode::GCODE_EXTRUSION_ROLES_COUNT; ++i)
boost::hash_combine(hash, m_viewer.is_extrusion_role_visible(libvgcode::EGCodeExtrusionRole(i)));
boost::hash_combine(hash, m_viewer.is_option_visible(libvgcode::EOptionType::Travels));
boost::hash_combine(hash, m_viewer.is_option_visible(libvgcode::EOptionType::Wipes));
for (float value : m_clipping_plane)
boost::hash_combine(hash, value);
return hash;
}
void GCodeViewer::render_shadow_casters(const Transform3d& light_view_matrix, const Transform3d& light_projection_matrix, const Vec3d& light_position)
{
if (!has_data())
@@ -2238,11 +2259,26 @@ void GCodeViewer::set_shadow_map(int texture_unit, const Transform3d& light_view
intensity, texel_size);
}
void GCodeViewer::set_light_top_dir(const Vec3d& direction)
{
m_viewer.set_light_top_dir(libvgcode::convert(static_cast<Vec3f>(direction.cast<float>())));
}
void GCodeViewer::set_tone(float exposure, float saturation)
{
m_viewer.set_tone(exposure, saturation);
}
void GCodeViewer::set_clipping_plane(const ClippingPlane& plane)
{
// Flipped to match ClippingPlane::distance().
const Vec3f normal = -plane.get_normal().cast<float>();
m_clipping_plane = plane.is_active() ?
std::array<float, 4>{ normal.x(), normal.y(), normal.z(), float(plane.get_offset()) } :
std::array<float, 4>{ 0.0f, 0.0f, 0.0f, 1.0f };
m_viewer.set_clipping_plane(m_clipping_plane);
}
void GCodeViewer::render_overlay(int canvas_width, int canvas_height, int right_margin)
{
if (m_viewer.get_extrusion_roles().empty())