欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

java递归画谢尔宾斯基三角形

程序员文章站 2024-02-21 18:54:34
...

什么是谢尔宾斯基三角形

谢尔宾斯基三角形(英语:Sierpinski triangle)是一种分形,由波兰数学家谢尔宾斯基在1915年提出。它是自相似集的例子。它的豪斯多夫维是log(3)/log(2) ≈ 1.585。

形状

java递归画谢尔宾斯基三角形

构造

递归

  1. 画一个三角形。(多数使用等边三角形)
  2. 沿三边中点的连线,将它分成四个小三角形。
  3. 对其余三个小三角形重复1。

若取一个正方形或其他形状开始,用类似的方法构作,形状也会和谢尔宾斯基三角形相近。

Chaos Game

  1. 用随机的方法(Chaos Game),都可得到谢尔宾斯基三角形:
  2. 任意取平面上三点A,B,C,组成一三角形
  3. 任意取三角形ABC内的一点P,画出 该点
  4. 画出 P和三角形其中一个顶点的中点
  5. 重复1

L系统
这条曲线以L系统来记述为:
变量: A , B 常数: + , - 公理: A 规则: A → B-A-B B → A+B+A A,B : 向前
-: 左转60°
+: 右转60°

这里我们使用递归方法画谢尔宾斯基三角形。

思路

1、递归

先根据自己选的三个点(x1,y1),(x2,y2),(x3,y3)(x_1, y_1),(x_2, y_2),(x_3, y_3)画三条线,然后分别取(x1,y1),(x3,y3);(x1,y1),(x2,y2);(x2,y2),(x3,y3)(x_1, y_1),(x_3, y_3);(x_1, y_1),(x_2, y_2);(x_2, y_2),(x_3, y_3)的中点为(x4,y4),(x5,y5),(x6,y6)(x_4,y_4),(x_5,y_5),(x_6,y_6),连接(x4,y4),(x5,y5),(x6,y6)(x_4,y_4),(x_5,y_5),(x_6,y_6)三点。此时除了中间的三角形外形成了三个新的三角形,分别把它们的顶点看做(x1,y1),(x2,y2),(x3,y3)(x_1, y_1),(x_2, y_2),(x_3, y_3),再重复上面的步骤。

public void x(int x1, int y1, int x2, int y2, int x3, int y3, int n) {
		index = 0;
		if (n > 0) {// 迭代条件
			n--;
			g.drawLine(x1, y1, x2, y2);
			g.drawLine(x3, y3, x2, y2);
			g.drawLine(x1, y1, x3, y3);

			int x4 = (int) ((x1 + x3) / 2);
			int y4 = (int) ((y1 + y3) / 2);
			int x5 = (int) ((x1 + x2) / 2);
			int y5 = (int) ((y2 + y1) / 2);
			int x6 = (int) ((x3 + x2) / 2);
			int y6 = (int) ((y3 + y2) / 2);

			g.drawLine(x4, y4, x5, y5);
			g.drawLine(x6, y6, x5, y5);
			g.drawLine(x4, y4, x6, y6);

			// 迭代
			x(x1, y1, x5, y5, x4, y4, n);
			x(x5, y5, x2, y2, x6, y6, n);
			x(x4, y4, x6, y6, x3, y3, n);
		}
	}

示意图:
java递归画谢尔宾斯基三角形

2、画窗体

和普通的画窗体没什么区别。

package 谢尔宾斯基三角形;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.FlowLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;

