diagrams-lib 1.0.1 → 1.1
raw patch · 45 files changed
+1479/−485 lines, 45 filesdep −NumInstancesdep ~arraydep ~diagrams-coredep ~lens
Dependencies removed: NumInstances
Dependency ranges changed: array, diagrams-core, lens
Files
- CHANGES.markdown +69/−0
- LICENSE +2/−1
- diagrams-lib.cabal +16/−11
- src/Diagrams/Align.hs +22/−8
- src/Diagrams/Attributes.hs +41/−18
- src/Diagrams/Attributes/Compile.hs +122/−0
- src/Diagrams/Combinators.hs +24/−10
- src/Diagrams/CubicSpline.hs +0/−3
- src/Diagrams/Deform.hs +117/−0
- src/Diagrams/Path.hs +15/−9
- src/Diagrams/Prelude.hs +5/−0
- src/Diagrams/Prelude/ThreeD.hs +150/−0
- src/Diagrams/Segment.hs +46/−11
- src/Diagrams/ThreeD.hs +10/−9
- src/Diagrams/ThreeD/Align.hs +209/−0
- src/Diagrams/ThreeD/Camera.hs +27/−22
- src/Diagrams/ThreeD/Deform.hs +51/−0
- src/Diagrams/ThreeD/Light.hs +12/−8
- src/Diagrams/ThreeD/Shapes.hs +13/−16
- src/Diagrams/ThreeD/Transform.hs +123/−18
- src/Diagrams/ThreeD/Types.hs +19/−28
- src/Diagrams/ThreeD/Vector.hs +9/−10
- src/Diagrams/Trail.hs +6/−6
- src/Diagrams/TrailLike.hs +2/−2
- src/Diagrams/Transform.hs +1/−1
- src/Diagrams/TwoD.hs +13/−7
- src/Diagrams/TwoD/Align.hs +2/−2
- src/Diagrams/TwoD/Arc.hs +38/−37
- src/Diagrams/TwoD/Arrow.hs +48/−23
- src/Diagrams/TwoD/Arrowheads.hs +28/−27
- src/Diagrams/TwoD/Combinators.hs +3/−3
- src/Diagrams/TwoD/Curvature.hs +1/−2
- src/Diagrams/TwoD/Deform.hs +36/−0
- src/Diagrams/TwoD/Ellipse.hs +3/−1
- src/Diagrams/TwoD/Image.hs +13/−10
- src/Diagrams/TwoD/Offset.hs +27/−16
- src/Diagrams/TwoD/Path.hs +35/−10
- src/Diagrams/TwoD/Polygons.hs +18/−14
- src/Diagrams/TwoD/Segment.hs +8/−7
- src/Diagrams/TwoD/Shapes.hs +3/−3
- src/Diagrams/TwoD/Text.hs +1/−0
- src/Diagrams/TwoD/Transform.hs +23/−21
- src/Diagrams/TwoD/Transform/ScaleInv.hs +7/−5
- src/Diagrams/TwoD/Types.hs +51/−98
- src/Diagrams/TwoD/Vector.hs +10/−8
CHANGES.markdown view
@@ -1,3 +1,72 @@+1.1 (8 March 2014)+------------------++* **New features**++ - Support for `Deformation`s, arbitrary (non-affine)+ transformations on objects such as points, paths, and located+ trails (though not on diagrams).++ - New functions `clipTo`, which clips a diagram's envelope and+ trace along with its visual representation, and `clipped`, which+ clips the diagram's visual representation but replaces its+ envelope and trace with those of the clipping path.++ - New `arrowV` function, for creating an arrow with the direction+ and magnitude of a given vector.++ - `gap` traversal, for setting the head and tail gaps of an arrow+ simultaneously.++ - Generalized types for `centerXY` and `snugXY`, based on new+ `basis` function from `diagrams-core++ - New 3D `Transform`s, alignment, and 3D-specific `Prelude`.++ - New `frame` function similar to `pad`, but increases the envelope+ of a diagram by an amount specified in local units in every direction+ irrespective of the local origin.++ - New `splitFills` function for pushing fill attributes down to+ subtrees containing only loops (mostly of relevance only to+ backend implementors).++* **New instances**++ - `Typeable` instances for all data types that are used as diagram+ primitives.+ - `Sectionable` instance for `FixedSegment`.++* **API changes**++ - `Angle` is now a type, rather than a class. It uses a single+ internal representation for angles, and lenses `turn`, `rad,`+ and `deg` are supplied for constructing (using `@@`) and viewing+ (using `^.`) `Angle`s in various units. In addition, the `Num`+ instance for `Angle` has been removed, eliminating a class of+ errors where a bare number is interpreted in units other than+ what you expect.++ - Removed `Num` instance for angles.++* **Dependency/version changes**++ - Require `lens >= 4.0`.+ - Allow `array-0.5`.+ - Allow `hashable-1.1`.+ - Remove `NumInstances` dependency.++* **Bug fixes**++ - Exclude joins in offsets on close segments (#160).+ - Exclude extra segment when joining loops in offset (#155).++* **Performance improvements**++ - `colorToSRGBA` function now avoids expensive matrix operations,+ offering dramatic speedups in rendering diagrams with many color+ attributes.+ 1.0.1 (26 January 2014) -----------------------
LICENSE view
@@ -1,7 +1,8 @@-Copyright (c) 2011-2013 diagrams-lib team:+Copyright (c) 2011-2014 diagrams-lib team: Jan Bracker <jan.bracker@googlemail.com> Daniel Bergey <bergey@alum.mit.edu>+ Denys Duchier <denys.duchier@univ-orleans.fr> Daniil Frumin <difrumin@gmail.com> Niklas Haas <nand@lavabit.com> Peter Hall <peter.hall@memorphic.com>
diagrams-lib.cabal view
@@ -1,5 +1,5 @@ Name: diagrams-lib-Version: 1.0.1+Version: 1.1 Synopsis: Embedded domain-specific language for declarative graphics Description: Diagrams is a flexible, extensible EDSL for creating graphics of many types. Graphics can be created@@ -19,17 +19,19 @@ Cabal-version: >=1.10 Extra-source-files: CHANGES.markdown, README.markdown, diagrams/*.svg Extra-doc-files: diagrams/*.svg-Tested-with: GHC == 7.4.2, GHC == 7.6.1+Tested-with: GHC == 7.4.2, GHC == 7.6.3, GHC == 7.8.1 Source-repository head type: git location: http://github.com/diagrams/diagrams-lib.git Library Exposed-modules: Diagrams.Prelude,+ Diagrams.Prelude.ThreeD, Diagrams.Align, Diagrams.Combinators, Diagrams.Coordinates, Diagrams.Attributes,+ Diagrams.Attributes.Compile, Diagrams.Points, Diagrams.Located, Diagrams.Parametric,@@ -43,6 +45,7 @@ Diagrams.Solve, Diagrams.Tangent, Diagrams.Transform,+ Diagrams.Deform Diagrams.BoundingBox, Diagrams.Names, Diagrams.Envelope,@@ -54,6 +57,7 @@ Diagrams.TwoD.Arrow, Diagrams.TwoD.Arrowheads, Diagrams.TwoD.Combinators,+ Diagrams.TwoD.Deform, Diagrams.TwoD.Transform, Diagrams.TwoD.Transform.ScaleInv, Diagrams.TwoD.Ellipse,@@ -70,13 +74,15 @@ Diagrams.TwoD.Text, Diagrams.TwoD.Image, Diagrams.TwoD.Adjust,- Diagrams.ThreeD.Types,- Diagrams.ThreeD.Shapes,- Diagrams.ThreeD.Vector,- Diagrams.ThreeD.Transform,+ Diagrams.ThreeD.Align, Diagrams.ThreeD.Camera,+ Diagrams.ThreeD.Deform, Diagrams.ThreeD.Light,- Diagrams.ThreeD+ Diagrams.ThreeD.Shapes,+ Diagrams.ThreeD.Transform,+ Diagrams.ThreeD.Types,+ Diagrams.ThreeD.Vector,+ Diagrams.ThreeD, Diagrams.Animation, Diagrams.Animation.Active, Diagrams.Util,@@ -84,21 +90,20 @@ Diagrams.Backend.CmdLine Build-depends: base >= 4.2 && < 4.8, containers >= 0.3 && < 0.6,- array >= 0.3 && < 0.5,+ array >= 0.3 && < 0.6, semigroups >= 0.3.4 && < 0.13, monoid-extras >= 0.3 && < 0.4,- diagrams-core >= 1.0 && < 1.1,+ diagrams-core >= 1.1 && < 1.2, active >= 0.1 && < 0.2, vector-space >= 0.7.7 && < 0.9, vector-space-points >= 0.1.2 && < 0.2, MemoTrie >= 0.6 && < 0.7,- NumInstances >= 1.2 && < 1.4, colour >= 2.3.2 && < 2.4, data-default-class < 0.1, pretty >= 1.0.1.2 && < 1.2, fingertree >= 0.1 && < 0.2, intervals >= 0.3 && < 0.5,- lens >= 3.9 && < 3.11,+ lens >= 4.0 && < 4.1, tagged >= 0.7, optparse-applicative >= 0.7 && < 0.8, filepath,
src/Diagrams/Align.hs view
@@ -29,13 +29,14 @@ , align , snug- , center+ , centerV, center , snugBy- , snugCenter+ , snugCenterV, snugCenter ) where import Diagrams.Core+import Diagrams.Util (applyAll) import Data.AffineSpace (alerp, (.-.)) import Data.VectorSpace@@ -140,15 +141,28 @@ => V a -> a -> a snug v = snugBy v 1 --- | @center v@ centers an enveloped object along the direction of+-- | @centerV v@ centers an enveloped object along the direction of -- @v@.-center :: ( Alignable a, HasOrigin a, Num (Scalar (V a))+centerV :: ( Alignable a, HasOrigin a, Num (Scalar (V a)) , Fractional (Scalar (V a))) => V a -> a -> a-center v = alignBy v 0+centerV v = alignBy v 0 --- | Like @center@ using trace.-snugCenter+-- | @center@ centers an enveloped object along all of its basis vectors.+center :: ( HasLinearMap (V a), Alignable a, HasOrigin a, Num (Scalar (V a)),+ Fractional (Scalar (V a))) => a -> a+center d = applyAll fs d+ where+ fs = map centerV basis++-- | Like @centerV@ using trace.+snugCenterV :: (Fractional (Scalar (V a)), Alignable a, Traced a, HasOrigin a) => V a -> a -> a-snugCenter v = (alignBy' traceBoundary) v 0+snugCenterV v = (alignBy' traceBoundary) v 0 +-- | Like @center@ using trace.+snugCenter :: ( HasLinearMap (V a), Alignable a, HasOrigin a, Num (Scalar (V a)),+ Fractional (Scalar (V a)), Traced a) => a -> a+snugCenter d = applyAll fs d+ where+ fs = map snugCenterV basis
src/Diagrams/Attributes.hs view
@@ -1,6 +1,8 @@ {-# LANGUAGE DeriveDataTypeable #-} {-# LANGUAGE ExistentialQuantification #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TypeFamilies #-} ----------------------------------------------------------------------------- -- | -- Module : Diagrams.Attributes@@ -37,7 +39,7 @@ , Opacity, getOpacity, opacity -- ** Converting colors- , toRGBAUsingSpace, colorToSRGBA, colorToRGBA+ , colorToSRGBA, colorToRGBA -- * Lines -- ** Width@@ -55,20 +57,28 @@ -- ** Dashing , Dashing(..), DashingA, getDashing, dashing + -- * Compilation utilities+ , splitFills+ ) where -import Control.Lens (Setter, sets)+import Control.Lens (Setter, sets) import Data.Colour-import Data.Colour.RGBSpace-import Data.Colour.SRGB (sRGBSpace)+import Data.Colour.RGBSpace (RGB (..))+import Data.Colour.SRGB (toSRGB) import Data.Default.Class-import Data.Maybe (fromMaybe)+import Data.Maybe (fromMaybe) import Data.Monoid.Recommend import Data.Semigroup import Data.Typeable +import Diagrams.Attributes.Compile import Diagrams.Core-import Diagrams.Core.Style (setAttr)+import Diagrams.Core.Style (setAttr)+import Diagrams.Core.Types (RTree)+import Diagrams.Located (unLoc)+import Diagrams.Path (Path, pathTrails)+import Diagrams.Trail (isLoop) ------------------------------------------------------------ -- Color -------------------------------------------------@@ -226,20 +236,14 @@ toAlphaColour (FillColor c) = toAlphaColour . getLast . getRecommend $ c fromAlphaColour = FillColor . Commit . Last . fromAlphaColour --- | Convert to an RGB space while preserving the alpha channel.-toRGBAUsingSpace :: Color c => RGBSpace Double -> c- -> (Double, Double, Double, Double)-toRGBAUsingSpace s col = (r,g,b,a)- where c' = toAlphaColour col- c = toRGBUsingSpace s (alphaToColour c')- a = alphaChannel c'- r = channelRed c- g = channelGreen c- b = channelBlue c- -- | Convert to sRGBA. colorToSRGBA, colorToRGBA :: Color c => c -> (Double, Double, Double, Double)-colorToSRGBA = toRGBAUsingSpace sRGBSpace+colorToSRGBA col = (r, g, b, a)+ where+ c' = toAlphaColour col+ c = alphaToColour c'+ a = alphaChannel c'+ RGB r g b = toSRGB c colorToRGBA = colorToSRGBA {-# DEPRECATED colorToRGBA "Renamed to colorToSRGBA." #-}@@ -396,3 +400,22 @@ -- stroke should start. -> a -> a dashing ds offs = applyAttr (DashingA (Last (Dashing ds offs)))++------------------------------------------------------------++data FillLoops v = FillLoops++instance Typeable v => SplitAttribute (FillLoops v) where+ type AttrType (FillLoops v) = FillColor+ type PrimType (FillLoops v) = Path v++ primOK _ = all (isLoop . unLoc) . pathTrails++-- | Push fill attributes down until they are at the root of subtrees+-- containing only loops. This makes life much easier for backends,+-- which typically have a semantics where fill attributes are+-- applied to lines/non-closed paths as well as loops/closed paths,+-- whereas in the semantics of diagrams, fill attributes only apply+-- to loops.+splitFills :: forall b v a. Typeable v => RTree b v a -> RTree b v a+splitFills = splitAttr (FillLoops :: FillLoops v)
+ src/Diagrams/Attributes/Compile.hs view
@@ -0,0 +1,122 @@+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE GADTs #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TypeFamilies #-}+-----------------------------------------------------------------------------+-- |+-- Module : Diagrams.Attributes.Compile+-- Copyright : (c) 2014 diagrams-lib team (see LICENSE)+-- License : BSD-style (see LICENSE)+-- Maintainer : diagrams-discuss@googlegroups.com+--+-- XXX+--+-----------------------------------------------------------------------------++module Diagrams.Attributes.Compile (+ SplitAttribute(..), splitAttr+ ) where++import Data.Typeable++import Control.Arrow (second)+import Control.Lens ((%~), (&), _Wrapping')+import qualified Data.Map as M+import Data.Semigroup ((<>))+import Data.Tree (Tree (..))++import Diagrams.Core+import Diagrams.Core.Style (Style (..), attrToStyle)+import Diagrams.Core.Types (RNode (..), RTree)++------------------------------------------------------------++-- This is a sort of roundabout, overly-general way to define+-- splitFills; it's done this way to facilitate testing.++class (AttributeClass (AttrType code), Typeable (PrimType code)) => SplitAttribute code where+ type AttrType code :: *+ type PrimType code :: *++ primOK :: code -> PrimType code -> Bool++-- | Push certain attributes down until they are at the roots of trees+-- containing only "safe" nodes. In particular this is used to push+-- fill attributes down until they are over only loops; see+-- 'splitFills'.+splitAttr :: forall code b v a. SplitAttribute code => code -> RTree b v a -> RTree b v a+splitAttr code = fst . splitAttr' Nothing+ where++ -- splitAttr' is where the most interesting logic happens.+ -- Mutually recursive with splitAttr'Forest. rebuildNode and+ -- applyMfc are helper functions.+ --+ -- Input: attribute to apply to "safe" subtrees.+ --+ -- Output: tree with attributes pushed down appropriately, and+ -- a Bool indicating whether the tree contains only "safe" prims (True) or+ -- contains some unsafe ones (False).+ splitAttr' :: Maybe (AttrType code) -> RTree b v a -> (RTree b v a, Bool)++ -- RStyle node: Check for the special attribute, and split it out of+ -- the style, combining it with the incoming attribute. Recurse and+ -- rebuild. The tricky bit is that we use some knot-tying to+ -- determine the right attribute to pass down to the subtrees based+ -- on this computed Bool: if all subtrees are safe, then we will+ -- apply the attribute at the root of this tree, and pass Nothing to+ -- all the subtrees. Otherwise, we pass the given attribute along.+ -- This works out because the attribute does not need to be+ -- pattern-matched until actually applying it at some root, so the+ -- recursion can proceed and the Bool values be computed with the+ -- actual value of the attributes nodes filled in lazily.+ splitAttr' mattr (Node (RStyle sty) cs) = (t', ok)+ where+ mattr' = mattr <> getAttr sty+ sty' = sty & _Wrapping' Style %~ M.delete ty+ ty = show . typeOf $ (undefined :: AttrType code)+ (cs', ok) = splitAttr'Forest mattr' cs+ t' | ok = rebuildNode Nothing ok (RStyle sty) cs'+ | otherwise = rebuildNode mattr ok (RStyle sty') cs'++ -- RPrim node: check whether it+ -- * is some sort of prim not under consideration: don't apply the attribute; return True+ -- * is unsafe: don't apply the attribute; return False+ -- * is safe : do apply the attribute; return True+ splitAttr' mattr (Node rp@(RPrim _ (Prim prm)) _) =+ case cast prm :: Maybe (PrimType code) of+ Nothing -> (Node rp [], True)+ Just p ->+ case primOK code p of+ True -> (rebuildNode mattr True rp [], True)+ False -> (Node rp [], False)++ -- RFrozenTr, RAnnot, REmpty cases: just recurse and rebuild. Note+ -- we assume that transformations do not affect the attributes.+ splitAttr' mattr (Node nd cs) = (t', ok)+ where+ (cs', ok) = splitAttr'Forest mattr cs+ t' = rebuildNode mattr ok nd cs'++ -- Recursively call splitAttr' on all subtrees, returning the+ -- logical AND of the Bool results returned (the whole forest is+ -- safe iff all subtrees are).+ splitAttr'Forest :: Maybe (AttrType code) -> [RTree b v a] -> ([RTree b v a], Bool)+ splitAttr'Forest mattr cs = (cs', ok)+ where+ (cs', ok) = second and . unzip . map (splitAttr' mattr) $ cs++ -- Given a fill attribute, a Bool indicating whether the given+ -- subforest contains only loops, a node, and a subforest, rebuild a+ -- tree, applying the fill attribute as appropriate (only if the+ -- Bool is true and the attribute is not Nothing).+ rebuildNode :: Maybe (AttrType code) -> Bool -> RNode b v a -> [RTree b v a] -> RTree b v a+ rebuildNode mattr ok nd cs+ | ok = applyMattr mattr (Node nd cs)+ | otherwise = Node nd cs++ -- Prepend a new fill color node if Just; the identity function if+ -- Nothing.+ applyMattr :: Maybe (AttrType code) -> RTree b v a -> RTree b v a+ applyMattr Nothing t = t+ applyMattr (Just a) t = Node (RStyle $ attrToStyle a) [t]
src/Diagrams/Combinators.hs view
@@ -20,7 +20,7 @@ withEnvelope, withTrace , phantom, strut- , pad+ , pad, frame , extrudeEnvelope, intrudeEnvelope -- * Binary operations@@ -37,8 +37,11 @@ ) where -import Control.Lens ( (&), (%~), (.~), Lens', makeLensesWith- , lensRules, lensField, generateSignatures, unwrapping)+import Data.Typeable++import Control.Lens (Lens', generateSignatures, lensField,+ lensRules, makeLensesWith, (%~), (&), (.~),+ (^.), _Wrapping) import Data.AdditiveGroup import Data.AffineSpace ((.+^)) import Data.Default.Class@@ -82,7 +85,7 @@ -- | @phantom x@ produces a \"phantom\" diagram, which has the same -- envelope and trace as @x@ but produces no output.-phantom :: (Backend b (V a), Enveloped a, Traced a, Monoid' m) => a -> QDiagram b (V a) m+phantom :: (Backend b (V a), Typeable (V a), Enveloped a, Traced a, Monoid' m) => a -> QDiagram b (V a) m phantom a = mkQD nullPrim (getEnvelope a) (getTrace a) mempty mempty -- | @pad s@ \"pads\" a diagram, expanding its envelope by a factor of@@ -97,6 +100,16 @@ => Scalar v -> QDiagram b v m -> QDiagram b v m pad s d = withEnvelope (d # scale s) d +-- | @frame s@ increases the envelope of a diagram by and absolute amount @s@,+-- s is in the local units of the diagram. This function is similar to @pad@,+-- only it takes an absolute quantity and pre-centering should not be+-- necessary.+frame :: ( Backend b v, InnerSpace v, OrderedField (Scalar v), Monoid' m)+ => Scalar v -> QDiagram b v m -> QDiagram b v m+frame s d = setEnvelope (onEnvelope t (d^.envelope)) d+ where+ t f = \x -> f x + s+ -- | @strut v@ is a diagram which produces no output, but with respect -- to alignment and envelope acts like a 1-dimensional segment -- oriented along the vector @v@, with local origin at its@@ -108,7 +121,8 @@ -- <<diagrams/src_Diagrams_Combinators_strutEx.svg#diagram=strutEx&width=300>> -- -- > strutEx = (circle 1 ||| strut unitX ||| circle 1) # centerXY # pad 1.1-strut :: ( Backend b v, InnerSpace v+strut :: ( Backend b v, Typeable v+ , InnerSpace v , OrderedField (Scalar v) , Monoid' m )@@ -156,10 +170,10 @@ :: ( Ord (Scalar v), Num (Scalar v), AdditiveGroup (Scalar v) , Floating (Scalar v), HasLinearMap v, InnerSpace v, Monoid' m ) => (Scalar v) -> v -> QDiagram b v m -> QDiagram b v m-deformEnvelope s v d = setEnvelope (getEnvelope d & unwrapping Envelope %~ deform) d+deformEnvelope s v d = setEnvelope (getEnvelope d & _Wrapping Envelope %~ deformE) d where- deform = Option . fmap deform' . getOption- deform' env v'+ deformE = Option . fmap deformE' . getOption+ deformE' env v' | dot > 0 = Max $ getMax (env v') + (dot * s) / magnitude v' | otherwise = env v' where@@ -360,9 +374,9 @@ -- default is 0. -- -- 'CatOpts' is an instance of 'Default', so 'with' may be used for--- the second argument, as in @cat' (1,2) with {sep = 2}@.+-- the second argument, as in @cat' (1,2) (with & sep .~ 2)@. ----- Note that @cat' v with {catMethod = Distrib} === mconcat@+-- Note that @cat' v (with & catMethod .~ Distrib) === mconcat@ -- (distributing with a separation of 0 is the same as -- superimposing). cat' :: ( Juxtaposable a, Monoid' a, HasOrigin a
src/Diagrams/CubicSpline.hs view
@@ -31,9 +31,6 @@ import Diagrams.Trail import Diagrams.TrailLike (TrailLike (..)) --- for e.g. the Fractional (Double, Double) instance-import Data.NumInstances.Tuple ()- import Data.AffineSpace.Point import Data.VectorSpace
+ src/Diagrams/Deform.hs view
@@ -0,0 +1,117 @@+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE GADTs #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE UndecidableInstances #-}++module Diagrams.Deform (Deformation(..), Deformable(..), asDeformation) where++import Control.Lens (under, _Unwrapped)+import Data.AffineSpace+import Data.Basis+import Data.MemoTrie+import Data.Monoid hiding ((<>))+import Data.Semigroup+import Data.VectorSpace++import Diagrams.Core+import Diagrams.Located+import Diagrams.Parametric+import Diagrams.Path+import Diagrams.Segment+import Diagrams.Trail++------------------------------------------------------------+-- Deformations++-- | @Deformations@ are a superset of the affine transformations+-- represented by the 'Transformation' type. In general they are not+-- invertable. @Deformation@s include projective transformations.+-- @Deformation@ can represent other functions from points to points+-- which are "well-behaved", in that they do not introduce small wiggles.+data Deformation v = Deformation (Point v -> Point v)++instance Semigroup (Deformation v) where+ (Deformation p1) <> (Deformation p2) = Deformation (p1 . p2)++instance Monoid (Deformation v) where+ mappend = (<>)+ mempty = Deformation id++class Deformable a where+ -- | @deform' epsilon d a@ transforms @a@ by the deformation @d@.+ -- If the type of @a@ is not closed under projection, approximate+ -- to accuracy @epsilon@.+ deform' :: Scalar (V a) -> Deformation (V a) -> a -> a++ -- | @deform d a@ transforms @a@ by the deformation @d@.+ -- If the type of @a@ is not closed under projection, @deform@+ -- should call @deform'@ with some reasonable default value of+ -- @epsilon@.+ deform :: Deformation (V a) -> a -> a++-- | @asDeformation@ converts a 'Transformation' to a 'Deformation' by+-- discarding the inverse transform. This allows reusing+-- @Transformation@s in the construction of @Deformation@s.+asDeformation+ :: ( HasTrie (Basis v), HasBasis v) => Transformation v -> Deformation v+asDeformation t = Deformation f' where+ f' = papply t++------------------------------------------------------------+-- Instances++instance Deformable (Point v) where+ deform' = const deform+ deform (Deformation l) = l++-- | Cubic curves are not closed under perspective projections.+-- Therefore @Segment@s are not an instance of Deformable. However,+-- the deformation of a @Segment@ can be approximated to arbitrary+-- precision by a series of @Segment@s. @deformSegment@ does this,+-- which allows types built from lists of @Segment@s to themselves be+-- @Deformable@.+deformSegment :: (VectorSpace v, InnerSpace v, s ~ Scalar v, Ord s, Fractional s, Floating s) =>+ s -> Deformation v -> FixedSegment v -> [FixedSegment v]+deformSegment epsilon t s+ | goodEnough epsilon t s = [approx t s]+ | otherwise = concatMap (deformSegment epsilon t) [s1, s2]+ where+ (s1, s2) = splitAtParam s 0.5++approx :: (VectorSpace v, InnerSpace v, s ~ Scalar v, Ord s, Fractional s, Floating s) =>+ Deformation v -> FixedSegment v -> FixedSegment v+approx t (FLinear p0 p1) = FLinear (deform t p0) (deform t p1)+approx t (FCubic p0 c1 c2 p1) = FCubic (f p0) (f c1) (f c2) (f p1) where+ f = deform t++goodEnough :: (VectorSpace v, InnerSpace v, s ~ Scalar v, Ord s, Fractional s, Floating s) =>+ s -> Deformation v -> FixedSegment v -> Bool+goodEnough e t s =+ all (< e) [magnitude $ deform t (s `atParam` u) .-. approx t s `atParam` u+ | u <- [0.25, 0.5, 0.75]]++instance (VectorSpace v, InnerSpace v,+ s ~ Scalar v, Ord s, Fractional s, Floating s, Show s, Show v) =>+ Deformable (Located (Trail v)) where+ deform' eps p t+ | isLine $ unLoc t = line `at` p0+ | otherwise = glueTrail line `at` p0+ where+ segs = concatMap (deformSegment eps p) $ fixTrail t+ p0 = case segs of+ (FLinear start _:_) -> start+ (FCubic start _ _ _:_) -> start+ _ -> loc t -- default in case of empty trail+ line = trailFromSegments $ map (unLoc . fromFixedSeg) segs+ deform p t = deform' (0.01 * extent) p t where+ -- estimate the "size" of the Trail' as+ -- the maximum distance to any vertex+ extent = maximum . map dist . trailVertices $ t+ dist pt = magnitude $ pt .-. loc t++instance (VectorSpace v, InnerSpace v,+ s ~ Scalar v, Ord s, Fractional s, Floating s, Show s, Show v) =>+ Deformable (Path v) where+ deform' eps p = under _Unwrapped $ map (deform' eps p)+ deform p = under _Unwrapped $ map (deform p)
src/Diagrams/Path.hs view
@@ -1,3 +1,4 @@+{-# LANGUAGE DeriveDataTypeable #-} {-# LANGUAGE DeriveFunctor #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleInstances #-}@@ -57,6 +58,8 @@ ) where +import Data.Typeable+ import Diagrams.Align import Diagrams.Core import Diagrams.Core.Points ()@@ -68,8 +71,8 @@ import Diagrams.Transform import Control.Arrow ((***))-import Control.Lens ( Wrapped(..), mapped, over, view, iso- , unwrapped, wrapped, (%~), op)+import Control.Lens (Rewrapped, Wrapped (..), iso, mapped, op,+ over, view, (%~), _Unwrapped', _Wrapped) import Data.AffineSpace import qualified Data.Foldable as F import Data.List (partition)@@ -85,11 +88,14 @@ -- and they form a monoid under /superposition/ (placing one path on -- top of another) rather than concatenation. newtype Path v = Path [Located (Trail v)]- deriving (Semigroup, Monoid)+ deriving (Semigroup, Monoid, Typeable) -instance Wrapped [Located (Trail v)] [Located (Trail v')] (Path v) (Path v')- where wrapped = iso Path $ \(Path x) -> x+instance Wrapped (Path v) where+ type Unwrapped (Path v) = [Located (Trail v)]+ _Wrapped' = iso (\(Path x) -> x) Path +instance Rewrapped (Path v) (Path v')+ -- | Extract the located trails making up a 'Path'. pathTrails :: Path v -> [Located (Trail v)] pathTrails = op Path@@ -101,7 +107,7 @@ type instance V (Path v) = v instance VectorSpace v => HasOrigin (Path v) where- moveOriginTo = over unwrapped . map . moveOriginTo+ moveOriginTo = over _Wrapped' . map . moveOriginTo --moveOriginTo = over pathTrails . map . moveOriginTo -- | Paths are trail-like; a trail can be used to construct a@@ -112,7 +118,7 @@ -- See Note [Transforming paths] instance (HasLinearMap v, InnerSpace v, OrderedField (Scalar v)) => Transformable (Path v) where- transform = over unwrapped . map . transform+ transform = over _Wrapped . map . transform {- ~~~~ Note [Transforming paths] @@ -196,7 +202,7 @@ -- the first containing all the trails for which the predicate returns -- @True@, and the second containing the remaining trails. partitionPath :: (Located (Trail v) -> Bool) -> Path v -> (Path v, Path v)-partitionPath p = (view wrapped *** view wrapped) . partition p . op Path+partitionPath p = (view _Unwrapped' *** view _Unwrapped') . partition p . op Path ------------------------------------------------------------ -- Modifying paths ---------------------------------------@@ -210,4 +216,4 @@ -- | Reverse all the component trails of a path. reversePath :: (InnerSpace v, OrderedField (Scalar v)) => Path v -> Path v-reversePath = unwrapped . mapped %~ reverseLocTrail+reversePath = _Wrapped . mapped %~ reverseLocTrail
src/Diagrams/Prelude.hs view
@@ -61,6 +61,10 @@ -- conjugation of transformations. , module Diagrams.Transform + -- | Projective transformations and other deformations+ -- lacking an inverse.+ , module Diagrams.Deform+ -- | Giving names to subdiagrams and later retrieving -- subdiagrams by name. , module Diagrams.Names@@ -124,6 +128,7 @@ import Diagrams.Combinators import Diagrams.Coordinates import Diagrams.CubicSpline+import Diagrams.Deform import Diagrams.Envelope import Diagrams.Located import Diagrams.Names
+ src/Diagrams/Prelude/ThreeD.hs view
@@ -0,0 +1,150 @@+{-# OPTIONS_GHC -fno-warn-duplicate-exports #-}+-----------------------------------------------------------------------------+-- |+-- Module : Diagrams.Prelude.ThreeD+-- Copyright : (c) 2014 diagrams-lib team (see LICENSE)+-- License : BSD-style (see LICENSE)+-- Maintainer : diagrams-discuss@googlegroups.com+--+-- A module to re-export most of the functionality of the diagrams+-- core and standard library, including 3D types and functions.+--+-----------------------------------------------------------------------------++module Diagrams.Prelude.ThreeD+ (+ -- * Core library+ -- | The core definitions of transformations, diagrams,+ -- backends, and so on.+ module Diagrams.Core++ -- * Standard library++ -- | Attributes (color, line style, etc.) and styles.+ , module Diagrams.Attributes++ -- | Alignment of diagrams relative to their envelopes.+ , module Diagrams.Align++ -- | Combining multiple diagrams into one.+ , module Diagrams.Combinators++ -- | Giving concrete locations to translation-invariant things.+ , module Diagrams.Located++ -- | Linear and cubic bezier segments.+ , module Diagrams.Segment++ -- | Trails.+ , module Diagrams.Trail++ -- | Parametrization of segments and trails.+ , module Diagrams.Parametric++ -- | Adjusting the length of parameterized objects.+ , module Diagrams.Parametric.Adjust++ -- | Computing tangent and normal vectors of segments and+ -- trails.+ , module Diagrams.Tangent++ -- | Trail-like things.+ , module Diagrams.TrailLike++ -- | Paths.+ , module Diagrams.Path++ -- | Cubic splines.+ , module Diagrams.CubicSpline++ -- | Some additional transformation-related functions, like+ -- conjugation of transformations.+ , module Diagrams.Transform++ -- | Giving names to subdiagrams and later retrieving+ -- subdiagrams by name.+ , module Diagrams.Names++ -- | Envelopes, aka functional bounding regions.+ , module Diagrams.Envelope++ -- | Traces, aka embedded raytracers, for finding points on+ -- the boundary of a diagram.+ , module Diagrams.Trace++ -- | A query is a function that maps points in a vector space+ -- to values in some monoid; they can be used to annotate+ -- the points of a diagram with some values.+ , module Diagrams.Query++ -- | Utilities for working with points.+ , module Diagrams.Points++ -- | Convenience infix operators for working with coordinates.+ , module Diagrams.Coordinates++ -- | things (shapes, transformations, combinators) specific+ -- to creating three-dimensional diagrams.+ , module Diagrams.ThreeD++ -- | Tools for making animations.+ , module Diagrams.Animation++ -- | Various utility definitions.+ , module Diagrams.Util++ -- * Convenience re-exports+ -- | For representing and operating on colors.+ , module Data.Colour+ -- | A large list of color names.+ , module Data.Colour.Names+ -- | Semigroups and monoids show up all over the place, so things from+ -- Data.Semigroup and Data.Monoid often come in handy.+ , module Data.Semigroup+ -- | For computing with vectors.+ , module Data.VectorSpace+ -- | For computing with points and vectors.+ , module Data.AffineSpace++ -- | For working with 'Active' (i.e. animated) things.+ , module Data.Active++ -- | Essential Lens Combinators+ , (&), (.~), (%~)++ , Applicative(..), (*>), (<*), (<$>), (<$), liftA, liftA2, liftA3+ ) where++import Diagrams.Core++import Diagrams.Align+import Diagrams.Animation+import Diagrams.Attributes+import Diagrams.Combinators+import Diagrams.Coordinates+import Diagrams.CubicSpline+import Diagrams.Envelope+import Diagrams.Located+import Diagrams.Names+import Diagrams.Parametric+import Diagrams.Parametric.Adjust+import Diagrams.Path+import Diagrams.Points+import Diagrams.Query+import Diagrams.Segment+import Diagrams.Tangent+import Diagrams.Trace+import Diagrams.Trail+import Diagrams.TrailLike+import Diagrams.Transform+import Diagrams.ThreeD+import Diagrams.Util++import Control.Applicative+import Data.Active+import Data.AffineSpace+import Data.Colour hiding (AffineSpace (..), atop, over)+import Data.Colour.Names hiding (tan)+import Data.Semigroup+import Data.VectorSpace hiding (Sum (..))+import Control.Lens ((&), (.~), (%~))
src/Diagrams/Segment.hs view
@@ -64,7 +64,7 @@ ) where -import Control.Lens (makeLenses, Wrapped(..), iso, op)+import Control.Lens (makeLenses, Wrapped(..), Rewrapped, iso, op) import Control.Applicative (liftA2) import Data.AffineSpace import Data.FingerTree@@ -375,6 +375,35 @@ p3 = alerp p21 p22 t +instance Num (Scalar v) => DomainBounds (FixedSegment v)++instance (VectorSpace v, Num (Scalar v)) => EndValues (FixedSegment v) where+ atStart (FLinear p0 _) = p0+ atStart (FCubic p0 _ _ _) = p0+ atEnd (FLinear _ p1) = p1+ atEnd (FCubic _ _ _ p1 ) = p1++instance (VectorSpace v, Fractional (Scalar v)) => Sectionable (FixedSegment v) where+ splitAtParam (FLinear p0 p1) t = (left, right)+ where left = FLinear p0 p+ right = FLinear p p1+ p = alerp p0 p1 t+ splitAtParam (FCubic p0 c1 c2 p1) t = (left, right)+ where left = FCubic p0 a b cut+ right = FCubic cut c d p1+ -- first round+ a = alerp p0 c1 t+ p = alerp c1 c2 t+ d = alerp c2 p1 t+ -- second round+ b = alerp a p t+ c = alerp p d t+ -- final round+ cut = alerp b c t++ reverseDomain (FLinear p0 p1) = FLinear p1 p0+ reverseDomain (FCubic p0 c1 c2 p1) = FCubic p1 c2 c1 p0+ ------------------------------------------------------------ -- Segment measures -------------------------------------- ------------------------------------------------------------@@ -387,9 +416,12 @@ newtype SegCount = SegCount (Sum Int) deriving (Semigroup, Monoid) -instance Wrapped (Sum Int) (Sum Int) SegCount SegCount- where wrapped = iso SegCount $ \(SegCount x) -> x+instance Wrapped SegCount where+ type Unwrapped SegCount = Sum Int+ _Wrapped' = iso (\(SegCount x) -> x) SegCount +instance Rewrapped SegCount SegCount+ -- | A type to represent the total arc length of a chain of -- segments. The first component is a \"standard\" arc length, -- computed to within a tolerance of @10e-6@. The second component is@@ -399,13 +431,13 @@ newtype ArcLength v = ArcLength (Sum (Interval (Scalar v)), Scalar v -> Sum (Interval (Scalar v))) -instance (Scalar v ~ u, Scalar v' ~ u', u ~ u') => Wrapped- (Sum (Interval u), u -> Sum (Interval u ))- (Sum (Interval u'), u' -> Sum (Interval u'))- (ArcLength v)- (ArcLength v')- where wrapped = iso ArcLength $ \(ArcLength x) -> x+instance Wrapped (ArcLength v) where+ type Unwrapped (ArcLength v) =+ (Sum (Interval (Scalar v)), Scalar v -> Sum (Interval (Scalar v)))+ _Wrapped' = iso (\(ArcLength x) -> x) ArcLength +instance Rewrapped (ArcLength v) (ArcLength v')+ -- | Project out the cached arc length, stored together with error -- bounds. getArcLengthCached :: ArcLength v -> Interval (Scalar v)@@ -433,8 +465,11 @@ -- segments. newtype TotalOffset v = TotalOffset v -instance Wrapped v v (TotalOffset v) (TotalOffset v)- where wrapped = iso TotalOffset $ \(TotalOffset x) -> x+instance Wrapped (TotalOffset v) where+ type Unwrapped (TotalOffset v) = v+ _Wrapped' = iso (\(TotalOffset x) -> x) TotalOffset++instance Rewrapped (TotalOffset v) (TotalOffset v') instance AdditiveGroup v => Semigroup (TotalOffset v) where TotalOffset v1 <> TotalOffset v2 = TotalOffset (v1 ^+^ v2)
src/Diagrams/ThreeD.hs view
@@ -31,18 +31,19 @@ -- for rendering 3D geometry to (2D) images. ----------------------------------------------------------------------------- module Diagrams.ThreeD- ( module Diagrams.ThreeD.Types+ ( module Diagrams.ThreeD.Align+ , module Diagrams.ThreeD.Camera+ , module Diagrams.ThreeD.Light+ , module Diagrams.ThreeD.Shapes , module Diagrams.ThreeD.Transform+ , module Diagrams.ThreeD.Types , module Diagrams.ThreeD.Vector- , module Diagrams.ThreeD.Shapes- , module Diagrams.ThreeD.Light- , module Diagrams.ThreeD.Camera ) where --import Diagrams.ThreeD.Transform-import Diagrams.ThreeD.Vector-import Diagrams.ThreeD.Light-import Diagrams.ThreeD.Types+import Diagrams.ThreeD.Align import Diagrams.ThreeD.Camera+import Diagrams.ThreeD.Light import Diagrams.ThreeD.Shapes+import Diagrams.ThreeD.Transform+import Diagrams.ThreeD.Types+import Diagrams.ThreeD.Vector
+ src/Diagrams/ThreeD/Align.hs view
@@ -0,0 +1,209 @@+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE TypeFamilies #-}+-----------------------------------------------------------------------------+-- |+-- Module : Diagrams.ThreeD.Align+-- Copyright : (c) 2013 diagrams-lib team (see LICENSE)+-- License : BSD-style (see LICENSE)+-- Maintainer : diagrams-discuss@googlegroups.com+--+-- Alignment combinators specialized for three dimensions. See+-- "Diagrams.Align" for more general alignment combinators.+--+-- The basic idea is that alignment is achieved by moving diagrams'+-- local origins relative to their envelopes or traces (or some other+-- sort of boundary). For example, to align several diagrams along+-- their tops, we first move their local origins to the upper edge of+-- their boundary (using e.g. @map 'alignZMax'@), and then put them+-- together with their local origins along a line (using e.g. 'cat'+-- from "Diagrams.Combinators").+--+-----------------------------------------------------------------------------++module Diagrams.ThreeD.Align+ ( -- * Absolute alignment+ -- ** Align by envelope+ alignXMin, alignXMax, alignYMin, alignYMax, alignZMin, alignZMax++ -- ** Align by trace+ , snugXMin, snugXMax, snugYMin, snugYMax, snugZMin, snugZMax++ -- * Relative alignment+ , alignX, snugX, alignY, snugY, alignZ, snugZ++ -- * Centering+ , centerX, centerY, centerZ+ , centerXY, centerXZ, centerYZ, centerXYZ+ , snugCenterX, snugCenterY, snugCenterZ+ , snugCenterXY, snugCenterXZ, snugCenterYZ, snugCenterXYZ++ ) where++import Diagrams.Core++import Diagrams.Align+import Diagrams.ThreeD.Types+import Diagrams.ThreeD.Vector++import Data.VectorSpace++-- | Translate the diagram along unitX so that all points have+-- positive x-values.+alignXMin :: (Alignable a, HasOrigin a, V a ~ R3) => a -> a+alignXMin = align (negateV unitX)++snugXMin :: (Fractional (Scalar (V a)), Alignable a, Traced a,+ HasOrigin a, V a ~ R3) => a -> a+snugXMin = snug (negateV unitX)++-- | Translate the diagram along unitX so that all points have+-- negative x-values.+alignXMax :: (Alignable a, HasOrigin a, V a ~ R3) => a -> a+alignXMax = align unitX++snugXMax :: (Fractional (Scalar (V a)), Alignable a, Traced a,+ HasOrigin a, V a ~ R3) => a -> a+snugXMax = snug unitX+++-- | Translate the diagram along unitY so that all points have+-- negative y-values.+alignYMax :: (Alignable a, HasOrigin a, V a ~ R3) => a -> a+alignYMax = align unitY++-- | Move the origin along unitY until it touches the edge of the+-- diagram.+snugYMax:: (Fractional (Scalar (V a)), Alignable a, Traced a,+ HasOrigin a, V a ~ R3) => a -> a+snugYMax = snug unitY++-- | Translate the diagram along unitY so that all points have+-- positive y-values.+alignYMin :: (Alignable a, HasOrigin a, V a ~ R3) => a -> a+alignYMin = align (negateV unitY)++snugYMin :: (Fractional (Scalar (V a)), Alignable a, Traced a,+ HasOrigin a, V a ~ R3) => a -> a+snugYMin = snug (negateV unitY)++-- | Translate the diagram along unitZ so that all points have+-- negative z-values.+alignZMax :: (Alignable a, HasOrigin a, V a ~ R3) => a -> a+alignZMax = align unitZ++-- | Move the origin along unitZ until it touches the edge of the+-- diagram.+snugZMax:: (Fractional (Scalar (V a)), Alignable a, Traced a,+ HasOrigin a, V a ~ R3) => a -> a+snugZMax = snug unitZ++-- | Translate the diagram along unitZ so that all points have+-- positive z-values.+alignZMin :: (Alignable a, HasOrigin a, V a ~ R3) => a -> a+alignZMin = align (negateV unitZ)++-- | Move the origin along unit_Z until it touches the edge of the+-- diagram.+snugZMin :: (Fractional (Scalar (V a)), Alignable a, Traced a,+ HasOrigin a, V a ~ R3) => a -> a+snugZMin = snug (negateV unitZ)++-- | @alignX@ and @snugX@ move the local origin along unitX as follows:+--+-- * @alignX (-1)@ moves the local origin to the low-x of the boundary;+--+-- * @align 1@ moves the local origin to the high-x edge;+--+-- * any other argument interpolates linearly between these. For+-- example, @alignX 0@ centers, @alignX 2@ moves the origin one+-- \"radius\" to the right of the right edge, and so on.+--+-- * @snugX@ works the same way.++alignX :: (Alignable a, HasOrigin a, V a ~ R3) => Double -> a -> a+alignX = alignBy unitX++-- | See the documentation for 'alignX'.+snugX :: (Fractional (Scalar (V a)), Alignable a, Traced a,+ HasOrigin a, V a ~ R3) => Double -> a -> a+snugX = snugBy unitX++-- | Like 'alignX', but moving the local origin vertically, with an+-- argument of @1@ corresponding to the top edge and @(-1)@ corresponding+-- to the bottom edge.+alignY :: (Alignable a, HasOrigin a, V a ~ R3) => Double -> a -> a+alignY = alignBy unitY++snugY :: (Fractional (Scalar (V a)), Alignable a, Traced a,+ HasOrigin a, V a ~ R3) => Double -> a -> a+snugY = snugBy unitY+++-- | Like 'alignX', but moving the local origin in the Z direction, with an+-- argument of @1@ corresponding to the top edge and @(-1)@ corresponding+-- to the bottom edge.+alignZ :: (Alignable a, HasOrigin a, V a ~ R3) => Double -> a -> a+alignZ = alignBy unitZ++snugZ :: (Fractional (Scalar (V a)), Alignable a, Traced a,+ HasOrigin a, V a ~ R3) => Double -> a -> a+snugZ = snugBy unitZ++-- | Center the local origin along the X-axis.+centerX :: (Alignable a, HasOrigin a, V a ~ R3) => a -> a+centerX = alignBy unitX 0++snugCenterX :: (Fractional (Scalar (V a)), Alignable a, Traced a,+ HasOrigin a, V a ~ R3) => a -> a+snugCenterX = snugBy unitX 0++-- | Center the local origin along the Y-axis.+centerY :: (Alignable a, HasOrigin a, V a ~ R3) => a -> a+centerY = alignBy unitY 0++snugCenterY :: (Fractional (Scalar (V a)), Alignable a, Traced a,+ HasOrigin a, V a ~ R3) => a -> a+snugCenterY = snugBy unitY 0++-- | Center the local origin along the Z-axis.+centerZ :: (Alignable a, HasOrigin a, V a ~ R3) => a -> a+centerZ = alignBy unitZ 0++snugCenterZ :: (Fractional (Scalar (V a)), Alignable a, Traced a,+ HasOrigin a, V a ~ R3) => a -> a+snugCenterZ = snugBy unitZ 0++-- | Center along both the X- and Y-axes.+centerXY :: (Alignable a, HasOrigin a, V a ~ R3) => a -> a+centerXY = centerX . centerY++snugCenterXY :: (Fractional (Scalar (V a)), Alignable a, Traced a,+ HasOrigin a, V a ~ R3) => a -> a+snugCenterXY = snugCenterX . snugCenterY+++-- | Center along both the X- and Z-axes.+centerXZ :: (Alignable a, HasOrigin a, V a ~ R3) => a -> a+centerXZ = centerX . centerZ++snugCenterXZ :: (Fractional (Scalar (V a)), Alignable a, Traced a,+ HasOrigin a, V a ~ R3) => a -> a+snugCenterXZ = snugCenterX . snugCenterZ+++-- | Center along both the Y- and Z-axes.+centerYZ :: (Alignable a, HasOrigin a, V a ~ R3) => a -> a+centerYZ = centerZ . centerY++snugCenterYZ :: (Fractional (Scalar (V a)), Alignable a, Traced a,+ HasOrigin a, V a ~ R3) => a -> a+snugCenterYZ = snugCenterZ . snugCenterY++-- | Center an object in three dimensions.+centerXYZ :: (Fractional (Scalar (V a)), Alignable a, Traced a,+ HasOrigin a, V a ~ R3) => a -> a+centerXYZ = centerX . centerY . centerZ++snugCenterXYZ :: (Fractional (Scalar (V a)), Alignable a, Traced a,+ HasOrigin a, V a ~ R3) => a -> a+snugCenterXYZ = snugCenterX . snugCenterY . snugCenterZ
src/Diagrams/ThreeD/Camera.hs view
@@ -1,8 +1,9 @@-{-# LANGUAGE TypeFamilies #-}-{-# LANGUAGE MultiParamTypeClasses #-}-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE GADTs #-}-{-# LANGUAGE TemplateHaskell #-}+{-# LANGUAGE DeriveDataTypeable #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE GADTs #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE TemplateHaskell #-}+{-# LANGUAGE TypeFamilies #-} ----------------------------------------------------------------------------- -- |@@ -28,32 +29,35 @@ ) where -import Control.Lens (makeLenses)-import Data.Monoid-import Data.Cross+import Control.Lens (makeLenses)+import Data.Cross+import Data.Monoid+import Data.Typeable -import Diagrams.Core-import Diagrams.ThreeD.Types-import Diagrams.ThreeD.Vector+import Diagrams.Core+import Diagrams.ThreeD.Types+import Diagrams.ThreeD.Vector -- Parameterize Camera on the lens type, so that Backends can express which -- lenses they handle. data Camera l = Camera- { camLoc :: P3- , forward :: R3- , up :: R3- , lens :: l+ { camLoc :: P3+ , forward :: R3+ , up :: R3+ , lens :: l }+ deriving Typeable -class CameraLens l where+class Typeable l => CameraLens l where -- | The natural aspect ratio of the projection. aspect :: l -> Double -- | A perspective projection data PerspectiveLens = PerspectiveLens- { _horizontalFieldOfView :: Deg -- ^ Horizontal field of view.- , _verticalFieldOfView :: Deg -- ^ Vertical field of view.+ { _horizontalFieldOfView :: Angle -- ^ Horizontal field of view.+ , _verticalFieldOfView :: Angle -- ^ Vertical field of view. }+ deriving Typeable makeLenses ''PerspectiveLens @@ -65,6 +69,7 @@ { _orthoWidth :: Double -- ^ Width , _orthoHeight :: Double -- ^ Height }+ deriving Typeable makeLenses ''OrthoLens @@ -104,15 +109,15 @@ -- | mm50 has the field of view of a 50mm lens on standard 35mm film, -- hence an aspect ratio of 3:2.-mm50 = PerspectiveLens 40.5 27+mm50 = PerspectiveLens (40.5 @@ deg) (27 @@ deg) --- | mm50Wide has the same vertical field of view as mm50, but an+-- | mm50blWide has the same vertical field of view as mm50, but an -- aspect ratio of 1.6, suitable for wide screen computer monitors.-mm50Wide = PerspectiveLens 43.2 27+mm50Wide = PerspectiveLens (43.2 @@ deg) (27 @@ deg) -- | mm50Narrow has the same vertical field of view as mm50, but an -- aspect ratio of 4:3, for VGA and similar computer resulotions.-mm50Narrow = PerspectiveLens 36 27+mm50Narrow = PerspectiveLens (36 @@ deg) (27 @@ deg) camForward :: Direction d => Camera l -> d camForward = direction . forward
+ src/Diagrams/ThreeD/Deform.hs view
@@ -0,0 +1,51 @@+module Diagrams.ThreeD.Deform where++import Control.Lens++import Diagrams.Deform++import Diagrams.Coordinates+import Diagrams.ThreeD.Types++-- | The parallel projection onto the plane x=0+parallelX0 :: Deformation R3+parallelX0 = Deformation (& _x .~ 0)++-- | The perspective division onto the plane x=1 along lines going+-- through the origin.+perspectiveX1 :: Deformation R3+perspectiveX1 = Deformation (\p -> let x = p^._x in+ p & _x .~ 1 & _y //~ x & _z //~ x )++-- | The parallel projection onto the plane y=0+parallelY0 :: Deformation R3+parallelY0 = Deformation (& _y .~ 0)++-- | The perspective division onto the plane y=1 along lines going+-- through the origin.+perspectiveY1 :: Deformation R3+perspectiveY1 = Deformation (\p -> let y = p^._y in+ p & _x //~ y & _y .~ 1 & _z //~ y )++-- | The parallel projection onto the plane z=0+parallelZ0 :: Deformation R3+parallelZ0 = Deformation (& _z .~ 0)++-- | The perspective division onto the plane z=1 along lines going+-- through the origin.+perspectiveZ1 :: Deformation R3+perspectiveZ1 = Deformation (\p -> let z = p^._z in+ p & _x //~ z & _y //~ z & _z .~ 1 )++-- | The viewing transform for a viewer facing along the positive X+-- axis. X coördinates stay fixed, while Y coördinates are compressed+-- with increasing distance. @asDeformation (translation unitX) <>+-- parallelX0 <> frustrumX = perspectiveX1@+facingX :: Deformation R3+facingX = Deformation (\v -> v & _y //~ (v^._x) & _z //~ (v^._x))++facingY :: Deformation R3+facingY = Deformation (\v -> v & _x //~ (v^._y) & _z //~ (v^._y))++facingZ :: Deformation R3+facingZ = Deformation (\v -> v & _x //~ (v^._z) & _y //~ (v^._z))
src/Diagrams/ThreeD/Light.hs view
@@ -1,6 +1,7 @@-{-# LANGUAGE TypeFamilies #-}-{-# LANGUAGE MultiParamTypeClasses #-}-{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE DeriveDataTypeable #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE TypeFamilies #-} ----------------------------------------------------------------------------- -- |@@ -15,16 +16,19 @@ module Diagrams.ThreeD.Light where -import Data.Colour-import Data.Monoid+import Data.Colour+import Data.Monoid+import Data.Typeable -import Diagrams.Core-import Diagrams.ThreeD.Types-import Diagrams.ThreeD.Vector+import Diagrams.Core+import Diagrams.ThreeD.Types+import Diagrams.ThreeD.Vector data PointLight = PointLight P3 (Colour Double)+ deriving Typeable data ParallelLight = ParallelLight R3 (Colour Double)+ deriving Typeable type instance V PointLight = R3 type instance V ParallelLight = R3
src/Diagrams/ThreeD/Shapes.hs view
@@ -1,8 +1,7 @@-{-# LANGUAGE TypeFamilies- , FlexibleContexts- , MultiParamTypeClasses- , ViewPatterns- #-}+{-# LANGUAGE DeriveDataTypeable #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE TypeFamilies #-} ----------------------------------------------------------------------------- -- | -- Module : Diagrams.ThreeD.Shapes@@ -20,19 +19,17 @@ , sphere ) where -import Prelude hiding (minimum)-import Data.Semigroup--import Data.AffineSpace-import Data.Monoid.Inf (minimum)-import Data.VectorSpace--import Diagrams.Core+import Data.Typeable -import Diagrams.ThreeD.Types-import Diagrams.Solve+import Data.AffineSpace+import Data.Semigroup+import Data.VectorSpace+import Diagrams.Core+import Diagrams.Solve+import Diagrams.ThreeD.Types data Ellipsoid = Ellipsoid T3+ deriving Typeable type instance V Ellipsoid = R3 @@ -51,7 +48,7 @@ mempty (Query sphereQuery) where sphereEnv v = 1 / magnitude v- sphereTrace p v = minimum (quadForm a b c)+ sphereTrace p v = mkSortedList $ quadForm a b c where a = v <.> v b = 2 *^ p' <.> v c = p' <.> p' - 1
src/Diagrams/ThreeD/Transform.hs view
@@ -16,16 +16,42 @@ -- ----------------------------------------------------------------------------- -module Diagrams.ThreeD.Transform where+module Diagrams.ThreeD.Transform+ (+ -- * Rotation+ aboutX, aboutY, aboutZ+ , rotationAbout, pointAt, pointAt' + -- * Scaling+ , scalingX, scalingY, scalingZ+ , scaleX, scaleY, scaleZ+ , scaling, scale++ -- * Translation+ , translationX, translateX+ , translationY, translateY+ , translationZ, translateZ+ , translation, translate++ -- * Reflection+ , reflectionX, reflectX+ , reflectionY, reflectY+ , reflectionZ, reflectZ+ , reflectionAbout, reflectAbout++ -- * Utilities for Backends+ , onBasis+ ) where+ import Diagrams.Core import qualified Diagrams.Core.Transform as T +import Diagrams.Transform import Diagrams.ThreeD.Types import Diagrams.ThreeD.Vector import Diagrams.Coordinates -import Control.Lens ((*~), (//~))+import Control.Lens (view, (*~), (//~)) import Data.Semigroup import Data.AffineSpace@@ -37,39 +63,39 @@ -- A positive angle brings positive x-values towards the positive-y axis. -- -- The angle can be expressed using any type which is an--- instance of 'Angle'. For example, @aboutZ (1\/4 ::--- 'Turn')@, @aboutZ (tau\/4 :: 'Rad')@, and @aboutZ (90 ::--- 'Deg')@ all represent the same transformation, namely, a+-- instance of 'Angle'. For example, @aboutZ (1\/4 \@\@+-- 'turn')@, @aboutZ (tau\/4 \@\@ 'rad')@, and @aboutZ (90 \@\@+-- 'deg')@ all represent the same transformation, namely, a -- counterclockwise rotation by a right angle. For more general rotations, -- see 'rotationAbout'. -- -- Note that writing @aboutZ (1\/4)@, with no type annotation, will -- yield an error since GHC cannot figure out which sort of angle -- you want to use.-aboutZ :: Angle a => a -> T3+aboutZ :: Angle -> T3 aboutZ ang = fromLinear r (linv r) where r = rot theta <-> rot (-theta)- Rad theta = convertAngle ang+ theta = view rad ang rot th (coords -> x :& y :& z) = (cos th * x - sin th * y) ^& (sin th * x + cos th * y) ^& z -- | Like 'aboutZ', but rotates about the X axis, bringing positive y-values -- towards the positive z-axis.-aboutX :: Angle a => a -> T3+aboutX :: Angle -> T3 aboutX ang = fromLinear r (linv r) where r = rot theta <-> rot (-theta)- Rad theta = convertAngle ang+ theta = view rad ang rot th (coords -> x :& y :& z) = (x) ^& (cos th * y - sin th * z) ^& (sin th * y + cos th * z) -- | Like 'aboutZ', but rotates about the Y axis, bringing postive -- x-values towards the negative z-axis.-aboutY :: Angle a => a -> T3+aboutY :: Angle -> T3 aboutY ang = fromLinear r (linv r) where r = rot theta <-> rot (-theta)- Rad theta = convertAngle ang+ theta = view rad ang rot th (coords -> x :& y :& z) = (cos th * x + sin th * z) ^& y ^& (-sin th * x + cos th * z)@@ -77,17 +103,17 @@ -- | @rotationAbout p d a@ is a rotation about a line parallel to @d@ -- passing through @p@. rotationAbout- :: (Angle a, Direction d)+ :: Direction d => P3 -- ^ origin of rotation -> d -- ^ direction of rotation axis- -> a -- ^ angle of rotation+ -> Angle -- ^ angle of rotation -> T3 rotationAbout p d a = mconcat [translation (negateV t), fromLinear r (linv r), translation t] where r = rot theta <-> rot (-theta)- Rad theta = convertAngle a+ theta = view rad a w = fromDirection d rot :: Double -> R3 -> R3 rot th v = v ^* cos th ^+^@@ -110,10 +136,10 @@ pointAt' :: R3 -> R3 -> R3 -> T3 pointAt' about initial final = tilt <> pan where inPanPlane = final ^-^ project final initial- panAngle = angleBetween initial inPanPlane :: Turn- pan = rotationAbout origin (direction about :: Spherical Turn) panAngle- tiltAngle = angleBetween initial inPanPlane :: Turn- tiltDir = direction $ cross3 inPanPlane about :: Spherical Turn+ panAngle = angleBetween initial inPanPlane+ pan = rotationAbout origin (direction about :: Spherical) panAngle+ tiltAngle = angleBetween initial inPanPlane+ tiltDir = direction $ cross3 inPanPlane about :: Spherical tilt = rotationAbout origin tiltDir tiltAngle -- Scaling -------------------------------------------------@@ -150,6 +176,85 @@ -- uniformly, use 'scale'. scaleZ :: (Transformable t, V t ~ R3) => Double -> t -> t scaleZ = transform . scalingZ++-- Translation ----------------------------------------++-- | Construct a transformation which translates by the given distance+-- in the x direction.+translationX :: Double -> T3+translationX x = translation (x ^& 0 ^& 0)++-- | Translate a diagram by the given distance in the x+-- direction.+translateX :: (Transformable t, V t ~ R3) => Double -> t -> t+translateX = transform . translationX++-- | Construct a transformation which translates by the given distance+-- in the y direction.+translationY :: Double -> T3+translationY y = translation (0 ^& y ^& 0)++-- | Translate a diagram by the given distance in the y+-- direction.+translateY :: (Transformable t, V t ~ R3) => Double -> t -> t+translateY = transform . translationY++-- | Construct a transformation which translates by the given distance+-- in the z direction.+translationZ :: Double -> T3+translationZ z = translation (0 ^& 0 ^& z)++-- | Translate a diagram by the given distance in the y+-- direction.+translateZ :: (Transformable t, V t ~ R3) => Double -> t -> t+translateZ = transform . translationZ++-- Reflection ----------------------------------------------++-- | Construct a transformation which flips a diagram across x=0,+-- i.e. sends the point (x,y,z) to (-x,y,z).+reflectionX :: T3+reflectionX = scalingX (-1)++-- | Flip a diagram across x=0, i.e. send the point (x,y,z) to (-x,y,z).+reflectX :: (Transformable t, V t ~ R3) => t -> t+reflectX = transform reflectionX++-- | Construct a transformation which flips a diagram across y=0,+-- i.e. sends the point (x,y,z) to (x,-y,z).+reflectionY :: T3+reflectionY = scalingY (-1)++-- | Flip a diagram across y=0, i.e. send the point (x,y,z) to+-- (x,-y,z).+reflectY :: (Transformable t, V t ~ R3) => t -> t+reflectY = transform reflectionY++-- | Construct a transformation which flips a diagram across z=0,+-- i.e. sends the point (x,y,z) to (x,y,-z).+reflectionZ :: T3+reflectionZ = scalingZ (-1)++-- | Flip a diagram across z=0, i.e. send the point (x,y,z) to+-- (x,y,-z).+reflectZ :: (Transformable t, V t ~ R3) => t -> t+reflectZ = transform reflectionZ++-- | @reflectionAbout p v@ is a reflection across the plane through+-- the point @p@ and normal to vector @v@.+reflectionAbout :: P3 -> R3 -> T3+reflectionAbout p v =+ conjugate (translation (origin .-. p)) reflect where+ reflect = fromLinear t (linv t)+ t = f v <-> f (negateV v)+ f u w = w ^-^ 2 *^ project u w++-- | @reflectAbout p v@ reflects a diagram in the line determined by+-- the point @p@ and the vector @v@.+reflectAbout :: (Transformable t, V t ~ R3) => P3 -> R3 -> t -> t+reflectAbout p v = transform (reflectionAbout p v)++-- Utilities ---------------------------------------- -- | Get the matrix equivalent of an affine transform, as a triple of -- columns paired with the translation vector. This is mostly
src/Diagrams/ThreeD/Types.hs view
@@ -29,13 +29,8 @@ -- * Two-dimensional angles -- | These are defined in "Diagrams.TwoD.Types" but -- reëxported here for convenience.- , Angle(..)- , Turn(Turn), asTurn- , CircleFrac- , Rad(Rad), asRad- , Deg(Deg), asDeg-- , fullTurn, convertAngle, angleRatio+ , Angle, rad, turn, deg, (@@)+ , fullTurn, angleRatio -- * Directions in 3D , Direction(..)@@ -43,8 +38,8 @@ , asSpherical ) where -import Control.Applicative-import Control.Lens (Iso', iso, over, Wrapped, wrapped, _1, _2, _3)+import Control.Lens (Iso', iso, over, Wrapped(..), Rewrapped+ , _1, _2, _3) import Diagrams.Core import Diagrams.TwoD.Types@@ -78,10 +73,13 @@ unr3 = unR3 -- | Lens wrapped isomorphisms for R3.-instance Wrapped (Double, Double, Double) (Double, Double, Double) R3 R3 where- wrapped = iso r3 unr3- {-# INLINE wrapped #-}+instance Wrapped R3 where+ type Unwrapped R3 = (Double, Double, Double)+ _Wrapped' = iso unr3 r3+ {-# INLINE _Wrapped' #-} +instance Rewrapped R3 R3+ type instance V R3 = R3 instance VectorSpace R3 where@@ -139,35 +137,28 @@ -- based on that of the Angle class in 2D. class Direction d where- -- | Convert to polar angles- toSpherical :: Angle a => d -> Spherical a+ -- | Convert to spherical coördinates+ toSpherical :: d -> Spherical - -- | Convert from polar angles- fromSpherical :: Angle a => Spherical a -> d+ -- | Convert from spherical coördinates+ fromSpherical :: Spherical -> d -- | A direction expressed as a pair of spherical coordinates. -- `Spherical 0 0` is the direction of `unitX`. The first coordinate -- represents rotation about the Z axis, the second rotation towards the Z axis.-data Spherical a = Spherical a a+data Spherical = Spherical Angle Angle deriving (Show, Read, Eq) -instance Applicative Spherical where- pure a = Spherical a a- Spherical a b <*> Spherical c d = Spherical (a c) (b d)--instance Functor Spherical where- fmap f s = pure f <*> s--instance (Angle a) => Direction (Spherical a) where- toSpherical = fmap convertAngle- fromSpherical = fmap convertAngle+instance Direction Spherical where+ toSpherical = id+ fromSpherical = id -- | The identity function with a restricted type, for conveniently -- restricting unwanted polymorphism. For example, @fromDirection -- . asSpherical . camForward@ gives a unit vector pointing in the -- direction of the camera view. Without @asSpherical@, the -- intermediate type would be ambiguous.-asSpherical :: Spherical Turn -> Spherical Turn+asSpherical :: Spherical -> Spherical asSpherical = id instance HasX R3 where
src/Diagrams/ThreeD/Vector.hs view
@@ -20,7 +20,7 @@ direction, fromDirection, angleBetween, angleBetweenDirs ) where -import Control.Lens (op)+import Control.Lens ((^.)) import Data.VectorSpace import Data.Cross @@ -61,24 +61,23 @@ | otherwise = fromSpherical $ Spherical θ φ where r = magnitude v (x,y,z) = unr3 v- φ = Rad . asin $ z / r- θ = Rad . atan2 y $ x- zero = Rad $ 0+ φ = asin (z / r) @@ rad+ θ = atan2 y x @@ rad+ zero = 0 @@ rad -- | @fromDirection d@ is the unit vector in the direction @d@. fromDirection :: Direction d => d -> R3 fromDirection (toSpherical -> (Spherical θ' φ')) = r3 (x,y,z) where- θ = op Rad $ θ'- φ = op Rad $ φ'+ θ = θ'^.rad+ φ = φ'^.rad x = cos θ * cos φ y = sin θ * cos φ z = sin φ -- | compute the positive angle between the two vectors in their common plane-angleBetween :: (Angle a, Num a, Ord a) => R3 -> R3 -> a-angleBetween v1 v2 = convertAngle . Rad $- atan2 (magnitude $ cross3 v1 v2) (v1 <.> v2)+angleBetween :: R3 -> R3 -> Angle+angleBetween v1 v2 = atan2 (magnitude $ cross3 v1 v2) (v1 <.> v2) @@ rad -- | compute the positive angle between the two vectors in their common plane-angleBetweenDirs :: (Direction d, Angle a, Num a, Ord a) => d -> d -> a+angleBetweenDirs :: Direction d => d -> d -> Angle angleBetweenDirs d1 d2 = angleBetween (fromDirection d1) (fromDirection d2)
src/Diagrams/Trail.hs view
@@ -103,7 +103,7 @@ ) where import Control.Arrow ((***))-import Control.Lens (AnIso', iso, view, op, Wrapped(..))+import Control.Lens (AnIso', iso, view, op, Wrapped(..), Rewrapped) import Data.AffineSpace import Data.FingerTree (FingerTree, ViewL (..), ViewR (..), (<|), (|>))@@ -151,11 +151,11 @@ newtype SegTree v = SegTree (FingerTree (SegMeasure v) (Segment Closed v)) deriving (Eq, Ord, Show) -instance Wrapped (FingerTree (SegMeasure v) (Segment Closed v))- (FingerTree (SegMeasure v) (Segment Closed v))- (SegTree v)- (SegTree v)- where wrapped = iso SegTree $ \(SegTree x) -> x+instance Wrapped (SegTree v) where+ type Unwrapped (SegTree v) = FingerTree (SegMeasure v) (Segment Closed v)+ _Wrapped' = iso (\(SegTree x) -> x) SegTree++instance Rewrapped (SegTree v) (SegTree v') type instance V (SegTree v) = v
src/Diagrams/TrailLike.hs view
@@ -30,7 +30,7 @@ import Data.AffineSpace ((.-.)) import Data.VectorSpace-import Control.Lens (view, wrapped)+import Control.Lens (view, _Unwrapped') import Diagrams.Core import Diagrams.Located@@ -96,7 +96,7 @@ -- | Translationally invariant things are trail-like as long as the -- underlying type is. instance TrailLike t => TrailLike (TransInv t) where- trailLike = view wrapped . trailLike+ trailLike = view _Unwrapped' . trailLike -- | 'Located' things are trail-like as long as the underlying type -- is. The location is taken to be the location of the input
src/Diagrams/Transform.hs view
@@ -41,7 +41,7 @@ -- | Carry out some transformation \"under\" another one: @f ``under`` -- t@ first applies @t@, then @f@, then the inverse of @t@. For--- example, @'scaleX' 2 ``under`` 'rotationBy' (-1/8 :: Turn)@+-- example, @'scaleX' 2 ``under`` 'rotation' (-1/8 \@\@ Turn)@ -- is the transformation which scales by a factor of 2 along the -- diagonal line y = x. --
src/Diagrams/TwoD.hs view
@@ -69,11 +69,10 @@ -- * Angles , tau- , Angle(..)- , Turn(..), asTurn, CircleFrac- , Rad(..), asRad- , Deg(..), asDeg- , fullTurn, fullCircle, convertAngle+ , Angle+ , rad, turn, deg+ , fullTurn, fullCircle, angleRatio+ , (@@) -- * Paths -- ** Stroking@@ -84,7 +83,7 @@ , StrokeOpts(..), vertexNames, queryFillRule -- ** Clipping- , clipBy+ , clipBy, clipTo, clipped -- * Shapes -- ** Rules@@ -134,12 +133,13 @@ , RoundedRectOpts(..), radiusTL, radiusTR, radiusBL, radiusBR -- ** Arrows- , arrow, arrow'+ , arrowV, arrowV' , arrowAt, arrowAt' , arrowBetween, arrowBetween' , connect, connect' , connectPerim, connectPerim' , connectOutside, connectOutside'+ , arrow, arrow' , straightShaft , module Diagrams.TwoD.Arrowheads @@ -152,6 +152,7 @@ , tailSize , headGap , tailGap+ , gap , headColor , headStyle , tailColor@@ -188,6 +189,10 @@ , shearingX, shearX , shearingY, shearY + -- * Deformations - non-affine transforms+ , parallelX0, perspectiveX1, parallelY0, perspectiveY1+ , facingX, facingY+ -- * Combinators -- ** Combining multiple diagrams , (===), (|||), atAngle@@ -246,6 +251,7 @@ import Diagrams.TwoD.Model import Diagrams.TwoD.Path import Diagrams.TwoD.Polygons+import Diagrams.TwoD.Deform import Diagrams.TwoD.Shapes import Diagrams.TwoD.Size import Diagrams.TwoD.Text
src/Diagrams/TwoD/Align.hs view
@@ -144,8 +144,8 @@ -- | Center along both the X- and Y-axes. centerXY :: (Alignable a, HasOrigin a, V a ~ R2) => a -> a-centerXY = centerX . centerY+centerXY = center snugCenterXY :: (Fractional (Scalar (V a)), Alignable a, Traced a, HasOrigin a, V a ~ R2) => a -> a-snugCenterXY = snugCenterX . snugCenterY+snugCenterXY = snugCenter
src/Diagrams/TwoD/Arc.hs view
@@ -31,11 +31,12 @@ import Diagrams.TwoD.Transform import Diagrams.TwoD.Types import Diagrams.TwoD.Vector (direction, e, unitX)-import Diagrams.Util (tau, ( # ))+import Diagrams.Util (( # )) +import Control.Lens ((^.)) import Data.AffineSpace ((.-.)) import Data.Semigroup ((<>))-import Data.VectorSpace (magnitude, negateV, (*^), (^-^))+import Data.VectorSpace import Diagrams.Coordinates -- For details of this approximation see:@@ -45,27 +46,27 @@ -- the positive y direction and sweeps counterclockwise through @s@ -- radians. The approximation is only valid for angles in the first -- quadrant.-bezierFromSweepQ1 :: Rad -> Segment Closed R2-bezierFromSweepQ1 s = fmap (^-^ v) . rotate (s/2) $ bezier3 c2 c1 p0- where p0@(coords -> x :& y) = rotate (s/2) v+bezierFromSweepQ1 :: Angle -> Segment Closed R2+bezierFromSweepQ1 s = fmap (^-^ v) . rotate (s ^/ 2) $ bezier3 c2 c1 p0+ where p0@(coords -> x :& y) = rotate (s ^/ 2) v c1 = ((4-x)/3) ^& ((1-x)*(3-x)/(3*y)) c2 = reflectY c1 v = unitX -- | @bezierFromSweep s@ constructs a series of 'Cubic' segments that -- start in the positive y direction and sweep counter clockwise--- through @s@ radians. If @s@ is negative, it will start in the+-- through the angle @s@. If @s@ is negative, it will start in the -- negative y direction and sweep clockwise. When @s@ is less than -- 0.0001 the empty list results. If the sweep is greater than tau--- then it is truncated to tau.-bezierFromSweep :: Rad -> [Segment Closed R2]+-- radians then it is truncated to one full revolution.+bezierFromSweep :: Angle -> [Segment Closed R2] bezierFromSweep s- | s > tau = bezierFromSweep tau- | s < 0 = fmap reflectY . bezierFromSweep $ (-s)- | s < 0.0001 = []- | s < tau/4 = [bezierFromSweepQ1 s]- | otherwise = bezierFromSweepQ1 (tau/4)- : map (rotateBy (1/4)) (bezierFromSweep (max (s - tau/4) 0))+ | s > fullTurn = bezierFromSweep fullTurn+ | s < zeroV = fmap reflectY . bezierFromSweep $ (negateV s)+ | s < 0.0001 @@ rad = []+ | s < fullTurn^/4 = [bezierFromSweepQ1 s]+ | otherwise = bezierFromSweepQ1 (fullTurn^/4)+ : map (rotateBy (1/4)) (bezierFromSweep (max (s ^-^ fullTurn^/4) zeroV)) {- ~~~~ Note [segment spacing]@@ -89,29 +90,29 @@ -- | Given a start angle @s@ and an end angle @e@, @'arcT' s e@ is the -- 'Trail' of a radius one arc counterclockwise between the two angles.-arcT :: Angle a => a -> a -> Trail R2+arcT :: Angle -> Angle -> Trail R2 arcT start end- | end' < start' = arcT start' (end' + fromIntegral d)- | otherwise = (if sweep >= tau then glueTrail else id)+ | end' < start' = arcT start (end ^+^ (fromIntegral d @@ turn))+ | otherwise = (if sweep >= fullTurn then glueTrail else id) $ trailFromSegments bs- where sweep = convertAngle $ end - start+ where sweep = end ^-^ start bs = map (rotate start) . bezierFromSweep $ sweep -- We want to compare the start and the end and in case -- there isn't some law about 'Angle' ordering, we use a -- known 'Angle' for that.- start' = convertAngle start :: Turn- end' = convertAngle end+ start' = start^.turn+ end' = end^.turn d = ceiling (start' - end') :: Integer -- | Given a start angle @s@ and an end angle @e@, @'arc' s e@ is the -- path of a radius one arc counterclockwise between the two angles. -- The origin of the arc is its center.-arc :: (Angle a, TrailLike t, V t ~ R2) => a -> a -> t+arc :: (TrailLike t, V t ~ R2) => Angle -> Angle -> t arc start end = trailLike $ arcT start end `at` (rotate start $ p2 (1,0)) -- | Like 'arc' but clockwise.-arcCW :: (Angle a, TrailLike t, V t ~ R2) => a -> a -> t+arcCW :: (TrailLike t, V t ~ R2) => Angle -> Angle -> t arcCW start end = trailLike $ -- flipped arguments to get the path we want -- then reverse the trail to get the cw direction.@@ -129,9 +130,9 @@ -- -- <<diagrams/src_Diagrams_TwoD_Arc_arc'Ex.svg#diagram=arc'Ex&width=300>> ----- > arc'Ex = mconcat [ arc' r 0 (1/4 :: Turn) | r <- [0.5,-1,1.5] ]+-- > arc'Ex = mconcat [ arc' r 0 (1/4 \@\@ turn) | r <- [0.5,-1,1.5] ] -- > # centerXY # pad 1.1-arc' :: (Angle a, TrailLike p, V p ~ R2) => Double -> a -> a -> p+arc' :: (TrailLike p, V p ~ R2) => Double -> Angle -> Angle -> p arc' r start end = trailLike $ scale (abs r) ts `at` (rotate start $ p2 (abs r,0)) where ts | r < 0 = reverseTrail $ arcT end start | otherwise = arcT start end@@ -142,13 +143,13 @@ -- <<diagrams/src_Diagrams_TwoD_Arc_wedgeEx.svg#diagram=wedgeEx&width=400>> -- -- > wedgeEx = hcat' (with & sep .~ 0.5)--- > [ wedge 1 (0 :: Turn) (1/4)--- > , wedge 1 (7/30 :: Turn) (11/30)--- > , wedge 1 (1/8 :: Turn) (7/8)+-- > [ wedge 1 (0 \@\@ turn) (1/4)+-- > , wedge 1 (7/30 \@\@ turn) (11/30)+-- > , wedge 1 (1/8 \@\@ turn) (7/8) -- > ] -- > # fc blue -- > # centerXY # pad 1.1-wedge :: (Angle a, TrailLike p, V p ~ R2) => Double -> a -> a -> p+wedge :: (TrailLike p, V p ~ R2) => Double -> Angle -> Angle -> p wedge r a1 a2 = trailLike . (`at` origin) . glueTrail . wrapLine $ fromOffsets [r *^ e a1] <> arc a1 a2 # scale r@@ -166,7 +167,7 @@ -- > [ arcBetween origin (p2 (2,1)) ht | ht <- [-0.2, -0.1 .. 0.2] ] -- > # centerXY # pad 1.1 arcBetween :: (TrailLike t, V t ~ R2) => P2 -> P2 -> Double -> t-arcBetween p q ht = trailLike (a # rotateBy (direction v) # moveTo p)+arcBetween p q ht = trailLike (a # rotate (direction v) # moveTo p) where h = abs ht isStraight = h < 0.00001@@ -174,10 +175,10 @@ d = magnitude (q .-. p) th = acos ((d*d - 4*h*h)/(d*d + 4*h*h)) r = d/(2*sin th)- mid | ht >= 0 = tau/4- | otherwise = 3*tau/4- st = mid - Rad th- end = mid + Rad th+ mid | ht >= 0 = fullTurn ^/ 4+ | otherwise = 3 *^ fullTurn ^/ 4+ st = mid ^-^ (th @@ rad)+ end = mid ^+^ (th @@ rad) a | isStraight = fromOffsets [d *^ unitX] | otherwise@@ -194,13 +195,13 @@ -- <<diagrams/src_Diagrams_TwoD_Arc_annularWedgeEx.svg#diagram=annularWedgeEx&width=400>> -- -- > annularWedgeEx = hcat' (with & sep .~ 0.50)--- > [ annularWedge 1 0.5 (0 :: Turn) (1/4)--- > , annularWedge 1 0.3 (7/30 :: Turn) (11/30)--- > , annularWedge 1 0.7 (1/8 :: Turn) (7/8)+-- > [ annularWedge 1 0.5 (0 \@\@ turn) (1/4)+-- > , annularWedge 1 0.3 (7/30 \@\@ turn) (11/30)+-- > , annularWedge 1 0.7 (1/8 \@\@ turn) (7/8) -- > ] -- > # fc blue -- > # centerXY # pad 1.1-annularWedge :: (Angle a, TrailLike p, V p ~ R2) => Double -> Double -> a -> a -> p+annularWedge :: (TrailLike p, V p ~ R2) => Double -> Double -> Angle -> Angle -> p annularWedge r1' r2' a1 a2 = trailLike . (`at` o) . glueTrail . wrapLine $ fromOffsets [(r1'-r2') *^ e a1] <> arc a1 a2 # scale r1'
src/Diagrams/TwoD/Arrow.hs view
@@ -46,7 +46,7 @@ -- > -- > -- Connect two diagrams and two points on their trails. -- > ex12 = dias # connect "first" "second"--- > # connectPerim "first" "second" (15/16 :: Turn) (9/16 :: Turn)+-- > # connectPerim "first" "second" (15/16 \@\@ turn) (9/16 \@\@ turn) -- > -- > -- Place an arrow at (0,0) the size and direction of (0,1). -- > ex3 = arrowAt origin unit_Y@@ -54,8 +54,8 @@ -- > example2 = (ex12 <> ex3) # centerXY # pad 1.1 -- * Creating arrows- arrow- , arrow'+ arrowV+ , arrowV' , arrowAt , arrowAt' , arrowBetween@@ -66,8 +66,10 @@ , connectPerim' , connectOutside , connectOutside'- , straightShaft + , arrow+ , arrow'+ -- * Options , ArrowOpts(..) @@ -78,27 +80,30 @@ , tailSize , headGap , tailGap+ , gap , headColor , headStyle , tailColor , tailStyle , shaftColor , shaftStyle+ , straightShaft -- | See "Diagrams.TwoD.Arrowheads" for a list of standard -- arrowheads and help creating your own. , module Diagrams.TwoD.Arrowheads ) where +import Control.Applicative ((<*>)) import Control.Arrow (first)-import Control.Lens (Lens', Setter',+import Control.Lens (Lens', Setter', Traversal', generateSignatures, lensRules, makeLensesWith, (%~), (&), (.~), (^.)) import Data.AffineSpace import Data.Default.Class import Data.Functor ((<$>))-import Data.Maybe (fromJust, fromMaybe)+import Data.Maybe (fromMaybe) import Data.Monoid (mempty, (<>)) import Data.Monoid.Coproduct (untangle) import Data.Monoid.Split@@ -117,7 +122,7 @@ import Diagrams.Trail import Diagrams.TwoD.Arrowheads import Diagrams.TwoD.Path (strokeT)-import Diagrams.TwoD.Transform (rotate, rotateBy, translateX)+import Diagrams.TwoD.Transform (rotate, translateX) import Diagrams.TwoD.Transform.ScaleInv (scaleInvPrim) import Diagrams.TwoD.Types import Diagrams.TwoD.Vector (direction, unitX, unit_X)@@ -176,9 +181,13 @@ -- | Distance to leave between the head and the target point. headGap :: Lens' ArrowOpts Double+ -- | Distance to leave between the starting point and the tail. tailGap :: Lens' ArrowOpts Double +gap :: Traversal' ArrowOpts Double+gap f opts = (\h t -> opts & headGap .~ h & tailGap .~ t) <$> f (opts ^. headGap) <*> f (opts ^. tailGap)+ -- | Style to apply to the head. @headStyle@ is modified by using the lens -- combinator @%~@ to change the current style. For example, to change -- an opaque black arrowhead to translucent orange:@@ -373,8 +382,8 @@ -- Build an arrow and set its endpoints to the image under tr of origin and (len,0). dArrow sty tr ln = (h' <> t' <> shaft)- # moveOriginBy (tWidth *^ (unit_X # rotateBy tAngle))- # rotateBy (direction (q .-. p) - dir)+ # moveOriginBy (tWidth *^ (unit_X # rotate tAngle))+ # rotate (direction (q .-. p) ^-^ dir) # moveTo p where @@ -398,7 +407,7 @@ shaftTrail = rawShaftTrail -- rotate it so it is pointing in the positive X direction- # rotateBy (- direction (trailOffset rawShaftTrail))+ # rotate (negateV direction (trailOffset rawShaftTrail)) -- apply the context transformation -- in case it includes -- things like flips and shears (the possibility of shears -- is why we must rotate it to a neutral position first)@@ -409,8 +418,8 @@ hWidth = hWidth' + opts^.headGap -- Calculate the angles that the head and tail should point.- tAngle = direction . tangentAtStart $ shaftTrail :: Turn- hAngle = direction . tangentAtEnd $ shaftTrail :: Turn+ tAngle = direction . tangentAtStart $ shaftTrail+ hAngle = direction . tangentAtEnd $ shaftTrail -- Calculte the scaling factor to apply to the shaft shaftTrail so that the entire -- arrow will be of length len. Then apply it to the shaft and make the@@ -420,9 +429,9 @@ shaft = strokeT shaftTrail' # applyStyle (shaftSty opts) -- Adjust the head and tail to point in the directions of the shaft ends.- h' = h # rotateBy hAngle+ h' = h # rotate hAngle # moveTo (origin .+^ shaftTrail' `atParam` domainUpper shaftTrail')- t' = t # rotateBy tAngle+ t' = t # rotate tAngle -- Find out what direction the arrow is pointing so we can set it back -- to point in the direction unitX when we are done.@@ -443,18 +452,31 @@ arrowBetween' opts s e = arrowAt' opts s (e .-. s) -- | Create an arrow starting at s with length and direction determined by--- the vectore v.+-- the vector v. arrowAt :: Renderable (Path R2) b => P2 -> R2 -> Diagram b R2 arrowAt s v = arrowAt' def s v arrowAt' :: Renderable (Path R2) b => ArrowOpts -> P2 -> R2 -> Diagram b R2-arrowAt' opts s v = arrow' opts len # rotateBy dir # moveTo s+arrowAt' opts s v = arrow' opts len # rotate dir # moveTo s where len = magnitude v dir = direction v +-- | @arrowV v@ creates an arrow with the direction and magnitude of+-- the vector @v@ (with its tail at the origin), using default+-- parameters.+arrowV :: Renderable (Path R2) b => R2 -> Diagram b R2+arrowV = arrowV' def++-- | @arrowV' v@ creates an arrow with the direction and magnitude of+-- the vector @v@ (with its tail at the origin).+arrowV'+ :: Renderable (Path R2) b+ => ArrowOpts -> R2 -> Diagram b R2+arrowV' opts = arrowAt' opts origin+ -- | Connect two diagrams with a straight arrow. connect :: (Renderable (Path R2) b, IsName n1, IsName n2)@@ -474,14 +496,14 @@ -- | Connect two diagrams at point on the perimeter of the diagrams, choosen -- by angle. connectPerim- :: (Renderable (Path R2) b, IsName n1, IsName n2, Angle a)- => n1 -> n2 -> a -> a+ :: (Renderable (Path R2) b, IsName n1, IsName n2)+ => n1 -> n2 -> Angle -> Angle -> (Diagram b R2 -> Diagram b R2) connectPerim = connectPerim' def connectPerim'- :: (Renderable (Path R2) b, IsName n1, IsName n2, Angle a)- => ArrowOpts -> n1 -> n2 -> a -> a+ :: (Renderable (Path R2) b, IsName n1, IsName n2)+ => ArrowOpts -> n1 -> n2 -> Angle -> Angle -> (Diagram b R2 -> Diagram b R2) connectPerim' opts n1 n2 a1 a2 = withName n1 $ \sub1 ->@@ -508,6 +530,9 @@ withName n2 $ \b2 -> let v = location b2 .-. location b1 midpoint = location b1 .+^ (v/2)- s = fromJust $ traceP midpoint (-v) b1- e = fromJust $ traceP midpoint v b2- in atop (arrowBetween' opts s e)+ s' = traceP midpoint (-v) b1+ e' = traceP midpoint v b2+ in+ case (s', e') of+ (Just s, Just e) -> atop (arrowBetween' opts s e)+ (_, _) -> id
src/Diagrams/TwoD/Arrowheads.hs view
@@ -56,8 +56,8 @@ ) where import Control.Lens ((&), (.~))-import Data.Default.Class import Data.AffineSpace+import Data.Default.Class import Data.Functor ((<$>)) import Data.Maybe (fromMaybe) import Data.Monoid (mempty, (<>))@@ -103,27 +103,29 @@ -- | <<diagrams/src_Diagrams_TwoD_Arrowheads_tri25Ex.svg#diagram=tri25Ex&width=120>> --- > tri25Ex = arrowAt' (with & arrowHead .~ arrowheadTriangle (2/5 :: Turn) & shaftStyle %~ lw 0)+-- > tri25Ex = arrowAt' (with & arrowHead .~ arrowheadTriangle (2/5 \@\@ turn) & shaftStyle %~ lw 0) -- > origin (r2 (0.001, 0)) -- > <> square 0.6 # alignL # lw 0-arrowheadTriangle :: Angle a => a -> ArrowHT+arrowheadTriangle :: Angle -> ArrowHT arrowheadTriangle theta = aHead where aHead size _ = (p, mempty) where- p = polygon (def & polyType .~ PolyPolar [theta, (-2 * theta)]+ p = polygon (def & polyType .~ PolyPolar [theta, (negateV 2 *^ theta)] (repeat (htRadius * size)) & polyOrient .~ NoOrient) # alignL -- | Isoceles triangle with linear concave base. Inkscape type 1 - dart like.-arrowheadDart :: Angle a => a -> ArrowHT+arrowheadDart :: Angle -> ArrowHT arrowheadDart theta = aHead where aHead size shaftWidth = (dartP # moveOriginTo (dartVertices !! 2), joint) where- a = toTurn theta r = htRadius * size- dartP = polygon (def & polyType .~ PolyPolar [a, 1/2 - a, 1/2 - a]- [r, r, 0.1 * size, r] & polyOrient .~ NoOrient)+ dartP = polygon+ ( def & polyType .~ PolyPolar [theta, (1/2 @@ turn) ^-^ theta, (1/2 @@ turn) ^-^ theta]+ [r, r, 0.1 * size, r]+ & polyOrient .~ NoOrient+ ) dartVertices = (concat . pathVertices) $ dartP m = magnitude (dartVertices !! 1 .-. dartVertices !! 3) s = 1 - shaftWidth / m@@ -135,7 +137,7 @@ , dartVertices !! 2 ]) # alignR -- | Isoceles triangle with curved concave base. Inkscape type 2.-arrowheadSpike :: Angle a => a -> ArrowHT+arrowheadSpike :: Angle -> ArrowHT arrowheadSpike theta = aHead where aHead size shaftWidth = (barb # moveOriginBy (m *^ unit_X) , joint)@@ -144,11 +146,11 @@ a' = reflectY a l1 = trailFromSegments [straight (unit_X2 ^+^ a)] l2 = trailFromSegments [reverseSegment . straight $ (unit_X2 ^+^ a')]- c = reflectX $ arc' htRadius theta (-theta)+ c = reflectX $ arc' htRadius theta (negateV theta) barb = (closedPath $ (l1 <> c <> l2)) # scale size m = xWidth barb --c `atParam` 0.5- b = Rad $ asin ((shaftWidth / 2) / (htRadius * size))- c' = arc' htRadius (-b ) b # scale size+ b = asin ((shaftWidth / 2) / (htRadius * size)) @@ rad+ c' = arc' htRadius (negateV b) b # scale size joint = (closedPath $ (c')) # centerY # alignR xWidth p = pa + pb where@@ -156,7 +158,7 @@ pb = fromMaybe 0 (magnitude <$> traceV origin unit_X p) -- | Curved sides, linear concave base. Illustrator CS5 #3-arrowheadThorn :: Angle a => a -> Double -> ArrowHT+arrowheadThorn :: Angle -> Double -> ArrowHT arrowheadThorn theta r = aHead where aHead size shaftWidth = (thornP # moveOriginTo (thornVertices !! 2), joint)@@ -165,7 +167,7 @@ c1 = curvedSide theta l1 = straight $ (reflectY a) ^-^ (unit_X2 # scale r) l2 = straight $ unit_X2 # scale r ^-^ a- c2 = c1 # rotate (-theta)+ c2 = c1 # rotate (negateV theta) thornP = (closedPath $ trailFromSegments [c1, l1, l2, c2]) # scale size thornVertices = (concat . pathVertices) $ thornP m = magnitude (thornVertices !! 1 .-. thornVertices !! 3)@@ -178,7 +180,7 @@ , thornVertices !! 2 ]) # alignR -- | Make a side for the thorn head.-curvedSide :: Angle a => a -> Segment Closed R2+curvedSide :: Angle -> Segment Closed R2 curvedSide theta = bezier3 ctrl1 ctrl2 end where v0 = scaleR unit_X@@ -202,7 +204,7 @@ smooth [] = mempty smooth (x:xs) = cubicSpline True x <> smooth xs -arrowheadMissile :: Angle a => a -> ArrowHT+arrowheadMissile :: Angle -> ArrowHT arrowheadMissile theta = smoothArrowhead $ arrowheadDart theta -- Standard heads ---------------------------------------------------------@@ -213,31 +215,31 @@ -- > triEx = drawHead tri tri :: ArrowHT-tri = arrowheadTriangle (1/3 :: Turn)+tri = arrowheadTriangle (1/3 @@ turn) -- | <<diagrams/src_Diagrams_TwoD_Arrowheads_spikeEx.svg#diagram=spikeEx&width=100>> -- > spikeEx = drawHead spike spike :: ArrowHT-spike = arrowheadSpike (3/8 :: Turn)+spike = arrowheadSpike (3/8 @@ turn) -- | <<diagrams/src_Diagrams_TwoD_Arrowheads_thornEx.svg#diagram=thornEx&width=100>> -- > thornEx = drawHead thorn thorn :: ArrowHT-thorn = arrowheadThorn (3/8 :: Turn) 1+thorn = arrowheadThorn (3/8 @@ turn) 1 -- | <<diagrams/src_Diagrams_TwoD_Arrowheads_dartEx.svg#diagram=dartEx&width=100>> -- > dartEx = drawHead dart dart :: ArrowHT-dart = arrowheadDart (2/5 :: Turn)+dart = arrowheadDart (2/5 @@ turn) -- | <<diagrams/src_Diagrams_TwoD_Arrowheads_missileEx.svg#diagram=missileEx&width=100>> -- > missileEx = drawHead missile missile :: ArrowHT-missile = arrowheadMissile (2/5 :: Turn)+missile = arrowheadMissile (2/5 @@ turn) -- Tails ------------------------------------------------------------------ -- > drawTail t = arrowAt' (with & arrowTail .~ t & shaftStyle %~ lw 0 & arrowHead .~ noHead)@@ -255,7 +257,7 @@ t = reflectX t' j = reflectX j' -arrowtailBlock :: Angle a => a -> ArrowHT+arrowtailBlock :: Angle -> ArrowHT arrowtailBlock theta = aTail where aTail size _ = (t, mempty)@@ -268,16 +270,15 @@ x = magnitude b -- | The angle is where the top left corner intersects the circle.-arrowtailQuill :: Angle a => a -> ArrowHT+arrowtailQuill :: Angle -> ArrowHT arrowtailQuill theta =aTail where aTail size shaftWidth = (t, j) where t = ( closedPath $ trailFromVertices [v0, v1, v2, v3, v4, v5, v0] ) # scale size # alignR- theta' = toTurn theta v0 = p2 (0.5, 0)- v2 = p2 (unr2 $ e theta' # scaleR)+ v2 = p2 (unr2 $ e theta # scaleR) v1 = v2 # translateX (5/4 * htRadius) v3 = p2 (-0.1, 0) v4 = v2 # reflectY@@ -329,10 +330,10 @@ -- > quillEx = drawTail quill quill :: ArrowHT-quill = arrowtailQuill (2/5 :: Turn)+quill = arrowtailQuill (2/5 @@ turn) -- | <<diagrams/src_Diagrams_TwoD_Arrowheads_blockEx.svg#diagram=blockEx&width=100>> -- > blockEx = drawTail block block :: ArrowHT-block = arrowtailBlock (2/5 :: Turn)+block = arrowtailBlock (2/5 @@ turn)
src/Diagrams/TwoD/Combinators.hs view
@@ -76,7 +76,7 @@ -- -- to place @c@ above @d@. The local origin of the resulting -- combined diagram is the same as the local origin of the first.--- @(|||)@ is associative and has 'mempty' as an identity. See the+-- @(===)@ is associative and has 'mempty' as an identity. See the -- documentation of 'beside' for more information. (===) :: (Juxtaposable a, V a ~ R2, Semigroup a) => a -> a -> a (===) = beside (negateV unitY)@@ -84,7 +84,7 @@ -- | Place two diagrams (or other juxtaposable objects) horizontally -- adjacent to one another, with the first diagram to the left of -- the second. The local origin of the resulting combined diagram--- is the same as the local origin of the first. @(===)@ is+-- is the same as the local origin of the first. @(|||)@ is -- associative and has 'mempty' as an identity. See the -- documentation of 'beside' for more information. (|||) :: (Juxtaposable a, V a ~ R2, Semigroup a) => a -> a -> a@@ -95,7 +95,7 @@ -- 'th' from the first. The local origin of the resulting combined -- diagram is the same as the local origin of the first. -- See the documentation of 'beside' for more information.-atAngle :: (Juxtaposable a, V a ~ R2, Semigroup a, Angle b) => b -> a -> a -> a+atAngle :: (Juxtaposable a, V a ~ R2, Semigroup a) => Angle -> a -> a -> a atAngle th = beside (fromDirection th) -- | Lay out a list of juxtaposable objects in a row from left to right,
src/Diagrams/TwoD/Curvature.hs view
@@ -20,7 +20,6 @@ ) where import Data.Monoid.Inf-import Data.NumInstances.Tuple () import Data.VectorSpace import Control.Arrow (first, second)@@ -147,7 +146,7 @@ where (x' ,y' ) = seg `tangentAtParam` t (x'',y'') = secondDerivative- secondDerivative = (6*(3*t-2))*^b + (6-18*t)*^c + (6*t)*^d+ secondDerivative = (6*(3*t-2))*^b ^+^ (6-18*t)*^c ^+^ (6*t)*^d -- TODO: We should be able to generalize this to higher dimensions. See -- <http://en.wikipedia.org/wiki/Curvature>
+ src/Diagrams/TwoD/Deform.hs view
@@ -0,0 +1,36 @@+module Diagrams.TwoD.Deform where++import Control.Lens++import Diagrams.Deform++import Diagrams.Coordinates+import Diagrams.TwoD.Types++-- | The parallel projection onto the line x=0+parallelX0 :: Deformation R2+parallelX0 = Deformation (& _x .~ 0)++-- | The perspective division onto the line x=1 along lines going+-- through the origin.+perspectiveX1 :: Deformation R2+perspectiveX1 = Deformation (\p -> p & _y //~ (p^._x) & _x .~ 1)++-- | The parallel projection onto the line y=0+parallelY0 :: Deformation R2+parallelY0 = Deformation (& _y .~ 0)++-- | The perspective division onto the line y=1 along lines going+-- through the origin.+perspectiveY1 :: Deformation R2+perspectiveY1 = Deformation (\p -> p & _x //~ (p^._y) & _y .~ 1)++-- | The viewing transform for a viewer facing along the positive X+-- axis. X coördinates stay fixed, while Y coördinates are compressed+-- with increasing distance. @asDeformation (translation unitX) <>+-- parallelX0 <> frustrumX = perspectiveX1@+facingX :: Deformation R2+facingX = Deformation (\v -> v & _y //~ (v^._x))++facingY :: Deformation R2+facingY = Deformation (\v -> v & _x //~ (v^._y))
src/Diagrams/TwoD/Ellipse.hs view
@@ -22,6 +22,8 @@ , ellipseXY ) where +import Data.AdditiveGroup+ import Diagrams.Core import Diagrams.Located (at)@@ -33,7 +35,7 @@ -- | A circle of radius 1, with center at the origin. unitCircle :: (TrailLike t, V t ~ R2) => t-unitCircle = trailLike $ arcT 0 (tau::Rad) `at` (p2 (1,0))+unitCircle = trailLike $ arcT zeroV fullTurn `at` (p2 (1,0)) -- | A circle of the given radius, centered at the origin. As a path, -- it begins at (r,0).
src/Diagrams/TwoD/Image.hs view
@@ -1,7 +1,8 @@-{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE DeriveDataTypeable #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE TemplateHaskell #-}+{-# LANGUAGE TypeFamilies #-} ----------------------------------------------------------------------------- -- | -- Module : Diagrams.TwoD.Image@@ -19,19 +20,20 @@ , image ) where -import Control.Lens (makeLenses)+import Control.Lens (makeLenses)+import Data.Typeable -import Diagrams.Core+import Diagrams.Core -import Diagrams.Path-import Diagrams.TwoD.Types-import Diagrams.TwoD.Path-import Diagrams.TwoD.Shapes-import Diagrams.TwoD.Size (SizeSpec2D(..))+import Diagrams.Path+import Diagrams.TwoD.Path+import Diagrams.TwoD.Shapes+import Diagrams.TwoD.Size (SizeSpec2D (..))+import Diagrams.TwoD.Types -import Data.AffineSpace ((.-.))+import Data.AffineSpace ((.-.)) -import Data.Semigroup+import Data.Semigroup -- | An external image primitive, representing an image the backend -- should import from another file when rendering.@@ -39,6 +41,7 @@ , _imgSize :: SizeSpec2D , _imgTransf :: T2 }+ deriving Typeable makeLenses ''Image
src/Diagrams/TwoD/Offset.hs view
@@ -38,9 +38,10 @@ ) where import Control.Applicative-import Control.Lens hiding (at, moveTo)+import Control.Lens hiding (at) import Data.AffineSpace+import Data.Maybe (catMaybes) import Data.Monoid import Data.Monoid.Inf import Data.VectorSpace@@ -263,9 +264,10 @@ -- loop. -> Located (Trail R2) -> Located (Trail R2)-offsetTrail' opts r t = joinSegments j isLoop (opts^.offsetMiterLimit) r ends . offset $ t+offsetTrail' opts r t = joinSegments eps j isLoop (opts^.offsetMiterLimit) r ends . offset $ t where- offset = map (bindLoc (offsetSegment (opts^.offsetEpsilon) r)) . locatedTrailSegments+ eps = opts^.offsetEpsilon+ offset = map (bindLoc (offsetSegment eps r)) . locatedTrailSegments ends | isLoop = (\(a:as) -> as ++ [a]) . trailVertices $ t | otherwise = tail . trailVertices $ t j = fromLineJoin (opts^.offsetJoin)@@ -356,8 +358,10 @@ expandLine :: ExpandOpts -> Double -> Located (Trail' Line R2) -> Located (Trail R2) expandLine opts r (mapLoc wrapLine -> t) = caps cap r s e (f r) (f $ -r) where- offset r' = map (bindLoc (offsetSegment (opts^.expandEpsilon) r')) . locatedTrailSegments- f r' = joinSegments (fromLineJoin (opts^.expandJoin)) False (opts^.expandMiterLimit) r' ends . offset r' $ t+ eps = opts^.expandEpsilon+ offset r' = map (bindLoc (offsetSegment eps r')) . locatedTrailSegments+ f r' = joinSegments eps (fromLineJoin (opts^.expandJoin)) False (opts^.expandMiterLimit) r' ends + . offset r' $ t ends = tail . trailVertices $ t s = atStart t e = atEnd t@@ -366,8 +370,10 @@ expandLoop :: ExpandOpts -> Double -> Located (Trail' Loop R2) -> Path R2 expandLoop opts r (mapLoc wrapLoop -> t) = (trailLike $ f r) <> (trailLike . reverseDomain . f $ -r) where- offset r' = map (bindLoc (offsetSegment (opts^.expandEpsilon) r')) . locatedTrailSegments- f r' = joinSegments (fromLineJoin (opts^.expandJoin)) True (opts^.expandMiterLimit) r' ends . offset r' $ t+ eps = opts^.expandEpsilon+ offset r' = map (bindLoc (offsetSegment eps r')) . locatedTrailSegments+ f r' = joinSegments eps (fromLineJoin (opts^.expandJoin)) True (opts^.expandMiterLimit) r' ends + . offset r' $ t ends = (\(a:as) -> as ++ [a]) . trailVertices $ t -- | Expand a 'Trail' with the given radius and default options. See 'expandTrail''.@@ -456,10 +462,10 @@ -- Arc helpers arcV :: (TrailLike t, V t ~ R2) => R2 -> R2 -> t-arcV u v = arc (direction u) (direction v :: Turn)+arcV u v = arc (direction u) (direction v) arcVCW :: (TrailLike t, V t ~ R2) => R2 -> R2 -> t-arcVCW u v = arcCW (direction u) (direction v :: Turn)+arcVCW u v = arcCW (direction u) (direction v) -- | Join together a list of located trails with the given join style. The@@ -470,20 +476,25 @@ -- Note: this is not a general purpose join and assumes that we are joining an -- offset trail. For instance, a fixed radius arc will not fit between arbitrary -- trails without trimming or extending.-joinSegments :: (Double -> Double -> P2 -> Located (Trail R2) -> Located (Trail R2) -> Trail R2)+joinSegments :: Double+ -> (Double -> Double -> P2 -> Located (Trail R2) -> Located (Trail R2) -> Trail R2) -> Bool -> Double -> Double -> [Point R2] -> [Located (Trail R2)] -> Located (Trail R2)-joinSegments _ _ _ _ _ [] = mempty `at` origin-joinSegments _ _ _ _ [] _ = mempty `at` origin-joinSegments j isLoop ml r es ts@(t:_) = t'+joinSegments _ _ _ _ _ _ [] = mempty `at` origin+joinSegments _ _ _ _ _ [] _ = mempty `at` origin+joinSegments epsilon j isLoop ml r es ts@(t:_) = t' where- t' | isLoop = mapLoc (glueTrail . (<> mconcat (take (length ts) $ ss es (ts ++ [t])))) t- | otherwise = mapLoc (<> mconcat (ss es ts)) t- ss es' ts' = [j ml r e a b <> unLoc b | (e,(a,b)) <- zip es' . (zip <*> tail) $ ts']+ t' | isLoop = mapLoc (glueTrail . (<> f (take (length ts * 2 - 1) $ ss es (ts ++ [t])))) t+ | otherwise = mapLoc (<> f (ss es ts)) t+ ss es' ts' = concat [[test a b $ j ml r e a b, Just $ unLoc b] | (e,(a,b)) <- zip es' . (zip <*> tail) $ ts']+ test a b tj+ | atStart b `distance` atEnd a > epsilon = Just tj+ | otherwise = Nothing+ f = mconcat . catMaybes -- | Take a join style and give the join function to be used by joinSegments. fromLineJoin
src/Diagrams/TwoD/Path.hs view
@@ -41,13 +41,13 @@ -- * Clipping - , Clip(..), clipBy+ , Clip(..), clipBy, clipTo, clipped ) where import Control.Applicative (liftA2) import Control.Lens ( makeWrapped, makeLensesWith, (.~), (^.) , generateSignatures, lensRules, op- , Lens, Lens', unwrapped)+ , Lens, Lens', _Wrapped') import qualified Data.Foldable as F import Data.Semigroup import Data.Typeable@@ -56,8 +56,10 @@ import Data.Default.Class import Data.VectorSpace +import Diagrams.Combinators (withEnvelope, withTrace) import Diagrams.Coordinates import Diagrams.Core+import Diagrams.Core.Trace import Diagrams.Located (Located, mapLoc, unLoc) import Diagrams.Parametric import Diagrams.Path@@ -167,12 +169,12 @@ -- -- * Names can be assigned to the path's vertices ----- 'StrokeOpts' is an instance of 'Default', so @stroke' 'with' {--- ... }@ syntax may be used.+-- 'StrokeOpts' is an instance of 'Default', so @stroke' ('with' &+-- ... )@ syntax may be used. stroke' :: (Renderable (Path R2) b, IsName a) => StrokeOpts a -> Path R2 -> Diagram b R2 stroke' opts path- | null (pLines ^. unwrapped) = mkP pLoops- | null (pLoops ^. unwrapped) = mkP pLines+ | null (pLines ^. _Wrapped') = mkP pLoops+ | null (pLoops ^. _Wrapped') = mkP pLines | otherwise = mkP pLines <> mkP pLoops where (pLines,pLoops) = partitionPath (isLine . unLoc) path@@ -361,7 +363,30 @@ clipBy :: (HasStyle a, V a ~ R2) => Path R2 -> a -> a clipBy = applyTAttr . Clip . (:[]) --- XXX Should include a 'clipTo' function which clips a diagram AND--- restricts its envelope. It will have to take a *pointwise minimum*--- of the diagram's current envelope and the path's envelope. Not--- sure of the best way to do this at the moment.+-- | Clip a diagram to the given path setting its envelope to the+-- pointwise minimum of the envelopes of the diagram and path. The+-- trace consists of those parts of the original diagram's trace+-- which fall within the clipping path, or parts of the path's trace+-- within the original diagram.+clipTo :: (Renderable (Path R2) b) => Path R2 -> Diagram b R2 -> Diagram b R2+clipTo p d = setTrace intersectionTrace . toEnvelope $ clipBy p d+ where+ envP = appEnvelope . getEnvelope $ p+ envD = appEnvelope . getEnvelope $ d+ toEnvelope = case (envP, envD) of+ (Just eP, Just eD) -> setEnvelope . mkEnvelope $ \v -> min (eP v) (eD v)+ (_, _) -> id+ intersectionTrace = Trace intersections+ intersections pt v =+ -- on boundary of d, inside p+ onSortedList (filter pInside) (appTrace (getTrace d) pt v) <>+ -- or on boundary of p, inside d+ onSortedList (filter dInside) (appTrace (getTrace p) pt v) where+ newPt dist = pt .+^ v ^* dist+ pInside dDist = runFillRule Winding (newPt dDist) p+ dInside pDist = getAny . sample d $ newPt pDist++-- | Clip a diagram to the clip path taking the envelope and trace of the clip+-- path.+clipped :: (Renderable (Path R2) b) => Path R2 -> Diagram b R2 -> Diagram b R2+clipped p = (withTrace p) . (withEnvelope p) . (clipBy p)
src/Diagrams/TwoD/Polygons.hs view
@@ -45,7 +45,8 @@ ) where -import Control.Lens hiding (transform, at, (<.>), (&), (#))+import Control.Lens (Lens', generateSignatures, lensRules,+ makeLensesWith, (.~), (^.), view) import Control.Monad (forM, liftM) import Control.Monad.ST (ST, runST) import Data.Array.ST (STUArray, newArray, readArray,@@ -57,8 +58,7 @@ import Data.AffineSpace ((.+^), (.-.)) import Data.Default.Class-import Data.VectorSpace (magnitude, normalized, project, (<.>),- (^*))+import Data.VectorSpace import Diagrams.Core import Diagrams.Located@@ -72,7 +72,7 @@ import Diagrams.Util (tau, ( # )) -- | Method used to determine the vertices of a polygon.-data PolyType = forall a. Angle a => PolyPolar [a] [Double]+data PolyType = PolyPolar [Angle] [Double] -- ^ A \"polar\" polygon. -- -- * The first argument is a list of /central/@@ -89,7 +89,7 @@ -- circle) can be constructed using a second -- argument of @(repeat r)@. - | forall a. Angle a => PolySides [a] [Double]+ | PolySides [Angle] [Double] -- ^ A polygon determined by the distance between -- successive vertices and the angles formed by -- each three successive vertices. In other@@ -180,7 +180,7 @@ -- | Generate the located trail of a polygon specified by polar data -- (central angles and radii). See 'PolyPolar'.-polyPolarTrail :: Angle a => [a] -> [Double] -> Located (Trail R2)+polyPolarTrail :: [Angle] -> [Double] -> Located (Trail R2) polyPolarTrail [] _ = emptyTrail `at` origin polyPolarTrail _ [] = emptyTrail `at` origin polyPolarTrail ans (r:rs) = tr `at` p1@@ -189,16 +189,16 @@ tr = closeTrail . trailFromVertices $ zipWith (\a l -> rotate a . scale l $ p2 (1,0))- (scanl (+) 0 ans)+ (scanl (^+^) zeroV ans) (r:rs) -- | Generate the vertices of a polygon specified by side length and -- angles, and a starting point for the trail such that the origin -- is at the centroid of the vertices. See 'PolySides'.-polySidesTrail :: Angle a => [a] -> [Double] -> Located (Trail R2)+polySidesTrail :: [Angle] -> [Double] -> Located (Trail R2) polySidesTrail ans ls = tr `at` (centroid ps # scale (-1)) where- ans' = scanl (+) 0 ans+ ans' = scanl (^+^) zeroV ans offsets = zipWith rotate ans' (map (unitY ^*) ls) ps = scanl (.+^) origin offsets tr = closeTrail . trailFromOffsets $ offsets@@ -206,7 +206,7 @@ -- | Generate the vertices of a regular polygon. See 'PolyRegular'. polyRegularTrail :: Int -> Double -> Located (Trail R2) polyRegularTrail n r = polyPolarTrail- (take (n-1) . repeat $ (tau::Rad) / fromIntegral n)+ (take (n-1) . repeat $ fullTurn ^/ fromIntegral n) (repeat r) -- | Generate a transformation to orient a trail. @orient v t@@@ -223,17 +223,21 @@ (zip3 (tail (cycle xs)) xs (last xs : init xs)) distAlong w ((.-. origin) -> p) = signum (w <.> p) * magnitude (project w p) sndOf3 (_,b,_) = b- a = minimumBy (comparing abs) . map (angleFromNormal . (.-. x)) $ [n1,n2]+ a :: Angle+ a = minimumBy (comparing $ abs . view rad)+ . map (angleFromNormal . (.-. x)) $ [n1,n2] v' = normalized v+ angleFromNormal :: R2 -> Angle angleFromNormal o | leftTurn o' v' = phi- | otherwise = negate phi+ | otherwise = negateV phi where o' = normalized o theta = acos (v' <.> o')+ phi :: Angle phi- | theta <= tau/4 = Rad $ tau/4 - theta- | otherwise = Rad $ theta - tau/4+ | theta <= tau/4 = tau/4 - theta @@ rad+ | otherwise = theta - tau/4 @@ rad ------------------------------------------------------------ -- Function graphs
src/Diagrams/TwoD/Segment.hs view
@@ -24,7 +24,6 @@ import Control.Applicative (liftA2) import Data.AffineSpace-import Data.Monoid.Inf hiding (minimum) import Data.VectorSpace import Diagrams.Core@@ -38,6 +37,10 @@ import Diagrams.TwoD.Vector import Diagrams.Util +{- All instances of Traced should maintain the invariant that the list of+ traces is sorted in increasing order.+-}+ instance Traced (Segment Closed R2) where getTrace = getTrace . mkFixedSeg . (`at` origin) @@ -73,8 +76,8 @@ t1 = (perp v0 <.> p) / det in if det == 0 || t0 < 0 || t0 > 1- then Infinity- else Finite t1+ then mkSortedList []+ else mkSortedList [t1] {- To do intersection of a line with a cubic Bezier, we first rotate and scale everything so that the line has parameters (origin, unitX);@@ -90,7 +93,7 @@ let bez'@(FCubic x1 c1 c2 x2) = bez # moveOriginTo p1- # rotateBy (negate (direction v1))+ # rotate (negateV (direction v1)) # scale (1/magnitude v1) [y0,y1,y2,y3] = map (snd . unp2) [x1,c1,c2,x2] a = -y0 + 3*y1 - 3*y2 + y3@@ -100,7 +103,5 @@ ts = filter (liftA2 (&&) (>= 0) (<= 1)) (cubForm a b c d) xs = map (fst . unp2 . atParam bez') ts in- case xs of- [] -> Infinity- _ -> Finite (minimum xs)+ mkSortedList xs
src/Diagrams/TwoD/Shapes.hs view
@@ -59,7 +59,7 @@ import Diagrams.Util -import Control.Lens (makeLenses, op, (.~), (^.), (&))+import Control.Lens (makeLenses, op, (&), (.~), (^.)) import Data.Default.Class import Data.Semigroup @@ -140,7 +140,7 @@ regPoly :: (TrailLike t, V t ~ R2) => Int -> Double -> t regPoly n l = polygon (def & polyType .~ PolySides- (repeat (1/ fromIntegral n :: Turn))+ (repeat (1/fromIntegral n @@ turn)) (replicate (n-1) l) & polyOrient .~ OrientH )@@ -311,4 +311,4 @@ mkCorner k r | r == 0 = mempty | r < 0 = doArc 3 2 | otherwise = doArc 0 1- where doArc d d' = arc' r ((k+d)/4) ((k+d')/4:: Turn)+ where doArc d d' = arc' r ((k+d)/4 @@ turn) ((k+d')/4 @@ turn)
src/Diagrams/TwoD/Text.hs view
@@ -54,6 +54,7 @@ -- vector space of the text to the vector space in which it is -- embedded. data Text = Text T2 TextAlignment String+ deriving Typeable type instance V Text = R2
src/Diagrams/TwoD/Transform.hs view
@@ -59,50 +59,52 @@ import Diagrams.TwoD.Vector (direction) import Diagrams.Coordinates +import Data.AdditiveGroup import Data.AffineSpace import Data.Semigroup+import Control.Lens (review, (^.)) -- Rotation ------------------------------------------------ -- | Create a transformation which performs a rotation about the local -- origin by the given angle. See also 'rotate'.-rotation :: Angle a => a -> T2+rotation :: Angle -> T2 rotation ang = fromLinear r (linv r) where r = rot theta <-> rot (-theta)- Rad theta = convertAngle ang+ theta = ang^.rad rot th (coords -> x :& y) = (cos th * x - sin th * y) ^& (sin th * x + cos th * y) -- | Rotate about the local origin by the given angle. Positive angles -- correspond to counterclockwise rotation, negative to--- clockwise. The angle can be expressed using any type which is an--- instance of 'Angle'. For example, @rotate (1\/4 ::--- 'Turn')@, @rotate (tau\/4 :: 'Rad')@, and @rotate (90 ::--- 'Deg')@ all represent the same transformation, namely, a--- counterclockwise rotation by a right angle. To rotate about some--- point other than the local origin, see 'rotateAbout'.+-- clockwise. The angle can be expressed using any of the 'Iso's on+-- 'Angle'. For example, @rotate (1\/4 \@\@ 'turn')@, @rotate+-- (tau\/4 \@\@ rad)@, and @rotate (90 \@\@ deg)@ all+-- represent the same transformation, namely, a counterclockwise+-- rotation by a right angle. To rotate about some point other than+-- the local origin, see 'rotateAbout'. ----- Note that writing @rotate (1\/4)@, with no type annotation, will--- yield an error since GHC cannot figure out which sort of angle--- you want to use. In this common situation you can use--- 'rotateBy', which is specialized to take a 'Turn' argument.-rotate :: (Transformable t, V t ~ R2, Angle a) => a -> t -> t+-- Note that writing @rotate (1\/4)@, with no 'Angle' constructor,+-- will yield an error since GHC cannot figure out which sort of+-- angle you want to use. In this common situation you can use+-- 'rotateBy', which interprets its argument as a number of turns.+rotate :: (Transformable t, V t ~ R2) => Angle -> t -> t rotate = transform . rotation --- | A synonym for 'rotate', specialized to only work with--- @Turn@ arguments; it can be more convenient to write--- @rotateBy (1\/4)@ than @'rotate' (1\/4 :: 'Turn')@.-rotateBy :: (Transformable t, V t ~ R2) => Turn -> t -> t-rotateBy = transform . rotation+-- | A synonym for 'rotate', interpreting its argument in units of+-- turns; it can be more convenient to write @rotateBy (1\/4)@ than+-- @'rotate' (1\/4 \@\@ 'turn')@.+rotateBy :: (Transformable t, V t ~ R2) => Double -> t -> t+rotateBy = transform . rotation . review turn -- | @rotationAbout p@ is a rotation about the point @p@ (instead of -- around the local origin).-rotationAbout :: Angle a => P2 -> a -> T2+rotationAbout :: P2 -> Angle -> T2 rotationAbout p angle = conjugate (translation (origin .-. p)) (rotation angle) -- | @rotateAbout p@ is like 'rotate', except it rotates around the -- point @p@ instead of around the local origin.-rotateAbout :: (Transformable t, V t ~ R2, Angle a) => P2 -> a -> t -> t+rotateAbout :: (Transformable t, V t ~ R2) => P2 -> Angle -> t -> t rotateAbout p angle = rotate angle `under` translation (origin .-. p) -- Scaling -------------------------------------------------@@ -203,7 +205,7 @@ -- the point @p@ and vector @v@. reflectionAbout :: P2 -> R2 -> T2 reflectionAbout p v =- conjugate (rotation (-direction v :: Rad) <> translation (origin .-. p))+ conjugate (rotation (negateV $ direction v) <> translation (origin .-. p)) reflectionY -- | @reflectAbout p v@ reflects a diagram in the line determined by
src/Diagrams/TwoD/Transform/ScaleInv.hs view
@@ -1,3 +1,4 @@+{-# LANGUAGE DeriveDataTypeable #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE TemplateHaskell #-}@@ -20,8 +21,10 @@ where import Control.Lens (makeLenses, view)+import Data.AdditiveGroup import Data.AffineSpace ((.-.)) import Data.Semigroup+import Data.Typeable import Diagrams.Core import Diagrams.TwoD.Transform@@ -61,11 +64,11 @@ data ScaleInv t = ScaleInv- { _scaleInvObj :: t+ { _scaleInvObj :: t , _scaleInvDir :: R2 , _scaleInvLoc :: P2 }- deriving (Show)+ deriving (Show, Typeable) makeLenses ''ScaleInv @@ -82,8 +85,7 @@ instance (V t ~ R2, Transformable t) => Transformable (ScaleInv t) where transform tr (ScaleInv t v l) = ScaleInv (trans . rot $ t) (rot v) l' where- angle :: Rad- angle = direction (transform tr v) - direction v+ angle = direction (transform tr v) ^-^ direction v rot :: (Transformable t, V t ~ R2) => t -> t rot = rotateAbout l angle l' = transform tr l@@ -184,6 +186,6 @@ -- scale-invariant things will be used only as \"decorations\" (/e.g./ -- arrowheads) which should not affect the envelope, trace, and -- query.-scaleInvPrim :: (Transformable t, Renderable t b, V t ~ R2, Monoid m)+scaleInvPrim :: (Transformable t, Typeable t, Renderable t b, V t ~ R2, Monoid m) => t -> R2 -> QDiagram b R2 m scaleInvPrim t d = mkQD (Prim $ scaleInv t d) mempty mempty mempty mempty
src/Diagrams/TwoD/Types.hs view
@@ -2,7 +2,7 @@ {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-} {-# LANGUAGE MultiParamTypeClasses #-}-{-# LANGUAGE TemplateHaskell #-}+{-# LANGUAGE RankNTypes #-} {-# LANGUAGE TypeFamilies #-} {-# LANGUAGE TypeOperators #-} {-# LANGUAGE TypeSynonymInstances #-}@@ -26,25 +26,21 @@ , T2 -- * Angles- , Angle(..)-- , Turn(..), asTurn, CircleFrac- , Rad(..), asRad- , Deg(..), asDeg- , fullTurn, fullCircle, convertAngle, angleRatio+ , Angle+ , rad, turn, deg+ , fullTurn, fullCircle, angleRatio+ , (@@) ) where -import Control.Lens (Iso', Wrapped, iso, makeWrapped, op,- wrapped, _1, _2)+import Control.Lens (Iso', Wrapped(..), Rewrapped, iso+ , review , (^.), _1, _2) import Diagrams.Coordinates import Diagrams.Core-import Diagrams.Util (tau) import Data.AffineSpace.Point import Data.Basis import Data.MemoTrie (HasTrie (..))-import Data.NumInstances.Tuple () import Data.VectorSpace import Data.Typeable@@ -136,10 +132,13 @@ mkR2 = curry r2 -- | Lens wrapped isomorphisms for R2.-instance Wrapped (Double, Double) (Double, Double) R2 R2 where- wrapped = iso r2 unr2- {-# INLINE wrapped #-}+instance Wrapped R2 where+ type Unwrapped R2 = (Double, Double)+ _Wrapped' = iso unr2 r2+ {-# INLINE _Wrapped' #-} +instance Rewrapped R2 R2+ type instance V R2 = R2 instance VectorSpace R2 where@@ -239,98 +238,52 @@ ------------------------------------------------------------ -- Angles --- | Newtype wrapper used to represent angles as fractions of a--- circle. For example, 1\/3 turn = tau\/3 radians = 120 degrees.-newtype Turn = Turn Double- deriving (Read, Show, Eq, Ord, Enum, Fractional, Num, Real, RealFrac, AdditiveGroup)--makeWrapped ''Turn--instance VectorSpace Turn where- type Scalar Turn = Double- s *^ Turn t = Turn (s*t)---- | The identity function with a restricted type, for conveniently--- declaring that some value should have type 'Turn'. For example,--- @rotation . asTurn . fromRational@ constructs a rotation from a--- rational value considered as a @Turn@. Without @asTurn@, the angle--- type would be ambiguous.-asTurn :: Turn -> Turn-asTurn = id---- | Deprecated synonym for 'Turn', retained for backwards compatibility.-type CircleFrac = Turn---- | Newtype wrapper for representing angles in radians.-newtype Rad = Rad Double- deriving (Read, Show, Eq, Ord, Enum, Floating, Fractional, Num, Real, RealFloat, RealFrac, AdditiveGroup)--makeWrapped ''Rad--instance VectorSpace Rad where- type Scalar Rad = Double- s *^ Rad r = Rad (s*r)---- | The identity function with a restricted type, for conveniently--- declaring that some value should have type 'Rad'. For example,--- @rotation . asRad . fromRational@ constructs a rotation from a--- rational value considered as a value in radians. Without @asRad@,--- the angle type would be ambiguous.-asRad :: Rad -> Rad-asRad = id---- | Newtype wrapper for representing angles in degrees.-newtype Deg = Deg Double- deriving (Read, Show, Eq, Ord, Enum, Fractional, Num, Real, RealFrac, AdditiveGroup)--makeWrapped ''Deg--instance VectorSpace Deg where- type Scalar Deg = Double- s *^ Deg d = Deg (s*d)---- | The identity function with a restricted type, for conveniently--- declaring that some value should have type 'Deg'. For example,--- @rotation . asDeg . fromIntegral@ constructs a rotation from an--- integral value considered as a value in degrees. Without @asDeg@,--- the angle type would be ambiguous.-asDeg :: Deg -> Deg-asDeg = id---- | Type class for types that measure angles.-class Num a => Angle a where- -- | Convert to a turn, /i.e./ a fraction of a circle.- toTurn :: a -> Turn+-- | Angles can be expressed in a variety of units. Internally,+-- they are represented in radians.+newtype Angle = Radians Double+ deriving (Read, Show, Eq, Ord, Enum, AdditiveGroup) - -- | Convert from a turn, /i.e./ a fraction of a circle.- fromTurn :: Turn -> a+instance VectorSpace Angle where+ type Scalar Angle = Double+ s *^ Radians t = Radians (s*t) -instance Angle Turn where- toTurn = id- fromTurn = id+-- | The radian measure of an @Angle@ @a@ can be accessed as @a+-- ^. rad@. A new @Angle@ can be defined in radians as @pi \@\@ rad@.+rad :: Iso' Angle Double+rad = iso (\(Radians r) -> r) Radians --- | tau radians = 1 full turn.-instance Angle Rad where- toTurn = Turn . (/tau) . op Rad- fromTurn = Rad . (*tau) . op Turn+-- | The measure of an @Angle@ @a@ in full circles can be accessed as+-- @a ^. turn@. A new @Angle@ of one-half circle can be defined in as+-- @1/2 \@\@ turn@.+turn :: Iso' Angle Double+turn = iso (\(Radians r) -> r/2/pi) (Radians . (*(2*pi))) --- | 360 degrees = 1 full turn.-instance Angle Deg where- toTurn = Turn . (/360) . op Deg- fromTurn = Deg . (*360) . op Turn+-- | The degree measure of an @Angle@ @a@ can be accessed as @a+-- ^. deg@. A new @Angle@ can be defined in degrees as @180 \@\@+-- deg@.+deg :: Iso' Angle Double+deg = iso (\(Radians r) -> r/2/pi*360) (Radians . (*(2*pi/360))) -- | An angle representing one full turn.-fullTurn :: Angle a => a-fullTurn = fromTurn 1+fullTurn :: Angle+fullTurn = 1 @@ turn -- | Deprecated synonym for 'fullTurn', retained for backwards compatibility.-fullCircle :: Angle a => a+fullCircle :: Angle fullCircle = fullTurn --- | Convert between two angle representations.-convertAngle :: (Angle a, Angle b) => a -> b-convertAngle = fromTurn . toTurn+-- | Calculate ratio between two angles.+angleRatio :: Angle -> Angle -> Double+angleRatio a b = (a^.rad) / (b^.rad) --- | Calculate ratio between two angles-angleRatio :: Angle a => a -> a -> Double-angleRatio a b = op Turn (toTurn a) / op Turn (toTurn b)++-- | @30 \@\@ deg@ is an @Angle@ of the given measure and units.+--+-- More generally, @\@\@@ reverses the @Iso\'@ on its right, and+-- applies the @Iso\'@ to the value on the left. @Angle@s are the+-- motivating example where this order improves readability.+(@@) :: b -> Iso' a b -> a+-- The signature above is slightly specialized, in favor of readability+a @@ i = review i a++infixl 5 @@
src/Diagrams/TwoD/Vector.hs view
@@ -22,6 +22,8 @@ , perp, leftTurn ) where +import Control.Lens ((^.))+import Data.AdditiveGroup import Data.VectorSpace ((<.>)) import Diagrams.TwoD.Types import Diagrams.Coordinates@@ -45,25 +47,25 @@ -- | Compute the direction of a vector, measured counterclockwise from -- the positive x-axis as a fraction of a full turn. The zero -- vector is arbitrarily assigned the direction 0.-direction :: Angle a => R2 -> a-direction (coords -> x :& y) = convertAngle . Rad $ atan2 y x+direction :: R2 -> Angle+direction (coords -> x :& y) = atan2 y x @@ rad -- | Compute the counterclockwise angle from the first vector to the second.-angleBetween :: (Angle a, Num a, Ord a) => R2 -> R2 -> a+angleBetween :: R2 -> R2 -> Angle angleBetween v1 v2- | d2 > d1 = d2 - d1- | otherwise = fullTurn + d2 - d1+ | d2 > d1 = d2 ^-^ d1+ | otherwise = fullTurn ^+^ d2 ^-^ d1 where d1 = direction v1 d2 = direction v2 -- | Convert an angle into a unit vector pointing in that direction.-fromDirection :: Angle a => a -> R2+fromDirection :: Angle -> R2 fromDirection a = cos a' ^& sin a'- where Rad a' = convertAngle a+ where a' = a^.rad -- | A convenient synonym for 'fromDirection'.-e :: Angle a => a -> R2+e :: Angle -> R2 e = fromDirection -- | @perp v@ is perpendicular to and has the same magnitude as @v@.