PenroseKiteDart-1.0.0: Library to explore Penrose's Kite and Dart Tilings.
Copyright(c) Chris Reade 2021
LicenseBSD-style
Maintainerchrisreade@mac.com
Stabilityexperimental
Safe HaskellSafe-Inferred
LanguageHaskell2010

Tgraph.Force

Description

This module includes force and tryForce plus related operations for testing and experimenting such as tryStepForce, tryAddHalfKite and tryAddHalfDart. It introduces BoundaryState and ForceState types and includes a Forcible class with instances for Tgraph, BoundaryState, and ForceState. It exposes the calculation of relative angle of edges at boundary vertices used to find existing edges. It imports a touching check for adding new vertices (with locateVertices and addVPoint).

Synopsis

Touching vertex checking

touchCheck :: Point V2 Double -> VertexMap (Point V2 Double) -> Bool Source #

touchCheck p vpMap - check if a vertex location p touches (is too close to) any other vertex location in the mapping vpMap

BoundaryState operations

data BoundaryState Source #

A BoundaryState records the boundary directed edges (directed so that faces are on LHS and exterior is on RHS) plus a mapping of boundary vertices to their incident faces, plus a mapping of boundary vertices to positions (using Tgraph.Prelude.locateVertices). It also keeps track of all the faces and the next vertex label to be used when adding a new vertex.

Constructors

BoundaryState 

Fields

makeBoundaryState :: Tgraph -> BoundaryState Source #

Calculates BoundaryState information from a Tgraph also checks for no crossing boundaries as these could cause difficult to trace errors in forcing.

recoverGraph :: BoundaryState -> Tgraph Source #

Converts a BoundaryState back to a Tgraph

changeVFMap :: TileFace -> VertexMap [TileFace] -> VertexMap [TileFace] Source #

changeVFMap f vfmap - adds f to the list of faces associated with each v in f, returning a revised vfmap

facesAtBV :: BoundaryState -> Vertex -> [TileFace] Source #

facesAtBV bd v - returns the faces found at v (which must be a boundary vertex)

boundaryFaces :: BoundaryState -> [TileFace] Source #

return a list of faces which have a boundary vertex from a BoundaryState

Types: Update, UpdateMap, UpdateGenerator, ForceState

data Update Source #

An Update is either safe or unsafe. A safe update has a new face involving 3 existing vertices. An unsafe update has a makeFace function to create the new face when given a fresh third vertex.

Instances

Instances details
Show Update Source #

0 is used as a dummy variable to show unsafe updates (to display the function explicitly)

Instance details

Defined in Tgraph.Force

type UpdateMap = Map Dedge Update Source #

UpdateMap: partial map associating updates with (some) boundary directed edges. (Any boundary directed edge will have the opposite direction in some face.)

data ForceState Source #

ForceState: The force state records information between executing single face updates during forcing (a BoundaryState and an UpdateMap).

type UpdateGenerator = BoundaryState -> [Dedge] -> Try UpdateMap Source #

UpdateGenerator abbreviates the type of functions which capture one or more of the forcing rules. They produce a (Try) UpdateMap when given a BoundaryState and a focus list of particular directed boundary edges. Each forcing rule has a particular UpdateGenerator, but they can also be combined (e.g in sequence - allUGenerator or otherwise - defaultAllUGenerator).

Forcible class and Instances (ForceState, BoundaryState, Tgraph)

class Forcible a where Source #

Forcible class has operations to (indirectly) implement forcing and single step forcing (tryForceWith, tryStepForceWith) for any Forcible. The class operations are more general to allow for other force related operations to be generalised for use on any Forcible. Both tryForceWith and tryStepForceWith are implemented using tryFSOpWith, and tryAddHalfKite and tryAddHalfDart are implemented using tryChangeBoundaryWith.

