2

I wish to know the coordinates of a vertex on an ellipse, given an angle. The ellipse center is on x = 0 and y = 0. The semi-major axis is x and has value 1. The semi-minor axis is y and has value 0.38. The angle is variable.

I have tried everything on this Stack Exchange post but nothing worked: https://math.stackexchange.com/questions/22064/calculating-a-point-that-lies-on-an-ellipse-given-an-angle

It's probably not the given solutions that are wrong, but my integration of them in JavaScript.

For example, this does not work:

a = 1
b = 0.38

function getAngle(x1, y1, x2, y2) {
    return Math.atan((y2 - y1) / (x2 - x1)) * 180 / Math.PI
}

angle = getAngle(0,0,0.3,0.7)

x = (a*b*Math.cos(angle))
/
Math.sqrt(
    (a*Math.cos(angle))**2 +
    (b*Math.sin(angle))**2
)

y = (a*b*Math.sin(angle))
/
Math.sqrt(
    (a*Math.cos(angle))**2 +
    (b*Math.sin(angle))**2
)

console.log({x,y})

Output:

{
    "x": -0.35112437558647797,
    "y": -0.3823646436315567
}

As you can see on this image, the result is not a vertex on the ellipse:

enter image description here

How do I find the coordinates of a point in the ellipse based on an angle?

