Related
I've found this codepen that I'm integrating on a small website I have for a project. I don't want to put too much time into it, so I'll just be using a codepen I found. I'll do some adjustments to it, like the shape.
I managed to implement it, but now I'm having trouble to change the colors and the shape of the confetti.
The colors are completely random and I'd love for this to just be simple.
Also, I can't seem to be able to change the shape. I can only adjust the size.
If anyone here feels like giving me a nudge in the right direction, that'd be great!
'use strict';
// If set to true, the user must press
// UP UP DOWN ODWN LEFT RIGHT LEFT RIGHT A B
// to trigger the confetti with a random color theme.
// Otherwise the confetti constantly falls.
var onlyOnKonami = false;
$(function() {
// Globals
var $window = $(window)
, random = Math.random
, cos = Math.cos
, sin = Math.sin
, PI = Math.PI
, PI2 = PI * 2
, timer = undefined
, frame = undefined
, confetti = [];
// Settings
var konami = [38, 38, 40, 40, 37, 39, 37, 39, 66, 65]
, pointer = 0;
var particles = 150
, spread = 40
, sizeMin = 3
, sizeMax = 12 - sizeMin
, eccentricity = 10
, deviation = 100
, dxThetaMin = -.1
, dxThetaMax = -dxThetaMin - dxThetaMin
, dyMin = .13
, dyMax = .18
, dThetaMin = .4
, dThetaMax = .7 - dThetaMin;
var colorThemes = [
function() {
return color(200 * random()|0, 200 * random()|0, 200 * random()|0);
}, function() {
var black = 200 * random()|0; return color(200, black, black);
}, function() {
var black = 200 * random()|0; return color(black, 200, black);
}, function() {
var black = 200 * random()|0; return color(black, black, 200);
}, function() {
return color(200, 100, 200 * random()|0);
}, function() {
return color(200 * random()|0, 200, 200);
}, function() {
var black = 256 * random()|0; return color(black, black, black);
}, function() {
return colorThemes[random() < .5 ? 1 : 2]();
}, function() {
return colorThemes[random() < .5 ? 3 : 5]();
}, function() {
return colorThemes[random() < .5 ? 2 : 4]();
}
];
function color(r, g, b) {
return 'rgb(' + r + ',' + g + ',' + b + ')';
}
// Cosine interpolation
function interpolation(a, b, t) {
return (1-cos(PI*t))/2 * (b-a) + a;
}
// Create a 1D Maximal Poisson Disc over [0, 1]
var radius = 1/eccentricity, radius2 = radius+radius;
function createPoisson() {
// domain is the set of points which are still available to pick from
// D = union{ [d_i, d_i+1] | i is even }
var domain = [radius, 1-radius], measure = 1-radius2, spline = [0, 1];
while (measure) {
var dart = measure * random(), i, l, interval, a, b, c, d;
// Find where dart lies
for (i = 0, l = domain.length, measure = 0; i < l; i += 2) {
a = domain[i], b = domain[i+1], interval = b-a;
if (dart < measure+interval) {
spline.push(dart += a-measure);
break;
}
measure += interval;
}
c = dart-radius, d = dart+radius;
// Update the domain
for (i = domain.length-1; i > 0; i -= 2) {
l = i-1, a = domain[l], b = domain[i];
// c---d c---d Do nothing
// c-----d c-----d Move interior
// c--------------d Delete interval
// c--d Split interval
// a------b
if (a >= c && a < d)
if (b > d) domain[l] = d; // Move interior (Left case)
else domain.splice(l, 2); // Delete interval
else if (a < c && b > c)
if (b <= d) domain[i] = c; // Move interior (Right case)
else domain.splice(i, 0, c, d); // Split interval
}
// Re-measure the domain
for (i = 0, l = domain.length, measure = 0; i < l; i += 2)
measure += domain[i+1]-domain[i];
}
return spline.sort();
}
// Create the overarching container
var container = document.createElement('div');
container.style.position = 'fixed';
container.style.top = '0';
container.style.left = '0';
container.style.width = '100%';
container.style.height = '0';
container.style.overflow = 'visible';
container.style.zIndex = '9999';
// Confetto constructor
function Confetto(theme) {
this.frame = 0;
this.outer = document.createElement('div');
this.inner = document.createElement('div');
this.outer.appendChild(this.inner);
var outerStyle = this.outer.style, innerStyle = this.inner.style;
outerStyle.position = 'absolute';
outerStyle.width = (sizeMin + sizeMax * random()) + 'px';
outerStyle.height = (sizeMin + sizeMax * random()) + 'px';
innerStyle.width = '100%';
innerStyle.height = '100%';
innerStyle.backgroundColor = theme();
outerStyle.perspective = '50px';
outerStyle.transform = 'rotate(' + (360 * random()) + 'deg)';
this.axis = 'rotate3D(' +
cos(360 * random()) + ',' +
cos(360 * random()) + ',0,';
this.theta = 360 * random();
this.dTheta = dThetaMin + dThetaMax * random();
innerStyle.transform = this.axis + this.theta + 'deg)';
this.x = $window.width() * random();
this.y = -deviation;
this.dx = sin(dxThetaMin + dxThetaMax * random());
this.dy = dyMin + dyMax * random();
outerStyle.left = this.x + 'px';
outerStyle.top = this.y + 'px';
// Create the periodic spline
this.splineX = createPoisson();
this.splineY = [];
for (var i = 1, l = this.splineX.length-1; i < l; ++i)
this.splineY[i] = deviation * random();
this.splineY[0] = this.splineY[l] = deviation * random();
this.update = function(height, delta) {
this.frame += delta;
this.x += this.dx * delta;
this.y += this.dy * delta;
this.theta += this.dTheta * delta;
// Compute spline and convert to polar
var phi = this.frame % 7777 / 7777, i = 0, j = 1;
while (phi >= this.splineX[j]) i = j++;
var rho = interpolation(
this.splineY[i],
this.splineY[j],
(phi-this.splineX[i]) / (this.splineX[j]-this.splineX[i])
);
phi *= PI2;
outerStyle.left = this.x + rho * cos(phi) + 'px';
outerStyle.top = this.y + rho * sin(phi) + 'px';
innerStyle.transform = this.axis + this.theta + 'deg)';
return this.y > height+deviation;
};
}
function poof() {
if (!frame) {
// Append the container
document.body.appendChild(container);
// Add confetti
var theme = colorThemes[onlyOnKonami ? colorThemes.length * random()|0 : 0]
, count = 0;
(function addConfetto() {
if (onlyOnKonami && ++count > particles)
return timer = undefined;
var confetto = new Confetto(theme);
confetti.push(confetto);
container.appendChild(confetto.outer);
timer = setTimeout(addConfetto, spread * random());
})(0);
// Start the loop
var prev = undefined;
requestAnimationFrame(function loop(timestamp) {
var delta = prev ? timestamp - prev : 0;
prev = timestamp;
var height = $window.height();
for (var i = confetti.length-1; i >= 0; --i) {
if (confetti[i].update(height, delta)) {
container.removeChild(confetti[i].outer);
confetti.splice(i, 1);
}
}
if (timer || confetti.length)
return frame = requestAnimationFrame(loop);
// Cleanup
document.body.removeChild(container);
frame = undefined;
});
}
}
$window.keydown(function(event) {
pointer = konami[pointer] === event.which
? pointer+1
: +(event.which === konami[0]);
if (pointer === konami.length) {
pointer = 0;
poof();
}
});
if (!onlyOnKonami) poof();
});
Look inside Confetto() it's just using a <div> inside another <div> and giving it random width, height, 3D rotation, etc., you should be able to manipulate innerStyle and/or outerStyle to your liking there.
Try adding something like this to make it rounded:
innerStyle.borderRadius = '100%';
Try something like this to make it squared:
innerStyle.borderRadius = '0';
Or a combination:
innerStyle.borderRadius = '100% 0 0 100%';
You should be able to set any style property you need, by camel-casing the css name (.border for border, .borderBottom for border-bottom, .borderBottomColor for border-bottom-color, etc.). Check this list for ideas
I'm currently working on a Pinball game using the HTML5 Canvas and JavaScript. Right now I'm getting a hard time with the pixel by pixel collision, which is fundamental because of the flippers.
Right now my Bounding Box Collision seems to be working
checkCollision(element) {
if (this.checkCollisionBoundingBox(element)) {
console.log("colision with the element bounding box");
if (this.checkCollisionPixelByPixel(element)) {
return true;
} else {
return false;
}
} else {
return false;
}
}
checkCollisionBoundingBox(element) {
if (this.pos.x < element.pos.x + element.width && this.pos.x + this.width > element.pos.x && this.pos.y < element.pos.y + element.height && this.pos.y + this.height > element.pos.y) {
return true;
} else {
return false;
}
}
I've tried several ways of implementing the pixel by pixel one but for some reason it does not work perfectly (on walls, on images, on sprites etc). I'll leave them here:
checkCollisionPixelByPixel(element) {
var x_left = Math.floor(Math.max(this.pos.x, element.pos.x));
var x_right = Math.floor(Math.min(this.pos.x + this.width, element.pos.x + element.width));
var y_top = Math.floor(Math.max(this.pos.y, element.pos.y));
var y_bottom = Math.floor(Math.min(this.pos.y + this.height, element.pos.y + element.height));
for (var y = y_top; y < y_bottom; y++) {
for (var x = x_left; x < x_right; x++) {
var x_0 = Math.round(x - this.pos.x);
var y_0 = Math.round(y - this.pos.y);
var n_pix = y_0 * (this.width * this.total) + (this.width * (this.actual-1)) + x_0; //n pixel to check
var pix_op = this.imgData.data[4 * n_pix + 3]; //opacity (R G B A)
var element_x_0 = Math.round(x - element.pos.x);
var element_y_0 = Math.round(y - element.pos.y);
var element_n_pix = element_y_0 * (element.width * element.total) + (element.width * (element.actual-1)) + element_x_0; //n pixel to check
var element_pix_op = element.imgData.data[4 * element_n_pix + 3]; //opacity (R G B A)
console.log(element_pix_op);
if (pix_op == 255 && element_pix_op == 255) {
console.log("Colision pixel by pixel");
/*Debug*/
/*console.log("This -> (R:" + this.imgData.data[4 * n_pix] + ", G:" + this.imgData.data[4 * n_pix + 1] + ", B:" + this.imgData.data[4 * n_pix + 2] + ", A:" + pix_op + ")");
console.log("Element -> (R:" + element.imgData.data[4 * element_n_pix] + ", G:" + element.imgData.data[4 * element_n_pix + 1] + ", B:" + element.imgData.data[4 * element_n_pix + 2] + ", A:" + element_pix_op + ")");
console.log("Collision -> (x:" + x + ", y:" + y +")");
console.log("This(Local) -> (x:" + x_0 + ", y:" + y_0+")");
console.log("Element(Local) -> (x:" + element_x_0 + ", y:" + element_y_0+")");*/
/*ball vector*/
var vector = {
x: (x_0 - Math.floor(this.imgData.width / 2)),
y: -(y_0 - Math.floor(this.imgData.height / 2))
};
//console.log("ball vector -> ("+vector.x+", "+vector.y+") , Angulo: "+ Math.atan(vector.y/vector.x)* 180/Math.PI);
// THIS WAS THE FIRST TRY, IT DIDN'T WORK WHEN THE BALL WAS GOING NORTHEAST AND COLLIDED WITH A WALL. DIDN'T WORK AT ALL WITH SPRITES
//this.angle = (Math.atan2(vector.y, vector.x) - Math.PI) * (180 / Math.PI);
// THIS WAS THE SECOND ATTEMPT, WORKS WORSE THAN THE FIRST ONE :/
//normal vector
var normal = {
x: (x_0 - (this.imgData.width / 2)),
y: -(y_0 - (this.imgData.height / 2))
};
//Normalizar o vetor
var norm = Math.sqrt(normal.x * normal.x + normal.y * normal.y);
if (norm != 0) {
normal.x = normal.x / norm;
normal.y = normal.y / norm;
}
var n_rad = Math.atan2(normal.y, normal.x);
var n_deg = (n_rad + Math.PI) * 180 / Math.PI;
console.log("Vetor Normal -> (" + normal.x + ", " + normal.y + ") , Angulo: " + n_deg);
//Vetor Velocidade
var velocity = {
x: Math.cos((this.angle * Math.PI / 180) - Math.PI),
y: Math.sin((this.angle * Math.PI / 180) - Math.PI)
};
console.log("Vetor Velocidade -> (" + velocity.x + ", " + velocity.y + ") , Angulo: " + this.angle);
//Vetor Reflexao
var ndotv = normal.x * velocity.x + normal.y * velocity.y;
var reflection = {
x: -2 * ndotv * normal.x + velocity.x,
y: -2 * ndotv * normal.y + velocity.y
};
var r_rad = Math.atan2(reflection.y, reflection.x);
var r_deg = (r_rad + Math.PI) * 180 / Math.PI;
console.log("Vetor Reflexao -> (" + reflection.x + ", " + reflection.y + ") , Angulo: " + r_deg);
this.angle = r_deg;
return true;
}
}
}
return false;
}
}
The ball class
class Ball extends Element {
constructor(img, pos, width, height, n, sound, angle, speed) {
super(img, pos, width, height, n, sound);
this.angle = angle; //direction [0:360[
this.speed = speed;
}
move(ctx, cw, ch) {
var rads = this.angle * Math.PI / 180
var vx = Math.cos(rads) * this.speed / 60;
var vy = Math.sin(rads) * this.speed / 60;
this.pos.x += vx;
this.pos.y -= vy;
ctx.clearRect(0, 0, cw, ch);
this.draw(ctx, 1);
}
}
Assuming a "flipper" is composed of 2 arcs and 2 lines it would be much faster to do collision detection mathematically rather than by the much slower pixel-test method. Then you just need 4 math collision tests.
Even if your flippers are a bit more complicated than arcs+lines, the math hit tests would be "good enough" -- meaning in your fast-moving game, the user cannot visually notice the approximate math results vs the pixel-perfect results and the difference between the 2 types of tests will not affect gameplay at all. But the pixel-test version will take magnitudes more time and resources to accomplish. ;-)
First two circle-vs-circle collision tests:
function CirclesColliding(c1,c2){
var dx=c2.x-c1.x;
var dy=c2.y-c1.y;
var rSum=c1.r+c2.r;
return(dx*dx+dy*dy<=rSum*rSum);
}
Then two circle-vs-line-segment collision tests:
// [x0,y0] to [x1,y1] define a line segment
// [cx,cy] is circle centerpoint, cr is circle radius
function isCircleSegmentColliding(x0,y0,x1,y1,cx,cy,cr){
// calc delta distance: source point to line start
var dx=cx-x0;
var dy=cy-y0;
// calc delta distance: line start to end
var dxx=x1-x0;
var dyy=y1-y0;
// Calc position on line normalized between 0.00 & 1.00
// == dot product divided by delta line distances squared
var t=(dx*dxx+dy*dyy)/(dxx*dxx+dyy*dyy);
// calc nearest pt on line
var x=x0+dxx*t;
var y=y0+dyy*t;
// clamp results to being on the segment
if(t<0){x=x0;y=y0;}
if(t>1){x=x1;y=y1;}
return( (cx-x)*(cx-x)+(cy-y)*(cy-y) < cr*cr );
}
I am trying to create a simulation for experimenting with the Brownian motion. The idea is to make numerous particles (points) each moving randomly and colliding with each other, while conserving the total energy. That, I did succeed programming.
Now the problematic part is to make these point-like particles collide with a big circle and program the collision correctly. The difference is I made the particles move towards a totally random angle after their collision with each other, but when they collide with the circle, their previous path and speed, together with the circle's path and speed determines the result. So what I tried to do is to convert to a coordinate system attached to the center of the circle, calculate the collision point of every particle, which entered into the circle in the previous step, then calculate the angle in which they arrived. Now then the momentum of the particle is decomposed into two components: the tangential component remains the same, while the radial component changes according to the laws of collisions. This affects the motion of the circle as well. After calculating all the collisions, we move back to the original reference system, and draw all the objects.
The code can be found below, and it seems to be broken. I guess I was mistaken when calculating with the angles, because some particles can be shown arriving to the circle at one point, then "exiting" through the other side of it. I just lost track in following it all. I'm a complete newbie to programming, so be... um... gentle, please. :) Another strange thing is that the circle trends to move to a direction it started to move early, which is not what it supposed to do.
The mass of the particles are given, the mass of the circle is calculated with it's radius.
You can find the simulation on this website:
http://sixy.uw.hu/brown
It is not finished yet, but the start button works fine. :)
The part of the code I'm asking about is here: (shall I give you the whole code?)
function animateParticles() {
if ($('N').value != particleCount || $('S').value != circle.R) {
reset();
}
switch (activeGraph) {
case '#graph':
chartOptions.title.text = 'Távolság';
chartOptions.axisY.title = 'd [px]';
break;
if (Date.now() > lastAnim + 22) {
context.clearRect(0, 0, canvas.width, canvas.height);
// Particles step and collide
for (var i in particle) {
// Particle steps
{
particle[i].x += particle[i].vx;
particle[i].y += particle[i].vy;
}
// If not in the circle
if ((circle.x - particle[i].x) * (circle.x - particle[i].x) + (circle.y - particle[i].y) * (circle.y - particle[i].y) > circle.R * circle.R) {
// Collides with some of the rest of the particles
for (var j = 1; j <= $('N').value; j += Math.ceil(particleCount / (Math.random() * 50 + 50))) {
// If that's not in the circle as well
if ((circle.x - particle[j].x) * (circle.x - particle[j].x) + (circle.y - particle[j].y) * (circle.y - particle[j].y) > circle.R * circle.R) {
// If not himself
if (j != i) {
if (particle[i].coll == 0) {
// Collide
if (Math.pow((particle[i].x - particle[j].x), 2) + Math.pow((particle[i].y - particle[j].y), 2) <= 4) {
var tkpx = (particle[i].vx + particle[j].vx) / 2;
var tkpy = (particle[i].vy + particle[j].vy) / 2;
var fi = Math.random() * 2 * Math.PI;
var index;
var ix = particle[i].vx;
var iy = particle[i].vy;
particle[i].vx = Math.sqrt(Math.pow((ix - tkpx), 2) + Math.pow((iy - tkpy), 2)) * Math.cos(fi) + tkpx;
particle[i].vy = Math.sqrt(Math.pow((ix - tkpx), 2) + Math.pow((iy - tkpy), 2)) * Math.sin(fi) + tkpy;
particle[j].vx = -Math.sqrt(Math.pow((ix - tkpx), 2) + Math.pow((iy - tkpy), 2)) * Math.cos(fi) + tkpx;
particle[j].vy = -Math.sqrt(Math.pow((ix - tkpx), 2) + Math.pow((iy - tkpy), 2)) * Math.sin(fi) + tkpy;
}
}
}
if (particle[i].coll >= 1) {
particle[i].coll -= 1;
}
}
}
}
// Collision with the walls
{
if (particle[i].x >= canvas.width) {
var temp;
temp = particle[i].x - canvas.width;
particle[i].x = canvas.width - temp;
particle[i].vx *= -1
}
if (particle[i].x <= 0) {
var temp;
temp = -particle[i].x;
particle[i].x = temp;
particle[i].vx *= -1;
}
if (particle[i].y >= canvas.height) {
var temp;
temp = particle[i].y - canvas.height;
particle[i].y = canvas.height - temp;
particle[i].vy *= -1;
}
if (particle[i].y <= 0) {
var temp;
temp = -particle[i].y;
particle[i].y = temp;
particle[i].vy *= -1;
}
}
}
//console.log(circle);
// Circle steps and collides with particles
var cu = 0;
var cv = 0;
for (var i in particle) {
if ((circle.x - particle[i].x) * (circle.x - particle[i].x) + (circle.y - particle[i].y) * (circle.y - particle[i].y) < circle.R * circle.R) {
var pu;
var pv;
var px;
var py;
var px0;
var py0;
var t;
var t2;
var Tx;
var Ty;
var fi;
var p;
var q;
var cuu;
var cvv;
px = particle[i].x - circle.x;
py = particle[i].y - circle.y;
pu = particle[i].vx - circle.vx;
pv = particle[i].vy - circle.vy;
px0 = px - particle[i].vx;
py0 = py - particle[i].vy;
// Calculating the meeting point of the collision
t = (-(px0 * pu + py0 * pv) - Math.sqrt(circle.R * circle.R * (pu * pu + pv * pv) - Math.pow(px0 * pv - py0 * pu, 2))) / (pu * pu + pv * pv);
t2 = ((px - px0) * (px - px0) + (py - py0) * (py - py0)) / Math.sqrt(pu * pu + pv * pv) - t;
Tx = px0 + t * pu;
Ty = py0 + t * pv;
//console.log("TX: ", Tx);
//console.log("TY: ", Ty);
// Calculating the angle
{
if (Tx > 0 && Ty >= 0) {
fi = 2 * Math.PI - Math.atan(Ty / Tx);
}
else if (Tx < 0 && Ty >= 0) {
fi = Math.PI - Math.atan(Ty / Tx);
}
else if (Tx < 0 && Ty < 0) {
fi = Math.PI / 2 + Math.atan(Ty / Tx);
}
else if (Tx > 0 && Ty < 0) {
fi = -Math.atan(Ty / Tx);
}
else if (Tx = 0 && Ty >= 0) {
fi = 3 / 2 * Math.PI;
}
else if (Tx = 0 && Ty < 0) {
fi = Math.PI / 2;
}
}
//console.log("FI:", fi);
p = pu * Math.cos(fi) + pv * Math.sin(fi);
q = -pu * Math.sin(fi) + pv * Math.cos(fi);
cuu = 2 * q / (circle.M + 1);
cvv = 0;
q *= (1 - circle.M) / (1 + circle.M);
cu += cuu * Math.cos(-fi) + cvv * Math.sin(-fi);
cv += -cuu * Math.sin(-fi) + cvv * Math.cos(-fi);
//console.log("CU: ", cu);
//console.log("CV: ", cv);
pu = p * Math.cos(-fi) + q * Math.sin(-fi);
pv = -p * Math.sin(-fi) + q * Math.cos(-fi);
px = Tx + t2 * pu;
py = Ty + t2 * pv;
particle[i].x = px + circle.x;
particle[i].y = py + circle.y;
particle[i].vx = pu + circle.vx;
particle[i].vy = pv + circle.vy;
}
}
// Moving circle
{
circle.vx += cu;
circle.vy += cv;
circle.x += circle.vx;
circle.y += circle.vy;
for (var i in particle) {
while ((circle.x - particle[i].x) * (circle.x - particle[i].x) + (circle.y - particle[i].y) * (circle.y - particle[i].y) < circle.R * circle.R) {
particle[i].x += circle.vx / Math.abs(circle.vx);
particle[i].y += circle.vy / Math.abs(circle.vy);
}
}
circlePath.push({x: circle.x, y: circle.y});
if (Date.now() > lastDraw + 500) {
items.push({
y: Math.sqrt((circle.x - canvas.width / 2) * (circle.x - canvas.width / 2) + (circle.y - canvas.height / 2) * (circle.y - canvas.height / 2)),
x: (items.length + 1) / 2
});
drawChart(activeGraph);
lastDraw = Date.now();
}
}
//console.log(circle);
drawCircle();
for (var i in particle) drawParticle(particle[i]);
lastAnim = Date.now();
}
animation = window.requestAnimationFrame(animateParticles);
}
I need to be able to measure area with n amount amount of gps coordinates using javascript / jquery. I was not able to find this done using javascript or with n amount of coordinates so I decided to ask if anyone knows how to?
One example would be that I have 6 seperate lat / lon coordinates which I need to use to measure their area in square meters.
Any help? :)
This solution will only work for convex polygons formed from latitude/longitude points. The majority of the work is in rearranging the latitude/longitude points in counter-clockwise order. Once the reordering is done, you can find the area of the irregular polygon easily.
//make sure to add the first term to the end of both arrays
// ---------------SAME---------------
// | |
var lats = [25.767368, 34.088808, 40.727093, 25.767368];
// | |
var lons = [-80.18930, -118.40612, -73.97864, -80.18930];
//get the average center point of the polygon
var lats_sum = 0;
var lons_sum = 0;
for (var i=lats.length; i--;) {
lats_sum += lats[i];
lons_sum += lons[i];
}
var lat_origin = lats_sum / lats.length;
var lon_origin = lons_sum / lons.length;
//translate origin to (0,0) by shifting lat lons
//and calculate the standard angle of the point
var angles = new Array(lats.length);
for (var j=lats.length; j--;) {
lats[j] -= lat_origin;
lons[j] -= lon_origin;
if (lons[j] >= 0 && lats[j] >= 0) {
angles[j] = Math.abs(Math.atan(lats[j]/lons[j]) * 180 / Math.PI);
} else if (lons[j] < 0 && lats[j] >= 0) {
angles[j] = 90 + Math.abs(Math.atan(lats[j]/lons[j]) * 180 / Math.PI);
} else if (lons[j] < 0 && lats[j] < 0) {
angles[j] = 180 + Math.abs(Math.atan(lats[j]/lons[j]) * 180 / Math.PI);
} else if (lons[j] >= 0 && lats[j] < 0) {
angles[j] = 270 + Math.abs(Math.atan(lats[j]/lons[j]) * 180 / Math.PI);
}
}
//re-arrange the points from least to greatest angle
var cur_ang, cur_lat, cur_lon;
for (var l = 0; l < angles.length; l++) {
for (var k = 0; k < angles.length - 1; k++) {
cur_ang = angles[k];
cur_lat = lats[k];
cur_lon = lons[k];
if (cur_ang < angles[k+1]) {
angles[k] = angles[k+1];
lats[k] = lats[k+1];
lons[k] = lons[k+1];
angles[k+1] = cur_ang;
lats[k+1] = cur_lat;
lons[k+1] = cur_lon;
}
}
}
//calculate area for irregular polygon
var sum1 = 0;
var sum2 = 0;
for (var t = 0; t < lats.length; t++) {
if (t != lats.length - 1) {
sum1 += lats[t] * lons[t+1];
sum2 += lons[t] * lats[t+1];
} else {
sum1 += lats[t] * lons[0];
sum2 += lons[t] * lats[0];
}
}
var area = (sum1 - sum2) / 2.0;
console.log("Area: " + area);
Then when it comes to converting that lat lon area to meters squared I have no idea. This is a function I found online that may help you get a start.
// Use to convert from lat long dist to meters
function measure(lat1, lon1, lat2, lon2){ // generally used geo measurement function
var R = 6378.137; // Radius of earth in KM
var dLat = (lat2 - lat1) * Math.PI / 180;
var dLon = (lon2 - lon1) * Math.PI / 180;
var a = Math.sin(dLat/2) * Math.sin(dLat/2) +
Math.cos(lat1 * Math.PI / 180) * Math.cos(lat2 * Math.PI / 180) *
Math.sin(dLon/2) * Math.sin(dLon/2);
var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
var d = R * c;
return d * 1000; // meters
}
I've got the following JavaScript script for generating a 2D maze:
/*
* 3 June 2003, [[:en:User:Cyp]]:
* Maze, generated by my algorithm
* 24 October 2006, [[:en:User:quin]]:
* Source edited for clarity
* 25 January 2009, [[:en:User:DebateG]]:
* Source edited again for clarity and reusability
* 1 June 2009, [[:en:User:Nandhp]]:
* Source edited to produce SVG file when run from the command-line
* 7 January, 2011 [[:en:User:SharkD]]:
* Source converted to JavaScript
*
* This program was originally written by [[:en:User:Cyp]], who
* attached it to the image description page for an image generated by
* it on en.wikipedia. The image was licensed under CC-BY-SA-3.0/GFDL.
*/
/* Recreate a math function that exists in Java but not JavaScript. */
Math.nextInt = function (number) {
return Math.floor(Math.random() * number)
}
/* Recreate a system function that exists in Java but not JavaScript.
* Uncomment either WScript.Echo() or alert() depending on whether you are
* running the script from the Windows command-line or a Web page.
*/
function println(string)
{
// if inside Windows Scripting Host
WScript.Echo(string)
// if inside a Web page
// alert(string)
}
/* Define the bit masks */
var Constants =
{
WALL_ABOVE : 1,
WALL_BELOW : 2,
WALL_LEFT : 4,
WALL_RIGHT : 8,
QUEUED : 16,
IN_MAZE : 32
}
/* Construct a Maze with specified width, height, and cell_width */
function Maze(width, height, cell_width) {
if (width)
this.width = width;
else
this.width = 20;
if (height)
this.height = height;
else
this.height = 20;
if (cell_width)
this.cell_width = cell_width;
else
this.cell_width = 10;
this.maze = []
/* The maze generation algorithm. */
this.createMaze = function() {
var width = this.width
var height = this.height
var maze = this.maze
var x, y, n, d;
var dx = [ 0, 0, -1, 1 ];
var dy = [ -1, 1, 0, 0 ];
var todo = new Array(height * width);
var todonum = 0;
/* We want to create a maze on a grid. */
/* We start with a grid full of walls. */
for (x = 0; x < width; ++x) {
maze[x] = []
for (y = 0; y < height; ++y) {
if (x == 0 || x == width - 1 || y == 0 || y == height - 1) {
maze[x][y] = Constants.IN_MAZE;
}
else {
maze[x][y] = 63;
}
}
}
/* Select any square of the grid, to start with. */
x = 1 + Math.nextInt(width - 2);
y = 1 + Math.nextInt(height - 2);
/* Mark this square as connected to the maze. */
maze[x][y] &= ~48;
/* Remember the surrounding squares, as we will */
for (d = 0; d < 4; ++d) {
if ((maze[x + dx[d]][y + dy[d]] & Constants.QUEUED) != 0) {
/* want to connect them to the maze. */
todo[todonum++] = ((x + dx[d]) << Constants.QUEUED) | (y + dy[d]);
maze[x + dx[d]][y + dy[d]] &= ~Constants.QUEUED;
}
}
/* We won't be finished until all is connected. */
while (todonum > 0) {
/* We select one of the squares next to the maze. */
n = Math.nextInt(todonum);
x = todo[n] >> 16; /* the top 2 bytes of the data */
y = todo[n] & 65535; /* the bottom 2 bytes of the data */
/* We will connect it, so remove it from the queue. */
todo[n] = todo[--todonum];
/* Select a direction, which leads to the maze. */
do {
d = Math.nextInt(4);
}
while ((maze[x + dx[d]][y + dy[d]] & Constants.IN_MAZE) != 0);
/* Connect this square to the maze. */
maze[x][y] &= ~((1 << d) | Constants.IN_MAZE);
maze[x + dx[d]][y + dy[d]] &= ~(1 << (d ^ 1));
/* Remember the surrounding squares, which aren't */
for (d = 0; d < 4; ++d) {
if ((maze[x + dx[d]][y + dy[d]] & Constants.QUEUED) != 0) {
/* connected to the maze, and aren't yet queued to be. */
todo[todonum++] = ((x + dx[d]) << Constants.QUEUED) | (y + dy[d]);
maze[x + dx[d]][y + dy[d]] &= ~Constants.QUEUED;
}
}
/* Repeat until finished. */
}
/* Add an entrance and exit. */
maze[1][1] &= ~Constants.WALL_ABOVE;
maze[width - 2][height - 2] &= ~Constants.WALL_BELOW;
}
/* Called to write the maze to an SVG file. */
this.printSVG = function () {
println("<svg width=\"" + (width * cell_width) + "\" height=\"" + (height*cell_width) + "\" version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\">\n"
+ " <g stroke=\"black\" stroke-width=\"1\" stroke-linecap=\"round\">\n" + this.drawMaze() + " </g>\n</svg>\n");
}
/* Main maze-drawing loop. */
this.drawMaze = function () {
var x, y;
var width = this.width;
var height = this.height;
var cell_width = this.cell_width
var outstring = ""
for (x = 1; x < width - 1; ++x) {
for (y = 1; y < height - 1; ++y) {
if ((this.maze[x][y] & Constants.WALL_ABOVE) != 0)
outstring += this.drawLine(x * cell_width, y * cell_width, (x + 1) * cell_width, y * cell_width);
if ((this.maze[x][y] & Constants.WALL_BELOW) != 0)
outstring += this.drawLine(x * cell_width, (y + 1) * cell_width, (x + 1) * cell_width, (y + 1) * cell_width);
if ((this.maze[x][y] & Constants.WALL_LEFT) != 0)
outstring += this.drawLine(x * cell_width, y * cell_width, x * cell_width, (y + 1) * cell_width);
if ((this.maze[x][y] & Constants.WALL_RIGHT) != 0)
outstring += this.drawLine((x + 1) * cell_width, y * cell_width, (x + 1) * cell_width, (y + 1) * cell_width);
}
}
return outstring
}
/* Draw a line, either in the SVG file or on the screen. */
this.drawLine = function (x1, y1, x2, y2) {
return " <line x1=\"" + x1 + "\" y1=\"" + y1 + "\" x2=\"" + x2 + "\" y2=\"" + y2 + "\" />\n";
}
}
/* Initialization method that will be called when the program is
* run from the command-line. Maze will be written as SVG file. */
function main(args) {
var m = new Maze();
m.createMaze();
m.printSVG();
}
/* execute the program */
main()
I would like to extend the script such that it creates a six axis 3D maze. However, in order to do so I have to understand the bitwise operations and what they are being used for. Could someone please explain to me why the original author chose to use bitwise operations, and what it is exactly they are doing in the script?
Thanks!
[edit - conclusion]
Since the problem is now solved, FYI here's the 3D version of the script:
/*
* 3 June 2003, [[:en:User:Cyp]]:
* Maze, generated by my algorithm
* 24 October 2006, [[:en:User:quin]]:
* Source edited for clarity
* 25 January 2009, [[:en:User:DebateG]]:
* Source edited again for clarity and reusability
* 1 June 2009, [[:en:User:Nandhp]]:
* Source edited to produce SVG file when run from the command-line
* 7 January, 2011 [[:en:User:SharkD]]:
* Source converted to JavaScript and third axis added
*
* This program was originally written by [[:en:User:Cyp]], who
* attached it to the image description page for an image generated by
* it on en.wikipedia. The image was licensed under CC-BY-SA-3.0/GFDL.
*/
/* Recreate a math function that exists in Java but not JavaScript. */
Math.nextInt = function (number) {
return Math.floor(Math.random() * number)
}
/* Recreate a system function that exists in Java but not JavaScript.
* Uncomment either WScript.Echo() or alert() depending on whether you are
* running the script from the Windows command-line or a Web page.
*/
function println(string)
{
// if inside Windows Scripting Host
WScript.Echo(string)
// if inside a Web page
// alert(string)
}
/* Define the bit masks */
var WALL_ABOVE = 1;
var WALL_BELOW = 2;
var WALL_LEFT = 4;
var WALL_RIGHT = 8;
var WALL_FRONT = 16;
var WALL_BACK = 32;
var QUEUED = 64;
var IN_MAZE = 128;
/* Construct a Maze with specified lenx, leny, and cell_width */
function Maze(lenx, leny, lenz, cell_width) {
if (lenx)
this.lenx = lenx;
else
this.lenx = 20;
if (leny)
this.leny = leny;
else
this.leny = 20;
if (lenz)
this.lenz = lenz;
else
this.lenz = 8;
if (cell_width)
this.cell_width = cell_width;
else
this.cell_width = 10;
this.maze = []
/* The maze generation algorithm. */
this.createMaze = function() {
var lenx = this.lenx
var leny = this.leny
var lenz = this.lenz
var maze = this.maze
var x, y, z, n, d;
var dx = [ 0, 0, -1, 1, 0, 0 ];
var dy = [ -1, 1, 0, 0, 0, 0 ];
var dz = [ 0, 0, 0, 0, -1, 1 ];
var todo = new Array(leny * lenx * lenz);
var todonum = 0;
/* We want to create a maze on a grid. */
/* We start with a grid full of walls. */
/* Except for the outer walls which are left open? */
for (x = 0; x < lenx; ++x) {
maze[x] = []
for (y = 0; y < leny; ++y) {
maze[x][y] = []
for (z = 0; z < lenz; ++z)
{
if (x == 0 || x == lenx - 1 || y == 0 || y == leny - 1 || z == 0 || z == lenz - 1) {
maze[x][y][z] = IN_MAZE;
}
else {
maze[x][y][z] = WALL_ABOVE + WALL_BELOW + WALL_LEFT + WALL_RIGHT + WALL_FRONT + WALL_BACK + QUEUED + IN_MAZE; // DUNNO!!!! 255
}
}
}
}
/* Select random square of the grid, to start with. */
x = 1 + Math.nextInt(lenx - 2);
y = 1 + Math.nextInt(leny - 2);
z = 1 + Math.nextInt(lenz - 2);
/* Mark this square as connected to the maze. */
maze[x][y][z] &= ~(QUEUED + IN_MAZE);
/* Remember the surrounding squares, as we will... */
for (d = 0; d < 6; ++d) {
if ((maze[x + dx[d]][y + dy[d]][z + dz[d]] & QUEUED) != 0) {
/* ...want to connect them to the maze. */
todo[todonum++] = [x + dx[d], y + dy[d], z + dz[d]];
maze[x + dx[d]][y + dy[d]][z + dz[d]] &= ~QUEUED;
}
}
/* We won't be finished until all is connected. */
while (todonum > 0) {
/* We select one of the squares next to the maze. */
n = Math.nextInt(todonum);
x = todo[n][0];
y = todo[n][1];
z = todo[n][2];
/* We will connect it, so remove it from the queue. */
todo[n] = todo[--todonum];
/* Select a random direction, which leads to the maze. */
do {
d = Math.nextInt(6);
}
while ((maze[x + dx[d]][y + dy[d]][z + dz[d]] & IN_MAZE) != 0);
/* Connect this square to the maze. */
maze[x][y][z] &= ~((1 << d) | IN_MAZE);
maze[x + dx[d]][y + dy[d]][z + dz[d]] &= ~(1 << (d ^ 1));
/* Remember the surrounding squares, which aren't... */
for (d = 0; d < 6; ++d) {
if ((maze[x + dx[d]][y + dy[d]][z + dz[d]] & QUEUED) != 0) {
/* ...connected to the maze, and aren't yet queued to be. */
todo[todonum++] = [x + dx[d], y + dy[d], z + dz[d]];
maze[x + dx[d]][y + dy[d]][z + dz[d]] &= ~QUEUED;
}
}
/* Repeat until finished. */
}
/* Add an entrance and exit. */
maze[1][1][1] &= ~WALL_ABOVE;
maze[lenx - 2][leny - 2][lenz - 2] &= ~WALL_BELOW;
}
/* Called to write the maze to an SVG file. */
this.printSVG = function () {
println("<svg lenx=\"" + (lenx * cell_width) + "\" leny=\"" + (leny * lenz * cell_width) + "\" version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\">\n"
+ " <g stroke=\"black\" stroke-lenx=\"1\" stroke-linecap=\"round\">\n" + this.drawMaze() + " </g>\n</svg>\n");
}
/* Main maze-drawing loop. */
this.drawMaze = function () {
var x, y, z;
var lenx = this.lenx;
var leny = this.leny;
var lenz = this.lenz;
var cell_width = this.cell_width
var outstring = ""
for (x = 1; x < lenx - 1; ++x) {
for (y = 1; y < leny - 1; ++y) {
for (z = 1; z < lenz - 1; ++z) {
var z_pos = z * leny * cell_width;
if ((this.maze[x][y][z] & WALL_ABOVE) != 0)
outstring += this.drawLine
(
x * cell_width,
y * cell_width + z_pos,
(x + 1) * cell_width,
y * cell_width + z_pos
);
if ((this.maze[x][y][z] & WALL_BELOW) != 0)
outstring += this.drawLine
(
x * cell_width,
(y + 1) * cell_width + z_pos,
(x + 1) * cell_width,
(y + 1) * cell_width + z_pos
);
if ((this.maze[x][y][z] & WALL_LEFT) != 0)
outstring += this.drawLine
(
x * cell_width,
y * cell_width + z_pos,
x * cell_width,
(y + 1) * cell_width + z_pos
);
if ((this.maze[x][y][z] & WALL_RIGHT) != 0)
outstring += this.drawLine
(
(x + 1) * cell_width,
y * cell_width + z_pos,
(x + 1) * cell_width,
(y + 1) * cell_width + z_pos
);
if ((this.maze[x][y][z] & WALL_FRONT) != 0)
outstring += this.drawLine
(
x * cell_width + cell_width/3,
(y + 1) * cell_width - cell_width/3 + z_pos,
(x + 1) * cell_width - cell_width/3,
y * cell_width + cell_width/3 + z_pos
);
if ((this.maze[x][y][z] & WALL_BACK) != 0)
outstring += this.drawLine
(
x * cell_width + cell_width/3,
y * cell_width + cell_width/3 + z_pos,
(x + 1) * cell_width - cell_width/3,
(y + 1) * cell_width - cell_width/3 + z_pos
);
}
}
}
return outstring
}
/* Draw a line, either in the SVG file or on the screen. */
this.drawLine = function (x1, y1, x2, y2) {
return " <line x1=\"" + x1 + "\" y1=\"" + y1 + "\" x2=\"" + x2 + "\" y2=\"" + y2 + "\" />\n";
}
}
/* Initialization method that will be called when the program is
* run from the command-line. Maze will be written as SVG file. */
function main(args) {
var m = new Maze();
m.createMaze();
m.printSVG();
}
/* execute the program */
main()
The most common reason to use bitwise operations is because they are fast and allow for compact storage of information in integers.
That said, this script appears to be using them as flags on each square in the grid.
Take a look at the following code:
/* Define the bit masks */
var Constants =
{
WALL_ABOVE : 1,
WALL_BELOW : 2,
WALL_LEFT : 4,
WALL_RIGHT : 8,
QUEUED : 16,
IN_MAZE : 32
}
Each of these constants occupies one bit in an integer. To check if the flag is set, all you need to do is determine if its bit location is set to 1 (typically by ANDing with that number and comparing to 0). To set a flag, you simply set that bit value (typically by ORing with the number). This is what the bitwise operators are doing.