在Java中绘制Sierpinski三角形

4

我在编写绘制谢尔宾斯基三角形 (或谢尔宾斯基海绵) 的代码时遇到了一些问题,但我不确定问题出在哪里。三角形的线条被绘制出来,然后是所有的分形图案,但最后整个图案就消失了。请帮忙解决?

import javax.swing.*;
import java.awt.*;

public class SierpinskiGasket extends JFrame {

Point x=new Point(5,545),
      y=new Point(300,25),
      z=new Point(605,545),
      current=x, target;
private int count=0;

public SierpinskiGasket () {
    super("Sierpinski Gasket");
    setSize(610,550);
    setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    getContentPane().setBackground(Color.WHITE);
    setLocationRelativeTo(null);
    setResizable(false);
    setVisible(true);
}

public void paint(Graphics g) {
    super.paint(g);
    if(count==0) {
    g.drawLine(x.x,x.y,y.x,y.y);
    g.drawLine(x.x,x.y,z.x,z.y);
    g.drawLine(z.x,z.y,y.x,y.y);
    } else {
        while(count<10000) {
            int choice=(int)(Math.random()*3);
            switch(choice) {
                case 0: target=x; break;
                case 1: target=y; break;
                case 2: target=z; break;
                default: System.exit(0);
            }
            current=midpoint(current,target);
            g.drawRect(current.x,current.y,5,5);
            repaint();
            count++;
        }
    }
    count++;
}

public Point midpoint(Point a, Point b) {
    return new Point((Math.round(a.x+b.x)/2),
                     (Math.round(a.y+b.y)/2));
}

public static void main(String[] args) {
    new SierpinskiGasket();
}
}
3个回答

6
  1. 不要从顶级容器(如JFrame)继承,这样做没有任何好处。
  2. 避免在顶级容器中进行绘制,而应该使用类似于JPanel的东西。
  3. 避免覆盖paint方法,改用paintComponent方法。有关更多详细信息,请参见执行自定义绘制

这不是绘制的工作方式(我不会尝试重写您的代码来使其正常运行)。

当重新绘制系统决定需要更新部分或整个UI时,会调用绘制方法。绘制是破坏性的,也就是说,当调用绘制方法时,Graphics将被完全刷新,需要您从头开始“重建”输出。

相反。

编写一个递归算法,可以绘制到像BufferedImage这样的地方,并在paintComponent中绘制它...

已更新

Swing中的绘画由RepaintManager控制,它负责确定何时重绘屏幕。您似乎认为paint是您控制的内容,但实际上并非如此。
根据您的需要,您需要准备好完全重绘UI或准备缓冲区以便绘制到UI上。

enter image description here

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.geom.AffineTransform;
import java.awt.geom.Path2D;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;

public class SierpinskisGasket {

    public static void main(String[] args) {
        new SierpinskisGasket();
    }

    public SierpinskisGasket() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                }

                JFrame frame = new JFrame("Testing");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.setLayout(new BorderLayout());
                frame.add(new TestPane());
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public class TestPane extends JPanel {

        public TestPane() {
        }

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

        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            Graphics2D g2d = (Graphics2D) g.create();
            BaseShape base = new BaseShape(Math.min(getWidth(), getHeight()));
            Rectangle bounds = base.getBounds();
            int x = (getWidth() - bounds.width) / 2;
            int y = (getHeight() - bounds.height) / 2;
            base.transform(AffineTransform.getTranslateInstance(x, y));
            g2d.fill(base);
            g2d.dispose();
        }
    }

    public class BaseShape extends Path2D.Float {

        public BaseShape(float size) {

            float subSize = size / 2f;
            Triangle top = new Triangle(subSize);
            top.transform(AffineTransform.getTranslateInstance((size - subSize) / 2, 0));
            append(top, false);

            Triangle left = new Triangle(subSize);
            left.transform(AffineTransform.getTranslateInstance(0, subSize));
            append(left, false);

            Triangle right = new Triangle(subSize);
            right.transform(AffineTransform.getTranslateInstance(subSize, subSize));
            append(right, false);

        }

    }

    public class Triangle extends Path2D.Float {

        public Triangle(float size) {

            moveTo(size / 2f, 0);
            lineTo(size, size);
            lineTo(0, size);
            closePath();

        }

    }

}

您不应该从paint中调用可能改变UI状态并触发另一个repaint的任何内容,否则您最终会陷入重新绘制的无限循环中,这将最终消耗您的CPU。

您还应该查看在AWT和Swing中绘图


这对我真的没有帮助。无论如何谢谢。 - user2361122
我主要关心的是为什么无法绘制分形。 - user2361122
2
你也在响应特定的状态或时间点。每次新的绘制周期都会清除“Graphics”上下文,这意味着你之前绘制的任何内容都不存在了。你需要在每个周期完全重建输出。当我运行你的代码时,我得到了一个计数器为1、1001的结果,但屏幕上没有显示任何东西,因为在第1001次迭代时,它被清除了...我确定我说过这话(虽然没有这么多的字)。 - MadProgrammer
感谢您的建议,并且花时间指出我的错误。需要注意的是,我是一名初学者程序员,对AWT或String知之甚少。我只在我的Java课程中学习了大约3周的AWT和Swing主题。在我的Java教材中,我得到了这个练习,在编写代码时,我所采用的方式基本上与所示相同。在我的文本中没有提到您在答案中提到的任何内容。我并不怀疑您的知识,但是我想至少在这门课程中,我应该像我所做的那样解决问题。 - user2361122
@MadProgrammer 我想我明白你的意思了。下面FOOM在他的回答中指出了错误,解决了我的主要问题(为什么分形图案没有被绘制)。现在我看到,在所有东西都被绘制后,它就消失了。 - user2361122
显示剩余2条评论

1
int i = ((int)Math.random()*3);    
System.out.println("i:"+i);

当我运行上述代码时,总是得到0。
所以你的“choice”变量总是为零……可能与你的问题有关?

哇!多么愚蠢的错误啊!这样修复它...有点。现在分形图形被绘制出来了,但是一旦完成就消失了。 - user2361122
我不是Swing专家,但我认为MadProgrammer说的“每个新的绘制周期都会清除图形上下文,这意味着你之前绘制的任何东西都不存在了”似乎描述了你遇到的问题,所以我建议你看看他的答案 :) - FOOM

-1

只需移除repaint()调用即可解决问题。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接