Canvas water/blob physics on a circular path with texture? - javascript

this is my first question after having relied on this site for years!
Anyway, I'd like to accomplish something similar to this effect:
http://www.flashmonkey.co.uk/html5/wave-physics/
But on a circular path, instead of a horizon. Essentially, a floating circle/blob in the center of the screen that would react to mouse interaction. What I'm not looking for is gravity, or for the circle to bounce around the screen - only surface ripples.
If at all possible I'd like to apply a static texture to the shape, is this a possibility? I'm completely new to Canvas!
I've already tried replacing some code from the above example with circular code from the following link, to very limited success:
http://www.html5canvastutorials.com/tutorials/html5-canvas-circles/
If only it were that easy :)
Any ideas?
Thanks in advance!

I tried to figure out how wave simulation works using View Source and JavaScript console. It's working fine but threw some JS errors. Also, it seems physics update is entangled with rendering in the render() method.
Here is what I found about the code:
The mouseMove() method creates disturbances on the wave based on mouse position, creating a peak around the mouse. The target variable is the index of the particle that needs to be updated, it's calculated from mouse pos.
if (particle && mouseY > particle.y) {
var speed = mouseY - storeY;
particles[target - 2].vy = speed / 6;
particles[target - 1].vy = speed / 5;
particles[target].vy = speed / 3;
particles[target + 1].vy = speed / 5;
particles[target + 2].vy = speed / 6;
storeY = mouseY;
}
Then, the particles around target are updated. The problem I found is that it does no bounds checking, i.e. it can potentially particles[-1] when target == 0. If that happens, an exception is thrown, the method call ends, but the code does not stop.
The render() method first updates the particle positions, then renders the wave.
Here is its physics code:
for (var u = particles.length - 1; u >= 0; --u) {
var fExtensionY = 0;
var fForceY = 0;
if (u > 0) {
fExtensionY = particles[u - 1].y - particles[u].y - springs[u - 1].iLengthY;
fForceY += -fK * fExtensionY;
}
if (u < particles.length - 1) {
fExtensionY = particles[u].y - particles[u + 1].y - springs[u].iLengthY;
fForceY += fK * fExtensionY;
}
fExtensionY = particles[u].y - particles[u].origY;
fForceY += fK / 15 * fExtensionY;
particles[u].ay = -fForceY / particles[u].mass;
particles[u].vy += particles[u].ay;
particles[u].ypos += particles[u].vy;
particles[u].vy /= 1.04;
}
Basically, it's Hooke's Law for a chain of particles linked by springs between them. For each particle u, it adds the attraction to the previous and next particles (the if statements check if they are available), to the variable fForceY. I don't fully understand the purpose of the springs array.
In the last four lines, it calculates the acceleration (force / mass), updates the velocity (add acceleration), then position (add velocity), and finally, reduce velocity by 1.04 (friction).
After the physics update, the code renders the wave:
context.clearRect(0, 0, stageWidth, stageHeight);
context.fillStyle = color;
context.beginPath();
for (u = 0; u < particles.length; u++) {
...
}
...
context.closePath();
context.fill();
I'm not explaining that, you need to read a canvas tutorial to understand it.
Here are some ideas to get started, note that I didn't test these code.
To modify the code to draw a circular wave, we need introduce a polar coordinate system, where the particle's x-position is the angle in the circle and y-position the distance from center. We should use theta and r here but it requires a large amount of refactoring. We will talk about transforming later.
mouseMove(): Compute particle index from mouse position on screen to polar coordinates, and make sure the disturbance wrap around:
Define the function (outside mouseMove(), we need this again later)
function wrapAround(i, a) { return (i + a.length) % a.length; }
Then change
particles[target - 2] --> particles[wrapAround(target - 2, particles)]
particles[target - 1] --> particles[wrapAround(target - 1, particles)]
...
The modulo operator does the job but I added particles.length so I don't modulo a negative number.
render(): Make sure the force calculation wrap around, so we need to wrapAround function again. We can strip away the two if statements:
fExtensionY = particles[wrapAround(u - 1, particles)].y - particles[u].y - springs[wrapAround(u - 1, springs)].iLengthY;
fForceY += -fK * fExtensionY;
fExtensionY = particles[u].y - particles[wrapAround(u + 1, particles)].y - springs[warpAround(u, springs)].iLengthY;
fForceY += fK * fExtensionY;
Here is the result so far in jsfiddle: Notice the wave propagate from the other side. http://jsfiddle.net/DM68M/
After that's done, the hardest part is rendering them on a circle. To do that, we need coordinate transform functions that treat particle's (x, y) as (angle in the circle, distance from center), and we also need inverse transforms for mouse interaction in mouseMove().
function particleCoordsToScreenCoords(particleX, particleY) {
return [ radiusFactor * particleY * Math.cos(particleX / angleFactor),
radiusFactor * particleY * Math.sin(particleX / angleFactor) ];
}
function screenCoordsToParticleCoords(screenX, screenY) {
// something involving Math.atan2 and Math.sqrt
}
Where the ...Factor variables needed to be determined separately. The angleFactor is two pi over the highest x-position found among particles array
Then, in the coordinates supplied to the context.lineTo, context.arc, use the particleCoordsToScreenCoords to transform the coordinates.

