Skip to content

Commit

Permalink
More general dual methods
Browse files Browse the repository at this point in the history
  • Loading branch information
mcallisto committed May 20, 2024
1 parent c8aa4f4 commit c2ab2a9
Show file tree
Hide file tree
Showing 3 changed files with 49 additions and 16 deletions.
26 changes: 25 additions & 1 deletion src/main/scala/io/github/scala_tessella/tessella/Tiling.scala
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,11 @@ class Tiling private(edges: List[Edge]) extends Graph(edges) with Ordered[Tiling
/** Unit length of the perimeter */
val perimeterLength: Int =
perimeter.toRingNodes.size


/** Edges inside of the perimeter */
def nonPerimeterEdges: List[Edge] =
graphEdges.diff(perimeter.toRingEdges.toList)

/** All [[PolygonPath]], those touching the perimeter and those that don't */
private lazy val (perimeterPolygons, innerPolygons) : (List[PolygonPath], List[PolygonPath]) =
orientedPolygons.partition(_.hasSharedNodesWith(perimeter.toRingNodes))
Expand Down Expand Up @@ -598,6 +602,26 @@ class Tiling private(edges: List[Edge]) extends Graph(edges) with Ordered[Tiling
def compare(a: Edge, b: Edge): Int =
MinAngleEdgeNodeOrdering.orElse(MaxAngleEdgeNodeOrdering).compare(a, b)

/** Extracts data from the dual of the tiling
*
* @param f map polygons to an element U
* @param g transforms couples of U into target
* @tparam T target
* @tparam U element associated to a polygon
*/
def dualTransform[T, U](f: List[PolygonPath] => Map[List[Edge], U], g: (U, U) => T): List[T] =
nonPerimeterEdges
.map(edge => f(orientedPolygons).filter((edges, _) => edges.contains(edge)).values)
.map(_.toList.toCouple)
.map(g(_, _))

/** Dual graph obtained by connecting each adjacent polygon */
def dual: Graph =
Graph(dualTransform(
_.map(_.toPolygonEdges).zipWithIndex.map((edges, i) => (edges, Node(i + 1))).toMap,
Edge(_, _)
))

/** Companion object for [[Tiling]] */
object Tiling extends UniTriangle with UniHex with Uni4Hex with Uni5Hex with Layered with Quadratic:

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -323,27 +323,18 @@ object SVG extends ConverterSVG:
else
None

private def polygonsCentreCoords: Map[List[Edge], Point] =
tiling.orientedPolygons.map(path =>
path.toPolygonEdges -> RegularPolygon2D(path.toPolygonPathNodes.map(tiling.coords).toList).center()
).toMap

private def innerEdges: List[Edge] =
tiling.graphEdges.diff(tiling.perimeter.toRingEdges.toList)

private def dualSegments: List[LineSegment] =
innerEdges
.map(edge => polygonsCentreCoords.filter((edges, _) => edges.contains(edge)).values)
.map(_.toList.toCouple)
.map(LineSegment(_, _))

private def dualSVG(showDual: Boolean): Option[Elem] =
if showDual then
Option(
group(
Option(Title("Dual")),
Option(Description("Dual tessellation by joining the centres of each two polygons sharing an edge")),
dualSegments.map(line)*
tiling.dualTransform(
_.map(path =>
path.toPolygonEdges -> RegularPolygon2D(path.toPolygonPathNodes.map(tiling.coords).toList).center()
).toMap,
LineSegment(_, _)
).map(line)*
).withStyle(dualStyle)
)
else
Expand Down
18 changes: 18 additions & 0 deletions src/test/scala/io/github/scala_tessella/tessella/TilingSpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -457,5 +457,23 @@ class TilingSpec extends AnyFlatSpec with Accuracy with should.Matchers {
.forall({ case ((a, b), (c, d)) => a == c && b.~=(d) }) shouldBe
true
}

"A tiling" can "have an empty dual graph" in {
triangle.dual.graphEdges.isEmpty shouldBe
true
}

it can "have a dual graph that is a valid tiling" in {
sqr4x4Reticulate.dual.toMaybeTiling.isRight shouldBe
true
}

it can "have a dual graph" in {
tri4x4Reticulate.dual.graphEdges shouldBe
List(
6--8, 9--10, 14--15, 3--4, 2--7, 11--12, 4--5, 2--8, 10--11,
13--15, 5--6, 8--9, 10--14, 15--16, 1--4, 2--3, 7--11, 12--13
)
}

}

0 comments on commit c2ab2a9

Please sign in to comment.