generating points that fall on the perimeter of an ellipse

i need help on generating points that fall on the perimeter of an ellipse. i know that on a x-y coordinate plane, the equation x^2/a^2 + y^2/b^2 = 1 defines all points that fall on the perimeter of an ellipse. solving for y, i get y = sqrt ( (a^2 - x^2) * b^2/a^2). had the computer graphics coordinate system been the same as cartesian x-y coordinate, then my problem would have been solved. but in computer graphics, as i am now reminded due to this encounter, the x goes from 0 to +infinity and y goes from 0 to +infinity.

is there an easier way to generate (x,y) points that fall on an ellipse's perimeter?

thanks.

[638 byte] By [jakestera] at [2007-11-27 10:19:31]
# 1

I don't know if this is the most efficient way, but...

double angle = //random angle between -pi and pi

double radius = //the radius of your circle

double x = Math.cos(angle) * radius;

double y = Math.sin(angle) * radius;

CaptainMorgan08a at 2007-7-28 16:58:10 > top of Java-index,Security,Cryptography...
# 2

import java.awt.*;

import java.awt.geom.*;

import javax.swing.*;

public class EllipsePoints extends JPanel {

protected void paintComponent(Graphics g) {

super.paintComponent(g);

Graphics2D g2 = (Graphics2D)g;

g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,

RenderingHints.VALUE_ANTIALIAS_ON);

int w = getWidth();

int h = getHeight();

double cx = w/2.0;

double cy = h/2.0;

double width = w*7/8.0;

double height = h*2/3.0;

g2.setPaint(Color.pink);

g2.draw(new Ellipse2D.Double(w/16.0, h/6.0, width, height));

double theta = 0;

for(int j = 0; j < 12; j++) {

double x = cx + (width/2)*Math.cos(theta);

double y = cy + (height/2)*Math.sin(theta);

g2.setPaint(new Color(240,200,220));

g2.draw(new Line2D.Double(cx, cy, x, y));

mark(x, y, g2, Color.red);

theta += Math.PI/6;

}

}

private void mark(double x, double y, Graphics2D g2, Color color) {

g2.setPaint(color);

g2.fill(new Ellipse2D.Double(x-2, y-2, 4, 4));

}

public static void main(String[] args) {

JFrame f = new JFrame();

f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

f.getContentPane().add(new EllipsePoints());

f.setSize(400,400);

f.setLocation(200,200);

f.setVisible(true);

}

}

crwooda at 2007-7-28 16:58:10 > top of Java-index,Security,Cryptography...
# 3

crwood,

thanks. one more question, i lost you in the iteration loop. you loop 12 times to generate 12 points. before the end of the loop, you increment theta by PI/6. why?

if i wanted to generate more than 12 points, how would i modify the code?

i thought that you would divide PI by half the number of points you want to generate. emprically, this thought was wrong. for example, if i wanted to generate 24 points, i would increment theta by PI/12.

jakestera at 2007-7-28 16:58:10 > top of Java-index,Security,Cryptography...
# 4

you loop 12 times to generate 12 points. before the end of the loop, you increment

theta by PI/6. why?

I wanted to draw a point for every 30 degrees of arc. 360 degrees divided by 30 equals 12.

So I needed to do do 12 loop iterations and advance theta by 30 degrees (PI/6) each time.

The basic identity is: 2PI radians equals 360 degrees. So 30 degrees is 2PI divided by 12.

24 points on the ellipse curve would be one point every: 360 degrees divided by 24 equals

15 degrees. 15 degrees is 2PI/24 equals PI/12, agreed.

Using 12 and PI/6 was lazy. It might make more sense/be easier to read if I had written:

double theta = 0;

int numPts = 12;

for(int j = 0; j < numPts; j++) {

System.out.printf("theta = %.1f%n", Math.toDegrees(theta));

double x = cx + (width/2)*Math.cos(theta);

double y = cy + (height/2)*Math.sin(theta);

g2.setPaint(new Color(240,200,220));

g2.draw(new Line2D.Double(cx, cy, x, y));

mark(x, y, g2, Color.red);

theta += (2*Math.PI)/numPts;

}

