diff --git a/README.md b/README.md index 5f3e0312..04ea0a5e 100644 --- a/README.md +++ b/README.md @@ -18,6 +18,6 @@ Install dependencies - [libsamplerate](http://www.mega-nerd.com/SRC/) - GTK+-2.0 if Linux (for file open/save dialog) -Run `make ARCH=linux` or `make ARCH=windows` or `make ARCH=apple` +Run `make ARCH=lin` or `make ARCH=win` or `make ARCH=mac` If the build breaks because you think I've missed a step, feel free to post an issue. diff --git a/ext/nanovg b/ext/nanovg index 8feae63a..c629efbf 160000 --- a/ext/nanovg +++ b/ext/nanovg @@ -1 +1 @@ -Subproject commit 8feae63a46fdf8ed83613b6a46da25d44adde07f +Subproject commit c629efbfa6ca90035d6625dfa3586048a9c3201f diff --git a/include/components.hpp b/include/components.hpp index 5c1a0173..eb2115e3 100644 --- a/include/components.hpp +++ b/include/components.hpp @@ -4,20 +4,38 @@ namespace rack { -#define SCHEME_BLACK nvgRGB(0x00, 0x00, 0x00) -#define SCHEME_WHITE nvgRGB(0xff, 0xff, 0xff) -#define SCHEME_RED nvgRGB(0xed, 0x2c, 0x24) -#define SCHEME_ORANGE nvgRGB(0xf2, 0xb1, 0x20) -#define SCHEME_YELLOW nvgRGB(0xf9, 0xdf, 0x1c) -#define SCHEME_GREEN nvgRGB(0x90, 0xc7, 0x3e) -#define SCHEME_CYAN nvgRGB(0x22, 0xe6, 0xef) -#define SCHEME_BLUE nvgRGB(0x29, 0xb2, 0xef) -#define SCHEME_PURPLE nvgRGB(0xd5, 0x2b, 0xed) + +enum ColorNames { + COLOR_BLACK, + COLOR_WHITE, + COLOR_RED, + COLOR_ORANGE, + COLOR_YELLOW, + COLOR_GREEN, + COLOR_CYAN, + COLOR_BLUE, + COLOR_PURPLE, + NUM_COLORS +}; + +extern const NVGcolor colors[NUM_COLORS]; //////////////////// // Knobs //////////////////// +struct SynthTechAlco : SpriteKnob { + SynthTechAlco() { + box.size = Vec(45, 45); + spriteOffset = Vec(-3, -2); + spriteSize = Vec(51, 51); + minIndex = 49; + maxIndex = -51; + spriteCount = 120; + spriteImage = Image::load("res/ComponentLibrary/SynthTechAlco.png"); + } +}; + struct KnobDavies1900h : SpriteKnob { KnobDavies1900h() { box.size = Vec(36, 36); @@ -102,7 +120,7 @@ typedef PJ301M OutputPortPJ301M; template struct PJ3410 : BASE { PJ3410() { - this->box.size = Vec(32, 32); + this->box.size = Vec(32, 31); this->spriteOffset = Vec(-1, -1); this->spriteSize = Vec(36, 36); this->spriteImage = Image::load("res/ComponentLibrary/PJ3410.png"); @@ -131,23 +149,31 @@ struct ValueLight : Light { float *value; }; -struct RedValueLight : ValueLight { +template +struct ColorValueLight : ValueLight { void step() { float v = sqrtBipolar(getf(value)); - color = nvgLerpRGBA(SCHEME_BLACK, SCHEME_RED, v); + color = nvgLerpRGBA(colors[COLOR_BLACK], colors[COLOR], v); } }; -struct GreenRedPolarityLight : ValueLight { +typedef ColorValueLight RedValueLight; +typedef ColorValueLight YellowValueLight; +typedef ColorValueLight GreenValueLight; + +template +struct PolarityLight : ValueLight { void step() { float v = sqrtBipolar(getf(value)); if (v >= 0.0) - color = nvgLerpRGBA(SCHEME_BLACK, SCHEME_GREEN, v); + color = nvgLerpRGBA(colors[COLOR_BLACK], colors[COLOR_POS], v); else - color = nvgLerpRGBA(SCHEME_BLACK, SCHEME_RED, -v); + color = nvgLerpRGBA(colors[COLOR_BLACK], colors[COLOR_NEG], -v); } }; +typedef PolarityLight GreenRedPolarityLight; + template struct LargeLight : BASE { LargeLight() { @@ -197,13 +223,13 @@ struct SilverScrew : Screw { struct LightPanel : Panel { LightPanel() { backgroundColor = nvgRGB(0xe8, 0xe8, 0xe8); - borderColor = nvgRGB(0xac, 0xac, 0xac); + borderColor = nvgRGB(0xa1, 0xa1, 0xa1); } }; struct DarkPanel : Panel { DarkPanel() { - backgroundColor = nvgRGB(0x0f, 0x0f, 0x0f); + backgroundColor = nvgRGB(0x17, 0x17, 0x17); borderColor = nvgRGB(0x5e, 0x5e, 0x5e); } }; diff --git a/include/gui.hpp b/include/gui.hpp index f95c8044..a634366d 100644 --- a/include/gui.hpp +++ b/include/gui.hpp @@ -13,7 +13,6 @@ void guiCursorUnlock(); const char *guiSaveDialog(const char *filters, const char *filename); const char *guiOpenDialog(const char *filters, const char *filename); -// TODO This should probably go in another file, like resources.hpp? -void drawSVG(NVGcontext *vg, NSVGimage *svg); +extern NVGcontext *gVg; } // namespace rack diff --git a/include/math.hpp b/include/math.hpp index 61a86a07..5112e18d 100644 --- a/include/math.hpp +++ b/include/math.hpp @@ -61,6 +61,12 @@ inline float sqrtBipolar(float x) { return x >= 0.0 ? sqrtf(x) : -sqrtf(-x); } +/** This is pretty much a scaled sinh */ +inline float exponentialBipolar(float b, float x) { + const float a = b - 1.0 / b; + return (powf(b, x) - powf(b, -x)) / a; +} + inline float sincf(float x) { if (x == 0.0) return 1.0; @@ -145,6 +151,12 @@ struct Vec { Vec round() { return Vec(roundf(x), roundf(y)); } + bool isFinite() { + return isfinite(x) && isfinite(y); + } + bool isZero() { + return x == 0.0 && y == 0.0; + } }; diff --git a/include/widgets.hpp b/include/widgets.hpp index cb05b574..47468be7 100644 --- a/include/widgets.hpp +++ b/include/widgets.hpp @@ -43,7 +43,7 @@ struct SVG { //////////////////// -// base class and traits +// Base widget //////////////////// /** A node in the 2D scene graph */ @@ -112,6 +112,22 @@ struct Widget { virtual void onChange() {} }; +struct TransformWidget : Widget { + /** The transformation matrix */ + float transform[6]; + TransformWidget(); + void reset(); + void translate(Vec delta); + void rotate(float angle); + void scale(Vec s); + void draw(NVGcontext *vg); +}; + + +//////////////////// +// Trait widgets +//////////////////// + /** Widget that does not respond to events */ struct TransparentWidget : virtual Widget { Widget *onMouseDown(Vec pos, int button) {return NULL;} @@ -157,6 +173,33 @@ struct SpriteWidget : virtual Widget { void draw(NVGcontext *vg); }; +struct SVGWidget : virtual Widget { + std::shared_ptr svg; + /** Sets the box size to the svg page */ + void step(); + void draw(NVGcontext *vg); +}; + +/** Caches a widget's draw() result to a framebuffer so it is called less frequently +When `dirty` is true, `scene` will be re-rendered on the next call to step(). +Events are not passed to the underlying scene. +*/ +struct FramebufferWidget : virtual Widget { + bool dirty = true; + /** The root object in the framebuffer scene + Its position is ignored for now, fixed at (0, 0) + The FramebufferWidget owns the pointer + */ + Widget *scene = NULL; + struct Internal; + Internal *internal; + + FramebufferWidget(); + ~FramebufferWidget(); + void step(); + void draw(NVGcontext *vg); +}; + struct QuantityWidget : virtual Widget { float value = 0.0; float minValue = 0.0; @@ -179,7 +222,7 @@ struct QuantityWidget : virtual Widget { }; //////////////////// -// gui elements +// GUI widgets //////////////////// struct Label : Widget { diff --git a/src/components.cpp b/src/components.cpp new file mode 100644 index 00000000..dad6ae23 --- /dev/null +++ b/src/components.cpp @@ -0,0 +1,19 @@ +#include "components.hpp" + + +namespace rack { + +const NVGcolor colors[NUM_COLORS] = { + nvgRGB(0x00, 0x00, 0x00), + nvgRGB(0xff, 0xff, 0xff), + nvgRGB(0xed, 0x2c, 0x24), + nvgRGB(0xf2, 0xb1, 0x20), + nvgRGB(0xf9, 0xdf, 0x1c), + nvgRGB(0x90, 0xc7, 0x3e), + nvgRGB(0x22, 0xe6, 0xef), + nvgRGB(0x29, 0xb2, 0xef), + nvgRGB(0xd5, 0x2b, 0xed), +}; + + +} // namespace rack diff --git a/src/gui.cpp b/src/gui.cpp index c2d72515..d25fdbb1 100644 --- a/src/gui.cpp +++ b/src/gui.cpp @@ -7,9 +7,10 @@ #include "scene.hpp" // Include implementations here -// By the way, please stop packaging your libraries like this. It's easiest to use a single source file (e.g. foo.c) and a single header (e.g. foo.h) +// By the way, please stop packaging your libraries like this. It's best to use a single source file (e.g. foo.c) and a single header (e.g. foo.h) #define NANOVG_GL3_IMPLEMENTATION #include "../ext/nanovg/src/nanovg_gl.h" +#include "../ext/nanovg/src/nanovg_gl_utils.h" #define BLENDISH_IMPLEMENTATION #include "../ext/oui/blendish.h" #define NANOSVG_IMPLEMENTATION @@ -23,8 +24,8 @@ extern "C" { namespace rack { static GLFWwindow *window = NULL; -static NVGcontext *vg = NULL; static std::shared_ptr defaultFont; +NVGcontext *gVg = NULL; void windowSizeCallback(GLFWwindow* window, int width, int height) { @@ -160,16 +161,16 @@ void renderGui() { // Update and render glViewport(0, 0, width, height); - glClearColor(1.0, 1.0, 1.0, 1.0); + glClearColor(0.0, 0.0, 0.0, 1.0); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); - nvgBeginFrame(vg, width, height, 1.0); + nvgBeginFrame(gVg, width, height, 1.0); - nvgSave(vg); - gScene->draw(vg); - nvgRestore(vg); + nvgSave(gVg); + gScene->draw(gVg); + nvgRestore(gVg); - nvgEndFrame(vg); + nvgEndFrame(gVg); glfwSwapBuffers(window); } @@ -180,12 +181,10 @@ void guiInit() { err = glfwInit(); assert(err); -// #ifndef WINDOWS glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2); glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); -// #endif window = glfwCreateWindow(1000, 750, gApplicationName.c_str(), NULL, NULL); assert(window); glfwMakeContextCurrent(window); @@ -210,8 +209,8 @@ void guiInit() { glfwSetWindowSizeLimits(window, 240, 160, GLFW_DONT_CARE, GLFW_DONT_CARE); // Set up NanoVG - vg = nvgCreateGL3(NVG_ANTIALIAS); - assert(vg); + gVg = nvgCreateGL3(NVG_ANTIALIAS); + assert(gVg); // Set up Blendish defaultFont = Font::load("res/DejaVuSans.ttf"); @@ -221,7 +220,7 @@ void guiInit() { void guiDestroy() { defaultFont.reset(); - nvgDeleteGL3(vg); + nvgDeleteGL3(gVg); glfwDestroyWindow(window); glfwTerminate(); } @@ -275,7 +274,7 @@ const char *guiOpenDialog(const char *filters, const char *filename) { //////////////////// Font::Font(const std::string &filename) { - handle = nvgCreateFont(vg, filename.c_str(), filename.c_str()); + handle = nvgCreateFont(gVg, filename.c_str(), filename.c_str()); if (handle >= 0) { fprintf(stderr, "Loaded font %s\n", filename.c_str()); } @@ -285,7 +284,7 @@ Font::Font(const std::string &filename) { } Font::~Font() { - // There is no NanoVG deleteFont() function, so do nothing + // There is no NanoVG deleteFont() function yet, so do nothing } std::shared_ptr Font::load(const std::string &filename) { @@ -301,7 +300,7 @@ std::shared_ptr Font::load(const std::string &filename) { //////////////////// Image::Image(const std::string &filename) { - handle = nvgCreateImage(vg, filename.c_str(), NVG_IMAGE_REPEATX | NVG_IMAGE_REPEATY); + handle = nvgCreateImage(gVg, filename.c_str(), NVG_IMAGE_REPEATX | NVG_IMAGE_REPEATY); if (handle > 0) { fprintf(stderr, "Loaded image %s\n", filename.c_str()); } @@ -312,7 +311,7 @@ Image::Image(const std::string &filename) { Image::~Image() { // TODO What if handle is invalid? - nvgDeleteImage(vg, handle); + nvgDeleteImage(gVg, handle); } std::shared_ptr Image::load(const std::string &filename) { @@ -350,83 +349,4 @@ std::shared_ptr SVG::load(const std::string &filename) { } -//////////////////// -// drawSVG -//////////////////// - -NVGcolor getNVGColor(int color) { - return nvgRGBA((color >> 0) & 0xff, (color >> 8) & 0xff, (color >> 16) & 0xff, (color >> 24) & 0xff); - // return nvgRGBA((color >> 24) & 0xff, (color >> 16) & 0xff, (color >> 8) & 0xff, (color) & 0xff); -} - -void drawSVG(NVGcontext *vg, NSVGimage *svg) { - for (NSVGshape *shape = svg->shapes; shape; shape = shape->next) { - // printf(" new shape: id \"%s\", fillrule %d\n", shape->id, shape->fillRule); - - if (!(shape->flags & NSVG_FLAGS_VISIBLE)) - continue; - - nvgSave(vg); - nvgGlobalAlpha(vg, shape->opacity); - nvgStrokeWidth(vg, shape->strokeWidth); - // strokeDashOffset, strokeDashArray, strokeDashCount not supported - // strokeLineJoin, strokeLineCap not supported - - // Build path - nvgBeginPath(vg); - for (NSVGpath *path = shape->paths; path; path = path->next) { - // printf(" new path: %d points, %s\n", path->npts, path->closed ? "closed" : "notclosed"); - - nvgMoveTo(vg, path->pts[0], path->pts[1]); - for (int i = 1; i < path->npts; i += 3) { - float *p = &path->pts[2*i]; - nvgBezierTo(vg, p[0], p[1], p[2], p[3], p[4], p[5]); - // nvgLineTo(vg, p[4], p[5]); - } - - if (path->closed) - nvgClosePath(vg); - - - if (path->next) - nvgPathWinding(vg, NVG_HOLE); - } - - // Fill shape - if (shape->fill.type) { - switch (shape->fill.type) { - case NSVG_PAINT_COLOR: { - NVGcolor color = getNVGColor(shape->fill.color); - nvgFillColor(vg, color); - // printf(" fill color (%f %f %f %f)\n", color.r, color.g, color.b, color.a); - } break; - case NSVG_PAINT_LINEAR_GRADIENT: { - NSVGgradient *g = shape->fill.gradient; - // printf(" lin grad: %f\t%f\n", g->fx, g->fy); - } break; - } - nvgFill(vg); - } - - // Stroke shape - if (shape->stroke.type) { - switch (shape->stroke.type) { - case NSVG_PAINT_COLOR: { - NVGcolor color = getNVGColor(shape->stroke.color); - nvgFillColor(vg, color); - // printf(" stroke color (%f %f %f %f)\n", color.r, color.g, color.b, color.a); - } break; - case NSVG_PAINT_LINEAR_GRADIENT: { - NSVGgradient *g = shape->stroke.gradient; - // printf(" lin grad: %f\t%f\n", g->fx, g->fy); - } break; - } - nvgStroke(vg); - } - - nvgRestore(vg); - } -} - - } // namespace rack diff --git a/src/widgets/FramebufferWidget.cpp b/src/widgets/FramebufferWidget.cpp new file mode 100644 index 00000000..0fe391ea --- /dev/null +++ b/src/widgets/FramebufferWidget.cpp @@ -0,0 +1,88 @@ +#include "widgets.hpp" +#include "gui.hpp" +#include +#include "../ext/nanovg/src/nanovg_gl.h" +#include "../ext/nanovg/src/nanovg_gl_utils.h" + + +namespace rack { + + +struct FramebufferWidget::Internal { + NVGLUframebuffer *fb = NULL; + + ~Internal() { + setFramebuffer(NULL); + } + void setFramebuffer(NVGLUframebuffer *fb) { + if (this->fb) + nvgluDeleteFramebuffer(this->fb); + this->fb = fb; + } +}; + +FramebufferWidget::FramebufferWidget() { + internal = new Internal(); +} + +FramebufferWidget::~FramebufferWidget() { + if (scene) { + delete scene; + } + delete internal; +} + +/** A margin in pixels around the scene in the framebuffer */ +static const int margin = 1; + +void FramebufferWidget::step() { + if (!scene) + return; + + scene->step(); + + // Render the scene to the framebuffer if dirty + if (dirty) { + assert(scene->box.size.isFinite()); + int width = ceilf(scene->box.size.x) + 2*margin; + int height = ceilf(scene->box.size.y) + 2*margin; + + internal->setFramebuffer(NULL); + NVGLUframebuffer *fb = nvgluCreateFramebuffer(gVg, width, height, NVG_IMAGE_REPEATX | NVG_IMAGE_REPEATY); + assert(fb); + internal->setFramebuffer(fb); + + // TODO Support screens with pixelRatio != 1.0 (e.g. Retina) by using the actual size of the framebuffer, etc. + const float pixelRatio = 1.0; + nvgluBindFramebuffer(fb); + glViewport(0.0, 0.0, width, height); + glClearColor(0.0, 0.0, 0.0, 0.0); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); + nvgBeginFrame(gVg, width, height, pixelRatio); + + nvgTranslate(gVg, margin, margin); + scene->draw(gVg); + + nvgEndFrame(gVg); + nvgluBindFramebuffer(NULL); + + dirty = false; + } +} + +void FramebufferWidget::draw(NVGcontext *vg) { + if (!internal->fb) + return; + + // Draw framebuffer image + int width, height; + nvgImageSize(vg, internal->fb->image, &width, &height); + nvgBeginPath(vg); + nvgRect(vg, -margin, -margin, width, height); + NVGpaint paint = nvgImagePattern(vg, -margin, -margin, width, height, 0.0, internal->fb->image, 1.0); + nvgFillPaint(vg, paint); + nvgFill(vg); +} + + +} // namespace rack diff --git a/src/widgets/Light.cpp b/src/widgets/Light.cpp index 878bce6f..67534fa4 100644 --- a/src/widgets/Light.cpp +++ b/src/widgets/Light.cpp @@ -17,18 +17,18 @@ void Light::draw(NVGcontext *vg) { nvgStrokeColor(vg, colorOutline); nvgStroke(vg); - // nvgGlobalCompositeOperation(vg, NVG_LIGHTER); - // NVGpaint paint; - // NVGcolor icol = color; - // icol.a = 0.2; - // NVGcolor ocol = color; - // ocol.a = 0.0; - // float oradius = radius + 20.0; - // paint = nvgRadialGradient(vg, radius, radius, radius, oradius, icol, ocol); - // nvgFillPaint(vg, paint); - // nvgBeginPath(vg); - // nvgRect(vg, radius - oradius, radius - oradius, 2*oradius, 2*oradius); - // nvgFill(vg); + nvgGlobalCompositeOperation(vg, NVG_LIGHTER); + NVGpaint paint; + NVGcolor icol = color; + icol.a = 0.1; + NVGcolor ocol = color; + ocol.a = 0.0; + float oradius = radius + 30.0; + paint = nvgRadialGradient(vg, radius, radius, radius, oradius, icol, ocol); + nvgFillPaint(vg, paint); + nvgBeginPath(vg); + nvgRect(vg, radius - oradius, radius - oradius, 2*oradius, 2*oradius); + nvgFill(vg); } diff --git a/src/widgets/ModuleWidget.cpp b/src/widgets/ModuleWidget.cpp index 9d8d7ce2..38f0a6a3 100644 --- a/src/widgets/ModuleWidget.cpp +++ b/src/widgets/ModuleWidget.cpp @@ -181,7 +181,7 @@ void ModuleWidget::onMouseDown(int button) { menu->pushChild(menuLabel); ResetParamsMenuItem *resetItem = new ResetParamsMenuItem(); - resetItem->text = "Reset parameters"; + resetItem->text = "Initialize parameters"; resetItem->moduleWidget = this; menu->pushChild(resetItem); diff --git a/src/widgets/SVGWidget.cpp b/src/widgets/SVGWidget.cpp new file mode 100644 index 00000000..929d5675 --- /dev/null +++ b/src/widgets/SVGWidget.cpp @@ -0,0 +1,95 @@ +#include "widgets.hpp" + + +namespace rack { + + +static NVGcolor getNVGColor(int color) { + return nvgRGBA((color >> 0) & 0xff, (color >> 8) & 0xff, (color >> 16) & 0xff, (color >> 24) & 0xff); + // return nvgRGBA((color >> 24) & 0xff, (color >> 16) & 0xff, (color >> 8) & 0xff, (color) & 0xff); +} + +static void drawSVG(NVGcontext *vg, NSVGimage *svg) { + for (NSVGshape *shape = svg->shapes; shape; shape = shape->next) { + // printf(" new shape: id \"%s\", fillrule %d\n", shape->id, shape->fillRule); + + if (!(shape->flags & NSVG_FLAGS_VISIBLE)) + continue; + + nvgSave(vg); + nvgGlobalAlpha(vg, shape->opacity); + nvgStrokeWidth(vg, shape->strokeWidth); + // strokeDashOffset, strokeDashArray, strokeDashCount not supported + // strokeLineJoin, strokeLineCap not supported + + // Build path + nvgBeginPath(vg); + for (NSVGpath *path = shape->paths; path; path = path->next) { + // printf(" new path: %d points, %s\n", path->npts, path->closed ? "closed" : "notclosed"); + + nvgMoveTo(vg, path->pts[0], path->pts[1]); + for (int i = 1; i < path->npts; i += 3) { + float *p = &path->pts[2*i]; + nvgBezierTo(vg, p[0], p[1], p[2], p[3], p[4], p[5]); + // nvgLineTo(vg, p[4], p[5]); + } + + if (path->closed) + nvgClosePath(vg); + + + if (path->next) + nvgPathWinding(vg, NVG_HOLE); + } + + // Fill shape + if (shape->fill.type) { + switch (shape->fill.type) { + case NSVG_PAINT_COLOR: { + NVGcolor color = getNVGColor(shape->fill.color); + nvgFillColor(vg, color); + // printf(" fill color (%f %f %f %f)\n", color.r, color.g, color.b, color.a); + } break; + case NSVG_PAINT_LINEAR_GRADIENT: { + // NSVGgradient *g = shape->fill.gradient; + // printf(" lin grad: %f\t%f\n", g->fx, g->fy); + } break; + } + nvgFill(vg); + } + + // Stroke shape + if (shape->stroke.type) { + switch (shape->stroke.type) { + case NSVG_PAINT_COLOR: { + NVGcolor color = getNVGColor(shape->stroke.color); + nvgFillColor(vg, color); + // printf(" stroke color (%f %f %f %f)\n", color.r, color.g, color.b, color.a); + } break; + case NSVG_PAINT_LINEAR_GRADIENT: { + // NSVGgradient *g = shape->stroke.gradient; + // printf(" lin grad: %f\t%f\n", g->fx, g->fy); + } break; + } + nvgStroke(vg); + } + + nvgRestore(vg); + } +} + + +void SVGWidget::step() { + // Automatically wrap box size to SVG page size + if (svg) + box.size = Vec(svg->handle->width, svg->handle->height); + else + box.size = Vec(); +} + +void SVGWidget::draw(NVGcontext *vg) { + drawSVG(vg, svg->handle); +} + + +} // namespace rack diff --git a/src/widgets/TransformWidget.cpp b/src/widgets/TransformWidget.cpp new file mode 100644 index 00000000..1206b672 --- /dev/null +++ b/src/widgets/TransformWidget.cpp @@ -0,0 +1,40 @@ +#include "scene.hpp" + + +namespace rack { + + +TransformWidget::TransformWidget() { + reset(); +} + +void TransformWidget::reset() { + nvgTransformIdentity(transform); +} + +void TransformWidget::translate(Vec delta) { + float t[6]; + nvgTransformTranslate(t, delta.x, delta.y); + nvgTransformPremultiply(transform, t); +} + +void TransformWidget::rotate(float angle) { + float t[6]; + nvgTransformRotate(t, angle); + nvgTransformPremultiply(transform, t); +} + +void TransformWidget::scale(Vec s) { + float t[6]; + nvgTransformScale(t, s.x, s.y); + nvgTransformPremultiply(transform, t); +} + +void TransformWidget::draw(NVGcontext *vg) { + // No need to save the state because that is done in the parent + nvgTransform(vg, transform[0], transform[1], transform[2], transform[3], transform[4], transform[5]); + Widget::draw(vg); +} + + +} // namespace rack diff --git a/src/widgets/WireWidget.cpp b/src/widgets/WireWidget.cpp index 8c98c7e2..98eeb1b2 100644 --- a/src/widgets/WireWidget.cpp +++ b/src/widgets/WireWidget.cpp @@ -32,7 +32,7 @@ static void drawWire(NVGcontext *vg, Vec pos1, Vec pos2, NVGcolor color, float t // Wire if (opacity > 0.0) { nvgSave(vg); - nvgGlobalAlpha(vg, opacity); + nvgGlobalAlpha(vg, powf(opacity, 1.5)); float dist = pos1.minus(pos2).norm(); Vec slump;