-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Add random polygons, with validity constraints
- Loading branch information
Showing
2 changed files
with
107 additions
and
0 deletions.
There are no files selected for viewing
74 changes: 74 additions & 0 deletions
74
src/main/scala/io/github/scala_tessella/tessella/creation/Randomic.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) |
33 changes: 33 additions & 0 deletions
33
src/test/scala/io/github/scala_tessella/tessella/creation/RandomicSpec.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
} | ||
|
||
} |