packages feed

moonlight-planar-1.1.0.0: docs/examples/Moonlight/Planar/Example/AlphaBoundary.hs

module Moonlight.Planar.Example.AlphaBoundary
  ( AlphaBoundaryExampleError (..)
  , BoundaryShape (..)
  , alphaBoundaryShapes
  ) where

import Data.Bifunctor (first)
import qualified Data.Vector as Vector
import Moonlight.Planar.Alpha (alphaShapeContainsFace)
import Moonlight.Planar.BulkLoad (delaunayGeometry)
import Moonlight.Planar.FloodFillIterator (BoundaryObstruction (..), RegionBoundary, boundaryLoopVertices, componentBoundary, faceComponents, regionBoundaryHoleLoops, regionBoundaryOuterLoop)
import Moonlight.Planar.Types (BuildError (..))
import Moonlight.Planar.Point (Point (..))
import Moonlight.Planar.Scalar (RadiusSquaredError (..), mkRadiusSquared)

data AlphaBoundaryExampleError
  = AlphaBoundaryBuildFailed !BuildError
  | AlphaBoundaryRadiusFailed !RadiusSquaredError
  | AlphaBoundaryDescentFailed !BoundaryObstruction
  deriving stock (Eq, Show)

data BoundaryShape = BoundaryShape
  { boundaryOuterVertexCount :: !Int
  , boundaryHoleCount :: !Int
  }
  deriving stock (Eq, Show)

-- | Descend the closed alpha-shape face section of one square to its boundary.
alphaBoundaryShapes :: Either AlphaBoundaryExampleError [BoundaryShape]
alphaBoundaryShapes = do
  mesh <-
    first AlphaBoundaryBuildFailed
      ( delaunayGeometry
          ( Vector.fromList
              [ Point 0 0
              , Point 2 0
              , Point 2 2
              , Point 0 2
              ]
          )
      )
  threshold <- first AlphaBoundaryRadiusFailed (mkRadiusSquared 2)
  let selectedComponents =
        fmap snd
          (filter fst (faceComponents mesh (alphaShapeContainsFace threshold mesh)))
  traverse
    ( fmap boundaryShape
        . first AlphaBoundaryDescentFailed
        . componentBoundary mesh
    )
    selectedComponents

boundaryShape :: RegionBoundary -> BoundaryShape
boundaryShape boundary =
  BoundaryShape
    { boundaryOuterVertexCount =
        length (boundaryLoopVertices (regionBoundaryOuterLoop boundary))
    , boundaryHoleCount = length (regionBoundaryHoleLoops boundary)
    }