|  | //***********************************************************************************************
//Impromptu Modular: Modules for VCV Rack by Marc Boulé
//
//Based on code from Valley Rack Free by Dale Johnson
//See ./LICENSE.txt for all licenses
//***********************************************************************************************
#include "IMWidgets.hpp"
// Dynamic SVGScrew
ScrewCircle::ScrewCircle(float _angle) {
	static const float highRadius = 1.4f;// radius for 0 degrees (screw looks like a +)
	static const float lowRadius = 1.1f;// radius for 45 degrees (screw looks like an x)
	angle = _angle;
	_angle = fabs(angle - M_PI/4.0f);
	radius = ((highRadius - lowRadius)/(M_PI/4.0f)) * _angle + lowRadius;
}
void ScrewCircle::draw(NVGcontext *vg) {
	NVGcolor backgroundColor = nvgRGB(0x72, 0x72, 0x72); 
	NVGcolor borderColor = nvgRGB(0x72, 0x72, 0x72);
	nvgBeginPath(vg);
	nvgCircle(vg, box.size.x/2.0f, box.size.y/2.0f, radius);// box, radius
	nvgFillColor(vg, backgroundColor);
	nvgFill(vg);
	nvgStrokeWidth(vg, 1.0);
	nvgStrokeColor(vg, borderColor);
	nvgStroke(vg);
}
DynamicSVGScrew::DynamicSVGScrew() {
    mode = nullptr;
    oldMode = -1;
 
	
	// for random rotated screw used in primary mode (code copied from ImpromptuModular.cpp ScrewSilverRandomRot::ScrewSilverRandomRot())
	// **********
	float angle0_90 = randomUniform()*M_PI/2.0f;
	//float angle0_90 = randomUniform() > 0.5f ? M_PI/4.0f : 0.0f;// for testing
	
	tw = new TransformWidget();
	addChild(tw);
	
	sw = new SVGWidget();
	tw->addChild(sw);
	//sw->setSVG(SVG::load(assetPlugin(plugin, "res/Screw.svg")));
	sw->setSVG(SVG::load(assetGlobal("res/ComponentLibrary/ScrewSilver.svg")));
	
	sc = new ScrewCircle(angle0_90);
	sc->box.size = sw->box.size;
	tw->addChild(sc);
	
	box.size = sw->box.size;
	tw->box.size = sw->box.size; 
	tw->identity();
	// Rotate SVG
	Vec center = sw->box.getCenter();
	tw->translate(center);
	tw->rotate(angle0_90);
	tw->translate(center.neg());	
	// for fixed svg screw used in alternate mode
	// **********
	swAlt = new SVGWidget();
	swAlt->visible = false;
    addChild(swAlt);
}
void DynamicSVGScrew::addSVGalt(std::shared_ptr<SVG> svg) {
    if(!swAlt->svg) {
        swAlt->setSVG(svg);
    }
}
void DynamicSVGScrew::step() { // all code except middle if() from SVGPanel::step() in SVGPanel.cpp
    if (isNear(rack::global_ui->window.gPixelRatio, 1.0)) {
		// Small details draw poorly at low DPI, so oversample when drawing to the framebuffer
        oversample = 2.f;
    }
    if(mode != nullptr && *mode != oldMode) {
        if ((*mode) == 0) {
			sw->visible = true;
			swAlt->visible = false;
		}
		else {
			sw->visible = false;
			swAlt->visible = true;
		}
        oldMode = *mode;
        dirty = true;
    }
	FramebufferWidget::step();
}
// Dynamic SVGPanel
void PanelBorderWidget_Impromptu::draw(NVGcontext *vg) {  // carbon copy from SVGPanel.cpp
    NVGcolor borderColor = nvgRGBAf(0.5, 0.5, 0.5, 0.5);
    nvgBeginPath(vg);
    nvgRect(vg, 0.5, 0.5, box.size.x - 1.0, box.size.y - 1.0);// full rect of module (including expansion area if a module has one)
    nvgStrokeColor(vg, borderColor);
    nvgStrokeWidth(vg, 1.0);
    nvgStroke(vg);
	if (expWidth != nullptr && *expWidth != nullptr) {// add expansion division when pannel uses expansion area
		int expW = **expWidth;
		nvgBeginPath(vg);
		nvgMoveTo(vg, box.size.x - expW, 1);
		nvgLineTo(vg, box.size.x - expW, box.size.y - 1.0);
		nvgStrokeWidth(vg, 2.0);
		nvgStroke(vg);
	}
}
DynamicSVGPanel::DynamicSVGPanel() {
    mode = nullptr;
    oldMode = -1;
	expWidth = nullptr;
    visiblePanel = new SVGWidget();
    addChild(visiblePanel);
    border = new PanelBorderWidget_Impromptu();
	border->expWidth = &expWidth;
    addChild(border);
}
void DynamicSVGPanel::addPanel(std::shared_ptr<SVG> svg) {
    panels.push_back(svg);
    if(!visiblePanel->svg) {
        visiblePanel->setSVG(svg);
        box.size = visiblePanel->box.size.div(RACK_GRID_SIZE).round().mult(RACK_GRID_SIZE);
        border->box.size = box.size;
    }
}
void DynamicSVGPanel::step() { // all code except middle if() from SVGPanel::step() in SVGPanel.cpp
    if (isNear(rack::global_ui->window.gPixelRatio, 1.0)) {
		// Small details draw poorly at low DPI, so oversample when drawing to the framebuffer
        oversample = 2.f;
    }
    if(mode != nullptr && *mode != oldMode) {
        visiblePanel->setSVG(panels[*mode]);
        oldMode = *mode;
        dirty = true;
    }
	FramebufferWidget::step();
}
// Dynamic SVGPort
DynamicSVGPort::DynamicSVGPort() {
    mode = nullptr;
    oldMode = -1;
	//SVGPort constructor automatically called
}
void DynamicSVGPort::addFrame(std::shared_ptr<SVG> svg) {
    frames.push_back(svg);
    if(!background->svg)
        SVGPort::setSVG(svg);
}
void DynamicSVGPort::step() {
    if (isNear(rack::global_ui->window.gPixelRatio, 1.0)) {
		// Small details draw poorly at low DPI, so oversample when drawing to the framebuffer
        oversample = 2.f;
    }
    if(mode != nullptr && *mode != oldMode) {
        background->setSVG(frames[*mode]);
        oldMode = *mode;
        dirty = true;
    }
	Port::step();
}
// Dynamic SVGSwitch
DynamicSVGSwitch::DynamicSVGSwitch() {
    mode = nullptr;
    oldMode = -1;
	//SVGSwitch constructor automatically called
}
void DynamicSVGSwitch::addFrameAll(std::shared_ptr<SVG> svg) {
    framesAll.push_back(svg);
	if (framesAll.size() == 2) {
		addFrame(framesAll[0]);
		addFrame(framesAll[1]);
	}
}
void DynamicSVGSwitch::step() {
    if (isNear(rack::global_ui->window.gPixelRatio, 1.0)) {
		// Small details draw poorly at low DPI, so oversample when drawing to the framebuffer
        oversample = 2.f;
    }
    if(mode != nullptr && *mode != oldMode) {
        if ((*mode) == 0) {
			frames[0]=framesAll[0];
			frames[1]=framesAll[1];
		}
		else {
			frames[0]=framesAll[2];
			frames[1]=framesAll[3];
		}
        oldMode = *mode;
		onChange(*(new EventChange()));// required because of the way SVGSwitch changes images, we only change the frames above.
		//dirty = true;// dirty is not sufficient when changing via frames assignments above (i.e. onChange() is required)
    }
}
// Dynamic SVGKnob
DynamicSVGKnob::DynamicSVGKnob() {
    mode = nullptr;
    oldMode = -1;
	effect = new SVGWidget();
	//SVGKnob constructor automatically called
}
void DynamicSVGKnob::addFrameAll(std::shared_ptr<SVG> svg) {
    framesAll.push_back(svg);
	if (framesAll.size() == 1) {
		setSVG(svg);
	}
}
void DynamicSVGKnob::addEffect(std::shared_ptr<SVG> svg) {
    effect->setSVG(svg);
	addChild(effect);
}
void DynamicSVGKnob::step() {
    if (isNear(rack::global_ui->window.gPixelRatio, 1.0)) {
		// Small details draw poorly at low DPI, so oversample when drawing to the framebuffer
        oversample = 2.f;
    }
    if(mode != nullptr && *mode != oldMode) {
        if ((*mode) == 0) {
			setSVG(framesAll[0]);
			effect->visible = false;
		}
		else {
			setSVG(framesAll[1]);
			effect->visible = true;
		}
        oldMode = *mode;
		dirty = true;
    }
	SVGKnob::step();
}
// Dynamic IMTactile
DynamicIMTactile::DynamicIMTactile() {
	snap = false;
	smooth = false;// must be false or else DynamicIMTactile::changeValue() call from module will crash Rack
	wider = nullptr;
	paramReadRequest = nullptr;
	oldWider = -1.0f;
	box.size = Vec(padWidth, padHeight);
}
void DynamicIMTactile::step() {
   if(wider != nullptr && *wider != oldWider) {
        if ((*wider) > 0.5f) {
			box.size = Vec(padWidthWide, padHeight);
		}
		else {
			box.size = Vec(padWidth, padHeight);
		}
        oldWider = *wider;
    }	
	if (paramReadRequest != nullptr) {
		float readVal = *paramReadRequest;
		if (readVal != -10.0f) {
			setValue(readVal);
			*paramReadRequest = -10.0f;
		}
	}
	FramebufferWidget::step();
}
void DynamicIMTactile::onDragStart(EventDragStart &e) {
	dragValue = value;
	dragY = rack::global_ui->app.gRackWidget->lastMousePos.y;
}
void DynamicIMTactile::onDragMove(EventDragMove &e) {
	float rangeValue = maxValue - minValue;// infinite not supported (not relevant)
	float newDragY = rack::global_ui->app.gRackWidget->lastMousePos.y;
	float delta = -(newDragY - dragY) * rangeValue / box.size.y;
	dragY = newDragY;
	dragValue += delta;
	float dragValueClamped = clamp2(dragValue, minValue, maxValue);
	if (snap)
		dragValueClamped = roundf(dragValueClamped);
	setValue(dragValueClamped);
}
void DynamicIMTactile::onMouseDown(EventMouseDown &e) {
	float val = rescale(e.pos.y, box.size.y, 0.0f , minValue, maxValue);
	if (snap)
		val = roundf(val);
	setValue(val);
	ParamWidget::onMouseDown(e);
}
//void DynamicIMTactile::changeValue(float newVal) {
//	setValue(newVal);
//}
 |