Skip to content

Instantly share code, notes, and snippets.

@maggiben
Created November 20, 2013 04:32
Show Gist options
  • Save maggiben/7557762 to your computer and use it in GitHub Desktop.
Save maggiben/7557762 to your computer and use it in GitHub Desktop.
A Pen by Benjamin.
<div class="middle">
<canvas id="iris" class="stage" width="400" height="400"></canvas>
<h4 id="fps" class="fps"></h4>
<span id=""></span>
</div>

Iris

A 2D array of radial fibres connected by springs (thanks to Traer physics). When the fibres are stretched beyond a point, the springs break, creating openings in the fibres like in (some) human irises.

A Pen by Benjamin on CodePen.

License.

(function () {
var lastTime = 0;
var vendors = ['ms', 'moz', 'webkit', 'o'];
for (var x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) {
window.requestAnimationFrame = window[vendors[x] + 'RequestAnimationFrame'];
window.cancelAnimationFrame =
window[vendors[x] + 'CancelAnimationFrame'] || window[vendors[x] + 'CancelRequestAnimationFrame'];
}
if (!window.requestAnimationFrame)
window.requestAnimationFrame = function (callback, element) {
var currTime = new Date().getTime();
var timeToCall = Math.max(0, 16 - (currTime - lastTime));
var id = window.setTimeout(function () {
callback(currTime + timeToCall);
},
timeToCall);
lastTime = currTime + timeToCall;
return id;
};
if (!window.cancelAnimationFrame)
window.cancelAnimationFrame = function (id) {
clearTimeout(id);
};
}());
$(document).ready(function () {
Array.prototype.fill = function(x,n) {
"use strict"
if(typeof(n)=='undefined') {
var n = this.length;
}
while (n--) {
this[n] = x;
};
return this
}
Array.prototype.copy = function(src, sstart, dst, dstart, length) {
for(var i = sstart; i < sstart+length;i+=1 ) {
dst[dstart++] = src[i];
}
}
// Given : low <= high
// Returns : a random number in the range [low, high)
RandomRange = function(low, high) {
return Math.random()*(high-low) + low;
}
var recording;
var i, j, x, y;
var angle;
var irisRadius = 200;
var pupilFraction = 0.2;
var pupilRadius = irisRadius * pupilFraction;
var r;
var numFibres = 75;
var numCircles = 75 * (1 - pupilFraction); // fewer circles if the pupil is bigger
var storedradius = new Array(numCircles);
var SPRING_STRENGTH = 0.4;
var SPRING_DAMPING = 0.1;
var maxstretch;
var springnumber; var springnumber2; var springnumber3;
var springnumber4; var springnumber5; var springnumber6; var springnumber7;
var doRemoveSprings = true;
var initialNumSprings = 0;
var physics;
var particles;
var canvas = document.getElementById('iris');
var context = canvas.getContext('2d');
var width = canvas.width;
var height = canvas.height;
var isRunning = false;
var lastCalledTime;
var fps;
var setup = function() {
physics = new ParticleSystem(0.0, 0.1);
physics.setIntegrator = physics.EULER;
particles = new Array(numCircles);
for(i = 0; i < numCircles; i++) {
particles[i] = new Array(numFibres);
}
initialNumSprings = physics.numberOfSprings();
for (i=0; i<numCircles; i++){
r = pupilRadius + i*(irisRadius - pupilRadius)/numCircles;
storedradius[i] = r;
for (j = 0; j < numFibres; j++) {
angle = j * (Math.PI * 2) / numFibres;
x = Math.cos(angle) * r;
y = Math.sin(angle) * r;
particles[i][j] = physics.makeParticle(0.2, x, y, 0.0);
}
}
// make radial springs
for (i = 1; i < numCircles; i++) {
for (j = 0; j < numFibres; j++) {
physics.makeSpring(particles[i - 1][j], particles[i][j],
SPRING_STRENGTH,
SPRING_DAMPING,
(irisRadius - pupilRadius) / numCircles); // outer springs longer
}
}
// make concentric springs
for (i = 0; i < numCircles; i++) {
for (j = 1; j < numFibres; j++) {
physics.makeSpring(particles[i][j-1], particles[i][j],
RandomRange(0.01, 0.1) * SPRING_STRENGTH,
SPRING_DAMPING,
RandomRange(0.2, 0.4) * (Math.PI * 2) * storedradius[i] / numFibres * (0.8 + 0.2 * (1 - Math.sin(Math.PI * i / numCircles))));
}
}
// stich up last set concentric springs
for (i = 0; i < numCircles; i++) {
physics.makeSpring(particles[i][numFibres-1], particles[i][0],
RandomRange(0.01, 0.1) * SPRING_STRENGTH,
SPRING_DAMPING,
RandomRange(0.2, 0.4)* (Math.PI * 2) *storedradius[i] / numFibres * (0.8 + 0.2 * (1 - Math.sin(Math.PI * i / numCircles))));
}
for (j = 0; j < numFibres; j++) {
particles[0][j].makeFixed();
}
for (j=0; j < numFibres; j++) {
particles[numCircles-1][j].makeFixed();
}
}
var draw = function() {
// Store the current transformation matrix
context.save();
// Use the identity matrix while clearing the canvas
context.setTransform(1, 0, 0, 1, 0, 0);
context.clearRect(0, 0, canvas.width, canvas.height);
// Restore the transform
context.restore();
if(!lastCalledTime) {
lastCalledTime = new Date().getTime();
fps = 0;
}
delta = (new Date().getTime() - lastCalledTime)/1000;
lastCalledTime = new Date().getTime();
fps = Math.floor(1 / delta);
fpswrite()(fps);
if(isRunning)
requestAnimationFrame(draw);
physics.tick(1);
makeFibreLines();
if (doRemoveSprings){
removeSprings();
}
}
var makeFibreLines = function() {
context.lineWidth = "1";
context.strokeStyle = "#f1f1f1";
for (i=1; i < numCircles; i++){ // Go through the circles
for (j=0; j < numFibres; j++) { // Go through the radiating particles
// draw lines between all the radiating points
context.beginPath();
context.moveTo(width / 2, height / 2);
context.moveTo(particles[i][j].position.x + (width / 2), particles[i][j].position.y + (height / 2));
context.lineTo(particles[i-1][j].position.x + (width / 2), particles[i-1][j].position.y + (height / 2));
context.stroke();
}
}
for (j=1; j < numFibres; j++) { // Go through the radiating points
// draw a line from each outer point to the point next to it
context.beginPath();
context.moveTo(width / 2, height / 2);
context.moveTo(particles[numCircles-1][j].position.x + (width / 2), particles[numCircles-1][j].position.y + (height / 2));
context.lineTo(particles[numCircles-1][j-1].position.x + (width / 2), particles[numCircles-1][j-1].position.y + (height / 2));
context.stroke();
// draw a line from each inner point to the point next to it
context.beginPath();
context.moveTo(width / 2, height / 2);
context.moveTo(particles[0][j].position.x + (width / 2), particles[0][j].position.y + (height / 2));
context.lineTo(particles[0][j-1].position.x + (width / 2), particles[0][j-1].position.y + (height / 2));
context.stroke();
}
// For the first (outermost) radiating point
// connect the first and last points on the outer circle
context.beginPath();
context.moveTo(particles[numCircles-1][0].position.x + (width / 2), particles[numCircles-1][0].position.y + (height / 2));
context.lineTo(particles[numCircles-1][numFibres-1].position.x + (width / 2), particles[numCircles-1][numFibres-1].position.y + (height / 2));
context.stroke();
// connect the first and last points on the inner circle
context.beginPath();
context.moveTo(particles[0][numFibres-1].position.x + (width / 2), particles[0][numFibres-1].position.y + (height / 2));
context.lineTo(particles[0][0].position.x + (width / 2), particles[0][0].position.y + (height / 2));
context.stroke();
}
var fpswrite = (function (){
var el = document.getElementById("fps");
return function(fps) {
el.innerText = fps + "fps";
}
})
var removeSprings = function() { // function to create circular voids
maxstretch = 0.0;
if (physics.numberOfSprings() > (0.9 * initialNumSprings)) {
// If most of the original springs still exist
for (i = 0; i < physics.numberOfSprings(); i++) {
var S = physics.getSpring(i);
if (S.currentLength()/ S.restLength() > maxstretch) {
// springnumber7=springnumber6;
// springnumber6=springnumber5;
springnumber5 = springnumber4;
springnumber4 = springnumber3;
springnumber3 = springnumber2; // pass 2's value to 3
springnumber2 = springnumber; // pass the highest value to 2
springnumber = i;
maxstretch = S.currentLength() / S.restLength();
// make that most-stretched one 'springnumber'
}
}
physics.removeSpring(springnumber); // remove that spring
physics.removeSpring(springnumber2);
physics.removeSpring(springnumber3);
physics.removeSpring(springnumber4);
physics.removeSpring(springnumber5);
// physics.removeSpring(springnumber6);
// physics.removeSpring(springnumber7);
}
else {
return;
} // otherwise, stop removing springs
}
setup();
isRunning = true;
draw();
setTimeout(function(){
isRunning = false;
}, 10 * 30000);
document.onkeydown = function checkKey(event) {
event = event || window.event;
if (event.keyCode == '38') {
// up arrow
doRemoveSprings = true;
}
else if (event.keyCode == '40') {
// down arrow
doRemoveSprings = false;
}
}
});
html, body {
overflow: hidden;
}
body {
background-color: rgb(1, 1, 1);
}
.middle {
padding-top: 100px;
position: relative;
display: table;
margin: 0 auto;
}
.stage {
width:400px;
height:400px;
background-color: rgb(1, 1, 1);
display: block;
}
.fps {
color: #c0c0c0;
text-align: center;
display: none;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment