Built with blockbuilder.org
forked from erikhazzard's block: voronoi+boids-1-grey-transparent
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 = 20;
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: 3,
mass: 10
}));
}
GAME.entitiesStatic = new Entities();
var numStaticEntities = 3;
for(var i=0;i<numStaticEntities; i++){
GAME.entitiesStatic.add(
new Entity({
position: new Vector(width/2, height/2),
mass: 20,
maxSpeed: 2
})
);
}
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;
/**
* Combined voronoi + boids
*
*/
var canvas = document.querySelector("canvas"),
context = canvas.getContext("2d"),
width = canvas.width,
height = canvas.height;
var n = TOTAL_ENTITIES,
particles = new Array(n);
var voronoi = d3.voronoi()
.extent([[-1, -1], [width + 1, height + 1]]);
for (var i = 0; i < n; ++i) particles[i] = {0: Math.random() * width, 1: Math.random() * height, vx: 0, vy: 0};
/**
*
* Tick function
*
*/
var timer = 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 (var i = 0; i < n; ++i) {
var p = particles[i];
p[0] += p.vx; if (p[0] < 0) p[0] = p.vx *= -1; else if (p[0] > width) p[0] = width + (p.vx *= -1);
p[1] += p.vy; if (p[1] < 0) p[1] = p.vy *= -1; else if (p[1] > height) p[1] = height + (p.vy *= -1);
p.vx += 0.1 * (Math.random() - 0.5) - 0.01 * p.vx;
p.vy += 0.1 * (Math.random() - 0.5) - 0.01 * p.vy;
// use entity positions
p[0] = entitiesToIterate[i].position.x;
p[1] = entitiesToIterate[i].position.y;
}
var topology = computeTopology(voronoi(particles));
/**
* Draw
*/
// context.clearRect(0, 0, width, height);
context.rect(0, 0, width, height);
context.stroke();
context.fillStyle = 'rgba(255, 255, 255, 1)';
context.fill();
// context.globalAlpha = 0.1;
/** Draw lines */
//context.beginPath();
//renderMultiLineString(context, topojson.mesh(topology, topology.objects.voronoi, function(a, b) { return a !== b; }));
//context.strokeStyle = "rgba(0,0,0,0.5)";
//context.lineWidth = 0.2;
//context.stroke();
/** Draw dots */
//particles.forEach(function(p, i) {
//context.beginPath();
//context.arc(p[0], p[1], 2.5, 0, 2 * Math.PI);
//context.fillStyle = i & 1 ? "rgba(255,0,0,1)" : "rgba(0,0,0,0.6)";
//context.fill();
//});
/** Draw topology */
context.beginPath();
renderMultiPolygon(context, topojson.merge(topology, topology.objects.voronoi.geometries.filter(function(d, i) { return i & 1; })));
context.fillStyle = "rgba(100,150,200,0.1)";
context.fillStyle = "rgba(150,150,150,0.001)";
context.fill();
context.lineWidth = 2.5;
context.lineJoin = "round";
context.strokeStyle = "rgba(50,100,150,1)";
context.strokeStyle = "rgba(100,100,100,0.2)";
context.stroke();
/**
context.shadowColor = "#a0a0a0"
context.shadowOffsetX = 1;
context.shadowOffsetY = 0;
context.shadowBlur = 10;
*/
});
function computeTopology(diagram) {
var cells = diagram.cells,
arcs = [],
arcIndex = -1,
arcIndexByEdge = {};
return {
objects: {
voronoi: {
type: "GeometryCollection",
geometries: cells.map(function(cell) {
var cell,
site = cell.site,
halfedges = cell.halfedges,
cellArcs = [],
clipArc;
halfedges.forEach(function(halfedge) {
var edge = diagram.edges[halfedge];
if (edge.right) {
var l = edge.left.index,
r = edge.right.index,
k = l + "," + r,
i = arcIndexByEdge[k];
if (i == null) arcs[i = arcIndexByEdge[k] = ++arcIndex] = edge;
cellArcs.push(site === edge.left ? i : ~i);
clipArc = null;
} else if (clipArc) { // Coalesce border edges.
if (edge.left) edge = edge.slice(); // Copy-on-write.
clipArc.push(edge[1]);
} else {
arcs[++arcIndex] = clipArc = edge;
cellArcs.push(arcIndex);
}
});
// Ensure the last point in the polygon is identical to the first point.
var firstArcIndex = cellArcs[0],
lastArcIndex = cellArcs[cellArcs.length - 1],
firstArc = arcs[firstArcIndex < 0 ? ~firstArcIndex : firstArcIndex],
lastArc = arcs[lastArcIndex < 0 ? ~lastArcIndex : lastArcIndex];
lastArc[lastArcIndex < 0 ? 0 : lastArc.length - 1] = firstArc[firstArcIndex < 0 ? firstArc.length - 1 : 0].slice();
return {
type: "Polygon",
data: site.data,
arcs: [cellArcs]
};
})
}
},
arcs: arcs
};
}
function renderMultiLineString(context, line) {
line.coordinates.forEach(function(line) {
line.forEach(function(point, i) {
if (i) context.lineTo(point[0], point[1]);
else context.moveTo(point[0], point[1]);
});
});
}
function renderMultiPolygon(context, polygon) {
polygon.coordinates.forEach(function(polygon) {
polygon.forEach(function(ring) {
ring.forEach(function(point, i) {
if (i) context.lineTo(point[0], point[1]);
else context.moveTo(point[0], point[1]);
});
});
});
}
</script>
</body>
</html>