Last active
September 10, 2022 16:24
-
-
Save jasoncoon/435f8f52dce7fb5287b01de0faca4859 to your computer and use it in GitHub Desktop.
Evil Eye pattern for Lux Lavalier
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Evil Eye 2D - 9/7/2022 | |
* Jason Coon (Evil Genius Labs) | |
* Video demo: https://twitter.com/jasoncoon_/status/1567671615214731264 | |
* | |
* Slight modification of Blinky Eyes 2D - 6/5/2022 | |
* Debra Ansell (GeekMomProjects) | |
* | |
* Simple code to display 1 blinking eye on an LED matrix. Tested on a Lux Lavalier (40mm Fibonacci64 Micro). | |
* Fine tuning variable values for the best look will likely depend a lot | |
* on the size and resolution of the specific display. | |
* */ | |
var neyes = 1; // showing 2 eyes is untested, doesn't work well on Lux Lavalier | |
var Amax = 0.95; // Maximum eye width (based on one eye, code will take care of shrinking for 2) | |
var Bmax = 0.5; // Maximum eye height (Using value of 0.25 for 1 eye, 0.20 for 2 eyes) | |
var Riris = 0.25; // Radius of the iris (using value of 0.16 for 1 eye, 0.12 for 2 eyes) | |
var lineThreshold = 0.25; // Controls thicknes of eye (ellipse) outline | |
var blink = false; // Are we in the middle of a blink? | |
var blinkPhase = 0; // How long have we been in this phase of blinking (value in seconds) | |
var blinkInterval = 6; // Time interval (in seconds) before the next blink | |
var blinkLength = 0.25; // Total time interval (in seconds) of the blink | |
var move = false; // Are the irises moving? (from center to side, then back again) | |
var movePhase = 0; // How long have we been in this phase of iris motion (value in seconds) | |
var moveDir = 1; // Which direction are the irises moving (either -1 or 1) | |
var moveInterval = 4; // Time interval (in seconds) before we move | |
var moveLength = 0.3; // Total time interval (in seconds) for the eye movement | |
var midIris = 0; // Position of the middle of the iris relative to the middle of the eye | |
var midIrisY = 0; | |
var B; // Current eye outline (ellipse) height for use in the ellipse equation (changes during the blink phase) | |
var t1; | |
// Returns the Y value for an ellipse with the given parameters a, b in a standard ellipse equation | |
function ellipseY(x, a, b) { | |
return b * sqrt(1 - (x * x) / (a * a)); | |
} | |
export function beforeRender(delta) { | |
t1 = time(0.04); | |
// Update our motion timer | |
movePhase += delta / 1000; | |
if (move) { | |
if (movePhase > moveLength) { | |
// At the end of motion time interval | |
movePhase = 0; | |
move = false; | |
//randomize time to next movement | |
moveInterval = 1.6 + random(5.4); | |
} | |
// Position of iris is changing | |
midIris = (moveDir * (wave(movePhase) - 0.5)) / 2; | |
} else { | |
if (movePhase > moveInterval) { | |
// At the end of motionless time interval | |
movePhase = 0; | |
// Randomize direction of eye movement | |
moveDir = random(1) > 0.5 ? 1 : -1; | |
move = true; | |
} | |
// If not moving, iris position fixed in the center | |
midIris = 0; | |
} | |
blinkPhase += delta / 1000; | |
if (blink) { | |
if (blinkPhase > blinkLength) { | |
// At the end of blinking time interval | |
blinkPhase = 0; | |
blink = false; | |
// Randomize time to next blink | |
blinkInterval = 4 + random(4); | |
} else { | |
B = clamp(B * wave(0.5 + blinkPhase / (2 * blinkLength)), 0.05, Bmax); | |
} | |
} else { | |
//Not blinking | |
// B is at maximum value unless we are blinking | |
B = Bmax; | |
if (blinkPhase > blinkInterval) { | |
// At the end of non-blinking time interval | |
blinkPhase = 0; // Reset clock | |
blink = true; // Start blink mode | |
} | |
} | |
} | |
export function render2D(index, x0, y0) { | |
// Rescale coordinates to (0,0) in center of screen. X scale depends on whether we have 1 or 2 eyes | |
x = (x0 - 0.5) * neyes; | |
if (neyes > 1) { | |
x += x > 0 ? -0.5 : 0.5; | |
} | |
y = (y0 - 0.5) * neyes; | |
// Distance from center of iris to our pixel | |
dIris = hypot((midIris - x) / neyes, y); | |
// Distance to elipse line | |
dscale = hypot(x / Amax, y / B); | |
v = 0; | |
// Draw the iris if (a) not blinking (b) we're within the iris radius | |
if (!blink && dIris < Riris) { | |
// Draw iris | |
v = dIris / Riris; // dark center, getting brighter with increasing radius away from center of iris | |
v -= hypot(x, y); | |
v -= t1; | |
v *= v; | |
v = v % 1; | |
hsv(0, 1, v); | |
} else { | |
// Draw eye outline | |
v = dscale < 1 ? max(dscale - lineThreshold, lineThreshold) : 0; | |
val = v * v * v; | |
val *= val; | |
rgb(val, val, val); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment