2.1 题目描述
绘制一个简单的分形树,如图2.1:
图2.1 分形树
先垂直绘制一根线段,然后在线段顶端向右一定倾角绘制一根线段,长度分别为原线段的k倍,再同样的,在线段左侧以固定倾角绘制一根线段,如此反复,直至线段长度小于某个较小的值。其中,线条颜色以及长度粗细,夹角(例如产 生某个范围的随机数)都可以自行进行微调。
2.2 程序使用说明
- 启动程序;
2. 等待树枝绘制完成。2.3 分析和设计
- 思路
绘制一条树枝需要知道树枝的起点、偏角以及长度。树枝的起点为枝干的终点,右树枝的的偏角为枝干偏角减去左右树枝夹角的一半,左树枝的偏角为枝干偏角加上左右树枝夹角的一半。左右树枝绘制完成后,再以当前左右树枝为枝干绘制它们的树枝,直到树枝足够短。
2. 伪代码GenerateTree(startX, startY, angle, length)
// 输入:树枝的起点startX、startY,枝干的偏角angle,树枝长度length
// 输出:分形树
// 随机产生一个角度作为左右树枝夹角的一半
randAngle <- randam()
leftEndX <- startX + cos((angle+randAngle)/180*PI)*length*0.8
// 向上的y值较小
leftEndY <- startY - sin((angle+randAngle)/180*PI)*length*0.8
// 绘制树枝
draw(startX, startY, leftEndX, leftEndY)
GenerateTree(leftEndX, leftEndY, angle+randAngle, length*0.8)
rightEndX <- startX + cos((angle-randAngle)/180*PI)*length*0.8
// 向上的y值较小
rightEndY <- startY - sin((angle-randAngle)/180*PI)*length*0.8
// 绘制树枝
draw(startX, startY, rightEndX, rightEndY)
GenerateTree(rightEndX, rightEndY, angle-randAngle, length*0.8)
- 时间复杂度
分开树是一棵二叉树,所以时间复杂度为O(2n)。2.4 测试用例
测试效果如图2.1所示。
图2.1 分形树2.5 源代码(含注释)
import javafx.application.Application;
import javafx.application.Platform;
import javafx.scene.Scene;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Line;
import javafx.stage.Stage;
import java.util.Random;
public class FractalTree extends Application {
private Pane tree;
@Override
public void start(Stage primaryStage) throws Exception {
tree = new Pane();
double length = 100.0;
int startX = 500;
int startY = 600;
double stokeWidth = 15;
Line line = new Line(startX, startY, startX, startY-length);
line.setStrokeWidth(stokeWidth);
line.setStroke(Color.valueOf("rgb(0,0,0)"));
tree.getChildren().add(line);
Scene scene = new Scene(tree, startX*2, startY);
primaryStage.setScene(scene);
primaryStage.show();
generateBranch(length, startX, startY-length, 90, stokeWidth*0.8, 0);
}
/**
* 画树枝
* @param length 树枝长度
* @param startX 树枝的起始X坐标
* @param startY 树枝的起始X坐标
* @param angle 当前枝干的偏角
* @param strokeWidth 宽度
*/
private void generateBranch(double length, double startX, double startY, double angle, double strokeWidth, int red){
if (red > 255){
red = 255;
}
if (length < 8){
return ;
}
// 15度以上的开角,以枝干为轴,各树枝偏移一半
double randAngle = (15+new Random(System.currentTimeMillis()).nextInt(60))/2;
double rightEndX = startX+Math.cos((angle-randAngle)/180*Math.PI)*length;
double rightEndY = startY-Math.sin((angle-randAngle)/180*Math.PI)*length;
Line right = new Line(startX, startY, rightEndX, rightEndY);
right.setStrokeWidth(strokeWidth);
right.setStroke(Color.valueOf("rgb("+red+",0,0)"));
int finalRed = red;
new Thread(() -> {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
Platform.runLater(() -> tree.getChildren().add(right));
generateBranch(length*0.8, rightEndX, rightEndY, angle-randAngle, strokeWidth*0.8, finalRed+15);
}).start();
double leftEndX = startX+Math.cos((angle+randAngle)/180*Math.PI)*length;
double leftEndY = startY-Math.sin((angle+randAngle)/180*Math.PI)*length;
Line left = new Line(startX, startY, leftEndX, leftEndY);
left.setStrokeWidth(strokeWidth);
left.setStroke(Color.valueOf("rgb("+red+",0,0)"));
new Thread(() -> {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
Platform.runLater(() -> tree.getChildren().add(left));
generateBranch(length*0.8, leftEndX, leftEndY, angle+randAngle, strokeWidth*0.8, finalRed+15);
}).start();
}
public static void main(String[] args) {
launch(args);
}
}