pure-cdt-0.2.0.0: src/Triangulation/Check.hs
-- | Validity checks on sets of triangles. Quadratic; meant for tests and debugging.
module Triangulation.Check (
isDelaunay,
isLocallyDelaunay,
hasNoIntersections,
) where
import Data.HashSet qualified as HS
import Data.Maybe (isNothing)
import Triangulation.Geometry.Edge (Edge (..), intersection)
import Triangulation.Geometry.Triangle (Triangle (..), isOutsideCircumcircle, triangleEdges)
import Triangulation.Store qualified as Store
-- | No vertex lies strictly inside the circumcircle of any triangle: the
-- (unconstrained) Delaunay property.
isDelaunay :: [Triangle] -> Bool
isDelaunay ts =
let points = HS.toList . HS.fromList $ concatMap (\(Triangle p1 p2 p3) -> [p1, p2, p3]) ts
others (Triangle p1 p2 p3) = filter (`notElem` [p1, p2, p3]) points
in all (\t -> all (`isOutsideCircumcircle` t) (others t)) ts
-- | Every edge shared by two triangles is locally Delaunay: the apex of each
-- triangle lies outside (or on) the circumcircle of the other. Together with
-- the constraint that segments are never flipped this characterises a
-- constrained Delaunay triangulation.
isLocallyDelaunay :: [Triangle] -> Bool
isLocallyDelaunay ts = all locallyDelaunay (Store.edges store)
where
store = foldr Store.insert Store.empty ts
locallyDelaunay e = case Store.trianglesOn e store of
[t1, t2] -> isOutsideCircumcircle (apex e t2) t1 && isOutsideCircumcircle (apex e t1) t2
_ -> True
apex (Edge a b) (Triangle x y z) = case filter (`notElem` [a, b]) [x, y, z] of
p : _ -> p
[] -> x
-- | No two edges cross.
hasNoIntersections :: [Triangle] -> Bool
hasNoIntersections = go
where
go [] = True
go (t : ts) = all (notCrossing t) ts && go ts
notCrossing t1 t2 = and [isNothing (intersection e1 e2) | e1 <- triangleEdges t1, e2 <- triangleEdges t2]