Mixing and Geometry Selection menu actions

This commit is contained in:
brunoherbelin
2021-03-15 21:26:30 +01:00
parent 3b09bc877c
commit ecba54196f
5 changed files with 212 additions and 54 deletions

View File

@@ -21,6 +21,7 @@
#include "Decorations.h" #include "Decorations.h"
#include "UserInterfaceManager.h" #include "UserInterfaceManager.h"
#include "BoundingBoxVisitor.h" #include "BoundingBoxVisitor.h"
#include "ActionManager.h"
#include "Log.h" #include "Log.h"
#include "GeometryView.h" #include "GeometryView.h"
@@ -150,7 +151,7 @@ void GeometryView::update(float dt)
if (Mixer::manager().view() == this ) if (Mixer::manager().view() == this )
{ {
updateSelectionOverlay(); updateSelectionOverlay();
overlay_selection_icon_->visible_ = false; // overlay_selection_icon_->visible_ = false;
} }
} }
@@ -268,34 +269,114 @@ void GeometryView::draw()
if (ImGui::BeginPopup("GeometrySourceContextMenu")) { if (ImGui::BeginPopup("GeometrySourceContextMenu")) {
Source *s = Mixer::manager().currentSource(); Source *s = Mixer::manager().currentSource();
if (s != nullptr) { 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" )){ if (ImGui::Selectable( ICON_FA_VECTOR_SQUARE " Reset" )){
s->group(mode_)->scale_ = glm::vec3(1.f); s->group(mode_)->scale_ = glm::vec3(1.f);
s->group(mode_)->rotation_.z = 0; s->group(mode_)->rotation_.z = 0;
s->group(mode_)->crop_ = glm::vec3(1.f); s->group(mode_)->crop_ = glm::vec3(1.f);
s->group(mode_)->translation_ = glm::vec3(0.f); s->group(mode_)->translation_ = glm::vec3(0.f);
s->touch(); s->touch();
Action::manager().store(std::string("Source Reset."), s->id());
} }
else if (ImGui::Selectable( ICON_FA_EXPAND " Fit" )){ if (ImGui::Selectable( ICON_FA_CROSSHAIRS " Reset position" )){
glm::vec3 scale = glm::vec3(1.f); s->group(mode_)->translation_ = glm::vec3(0.f);
FrameBuffer *output = Mixer::manager().session()->frame(); s->touch();
if (output) scale.x = output->aspectRatio() / s->frame()->aspectRatio(); Action::manager().store(std::string("Source Reset position."), s->id());
s->group(mode_)->scale_ = scale; }
if (ImGui::Selectable( ICON_FA_COMPASS " Reset rotation" )){
s->group(mode_)->rotation_.z = 0; s->group(mode_)->rotation_.z = 0;
s->group(mode_)->translation_ = glm::vec3(0.f);
s->touch(); s->touch();
Action::manager().store(std::string("Source Reset rotation."), s->id());
} }
else if (ImGui::Selectable( ICON_FA_CROSSHAIRS " Center" )){ if (ImGui::Selectable( ICON_FA_EXPAND_ALT " Reset aspect ratio" )){
s->group(mode_)->translation_ = glm::vec3(0.f);
s->touch();
}
else if (ImGui::Selectable( ICON_FA_EXPAND_ALT " Restore aspect ratio" )){
s->group(mode_)->scale_.x = s->group(mode_)->scale_.y; 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->group(mode_)->scale_.x *= s->group(mode_)->crop_.x / s->group(mode_)->crop_.y;
s->touch(); s->touch();
Action::manager().store(std::string("Source aspect ratio."), s->id());
} }
} }
ImGui::EndPopup(); 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<glm::mat4>(), -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<glm::mat4>(), -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<float>() / 2.f;
}
else {
factor *= output_surface_->scale_.x / overlay_selection_->scale_.x;
}
glm::mat4 S = glm::scale(glm::identity<glm::mat4>(), glm::vec3(factor, factor, 1.f));
glm::mat4 R = glm::rotate(glm::identity<glm::mat4>(), 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<Node *, glm::vec2> GeometryView::pick(glm::vec2 P)
} }
break; 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); 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<Node *, glm::vec2> pick) View::Cursor GeometryView::grab (Source *s, glm::vec2 from, glm::vec2 to, std::pair<Node *, glm::vec2> pick)
{ {
View::Cursor ret = Cursor(); 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 ) ); float factor = glm::length( glm::vec2( selection_to ) ) / glm::length( glm::vec2( selection_from ) );
glm::mat4 S = glm::scale(glm::identity<glm::mat4>(), glm::vec3(factor, factor, 1.f)); glm::mat4 S = glm::scale(glm::identity<glm::mat4>(), glm::vec3(factor, factor, 1.f));
// if interaction with selection // if interaction with selection SCALING handle
if (pick.first == overlay_selection_scale_) { 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 ); glm::vec4 vec = S * glm::vec4( overlay_selection_stored_status_->scale_, 0.f );
overlay_selection_->scale_ = glm::vec3(vec); overlay_selection_->scale_ = glm::vec3(vec);
// Transform sources // apply to selection sources
// complete transform matrix (right to left) : move to center, scale and move back // NB: complete transform matrix (right to left) : move to center, scale and move back
glm::mat4 M = T * S * glm::inverse(T); glm::mat4 M = T * S * glm::inverse(T);
// apply to every sources in selection applySelectionTransform(M);
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();
}
// store action in history
current_action_ = "Scale selection";
current_id_ = Mixer::selection().front()->id();
ret.type = Cursor_ResizeNWSE; ret.type = Cursor_ResizeNWSE;
} }
// if interaction with selection ROTATION handle
else if (pick.first == overlay_selection_rotate_) { 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::mat4>(), glm::vec3(factor, factor, 1.f));
}
// compute rotation angle // compute rotation angle
float angle = glm::orientedAngle( glm::normalize(glm::vec2(selection_from)), glm::normalize(glm::vec2(selection_to))); float angle = glm::orientedAngle( glm::normalize(glm::vec2(selection_from)), glm::normalize(glm::vec2(selection_to)));
glm::mat4 R = glm::rotate(glm::identity<glm::mat4>(), angle, glm::vec3(0.f, 0.f, 1.f) ); glm::mat4 R = glm::rotate(glm::identity<glm::mat4>(), 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 ); glm::vec4 vec = S * glm::vec4( overlay_selection_stored_status_->scale_, 0.f );
overlay_selection_->scale_ = glm::vec3(vec); overlay_selection_->scale_ = glm::vec3(vec);
overlay_selection_->rotation_.z = overlay_selection_stored_status_->rotation_.z + angle; overlay_selection_->rotation_.z = overlay_selection_stored_status_->rotation_.z + angle;
// Transform sources // apply to selection sources
// complete transform matrix (right to left) : move to center, rotate, scale and move back // NB: complete transform matrix (right to left) : move to center, rotate, scale and move back
glm::mat4 M = T * S * R * glm::inverse(T); glm::mat4 M = T * S * R * glm::inverse(T);
// apply to every sources in selection: applySelectionTransform(M);
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();
}
// store action in history
current_action_ = "Scale and rotate selection";
current_id_ = Mixer::selection().front()->id();
ret.type = Cursor_Hand; ret.type = Cursor_Hand;
} }
} }
// update cursor // 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::SCALE]->visible_ = false;
s->handles_[mode_][Handles::CROP]->visible_ = false; s->handles_[mode_][Handles::CROP]->visible_ = false;
s->handles_[mode_][Handles::MENU]->visible_ = false; s->handles_[mode_][Handles::MENU]->visible_ = false;
// ROTATION on CENTER // ROTATION on CENTER
overlay_rotation_->visible_ = true; overlay_rotation_->visible_ = true;
overlay_rotation_->translation_.x = s->stored_status_->translation_.x; 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_->visible_ = true;
overlay_rotation_fix_->copyTransform(overlay_rotation_); overlay_rotation_fix_->copyTransform(overlay_rotation_);
overlay_rotation_clock_->visible_ = false; overlay_rotation_clock_->visible_ = false;
// rotation center to center of source (disregarding scale) // rotation center to center of source (disregarding scale)
glm::mat4 T = glm::translate(glm::identity<glm::mat4>(), s->stored_status_->translation_); glm::mat4 T = glm::translate(glm::identity<glm::mat4>(), s->stored_status_->translation_);
source_from = glm::inverse(T) * glm::vec4( scene_from, 1.f ); source_from = glm::inverse(T) * glm::vec4( scene_from, 1.f );
@@ -867,6 +982,11 @@ void GeometryView::terminate()
overlay_scaling_->visible_ = false; overlay_scaling_->visible_ = false;
overlay_crop_->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 // restore of all handles overlays
glm::vec2 c(0.f, 0.f); glm::vec2 c(0.f, 0.f);
for (auto sit = Mixer::manager().session()->begin(); for (auto sit = Mixer::manager().session()->begin();
@@ -944,3 +1064,4 @@ void GeometryView::updateSelectionOverlay()
overlay_selection_rotate_->color = overlay_selection_icon_->color; overlay_selection_rotate_->color = overlay_selection_icon_->color;
} }
} }

