Java 如何迭代绘制分形?

Java 如何迭代绘制分形?,java,swing,fractals,Java,Swing,Fractals,我目前有一个使用递归绘制分形树的工作代码。但是,当我尝试迭代地绘制它时,它不起作用 import java.awt.Color; import java.awt.Graphics; import javax.swing.JFrame; /* -Used built in Math trig methods to accomodate angling...looked this up online https://stackoverflow.com/questions/3003263

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

import java.awt.Color;
import java.awt.Graphics;
import javax.swing.JFrame;

 /*

 -Used built in Math trig methods to accomodate angling...looked this up online


 https://stackoverflow.com/questions/30032635/java-swing-draw-a-line-at-a-specific-angle


*/



public class Test extends JFrame {

    public Test() {

        setBounds(100, 100, 800, 600);  //sets the boundary for drawing

    }

    public void drawTree(Graphics g, int x1, int y1, double angle, int depth) {

        System.out.println("x");

        if (depth == 6){
            return; //base case here to prevent infinite recursion..
        }
        else {


            System.out.println("y1");

            //embedded portion '(Math.toRadians(angle) * depth * 10.0)'represents angle...

            int x2 = x1 + (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 = y1 + (int) (Math.sin(Math.toRadians(angle)) * depth * 10.0);  //hor. shift calculated using the Cos..PARSED to int


          //  System.out.println("x2: " + x2);//will reflect the change in vertical shift
            //System.out.println("y2: " + y2);//will reflect change in hor. shift

            g.drawLine(x1, y1, x2, y2);//value x1 equals previous line...in other word, start at where previously left off
            // notice that the end point (x2 and y2) becomes starting point for each successive call


            drawTree(g, x2, y2, angle - 20, depth - 1); //DRAWS LEFT SIDE?

           // drawTree(g, x2, y2, angle + 20, depth - 1); //DRAWS RIGHT SIDE?

        }



        if (depth == 6){
            return; //base case here to prevent infinite recursion..
        }
        else {


            System.out.println("y2");


            int x2 = x1 + (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 = y1 + (int) (Math.sin(Math.toRadians(angle)) * depth * 10.0);  //hor. shift calculated using the Cos..PARSED to int


           // System.out.println("x2: " + x2);//will reflect the change in vertical shift
            //System.out.println("y2: " + y2);//will reflect change in hor. shift

            g.drawLine(x1, y1, x2, y2);//value x1 equals previous line...in other word, start at where previously left off
            // notice that the end point (x2 and y2) becomes starting point for each successive call


           // drawTree(g, x2, y2, angle - 20, depth - 1); //DRAWS LEFT SIDE?

            drawTree(g, x2, y2, angle + 20, depth - 1); //DRAWS RIGHT SIDE?

        }

    }




    public void drawIteratively(Graphics g, int x1A, int y1A, int x1B, int y1B, double angleA, double angleB, int depthA, int depthB){

        while (depthA != 4) {


            int x2A = x1A + (int) (Math.cos(Math.toRadians(angleA)) * depthA * 10.0);
            int y2A = y1A + (int) (Math.sin(Math.toRadians(angleA)) * depthA * 10.0);

            g.drawLine(x1A, y1A, x2A, y2A);   //remember it must continue drawing from where it left off

            angleA = angleA - 20;
            depthA = depthA - 1;

            x1A = x2A;
            y1A = y2A;

        }

        while (depthA != 4) {


            int x2A = x1A + (int) (Math.cos(Math.toRadians(angleA)) * depthA * 10.0);
            int y2A = y1A + (int) (Math.sin(Math.toRadians(angleA)) * depthA * 10.0);

            g.drawLine(x1A, y1A, x2A, y2A);   //remember it must continue drawing from where it left off

            angleA = angleA - 20;
            depthA = depthA - 1;

            x1A = x2A;
            y1A = y2A;

        }

        /*

        while(depthB != 4){


            int x2B = x1B + (int) (Math.cos(Math.toRadians(angleB)) * depthB * 10.0);
            int y2B = y1B + (int) (Math.sin(Math.toRadians(angleB)) * depthB * 10.0);

            g.drawLine(x1B, y1B, x2B, y2B);

            angleB = angleB + 20;
            depthB = depthB - 1;

            x1B = x2B;
            y1B = y2B;

        }
        */

    }






    @Override
    public void paint(Graphics g) {
        g.setColor(Color.BLUE);


       //drawTree(g, 400, 400, -90, 9); //these values corresponding to original line aka trunk during initial method call

        drawIteratively(g, 400, 500, 400,500 ,-90 , -90, 9,9);




    }

    public static void main(String[] args) {

        new Test().setVisible(true);

    }
}

忽略分形数学:如果要保留递归图形,请使用一个函数扭曲长过程(递归计算),并让它更新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();
    }
}

