@@ -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); | |||
} | |||
} | |||