Draw ellipse with 5points in canvas - javascript

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

Related

How to rotate a squashed circle (ellipse) in degrees

I draw a circle path with a point (that's circle_x and circle_Y) in my code like this:
var circSize = 15;
var circArc = 2 * Math.PI;
var aspectAdd = 10;
var circle_x = (circSize + aspectAdd) * Math.cos(circArc);
var circle_y = (circSize * Math.sin(circArc);
The circle is then squashed with aspectAdd to make it an ellipse.
The ellipse is pure an invisible path that is used as an animation path for another point.
How do I rotate the ellipse in degrees?
It has to be in pure mathematics. I don't use the canvas ctx or svg path script for this instance (where I could easily give a rotation value).
You probably should've asked this in a math stackoverflow.
You need to apply a rotation to your parameteric functions.
Specifically,
r_x = x * cos(theta) - y * sin(theta)
r_y = x * sin(theta) + y * cos(theta)
I'll let you handle the substitions.
Equation to find points of ellipse with semi-axes rx and ry, rotated by angle fi.
Calculate points for t parameter in range 0..2*Pi with needed step.
x = rx * Cos(t) * Cos(fi) - ry * Sin(t) * Sin(fi) + cx
y = rx * Cos(t) * Sin(fi) + ry * Sin(t) * Cos(fi) + cy
Example for for rx=100, ry=60, fi=-Pi/6:
Addition:
seems you calculate ellipse points in fluid.js file here:
var r_x = circle_x * Math.cos(circRot) - circle_y * Math.sin(circRot);
var r_y = circle_x * Math.sin(circRot) + circle_y * Math.cos(circRot);
So you need to modify these lines to test them:
var ellipseRot = Math.PI / 4
var r_x = circle_x * Math.cos(circRot) * Math.cos(ellipseRot)- circle_y * Math.sin(circRot) * Math.sin(ellipseRot);
var r_y = circle_x * Math.sin(circRot) *Math.sin(ellipseRot) + circle_y * Math.cos(circRot) * Math.cos(ellipseRot);
then change ellipseRot with slider

Bezier curve math

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

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)

Calculate the control points for cubic Bézier curve from an arc

I have an geometric arc defined by the two end points (P0 = x1,y1 and P4 = x2,y2) and either the radius R or the center of the arc (C = xc, yc). I need to calculate the two control points P2, P3 for the cubic Bézier curve in javascript. In my particular case the arc angle will be less than 90 degrees.
I have search the internet and stackoverflow and any solutions are incomplete, not generalized for an arc of indeterminate angle, or too complex for me to understand.
Does anyone have any javascript code or pseudo code that would help? I previously asked a similar question but improperly referred to the Bézier curve as quadratic when I need a cubic Bézier curve.
Cubic Bezier approximation of the circle arc, defined by coordinates of starting and ending points, center and radius of the circle – (x1,y1) = P0, (x2,y2) = P3, C = (cx,cy), R.
(Approximation of arс, defined by angles, could be found here)
Control points of Bezier should be on the tangents to given points on the circle. One possible approximation method – middle point of (symmetric) curve should lay on the circle.
(Note that for good approximation arc angle cannot be large)
Radius-vectors of given points:
V1 = (x1-cx, y1 - cy)
V2 = (x2-cx, y2 - cy)
Tangent vectors:
T1 = (cy – y1, x1 – cx)
T2 = (y2 - cy, cx – x2)
Coordinates of control points (k – unknown yet factor):
P1 = P0 + k * T1
P2 = P3 + k * T2
Middle point of Bezier:
MB = B(1/2) = P0 * 1/8 + P1 * 3/8 + P2 * 3/8 + P3 * 1/8 =
P0 * 1/8 + P0 * 3/8 + k * T1 * 3/8 + P3 * 3/8 + k * T2 * 3/8 + P3 * 1/8 =
(P0 + P3)/2 + k * 3/8 * (T1 +T2)
Now solve equation against k factor
(MB.X – cx)^2 + (MB.Y – cy)^2 = R^2
There are two solutions possible – we need positive one, if input points are in the right order (in common case for arcs < Pi - with the smallest magnitude)
Delphi code (doesn't check input data, doesn't treat extra cases) and it's result:
procedure BezierArcByPoints(x1, y1, x2, y2, cx, cy, R: Integer;
var Pts: array of TPoint);
var
t1x, t1y, t2x, t2y, dx, dy, k, tx, ty, D, a, b, c: Double;
begin
t1x := cy - y1;
t1y := x1 - cx;
t2x := y2 - cy;
t2y := cx - x2;
dx := (x1 + x2) / 2 - cx;
dy := (y1 + y2) / 2 - cy;
tx := 3 / 8 * (t1x + t2x);
ty := 3 / 8 * (t1y + t2y);
a := tx * tx + ty * ty;
b := dx * tx + dy * ty;
c := dx * dx + dy * dy - R * R;
D := b * b - a * c;
if D > 0 then begin
k := (Sqrt(D) - b) / a;
Pts[0] := Point(x1, y1);
Pts[3] := Point(x2, y2);
Pts[1] := Point(x1 + Round(k * t1x), y1 + Round(k * t1y));
Pts[2] := Point(x2 + Round(k * t2x), y2 + Round(k * t2y));
end;
end;
var
Pts: array [0 .. 3] of TPoint;
an1, an2: Double;
begin
an1 := 0;
an2 := Pi / 2;
Canvas.Pen.Color := clBlue;
Canvas.Pen.Width := 1;
Canvas.Ellipse(100, 100, 301, 301);
BezierArcByPoints(200 + Round(100 * Cos(an1)),
200 + Round(100 * Sin(an1)),
200 + Round(100 * Cos(an2)),
200 + Round(100 * Sin(an2)),
200, 200, 100, Pts);
Canvas.Pen.Color := clRed;
Canvas.Pen.Width := 3;
Canvas.PolyBezier(Pts);
end;
VBA Implementation of #Mbo's code:
I have no idea how this works without PI, or any Tan, but here it is:
I use it to draw curves, or arc of a circle using VBA in Excel. I was not able to figure out how to place the control points, but the code above just did it! I'm not gona pretend I understand any of it, but I figured the VBA implementation might be valuable, expecially since the documentation on the topic of the .addCurve method is extremely scarce.
Note that I've noticed the arc will progressively difform when using large rotations, but it's the only piece of code I was able to implement successfully.
Private Type Coordinates
x As long
y As long
End Type
Sub BezierArcByPoints(x1 As Long, _
y1 As Long, _
x2 As Long, _
y2 As Long, _
cx As Long, _
cy As Long, _
R As Double)
Dim t1x As Double
Dim t1y As Double
Dim t2x As Double
Dim t2y As Double
Dim dx As Double
Dim dy As Double
Dim k As Double
Dim tx As Double
Dim ty As Double
Dim D As Double
Dim a As Double
Dim b As Double
Dim c As Double
t1x = cy - y1
t1y = x1 - cx
t2x = y2 - cy
t2y = cx - x2
dx = (x1 + x2) / 2 - cx
dy = (y1 + y2) / 2 - cy
tx = 3 / 8 * (t1x + t2x)
ty = 3 / 8 * (t1y + t2y)
a = tx * tx + ty * ty
b = dx * tx + dy * ty
c = dx * dx + dy * dy - R * R
D = b * b - a * c
Dim c1 As Coordinates
Dim c2 As Coordinates
If D > 0 Then
k = (D ^ 0.5 - b) / a
c1.x = x1 + Round(k * t1x)
c1.y = y1 + Round(k * t1y)
c2.x = x2 + Round(k * t2x)
c2.y = y2 + Round(k * t2y)
Dim Pts(1 To 4, 1 To 2) As Single
Pts(1, 1) = x1
Pts(1, 2) = y1
Pts(2, 1) = c1.x ' Bezier control point 1
Pts(2, 2) = c1.y
Pts(3, 1) = c2.x ' Bezier control point 2
Pts(3, 2) = c2.y
Pts(4, 1) = x2
Pts(4, 2) = y2
End If
ActiveSheet.Shapes.AddCurve(SafeArrayOfPoints:=Pts).Select
Selection.ShapeRange.Fill.Visible = msoFalse
Selection.name = "objArc"
With Selection.ShapeRange.Line
.Visible = msoTrue
.DashStyle = msoLineSysDash
.ForeColor.RGB = RGB(255, 0, 0)
.Transparency = 0
End With
' If you have little circles to display where the control points are located
Dim objAnchor As Shape
Dim objAnchor2 As Shape
Set objAnchor = ActiveSheet.Shapes("objAnchor")
Set objAnchor2 = ActiveSheet.Shapes("objAnchor2")
Call setObjCoord(objAnchor, c1, True)
Call setObjCoord(objAnchor2, c2, True)
End Sub
Note that this function is only able to draw clockwise. If you have a negative rotation, make sure to reverse x1, y1 and x2, y2.
Example:
If Rotation < 0 Then
Call BezierArcByPoints(pointA.x, pointA.y, pointB.x, pointB.y, Axis.x, Axis.y, Radius)
Else
Call BezierArcByPoints(pointB.x, pointB.y, pointA.x, pointA.y, Axis.x, Axis.y, Radius)
End If

How can rotate an elliptical animation path by a certain amount of degrees?

I'm using the following to get the x and y position of an entity as it travels along an elliptical path over time:
x = Math.cos(time)*width/2
y = Math.sin(time)*height/2
Is there a simple way to rotate the entire thing by a certain amount of degrees, say 45, or 132 for example?
You may use a simple rotation transformation:
x1 = x*cos(a) - y*sin(a)
y1 = x*sin(a) + y*cos(a)
Where a - is the angle to rotate.
This Wikipedia article explains that in detail
For each point(x, y) you calculated with above equation, you can rotate it theta degree(counter-clockwise) by the following equation
x' = x * cos(theta) - y * sin(theta);
y' = x * sin(theta) + y * cos(theta);
where x and y are the original coordinates before rotation, x' and y' are the coordinates after rotation, theta is the angle to rotate.
coordinate rotation
Yes, just do a 2D rotation on the resulting x and y to rotate your ellipse:
xrot = x * cos(A) - y * sin(A)
yrot = x * sin(A) + y * cos(A)
And remember that Radians = Degrees * PI / 180.

Categories

Resources