Skip to content

Commit ebdab89

Browse files
authored
Fix publish wasm (#52)
* what a mess * . * . * . * ,. * . * .
1 parent b5d0415 commit ebdab89

18 files changed

Lines changed: 805 additions & 617 deletions

.scalex/index.bin

3.35 KB
Binary file not shown.
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package boid
2+
3+
object BoidConfig:
4+
val numBoids = 150
5+
val maxSpeed = 5
6+
val maxForce = 0.25
7+
val separationDistance = 30.0
8+
val alignmentDistance = 50.0
9+
val cohesionDistance = 50.0
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
package boid
2+
3+
import vecxt.all.*
4+
import vecxt.BoundsCheck.DoBoundsCheck.yes
5+
6+
object BoidForces:
7+
import BoidConfig.*
8+
9+
def limitForce(fx: Double, fy: Double, maxForce: Double): (Double, Double) =
10+
val magnitude = math.hypot(fx, fy)
11+
if magnitude > maxForce && magnitude > 0 then
12+
(fx * maxForce / magnitude, fy * maxForce / magnitude)
13+
else (fx, fy)
14+
15+
def wrapBoundaries(
16+
positions: Matrix[Double],
17+
boidIndex: Int,
18+
width: Double,
19+
height: Double
20+
): Unit =
21+
if positions((boidIndex, 0)) < 0 then positions((boidIndex, 0)) = width
22+
if positions((boidIndex, 0)) > width then positions((boidIndex, 0)) = 0
23+
if positions((boidIndex, 1)) < 0 then positions((boidIndex, 1)) = height
24+
if positions((boidIndex, 1)) > height then positions((boidIndex, 1)) = 0
25+
26+
inline def calculateForces(
27+
positions: Matrix[Double],
28+
velocities: Matrix[Double],
29+
boidIndex: Int
30+
): (Double, Double, Double, Double, Double, Double) =
31+
var sepX = 0.0
32+
var sepY = 0.0
33+
var sepCount = 0
34+
35+
var aliX = 0.0
36+
var aliY = 0.0
37+
var aliCount = 0
38+
39+
var cohX = 0.0
40+
var cohY = 0.0
41+
var cohCount = 0
42+
43+
val boidX = positions((boidIndex, 0))
44+
val boidY = positions((boidIndex, 1))
45+
val boidVelX = velocities((boidIndex, 0))
46+
val boidVelY = velocities((boidIndex, 1))
47+
48+
var j = 0
49+
while j < numBoids do
50+
if j != boidIndex then
51+
val dx = boidX - positions((j, 0))
52+
val dy = boidY - positions((j, 1))
53+
val distanceSquared = dx * dx + dy * dy
54+
55+
if distanceSquared > 0 && distanceSquared < separationDistance * separationDistance then
56+
val distance = math.sqrt(distanceSquared)
57+
sepX += dx / distance
58+
sepY += dy / distance
59+
sepCount += 1
60+
61+
if distanceSquared > 0 && distanceSquared < alignmentDistance * alignmentDistance then
62+
aliX += velocities((j, 0))
63+
aliY += velocities((j, 1))
64+
aliCount += 1
65+
66+
if distanceSquared > 0 && distanceSquared < cohesionDistance * cohesionDistance then
67+
cohX += positions((j, 0))
68+
cohY += positions((j, 1))
69+
cohCount += 1
70+
j += 1
71+
72+
// Process separation
73+
if sepCount > 0 then
74+
sepX /= sepCount
75+
sepY /= sepCount
76+
val magnitude = math.hypot(sepX, sepY)
77+
if magnitude > 0 then
78+
sepX = (sepX / magnitude) * maxSpeed - boidVelX
79+
sepY = (sepY / magnitude) * maxSpeed - boidVelY
80+
val (lx, ly) = limitForce(sepX, sepY, maxForce)
81+
sepX = lx
82+
sepY = ly
83+
84+
// Process alignment
85+
if aliCount > 0 then
86+
aliX /= aliCount
87+
aliY /= aliCount
88+
val magnitude = math.hypot(aliX, aliY)
89+
if magnitude > 0 then
90+
aliX = (aliX / magnitude) * maxSpeed - boidVelX
91+
aliY = (aliY / magnitude) * maxSpeed - boidVelY
92+
val (lx, ly) = limitForce(aliX, aliY, maxForce)
93+
aliX = lx
94+
aliY = ly
95+
96+
// Process cohesion
97+
if cohCount > 0 then
98+
cohX = cohX / cohCount - boidX
99+
cohY = cohY / cohCount - boidY
100+
val magnitude = math.hypot(cohX, cohY)
101+
if magnitude > 0 then
102+
cohX = (cohX / magnitude) * maxSpeed - boidVelX
103+
cohY = (cohY / magnitude) * maxSpeed - boidVelY
104+
val (lx, ly) = limitForce(cohX, cohY, maxForce)
105+
cohX = lx
106+
cohY = ly
107+
108+
(sepX, sepY, aliX, aliY, cohX, cohY)
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
package boid
2+
3+
import org.scalajs.dom
4+
import org.scalajs.dom.{document, html}
5+
import vecxt.all.*
6+
import vecxt.BoundsCheck.DoBoundsCheck.yes
7+
8+
object BoidRenderer:
9+
import BoidConfig.*
10+
11+
def createCanvas(): (html.Canvas, dom.CanvasRenderingContext2D) =
12+
val width = dom.window.innerWidth
13+
val height = dom.window.innerHeight
14+
val canvas = document.createElement("canvas").asInstanceOf[html.Canvas]
15+
canvas.width = width.toInt
16+
canvas.height = height.toInt
17+
document.body.appendChild(canvas)
18+
19+
val ctx = canvas.getContext("2d").asInstanceOf[dom.CanvasRenderingContext2D]
20+
ctx.fillStyle = "red"
21+
ctx.strokeStyle = "black"
22+
ctx.lineWidth = 1
23+
ctx.font = "16px Arial"
24+
ctx.textAlign = "center"
25+
ctx.textBaseline = "middle"
26+
27+
(canvas, ctx)
28+
29+
def drawBoids(
30+
ctx: dom.CanvasRenderingContext2D,
31+
positions: Matrix[Double],
32+
width: Double,
33+
height: Double
34+
): Unit =
35+
ctx.clearRect(0, 0, width, height)
36+
for i <- 0 until numBoids do
37+
ctx.beginPath()
38+
ctx.arc(positions(i, 0), positions(i, 1), 5, 0, 2 * math.Pi)
39+
ctx.fill()
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
package boid
2+
3+
import org.scalajs.dom
4+
import scala.util.Random
5+
import vecxt.all.*
6+
import vecxt.BoundsCheck.DoBoundsCheck.yes
7+
8+
import BoidConfig.*
9+
import BoidForces.*
10+
import BoidRenderer.*
11+
12+
@main def main =
13+
println("aha go where no one has gone before..")
14+
15+
val positions = Matrix.zeros[Double](numBoids, 2)
16+
val velocities = Matrix.zeros[Double](numBoids, 2)
17+
val accelerations = Matrix.zeros[Double](numBoids, 2)
18+
19+
val (canvas, ctx) = createCanvas()
20+
val width = canvas.width.toDouble
21+
val height = canvas.height.toDouble
22+
23+
println("Canvas initialized with dimensions: " + width + "x" + height)
24+
25+
// Initialize boids with random positions and velocities
26+
for i <- 0 until numBoids do
27+
positions((i, 0)) = Random.nextDouble() * width
28+
positions((i, 1)) = Random.nextDouble() * height
29+
velocities((i, 0)) = (Random.nextDouble() - 0.5) * maxSpeed
30+
velocities((i, 1)) = (Random.nextDouble() - 0.5) * maxSpeed
31+
32+
inline def animate(): Unit =
33+
// Calculate all forces in one pass
34+
var i = 0
35+
while i < numBoids do
36+
val (sepX, sepY, aliX, aliY, cohX, cohY) =
37+
calculateForces(positions, velocities, i)
38+
39+
accelerations((i, 0)) = sepX * 1.5 + aliX * 1.0 + cohX * 1.0
40+
accelerations((i, 1)) = sepY * 1.5 + aliY * 1.0 + cohY * 1.0
41+
i += 1
42+
43+
// Update boids
44+
for i <- 0 until numBoids do
45+
velocities((i, 0)) = velocities((i, 0)) + accelerations((i, 0))
46+
velocities((i, 1)) = velocities((i, 1)) + accelerations((i, 1))
47+
48+
// Limit speed
49+
val speed = math.hypot(velocities(i, 0), velocities(i, 1))
50+
if speed > maxSpeed then
51+
velocities((i, 0)) = velocities((i, 0)) * maxSpeed / speed
52+
velocities((i, 1)) = velocities((i, 1)) * maxSpeed / speed
53+
54+
positions((i, 0)) = positions((i, 0)) + velocities((i, 0))
55+
positions((i, 1)) = positions((i, 1)) + velocities((i, 1))
56+
57+
wrapBoundaries(positions, i, width, height)
58+
59+
drawBoids(ctx, positions, width, height)
60+
61+
dom.window.setInterval(() => animate(), 25)

plugin/integration/src/linkInMem.test.scala renamed to plugin/integration/src/MemJs.test.scala

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package io.github.quafadas.millSite
1+
package io.github.quafadas.sjsls
22

33
import mill.api.Discover
44
import mill.api.Task.Simple
@@ -13,7 +13,7 @@ import utest.*
1313
object MemJsTests extends TestSuite:
1414
def tests: Tests = Tests {
1515
test("Hashed JS files have correct cross-module references") {
16-
object build extends TestRootModule with io.github.quafadas.InMemoryHashScalaJSModule:
16+
object build extends TestRootModule with InMemoryFastLinkHashScalaJSModule:
1717
override def scalaVersion: Simple[String] = "3.8.2"
1818
override def moduleSplitStyle: Simple[ModuleSplitStyle] =
1919
ModuleSplitStyle.SmallModulesFor("webapp")
@@ -73,7 +73,7 @@ object MemJsTests extends TestSuite:
7373
}
7474

7575
test("InMemoryHashScalaJSModule fullLinkJS with scalaJSMinify=true produces smaller JS than fastLinkJS") {
76-
object build extends TestRootModule with io.github.quafadas.InMemoryHashScalaJSModule:
76+
object build extends TestRootModule with InMemoryFastLinkHashScalaJSModule:
7777
override def scalaVersion: Simple[String] = "3.8.2"
7878
override def moduleSplitStyle: Simple[ModuleSplitStyle] =
7979
ModuleSplitStyle.SmallModulesFor("webapp")
@@ -120,7 +120,7 @@ object MemJsTests extends TestSuite:
120120
}
121121

122122
test("InMemoryHashScalaJSModule fullLinkJS with scalaJSMinify=false writes hashed files without terser") {
123-
object build extends TestRootModule with io.github.quafadas.InMemoryHashScalaJSModule:
123+
object build extends TestRootModule with InMemoryFastLinkHashScalaJSModule:
124124
override def scalaVersion: Simple[String] = "3.8.2"
125125
override def moduleSplitStyle: Simple[ModuleSplitStyle] =
126126
ModuleSplitStyle.SmallModulesFor("webapp")
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
package io.github.quafadas.sjsls
2+
3+
import mill.api.Discover
4+
import mill.api.Task.Simple
5+
import mill.testkit.TestRootModule
6+
import mill.testkit.UnitTester
7+
import mill.util.TokenReaders.*
8+
import mill.javalib.DepSyntax
9+
import mill.scalajslib.ContentHashScalaJSModule
10+
import mill.scalajslib.api.ModuleSplitStyle
11+
import utest.*
12+
import io.github.quafadas.sjsls.FileBasedContentHashScalaJSModule
13+
14+
object BoidJsTests extends TestSuite:
15+
def tests: Tests = Tests {
16+
test("simple boid example emits content-hashed files with correct cross-module references") {
17+
object build extends TestRootModule with FileBasedContentHashScalaJSModule:
18+
override def scalaVersion: Simple[String] = "3.8.2"
19+
20+
override def scalaJSExperimentalUseWebAssembly = true
21+
22+
override def moduleSplitStyle: Simple[ModuleSplitStyle] = ModuleSplitStyle.FewestModules
23+
24+
override def mvnDeps = Seq(
25+
mvn"org.scala-js::scalajs-dom::2.8.1",
26+
mvn"io.github.quafadas::vecxt::0.0.38"
27+
)
28+
29+
lazy val millDiscover = Discover[this.type]
30+
end build
31+
32+
val resourceFolder = os.Path(sys.env("MILL_TEST_RESOURCE_DIR"))
33+
34+
UnitTester(build, resourceFolder / "boid space").scoped {
35+
eval =>
36+
val Right(resultC) = eval(build.compile).runtimeChecked
37+
38+
val Right(result) = eval(build.fastLinkJS).runtimeChecked
39+
40+
val Right(resultFull) = eval(build.fullLinkJS).runtimeChecked
41+
// val report = result.value
42+
// val outputDir = report.dest.path
43+
// val files = os.list(outputDir).map(_.last).toSet
44+
// val jsFiles = files.filter(f => f.endsWith(".js") && !f.endsWith(".js.map"))
45+
46+
// // Must produce at least one file — catches the regression where the else-branch
47+
// // returned super's report without writing anything to Task.dest.
48+
// if jsFiles.isEmpty then
49+
// throw new java.lang.AssertionError(
50+
// s"fullLinkJS produced no .js files in Task.dest. Files present: ${files.mkString(", ")}"
51+
// )
52+
// end if
53+
54+
// // No original (unhashed) JS filename should exist.
55+
// assert(!files.contains("main.js"))
56+
57+
// // No hashed JS filename should contain a hyphen.
58+
// jsFiles.foreach(filename => assert(!filename.contains("-")))
59+
60+
// // Every cross-module import must reference a file that actually exists in the output.
61+
// jsFiles.foreach {
62+
// filename =>
63+
// val content = os.read(outputDir / filename)
64+
// val imports = ContentHashScalaJSModule.parseJsImports(content)
65+
// imports.foreach {
66+
// importedName =>
67+
// if !jsFiles.contains(importedName) then
68+
// throw new java.lang.AssertionError(
69+
// s"In $filename: import '$importedName' not found in output. " +
70+
// s"Output files: ${jsFiles.mkString(", ")}"
71+
// )
72+
// }
73+
// }
74+
75+
// // The public modules reported back must have hashed filenames present in output.
76+
// assert(report.publicModules.nonEmpty)
77+
// report
78+
// .publicModules
79+
// .foreach {
80+
// m =>
81+
// if !jsFiles.contains(m.jsFileName) then
82+
// throw new java.lang.AssertionError(
83+
// s"Public module '${m.moduleID}' jsFileName '${m.jsFileName}' not found in output"
84+
// )
85+
// }
86+
}
87+
}
88+
}
89+
end BoidJsTests

plugin/integration/src/index.test.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package io.github.quafadas.millSite
1+
package io.github.quafadas.sjsls
22

33
import mill.api.Discover
44
import mill.api.Task.Simple
@@ -13,7 +13,7 @@ import utest.*
1313
object IndexHtmlTests extends TestSuite:
1414
def tests: Tests = Tests {
1515
test("Hashed JS files have correct cross-module references") {
16-
object build extends TestRootModule with io.github.quafadas.ScalaJsRefreshModule:
16+
object build extends TestRootModule with ScalaJsRefreshModule:
1717
override def scalaVersion: Simple[String] = "3.8.2"
1818

1919
override def moduleSplitStyle: Simple[ModuleSplitStyle] =

0 commit comments

Comments
 (0)