filling circle with hexagons (different approach) - javascript

As my previous approach doesn't seem to work and a solution would be rather complex, I have decided to try another approach which might be a little bit simpler.
This time, before the code draws any hexagon, it has to determine as how many rows and columns can fit in the pre-defined circle and based on this outcome it then starts drawing all the hexagons.
So far it kind of work, but as in my previous approach, there are times when the hexes are overlapping , or leaving a large gap in the lower part of the circle.
Another concern is , how do I format these hexagons into a grid?
Note, there is a small slider under the canvas, that lets you increase/decrease circle's radius and redraw the hexagons.
var c_el = document.getElementById("myCanvas");
var ctx = c_el.getContext("2d");
var canvas_width = c_el.clientWidth;
var canvas_height = c_el.clientHeight;
var circle = {
r: 120, /// radius
pos: {
x: (canvas_width / 2),
y: (canvas_height / 2)
}
}
var hexagon = {
r: 20,
pos:{
x: 0,
y: 0
}
}
var hex_w = hexagon.r * 2;
var hex_h = Math.floor( Math.sqrt(3) * hexagon.r );
var hex_s = (3/2) * hexagon.r;
fill_CircleWithHex( circle );
function fill_CircleWithHex(circle){
drawCircle( circle );
var c_h = circle.r * 2; /// circle height ////
var c_w = c_h; //// circle width /////
var max_hex_H = Math.round( c_h / hex_h );
var row_sizes = []
for(var row= 0; row< max_hex_H; row++){
var d = circle.r - ( row* hex_h); //// distance from circle's center to the row's chord ////
var c = 2 * (Math.sqrt((circle.r*circle.r) - (d * d))); /// length of the row's chord ////
var row_length = Math.floor(c / (hexagon.r * 3));
row_sizes.push( row_length )
}
console.log("circle_r : "+circle.r);
console.log("hex_r : "+hexagon.r);
console.log("max_hex_H : "+max_hex_H);
console.log("max_hex_W : ", row_sizes)
for(var row = 0; row < row_sizes.length; row++){
var max_hex_W = row_sizes[row];
var x_offset = Math.floor((c_w - (max_hex_W * hex_w)) / 2);
for(var col = 1; col < max_hex_W; col++){
hexagon.pos.x = (col * hex_w) + (circle.pos.x - circle.r) + x_offset ;
hexagon.pos.y = (row * hex_h) + (circle.pos.y - circle.r);
ctx.fillText(row+""+col, hexagon.pos.x - 6, hexagon.pos.y+4);
drawHexagon(hexagon)
}
}
}
function drawHexagon(hex){
var angle_deg, angle_rad, cor_x, cor_y;
ctx.beginPath();
for(var c=0; c <= 5; c++){
angle_deg = 60 * c;
angle_rad = (Math.PI / 180) * angle_deg;
cor_x = hex.r * Math.cos(angle_rad); //// corner_x ///
cor_y = hex.r* Math.sin(angle_rad); //// corner_y ///
if(c === 0){
ctx.moveTo(hex.pos.x+ cor_x, hex.pos.y+cor_y);
}else{
ctx.lineTo(hex.pos.x+cor_x, hex.pos.y+cor_y);
}
}
ctx.closePath();
ctx.stroke();
}
function drawCircle( circle ){
ctx.beginPath();
ctx.arc(circle.pos.x, circle.pos.y, circle.r, 0, 2 * Math.PI);
ctx.stroke();
}
$(function() {
$( "#slider" ).slider({
max: 200,
min:0,
value:100,
create: function( event, ui ) {
$("#value").html( $(this).slider('value') );
},
change: function( event, ui ) {
$("#value").html(ui.value);
},
slide: function( event, ui){
$("#value").html(ui.value);
circle.r = ui.value;
ctx.clearRect(0,0, canvas_width, canvas_height);
fill_CircleWithHex(circle);
}
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<link href="https://cdnjs.cloudflare.com/ajax/libs/jqueryui/1.11.4/jquery-ui.min.css" rel="stylesheet"/>
<script src="https://ajax.googleapis.com/ajax/libs/jqueryui/1.11.4/jquery-ui.min.js"></script>
<canvas id="myCanvas" width="350" height="250" style="border:1px solid #d3d3d3;"> </canvas>
<div style="width: 200px; height: 40px;">
<div id="slider" style="position:relative; width: 150px; top: 4px;float: left;"></div> <div id="value" style="float: left;"> 0 </div>
</div>

The following solves the packing problem for a regular honeycomb structure centered on the circle's midpoint. Regular means:
the set of all hexagons is symmetric under 60 deg rotations around the circle's center.
The coordinates of the individual hexagons represent the ordinal number of the hexagon shell countered from the center and the clockwise sequence number starting at high noon.
As the circle widens, new hexagon shells do not necessarily get filled as a whole. Though the degree of freedom to fill the outer shell partially produces an improved solution, it is still not optimal. Relaxing the regularity to rotational symmetries wrt other angles than 60 deg ( namely 120 and 180 deg ) will permit a higher coverage of the circle's interior.
I shall look into the math behind that for the next revision of this code (and possibly find a theorem to prove rotational symmetry around the circle'smidpoint is a necessary condition for optimality).
var c_el;
var ctx;
var canvas_width;
var canvas_height;
var circle;
var hexagon;
var hex_w;
var hex_h;
var hex_s;
var delta;
function drawHexagonAt ( po_ctr_hex, pn_circle, pn_sector ) {
var cur
;
cur = { x: po_ctr_hex.x - 0.5 * hexagon.r, y: po_ctr_hex.y - delta };
ctx.beginPath();
ctx.moveTo(cur.x, cur.y);
cur.x = cur.x + hexagon.r;
cur.y = cur.y;
ctx.lineTo(cur.x, cur.y);
cur.x = cur.x + hexagon.r / 2;
cur.y = cur.y + delta;
ctx.lineTo(cur.x, cur.y);
cur.x = cur.x - hexagon.r / 2;
cur.y = cur.y + delta;
ctx.lineTo(cur.x, cur.y);
cur.x = cur.x - hexagon.r;
cur.y = cur.y;
ctx.lineTo(cur.x, cur.y);
cur.x = cur.x - hexagon.r / 2;
cur.y = cur.y - delta;
ctx.lineTo(cur.x, cur.y);
cur.x = cur.x + hexagon.r / 2;
cur.y = cur.y - delta;
ctx.lineTo(cur.x, cur.y);
ctx.closePath();
ctx.stroke();
cur.x = cur.x + hexagon.r / 2;
cur.y = cur.y + delta;
ctx.fillText(pn_circle + "/" + pn_sector, cur.x-6, cur.y+4);
} // drawHexagonAt
function fill_CircleWithHex(circle){
drawCircle( circle );
var radacc2;
var iter = 0;
var sector = 0;
var i, j;
var ctr = { x: circle.pos.x , y: circle.pos.y };
var cur = { x: 0 , y: 0 };
delta = Math.floor(Math.sqrt(3) * 0.5 * hexagon.r);
radacc2 = hexagon.r * hexagon.r;
while ( (radacc2 < circle.r * circle.r) ) {
cur.x = ctr.x;
cur.y = ctr.y - iter * 2 * delta;
if (iter === 0) {
drawHexagonAt ( cur, 0, 0 );
}
else {
for ( var i=0; i < 6; i++ ) {
// j-loops -- next honeycomb
sector = 0;
for ( var j=0; j < iter; j++ ) {
cur.x = cur.x + 1.5 * hexagon.r;
cur.y = cur.y + delta;
drawHexagonAt ( cur, iter, sector++ );
}
for ( var j=0; j < iter; j++ ) {
cur.x = cur.x;
cur.y = cur.y + 2 * delta;
drawHexagonAt ( cur, iter, sector++ );
}
for ( var j=0; j < iter; j++ ) {
cur.x = cur.x - 1.5 * hexagon.r;
cur.y = cur.y + delta;
drawHexagonAt ( cur, iter, sector++ );
}
for ( var j=0; j < iter; j++ ) {
cur.x = cur.x - 1.5 * hexagon.r;
cur.y = cur.y - delta;
drawHexagonAt ( cur, iter, sector++ );
}
for ( var j=0; j < iter; j++ ) {
cur.x = cur.x;
cur.y = cur.y - 2 * delta;
drawHexagonAt ( cur, iter, sector++ );
}
for ( var j=0; j < iter; j++ ) {
cur.x = cur.x + 1.5 * hexagon.r;
cur.y = cur.y - delta;
drawHexagonAt ( cur, iter, sector++ );
}
} // i-loop -- meta-honeycomb
} // if -- Iteration 1 vs. n > 1
// radacc update
iter++;
radacc2 = ((2*iter + 1) * delta) * ((2*iter + 1) * delta) + hexagon.r * hexagon.r / 4;
} // while -- komplette Shells
//
// Partielle Shells
//
var proceed;
do {
cur.x = ctr.x;
cur.y = ctr.y - iter * 2 * delta;
proceed = false;
for ( var i=0; i < 6; i++ ) {
// j-loops -- next honeycomb
sector = 0;
for ( var j=0; j < iter; j++ ) {
cur.x = cur.x + 1.5 * hexagon.r;
cur.y = cur.y + delta;
sector++
if ( Math.sqrt ( ( cur.x - ctr.x) * ( cur.x - ctr.x) + ( cur.y - ctr.y) * ( cur.y - ctr.y) ) + hexagon.r < circle.r ) {
drawHexagonAt ( cur, iter, sector );
proceed = true;
}
}
for ( var j=0; j < iter; j++ ) {
cur.x = cur.x;
cur.y = cur.y + 2 * delta;
sector++
if ( Math.sqrt ( ( cur.x - ctr.x) * ( cur.x - ctr.x) + ( cur.y - ctr.y) * ( cur.y - ctr.y) ) + hexagon.r < circle.r ) {
drawHexagonAt ( cur, iter, sector );
proceed = true;
}
}
for ( var j=0; j < iter; j++ ) {
cur.x = cur.x - 1.5 * hexagon.r;
cur.y = cur.y + delta;
sector++
if ( Math.sqrt ( ( cur.x - ctr.x) * ( cur.x - ctr.x) + ( cur.y - ctr.y) * ( cur.y - ctr.y) ) + hexagon.r < circle.r ) {
drawHexagonAt ( cur, iter, sector );
proceed = true;
}
}
for ( var j=0; j < iter; j++ ) {
cur.x = cur.x - 1.5 * hexagon.r;
cur.y = cur.y - delta;
sector++
if ( Math.sqrt ( ( cur.x - ctr.x) * ( cur.x - ctr.x) + ( cur.y - ctr.y) * ( cur.y - ctr.y) ) + hexagon.r < circle.r ) {
drawHexagonAt ( cur, iter, sector );
proceed = true;
}
}
for ( var j=0; j < iter; j++ ) {
cur.x = cur.x;
cur.y = cur.y - 2 * delta;
sector++
if ( Math.sqrt ( ( cur.x - ctr.x) * ( cur.x - ctr.x) + ( cur.y - ctr.y) * ( cur.y - ctr.y) ) + hexagon.r < circle.r ) {
drawHexagonAt ( cur, iter, sector );
proceed = true;
}
}
for ( var j=0; j < iter; j++ ) {
cur.x = cur.x + 1.5 * hexagon.r;
cur.y = cur.y - delta;
sector++
if ( Math.sqrt ( ( cur.x - ctr.x) * ( cur.x - ctr.x) + ( cur.y - ctr.y) * ( cur.y - ctr.y) ) + hexagon.r < circle.r ) {
drawHexagonAt ( cur, iter, sector );
proceed = true;
}
}
} // i-loop -- meta-honeycomb
iter++;
} while (proceed && (iter < 15));
} // fill_CircleWithHex
function drawCircle( circle ){
ctx.beginPath();
ctx.arc(circle.pos.x, circle.pos.y, circle.r, 0, 2 * Math.PI);
ctx.stroke();
}
$(function() {
$( "#slider" ).slider({
max: 200,
min:0,
value:100,
create: function( event, ui ) {
$("#value").html( $(this).slider('value') );
},
change: function( event, ui ) {
$("#value").html(ui.value);
},
slide: function( event, ui){
$("#value").html(ui.value);
circle.r = ui.value;
ctx.clearRect(0,0, canvas_width, canvas_height);
fill_CircleWithHex(circle);
}
});
});
$(document).ready(function () {
c_el = document.getElementById("myCanvas");
ctx = c_el.getContext("2d");
canvas_width = c_el.clientWidth;
canvas_height = c_el.clientHeight;
circle = {
r: 120, /// radius
pos: {
x: (canvas_width / 2),
y: (canvas_height / 2)
}
};
hexagon = {
r: 20,
pos:{
x: 0,
y: 0
}
};
hex_w = hexagon.r * 2;
hex_h = Math.floor( Math.sqrt(3) * hexagon.r );
hex_s = (3/2) * hexagon.r;
fill_CircleWithHex( circle );
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<link href="https://cdnjs.cloudflare.com/ajax/libs/jqueryui/1.11.4/jquery-ui.min.css" rel="stylesheet"/>
<script src="https://ajax.googleapis.com/ajax/libs/jqueryui/1.11.4/jquery-ui.min.js"></script>
<canvas id="myCanvas" width="350" height="250" style="border:1px solid #d3d3d3;"> </canvas>
<div style="width: 200px; height: 40px;">
<div id="slider" style="position:relative; width: 150px; top: 4px;float: left;"></div> <div id="value" style="float: left;"> 0 </div>
</div>

Spent some time on your code to pack the hex's. Its not perfect and am sure there is a better way to do this. Check it out if it helps, or if you could fix the hex's stepping out of the circle [now, there is an issue with calculation of row_sizes]. Maybe I can look at it again whenever I get time, or we can look at other ways to do this.
var c_el = document.getElementById("myCanvas");
var ctx = c_el.getContext("2d");
var canvas_width = c_el.clientWidth;
var canvas_height = c_el.clientHeight;
var circle = {
r: 120, /// radius
pos: {
x: (canvas_width / 2),
y: (canvas_height / 2)
}
}
var hexagon = {
r: 20,
pos:{
x: 0,
y: 0
}
}
var hex_w = hexagon.r * 3; /// added spacing
var hex_h = Math.floor( Math.sqrt(3) * hexagon.r / 2 ); /// added spacing
var hex_s = (3/2) * hexagon.r;
var hex_width = 33.4; //based on r = 20
fill_CircleWithHex( circle );
function fill_CircleWithHex(circle){
drawCircle( circle );
var c_h = circle.r * 2; /// circle height ////
var c_w = c_h; //// circle width /////
var max_hex_H = Math.round( c_h / ( hex_h ));
var row_sizes = []
for(var col= 0; col < max_hex_H; col++){
var d = circle.r - ( col * hex_h ); //// distance from circle's center to the row's chord ////
var c = 2 * (Math.sqrt((circle.r*circle.r) - (d * d))); /// length of the row's chord ////
row_sizes.push( Math.ceil(c / (hexagon.r * 3)) )
}
for(var row = 0; row < row_sizes.length; row++){
var max_hex_W = row_sizes[row];
console.log(hex_w);
var x_offset = Math.floor((c_w - (max_hex_W * hex_w))) + row%2 * hex_width - hex_width/2; // changed offset to define a zig zag
for(var col = 1; col < max_hex_W; col++){
hexagon.pos.x = (col * hex_w) + (circle.pos.x - circle.r) + x_offset ;
hexagon.pos.y = (row * 17.3) + (circle.pos.y - circle.r) ;
ctx.fillText(row+""+col, hexagon.pos.x - 6, hexagon.pos.y+4);
drawHexagon(hexagon)
}
}
}
function drawHexagon(hex){
var angle_deg, angle_rad, cor_x, cor_y;
ctx.beginPath();
for(var c=0; c <= 5; c++){
angle_deg = 60 * c;
angle_rad = (Math.PI / 180) * angle_deg;
cor_x = hex.r * Math.cos(angle_rad); //// corner_x ///
cor_y = hex.r* Math.sin(angle_rad); //// corner_y ///
if(c === 0){
ctx.moveTo(hex.pos.x+ cor_x, hex.pos.y+cor_y);
}else{
ctx.lineTo(hex.pos.x+cor_x, hex.pos.y+cor_y);
}
}
ctx.closePath();
ctx.stroke();
}
function drawCircle( circle ){
ctx.beginPath();
ctx.arc(circle.pos.x, circle.pos.y, circle.r, 0, 2 * Math.PI);
ctx.stroke();
}
$(function() {
$( "#slider" ).slider({
max: 200,
min:0,
value:100,
create: function( event, ui ) {
$("#value").html( $(this).slider('value') );
},
change: function( event, ui ) {
$("#value").html(ui.value);
},
slide: function( event, ui){
$("#value").html(ui.value);
circle.r = ui.value;
ctx.clearRect(0,0, canvas_width, canvas_height);
fill_CircleWithHex(circle);
}
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<link href="https://cdnjs.cloudflare.com/ajax/libs/jqueryui/1.11.4/jquery-ui.min.css" rel="stylesheet"/>
<script src="https://ajax.googleapis.com/ajax/libs/jqueryui/1.11.4/jquery-ui.min.js"></script>
<canvas id="myCanvas" width="350" height="250" style="border:1px solid #d3d3d3;"> </canvas>
<div style="width: 200px; height: 40px;">
<div id="slider" style="position:relative; width: 150px; top: 4px;float: left;"></div> <div id="value" style="float: left;"> 0 </div>
</div>

The short answer: there is no easy way to do this. You have too many special cases. To illustrate that, start simple with 1, 2, 3, 4, 6 and 7 hexagons, draw the minimum circle that will fit over them, and make note where the center of the circle ends up.
As you can see the center of the circle moves around quite a bit. It can end up in the middle of a hexagon, on a vertex or a junction.
From then on it only gets worse.
The closest I could find on this problem is this page.
EDIT: You may want to check out the following blog page for a very comprehensive treatment on hexagons in programming.
http://www.redblobgames.com/grids/hexagons/

Related

Can't line ellipse with the coordinates of a vector

I'm trying to line up some ellipse with the endpoint of a line that moves and rotates, and the end goal is to be able to calculate the coordinates of said endpoint. I can calculate the initial position, but as soon as I move the car all of the ellipses go in the opposite direction. I'm not great at geometry and I can't figure out why it's doing this or how to fix it.
What I have so far:
sketch.js
// Declares global variables
const h = window.innerHeight;
const w = window.innerWidth;
var car;
var borders = [];
var pos = {
x: w / 2,
y: h / 2,
angle: 0
}
function setup () {
// Creates the canvas
background ( '#000000' );
createCanvas ( w, h );
angleMode ( DEGREES );
rectMode ( CENTER );
stroke ( 255 );
// Creates some borders
borders.push ( new Border ( 0, height / 2, width, height / 2 ) );
// Creates the car
car = new Car();
}
function draw () {
// Clears the canvas
background ( '#000000' );
fill("white");
// Displays the borders
for ( var border of borders ) {
border.show();
}
// Moves the car
if ( keyIsDown ( UP_ARROW ) ) {
car.forward();
}
if ( keyIsDown ( DOWN_ARROW ) ) {
car.backward();
}
// Checks if the car is intersecting
const intersection = car.intersects ( borders );
car.emit(borders);
if ( intersection ) {
fill("red");
}
// Displays the car on the canvas
translate(pos.x, pos.y);
rotate(pos.angle);
car.show();
}
car.js
class Car {
// Defines the class
constructor ( ) {
this.pos = createVector ( 0, 0 );
this.width = 20;
this.length = 40;
this.speed = 0;
this.rays = [];
// Creates the car's rays
for ( var x = 0; x < 360; x += 20 ) {
this.rays.push ( new Ray ( createVector ( this.pos.x, this.pos.y ), radians ( x ) ) );
}
}
// Displays the car on the canvas
show () {
// Displays the car
rect ( this.pos.x, this.pos.y, this.width, this.length );
// Displays the rays
for ( var ray of this.rays ) {
ray.show();
}
}
// Checks if any rays are intersecting a border
emit ( borders ) {
// Loops through all the rays
for ( var ray of this.rays ) {
// Loops through all the borders
for ( var border of borders ) {
const intersection = ray.intersects ( border );
if ( intersection ) {
console.log(intersection);
}
}
}
}
// Moves forward
forward () {
pos.y -= cos(pos.angle) * 4;
pos.x += sin(pos.angle) * 4;
// Steers left
if ( keyIsDown ( LEFT_ARROW ) ) {
pos.angle -= 3;
}
// Steers right
if ( keyIsDown ( RIGHT_ARROW ) ) {
pos.angle += 3;
}
}
// Moves backward
backward () {
pos.y += cos(pos.angle) * 4;
pos.x -= sin(pos.angle) * 4;
// Steers left
if ( keyIsDown ( LEFT_ARROW ) ) {
pos.angle -= 3;
}
// Steers right
if ( keyIsDown ( RIGHT_ARROW ) ) {
pos.angle += 3;
}
}
// Checks if the car is intersecting any borders
intersects ( borders ) {
// Calculates the car's corners
var corners = [
createVector ( pos.x - ( this.width / 2 ), pos.y - ( this.length / 2 ) ),
createVector ( pos.x + ( this.width / 2 ), pos.y - ( this.length / 2 ) ),
createVector ( pos.x + ( this.width / 2 ), pos.y + ( this.length / 2 ) ),
createVector ( pos.x - ( this.width / 2 ), pos.y + ( this.length / 2 ) )
];
var sides = [
[
corners[0],
corners[1]
],
[
corners[1],
corners[2]
],
[
corners[2],
corners[3]
],
[
corners[3],
corners[0]
]
];
// Loops through each side
for ( var side of sides ) {
// Loops through each border
for ( var border of borders ) {
var x1 = side[0].x;
var y1 = side[0].y;
var x2 = side[1].x;
var y2 = side[1].y;
const x3 = border.x.x;
const y3 = border.x.y;
const x4 = border.y.x;
const y4 = border.y.y;
// Rotates the corners relative to the car
var tempX1 = x1 - pos.x;
var tempY1 = y1 - pos.y;
var tempX2 = x2 - pos.x;
var tempY2 = y2 - pos.y;
var rotatedX1 = tempX1 * cos ( pos.angle ) - tempY1 * sin ( pos.angle );
var rotatedY1 = tempX1 * sin ( pos.angle ) + tempY1 * cos ( pos.angle );
var rotatedX2 = tempX2 * cos ( pos.angle ) - tempY2 * sin ( pos.angle );
var rotatedY2 = tempX2 * sin ( pos.angle ) + tempY2 * cos ( pos.angle );
x1 = rotatedX1 + pos.x;
y1 = rotatedY1 + pos.y;
x2 = rotatedX2 + pos.x;
y2 = rotatedY2 + pos.y;
// Checks if the car is intersecting
const d = ( x1 - x2 ) * ( y3 - y4 ) - ( y1 - y2 ) * ( x3 - x4 );
const t = ( x1 - x3 ) * ( y3 - y4 ) - ( y1 - y3 ) * ( x3 - x4 ) / d;
if ( 0 <= t && t <= 1 ) {
return createVector ( x1 + t * ( x2 - x1 ), y1 + t * ( y2 - y1 ) );
}
}
}
}
}
ray.js
class Ray {
// Defines the class
constructor ( position, angle ) {
this.pos = position;
this.angle = angle;
this.dir = p5.Vector.fromAngle ( angle );
}
// Checks if the ray is intersecting a border
intersects ( border ) {
// const endpoints = [
// ];
var x1 = pos.x;
var y1 = pos.y;
var x2 = this.dir.x * 100;
var y2 = this.dir.y * 100;
const x3 = border.x.x;
const y3 = border.x.y;
const x4 = border.y.x;
const y4 = border.y.y;
// Rotates the corners relative to the car
var tempX2 = x2 - pos.x;
var tempY2 = y2 - pos.y;
var rotatedX2 = tempX2 * cos ( pos.angle ) - tempY2 * sin ( pos.angle );
var rotatedY2 = tempX2 * sin ( pos.angle ) + tempY2 * cos ( pos.angle );
x2 = rotatedX2 + pos.x;
y2 = rotatedY2 + pos.y;
/* This is where I'm having difficulty */
push();
translate(pos.x, pos.y);
ellipse(x2, y2, 10);
pop();
// const den = ( ( x1 - x2 ) * ( y3 - y4 ) - ( y1 - y2 ) * ( x3 - x4 ) );
// if ( den == 0 ) {
// console.log(1);
// return;
// }
// var t = ( ( x1 - x3 ) * ( y3 - y4 ) - ( y1 - y3 ) * ( x3 - x4 ) ) / den;
// var u = ( ( x2 - x1 ) * ( y1 - y3 ) - ( y2 - y1 ) * ( x1 - x3 ) ) / den;
// if ( 0 <= t && t <= 1 ) {
// const x = x1 + t * ( x2 - x1 );
// const y = y1 + t * ( y2 - y1 );
// // return createVector ( x, y );
// ellipse ( x, y, 10 );
// }
// return;
}
// Displays the ray on the canvas
show () {
push();
translate(this.pos.x, this.pos.y);
line(0, 0, this.dir.x * 100, this.dir.y * 100);
pop();
}
}
border.js
class Border {
// Defines the class
constructor ( x1, y1, x2, y2 ) {
this.x = createVector ( x1, y1 );
this.y = createVector ( x2, y2 );
}
show () {
stroke ( 255 );
line ( this.x.x, this.x.y, this.y.x, this.y.y );
}
}
My interpretation is that in lines 31-36 of ray.js, you're trying to rotate the point x2, y2 by pos.angle. You actually don't need to do any trig in order to do that.
In the trig, you subtracted off pos from this.dir. But this.dir was already a vector whose base was at the position of the car, so the coordinates of this.dir were already relative to the position of the car. Here is one solution, where x2 and y2 are initialized with the rotation:
var x2 = this.dir.x * 100 * cos ( pos.angle ) -
this.dir.y * 100 * sin ( pos.angle );
var y2 = this.dir.x * 100 * sin ( pos.angle ) +
this.dir.y * 100 * cos ( pos.angle );
As I said above, though, you don't have to do any trig. You can make use of the .rotate() method:
var x2 = this.dir.copy().rotate(radians(pos.angle)).x * 100;
var y2 = this.dir.copy().rotate(radians(pos.angle)).y * 100;
NOTE: you need to convert to radians when using .rotate() and other vector methods because angleMode() does not affect vectors.
In both of these solutions, you should get rid of lines 31-36.
I think you might run into problems, however, when you try to apply this. Since x2 and y2 are relative to the car's position, they are not the actual endpoints of the lines drawn on the screen. Rather, they are in a circle of points up by the top left corner. If you were to try to test whether the line segment between x1, y1 and x2, y2 intersects with the line segment between x3, y3 and x4, y4, you would get that it only intersects when the border is between pos and the top left corner.
I think what you're actually looking for is
var x2 = this.dir.copy().rotate(radians(pos.angle)).x + pos.x;
var y2 = this.dir.copy().rotate(radians(pos.angle)).y + pos.y;
NOTE: you would also have to get rid of the translation when you display the ellipses in order for this to look right.
When I uncomment your other code in ray.js, it behaves better in this last version.
Additionally, you seem to multiply this.dir.x and this.dir.y by 100 every time you refer to them. You can omit this if you make this.dir 100 pixels long, which is built into the .fromAngle() method:
this.dir = p5.Vector.fromAngle(angle, 100);
will give you a vector in the proper direction with length 100.

Canvas Impulse Animation Effect

I'm studying the following canvas animation by Matei Copot.
Can someone explain how the "impulse"/shooting effect works, and how, say I can simplify the code to only have 3 stationary dots a, b, and c (while showing the impulse effect between a-> b and between b -> c)?
var w = c.width = window.innerWidth,
h = c.height = window.innerHeight,
ctx = c.getContext( '2d' ),
opts = {
range: 180,
baseConnections: 3,
addedConnections: 5,
baseSize: 5,
minSize: 1,
dataToConnectionSize: .4,
sizeMultiplier: .7,
allowedDist: 40,
baseDist: 40,
addedDist: 30,
connectionAttempts: 100,
dataToConnections: 1,
baseSpeed: .04,
addedSpeed: .05,
baseGlowSpeed: .4,
addedGlowSpeed: .4,
rotVelX: .003,
rotVelY: .002,
repaintColor: '#111',
connectionColor: 'hsla(200,60%,light%,alp)',
rootColor: 'hsla(0,60%,light%,alp)',
endColor: 'hsla(160,20%,light%,alp)',
dataColor: 'hsla(40,80%,light%,alp)',
wireframeWidth: .1,
wireframeColor: '#88f',
depth: 250,
focalLength: 250,
vanishPoint: {
x: w / 2,
y: h / 2
}
},
squareRange = opts.range * opts.range,
squareAllowed = opts.allowedDist * opts.allowedDist,
mostDistant = opts.depth + opts.range,
sinX = sinY = 0,
cosX = cosY = 0,
connections = [],
toDevelop = [],
data = [],
all = [],
tick = 0,
totalProb = 0,
animating = false,
Tau = Math.PI * 2;
ctx.fillStyle = '#222';
ctx.fillRect( 0, 0, w, h );
ctx.fillStyle = '#ccc';
ctx.font = '50px Verdana';
ctx.fillText( 'Calculating Nodes', w / 2 - ctx.measureText( 'Calculating Nodes' ).width / 2, h / 2 - 15 );
window.setTimeout( init, 4 ); // to render the loading screen
function init(){
connections.length = 0;
data.length = 0;
all.length = 0;
toDevelop.length = 0;
var connection = new Connection( 0, 0, 0, opts.baseSize );
connection.step = Connection.rootStep;
connections.push( connection );
all.push( connection );
connection.link();
while( toDevelop.length > 0 ){
toDevelop[ 0 ].link();
toDevelop.shift();
}
if( !animating ){
animating = true;
anim();
}
}
function Connection( x, y, z, size ){
this.x = x;
this.y = y;
this.z = z;
this.size = size;
this.screen = {};
this.links = [];
this.probabilities = [];
this.isEnd = false;
this.glowSpeed = opts.baseGlowSpeed + opts.addedGlowSpeed * Math.random();
}
Connection.prototype.link = function(){
if( this.size < opts.minSize )
return this.isEnd = true;
var links = [],
connectionsNum = opts.baseConnections + Math.random() * opts.addedConnections |0,
attempt = opts.connectionAttempts,
alpha, beta, len,
cosA, sinA, cosB, sinB,
pos = {},
passedExisting, passedBuffered;
while( links.length < connectionsNum && --attempt > 0 ){
alpha = Math.random() * Math.PI;
beta = Math.random() * Tau;
len = opts.baseDist + opts.addedDist * Math.random();
cosA = Math.cos( alpha );
sinA = Math.sin( alpha );
cosB = Math.cos( beta );
sinB = Math.sin( beta );
pos.x = this.x + len * cosA * sinB;
pos.y = this.y + len * sinA * sinB;
pos.z = this.z + len * cosB;
if( pos.x*pos.x + pos.y*pos.y + pos.z*pos.z < squareRange ){
passedExisting = true;
passedBuffered = true;
for( var i = 0; i < connections.length; ++i )
if( squareDist( pos, connections[ i ] ) < squareAllowed )
passedExisting = false;
if( passedExisting )
for( var i = 0; i < links.length; ++i )
if( squareDist( pos, links[ i ] ) < squareAllowed )
passedBuffered = false;
if( passedExisting && passedBuffered )
links.push( { x: pos.x, y: pos.y, z: pos.z } );
}
}
if( links.length === 0 )
this.isEnd = true;
else {
for( var i = 0; i < links.length; ++i ){
var pos = links[ i ],
connection = new Connection( pos.x, pos.y, pos.z, this.size * opts.sizeMultiplier );
this.links[ i ] = connection;
all.push( connection );
connections.push( connection );
}
for( var i = 0; i < this.links.length; ++i )
toDevelop.push( this.links[ i ] );
}
}
Connection.prototype.step = function(){
this.setScreen();
this.screen.color = ( this.isEnd ? opts.endColor : opts.connectionColor ).replace( 'light', 30 + ( ( tick * this.glowSpeed ) % 30 ) ).replace( 'alp', .2 + ( 1 - this.screen.z / mostDistant ) * .8 );
for( var i = 0; i < this.links.length; ++i ){
ctx.moveTo( this.screen.x, this.screen.y );
ctx.lineTo( this.links[ i ].screen.x, this.links[ i ].screen.y );
}
}
Connection.rootStep = function(){
this.setScreen();
this.screen.color = opts.rootColor.replace( 'light', 30 + ( ( tick * this.glowSpeed ) % 30 ) ).replace( 'alp', ( 1 - this.screen.z / mostDistant ) * .8 );
for( var i = 0; i < this.links.length; ++i ){
ctx.moveTo( this.screen.x, this.screen.y );
ctx.lineTo( this.links[ i ].screen.x, this.links[ i ].screen.y );
}
}
Connection.prototype.draw = function(){
ctx.fillStyle = this.screen.color;
ctx.beginPath();
ctx.arc( this.screen.x, this.screen.y, this.screen.scale * this.size, 0, Tau );
ctx.fill();
}
function Data( connection ){
this.glowSpeed = opts.baseGlowSpeed + opts.addedGlowSpeed * Math.random();
this.speed = opts.baseSpeed + opts.addedSpeed * Math.random();
this.screen = {};
this.setConnection( connection );
}
Data.prototype.reset = function(){
this.setConnection( connections[ 0 ] );
this.ended = 2;
}
Data.prototype.step = function(){
this.proportion += this.speed;
if( this.proportion < 1 ){
this.x = this.ox + this.dx * this.proportion;
this.y = this.oy + this.dy * this.proportion;
this.z = this.oz + this.dz * this.proportion;
this.size = ( this.os + this.ds * this.proportion ) * opts.dataToConnectionSize;
} else
this.setConnection( this.nextConnection );
this.screen.lastX = this.screen.x;
this.screen.lastY = this.screen.y;
this.setScreen();
this.screen.color = opts.dataColor.replace( 'light', 40 + ( ( tick * this.glowSpeed ) % 50 ) ).replace( 'alp', .2 + ( 1 - this.screen.z / mostDistant ) * .6 );
}
Data.prototype.draw = function(){
if( this.ended )
return --this.ended; // not sre why the thing lasts 2 frames, but it does
ctx.beginPath();
ctx.strokeStyle = this.screen.color;
ctx.lineWidth = this.size * this.screen.scale;
ctx.moveTo( this.screen.lastX, this.screen.lastY );
ctx.lineTo( this.screen.x, this.screen.y );
ctx.stroke();
}
Data.prototype.setConnection = function( connection ){
if( connection.isEnd )
this.reset();
else {
this.connection = connection;
this.nextConnection = connection.links[ connection.links.length * Math.random() |0 ];
this.ox = connection.x; // original coordinates
this.oy = connection.y;
this.oz = connection.z;
this.os = connection.size; // base size
this.nx = this.nextConnection.x; // new
this.ny = this.nextConnection.y;
this.nz = this.nextConnection.z;
this.ns = this.nextConnection.size;
this.dx = this.nx - this.ox; // delta
this.dy = this.ny - this.oy;
this.dz = this.nz - this.oz;
this.ds = this.ns - this.os;
this.proportion = 0;
}
}
Connection.prototype.setScreen = Data.prototype.setScreen = function(){
var x = this.x,
y = this.y,
z = this.z;
// apply rotation on X axis
var Y = y;
y = y * cosX - z * sinX;
z = z * cosX + Y * sinX;
// rot on Y
var Z = z;
z = z * cosY - x * sinY;
x = x * cosY + Z * sinY;
this.screen.z = z;
// translate on Z
z += opts.depth;
this.screen.scale = opts.focalLength / z;
this.screen.x = opts.vanishPoint.x + x * this.screen.scale;
this.screen.y = opts.vanishPoint.y + y * this.screen.scale;
}
function squareDist( a, b ){
var x = b.x - a.x,
y = b.y - a.y,
z = b.z - a.z;
return x*x + y*y + z*z;
}
function anim(){
window.requestAnimationFrame( anim );
ctx.globalCompositeOperation = 'source-over';
ctx.fillStyle = opts.repaintColor;
ctx.fillRect( 0, 0, w, h );
++tick;
var rotX = tick * opts.rotVelX,
rotY = tick * opts.rotVelY;
cosX = Math.cos( rotX );
sinX = Math.sin( rotX );
cosY = Math.cos( rotY );
sinY = Math.sin( rotY );
if( data.length < connections.length * opts.dataToConnections ){
var datum = new Data( connections[ 0 ] );
data.push( datum );
all.push( datum );
}
ctx.globalCompositeOperation = 'lighter';
ctx.beginPath();
ctx.lineWidth = opts.wireframeWidth;
ctx.strokeStyle = opts.wireframeColor;
all.map( function( item ){ item.step(); } );
ctx.stroke();
ctx.globalCompositeOperation = 'source-over';
all.sort( function( a, b ){ return b.screen.z - a.screen.z } );
all.map( function( item ){ item.draw(); } );
/*ctx.beginPath();
ctx.strokeStyle = 'red';
ctx.arc( opts.vanishPoint.x, opts.vanishPoint.y, opts.range * opts.focalLength / opts.depth, 0, Tau );
ctx.stroke();*/
}
window.addEventListener( 'resize', function(){
opts.vanishPoint.x = ( w = c.width = window.innerWidth ) / 2;
opts.vanishPoint.y = ( h = c.height = window.innerHeight ) / 2;
ctx.fillRect( 0, 0, w, h );
});
window.addEventListener( 'click', init );
canvas {
position: absolute;
top: 0;
left: 0;
}
<canvas id=c></canvas>
<!--
ALGORITHM:
structure:
- gen( x,y,z ):
- create node at x,y,z // blue
- append some children to list:
- within a certain distance to parent
- outside a certain distance from any node
- within a global distance
- if no children
- don't append any
- set as end node // green-ish
- gen( 0,0,0 ) // red
- while list has items
- gen( position of first item )
- remove first item
impulse behaviour:
- pick( node ):
- if node is end node
- pick( original node )
- else
- pick( random node from node children )
- pick( original node)
-->

Using HTML over canvas, nothing has worked

I'm trying to get my OVERLAY tag to appear on top of my canvas javascript. I've gone through all the questions on here but nothing has worked!
Please help! Code:
page.html
<!DOCTYPE html>
<html lang="en">
<head>
<link rel="stylesheet" href="style.css">
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<div id='container'>
<canvas id='canvas'></canvas>
<script src="test-script.js"></script>
<div id='overlay'>OVERLAY
<br></br>
OVERLAY
<br></br>
OVERLAY
</div>
</div>
</body>
</html>
style.css
#canvas {position: fixed; z-index: -1;}
#overlay {margin-top: -50px; z-index:0; position: relative;}
test-script.js
var ns = ns || {};
(function draw() {
var c;
var ctx;
var trails = [];
document.body.onload = function() {
c = document.getElementById( 'canvas' );
c.width = 2000;
c.height = 2000;
document.body.appendChild( c );
ctx = c.getContext( "2d" );
trails.push( new ns.trailer( [990000, 990000, 990000, 600000, 600000 ]));
// trails.push( new ns.trailer( [ 600000,600000,600000,600000,600000,600000,600000 ] ));
trails.push( new ns.trailer( [ 8000000, 8000000, 8000000, 990000, 990000 ] ));
document.onmousedown = reset;
reset();
setInterval( compute, 0 );
}
function reset() {
ctx.fillStyle = "white";
ctx.fillRect( 0,0,c.width,c.height );
for( var i =0; i < trails.length; i++ ) {
trails[ i ].reset();
}
}
function compute() {
for( var i =0; i < trails.length; i++ ) {
trails[ i ].compute( ctx );
}
}
})();
ns.trailer = function( colors ) {
this.points = [];
this.stroke = new ns.stroke( null, 100, 10, colors[ 0 ] );
this.colorIterator = 10;
this.colors = colors;
}
ns.trailer.prototype = {
reset : function() {
this.points = [];
this.width = document.body.offsetWidth;
this.height = document.body.offsetHeight;
this.radius = Math.max( this.width, this.height );
this.center = new ns.point( this.width / 2, this.height / 2 );
this.a0 = Math.random() * Math.PI * 2;
this.a1 = Math.random() * Math.PI * 2;
this.a2 = Math.random() * Math.PI * 2;
var mul = 1 + Math.random() * 2;
if( Math.random() > .5 ) mul *= 5;
else mul /= 2;
this.s0 = ( Math.random() - .5 ) * mul / 180 * Math.PI;
this.s1 = ( Math.random() - .5 ) * mul / 180 * Math.PI;
this.s2 = ( Math.random() - .5 ) * mul / 180 * Math.PI;
},
compute : function( ctx ) {
with( this ) {
a0 += s0;
a1 += s1;
a2 += s2;
var c = Math.cos( a0 ) * Math.cos( a1 ) * Math.cos( a2 );
var s = Math.sin( a0 ) * Math.sin( a1 ) * Math.sin( a2 );
points.push( new ns.point( center.x + c * radius,
center.y + s * radius ) );
if( points.length > 10 ) points.shift();
stroke.anchors = points;
stroke.draw( ctx );
var t = .5 + (Math.sin( new Date().getTime() * .001 ) * .5 );
stroke.color = colors[ Math.floor( t * colors.length ) ];
stroke.width = 25 + ( 1 - t ) * 50;
//stroke.strokeCount = 5 + t * 5;
stroke.strokeCount = 5;
}
}
}
ns.point = function( x,y ) {
this.x = x;
this.y = y;
}
ns.point.prototype = {
add : function( p ) {
return new ns.point( this.x + p.x, this.y + p.y );
}.
sub : function( p ) {
return new ns.point( this.x - p.x, this.y - p.y );
},
negate : function() {
this.x *= -1;
this.y *= -1;
return this;
},
clone : function() {
return new ns.point( this.x, this.y );
},
length : function() {
return Math.sqrt( this.x * this.x + this.y * this.y );
},
normalize : function ( scale ) {
scale = scale || 1;
var l = this.length();
this.x /= l;
this.x *= scale;
this.y /= l;
this.y *= scale;
return this;
}
}
ns.stroke = function( anchors, width, strokeCount, color ) {
this.anchors = anchors;
this.width = width;
this.strokeCount = strokeCount;
this.color = color;
}
ns.stroke.prototype = {
normal : function( p0, p1 ){
return new ns.point( -( p1.y - p0.y ), ( p1.x - p0.x ) );
},
draw : function( ctx ) {
if( this.anchors == undefined ) return;
var half = this.height * .5;
var p, c, n, pnorm, pln, prn, cnorm, cln, crn;
with( this ) {
for( var j = 0; j < strokeCount; j++ ) {
half = width * .5 * Math.random();
var col = ns.variation( color, 35 );
ctx.lineWidth = .1 + Math.random() * 2;
for( var i = 0; i < anchors.length - 2; i++ ) {
p = anchors[ i ];
c = anchors[ i+1 ];
n = anchors[ i+2 ];
pnorm = normal( p, c );
cnorm = normal( c, n );
half += ( Math.random() - .5 );
pnorm.normalize( half );
pln = p.add( pnorm );
pnorm.normalize( -half );
prn = p.add( pnorm );
half += ( Math.random() - .5 );
cnorm.normalize( half );
cln = c.add( cnorm );
cnorm.normalize( -half );
crn = c.add( cnorm );
ctx.beginPath();
ctx.strokeStyle = col;
ctx.moveTo( prn.x, prn.y );
ctx.lineTo( crn.x, crn.y );
ctx.stroke();
ctx.closePath();
ctx.beginPath();
ctx.strokeStyle = col;
ctx.moveTo( pln.x, pln.y );
ctx.lineTo( cln.x, cln.y );
ctx.stroke();
ctx.closePath();
}
}
}
}
}
ns.variation = function( color, amount ) {
amount = amount || 25;
var r = color >> 16 & 0xFF;
var g = color >> 8 & 0xFF;
var b = color & 0xFF;
r += Math.floor( ( Math.random() - .5 ) * amount );
g += Math.floor( ( Math.random() - .5 ) * amount );
b += Math.floor( ( Math.random() - .5 ) * amount );
r = r > 0xFF ? 0xFF : r < 0 ? 0 : r;
g = g > 0xFF ? 0xFF : g < 0 ? 0 : g;
b = b > 0xFF ? 0xFF : b < 0 ? 0 : b;
return "rgba("+r+','+g+','+b+','+Math.random()+');';
}
**I've added my Javascript code
You need to use absolute position. Also mention width and height to 100%. z-index should be higher to place element over other elements.
#canvas {
position: fixed;
}
#overlay {
z-index: 9;
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.3);
}
<div id='container'>
<canvas id='canvas'></canvas>
<div id='overlay'>OVERLAY
<br>OVERLAY
<br>OVERLAY
</div>
</div>
CSS
#container {
position: relative;
}
#overlay {
position:absolute;
top:50px;
left:150px;
z-index:10;
}
Adjust the "top" and "left" amounts to get OVERLAY positioned on top of the canvas.
It was the JavaScript.
Run my code snippet.
var ns = ns || {};
(function draw() {
var c;
var ctx;
var trails = [];
document.body.onload = function() {
c = document.getElementById( 'canvas' );
c.width = 2000;
c.height = 2000;
document.body.appendChild( c );
ctx = c.getContext( "2d" );
trails.push( new ns.trailer( [990000, 990000, 990000, 600000, 600000 ]));
// trails.push( new ns.trailer( [ 600000,600000,600000,600000,600000,600000,600000 ] ));
trails.push( new ns.trailer( [ 8000000, 8000000, 8000000, 990000, 990000 ] ));
document.onmousedown = reset;
reset();
setInterval( compute, 0 );
};
function reset() {
ctx.fillStyle = "white";
ctx.fillRect( 0,0,c.width,c.height );
for( var i =0; i < trails.length; i++ ) {
trails[ i ].reset();
}
}
function compute() {
for( var i =0; i < trails.length; i++ ) {
trails[ i ].compute( ctx );
}
}
})();
ns.trailer = function( colors ) {
this.points = [];
this.stroke = new ns.stroke( null, 100, 10, colors[ 0 ] );
this.colorIterator = 10;
this.colors = colors;
};
ns.trailer.prototype = {
reset : function() {
this.points = [];
this.width = document.body.offsetWidth;
this.height = document.body.offsetHeight;
this.radius = Math.max( this.width, this.height );
this.center = new ns.point( this.width / 2, this.height / 2 );
this.a0 = Math.random() * Math.PI * 2;
this.a1 = Math.random() * Math.PI * 2;
this.a2 = Math.random() * Math.PI * 2;
var mul = 1 + Math.random() * 2;
if( Math.random() > .5 ) mul *= 5;
else mul /= 2;
this.s0 = ( Math.random() - .5 ) * mul / 180 * Math.PI;
this.s1 = ( Math.random() - .5 ) * mul / 180 * Math.PI;
this.s2 = ( Math.random() - .5 ) * mul / 180 * Math.PI;
},
compute : function( ctx ) {
with( this ) {
a0 += s0;
a1 += s1;
a2 += s2;
var c = Math.cos( a0 ) * Math.cos( a1 ) * Math.cos( a2 );
var s = Math.sin( a0 ) * Math.sin( a1 ) * Math.sin( a2 );
points.push( new ns.point( center.x + c * radius,
center.y + s * radius ) );
if( points.length > 10 ) points.shift();
stroke.anchors = points;
stroke.draw( ctx );
var t = .5 + (Math.sin( new Date().getTime() * .001 ) * .5 );
stroke.color = colors[ Math.floor( t * colors.length ) ];
stroke.width = 25 + ( 1 - t ) * 50;
//stroke.strokeCount = 5 + t * 5;
stroke.strokeCount = 5;
}
}
};
ns.point = function( x,y ) {
this.x = x;
this.y = y;
};
ns.point.prototype = {
add : function( p ) {
return new ns.point( this.x + p.x, this.y + p.y );
},
sub : function( p ) {
return new ns.point( this.x - p.x, this.y - p.y );
},
negate : function() {
this.x *= -1;
this.y *= -1;
return this;
},
clone : function() {
return new ns.point( this.x, this.y );
},
length : function() {
return Math.sqrt( this.x * this.x + this.y * this.y );
},
normalize : function ( scale ) {
scale = scale || 1;
var l = this.length();
this.x /= l;
this.x *= scale;
this.y /= l;
this.y *= scale;
return this;
}
};
ns.stroke = function( anchors, width, strokeCount, color ) {
this.anchors = anchors;
this.width = width;
this.strokeCount = strokeCount;
this.color = color;
};
ns.stroke.prototype = {
normal : function( p0, p1 ){
return new ns.point( -( p1.y - p0.y ), ( p1.x - p0.x ) );
},
draw : function( ctx ) {
if( this.anchors === undefined ) return;
var half = this.height * .5;
var p, c, n, pnorm, pln, prn, cnorm, cln, crn;
with( this ) {
for( var j = 0; j < strokeCount; j++ ) {
half = width * .5 * Math.random();
var col = ns.variation( color, 35 );
ctx.lineWidth = .1 + Math.random() * 2;
for( var i = 0; i < anchors.length - 2; i++ ) {
p = anchors[ i ];
c = anchors[ i+1 ];
n = anchors[ i+2 ];
pnorm = normal( p, c );
cnorm = normal( c, n );
half += ( Math.random() - .5 );
pnorm.normalize( half );
pln = p.add( pnorm );
pnorm.normalize( -half );
prn = p.add( pnorm );
half += ( Math.random() - .5 );
cnorm.normalize( half );
cln = c.add( cnorm );
cnorm.normalize( -half );
crn = c.add( cnorm );
ctx.beginPath();
ctx.strokeStyle = col;
ctx.moveTo( prn.x, prn.y );
ctx.lineTo( crn.x, crn.y );
ctx.stroke();
ctx.closePath();
ctx.beginPath();
ctx.strokeStyle = col;
ctx.moveTo( pln.x, pln.y );
ctx.lineTo( cln.x, cln.y );
ctx.stroke();
ctx.closePath();
}
}
}
}
};
ns.variation = function( color, amount ) {
amount = amount || 25;
var r = color && 16 && 0xFF;
var g = color && 8 && 0xFF;
var b = color && 0xFF;
r += Math.floor( ( Math.random() - .5 ) * amount );
g += Math.floor( ( Math.random() - .5 ) * amount );
b += Math.floor( ( Math.random() - .5 ) * amount );
r = r > 0xFF ? 0xFF : r < 0 ? 0 : r;
g = g > 0xFF ? 0xFF : g < 0 ? 0 : g;
b = b > 0xFF ? 0xFF : b < 0 ? 0 : b;
return "rgba("+r+','+g+','+b+','+Math.random()+');';
};
#container {
position: relative;
}
#overlay {
position:absolute;
top:50px;
left:150px;
z-index:10;
}
<body>
<div id='container'>
<div id='overlay'>
<h1>
OVERLAY
</h1>
</div>
<canvas id='canvas'>
</canvas>
</div>
<!-- scripts -->
<script type="text/javascript" src="test-script.js"></script>
</body>

HTML5 canvas following mouse effect jquery

I'm searching the web for quite sometime for the name of this effect... I've seen it on many sites but can't find a guide or a name to look for it and learn how to do it.
The effect I'm talking about is in this website header:
http://diogodantas.com/demo/elegant-index#0
check this. i just ctrl + c,v.
(function() {
var width, height, largeHeader, canvas, ctx, points, target, animateHeader = true;
// Main
initHeader();
initAnimation();
addListeners();
function initHeader() {
width = window.innerWidth;
height = window.innerHeight;
target = {x: width/2, y: height/2};
largeHeader = document.getElementById('large-header');
largeHeader.style.height = height+'px';
canvas = document.getElementById('demo-canvas');
canvas.width = width;
canvas.height = height;
ctx = canvas.getContext('2d');
// create points
points = [];
for(var x = 0; x < width; x = x + width/20) {
for(var y = 0; y < height; y = y + height/20) {
var px = x + Math.random()*width/20;
var py = y + Math.random()*height/20;
var p = {x: px, originX: px, y: py, originY: py };
points.push(p);
}
}
// for each point find the 5 closest points
for(var i = 0; i < points.length; i++) {
var closest = [];
var p1 = points[i];
for(var j = 0; j < points.length; j++) {
var p2 = points[j]
if(!(p1 == p2)) {
var placed = false;
for(var k = 0; k < 5; k++) {
if(!placed) {
if(closest[k] == undefined) {
closest[k] = p2;
placed = true;
}
}
}
for(var k = 0; k < 5; k++) {
if(!placed) {
if(getDistance(p1, p2) < getDistance(p1, closest[k])) {
closest[k] = p2;
placed = true;
}
}
}
}
}
p1.closest = closest;
}
// assign a circle to each point
for(var i in points) {
var c = new Circle(points[i], 2+Math.random()*2, 'rgba(255,255,255,0.3)');
points[i].circle = c;
}
}
// Event handling
function addListeners() {
if(!('ontouchstart' in window)) {
window.addEventListener('mousemove', mouseMove);
}
window.addEventListener('scroll', scrollCheck);
window.addEventListener('resize', resize);
}
function mouseMove(e) {
var posx = posy = 0;
if (e.pageX || e.pageY) {
posx = e.pageX;
posy = e.pageY;
}
else if (e.clientX || e.clientY) {
posx = e.clientX + document.body.scrollLeft + document.documentElement.scrollLeft;
posy = e.clientY + document.body.scrollTop + document.documentElement.scrollTop;
}
target.x = posx;
target.y = posy;
}
function scrollCheck() {
if(document.body.scrollTop > height) animateHeader = false;
else animateHeader = true;
}
function resize() {
width = window.innerWidth;
height = window.innerHeight;
largeHeader.style.height = height+'px';
canvas.width = width;
canvas.height = height;
}
// animation
function initAnimation() {
animate();
for(var i in points) {
shiftPoint(points[i]);
}
}
function animate() {
if(animateHeader) {
ctx.clearRect(0,0,width,height);
for(var i in points) {
// detect points in range
if(Math.abs(getDistance(target, points[i])) < 4000) {
points[i].active = 0.3;
points[i].circle.active = 0.6;
} else if(Math.abs(getDistance(target, points[i])) < 20000) {
points[i].active = 0.1;
points[i].circle.active = 0.3;
} else if(Math.abs(getDistance(target, points[i])) < 40000) {
points[i].active = 0.02;
points[i].circle.active = 0.1;
} else {
points[i].active = 0;
points[i].circle.active = 0;
}
drawLines(points[i]);
points[i].circle.draw();
}
}
requestAnimationFrame(animate);
}
function shiftPoint(p) {
TweenLite.to(p, 1+1*Math.random(), {x:p.originX-50+Math.random()*100,
y: p.originY-50+Math.random()*100, ease:Circ.easeInOut,
onComplete: function() {
shiftPoint(p);
}});
}
// Canvas manipulation
function drawLines(p) {
if(!p.active) return;
for(var i in p.closest) {
ctx.beginPath();
ctx.moveTo(p.x, p.y);
ctx.lineTo(p.closest[i].x, p.closest[i].y);
ctx.strokeStyle = 'rgba(156,217,249,'+ p.active+')';
ctx.stroke();
}
}
function Circle(pos,rad,color) {
var _this = this;
// constructor
(function() {
_this.pos = pos || null;
_this.radius = rad || null;
_this.color = color || null;
})();
this.draw = function() {
if(!_this.active) return;
ctx.beginPath();
ctx.arc(_this.pos.x, _this.pos.y, _this.radius, 0, 2 * Math.PI, false);
ctx.fillStyle = 'rgba(156,217,249,'+ _this.active+')';
ctx.fill();
};
}
// Util
function getDistance(p1, p2) {
return Math.pow(p1.x - p2.x, 2) + Math.pow(p1.y - p2.y, 2);
}
})();
I still cannot find a name but here is a link
https://codepen.io/thetwistedtaste/pen/GgrWLp
/*
*
* TERMS OF USE -
*
* Open source under the BSD License.
*
* Copyright © 2001 Robert Penner
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* Neither the name of the author nor the names of contributors may be used to endorse
* or promote products derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
// http://paulirish.com/2011/requestanimationframe-for-smart-animating/
// http://my.opera.com/emoller/blog/2011/12/20/requestanimationframe-for-smart-er-animating
// requestAnimationFrame polyfill by Erik Möller. fixes from Paul Irish and Tino Zijdel
// MIT license
(function() {
var lastTime = 0;
var vendors = ['ms', 'moz', 'webkit', 'o'];
for(var x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) {
window.requestAnimationFrame = window[vendors[x]+'RequestAnimationFrame'];
window.cancelAnimationFrame = window[vendors[x]+'CancelAnimationFrame'] || window[vendors[x]+'CancelRequestAnimationFrame'];
}
if (!window.requestAnimationFrame)
window.requestAnimationFrame = function(callback, element) {
var currTime = new Date().getTime();
var timeToCall = Math.max(0, 16 - (currTime - lastTime));
var id = window.setTimeout(function() { callback(currTime + timeToCall); },
timeToCall);
lastTime = currTime + timeToCall;
return id;
};
if (!window.cancelAnimationFrame)
window.cancelAnimationFrame = function(id) {
clearTimeout(id);
};
}());
$( function() {
var width, height, canvas, ctx, points, target, animateHeader = true;
// Main
initHeader();
initAnimation();
addListeners();
function initHeader() {
width = window.innerWidth;
height = window.innerHeight;
target = {
x: width / 2,
y: height / 3
};
canvas = document.getElementById( 'spiders' );
canvas.width = width;
canvas.height = height;
ctx = canvas.getContext( '2d' );
// create points
points = [];
for ( var x = 0; x < width; x = x + width / 20 ) {
for ( var y = 0; y < height; y = y + height / 20 ) {
var px = x + Math.random() * width / 20;
var py = y + Math.random() * height / 20;
var p = {
x: px,
originX: px,
y: py,
originY: py
};
points.push( p );
}
}
// for each point find the 5 closest points
for ( var i = 0; i < points.length; i++ ) {
var closest = [];
var p1 = points[ i ];
for ( var j = 0; j < points.length; j++ ) {
var p2 = points[ j ]
if ( !( p1 == p2 ) ) {
var placed = false;
for ( var k = 0; k < 5; k++ ) {
if ( !placed ) {
if ( closest[ k ] == undefined ) {
closest[ k ] = p2;
placed = true;
}
}
}
for ( var k = 0; k < 5; k++ ) {
if ( !placed ) {
if ( getDistance( p1, p2 ) < getDistance( p1, closest[ k ] ) ) {
closest[ k ] = p2;
placed = true;
}
}
}
}
}
p1.closest = closest;
}
// assign a circle to each point
for ( var i in points ) {
var c = new Circle( points[ i ], 2 + Math.random() * 2, 'rgba(255,255,255,0.3)' );
points[ i ].circle = c;
}
}
// Event handling
function addListeners() {
if ( !( 'ontouchstart' in window ) ) {
window.addEventListener( 'mousemove', mouseMove );
}
window.addEventListener( 'scroll', scrollCheck );
window.addEventListener( 'resize', resize );
}
function mouseMove( e ) {
var posx = posy = 0;
if ( e.pageX || e.pageY ) {
posx = e.pageX;
posy = e.pageY;
} else if ( e.clientX || e.clientY ) {
posx = e.clientX + document.body.scrollLeft + document.documentElement.scrollLeft;
posy = e.clientY + document.body.scrollTop + document.documentElement.scrollTop;
}
target.x = posx;
target.y = posy;
}
function scrollCheck() {
if ( document.body.scrollTop > height ) animateHeader = false;
else animateHeader = true;
}
function resize() {
width = window.innerWidth;
height = window.innerHeight;
canvas.width = width;
canvas.height = height;
}
// animation
function initAnimation() {
animate();
for ( var i in points ) {
shiftPoint( points[ i ] );
}
}
function animate() {
if ( animateHeader ) {
ctx.clearRect( 0, 0, width, height );
for ( var i in points ) {
// detect points in range
if ( Math.abs( getDistance( target, points[ i ] ) ) < 2000 ) {
points[ i ].active = 0.2;
points[ i ].circle.active = 0.5;
} else if ( Math.abs( getDistance( target, points[ i ] ) ) < 20000 ) {
points[ i ].active = 0.1;
points[ i ].circle.active = 0.3;
} else if ( Math.abs( getDistance( target, points[ i ] ) ) < 70000 ) {
points[ i ].active = 0.02;
points[ i ].circle.active = 0.09;
} else if ( Math.abs( getDistance( target, points[ i ] ) ) < 140000 ) {
points[ i ].active = 0;
points[ i ].circle.active = 0.02;
} else {
points[ i ].active = 0;
points[ i ].circle.active = 0;
}
drawLines( points[ i ] );
points[ i ].circle.draw();
}
}
requestAnimationFrame( animate );
}
function shiftPoint( p ) {
TweenLite.to( p, 1 + 1 * Math.random(), {
x: p.originX - 50 + Math.random() * 100,
y: p.originY - 50 + Math.random() * 100,
onComplete: function() {
shiftPoint( p );
}
} );
}
// Canvas manipulation
function drawLines( p ) {
if ( !p.active ) return;
for ( var i in p.closest ) {
ctx.beginPath();
ctx.moveTo( p.x, p.y );
ctx.lineTo( p.closest[ i ].x, p.closest[ i ].y );
ctx.strokeStyle = 'rgba(255,255,255,' + p.active + ')';
ctx.stroke();
}
}
function Circle( pos, rad, color ) {
var _this = this;
// constructor
( function() {
_this.pos = pos || null;
_this.radius = rad || null;
_this.color = color || null;
} )();
this.draw = function() {
if ( !_this.active ) return;
ctx.beginPath();
ctx.arc( _this.pos.x, _this.pos.y, _this.radius, 0, 2 * Math.PI, false );
ctx.fillStyle = 'rgba(255,255,255,' + _this.active + ')';
ctx.fill();
};
}
// Util
function getDistance( p1, p2 ) {
return Math.pow( p1.x - p2.x, 2 ) + Math.pow( p1.y - p2.y, 2 );
}
} );
body {}
i {
position: absolute;
top:0; bottom:0;left:0;right:0;
display:block;
background:black;
z-index:-1;
}
canvas#spiders {
height:100vh;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/1.19.0/TweenLite.min.js"></script><script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<i></i>
<canvas id="spiders" class="hidden-xs" width="1280" height="451"></canvas>

Altering color of individual sprite in three.js

I just started playing around with three.js and I'm having some trouble modifying the color of individual sprites within an array.
I'm working with the example found here, from threejs.org.
I'm attempting to modify each sprite's color based on its scale value. In particular, I added the following line to the last function, from the source file.
function render() {
camera.position.x += ( mouseX - camera.position.x ) * .05;
camera.position.y += ( - mouseY - camera.position.y ) * .05;
camera.lookAt( scene.position );
var i = 0;
for ( var ix = 0; ix < AMOUNTX; ix ++ ) {
for ( var iy = 0; iy < AMOUNTY; iy ++ ) {
particle = particles[ i++ ];
particle.position.y = ( Math.sin( ( ix + count ) * 0.3 ) * 50 ) +
( Math.sin( ( iy + count ) * 0.5 ) * 50 );
particle.scale.x = particle.scale.y = ( Math.sin( ( ix + count ) * 0.3 ) + 1 ) * 4 +
( Math.sin( ( iy + count ) * 0.5 ) + 1 ) * 4;
// Added this line in an attempt to change color based on scale //
particle.material.color.setHSL(particle.scale.x * .1, .2, .2);
}
}
renderer.render( scene, camera );
count += 0.1;
}
However, the added line changes every particle to the same color values. I'd assumed that each element of the array would be accessed and modified within the loop, but it doesn't seem to be the case.
The material is being shared with all the particles,
var material = new THREE.SpriteCanvasMaterial( {
color: 0xffffff,
program: function ( context ) {
context.beginPath();
context.arc( 0, 0, 0.5, 0, PI2, true );
context.fill();
}
} );
Move this above the loop where the sprites particles are created so they get a separate material each.
The answer about each sprite having its own material sent me in the right direction. I set material to an array and added an association between each material and sprite element.
var i = 0;
var j = 0;
for ( var ix = 0; ix < AMOUNTX; ix ++ ) {
for ( var iy = 0; iy < AMOUNTY; iy ++ ) {
material[ j ] = new THREE.SpriteCanvasMaterial( {
vertexColors: THREE.VertexColors,
program: function ( context ) {
context.beginPath();
context.arc( 0, 0, 0.5, 0, PI2, true );
context.fill();
}
} );
particle = particles[ i ++ ] = new THREE.Sprite( material[ j ++ ] );
particle.position.x = ix * SEPARATION - ( ( AMOUNTX * SEPARATION ) / 2 );
particle.position.z = iy * SEPARATION - ( ( AMOUNTY * SEPARATION ) / 2 );
scene.add( particle );
}
}

Categories