Another way to increment theta is

theta += Math.toRadians(360/numPts);

crwooda at 2007-7-28 16:58:10 > top of Java-index,Security,Cryptography...
# 5

crywood,

thanks! i noticed that you have helped me in the past too with http://forum.java.sun.com/thread.jspa?threadID=5190827.

the reason why i'm generating points around an ellipse is to draw an arrow from one ellipse to another as in the thread above (with rectangles). for circles, the code is easy, but i could not figure out how to do so with an ellipse. my thinking was to generate points on an ellipse, figure out which point is closest to the other ellipse (where the arrow is coming from), and draw an arrow to that closest point.

i know there must be a more efficient way. could you shed some light on this?

jakestera at 2007-7-28 16:58:10 > top of Java-index,Security,Cryptography...
# 6

import java.awt.*;

import java.awt.geom.*;

import javax.swing.*;

public class EllipsePointers extends JPanel {

Ellipse2D.Double e1 = new Ellipse2D.Double(25,40,150,75);

Ellipse2D.Double e2 = new Ellipse2D.Double(200,220,200,125);

double barb = 20;

double phi = Math.toRadians(20);

protected void paintComponent(Graphics g) {

super.paintComponent(g);

Graphics2D g2 = (Graphics2D)g;

g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,

RenderingHints.VALUE_ANTIALIAS_ON);

g2.setPaint(Color.blue);

g2.draw(e1);

g2.draw(e2);

g2.setPaint(Color.red);

g2.draw(getPath());

}

private GeneralPath getPath() {

double x1 = e1.getCenterX();

double y1 = e1.getCenterY();

double x2 = e2.getCenterX();

double y2 = e2.getCenterY();

Line2D.Double line = new Line2D.Double(x1, y1, x2, y2);

Point2D.Double p1 = getContactPoint(line, e1);

Point2D.Double p2 = getContactPoint(line, e2);

GeneralPath path = new GeneralPath(new Line2D.Double(p1, p2));

double theta = Math.atan2(y2-y1, x2-x1);

appendArrowHead(path, p2, theta+Math.PI);

return path;

}

private Point2D.Double getContactPoint(Line2D.Double line, Ellipse2D.Double e) {

Point2D.Double p = new Point2D.Double();

double flatness = 0.01;

PathIterator pit = e.getPathIterator(null, flatness);

double[] coords = new double[2];

double minDist = Double.MAX_VALUE;

int count = 0;

while(!pit.isDone()) {

int type = pit.currentSegment(coords);

switch(type) {

case PathIterator.SEG_MOVETO: // fall through

case PathIterator.SEG_LINETO:

double distance = line.ptSegDist(coords[0], coords[1]);

if(distance < minDist) {

minDist = distance;

p.setLocation(coords[0], coords[1]);

}

break;

case PathIterator.SEG_CLOSE: break;

default:

System.out.println("Unexpected type: " + type);

}

count++;

pit.next();

}

System.out.println("count = " + count);

return p;

}

private void appendArrowHead(GeneralPath path, Point2D.Double p, double theta) {

double x = p.x + barb*Math.cos(theta-phi);

double y = p.y + barb*Math.sin(theta-phi);

path.moveTo((float)x, (float)y);

path.lineTo((float)p.x, (float)p.y);

x = p.x + barb*Math.cos(theta+phi);

y = p.y + barb*Math.sin(theta+phi);

path.lineTo((float)x, (float)y);

}

public static void main(String[] args) {

JFrame f = new JFrame();

f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

f.getContentPane().add(new EllipsePointers());

f.setSize(500,400);

f.setLocation(200,200);

f.setVisible(true);

}

}

crwooda at 2007-7-28 16:58:10 > top of Java-index,Security,Cryptography...
# 7

thanks. you must be some java guru or angel.

jakestera at 2007-7-28 16:58:10 > top of Java-index,Security,Cryptography...