/* * This file is part of vimix - video live mixer * * **Copyright** (C) 2020-2021 Bruno Herbelin * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . **/ #include #include #include #include #include #include "Log.h" #include "Decorations.h" #include "GlmToolkit.h" #include "PickingVisitor.h" PickingVisitor::PickingVisitor(glm::vec3 coordinates, bool force) : Visitor(), force_(force), modelview_(glm::mat4(1.f)) { points_.push_back( coordinates ); } PickingVisitor::PickingVisitor(glm::vec3 selectionstart, glm::vec3 selection_end, bool force) : Visitor(), force_(force), modelview_(glm::mat4(1.f)) { points_.push_back( selectionstart ); points_.push_back( selection_end ); } void PickingVisitor::visit(Node &n) { // use the transform modified during update modelview_ *= n.transform_; } void PickingVisitor::visit(Group &n) { if (!n.visible_ && !force_) return; glm::mat4 mv = modelview_; for (NodeSet::iterator node = n.begin(); node != n.end(); ++node) { if ( (*node)->visible_ || force_) (*node)->accept(*this); modelview_ = mv; } } void PickingVisitor::visit(Switch &n) { if ((!n.visible_ && !force_) || n.numChildren()<1) return; glm::mat4 mv = modelview_; n.activeChild()->accept(*this); modelview_ = mv; } void PickingVisitor::visit(Primitive &) { // by default, a Primitive is not interactive } void PickingVisitor::visit(Surface &n) { if (!n.visible_ && !force_) return; // if more than one point given for testing: test overlap if (points_.size() > 1) { // create bounding box for those points (2 in practice) GlmToolkit::AxisAlignedBoundingBox bb_points; bb_points.extend(points_); // update the coordinates of the Surface bounding box to match transform GlmToolkit::AxisAlignedBoundingBox surf; surf = n.bbox().transformed(modelview_); // Test inclusion of all four corners of the Surface inside the selection bounding box if ( bb_points.contains( surf) ) { // add this surface to the nodes picked nodes_.push_back( std::pair(&n, glm::vec2(0.f)) ); } // // ALTERNATIVE BEHAVIOR : test bounding box for overlap only // // apply inverse transform // bb_points = bb_points.transformed(glm::inverse(modelview_)) ; // if ( bb_points.intersect( n.bbox() ) ) { // // add this surface to the nodes picked // nodes_.push_back( std::pair(&n, glm::vec2(0.f)) ); // } } // only one point else if (points_.size() > 0) { // apply inverse transform to the point of interest glm::vec4 P = glm::inverse(modelview_) * glm::vec4( points_[0], 1.f ); // test bounding box for picking from a single point if ( n.bbox().contains( glm::vec3(P)) ) { // add this surface to the nodes picked nodes_.push_back( std::pair(&n, glm::vec2(P)) ); } } } void PickingVisitor::visit(Disk &n) { // discard if not visible or if not exactly one point given for picking if ((!n.visible_ && !force_) || points_.size() != 1) return; // apply inverse transform to the point of interest glm::vec4 P = glm::inverse(modelview_) * glm::vec4( points_[0], 1.f ); // test distance for picking from a single point if ( glm::length(glm::vec2(P)) < 1.f ) // add this surface to the nodes picked nodes_.push_back( std::pair(&n, glm::vec2(P)) ); } void PickingVisitor::visit(Handles &n) { // discard if not visible or if not exactly one point given for picking if ((!n.visible_ && !force_) || points_.size() != 1) return; // apply inverse transform to the point of interest glm::vec4 P = glm::inverse(modelview_) * glm::vec4( points_[0], 1.f ); // inverse transform to check the scale glm::vec4 S = glm::inverse(modelview_) * glm::vec4( 0.05f, 0.05f, 0.f, 0.f ); float scale = glm::length( glm::vec2(S) ); // extract rotation from modelview glm::mat4 ctm; glm::vec3 rot(0.f); glm::vec4 vec = modelview_ * glm::vec4(1.f, 0.f, 0.f, 0.f); rot.z = glm::orientedAngle( glm::vec3(1.f, 0.f, 0.f), glm::normalize(glm::vec3(vec)), glm::vec3(0.f, 0.f, 1.f) ); ctm = glm::rotate(glm::identity(), -rot.z, glm::vec3(0.f, 0.f, 1.f)) * modelview_ ; vec = ctm * glm::vec4(1.f, 1.f, 0.f, 0.f); glm::vec4 mirror = glm::sign(vec); bool picked = false; if ( n.type() == Handles::RESIZE ) { // 4 corners picked = ( glm::length(glm::vec2(+1.f, +1.f)- glm::vec2(P)) < scale || glm::length(glm::vec2(+1.f, -1.f)- glm::vec2(P)) < scale || glm::length(glm::vec2(-1.f, +1.f)- glm::vec2(P)) < scale || glm::length(glm::vec2(-1.f, -1.f)- glm::vec2(P)) < scale ); } else if ( n.type() == Handles::RESIZE_H ){ // left & right picked = ( glm::length(glm::vec2(+1.f, 0.f)- glm::vec2(P)) < scale || glm::length(glm::vec2(-1.f, 0.f)- glm::vec2(P)) < scale ); } else if ( n.type() == Handles::RESIZE_V ){ // top & bottom picked = ( glm::length(glm::vec2(0.f, +1.f)- glm::vec2(P)) < scale || glm::length(glm::vec2(0.f, -1.f)- glm::vec2(P)) < scale ); } else if ( n.type() == Handles::ROTATE ){ // the icon for rotation is on the right top corner at (0.12, 0.12) in scene coordinates glm::vec4 pos = glm::inverse(ctm) * ( mirror * glm::vec4( 0.12f, 0.12f, 0.f, 0.f ) ); picked = glm::length( glm::vec2( 1.f, 1.f) + glm::vec2(pos) - glm::vec2(P) ) < 1.5f * scale; } else if ( n.type() == Handles::SCALE ){ // the icon for scaling is on the right bottom corner at (0.12, -0.12) in scene coordinates glm::vec4 pos = glm::inverse(ctm) * ( mirror * glm::vec4( 0.12f, -0.12f, 0.f, 0.f ) ); picked = glm::length( glm::vec2( 1.f, -1.f) + glm::vec2(pos) - glm::vec2(P) ) < 1.5f * scale; } else if ( n.type() == Handles::CROP ){ // the icon for cropping is on the left bottom corner at (0.12, 0.12) in scene coordinates glm::vec4 pos = glm::inverse(ctm) * ( mirror * glm::vec4( 0.12f, 0.12f, 0.f, 0.f ) ); picked = glm::length( glm::vec2( -1.f, -1.f) + glm::vec2(pos) - glm::vec2(P) ) < 1.5f * scale; } else if ( n.type() == Handles::MENU ){ // the icon for menu is on the left top corner at (-0.12, 0.12) in scene coordinates glm::vec4 pos = glm::inverse(ctm) * ( mirror * glm::vec4( -0.12f, 0.12f, 0.f, 0.f ) ); picked = glm::length( glm::vec2( -1.f, 1.f) + glm::vec2(pos) - glm::vec2(P) ) < 1.5f * scale; } else if ( n.type() == Handles::LOCKED || n.type() == Handles::UNLOCKED ){ // the icon for lock is on the right bottom corner at (-0.12, 0.12) in scene coordinates glm::vec4 pos = glm::inverse(ctm) * ( mirror * glm::vec4( -0.12f, 0.12f, 0.f, 0.f ) ); picked = glm::length( glm::vec2( 1.f, -1.f) + glm::vec2(pos) - glm::vec2(P) ) < 1.5f * scale; } if ( picked ) // add this to the nodes picked nodes_.push_back( std::pair(&n, glm::vec2(P)) ); } void PickingVisitor::visit(Symbol& n) { // discard if not visible or if not exactly one point given for picking if ((!n.visible_ && !force_) || points_.size() != 1) return; // apply inverse transform to the point of interest glm::vec4 P = glm::inverse(modelview_) * glm::vec4( points_[0], 1.f ); // test bounding box for picking from a single point if ( n.bbox().contains( glm::vec3(P)) ) { // add this to the nodes picked nodes_.push_back( std::pair(&n, glm::vec2(P)) ); } } void PickingVisitor::visit(Scene &n) { n.root()->accept(*this); }