@@ -124,6 +124,7 @@ struct ParamWidget : OpaqueWidget, QuantityWidget { | |||||
void onChange(); | void onChange(); | ||||
}; | }; | ||||
/** Implements vertical dragging behavior for ParamWidgets */ | |||||
struct Knob : ParamWidget { | struct Knob : ParamWidget { | ||||
void onDragStart(); | void onDragStart(); | ||||
void onDragMove(Vec mouseRel); | void onDragMove(Vec mouseRel); | ||||
@@ -150,42 +151,76 @@ struct SVGKnob : Knob, FramebufferWidget { | |||||
void onChange(); | void onChange(); | ||||
}; | }; | ||||
struct Switch : ParamWidget, SpriteWidget { | |||||
struct SVGSlider : Knob, FramebufferWidget { | |||||
/** Intermediate positions will be interpolated between these positions */ | |||||
Vec minHandlePos, maxHandlePos; | |||||
/** Not owned */ | |||||
SVGWidget *background; | |||||
SVGWidget *handle; | |||||
SVGSlider(); | |||||
void step(); | |||||
void onChange(); | |||||
}; | }; | ||||
struct SVGSwitch : ParamWidget, FramebufferWidget { | |||||
struct Switch : ParamWidget { | |||||
virtual void setIndex(int index) {} | |||||
}; | |||||
struct SVGSwitch : virtual Switch, FramebufferWidget { | |||||
std::vector<std::shared_ptr<SVG>> frames; | |||||
/** Not owned */ | /** Not owned */ | ||||
TransformWidget *tw; | |||||
SVGWidget *swPressed; | |||||
SVGWidget *swReleased; | |||||
SVGWidget *sw; | |||||
SVGSwitch(); | |||||
/** Adds an SVG file to represent the next switch position */ | |||||
void addFrame(std::shared_ptr<SVG> svg); | |||||
void setIndex(int index); | |||||
void step(); | |||||
}; | }; | ||||
/** A switch that cycles through each mechanical position */ | |||||
struct ToggleSwitch : virtual Switch { | struct ToggleSwitch : virtual Switch { | ||||
void onDragStart() { | void onDragStart() { | ||||
index = 1; | |||||
// Cycle through values | |||||
// e.g. a range of [0.0, 3.0] would have modes 0, 1, 2, and 3. | |||||
float v = value + 1.0; | |||||
setValue(v <= maxValue ? v : minValue); | |||||
} | |||||
void onChange() { | |||||
int index = (int)roundf(value); | |||||
setIndex(index); | |||||
ParamWidget::onChange(); | |||||
} | |||||
}; | |||||
/** FIXME I don't think this should exist. The audio engine should read from a MomentarySwitch and increment its internal state, instead of relying on the knob to do that logic. */ | |||||
struct ModeSwitch : virtual Switch { | |||||
void onDragStart() { | |||||
setIndex(1); | |||||
} | } | ||||
void onDragEnd() { | void onDragEnd() { | ||||
index = 0; | |||||
setIndex(0); | |||||
} | } | ||||
void onDragDrop(Widget *origin) { | void onDragDrop(Widget *origin) { | ||||
if (origin != this) | if (origin != this) | ||||
return; | return; | ||||
// Cycle through modes | |||||
// Cycle through values | |||||
// e.g. a range of [0.0, 3.0] would have modes 0, 1, 2, and 3. | // e.g. a range of [0.0, 3.0] would have modes 0, 1, 2, and 3. | ||||
float v = value + 1.0; | float v = value + 1.0; | ||||
setValue(v > maxValue ? minValue : v); | |||||
setValue(v <= maxValue ? v : minValue); | |||||
} | } | ||||
}; | }; | ||||
/** A switch that is turned on when held */ | |||||
struct MomentarySwitch : virtual Switch { | struct MomentarySwitch : virtual Switch { | ||||
void onDragStart() { | void onDragStart() { | ||||
setValue(maxValue); | setValue(maxValue); | ||||
index = 1; | |||||
setIndex(1); | |||||
} | } | ||||
void onDragEnd() { | void onDragEnd() { | ||||
setValue(minValue); | setValue(minValue); | ||||
index = 0; | |||||
setIndex(0); | |||||
} | } | ||||
}; | }; | ||||
@@ -218,21 +253,20 @@ struct Port : OpaqueWidget { | |||||
void onDragLeave(Widget *origin); | void onDragLeave(Widget *origin); | ||||
}; | }; | ||||
struct SpritePort : Port, SpriteWidget { | |||||
void draw(NVGcontext *vg) { | |||||
Port::draw(vg); | |||||
SpriteWidget::draw(vg); | |||||
} | |||||
}; | |||||
struct SVGPort : Port, FramebufferWidget { | struct SVGPort : Port, FramebufferWidget { | ||||
SVGWidget *sw; | |||||
SVGWidget *background; | |||||
SVGPort(); | SVGPort(); | ||||
void setSVG(std::shared_ptr<SVG> svg); | |||||
void draw(NVGcontext *vg); | void draw(NVGcontext *vg); | ||||
}; | }; | ||||
/** If you don't add these to your ModuleWidget, they will fall out of the rack... */ | |||||
struct SVGScrew : FramebufferWidget { | |||||
SVGWidget *sw; | |||||
SVGScrew(); | |||||
}; | |||||
//////////////////// | //////////////////// | ||||
// scene | // scene | ||||
//////////////////// | //////////////////// | ||||
@@ -313,15 +313,17 @@ struct BefacoTinyKnob : SVGKnob { | |||||
} | } | ||||
}; | }; | ||||
struct BefacoSlidePot : SpriteKnob { | |||||
struct BefacoSlidePot : SVGSlider { | |||||
BefacoSlidePot() { | BefacoSlidePot() { | ||||
box.size = Vec(12, 122); | |||||
spriteOffset = Vec(-2, -6); | |||||
spriteSize = Vec(18, 134); | |||||
minIndex = 97; | |||||
maxIndex = 0; | |||||
spriteCount = 98; | |||||
spriteImage = Image::load("res/ComponentLibrary/BefacoSlidePot.png"); | |||||
Vec margin = Vec(3.5, 3.5); | |||||
maxHandlePos = Vec(-1, -2).plus(margin); | |||||
minHandlePos = Vec(-1, 87).plus(margin); | |||||
background->svg = SVG::load("res/ComponentLibrary/BefacoSlidePot.svg"); | |||||
background->wrap(); | |||||
background->box.pos = margin; | |||||
box.size = background->box.size.plus(margin.mult(2)); | |||||
handle->svg = SVG::load("res/ComponentLibrary/BefacoSlidePotHandle.svg"); | |||||
handle->wrap(); | |||||
} | } | ||||
}; | }; | ||||
@@ -329,33 +331,30 @@ struct BefacoSlidePot : SpriteKnob { | |||||
// Jacks | // Jacks | ||||
//////////////////// | //////////////////// | ||||
struct PJ301MPort : SpritePort { | |||||
struct PJ301MPort : SVGPort { | |||||
PJ301MPort() { | 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")); | |||||
padding = Vec(1, 1); | |||||
background->svg = SVG::load("res/ComponentLibrary/PJ301M.svg"); | |||||
background->wrap(); | |||||
box.size = background->box.size; | |||||
} | } | ||||
}; | }; | ||||
struct PJ3410Port : SpritePort { | |||||
struct PJ3410Port : SVGPort { | |||||
PJ3410Port() { | 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")); | |||||
padding = Vec(1, 1); | |||||
background->svg = SVG::load("res/ComponentLibrary/PJ3410.svg"); | |||||
background->wrap(); | |||||
box.size = background->box.size; | |||||
} | } | ||||
}; | }; | ||||
struct CL1362Port : SpritePort { | |||||
struct CL1362Port : SVGPort { | |||||
CL1362Port() { | 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")); | |||||
padding = Vec(1, 1); | |||||
background->svg = SVG::load("res/ComponentLibrary/CL1362.svg"); | |||||
background->wrap(); | |||||
box.size = background->box.size; | |||||
} | } | ||||
}; | }; | ||||
@@ -417,45 +416,41 @@ struct SmallLight : BASE { | |||||
// Switches | // Switches | ||||
//////////////////// | //////////////////// | ||||
// struct BefacoBigKnob : { | |||||
// BefacoBigKnob() { | |||||
// box.size = Vec(75, 75); | |||||
// minAngle = -0.75*M_PI; | |||||
// maxAngle = 0.75*M_PI; | |||||
// setSVG(SVG::load("res/ComponentLibrary/BefacoBigKnob.svg")); | |||||
// } | |||||
// }; | |||||
struct NKK : SVGSwitch, ToggleSwitch { | |||||
NKK() { | |||||
addFrame(SVG::load("res/ComponentLibrary/NKK0.svg")); | |||||
addFrame(SVG::load("res/ComponentLibrary/NKK1.svg")); | |||||
addFrame(SVG::load("res/ComponentLibrary/NKK2.svg")); | |||||
sw->wrap(); | |||||
box.size = sw->box.size; | |||||
} | |||||
}; | |||||
//////////////////// | //////////////////// | ||||
// Misc | // Misc | ||||
//////////////////// | //////////////////// | ||||
/** If you don't add these to your ModuleWidget, it will fall out of the rack... */ | |||||
struct Screw : SpriteWidget { | |||||
Screw() { | |||||
box.size = Vec(15, 14); | |||||
spriteOffset = Vec(0, 0); | |||||
spriteSize = Vec(15, 14); | |||||
} | |||||
}; | |||||
struct BlackScrew : Screw { | |||||
BlackScrew() { | |||||
spriteImage = Image::load("res/ComponentLibrary/ScrewBlack.png"); | |||||
struct ScrewSilver : SVGScrew { | |||||
ScrewSilver() { | |||||
sw->svg = SVG::load("res/ComponentLibrary/ScrewSilver.svg"); | |||||
sw->wrap(); | |||||
box.size = sw->box.size; | |||||
} | } | ||||
}; | }; | ||||
struct SilverScrew : Screw { | |||||
SilverScrew() { | |||||
spriteImage = Image::load("res/ComponentLibrary/ScrewSilver.png"); | |||||
struct ScrewBlack : SVGScrew { | |||||
ScrewBlack() { | |||||
sw->svg = SVG::load("res/ComponentLibrary/ScrewBlack.svg"); | |||||
sw->wrap(); | |||||
box.size = sw->box.size; | |||||
} | } | ||||
}; | }; | ||||
struct LightPanel : Panel { | struct LightPanel : Panel { | ||||
LightPanel() { | LightPanel() { | ||||
backgroundColor = nvgRGB(0xf2, 0xf2, 0xf2); | |||||
borderColor = nvgRGB(0xb8, 0xb8, 0xb8); | |||||
backgroundColor = nvgRGB(0xe8, 0xe8, 0xe8); | |||||
borderColor = nvgRGB(0xac, 0xac, 0xac); | |||||
} | } | ||||
}; | }; | ||||
@@ -191,6 +191,9 @@ struct Rect { | |||||
Rect() {} | Rect() {} | ||||
Rect(Vec pos, Vec size) : pos(pos), size(size) {} | Rect(Vec pos, Vec size) : pos(pos), size(size) {} | ||||
static Rect fromMinMax(Vec min, Vec max) { | |||||
return Rect(min, max.minus(min)); | |||||
} | |||||
/** Returns whether this Rect contains another Rect, inclusive on the top/left, non-inclusive on the bottom/right */ | /** Returns whether this Rect contains another Rect, inclusive on the top/left, non-inclusive on the bottom/right */ | ||||
bool contains(Vec v) { | bool contains(Vec v) { | ||||
@@ -69,8 +69,8 @@ Port *createOutput(Vec pos, Module *module, int outputId) { | |||||
} | } | ||||
template <class TScrew> | template <class TScrew> | ||||
Screw *createScrew(Vec pos) { | |||||
Screw *screw = new TScrew(); | |||||
Widget *createScrew(Vec pos) { | |||||
Widget *screw = new TScrew(); | |||||
screw->box.pos = pos; | screw->box.pos = pos; | ||||
return screw; | return screw; | ||||
} | } | ||||
@@ -195,7 +195,7 @@ struct FramebufferWidget : virtual Widget { | |||||
/** A margin in pixels around the children in the framebuffer | /** A margin in pixels around the children in the framebuffer | ||||
This prevents cutting the rendered SVG off on the box edges. | This prevents cutting the rendered SVG off on the box edges. | ||||
*/ | */ | ||||
int margin = 0; | |||||
Vec padding; | |||||
/** The root object in the framebuffer scene | /** The root object in the framebuffer scene | ||||
The FramebufferWidget owns the pointer | The FramebufferWidget owns the pointer | ||||
*/ | */ | ||||
@@ -6,7 +6,7 @@ | |||||
namespace rack { | namespace rack { | ||||
#define KNOB_SENSITIVITY 0.001 | |||||
#define KNOB_SENSITIVITY 0.0015 | |||||
void Knob::onDragStart() { | void Knob::onDragStart() { | ||||
@@ -21,6 +21,7 @@ void Port::disconnect() { | |||||
void Port::draw(NVGcontext *vg) { | void Port::draw(NVGcontext *vg) { | ||||
if (gRackWidget->activeWire) { | if (gRackWidget->activeWire) { | ||||
// Dim the Port if the active wire cannot plug into this Port | |||||
if (type == INPUT ? gRackWidget->activeWire->inputPort : gRackWidget->activeWire->outputPort) | if (type == INPUT ? gRackWidget->activeWire->inputPort : gRackWidget->activeWire->outputPort) | ||||
nvgGlobalAlpha(vg, 0.5); | nvgGlobalAlpha(vg, 0.5); | ||||
} | } | ||||
@@ -5,12 +5,13 @@ namespace rack { | |||||
SVGKnob::SVGKnob() { | SVGKnob::SVGKnob() { | ||||
margin = 4; | |||||
padding = Vec(1, 1); | |||||
shadow = new CircularShadow(); | shadow = new CircularShadow(); | ||||
shadow->blur = 5.0; | shadow->blur = 5.0; | ||||
shadow->box.pos = Vec(0, 1); | shadow->box.pos = Vec(0, 1); | ||||
addChild(shadow); | |||||
// TODO Remove shadow entirely | |||||
// addChild(shadow); | |||||
tw = new TransformWidget(); | tw = new TransformWidget(); | ||||
addChild(tw); | addChild(tw); | ||||
@@ -5,13 +5,10 @@ namespace rack { | |||||
SVGPort::SVGPort() { | SVGPort::SVGPort() { | ||||
sw = new SVGWidget(); | |||||
addChild(sw); | |||||
} | |||||
padding = Vec(1, 1); | |||||
void SVGPort::setSVG(std::shared_ptr<SVG> svg) { | |||||
sw->svg = svg; | |||||
sw->wrap(); | |||||
background = new SVGWidget(); | |||||
addChild(background); | |||||
} | } | ||||
void SVGPort::draw(NVGcontext *vg) { | void SVGPort::draw(NVGcontext *vg) { | ||||
@@ -0,0 +1,15 @@ | |||||
#include "app.hpp" | |||||
namespace rack { | |||||
SVGScrew::SVGScrew() { | |||||
padding = Vec(1, 1); | |||||
sw = new SVGWidget(); | |||||
addChild(sw); | |||||
} | |||||
} // namespace rack |
@@ -0,0 +1,30 @@ | |||||
#include "app.hpp" | |||||
namespace rack { | |||||
SVGSlider::SVGSlider() { | |||||
background = new SVGWidget(); | |||||
addChild(background); | |||||
handle = new SVGWidget(); | |||||
addChild(handle); | |||||
} | |||||
void SVGSlider::step() { | |||||
if (dirty) { | |||||
// Update handle position | |||||
Vec handlePos = Vec(mapf(value, minValue, maxValue, minHandlePos.x, maxHandlePos.x), mapf(value, minValue, maxValue, minHandlePos.y, maxHandlePos.y)); | |||||
handle->box.pos = handlePos; | |||||
} | |||||
FramebufferWidget::step(); | |||||
} | |||||
void SVGSlider::onChange() { | |||||
dirty = true; | |||||
ParamWidget::onChange(); | |||||
} | |||||
} // namespace rack |
@@ -0,0 +1,33 @@ | |||||
#include "app.hpp" | |||||
namespace rack { | |||||
SVGSwitch::SVGSwitch() { | |||||
padding = Vec(1, 1); | |||||
sw = new SVGWidget(); | |||||
addChild(sw); | |||||
} | |||||
void SVGSwitch::addFrame(std::shared_ptr<SVG> svg) { | |||||
frames.push_back(svg); | |||||
// Automatically set the frame as this SVG file. | |||||
// This allows us to wrap() the widget after calling | |||||
if (!sw->svg) | |||||
sw->svg = svg; | |||||
} | |||||
void SVGSwitch::setIndex(int index) { | |||||
if (0 <= index && index < (int)frames.size()) | |||||
sw->svg = frames[index]; | |||||
dirty = true; | |||||
} | |||||
void SVGSwitch::step() { | |||||
FramebufferWidget::step(); | |||||
} | |||||
} // namespace rack |
@@ -10,7 +10,6 @@ namespace rack { | |||||
struct FramebufferWidget::Internal { | struct FramebufferWidget::Internal { | ||||
NVGLUframebuffer *fb = NULL; | NVGLUframebuffer *fb = NULL; | ||||
Vec offset; | |||||
~Internal() { | ~Internal() { | ||||
setFramebuffer(NULL); | setFramebuffer(NULL); | ||||
@@ -31,22 +30,16 @@ FramebufferWidget::~FramebufferWidget() { | |||||
} | } | ||||
void FramebufferWidget::step() { | void FramebufferWidget::step() { | ||||
if (children.empty()) | |||||
return; | |||||
// Step children before rendering | // Step children before rendering | ||||
Widget::step(); | Widget::step(); | ||||
// Render the scene to the framebuffer if dirty | // Render the scene to the framebuffer if dirty | ||||
if (dirty) { | if (dirty) { | ||||
Rect childrenBox = getChildrenBoundingBox(); | |||||
assert(childrenBox.size.isFinite()); | |||||
int width = ceilf(childrenBox.size.x) + 2*margin; | |||||
int height = ceilf(childrenBox.size.y) + 2*margin; | |||||
internal->offset = childrenBox.pos.minus(Vec(margin, margin)); | |||||
Vec fbSize = box.size.plus(padding.mult(2)); | |||||
assert(fbSize.isFinite()); | |||||
internal->setFramebuffer(NULL); | internal->setFramebuffer(NULL); | ||||
NVGLUframebuffer *fb = nvgluCreateFramebuffer(gVg, width, height, NVG_IMAGE_REPEATX | NVG_IMAGE_REPEATY); | |||||
NVGLUframebuffer *fb = nvgluCreateFramebuffer(gVg, fbSize.x, fbSize.y, NVG_IMAGE_REPEATX | NVG_IMAGE_REPEATY); | |||||
if (!fb) | if (!fb) | ||||
return; | return; | ||||
internal->setFramebuffer(fb); | internal->setFramebuffer(fb); | ||||
@@ -54,12 +47,12 @@ void FramebufferWidget::step() { | |||||
// TODO Support screens with pixelRatio != 1.0 (e.g. Retina) by using the actual size of the framebuffer, etc. | // TODO Support screens with pixelRatio != 1.0 (e.g. Retina) by using the actual size of the framebuffer, etc. | ||||
const float pixelRatio = 1.0; | const float pixelRatio = 1.0; | ||||
nvgluBindFramebuffer(fb); | nvgluBindFramebuffer(fb); | ||||
glViewport(0.0, 0.0, width, height); | |||||
glViewport(0.0, 0.0, fbSize.x, fbSize.y); | |||||
glClearColor(0.0, 0.0, 0.0, 0.0); | glClearColor(0.0, 0.0, 0.0, 0.0); | ||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); | glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); | ||||
nvgBeginFrame(gVg, width, height, pixelRatio); | |||||
nvgBeginFrame(gVg, fbSize.x, fbSize.y, pixelRatio); | |||||
nvgTranslate(gVg, -internal->offset.x, -internal->offset.y); | |||||
nvgTranslate(gVg, padding.x, padding.y); | |||||
Widget::draw(gVg); | Widget::draw(gVg); | ||||
nvgEndFrame(gVg); | nvgEndFrame(gVg); | ||||
@@ -77,8 +70,8 @@ void FramebufferWidget::draw(NVGcontext *vg) { | |||||
int width, height; | int width, height; | ||||
nvgImageSize(vg, internal->fb->image, &width, &height); | nvgImageSize(vg, internal->fb->image, &width, &height); | ||||
nvgBeginPath(vg); | nvgBeginPath(vg); | ||||
nvgRect(vg, internal->offset.x, internal->offset.y, width, height); | |||||
NVGpaint paint = nvgImagePattern(vg, internal->offset.x, internal->offset.y, width, height, 0.0, internal->fb->image, 1.0); | |||||
nvgRect(vg, -padding.x, -padding.y, width, height); | |||||
NVGpaint paint = nvgImagePattern(vg, -padding.x, -padding.y, width, height, 0.0, internal->fb->image, 1.0); | |||||
nvgFillPaint(vg, paint); | nvgFillPaint(vg, paint); | ||||
nvgFill(vg); | nvgFill(vg); | ||||
@@ -19,7 +19,7 @@ static void drawSVG(NVGcontext *vg, NSVGimage *svg) { | |||||
DEBUG_ONLY(printf("new image: %g x %g px\n", svg->width, svg->height);) | DEBUG_ONLY(printf("new image: %g x %g px\n", svg->width, svg->height);) | ||||
int shapeIndex = 0; | int shapeIndex = 0; | ||||
for (NSVGshape *shape = svg->shapes; shape; shape = shape->next, shapeIndex++) { | 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);) | |||||
DEBUG_ONLY(printf(" new shape: %d id \"%s\", fillrule %d, from (%f, %f) to (%f, %f)\n", shapeIndex, shape->id, shape->fillRule, shape->bounds[0], shape->bounds[1], shape->bounds[2], shape->bounds[3]);) | |||||
if (!(shape->flags & NSVG_FLAGS_VISIBLE)) | if (!(shape->flags & NSVG_FLAGS_VISIBLE)) | ||||
continue; | continue; | ||||
@@ -29,14 +29,10 @@ static void drawSVG(NVGcontext *vg, NSVGimage *svg) { | |||||
if (shape->opacity < 1.0) | if (shape->opacity < 1.0) | ||||
nvgGlobalAlpha(vg, shape->opacity); | nvgGlobalAlpha(vg, shape->opacity); | ||||
nvgStrokeWidth(vg, shape->strokeWidth); | |||||
// strokeDashOffset, strokeDashArray, strokeDashCount not yet supported | |||||
// strokeLineJoin, strokeLineCap not yet supported | |||||
// Build path | // Build path | ||||
nvgBeginPath(vg); | nvgBeginPath(vg); | ||||
for (NSVGpath *path = shape->paths; path; path = path->next) { | for (NSVGpath *path = shape->paths; path; path = path->next) { | ||||
DEBUG_ONLY(printf(" new path: %d points, %s\n", path->npts, path->closed ? "closed" : "open");) | |||||
DEBUG_ONLY(printf(" new path: %d points, %s, from (%f, %f) to (%f, %f)\n", path->npts, path->closed ? "closed" : "open", path->bounds[0], path->bounds[1], path->bounds[2], path->bounds[3]);) | |||||
nvgMoveTo(vg, path->pts[0], path->pts[1]); | nvgMoveTo(vg, path->pts[0], path->pts[1]); | ||||
for (int i = 1; i < path->npts; i += 3) { | for (int i = 1; i < path->npts; i += 3) { | ||||
@@ -61,14 +57,38 @@ static void drawSVG(NVGcontext *vg, NSVGimage *svg) { | |||||
DEBUG_ONLY(printf(" fill color (%g, %g, %g, %g)\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; | } break; | ||||
case NSVG_PAINT_LINEAR_GRADIENT: { | case NSVG_PAINT_LINEAR_GRADIENT: { | ||||
// NSVGgradient *g = shape->fill.gradient; | |||||
// printf(" lin grad: %f\t%f\n", g->fx, g->fy); | |||||
NSVGgradient *g = shape->fill.gradient; | |||||
DEBUG_ONLY(printf(" linear gradient: %f\t%f\n", g->fx, g->fy);) | |||||
} break; | |||||
case NSVG_PAINT_RADIAL_GRADIENT: { | |||||
NSVGgradient *g = shape->fill.gradient; | |||||
DEBUG_ONLY(printf(" radial gradient: %f\t%f\t%f\t%f\t%f\t%f\t%f\t%f\n", g->fx, g->fy, g->xform[0], g->xform[1], g->xform[2], g->xform[3], g->xform[4], g->xform[5]);) | |||||
for (int i = 0; i < g->nstops; i++) { | |||||
DEBUG_ONLY(printf(" stop: #%08x\t%f\n", g->stops[i].color, g->stops[i].offset);) | |||||
} | |||||
assert(g->nstops >= 1); | |||||
NVGcolor color0 = getNVGColor(g->stops[0].color); | |||||
NVGcolor color1 = getNVGColor(g->stops[g->nstops - 1].color); | |||||
float inverse[6]; | |||||
Rect shapeBox = Rect::fromMinMax(Vec(shape->bounds[0], shape->bounds[1]), Vec(shape->bounds[2], shape->bounds[3])); | |||||
nvgTransformInverse(inverse, g->xform); | |||||
Vec c; | |||||
nvgTransformPoint(&c.x, &c.y, inverse, 5, 5); | |||||
printf("%f %f\n", c.x, c.y); | |||||
NVGpaint paint = nvgRadialGradient(vg, c.x, c.y, 0.0, 160, color0, color1); | |||||
nvgFillPaint(vg, paint); | |||||
} break; | } break; | ||||
} | } | ||||
nvgFill(vg); | nvgFill(vg); | ||||
} | } | ||||
// Stroke shape | // Stroke shape | ||||
nvgStrokeWidth(vg, shape->strokeWidth); | |||||
// strokeDashOffset, strokeDashArray, strokeDashCount not yet supported | |||||
// strokeLineJoin, strokeLineCap not yet supported | |||||
if (shape->stroke.type) { | if (shape->stroke.type) { | ||||
switch (shape->stroke.type) { | switch (shape->stroke.type) { | ||||
case NSVG_PAINT_COLOR: { | case NSVG_PAINT_COLOR: { | ||||
@@ -92,10 +112,12 @@ static void drawSVG(NVGcontext *vg, NSVGimage *svg) { | |||||
void SVGWidget::wrap() { | void SVGWidget::wrap() { | ||||
if (svg) | |||||
if (svg) { | |||||
box.size = Vec(svg->handle->width, svg->handle->height); | box.size = Vec(svg->handle->width, svg->handle->height); | ||||
else | |||||
} | |||||
else { | |||||
box.size = Vec(); | box.size = Vec(); | ||||
} | |||||
} | } | ||||
void SVGWidget::draw(NVGcontext *vg) { | void SVGWidget::draw(NVGcontext *vg) { | ||||