| @@ -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 | |||
| //////////////////// | |||
| @@ -290,8 +317,11 @@ struct Port : OpaqueWidget { | |||
| Module *module = NULL; | |||
| PortType type = INPUT; | |||
| int portId; | |||
| MultiLightWidget *plugLight; | |||
| Port(); | |||
| ~Port(); | |||
| void step() override; | |||
| void draw(NVGcontext *vg) override; | |||
| void onMouseDown(EventMouseDown &e) override; | |||
| void onDragStart(EventDragStart &e) override; | |||
| @@ -315,29 +345,6 @@ struct SVGScrew : FramebufferWidget { | |||
| 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 | |||
| //////////////////// | |||
| @@ -347,7 +354,6 @@ struct Toolbar : OpaqueWidget { | |||
| Slider *wireTensionSlider; | |||
| Slider *zoomSlider; | |||
| RadioButton *cpuUsageButton; | |||
| RadioButton *plugLightButton; | |||
| Toolbar(); | |||
| void draw(NVGcontext *vg) override; | |||
| @@ -368,43 +368,43 @@ struct CL1362Port : SVGPort { | |||
| // Lights | |||
| //////////////////// | |||
| struct RedLight : ColorLightWidget { | |||
| struct RedLight : ModuleLightWidget { | |||
| RedLight() { | |||
| addColor(COLOR_RED); | |||
| addBaseColor(COLOR_RED); | |||
| } | |||
| }; | |||
| struct GreenLight : ColorLightWidget { | |||
| struct GreenLight : ModuleLightWidget { | |||
| GreenLight() { | |||
| addColor(COLOR_GREEN); | |||
| addBaseColor(COLOR_GREEN); | |||
| } | |||
| }; | |||
| struct YellowLight : ColorLightWidget { | |||
| struct YellowLight : ModuleLightWidget { | |||
| YellowLight() { | |||
| addColor(COLOR_YELLOW); | |||
| addBaseColor(COLOR_YELLOW); | |||
| } | |||
| }; | |||
| struct BlueLight : ColorLightWidget { | |||
| struct BlueLight : ModuleLightWidget { | |||
| BlueLight() { | |||
| addColor(COLOR_BLUE); | |||
| addBaseColor(COLOR_BLUE); | |||
| } | |||
| }; | |||
| /** Reads two adjacent lightIds, so `lightId` and `lightId + 1` must be defined */ | |||
| struct GreenRedLight : ColorLightWidget { | |||
| struct GreenRedLight : ModuleLightWidget { | |||
| GreenRedLight() { | |||
| addColor(COLOR_GREEN); | |||
| addColor(COLOR_RED); | |||
| addBaseColor(COLOR_GREEN); | |||
| addBaseColor(COLOR_RED); | |||
| } | |||
| }; | |||
| struct RedGreenBlueLight : ColorLightWidget { | |||
| struct RedGreenBlueLight : ModuleLightWidget { | |||
| 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; | |||
| }; | |||
| 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 { | |||
| /** Voltage of the port, zero if not plugged in. Read-only by Module */ | |||
| float value = 0.0; | |||
| /** Whether a wire is plugged in */ | |||
| bool active = false; | |||
| Light plugLights[2]; | |||
| /** Returns the value if a wire is plugged in, otherwise returns the given default value */ | |||
| float normalize(float normalValue) { | |||
| return active ? value : normalValue; | |||
| @@ -27,16 +38,7 @@ struct Output { | |||
| float value = 0.0; | |||
| /** Whether a wire is plugged in */ | |||
| 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; | |||
| /** Deprecated, use constructor below this one */ | |||
| Module() {} | |||
| Module() DEPRECATED {} | |||
| /** Constructs Module with a fixed number of params, inputs, and outputs */ | |||
| Module(int numParams, int numInputs, int numOutputs, int numLights = 0) { | |||
| params.resize(numParams); | |||
| @@ -74,11 +74,11 @@ Port *createOutput(Vec pos, Module *module, int outputId) { | |||
| } | |||
| template<class TModuleLightWidget> | |||
| ModuleLightWidget *createLight(Vec pos, Module *module, int lightId) { | |||
| ModuleLightWidget *createLight(Vec pos, Module *module, int firstLightId) { | |||
| ModuleLightWidget *light = new TModuleLightWidget(); | |||
| light->box.pos = pos; | |||
| light->module = module; | |||
| light->lightId = lightId; | |||
| light->firstLightId = firstLightId; | |||
| 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 "gui.hpp" | |||
| #include "components.hpp" | |||
| #include "engine.hpp" | |||
| 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() { | |||
| // 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); | |||
| } | |||
| 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) { | |||
| WireWidget *activeWire = gRackWidget->wireContainer->activeWire; | |||
| if (activeWire) { | |||
| @@ -166,16 +166,6 @@ Toolbar::Toolbar() { | |||
| } | |||
| 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(); | |||
| @@ -155,7 +155,9 @@ void WireWidget::draw(NVGcontext *vg) { | |||
| if (!(inputPort && outputPort)) | |||
| 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) { | |||
| @@ -166,31 +168,20 @@ void WireWidget::drawPlugs(NVGcontext *vg) { | |||
| drawPlug(vg, inputPos, color); | |||
| // 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) { | |||
| float v = brightness * brightness; | |||
| float v = (brightness > 0.0) ? brightness * brightness : 0.0; | |||
| if (v < value) { | |||
| // Fade out light with lambda = 2 * framerate | |||
| value += (v - value) * sampleTime * (60.0 * 2.0); | |||
| @@ -87,6 +87,23 @@ static void engineStep() { | |||
| // Step modules | |||
| for (Module *module : modules) { | |||
| 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 | |||
| @@ -52,10 +52,6 @@ static json_t *settingsToJson() { | |||
| json_t *sampleRateJ = json_real(engineGetSampleRate()); | |||
| 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 | |||
| json_t *lastPathJ = json_string(gRackWidget->lastPath.c_str()); | |||
| json_object_set_new(rootJ, "lastPath", lastPathJ); | |||
| @@ -114,11 +110,6 @@ static void settingsFromJson(json_t *rootJ) { | |||
| 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 | |||
| json_t *lastPathJ = json_object_get(rootJ, "lastPath"); | |||
| if (lastPathJ) | |||