diff --git a/include/app/PortWidget.hpp b/include/app/PortWidget.hpp index e05a5014..8bbd036c 100644 --- a/include/app/PortWidget.hpp +++ b/include/app/PortWidget.hpp @@ -39,6 +39,7 @@ struct PortWidget : widget::OpaqueWidget { void onDragDrop(const DragDropEvent& e) override; void onDragEnter(const DragEnterEvent& e) override; void onDragLeave(const DragLeaveEvent& e) override; + void onContextDestroy(const ContextDestroyEvent& e) override; }; diff --git a/res/ComponentLibrary/PlugPort.svg b/res/ComponentLibrary/PlugPort.svg new file mode 100644 index 00000000..12af8572 --- /dev/null +++ b/res/ComponentLibrary/PlugPort.svg @@ -0,0 +1,213 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/app/CableWidget.cpp b/src/app/CableWidget.cpp index da18986d..7a68735d 100644 --- a/src/app/CableWidget.cpp +++ b/src/app/CableWidget.cpp @@ -4,6 +4,7 @@ #include #include #include +#include #include #include #include @@ -13,12 +14,20 @@ namespace rack { namespace app { +struct CableWidget::Internal { + std::shared_ptr plugPortSvg; +}; + + CableWidget::CableWidget() { + internal = new Internal; color = color::BLACK_TRANSPARENT; + internal->plugPortSvg = Svg::load(asset::system("res/ComponentLibrary/PlugPort.svg")); } CableWidget::~CableWidget() { setCable(NULL); + delete internal; } void CableWidget::setNextCableColor() { @@ -115,36 +124,43 @@ void CableWidget::fromJson(json_t* rootJ) { } } -static void drawPlug(NVGcontext* vg, math::Vec pos, NVGcolor color) { +static void CableWidget_drawPlug(CableWidget* that, const widget::Widget::DrawArgs& args, math::Vec pos, 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(vg); - nvgCircle(vg, pos.x, pos.y, 9); - nvgFillColor(vg, color); - nvgFill(vg); + nvgBeginPath(args.vg); + nvgCircle(args.vg, 0.0, 0.0, 9); + nvgFillColor(args.vg, color); + nvgFill(args.vg); // Border - nvgStrokeWidth(vg, 1.0); - nvgStrokeColor(vg, colorOutline); - nvgStroke(vg); - - // Hole - nvgBeginPath(vg); - nvgCircle(vg, pos.x, pos.y, 5); - nvgFillColor(vg, nvgRGBf(0.0, 0.0, 0.0)); - nvgFill(vg); + nvgStrokeWidth(args.vg, 1.0); + nvgStrokeColor(args.vg, colorOutline); + nvgStroke(args.vg); + + // Port + 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); } -static void drawCable(NVGcontext* vg, math::Vec pos1, math::Vec pos2, NVGcolor color, float thickness, float tension, float opacity) { +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); // Cable if (opacity > 0.0) { - nvgSave(vg); + nvgSave(args.vg); // This power scaling looks more linear than actual linear scaling - nvgGlobalAlpha(vg, std::pow(opacity, 1.5)); + nvgGlobalAlpha(args.vg, std::pow(opacity, 1.5)); float dist = pos1.minus(pos2).norm(); math::Vec slump; @@ -155,31 +171,31 @@ static void drawCable(NVGcontext* vg, math::Vec pos1, math::Vec pos2, NVGcolor c pos1 = pos1.plus(pos3.minus(pos1).normalize().mult(9)); pos2 = pos2.plus(pos3.minus(pos2).normalize().mult(9)); - nvgLineJoin(vg, NVG_ROUND); + nvgLineJoin(args.vg, NVG_ROUND); // Shadow math::Vec pos4 = pos3.plus(slump.mult(0.08)); - nvgBeginPath(vg); - nvgMoveTo(vg, pos1.x, pos1.y); - nvgQuadTo(vg, pos4.x, pos4.y, pos2.x, pos2.y); - nvgStrokeColor(vg, colorShadow); - nvgStrokeWidth(vg, thickness); - nvgStroke(vg); + 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(vg); - nvgMoveTo(vg, pos1.x, pos1.y); - nvgQuadTo(vg, pos3.x, pos3.y, pos2.x, pos2.y); - nvgStrokeColor(vg, colorOutline); - nvgStrokeWidth(vg, thickness); - nvgStroke(vg); + 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(vg, color); - nvgStrokeWidth(vg, thickness - 2); - nvgStroke(vg); + nvgStrokeColor(args.vg, color); + nvgStrokeWidth(args.vg, thickness - 2); + nvgStroke(args.vg); - nvgRestore(vg); + nvgRestore(args.vg); } } @@ -212,33 +228,36 @@ void CableWidget::draw(const DrawArgs& args) { math::Vec outputPos = getOutputPos(); math::Vec inputPos = getInputPos(); - drawCable(args.vg, outputPos, inputPos, color, thickness, tension, opacity); + CableWidget_drawCable(this, args, outputPos, inputPos, color, thickness, tension, opacity); } void CableWidget::drawPlugs(const DrawArgs& args) { + // Draw output plug math::Vec outputPos = getOutputPos(); - math::Vec inputPos = getInputPos(); - - // Draw plug if the cable is on top, or if the cable is incomplete - if (!isComplete() || APP->scene->rack->getTopCable(outputPort) == this) { - drawPlug(args.vg, outputPos, color); - if (isComplete()) { - // Draw plug light - nvgSave(args.vg); - nvgTranslate(args.vg, outputPos.x - 4, outputPos.y - 4); - outputPort->getPlugLight()->draw(args); - nvgRestore(args.vg); - } + bool outputTop = !isComplete() || APP->scene->rack->getTopCable(outputPort) == this; + CableWidget_drawPlug(this, args, outputPos, color, outputTop); + if (outputTop && isComplete()) { + // Draw output plug light + nvgSave(args.vg); + LightWidget* plugLight = outputPort->getPlugLight(); + math::Vec plugPos = outputPos.minus(plugLight->getSize().div(2)); + nvgTranslate(args.vg, VEC_ARGS(plugPos)); + plugLight->draw(args); + nvgRestore(args.vg); } - if (!isComplete() || APP->scene->rack->getTopCable(inputPort) == this) { - drawPlug(args.vg, inputPos, color); - if (isComplete()) { - nvgSave(args.vg); - nvgTranslate(args.vg, inputPos.x - 4, inputPos.y - 4); - inputPort->getPlugLight()->draw(args); - nvgRestore(args.vg); - } + // Draw input plug + math::Vec inputPos = getInputPos(); + bool inputTop = !isComplete() || APP->scene->rack->getTopCable(inputPort) == this; + CableWidget_drawPlug(this, args, inputPos, color, inputTop); + if (inputTop && isComplete()) { + // Draw input plug light + nvgSave(args.vg); + LightWidget* plugLight = inputPort->getPlugLight(); + math::Vec plugPos = inputPos.minus(plugLight->getSize().div(2)); + nvgTranslate(args.vg, VEC_ARGS(plugPos)); + plugLight->draw(args); + nvgRestore(args.vg); } } diff --git a/src/app/PortWidget.cpp b/src/app/PortWidget.cpp index b86e8dd9..2feb8715 100644 --- a/src/app/PortWidget.cpp +++ b/src/app/PortWidget.cpp @@ -63,36 +63,33 @@ struct PortTooltip : ui::Tooltip { }; -struct PlugLight : MultiLightWidget { - PlugLight() { - addBaseColor(componentlibrary::SCHEME_GREEN); - addBaseColor(componentlibrary::SCHEME_RED); - addBaseColor(componentlibrary::SCHEME_BLUE); - box.size = math::Vec(8, 8); - bgColor = componentlibrary::SCHEME_BLACK_TRANSPARENT; - } -}; - - struct PortWidget::Internal { ui::Tooltip* tooltip = NULL; - PlugLight* plugLight; + app::MultiLightWidget* plugLight; }; PortWidget::PortWidget() { internal = new Internal; + + using namespace componentlibrary; + struct PlugLight : TRedGreenBlueLight>> { + PlugLight() { + // fb->bypassed = true; + fb->oversample = 1.0; + } + }; internal->plugLight = new PlugLight; } PortWidget::~PortWidget() { + // The port shouldn't have any cables when destroyed, but just to make sure. + if (module) + APP->scene->rack->clearCablesOnPort(this); // HACK: In case onDragDrop() is called but not onLeave() afterwards... destroyTooltip(); // plugLight is not a child but owned by the PortWidget, so we need to delete it here delete internal->plugLight; - // The port shouldn't have any cables when destroyed, but just to make sure. - if (module) - APP->scene->rack->clearCablesOnPort(this); delete internal; } @@ -151,6 +148,7 @@ void PortWidget::step() { values[i] = module->inputs[portId].plugLights[i].getBrightness(); } internal->plugLight->setBrightnesses(values); + internal->plugLight->step(); Widget::step(); } @@ -336,6 +334,11 @@ void PortWidget::onDragLeave(const DragLeaveEvent& e) { } } +void PortWidget::onContextDestroy(const ContextDestroyEvent& e) { + internal->plugLight->onContextDestroy(e); + Widget::onContextDestroy(e); +} + } // namespace app } // namespace rack diff --git a/src/app/RackWidget.cpp b/src/app/RackWidget.cpp index e4be9799..4415dbd7 100644 --- a/src/app/RackWidget.cpp +++ b/src/app/RackWidget.cpp @@ -60,7 +60,11 @@ struct CableContainer : widget::TransparentWidget { for (widget::Widget* w : children) { CableWidget* cw = dynamic_cast(w); assert(cw); - cw->drawPlugs(args); + + DrawArgs childArgs = args; + // TODO Make clip box equal actual viewport + childArgs.clipBox = math::Rect::inf(); + cw->drawPlugs(childArgs); } Widget::draw(args); @@ -433,6 +437,7 @@ void RackWidget::removeModule(ModuleWidget* m) { bool RackWidget::requestModulePos(ModuleWidget* mw, math::Vec pos) { // Check intersection with other modules math::Rect mwBox = math::Rect(pos, mw->box.size); + mwBox.size.x -= 0.01; for (widget::Widget* w2 : moduleContainer->children) { // Don't intersect with self if (mw == w2) @@ -441,7 +446,9 @@ bool RackWidget::requestModulePos(ModuleWidget* mw, math::Vec pos) { if (!w2->visible) continue; // Check intersection - if (mwBox.intersects(w2->box)) + math::Rect w2Box = w2->box; + w2Box.size.x -= 0.01; + if (mwBox.intersects(w2Box)) return false; } diff --git a/src/engine/Module.cpp b/src/engine/Module.cpp index eec44d60..4cf2649e 100644 --- a/src/engine/Module.cpp +++ b/src/engine/Module.cpp @@ -315,16 +315,12 @@ static void Port_step(Port* that, float deltaTime) { } else if (that->channels == 1) { float v = that->getVoltage() / 10.f; - that->plugLights[0].setSmoothBrightness(v, deltaTime); - that->plugLights[1].setSmoothBrightness(-v, deltaTime); + that->plugLights[0].setSmoothBrightness(-v, deltaTime); + that->plugLights[1].setSmoothBrightness(v, deltaTime); that->plugLights[2].setBrightness(0.f); } else { - float v2 = 0.f; - for (int c = 0; c < that->channels; c++) { - v2 += std::pow(that->getVoltage(c), 2); - } - float v = std::sqrt(v2) / 10.f; + float v = that->getVoltageRMS() / 10.f; that->plugLights[0].setBrightness(0.f); that->plugLights[1].setBrightness(0.f); that->plugLights[2].setSmoothBrightness(v, deltaTime); diff --git a/src/widget/FramebufferWidget.cpp b/src/widget/FramebufferWidget.cpp index b057bcbf..eee25af6 100644 --- a/src/widget/FramebufferWidget.cpp +++ b/src/widget/FramebufferWidget.cpp @@ -105,6 +105,7 @@ void FramebufferWidget::step() { // Create a framebuffer if (internal->fbSize.isFinite() && !internal->fbSize.isZero()) { internal->fb = nvgluCreateFramebuffer(vg, internal->fbSize.x, internal->fbSize.y, 0); + // DEBUG("Created framebuffer of size (%f, %f)", VEC_ARGS(internal->fbSize)); } } if (!internal->fb) { diff --git a/src/widget/Widget.cpp b/src/widget/Widget.cpp index 78234d7d..f33fc983 100644 --- a/src/widget/Widget.cpp +++ b/src/widget/Widget.cpp @@ -268,15 +268,16 @@ void Widget::draw(const DrawArgs& args) { if (!args.clipBox.intersects(child->box)) continue; - DrawArgs childCtx = args; + DrawArgs childArgs = args; // Intersect child clip box with self - childCtx.clipBox = childCtx.clipBox.intersect(child->box); - childCtx.clipBox.pos = childCtx.clipBox.pos.minus(child->box.pos); + childArgs.clipBox = childArgs.clipBox.intersect(child->box); + // Offset clip box by child pos + childArgs.clipBox.pos = childArgs.clipBox.pos.minus(child->box.pos); nvgSave(args.vg); nvgTranslate(args.vg, child->box.pos.x, child->box.pos.y); - child->draw(childCtx); + child->draw(childArgs); #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wdeprecated-declarations"