Как стать автором
Обновить

Фрактальный кустик от новичка для новичков

Время на прочтение4 мин
Количество просмотров22K
Как я уже упоминал, я начинающий (и самый скромный во Вселенной) программист на Java. Иногда душа просит чего-то такого, вот прям чтоб сначала развернулась, а потом опять свернулась. Красоты хочется. А красоту рисовать мы еще не обучены. Зато обучены рисовать палочки и кружочки.

И с боевым кличем «Красота в простоте!», рисуем из палочек. А что мы можем нарисовать красивое и простое, да чтоб коллеги ахнули в восторге? И тут на помощь приходит красивое слово – Фрактал.

Сначала определение: «Фрактал – это структура из частей… бла-бла-бла… самоподобие… бла-бла-бла… красиво… бла-бла-бла...».



И вооруженные этим исчерпывающим знанием давайте нарисуем фрактальное дерево, а точнее кустик.

Для начала создадим оболочку откуда будем всё запускать:

Класс FractalTreeTest
public class FractalTreeTest {

	public static void main(String[] args) {
		EventQueue.invokeLater(new Runnable() {		
			public void run() {
				JFractalFrame frame = new JFractalFrame();
				frame.setVisible(true);
			}
		});
	}
}


Теперь создадим класс, отвечающий за вывод на экран окна и запуск анимации:

Класс JFractalFrame
class JFractalFrame extends JFrame{
	boolean startAnimation=false;
	JPanel paintPanel;
	
	public JFractalFrame() {
		setTitle("FractalTree");
		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		paintPanel = new PaintPanel();
		paintPanel.setBackground(Color.DARK_GRAY);
		add(paintPanel);
		requestFocus();//Обращаем фокус на наш фрейм. Без этой строки, реакции не будет
		// А здесь отлавливаем любое нажатие на кнопку, и запускаем анимацию
		addKeyListener(new KeyAdapter() {
			public void keyPressed(KeyEvent e){
				if(e.getKeyCode()>0 && !startAnimation){
					pack();
					startAnimation=true;
					startAnimation ();
				}
			}
		});
		paintPanel.repaint();
		pack();
	}
	
	public void startAnimation(){
		Runnable galaxyRun = new FractalRunnable((PaintPanel) paintPanel);
		Thread t = new Thread(galaxyRun);
		t.start();
	}
}


Класс с панелью, на которой отрисовывается все действие. Именно в ней содержится рекурсивная функция fractal, отрисовывающая новыве ветки:

Класс PaintPanel
class PaintPanel extends JPanel{
	List<Branch> listBranch =  new CopyOnWriteArrayList<>();
	double angle=0;
		
	public void setAngle(double angle) {
		this.angle = angle;
	}
	//Рекурсивная функция, в которой отрисовываются две новые ветки исходящие из предыдущей, и добавляются в список
           //в неё передается длина ветки, точка начала отрисовки, угол наклона, и шаг рекурсии
	public void fractal(int startLength, Point2D startPoint, double alpha, int step){
		if(alpha<0) alpha=360;
		double radian =(alpha/(180/Math.PI));
		Point2D endPoint1 = new Point2D();
		Point2D endPoint2 = new Point2D();
				
		endPoint1.setX((float) (startPoint.getX()-startLength*Math.cos(radian)));
		endPoint1.setY((float) (startPoint.getY()-startLength*Math.sin(radian)));
		addBranch(new Branch(startPoint, endPoint1, startLength));
		
		endPoint2.setX((float) (startPoint.getX()-startLength*Math.cos(radian)));
		endPoint2.setY((float) (startPoint.getY()-startLength*Math.sin(radian)));
		addBranch(new Branch(startPoint, endPoint2, startLength));
		
		if(step>0){
			step--;
			startLength-=4; //уменьшаем длину ветки
			//попробуйте поэкспериментировать в следующих строках со знаками и числами. Можете 
                                   //получить интересные варианты.
			fractal(startLength, endPoint1, alpha-(20+angle),step); //angle понадобится для анимации
			fractal(startLength, endPoint2, alpha+(20-angle), step);	
		}
	}
	
