{-# LANGUAGE FlexibleContexts       #-}
{-# LANGUAGE FlexibleInstances      #-}
{-# LANGUAGE FunctionalDependencies #-}
{-# LANGUAGE MultiParamTypeClasses  #-}
-----------------------------------------------------------------------------
-- |
-- Module      :  Diagrams.Query
-- Copyright   :  (c) 2013 diagrams-lib team (see LICENSE)
-- License     :  BSD-style (see LICENSE)
-- Maintainer  :  diagrams-discuss@googlegroups.com
--
-- A query is a function that maps points in a vector space to values
-- in some monoid. Queries naturally form a monoid, with two queries
-- being combined pointwise.
--
-----------------------------------------------------------------------------

module Diagrams.Query
  ( -- * Queries
    Query(..)
  , HasQuery (..)
  , sample
  , inquire

    -- ** Queries on diagrams
  , query
  , value
  , resetValue
  , clearValue
  ) where

import           Data.Monoid

import           Diagrams.Core

-- | Types which can answer a 'Query' about points inside the geometric
--   object.
--
--   If @t@ and @m@ are both a 'Semigroup's, 'getQuery' should satisfy
--
-- @
-- 'getQuery' (t1 <> t2) = 'getQuery' t1 <> 'getQuery' t2
-- @
class HasQuery t m | t -> m where
  -- | Extract the query of an object.
  getQuery :: t -> Query (V t) (N t) m

instance HasQuery (Query v n m) m where
  getQuery :: Query v n m -> Query (V (Query v n m)) (N (Query v n m)) m
getQuery = Query v n m -> Query (V (Query v n m)) (N (Query v n m)) m
forall a. a -> a
id

instance Monoid m => HasQuery (QDiagram b v n m) m where
  getQuery :: QDiagram b v n m
-> Query (V (QDiagram b v n m)) (N (QDiagram b v n m)) m
getQuery = QDiagram b v n m
-> Query (V (QDiagram b v n m)) (N (QDiagram b v n m)) m
forall m b (v :: * -> *) n.
Monoid m =>
QDiagram b v n m -> Query v n m
query

-- | Test if a point is not equal to 'mempty'.
--
-- @
-- 'inquire' :: 'QDiagram' b v n 'Any' -> 'Point' v n -> 'Bool'
-- 'inquire' :: 'Query' v n 'Any'      -> 'Point' v n -> 'Bool'
-- 'inquire' :: 'Diagrams.BoundingBox.BoundingBox' v n  -> 'Point' v n -> 'Bool'
-- @
inquire :: HasQuery t Any => t -> Point (V t) (N t) -> Bool
inquire :: t -> Point (V t) (N t) -> Bool
inquire t
t = Any -> Bool
getAny (Any -> Bool)
-> (Point (V t) (N t) -> Any) -> Point (V t) (N t) -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. t -> Point (V t) (N t) -> Any
forall t m. HasQuery t m => t -> Point (V t) (N t) -> m
sample t
t

-- | Sample a diagram's query function at a given point.
--
-- @
-- 'sample' :: 'QDiagram' b v n m -> 'Point' v n -> m
-- 'sample' :: 'Query' v n m      -> 'Point' v n -> m
-- 'sample' :: 'Diagrams.BoundingBox.BoundingBox' v n  -> 'Point' v n -> 'Any'
-- 'sample' :: 'Diagrams.Path.Path' 'V2' 'Double'   -> 'Point' v n -> 'Diagrams.TwoD.Path.Crossings'
-- @
sample :: HasQuery t m => t -> Point (V t) (N t) -> m
sample :: t -> Point (V t) (N t) -> m
sample = Query (V t) (N t) m -> Point (V t) (N t) -> m
forall (v :: * -> *) n m. Query v n m -> Point v n -> m
runQuery (Query (V t) (N t) m -> Point (V t) (N t) -> m)
-> (t -> Query (V t) (N t) m) -> t -> Point (V t) (N t) -> m
forall b c a. (b -> c) -> (a -> b) -> a -> c
. t -> Query (V t) (N t) m
forall t m. HasQuery t m => t -> Query (V t) (N t) m
getQuery

-- | Set the query value for 'True' points in a diagram (/i.e./ points
--   \"inquire\" the diagram); 'False' points will be set to 'mempty'.
value :: Monoid m => m -> QDiagram b v n Any -> QDiagram b v n m
value :: m -> QDiagram b v n Any -> QDiagram b v n m
value m
m = (Any -> m) -> QDiagram b v n Any -> QDiagram b v n m
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap Any -> m
fromAny
  where fromAny :: Any -> m
fromAny (Any Bool
True)  = m
m
        fromAny (Any Bool
False) = m
forall a. Monoid a => a
mempty

-- | Reset the query values of a diagram to @True@/@False@: any values
--   equal to 'mempty' are set to 'False'; any other values are set to
--   'True'.
resetValue :: (Eq m, Monoid m) => QDiagram b v n m -> QDiagram b v n Any
resetValue :: QDiagram b v n m -> QDiagram b v n Any
resetValue = (m -> Any) -> QDiagram b v n m -> QDiagram b v n Any
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap m -> Any
forall a. (Eq a, Monoid a) => a -> Any
toAny
  where toAny :: a -> Any
toAny a
m | a
m a -> a -> Bool
forall a. Eq a => a -> a -> Bool
== a
forall a. Monoid a => a
mempty = Bool -> Any
Any Bool
False
                | Bool
otherwise   = Bool -> Any
Any Bool
True

-- | Set all the query values of a diagram to 'False'.
clearValue :: QDiagram b v n m -> QDiagram b v n Any
clearValue :: QDiagram b v n m -> QDiagram b v n Any
clearValue = (m -> Any) -> QDiagram b v n m -> QDiagram b v n Any
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (Any -> m -> Any
forall a b. a -> b -> a
const (Bool -> Any
Any Bool
False))