public class DrawUI {
	public void intiui() {
		// 创建画图板窗口
		JFrame jf = new JFrame("谢尔宾斯基三角形");

		// 创建监听器对象
		Listener ls = new Listener();

		// 设置面板属性
		jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		jf.setSize(800, 700); // 设置大小为800*700
		jf.setLocationRelativeTo(null);// 设置居中显示
		jf.setLayout(new BorderLayout());// 设置Border布局(JFrame默认为Border布局)

		// 创建Panel(且Panel为流式布局)
		JPanel jp1 = new JPanel();
		JPanel jp2 = new JPanel();
		JPanel jp3 = new JPanel();
		JPanel jp4 = new JPanel();
		jp1.setLayout(new FlowLayout());
		jp2.setLayout(new FlowLayout());
		jp1.setPreferredSize(new Dimension(70, 100));
		jp2.setPreferredSize(new Dimension(40, 100));
		jf.add(jp1, BorderLayout.WEST);
		jf.add(jp2, BorderLayout.EAST);
		jf.add(jp3, BorderLayout.CENTER);
		jf.add(jp4, BorderLayout.SOUTH);
		jp3.addMouseMotionListener(ls);
		jp3.addMouseListener(ls);

		// 用数组和循环创建按钮并添加监听器 格式:类型[] 数组名 = {"变量","变量"...};
		String[] ShapeBtn = { "生成", "清屏" };
		for (int i = 0; i < ShapeBtn.length; i++) {
			JButton shape = new JButton(ShapeBtn[i]);
			jp1.add(shape);
			shape.addActionListener(ls);
		}

		// 创建颜色按钮
		Color[] ColorBtn = { Color.BLUE, Color.GREEN, Color.RED, Color.YELLOW, Color.CYAN, Color.BLACK };
		Dimension colorBtnSize = new Dimension(30, 30); // 设置颜色方块的大小
		for (int i = 0; i < ColorBtn.length; i++) {
			JButton color = new JButton(); // 创建颜色按钮对象
			color.setBackground(ColorBtn[i]);// 设置按钮的背景颜色
			color.setPreferredSize(colorBtnSize);
			jp2.add(color);
			color.addActionListener(ls);
		}

		// 设置窗口可见
		jf.setVisible(true);

		// 获取窗体的画布,并传给监听器对象(必须在设置窗口可见之后)
		ls.g = jp3.getGraphics();

	}

	public static void main(String[] args) {
		DrawUI ui = new DrawUI();
		ui.intiui();
	}
}

3、监听器

package 谢尔宾斯基三角形;

import java.awt.Color;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import javax.swing.JButton;

public class Listener implements ActionListener, MouseListener, MouseMotionListener {

	int x1;
	int y1;
	int x2;
	int y2;
	int x3;
	int y3;
	String action;
	Graphics g;
	int[] X = new int[3];
	int[] Y = new int[3];
	int index = 0;
	int n = 3;

	public void x(int x1, int y1, int x2, int y2, int x3, int y3, int n) {
		index = 0;
		if (n > 0) {// 迭代条件
			n--;
			g.drawLine(x1, y1, x2, y2);
			g.drawLine(x3, y3, x2, y2);
			g.drawLine(x1, y1, x3, y3);

			int x4 = (int) ((x1 + x3) / 2);
			int y4 = (int) ((y1 + y3) / 2);
			int x5 = (int) ((x1 + x2) / 2);
			int y5 = (int) ((y2 + y1) / 2);
			int x6 = (int) ((x3 + x2) / 2);
			int y6 = (int) ((y3 + y2) / 2);

			g.drawLine(x4, y4, x5, y5);
			g.drawLine(x6, y6, x5, y5);
			g.drawLine(x4, y4, x6, y6);

			// 迭代
			x(x1, y1, x5, y5, x4, y4, n);
			x(x5, y5, x2, y2, x6, y6, n);
			x(x4, y4, x6, y6, x3, y3, n);
		}
	}

	@Override
	public void mouseClicked(MouseEvent e) {
		if (index < 3) {
			// 获取三个顶点的坐标
			X[index] = e.getX();
			Y[index] = e.getY();
			System.out.println("第" + (index + 1) + "个点");
			g.fillOval(X[index] - 3, Y[index] - 3, 6, 6);
			index++;
		}

		// 把坐标储存在x_i, y_i中
		x1 = X[0];
		x2 = X[1];
		x3 = X[2];
		y1 = Y[0];
		y2 = Y[1];
		y3 = Y[2];
	}

	public void actionPerformed(ActionEvent e) {
		// TODO Auto-generated method stub
		action = e.getActionCommand();
		if (action.equals("")) {// 颜色
			// 获取事件源
			Object srcObj = e.getSource();
			// 获取按钮
			JButton srcBtn = (JButton) srcObj;
			// 获取颜色
			Color color = srcBtn.getBackground();
			g.setColor(color);
		} else if ("清屏".equals(action)) {
			g.clearRect(0, 0, 2000, 2000);
		} else if (action.equals("生成")) {
			x(x1, y1, x2, y2, x3, y3, n);
		}
	}

