| @@ -141,6 +141,7 @@ struct SVGKnob : Knob, FramebufferWidget { | |||
| SVGKnob(); | |||
| void setSVG(std::shared_ptr<SVG> svg); | |||
| void step(); | |||
| void draw(NVGcontext *vg); | |||
| void onChange(); | |||
| }; | |||
| @@ -180,15 +181,16 @@ struct MomentarySwitch : virtual Switch { | |||
| // ports | |||
| //////////////////// | |||
| struct Port : OpaqueWidget, SpriteWidget { | |||
| struct Port : OpaqueWidget { | |||
| enum PortType { | |||
| DEFAULT, | |||
| INPUT, | |||
| OUTPUT | |||
| }; | |||
| Module *module = NULL; | |||
| WireWidget *connectedWire = NULL; | |||
| PortType type; | |||
| PortType type = DEFAULT; | |||
| int portId; | |||
| Port(); | |||
| @@ -204,6 +206,21 @@ struct Port : OpaqueWidget, SpriteWidget { | |||
| void onDragLeave(Widget *origin); | |||
| }; | |||
| struct SpritePort : Port, SpriteWidget { | |||
| void draw(NVGcontext *vg) { | |||
| Port::draw(vg); | |||
| SpriteWidget::draw(vg); | |||
| } | |||
| }; | |||
| struct SVGPort : Port, FramebufferWidget { | |||
| SVGWidget *sw; | |||
| SVGPort(); | |||
| void setSVG(std::shared_ptr<SVG> svg); | |||
| void draw(NVGcontext *vg); | |||
| }; | |||
| //////////////////// | |||
| // scene | |||
| //////////////////// | |||
| @@ -24,15 +24,239 @@ extern const NVGcolor colors[NUM_COLORS]; | |||
| // Knobs | |||
| //////////////////// | |||
| struct SynthTechAlco : SpriteKnob { | |||
| struct Rogan : SVGKnob { | |||
| Rogan() { | |||
| minAngle = -0.75*M_PI; | |||
| maxAngle = 0.75*M_PI; | |||
| } | |||
| }; | |||
| struct Rogan6PS : Rogan { | |||
| Rogan6PS() { | |||
| box.size = Vec(89, 89); | |||
| minAngle = -0.83*M_PI; | |||
| maxAngle = 0.83*M_PI; | |||
| } | |||
| }; | |||
| struct Rogan5PS : Rogan { | |||
| Rogan5PS() { | |||
| box.size = Vec(60, 60); | |||
| } | |||
| }; | |||
| struct Rogan3PS : Rogan { | |||
| Rogan3PS() { | |||
| box.size = Vec(52, 52); | |||
| } | |||
| }; | |||
| struct Rogan3P : Rogan { | |||
| Rogan3P() { | |||
| box.size = Vec(42, 42); | |||
| } | |||
| }; | |||
| struct Rogan2S : Rogan { | |||
| Rogan2S() { | |||
| box.size = Vec(43, 43); | |||
| } | |||
| }; | |||
| struct Rogan2PS : Rogan { | |||
| Rogan2PS() { | |||
| box.size = Vec(43, 43); | |||
| } | |||
| }; | |||
| struct Rogan2P : Rogan { | |||
| Rogan2P() { | |||
| box.size = Vec(34, 34); | |||
| } | |||
| }; | |||
| struct Rogan1PS : Rogan { | |||
| Rogan1PS() { | |||
| box.size = Vec(40, 40); | |||
| } | |||
| }; | |||
| struct Rogan1P : Rogan { | |||
| Rogan1P() { | |||
| box.size = Vec(31, 31); | |||
| } | |||
| }; | |||
| struct Rogan6PSWhite : Rogan6PS { | |||
| Rogan6PSWhite() { | |||
| setSVG(SVG::load("res/ComponentLibrary/Rogan6PSWhite.svg")); | |||
| } | |||
| }; | |||
| struct Rogan5PSGray : Rogan5PS { | |||
| Rogan5PSGray() { | |||
| setSVG(SVG::load("res/ComponentLibrary/Rogan5PSGray.svg")); | |||
| } | |||
| }; | |||
| struct Rogan3PSBlue : Rogan3PS { | |||
| Rogan3PSBlue() { | |||
| setSVG(SVG::load("res/ComponentLibrary/Rogan3PSBlue.svg")); | |||
| } | |||
| }; | |||
| struct Rogan3PSRed : Rogan3PS { | |||
| Rogan3PSRed() { | |||
| setSVG(SVG::load("res/ComponentLibrary/Rogan3PSRed.svg")); | |||
| } | |||
| }; | |||
| struct Rogan3PSGreen : Rogan3PS { | |||
| Rogan3PSGreen() { | |||
| setSVG(SVG::load("res/ComponentLibrary/Rogan3PSGreen.svg")); | |||
| } | |||
| }; | |||
| struct Rogan3PSWhite : Rogan3PS { | |||
| Rogan3PSWhite() { | |||
| setSVG(SVG::load("res/ComponentLibrary/Rogan3PSWhite.svg")); | |||
| } | |||
| }; | |||
| struct Rogan3PBlue : Rogan3P { | |||
| Rogan3PBlue() { | |||
| setSVG(SVG::load("res/ComponentLibrary/Rogan3PBlue.svg")); | |||
| } | |||
| }; | |||
| struct Rogan3PRed : Rogan3P { | |||
| Rogan3PRed() { | |||
| setSVG(SVG::load("res/ComponentLibrary/Rogan3PRed.svg")); | |||
| } | |||
| }; | |||
| struct Rogan3PGreen : Rogan3P { | |||
| Rogan3PGreen() { | |||
| setSVG(SVG::load("res/ComponentLibrary/Rogan3PGreen.svg")); | |||
| } | |||
| }; | |||
| struct Rogan3PWhite : Rogan3P { | |||
| Rogan3PWhite() { | |||
| setSVG(SVG::load("res/ComponentLibrary/Rogan3PWhite.svg")); | |||
| } | |||
| }; | |||
| struct Rogan2SGray : Rogan2S { | |||
| Rogan2SGray() { | |||
| setSVG(SVG::load("res/ComponentLibrary/Rogan2SGray.svg")); | |||
| } | |||
| }; | |||
| struct Rogan2PSBlue : Rogan2PS { | |||
| Rogan2PSBlue() { | |||
| setSVG(SVG::load("res/ComponentLibrary/Rogan2PSBlue.svg")); | |||
| } | |||
| }; | |||
| struct Rogan2PSRed : Rogan2PS { | |||
| Rogan2PSRed() { | |||
| setSVG(SVG::load("res/ComponentLibrary/Rogan2PSRed.svg")); | |||
| } | |||
| }; | |||
| struct Rogan2PSGreen : Rogan2PS { | |||
| Rogan2PSGreen() { | |||
| setSVG(SVG::load("res/ComponentLibrary/Rogan2PSGreen.svg")); | |||
| } | |||
| }; | |||
| struct Rogan2PSWhite : Rogan2PS { | |||
| Rogan2PSWhite() { | |||
| setSVG(SVG::load("res/ComponentLibrary/Rogan2PSWhite.svg")); | |||
| } | |||
| }; | |||
| struct Rogan2PBlue : Rogan2P { | |||
| Rogan2PBlue() { | |||
| setSVG(SVG::load("res/ComponentLibrary/Rogan2PBlue.svg")); | |||
| } | |||
| }; | |||
| struct Rogan2PRed : Rogan2P { | |||
| Rogan2PRed() { | |||
| setSVG(SVG::load("res/ComponentLibrary/Rogan2PRed.svg")); | |||
| } | |||
| }; | |||
| struct Rogan2PGreen : Rogan2P { | |||
| Rogan2PGreen() { | |||
| setSVG(SVG::load("res/ComponentLibrary/Rogan2PGreen.svg")); | |||
| } | |||
| }; | |||
| struct Rogan2PWhite : Rogan2P { | |||
| Rogan2PWhite() { | |||
| setSVG(SVG::load("res/ComponentLibrary/Rogan2PWhite.svg")); | |||
| } | |||
| }; | |||
| struct Rogan1PSBlue : Rogan1PS { | |||
| Rogan1PSBlue() { | |||
| setSVG(SVG::load("res/ComponentLibrary/Rogan1PSBlue.svg")); | |||
| } | |||
| }; | |||
| struct Rogan1PSRed : Rogan1PS { | |||
| Rogan1PSRed() { | |||
| setSVG(SVG::load("res/ComponentLibrary/Rogan1PSRed.svg")); | |||
| } | |||
| }; | |||
| struct Rogan1PSGreen : Rogan1PS { | |||
| Rogan1PSGreen() { | |||
| setSVG(SVG::load("res/ComponentLibrary/Rogan1PSGreen.svg")); | |||
| } | |||
| }; | |||
| struct Rogan1PSWhite : Rogan1PS { | |||
| Rogan1PSWhite() { | |||
| setSVG(SVG::load("res/ComponentLibrary/Rogan1PSWhite.svg")); | |||
| } | |||
| }; | |||
| struct Rogan1PBlue : Rogan1P { | |||
| Rogan1PBlue() { | |||
| setSVG(SVG::load("res/ComponentLibrary/Rogan1PBlue.svg")); | |||
| } | |||
| }; | |||
| struct Rogan1PRed : Rogan1P { | |||
| Rogan1PRed() { | |||
| setSVG(SVG::load("res/ComponentLibrary/Rogan1PRed.svg")); | |||
| } | |||
| }; | |||
| struct Rogan1PGreen : Rogan1P { | |||
| Rogan1PGreen() { | |||
| setSVG(SVG::load("res/ComponentLibrary/Rogan1PGreen.svg")); | |||
| } | |||
| }; | |||
| struct Rogan1PWhite : Rogan1P { | |||
| Rogan1PWhite() { | |||
| setSVG(SVG::load("res/ComponentLibrary/Rogan1PWhite.svg")); | |||
| } | |||
| }; | |||
| struct SynthTechAlco : SVGKnob { | |||
| 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"); | |||
| minAngle = -0.82*M_PI; | |||
| maxAngle = 0.82*M_PI; | |||
| setSVG(SVG::load("res/ComponentLibrary/SynthTechAlco.svg")); | |||
| } | |||
| }; | |||
| @@ -62,6 +286,15 @@ struct Davies1900hRedKnob : Davies1900hKnob { | |||
| } | |||
| }; | |||
| struct Trimpot : SVGKnob { | |||
| Trimpot() { | |||
| box.size = Vec(17, 17); | |||
| minAngle = -0.75*M_PI; | |||
| maxAngle = 0.75*M_PI; | |||
| setSVG(SVG::load("res/ComponentLibrary/Trimpot.svg")); | |||
| } | |||
| }; | |||
| struct BefacoBigKnob : SVGKnob { | |||
| BefacoBigKnob() { | |||
| box.size = Vec(75, 75); | |||
| @@ -96,30 +329,33 @@ struct BefacoSlidePot : SpriteKnob { | |||
| // Jacks | |||
| //////////////////// | |||
| struct PJ301MPort : Port { | |||
| struct PJ301MPort : SpritePort { | |||
| PJ301MPort() { | |||
| box.size = Vec(24, 24); | |||
| spriteOffset = Vec(-2, -2); | |||
| spriteSize = Vec(30, 30); | |||
| spriteImage = Image::load("res/ComponentLibrary/PJ301M.png"); | |||
| // setSVG(SVG::load("res/ComponentLibrary/PJ301M.svg")); | |||
| } | |||
| }; | |||
| struct PJ3410Port : Port { | |||
| struct PJ3410Port : SpritePort { | |||
| PJ3410Port() { | |||
| box.size = Vec(32, 31); | |||
| spriteOffset = Vec(-1, -1); | |||
| spriteSize = Vec(36, 36); | |||
| spriteImage = Image::load("res/ComponentLibrary/PJ3410.png"); | |||
| // setSVG(SVG::load("res/ComponentLibrary/PJ3410.svg")); | |||
| } | |||
| }; | |||
| struct CL1362Port : Port { | |||
| struct CL1362Port : SpritePort { | |||
| CL1362Port() { | |||
| box.size = Vec(33, 29); | |||
| spriteOffset = Vec(-2, -2); | |||
| spriteSize = Vec(39, 36); | |||
| spriteImage = Image::load("res/ComponentLibrary/CL1362.png"); | |||
| // setSVG(SVG::load("res/ComponentLibrary/CL1362.svg")); | |||
| } | |||
| }; | |||
| @@ -5,6 +5,53 @@ | |||
| namespace rack { | |||
| //////////////////// | |||
| // integer functions | |||
| //////////////////// | |||
| inline int mini(int a, int b) { | |||
| return a < b ? a : b; | |||
| } | |||
| inline int maxi(int a, int b) { | |||
| return a > b ? a : b; | |||
| } | |||
| inline int absi(int a) { | |||
| return a >= 0 ? a : -a; | |||
| } | |||
| // Euclidean modulus, always returns 0 <= mod < base for positive base | |||
| // Assumes this architecture's division is non-Euclidean | |||
| inline int eucmod(int a, int base) { | |||
| int mod = a % base; | |||
| return mod < 0 ? mod + base : mod; | |||
| } | |||
| inline int log2i(int n) { | |||
| int i = 0; | |||
| while (n >>= 1) { | |||
| i++; | |||
| } | |||
| return i; | |||
| } | |||
| inline bool ispow2(int n) { | |||
| return n > 0 && (n & (n - 1)) == 0; | |||
| } | |||
| //////////////////// | |||
| // float functions | |||
| //////////////////// | |||
| inline float radtodeg(float x) { | |||
| return x * (180.0 / M_PI); | |||
| } | |||
| inline float degtorad(float x) { | |||
| return x * (M_PI / 180.0); | |||
| } | |||
| /** Limits a value between a minimum and maximum | |||
| If min > max for some reason, returns min | |||
| */ | |||
| @@ -31,14 +78,6 @@ inline float crossf(float a, float b, float frac) { | |||
| return (1.0 - frac) * a + frac * b; | |||
| } | |||
| inline int mini(int a, int b) { | |||
| return a < b ? a : b; | |||
| } | |||
| inline int maxi(int a, int b) { | |||
| return a > b ? a : b; | |||
| } | |||
| inline float quadraticBipolar(float x) { | |||
| float x2 = x*x; | |||
| return x >= 0.0 ? x2 : -x2; | |||
| @@ -92,25 +131,6 @@ inline float interpf(const float *p, float x) { | |||
| return crossf(p[xi], p[xi+1], xf); | |||
| } | |||
| // Euclidean modulus, always returns 0 <= mod < base for positive base | |||
| // Assumes this architecture's division is non-Euclidean | |||
| inline int eucmod(int a, int base) { | |||
| int mod = a % base; | |||
| return mod < 0 ? mod + base : mod; | |||
| } | |||
| inline int log2i(int n) { | |||
| int i = 0; | |||
| while (n >>= 1) { | |||
| i++; | |||
| } | |||
| return i; | |||
| } | |||
| inline bool ispow2(int n) { | |||
| return n > 0 && (n & (n - 1)) == 0; | |||
| } | |||
| //////////////////// | |||
| // 2D float vector | |||
| //////////////////// | |||
| @@ -23,11 +23,13 @@ void ModuleWidget::setModule(Module *module) { | |||
| } | |||
| void ModuleWidget::addInput(Port *input) { | |||
| assert(input->type == Port::INPUT); | |||
| inputs.push_back(input); | |||
| addChild(input); | |||
| } | |||
| void ModuleWidget::addOutput(Port *output) { | |||
| assert(output->type == Port::OUTPUT); | |||
| outputs.push_back(output); | |||
| addChild(output); | |||
| } | |||
| @@ -24,7 +24,6 @@ void Port::draw(NVGcontext *vg) { | |||
| if (type == INPUT ? gRackWidget->activeWire->inputPort : gRackWidget->activeWire->outputPort) | |||
| nvgGlobalAlpha(vg, 0.5); | |||
| } | |||
| SpriteWidget::draw(vg); | |||
| } | |||
| void Port::onMouseDown(int button) { | |||
| @@ -35,6 +35,26 @@ void SVGKnob::step() { | |||
| FramebufferWidget::step(); | |||
| } | |||
| void SVGKnob::draw(NVGcontext *vg) { | |||
| // Draw circular shadow below knob | |||
| // TODO This is a hack. Make a CircularShadow its own class | |||
| { | |||
| nvgBeginPath(vg); | |||
| float margin = 5.0; | |||
| nvgRect(vg, -margin, -margin, box.size.x + 2*margin, box.size.y + 2*margin); | |||
| nvgFillColor(vg, nvgRGBAf(0.0, 0.0, 0.0, 0.25)); | |||
| Vec c = box.size.div(2.0); | |||
| float radius = c.x - 1; | |||
| NVGcolor icol = nvgRGBAf(0.0, 0.0, 0.0, 0.25); | |||
| NVGcolor ocol = nvgRGBAf(0.0, 0.0, 0.0, 0.0); | |||
| NVGpaint paint = nvgRadialGradient(vg, c.x, c.y + 1, radius, radius + 3, icol, ocol); | |||
| nvgFillPaint(vg, paint); | |||
| nvgFill(vg); | |||
| } | |||
| FramebufferWidget::draw(vg); | |||
| } | |||
| void SVGKnob::onChange() { | |||
| dirty = true; | |||
| ParamWidget::onChange(); | |||
| @@ -0,0 +1,23 @@ | |||
| #include "app.hpp" | |||
| namespace rack { | |||
| SVGPort::SVGPort() { | |||
| sw = new SVGWidget(); | |||
| setScene(sw); | |||
| } | |||
| void SVGPort::setSVG(std::shared_ptr<SVG> svg) { | |||
| sw->svg = svg; | |||
| sw->wrap(); | |||
| } | |||
| void SVGPort::draw(NVGcontext *vg) { | |||
| Port::draw(vg); | |||
| FramebufferWidget::draw(vg); | |||
| } | |||
| } // namespace rack | |||
| @@ -1,31 +1,42 @@ | |||
| #include "widgets.hpp" | |||
| // #define DEBUG_ONLY(x) x | |||
| #define DEBUG_ONLY(x) | |||
| 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 NVGcolor getNVGColor(uint32_t color) { | |||
| return nvgRGBA( | |||
| (color >> 0) & 0xff, | |||
| (color >> 8) & 0xff, | |||
| (color >> 16) & 0xff, | |||
| (color >> 24) & 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); | |||
| DEBUG_ONLY(printf("new image: %g x %g px\n", svg->width, svg->height);) | |||
| int shapeIndex = 0; | |||
| for (NSVGshape *shape = svg->shapes; shape; shape = shape->next, shapeIndex++) { | |||
| DEBUG_ONLY(printf(" new shape: %d id \"%s\", fillrule %d\n", shapeIndex, shape->id, shape->fillRule);) | |||
| if (!(shape->flags & NSVG_FLAGS_VISIBLE)) | |||
| continue; | |||
| nvgSave(vg); | |||
| nvgGlobalAlpha(vg, shape->opacity); | |||
| if (shape->opacity < 1.0) | |||
| nvgGlobalAlpha(vg, shape->opacity); | |||
| nvgStrokeWidth(vg, shape->strokeWidth); | |||
| // strokeDashOffset, strokeDashArray, strokeDashCount not supported | |||
| // strokeLineJoin, strokeLineCap not supported | |||
| // strokeDashOffset, strokeDashArray, strokeDashCount not yet supported | |||
| // strokeLineJoin, strokeLineCap not yet 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"); | |||
| DEBUG_ONLY(printf(" new path: %d points, %s\n", path->npts, path->closed ? "closed" : "open");) | |||
| nvgMoveTo(vg, path->pts[0], path->pts[1]); | |||
| for (int i = 1; i < path->npts; i += 3) { | |||
| @@ -37,7 +48,6 @@ static void drawSVG(NVGcontext *vg, NSVGimage *svg) { | |||
| if (path->closed) | |||
| nvgClosePath(vg); | |||
| if (path->next) | |||
| nvgPathWinding(vg, NVG_HOLE); | |||
| } | |||
| @@ -48,7 +58,7 @@ static void drawSVG(NVGcontext *vg, NSVGimage *svg) { | |||
| 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); | |||
| DEBUG_ONLY(printf(" fill color (%g, %g, %g, %g)\n", color.r, color.g, color.b, color.a);) | |||
| } break; | |||
| case NSVG_PAINT_LINEAR_GRADIENT: { | |||
| // NSVGgradient *g = shape->fill.gradient; | |||
| @@ -63,8 +73,8 @@ static void drawSVG(NVGcontext *vg, NSVGimage *svg) { | |||
| 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); | |||
| nvgStrokeColor(vg, color); | |||
| DEBUG_ONLY(printf(" stroke color (%g, %g, %g, %g)\n", color.r, color.g, color.b, color.a);) | |||
| } break; | |||
| case NSVG_PAINT_LINEAR_GRADIENT: { | |||
| // NSVGgradient *g = shape->stroke.gradient; | |||
| @@ -76,6 +86,8 @@ static void drawSVG(NVGcontext *vg, NSVGimage *svg) { | |||
| nvgRestore(vg); | |||
| } | |||
| DEBUG_ONLY(printf("\n");) | |||
| } | |||
| @@ -87,7 +99,9 @@ void SVGWidget::wrap() { | |||
| } | |||
| void SVGWidget::draw(NVGcontext *vg) { | |||
| drawSVG(vg, svg->handle); | |||
| if (svg && svg->handle) { | |||
| drawSVG(vg, svg->handle); | |||
| } | |||
| } | |||