distance measure using mpu 6050 accelerometer - mpu6050

I want to calculate traveled distance of mpu 6050 sensor using the acceleration of x,y,z. How to get the traveled distance that sensor traveled using accelerometer values by integrating.Can you please explain the code example for getting the velocity and displacement
a_present = aaReal.x * 9.8;
a_average = (a_present + a_previous) / 2;
v_present = v_previous + a_average * 0.001 ;
v_average = (v_previous + v_present) / 2;
s_present = v_previous * 0.001 + 0.5 * a_average * 0.001 * 0.001;
Serial.println("v_present " + (String)v_present + "\t\ts_present " +(String)s_present+"\t\ts_all "+(String)s_all + "\ta_real "+(String)a_present);
a_previous = a_average;
v_previous = v_present;
s_all = s_all + s_present;

Related

Given a position and rotation, how can I find a point that extends X distance from it?

I have a point in 3D space, and that point has an orientation. I want to get the end point of a line X distance from the origin point, following in orientation. How would I do this?
The answer doesn't have to be specific to any library, but I am using Three.JS.
In regards to Three.JS, depending on what you're starting with, or your comfort, there are several solutions.
Object3D
It provides you with extra utility methods that lets Three.js figure out the math for you:
// Create an Object3D
const element = new THREE.Object3D();
// Set position and orientation
element.position.copy(startPosition);
element.rotation.copy(eulerRotations);
// Move "forward" by the desired distance
element.translateZ(distance);
// Now we have our final position!
console.log(element.position);
I think the key to your question is the Object3D.translateZ() method, you can read more about it in the docs.
Vector3
Internally, what Object3D just did was Vector3 math. If you're only dealing with points and orientations, it might make more sense to use Vector3 directly:
const finalPosition = new Vector3(0, 0, 1)
.applyQuaternion(quaternionRotations)
.multiplyScalar(distance)
.add(startPosition);
Math
If you only want the math, this is what Three.JS is doing under the hood:
let x = 0;
let y = 0;
let z = 1;
const ix = quaternionW * x + quaternionY * z - quaternionZ * y;
const iy = quaternionW * y + quaternionZ * x - quaternionX * z;
const iz = quaternionW * z + quaternionX * y - quaternionY * x;
const iw = - quaternionX * x - quaternionY * y - quaternionZ * z;
x = ix * quaternionW + iw * - quaternionX + iy * - quaternionZ - iz * - quaternionY;
y = iy * quaternionW + iw * - quaternionY + iz * - quaternionX - ix * - quaternionZ;
z = iz * quaternionW + iw * - quaternionZ + ix * - quaternionY - iy * - quaternionX;
x = x * distance + originalPositionX;
y = y * distance + originalPositionY;
z = z * distance + originalPositionZ;
Which can be simplified to this:
function ray(position, distance, direction) {
const dy2 = 2 * direction.y;
const dx2 = 2 * direction.x;
const x = position.x + distance * (dy2 * direction.w + dx2 * direction.z);
const y = position.y + distance * (dy2 * direction.z - dx2 * direction.w);
const z =
position.z +
distance *
(-1 * Math.pow(direction.y, 2) +
Math.pow(direction.z, 2) +
Math.pow(direction.w, 2) -
Math.pow(direction.x, 2));
return {x, y, z};
}

How does B(t) of a bezier curve move when P1/P2 move along tangent?

