I've been trying to add a (code pen) animation on my website and I'm honestly not sure what I'm missing on this one. I have tried running it in jsfiddle as well and it tells me that delaunay is not defined. https://codepen.io/hduffin1/pen/QOMZJg I'm not too sure what I'm doing wrong since the code works inside of code pen and I have been able to replicate other ones that I've tried using from code pen but for whatever reason, I can't seem to get this one to work.
Html
<canvas id="stars" width="300" height="300"></canvas>
CSS
html,
body {
margin: 0;
padding: 0;
}
body {
background-color: #31102f; //#280B29
background: radial-gradient(
ellipse at center,
rgba(49, 16, 47, 1) 0%,
rgba(40, 11, 41, 1) 100%
);
}
#stars {
display: block;
position: relative;
width: 100%;
height: 16rem;
height: 100vh;
z-index: 1;
}
JS
/**
* Stars
* Inspired by Steve Courtney's poster art for Celsius GS's Drifter - http://celsiusgs.com/drifter/posters.php
* by Cory Hughart - http://coryhughart.com
*/
// Settings
var particleCount = 40,
flareCount = 10,
motion = 0.05,
tilt = 0.05,
color = '#FFEED4',
particleSizeBase = 1,
particleSizeMultiplier = 0.5,
flareSizeBase = 100,
flareSizeMultiplier = 100,
lineWidth = 1,
linkChance = 75, // chance per frame of link, higher = smaller chance
linkLengthMin = 5, // min linked vertices
linkLengthMax = 7, // max linked vertices
linkOpacity = 0.25; // number between 0 & 1
linkFade = 90, // link fade-out frames
linkSpeed = 1, // distance a link travels in 1 frame
glareAngle = -60,
glareOpacityMultiplier = 0.05,
renderParticles = true,
renderParticleGlare = true,
renderFlares = true,
renderLinks = true,
renderMesh = false,
flicker = true,
flickerSmoothing = 15, // higher = smoother flicker
blurSize = 0,
orbitTilt = true,
randomMotion = true,
noiseLength = 1000,
noiseStrength = 1;
var canvas = document.getElementById('stars'),
//orbits = document.getElementById('orbits'),
context = canvas.getContext('2d'),
mouse = { x: 0, y: 0 },
m = {},
r = 0,
c = 1000, // multiplier for delaunay points, since floats too small can mess up the algorithm
n = 0,
nAngle = (Math.PI * 2) / noiseLength,
nRad = 100,
nScale = 0.5,
nPos = {x: 0, y: 0},
points = [],
vertices = [],
triangles = [],
links = [],
particles = [],
flares = [];
function init() {
var i, j, k;
// requestAnimFrame polyfill
window.requestAnimFrame = (function(){
return window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
function( callback ){
window.setTimeout(callback, 1000 / 60);
};
})();
// Fade in background
/*
var background = document.getElementById('background'),
bgImg = new Image(),
bgURL = '/img/background.jpg';
bgImg.onload = function() {
//console.log('background loaded');
background.style.backgroundImage = 'url("'+bgURL+'")';
background.className += ' loaded';
}
bgImg.src = bgURL;
*/
// Size canvas
resize();
mouse.x = canvas.clientWidth / 2;
mouse.y = canvas.clientHeight / 2;
// Create particle positions
for (i = 0; i < particleCount; i++) {
var p = new Particle();
particles.push(p);
points.push([p.x*c, p.y*c]);
}
//console.log(JSON.stringify(points));
// Delaunay triangulation
//var Delaunay = require('delaunay-fast');
vertices = Delaunay.triangulate(points);
//console.log(JSON.stringify(vertices));
// Create an array of "triangles" (groups of 3 indices)
var tri = [];
for (i = 0; i < vertices.length; i++) {
if (tri.length == 3) {
triangles.push(tri);
tri = [];
}
tri.push(vertices[i]);
}
//console.log(JSON.stringify(triangles));
// Tell all the particles who their neighbors are
for (i = 0; i < particles.length; i++) {
// Loop through all tirangles
for (j = 0; j < triangles.length; j++) {
// Check if this particle's index is in this triangle
k = triangles[j].indexOf(i);
// If it is, add its neighbors to the particles contacts list
if (k !== -1) {
triangles[j].forEach(function(value, index, array) {
if (value !== i && particles[i].neighbors.indexOf(value) == -1) {
particles[i].neighbors.push(value);
}
});
}
}
}
//console.log(JSON.stringify(particles));
if (renderFlares) {
// Create flare positions
for (i = 0; i < flareCount; i++) {
flares.push(new Flare());
}
}
// Motion mode
//if (Modernizr && Modernizr.deviceorientation) {
if ('ontouchstart' in document.documentElement && window.DeviceOrientationEvent) {
console.log('Using device orientation');
window.addEventListener('deviceorientation', function(e) {
mouse.x = (canvas.clientWidth / 2) - ((e.gamma / 90) * (canvas.clientWidth / 2) * 2);
mouse.y = (canvas.clientHeight / 2) - ((e.beta / 90) * (canvas.clientHeight / 2) * 2);
//console.log('Center: x:'+(canvas.clientWidth/2)+' y:'+(canvas.clientHeight/2));
//console.log('Orientation: x:'+mouse.x+' ('+e.gamma+') y:'+mouse.y+' ('+e.beta+')');
}, true);
}
else {
// Mouse move listener
console.log('Using mouse movement');
document.body.addEventListener('mousemove', function(e) {
//console.log('moved');
mouse.x = e.clientX;
mouse.y = e.clientY;
});
}
// Random motion
if (randomMotion) {
//var SimplexNoise = require('simplex-noise');
//var simplex = new SimplexNoise();
}
// Animation loop
(function animloop(){
requestAnimFrame(animloop);
resize();
render();
})();
}
function render() {
if (randomMotion) {
n++;
if (n >= noiseLength) {
n = 0;
}
nPos = noisePoint(n);
//console.log('NOISE x:'+nPos.x+' y:'+nPos.y);
}
// Clear
context.clearRect(0, 0, canvas.width, canvas.height);
if (blurSize > 0) {
context.shadowBlur = blurSize;
context.shadowColor = color;
}
if (renderParticles) {
// Render particles
for (var i = 0; i < particleCount; i++) {
particles[i].render();
}
}
if (renderMesh) {
// Render all lines
context.beginPath();
for (var v = 0; v < vertices.length-1; v++) {
// Splits the array into triplets
if ((v + 1) % 3 === 0) { continue; }
var p1 = particles[vertices[v]],
p2 = particles[vertices[v+1]];
//console.log('Line: '+p1.x+','+p1.y+'->'+p2.x+','+p2.y);
var pos1 = position(p1.x, p1.y, p1.z),
pos2 = position(p2.x, p2.y, p2.z);
context.moveTo(pos1.x, pos1.y);
context.lineTo(pos2.x, pos2.y);
}
context.strokeStyle = color;
context.lineWidth = lineWidth;
context.stroke();
context.closePath();
}
if (renderLinks) {
// Possibly start a new link
if (random(0, linkChance) == linkChance) {
var length = random(linkLengthMin, linkLengthMax);
var start = random(0, particles.length-1);
startLink(start, length);
}
// Render existing links
// Iterate in reverse so that removing items doesn't affect the loop
for (var l = links.length-1; l >= 0; l--) {
if (links[l] && !links[l].finished) {
links[l].render();
}
else {
delete links[l];
}
}
}
if (renderFlares) {
// Render flares
for (var j = 0; j < flareCount; j++) {
flares[j].render();
}
}
/*
if (orbitTilt) {
var tiltX = -(((canvas.clientWidth / 2) - mouse.x + ((nPos.x - 0.5) * noiseStrength)) * tilt),
tiltY = (((canvas.clientHeight / 2) - mouse.y + ((nPos.y - 0.5) * noiseStrength)) * tilt);
orbits.style.transform = 'rotateY('+tiltX+'deg) rotateX('+tiltY+'deg)';
}
*/
}
function resize() {
canvas.width = window.innerWidth * (window.devicePixelRatio || 1);
canvas.height = canvas.width * (canvas.clientHeight / canvas.clientWidth);
}
function startLink(vertex, length) {
//console.log('LINK from '+vertex+' (length '+length+')');
links.push(new Link(vertex, length));
}
// Particle class
var Particle = function() {
this.x = random(-0.1, 1.1, true);
this.y = random(-0.1, 1.1, true);
this.z = random(0,4);
this.color = color;
this.opacity = random(0.1,1,true);
this.flicker = 0;
this.neighbors = []; // placeholder for neighbors
};
Particle.prototype.render = function() {
var pos = position(this.x, this.y, this.z),
r = ((this.z * particleSizeMultiplier) + particleSizeBase) * (sizeRatio() / 1000),
o = this.opacity;
if (flicker) {
var newVal = random(-0.5, 0.5, true);
this.flicker += (newVal - this.flicker) / flickerSmoothing;
if (this.flicker > 0.5) this.flicker = 0.5;
if (this.flicker < -0.5) this.flicker = -0.5;
o += this.flicker;
if (o > 1) o = 1;
if (o < 0) o = 0;
}
context.fillStyle = this.color;
context.globalAlpha = o;
context.beginPath();
context.arc(pos.x, pos.y, r, 0, 2 * Math.PI, false);
context.fill();
context.closePath();
if (renderParticleGlare) {
context.globalAlpha = o * glareOpacityMultiplier;
/*
context.ellipse(pos.x, pos.y, r * 30, r, 90 * (Math.PI / 180), 0, 2 * Math.PI, false);
context.fill();
context.closePath();
*/
context.ellipse(pos.x, pos.y, r * 100, r, (glareAngle - ((nPos.x - 0.5) * noiseStrength * motion)) * (Math.PI / 180), 0, 2 * Math.PI, false);
context.fill();
context.closePath();
}
context.globalAlpha = 1;
};
// Flare class
var Flare = function() {
this.x = random(-0.25, 1.25, true);
this.y = random(-0.25, 1.25, true);
this.z = random(0,2);
this.color = color;
this.opacity = random(0.001, 0.01, true);
};
Flare.prototype.render = function() {
var pos = position(this.x, this.y, this.z),
r = ((this.z * flareSizeMultiplier) + flareSizeBase) * (sizeRatio() / 1000);
// Feathered circles
/*
var grad = context.createRadialGradient(x+r,y+r,0,x+r,y+r,r);
grad.addColorStop(0, 'rgba(255,255,255,'+f.o+')');
grad.addColorStop(0.8, 'rgba(255,255,255,'+f.o+')');
grad.addColorStop(1, 'rgba(255,255,255,0)');
context.fillStyle = grad;
context.beginPath();
context.fillRect(x, y, r*2, r*2);
context.closePath();
*/
context.beginPath();
context.globalAlpha = this.opacity;
context.arc(pos.x, pos.y, r, 0, 2 * Math.PI, false);
context.fillStyle = this.color;
context.fill();
context.closePath();
context.globalAlpha = 1;
};
// Link class
var Link = function(startVertex, numPoints) {
this.length = numPoints;
this.verts = [startVertex];
this.stage = 0;
this.linked = [startVertex];
this.distances = [];
this.traveled = 0;
this.fade = 0;
this.finished = false;
};
Link.prototype.render = function() {
// Stages:
// 0. Vertex collection
// 1. Render line reaching from vertex to vertex
// 2. Fade out
// 3. Finished (delete me)
var i, p, pos, points;
switch (this.stage) {
// VERTEX COLLECTION STAGE
case 0:
// Grab the last member of the link
var last = particles[this.verts[this.verts.length-1]];
//console.log(JSON.stringify(last));
if (last && last.neighbors && last.neighbors.length > 0) {
// Grab a random neighbor
var neighbor = last.neighbors[random(0, last.neighbors.length-1)];
// If we haven't seen that particle before, add it to the link
if (this.verts.indexOf(neighbor) == -1) {
this.verts.push(neighbor);
}
// If we have seen that article before, we'll just wait for the next frame
}
else {
//console.log(this.verts[0]+' prematurely moving to stage 3 (0)');
this.stage = 3;
this.finished = true;
}
if (this.verts.length >= this.length) {
// Calculate all distances at once
for (i = 0; i < this.verts.length-1; i++) {
var p1 = particles[this.verts[i]],
p2 = particles[this.verts[i+1]],
dx = p1.x - p2.x,
dy = p1.y - p2.y,
dist = Math.sqrt(dx*dx + dy*dy);
this.distances.push(dist);
}
//console.log('Distances: '+JSON.stringify(this.distances));
//console.log('verts: '+this.verts.length+' distances: '+this.distances.length);
//console.log(this.verts[0]+' moving to stage 1');
this.stage = 1;
}
break;
// RENDER LINE ANIMATION STAGE
case 1:
if (this.distances.length > 0) {
points = [];
//var a = 1;
// Gather all points already linked
for (i = 0; i < this.linked.length; i++) {
p = particles[this.linked[i]];
pos = position(p.x, p.y, p.z);
points.push([pos.x, pos.y]);
}
var linkSpeedRel = linkSpeed * 0.00001 * canvas.width;
this.traveled += linkSpeedRel;
var d = this.distances[this.linked.length-1];
// Calculate last point based on linkSpeed and distance travelled to next point
if (this.traveled >= d) {
this.traveled = 0;
// We've reached the next point, add coordinates to array
//console.log(this.verts[0]+' reached vertex '+(this.linked.length+1)+' of '+this.verts.length);
this.linked.push(this.verts[this.linked.length]);
p = particles[this.linked[this.linked.length-1]];
pos = position(p.x, p.y, p.z);
points.push([pos.x, pos.y]);
if (this.linked.length >= this.verts.length) {
//console.log(this.verts[0]+' moving to stage 2 (1)');
this.stage = 2;
}
}
else {
// We're still travelling to the next point, get coordinates at travel distance
// http://math.stackexchange.com/a/85582
var a = particles[this.linked[this.linked.length-1]],
b = particles[this.verts[this.linked.length]],
t = d - this.traveled,
x = ((this.traveled * b.x) + (t * a.x)) / d,
y = ((this.traveled * b.y) + (t * a.y)) / d,
z = ((this.traveled * b.z) + (t * a.z)) / d;
pos = position(x, y, z);
//console.log(this.verts[0]+' traveling to vertex '+(this.linked.length+1)+' of '+this.verts.length+' ('+this.traveled+' of '+this.distances[this.linked.length]+')');
points.push([pos.x, pos.y]);
}
this.drawLine(points);
}
else {
//console.log(this.verts[0]+' prematurely moving to stage 3 (1)');
this.stage = 3;
this.finished = true;
}
break;
// FADE OUT STAGE
case 2:
if (this.verts.length > 1) {
if (this.fade < linkFade) {
this.fade++;
// Render full link between all vertices and fade over time
points = [];
var alpha = (1 - (this.fade / linkFade)) * linkOpacity;
for (i = 0; i < this.verts.length; i++) {
p = particles[this.verts[i]];
pos = position(p.x, p.y, p.z);
points.push([pos.x, pos.y]);
}
this.drawLine(points, alpha);
}
else {
//console.log(this.verts[0]+' moving to stage 3 (2a)');
this.stage = 3;
this.finished = true;
}
}
else {
//console.log(this.verts[0]+' prematurely moving to stage 3 (2b)');
this.stage = 3;
this.finished = true;
}
break;
// FINISHED STAGE
case 3:
default:
this.finished = true;
break;
}
};
Link.prototype.drawLine = function(points, alpha) {
if (typeof alpha !== 'number') alpha = linkOpacity;
if (points.length > 1 && alpha > 0) {
//console.log(this.verts[0]+': Drawing line '+alpha);
context.globalAlpha = alpha;
context.beginPath();
for (var i = 0; i < points.length-1; i++) {
context.moveTo(points[i][0], points[i][1]);
context.lineTo(points[i+1][0], points[i+1][1]);
}
context.strokeStyle = color;
context.lineWidth = lineWidth;
context.stroke();
context.closePath();
context.globalAlpha = 1;
}
};
// Utils
function noisePoint(i) {
var a = nAngle * i,
cosA = Math.cos(a),
sinA = Math.sin(a),
//value = simplex.noise2D(nScale * cosA + nScale, nScale * sinA + nScale),
//rad = nRad + value;
rad = nRad;
return {
x: rad * cosA,
y: rad * sinA
};
}
function position(x, y, z) {
return {
x: (x * canvas.width) + ((((canvas.width / 2) - mouse.x + ((nPos.x - 0.5) * noiseStrength)) * z) * motion),
y: (y * canvas.height) + ((((canvas.height / 2) - mouse.y + ((nPos.y - 0.5) * noiseStrength)) * z) * motion)
};
}
function sizeRatio() {
return canvas.width >= canvas.height ? canvas.width : canvas.height;
}
function random(min, max, float) {
return float ?
Math.random() * (max - min) + min :
Math.floor(Math.random() * (max - min + 1)) + min;
}
// init
if (canvas) init();
When I entered 'https://codepen.io/hduffin1/pen/QOMZJg', 'delaunay.js' is included in the setting.
Add the following script and it should work.
<script src="https://rawgit.com/ironwallaby/delaunay/master/delaunay.js"></script>
/**
* Stars
* Inspired by Steve Courtney's poster art for Celsius GS's Drifter - http://celsiusgs.com/drifter/posters.php
* by Cory Hughart - http://coryhughart.com
*/
// Settings
var particleCount = 40,
flareCount = 10,
motion = 0.05,
tilt = 0.05,
color = '#FFEED4',
particleSizeBase = 1,
particleSizeMultiplier = 0.5,
flareSizeBase = 100,
flareSizeMultiplier = 100,
lineWidth = 1,
linkChance = 75, // chance per frame of link, higher = smaller chance
linkLengthMin = 5, // min linked vertices
linkLengthMax = 7, // max linked vertices
linkOpacity = 0.25; // number between 0 & 1
linkFade = 90, // link fade-out frames
linkSpeed = 1, // distance a link travels in 1 frame
glareAngle = -60,
glareOpacityMultiplier = 0.05,
renderParticles = true,
renderParticleGlare = true,
renderFlares = true,
renderLinks = true,
renderMesh = false,
flicker = true,
flickerSmoothing = 15, // higher = smoother flicker
blurSize = 0,
orbitTilt = true,
randomMotion = true,
noiseLength = 1000,
noiseStrength = 1;
var canvas = document.getElementById('stars'),
//orbits = document.getElementById('orbits'),
context = canvas.getContext('2d'),
mouse = { x: 0, y: 0 },
m = {},
r = 0,
c = 1000, // multiplier for delaunay points, since floats too small can mess up the algorithm
n = 0,
nAngle = (Math.PI * 2) / noiseLength,
nRad = 100,
nScale = 0.5,
nPos = {x: 0, y: 0},
points = [],
vertices = [],
triangles = [],
links = [],
particles = [],
flares = [];
function init() {
var i, j, k;
// requestAnimFrame polyfill
window.requestAnimFrame = (function(){
return window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
function( callback ){
window.setTimeout(callback, 1000 / 60);
};
})();
// Fade in background
/*
var background = document.getElementById('background'),
bgImg = new Image(),
bgURL = '/img/background.jpg';
bgImg.onload = function() {
//console.log('background loaded');
background.style.backgroundImage = 'url("'+bgURL+'")';
background.className += ' loaded';
}
bgImg.src = bgURL;
*/
// Size canvas
resize();
mouse.x = canvas.clientWidth / 2;
mouse.y = canvas.clientHeight / 2;
// Create particle positions
for (i = 0; i < particleCount; i++) {
var p = new Particle();
particles.push(p);
points.push([p.x*c, p.y*c]);
}
//console.log(JSON.stringify(points));
// Delaunay triangulation
//var Delaunay = require('delaunay-fast');
vertices = Delaunay.triangulate(points);
//console.log(JSON.stringify(vertices));
// Create an array of "triangles" (groups of 3 indices)
var tri = [];
for (i = 0; i < vertices.length; i++) {
if (tri.length == 3) {
triangles.push(tri);
tri = [];
}
tri.push(vertices[i]);
}
//console.log(JSON.stringify(triangles));
// Tell all the particles who their neighbors are
for (i = 0; i < particles.length; i++) {
// Loop through all tirangles
for (j = 0; j < triangles.length; j++) {
// Check if this particle's index is in this triangle
k = triangles[j].indexOf(i);
// If it is, add its neighbors to the particles contacts list
if (k !== -1) {
triangles[j].forEach(function(value, index, array) {
if (value !== i && particles[i].neighbors.indexOf(value) == -1) {
particles[i].neighbors.push(value);
}
});
}
}
}
//console.log(JSON.stringify(particles));
if (renderFlares) {
// Create flare positions
for (i = 0; i < flareCount; i++) {
flares.push(new Flare());
}
}
// Motion mode
//if (Modernizr && Modernizr.deviceorientation) {
if ('ontouchstart' in document.documentElement && window.DeviceOrientationEvent) {
console.log('Using device orientation');
window.addEventListener('deviceorientation', function(e) {
mouse.x = (canvas.clientWidth / 2) - ((e.gamma / 90) * (canvas.clientWidth / 2) * 2);
mouse.y = (canvas.clientHeight / 2) - ((e.beta / 90) * (canvas.clientHeight / 2) * 2);
//console.log('Center: x:'+(canvas.clientWidth/2)+' y:'+(canvas.clientHeight/2));
//console.log('Orientation: x:'+mouse.x+' ('+e.gamma+') y:'+mouse.y+' ('+e.beta+')');
}, true);
}
else {
// Mouse move listener
console.log('Using mouse movement');
document.body.addEventListener('mousemove', function(e) {
//console.log('moved');
mouse.x = e.clientX;
mouse.y = e.clientY;
});
}
// Random motion
if (randomMotion) {
//var SimplexNoise = require('simplex-noise');
//var simplex = new SimplexNoise();
}
// Animation loop
(function animloop(){
requestAnimFrame(animloop);
resize();
render();
})();
}
function render() {
if (randomMotion) {
n++;
if (n >= noiseLength) {
n = 0;
}
nPos = noisePoint(n);
//console.log('NOISE x:'+nPos.x+' y:'+nPos.y);
}
// Clear
context.clearRect(0, 0, canvas.width, canvas.height);
if (blurSize > 0) {
context.shadowBlur = blurSize;
context.shadowColor = color;
}
if (renderParticles) {
// Render particles
for (var i = 0; i < particleCount; i++) {
particles[i].render();
}
}
if (renderMesh) {
// Render all lines
context.beginPath();
for (var v = 0; v < vertices.length-1; v++) {
// Splits the array into triplets
if ((v + 1) % 3 === 0) { continue; }
var p1 = particles[vertices[v]],
p2 = particles[vertices[v+1]];
//console.log('Line: '+p1.x+','+p1.y+'->'+p2.x+','+p2.y);
var pos1 = position(p1.x, p1.y, p1.z),
pos2 = position(p2.x, p2.y, p2.z);
context.moveTo(pos1.x, pos1.y);
context.lineTo(pos2.x, pos2.y);
}
context.strokeStyle = color;
context.lineWidth = lineWidth;
context.stroke();
context.closePath();
}
if (renderLinks) {
// Possibly start a new link
if (random(0, linkChance) == linkChance) {
var length = random(linkLengthMin, linkLengthMax);
var start = random(0, particles.length-1);
startLink(start, length);
}
// Render existing links
// Iterate in reverse so that removing items doesn't affect the loop
for (var l = links.length-1; l >= 0; l--) {
if (links[l] && !links[l].finished) {
links[l].render();
}
else {
delete links[l];
}
}
}
if (renderFlares) {
// Render flares
for (var j = 0; j < flareCount; j++) {
flares[j].render();
}
}
/*
if (orbitTilt) {
var tiltX = -(((canvas.clientWidth / 2) - mouse.x + ((nPos.x - 0.5) * noiseStrength)) * tilt),
tiltY = (((canvas.clientHeight / 2) - mouse.y + ((nPos.y - 0.5) * noiseStrength)) * tilt);
orbits.style.transform = 'rotateY('+tiltX+'deg) rotateX('+tiltY+'deg)';
}
*/
}
function resize() {
canvas.width = window.innerWidth * (window.devicePixelRatio || 1);
canvas.height = canvas.width * (canvas.clientHeight / canvas.clientWidth);
}
function startLink(vertex, length) {
//console.log('LINK from '+vertex+' (length '+length+')');
links.push(new Link(vertex, length));
}
// Particle class
var Particle = function() {
this.x = random(-0.1, 1.1, true);
this.y = random(-0.1, 1.1, true);
this.z = random(0,4);
this.color = color;
this.opacity = random(0.1,1,true);
this.flicker = 0;
this.neighbors = []; // placeholder for neighbors
};
Particle.prototype.render = function() {
var pos = position(this.x, this.y, this.z),
r = ((this.z * particleSizeMultiplier) + particleSizeBase) * (sizeRatio() / 1000),
o = this.opacity;
if (flicker) {
var newVal = random(-0.5, 0.5, true);
this.flicker += (newVal - this.flicker) / flickerSmoothing;
if (this.flicker > 0.5) this.flicker = 0.5;
if (this.flicker < -0.5) this.flicker = -0.5;
o += this.flicker;
if (o > 1) o = 1;
if (o < 0) o = 0;
}
context.fillStyle = this.color;
context.globalAlpha = o;
context.beginPath();
context.arc(pos.x, pos.y, r, 0, 2 * Math.PI, false);
context.fill();
context.closePath();
if (renderParticleGlare) {
context.globalAlpha = o * glareOpacityMultiplier;
/*
context.ellipse(pos.x, pos.y, r * 30, r, 90 * (Math.PI / 180), 0, 2 * Math.PI, false);
context.fill();
context.closePath();
*/
context.ellipse(pos.x, pos.y, r * 100, r, (glareAngle - ((nPos.x - 0.5) * noiseStrength * motion)) * (Math.PI / 180), 0, 2 * Math.PI, false);
context.fill();
context.closePath();
}
context.globalAlpha = 1;
};
// Flare class
var Flare = function() {
this.x = random(-0.25, 1.25, true);
this.y = random(-0.25, 1.25, true);
this.z = random(0,2);
this.color = color;
this.opacity = random(0.001, 0.01, true);
};
Flare.prototype.render = function() {
var pos = position(this.x, this.y, this.z),
r = ((this.z * flareSizeMultiplier) + flareSizeBase) * (sizeRatio() / 1000);
// Feathered circles
/*
var grad = context.createRadialGradient(x+r,y+r,0,x+r,y+r,r);
grad.addColorStop(0, 'rgba(255,255,255,'+f.o+')');
grad.addColorStop(0.8, 'rgba(255,255,255,'+f.o+')');
grad.addColorStop(1, 'rgba(255,255,255,0)');
context.fillStyle = grad;
context.beginPath();
context.fillRect(x, y, r*2, r*2);
context.closePath();
*/
context.beginPath();
context.globalAlpha = this.opacity;
context.arc(pos.x, pos.y, r, 0, 2 * Math.PI, false);
context.fillStyle = this.color;
context.fill();
context.closePath();
context.globalAlpha = 1;
};
// Link class
var Link = function(startVertex, numPoints) {
this.length = numPoints;
this.verts = [startVertex];
this.stage = 0;
this.linked = [startVertex];
this.distances = [];
this.traveled = 0;
this.fade = 0;
this.finished = false;
};
Link.prototype.render = function() {
// Stages:
// 0. Vertex collection
// 1. Render line reaching from vertex to vertex
// 2. Fade out
// 3. Finished (delete me)
var i, p, pos, points;
switch (this.stage) {
// VERTEX COLLECTION STAGE
case 0:
// Grab the last member of the link
var last = particles[this.verts[this.verts.length-1]];
//console.log(JSON.stringify(last));
if (last && last.neighbors && last.neighbors.length > 0) {
// Grab a random neighbor
var neighbor = last.neighbors[random(0, last.neighbors.length-1)];
// If we haven't seen that particle before, add it to the link
if (this.verts.indexOf(neighbor) == -1) {
this.verts.push(neighbor);
}
// If we have seen that particle before, we'll just wait for the next frame
}
else {
//console.log(this.verts[0]+' prematurely moving to stage 3 (0)');
this.stage = 3;
this.finished = true;
}
if (this.verts.length >= this.length) {
// Calculate all distances at once
for (i = 0; i < this.verts.length-1; i++) {
var p1 = particles[this.verts[i]],
p2 = particles[this.verts[i+1]],
dx = p1.x - p2.x,
dy = p1.y - p2.y,
dist = Math.sqrt(dx*dx + dy*dy);
this.distances.push(dist);
}
//console.log('Distances: '+JSON.stringify(this.distances));
//console.log('verts: '+this.verts.length+' distances: '+this.distances.length);
//console.log(this.verts[0]+' moving to stage 1');
this.stage = 1;
}
break;
// RENDER LINE ANIMATION STAGE
case 1:
if (this.distances.length > 0) {
points = [];
//var a = 1;
// Gather all points already linked
for (i = 0; i < this.linked.length; i++) {
p = particles[this.linked[i]];
pos = position(p.x, p.y, p.z);
points.push([pos.x, pos.y]);
}
var linkSpeedRel = linkSpeed * 0.00001 * canvas.width;
this.traveled += linkSpeedRel;
var d = this.distances[this.linked.length-1];
// Calculate last point based on linkSpeed and distance travelled to next point
if (this.traveled >= d) {
this.traveled = 0;
// We've reached the next point, add coordinates to array
//console.log(this.verts[0]+' reached vertex '+(this.linked.length+1)+' of '+this.verts.length);
this.linked.push(this.verts[this.linked.length]);
p = particles[this.linked[this.linked.length-1]];
pos = position(p.x, p.y, p.z);
points.push([pos.x, pos.y]);
if (this.linked.length >= this.verts.length) {
//console.log(this.verts[0]+' moving to stage 2 (1)');
this.stage = 2;
}
}
else {
// We're still travelling to the next point, get coordinates at travel distance
// http://math.stackexchange.com/a/85582
var a = particles[this.linked[this.linked.length-1]],
b = particles[this.verts[this.linked.length]],
t = d - this.traveled,
x = ((this.traveled * b.x) + (t * a.x)) / d,
y = ((this.traveled * b.y) + (t * a.y)) / d,
z = ((this.traveled * b.z) + (t * a.z)) / d;
pos = position(x, y, z);
//console.log(this.verts[0]+' traveling to vertex '+(this.linked.length+1)+' of '+this.verts.length+' ('+this.traveled+' of '+this.distances[this.linked.length]+')');
points.push([pos.x, pos.y]);
}
this.drawLine(points);
}
else {
//console.log(this.verts[0]+' prematurely moving to stage 3 (1)');
this.stage = 3;
this.finished = true;
}
break;
// FADE OUT STAGE
case 2:
if (this.verts.length > 1) {
if (this.fade < linkFade) {
this.fade++;
// Render full link between all vertices and fade over time
points = [];
var alpha = (1 - (this.fade / linkFade)) * linkOpacity;
for (i = 0; i < this.verts.length; i++) {
p = particles[this.verts[i]];
pos = position(p.x, p.y, p.z);
points.push([pos.x, pos.y]);
}
this.drawLine(points, alpha);
}
else {
//console.log(this.verts[0]+' moving to stage 3 (2a)');
this.stage = 3;
this.finished = true;
}
}
else {
//console.log(this.verts[0]+' prematurely moving to stage 3 (2b)');
this.stage = 3;
this.finished = true;
}
break;
// FINISHED STAGE
case 3:
default:
this.finished = true;
break;
}
};
Link.prototype.drawLine = function(points, alpha) {
if (typeof alpha !== 'number') alpha = linkOpacity;
if (points.length > 1 && alpha > 0) {
//console.log(this.verts[0]+': Drawing line '+alpha);
context.globalAlpha = alpha;
context.beginPath();
for (var i = 0; i < points.length-1; i++) {
context.moveTo(points[i][0], points[i][1]);
context.lineTo(points[i+1][0], points[i+1][1]);
}
context.strokeStyle = color;
context.lineWidth = lineWidth;
context.stroke();
context.closePath();
context.globalAlpha = 1;
}
};
// Utils
function noisePoint(i) {
var a = nAngle * i,
cosA = Math.cos(a),
sinA = Math.sin(a),
//value = simplex.noise2D(nScale * cosA + nScale, nScale * sinA + nScale),
//rad = nRad + value;
rad = nRad;
return {
x: rad * cosA,
y: rad * sinA
};
}
function position(x, y, z) {
return {
x: (x * canvas.width) + ((((canvas.width / 2) - mouse.x + ((nPos.x - 0.5) * noiseStrength)) * z) * motion),
y: (y * canvas.height) + ((((canvas.height / 2) - mouse.y + ((nPos.y - 0.5) * noiseStrength)) * z) * motion)
};
}
function sizeRatio() {
return canvas.width >= canvas.height ? canvas.width : canvas.height;
}
function random(min, max, float) {
return float ?
Math.random() * (max - min) + min :
Math.floor(Math.random() * (max - min + 1)) + min;
}
// init
if (canvas) init();
html,
body {
margin: 0;
padding: 0;
}
body {
background-color: #31102F;
background: radial-gradient(ellipse at center, #31102f 0%, #280b29 100%);
}
#stars {
display: block;
position: relative;
width: 100%;
height: 16rem;
height: 100vh;
z-index: 1;
}
<script src="https://rawgit.com/ironwallaby/delaunay/master/delaunay.js"></script>
<canvas id="stars" width="300" height="300"></canvas>
In this script I’m trying to make a coordinate plane with two dots/circles. When I add the second dot in the code, it only shows the second one.
The part with the dots is this piece of code:
point(AX, AY, false, 'red', 6)
point(BX, BY, false, 'red', 6)
Can someone please help me with this problem? Thanks a lot!
start();
function start() {
console.clear();
document.getElementById("canvas");
var ctx = canvas.getContext("2d");
var AX = document.getElementById("AX").value;
var AY = document.getElementById("AY").value;
var BX = document.getElementById("BX").value;
var BY = document.getElementById("BY").value;
var SQUARE_SIZE = 30;
var XSCALE = 1;
var YSCALE = 1;
var centerX = 0;
var centerY = 0;
selected = [];
points = [];
lines = [];
segments = [];
history = [];
circles = [];
function point(x, y, isSelected, color, r) {
this.x = x;
this.y = y;
this.color = color;
this.r = r;
this.add = function () {
plotPoint(this.x, this.y, this.color, this.r);
};
points.push(this);
if (isSelected) {
selected.push(this);
}
this.distance = function (gx, gy) {
return Math.sqrt(Math.pow(this.x - gx, 2) + Math.pow(this.y - gy, 2));
};
}
function point1(x, y, isSelected, color, r) {
this.x = x;
this.y = y;
this.color = color;
this.r = r;
this.add = function () {
plotPoint(this.x, this.y, this.color, this.r);
};
points.push(this);
if (isSelected) {
selected.push(this);
}
this.distance = function (gx, gy) {
return Math.sqrt(Math.pow(this.x - gx, 2) + Math.pow(this.y - gy, 2));
};
}
function circle(x, y, color, r) {
this.x = x;
this.y = y;
this.color = color;
this.r = r;
this.add = function () {
ctx.beginPath();
ctx.arc(convertX(x), convertY(y), r, 0, 2 * Math.PI);
ctx.stroke();
};
}
function line(m, b, color, width) {
this.m = m;
this.b = b;
this.color = color;
this.width = width;
lines.push(this);
}
function segment(a, b, color, width) {
this.a = a;
this.b = b;
this.color = color;
this.width = width;
this.getSlope = function () {
return (b.y - a.y) / (b.x - a.x);
};
this.getIntercept = function () {
var m = this.getSlope();
return a.y - m * a.x;
};
this.getLength = function () {
return Math.sqrt(this.a.x - this.b.x + this.a.y - this.b.y);
};
this.distance = function (gx, gy) {
//var m = (b.y-a.y)/(b.x-a.x)
//var bb = a.y-m*a.x
var m = this.getSlope();
var bb = this.getIntercept();
var pim = 1 / -m;
var pib = gy - pim * gx;
if (m === 0) {
pix = gx;
piy = this.a.y;
} else if (Math.abs(m) === Infinity) {
var pix = this.a.x;
var piy = gy;
} else {
var pix = (pib - bb) / (m - pim); //((gy-(gx/m)-bb)*m)/(m*m-1)
var piy = pim * pix + pib;
}
//console.log("m:"+m+" pim:"+pim+" pib:"+pib+" pix"+pix+" piy:"+piy)
if (
((this.a.x <= pix && pix <= this.b.x) ||
(this.b.x <= pix && pix <= this.a.x)) &&
((this.a.y <= piy && piy <= this.b.y) ||
(this.b.y <= piy && piy <= this.a.y))
) {
var d = Math.sqrt(Math.pow(gx - pix, 2) + Math.pow(gy - piy, 2));
return d;
} else {
var d = Math.min(this.a.distance(gx, gy), this.b.distance(gx, gy));
return d;
}
};
this.add = function () {
if (selected.indexOf(this) > -1) {
plotLine(this.a.x, this.a.y, this.b.x, this.b.y, color, width, [5, 2]);
} else {
plotLine(this.a.x, this.a.y, this.b.x, this.b.y, color, width);
}
};
segments.push(this);
}
// var a = new point(1,1)
// var b = new point(3,4)
// new segment(a,b)
//var testline = new line(1, 2, 'red', 1)
function drawLine(x1, y1, x2, y2, color, width, dash) {
ctx.save();
ctx.beginPath();
ctx.moveTo(x1, y1);
ctx.lineTo(x2, y2);
ctx.strokeStyle = color;
ctx.lineWidth = width;
if (dash !== undefined) {
ctx.setLineDash(dash);
}
ctx.stroke();
ctx.restore();
}
function convertX(x) {
return ((x - xmin - centerX) / (xmax - xmin)) * width;
}
function revertX(x) {
return (x * (xmax - xmin)) / width + centerX + xmin;
}
function convertY(y) {
return ((ymax - y - centerY) / (ymax - ymin)) * height;
}
function revertY(y) {
return (y * (ymin - ymax)) / height - centerY - ymin;
}
function addAxis() {
var TICK = 0;
for (
var i = Math.floor(xmin + centerX);
i <= Math.floor(xmax + centerX);
i += XSCALE
) {
drawLine(
convertX(i),
convertY(0) + TICK,
convertX(i),
convertY(0) - TICK
);
}
for (
var i = Math.floor(ymin - centerY);
i <= Math.floor(ymax - centerY);
i += YSCALE
) {
drawLine(
convertX(0) - TICK,
convertY(i),
convertX(0) + TICK,
convertY(i)
);
}
}
function addGrid() {
for (
var i = Math.floor(ymin - centerY);
i <= Math.floor(ymax - centerY);
i += YSCALE
) {
drawLine(0, convertY(i), width, convertY(i), "lightgrey", 1);
}
for (
var i = Math.floor(xmin + centerX);
i <= Math.floor(xmax + centerX);
i += XSCALE
) {
drawLine(convertX(i), height, convertX(i), 0, "lightgrey", 1);
}
}
function addPoints() {
for (const p of points) {
p.add();
}
}
function addCircles() {
for (const c of circles) {
c.add();
}
}
function addLines() {
for (const l of lines) {
plotLine(
xmin + centerX,
l.m * (xmin + centerX) + l.b,
xmax + centerX,
l.m * (xmax + centerX) + l.b,
l.color,
l.width
);
}
}
function addSegments() {
for (const s of segments) {
s.add();
}
}
function plotPoint(x, y, color, r) {
if (r === undefined) {
r = 2;
}
if (color === undefined) {
color = "black";
}
ctx.fillStyle = color;
ctx.beginPath();
ctx.arc(convertX(x), convertY(y), r, 0, 2 * Math.PI);
ctx.fill();
}
function plotCircle(x, y, color, r) {
if (r === undefined) {
r = 2;
}
if (color === undefined) {
color = "black";
}
ctx.beginPath();
ctx.arc(convertX(x), convertY(y), r, 0, 2 * Math.PI);
ctx.stroke();
}
function plotLine(x1, y1, x2, y2, color, width, dash) {
ctx.save();
ctx.beginPath();
ctx.moveTo(convertX(x1), convertY(y1));
ctx.lineTo(convertX(x2), convertY(y2));
ctx.strokeStyle = color;
ctx.lineWidth = width;
if (dash !== undefined) {
ctx.setLineDash(dash);
}
ctx.stroke();
ctx.restore();
}
function snap(x) {
if ((x - Math.round(x)) * (x - Math.round(x)) < 0.01) {
return Math.round(x);
} else {
return x;
}
}
function mouseDown(evt) {
x = evt.clientX;
y = evt.clientY;
ocx = centerX;
ocy = centerY;
if (evt.buttons === 2) {
for (const p of points) {
if (
nx * nx - 2 * convertX(p.x) * nx + convertX(p.x) * convertX(p.x) <
36 &&
ny * ny - 2 * convertY(p.y) * ny + convertY(p.y) * convertY(p.y) < 36
) {
s = new segment(p, new point(revertX(x), revertY(y), true));
selected.push(s);
return;
}
}
new point(snap(revertX(x)), snap(revertY(y)));
}
if (evt.buttons === 1) {
for (const p of points) {
if (p.distance(revertX(x), revertY(y)) < 0.2) {
selected.push(p);
}
console.log(p.distance(revertX(x), revertY(y)));
}
for (const s of segments) {
if (s.distance(revertX(x), revertY(y)) < 0.2) {
selected.push(s);
}
console.log(s.distance(revertX(x), revertY(y)));
}
}
onresize();
}
function mouseUp() {
selected = [];
}
function mouseMove(evt) {
console.clear();
nx = evt.clientX;
ny = evt.clientY;
gx = revertX(nx);
gy = revertY(ny);
if (evt.buttons === 1) {
if (selected.length > 0) {
for (const p of selected) {
p.x = snap(gx);
p.y = snap(gy);
}
} else {
centerX = (x - nx) / SQUARE_SIZE + ocx;
centerY = (y - ny) / SQUARE_SIZE + ocy;
}
}
if (evt.buttons === 2) {
for (const p of selected) {
p.x = snap(gx);
p.y = snap(gy);
}
}
console.log("coords: " + gx + ", " + gy);
console.log("points: " + points);
console.log("segments:" + segments);
console.log("selected: " + selected);
onresize();
}
function keyPress(evt) {
if ((evt.keyCode = 32)) {
//space
if (selected.length > 0) selected = [];
}
onresize();
}
point(AX, AY, false, "red", 6);
point(BX, BY, false, "red", 6);
window.onresize = function () {
width = canvas.width = window.innerWidth;
height = canvas.height = window.innerHeight;
xmin = -width / SQUARE_SIZE / 2;
xmax = width / SQUARE_SIZE / 2;
ymin = -height / SQUARE_SIZE / 2;
ymax = height / SQUARE_SIZE / 2;
addGrid();
addAxis();
addPoints();
addLines();
addSegments();
addCircles();
ctx.font = "12px Arial";
ctx.fillStyle = "black";
ctx.fillText("Number of Points: " + points.length, 20, 30);
ctx.fillText("Points Slected: " + selected.length, 20, 50);
};
onresize();
}
<h2>LocusCreator v1.0 - © Niels Langerak</h2>
<p>Use the inputboxes to fill in all the info to make the locus.</p>
<p>Circle A - X:</p>
<input type="number" id="AX" value="0">
<p>Circle A - Y:</p>
<input type="number" id="AY" value="0">
<p>Circle B - X:</p>
<input type="number" id="BX" value="1">
<p>Circle B - Y:</p>
<input type="number" id="BY" value="1">
<button onclick="start()">Reload</button>
<canvas width=600px height=600px id='canvas'>
You should use the new keyword when creating your point:
new point(AX, AY, false, 'red', 6)
new point(BX, BY, false, 'red', 6)
It creates a new instance of point, but not overwrite it, as it works in your code.
N-Body gravity simulation seems to be working fine at first glance, and the same is true for body collisions, but once gravitationally attracted objects start to collide, they start to spiral around each other frantically and the collection of them as a whole have very erratic motion... The code (html-javascript) will be included below, and to reproduce what I'm talking about, you can create a new body by clicking in a random location on the screen.
The math for gravitational attraction is done in the Body.prototype.gravityCalc() method of the Body object type (line 261). The math for the collision resolution is found in the dynamic collision section of the bodyHandle() function (line 337).
//////////////////////////////////////////////////////////////////////////////////////////////////////////
// event handling
document.addEventListener('keydown', keyDown);
document.addEventListener('mousedown', mouseDown)
document.addEventListener('mouseup', mouseUp)
document.addEventListener('mousemove', mouseMove);
document.addEventListener('touchstart', touchStart);
document.addEventListener('touchmove', touchMove);
document.addEventListener('touchend', touchEnd);
window.addEventListener('resize', resize);
window.onload = function() {reset()}
mouseDown = false;
nothingGrabbed = true;
mouseX = 0;
mouseY = 0;
function keyDown(data) {
if(data.key == "r") {
clearInterval(loop);
reset();
}
else if(data.key == 'g') {
gravityOn = !gravityOn;
}
else if(data.key == 'Delete') {
for(i = 0; i < bodies.length ; i++) {
if(((mouseX - bodies[i].x)**2 + (mouseY - bodies[i].y)**2) <= bodies[i].radius**2) {
bodies.splice(i, 1);
}
}
}
else if(data.key == 'c') {
gravity_c *= -1;
}
else if(data.key == 'f') {
falling = !falling;
}
else if(data.key == 'a') {
acceleration *= -1;
}
}
function mouseDown(data) {
mouseDown = true;
nothingGrabbed = true;
mouseX = data.clientX;
mouseY = canvas.height - data.clientY;
}
function mouseUp(data) {
mouseDown = false;
nothingGrabbed = true;
for(i = 0; i < bodies.length; i++) {
bodies[i].grabbed = false
}
}
function mouseMove(data) {
mouseX = data.clientX;
mouseY = canvas.height - data.clientY;
}
function touchStart(data) {
mouseDown = true;
nothingGrabbed = true;
mouseX = data.touches[0].clientX;
mouseY = canvas.height - data.touches[0].clientY;
}
function touchMove(data) {
mouseX = data.touches[0].clientX;
mouseY = canvas.height - data.touches[0].clientY;
}
function touchEnd(data) {
mouseDown = false;
nothingGrabbed = true;
for(i=0;i<bodies.length;i++) {
bodies[i].grabbed = false;
}
}
function resize(data) {
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////
// Initialize Variables
function reset() {
canvas = document.getElementById("canvas");
ctx = canvas.getContext('2d');
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
canvas.color = 'rgb(70, 70, 70)';
scale = Math.min(canvas.width, canvas.height);
fps = 120;
running = true;
loop = setInterval(main, 1000/fps);
gravityOn = true // if true, objects are gravitationally attracted to each other
gravity_c = 334000 // universe's gravitational constant
boundaryCollision = true // if true, objects collide with edges of canvas
wallDampen = 0.7 // number to multiply by when an objects hit a wall
bodyCollision = true // if true, bodies will collide with each other
bodyDampen = 0.4 // number to multiply when two objects collide
falling = false // if true, objects will fall to the bottom of the screen
acceleration = 400
bodies = [] // a list of each Body object
collidingPairs = [] // a list of pairs of colliding bodies
/*
var bounds = 200;
for(i = 0; i<70; i++) { // randomly place bodies
Body.create({
x: Math.floor(Math.random()*canvas.width),
y: Math.floor(Math.random()*canvas.height),
a: Math.random()*Math.PI*2,
xV: Math.floor(Math.random() * (bounds - -bounds)) + -bounds,
yV: Math.floor(Math.random() * (bounds - -bounds)) + -bounds,
mass: Math.ceil(Math.random()*23)
})
} */
/*
Body.create({
x: canvas.width/2 - 50,
xV: 10,
yV: 0,
aV: 3,
y: canvas.height/2 + 0,
mass: 10
});
Body.create({
x: canvas.width/2 + 50,
xV: 0,
aV: 0,
y: canvas.height/2,
mass: 10
});
*/
Body.create({
x: canvas.width/2,
y: canvas.height/2,
mass: 24,
xV: -10.83
});
Body.create({
x: canvas.width/2,
y: canvas.height/2 + 150,
mass: 1,
xV: 260,
color: 'teal'
});
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////
// Body Type Object
function Body(params) {
this.x = params.x || canvas.width/2;
this.y = params.y || canvas.height/2;
this.a = params.a || 0;
this.xV = params.xV || 0;
this.yV = params.yV || 0;
this.aV = params.aV || 0;
this.xA = params.xA || 0;
this.yA = params.yA || 0;
this.aA = params.aA || 0;
this.grabbed = false;
this.edgeBlock = params.edgeBlock || boundaryCollision;
this.gravity = params.gravityOn || gravityOn;
this.mass = params.mass || 6;
this.density = params.density || 0.008;
this.radius = params.radius || (this.mass/(Math.PI*this.density))**0.5;
this.color = params.color || 'crimson';
this.lineWidth = params.lineWidth || 2;
}
Body.create = function(params) {
bodies.push(new Body(params));
}
Body.prototype.move = function() {
this.xV += this.xA/fps;
this.yV += this.yA/fps;
this.aV += this.aA/fps;
this.x += this.xV/fps;
this.y += this.yV/fps;
this.a += this.aV/fps;
if(this.edgeBlock) {
if(this.x + this.radius > canvas.width) {
this.x = canvas.width - this.radius;
this.xV *= -wallDampen
}
else if(this.x - this.radius < 0) {
this.x = this.radius;
this.xV *= -wallDampen;
}
if(this.y + this.radius > canvas.height) {
this.y = canvas.height - this.radius;
this.yV *= -wallDampen;
}
else if(this.y - this.radius < 0) {
this.y = this.radius;
this.yV *= -wallDampen;
}
}
if(this.grabbed) {
this.xA = 0;
this.yA = 0;
this.xV = 0;
this.yV = 0;
this.x = mouseX;
this.y = mouseY;
}
}
Body.prototype.draw = function() {
ctx.beginPath();
ctx.strokeStyle = 'black';
ctx.lineWidth = this.lineWidth;
ctx.fillStyle = this.color;
ctx.arc(this.x, canvas.height - this.y, this.radius, 0, Math.PI*2, true);
ctx.fill();
ctx.stroke();
ctx.closePath()
ctx.beginPath();
ctx.strokeStyle = 'black';
ctx.lineWidth = this.linewidth;
ctx.moveTo(this.x, canvas.height - this.y);
ctx.lineTo(this.x + this.radius*Math.cos(this.a), canvas.height - (this.y + this.radius*Math.sin(this.a)))
ctx.stroke();
ctx.closePath();
}
// calculates gravitational attraction to 'otherObject'
Body.prototype.gravityCalc = function(otherObject) {
var x1 = this.x;
var y1 = this.y;
var x2 = otherObject.x;
var y2 = otherObject.y;
var distSquare = ((x2-x1)**2 + (y2-y1)**2);
var val = (gravity_c*otherObject.mass)/((distSquare)**(3/2));
var xA = val * (x2 - x1);
var yA = val * (y2 - y1);
return [xA, yA]
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////
// Physics Code
function bodyHandle() {
for(i = 0; i < bodies.length; i++) {
if(mouseDown && nothingGrabbed) {
if(Math.abs((mouseX - bodies[i].x)**2 + (mouseY - bodies[i].y)**2) <= bodies[i].radius**2) {
bodies[i].grabbed = true;
nothingGrabbed = false;
}
}
bodies[i].draw()
if(running) {
if(falling) {
bodies[i].yV -= acceleration/fps;
}
bodies[i].move();
}
bodies[i].xA = 0;
bodies[i].yA = 0;
collidingPairs = []
if(gravityOn || bodyCollision) {
for(b = 0; b < bodies.length; b++) {
if(i != b) {
if(bodyCollision) {
var x1 = bodies[i].x;
var y1 = bodies[i].y;
var x2 = bodies[b].x;
var y2 = bodies[b].y;
var rSum = bodies[i].radius + bodies[b].radius;
var dist = { // vector
i: x2 - x1,
j: y2 - y1,
mag: ((x2-x1)**2 + (y2-y1)**2)**0.5,
norm: {
i: (x2-x1)/(((x2-x1)**2 + (y2-y1)**2)**0.5),
j: (y2-y1)/(((x2-x1)**2 + (y2-y1)**2)**0.5)
}
}
if(dist.mag <= rSum) { // static collision
var overlap = rSum - dist.mag;
bodies[i].x -= overlap/2 * dist.norm.i;
bodies[i].y -= overlap/2 * dist.norm.j;
bodies[b].x += overlap/2 * dist.norm.i;
bodies[b].y += overlap/2 * dist.norm.j;
collidingPairs.push([bodies[i], bodies[b]]);
}
}
if(gravityOn) {
if(bodies[i].gravity) {
var accel = bodies[i].gravityCalc(bodies[b]);
bodies[i].xA += accel[0];
bodies[i].yA += accel[1];
}
}
}
}
}
for(c = 0; c < collidingPairs.length; c++) { // dynamic collision
var x1 = collidingPairs[c][0].x;
var y1 = collidingPairs[c][0].y;
var r1 = collidingPairs[c][0].radius;
var x2 = collidingPairs[c][1].x;
var y2 = collidingPairs[c][1].y;
var r2 = collidingPairs[c][1].radius;
var dist = { // vector from b1 to b2
i: x2 - x1,
j: y2 - y1,
mag: ((x2-x1)**2 + (y2-y1)**2)**0.5,
norm: {
i: (x2-x1)/(((x2-x1)**2 + (y2-y1)**2)**0.5),
j: (y2-y1)/(((x2-x1)**2 + (y2-y1)**2)**0.5)
}
}
var m1 = collidingPairs[c][0].mass;
var m2 = collidingPairs[c][1].mass;
var norm = { // vector normal along 'wall' of collision
i: -dist.j/(((dist.i)**2 + (-dist.j)**2)**0.5),
j: dist.i/(((dist.i)**2 + (-dist.j)**2)**0.5)
}
var perp = { // vector normal pointing from b1 to b2
i: dist.norm.i,
j: dist.norm.j
}
var vel1 = { // vector of b1 velocity
i: collidingPairs[c][0].xV,
j: collidingPairs[c][0].yV,
dot: function(vect) {
return collidingPairs[c][0].xV * vect.i + collidingPairs[c][0].yV * vect.j
}
}
var vel2 = { // vector of b2 velocity
i: collidingPairs[c][1].xV,
j: collidingPairs[c][1].yV,
dot: function(vect) {
return collidingPairs[c][1].xV * vect.i + collidingPairs[c][1].yV * vect.j
}
}
// new velocities along perp^ of b1 and b2
var nV1Perp = (vel1.dot(perp))*(m1-m2)/(m1+m2) + (vel2.dot(perp))*(2*m2)/(m1+m2);
var nV2Perp = (vel1.dot(perp))*(2*m1)/(m1+m2) + (vel2.dot(perp))*(m2-m1)/(m1+m2);
/* testing rotation after collision
// velocities of the points of collision on b1 and b2
var pVel1M = vel1.dot(norm) + collidingPairs[c][0].aV*r1;
var pVel2M = vel2.dot(norm) + collidingPairs[c][1].aV*r2;
// moment of inertia for b1 and b2
var I1 = 1/2 * m1 * r1**2;
var I2 = 1/2 * m2 * r2**2;
// new velocities of the points of collisions on b1 and b2
var newpVel1M = ((I1-I2)/(I1+I2))*pVel1M + ((2*I2)/(I1+I2))*pVel2M;
var newpVel2M = ((2*I1)/(I1+I2))*pVel1M + ((I2-I1)/(I1+I2))*pVel2M;
var vectToCol1 = { // vector from x1,y1 to point of collision on b1
i: r1*perp.i,
j: r1*perp.j
};
var vectToCol2 = { // vector from x2,y2 to point of collision on b2
i: r2*-perp.i,
j: r2*-perp.j
};
// sign of cross product of pVelM and vectToCol
var vCrossR1 = (pVel1M*norm.i)*(vectToCol1.j) - (pVel1M*norm.j)*(vectToCol1.i);
vCrossR1 = vCrossR1/Math.abs(vCrossR1);
var vCrossR2 = (pVel2M*norm.i)*(vectToCol2.j) - (pVel2M*norm.j)*(vectToCol2.i);
vCrossR2 = vCrossR2/Math.abs(vCrossR2);
collidingPairs[c][0].aV = vCrossR1 * (newpVel1M)/r1;
collidingPairs[c][1].aV = vCrossR2 * (newpVel2M)/r2;
/* draw collision point velocity vectors [debugging]
ctx.beginPath();
ctx.strokeStyle = 'black';
ctx.moveTo(x1 + vectToCol1.i, canvas.height - (y1 + vectToCol1.j));
ctx.lineTo((x1+vectToCol1.i) + pVel1M*norm.i, (canvas.height- (y1+vectToCol1.j + pVel1M*norm.j)));
ctx.stroke();
ctx.closePath();
ctx.beginPath();
ctx.strokeStyle = 'white';
ctx.moveTo(x2 + vectToCol2.i, canvas.height - (y2 + vectToCol2.j));
ctx.lineTo((x2+vectToCol2.i) + pVel2M*norm.i, (canvas.height- (y2+vectToCol2.j + pVel2M*norm.j)));
ctx.stroke();
ctx.closePath();
console.log(pVel1M, pVel2M);
clearInterval(loop);
*/
collidingPairs[c][0].xV = vel1.dot(norm)*norm.i + nV1Perp*perp.i * bodyDampen;
collidingPairs[c][0].yV = vel1.dot(norm)*norm.j + nV1Perp*perp.j * bodyDampen;
collidingPairs[c][1].xV = vel2.dot(norm)*norm.i + nV2Perp*perp.i * bodyDampen;
collidingPairs[c][1].yV = vel2.dot(norm)*norm.j + nV2Perp*perp.j * bodyDampen;
}
}
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////
// Main Loop
function main() {
// blank out canvas
ctx.fillStyle = canvas.color;
ctx.fillRect(0, 0, canvas.width, canvas.height);
bodyHandle();
if(nothingGrabbed && mouseDown) {
bodies.push(new Body({x: mouseX,
y: mouseY,
mass: 90}));
bodies[bodies.length-1].move();
bodies[bodies.length-1].draw();
}
}
<html>
<meta name='viewport' content='width=device-width,height=device-height'>
<body>
<canvas id="canvas" width='300px' height='300px'></canvas>
<style>
body {
padding: 0;
margin: 0;
}
canvas {
padding: 0;
margin: 0;
}
</style>
</html>
I cannot tell you much about the code. Personally it seems to me that the animations could be correct.
If you want to test your code you could try to test if laws of conservation of energy and momentum are respected. You could, for example, sum the momentum of every object (mass times velocity) and see if the number are maintained constant when there are no forces from the outside (collisions with the wall). To do this I would suggest to make the free space available larger. Another quantity is the total energy (kinetic plus potential) which is a bit harder, but still easy to compute (to compute tot. pot. energy you have to sum over all pairs).
I'm trying to make a canvas with the ability to draw (with mousedown) paths that are filled with a color like so:
I have this code that kinda works but it's crashing usually when I start to draw, probably because of its too much calculation load on the plotLine function.
I'm wondering if I can do this more efficiently to prevent the crash if possible.
// Some setup code
var c = document.querySelector("canvas"),
ctx = c.getContext("2d"),
colors = [
{ r: 198, g: 232, b: 250 },
{ r: 249, g: 213, b: 228 },
{ r: 254, g: 250, b: 214 }
],
cIndex = 0,
maxColors = colors.length,
total = 0,
segment = 500,
isDown = false,
px,
py;
setSize();
c.onmousedown = c.ontouchstart = function(e) {
isDown = true;
var pos = getPos(e);
px = pos.x;
py = pos.y;
};
window.onmousemove = window.ontouchmove = function(e) {
if (isDown) plot(e);
};
window.onmouseup = window.ontouchend = function(e) {
e.preventDefault();
isDown = false;
};
function getPos(e) {
e.preventDefault();
if (e.touches) e = e.touches[0];
var r = c.getBoundingClientRect();
return {
x: e.clientX - r.left,
y: e.clientY - r.top
};
}
function plot(e) {
var pos = getPos(e);
plotLine(ctx, px, py, pos.x, pos.y);
px = pos.x;
py = pos.y;
}
function plotLine(ctx, x1, y1, x2, y2) {
var diffX = Math.abs(x2 - x1),
diffY = Math.abs(y2 - y1),
dist = Math.sqrt(diffX * diffX + diffY * diffY),
step = dist / 50,
i = 0,
t,
b,
x,
y;
while (i <= dist) {
t = Math.min(1, i / dist);
x = x1 + (x2 - x1) * t;
y = y1 + (y2 - y1) * t;
ctx.fillStyle = getColor();
ctx.beginPath();
ctx.arc(x, y, 10, 0, Math.PI * 2);
ctx.fill();
i += step;
}
function getColor() {
var r, g, b, t, c1, c2;
c1 = colors[cIndex];
c2 = colors[(cIndex + 1) % maxColors];
t = Math.min(1, total / segment);
if (++total > segment) {
total = 0;
if (++cIndex >= maxColors) cIndex = 0;
}
r = c1.r + (c2.r - c1.r) * t;
g = c1.g + (c2.g - c1.g) * t;
b = c1.b + (c2.b - c1.b) * t;
return "rgb(" + (r | 0) + "," + (g | 0) + "," + (b | 0) + ")";
}
}
window.onresize = setSize;
function setSize() {
c.width = window.innerWidth;
c.height = window.innerHeight;
}
document.querySelector("button").onclick = function() {
ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
};
html,
body {
background: #777;
margin: 0;
overflow: hidden;
}
canvas {
position: fixed;
left: 0;
top: 0;
background: #333;
}
button {
position: fixed;
left: 10px;
top: 10px;
}
<canvas></canvas>
<button>Clear</button>
In the function plotLine there is a risk that dist and step are 0. In that case the while loop will never end.
So just add this line:
if (!step) return;
Or, else replace <= by < in the loop condition:
while (i < dist) {