how do I calculate the points in a 5-point polygon [closed] - java

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 8 years ago.
Improve this question
I have a class Demo with a button and when the user clicks the button called polygon a polygon is drawn starting from the point where they hit, The code works fine in drawing but unfortunately it draws the polygon in the wrong place.
The PolygonShape Class
class PolygonShape {
int x, y;
private Polygon p;
public PolygonShape(int x, int y) {
// the x, y sent to this constructor
//are the cordinates of the point where the user clicked
this.x = x;
this.y = y;
}
public void draw(Graphics g) {
p = new Polygon();
for (int i = 0; i < 5; i++)
p.addPoint((int) (x + y * Math.cos(i * 2 * Math.PI / 5)),
(int) (x + y * Math.sin(i * 2 * Math.PI / 5)));
g.drawPolygon(p);
}
}

Assuming x and y are the center of the polygon, you're using them wrong (you need to add x to the x coordinate and y to the y coordinate) and you're missing another important variable: r for radius. Instead of multiplying by y, you should multiply by r in your formulae.
In other words:
class PolygonShape {
int x, y, r;
private Polygon p;
public PolygonShape(int x, int y, int r) {
this.x = x;
this.y = y;
this.r = r;
}
// Provide a default radius of 100 pixels if no radius is given.
public PolygonShape(int x, int y) {
this(x, y, 100);
}
public void draw(Graphics g) {
p = new Polygon();
for (int i = 0; i < 5; i++) {
double angle = i * 2 * Math.PI / 5;
p.addPoint((int) (x + r * Math.cos(angle)),
(int) (y + r * Math.sin(angle)));
}
g.drawPolygon(p);
}
}

Another option is to set the translation on the graphics before drawing:
final Graphics2D g2 = (Graphics2D)g.create();
g2.translate(x, y);
g2.drawPolygon(p);
You may need to do -x, -y, you'll have to try it.
I am working on a new graphics object (g2) so that the translate is not permanent.
Advantage is, can draw same shape in multiple places, just vary x and y.

Related

Java how to make a circle slowly move around?

Hello I have an app where there are circles floating around. At the moment the don't float around, which is the problem. I want them to slowly move around in random directions. How can I do this?
Here is my circle class:
public class data {
public int x,y, size,id;
public data(int x,int y){
this.x = x;
this.y = y;
size = new Random().nextInt(50);
id = new Random().nextInt(10);
}
public void tick(){
}
public void render(Graphics g){
g.setColor(new Color(38,127,0));
g.fillOval(x, y, size, size);
g.setColor(Color.black);
g.drawOval(x, y, size, size);
}
}
You can have a very random movement by adding a random value to x and to y every tick:
private Random random = new Random();
public void tick() {
x = x + random.nextFloat();
y = y + random.nextFloat();
}
This will result in a very fuzzy motion.
Another option is to have 2 variables: motionX and motionY. Those get added to x and y every tick, after which you add random values to motionX and motionY:
private Random random = new Random();
private float xMotion = 0f, yMotion = 0f;
private float factor = 0.5f; //just a value to reduce speed
private void tick() {
x = x + xMotion;
y = y + yMotion;
xMotion = xMotion + random.nextFloat() * factor;
yMotion = yMotion + random.nextFloat() * factor;
}
First of all, I think you messed up which one you should random, you random selected a size and the id(what is this for anyways?), not the x,y value. Also, I don't know why, but it seems that the render program must be called paint.

Java custom Path2D