When P1 changes from (0,4) to (0,2), Q1(t=0.5) and Q2(t=0.6) move 0.75 and 0.576 respectively. How can I calculate for any B(t) the distance it moves when P1 or P2 move along (Start--P1) or (P2--End) respectively?
Just write Bezier curve expression:
B(t) = P0 * (1-t)^3 + P1 * 3 * t * (1-t)^2 + P2 * 3 * t^2 * (1-t) + P3 * t^3
Let's P1' is new position of P1 control point. Only the second term will be changed, so
DeltaB(t) = B'(t) - B(t) = (P1' - P1) * 3 * t * (1-t)^2
if P1' lies on P0-P1, then
P1' = P0 + (P1 - P0) * u
DeltaB(t) = (P0 + (P1 - P0) * u - P1) * 3 * t * (1-t)^2 =
(P0 - P1) * (1 - u) * 3 * t * (1-t)^2
For your example data
u = 0.5
(P0 - P1) * (1 - u) = (0, -2) // (x,y) components of vector
DeltaB(0.5) = (0, -2 * 3 * 0.5 * 0.25) = (0, -0.75)
DeltaB(0.6) = (0, -2 * 3 * 0.6 * 0.4 * 0.4) = (0, -0.576)

Bézier curve calculation

I'm trying to work a simple Bezier curve but I just can't understand the maths behind it, if someone could explain to me this simple Bezier curve it would be awesome :)
We have
point1
X: 0
Y: 0
point2
X: 100
Y: 100
control point1
X: 0
Y: 100
So basically we have a line that starts from the top left and will get a diagonal line from the top left to the bottom right of a 100 by 100 pixel square, but now I want to try and pull that line to the control point at X position 0 and Y position 100, could someone explain to me the simple maths that happens here, can if possible show the calculations with my values :).
Thank you
P.S. a basic picture
UPDATE
Here is my simple javascript code that uses the equation
canvasHolder = document.getElementById( 'canvas' );
canvas = canvasHolder.getContext('2d');
var deltaT = 0.10;
var point1 = new Point(300,300);
var point2 = new Point(400,400);
var controlPoint1 = new Point(100,200);
//Bezier cruve
for( var i = 0; i < 10; i++ )
{
var t = (i/10) * 0.1;
var xPos = ( 1.0 * - t*t ) * point1.x + 2.0 * ( 1 - t ) * t * controlPoint1.x + t * t * point2.x;
var yPos = ( 1.0 * - t*t ) * point1.y + 2.0 * ( 1 - t ) * t * controlPoint1.y + t * t * point2.y;
console.log( "xPos = " + xPos + ", yPos = " + yPos );
var particle = new Particle( "circle", xPos, yPos, 5, "#FF0000", "#333333");
particles[i] = particle;
}
here is the output for that
xPos = 0, yPos = 0 bezierCurve.js:35
xPos = 1.9900000000000004, yPos = 3.970000000000001 bezierCurve.js:35
xPos = 3.9600000000000004, yPos = 7.880000000000001 bezierCurve.js:35
xPos = 5.909999999999999, yPos = 11.729999999999999 bezierCurve.js:35
xPos = 7.840000000000001, yPos = 15.520000000000001 bezierCurve.js:35
xPos = 9.75, yPos = 19.25 bezierCurve.js:35
xPos = 11.639999999999997, yPos = 22.919999999999998 bezierCurve.js:35
xPos = 13.509999999999996, yPos = 26.529999999999998 bezierCurve.js:35
xPos = 15.360000000000003, yPos = 30.080000000000005 bezierCurve.js:35
xPos = 17.190000000000005, yPos = 33.57000000000001
any reason on why I am getting this result
Edit,
its ok i was doing this
var xPos = ( 1.0 * - t*t ) * point1.x + 2.0 * ( 1 - t ) * t * controlPoint1.x + t * t * point2.x;
when it is this
var xPos = ( 1.0 - t*t ) * point1.x + 2.0 * ( 1 - t ) * t * controlPoint1.x + t * t * point2.x;
Bezier curves are parametric curves. That means that you will have 2 equations: 1 for the x coordinates over the curve, and the other for the y coordinates over the curve.
In this case, you're working with a quadratic bezier. The equation for quadratic beziers is:
x(t) = (1.0 - t^2) * p0.x + 2.0 * (1 - t) * t * p1.x + t^2 * p2.x;
y(t) = (1.0 - t^2) * p0.y + 2.0 * (1 - t) * t * p1.y + t^2 * p2.y;
In this case, p0 is the start point, p1 is the control point, and p2 is the end point.
t varies from 0 to 1 over the curve. So to draw it using a standard line drawing function, you'd do the following:
float numDivisions = 100.0; // <- You need to decide what this value should be. See below
float t;
float deltaT = t / numDivisions;
MoveTo (p0.x, p0.y);
for (t = 0; t <= 1.0; t += deltaT)
{
x = (1.0 - t*t) * p0.x + 2.0 * (1 - t) * t * p1.x + t * t * p2.x;
y = (1.0 - t*t) * p0.y + 2.0 * (1 - t) * t * p1.y + t * t * p2.y;
LineTo (x, y);
}
The numDivisions will control the resolution of the curves. If you only have 3 divisions, you'll get the start point, 1 point in the middle (parametrically speaking), and the end point. If you use 10,000 points, you'll probably draw much more than you need to. A rule of thumb is that the straight lines connecting the points will always be longer than the actual curve, so you never need to use a number of divisions greater than that. (You can use a Euclidean distance between the points.)
You can derive the parametric equations for a bezier curve from the geometrical representation:
(a)--A-----(c)
x \
C
\
\
(b)
Draw a line from a->c->b
Subdivide the line from a->c by a point A, where A = t *(c- a) + a, i.e. t units from a towards c (0<=t<=1). Using the same ratio t, advance from c towards b, the same t units (point C).
(Recursively) subdivide A->C to sections of t and 1-t, placing the point x at the splitting point. That's the true point located at the Bezier curve.
The same procedure can be used to generate cubic Beziers -- adding one more step to the process.
Summing: (a = { ax, ay }), A = { Ax, Ay }) etc.
A = (c-a)*t + a = { cx-ax * t + ax, cy-ay * t + ay }
B = (b-c)*t + c = { bx-cx * t + cx, by-cy * t + cy }
x = (C-A)*t + A = { Cx-Ax * t + Ax, Cy-Ay * y + Ay }
(Notice, that if a = { ax, ay, az } i.e. a 3D point, the same principle holds)
These formulas can now be combined to produce a closed form equation for Cx, Cy, [Cz] as a quadratic function of t, and constants ax,ay,cx,cy,bx,by, as in the first answer by user1118321.

