Related
I'm using the lerp() function to move my circle across a line, but it isn't working. The circle always ends up on the line somewhere depending on what my amt parameter is for the lerp() function. If I put 0.5 for the amt then the circle is placed half way down the line, but I can't see it move nor does the circle finish moving down the length of the circle. So can anyone help me make the circle move down the line?
float x1,y1,x2,y2;
float cx,cy;
float x4,y4;
void setup() {
size(600,600);
x1 = 200;
y1 = 150;
x2 = 300;
y2 = 250;
cx = 450;
cy = 200;
}
void draw() {
background(60);
stroke(220);
line(x1,y1,x2,y2);
noFill();
noStroke();
// calculate the point
float k = ((y2-y1) * (cx-x1) - (x2-x1) * (cy-y1))
/ ((y2-y1)*(y2-y1) + (x2-x1)*(x2-x1));
float x4 = cx - k * (y2-y1);
float y4 = cy + k * (x2-x1);
stroke(0);
line(cx,cy,x4,y4); //line connecting circle and point on line
float x = lerp(cx, x4, .1);
float y = lerp(cy, y4, .1);
fill(255, 0, 175);
ellipse(x4,y4, 8,8);
fill(175, 0, 255);
ellipse(x, y, 50, 50);
}
You need to use a variable for the amount value passed into the lerp() function. Then just increase that variable over time to animate:
float amount = 0;
float speed = .001;
void setup() {
size(500, 500);
}
void draw() {
float startX = 0;
float startY = 0;
float endX = width;
float endY = height;
float currentX = lerp(startX, endX, amount);
float currentY = lerp(startY, endY, amount);
background(0);
ellipse(currentX, currentY, 20, 20);
amount += speed;
}
I've implemented Bresenham Circle drawing algorithm in Java. But the output is not correctly drawn! I can't find where the problem is.
My code and output image is given below. Any help is appreciated.
public void display(GLAutoDrawable drawable) {
final GL2 gl = drawable.getGL().getGL2();
gl.glBegin (GL2.GL_POINTS);
double radius = 0.6;//sc.nextDouble();
double x =0.0;
double y = radius;
gl.glVertex2d(0.0,0.0);
gl.glVertex2d(x,y);
gl.glVertex2d(-x,y);
gl.glVertex2d(x,-y);
gl.glVertex2d(-x,-y);
gl.glVertex2d(y,x);
gl.glVertex2d(-y,x);
gl.glVertex2d(y,-x);
gl.glVertex2d(-y,-x);
double d = 5 - 4*radius;
while(x<y){
if(d<0){ //dE
x+=.01;
d+=(2*x + 3)*4;
}else{
x+=.01;
y-=.01;
d+=(2*x - 2*y +5)*4;
}
gl.glVertex2d(x,y);
gl.glVertex2d(-x,y);
gl.glVertex2d(x,-y);
gl.glVertex2d(-x,-y);
gl.glVertex2d(y,x);
gl.glVertex2d(-y,x);
gl.glVertex2d(y,-x);
gl.glVertex2d(-y,-x);
}
gl.glEnd();
}
Notice that the original Bresenham's circle algorithm works only with integers. Since your update is x_{n+1}=x_n+eps you can modify your y update to
y_{n+1}^2 = y_n^2 - 2*eps*n-eps*eps
The derivation is the same as the one given at the wiki page.
public void display(GLAutoDrawable drawable) {
final GL2 gl = drawable.getGL().getGL2();
gl.glBegin (GL2.GL_POINTS);
double radius = 0.6;//sc.nextDouble();
double x =0.0;
double y = radius;
gl.glVertex2d(0.0,0.0);
gl.glVertex2d(x,y);
gl.glVertex2d(-x,y);
gl.glVertex2d(x,-y);
gl.glVertex2d(-x,-y);
gl.glVertex2d(y,x);
gl.glVertex2d(-y,x);
gl.glVertex2d(y,-x);
gl.glVertex2d(-y,-x);
double eps = .01;
double eps2 = eps*eps;
while(x<y){
y = Math.sqrt(y*y-2*eps*x-eps2);
x+= eps;
gl.glVertex2d(x,y);
gl.glVertex2d(-x,y);
gl.glVertex2d(x,-y);
gl.glVertex2d(-x,-y);
gl.glVertex2d(y,x);
gl.glVertex2d(-y,x);
gl.glVertex2d(y,-x);
gl.glVertex2d(-y,-x);
}
gl.glEnd();
}
Result:
Be also careful of the aspect ratio. This algorithm works properly for ratio 1:1. If, however, your aspect ratio is a:b your equation for the circle would become x^2/a^2+y^2/b^2=r^2. You can change the update accordingly.
Here is example with drawing your custom circle by using Bresenham's algorithm.
Full code can be found in my repo : https://github.com/Maiakov/algorithms/tree/master/Task40
/**
* Write a routine to draw a circle (x ** 2 + y ** 2 = r ** 2) without making use of any floating point
* <p>
* computations at all.
*/
public class DrawCircleAlgorithm {
public static void drawCircle(int radius, int centerX, int centerY, Graphics g) {
int y = radius;
int x = 0;
int delta = calculateStartDelta(radius);
while (y >= x) {
drawPixelAndReflect(centerX, centerY, x, y, g);
if (delta < 0) {
delta = calculateDeltaForHorizontalPixel(delta, x);
} else {
delta = calculateDeltaForDiagonalPixel(delta, x, y);
y--;
}
x++;
}
}
private static int calculateStartDelta(int radius) {
return 3 - 2 * radius;
}
private static int calculateDeltaForHorizontalPixel(int oldDelta, int x) {
return oldDelta + 4 * x + 6;
}
private static int calculateDeltaForDiagonalPixel(int oldDelta, int x, int y) {
return oldDelta + 4 * (x - y) + 10;
}
private static void drawPixelAndReflect(int centerX, int centerY, int x, int y, Graphics g) {
g.drawLine(centerX + x, centerY + y, centerX + x, centerY + y);
g.drawLine(centerX + x, centerY - y, centerX + x, centerY - y);
g.drawLine(centerX - x, centerY + y, centerX - x, centerY + y);
g.drawLine(centerX - x, centerY - y, centerX - x, centerY - y);
g.drawLine(centerX - y, centerY + x, centerX - y, centerY + x);
g.drawLine(centerX - y, centerY - x, centerX - y, centerY - x);
g.drawLine(centerX + y, centerY + x, centerX + y, centerY + x);
g.drawLine(centerX + y, centerY - x, centerX + y, centerY - x);
}
}
Try this. I don't have java on the computer I am on right now, so let's see if it works. Make sure to work with integers since what you are normalizing are the block sizes!
Edit: Added integers.
public void display(GLAutoDrawable drawable) {
final GL2 gl = drawable.getGL().getGL2();
gl.glBegin (GL2.GL_POINTS);
double radius = 0.6;//sc.nextDouble();
double x =0.0;
double y = radius;
gl.glVertex2d(0.0,0.0);
gl.glVertex2d(x,y);
gl.glVertex2d(-x,y);
gl.glVertex2d(x,-y);
gl.glVertex2d(-x,-y);
gl.glVertex2d(y,x);
gl.glVertex2d(-y,x);
gl.glVertex2d(y,-x);
gl.glVertex2d(-y,-x);
double d = 3 - 2*radius;
while(x<y){
x++;
if(d<0){ //dE
d= d + 4x + 6;
}else{
y--;
d= d+ 4(x - y) + 10;
}
gl.glVertex2d(x,y);
gl.glVertex2d(-x,y);
gl.glVertex2d(x,-y);
gl.glVertex2d(-x,-y);
gl.glVertex2d(y,x);
gl.glVertex2d(-y,x);
gl.glVertex2d(y,-x);
gl.glVertex2d(-y,-x);
}
gl.glEnd();
}
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.
Hey i implement code to rotate AND flip image.
Left, Right, Upsidedown rotation works.
Fliping Horizontal, Vertical works.
But they dont work together
When i flip image and then i rotate, it disappears.
BUT
When i flip and flip image (so it is as it was before flip) i can rotate normally.
I try to understand what is wrong and i think the problem is with transform or scale.
Do you have any idea how to fix this code ?
/**
* Paint the icons of this compound icon at the specified location
*
* #param c
* The component on which the icon is painted
* #param g
* the graphics context
* #param x
* the X coordinate of the icon's top-left corner
* #param y
* the Y coordinate of the icon's top-left corner
*/
#Override
public void paintIcon(Component c, Graphics g, int x, int y) {
Graphics2D g2 = (Graphics2D) g.create();
AffineTransform af = g2.getTransform();
int cWidth = icon.getIconWidth() / 2;
int cHeight = icon.getIconHeight() / 2;
int xAdjustment = (icon.getIconWidth() % 2) == 0 ? 0 : -1;
int yAdjustment = (icon.getIconHeight() % 2) == 0 ? 0 : -1;
if (rotate == Rotate.DOWN) {
g2.translate(x + cHeight, y + cWidth);
g2.rotate(Math.toRadians(90));
icon.paintIcon(c, g2, -cWidth, yAdjustment - cHeight);
} else if (rotate == Rotate.UP) {
g2.translate(x + cHeight, y + cWidth);
g2.rotate(Math.toRadians(-90));
icon.paintIcon(c, g2, xAdjustment - cWidth, -cHeight);
} else if (rotate == Rotate.UPSIDE_DOWN) {
g2.translate(x + cWidth, y + cHeight);
g2.rotate(Math.toRadians(180));
icon.paintIcon(c, g2, xAdjustment - cWidth, yAdjustment - cHeight);
} else if (rotate == Rotate.VERTICAL) {
g2.translate(0, getIconHeight());
g2.scale(1, -1);
icon.paintIcon(c, g2, x, y);
vert = !vert; //boolean flag
} else if (rotate == Rotate.HORIZONTAL) {
g2.translate(getIconWidth(), 0);
g2.scale(-1, 1);
icon.paintIcon(c, g2, x, y);
hor = !hor; //boolean flag
} else if (rotate == Rotate.VERTICALLY_HORIZONTAL) {
g2.translate(getIconWidth(), getIconHeight());
g2.scale(-1, -1);
icon.paintIcon(c, g2, x, y);
hor = !hor;
vert = !vert;
} else if (rotate == Rotate.CENTER) {
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
AffineTransform original = g2.getTransform();
AffineTransform at = new AffineTransform();
at.concatenate(original);
at.translate((getIconWidth() - icon.getIconWidth()) / 2,
(getIconHeight() - icon.getIconHeight()) / 2);
at.rotate(Math.toRadians(angle), x + cWidth, y + cHeight);
g2.setTransform(at);
icon.paintIcon(c, g2, x, y);
g2.setTransform(original);
}
}
If you want them to work together you've got to save the image you've made with your transform. Here is an example
Image orig = ...;
Image transformedCopy = ...;
AffineTransformation at = ...;
transformedCopy.getGraphics().setTransform(at).drawImage(orig);
//transformedCopy will now have a copy of the transformed image
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