Browse Source

Add back force-moving of modules.

tags/v2.1.2
Andrew Belt 2 years ago
parent
commit
f47729b40d
4 changed files with 144 additions and 77 deletions
  1. +14
    -0
      include/app/ModuleWidget.hpp
  2. +12
    -5
      include/app/RackWidget.hpp
  3. +2
    -2
      src/app/ModuleWidget.cpp
  4. +116
    -70
      src/app/RackWidget.cpp

+ 14
- 0
include/app/ModuleWidget.hpp View File

@@ -116,6 +116,20 @@ struct ModuleWidget : widget::OpaqueWidget {
void removeAction();
void createContextMenu();

// Returns the position in grid coordinates
math::Vec getGridPosition() {
return getPosition().div(RACK_GRID_SIZE).round();
}
void setGridPosition(math::Vec pos) {
setPosition(pos.mult(RACK_GRID_SIZE));
}
math::Vec getGridSize() {
return getSize().div(RACK_GRID_SIZE).round();
}
math::Rect getGridBox() {
return math::Rect(getGridPosition(), getGridSize());
}

PRIVATE math::Vec& dragOffset();
PRIVATE bool& dragEnabled();
PRIVATE engine::Module* releaseModule();


+ 12
- 5
include/app/RackWidget.hpp View File

@@ -61,16 +61,23 @@ struct RackWidget : widget::OpaqueWidget {
/** Removes the module and transfers ownership to the caller.
*/
void removeModule(ModuleWidget* mw);
ModuleWidget* getModule(int64_t moduleId);
std::vector<ModuleWidget*> getModules();
bool hasModules();

// Module position methods

/** Sets a module's box if non-colliding. Returns true if set */
bool requestModulePos(ModuleWidget* mw, math::Vec pos);
/** Moves a module to the closest non-colliding position */
void setModulePosNearest(ModuleWidget* mw, math::Vec pos);
/** Moves a module to a position, pushing other modules in the same row to the left or right, as needed. */
void setModulePosForce(ModuleWidget* mw, math::Vec pos);
PRIVATE void forceSetModulePos(ModuleWidget* mw, math::Vec pos);
PRIVATE void forceUnsetModulePos(ModuleWidget* mw);
ModuleWidget* getModule(int64_t moduleId);
std::vector<ModuleWidget*> getModules();
bool hasModules();
/** Moves a module, contracting old module positions and pushing modules to the right as needed to fit. */
void setModulePosSqueeze(ModuleWidget* mw, math::Vec pos);
PRIVATE void squeezeModulePos(ModuleWidget* mw, math::Vec pos);
PRIVATE void unsqueezeModulePos(ModuleWidget* mw);
/** Saves positions of modules for getModuleDragAction(). */
void updateModuleOldPositions();
history::ComplexAction* getModuleDragAction();



+ 2
- 2
src/app/ModuleWidget.cpp View File

@@ -831,7 +831,7 @@ void ModuleWidget::cloneAction(bool cloneCables) {
// Place module to the right of `this` module, by forcing it to 1 HP to the right.
math::Vec clonedPos = box.pos;
clonedPos.x += clonedModuleWidget->box.getWidth();
APP->scene->rack->forceSetModulePos(clonedModuleWidget, clonedPos);
APP->scene->rack->squeezeModulePos(clonedModuleWidget, clonedPos);
h->push(APP->scene->rack->getModuleDragAction());

// history::ModuleAdd
@@ -894,7 +894,7 @@ void ModuleWidget::removeAction() {

// Unset module position from rack.
APP->scene->rack->updateModuleOldPositions();
APP->scene->rack->forceUnsetModulePos(this);
APP->scene->rack->unsqueezeModulePos(this);
h->push(APP->scene->rack->getModuleDragAction());

// history::ModuleRemove


+ 116
- 70
src/app/RackWidget.cpp View File

@@ -339,7 +339,7 @@ void RackWidget::fromJson(json_t* rootJ) {
pos = pos.mult(RACK_GRID_SIZE);
}
pos = pos.plus(RACK_OFFSET);
forceSetModulePos(mw, pos);
squeezeModulePos(mw, pos);

internal->moduleContainer->addChild(mw);
}
@@ -598,7 +598,7 @@ void RackWidget::addModuleAtMouse(ModuleWidget* mw) {
assert(mw);
// Move module nearest to the mouse position
math::Vec pos = internal->mousePos.minus(mw->box.size.div(2));
setModulePosForce(mw, pos);
setModulePosSqueeze(mw, pos);
addModule(mw);
}

@@ -620,6 +620,32 @@ void RackWidget::removeModule(ModuleWidget* m) {
internal->moduleContainer->removeChild(m);
}

ModuleWidget* RackWidget::getModule(int64_t moduleId) {
for (widget::Widget* w : internal->moduleContainer->children) {
ModuleWidget* mw = dynamic_cast<ModuleWidget*>(w);
assert(mw);
if (mw->module->id == moduleId)
return mw;
}
return NULL;
}

std::vector<ModuleWidget*> RackWidget::getModules() {
std::vector<ModuleWidget*> mws;
mws.reserve(internal->moduleContainer->children.size());
for (widget::Widget* w : internal->moduleContainer->children) {
ModuleWidget* mw = dynamic_cast<ModuleWidget*>(w);
assert(mw);
mws.push_back(mw);
}
mws.shrink_to_fit();
return mws;
}

bool RackWidget::hasModules() {
return internal->moduleContainer->children.empty();
}

bool RackWidget::requestModulePos(ModuleWidget* mw, math::Vec pos) {
// Check intersection with other modules
math::Rect mwBox = math::Rect(pos, mw->box.size);
@@ -663,47 +689,98 @@ void RackWidget::setModulePosNearest(ModuleWidget* mw, math::Vec pos) {
});
}

/** Converts pixel coordinates to rack grid coordinates */
static math::Rect getGridBox(math::Rect r) {
return math::Rect(r.pos.div(RACK_GRID_SIZE).round(), r.size.div(RACK_GRID_SIZE).round());
static bool compareModuleLeft(ModuleWidget* a, ModuleWidget* b) {
return a->getGridBox().getLeft() < b->getGridBox().getLeft();
}

void RackWidget::setModulePosForce(ModuleWidget* mw, math::Vec pos) {
math::Rect mwBox;
mwBox.pos = pos.div(RACK_GRID_SIZE).round();
mwBox.size = mw->getGridSize();

// Collect modules to the left and right of new pos
std::set<ModuleWidget*, decltype(compareModuleLeft)*> leftModules(compareModuleLeft);
std::set<ModuleWidget*, decltype(compareModuleLeft)*> rightModules(compareModuleLeft);
for (widget::Widget* w2 : internal->moduleContainer->children) {
ModuleWidget* mw2 = (ModuleWidget*) w2;
// Skip this module
if (mw2 == mw)
continue;
// Modules must be on the same row as pos
if (mw2->getGridBox().getTop() != mwBox.getTop())
continue;
// Insert into leftModules or rightModules
if (mw2->getGridBox().getLeft() >= mwBox.getLeft())
rightModules.insert(mw2);
else
leftModules.insert(mw2);
}

// Set module position
mw->setGridPosition(mwBox.pos);

// Shove left modules
math::Vec cursor = mwBox.getTopLeft();
for (auto it = leftModules.rbegin(); it != leftModules.rend(); it++) {
ModuleWidget* mw2 = (ModuleWidget*) *it;
math::Rect mw2Box = mw2->getGridBox();

if (mw2Box.getRight() <= cursor.x)
break;

mw2Box.pos.x = cursor.x - mw2Box.size.x;
mw2->setGridPosition(mw2Box.pos);
cursor.x = mw2Box.getLeft();
}

// Shove right modules
cursor = mwBox.getTopRight();
for (auto it = rightModules.begin(); it != rightModules.end(); it++) {
ModuleWidget* mw2 = (ModuleWidget*) *it;
math::Rect mw2Box = mw2->getGridBox();

if (mw2Box.getLeft() >= cursor.x)
break;

mw2Box.pos.x = cursor.x;
mw2->setGridPosition(mw2Box.pos);
cursor.x = mw2Box.getRight();
}

updateExpanders();
}

void RackWidget::setModulePosSqueeze(ModuleWidget* mw, math::Vec pos) {
// Reset modules to their old positions, including this module
for (auto& pair : internal->moduleOldPositions) {
widget::Widget* w2 = pair.first;
w2->box.pos = pair.second;
}

forceUnsetModulePos(mw);
forceSetModulePos(mw, pos);
unsqueezeModulePos(mw);
squeezeModulePos(mw, pos);

updateExpanders();
}

void RackWidget::forceSetModulePos(ModuleWidget* mw, math::Vec pos) {
math::Rect mwBox = mw->box;
mwBox.pos = pos;
mwBox = getGridBox(mwBox);

// Comparator of X coordinates
auto cmp = [&](ModuleWidget* a, ModuleWidget* b) {
return getGridBox(a->box).getLeft() < getGridBox(b->box).getLeft();
};
void RackWidget::squeezeModulePos(ModuleWidget* mw, math::Vec pos) {
math::Rect mwBox;
mwBox.pos = pos.div(RACK_GRID_SIZE).round();
mwBox.size = mw->getGridSize();

// Collect modules to the left and right of new pos
std::set<ModuleWidget*, decltype(cmp)> leftModules(cmp);
std::set<ModuleWidget*, decltype(cmp)> rightModules(cmp);
std::set<ModuleWidget*, decltype(compareModuleLeft)*> leftModules(compareModuleLeft);
std::set<ModuleWidget*, decltype(compareModuleLeft)*> rightModules(compareModuleLeft);
for (widget::Widget* w2 : internal->moduleContainer->children) {
ModuleWidget* mw2 = (ModuleWidget*) w2;
// Skip this module
if (mw2 == mw)
continue;
// Modules must be on the same row as pos
if (getGridBox(mw2->box).getTop() != mwBox.getTop())
if (mw2->getGridBox().getTop() != mwBox.getTop())
continue;
// Insert into leftModules or rightModules
if (getGridBox(mw2->box).getLeft() >= mwBox.getLeft())
if (mw2->getGridBox().getLeft() >= mwBox.getLeft())
rightModules.insert(mw2);
else
leftModules.insert(mw2);
@@ -713,54 +790,49 @@ void RackWidget::forceSetModulePos(ModuleWidget* mw, math::Vec pos) {
ModuleWidget* rightModule = rightModules.empty() ? NULL : *rightModules.begin();

// If there isn't enough space between the last leftModule and first rightModule, place module to the right of the leftModule and shove right modules.
if (leftModule && rightModule && getGridBox(leftModule->box).getRight() + mwBox.getWidth() > getGridBox(rightModule->box).getLeft()) {
mwBox.pos.x = getGridBox(leftModule->box).getRight();
if (leftModule && rightModule && leftModule->getGridBox().getRight() + mwBox.getWidth() > rightModule->getGridBox().getLeft()) {
mwBox.pos.x = leftModule->getGridBox().getRight();

// Shove right modules
float xRight = mwBox.getRight();
for (auto it = rightModules.begin(); it != rightModules.end(); it++) {
widget::Widget* w2 = *it;
ModuleWidget* mw2 = (ModuleWidget*) w2;
if (getGridBox(mw2->box).getLeft() >= xRight)
if (mw2->getGridBox().getLeft() >= xRight)
break;
mw2->box.pos.x = xRight * RACK_GRID_WIDTH;
xRight = getGridBox(mw2->box).getRight();
xRight = mw2->getGridBox().getRight();
}
}
// Place right of leftModule
else if (leftModule && getGridBox(leftModule->box).getRight() > mwBox.getLeft()) {
mwBox.pos.x = getGridBox(leftModule->box).getRight();
else if (leftModule && leftModule->getGridBox().getRight() > mwBox.getLeft()) {
mwBox.pos.x = leftModule->getGridBox().getRight();
}
// Place left of rightModule
else if (rightModule && getGridBox(rightModule->box).getLeft() < mwBox.getRight()) {
mwBox.pos.x = getGridBox(rightModule->box).getLeft() - mwBox.getWidth();
else if (rightModule && rightModule->getGridBox().getLeft() < mwBox.getRight()) {
mwBox.pos.x = rightModule->getGridBox().getLeft() - mwBox.getWidth();
}

// Commit new pos
mw->box.pos = mwBox.pos * RACK_GRID_SIZE;
mw->setGridPosition(mwBox.pos);
}

void RackWidget::forceUnsetModulePos(ModuleWidget* mw) {
math::Rect mwBox = getGridBox(mw->box);

// Comparator of X coordinates
auto cmp = [&](ModuleWidget* a, ModuleWidget* b) {
return getGridBox(a->box).getLeft() < getGridBox(b->box).getLeft();
};
void RackWidget::unsqueezeModulePos(ModuleWidget* mw) {
math::Rect mwBox = mw->getGridBox();

// Collect modules to the left and right of old pos, including this module.
std::set<ModuleWidget*, decltype(cmp)> leftModules(cmp);
std::set<ModuleWidget*, decltype(cmp)> rightModules(cmp);
std::set<ModuleWidget*, decltype(compareModuleLeft)*> leftModules(compareModuleLeft);
std::set<ModuleWidget*, decltype(compareModuleLeft)*> rightModules(compareModuleLeft);
for (widget::Widget* w2 : internal->moduleContainer->children) {
ModuleWidget* mw2 = static_cast<ModuleWidget*>(w2);
// Skip this module
if (mw2 == mw)
continue;
// Modules must be on the same row as pos
if (getGridBox(mw2->box).getTop() != mwBox.getTop())
if (mw2->getGridBox().getTop() != mwBox.getTop())
continue;
// Insert into leftModules or rightModules
if (getGridBox(mw2->box).getLeft() >= mwBox.getLeft())
if (mw2->getGridBox().getLeft() >= mwBox.getLeft())
rightModules.insert(mw2);
else
leftModules.insert(mw2);
@@ -771,49 +843,23 @@ void RackWidget::forceUnsetModulePos(ModuleWidget* mw) {
ModuleWidget* rightModule = rightModules.empty() ? NULL : *rightModules.begin();

// Shove right modules back to empty space left by module.
if (leftModule && rightModule && (leftModule != mw) && (rightModule != mw) && getGridBox(leftModule->box).getRight() >= mwBox.getLeft() && getGridBox(rightModule->box).getLeft() <= mwBox.getRight()) {
if (leftModule && rightModule && (leftModule != mw) && (rightModule != mw) && leftModule->getGridBox().getRight() >= mwBox.getLeft() && rightModule->getGridBox().getLeft() <= mwBox.getRight()) {
float xLeft = mwBox.getLeft();
float xRight = mwBox.getRight();
for (auto it = rightModules.begin(); it != rightModules.end(); it++) {
widget::Widget* w2 = *it;
ModuleWidget* mw2 = static_cast<ModuleWidget*>(w2);
// Break when module is no longer touching
if (xRight < getGridBox(mw2->box).getLeft())
if (xRight < mw2->getGridBox().getLeft())
break;
// Move module to the left
xRight = getGridBox(mw2->box).getRight();
xRight = mw2->getGridBox().getRight();
mw2->box.pos.x = xLeft * RACK_GRID_WIDTH;
xLeft = getGridBox(mw2->box).getRight();
xLeft = mw2->getGridBox().getRight();
}
}
}

ModuleWidget* RackWidget::getModule(int64_t moduleId) {
for (widget::Widget* w : internal->moduleContainer->children) {
ModuleWidget* mw = dynamic_cast<ModuleWidget*>(w);
assert(mw);
if (mw->module->id == moduleId)
return mw;
}
return NULL;
}

std::vector<ModuleWidget*> RackWidget::getModules() {
std::vector<ModuleWidget*> mws;
mws.reserve(internal->moduleContainer->children.size());
for (widget::Widget* w : internal->moduleContainer->children) {
ModuleWidget* mw = dynamic_cast<ModuleWidget*>(w);
assert(mw);
mws.push_back(mw);
}
mws.shrink_to_fit();
return mws;
}

bool RackWidget::hasModules() {
return internal->moduleContainer->children.empty();
}

void RackWidget::updateModuleOldPositions() {
internal->moduleOldPositions.clear();
for (ModuleWidget* mw : getModules()) {


Loading…
Cancel
Save