packages feed

juicy-gcode-0.2.1.0: src/Graphics/BiArc.hs

module Graphics.BiArc (
      BiArc (..)
    , fromPoints
    , arcLength
    , jointAt
) where

import qualified Graphics.CircularArc as CA
import qualified Graphics.Line as L

import Linear hiding (angle)
import Control.Lens

import Graphics.Curve

data BiArc = BiArc { _a1 :: CA.CircularArc
                   , _a2 :: CA.CircularArc
                   } deriving Show

instance Curve BiArc where
    pointAt arc t
        | t <= s
            = pointAt (_a1 arc) (t / s)
        | otherwise
            = pointAt (_a2 arc) ((t - s) / (1 - s))
        where
            s = jointAt arc

fromPoints :: V2 Double -- Start point
           -> V2 Double -- Tangent vector at start point
           -> V2 Double -- End point
           -> V2 Double -- Tangent vector at end point
           -> V2 Double -- Transition point (connection point of the arcs)    
           -> BiArc
fromPoints p1 t1 p2 t2 t
    = BiArc (CA.CircularArc c1 r1 startAngle1 sweepAngle1 p1 t) (CA.CircularArc c2 r2 startAngle2 sweepAngle2 t p2)
    where
        -- Calculate the orientation
        osum = (t ^. _x - p1 ^. _x) * (t ^. _y + p1 ^. _y)
             + (p2 ^. _x - t ^. _x) * (p2 ^. _y + t ^. _y)
             + (p1 ^. _x - p2 ^. _x) * (p1 ^. _y + p2 ^. _y)
        cw = osum  < 0

        -- Calculate perpendicular lines to the tangent at P1 and P2
        tl1 = L.createPerpendicularAt p1 (p1 + t1)
        tl2 = L.createPerpendicularAt p2 (p2 + t2)

        -- Calculate the perpendicular bisector of P1T and P2T
        p1t2 = (p1 + t) ^/ 2
        pb_p1t = L.createPerpendicularAt p1t2 t

        p2t2 = (p2 + t) ^/ 2
        pb_p2t = L.createPerpendicularAt p2t2 t

        -- The origo of the circles are at the intersection points
        c1 = L.intersection tl1 pb_p1t
        c2 = L.intersection tl2 pb_p2t

        -- Calculate the radii
        r1 = distance c1 p1
        r2 = distance c2 p2

        -- Calculate start and sweep angles
        startVector1 = p1 - c1;
        endVector1 = t - c1;
        startAngle1 = atan2 (startVector1 ^. _y) (startVector1 ^. _x)
        sweepAngle1' = atan2 (endVector1 ^. _y) (endVector1 ^. _x) - startAngle1

        startVector2 = t - c2
        endVector2 = p2 - c2
        startAngle2 = atan2 (startVector2 ^. _y) (startVector2 ^. _x)
        sweepAngle2' = atan2 (endVector2 ^. _y) (endVector2 ^. _x) - startAngle2

        -- Adjust angles according to the orientation of the curve
        sweepAngle1 = adjustSweepAngle cw sweepAngle1'
        sweepAngle2 = adjustSweepAngle cw sweepAngle2'

adjustSweepAngle :: Bool -> Double -> Double
adjustSweepAngle True angle | angle < 0 = 2 * pi + angle
adjustSweepAngle False angle | angle > 0 = angle - 2 * pi
adjustSweepAngle _ angle = angle

arcLength :: BiArc -> Double
arcLength arc = CA.arcLength (_a1 arc) + CA.arcLength (_a2 arc)

jointAt :: BiArc -> Double
jointAt arc = CA.arcLength (_a1 arc) / arcLength arc