	public void mousePressed(MouseEvent e) {
		// TODO Auto-generated method stub

	}

	public void mouseReleased(MouseEvent e) {
		// TODO Auto-generated method stub

	}

	public void mouseEntered(MouseEvent e) {
		// TODO Auto-generated method stub

	}

	public void mouseExited(MouseEvent e) {
		// TODO Auto-generated method stub
	}

	public void mouseDragged(MouseEvent e) {
		// TODO Auto-generated method stub

	}

	public void mouseMoved(MouseEvent e) {
		// TODO Auto-generated method stub

	}
}

此时已经可以通过改变颜色和nn的值来画出不同颜色和迭代次数的谢尔宾斯基三角形,但是通过改变nn的值来改变迭代次数未免太没有交互性,因此在窗体和监听器中添加滑动条,我们就可以通过滑动滑动条的滑块来改变nn的值。

4、滑动条

在窗体中

// 创建滑动条
JSlider JS = new JSlider();
// 添加滑动条
jp4.add(JS);
JS.setValue(3);
JS.setSnapToTicks(true);
JS.setMaximum(6);
JS.setMinimum(0);
JS.addChangeListener(ls);

在监听器中

	public void stateChanged(ChangeEvent e) {
		// 获取滑动条对象
		Object Ob = e.getSource();
		JSlider Obj = (JSlider) Ob;
		n = Obj.getValue();
		// 清屏
		g.clearRect(0, 0, 2000, 2000);
		// 画顶点
		for (int i = 0; i < 3; i++) {
			g.fillOval(X[i] - 3, Y[i] - 3, 6, 6);
		}
		// 递归
		x(x1, y1, x2, y2, x3, y3, n);
	}

完整代码

DrawUI.java

package 谢尔宾斯基三角形;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.FlowLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JSlider;

public class DrawUI {
	public void intiui() {
		// 创建画图板窗口
		JFrame jf = new JFrame("谢尔宾斯基三角形");

		// 创建监听器对象
		Listener ls = new Listener();

		// 设置面板属性
		jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		jf.setSize(800, 700); // 设置大小为800*700
		jf.setLocationRelativeTo(null);// 设置居中显示
		jf.setLayout(new BorderLayout());// 设置Border布局(JFrame默认为Border布局)

		// 创建Panel(且Panel为流式布局)
		JPanel jp1 = new JPanel();
		JPanel jp2 = new JPanel();
		JPanel jp3 = new JPanel();
		JPanel jp4 = new JPanel();
		jp1.setLayout(new FlowLayout());
		jp2.setLayout(new FlowLayout());
		jp1.setPreferredSize(new Dimension(70, 100));
		jp2.setPreferredSize(new Dimension(40, 100));
		jf.add(jp1, BorderLayout.WEST);
		jf.add(jp2, BorderLayout.EAST);
		jf.add(jp3, BorderLayout.CENTER);
		jf.add(jp4, BorderLayout.SOUTH);
		jp3.addMouseMotionListener(ls);
		jp3.addMouseListener(ls);

		// 创建滑动条
		JSlider JS = new JSlider();
		// 添加滑动条
		jp4.add(JS);
		JS.setValue(3);
		JS.setSnapToTicks(true);
		JS.setMaximum(6);
		JS.setMinimum(0);
		JS.addChangeListener(ls);

		// 用数组和循环创建按钮并添加监听器 格式:类型[] 数组名 = {"变量","变量"...};
		String[] ShapeBtn = { "生成", "清屏" };
		for (int i = 0; i < ShapeBtn.length; i++) {
			JButton shape = new JButton(ShapeBtn[i]);
			jp1.add(shape);
			shape.addActionListener(ls);
		}

		// 创建颜色按钮
		Color[] ColorBtn = { Color.BLUE, Color.GREEN, Color.RED, Color.YELLOW, Color.CYAN, Color.BLACK };
		Dimension colorBtnSize = new Dimension(30, 30); // 设置颜色方块的大小
		for (int i = 0; i < ColorBtn.length; i++) {
			JButton color = new JButton(); // 创建颜色按钮对象
			color.setBackground(ColorBtn[i]);// 设置按钮的背景颜色
			color.setPreferredSize(colorBtnSize);
			jp2.add(color);
			color.addActionListener(ls);
		}

		// 设置窗口可见
		jf.setVisible(true);

		// 获取窗体的画布,并传给监听器对象(必须在设置窗口可见之后)
		ls.g = jp3.getGraphics();

	}

