Design: in-canvas gizmos for Draft/Cut + operand highlight for Boolean/Sweep/Loft

Closes the gizmo-parity gap (bd snaporca-4h5): the five solid features that were
card-only now give in-canvas feedback like their siblings.

Draft — angle-arc drag gizmo (clone of the Revolve gizmo): once a side face is
picked, a cyan arc anchored at the face centroid shows the taper angle; drag the
tip or type the angle, the live ghost tapers with it. Axis = world +Z (the neutral
pull direction), clamped [-89, 89].

Cut — plane offset-arrow + cutting-plane rectangle (clone of the Shell arrow, adds
a wire rectangle in the cut plane sized to the target body bbox). The arrow drags
the signed offset along the plane normal; the rectangle rides at the cut position;
the ghost splits live. (Also covers bd snaporca-1gh / snaporca-mmr.)

Boolean / Sweep / Loft — operand highlighting (new by-index highlight infra):
- Boolean tints the target body teal-green and the tool body orange (per-index
  body tint added to the DesignCanvas GLVolume colour loop + set_operand_bodies).
- Sweep tints the profile sketch cyan and the path sketch magenta.
- Loft tints every selected profile sketch green.
  Sketch tints reuse the DisplaySketch overlay via a feature-index -> colour map
  (sketch_hl_color); DisplaySketch struct unchanged. All self-gate by active tool
  and clear on close_tool.

Wiring mirrors the existing gizmo pattern 1:1 (m_*_active / render_* / set_* /
clear_* / update_* in refresh_preview / DesignCanvas passthroughs / render dispatch
+ on_mouse drag branch). Both forks; DesignSketchTool.{cpp,hpp} + DesignCanvas.hpp
+ DesignPanel.hpp byte-identical across forks. Built clean on both. Draft, Cut and
Boolean highlight live-verified on :10; Sweep/Loft sketch tint is code-complete and
build-clean (visual check pending — needs hand-drawn profile/path sketches).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BVzKmX6Y1aEteit1HTXG4Q
This commit is contained in:
Tommaso Bianchi
2026-07-01 10:19:33 +02:00
co-authored by Claude Opus 4.8
parent 8cbf5b393b
commit 5a005d4728
6 changed files with 476 additions and 4 deletions
+74
View File
@@ -1907,6 +1907,16 @@ DesignPanel::DesignPanel(wxWindow* parent)
refresh_preview();
});
m_viewport->set_on_draft_angle_changed([this](double angle) {
if (m_draft_angle) m_draft_angle->SetValue(angle);
refresh_preview();
});
m_viewport->set_on_cut_offset_changed([this](double v) {
if (m_cut_offset) m_cut_offset->SetValue(v);
refresh_preview();
});
m_viewport->set_on_pattern_changed([this](double value) {
// Linear drag feeds spacing; circular drag feeds angle. The card knows which is live.
if (m_pattern_type && m_pattern_type->GetSelection() == 1) {
@@ -5171,6 +5181,60 @@ void DesignPanel::update_revolve_gizmo()
m_revolve_angle->GetValue(), m_revolve_flip->GetValue());
}
void DesignPanel::update_draft_gizmo()
{
if (!m_viewport) return;
if (m_active != Tool::Draft || m_sel_solid_face < 0 || m_sel_solid_body < 0
|| m_sel_solid_body >= int(m_doc.bodies.size())) {
m_viewport->clear_draft_gizmo();
return;
}
const TopoDS_Face f = GeometryEngine::face_by_index(m_doc.bodies[m_sel_solid_body].shape, m_sel_solid_face);
const Vec3d c = GeometryEngine::face_centroid_world(f);
const Vec3d n = GeometryEngine::face_normal_world(f);
m_viewport->set_draft_gizmo(c, n, m_draft_angle->GetValue());
}
void DesignPanel::update_cut_gizmo()
{
if (!m_viewport) return;
if (m_active != Tool::Cut) { m_viewport->clear_cut_gizmo(); return; }
const int bi = m_cut_target ? m_cut_target->GetSelection() : -1;
if (bi < 0 || bi >= int(m_doc.bodies.size()) || bi >= int(m_doc.display_body_meshes.size())) {
m_viewport->clear_cut_gizmo();
return;
}
const SketchPlane plane = plane_from_choice(m_cut_plane->GetSelection());
const BoundingBoxf3 bb = m_doc.display_body_meshes[bi].bounding_box();
const Vec3d center = bb.center();
const double half = std::max(0.5 * (bb.max - bb.min).norm(), 10.0);
m_viewport->set_cut_gizmo(plane, m_cut_offset->GetValue(), center, half);
}
void DesignPanel::update_operand_highlight()
{
if (!m_viewport) return;
// default: nothing highlighted
int bt = -1, bl = -1;
std::vector<std::pair<int, ColorRGBA>> sk;
if (m_active == Tool::Boolean) {
if (m_bool_target) bt = m_bool_target->GetSelection();
if (m_bool_tool) bl = m_bool_tool->GetSelection();
} else if (m_active == Tool::Sweep) {
if (m_sweep_profile_ref >= 0) sk.emplace_back(m_sweep_profile_ref, ColorRGBA(0.30f, 0.85f, 1.0f, 1.0f)); // profile = cyan
if (m_sweep_path_ref >= 0) sk.emplace_back(m_sweep_path_ref, ColorRGBA(1.00f, 0.40f, 0.90f, 1.0f)); // path = magenta
} else if (m_active == Tool::Loft) {
// checked rows of m_loft_list map to feature indices via m_loft_sketch_idx
// (exactly as build_candidate(Tool::Loft) reads them).
if (m_loft_list)
for (unsigned i = 0; i < m_loft_list->GetCount(); ++i)
if (m_loft_list->IsChecked(i) && i < m_loft_sketch_idx.size())
sk.emplace_back(m_loft_sketch_idx[i], ColorRGBA(0.40f, 0.90f, 0.50f, 1.0f)); // profiles = green
}
m_viewport->set_operand_bodies(bt, bl);
m_viewport->set_highlight_sketches(std::move(sk));
}
void DesignPanel::update_pattern_gizmo()
{
if (!m_viewport) return;
@@ -5449,6 +5513,12 @@ void DesignPanel::refresh_preview()
update_shell_gizmo();
// Same for the Revolve angle-arc around the axis (self-gates: only while the Revolve card is open).
update_revolve_gizmo();
// Same for the Draft angle-arc around the face centroid (self-gates: only while the Draft card is open).
update_draft_gizmo();
// Same for the Cut plane offset arrow + rectangle (self-gates: only while the Cut card is open).
update_cut_gizmo();
// Operand highlight for Boolean (body tints) / Sweep / Loft (sketch tints); self-gates by tool.
update_operand_highlight();
// Same for the Pattern spacing arrow / angle-arc (self-gates: only while the Pattern card is open).
update_pattern_gizmo();
// Datum-plane resize handles (self-gates: only while the Plane card is open).
@@ -5604,7 +5674,11 @@ void DesignPanel::close_tool()
m_viewport->clear_thread_gizmo();
m_viewport->clear_shell_gizmo();
m_viewport->clear_revolve_gizmo();
m_viewport->clear_draft_gizmo();
m_viewport->clear_cut_gizmo();
m_viewport->clear_pattern_gizmo();
m_viewport->set_operand_bodies(-1, -1);
m_viewport->set_highlight_sketches({});
m_viewport->clear_datum_gizmo();
update_reference_planes(); // back to no-tool: show the origin planes if there is no object yet
m_form->Layout();