plinstrument:
{
calc:"
// (based on Swing1040RGBW)

#include	colorconv
#include	ledref
#include	strobe

// controls
cMod;
cTlt; cSpd; cDim; cSht; cR; cG; cB; cW; cPtn; cVel; cBgc; cBgl; cPix; cRst;

// parameters
pRTl;
pMod;

// constants
kPosY = 0.000;
kDia = 0.06;
kBaseWid = 1.10;
kBaseDep = 0.10;
kBaseHi = 0.085;
kCenterHi = 0.2;
kZBack = 0.04;
kZFront = -0.04;
kHeadWid = 1.080;
kHeadHi = 0.08;

kCnt = 2;
kSpread = Spread(kCnt, 0.5, 2);	// beam angle?
kPixs = 10;
kPixPitch = 0.1;
kSource = LEDRefConv(40, kDia, kLEDRefWatt_RGBW, kLEDRefDia_RGBW, kLEDRefSrc_RGBW);
kWhite = kLEDRefWhite_RGBW;


// protocol parameters
CENTER[1] = kCenterHi;
PAN;
TILT;
LIGHT;
SIMPLE;
SHAPE;
CTLS;

PRM=1;
CTL;


// tilt

kTilt = 205;	// (?)
kTiltSpeedMin = kTilt / 25.5;	// [deg/s]	(?)
kTiltSpeedMax = kTilt / 1.00;	// [deg/s]	(?)

gTilt;


TiltVal#
(
	(pRTl? -1 : 1) * (cTlt / 256 - 0.5)
);


TiltTime#
(
	dt = kTilt * TiltVal() - gTilt;
	(dt != 0)?	// change
	(
		ts = ParamSqr(kTiltSpeedMin, kTiltSpeedMax, 255, 0, cSpd);
		gTilt += LimitChange($0, dt, ts);
		1
	)
	:
	0
);


// shutter

kStrobeMin = 1;			// [Hz] (?)
kStrobeMax = 20;		// [Hz] (?)
kStrobeDur = 1 / 30;	// (?)

gShutter;
gStrobe[0];


ShutterTime#
(
	(pMod == 0 |
	 pMod == 2)?
	(
		gShutter = 1;
	)
	:
	(
		(cSht < 20)?	// none
		(
			gShutter = 1;
		)
		:	// strobe
		(
			ss = ParamSqr(kStrobeMin, kStrobeMax, 20, 255, cSht);
			StrobeProgress(gStrobe, $0 * ss, kStrobeDur * ss);
			gShutter = gStrobe.ef;
		);
	);
);


// dimmer

gDimmer;

DimmerTime#
(
	(pMod == 0 |
	 pMod == 2)?
	(
		gDimmer = 1;
	)
	:
	(
		gDimmer = cDim / 255;
	);
);


// color (foreground)

gColor;		// rgbw

ColorTime#
(
	(
		(pMod == 2)?
		(
			gColor = 0;
		)
		:
		(
			gColor = Vec(cR / 255, cG / 255, cB / 255, cW / 255);
		);
	);
);


// pix

gPix;	// rgbw

PixTime#
(
	(pMod < 2)?
	(
		@(i < kPixs)
		(
			gPix[i] = 0;
			++i;
		);
	)
	:
	(
		@(i < kPixs)
		(
			p = cPix[i];
			gPix[i] = Vec(p.cR / 255, p.cG / 255, p.cB / 255, p.cW / 255);
			++i;
		);
	);
);


// protocol methods

UPDATE#
(
	dir = TiltTime($0);
	DimmerTime($0);
	ShutterTime($0);
	ColorTime($0);
	PixTime($0);

	PRM?
	(
		cMod = pMod;
		CTLS = 1;

		@(i < kPixs)
		(
			l = LIGHT[i];
			l.pos[0] = kPixPitch * (i - (kPixs - 1) * 0.5);
			l.pos[1] = kPosY;
			l.pos[2] = kZFront;
			l.dia = kDia;
			l.cnt = kCnt;
			l.spdh = kSpread;
			l.spdv = kSpread;
			LIGHT[i] = l;
			++i;
		);

		// simplified
		sd = SIMPLE[0];
		sd.pos[2] = kZFront;
		sd.dia = kDia;
		sd.cnt = kCnt;
		sd.spdh = kSpread;
		sd.spdv = kSpread;
		sd.filh = kPixPitch * (kPixs - 1);
		sd.ssn = kPixs;
		SIMPLE[0] = sd;
	);

	(dir | PRM)?
	(
		PAN = 0;
		TILT = 90 + gTilt;
		SHAPE = MovingShape(kBaseWid, kBaseHi, kHeadWid, kHeadHi, kZFront, kZBack, kCenterHi, PAN, TILT, kBaseDep);
	);

	(
		// setup light
		d = kSource * gDimmer * gShutter;
		i = 0;
		@(i < kPixs)
		(
			// htp
			c = 0;	// (reset)
			p = gPix[i];
			c[0] = max(gColor[0], p[0]);
			c[1] = max(gColor[1], p[1]);
			c[2] = max(gColor[2], p[2]);
			c[3] = max(gColor[3], p[3]);
			c = RGBfromRGBW(c, kWhite);
			c = VecScale(c, d);

			l = LIGHT[i];
			l.color = c;
			LIGHT[i] = l;
			++i;

			// sum for simplified
			sc = VecAdd(sc, c);
		);

		// simplified
		sd = SIMPLE[0];
		sd.color = VecScale(sc, 1 / kPixs);
		sd.slv = SimpleOverlap(kPixPitch, spread);
		SIMPLE[0] = sd;
	);

	PRM = CTL = 0;
);

";


chmap:
"cTlt cSpd cR cG cB cW cRst",
"cTlt cSpd cDim cSht cR cG cB cW cPtn cVel cBgc cBgl cRst",
"cTlt cSpd cPix(cR cG cB cW)[10] cRst";


ctl:
{
	controls:
	{
		type:numeric;
		id:cTlt;
		name:Tilt;
		max:255;
		ini:128;
	},
	{
		type:numeric;
		id:cSpd;
		name:Speed;
		max:255;
	},
	{
		type:mode;
		id:cMod;
		modes:
		{
			controls:
			{
				type:numeric;
				id:cR;
				name:R;
				max:255;
				ini:255;
			},
			{
				type:numeric;
				id:cG;
				name:G;
				max:255;
				ini:255;
			},
			{
				type:numeric;
				id:cB;
				name:B;
				max:255;
				ini:255;
			},
			{
				type:numeric;
				id:cW;
				name:W;
				max:255;
				ini:255;
			};
		},
		{
			controls:
			{
				type:numeric;
				id:cDim;
				name:Dimmer;
				max:255;
				ini:255;
			},
			{
				type:numeric;
				id:cSht;
				name:Strobe;
				max:255;
			},
			{
				type:numeric;
				id:cR;
				name:R;
				max:255;
				ini:255;
			},
			{
				type:numeric;
				id:cG;
				name:G;
				max:255;
				ini:255;
			},
			{
				type:numeric;
				id:cB;
				name:B;
				max:255;
				ini:255;
			},
			{
				type:numeric;
				id:cW;
				name:W;
				max:255;
				ini:255;
			},
			{
				type:numeric;
				id:cPtn;
				name:Pattern;
				max:255;
			},
			{
				type:numeric;
				id:cVel;
				name:Velocity;
				max:255;
			},
			{
				type:numeric;
				id:cBgc;
				name:BgColor;
				max:255;
			},
			{
				type:numeric;
				id:cBgl;
				name:BgLight;
				max:255;
			};
		},
		{
			controls:
			{
				type:array;
				id:cPixs;
				nam:Pixel;
				label:Pixel;
				base:1;
				elemid:cPix;
				fixed:1;
				min:10;
				max:10;
				ini:10;
				controls:
				{
					type:numeric;
					id:cR;
					name:R;
					max:255;
					ini:255;
				},
				{
					type:numeric;
					id:cG;
					name:G;
					max:255;
					ini:255;
				},
				{
					type:numeric;
					id:cB;
					name:B;
					max:255;
					ini:255;
				},
				{
					type:numeric;
					id:cW;
					name:W;
					max:255;
					ini:255;
				};
			};
		};
	},
	{
		type:numeric;
		id:cRst;
		name:Reset;
		max:255;
	};
};


prm:
{
	controls:
	{
		type:check;
		id:pRTl;
		name:"-Tilt";
	},
	{
		type:selector;
		id:pMod;
		name:Mode;
		ini:0;
		items:
		{
			text:7ch;
			value:0;
		},
		{
			text:13ch;
			value:1;
		},
		{
			text:43ch;
			value:2;
		};
	};
};

};
