|
- #include <app/CableWidget.hpp>
- #include <widget/SvgWidget.hpp>
- #include <widget/TransformWidget.hpp>
- #include <app/Scene.hpp>
- #include <app/RackWidget.hpp>
- #include <app/ModuleWidget.hpp>
- #include <context.hpp>
- #include <asset.hpp>
- #include <settings.hpp>
- #include <engine/Engine.hpp>
- #include <engine/Port.hpp>
- #include <app/MultiLightWidget.hpp>
- #include <componentlibrary.hpp>
-
-
- namespace rack {
- namespace app {
-
-
- struct TintWidget : widget::Widget {
- NVGcolor color = color::WHITE;
- void draw(const DrawArgs& args) override {
- nvgTint(args.vg, color);
- Widget::draw(args);
- }
- };
-
-
- struct PlugLight : componentlibrary::TRedGreenBlueLight<app::MultiLightWidget> {
- PlugLight() {
- box.size = math::Vec(9, 9);
- }
- };
-
-
- struct PlugWidget : widget::Widget {
- float angle = 0.5f * M_PI;
- PortWidget* portWidget = NULL;
-
- widget::FramebufferWidget* fb;
- widget::TransformWidget* plugTransform;
- TintWidget* plugTint;
- widget::SvgWidget* plug;
-
- widget::SvgWidget* plugPort;
-
- app::MultiLightWidget* plugLight;
-
- PlugWidget() {
- fb = new widget::FramebufferWidget;
- addChild(fb);
-
- plugTransform = new widget::TransformWidget;
- fb->addChild(plugTransform);
-
- plugTint = new TintWidget;
- plugTransform->addChild(plugTint);
-
- plug = new widget::SvgWidget;
- plug->setSvg(window::Svg::load(asset::system("res/ComponentLibrary/Plug.svg")));
- plugTint->addChild(plug);
- plugTransform->setSize(plug->getSize());
- plugTransform->setPosition(plug->getSize().mult(-0.5));
- plugTint->setSize(plug->getSize());
-
- plugPort = new widget::SvgWidget;
- plugPort->setSvg(window::Svg::load(asset::system("res/ComponentLibrary/PlugPort.svg")));
- plugPort->setPosition(plugPort->getSize().mult(-0.5));
- fb->addChild(plugPort);
-
- plugLight = new PlugLight;
- plugLight->setPosition(plugLight->getSize().mult(-0.5));
- addChild(plugLight);
-
- setSize(plug->getSize());
- }
-
- void step() override {
- std::vector<float> values(3);
- if (portWidget && plugLight->isVisible()) {
- engine::Port* port = portWidget->getPort();
- if (port) {
- for (int i = 0; i < 3; i++) {
- values[i] = port->plugLights[i].getBrightness();
- }
- }
- }
- plugLight->setBrightnesses(values);
-
- Widget::step();
- }
-
- void setColor(NVGcolor color) {
- if (color::isEqual(color, plugTint->color))
- return;
- plugTint->color = color;
- fb->setDirty();
- }
-
- void setAngle(float angle) {
- if (angle == this->angle)
- return;
- this->angle = angle;
- plugTransform->identity();
- plugTransform->rotate(angle - 0.5f * M_PI, plug->getSize().div(2));
- fb->setDirty();
- }
-
- void setPortWidget(PortWidget* portWidget) {
- this->portWidget = portWidget;
- }
-
- void setTop(bool top) {
- plugLight->setVisible(top);
- }
- };
-
-
- struct CableWidget::Internal {
- };
-
-
- CableWidget::CableWidget() {
- internal = new Internal;
- color = color::BLACK_TRANSPARENT;
-
- inputPlug = new PlugWidget;
- addChild(inputPlug);
-
- outputPlug = new PlugWidget;
- addChild(outputPlug);
- }
-
-
- CableWidget::~CableWidget() {
- setCable(NULL);
- delete internal;
- }
-
-
- bool CableWidget::isComplete() {
- return outputPort && inputPort;
- }
-
-
- void CableWidget::updateCable() {
- if (cable) {
- APP->engine->removeCable(cable);
- delete cable;
- cable = NULL;
- }
- if (inputPort && outputPort) {
- cable = new engine::Cable;
- cable->inputModule = inputPort->module;
- cable->inputId = inputPort->portId;
- cable->outputModule = outputPort->module;
- cable->outputId = outputPort->portId;
- APP->engine->addCable(cable);
- }
- }
-
-
- void CableWidget::setCable(engine::Cable* cable) {
- if (this->cable) {
- APP->engine->removeCable(this->cable);
- delete this->cable;
- this->cable = NULL;
- }
- if (cable) {
- app::ModuleWidget* outputMw = APP->scene->rack->getModule(cable->outputModule->id);
- if (!outputMw)
- throw Exception("Cable cannot find output ModuleWidget %lld", (long long) cable->outputModule->id);
- outputPort = outputMw->getOutput(cable->outputId);
- if (!outputPort)
- throw Exception("Cable cannot find output port %d", cable->outputId);
-
- app::ModuleWidget* inputMw = APP->scene->rack->getModule(cable->inputModule->id);
- if (!inputMw)
- throw Exception("Cable cannot find input ModuleWidget %lld", (long long) cable->inputModule->id);
- inputPort = inputMw->getInput(cable->inputId);
- if (!inputPort)
- throw Exception("Cable cannot find input port %d", cable->inputId);
-
- this->cable = cable;
- }
- else {
- outputPort = NULL;
- inputPort = NULL;
- }
- }
-
-
- engine::Cable* CableWidget::getCable() {
- return cable;
- }
-
-
- math::Vec CableWidget::getInputPos() {
- if (inputPort) {
- return inputPort->getRelativeOffset(inputPort->box.zeroPos().getCenter(), APP->scene->rack);
- }
- else if (hoveredInputPort) {
- return hoveredInputPort->getRelativeOffset(hoveredInputPort->box.zeroPos().getCenter(), APP->scene->rack);
- }
- else {
- return APP->scene->rack->getMousePos();
- }
- }
-
-
- math::Vec CableWidget::getOutputPos() {
- if (outputPort) {
- return outputPort->getRelativeOffset(outputPort->box.zeroPos().getCenter(), APP->scene->rack);
- }
- else if (hoveredOutputPort) {
- return hoveredOutputPort->getRelativeOffset(hoveredOutputPort->box.zeroPos().getCenter(), APP->scene->rack);
- }
- else {
- return APP->scene->rack->getMousePos();
- }
- }
-
-
- void CableWidget::mergeJson(json_t* rootJ) {
- std::string s = color::toHexString(color);
- json_object_set_new(rootJ, "color", json_string(s.c_str()));
- }
-
-
- void CableWidget::fromJson(json_t* rootJ) {
- json_t* colorJ = json_object_get(rootJ, "color");
- if (colorJ && json_is_string(colorJ)) {
- color = color::fromHexString(json_string_value(colorJ));
- }
- else {
- // In <v0.6.0, cables used JSON objects instead of hex strings. Just ignore them if so and use the existing cable color.
- // In <=v1, cable colors were not serialized.
- color = APP->scene->rack->getNextCableColor();
- }
- }
-
-
- static math::Vec getSlumpPos(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;
- }
-
-
- void CableWidget::step() {
- math::Vec outputPos = getOutputPos();
- math::Vec inputPos = getInputPos();
- math::Vec slump = getSlumpPos(outputPos, inputPos);
-
- NVGcolor colorOpaque = color;
- colorOpaque.a = 1.f;
-
- // Draw output plug
- bool outputTop = !isComplete() || APP->scene->rack->getTopCable(outputPort) == this;
- outputPlug->setPosition(outputPos);
- outputPlug->setTop(outputTop);
- outputPlug->setAngle(slump.minus(outputPos).arg());
- outputPlug->setColor(colorOpaque);
- outputPlug->setPortWidget(outputPort);
-
- // Draw input plug
- bool inputTop = !isComplete() || APP->scene->rack->getTopCable(inputPort) == this;
- inputPlug->setPosition(inputPos);
- inputPlug->setTop(inputTop);
- inputPlug->setAngle(slump.minus(inputPos).arg());
- inputPlug->setColor(colorOpaque);
- inputPlug->setPortWidget(inputPort);
-
- Widget::step();
- }
-
-
- void CableWidget::draw(const DrawArgs& args) {
- // Draw plugs
- Widget::draw(args);
- }
-
-
- void CableWidget::drawLayer(const DrawArgs& args, int layer) {
- // Cable shadow and cable
- if (layer == 2 || layer == 3) {
- float opacity = settings::cableOpacity;
- bool thick = false;
-
- if (isComplete()) {
- engine::Output* output = &cable->outputModule->outputs[cable->outputId];
- // Increase thickness if output port is polyphonic
- if (output->isPolyphonic()) {
- thick = true;
- }
-
- // Draw opaque if mouse is hovering over a connected port
- Widget* hoveredWidget = APP->event->hoveredWidget;
- if (outputPort == hoveredWidget || inputPort == hoveredWidget) {
- opacity = 1.0;
- }
- // Draw translucent cable if not active (i.e. 0 channels)
- else if (output->getChannels() == 0) {
- opacity *= 0.5;
- }
- }
- else {
- // Draw opaque if the cable is incomplete
- opacity = 1.0;
- }
-
- if (opacity <= 0.0)
- return;
- nvgAlpha(args.vg, std::pow(opacity, 1.5));
-
- math::Vec outputPos = getOutputPos();
- math::Vec inputPos = getInputPos();
-
- float thickness = thick ? 9.0 : 6.0;
-
- // The endpoints are off-center
- math::Vec slump = getSlumpPos(outputPos, inputPos);
- outputPos = outputPos.plus(slump.minus(outputPos).normalize().mult(13.0));
- inputPos = inputPos.plus(slump.minus(inputPos).normalize().mult(13.0));
-
- nvgLineCap(args.vg, NVG_ROUND);
- // Avoids glitches when cable is bent
- nvgLineJoin(args.vg, NVG_ROUND);
-
- if (layer == 2) {
- // Draw cable shadow
- math::Vec shadowSlump = slump.plus(math::Vec(0, 30));
- nvgBeginPath(args.vg);
- nvgMoveTo(args.vg, VEC_ARGS(outputPos));
- nvgQuadTo(args.vg, VEC_ARGS(shadowSlump), VEC_ARGS(inputPos));
- NVGcolor shadowColor = nvgRGBAf(0, 0, 0, 0.10);
- nvgStrokeColor(args.vg, shadowColor);
- nvgStrokeWidth(args.vg, thickness - 1.0);
- nvgStroke(args.vg);
- }
- else if (layer == 3) {
- // Draw cable outline
- nvgBeginPath(args.vg);
- nvgMoveTo(args.vg, VEC_ARGS(outputPos));
- nvgQuadTo(args.vg, VEC_ARGS(slump), VEC_ARGS(inputPos));
- // nvgStrokePaint(args.vg, nvgLinearGradient(args.vg, VEC_ARGS(outputPos), VEC_ARGS(inputPos), color::mult(color, 0.5), color));
- nvgStrokeColor(args.vg, color::mult(color, 0.8));
- nvgStrokeWidth(args.vg, thickness);
- nvgStroke(args.vg);
-
- // Draw cable
- nvgStrokeColor(args.vg, color::mult(color, 0.95));
- nvgStrokeWidth(args.vg, thickness - 1.0);
- nvgStroke(args.vg);
- }
- }
-
- Widget::drawLayer(args, layer);
- }
-
-
- engine::Cable* CableWidget::releaseCable() {
- engine::Cable* cable = this->cable;
- this->cable = NULL;
- return cable;
- }
-
-
- } // namespace app
- } // namespace rack
|