I wrote a little program which draws a circular chromatic gradation using Andres' algorithm.
Here is the result of an execution :
If the final-user gives the program a shift, in radians, it will use the latter to begin the drawing of the circle at this angle. Thus, the gradation will begin at this angle (and that's what I want).
For example, you can see that the gradation begins at the bottom of the circle and not at its left in the following image.
But as you can see, two problems appear :
First, there are some gaps in the circle ;
Then, the circumference of the circle is quite imprecise.
I think it's a problem of precision. So I asked myself if there were some bad casts but it's not the case.
Well, I don't know what is the reason why my circle is so imprecise. Could you help me please ?
Here is the class that draws each pixel of the circle. It takes on board the specified shift, in radians.
/**
* Constructs a Pixel taking account of a shift and near the position (x0 ; y0)
* #param x
* #param y
* #param color
* #param angle
* #param x0
* #param y0
*/
Pixel(double x, double y, Color color, double angle, double x0, double y0) {
this.x = (int) (x0 + (x-x0) * Math.cos(angle) - (y-y0) * Math.sin(angle));
this.y = (int) (y0 + (y-y0) * Math.cos(angle) + (x-x0) * Math.sin(angle));
this.color = color;
}
Now, the code that draws the circle, using Andres' algorithm and the Pixel class.
case "Andres' algorithm":
w = 2 * Math.PI;
for(double current_thickness = 0; current_thickness < this.thickness; current_thickness++) {
x = 0;
y = (int) (radius + current_thickness);
double d = radius + current_thickness - 1;
while (y >= x) {
double octant_1_x = x0 + x, octant_1_y = y0 + y;
double octant_2_x = x0 + y, octant_2_y = y0 + x;
double octant_3_x = x0 - x, octant_3_y = y0 + y;
double octant_4_x = x0 - y, octant_4_y = y0 + x;
double octant_5_x = x0 + x, octant_5_y = y0 - y;
double octant_6_x = x0 + y, octant_6_y = y0 - x;
double octant_7_x = x0 - x, octant_7_y = y0 - y;
double octant_8_x = x0 - y, octant_8_y = y0 - x;
max_counter++;
double[] rgb_gradation_octant_1 = PhotoRetouchingFormulas.chromatic_gradation(Math.atan2(octant_1_y - y0, octant_1_x - x0) + Math.PI, w);
updates.add(new Pixel(octant_1_x, octant_1_y,
Color.color(rgb_gradation_octant_1[0], rgb_gradation_octant_1[1], rgb_gradation_octant_1[2]),
circle_gradation_beginning, x0, y0)); // octant n°1
double[] rgb_gradation_octant_2 = PhotoRetouchingFormulas.chromatic_gradation(Math.atan2(octant_2_y - y0, octant_2_x - x0) + Math.PI, w);
updates.add(new Pixel(octant_2_x, octant_2_y,
Color.color(rgb_gradation_octant_2[0], rgb_gradation_octant_2[1], rgb_gradation_octant_2[2]),
circle_gradation_beginning, x0, y0));
double[] rgb_gradation_octant_3 = PhotoRetouchingFormulas.chromatic_gradation(Math.atan2(octant_3_y - y0, octant_3_x - x0) + Math.PI, w);
updates.add(new Pixel(octant_3_x, octant_3_y,
Color.color(rgb_gradation_octant_3[0], rgb_gradation_octant_3[1], rgb_gradation_octant_3[2]),
circle_gradation_beginning, x0, y0));
double[] rgb_gradation_octant_4 = PhotoRetouchingFormulas.chromatic_gradation(Math.atan2(octant_4_y - y0, octant_4_x - x0) + Math.PI, w);
updates.add(new Pixel(octant_4_x, octant_4_y,
Color.color(rgb_gradation_octant_4[0], rgb_gradation_octant_4[1], rgb_gradation_octant_4[2]),
circle_gradation_beginning, x0, y0)); // octant n°4
double[] rgb_gradation_octant_5 = PhotoRetouchingFormulas.chromatic_gradation(Math.atan2(octant_5_y-y0, octant_5_x-x0) + Math.PI, w);
updates.add(new Pixel(octant_5_x, octant_5_y,
Color.color(rgb_gradation_octant_5[0], rgb_gradation_octant_5[1], rgb_gradation_octant_5[2]),
circle_gradation_beginning, x0, y0)); // octant n°5
double[] rgb_gradation_octant_6 = PhotoRetouchingFormulas.chromatic_gradation(Math.atan2(octant_6_y-y0, octant_6_x-x0) + Math.PI, w);
updates.add(new Pixel(octant_6_x, octant_6_y,
Color.color(rgb_gradation_octant_6[0], rgb_gradation_octant_6[1], rgb_gradation_octant_6[2]),
circle_gradation_beginning, x0, y0));
double[] rgb_gradation_octant_7 = PhotoRetouchingFormulas.chromatic_gradation(Math.atan2(octant_7_y-y0, octant_7_x-x0) + Math.PI, w);
updates.add(new Pixel(octant_7_x, octant_7_y,
Color.color(rgb_gradation_octant_7[0], rgb_gradation_octant_7[1], rgb_gradation_octant_7[2]),
circle_gradation_beginning, x0, y0));
double[] rgb_gradation_octant_8 = PhotoRetouchingFormulas.chromatic_gradation(Math.atan2(octant_8_y-y0, octant_8_x-x0) + Math.PI, w);
updates.add(new Pixel(octant_8_x, octant_8_y,
Color.color(rgb_gradation_octant_8[0], rgb_gradation_octant_8[1], rgb_gradation_octant_8[2]),
circle_gradation_beginning, x0, y0)); // octant n°8
if (d >= 2 * x) {
d -= 2 * x + 1;
x++;
} else if (d < 2 * (radius + thickness - y)) {
d += 2 * y - 1;
y--;
} else {
d += 2 * (y - x - 1);
y--;
x++;
}
}
}
gui.getImageAnimation().setMax(max_counter*8);
break;
you don't have to 'rotate' the pixel, you just have to draw them
Pixel(double x, double y, Color color, double angle, double x0, double y0) {
this.x = (int) (x + x0);
this.y = (int) (y + y0);
this.color = getColorForAngle(angle);
}
Bresenham / Andres provide all pixels of the circle, no need to translate/rotate them
Related
I'm trying to draw Pie chart with PDFbox, but there are white lines between the slices, could anyone help me with this? is there an option for this?
Attached the code for drawing the arc that I'm using:
while (start < stop) {
List<Float> smallArc = PdfUtils.createSmallArc(a, b, radius, start, start +
2.0944 > stop ? stop : start + 2.0944);
contentStream.saveGraphicsState();
contentStream.setNonStrokingColor(components[0], components[1],
components[2]);
contentStream.moveTo(smallArc.get(0), smallArc.get(1));
contentStream.curveTo(smallArc.get(2), smallArc.get(3), smallArc.get(4),
smallArc.get(5), smallArc.get(6), smallArc.get(7));
contentStream.fill();
contentStream.restoreGraphicsState();
start += 2.0944;
}
public static List<Float> createSmallArc(float x, float y, double r, double a1, double a2) {
double a = (a2 - a1) / 2;
double x4 = r * Math.cos(a);
double y4 = r * Math.sin(a);
double x1 = x4;
double y1 = -y4;
double q1 = x1 * x1 + y1 * y1;
double q2 = q1 + x1 * x4 + y1 * y4;
double k2 = 4 / 3d * (Math.sqrt(2 * q1 * q2) - q2) / (x1 * y4 - y1 * x4);
double x2 = x1 - k2 * y1;
double y2 = y1 + k2 * x1;
double x3 = x2;
double y3 = -y2;
double ar = a + a1;
double cos_ar = Math.cos(ar);
double sin_ar = Math.sin(ar);
List<Float> list = new ArrayList<Float>();
list.add((float) (r * Math.cos(a1)) + x);
list.add((float) -(r * Math.sin(a1)) + y);
list.add((float) (x2 * cos_ar - y2 * sin_ar) + x);
list.add((float) -(x2 * sin_ar + y2 * cos_ar) + y);
list.add((float) (x3 * cos_ar - y3 * sin_ar) + x);
list.add((float) -(x3 * sin_ar + y3 * cos_ar) + y);
list.add((float) (r * Math.cos(a2)) + x);
list.add((float) -(r * Math.sin(a2)) + y);
return list;
}
Attached image of the result:
Thanks
Instead of using fill(), use the fillAndStroke() method and call setStrokingColor() with the same parameters that you used for setNonStrokingColor().
I am experimenting with libgdx trying to make my first simple game. I have managed to make a line rotate.
What I haven't accomplished is adding a perpendicular line, and making it a cross and then rotating this shape.
public void render(ShapeRenderer renderer) {
renderer.set(ShapeType.Line);
renderer.setColor(COLOR);
/**
* finding the angle
*/
float elapsedNanoseconds = TimeUtils.nanoTime() - initialTime;
float elapsedSeconds = MathUtils.nanoToSec * elapsedNanoseconds;
float elapsedPeriods = elapsedSeconds / 2.0f;
float cyclePosition = elapsedPeriods % 1;
float x = WORLD_SIZE / 2 + radius * MathUtils.cos(MathUtils.PI2 * cyclePosition);
float y = WORLD_SIZE / 2 + radius * MathUtils.sin(MathUtils.PI2 * cyclePosition);
//line rotates and moves at the same time.
renderer.line(position.x - x, position.y + y, position.x + x, position.y - y);
}
position is a Vector2() class object which is updated every delta seconds and holds the current center of the line.
I managed to find the solution. I will leave it here in case someone is having similar issues:
public void render(ShapeRenderer renderer) {
/**
* find the angle
*/
float elapsedNanoseconds = TimeUtils.nanoTime() - initialTime;
float elapsedSeconds = MathUtils.nanoToSec * elapsedNanoseconds;
float elapsedPeriods = elapsedSeconds / 2.0f;
float cyclePosition = elapsedPeriods % 1;
float x = WORLD_SIZE / 2 + radius * MathUtils.cos(MathUtils.PI2 * cyclePosition);
float y = WORLD_SIZE / 2 + radius * MathUtils.sin(MathUtils.PI2 * cyclePosition);
float x2 = WORLD_SIZE / 2 + radius * MathUtils.sin(MathUtils.PI2 * -cyclePosition);
float y2 = WORLD_SIZE / 2 + radius * MathUtils.cos(MathUtils.PI2 * -cyclePosition);
renderer.set(ShapeType.Line);
renderer.setColor(COLOR);
renderer.line(position.x - x, position.y + y, position.x + x, position.y - y);
renderer.line(position.x - x2, position.y + y2, position.x + x2, position.y - y2);
}
I am drawing a triangle on canvas:
float x = 540;
float y = 960;
Path path = new Path();
path.moveTo(x, y);
path.lineTo(x+18, y+60);
path.lineTo(x-18, y+60);
path.lineTo(x, y);
canvas.drawPath(path, mPaint);
This canvas has another objects. But I need to rotate only this triangle around center of this triangle on a random angle (between 0 and 360). How to implement it? How to get coordinates vertexes of a triangle after rotation?
Its simple math, if the center of the triangle is (x,y) and the center-vertex distace is A, the three vertex will be
-(A*Math.cos(angle),A*Math.sin(angle))
-(A*Math.cos(angle+2*Math.PI/3),A*Math.sin(angle+2*Math.PI/3))
-(A*Math.cos(angle-2*Math.PI/3),A*Math.sin(angle-2*Math.PI/3))
I solve it by this way:
float angle = (float) Math.toRadians(90); // Angle to rotate
// Size of triangle
final float height = 60;
final float width = 36;
// Display coordinates where triangle will be drawn
float centerX = 540;
float centerY = 960;
// Vertex's coordinates before rotating
float x1 = centerX;
float y1 = centerY - height / 2;
float x2 = centerX + width / 2;
float y2 = centerY + height / 2;
float x3 = centerX - width / 2;
float y3 = y2;
// Rotating
float x1r = (float) ((x1 - centerX) * Math.cos(angle) - (y1 - centerY) * Math.sin(angle) + centerX);
float y1r = (float) ((x1 - centerX) * Math.sin(angle) + (y1 - centerY) * Math.cos(angle) + centerY);
float x2r = (float) ((x2 - centerX) * Math.cos(angle) - (y2 - centerY) * Math.sin(angle) + centerX);
float y2r = (float) ((x2 - centerX) * Math.sin(angle) + (y2 - centerY) * Math.cos(angle) + centerY);
float x3r = (float) ((x3 - centerX) * Math.cos(angle) - (y3 - centerY) * Math.sin(angle) + centerX);
float y3r = (float) ((x3 - centerX) * Math.sin(angle) + (y3 - centerY) * Math.cos(angle) + centerY);
// Drawing
Path path = new Path();
path.moveTo(x1r, y1r);
path.lineTo(x2r, y2r);
path.lineTo(x3r, y3r);
path.lineTo(x1r, y1r);
canvas.drawPath(path, mPaint);
Thanks to Gimka and Nofate for help.
My program contains main shapes i.e square, rect, circle they work good ( for x2,y2 in those shapes I used absolute values e.g square( x, y, 5, 5) ), but when working with triangle shape,
Triangles in my program glitches.
below is the code of my program modules,
if (vehicleStyle.getVehicleShape().equals(VehicleShape.TRIANGLE)) {
processingVisualizer
.fill(vehicleStyle.getColor().red,
vehicleStyle.getColor().green,
vehicleStyle.getColor().blue);
processingVisualizer.strokeWeight(1 * vehicleSize);
//System.out.println(x + "-" + y);
//## to place face toward movement direction
/*
* -----<|----
* | |
* \/ /\
* | |
* -----|>----
*/
float x2, y2, x3, y3;
if (x == 100) {
System.out.println( "x==100");
x2 = x - 5;
y2 = y + 5;
x3 = x + 5;
y3 = y + 5;
processingVisualizer.triangle(x, y, x2, y2, x3, y3);
} else if (x == 20) {
System.out.println( "x==20");
x2 = x - 2;
y2 = y - 2;
x3 = x + 2;
y3 = y - 2;
processingVisualizer.triangle(x, y, x2, y2, x3, y3);
} else if (y == 100) {
System.out.println( "y ==100");
x2 = x - 2;
y2 = y - 2;
x3 = x - 2;
y3 = y + 2;
processingVisualizer.triangle(x, y, x2, y2, x3, y3);
} else if (y == 20) {
System.out.println( "y ==20");
x2 = x+5;//x - 2;
y2 = y-5;
x3 = x+5 ;//- 2;
y3 = y+5;
processingVisualizer.triangle(x, y, x2, y2, x3, y3);
}
}
processingVisualizer.strokeWeight(1);
}
Can't test your code, but my assumption is the "glitch" happens because you are rendering a triangle only when your 4 conditions are met. You should update the triangle's corner positions based on your conditions if you like, but render the triangle all time, even when the positions are out of date:
if (vehicleStyle.getVehicleShape().equals(VehicleShape.TRIANGLE)) {
processingVisualizer
.fill(vehicleStyle.getColor().red,
vehicleStyle.getColor().green,
vehicleStyle.getColor().blue);
processingVisualizer.strokeWeight(1 * vehicleSize);
//System.out.println(x + "-" + y);
//## to place face toward movement direction
/*
* -----<|----
* | |
* \/ /\
* | |
* -----|>----
*/
float x2, y2, x3, y3;
if (x == 100) {
System.out.println( "x==100");
x2 = x - 5;
y2 = y + 5;
x3 = x + 5;
y3 = y + 5;
} else if (x == 20) {
System.out.println( "x==20");
x2 = x - 2;
y2 = y - 2;
x3 = x + 2;
y3 = y - 2;
} else if (y == 100) {
System.out.println( "y ==100");
x2 = x - 2;
y2 = y - 2;
x3 = x - 2;
y3 = y + 2;
} else if (y == 20) {
System.out.println( "y ==20");
x2 = x+5;//x - 2;
y2 = y-5;
x3 = x+5 ;//- 2;
y3 = y+5;
}
processingVisualizer.triangle(x, y, x2, y2, x3, y3);
}
processingVisualizer.strokeWeight(1);
}
Also, you can easily compute the direction of motion, if you store the previous position, using the arc tangent function (atan2()):
float angle = atan2(currentY-previousY,currentX-previousX);
Here's a quick example:
float cx,cy,px,py;//current x,y, previous x,y
float len = 15;
void setup(){
size(200,200);
background(255);
}
void draw(){
//update position - chase mouse with a bit of easing
cx -= (cx - mouseX) * .035;
cy -= (cy - mouseY) * .035;
//find direction of movement based on the current position
float angle = atan2(cy-py,cx-px);
//store previous position
px = cx;
py = cy;
//render
fill(255,10);noStroke();
rect(0,0,width,height);
fill(127,32);stroke(0);
pushMatrix();
translate(cx,cy);
pushMatrix();
rotate(angle);
triangle(len,0,-len,-len,-len,len);
line(0,0,len,0);
popMatrix();
popMatrix();
}
Can anyone guide me how to code the arrow line in different direction.
wa and wl is positive the rectangle will be on top of the x-axis. Below example shown if wl is negative and wa is positive. The code below shown how i code the rectangle shape. x1 is the varaible to state where to start at x axis. e1 is the length of the shape, wa1 and wl1 is the height. wsign to determine the height wa1 or wl1 should display at negative side or positive side.
if (Math.abs(wl1) > Math.abs(wa1)) {
y_scale = (load_y0 - 40) / (double) Math.abs(wl1);
} else {
y_scale = (load_y0 - 40) / (double) Math.abs(wa1);
}
g.drawLine((int) ((double) x0 + x1 * x_scale), (int) (load_y),
(int) ((double) x0 + x1 * x_scale),
(int) (load_y + (wa1 * y_scale) * -1));
g.drawLine((int) ((double) x0 + (x1 + e1) * x_scale),
(int) (load_y), (int) ((double) x0 + (x1 + e1)
* x_scale), (int) (load_y + (wl1 * y_scale)
* -1));
g.drawLine((int) ((double) x0 + x1 * x_scale),
(int) (load_y + (wa1 * y_scale * -1)),
(int) ((double) x0 + (x1 + e1) * x_scale),
(int) (load_y + (wl1 * y_scale) * -1));
Here is a simple routine (adopted from here) for drawing arbitrary arrows:
import static java.awt.geom.AffineTransform.*;
import java.awt.*;
import java.awt.geom.AffineTransform;
import javax.swing.*;
public class Main {
public static void main(String args[]) {
JFrame t = new JFrame();
t.add(new JComponent() {
private final int ARR_SIZE = 4;
void drawArrow(Graphics g1, int x1, int y1, int x2, int y2) {
Graphics2D g = (Graphics2D) g1.create();
double dx = x2 - x1, dy = y2 - y1;
double angle = Math.atan2(dy, dx);
int len = (int) Math.sqrt(dx*dx + dy*dy);
AffineTransform at = AffineTransform.getTranslateInstance(x1, y1);
at.concatenate(AffineTransform.getRotateInstance(angle));
g.transform(at);
// Draw horizontal arrow starting in (0, 0)
g.drawLine(0, 0, len, 0);
g.fillPolygon(new int[] {len, len-ARR_SIZE, len-ARR_SIZE, len},
new int[] {0, -ARR_SIZE, ARR_SIZE, 0}, 4);
}
public void paintComponent(Graphics g) {
for (int x = 15; x < 200; x += 16)
drawArrow(g, x, x, x, 150);
drawArrow(g, 30, 300, 300, 190);
}
});
t.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
t.setSize(400, 400);
t.setVisible(true);
}
}
Result:
Simple arrow sample
/**
* #param fromPoint end of the arrow
* #param rotationDeg rotation angle of line
* #param length arrow length
* #param wingsAngleDeg wingspan of arrow
* #return Path2D arrow shape
*/
public static Path2D createArrowForLine(
Point2D fromPoint,
double rotationDeg,
double length,
double wingsAngleDeg) {
double ax = fromPoint.getX();
double ay = fromPoint.getY();
double radB = Math.toRadians(-rotationDeg + wingsAngleDeg);
double radC = Math.toRadians(-rotationDeg - wingsAngleDeg);
Path2D resultPath = new Path2D.Double();
resultPath.moveTo(length * Math.cos(radB) + ax, length * Math.sin(radB) + ay);
resultPath.lineTo(ax, ay);
resultPath.lineTo(length * Math.cos(radC) + ax, length * Math.sin(radC) + ay);
return resultPath;
}