Skip to content

Instantly share code, notes, and snippets.

@lihaoyi
Forked from lihaoyi/BasicOperations.scala
Last active January 13, 2017 12:42
Show Gist options
  • Save lihaoyi/9443f8e0ecc68d1058ad to your computer and use it in GitHub Desktop.
Save lihaoyi/9443f8e0ecc68d1058ad to your computer and use it in GitHub Desktop.
object ScalaJSExample extends js.JSApp{
def main() = {
val xs = Seq(1, 2, 3)
println(xs.toString)
val ys = Seq(4, 5, 6)
println(ys.toString)
val zs = for{
x <- xs
y <- ys
} yield x * y
println(zs.toString)
}
}
import scala.util.Random
case class Point(x: Double, y: Double){
def +(p: Point) = Point(x + p.x, y + p.y)
def -(p: Point) = Point(x - p.x, y - p.y)
def *(d: Double) = Point(x * d, y * d)
def /(d: Double) = Point(x / d, y / d)
def length = Math.sqrt(x * x + y * y)
}
class Enemy(var pos: Point, var vel: Point)
object ScalaJSExample extends js.JSApp{
import Page._
var startTime = js.Date.now()
var player = Point(dom.innerWidth.toInt/2, dom.innerHeight.toInt/2)
var enemies = Seq.empty[Enemy]
var death: Option[(String, Int)] = None
def run() = {
enemies = enemies.filter(e =>
e.pos.x >= 0 && e.pos.x <= canvas.width &&
e.pos.y >= 0 && e.pos.y <= canvas.height
)
def randSpeed = Random.nextInt(5) - 3
enemies = enemies ++ Seq.fill(20 - enemies.length)(
new Enemy(
Point(Random.nextInt(canvas.width.toInt), 0),
Point(randSpeed, randSpeed)
)
)
for(enemy <- enemies){
enemy.pos = enemy.pos + enemy.vel
val delta = player - enemy.pos
enemy.vel = enemy.vel + delta / delta.length / 100
}
if(enemies.exists(e => (e.pos - player).length < 20)){
death = Some((s"You lasted $deltaT seconds", 100))
enemies = enemies.filter(e => (e.pos - player).length > 20)
}
}
def deltaT = ((js.Date.now() - startTime) / 1000).toInt
def draw() = {
renderer.clearRect(0, 0, canvas.width, canvas.height)
death match{
case None =>
renderer.fillStyle = "white"
renderer.fillRect(player.x - 10, player.y - 10, 20, 20)
renderer.fillText("player", player.x - 15, player.y - 30)
renderer.fillStyle = "red"
for (enemy <- enemies){
renderer.fillRect(enemy.pos.x - 10, enemy.pos.y - 10, 20, 20)
}
renderer.fillStyle = "white"
renderer.fillText(s"$deltaT seconds", canvas.width / 2 - 100, canvas.height / 5)
case Some((msg, time)) =>
renderer.fillStyle = "white"
renderer.fillText(msg, canvas.width / 2 - 100, canvas.height / 2)
if (time - 1 == 0){
death = None
startTime = js.Date.now()
}else{
death = Option((msg, time - 1))
}
}
}
def main() = {
dom.document.onmousemove = { (e: dom.MouseEvent) =>
player = Point(e.clientX.toInt, e.clientY.toInt)
(): js.Any
}
dom.setInterval(() => {run(); draw()}, 20)
}
}
/**
* A spiritual clone of Flappy Bird. Click to jump, don't hit the white barriers!
*
* High scores are saved using HTML5 Local Storage.
*/
object ScalaJSExample extends js.JSApp{
import Page._
var game = new Game()
def main() = {
dom.setInterval(() => game.run(), 20 * 800 / canvas.width)
canvas.onclick = { (e: dom.MouseEvent) =>
game.v += canvas.height / 200
}
}
class Game{
renderer.fillStyle = "black"
renderer.fillRect(0, 0, canvas.width, canvas.height)
var i = 0.0
var h = canvas.height / 2.0
var v = 0.0
val a = -0.05
def run() = {
val imageData = renderer.getImageData(1, 0, canvas.width-1, canvas.height)
clear()
println("Score: " + i.toDouble.toInt)
val highScore = Option(dom.localStorage.getItem("highScore")).fold(0)(_.toString.toDouble.toInt)
println("High Score: " + highScore)
i += 1
dom.localStorage.setItem("highScore", Math.max(i, highScore).toString)
v += a
h += v
val spotData = renderer.getImageData(canvas.width/2 + 3, canvas.height - h, 1, 1).data
if (spotData.take(3).map(_.toInt).sum != 0 || h > canvas.height || h < 0) {
game = new Game()
} else{
renderer.putImageData(imageData, 0, 0)
renderer.fillStyle = "black"
renderer.fillRect(canvas.width-1, 0, 1, canvas.height)
if (i.toInt % (canvas.width / 3).toInt == 0){
val gapHeight = canvas.height / 5
val gap = util.Random.nextInt(canvas.height.toInt - gapHeight.toInt)
renderer.fillStyle = "white"
renderer.fillRect(canvas.width-2, 0, 2, gap)
renderer.fillRect(canvas.width-2, gap + gapHeight, 2, canvas.height - gap - gapHeight)
}
val r = (h / canvas.height * 255).toInt
val b = (255 / (1 + Math.pow(Math.E, -v))).toInt
renderer.fillStyle = s"rgb($r, 255, $b)"
renderer.fillRect(canvas.width / 2, canvas.height - h, 2, 2)
}
}
}
}
/**
* Simple, zero-dependency, self-contained JSON parser, serializer and AST
* taken from
*
* https://github.com/nestorpersist/json
*
* Notably, does not use the browser's native JSON capability, but re-implements
* it all in order to be 100% compatible with both Scala-JS and Scala-JVM
*/
object ScalaJSExample extends js.JSApp{
def main() = {
val parsed = Json.read(ugly)
println(parsed(0).toString)
println(parsed(8)("real").toString)
println(parsed(8)("comment").toString)
println(parsed(8)("jsontext").toString)
println(Json.write(parsed(8)).toString)
}
val ugly =
"""
|[
| "JSON Test Pattern pass1",
| {"object with 1 member":["array with 1 element"]},
| {},
| [],
| -42,
| true,
| false,
| null,
| {
| "integer": 1234567890,
| "real": -9876.543210,
| "e": 0.123456789e-12,
| "E": 1.234567890E+34,
| "": 23456789012E66,
| "zero": 0,
| "one": 1,
| "space": " ",
| "quote": "\"",
| "backslash": "\\",
| "controls": "\b\f\n\r\t",
| "slash": "/ & \/",
| "alpha": "abcdefghijklmnopqrstuvwyz",
| "ALPHA": "ABCDEFGHIJKLMNOPQRSTUVWYZ",
| "digit": "0123456789",
| "0123456789": "digit",
| "special": "`1~!@#$%^&*()_+-={':[,]}|;.</>?",
| "hex": "\u0123\u4567\u89AB\uCDEF\uabcd\uef4A",
| "true": true,
| "false": false,
| "null": null,
| "array":[ ],
| "object":{ },
| "address": "50 St. James Street",
| "url": "http://www.JSON.org/",
| "comment": "// /* <!-- --",
| "# -- --> */": " ",
| " s p a c e d " :[1,2 , 3
|
|,
|
|4 , 5 , 6 ,7 ],"compact":[1,2,3,4,5,6,7],
| "jsontext": "{\"object with 1 member\":[\"array with 1 element\"]}",
| "quotes": "&#34; \u005Cu0022 %22 0x22 034 &#x22;",
| "\/\\\"\uCAFE\uBABE\uAB98\uFCDE\ubcda\uef4A\b\f\n\r\t`1~!@#$%^&*()_+-=[]{}|;:',./<>?"
|: "A key can be any string"
| },
| 0.5 ,98.6
|,
|99.44
|,
|
|1066,
|1e1,
|0.1e1,
|1e-1,
|1e00,2e+00,2e-00
|,"rosebud"]
""".stripMargin
}
object Js {
sealed trait Value{
def value: Any
def apply(i: Int): Value = this.asInstanceOf[Array].value(i)
def apply(s: java.lang.String): Value = this.asInstanceOf[Object].value.find(_._1 == s).get._2
}
case class String(value: java.lang.String) extends Value
case class Object(value: Seq[(java.lang.String, Value)]) extends Value
case class Array(value: Seq[Value]) extends Value
case class Number(value: java.lang.String) extends Value
case object False extends Value{
def value = true
}
case object True extends Value{
def value = false
}
case object Null extends Value{
def value = null
}
}
object Json {
def write(v: Js.Value): String = v match {
case Js.String(s) =>
val out = s.flatMap {
case '\\' => "\\\\"
case '"' => "\\\""
case '/' => "\\/"
case '\b' => "\\b"
case '\t' => "\\t"
case '\n' => "\\n"
case '\f' => "\\f"
case '\r' => "\\r"
case c if c < ' ' =>
val t = "000" + Integer.toHexString(c)
"\\u" + t.takeRight(4)
case c => c.toString
}
'"' + out + '"'
case Js.Object(kv) =>
val contents = kv.toIterator.map{
case (k, v) => s"${write(Js.String(k))}: ${write(v)}"
}.mkString(", ")
s"{$contents}"
case Js.Array(vs) => s"[${vs.map(write).mkString(", ")}]"
case Js.Number(d) => d
case Js.False => "false"
case Js.True => "true"
case Js.Null => "null"
}
/**
* Self-contained JSON parser adapted from
*
* https://github.com/nestorpersist/json
*/
def read(s: String): Js.Value = {
// *** Character Kinds
type CharKind = Int
val Letter = 0
val Digit = 1
val Minus = 2
val Quote = 3
val Colon = 4
val Comma = 5
val Lbra = 6
val Rbra = 7
val Larr = 8
val Rarr = 9
val Blank = 10
val Other = 11
val Eof = 12
val Slash = 13
// *** Token Kinds
type TokenKind = Int
val ID = 0
val STRING = 1
val NUMBER = 2
val BIGNUMBER = 3
val FLOATNUMBER = 4
val COLON = 5
val COMMA = 6
val LOBJ = 7
val ROBJ = 8
val LARR = 9
val RARR = 10
val BLANK = 11
val EOF = 12
// *** Character => CharKind Map ***
val charKind = (0 to 255).toArray.map {
case c if 'a'.toInt <= c && c <= 'z'.toInt => Letter
case c if 'A'.toInt <= c && c <= 'Z'.toInt => Letter
case c if '0'.toInt <= c && c <= '9'.toInt => Digit
case '-' => Minus
case ',' => Comma
case '"' => Quote
case ':' => Colon
case '{' => Lbra
case '}' => Rbra
case '[' => Larr
case ']' => Rarr
case ' ' => Blank
case '\t' => Blank
case '\n' => Blank
case '\r' => Blank
case '/' => Slash
case _ => Other
}
// *** Character Escapes
val escapeMap = Map[Int, String](
'\\'.toInt -> "\\",
'/'.toInt -> "/",
'\"'.toInt -> "\"",
'b'.toInt -> "\b",
'f'.toInt -> "\f",
'n'.toInt -> "\n",
'r'.toInt -> "\r",
't'.toInt -> "\t"
)
// *** Import Shared Data ***
// *** INPUT STRING ***
// array faster than accessing string directly using charAt
//final val s1 = s.toCharArray()
val size = s.size
// *** CHARACTERS ***
var pos = 0
var ch: Int = 0
var chKind: CharKind = 0
var chLinePos: Int = 0
var chCharPos: Int = 0
def chNext() = {
if (pos < size) {
//ch = s1(pos).toInt
ch = s.charAt(pos)
chKind = if (ch < 255) {
charKind(ch)
} else {
Other
}
pos += 1
if (ch == '\n'.toInt) {
chLinePos += 1
chCharPos = 1
} else {
chCharPos += 1
}
} else {
ch = -1
pos = size + 1
chKind = Eof
}
}
def chError(msg: String): Nothing = {
throw new Json.Exception(msg, s, chLinePos, chCharPos)
}
def chMark = pos - 1
def chSubstr(first: Int, delta: Int = 0) = {
s.substring(first, pos - 1 - delta)
}
// *** LEXER ***
var tokenKind = BLANK
var tokenValue = ""
var linePos = 1
var charPos = 1
def getDigits() = {
while (chKind == Digit) chNext()
}
def handleDigit() {
val first = chMark
getDigits()
val k1 = if (ch == '.'.toInt) {
chNext()
getDigits()
BIGNUMBER
} else {
NUMBER
}
val k2 = if (ch == 'E'.toInt || ch == 'e'.toInt) {
chNext()
if (ch == '+'.toInt) {
chNext()
} else if (ch == '-'.toInt) {
chNext()
}
getDigits()
FLOATNUMBER
} else {
k1
}
tokenKind = k2
tokenValue = chSubstr(first)
}
def handleRaw() {
chNext()
val first = chMark
var state = 0
do {
if (chKind == Eof) chError("EOF encountered in raw string")
state = (ch, state) match {
case ('}', _) => 1
case ('"', 1) => 2
case ('"', 2) => 3
case ('"', 3) => 0
case _ => 0
}
chNext()
} while (state != 3)
tokenKind = STRING
tokenValue = chSubstr(first, 3)
}
def handle(i: Int) = {
chNext()
tokenKind = i
tokenValue = ""
}
def tokenNext() {
do {
linePos = chLinePos
charPos = chCharPos
val kind: Int = chKind
kind match {
case Letter =>
val first = chMark
while (chKind == Letter || chKind == Digit) {
chNext()
}
tokenKind = ID
tokenValue = chSubstr(first)
case Digit => handleDigit()
case Minus =>
chNext()
handleDigit()
tokenValue = "-" + tokenValue
case Quote =>
val sb = new StringBuilder(50)
chNext()
var first = chMark
while (ch != '"'.toInt && ch >= 32) {
if (ch == '\\'.toInt) {
sb.append(chSubstr(first))
chNext()
escapeMap.get(ch) match {
case Some(s) =>
sb.append(s)
chNext()
case None =>
if (ch != 'u'.toInt) chError("Illegal escape")
chNext()
var code = 0
for (i <- 1 to 4) {
val ch1 = ch.toChar.toString
val i = "0123456789abcdef".indexOf(ch1.toLowerCase)
if (i == -1) chError("Illegal hex character")
code = code * 16 + i
chNext()
}
sb.append(code.toChar.toString)
}
first = chMark
} else {
chNext()
}
}
if (ch != '"') chError("Unexpected string character: " + ch.toChar)
sb.append(chSubstr(first))
tokenKind = STRING
tokenValue = sb.toString()
chNext()
if (tokenValue.length() == 0 && ch == '{') {
handleRaw()
}
case Colon => handle(COLON)
case Comma => handle(COMMA)
case Lbra => handle(LOBJ)
case Rbra => handle(ROBJ)
case Larr => handle(LARR)
case Rarr => handle(RARR)
case Blank =>
do chNext() while (chKind == Blank)
tokenKind = BLANK
tokenValue = ""
case Other => chError("Unexpected character: " + ch.toChar + " " + ch)
case Eof =>
chNext()
tokenKind = EOF
tokenValue = ""
case Slash =>
if (chKind != Slash) chError("Expecting Slash")
do chNext() while (ch != '\n' && chKind != Eof)
tokenKind = BLANK
tokenValue = ""
}
} while (tokenKind == BLANK)
}
def tokenError(msg: String): Nothing = {
throw new Json.Exception(msg, s, linePos, charPos)
}
// *** PARSER ***
def handleEof() = tokenError("Unexpected eof")
def handleUnexpected(i: String) = tokenError(s"Unexpected input: [$i]")
def handleArray(): Js.Array = {
tokenNext()
var result = List.empty[Js.Value]
while (tokenKind != RARR) {
result = getJson() :: result
tokenKind match{
case COMMA => tokenNext()
case RARR => // do nothing
case _ => tokenError("Expecting , or ]")
}
}
tokenNext()
Js.Array(result.reverse)
}
def handleObject(): Js.Object = {
tokenNext()
var result = List.empty[(String, Js.Value)]
while (tokenKind != ROBJ) {
if (tokenKind != STRING && tokenKind != ID) tokenError("Expecting string or name")
val name = tokenValue
tokenNext()
if (tokenKind != COLON) tokenError("Expecting :")
tokenNext()
result = (name -> getJson()) :: result
tokenKind match{
case COMMA => tokenNext()
case ROBJ => // do nothing
case _ => tokenError("Expecting , or }")
}
}
tokenNext()
Js.Object(result.reverse)
}
def handleNumber(name: String, f: String => Unit) = {
val v = try {
f(tokenValue)
} catch {
case _: Throwable => tokenError("Bad " + name)
}
val old = tokenValue
tokenNext()
Js.Number(old)
}
def getJson(): Js.Value = {
val kind: Int = tokenKind
val result: Js.Value = kind match {
case ID =>
val result = tokenValue match {
case "true" => Js.True
case "false" => Js.False
case "null" => Js.Null
case _ => tokenError("Not true, false, or null")
}
tokenNext()
result
case STRING =>
val result = tokenValue
tokenNext()
Js.String(result)
case NUMBER => handleNumber("NUMBER", _.toLong)
case BIGNUMBER => handleNumber("BIGNUMBER", _.toDouble)
case FLOATNUMBER => handleNumber("FLOATNUMBER", _.toDouble)
case COLON => handleUnexpected(":")
case COMMA => handleUnexpected(",")
case LOBJ => handleObject()
case ROBJ => handleUnexpected("}")
case LARR => handleArray()
case RARR => handleUnexpected("]")
case EOF => handleEof()
}
result
}
def parse(): Js.Value = {
chNext()
tokenNext()
val result = getJson
if (tokenKind != EOF) tokenError("Excess input")
result
}
parse()
}
class Exception(val msg: String,
val input: String,
val line: Int,
val char: Int)
extends scala.Exception(s"JsonParse Error: $msg line $line [$char] in $input")
}
import scalatags.JsDom.all._
/**
* Splash page for www.scala-js-fiddle.com. Uses Scalatags to render a HTML page
* containing the text on the page, while using a HTML5 Canvas element to slowly
* render a Sierpinski Triangle behind the text pixel by pixel.
*/
object ScalaJSExample extends js.JSApp{
def main() = {
Page.clear()
println(
div(
padding:="10px",
id:="page",
h1(
marginTop:="0px",
img(src:="/Shield.svg", height:="40px", marginBottom:="-10px"),
" Scala.jsFiddle"
),
p(
"Scala.jsFiddle is an online scratchpad to try out your ",
a(href:="http://www.scala-js.org/", "Scala.js "),
"snippets. Enter Scala code on the left and see the results on the right, ",
"after the Scala is compiled to Javascript using the Scala.js compiler."
),
ul(
li(yellow("Ctrl/Cmd-Enter"), ": compile and execute"),
li(yellow("Ctrl/Cmd-Shift-Enter"), ": compile and execute optimized code"),
li(yellow("Ctrl/Cmd-Space"), ": autocomplete at caret"),
li(yellow("Ctrl/Cmd-J"), ": show the generated Javascript code"),
li(yellow("Ctrl/Cmd-Shift-J"), ": show the generated code, optimized by Google Closure"),
li(yellow("Ctrl/Cmd-S"), ": save the code to a public Gist"),
li(yellow("Ctrl/Cmd-E"), ": export your code to a stand-alone web page")
),
p(
"To load the code from an existing gist, simply go to "
),
ul(li(green(s"${Page.fiddleUrl}/gist/<gist-id>"))),
p(
"Where ",green("<gist-id>"), " is the last section of the gist's URL.",
"You can also go to:"
),
ul(li(green(s"${Page.fiddleUrl}/gist/<gist-id>/<file-name>"))),
p(
"If you want a particular file in a multi file gist."
),
p(
"If you need ideas of things you can make using Scala.jsFiddle, check ",
"out some of our examples:"
),
ul(
for {
(file, name) <- Seq(
"BasicOperations.scala" -> "Basic Operations",
"SierpinskiTriangle.scala" -> "Sierpinski Triangle",
"Turmites.scala" -> "Turmites",
"Oscilloscope.scala" -> "Oscilloscope",
"FlappyLine.scala" -> "Flappy Line",
"SquareRoot.scala" -> "Square Root solver",
"DodgeTheDots.scala" -> "Dodge the Dots",
"SpaceInvaders.scala" -> "Space Invaders",
"SquareRootRx.scala" -> "Square Root solver with Scala.rx",
"TodoMVC.scala" -> "TodoMVC",
"JsonParser.scala" -> "JSON Parser",
// "ScalaAsyncPaintbrush.scala" -> "Scala Async Paintbrush",
"RayTracer.scala" -> "RayTracer"
)
} yield li(
a(href:=(dom.document.location.origin + s"/gist/${Page.fiddleGistId}/" + file), name)
)
),
p("Scala-Js-Fiddle comes with the following default imports at the top of every program:"),
pre(Page.fiddlePrelude),
p(
"This imports the Scala.js ", blue("js"), " and ", blue("dom"), " namespaces ",
"ready to use, imports the ", blue("Page"), " object and imports the ",
blue("scalatags.JsDom"), " namespace to support the helper functions in ",
blue("Page"), "."
),
p(blue("Page"), " provides the following built-in variables to give you started quickly:"),
ul(
li(
green("canvas"), ": a ", blue("dom.HTMLCanvasElement"), " that lets you draw ",
"on the right pane"
),
li(
green("renderer"), ": a ", blue("dom.CanvasRenderingContext2D"), " that ",
"lets you draw on the ", blue("canvas")
),
li(
green("print/println(s: Frag*)"), ": prints the given values to the right pane as ",
"a scalatags ", blue("Frag"), "s. These could be ", blue("String"), "s, numbers, or ",
"Scalatags ", blue("Tag"), "s. This behaves slightly differently from the built-in ",
blue("println"), " which is available as ", blue("Predef.println"), " and prints to ",
"the browser console"
),
li(green("clear()"), ": Removes all printed output from the right pane"),
li(
green("scroll(px: Int)"), ": Scrolls the right pane up or down by ",
"the given number of pixels"
),
li(
"The colors ", red("red"), ", ", green("green"), ", ", blue("blue"),
", ", yellow("yellow"), " and ", orange("orange"),
" to help prettify your output"
)
),
p("Apart from these inbuilt helpers, available libraries include:"),
ul(
li(a(href:="https://github.com/lihaoyi/scalatags", "scalatags")),
li(a(href:="https://github.com/lihaoyi/scala.rx", "scala.rx")),
li(a(href:="https://github.com/scala-js/scala-js-dom", "scala-js-dom")),
li(a(href:="https://github.com/scala/async", "scala-async"))
),
p(
"Scala.jsFiddle is made by ", a(href:="https://github.com/lihaoyi")("Li Haoyi"),
" and can be found on ", a(href:="https://github.com/lihaoyi/scala-js-fiddle")("Github")
)
)
)
// scroll to top so if the users screen is too short, he sees the top of the
// rendered page rather than the bottom
Page.scroll(-1000)
}
}
import Math._
object ScalaJSExample extends js.JSApp{
def main(): Unit = {
val (h, w) = (Page.canvas.height, Page.canvas.width)
var x = 0.0
val graphs = Seq[(String, Double => Double)](
("red", sin),
("green", x => 2 - abs(x % 8 - 4)),
("blue", x => 3 * pow(sin(x / 12), 2) * sin(x))
).zipWithIndex
dom.setInterval(() => {
x = (x + 1) % w
if (x == 0) Page.renderer.clearRect(0, 0, w, h)
else for (((color, func), i) <- graphs) {
val y = func(x/w * 75) * h/40 + h/3 * (i+0.5)
Page.renderer.fillStyle = color
Page.renderer.fillRect(x, y, 3, 3)
}
}, 10)
}
}
import math._
import scala.scalajs.concurrent.JSExecutionContext.Implicits.queue
import ScalaJSExample.{Color, Epsilon}
import scala.language.postfixOps
/**
* A simple ray tracer, taken from the PyPy benchmarks
*
* https://bitbucket.org/pypy/benchmarks/src/846fa56a282b/own/raytrace-simple.py?at=default
*
* Half the lines of code
*/
object ScalaJSExample extends js.JSApp{
import Page._
val Epsilon = 0.00001
type Color = Vec
val Color = Vec
def main() = {
val r = new util.Random(16314302)
val spiral = for (i <- 0 until 11) yield {
val theta = i * (i + 5) * Pi / 100 + 0.3
val center = (0 - 4 * sin(theta), 1.5 - i / 2.0, 0 - 4 * cos(theta))
val form = Sphere(center, 0.3 + i * 0.1)
val surface = Flat((i / 6.0, 1 - i / 6.0, 0.5))
(form, surface)
}
def rand(d: Double) = (r.nextDouble() - 0.5) * d * 2
val drops = Array(
Sphere((2.5, 2.5, -8), 0.3),
Sphere((1.5, 2.2, -7), 0.25),
Sphere((-1.3, 0.8, -8.5), 0.15),
Sphere((0.5, -2.5, -7.5), 0.2),
Sphere((-1.8, 2.3, -7.5), 0.3),
Sphere((-1.8, -2.3, -7.5), 0.3),
Sphere((1.3, 0.0, -8), 0.25)
).map(_ -> Refractor())
val s = new Scene(
objects = Array(
Sphere((0, 0, 0), 2) -> Flat((1, 1, 1), specularC = 0.6, lambertC = 0.4),
Plane((0, 4, 0), (0, 1, 0)) -> Checked(),
Plane((0, -4, 0), (0, 1, 0)) -> Flat((0.9, 1, 1)),
Plane((6, 0, 0), (1, 0, 0)) -> Flat((1, 0.9, 1)),
Plane((-6, 0, 0), (1, 0, 0)) -> Flat((1, 1, 0.9)),
Plane((0, 0, 6), (0, 0, 1)) -> Flat((0.9, 0.9, 1))
) ++ spiral ++ drops,
lightPoints = Array(
Light((0, -3, 0), (3, 3, 0)),
Light((3, 3, 0), (0, 3, 3)),
Light((-3, 3, 0), (3, 0, 3))
),
position = (0, 0, -15),
lookingAt = (0, 0, 0),
fieldOfView = 45.0
)
val c = new Canvas{
val width = math.min(canvas.width.toInt, canvas.height.toInt)
val height = math.min(canvas.width.toInt, canvas.height.toInt)
val data = renderer.getImageData(0, 0, canvas.width, canvas.height)
def save(y: Int): Unit = {
renderer.putImageData(data, 0, 0, 0, y-1, width, 1)
}
def plot(x: Int, y: Int, rgb: ScalaJSExample.Color): Unit = {
val index = (y * data.width + x) * 4
data.data(index+0) = (rgb.x * 255).toInt
data.data(index+1) = (rgb.y * 255).toInt
data.data(index+2) = (rgb.z * 255).toInt
data.data(index+3) = 255
}
}
s.render(c)
}
}
final case class Vec(x: Double, y: Double, z: Double){
def magnitude = sqrt(this dot this)
def +(o: Vec) = Vec(x + o.x, y + o.y, z + o.z)
def -(o: Vec) = Vec(x - o.x, y - o.y, z - o.z)
def *(o: Vec) = Vec(x * o.x, y * o.y, z * o.z)
def *(f: Double) = Vec(x * f, y * f, z * f)
def /(f: Double) = Vec(x / f, y / f, z / f)
def dot(o: Vec) = x * o.x + y * o.y + z * o.z
def cross(o: Vec) = Vec(
y * o.z - z * o.y,
z * o.x - x * o.z,
x * o.y - y * o.x
)
def normalized = this / magnitude
def reflectThrough(normal: Vec) = this - normal * (this dot normal) * 2
}
object Vec{
case class Unit(x: Double, y: Double, z: Double)
implicit def normalizer(v: Vec) = {
val l = v.magnitude
new Unit(v.x / l, v.y / l, v.z / l)
}
implicit def denormalizer(v: Vec.Unit) = new Vec(v.x, v.y, v.z)
implicit def pointify[X: Numeric, Y: Numeric, Z: Numeric](x: (X, Y, Z)): Vec = Vec(
implicitly[Numeric[X]].toDouble(x._1),
implicitly[Numeric[Y]].toDouble(x._2),
implicitly[Numeric[Z]].toDouble(x._3)
)
implicit def pointify2[X: Numeric, Y: Numeric, Z: Numeric](x: (X, Y, Z)): Vec.Unit = Vec.normalizer(x)
}
abstract class Form{
def intersectionTime(ray: Ray): Double
def normalAt(p: Vec): Vec
}
case class Sphere(center: Vec, radius: Double) extends Form{
def intersectionTime(ray: Ray) = {
val cp = center - ray.point
val v = cp dot ray.vector
val d = radius * radius - ((cp dot cp) - v * v)
if (d < 0) -1
else v - sqrt(d)
}
def normalAt(p: Vec) = (p - center).normalized
}
case class Plane(point: Vec, normal: Vec.Unit) extends Form{
def intersectionTime(ray: Ray) = {
val v = ray.vector dot normal
if (v != 0) ((point - ray.point) dot normal) / v
else -1
}
def normalAt(p: Vec) = normal
}
case class Ray(point: Vec, vector: Vec.Unit){
def pointAtTime(t: Double) = point + vector * t
}
case class Light(center: Vec, color: Color)
abstract class Surface{
def colorAt(scene: Scene, ray: Ray, p: Vec, normal: Vec.Unit, depth: Int): Color
}
abstract class SolidSurface extends Surface{
def baseColorAt(p: Vec): Color
def specularC: Double
def lambertC: Double
val ambientC = 1.0 - specularC - lambertC
def colorAt(scene: Scene, ray: Ray, p: Vec, normal: Vec.Unit, depth: Int): Color = {
val b = baseColorAt(p)
val specular = {
val reflectedRay = Ray(p, ray.vector.reflectThrough(normal))
val reflectedColor = scene.rayColor(reflectedRay, depth)
reflectedColor * specularC
}
val lambert = {
var lambertAmount = Vec(0, 0, 0)
for (i <- 0 until scene.lightPoints.length) {
val light = scene.lightPoints(i)
if (scene.lightIsVisible(light.center, p)) {
val d = p - light.center
val dLength = d.magnitude
val contribution = light.color * abs(d dot normal / (dLength * dLength))
lambertAmount += contribution
}
}
b * lambertAmount * lambertC
}
val ambient = b * ambientC
specular + lambert + ambient
}
}
case class Refractor(refractiveIndex: Double = 0.5) extends Surface{
def colorAt(scene: Scene, ray: Ray, p: Vec, normal: Vec.Unit, depth: Int): Color = {
val r = if ((normal dot ray.vector) < 0)
refractiveIndex
else
1.0 / refractiveIndex
val c = (normal * -1) dot ray.vector
val sqrtValue = 1 - r * r * (1 - c * c)
if (sqrtValue > 0){
val refracted = ray.vector * r + normal * (r * c - sqrt(sqrtValue))
scene.rayColor(Ray(p, refracted), depth)
}else{
val perp = ray.vector dot normal
val reflected: Vec = Vec.denormalizer(ray.vector) + normal * 2 * perp
scene.rayColor(Ray(p, reflected), depth)
}
}
}
case class Flat(baseColor: Color = Color(1, 1, 1),
specularC: Double = 0.3,
lambertC: Double = 0.6) extends SolidSurface{
def baseColorAt(p: Vec) = baseColor
}
case class Checked(baseColor: Color = Color(1, 1, 1),
specularC: Double = 0.3,
lambertC: Double = 0.6,
otherColor: Color = (0, 0, 0),
checkSize: Double = 1) extends SolidSurface{
override def baseColorAt(p: Vec) = {
val v = p * (1.0 / checkSize)
def f(x: Double) = (abs(x) + 0.5).toInt
if ((f(v.x) + f(v.y) + f(v.z)) % 2 == 1) otherColor
else baseColor
}
}
abstract class Canvas{
def width: Int
def height: Int
def save(y: Int): Unit
def plot(x: Int, y: Int, rgb: Color)
}
class Scene(objects: Array[(Form, Surface)],
val lightPoints: Array[Light],
position: Vec,
lookingAt: Vec,
fieldOfView: Double){
def lightIsVisible(l: Vec, p: Vec) = {
val ray = Ray(p, l - p)
val length = (l - p).magnitude
var visible = true
for (i <- 0 until objects.length){
val (o, s) = objects(i)
val t = o.intersectionTime(ray)
if (t > Epsilon && t < length - Epsilon){
visible = false
}
}
visible
}
def rayColor(ray: Ray, depth: Int): Color = {
if (depth > 3) (0, 0, 0)
else{
var (minT, minO, minS) = (-1.0, null: Form, null: Surface)
for(i <- 0 until objects.length){
val (o, s) = objects(i)
val t = o.intersectionTime(ray)
if (t > Epsilon && (t < minT || minT < 0)){
minT = t
minO = o
minS = s
}
}
minT match{
case -1 => (0, 0, 0)
case t =>
val p = ray.pointAtTime(minT)
minS.colorAt(this, ray, p, minO.normalAt(p), depth + 1)
}
}
}
def render(canvas: Canvas) = {
val fovRadians = Pi * (fieldOfView / 2.0) / 180.0
val halfWidth = tan(fovRadians)
val halfHeight = halfWidth
val width = halfWidth * 2
val height= halfHeight * 2
val pixelWidth = width / (canvas.width - 1)
val pixelHeight = height / (canvas.height - 1)
val eye = Ray(position, lookingAt - position)
val vpRight = eye.vector.cross((0, 1, 0)).normalized
val vpUp = vpRight.cross(eye.vector).normalized
var y = 0;
lazy val interval: Int = dom.setInterval({ () =>
for (x <- 0 until canvas.width){
val xcomp = vpRight * (x * pixelWidth - halfWidth)
val ycomp = vpUp * (y * pixelHeight - halfHeight)
val ray = Ray(eye.point, xcomp + ycomp + eye.vector)
val color = rayColor(ray, 0)
canvas.plot(x, y, color)
}
canvas.save(y)
if (y > canvas.height) dom.clearInterval(interval)
y+= 1
}, 0)
interval
}
}
import org.scalajs.dom
import concurrent._
import async.Async._
import scalajs.concurrent.JSExecutionContext.Implicits.queue
/**
* Re-implementation of the mouse-drag example from Deprecating the Observer
* Pattern
*
* http://lampwww.epfl.ch/~imaier/pub/DeprecatingObserversTR2010.pdf
*
* Using Scala.Async instead of Scala.React and continuations. Click and drag
* around the canvas on the right to see pretty shapes appear.
*/
object ScalaJSExample extends js.JSApp{
import Page._
def main() = {
val command = Channel[dom.MouseEvent]()
canvas.onmousemove = command.update _
canvas.onmouseup = command.update _
canvas.onmousedown = command.update _
renderer.lineWidth = 5
renderer.strokeStyle = "red"
renderer.fillStyle = "cyan"
val flow = async{
while(true){
renderer.beginPath()
val start = await(command.filter(_.`type` == "mousedown")())
renderer.moveTo(start.clientX - canvas.width, start.clientY)
var m = await(command())
while(m.`type` == "mousemove"){
renderer.lineTo(m.clientX - canvas.width, m.clientY)
renderer.stroke()
m = await(command())
}
renderer.fill()
}
}
}
}
case class Channel[T](){
private[this] var value: Promise[T] = null
def apply(): Future[T] = {
value = Promise[T]()
value.future
}
def update(t: T) = {
if (value != null && !value.isCompleted) value.success(t)
}
def filter(p: T => Boolean) = {
val filtered = Channel[T]()
async{
while(true){
val t = await(this())
if (p(t)) filtered() = t
}
}
filtered
}
}
case class Pt(x: Double, y: Double)
object ScalaJSExample extends js.JSApp{
println("Hello!!")
val corners = Seq(
Pt(Page.canvas.width/2, 0),
Pt(0, Page.canvas.height),
Pt(Page.canvas.width, Page.canvas.height)
)
var p = corners(0)
val (w, h) = (Page.canvas.height.toDouble, Page.canvas.height.toDouble)
def main() = {
dom.setInterval(() => for(_ <- 0 until 10){
val c = corners(util.Random.nextInt(3))
p = Pt((p.x + c.x) / 2, (p.y + c.y) / 2)
val m = (p.y / h)
val r = 255 - (p.x / w * m * 255).toInt
val g = 255 - ((w-p.x) / w * m * 255).toInt
val b = 255 - ((h - p.y) / h * 255).toInt
Page.renderer.fillStyle = s"rgb($r, $g, $b)"
Page.renderer.fillRect(p.x, p.y, 1, 1)
}, 10)
}
}
object ScalaJSExample extends js.JSApp{
def main(): Unit = {
var i = 0
dom.setInterval(() => for(_ <- 0 until 5){
i += 1
val x = i % canvas.width
val y = Math.sin(i / canvas.width * 10) * canvas.height / 4 + canvas.height / 2
output.clear()
val r = math.abs(((y / canvas.height - 0.5) * 255 * 4).toInt)
println(x)
println(y)
renderer.fillStyle = s"rgb($r, 255, 255)"
renderer.fillRect(x, y, 2, 2)
}, 10)
}
}
case class Point(x: Double, y: Double){
def +(p: Point) = Point(x + p.x, y + p.y)
def -(p: Point) = Point(x - p.x, y - p.y)
def /(d: Double) = Point(x / d, y / d)
def *(d: Double) = Point(x * d, y * d)
def length = Math.sqrt(x * x + y * y)
}
object ScalaJSExample extends js.JSApp{
import Page._
var count = 0
var player = Point(dom.innerWidth / 2, dom.innerHeight / 2)
val corners = Seq(Point(255, 255), Point(0, 255), Point(128, 0))
var bullets = Seq.empty[Point]
var enemies = Seq.empty[Point]
var wave = 1
def run = {
count += 1
bullets = bullets.map(
p => Point(p.x, p.y - 5)
)
if (enemies.isEmpty){
enemies = for{
x <- (0 until canvas.width.toInt by 50)
y <- 0 until wave
} yield {
Point(x, 50 + y * 50)
}
wave += 1
}
enemies = enemies.filter( e =>
!bullets.exists(b =>
(e - b).length < 5
)
)
enemies = enemies.map{ e =>
val i = count % 200
if (i < 50) e.copy(x = e.x - 0.2)
else if (i < 100) e.copy(y = e.y + 0.2)
else if (i < 150) e.copy(x = e.x + 0.2)
else e.copy(y = e.y + 0.2)
}
if (keysDown(38)) player += Point(0, -2)
if (keysDown(37)) player += Point(-2, 0)
if (keysDown(39)) player += Point(2, 0)
if (keysDown(40)) player += Point(0, 2)
}
def draw = {
renderer.clearRect(0, 0, canvas.width, canvas.height)
renderer.fillStyle = "white"
renderer.fillRect(player.x - 5, player.y - 5, 10, 10)
renderer.fillStyle = "yellow"
for (enemy <- enemies){
renderer.fillRect(enemy.x - 5, enemy.y - 5, 10, 10)
}
renderer.fillStyle = "red"
for (bullet <- bullets){
renderer.fillRect(bullet.x - 2, bullet.y - 2, 4, 4)
}
}
val keysDown = collection.mutable.Set.empty[Int]
def main() = {
dom.onkeypress = {(e: dom.KeyboardEvent) =>
if (e.keyCode.toInt == 32) bullets = player +: bullets
}
dom.onkeydown = {(e: dom.KeyboardEvent) =>
keysDown.add(e.keyCode.toInt)
}
dom.onkeyup = {(e: dom.KeyboardEvent) =>
keysDown.remove(e.keyCode.toInt)
}
dom.setInterval(() => {run; draw}, 20)
}
}
/**
* A simple square root solver, one step at a time
*/
object ScalaJSExample extends js.JSApp{
def main() = {
val n = 1000.0
var guess = 1.0
lazy val id: Int = dom.setInterval(() => {
val newGuess = guess - (guess * guess - n) / (2 * guess)
if (newGuess == guess) dom.clearInterval(id)
else guess = newGuess
println(guess)
}, 1000)
id
}
}
/**
* A simple Scala.Rx dataflow graph which looks like:
*
* guess -> error
* ^ |
* | v
* --------o
*
* Where the changes propagate around and around until
* the magnitude of `error` drops below `epsilon`
*
*/
object ScalaJSExample extends js.JSApp{
def main() = {
import rx._
val n = 10.0
val guess = Var(1.0)
val epsilon = 0.0000000001
val error = Rx{ guess() * guess() - n }
val o = Obs(error){
println("Guess: " + guess())
if (math.abs(error()) > epsilon) {
guess() = guess() - error() / (2 * guess())
}
}
}
}
/**
* Port of the Scala.js TodoMVC example application
*
* http://lihaoyi.github.io/workbench-example-app/todo.html
*
* To Scala.jsFiddle. Mostly involved sticking the two source files into one
* (since Scala.jsFiddle doesn't support multiple files) and inlining/tweaking
* the CSS so it looks right
*/
import org.scalajs.dom
import rx._
import scala.collection.{SortedMap, mutable}
import scalatags.JsDom.tags2
import scalatags.JsDom.tags2.section
import scala.util.{Failure, Success, Random}
import rx.core.{Propagator, Obs}
import org.scalajs.dom.{Element, DOMParser}
/**
* A minimal binding between Scala.Rx and Scalatags and Scala-Js-Dom
*/
object Framework {
/**
* Wraps reactive strings in spans, so they can be referenced/replaced
* when the Rx changes.
*/
implicit def RxStr[T](r: Rx[T])(implicit f: T => Frag): Modifier = {
rxMod(Rx(span(r())))
}
/**
* Sticks some Rx into a Scalatags fragment, which means hooking up an Obs
* to propagate changes into the DOM via the element's ID. Monkey-patches
* the Obs onto the element itself so we have a reference to kill it when
* the element leaves the DOM (e.g. it gets deleted).
*/
implicit def rxMod(r: Rx[HtmlTag]): Modifier = {
def rSafe = r.toTry match {
case Success(v) => v.render
case Failure(e) => span(e.toString, backgroundColor := "red").render
}
var last = rSafe
Obs(r, skipInitial = true){
val newLast = rSafe
last.parentElement.replaceChild(newLast, last)
last = newLast
}
last
}
implicit def RxAttrValue[T: AttrValue] = new AttrValue[Rx[T]]{
def apply(t: Element, a: Attr, r: Rx[T]): Unit = {
Obs(r){ implicitly[AttrValue[T]].apply(t, a, r())}
}
}
implicit def RxStyleValue[T: StyleValue] = new StyleValue[Rx[T]]{
def apply(t: Element, s: Style, r: Rx[T]): Unit = {
Obs(r){ implicitly[StyleValue[T]].apply(t, s, r())}
}
}
}
case class Task(txt: Var[String], done: Var[Boolean])
object ScalaJSExample extends js.JSApp{
import Framework._
val editing = Var[Option[Task]](None)
val tasks = Var(
Seq(
Task(Var("TodoMVC Task A"), Var(true)),
Task(Var("TodoMVC Task B"), Var(false)),
Task(Var("TodoMVC Task C"), Var(false))
)
)
val filter = Var("All")
val filters = Map[String, Task => Boolean](
("All", t => true),
("Active", !_.done()),
("Completed", _.done())
)
val done = Rx{tasks().count(_.done())}
val notDone = Rx{tasks().length - done()}
val inputBox = input(
id:="new-todo",
placeholder:="What needs to be done?",
autofocus:=true
).render
def main() = {
println(
div(
tags2.style(inlineCss),
section(id:="todoapp", color:="black")(
header(id:="header")(
h1("todos"),
form(
inputBox,
onsubmit := { () =>
tasks() = Task(Var(inputBox.value), Var(false)) +: tasks()
inputBox.value = ""
false
}
)
),
section(id:="main")(
input(
id:="toggle-all",
`type`:="checkbox",
cursor:="pointer",
onclick := { () =>
val target = tasks().exists(_.done() == false)
Var.set(tasks().map(_.done -> target): _*)
}
),
label(`for`:="toggle-all", "Mark all as complete"),
Rx {
ul(id := "todo-list")(
for (task <- tasks() if filters(filter())(task)) yield {
val inputRef = input(`class` := "edit", value := task.txt()).render
li(
`class` := Rx{
if (task.done()) "completed"
else if (editing() == Some(task)) "editing"
else ""
},
div(`class` := "view")(
"ondblclick".attr := { () =>
editing() = Some(task)
},
input(
`class` := "toggle",
`type` := "checkbox",
cursor := "pointer",
onchange := { () =>
task.done() = !task.done()
},
if (task.done()) checked := true
),
label(task.txt()),
button(
`class` := "destroy",
cursor := "pointer",
onclick := { () =>tasks() = tasks().filter(_ != task) }
)
),
form(
onsubmit := { () =>
task.txt() = inputRef.value
editing() = None
false
},
inputRef
)
)
}
)
},
footer(id:="footer")(
span(id:="todo-count")(strong(RxStr(notDone)(intFrag)), " item left"),
ul(id:="filters")(
for ((name, pred) <- filters.toSeq) yield {
li(a(
`class`:=Rx{
if(name == filter()) "selected"
else ""
},
name,
href:="#",
onclick := {() => filter() = name}
))
}
),
button(
id:="clear-completed",
onclick := { () => tasks() = tasks().filter(!_.done()) },
"Clear completed (", done, ")"
)
)
),
footer(id:="info")(
p("Double-click to edit a todo"),
p(a(href:="https://github.com/lihaoyi/workbench-example-app/blob/todomvc/src/main/scala/example/ScalaJSExample.scala")("Source Code")),
p("Created by ", a(href:="http://github.com/lihaoyi")("Li Haoyi"))
)
)
).render
)
}
def inlineCss = """
html,
body {
margin: 0;
padding: 0;
}
button {
margin: 0;
padding: 0;
border: 0;
background: none;
font-size: 100%;
vertical-align: baseline;
font-family: inherit;
color: inherit;
-webkit-appearance: none;
-ms-appearance: none;
-o-appearance: none;
appearance: none;
}
body{
background: url('https://raw.github.com/lihaoyi/workbench-example-app/todomvc/src/main/resources/css/bg.png');
}
#sandbox {
font: 14px 'Helvetica Neue', Helvetica, Arial, sans-serif;
line-height: 1.4em;
background: url('https://raw.github.com/lihaoyi/workbench-example-app/todomvc/src/main/resources/css/bg.png');
color: #4d4d4d;
width: 550px;
margin: 0 auto;
-webkit-font-smoothing: antialiased;
-moz-font-smoothing: antialiased;
-ms-font-smoothing: antialiased;
-o-font-smoothing: antialiased;
font-smoothing: antialiased;
}
button,
input[type="checkbox"] {
outline: none;
}
#todoapp {
background: #fff;
background: rgba(255, 255, 255, 0.9);
margin: 130px 0 40px 0;
border: 1px solid #ccc;
position: relative;
border-top-left-radius: 2px;
border-top-right-radius: 2px;
box-shadow: 0 2px 6px 0 rgba(0, 0, 0, 0.2),
0 25px 50px 0 rgba(0, 0, 0, 0.15);
}
#todoapp:before {
content: '';
border-left: 1px solid #f5d6d6;
border-right: 1px solid #f5d6d6;
width: 2px;
position: absolute;
top: 0;
left: 40px;
height: 100%;
}
#todoapp input::-webkit-input-placeholder {
font-style: italic;
}
#todoapp input::-moz-placeholder {
font-style: italic;
color: #a9a9a9;
}
#todoapp h1 {
position: absolute;
top: -120px;
width: 100%;
font-size: 70px;
font-weight: bold;
text-align: center;
color: #b3b3b3;
color: rgba(255, 255, 255, 0.3);
text-shadow: -1px -1px rgba(0, 0, 0, 0.2);
-webkit-text-rendering: optimizeLegibility;
-moz-text-rendering: optimizeLegibility;
-ms-text-rendering: optimizeLegibility;
-o-text-rendering: optimizeLegibility;
text-rendering: optimizeLegibility;
}
#header {
padding-top: 15px;
border-radius: inherit;
}
#header:before {
content: '';
position: absolute;
top: 0;
right: 0;
left: 0;
height: 15px;
z-index: 2;
border-bottom: 1px solid #6c615c;
background: #8d7d77;
background: -webkit-gradient(linear, left top, left bottom, from(rgba(132, 110, 100, 0.8)),to(rgba(101, 84, 76, 0.8)));
background: -webkit-linear-gradient(top, rgba(132, 110, 100, 0.8), rgba(101, 84, 76, 0.8));
background: linear-gradient(top, rgba(132, 110, 100, 0.8), rgba(101, 84, 76, 0.8));
filter: progid:DXImageTransform.Microsoft.gradient(GradientType=0,StartColorStr='#9d8b83', EndColorStr='#847670');
border-top-left-radius: 1px;
border-top-right-radius: 1px;
}
#new-todo,
.edit {
position: relative;
margin: 0;
width: 100%;
font-size: 24px;
font-family: inherit;
line-height: 1.4em;
border: 0;
outline: none;
color: inherit;
padding: 6px;
border: 1px solid #999;
box-shadow: inset 0 -1px 5px 0 rgba(0, 0, 0, 0.2);
-moz-box-sizing: border-box;
-ms-box-sizing: border-box;
-o-box-sizing: border-box;
box-sizing: border-box;
-webkit-font-smoothing: antialiased;
-moz-font-smoothing: antialiased;
-ms-font-smoothing: antialiased;
-o-font-smoothing: antialiased;
font-smoothing: antialiased;
}
#new-todo {
padding: 16px 16px 16px 60px;
border: none;
background: rgba(0, 0, 0, 0.02);
z-index: 100;
box-shadow: none;
}
#main {
position: relative;
z-index: 2;
border-top: 1px dotted #adadad;
}
label[for='toggle-all'] {
display: none;
}
#toggle-all {
position: absolute;
top: -42px;
left: -4px;
width: 40px;
text-align: center;
/* Mobile Safari */
border: none;
}
#toggle-all:before {
content: '»';
font-size: 28px;
color: #d9d9d9;
padding: 0 25px 7px;
}
#toggle-all:checked:before {
color: #737373;
}
#todo-list {
margin: 0;
padding: 0;
list-style: none;
}
#todo-list li {
position: relative;
font-size: 24px;
border-bottom: 1px dotted #ccc;
}
#todo-list li:last-child {
border-bottom: none;
}
#todo-list li.editing {
border-bottom: none;
padding: 0;
}
#todo-list li.editing .edit {
display: block;
width: 506px;
padding: 13px 17px 12px 17px;
margin: 0 0 0 43px;
}
#todo-list li.editing .view {
display: none;
}
#todo-list li .toggle {
text-align: center;
width: 40px;
/* auto, since non-WebKit browsers doesn't support input styling */
height: auto;
position: absolute;
top: 0;
bottom: 0;
margin: auto 0;
/* Mobile Safari */
border: none;
-webkit-appearance: none;
-ms-appearance: none;
-o-appearance: none;
appearance: none;
}
#todo-list li .toggle:after {
content: '✔';
/* 40 + a couple of pixels visual adjustment */
line-height: 43px;
font-size: 20px;
color: #d9d9d9;
text-shadow: 0 -1px 0 #bfbfbf;
}
#todo-list li .toggle:checked:after {
color: #85ada7;
text-shadow: 0 1px 0 #669991;
bottom: 1px;
position: relative;
}
#todo-list li label {
white-space: pre;
word-break: break-word;
padding: 15px 60px 15px 15px;
margin-left: 45px;
display: block;
line-height: 1.2;
-webkit-transition: color 0.4s;
transition: color 0.4s;
}
#todo-list li.completed label {
color: #a9a9a9;
text-decoration: line-through;
}
#todo-list li .destroy {
display: none;
position: absolute;
top: 0;
right: 10px;
bottom: 0;
width: 40px;
height: 40px;
margin: auto 0;
font-size: 22px;
color: #a88a8a;
-webkit-transition: all 0.2s;
transition: all 0.2s;
}
#todo-list li .destroy:hover {
text-shadow: 0 0 1px #000,
0 0 10px rgba(199, 107, 107, 0.8);
-webkit-transform: scale(1.3);
-ms-transform: scale(1.3);
transform: scale(1.3);
}
#todo-list li .destroy:after {
content: '✖';
}
#todo-list li:hover .destroy {
display: block;
}
#todo-list li .edit {
display: none;
}
#todo-list li.editing:last-child {
margin-bottom: -1px;
}
#footer {
color: #777;
padding: 0 15px;
position: absolute;
right: 0;
bottom: -31px;
left: 0;
height: 20px;
text-align: center;
}
#footer:before {
content: '';
position: absolute;
right: 0;
bottom: 31px;
left: 0;
height: 50px;
z-index: -1;
box-shadow: 0 1px 1px rgba(0, 0, 0, 0.3),
0 6px 0 -3px rgba(255, 255, 255, 0.8),
0 7px 1px -3px rgba(0, 0, 0, 0.3),
0 43px 0 -6px rgba(255, 255, 255, 0.8),
0 44px 2px -6px rgba(0, 0, 0, 0.2);
}
#todo-count {
float: left;
text-align: left;
}
#filters {
margin: 0;
padding: 0;
list-style: none;
position: absolute;
right: 0;
left: 0;
}
#filters li {
display: inline;
}
#filters li a {
color: #83756f;
margin: 2px;
text-decoration: none;
}
#filters li a.selected {
font-weight: bold;
}
#clear-completed {
float: right;
position: relative;
line-height: 20px;
text-decoration: none;
background: rgba(0, 0, 0, 0.1);
font-size: 11px;
padding: 0 10px;
border-radius: 3px;
box-shadow: 0 -1px 0 0 rgba(0, 0, 0, 0.2);
}
#clear-completed:hover {
background: rgba(0, 0, 0, 0.15);
box-shadow: 0 -1px 0 0 rgba(0, 0, 0, 0.3);
}
#info {
margin: 65px auto 0;
color: #a6a6a6;
font-size: 12px;
text-shadow: 0 1px 0 rgba(255, 255, 255, 0.7);
text-align: center;
}
#info a {
color: inherit;
}
/*
Hack to remove background from Mobile Safari.
Can't use it globally since it destroys checkboxes in Firefox and Opera
*/
@media screen and (-webkit-min-device-pixel-ratio:0) {
#toggle-all,
#todo-list li .toggle {
background: none;
}
#todo-list li .toggle {
height: 40px;
}
#toggle-all {
top: -56px;
left: -15px;
width: 65px;
height: 41px;
-webkit-transform: rotate(90deg);
-ms-transform: rotate(90deg);
transform: rotate(90deg);
-webkit-appearance: none;
appearance: none;
}
}
.hidden {
display: none;
}
hr {
margin: 20px 0;
border: 0;
border-top: 1px dashed #C5C5C5;
border-bottom: 1px dashed #F7F7F7;
}
.learn a {
font-weight: normal;
text-decoration: none;
color: #b83f45;
}
.learn a:hover {
text-decoration: underline;
color: #787e7e;
}
.learn h3,
.learn h4,
.learn h5 {
margin: 10px 0;
font-weight: 500;
line-height: 1.2;
color: #000;
}
.learn h3 {
font-size: 24px;
}
.learn h4 {
font-size: 18px;
}
.learn h5 {
margin-bottom: 0;
font-size: 14px;
}
.learn ul {
padding: 0;
margin: 0 0 30px 25px;
}
.learn li {
line-height: 20px;
}
.learn p {
font-size: 15px;
font-weight: 300;
line-height: 1.3;
margin-top: 0;
margin-bottom: 0;
}
.quote {
border: none;
margin: 20px 0 60px 0;
}
.quote p {
font-style: italic;
}
.quote p:before {
content: '“';
font-size: 50px;
opacity: .15;
position: absolute;
top: -20px;
left: 3px;
}
.quote p:after {
content: '”';
font-size: 50px;
opacity: .15;
position: absolute;
bottom: -42px;
right: 3px;
}
.quote footer {
position: absolute;
bottom: -40px;
right: 0;
}
.quote footer img {
border-radius: 3px;
}
.quote footer a {
margin-left: 5px;
vertical-align: middle;
}
.speech-bubble {
position: relative;
padding: 10px;
background: rgba(0, 0, 0, .04);
border-radius: 5px;
}
.speech-bubble:after {
content: '';
position: absolute;
top: 100%;
right: 30px;
border: 13px solid transparent;
border-top-color: rgba(0, 0, 0, .04);
}
.learn-bar > .learn {
position: absolute;
width: 272px;
top: 8px;
left: -300px;
padding: 10px;
border-radius: 5px;
background-color: rgba(255, 255, 255, .6);
-webkit-transition-property: left;
transition-property: left;
-webkit-transition-duration: 500ms;
transition-duration: 500ms;
}
@media (min-width: 899px) {
.learn-bar {
width: auto;
margin: 0 0 0 300px;
}
.learn-bar > .learn {
left: 8px;
}
.learn-bar #todoapp {
width: 550px;
margin: 130px auto 40px auto;
}
}
"""
}
import scalatags.JsDom.all._
case class Pt(x: Double, y: Double)
object ScalaJSExample extends js.JSApp{
val directions = Seq(
Pt(0, 1),
Pt(1, 0),
Pt(0, -1),
Pt(-1, 0)
)
val (w, h) = (Page.canvas.width, Page.canvas.height)
var turmites = Seq(
Pt(w / 2, h / 2) -> 0
)
def main() = {
dom.setInterval(() => for(_ <- 0 until 10){
turmites = for((oldP, facing) <- turmites) yield {
val d = directions(facing)
val p = Pt((oldP.x + d.x + w) % w, (oldP.y + d.y + h) % h)
val data = Page.renderer.getImageData(p.x, p.y, 1, 1).data
val newFacing = if (data(0).toInt == 0){
val m = (p.y / h)
val r = 255 - (p.x / w * m * 255).toInt
val g = 255 - ((w - p.x) / w * m * 255).toInt
val b = 255 - ((h - p.y) / h * 255).toInt
Page.renderer.fillStyle = s"rgb($r, $g, $b)"
(facing + 3) % 4
}else{
Page.renderer.fillStyle = "black"
(facing + 1) % 4
}
Page.renderer.fillRect(p.x, p.y, 1, 1)
p -> newFacing
}
}, 20)
Page.canvas.onclick = { (e: dom.MouseEvent) =>
val rect = Page.canvas.getBoundingClientRect()
val newP = Pt(e.clientX.toInt - rect.left, e.clientY.toInt - rect.top)
turmites = turmites :+ (newP -> util.Random.nextInt(4))
}
}
println(
"An implementation of ", a(href:="http://en.wikipedia.org/wiki/Turmites")("Turmites"),
", in particular ", a(href:="http://en.wikipedia.org/wiki/Langton's_ant")("Langton's Ant"),
", on the HTML canvas. Click anywhere to create more turmites."
)
}
@steshaw
Copy link

steshaw commented Jul 15, 2015

Dear @lihaoyi, I found this gist via scala-js-fiddle.com. I was wondering which gist to use. This one or the one it was forked from?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment