ifcviewer-full: length tool (2/3/4+ point distance, angle, polygon area)

ViewportWindow trades the area_tool_active_ bool for an enum ToolMode
{None, Area, Length}; the existing surfacePickedInTool signal carries
both, the app dispatches on toolMode().  Esc exits any active tool;
Backspace/Delete in length mode emits toolBackspacePressed which the
length tool uses to remove the last point.

LengthMeasurement collects clicked world-space points and adapts the
readout: 2pt → distance + axis-aligned ΔX/ΔY/ΔZ, 3pt → angle at the
middle vertex + triangle area, 4+pt → best-fit-plane PCA + shoelace
when planar (RMS plane distance / bbox diag < 1e-3) else fan
triangulation, with the chosen method labelled in the readout.  Per-
segment lengths float at each midpoint.

OverlayRenderer grows three new pipelines to support this:
  - point sprite shader: gl_PointCoord-based outlined disc with
    fwidth-smoothed inner/stroke bands, a single draw call.
  - line shader: CPU-expand each segment to 6 verts carrying both
    endpoints + (side, along) corner index; vertex shader computes
    the screen-space perpendicular and offsets accordingly.  Real
    outlined lines independent of the driver's glLineWidth clamp.
  - screen-space rect shader: HUD + label backgrounds drawn as raw
    GL quads in NDC.  QPainter::fillRect on QOpenGLPaintDevice was
    silently dropping fills across drivers; bypassing it entirely
    via this shader makes backgrounds reliable.  Cull-face is also
    explicitly disabled here — GL_TRIANGLES respects it but the
    line/point primitives don't, so this was the one path needing
    the fix.

setOverlayLines / setOverlayPoints take an inner color, an outline
color, and an extra-pixels-per-side stroke amount.  Lines + points
draw with GL_ALWAYS so measurement annotations stay visible through
geometry; highlight tris stay depth-aware (GL_LEQUAL) so area
shading still tints the surface in place.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
Dion Moult
2026-05-07 21:41:47 +10:00
parent c8d39cc481
commit 5bc7e4b98b
8 changed files with 1019 additions and 130 deletions
+52 -18
View File
@@ -230,12 +230,18 @@ public:
};
bool pickMeshLocalAt(int x, int y, MeshLocalPick& out);
// Area tool: while active, LMB clicks emit surfacePickedInTool with
// the click coordinates instead of swapping object selection — the
// app interprets them (typically by calling pickMeshLocalAt and
// accumulating triangle area). Esc exits.
// Measurement tool modes. While any tool is active, LMB clicks emit
// surfacePickedInTool with the click coordinates (instead of swapping
// object selection); the app interprets them per-tool. Esc exits the
// active tool. Backspace/Delete in length mode emits
// toolBackspacePressed for "remove last point" semantics.
enum class ToolMode { None, Area, Length };
Q_ENUM(ToolMode)
void toggleAreaTool();
bool areaToolActive() const { return area_tool_active_; }
void toggleLengthTool();
void setToolMode(ToolMode mode);
ToolMode toolMode() const { return tool_mode_; }
// Replace the overlay highlight-triangle list rendered after the main
// pass. `world_xyz` is 3 floats per vertex, 3 verts per triangle, in
@@ -244,10 +250,35 @@ public:
void setHighlightTriangles(const std::vector<float>& world_xyz,
float r, float g, float b, float a);
// Top-left HUD text drawn via QPainter on top of the GL surface at
// the end of each frame. Empty hides the HUD. Used today by the
// area-measurement tool for the running total; later tools can pile
// additional readouts in by extending this with a multi-line API.
// Replace the overlay-line list (3 floats per vertex, 2 verts per
// segment, world space). When stroke_a > 0 each segment is rendered
// with a wider (line_width + 2*stroke_extra) halo behind the inner
// line_width — proper outlined lines via screen-space-quad shader.
void setOverlayLines(const std::vector<float>& world_xyz,
float r, float g, float b, float a,
float line_width,
float stroke_r, float stroke_g, float stroke_b, float stroke_a,
float stroke_extra);
// Replace the overlay-point list (3 floats per point, world space).
// pixel_size is the inner-disc diameter in physical pixels; when
// stroke_a > 0 the sprite is enlarged by 2*stroke_extra to draw a
// halo around the inner disc — proper outlined points via the
// sprite shader (no two-pass glPointSize trickery).
void setOverlayPoints(const std::vector<float>& world_xyz,
float r, float g, float b, float a,
float pixel_size,
float stroke_r, float stroke_g, float stroke_b, float stroke_a,
float stroke_extra);
// Replace the world-anchored label list — projected to screen space
// and drawn via QPainter inside the same overlay pass as the HUD.
// Used today for per-segment length readouts.
void setOverlayLabels(const std::vector<OverlayRenderer::Label>& labels);
// Multi-line HUD text drawn via QPainter on top of the GL surface at
// the end of each frame. Empty hides the HUD. Newlines split the
// string into separate rows in the same translucent box.
void setHudText(const QString& text);
// Federation pipeline: composed instance transform =
@@ -344,14 +375,17 @@ signals:
void objectPicked(uint32_t object_id);
void initialized();
void frameStatsUpdated(const ViewportWindow::FrameStats& stats);
// Emitted instead of objectPicked when the area tool is active. The
// app is expected to call pickMeshLocalAt(x, y, ...) and accumulate.
// modifiers carries the Qt::KeyboardModifiers held at click time so
// the app can branch on Alt etc.
// Emitted instead of objectPicked when any measurement tool is active.
// The app branches on toolMode() to decide what to do, calls
// pickMeshLocalAt(x, y, ...) for hit details, and accumulates.
// modifiers carries Qt::KeyboardModifiers at click time (Alt etc.).
void surfacePickedInTool(int x, int y, int modifiers);
// Emitted whenever toggleAreaTool flips the mode. The app uses this
// to reset accumulator state on entry/exit.
void areaToolToggled(bool active);
// Emitted whenever the active tool changes (incl. on→off transitions).
// The app uses this to reset accumulator state on entry/exit.
void toolModeChanged(ViewportWindow::ToolMode mode);
// Backspace/Delete pressed while a tool is active. Used by the length
// tool to remove the last point; other tools may ignore it.
void toolBackspacePressed();
protected:
void exposeEvent(QExposeEvent* event) override;
@@ -651,8 +685,8 @@ private:
// Selection
uint32_t selected_object_id_ = 0;
// Area-measurement tool: see toggleAreaTool / surfacePickedInTool.
bool area_tool_active_ = false;
// Active measurement tool: see ToolMode / surfacePickedInTool.
ToolMode tool_mode_ = ToolMode::None;
// Renders any client-supplied overlay primitives (highlight triangles
// today; lines/points/labels later) in their own pass after the main