diff --git a/lang-trials/scala/src/main/scala/LatticeCircleApp.scala b/lang-trials/scala/src/main/scala/LatticeCircleApp.scala index 602b2fc..12ee36c 100644 --- a/lang-trials/scala/src/main/scala/LatticeCircleApp.scala +++ b/lang-trials/scala/src/main/scala/LatticeCircleApp.scala @@ -8,7 +8,6 @@ // import com.raquo.laminar.api.L.{*, given} -/*import breeze.linalg._*/ import narr.* import org.scalajs.dom import org.scalajs.dom.document @@ -20,57 +19,7 @@ class Circle(var centerX: Double, var centerY: Double, var radius: Double) object LatticeCircleApp: val canvas = canvasTag(widthAttr := 600, heightAttr := 600) val ctx = canvas.ref.getContext("2d").asInstanceOf[dom.CanvasRenderingContext2D] - val dataBusList = List.tabulate(6)(_ => new EventBus[Double]) - val dataStream = dataBusList(0).events - .combineWith(dataBusList(1).events) - .combineWith(dataBusList(2).events) - .combineWith(dataBusList(3).events) - .combineWith(dataBusList(4).events) - .combineWith(dataBusList(5).events) - .map(dataTuple => NArray(dataTuple(0), dataTuple(1), dataTuple(2), dataTuple(3), dataTuple(4), dataTuple(5))) - .filter(data => data.forall(!_.isNaN())) - /* - val dataList = List(-1.0, 0.0, 0.0, -1.0, 1.0, 0.0).map(Var(_)) - val dataStream = dataList(0).signal - .combineWith(dataList(1).signal) - .combineWith(dataList(2).signal) - .combineWith(dataList(3).signal) - .combineWith(dataList(4).signal) - .combineWith(dataList(5).signal) - .map(dataTuple => NArray(dataTuple(0), dataTuple(1), dataTuple(2), dataTuple(3), dataTuple(4), dataTuple(5))) - */ - val pointStream = dataStream.map(data => Matrix[3, 2](data)) - /* Breeze version */ - /* - val pointStream = dataStream.map(data => new DenseMatrix(2, 3, Array(data(0), data(1), data(2), data(3), data(4), data(5)))) - */ - - /* Breeze version */ - /* - def circThru(points: DenseMatrix[Double]): Circle = - // build the matrix that maps the circle's coefficient vector to the - // negative of the linear part of the circle's equation, evaluated at the - // given points - val negLinPart = DenseMatrix.horzcat( - 2.0*points.t, - DenseMatrix.ones[Double](3, 1) - ) - - // find the quadrdatic part of the circle's equation, evaluated at the given - // points - val quadPart = points(::, *).map(v => v dot v) - - // find the circle's coefficient vector, and from there its center and - // radius - val coeffs = negLinPart \ quadPart - val centerX = coeffs(0, 0) - val centerY = coeffs(1, 0) - Circle( - centerX, - centerY, - math.sqrt(coeffs(2, 0) + centerX*centerX + centerY*centerY) - ) - */ + val data = List("-1", "0", "0", "-1", "1", "0").map(Var(_)) def circThru(points: Matrix[3, 2]): Option[Circle] = // build the matrix that maps the circle's coefficient vector to the @@ -78,8 +27,6 @@ object LatticeCircleApp: // given points val negLinPart = Matrix.ones[3, 3] negLinPart.setMatrix(0, 0, points * 2.0) - println("built neg lin part") - println(negLinPart) // find the quadrdatic part of the circle's equation, evaluated at the given // points @@ -88,8 +35,6 @@ object LatticeCircleApp: k => points(k, 0)*points(k, 0) + points(k, 1)*points(k, 1) ) ) - println("build quad part") - println(quadPart) // find the circle's coefficient vector, and from there its center and // radius @@ -105,7 +50,7 @@ object LatticeCircleApp: catch _ => return None - def draw(points: Matrix[3, 2]): Unit = + def draw(): Unit = // center and normalize the coordinate system val width = canvas.ref.width val height = canvas.ref.height @@ -144,110 +89,72 @@ object LatticeCircleApp: ctx.stroke() // find and draw the circle through the given points - circThru(points) match - case Some(circ) => - ctx.beginPath() - ctx.strokeStyle = highlightStyle - ctx.arc( - res * circ.centerX, - res * circ.centerY, - res * circ.radius, - 0.0, 2.0*math.Pi - ) - ctx.stroke() - case None => + val dataNow = NArray.tabulate(6)(n => + try + data(n).signal.now().toDouble + catch + _ => Double.NaN + ) + if dataNow.forall(t => t == t.floor) then + // all of the coordinates are integer and non-NaN + val points = Matrix[3, 2](dataNow) + circThru(points) match + case Some(circ) => + ctx.beginPath() + ctx.strokeStyle = highlightStyle + ctx.arc( + res * circ.centerX, + res * circ.centerY, + res * circ.radius, + 0.0, 2.0*math.Pi + ) + ctx.stroke() + case None => // draw the data points for n <- 0 to 2 do - ctx.beginPath() - ctx.fillStyle = pointFillStyles(n) - ctx.strokeStyle = pointStrokeStyles(n) - ctx.arc( - res * points(n, 0), - res * points(n, 1), - 3.0, - 0.0, 2.0*math.Pi - ) - ctx.fill() - ctx.stroke() + val indX = 2*n + val indY = indX + 1 + if + dataNow(indX) == dataNow(indX).floor && + dataNow(indY) == dataNow(indY).floor + then + ctx.beginPath() + ctx.fillStyle = pointFillStyles(n) + ctx.strokeStyle = pointStrokeStyles(n) + ctx.arc( + res * dataNow(indX), + res * dataNow(indY), + 3.0, + 0.0, 2.0*math.Pi + ) + ctx.fill() + ctx.stroke() + + def coordInput(n: Int): Input = + input( + typ := "number", + cls := s"point-${(1.0 + 0.5*n).floor.toInt}", + controlled( + value <-- data(n).signal, + onInput.mapToValue --> data(n).writer + ), + data(n).signal --> { _ => draw() } + ) def main(args: Array[String]): Unit = lazy val app = div( canvas, div( - pointStream --> draw, idAttr := "data-panel", div("x"), div("y"), - input(typ := "number", cls := "point-1", inContext(thisNode => onInput.mapTo(thisNode.ref.valueAsNumber) --> dataBusList(0))), - input(typ := "number", cls := "point-1", inContext(thisNode => onInput.mapTo(thisNode.ref.valueAsNumber) --> dataBusList(1))), - input(typ := "number", cls := "point-2", inContext(thisNode => onInput.mapTo(thisNode.ref.valueAsNumber) --> dataBusList(2))), - input(typ := "number", cls := "point-2", inContext(thisNode => onInput.mapTo(thisNode.ref.valueAsNumber) --> dataBusList(3))), - input(typ := "number", cls := "point-3", inContext(thisNode => onInput.mapTo(thisNode.ref.valueAsNumber) --> dataBusList(4))), - input(typ := "number", cls := "point-3", inContext(thisNode => onInput.mapTo(thisNode.ref.valueAsNumber) --> dataBusList(5))) - /* attempt to use controlled inputs */ - /* - input( - typ := "number", - cls := "point-1", - controlled( - value <-- dataList(0).signal, - inContext( - thisNode => onInput.mapTo(thisNode.ref.valueAsNumber) --> dataList(0).writer - ) - ) - ), - input( - typ := "number", - cls := "point-1", - controlled( - value <-- dataList(1).signal, - inContext( - thisNode => onInput.mapTo(thisNode.ref.valueAsNumber) --> dataList(1).writer - ) - ) - ), - input( - typ := "number", - cls := "point-2", - controlled( - value <-- dataList(2).signal, - inContext( - thisNode => onInput.mapTo(thisNode.ref.valueAsNumber) --> dataList(2).writer - ) - ) - ), - input( - typ := "number", - cls := "point-2", - controlled( - value <-- dataList(3).signal, - inContext( - thisNode => onInput.mapTo(thisNode.ref.valueAsNumber) --> dataList(3).writer - ) - ) - ), - input( - typ := "number", - cls := "point-3", - controlled( - value <-- dataList(4).signal, - inContext( - thisNode => onInput.mapTo(thisNode.ref.valueAsNumber) --> dataList(4).writer - ) - ) - ), - input( - typ := "number", - cls := "point-3", - controlled( - value <-- dataList(5).signal, - inContext( - thisNode => onInput.mapTo(thisNode.ref.valueAsNumber) --> dataList(5).writer - ) - ) - ) - */ + coordInput(0), + coordInput(1), + coordInput(2), + coordInput(3), + coordInput(4), + coordInput(5) ) ) renderOnDomContentLoaded(document.body, app)