用javafx框架tornadofx演示分形图的绘制

2019-10-14 17:21:12 浏览数 (1)

用到了kotlin 协程、带方法的枚举类等知识点

代码语言:txt复制
import javafx.beans.property.SimpleObjectProperty
import javafx.geometry.Orientation
import javafx.scene.canvas.Canvas
import javafx.scene.canvas.GraphicsContext
import javafx.scene.control.TextFormatter
import javafx.scene.control.ToggleGroup
import javafx.scene.paint.Color
import kotlinx.coroutines.*
import tornadofx.*
import kotlin.math.PI
import kotlin.math.max
import kotlin.math.sin

class FractalApp : App(FractalView::class)
class FractalController : Controller() {

    val v by inject<FractalView>()
    val canvas0 = v.canvas0

   fun draw(fs: FractalShape1)= GlobalScope.launch {
       canvas0.graphicsContext2D.clearRect(0.0, 0.0, canvas0.width, canvas0.height)
       fs.drawFractal(canvas0.graphicsContext2D,
               v.x.value,
               v.y.value,
               v.widthh.value.toDouble(),
               v.heightt.value.toDouble(),
               v.depth.value,
               v.maxDepth.value,
               v.color.value)
   }
}

class FractalView : View("分形图动画演示") {
    val c by inject<FractalController>()
    val fractal = SimpleObjectProperty(this, "fractal", FractalShape1.Rectangle)
    val x = doubleProperty()
    val y = doubleProperty()
    val widthh = intProperty(700)
    val heightt = intProperty(600)
    val depth = intProperty(4)
    val maxDepth = intProperty(6)
    val color = SimpleObjectProperty(this, "color", MyColor1.Indigo)

    lateinit var canvas0: Canvas
    lateinit var shapeGoup: ToggleGroup

