mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-14 03:14:23 +00:00
viewport: select a section plane, highlight it, delete the selected one
Previously Del always removed the most recently added section plane. Now a plane can be picked and deleted individually: - ViewportCore tracks a selected plane index, kept valid as planes are added (the new one becomes selected), removed, or cleared. - Clicking a gizmo with the section tool active selects that plane. - The section gizmo geometry is baked white and coloured via its per-plane tint, so the selected plane draws in a bright amber highlight while the rest stay red (unchanged look). - Del/Backspace removes the selected plane, falling back to the most recent one when nothing is selected. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -172,18 +172,20 @@ bool SectionGizmoRenderer::init(WGPUDevice device, WGPUQueue queue,
|
||||
if (!device_ || !queue_) return false;
|
||||
|
||||
// ---- Gizmo geometry: 9 line segments (quad outline + normal arrow) ----
|
||||
// Baked white so the per-plane `tint` uniform supplies the colour (red
|
||||
// normally, a highlight colour for the selected plane — see encode()).
|
||||
struct Seg { std::array<float, 3> s, e, c; };
|
||||
static constexpr std::array<float, 3> kRed = { 1.000f, 0.200f, 0.322f };
|
||||
static constexpr std::array<float, 3> kWhite = { 1.0f, 1.0f, 1.0f };
|
||||
static const Seg segs[] = {
|
||||
{ {-1, -1, 0}, { 1, -1, 0}, kRed }, // quad outline
|
||||
{ { 1, -1, 0}, { 1, 1, 0}, kRed },
|
||||
{ { 1, 1, 0}, {-1, 1, 0}, kRed },
|
||||
{ {-1, 1, 0}, {-1, -1, 0}, kRed },
|
||||
{ { 0, 0, 0}, { 0, 0, 1}, kRed }, // arrow shaft along +n
|
||||
{ { 0, 0, 1}, {-0.18f, 0, 0.78f}, kRed }, // arrow head
|
||||
{ { 0, 0, 1}, { 0.18f, 0, 0.78f}, kRed },
|
||||
{ { 0, 0, 1}, { 0, -0.18f, 0.78f}, kRed },
|
||||
{ { 0, 0, 1}, { 0, 0.18f, 0.78f}, kRed },
|
||||
{ {-1, -1, 0}, { 1, -1, 0}, kWhite }, // quad outline
|
||||
{ { 1, -1, 0}, { 1, 1, 0}, kWhite },
|
||||
{ { 1, 1, 0}, {-1, 1, 0}, kWhite },
|
||||
{ {-1, 1, 0}, {-1, -1, 0}, kWhite },
|
||||
{ { 0, 0, 0}, { 0, 0, 1}, kWhite }, // arrow shaft along +n
|
||||
{ { 0, 0, 1}, {-0.18f, 0, 0.78f}, kWhite }, // arrow head
|
||||
{ { 0, 0, 1}, { 0.18f, 0, 0.78f}, kWhite },
|
||||
{ { 0, 0, 1}, { 0, -0.18f, 0.78f}, kWhite },
|
||||
{ { 0, 0, 1}, { 0, 0.18f, 0.78f}, kWhite },
|
||||
};
|
||||
std::vector<float> verts;
|
||||
verts.reserve(std::size(segs) * 6 * 11);
|
||||
@@ -302,7 +304,8 @@ bool SectionGizmoRenderer::init(WGPUDevice device, WGPUQueue queue,
|
||||
|
||||
void SectionGizmoRenderer::encode(WGPURenderPassEncoder pass, const Eigen::Matrix4f& view_proj,
|
||||
const std::vector<SectionPlane>& planes,
|
||||
int viewport_w_px, int viewport_h_px, int device_pixel_ratio) {
|
||||
int viewport_w_px, int viewport_h_px, int device_pixel_ratio,
|
||||
int selected_index) {
|
||||
if (!pipeline_ || planes.empty()) return;
|
||||
wgpuRenderPassEncoderSetPipeline(pass, pipeline_);
|
||||
wgpuRenderPassEncoderSetVertexBuffer(pass, 0, vertex_buffer_, 0, WGPU_WHOLE_SIZE);
|
||||
@@ -321,9 +324,15 @@ void SectionGizmoRenderer::encode(WGPURenderPassEncoder pass, const Eigen::Matri
|
||||
// arrow would shoot past the eye (clip.w<0) and vanish.
|
||||
const float half = 1.0f;
|
||||
|
||||
// Red normally; a bright amber highlight for the selected plane.
|
||||
const bool selected = (i == selected_index);
|
||||
const float tr = selected ? 1.00f : 1.000f;
|
||||
const float tg = selected ? 0.75f : 0.200f;
|
||||
const float tb = selected ? 0.10f : 0.322f;
|
||||
|
||||
uint8_t slot[256];
|
||||
packSectionUniform(slot, view_proj, plane.origin, half, tangent, line_w,
|
||||
bitangent, nn, 1.0f, 1.0f, 1.0f, 1.0f, vw, vh);
|
||||
bitangent, nn, tr, tg, tb, 1.0f, vw, vh);
|
||||
const uint32_t slot_offset = uint32_t(i) * kSectionUniformSlot;
|
||||
wgpuQueueWriteBuffer(queue_, uniform_buffer_, slot_offset, slot, sizeof(slot));
|
||||
wgpuRenderPassEncoderSetBindGroup(pass, 0, bind_group_, 1, &slot_offset);
|
||||
|
||||
@@ -51,9 +51,11 @@ public:
|
||||
bool ready() const { return pipeline_ != nullptr; }
|
||||
|
||||
// Draw one gizmo per plane into an already-open render pass (the main pass).
|
||||
// `selected_index` (or -1) is drawn with a highlight tint to show selection.
|
||||
void encode(WGPURenderPassEncoder pass, const Eigen::Matrix4f& view_proj,
|
||||
const std::vector<SectionPlane>& planes,
|
||||
int viewport_w_px, int viewport_h_px, int device_pixel_ratio);
|
||||
int viewport_w_px, int viewport_h_px, int device_pixel_ratio,
|
||||
int selected_index = -1);
|
||||
|
||||
// Screen-space hit test: index of the plane whose gizmo (arrow segment,
|
||||
// origin→origin+normal) the (x, y) logical-pixel point lies within
|
||||
|
||||
@@ -6439,7 +6439,7 @@ void ViewportCore::render() {
|
||||
// Section-plane gizmo — shared renderer, drawn for desktop + web from here.
|
||||
// (The desktop's OverlayRenderer no longer draws it, to avoid doubling.)
|
||||
section_gizmo_.encode(pass, vp_this_frame, section_planes_,
|
||||
viewport_w_px, viewport_h_px, dpr_int);
|
||||
viewport_w_px, viewport_h_px, dpr_int, section_selected_index_);
|
||||
|
||||
// Remaining in-pass overlays (highlight triangles, pivot, overlay
|
||||
// lines/points). QtViewportHost forwards to overlays_.X(); web host no-ops.
|
||||
@@ -6728,6 +6728,8 @@ bool ViewportCore::addSectionPlaneAtSurface(const Eigen::Vector3f& point,
|
||||
p.d = -n.dot(point);
|
||||
p.visual_radius = (visual_radius > 0.0f) ? visual_radius : 1.0f;
|
||||
section_planes_.push_back(p);
|
||||
// The freshly added plane becomes the selected one.
|
||||
section_selected_index_ = int(section_planes_.size()) - 1;
|
||||
Log::info()
|
||||
<< "[wgpu section] added plane #" << section_planes_.size() - 1
|
||||
<< " origin=(" << point.x() << "," << point.y() << "," << point.z() << ")"
|
||||
@@ -6736,9 +6738,23 @@ bool ViewportCore::addSectionPlaneAtSurface(const Eigen::Vector3f& point,
|
||||
return true;
|
||||
}
|
||||
|
||||
void ViewportCore::setSelectedSectionPlane(int index) {
|
||||
const int clamped = (index >= 0 && index < int(section_planes_.size())) ? index : -1;
|
||||
if (clamped == section_selected_index_) return;
|
||||
section_selected_index_ = clamped;
|
||||
host_->requestFrame();
|
||||
}
|
||||
|
||||
void ViewportCore::removeSectionPlane(int index) {
|
||||
if (index < 0 || index >= int(section_planes_.size())) return;
|
||||
section_planes_.erase(section_planes_.begin() + index);
|
||||
// Keep the selection pointing at the same plane: clear it if it was the one
|
||||
// removed, shift it down if it sat after the removed index.
|
||||
if (section_selected_index_ == index) {
|
||||
section_selected_index_ = -1;
|
||||
} else if (section_selected_index_ > index) {
|
||||
--section_selected_index_;
|
||||
}
|
||||
Log::info() << "[wgpu section] removed plane " << index;
|
||||
host_->requestFrame();
|
||||
}
|
||||
@@ -6746,6 +6762,7 @@ void ViewportCore::removeSectionPlane(int index) {
|
||||
void ViewportCore::clearSectionPlanes() {
|
||||
if (section_planes_.empty()) return;
|
||||
section_planes_.clear();
|
||||
section_selected_index_ = -1;
|
||||
Log::info() << "[wgpu section] cleared all planes";
|
||||
host_->requestFrame();
|
||||
}
|
||||
|
||||
@@ -534,6 +534,11 @@ public:
|
||||
// Number of active section planes (0..kMaxSectionPlanes).
|
||||
int sectionPlaneCount() const { return int(section_planes_.size()); }
|
||||
|
||||
// The selected section plane (drawn highlighted; the target of a delete), or
|
||||
// -1 for none. Index is kept valid as planes are added/removed/cleared.
|
||||
void setSelectedSectionPlane(int index);
|
||||
int selectedSectionPlane() const { return section_selected_index_; }
|
||||
|
||||
// ---- Section gizmo interaction (shared desktop + web) -------------------
|
||||
//
|
||||
// All coords are LOGICAL (CSS) pixels; the core derives the logical viewport
|
||||
@@ -1057,6 +1062,7 @@ private:
|
||||
// the press point (logical px) so update can slide it along the normal.
|
||||
bool section_drag_active_ = false;
|
||||
int section_drag_index_ = -1;
|
||||
int section_selected_index_ = -1;
|
||||
Eigen::Vector3f section_drag_start_origin_ = Eigen::Vector3f::Zero();
|
||||
int section_drag_start_mx_ = 0;
|
||||
int section_drag_start_my_ = 0;
|
||||
|
||||
@@ -1554,6 +1554,7 @@ void ViewportWindow::mousePressEvent(QMouseEvent* event) {
|
||||
const Eigen::Vector2i lp = toV2i(event->position().toPoint());
|
||||
const int hit = core_.hitTestSectionGizmo(lp.x(), lp.y());
|
||||
if (hit >= 0 && core_.beginSectionDrag(hit, lp.x(), lp.y())) {
|
||||
core_.setSelectedSectionPlane(hit); // clicking a gizmo selects it
|
||||
nav_drag_kind_ = NavDrag::Inactive;
|
||||
Log::info().noquote().nospace()
|
||||
<< "[wgpu section] drag start: plane=" << hit;
|
||||
@@ -1918,9 +1919,10 @@ void ViewportWindow::keyPressEvent(QKeyEvent* event) {
|
||||
|
||||
// Section tool. K toggles the tool; Shift+K clears all planes. When
|
||||
// the tool is active, click adds a plane at the surface (handled in
|
||||
// mouseReleaseEvent), Esc deactivates, Del/Backspace removes the
|
||||
// most recently added plane. Mirrors GL ViewportWindow + Bonsai's
|
||||
// bind_shortcut(K / Shift+K) bindings.
|
||||
// mouseReleaseEvent) or selects the gizmo under the cursor, Esc
|
||||
// deactivates, Del/Backspace removes the selected plane (or the most
|
||||
// recent one when nothing is selected). Mirrors GL ViewportWindow +
|
||||
// Bonsai's bind_shortcut(K / Shift+K) bindings.
|
||||
if (key == Qt::Key_K && !event->isAutoRepeat()) {
|
||||
if (mods == Qt::ShiftModifier) {
|
||||
clearSectionPlanes();
|
||||
@@ -1936,7 +1938,11 @@ void ViewportWindow::keyPressEvent(QKeyEvent* event) {
|
||||
}
|
||||
if ((key == Qt::Key_Delete || key == Qt::Key_Backspace)
|
||||
&& !section_planes_.empty()) {
|
||||
removeSectionPlane(int(section_planes_.size()) - 1);
|
||||
// Delete the selected plane; fall back to the most recent one when
|
||||
// nothing is selected.
|
||||
const int selected = core_.selectedSectionPlane();
|
||||
removeSectionPlane(selected >= 0 ? selected
|
||||
: int(section_planes_.size()) - 1);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user