| @@ -277,6 +277,33 @@ struct MomentarySwitch : virtual Switch { | |||||
| } | } | ||||
| }; | }; | ||||
| //////////////////// | |||||
| // lights | |||||
| //////////////////// | |||||
| struct LightWidget : TransparentWidget { | |||||
| NVGcolor bgColor = nvgRGBf(0, 0, 0); | |||||
| NVGcolor color = nvgRGBf(1, 1, 1); | |||||
| void draw(NVGcontext *vg) override; | |||||
| }; | |||||
| /** Mixes a list of colors based on a list of brightness values */ | |||||
| struct MultiLightWidget : LightWidget { | |||||
| std::vector<NVGcolor> baseColors; | |||||
| void addBaseColor(NVGcolor baseColor); | |||||
| /** Sets the color to a linear combination of the baseColors with the given weights */ | |||||
| void setValues(const std::vector<float> &values); | |||||
| }; | |||||
| /** A MultiLightWidget that points to a module's Light or a range of lights | |||||
| Will access firstLightId, firstLightId + 1, etc. for each added color | |||||
| */ | |||||
| struct ModuleLightWidget : MultiLightWidget { | |||||
| Module *module = NULL; | |||||
| int firstLightId; | |||||
| void step() override; | |||||
| }; | |||||
| //////////////////// | //////////////////// | ||||
| // ports | // ports | ||||
| //////////////////// | //////////////////// | ||||
| @@ -290,8 +317,11 @@ struct Port : OpaqueWidget { | |||||
| Module *module = NULL; | Module *module = NULL; | ||||
| PortType type = INPUT; | PortType type = INPUT; | ||||
| int portId; | int portId; | ||||
| MultiLightWidget *plugLight; | |||||
| Port(); | |||||
| ~Port(); | ~Port(); | ||||
| void step() override; | |||||
| void draw(NVGcontext *vg) override; | void draw(NVGcontext *vg) override; | ||||
| void onMouseDown(EventMouseDown &e) override; | void onMouseDown(EventMouseDown &e) override; | ||||
| void onDragStart(EventDragStart &e) override; | void onDragStart(EventDragStart &e) override; | ||||
| @@ -315,29 +345,6 @@ struct SVGScrew : FramebufferWidget { | |||||
| SVGScrew(); | SVGScrew(); | ||||
| }; | }; | ||||
| //////////////////// | |||||
| // lights | |||||
| //////////////////// | |||||
| struct LightWidget : TransparentWidget { | |||||
| NVGcolor bgColor = nvgRGBf(0, 0, 0); | |||||
| NVGcolor color = nvgRGBf(1, 1, 1); | |||||
| void draw(NVGcontext *vg) override; | |||||
| }; | |||||
| /** A LightWidget that points to a module's Light or a range of lights */ | |||||
| struct ModuleLightWidget : LightWidget { | |||||
| Module *module = NULL; | |||||
| int lightId; | |||||
| }; | |||||
| /** Mixes colors based on the brightness of the module light at lightId, lightId + 1, etc */ | |||||
| struct ColorLightWidget : ModuleLightWidget { | |||||
| std::vector<NVGcolor> colors; | |||||
| void addColor(NVGcolor c); | |||||
| void step() override; | |||||
| }; | |||||
| //////////////////// | //////////////////// | ||||
| // scene | // scene | ||||
| //////////////////// | //////////////////// | ||||
| @@ -347,7 +354,6 @@ struct Toolbar : OpaqueWidget { | |||||
| Slider *wireTensionSlider; | Slider *wireTensionSlider; | ||||
| Slider *zoomSlider; | Slider *zoomSlider; | ||||
| RadioButton *cpuUsageButton; | RadioButton *cpuUsageButton; | ||||
| RadioButton *plugLightButton; | |||||
| Toolbar(); | Toolbar(); | ||||
| void draw(NVGcontext *vg) override; | void draw(NVGcontext *vg) override; | ||||
| @@ -368,43 +368,43 @@ struct CL1362Port : SVGPort { | |||||
| // Lights | // Lights | ||||
| //////////////////// | //////////////////// | ||||
| struct RedLight : ColorLightWidget { | |||||
| struct RedLight : ModuleLightWidget { | |||||
| RedLight() { | RedLight() { | ||||
| addColor(COLOR_RED); | |||||
| addBaseColor(COLOR_RED); | |||||
| } | } | ||||
| }; | }; | ||||
| struct GreenLight : ColorLightWidget { | |||||
| struct GreenLight : ModuleLightWidget { | |||||
| GreenLight() { | GreenLight() { | ||||
| addColor(COLOR_GREEN); | |||||
| addBaseColor(COLOR_GREEN); | |||||
| } | } | ||||
| }; | }; | ||||
| struct YellowLight : ColorLightWidget { | |||||
| struct YellowLight : ModuleLightWidget { | |||||
| YellowLight() { | YellowLight() { | ||||
| addColor(COLOR_YELLOW); | |||||
| addBaseColor(COLOR_YELLOW); | |||||
| } | } | ||||
| }; | }; | ||||
| struct BlueLight : ColorLightWidget { | |||||
| struct BlueLight : ModuleLightWidget { | |||||
| BlueLight() { | BlueLight() { | ||||
| addColor(COLOR_BLUE); | |||||
| addBaseColor(COLOR_BLUE); | |||||
| } | } | ||||
| }; | }; | ||||
| /** Reads two adjacent lightIds, so `lightId` and `lightId + 1` must be defined */ | /** Reads two adjacent lightIds, so `lightId` and `lightId + 1` must be defined */ | ||||
| struct GreenRedLight : ColorLightWidget { | |||||
| struct GreenRedLight : ModuleLightWidget { | |||||
| GreenRedLight() { | GreenRedLight() { | ||||
| addColor(COLOR_GREEN); | |||||
| addColor(COLOR_RED); | |||||
| addBaseColor(COLOR_GREEN); | |||||
| addBaseColor(COLOR_RED); | |||||
| } | } | ||||
| }; | }; | ||||
| struct RedGreenBlueLight : ColorLightWidget { | |||||
| struct RedGreenBlueLight : ModuleLightWidget { | |||||
| RedGreenBlueLight() { | RedGreenBlueLight() { | ||||
| addColor(COLOR_RED); | |||||
| addColor(COLOR_GREEN); | |||||
| addColor(COLOR_BLUE); | |||||
| addBaseColor(COLOR_RED); | |||||
| addBaseColor(COLOR_GREEN); | |||||
| addBaseColor(COLOR_BLUE); | |||||
| } | } | ||||
| }; | }; | ||||
| @@ -11,11 +11,22 @@ struct Param { | |||||
| float value = 0.0; | float value = 0.0; | ||||
| }; | }; | ||||
| struct Light { | |||||
| /** The square of the brightness value */ | |||||
| float value = 0.0; | |||||
| float getBrightness(); | |||||
| void setBrightness(float brightness) { | |||||
| value = brightness * brightness; | |||||
| } | |||||
| void setBrightnessSmooth(float brightness); | |||||
| }; | |||||
| struct Input { | struct Input { | ||||
| /** Voltage of the port, zero if not plugged in. Read-only by Module */ | /** Voltage of the port, zero if not plugged in. Read-only by Module */ | ||||
| float value = 0.0; | float value = 0.0; | ||||
| /** Whether a wire is plugged in */ | /** Whether a wire is plugged in */ | ||||
| bool active = false; | bool active = false; | ||||
| Light plugLights[2]; | |||||
| /** Returns the value if a wire is plugged in, otherwise returns the given default value */ | /** Returns the value if a wire is plugged in, otherwise returns the given default value */ | ||||
| float normalize(float normalValue) { | float normalize(float normalValue) { | ||||
| return active ? value : normalValue; | return active ? value : normalValue; | ||||
| @@ -27,16 +38,7 @@ struct Output { | |||||
| float value = 0.0; | float value = 0.0; | ||||
| /** Whether a wire is plugged in */ | /** Whether a wire is plugged in */ | ||||
| bool active = false; | bool active = false; | ||||
| }; | |||||
| struct Light { | |||||
| /** The square of the brightness value */ | |||||
| float value = 0.0; | |||||
| float getBrightness(); | |||||
| void setBrightness(float brightness) { | |||||
| value = brightness * brightness; | |||||
| } | |||||
| void setBrightnessSmooth(float brightness); | |||||
| Light plugLights[2]; | |||||
| }; | }; | ||||
| @@ -49,7 +51,7 @@ struct Module { | |||||
| float cpuTime = 0.0; | float cpuTime = 0.0; | ||||
| /** Deprecated, use constructor below this one */ | /** Deprecated, use constructor below this one */ | ||||
| Module() {} | |||||
| Module() DEPRECATED {} | |||||
| /** Constructs Module with a fixed number of params, inputs, and outputs */ | /** Constructs Module with a fixed number of params, inputs, and outputs */ | ||||
| Module(int numParams, int numInputs, int numOutputs, int numLights = 0) { | Module(int numParams, int numInputs, int numOutputs, int numLights = 0) { | ||||
| params.resize(numParams); | params.resize(numParams); | ||||
| @@ -74,11 +74,11 @@ Port *createOutput(Vec pos, Module *module, int outputId) { | |||||
| } | } | ||||
| template<class TModuleLightWidget> | template<class TModuleLightWidget> | ||||
| ModuleLightWidget *createLight(Vec pos, Module *module, int lightId) { | |||||
| ModuleLightWidget *createLight(Vec pos, Module *module, int firstLightId) { | |||||
| ModuleLightWidget *light = new TModuleLightWidget(); | ModuleLightWidget *light = new TModuleLightWidget(); | ||||
| light->box.pos = pos; | light->box.pos = pos; | ||||
| light->module = module; | light->module = module; | ||||
| light->lightId = lightId; | |||||
| light->firstLightId = firstLightId; | |||||
| return light; | return light; | ||||
| } | } | ||||
| @@ -1,27 +0,0 @@ | |||||
| #include "app.hpp" | |||||
| #include "engine.hpp" | |||||
| namespace rack { | |||||
| void ColorLightWidget::addColor(NVGcolor c) { | |||||
| colors.push_back(c); | |||||
| } | |||||
| void ColorLightWidget::step() { | |||||
| assert(module); | |||||
| assert(module->lights.size() >= lightId + colors.size()); | |||||
| color = nvgRGBf(0, 0, 0); | |||||
| for (int i = 0; i < (int)colors.size(); i++) { | |||||
| NVGcolor c = colors[i]; | |||||
| float brightness = module->lights[lightId + i].getBrightness(); | |||||
| brightness = clampf(brightness, 0.0, 1.0); | |||||
| color.r += c.r * brightness; | |||||
| color.g += c.g * brightness; | |||||
| color.b += c.b * brightness; | |||||
| } | |||||
| } | |||||
| } // namespace rack | |||||
| @@ -0,0 +1,22 @@ | |||||
| #include "app.hpp" | |||||
| #include "engine.hpp" | |||||
| namespace rack { | |||||
| void ModuleLightWidget::step() { | |||||
| assert(module); | |||||
| assert(module->lights.size() >= firstLightId + baseColors.size()); | |||||
| std::vector<float> values(baseColors.size()); | |||||
| for (size_t i = 0; i < baseColors.size(); i++) { | |||||
| float value = module->lights[firstLightId + i].getBrightness(); | |||||
| value = clampf(value, 0.0, 1.0); | |||||
| values[i] = value; | |||||
| } | |||||
| setValues(values); | |||||
| } | |||||
| } // namespace rack | |||||
| @@ -0,0 +1,24 @@ | |||||
| #include "app.hpp" | |||||
| namespace rack { | |||||
| void MultiLightWidget::addBaseColor(NVGcolor baseColor) { | |||||
| baseColors.push_back(baseColor); | |||||
| } | |||||
| void MultiLightWidget::setValues(const std::vector<float> &values) { | |||||
| assert(values.size() == baseColors.size()); | |||||
| color = nvgRGBf(0, 0, 0); | |||||
| for (size_t i = 0; i < baseColors.size(); i++) { | |||||
| NVGcolor c = baseColors[i]; | |||||
| float value = values[i]; | |||||
| color.r += c.r * value; | |||||
| color.g += c.g * value; | |||||
| color.b += c.b * value; | |||||
| } | |||||
| } | |||||
| } // namespace rack | |||||
| @@ -1,13 +1,45 @@ | |||||
| #include "app.hpp" | #include "app.hpp" | ||||
| #include "gui.hpp" | #include "gui.hpp" | ||||
| #include "components.hpp" | |||||
| #include "engine.hpp" | |||||
| namespace rack { | namespace rack { | ||||
| struct PlugLight : MultiLightWidget { | |||||
| PlugLight() { | |||||
| addBaseColor(COLOR_GREEN); | |||||
| addBaseColor(COLOR_RED); | |||||
| box.size = Vec(8, 8); | |||||
| bgColor = COLOR_BLACK_TRANSPARENT; | |||||
| } | |||||
| }; | |||||
| Port::Port() { | |||||
| plugLight = new PlugLight(); | |||||
| } | |||||
| Port::~Port() { | Port::~Port() { | ||||
| // plugLight is not a child and is thus owned by the Port, so we need to delete it here | |||||
| delete plugLight; | |||||
| gRackWidget->wireContainer->removeAllWires(this); | gRackWidget->wireContainer->removeAllWires(this); | ||||
| } | } | ||||
| void Port::step() { | |||||
| std::vector<float> values(2); | |||||
| if (type == INPUT) { | |||||
| values[0] = module->inputs[portId].plugLights[0].getBrightness(); | |||||
| values[1] = module->inputs[portId].plugLights[1].getBrightness(); | |||||
| } | |||||
| else { | |||||
| values[0] = module->outputs[portId].plugLights[0].getBrightness(); | |||||
| values[1] = module->outputs[portId].plugLights[1].getBrightness(); | |||||
| } | |||||
| plugLight->setValues(values); | |||||
| } | |||||
| void Port::draw(NVGcontext *vg) { | void Port::draw(NVGcontext *vg) { | ||||
| WireWidget *activeWire = gRackWidget->wireContainer->activeWire; | WireWidget *activeWire = gRackWidget->wireContainer->activeWire; | ||||
| if (activeWire) { | if (activeWire) { | ||||
| @@ -166,16 +166,6 @@ Toolbar::Toolbar() { | |||||
| } | } | ||||
| xPos += margin; | xPos += margin; | ||||
| { | |||||
| plugLightButton = new RadioButton(); | |||||
| plugLightButton->box.pos = Vec(xPos, margin); | |||||
| plugLightButton->box.size.x = 100; | |||||
| plugLightButton->label = "Plug lights"; | |||||
| addChild(plugLightButton); | |||||
| xPos += plugLightButton->box.size.x; | |||||
| } | |||||
| xPos += margin; | |||||
| /* | /* | ||||
| { | { | ||||
| cpuUsageButton = new RadioButton(); | cpuUsageButton = new RadioButton(); | ||||
| @@ -155,7 +155,9 @@ void WireWidget::draw(NVGcontext *vg) { | |||||
| if (!(inputPort && outputPort)) | if (!(inputPort && outputPort)) | ||||
| opacity = 1.0; | opacity = 1.0; | ||||
| drawWire(vg, getOutputPos(), getInputPos(), color, tension, opacity); | |||||
| Vec outputPos = getOutputPos(); | |||||
| Vec inputPos = getInputPos(); | |||||
| drawWire(vg, outputPos, inputPos, color, tension, opacity); | |||||
| } | } | ||||
| void WireWidget::drawPlugs(NVGcontext *vg) { | void WireWidget::drawPlugs(NVGcontext *vg) { | ||||
| @@ -166,31 +168,20 @@ void WireWidget::drawPlugs(NVGcontext *vg) { | |||||
| drawPlug(vg, inputPos, color); | drawPlug(vg, inputPos, color); | ||||
| // Draw plug light | // Draw plug light | ||||
| /* | |||||
| if (gToolbar->plugLightButton->value > 0.0) { | |||||
| if (wire) { | |||||
| Output &output = wire->outputModule->outputs[wire->outputId]; | |||||
| float value = output.value / 8.0; | |||||
| outputLight->box.size = Vec(10, 10); | |||||
| inputLight->box.size = Vec(10, 10); | |||||
| outputLight->box.pos = outputPos.minus(Vec(5, 5)); | |||||
| inputLight->box.pos = inputPos.minus(Vec(5, 5)); | |||||
| outputLight->setValue(value); | |||||
| inputLight->setValue(value); | |||||
| } | |||||
| else { | |||||
| outputLight->setValue(0.0); | |||||
| inputLight->setValue(0.0); | |||||
| } | |||||
| outputLight->visible = true; | |||||
| inputLight->visible = true; | |||||
| // TODO | |||||
| // Only draw this when light is on top of the plug stack | |||||
| if (outputPort) { | |||||
| nvgSave(vg); | |||||
| nvgTranslate(vg, outputPos.x - 4, outputPos.y - 4); | |||||
| outputPort->plugLight->draw(vg); | |||||
| nvgRestore(vg); | |||||
| } | } | ||||
| else { | |||||
| outputLight->visible = false; | |||||
| inputLight->visible = false; | |||||
| if (inputPort) { | |||||
| nvgSave(vg); | |||||
| nvgTranslate(vg, inputPos.x - 4, inputPos.y - 4); | |||||
| inputPort->plugLight->draw(vg); | |||||
| nvgRestore(vg); | |||||
| } | } | ||||
| */ | |||||
| Widget::draw(vg); | |||||
| } | } | ||||
| @@ -39,7 +39,7 @@ float Light::getBrightness() { | |||||
| } | } | ||||
| void Light::setBrightnessSmooth(float brightness) { | void Light::setBrightnessSmooth(float brightness) { | ||||
| float v = brightness * brightness; | |||||
| float v = (brightness > 0.0) ? brightness * brightness : 0.0; | |||||
| if (v < value) { | if (v < value) { | ||||
| // Fade out light with lambda = 2 * framerate | // Fade out light with lambda = 2 * framerate | ||||
| value += (v - value) * sampleTime * (60.0 * 2.0); | value += (v - value) * sampleTime * (60.0 * 2.0); | ||||
| @@ -87,6 +87,23 @@ static void engineStep() { | |||||
| // Step modules | // Step modules | ||||
| for (Module *module : modules) { | for (Module *module : modules) { | ||||
| module->step(); | module->step(); | ||||
| // TODO skip this step when plug lights are disabled | |||||
| // Step ports | |||||
| for (Input &input : module->inputs) { | |||||
| if (input.active) { | |||||
| float value = input.value / 10.0; | |||||
| input.plugLights[0].setBrightnessSmooth(value); | |||||
| input.plugLights[1].setBrightnessSmooth(-value); | |||||
| } | |||||
| } | |||||
| for (Output &output : module->outputs) { | |||||
| if (output.active) { | |||||
| float value = output.value / 10.0; | |||||
| output.plugLights[0].setBrightnessSmooth(value); | |||||
| output.plugLights[1].setBrightnessSmooth(-value); | |||||
| } | |||||
| } | |||||
| } | } | ||||
| // Step cables by moving their output values to inputs | // Step cables by moving their output values to inputs | ||||
| @@ -52,10 +52,6 @@ static json_t *settingsToJson() { | |||||
| json_t *sampleRateJ = json_real(engineGetSampleRate()); | json_t *sampleRateJ = json_real(engineGetSampleRate()); | ||||
| json_object_set_new(rootJ, "sampleRate", sampleRateJ); | json_object_set_new(rootJ, "sampleRate", sampleRateJ); | ||||
| // plugLight | |||||
| json_t *plugLightJ = json_boolean(gToolbar->plugLightButton->value > 0.0); | |||||
| json_object_set_new(rootJ, "plugLight", plugLightJ); | |||||
| // lastPath | // lastPath | ||||
| json_t *lastPathJ = json_string(gRackWidget->lastPath.c_str()); | json_t *lastPathJ = json_string(gRackWidget->lastPath.c_str()); | ||||
| json_object_set_new(rootJ, "lastPath", lastPathJ); | json_object_set_new(rootJ, "lastPath", lastPathJ); | ||||
| @@ -114,11 +110,6 @@ static void settingsFromJson(json_t *rootJ) { | |||||
| engineSetSampleRate(sampleRate); | engineSetSampleRate(sampleRate); | ||||
| } | } | ||||
| // plugLight | |||||
| json_t *plugLightJ = json_object_get(rootJ, "plugLight"); | |||||
| if (plugLightJ) | |||||
| gToolbar->plugLightButton->setValue(json_is_true(plugLightJ) ? 1.0 : 0.0); | |||||
| // lastPath | // lastPath | ||||
| json_t *lastPathJ = json_object_get(rootJ, "lastPath"); | json_t *lastPathJ = json_object_get(rootJ, "lastPath"); | ||||
| if (lastPathJ) | if (lastPathJ) | ||||