diagrams-lib-1.1: src/Diagrams/TwoD/Segment.hs
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE UndecidableInstances #-}
{-# OPTIONS_GHC -fno-warn-orphans #-}
-- Orphan Traced instances for Segment Closed R2 and FixedSegment R2.
-- They can't go in Traced; but they shouldn't really go in
-- Diagrams.Segment either because we only have Traced instances for
-- the special case of R2.
-----------------------------------------------------------------------------
-- |
-- Module : Diagrams.TwoD.Segment
-- Copyright : (c) 2012 diagrams-lib team (see LICENSE)
-- License : BSD-style (see LICENSE)
-- Maintainer : diagrams-discuss@googlegroups.com
--
-- Segments in two dimensions are special since we may meaningfully
-- compute their point of intersection with a ray.
--
-----------------------------------------------------------------------------
module Diagrams.TwoD.Segment where
import Control.Applicative (liftA2)
import Data.AffineSpace
import Data.VectorSpace
import Diagrams.Core
import Diagrams.Located
import Diagrams.Parametric
import Diagrams.Segment
import Diagrams.Solve
import Diagrams.TwoD.Transform
import Diagrams.TwoD.Types
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)
instance Traced (FixedSegment R2) where
{- Given lines defined by p0 + t0 * v0 and p1 + t1 * v1, their point of
intersection in 2D is given by
t_i = (v_(1-i)^ . (p1 - p0)) / (v1^ . v0)
where v^ denotes the perpendicular to v, i.e. v rotated by
-tau/4.
This can be derived by starting with the parametric equation
p0 + v0 t0 = p1 + v1 t1
and rearranging to get the matrix equation
[v0 -v1] [ t0 ] = (p1 - p0)
[ t1 ]
Working out the product of the inverse of [v0 -v1] with (p1 - p0)
results in the above formulas for t_i.
-}
getTrace (FLinear p0 p0') = mkTrace $ \p1 v1 ->
let
v0 = p0' .-. p0
det = perp v1 <.> v0
p = p1 .-. p0
t0 = (perp v1 <.> p) / det
t1 = (perp v0 <.> p) / det
in
if det == 0 || t0 < 0 || t0 > 1
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);
then we find the intersection(s) of the Bezier with the x-axis.
XXX could we speed this up by first checking whether all the
control point y-coordinates lie on the same side of the x-axis (if so,
there can't possibly be any intersections)? Need to set up some
benchmarks.
-}
getTrace bez@(FCubic {}) = mkTrace $ \p1 v1 ->
let
bez'@(FCubic x1 c1 c2 x2) =
bez # moveOriginTo p1
# 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
b = 3*y0 - 6*y1 + 3*y2
c = -3*y0 + 3*y1
d = y0
ts = filter (liftA2 (&&) (>= 0) (<= 1)) (cubForm a b c d)
xs = map (fst . unp2 . atParam bez') ts
in
mkSortedList xs