I have created a custom Path2D class to draw an H-shaped "calliper" on screen, for a project I am doing. I want to drag and eventually resize the calliper on screen. I have managed to get the Path2D set up so I can draw the calliper, and the code looks like this:
Declaration and Constructor:
public class Calliper extends Path2D.Double
{
// X and Y coordinates of all six points on Calliper
double cX1, cX2, cX3, cX4, cX5, cX6;
double cY1, cY2, cY3, cY4, cY5, cY6;
// Width and Height
double cWidth;
double cHeight;
public Calliper(double x, double y, double w, double h)
{
cWidth = w;
cHeight = h;
cX1 = x;
cY1 = y;
cX2 = x;
cY2 = y + (h/2);
cX3 = x;
cY3 = y + h;
cX4 = x + w;
cY4 = y;
cX5 = cX4;
cY5 = cY4 + (h /2);
cX6 = cX4;
cY6 = cY4 + h;
build();
}
build() method (used to draw the path) and setCalliper() method, used to redefine the coordinates, or width, height:
private void build()
{
// Draw the path for the calliper
moveTo(cX1, cY1);
lineTo(cX2, cY2);
lineTo(cX3, cY3);
moveTo(cX2, cY2);
lineTo(cX5, cY5);
moveTo(cX4, cY4);
lineTo(cX6, cY6);
}
public void setCalliper(double x, double y, double w, double h)
{
// Rebuild the calliper using different x,y coordinates, or
// different width/height
cWidth = w;
cHeight = h;
cX1 = x;
cY1 = y;
cX2 = x;
cY2 = y + (h/2);
cX3 = x;
cY3 = y + h;
cX4 = x + w;
cY4 = y;
cX5 = cX4;
cY5 = cY4 + (h /2);
cX6 = cX4;
cY6 = cY4 + h;
build();
}
I have created a class to draw this calliper on the screen, which it will do, however if I try to drag the calliper around the screen, it doesn't erase the original shape as I drag, so I get a long trail of shapes left behind. I thought I had omitted super.paintComponent(g) from my paintComponent(Graphics g) method, but even with it in there the code still does not work.
My drag method looks like this:
#Override
public void mouseDragged(MouseEvent ev)
{
double mx = ev.getX();
double my = ev.getY();
if (dragging)
{
calX = mx - offsetX;
calY = my - offsetY;
cal = setCalliper(calX, calY, calW, calH);
repaint();
}
}
If I change the line cal = setCalliper(calX, calY, calW, calH); above to read cal = new Calliper(calX, calY, calW, calH); then it works, but I have been told I shouldn't do it this way.
Any ideas why it doesn't work as expected?
The setCalliper() directly calls the build method, a method which appends new points to all the previous points added to the Path2D - so each time mouseDragged is called more points are added to the Path. Try calling reset() before calling build() (or call reset in the build method before the moveTo/lineTo calls).

Implementing Bresenham's circle drawing algorithm

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.

Java shoot towards mouse

I have a top down 2d game where you walk around shooting bad guys. I want to be able to shoot towards the mouse, no mater what direction it is but I have absolutely no idea how to do this.
Here is my bullet class:
public class bullet {
public double x, y,dy,dx,mx,my;
public int dir;
public Rectangle r = new Rectangle((int) x, (int) y, 5, 5);
public bullet(double x, double y) {
this.x = x+10;
this.y = y+10;
this.mx = Comp.mx;
this.my = Comp.my;
r = new Rectangle((int) x, (int) y, 5, 5);
if (x < mx+play.camx) {
dx = 1;
}
if (x > mx+play.camx) {
dx = -1;
}
if (y < my+play.camy) {
dy = 1;
}
if (y > my+play.camy) {
dy = -1;
}
}
public void tick() {
x+=dx;
y+=dy;
r = new Rectangle((int) x - play.camx, (int) y - play.camy, 5, 5);
}
public void render(Graphics g) {
g.setColor(Color.black);
g.fillRect((int) x - play.camx, (int) y - play.camy, 5, 5);
}
}
Basicially, you need calculate the angel between the start point and end point, something like...
angle = -Math.toDegrees(Math.atan2(startX - endX, startY - endY)) + 180;
As an example:
Rotating a triangle around a point java
Java make a directed line and make it move
mouse motion listener only in one direction
Java: Move image towards mouse position
To track the mouse, use a MouseListener and MouseMotionListerner
Take a look at:
How to write a MouseListener
How to write a MouseMotionListener
Try using MouseInfo.getPointerInfo().getPosition() ( http://download.oracle.com/javase/1.5.0/docs/api/java/awt/PointerInfo.html#getLocation%28%29) It will return a point object.
Use a timer and on every timer event you'll move your bullet a specific length (which you would want it to move) towards the mouse position provided by aforementioned method.
You could do it like reducing difference of x- and y- Variables of mouse position and bullet position.

Shoot to the mouse direction

The problem:
I've got this "Shot" class. In the code, the target variables are the mouseX and mouseY.
So when i click the mouse button, my player class will create a new shot object.
But the shooting is inaccurate.
How can i calculate the correct dx and dy?
If i add the dx and dy to the "bullet's" x and y, the bullet will move to the mouse's direction.This is what i want. The mouse position is stored in targetX and targetY, when the object is created. This is the point what the oval wants to reach.
Links:
The game (finished)
The code (from Shot.java):
public class Shot extends Entity {
private float targetX, targetY;
public Shot(World world, float x, float y, int width, int height, Color color, float targetX, float targetY) {
super(world, x, y, width, height, color);
this.targetX = targetX;
this.targetY = targetY;
}
#Override
public void render(GameContainer gc, Graphics g, Camera camera) {
g.setColor(color);
g.fillOval(x - camera.getX(), y - camera.getY(), width, height);
}
#Override
public void update(GameContainer gc, int delta) {
float dx = targetX - x;
float dy = targetY - y;
x += dx * delta * .001f;
y += dy * delta * .001f;
}
}
I tried this, but still not work:
#Override
public void update(GameContainer gc, int delta) {
float length = (float) Math.sqrt((targetX - x) * (targetX - x) + (targetY - y) * (targetY - y));
double dx = (targetX - x) / length * delta;
double dy = (targetY - y) / length * delta;
x += dx;
y += dy;
}
I did it! Here is my solution:
The problem was that, the target was the window's mouse position, and not the world's mouse position.
This is how i calculated the world's mouse positions:
float mouseWorldX = x + (mouseX - screen_width / 2); // x = player's x position
float mouseWorldY = y + (mouseY - screen_height / 2); // y = player's y position
This is code from my game at the moment is used to move a unit to the mouse when the right mouse button is pressed:
length = Math.sqrt((target_X - player_X)*(target_X - player_X) + (target_Y - player_Y)*(target_Y - player_Y)); //calculates the distance between the two points
speed_X = (target_X - player_X) /length * player_Speed;
speed_Y = (target_Y - player_Y) /length * player_Speed;
This will move an object to the target in a line at a set speed.
Edit: this is the actual code right from my game
if(input.isMouseButtonDown(Input.MOUSE_RIGHT_BUTTON))
{
length = (float) Math.sqrt((player_waypoint_X - player_X)*(player_waypoint_X - player_X) + (player_waypoint_Y - player_Y)*(player_waypoint_Y - player_Y));
velocityX = (float) (player_waypoint_X - player_X) /length * (float) PlayerStats.player.db_player_Speed;
velocityY = (float) (player_waypoint_Y - player_Y) /length * (float) PlayerStats.player.db_player_Speed;
player_waypoint_X = input.getMouseX() - 2;
player_waypoint_Y = input.getMouseY() - 2;
}
For testing purposes the velocity's are defined in the init method along with length. Every time the right mouse is pressed the waypoints's X and Y are changed to the mouse location.
I learned this from this question
velocity calculation algorithm.
in order to make the bullets not all change direction every shot, create an array list so that each bullet fired has its own x and y velocity

Categories

Resources