waterfall-cad-0.2.0.0: src/Waterfall/TwoD/Transforms.hs
{-# LANGUAGE InstanceSigs #-}
{-# LANGUAGE FlexibleInstances #-}
module Waterfall.TwoD.Transforms
( Transformable2D
, rotate2D
, scale2D
, uScale2D
, translate2D
, mirror2D
) where
import Waterfall.TwoD.Internal.Path2D (Path2D (..))
import Waterfall.Internal.Finalizers (toAcquire, unsafeFromAcquire)
import Linear.V2 (V2 (..))
import Linear ((*^), normalize, dot)
import qualified OpenCascade.GP.Trsf as GP.Trsf
import qualified OpenCascade.GP as GP
import qualified OpenCascade.GP.GTrsf as GP.GTrsf
import qualified OpenCascade.GP.Ax1 as GP.Ax1
import qualified OpenCascade.GP.Ax2 as GP.Ax2
import qualified OpenCascade.GP.Dir as GP.Dir
import qualified OpenCascade.GP.Vec as GP.Vec
import qualified OpenCascade.BRepBuilderAPI.Transform as BRepBuilderAPI.Transform
import qualified OpenCascade.BRepBuilderAPI.GTransform as BRepBuilderAPI.GTransform
import OpenCascade.Inheritance (upcast, unsafeDowncast)
import Control.Monad.IO.Class (liftIO)
import Data.Acquire
import Foreign.Ptr
import Waterfall.TwoD.Internal.Shape (Shape(..))
-- | Typeclass for objects that can be manipulated in 2D space
class Transformable2D a where
-- | Rotate by an angle (in radians) about the origin
rotate2D :: Double -> a -> a
-- | Scale by different amounts along the x and y axes
scale2D :: V2 Double -> a -> a
-- | Scale uniformally along both axes
uScale2D :: Double -> a -> a
-- | Translate by a distance in 2D space
translate2D :: V2 Double -> a -> a
-- | Mirror in the line, which passes through the origin, tangent to the specified vector
--
-- Note that in order to maintain consistency with 'Waterfall.Transforms.Transformable',
-- the mirror is in the line / tangent / to the vector, not in the line / parallel / to the vector
mirror2D :: V2 Double -> a -> a
fromTrsfPath :: Acquire (Ptr GP.Trsf) -> Path2D -> Path2D
fromTrsfPath mkTrsf (Path2D p) = Path2D . unsafeFromAcquire $ do
path <- toAcquire p
trsf <- mkTrsf
(liftIO . unsafeDowncast) =<< BRepBuilderAPI.Transform.transform (upcast path) trsf True
fromTrsfShape :: Acquire (Ptr GP.Trsf) -> Shape -> Shape
fromTrsfShape mkTrsf (Shape theRawShape) = Shape . unsafeFromAcquire $ do
shape <- toAcquire theRawShape
trsf <- mkTrsf
BRepBuilderAPI.Transform.transform shape trsf True
fromGTrsfPath :: Acquire (Ptr GP.GTrsf) -> Path2D -> Path2D
fromGTrsfPath mkTrsf (Path2D p) = Path2D . unsafeFromAcquire $ do
path <- toAcquire p
trsf <- mkTrsf
(liftIO . unsafeDowncast) =<< BRepBuilderAPI.GTransform.gtransform (upcast path) trsf True
fromGTrsfShape :: Acquire (Ptr GP.GTrsf) -> Shape -> Shape
fromGTrsfShape mkTrsf (Shape theRawShape) = Shape . unsafeFromAcquire $ do
shape <- toAcquire theRawShape
trsf <- mkTrsf
BRepBuilderAPI.GTransform.gtransform shape trsf True
rotateTrsf :: Double -> Acquire (Ptr GP.Trsf)
rotateTrsf angle = do
trsf <- GP.Trsf.new
o <- GP.origin
dir <- GP.Dir.new 0 0 1
axis <- GP.Ax1.new o dir
liftIO $ GP.Trsf.setRotationAboutAxisAngle trsf axis angle
return trsf
scaleGTrsf :: V2 Double -> Acquire (Ptr GP.GTrsf)
scaleGTrsf (V2 x y) = do
trsf <- GP.GTrsf.new
liftIO $ do
GP.GTrsf.setValue trsf 1 1 x
GP.GTrsf.setValue trsf 2 2 y
GP.GTrsf.setValue trsf 3 3 1
GP.GTrsf.setForm trsf
return trsf
uScaleTrsf :: Double -> Acquire (Ptr GP.Trsf)
uScaleTrsf factor = do
trsf <- GP.Trsf.new
o <- GP.origin
liftIO $ GP.Trsf.setScale trsf o factor
return trsf
translateTrsf :: V2 Double -> Acquire (Ptr GP.Trsf)
translateTrsf (V2 x y) = do
trsf <- GP.Trsf.new
vec <- GP.Vec.new x y 0
liftIO $ GP.Trsf.setTranslation trsf vec
return trsf
mirrorTrsf :: V2 Double -> Acquire (Ptr GP.Trsf)
mirrorTrsf (V2 x y) = do
trsf <- GP.Trsf.new
dir <- GP.Dir.new x y 0
axis <- GP.xoy
liftIO $ do
GP.Ax2.setDirection axis dir
GP.Trsf.setMirrorAboutAx2 trsf axis
return trsf
instance Transformable2D Path2D where
rotate2D :: Double -> Path2D -> Path2D
rotate2D = fromTrsfPath . rotateTrsf
scale2D :: V2 Double -> Path2D -> Path2D
scale2D = fromGTrsfPath . scaleGTrsf
uScale2D :: Double -> Path2D -> Path2D
uScale2D = fromTrsfPath . uScaleTrsf
translate2D :: V2 Double -> Path2D -> Path2D
translate2D = fromTrsfPath .translateTrsf
mirror2D :: V2 Double -> Path2D -> Path2D
mirror2D = fromTrsfPath . mirrorTrsf
instance Transformable2D Shape where
rotate2D :: Double -> Shape -> Shape
rotate2D = fromTrsfShape . rotateTrsf
scale2D :: V2 Double -> Shape -> Shape
scale2D = fromGTrsfShape . scaleGTrsf
uScale2D :: Double -> Shape -> Shape
uScale2D = fromTrsfShape . uScaleTrsf
translate2D :: V2 Double -> Shape -> Shape
translate2D = fromTrsfShape .translateTrsf
mirror2D :: V2 Double -> Shape -> Shape
mirror2D = fromTrsfShape . mirrorTrsf
instance Transformable2D (V2 Double) where
scale2D :: V2 Double -> V2 Double -> V2 Double
scale2D = (*)
-- Uniform Scale
uScale2D :: Double -> V2 Double -> V2 Double
uScale2D = (*^)
rotate2D :: Double -> V2 Double -> V2 Double
rotate2D angle (V2 x y) =
let c = cos angle
s = sin angle
in V2 (x * c - y * s) (x * s + y * c)
translate2D :: V2 Double -> V2 Double -> V2 Double
translate2D = (+)
mirror2D :: V2 Double -> V2 Double -> V2 Double
mirror2D mirrorVec toMirror =
let nm = normalize mirrorVec
in toMirror - (2 * (nm `dot` toMirror) *^ nm)