Google Maps API V3 - Polygon SMOOTHED edges

Is it possible to smooth the lines/edges for a polygon? It's currently very sharp and angular and it would be great if those angles actually had curvature to them. Any ideas?
Add additional points into your polygon. The more points that are plotted, the more gradual the curve will be.
Here is a smoothing algorithm based on bSpline that worked for me on Android inspired by https://johan.karlsteen.com/2011/07/30/improving-google-maps-polygons-with-b-splines/
public List<LatLng> bspline(List<LatLng> poly) {
if (poly.get(0).latitude != poly.get(poly.size()-1).latitude || poly.get(0).longitude != poly.get(poly.size()-1).longitude){
poly.add(new LatLng(poly.get(0).latitude,poly.get(0).longitude));
}
else{
poly.remove(poly.size()-1);
}
poly.add(0,new LatLng(poly.get(poly.size()-1).latitude,poly.get(poly.size()-1).longitude));
poly.add(new LatLng(poly.get(1).latitude,poly.get(1).longitude));
Double[] lats = new Double[poly.size()];
Double[] lons = new Double[poly.size()];
for (int i=0;i<poly.size();i++){
lats[i] = poly.get(i).latitude;
lons[i] = poly.get(i).longitude;
}
double ax, ay, bx, by, cx, cy, dx, dy, lat, lon;
float t;
int i;
List<LatLng> points = new ArrayList<>();
// For every point
for (i = 2; i < lats.length - 2; i++) {
for (t = 0; t < 1; t += 0.2) {
ax = (-lats[i - 2] + 3 * lats[i - 1] - 3 * lats[i] + lats[i + 1]) / 6;
ay = (-lons[i - 2] + 3 * lons[i - 1] - 3 * lons[i] + lons[i + 1]) / 6;
bx = (lats[i - 2] - 2 * lats[i - 1] + lats[i]) / 2;
by = (lons[i - 2] - 2 * lons[i - 1] + lons[i]) / 2;
cx = (-lats[i - 2] + lats[i]) / 2;
cy = (-lons[i - 2] + lons[i]) / 2;
dx = (lats[i - 2] + 4 * lats[i - 1] + lats[i]) / 6;
dy = (lons[i - 2] + 4 * lons[i - 1] + lons[i]) / 6;
lat = ax * Math.pow(t + 0.1, 3) + bx * Math.pow(t + 0.1, 2) + cx * (t + 0.1) + dx;
lon = ay * Math.pow(t + 0.1, 3) + by * Math.pow(t + 0.1, 2) + cy * (t + 0.1) + dy;
points.add(new LatLng(lat, lon));
}
}
return points;
}