	public void addBranch(Branch b){
		listBranch.add(b);
	}
	
	public void paintComponent(Graphics g){
		super.paintComponent(g);
//Отрисовываем в середине экрана, с начальной длиной ветки 60, углом 90, и на 10 шагов
		fractal(60, new Point2D(320, 480), 90, 10);
		Random randomX = new Random();
		Graphics2D g2d = (Graphics2D)g;
		for(Branch b: listBranch){
		// Можно отрисовывать так, но получится «сумасшедшее» дискотечное дерево	
g2d.setColor(new Color(randomX.nextInt(255),randomX.nextInt(255),randomX.nextInt(255)));
		//Если закомментировать предыдущую строку, и раскомментировать следующий код, 
               //то ствол, ветви и листья будут отрисовываться разным цветом
			/*
			if(b.length>30)
				g2d.setColor(Color.ORANGE.darker());
			else
				g2d.setColor(Color.GREEN);
				*/
			g2d.draw(b.getShape());
			
		}
	//после отрисовки очищаем список, чтобы можно было принять новый
		listBranch.clear();
	}
//задаем размер панели отрисовки
	public Dimension getPreferredSize() {
		// TODO Auto-generated method stub
		return new Dimension(640,480);
	}	
}


Класс двумерной точки:

Класс Point2D
class Point2D{
	
private float x, y;
	//создаем точку по двум координатам
	public Point2D(float x, float y) {
		this.x=x;
		this.y = y;
	}
	
	public Point2D(){
		
	}
	
	public void setX(float x) {
		this.x = x;
	}
	
	public void setY(float y) {
		this.y = y;
	}
	public float getX() {
		return x;
	}
	public float getY() {
		return y;
	}

}


Класс отвечающий за отрисовку ветки:

Класс Branch
class Branch{
	Point2D begin;
	Point2D end;
	int length;
	//Строим ветку по двум точкам	
	public Branch(Point2D begin, Point2D end, int length) {
		this.begin=begin;
		this.end=end;
		this.length=length;
	}
	//рисуем прямую линию по заданным координатам
	public Line2D getShape(){
		return new Line2D.Double(begin.getX(), begin.getY(), end.getX(), end.getY());
	}
	
}


И наконец класс Runnable, в котором будет крутиться наша анимация:

Класс FractalRunnable
class FractalRunnable implements Runnable{
	PaintPanel paintPanel;
	
	public FractalRunnable(PaintPanel paintPanel) {
		// TODO Auto-generated constructor stub
		this.paintPanel=paintPanel;
	}
	
	public void run() {
		double count=0;
		boolean leftDir = true;
		while(true){
		//»Что стоишь качаясь, до самого тына…» С.Есенин
		//Высота тына регулируется переменной count
			if(count>8 && a<count){
				leftDir=false;
			}
			
			if(count<-8 && count>-9){
				leftDir=true;
				}
			if(leftDir)
				count+=0.01;
			else
				count-=0.01;
				
			paintPanel.setAngle(a);
			paintPanel.repaint();
			try {
				Thread.sleep(5);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		
	}
}


Запускаем, нажимаем на любую кнопку на клавиатуре, и радуемся.

Засим позвольте откланяться.
Теги:
Хабы:
Всего голосов 26: ↑19 и ↓7+12
Комментарии20

Публикации

Истории

Работа

Java разработчик
367 вакансий

Ближайшие события

15 – 16 ноября
IT-конференция Merge Skolkovo
Москва
22 – 24 ноября
Хакатон «AgroCode Hack Genetics'24»
Онлайн
28 ноября
Конференция «TechRec: ITHR CAMPUS»
МоскваОнлайн
25 – 26 апреля
IT-конференция Merge Tatarstan 2025
Казань