{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE UndecidableInstances #-}
module Diagrams.Parametric.Adjust
( adjust
, AdjustOpts(_adjMethod, _adjSide, _adjEps)
, adjMethod, adjSide, adjEps
, AdjustMethod(..), AdjustSide(..)
) where
import Control.Lens (Lens', generateSignatures, lensRules, makeLensesWith, (&),
(.~), (^.))
import Data.Proxy
import Data.Default.Class
import Diagrams.Core.V
import Diagrams.Parametric
data AdjustMethod n = ByParam n
| ByAbsolute n
| ToAbsolute n
data AdjustSide = Start
| End
| Both
deriving (Show, Read, Eq, Ord, Bounded, Enum)
data AdjustOpts n = AO { _adjMethod :: AdjustMethod n
, _adjSide :: AdjustSide
, _adjEps :: n
, adjOptsvProxy :: Proxy n
}
makeLensesWith (lensRules & generateSignatures .~ False) ''AdjustOpts
-- | Which method should be used for adjusting?
adjMethod :: Lens' (AdjustOpts n) (AdjustMethod n)
-- | Which end(s) of the object should be adjusted?
adjSide :: Lens' (AdjustOpts n) AdjustSide
-- | Tolerance to use when doing adjustment.
adjEps :: Lens' (AdjustOpts n) n
instance Fractional n => Default (AdjustMethod n) where
def = ByParam 0.2
instance Default AdjustSide where
def = Both
instance Fractional n => Default (AdjustOpts n) where
def = AO { _adjMethod = def
, _adjSide = def
, _adjEps = stdTolerance
, adjOptsvProxy = Proxy
}
-- | Adjust the length of a parametric object such as a segment or
-- trail. The second parameter is an option record which controls how
-- the adjustment should be performed; see 'AdjustOpts'.
adjust :: (N t ~ n, Sectionable t, HasArcLength t, Fractional n)
=> t -> AdjustOpts n -> t
adjust s opts = section s
(if opts^.adjSide == End then domainLower s else getParam s)
(if opts^.adjSide == Start then domainUpper s else domainUpper s - getParam (reverseDomain s))
where
getParam seg = case opts^.adjMethod of
ByParam p -> -p * bothCoef
ByAbsolute len -> param (-len * bothCoef)
ToAbsolute len -> param (absDelta len * bothCoef)
where
param = arcLengthToParam eps seg
absDelta len = arcLength eps s - len
bothCoef = if opts^.adjSide == Both then 0.5 else 1
eps = opts^.adjEps