diff --git a/GeometryView.cpp b/GeometryView.cpp index a58d08e..337090c 100644 --- a/GeometryView.cpp +++ b/GeometryView.cpp @@ -21,6 +21,7 @@ #include "Decorations.h" #include "UserInterfaceManager.h" #include "BoundingBoxVisitor.h" +#include "ActionManager.h" #include "Log.h" #include "GeometryView.h" @@ -150,7 +151,7 @@ void GeometryView::update(float dt) if (Mixer::manager().view() == this ) { updateSelectionOverlay(); - overlay_selection_icon_->visible_ = false; +// overlay_selection_icon_->visible_ = false; } } @@ -268,34 +269,114 @@ void GeometryView::draw() if (ImGui::BeginPopup("GeometrySourceContextMenu")) { Source *s = Mixer::manager().currentSource(); if (s != nullptr) { + if (ImGui::Selectable( ICON_FA_EXPAND " Fit" )){ + s->group(mode_)->scale_ = glm::vec3(output_surface_->scale_.x/ s->frame()->aspectRatio(), 1.f, 1.f); + s->group(mode_)->rotation_.z = 0; + s->group(mode_)->translation_ = glm::vec3(0.f); + s->touch(); + Action::manager().store(std::string("Source Fit."), s->id()); + } if (ImGui::Selectable( ICON_FA_VECTOR_SQUARE " Reset" )){ s->group(mode_)->scale_ = glm::vec3(1.f); s->group(mode_)->rotation_.z = 0; s->group(mode_)->crop_ = glm::vec3(1.f); s->group(mode_)->translation_ = glm::vec3(0.f); s->touch(); + Action::manager().store(std::string("Source Reset."), s->id()); } - else if (ImGui::Selectable( ICON_FA_EXPAND " Fit" )){ - glm::vec3 scale = glm::vec3(1.f); - FrameBuffer *output = Mixer::manager().session()->frame(); - if (output) scale.x = output->aspectRatio() / s->frame()->aspectRatio(); - s->group(mode_)->scale_ = scale; + if (ImGui::Selectable( ICON_FA_CROSSHAIRS " Reset position" )){ + s->group(mode_)->translation_ = glm::vec3(0.f); + s->touch(); + Action::manager().store(std::string("Source Reset position."), s->id()); + } + if (ImGui::Selectable( ICON_FA_COMPASS " Reset rotation" )){ s->group(mode_)->rotation_.z = 0; - s->group(mode_)->translation_ = glm::vec3(0.f); s->touch(); + Action::manager().store(std::string("Source Reset rotation."), s->id()); } - else if (ImGui::Selectable( ICON_FA_CROSSHAIRS " Center" )){ - s->group(mode_)->translation_ = glm::vec3(0.f); - s->touch(); - } - else if (ImGui::Selectable( ICON_FA_EXPAND_ALT " Restore aspect ratio" )){ + if (ImGui::Selectable( ICON_FA_EXPAND_ALT " Reset aspect ratio" )){ s->group(mode_)->scale_.x = s->group(mode_)->scale_.y; s->group(mode_)->scale_.x *= s->group(mode_)->crop_.x / s->group(mode_)->crop_.y; s->touch(); + Action::manager().store(std::string("Source aspect ratio."), s->id()); } } ImGui::EndPopup(); } + // display popup menu + if (show_context_menu_ == MENU_SELECTION) { + ImGui::OpenPopup( "GeometrySelectionContextMenu" ); + show_context_menu_ = MENU_NONE; + } + if (ImGui::BeginPopup("GeometrySelectionContextMenu")) { + + // colored context menu + ImGui::PushStyleColor(ImGuiCol_Text, ImGuiToolkit::HighlightColor()); + ImGui::PushStyleColor(ImGuiCol_HeaderHovered, ImVec4(0.36f, 0.36f, 0.36f, 0.44f)); + + // batch manipulation of sources in Geometry view + if (ImGui::Selectable( ICON_FA_EXPAND " Fit all" )){ + for (auto sit = Mixer::selection().begin(); sit != Mixer::selection().end(); sit++){ + (*sit)->group(mode_)->scale_ = glm::vec3(output_surface_->scale_.x/ (*sit)->frame()->aspectRatio(), 1.f, 1.f); + (*sit)->group(mode_)->rotation_.z = 0; + (*sit)->group(mode_)->translation_ = glm::vec3(0.f); + (*sit)->touch(); + } + Action::manager().store(std::string("Selection Fit all."), Mixer::selection().front()->id()); + } + if (ImGui::Selectable( ICON_FA_VECTOR_SQUARE " Reset all" )){ + // apply to every sources in selection + for (auto sit = Mixer::selection().begin(); sit != Mixer::selection().end(); sit++){ + (*sit)->group(mode_)->scale_ = glm::vec3(1.f); + (*sit)->group(mode_)->rotation_.z = 0; + (*sit)->group(mode_)->crop_ = glm::vec3(1.f); + (*sit)->group(mode_)->translation_ = glm::vec3(0.f); + (*sit)->touch(); + } + Action::manager().store(std::string("Selection Reset all."), Mixer::selection().front()->id()); + } + if (ImGui::Selectable( ICON_FA_COMPASS " Align all" )){ + // apply to every sources in selection + for (auto sit = Mixer::selection().begin(); sit != Mixer::selection().end(); sit++){ + (*sit)->group(mode_)->rotation_.z = overlay_selection_->rotation_.z; + (*sit)->touch(); + } + Action::manager().store(std::string("Selection Align all."), Mixer::selection().front()->id()); + } +// if (ImGui::Selectable( ICON_FA_TH " Mosaic" )){ // TODO + +// } + ImGui::Separator(); + // manipulation of selection + if (ImGui::Selectable( ICON_FA_CROSSHAIRS " Center" )){ + glm::mat4 T = glm::translate(glm::identity(), -overlay_selection_->translation_); + initiate(); + applySelectionTransform(T); + Action::manager().store(std::string("Selection Center."), Mixer::selection().front()->id()); + } + if (ImGui::Selectable( ICON_FA_COMPRESS " Best Fit" )){ + glm::mat4 T = glm::translate(glm::identity(), -overlay_selection_->translation_); + float factor = 1.f; + float angle = -overlay_selection_->rotation_.z; + if ( overlay_selection_->scale_.x < overlay_selection_->scale_.y) { + factor *= output_surface_->scale_.x / overlay_selection_->scale_.y; + angle += glm::pi() / 2.f; + } + else { + factor *= output_surface_->scale_.x / overlay_selection_->scale_.x; + } + glm::mat4 S = glm::scale(glm::identity(), glm::vec3(factor, factor, 1.f)); + glm::mat4 R = glm::rotate(glm::identity(), angle, glm::vec3(0.f, 0.f, 1.f) ); + glm::mat4 M = S * R * T; + initiate(); + applySelectionTransform(M); + Action::manager().store(std::string("Selection Best Fit."), Mixer::selection().front()->id()); + } + + + ImGui::PopStyleColor(2); + ImGui::EndPopup(); + } } @@ -392,6 +473,11 @@ std::pair GeometryView::pick(glm::vec2 P) } break; } + else if ( overlay_selection_icon_ != nullptr && (*itp).first == overlay_selection_icon_ ) { + pick = (*itp); + openContextMenu(MENU_SELECTION); + break; + } } } @@ -407,6 +493,22 @@ bool GeometryView::canSelect(Source *s) { return ( View::canSelect(s) && s->active() && s->workspace() == Settings::application.current_workspace); } + +void GeometryView::applySelectionTransform(glm::mat4 M) +{ + for (auto sit = Mixer::selection().begin(); sit != Mixer::selection().end(); sit++){ + // recompute all from matrix transform + glm::mat4 transform = M * (*sit)->stored_status_->transform_; + glm::vec3 tra, rot, sca; + GlmToolkit::inverse_transform(transform, tra, rot, sca); + (*sit)->group(mode_)->translation_ = tra; + (*sit)->group(mode_)->scale_ = sca; + (*sit)->group(mode_)->rotation_ = rot; + // will have to be updated + (*sit)->touch(); + } +} + View::Cursor GeometryView::grab (Source *s, glm::vec2 from, glm::vec2 to, std::pair pick) { View::Cursor ret = Cursor(); @@ -431,60 +533,75 @@ View::Cursor GeometryView::grab (Source *s, glm::vec2 from, glm::vec2 to, std::p float factor = glm::length( glm::vec2( selection_to ) ) / glm::length( glm::vec2( selection_from ) ); glm::mat4 S = glm::scale(glm::identity(), glm::vec3(factor, factor, 1.f)); - // if interaction with selection + // if interaction with selection SCALING handle if (pick.first == overlay_selection_scale_) { - // apply to overlay + // show manipulation overlay + overlay_scaling_cross_->visible_ = true; + overlay_scaling_grid_->visible_ = false; + overlay_scaling_->visible_ = true; + overlay_scaling_->translation_.x = overlay_selection_stored_status_->translation_.x; + overlay_scaling_->translation_.y = overlay_selection_stored_status_->translation_.y; + overlay_scaling_->rotation_.z = overlay_selection_stored_status_->rotation_.z; + overlay_scaling_->update(0); + overlay_scaling_cross_->copyTransform(overlay_scaling_); + overlay_scaling_->color = overlay_selection_icon_->color; + overlay_scaling_cross_->color = overlay_selection_icon_->color; + + // apply to selection overlay glm::vec4 vec = S * glm::vec4( overlay_selection_stored_status_->scale_, 0.f ); overlay_selection_->scale_ = glm::vec3(vec); - // Transform sources - // complete transform matrix (right to left) : move to center, scale and move back + // apply to selection sources + // NB: complete transform matrix (right to left) : move to center, scale and move back glm::mat4 M = T * S * glm::inverse(T); - // apply to every sources in selection - for (auto sit = Mixer::selection().begin(); sit != Mixer::selection().end(); sit++){ - // displacement - vec = M * glm::vec4( (*sit)->stored_status_->translation_, 1.f ); - (*sit)->group(mode_)->translation_ = glm::vec3(vec); - // scale - vec = M * glm::vec4( (*sit)->stored_status_->scale_, 0.f ); - (*sit)->group(mode_)->scale_ = glm::vec3(vec); - // will have to be updated - (*sit)->touch(); - } + applySelectionTransform(M); + // store action in history + current_action_ = "Scale selection"; + current_id_ = Mixer::selection().front()->id(); ret.type = Cursor_ResizeNWSE; } + // if interaction with selection ROTATION handle else if (pick.first == overlay_selection_rotate_) { + // show manipulation overlay + overlay_rotation_->visible_ = true; + overlay_rotation_->translation_.x = overlay_selection_stored_status_->translation_.x; + overlay_rotation_->translation_.y = overlay_selection_stored_status_->translation_.y; + overlay_rotation_->update(0); + overlay_rotation_->color = overlay_selection_icon_->color; + overlay_rotation_fix_->visible_ = false; + overlay_rotation_fix_->copyTransform(overlay_rotation_); + overlay_rotation_fix_->color = overlay_selection_icon_->color; + + // cancel out scaling with SHIFT modifier key + if (UserInterface::manager().shiftModifier()) { + overlay_rotation_fix_->visible_ = true; + float factor = glm::length( glm::vec2( overlay_selection_->scale_ ) ) / glm::length( glm::vec2( overlay_selection_stored_status_->scale_ ) ); + S = glm::scale(glm::identity(), glm::vec3(factor, factor, 1.f)); + } + // compute rotation angle float angle = glm::orientedAngle( glm::normalize(glm::vec2(selection_from)), glm::normalize(glm::vec2(selection_to))); glm::mat4 R = glm::rotate(glm::identity(), angle, glm::vec3(0.f, 0.f, 1.f) ); - // apply to overlay + // apply to selection overlay glm::vec4 vec = S * glm::vec4( overlay_selection_stored_status_->scale_, 0.f ); overlay_selection_->scale_ = glm::vec3(vec); overlay_selection_->rotation_.z = overlay_selection_stored_status_->rotation_.z + angle; - // Transform sources - // complete transform matrix (right to left) : move to center, rotate, scale and move back + // apply to selection sources + // NB: complete transform matrix (right to left) : move to center, rotate, scale and move back glm::mat4 M = T * S * R * glm::inverse(T); - // apply to every sources in selection: - for (auto sit = Mixer::selection().begin(); sit != Mixer::selection().end(); sit++){ - - glm::mat4 transform = M * (*sit)->stored_status_->transform_; - glm::vec3 tra, rot, sca; - GlmToolkit::inverse_transform(transform, tra, rot, sca); - (*sit)->group(mode_)->translation_ = tra; - (*sit)->group(mode_)->scale_ = sca; - (*sit)->group(mode_)->rotation_ = rot; - - // will have to be updated - (*sit)->touch(); - } + applySelectionTransform(M); + // store action in history + current_action_ = "Scale and rotate selection"; + current_id_ = Mixer::selection().front()->id(); ret.type = Cursor_Hand; } + } // update cursor @@ -751,7 +868,6 @@ View::Cursor GeometryView::grab (Source *s, glm::vec2 from, glm::vec2 to, std::p s->handles_[mode_][Handles::SCALE]->visible_ = false; s->handles_[mode_][Handles::CROP]->visible_ = false; s->handles_[mode_][Handles::MENU]->visible_ = false; - // ROTATION on CENTER overlay_rotation_->visible_ = true; overlay_rotation_->translation_.x = s->stored_status_->translation_.x; @@ -760,7 +876,6 @@ View::Cursor GeometryView::grab (Source *s, glm::vec2 from, glm::vec2 to, std::p overlay_rotation_fix_->visible_ = true; overlay_rotation_fix_->copyTransform(overlay_rotation_); overlay_rotation_clock_->visible_ = false; - // rotation center to center of source (disregarding scale) glm::mat4 T = glm::translate(glm::identity(), s->stored_status_->translation_); source_from = glm::inverse(T) * glm::vec4( scene_from, 1.f ); @@ -867,6 +982,11 @@ void GeometryView::terminate() overlay_scaling_->visible_ = false; overlay_crop_->visible_ = false; + overlay_rotation_->color = glm::vec4(1.f, 1.f, 1.f, 0.8f); + overlay_rotation_fix_->color = glm::vec4(1.f, 1.f, 1.f, 0.8f); + overlay_scaling_->color = glm::vec4(1.f, 1.f, 1.f, 0.8f); + overlay_scaling_cross_->color = glm::vec4(1.f, 1.f, 1.f, 0.8f); + // restore of all handles overlays glm::vec2 c(0.f, 0.f); for (auto sit = Mixer::manager().session()->begin(); @@ -944,3 +1064,4 @@ void GeometryView::updateSelectionOverlay() overlay_selection_rotate_->color = overlay_selection_icon_->color; } } + diff --git a/GeometryView.h b/GeometryView.h index 2c9d1b5..5bffac9 100644 --- a/GeometryView.h +++ b/GeometryView.h @@ -23,12 +23,12 @@ private: Surface *output_surface_; Node *overlay_position_; Node *overlay_position_cross_; - Node *overlay_rotation_; - Node *overlay_rotation_fix_; + Symbol *overlay_rotation_; + Symbol *overlay_rotation_fix_; Node *overlay_rotation_clock_; Node *overlay_rotation_clock_hand_; - Node *overlay_scaling_; - Node *overlay_scaling_cross_; + Symbol *overlay_scaling_; + Symbol *overlay_scaling_cross_; Node *overlay_scaling_grid_; Node *overlay_crop_; @@ -37,6 +37,8 @@ private: Group *overlay_selection_stored_status_; Handles *overlay_selection_scale_; Handles *overlay_selection_rotate_; + + void applySelectionTransform(glm::mat4 M); }; diff --git a/MixingView.cpp b/MixingView.cpp index 23635c3..ee3b5df 100644 --- a/MixingView.cpp +++ b/MixingView.cpp @@ -161,12 +161,43 @@ void MixingView::draw() (*it)->group(View::MIXING)->translation_ -= overlay_selection_->translation_; (*it)->touch(); } + Action::manager().store(std::string("Selection Center."), Mixer::selection().front()->id()); } - if (ImGui::Selectable( ICON_FA_EXPAND_ARROWS_ALT " Expand & Hide" )){ + if (ImGui::Selectable( ICON_FA_HAYKAL " Distribute" )){ + SourceList list; + glm::vec2 center = glm::vec2(0.f, 0.f); + for (SourceList::iterator it = Mixer::selection().begin(); it != Mixer::selection().end(); it++) { + list.push_back(*it); + // compute barycenter (1) + center += glm::vec2((*it)->group(View::MIXING)->translation_); + } + // compute barycenter (2) + center /= list.size(); + // sort the vector of sources in clockwise order around the center pos_ + list = mixing_sorted( list, center); + // average distance + float d = 0.f; + for (SourceList::iterator it = list.begin(); it != list.end(); it++) { + d += glm::distance(glm::vec2((*it)->group(View::MIXING)->translation_), center); + } + d /= list.size(); + // distribute with equal angle + float angle = 0.f; + for (SourceList::iterator it = list.begin(); it != list.end(); it++) { + glm::vec2 P = center + glm::rotate(glm::vec2(0.f, d), angle); + (*it)->group(View::MIXING)->translation_.x = P.x; + (*it)->group(View::MIXING)->translation_.y = P.y; + (*it)->touch(); + angle -= glm::two_pi() / float(list.size()); + } + Action::manager().store(std::string("Selection Distribute."), Mixer::selection().front()->id()); + } + if (ImGui::Selectable( ICON_FA_EXPAND_ARROWS_ALT " Expand to border" )){ SourceList::iterator it = Mixer::selection().begin(); for (; it != Mixer::selection().end(); it++) { (*it)->setAlpha(0.f); } + Action::manager().store(std::string("Selection Expand to border."), Mixer::selection().front()->id()); } ImGui::PopStyleColor(2); ImGui::EndPopup(); diff --git a/TextureView.cpp b/TextureView.cpp index 003d223..30b1d85 100644 --- a/TextureView.cpp +++ b/TextureView.cpp @@ -834,11 +834,15 @@ void TextureView::draw() s->group(mode_)->translation_ = glm::vec3(0.f); s->touch(); } - else if (ImGui::Selectable( ICON_FA_CROSSHAIRS " Center" )){ + if (ImGui::Selectable( ICON_FA_CROSSHAIRS " Reset position" )){ s->group(mode_)->translation_ = glm::vec3(0.f); s->touch(); } - else if (ImGui::Selectable( ICON_FA_EXPAND_ALT " Restore aspect ratio" )){ + if (ImGui::Selectable( ICON_FA_COMPASS " Reset rotation" )){ + s->group(mode_)->rotation_.z = 0; + s->touch(); + } + if (ImGui::Selectable( ICON_FA_EXPAND_ALT " Reset aspect ratio" )){ s->group(mode_)->scale_.x = s->group(mode_)->scale_.y; s->group(mode_)->scale_.x *= s->group(mode_)->crop_.x / s->group(mode_)->crop_.y; s->touch(); diff --git a/UserInterfaceManager.cpp b/UserInterfaceManager.cpp index ec76939..20efc67 100644 --- a/UserInterfaceManager.cpp +++ b/UserInterfaceManager.cpp @@ -626,13 +626,13 @@ void UserInterface::handleMouse() Source *current = Mixer::manager().currentSource(); if (current) { - // grab current sources - View::Cursor c = Mixer::manager().view()->grab(current, mouseclic[ImGuiMouseButton_Left], mouse_smooth, picked); // grab others from selection for (auto it = Mixer::selection().begin(); it != Mixer::selection().end(); it++) { if ( *it != current ) Mixer::manager().view()->grab(*it, mouseclic[ImGuiMouseButton_Left], mouse_smooth, picked); } + // grab current sources + View::Cursor c = Mixer::manager().view()->grab(current, mouseclic[ImGuiMouseButton_Left], mouse_smooth, picked); setMouseCursor(io.MousePos, c); } // action on other (non-source) elements in the view