Add tests which check that web demo canvas examples compile correctly
This commit is contained in:
@@ -0,0 +1,334 @@
|
||||
/*
|
||||
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
|
||||
// importing some of the API defined
|
||||
import jquery.*
|
||||
import html5.*
|
||||
import java.util.ArrayList
|
||||
import js.*
|
||||
import js.Math
|
||||
import js.setInterval
|
||||
import html5.getCanvas
|
||||
import html5.getKotlinLogo
|
||||
import jquery.jq
|
||||
import js.setTimeout
|
||||
import java.util.List
|
||||
|
||||
|
||||
abstract class Shape() {
|
||||
|
||||
abstract fun draw(state : CanvasState)
|
||||
// these two abstract methods defines that our shapes can be dragged
|
||||
abstract fun contains(mousePos : Vector) : Boolean
|
||||
abstract var pos : Vector
|
||||
|
||||
var selected : Boolean = false
|
||||
|
||||
// a couple of helper extension methods we'll be using in the derived classes
|
||||
fun Context.shadowed(shadowOffset : Vector, alpha : Double, render : Context.() -> Unit) {
|
||||
save()
|
||||
shadowColor = "rgba(100, 100, 100, $alpha)"
|
||||
shadowBlur = 5.0
|
||||
shadowOffsetX = shadowOffset.x
|
||||
shadowOffsetY = shadowOffset.y
|
||||
render()
|
||||
restore()
|
||||
}
|
||||
|
||||
fun Context.fillPath(constructPath : Context.() -> Unit) {
|
||||
beginPath()
|
||||
constructPath()
|
||||
closePath()
|
||||
fill()
|
||||
}
|
||||
}
|
||||
|
||||
val Kotlin = Logo(v(250.0, 75.0))
|
||||
|
||||
class Logo(override var pos : Vector) : Shape()
|
||||
{
|
||||
val relSize : Double = 0.25
|
||||
val shadowOffset = v(-3.0, 3.0)
|
||||
val imageSize = v(377.0, 393.0)
|
||||
var size : Vector = imageSize * relSize
|
||||
// get-only properties like this saves you lots of typing and are very expressive
|
||||
val position : Vector
|
||||
get() = if (selected) pos - shadowOffset else pos
|
||||
|
||||
|
||||
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("/static/images/kotlinlogowobackground.png"), 0, 0,
|
||||
imageSize.x.toInt(), imageSize.y.toInt(),
|
||||
position.x.toInt(), position.y.toInt(),
|
||||
size.x.toInt(), size.y.toInt())
|
||||
}
|
||||
|
||||
override fun draw(state : CanvasState) {
|
||||
val context = state.context
|
||||
if (selected) {
|
||||
// using helper we defined in Shape class
|
||||
context.shadowed(shadowOffset, 0.2) {
|
||||
drawLogo(state)
|
||||
}
|
||||
} else {
|
||||
drawLogo(state)
|
||||
}
|
||||
}
|
||||
|
||||
override fun contains(mousePos: Vector): Boolean = mousePos.isInRect(pos, size)
|
||||
|
||||
val centre : Vector
|
||||
get() = pos + size * 0.5
|
||||
}
|
||||
|
||||
val gradientGenerator = RadialGradientGenerator(getContext())
|
||||
|
||||
class Creature(override var pos : Vector, val state : CanvasState) : Shape() {
|
||||
|
||||
val shadowOffset = v(-5.0, 5.0)
|
||||
val colorStops = gradientGenerator.getNext()
|
||||
val relSize = 0.05
|
||||
// these properties have no backing fields and in java/javascript they could be represented as little helper functions
|
||||
val radius : Double
|
||||
get() = state.width * relSize
|
||||
val position : Vector
|
||||
get() = if (selected) pos - shadowOffset else pos
|
||||
val directionToLogo : Vector
|
||||
get() = (Kotlin.centre - position).normalized
|
||||
|
||||
//notice how the infix call can make some expressions extremely expressive
|
||||
override fun contains(mousePos : Vector) = pos distanceTo mousePos < radius
|
||||
|
||||
// defining more nice extension functions
|
||||
fun Context.circlePath(position : Vector, rad : Double) {
|
||||
arc(position.x, position.y, rad, 0.0, 2 * Math.PI, false)
|
||||
}
|
||||
|
||||
//notice we can use an extension function we just defined inside another extension function
|
||||
fun Context.fillCircle(position : Vector, rad : Double) {
|
||||
fillPath {
|
||||
circlePath(position, rad)
|
||||
}
|
||||
}
|
||||
|
||||
override fun draw(state : CanvasState) {
|
||||
val context = state.context
|
||||
if (!selected) {
|
||||
drawCreature(context)
|
||||
} else {
|
||||
drawCreatureWithShadow(context)
|
||||
}
|
||||
}
|
||||
|
||||
fun drawCreature(context : Context) {
|
||||
context.fillStyle = getGradient(context)
|
||||
context.fillPath {
|
||||
tailPath(context)
|
||||
circlePath(position, radius)
|
||||
}
|
||||
drawEye(context)
|
||||
}
|
||||
|
||||
fun getGradient(context : Context) : CanvasGradient {
|
||||
val gradientCentre = position + directionToLogo * (radius / 4)
|
||||
val gradient = context.createRadialGradient(gradientCentre.x, gradientCentre.y, 1.0, gradientCentre.x, gradientCentre.y, 2 * radius)
|
||||
for (colorStop in colorStops) {
|
||||
gradient.addColorStop(colorStop._1, colorStop._2)
|
||||
}
|
||||
return gradient
|
||||
}
|
||||
|
||||
fun tailPath(context : Context) {
|
||||
val tailDirection = -directionToLogo
|
||||
val tailPos = position + tailDirection * radius * 1.0
|
||||
val tailSize = radius * 1.6
|
||||
val angle = Math.PI / 6.0
|
||||
val p1 = tailPos + tailDirection.rotatedBy(angle) * tailSize
|
||||
val p2 = tailPos + tailDirection.rotatedBy(-angle) * tailSize
|
||||
val middlePoint = position + tailDirection * radius * 1.0
|
||||
context.moveTo(tailPos.x.toInt(), tailPos.y.toInt())
|
||||
context.lineTo(p1.x.toInt(), p1.y.toInt())
|
||||
context.quadraticCurveTo(middlePoint.x, middlePoint.y, p2.x, p2.y)
|
||||
context.lineTo(tailPos.x.toInt(), tailPos.y.toInt())
|
||||
}
|
||||
|
||||
fun drawEye(context : Context) {
|
||||
val eyePos = directionToLogo * radius * 0.6 + position
|
||||
val eyeRadius = radius / 3
|
||||
val eyeLidRadius = eyeRadius / 2
|
||||
context.fillStyle = "#FFFFFF"
|
||||
context.fillCircle(eyePos, eyeRadius)
|
||||
context.fillStyle = "#000000"
|
||||
context.fillCircle(eyePos, eyeLidRadius)
|
||||
}
|
||||
|
||||
fun drawCreatureWithShadow(context : Context) {
|
||||
context.shadowed(shadowOffset, 0.7) {
|
||||
context.fillStyle = getGradient(context)
|
||||
fillPath {
|
||||
tailPath(context)
|
||||
context.circlePath(position, radius)
|
||||
}
|
||||
}
|
||||
drawEye(context)
|
||||
}
|
||||
}
|
||||
|
||||
class CanvasState(val canvas : Canvas) {
|
||||
var width = canvas.width.toDouble()
|
||||
var height = canvas.height.toDouble()
|
||||
val size : Vector
|
||||
get() = v(width, height)
|
||||
val context = getContext()
|
||||
var valid = false
|
||||
var shapes = ArrayList<Shape>()
|
||||
var selection : Shape? = null
|
||||
var dragOff = Vector()
|
||||
val interval = 1000 / 30
|
||||
|
||||
{
|
||||
jq(canvas).mousedown {
|
||||
valid = false
|
||||
selection = null
|
||||
val mousePos = mousePos(it)
|
||||
for (shape in shapes) {
|
||||
if (mousePos in shape) {
|
||||
dragOff = mousePos - shape.pos
|
||||
shape.selected = true
|
||||
selection = shape
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
jq(canvas).mousemove {
|
||||
if (selection != null) {
|
||||
selection.sure().pos = mousePos(it) - dragOff
|
||||
valid = false
|
||||
}
|
||||
}
|
||||
|
||||
jq(canvas).mouseup {
|
||||
if (selection != null) {
|
||||
selection.sure().selected = false
|
||||
}
|
||||
selection = null
|
||||
valid = false
|
||||
}
|
||||
|
||||
jq(canvas).dblclick {
|
||||
val newCreature = Creature(mousePos(it), this @CanvasState)
|
||||
addShape(newCreature)
|
||||
valid = false
|
||||
}
|
||||
|
||||
setInterval({
|
||||
draw()
|
||||
}, interval)
|
||||
}
|
||||
|
||||
fun mousePos(e : MouseEvent) : Vector {
|
||||
var offset = Vector()
|
||||
var element : DomElement? = canvas
|
||||
while (element != null) {
|
||||
val el : DomElement = element.sure()
|
||||
offset += Vector(el.offsetLeft, el.offsetTop)
|
||||
element = el.offsetParent
|
||||
}
|
||||
return Vector(e.pageX, e.pageY) - offset
|
||||
}
|
||||
|
||||
fun addShape(shape : Shape) {
|
||||
shapes.add(shape)
|
||||
valid = false
|
||||
}
|
||||
|
||||
fun clear() {
|
||||
context.fillStyle = "#FFFFFF"
|
||||
context.fillRect(0, 0, width.toInt(), height.toInt())
|
||||
context.strokeStyle = "#000000"
|
||||
context.lineWidth = 4.0
|
||||
context.strokeRect(0, 0, width.toInt(), height.toInt())
|
||||
}
|
||||
|
||||
fun draw() {
|
||||
if (valid) return
|
||||
|
||||
clear()
|
||||
for (shape in shapes.reversed()) {
|
||||
shape.draw(this)
|
||||
}
|
||||
Kotlin.draw(this)
|
||||
valid = true
|
||||
}
|
||||
}
|
||||
|
||||
class RadialGradientGenerator(val context : Context) {
|
||||
val gradients = ArrayList<Array<#(Double, String)>>()
|
||||
var current = 0
|
||||
|
||||
fun newColorStops(vararg colorStops : #(Double, String)) {
|
||||
gradients.add(colorStops)
|
||||
}
|
||||
|
||||
{
|
||||
newColorStops(#(0.0, "#F59898"), #(0.5, "#F57373"), #(1.0, "#DB6B6B"))
|
||||
newColorStops(#(0.39, "rgb(140,167,209)"), #(0.7, "rgb(104,139,209)"), #(0.85, "rgb(67,122,217)"))
|
||||
newColorStops(#(0.0, "rgb(255,222,255)"), #(0.5, "rgb(255,185,222)"), #(1.0, "rgb(230,154,185)"))
|
||||
newColorStops(#(0.0, "rgb(255,209,114)"), #(0.5, "rgb(255,174,81)"), #(1.0, "rgb(241,145,54)"))
|
||||
newColorStops(#(0.0, "rgb(132,240,135)"), #(0.5, "rgb(91,240,96)"), #(1.0, "rgb(27,245,41)"))
|
||||
newColorStops(#(0.0, "rgb(250,147,250)"), #(0.5, "rgb(255,80,255)"), #(1.0, "rgb(250,0,217)"))
|
||||
}
|
||||
|
||||
fun getNext() : Array<#(Double, String)> {
|
||||
val result = gradients.get(current)
|
||||
current = (current + 1) % gradients.size()
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
fun v(x : Double, y : Double) = Vector(x, y)
|
||||
|
||||
class Vector(val x : Double = 0.0, val y : Double = 0.0) {
|
||||
fun plus(v : Vector) = v(x + v.x, y + v.y)
|
||||
fun minus() = v(-x, -y)
|
||||
fun minus(v : Vector) = v(x - v.x, y - v.y)
|
||||
fun times(koef : Double) = v(x * koef, y * koef)
|
||||
fun distanceTo(v : Vector) = Math.sqrt((this - v).sqr)
|
||||
fun rotatedBy(theta : Double) : Vector {
|
||||
val sin = Math.sin(theta)
|
||||
val cos = Math.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)
|
||||
|
||||
val sqr : Double
|
||||
get() = x * x + y * y
|
||||
val normalized : Vector
|
||||
get() = this * (1.0 / Math.sqrt(sqr))
|
||||
}
|
||||
|
||||
fun main(args : Array<String>) {
|
||||
val state = CanvasState(getCanvas())
|
||||
state.addShape(Kotlin)
|
||||
state.addShape(Creature(state.size * 0.25, state))
|
||||
state.addShape(Creature(state.size * 0.75, state))
|
||||
setTimeout({
|
||||
state.valid = false
|
||||
})
|
||||
}
|
||||
|
||||
fun <T> List<T>.reversed() : List<T> {
|
||||
val result = ArrayList<T>()
|
||||
var i = size()
|
||||
while (i > 0) {
|
||||
result.add(get(--i))
|
||||
}
|
||||
return result
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
This example is based on example from html5 canvas2D docs:
|
||||
http://www.w3.org/TR/2dcontext/
|
||||
Note that only a subset of the api is supported for now.
|
||||
*/
|
||||
|
||||
package fancylines
|
||||
|
||||
import js.*;
|
||||
import html5.*;
|
||||
import jquery.*;
|
||||
|
||||
fun main(args : Array<String>) {
|
||||
//jq is a name for JQuery function
|
||||
jq {
|
||||
FancyLines().run();
|
||||
}
|
||||
}
|
||||
|
||||
class FancyLines() {
|
||||
// we use two 'magic' functions here getContext() and getCanvas()
|
||||
val context = getContext();
|
||||
val height = getCanvas().height;
|
||||
val width = getCanvas().width;
|
||||
var x = width * Math.random();
|
||||
var y = height * Math.random();
|
||||
var hue = 0;
|
||||
|
||||
fun line() {
|
||||
context.save();
|
||||
|
||||
context.beginPath();
|
||||
|
||||
context.lineWidth = 20.0 * Math.random();
|
||||
context.moveTo(x.toInt(), y.toInt());
|
||||
|
||||
x = width * Math.random();
|
||||
y = height * Math.random();
|
||||
|
||||
context.bezierCurveTo(width * Math.random(), height * Math.random(),
|
||||
width * Math.random(), height * Math.random(), x, y);
|
||||
|
||||
hue += Math.random() * 10;
|
||||
|
||||
context.strokeStyle = "hsl($hue, 50%, 50%)";
|
||||
|
||||
context.shadowColor = "white";
|
||||
context.shadowBlur = 10.0;
|
||||
|
||||
context.stroke();
|
||||
|
||||
context.restore();
|
||||
}
|
||||
|
||||
fun blank() {
|
||||
context.fillStyle = "rgba(255,255,1,0.1)";
|
||||
context.fillRect(0, 0, width, height);
|
||||
}
|
||||
|
||||
fun run() {
|
||||
setInterval({line()}, 40);
|
||||
setInterval({blank()}, 100);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
/*
|
||||
This example is just simple text floating around. If u are using chrome, there is a bug that spoil the visuals.
|
||||
*/
|
||||
package hello
|
||||
|
||||
import js.*
|
||||
import html5.*
|
||||
import jquery.*
|
||||
|
||||
val context = getContext()
|
||||
val height = getCanvas().height
|
||||
val width = getCanvas().width
|
||||
|
||||
// class representing a floating text
|
||||
class HelloKotlin() {
|
||||
var relX = 0.2 + 0.2 * Math.random()
|
||||
var relY = 0.4 + 0.2 * Math.random()
|
||||
|
||||
val absX : Double
|
||||
get() = (relX * width)
|
||||
val absY : Double
|
||||
get() = (relY * height)
|
||||
|
||||
var relXVelocity = randomVelocity()
|
||||
var relYVelocity = randomVelocity()
|
||||
|
||||
|
||||
val message = "Hello, Kotlin!"
|
||||
val textHeightInPixels = 20
|
||||
{
|
||||
context.font = "bold ${textHeightInPixels}px Georgia, serif"
|
||||
}
|
||||
val textWidthInPixels = context.measureText(message).width
|
||||
|
||||
fun draw() {
|
||||
context.save()
|
||||
move()
|
||||
// if you using chrome chances are good you wont see the shadow
|
||||
context.shadowColor = "#000000"
|
||||
context.shadowBlur = 5.0
|
||||
context.shadowOffsetX = -4.0
|
||||
context.shadowOffsetY = 4.0
|
||||
context.fillStyle = "rgb(242,160,110)"
|
||||
context.fillText(message, absX.toInt(), absY.toInt())
|
||||
context.restore()
|
||||
}
|
||||
|
||||
fun move() {
|
||||
val relTextWidth = textWidthInPixels / width
|
||||
if (relX > (1.0 - relTextWidth - relXVelocity.abs) || relX < relXVelocity.abs) {
|
||||
relXVelocity *= -1
|
||||
}
|
||||
val relTextHeight = textHeightInPixels / height
|
||||
if (relY > (1.0 - relYVelocity.abs) || relY < relYVelocity.abs + relTextHeight) {
|
||||
relYVelocity *= -1
|
||||
}
|
||||
relX += relXVelocity
|
||||
relY += relYVelocity
|
||||
}
|
||||
|
||||
fun randomVelocity() = 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, width, height)
|
||||
context.restore()
|
||||
}
|
||||
|
||||
fun main(args : Array<String>) {
|
||||
val interval = 50
|
||||
// we pass a literal that constructs a new HelloKotlin object
|
||||
val logos = Array(3) {
|
||||
HelloKotlin()
|
||||
}
|
||||
jq {
|
||||
setInterval({
|
||||
renderBackground()
|
||||
for (logo in logos) {
|
||||
logo.draw()
|
||||
}
|
||||
}, interval)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,466 @@
|
||||
import jquery.*
|
||||
import html5.*
|
||||
import java.util.ArrayList
|
||||
import js.setInterval
|
||||
import js.DomElement
|
||||
import js.setTimeout
|
||||
import js.*;
|
||||
|
||||
val PATH_TO_IMAGES = "/static/images/canvas/"
|
||||
|
||||
val state = CanvasState(getCanvas())
|
||||
val colors = Colors()
|
||||
|
||||
var trafficLightUp = TrafficLight(v(180.0, 181.0), "up", "red")
|
||||
var trafficLightDown = TrafficLight(v(100.0, 77.0), "down", "red")
|
||||
var trafficLightLeft = TrafficLight(v(228.0, 109.0), "left", "green")
|
||||
var trafficLightRight = TrafficLight(v(55.0, 145.0), "right", "green")
|
||||
|
||||
|
||||
fun main(args : Array<String>) {
|
||||
state.addShape(Map(v(10.0, 10.0)))
|
||||
|
||||
state.addShape(trafficLightLeft)
|
||||
state.addShape(trafficLightUp)
|
||||
state.addShape(trafficLightDown)
|
||||
state.addShape(trafficLightRight)
|
||||
state.addShape(Car(v(178.0, 205.0), "up", "red"))
|
||||
state.addShape(Car(v(95.0, 4.0), "down", "white"))
|
||||
state.addShape(Car(v(278.0, 108.0), "left", "blue"))
|
||||
state.addShape(Car(v(0.0, 142.0), "right", "black"))
|
||||
state.addShape(Border())
|
||||
state.addShape(Image(PATH_TO_IMAGES + "controls.png", v(380.0, 10.0), v(190.0, 56.0)))
|
||||
state.addShape(Button(PATH_TO_IMAGES + "lr.png", v(420.0, 70.0), v(120.0, 50.0)))
|
||||
state.addShape(Button(PATH_TO_IMAGES + "ud.png", v(455.0, 120.0), v(50.0, 120.0)))
|
||||
}
|
||||
|
||||
fun v(x : Double, y : Double) = Vector(x, y)
|
||||
|
||||
class Image(val src : String, override var pos : Vector, var imageSize : Vector) : Shape() {
|
||||
override fun draw() {
|
||||
state.context.drawImage(getImage(src), 0, 0,
|
||||
imageSize.x.toInt(), imageSize.y.toInt(),
|
||||
pos.x.toInt(), pos.y.toInt(),
|
||||
imageSize.x.toInt(), imageSize.y.toInt())
|
||||
}
|
||||
|
||||
fun contains(mousePos : Vector) : Boolean = mousePos.isInRect(pos, imageSize)
|
||||
}
|
||||
|
||||
class Button(val src : String, override var pos : Vector, var imageSize : Vector) : Shape() {
|
||||
var isMouseOver = false
|
||||
var isMouseDown = false
|
||||
|
||||
override fun draw() {
|
||||
if (isMouseOver) {
|
||||
state.context.shadowed(v(- 3.0, 3.0), 1.2) {
|
||||
state.context.drawImage(getImage(src), 0, 0,
|
||||
imageSize.x.toInt(), imageSize.y.toInt(),
|
||||
pos.x.toInt(), pos.y.toInt(),
|
||||
imageSize.x.toInt(), imageSize.y.toInt())
|
||||
}
|
||||
} else if (isMouseDown) {
|
||||
state.context.shadowed(v(- 3.0, 3.0), 0.8) {
|
||||
state.context.drawImage(getImage(src), 0, 0,
|
||||
imageSize.x.toInt(), imageSize.y.toInt(),
|
||||
pos.x.toInt(), pos.y.toInt(),
|
||||
imageSize.x.toInt(), imageSize.y.toInt())
|
||||
}
|
||||
} else {
|
||||
state.context.drawImage(getImage(src), 0, 0,
|
||||
imageSize.x.toInt(), imageSize.y.toInt(),
|
||||
pos.x.toInt(), pos.y.toInt(),
|
||||
imageSize.x.toInt(), imageSize.y.toInt())
|
||||
}
|
||||
}
|
||||
|
||||
fun mouseClick() {
|
||||
isMouseDown = true
|
||||
setTimeout({
|
||||
isMouseDown = false
|
||||
}, 1000)
|
||||
}
|
||||
|
||||
fun mouseOver() {
|
||||
isMouseOver = true
|
||||
setTimeout({
|
||||
isMouseOver = false
|
||||
}, 1000)
|
||||
}
|
||||
|
||||
|
||||
fun contains(mousePos : Vector) : Boolean = mousePos.isInRect(pos, imageSize)
|
||||
}
|
||||
|
||||
class Border() : Shape() {
|
||||
override var pos = Vector(4.0, 4.0);
|
||||
|
||||
override fun draw() {
|
||||
state.context.fillStyle = colors.white
|
||||
state.context.fillRect(2, 4, 10, 292)
|
||||
state.context.fillRect(330, 4, 370, 292)
|
||||
state.context.fillRect(2, 2, 330, 10)
|
||||
state.context.fillRect(4, 265, 340, 380)
|
||||
state.context.strokeStyle = colors.black
|
||||
state.context.lineWidth = 4.0
|
||||
state.context.strokeRect(0, 0, state.width.toInt(), state.height.toInt())
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class Timer(override var pos : Vector) : Shape() {
|
||||
var timeLeftForChangeColor : Char = 'c'
|
||||
var timeStartLastChangeColor = Date().getTime();
|
||||
var timerLength = 13
|
||||
|
||||
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.fillText("" + timeLeftForChangeColor, pos.x.toInt(), pos.y.toInt())
|
||||
}
|
||||
|
||||
fun resetTimer() {
|
||||
timeStartLastChangeColor = Date().getTime()
|
||||
timerLength = 10
|
||||
}
|
||||
}
|
||||
|
||||
//Colors constants
|
||||
class Colors() {
|
||||
val black : String = "#000000"
|
||||
val white = "#FFFFFF"
|
||||
val grey = "#C0C0C0"
|
||||
val red = "#EF4137"
|
||||
val yellow = "#FCE013"
|
||||
val green = "#0E9648"
|
||||
}
|
||||
|
||||
class TrafficLight(override var pos : Vector, val direction : String, val startColor : String) : Shape() {
|
||||
val list = ArrayList<TrafficLightItem>()
|
||||
var size = Vector(27.0, 34.0);
|
||||
var timer = Timer(Vector(pos.x + 6, pos.y + 12))
|
||||
var currentColor = startColor;
|
||||
var isForceColorChange = false
|
||||
var changeColorForward = (startColor == "red")
|
||||
|
||||
{
|
||||
list.add(TrafficLightItem(v(pos.x, pos.y), PATH_TO_IMAGES + "red_color.png"))
|
||||
list.add(TrafficLightItem(v(pos.x, pos.y), PATH_TO_IMAGES + "yellow_color.png"))
|
||||
list.add(TrafficLightItem(v(pos.x, pos.y), PATH_TO_IMAGES + "green_color.png"))
|
||||
list.add(TrafficLightItem(v(pos.x, pos.y), PATH_TO_IMAGES + "green_color_flash.png"))
|
||||
}
|
||||
|
||||
override fun draw() {
|
||||
when (currentColor) {
|
||||
"red" -> list.get(0).draw()
|
||||
"yellow" -> list.get(1).draw()
|
||||
"green" -> list.get(2).draw()
|
||||
"green_flash" -> list.get(3).draw()
|
||||
else -> {
|
||||
}
|
||||
}
|
||||
timer.draw()
|
||||
}
|
||||
|
||||
fun setRed() {
|
||||
if (currentColor != "red" && currentColor != "yellow" && currentColor != "green_flash") {
|
||||
isForceColorChange = true
|
||||
changeColor()
|
||||
}
|
||||
}
|
||||
|
||||
fun setGreen() {
|
||||
if (currentColor != "green" && currentColor != "green_flash" && currentColor != "yellow") {
|
||||
isForceColorChange = true
|
||||
changeColor()
|
||||
}
|
||||
}
|
||||
|
||||
fun changeColor() {
|
||||
if (changeColorForward) changeColorForward() else changeColorBackward()
|
||||
}
|
||||
|
||||
fun changeColorForward() {
|
||||
changeColorForward = false
|
||||
currentColor = "yellow"
|
||||
setTimeout({
|
||||
if (!isForceColorChange) timer.resetTimer() else isForceColorChange = false
|
||||
currentColor = "green"
|
||||
}, 3000)
|
||||
}
|
||||
|
||||
|
||||
fun changeColorBackward() {
|
||||
changeColorForward = true
|
||||
currentColor = "green_flash"
|
||||
setTimeout({
|
||||
currentColor = "yellow"
|
||||
setTimeout({
|
||||
if (!isForceColorChange) timer.resetTimer() else isForceColorChange = false
|
||||
currentColor = "red"
|
||||
}, 1000)
|
||||
}, 2000)
|
||||
}
|
||||
|
||||
fun canMove() : Boolean {
|
||||
return (currentColor != "red" && currentColor != "yellow")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//One element from Traffic light
|
||||
class TrafficLightItem(override var pos : Vector, val imageSrc : String) : Shape() {
|
||||
val relSize : Double = 0.5
|
||||
val imageSize = v(33.0, 33.0)
|
||||
var size : Vector = imageSize * relSize
|
||||
|
||||
var isFlashing = (imageSrc == PATH_TO_IMAGES + "green_color_flash.png")
|
||||
var isFlashNow = false
|
||||
var countOfFlash = 0
|
||||
|
||||
override fun draw() {
|
||||
size = imageSize * relSize
|
||||
if (isFlashing) {
|
||||
if (isFlashNow) {
|
||||
if (countOfFlash > 6) {
|
||||
isFlashNow = false
|
||||
countOfFlash = 0
|
||||
} else {
|
||||
countOfFlash++
|
||||
}
|
||||
} else {
|
||||
state.context.drawImage(getImage(PATH_TO_IMAGES + "green_color.png"), 0, 0,
|
||||
imageSize.x.toInt(), imageSize.y.toInt(),
|
||||
pos.x.toInt(), pos.y.toInt(),
|
||||
size.x.toInt(), size.y.toInt())
|
||||
if (countOfFlash > 6) {
|
||||
isFlashNow = true
|
||||
countOfFlash = 0
|
||||
} else {
|
||||
countOfFlash++
|
||||
}
|
||||
}
|
||||
} else {
|
||||
state.context.drawImage(getImage(imageSrc), 0, 0,
|
||||
imageSize.x.toInt(), imageSize.y.toInt(),
|
||||
pos.x.toInt(), pos.y.toInt(),
|
||||
size.x.toInt(), size.y.toInt())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
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);
|
||||
|
||||
override fun draw() {
|
||||
if (direction == "up" || direction == "down") {
|
||||
state.context.drawImage(getImage(PATH_TO_IMAGES + color + "_car.png"), 0, 0,
|
||||
imageSize.x.toInt(), imageSize.y.toInt(),
|
||||
pos.x.toInt(), pos.y.toInt(),
|
||||
imageSize.x.toInt(), imageSize.y.toInt())
|
||||
if ((!isNearStopLine()) || (trafficLightUp.canMove() && isNearStopLine()) ) {
|
||||
move()
|
||||
} else {
|
||||
speed = getRandomArbitary(2, 10)
|
||||
}
|
||||
} else {
|
||||
state.context.drawImage(getImage(PATH_TO_IMAGES + color + "_car.png"), 0, 0,
|
||||
imageSize.y.toInt(), imageSize.x.toInt(),
|
||||
pos.x.toInt(), pos.y.toInt(),
|
||||
imageSize.y.toInt(), imageSize.x.toInt())
|
||||
if ((!isNearStopLine()) || (trafficLightLeft.canMove() && isNearStopLine()) ) {
|
||||
move()
|
||||
} else {
|
||||
speed = getRandomArbitary(2, 10)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
fun isNearStopLine() : Boolean {
|
||||
when (direction) {
|
||||
"up" -> return (pos.y > 198 && pos.y < 208)
|
||||
"down" -> return (pos.y > 10 && pos.y < 20)
|
||||
"right" -> return (pos.x > - 8 && pos.x < 2)
|
||||
"left" -> return (pos.x > 243 && pos.x < 253)
|
||||
else -> return false
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
fun move() {
|
||||
var x = pos.x
|
||||
var y = pos.y
|
||||
|
||||
when (direction) {
|
||||
"up" -> if (pos.y < - 50) y = 250.0 else y = pos.y - speed
|
||||
"down" -> if (pos.y > 300) y = 0.0 else y = pos.y + speed
|
||||
"right" -> if (pos.x > 300) x = - 10.0 else x = pos.x + speed
|
||||
"left" -> if (pos.x < - 50) x = 340.0 else x = pos.x - speed
|
||||
else -> {
|
||||
}
|
||||
}
|
||||
|
||||
pos = v(x, y)
|
||||
}
|
||||
}
|
||||
|
||||
class Map(override var pos : Vector) : Shape() {
|
||||
val relSize : Double = 0.8
|
||||
val imageSize = v(420.0, 323.0)
|
||||
var size : Vector = imageSize * relSize
|
||||
|
||||
override fun draw() {
|
||||
size = imageSize * relSize
|
||||
state.context.drawImage(getImage(PATH_TO_IMAGES + "crossroads.jpg"), 0, 0,
|
||||
imageSize.x.toInt(), imageSize.y.toInt(),
|
||||
pos.x.toInt(), pos.y.toInt(),
|
||||
size.x.toInt(), size.y.toInt())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class CanvasState(val canvas : Canvas) {
|
||||
val context = getContext()
|
||||
var shapes = ArrayList<Shape>()
|
||||
|
||||
var width = canvas.width.toDouble()
|
||||
var height = canvas.height.toDouble()
|
||||
|
||||
val size : Vector
|
||||
get() = v(width, height)
|
||||
|
||||
fun addShape(shape : Shape) {
|
||||
shapes.add(shape)
|
||||
}
|
||||
|
||||
{
|
||||
jq(canvas).click {
|
||||
val mousePos = mousePos(it)
|
||||
for (shape in shapes) {
|
||||
if (shape is Button && mousePos in shape) {
|
||||
val name = shape.src
|
||||
shape.mouseClick()
|
||||
when (name) {
|
||||
PATH_TO_IMAGES + "lr.png" -> {
|
||||
|
||||
trafficLightUp.setRed()
|
||||
trafficLightDown.setRed()
|
||||
trafficLightLeft.setGreen()
|
||||
trafficLightRight.setGreen()
|
||||
}
|
||||
PATH_TO_IMAGES + "ud.png" -> {
|
||||
|
||||
trafficLightLeft.setRed()
|
||||
trafficLightRight.setRed()
|
||||
trafficLightUp.setGreen()
|
||||
trafficLightDown.setGreen()
|
||||
|
||||
}
|
||||
else -> continue
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
jq(canvas).mousemove {
|
||||
val mousePos = mousePos(it)
|
||||
for (shape in shapes) {
|
||||
if (shape is Button && mousePos in shape) {
|
||||
val name = shape.src
|
||||
shape.mouseOver()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
setInterval({
|
||||
draw()
|
||||
}, 1000 / 30)
|
||||
|
||||
setInterval({
|
||||
trafficLightUp.changeColor()
|
||||
trafficLightLeft.changeColor()
|
||||
trafficLightRight.changeColor()
|
||||
trafficLightDown.changeColor()
|
||||
}, 10000)
|
||||
|
||||
|
||||
}
|
||||
|
||||
fun mousePos(e : MouseEvent) : Vector {
|
||||
var offset = Vector()
|
||||
var element : DomElement? = canvas
|
||||
while (element != null) {
|
||||
val el : DomElement = element.sure()
|
||||
offset += Vector(el.offsetLeft, el.offsetTop)
|
||||
element = el.offsetParent
|
||||
}
|
||||
return v(e.pageX, e.pageY) - offset
|
||||
}
|
||||
|
||||
fun draw() {
|
||||
clear()
|
||||
for (shape in shapes) {
|
||||
shape.draw()
|
||||
}
|
||||
}
|
||||
|
||||
fun clear() {
|
||||
context.fillStyle = colors.white
|
||||
context.fillRect(0, 0, width.toInt(), height.toInt())
|
||||
}
|
||||
}
|
||||
|
||||
abstract class Shape() {
|
||||
abstract var pos : Vector
|
||||
abstract fun draw()
|
||||
|
||||
// a couple of helper extension methods we'll be using in the derived classes
|
||||
fun Context.shadowed(shadowOffset : Vector, alpha : Double, render : Context.() -> Unit) {
|
||||
save()
|
||||
shadowColor = "rgba(100, 100, 100, $alpha)"
|
||||
shadowBlur = 5.0
|
||||
shadowOffsetX = shadowOffset.x
|
||||
shadowOffsetY = shadowOffset.y
|
||||
render()
|
||||
restore()
|
||||
}
|
||||
|
||||
fun Context.fillPath(constructPath : Context.() -> Unit) {
|
||||
beginPath()
|
||||
constructPath()
|
||||
closePath()
|
||||
fill()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class Vector(val x : Double = 0.0, val y : Double = 0.0) {
|
||||
fun plus(v : Vector) = v(x + v.x, y + v.y)
|
||||
fun minus() = v(- x, - y)
|
||||
fun minus(v : Vector) = v(x - v.x, y - v.y)
|
||||
fun times(koef : Double) = v(x * koef, y * koef)
|
||||
fun distanceTo(v : Vector) = Math.sqrt((this - v).sqr)
|
||||
fun rotatedBy(theta : Double) : Vector {
|
||||
val sin = Math.sin(theta)
|
||||
val cos = Math.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)
|
||||
|
||||
val sqr : Double
|
||||
get() = x * x + y * y
|
||||
val normalized : Vector
|
||||
get() = this * (1.0 / Math.sqrt(sqr))
|
||||
}
|
||||
|
||||
fun getRandomArbitary(val min : Int, val max : Int) : Double {
|
||||
return Math.random() * (max - min) + min;
|
||||
}
|
||||
Reference in New Issue
Block a user