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