moonlight-planar-1.1.0.0: src-dcel/Moonlight/Planar/Curve/Region.hs
{-# LANGUAGE DerivingStrategies #-}
-- | Explicit simple outer/hole assembly of approximated curves. Admission
-- proves polygon topology only, never topology equivalence with the curves.
module Moonlight.Planar.Curve.Region
( CurveComponent (..)
, CurveRegionError (..)
, lowerSimpleRegion
) where
import Data.Bifunctor (first)
import Moonlight.Planar.Curve (ClosedTrail, Located)
import Moonlight.Planar.Curve.Lowering
( LoweringPolicy, LoweringError, LoweredPath, lowerClosedTrail, loweredPoints )
import Moonlight.Planar.Region
( PlanarRegion, RegionValidationError, exactLoop, polygonComponent, planarRegion )
-- | The submitted winding is significant: outer CCW, holes CW. An explicitly
-- located closed trail does not by itself claim simplicity or containment.
data CurveComponent = CurveComponent
{ curveOuter :: !(Located ClosedTrail)
, curveHoles :: ![Located ClosedTrail]
}
data CurveRegionError
= CurveLoweringRefused !LoweringError
| CurvePolygonRefused !RegionValidationError
deriving stock (Eq, Show)
-- | The lowering policy's leaf budget applies per contour. Receipts remain in
-- submitted component/outer/hole order, independently of polygon canonicalization.
-- Coordinates remain source-local; the policy's affine map only measures error.
lowerSimpleRegion
:: LoweringPolicy
-> [CurveComponent]
-> Either CurveRegionError (PlanarRegion, [LoweredPath])
lowerSimpleRegion policy components = do
admitted <- traverse lowerComponent components
region <- first CurvePolygonRefused (planarRegion (map fst admitted))
pure (region, concatMap snd admitted)
where
lowerComponent (CurveComponent outer holes) = do
outerPath <- first CurveLoweringRefused (lowerClosedTrail policy outer)
holePaths <- traverse (first CurveLoweringRefused . lowerClosedTrail policy) holes
outerLoop <- first CurvePolygonRefused (exactLoop (loweredPoints outerPath))
holeLoops <- traverse (first CurvePolygonRefused . exactLoop . loweredPoints) holePaths
component <- first CurvePolygonRefused (polygonComponent outerLoop holeLoops)
pure (component, outerPath : holePaths)