92 lines
2.7 KiB
Scala
92 lines
2.7 KiB
Scala
|
import com.raquo.laminar.api.L.{*, given}
|
||
|
import narr.*
|
||
|
import org.scalajs.dom
|
||
|
import org.scalajs.dom.document
|
||
|
import scala.math.{cos, sin}
|
||
|
import slash.matrix.Matrix
|
||
|
import slash.matrix.decomposition.Eigen
|
||
|
|
||
|
object CircularLawApp:
|
||
|
val canvas = canvasTag(widthAttr := 600, heightAttr := 600)
|
||
|
val ctx = canvas.ref.getContext("2d").asInstanceOf[dom.CanvasRenderingContext2D]
|
||
|
|
||
|
val (eigvalSeries, runTimeReport) = randomEigvalSeries[60]()
|
||
|
val timeVar = Var("0")
|
||
|
|
||
|
def draw(timeStr: String): Unit =
|
||
|
// center and normalize the coordinate system
|
||
|
val width = canvas.ref.width
|
||
|
val height = canvas.ref.height
|
||
|
ctx.setTransform(1d, 0d, 0d, -1d, 0.5*width, 0.5*height)
|
||
|
|
||
|
// clear the previous frame
|
||
|
ctx.clearRect(-0.5*width, -0.5*width, width, height)
|
||
|
|
||
|
// find the resolution
|
||
|
val rDisp: Double = 1.5
|
||
|
val res = width / (2*rDisp)
|
||
|
|
||
|
// draw the eigenvalues
|
||
|
val eigvals = eigvalSeries(timeStr.toInt)
|
||
|
for n <- 0 to eigvals(0).length-1 do
|
||
|
ctx.beginPath()
|
||
|
ctx.arc(
|
||
|
res * eigvals(0)(n),
|
||
|
res * eigvals(1)(n),
|
||
|
3d,
|
||
|
0d, 2*math.Pi
|
||
|
)
|
||
|
ctx.fill()
|
||
|
|
||
|
def eigvalsRotated[N <: Int](A: Matrix[N, N], time: Double)(using ValueOf[N]): (NArray[Double], NArray[Double]) =
|
||
|
// create transformation
|
||
|
val maxFreq = 4
|
||
|
val T = Matrix.identity[N, N]
|
||
|
val dim: Int = valueOf[N]
|
||
|
for n <- 0 to dim by 2 do
|
||
|
val a = cos(math.Pi * time * (n % maxFreq))
|
||
|
val b = sin(math.Pi * time * (n % maxFreq))
|
||
|
T(n, n) = a
|
||
|
T(n+1, n) = b
|
||
|
T(n, n+1) = -b
|
||
|
T(n+1, n+1) = a
|
||
|
|
||
|
// find eigenvalues
|
||
|
val eigen = Eigen(T*A)
|
||
|
(
|
||
|
eigen.realEigenvalues.asInstanceOf[NArray[Double]],
|
||
|
eigen.imaginaryEigenvalues.asInstanceOf[NArray[Double]]
|
||
|
)
|
||
|
|
||
|
def randomEigvalSeries[N <: Int]()(using ValueOf[N]): (List[(NArray[Double], NArray[Double])], String) =
|
||
|
val timeRes = 100
|
||
|
val dim: Int = valueOf[N]
|
||
|
val startTime = System.currentTimeMillis()
|
||
|
val A = new Matrix[N, N](
|
||
|
NArray.tabulate(dim*dim)(k => (math.E*k*k) % 2 - 1)
|
||
|
).times(math.sqrt(3d / dim))
|
||
|
val series = List.tabulate(timeRes)(t => eigvalsRotated(A, t.toDouble / timeRes))
|
||
|
val endTime = System.currentTimeMillis()
|
||
|
val runTime = endTime - startTime
|
||
|
(series, runTime.toString() + " ms")
|
||
|
|
||
|
def main(args: Array[String]): Unit =
|
||
|
ctx.fillStyle = "white"
|
||
|
|
||
|
lazy val app = div(
|
||
|
idAttr := "app",
|
||
|
div(runTimeReport),
|
||
|
canvas,
|
||
|
input(
|
||
|
typ := "range",
|
||
|
minAttr := "0",
|
||
|
maxAttr := (eigvalSeries.length-1).toString,
|
||
|
controlled(
|
||
|
value <-- timeVar.signal,
|
||
|
onInput.mapToValue --> timeVar.writer
|
||
|
),
|
||
|
timeVar.signal --> draw
|
||
|
)
|
||
|
)
|
||
|
renderOnDomContentLoaded(document.body, app)
|