Created
February 8, 2012 20:21
-
-
Save monde/1773107 to your computer and use it in GitHub Desktop.
codes to Nick Kwiatek's ascii js animation page (http://nkwiatek.com/)
This file contains hidden or 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
// this is in the process of being rewritten and optimized | |
/** | |
* Copyright (c) 2011 Nick Kwiatek <http://nkwiatek.com> | |
* | |
* Permission is hereby granted, free of charge, to any person | |
* obtaining a copy of this software and associated documentation | |
* files (the "Software"), to deal in the Software without | |
* restriction, including without limitation the rights to use, | |
* copy, modify, merge, publish, distribute, sublicense, and/or sell | |
* copies of the Software, and to permit persons to whom the | |
* Software is furnished to do so, subject to the following | |
* conditions: | |
* | |
* The above copyright notice and this permission notice shall be | |
* included in all copies or substantial portions of the Software. | |
* | |
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | |
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES | |
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | |
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT | |
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, | |
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | |
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR | |
* OTHER DEALINGS IN THE SOFTWARE. | |
*/ | |
(function(window) { | |
var FluidDisplayASCII = function(fluid) { | |
var _Config = { | |
// strength of forces | |
density: 260 | |
// ascii gradient | |
//gradient: [' ', '·','.','•','¤','x','X','N','M'], | |
//gradient: [' ', '·','.','¤','x','X','N','M','¶'], | |
//gradient: [' ', '·','~','¢','»','¤','X','¥','¶'], | |
, gradient: [' ', '·','~','¢','c','»','¤','X','M','¶'] | |
// animation update timing in milliseconds | |
, tickrate: 15 | |
, blastDensity: 9000 | |
, blastVelocity: 6000 | |
}; | |
this.Config = _Config; | |
// creates/retrieves stored object which allows us to manipulate the ASCII text block being displayed | |
var getDisplay = (function() { | |
// cache of ascii gradient data | |
var _gradient = _Config.gradient; | |
var _gradient_len = _Config.gradient.length - 1; | |
// container for holding the display text | |
var _ascii = []; | |
// the artifical dimensions of the field | |
var _width = null; | |
var _height = null; | |
var _length = null; | |
// object returned by func, for manipulating display text | |
var _Display = { | |
length: function() { | |
return _length; | |
}, | |
// TODO: figure out how this should work in conjunction with field.reset() | |
reset: function() { | |
resizeDisplay(dimensions.width, dimensions.height, true); | |
}, | |
set: function(field) { | |
var rowSize = field.rowSize(); | |
var dens = field.rawDensityArray; | |
var y = _height - 1; do { | |
// small optimisations here | |
var yAsciiOffset = y * _width; | |
var yDensityOffset = (y + 1) * rowSize; | |
var x = _width; do { | |
// we use x - 1 because our array is index 1, theirs is index 0 | |
var value = dens[x + yDensityOffset] + 0.5 | 0; // bitwise round | |
if (value > _gradient_len) { | |
value = _gradient_len; | |
} | |
_ascii[x + yAsciiOffset] = _gradient[value]; | |
} while(x-- > 1); | |
} while(y--); | |
}, | |
toString: function() { | |
// much faster implementation, by like 1000%, even on engines natively supporting Array.reduce() | |
// slices array by row length, appends newline | |
var retval = ''; | |
for (var i = 1; i <= _height; i++) { | |
var range = i * _width + 1; // + 1 because the slice end is not inclusive (but we need it to be) | |
retval += _ascii.slice(range - _width, range).join('') + '\n'; | |
} | |
return retval; | |
} | |
}; | |
// private function for resizing private ascii container | |
var _resizeDisplay = function (width, height, overwrite) { | |
// is this how you do default values? | |
if (overwrite === undefined) { | |
overwrite = false; | |
} | |
_width = width; | |
_height = height; | |
_length = width * height; | |
for (var i = 1; i <= _length; i++) { | |
if (overwrite || _ascii[i] === undefined) { | |
_ascii[i] = _Config.gradient[1]; | |
} | |
} | |
}; | |
// Display factory (returns cache if valid) | |
return function(width, height) { | |
// if for some reason this is unoptimal, | |
// we can use an event listener callback on fluid resize, | |
// instead of requiring width/height on get() | |
if (width !== _Display.width && height !== _Display.height) { | |
_resizeDisplay(width, height); | |
} | |
return _Display; | |
}; | |
})(); | |
// | |
// TODO: addForce, generalize the mouse calculation operation into a func | |
// onclick: force | |
// take setUI on force obj out of display renderer | |
// store the bound DOM element inside this object's scope | |
this.bindElement = function(DOMElement) { | |
// used on field.update to determine whether we should apply velocity | |
var checkMouseMovement = false; | |
var checkMouseClick = false; | |
// mouse coordinates relative to element's origin | |
var mouse_cur = {x: null, y: null}; | |
var mouse_prev = null; | |
var origin = (function(el) { | |
var x = 0, y = 0; | |
do { | |
x += el.offsetLeft; | |
y += el.offsetTop; | |
} while (el = el.offsetParent); | |
return { x: x, y: y }; | |
})(DOMElement); | |
DOMElement.unselectable = true; | |
DOMElement.onmousedown = (function() { | |
checkMouseClick = true; | |
}) | |
DOMElement.onmousemove = (function() { | |
var doc_body = window.document.body; | |
var doc_element = window.document.documentElement; | |
return function(e) { | |
// the following browser-wide mouse coordinates logic taken mostly from quirksmode | |
// http://www.quirksmode.org/js/events_properties.html#position | |
if (!e) { | |
e = window.event; | |
} | |
if (e.pageX || e.pageY) { | |
mouse_cur.x = e.pageX; | |
mouse_cur.y = e.pageY; | |
} | |
else if (e.clientX || e.clientY) { | |
mouse_cur.x = e.clientX + doc_body.scrollLeft + doc_element.scrollLeft; | |
mouse_cur.y = e.clientY + doc_body.scrollTop + doc_element.scrollTop; | |
} | |
// has to be initialized somewhere; better here than in the UI callback | |
// could probably do a crazy trick involving a named var for 'onmousemove =', | |
// having it first be a check for mouse_prev, then replacing the named func | |
// with a func that doesn't have the check, if this line of code is REALLY an issue | |
// probably not a factor here, but an interesting idea for somewhere more critical | |
if (mouse_prev === null) { | |
mouse_prev = {x: mouse_cur.x, y: mouse_cur.y}; | |
} | |
checkMouseMovement = true; | |
} | |
})(); | |
// called on fluid's update() method (before physics calculations) | |
// we can make changes to the field here | |
fluid.setUICallback(function(field) { | |
// click explosion | |
if (checkMouseClick) { | |
var x_fluid | |
, y_fluid | |
, deWidth = DOMElement.offsetWidth | |
, deHeight = DOMElement.offsetHeight | |
, fWidth = field.width() | |
, fHeight = field.height() | |
// map element coords to fluid coords | |
x_fluid = (mouse_cur.x * (fWidth / deWidth)) + 0.5 | 0 // bitwise round | |
y_fluid = (mouse_cur.y * (fHeight / deHeight)) + 0.5 | 0 // bitwise round | |
// apply the shockwave by setting params in the 8 pixels around the current position | |
// (as well as some extra dead weight in the current position) | |
for (var w = -1; w <= 1; w++) { | |
for (var h = -1; h <= 1; h++) { | |
field.setDensity(x_fluid + w, y_fluid + h, _Config.blastDensity) | |
field.setVelocity(x_fluid + w, y_fluid + h, _Config.blastVelocity * w, _Config.blastVelocity * h) | |
} | |
} | |
checkMouseClick = false | |
} | |
// add velocity if the mouse has moved within the body of the display | |
if (checkMouseMovement /*&& mouse_prev != null*/) { | |
var diff_x = mouse_cur.x - mouse_prev.x; | |
var diff_y = mouse_cur.y - mouse_prev.y; | |
var distance = Math.sqrt(diff_x * diff_x + diff_y * diff_y) + 0.5 | 0; // bitwise round | |
distance = distance < 1 ? 1 : distance; | |
// small optimization | |
var fSetVelocity = field.setVelocity; | |
var fSetDensity = field.setDensity; | |
var fWidth = field.width(); | |
var fHeight = field.height(); | |
var deWidth = DOMElement.offsetWidth; | |
var deHeight = DOMElement.offsetHeight; | |
var mp_x = mouse_prev.x; | |
var mp_y = mouse_prev.y; | |
// interpolate steps between the current position and the previous capture | |
var i = distance - 1; do { | |
var x = (((mp_x + diff_x * (i / distance)) / deWidth) * fWidth) + 0.5 | 0; // bitwise round | |
var y = (((mp_y + diff_y * (i / distance)) / deHeight) * fHeight) + 0.5 | 0; // bitwise round | |
fSetVelocity(x, y, diff_x, diff_y); | |
fSetDensity(x, y, _Config.density); | |
} while(i--); | |
// update record of calculation | |
mouse_prev.x = mouse_cur.x; | |
mouse_prev.y = mouse_cur.y; | |
checkMouseMovement = false; | |
} | |
}); | |
// called at the end of every frame, after calculations are complete | |
// draw changes here | |
fluid.setDisplayFunction(function(field) { | |
// update display with data from field | |
var display = getDisplay(field.width(), field.height()); | |
display.set(field); | |
// update the DOM DOMElement with the display text | |
/*while(DOMElement.childNodes.length >= 1) { | |
DOMElement.removeChild(DOMElement.firstChild); | |
} | |
DOMElement.appendChild(DOMElement.ownerDocument.createTextNode( display.toString() ));*/ | |
DOMElement.innerHTML = display.toString(); | |
}); | |
}; | |
// public interface for starting/stopping animation | |
this.Animation = { | |
interval: null, | |
start: function(fnOnFrameUpdate) { | |
if (this.interval !== null) { | |
return; | |
} | |
// this IIFE will return either: | |
// - a function that calls the callback | |
// - a function that just does the tick | |
// depending on whether fnOnFrameUpdate was passed | |
// todo: replace `fluid` with `callback` passed with `Function.bind(fluid, fluid.update)` | |
;(function(interval, tickrate, fluid, fnOnFrameUpdate) { | |
var fn = (fnOnFrameUpdate instanceof Function) ? | |
function tickWithCallback() { | |
fluid.update(); | |
fnOnFrameUpdate(); | |
interval = setTimeout(tickWithCallback, tickrate); | |
} | |
: function tickNoCallback() { | |
fluid.update(); | |
interval = setTimeout(tickNoCallback, tickrate); | |
} | |
// start the clock | |
fn(); | |
// redundant vars passed here to whittle down the closure nesting | |
// (for performance -- this shit gets called every frame) | |
}(this.interval, _Config.tickrate, fluid, fnOnFrameUpdate)) | |
}, | |
/*start: function(cbOnFrameUpdate) { | |
if (this.interval === null) { | |
var _proc = ((function(interval, tickrate) { | |
return function tick() { | |
fluid.update(); | |
if (cbOnFrameUpdate instanceof Function) { | |
cbOnFrameUpdate(); | |
} | |
interval = setTimeout(procClosureHandle, tickrate); | |
}; | |
})(this.interval, _Config.tickrate)); | |
procClosureHandle = _proc; | |
procClosureHandle(); | |
} | |
},*/ | |
stop: function() { | |
if (this.interval !== null) { | |
clearInterval(this.interval); | |
this.interval = null; | |
} | |
} | |
}; | |
}; | |
window['FluidDisplayASCII'] = FluidDisplayASCII; | |
})(window); |
This file contains hidden or 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
// Based on http://www.dgp.toronto.edu/people/stam/reality/Research/pdf/GDC03.pdf | |
// this is in the process of being rewritten and optimized | |
/** | |
* Copyright (c) 2009 Oliver Hunt <http://nerget.com> | |
* | |
* Permission is hereby granted, free of charge, to any person | |
* obtaining a copy of this software and associated documentation | |
* files (the "Software"), to deal in the Software without | |
* restriction, including without limitation the rights to use, | |
* copy, modify, merge, publish, distribute, sublicense, and/or sell | |
* copies of the Software, and to permit persons to whom the | |
* Software is furnished to do so, subject to the following | |
* conditions: | |
* | |
* The above copyright notice and this permission notice shall be | |
* included in all copies or substantial portions of the Software. | |
* | |
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | |
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES | |
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | |
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT | |
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, | |
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | |
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR | |
* OTHER DEALINGS IN THE SOFTWARE. | |
*/ | |
function FluidField() { | |
function addFields(x, s, dt) { | |
var i = size - 1; do { | |
x[i] += dt * s[i] | |
} while (i--); | |
} | |
// second slowest function | |
function set_bnd(b, x) { | |
// small caching optimizations | |
var lastRow = height * rowSize; | |
var maxEdge = (height + 1) * rowSize; | |
var maxWidth = (width + 1); | |
if (b===1) { | |
for (var i = 1; i <= width; i++) { | |
x[i] = x[i + rowSize]; | |
x[i + maxEdge] = x[i + lastRow]; | |
} | |
for (var j = 1; i <= height; i++) { | |
var jRow = j * rowSize; | |
x[jRow] = -x[1 + jRow]; | |
x[maxWidth + jRow] = -x[width + jRow]; | |
} | |
} else if (b === 2) { | |
for (var i = 1; i <= width; i++) { | |
x[i] = -x[i + rowSize]; | |
x[i + maxEdge] = -x[i + lastRow]; | |
} | |
for (var j = 1; j <= height; j++) { | |
var jRow = j * rowSize; | |
x[jRow] = x[1 + jRow]; | |
x[maxWidth + jRow] = x[width + jRow]; | |
} | |
} else { | |
for (var i = 1; i <= width; i++) { | |
x[i] = x[i + rowSize]; | |
x[i + maxEdge] = x[i + lastRow]; | |
} | |
for (var j = 1; j <= height; j++) { | |
var jRow = j * rowSize; | |
x[jRow] = x[1 + jRow]; | |
x[maxWidth + jRow] = x[width + jRow]; | |
} | |
} | |
x[0] = 0.5 * (x[1] + x[rowSize]); | |
x[maxEdge] = 0.5 * (x[1 + maxEdge] + x[lastRow]); | |
x[maxWidth] = 0.5 * (x[width] + x[maxWidth + rowSize]); | |
x[maxWidth+maxEdge] = 0.5 * (x[width + maxEdge] + x[maxWidth + lastRow]); | |
} | |
// this function is INCREDIBLY slow | |
function lin_solve(b, x, x0, a, c) { | |
if (a === 0 && c === 1) { | |
for (var j=1 ; j<=height; j++) { | |
var currentRow = j * rowSize; | |
++currentRow; | |
var i = width - 1; do { | |
x[currentRow] = x0[currentRow]; | |
++currentRow; | |
} while (i--); | |
} | |
set_bnd(b, x); | |
} else { | |
var invC = 1 / c; | |
for (var k=0 ; k<iterations; k++) { | |
for (var j=1 ; j<=height; j++) { | |
var lastRow = (j - 1) * rowSize; | |
var currentRow = j * rowSize; | |
var nextRow = (j + 1) * rowSize; | |
var lastX = x[currentRow]; | |
++currentRow; | |
var i = width; do { | |
lastX = x[currentRow] = (x0[currentRow] + a*(lastX+x[++currentRow]+x[++lastRow]+x[++nextRow])) * invC; | |
} while (i-- > 1) | |
} | |
set_bnd(b, x); | |
} | |
} | |
} | |
function diffuse(b, x, x0, dt) | |
{ | |
var a = 0; | |
lin_solve(b, x, x0, a, 1 + 4*a); | |
} | |
function lin_solve2(x, x0, y, y0, a, c) | |
{ | |
if (a === 0 && c === 1) { | |
for (var j=1 ; j <= height; j++) { | |
var currentRow = j * rowSize; | |
++currentRow; | |
for (var i = 0; i < width; i++) { | |
x[currentRow] = x0[currentRow]; | |
y[currentRow] = y0[currentRow]; | |
++currentRow; | |
} | |
} | |
set_bnd(1, x); | |
set_bnd(2, y); | |
} else { | |
var invC = 1/c; | |
for (var k=0 ; k<iterations; k++) { | |
for (var j=1 ; j <= height; j++) { | |
var lastRow = (j - 1) * rowSize; | |
var currentRow = j * rowSize; | |
var nextRow = (j + 1) * rowSize; | |
var lastX = x[currentRow]; | |
var lastY = y[currentRow]; | |
++currentRow; | |
for (var i = 1; i <= width; i++) { | |
lastX = x[currentRow] = (x0[currentRow] + a * (lastX + x[currentRow] + x[lastRow] + x[nextRow])) * invC; | |
lastY = y[currentRow] = (y0[currentRow] + a * (lastY + y[++currentRow] + y[++lastRow] + y[++nextRow])) * invC; | |
} | |
} | |
set_bnd(1, x); | |
set_bnd(2, y); | |
} | |
} | |
} | |
function diffuse2(x, x0, y, y0, dt) | |
{ | |
var a = 0; | |
lin_solve2(x, x0, y, y0, a, 1 + 4 * a); | |
} | |
function advect(b, d, d0, u, v, dt) | |
{ | |
var Wdt0 = dt * width; | |
var Hdt0 = dt * height; | |
var Wp5 = width + 0.5; | |
var Hp5 = height + 0.5; | |
for (var j = 1; j<= height; j++) { | |
var pos = j * rowSize; | |
for (var i = 1; i <= width; i++) { | |
var x = i - Wdt0 * u[++pos]; | |
var y = j - Hdt0 * v[pos]; | |
if (x < 0.5) | |
x = 0.5; | |
else if (x > Wp5) | |
x = Wp5; | |
var i0 = x | 0; | |
var i1 = i0 + 1; | |
if (y < 0.5) | |
y = 0.5; | |
else if (y > Hp5) | |
y = Hp5; | |
var j0 = y | 0; | |
var j1 = j0 + 1; | |
var s1 = x - i0; | |
var s0 = 1 - s1; | |
var t1 = y - j0; | |
var t0 = 1 - t1; | |
var row1 = j0 * rowSize; | |
var row2 = j1 * rowSize; | |
d[pos] = s0 * (t0 * d0[i0 + row1] + t1 * d0[i0 + row2]) + s1 * (t0 * d0[i1 + row1] + t1 * d0[i1 + row2]); | |
} | |
} | |
set_bnd(b, d); | |
} | |
function project(u, v, p, div) { | |
var h = -0.5 / Math.sqrt(width * height); | |
for (var j = 1 ; j <= height; j++ ) { | |
var row = j * rowSize; | |
var previousRow = (j - 1) * rowSize; | |
var prevValue = row - 1; | |
var currentRow = row; | |
var nextValue = row + 1; | |
var nextRow = (j + 1) * rowSize; | |
for (var i = 1; i <= width; i++ ) { | |
div[++currentRow] = h * (u[++nextValue] - u[++prevValue] + v[++nextRow] - v[++previousRow]); | |
p[currentRow] = 0; | |
} | |
} | |
set_bnd(0, div); | |
set_bnd(0, p); | |
lin_solve(0, p, div, 1, 4 ); | |
var wScale = 0.5 * width; | |
var hScale = 0.5 * height; | |
for (var j = 1; j<= height; j++ ) { | |
var prevPos = j * rowSize - 1; | |
var currentPos = j * rowSize; | |
var nextPos = j * rowSize + 1; | |
var prevRow = (j - 1) * rowSize; | |
var nextRow = (j + 1) * rowSize; | |
for (var i = 1; i<= width; i++) { | |
u[++currentPos] -= wScale * (p[++nextPos] - p[++prevPos]); | |
v[currentPos] -= hScale * (p[++nextRow] - p[++prevRow]); | |
} | |
} | |
set_bnd(1, u); | |
set_bnd(2, v); | |
} | |
function dens_step(x, x0, u, v, dt) | |
{ | |
addFields(x, x0, dt); | |
diffuse(0, x0, x, dt ); | |
advect(0, x, x0, u, v, dt ); | |
} | |
function vel_step(u, v, u0, v0, dt) | |
{ | |
addFields(u, u0, dt ); | |
addFields(v, v0, dt ); | |
var temp = u0; u0 = u; u = temp; | |
var temp = v0; v0 = v; v = temp; | |
diffuse2(u,u0,v,v0, dt); | |
project(u, v, u0, v0); | |
var temp = u0; u0 = u; u = temp; | |
var temp = v0; v0 = v; v = temp; | |
advect(1, u, u0, u0, v0, dt); | |
advect(2, v, v0, u0, v0, dt); | |
project(u, v, u0, v0 ); | |
} | |
var uiCallback = function(d,u,v) {}; | |
function Field(dens, u, v) { | |
// Just exposing the fields here rather than using accessors is a measurable win during display (maybe 5%) | |
// but makes the code ugly. | |
this.setDensity = function(x, y, d) { | |
dens[(x + 1) + (y + 1) * rowSize] = d; | |
} | |
this.getDensity = function(x, y) { | |
return dens[(x + 1) + (y + 1) * rowSize]; | |
} | |
this.setVelocity = function(x, y, xv, yv) { | |
u[(x + 1) + (y + 1) * rowSize] = xv; | |
v[(x + 1) + (y + 1) * rowSize] = yv; | |
} | |
this.getXVelocity = function(x, y) { | |
return u[(x + 1) + (y + 1) * rowSize]; | |
} | |
this.getYVelocity = function(x, y) { | |
return v[(x + 1) + (y + 1) * rowSize]; | |
} | |
this.width = function() { return width; } | |
this.height = function() { return height; } | |
// NK -- sorry Oliver, exposing them anyway | |
this.rawDensityArray = dens; | |
this.rowSize = function() { return rowSize; } | |
} | |
function queryUI(d, u, v) { | |
var i = size - 1; do { | |
u[i] = v[i] = d[i] = 0.0; | |
} while (i--); | |
uiCallback(new Field(d, u, v)); | |
} | |
this.update = function () { | |
queryUI(dens_prev, u_prev, v_prev); | |
vel_step(u, v, u_prev, v_prev, dt); | |
dens_step(dens, dens_prev, u, v, dt); | |
displayFunc(new Field(dens, u, v)); | |
} | |
this.setDisplayFunction = function(func) { | |
displayFunc = func; | |
} | |
this.iterations = function() { return iterations; } | |
this.setIterations = function(iters) { | |
if (iters > 0 && iters <= 100) | |
iterations = iters; | |
} | |
this.setUICallback = function(callback) { | |
uiCallback = callback; | |
} | |
var iterations = 10; | |
var visc = 0.5; | |
var dt = 0.1; | |
var dens; | |
var dens_prev; | |
var u; | |
var u_prev; | |
var v; | |
var v_prev; | |
var width; | |
var height; | |
var rowSize; | |
var size; | |
var displayFunc; | |
function reset() | |
{ | |
rowSize = width + 2; | |
size = (width+2)*(height+2); | |
dens = new Array(size); | |
dens_prev = new Array(size); | |
u = new Array(size); | |
u_prev = new Array(size); | |
v = new Array(size); | |
v_prev = new Array(size); | |
for (var i = 0; i < size; i++) | |
dens_prev[i] = u_prev[i] = v_prev[i] = dens[i] = u[i] = v[i] = 0; | |
} | |
this.reset = reset; | |
this.setResolution = function (hRes, wRes) | |
{ | |
var res = wRes * hRes; | |
if (res > 0 && res < 1000000 && (wRes != width || hRes != height)) { | |
width = wRes; | |
height = hRes; | |
reset(); | |
return true; | |
} | |
return false; | |
} | |
this.setResolution(64); | |
} |
This file contains hidden or 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
<!doctype HTML> | |
<html class="no-js"> | |
<head> | |
<meta charset="utf-8"> | |
<title>Nick Kwiatek — Web & interaction designer + developer</title> | |
<script>(function(H){H.className=H.className.replace(/\bno-js\b/,'js')})(document.documentElement)</script> | |
<link rel="stylesheet" type="text/css" href="style.css" /> | |
<link rel="shortcut icon" type="image/x-icon" href="/favicon.ico"> | |
<meta name="keywords" content="nick, kwiatek, nick kwiatek, design, web design, ux, user experience, interface, interface design, development, javascript, typography, dislikes when people use internet memes in public"> | |
<meta name="description" content="Making interfaces fun and not annoying since 1998 (first with Apple Hypercard; now with the web)."> | |
<meta name="author" content="Nick Kwiatek"> | |
<meta name="copyright" content="2012"> | |
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js"></script> | |
</head> | |
<body class="smaller"> | |
<div id="overlay"> | |
<div id="card"> | |
<br/> | |
<br/> | |
<h1>Nick Kwiatek</h1><br/> | |
<h2>Web & interaction designer + developer</h2><br/> | |
<h2>New York, NY</h2><br/> | |
<br/> | |
→ <a href="mailto:[email protected]">Email me</a><br/> | |
→ <a href="http://twitter.com/nkwiatek" target="_blank">Send me a tweet</a><br/> | |
<br/><br/><dl> | |
<dd>"People create programs to direct processes. In effect, we conjure the spirits of the computer with our spells." <br/><br/></dd> | |
<dt>—The Structure & Interpretation<br/>of Computer Programs </dt> | |
</dl> | |
<br/> | |
<br/> | |
</div> | |
<br/> | |
<div id="controls"> | |
<br/> | |
<button id="clear">"Disappear!"</button><br/> | |
<br/> | |
</div> | |
</div> | |
<p id="fluid" unselectable="on"></p> | |
<div id="fps">fps: 0</div> | |
<script type="text/javascript" src="fluid.solver.js"></script> | |
<script type="text/javascript" src="fluid.display.ascii.js"></script> | |
<script type="text/javascript"> | |
jQuery(document).ready(function($) { | |
var fluid | |
, el | |
, display | |
, displayFPS | |
// physics solver | |
fluid = new FluidField(); | |
// display DOM element | |
el = document.getElementById('fluid'); | |
$(window) | |
.resize(function(e) { | |
var glyphDims = (function() { | |
var $dummy = $(el).clone().text('M').css({visibility: 'hidden'}).appendTo('body'); | |
var retval = { | |
width: $dummy.width(), | |
height: parseInt( $dummy.css('line-height'), 10 ) // more reliable than height() | |
}; | |
$dummy.remove(); | |
return retval; | |
})(); | |
// this won't be perfect, as the screen dims won't often be evenly disible | |
fluid.setResolution(Math.round($(window).height() / glyphDims.height), Math.round($(window).width() / glyphDims.width)); | |
}) | |
.trigger('resize'); | |
// display component | |
display = new FluidDisplayASCII(fluid) | |
display.bindElement(el) | |
displayFPS = function() { | |
var frames = 0 | |
, time_start = new Date() | |
, $DOMfps = $('#fps') | |
$DOMfps.show() | |
return function(frame) { | |
var time_end = new Date() | |
frames++ | |
if ((time_end - time_start) >= 1000) { | |
$DOMfps.text('fps: ' + Math.round((1000 * frames / (time_end - time_start)))) | |
time_start = time_end | |
frames = 0 | |
} | |
} | |
} | |
// final configuration | |
//fluid.setIterations(15); | |
//display.Config.density = 260; | |
fluid.setIterations(5) | |
display.Config.density = 180 | |
display.Animation.start( /*displayFPS()*/ ) | |
// rest of initation | |
$('#overlay').hide().fadeTo('slow', 1) | |
$('button#clear').mousedown(function(e) { | |
var $el = $(el) | |
$el | |
.addClass('fading') | |
.fadeOut('medium', function() { | |
fluid.reset() | |
$el | |
.removeClass('fading') | |
.fadeIn('fast') | |
}) | |
}) | |
}) | |
</script> | |
<script type="text/javascript"> | |
var _gaq = _gaq || []; | |
_gaq.push(['_setAccount', 'UA-28391837-1']); | |
_gaq.push(['_trackPageview']); | |
(function() { | |
var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true; | |
ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js'; | |
var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s); | |
})(); | |
</script> | |
</body> | |
</html> |
This file contains hidden or 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
* { | |
padding: 0; | |
margin: 0; | |
text-rendering: optimizeSpeed; | |
-webkit-font-smoothing: none; | |
} | |
a { | |
color: red !important; | |
text-decoration: underline; | |
} | |
a:hover { | |
text-decoration: none; | |
} | |
body { | |
overflow: hidden; | |
} | |
body * { | |
font-size: 12px; | |
line-height: 16px; | |
font-family: "Courier New"; | |
color: #444; | |
} | |
body.smaller * { | |
font-size: 11px; | |
line-height: 15px; | |
} | |
#fluid { | |
color: red; | |
white-space: pre; | |
width: auto; | |
height: auto; | |
display: inline-block; | |
cursor: default; | |
-moz-user-select: -moz-none; | |
-khtml-user-select: none; | |
-webkit-user-select: none; | |
user-select: none; | |
/* | |
-webkit-transition: all 0.3s ease-out; | |
-moz-transition: all 0.3s ease-out; | |
-o-transition: all 0.3s ease-out; | |
transition: all 0.3s ease-out; | |
*/ | |
} | |
#fluid.fading { | |
font-style: italic; | |
} | |
#fps { | |
position: absolute; | |
bottom: 10px; | |
right: 10px; | |
background: white; | |
padding: 10px; | |
display: none; | |
} | |
.js #overlay { | |
display: none; | |
} | |
#overlay { | |
position: absolute; | |
top: 63px; | |
left: 70px; | |
pointer-events: none; | |
z-index: 100; | |
} | |
#overlay > div { | |
background: white; | |
/*padding: 0 27px 0 22px;*/ | |
padding: 0 21px; | |
pointer-events: auto; | |
} | |
#card { | |
width: 287px; | |
} | |
#controls { | |
display: inline-block; | |
/*background: none !important;*/ | |
} | |
body.smaller #overlay { | |
top: 61px; | |
} | |
h1, h2, p { | |
display: inline-block; | |
} | |
h2 { | |
font-weight: normal; | |
} | |
dd { | |
text-indent: -4px; | |
} | |
dt { | |
/*text-indent: -4px;*/ | |
font-style: italic; | |
text-align: right; | |
} | |
button#clear { | |
background: red; | |
color: white; | |
font-weight: bold; | |
padding: 6px 7px; | |
border: 1px solid rgb(240, 0, 0); | |
cursor: pointer; | |
-webkit-box-shadow: 2px 2px 0px 0px rgb(210, 210, 210); | |
-moz-box-shadow: 2px 2px 0px 0px rgb(210, 210, 210); | |
box-shadow: 2px 2px 0px 0px rgb(210, 210, 210); | |
} | |
button#clear:hover { | |
position: relative; | |
left: -1px; | |
top: -1px; | |
-webkit-box-shadow: 3px 3px 0px 0px rgb(220, 220, 220); | |
-moz-box-shadow: 3px 3px 0px 0px rgb(220, 220, 220); | |
box-shadow: 3px 3px 0px 0px rgb(220, 220, 220); | |
} | |
button#clear:active { | |
position: relative; | |
left: 1px; | |
top: 1px; | |
-webkit-box-shadow: 0px 0px; | |
-moz-box-shadow: 0px 0px; | |
box-shadow: 0px 0px; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment