Skip to content

Commit

Permalink
add vector arithmetic derivation with quotidian
Browse files Browse the repository at this point in the history
  • Loading branch information
kitlangton committed Mar 16, 2024
1 parent de10c56 commit 85b0e7f
Show file tree
Hide file tree
Showing 17 changed files with 333 additions and 84 deletions.
8 changes: 6 additions & 2 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -76,10 +76,14 @@ lazy val root = project
)

lazy val animus = crossProject(JSPlatform)
.in(file("."))
.in(file("modules/core"))
.settings(
name := "animus",
commonSettings,
libraryDependencies += "dev.zio" %%% "zio-test" % zioVersion % Test
libraryDependencies ++= Seq(
"dev.zio" %%% "zio-test" % zioVersion % Test,
"io.github.kitlangton" %%% "quotidian" % "0.0.14"
)
)
.jsSettings(
scalaJSLinkerConfig ~= { _.withSourceMap(false) },
Expand Down
7 changes: 3 additions & 4 deletions example/src/main/scala/example/AnimatedCount.scala
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
package example

import com.raquo.laminar.api.L._
import animus._
import com.raquo.laminar.api.L.*
import animus.*

case class AnimatedCount($count: Signal[Int]) extends Component {
case class AnimatedCount($count: Signal[Int]) extends Component:
val $digits: Signal[List[(String, Int)]] =
$count.map(_.toString.split("").reverse.zipWithIndex.reverse.toList)

Expand All @@ -30,4 +30,3 @@ case class AnimatedCount($count: Signal[Int]) extends Component {
)
}
)
}
142 changes: 115 additions & 27 deletions example/src/main/scala/example/Main.scala
Original file line number Diff line number Diff line change
@@ -1,9 +1,71 @@
package example

import animus.*
import com.raquo.airstream.timing.PeriodicStream
import com.raquo.laminar.api.L.*
import org.scalajs.dom.document

def periodic[A](min: Int, max: Int, value: => A): Signal[A] =
PeriodicStream[A](
value,
_ => Some(value -> scala.util.Random.between(min, max)),
false
).startWith(value)

final case class Origin(
x: Double,
y: Double
) derives VectorArithmetic

final case class Rect(
origin: Origin,
width: Double,
height: Double,
red: Double = scala.util.Random.nextDouble(),
green: Double = scala.util.Random.nextDouble(),
blue: Double = scala.util.Random.nextDouble()

// rotation: Double = 0
) derives VectorArithmetic:
def x: Double = origin.x
def y: Double = origin.y

final case class RectView(
styles: String = "bg-red-600"
) extends View:
def windowWidth = org.scalajs.dom.window.innerHeight

def randomRect = Rect(
Origin(0, 0),
10,
// scala.util.Random.nextDouble() * 80,
scala.util.Random.nextDouble() * windowWidth
// 0 // scala.util.Random.nextDouble() * 30 - 15
)

val rectSignal = periodic(500, 1000, randomRect).spring

def body =
div(
top <-- rectSignal.map(_.y).px,
left <-- rectSignal.map(_.x).px,
width <-- rectSignal.map(_.width).px,
height <-- rectSignal.map(_.height).px,
background <-- rectSignal.map { r =>
s"rgb(${r.red * 255},${r.red * 255},${r.red * 255})"
},
opacity(0.15)
// transform <-- rectSignal.map { r =>
// s"rotate(${r.rotation}deg)"
// }
)

trait View:
def body: HtmlElement

object View:
given Conversion[View, HtmlElement] = _.body

object Main:
def main(args: Array[String]): Unit =
documentEvents(_.onDomContentLoaded).foreach { _ =>
Expand Down Expand Up @@ -45,29 +107,40 @@ object Main:

def body =
div(
cls("bg-neutral-900 font-bold min-h-screen text-6xl text-neutral-100"),
cls("tracking-wider flex-col p-24"),
animusText,
cls("bg-neutral-900"),
div(
cls("text-lg mt-4 items-center max-w-lg"),
cls("z-10 relative font-bold overflow-hidden text-6xl text-neutral-100 h-screen flex flex-col"),
cls("tracking-wider flex-col p-4 sm:p-24"),
animusText,
div(
cls("items-center flex flex-wrap"),
"is a",
children <-- article.splitOneTransition(identity) { (_, string, _, t) =>
div(
string,
t.width
)
},
adjectiveOne.body,
div(s"and"),
AdjectiveView().body
),
div(s"spring animation library for Scala.js.")
cls("text-lg mt-4 items-center max-w-lg"),
div(
cls("items-center flex flex-wrap"),
"is a",
children <-- article.splitOneTransition(identity) { (_, string, _, t) =>
div(
string,
t.width
)
},
adjectiveOne.body,
div(s"and"),
AdjectiveView().body
),
div(s"spring animation library for Scala.js.")
)
),
div(
cls("w-full flex justify-center items-center"),
cls("fixed inset-0"),
transform := "translateZ(0)",
List.fill(200)(
RectView("bg-neutral-800")
)
)
)

