Document everything
This commit is contained in:
parent
1ad4c1f13c
commit
077b393bd1
17 changed files with 258 additions and 26 deletions
|
|
@ -12,14 +12,19 @@ import Data.NumIdr.Transform.Transform
|
|||
%default total
|
||||
|
||||
|
||||
||| An affine transform can contain any invertible affine map.
|
||||
public export
|
||||
Affine : Nat -> Type -> Type
|
||||
Affine = Transform TAffine
|
||||
|
||||
||| Determine if a homogeneous matrix represents an affine transform
|
||||
||| (i.e. is invertible).
|
||||
export
|
||||
isAffine : FieldCmp a => HMatrix' n a -> Bool
|
||||
isAffine mat = isHMatrix mat && invertible (getMatrix mat)
|
||||
|
||||
|
||||
||| Try to construct an affine transform from a homogeneous matrix.
|
||||
export
|
||||
fromHMatrix : FieldCmp a => HMatrix' n a -> Maybe (Affine n a)
|
||||
fromHMatrix mat = if isAffine mat
|
||||
|
|
|
|||
|
|
@ -8,13 +8,23 @@ import Data.NumIdr.Matrix
|
|||
import Data.NumIdr.Homogeneous
|
||||
import Data.NumIdr.Transform.Point
|
||||
import Data.NumIdr.Transform.Transform
|
||||
import Data.NumIdr.Transform.Orthonormal
|
||||
|
||||
%default total
|
||||
|
||||
|
||||
||| An isometry is an affine transformation that preserves distance between
|
||||
||| points. It encompasses translations, rotations, and reflections.
|
||||
public export
|
||||
Isometry : Nat -> Type -> Type
|
||||
Isometry = Transform TIsometry
|
||||
|
||||
||| Determine if a matrix represents an isometry.
|
||||
export
|
||||
isIsometry : Eq a => Num a => HMatrix' n a -> Bool
|
||||
isIsometry mat = isHMatrix mat && isOrthonormal' (getMatrix mat)
|
||||
|
||||
-- TODO: Add Isometry constructors
|
||||
||| Try to construct an isometry from a homogeneous matrix.
|
||||
export
|
||||
fromHMatrix : Eq a => Num a => HMatrix' n a -> Maybe (Isometry n a)
|
||||
fromHMatrix mat = if isIsometry mat then Just (unsafeMkTrans mat) else Nothing
|
||||
|
|
|
|||
|
|
@ -12,20 +12,25 @@ import Data.NumIdr.Transform.Transform
|
|||
%default total
|
||||
|
||||
|
||||
||| A linear transform can contain any invertible linear map.
|
||||
public export
|
||||
Linear : Nat -> Type -> Type
|
||||
Linear = Transform TLinear
|
||||
|
||||
|
||||
||| Determine if a homogeneous matrix represents a linear transform.
|
||||
export
|
||||
isLinear : FieldCmp a => HMatrix' n a -> Bool
|
||||
isLinear mat = isHMatrix mat && invertible (getMatrix mat)
|
||||
&& all (==0) (getTranslationVector mat)
|
||||
|
||||
||| Try to construct a linear transform from a homogeneous matrix.
|
||||
export
|
||||
fromHMatrix : FieldCmp a => HMatrix' n a -> Maybe (Linear n a)
|
||||
fromHMatrix mat = if isLinear mat then Just (unsafeMkTrans mat) else Nothing
|
||||
|
||||
||| Try to construct a linear transform from a matrix.
|
||||
||| This will fail if the matrix is not invertible.
|
||||
export
|
||||
fromMatrix : FieldCmp a => Matrix' n a -> Maybe (Linear n a)
|
||||
fromMatrix mat = if invertible mat then Just (unsafeMkTrans (matrixToH mat))
|
||||
|
|
|
|||
|
|
@ -12,39 +12,54 @@ import Data.NumIdr.Transform.Transform
|
|||
%default total
|
||||
|
||||
|
||||
||| An orthonormal transform is one that contains an orthonormal matrix,
|
||||
||| also known as an improper rotation or rotoreflection.
|
||||
public export
|
||||
Orthonormal : Nat -> Type -> Type
|
||||
Orthonormal = Transform TOrthonormal
|
||||
|
||||
|
||||
||| Determine if a matrix represents an orthonormal transform.
|
||||
export
|
||||
isOrthonormal' : Eq a => Num a => Matrix' n a -> Bool
|
||||
isOrthonormal' {n} mat with (viewShape mat)
|
||||
_ | Shape [n,n] = identity == fromFunction [n,n] (\[i,j] => getColumn i mat `dot` getColumn j mat)
|
||||
|
||||
||| Try to construct an orthonormal transform from a matrix.
|
||||
export
|
||||
fromMatrix : Eq a => Num a => Matrix' n a -> Maybe (Orthonormal n a)
|
||||
fromMatrix mat = if isOrthonormal' mat then Just (unsafeMkTrans (matrixToH mat))
|
||||
else Nothing
|
||||
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
-- Reflections
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
|
||||
||| Construct an orthonormal transform that reflects a particular coordinate.
|
||||
export
|
||||
reflect : {n : _} -> Neg a => Fin n -> Orthonormal n a
|
||||
reflect i = unsafeMkTrans $ indexSet [weaken i,weaken i] (-1) identity
|
||||
|
||||
||| The orthonormal transform that reflects on the x-coordinate (first coordinate).
|
||||
export
|
||||
reflectX : {n : _} -> Neg a => Orthonormal (1 + n) a
|
||||
reflectX = reflect 0
|
||||
|
||||
||| The orthonormal transform that reflects on the y-coordinate (second coordinate).
|
||||
export
|
||||
reflectY : {n : _} -> Neg a => Orthonormal (2 + n) a
|
||||
reflectY = reflect 1
|
||||
|
||||
||| The orthonormal transform that reflects on the z-coordinate (third coordinate).
|
||||
export
|
||||
reflectZ : {n : _} -> Neg a => Orthonormal (3 + n) a
|
||||
reflectZ = reflect 2
|
||||
|
||||
|
||||
||| Construct an orthonormal transform that reflects along a hyperplane of the
|
||||
||| given normal vector. The input does not have to be a unit vector.
|
||||
export
|
||||
reflectNormal : (Neg a, Fractional a) => Vector n a -> Orthonormal n a
|
||||
reflectNormal {n} v with (viewShape v)
|
||||
|
|
|
|||
|
|
@ -10,6 +10,9 @@ import Data.NumIdr.Interfaces
|
|||
%default total
|
||||
|
||||
|
||||
||| A point is a wrapper around the basic vector type, intended to be used with
|
||||
||| `Transform`. These contain the exact same information as a vector, but
|
||||
||| behave differently when transforms are applied to them.
|
||||
export
|
||||
record Point n a where
|
||||
constructor MkPoint
|
||||
|
|
@ -23,19 +26,23 @@ record Point n a where
|
|||
--------------------------------------------------------------------------------
|
||||
|
||||
|
||||
||| Construct a point from a vector.
|
||||
export
|
||||
fromVector : Vector n a -> Point n a
|
||||
fromVector = MkPoint
|
||||
|
||||
||| Convert a point to a vector.
|
||||
export
|
||||
toVector : Point n a -> Vector n a
|
||||
toVector = vec
|
||||
|
||||
||| Construct a point given its coordinates.
|
||||
export
|
||||
point : Vect n a -> Point n a
|
||||
point = fromVector . vector
|
||||
|
||||
|
||||
||| The origin point.
|
||||
export
|
||||
origin : Num a => {n : Nat} -> Point n a
|
||||
origin = fromVector $ zeros [n]
|
||||
|
|
@ -49,23 +56,34 @@ origin = fromVector $ zeros [n]
|
|||
infixl 10 !!
|
||||
infixl 10 !?
|
||||
|
||||
||| Index the point at the given coordinate.
|
||||
export
|
||||
index : Fin n -> Point n a -> a
|
||||
index n (MkPoint v) = index n v
|
||||
index i (MkPoint v) = index i v
|
||||
|
||||
||| Index the point at the given coordinate.
|
||||
|||
|
||||
||| This is the operator form of `index`.
|
||||
export
|
||||
(!!) : Point n a -> Fin n -> a
|
||||
(!!) = flip index
|
||||
|
||||
||| Index the point at the given coordinate, returning `Nothing` if the index
|
||||
||| is out of bounds.
|
||||
export
|
||||
indexNB : Nat -> Point n a -> Maybe a
|
||||
indexNB n (MkPoint v) = indexNB n v
|
||||
|
||||
||| Index the point at the given coordinate, returning `Nothing` if the index
|
||||
||| is out of bounds.
|
||||
|||
|
||||
||| This is the operator form of `indexNB`.
|
||||
export
|
||||
(!?) : Point n a -> Nat -> Maybe a
|
||||
(!?) = flip indexNB
|
||||
|
||||
|
||||
||| Return a `Vect` of the coordinates in the point.
|
||||
export
|
||||
toVect : Point n a -> Vect n a
|
||||
toVect = toVect . vec
|
||||
|
|
@ -73,14 +91,17 @@ toVect = toVect . vec
|
|||
|
||||
-- Named projections
|
||||
|
||||
||| Return the x-coordinate (the first value) of the vector.
|
||||
export
|
||||
(.x) : Point (1 + n) a -> a
|
||||
(.x) = index FZ
|
||||
|
||||
||| Return the y-coordinate (the second value) of the vector.
|
||||
export
|
||||
(.y) : Point (2 + n) a -> a
|
||||
(.y) = index (FS FZ)
|
||||
|
||||
||| Return the z-coordinate (the third value) of the vector.
|
||||
export
|
||||
(.z) : Point (3 + n) a -> a
|
||||
(.z) = index (FS (FS FZ))
|
||||
|
|
@ -98,15 +119,18 @@ infixl 8 -.
|
|||
-- These would have been named simply (+) and (-), but that caused
|
||||
-- too many issues with interface resolution.
|
||||
|
||||
||| Add a vector to a point.
|
||||
export
|
||||
(+.) : Num a => Vector n a -> Point n a -> Point n a
|
||||
a +. MkPoint b = MkPoint (zipWith (+) a b)
|
||||
|
||||
||| Add a point to a vector.
|
||||
export
|
||||
(.+) : Num a => Point n a -> Vector n a -> Point n a
|
||||
MkPoint a .+ b = MkPoint (zipWith (+) a b)
|
||||
|
||||
|
||||
||| Subtract two points to get the vector between them.
|
||||
export
|
||||
(-.) : Neg a => Point n a -> Point n a -> Vector n a
|
||||
MkPoint a -. MkPoint b = zipWith (-) a b
|
||||
|
|
|
|||
|
|
@ -8,12 +8,38 @@ import Data.NumIdr.Matrix
|
|||
import Data.NumIdr.Homogeneous
|
||||
import Data.NumIdr.Transform.Point
|
||||
import Data.NumIdr.Transform.Transform
|
||||
import Data.NumIdr.Transform.Rotation
|
||||
|
||||
%default total
|
||||
|
||||
|
||||
||| A rigid transform encodes a (proper) rigid transformation, also known as a
|
||||
||| rototranslation.
|
||||
public export
|
||||
Rigid : Nat -> Type -> Type
|
||||
Rigid = Transform TRigid
|
||||
|
||||
-- TODO: Add Rigid constructors
|
||||
|
||||
||| Determine if a homogeneous matrix represents a rigid transform.
|
||||
export
|
||||
isRigid : FieldCmp a => HMatrix' n a -> Bool
|
||||
isRigid mat = isHMatrix mat && isRotation' (getMatrix mat)
|
||||
|
||||
|
||||
export
|
||||
rigid2D : Vector 2 Double -> Double -> Rigid 2 Double
|
||||
rigid2D v a = setTranslation v (rotate2D a)
|
||||
|
||||
|
||||
||| Construct a 3D rigid transform representing an observer standing at `orig`
|
||||
||| and facing towards `target`.
|
||||
|||
|
||||
||| @ orig The origin point, i.e. the point that the origin is mapped to.
|
||||
||| @ target The point that the z-axis is directed towards.
|
||||
||| @ up The vertical direction, the direction that the y-axis faces.
|
||||
export
|
||||
faceTowards : (orig, target : Point 3 Double) -> (up : Vector 3 Double)
|
||||
-> Rigid 3 Double
|
||||
faceTowards orig target up = setTranslation (toVector orig)
|
||||
(faceTowards (orig -. target) up)
|
||||
|
|
|
|||
|
|
@ -13,32 +13,63 @@ import Data.NumIdr.Transform.Orthonormal
|
|||
%default total
|
||||
|
||||
|
||||
||| A transform that contains a rotation.
|
||||
public export
|
||||
Rotation : Nat -> Type -> Type
|
||||
Rotation = Transform TRotation
|
||||
|
||||
|
||||
||| Determine if a matrix represents a rotation.
|
||||
-- HACK: Replace with more efficient method
|
||||
export
|
||||
isRotation' : FieldCmp a => Matrix' n a -> Bool
|
||||
isRotation' mat = isOrthonormal mat && det mat == 1
|
||||
isRotation' mat = isOrthonormal' mat && det mat == 1
|
||||
|
||||
||| Try to constuct a rotation from a matrix.
|
||||
fromMatrix : FieldCmp a => Matrix' n a -> Maybe (Rotation n a)
|
||||
fromMatrix mat = if isRotation' mat then Just (unsafeMkTrans $ matrixToH mat)
|
||||
else Nothing
|
||||
|
||||
||| Determine if a homogeneous matrix represents a rotation.
|
||||
export
|
||||
isRotation : FieldCmp a => HMatrix' n a -> Bool
|
||||
isRotation {n} mat with (viewShape mat)
|
||||
_ | Shape [S n, S n] = isHMatrix mat && all (==0) (mat !!.. [EndBound last, One last])
|
||||
|
||||
||| Construct a 2D rotation that rotates by the given angle (in radians).
|
||||
export
|
||||
rotate2D : Num a => Double -> Rotation 2 Double
|
||||
rotate2D = unsafeMkTrans . rotate2DH
|
||||
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
-- 3D rotations
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
|
||||
||| Construct a 3D rotation around the x-axis.
|
||||
export
|
||||
rotate3DX : Num a => Double -> Rotation 3 Double
|
||||
rotate3DX : Double -> Rotation 3 Double
|
||||
rotate3DX = unsafeMkTrans . rotate3DXH
|
||||
|
||||
||| Construct a 3D rotation around the y-axis.
|
||||
export
|
||||
rotate3DY : Num a => Double -> Rotation 3 Double
|
||||
rotate3DY : Double -> Rotation 3 Double
|
||||
rotate3DY = unsafeMkTrans . rotate3DYH
|
||||
|
||||
||| Construct a 3D rotation around the z-axis.
|
||||
export
|
||||
rotate3DZ : Num a => Double -> Rotation 3 Double
|
||||
rotate3DZ : Double -> Rotation 3 Double
|
||||
rotate3DZ = unsafeMkTrans . rotate3DZH
|
||||
|
||||
|
||||
||| Construct a rotation representing an observer facing towards `dir`.
|
||||
|||
|
||||
||| @ dir The facing direction, aligned with the z-axis.
|
||||
||| @ up The vertical direction, the direction that the y-axis faces.
|
||||
export
|
||||
faceTowards : (dir, up : Vector 3 Double) -> Rotation 3 Double
|
||||
faceTowards dir up = let z = normalize dir
|
||||
x = normalize (up `cross` z)
|
||||
y = normalize (z `cross` x)
|
||||
in unsafeMkTrans $ matrixToH $ hstack [x,y,z]
|
||||
|
|
|
|||
|
|
@ -16,12 +16,14 @@ import Data.NumIdr.Transform.Point
|
|||
--------------------------------------------------------------------------------
|
||||
|
||||
|
||||
||| A transform type encodes the properties of a transform. There are 8 transform
|
||||
||| types, and together with the coersion relation `(:<)` they form a semilattice.
|
||||
export
|
||||
TransType : Type
|
||||
TransType = (Fin 4, Bool)
|
||||
|
||||
namespace TransType
|
||||
export
|
||||
public export
|
||||
TAffine, TIsometry, TRigid, TTranslation,
|
||||
TLinear, TOrthonormal, TRotation, TTrivial : TransType
|
||||
TAffine = (3, True)
|
||||
|
|
@ -39,26 +41,46 @@ namespace TransType
|
|||
--------------------------------------------------------------------------------
|
||||
|
||||
|
||||
||| Coersion relation for transform types.
|
||||
||| `a :< b` is `True` if and only if any transform of type `a` can be cast into
|
||||
||| a transform of type `b`.
|
||||
public export
|
||||
(:<) : TransType -> TransType -> Bool
|
||||
(xn, xb) :< (yn, yb) = (xn <= yn) && (xb >= yb)
|
||||
(xn, xb) :< (yn, yb) = (xn <= yn) && (xb <= yb)
|
||||
|
||||
||| Return the type of transform resulting from multiplying transforms of
|
||||
||| the two input types.
|
||||
public export
|
||||
transMult : TransType -> TransType -> TransType
|
||||
transMult (xn, xb) (yn, yb) = (max xn yn, xb && yb)
|
||||
transMult (xn, xb) (yn, yb) = (max xn yn, xb || yb)
|
||||
|
||||
||| Return the linearized transform type, i.e. the transform type resulting
|
||||
||| from removing the translation component of the original transform.
|
||||
public export
|
||||
linearizeType : TransType -> TransType
|
||||
linearizeType = mapSnd (const False)
|
||||
|
||||
||| Return the delinearized transform type, i.e. the transform type resulting
|
||||
||| from adding a translation to the original transform.
|
||||
public export
|
||||
delinearizeType : TransType -> TransType
|
||||
delinearizeType = mapSnd (const True)
|
||||
|
||||
|
||||
||| A transform is a wrapper for a homogeneous matrix subject to certain
|
||||
||| restrictions, such as a rotation, an isometry, or a rigid transform.
|
||||
||| The restriction on the transform is encoded by the transform's *type*.
|
||||
|||
|
||||
||| Transforms have special behavior over matrices when it comes to multiplication.
|
||||
||| When a transform is applied to a vector, only the linear part of the transform
|
||||
||| is applied, as if `linearize` were called on the transform before the operation.
|
||||
|||
|
||||
||| In order for non-linear transformations to be used, the transform should be
|
||||
||| applied to the special wrapper type `Point`. This separates the concepts of
|
||||
||| point and vector, which is often useful when working with affine maps.
|
||||
export
|
||||
data Transform : TransType -> Nat -> Type -> Type where
|
||||
MkTrans : (type : TransType) -> HMatrix' n a -> Transform type n a
|
||||
MkTrans : (ty : TransType) -> HMatrix' n a -> Transform ty n a
|
||||
|
||||
%name Transform t
|
||||
|
||||
|
|
@ -67,15 +89,31 @@ export
|
|||
unsafeMkTrans : {ty : _} -> HMatrix' n a -> Transform ty n a
|
||||
unsafeMkTrans = MkTrans _
|
||||
|
||||
export
|
||||
toHMatrix : Transform ty n a -> HMatrix' n a
|
||||
toHMatrix (MkTrans _ mat) = mat
|
||||
|
||||
||| Unwrap the inner homogeneous matrix from a transform.
|
||||
export
|
||||
getHMatrix : Transform ty n a -> HMatrix' n a
|
||||
getHMatrix (MkTrans _ mat) = mat
|
||||
|
||||
||| Unwrap the inner matrix from a transform, ignoring the translation component
|
||||
||| if one exists.
|
||||
export
|
||||
getMatrix : Transform ty n a -> Matrix' n a
|
||||
getMatrix (MkTrans _ mat) = getMatrix mat
|
||||
|
||||
||| Linearize a transform by removing its translation component.
|
||||
||| If the transform is already linear, then this function does nothing.
|
||||
export
|
||||
linearize : Num a => Transform ty n a -> Transform (linearizeType ty) n a
|
||||
linearize {n} (MkTrans _ mat) with (viewShape mat)
|
||||
_ | Shape [S n,S n] = MkTrans _ (hmatrix (getMatrix mat) (zeros _))
|
||||
|
||||
||| Set the translation component of a transform.
|
||||
|||
|
||||
||| `setTranslation v tr == translate v *. linearize tr`
|
||||
|||
|
||||
||| If `tr` is linear:
|
||||
||| `setTranslation v tr == translate v *. tr`
|
||||
export
|
||||
setTranslation : Num a => Vector n a -> Transform ty n a
|
||||
-> Transform (delinearizeType ty) n a
|
||||
|
|
@ -124,7 +162,3 @@ export
|
|||
export
|
||||
{t2 : _} -> So (t1 :< t2) => Cast a b => Cast (Transform t1 n a) (Transform t2 n b) where
|
||||
cast (MkTrans t1 mat) = MkTrans t2 (cast mat)
|
||||
|
||||
export
|
||||
Show a => Show (Transform ty n a) where
|
||||
showPrec p (MkTrans ty mat) = showCon p "MkTrans" $ showArg ty ++ showArg mat
|
||||
|
|
|
|||
|
|
@ -12,20 +12,25 @@ import Data.NumIdr.Transform.Transform
|
|||
%default total
|
||||
|
||||
|
||||
||| A translation is a transform that adds a constant vector value
|
||||
||| to its input point.
|
||||
public export
|
||||
Translation : Nat -> Type -> Type
|
||||
Translation = Transform TTranslation
|
||||
|
||||
|
||||
||| Determine if a homogeneous matrix encodes a translation.
|
||||
export
|
||||
isTranslation : Eq a => Num a => HMatrix' n a -> Bool
|
||||
isTranslation {n} mat with (viewShape mat)
|
||||
_ | Shape [S n,S n] = isHMatrix mat && getMatrix mat == identity
|
||||
|
||||
||| Construct a translation given a vector.
|
||||
export
|
||||
translate : Num a => Vector n a -> Translation n a
|
||||
translate v = unsafeMkTrans (translationH v)
|
||||
|
||||
||| Try to construct a translation from a homogeneous matrix.
|
||||
export
|
||||
fromHMatrix : (Eq a, Num a) => HMatrix' n a -> Maybe (Translation n a)
|
||||
fromHMatrix : Eq a => Num a => HMatrix' n a -> Maybe (Translation n a)
|
||||
fromHMatrix mat = if isTranslation mat then Just (unsafeMkTrans mat) else Nothing
|
||||
|
|
|
|||
|
|
@ -12,11 +12,15 @@ import Data.NumIdr.Transform.Transform
|
|||
%default total
|
||||
|
||||
|
||||
||| A trivial transform is a transform that must leave all points unchanged.
|
||||
||| This transform type only exists so that `linearize` can take a translation
|
||||
||| as input.
|
||||
public export
|
||||
Trivial : Nat -> Type -> Type
|
||||
Trivial = Transform TTrivial
|
||||
|
||||
|
||||
||| Determine if a homogeneous matrix is trivial.
|
||||
export
|
||||
isTrivial : Eq a => Num a => HMatrix' n a -> Bool
|
||||
isTrivial {n} mat with (viewShape mat)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue