I'm trying to write a code that approximates the value of PI.
What I'm doing is:
drawing a circle inside a rectangle
drawing random points inside the rectangle and circle
calculating the ratio between rect/cicle
calculating 4/ratio
that should be PI
This my code:
import java.awt.Color;
import java.awt.Frame;
import java.awt.Graphics;
import java.awt.Point;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.util.ArrayList;
import java.util.Random;
public class Main extends Frame {
int width = 800;
ArrayList<Point> list = new ArrayList<Point>();
public void points(Graphics g) {
int numPoint = 10000000;
for (int i = 0; i < numPoint; i++) {
int min = 23;
int max = 23 + width;
Random rand = new Random();
int x = rand.nextInt(width);
int y = (int) (Math.random() * (max - min + 1) + min);
Point temp = new Point(x, y);
list.add(temp);
if (inCircle(temp)) {
g.setColor(Color.green);
} else {
g.setColor(Color.blue);
}
g.drawLine(x, y, x, y);
}
}
public void paint(Graphics g) {
g.fillRect(0, 0, 1000, 1000);
int x = width / 2;
int y = width / 2 + 23;
int radius = width / 2;
g.setColor(Color.WHITE);
g.drawOval(x - radius, y - radius, radius * 2, radius * 2);
g.drawRect(0, 23, width, width);
points(g);
calculatingPI();
}
public void calculatingPI() {
double inCircle = 0;
double inRect = list.size();
for (Point p : list) {
if (inCircle(p)) {
inCircle++;
}
}
double ratio = inRect / inCircle;
System.out.print("PI is approximated to: " + 4 / ratio + " ");
}
public boolean inCircle(Point p) {
Point center = new Point(width / 2, width / 2 + 23);
return center.distance(p) <= width / 2;
}
public static void main(String[] args) {
Frame frame = new Main();
frame.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent we) {
System.exit(0);
}
});
// circle coordinates.
frame.setSize(800, 1000);
frame.setVisible(true);
}
}
It works quite fine, even if most of the time the number is around 1,13 which is not a great approx.
The question is:
The more I decrease the size of the rectangle and circle, (without changing the number of points), the less PI becomes accurate. I don't understand, why is that? Is there a problem in my code?
Shouldn't it be the opposite? The smallest the area, the more points are accurate, the more PI is accurate. Why is isn't it the case?
You are using integer pixels. This means the smaller you make your "circle", the worse it approximates a true circle. For example here's the circle within a 3x3 pixel square: it does not look circular at all.
█
███
█
To get a better approximation, use double floating point numbers instead of integers. Use Point2D.Double instead of the Point class:
ArrayList<Point2D.Double> list = new ArrayList<>();
To generate the random points:
double x = Math.random() * width;
double y = Math.random() * (max - min) + min;
Point2D.Double temp = new Point2D.Double(x, y);
Note that where you had max-min+1, the +1 has to be removed.
To test if the point is within the circle:
public boolean inCircle(Point2D.Double p) {
Point2D.Double center = new Point2D.Double(width / 2d, width / 2d + 23);
return center.distance(p) <= width / 2d;
}
I have written an implementation of Bresenham's circle drawing algorithm. This algorithms takes advantage of the highly symmetrical properties of a circle (it only computes points from the 1st octant and draws the other points by taking advantage of symmetry). Therefore I was expecting it to be very fast. The Graphics programming black book, chapter #35 was titled "Bresenham is fast, and fast is good", and though it was about the line drawing algorithm, I could reasonably expect the circle drawing algorithm to also be fast (since the principle is the same).
Here is my java, swing implementation
public static void drawBresenhamsCircle(int r, double width, double height, Graphics g) {
int x,y,d;
y = r;
x = 0;
drawPoint(x, y, width, height,g);
d = (3-2*(int)r);
while (x <= y) {
if (d <= 0) {
d = d + (4*x + 6);
} else {
d = d + 4*(x-y) + 10;
y--;
}
x++;
drawPoint(x, y, width, height,g);
drawPoint(-x, y, width, height,g);
drawPoint(x, -y, width, height,g);
drawPoint(-x, -y, width, height,g);
drawPoint(y, x, width, height,g);
drawPoint(-y, x, width, height,g);
drawPoint(y, -x, width, height,g);
drawPoint(-y, -x, width, height,g);
}
}
This method uses the following drawPointmethod:
public static void drawPoint(double x, double y,double width,double height, Graphics g) {
double nativeX = getNativeX(x, width);
double nativeY = getNativeY(y, height);
g.fillRect((int)nativeX, (int)nativeY, 1, 1);
}
The two methods getNativeX and getNativeY are used to switch coordinates from originating in the upper left corner of the screen to a system that has it origin in the center of the panel with a more classic axis orientation.
public static double getNativeX(double newX, double width) {
return newX + (width/2);
}
public static double getNativeY(double newY, double height) {
return (height/2) - newY;
}
I have also created an implementation of a circle drawing algorithm based on trigonometrical formulaes (x=R*Math.cos(angle)and y= R*Math.sin(angle)) and a third implementation using a call to the standard drawArc method (available on the Graphics object). These additional implementations are for the sole purpose of comparing Bresenham's algorithm to them.
I then created methods to draw a bunch of circles in order to be able to get good measures of the spent time. Here is the method I use to draw a bunch of circles using Bresenham's algorithm
public static void drawABunchOfBresenhamsCircles(int numOfCircles, double width, double height, Graphics g) {
double r = 5;
double step = (300.0-5.0)/numOfCircles;
for (int i = 1; i <= numOfCircles; i++) {
drawBresenhamsCircle((int)r, width, height, g);
r += step;
}
}
Finally I override the paint method of the JPanel I am using, to draw the bunch of circles and to measure the time it took each type to draw. Here is the paint method:
public void paint(Graphics g) {
Graphics2D g2D = (Graphics2D)g;
g2D.setColor(Color.RED);
long trigoStartTime = System.currentTimeMillis();
drawABunchOfTrigonometricalCircles(1000, this.getWidth(), this.getHeight(), g);
long trigoEndTime = System.currentTimeMillis();
long trigoDelta = trigoEndTime - trigoStartTime;
g2D.setColor(Color.BLUE);
long bresenHamsStartTime = System.currentTimeMillis();
drawABunchOfBresenhamsCircles(1000, this.getWidth(), this.getHeight(), g);
long bresenHamsEndTime = System.currentTimeMillis();
long bresenDelta = bresenHamsEndTime - bresenHamsStartTime;
g2D.setColor(Color.GREEN);
long standardStarTime = System.currentTimeMillis();
drawABunchOfStandardCircles(1000, this.getWidth(), this.getHeight(),g);
long standardEndTime = System.currentTimeMillis();
long standardDelta = standardEndTime - standardStarTime;
System.out.println("Trigo : " + trigoDelta + " milliseconds");
System.out.println("Bresenham :" + bresenDelta + " milliseconds");
System.out.println("Standard :" + standardDelta + " milliseconds");
}
Here is the kind of rendering it would generate (drawing 1000 circles of each type)
Unfortunately my Bresenham's implementation is very slow. I took many comparatives measures, and the Bresenham's implementation is not only slower than the Graphics.drawArcbut also slower than the trigonometrical approach. Take a look at the following measures for a various number of circles drawn.
What part of my implementation is more time-consuming? Is there any workaround I could use to improve it? Thanks for helping.
[EDITION]: as requested by #higuaro, here is my trigonometrical algorithm for drawing a circle
public static void drawTrigonometricalCircle (double r, double width, double height, Graphics g) {
double x0 = 0;
double y0 = 0;
boolean isStart = true;
for (double angle = 0; angle <= 2*Math.PI; angle = angle + Math.PI/36) {
double x = r * Math.cos(angle);
double y = r * Math.sin(angle);
drawPoint((double)x, y, width, height, g);
if (!isStart) {
drawLine(x0, y0, x, y, width, height, g);
}
isStart = false;
x0 = x;
y0 = y;
}
}
And the method used to draw a bunch of trigonometrical circles
public static void drawABunchOfTrigonometricalCircles(int numOfCircles, double width, double height, Graphics g) {
double r = 5;
double step = (300.0-5.0)/numOfCircles;
for (int i = 1; i <= numOfCircles; i++) {
drawTrigonometricalCircle(r, width, height, g);
r += step;
}
}
Your Bresenham method isn't slow per se, it's just comparatively slow.
Swing's drawArc() implementation is machine-dependent, using native code. You'll never beat it using Java, so don't bother trying. (I'm actually surprised the Java Bresenham method is as fast as it is compared to drawArc(), a testament to the quality of the virtual machine executing the Java bytecode.)
Your trigonometric method, however, is unnecessarily fast, because you're not comparing it to Bresenham on an equal basis.
The trig method has a set angular resolution of PI/36 (~4.7 degrees), as in this operation at the end of the for statement:
angle = angle + Math.PI/36
Meanwhile, your Bresenham method is radius-dependent, computing a value at each pixel change. As each octant produces sqrt(2) points, multiplying that by 8 and dividing by 2*Pi will give you the equivalent angular resolution. So to be on equal footing with the Bresenham method, your trig method should therefore have:
resolution = 4 * r * Math.sqrt(2) / Math.PI;
somewhere outside the loop, and increment your for by it as in:
angle += resolution
Since we will now be back to pixel-level resolutions, you can actually improve the trig method and cut out the subsequent drawline call and assignments to x0 and y0, eliminate unnecessarily casts, and furthermore reduce calls to Math. Here's the new method in its entirety:
public static void drawTrigonometricalCircle (double r, double width, double height,
Graphics g) {
double localPi = Math.PI;
double resolution = 4 * r * Math.sqrt(2) / Math.PI;
for (double angle = 0; angle <= localPi; angle += resolution) {
double x = r * Math.cos(angle);
double y = r * Math.sin(angle);
drawPoint(x, y, width, height, g);
}
}
The trig method will now be executing more often by several orders of magnitude depending on the size of r.
I'd be interested to see your results.
Your problem lies in that Bresenham's algorithm does a variable number of iterations depending on the size of the circle whereas your trigonometric approach always does a fixed number of iterations.
This also means that Bresenham's algorithm will always produce a smooth looking circle whereas your trigonometric approach will produce worse looking circles as the radius increases.
To make it more even, change the trigonometric approach to produce approximately as many points as the Bresenham implementation and you'll see just how much faster it is.
I wrote some code to benchmark this and also print the number of points produced and here are the initial results:
Trigonometric: 181 ms, 73 points average
Bresenham: 120 ms, 867.568 points average
After modifying your trigonometric class to do more iterations for smoother circles:
int totalPoints = (int)Math.ceil(0.7 * r * 8);
double delta = 2 * Math.PI / totalPoints;
for (double angle = 0; angle <= 2*Math.PI; angle = angle + delta) {
These are the results:
Trigonometric: 2006 ms, 854.933 points average
Bresenham: 120 ms, 867.568 points average
I lately wrote a bresenham circle drawing implemenation myself for a sprite rasterizer and tried to optimize it a bit. I'm not sure if it will be faster or slower than what you did but i think it should have a pretty decent execution time.
Also unfortunately it is written in C++. If i have time tomorrow i might edit my answer with a ported Java version and an example picture for the result but for now you'd have to do it yourself if you want (or someone else who would want to take his time and edit it.)
Bascically, what it does is use the bresenham algorithm to aquire the positions for the outer edges of the circle, then perform the algorithm for 1/8th of the circle and mirror that for the the remaining 7 parts by drawing straight lines from the center to the outer edge.
Color is just an rgba value
Color* createCircleColorArray(const int radius, const Color& color, int& width, int& height) {
// Draw circle with custom bresenham variation
int decision = 3 - (2 * radius);
int center_x = radius;
int center_y = radius;
Color* data;
// Circle is center point plus radius in each direction high/wide
width = height = 2 * radius + 1;
data = new Color[width * height];
// Initialize data array for transparency
std::fill(data, data + width * height, Color(0.0f, 0.0f, 0.0f, 0.0f));
// Lambda function just to draw vertical/horizontal straight lines
auto drawLine = [&data, width, height, color] (int x1, int y1, int x2, int y2) {
// Vertical
if (x1 == x2) {
if (y2 < y1) {
std::swap(y1, y2);
}
for (int x = x1, y = y1; y <= y2; y++) {
data[(y * width) + x] = color;
}
}
// Horizontal
if (y1 == y2) {
if (x2 < x1) {
std::swap(x1, x2);
}
for (int x = x1, y = y1; x <= x2; x++) {
data[(y * width) + x] = color;
}
}
};
// Lambda function to draw actual circle split into 8 parts
auto drawBresenham = [color, drawLine] (int center_x, int center_y, int x, int y) {
drawLine(center_x + x, center_y + x, center_x + x, center_y + y);
drawLine(center_x - x, center_y + x, center_x - x, center_y + y);
drawLine(center_x + x, center_y - x, center_x + x, center_y - y);
drawLine(center_x - x, center_y - x, center_x - x, center_y - y);
drawLine(center_x + x, center_y + x, center_x + y, center_y + x);
drawLine(center_x - x, center_y + x, center_x - y, center_y + x);
drawLine(center_x + x, center_y - x, center_x + y, center_y - x);
drawLine(center_x - x, center_y - x, center_x - y, center_y - x);
};
for (int x = 0, y = radius; y >= x; x++) {
drawBresenham(center_x, center_y, x, y);
if (decision > 0) {
y--;
decision += 4 * (x - y) + 10;
}
else {
decision += 4 * x + 6;
}
}
return data;
}
//Edit
Oh wow, I just realized how old this question is.
I need a method to get the points of the circle, I have one that I found online but unfortunately I'd like to add a boolean filled to it:
public static Location[] getCylinderAt(Location loc, int r, int height) {
ArrayList<Location> list = new ArrayList<>();
int cx = loc.getBlockX();
int cy = loc.getBlockY();
int cz = loc.getBlockZ();
World w = loc.getWorld();
int rSquared = r * r;
for (int x = cx - r; x <= cx + r; x++) {
for (int y = cy - height; y <= cy + height; y++) {
for (int z = cz - r; z <= cz + r; z++) {
if ((cx - x) * (cx - x) + (cz - z) * (cz - z) <= rSquared) {
list.add(new Location(w, x, y, z));
}
}
}
}
return list.toArray(new Location[list.size()]);
}
I can't really get my head around the maths involved in this and have been searching through non minecraft sources to create my own but to no avail.
Ideally I'd like to be able to change the method to this:
public static Location[] getCylinderAt(Location loc, boolean filled, int r, int height)
Thanks guys! If you like I can remove all of the minecraft references, but I didn't think it'd be necessary as a Location is basically a Vector with a few added minecraft only variables!
Thanks for reading :)
Are you looking for a way to compute pixels on the rim of a circle, as opposed to those inside, for the case where filled is false? If so, have a look at the midpoint circle algorithm. It describes how a circle can be drawn in a raster image.
I want an circular movement of an image in JAVA, i thought I have the solution but it doesn't work and i'm a bit clueless now.
For calculating the points it needs to go im using pythagoras to calculate the height (point B).
if it does one round im satisfied but more rounds would be cool.
The image size is around 500 x 300 pixels.
Here's my code :
package vogel;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.image.*;
import java.io.*;
import javax.imageio.*;
import javax.swing.*;
public class Vogel extends Component {
private int x;
private int r;
private int b;
BufferedImage img;
public vogel() {
try {
img = ImageIO.read(new File("F:/JAVA/workspace/School/src/vogel/vogel.png"));
} catch (IOException e) {
}
r = 60;
x = 10;
}
#Override
public void paint(Graphics g) {
for(int i = -x; i <= x; i++) {
b = (int)Math.sqrt(r^2 - i^2);
g.drawImage(img, x, b, this);
}
}
public static void main(String[] args) {
JFrame f = new JFrame("Vogel");
f.setSize(1000,1000);
f.add(new Vogel());
f.setVisible(true);
for (int number = 1; number <= 1500000; number++) {
f.repaint();
try {
Thread.sleep(50);
} catch (InterruptedException e) {}
}
}
}
Using your loop in the paint(Graphics) method, it draws 21 birds with one repaint.
You should do it with an angle stored in an object variable and use the Math.sin() and Math.cos() function calculate the x and y position. The angle should be increased with every repaint().
To add:
// To control the radius of moving
private final double MAX_X = 200;
private final double MAX_Y = 200;
private double angle = 0;
#Override
public void paint(Graphics g) {
// increase angle (should be a double value)
angle += 0.1;
// rotate around P(0/0), assuming that 0° is vector (1/0)
int x = (int) (Math.cos(angle) * MAX_X);
int y = (int) (Math.sin(angle) * MAX_Y);
// move P to center of JFrame (width and height = 1000)
x += 500;
y += 500;
// image is 500x300, calc upper left corner
x -= 250;
y -= 150;
// draw
g.drawImage(img, x, y, null);
}
To remove:
private double x, b, r;
So this is the code, try it.
Addition to Sibbo's code to convert angle to rads
private double angle = 0.1;
#Override
public void paint(Graphics g) {
// increase angle (should be a double value
double random = angle * 2.0 * Math.PI/360.0; //this will convert it to rads
// rotate around P(0/0)
int x = (int) (Math.cos(random) * MAX_X);
int y = (int) (Math.sin(random) * MAX_Y);
// move P to center of JFrame (width and height = 1000)
x += 500;
y += 500;
// image is 500x300, calc upper left corner
x -= 250;
y -= 150;
angle++
// draw
g.drawImage(img, x, y, null);
}
So I'm trying to implement a test where a oval can connect with a circle, but it's not working.
edist = (float) Math.sqrt(
Math.pow((px + ((pwidth/2) )) - (bx + (bsize/2)), 2 ) +
Math.pow(-((py + ((pwidth/2)) ) - (bx + (bsize/2))), 2 )
);
and here is the full code (requires Slick2D):
import org.newdawn.slick.AppGameContainer;
import org.newdawn.slick.BasicGame;
import org.newdawn.slick.Color;
import org.newdawn.slick.GameContainer;
import org.newdawn.slick.Graphics;
import org.newdawn.slick.Input;
import org.newdawn.slick.SlickException;
public class ColTest extends BasicGame{
float px = 50;
float py = 50;
float pheight = 50;
float pwidth = 50;
float bx = 200;
float by = 200;
float bsize = 200;
float edist;
float pspeed = 3;
Input input;
public ColTest()
{
super("ColTest");
}
#Override
public void init(GameContainer gc)
throws SlickException {
}
#Override
public void update(GameContainer gc, int delta)
throws SlickException
{
input = gc.getInput();
try{
if(input.isKeyDown(Input.KEY_UP))
py-=pspeed;
if(input.isKeyDown(Input.KEY_DOWN))
py+=pspeed;
if(input.isKeyDown(Input.KEY_LEFT))
px-=pspeed;
if(input.isKeyDown(Input.KEY_RIGHT))
px+=pspeed;
}
catch(Exception e){}
}
public void render(GameContainer gc, Graphics g)
throws SlickException
{
g.setColor(new Color(255,255,255));
g.drawString("col: " + col(), 10, 10);
g.drawString("edist: " + edist + " dist: " + dist, 10, 100);
g.fillRect(px, py, pwidth, pheight);
g.setColor(new Color(255,0,255));
g.fillOval(px, py, pwidth, pheight);
g.setColor(new Color(255,255,255));
g.fillOval(200, 200, 200, 200);
}
public boolean col(){
edist = (float) Math.sqrt(Math.pow((px + ((pwidth/2) )) - (bx + (bsize/2)), 2) + Math.pow(-((py + ((pwidth/2)) ) - (bx + (bsize/2))), 2));
if(edist <= (bsize/2) + (px + (pwidth/2)))
return true;
else
return false;
}
public float rotate(float x, float y, float ox, float oy, float a, boolean b)
{
float dst = (float) Math.sqrt(Math.pow(x-ox,2.0)+ Math.pow(y-oy,2.0));
float oa = (float) Math.atan2(y-oy,x-ox);
if(b)
return (float) Math.cos(oa + Math.toRadians(a))*dst+ox;
else
return (float) Math.sin(oa + Math.toRadians(a))*dst+oy;
}
public static void main(String[] args)
throws SlickException
{
AppGameContainer app =
new AppGameContainer( new ColTest() );
app.setShowFPS(false);
app.setAlwaysRender(true);
app.setTargetFrameRate(60);
app.setDisplayMode(800, 600, false);
app.start();
}
}
Is using ovals an absolute requirement? You can approximate collisions between fancier shapes by representing them with multiple circles. That way you can use very a simple collision detection between circles and still achieve a high level of accuracy for the viewer.
collision(c1, c2) {
dx = c1.x - c2.x;
dy = c1.y - c2.y;
dist = c1.radius + c2.radius;
return (dx * dx + dy * dy <= dist * dist)
}
(source: strd6.com)
Finding the intersection is harder than you think. Your col() method is a bit off, but that approach will at best be able to tell you if a single point is within the circle. It won't be able to really detect intersections.
I Googled up some code for computing the actual intersections. I found one in JavaScript that's really interesting and really complicated. Take a look at the source.
If you wanted something a bit simpler (but less accurate), you could check a few points around the ellipse to see if they're within the circle.
private boolean isInCircle(float x, float y) {
float r = bsize / 2;
float center_x = bx + r;
float center_y = by + r;
float dist = (float) Math.sqrt(Math.pow(x - center_x, 2) + Math.pow(y - center_y, 2));
return dist < r;
}
public boolean col() {
return
isInCircle(px + pwidth / 2, py ) || // top
isInCircle(px + pwidth , py + pheight / 2) || // right
isInCircle(px + pwidth / 2, py + pheight ) || // bottom
isInCircle(px , py + pheight / 2); // left
}
If you plan on implementing more shapes and/or need the minimum distance between your shapes, you could start using GJK : you would only need to implement the support functions for each new shape. If computation time is also critical, GJK is definitely something you should look at, but it would surely require some more programming on your side.
If you can find your foci you can check for collision with the pseudo code below.
WARNING this only works for two ellipse collisions (ellipse and circle collisions work also).
r = length of semi major axis
a_x = x coordinate of foci 1 of the first ellipse
a_y = y coordinate of foci 1 of the first ellipse
b_x = x coordinate of foci 2 of the first ellipse
b_y = y coordinate of foci 2 of the first ellipse
c_x = x coordinate of foci 1 of the second ellipse
c_y = y coordinate of foci 1 of the second ellipse
d_x = x coordinate of foci 2 of the second ellipse
d_y = y coordinate of foci 2 of the second ellipse
p_x = (a_x+b_x+c_x+d_x)/4 // i.e. the average of the foci x values
p_y = (a_y+b_y+c_y+d_y)/4 // i.e. the average of the foci y values
if r >= ( sqrt( (p_x + a_x)^2+(p_y + a_y)^2 ) + sqrt( (p_x + a_x)^2+(p_y + a_y)^2 ) )
then collision
If you really want the derivation of this let me know and I'll provide it. But it uses the idea that the sum of the distances between the foci of an ellipse and any point on the edge of an ellipse are a set distance apart (the semi major axis). And solves for a point that is on the edge of both ellipsoids and if one exist then their is a collision.