忽略分形数学:如果要保留递归图形,请使用一个函数扭曲长过程(递归计算),并让它更新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();
    }
}

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

  • 您需要某种类型的类,您可以调用它来计算下一步并记录它
  • 您需要确保只从事件调度线程的上下文中更新状态。这一点很重要,因为您不想更新UI或任何UI可能依赖于EDT之外的内容,否则您将面临比赛条件和油漆脏的风险
因此,首先,我们需要一些方法以某种阶梯式的方式创建分支。其思想是每次通知类更新时只生成一个新的分支

该类将只包含它的状态和管理,但将提供对它创建的点的
列表的访问,可能类似于

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);
    }

}

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

  • 您需要某种类型的类,您可以调用它来计算下一步并记录它
  • 您需要确保只从事件调度线程的上下文中更新状态。这一点很重要,因为您不想更新UI或任何UI可能依赖于EDT之外的内容,否则您将面临比赛条件和油漆脏的风险
因此,首先,我们需要一些方法以某种阶梯式的方式创建分支。其思想是每次通知类更新时只生成一个新的分支

该类将只包含它的状态和管理,但将提供对它创建的点的
列表的访问,可能类似于

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);
    }

}

您不应该从
JFrame
扩展并覆盖它的
paint
方法-一个框架中有很多东西会影响绘制过程-此外,您正在破坏绘制链。相反,从一个
JPanel
开始,覆盖它的
paintComponent
方法(别忘了调用
super.paintComponent
),你想要的是一个Swing
定时器
,Swing
定时器的每次迭代都会更新一些状态,从而增加分形。您需要将其绘制到一个
BufferedImage
或维护某种结构,该结构可由
paintComponet
方法用于渲染当前结果。这将改变代码的工作方式,因为不使用递归方法,您将需要维护每次传递所需的信息,以便它可以为method@MadProgrammer迂腐,但是“它的
绘画方法”,“它的
绘画组件”
“--对不起,但它让我讨厌;)我认为你需要在第一次循环后重置
depthA
。你不应该从
JFrame
扩展并覆盖它的
paint
方法-一个框架中有很多东西会影响绘制过程-此外,你正在破坏绘制链。相反,从一个
JPanel
开始,覆盖它的
paintComponent
方法(别忘了调用
super.paintComponent
),你想要的是一个Swing
定时器
,Swing
定时器的每次迭代都会更新一些状态,从而增加分形。您需要将其绘制到一个
BufferedImage
或维护某种结构,该结构可由
paintComponet
方法用于渲染当前结果。这将改变代码的工作方式,因为不使用递归方法,您将需要维护每次传递所需的信息,以便它可以为method@MadProgrammer迂腐,但是“它的
绘画方法”,“它的
绘画组件”
“--对不起,但它让我讨厌;)我认为您需要在第一次循环后重置
depthA
。不调用
super.paintComponent
将从UI上可能出现的其他组件生成绘制人工制品-最好使用
BufferedImage
或维护一些可以重新生成的结构是的,我知道
super.paintComponent(g)
是必需的,因此我的注释
//super.paintComponent(g);需要存储所有计算的点,以便可以重新绘制。推荐的
。我实现它并不是为了让示例尽可能的简单和基本。不要评论,展示,问题是OP会忽略,只会学会做错误的事情-没有捷径不能调用
super。paintComponent
会从UI上可能出现的其他组件生成绘制人工制品-最好使用
BuffereImage
或维护一些可以重新生成的结构Yes,我知道
super.paintComponent(g)
是必需的,因此我的注释
//super.paintComponent(g);需要存储所有计算的点,以便可以重新绘制。推荐的
。我实施它并不是为了让示例尽可能的简单和基本。不要评论,展示,问题是OP会忽略,只会学会做错误的事情-没有捷径