Related

What is the easiest way to calculate position of balls on collision?

I'm trying to make some simple pool game in java script. I have made it but I do not love way of checking if two balls will collide in next frame. I would like to have more easier way to calculate coordinates of balls when collision occurs. I found lot of answers base on collision kinematics, how to handle velocities and directions after collision, but no calculating a position when collision occurs.
As you can see in sample diagram, gold ball is moving slower than a blue ball, and with distance that each ball will have to move on next frame will not be considered as collision. But, as you can see, they should collide (dashed lines).
In that cause I have divided each movement into sectors and calculating if distance between the points is equal or smaller than ball diameter, which is slowing down process when many balls (like in snooker) have to be calculated in each frame, plus that way is not always 100% accurate and balls can go in inaccurate angles after hit (not a big difference, but important in snooker).
Is there any easier way to calculate those (XAC,YAC) and (XBC,YBC) values with knowing start positions and velocities of each ball without dividing ball paths into sectors and calculating many times to find a proper distance?
It is worth to precalculate collision event only once (this approach works well with reliable number of balls, because we have to treat all ~n^2 pairs of balls).
The first ball position is A0, velocity vector is VA.
The second ball position is B0, velocity vector is VB.
To simplify calculations, we can use Halileo principle - use moving coordinate system connected with the first ball. In that system position and velocity of the first ball are always zero. The second ball position against time is :
B'(t) = (B0 - A0) + (VB - VA) * t = B0' + V'*t
and we just need to find solution of quadratic equation for collision distance=2R:
(B0'.X + V'.X*t)^2 + (B0'.X + V'.Y*t)^2 = 4*R^2
Solving this equation for unknown time t, we might get cases: no solutions (no collision), single solution (only touch event), two solutions - in this case smaller t value corresponds to the physical moment of collision.
Example (sorry, in Python, ** is power operator):
def collision(ax, ay, bx, by, vax, vay, vbx, vby, r):
dx = bx - ax
dy = by - ay
vx = vbx - vax
vy = vby - vay
#(dx + vx*t)**2 + (dy + vy*t)**2 == 4*r*r solve this equation
#coefficients
a = vx**2 + vy**2
b = 2*(vx*dx + vy*dy)
c = dx**2+dy**2 - 4*r**2
dis = b*b - 4*a*c
if dis<0:
return None
else:
t = 0.5*(-b - dis**0.5)/a ##includes case of touch when dis=0
return [(ax + t * vax, ay + t * vay), (bx + t * vbx, by + t * vby)]
print(collision(0,0,100,0,50,50,-50,50,10)) #collision
print(collision(0,0,100,0,50,50,-50,80,10)) #miss
print(collision(0,0,100,0,100,0,99,0,10)) #long lasting chase along OX axis
[(40.0, 40.0), (60.0, 40.0)]
None
[(8000.0, 0.0), (8020.0, 0.0)]
Regarding to MBo's solution, here is a function in java script that will calculate coordinates of balls on collision and time in which collision will happen:
calcCollisionBallCoordinates(ball1_x, ball1_y, ball2_x, ball2_y, ball1_vx, ball1_vy, ball2_vx, ball2_vy, r) {
let dx = ball2_x - ball1_x,
dy = ball2_y - ball1_y,
vx = ball2_vx - ball1_vx,
vy = ball2_vy - ball1_vy,
a = Math.pow(vx, 2) + Math.pow(vy, 2),
b = 2 * (vx * dx + vy * dy),
c = Math.pow(dx, 2) + Math.pow(dy, 2) - 4 * Math.pow(r, 2),
dis = Math.pow(b, 2) - 4 * a * c;
if (dis < 0) {
//no collision
return false;
} else {
let t1 = 0.5 * (-b - Math.sqrt(dis)) / a,
t2 = 0.5 * (-b + Math.sqrt(dis)) / a,
t = Math.min(t1, t2);
if (t < 0) {
//time cannot be smaller than zero
return false;
}
return {
ball1: {x: ball1_x + t * ball1_vx, y: ball1_y + t * ball1_vy},
ball2: {x: ball2_x + t * ball2_vx, y: ball2_y + t * ball2_vy},
time: t
};
}
}

