From c49ea7f0f3a8a8bd7f18bbe86eba69e7857cec5e Mon Sep 17 00:00:00 2001 From: Andrew Belt Date: Mon, 2 Aug 2021 16:46:46 -0400 Subject: [PATCH] Add Pyer's plug SVG to CableWidget. WIP cable appearance. --- res/ComponentLibrary/Plug.svg | 231 ++++++++++++++++++++++++++++++++++ src/app/CableWidget.cpp | 135 ++++++++++---------- 2 files changed, 303 insertions(+), 63 deletions(-) create mode 100644 res/ComponentLibrary/Plug.svg diff --git a/res/ComponentLibrary/Plug.svg b/res/ComponentLibrary/Plug.svg new file mode 100644 index 00000000..0f5632f9 --- /dev/null +++ b/res/ComponentLibrary/Plug.svg @@ -0,0 +1,231 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/app/CableWidget.cpp b/src/app/CableWidget.cpp index 147bf142..f6a8c043 100644 --- a/src/app/CableWidget.cpp +++ b/src/app/CableWidget.cpp @@ -15,6 +15,7 @@ namespace app { struct CableWidget::Internal { + std::shared_ptr plugSvg; std::shared_ptr plugPortSvg; }; @@ -22,6 +23,7 @@ struct CableWidget::Internal { CableWidget::CableWidget() { internal = new Internal; color = color::BLACK_TRANSPARENT; + internal->plugSvg = Svg::load(asset::system("res/ComponentLibrary/Plug.svg")); internal->plugPortSvg = Svg::load(asset::system("res/ComponentLibrary/PlugPort.svg")); } @@ -124,91 +126,96 @@ void CableWidget::fromJson(json_t* rootJ) { } } -static void CableWidget_drawPlug(CableWidget* that, const widget::Widget::DrawArgs& args, math::Vec pos, NVGcolor color, bool top) { +static math::Vec getCableSlump(math::Vec pos1, math::Vec pos2) { + float dist = pos1.minus(pos2).norm(); + math::Vec avg = pos1.plus(pos2).div(2); + // Lower average point as distance increases + avg.y += (1.0 - settings::cableTension) * (150.0 + 1.0 * dist); + return avg; +} + +static void CableWidget_drawPlug(CableWidget* that, const widget::Widget::DrawArgs& args, math::Vec pos, math::Vec slump, NVGcolor color, bool top) { if (!top) return; - NVGcolor colorOutline = nvgLerpRGBA(color, nvgRGBf(0.0, 0.0, 0.0), 0.5); nvgSave(args.vg); nvgTranslate(args.vg, pos.x, pos.y); - // Plug solid - nvgBeginPath(args.vg); - nvgCircle(args.vg, 0.0, 0.0, 9); - nvgFillColor(args.vg, color); - nvgFill(args.vg); - - // Border - nvgStrokeWidth(args.vg, 1.0); - nvgStrokeColor(args.vg, colorOutline); - nvgStroke(args.vg); + // Plug + nvgSave(args.vg); + nvgTint(args.vg, color); + std::shared_ptr plugSvg = that->internal->plugSvg; + math::Vec plugSize = plugSvg->getSize(); + float angle = slump.minus(pos).arg() - 0.5f * M_PI; + nvgRotate(args.vg, angle); + nvgTranslate(args.vg, VEC_ARGS(plugSize.div(2).neg())); + plugSvg->draw(args.vg); + nvgRestore(args.vg); // Port + nvgSave(args.vg); std::shared_ptr plugPortSvg = that->internal->plugPortSvg; math::Vec plugPortSize = plugPortSvg->getSize(); nvgTranslate(args.vg, VEC_ARGS(plugPortSize.div(2).neg())); plugPortSvg->draw(args.vg); + nvgRestore(args.vg); nvgRestore(args.vg); } -static void CableWidget_drawCable(CableWidget* that, const widget::Widget::DrawArgs& args, math::Vec pos1, math::Vec pos2, NVGcolor color, float thickness, float tension, float opacity) { - NVGcolor colorShadow = nvgRGBAf(0, 0, 0, 0.10); - NVGcolor colorOutline = nvgLerpRGBA(color, nvgRGBf(0.0, 0.0, 0.0), 0.5); +static void CableWidget_drawCable(CableWidget* that, const widget::Widget::DrawArgs& args, math::Vec pos1, math::Vec pos2, NVGcolor color, bool thick, float opacity) { + if (opacity <= 0.0) + return; - // Cable - if (opacity > 0.0) { - nvgSave(args.vg); - // This power scaling looks more linear than actual linear scaling - nvgAlpha(args.vg, std::pow(opacity, 1.5)); - - float dist = pos1.minus(pos2).norm(); - math::Vec slump; - slump.y = (1.0 - tension) * (150.0 + 1.0 * dist); - math::Vec pos3 = pos1.plus(pos2).div(2).plus(slump); - - // Adjust pos1 and pos2 to not draw over the plug - pos1 = pos1.plus(pos3.minus(pos1).normalize().mult(9)); - pos2 = pos2.plus(pos3.minus(pos2).normalize().mult(9)); - - nvgLineJoin(args.vg, NVG_ROUND); - - // Shadow - math::Vec pos4 = pos3.plus(slump.mult(0.08)); - nvgBeginPath(args.vg); - nvgMoveTo(args.vg, pos1.x, pos1.y); - nvgQuadTo(args.vg, pos4.x, pos4.y, pos2.x, pos2.y); - nvgStrokeColor(args.vg, colorShadow); - nvgStrokeWidth(args.vg, thickness); - nvgStroke(args.vg); - - // Cable outline - nvgBeginPath(args.vg); - nvgMoveTo(args.vg, pos1.x, pos1.y); - nvgQuadTo(args.vg, pos3.x, pos3.y, pos2.x, pos2.y); - nvgStrokeColor(args.vg, colorOutline); - nvgStrokeWidth(args.vg, thickness); - nvgStroke(args.vg); - - // Cable solid - nvgStrokeColor(args.vg, color); - nvgStrokeWidth(args.vg, thickness - 2); - nvgStroke(args.vg); + float thickness = thick ? 10.0 : 6.0; - nvgRestore(args.vg); - } + // The endpoints are off-center + math::Vec slump = getCableSlump(pos1, pos2); + pos1 = pos1.plus(slump.minus(pos1).normalize().mult(13.0)); + pos2 = pos2.plus(slump.minus(pos2).normalize().mult(13.0)); + + nvgSave(args.vg); + nvgAlpha(args.vg, std::pow(opacity, 1.5)); + + nvgLineCap(args.vg, NVG_ROUND); + // Avoids glitches when cable is bent + nvgLineJoin(args.vg, NVG_ROUND); + + // Shadow + math::Vec shadowSlump = slump.plus(math::Vec(0, 30)); + nvgBeginPath(args.vg); + nvgMoveTo(args.vg, VEC_ARGS(pos1)); + nvgQuadTo(args.vg, VEC_ARGS(shadowSlump), VEC_ARGS(pos2)); + NVGcolor shadowColor = nvgRGBAf(0, 0, 0, 0.10); + nvgStrokeColor(args.vg, shadowColor); + nvgStrokeWidth(args.vg, thickness - 1.0); + nvgStroke(args.vg); + + // Cable solid + nvgBeginPath(args.vg); + nvgMoveTo(args.vg, VEC_ARGS(pos1)); + nvgQuadTo(args.vg, VEC_ARGS(slump), VEC_ARGS(pos2)); + // nvgStrokePaint(args.vg, nvgLinearGradient(args.vg, VEC_ARGS(pos1), VEC_ARGS(pos2), color::mult(color, 0.5), color)); + nvgStrokeColor(args.vg, color::mult(color, 0.75)); + nvgStrokeWidth(args.vg, thickness); + nvgStroke(args.vg); + + nvgStrokeColor(args.vg, color); + nvgStrokeWidth(args.vg, thickness - 1.0); + nvgStroke(args.vg); + + nvgRestore(args.vg); } void CableWidget::draw(const DrawArgs& args) { float opacity = settings::cableOpacity; - float tension = settings::cableTension; - float thickness = 5; + bool thick = false; if (isComplete()) { engine::Output* output = &cable->outputModule->outputs[cable->outputId]; // Increase thickness if output port is polyphonic if (output->channels > 1) { - thickness = 9; + thick = true; } // Draw opaque if mouse is hovering over a connected port @@ -228,14 +235,17 @@ void CableWidget::draw(const DrawArgs& args) { math::Vec outputPos = getOutputPos(); math::Vec inputPos = getInputPos(); - CableWidget_drawCable(this, args, outputPos, inputPos, color, thickness, tension, opacity); + CableWidget_drawCable(this, args, outputPos, inputPos, color, thick, opacity); } void CableWidget::drawPlugs(const DrawArgs& args) { - // Draw output plug math::Vec outputPos = getOutputPos(); + math::Vec inputPos = getInputPos(); + math::Vec slump = getCableSlump(outputPos, inputPos); + + // Draw output plug bool outputTop = !isComplete() || APP->scene->rack->getTopCable(outputPort) == this; - CableWidget_drawPlug(this, args, outputPos, color, outputTop); + CableWidget_drawPlug(this, args, outputPos, slump, color, outputTop); if (outputTop && isComplete()) { // Draw output plug light nvgSave(args.vg); @@ -247,9 +257,8 @@ void CableWidget::drawPlugs(const DrawArgs& args) { } // Draw input plug - math::Vec inputPos = getInputPos(); bool inputTop = !isComplete() || APP->scene->rack->getTopCable(inputPort) == this; - CableWidget_drawPlug(this, args, inputPos, color, inputTop); + CableWidget_drawPlug(this, args, inputPos, slump, color, inputTop); if (inputTop && isComplete()) { // Draw input plug light nvgSave(args.vg);