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
forked from erikhazzard's block: voronoi+boids-delaunay-1
forked from erikhazzard's block: voronoi+boids-delaunay-2-mesh
forked from erikhazzard's block: voronoi+boids-combined-delaunay-3
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: 7,
mass: 10
}));
}
GAME.entitiesStatic = new Entities();
var numStaticEntities = 10;
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 voronoi = 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;
}
/**
* Draw
*/
context.clearRect(0, 0, width, height);
context.globalAlpha = 0.3;
/* Draw triangles */
var triangles = voro.triangles(particles),
links = voro.links(particles);
/** draw it */
//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();
/** Draw links */
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();
/** Draw dots */
//context.beginPath();
//particles.forEach(function(p) { drawParticle(p[0], p[1]); });
//context.fillStyle = "#000";
//context.fill();
context.globalAlpha = 0.9;
// voronoi draw
var topology = computeTopology(voronoi(particles));
/** 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,1)";
context.fill();
context.lineWidth = 1.5;
context.lineJoin = "round";
context.strokeStyle = "rgba(50,100,150,1)";
context.stroke();
});
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];
}
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>