Bezier curve math - javascript

3 weeks ago I asked a question on how to keep the ratio for the bezier curve when changing the X points. "MBo" helps me, but there was a problem and he recommend me to make a new topic.
The problem is that P0.Y and P2.Y can be different and therefore the curve looks like a "brolly".
Now I have this and when changing P0.X and P2.X I want keep the ratio which work's fine:
https://www.w3schools.com/code/tryit.asp?filename=FXDIZMBCCYNA
When changing P0.Y for example it looks like a "brolly" (the P1.X is not exactly in the middle):
https://www.w3schools.com/code/tryit.asp?filename=FXDJ733KQZM4
OK, I try to explain it more closely.
I have four points (X1, Y1, X2, Y2) and want a bezier curve based on the points so:
P0.X is on X1, P1.X between X1 and X2, and P2.X on X2.
P0.Y is on Y1 and P2.Y on Y2.
When I now have this:
ctx.moveTo(0, 50);
ctx.quadraticCurveTo(100, 25, 200, 50);
And change the position of x1 and x2 I keep the ratio from above:
ctx.moveTo(0, 50);
ctx.quadraticCurveTo(25, 44, 50, 50);
Ok, so far this part work's fine. Now my problem is when I change the Y1 or Y2 it looks like "brolly" also the curve is not round like above because the P1.X is not exactly in the middle.
ctx.moveTo(0, 250);
ctx.quadraticCurveTo(100, 25, 200, 50);
Where it should like this:

I see it like this:
So you got some 3 points (P0(x0,y0),P1(x1,y1),P2(x2,y2)). Now what you want to do is still unclear but I assume you just want to resize the curve along x axis and maintain the shape in y axis too...
So you need to change all the stuff proprtionaly... so maintaining this:
(x1-x0) / (x2-x0) = (x1'-x0') / (x2'-x0')
(y1-y0) / (y2-y0) = (y1'-y0') / (y2'-y0')
(x1-x0) / (x1'-x0') = c
(y1-y0) / (y1'-y0') = c
(x2-x0) / (x2'-x0') = c
(y2-y0) / (y2'-y0') = c
(x2-x1) / (x2'-x1') = c
(y2-y1) / (y2'-y1') = c
where x,y are original points and x',y' are the changed ones
for your example:
P0=( 0,50)
P1=(100,25)
P2=(200,50)
x2'=50
you need to recompute the rest start with scale:
c = (x2-x0) / (x2'-x0') = (200-0)/(50-0) = 200/50 = 4
then just recompute what is missing:
(x1-x0) / c = (x1'-x0') // x0=0, x0'=0
x1 / c = x1'
x1' = 100/4 = 25
(y1'-y0') = (y1-y0) / c // y0' = y0
y1' = (y1-y0) / c + y0
y1' = (25-50) / 4 + 50
y1' = 43.75
(y2'-y0') = (y2-y0) / c // y0' = y0
y1' = (y2-y0) / c + y0
y1' = (50-50) / 4 + 50
y1' = 50
The same goes for changing y ... once you change any y you need to recompute the remaining x,y affected in the same manner...

