2.1 题目描述

绘制一个简单的分形树,如图2.1:
image.png
图2.1 分形树
先垂直绘制一根线段,然后在线段顶端向右一定倾角绘制一根线段,长度分别为原线段的k倍,再同样的,在线段左侧以固定倾角绘制一根线段,如此反复,直至线段长度小于某个较小的值。其中,线条颜色以及长度粗细,夹角(例如产 生某个范围的随机数)都可以自行进行微调。

2.2 程序使用说明

  1. 启动程序;
    2. 等待树枝绘制完成。

    2.3 分析和设计

  2. 思路
    绘制一条树枝需要知道树枝的起点、偏角以及长度。树枝的起点为枝干的终点,右树枝的的偏角为枝干偏角减去左右树枝夹角的一半,左树枝的偏角为枝干偏角加上左右树枝夹角的一半。左右树枝绘制完成后,再以当前左右树枝为枝干绘制它们的树枝,直到树枝足够短。
    2. 伪代码
    1. GenerateTree(startX, startY, angle, length)
    2. // 输入:树枝的起点startX、startY,枝干的偏角angle,树枝长度length
    3. // 输出:分形树
    4. // 随机产生一个角度作为左右树枝夹角的一半
    5. randAngle <- randam()
    6. leftEndX <- startX + cos((angle+randAngle)/180*PI)*length*0.8
    7. // 向上的y值较小
    8. leftEndY <- startY - sin((angle+randAngle)/180*PI)*length*0.8
    9. // 绘制树枝
    10. draw(startX, startY, leftEndX, leftEndY)
    11. GenerateTree(leftEndX, leftEndY, angle+randAngle, length*0.8)
    12. rightEndX <- startX + cos((angle-randAngle)/180*PI)*length*0.8
    13. // 向上的y值较小
    14. rightEndY <- startY - sin((angle-randAngle)/180*PI)*length*0.8
    15. // 绘制树枝
    16. draw(startX, startY, rightEndX, rightEndY)
    17. GenerateTree(rightEndX, rightEndY, angle-randAngle, length*0.8)
  3. 时间复杂度
    分开树是一棵二叉树,所以时间复杂度为O(2n)。

    2.4 测试用例

    测试效果如图2.1所示。
    image.png
    图2.1 分形树

    2.5 源代码(含注释)

    1. import javafx.application.Application;
    2. import javafx.application.Platform;
    3. import javafx.scene.Scene;
    4. import javafx.scene.layout.Pane;
    5. import javafx.scene.paint.Color;
    6. import javafx.scene.shape.Line;
    7. import javafx.stage.Stage;
    8. import java.util.Random;
    9. public class FractalTree extends Application {
    10. private Pane tree;
    11. @Override
    12. public void start(Stage primaryStage) throws Exception {
    13. tree = new Pane();
    14. double length = 100.0;
    15. int startX = 500;
    16. int startY = 600;
    17. double stokeWidth = 15;
    18. Line line = new Line(startX, startY, startX, startY-length);
    19. line.setStrokeWidth(stokeWidth);
    20. line.setStroke(Color.valueOf("rgb(0,0,0)"));
    21. tree.getChildren().add(line);
    22. Scene scene = new Scene(tree, startX*2, startY);
    23. primaryStage.setScene(scene);
    24. primaryStage.show();
    25. generateBranch(length, startX, startY-length, 90, stokeWidth*0.8, 0);
    26. }
    27. /**
    28. * 画树枝
    29. * @param length 树枝长度
    30. * @param startX 树枝的起始X坐标
    31. * @param startY 树枝的起始X坐标
    32. * @param angle 当前枝干的偏角
    33. * @param strokeWidth 宽度
    34. */
    35. private void generateBranch(double length, double startX, double startY, double angle, double strokeWidth, int red){
    36. if (red > 255){
    37. red = 255;
    38. }
    39. if (length < 8){
    40. return ;
    41. }
    42. // 15度以上的开角,以枝干为轴,各树枝偏移一半
    43. double randAngle = (15+new Random(System.currentTimeMillis()).nextInt(60))/2;
    44. double rightEndX = startX+Math.cos((angle-randAngle)/180*Math.PI)*length;
    45. double rightEndY = startY-Math.sin((angle-randAngle)/180*Math.PI)*length;
    46. Line right = new Line(startX, startY, rightEndX, rightEndY);
    47. right.setStrokeWidth(strokeWidth);
    48. right.setStroke(Color.valueOf("rgb("+red+",0,0)"));
    49. int finalRed = red;
    50. new Thread(() -> {
    51. try {
    52. Thread.sleep(10);
    53. } catch (InterruptedException e) {
    54. e.printStackTrace();
    55. }
    56. Platform.runLater(() -> tree.getChildren().add(right));
    57. generateBranch(length*0.8, rightEndX, rightEndY, angle-randAngle, strokeWidth*0.8, finalRed+15);
    58. }).start();
    59. double leftEndX = startX+Math.cos((angle+randAngle)/180*Math.PI)*length;
    60. double leftEndY = startY-Math.sin((angle+randAngle)/180*Math.PI)*length;
    61. Line left = new Line(startX, startY, leftEndX, leftEndY);
    62. left.setStrokeWidth(strokeWidth);
    63. left.setStroke(Color.valueOf("rgb("+red+",0,0)"));
    64. new Thread(() -> {
    65. try {
    66. Thread.sleep(10);
    67. } catch (InterruptedException e) {
    68. e.printStackTrace();
    69. }
    70. Platform.runLater(() -> tree.getChildren().add(left));
    71. generateBranch(length*0.8, leftEndX, leftEndY, angle+randAngle, strokeWidth*0.8, finalRed+15);
    72. }).start();
    73. }
    74. public static void main(String[] args) {
    75. launch(args);
    76. }
    77. }