{-# LANGUAGE OverloadedStrings #-}
module Data.Geometry.Ipe.FromIpe(
_asPoint
, _asLineSegment
, _asRectangle
, _asTriangle
, _asPolyLine
, _asSomePolygon, _asSimplePolygon, _asMultiPolygon
, _withAttrs
, HasDefaultFromIpe(..)
, readAll, readAllFrom
) where
import Control.Lens hiding (Simple)
import Data.Ext
import Data.Geometry.Ball
import Data.Geometry.Box
import Data.Geometry.Ellipse (Ellipse, _EllipseCircle)
import Data.Geometry.Ipe.Path
import Data.Geometry.Ipe.Reader
import Data.Geometry.Ipe.Types
import Data.Geometry.LineSegment
import Data.Geometry.Point
import qualified Data.Geometry.PolyLine as PolyLine
import Data.Geometry.Polygon
import Data.Geometry.Properties
import Data.Geometry.Triangle
import qualified Data.LSeq as LSeq
import Data.List.NonEmpty (NonEmpty(..))
_asPoint :: Prism' (IpeSymbol r) (Point 2 r)
_asPoint = prism' (flip Symbol "mark/disk(sx)") (Just . view symbolPoint)
_asLineSegment :: Prism' (Path r) (LineSegment 2 () r)
_asLineSegment = prism' seg2path path2seg
where
seg2path = review _asPolyLine . PolyLine.fromLineSegment
path2seg p = PolyLine.asLineSegment' =<< preview _asPolyLine p
_asPolyLine :: Prism' (Path r) (PolyLine.PolyLine 2 () r)
_asPolyLine = prism' poly2path path2poly
where
poly2path = Path . fromSingleton . PolyLineSegment
path2poly = preview (pathSegments.traverse._PolyLineSegment)
_asSimplePolygon :: Prism' (Path r) (Polygon Simple () r)
_asSimplePolygon = _asSomePolygon._Left
_asRectangle :: forall r. (Num r, Ord r) => Prism' (Path r) (Rectangle () r)
_asRectangle = prism' rectToPath pathToRect
where
rectToPath (corners -> Corners a b c d) = review _asSimplePolygon . fromPoints $ [a,b,c,d]
pathToRect p = p^?_asSimplePolygon >>= asRect
asRect :: SimplePolygon () r -> Maybe (Rectangle () r)
asRect pg = case pg^..outerBoundary.traverse of
[a,b,c,d] | isH a b && isV b c && isH c d && isV d a -> Just (boundingBoxList' [a,c])
[a,b,c,d] | isV a b && isH b c && isV c d && isH d a -> Just (boundingBoxList' [a,c])
_ -> Nothing
isH (p :+ _) (q :+ _) = p^.xCoord == q^.xCoord
isV (p :+ _) (q :+ _) = p^.yCoord == q^.yCoord
_asTriangle :: Prism' (Path r) (Triangle 2 () r)
_asTriangle = prism' triToPath path2tri
where
triToPath (Triangle p q r) = polygonToPath . fromPoints . map (&extra .~ ()) $ [p,q,r]
path2tri p = case p^..pathSegments.traverse._PolygonPath of
[] -> Nothing
[pg] -> case polygonVertices pg of
(a :| [b,c]) -> Just $ Triangle a b c
_ -> Nothing
_ -> Nothing
_asEllipse :: Prism' (Path r) (Ellipse r)
_asEllipse = prism' toPath toEllipse
where
toPath = Path . fromSingleton . EllipseSegment
toEllipse p = case p^..pathSegments.traverse._EllipseSegment of
[e] -> Just e
_ -> Nothing
_asCircle :: (Floating r, Eq r) => Prism' (Path r) (Circle () r)
_asCircle = _asEllipse._EllipseCircle
_asDisk :: (Floating r, Eq r) => Prism' (Path r) (Disk () r)
_asDisk = _asCircle.from _DiskCircle
_asMultiPolygon :: Prism' (Path r) (MultiPolygon () r)
_asMultiPolygon = _asSomePolygon._Right
_asSomePolygon :: Prism' (Path r) (SomePolygon () r)
_asSomePolygon = prism' embed pathToPolygon
where
embed = either polygonToPath polygonToPath
polygonToPath :: Polygon t () r -> Path r
polygonToPath pg@(SimplePolygon _) = Path . fromSingleton . PolygonPath $ pg
polygonToPath (MultiPolygon vs hs) = Path . LSeq.fromNonEmpty . fmap PolygonPath
$ (SimplePolygon vs) :| hs
pathToPolygon :: Path r -> Maybe (Either (SimplePolygon () r) (MultiPolygon () r))
pathToPolygon p = case p^..pathSegments.traverse._PolygonPath of
[] -> Nothing
[pg] -> Just . Left $ pg
SimplePolygon vs: hs -> Just . Right $ MultiPolygon vs hs
_withAttrs :: Prism' (IpeObject r) (i r :+ IpeAttributes i r) -> Prism' (i r) g
-> Prism' (IpeObject r) (g :+ IpeAttributes i r)
_withAttrs po pg = prism' g2o o2g
where
g2o = review po . over core (review pg)
o2g o = preview po o >>= \(i :+ ats) -> (:+ ats) <$> preview pg i
class HasDefaultFromIpe g where
type DefaultFromIpe g :: * -> *
defaultFromIpe :: (r ~ NumType g)
=> Prism' (IpeObject r) (g :+ IpeAttributes (DefaultFromIpe g) r)
instance HasDefaultFromIpe (Point 2 r) where
type DefaultFromIpe (Point 2 r) = IpeSymbol
defaultFromIpe = _withAttrs _IpeUse _asPoint
where
instance HasDefaultFromIpe (LineSegment 2 () r) where
type DefaultFromIpe (LineSegment 2 () r) = Path
defaultFromIpe = _withAttrs _IpePath _asLineSegment
instance HasDefaultFromIpe (Ellipse r) where
type DefaultFromIpe (Ellipse r) = Path
defaultFromIpe = _withAttrs _IpePath _asEllipse
instance (Floating r, Eq r) => HasDefaultFromIpe (Circle () r) where
type DefaultFromIpe (Circle () r) = Path
defaultFromIpe = _withAttrs _IpePath _asCircle
instance (Floating r, Eq r) => HasDefaultFromIpe (Disk () r) where
type DefaultFromIpe (Disk () r) = Path
defaultFromIpe = _withAttrs _IpePath _asDisk
instance HasDefaultFromIpe (PolyLine.PolyLine 2 () r) where
type DefaultFromIpe (PolyLine.PolyLine 2 () r) = Path
defaultFromIpe = _withAttrs _IpePath _asPolyLine
instance HasDefaultFromIpe (SimplePolygon () r) where
type DefaultFromIpe (SimplePolygon () r) = Path
defaultFromIpe = _withAttrs _IpePath _asSimplePolygon
instance HasDefaultFromIpe (MultiPolygon () r) where
type DefaultFromIpe (MultiPolygon () r) = Path
defaultFromIpe = _withAttrs _IpePath _asMultiPolygon
readAll :: (HasDefaultFromIpe g, r ~ NumType g)
=> IpePage r -> [g :+ IpeAttributes (DefaultFromIpe g) r]
readAll p = p^..content.traverse.defaultFromIpe
readAllFrom :: (HasDefaultFromIpe g, r ~ NumType g, Coordinate r, Eq r)
=> FilePath -> IO [g :+ IpeAttributes (DefaultFromIpe g) r]
readAllFrom fp = foldMap readAll <$> readSinglePageFile fp
fromSingleton :: a -> LSeq.LSeq 1 a
fromSingleton = LSeq.fromNonEmpty . (:| [])