Bezier Handles Keep Radius and Symmetry but not length - javascript

I'm Having a Bezier Curve in Javascript built with a few bezier Curves.
I can move handles and they keep the symmetry. I'm doing that by first calculating
the distance between Handle and Point on Beziér. Then I compare the distances
of the two handles, calculate a multiplier and apply it to the not dragged
handle. This works for keeping Symmetry.
But I want to achieve that the length of the not dragged handle stays the same.
http://cl.ly/image/0c1z00131m2y (a little picture explaining what i mean).
The code, i currently use to calculate the movement is this:
dx = Math.abs(drag.x - point.p[(draggedItemIndex)/2].x);
dy = Math.abs(drag.y - point.p[(draggedItemIndex)/2].y);
dx2 = Math.abs(point.cp[draggedItemIndex-1].x - point.p[draggedItemIndex/2].x);
dy2 = Math.abs(point.cp[draggedItemIndex-1].y - point.p[draggedItemIndex/2].y);
dxdx = dx2/dx;
dydy = dy2/dy;
point.cp[draggedItemIndex-1].x -= dragX*dxdx;
point.cp[draggedItemIndex-1].y -= dragY*dydy;
Thank you for your answer.

I'm now doing it with ciruclar calculations.
//Circle Center Point
cx = point.p[(draggedItemIndex)/2].x;
cy = point.p[(draggedItemIndex)/2].y;
//Dragged Point Position (To Circle Origin)
x1 = drag.x - cx;
y1 = drag.y - cy;
//Mirrored Point Position (To Circle Origin)
x2 = point.cp[draggedItemIndex-1].x - cx;
y2 = point.cp[draggedItemIndex-1].y - cy;
//Angle Dragged Point
a1 = Math.atan2(-y1,x1)*(180/Math.PI);
//Mirrored Angle
a2 = (a1-180)*(Math.PI/180)*(-1);
//Mirrored Point Radius
r = Math.sqrt(Math.pow(x2, 2)+Math.pow(y2, 2));
//Apply new Position to Point
point.cp[draggedItemIndex-1].x = cx + r * Math.cos(a2);
point.cp[draggedItemIndex-1].y = cy + r * Math.sin(a2);

Related

Calculating a circle's velocity

I've been working on this problem for a bit, and it doesn't seem too hard, but I'm getting tired and it seems more and more complicated the more I try (but it's probably really easy).
My goal is to have a ball bounce off another ball. Seems easy enough.
Ball 2 is controlled by the user's mouse (so far it's sort of like single player pong, but it's a circle instead of a rectangle) so its velocity doesn't matter.
Ball 1 has a few attributes, including dx (the x distance it moves every frame) and dy (dx, but for the y coordinate)
The problem with what I have so far is that you don't know what values will be positive and what will be negative (so the speed can severely increase or decrease instantly), you might be able to fix this using many else if's, but I'm too confused to think right now.
Here is the important part of this function. Also, I've tried to set it up so that dx + dy is always the same, even when the numbers change, so that it looks more natural.
if (collision(ball, paddle)) {
diffX = paddle.x-ball.x;
diffY = paddle.y-ball.y;
totalVel = ball.dx+ball.dy;
dir = {
x : diffX/(diffX+diffY)*-totalVel,
y : diffY/(diffX+diffY)*-totalVel
};
ball.dx = dir.x;
ball.dy = dir.y;
}
Here is a JSFiddle with the full code
https://jsfiddle.net/a2prr0uw/1/
So firstly let's start by defining what a "bounce" is - the speed is the same, but the direction (on both axis) will be inverted. If we treat dx and dy like a vector, then we can first get the incoming speed of the ball like this:
var ballSpeed = Math.sqrt((ball.dx * ball.dx) + (ball.dy * ball.dy));
The above value will always be positive, regardless of what dx and dy are doing.
Next, we'll need the incoming direction of the ball - that bit is the same as what you've currently got:
diffX = paddle.x-ball.x;
diffY = paddle.y-ball.y;
However if we treat this as a vector too, it essentially has a totally unknown length. So, let's normalise it so it's a direction vector with a length of 1:
var distanceBetweenPaddleAndBall = Math.sqrt((diffX * diffX) + (diffY * diffY));
diffX /= distanceBetweenPaddleAndBall;
diffY /= distanceBetweenPaddleAndBall;
diffX and diffY is now a normalised direction vector - the direction the ball is currently going in - and ballSpeed is the speed we'd like it to go.
So now we'll apply our bounce - flip the direction and retain the speed. That becomes this:
dir = {
x : -diffX * ballSpeed,
y : -diffY * ballSpeed
};
Put it all together and we end up with this:
if (collision(ball, paddle)) {
diffX = paddle.x-ball.x;
diffY = paddle.y-ball.y;
// How fast is the ball coming in?
var ballSpeed = Math.sqrt((ball.dx * ball.dx) + (ball.dy * ball.dy));
// How far is the ball from the paddle?
var distanceBetweenPaddleAndBall = Math.sqrt((diffX * diffX) + (diffY * diffY));
// Normalise diffX and diffY so we have a direction vector:
diffX /= distanceBetweenPaddleAndBall;
diffY /= distanceBetweenPaddleAndBall;
// Apply the bounce and the original ball speed:
dir = {
x : -diffX * ballSpeed,
y : -diffY * ballSpeed
};
ball.dx = dir.x;
ball.dy = dir.y;
}
And here it is as a fork of your fiddle too.
not an answer but some considerations on your bouncing logic:
you have to calculate the balls direction (dy/dx)
the collision has also a direction (angle beween both centers = b.x-p.x / b.y-p.y)
the angle after bouncing has to be calculated based on these two angles: using ther 2nd for mirroring
to calculate the new dx & dy after collision you will need the original velocity Math.abs(Math.sqrt(Math.pow(dx)+Math.pow(dy))) of the ball
based on this velocity and the new direction you can calc the new dx & dy

