Как я уже упоминал, я начинающий (и самый скромный во Вселенной) программист на Java. Иногда душа просит чего-то такого, вот прям чтоб сначала развернулась, а потом опять свернулась. Красоты хочется. А красоту рисовать мы еще не обучены. Зато обучены рисовать палочки и кружочки.
И с боевым кличем «Красота в простоте!», рисуем из палочек. А что мы можем нарисовать красивое и простое, да чтоб коллеги ахнули в восторге? И тут на помощь приходит красивое слово – Фрактал.
Сначала определение: «Фрактал – это структура из частей… бла-бла-бла… самоподобие… бла-бла-бла… красиво… бла-бла-бла...».
И вооруженные этим исчерпывающим знанием давайте нарисуем фрактальное дерево, а точнее кустик.
Для начала создадим оболочку откуда будем всё запускать:
Теперь создадим класс, отвечающий за вывод на экран окна и запуск анимации:
Класс с панелью, на которой отрисовывается все действие. Именно в ней содержится рекурсивная функция fractal, отрисовывающая новыве ветки:
Класс двумерной точки:
Класс отвечающий за отрисовку ветки:
И наконец класс Runnable, в котором будет крутиться наша анимация:
Запускаем, нажимаем на любую кнопку на клавиатуре, и радуемся.
Засим позвольте откланяться.
И с боевым кличем «Красота в простоте!», рисуем из палочек. А что мы можем нарисовать красивое и простое, да чтоб коллеги ахнули в восторге? И тут на помощь приходит красивое слово – Фрактал.
Сначала определение: «Фрактал – это структура из частей… бла-бла-бла… самоподобие… бла-бла-бла… красиво… бла-бла-бла...».
И вооруженные этим исчерпывающим знанием давайте нарисуем фрактальное дерево, а точнее кустик.
Для начала создадим оболочку откуда будем всё запускать:
Класс 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();
}
}
}
}
Запускаем, нажимаем на любую кнопку на клавиатуре, и радуемся.
Засим позвольте откланяться.