From abd194b9593055774f251b11db5f5d65778ca9d1 Mon Sep 17 00:00:00 2001 From: Andrew Belt Date: Wed, 25 Oct 2017 18:20:14 -0400 Subject: [PATCH 1/8] Use cmath instead of math.h --- include/math.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/math.hpp b/include/math.hpp index 9f9f03ec..bf372dda 100644 --- a/include/math.hpp +++ b/include/math.hpp @@ -2,7 +2,7 @@ #include #include -#include +#include namespace rack { @@ -204,7 +204,7 @@ struct Vec { return x == 0.0 && y == 0.0; } bool isFinite() { - return isfinite(x) && isfinite(y); + return std::isfinite(x) && std::isfinite(y); } Vec clamp(Rect bound); }; From 923942e2892fb4ccb202d99e7893c62cd3174452 Mon Sep 17 00:00:00 2001 From: Andrew Belt Date: Wed, 25 Oct 2017 18:39:29 -0400 Subject: [PATCH 2/8] Set FrameBuffer::oversample to 1 by default --- .gitignore | 1 + include/widgets.hpp | 4 +++ src/app/RackWidget.cpp | 53 ++++++++++++++++--------------- src/gui.cpp | 2 +- src/widgets/FramebufferWidget.cpp | 8 ++--- 5 files changed, 35 insertions(+), 33 deletions(-) diff --git a/.gitignore b/.gitignore index 33ec4075..8e77f61a 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,4 @@ *.vcv /patches /design +.DS_Store diff --git a/include/widgets.hpp b/include/widgets.hpp index df978b3e..17150936 100644 --- a/include/widgets.hpp +++ b/include/widgets.hpp @@ -230,6 +230,10 @@ Events are not passed to the underlying scene. struct FramebufferWidget : virtual Widget { /** Set this to true to re-render the children to the framebuffer the next time it is drawn */ bool dirty = true; + /** A margin in pixels around the children in the framebuffer + This prevents cutting the rendered SVG off on the box edges. + */ + float oversample; /** The root object in the framebuffer scene The FramebufferWidget owns the pointer */ diff --git a/src/app/RackWidget.cpp b/src/app/RackWidget.cpp index 95a95545..4b90ac5c 100644 --- a/src/app/RackWidget.cpp +++ b/src/app/RackWidget.cpp @@ -15,14 +15,15 @@ namespace rack { RackWidget::RackWidget() { - rails = new FramebufferWidget(); - rails->box.size = Vec(); - { - RackRail *rail = new RackRail(); - rail->box.size = Vec(); - rails->addChild(rail); - } - addChild(rails); + // rails = new FramebufferWidget(); + // rails->box.size = Vec(); + // rails->oversample = 1.0; + // { + // RackRail *rail = new RackRail(); + // rail->box.size = Vec(); + // rails->addChild(rail); + // } + // addChild(rails); moduleContainer = new Widget(); addChild(moduleContainer); @@ -340,23 +341,23 @@ void RackWidget::step() { // We assume that the size is reset by a parent before calling step(). Otherwise it will grow unbounded. box.size = box.size.max(moduleSize); - // Adjust size and position of rails - Widget *rail = rails->children.front(); - Rect bound = getViewport(Rect(Vec(), box.size)); - if (!rails->box.contains(bound)) { - // Add a margin around the otherwise tight bound, so that scrolling slightly will not require a re-render of rails. - Vec margin = Vec(100, 100); - bound.pos = bound.pos.minus(margin); - bound.size = bound.size.plus(margin.mult(2)); - rails->box = bound; - // Compute offset of rail within rails framebuffer - Vec grid = Vec(RACK_GRID_WIDTH, RACK_GRID_HEIGHT); - Vec gridPos = bound.pos.div(grid).floor().mult(grid); - bound.pos = gridPos.minus(bound.pos); - bound.size = bound.size.minus(bound.pos); - rail->box = bound; - rails->dirty = true; - } + // // Adjust size and position of rails + // Widget *rail = rails->children.front(); + // Rect bound = getViewport(Rect(Vec(), box.size)); + // if (!rails->box.contains(bound)) { + // // Add a margin around the otherwise tight bound, so that scrolling slightly will not require a re-render of rails. + // Vec margin = Vec(100, 100); + // bound.pos = bound.pos.minus(margin); + // bound.size = bound.size.plus(margin.mult(2)); + // rails->box = bound; + // // Compute offset of rail within rails framebuffer + // Vec grid = Vec(RACK_GRID_WIDTH, RACK_GRID_HEIGHT); + // Vec gridPos = bound.pos.div(grid).floor().mult(grid); + // bound.pos = gridPos.minus(bound.pos); + // bound.size = bound.size.minus(bound.pos); + // rail->box = bound; + // rails->dirty = true; + // } // Autosave every 15 seconds if (gGuiFrame % (60*15) == 0) { @@ -525,7 +526,7 @@ void RackWidget::onMouseDownOpaque(int button) { } void RackWidget::onZoom() { - rails->box.size = Vec(); + // rails->box.size = Vec(); Widget::onZoom(); } diff --git a/src/gui.cpp b/src/gui.cpp index e8c260c5..a9be0f76 100644 --- a/src/gui.cpp +++ b/src/gui.cpp @@ -358,7 +358,7 @@ void guiRun() { std::this_thread::sleep_for(std::chrono::duration(minTime - frameTime)); } endTime = glfwGetTime(); - // printf("%lf fps\n", 1.0 / (endTime - startTime)); + printf("%lf fps\n", 1.0 / (endTime - startTime)); } } diff --git a/src/widgets/FramebufferWidget.cpp b/src/widgets/FramebufferWidget.cpp index 9792635a..703fb65e 100644 --- a/src/widgets/FramebufferWidget.cpp +++ b/src/widgets/FramebufferWidget.cpp @@ -9,12 +9,6 @@ namespace rack { -/** A margin in pixels around the children in the framebuffer -This prevents cutting the rendered SVG off on the box edges. -*/ -static const float oversample = 2.0; - - struct FramebufferWidget::Internal { NVGLUframebuffer *fb = NULL; Rect box; @@ -32,6 +26,7 @@ struct FramebufferWidget::Internal { FramebufferWidget::FramebufferWidget() { internal = new Internal(); + oversample = 1.0; } FramebufferWidget::~FramebufferWidget() { @@ -65,6 +60,7 @@ void FramebufferWidget::draw(NVGcontext *vg) { if (fbSize.x <= 0.0 || fbSize.y <= 0.0) return; + printf("rendering framebuffer %f %f\n", fbSize.x, fbSize.y); // Delete old one first to free up GPU memory internal->setFramebuffer(NULL); // Create a framebuffer from the main nanovg context. We will draw to this in the secondary nanovg context. From 99ad658694691c60bf39af8d3ee790911c51d3e7 Mon Sep 17 00:00:00 2001 From: Andrew Belt Date: Wed, 25 Oct 2017 19:05:11 -0400 Subject: [PATCH 3/8] Oversample SVGPanels by 2, everything else by 1 --- include/app.hpp | 1 + src/app/RackWidget.cpp | 54 +++++++++++++++---------------- src/app/SVGPanel.cpp | 4 +++ src/app/Toolbar.cpp | 2 +- src/gui.cpp | 2 +- src/widgets/FramebufferWidget.cpp | 3 +- src/widgets/SVGWidget.cpp | 1 + 7 files changed, 36 insertions(+), 31 deletions(-) diff --git a/include/app.hpp b/include/app.hpp index ab9b6908..cab745ed 100644 --- a/include/app.hpp +++ b/include/app.hpp @@ -155,6 +155,7 @@ struct Panel : TransparentWidget { }; struct SVGPanel : FramebufferWidget { + SVGPanel(); void setBackground(std::shared_ptr svg); }; diff --git a/src/app/RackWidget.cpp b/src/app/RackWidget.cpp index 4b90ac5c..3177567c 100644 --- a/src/app/RackWidget.cpp +++ b/src/app/RackWidget.cpp @@ -15,15 +15,15 @@ namespace rack { RackWidget::RackWidget() { - // rails = new FramebufferWidget(); - // rails->box.size = Vec(); - // rails->oversample = 1.0; - // { - // RackRail *rail = new RackRail(); - // rail->box.size = Vec(); - // rails->addChild(rail); - // } - // addChild(rails); + rails = new FramebufferWidget(); + rails->box.size = Vec(); + rails->oversample = 1.0; + { + RackRail *rail = new RackRail(); + rail->box.size = Vec(); + rails->addChild(rail); + } + addChild(rails); moduleContainer = new Widget(); addChild(moduleContainer); @@ -341,23 +341,23 @@ void RackWidget::step() { // We assume that the size is reset by a parent before calling step(). Otherwise it will grow unbounded. box.size = box.size.max(moduleSize); - // // Adjust size and position of rails - // Widget *rail = rails->children.front(); - // Rect bound = getViewport(Rect(Vec(), box.size)); - // if (!rails->box.contains(bound)) { - // // Add a margin around the otherwise tight bound, so that scrolling slightly will not require a re-render of rails. - // Vec margin = Vec(100, 100); - // bound.pos = bound.pos.minus(margin); - // bound.size = bound.size.plus(margin.mult(2)); - // rails->box = bound; - // // Compute offset of rail within rails framebuffer - // Vec grid = Vec(RACK_GRID_WIDTH, RACK_GRID_HEIGHT); - // Vec gridPos = bound.pos.div(grid).floor().mult(grid); - // bound.pos = gridPos.minus(bound.pos); - // bound.size = bound.size.minus(bound.pos); - // rail->box = bound; - // rails->dirty = true; - // } + // Adjust size and position of rails + Widget *rail = rails->children.front(); + Rect bound = getViewport(Rect(Vec(), box.size)); + if (!rails->box.contains(bound)) { + // Add a margin around the otherwise tight bound, so that scrolling slightly will not require a re-render of rails. + Vec margin = Vec(100, 100); + bound.pos = bound.pos.minus(margin); + bound.size = bound.size.plus(margin.mult(2)); + rails->box = bound; + // Compute offset of rail within rails framebuffer + Vec grid = Vec(RACK_GRID_WIDTH, RACK_GRID_HEIGHT); + Vec gridPos = bound.pos.div(grid).floor().mult(grid); + bound.pos = gridPos.minus(bound.pos); + bound.size = bound.size.minus(bound.pos); + rail->box = bound; + rails->dirty = true; + } // Autosave every 15 seconds if (gGuiFrame % (60*15) == 0) { @@ -526,7 +526,7 @@ void RackWidget::onMouseDownOpaque(int button) { } void RackWidget::onZoom() { - // rails->box.size = Vec(); + rails->box.size = Vec(); Widget::onZoom(); } diff --git a/src/app/SVGPanel.cpp b/src/app/SVGPanel.cpp index 815a89fb..8d547db6 100644 --- a/src/app/SVGPanel.cpp +++ b/src/app/SVGPanel.cpp @@ -16,6 +16,10 @@ struct PanelBorder : TransparentWidget { }; +SVGPanel::SVGPanel() { + oversample = 2.0; +} + void SVGPanel::setBackground(std::shared_ptr svg) { clearChildren(); diff --git a/src/app/Toolbar.cpp b/src/app/Toolbar.cpp index 22df1f26..df526bc3 100644 --- a/src/app/Toolbar.cpp +++ b/src/app/Toolbar.cpp @@ -153,7 +153,7 @@ Toolbar::Toolbar() { zoomSlider->box.size.x = 150; zoomSlider->label = "Zoom"; zoomSlider->unit = "%"; - zoomSlider->setLimits(33.33, 200.0); + zoomSlider->setLimits(50.0, 200.0); zoomSlider->setDefaultValue(100.0); addChild(zoomSlider); xPos += zoomSlider->box.size.x; diff --git a/src/gui.cpp b/src/gui.cpp index a9be0f76..e8c260c5 100644 --- a/src/gui.cpp +++ b/src/gui.cpp @@ -358,7 +358,7 @@ void guiRun() { std::this_thread::sleep_for(std::chrono::duration(minTime - frameTime)); } endTime = glfwGetTime(); - printf("%lf fps\n", 1.0 / (endTime - startTime)); + // printf("%lf fps\n", 1.0 / (endTime - startTime)); } } diff --git a/src/widgets/FramebufferWidget.cpp b/src/widgets/FramebufferWidget.cpp index 703fb65e..c97713c5 100644 --- a/src/widgets/FramebufferWidget.cpp +++ b/src/widgets/FramebufferWidget.cpp @@ -25,8 +25,8 @@ struct FramebufferWidget::Internal { FramebufferWidget::FramebufferWidget() { - internal = new Internal(); oversample = 1.0; + internal = new Internal(); } FramebufferWidget::~FramebufferWidget() { @@ -60,7 +60,6 @@ void FramebufferWidget::draw(NVGcontext *vg) { if (fbSize.x <= 0.0 || fbSize.y <= 0.0) return; - printf("rendering framebuffer %f %f\n", fbSize.x, fbSize.y); // Delete old one first to free up GPU memory internal->setFramebuffer(NULL); // Create a framebuffer from the main nanovg context. We will draw to this in the secondary nanovg context. diff --git a/src/widgets/SVGWidget.cpp b/src/widgets/SVGWidget.cpp index 800eb635..524fc309 100644 --- a/src/widgets/SVGWidget.cpp +++ b/src/widgets/SVGWidget.cpp @@ -201,6 +201,7 @@ void SVGWidget::wrap() { void SVGWidget::draw(NVGcontext *vg) { if (svg && svg->handle) { + // printf("drawing svg %f %f\n", box.size.x, box.size.y); drawSVG(vg, svg->handle); } } From 580f0db6b45b518096fdeb196134facca0e00000 Mon Sep 17 00:00:00 2001 From: Andrew Belt Date: Wed, 25 Oct 2017 20:11:41 -0400 Subject: [PATCH 4/8] Change zoom only when zoom slider has finished moving (onAction()) --- include/app.hpp | 1 + include/math.hpp | 4 ++++ src/app.cpp | 4 +++- src/app/RackScene.cpp | 3 --- src/app/SVGPanel.cpp | 4 +++- src/app/Toolbar.cpp | 10 ++++++++-- src/settings.cpp | 8 +++++--- src/widgets/FramebufferWidget.cpp | 1 + src/widgets/Slider.cpp | 2 ++ 9 files changed, 27 insertions(+), 10 deletions(-) diff --git a/include/app.hpp b/include/app.hpp index cab745ed..ff19a64b 100644 --- a/include/app.hpp +++ b/include/app.hpp @@ -355,6 +355,7 @@ extern std::string gApplicationVersion; extern std::string gApiHost; // Easy access to "singleton" widgets +extern RackScene *gRackScene; extern RackWidget *gRackWidget; extern Toolbar *gToolbar; diff --git a/include/math.hpp b/include/math.hpp index bf372dda..bd89f646 100644 --- a/include/math.hpp +++ b/include/math.hpp @@ -61,6 +61,10 @@ inline float eucmodf(float a, float base) { return mod < 0.0 ? mod + base : mod; } +inline float nearf(float a, float b, float epsilon = 1e-6) { + return fabsf(a - b) <= epsilon; +} + /** Limits a value between a minimum and maximum If min > max, the limits are switched */ diff --git a/src/app.cpp b/src/app.cpp index ff97af8a..8b8bade6 100644 --- a/src/app.cpp +++ b/src/app.cpp @@ -14,10 +14,12 @@ std::string gApiHost = "http://api.vcvrack.com"; RackWidget *gRackWidget = NULL; Toolbar *gToolbar = NULL; +RackScene *gRackScene = NULL; void sceneInit() { - gScene = new RackScene(); + gRackScene = new RackScene(); + gScene = gRackScene; } void sceneDestroy() { diff --git a/src/app/RackScene.cpp b/src/app/RackScene.cpp index 51a065a2..e55ab507 100644 --- a/src/app/RackScene.cpp +++ b/src/app/RackScene.cpp @@ -62,9 +62,6 @@ void RackScene::step() { .plus(Vec(500, 500)) .div(zoomWidget->zoom); - // Set zoom from the toolbar's zoom slider - zoomWidget->setZoom(gToolbar->zoomSlider->value / 100.0); - Scene::step(); zoomWidget->box.size = gRackWidget->box.size.mult(zoomWidget->zoom); diff --git a/src/app/SVGPanel.cpp b/src/app/SVGPanel.cpp index 8d547db6..7c0201bc 100644 --- a/src/app/SVGPanel.cpp +++ b/src/app/SVGPanel.cpp @@ -1,4 +1,5 @@ #include "app.hpp" +#include "gui.hpp" namespace rack { @@ -17,7 +18,8 @@ struct PanelBorder : TransparentWidget { SVGPanel::SVGPanel() { - oversample = 2.0; + if (nearf(gPixelRatio, 1.0)) + oversample = 2.0; } void SVGPanel::setBackground(std::shared_ptr svg) { diff --git a/src/app/Toolbar.cpp b/src/app/Toolbar.cpp index df526bc3..f890485a 100644 --- a/src/app/Toolbar.cpp +++ b/src/app/Toolbar.cpp @@ -148,12 +148,18 @@ Toolbar::Toolbar() { xPos += margin; { - zoomSlider = new Slider(); + struct ZoomSlider : Slider { + void onAction() override { + Slider::onAction(); + gRackScene->zoomWidget->setZoom(value / 100.0); + } + }; + zoomSlider = new ZoomSlider(); zoomSlider->box.pos = Vec(xPos, margin); zoomSlider->box.size.x = 150; zoomSlider->label = "Zoom"; zoomSlider->unit = "%"; - zoomSlider->setLimits(50.0, 200.0); + zoomSlider->setLimits(25.0, 200.0); zoomSlider->setDefaultValue(100.0); addChild(zoomSlider); xPos += zoomSlider->box.size.x; diff --git a/src/settings.cpp b/src/settings.cpp index d90faa0d..95ea7ed9 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -28,7 +28,7 @@ static json_t *settingsToJson() { json_object_set_new(rootJ, "wireTension", tensionJ); // zoom - float zoom = gToolbar->zoomSlider->value; + float zoom = gRackScene->zoomWidget->zoom; json_t *zoomJ = json_real(zoom); json_object_set_new(rootJ, "zoom", zoomJ); @@ -69,8 +69,10 @@ static void settingsFromJson(json_t *rootJ) { // zoom json_t *zoomJ = json_object_get(rootJ, "zoom"); - if (zoomJ) - gToolbar->zoomSlider->value = json_number_value(zoomJ); + if (zoomJ) { + gRackScene->zoomWidget->setZoom(clampf(json_number_value(zoomJ), 0.25, 4.0)); + gToolbar->zoomSlider->setValue(json_number_value(zoomJ) * 100.0); + } // allowCursorLock json_t *allowCursorLockJ = json_object_get(rootJ, "allowCursorLock"); diff --git a/src/widgets/FramebufferWidget.cpp b/src/widgets/FramebufferWidget.cpp index c97713c5..d3e85016 100644 --- a/src/widgets/FramebufferWidget.cpp +++ b/src/widgets/FramebufferWidget.cpp @@ -60,6 +60,7 @@ void FramebufferWidget::draw(NVGcontext *vg) { if (fbSize.x <= 0.0 || fbSize.y <= 0.0) return; + // printf("rendering framebuffer %f %f\n", fbSize.x, fbSize.y); // Delete old one first to free up GPU memory internal->setFramebuffer(NULL); // Create a framebuffer from the main nanovg context. We will draw to this in the secondary nanovg context. diff --git a/src/widgets/Slider.cpp b/src/widgets/Slider.cpp index ae82fd24..a0e2c3aa 100644 --- a/src/widgets/Slider.cpp +++ b/src/widgets/Slider.cpp @@ -23,11 +23,13 @@ void Slider::onDragMove(Vec mouseRel) { void Slider::onDragEnd() { state = BND_DEFAULT; guiCursorUnlock(); + onAction(); } void Slider::onMouseDownOpaque(int button) { if (button == 1) { setValue(defaultValue); + onAction(); } } From caa1a5798ebd6922b3b27a678cb571d02b2555ec Mon Sep 17 00:00:00 2001 From: Andrew Belt Date: Wed, 25 Oct 2017 21:57:46 -0400 Subject: [PATCH 5/8] Fix dropdown menu offset when zooming --- include/math.hpp | 15 +++++++++++++++ include/widgets.hpp | 8 +++++++- src/app/RackScrollWidget.cpp | 2 +- src/app/Toolbar.cpp | 4 ++-- src/app/WireWidget.cpp | 12 ++++-------- src/core/AudioInterface.cpp | 6 +++--- src/core/MidiClockToCV.cpp | 2 +- src/core/MidiIO.cpp | 4 ++-- src/core/QuadMidiToCV.cpp | 2 +- src/widgets/Tooltip.cpp | 2 +- src/widgets/Widget.cpp | 34 +++++++++++++++++----------------- src/widgets/ZoomWidget.cpp | 5 +++++ 12 files changed, 59 insertions(+), 37 deletions(-) diff --git a/include/math.hpp b/include/math.hpp index bd89f646..73bee8e5 100644 --- a/include/math.hpp +++ b/include/math.hpp @@ -271,6 +271,21 @@ struct Rect { r.pos.y = clampf(pos.y, bound.pos.y, bound.pos.y + bound.size.y - size.y); return r; } + /** Expands this Rect to contain `other` */ + Rect expand(Rect other) { + Rect r; + r.pos.x = fminf(pos.x, other.pos.x); + r.pos.y = fminf(pos.y, other.pos.y); + r.size.x = fmaxf(pos.x + size.x, other.pos.x + other.size.x) - r.pos.x; + r.size.y = fmaxf(pos.y + size.y, other.pos.y + other.size.y) - r.pos.y; + return r; + } + /** Returns a Rect with its position set to zero */ + Rect zeroPos() { + Rect r; + r.size = size; + return r; + } }; diff --git a/include/widgets.hpp b/include/widgets.hpp index 17150936..a3fe6d92 100644 --- a/include/widgets.hpp +++ b/include/widgets.hpp @@ -56,8 +56,13 @@ struct Widget { virtual ~Widget(); - Vec getAbsolutePos(); Rect getChildrenBoundingBox(); + /** Returns `v` transformed into the coordinate system of `relative` */ + virtual Vec getRelativeOffset(Vec v, Widget *relative); + /** Returns `v` transformed into world coordinates */ + Vec getAbsoluteOffset(Vec v) { + return getRelativeOffset(v, NULL); + } /** Returns a subset of the given Rect bounded by the box of this widget and all ancestors */ virtual Rect getViewport(Rect r); @@ -149,6 +154,7 @@ struct TransformWidget : Widget { struct ZoomWidget : Widget { float zoom = 1.0; + Vec getRelativeOffset(Vec v, Widget *relative) override; Rect getViewport(Rect r) override; void setZoom(float zoom); void draw(NVGcontext *vg) override; diff --git a/src/app/RackScrollWidget.cpp b/src/app/RackScrollWidget.cpp index b2d4357f..d3a9dc36 100644 --- a/src/app/RackScrollWidget.cpp +++ b/src/app/RackScrollWidget.cpp @@ -6,7 +6,7 @@ namespace rack { void RackScrollWidget::step() { - Vec pos = gMousePos.minus(getAbsolutePos()); + Vec pos = gRackWidget->lastMousePos; // Scroll rack if dragging cable near the edge of the screen if (gRackWidget->wireContainer->activeWire) { float margin = 20.0; diff --git a/src/app/Toolbar.cpp b/src/app/Toolbar.cpp index f890485a..5a04460a 100644 --- a/src/app/Toolbar.cpp +++ b/src/app/Toolbar.cpp @@ -39,7 +39,7 @@ struct QuitItem : MenuItem { struct FileChoice : ChoiceButton { void onAction() override { Menu *menu = gScene->createMenu(); - menu->box.pos = getAbsolutePos().plus(Vec(0, box.size.y)); + menu->box.pos = getAbsoluteOffset(Vec(0, box.size.y)); menu->box.size.x = box.size.x; { @@ -70,7 +70,7 @@ struct SampleRateItem : MenuItem { struct SampleRateChoice : ChoiceButton { void onAction() override { Menu *menu = gScene->createMenu(); - menu->box.pos = getAbsolutePos().plus(Vec(0, box.size.y)); + menu->box.pos = getAbsoluteOffset(Vec(0, box.size.y)); menu->box.size.x = box.size.x; PauseItem *pauseItem = new PauseItem(); diff --git a/src/app/WireWidget.cpp b/src/app/WireWidget.cpp index 90ef6636..be314394 100644 --- a/src/app/WireWidget.cpp +++ b/src/app/WireWidget.cpp @@ -124,31 +124,27 @@ void WireWidget::updateWire() { } Vec WireWidget::getOutputPos() { - Vec pos; if (outputPort) { - pos = Rect(outputPort->getAbsolutePos(), outputPort->box.size).getCenter(); + return outputPort->getRelativeOffset(outputPort->box.zeroPos().getCenter(), gRackWidget); } else if (hoveredOutputPort) { - pos = Rect(hoveredOutputPort->getAbsolutePos(), hoveredOutputPort->box.size).getCenter(); + return hoveredOutputPort->getRelativeOffset(hoveredOutputPort->box.zeroPos().getCenter(), gRackWidget); } else { return gRackWidget->lastMousePos; } - return pos.minus(getAbsolutePos().minus(box.pos)); } Vec WireWidget::getInputPos() { - Vec pos; if (inputPort) { - pos = Rect(inputPort->getAbsolutePos(), inputPort->box.size).getCenter(); + return inputPort->getRelativeOffset(inputPort->box.zeroPos().getCenter(), gRackWidget); } else if (hoveredInputPort) { - pos = Rect(hoveredInputPort->getAbsolutePos(), hoveredInputPort->box.size).getCenter(); + return hoveredInputPort->getRelativeOffset(hoveredInputPort->box.zeroPos().getCenter(), gRackWidget); } else { return gRackWidget->lastMousePos; } - return pos.minus(getAbsolutePos().minus(box.pos)); } void WireWidget::draw(NVGcontext *vg) { diff --git a/src/core/AudioInterface.cpp b/src/core/AudioInterface.cpp index 69a28caa..0da52dc0 100644 --- a/src/core/AudioInterface.cpp +++ b/src/core/AudioInterface.cpp @@ -335,7 +335,7 @@ struct AudioChoice : ChoiceButton { AudioInterface *audioInterface; void onAction() override { Menu *menu = gScene->createMenu(); - menu->box.pos = getAbsolutePos().plus(Vec(0, box.size.y)); + menu->box.pos = getAbsoluteOffset(Vec(0, box.size.y)).round(); menu->box.size.x = box.size.x; int deviceCount = audioInterface->getDeviceCount(); @@ -373,7 +373,7 @@ struct SampleRateChoice : ChoiceButton { AudioInterface *audioInterface; void onAction() override { Menu *menu = gScene->createMenu(); - menu->box.pos = getAbsolutePos().plus(Vec(0, box.size.y)); + menu->box.pos = getAbsoluteOffset(Vec(0, box.size.y)).round(); menu->box.size.x = box.size.x; const float sampleRates[6] = {44100, 48000, 88200, 96000, 176400, 192000}; @@ -404,7 +404,7 @@ struct BlockSizeChoice : ChoiceButton { AudioInterface *audioInterface; void onAction() override { Menu *menu = gScene->createMenu(); - menu->box.pos = getAbsolutePos().plus(Vec(0, box.size.y)); + menu->box.pos = getAbsoluteOffset(Vec(0, box.size.y)).round(); menu->box.size.x = box.size.x; const int blockSizes[] = {64, 128, 256, 512, 1024, 2048, 4096}; diff --git a/src/core/MidiClockToCV.cpp b/src/core/MidiClockToCV.cpp index d37ef9ff..ac7c9626 100644 --- a/src/core/MidiClockToCV.cpp +++ b/src/core/MidiClockToCV.cpp @@ -201,7 +201,7 @@ struct ClockRatioChoice : ChoiceButton { void onAction() { Menu *menu = gScene->createMenu(); - menu->box.pos = getAbsolutePos().plus(Vec(0, box.size.y)); + menu->box.pos = getAbsoluteOffset(Vec(0, box.size.y)).round(); menu->box.size.x = box.size.x; for (unsigned long ratio = 0; ratio < ratioNames.size(); ratio++) { diff --git a/src/core/MidiIO.cpp b/src/core/MidiIO.cpp index 346b93e0..ed87bbcf 100644 --- a/src/core/MidiIO.cpp +++ b/src/core/MidiIO.cpp @@ -182,7 +182,7 @@ void MidiItem::onAction() { void MidiChoice::onAction() { Menu *menu = gScene->createMenu(); - menu->box.pos = getAbsolutePos().plus(Vec(0, box.size.y)); + menu->box.pos = getAbsoluteOffset(Vec(0, box.size.y)).round(); menu->box.size.x = box.size.x; { @@ -217,7 +217,7 @@ void ChannelItem::onAction() { void ChannelChoice::onAction() { Menu *menu = gScene->createMenu(); - menu->box.pos = getAbsolutePos().plus(Vec(0, box.size.y)); + menu->box.pos = getAbsoluteOffset(Vec(0, box.size.y)).round(); menu->box.size.x = box.size.x; { diff --git a/src/core/QuadMidiToCV.cpp b/src/core/QuadMidiToCV.cpp index 5f27f7b3..60e43a07 100644 --- a/src/core/QuadMidiToCV.cpp +++ b/src/core/QuadMidiToCV.cpp @@ -259,7 +259,7 @@ struct ModeChoice : ChoiceButton { void onAction() { Menu *menu = gScene->createMenu(); - menu->box.pos = getAbsolutePos().plus(Vec(0, box.size.y)); + menu->box.pos = getAbsoluteOffset(Vec(0, box.size.y)).round(); menu->box.size.x = box.size.x; for (unsigned long i = 0; i < modeNames.size(); i++) { diff --git a/src/widgets/Tooltip.cpp b/src/widgets/Tooltip.cpp index bc177264..d6184fab 100644 --- a/src/widgets/Tooltip.cpp +++ b/src/widgets/Tooltip.cpp @@ -6,7 +6,7 @@ namespace rack { void Tooltip::step() { // Follow the mouse - box.pos = gMousePos.minus(parent->getAbsolutePos()); + box.pos = gMousePos; // Wrap size to contents // box.size = getChildrenBoundingBox().getBottomRight(); diff --git a/src/widgets/Widget.cpp b/src/widgets/Widget.cpp index 5fbab712..65af2567 100644 --- a/src/widgets/Widget.cpp +++ b/src/widgets/Widget.cpp @@ -16,28 +16,28 @@ Widget::~Widget() { clearChildren(); } -Vec Widget::getAbsolutePos() { - // Recursively compute position offset from parents - if (!parent) { - return box.pos; - } - else { - return box.pos.plus(parent->getAbsolutePos()); +Rect Widget::getChildrenBoundingBox() { + Rect bound; + for (Widget *child : children) { + if (child == children.front()) { + bound = child->box; + } + else { + bound = bound.expand(child->box); + } } + return bound; } -Rect Widget::getChildrenBoundingBox() { - if (children.empty()) { - return Rect(); +Vec Widget::getRelativeOffset(Vec v, Widget *relative) { + if (this == relative) { + return v; } - - Vec topLeft = Vec(INFINITY, INFINITY); - Vec bottomRight = Vec(-INFINITY, -INFINITY); - for (Widget *child : children) { - topLeft = topLeft.min(child->box.pos); - bottomRight = bottomRight.max(child->box.getBottomRight()); + v = v.plus(box.pos); + if (parent) { + v = parent->getRelativeOffset(v, relative); } - return Rect(topLeft, bottomRight.minus(topLeft)); + return v; } Rect Widget::getViewport(Rect r) { diff --git a/src/widgets/ZoomWidget.cpp b/src/widgets/ZoomWidget.cpp index d65ab035..a0bb4b0c 100644 --- a/src/widgets/ZoomWidget.cpp +++ b/src/widgets/ZoomWidget.cpp @@ -3,6 +3,11 @@ namespace rack { + +Vec ZoomWidget::getRelativeOffset(Vec v, Widget *relative) { + return Widget::getRelativeOffset(v.mult(zoom), relative); +} + Rect ZoomWidget::getViewport(Rect r) { r.pos = r.pos.mult(zoom); r.size = r.size.mult(zoom); From a58ab537076ad56ff2596b90e395d8230cc9eed3 Mon Sep 17 00:00:00 2001 From: Andrew Belt Date: Wed, 25 Oct 2017 22:13:59 -0400 Subject: [PATCH 6/8] Add Light::setBrightnessSmooth() --- include/engine.hpp | 3 ++- src/engine.cpp | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/include/engine.hpp b/include/engine.hpp index ce7d43d0..cfff9b1d 100644 --- a/include/engine.hpp +++ b/include/engine.hpp @@ -30,8 +30,9 @@ struct Output { }; struct Light { + /** The square of the brightness value */ float value = 0.0; - void setSmooth(float v); + void setBrightnessSmooth(float brightness); }; diff --git a/src/engine.cpp b/src/engine.cpp index f34ca99e..ef0163dd 100644 --- a/src/engine.cpp +++ b/src/engine.cpp @@ -33,8 +33,8 @@ static int smoothParamId; static float smoothValue; -void Light::setSmooth(float v) { - value += (v - value) / sampleRate * 60.0; +void Light::setBrightnessSmooth(float brightness) { + value += (powf(brightness, 2) - value) / sampleRate * 60.0; } From 41c66f1d8a392d8ea11d3b2e5790d4e58b062299 Mon Sep 17 00:00:00 2001 From: Andrew Belt Date: Wed, 25 Oct 2017 22:22:04 -0400 Subject: [PATCH 7/8] Set SVGPanel oversampling on every frame --- include/app.hpp | 2 +- src/app/SVGPanel.cpp | 6 ++++-- src/gui.cpp | 10 +++++++--- 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/include/app.hpp b/include/app.hpp index ff19a64b..8a53f1b6 100644 --- a/include/app.hpp +++ b/include/app.hpp @@ -155,7 +155,7 @@ struct Panel : TransparentWidget { }; struct SVGPanel : FramebufferWidget { - SVGPanel(); + void step() override; void setBackground(std::shared_ptr svg); }; diff --git a/src/app/SVGPanel.cpp b/src/app/SVGPanel.cpp index 7c0201bc..178d7d65 100644 --- a/src/app/SVGPanel.cpp +++ b/src/app/SVGPanel.cpp @@ -17,9 +17,11 @@ struct PanelBorder : TransparentWidget { }; -SVGPanel::SVGPanel() { - if (nearf(gPixelRatio, 1.0)) +void SVGPanel::step() { + if (nearf(gPixelRatio, 1.0)) { oversample = 2.0; + } + FramebufferWidget::step(); } void SVGPanel::setBackground(std::shared_ptr svg) { diff --git a/src/gui.cpp b/src/gui.cpp index e8c260c5..d53f2699 100644 --- a/src/gui.cpp +++ b/src/gui.cpp @@ -218,9 +218,6 @@ void errorCallback(int error, const char *description) { void renderGui() { int width, height; glfwGetFramebufferSize(gWindow, &width, &height); - int windowWidth, windowHeight; - glfwGetWindowSize(gWindow, &windowWidth, &windowHeight); - gPixelRatio = (float)width / windowWidth; // Update and render nvgBeginFrame(gVg, width, height, gPixelRatio); @@ -341,6 +338,13 @@ void guiRun() { } glfwSetWindowTitle(gWindow, title.c_str()); + // Get framebuffer size + int width, height; + glfwGetFramebufferSize(gWindow, &width, &height); + int windowWidth, windowHeight; + glfwGetWindowSize(gWindow, &windowWidth, &windowHeight); + gPixelRatio = (float)width / windowWidth; + // Step scene gScene->step(); From 343a5214634920d296cc9d8734cc4febc8d9885c Mon Sep 17 00:00:00 2001 From: Andrew Belt Date: Thu, 26 Oct 2017 04:23:34 -0400 Subject: [PATCH 8/8] Slightly more direct message in gui.cpp --- src/gui.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/gui.cpp b/src/gui.cpp index d53f2699..d6e82fb9 100644 --- a/src/gui.cpp +++ b/src/gui.cpp @@ -254,7 +254,7 @@ void guiInit() { glfwWindowHint(GLFW_DOUBLEBUFFER, GLFW_TRUE); gWindow = glfwCreateWindow(640, 480, "", NULL, NULL); if (!gWindow) { - osdialog_message(OSDIALOG_ERROR, OSDIALOG_OK, "Cannot open window with OpenGL 2.0 renderer. Does your graphics card support OpenGL 2.0 or greater? If so, are the latest drivers installed?"); + osdialog_message(OSDIALOG_ERROR, OSDIALOG_OK, "Cannot open window with OpenGL 2.0 renderer. Does your graphics card support OpenGL 2.0 or greater? If so, make sure you have the latest graphics drivers installed."); exit(1); } @@ -274,7 +274,7 @@ void guiInit() { glewExperimental = GL_TRUE; err = glewInit(); if (err != GLEW_OK) { - osdialog_message(OSDIALOG_ERROR, OSDIALOG_OK, "Could not initialize GLEW. Does your graphics card support OpenGL 2.0 or greater? If so, are the latest drivers installed?"); + osdialog_message(OSDIALOG_ERROR, OSDIALOG_OK, "Could not initialize GLEW. Does your graphics card support OpenGL 2.0 or greater? If so, make sure you have the latest graphics drivers installed."); exit(1); }