| @@ -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) { | ||||