{-# OPTIONS -Wall #-}

--------------------------------------------------------------------------------
-- |
-- Module      :  Wumpus.Drawing.Paths.ControlPoints
-- Copyright   :  (c) Stephen Tetley 2010
-- License     :  BSD3
--
-- Maintainer  :  Stephen Tetley <stephen.tetley@gmail.com>
-- Stability   :  highly unstable
-- Portability :  GHC
--
-- Collection of point manufacturing functions.
--
-- \*\* WARNING \*\* this module is experimental and may change 
-- significantly in future revisions.
-- 
--------------------------------------------------------------------------------

module Wumpus.Drawing.Paths.ControlPoints
  ( 

    midpointIsosceles
  , dblpointIsosceles

  , rectangleFromBasePoints
  , squareFromBasePoints
  , usquareFromBasePoints

  , trapezoidFromBasePoints

  , squareFromCornerPoints

  ) where

import Wumpus.Basic.Kernel

import Wumpus.Core                              -- package: wumpus-core

import Data.AffineSpace                         -- package: vector-space




-- | 'midpointIsosceles' : 
-- @ altitude * start_pt * end_pt -> mid_pt @
--
-- Triangular midpoint.
-- 
-- @u@ is the altitude of the triangle - negative values of u 
-- form the triangle below the line.
--
midpointIsosceles :: (Real u, Floating u) 
                  => u -> Point2 u -> Point2 u -> Point2 u
midpointIsosceles u p1@(P2 x1 y1) p2@(P2 x2 y2) = 
    mid_pt .+^ avec perp_ang u
  where
    mid_pt    = P2 (x1 + 0.5*(x2-x1)) (y1 + 0.5*(y2-y1))
    perp_ang  = (pi*0.5) + vdirection (pvec p1 p2) 



-- | 'dblpointIsosceles' : 
-- @ altitude * start_pt * end_pt * (third_pt, two_thirds_pt) @
-- 
-- Double triangular joint - one joint at a third of the line
-- length, the other at two thirds.
-- 
dblpointIsosceles :: (Real u, Floating u) 
                      => u -> Point2 u -> Point2 u -> (Point2 u, Point2 u)  
dblpointIsosceles u p1@(P2 x1 y1) p2@(P2 x2 y2) = 
    (mid1 .+^ avec perp_ang u, mid2 .-^ avec perp_ang u)
  where
    mid1      = P2 (x1 + 0.33*(x2-x1)) (y1 + 0.33*(y2-y1))
    mid2      = P2 (x1 + 0.66*(x2-x1)) (y1 + 0.66*(y2-y1))
    perp_ang  = (pi*0.5) + vdirection (pvec p1 p2) 


--------------------------------------------------------------------------------



-- | 'rectangleFromBasePoints' : 
-- @ altitude * start_pt * end_pt * (top_left, top_right) @
-- 
-- Control points forming a rectangle. 
--
-- The two manufactured control points form the top corners, 
-- so the supplied points map as @start_point == bottom_left@ and 
-- @end_point == bottom_right@.
--
rectangleFromBasePoints :: (Real u, Floating u) 
                  => u -> Point2 u -> Point2 u -> (Point2 u, Point2 u)
rectangleFromBasePoints u p1 p2 = (cp1, cp2)
  where
    base_vec  = pvec p1 p2
    theta     = vdirection base_vec
    cp1       = displacePerpendicular u theta p1
    cp2       = displacePerpendicular u theta p2


-- | 'squareFromBasePoints' : 
-- @ start_pt -> end_pt -> (top_left, top_right) @
-- 
-- Control points forming a square - side_len derived from the 
-- distance between start and end points.
--
-- The two manufactured control points form the top corners, 
-- so the supplied points map as @start_point == bottom_left@ and 
-- @end_point == bottom_right@.
--
squareFromBasePoints :: (Real u, Floating u) 
                     => Point2 u -> Point2 u -> (Point2 u, Point2 u)
squareFromBasePoints p1 p2 = rectangleFromBasePoints side_len p1 p2
  where
    side_len  = vlength $ pvec p1 p2


-- | 'usquareFromBasePoints' : 
-- @ start_pt -> end_pt -> (bottom_left, bottom_right) @
-- 
-- Control points forming a square - side_len derived from the 
-- distance between start and end points.
--
-- As per 'squareFromBasePoints' but the square is drawn 
-- /underneath/ the line formed between the start and end points.
-- (Underneath is modulo the direction, of course).
--
-- The two manufactured control points form the /bottom/ corners, 
-- so the supplied points map as @start_point == top_left@ and 
-- @end_point == top_right@.
-- 
usquareFromBasePoints :: (Real u, Floating u) 
                      => Point2 u -> Point2 u -> (Point2 u, Point2 u)
usquareFromBasePoints p1 p2 = rectangleFromBasePoints side_len p1 p2
  where
    side_len  = negate $ vlength $ pvec p1 p2





-- | 'trapezoidFromBasePoints' : 
-- @ altitude * ratio_to_base * start_pt * end_pt -> (top_left, top_right) @
--
-- Control points form an isosceles trapezoid.
--
-- The two manufactured control points form the top corners, 
-- so the supplied points map as @start_point == bottom_left@ and 
-- @end_point == bottom_right@.
-- 
trapezoidFromBasePoints :: (Real u, Floating u) 
                        => u -> u -> Point2 u -> Point2 u 
                        -> (Point2 u, Point2 u) 
trapezoidFromBasePoints u ratio_to_base p1 p2 = (cp1, cp2)
  where
    base_vec  = pvec p1 p2
    base_len  = vlength base_vec
    theta     = vdirection base_vec
    half_ulen = 0.5 * ratio_to_base * base_len
    base_mid  = displaceParallel (0.5 * base_len) theta p1
    ubase_mid = displacePerpendicular u theta base_mid
    cp1       = displaceParallel (-half_ulen) theta ubase_mid
    cp2       = displaceParallel   half_ulen  theta ubase_mid




-- | 'squareFromCornerPoints' : 
-- @ altitude * start_pt * end_pt * (top_left, bottom_right) @
-- 
-- Control points forming a square bisected by the line from 
-- start_pt to end_pt. 
--
-- The two manufactured control points form the top_left and
-- bottom_right corners, so the supplied points map as 
-- @start_point == bottom_left@ and @end_point == top_right@.
--
squareFromCornerPoints :: (Real u, Floating u) 
                       => Point2 u -> Point2 u -> (Point2 u, Point2 u) 
squareFromCornerPoints p1 p2 = (cp1, cp2)
  where
    base_vec  = pvec p1 p2
    half_len  = 0.5 * (vlength base_vec)
    theta     = vdirection base_vec
    base_mid  = displaceParallel half_len theta p1
    cp1       = displacePerpendicular   half_len  theta base_mid
    cp2       = displacePerpendicular (-half_len) theta base_mid