diff --git a/indigo/indigo-extras/src/test/scala/indigoextras/jobs/JobMarketTests.scala b/indigo/indigo-extras/src/test/scala/indigoextras/jobs/JobMarketTests.scala index bd2821875..cf715979a 100644 --- a/indigo/indigo-extras/src/test/scala/indigoextras/jobs/JobMarketTests.scala +++ b/indigo/indigo-extras/src/test/scala/indigoextras/jobs/JobMarketTests.scala @@ -79,7 +79,7 @@ class JobMarketTests extends munit.FunSuite { val job: Job = SampleJobs.WanderTo(10) val market: JobMarket[Unit] = JobMarket.subSystem(SubSystemId("market")) - assertEquals(market.present(context, List(job)).unsafeGet.layers.flatMap(_.nodes).isEmpty, true) + assertEquals(market.present(context, List(job)).unsafeGet.layers.flatMap(_.toBatch).flatMap(_.nodes).isEmpty, true) assertEquals(market.present(context, List(job)).unsafeGlobalEvents.isEmpty, true) assertEquals(market.present(context, List(job)).unsafeGet.audio, None) } diff --git a/indigo/indigo-extras/src/test/scala/indigoextras/subsystems/AutomataTests.scala b/indigo/indigo-extras/src/test/scala/indigoextras/subsystems/AutomataTests.scala index 6408322b2..165216395 100644 --- a/indigo/indigo-extras/src/test/scala/indigoextras/subsystems/AutomataTests.scala +++ b/indigo/indigo-extras/src/test/scala/indigoextras/subsystems/AutomataTests.scala @@ -95,8 +95,10 @@ class AutomataTests extends munit.FunSuite { .present(ctx, nextState) .unsafeGet .layers - .find(l => l.key.contains(layerKey)) + .find(l => l.hasTag(layerKey)) .get + .toBatch + .head .nodes .collect { case g: Graphic[_] => g } .head diff --git a/indigo/indigo/src/main/scala/indigo/shared/platform/SceneProcessor.scala b/indigo/indigo/src/main/scala/indigo/shared/platform/SceneProcessor.scala index 166acbdea..353b50c5b 100644 --- a/indigo/indigo/src/main/scala/indigo/shared/platform/SceneProcessor.scala +++ b/indigo/indigo/src/main/scala/indigo/shared/platform/SceneProcessor.scala @@ -112,7 +112,7 @@ final class SceneProcessor( val displayLayers: scalajs.js.Array[(DisplayLayer, scalajs.js.Array[(String, DisplayObject)])] = scene.layers - .flatMap(_.layer.toBatch) + .flatMap(_.toBatch) .toJSArray .filter(l => l.visible.getOrElse(true)) .zipWithIndex diff --git a/indigo/indigo/src/main/scala/indigo/shared/scenegraph/Layer.scala b/indigo/indigo/src/main/scala/indigo/shared/scenegraph/Layer.scala index 465a85fbf..6c4f8f087 100644 --- a/indigo/indigo/src/main/scala/indigo/shared/scenegraph/Layer.scala +++ b/indigo/indigo/src/main/scala/indigo/shared/scenegraph/Layer.scala @@ -17,9 +17,6 @@ import scala.annotation.tailrec */ enum Layer derives CanEqual: - // An Empty layer - case Empty - /** A 'stack' represents a group of nested layers. Stacks are purely for organisation, and are ignored at render time. * * @param layers @@ -69,9 +66,6 @@ enum Layer derives CanEqual: */ def withMagnification(level: Int): Layer = this match - case e @ Layer.Empty => - e - case l: Layer.Stack => l.copy( layers = l.layers.map(_.withMagnification(level)) @@ -89,9 +83,6 @@ enum Layer derives CanEqual: val t = remaining.tail h match - case Layer.Empty => - rec(t, acc) - case Layer.Stack(layers) => rec(layers ++ t, acc) diff --git a/indigo/indigo/src/main/scala/indigo/shared/scenegraph/LayerEntry.scala b/indigo/indigo/src/main/scala/indigo/shared/scenegraph/LayerEntry.scala index a3d99661f..5843896e9 100644 --- a/indigo/indigo/src/main/scala/indigo/shared/scenegraph/LayerEntry.scala +++ b/indigo/indigo/src/main/scala/indigo/shared/scenegraph/LayerEntry.scala @@ -1,19 +1,29 @@ package indigo.shared.scenegraph -import indigo.BindingKey +import indigo.shared.collections.Batch +import indigo.shared.datatypes.BindingKey +/** Layer entries are holders for Layers, that can either be tagged or untagged. If a layer entry is tagged with a + * `BindingKey`, then if two SceneUpdateFragements are merged together, two entries will the same tag will be combined + * at the depth of the original. + */ enum LayerEntry: def layer: Layer case Untagged(layer: Layer) - case Tagged(key: BindingKey, layer: Layer) + case Tagged(tag: BindingKey, layer: Layer) - def hasKey(key: BindingKey): Boolean = + def hasTag(tag: BindingKey): Boolean = this match case _: LayerEntry.Untagged => false - case l: LayerEntry.Tagged => l.key == key + case l: LayerEntry.Tagged => l.tag == tag - def withKey(newKey: BindingKey): LayerEntry = + def giveTag: Option[BindingKey] = + this match + case Untagged(_) => None + case Tagged(tag, _) => Option(tag) + + def withTag(newKey: BindingKey): LayerEntry = LayerEntry.Tagged(newKey, this.layer) def withLayer(newLayer: Layer): LayerEntry = @@ -33,13 +43,16 @@ enum LayerEntry: case l: LayerEntry.Untagged => l.copy(layer = l.layer.withMagnification(level)) case l: LayerEntry.Tagged => l.copy(layer = l.layer.withMagnification(level)) + def toBatch: Batch[Layer.Content] = + layer.toBatch + object LayerEntry: - def apply(key: BindingKey, layer: Layer): LayerEntry = - LayerEntry.Tagged(key, layer) + def apply(tag: BindingKey, layer: Layer): LayerEntry = + LayerEntry.Tagged(tag, layer) def apply(maybeKey: Option[BindingKey], layer: Layer): LayerEntry = - maybeKey.fold(LayerEntry(layer))(key => LayerEntry(key, layer)) + maybeKey.fold(LayerEntry(layer))(tag => LayerEntry(tag, layer)) def apply(keyAndLayer: (BindingKey, Layer)): LayerEntry = LayerEntry.Tagged(keyAndLayer._1, keyAndLayer._2) diff --git a/indigo/indigo/src/main/scala/indigo/shared/scenegraph/SceneUpdateFragment.scala b/indigo/indigo/src/main/scala/indigo/shared/scenegraph/SceneUpdateFragment.scala index 51aeebff5..bc43721de 100644 --- a/indigo/indigo/src/main/scala/indigo/shared/scenegraph/SceneUpdateFragment.scala +++ b/indigo/indigo/src/main/scala/indigo/shared/scenegraph/SceneUpdateFragment.scala @@ -60,14 +60,14 @@ final case class SceneUpdateFragment( SceneUpdateFragment.insertLayer(this, LayerEntry(Layer(nodes))) def addLayers(newLayers: Batch[LayerEntry]): SceneUpdateFragment = - this.copy(layers = newLayers.foldLeft(layers)((acc, l) => SceneUpdateFragment.mergeLayers(this.layers, l))) + this.copy(layers = newLayers.foldLeft(layers)((acc, l) => SceneUpdateFragment.mergeLayers(acc, l))) def addLayers(newLayers: LayerEntry*): SceneUpdateFragment = addLayers(newLayers.toBatch) @targetName("addLayers_batch_key_layer") def addLayers(newLayers: Batch[(BindingKey, Layer)]): SceneUpdateFragment = this.copy( layers = newLayers.foldLeft(layers) { case (acc, (k, l)) => - SceneUpdateFragment.mergeLayers(this.layers, LayerEntry(k, l)) + SceneUpdateFragment.mergeLayers(acc, LayerEntry(k, l)) } ) @targetName("addLayers_args_key_layer") @@ -76,7 +76,7 @@ final case class SceneUpdateFragment( @targetName("addLayers_batch_layer") def addLayers(newLayers: Batch[Layer]): SceneUpdateFragment = this.copy( - layers = newLayers.foldLeft(layers)((acc, l) => SceneUpdateFragment.mergeLayers(this.layers, LayerEntry(l))) + layers = newLayers.foldLeft(layers)((acc, l) => SceneUpdateFragment.mergeLayers(acc, LayerEntry(l))) ) @targetName("addLayers_args_layer") def addLayers(newLayers: Layer*): SceneUpdateFragment = @@ -101,9 +101,6 @@ final case class SceneUpdateFragment( def mapLayers(f: LayerEntry => LayerEntry): SceneUpdateFragment = this.copy(layers = layers.map(_.modify(f))) - @targetName("mapLayers_untagged") - def mapLayers(f: Layer => Layer): SceneUpdateFragment = - this.copy(layers = layers.map(_.modifyLayer(f))) def noLights: SceneUpdateFragment = this.copy(lights = Batch.empty) @@ -227,21 +224,23 @@ object SceneUpdateFragment: case l @ LayerEntry.Untagged(_) => layers :+ layer - case LayerEntry.Tagged(t, l) if layers.exists(_.hasKey(t)) => - layers.map { ll => - if ll.hasKey(t) then + case LayerEntry.Tagged(t, l) if layers.exists(_.hasTag(t)) => + layers.map { + case x: LayerEntry.Untagged => + x + + case x @ LayerEntry.Tagged(k, ll) if t == k => val newLayer = - (ll.layer, l) match - case (Layer.Empty, b) => b - case (a: Layer.Stack, Layer.Empty) => a + (ll, l) match case (a: Layer.Stack, b: Layer.Content) => a.append(b) case (a: Layer.Stack, b: Layer.Stack) => a ++ b - case (a: Layer.Content, Layer.Empty) => a case (a: Layer.Content, b: Layer.Content) => a |+| b case (a: Layer.Content, b: Layer.Stack) => a :: b LayerEntry.Tagged(t, newLayer) - else ll + + case x: LayerEntry.Tagged => + x } case LayerEntry.Tagged(_, _) => diff --git a/indigo/indigo/src/test/scala/indigo/shared/scenegraph/LayerTests.scala b/indigo/indigo/src/test/scala/indigo/shared/scenegraph/LayerTests.scala index 59c28e6b4..90e6e2c42 100644 --- a/indigo/indigo/src/test/scala/indigo/shared/scenegraph/LayerTests.scala +++ b/indigo/indigo/src/test/scala/indigo/shared/scenegraph/LayerTests.scala @@ -7,18 +7,9 @@ import indigo.shared.materials.BlendMaterial class LayerTests extends munit.FunSuite { - test("combining layer should preserve left hand side magnification") { - - assertEquals(Layer(BindingKey("key A")).key, Some(BindingKey("key A"))) - assertEquals((Layer(BindingKey("key A")) |+| Layer(BindingKey("key B"))).key, Some(BindingKey("key A"))) - assertEquals((Layer(BindingKey("key A")) |+| Layer(Batch.empty)).key, Some(BindingKey("key A"))) - assertEquals((Layer(Batch.empty) |+| Layer(BindingKey("key B"))).key, Some(BindingKey("key B"))) - - } - test("Can add a blend material with no Blending instance in place") { val layer = - Layer(BindingKey("test")).withBlendMaterial(BlendMaterial.Lighting(RGBA.Red)) + Layer.empty.withBlendMaterial(BlendMaterial.Lighting(RGBA.Red)) layer.blending match case Some(Blending(entity, layer, BlendMaterial.Lighting(color), clearColor)) => @@ -30,7 +21,7 @@ class LayerTests extends munit.FunSuite { test("Can modify blending with no Blending instance in place") { val layer = - Layer(BindingKey("test")).modifyBlending(_.withClearColor(RGBA.Green)) + Layer.empty.modifyBlending(_.withClearColor(RGBA.Green)) layer.blending match case Some(Blending(entity, layer, blendMaterial, Some(clearColor))) => @@ -42,7 +33,7 @@ class LayerTests extends munit.FunSuite { test("Can add an entity blend mode with no Blending instance in place") { val layer = - Layer(BindingKey("test")).withEntityBlend(Blend.LightingEntity) + Layer.empty.withEntityBlend(Blend.LightingEntity) layer.blending match case Some(Blending(entity, layer, blendMaterial, clearColor)) => @@ -54,7 +45,7 @@ class LayerTests extends munit.FunSuite { test("Can add a layer blend mode with no Blending instance in place") { val layer = - Layer(BindingKey("test")).withLayerBlend(Blend.LightingEntity) + Layer.empty.withLayerBlend(Blend.LightingEntity) layer.blending match case Some(Blending(entity, layer, blendMaterial, clearColor)) => diff --git a/indigo/indigo/src/test/scala/indigo/shared/scenegraph/SceneUpdateFragmentTests.scala b/indigo/indigo/src/test/scala/indigo/shared/scenegraph/SceneUpdateFragmentTests.scala index e04a004b2..a7cd8d82b 100644 --- a/indigo/indigo/src/test/scala/indigo/shared/scenegraph/SceneUpdateFragmentTests.scala +++ b/indigo/indigo/src/test/scala/indigo/shared/scenegraph/SceneUpdateFragmentTests.scala @@ -10,22 +10,23 @@ class SceneUpdateFragmentTests extends munit.FunSuite { test("Able to add a batch of layers from a constructor") { val actual = - SceneUpdateFragment(Batch(Layer(BindingKey("key A")), Layer(BindingKey("key B")))) + SceneUpdateFragment(Batch(BindingKey("key A") -> Layer.empty, BindingKey("key B") -> Layer.empty)) val expected = - SceneUpdateFragment.empty.addLayers(Batch(Layer(BindingKey("key A")), Layer(BindingKey("key B")))) + SceneUpdateFragment.empty.addLayers( + Batch(LayerEntry(BindingKey("key A"), Layer.empty), LayerEntry(BindingKey("key B"), Layer.empty)) + ) assertEquals(actual, expected) - } test("Able to add an optional layer from a constructor (Some)") { val actual = - SceneUpdateFragment(Option(Layer(BindingKey("key A")))) + SceneUpdateFragment(Option(BindingKey("key A") -> Layer.empty)) val expected = - SceneUpdateFragment.empty.addLayers(Batch(Layer(BindingKey("key A")))) + SceneUpdateFragment.empty.addLayers(Batch(LayerEntry(BindingKey("key A"), Layer.empty))) assertEquals(actual, expected) @@ -46,81 +47,85 @@ class SceneUpdateFragmentTests extends munit.FunSuite { test("Adding a layer with an existing key merges magnification down (none, none)") { val scene = - SceneUpdateFragment.empty.addLayer(Layer(BindingKey("key A"))) + SceneUpdateFragment.empty.addLayer(LayerEntry(BindingKey("key A"), Layer.empty)) val actual = - scene.addLayer(Layer(BindingKey("key A"))) + scene.addLayer(BindingKey("key A") -> Layer.empty) assert(actual.layers.length == 1) - assertEquals(actual.layers.head.magnification, None) + assertEquals(actual.layers.flatMap(_.toBatch).head.magnification, None) } test("Adding a layer with an existing key merges magnification down (some, some)") { val scene = - SceneUpdateFragment.empty.addLayer(Layer(BindingKey("key A")).withMagnification(2)) + SceneUpdateFragment.empty.addLayer((BindingKey("key A") -> Layer.empty.withMagnification(2))) val actual = - scene.addLayer(Layer(BindingKey("key A")).withMagnification(1)) + scene.addLayer(LayerEntry(BindingKey("key A"), Layer.empty.withMagnification(1))) assert(actual.layers.length == 1) - assertEquals(actual.layers.head.magnification, Some(2)) + assertEquals(actual.layers.flatMap(_.toBatch).head.magnification, Some(2)) } test("Adding a layer with an existing key merges magnification down (none, some)") { val scene = - SceneUpdateFragment.empty.addLayer(Layer(BindingKey("key A"))) + SceneUpdateFragment.empty.addLayer(BindingKey("key A") -> Layer.empty) val actual = - scene.addLayer(Layer(BindingKey("key A")).withMagnification(1)) + scene.addLayer(BindingKey("key A") -> Layer.empty.withMagnification(1)) assert(actual.layers.length == 1) - assertEquals(actual.layers.head.magnification, Some(1)) + assertEquals(actual.layers.flatMap(_.toBatch).head.magnification, Some(1)) } test("Adding a layer with an existing key merges magnification down (some, none)") { val scene = - SceneUpdateFragment.empty.addLayer(Layer(BindingKey("key A")).withMagnification(2)) + SceneUpdateFragment.empty.addLayer(BindingKey("key A") -> Layer.empty.withMagnification(2)) val actual = - scene.addLayer(Layer(BindingKey("key A"))) + scene.addLayer(BindingKey("key A") -> Layer.empty) assert(actual.layers.length == 1) - assertEquals(actual.layers.head.magnification, Some(2)) + assertEquals(actual.layers.flatMap(_.toBatch).head.magnification, Some(2)) } test("Replace layers using withLayers") { val scene = - SceneUpdateFragment.empty.addLayer(Layer(BindingKey("key A"))) + SceneUpdateFragment.empty.addLayer(BindingKey("key A") -> Layer.empty) val actual = - scene.withLayers(Layer(BindingKey("key B"))) + scene.withLayers(BindingKey("key B") -> Layer.empty) assert(actual.layers.length == 1) - assertEquals(actual.layers.head.key, Some(BindingKey("key B"))) + + actual.layers.head match + case LayerEntry.Untagged(_) => fail("Should have been a tagged layer entry") + case LayerEntry.Tagged(key, _) => + assertEquals(key, BindingKey("key B")) } test("SUF append preseves layer keys") { val sceneA: SceneUpdateFragment = - SceneUpdateFragment.empty.addLayer(Layer(BindingKey("key A")).withMagnification(2)) + SceneUpdateFragment.empty.addLayer(BindingKey("key A") -> Layer.empty.withMagnification(2)) val sceneB: SceneUpdateFragment = - SceneUpdateFragment.empty.addLayer(Layer(BindingKey("key A")).withMagnification(3)) + SceneUpdateFragment.empty.addLayer(BindingKey("key A") -> Layer.empty.withMagnification(3)) val actual: SceneUpdateFragment = sceneA |+| sceneB assert(actual.layers.length == 1) - assertEquals(actual.layers.head.magnification, Some(2)) + assertEquals(actual.layers.flatMap(_.toBatch).head.magnification, Some(2)) } @@ -151,15 +156,25 @@ class SceneUpdateFragmentTests extends munit.FunSuite { test("Map over layers") { val scene = SceneUpdateFragment.empty - .addLayer(Layer(BindingKey("key A")).withMagnification(1)) - .addLayer(Layer(BindingKey("key B")).withMagnification(1)) + .addLayer(BindingKey("key A") -> Layer.empty.withMagnification(1)) + .addLayer(BindingKey("key B") -> Layer.empty.withMagnification(1)) val actual = - scene.mapLayers(l => l.withKey(BindingKey(l.key.map(_.toString).getOrElse("!") + "?")).withMagnification(2)) + scene.mapLayers { + case LayerEntry.Untagged(_) => + fail("Should have been a tagged layer entry") + + case l @ LayerEntry.Tagged(key, layer) => + l.withTag(BindingKey(key.toString + "?")).withMagnification(2) + } + + assert(actual.layers.flatMap(_.toBatch).length == 2) - assert(actual.layers.length == 2) - assertEquals(actual.layers.map(_.key.get).toList, List(BindingKey("key A?"), BindingKey("key B?"))) - assertEquals(actual.layers.map(_.magnification.get).toList, List(2, 2)) + assertEquals( + actual.layers.map(_.giveTag.get).toList, + List(BindingKey("key A?"), BindingKey("key B?")) + ) + assertEquals(actual.layers.flatMap(_.toBatch).map(_.magnification.get).toList, List(2, 2)) } } diff --git a/indigo/indigo/src/test/scala/indigo/shared/subsystems/SubSystemTests.scala b/indigo/indigo/src/test/scala/indigo/shared/subsystems/SubSystemTests.scala index 3c80dbbac..ea636a202 100644 --- a/indigo/indigo/src/test/scala/indigo/shared/subsystems/SubSystemTests.scala +++ b/indigo/indigo/src/test/scala/indigo/shared/subsystems/SubSystemTests.scala @@ -14,6 +14,7 @@ class SubSystemTests extends munit.FunSuite { .present(context(6).copy(reference = 10), 1230) .unsafeGet .layers + .flatMap(_.toBatch) .head .nodes .head @@ -32,6 +33,7 @@ class SubSystemTests extends munit.FunSuite { .present(context(6).copy(reference = 10), points.unsafeGet) .unsafeGet .layers + .flatMap(_.toBatch) .head .nodes .head @@ -51,6 +53,7 @@ class SubSystemTests extends munit.FunSuite { .present(context(6).copy(reference = 10), points.unsafeGet) .unsafeGet .layers + .flatMap(_.toBatch) .head .nodes .head diff --git a/indigo/indigo/src/test/scala/indigo/shared/subsystems/SubSystemsRegisterTests.scala b/indigo/indigo/src/test/scala/indigo/shared/subsystems/SubSystemsRegisterTests.scala index 9f3fbf2d7..45082539e 100644 --- a/indigo/indigo/src/test/scala/indigo/shared/subsystems/SubSystemsRegisterTests.scala +++ b/indigo/indigo/src/test/scala/indigo/shared/subsystems/SubSystemsRegisterTests.scala @@ -55,6 +55,7 @@ class SubSystemsRegisterTests extends munit.FunSuite { .present(context(6), 0) .unsafeGet .layers + .flatMap(_.toBatch) .flatMap(_.nodes) .map(_.asInstanceOf[Text[?]].text)