View File

@@ -23,12 +23,12 @@ private:
Surface *output_surface_; Surface *output_surface_;
Node *overlay_position_; Node *overlay_position_;
Node *overlay_position_cross_; Node *overlay_position_cross_;
Node *overlay_rotation_; Symbol *overlay_rotation_;
Node *overlay_rotation_fix_; Symbol *overlay_rotation_fix_;
Node *overlay_rotation_clock_; Node *overlay_rotation_clock_;
Node *overlay_rotation_clock_hand_; Node *overlay_rotation_clock_hand_;
Node *overlay_scaling_; Symbol *overlay_scaling_;
Node *overlay_scaling_cross_; Symbol *overlay_scaling_cross_;
Node *overlay_scaling_grid_; Node *overlay_scaling_grid_;
Node *overlay_crop_; Node *overlay_crop_;
@@ -37,6 +37,8 @@ private:
Group *overlay_selection_stored_status_; Group *overlay_selection_stored_status_;
Handles *overlay_selection_scale_; Handles *overlay_selection_scale_;
Handles *overlay_selection_rotate_; Handles *overlay_selection_rotate_;
void applySelectionTransform(glm::mat4 M);
}; };

View File

@@ -161,12 +161,43 @@ void MixingView::draw()
(*it)->group(View::MIXING)->translation_ -= overlay_selection_->translation_; (*it)->group(View::MIXING)->translation_ -= overlay_selection_->translation_;
(*it)->touch(); (*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>() / 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(); SourceList::iterator it = Mixer::selection().begin();
for (; it != Mixer::selection().end(); it++) { for (; it != Mixer::selection().end(); it++) {
(*it)->setAlpha(0.f); (*it)->setAlpha(0.f);
} }
Action::manager().store(std::string("Selection Expand to border."), Mixer::selection().front()->id());
} }
ImGui::PopStyleColor(2); ImGui::PopStyleColor(2);
ImGui::EndPopup(); ImGui::EndPopup();