To improve performance of a sequence of force related operations, express each as a ForceState -> Try ForceState, then use (or (=>) to combine and pass to tryFSOpWith. This ensures there are no unnecessary conversions between steps.

Methods

tryFSOpWith :: UpdateGenerator -> (ForceState -> Try ForceState) -> a -> Try a Source #

tryFSOpWith (try ForseState Operation with) when given an update generator, generalises a (try) ForceState operation to a (try) Forcible operation. The update generator is only used to initialise a ForceState when there is not one already available (i.e not used when the Forcible is a ForceState)

tryInitFSWith :: UpdateGenerator -> a -> Try ForceState Source #

tryInitFSWith (try initialising a ForceState with) when given an update generator tries to create an initial ForceState (ready for forcing) from a Forcible. Again, the update generator is not used when the instance is a ForceState.

tryChangeBoundaryWith :: UpdateGenerator -> (BoundaryState -> Try BoundaryChange) -> a -> Try a Source #

tryChangeBoundaryWith when given an update generator, converts a (try) BoundaryState changing operation to a (try) Forcible operation. The update generator is only used when the instance is a ForceState (to revise the update map in the result).

Instances

Instances details
Forcible BoundaryState Source #

BoundaryStates are Forcible

Instance details

Defined in Tgraph.Force

Forcible ForceState Source #

ForceStates are Forcible

Instance details

Defined in Tgraph.Force

Forcible Tgraph Source #

Tgraphs are Forcible

Instance details

Defined in Tgraph.Force

Forcible TrackedTgraph Source #

TrackedTgraphs are Forcible

Instance details

Defined in Tgraphs

Generalised forcing operations

tryForceWith :: Forcible a => UpdateGenerator -> a -> Try a Source #

try forcing using a given UpdateGenerator. tryForceWith uGen fs - recursively does updates using uGen until there are no more updates. It produces Left report if it encounters a Forcible representing a stuck/incorrect Tgraph.

tryStepForceWith :: Forcible a => UpdateGenerator -> Int -> a -> Try a Source #

try a given number of force steps using a given UpdateGenerator.

tryFSOp :: Forcible a => (ForceState -> Try ForceState) -> a -> Try a Source #

A version of tryFSOpWith using defaultAllUGen representing all 10 rules for updates.

tryForce :: Forcible a => a -> Try a Source #

A version of the main force function using defaultAllUGen representing all 10 rules for updates. This returns Left report on discovering a stuck Tgraph and Right a (with a the resulting forcible) otherwise.

force :: Forcible a => a -> a Source #

The main force function using defaultAllUGen representing all 10 rules for updates. This raises an error on discovering a stuck/incorrect Forcible.

wholeTiles :: Forcible a => a -> a Source #

special case of forcing only half tiles to whole tiles

forceWith :: Forcible a => UpdateGenerator -> a -> a Source #

forceWith ugen: force using the given UpdateGenerator

tryInitFS :: Forcible a => a -> Try ForceState Source #

try to initialize a force state with the default UpdateGenerator. Returns a Left report if it finds a stuck forcible.

initFS :: Forcible a => a -> ForceState Source #

initialize a force state with the default UpdateGenerator. Raises aan error if it finds a stuck forcible.

tryStepForce :: Forcible a => Int -> a -> Try a Source #

tryStepForce n a - produces a (Right) intermediate Forcible after n steps (n face additions) starting from Forcible a. or a Left report if it encounters a stuck/incorrect Forcible within n steps. If forcing finishes successfully in n or fewer steps, it will return that final Forcible.

stepForce :: Forcible a => Int -> a -> a Source #

stepForce n a - produces an intermediate intermediate Forcible after n steps (n face additions) starting from Forcible a. It raises an error if it encounters a stuckincorrect TgraphForcible within n steps. If forcing finishes successfully in n or fewer steps, it will return that final Forcible.

tryChangeBoundary :: Forcible a => (BoundaryState -> Try BoundaryChange) -> a -> Try a Source #

specialises tryChangeBoundaryWith to the default update generator.

Force Related Functions

addHalfKite :: Forcible a => Dedge -> a -> a Source #

addHalfKite is for adding a single half kite on a chosen boundary Dedge of a Forcible. The Dedge must be a boundary edge but the direction is not important as the correct direction is automatically calculated. It will raise an error if the edge is a dart join or if a conflict (stuck graph) is detected or if the edge is not a boundary edge.

tryAddHalfKite :: Forcible a => Dedge -> a -> Try a Source #

tryAddHalfKite is a version of addHalfKite which returns a Try with a Left report if it finds a stuck/incorrect graph, or if the edge is a dart join, or if the edge is not a boundary edge.

addHalfDart :: Forcible a => Dedge -> a -> a Source #

addHalfDart is for adding a single half dart on a chosen boundary Dedge of a Forcible. The Dedge must be a boundary edge but the direction is not important as the correct direction is automatically calculated. It will raise an error if the edge is a dart short edge or kite join or if a conflict (stuck graph) is detected or if the edge is not a boundary edge.

tryAddHalfDart :: Forcible a => Dedge -> a -> Try a Source #

tryAddHalfDart is a version of addHalfDart which returns a Try with a Left report if it finds a stuck/incorrect graph, or if the edge is a dart short edge or kite join, or if the edge is not a boundary edge.

Specialised forcing operations (used for inspecting steps)

tryOneStepWith :: UpdateGenerator -> ForceState -> Try (Maybe (ForceState, BoundaryChange)) Source #

tryOneStepWith uGen fs does one force step. It returns a (Try maybe) with a new force sate paired with the boundary change for debugging purposes. It uses uGen to revise updates in the final ForceState. It produces Left report for a stuck/incorrect graph. A result of Right Nothing indicates forcing has finished and there are no more updates.

tryOneStepF :: ForceState -> Try (Maybe (ForceState, BoundaryChange)) Source #

tryOneStepF is a special case of tryOneStepWith only used for debugging

Updating BoundaryState and ForceState after a single force step

data BoundaryChange Source #

BoundaryChange records the new boundary state after completing an update (by either trySafeUpdate or tryUnsafeUpdate) along with a list of directed edges which are no longer on the boundary, plus a list of boundary edges revised (and requiring updates to be recalculated). See affectedBoundary.

Constructors

BoundaryChange 

Fields

Instances

Instances details
Show BoundaryChange Source # 
Instance details

Defined in Tgraph.Force

affectedBoundary :: BoundaryState -> [Dedge] -> [Dedge] Source #

Given a BoundaryState with a list of one new boundary edge or two adjacent new boundary edges (or exceptionally no new boundary edges) to be added, it extends the list with adjacent boundary edges (to produce 3 or 4 or none). (Used to calculate revisedEdges in a BoundaryChange) (When a face is fitted in to a hole with 3 sides there is no new boundary.)

tryReviseUpdates :: UpdateGenerator -> BoundaryChange -> UpdateMap -> Try UpdateMap Source #

tryReviseUpdates uGen bdChange: revises the UpdateMap after boundary change (bdChange) using uGen to calculate new updates.

tryReviseFSWith :: UpdateGenerator -> BoundaryChange -> ForceState -> Try ForceState Source #

tryReviseFSWith ugen bdC fs tries to revise fs after a boundary change (bdC) by calculating the revised updates with ugen (and using the new boundary state in bdC).

Auxiliary Functions for doing a force step

findSafeUpdate :: UpdateMap -> Maybe Update Source #

finds the first safe update - Nothing if there are none (ordering is directed edge key ordering)

tryUnsafes :: ForceState -> Try (Maybe BoundaryChange) Source #

tryUnsafes: Should only be used when there are no Safe updates in the UpdateMap. tryUnsafes works through the unsafe updates in (directed edge) key order and completes the first unsafe update that is not blocked (by a touching vertex), returning Right (Just bdC) where bdC is the resulting boundary change (if there is one). It returns Right Nothing if there are no unsafe updates but Left report if there are unsafes but all unsafes are blocked, where report describes the problem.

checkUnsafeUpdate :: BoundaryState -> Update -> Maybe BoundaryChange Source #

checkUnsafeUpdate bd u, calculates the resulting boundary change for an unsafe update (u) with a new vertex (raising an error if u is a safe update). It performs a touching vertex check with the new vertex returning Nothing if there is a touching vertex (blocked case). Otherwise it returns Just bdc with bdc a boundary change. [Note: Try is not used as a conflict cannot be found in the unsafe case, and blocking is only a problem when all unsafe updates are blocked (and there is at least one) - see tryUnsafes]

trySafeUpdate :: BoundaryState -> Update -> Try BoundaryChange Source #

trySafeUpdate bd u adds a new face by completing a safe update u on BoundaryState bd (raising an error if u is an unsafe update). It returns a Right BoundaryChange (containing a new BoundaryState, removed boundary edges and revised boundary edge list), unless a stuck/incorrect graph is found. It checks that the new face is not in conflict with existing faces, producing (Left report) if there is a conflict. It should cater for the exceptional case where the update removes 3 boundary edges in a triangle (and removes 3 boundary vertices), closing a hole.

tryUpdate :: BoundaryState -> Update -> Try BoundaryChange Source #

tryUpdate: tries a single update (safe or unsafe), producing Left report if the update creates a touching vertex in the unsafe case, or if a stuck/incorrect Tgraph is discovered in the safe case.

Recalibrating versions of force and tryForce

recalculateBVLocs :: BoundaryState -> BoundaryState Source #

This recalibrates a BoundaryState by recalculating boundary vertex positions from scratch with locateVertices. (Used at intervals in tryRecalibrateForce and recalibrateForce).

tryRecalibratingForce :: Forcible c => c -> Try c Source #

A version of tryForce that recalibrates at 20,000 step intervals by recalculating boundary vertex positions from scratch. This is needed to limit accumulated inaccuracies when large numbers of faces are added in forcing.

recalibratingForce :: Forcible c => c -> c Source #

A version of force that recalibrates at 20,000 step intervals by recalculating boundary vertex positions from scratch. This is needed to limit accumulation of errors when large numbers of faces are added in forcing.

Main All Update Generators

defaultAllUGen :: UpdateGenerator Source #

An alternative to allUGenerator, and used as the default. It uses the same rules and UCheckers, but makes decisions based on the EdgeType of a boundary edge (instead of trying each UFinder in turn). If there are any Left..(fail reports) for the given boundary edges the result is a sigle Left.. concatenating all the failure reports (unlike allUGenerator).

allUGenerator :: UpdateGenerator Source #

allUGenerator combines all the 10 rule update generators. They are combined in sequence (keeping the rule order) after applying each to the supplied BoundaryState and a focus edge list. (See also defaultAllUGen). This version returns a Left..(fail report) for the first generator that produces a Left..(fail report). See $rules

Tools for making update generators

type UFinder = BoundaryState -> [Dedge] -> [(Dedge, TileFace)] Source #

UFinder (Update case finder functions). Given a BoundaryState and a list of (focus) boundary directed edges, such a function returns each focus edge satisfying the particular update case paired with the tileface matching that edge. For example, if the function is looking for dart short edges on the boundary, it will return only those focus edges which are half-dart short edges, each paired with its half-dart face.

type UChecker = BoundaryState -> TileFace -> Try Update Source #

UChecker (Update checker functions). Given a BoundaryState and a particular tileface (on the boundary), such functions try to produce particular updates on the boundary edge of the given tileface. [They are called update checkers because they may uncover an incorrect/stuck tiling (using tryFindThirdV) when creating the update.] As an example, addKiteShortE will try to produce an update to add a half-kite with short edge against the boundary. Such a function can be used with a UFinder that either returns dart halves with short edge on the boundary (nonKDarts in rule 2) or returns kite halves with short edge on the boundary (kitesWingDartOrigin in rule 3).

boundaryFilter :: (BoundaryState -> Dedge -> TileFace -> Bool) -> UFinder Source #

This is a general purpose filter used to create UFinder functions for each force rule. It requires a face predicate. The face predicate takes a BoundaryState bd, a boundary Dedge (a,b) and the TileFace with the edge (b,a) and decides whether the face is wanted or not (True = wanted) This will be used to filter all the faces at the focus edges (when given a BoundaryState and list of focus edges). For some predicates the BoundaryState argument is not used (eg boundaryJoin in incompleteHalves), but for others it is used to look at other faces at b or at a besides the supplied face (eg in kitesWingDartOrigin)

makeUpdate :: (Vertex -> TileFace) -> Maybe Vertex -> Update Source #

makeUpdate f x constructs a safe update if x is Just .. and an unsafe update if x is Nothing

makeGenerator :: UChecker -> UFinder -> UpdateGenerator Source #

makeGenerator combines an update case finder (UFinder) with its corresponding update checker (UChecker) to produce an update generator function. This is used to make each of the 10 update generators corresponding to 10 rules.

When the generator is given a BoundaryState and list of focus edges, the finder produces a list of pairs of dedge and face, the checker is used to convert the face in each pair to an update (which can fail with a Left report), and the new updates are returned as a map (with the dedges as key) in a Right result.

BoundaryState vertex predicates and properties

mustbeStar :: BoundaryState -> Vertex -> Bool Source #

A vertex on the boundary must be a star if it has 7 or more dart origins

mustbeSun :: BoundaryState -> Vertex -> Bool Source #

A vertex on the boundary must be a sun if it has 5 or more kite origins

mustbeDeuce :: BoundaryState -> Vertex -> Bool Source #

A vertex on the boundary which is an oppV of a kite must be a deuce if there is a shared kite short edge at the vertex.

mustbeKing :: BoundaryState -> Vertex -> Bool Source #

A boundary vertex which is a kite wing and has 4 dart origins must be a king vertex

isKiteWing :: BoundaryState -> Vertex -> Bool Source #

isKiteWing bd v - Vertex v is a kite wing in BoundaryState bd

isKiteOppV :: BoundaryState -> Vertex -> Bool Source #

isKiteOppV bd v - Vertex v is a kite oppV in BoundaryState bd

isDartOrigin :: BoundaryState -> Vertex -> Bool Source #

isDartOrigin bd v - Vertex v is a dart origin in BoundaryState bd

mustbeQueen :: BoundaryState -> Vertex -> Bool Source #

A boundary vertex with >2 kite wings is a queen vertex (needing a fourth kite on a kite short edge or dart on a kite long edge)

kiteWingCount :: BoundaryState -> Vertex -> Int Source #

kiteWingCount bd v - the number of kite wings at v in BoundaryState bd

mustbeJack :: BoundaryState -> Vertex -> Bool Source #

mustbeJack (large dart base / jack) is true of a boundary vertex if it is the wing of two darts not sharing a long edge or it is a wing of a dart and also a kite origin (false means it is either undetermined or is a large kite centre - deuce)

Forcing Rules and Individual Update Generators (with corresponding Finders) for each rule.

wholeTileUpdates :: UpdateGenerator Source #

Update generator for rule (1)

incompleteHalves :: UFinder Source #

Find faces with missing opposite face (mirror face)

aceKiteUpdates :: UpdateGenerator Source #

Update generator for rule (2)

nonKDarts :: UFinder Source #

Find half darts with boundary short edge

queenOrKingUpdates :: UpdateGenerator Source #

Update generator for rule (3) queen and king vertices add a missing kite half (on a boundary kite short edge)

kitesWingDartOrigin :: UFinder Source #

Find kites with boundary short edge where the wing is also a dart origin

deuceDartUpdates :: UpdateGenerator Source #

Update generator for rule (4) (for deuce vertices = largeKiteCentres) Kites whose short edge (b,a) matches a boundary edge (a,b) where their oppV has 2 other kite halves sharing a shortE. These need a dart adding on the short edge.

kiteGaps :: UFinder Source #

Find kite halves with a short edge on the boundary (a,b) where there are 2 other kite halves sharing a short edge at oppV of the kite half (a for left kite and b for right kite)

jackDartUpdates :: UpdateGenerator Source #

Update generator for rule (5) jackDartUpdates - jack vertex add a missing second dart

noTouchingDart :: UFinder Source #

Find kite halves with a short edge on the boundary (a,b) where oppV is a largeDartBase vertex (oppV is a for left kite and b for right kite). The function mustbeJack determines if a vertex must be a a largeDartBase / jack

sunStarUpdates :: UpdateGenerator Source #

Update generator for rule (6) sunStarUpdates is for vertices that must be either sun or star almostSunStar finds half-kites/half-darts with a long edge on the boundary where their origin vertex has 8 total half-kites/half-darts respectively or their origin vertex has 6 total half-kites in the case of kites only completeSunStar will add a new face of the same type (dart/kite) sharing the long edge.

almostSunStar :: UFinder Source #

Find a boundary long edge of either a dart where there are at least 7 dart origins, or a kite where there are at least 5 kite origins

jackKiteUpdates :: UpdateGenerator Source #

Update generator for rule (7) jack vertices (largeDartBases) with dart long edge on boundary - add missing kite top

jackMissingKite :: UFinder Source #

Find jack vertices (largeDartBases) with dart long edge on the boundary

kingDartUpdates :: UpdateGenerator Source #

Update generator for rule (8) king vertices with 2 of the 3 darts - add another half dart on a boundary long edge of existing darts

kingMissingThirdDart :: UFinder Source #

Find king vertices with a dart long edge on the boundary and 2 of the 3 darts at its origin plus a kite wing at its origin

queenDartUpdates :: UpdateGenerator Source #

Update generator for rule (9) queen vertices (with 4 kite wings) -- add any missing half dart on a boundary kite long edge

queenMissingDarts :: UFinder Source #

Find queen vertices (with 3 or 4 kite wings) and a boundary kite long edge

queenKiteUpdates :: UpdateGenerator Source #

Update generator for rule (10) queen vertices with 3 kite wings -- add missing fourth half kite on a boundary kite short edge

queenMissingKite :: UFinder Source #

Find queen vertices with only 3 kite wings and a kite short edge on the boundary

Six Update Checkers

completeHalf :: UChecker Source #

completeHalf will check an update to add a symmetric (mirror) face for a given face at a boundary join edge.

addKiteShortE :: UChecker Source #

add a (missing) half kite on a (boundary) short edge of a dart or kite

addDartShortE :: UChecker Source #

add a half dart top to a boundary short edge of a half kite.

completeSunStar :: UChecker Source #

add a kite half to a kite long edge or dart half to a dart long edge

addKiteLongE :: UChecker Source #

add a kite to a long edge of a dart or kite

addDartLongE :: UChecker Source #

add a half dart on a boundary long edge of a dart or kite

Auxiliary Functions for adding faces: externalAngle and tryFindThirdV. $Additions

tryFindThirdV :: BoundaryState -> Dedge -> (Int, Int) -> Try (Maybe Vertex) Source #

tryFindThirdV finds a neighbouring third vertex on the boundary if there is one in the correct direction for a face added to the right hand side of a directed boundary edge. In tryFindThirdV bd (a,b) (n,m), the two integer arguments n and m are the INTERNAL angles for the new face on the boundary directed edge (a,b) (for a and b respectively) expressed as multiples of tt (tt being a tenth turn) and must both be either 1,2, or 3. tryFindThirdV compares these internal angles with the external angles of the boundary calculated at a and b. If one of them matches, then an adjacent boundary edge will lead to the required vertex. If either n or m is too large a Left report is returned indicating an incorrect graph (stuck tiling). If n and m are smaller than the respective external angles, Right Nothing is returned.

externalAngle :: BoundaryState -> Vertex -> Int Source #

externalAngle bd v - calculates the external angle at boundary vertex v in BoundaryState bd as an integer multiple of tt (tenth turn), so 1..9. It relies on there being no crossing boundaries, so that there is a single external angle at each boundary vertex.