I have this problem where I have to draw a recursive snow flake such that the flake has 6 lines coming from a center point, each 60 degrees apart from one another, then the endpoint of each of those lines, is the center point of another flake. Each time it branches off to another flake, the length is cut in half. The length is defined as just one side of the flake. This recursion continues to happen until the length is down to a distance such as 20 pixels where you can no longer distinguish the different flakes.
I understand that recursion is just a method that calls itself, but I'm just having difficulty setting up this problem. What I've got so far is the math to draw the initial flake but I just don't know how to set up the recursion to draw the flakes.
Here's what I have so far.
import java.awt.*;
import java.util.Random;
import javax.swing.*;
public class SnowFlake extends JPanel {
private MainWindow panel;
private int x1OfFlake = 400;
private int y1OfFlake = 400;
private int maxLength = 200; // this is length of, 1 0f 6 branches for each individual flake
public SnowFlake() {
// generateRandCoodinatesLength();
}
public void generateRandCoodinatesLength() {
Random rn = new Random();
maxLength = rn.nextInt(200) + 100;
x1OfFlake = rn.nextInt(700) + 100;
y1OfFlake = rn.nextInt(700) + 100;
}
public void paint(Graphics g) {
drawFlake(levels, x1OfFlake, y1OfFlake, g); // x1, y1, x2, y2
}
public void drawFlake(int level, int x1, int y1, Graphics g){
//below was just how I made sure my picture was correct
/*
g.drawLine(x1, y1, 600, 400); //1
g.drawLine(x1, y1, 500, 227); //2
g.drawLine(x1, y1, 300, 227); //3
g.drawLine(x1, y1, 200, 400); //4
g.drawLine(x1, y1, 300, 573); //5
g.drawLine(x1, y1, 500, 573); //6
*/
g.drawLine(x1, y1, x1 + maxLength, y1); // 1
g.drawLine(x1, y1, x1 + (maxLength / 2), y1 - ((int) ((maxLength / 2) * Math.sqrt(3)))); // 2
g.drawLine(x1, y1, x1 - (maxLength / 2), y1 - ((int) ((maxLength / 2) * Math.sqrt(3)))); // 3
g.drawLine(x1, y1, x1 - maxLength, y1); // 4
g.drawLine(x1, y1, x1 - (maxLength / 2), y1 + ((int) ((maxLength / 2) * Math.sqrt(3)))); // 5
g.drawLine(x1, y1, x1 + (maxLength / 2), y1 + ((int) ((maxLength / 2) * Math.sqrt(3)))); // 6
}
}
This is what the end result should kind of look like, except with more branches drawn.
picture
This could bring you closer to what you want
public void drawFlake(int level, float angleDegrees, Graphics g) {
/*
* Exit condition
* If the max number of levels has been reached,
* or the maxLength is no longer visible when drawn
*/
if (level >= MAX_LEVEL || maxLength == 0) {
return;
}
/*
* Secondary condition, increment the level if we've gone around the
* circle once
*/
if (angleDegrees >= 360) {
maxLength *= .9;
drawFlake(level + 1, 0, g);
return;
}
g.drawLine(
centerX,
centerY,
centerX + (int) (maxLength * Math.sin(Math.toRadians(angleDegrees))),
centerY + (int) (maxLength * Math.cos(Math.toRadians(angleDegrees))));
int currentLevelAngleIncrement = 60 / (level + 1);
drawFlake(level, angleDegrees + currentLevelAngleIncrement, g);
}
Ends up something like this..
Not sure what level is - doesn't appear to be used in the drawFlake method...
I would change your recursive function to take the x and y coordinates and the length of the current line:
public void drawFlake(int x1, int y1, int length, Graphics g) {
// draw the current flake - exactly the same as your method except that it
// uses the length passed not the maximum length
g.drawLine(x1, y1, x1 + length, y1); // 1
g.drawLine(x1, y1, x1 + (length / 2), y1 - ((int) ((length / 2) * Math.sqrt(3)))); // 2
g.drawLine(x1, y1, x1 - (length / 2), y1 - ((int) ((length / 2) * Math.sqrt(3)))); // 3
g.drawLine(x1, y1, x1 - length, y1); // 4
g.drawLine(x1, y1, x1 - (length / 2), y1 + ((int) ((length / 2) * Math.sqrt(3)))); // 5
g.drawLine(x1, y1, x1 + (length / 2), y1 + ((int) ((length / 2) * Math.sqrt(3)))); // 6
// recursion
int newLength = length / 2;
if (newLength >= MIN_LENGTH) {
// this is the recursive bit - call the function again for each of the end points
drawFlake(x1 + length, y1, newLength, g);
drawFlake(x1 + (length / 2), y1 - ((int) ((length / 2) * Math.sqrt(3))), newLength, g);
drawFlake(x1 - (length / 2), y1 - ((int) ((length / 2) * Math.sqrt(3))), newLength, g);
drawFlake(x1 - length, y1, newLength, g);
drawFlake(x1 - (length / 2), y1 + ((int) ((length / 2) * Math.sqrt(3))), newLength, g);
drawFlake(x1 + (length / 2), y1 + ((int) ((length / 2) * Math.sqrt(3))), newLength, g);
}
}
Then just kick it off with the start values:
public void paint(Graphics g) {
drawFlake(x1OfFlake, y1OfFlake, maxLength, g);
}
Note that the coordinates passed to the recursive drawFlake methods are the same as the end points of the drawLine calls, so you could calculate these once and reuse them.
Also, for readability (and maybe performance), I'd just calculate (length / 2) * Math.sqrt(3) once at the start of each call.
Haven't run it, so may be some typos, but should get you on the right lines.
Thanks for your help, the code below helped a lot with the recursion. It makes more sense now that I have to pass those variables into the method so they are actually changing. Also with this, I noticed that if you only divide the length by two it just forms a hexagonal shape, filled with triangles. So messing around with that and the minimum length solved that problem.
public void drawFlake(int x1, int y1, int length, Graphics g){
g.drawLine(x1, y1, x1 + length, y1); // 1
g.drawLine(x1, y1, x1 + (length / 2), y1 - ((int) ((length / 2) * Math.sqrt(3)))); // 2
g.drawLine(x1, y1, x1 - (length / 2), y1 - ((int) ((length / 2) * Math.sqrt(3)))); // 3
g.drawLine(x1, y1, x1 - length, y1); // 4
g.drawLine(x1, y1, x1 - (length / 2), y1 + ((int) ((length / 2) * Math.sqrt(3)))); // 5
g.drawLine(x1, y1, x1 + (length / 2), y1 + ((int) ((length / 2) * Math.sqrt(3)))); // 6
//recursion
int newLength = length/3; //changed this to 3 to make it more look more like a flake
if(newLength >= minLength){
drawFlake(x1 + length, y1, newLength, g);
drawFlake(x1 + (length / 2), y1 - ((int) ((length / 2) * Math.sqrt(3))), newLength, g);
drawFlake(x1 - (length / 2), y1 - ((int) ((length / 2) * Math.sqrt(3))), newLength, g);
drawFlake(x1 - length, y1, newLength, g);
drawFlake(x1 - (length / 2), y1 + ((int) ((length / 2) * Math.sqrt(3))), newLength, g);
drawFlake(x1 + (length / 2), y1 + ((int) ((length / 2) * Math.sqrt(3))), newLength, g);
}
Related
I am trying to draw an arc on Jpanel in swing from user input having the center of arc, starting point and end point of arc.
here is my current
int x1 = 300; //start point
int y1 = 300;
int x2 = 350; //center point of arc
int y2 = 350;
int x3 = 300; //end point of arc
int y3 = 400;
int h1 = y1 - y2; //calculate with and height from start-center and center-end
int d1 = x2 - x1;
int h2 = y2 - y3;
int d2 = x3 - x2;
int startangle = (int)(Math.atan(h1 / d1) * 180 / Math.PI);
if (x2 > x1 && y2 > y1) {
startangle = 180 - startangle;
} else if (x2 < x1) {
//change sign
} else if (y1 < y2) {
//change sign
}
System.out.println("x1,y1\n" + x1 + "\n" + y1 + "\n" + d2 / h2 + "\n" + Math.atan(d2 / h2) * 180 / Math.PI);
int endangle = (int)(Math.atan2(x3, y3) * 180 / Math.PI);
System.out.println("args: " + "\n" + x2 + "\n" + y2 + "\n" + startangle + "\n" + endangle + "\n");
g2.drawArc(x1, y1, d1, h1, startangle, startangle);
g2.drawArc(x2, y2, d2, h2, 0, endangle);
However i am not getting the arc on screen, literally nothing related to it (other shapes work but not this one). No errors or exceptions were thrown.
Edit: Thanks to #MadProgrammer's comment, i am getting a shape but not what i expect.
What i get:
What i expect from the same set of coordinates:
Edit 2: managed to make it work by using a bezier curve instead of an arc
It worked by using a bezier curve and drawing quadcurve in two phases (start-middle,middle-end) using the calculated control points instead of the drawArc method.
I think the bounding rectangle of drawarc is the height and width of the ellipse that your arc is part of.
I am currently making a program which animates the simple harmonic motion of a mass-spring when it is displaced. I have everything working apart from the fact that instead of drawing something like a spring, my program currently uses the graphics.drawline method to draw a straight line to represent the spring. I ideally want something like this however I am not very experienced with graphics and don't really know how to approach it, I tried to make an algorithm myself but it kept falling apart. Does anyone know of any existing algorithms which I could utilise here? If the stretching of the spring looked realistic then that would be great too (if possible).
Here is my current code:
g.fillRect(width/10 - 2, height/2 - 10, 4, 20);
g.fillRect(9*width/10 - 2, height/2 - 10, 4, 20);
g.drawLine(width/10, height/2, (int) (width/2 - (sCoefficientH * s)), height/2);
g.fillOval((int) (width/2 - (sCoefficientH * s)) -5, height/2 - 5, 10, 10);
As you can see there is a line connecting the wall (small rectangle) to the oval (which represents the mass on the spring). If I could add in a new method in this class which takes 2 co-ordinates and a relaxed size (where it wouldn't look compressed) and returns the graphics object (note that I'm not using Graphics2D) with the spring drawn in the correct place then I think it would look a lot nicer. This is what it looks like currently.
Try this:
void drawSpring(double x1, double y1, double x2, double y2, double w, int N, Graphics g)
{
// vector increment
double inv = 0.25 / (double)N;
double dx = (x2 - x1) * inv,
dy = (y2 - y1) * inv;
// perpendicular direction
double inv2 = w / sqrt(dx * dx + dy * dy);
double px = dy * inv2,
py = -dx * inv2;
// loop
double x = x1, y = y1;
for (int i = 0; i < N; i++)
{
g.drawLine(x , y ,
x + dx + px, y + dy + py);
g.drawLine(x + dx + px, y + dy + py,
x + 3.0 * dx - px, y + 3.0 * dy - py);
g.drawLine(x + 3.0 * dx - px, y + 3.0 * dy - py,
x + 4.0 * dx , y + 4.0 * dy );
x += 4.0 * dx;
y += 4.0 * dy;
}
}
Maybe change Graphics to whatever the equivalent is in Java.
EDIT: what I got in VB.NET:
I am making a brick breaker game in Java for fun. In this game the bat is a curved arc that goes around the circumference of a circle. I am struggling to make the bat behave properly.
I am drawing an arc that comes from 2 points on the circle:
public void update(){
if(dir == 1){
angle += 0.05;
}else if(dir == 0){
angle -= 0.05;
}
x0 = a + r * Math.cos(angle);
y0 = b + r * Math.sin(angle);
x1 = a + r * Math.cos(angle - 0.1);
y1 = b + r * Math.sin(angle - 0.1);
}
public void draw(Graphics2D g){
g.setColor(Color.black);
g.fillRect(0, 0, GamePanel.WIDTH, GamePanel.HEIGHT);
int tr = (int)Math.sqrt((x0-a)*(x0-a) + (y0-b)*(y0-b));
int x = (int) (a - tr);
int y = (int) (a - tr);
int width = 2*tr;
int height = 2*tr;
int startAngle = (int) (180/Math.PI*Math.atan2(y0-b, x0-a));
int endAngle = (int) (180/Math.PI*Math.atan2(y1-b, x1-a));
g.setColor(Color.white);
g.drawArc(x, y, width, height, startAngle, endAngle);
}
In theory this should work, the second points being generated from the angle going slightly further, but the length of the arc keeps varying in size...? That is where the problem lies.
This here statement breaks the pattern:
int y = (int) (a - tr);
It would make more sense to use
int y = (int) (b - tr);
And then there is the way g.drawArc is being called:
g.drawArc(x, y, width, height, startAngle, endAngle);
The last parameter is the angle of the arc, so I think you want
g.drawArc(x, y, width, height, startAngle, endAngle - startAngle );
possibly even
g.drawArc(x, y, width, height, startAngle, Math.abs(endAngle - startAngle) );
The following code is an animation of a clock.
What changes would have to be made in the program to make it not flicker?
Why does the field initiation have to take place in the addNotify() function for the hands to be displayed?
You can copy all of it into one .java file and it should work (flicker ;) ).
import java.awt.*;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.util.Calendar;
import java.util.GregorianCalendar;
public class ac extends Frame implements Runnable {
// for double buffering
Graphics og;
Image oi;
Thread t = null;
int width, height;
int timeH, timeM, timeS, radius = 50, lenH, lenM, lenS,
lenIn, cx, cy, x1, y1, x2, y2;
private boolean timeToQuit;
ac() {
addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent we) {
stopRunning();
dispose();
}
});
setMinimumSize(new Dimension(300, 300));
EventQueue.invokeLater(new Runnable() {
public void run() {
setVisible(true);
}
});
}
public void addNotify() {
super.addNotify();
width = getBounds().width;
height = getBounds().height;
setSize(width, height);
lenH = 5 * radius / 10;
lenM = 6 * radius / 10;
lenS = 7 * radius / 10;
lenIn = 8 * radius / 10;
cx = width / 2;
cy = height / 2;
// for double buffering
oi = createImage(width, height);
og = oi.getGraphics();
}
public static void main(String [] args) {
ac clock = new ac();
clock.start();
}
public void start() {
if (t == null) {
t = new Thread(this);
t.start();
}
}
public void stopRunning() {
timeToQuit = true;
}
public void run() {
Thread.currentThread().setPriority(Thread.NORM_PRIORITY - 1);
do {
repaint();
try {
Thread.sleep(500);
}
catch (InterruptedException e) {
}
}
while (!timeToQuit);
System.out.println("Quitting");
}
public void paint(Graphics g) {
og.setColor(new Color(170, 170, 170));
og.fillRect(0, 0, width, height);
Calendar cal = new GregorianCalendar();
timeH = cal.get(Calendar.HOUR);
timeM = cal.get(Calendar.MINUTE);
timeS = cal.get(Calendar.SECOND);
if (timeH >= 12)
timeH -= 12;
for (int i = 1 ; i < 13 ; i++) {
og.setColor(new Color(20, 20, 20));
x2 = (int) (cx + radius * Math.sin(i * 2 * 3.14159f / 12));
y2 = (int) (cy - radius * Math.cos(i * 2 * 3.14159f / 12));
if (i % 3 != 0) {
x1 = (int) (cx + 0.9f * radius * Math.sin(i * 2 * 3.14159f / 12));
y1 = (int) (cy - 0.9f * radius * Math.cos(i * 2 * 3.14159f / 12));
}
else {
x1 = (int) (cx + 0.8f * radius * Math.sin(i * 2 * 3.14159f / 12));
y1 = (int) (cy - 0.8f * radius * Math.cos(i * 2 * 3.14159f / 12));
}
og.drawLine(x1, y1, x2, y2);
og.setColor(new Color(230, 230, 230));
og.drawLine(x1 - 1, y1 - 1, x2 - 1, y2 - 1);
}
og.setColor(new Color(20, 20, 20));
x2 = (int) (cx + lenH * Math.sin((timeH + timeM / 60.0f + timeS / 3600.0f)
* 2 * 3.14159f / 12));
y2 = (int) (cy - lenH * Math.cos((timeH + timeM / 60.0f + timeS / 3600.0f)
* 2 * 3.14159f / 12));
og.drawLine(cx, cy, x2, y2);
og.setColor(Color.red);
og.drawLine(cx - 1, cy - 1, x2 - 1, y2 - 1);
og.setColor(new Color(20, 20, 20));
x2 = (int) (cx + lenM * Math.sin((timeM + timeS / 60.0f) * 2 * 3.14159f
/ 60));
y2 = (int) (cy - lenM * Math.cos((timeM + timeS / 60.0f) * 2 * 3.14159f
/ 60));
og.drawLine(cx, cy, x2, y2);
og.setColor(Color.green);
og.drawLine(cx - 1, cy - 1, x2 - 1, y2 - 1);
og.setColor(new Color(20, 20, 20));
x2 = (int) (cx + lenS * Math.sin(timeS * 2 * 3.14159f / 60));
y2 = (int) (cy - lenS * Math.cos(timeS * 2 * 3.14159f / 60));
og.drawLine(cx, cy, x2, y2);
og.setColor(Color.blue);
og.drawLine(cx - 1, cy - 1, x2 - 1, y2 - 1);
og.setColor(new Color(20, 20, 20));
og.drawOval((width - 2 * radius) / 2, (height - 2 * radius) / 2,
2 * radius, 2 * radius);
og.setColor(new Color(230, 230, 230));
og.drawOval((width - 2 * radius) / 2 - 1, (height - 2 * radius) / 2 - 1,
2 * radius, 2 * radius);
g.drawImage(oi, 0, 0, this);
}
}
This uses awt which is old and it paints on to the main Frame - not the best way to make a swing UI. You can try this :
make the timer - sleep amount to less or more, try 300 or 800
use a Panel to draw on, add the panel to the frame
use swing (use a JComponent, it double buffers automatically, and a JFrame
For 2 and 3 you can keep original class to make Frame/JFrame and a new class for the java.awt.Panel or javax.swing.JComponent which is added to the former.
Does not flicker on my mac system, so i think its an OS + times its refreshed issue. Meaning suggestion 1 might work - try that first. Talking about line of code :
Thread.sleep(800);
Believe it or not, the problem was solved by adding the function:
public void update(Graphics g)
{
paint(g);
}
But the question remains, why? And why aren't the hands displayed when you initiate the class fields outside the addNotify() function?
I'm trying to draw Sierpinski's Triangle recursively in Java, but it doesn't work, though to me the logic seems fine. The base case is when the triangles are within 2 pixels of each other, hence the use of the Distance Formula.
import java.awt.Graphics;
import java.awt.Color;
import java.awt.Font;
import java.awt.Canvas;
public class Triangle extends Canvas implements Runnable
{
private static final int WIDTH = 800;
private static final int HEIGHT = 600;
public Triangle()
{
setBackground(Color.WHITE);
}
public void paint( Graphics window )
{
window.setColor(Color.BLUE);
window.setFont(new Font("ARIAL",Font.BOLD,24));
window.drawString("Serpinski's Gasket", 25, 50);
triangle(window, (WIDTH-10)/2, 20, WIDTH-40, HEIGHT-20, 40, HEIGHT-20, 4);
}
public void triangle(Graphics window, int x1, int y1, int x2, int y2, int x3, int y3, int r)
{
//if statement base case
//midpoint = (x1 + x2 / 2), (y1 + y2/ 2)
if(Math.sqrt((double)(Math.pow(x2-x1, 2)) + (double)(Math.pow(y2-y1, 2))) > 2)
//if(r==0)
{
window.drawLine(x1, y1, x2, y2);
window.drawLine(x2, y2, x3, y3);
window.drawLine(x3, y3, x1, y1);
}
int xa, ya, xb, yb, xc, yc; // make 3 new triangles by connecting the midpoints of
xa = (x1 + x2) / 2; //. the previous triangle
ya = (y1 + y2) / 2;
xb = (x1 + x3) / 2;
yb = (y1 + y3) / 2;
xc = (x2 + x3) / 2;
yc = (y2 + y3) / 2;
triangle(window, x1, y1, xa, ya, xb, yb, r-1); // recursively call the function using the 3 triangles
triangle(window, xa, ya, x2, y2, xc, yc, r-1);
triangle(window, xb, yb, xc, yc, x3, y3, r-1);
}
public void run()
{
try{
Thread.currentThread().sleep(3);
}
catch(Exception e)
{
}
}
}
The Runner is
import javax.swing.JFrame;
public class FractalRunner extends JFrame
{
private static final int WIDTH = 800;
private static final int HEIGHT = 600;
public FractalRunner()
{
super("Fractal Runner");
setSize(WIDTH+40,HEIGHT+40);
getContentPane().add(new Triangle());
setVisible(true);
}
public static void main( String args[] )
{
FractalRunner run = new FractalRunner();
}
}
To me this should work but it causes a runtime/StackOverFlow error that I don't know how to correct. Any help?
You need to move the recursive calls to triangle, and the associated math, inside the conditional check on the separation. Right now, it will always call it and therefore you get the stack overflow.
Chances are your base case might not be working properly- what if the distance between two triangles is never two pixels? say we star with y1 and x1 being 0 and 200. their midpoint would be 100, then 50, 25, 12, 6, 3, 1<--- never hits the 2 pixel base case...
"StdDraw" was taken from here:
public class Sierpinski {
public static void sierpinski(int n) {
sierpinski(n, 0, 0, 1);
}
public static void sierpinski(int n, double x, double y, double size) {
if (n == 0) return;
//compute triangle points
double x0 = x;
double y0 = y;
double x1 = x0 + size;
double y1 = y0;
double x2 = x0 + size / 2;
double y2 = y0 + (Math.sqrt(3)) * size / 2;
// draw the triangle
StdDraw.line(x0, y0, x1, y1);
StdDraw.line(x0, y0, x2, y2);
StdDraw.line(x1, y1, x2, y2);
StdDraw.show(100);
//recursive calls
sierpinski(n-1, x0, y0, size / 2);
sierpinski(n-1, (x0 + x1) / 2, (y0 + y1) / 2, size / 2);
sierpinski(n-1, (x0 + x2) / 2, (y0 + y2) / 2, size / 2);
}
// read in a command-line argument n and plot an order Sierpinski Triangle
public static void main(String[] args) {
int n = Integer.parseInt(args[0]);
StdDraw.setPenRadius(0.005);
sierpinski(n);
}
}
Guy