	public static void main(String[] args) {
		DrawUI ui = new DrawUI();
		ui.intiui();
	}
}

Listener.java

package 谢尔宾斯基三角形;

import java.awt.Color;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import javax.swing.JButton;
import javax.swing.JSlider;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;

public class Listener implements ActionListener, MouseListener, MouseMotionListener, ChangeListener {

	int x1;
	int y1;
	int x2;
	int y2;
	int x3;
	int y3;
	String action;
	Graphics g;
	int[] X = new int[3];
	int[] Y = new int[3];
	int index = 0;
	int n = 3;

	public void x(int x1, int y1, int x2, int y2, int x3, int y3, int n) {
		index = 0;
		if (n > 0) {
			n--;
			g.drawLine(x1, y1, x2, y2);
			g.drawLine(x3, y3, x2, y2);
			g.drawLine(x1, y1, x3, y3);

			int x4 = (int) ((x1 + x3) / 2);
			int y4 = (int) ((y1 + y3) / 2);
			int x5 = (int) ((x1 + x2) / 2);
			int y5 = (int) ((y2 + y1) / 2);
			int x6 = (int) ((x3 + x2) / 2);
			int y6 = (int) ((y3 + y2) / 2);

			g.drawLine(x4, y4, x5, y5);
			g.drawLine(x6, y6, x5, y5);
			g.drawLine(x4, y4, x6, y6);

			// 迭代
			x(x1, y1, x5, y5, x4, y4, n);
			x(x5, y5, x2, y2, x6, y6, n);
			x(x4, y4, x6, y6, x3, y3, n);
		}
	}

	@Override
	public void mouseClicked(MouseEvent e) {
		if (index < 3) {
			// 获取三个顶点的坐标
			X[index] = e.getX();
			Y[index] = e.getY();
			System.out.println("第" + (index + 1) + "个点");
			g.fillOval(X[index] - 3, Y[index] - 3, 6, 6);
			index++;
		}

		// 把坐标储存在x_i, y_i中
		x1 = X[0];
		x2 = X[1];
		x3 = X[2];
		y1 = Y[0];
		y2 = Y[1];
		y3 = Y[2];
	}

	public void actionPerformed(ActionEvent e) {
		// TODO Auto-generated method stub
		action = e.getActionCommand();
		if (action.equals("")) {// 颜色
			// 获取事件源
			Object srcObj = e.getSource();
			// 获取按钮
			JButton srcBtn = (JButton) srcObj;
			// 获取颜色
			Color color = srcBtn.getBackground();
			g.setColor(color);
		} else if ("清屏".equals(action)) {
			g.clearRect(0, 0, 2000, 2000);
		} else if (action.equals("生成")) {
			x(x1, y1, x2, y2, x3, y3, n);
		}
	}

	public void stateChanged(ChangeEvent e) {
		// 获取滑动条对象
		Object Ob = e.getSource();
		JSlider Obj = (JSlider) Ob;
		n = Obj.getValue();
		// 清屏
		g.clearRect(0, 0, 2000, 2000);
		// 画顶点
		for (int i = 0; i < 3; i++) {
			g.fillOval(X[i] - 3, Y[i] - 3, 6, 6);
		}
		// 递归
		x(x1, y1, x2, y2, x3, y3, n);
	}

	public void mousePressed(MouseEvent e) {
		// TODO Auto-generated method stub

	}

	public void mouseReleased(MouseEvent e) {
		// TODO Auto-generated method stub

	}

	public void mouseEntered(MouseEvent e) {
		// TODO Auto-generated method stub

	}

	public void mouseExited(MouseEvent e) {
		// TODO Auto-generated method stub
	}

	public void mouseDragged(MouseEvent e) {
		// TODO Auto-generated method stub

	}

	public void mouseMoved(MouseEvent e) {
		// TODO Auto-generated method stub

	}
}

运行

  1. 在画板中随机选三个点。
  2. 点击 “生成”。
  3. 滑动底部的滑动条即可改变谢尔宾斯基三角形的迭代次数。

效果

java递归画谢尔宾斯基三角形
滑动滑动条的滑块:
java递归画谢尔宾斯基三角形