I am going to try my best to give context for the below code. This is a method used to draw a circle and its center point in a 50x50 white square background. The following variables were used:
xc,yx - the center coordinates used to compute the circle
r - the radius of the circle
STEP - how often a new point is drawn on the circumference of the circle
x,y - the coordinates of each point that will make up the circle
Right now, my method uses a for loop to compute each points R,G, and B coordinates along the circumference of the circle based on the center point and the radius. What I am trying to do is anti-alias my output circle so that the round parts are not as jagged. However, I want to do this using only math and variables and I do not want to use any of Java's build in methods. Thank you to anyone who can help or point me in the right direction.
Below is my routine:
protected void proc_21() {
info = "Draw anti-aliased circle";
int xc = (int) rand(1, imgW - 2);
int yc = (int) rand(1, imgH - 2);
int r = (int) rand(4, 0.35f * (imgW + imgH));
int STEP = (2 * (int) Math.PI * r) * 57;
System.out.printf("circle centered at (%d,%d), radius = %d, draw in %d steps. \n", xc,yc,r,STEP);
for (int i = 0; i < STEP; i++) {
int x = (int) Math.round(xc + r * Math.cos(i));
int y = (int) Math.round(yc + r * Math.sin(i));
if (0 <= x && x < imgW) {
if ( 0 <= y && y < imgH) {
imgNew.setR(x, y, 0);
imgNew.setG(x, y, 0);
imgNew.setB(x, y, 1);
}
}
}
// set center to red
imgNew.setR(xc, yc, 1);
imgNew.setG(xc, yc, 0);
imgNew.setB(xc, yc, 0);
}
Related
I have to draw a ring using lines (drawLine) in Java that should look like the attached picture. We are provided with the classDrawingPanel that can be found here.
I've made a regular circle using lines, but I'm unsure how to get the ring shape. I'm new to programming and this is my first post, so apologies if I've missed something important.
This is my code so far:
public static int panelSize = 400;
public static void drawCircle()
{
double radius = 200;
int x2 = 200;
int y2 = 200;
DrawingPanel dp = new DrawingPanel(panelSize, panelSize);
dp.setBackground(Color.CYAN);
Graphics dpGraphics = dp.getGraphics();
dpGraphics.setColor(Color.RED);
for (int circle = 0; circle <= 360; circle++)
{
int x = (int)(x2 + Math.sin(circle * (Math.PI / 180)) * radius);
int y = (int)(y2 + Math.cos (circle * (Math.PI / 180)) * radius);
dpGraphics.drawLine(x, y, x2, y2);
}
}
This is what the final result should look like:
Such a figure can be drawn by drawing a line from one point to a point farther away on the circle, passing the starting point several times.
This is what I came up with:
// Radius
int radius = 200;
// center of the circle
int centerX = 300, centerY = 300;
// The number of edges. Set to 5 for a pentagram
int mod = 136;
// The number of "points" to skip - set to 2 for a pentagram
int skip = 45;
// Precalculated multipier for sin/cos
double multi = skip * 2.0 * Math.PI / mod;
// First point, calculated by hand
int x1 = centerX; // sin(0) = 0
int y1 = centerY + radius; // cos(0) == 1
for (int circle = 1; circle <= mod; circle++)
{
// Calculate the end point of the line.
int x2 = (int) (centerX + radius * Math.sin(circle * multi));
int y2 = (int) (centerY + radius * Math.cos(circle * multi));
dpGraphics.drawLine(x1, y1, x2, y2);
// Next start point for the line is the current end point
x1 = x2;
y1 = y2;
}
The result looks like this:
so, as an assaignment i want to make an analog clock, and i am pretty far. I only need the numbers around the clock, but i cant figure out how to make these. I have made some dots right now, but i want to replace theese with the numbers 1-12.. does any1 know an easy and fast way of doing this? my code is as following:
int cx, cy;
float secondsRadius;
float minutesRadius;
float hoursRadius;
float clockDiameter;
void setup() {
size(1366,768);
stroke(255);
float radius = min(width/1.2, height/1.2) / 2;
secondsRadius = radius * 0.72;
minutesRadius = radius * 0.60;
hoursRadius = radius * 0.50;
clockDiameter = radius * 1.8;
cx = width / 2;
cy = height / 2;
}
void draw() {
background(random(0,255),random(0,255),random(0,255));
// Draw the clock background
fill(0);
noStroke();
ellipse(cx, cy, clockDiameter, clockDiameter);
// Angles for sin() and cos() start at 3 o'clock;
// subtract HALF_PI to make them start at the top
float s = map(second(), 0, 60, 0, TWO_PI) - HALF_PI;
float m = map(minute() + norm(second(), 0, 60), 0, 60, 0, TWO_PI) - HALF_PI;
float h = map(hour() + norm(minute(), 0, 60), 0, 24, 0, TWO_PI * 2) - HALF_PI;
// Draw the hands of the clock
stroke(255);
strokeWeight(1);
line(cx, cy, cx + cos(s) * secondsRadius, cy + sin(s) * secondsRadius);
strokeWeight(2);
line(cx, cy, cx + cos(m) * minutesRadius, cy + sin(m) * minutesRadius);
strokeWeight(4);
line(cx, cy, cx + cos(h) * hoursRadius, cy + sin(h) * hoursRadius);
// Draw the dots arround the clock
strokeWeight(2);
beginShape(POINTS);
for (int a = 0; a < 360; a+=30) {
float angle = radians(a);
float x = cx + cos(angle) * secondsRadius;
float y = cy + sin(angle) * secondsRadius;
vertex(x, y);
}
endShape();
textSize(40);
text("Dank Clock", 570,40);
}
You already have a loop that goes around the clock and places dots at the clock positions. Now all you need is some logic that draws the hour at those positions.
Processing has a text() function that allows you to draw text (or numbers) to the screen. You can just call that instead of vertex() to draw the hours.
To get the hours to draw, just use an int variable that you increment each time through the loop. Something like this:
int hour = 3;
for (int a = 0; a < 360; a+=30) {
float angle = radians(a);
float x = cx + cos(angle) * secondsRadius;
float y = cy + sin(angle) * secondsRadius;
vertex(x, y);
fill(255);
text(hour, x, y);
hour++;
if(hour > 12){
hour = 1;
}
}
Notice that I'm starting at 3 because your angle starts at 0, which points all the way to the right. When the loop goes over the 12, I just start hour back over at 1.
You could probably also figure out a simple formula that maps from a to an hour, that way you don't have to do the incrementing yourself.
I'm making an application about space physics, so I do lots with orbits. Naturally, I encounter the Ellipse2D.Double to draw my orbits on the screen.
Whenever my JPanel refreshes, I draw the orbit of a body using an Ellipse2D, as well as the body itself with a different method.
Essentially, I discovered that when numbers get very large (whether it be the size of the orbits get large or the visualization is zoomed in very far), the position of the body and the Ellipse2D do not line up.
I calculate the position of the body using a conversion from polar coordinates to rectangular coordinates, and I leave the math for the Ellipse2D up to the geom package.
Take a look at this code sample. It's the most self-contained version of my problem that I can make, since scale of the circle has to be very large:
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Line2D;
import java.math.BigDecimal;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class EllipseDemo extends JPanel {
public static void main(String[] args) {
JFrame frame = new JFrame();
frame.setSize(500, 500);
frame.add(new EllipseDemo());
frame.setVisible(true);
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
// These values allow for a very zoomed in view of a piece of the circle
BigDecimal[] circleCenter = { new BigDecimal(-262842.5), new BigDecimal(-93212.8) };
BigDecimal circleRadius = new BigDecimal(279081.3);
// Draw the circle at the given center, with the given width and height
// x = centerx - radius, y = centery - radius, w = h = radius * 2
g2d.draw(new Ellipse2D.Double(circleCenter[0].subtract(circleRadius).doubleValue(),
circleCenter[1].subtract(circleRadius).doubleValue(), circleRadius.multiply(new BigDecimal(2)).doubleValue(),
circleRadius.multiply(new BigDecimal(2)).doubleValue()));
// Get a rectangular conversion of a point on the circle at this angle
BigDecimal angle = new BigDecimal(0.34117696217);
BigDecimal[] rectangular = convertPolarToRectangular(new BigDecimal[] {
circleRadius, angle });
// Draw a line from the center of the circle to the point
g2d.draw(new Line2D.Double(circleCenter[0].doubleValue(), circleCenter[1].doubleValue(),
circleCenter[0].add(rectangular[0]).doubleValue(), circleCenter[1]
.add(rectangular[1]).doubleValue()));
}
public BigDecimal[] convertPolarToRectangular(BigDecimal[] polar) {
BigDecimal radius = polar[0];
BigDecimal angle = polar[1];
BigDecimal x = radius.multiply(new BigDecimal(Math.cos(angle.doubleValue())));
BigDecimal y = radius.multiply(new BigDecimal(Math.sin(angle.doubleValue())));
return new BigDecimal[] { x, y };
}
}
The code above essentially draws a circle on the screen very far away with a large radius. I've picked the dimension so that a piece of the circle is visible in the small window.
Then it draws a line from the center of the circle to a point on the circle that's visible in the window: I picked an angle that was visible on the window and used geometry to convert that angle and the radius of the circle into rectangular coordinates.
This is what the program displays:
Notice that the line doesn't actually end up touching the ellipse. Now, I decided I had to find out whether it was the point I calculated or the ellipse that were incorrect. I did the math on my calculator, and found that the line was correct, and the ellipse incorrect:
Considering that the calculator is probably not wrong, I am led to believe the Ellipse2D is not drawing correctly. However, I tried many other angles, and this is the pattern I found:
And that leads me to believe the calculations are somehow wrong.
So that's my problem. Should I be using something other than Ellipse2D? Maybe Ellipse2D is not accurate enough? I used BigDecimals in my code sample because I thought it would give me more precision - is that the wrong approach? My ultimate goal is to be able to calculate the rectangular position of a point on an ellipse at a specific angle.
Thanks in advance.
You see this error because Ellipse2D is approximated by four cubic curves. To make sure just take a look at its path iterator defining shape border: http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/6-b14/java/awt/geom/EllipseIterator.java#187
To improve quality we should approximate ellipse by higher number of cubic curves. Here is an extention of standard java implementation with changeable number of segments:
class BetterEllipse extends Ellipse2D.Double {
private int segments;
public BetterEllipse(int segments, double x, double y, double w, double h) {
super(x, y, w, h);
this.segments = segments;
}
public int getSegments() {
return segments;
}
#Override
public PathIterator getPathIterator(final AffineTransform affine) {
return new PathIterator() {
private int index = 0;
#Override
public void next() {
index++;
}
#Override
public int getWindingRule() {
return WIND_NON_ZERO;
}
#Override
public boolean isDone() {
return index > getSegments() + 1;
}
#Override
public int currentSegment(double[] coords) {
int count = getSegments();
if (index > count)
return SEG_CLOSE;
BetterEllipse ellipse = BetterEllipse.this;
double x = ellipse.getCenterX() + Math.sin(2 * Math.PI * index / count) * ellipse.getWidth() / 2;
double y = ellipse.getCenterY() + Math.cos(2 * Math.PI * index / count) * ellipse.getHeight() / 2;
if (index == 0) {
coords[0] = x;
coords[1] = y;
if (affine != null)
affine.transform(coords, 0, coords, 0, 1);
return SEG_MOVETO;
}
double x0 = ellipse.getCenterX() + Math.sin(2 * Math.PI * (index - 2) / count) * ellipse.getWidth() / 2;
double y0 = ellipse.getCenterY() + Math.cos(2 * Math.PI * (index - 2) / count) * ellipse.getHeight() / 2;
double x1 = ellipse.getCenterX() + Math.sin(2 * Math.PI * (index - 1) / count) * ellipse.getWidth() / 2;
double y1 = ellipse.getCenterY() + Math.cos(2 * Math.PI * (index - 1) / count) * ellipse.getHeight() / 2;
double x2 = x;
double y2 = y;
double x3 = ellipse.getCenterX() + Math.sin(2 * Math.PI * (index + 1) / count) * ellipse.getWidth() / 2;
double y3 = ellipse.getCenterY() + Math.cos(2 * Math.PI * (index + 1) / count) * ellipse.getHeight() / 2;
double x1ctrl = x1 + (x2 - x0) / 6;
double y1ctrl = y1 + (y2 - y0) / 6;
double x2ctrl = x2 + (x1 - x3) / 6;
double y2ctrl = y2 + (y1 - y3) / 6;
coords[0] = x1ctrl;
coords[1] = y1ctrl;
coords[2] = x2ctrl;
coords[3] = y2ctrl;
coords[4] = x2;
coords[5] = y2;
if (affine != null)
affine.transform(coords, 0, coords, 0, 3);
return SEG_CUBICTO;
}
#Override
public int currentSegment(float[] coords) {
double[] temp = new double[6];
int ret = currentSegment(temp);
for (int i = 0; i < coords.length; i++)
coords[i] = (float)temp[i];
return ret;
}
};
}
}
And here is how you can use it in your code instead of standard one (I use 100 segments here):
g2d.draw(new BetterEllipse(100, circleCenter[0].subtract(circleRadius).doubleValue(),
circleCenter[1].subtract(circleRadius).doubleValue(), circleRadius.multiply(new BigDecimal(2)).doubleValue(),
circleRadius.multiply(new BigDecimal(2)).doubleValue()));
I have a diamond drawn on a panel and I'm trying to calculate if the mouse position is within its bounds. The problem is trying to the diamond, it needs four coordinates making up four lines to create the shape.
The easy thing to do is to create/fit a square within the bounds of the diamond, but I want to account for the remaining triangular areas outside. I initially thought I trying to calculate the slope between two points and figuring out if the x and y of the mouse intersects the line by adding the slope into the the equation, but it proved to be a lot more difficult when it comes to the lines that make up the TOP->RIGHT, RIGHT->BOTTOM and BOTTOM->LEFT given that the center of the diamond is not zero.
Is there an easier implementation to check if the mouse's x,y is within the bounds of the diamond?
In pseudo-code (to be more readable):
Point org = new Point(64, 32); // Center.
Point radii = new Point(32, 16); // Half the size.
Point mousePos = ...
mousePos -= org; // Relative to the center.
boolean inside = Math.abs(mousePos.x) * radii.y + Math.abs(mousePos.y) * radii.x
<= radii.x * radii.y;
Of math one knows (0, radii.y) and (radii.x, 0) determine the border line in the positive quadrant.
That comes down to the formula above.
static boolean isInsideDiamond(int x, int y, int[] xs, int[] ys) {
int minX = xs[0];
int maxX = minX;
int minY = ys[0];
int maxY = minY;
for (int i = 1; i < 4; ++i) {
minX = Math.min(minX, xs[i]);
maxX = Math.max(maxX, xs[i]);
minY = Math.min(minY, ys[i]);
maxY = Math.max(maxY, ys[i]);
}
int orgX = (minX + maxX) / 2;
int orgY = (minY + maxY) / 2;
int radX = (maxX - minX) / 2;
int radY = (maxY - minY) / 2;
return isInsideDiamond(x, y, orgX, orgY, radX, radY);
}
static boolean isInsideDiamond(int x, int y, int orgX, int orgY, int radX,
int radY) {
x -= orgX;
x = Math.abs(x);
y -= orgY;
y = Math.abs(y);
return x * radY + y * radX <= radX * radY;
}
By the way:
Polygon diamond = new Polygon(xs, ys, 4);
boolean inside = diamond.contains(x, y);
I'm trying to draw waves onto the sea, but I got no idea how to moves those from top to bottom. No matter what I change, they stay at the top of the picture.
Here's the code I'm using to draw the waves:
Dimension d = getSize();
int x, y, winWidth = d.width, winHeight = d.height;
int halfHeight = 10;
int lastX = 0, lastY = halfHeight;
double trajectory = 2 * Math.PI;
double factor = trajectory / 100;
for (x = 1; x <= winWidth; x++) {
double sine = Math.sin (x * factor) * halfHeight;
y = halfHeight - (int)sine;
graafika.drawLine (x, y, lastX, lastY);
lastX = x; lastY = y;
}
Here's a picture, how it looks at the moment.
Thanks for the help!
Use height?
graafika.drawLine (x, y+winHeight-20, lastX, lastY+winHeight-20);
You can just "mirror" the placement of the wave by replacing
y = halfHeight - (int)sine;
with
y = winHeight - (halfHeight - (int)sine);
This first places your sine wave at the very bottom, then moving it up a half sine wave, allowing the whole wave to be visible.