moonlight-planar-1.1.0.0: test/public-components/Main.hs
module Main (main) where
import Data.Bifunctor (first)
import qualified Data.Vector as Vector
import qualified Moonlight.Planar.AdmissionSpec as AdmissionSpec
import Moonlight.Planar.BulkLoad (delaunayGeometry)
import Moonlight.Planar.Dcel (numVertices)
import Moonlight.Planar.FloodFillIterator
( RectangleMetricError
, verticesInRectangle
)
import Moonlight.Planar.Handles.Iterators.DynamicIterators
( vertexHandles
)
import Moonlight.Planar.Handles.Scoped
( scopedVertexPoint
, scopedVertices
, withScopedTriangulation
)
import Moonlight.Planar.HintGenerator (buildHierarchyHint, hierarchyHint)
import Moonlight.Planar.PointLocation (locatePoint)
import Moonlight.Planar.Refinement
( refinementAdditionalVertexBudget
, refinementExcludesOuterFaces
, refinementMaximumArea
, refinementMinimumArea
, refinementPreservesConstraints
, validateRefinementParameters
, withAdditionalVertexBudget
, withConstraintPreservation
, withMaximumArea
, withMinimumArea
, withOuterFaceExclusion
)
import Moonlight.Planar.Session
( insertVertexAtNearVertex
, removeAt
, withSession
)
import Moonlight.Planar.Types (BuildError, defaultRefinementParameters, InsertionDisposition (Inserted), Location (OnVertex))
import Moonlight.Planar.Point (Point (..), PointValidationError)
import qualified Moonlight.Planar.Telemetry as Telemetry
import Moonlight.Planar.Point (mkQueryPoint)
import Moonlight.Planar.Math (orient2d)
import Moonlight.Planar.Validation (validateTriangulation)
import System.Exit (die)
data PublicComponentFailure
= PublicBuildFailure !BuildError
| PublicQueryFailure !PointValidationError
| PublicRectangleFailure !RectangleMetricError
| PublicSeedVertexMissing
deriving stock (Show)
data PublicComponentSummary = PublicComponentSummary
{ residentVertices :: !Int
, owningVertexHandles :: !Int
, scopedVertexPoints :: !Int
, rectangleVertices :: !Int
, locatedResidentVertex :: !Bool
, admittedPredicateCorrect :: !Bool
, hierarchyTelemetryNearestCorrect :: !Bool
, sessionInsertedVertex :: !Bool
, sessionRemovedVertex :: !Bool
, finalMeshValid :: !Bool
}
deriving stock (Eq, Show)
main :: IO ()
main = do
AdmissionSpec.tests
assertRefinementParameterAPI
either
(die . ("public component workflow refused: " <>) . show)
assertExpected
publicComponentWorkflow
assertRefinementParameterAPI :: IO ()
assertRefinementParameterAPI =
case do
withAdditionalVertexBudget 32 defaultRefinementParameters
>>= withMaximumArea 4
>>= withMinimumArea 1
>>= pure . withConstraintPreservation True
>>= pure . withOuterFaceExclusion True
of
Left obstruction -> die ("checked refinement authoring refused: " <> show obstruction)
Right parameters -> do
case validateRefinementParameters parameters of
Left obstruction -> die ("admitted refinement parameters failed validation: " <> show obstruction)
Right () -> pure ()
if refinementAdditionalVertexBudget parameters == Just 32
&& refinementMinimumArea parameters == Just 1
&& refinementMaximumArea parameters == Just 4
&& refinementPreservesConstraints parameters
&& refinementExcludesOuterFaces parameters
then pure ()
else die "checked refinement observations disagreed with their setters"
case withMaximumArea 2 defaultRefinementParameters >>= withMinimumArea 3 of
Left _ -> pure ()
Right _ -> die "minimum area greater than maximum area was admitted"
publicComponentWorkflow :: Either PublicComponentFailure PublicComponentSummary
publicComponentWorkflow = do
square <-
first PublicBuildFailure
( delaunayGeometry
( Vector.fromList
[ Point (-1) (-1)
, Point 1 (-1)
, Point 1 1
, Point (-1) 1
]
)
)
query <- first PublicQueryFailure (mkQueryPoint (Point (-1) (-1)))
seed <- case locatePoint square query of
OnVertex vertex -> Right vertex
_ -> Left PublicSeedVertexMissing
hierarchy <- first PublicBuildFailure (buildHierarchyHint 2 square)
let hintedVertex = case hierarchyHint hierarchy query of
Just (Telemetry.VertexHint vertex) -> Just vertex
_ -> Nothing
((disposition, removed), restored, _) <-
first PublicBuildFailure
( withSession square 1 $ do
(_, inserted) <- insertVertexAtNearVertex seed (Point 0 0) ()
removal <- removeAt (Point 0 0)
pure (inserted, removal)
)
queryRight <- first PublicQueryFailure (mkQueryPoint (Point 1 (-1)))
queryTop <- first PublicQueryFailure (mkQueryPoint (Point (-1) 1))
inside <-
first PublicRectangleFailure
(verticesInRectangle restored (Point (-2) (-2)) (Point 2 2))
pure
PublicComponentSummary
{ residentVertices = numVertices restored
, owningVertexHandles = length (vertexHandles restored)
, scopedVertexPoints =
withScopedTriangulation restored $ \scoped ->
length (fmap (scopedVertexPoint scoped) (scopedVertices scoped))
, rectangleVertices = length inside
, locatedResidentVertex = case locatePoint restored query of
OnVertex _ -> True
_ -> False
, admittedPredicateCorrect = orient2d query queryRight queryTop == GT
, hierarchyTelemetryNearestCorrect =
hintedVertex /= Nothing
&& fmap fst (Telemetry.nearestNeighbor square hintedVertex query) == Just seed
, sessionInsertedVertex = disposition == Inserted
, sessionRemovedVertex = maybe False (const True) removed
, finalMeshValid = null (validateTriangulation restored)
}
assertExpected :: PublicComponentSummary -> IO ()
assertExpected actual
| actual == expected = putStrLn "public components: ok"
| otherwise =
die
( "public component summary mismatch; expected "
<> show expected
<> ", got "
<> show actual
)
where
expected =
PublicComponentSummary
{ residentVertices = 4
, owningVertexHandles = 4
, scopedVertexPoints = 4
, rectangleVertices = 4
, locatedResidentVertex = True
, admittedPredicateCorrect = True
, hierarchyTelemetryNearestCorrect = True
, sessionInsertedVertex = True
, sessionRemovedVertex = True
, finalMeshValid = True
}