diff --git a/deps/polyscope b/deps/polyscope index f075418..c159e57 160000 --- a/deps/polyscope +++ b/deps/polyscope @@ -1 +1 @@ -Subproject commit f07541805443269e1bba221d26febc0ae6de07f6 +Subproject commit c159e571e73aa7b05138ccdcc2090c23e0f2fb9f diff --git a/src/cpp/core.cpp b/src/cpp/core.cpp index 792d709..4ab211e 100644 --- a/src/cpp/core.cpp +++ b/src/cpp/core.cpp @@ -168,10 +168,9 @@ PYBIND11_MODULE(polyscope_bindings, m) { m.def("get_buffer_size", &ps::view::getBufferSize); m.def("set_window_resizable", &ps::view::setWindowResizable); m.def("get_window_resizable", &ps::view::getWindowResizable); - m.def("set_view_from_json", ps::view::setViewFromJson); - m.def("get_view_as_json", ps::view::getViewAsJson); - m.def("screen_coords_to_world_ray", ps::view::screenCoordsToWorldRay); - m.def("screen_coords_to_world_position", ps::view::screenCoordsToWorldPosition); + m.def("set_view_from_json", &ps::view::setViewFromJson); + m.def("get_view_as_json", &ps::view::getViewAsJson); + m.def("screen_coords_to_world_ray", &ps::view::screenCoordsToWorldRay); m.def("set_background_color", [](glm::vec4 c) { for(int i = 0; i < 4; i++) ps::view::bgColor[i] = c[i]; }); m.def("get_background_color", []() { return glm2eigen(glm::vec4{ ps::view::bgColor[0], ps::view::bgColor[1], ps::view::bgColor[2], ps::view::bgColor[3] @@ -187,9 +186,9 @@ PYBIND11_MODULE(polyscope_bindings, m) { // === Messages m.def("info", overload_cast_()(&ps::info), "Send an info message"); - m.def("warning", ps::warning, "Send a warning message"); - m.def("error", ps::error, "Send an error message"); - m.def("terminating_error", ps::terminatingError, "Send a terminating error message"); + m.def("warning", &ps::warning, "Send a warning message"); + m.def("error", &ps::error, "Send an error message"); + m.def("terminating_error", &ps::terminatingError, "Send a terminating error message"); // === Callback m.def("set_user_callback", [](const std::function& func) { @@ -207,34 +206,30 @@ PYBIND11_MODULE(polyscope_bindings, m) { m.def("clear_user_callback", []() {ps::state::userCallback = nullptr;}); // === Pick - m.def("have_selection", [](){ return ps::pick::haveSelection();}); - m.def("get_selection", [](){ - const auto selection = ps::pick::getSelection(); - const auto * structure = std::get<0>(selection); - if (structure == nullptr) { - return std::make_tuple(std::string(), size_t{0}); - } - return std::make_tuple(structure->name, std::get<1>(selection)); - }); - m.def( - "set_selection", - [](const std::string &name, size_t index){ - for(const auto &structureTypeName : std::array{ - ps::PointCloud::structureTypeName, - ps::CurveNetwork::structureTypeName, - ps::SurfaceMesh::structureTypeName, - ps::VolumeMesh::structureTypeName - }) { - if (ps::hasStructure(structureTypeName, name)) { - auto * structure = ps::getStructure(structureTypeName, name); - ps::pick::setSelection(std::make_pair(structure, index)); - break; - } - } - }, - py::arg("name"), - py::arg("index") - ); + + py::class_(m, "PickResult") + .def(py::init<>()) + .def_readonly("is_hit", &ps::PickResult::isHit) + // .def_readonly("structure", &ps::PickResult::structure) + .def_readonly("structure_handle", &ps::PickResult::structureHandle) + .def_readonly("structure_type", &ps::PickResult::structureType) + .def_readonly("structure_name", &ps::PickResult::structureName) + .def_readonly("screen_coords", &ps::PickResult::screenCoords) + .def_readonly("buffer_inds", &ps::PickResult::bufferInds) + .def_readonly("position", &ps::PickResult::position) + .def_readonly("depth", &ps::PickResult::depth) + .def_readonly("local_index", &ps::PickResult::localIndex) + ; + + // stateful selection + m.def("have_selection", &ps::haveSelection); + m.def("get_selection", &ps::getSelection); + m.def("reset_selection", &ps::resetSelection); + // inentionally no binding to set_selection(), it is low-level and not obvious how to bind + + // query what is under a pixel + m.def("pick_at_screen_coords", &ps::pickAtScreenCoords); + m.def("pick_at_buffer_inds", &ps::pickAtBufferInds); // === Ground plane and shadows m.def("set_ground_plane_mode", [](ps::GroundPlaneMode x) { ps::options::groundPlaneMode = x; }); @@ -379,12 +374,12 @@ PYBIND11_MODULE(polyscope_bindings, m) { .value("arcball", ps::view::NavigateStyle::Arcball) .value("none", ps::view::NavigateStyle::None) .value("first_person", ps::view::NavigateStyle::FirstPerson) - .export_values(); + ; py::enum_(m, "ProjectionMode") .value("perspective", ps::ProjectionMode::Perspective) .value("orthographic", ps::ProjectionMode::Orthographic) - .export_values(); + ; py::enum_(m, "UpDir") .value("x_up", ps::UpDir::XUp) @@ -393,7 +388,7 @@ PYBIND11_MODULE(polyscope_bindings, m) { .value("neg_x_up", ps::UpDir::NegXUp) .value("neg_y_up", ps::UpDir::NegYUp) .value("neg_z_up", ps::UpDir::NegZUp) - .export_values(); + ; py::enum_(m, "FrontDir") .value("x_front", ps::FrontDir::XFront) @@ -402,24 +397,24 @@ PYBIND11_MODULE(polyscope_bindings, m) { .value("neg_x_front", ps::FrontDir::NegXFront) .value("neg_y_front", ps::FrontDir::NegYFront) .value("neg_z_front", ps::FrontDir::NegZFront) - .export_values(); + ; py::enum_(m, "DataType") .value("standard", ps::DataType::STANDARD) .value("symmetric", ps::DataType::SYMMETRIC) .value("magnitude", ps::DataType::MAGNITUDE) .value("categorical", ps::DataType::CATEGORICAL) - .export_values(); + ; py::enum_(m, "VectorType") .value("standard", ps::VectorType::STANDARD) .value("ambient", ps::VectorType::AMBIENT) - .export_values(); + ; py::enum_(m, "ParamCoordsType") .value("unit", ps::ParamCoordsType::UNIT) .value("world", ps::ParamCoordsType::WORLD) - .export_values(); + ; py::enum_(m, "ParamVizStyle") .value("checker", ps::ParamVizStyle::CHECKER) @@ -427,63 +422,94 @@ PYBIND11_MODULE(polyscope_bindings, m) { .value("grid", ps::ParamVizStyle::GRID) .value("local_check", ps::ParamVizStyle::LOCAL_CHECK) .value("local_rad", ps::ParamVizStyle::LOCAL_RAD) - .export_values(); + ; py::enum_(m, "BackFacePolicy") .value("identical", ps::BackFacePolicy::Identical) .value("different", ps::BackFacePolicy::Different) .value("custom", ps::BackFacePolicy::Custom) .value("cull", ps::BackFacePolicy::Cull) - .export_values(); + ; py::enum_(m, "GroundPlaneMode") .value("none", ps::GroundPlaneMode::None) .value("tile", ps::GroundPlaneMode::Tile) .value("tile_reflection", ps::GroundPlaneMode::TileReflection) .value("shadow_only", ps::GroundPlaneMode::ShadowOnly) - .export_values(); + ; py::enum_(m, "GroundPlaneHeightMode") .value("automatic", ps::GroundPlaneHeightMode::Automatic) .value("manual", ps::GroundPlaneHeightMode::Manual) - .export_values(); + ; py::enum_(m, "TransparencyMode") .value("none", ps::TransparencyMode::None) .value("simple", ps::TransparencyMode::Simple) .value("pretty", ps::TransparencyMode::Pretty) - .export_values(); + ; + + py::enum_(m, "CurveNetworkElement") + .value("node", ps::CurveNetworkElement::NODE) + .value("edge", ps::CurveNetworkElement::EDGE) + ; + + py::enum_(m, "MeshElement") + .value("vertex", ps::MeshElement::VERTEX) + .value("face", ps::MeshElement::FACE) + .value("edge", ps::MeshElement::EDGE) + .value("halfedge", ps::MeshElement::HALFEDGE) + .value("corner", ps::MeshElement::CORNER) + ; + + py::enum_(m, "MeshSelectionMode") + .value("auto", ps::MeshSelectionMode::Auto) + .value("vertices_only", ps::MeshSelectionMode::VerticesOnly) + .value("faces_only", ps::MeshSelectionMode::FacesOnly) + ; + + py::enum_(m, "VolumeMeshElement") + .value("vertex", ps::VolumeMeshElement::VERTEX) + .value("edge", ps::VolumeMeshElement::EDGE) + .value("face", ps::VolumeMeshElement::FACE) + .value("cell", ps::VolumeMeshElement::CELL) + ; + + py::enum_(m, "VolumeGridElement") + .value("node", ps::VolumeGridElement::NODE) + .value("cell", ps::VolumeGridElement::CELL) + ; py::enum_(m, "PointRenderMode") .value("sphere", ps::PointRenderMode::Sphere) .value("quad", ps::PointRenderMode::Quad) - .export_values(); + ; py::enum_(m, "FilterMode") .value("linear", ps::FilterMode::Linear) .value("nearest", ps::FilterMode::Nearest) - .export_values(); + ; py::enum_(m, "ImageOrigin") .value("lower_left", ps::ImageOrigin::LowerLeft) .value("upper_left", ps::ImageOrigin::UpperLeft) - .export_values(); + ; py::enum_(m, "MeshShadeStyle") .value("smooth", ps::MeshShadeStyle::Smooth) .value("flat", ps::MeshShadeStyle::Flat) .value("tri_flat", ps::MeshShadeStyle::TriFlat) - .export_values(); + ; py::enum_(m, "IsolineStyle") .value("stripe", ps::IsolineStyle::Stripe) .value("contour", ps::IsolineStyle::Contour) - .export_values(); + ; py::enum_(m, "ImplicitRenderMode") .value("sphere_march", ps::ImplicitRenderMode::SphereMarch) .value("fixed_step", ps::ImplicitRenderMode::FixedStep) - .export_values(); + ; py::enum_(m, "ManagedBufferType") .value(ps::typeName(ps::ManagedBufferType::Float ).c_str(), ps::ManagedBufferType::Float ) @@ -499,14 +525,14 @@ PYBIND11_MODULE(polyscope_bindings, m) { .value(ps::typeName(ps::ManagedBufferType::UVec2 ).c_str(), ps::ManagedBufferType::UVec2 ) .value(ps::typeName(ps::ManagedBufferType::UVec3 ).c_str(), ps::ManagedBufferType::UVec3 ) .value(ps::typeName(ps::ManagedBufferType::UVec4 ).c_str(), ps::ManagedBufferType::UVec4 ) - .export_values(); + ; py::enum_(m, "DeviceBufferType") .value("attribute", ps::DeviceBufferType::Attribute) .value("texture1d", ps::DeviceBufferType::Texture1d) .value("texture2d", ps::DeviceBufferType::Texture2d) .value("texture3d", ps::DeviceBufferType::Texture3d) - .export_values(); + ; // === Mini bindings for a little bit of glm py::class_(m, "glm_vec2"). @@ -530,6 +556,13 @@ PYBIND11_MODULE(polyscope_bindings, m) { return std::tuple(x[0], x[1], x[2], x[3]); }); + py::class_(m, "glm_ivec2"). + def(py::init()) + .def("as_tuple", + [](const glm::ivec2& x) { + return std::tuple(x[0], x[1]); + }); + py::class_(m, "glm_uvec3"). def(py::init()) .def("as_tuple", diff --git a/src/cpp/curve_network.cpp b/src/cpp/curve_network.cpp index 4392a91..80c5a93 100644 --- a/src/cpp/curve_network.cpp +++ b/src/cpp/curve_network.cpp @@ -16,7 +16,13 @@ namespace ps = polyscope; // clang-format off void bind_curve_network(py::module& m) { - // == Helper quantity classes + // == Helper classes + py::class_(m, "CurveNetworkPickResult") + .def(py::init<>()) + .def_readonly("element_type", &ps::CurveNetworkPickResult::elementType) + .def_readonly("index", &ps::CurveNetworkPickResult::index) + .def_readonly("t_edge", &ps::CurveNetworkPickResult::tEdge) + ; // Scalar quantities bindScalarQuantity(m, "CurveNetworkNodeScalarQuantity"); @@ -48,6 +54,9 @@ void bind_curve_network(py::module& m) { .def("set_material", &ps::CurveNetwork::setMaterial, "Set material") .def("get_material", &ps::CurveNetwork::getMaterial, "Get material") + // picking + .def("interpret_pick_result", &ps::CurveNetwork::interpretPickResult) + // quantities .def("add_node_color_quantity", &ps::CurveNetwork::addNodeColorQuantity, "Add a color function at nodes", py::arg("name"), py::arg("values"), py::return_value_policy::reference) diff --git a/src/cpp/point_cloud.cpp b/src/cpp/point_cloud.cpp index db6da9e..15b739d 100644 --- a/src/cpp/point_cloud.cpp +++ b/src/cpp/point_cloud.cpp @@ -20,7 +20,11 @@ using overload_cast_ = pybind11::detail::overload_cast_impl; // clang-format off void bind_point_cloud(py::module& m) { - // == Helper quantity classes + // == Helper classes + py::class_(m, "PointCloudPickResult") + .def(py::init<>()) + .def_readonly("index", &ps::PointCloudPickResult::index) + ; // Scalar quantities bindScalarQuantity(m, "PointCloudScalarQuantity"); @@ -50,6 +54,9 @@ void bind_point_cloud(py::module& m) { .def("set_point_render_mode", &ps::PointCloud::setPointRenderMode, "Set point render mode") .def("get_point_render_mode", &ps::PointCloud::getPointRenderMode, "Get point render mode") + // picking + .def("interpret_pick_result", &ps::PointCloud::interpretPickResult) + // variable radius .def("set_point_radius_quantity", overload_cast_()(&ps::PointCloud::setPointRadiusQuantity), diff --git a/src/cpp/surface_mesh.cpp b/src/cpp/surface_mesh.cpp index ae97ebf..a70060f 100644 --- a/src/cpp/surface_mesh.cpp +++ b/src/cpp/surface_mesh.cpp @@ -18,7 +18,14 @@ namespace ps = polyscope; void bind_surface_mesh(py::module& m) { - // == Helper quantity classes + // == Helper classes + py::class_(m, "SurfaceMeshPickResult") + .def(py::init<>()) + .def_readonly("element_type", &ps::SurfaceMeshPickResult::elementType) + .def_readonly("index", &ps::SurfaceMeshPickResult::index) + .def_readonly("bary_coords", &ps::SurfaceMeshPickResult::baryCoords) + ; + // Scalar quantities bindScalarQuantity(m, "SurfaceVertexScalarQuantity"); @@ -91,12 +98,17 @@ void bind_surface_mesh(py::module& m) { .def("get_smooth_shade", &ps::SurfaceMesh::isSmoothShade, "Get if smooth shading is enabled") .def("set_shade_style", &ps::SurfaceMesh::setShadeStyle, "Set shading") .def("get_shade_style", &ps::SurfaceMesh::getShadeStyle, "Get shading") + .def("set_selection_mode", &ps::SurfaceMesh::setSelectionMode) + .def("get_selection_mode", &ps::SurfaceMesh::getSelectionMode) .def("set_material", &ps::SurfaceMesh::setMaterial, "Set material") .def("get_material", &ps::SurfaceMesh::getMaterial, "Get material") .def("set_back_face_policy", &ps::SurfaceMesh::setBackFacePolicy, "Set back face policy") .def("get_back_face_policy", &ps::SurfaceMesh::getBackFacePolicy, "Get back face policy") .def("set_back_face_color", &ps::SurfaceMesh::setBackFaceColor, "Set back face color") .def("get_back_face_color", &ps::SurfaceMesh::getBackFaceColor, "Get back face color") + + // picking + .def("interpret_pick_result", &ps::SurfaceMesh::interpretPickResult) // permutations & bases .def("set_edge_permutation", &ps::SurfaceMesh::setEdgePermutation, "Set edge permutation") diff --git a/src/cpp/volume_grid.cpp b/src/cpp/volume_grid.cpp index 683bc48..ea72501 100644 --- a/src/cpp/volume_grid.cpp +++ b/src/cpp/volume_grid.cpp @@ -17,7 +17,12 @@ namespace ps = polyscope; void bind_volume_grid(py::module& m) { - // == Helper quantity classes + // == Helper classes + py::class_(m, "VolumeGridPickResult") + .def(py::init<>()) + .def_readonly("element_type", &ps::VolumeGridPickResult::elementType) + .def_readonly("index", &ps::VolumeGridPickResult::index) + ; // Scalar quantities bindScalarQuantity(m, "VolumeGridNodeScalarQuantity") @@ -62,6 +67,8 @@ void bind_volume_grid(py::module& m) { .def("set_cube_size_factor", &ps::VolumeGrid::setCubeSizeFactor, "Set cube size factor") .def("get_cube_size_factor", &ps::VolumeGrid::getCubeSizeFactor, "Get cube size factor") + // picking + .def("interpret_pick_result", &ps::VolumeGrid::interpretPickResult) // = quantities diff --git a/src/cpp/volume_mesh.cpp b/src/cpp/volume_mesh.cpp index ada5d71..78781d1 100644 --- a/src/cpp/volume_mesh.cpp +++ b/src/cpp/volume_mesh.cpp @@ -16,7 +16,12 @@ namespace ps = polyscope; void bind_volume_mesh(py::module& m) { - // == Helper quantity classes + // == Helper classes + py::class_(m, "VolumeMeshPickResult") + .def(py::init<>()) + .def_readonly("element_type", &ps::VolumeMeshPickResult::elementType) + .def_readonly("index", &ps::VolumeMeshPickResult::index) + ; // Scalar quantities bindVMVScalarQuantity(m, "VolumeMeshVertexScalarQuantity"); @@ -52,6 +57,8 @@ void bind_volume_mesh(py::module& m) { .def("set_material", &ps::VolumeMesh::setMaterial, "Set material") .def("get_material", &ps::VolumeMesh::getMaterial, "Get material") + // picking + .def("interpret_pick_result", &ps::VolumeMesh::interpretPickResult) // = quantities diff --git a/src/polyscope/core.py b/src/polyscope/core.py index 39a26dd..bca4a62 100644 --- a/src/polyscope/core.py +++ b/src/polyscope/core.py @@ -1,6 +1,8 @@ import polyscope_bindings as psb import polyscope.imgui as psim +import polyscope + import os import numpy as np @@ -233,8 +235,10 @@ def get_view_as_json(): def screen_coords_to_world_ray(screen_coords): return np.array(psb.screen_coords_to_world_ray(glm2(screen_coords)).as_tuple()) +# deprecated! use query_pick_at_screen_coords def screen_coords_to_world_position(screen_coords): - return np.array(psb.screen_coords_to_world_position(glm2(screen_coords)).as_tuple()) + pick_result = pick(screen_coords=screen_coords) + return pick_result.position def set_background_color(c): if len(c) == 3: c = (c[0], c[1], c[2], 1.0) @@ -298,18 +302,87 @@ def set_user_callback(func): def clear_user_callback(): psb.clear_user_callback() -### Pick +### Picking + +def pick(*, screen_coords=None, buffer_inds=None): + if(screen_coords is not None and buffer_inds is not None): + raise ValueError("pass args one of screen_coords OR buffer_inds, but not both") + if(screen_coords is None and buffer_inds is None): + raise ValueError("must pass args one of screen_coords or buffer_inds") + + if(screen_coords is not None): + raw_result = psb.pick_at_screen_coords(glm2(screen_coords)) + if(buffer_inds is not None): + raw_result = psb.pick_at_buffer_inds(glm2i(buffer_inds)) + + return PickResult(raw_result) + def have_selection(): return psb.have_selection() def get_selection(): - return psb.get_selection() + return PickResult(psb.get_selection()) + +def reset_selection(): + psb.reset_selection() + +class PickResult: + + def __init__(self, bound_pick_result): + + self.raw_result = bound_pick_result + + # translate most properties + self.is_hit = bound_pick_result.is_hit + self.structure_type_name = bound_pick_result.structure_type + self.structure_name = bound_pick_result.structure_name + self.screen_coords = bound_pick_result.screen_coords.as_tuple() + self.buffer_inds = bound_pick_result.buffer_inds.as_tuple() + self.position = np.array(bound_pick_result.position.as_tuple()) + self.depth = bound_pick_result.depth + self.local_index = bound_pick_result.local_index -def set_selection(name, index): - psb.set_selection(name, index) + # additional per-structure data, such as barycentric coordinates + # if its a triangle in a mesh + self.structure_data = {} + + self.resolve_additional_data() + + def resolve_additional_data(self): + # resolve data from various types + + if self.structure_type_name == "Point Cloud": + polyscope.point_cloud.get_point_cloud(self.structure_name).append_pick_data(self) + + if self.structure_type_name == "Curve Network": + polyscope.curve_network.get_curve_network(self.structure_name).append_pick_data(self) + + if self.structure_type_name == "Surface Mesh": + polyscope.surface_mesh.get_surface_mesh(self.structure_name).append_pick_data(self) + + if self.structure_type_name == "Volume Mesh": + polyscope.volume_mesh.get_volume_mesh(self.structure_name).append_pick_data(self) + + if self.structure_type_name == "Volume Grid": + polyscope.volume_grid.get_volume_grid(self.structure_name).append_pick_data(self) + + def __str__(self): + return f""" +PickResult( + is_hit={self.is_hit}, + structure_type_name={self.structure_type_name}, + structure_name={self.structure_name}, + screen_coords={self.screen_coords}, + buffer_inds={self.buffer_inds}, + position={self.position}, + depth={self.depth}, + local_index={self.local_index}, + structure_data={self.structure_data} +) +""" ## Ground plane and shadows -def set_ground_plane_mode(mode_str): +def set_ground_plane_mode(mode_str):# psb.set_ground_plane_mode(str_to_ground_plane_mode(mode_str)) def set_ground_plane_height_mode(mode_str): @@ -526,6 +599,8 @@ def generate_camera_ray_corners(self): ## Small utilities def glm2(vals): return psb.glm_vec2(vals[0], vals[1]) +def glm2i(vals): + return psb.glm_ivec2(vals[0], vals[1]) def glm3u(vals): return psb.glm_uvec3(vals[0], vals[1], vals[2]) def glm3(vals): @@ -566,6 +641,19 @@ def load_color_map(cmap_name, filename): ## String-to-enum translation +# General case +# TODO: gradually replace the enum-specific methods below with this, +# ensuring each doesn't have any special breakage +def str_to_enum(s, enum): + if s in enum.__members__: return enum.__members__[s] + enum_val_names = [x for x in enum.__members__] + raise ValueError(f"Bad specifier '{s}' for {enum.__name__}, should be one of [{','.join(enum_val_names)}]") + +def enum_to_str(enum_val): + return enum_val.name + +## Per-enum maps + d_navigate = { "turntable" : psb.NavigateStyle.turntable, "free" : psb.NavigateStyle.free, @@ -762,7 +850,7 @@ def str_to_transparency_mode(s): } if s not in d: - raise ValueError("Bad transparenccy mode specifier '{}', should be one of [{}]".format(s, + raise ValueError("Bad transparency mode specifier '{}', should be one of [{}]".format(s, ",".join(["'{}'".format(x) for x in d.keys()]))) return d[s] diff --git a/src/polyscope/curve_network.py b/src/polyscope/curve_network.py index 3be899b..235db11 100644 --- a/src/polyscope/curve_network.py +++ b/src/polyscope/curve_network.py @@ -3,6 +3,7 @@ from polyscope.core import str_to_datatype, str_to_vectortype, glm3 from polyscope.structure import Structure from polyscope.common import process_quantity_args, process_scalar_args, process_color_args, process_vector_args, check_all_args_processed +from polyscope.core import str_to_enum, enum_to_str class CurveNetwork(Structure): @@ -89,6 +90,12 @@ def set_material(self, mat): def get_material(self): return self.bound_instance.get_material() + # Picking + def append_pick_data(self, pick_result): + struct_result = self.bound_instance.interpret_pick_result(pick_result.raw_result) + pick_result.structure_data["element_type"] = enum_to_str(struct_result.element_type) + pick_result.structure_data["index"] = struct_result.index + pick_result.structure_data["t_edge"] = struct_result.t_edge ## Quantities diff --git a/src/polyscope/point_cloud.py b/src/polyscope/point_cloud.py index 6ce1792..c1fed35 100644 --- a/src/polyscope/point_cloud.py +++ b/src/polyscope/point_cloud.py @@ -89,6 +89,9 @@ def set_material(self, mat): def get_material(self): return self.bound_instance.get_material() + def append_pick_data(self, pick_result): + struct_result = self.bound_instance.interpret_pick_result(pick_result.raw_result) + pick_result.structure_data["index"] = struct_result.index ## Quantities diff --git a/src/polyscope/surface_mesh.py b/src/polyscope/surface_mesh.py index c8118a7..b0c8466 100644 --- a/src/polyscope/surface_mesh.py +++ b/src/polyscope/surface_mesh.py @@ -3,7 +3,7 @@ from polyscope.core import str_to_datatype, str_to_vectortype, str_to_param_coords_type, \ str_to_param_viz_style, str_to_back_face_policy, back_face_policy_to_str,\ - str_to_image_origin, glm3 + str_to_image_origin, glm3, enum_to_str, str_to_enum from polyscope.structure import Structure from polyscope.common import process_quantity_args, process_scalar_args, process_color_args, process_vector_args, process_texture_map_args, process_parameterization_args, check_all_args_processed, check_is_scalar_image, check_is_image3 @@ -82,6 +82,16 @@ def set_transparency_quantity(self, quantity_name): self.bound_instance.set_transparency_quantity(quantity_name) def clear_transparency_quantity(self): self.bound_instance.clear_transparency_quantity() + + + # Picking + def append_pick_data(self, pick_result): + struct_result = self.bound_instance.interpret_pick_result(pick_result.raw_result) + pick_result.structure_data["element_type"] = enum_to_str(struct_result.element_type) + pick_result.structure_data["index"] = struct_result.index + bary_coords = np.array(struct_result.bary_coords.as_tuple()) + if not (bary_coords == np.array((-1,-1,-1))).all(): + pick_result.structure_data["bary_coords"] = bary_coords ## Options @@ -110,6 +120,12 @@ def set_smooth_shade(self, val): def get_smooth_shade(self): return self.bound_instance.get_smooth_shade() + # Selection Mode + def set_selection_mode(self, val): + self.bound_instance.set_selection_mode(str_to_enum(val, psb.MeshSelectionMode)) + def get_selection_mode(self): + return enum_to_str(self.bound_instance.get_selection_mode()) + # Material def set_material(self, mat): self.bound_instance.set_material(mat) diff --git a/src/polyscope/volume_grid.py b/src/polyscope/volume_grid.py index d1e44b4..50f2ca2 100644 --- a/src/polyscope/volume_grid.py +++ b/src/polyscope/volume_grid.py @@ -1,7 +1,7 @@ import polyscope_bindings as psb import numpy as np -from polyscope.core import str_to_datatype, str_to_vectortype, str_to_param_coords_type, str_to_param_viz_style, glm3, glm3u +from polyscope.core import str_to_datatype, str_to_vectortype, str_to_param_coords_type, str_to_param_viz_style, glm3, glm3u, enum_to_str from polyscope.structure import Structure from polyscope.common import process_quantity_args, process_scalar_args, process_color_args, process_vector_args, check_all_args_processed, check_and_pop_arg @@ -114,6 +114,12 @@ def mark_cells_as_used(self): return self.bound_instance.mark_cells_as_used() + # Picking + def append_pick_data(self, pick_result): + struct_result = self.bound_instance.interpret_pick_result(pick_result.raw_result) + pick_result.structure_data["element_type"] = enum_to_str(struct_result.element_type) + pick_result.structure_data["index"] = struct_result.index + ## Quantities # Scalar diff --git a/src/polyscope/volume_mesh.py b/src/polyscope/volume_mesh.py index f9eb284..fbbbd20 100644 --- a/src/polyscope/volume_mesh.py +++ b/src/polyscope/volume_mesh.py @@ -1,7 +1,7 @@ import polyscope_bindings as psb import numpy as np -from polyscope.core import str_to_datatype, str_to_vectortype, str_to_param_coords_type, str_to_param_viz_style, glm3 +from polyscope.core import str_to_datatype, str_to_vectortype, str_to_param_coords_type, str_to_param_viz_style, glm3, enum_to_str from polyscope.structure import Structure from polyscope.common import process_quantity_args, process_scalar_args, process_color_args, process_vector_args, check_all_args_processed @@ -82,7 +82,7 @@ def update_vertex_positions(self, vertices): ## Options - + # Color def set_color(self, val): self.bound_instance.set_color(glm3(val)) @@ -113,6 +113,12 @@ def set_material(self, mat): def get_material(self): return self.bound_instance.get_material() + # Picking + def append_pick_data(self, pick_result): + struct_result = self.bound_instance.interpret_pick_result(pick_result.raw_result) + pick_result.structure_data["element_type"] = enum_to_str(struct_result.element_type) + pick_result.structure_data["index"] = struct_result.index + ## Quantities diff --git a/test/polyscope_demo.py b/test/polyscope_demo.py index 7f88f2d..70c0723 100644 --- a/test/polyscope_demo.py +++ b/test/polyscope_demo.py @@ -32,6 +32,7 @@ def color_func(pts): A[:,0] = np.cos(3*pts[:,0])**2 return A +is_test_clicking = False def main(): @@ -56,11 +57,28 @@ def my_function(): def callback(): # Executed every frame + global is_test_clicking + + if is_test_clicking: + if(psim.Button("Cancel")): + is_test_clicking = False + + io = psim.GetIO() + if io.MouseClicked[0]: + screen_coords = io.MousePos + pick_result = polyscope.pick(screen_coords=screen_coords) + print(pick_result) + is_test_clicking = False + if(psim.Button("Test button")): my_function() implicit_ui() + if(psim.Button("Test pick")): + is_test_clicking = True + + polyscope.set_user_callback(callback) diff --git a/test/polyscope_test.py b/test/polyscope_test.py index 5e572f0..856d8ce 100644 --- a/test/polyscope_test.py +++ b/test/polyscope_test.py @@ -237,6 +237,14 @@ def test_screenshot(self): self.assertEqual(buff.shape, (h,w,4)) ps.show(3) + + + def test_picking(self): + + res = ps.pick(buffer_inds=(77,88)) + res = ps.pick(screen_coords=(0.78,.96)) + + ps.show(3) def test_slice_plane(self): @@ -1138,6 +1146,12 @@ def test_options(self): p.set_edge_width(1.5) ps.show(3) self.assertAlmostEqual(p.get_edge_width(), 1.5) + + # Selection mode + p.set_selection_mode('auto') + p.set_selection_mode('vertices_only') + p.set_selection_mode('faces_only') + ps.show(3) # Material p.set_material("candy")