View File

@@ -834,11 +834,15 @@ void TextureView::draw()
s->group(mode_)->translation_ = glm::vec3(0.f); s->group(mode_)->translation_ = glm::vec3(0.f);
s->touch(); 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->group(mode_)->translation_ = glm::vec3(0.f);
s->touch(); 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_)->scale_.y;
s->group(mode_)->scale_.x *= s->group(mode_)->crop_.x / s->group(mode_)->crop_.y; s->group(mode_)->scale_.x *= s->group(mode_)->crop_.x / s->group(mode_)->crop_.y;
s->touch(); s->touch();

View File

@@ -626,13 +626,13 @@ void UserInterface::handleMouse()
Source *current = Mixer::manager().currentSource(); Source *current = Mixer::manager().currentSource();
if (current) if (current)
{ {
// grab current sources
View::Cursor c = Mixer::manager().view()->grab(current, mouseclic[ImGuiMouseButton_Left], mouse_smooth, picked);
// grab others from selection // grab others from selection
for (auto it = Mixer::selection().begin(); it != Mixer::selection().end(); it++) { for (auto it = Mixer::selection().begin(); it != Mixer::selection().end(); it++) {
if ( *it != current ) if ( *it != current )
Mixer::manager().view()->grab(*it, mouseclic[ImGuiMouseButton_Left], mouse_smooth, picked); 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); setMouseCursor(io.MousePos, c);
} }
// action on other (non-source) elements in the view // action on other (non-source) elements in the view