You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
677 lines
20 KiB
677 lines
20 KiB
// License: Apache 2.0. See LICENSE file in root directory.
|
|
// Copyright(c) 2020 Intel Corporation. All Rights Reserved.
|
|
|
|
#include "measurement.h"
|
|
#include "ux-window.h"
|
|
#include <rs-config.h>
|
|
#include <librealsense2/hpp/rs_export.hpp>
|
|
|
|
#include "opengl3.h"
|
|
|
|
|
|
using namespace rs2;
|
|
|
|
void measurement::enable() {
|
|
measurement_active = true;
|
|
config_file::instance().set(configurations::viewer::is_measuring, true);
|
|
}
|
|
void measurement::disable() {
|
|
state.points.clear();
|
|
state.edges.clear();
|
|
state.polygons.clear();
|
|
measurement_active = false;
|
|
config_file::instance().set(configurations::viewer::is_measuring, false);
|
|
}
|
|
bool measurement::is_enabled() const { return measurement_active; }
|
|
|
|
bool measurement::display_mouse_picked_tooltip() const{
|
|
return !(measurement_active && state.points.size() == 1) && !measurement_point_hovered;
|
|
}
|
|
|
|
bool measurement::manipulating() const { return dragging_measurement_point; }
|
|
|
|
std::vector<int> measurement_state::find_path(int from, int to)
|
|
{
|
|
std::map<int, int> parents;
|
|
std::deque<int> q;
|
|
|
|
q.push_back(from);
|
|
for (int i = 0; i < points.size(); i++)
|
|
{
|
|
parents[i] = -1;
|
|
}
|
|
|
|
while (q.size())
|
|
{
|
|
auto vert = q.front();
|
|
q.pop_front();
|
|
|
|
for (auto&& pair : edges)
|
|
{
|
|
int next = -1;
|
|
if (pair.first == vert) next = pair.second;
|
|
if (pair.second == vert) next = pair.first;
|
|
if (next >= 0 && parents[next] == -1)
|
|
{
|
|
parents[next] = vert;
|
|
q.push_back(next);
|
|
}
|
|
}
|
|
}
|
|
|
|
std::vector<int> path;
|
|
parents[from] = -1;
|
|
|
|
if (parents[to] != -1)
|
|
{
|
|
auto p = to;
|
|
while (p != -1)
|
|
{
|
|
path.push_back(p);
|
|
p = parents[p];
|
|
}
|
|
}
|
|
|
|
return path;
|
|
}
|
|
|
|
void measurement::add_point(interest_point p)
|
|
{
|
|
auto shift = ImGui::IsKeyDown(GLFW_KEY_LEFT_SHIFT) || ImGui::IsKeyDown(GLFW_KEY_RIGHT_SHIFT);
|
|
|
|
if (is_enabled())
|
|
{
|
|
commit_state();
|
|
|
|
if (state.points.size() >= 2 && !shift)
|
|
{
|
|
state.points.clear();
|
|
state.edges.clear();
|
|
state.polygons.clear();
|
|
}
|
|
|
|
int last = int(state.points.size());
|
|
if (current_hovered_point == -1 ||
|
|
current_hovered_point >= state.points.size())
|
|
{
|
|
state.points.push_back(p);
|
|
}
|
|
else
|
|
{
|
|
last = current_hovered_point;
|
|
}
|
|
|
|
int prev = last - 1;
|
|
if (last_hovered_point >= 0 && last_hovered_point < state.points.size())
|
|
prev = last_hovered_point;
|
|
|
|
if (state.edges.size())
|
|
{
|
|
auto path = state.find_path(prev, last);
|
|
if (path.size())
|
|
{
|
|
state.polygons.push_back(path);
|
|
|
|
std::vector<float3> poly;
|
|
for (auto&& idx : path) poly.push_back(state.points[idx].pos);
|
|
|
|
auto area = calculate_area(poly);
|
|
log_function( rsutils::string::from() << "Measured area of " << area_to_string(area));
|
|
}
|
|
}
|
|
|
|
if (state.points.size() >= 2)
|
|
{
|
|
auto dist = state.points[last].pos - state.points[prev].pos;
|
|
state.edges.push_back(std::make_pair(last, prev));
|
|
log_function( rsutils::string::from() << "Measured distance of " << length_to_string(dist.length()));
|
|
}
|
|
|
|
last_hovered_point = int(state.points.size() - 1);
|
|
|
|
commit_state();
|
|
}
|
|
}
|
|
|
|
std::string measurement::area_to_string(float area)
|
|
{
|
|
return rsutils::string::from() << std::setprecision(2) << area << " m";
|
|
}
|
|
|
|
std::string measurement::length_to_string(float distance)
|
|
{
|
|
std::string label;
|
|
if (is_metric())
|
|
{
|
|
if (distance < 0.01f)
|
|
{
|
|
label = rsutils::string::from() << std::setprecision(3) << distance * 1000.f << " mm";
|
|
} else if (distance < 1.f) {
|
|
label = rsutils::string::from() << std::setprecision(3) << distance * 100.f << " cm";
|
|
} else {
|
|
label = rsutils::string::from() << std::setprecision(3) << distance << " m";
|
|
}
|
|
} else
|
|
{
|
|
if (distance < 0.0254f)
|
|
{
|
|
label = rsutils::string::from() << std::setprecision(3) << distance * 1000.f << " mm";
|
|
} else if (distance < 0.3048f) {
|
|
label = rsutils::string::from() << std::setprecision(3) << distance / 0.0254 << " in";
|
|
} else if (distance < 0.9144) {
|
|
label = rsutils::string::from() << std::setprecision(3) << distance / 0.3048f << " ft";
|
|
} else {
|
|
label = rsutils::string::from() << std::setprecision(3) << distance / 0.9144 << " yd";
|
|
}
|
|
}
|
|
return label;
|
|
}
|
|
|
|
float measurement::calculate_area(std::vector<float3> poly)
|
|
{
|
|
if (poly.size() < 3) return 0.f;
|
|
|
|
float3 total{ 0.f, 0.f, 0.f };
|
|
|
|
for (int i = 0; i < poly.size(); i++)
|
|
{
|
|
auto v1 = poly[i];
|
|
auto v2 = poly[(i+1) % poly.size()];
|
|
auto prod = cross(v1, v2);
|
|
total = total + prod;
|
|
}
|
|
|
|
auto a = poly[1] - poly[0];
|
|
auto b = poly[2] - poly[0];
|
|
auto n = cross(a, b);
|
|
return std::abs( total * n.normalized() ) / 2;
|
|
}
|
|
|
|
void draw_sphere(const float3& pos, float r, int lats, int longs)
|
|
{
|
|
for(int i = 0; i <= lats; i++)
|
|
{
|
|
float lat0 = float(M_PI) * (-0.5f + (float) (i - 1) / lats);
|
|
float z0 = sin(lat0);
|
|
float zr0 = cos(lat0);
|
|
|
|
float lat1 = float(M_PI) * (-0.5f + (float) i / lats);
|
|
float z1 = sin(lat1);
|
|
float zr1 = cos(lat1);
|
|
|
|
glBegin(GL_QUAD_STRIP);
|
|
for(int j = 0; j <= longs; j++)
|
|
{
|
|
float lng = 2.f * float(M_PI) * (float) (j - 1) / longs;
|
|
float x = cos(lng);
|
|
float y = sin(lng);
|
|
|
|
glNormal3f(pos.x + x * zr0, pos.y + y * zr0, pos.z + z0);
|
|
glVertex3f(pos.x + r * x * zr0, pos.y + r * y * zr0, pos.z + r * z0);
|
|
glNormal3f(pos.x + x * zr1, pos.y + y * zr1, pos.z + z1);
|
|
glVertex3f(pos.x + r * x * zr1, pos.y + r * y * zr1, pos.z + r * z1);
|
|
}
|
|
glEnd();
|
|
}
|
|
}
|
|
|
|
rs2::float2 measurement::project_to_2d(rs2::float3 pos)
|
|
{
|
|
int32_t vp[4];
|
|
glGetIntegerv(GL_VIEWPORT, vp);
|
|
check_gl_error();
|
|
|
|
GLfloat model[16];
|
|
glGetFloatv(GL_MODELVIEW_MATRIX, model);
|
|
GLfloat proj[16];
|
|
glGetFloatv(GL_PROJECTION_MATRIX, proj);
|
|
|
|
rs2::matrix4 p(proj);
|
|
rs2::matrix4 v(model);
|
|
|
|
return translate_3d_to_2d(pos, p, v, rs2::matrix4::identity(), vp);
|
|
}
|
|
|
|
void measurement::draw_label(ux_window& win, float3 pos, float distance, int height, bool is_area)
|
|
{
|
|
auto w_pos = project_to_2d(pos);
|
|
std::string label = is_area ? area_to_string(distance) : length_to_string(distance);
|
|
if (is_area) ImGui::PushFont(win.get_large_font());
|
|
auto size = ImGui::CalcTextSize(label.c_str());
|
|
if (is_area) ImGui::PopFont();
|
|
|
|
std::string win_id = rsutils::string::from() << "measurement_" << id;
|
|
id++;
|
|
|
|
auto flags = ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoTitleBar;
|
|
|
|
if (!is_area)
|
|
{
|
|
ImGui::PushStyleColor(ImGuiCol_Text, regular_blue);
|
|
ImGui::PushStyleColor(ImGuiCol_WindowBg, almost_white_bg);
|
|
}
|
|
else
|
|
{
|
|
ImGui::PushStyleColor(ImGuiCol_Text, white);
|
|
ImGui::PushStyleColor(ImGuiCol_WindowBg, transparent);
|
|
}
|
|
ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 10);
|
|
ImGui::SetNextWindowPos(ImVec2(w_pos.x - size.x / 2, height - w_pos.y - size.y / 2 - 5));
|
|
ImGui::SetNextWindowSize(ImVec2(size.x + 20, size.y - 15));
|
|
ImGui::Begin(win_id.c_str(), nullptr, flags);
|
|
|
|
if (is_area) ImGui::PushFont(win.get_large_font());
|
|
ImGui::Text("%s", label.c_str());
|
|
if (is_area) {
|
|
ImGui::PopFont();
|
|
ImGui::SameLine();
|
|
ImGui::SetCursorPosX(ImGui::GetCursorPosX() - 7);
|
|
ImGui::Text("%s", "2");
|
|
}
|
|
|
|
ImGui::End();
|
|
ImGui::PopStyleVar();
|
|
ImGui::PopStyleColor(2);
|
|
}
|
|
|
|
void measurement::draw_ruler(ux_window& win, float3 from, float3 to, float height, int selected)
|
|
{
|
|
std::vector<float3> parts;
|
|
parts.push_back(from);
|
|
auto dir = to - from;
|
|
auto l = dir.length();
|
|
auto unit = is_metric() ? 0.01f : 0.0254f;
|
|
if (l > 0.5f) unit = is_metric() ? 0.1f : 0.03048f;
|
|
auto parts_num = l / unit;
|
|
for (int i = 0; i < parts_num; i++)
|
|
{
|
|
auto t = i / (float)parts_num;
|
|
parts.push_back(from + dir * t);
|
|
}
|
|
parts.push_back(to);
|
|
|
|
glLineWidth(3.f);
|
|
glBegin(GL_LINES);
|
|
for (int i = 1; i < parts.size(); i++)
|
|
{
|
|
auto alpha = selected == 0 ? 0.4f : 0.9f;
|
|
alpha = selected == 2 ? 1.f : alpha;
|
|
if (i % 2 == 0) glColor4f(1.f, 1.f, 1.f, alpha);
|
|
else glColor4f(light_blue.x, light_blue.y, light_blue.z, alpha);
|
|
auto from = parts[i-1];
|
|
auto to = parts[i]; // intentional shadowing
|
|
glVertex3d(from.x, from.y, from.z);
|
|
glVertex3d(to.x, to.y, to.z);
|
|
}
|
|
glEnd();
|
|
|
|
if (selected == 2)
|
|
{
|
|
// calculate center of the ruler line
|
|
float3 ctr = from + (to - from) / 2;
|
|
float distance = (to - from).length();
|
|
draw_label(win, ctr, distance, int(height));
|
|
}
|
|
}
|
|
|
|
void measurement::mouse_pick(ux_window& win, float3 picked, float3 normal)
|
|
{
|
|
_picked = picked; _normal = normal;
|
|
mouse_picked_event.add_value(!input_ctrl.mouse_down);
|
|
|
|
if (input_ctrl.click) {
|
|
add_point({ _picked, _normal });
|
|
}
|
|
|
|
if (is_enabled())
|
|
{
|
|
if (point_hovered(win) < 0 && hovered_edge_id < 0)
|
|
win.cross_hovered();
|
|
}
|
|
}
|
|
|
|
void measurement::update_input(ux_window& win, const rs2::rect& viewer_rect)
|
|
{
|
|
id = 0;
|
|
|
|
if (ImGui::IsKeyPressed('Z') || ImGui::IsKeyPressed('z'))
|
|
restore_state();
|
|
|
|
input_ctrl.prev_mouse_down = input_ctrl.mouse_down;
|
|
|
|
auto rect_copy = viewer_rect;
|
|
rect_copy.y += 60;
|
|
input_ctrl.click = false;
|
|
if (win.get_mouse().mouse_down[0] && !input_ctrl.mouse_down)
|
|
{
|
|
input_ctrl.mouse_down = true;
|
|
input_ctrl.down_pos = win.get_mouse().cursor;
|
|
input_ctrl.selection_started = win.time();
|
|
}
|
|
if (input_ctrl.mouse_down && !win.get_mouse().mouse_down[0])
|
|
{
|
|
input_ctrl.mouse_down = false;
|
|
if (win.time() - input_ctrl.selection_started < 0.5 &&
|
|
(win.get_mouse().cursor - input_ctrl.down_pos).length() < 100)
|
|
{
|
|
if (rect_copy.contains(win.get_mouse().cursor))
|
|
{
|
|
input_ctrl.click = true;
|
|
input_ctrl.click_time = float(glfwGetTime());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
int measurement::point_hovered(ux_window& win)
|
|
{
|
|
for (int i = 0; i < state.points.size(); i++)
|
|
{
|
|
auto&& point = state.points[i];
|
|
auto pos_2d = project_to_2d(point.pos);
|
|
pos_2d.y = win.framebuf_height() - pos_2d.y;
|
|
|
|
if ((pos_2d - win.get_mouse().cursor).length() < 15)
|
|
{
|
|
return i;
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
float distance_to_line(rs2::float2 a, rs2::float2 b, rs2::float2 p)
|
|
{
|
|
const float l2 = dot(b - a, b - a);
|
|
if (l2 == 0.0) return (p - a).length();
|
|
const float t = clamp(dot(p - a, b - a) / l2, 0.f, 1.f);
|
|
return (lerp(a, b, t) - p).length();
|
|
}
|
|
|
|
int measurement::edge_hovered(ux_window& win)
|
|
{
|
|
for (int i = 0; i < state.edges.size(); i++)
|
|
{
|
|
auto&& a = state.points[state.edges[i].first];
|
|
auto&& b = state.points[state.edges[i].second];
|
|
|
|
auto a_2d = project_to_2d(a.pos);
|
|
auto b_2d = project_to_2d(b.pos);
|
|
|
|
auto cursor = win.get_mouse().cursor;
|
|
cursor.y = win.framebuf_height() - cursor.y;
|
|
|
|
if (distance_to_line(a_2d, b_2d, cursor) < 15)
|
|
{
|
|
return i;
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
void measurement::commit_state()
|
|
{
|
|
state_history.push_back(state);
|
|
if (state_history.size() > 100)
|
|
{
|
|
state_history.pop_front();
|
|
}
|
|
}
|
|
|
|
void measurement::restore_state()
|
|
{
|
|
auto new_state = state;
|
|
while (state_history.size() && new_state == state)
|
|
{
|
|
new_state = state_history.back();
|
|
state_history.pop_back();
|
|
}
|
|
state = new_state;
|
|
}
|
|
|
|
void measurement::draw(ux_window& win)
|
|
{
|
|
auto shift = ImGui::IsKeyDown(GLFW_KEY_LEFT_SHIFT) || ImGui::IsKeyDown(GLFW_KEY_RIGHT_SHIFT);
|
|
|
|
auto p_idx = point_hovered(win);
|
|
if (p_idx >= 0 && !win.get_mouse().mouse_down[0])
|
|
{
|
|
_picked = state.points[p_idx].pos;
|
|
_normal = state.points[p_idx].normal;
|
|
}
|
|
if (mouse_picked_event.eval() && is_enabled())
|
|
{
|
|
glDisable(GL_DEPTH_TEST);
|
|
glLineWidth(2.f);
|
|
glEnable(GL_BLEND);
|
|
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
|
|
|
float size = _picked.z * 0.03f;
|
|
|
|
glBegin(GL_LINES);
|
|
glColor3f(1.f, 1.f, 1.f);
|
|
glVertex3d(_picked.x, _picked.y, _picked.z);
|
|
auto nend = _picked + _normal * size * 0.3f;
|
|
glVertex3d(nend.x, nend.y, nend.z);
|
|
glEnd();
|
|
|
|
glBegin(GL_TRIANGLES);
|
|
|
|
if (input_ctrl.mouse_down) size -= _picked.z * 0.01f;
|
|
size += _picked.z * 0.01f * single_wave(input_ctrl.click_period());
|
|
|
|
auto axis1 = cross(vec3d{ _normal.x, _normal.y, _normal.z }, vec3d{ 0.f, 1.f, 0.f });
|
|
auto faxis1 = float3 { axis1.x, axis1.y, axis1.z };
|
|
faxis1.normalized();
|
|
auto axis2 = cross(vec3d{ _normal.x, _normal.y, _normal.z }, axis1);
|
|
auto faxis2 = float3 { axis2.x, axis2.y, axis2.z };
|
|
faxis2.normalized();
|
|
|
|
matrix4 basis = matrix4::identity();
|
|
basis(0, 0) = faxis1.x;
|
|
basis(0, 1) = faxis1.y;
|
|
basis(0, 2) = faxis1.z;
|
|
|
|
basis(1, 0) = faxis2.x;
|
|
basis(1, 1) = faxis2.y;
|
|
basis(1, 2) = faxis2.z;
|
|
|
|
basis(2, 0) = _normal.x;
|
|
basis(2, 1) = _normal.y;
|
|
basis(2, 2) = _normal.z;
|
|
|
|
const int segments = 50;
|
|
for (int i = 0; i < segments; i++)
|
|
{
|
|
auto t1 = 2.f * float(M_PI) * ((float)i / segments);
|
|
auto t2 = 2.f * float(M_PI) * ((float)(i+1) / segments);
|
|
float4 xy1 { cosf(t1) * size, sinf(t1) * size, 0.f, 1.f };
|
|
xy1 = basis * xy1;
|
|
xy1 = float4 { _picked.x + xy1.x, _picked.y + xy1.y, _picked.z + xy1.z, 1.f };
|
|
float4 xy2 { cosf(t1) * size * 0.5f, sinf(t1) * size * 0.5f, 0.f, 1.f };
|
|
xy2 = basis * xy2;
|
|
xy2 = float4 { _picked.x + xy2.x, _picked.y + xy2.y, _picked.z + xy2.z, 1.f };
|
|
float4 xy3 { cosf(t2) * size * 0.5f, sinf(t2) * size * 0.5f, 0.f, 1.f };
|
|
xy3 = basis * xy3;
|
|
xy3 = float4 { _picked.x + xy3.x, _picked.y + xy3.y, _picked.z + xy3.z, 1.f };
|
|
float4 xy4 { cosf(t2) * size, sinf(t2) * size, 0.f, 1.f };
|
|
xy4 = basis * xy4;
|
|
xy4 = float4 { _picked.x + xy4.x, _picked.y + xy4.y, _picked.z + xy4.z, 1.f };
|
|
//glVertex3fv(&_picked.x);
|
|
|
|
glColor4f(white.x, white.y, white.z, 0.5f);
|
|
glVertex3fv(&xy1.x);
|
|
glColor4f(white.x, white.y, white.z, 0.8f);
|
|
glVertex3fv(&xy2.x);
|
|
glVertex3fv(&xy3.x);
|
|
|
|
glColor4f(white.x, white.y, white.z, 0.5f);
|
|
glVertex3fv(&xy1.x);
|
|
glVertex3fv(&xy4.x);
|
|
glColor4f(white.x, white.y, white.z, 0.8f);
|
|
glVertex3fv(&xy3.x);
|
|
}
|
|
//glVertex3fv(&_picked.x); glVertex3fv(&end.x);
|
|
glEnd();
|
|
|
|
if (state.points.size() == 1 || (shift && state.points.size()))
|
|
{
|
|
auto p0 = (last_hovered_point >= 0 && last_hovered_point < state.points.size())
|
|
? state.points[last_hovered_point] : state.points.back();
|
|
draw_ruler(win, _picked, p0.pos, win.framebuf_height(), 2);
|
|
}
|
|
|
|
glDisable(GL_BLEND);
|
|
glEnable(GL_DEPTH_TEST);
|
|
}
|
|
|
|
glEnable(GL_BLEND);
|
|
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
|
|
|
hovered_edge_id = edge_hovered(win);
|
|
|
|
for (auto&& poly : state.polygons)
|
|
{
|
|
auto ancor = state.points[poly.front()];
|
|
|
|
std::vector<float3> points;
|
|
points.push_back(ancor.pos);
|
|
|
|
auto mid = ancor.pos;
|
|
|
|
glDisable(GL_DEPTH_TEST);
|
|
|
|
glBegin(GL_TRIANGLES);
|
|
glColor4f(light_blue.x, light_blue.y, light_blue.z, 0.3f);
|
|
for (int i = 0; i < poly.size() - 1; i++)
|
|
{
|
|
auto b = state.points[poly[i]];
|
|
auto c = state.points[poly[i+1]];
|
|
glVertex3fv(&ancor.pos.x);
|
|
glVertex3fv(&b.pos.x);
|
|
glVertex3fv(&c.pos.x);
|
|
mid = mid + c.pos;
|
|
points.push_back(c.pos);
|
|
}
|
|
glEnd();
|
|
|
|
glEnable(GL_DEPTH_TEST);
|
|
|
|
mid = mid * (1.f / poly.size());
|
|
|
|
auto area = calculate_area(points);
|
|
|
|
draw_label(win, mid, area, int(win.framebuf_height()), true);
|
|
}
|
|
|
|
for (int i = 0; i < state.edges.size(); i++)
|
|
{
|
|
auto&& pair = state.edges[i];
|
|
glDisable(GL_DEPTH_TEST);
|
|
int selected = 1;
|
|
if (hovered_edge_id >= 0)
|
|
selected = hovered_edge_id == i ? 2 : 0;
|
|
draw_ruler(win, state.points[pair.second].pos, state.points[pair.first].pos, win.framebuf_height(), selected);
|
|
glEnable(GL_DEPTH_TEST);
|
|
}
|
|
|
|
if (win.get_mouse().mouse_down[1]) {
|
|
commit_state();
|
|
state.points.clear();
|
|
state.edges.clear();
|
|
state.polygons.clear();
|
|
}
|
|
|
|
for (auto&& points: state.points)
|
|
{
|
|
glColor4f(light_blue.x, light_blue.y, light_blue.z, 0.9f);
|
|
draw_sphere(points.pos, 0.011f, 20, 20);
|
|
}
|
|
glDisable(GL_DEPTH_TEST);
|
|
|
|
current_hovered_point = -1;
|
|
measurement_point_hovered = false;
|
|
int hovered_point = point_hovered(win);
|
|
if (hovered_point >= 0)
|
|
{
|
|
if (!shift) last_hovered_point = hovered_point;
|
|
current_hovered_point = hovered_point;
|
|
if (!input_ctrl.prev_mouse_down && win.get_mouse().mouse_down[0])
|
|
{
|
|
dragging_point_index = hovered_point;
|
|
measurement_point_hovered = true;
|
|
if (input_ctrl.click_period() > 0.5f)
|
|
{
|
|
dragging_measurement_point = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
int i = 0;
|
|
for (auto&& points: state.points)
|
|
{
|
|
if (measurement_point_hovered)
|
|
glColor4f(white.x, white.y, white.z, dragging_point_index == i ? 0.8f : 0.1f);
|
|
else
|
|
glColor4f(white.x, white.y, white.z, 0.6f);
|
|
|
|
draw_sphere(points.pos, dragging_point_index == i ? 0.012f : 0.008f, 20, 20);
|
|
i++;
|
|
}
|
|
|
|
glEnable(GL_DEPTH_TEST);
|
|
glDisable(GL_BLEND);
|
|
|
|
if (!win.get_mouse().mouse_down[0] || input_ctrl.click_period() < 0.5f)
|
|
{
|
|
if (dragging_measurement_point && state.points.size() >= 2)
|
|
{
|
|
dragging_measurement_point = false;
|
|
input_ctrl.click_time = 0;
|
|
|
|
for (auto&& e : state.edges)
|
|
{
|
|
if (e.first == dragging_point_index || e.second == dragging_point_index)
|
|
{
|
|
auto dist = state.points[e.first].pos - state.points[e.second].pos;
|
|
log_function( rsutils::string::from() << "Adjusted measurement to " << length_to_string(dist.length()));
|
|
}
|
|
}
|
|
|
|
for (auto&& path : state.polygons)
|
|
{
|
|
if (std::find(path.begin(), path.end(), dragging_point_index) != path.end())
|
|
{
|
|
std::vector<float3> poly;
|
|
for (auto&& idx : path) poly.push_back(state.points[idx].pos);
|
|
|
|
auto area = calculate_area(poly);
|
|
log_function( rsutils::string::from() << "Adjusted area of " << area_to_string(area));
|
|
}
|
|
}
|
|
|
|
commit_state();
|
|
}
|
|
dragging_point_index = -1;
|
|
}
|
|
if (dragging_measurement_point && dragging_point_index >= 0)
|
|
{
|
|
state.points[dragging_point_index].pos = _picked;
|
|
}
|
|
|
|
if (point_hovered(win) >= 0 || hovered_edge_id >= 0)
|
|
win.link_hovered();
|
|
}
|
|
|
|
void measurement::show_tooltip(ux_window& win)
|
|
{
|
|
if (mouse_picked_event.eval() && ImGui::IsWindowHovered())
|
|
{
|
|
if (display_mouse_picked_tooltip() && hovered_edge_id < 0)
|
|
{
|
|
std::string tt = rsutils::string::from() << std::fixed << std::setprecision(3)
|
|
<< _picked.x << ", " << _picked.y << ", " << _picked.z << " meters";
|
|
ImGui::SetTooltip("%s", tt.c_str());
|
|
}
|
|
}
|
|
}
|