Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add arcCCW and friends. Fix offset joins. #221

Merged
merged 6 commits into from
Nov 8, 2014
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 5 additions & 4 deletions src/Diagrams/Angle.hs
Original file line number Diff line number Diff line change
Expand Up @@ -28,16 +28,17 @@ module Diagrams.Angle
, asinA, acosA, atanA, atan2A, atan2A'

-- ** Angle utilities
, angleBetween, angleRatio
, angleBetween, angleRatio, normalizeAngle

-- ** Classes
, HasTheta(..)
, HasPhi(..)
) where

import Control.Applicative
import Control.Lens (Iso', Lens', iso, review, (^.))
import Control.Lens (Iso', Lens', iso, review, (^.), over)
import Data.Monoid hiding ((<>))
import Data.Fixed
import Data.Semigroup

import Diagrams.Core.V
Expand Down Expand Up @@ -172,8 +173,8 @@ angleBetween v1 v2 = acosA (signorm v1 `dot` signorm v2)
-- N.B.: Currently discards the common plane information.

-- | Normalize an angle so that it lies in the [0,tau) range.
-- normalizeAngle :: (Floating n, Real n) => Angle n -> Angle n
-- normalizeAngle = over rad (`mod'` (2 * pi))
normalizeAngle :: (Floating n, Real n) => Angle n -> Angle n
normalizeAngle = over rad (`mod'` (2 * pi))