papillon
  • 1,807
  • 2
  • 19
  • 39
  • 1
    Does this answer your question? [OpenGL Ellipse with start/end angle issues](https://stackoverflow.com/questions/71209038/opengl-ellipse-with-start-end-angle-issues) see **[Edit2] Fast analytic O(1) solution** in my answer there – Spektre Nov 21 '22 at 08:32

1 Answers1

5

Such an ellipse is just a circle, scaled by a factor 0.38 on y axis.

The reasoning I would apply to understand the answer would be to reverse the scaling.

If you were to scale the whole drawing y axis by a factor 1/0.38≈2.63, then the ellipse would look like a circle. But then, angle of the blue line would be different. Because it would have a slot not of 0.7/0.3 but of 2.63×0.7/0.3.

And that angle, would also be the angle of the point intersection of that blue line and the ellipse. It is the parametric coordinate of the intersection in the ellipse.

Since that parametric coordinate does not depend on the scaling, it remain the same even at the current 0.38 scale

So, conclusion is that

let angle = Math.atan((0.7/0.3)*a/b);
let x = a*Math.cos(angle);
let y = b*Math.sin(angle);

Demonstration:

let a=1;
let b=0.38;

let canvas=document.getElementById('canvas');
let ctx = canvas.getContext("2d");
let y0=120;
let x0=100;
let sc=150;
ctx.strokeStyle='#000000';
// Axis
ctx.beginPath(); 
ctx.moveTo(0,y0);
ctx.lineTo(300,y0);
ctx.stroke();
ctx.moveTo(x0,0);
ctx.lineTo(x0,300);
ctx.stroke();
// Ellipse
ctx.strokeStyle='#ff0000';
ctx.beginPath();
ctx.moveTo(x0+sc*a,y0);
ctx.ellipse(x0,y0,sc*a, sc*b, 0, 0, 6.28);
ctx.stroke();
// Blue line
ctx.strokeStyle='#0000ff';
ctx.beginPath();
ctx.moveTo(x0,y0);
ctx.lineTo(x0+0.3*sc, y0-0.7*sc);
ctx.stroke();

// Intersection coordinate. The next 3 lines are my answer
// (all the lines before, and after the next 3, are just 
// what is needed for drawing)
let angle = Math.atan((0.7/0.3)*a/b);
let x = a*Math.cos(angle);
let y = b*Math.sin(angle);

// Draw a yellow box at this point. It should intersect both ellipse and line
ctx.fillStyle='#888800';
ctx.beginPath();
ctx.fillRect(x0+x*sc-5, y0-y*sc-5, 10, 10);
<canvas id=canvas width=300 height=200>
</canvas>

Now, you may note that it does not answer strictly to the question in your question title. It does not give coordinate of a point in an ellipse based on an angle. Since angle here is not the angle of the blue line, but the angle it would have if we were scaling the chart so the ellipse appeared as a circle.

But from the body of your question, and more over from your code, it appear that your real question is "finding point in an ellipse based on a line slope 0.7/0.3".

But if you really want to find the point from the angle (the angle of the blue line, you just have to use tan to get the slope, before scaling and using atan to get the real angle we need.

So

let parametricAngle = Math.atan(Math.tan(inputAngle)*a/b);
let x=a*Math.cos(parametricAngle);
let y=b*Math.sin(parametricAngle);
chrslg
  • 9,023
  • 5
  • 17
  • 31
  • Awesome! +1. I tried to do it using canvas [scale](https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/scale) and [translate](https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/translate), but that didn't work as well. – Yogi Nov 21 '22 at 00:52
  • Amazing, and way more simple than what the other solutions I found and that I could not make work! Would it be simple to add the constraint of a variable center of the ellipse? If it's center is not 0;0 but 0.5;3 for example, would it be simple to apply this solution? – papillon Nov 21 '22 at 08:32
  • 1
    @papillon That changes nothing, or almost so. You can see, btw, than in my javascript snippet, in reality center is not (0,0) but (x0,y0)=(100,120), since (0,0) in a canvas would be in the corner of the canvas. So, If you have a blue line passing through the center (x0,y0) and another point (x1,y1), (x1-x0) becomes the new 0.3 of your example, and (y1-y0) the new 0.7. So angle of the point is `atan((y1-y0)/(x1-x0))` and what I've called `parametricAngle` (that is the angle you have to pass to `cos` and `sin`) is `atan((y1-y0)/(x1-x0)*a/b)`. – chrslg Nov 21 '22 at 10:11
  • 1
    Then, from this `parametricAngle=Math.atan((y1-y0)/(x1-x0)*a/b)`, you can know the intersection point which is `x=x0+a*Math.cos(parametricAngle);` and `y=y0+b*Math.sin(parametricAngle);`. – chrslg Nov 21 '22 at 10:12
  • 1
    Almost as I did in the snippet. With `x0`, `y0` being 100 and 120. And `x1-x0` 0.3, `y1-y0` 0.7. The only difference is that in the snipped I said `y=y0-b*Math.sin(parametricAngle)`, so `-` instead of `+`. That is because in canvas, y axis is downward, and I wanted to imitate your graph, where y axis is, as tradition, upward. – chrslg Nov 21 '22 at 10:14
  • 1
    Note, in defense of solutions you got on Math.SE, that they reply to the initial question. That is not "how I find an intersection of an ellipse and a line", but "how I find the point of an ellipse at angle `angle`". My answer to that is also simpler (just compute `parametricAngle=Math.atan(Math.tan(angle)*a/b)`. And then compute cos and sin of that. Which at the end leads to expression `x=a*Math.cos(Math.atan(Math.tan(angle)*a/b))` and `y=b*Math.sin(Math.atan(Math.tan(angle)*a/b))` – chrslg Nov 21 '22 at 10:20
  • 1
    Such sin, cos, tan atan are quite high level operations. It may be simpler to understand, but it is a bit harder for the computer to compute. And those `sin(atan(tan(...)))` and `cos(atan(tan(...)))` have simpler formulation (from the computer point of view, not from yours :D). The answers form Math.SE are those simpler expressions. – chrslg Nov 21 '22 at 10:21
  • 1
    In your case, it would translate to, naming the director of the blue line dx,dy as `dx=0.3; dy=0.7;`, x and y can be computed as `x=a*b*/Math.sqrt(b*b+a*a*dy*dy/dx/dx)` and `y=b*Math.sqrt(1-x*x/a/a)` – chrslg Nov 21 '22 at 10:33
  • very clear, thanks @chrslg ! :) – papillon Nov 21 '22 at 10:58
  • Just an inversion from the solution of Math.SE (which calls y what I call x, and x what I call y). But outside that, they are the same values. And are faster to compute. Even if slightly harder to explain – chrslg Nov 21 '22 at 10:59