Search code examples
javaswingrecursionjpanelpaintcomponent

Drawing Sierpinski Triangle fractal - Java


My program runs and successfully draws the Sierpinski Triangle. However, it only appears for a short while and then disappears. Why is this?

Here's my program (2 classes). This is fully compilable and runnable.

TrianglePanel.java

    import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Point;
import java.util.ArrayList;

import javax.swing.JFrame;
import javax.swing.JPanel;

public class TrianglePanel extends JPanel {

    int level = 2;
    int width;
    int height;
    Graphics g;

    public static void main(String[] agrs) {
    TrianglePanel panel = new TrianglePanel();
    JFrame frame = new JFrame();

    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.setSize(new Dimension(400, 400));
    frame.setLocationRelativeTo(null);

    frame.add(panel);

    frame.setVisible(true);
    }

    public TrianglePanel() {

    }

    public void paintComponent(Graphics g) {

    this.g = g;

    width = this.getSize().width;
    height = this.getSize().height;

    ArrayList<Line> queue = new ArrayList<Line>();

    queue.add(new Line(new Point(width / 2, 0), new Point(0, height)));
    queue.add(new Line(new Point(0, height), new Point(width, height)));
    queue.add(new Line(new Point(width, height), new Point(width / 2, 0)));

    drawLine(queue.get(0));
    drawLine(queue.get(1));
    drawLine(queue.get(2));

    int counter = 0;

    while (true) {
        if (level > 6) {
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        break;
        }

        Line[] toDraw = queue.get(0).getTriangleToDraw(level, width, height);

        Point[] toDrawPoints = toDraw[0].getPoints();

        if (Math.sqrt(((Math.pow(toDrawPoints[1].x - toDrawPoints[0].x, 2)) + (Math.pow(toDrawPoints[1].y - toDrawPoints[0].y, 2)))) <= 4) {
        System.out.println("breakyo");
        break;
        }

        drawLine(toDraw[0]);
        drawLine(toDraw[1]);
        drawLine(toDraw[2]);

        queue.remove(0);

        queue.add(toDraw[0]);
        queue.add(toDraw[1]);
        queue.add(toDraw[2]);

        counter++;

        System.out.println(counter + " and " + level);

        if (counter - Math.pow(3, level - 1) == 0) {
        counter = 0;
        level++;
        }

    }
    }

    private void drawLine(Line line) {
    g.drawLine(line.getPoints()[0].x, line.getPoints()[0].y, line.getPoints()[1].x, line.getPoints()[1].y);
    }
}

Line.java

import java.awt.Point;

public class Line {

    private Point endpoint1;
    private Point endpoint2;
    private Point midpoint;
    private double slope;

    public Line(Point endpoint1, Point endpoint2) {
    this.endpoint1 = endpoint1;
    this.endpoint2 = endpoint2;

    midpoint = new Point((endpoint1.x + endpoint2.x) / 2, (endpoint1.y + endpoint2.y) / 2);
    slope = (double) (endpoint2.y - endpoint1.y) / (double) (endpoint2.x - endpoint1.x);

    }

    public Point[] getPoints() {
    return new Point[] { endpoint1, endpoint2 };
    }

    public Line[] getTriangleToDraw(int level, int width, int height) {

    Line[] lines = new Line[3];

    if (slope > 0) { //negative slope
        Point left = new Point(midpoint.x - (width / (int)(Math.pow(2, level - 1))), midpoint.y);
        Point middle = new Point(midpoint.x - (width / (int)(Math.pow(2, level))), midpoint.y + (height / (int)(Math.pow(2, level - 1))));

        lines[0] = new Line(midpoint, left);
        lines[1] = new Line(midpoint, middle);
        lines[2] = new Line(middle, left);
    }

    else if (slope < 0) { // positive slope
        Point right = new Point(midpoint.x + (width / (int)(Math.pow(2, level - 1))), midpoint.y);
        Point middle = new Point(midpoint.x + (width / (int)(Math.pow(2, level))), midpoint.y + (height / (int)(Math.pow(2, level - 1))));

        lines[0] = new Line(midpoint, right);
        lines[1] = new Line(midpoint, middle);
        lines[2] = new Line(middle, right);
    }

    else { //zero slope
        Point left = new Point(midpoint.x - (width / (int)(Math.pow(2, level))), midpoint.y - (height / (int)(Math.pow(2, level - 1))));
        Point right = new Point(midpoint.x + (width / (int)(Math.pow(2, level))), midpoint.y - (height / (int)(Math.pow(2, level - 1))));

        lines[0] = new Line(midpoint, left);
        lines[1] = new Line(midpoint, right);
        lines[2] = new Line(left, right);
    }

    return lines;
    }
}