------------------------------------------------------------
-- Polar Coordinates
Expand Down
34 changes: 25 additions & 9 deletions src/Diagrams/Direction.hs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
{-# LANGUAGE DeriveFunctor #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE UndecidableInstances #-}
Expand All @@ -15,15 +16,18 @@
module Diagrams.Direction
( Direction
, _Dir
, direction, fromDirection
, direction, dir, fromDirection, fromDir
, angleBetweenDirs
, dirBetween
) where

import Control.Lens (Iso', iso)

import Diagrams.Angle
import Diagrams.Core

import Linear.Affine
import Linear.Vector
import Linear.Metric

--------------------------------------------------------------------------------
Expand All @@ -33,14 +37,14 @@ import Linear.Metric
-- can think of a @Direction@ as a vector that has forgotten its
-- magnitude. @Direction@s can be used with 'fromDirection' and the
-- lenses provided by its instances.
newtype Direction v n = Direction (v n)
deriving (Read, Show, Eq, Ord) -- todo: special instances
newtype Direction v n = Dir (v n)
deriving (Read, Show, Eq, Ord, Functor) -- todo: special instances

type instance V (Direction v n) = v
type instance N (Direction v n) = n

instance (V (v n) ~ v, N (v n) ~ n, Transformable (v n)) => Transformable (Direction v n) where
transform t (Direction v) = Direction (transform t v)
transform t (Dir v) = Dir (transform t v)

instance HasTheta v => HasTheta (Direction v) where
_theta = _Dir . _theta
Expand All @@ -49,22 +53,34 @@ instance HasPhi v => HasPhi (Direction v) where
_phi = _Dir . _phi

-- | _Dir is provided to allow efficient implementations of functions
-- in particular vector-spaces, but should be used with care as it
-- exposes too much information.
-- in particular vector-spaces, but should be used with care as it
-- exposes too much information.
_Dir :: Iso' (Direction v n) (v n)
_Dir = iso (\(Direction v) -> v) Direction
_Dir = iso (\(Dir v) -> v) Dir

-- | @direction v@ is the direction in which @v@ points. Returns an
-- unspecified value when given the zero vector as input.
direction :: v n -> Direction v n
direction = Direction
direction = Dir

-- | Synonym for 'direction'.
dir :: v n -> Direction v n
dir = Dir

-- | @fromDirection d@ is the unit vector in the direction @d@.
fromDirection :: (Metric v, Floating n) => Direction v n -> v n
fromDirection (Direction v) = signorm v
fromDirection (Dir v) = signorm v

-- | Synonym for 'fromDirection'.
fromDir :: (Metric v, Floating n) => Direction v n -> v n
fromDir (Dir v) = signorm v

-- | compute the positive angle between the two directions in their common plane
angleBetweenDirs :: (Metric v, Floating n)
=> Direction v n -> Direction v n -> Angle n
angleBetweenDirs d1 d2 = angleBetween (fromDirection d1) (fromDirection d2)

-- | @dirBetween p q@ returns the directions from @p@ to @q@
dirBetween :: (Additive v, Num n) => Point v n -> Point v n -> Direction v n
dirBetween p q = dir $ p .-. q

45 changes: 34 additions & 11 deletions src/Diagrams/TwoD/Arc.hs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
{-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE ViewPatterns #-}
{-# LANGUAGE TypeFamilies #-}
-----------------------------------------------------------------------------
-- |
Expand All @@ -16,6 +17,10 @@ module Diagrams.TwoD.Arc
( arc
, arc'
, arcT

, arcCCW
, arcCW

, bezierFromSweep

, wedge
Expand All @@ -32,7 +37,7 @@ import Diagrams.Trail
import Diagrams.TrailLike
import Diagrams.TwoD.Transform
import Diagrams.TwoD.Types
import Diagrams.TwoD.Vector (unitX, unitY, unit_Y)
import Diagrams.TwoD.Vector (unitX, unitY, unit_Y, e)
import Diagrams.Util (( # ))

import Control.Lens ((&), (<>~), (^.))
Expand All @@ -50,11 +55,10 @@ import Linear.Vector
-- angle @s@. The approximation is only valid for angles in the
-- first quadrant.
bezierFromSweepQ1 :: Floating n => Angle n -> Segment Closed V2 n
bezierFromSweepQ1 s = mapSegmentVectors (^-^ v) . rotate (s ^/ 2) $ bezier3 c2 c1 p0
where p0@(V2 x y) = rotate (s ^/ 2) v
bezierFromSweepQ1 s = mapSegmentVectors (^-^ unitX) . rotate (s ^/ 2) $ bezier3 c2 c1 p0
where p0@(V2 x y) = e (s ^/ 2)
c1 = V2 ((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
Expand Down Expand Up @@ -94,17 +98,17 @@ the approximation error.
-- is the 'Trail' of a radius one arc starting at @d@ and sweeping out
-- the angle @s@ counterclockwise (for positive s). The resulting
-- @Trail@ is allowed to wrap around and overlap itself.
arcT :: RealFloat n => Direction V2 n -> Angle n -> Trail V2 n
arcT :: OrderedField n => Direction V2 n -> Angle n -> Trail V2 n
arcT start sweep = trailFromSegments bs
where
bs = map (rotate $ start ^. _theta) . bezierFromSweep $ sweep
bs = map (rotateTo start) . bezierFromSweep $ sweep

-- | Given a start direction @d@ and a sweep angle @s@, @'arc' d s@ is the
-- path of a radius one arc starting at @d@ and sweeping out the angle
-- @s@ counterclockwise (for positive s). The resulting
-- @Trail@ is allowed to wrap around and overlap itself.
arc :: (TrailLike t, V t ~ V2, N t ~ n, RealFloat n) => Direction V2 n -> Angle n -> t
arc start sweep = trailLike $ arcT start sweep `at` rotate (start ^. _theta) (p2 (1,0))
arc :: (InSpace V2 n t, OrderedField n, TrailLike t) => Direction V2 n -> Angle n -> t
arc start sweep = trailLike $ arcT start sweep `at` P (fromDirection start)

-- | Given a radus @r@, a start direction @d@ and an angle @s@,
-- @'arc'' r d s@ is the path of a radius @(abs r)@ arc starting at
Expand All @@ -115,10 +119,29 @@ arc start sweep = trailLike $ arcT start sweep `at` rotate (start ^. _theta) (p2
--
-- > arc'Ex = mconcat [ arc' r (0 @@ turn) (1/4 @@ turn) | r <- [0.5,-1,1.5] ]
-- > # centerXY # pad 1.1
arc' :: (TrailLike t, V t ~ V2, N t ~ n, RealFloat n) => n -> Direction V2 n -> Angle n -> t
arc' r start sweep = trailLike $ scale (abs r) ts `at` rotate (start ^. _theta) (p2 (abs r,0))
arc' :: (InSpace V2 n t, OrderedField n, TrailLike t) => n -> Direction V2 n -> Angle n -> t
arc' (abs -> r) start sweep = trailLike $ scale r ts `at` P (r *^ fromDirection start)
where ts = arcT start sweep

arcCCWT :: RealFloat n => Direction V2 n -> Direction V2 n -> Trail V2 n
arcCCWT start end = trailFromSegments bs
where
bs = map (rotateTo start) . bezierFromSweep $ sweep
sweep = normalizeAngle $ end ^. _theta ^-^ start ^. _theta

-- | Given a start direction @s@ and end direction @e@, @arcCCW s e@ is the
-- path of a radius one arc counterclockwise between the two directions.
-- The origin of the arc is its center.
arcCCW :: (InSpace V2 n t, RealFloat n, TrailLike t) => Direction V2 n -> Direction V2 n -> t
arcCCW start end = trailLike $ arcCCWT start end `at` P (fromDirection start)

-- | Like 'arcAngleCCW' but clockwise.
arcCW :: (InSpace V2 n t, RealFloat n, TrailLike t) => Direction V2 n -> Direction V2 n -> t
arcCW start end = trailLike $
-- flipped arguments to get the path we want
-- then reverse the trail to get the cw direction.
reverseTrail (arcCCWT end start) `at` P (fromDirection start)

-- | Create a circular wedge of the given radius, beginning at the
-- given direction and extending through the given angle.
--
Expand All @@ -131,7 +154,7 @@ arc' r start sweep = trailLike $ scale (abs r) ts `at` rotate (start ^. _theta)
-- > ]
-- > # fc blue
-- > # centerXY # pad 1.1
wedge :: (TrailLike t, V t ~ V2, N t ~ n, RealFloat n) => n -> Direction V2 n -> Angle n -> t
wedge :: (InSpace V2 n t, OrderedField n, TrailLike t) => n -> Direction V2 n -> Angle n -> t
wedge r d s = trailLike . (`at` origin) . glueTrail . wrapLine
$ fromOffsets [r *^ fromDirection d]
<> arc d s # scale r
Expand Down
2 changes: 1 addition & 1 deletion src/Diagrams/TwoD/Arrow.hs
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ import Diagrams.Core.Types (QDiaLeaf (..), mkQD')

import Diagrams.Angle
import Diagrams.Attributes
import Diagrams.Direction
import Diagrams.Direction hiding (dir)
import Diagrams.Located (Located(..), unLoc)
import Diagrams.Parametric
import Diagrams.Path
Expand Down
15 changes: 3 additions & 12 deletions src/Diagrams/TwoD/Offset.hs
Original file line number Diff line number Diff line change
Expand Up @@ -51,9 +51,8 @@ import Data.Default.Class

import Diagrams.Core

import Diagrams.Angle
import Diagrams.Attributes
import Diagrams.Direction (direction)
import Diagrams.Direction
import Diagrams.Located
import Diagrams.Parametric
import Diagrams.Path
Expand Down Expand Up @@ -470,16 +469,8 @@ capSquare _r c a b = unLoc $ fromVertices [ a, a .+^ v, b .+^ v, b ]
capArc :: RealFloat n => n -> Point V2 n -> Point V2 n -> Point V2 n -> Trail V2 n
capArc r c a b = trailLike . moveTo c $ fs
where
fs | r < 0 = scale (-r) $ arcVCW (a .-. c) (b .-. c)
| otherwise = scale r $ arcV (a .-. c) (b .-. c)

-- Arc helpers
-- always picks the shorter arc (< τ/2)
arcV :: (OrderedField n, RealFloat n, TrailLike t, V t ~ V2, N t ~ n) => V2 n -> V2 n -> t
arcV u v = arc (direction u) (angleBetween v u)

arcVCW :: (OrderedField n, RealFloat n, TrailLike t, V t ~ V2, N t ~ n) => V2 n -> V2 n -> t
arcVCW u v = arc (direction u) (negated $ angleBetween v u)
fs | r < 0 = scale (-r) $ arcCW (dirBetween a c) (dirBetween b c)
| otherwise = scale r $ arcCCW (dirBetween a c) (dirBetween b c)

-- | Join together a list of located trails with the given join style. The
-- style is given as a function to compute the join given the local information
Expand Down
12 changes: 7 additions & 5 deletions src/Diagrams/TwoD/Transform.hs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE ViewPatterns #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeOperators #-}
-----------------------------------------------------------------------------
Expand Down Expand Up @@ -53,11 +54,12 @@ module Diagrams.TwoD.Transform
import Diagrams.Angle
import Diagrams.Core
import Diagrams.Core.Transform
import Diagrams.Direction
import Diagrams.Transform
import Diagrams.TwoD.Types
import Diagrams.TwoD.Vector

import Control.Lens (review, (&), (*~), (.~), (//~))
import Control.Lens (review, view, (&), (*~), (.~), (//~))
import Data.Semigroup

import Linear.Affine
Expand Down Expand Up @@ -109,13 +111,13 @@ rotateAround :: (InSpace V2 n t, Transformable t, Floating n) => P2 n -> Angle n
rotateAround p angle = rotate angle `under` translation (origin .-. p)

-- | The rotation that aligns the x-axis with the given non-zero vector.
rotationTo :: OrderedField n => V2 n -> T2 n
rotationTo (V2 x y) = rotation (atan2A' y x)
rotationTo :: OrderedField n => Direction V2 n -> T2 n
rotationTo (view _Dir -> V2 x y) = rotation (atan2A' y x)
-- could be done with Direction

-- | Rotate around the local origin such that the x axis aligns with the
-- given direction.
rotateTo :: (InSpace V2 n t, OrderedField n, Transformable t) => V2 n -> t -> t
rotateTo :: (InSpace V2 n t, OrderedField n, Transformable t) => Direction V2 n -> t -> t
rotateTo = transform . rotationTo

-- Scaling -------------------------------------------------
Expand Down Expand Up @@ -216,7 +218,7 @@ reflectY = transform reflectionY
-- the point @p@ and vector @v@.
reflectionAbout :: OrderedField n => P2 n -> V2 n -> T2 n
reflectionAbout p v =
conjugate (rotationTo (negated v) <> translation (origin .-. p))
conjugate (rotationTo (direction $ negated v) <> translation (origin .-. p))
reflectionY

-- | @reflectAbout p v@ reflects a diagram in the line determined by
Expand Down
16 changes: 9 additions & 7 deletions src/Diagrams/TwoD/Vector.hs
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,10 @@ module Diagrams.TwoD.Vector
unitX, unitY, unit_X, unit_Y

-- * Converting between vectors and angles
, e, xDir
, e, xDir, angleDir

-- * 2D vector utilities
, perp, leftTurn
-- * Synonym for R2 things
) where

import Control.Lens (view, (&), (.~))
Expand Down Expand Up @@ -46,16 +45,19 @@ unitY = zero & _y .~ 1
unit_Y :: (R2 v, Additive v, Num n) => v n
unit_Y = zero & _y .~ (-1)

-- | The origin of the direction AffineSpace. For all d, @d .-. xDir
-- = d^._theta@.
-- | A 'Direction' pointing in the X direction.
xDir :: (R1 v, Additive v, Num n) => Direction v n
xDir = direction unitX
xDir = dir unitX

-- | A unit vector at a specified angle counterclockwise from the
-- positive X axis.
-- | A unit vector at a specified angle counter-clockwise from the
-- positive X axis.
e :: Floating n => Angle n -> V2 n
e = angle . view rad

-- | A direction at a specified angle counter-clockwise from the 'xDir'.
angleDir :: Floating n => Angle n -> Direction V2 n
angleDir = dir . e

-- | @leftTurn v1 v2@ tests whether the direction of @v2@ is a left
-- turn from @v1@ (that is, if the direction of @v2@ can be obtained
-- from that of @v1@ by adding an angle 0 <= theta <= tau/2).
Expand Down