waterfall-cad-0.2.0.0: src/Waterfall/Transforms.hs
{-# LANGUAGE InstanceSigs #-}
{-# LANGUAGE FlexibleInstances #-}
module Waterfall.Transforms
( Transformable
, scale
, uScale
, rotate
, translate
, mirror
) where
import Waterfall.Internal.Solid (Solid (..), acquireSolid, solidFromAcquire)
import Waterfall.Internal.Finalizers (toAcquire, unsafeFromAcquire)
import Linear.V3 (V3 (..))
import Linear ((*^), normalize, dot )
import qualified Linear.Quaternion as Quaternion
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 Control.Monad.IO.Class (liftIO)
import Data.Acquire
import Foreign.Ptr
import Waterfall.Internal.Path (Path(..))
import OpenCascade.Inheritance (upcast, unsafeDowncast)
-- | Typeclass for objects that can be manipulated in 3D space
class Transformable a where
-- | Scale by different amounts along the x, y and z axes
scale :: V3 Double -> a -> a
-- Uniform Scale
-- | Scale uniformally along all axes
uScale :: Double -> a -> a
-- | Rotate by Axis and Angle (in radians)
rotate :: V3 Double -> Double -> a -> a
-- | Translate by a vector in 3D space
translate :: V3 Double -> a -> a
-- | Mirror in the plane, which passes through the origin, tangent to the specified vector
mirror :: V3 Double -> a -> a
fromTrsfSolid :: Acquire (Ptr GP.Trsf) -> Solid -> Solid
fromTrsfSolid mkTrsf s = solidFromAcquire $ do
solid <- acquireSolid s
trsf <- mkTrsf
BRepBuilderAPI.Transform.transform solid trsf True
fromGTrsfSolid :: Acquire (Ptr GP.GTrsf) -> Solid -> Solid
fromGTrsfSolid mkTrsf s = solidFromAcquire $ do
solid <- acquireSolid s
trsf <- mkTrsf
BRepBuilderAPI.GTransform.gtransform solid trsf True
fromTrsfPath :: Acquire (Ptr GP.Trsf) -> Path -> Path
fromTrsfPath mkTrsf (Path p) = Path . unsafeFromAcquire $ do
path <- toAcquire p
trsf <- mkTrsf
(liftIO . unsafeDowncast) =<< BRepBuilderAPI.Transform.transform (upcast path) trsf True
fromGTrsfPath :: Acquire (Ptr GP.GTrsf) -> Path -> Path
fromGTrsfPath mkTrsf (Path p) = Path . unsafeFromAcquire $ do
path <- toAcquire p
trsf <- mkTrsf
(liftIO . unsafeDowncast) =<< BRepBuilderAPI.GTransform.gtransform (upcast path) trsf True
scaleTrsf :: V3 Double -> Acquire (Ptr GP.GTrsf)
scaleTrsf (V3 x y z ) = 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 z
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
rotateTrsf :: V3 Double -> Double -> Acquire (Ptr GP.Trsf)
rotateTrsf (V3 ax ay az) angle = do
trsf <- GP.Trsf.new
o <- GP.origin
dir <- GP.Dir.new ax ay az
axis <- GP.Ax1.new o dir
liftIO $ GP.Trsf.setRotationAboutAxisAngle trsf axis angle
return trsf
translateTrsf :: V3 Double -> Acquire (Ptr GP.Trsf)
translateTrsf (V3 x y z) = do
trsf <- GP.Trsf.new
vec <- GP.Vec.new x y z
liftIO $ GP.Trsf.setTranslation trsf vec
return trsf
mirrorTrsf :: V3 Double -> Acquire (Ptr GP.Trsf)
mirrorTrsf (V3 x y z) = do
trsf <- GP.Trsf.new
dir <- GP.Dir.new x y z
axis <- GP.xoy
liftIO $ do
GP.Ax2.setDirection axis dir
GP.Trsf.setMirrorAboutAx2 trsf axis
return trsf
instance Transformable Solid where
scale :: V3 Double -> Solid -> Solid
scale = fromGTrsfSolid . scaleTrsf
uScale :: Double -> Solid -> Solid
uScale = fromTrsfSolid . uScaleTrsf
rotate :: V3 Double -> Double -> Solid -> Solid
rotate axis angle = fromTrsfSolid (rotateTrsf axis angle)
translate :: V3 Double -> Solid -> Solid
translate = fromTrsfSolid . translateTrsf
mirror :: V3 Double -> Solid -> Solid
mirror = fromTrsfSolid . mirrorTrsf
instance Transformable Path where
scale :: V3 Double -> Path -> Path
scale = fromGTrsfPath . scaleTrsf
uScale :: Double -> Path -> Path
uScale = fromTrsfPath . uScaleTrsf
rotate :: V3 Double -> Double -> Path -> Path
rotate axis angle = fromTrsfPath (rotateTrsf axis angle)
translate :: V3 Double -> Path -> Path
translate = fromTrsfPath . translateTrsf
mirror :: V3 Double -> Path -> Path
mirror = fromTrsfPath . mirrorTrsf
instance Transformable (V3 Double) where
scale :: V3 Double -> V3 Double -> V3 Double
scale = (*)
-- Uniform Scale
uScale :: Double -> V3 Double -> V3 Double
uScale = (*^)
rotate :: V3 Double -> Double -> V3 Double -> V3 Double
rotate axis angle = Quaternion.rotate (Quaternion.axisAngle axis angle)
translate :: V3 Double -> V3 Double -> V3 Double
translate = (+)
mirror :: V3 Double -> V3 Double -> V3 Double
mirror mirrorVec toMirror =
let nm = normalize mirrorVec
in toMirror - (2 * (nm `dot` toMirror) *^ nm)