How to calculate center of an ellipse by two points and radius sizes
Asked Answered
B

6

23

While working on SVG implementation for Internet Explorer to be based on its own VML format I came to a problem of translation of an SVG elliptical arc to an VML elliptical arc.

In VML an arc is given by: two angles for two points on ellipse and lengths of radiuses, In SVG an arc is given by: two pairs of coordinates for two points on ellipse and sizes of ellipse boundary box

So, the question is: How to express angles of two points on ellipse to two pairs of their coordinates. An intermediate question could be: How to find the center of an ellipse by coordinates of a pair of points on its curve.

Update: Let's have a precondition saying that an ellipse is normally placed (its radiuses are parallel to linear coordinate system axis), thus no rotation is applied.

Update: This question is not related to svg:ellipse element, rather to "a" elliptical arc command in svg:path element (SVG Paths: The elliptical arc curve commands)

Bulla answered 13/10, 2008 at 13:57 Comment(0)
B
31

So the solution is here:

The parametrized formula of an ellipse:

x = x0 + a * cos(t)
y = y0 + b * sin(t)

Let's put known coordinates of two points to it:

x1 = x0 + a * cos(t1)
x2 = x0 + a * cos(t2)
y1 = y0 + b * sin(t1)
y2 = y0 + b * sin(t2)

Now we have a system of equations with 4 variables: center of ellipse (x0/y0) and two angles t1, t2

Let's subtract equations in order to get rid of center coordinates:

x1 - x2 = a * (cos(t1) - cos(t2))
y1 - y2 = b * (sin(t1) - sin(t2))

This can be rewritten (with product-to-sum identities formulas) as:

(x1 - x2) / (2 * a) = sin((t1 + t2) / 2) * sin((t1 - t2) / 2)
(y2 - y1) / (2 * b) = cos((t1 + t2) / 2) * sin((t1 - t2) / 2)

Let's replace some of the equations:

r1: (x1 - x2) / (2 * a)
r2: (y2 - y1) / (2 * b)
a1: (t1 + t2) / 2
a2: (t1 - t2) / 2

Then we get simple equations system:

r1 = sin(a1) * sin(a2)
r2 = cos(a1) * sin(a2)

Dividing first equation by second produces:

a1 = arctan(r1/r2)

Adding this result to the first equation gives:

a2 = arcsin(r2 / cos(arctan(r1/r2)))

Or, simple (using compositions of trig and inverse trig functions):

a2 = arcsin(r2 / (1 / sqrt(1 + (r1/r2)^2)))

or even more simple:

a2 = arcsin(sqrt(r1^2 + r2^2))

Now the initial four-equations system can be resolved with easy and all angles as well as eclipse center coordinates can be found.

Bulla answered 13/10, 2008 at 16:13 Comment(6)
To be explicit: t1 = a1+a2, t2 = a1-a2, x0 = x1 - acos(t1), y0 = y1 - bsin(t1)Ventilate
Could you please post an example converting from an SVG arc to a VML arc? e.g. "A80 80 0 1 0 200 200" produces a clockwise large arc of radius 80 to 200,200. How could this replicated in VML? Thanks!Antihistamine
Also, is y0 = y1 - b*sin(t1) correct, or should that be sin(t2)?Antihistamine
Be careful with the last step, taking r2 inside the sqrt gets rid of its sign, giving bad results for some cases!Estrellaestrellita
I implemented this and also checked the derivation. There is a subtle error in this. r2 should be (y1 - y2) / (2 * b) (instead of (y2 - y1) / (2 * b)) and a1 comes out as arctan(-r1/r2) (instead of arctan(r1/r2)). In my implementation, the original equations cause the ellipse to be on the opposite side of x1,y1, while with my changes the ellipse is now in the right place.Heyer
I believe the sentence "Adding this result to the first equation gives" should be rephrased to "Inserting this result into the second equation gives". Otherwise great answer! I've baked it into code and posted it belowTraceable
N
10

The elliptical curve arc link you posted includes a link to elliptical arc implementation notes.

In there, you will find the equations for conversion from endpoint to centre parameterisation.

Here is my JavaScript implementation of those equations, taken from an interactive demo of elliptical arc paths, using Sylvester.js to perform the matrix and vector calculations.

