import javafx.animation.AnimationTimer import javafx.application.Application import javafx.application.Platform import javafx.geometry.Pos import javafx.scene.canvas.GraphicsContext import javafx.scene.control.RadioButton import javafx.scene.paint.Color import tornadofx.* import java.util.* import kotlin.math.PI import kotlin.math.cos import kotlin.math.sin fun main(args: Array<String>) { Application.launch(TestApp::class.java, *args) } class TestApp : App(TestView::class) class TestView : View("一顆心到一個圓的演變") { val isRun = booleanProperty() val pNums = intProperty() val aniMate = AniTimer() var t = -PI var renderedList: MutableList<Point> = LinkedList<Point>() val iip = intProperty(2) val step = doubleProperty(0.1) lateinit var context: GraphicsContext override val root = borderpane { top = vbox(10) { style { alignment = Pos.CENTER } hbox(10) { label("點密度:") togglegroup { listOf(0.01, 0.03, 0.05, 0.07, 0.1).map { v -> radiobutton(v.toString(), this, v) { if (v === 0.1) isSelected = true } } selectedToggleProperty().addListener { _, _, newValue -> step.value = (newValue as RadioButton).text.toDouble() runn() } } } hbox(10) { label("花瓣數:") togglegroup { val tg=this vbox(10) { hbox(10) { listOf(2, 3, 4, 5,6,7,8,9,10,11).map { v -> radiobutton(v.toString(), tg, v) { if (v === 2) isSelected = true } } } hbox(10) { listOf(12,13,14,15,16,17,18,19,20).map { v -> radiobutton(v.toString(), tg, v) } } hbox(10) { listOf(21,22,23,24,25,26,27,28,150).map { v -> radiobutton(v.toString(), tg, v) } } } selectedToggleProperty().addListener { _, _, newValue -> iip.value = (newValue as RadioButton).text.toInt() runn() } } button("start") { isRun.addListener { _, _, isrun -> if (isrun) this.text = "Pause" else this.text = "Start" } action { runn() } } label(pNums.stringBinding { "當前點數:$it" }) } } center = canvas(700.0, 550.0) { style { alignment = Pos.CENTER } context = this.graphicsContext2D paddingAll = 30 } } fun runn(){ if (isRun.value) { aniMate.stop() isRun.value = false } else { t = -PI renderedList.clear() pNums.value = 0 aniMate.start() isRun.value = true } } inner class AniTimer : AnimationTimer() { var lastTime = 0L val r = 10.0 var syncLock = Any() // var ii = iip.value override fun handle(now: Long) { if ((now - lastTime) > 10000000) { lastTime = now } else { return } Platform.runLater { val y = 250 + (iip.value * cos(t) - cos(iip.value * t)) * -200/iip.value val x = 300 + (iip.value * sin(t) - sin(iip.value * t)) * 200/iip.value val p = Point(x, y) // 鎖住,防止其餘線程修改 synchronized(syncLock) { // 添加歷史記錄 renderedList.add(p) // 清屏 context.fill = Color.WHITE context.clearRect(0.0, 0.0, 1000.0, 700.0) context.fill = Color.RED // 渲染點 for (point in renderedList) { context.fillOval(point.x, point.y, r, r) } pNums.value += 1 t += step.value // 控制點的數量 if (t > PI) { aniMate.stop() isRun.value = false } } } } } } class Point(val x: Double, val y: Double)