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会忽略,只会学会做错误的事情-没有捷径