HSF-2020-CFP-Website/grayscott.js
2019-10-12 22:07:48 +02:00

611 lines
16 KiB
JavaScript

/*
* Gray-Scott
*
* A solver of the Gray-Scott model of reaction diffusion.
*
* ©2012 pmneila.
* p.mneila at upm.es
*/
(function(){
// Canvas.
var canvas;
var canvasQ;
var canvasWidth;
var canvasHeight;
var mMouseX, mMouseY;
var mMouseDown = false;
var mRenderer;
var mScene;
var mCamera;
var mUniforms;
var mColors;
var mColorsNeedUpdate = true;
var mLastTime = 0;
var mTexture1, mTexture2;
var mGSMaterial, mScreenMaterial;
var mScreenQuad;
var mToggled = false;
var mMinusOnes = new THREE.Vector2(-1, -1);
// Some presets.
var presets = [
{
feed: 0.031,
kill: 0.057
},
{
feed: 0.031,
kill: 0.058
},
{
feed: 0.031,
kill: 0.060
},
{
feed: 0.033,
kill: 0.058
},
{
feed: 0.033,
kill: 0.059
},
{
feed: 0.033,
kill: 0.061
},
{
feed: 0.035,
kill: 0.059
},
{
feed: 0.035,
kill: 0.060
},
{
feed: 0.035,
kill: 0.061
}
];
// Configuration.
var feed = presets[0].feed;
var kill = presets[0].kill;
var noAnimation = false;
skipWebGL = function(){
var mobile = /Android|Mobile|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);
var noWebgl = false;
var canvas;
var ctx;
try {
canvas = document.createElement('canvas');
ctx = canvas.getContext('webgl') || canvas.getContext('experimental-webgl');
}
catch (e) {
console && "log" in console && console.log("No webgl");
}
if (ctx == undefined) {
noWebgl = true;
}
if (noWebgl || mobile) {
return true;
}
return false
}
init = function()
{
if( skipWebGL() ){
$("canvas").hide();
$("#replacementImage").show()
noAnimation = true
return
}
canvasQ = $('#myCanvas');
canvas = canvasQ.get(0);
init_controls();
canvas.onmousedown = onMouseDown;
canvas.onmouseup = onMouseUp;
canvas.onmousemove = onMouseMove;
mRenderer = new THREE.WebGLRenderer({canvas: canvas, preserveDrawingBuffer: true});
mScene = new THREE.Scene();
mCamera = new THREE.OrthographicCamera(-0.5, 0.5, 0.5, -0.5, 0, 100);
mCamera.position.z = 100;
mCamera.scale.x = mCamera.scale.y = 0.99985 ;
mScene.add(mCamera);
// For debugging purpose
window.mCamera = mCamera;
mUniforms = {
screenWidth: {type: "f", value: undefined},
screenHeight: {type: "f", value: undefined},
tSource: {type: "t", value: undefined},
delta: {type: "f", value: 0.0},
feed: {type: "f", value: feed},
kill: {type: "f", value: kill},
brush: {type: "v2", value: new THREE.Vector2(-10, -10)},
color1: {type: "v4", value: new THREE.Vector4(0, 0, 0.0, 0)},
color2: {type: "v4", value: new THREE.Vector4(0, 1, 0, 0.2)},
color3: {type: "v4", value: new THREE.Vector4(1, 1, 0, 0.21)},
color4: {type: "v4", value: new THREE.Vector4(1, 0, 0, 0.4)},
color5: {type: "v4", value: new THREE.Vector4(1, 1, 1, 0.6)}
};
mColors = [mUniforms.color1, mUniforms.color2, mUniforms.color3, mUniforms.color4, mUniforms.color5];
$("#gradient").gradient("setUpdateCallback", onUpdatedColor);
mGSMaterial = new THREE.ShaderMaterial({
uniforms: mUniforms,
vertexShader: document.getElementById('standardVertexShader').textContent,
fragmentShader: document.getElementById('gsFragmentShader').textContent,
});
mScreenMaterial = new THREE.ShaderMaterial({
uniforms: mUniforms,
vertexShader: document.getElementById('standardVertexShader').textContent,
fragmentShader: document.getElementById('screenFragmentShader').textContent,
});
var plane = new THREE.PlaneGeometry(1.0, 1.0);
mScreenQuad = new THREE.Mesh(plane, mScreenMaterial);
mScene.add(mScreenQuad);
mColorsNeedUpdate = true;
resize(canvas.clientWidth, canvas.clientHeight);
render(0);
mUniforms.brush.value = new THREE.Vector2(0.5, 0.5);
mLastTime = new Date().getTime();
requestAnimationFrame(render);
}
var resize = function(width, height)
{
// Set the new shape of canvas.
canvasQ.width(width);
canvasQ.height(height);
// Get the real size of canvas.
canvasWidth = canvasQ.width();
canvasHeight = canvasQ.height();
mRenderer.setSize(canvasWidth, canvasHeight);
// TODO: Possible memory leak?
mTexture1 = new THREE.WebGLRenderTarget(canvasWidth/2, canvasHeight/2,
{minFilter: THREE.LinearFilter,
magFilter: THREE.LinearFilter,
format: THREE.RGBAFormat,
type: THREE.FloatType});
mTexture2 = new THREE.WebGLRenderTarget(canvasWidth/2, canvasHeight/2,
{minFilter: THREE.LinearFilter,
magFilter: THREE.LinearFilter,
format: THREE.RGBAFormat,
type: THREE.FloatType});
mTexture1.wrapS = THREE.RepeatWrapping;
mTexture1.wrapT = THREE.RepeatWrapping;
mTexture2.wrapS = THREE.RepeatWrapping;
mTexture2.wrapT = THREE.RepeatWrapping;
mUniforms.screenWidth.value = canvasWidth/2;
mUniforms.screenHeight.value = canvasHeight/2;
}
var render = function(time)
{
var dt = (time - mLastTime)/20.0;
if(dt > 0.8 || dt<=0)
dt = 0.8;
mLastTime = time;
mScreenQuad.material = mGSMaterial;
mUniforms.delta.value = dt;
mUniforms.feed.value = feed;
mUniforms.kill.value = kill;
for(var i=0; i<8; ++i)
{
if(!mToggled)
{
mUniforms.tSource.value = mTexture1;
mRenderer.render(mScene, mCamera, mTexture2, true);
mUniforms.tSource.value = mTexture2;
}
else
{
mUniforms.tSource.value = mTexture2;
mRenderer.render(mScene, mCamera, mTexture1, true);
mUniforms.tSource.value = mTexture1;
}
mToggled = !mToggled;
mUniforms.brush.value = mMinusOnes;
}
if(mColorsNeedUpdate)
updateUniformsColors();
mScreenQuad.material = mScreenMaterial;
mRenderer.render(mScene, mCamera);
requestAnimationFrame(render);
}
loadPreset = function(idx)
{
feed = presets[idx].feed;
kill = presets[idx].kill;
worldToForm();
}
var updateUniformsColors = function()
{
var values = $("#gradient").gradient("getValuesRGBS");
for(var i=0; i<values.length; i++)
{
var v = values[i];
mColors[i].value = new THREE.Vector4(v[0], v[1], v[2], v[3]);
}
mColorsNeedUpdate = false;
}
var onUpdatedColor = function()
{
mColorsNeedUpdate = true;
updateShareString();
}
var onMouseMove = function(e)
{
var ev = e ? e : window.event;
mMouseX = ev.pageX - canvasQ.offset().left; // these offsets work with
mMouseY = ev.pageY - canvasQ.offset().top; // scrolled documents too
if(mMouseDown)
mUniforms.brush.value = new THREE.Vector2(mMouseX/canvasWidth, 1-mMouseY/canvasHeight);
}
var onMouseDown = function(e)
{
var ev = e ? e : window.event;
mMouseDown = true;
mUniforms.brush.value = new THREE.Vector2(mMouseX/canvasWidth, 1-mMouseY/canvasHeight);
}
var onMouseUp = function(e)
{
mMouseDown = false;
}
clean = function()
{
mUniforms.brush.value = new THREE.Vector2(-10, -10);
}
snapshot = function()
{
var dataURL = canvas.toDataURL("image/png");
window.open(dataURL, "name-"+Math.random());
}
// resize canvas to fullscreen, scroll to upper left
// corner and try to enable fullscreen mode and vice-versa
fullscreen = function() {
var canv = $('#myCanvas');
var elem = canv.get(0);
if(isFullscreen())
{
// end fullscreen
if (elem.cancelFullscreen) {
elem.cancelFullscreen();
} else if (document.mozCancelFullScreen) {
document.mozCancelFullScreen();
} else if (document.webkitCancelFullScreen) {
document.webkitCancelFullScreen();
}
}
if(!isFullscreen())
{
// save current dimensions as old
window.oldCanvSize = {
width : canv.width(),
height: canv.height()
};
// adjust canvas to screen size
resize(screen.width, screen.height);
// scroll to upper left corner
$('html, body').scrollTop(canv.offset().top);
$('html, body').scrollLeft(canv.offset().left);
// request fullscreen in different flavours
if (elem.requestFullscreen) {
elem.requestFullscreen();
} else if (elem.mozRequestFullScreen) {
elem.mozRequestFullScreen();
} else if (elem.webkitRequestFullscreen) {
elem.webkitRequestFullscreen();
}
}
}
var isFullscreen = function()
{
return document.mozFullScreenElement ||
document.webkitCurrentFullScreenElement ||
document.fullscreenElement;
}
$(document).bind('webkitfullscreenchange mozfullscreenchange fullscreenchange', function(ev) {
// restore old canvas size
if(!isFullscreen())
resize(window.oldCanvSize.width, window.oldCanvSize.height);
});
var worldToForm = function()
{
//document.ex.sldReplenishment.value = feed * 1000;
$("#sld_replenishment").slider("value", feed);
$("#sld_diminishment").slider("value", kill);
}
var init_controls = function()
{
$("#sld_replenishment").slider({
value: feed, min: 0, max:0.1, step:0.001,
change: function(event, ui) {$("#replenishment").html(ui.value); feed = ui.value; updateShareString();},
slide: function(event, ui) {$("#replenishment").html(ui.value); feed = ui.value; updateShareString();}
});
$("#sld_replenishment").slider("value", feed);
$("#sld_diminishment").slider({
value: kill, min: 0, max:0.073, step:0.001,
change: function(event, ui) {$("#diminishment").html(ui.value); kill = ui.value; updateShareString();},
slide: function(event, ui) {$("#diminishment").html(ui.value); kill = ui.value; updateShareString();}
});
$("#sld_diminishment").slider("value", kill);
$('#share').keypress(function (e) {
if (e.which == 13) {
parseShareString();
return false;
}
});
$("#btn_clear").button({
icons : {primary : "ui-icon-document"},
text : false
});
$("#btn_snapshot").button({
icons : {primary : "ui-icon-image"},
text : false
});
$("#btn_fullscreen").button({
icons : {primary : "ui-icon-arrow-4-diag"},
text : false
});
}
alertInvalidShareString = function()
{
$("#share").val("Invalid string!");
setTimeout(updateShareString, 1000);
}
parseShareString = function()
{
var str = $("#share").val();
var fields = str.split(",");
if(fields.length != 12)
{
alertInvalidShareString();
return;
}
var newFeed = parseFloat(fields[0]);
var newKill = parseFloat(fields[1]);
if(isNaN(newFeed) || isNaN(newKill))
{
alertInvalidShareString();
return;
}
var newValues = [];
for(var i=0; i<5; i++)
{
var v = [parseFloat(fields[2+2*i]), fields[2+2*i+1]];
if(isNaN(v[0]))
{
alertInvalidShareString();
return;
}
// Check if the string is a valid color.
if(! /^#[0-9A-F]{6}$/i.test(v[1]))
{
alertInvalidShareString();
return;
}
newValues.push(v);
}
$("#gradient").gradient("setValues", newValues);
feed = newFeed;
kill = newKill;
worldToForm();
}
updateShareString = function()
{
var str = "".concat(feed, ",", kill);
var values = $("#gradient").gradient("getValues");
for(var i=0; i<values.length; i++)
{
var v = values[i];
str += "".concat(",", v[0], ",", v[1]);
}
$("#share").val(str);
}
var mWord = [[[682,100],[682,667],[569,667],[569,441],[454,441],[454,667],[342,667],[342,100],[454,100],[454,100],[454,326],[569,326],[569,100]],
//
[[682,100],[682,213],[457,213],[457,328],[682,328],[682,667],[342,667],[342,553],[570,553],[570,440],[342,440],[342,100],[682,100]],
//
[[683,100],[683,215],[453,215],[453,328],[683,328],[683,441],[453,441],[453,668],[342,668],[342,100],[683,100]]]
var currentLetterIndex = 0
var currentPositionIndex = 0
var simSpeed = 0.1
var curPos = []
var nexPos = []
var vectX = 0
var vectY = 0
var angle = 0
var moveX = 0
var moveY = 0
var curX = mWord[0][0][0]
var curY = mWord[0][0][1]
simulateClick = function(){
if( noAnimation ) {
return;
}
if( mMouseDown || noAnimation == true ){
return
}
// Let's get the current X,Y
curPos = mWord[currentLetterIndex][currentPositionIndex]
// Let's get the next position
// If last letter, loop with first
if ( currentPositionIndex >= mWord[currentLetterIndex].length - 1 ) {
nexPos = mWord[currentLetterIndex][0]
}else{
nexPos = mWord[currentLetterIndex][currentPositionIndex +1]
}
// Let's get the line to draw X and Y vector components
vectX = nexPos[0] - curPos[0]
vectY = nexPos[1] - curPos[1]
// Let's move along the vector on the line
angle = Math.atan2( vectY, vectX )
moveX = Math.cos(angle) * simSpeed
moveY = Math.sin(angle) * simSpeed
curX += moveX
curY += moveY
// Draw the point
mUniforms.brush.value = new THREE.Vector2(curX/canvasWidth, 1-curY/canvasHeight);
// Is line fully drawn?
var finishedPos = false
if (( moveX > 0 && curX > nexPos[0])
|| ( moveX < 0 && curX < nexPos[0])
|| ( moveY > 0 && curY > nexPos[1])
|| ( moveY < 0 && curY < nexPos[1]) ){
finishedPos = true
}
// Change position and letter if necessary
if( finishedPos ){
// Get new letter if it was the last line
if( currentPositionIndex >= mWord[currentLetterIndex].length - 1 ){
currentLetterIndex = currentLetterIndex == (mWord.length - 1) ? 0 : currentLetterIndex + 1
currentPositionIndex = 0
// Randomize preset
key = parseInt( Math.random() * presets.length)
newPreset = presets[key]
feed= newPreset.feed
kill = newPreset.kill
// mCamera.left = - 0.501
// mCamera.right = 0.501
// Just a new line of the letter
}else{
currentPositionIndex++
}
// Force new position point
curX = mWord[currentLetterIndex][currentPositionIndex][0]
curY = mWord[currentLetterIndex][currentPositionIndex][1]
}
var factor = 0.000002
var positionBoundary = 0.00005
randX = (Math.random() - 0.5 ) * factor;
randY = (Math.random() - 0.5 ) * factor;
randS = (Math.random() - 0.5 ) * factor * 5 ;
if( mCamera.position.x < -positionBoundary ){
mCamera.position.x = -positionBoundary;
}else if( mCamera.position.x > positionBoundary ){
mCamera.position.x = positionBoundary;
}else if(mCamera.position.y < -positionBoundary ){
mCamera.position.y = -positionBoundary;
} else if(mCamera.position.y > positionBoundary ){
mCamera.position.y = positionBoundary;
}
mCamera.position.x += randX;
mCamera.position.y += randY;
if( mCamera.scale.x < 0.9997 ){
mCamera.scale.x = 0.9997
}else if(mCamera.scale.x >= 1.0003 ){
mCamera.scale.x = 1.0003;
}
mCamera.scale.x += randS;
mCamera.scale.y += randS ;
mCamera.updateProjectionMatrix();
}
setInterval( simulateClick, 1 )
})();
$(function(){
$(".close").bind("click",function(){
$(this).parent().toggle();
})
$("#format_show").bind("click",function(){
$("#format").toggle();
})
})