bool mouse_up()

in code/cpp/tools/scene_annotation_tool/main.cpp [2149:2519]


bool mouse_up(igl::opengl::glfw::Viewer& viewer, int button, int modifier) {

    if (g_mouse_drawing_create) {
        if (g_segmentation_layer == SEGMENTATION_LAYER_SEMANTIC_INSTANCE) {
            g_semantic_instance_id = create_new_semantic_instance_segmentation_id();
            g_updated_semantic_instance_id = true;
        }
    }

    if (g_mouse_drawing) {

        if (g_mouse_drawing_positions.size() == 0) {
            g_mouse_rotation       = false;
            g_mouse_translation    = false;
            g_mouse_drawing        = false;
            g_mouse_drawing_create = false;

            return true;
        }

        //
        // raycasting setup
        //

        int width_window, height_window;
        glfwGetFramebufferSize(viewer.window, &width_window, &height_window);

        arma::vec camera_look_from_position = { viewer.core().camera_eye(0),    viewer.core().camera_eye(1),    viewer.core().camera_eye(2) };
        arma::vec camera_look_at_position   = { viewer.core().camera_center(0), viewer.core().camera_center(1), viewer.core().camera_center(2) };
        arma::vec camera_look_at_dir        = arma::normalise(camera_look_at_position - camera_look_from_position);
        arma::vec camera_up_hint            = { 0, 0, 1 };

        // The convention here is that the camera's positive x axis points right, the positive y
        // axis points up, and the positive z axis points away from where the camera is looking.
        arma::vec camera_z_axis = -arma::normalise(camera_look_at_dir);
        arma::vec camera_x_axis = -arma::normalise(arma::cross(camera_z_axis, camera_up_hint));
        arma::vec camera_y_axis = arma::normalise(arma::cross(camera_z_axis, camera_x_axis));

        arma::mat R_world_from_cam = arma::ones<arma::mat>(3,3) * std::numeric_limits<float>::infinity();
        R_world_from_cam.col(0)    = camera_x_axis;
        R_world_from_cam.col(1)    = camera_y_axis;
        R_world_from_cam.col(2)    = camera_z_axis;

        auto fov_y = viewer.core().camera_view_angle * (igl::PI/180.0);
        auto fov_x = 2.0 * std::atan(width_window * std::tan(fov_y/2.0) / height_window);

        auto uv_min  = -1.0;
        auto uv_max  = 1.0;
        auto half_du = 0.5 * (uv_max - uv_min) / width_window;
        auto half_dv = 0.5 * (uv_max - uv_min) / height_window;
        arma::vec u  = arma::linspace(uv_min+half_du, uv_max-half_du, width_window);
        arma::vec v  = arma::reverse(arma::linspace(uv_min+half_dv, uv_max-half_dv, height_window));

        //
        // eyedropper is a special case because it doesn't actually do any drawing
        //

        if (g_tool == TOOL_EYEDROPPER) {

            arma::ivec p = g_mouse_drawing_positions.back();
            auto s_x     = p(0);
            auto s_y     = p(1);
            auto u_      = u(s_x);
            auto v_      = v(s_y);

            arma::vec ray_cam   = { u_*std::tan(fov_x/2.0), v_*std::tan(fov_y/2.0), -1.0 };
            arma::vec ray_world = arma::normalise(R_world_from_cam*ray_cam);

            // initialize ray
            arma::vec ray_position = camera_look_from_position;
            arma::vec ray_direction_normalized = ray_world;

            RTCRayHit rtc_ray_hit;

            rtc_ray_hit.ray.org_x = (float)ray_position(0);
            rtc_ray_hit.ray.org_y = (float)ray_position(1);
            rtc_ray_hit.ray.org_z = (float)ray_position(2);
            rtc_ray_hit.ray.dir_x = (float)ray_direction_normalized(0);
            rtc_ray_hit.ray.dir_y = (float)ray_direction_normalized(1);
            rtc_ray_hit.ray.dir_z = (float)ray_direction_normalized(2);
            rtc_ray_hit.ray.tnear = 0.0f;
            rtc_ray_hit.ray.tfar  = std::numeric_limits<float>::infinity();
            rtc_ray_hit.ray.time  = 0.0f;
            rtc_ray_hit.ray.mask  = 0;
            rtc_ray_hit.ray.id    = 0;
            rtc_ray_hit.ray.flags = 0;

            rtc_ray_hit.hit.Ng_x      = std::numeric_limits<float>::infinity();
            rtc_ray_hit.hit.Ng_y      = std::numeric_limits<float>::infinity();
            rtc_ray_hit.hit.Ng_z      = std::numeric_limits<float>::infinity();
            rtc_ray_hit.hit.u         = std::numeric_limits<float>::infinity();
            rtc_ray_hit.hit.v         = std::numeric_limits<float>::infinity();
            rtc_ray_hit.hit.primID    = RTC_INVALID_GEOMETRY_ID;
            rtc_ray_hit.hit.geomID    = RTC_INVALID_GEOMETRY_ID;
            rtc_ray_hit.hit.instID[0] = RTC_INVALID_GEOMETRY_ID;

            // intersect ray with scene
            rtcIntersect1(g_rtc_scene, &g_rtc_intersect_context, &rtc_ray_hit);

            if (rtc_ray_hit.ray.tfar < std::numeric_limits<float>::infinity()) {
                assert(rtc_ray_hit.hit.primID != RTC_INVALID_GEOMETRY_ID);
                assert(rtc_ray_hit.hit.geomID != RTC_INVALID_GEOMETRY_ID);
            } else {
                assert(rtc_ray_hit.hit.primID == RTC_INVALID_GEOMETRY_ID);
                assert(rtc_ray_hit.hit.geomID == RTC_INVALID_GEOMETRY_ID);
            }

            if (rtc_ray_hit.hit.geomID != RTC_INVALID_GEOMETRY_ID) {
                auto face_id = rtc_ray_hit.hit.primID;

                if (g_segmentation_layer == SEGMENTATION_LAYER_SEMANTIC_INSTANCE) {
                    if (g_segmentation_state_curr.mesh_objects_sii(g_faces_oi_curr(face_id)) != -1) {
                        g_semantic_instance_id = g_segmentation_state_curr.mesh_objects_sii(g_faces_oi_curr(face_id));
                        g_updated_semantic_instance_id = true;
                    }
                }
                if (g_segmentation_layer == SEGMENTATION_LAYER_SEMANTIC) {
                    if (g_segmentation_state_curr.mesh_objects_si(g_faces_oi_curr(face_id)) != -1) {
                        g_semantic_id = g_segmentation_state_curr.mesh_objects_si(g_faces_oi_curr(face_id));
                    }
                }
            }
        }

        //
        // for the pen and line tools, approximately rasterize lines by oversampling, rather than implementing an exact rasterization algorithm like DDA
        //

        std::set<std::pair<int,int>> covered_pixels;

        if (g_tool == TOOL_PEN) {

            if (g_mouse_drawing_positions.size() == 1) {
                arma::ivec p = g_mouse_drawing_positions.at(0);
                covered_pixels.insert({p(0), p(1)});
            } else {

                auto k = 4; // number of samples per unit of screen space distance

                for (int i = 0; i < g_mouse_drawing_positions.size() - 1; i++) {
                    arma::ivec p0    = g_mouse_drawing_positions.at(i);
                    arma::ivec p1    = g_mouse_drawing_positions.at(i+1);
                    arma::vec p0_    = arma::conv_to<arma::vec>::from(p0);
                    arma::vec p1_    = arma::conv_to<arma::vec>::from(p1);
                    auto num_samples = (int)(k*arma::norm(p1_ - p0_) + 1);
                    arma::vec x_vals = arma::linspace(p0_(0), p1_(0), num_samples);
                    arma::vec y_vals = arma::linspace(p0_(1), p1_(1), num_samples);

                    for (int j = 0; j < num_samples; j++) {
                        arma::ivec p = {(int)x_vals(j), (int)y_vals(j)};
                        covered_pixels.insert({p(0), p(1)});
                    }
                }
            }
        }

        if (g_tool == TOOL_LINE) {

            if (g_mouse_drawing_positions.size() == 1) {
                arma::ivec p = g_mouse_drawing_positions.at(0);
                covered_pixels.insert({p(0), p(1)});
            } else {

                auto k = 4; // number of samples per unit of screen space distance

                arma::ivec p0    = g_mouse_drawing_positions.front();
                arma::ivec p1    = g_mouse_drawing_positions.back();
                arma::vec p0_    = arma::conv_to<arma::vec>::from(p0);
                arma::vec p1_    = arma::conv_to<arma::vec>::from(p1);
                auto num_samples = (int)(k*arma::norm(p1_ - p0_) + 1);
                arma::vec x_vals = arma::linspace(p0_(0), p1_(0), num_samples);
                arma::vec y_vals = arma::linspace(p0_(1), p1_(1), num_samples);

                for (int j = 0; j < num_samples; j++) {
                    arma::ivec p = {(int)x_vals(j), (int)y_vals(j)};
                    covered_pixels.insert({p(0), p(1)});
                }
            }
        }

        if (g_tool == TOOL_RECTANGLE) {

            if (g_mouse_drawing_positions.size() == 1) {
                arma::ivec p = g_mouse_drawing_positions.at(0);
                covered_pixels.insert({p(0), p(1)});
            } else {
                arma::ivec p0 = g_mouse_drawing_positions.front();
                arma::ivec p1 = g_mouse_drawing_positions.back();
                auto x_min    = std::min(p0(0), p1(0));
                auto y_min    = std::min(p0(1), p1(1));
                auto x_max    = std::max(p0(0), p1(0));
                auto y_max    = std::max(p0(1), p1(1));

                for (int y = y_min; y <= y_max; y++) {
                    for (int x = x_min; x <= x_max; x++) {
                        covered_pixels.insert({x, y});
                    }
                }
            }
        }

        g_mouse_drawing_positions.clear();

        if (g_tool == TOOL_PEN || g_tool == TOOL_LINE || g_tool == TOOL_RECTANGLE) {

            //
            // perform raycasting for each covered pixel
            //

            std::set<int> covered_face_ids;
            for (auto p : covered_pixels) {

                auto s_x   = p.first;
                auto s_y   = p.second;
                auto u_    = u(s_x);
                auto v_    = v(s_y);

                arma::vec ray_cam   = { u_*std::tan(fov_x/2.0), v_*std::tan(fov_y/2.0), -1.0 };
                arma::vec ray_world = arma::normalise(R_world_from_cam*ray_cam);

                // initialize ray
                arma::vec ray_position = camera_look_from_position;
                arma::vec ray_direction_normalized = ray_world;

                RTCRayHit rtc_ray_hit;

                rtc_ray_hit.ray.org_x = (float)ray_position(0);
                rtc_ray_hit.ray.org_y = (float)ray_position(1);
                rtc_ray_hit.ray.org_z = (float)ray_position(2);
                rtc_ray_hit.ray.dir_x = (float)ray_direction_normalized(0);
                rtc_ray_hit.ray.dir_y = (float)ray_direction_normalized(1);
                rtc_ray_hit.ray.dir_z = (float)ray_direction_normalized(2);
                rtc_ray_hit.ray.tnear = 0.0f;
                rtc_ray_hit.ray.tfar  = std::numeric_limits<float>::infinity();
                rtc_ray_hit.ray.time  = 0.0f;
                rtc_ray_hit.ray.mask  = 0;
                rtc_ray_hit.ray.id    = 0;
                rtc_ray_hit.ray.flags = 0;

                rtc_ray_hit.hit.Ng_x      = std::numeric_limits<float>::infinity();
                rtc_ray_hit.hit.Ng_y      = std::numeric_limits<float>::infinity();
                rtc_ray_hit.hit.Ng_z      = std::numeric_limits<float>::infinity();
                rtc_ray_hit.hit.u         = std::numeric_limits<float>::infinity();
                rtc_ray_hit.hit.v         = std::numeric_limits<float>::infinity();
                rtc_ray_hit.hit.primID    = RTC_INVALID_GEOMETRY_ID;
                rtc_ray_hit.hit.geomID    = RTC_INVALID_GEOMETRY_ID;
                rtc_ray_hit.hit.instID[0] = RTC_INVALID_GEOMETRY_ID;

                // intersect ray with scene
                rtcIntersect1(g_rtc_scene, &g_rtc_intersect_context, &rtc_ray_hit);

                if (rtc_ray_hit.ray.tfar < std::numeric_limits<float>::infinity()) {
                    assert(rtc_ray_hit.hit.primID != RTC_INVALID_GEOMETRY_ID);
                    assert(rtc_ray_hit.hit.geomID != RTC_INVALID_GEOMETRY_ID);
                } else {
                    assert(rtc_ray_hit.hit.primID == RTC_INVALID_GEOMETRY_ID);
                    assert(rtc_ray_hit.hit.geomID == RTC_INVALID_GEOMETRY_ID);
                }

                if (rtc_ray_hit.hit.geomID != RTC_INVALID_GEOMETRY_ID) {
                    auto face_id = rtc_ray_hit.hit.primID;
                    covered_face_ids.insert(face_id);
                }
            }

            //
            // perform selection logic for each covered face
            //

            std::set<int> selected_mesh_object_ids;

            for (auto f : covered_face_ids) {

                auto select_face = true;

                if (g_select_only_null_semantic_instance_id && g_segmentation_state_curr.mesh_objects_sii(g_faces_oi_curr(f)) != -1) {
                    select_face = false;
                }
                if (g_select_only_valid_semantic_instance_id && g_segmentation_state_curr.mesh_objects_sii(g_faces_oi_curr(f)) == -1) {
                    select_face = false;
                }
                if (g_select_only_null_semantic_id && g_segmentation_state_curr.mesh_objects_si(g_faces_oi_curr(f)) != -1) {
                    select_face = false;
                }
                if (g_select_only_valid_semantic_id && g_segmentation_state_curr.mesh_objects_si(g_faces_oi_curr(f)) == -1) {
                    select_face = false;
                }

                if (select_face) {
                    if (g_prefer_select_faces_by_mode == PREFER_SELECT_FACES_BY_MODE_OBJECT_MESH_ID) {
                        selected_mesh_object_ids.insert(g_faces_oi_curr(f));
                    }

                    if (g_prefer_select_faces_by_mode == PREFER_SELECT_FACES_BY_MODE_SEMANTIC_INSTANCE_ID) {
                        if (g_segmentation_state_curr.mesh_objects_sii(g_faces_oi_curr(f)) == -1) {
                            selected_mesh_object_ids.insert(g_faces_oi_curr(f));
                        } else {
                            arma::uvec indices = arma::find(g_segmentation_state_curr.mesh_objects_sii == g_segmentation_state_curr.mesh_objects_sii(g_faces_oi_curr(f)));
                            for (auto i : indices) {
                                selected_mesh_object_ids.insert(i);
                            }
                        }
                    }

                    if (g_prefer_select_faces_by_mode == PREFER_SELECT_FACES_BY_MODE_SEMANTIC_ID) {
                        if (g_segmentation_state_curr.mesh_objects_si(g_faces_oi_curr(f)) == -1) {
                            selected_mesh_object_ids.insert(g_faces_oi_curr(f));
                        } else {
                            arma::uvec indices = arma::find(g_segmentation_state_curr.mesh_objects_si == g_segmentation_state_curr.mesh_objects_si(g_faces_oi_curr(f)));
                            for (auto i : indices) {
                                selected_mesh_object_ids.insert(i);
                            }
                        }
                    }
                }
            }

            //
            // save segmentation state
            //

            g_segmentation_state_prev = g_segmentation_state_curr;
            g_can_undo                = true;
            g_can_redo                = false;

            //
            // update segmentation state
            //

            auto updated_segmentation_state = false;
            for (auto m : selected_mesh_object_ids) {
                updated_segmentation_state = true;
                if (g_segmentation_layer == SEGMENTATION_LAYER_SEMANTIC_INSTANCE) {                    
                    int layer_index_val;
                    if (g_erase_mode) {
                        layer_index_val = -1;
                    } else {
                        if (g_assign_unique_semantic_instance_ids_to_each_mesh_object) {
                            g_semantic_instance_id         = create_new_semantic_instance_segmentation_id();
                            g_updated_semantic_instance_id = true;
                        }
                        layer_index_val = g_semantic_instance_id;
                    }
                    g_segmentation_state_curr.mesh_objects_sii(m) = layer_index_val;
                }

                if (g_segmentation_layer == SEGMENTATION_LAYER_SEMANTIC) {
                    int layer_index_val;
                    if (g_erase_mode) {
                        layer_index_val = -1;
                    } else {
                        layer_index_val = g_semantic_id;
                    }
                    g_segmentation_state_curr.mesh_objects_si(m) = layer_index_val;
                }                
            }

            if (updated_segmentation_state) {
                update_derived_segmentation_state(selected_mesh_object_ids);
                set_viewer_mesh_colors();
            }
        }
    }

    g_mouse_rotation       = false;
    g_mouse_translation    = false;
    g_mouse_drawing        = false;
    g_mouse_drawing_create = false;

    return true;
}