From 0ca59b3a40721aaa35c1b12bd2b8289e4e524901 Mon Sep 17 00:00:00 2001 From: Ilya Gorbunov Date: Tue, 18 Sep 2018 02:26:36 +0300 Subject: [PATCH] Use modern API of Kotlin 1.3 in JS canvas examples --- .../webDemoCanvasExamples/Creatures.kt | 70 +++++------ .../webDemoCanvasExamples/Fancy lines.kt | 28 +++-- .../webDemoCanvasExamples/Hello, Kotlin.kt | 34 ++--- .../webDemoCanvasExamples/Traffic light.kt | 117 +++++++----------- 4 files changed, 112 insertions(+), 137 deletions(-) diff --git a/js/js.translator/testData/webDemoCanvasExamples/Creatures.kt b/js/js.translator/testData/webDemoCanvasExamples/Creatures.kt index 84d8e592887..f64d665ee6d 100644 --- a/js/js.translator/testData/webDemoCanvasExamples/Creatures.kt +++ b/js/js.translator/testData/webDemoCanvasExamples/Creatures.kt @@ -1,13 +1,15 @@ /* - In this example strange creatures are watching the kotlin logo. You can drag'n'drop them as well as the logo. - Doubleclick to add more creatures but be careful. They may be watching you! -*/ + * In this example strange creatures are watching the kotlin logo. + * You can drag'n'drop them as well as the logo. Doubleclick to add + * more creatures but be careful. They may be watching you! + */ package creatures import jquery.* -import kotlin.js.* -import kotlin.browser.window import org.w3c.dom.* +import kotlin.browser.document +import kotlin.browser.window +import kotlin.math.* fun getImage(path: String): HTMLImageElement { @@ -30,7 +32,8 @@ abstract class Shape() { abstract fun draw(state: CanvasState) // these two abstract methods defines that our shapes can be dragged - abstract operator fun contains(mousePos: Vector): Boolean + operator abstract fun contains(mousePos: Vector): Boolean + abstract var pos: Vector var selected: Boolean = false @@ -57,9 +60,9 @@ abstract class Shape() { val Kotlin = Logo(v(250.0, 75.0)) class Logo(override var pos: Vector) : Shape() { - val relSize: Double = 0.25 + val relSize: Double = 0.15 val shadowOffset = v(-3.0, 3.0) - val imageSize = v(377.0, 393.0) + val imageSize = v(200.0, 200.0) var size: Vector = imageSize * relSize // get-only properties like this saves you lots of typing and are very expressive val position: Vector @@ -69,7 +72,7 @@ class Logo(override var pos: Vector) : Shape() { fun drawLogo(state: CanvasState) { size = imageSize * (state.size.x / imageSize.x) * relSize // getKotlinLogo() is a 'magic' function here defined only for purposes of demonstration but in fact it just find an element containing the logo - state.context.drawImage(getImage("http://kotlin-demo.jetbrains.com/static/images/kotlinlogowobackground.png"), 0.0, 0.0, + state.context.drawImage(getImage("http://try.kotlinlang.org/static/images/canvas/Kotlin-logo.png"), 0.0, 0.0, imageSize.x, imageSize.y, position.x, position.y, size.x, size.y) @@ -82,8 +85,7 @@ class Logo(override var pos: Vector) : Shape() { context.shadowed(shadowOffset, 0.2) { drawLogo(state) } - } - else { + } else { drawLogo(state) } } @@ -114,7 +116,7 @@ class Creature(override var pos: Vector, val state: CanvasState) : Shape() { // defining more nice extension functions fun CanvasRenderingContext2D.circlePath(position: Vector, rad: Double) { - arc(position.x, position.y, rad, 0.0, 2 * Math.PI, false) + arc(position.x, position.y, rad, 0.0, 2 * PI, false) } //notice we can use an extension function we just defined inside another extension function @@ -128,8 +130,7 @@ class Creature(override var pos: Vector, val state: CanvasState) : Shape() { val context = state.context if (!selected) { drawCreature(context) - } - else { + } else { drawCreatureWithShadow(context) } } @@ -156,7 +157,7 @@ class Creature(override var pos: Vector, val state: CanvasState) : Shape() { val tailDirection = -directionToLogo val tailPos = position + tailDirection * radius * 1.0 val tailSize = radius * 1.6 - val angle = Math.PI / 6.0 + val angle = PI / 6.0 val p1 = tailPos + tailDirection.rotatedBy(angle) * tailSize val p2 = tailPos + tailDirection.rotatedBy(-angle) * tailSize val middlePoint = position + tailDirection * radius * 1.0 @@ -189,13 +190,13 @@ class Creature(override var pos: Vector, val state: CanvasState) : Shape() { } class CanvasState(val canvas: HTMLCanvasElement) { - var width = canvas.width.toDouble() - var height = canvas.height.toDouble() + var width = canvas.width + var height = canvas.height val size: Vector - get() = v(width, height) + get() = v(width.toDouble(), height.toDouble()) val context = creatures.context var valid = false - var shapes = ArrayList() + var shapes = mutableListOf() var selection: Shape? = null var dragOff = Vector() val interval = 1000 / 30 @@ -246,7 +247,7 @@ class CanvasState(val canvas: HTMLCanvasElement) { var element: HTMLElement? = canvas while (element != null) { val el: HTMLElement = element - offset += Vector(el.offsetLeft, el.offsetTop) + offset += Vector(el.offsetLeft.toDouble(), el.offsetTop.toDouble()) element = el.offsetParent as HTMLElement? } return Vector(e.pageX, e.pageY) - offset @@ -259,17 +260,17 @@ class CanvasState(val canvas: HTMLCanvasElement) { fun clear() { context.fillStyle = "#FFFFFF" - context.fillRect(0.0, 0.0, width, height) + context.fillRect(0.0, 0.0, width.toDouble(), height.toDouble()) context.strokeStyle = "#000000" context.lineWidth = 4.0 - context.strokeRect(0.0, 0.0, width, height) + context.strokeRect(0.0, 0.0, width.toDouble(), height.toDouble()) } fun draw() { if (valid) return clear() - for (shape in shapes.reversed()) { + for (shape in shapes.asReversed()) { shape.draw(this) } Kotlin.draw(this) @@ -278,7 +279,7 @@ class CanvasState(val canvas: HTMLCanvasElement) { } class RadialGradientGenerator(val context: CanvasRenderingContext2D) { - val gradients = ArrayList>>() + val gradients = mutableListOf>>() var current = 0 fun newColorStops(vararg colorStops: Pair) { @@ -302,29 +303,26 @@ class RadialGradientGenerator(val context: CanvasRenderingContext2D) { } fun v(x: Double, y: Double) = Vector(x, y) -fun v(x: Int, y: Int) = Vector(x.toDouble(), y.toDouble()) class Vector(val x: Double = 0.0, val y: Double = 0.0) { - constructor(x: Int, y: Int) : this(x.toDouble(), y.toDouble()) - operator fun plus(v: Vector) = v(x + v.x, y + v.y) operator fun unaryMinus() = v(-x, -y) operator fun minus(v: Vector) = v(x - v.x, y - v.y) operator fun times(koef: Double) = v(x * koef, y * koef) - infix fun distanceTo(v: Vector) = Math.sqrt((this - v).sqr) + infix fun distanceTo(v: Vector) = sqrt((this - v).sqr) fun rotatedBy(theta: Double): Vector { - val sin = Math.sin(theta) - val cos = Math.cos(theta) + val sin = sin(theta) + val cos = cos(theta) return v(x * cos - y * sin, x * sin + y * cos) } fun isInRect(topLeft: Vector, size: Vector) = (x >= topLeft.x) && (x <= topLeft.x + size.x) && - (y >= topLeft.y) && (y <= topLeft.y + size.y) + (y >= topLeft.y) && (y <= topLeft.y + size.y) val sqr: Double get() = x * x + y * y val normalized: Vector - get() = this * (1.0 / Math.sqrt(sqr)) + get() = this * (1.0 / sqrt(sqr)) } fun main(args: Array) { @@ -340,11 +338,3 @@ fun main(args: Array) { } } -fun List.reversed(): List { - val result = ArrayList() - var i = size - while (i > 0) { - result.add(get(--i)) - } - return result -} diff --git a/js/js.translator/testData/webDemoCanvasExamples/Fancy lines.kt b/js/js.translator/testData/webDemoCanvasExamples/Fancy lines.kt index d48bb3ccfb7..8a370ed2488 100644 --- a/js/js.translator/testData/webDemoCanvasExamples/Fancy lines.kt +++ b/js/js.translator/testData/webDemoCanvasExamples/Fancy lines.kt @@ -6,10 +6,12 @@ Note that only a subset of the api is supported for now. package fancylines -import kotlin.js.* -import kotlin.browser.window -import org.w3c.dom.* import jquery.* +import org.w3c.dom.CanvasRenderingContext2D +import org.w3c.dom.HTMLCanvasElement +import kotlin.browser.document +import kotlin.browser.window +import kotlin.random.* fun main(args: Array) { jq { @@ -26,25 +28,27 @@ class FancyLines() { val context = canvas.getContext("2d") as CanvasRenderingContext2D val height = canvas.height.toDouble() val width = canvas.width.toDouble() - var x = width * Math.random() - var y = height * Math.random() - var hue = 0; + fun nextX() = Random.nextDouble(width) + fun nextY() = Random.nextDouble(height) + var x = nextX() + var y = nextY() + var hue = 0 fun line() { context.save(); context.beginPath(); - context.lineWidth = 20.0 * Math.random(); + context.lineWidth = Random.nextDouble(20.0) context.moveTo(x, y); - x = width * Math.random(); - y = height * Math.random(); + x = nextX() + y = nextY() - context.bezierCurveTo(width * Math.random(), height * Math.random(), - width * Math.random(), height * Math.random(), x, y); + context.bezierCurveTo(nextX(), nextY(), + nextX(), nextY(), x, y) - hue += (Math.random() * 10).toInt(); + hue += Random.nextInt(10) context.strokeStyle = "hsl($hue, 50%, 50%)"; diff --git a/js/js.translator/testData/webDemoCanvasExamples/Hello, Kotlin.kt b/js/js.translator/testData/webDemoCanvasExamples/Hello, Kotlin.kt index 6f9196e9533..b8a0d0d2c73 100644 --- a/js/js.translator/testData/webDemoCanvasExamples/Hello, Kotlin.kt +++ b/js/js.translator/testData/webDemoCanvasExamples/Hello, Kotlin.kt @@ -3,10 +3,13 @@ */ package hello -import kotlin.js.* -import kotlin.browser.window -import org.w3c.dom.* import jquery.* +import org.w3c.dom.CanvasRenderingContext2D +import org.w3c.dom.HTMLCanvasElement +import kotlin.browser.document +import kotlin.browser.window +import kotlin.random.* +import kotlin.math.* val canvas: HTMLCanvasElement get() { @@ -19,21 +22,21 @@ val context: CanvasRenderingContext2D } -val width: Double +val width: Int get() { - return canvas.width.toDouble() + return canvas.width } -val height: Double +val height: Int get() { - return canvas.height.toDouble() + return canvas.height } // class representing a floating text class HelloKotlin() { - var relX = 0.2 + 0.2 * Math.random() - var relY = 0.4 + 0.2 * Math.random() + var relX = Random.nextDouble(0.2, 0.4) + var relY = Random.nextDouble(0.4, 0.6) val absX: Double get() = (relX * width) @@ -66,28 +69,25 @@ class HelloKotlin() { fun move() { val relTextWidth = textWidthInPixels / width - if (relX > (1.0 - relTextWidth - relXVelocity.abs) || relX < relXVelocity.abs) { + if (relX > (1.0 - relTextWidth - relXVelocity.absoluteValue) || relX < relXVelocity.absoluteValue) { relXVelocity *= -1 } val relTextHeight = textHeightInPixels / height - if (relY > (1.0 - relYVelocity.abs) || relY < relYVelocity.abs + relTextHeight) { + if (relY > (1.0 - relYVelocity.absoluteValue) || relY < relYVelocity.absoluteValue + relTextHeight) { relYVelocity *= -1 } relX += relXVelocity relY += relYVelocity } - fun randomVelocity() = 0.03 * Math.random() * (if (Math.random() < 0.5) 1 else -1) + fun randomVelocity() = Random.nextDouble(-0.03, 0.03) // same as 0.03 * Math.random() * (if (Math.random() < 0.5) 1 else -1) - - val Double.abs: Double - get() = if (this > 0) this else -this } fun renderBackground() { context.save() context.fillStyle = "#5C7EED" - context.fillRect(0.0, 0.0, width, height) + context.fillRect(0.0, 0.0, width.toDouble(), height.toDouble()) context.restore() } @@ -107,3 +107,5 @@ fun main(args: Array) { }, interval) } } + + diff --git a/js/js.translator/testData/webDemoCanvasExamples/Traffic light.kt b/js/js.translator/testData/webDemoCanvasExamples/Traffic light.kt index 1c8debba7ae..e53204c93d1 100644 --- a/js/js.translator/testData/webDemoCanvasExamples/Traffic light.kt +++ b/js/js.translator/testData/webDemoCanvasExamples/Traffic light.kt @@ -1,9 +1,16 @@ +/* + * In this example you can see a crossroads. Traffic light change color by timer, + * but you can change it manually using controls at the right part of screen. + */ package traffic -import kotlin.js.* -import kotlin.browser.window -import org.w3c.dom.* import jquery.* +import org.w3c.dom.* +import kotlin.browser.document +import kotlin.browser.window +import kotlin.math.* +import kotlin.random.* +import kotlin.js.Date fun getImage(path: String): HTMLImageElement { val image = window.document.createElement("img") as HTMLImageElement @@ -22,22 +29,10 @@ val context: CanvasRenderingContext2D } -val PATH_TO_IMAGES = "http://kotlin-demo.jetbrains.com/static/images/canvas/" +val PATH_TO_IMAGES = "http://try.kotlinlang.org/static/images/canvas/" -var _state: CanvasState? = null -val state: CanvasState - get() { - if (_state == null) { - _state = CanvasState(canvas) - } - return _state!! - } - -val colors: Colors - get() { - return Colors() - } +val state: CanvasState by lazy { CanvasState(canvas) } var trafficLightUp = TrafficLight(v(180.0, 181.0), "up", "red") var trafficLightDown = TrafficLight(v(100.0, 77.0), "down", "red") @@ -63,7 +58,6 @@ fun main(args: Array) { } fun v(x: Double, y: Double) = Vector(x, y) -fun v(x: Int, y: Int) = Vector(x, y) class Image(val src: String, override var pos: Vector, var imageSize: Vector) : Shape() { override fun draw() { @@ -73,7 +67,7 @@ class Image(val src: String, override var pos: Vector, var imageSize: Vector) : imageSize.x, imageSize.y) } - operator fun contains(mousePos: Vector): Boolean = mousePos.isInRect(pos, imageSize) + fun contains(mousePos: Vector): Boolean = mousePos.isInRect(pos, imageSize) } class Button(val src: String, override var pos: Vector, var imageSize: Vector) : Shape() { @@ -88,16 +82,14 @@ class Button(val src: String, override var pos: Vector, var imageSize: Vector) : pos.x, pos.y, imageSize.x, imageSize.y) } - } - else if (isMouseDown) { + } else if (isMouseDown) { state.context.shadowed(v(-3.0, 3.0), 0.8) { state.context.drawImage(getImage(src), 0.0, 0.0, imageSize.x, imageSize.y, pos.x, pos.y, imageSize.x, imageSize.y) } - } - else { + } else { state.context.drawImage(getImage(src), 0.0, 0.0, imageSize.x, imageSize.y, pos.x, pos.y, @@ -129,14 +121,14 @@ class Border() : Shape() { override var pos = Vector(4.0, 4.0); override fun draw() { - state.context.fillStyle = colors.white + state.context.fillStyle = Colors.white state.context.fillRect(2.0, 4.0, 10.0, 292.0) state.context.fillRect(330.0, 4.0, 370.0, 292.0) state.context.fillRect(2.0, 2.0, 330.0, 10.0) state.context.fillRect(4.0, 265.0, 340.0, 380.0) - state.context.strokeStyle = colors.black + state.context.strokeStyle = Colors.black state.context.lineWidth = 4.0 - state.context.strokeRect(0.0, 0.0, state.width, state.height) + state.context.strokeRect(0.0, 0.0, state.width.toDouble(), state.height.toDouble()) } } @@ -149,7 +141,7 @@ class Timer(override var pos: Vector) : Shape() { override fun draw() { timeLeftForChangeColor = ("" + (timerLength - (Date().getTime() - timeStartLastChangeColor) / 1000)).get(0) state.context.font = "bold 9px Arial, serif" - state.context.fillStyle = colors.black + state.context.fillStyle = Colors.black state.context.fillText("" + timeLeftForChangeColor, pos.x, pos.y) } @@ -160,7 +152,7 @@ class Timer(override var pos: Vector) : Shape() { } //Colors constants -class Colors() { +object Colors { val black: String = "#000000" val white = "#FFFFFF" val grey = "#C0C0C0" @@ -170,12 +162,12 @@ class Colors() { } class TrafficLight(override var pos: Vector, val direction: String, val startColor: String) : Shape() { - val list = ArrayList() + val list = mutableListOf() var size = Vector(27.0, 34.0); var timer = Timer(Vector(pos.x + 6, pos.y + 12)) var currentColor = startColor; var isForceColorChange = false - private var changeColorForward = (startColor == "red") + var shouldChangeColorForward = (startColor == "red") init { list.add(TrafficLightItem(v(pos.x, pos.y), PATH_TO_IMAGES + "red_color.png")) @@ -211,11 +203,11 @@ class TrafficLight(override var pos: Vector, val direction: String, val startCol } fun changeColor() { - if (changeColorForward) changeColorForward() else changeColorBackward() + if (shouldChangeColorForward) changeColorForward() else changeColorBackward() } fun changeColorForward() { - changeColorForward = false + shouldChangeColorForward = false currentColor = "yellow" window.setTimeout({ if (!isForceColorChange) timer.resetTimer() else isForceColorChange = false @@ -226,7 +218,7 @@ class TrafficLight(override var pos: Vector, val direction: String, val startCol fun changeColorBackward() { - changeColorForward = true + shouldChangeColorForward = true currentColor = "green_flash" window.setTimeout({ currentColor = "yellow" @@ -261,12 +253,10 @@ class TrafficLightItem(override var pos: Vector, val imageSrc: String) : Shape() if (countOfFlash > 6) { isFlashNow = false countOfFlash = 0 - } - else { + } else { countOfFlash++ } - } - else { + } else { state.context.drawImage(getImage(PATH_TO_IMAGES + "green_color.png"), 0.0, 0.0, imageSize.x, imageSize.y, pos.x, pos.y, @@ -274,13 +264,11 @@ class TrafficLightItem(override var pos: Vector, val imageSrc: String) : Shape() if (countOfFlash > 6) { isFlashNow = true countOfFlash = 0 - } - else { + } else { countOfFlash++ } } - } - else { + } else { state.context.drawImage(getImage(imageSrc), 0.0, 0.0, imageSize.x, imageSize.y, pos.x, pos.y, @@ -292,7 +280,8 @@ class TrafficLightItem(override var pos: Vector, val imageSrc: String) : Shape() class Car(override var pos: Vector, val direction: String, val color: String) : Shape() { val imageSize = v(25.0, 59.0) - var speed = getRandomArbitary(2, 10); + fun randomSpeed() = Random.nextDouble(2.0, 10.0) + var speed = randomSpeed() override fun draw() { if (direction == "up" || direction == "down") { @@ -302,21 +291,18 @@ class Car(override var pos: Vector, val direction: String, val color: String) : imageSize.x, imageSize.y) if ((!isNearStopLine()) || (trafficLightUp.canMove() && isNearStopLine()) ) { move() + } else { + speed = randomSpeed() } - else { - speed = getRandomArbitary(2, 10) - } - } - else { + } else { state.context.drawImage(getImage(PATH_TO_IMAGES + color + "_car.png"), 0.0, 0.0, imageSize.y, imageSize.x, pos.x, pos.y, imageSize.y, imageSize.x) if ((!isNearStopLine()) || (trafficLightLeft.canMove() && isNearStopLine()) ) { move() - } - else { - speed = getRandomArbitary(2, 10) + } else { + speed = randomSpeed() } } @@ -368,13 +354,13 @@ class Map(override var pos: Vector) : Shape() { class CanvasState(val canvas: HTMLCanvasElement) { val context = traffic.context - var shapes = ArrayList() + var shapes = mutableListOf() - var width = canvas.width.toDouble() - var height = canvas.height.toDouble() + var width = canvas.width + var height = canvas.height val size: Vector - get() = v(width, height) + get() = v(width.toDouble(), height.toDouble()) fun addShape(shape: Shape) { shapes.add(shape) @@ -414,7 +400,6 @@ class CanvasState(val canvas: HTMLCanvasElement) { val mousePos = mousePos(it) for (shape in shapes) { if (shape is Button && mousePos in shape) { - val name = shape.src shape.mouseOver() } } @@ -440,7 +425,7 @@ class CanvasState(val canvas: HTMLCanvasElement) { var element: HTMLElement? = canvas while (element != null) { val el: HTMLElement = element - offset += Vector(el.offsetLeft, el.offsetTop) + offset += Vector(el.offsetLeft.toDouble(), el.offsetTop.toDouble()) element = el.offsetParent as HTMLElement? } return Vector(e.pageX, e.pageY) - offset @@ -454,8 +439,8 @@ class CanvasState(val canvas: HTMLCanvasElement) { } fun clear() { - context.fillStyle = colors.white - context.fillRect(0.0, 0.0, width, height) + context.fillStyle = Colors.white + context.fillRect(0.0, 0.0, width.toDouble(), height.toDouble()) } } @@ -484,28 +469,22 @@ abstract class Shape() { } class Vector(val x: Double = 0.0, val y: Double = 0.0) { - constructor(x: Int, y: Int) : this(x.toDouble(), y.toDouble()) - operator fun plus(v: Vector) = v(x + v.x, y + v.y) - operator fun unaryMinus() = v(-x, -y) operator fun minus(v: Vector) = v(x - v.x, y - v.y) operator fun times(koef: Double) = v(x * koef, y * koef) - fun distanceTo(v: Vector) = Math.sqrt((this - v).sqr) + fun distanceTo(v: Vector) = sqrt((this - v).sqr) fun rotatedBy(theta: Double): Vector { - val sin = Math.sin(theta) - val cos = Math.cos(theta) + val sin = sin(theta) + val cos = cos(theta) return v(x * cos - y * sin, x * sin + y * cos) } fun isInRect(topLeft: Vector, size: Vector) = (x >= topLeft.x) && (x <= topLeft.x + size.x) && - (y >= topLeft.y) && (y <= topLeft.y + size.y) + (y >= topLeft.y) && (y <= topLeft.y + size.y) val sqr: Double get() = x * x + y * y val normalized: Vector - get() = this * (1.0 / Math.sqrt(sqr)) + get() = this * (1.0 / sqrt(sqr)) } -fun getRandomArbitary(min: Int, max: Int): Double { - return Math.random() * (max - min) + min; -}