// Calculate the centre of the ellipse
// Based on http://www.w3.org/TR/SVG/implnote.html#ArcConversionEndpointToCenter
var x1 = 150;  // Starting x-point of the arc
var y1 = 150;  // Starting y-point of the arc
var x2 = 400;  // End x-point of the arc
var y2 = 300;  // End y-point of the arc
var fA = 1;    // Large arc flag
var fS = 1;    // Sweep flag
var rx = 100;  // Horizontal radius of ellipse
var ry =  50;  // Vertical radius of ellipse
var phi = 0;   // Angle between co-ord system and ellipse x-axes

var Cx, Cy;

// Step 1: Compute (x1′, y1′)
var M = $M([
               [ Math.cos(phi), Math.sin(phi)],
               [-Math.sin(phi), Math.cos(phi)]
            ]);
var V = $V( [ (x1-x2)/2, (y1-y2)/2 ] );
var P = M.multiply(V);

var x1p = P.e(1);  // x1 prime
var y1p = P.e(2);  // y1 prime


// Ensure radii are large enough
// Based on http://www.w3.org/TR/SVG/implnote.html#ArcOutOfRangeParameters
// Step (a): Ensure radii are non-zero
// Step (b): Ensure radii are positive
rx = Math.abs(rx);
ry = Math.abs(ry);
// Step (c): Ensure radii are large enough
var lambda = ( (x1p * x1p) / (rx * rx) ) + ( (y1p * y1p) / (ry * ry) );
if(lambda > 1)
{
    rx = Math.sqrt(lambda) * rx;
    ry = Math.sqrt(lambda) * ry;
}


// Step 2: Compute (cx′, cy′)
var sign = (fA == fS)? -1 : 1;
// Bit of a hack, as presumably rounding errors were making his negative inside the square root!
if((( (rx*rx*ry*ry) - (rx*rx*y1p*y1p) - (ry*ry*x1p*x1p) ) / ( (rx*rx*y1p*y1p) + (ry*ry*x1p*x1p) )) < 1e-7)
    var co = 0;
else
    var co = sign * Math.sqrt( ( (rx*rx*ry*ry) - (rx*rx*y1p*y1p) - (ry*ry*x1p*x1p) ) / ( (rx*rx*y1p*y1p) + (ry*ry*x1p*x1p) ) );
var V = $V( [rx*y1p/ry, -ry*x1p/rx] );
var Cp = V.multiply(co);

// Step 3: Compute (cx, cy) from (cx′, cy′)
var M = $M([
               [ Math.cos(phi), -Math.sin(phi)],
               [ Math.sin(phi),  Math.cos(phi)]
            ]);
var V = $V( [ (x1+x2)/2, (y1+y2)/2 ] );
var C = M.multiply(Cp).add(V);

Cx = C.e(1);
Cy = C.e(2);
Nahshon answered 13/7, 2012 at 8:55 Comment(0)
G
3

An ellipse cannot be defined by only two points. Even a circle (a special cased ellipse) is defined by three points.

Even with three points, you would have infinite ellipses passing through these three points (think: rotation).

Note that a bounding box suggests a center for the ellipse, and most probably assumes that its major and minor axes are parallel to the x,y (or y,x) axes.

Gamine answered 13/10, 2008 at 14:3 Comment(1)
Indeed, I forgotten to add a note on that. Will do now.Bulla
S
1

The intermediate question is fairly easy... you don't. You work out the centre of an ellipse from the bounding box (namely, the centre of the box is the centre of the ellipse, as long as the ellipse is centred in the box).

For your first question, I'd look at the polar form of the ellipse equation, which is available on Wikipedia. You would need to work out the eccentricity of the ellipse as well.

Or you could brute force the values from the bounding box... work out if a point lies on the ellipse and matches the angle, and iterate through every point in the bounding box.

Silberman answered 13/10, 2008 at 14:5 Comment(5)
I am talking about elliptic arcs in svg:path, not an ellipse. As for the maths, I've done some three papers of solving equations both in polar and linear coordinate systems, still no luck.Bulla
It should still be the same process. An elliptic arc is just a section of an ellipse with 2 'bounding points' of the ellipse. The bounding rectangle should be sufficient to give you all the information to construct the full ellipse. You then just need to feed the angles in to the equation.Silberman
Again, I did not formulate question properly. It is not bounding rectangle that is given, but the sizes of radius.Bulla
well, the radii given would let you formulate a bounding rectangle. Namely the x radius will let you gain the +x and -x values, and the same for the y. The combinations of these will give you all the points of the bounding rectangle. If a centre isn't given, then the only conclusion is that it's 0,0Silberman
This is the problem, center is not in 0,0 (it can indeed be found anywhere) and an intermediate step could be finding the coordinate of center, later it would be easier to calcaulate the angles (given coordinates of points and sizes of bounding rectangle)Bulla
O
0