val adjectives = Vector(
lazy val adjectives = Array(
"aesthetic",
"pulchritudinous",
"resplendent",
Expand All @@ -77,29 +150,44 @@ object Main:
"succulent",
"delicious",
"luscious",
"velvety"
"velvety",
"ebullient",
"effervescent",
"incandescent",
"luminescent",
"ethereal",
"iridescent",
"quintessential",
"vivacious",
"sumptuous",
"pellucid",
"euphonious",
"mellifluous",
"opalescent",
"coruscating",
"diaphanous",
"lustrous",
"rhapsodic",
"limpid",
"refulgent",
"dulcet"
)

def randomAdjective =
adjectives(scala.util.Random.nextInt(adjectives.size))

def adjectiveStream: Signal[String] =
EventStream
.periodic(
700
)
.mapTo(randomAdjective)
.toSignal(randomAdjective)
periodic(1000, 3000, randomAdjective)
.composeChanges(_.delay(1))

final case class AdjectiveView():
val adjective = adjectiveStream

def body =
div(
cls(s"flex flex-col my-[8px] rounded-sm bg-neutral-700 px-1.5 mx-2 overflow-hidden"),
cls(s"flex flex-col my-[8px] mx-2 overflow-hidden"),
div(
cls("flex whitespace-nowrap"),
cls("flex whitespace-nowrap text-neutral-900 bg-neutral-300 px-2"),
children <-- adjective.splitOneTransition(identity) { (_, string, _, t) =>
div(
string,
Expand Down
33 changes: 0 additions & 33 deletions js/src/main/scala/animus/VectorArithmetic.scala

This file was deleted.

File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,10 @@ object AnimationManager:

var lastTick: Double = 0

val max = 1000.0 / 30

val tick: scalajs.js.Function1[Double, Unit] = n =>
val delta: Double = (n - lastTick) min 2600.0
val delta: Double = (n - lastTick) min max
val deltaSeconds: Double = delta / 1_000.0
lastTick = n

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,38 +9,33 @@ import animus.Animator

import scala.collection.mutable



class SpringSignal[A](
override protected val parent: Signal[A],
configureSpring: Animator[A] => Animator[A]
override protected val parent: Signal[A],
configureSpring: Animator[A] => Animator[A]
)(implicit
vectorArithmetic: VectorArithmetic[A]
vectorArithmetic: VectorArithmetic[A]
) extends Signal[A]
with WritableSignal[A]
with SingleParentSignal[A, A] {
with SingleParentSignal[A, A]:

private var spring: Animator[A] = _
var animationId: Int = -1
var animationId: Int = -1

override def onStart(): Unit = {
override def onStart(): Unit =
super.onStart()
animationId = AnimationManager.addAnimation(spring)
}

override def onStop(): Unit = {
override def onStop(): Unit =
super.onStop()
AnimationManager.removeAnimation(animationId)
}

override protected def currentValueFromParent(): Try[A] = {
override protected def currentValueFromParent(): Try[A] =
val value = parent.tryNow()
value.foreach { a =>
spring = configureSpring(Animator.make(a))
spring.callback = fireQuick
}
value
}

override protected[airstream] val topoRank: Int = Protected.topoRank(parent) + 1

Expand All @@ -50,10 +45,7 @@ class SpringSignal[A](
override protected[airstream] def onTry(nextValue: Try[A], transaction: Transaction): Unit =
nextValue.foreach { a =>
spring.setTarget(a)
if (spring.isDone) {
if spring.isDone then
spring.isDone = false
AnimationManager.addAnimation(spring)
}
}

}
Loading

0 comments on commit 85b0e7f

Please sign in to comment.