@@ -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) | |||