Skip to content

Commit

Permalink
Merge branch 'features/module-pushing' into features/experiments
Browse files Browse the repository at this point in the history
# Conflicts:
#	README.md
  • Loading branch information
Rcomian committed Sep 3, 2018
2 parents 3504ded + aa419f5 commit 444168e
Show file tree
Hide file tree
Showing 4 changed files with 78 additions and 1 deletion.
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,11 @@ You can append a whole patch, or skip the first row of the patch you're appendin

The skip first row feature lets you build a component with audio and test trigger modules in the first row that don't get added to the combined patch when you append it.

# Experiment - module pushing

Holding shift while moving a module left or right will push any other modules it hits out of the way.
This lets you push a bunch of modules together to easily make space for another module.

# Rack

*Rack* is the engine for the VCV open-source virtual modular synthesizer.
Expand Down
2 changes: 2 additions & 0 deletions include/app.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -187,8 +187,10 @@ struct RackWidget : OpaqueWidget {
void cloneModule(ModuleWidget *m);
/** Sets a module's box if non-colliding. Returns true if set */
bool requestModuleBox(ModuleWidget *m, Rect box);
std::vector<ModuleWidget*> getCollidingWidgets(ModuleWidget *m, Rect box);
/** Moves a module to the closest non-colliding position */
bool requestModuleBoxNearest(ModuleWidget *m, Rect box);
bool requestModuleBoxWithPush(ModuleWidget *m, Rect box);

void step() override;
void draw(NVGcontext *vg) override;
Expand Down
6 changes: 5 additions & 1 deletion src/app/ModuleWidget.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -428,7 +428,11 @@ void ModuleWidget::onDragMove(EventDragMove &e) {
if (!gRackWidget->lockModules) {
Rect newBox = box;
newBox.pos = gRackWidget->lastMousePos.minus(dragPos);
gRackWidget->requestModuleBoxNearest(this, newBox);
if (windowIsShiftPressed()) {
gRackWidget->requestModuleBoxWithPush(this, newBox);
} else {
gRackWidget->requestModuleBoxNearest(this, newBox);
}
}
}

Expand Down
66 changes: 66 additions & 0 deletions src/app/RackWidget.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -503,6 +503,22 @@ bool RackWidget::requestModuleBox(ModuleWidget *m, Rect box) {
return true;
}

std::vector<ModuleWidget*> RackWidget::getCollidingWidgets(ModuleWidget *m, Rect box) {
std::vector<ModuleWidget*> result;

if (box.pos.x < 0 || box.pos.y < 0)
return result;

for (Widget *child2 : moduleContainer->children) {
if (m == child2) continue;
if (box.intersects(child2->box)) {
result.push_back(dynamic_cast<ModuleWidget*>(child2));
}
}

return result;
}

bool RackWidget::requestModuleBoxNearest(ModuleWidget *m, Rect box) {
// Create possible positions
int x0 = roundf(box.pos.x / RACK_GRID_WIDTH);
Expand All @@ -529,6 +545,56 @@ bool RackWidget::requestModuleBoxNearest(ModuleWidget *m, Rect box) {
return false;
}

bool RackWidget::requestModuleBoxWithPush(ModuleWidget *m, Rect box) {

// Find new position
int x0 = roundf(box.pos.x / RACK_GRID_WIDTH);
int y0 = roundf(box.pos.y / RACK_GRID_HEIGHT);

Rect newBox = box;
newBox.pos = Vec(x0 * RACK_GRID_WIDTH, y0 * RACK_GRID_HEIGHT);

// If moving out of bounds, fail the move
if (newBox.pos.x < 0.f || newBox.pos.y < 0.f) {
return false;
}

// If moving to another row, fail the push
if (m->box.pos.y != newBox.pos.y) {
return false;
}

bool toTheRight = m->box.pos.x < box.pos.x;
bool toTheLeft = !toTheRight;

auto colliders = getCollidingWidgets(m, newBox);
if (colliders.size() > (size_t)1) {
// Hitting too many other modules
return false;
}

for (auto colliding : colliders) {
Rect newCollidingBox = colliding->box;
if (toTheRight && colliding->box.pos.x > newBox.pos.x) {
newCollidingBox.pos.x = newBox.pos.x + newBox.size.x;
} else if (toTheLeft && colliding->box.pos.x <= newBox.pos.x) {
newCollidingBox.pos.x = newBox.pos.x - colliding->box.size.x;
} else {
// Moving module too fast - this module is trying to go the opposite way
return false;
}

if (!requestModuleBoxWithPush(colliding, newCollidingBox)) {
// We couldn't move a module out of the way - fail our move too
return false;
}
}

m->box = newBox;

return true;
}

void RackWidget::step() {
// Expand size to fit modules
Vec moduleSize = moduleContainer->getChildrenBoundingBox().getBottomRight();
Expand Down

0 comments on commit 444168e

Please sign in to comment.