Draft CANVAS editing

Starting to add Canvas edit in Geometry view
This commit is contained in:
Bruno Herbelin
2025-01-25 12:05:51 +01:00
parent 928e798d86
commit ea4615ea65
6 changed files with 517 additions and 184 deletions

View File

@@ -583,6 +583,15 @@ void DisplaysView::draw()
// Add / Remove windows
ImGui::SameLine();
if ( Settings::application.num_output_windows > 0 ) {
if (ImGuiToolkit::IconButton(19, 4, "Less windows")) {
--Settings::application.num_output_windows;
current_window_ = -1;
}
}
else
ImGuiToolkit::Icon(19, 4, false);
ImGui::SameLine();
if ( Settings::application.num_output_windows < MAX_OUTPUT_WINDOW) {
if (ImGuiToolkit::IconButton(18, 4, "More windows")) {
++Settings::application.num_output_windows;
@@ -592,15 +601,6 @@ void DisplaysView::draw()
}
else
ImGuiToolkit::Icon(18, 4, false);
ImGui::SameLine();
if ( Settings::application.num_output_windows > 0 ) {
if (ImGuiToolkit::IconButton(19, 4, "Less windows")) {
--Settings::application.num_output_windows;
current_window_ = -1;
}
}
else
ImGuiToolkit::Icon(19, 4, false);
// Modify current window
if (current_window_ > -1) {

View File

@@ -47,6 +47,16 @@
#include "GeometryView.h"
const char* GeometryView::editor_icons[2] = { ICON_FA_OBJECT_UNGROUP, ICON_FA_BORDER_ALL };
const char *GeometryView::editor_names[2] = {"Edit sources", " Edit canvas"};
void Canvas::setCurrent(bool on)
{
root_->visible_ = true;
frames_->setActive( on ? 1 : 0);
}
GeometryView::GeometryView() : View(GEOMETRY)
{
scene.root()->scale_ = glm::vec3(GEOMETRY_DEFAULT_SCALE, GEOMETRY_DEFAULT_SCALE, 1.0f);
@@ -62,9 +72,52 @@ GeometryView::GeometryView() : View(GEOMETRY)
output_surface_ = new Surface;
output_surface_->visible_ = false;
scene.fg()->attach(output_surface_);
Frame *border = new Frame(Frame::SHARP, Frame::THIN, Frame::NONE);
border->color = glm::vec4( COLOR_FRAME, 1.f );
scene.fg()->attach(border);
// Geometry surfaces
canvas_ = std::vector<Canvas>(MAX_OUTPUT_CANVAS);
for (auto &c : canvas_) {
// root node
c.root_ = new Group;
scene.fg()->attach(c.root_);
c.root_->visible_ = false;
// attach all modes of frame to the switch node
c.frames_ = new Switch;
c.root_->attach(c.frames_);
// frames_[0] : not current
Frame *frame = new Frame(Frame::SHARP, Frame::THIN, Frame::NONE);
frame->color = glm::vec4(COLOR_FRAME, 0.95f);
c.frames_->attach(frame);
// frames_[1] : current
c.overlays_ = new Group;
c.frames_->attach(c.overlays_);
frame = new Frame(Frame::SHARP, Frame::LARGE, Frame::GLOW); // current
frame->color = glm::vec4(COLOR_FRAME, 1.f);
c.overlays_->attach(frame);
c.menu_ = new Handles(Handles::MENU);
c.menu_->color = glm::vec4(COLOR_FRAME, 1.f);
c.overlays_->attach(c.menu_);
c.handles_[Handles::RESIZE] = new Handles(Handles::RESIZE);
c.handles_[Handles::RESIZE]->color = glm::vec4(COLOR_FRAME, 1.f);
c.overlays_->attach(c.handles_[Handles::RESIZE]);
c.handles_[Handles::RESIZE_H] = new Handles(Handles::RESIZE_H);
c.handles_[Handles::RESIZE_H]->color = glm::vec4(COLOR_FRAME, 1.f);
c.overlays_->attach(c.handles_[Handles::RESIZE_H]);
c.handles_[Handles::RESIZE_V] = new Handles(Handles::RESIZE_V);
c.handles_[Handles::RESIZE_V]->color = glm::vec4(COLOR_FRAME, 1.f);
c.overlays_->attach(c.handles_[Handles::RESIZE_V]);
}
// first surface is always visible
canvas_current_ = 0;
canvas_[canvas_current_].setCurrent(false);
canvas_stored_status_ = new Group;
// User interface foreground
//
@@ -131,7 +184,7 @@ GeometryView::GeometryView() : View(GEOMETRY)
scene.fg()->attach(overlay_scaling_);
overlay_scaling_->visible_ = false;
border = new Frame(Frame::SHARP, Frame::THIN, Frame::NONE);
Frame *border = new Frame(Frame::SHARP, Frame::THIN, Frame::NONE);
border->color = glm::vec4( COLOR_HIGHLIGHT_SOURCE, 0.2f );
overlay_crop_ = border;
scene.fg()->attach(overlay_crop_);
@@ -235,24 +288,28 @@ void GeometryView::draw()
std::vector<Node *> overlays;
uint workspaces_counts_[Source::WORKSPACE_ANY+1] = {0};
uint hidden_count_ = 0;
for (auto source_iter = Mixer::manager().session()->begin();
source_iter != Mixer::manager().session()->end(); ++source_iter) {
// count if it is visible
if (Settings::application.views[mode_].ignore_mix || (*source_iter)->visible()) {
// if it is in the current workspace
if (Settings::application.current_workspace == Source::WORKSPACE_ANY
|| (*source_iter)->workspace() == Settings::application.current_workspace) {
// will draw its surface
surfaces.push_back((*source_iter)->groups_[mode_]);
// will draw its frame and locker icon
overlays.push_back((*source_iter)->frames_[mode_]);
overlays.push_back((*source_iter)->locker_);
// fills list of sources only in SOURCE EDIT MODE
if (editor_mode_ == EDIT_SOURCES) {
for (auto source_iter = Mixer::manager().session()->begin();
source_iter != Mixer::manager().session()->end(); ++source_iter) {
// count if it is visible
if (Settings::application.views[mode_].ignore_mix || (*source_iter)->visible()) {
// if it is in the current workspace
if (Settings::application.current_workspace == Source::WORKSPACE_ANY
|| (*source_iter)->workspace() == Settings::application.current_workspace) {
// will draw its surface
surfaces.push_back((*source_iter)->groups_[mode_]);
// will draw its frame and locker icon
overlays.push_back((*source_iter)->frames_[mode_]);
overlays.push_back((*source_iter)->locker_);
}
// count number of sources per workspace
workspaces_counts_[(*source_iter)->workspace()]++;
workspaces_counts_[Source::WORKSPACE_ANY]++;
}
// count number of sources per workspace
workspaces_counts_[(*source_iter)->workspace()]++;
workspaces_counts_[Source::WORKSPACE_ANY]++;
hidden_count_ += (*source_iter)->visible() ? 0 : 1;
}
hidden_count_ += (*source_iter)->visible() ? 0 : 1;
}
// 0. prepare projection for draw visitors
@@ -266,20 +323,22 @@ void GeometryView::draw()
DrawVisitor draw_rendering(output_surface_, projection, true);
scene.accept(draw_rendering);
// 3. Draw frames and icons of sources in the current workspace
DrawVisitor draw_overlays(overlays, projection);
scene.accept(draw_overlays);
if (!overlays.empty()) {
// 3. Draw frames and icons of sources in the current workspace
DrawVisitor draw_overlays(overlays, projection);
scene.accept(draw_overlays);
// 4. Draw control overlays of current source on top (if selected)
if (s != nullptr &&
(Settings::application.current_workspace == Source::WORKSPACE_ANY ||
s->workspace() == Settings::application.current_workspace) &&
(Settings::application.views[mode_].ignore_mix || s->visible()))
{
DrawVisitor dv(s->overlays_[mode_], projection);
scene.accept(dv);
// Always restore current source after draw
s->setMode(Source::CURRENT);
// 4. Draw control overlays of current source on top (if selected)
if (s != nullptr &&
(Settings::application.current_workspace == Source::WORKSPACE_ANY ||
s->workspace() == Settings::application.current_workspace) &&
(Settings::application.views[mode_].ignore_mix || s->visible()))
{
DrawVisitor dv(s->overlays_[mode_], projection);
scene.accept(dv);
// Always restore current source after draw
s->setMode(Source::CURRENT);
}
}
// 5. Finally, draw overlays of view
@@ -311,30 +370,91 @@ void GeometryView::draw()
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(0.15f, 0.15f, 0.15f, 0.99f));
ImGui::PushStyleColor(ImGuiCol_PopupBg, ImVec4(0.14f, 0.14f, 0.14f, 0.9f));
// toggle sources visibility flag
std::string _label = Settings::application.views[mode_].ignore_mix ? "Show " : "Hide ";
_label += "non visible sources\n(";
_label += std::to_string(hidden_count_) + " source" + (hidden_count_>1?"s are ":" is ") + "outside mixing circle)";
ImGuiToolkit::ButtonIconToggle(12, 0, &Settings::application.views[mode_].ignore_mix, _label.c_str());
#ifdef ENABLE_CANVAS
// SELECT EDITOR MODE
ImGui::SetNextItemWidth(ImGui::GetTextLineHeightWithSpacing() * 2.6);
if (ImGui::Button(
std::string(std::string(editor_icons[editor_mode_]) + " " + ICON_FA_SORT_DOWN)
.c_str()))
ImGui::OpenPopup("Geometry_mode_menu_popup");
if (ImGui::IsItemHovered())
ImGuiToolkit::ToolTip(editor_names[editor_mode_]);
if (ImGui::BeginPopup("Geometry_mode_menu_popup")) {
ImGuiToolkit::PushFont(ImGuiToolkit::FONT_DEFAULT);
for (int m = GeometryView::EDIT_SOURCES; m <= GeometryView::EDIT_CANVAS; ++m) {
if (ImGui::Selectable(
std::string(std::string(editor_icons[m]) + " " + editor_names[m]).c_str())) {
// change edit mode
editor_mode_ = m;
// cancel selection of sources
if (m != GeometryView::EDIT_SOURCES){
Mixer::selection().clear();
canvas_[canvas_current_].setCurrent(true);
}
else {
canvas_[canvas_current_].setCurrent(false);
}
}
}
ImGui::PopFont();
ImGui::EndPopup();
}
#endif
// SURFACES EDIT OPTIONS
if (editor_mode_ == EDIT_CANVAS) {
// add / remove surfaces
ImGui::SameLine(0, IMGUI_SAME_LINE);
if (Settings::application.num_output_surfaces > 1) {
if (ImGuiToolkit::IconButton(19, 4, "Less canvas")) {
Settings::application.num_output_surfaces--;
}
}
else
ImGuiToolkit::Icon(19, 4, false);
ImGui::SameLine(0, IMGUI_SAME_LINE);
if (Settings::application.num_output_surfaces < MAX_OUTPUT_CANVAS) {
if (ImGuiToolkit::IconButton(18, 4, "More canvas")) {
Settings::application.num_output_surfaces++;
}
} else
ImGuiToolkit::Icon(18, 4, false);
}
// SOURCES EDIT OPTIONS
else {
// toggle sources visibility flag
std::string _label = Settings::application.views[mode_].ignore_mix ? "Show " : "Hide ";
_label += "non visible sources\n(";
_label += std::to_string(hidden_count_) + " source" + (hidden_count_>1?"s are ":" is ") + "outside mixing circle)";
ImGui::SameLine(0, IMGUI_SAME_LINE);
ImGuiToolkit::ButtonIconToggle(12, 0, &Settings::application.views[mode_].ignore_mix, _label.c_str());
// select layers visibility
static std::vector<std::tuple<int, int, std::string> > _workspaces
= {{ICON_WORKSPACE_BACKGROUND, "Show only sources in\nBackground layer ("},
{ICON_WORKSPACE_CENTRAL, "Show only sources in\nWorkspace layer ("},
{ICON_WORKSPACE_FOREGROUND, "Show only sources in\nForeground layer ("},
{ICON_WORKSPACE, "Show sources in all layers ("}
};
ImGui::SameLine(0, IMGUI_SAME_LINE);
std::ostringstream oss;
oss << std::get<2>(_workspaces[Settings::application.current_workspace]);
oss << std::to_string(workspaces_counts_[Settings::application.current_workspace]);
oss << ")";
if (ImGuiToolkit::ButtonIcon(std::get<0>(
_workspaces[Settings::application.current_workspace]),
std::get<1>(
_workspaces[Settings::application.current_workspace]),
oss.str().c_str() )) {
Settings::application.current_workspace = (Settings::application.current_workspace+1)%4;
}
// select layers visibility
static std::vector<std::tuple<int, int, std::string> > _workspaces
= {{ICON_WORKSPACE_BACKGROUND, "Show only sources in\nBackground layer ("},
{ICON_WORKSPACE_CENTRAL, "Show only sources in\nWorkspace layer ("},
{ICON_WORKSPACE_FOREGROUND, "Show only sources in\nForeground layer ("},
{ICON_WORKSPACE, "Show sources in all layers ("}
};
ImGui::SameLine(0, IMGUI_SAME_LINE);
std::ostringstream oss;
oss << std::get<2>(_workspaces[Settings::application.current_workspace]);
oss << std::to_string(workspaces_counts_[Settings::application.current_workspace]);
oss << ")";
if (ImGuiToolkit::ButtonIcon(std::get<0>(
_workspaces[Settings::application.current_workspace]),
std::get<1>(
_workspaces[Settings::application.current_workspace]),
oss.str().c_str() )) {
Settings::application.current_workspace = (Settings::application.current_workspace+1)%4;
}
ImGui::PopStyleColor(6);
@@ -565,139 +685,163 @@ std::pair<Node *, glm::vec2> GeometryView::pick(glm::vec2 P)
// picking visitor found nodes?
if ( !pv.empty() ) {
// keep current source active if it is clicked
Source *current = Mixer::manager().currentSource();
if (current != nullptr) {
if ((Settings::application.current_workspace < Source::WORKSPACE_ANY &&
current->workspace() != Settings::application.current_workspace) ||
(!Settings::application.views[mode_].ignore_mix && !current->visible()) )
{
current = nullptr;
}
else {
// find if the current source was picked
auto itp = pv.rbegin();
for (; itp != pv.rend(); ++itp){
// test if source contains this node
Source::hasNode is_in_source((*itp).first );
if ( is_in_source( current ) ){
// a node in the current source was clicked !
pick = *itp;
// adapt grid to prepare grab action
adaptGridToSource(current, pick.first);
break;
}
}
// not found: the current source was not clicked
// OR the selection contains multiple sources and actions on single source are disabled
if (itp == pv.rend() || Mixer::selection().size() > 1) {
// SOURCE EDIT
if (editor_mode_ == EDIT_SOURCES) {
// keep current source active if it is clicked
Source *current = Mixer::manager().currentSource();
if (current != nullptr) {
if ((Settings::application.current_workspace < Source::WORKSPACE_ANY &&
current->workspace() != Settings::application.current_workspace) ||
(!Settings::application.views[mode_].ignore_mix && !current->visible()) )
{
current = nullptr;
pick = { nullptr, glm::vec2(0.f) };
}
// picking on the menu handle: show context menu
else if ( pick.first == current->handles_[mode_][Handles::MENU] ) {
openContextMenu(MENU_SOURCE);
}
// picking on the crop handle : switch to shape manipulation mode
else if (pick.first == current->handles_[mode_][Handles::EDIT_CROP]) {
current->manipulator_->setActive(0);
pick = { current->handles_[mode_][Handles::EDIT_SHAPE], glm::vec2(0.f) };
}
// picking on the shape handle : switch to crop manipulation mode
else if (pick.first == current->handles_[mode_][Handles::EDIT_SHAPE]) {
current->manipulator_->setActive(1);
pick = { current->handles_[mode_][Handles::EDIT_CROP], glm::vec2(0.f) };
}
// pick on the lock icon; unlock source
else if ( UserInterface::manager().ctrlModifier() && pick.first == current->lock_ ) {
lock(current, false);
pick = { current->locker_, pick.second };
// pick = { nullptr, glm::vec2(0.f) };
}
// pick on the open lock icon; lock source and cancel pick
else if ( UserInterface::manager().ctrlModifier() && pick.first == current->unlock_ ) {
lock(current, true);
pick = { nullptr, glm::vec2(0.f) };
}
// pick a locked source ; cancel pick
else if ( !UserInterface::manager().ctrlModifier() && current->locked() ) {
pick = { nullptr, glm::vec2(0.f) };
}
}
}
// the clicked source changed (not the current source)
if (current == nullptr) {
if (UserInterface::manager().ctrlModifier()) {
// default to failed pick
pick = { nullptr, glm::vec2(0.f) };
// loop over all nodes picked to detect clic on locks
for (auto itp = pv.rbegin(); itp != pv.rend(); ++itp){
// get if a source was picked
Source *s = Mixer::manager().findSource((*itp).first);
// lock icon of a source (not current) is picked : unlock
if ( s != nullptr && s->locked() && (*itp).first == s->lock_) {
lock(s, false);
pick = { s->locker_, (*itp).second };
break;
}
}
}
// no lock icon picked, find what else was picked
if ( pick.first == nullptr) {
// loop over all nodes picked
for (auto itp = pv.rbegin(); itp != pv.rend(); ++itp){
// get if a source was picked
Source *s = Mixer::manager().findSource((*itp).first);
// accept picked sources in current workspaces
if ( s!=nullptr &&
(Settings::application.current_workspace == Source::WORKSPACE_ANY ||
s->workspace() == Settings::application.current_workspace) &&
(Settings::application.views[mode_].ignore_mix || s->visible()) )
{
if ( !UserInterface::manager().ctrlModifier() ) {
// source is locked; can't move
if ( s->locked() )
continue;
// a non-locked source is picked (anywhere)
// not in an active selection? don't pick this one!
if ( Mixer::selection().size() > 1 && !Mixer::selection().contains(s) )
continue;
else {
// find if the current source was picked
auto itp = pv.rbegin();
for (; itp != pv.rend(); ++itp){
// test if source contains this node
Source::hasNode is_in_source((*itp).first );
if ( is_in_source( current ) ){
// a node in the current source was clicked !
pick = *itp;
// adapt grid to prepare grab action
adaptGridToSource(current, pick.first);
break;
}
// yeah, pick this one
pick = { s->group(mode_), (*itp).second };
break;
}
// not a source picked
else {
// picked on selection handles
if ( (*itp).first == overlay_selection_scale_ || (*itp).first == overlay_selection_rotate_ ) {
pick = (*itp);
// initiate selection manipulation
if (overlay_selection_stored_status_) {
overlay_selection_stored_status_->copyTransform(overlay_selection_);
overlay_selection_active_ = true;
// not found: the current source was not clicked
// OR the selection contains multiple sources and actions on single source are disabled
if (itp == pv.rend() || Mixer::selection().size() > 1) {
current = nullptr;
pick = { nullptr, glm::vec2(0.f) };
}
// picking on the menu handle: show context menu
else if ( pick.first == current->handles_[mode_][Handles::MENU] ) {
openContextMenu(MENU_SOURCE);
}
// picking on the crop handle : switch to shape manipulation mode
else if (pick.first == current->handles_[mode_][Handles::EDIT_CROP]) {
current->manipulator_->setActive(0);
pick = { current->handles_[mode_][Handles::EDIT_SHAPE], glm::vec2(0.f) };
}
// picking on the shape handle : switch to crop manipulation mode
else if (pick.first == current->handles_[mode_][Handles::EDIT_SHAPE]) {
current->manipulator_->setActive(1);
pick = { current->handles_[mode_][Handles::EDIT_CROP], glm::vec2(0.f) };
}
// pick on the lock icon; unlock source
else if ( UserInterface::manager().ctrlModifier() && pick.first == current->lock_ ) {
lock(current, false);
pick = { current->locker_, pick.second };
// pick = { nullptr, glm::vec2(0.f) };
}
// pick on the open lock icon; lock source and cancel pick
else if ( UserInterface::manager().ctrlModifier() && pick.first == current->unlock_ ) {
lock(current, true);
pick = { nullptr, glm::vec2(0.f) };
}
// pick a locked source ; cancel pick
else if ( !UserInterface::manager().ctrlModifier() && current->locked() ) {
pick = { nullptr, glm::vec2(0.f) };
}
}
}
// the clicked source changed (not the current source)
if (current == nullptr) {
if (UserInterface::manager().ctrlModifier()) {
// default to failed pick
pick = { nullptr, glm::vec2(0.f) };
// loop over all nodes picked to detect clic on locks
for (auto itp = pv.rbegin(); itp != pv.rend(); ++itp){
// get if a source was picked
Source *s = Mixer::manager().findSource((*itp).first);
// lock icon of a source (not current) is picked : unlock
if ( s != nullptr && s->locked() && (*itp).first == s->lock_) {
lock(s, false);
pick = { s->locker_, (*itp).second };
break;
}
}
}
// no lock icon picked, find what else was picked
if ( pick.first == nullptr) {
// loop over all nodes picked
for (auto itp = pv.rbegin(); itp != pv.rend(); ++itp){
// get if a source was picked
Source *s = Mixer::manager().findSource((*itp).first);
// accept picked sources in current workspaces
if ( s!=nullptr &&
(Settings::application.current_workspace == Source::WORKSPACE_ANY ||
s->workspace() == Settings::application.current_workspace) &&
(Settings::application.views[mode_].ignore_mix || s->visible()) )
{
if ( !UserInterface::manager().ctrlModifier() ) {
// source is locked; can't move
if ( s->locked() )
continue;
// a non-locked source is picked (anywhere)
// not in an active selection? don't pick this one!
if ( Mixer::selection().size() > 1 && !Mixer::selection().contains(s) )
continue;
}
// yeah, pick this one
pick = { s->group(mode_), (*itp).second };
break;
}
else if ( overlay_selection_icon_ != nullptr && (*itp).first == overlay_selection_icon_ ) {
pick = (*itp);
openContextMenu(MENU_SELECTION);
break;
// not a source picked
else {
// picked on selection handles
if ( (*itp).first == overlay_selection_scale_ || (*itp).first == overlay_selection_rotate_ ) {
pick = (*itp);
// initiate selection manipulation
if (overlay_selection_stored_status_) {
overlay_selection_stored_status_->copyTransform(overlay_selection_);
overlay_selection_active_ = true;
}
break;
}
else if ( overlay_selection_icon_ != nullptr && (*itp).first == overlay_selection_icon_ ) {
pick = (*itp);
openContextMenu(MENU_SELECTION);
break;
}
}
}
}
}
}
// CANVAS EDIT
else {
// picking node
pick = pv.back();
if (pick.first == canvas_[canvas_current_].menu_) {
// TODO context menu canvas
g_printerr("Canvas MENU\n");
}
// else {
// g_printerr("Pick x %f y %f\n", pick.second.x, pick.second.y);
// }
}
}
return pick;
}
bool GeometryView::canSelect(Source *s) {
bool GeometryView::canSelect(Source *s)
{
if (editor_mode_ != EDIT_SOURCES)
return false;
return ( s!=nullptr && View::canSelect(s) && s->ready() &&
(Settings::application.views[mode_].ignore_mix || s->visible()) &&
@@ -730,6 +874,71 @@ View::Cursor GeometryView::grab (Source *s, glm::vec2 from, glm::vec2 to, std::p
glm::vec3 scene_from = Rendering::manager().unProject(from, scene.root()->transform_);
glm::vec3 scene_to = Rendering::manager().unProject(to, scene.root()->transform_);
if (editor_mode_ == EDIT_CANVAS && pick.first) {
const glm::mat4 scene_to_canvas_transform = glm::inverse(canvas_stored_status_->transform_);
// const glm::mat4 canvas_to_scene_transform = scene_to_canvas_transform;
// which corner was picked ?
glm::vec2 corner = glm::round(pick.second);
// keep transform from source center to opposite corner
const glm::mat4 canvas_to_corner_transform = glm::translate(glm::identity<glm::mat4>(),
glm::vec3(corner, 0.f));
// transformation from scene to corner:
const glm::mat4 scene_to_corner_transform = canvas_to_corner_transform
* scene_to_canvas_transform;
const glm::mat4 corner_to_scene_transform = glm::inverse(scene_to_corner_transform);
// clamp coordinates of target cursor position to remain inside output surface
scene_to = glm::clamp(scene_to, glm::vec3(-output_surface_->scale_.x, -1.f, -10.f),
glm::vec3(output_surface_->scale_.x, 1.f, 10.f));
if (pick.first == canvas_[canvas_current_].handles_[Handles::RESIZE]) {
// hide other grips
canvas_[canvas_current_].menu_->visible_ = false;
canvas_[canvas_current_].handles_[Handles::RESIZE_H]->visible_ = false;
canvas_[canvas_current_].handles_[Handles::RESIZE_V]->visible_ = false;
// inform on which corner should be overlayed (opposite)
canvas_[canvas_current_].handles_[Handles::RESIZE]->overlayActiveCorner(-corner);
//
// Manipulate the handle in the SCENE coordinates to apply grid snap
//
glm::vec4 handle = corner_to_scene_transform * glm::vec4(corner * 2.f, 0.f, 1.f);
// move the corner we hold by the mouse translation (in scene reference frame)
handle = glm::translate(glm::identity<glm::mat4>(), scene_to - scene_from) * handle;
// snap handle coordinates to grid (if active)
if (grid->active())
handle = grid->snap(handle);
// Compute handleNODE_UPPER_RIGHT coordinates back in CORNER reference frame
handle = scene_to_corner_transform * handle;
// The scaling factor is computed by dividing new handle coordinates with the ones before transform
glm::vec2 corner_scaling = glm::vec2(handle) / glm::vec2(corner * 2.f);
// proportional SCALING with SHIFT
if (UserInterface::manager().shiftModifier()) {
corner_scaling = glm::vec2(glm::compMax(corner_scaling));
}
// Apply scaling to the source
canvas_[canvas_current_].root_->scale_ = canvas_stored_status_->scale_ * glm::vec3(corner_scaling, 1.f);
//
// Adjust translation
//
// The center of the source in CORNER reference frame
glm::vec4 corner_center = glm::vec4( corner, 0.f, 1.f);
// scale center of source in CORNER reference frame
corner_center = glm::scale(glm::identity<glm::mat4>(), glm::vec3(corner_scaling, 1.f)) * corner_center;
// convert center back into scene reference frame
corner_center = corner_to_scene_transform * corner_center;
// Apply scaling to the source
canvas_[canvas_current_].root_->translation_ = glm::vec3(corner_center);
}
// update cursor
return ret;
}
// No source is given
if (!s) {
@@ -886,6 +1095,7 @@ View::Cursor GeometryView::grab (Source *s, glm::vec2 from, glm::vec2 to, std::p
const glm::mat4 scene_to_corner_transform = source_to_corner_transform * scene_to_source_transform;
const glm::mat4 corner_to_scene_transform = glm::inverse(scene_to_corner_transform);
// picking on a Node
if (pick.first == s->handles_[mode_][Handles::NODE_LOWER_LEFT]) {
// hide other grips
@@ -1198,7 +1408,6 @@ View::Cursor GeometryView::grab (Source *s, glm::vec2 from, glm::vec2 to, std::p
s->handles_[mode_][Handles::MENU]->visible_ = false;
s->handles_[mode_][Handles::EDIT_CROP]->visible_ = false;
// get stored status
// glm::vec3 node_pos = glm::vec3( -s->stored_status_->data_[0].z, 0.f, 0.f);
glm::vec3 node_pos = glm::vec3( -s->stored_status_->data_[0].w, 0.f, 0.f);
// Compute target coordinates of manipulated handle into SCENE reference frame
node_pos = source_to_scene_transform * glm::vec4(node_pos, 1.f);
@@ -1208,7 +1417,6 @@ View::Cursor GeometryView::grab (Source *s, glm::vec2 from, glm::vec2 to, std::p
node_pos = scene_to_source_transform * glm::vec4(node_pos, 1.f);
// apply to source Node and to handles
// sourceNode->data_[0].z = - CLAMP( node_pos.x, -1.f, 0.f );
sourceNode->data_[0].w = - CLAMP( node_pos.x, -1.f, 0.f );
info << "Corner round " << std::fixed << std::setprecision(3) << sourceNode->data_[0].w;
}
@@ -1558,6 +1766,21 @@ View::Cursor GeometryView::grab (Source *s, glm::vec2 from, glm::vec2 to, std::p
return ret;
}
void GeometryView::initiate()
{
if (editor_mode_ == EDIT_SOURCES)
View::initiate();
else if (!current_action_ongoing_ ) {
// store status current canvas
canvas_stored_status_->copyTransform(canvas_[canvas_current_].root_);
canvas_stored_status_->update(0.f);
// initiated
current_action_ = "";
current_action_ongoing_ = true;
}
}
void GeometryView::terminate(bool force)
{
View::terminate(force);
@@ -1594,6 +1817,16 @@ void GeometryView::terminate(bool force)
overlay_selection_active_ = false;
// restore all handles canvas
for (auto &c : canvas_) {
c.menu_->visible_ = true;
c.handles_[Handles::RESIZE]->visible_ = true;
c.handles_[Handles::RESIZE]->overlayActiveCorner( glm::vec2(0.f, 0.f) );
c.handles_[Handles::RESIZE_H]->visible_ = true;
c.handles_[Handles::RESIZE_V]->visible_ = true;
}
// reset grid
adaptGridToSource();
}

View File

@@ -1,8 +1,33 @@
#ifndef GEOMETRYVIEW_H
#define GEOMETRYVIEW_H
// #define ENABLE_CANVAS
#include "View.h"
struct Canvas
{
bool current_;
Group *root_;
Switch *frames_;
Group *overlays_;
Handles *handles_[3];
Handles *menu_;
Canvas()
{
current_ = false;
root_ = nullptr;
frames_ = nullptr;
overlays_ = nullptr;
menu_ = nullptr;
handles_[0] = nullptr;
handles_[1] = nullptr;
handles_[2] = nullptr;
}
void setCurrent(bool on);
};
class GeometryView : public View
{
@@ -20,6 +45,7 @@ public:
std::pair<Node *, glm::vec2> pick(glm::vec2 P) override;
Cursor grab (Source *s, glm::vec2 from, glm::vec2 to, std::pair<Node *, glm::vec2> pick) override;
void initiate () override;
void terminate(bool force = false) override;
void arrow (glm::vec2) override;
Cursor over (glm::vec2) override;
@@ -38,6 +64,18 @@ private:
Node *overlay_scaling_grid_;
Node *overlay_crop_;
std::vector<Canvas> canvas_;
uint canvas_current_;
Group *canvas_stored_status_;
enum GeometryModes {
EDIT_SOURCES = 0,
EDIT_CANVAS = 1
};
uint editor_mode_;
static const char* editor_icons[2];
static const char* editor_names[2];
void updateSelectionOverlay(glm::vec4 color) override;
bool overlay_selection_active_;
Group *overlay_selection_stored_status_;

View File

@@ -124,6 +124,27 @@ void Settings::Save(uint64_t runtime, const std::string &filename)
pRoot->InsertEndChild(windowsNode);
}
// Surfaces
{
XMLElement *surfacesNode = xmlDoc.NewElement( "OutputSurfaces" );
surfacesNode->SetAttribute("num_output_surfaces", application.num_output_surfaces);
for (int i = 0; i < (int) application.surfaces.size(); ++i)
{
const Settings::CanvasConfig& w = application.surfaces[i];
XMLElement *surface = xmlDoc.NewElement( "Surface" );
surface->SetAttribute("id", i);
surface->SetAttribute("x", w.x);
surface->SetAttribute("y", w.y);
surface->SetAttribute("w", w.w);
surface->SetAttribute("h", w.h);
surfacesNode->InsertEndChild(surface);
}
pRoot->InsertEndChild(surfacesNode);
}
// General application preferences
XMLElement *applicationNode = xmlDoc.NewElement( "Application" );
applicationNode->SetAttribute("scale", application.scale);
@@ -639,6 +660,28 @@ void Settings::Load(const std::string &filename)
}
}
// Surfaces
{
XMLElement *pElement = pRoot->FirstChildElement("OutputSurfaces");
if (pElement) {
pElement->QueryIntAttribute("num_output_surfaces", &application.num_output_surfaces);
XMLElement *surfaceNode = pElement->FirstChildElement("Surface");
for (; surfaceNode; surfaceNode = surfaceNode->NextSiblingElement()) {
Settings::CanvasConfig w;
surfaceNode->QueryIntAttribute("x", &w.x);
surfaceNode->QueryIntAttribute("y", &w.y);
surfaceNode->QueryIntAttribute("w", &w.w);
surfaceNode->QueryIntAttribute("h", &w.h);
int i = 0;
surfaceNode->QueryIntAttribute("id", &i);
application.surfaces[i] = w;
}
}
}
// Brush
XMLElement * brushnode = pRoot->FirstChildElement("Brush");
if (brushnode != nullptr) {

View File

@@ -83,6 +83,17 @@ struct WindowConfig
};
struct CanvasConfig
{
int x, y, w, h;
CanvasConfig() : x(0), y(0), w(1), h(1)
{
}
};
struct ViewConfig
{
std::string name;
@@ -332,6 +343,10 @@ struct Application
int num_output_windows;
std::vector<WindowConfig> windows;
// multiple surfaces handling
int num_output_surfaces;
std::vector<CanvasConfig> surfaces;
// recent files histories
History recentSessions;
History recentPlaylists;
@@ -380,6 +395,8 @@ struct Application
windows = std::vector<WindowConfig>(1+MAX_OUTPUT_WINDOW);
windows[0].w = 1600;
windows[0].h = 930;
num_output_surfaces = 1;
surfaces = std::vector<CanvasConfig>(MAX_OUTPUT_CANVAS);
accept_audio = false;
dialogPosition = glm::ivec2(-1, -1);
image_sequence.framerate_mode = 15;

View File

@@ -9,6 +9,7 @@
#define MAX_RECENT_HISTORY 20
#define MAX_SESSION_LEVEL 3
#define MAX_OUTPUT_WINDOW 3
#define MAX_OUTPUT_CANVAS 8
#define VIMIX_GL_VERSION "opengl3"
#define VIMIX_GLSL_VERSION "#version 150"
@@ -76,7 +77,7 @@
#define MIXING_MIN_THRESHOLD 1.3f
#define MIXING_MAX_THRESHOLD 1.9f
#define MIXING_ICON_SCALE 0.15f, 0.15f, 1.f
#define GEOMETRY_DEFAULT_SCALE 1.4f
#define GEOMETRY_DEFAULT_SCALE 1.8f
#define GEOMETRY_MIN_SCALE 0.4f
#define GEOMETRY_MAX_SCALE 7.0f
#define LAYER_DEFAULT_SCALE 0.6f
@@ -145,6 +146,7 @@
#define COLOR_MONITOR 0.90f, 0.90f, 0.90f
#define COLOR_WINDOW 0.2f, 0.85f, 0.85f
#define COLOR_MENU_HOVERED 0.3f, 0.3f, 0.3f
#define COLOR_AUTOMATON 0.95f, 0.3f, 0.3f
#define OSC_PORT_RECV_DEFAULT 7000
#define OSC_PORT_SEND_DEFAULT 7001