Draw uniform quadratic curve

I'm trying to make a project where the user can draw arrows in a canvas and i need a curved line for that.
As you know one quadratic curve is represented by something like that:
M 65 100 Q 300, 100, 300, 20
Where the first two numbers(65, 100) represents the starting point coordinates, the last two (300,20) represents the ending point coordinates(arrow end).
I need to calculate the middle two numbers based on the first and second point, to make a nice looking curved line.
The first point will have the coordinates from mousedown and the second point from mouseup.
For now i'm using like this.
function addCurve(Ax, Ay, Bx, By){
canvas.add(new fabric.Path('M '+ Ax +' '+ Ay +' Q 100, 100, '+ Bx +', '+ By +'', { fill: '', stroke: 'red' }));
}
addCurve(100,0,200,0);
So, how to calculate the middle point coordinates to get an uniform curve?
I'm also using fabric.js in this project.
First start with the two end points
x1 = ? // start point
y1 = ?
x2 = ? // end point
y2 = ?
To get the mid point
mx = (x1 + x2) / 2;
my = (y1 + y2) / 2;
You will need the vector from first to second point
vx = x2 - x1;
vy = y2 - y1;
The line at 90deg (clockwise or right) from the start and end points is
px = -vy; // perpendicular
py = vx;
The line is the same length as the distance between the two points. The quadratic curve will extend out half the distance that the control point is from the line. So if we want the curve to be 1/4 out by length then half the p vector and add to mid point
cx = mx + px / 2; // get control point
cy = my + py / 2;
If you want the curve to bend the other way
cx = my - px / 2;
cy = my - py / 2;
Or you can write it with the curve amount as a var
var curveAmount = 0.25; // How far out the curve is compared to the line length
cx = my - px * (curveAmount * 2);
cy = my - py * (curveAmount * 2);
Make curveAmount bigger for more curve, smaller for less. Zero for no curve at all and negative to bend the other way.

Detect mouse is near circle edge

