Built with blockbuilder.org
forked from erikhazzard's block: voronoi+boids-1-grey-transparent
forked from erikhazzard's block: voronoi+boids-2-grey-transparent
forked from erikhazzard's block: voronoi+boids-4-few-points
forked from erikhazzard's block: voronoi+boids-delaunay-1
xxxxxxxxxx
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<link href="https://fonts.googleapis.com/css?family=Inconsolata|Prociono" rel="stylesheet">
<title>Voronoi Boids</title>
<link rel="stylesheet" href="style.css" />
</head>
<body>
<div id='viz-wrapper'>
<canvas id='canvas' width="1400" height="700"></canvas>
<svg id='viz'> </svg>
</div>
<script src='d3-topo-combined.js'></script>
<script src='entity-vector.js'></script>
<script>
canvas = document.getElementById('canvas');
context = canvas.getContext('2d');
//width / height
var width = canvas.width;
var height = canvas.height;
var rows = 60;
var columns = 60;
var size = 10;
var GAME = {};
window.GAME = GAME;
GAME.entities = new Entities();
GAME.grid = {};
GAME.resolution = size;
var image = new Image();
//============================================================================
//
//cell
//
//============================================================================
var Cell = function(params){
var i = 0;
var param = null;
this.previous = 0;
for(i in params){ if (params.hasOwnProperty(i)) {
param = params[i];
this[i] = param;
}}
};
Cell.prototype.draw = function(){
context.save();
context.fillStyle = this.color;
if(this.state < 1){
this.color = 'rgba(0,0,0,0)';
} else {
this.color = 'rgba(100,150,200,0.4)';
}
if(this.previous === 1 && this.state === 0){
this.color = 'rgba(200,50,50,0.4)';
}
else if(this.previous === 0 && this.state === 1){
this.color = 'rgba(50,200,50,0.4)';
}
context.fillRect(
this.i * size,
this.j * size,
size,
size
);
context.restore();
};
Cell.prototype.getNeighbors = function(){
//me
var neighbors = [];
var i=0;
var j=0;
var targetI = 0;
var targetJ = 0;
for(i=-1;i<=1;i++){
for(j=-1;j<=1;j++){
targetI = this.i + i;
targetJ = this.j + j;
if(targetI >= 0 && targetI <= rows
&& targetJ >= 0 && targetJ <= columns
&& GAME.grid[targetI] && GAME.grid[targetI][targetJ]
){
neighbors.push( GAME.grid[targetI][targetJ] );
}
}
}
return neighbors;
}
Cell.prototype.getState = function(){
//get neighbors
var neighbors = this.getNeighbors();
var cell = new Cell({
i: this.i,
j: this.j,
state: this.state,
color: this.color
});
var i=0;
var state = this.state;
var num = 0;
for(i=neighbors.length-1;i>=0;i--){
if( neighbors[i] && this !== neighbors[i] && neighbors[i].state > 0){
num += 1;
}
}
if(this.state === 1 && num < 2){
cell.state = 0;
}else if(this.state === 1 && num > 3){
cell.state = 0;
}else if(this.state === 0 && num === 3){
cell.state = 1;
}
return cell;
};
/**
* Setup cells
*/
var i=j=0;
for(i=rows-1;i>=0;i--){
GAME.grid[i] = [];
for(j=rows-1;j>=0;j--){
GAME.grid[i][j] = new Cell({
i: i,
j: j,
color: 'rgba(100,150,200,1)',
state: 0
});
}
};
var updateGrid = function(){
var nextGrid = {};
var color = null;
var cell = null;
var neighbors = null;
var i=0;
var j=0;
//Draw grid
for(i=rows-1;i>=0;i--){
nextGrid[i] = []
for(j=rows-1;j>=0;j--){
cell = GAME.grid[i][j];
cell.draw();
newCell = cell.getState();
nextGrid[i][j] = newCell;
nextGrid[i][j].previous = cell.state;
}
}
GAME.grid = nextGrid;
}
/**
* Entities
*/
//create some entities
var numEntities = 3;
for(var i=0;i<numEntities; i++){
GAME.entities.add( new Entity({
position: new Vector(
Math.random() * width | 0,
Math.random() * height | 0
),
velcoity: new Vector(
Math.random() * 20 | 0,
Math.random() * 20 | 0
),
maxSpeed: 7,
mass: 10
}));
}
GAME.entitiesStatic = new Entities();
var numStaticEntities = 13;
for(var i=0;i<numStaticEntities; i++){
GAME.entitiesStatic.add(
new Entity({
position: new Vector(width/2, height/2),
mass: 40,
maxSpeed: 9
})
);
}
GAME.util = {};
GAME.mouse = new Vector(0,0);
canvas.addEventListener('mousemove', function(e){
GAME.mouse = new Vector( e.clientX, e.clientY );
});
var TOTAL_ENTITIES = numEntities + numStaticEntities;
var canvas = document.querySelector("canvas"),
width = canvas.width,
height = canvas.height,
context = canvas.getContext("2d"),
voro = d3.voronoi().extent([[1, 1], [width - 1, height - 1]]);
var n = TOTAL_ENTITIES,
particles = new Array(n),
fill = d3.interpolate(
d3.cubehelix(80, 1.50, 0.8),
d3.cubehelix(-100, 0.75, 0.35)
),
radius = 10;
fill = function () { return 'rgba(100, 150, 200, 1)'; }
for (var i = 0; i < n; ++i) particles[i] = {0: Math.random() * width, 1: Math.random() * height, vx: 0, vy: 0};
for (var i = 0; i <= width; i += 80) particles.push([i, 0], [i, height]);
for (var i = 0; i <= height; i += 80) particles.push([0, i], [width, i]);
d3.timer(function(elapsed) {
var i=0,j=0,
entity = null, attraction=null,
steer = steerLine = desired = null;
var statics = GAME.entitiesStatic.entities;
var entities = GAME.entities.entities;
//GAME.grid = [];
//Draw entities
for(i in entities){
entity = entities[i];
////seek the mouse
//if(Math.random() < 0.7){
//entity.applyForce(
//entity.seekForce(GAME.mouse).multiply(.5)
//);
//}
//Separate this entity from nearby entities
entity.applyForce(
entity.separate(entities).multiply(0.3)
);
////FLOCK
entity.flock(entities);
//Random walking
entity.applyForce(
entity.walkForce().multiply(1)
);
for(j in statics){
entity.applyForce(
entity.seekForce(statics[j].position, false, 85).multiply(-4)
);
}
//entity.separate(statics)
//setup grid
column = parseInt(parseInt(entity.position.x,10) / GAME.resolution);
row = parseInt(parseInt(entity.position.y,10) / GAME.resolution);
if( !GAME.grid[column] ){
GAME.grid[column] = [];
}
if( !GAME.grid[column][row] ){
GAME.grid[column][row] = [];
}
//add to grid
GAME.grid[column][row].state = 1;
entity.update();
}
////STATIC entities
for(i in GAME.entitiesStatic.entities){
entity = GAME.entitiesStatic.entities[i];
column = parseInt(parseInt(entity.position.x,10) / GAME.resolution);
row = parseInt(parseInt(entity.position.y,10) / GAME.resolution);
if(GAME.grid[column] && GAME.grid[column][row]){
GAME.grid[column][row].state = 0;
}
entity.applyForce(
entity.walkForce().multiply(1)
);
entity.applyForce(
entity.separate(GAME.entitiesStatic.entities).multiply(1.2)
);
// follow mouse
entity.applyForce(
entity.seekForce(GAME.mouse).multiply(1.1)
);
entity.update();
}
let entitiesToIterate = entities.concat(GAME.entitiesStatic.entities);
window.z = entitiesToIterate;
for (i = 0; i < n; ++i) {
var p = particles[i];
p[0] += p.vx; if (p[0] < 0) p[0] += width; else if (p[0] > width) p[0] -= width;
p[1] += p.vy; if (p[1] < 0) p[1] += height; else if (p[1] > height) p[1] -= height;
p.vx += 0.1 * (Math.random() - .5) - 0.01 * p.vx;
p.vy += 0.1 * (Math.random() - .5) - 0.01 * p.vy;
// use entity positions
p[0] = entitiesToIterate[i].position.x;
p[1] = entitiesToIterate[i].position.y;
}
context.clearRect(0, 0, width, height);
var triangles = voro.triangles(particles),
links = voro.links(particles);
triangles.forEach(function(t) {
context.beginPath();
var r = drawRoundedTriangle(t[0][0], t[0][1], t[1][0], t[1][1], t[2][0], t[2][1], radius);
context.fillStyle = fill(Math.sqrt(r / 50));
context.fill();
});
context.beginPath();
triangles.forEach(function(t) {
drawRoundedTriangle(t[0][0], t[0][1], t[1][0], t[1][1], t[2][0], t[2][1], radius);
});
context.lineWidth = 5;
context.strokeStyle = "#ddd";
context.stroke();
/**
context.beginPath();
links.forEach(function(l) {
drawLink(l.source[0], l.source[1], l.target[0], l.target[1]);
});
context.lineWidth = 1;
context.strokeStyle = "#aaa";
context.stroke();
context.beginPath();
particles.forEach(function(p) { drawParticle(p[0], p[1]); });
context.fillStyle = "#000";
context.fill();
*/
});
function drawParticle(x, y) {
context.moveTo(x + 2, y);
context.arc(x, y, 2, 0, 2 * Math.PI);
}
function drawTriangle(x0, y0, x1, y1, x2, y2) {
context.moveTo(x0, y0);
context.lineTo(x1, y1);
context.lineTo(x2, y2);
context.closePath();
}
function drawLink(x0, y0, x1, y1) {
context.moveTo(x0, y0);
context.lineTo(x1, y1);
}
function drawRoundedTriangle(x0, y0, x1, y1, x2, y2, r) {
var circle = inscribeTriangle(x0, y0, x1, y1, x2, y2);
if (circle.radius <= r) {
context.moveTo(circle[0] + circle.radius, circle[1]);
context.arc(circle[0], circle[1], circle.radius, 0, 2 * Math.PI);
} else {
var p0 = closestPoint(circle[0], circle[1], x0, y0, x1, y1),
p1 = closestPoint(circle[0], circle[1], x1, y1, x2, y2),
p2 = closestPoint(circle[0], circle[1], x2, y2, x0, y0);
context.moveTo(p0[0], p0[1]);
context.arcTo(x1, y1, p1[0], p1[1], r);
context.arcTo(x2, y2, p2[0], p2[1], r);
context.arcTo(x0, y0, p0[0], p0[1], r);
context.closePath();
}
return circle.radius;
}
function closestPoint(x2, y2, x0, y0, x1, y1) {
var x10 = x1 - x0, y10 = y1 - y0,
x20 = x2 - x0, y20 = y2 - y0,
t = (x20 * x10 + y20 * y10) / (x10 * x10 + y10 * y10);
return [x0 + t * x10, y0 + t * y10];
}
function inscribeTriangle(x0, y0, x1, y1, x2, y2) {
var x01 = x0 - x1, y01 = y0 - y1,
x02 = x0 - x2, y02 = y0 - y2,
x12 = x1 - x2, y12 = y1 - y2,
l01 = Math.sqrt(x01 * x01 + y01 * y01),
l02 = Math.sqrt(x02 * x02 + y02 * y02),
l12 = Math.sqrt(x12 * x12 + y12 * y12),
k0 = l01 / (l01 + l02),
k1 = l12 / (l12 + l01),
center = segmentIntersection(x0, y0, x1 - k0 * x12, y1 - k0 * y12, x1, y1, x2 + k1 * x02, y2 + k1 * y02);
center.radius = Math.sqrt((l02 + l12 - l01) * (l12 + l01 - l02) * (l01 + l02 - l12) / (l01 + l02 + l12)) / 2;
return center;
}
function segmentIntersection(x0, y0, x1, y1, x2, y2, x3, y3) {
var x02 = x0 - x2, y02 = y0 - y2,
x10 = x1 - x0, y10 = y1 - y0,
x32 = x3 - x2, y32 = y3 - y2,
t = (x32 * y02 - y32 * x02) / (y32 * x10 - x32 * y10);
return [x0 + t * x10, y0 + t * y10];
}
</script>
</body>
</html>