Turning objects so they reset their y-rotation in Three.js

Using three.js, I'm creating a game with cars that move in a specific direction, depending on their y-rotation. An example would be 90 degrees. I use object.translateZ() to move them forward but I've run into a problem.
I'm using physijs to simulate the cars. Cars that run into each other may change their y-rotation (because of the crash) and I want to find a way for the cars to slowly change their rotation back to the original rotation like they are turning to get back on the road. Without this my city is very chaotic.
Here's the code that I'm already using (this is just part of it):
var targetRotation = 90
var rotation = car.mesh.rotation.y * 180 / Math.PI //to convert to degrees
I want to find a way to slowly change the car's rotation so it's the same as the target rotation.
Any help is appreciated! (but some sort of function would be perfect)
I've done stuff like this before in other systems (2D, not Three.js), and essentially all you want to do is gradually increment the angle until you reach something close enough to the target angle. Usually this means the float is less than the turning speed you're incrementing by (so you don't "overshoot" the value).
The amount of the increment depends on how quickly you want them to turn.
You also want to check if it's better to increase the angle or decrease (do you turn clockwise or counterclockwise) depending on if you're closer to 360 or 0. This prevents the car from turning the "long way" around. You can find this out by seeing if the difference is greater/less than 180.
We can use the modulus operator to get the "real" angle between -360/360.
var currentAngle = car.mesh.rotation.y * 180 / Math.PI; //car's angle in degrees
var targetAngle = 90; //preferred angle *this time* ;)
var turningSpeed = 1; //one degree per update (wayyy to high for real stuff, usually)
currentAngle = currentAngle % 360; //get the 0-360 remainder
if ( Math.abs(targetAngle - currentAngle) >= turningSpeed) {
var addto = 0
if (targetAngle - currentAngle < 0) {
addto = 360
}
if ( targetAngle - currentAngle + addto <= 180 ) {
currentAngle += turningSpeed;
}
else {
currentAngle -= turningSpeed;
}
}
else { currentAngle = targetAngle; } //we're close enough to just set it
car.mesh.rotation.y = ( currentAngle * Math.PI ) / 180; // back to radians!

Moving object from A to B smoothly across canvas

I am trying to move an object smoothly from point A to point B using HTML canvas and regular javascript.
Point A is a set of coordinates
Point B is in the case the cursor location.
I made a jsfiddle of what I have so far: https://jsfiddle.net/as9fhmw8/
while(projectile.mouseX > projectile.x && projectile.mouseY < projectile.y)
{
ctx.save();
ctx.beginPath();
ctx.translate(projectile.x, projectile.y);
ctx.arc(0,0,5,0,2*Math.PI);
ctx.fillStyle = "blue";
ctx.fill();
ctx.stroke();
ctx.restore();
if(projectile.mouseX > projectile.x && projectile.mouseY < projectile.y)
{
var stepsize = (projectile.mouseX - projectile.x) / (projectile.y - projectile.mouseY);
projectile.x += (stepsize + 1);
}
if(projectile.mouseY < projectile.y)
{
var stepsize = (projectile.y - projectile.mouseY) / (projectile.mouseX - projectile.x);
projectile.y -= (stepsize + 1);
}
}
Essentially what I can't figure out to do is to make the while loop slower (so that it appears animated in stead of just going through every iteration and showing the result).
I also can't figure out how to prevent the Arc from duplicating so that it creates a line that is permanent, instead of appearing to move from point a to point b.
Smooth animation here is really about determining how far to move your object for each iteration of the loop.
There is a little math involved here, but it's not too bad.
Velocity
Velocity in your case is just the speed at which your particles travel in any given direction over a period of time. If you want your particle to travel 200px over the course of 4 seconds, then the velocity would be 50px / second.
With this information, you can easily determine how many pixels to move (animate) a particle given some arbitrary length of time.
pixels = pixelsPerSecond * seconds
This is great to know how many pixels to move, but doesn't translate into individual X and Y coordinates. That's where vectors come in.
Vectors
A vector in mathematics is a measurement of both direction and magnitude. For our purposes, it's like combining our velocity with an angle (47°).
One of the great properties of vectors is it can be broken down into it's individual X and Y components (for 2-Dimensional space).
So if we wanted to move our particle at 50px / second at a 47° angle, we could calculate a vector for that like so:
function Vector(magnitude, angle){
var angleRadians = (angle * Math.PI) / 180;
this.magnitudeX = magnitude * Math.cos(angleRadians);
this.magnitudeY = magnitude * Math.sin(angleRadians);
}
var moveVector = new Vector(50, 47);
The wonderful thing about this is that these values can simply be added to any set of X and Y coordinates to move them based on your velocity calculation.
Mouse Move Vector
Modeling your objects in this way has the added benefit of making things nice and mathematically consistent. The distance between your particle and the mouse is just another vector.
We can back calculate both the distance and angle using a little bit more math. Remember that guy Pythagoras? Turns out he was pretty smart.
function distanceAndAngleBetweenTwoPoints(x1, y1, x2, y2){
var x = x2 - x1,
y = y2 - y1;
return {
// x^2 + y^2 = r^2
distance: Math.sqrt(x * x + y * y),
// convert from radians to degrees
angle: Math.atan2(y, x) * 180 / Math.PI
}
}
var mouseCoords = getMouseCoords();
var data = distanceAndAngleBetweenTwoPoints(particle.x, particle.y, mouse.x, mouse.y);
//Spread movement out over three seconds
var velocity = data.distance / 3;
var toMouseVector = new Vector(velocity, data.angle);
Smoothly Animating
Animating your stuff around the screen in a way that isn't jerky means doing the following:
Run your animation loop as fast as possible
Determine how much time has passed since last time
Move each item based on elapsed time.
Re-paint the screen
For the animation loop, I would use the requestAnimationFrame API instead of setInterval as it will have better overall performance.
Clearing The Screen
Also when you re-paint the screen, just draw a big rectangle over the entire thing in whatever background color you want before re-drawing your items.
ctx.globalCompositeOperation = "source-over";
ctx.fillStyle = "black";
ctx.fillRect(0, 0, canvas.width, canvas.height);
Putting It All Together
Here is a Fiddle demonstrating all these techniques: https://jsfiddle.net/jwcarroll/2r69j1ok/3/

How can I decelerate my inertial scroll algorithm when it nears the top or bottom?

I have written a small inertial scrolling algorithm for mousewheels in javascript.
It works perfectly for my needs however there is one part missing and I cannot seem to get the desired behaviour.
As the user scrolls to the end of the container, be it the top or the bottom. I would like to momentum to naturally decelerate to a stop. Currently it just halts instantly when it hits either edge, whatever speed it is currently going.
Rather than post a load of code here I have created a small jsfiddle to illustrate:
https://jsfiddle.net/o8xLw68L/8/
This is a simplified version of my current code. If you uncomment line 111 You can kind of see the behaviour I am looking for if you scroll down a little from the top of the div and then flick the mousewheel up reasonably quickly. You will see the momentum slow naturally ot the 0 position.
Inertial.prototype.smoothWheel = function(amt) {
this.targetY += amt;
//uncomment this line to see the decelleration almost work against the top edge of the container
//this.targetY = Math.max(0, this.targetY);
this.vy += (this.targetY - this.oldY) * this.stepAmt;
this.oldY = this.targetY;
}
The problem with this approach is that it only dampens the resulting this.vy property when the mousewheel pulses and therefore does not always work correctly since the user may be scrolling from lower down in the container, and at a faster speed, but without any continuing mousewheel pulses. (This is hard to articulate, the jsFiddle should make it clearer)
The solution will likely need to somehow dampen the this.vy property as we get sclose to the top or the bottom of the container, so that it decellerates at a faster pace than the natural this.friction property allows.
I am happy for the dampening area to be either hardcoded to be when you reach 300px of the top/bottom of the content. Or it alternatively, a percentage of the container height would work also.
Any help would be greatly appreciated.
It is possible to dampen the velocity to the value that is enough just to touch the top or bottom edge only by inertial motion (with friction) independently of mouse wheel speed.
Suppose that in current direction the distance to travel x is given. So, it is needed to find the velocity v0 which is enough to travel that distance with inertial motion with friction.
Current scrolling dynamics (vy *= friction) corresponds to laminar fluid flow. It can be written as differential equation:
dv = - (1 - friction) * v * dt; // velocity change dv in time interval dt
dv / v = - (1 - friction) * dt;
// integrating lhs and rhs with initial conditions (v = v0 at t = 0)
ln(v/v0) = - (1 - friction) * t;
v = v0 * exp(- (1 - friction) * t);
So, the velocity decays exponentially from v0 to zero with time.
Traveled distance:
dx = v * dt = v0 * exp(- (1 - friction) * t) * dt;
// integrating, initial conditions x = 0 at t = 0
x = v0 / (1 - friction) * (1 - exp(- (1 - friction) * t))
Thus it is possible to travel the following distance with starting velocity v0 in infinite time:
x = v0 / (1 - friction);
Basing on the distance that is left to the edge it is possible to bound the velocity:
Inertial.prototype.boundVelocity = function () {
var dist = 0;
if (this.dir == 1)
dist = this.scrollerPos;
else if (this.dir == -1)
dist = this.contentHeight - this.scrollerHeight - this.scrollerPos;
var maxv = dist * (1 - this.friction) + 1;
if (Math.abs(this.vy) > maxv) {
console.log('reduce velocity ' + this.vy + ' to ' + (-maxv * this.dir) + ', dist: ' + dist);
this.vy = -maxv * this.dir;
}
}
There is some small non zero minimal value for maxv (+1) to make sure the edges are always hit despite of discretization errors. That function is called when the velocity can be increased in smoothWheel() and also it is called in render() just to avoid numerical error accumulation.
The runnable example: https://jsfiddle.net/c675bkn9/

Rotating around a point gets closer and closer to it

I'm creating a web-application that's going to display 3D objects in a canvas. Now I came across this problem:
I am slowly rotating the camera around the scene so the 3D object can be looked at from all sides. For this I use this code (JavaScript):
var step = 0.1*Math.PI/180;
scene.camera.position.x = Math.cos(step) * (scene.camera.position.x - 0) - Math.sin(step) * (scene.camera.position.z - 0) + 0;
scene.camera.position.z = Math.sin(step) * (scene.camera.position.x - 0) + Math.cos(step) * (scene.camera.position.z - 0) + 0;
Those zeroes are the center of the scene, I leave them there in case we decide to use another base-origin.
This code will make the camera rotate around point 0,0, but it slowly gets closer and closer to it. Here are some screenshots to show you what it does:
There are no other parameters that have impact on the camera's position. I don't understand why it's doing this and what the problem could be.
I found what was causing this issue: I change the camera's X position, then I change the camera's Z position with the new value of it's X position. Because this will be different the origin no longer is relatively at the same position for both calculations.
This was easy to fix, just by storing them into two new variables and then assigning them
var posx = Math.cos(step) * (scene.camera.position.x - 0) - Math.sin(step) * (scene.camera.position.z - 0) + 0;
var posz = Math.sin(step) * (scene.camera.position.x - 0) + Math.cos(step) * (scene.camera.position.z - 0) + 0;
scene.camera.position.x = posx;
scene.camera.position.z = posz;

Categories