packages feed

mischief-math (empty) → 0.1.0.0

raw patch · 11 files changed

+566/−0 lines, 11 filesdep +basedep +containersdep +data-default

Dependencies added: base, containers, data-default, linear, mischief-ecs, mischief-math, primitive, text

Files

+ CHANGELOG.md view
@@ -0,0 +1,5 @@+# Revision history for mischief-math++## 0.1.0.0 ++* First version. Released on an unsuspecting world.
+ LICENSE view
@@ -0,0 +1,29 @@+Copyright (c) 2026, Damon+++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions are met:++    * Redistributions of source code must retain the above copyright+      notice, this list of conditions and the following disclaimer.++    * Redistributions in binary form must reproduce the above+      copyright notice, this list of conditions and the following+      disclaimer in the documentation and/or other materials provided+      with the distribution.++    * Neither the name of the copyright holder nor the names of its+      contributors may be used to endorse or promote products derived+      from this software without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT+HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ mischief-math.cabal view
@@ -0,0 +1,134 @@+cabal-version: 3.0+-- The cabal-version field refers to the version of the .cabal specification,+-- and can be different from the cabal-install (the tool) version and the+-- Cabal (the library) version you are using. As such, the Cabal (the library)+-- version used must be equal or greater than the version stated in this field.+-- Starting from the specification version 2.2, the cabal-version field must be+-- the first thing in the cabal file.+-- Initial package description 'mischief' generated by+-- 'cabal init'. For further documentation, see:+--   http://haskell.org/cabal/users-guide/+--+-- The name of the package.+name: mischief-math+-- The package version.+-- See the Haskell package versioning policy (PVP) for standards+-- guiding when and how versions should be incremented.+-- https://pvp.haskell.org+-- PVP summary:     +-+------- breaking API changes+--                  | | +----- non-breaking API additions+--                  | | | +--- code changes with no API change+version: 0.1.0.0+-- A short (one-line) description of the package.+-- synopsis:+-- A longer description of the package.+-- description:+-- The license under which the package is released.+license: BSD-3-Clause+-- The file containing the license text.+license-file: LICENSE+-- The package author(s).+author: Damon+-- An email address to which users can send suggestions, bug reports, and patches.+maintainer: pvdoriginal2019@gmail.com+homepage: https://codeberg.org/mischief/mischief+synopsis: Transform and Algebra for the Mischief game engine.+-- A copyright notice.+-- copyright:+build-type: Simple+-- Extra doc files to be distributed with the package, such as a CHANGELOG or a README.+extra-doc-files: CHANGELOG.md++-- Extra source files to be distributed with the package, such as examples, or a tutorial module.+-- extra-source-files:+common warnings+  ghc-options:+    "-Wall,"+    -Wno-name-shadowing++library+  -- Import common warning flags.+  import: warnings+  -- Modules exported by the library.+  exposed-modules:+    Mischief.Math+    Mischief.Math.Quat+    Mischief.Math.Transform+    Mischief.Math.Transform.Plugin+    Mischief.Math.Vec+    Mischief.Math.Vec.Orphans.Default+    Mischief.Math.Vec.Orphans.FieldAccessors++  -- Modules included in this library but not exported.+  -- other-modules:+  other-modules:+  -- LANGUAGE extensions used by modules in this package.z+  default-extensions:+    DefaultSignatures+    DeriveAnyClass+    DuplicateRecordFields+    FunctionalDependencies+    ImportQualifiedPost+    MagicHash+    NoFieldSelectors+    OverloadedRecordDot+    QuasiQuotes+    RequiredTypeArguments+    TemplateHaskell+    TypeFamilyDependencies+    UndecidableInstances++  -- other-extensions:+  -- Other library packages from which modules are imported.+  build-depends:+    base >=4.21.0 && <4.22,+    containers >=0.7 && <0.8,+    data-default >=0.8.0 && <0.9,+    linear >=1.23.3 && <1.24,+    mischief-ecs >=0.2.1 && <0.3,+    primitive >=0.9.1 && <0.10,+    text >=2.1.2 && <2.2,++  -- Directories containing source files.+  hs-source-dirs:+    src++  -- Base language which the package is written in.+  default-language:+    GHC2024++  ghc-options: -fno-omit-yields++test-suite mischief-test+  -- Import common warning flags.+  import: warnings+  ghc-options: -fno-omit-yields+  -- Base language which the package is written in.+  default-language: GHC2024+  -- Modules included in this executable, other than Main.+  -- other-modules:+  -- LANGUAGE extensions used by modules in this package.+  default-extensions:+    DeriveAnyClass+    DuplicateRecordFields+    NoFieldSelectors+    NoGeneralizedNewtypeDeriving+    OverloadedRecordDot+    RequiredTypeArguments+    TypeFamilyDependencies++  -- other-extensions:+  -- The interface type and version of the test suite.+  type: exitcode-stdio-1.0+  -- Directories containing source files.+  hs-source-dirs: test+  -- The entrypoint to the test suite.+  main-is: Main.hs+  -- Test dependencies.+  build-depends:+    base >=4.21.0.0 && <4.22,+    containers,+    data-default,+    mischief-ecs,+    mischief-math,+    primitive,
+ src/Mischief/Math.hs view
@@ -0,0 +1,61 @@+{-# OPTIONS_GHC -Wno-unused-imports #-}++module Mischief.Math+  ( -- * Dependencies+    -- $deps++    -- * How to Use+    -- $howtouse++    -- * Transform Propagation+    -- $prop+    module Linear,+    module Mischief.Math.Quat,+    module Mischief.Math.Transform,+    module Mischief.Math.Transform.Plugin,+  )+where++import Linear+import Mischief.ECS+import Mischief.Math.Quat (Quat)+import Mischief.Math.Transform (GlobalTransform (..), Transform (..))+import Mischief.Math.Transform qualified as Transform+import Mischief.Math.Transform.Plugin (TransformPlugin, propTransform)+import Mischief.Math.Vec.Orphans.Default ()+import Mischief.Math.Vec.Orphans.FieldAccessors ()++-- $deps+-- * [mischief-ecs])("Mischief.ECS")++-- $howtouse+-- This library is mostly a wrapper around the "Linear" library, which it also re-exports types, functions and operators from.+--+-- The most notable addition is 'Transform'. A component that encodes the translation (position), rotation and scale of entities.+--+-- Transform is usually constructed with its 'Default' instance, optionally specifying some of its fields:+--+-- @+-- insert ('def' {'translation' = 'V3' 5 1 0})+-- @+--+-- To manipulate Transforms, you can import the following module:+--+-- @+-- import "Mischief.Math.Transform" qualified as Transform+-- @+--+-- This module contains a few useful functions, such as @move@ which is used to update the translation:+--+-- @+-- Transform.'Transform.move' ('V3' 5 0 0) transform+-- @+--+-- Other notable functions are @Transform.'Transform.rotate'@. There is also @Transform.'Transform.rotateZ'@ if you wish to rotate+-- a 2d object.++-- $prop+-- You can add 'TransformPlugin' to your app. This will propagate the transform of all entities through the 'ChildOf' component.+-- Basically, each entity's 'Transform' will become their position relative to their parent.+--+-- This plugin also inserts the 'GlobalTransform' component to all relevant entities, which is a wrapper around a Transform that's global.
+ src/Mischief/Math/Quat.hs view
@@ -0,0 +1,65 @@+module Mischief.Math.Quat where++import Data.Default (Default (def))+import Linear (M33, Quaternion (Quaternion), V3 (V3), axisAngle, fromQuaternion)+import Mischief.Math.Vec (Dir3 (Dir3))+import Mischief.Math.Vec.Orphans.FieldAccessors ()++newtype Quat = Quat (Quaternion Float) deriving newtype (Show, Eq, Ord, Num)++instance Default Quat where+  def = identity++identity :: Quat+identity = Quat $ Quaternion 1 $ V3 0 0 0++fromAxisAngle :: Dir3 Float -> Float -> Quat+fromAxisAngle (Dir3 dir) angle = Quat $ axisAngle dir angle++fromXYZW :: (Float, Float, Float, Float) -> Quat+fromXYZW (x, y, z, w) = Quat $ Quaternion w (V3 x y z)++fromRotationX :: Float -> Quat+fromRotationX angle =+  let (s, c) = (sin angle, cos angle)+   in fromXYZW (s, 0, 0, c)++fromRotationY :: Float -> Quat+fromRotationY angle =+  let (s, c) = (sin angle, cos angle)+   in fromXYZW (0, s, 0, c)++fromRotationZ :: Float -> Quat+fromRotationZ angle =+  let (s, c) = (sin angle, cos angle)+   in fromXYZW (0, 0, s, c)++fromAngles :: V3 Float -> Quat+fromAngles vec3 = rotateX vec3.x $ rotateY vec3.y $ rotateZ vec3.z identity++fromEuler :: Euler -> Quat+fromEuler Euler {x, y, z} = fromAngles $ V3 x y z++rotateAxis :: Dir3 Float -> Float -> Quat -> Quat+rotateAxis dir angle quat = quat * fromAxisAngle dir angle++rotateX :: Float -> Quat -> Quat+rotateX angle quat = quat * fromRotationX angle++rotateY :: Float -> Quat -> Quat+rotateY angle quat = quat * fromRotationY angle++rotateZ :: Float -> Quat -> Quat+rotateZ angle quat = quat * fromRotationZ angle++toMat3 :: Quat -> M33 Float+toMat3 (Quat quat) = fromQuaternion quat++toEuler :: Quat -> Euler+toEuler = m33ToEuler . toMat3++data Euler = Euler {x :: Float, y :: Float, z :: Float} deriving (Show, Eq)++m33ToEuler :: M33 Float -> Euler+m33ToEuler (V3 (V3 m11 m12 m13) (V3 _ _ m23) (V3 _ _ m33)) =+  Euler {x = atan2 (-m23) m33 * 0.5, y = asin m13 * 0.5, z = atan2 (-m12) m11 * 0.5}
+ src/Mischief/Math/Transform.hs view
@@ -0,0 +1,90 @@+{-# OPTIONS_GHC -Wno-missing-methods #-}++module Mischief.Math.Transform where++import Data.Default (Default)+import GHC.Generics (Generic)+import Linear.Quaternion qualified as Quaternion+import Mischief.ECS (Tick, def)+import Mischief.ECS.Components (Component (onAdd))+import Mischief.ECS.Components.HooksDef+  ( HookContext (HookContext, entity),+  )+import Mischief.ECS.Exports (Default, Generic)+import Mischief.ECS.Hooks (hook)+import Mischief.ECS.World.Insert (insert)+import Mischief.ECS.World.Query (single)+import Mischief.ECS.World.Query.TH (q)+import Mischief.Math.Quat (Quat (Quat))+import Mischief.Math.Quat qualified as Quat+import Mischief.Math.Vec+import Mischief.Math.Vec.Orphans.Default ()++newtype GlobalTransform = GlobalTransform {transform :: Transform}+  deriving anyclass (Component, Default)+  deriving stock (Generic, Show)++data Transform = Transform {translation :: V3 Float, rotation :: Quat, scale :: V3 Float} deriving (Generic, Show, Eq)++instance Default Transform where+  def = Transform {translation = V3 0 0 0, rotation = def, scale = V3 1 1 1}++instance Component Transform where+  onAdd =+    [ hook $ \HookContext {entity} -> do+        Just transform <- single [q|entity. Transform|]+        insert (GlobalTransform transform) entity+    ]++composeTransforms :: Transform -> Transform -> Transform+composeTransforms t1 t2 =+  let Quat quat1 = t1.rotation+   in Transform+        { translation = t1.translation + (quat1 `Quaternion.rotate` (t1.scale * t2.translation)),+          rotation = t1.rotation * t2.rotation,+          scale = t1.scale * t2.scale+        }++setRotation :: Quat -> Transform -> Transform+setRotation rotation Transform {translation, scale} =+  Transform {translation, rotation, scale}++setRotationAxis :: Dir3 Float -> Float -> Transform -> Transform+setRotationAxis dir angle Transform {translation, scale} =+  Transform {translation, rotation = Quat.fromAxisAngle dir angle, scale}++setTranslation :: V3 Float -> Transform -> Transform+setTranslation translation Transform {rotation, scale} =+  Transform {translation, rotation, scale}++setScale :: V3 Float -> Transform -> Transform+setScale scale Transform {rotation, translation} =+  Transform {translation, rotation, scale}++move :: V3 Float -> Transform -> Transform+move trans Transform {translation, rotation, scale} =+  Transform {translation = translation + trans, rotation, scale}++scale :: V3 Float -> Transform -> Transform+scale sc Transform {translation, rotation, scale} =+  Transform {translation, rotation, scale = scale * sc}++rotate :: Quat -> Transform -> Transform+rotate rot Transform {translation, rotation, scale} =+  Transform {translation, rotation = rotation * rot, scale}++rotateAxis :: Dir3 Float -> Float -> Transform -> Transform+rotateAxis dir angle Transform {translation, rotation, scale} =+  Transform {translation, rotation = Quat.rotateAxis dir angle rotation, scale}++rotateX :: Float -> Transform -> Transform+rotateX angle Transform {translation, rotation, scale} =+  Transform {translation, rotation = Quat.rotateX angle rotation, scale}++rotateY :: Float -> Transform -> Transform+rotateY angle Transform {translation, rotation, scale} =+  Transform {translation, rotation = Quat.rotateY angle rotation, scale}++rotateZ :: Float -> Transform -> Transform+rotateZ angle Transform {translation, rotation, scale} =+  Transform {translation, rotation = Quat.rotateZ angle rotation, scale}
+ src/Mischief/Math/Transform/Plugin.hs view
@@ -0,0 +1,56 @@+{-# LANGUAGE OverloadedStrings #-}+{-# OPTIONS_GHC -Wno-incomplete-uni-patterns #-}++module Mischief.Math.Transform.Plugin where++import Mischief.ECS+import Mischief.ECS.App.Schedules (First)+import Mischief.ECS.Relationships.ChildOf qualified as ChildOf+import Mischief.Math.Transform (GlobalTransform (GlobalTransform), Transform, composeTransforms)++data TransformPlugin++instance Plugin TransformPlugin where+  init = do+    schedule @First $ systems propTransform++propTransform :: System ()+propTransform = do+  query_ qiterTransform++qiterTransform :: Query System ()+qiterTransform = do+  root <- [q|Entity / Without ChildOf -> *|]+  dirty <- qlift $ check [qf|Changed Transform|] root+  if dirty+    then do+      trans <- qget root [qd|Transform|]+      qlift $ insert (GlobalTransform trans) root+      child <- qiterE =<< qlift (ChildOf.children root)+      qpropTransformRec child (GlobalTransform trans)+    else+      qiterTransformRec root++-- | Recursively iterates downwards and starts propagating the transform when finding a dirty entity.+qiterTransformRec :: Entity -> Query System ()+qiterTransformRec entity = do+  child <- qiterE =<< qlift (ChildOf.children entity)+  dirty <- qlift $ check [qf|Changed Transform|] child++  if dirty+    then do+      global <- qget entity [qd|GlobalTransform|]+      qpropTransformRec child global+    else do+      qiterTransformRec child++-- | Given an entity and its parent's Global Transform, propagates the Global Transform downwards.+qpropTransformRec :: Entity -> GlobalTransform -> Query System ()+qpropTransformRec entity (GlobalTransform global) = do+  transform <- qget entity [qd|Transform|]++  let global' = global `composeTransforms` transform+  qlift $ insert (GlobalTransform global') entity++  child <- qiterE =<< qlift (ChildOf.children entity)+  qpropTransformRec child (GlobalTransform global')
+ src/Mischief/Math/Vec.hs view
@@ -0,0 +1,36 @@+module Mischief.Math.Vec+  ( V2 (..),+    V3 (..),+    V4 (..),+    ext2,+    trunc3,+    Dir3 (Dir3),+    dir3,+  )+where++import GHC.Records+import Linear++ext2 :: a -> V2 a -> V3 a+ext2 z (V2 x y) = V3 x y z++trunc3 :: V3 a -> V2 a+trunc3 (V3 x y _) = V2 x y++newtype Dir3 a = Dir3 (V3 a) deriving newtype (Show, Eq, Ord)++instance (Floating a, Epsilon a) => Num (Dir3 a) where+  (+) (Dir3 a) (Dir3 b) = Dir3 (normalize $ a + b)+  (*) (Dir3 a) (Dir3 b) = Dir3 (normalize $ a - b)+  abs (Dir3 a) = Dir3 (abs a)+  signum (Dir3 a) = Dir3 (signum a)+  fromInteger = Dir3 . fromInteger+  negate (Dir3 a) = Dir3 (negate a)++instance HasField "vec" (Dir3 a) (V3 a) where+  getField :: Dir3 a -> V3 a+  getField (Dir3 v3) = v3++dir3 :: (Floating a, Epsilon a) => a -> a -> a -> Dir3 a+dir3 x y z = Dir3 $ normalize $ V3 x y z
+ src/Mischief/Math/Vec/Orphans/Default.hs view
@@ -0,0 +1,12 @@+{-# OPTIONS_GHC -Wno-orphans #-}++module Mischief.Math.Vec.Orphans.Default where++import Data.Default+import Linear++instance (Default a) => Default (V2 a) where+  def = V2 (def @a) (def @a)++instance (Default a) => Default (V3 a) where+  def = V3 (def @a) (def @a) (def @a)
+ src/Mischief/Math/Vec/Orphans/FieldAccessors.hs view
@@ -0,0 +1,26 @@+{-# OPTIONS_GHC -Wno-orphans #-}++module Mischief.Math.Vec.Orphans.FieldAccessors where++import GHC.Records+import Linear++instance HasField "x" (V2 a) a where+  getField :: V2 a -> a+  getField (V2 x _) = x++instance HasField "y" (V2 a) a where+  getField :: V2 a -> a+  getField (V2 _ y) = y++instance HasField "x" (V3 a) a where+  getField :: V3 a -> a+  getField (V3 x _ _) = x++instance HasField "y" (V3 a) a where+  getField :: V3 a -> a+  getField (V3 _ y _) = y++instance HasField "z" (V3 a) a where+  getField :: V3 a -> a+  getField (V3 _ _ z) = z
+ test/Main.hs view
@@ -0,0 +1,52 @@+module Main where++import Control.Monad.IO.Class+import Data.Default+import Mischief.ECS+import Mischief.Math+import Mischief.Math.Mat qualified as Mat+import Mischief.Math.Quat qualified as Quat+import Mischief.Math.Transform (GlobalTransform (..), Transform)+import Mischief.Math.Transform qualified as Transform+import Mischief.Math.Vec (vec2, vec3)++quat = Quat.rotateX 1.7 Quat.identity++main :: IO ()+main = undefined++-- main :: IO ()+-- main = do+--   app <- newApp [transformPlugin, testPropagation]+--   runApp app++-- data Parent = Parent deriving (Component, Queryable)++-- data Child = Child deriving (Component, Queryable)++-- testPropagation :: Plugin ()+-- testPropagation = do+--   addSystems Startup setup+--   addSystems Update showTransforms++-- setup :: System ()+-- setup = do+--   let parent = Transform.setRotation (Quat.fromRotationZ 0.5) $ Transform.setTranslation (vec3 (0, 1, 0)) $ def @Transform+--   p <- spawn (Parent, parent)++--   let child = Transform.setRotation (Quat.fromRotationZ $ -0.2) $ Transform.setTranslation (vec3 (0, 2, 0)) $ def @Transform+--   _ <- spawn (Child, (child, R (ChildOf, p)))+--   return ()++-- showTransforms :: System ()+-- showTransforms = do+--   Just p <- single' @GlobalTransform $ with @Parent+--   Just c <- single' @GlobalTransform $ with @Child++--   let rot1 = Quat.toEuler p.value.transform.rotation+--   let rot2 = Quat.toEuler c.value.transform.rotation+--   liftIO $ print p+--   liftIO $ print c+--   liftIO $ print rot1+--   liftIO $ print rot2+--   return ()