Skip to content

Commit

Permalink
Randomic creation (#9)
Browse files Browse the repository at this point in the history
* Add random polygons, with validity constraints
  • Loading branch information
mcallisto authored Apr 27, 2024
1 parent e2ad63e commit a196c31
Show file tree
Hide file tree
Showing 2 changed files with 107 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
package io.github.scala_tessella.tessella
package creation

import RegularPolygon.{Polygon, Vertex}
import TilingGrowth.OtherNodeStrategy.BEFORE_PERIMETER
import Topology.Edge

import scala.util.Random

object Randomic:

extension (polygons: Vertex)

private def isContainedInAtLeastOne(containers: List[Vertex]): Boolean =
containers.exists(polygons.isContainedIn)

private def isContainedInPattern(pattern: Pattern): Boolean =
isContainedInAtLeastOne(pattern.distinctVertices.map(_.vertex))

extension (tiling: Tiling)

private def isEmpty: Boolean =
tiling.graphEdges.isEmpty

/** Tries adding a random polygon, with an optional addition validity clause
*
* @param maybePolygons polygons that can be added, if None all tessellable polygons
* @param validity function to filter tilings
*/
def randomStep(maybePolygons: Option[List[Polygon]] = None,
validity: Tiling => Boolean = _ => true): Option[Tiling] =
val polygons: List[Polygon] =
maybePolygons.getOrElse(Vertex.tessellablePolygons)
if isEmpty then
Option(Tiling.fromPolygon(polygons(Random.nextInt(polygons.size))))
else
val combinations: List[(Edge, Polygon)] =
for
polygon <- polygons
edge <- tiling.perimeter.toRingEdges
yield
(edge, polygon)
Random.shuffle(combinations)
.view
.map(tiling.maybeGrowEdge(_, _, BEFORE_PERIMETER))
.find(maybeTiling => maybeTiling.isRight && validity(maybeTiling.toOption.get))
.map(_.toOption.get)

/** Tries adding sequentially random polygons, with an optional addition validity clause
*
* @param steps number of additions
* @param maybePolygons polygons that can be added, if None all tessellable polygons
* @param validity function to filter tilings
* @return
*/
def randomSteps(steps: Int,
maybePolygons: Option[List[Polygon]] = None,
validity: Tiling => Boolean = _ => true): Option[Tiling] =
(0 until steps).foldLeft(Option(tiling))((maybeTiling, _) =>
maybeTiling.flatMap(_.randomStep(maybePolygons, validity))
)

/** Tries adding sequentially random polygons, within a given pattern
*
* @param steps number of additions
* @param pattern a [[Pattern]] each new polygon must follow
* @return
*/
def randomStepsWithinPattern(steps: Int, pattern: Pattern): Option[Tiling] =
val polygons: List[Polygon] =
pattern.vertices.flatMap(_.vertex.toPolygons).distinct
val validity: Tiling => Boolean =
_.orderedPerimeterMinorVertices.forall(_.isContainedInPattern(pattern))
randomSteps(steps, Option(polygons), validity)
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package io.github.scala_tessella.tessella
package creation

import Randomic.*
import RegularPolygon.Polygon
import conversion.SVG.toSVG

import org.scalatest.flatspec.AnyFlatSpec
import org.scalatest.matchers.should

class RandomicSpec extends AnyFlatSpec with Helper with should.Matchers{

"A random polygon" can "be added to an empty tiling" in {
Tiling.empty.randomStep().get.countPolygons shouldBe
1
}

it can "be added to a tiling formed by only a polygon" in {
triangle.randomStep().get.countPolygons shouldBe
2
}

"Two random polygons" can "be added to an empty tiling" in {
Tiling.empty.randomSteps(2).get.countPolygons shouldBe
2
}

"Ten random triangles or squares" can "be added to an empty tiling" in {
Tiling.empty.randomSteps(10, Option(List(Polygon(3), Polygon(4)))).get.hedrality <= 2 shouldBe
true
}

}

0 comments on commit a196c31

Please sign in to comment.