HTML5 Canvas & Processor friendly - How can I create this fill / mask? - javascript

Scenario
In this interactive, let's say the user clicks in four places on a canvas. I want my fill to follow the exact order of clicks. Please consider my "demonstration" below
The GREEN line shows the fill area I'm going for (the red is min bounding box and can be safely ignored).
I need to clip/mask the area otherwise I would use a stroke (easy). I have yet to find a processor friendly way to convert a stroke to a fill or I would use that (this has to be usable on mobile devices).
Question
How can I programmatically generate the green bounding path (fill) for these click points? I have all the coordinate (mouseevents) in an array to loop through.
Code
Here's the simplified stroke equivalent of what I'm trying to accomplish (jsfiddle)
var c = document.getElementById("c");
var ctx = c.getContext("2d");
ctx.moveTo(40, 40);
ctx.lineTo(180, 40);
ctx.lineTo(40, 180);
ctx.lineTo(180, 180);
ctx.lineWidth = 40;
ctx.lineCap = 'round';
ctx.lineJoin = 'round';
ctx.stroke();

Been working on this all day. Here's what I came up with:
Most important piece:
You HAVE to know the current orientation / direction of the cursor before you can save a point to the array. This is crucial and if you mess this up you will get a path that collapses on itself.
Here's code for that:
var radians = null;
var x2 = lastMouseEvent.clientX,
x1 = currentMouseEvent.clientX,
y2 = lastMouseEvent.clientY,
y1 = currentMouseEvent.clientY
radians = Math.atan2(y1 - y2, x1 - x2) * -1 // you can remove the -1 if everything is backwards for your app
collect all mouse coords in an array, store the current radians (inside/outside) too
loop through array drawing a line for the INSIDE of the path, factor in orientation of the pointer
reverse array to start drawing outside of path
connect last point from INSIDE track to first point of OUTSIDE track
loop through the array again, drawing a line for the OUTSIDE of the path, factor in orientation of the pointer
connect last point from OUTSIDE track to first point of INSIDE track
If you don't make sure all your points connect, you won't get a complete path and this won't work
Here's how you can factor in orientation, depending on track:
var radians = [orientation radians stored in your array]
var diameter = [diameter you want your path to be]
var inside = {
x: Math.sin(radians) * (diameter / 2),
y: Math.cos(radians) * (diameter / 2)
}
var outside = {
x: Math.sin(radians) * (diameter / -2),
y: Math.cos(radians) * (diameter / -2)
}
And all you do is add x/y values from your respect track to the coordinates you're currently looping through.
Yes, this is a pain in the ass, but the performance is great on all devices. GL HF

Related

Find point coordinates on a plane

Could you help me please find coordinates of point on a plane?
I try to find coordinates of the point.
If working with JavaScript, you would employ Math.sin() and Math.cos()
If you imagine a unit circle (a circle with radius 1),
and a straight line A starting from the circle's center going towards the
edge,
and you know the line A's angle (in radians). Or the angle in degrees to a reference line which points straight right (on your drawing, the reference line would be +90 degrees to the one which shows radius), in which case if line A is in the top half of the circle the angle would be positive, while if in the bottom half of the circle (like your drawing) the angle would be negative.
Of course, if you only have the angle in degrees, you would have to convert it to radians before passing it to sine and cosine functions. It is a simple operation, you can find many examples online:
function degrees_to_radians(degrees)
{
var pi = Math.PI;
return degrees * (pi/180);
}
Then Math.sin(angleInRadians) would tell you the Y location of the spot where line intersects the circle, while Math.cos(angleInRadians) would tell you the X location. Both X and Y would be relative to the circle center.
And, since the result is for the unit circle, you would also have to multiply both X and Y with actual radius (250). And then add circle's center location (543,250) to get the actual world coordinates of the point.
X = (X * 250) + 543 and Y = (Y * 250) + 250
Hope that helped, you can use Google image search to get some sine and cosine drawings if you're not getting clear picture.
Formula:
x = R * cos(a1) = R * sqrt(1 - ((y + ∆y) / R)^2)
Solution:
let delta = 466.5 - 440.5;
let x = 250 * Math.sqrt(1 - Math.pow(466.5 + delta) / 250, 2));

