|
|
@@ -0,0 +1,181 @@
|
|
|
+#include "cursor_guide_impl.h"
|
|
|
+#include "core/imgui_utility.hpp"
|
|
|
+
|
|
|
+#include <glm/gtx/transform.hpp>
|
|
|
+
|
|
|
+#include <vtkSphereSource.h>
|
|
|
+
|
|
|
+#include <unordered_set>
|
|
|
+
|
|
|
+namespace cursor_guide_impl {
|
|
|
+
|
|
|
+ void point_set::emplace(const glm::vec3 &p, float r) {
|
|
|
+ // convert to object coordinate
|
|
|
+ auto obj_p_h = glm::inverse(transform) * to_homo(p);
|
|
|
+ c.emplace_back(from_homo(obj_p_h), r);
|
|
|
+ }
|
|
|
+
|
|
|
+ bool point_set::try_picking(const glm::vec3 &tp) {
|
|
|
+ auto ret = false;
|
|
|
+ for (auto &point: c) {
|
|
|
+ // convert to world coordinate
|
|
|
+ auto wp = transform_p(transform, point.position);
|
|
|
+ if (glm::distance(wp, tp) <= point.radius) {
|
|
|
+ point.is_picked = true;
|
|
|
+ ret = true;
|
|
|
+ } else {
|
|
|
+ point.is_picked = false;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return ret;
|
|
|
+ }
|
|
|
+
|
|
|
+ void point_set::remove_picked() {
|
|
|
+ c.remove_if([](auto &p) {
|
|
|
+ return p.is_picked;
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ mesh_cache_type mesh_cache;
|
|
|
+
|
|
|
+ mesh_ptr get_mesh(float radius) {
|
|
|
+ if (auto iter = mesh_cache.find(radius);
|
|
|
+ iter != mesh_cache.end()) {
|
|
|
+ return iter->second;
|
|
|
+ }
|
|
|
+ vtkNew<vtkSphereSource> source;
|
|
|
+ source->SetRadius(radius);
|
|
|
+ source->SetPhiResolution(16);
|
|
|
+ source->SetThetaResolution(16);
|
|
|
+ source->SetCenter(0, 0, 0);
|
|
|
+ source->Update();
|
|
|
+ auto mesh = mesh_type::from_vtk(source->GetOutput());
|
|
|
+ mesh_cache.emplace(radius, mesh);
|
|
|
+ return mesh;
|
|
|
+ }
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+void cursor_guide::impl::binding_store_type::update_transform() {
|
|
|
+ if (pimpl->sophiar_conn == nullptr) return;
|
|
|
+ auto trans = pimpl->sophiar_conn->
|
|
|
+ query_transform_variable(transform_var);
|
|
|
+ if (trans.has_value()) {
|
|
|
+ points.transform = to_mat4(*trans);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+cursor_guide::impl::impl(const create_config &conf) {
|
|
|
+ sophiar_conn = conf.sophiar_conn;
|
|
|
+ std::ranges::transform(conf.item_list, std::back_inserter(binding_list),
|
|
|
+ [this](auto &item) { return binding_store_type(this, item); });
|
|
|
+ if (conf.item_list.empty()) {
|
|
|
+ binding_list.emplace_back(this, binding_store_base_type{.disp_name = "Null"});
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+void cursor_guide::impl::update(const guide_info &info) {
|
|
|
+ free_pos = {};
|
|
|
+ if (!info.position) return;
|
|
|
+ auto wp = *info.position;
|
|
|
+
|
|
|
+ bool picked = false;
|
|
|
+ for (auto &bind: binding_list) {
|
|
|
+ picked |= bind.points.try_picking(wp);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (picked) {
|
|
|
+ if (info.mouse_right) {
|
|
|
+ for (auto &bind: binding_list) {
|
|
|
+ bind.points.remove_picked();
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ if (info.mouse_left) {
|
|
|
+ assert(current_binding < binding_list.size());
|
|
|
+ binding_list[current_binding]
|
|
|
+ .points.emplace(wp, current_radius);
|
|
|
+ } else {
|
|
|
+ free_pos = wp;
|
|
|
+ current_radius += info.mouse_wheel;
|
|
|
+ current_radius = std::max(current_radius, 1.0f);
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+void cursor_guide::impl::show() {
|
|
|
+ auto bind_preview = binding_list[current_binding].disp_name.c_str();
|
|
|
+ if (ImGui::BeginCombo("Point Binding", bind_preview)) {
|
|
|
+ for (int k = 0; k < binding_list.size(); ++k) {
|
|
|
+ auto is_selected = (current_binding == k);
|
|
|
+ if (ImGui::Selectable(binding_list[k].disp_name.c_str(), is_selected)) {
|
|
|
+ current_binding = k;
|
|
|
+ }
|
|
|
+ if (is_selected) {
|
|
|
+ ImGui::SetItemDefaultFocus();
|
|
|
+ }
|
|
|
+ }
|
|
|
+ ImGui::EndCombo();
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+void cursor_guide::impl::pre_render_slot(const scene_ptr &info) {
|
|
|
+ if (free_pos.has_value()) {
|
|
|
+ auto item_info = scene_render_info::mesh_info{
|
|
|
+ .mesh = get_mesh(current_radius),
|
|
|
+ .material = {.ambient = color_free * amb_factor, .diffuse = color_free,},
|
|
|
+ };
|
|
|
+ info->items.push_back(
|
|
|
+ {.info = item_info, .transform = glm::translate(*free_pos),});
|
|
|
+ }
|
|
|
+
|
|
|
+ for (auto &bind: binding_list) {
|
|
|
+ bind.update_transform();
|
|
|
+ auto &points = bind.points;
|
|
|
+ for (auto &point: points.c) {
|
|
|
+ auto wp = transform_p(points.transform, point.position);
|
|
|
+ auto color = point.is_picked ? color_picked : color_fixed;
|
|
|
+ auto item_info = scene_render_info::mesh_info{
|
|
|
+ .mesh = get_mesh(point.radius),
|
|
|
+ .material = {.ambient = color * amb_factor, .diffuse = color,},
|
|
|
+ .enable_depth_alpha = true, .alpha_factor = 0.2,
|
|
|
+ };
|
|
|
+ info->items.push_back(
|
|
|
+ {.info = item_info, .transform = glm::translate(wp),});
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+cursor_guide::item_list_type
|
|
|
+cursor_guide::item_list_from_aug(const aug_list_type &aug_list) {
|
|
|
+ using cache_type =
|
|
|
+ std::unordered_set<std::string>;
|
|
|
+ cache_type cache;
|
|
|
+
|
|
|
+ auto ret = item_list_type();
|
|
|
+ for (auto &item: aug_list) {
|
|
|
+ auto disp_name = item.disp_name;
|
|
|
+ if (cache.contains(disp_name)) continue;
|
|
|
+ ret.emplace_back(disp_name, item.transform_var);
|
|
|
+ cache.emplace(disp_name);
|
|
|
+ }
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+
|
|
|
+cursor_guide::cursor_guide(const create_config &conf)
|
|
|
+ : pimpl(std::make_unique<impl>(conf)) {
|
|
|
+}
|
|
|
+
|
|
|
+cursor_guide::~cursor_guide() = default;
|
|
|
+
|
|
|
+void cursor_guide::update(const guide_info &info) {
|
|
|
+ pimpl->update(info);
|
|
|
+}
|
|
|
+
|
|
|
+void cursor_guide::show() {
|
|
|
+ pimpl->show();
|
|
|
+}
|
|
|
+
|
|
|
+void cursor_guide::pre_render_slot(const scene_ptr &info) {
|
|
|
+ pimpl->pre_render_slot(info);
|
|
|
+}
|