hgeometry-0.14: src/Data/Geometry/Polygon/Bezier.hs
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE ScopedTypeVariables #-}
module Data.Geometry.Polygon.Bezier
( PathJoin(..)
, fromBeziers
, approximate
, approximateSome
) where
import Control.Lens
import Data.Ext
import Data.Geometry.BezierSpline (BezierSpline, pattern Bezier3)
import qualified Data.Geometry.BezierSpline as Bezier
import Data.Geometry.Point
import Data.Geometry.PolyLine(points)
import Data.Geometry.Polygon
import qualified Data.Vector.Circular as CV
import qualified Data.Foldable as F
data PathJoin r
= JoinLine
| JoinCurve (Point 2 r) (Point 2 r)
deriving (Show, Eq, Ord)
-- | Construct a polygon from a closed set of bezier curves. Each curve must be connected to
-- its neighbours.
fromBeziers :: (Eq r, Num r) => [BezierSpline 3 2 r] -> SimplePolygon (PathJoin r) r
fromBeziers curves
| isCounterClockwise expanded = p
| otherwise = p'
where
p = unsafeFromPoints
[ a :+ JoinCurve b c
| Bezier3 a b c _d <- curves ]
p' = unsafeFromPoints
[ d :+ JoinCurve c b
| Bezier3 _a b c d <- reverse curves ]
expanded = unsafeFromPoints $ concat
[ map ext [a, b, c]
| Bezier3 a b c _d <- curves ]
approximate :: forall t r. (Ord r, Fractional r) => r -> Polygon t (PathJoin r) r -> Polygon t () r
approximate eps p =
case p of
SimplePolygon{} ->
let vs = p^.outerBoundaryVector
in unsafeFromCircularVector $ CV.concatMap f $ CV.zip vs (CV.rotateRight 1 vs)
MultiPolygon v hs -> MultiPolygon (approximate eps v) (map (approximate eps) hs)
where
f :: (Point 2 r :+ PathJoin r, Point 2 r :+ PathJoin r) -> CV.CircularVector (Point 2 r :+ ())
f (a :+ JoinLine, _) = CV.singleton (ext a)
f (a :+ JoinCurve b c, d :+ _) = let poly = Bezier.approximate eps (Bezier3 a b c d)
in CV.unsafeFromList . init . F.toList $ poly^.points
approximateSome :: (Ord r, Fractional r) => r -> SomePolygon (PathJoin r) r -> SomePolygon () r
approximateSome eps (Left p) = Left $ approximate eps p
approximateSome eps (Right p) = Right $ approximate eps p