I have a function which gets the mouse position in world space, then checks to see if the mouse is over or near to the circle's line.
The added complication how ever is the circle is transformed at an angle so it's more of an ellipse. I can't see to get the code to detect that the mouse is near the border of circle and am unsure where I am going wrong.
This is my code:
function check(evt){
var x = (evt.offsetX - element.width/2) + camera.x; // world space
var y = (evt.offsetY - element.height/2) + camera.y; // world space
var threshold = 20/scale; //margin to edge of circle
for(var i = 0; i < obj.length;i++){
// var mainAngle is related to the transform
var x1 = Math.pow((x - obj[i].originX), 2) / Math.pow((obj[i].radius + threshold) * 1,2);
var y1 = Math.pow((y - obj[i].originY),2) / Math.pow((obj[i].radius + threshold) * mainAngle,2);
var x0 = Math.pow((x - obj[i].originX),2) / Math.pow((obj[i].radius - threshold) * 1, 2);
var y0 = Math.pow((y - obj[i].originY),2) / Math.pow((obj[i].radius - threshold) * mainAngle, 2);
if(x1 + y1 <= 1 && x0 + y0 >= 1){
output.innerHTML += '<br/>Over';
return false;
}
}
output.innerHTML += '<br/>out';
}
To understand it better, I have a fiddle here: http://jsfiddle.net/nczbmbxm/ you can move the mouse over the circle, it should say "Over" when you are within the threshold of being near the circle's perimeter. Currently it does not seem to work. And I can't work out what the maths needs to be check for this.
There is a typo on line 34 with orignX
var x1 = Math.pow((x - obj[i].orignX), 2) / Math.pow((obj[i].radius + threshold) * 1,2);
should be
var x1 = Math.pow((x - obj[i].originX), 2) / Math.pow((obj[i].radius + threshold) * 1,2);
now you're good to go!
EDIT: In regards to the scaling of the image and further rotation of the circle, I would set up variables for rotation about the x-axis and y-axis, such as
var xAngle;
var yAngle;
then as an ellipse can be written in the form
x^2 / a^2 + y^2 / b^2 = 1
such as in Euclidean Geometry,
then the semi-major and semi-minor axes would be determined by the rotation angles. If radius is the circles actual radius. then
var semiMajor = radius * cos( xAngle );
var semiMinor = radius;
or
var semiMajor = radius;
var semiMinor = radius * cos( yAngle );
you would still need to do some more transformations if you wanted an x and y angle.
so if (xMouseC, yMouseC) are the mouse coordinates relative to the circles centre, all you must do is check if that point satisfies the equation of the ellipse to within a certain tolerance, i.e. plug in
a = semiMajor;
b = semiMinor;
x = xMouseC;
y = yMouseC;
and see if it is sufficiently close to 1.
Hope that helps!

elastic 2d ball collision using angles

