| @@ -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. | |||
| @@ -1 +1 @@ | |||
| Subproject commit 8feae63a46fdf8ed83613b6a46da25d44adde07f | |||
| Subproject commit c629efbfa6ca90035d6625dfa3586048a9c3201f | |||
| @@ -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<OutputPort> OutputPortPJ301M; | |||
| template <typename BASE> | |||
| 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 <int COLOR> | |||
| 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<COLOR_RED> RedValueLight; | |||
| typedef ColorValueLight<COLOR_YELLOW> YellowValueLight; | |||
| typedef ColorValueLight<COLOR_GREEN> GreenValueLight; | |||
| template <int COLOR_POS, int COLOR_NEG> | |||
| 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<COLOR_GREEN, COLOR_RED> GreenRedPolarityLight; | |||
| template <typename BASE> | |||
| 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); | |||
| } | |||
| }; | |||
| @@ -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 | |||
| @@ -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; | |||
| } | |||
| }; | |||
| @@ -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> 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 { | |||
| @@ -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 | |||
| @@ -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<Font> 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> Font::load(const std::string &filename) { | |||
| @@ -301,7 +300,7 @@ std::shared_ptr<Font> 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> Image::load(const std::string &filename) { | |||
| @@ -350,83 +349,4 @@ std::shared_ptr<SVG> 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 | |||
| @@ -0,0 +1,88 @@ | |||
| #include "widgets.hpp" | |||
| #include "gui.hpp" | |||
| #include <GL/glew.h> | |||
| #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 | |||
| @@ -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); | |||
| } | |||
| @@ -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); | |||
| @@ -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 | |||
| @@ -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 | |||
| @@ -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; | |||