Solution

  • This takes an approach similar to that suggested by @Hovercraft, but swaps the SwingWorker for a Timer and instead of setting a new icon to the label, it calls repaint().

    Partial fractal drawing

    import java.awt.*;
    import java.awt.event.*;
    import java.awt.image.BufferedImage;
    import javax.swing.*;
    import java.util.ArrayList;
    
    public class TrianglePanel extends JPanel {
    
        int level = 2;
        int width;
        int height;
        Timer timer;
        BufferedImage canvas;
        JLabel label;
    
        public static void main(String[] agrs) {
            TrianglePanel panel = new TrianglePanel();
            JFrame frame = new JFrame();
    
            frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
            frame.setLocationByPlatform(true);
    
            frame.add(panel);
            frame.pack();
    
            frame.setVisible(true);
    
        }
    
        public TrianglePanel() {
            final int width = 400;
            final int height = 400;
            canvas = new BufferedImage(width,height,BufferedImage.TYPE_INT_RGB);
            label = new JLabel(new ImageIcon(canvas));
            add( label );
            final Graphics2D g = canvas.createGraphics();
            g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
            g.setColor(new Color(255,127,0));
    
            final ArrayList<Line> queue = new ArrayList<Line>();
    
            queue.add(new Line(new Point(width / 2, 0), new Point(0, height)));
            queue.add(new Line(new Point(0, height), new Point(width, height)));
            queue.add(new Line(new Point(width, height), new Point(width / 2, 0)));
    
            drawLine(queue.get(0),g);
            drawLine(queue.get(1),g);
            drawLine(queue.get(2),g);
    
            ActionListener al = new ActionListener() {
                int counter = 0;
    
                public void actionPerformed(ActionEvent ae) {
                    if (level > 6) {
                        timer.stop();
                        return;
                    }
    
                    Line[] toDraw = queue.get(0).getTriangleToDraw(level, width, height);
    
                    Point[] toDrawPoints = toDraw[0].getPoints();
    
                    if (Math.sqrt(((Math.pow(toDrawPoints[1].x - toDrawPoints[0].x, 2)) + (Math.pow(toDrawPoints[1].y - toDrawPoints[0].y, 2)))) <= 4) {
                        System.out.println("breakyo");
                        return;
                    }
    
                    drawLine(toDraw[0],g);
                    drawLine(toDraw[1],g);
                    drawLine(toDraw[2],g);
    
                    queue.remove(0);
    
                    queue.add(toDraw[0]);
                    queue.add(toDraw[1]);
                    queue.add(toDraw[2]);
    
                    counter++;
    
                    System.out.println(counter + " and " + level);
    
                    if (counter - Math.pow(3, level - 1) == 0) {
                        counter = 0;
                        level++;
                    }
                    label.repaint();
                }
            };
            timer = new Timer(20,al);
            timer.start();
        }
    
        private void drawLine(Line line, Graphics g) {
            g.drawLine(line.getPoints()[0].x, line.getPoints()[0].y, line.getPoints()[1].x, line.getPoints()[1].y);
        }
    }
    
    class Line {
    
        private Point endpoint1;
        private Point endpoint2;
        private Point midpoint;
        private double slope;
    
        public Line(Point endpoint1, Point endpoint2) {
            this.endpoint1 = endpoint1;
            this.endpoint2 = endpoint2;
    
            midpoint = new Point((endpoint1.x + endpoint2.x) / 2, (endpoint1.y + endpoint2.y) / 2);
            slope = (double) (endpoint2.y - endpoint1.y) / (double) (endpoint2.x - endpoint1.x);
    
            }
    
            public Point[] getPoints() {
            return new Point[] { endpoint1, endpoint2 };
        }
    
        public Line[] getTriangleToDraw(int level, int width, int height) {
    
            Line[] lines = new Line[3];
    
            if (slope > 0) { //negative slope
                Point left = new Point(midpoint.x - (width / (int)(Math.pow(2, level - 1))), midpoint.y);
                Point middle = new Point(midpoint.x - (width / (int)(Math.pow(2, level))), midpoint.y + (height / (int)(Math.pow(2, level - 1))));
    
                lines[0] = new Line(midpoint, left);
                lines[1] = new Line(midpoint, middle);
                lines[2] = new Line(middle, left);
            }
    
            else if (slope < 0) { // positive slope
                Point right = new Point(midpoint.x + (width / (int)(Math.pow(2, level - 1))), midpoint.y);
                Point middle = new Point(midpoint.x + (width / (int)(Math.pow(2, level))), midpoint.y + (height / (int)(Math.pow(2, level - 1))));
    
                lines[0] = new Line(midpoint, right);
                lines[1] = new Line(midpoint, middle);
                lines[2] = new Line(middle, right);
            }
    
            else { //zero slope
                Point left = new Point(midpoint.x - (width / (int)(Math.pow(2, level))), midpoint.y - (height / (int)(Math.pow(2, level - 1))));
                Point right = new Point(midpoint.x + (width / (int)(Math.pow(2, level))), midpoint.y - (height / (int)(Math.pow(2, level - 1))));
    
                lines[0] = new Line(midpoint, left);
                lines[1] = new Line(midpoint, right);
                lines[2] = new Line(left, right);
            }
    
            return lines;
        }
    }