How do you calculate the axis-aligned bounding box of an ellipse?

If the major axis of the ellipse is vertical or horizontal, it's easy to calculate the bounding box, but what about when the ellipse is rotated?
The only way I can think of so far is to calculate all the points around the perimeter and find the max/min x and y values. It seems like there should be a simpler way.
If there's a function (in the mathematical sense) that describes an ellipse at an arbitrary angle, then I could use its derivative to find points where the slope is zero or undefined, but I can't seem to find one.
Edit: to clarify, I need the axis-aligned bounding box, i.e. it should not be rotated with the ellipse, but stay aligned with the x axis so transforming the bounding box won't work.
You could try using the parametrized equations for an ellipse rotated at an arbitrary angle:
x = h + a*cos(t)*cos(phi) - b*sin(t)*sin(phi) [1]
y = k + b*sin(t)*cos(phi) + a*cos(t)*sin(phi) [2]
...where ellipse has centre (h,k) semimajor axis a and semiminor axis b, and is rotated through angle phi.
You can then differentiate and solve for gradient = 0:
0 = dx/dt = -a*sin(t)*cos(phi) - b*cos(t)*sin(phi)
=>
tan(t) = -b*tan(phi)/a [3]
Which should give you many solutions for t (two of which you are interested in), plug that back into [1] to get your max and min x.
Repeat for [2]:
0 = dy/dt = b*cos(t)*cos(phi) - a*sin(t)*sin(phi)
=>
tan(t) = b*cot(phi)/a [4]
Lets try an example:
Consider an ellipse at (0,0) with a=2, b=1, rotated by PI/4:
[1] =>
x = 2*cos(t)*cos(PI/4) - sin(t)*sin(PI/4)
[3] =>
tan(t) = -tan(PI/4)/2 = -1/2
=>
t = -0.4636 + n*PI
We are interested in t = -0.4636 and t = -3.6052
So we get:
x = 2*cos(-0.4636)*cos(PI/4) - sin(-0.4636)*sin(PI/4) = 1.5811
and
x = 2*cos(-3.6052)*cos(PI/4) - sin(-3.6052)*sin(PI/4) = -1.5811
I found a simple formula at http://www.iquilezles.org/www/articles/ellipses/ellipses.htm (and ignored the z axis).
I implemented it roughly like this:
num ux = ellipse.r1 * cos(ellipse.phi);
num uy = ellipse.r1 * sin(ellipse.phi);
num vx = ellipse.r2 * cos(ellipse.phi+PI/2);
num vy = ellipse.r2 * sin(ellipse.phi+PI/2);
num bbox_halfwidth = sqrt(ux*ux + vx*vx);
num bbox_halfheight = sqrt(uy*uy + vy*vy);
Point bbox_ul_corner = new Point(ellipse.center.x - bbox_halfwidth,
ellipse.center.y - bbox_halfheight);
Point bbox_br_corner = new Point(ellipse.center.x + bbox_halfwidth,
ellipse.center.y + bbox_halfheight);
This is relative simple but a bit hard to explain since you haven't given us the way you represent your ellipse. There are so many ways to do it..
Anyway, the general principle goes like this: You can't calculate the axis aligned boundary box directly. You can however calculate the extrema of the ellipse in x and y as points in 2D space.
For this it's sufficient to take the equation x(t) = ellipse_equation(t) and y(t) = ellipse_equation(t). Get the first order derivate of it and solve it for it's root. Since we're dealing with ellipses that are based on trigonometry that's straight forward. You should end up with an equation that either gets the roots via atan, acos or asin.
Hint: To check your code try it with an unrotated ellipse: You should get roots at 0, Pi/2, Pi and 3*Pi/2.
Do that for each axis (x and y). You will get at most four roots (less if your ellipse is degenerated, e.g. one of the radii is zero). Evalulate the positions at the roots and you get all extreme points of the ellipse.
Now you're almost there. Getting the boundary box of the ellipse is as simple as scanning these four points for xmin, xmax, ymin and ymax.
Btw - if you have problems finding the equation of your ellipse: try to reduce it to the case that you have an axis aligned ellipse with a center, two radii and a rotation angle around the center.
If you do so the equations become:
// the ellipse unrotated:
temp_x(t) = radius.x * cos(t);
temp_y(t) = radius.y * sin(t);
// the ellipse with rotation applied:
x(t) = temp_x(t) * cos(angle) - temp_y(t) * sin(angle) + center.x;
y(t) = temp_x(t) * sin(angle) + temp_y(t) * cos(angle) + center.y;
Brilian Johan Nilsson.
I have transcribed your code to c# - ellipseAngle are now in degrees:
private static RectangleF EllipseBoundingBox(int ellipseCenterX, int ellipseCenterY, int ellipseRadiusX, int ellipseRadiusY, double ellipseAngle)
{
double angle = ellipseAngle * Math.PI / 180;
double a = ellipseRadiusX * Math.Cos(angle);
double b = ellipseRadiusY * Math.Sin(angle);
double c = ellipseRadiusX * Math.Sin(angle);
double d = ellipseRadiusY * Math.Cos(angle);
double width = Math.Sqrt(Math.Pow(a, 2) + Math.Pow(b, 2)) * 2;
double height = Math.Sqrt(Math.Pow(c, 2) + Math.Pow(d, 2)) * 2;
var x= ellipseCenterX - width * 0.5;
var y= ellipseCenterY + height * 0.5;
return new Rectangle((int)x, (int)y, (int)width, (int)height);
}
I think the most useful formula is this one. An ellipsis rotated from an angle phi from the origin has as equation:
where (h,k) is the center, a and b the size of the major and minor axis and t varies from -pi to pi.
From that, you should be able to derive for which t dx/dt or dy/dt goes to 0.
Here is the formula for the case if the ellipse is given by its foci and eccentricity (for the case where it is given by axis lengths, center and angle, see e. g. the answer by user1789690).
Namely, if the foci are (x0, y0) and (x1, y1) and the eccentricity is e, then
bbox_halfwidth = sqrt(k2*dx2 + (k2-1)*dy2)/2
bbox_halfheight = sqrt((k2-1)*dx2 + k2*dy2)/2
where
dx = x1-x0
dy = y1-y0
dx2 = dx*dx
dy2 = dy*dy
k2 = 1.0/(e*e)
I derived the formulas from the answer by user1789690 and Johan Nilsson.
If you work with OpenCV/C++ and use cv::fitEllipse(..) function, you may need bounding rect of ellipse. Here I made a solution using Mike's answer:
// tau = 2 * pi, see tau manifest
const double TAU = 2 * std::acos(-1);
cv::Rect calcEllipseBoundingBox(const cv::RotatedRect &anEllipse)
{
if (std::fmod(std::abs(anEllipse.angle), 90.0) <= 0.01) {
return anEllipse.boundingRect();
}
double phi = anEllipse.angle * TAU / 360;
double major = anEllipse.size.width / 2.0;
double minor = anEllipse.size.height / 2.0;
if (minor > major) {
std::swap(minor, major);
phi += TAU / 4;
}
double cosPhi = std::cos(phi), sinPhi = std::sin(phi);
double tanPhi = sinPhi / cosPhi;
double tx = std::atan(-minor * tanPhi / major);
cv::Vec2d eqx{ major * cosPhi, - minor * sinPhi };
double x1 = eqx.dot({ std::cos(tx), std::sin(tx) });
double x2 = eqx.dot({ std::cos(tx + TAU / 2), std::sin(tx + TAU / 2) });
double ty = std::atan(minor / (major * tanPhi));
cv::Vec2d eqy{ major * sinPhi, minor * cosPhi };
double y1 = eqy.dot({ std::cos(ty), std::sin(ty) });
double y2 = eqy.dot({ std::cos(ty + TAU / 2), std::sin(ty + TAU / 2) });
cv::Rect_<float> bb{
cv::Point2f(std::min(x1, x2), std::min(y1, y2)),
cv::Point2f(std::max(x1, x2), std::max(y1, y2))
};
return bb + anEllipse.center;
}
Here's a typescript function based on the above answers.
export function getRotatedEllipseBounds(
x: number,
y: number,
rx: number,
ry: number,
rotation: number
) {
const c = Math.cos(rotation)
const s = Math.sin(rotation)
const w = Math.hypot(rx * c, ry * s)
const h = Math.hypot(rx * s, ry * c)
return {
minX: x + rx - w,
minY: y + ry - h,
maxX: x + rx + w,
maxY: y + ry + h,
width: w * 2,
height: h * 2,
}
}
This code is based on the code user1789690 contributed above, but implemented in Delphi. I have tested this and as far as I can tell it works perfectly. I spent an entire day searching for an algorithm or some code, tested some that didn't work, and I was very happy to finally find the code above. I hope someone finds this useful. This code will calculate the bounding box of a rotated ellipse. The bounding box is axis aligned and NOT rotated with the ellipse. The radiuses are for the ellipse before it was rotated.
type
TSingleRect = record
X: Single;
Y: Single;
Width: Single;
Height: Single;
end;
function GetBoundingBoxForRotatedEllipse(EllipseCenterX, EllipseCenterY, EllipseRadiusX, EllipseRadiusY, EllipseAngle: Single): TSingleRect;
var
a: Single;
b: Single;
c: Single;
d: Single;
begin
a := EllipseRadiusX * Cos(EllipseAngle);
b := EllipseRadiusY * Sin(EllipseAngle);
c := EllipseRadiusX * Sin(EllipseAngle);
d := EllipseRadiusY * Cos(EllipseAngle);
Result.Width := Hypot(a, b) * 2;
Result.Height := Hypot(c, d) * 2;
Result.X := EllipseCenterX - Result.Width * 0.5;
Result.Y := EllipseCenterY - Result.Height * 0.5;
end;
This is my function for finding tight fit rectangle to ellipse with arbitrary orientation
I have opencv rect and point for implementation:
cg - center of the ellipse
size - major, minor axis of ellipse
angle - orientation of ellipse
cv::Rect ellipse_bounding_box(const cv::Point2f &cg, const cv::Size2f &size, const float angle) {
float a = size.width / 2;
float b = size.height / 2;
cv::Point pts[4];
float phi = angle * (CV_PI / 180);
float tan_angle = tan(phi);
float t = atan((-b*tan_angle) / a);
float x = cg.x + a*cos(t)*cos(phi) - b*sin(t)*sin(phi);
float y = cg.y + b*sin(t)*cos(phi) + a*cos(t)*sin(phi);
pts[0] = cv::Point(cvRound(x), cvRound(y));
t = atan((b*(1 / tan(phi))) / a);
x = cg.x + a*cos(t)*cos(phi) - b*sin(t)*sin(phi);
y = cg.y + b*sin(t)*cos(phi) + a*cos(t)*sin(phi);
pts[1] = cv::Point(cvRound(x), cvRound(y));
phi += CV_PI;
tan_angle = tan(phi);
t = atan((-b*tan_angle) / a);
x = cg.x + a*cos(t)*cos(phi) - b*sin(t)*sin(phi);
y = cg.y + b*sin(t)*cos(phi) + a*cos(t)*sin(phi);
pts[2] = cv::Point(cvRound(x), cvRound(y));
t = atan((b*(1 / tan(phi))) / a);
x = cg.x + a*cos(t)*cos(phi) - b*sin(t)*sin(phi);
y = cg.y + b*sin(t)*cos(phi) + a*cos(t)*sin(phi);
pts[3] = cv::Point(cvRound(x), cvRound(y));
long left = 0xfffffff, top = 0xfffffff, right = 0, bottom = 0;
for (int i = 0; i < 4; i++) {
left = left < pts[i].x ? left : pts[i].x;
top = top < pts[i].y ? top : pts[i].y;
right = right > pts[i].x ? right : pts[i].x;
bottom = bottom > pts[i].y ? bottom : pts[i].y;
}
cv::Rect fit_rect(left, top, (right - left) + 1, (bottom - top) + 1);
return fit_rect;
}
Here's a simple example of bounding box around rotated ellipse in javascript:
https://jsfiddle.net/rkn61mjL/1/
The idea is pretty simple and doesn't require complex calculations and solving gradients:
calculate a simple bounding box of non-rotated ellipse:
let p1 = [centerX - radiusX, centerY - radiusY];
let p2 = [centerX + radiusX, centerY - radiusY];
let p3 = [centerX + radiusX, centerY + radiusY];
let p4 = [centerX - radiusX, centerY + radiusY];
rotate all of the four points around the center of the ellipse:
p1 = [(p1[0]-centerX) * Math.cos(radians) - (p1[1]-centerY) * Math.sin(radians) + centerX,
(p1[0]-centerX) * Math.sin(radians) + (p1[1]-centerY) * Math.cos(radians) + centerY];
p2 = [(p2[0]-centerX) * Math.cos(radians) - (p2[1]-centerY) * Math.sin(radians) + centerX,
(p2[0]-centerX) * Math.sin(radians) + (p2[1]-centerY) * Math.cos(radians) + centerY];
p3 = [(p3[0]-centerX) * Math.cos(radians) - (p3[1]-centerY) * Math.sin(radians) + centerX,
(p3[0]-centerX) * Math.sin(radians) + (p3[1]-centerY) * Math.cos(radians) + centerY];
p4 = [(p4[0]-centerX) * Math.cos(radians) - (p4[1]-centerY) * Math.sin(radians) + centerX,
(p4[0]-centerX) * Math.sin(radians) + (p4[1]-centerY) * Math.cos(radians) + centerY];
Here is another version of Pranay Soni's code, implemented in js codepen I hope someone will find it useful
/**
* #param {Number} rotation
* #param {Number} majorAxis
* #param {Nmber} minorAxis
* #pivot {Point} pivot {x: number, y: number}
* #returns {Object}
*/
export function getElipseBoundingLines(ratation, majorAxis, minorAxis, pivot) {
const {cos, sin, tan, atan, round, min, max, PI} = Math;
let phi = rotation / 180 * PI;
if(phi === 0) phi = 0.00001;
// major axis
let a = majorAxis;
//minor axis
let b = minorAxis;
const getX = (pivot, phi, t) => {
return round(pivot.x + a * cos(t) * cos(phi) - b * sin(t) * sin(phi))
}
const getY = (pivot, phi, t) => {
return round(pivot.y + b * sin(t) * cos(phi) + a * cos(t) * sin(phi))
}
const X = [], Y = [];
let t = atan(-b * tan(phi) / a);
X.push(getX(pivot, phi, t));
Y.push(getY(pivot, phi, t));
t = atan(b * (1 / tan(phi) / a));
X.push(getX(pivot, phi, t));
Y.push(getY(pivot, phi, t));
phi += PI;
t = atan(-b * tan(phi) / a);
X.push(getX(pivot, phi, t));
Y.push(getY(pivot, phi, t));
t = atan(b * (1 / tan(phi)) / a);
X.push(getX(pivot, phi, t));
Y.push(getY(pivot, phi, t));
const left = min(...X);
const right = max(...X);
const top = min(...Y);
const bottom = max(...Y);
return {left, top, right, bottom};
}
The general method is to find the zeroes of the derivative of the parametric form of the ellipse along X and Y axes. The position of those zeroes give the edge points along vertical and horizontal directions (derivative is zero).
// compute point on ellipse from angle around ellipse (theta)
function arc(theta, cx, cy, rx, ry, alpha)
{
// theta is angle in radians around arc
// alpha is angle of rotation of ellipse in radians
var cos = Math.cos(alpha), sin = Math.sin(alpha),
x = rx*Math.cos(theta), y = ry*Math.sin(theta);
return {
x: cx + cos*x - sin*y,
y: cy + sin*x + cos*y
};
}
function bb_ellipse(cx, cy, rx, ry, alpha)
{
var tan = Math.tan(alpha),
p1, p2, p3, p4, theta,
xmin, ymin, xmax, ymax
;
// find min/max from zeroes of directional derivative along x and y
// along x axis
theta = Math.atan2(-ry*tan, rx);
// get point for this theta
p1 = arc(theta, cx, cy, rx, ry, alpha);
// get anti-symmetric point
p2 = arc(theta + Math.PI, cx, cy, rx, ry, alpha);
// along y axis
theta = Math.atan2(ry, rx*tan);
// get point for this theta
p3 = arc(theta, cx, cy, rx, ry, alpha);
// get anti-symmetric point
p4 = arc(theta + Math.PI, cx, cy, rx, ry, alpha);
// compute min/max values
ymin = Math.min(p3.y, p4.y)
xmin = Math.min(p1.x, p2.x);
ymax = Math.max(p3.y, p4.y);
xmax = Math.max(p1.x, p2.x);
// return bounding box vertices
return [
{x: xmin, y: ymin},
{x: xmax, y: ymin},
{x: xmax, y: ymax},
{x: xmin, y: ymax}
];
}
var cx = 120, cy = 120, rx = 100, ry = 40, alpha = -45;
function ellipse(cx, cy, rx, ry, alpha)
{
// create an ellipse
const ellipse = document.createElementNS('http://www.w3.org/2000/svg', 'ellipse');
ellipse.setAttribute('stroke', 'black');
ellipse.setAttribute('fill', 'none');
ellipse.setAttribute('cx', cx);
ellipse.setAttribute('cy', cy);
ellipse.setAttribute('rx', rx);
ellipse.setAttribute('ry', ry);
ellipse.setAttribute('transform', 'rotate('+alpha+' '+cx+' '+cy+')');
document.getElementById('svg').appendChild(ellipse);
// create the bounding box
const bb = bb_ellipse(cx, cy, rx, ry, /*angle in radians*/ alpha*Math.PI/180);
const polygon = document.createElementNS('http://www.w3.org/2000/svg', 'polygon');
polygon.setAttribute('stroke', 'red');
polygon.setAttribute('fill', 'none');
polygon.setAttribute('points', bb.map(p => String(p.x) + ' ' + String(p.y)).join(' '));
document.getElementById('svg').appendChild(polygon);
}
ellipse(cx, cy, rx, ry, alpha);
<svg xmlns="http://www.w3.org/2000/svg" id="svg" style="position:relative;width:240px;height:240px" viewBox="0 0 240 240"></svg>

Resources