TypeScript implementation based on the answer from Rikki.

Default DOMMatrix and DOMPoint are used for the calculations (Tested in the latest Chrome v.80) instead of the external library.

 ellipseCenter(
    x1: number,
    y1: number,
    rx: number,
    ry: number,
    rotateDeg: number,
    fa: number,
    fs: number,
    x2: number,
    y2: number
  ): DOMPoint {
    const phi = ((rotateDeg % 360) * Math.PI) / 180;
    const m = new DOMMatrix([
      Math.cos(phi),
      -Math.sin(phi),
      Math.sin(phi),
      Math.cos(phi),
      0,
      0,
    ]);
    let v = new DOMPoint((x1 - x2) / 2, (y1 - y2) / 2).matrixTransform(m);
    const x1p = v.x;
    const y1p = v.y;
    rx = Math.abs(rx);
    ry = Math.abs(ry);
    const lambda = (x1p * x1p) / (rx * rx) + (y1p * y1p) / (ry * ry);
    if (lambda > 1) {
      rx = Math.sqrt(lambda) * rx;
      ry = Math.sqrt(lambda) * ry;
    }
    const sign = fa === fs ? -1 : 1;
    const div =
      (rx * rx * ry * ry - rx * rx * y1p * y1p - ry * ry * x1p * x1p) /
      (rx * rx * y1p * y1p + ry * ry * x1p * x1p);

    const co = sign * Math.sqrt(Math.abs(div));

    // inverse matrix b and c
    m.b *= -1;
    m.c *= -1;
    v = new DOMPoint(
      ((rx * y1p) / ry) * co,
      ((-ry * x1p) / rx) * co
    ).matrixTransform(m);
    v.x += (x1 + x2) / 2;
    v.y += (y1 + y2) / 2;
    return v;
  }
Odysseus answered 12/4, 2020 at 9:8 Comment(0)
T
0

Answering part of the question with code

How to find the center of an ellipse by coordinates of a pair of points on its curve.

This is a TypeScript function which is based on the excellent accepted answer by Sergey Illinsky above (which ends somewhat halfway through, IMHO). It calculates the center of an ellipse with given radii, given the condition that both provided points a and b must lie on the circumference of the ellipse. Since there are (almost) always two solutions to this problem, the code choses the solution that places the ellipse "above" the two points:

(Note that the ellipse must have major and minor axis parallel to the horizontal/vertical)

/**
 * We're in 2D, so that's what our vertors look like
 */
export type Point = [number, number];

/**
 * Calculates the vector that connects the two points
 */
function deltaXY (from: Point, to: Point): Point {
    return [to[0]-from[0], to[1]-from[1]];
}

/**
 * Calculates the sum of an arbitrary amount of vertors
 */
function vecAdd (...vectors: Point[]): Point {
    return vectors.reduce((acc, curr) => [acc[0]+curr[0], acc[1]+curr[1]], [0, 0]);
}

/**
 * Given two points a and b, as well as ellipsis radii rX and rY, this 
 * function calculates the center-point of the ellipse, so that it
 * is "above" the two points and has them on the circumference
 */
function topLeftOfPointsCenter (a: Point, b: Point, rX: number, rY: number): Point {
    const delta = deltaXY(a, b);
    
    // Sergey's work leads up to a simple system of liner equations. 
    // Here, we calculate its general solution for the first of the two angles (t1)
    const A = Math.asin(Math.sqrt((delta[0]/(2*rX))**2+(delta[1]/(2*rY))**2));
    const B = Math.atan(-delta[0]/delta[1] * rY/rX);
    const alpha = A + B;
    
    // This may be the new center, but we don't know to which of the two
    // solutions it belongs, yet
    let newCenter = vecAdd(a, [
        rX * Math.cos(alpha),
        rY * Math.sin(alpha)
    ]);

    // Figure out if it is the correct solution, and adjusting if not
    const mean = vecAdd(a, [delta[0] * 0.5, delta[1] * 0.5]);
    const factor = mean[1] > newCenter[1] ? 1 : -1;
    const offMean = deltaXY(mean, newCenter);
    newCenter = vecAdd(mean, [offMean[0] * factor, offMean[1] * factor]);

    return newCenter;
}

This function does not check if a solution is possible, meaning whether the radii provided are large enough to even connect the two points!

Traceable answered 18/4, 2022 at 14:59 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.