Yes theres a few threads on this, but not many using angles and I'm really trying to figure it out this way,
I'm now stuck on setting the new velocity angles for the circles. I have been looking at:
http://www.hoomanr.com/Demos/Elastic2/
as a reference to it, but I'm stuck now.
Can anybody shed some light?
cx/cy/cx2/cy2 = center x/y for balls 1 and 2.
vx/vy/vx2/vy2 = velocities for x/y for balls 1 and 2
function checkCollision() {
var dx = cx2 - cx; //distance between x
var dy = cy2 - cy; // distance between y
var distance = Math.sqrt(dx * dx + dy * dy);
var ang = Math.atan2(cy - cy2, cx - cx2);
// was displaying these in a div to check
var d1 = Math.atan2(vx, vy); //ball 1 direction
var d2 = Math.atan2(vx2, vy2); //ball 2 direction
// this is where I am stuck, and i've worked out this is completely wrong now
// how do i set up the new velocities for
var newvx = vx * Math.cos(d1 - ang);
var newvy = vy * Math.sin(d1 - ang);
var newvx2 = vx2 * Math.cos(d2 - ang);
var newvy2 = vy2 * Math.sin(d2 - ang);
if (distance <= (radius1 + radius2)) {
//Set new velocity angles here at collision..
}
Heres a codepen link:
http://codepen.io/anon/pen/MwbMxX
A few directions :
• As mentioned in the comments, use only radians (no more *180/PI).
• atan2 takes y as first param, x as second param.
var d1 = Math.atan2(vy, vx); //ball 1 direction in angles
var d2 = Math.atan2(vy2, vx2); //ball 2 direction in angles
• to rotate a vector, compute first its norm, then only project it with the new angle :
var v1 = Math.sqrt(vx*vx+vy*vy);
var v2 = Math.sqrt(vx2*vx2+vy2*vy2);
var newvx = v1 * Math.cos(d1 - ang);
var newvy = v1 * Math.sin(d1 - ang);
var newvx2 = v2 * Math.cos(d2 - ang);
var newvy2 = v2 * Math.sin(d2 - ang);
• You are detecting the collision when it already happened, so both circles overlap, but you do NOT solve the collision, meaning the circles might still overlap on next iteration, leading to a new collision and a new direction taken, ... not solved, etc..
-->> You need to ensure both circles are not colliding any more after you solved the collision.
• Last issue, but not a small one, is how you compute the angle. No more time for you sorry, but it would be helpful both for you and us to build one (several) scheme showing how you compute the angles.
Updated (but not working) codepen here :
http://codepen.io/anon/pen/eNgmaY
Good luck.
Edit :
Your code at codepen.io/anon/pen/oXZvoe simplify to this :
var angle = Math.atan2(dy, dx),
spread = minDistance - distance,
ax = spread * Math.cos(angle),
ay = spread * Math.sin(angle);
vx -= ax;
vy -= ay;
vx2 += ax;
vy2 += ay;
You are substracting the gap between both circles from the speed. Since later you add the speed to the position, that will do the spatial separation (=> no more collision).
I think to understand what vx-=ax means, we have to remember newton : v = a*t, where a is the acceleration, so basically doing vx=-ax means applying a force having the direction between both centers as direction, and the amount by which both circle collided (spread) as intensity. That amount is obviously quite random, hence the numerical instability that you see : sometimes a small effect, sometimes a big one.
look here for a constant punch version :
http://codepen.io/anon/pen/WvpjeK
var angle = Math.atan2(dy, dx),
spread = minDistance - distance,
ax = spread * Math.cos(angle),
ay = spread * Math.sin(angle);
// solve collision (separation)
cx -= ax;
cy -= ay;
// give a punch to the speed
var punch = 2;
vx -= punch*Math.cos(angle);
vy -= punch*Math.sin(angle);
vx2 += punch*Math.cos(angle);
vy2 += punch*Math.sin(angle);

Detect if user clicks inside a circle

How can I detect when the user clicks inside the red bubble?
It should not be like a square field. The mouse must be really inside the circle:
Here's the code:
<canvas id="canvas" width="1000" height="500"></canvas>
<script>
var canvas = document.getElementById("canvas")
var ctx = canvas.getContext("2d")
var w = canvas.width
var h = canvas.height
var bubble = {
x: w / 2,
y: h / 2,
r: 30,
}
window.onmousedown = function(e) {
x = e.pageX - canvas.getBoundingClientRect().left
y = e.pageY - canvas.getBoundingClientRect().top
if (MOUSE IS INSIDE BUBBLE) {
alert("HELLO!")
}
}
ctx.beginPath()
ctx.fillStyle = "red"
ctx.arc(bubble.x, bubble.y, bubble.r, 0, Math.PI*2, false)
ctx.fill()
ctx.closePath()
</script>
A circle, is the geometric position of all the points whose distance from a central point is equal to some number "R".
You want to find the points whose distance is less than or equal to that "R", our radius.
The distance equation in 2d euclidean space is d(p1,p2) = root((p1.x-p2.x)^2 + (p1.y-p2.y)^2).
Check if the distance between your p and the center of the circle is less than the radius.
Let's say I have a circle with radius r and center at position (x0,y0) and a point (x1,y1) and I want to check if that point is in the circle or not.
I'd need to check if d((x0,y0),(x1,y1)) < r which translates to:
Math.sqrt((x1-x0)*(x1-x0) + (y1-y0)*(y1-y0)) < r
In JavaScript.
Now you know all these values (x0,y0) being bubble.x and bubble.y and (x1,y1) being x and y.
To test if a point is within a circle, you want to determine if the distance between the given point and the center of the circle is smaller than the radius of the circle.
Instead of using the point-distance formula, which involves the use of a (slow) square root, you can compare the non-square-rooted (or still-squared) distance between the points. If that distance is less than the radius squared, then you're in!
// x,y is the point to test
// cx, cy is circle center, and radius is circle radius
function pointInCircle(x, y, cx, cy, radius) {
var distancesquared = (x - cx) * (x - cx) + (y - cy) * (y - cy);
return distancesquared <= radius * radius;
}
(Not using your code because I want to keep the function general for onlookers who come to this question later)
This is slightly more complicated to comprehend, but its also faster, and if you intend on ever checking point-in-circle in a drawing/animation/object moving loop, then you'll want to do it the fastest way possible.
Related JS perf test:
http://jsperf.com/no-square-root
Just calculate the distance between the mouse pointer and the center of your circle, then decide whether it's inside:
var dx = x - bubble.x,
dy = y - bubble.y,
dist = Math.sqrt(dx * dx + dy * dy);
if (dist < bubble.r) {
alert('hello');
}
Demo
As mentioned in the comments, to eliminate Math.sqrt() you can use:
var distsq = dx * dx + dy * dy,
rsq = bubble.r * bubble.r;
if (distsq < rsq) {
alert('HELLO');
}
An alternative (not always useful meaning it will only work for the last path (re)defined, but I bring it up as an option):
x = e.pageX - canvas.getBoundingClientRect().left
y = e.pageY - canvas.getBoundingClientRect().top
if (ctx.isPointInPath(x, y)) {
alert("HELLO!")
}
Path can btw. be any shape.
For more details:
http://www.w3.org/TR/2dcontext/#dom-context-2d-ispointinpath

Categories

Resources