    // 过滤输入,只能输入1以上的正整数
    val firstTenFilter: (TextFormatter.Change) -> Boolean = { change ->
        !change.isAdded || change.controlNewText.let {
            it.isInt() && it.toInt() in 1..Int.MAX_VALUE
        }
    }
    override val root = borderpane {
        style = "-fx-font-size: 14pt; "
        right = vbox(5) {
            prefWidth=300.0
            form {
                fieldset(labelPosition = Orientation.HORIZONTAL) {
                    vbox(5) {
                        field("x:") {
                            textfield(x) {
                                text = "10"
                                filterInput(firstTenFilter)
                            }
                        }
                        field("y:") {
                            textfield(y) {
                                text = "10"
                                filterInput(firstTenFilter)
                            }
                        }
                        field("widthh:") {
                            spinner(100, 1000, 400, 100, false, widthh)
                        }
                        field("heightt:") {
                            spinner(100, 1000, 400, 100, false, heightt)
                        }
                        field("depth:") {
                            spinner(1, 10, 4, 1, false, depth)
                        }
                        field("maxDepth:") {
                            spinner(1, 10, 4, 1, false, maxDepth)
                        }
                    }
                    vbox(5) {
                        field("color:") {
                            combobox(property = color, values = MyColor1.all) {
                                selectionModel.selectedItemProperty().addListener { _, _, _ ->
                                    c.draw(fractal.value)
                                }
                            }
                        }
                        field("depth:") {
                            slider(1, 10, 4) {
                                isShowTickLabels = true
                                isShowTickMarks = true
                                valueProperty().bindBidirectional(depth)
                            }
                        }
                        field("maxDepth:") {
                            slider(1, 10, 6) {
                                isShowTickLabels = true
                                isShowTickMarks = true
                                valueProperty().bindBidirectional(maxDepth)
                            }
                        }

                        shapeGoup = togglegroup {
                            FractalShape1.values().forEach { fs ->
                                radiobutton(fs.name) {
                                    action {
                                        // 实现ToggleGroup与combobox联动
                                        fractal.value = fs
                                        c.draw(fs)
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
        center = stackpane {
            canvas0 = canvas(800.0, 700.0)
            paddingAll = 10
        }
    }
}

enum class MyColor1(val color: Color) {
    Red(Color.RED), Blue(Color.BLUE), Indigo(Color.INDIGO), Yellow(Color.YELLOW);

    companion object {
        val all by lazy { values().toList() }
    }
}

//分形形状,矩形、圆、椭圆
enum class FractalShape1 {
    Rectangle {
        override suspend fun drawFractal(g: GraphicsContext, x: Double, y: Double, w: Double, h: Double, depth: Int, maxDepth: Int, color: MyColor1) {
            g.fill = color.color
            if (depth == maxDepth) {
                g.fillRect(x, y, w, h)
                return
            }
            if ((w <= 1).and(h <= 1)) {
                g.fillRect(x, y, max(w, 1.0), max(h, 1.0))
                return
            }
            val w_3 = w / 3
            val h_3 = h / 3
            delay(delay0)
            drawFractal(g, x, y, w_3, h_3, depth   1, maxDepth, color)
            delay(delay0)

            drawFractal(g, x   2 * w_3, y, w_3, h_3, depth   1, maxDepth, color)
            delay(delay0)

            drawFractal(g, x   w_3, y   h_3, w_3, h_3, depth   1, maxDepth, color)
            delay(delay0)

            drawFractal(g, x, y   2 * h_3, w_3, h_3, depth   1, maxDepth, color)
            delay(delay0)

            drawFractal(g, x   2 * w_3, y   2 * h_3, w_3, h_3, depth   1, maxDepth, color)
            return
        }
    },
    Circle {
        override suspend fun drawFractal(g: GraphicsContext, x: Double, y: Double, w: Double, h: Double, depth: Int, maxDepth: Int, color: MyColor1) {
            g.fill = color.color

            if (depth == maxDepth) {
                g.fillOval(x, y, w, w)
                return
            }
            if (w <= 1) {
                g.fillOval(x, y, max(w, 1.0), max(w, 1.0))
                return
            }

            val w_3 = w / 3
            val h_3 = h / 3
            delay(delay0)

            drawFractal(g, x, y, w_3, h_3, depth   1, maxDepth, color)
            delay(delay0)
            drawFractal(g, x   2 * w_3, y, w_3, h_3, depth   1, maxDepth, color)
            delay(delay0)
            drawFractal(g, x   w_3, y   h_3, w_3, h_3, depth   1, maxDepth, color)
            delay(delay0)
            drawFractal(g, x, y   2 * h_3, w_3, h_3, depth   1, maxDepth, color)
            delay(delay0)
            drawFractal(g, x   2 * w_3, y   2 * h_3, w_3, h_3, depth   1, maxDepth, color)
            return
        }
    },
    Triangle {
        override suspend fun drawFractal(g: GraphicsContext, x: Double, y: Double, w: Double, h: Double, depth: Int, maxDepth: Int, color: MyColor1) {
            var side = w
            val y=y-20
            if (side <= 1) {
                Rectangle.drawFractal(g, x, y, 1.0, 1.0, depth, maxDepth, color)
                return
            }

            val bx = x   side
            val by = y
            val h = sin(60 * PI / 180) * side

            val cx = x   side / 2
            val cy = y - h
            if (depth == maxDepth) {
                drawTriangle(g, x, y, bx, by, cx, cy, color)
                return
            }

            val ab_centerx = (x   bx) / 2
            val ab_centery = (y   by) / 2
            val ac_centerx = (x   cx) / 2
            val ac_centery = (y   cy) / 2

            delay(delay0)

            // 左下角三角形
            drawFractal(g, x, y, w / 2, h, depth   1, maxDepth, color)
            delay(delay0)
// 上三角形
            drawFractal(g, ac_centerx, ac_centery, w / 2, h, depth   1, maxDepth, color)
            delay(delay0)
// 右下角三角形
            drawFractal(g, ab_centerx, ab_centery, w / 2, h, depth   1, maxDepth, color)
        }
    },
    Ellipse {
        override suspend fun drawFractal(g: GraphicsContext, x: Double, y: Double, w: Double, h: Double, depth: Int, maxDepth: Int, color: MyColor1) {
            g.fill = color.color
            if (depth == maxDepth) {
                g.fillOval(x, y, w, h)
                return
            }
            if ((w <= 1).and(h <= 1)) {
                g.fillOval(x, y, max(w, 1.0), max(h, 1.0))
                return
            }
            delay(delay0)

            val w_3 = w / 3
            val h_3 = h / 3*0.8
            drawFractal(g, x, y, w_3, h_3, depth   1, maxDepth, color)
            delay(delay0)
            drawFractal(g, x   2 * w_3, y, w_3, h_3, depth   1, maxDepth, color)
            delay(delay0)
            drawFractal(g, x   w_3, y   h_3, w_3, h_3, depth   1, maxDepth, color)
            delay(delay0)
            drawFractal(g, x, y   2 * h_3, w_3, h_3, depth   1, maxDepth, color)
            delay(delay0)
            drawFractal(g, x   2 * w_3, y   2 * h_3, w_3, h_3, depth   1, maxDepth, color)
            return
        }
    };

    companion object {
        val delay0=100L
        val all by lazy { values().toList() }
    }

    fun drawTriangle(g: GraphicsContext, ax: Double, ay: Double, bx: Double, by: Double, cx: Double, cy: Double, color: MyColor1) {
        g.fill = color.color
        g.strokePolygon(doubleArrayOf(ax, bx, cx, ax), doubleArrayOf(-ay, -by, -cy, -ay), 3)
    }

    abstract suspend fun drawFractal(g: GraphicsContext, x: Double, y: Double, w: Double, h: Double, depth: Int, maxDepth: Int, color: MyColor1)
}

0 人点赞