JavaScript
语言:
JaveScriptBabelCoffeeScript
确定
var Poisson = function(exports) {
var width, height, grid;
function distribute(count, radius, w, h) {
width = w;
height = h;
var cellSize = exports.cellSize = radius / Math.sqrt(2);
exports.grid = grid = buildGrid(Math.ceil(w / cellSize), Math.ceil(h / cellSize));
count = Math.min(count, Math.floor(w / cellSize) * Math.floor(h / cellSize))
var processList = [];
var firstPoint = new Point(w * .5, h * .5);
processList.push(firstPoint);
setGridPoint(imageToGrid(firstPoint, cellSize), firstPoint);
var result = [];
var total = count;
while (result.length < count) {
var added = false;
var attempt = 10;
while (!added) {
var rid = ~~(Math.random() * processList.length);
var point = processList[rid];
var newPoint = generateRandomPointAround(point, radius);
//check that the point is in the image region
//and no points exists in the point's neighbourhood
if (inRectangle(newPoint) && !inNeighbourhood(grid, newPoint, radius, cellSize)) {
processList.push(newPoint);
result.push(newPoint);
setGridPoint(imageToGrid(newPoint, cellSize), newPoint);
added = true;
}
if (attempt-- <= 0) break;
}
if (total-- <= 0) break;
}
return result;
}
function buildGrid(w, h) {
var g = [];
for (var i = 0; i < w; i++) {
var tmp = [];
for (var j = 0; j < h; j++) {
tmp.push(null);
}
g.push(tmp);
}
return g;
}
function setGridPoint(gridPoint, value) {
if (grid[gridPoint.x][gridPoint.y] != null) return;
grid[gridPoint.x][gridPoint.y] = value;
}
function inRectangle(point) {
return point.x > 0 && point.x < width && point.y > 0 && point.y < height;
}
function imageToGrid(point, cellSize) {
var gridX = ~~(point.x / cellSize);
var gridY = ~~(point.y / cellSize);
return new Point(gridX, gridY);
}
function generateRandomPointAround(point, mindist) {
var r1 = Math.random();
var r2 = Math.random();
var radius = mindist * (r1 + 1);
var angle = 2 * Math.PI * r2;
var newX = point.x + radius * Math.cos(angle);
var newY = point.y + radius * Math.sin(angle);
return new Point(newX, newY);
}
function inNeighbourhood(grid, point, mindist, cellSize) {
var gridPoint = imageToGrid(point, cellSize);
var cellsAroundPoint = squareAroundPoint(grid, gridPoint.x, gridPoint.y, 4);
var valid = false;
cellsAroundPoint.forEach(function(cell) {
if (cell == null) return;
var min_dist = mindist;
//using a noise (or any non-uniform) function
//var min_dist = mindist + simplex.simplex2( point.x * .01, point.y * .01 ) * mindist;
if (distance(cell, point) < min_dist) {
valid = true;
}
});
return valid;
}
function squareAroundPoint(grid, x, y, offset) {
var off = Math.round(offset / 2);
var minX = Math.max(0, Math.min(grid.length, x - off));
var maxX = Math.max(0, Math.min(grid.length, x + off));
var minY = Math.max(0, Math.min(grid[0].length, y - off));
var maxY = Math.max(0, Math.min(grid[0].length, y + off));
var tmp = [];
for (var i = minX; i < maxX; i++) {
for (var j = minY; j < maxY; j++) {
if (grid[i][j] != null) tmp.push(grid[i][j]);
}
}
return tmp;
}
exports.distribute = distribute;
return exports;
}({});
var Point = function() {
function Point(x, y, z) {
this.x = x || 0;
this.y = y || 0;
this.z = z || 0;
}
var _p = Point.prototype;
_p.constructor = Point;
return Point;
}();
function distance(a, b) {
var dx = a.x - b.x;
var dy = a.y - b.y;
var dz = a.z - b.z;
return Math.sqrt(dx * dx + dy * dy + dz * dz);
}
///
var canvas = document.createElement('canvas');
document.body.appendChild(canvas);
var w = canvas.width = window.innerWidth;
var h = canvas.height = window.innerHeight;
var ctx = canvas.getContext("2d");
//raf: https://github.com/cagosta/requestAnimationFrame/blob/master/app/requestAnimationFrame.js
(function(global) {
(function() {
if (global.requestAnimationFrame) {
return;
}
if (global.webkitRequestAnimationFrame) {
global.requestAnimationFrame = global['webkitRequestAnimationFrame'];
global.cancelAnimationFrame = global['webkitCancelAnimationFrame'] || global['webkitCancelRequestAnimationFrame'];
}
var lastTime = 0;
global.requestAnimationFrame = function(callback) {
var currTime = new Date().getTime();
var timeToCall = Math.max(0, 16 - (currTime - lastTime));
var id = global.setTimeout(function() {
callback(currTime + timeToCall);
}, timeToCall);
lastTime = currTime + timeToCall;
return id;
};
global.cancelAnimationFrame = function(id) {
clearTimeout(id);
};
})();
if (typeof define === 'function') {
define(function() {
return global.requestAnimationFrame;
});
}
})(window);
///
var radius = 15;
var distribution;
var length;
function init() {
w = canvas.width = window.innerWidth;
h = canvas.height = window.innerHeight;
distribution = Poisson.distribute(1000, radius, w, h);
length = distribution.length;
}
function update() {
requestAnimationFrame(update);
if (distribution == null) return;
for (var i = 0; i < 10; i++) {
if (distribution.length == 0) return;
var id = distribution.length;
p = distribution.shift();
ctx.globalAlpha = 1.25 - (id / length);
ctx.beginPath();
ctx.arc(p.x, p.y, (id / length) * radius / 2, 0, Math.PI * 2);
ctx.fill();
}
}
window.onload = init;
window.onresize = init;
window.onmousedown = init;
window.ontouchstart = init;
update();