When Bezier curve undergoes some affine transformation, the same transform is applied to their control points.
In your case transform is rotation and scaling around the first point (P0) of the curve.
Rotation angle is
fi = arctan((P2'.Y - P2.Y) / (P2.X - P0.X))
Scaling coeficient
Cf = Sqrt(1 + (P2'.Y - P2.Y)^2/(P2.X - P0.X)^2)
So new coordintes for control points are
xx = P1.X - P0.X
yy = P1.Y - P0.Y
nx = xx * Cos(fi) - yy * Sin(fi)
ny = xx * Sin(fi) + yy * Cos(fi)
P1'.X = P0.X + nx * Cf
P1'.Y = P0.Y + ny * Cf

Related

Draw ellipse with 5points in canvas

I am not so good in Mathematics. I have a requirement to draw an ellipse using 5 coordinates where user will click on 5 different position in a canvas and getting that clicked coordinate 1 ellipse will drawn. To draw ellipse in canvas I have the method
ctx.ellipse(x, y, radiusX, radiusY, rotation, startAngle, endAngle [, anticlockwise]);
Where I need the center position and 2radius of the ellipse. I only have 5coordinates of the perimeter of the ellipse. I get a matrics formula to calculate the ellipse.
ax2+bxy+cy2+dx+ey+f=0
I am unable to convert that equation to js. I am thankful to you if you please help me out to calculate the mazor and minor radius and center point of ellipse from 5 arbitary points
Having 5 points, you can find general formula of conic section (here ellipse) expanding determinant of this matrix (substitute xi,yi with your point coordinates):
(picture taken here)
Simple example to begin with
Using my answer for inverse problem
//calc implicit ellipse equation
//semiaxes rx, ry; rotated at fi radians; centered at (cx,cy)
//note: implicit form Ax^2+Bxy+Cy^2+Dx+Ey+F=0 (not 2B,2D,2E)
// in Pascal notation Sqr is squared
B := Sin(2 * Fi) * (ry * ry - rx * rx);
A := Sqr(ry * Cos(fi)) + Sqr(rx * Sin(fi));
C := Sqr(rx * Cos(fi)) + Sqr(ry * Sin(fi));
D := -B * cy - 2 * A * cx;
E := -2 * C * cy - B * cx;
F := C * cy * cy + A * cx * cx + B * cx * cy - rx * rx * ry * ry;
we can see that
Fi = 0.5 * atan2(B, A-C)
then
ry^2+rx^2 = A + C
ry^2-rx^2 = B / Sin(2*Fi)
so
ry = Sqrt((A + C + B / Sin(2*Fi))/2)
rx = Sqrt((A + C - B / Sin(2*Fi))/2)
(except for Fi=0 case, where we can extract semiaxes from A and C directly)
and then find cx, cy from D/E equation system
Also look at Wiki formulas for the same problem

Does anyone know what angle it should be?

I've got myself a game, the shooting of the player is working fine but that's because I'm using an on.click event and some maths but now I'm trying to get the enemy to shoot back to my player.
me is just the enemy, so me.x and me.y is the x and the y of the enemy.
p is the player so p.x and p.yis the x and the y of the player.
We are trying to shoot from the me.x and m.y to the p.x and p.y.
As the code stands now it justs shoots randomly every second to the right.
The canvas is 500x500.
me.angle = Math.atan2(p.x, p.y) / Math.PI * 180;
me.fireBullet = function (angle) {
var b = Bullet(me.id, angle); //bullet id, with angle pack
b.x = me.x;
b.y = me.y;
}
setInterval(function () {
me.fireBullet(me.angle); //target angle attack
}
, 1000);
}
tan(angle) = y / x | arctan()
angle = arctan(x / y)
Now we only need to take the x and y of the vector going from the player to the enemy:
angle = Math.atan( (me.x - p.x) / m(e.y - p.y)) || 0;
The fix was to find the difference between x and y took me a couple of tries but its working now.
var differenceX = p.x - me.x; //players x - targets x
var differenceY = p.y - me.y; //players y - targets y
me.angle = Math.atan2(differenceY, differenceX) / Math.PI * 180

Find a tangent between line segment and circle

I have a line segment A (x1, y1), B (x2, y2) and a circle with the center C (x3, y3). Also I have radius of the circle. How do I get tangents?
These tangent lines should always be parallel to line segment.
P.S. Sorry if it is doesn't make sense to you, I am very very bad at math. Ask me any questions, I just don't know what else be needed to solve this task. Thanks.
Segment defines direction vector
D = (dx, dy) = (b.x - ax, b.y - a.y)
dlen = Length(D) = Sqrt(dx * dx + dy * dy)
Radius-vector is perpendicular to tangent and it's length is R. Let circle center is (0,0) (we will make correction after). So
x * x + y * y = R^2
x * dx + y * dy = 0 //scalar product
Solving this system for unknown x,y, we have
x = +- R * dy / dlen
y = -+ R * dx / dlen
And after correction two base points of tangents are
px1 = x3 + R * dy / dlen
py1 = y3 - R * dx / dlen
px2 = x3 - R * dy / dlen
py2 = y3 + R * dx / dlen
Direction vectors for these tangents are D, so you can define second points for tangents as
px1_2 = x3 + R * dy / dlen + dx
py1_2 = y3 - R * dx / dlen + dy
And the first tangent is line through (px1, py1) and (px1_2, py1_2)
Note that solution works for any direction of segment (including horizontal and vertical)
Write the equation of the line AB in the implicit form
a X + b Y + c = 0
where the coefficients are normalized so that a² + b² = 1. https://en.wikipedia.org/wiki/Linear_equation#Two-point_form
The two requested tangents are parallel to AB, with equation
a X + b Y + c' = 0
and it suffices to express that the distance of the lines to the center of the circle is R,
|a Xc + b Yc + c'| = R
giving two solutions in c'.
I assume you are looking for the tangent lines with slope == the slope of the line. I will detail the algorithm and I leave the actual code to you.
So first lineslope = (y2-y1)/(x2-x1).
Next a circle with radius r centered at (x3, y3) can be described by the parametric equations x = r*cos(t) + x3; y = r*sin(t) + y3.
The slope of the circle at a given t is (dy/dt)/(dx/dt) = -cos(t)/sin(t) = -1/tan(t).
Set -1/tan(t) = lineslope = (y2-y1)/(x2-x1). Solving for t yields t = (pi / 2) - arctan(-(y2-y1)/(x2-x1)). (This should produce two values for t on [0, 2pi] which will be the two places the slope of the tangent line equals the slope of your segment).
Plug the value for t you just calculated into the circle equations from (3), and you will have the point (x4, y4) on the circle where the tangent line has the same slope as your line segment.
The equation of the tangent line is then just y - y4 = lineslope*(x-x4)

Creating dashes in a pattern of a circle using Javascript Math

I don't understand a lot about the code below and I would like someone to break it down piece by piece.
$(function() {
var centerX = 200,
centerY = 200,
radius = 100,
width = 15,
angles = []
function draw() {
var ctx = document.getElementById("canvas").getContext("2d");
var angle;
//why 180
for (var i = 0; i < 180; i += 10) {
//is the "angle" increasing on every iteration? can you demostrate this please
angle = 2.1 + (i * Math.PI / 90);
angles.push(angle)
ctx.beginPath();
ctx.moveTo(
centerX + Math.sin(angle) * radius,
centerY + Math.cos(angle) * radius
);
ctx.lineTo(
centerX + Math.sin(angle) * (radius + width),
centerY + Math.cos(angle) * (radius + width)
);
ctx.closePath();
ctx.stroke();
}
}
draw()
console.log(angles)
var str = "";
angles.forEach(function(e, i) {
str += " " + i + " : " + e + "|| Math.sin = " + Math.sin(e) + "|| Math.sin * radius = " + Math.sin(e) * radius
$(".display").html(str)
})
})
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.0.2/jquery.min.js"></script>
<canvas id="canvas" width="400" height="400"></canvas>
<div class="display"></div>
like for this part angle = 2.1 + (i * Math.PI / 90); I see i is incrementing by 10 and the author is multiplying it by Math.PI / 90 which is equal to 0.0348888888888889. I know Math.PI is 180 degrees, but were not doing 180/90. We're increasing the number 2.1 by small amount. I can't put all the pieces together.
and in for(var i = 0; i < 180; i += 10){ why did the author choose 180. I know 180 degrees is a half a circle is that why they chose it?
And I always see in other code people use cos for the x coord and sin for the y coord. It doesn't look like the author uses it like the way i just described. could you elaborate.
Any help would be appreciated!
EDIT: I'm also wondering when we use for(var i = 0; i < 180; i += 10){ we get the dashes in a full circle and when i do i < 90 we get a half a circle but the half circle is not straight like against an x or y axis its on an angle. why is it not on the axis? why is it on an angle?
Let's start with SOH CAH TOA (link).
Given Right angled triangle (one side is 90 deg)..
Triangle has an angle.
Triangle has a side that's Opposite (O) to the angle.
Triangle has side that touches both, the angle and the right angle, called (A) Adjacent Side.
Triangle has a side that is called Hypotenuse (H). This is the side that also touches the Angle but doesn't make a right angle with Opposite side.
Now to find any side you need to know at minimum the angle and 1 another side.
Ex: You know angle, Q, is 40deg and Hypotenuse is 10. So what is Adjacent;
Looking at SOH CAH TOA. I See that I know H, and need to know A. CAH has both. So I choose CAH.
Cos Q = Adj/Hyp
Now if you put this Triangle inside circle. Then Hyp will become the radius, like so:
Cos Q = Adj/radius
Now to draw line going outward from a circle i need to know a starting point and the ending point of a line AND i need to make those points align with circle angles.
To get starting point i can just use circle's boundary.
So to get x,y for a point on circle boundary i solve this equation further..
Cos Q * radius = Adj
Cos Q * radius = x //adj is x
& for y...
SOH
Sin Q = Opp/Hyp
Sin Q = Opp/radius
Sin Q * radius = Opp
Sin Q * radius = y
So
x = Cos Q * radius
y = Sin Q * radius
or in js..
var x = Math.cos(angle) * radius;
var y = Math.sin(angle) * radius;
Now we have points that follow a circle's boundary. But for there to be line like we want, we need two points.
This code simply puts in a bigger radius, which gives bigger circle, which gives 2nd points what we needed.
ctx.lineTo(
centerX + Math.sin(angle) * (radius + width),
centerY + Math.cos(angle) * (radius + width));
Code Formatted to be clear:
var centerX = 200,
centerY = 200,
radius = 100,
width = 15,
angles = [],
ctx = document.getElementById("canvas").getContext("2d");
function draw() {
var angle;
for (var i = 0; i < 180; i += 10) {
angle = 2.1 + (i * Math.PI / 90);
ctx.beginPath();
ctx.moveTo(
centerX + Math.sin(angle) * radius,
centerY + Math.cos(angle) * radius);
ctx.lineTo(
centerX + Math.sin(angle) * (radius + width),
centerY + Math.cos(angle) * (radius + width));
ctx.closePath();
ctx.stroke();
}
}
draw();
The code you cite is a bit awkward.
To navigate around a circle, it would be more clear to increment i from 0 to 360 (as in 360 degrees).
for(var i=0; i<360; i++){
Instead, the coder increments from 0 to 180, but then they compensate for halving the 360 degrees by doubling the formula that converts degrees to radians.
// this is the usual conversion of degrees to radians
var radians = degrees * Math.PI / 180;
// This "compensating" conversion is used by the coder
// This doubling of the conversion compensates for halving "i" to 180
var radians = degrees * Math.PI / 90;
A clearer factoring of the iteration would look like this:
// the conversion factor to turn degrees into radians
var degreesToRadians = Math.PI / 180;
// navigate 360 degrees around a circle
for(var i=0; i<360; i++){
// use the standard degrees-to-radians conversion factor
var angle = i * degreesToRadians;
// This roughly rotates the angle so that it starts
// at the 12 o'clock position on the circle
// This is unnecessary if you're navigating completely
// around the circle anyway (then it doesn't matter where you start)
angle += 2.1;
....
}
And...
The code is intentionally drawing lines that radiate away from the circles circumference. The coder is not attempting to follow the circumference at all.

Create equilateral triangle in the middle of canvas?

I want to draw an equilateral triangle in the middle of canvas. I tried this:
ctx.moveTo(canvas.width/2, canvas.height/2-50);
ctx.lineTo(canvas.width/2-50, canvas.height/2+50);
ctx.lineTo(canvas.width/2+50, canvas.height/2+50);
ctx.fill();
But the triangle looks a bit too tall.
How can I draw an equilateral triangle in the middle of canvas?
Someone told me you have to find the ratio of the height of an equilateral triangle to the side of an equilateral triangle.
h:s
What are the two numbers?
The equation for the three corner points is
x = r*cos(angle) + x_center
y = r*sin(angle) + y_center
where for angle = 0, (1./3)*(2*pi), and (2./3)*(2*pi); and where r is the radius of the circle in which the triangle is inscribed.
You have to do it with the height of the triangle
var h = side * (Math.sqrt(3)/2);
or
var h = side * Math.cos(Math.PI/6);
So the ratio h:s is equal to:
sqrt( 3 ) / 2 : 1 = cos( π / 6 ) : 1 ≈ 0.866025
See : http://jsfiddle.net/rWSKh/2/
A simple version where X and Y are the points you want to top of the triangle to be:
var height = 100 * (Math.sqrt(3)/2);
context.beginPath();
context.moveTo(X, Y);
context.lineTo(X+50, Y+height);
context.lineTo(X-50, Y+height);
context.lineTo(X, Y);
context.fill();
context.closePath();
This makes an equilateral triange with all sides = 100. Replace 100 with how long you want your side lengths to be.
After you find the midpoint of the canvas, if you want that to be your triangle's midpoint as well you can set X = midpoint's X and Y = midpoint's Y - 50 (for a 100 length triangle).
The side lengths will not be equal given those coordinates.
The horizontal line constructed on the bottom has a length of 100, but the other sides are actually the hypotenuse of a 50x100 triangle ( approx. 112).
I can get you started with drawing an equilateral triangle but I don't have the time to get it centered.
jsFiddle
var ax=0;
var ay=0;
var bx=0;
var by=150;
var dx=bx-ax
var dy=by-ay;
var dangle = Math.atan2(dy, dx) - Math.PI / 3;
var sideDist = Math.sqrt(dx * dx + dy * dy);
var cx = Math.cos(dangle) * sideDist + ax;
var cy = Math.sin(dangle) * sideDist + ay;
var canvas = document.getElementById('equ');
var ctx = canvas.getContext('2d');
ctx.beginPath();
ctx.moveTo(ax,ay);
ctx.lineTo(bx,by);
ctx.lineTo(cx,cy);
ctx.fill();
my code for drawing triangle also depending on direction (for lines). code is for Raphael lib.
drawTriangle(x2 - x1, y2 - y1, x2, y2);
function drawTriangle(dx, dy, midX, midY) {
var diff = 0;
var cos = 0.866;
var sin = 0.500;
var length = Math.sqrt(dx * dx + dy * dy) * 0.8;
dx = 8 * (dx / length);
dy = 8 * (dy / length);
var pX1 = (midX + diff) - (dx * cos + dy * -sin);
var pY1 = midY - (dx * sin + dy * cos);
var pX2 = (midX + diff) - (dx * cos + dy * sin);
var pY2 = midY - (dx * -sin + dy * cos);
return [
"M", midX + diff, midY,
"L", pX1, pY1,
"L", pX2, pY2,
"L", midX + diff, midY
].join(",");
}

Categories

Resources