如何迭代地绘制分形?

时间:2017-11-30 03:08:17

标签: java swing fractals

我目前有一个使用递归绘制分形树的工作代码。但是,当我尝试迭代绘制它时,它无法正常工作。

let width = image.size.width
let height = image.size.height
let size = min(width, height)
let x1 = (width - size) / 2
let y1 = (height - size) / 2
let x2 = x1 + size
let y2 = y1 + size

2 个答案:

答案 0 :(得分:2)

忽略分形数学:如果要保留递归绘图,请使用SwingWorker扭曲长进程(递归计算),并让它更新GUI。这是一个例子:

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.util.Random;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingWorker;

public class RecursiveDraw extends JFrame {

    private int x1A, y1A, x2A, y2A;
    private final int W = 700, H = 500;
    private Random random = new Random();
    private Color randomColor = Color.BLUE;
    private JPanel panel;

    public RecursiveDraw() {

        setDefaultCloseOperation(DISPOSE_ON_CLOSE);
        panel = new MyPanel();
        add(panel, BorderLayout.CENTER);
        pack();
        setVisible(true);
        new Task().run();
    }

    public void recursiveDraw(int x1A, int y1A, int depth){

        if(depth > 15) { return;}

        this.x1A = x1A; this.y1A = y1A;

        x2A = random.nextInt(W);
        y2A = random.nextInt(H);
        randomColor = new Color(random.nextInt(0xFFFFFF));
        panel.repaint();

        try {
            Thread.sleep(1000); //delay
        } catch (InterruptedException ex) { ex.printStackTrace();}

        recursiveDraw(x2A, y2A, ++depth );
    }

    class MyPanel extends JPanel{

        public MyPanel() {
            setPreferredSize(new Dimension(W,H));
        }

        @Override
        public void paintComponent(Graphics g) {
            //super.paintComponent(g);  //requires storing all points calculated
                                        //so they can be redrawn. recommended
            g.setColor(randomColor);
            g.drawLine(x1A, y1A, x2A, y2A);
        }
    }

    class Task extends SwingWorker<Void,Void> {

        @Override
        public Void doInBackground() {
            recursiveDraw(W/2, H/2, 0);
            return null;
        }
    }

    public static void main(String[] args) {

        new RecursiveDraw();
    }
}

@MadProgrammer提出的使用Timer的迭代绘图的基本结构可能如下所示:

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Random; 
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;

public class TimerIterativeDraw extends JFrame {

    private final static int W = 700, H = 500;
    private final static int DELAY= 1000;
    private final static int NUMBER_OF_DRAWS_LIMIT = 50;
    private int x2A = W/2, y2A = H/2, x1A, y1A, numberOfDraws;
    private Random random = new Random();
    private Color randomColor = Color.BLUE;
    private JPanel panel;
    private Timer timer;

    public TimerIterativeDraw() {

        setDefaultCloseOperation(DISPOSE_ON_CLOSE);
        panel = new MyPanel();
        add(panel, BorderLayout.CENTER);
        pack();
        setVisible(true);

        timer = new Timer(DELAY,new Task());
        timer.start();
    }

    public void upDateGui(){

         if(numberOfDraws++ >= NUMBER_OF_DRAWS_LIMIT){
             timer.stop();
         }
        x1A = x2A; y1A = y2A;
        x2A = random.nextInt(W);
        y2A = random.nextInt(H);
        randomColor = new Color(random.nextInt(0xFFFFFF));

        //for better implementation store all points in an array list
        //so they can be redrawn

        panel.repaint();
    }

    class MyPanel extends JPanel{

        public MyPanel() {
            setPreferredSize(new Dimension(W,H));
        }

        @Override
        public void paintComponent(Graphics g) {
            //super.paintComponent(g);  //requires storing all points calculated
                                        //so they can be redrawn. recommended
            g.setColor(randomColor);
            g.drawLine(x1A, y1A, x2A, y2A);
        }
    }

    class Task implements ActionListener {

        @Override
        public void actionPerformed(ActionEvent arg0) {
            upDateGui();
        }
    }

    public static void main(String[] args) {

        new TimerIterativeDraw();
    }
}

答案 1 :(得分:1)

可能有几种方法可以做到这一点,但是......

  • 你需要某种类,你可以调用它来计算下一步并记录它
  • 您需要确保只在事件调度线程的上下文中更新状态。这很重要,因为您不想更新用户界面或用户界面可能依赖于EDT的任何内容,否则您将面临竞争条件和脏涂料的风险。

所以,首先,我们需要某种方式以某种阶梯方式创建分支。我们的想法是每次告知课程更新时才生成新的分支。

该课程将包含它唯一的州和管理层,但会提供对其创建的List点的访问权限,可能类似......

public class Generator  {

    private List<Point> points;
    private double angle;
    private double delta;
    private int depth = 9;

    private Timer timer;

    public Generator(Point startPoint, double startAngle, double delta) {
        points = new ArrayList<>(25);
        points.add(startPoint);
        angle = startAngle;
        this.delta = delta;
    }

    public List<Point> getPoints() {
        return new ArrayList<Point>(points);
    }

    public boolean tick() {
        Point next = updateTree(points.get(points.size() - 1), angle);
        angle += delta;
        depth--;
        if (next != null) {
            points.add(next);
        }
        return next != null;
    }

    public Point updateTree(Point p, double angle) {

        if (depth == 6) {
            return null;
        }

        System.out.println("depth = " + depth + "; angle = " + angle);

        //embedded portion '(Math.toRadians(angle) * depth * 10.0)'represents angle...
        int x2 = p.x + (int) (Math.cos(Math.toRadians(angle)) * depth * 10.0);  //vertical shift calculated using the Sine...PARSED to int because method drawLine() accepts only ints as params
        int y2 = p.y + (int) (Math.sin(Math.toRadians(angle)) * depth * 10.0);  //hor. shift calculated using the Cos..PARSED to int

        return new Point(x2, y2);
    }

}

现在,这个类只生成一个分支,为了创建一个树,你需要这个类的两个实例,具有不同的delta s

接下来,我们需要一定要求这个生成器在常规基础上生成下一步。对我来说,这通常使用Swing Timer调用。

原因是:

  • 很简单。说真的,这真的很简单
  • 它不会阻止EDT,因此不会冻结UI
  • 它在EDT的上下文中更新,从而可以安全地从内部更新UI的状态。

将这两件事放在一个简单的JPanel中,它控制Timer并绘制点......

public class TestPane extends JPanel {

    private Generator left;
    private Generator right;

    public TestPane() {
        Point startPoint = new Point(200, 400);
        left = new Generator(startPoint, -90, -20);
        right = new Generator(startPoint, -90, 20);

        Timer timer = new Timer(1000, new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                boolean shouldContinue = left.tick() && right.tick();
                if (!shouldContinue) {
                    ((Timer)(e.getSource())).stop();
                }
                repaint();
            }
        });
        timer.start();
    }

    @Override
    public Dimension getPreferredSize() {
        return new Dimension(400, 400);
    }

    protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        Graphics2D g2d = (Graphics2D) g.create();
        g2d.setColor(Color.RED);
        render(g2d, left.getPoints());
        g2d.setColor(Color.BLUE);
        render(g2d, right.getPoints());
        g2d.dispose();
    }

    protected void render(Graphics2D g2d, List<Point> points) {
        Point start = points.remove(0);
        while (points.size() > 0) {
            Point end = points.remove(0);
            g2d.draw(new Line2D.Double(start, end));
            start = end;
        }
    }
}