blob: 2abb02c1576f0bf8af37f6183f9e5f891144b231 [file] [log] [blame]
Roman Elizarovf16fd272017-02-07 11:26:00 +03001/*
Roman Elizarov1f74a2d2018-06-29 19:19:45 +03002 * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
Roman Elizarovf16fd272017-02-07 11:26:00 +03003 */
4
Roman Elizarovfbb36d32017-01-23 16:03:54 +03005package examples
6
Roman Elizarov3754f952017-01-18 20:47:54 +03007import javafx.application.Application
8import javafx.scene.Node
9import javafx.scene.Scene
10import javafx.scene.control.Button
11import javafx.scene.layout.FlowPane
12import javafx.scene.layout.Pane
13import javafx.scene.paint.Color
14import javafx.scene.shape.Circle
15import javafx.scene.shape.Rectangle
16import javafx.stage.Stage
Roman Elizarovfbb36d32017-01-23 16:03:54 +030017import kotlinx.coroutines.experimental.CoroutineScope
Roman Elizarov3754f952017-01-18 20:47:54 +030018import kotlinx.coroutines.experimental.Job
19import kotlinx.coroutines.experimental.delay
20import kotlinx.coroutines.experimental.javafx.JavaFx
21import kotlinx.coroutines.experimental.launch
Roman Elizarov49ebab92017-01-24 12:20:06 +030022import java.text.SimpleDateFormat
Roman Elizarov3754f952017-01-18 20:47:54 +030023import java.util.*
24
25fun main(args: Array<String>) {
26 Application.launch(FxTestApp::class.java, *args)
27}
28
Roman Elizarovb5331122017-02-08 16:33:07 +030029fun log(msg: String) = println("${SimpleDateFormat("yyyyMMdd-HHmmss.sss").format(Date())} [${Thread.currentThread().name}] $msg")
Roman Elizarov3754f952017-01-18 20:47:54 +030030
31class FxTestApp : Application() {
32 val buttons = FlowPane().apply {
33 children += Button("Rect").apply {
34 setOnAction { doRect() }
35 }
36 children += Button("Circle").apply {
37 setOnAction { doCircle() }
38 }
39 children += Button("Clear").apply {
40 setOnAction { doClear() }
41 }
42 }
43
44 val root = Pane().apply {
45 children += buttons
46 }
47
48 val scene = Scene(root, 600.0, 400.0)
49
Roman Elizarov23f864e2017-03-03 19:57:47 +030050 override fun start(stage: Stage) {
51 stage.title = "Hello world!"
52 stage.scene = scene
53 stage.show()
Roman Elizarov3754f952017-01-18 20:47:54 +030054 }
55
56 val random = Random()
57 val animations = arrayListOf<Job>()
58 var animationIndex = 0
59
Roman Elizarovfbb36d32017-01-23 16:03:54 +030060 private fun animation(node: Node, block: suspend CoroutineScope.() -> Unit) {
Roman Elizarov3754f952017-01-18 20:47:54 +030061 root.children += node
Roman Elizarov32d95322017-02-09 15:57:31 +030062 val job = launch(JavaFx, block = block)
Roman Elizarov3754f952017-01-18 20:47:54 +030063 animations += job
Roman Elizarove7803472017-02-16 09:52:31 +030064 job.invokeOnCompletion { root.children -= node }
Roman Elizarov3754f952017-01-18 20:47:54 +030065 }
66
67 fun doRect() {
68 val node = Rectangle(20.0, 20.0).apply {
69 fill = Color.RED
70 }
71 val index = ++animationIndex
72 val speed = 5.0
73 animation(node) {
74 log("Started new 'rect' coroutine #$index")
75 var vx = speed
76 var vy = speed
77 var counter = 0
78 while (true) {
79 JavaFx.awaitPulse()
80 node.x += vx
81 node.y += vy
82 val xRange = 0.0 .. scene.width - node.width
83 val yRange = 0.0 .. scene.height - node.height
84 if (node.x !in xRange ) {
85 node.x = node.x.coerceIn(xRange)
86 vx = -vx
87 }
88 if (node.y !in yRange) {
89 node.y = node.y.coerceIn(yRange)
90 vy = -vy
91 }
92 if (counter++ > 100) {
93 counter = 0
94 delay(1000) // pause a bit
95 log("Delayed #$index for a while, resume and turn")
96 val t = vx
97 vx = vy
98 vy = -t
99 }
100 }
101 }
102 }
103
104 fun doCircle() {
105 val node = Circle(20.0).apply {
106 fill = Color.BLUE
107 }
108 val index = ++animationIndex
109 val acceleration = 0.1
110 val maxSpeed = 5.0
111 animation(node) {
112 log("Started new 'circle' coroutine #$index")
113 var sx = random.nextDouble() * maxSpeed
114 var sy = random.nextDouble() * maxSpeed
115 while (true) {
116 JavaFx.awaitPulse()
117 val dx = root.width / 2 - node.translateX
118 val dy = root.height / 2 - node.translateY
119 val dn = Math.sqrt(dx * dx + dy * dy)
120 sx += dx / dn * acceleration
121 sy += dy / dn * acceleration
122 val sn = Math.sqrt(sx * sx + sy * sy)
123 val trim = sn.coerceAtMost(maxSpeed)
124 sx = sx / sn * trim
125 sy = sy / sn * trim
126 node.translateX += sx
127 node.translateY += sy
128 }
129 }
130 }
131
132 fun doClear() {
133 animations.forEach { it.cancel() }
134 }
135}