detect collision between two circles and sliding them on each other

I'm trying to detect collision between two circles like this:
var circle1 = {radius: 20, x: 5, y: 5}; //moving
var circle2 = {radius: 12, x: 10, y: 5}; //not moving
var dx = circle1.x - circle2.x;
var dy = circle1.y - circle2.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < circle1.radius + circle2.radius) {
// collision detected
}else{
circle1.x += 1 * Math.cos(circle1.angle);
circle1.y += 1 * Math.sin(circle1.angle);
}
Now when collision is detected I want to slide the circle1 from on the circle2 (circle1 is moving) like this:
--circle1---------------------------------circle2-------------------------
I could do this by updating the angle of circle1 and Moving it toward the new angle when collision is detected.
Now My question is that how can I detect whether to update/increase the angle or update/decrease the angle based on which part of circle2 circle1 is colliding with ?? (circle one comes from all angles)
I would appreciate any help
This will depend a bit on how you are using these circles, and how many will ever exist in a single system, but if you are trying to simulate the effect of two bodies colliding under gravity where one roles around to the edge then falls off (or similar under-thrust scenario), then you should apply a constant acceleration or velocity to the moving object and after you compute it's movement phase, you do a displacement phase where you take the angle to the object you are colliding with and move it back far enough in that direction to reach circle1.radius + circle2.radius.
[edit] To get that redirection after falling though (not sure if you intended this or if it's just your sketch), there is probably going to be another force at play. Most likely it will involve a "stickiness" applied between the bodies. Basically, on a collision, you need to make sure that on the next movement cycle, you apply Normal Movement, then movement towards the other body, then the repulsion to make sure they don't overlap. This way it will stick to the big circle until gravity pulls way at enough of a direct angle to break the connection.
[edit2] If you want to make this smoother and achieve a natural curve as you fall away you can use an acceleration under friction formula. So, instead of this:
circle1.x += 1 * Math.cos(circle1.angle);
circle1.y += 1 * Math.sin(circle1.angle);
You want to create velocity properties for your object that are acted on by acceleration and friction until they balance out to a fixed terminal velocity. Think:
// constants - adjust these to get the speed and smoothness you desire
var accelerationX = 1;
var accelerationY = 0;
var friction = 0.8;
// part of physics loop
circle1.velX += (accelerationX * Math.cos(circle1.angle)) - (friction * circle1.velX);
circle1.velY += (accelerationY * Math.sin(circle1.angle)) - (friction * circle1.velX);
circle1.x += circle1.velX;
circle1.y += circle1.velY;
This way, when things hit they will slow down (or stop), then speed back up when they start moving again. The acceleration as it gets back up to speed will achieve a more natural arc as it falls away.
You could get the tangent of the point of contact between both circles, which would indicate you how much to change your angle compared to the destination point (or any horizontal plane).

canvas pendulum animation - canvas translating

I'm trying to make simple pendulum in HTML5 Canvas but I'm stuck. I want to swing it for 25 degrees to the left and to the right, so I calculated I should translate every frame about -3.5 px in y axis (and 3.5 px when swings to the right). I'm using below code
var rotation = Math.PI/180, //rotate about 1deg
translation = -3.5,
counter = 0; //count rotations
function draw() {
var element = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
ctx.clearRect(0,0,element.width,element.height);
ctx.translate(0, translation);
ctx.rotate(rotation);
//function draws all objects
objects(element,ctx);
if (counter == 25) {
rotation *= -1;
translation *= -1;
counter = -25;
}
counter += 1;
window.requestAnimationFrame(draw);
}
Everything looks good but when pendulum is changing direction then everything is translating in also x axis and after few seconds disappears from screen.. What is wrong in this code? Or maybe I was miss something in my calculations? My code here https://jsfiddle.net/qskxjzv9/2/
Thanks in advance for your answers.
The problem is that when there is rotation involved, then translation, the x and y's will be translated in a different direction than what may seem logic.
To get around this we don't actually have to involve translation more than using it for placing pivot (point of rotation) and then use absolute rotation based on a different way of calculating the pendulum movement.
For example, this will take care of both the translation problem as well as smoothing the pendulum movement:
Change the draw method to draw the pendulum with origin (0,0) - it's just a matter of changing the initial coordinates so they evolve around (0,0)
Translate to pivot point of screen - this is where the rotation will take place.
Rotate using sin() as a factor - this will create a smooth animation and look more like a pendulum and it will restrict the movement to angle as range is [-1,1]
Use counter to move sin() instead - this acts as a frequency-ish factor (you can later convert this into an actual frequency to say, have the pendulum move n number of times per minute etc.). To keep it simple I have just used the existing counter variable and reduced its step value.
The main code then:
var maxRot = 25 / 180 * Math.PI, // max 25° in both directions
counter = 0,
// these are better off outside loop
element = document.getElementById('canvas');
ctx = element.getContext('2d');
function draw() {
// reset transform using absolute transformation. Include x translation:
ctx.setTransform(1,0,0,1,element.width*0.5,0);
// clear screen, compensate for initial translate
ctx.clearRect(-element.width*0.5,0,element.width,element.height);
// rotate using sin() with max angle
ctx.rotate(Math.sin(counter) * maxRot);
// draw at new orientation which now is pivot point
objects(element, ctx);
// move sin() using "frequency"-ish value
counter += 0.05;
window.requestAnimationFrame(draw);
}
Fiddle
Additional
Thanks to #Blindman67 for providing additional improvements:
To control frequency in terms of oscillations you could do some minor changes - first define frequency:
var FREQUENCY = 3;
Define a function that will do the conversion:
function sint(time) {
return Math.sin(FREQUENCY * time * Math.PI * 0.002); // 0.002 allow time in ms
}
If you now change the draw() method to take a time parameter instead of the counter:
function draw(time) {
...
}
Then you can call rotation like this:
ctx.rotate(sint(time) * maxRot);
you need to translate the origin to the point you want to rotate around:
ctx.translate(element.width / 2, 0);
Then, the rotation as you suggest:
ctx.rotate(rotation);
And finally, translate back:
ctx.translate(- element.width / 2, 0);
See this commented fork of your fiddle.

Control object around sphere using quaternions

In my game the user controls an airplane (seen from the top), flying over the earth (a Sphere object). The airplane can rotate (steer) left or right (by pressing the LEFT or RIGHT arrow keys) and it can accelerate by pressing the UP arrow key. So the airplane always has a direction (rotation) and a certain speed (velocity) based on user input, stored in vx & vy variables.
In the render loop the vx & vy variables are used to rotate the globe. So the airplane does not actually move, it is the globe below the airplane that rotates to give the impression of the airplane flying over the earth.
This is all wonderful, until the player "reaches" the other side of the globe with his airplane. Now when the user flies "to the right" of the screen, the earth also rotates to the right, which makes it look that the airplane is flying backwards. The issue comes from trying to fit in some old 2D code of a previous airplane game of mine into this 3D game.
I would like to know how to solve this issue with quaternions. I am certain that I need those, but I just don't understand them fully. I figure that my vx and vy variables could still be useful for this, as they could make some kind of "new location" vector. From what I read is that I should normalize vectors, get an axis and an angle, but I am not sure of what and how to get these. Any help would be greatly appreciated!
Below is the code that rotates the earth when the user flies in a certain x/y direction plus an image to get a better picture of the game situation.
// AIRPLANE VARS
var friction = 0.85;
var vr = 7.5; // Rotate-velocity
var thrust = 0.5;
var max_speed = 20;
var vx = 0; // X-velocity
var vy = 0; // Y-velocity
// RENDER LOOP
function render() {
// check states
if (rotate_left) {
player.rotation.y = player.rotation.y + (vr * (Math.PI / 180));
} else if (rotate_right) {
player.rotation.y = player.rotation.y - (vr * (Math.PI / 180));
}
if(throttle){
//var radians = ((player.rotation.y * Math.PI) / 180);
var radians = player.rotation.y;
var ax = (Math.cos(radians) * thrust);
var ay = (Math.sin(radians) * thrust);
vx = vx + ax;
vy = vy + ay;
} else {
//ship.gotoAndStop(1);
vx = vx * friction;
vy = vy * friction;
}
// rotate the globe in the opposite direction of the airplane movement
globe.rotation.x = globe.rotation.x - (-vx/100);
globe.rotation.y = globe.rotation.y - (vy/100);
}
I am not familiar with your implementation framework, which appears from your tags to be three.js. It is also a bit difficult to see how your 'turn' controls affect the player, because you did not mention how the axes of the plane is defined. I may not be much help but I can give you some starting tips.
Firstly familiarise yourself with the structure of a quaternion and the implementation of it in three.js.
In many texts they appear as q = [w, x, y, z], however it seems in three.js they are defined as q = [x, y, z, w]. Don't worry too much about what those numbers are as they are very counter-intuitive to read.
There are a few ways to rotate the quaternion with respect to your velocity.
I think this is your best shot: rotate the quaternion by using the derivative equation given here, by calculating the angular velocity of the plane around the earth (and thus the earth around the plane). This is given by the 3D particle equation here. You can add the time-scaled derivative (dt*dqdt) to the quaternion q, then renormalise it in order to animate the rotation.
Another way is to pick a quaternion rotation that you want to end at, and use the slerp operation (built in to three.js).
If you give me some more details about how your sphere, plane and global frames are defined, I may be able to help more.

Translating an element with canvas

I'm trying to learn canvas by implementing a pie chart. I've managed to parse my data, draw the slices, and calculate the center of each arc, as noted by the black circles. But now I'm trying to draw one of the slices as though it had been "slid out". Not animate it (yet), just simply draw the slice as though it had been slid out.
I thought the easiest way would be to first calculate the point at which the new corner of the slice should be (free-hand drawn with the red X), translate there, draw my slice, then translate the origin back. I thought I could calculate this easily, since I know the center of the pie chart, and the point of the center of the arc (connected with a free-hand black line on the beige slice). But after asking this question, it seems this will involve solving a system of equations, one of which is second order. That's easy with a pen and paper, dauntingly hard in JavaScript.
Is there a simpler approach? Should I take a step back and realize that doing this is really the same as doing XYZ?
I know I haven't provided any code, but I'm just looking for ideas / pseudocode. (jQuery is tagged in the off chance there's a plugin will somehow help in this endeavor)
Getting the x and y of the translation is easy enough.
// cx and cy are the coordinates of the centre of your pie
// px and py are the coordinates of the black circle on your diagram
// off is the amount (range 0-1) by which to offset the arc
// adjust off as needed.
// rx and ry will be the amount to translate by
var dx = px-cx, dy = py-cy,
angle = Math.atan2(dy,dx),
dist = Math.sqrt(dx*dx+dy*dy);
rx = Math.cos(angle)*off*dist;
ry = Math.sin(angle)*off*dist;
Plug that into the code Simon Sarris gave you and you're done. I'd suggest an off value of 0.25.
Merely translating an element on a canvas is very easy and there shouldn't be any tricky equations here. In the most basic sense it is:
ctx.save();
ctx.translate(x, y);
// Draw the things you want offset by x, y
ctx.restore();
Here's a rudimentary example of a square pie and the same pie with one of the four "slices" translated:
http://jsfiddle.net/XqwY2/
To make the pie piece "slide out" the only thing you need to calculate is how far you want it to be. In my simple example the blue block is slid out 10, -10.
If you are wondering merely how to get the X and Y you want in the first place, well, that's not quite a javascript/canvas question. For points on a line given a distance this question: Finding points on a line with a given distance seems the most clear
Edit, here you are (from comments):
// Center point of pie
var x1 = 100;
var y1 = 100;
// End of pie slice (your black dot)
var x2 = 200;
var y2 = 0;
// The distance you want
var distance = 3;
var vx = x2 - x1; // x vector
var vy = y2 - y1; // y vector
var mag = Math.sqrt(vx*vx + vy*vy); // length
vx = mag/vx;
vy = mag/vy;
// The red X location that you want:
var px = x1 + vx * ( distance);
var py = y1 + vy * ( distance);
This would give you a px,py of (104.24, 95.76) for my made-up inputs.

Categories

Resources