Compare commits

...

13 Commits

Author SHA1 Message Date
orion kindel
96e85ca24d dont use exponential format 2026-02-25 12:29:08 -06:00
orion kindel
f8a18fe3fc warns 2026-02-23 15:41:53 -06:00
orion kindel
24d375993a fmt 2026-02-23 15:37:10 -06:00
orion kindel
26bf20cff9 tests green 2026-02-23 15:24:58 -06:00
orion kindel
39229ca2c4 spec uses custom transformer 2026-02-23 15:08:00 -06:00
orion kindel
b536f7fc4a files 2026-02-23 14:21:31 -06:00
orion kindel
1f7086f85e rename and finish 2026-02-23 14:17:33 -06:00
orion kindel
88fcf9b4a3 more 2026-02-23 12:35:20 -06:00
orion kindel
3029ea0e68 more shit 2026-02-23 11:40:11 -06:00
orion kindel
b1a1264be1 option warning 2026-02-20 15:22:03 -06:00
orion kindel
442351f47d test 2026-02-20 14:47:29 -06:00
orion kindel
8b7ad814fd moar 2026-02-20 13:52:29 -06:00
orion kindel
6ca132788f init mjcf bindings 2026-02-20 13:04:33 -06:00
48 changed files with 15097 additions and 55 deletions

1
.gitignore vendored
View File

@@ -11,3 +11,4 @@
.log
.purs-repl
.env
.spec-results

BIN
bun.lockb

Binary file not shown.

1912
doc/Modeling.rst Normal file

File diff suppressed because it is too large Load Diff

9882
doc/XMLReference.rst Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -9,11 +9,16 @@
},
"devDependencies": {
"bun-types": "1.0.11",
"mujoco_wasm": "^3.2.2",
"purs-tidy": "^0.10.0",
"spago": "^1.0.0"
},
"peerDependencies": {
"typescript": "^5.0.0"
},
"dependencies": {}
"dependencies": {
"react": "17",
"react-dom": "17",
"server": "react-dom/server"
}
}

View File

@@ -6,11 +6,16 @@
"core": {
"dependencies": [
"arrays",
"either",
"elmish",
"elmish-html",
"foldable-traversable",
"integers",
"maybe",
"numbers",
"ordered-collections",
"prelude",
"strings",
"tuples",
"typelevel-prelude",
"unsafe-coerce"
@@ -18,6 +23,8 @@
},
"test": {
"dependencies": [
"aff-promise",
"assert",
"spec",
"spec-node"
]
@@ -640,6 +647,21 @@
"unsafe-coerce"
]
},
"aff-promise": {
"type": "registry",
"version": "4.0.0",
"integrity": "sha256-Jgp3y+NWuuAmwz2V3LlWoX+AvxqfVglY77N5zTw8xnI=",
"dependencies": [
"aff",
"control",
"effect",
"either",
"exceptions",
"foreign",
"prelude",
"transformers"
]
},
"ansi": {
"type": "registry",
"version": "7.0.0",
@@ -718,6 +740,16 @@
"unsafe-coerce"
]
},
"assert": {
"type": "registry",
"version": "6.0.0",
"integrity": "sha256-hCZ1J8/71nQiRwsSV2j7iicppScOegBFZrLI6sPf9F8=",
"dependencies": [
"console",
"effect",
"prelude"
]
},
"avar": {
"type": "registry",
"version": "5.0.1",

View File

@@ -15,15 +15,22 @@ package:
test:
main: Test.Main
dependencies:
- aff-promise
- assert
- spec
- spec-node
dependencies:
- arrays
- either
- elmish
- elmish-html
- foldable-traversable
- integers
- maybe
- numbers
- ordered-collections
- prelude
- strings
- tuples
- typelevel-prelude
- unsafe-coerce

View File

@@ -1 +0,0 @@
module Mujoco where

167
src/Mujoco/MJCF.purs Normal file
View File

@@ -0,0 +1,167 @@
module Mujoco.MJCF
( Angle
, Cone
, Coordinate
, EnableDisable
, InertiaFromGeom
, Jacobian
, Props_compiler
, Props_flag
, Props_option
, Props_size
, Props_mujoco
, Props_statistic
, Props_plugin
, Solver
, plugin
, compiler
, flag
, mujoco
, option
, size
, statistic
, module X
) where
import Mujoco.MJCF.Prelude
import Mujoco.MJCF.Keyword as Kw
import Mujoco.MJCF.Keyframe (Props_key, key, keyframe) as X
import Mujoco.MJCF.Keyword (Acc(..), Affine(..), Allowed(..), Auto(..), Axis(..), Ball(..), Body(..), Box(..), CG(..), Camera(..), Capsule(..), Checker(..), Convex(..), Cross(..), Cube(..), Cubic(..), Cylinder(..), Degree(..), Dense(..), Depth(..), Dir(..), Directional(..), Disable(..), Dist(..), Distance(..), Edge(..), Ellipsoid(..), Elliptic(..), Emissive(..), Enable(..), Euler(..), Exact(..), Filter(..), FilterExact(..), Fixed(..), Flat(..), Force(..), Found(..), Free(..), Geom(..), Global(..), Gradient(..), Hfield(..), Hinge(..), Image(..), Implicit(..), ImplicitFast(..), Init(..), Integrator(..), Legacy(..), Linear(..), Local(..), MaxForce(..), Mesh(..), Metallic(..), MinDist(..), Muscle(..), NetForce(..), Never(..), Newton(..), None(..), Normal(..), Occlusion(..), Opacity(..), Origin(..), Orm(..), Orthographic(..), PGS(..), Perspective(..), Plane(..), Point(..), Pos(..), Positive(..), Pyramidal(..), Quaternion(..), RK4(..), Radian(..), Random(..), Real(..), Rgb(..), Rgba(..), Roughness(..), SRGB(..), Sdf(..), Segmentation(..), Shell(..), Site(..), Skybox(..), Slide(..), Sparse(..), Sphere(..), Spot(..), Tangent(..), Targetbody(..), Targetbodycom(..), Torque(..), Track(..), Trackcom(..), TwoD(..), User(..), Vel(..), Xbody(..), Zoh(..), false_, kw, true_) as X
import Mujoco.MJCF.Asset (LayerRole, MeshInertia, Props_hfield, Props_layer, Props_material, Props_mesh, Props_model, Props_texture, TextureBuiltin, TextureColorspace, TextureMark, TextureType, asset, hfield, layer, material, mesh, model, texture) as X
import Mujoco.MJCF.Body (CameraMode, CameraOutput, JointType, LightType, Projection, Props_attach, Props_body, Props_camera, Props_frame, Props_freejoint, Props_geom, Props_inertial, Props_joint, Props_light, Props_site, SiteType, SleepPolicy, attach, body, camera, frame, freejoint, geom, inertial, joint, light, site, worldbody) as X
import Mujoco.MJCF.Contact (Props_exclude, Props_pair, contact, exclude, pair) as X
import Mujoco.MJCF.Sensor (sensor) as X
import Mujoco.MJCF.Actuator (actuator) as X
import Mujoco.MJCF.Tendon (tendon) as X
import Mujoco.MJCF.XML (empty, text, fragment) as X
type Props_mujoco = (model :: String)
mujoco = tag @Props_mujoco "mujoco" :: Tag Props_mujoco
type Cone = Kw.Pyramidal \/ Kw.Elliptic
type Jacobian = Kw.Dense \/ Kw.Sparse
type Solver = Kw.PGS \/ Kw.CG \/ Kw.Newton
type EnableDisable = Kw.Enable \/ Kw.Disable
type Coordinate = Kw.Local \/ Kw.Global
type Angle = Kw.Radian \/ Kw.Degree
type InertiaFromGeom = Kw.Auto \/ Boolean
type Props_option =
( timestep :: Real
, impratio :: Real
, gravity :: Vec Real
, wind :: Vec Real
, magnetic :: Vec Real
, density :: Real
, viscosity :: Real
, o_margin :: Real
, o_solref :: Real /\ Real
, o_solimp :: Vec5 Real
, integrator :: Kw.Euler \/ Kw.RK4 \/ Kw.Implicit \/ Kw.ImplicitFast
, cone :: Cone
, jacobian :: Jacobian
, iterations :: Int
, tolerance :: Real
, ls_iterations :: Int
, ls_tolerance :: Real
, noslip_tolerance :: Real
, ccd_iterations :: Int
, ccd_tolerance :: Real
, sleep_tolerance :: Real
, sdf_iterations :: Int
, sdf_initpoints :: Int
, actuatorgroupdisable :: Array Int
, solver :: Solver
)
option = tag @Props_option "mjcf:option" :: Tag Props_option
type Props_flag =
( constraint :: EnableDisable
, equality :: EnableDisable
, frictionloss :: EnableDisable
, limit :: EnableDisable
, contact :: EnableDisable
, spring :: EnableDisable
, damper :: EnableDisable
, gravity :: EnableDisable
, clampctrl :: EnableDisable
, warmstart :: EnableDisable
, filterparent :: EnableDisable
, actuation :: EnableDisable
, refsafe :: EnableDisable
, sensor :: EnableDisable
, midphase :: EnableDisable
, nativeccd :: EnableDisable
, island :: EnableDisable
, eulerdamp :: EnableDisable
, autoreset :: EnableDisable
, override :: EnableDisable
, energy :: EnableDisable
, fwdinv :: EnableDisable
, invdiscrete :: EnableDisable
, multiccd :: EnableDisable
, sleep :: EnableDisable
)
flag = tagNoContent @Props_flag "flag" :: TagNoContent Props_flag
type Props_compiler =
( autolimits :: Boolean
, boundmass :: Real
, boundinertia :: Real
, settotalmass :: Real
, balanceinertia :: Boolean
, strippath :: Boolean
, coordinate :: Coordinate
, angle :: Angle
, fitaabb :: Boolean
, eulerseq :: String
, meshdir :: String
, texturedir :: String
, assetdir :: String
, discardvisual :: Boolean
, usethread :: Boolean
, fusestatic :: Boolean
, inertiafromgeom :: InertiaFromGeom
, alignfree :: Boolean
, inertiagrouprange :: Int /\ Int
, saveinertial :: Boolean
)
compiler = tagNoContent @Props_compiler "compiler" :: TagNoContent Props_compiler
type Props_size =
( memory :: String
, njmax :: Int
, nconmax :: Int
, nstack :: Int
, nuserdata :: Int
, nkey :: Int
, nuser_body :: Int
, nuser_jnt :: Int
, nuser_geom :: Int
, nuser_site :: Int
, nuser_cam :: Int
, nuser_tendon :: Int
, nuser_actuator :: Int
, nuser_sensor :: Int
)
size = tagNoContent @Props_size "size" :: TagNoContent Props_size
type Props_statistic =
( meanmass :: Real
, meaninertia :: Real
, meansize :: Real
, extent :: Real
, center :: Vec Real
)
statistic = tagNoContent @Props_statistic "statistic" :: TagNoContent Props_statistic
type Props_plugin = (plugin :: String, instance :: String)
-- | `body/plugin`, `asset/mesh/plugin`
plugin = tag @Props_plugin "plugin" :: Tag Props_plugin

View File

@@ -0,0 +1,143 @@
module Mujoco.MJCF.Actuator where
import Mujoco.MJCF.Prelude
import Mujoco.MJCF.Keyword as Kw
actuator = tag @() "actuator" :: Tag ()
type Interp = Kw.Zoh \/ Kw.Linear \/ Kw.Cubic
type DynType = Kw.None \/ Kw.Integrator \/ Kw.Filter \/ Kw.FilterExact \/ Kw.Muscle \/ Kw.User
type GainType = Kw.Fixed \/ Kw.Affine \/ Kw.Muscle \/ Kw.User
type BiasType = Kw.None \/ Kw.Affine \/ Kw.Muscle \/ Kw.User
type CommonMin r =
( class :: String
, group :: Int
, delay :: Real
, forcelimited :: Kw.Auto \/ Boolean
, ctrlrange :: Real /\ Real
, forcerange :: Real /\ Real
, user :: Array Real
, interp :: Interp
, nsample :: Int
| Named r
)
type Common r =
( ctrllimited :: Kw.Auto \/ Boolean
, lengthrange :: Real /\ Real
, gear :: Array Real
, cranklength :: Real
, joint :: String
, jointinparent :: String
, tendon :: String
, cranksite :: String
, slidersite :: String
, site :: String
, refsite :: String
| CommonMin r
)
type Props_general =
( actlimited :: Kw.Auto \/ Boolean
, actrange :: Real /\ Real
, actdim :: Int
, dyntype :: DynType
, gaintype :: GainType
, biastype :: BiasType
, dynprm :: Array Real
, gainprm :: Array Real
, biasprm :: Array Real
, actearly :: Boolean
, body :: String
| Common ()
)
general = tagNoContent @Props_general "general" :: TagNoContent Props_general
type Props_adhesion =
( body :: String
, gain :: Real
| CommonMin ()
)
adhesion = tagNoContent @Props_adhesion "adhesion" :: TagNoContent Props_adhesion
type Props_plugin =
( actlimited :: Kw.Auto \/ Boolean
, actdim :: Int
, dynprm :: Array Real
, actearly :: Boolean
, actrange :: Real /\ Real
, dyntype :: DynType
, instance :: String
, plugin :: String
| Common ()
)
plugin = tagNoContent @Props_plugin "plugin" :: TagNoContent Props_plugin
type Props_motor = Common ()
motor = tagNoContent @Props_motor "motor" :: TagNoContent Props_motor
type Props_position =
( kp :: Real
, kv :: Real
, dampratio :: Real
, timeconst :: Real
, inheritrange :: Real
| Common ()
)
position = tagNoContent @Props_position "position" :: TagNoContent Props_position
type Props_velocity =
( kv :: Real
| Common ()
)
velocity = tagNoContent @Props_velocity "velocity" :: TagNoContent Props_velocity
type Props_intvelocity =
( kp :: Real
, kv :: Real
, dampratio :: Real
, inheritrange :: Real
, actrange :: Real /\ Real
| Common ()
)
intvelocity = tagNoContent @Props_intvelocity "intvelocity" :: TagNoContent Props_intvelocity
type Props_damper =
( kv :: Real
| Common ()
)
damper = tagNoContent @Props_damper "damper" :: TagNoContent Props_damper
type Props_cylinder =
( timeconst :: Real
, area :: Real
, diameter :: Real
, bias :: Vec Real
| Common ()
)
cylinder = tagNoContent @Props_cylinder "cylinder" :: TagNoContent Props_cylinder
type Props_muscle =
( timeconst :: Real /\ Real
, tausmooth :: Real
, range :: Real /\ Real
, force :: Real
, scale :: Real
, lmin :: Real
, lmax :: Real
, vmax :: Real
, fpmax :: Real
, fvmax :: Real
| Common ()
)
muscle = tagNoContent @Props_muscle "muscle" :: TagNoContent Props_muscle

111
src/Mujoco/MJCF/Asset.purs Normal file
View File

@@ -0,0 +1,111 @@
module Mujoco.MJCF.Asset where
import Mujoco.MJCF.Prelude
import Mujoco.MJCF.Keyword as Kw
asset = tag @() "asset" :: Tag ()
type MeshInertia = Kw.Convex \/ Kw.Exact \/ Kw.Legacy \/ Kw.Shell
type Props_mesh =
( name :: String
, class :: String
, content_type :: String
, file :: String
, scale :: Vec Real
, inertia :: MeshInertia
, smoothnormal :: Boolean
, maxhullvert :: Int
, vertex :: Array Real
, normal :: Array Real
, texcoord :: Array Real
, face :: Array Int
, refpos :: Vec Real
, refquat :: Vec4 Real
, builtin :: String
, params :: Array Real
, material :: String
)
mesh = tag @Props_mesh "mesh" :: Tag Props_mesh
type Props_hfield =
( name :: String
, content_type :: String
, file :: String
, nrow :: Int
, ncol :: Int
, elevation :: Array Real
, size :: Vec4 Real
)
hfield = tagNoContent @Props_hfield "hfield" :: TagNoContent Props_hfield
type TextureType = Kw.TwoD \/ Kw.Cube \/ Kw.Skybox
type TextureColorspace = Kw.Auto \/ Kw.Linear \/ Kw.SRGB
type TextureBuiltin = Kw.None \/ Kw.Gradient \/ Kw.Checker \/ Kw.Flat
type TextureMark = Kw.None \/ Kw.Edge \/ Kw.Cross \/ Kw.Random
type Props_texture =
( name :: String
, type :: TextureType
, colorspace :: TextureColorspace
, content_type :: String
, file :: String
, gridsize :: Int /\ Int
, gridlayout :: String
, fileright :: String
, fileleft :: String
, fileup :: String
, filedown :: String
, filefront :: String
, fileback :: String
, builtin :: TextureBuiltin
, rgb1 :: Vec Real
, rgb2 :: Vec Real
, mark :: TextureMark
, markrgb :: Vec Real
, random :: Real
, width :: Int
, height :: Int
, hflip :: Boolean
, vflip :: Boolean
, nchannel :: Int
)
texture = tagNoContent @Props_texture "texture" :: TagNoContent Props_texture
type Props_material =
( name :: String
, class :: String
, texture :: String
, texrepeat :: Real /\ Real
, texuniform :: Boolean
, emission :: Real
, specular :: Real
, shininess :: Real
, reflectance :: Real
, metallic :: Real
, roughness :: Real
, rgba :: Vec4 Real
)
material = tag @Props_material "material" :: Tag Props_material
type LayerRole = Kw.Rgb \/ Kw.Normal \/ Kw.Occlusion \/ Kw.Roughness \/ Kw.Metallic \/ Kw.Opacity \/ Kw.Emissive \/ Kw.Orm \/ Kw.Rgba
type Props_layer =
( texture :: String
, role :: LayerRole
)
layer = tagNoContent @Props_layer "layer" :: TagNoContent Props_layer
type Props_model =
( name :: String
, file :: String
, content_type :: String
)
model = tagNoContent @Props_model "model" :: TagNoContent Props_model

209
src/Mujoco/MJCF/Body.purs Normal file
View File

@@ -0,0 +1,209 @@
module Mujoco.MJCF.Body
( CameraMode
, CameraOutput
, JointType
, LightType
, Projection
, Props_attach
, Props_body
, Props_camera
, Props_frame
, Props_freejoint
, Props_inertial
, Props_joint
, Props_light
, Props_site
, SiteType
, SleepPolicy
, attach
, body
, camera
, frame
, freejoint
, inertial
, joint
, light
, site
, worldbody
, module X
) where
import Mujoco.MJCF.Prelude
import Mujoco.MJCF.Geom (Props_geom, geom) as X
import Mujoco.MJCF.Keyword as Kw
type SleepPolicy = Kw.Auto \/ Kw.Never \/ Kw.Allowed \/ Kw.Init
type Props_body =
( name :: String
, childclass :: String
, mocap :: Boolean
, pos :: Vec Real
, quat :: Vec4 Real
, axisangle :: Vec4 Real
, xyaxes :: Array Real
, zaxis :: Vec Real
, euler :: Vec Real
, gravcomp :: Real
, sleep :: SleepPolicy
, user :: Array Real
)
body = tag @Props_body "body" :: Tag Props_body
worldbody = tag @Props_body "worldbody" :: Tag Props_body
type Props_inertial =
( pos :: Vec Real
, quat :: Vec4 Real
, axisangle :: Vec4 Real
, xyaxes :: Array Real
, zaxis :: Vec Real
, euler :: Vec Real
, mass :: Real
, diaginertia :: Vec Real
, fullinertia :: Array Real
)
inertial = tagNoContent @Props_inertial "inertial" :: TagNoContent Props_inertial
type JointType = Kw.Free \/ Kw.Ball \/ Kw.Slide \/ Kw.Hinge
type Props_joint =
( name :: String
, class :: String
, type :: JointType
, group :: Int
, pos :: Vec Real
, axis :: Vec Real
, springdamper :: Real /\ Real
, solreflimit :: Real /\ Real
, solimplimit :: Vec5 Real
, solreffriction :: Real /\ Real
, solimpfriction :: Vec5 Real
, stiffness :: Real
, range :: Real /\ Real
, limited :: Kw.Auto \/ Boolean
, actuatorfrcrange :: Real /\ Real
, actuatorfrclimited :: Kw.Auto \/ Boolean
, actuatorgravcomp :: Boolean
, margin :: Real
, ref :: Real
, springref :: Real
, armature :: Real
, damping :: Real
, frictionloss :: Real
, user :: Array Real
)
joint = tagNoContent @Props_joint "joint" :: TagNoContent Props_joint
type Props_freejoint =
( name :: String
, group :: Int
, align :: Kw.Auto \/ Boolean
)
freejoint = tagNoContent @Props_freejoint "freejoint" :: TagNoContent Props_freejoint
type SiteType = Kw.Sphere \/ Kw.Capsule \/ Kw.Ellipsoid \/ Kw.Cylinder \/ Kw.Box
type Props_site =
( name :: String
, class :: String
, type :: SiteType
, group :: Int
, material :: String
, rgba :: Vec4 Real
, size :: Vec Real
, fromto :: Array Real
, pos :: Vec Real
, quat :: Vec4 Real
, axisangle :: Vec4 Real
, xyaxes :: Array Real
, zaxis :: Vec Real
, euler :: Vec Real
, user :: Array Real
)
site = tagNoContent @Props_site "site" :: TagNoContent Props_site
type CameraMode = Kw.Fixed \/ Kw.Track \/ Kw.Trackcom \/ Kw.Targetbody \/ Kw.Targetbodycom
type Projection = Kw.Perspective \/ Kw.Orthographic
type CameraOutput = Kw.Rgb \/ Kw.Depth \/ Kw.Distance \/ Kw.Normal \/ Kw.Segmentation
type Props_camera =
( name :: String
, class :: String
, mode :: CameraMode
, target :: String
, projection :: Projection
, fovy :: Real
, resolution :: Int /\ Int
, output :: CameraOutput
, sensorsize :: Real /\ Real
, focal :: Real /\ Real
, focalpixel :: Real /\ Real
, principal :: Real /\ Real
, principalpixel :: Real /\ Real
, ipd :: Real
, pos :: Vec Real
, quat :: Vec4 Real
, axisangle :: Vec4 Real
, xyaxes :: Array Real
, zaxis :: Vec Real
, euler :: Vec Real
, user :: Array Real
)
camera = tagNoContent @Props_camera "camera" :: TagNoContent Props_camera
type LightType = Kw.Spot \/ Kw.Directional \/ Kw.Point \/ Kw.Image
type Props_light =
( name :: String
, class :: String
, mode :: CameraMode
, target :: String
, type :: LightType
, directional :: Boolean
, castshadow :: Boolean
, active :: Boolean
, pos :: Vec Real
, dir :: Vec Real
, diffuse :: Vec Real
, texture :: String
, intensity :: Real
, ambient :: Vec Real
, specular :: Vec Real
, range :: Real
, bulbradius :: Real
, attenuation :: Vec Real
, cutoff :: Real
, exponent :: Real
)
light = tagNoContent @Props_light "light" :: TagNoContent Props_light
-- TODO: body/composite reuses row types of joint, geom, site, skin, plugin
type Props_attach =
( model :: String
, body :: String
, prefix :: String
)
attach = tagNoContent @Props_attach "attach" :: TagNoContent Props_attach
type Props_frame =
( name :: String
, childclass :: String
, pos :: Vec Real
, quat :: Vec4 Real
, axisangle :: Vec4 Real
, xyaxes :: Array Real
, zaxis :: Vec Real
, euler :: Vec Real
)
frame = tagNoContent @Props_frame "frame" :: TagNoContent Props_frame

View File

@@ -0,0 +1,27 @@
module Mujoco.MJCF.Common
( Named
, Oriented
, Positioned
, Real
, Vec
, Vec4
, Vec5
) where
import Data.Tuple.Nested (type (/\))
type Named r = (name :: String | r)
type Positioned r = (pos :: Vec Real | r)
type Oriented r =
( quat :: Vec4 Real
, axisangle :: Vec4 Real
, xyaxes :: Array Real
, zaxis :: Vec Real
, euler :: Vec Real
| r
)
type Real = Number
type Vec a = a /\ a /\ a
type Vec4 a = a /\ a /\ a /\ a
type Vec5 a = a /\ a /\ a /\ a /\ a

View File

@@ -0,0 +1,29 @@
module Mujoco.MJCF.Contact where
import Mujoco.MJCF.Prelude
contact = tag @() "contact" :: Tag ()
type Props_pair =
( name :: String
, class :: String
, geom1 :: String
, geom2 :: String
, condim :: Int
, friction :: Vec5 Real
, solref :: Real /\ Real
, solimp :: Vec5 Real
, solreffriction :: Real /\ Real
, margin :: Real
, gap :: Real
)
pair = tagNoContent @Props_pair "pair" :: TagNoContent Props_pair
type Props_exclude =
( name :: String
, body1 :: String
, body2 :: String
)
exclude = tagNoContent @Props_exclude "exclude" :: TagNoContent Props_exclude

View File

@@ -0,0 +1,31 @@
module Mujoco.MJCF.Custom where
import Mujoco.MJCF.Prelude
custom = tag @() "custom" :: Tag ()
type Props_numeric =
( name :: String
, size :: Int
, data :: Array Real
)
numeric = tagNoContent @Props_numeric "numeric" :: TagNoContent Props_numeric
type Props_text =
( name :: String
, data :: String
)
text = tagNoContent @Props_text "text" :: TagNoContent Props_text
type Props_tuple = (name :: String)
tuple = tag @Props_tuple "tuple" :: Tag Props_tuple
type Props_element =
( objtype :: String
, objname :: String
, prm :: Real
)
element = tagNoContent @Props_element "element" :: TagNoContent Props_element

View File

@@ -0,0 +1,34 @@
module Mujoco.MJCF.Default where
import Mujoco.MJCF.Prelude
import Mujoco.MJCF.Actuator as Actuator
import Mujoco.MJCF.Asset as Asset
import Mujoco.MJCF.Body as Body
import Mujoco.MJCF.Contact as Contact
import Mujoco.MJCF.Equality as Equality
import Mujoco.MJCF.Geom as Geom
import Mujoco.MJCF.Tendon as Tendon
type Props_default = (class :: String)
default = tag @Props_default "default" :: Tag Props_default
mesh = tagNoContent @Asset.Props_mesh "mesh" :: TagNoContent Asset.Props_mesh
material = tagNoContent @Asset.Props_material "material" :: TagNoContent Asset.Props_material
joint = tagNoContent @Body.Props_joint "joint" :: TagNoContent Body.Props_joint
geom = tagNoContent @Geom.Props_geom "geom" :: TagNoContent Geom.Props_geom
site = tagNoContent @Body.Props_site "site" :: TagNoContent Body.Props_site
camera = tagNoContent @Body.Props_camera "camera" :: TagNoContent Body.Props_camera
light = tagNoContent @Body.Props_light "light" :: TagNoContent Body.Props_light
pair = tagNoContent @Contact.Props_pair "pair" :: TagNoContent Contact.Props_pair
equality = tagNoContent @(Equality.Common ()) "equality" :: TagNoContent (Equality.Common ())
tendon = tagNoContent @(Tendon.Common ()) "tendon" :: TagNoContent (Tendon.Common ())
general = tagNoContent @Actuator.Props_general "general" :: TagNoContent Actuator.Props_general
motor = tagNoContent @Actuator.Props_motor "motor" :: TagNoContent Actuator.Props_motor
position = tagNoContent @Actuator.Props_position "position" :: TagNoContent Actuator.Props_position
velocity = tagNoContent @Actuator.Props_velocity "velocity" :: TagNoContent Actuator.Props_velocity
intvelocity = tagNoContent @Actuator.Props_intvelocity "intvelocity" :: TagNoContent Actuator.Props_intvelocity
damper = tagNoContent @Actuator.Props_damper "damper" :: TagNoContent Actuator.Props_damper
cylinder = tagNoContent @Actuator.Props_cylinder "cylinder" :: TagNoContent Actuator.Props_cylinder
muscle = tagNoContent @Actuator.Props_muscle "muscle" :: TagNoContent Actuator.Props_muscle
adhesion = tagNoContent @Actuator.Props_adhesion "adhesion" :: TagNoContent Actuator.Props_adhesion

View File

@@ -0,0 +1,71 @@
module Mujoco.MJCF.Deformable.Flex where
import Mujoco.MJCF.Prelude
import Mujoco.MJCF.Geom as Geom
type Props_flex =
( name :: String
, dim :: Int
, radius :: Real
, body :: Array String
, vertex :: Array Real
, texcoord :: Array Real
, elemtexcoord :: Array Int
, element :: Array Int
, flatskin :: Boolean
, material :: String
, rgba :: Vec4 Real
, group :: Int
, node :: Array String
)
flex = tag @Props_flex "flex" :: Tag Props_flex
type Props_edge =
( stiffness :: Real
, damping :: Real
)
edge = tagNoContent @Props_edge "edge" :: TagNoContent Props_edge
data Elastic2d = Elastic2dNone | Elastic2dBend | Elastic2dStretch | Elastic2dBoth
instance Serialize Elastic2d where
serialize Elastic2dNone = "none"
serialize Elastic2dBend = "bend"
serialize Elastic2dStretch = "stretch"
serialize Elastic2dBoth = "both"
type Props_elasticity =
( young :: Real
, poisson :: Real
, damping :: Real
, thickness :: Real
, elastic2d :: Elastic2d
)
elasticity = tagNoContent @Props_elasticity "elasticity" :: TagNoContent Props_elasticity
data SelfCollide = SelfCollideNone | SelfCollideNarrow | SelfCollideBvh | SelfCollideSap | SelfCollideAuto
instance Serialize SelfCollide where
serialize SelfCollideNone = "none"
serialize SelfCollideNarrow = "narrow"
serialize SelfCollideBvh = "bvh"
serialize SelfCollideSap = "sap"
serialize SelfCollideAuto = "auto"
type Props_contact =
( internal :: Boolean
, selfcollide :: SelfCollide
, vertcollide :: Boolean
, activelayers :: Int
, priority :: Int
, friction :: Vec Real
, passive :: Boolean
| Geom.Solver
+ Geom.Contact
+ Geom.ContactSpacing ()
)
contact = tagNoContent @Props_contact "contact" :: TagNoContent Props_contact

View File

@@ -0,0 +1,27 @@
module Mujoco.MJCF.Deformable.Skin where
import Mujoco.MJCF.Prelude
type Props_skin =
( name :: String
, file :: String
, vertex :: Array Real
, texcoord :: Array Real
, face :: Array Int
, inflate :: Real
, material :: String
, rgba :: Vec4 Real
, group :: Int
)
skin = tag @Props_skin "skin" :: Tag Props_skin
type Props_bone =
( body :: String
, bindpos :: Vec Real
, bindquat :: Vec4 Real
, vertid :: Array Int
, vertweight :: Array Real
)
bone = tagNoContent @Props_bone "bone" :: TagNoContent Props_bone

View File

@@ -0,0 +1,5 @@
module Mujoco.MJCF.Deformable where
import Mujoco.MJCF.Prelude
deformable = tag @() "deformable" :: Tag ()

View File

@@ -0,0 +1,69 @@
module Mujoco.MJCF.Equality where
import Mujoco.MJCF.Prelude
type Common r =
( class :: String
, active :: Boolean
, solref :: Real /\ Real
, solimp :: Vec5 Real
| Named r
)
equality = tag @() "equality" :: Tag ()
type Props_connect =
( body1 :: String
, body2 :: String
, anchor :: Vec Real
, site1 :: String
, site2 :: String
| Common ()
)
connect = tag @Props_connect "connect" :: Tag Props_connect
type Props_weld =
( body1 :: String
, body2 :: String
, relpose :: Array Real
, anchor :: Vec Real
, site1 :: String
, site2 :: String
, torquescale :: Real
| Common ()
)
weld = tagNoContent @Props_weld "weld" :: TagNoContent Props_weld
type Props_joint =
( joint1 :: String
, joint2 :: String
, polycoef :: Vec5 Real
| Common ()
)
joint = tagNoContent @Props_joint "joint" :: TagNoContent Props_joint
type Props_tendon =
( tendon1 :: String
, tendon2 :: String
, polycoef :: Vec5 Real
| Common ()
)
tendon = tagNoContent @Props_tendon "tendon" :: TagNoContent Props_tendon
type Props_flex =
( flex :: String
| Common ()
)
flex = tagNoContent @Props_flex "flex" :: TagNoContent Props_flex
type Props_flexvert =
( flex :: String
| Common ()
)
flexvert = tagNoContent @Props_flexvert "flexvert" :: TagNoContent Props_flexvert

View File

@@ -0,0 +1,14 @@
module Mujoco.MJCF.Extension where
import Mujoco.MJCF.Prelude
extension = tag @() "extension" :: Tag ()
type Props_plugin = (plugin :: String)
plugin = tag @Props_plugin "plugin" :: Tag Props_plugin
type Props_instance = (name :: String)
instance_ = tag @Props_instance "instance" :: Tag Props_instance
type Props_config = (key :: String, value :: String)
config = tagNoContent @Props_config "config" :: TagNoContent Props_config

53
src/Mujoco/MJCF/Geom.purs Normal file
View File

@@ -0,0 +1,53 @@
module Mujoco.MJCF.Geom where
import Mujoco.MJCF.Prelude
import Mujoco.MJCF.Keyword as Kw
type Contact r =
( contype :: Int
, conaffinity :: Int
, condim :: Int
, priority :: Int
| r
)
type ContactSpacing r =
( margin :: Real
, gap :: Real
| r
)
type Solver r =
( solmix :: Real
, solref :: Real /\ Real
, solimp :: Vec5 Real
| r
)
type Props_geom =
( class :: String
, type :: Kw.Plane \/ Kw.Hfield \/ Kw.Sphere \/ Kw.Capsule \/ Kw.Ellipsoid \/ Kw.Cylinder \/ Kw.Box \/ Kw.Mesh \/ Kw.Sdf
, group :: Int
, size :: Array Real
, material :: String
, rgba :: Vec4 Real
, friction :: Vec Real
, mass :: Real
, density :: Real
, shellinertia :: Boolean
, fromto :: Array Real
, hfield :: String
, mesh :: String
, fitscale :: Real
, fluidshape :: Kw.None \/ Kw.Ellipsoid
, fluidcoef :: Vec5 Real
, user :: Array Real
| Named
+ Contact
+ ContactSpacing
+ Solver
+ Positioned
+ Oriented ()
)
geom = tag @Props_geom "geom" :: Tag Props_geom

View File

@@ -0,0 +1,18 @@
module Mujoco.MJCF.Keyframe where
import Mujoco.MJCF.Prelude
keyframe = tag @() "keyframe" :: Tag ()
type Props_key =
( time :: Real
, qpos :: Array Real
, qvel :: Array Real
, act :: Array Real
, ctrl :: Array Real
, mpos :: Array Real
, mquat :: Array Real
| Named ()
)
key = tagNoContent @Props_key "key" :: TagNoContent Props_key

View File

@@ -0,0 +1,528 @@
module Mujoco.MJCF.Keyword where
import Data.Either.Inject (class Inject, inj)
import Mujoco.MJCF.XML.Prop (class Serialize)
kw :: forall kw sum. Inject kw sum => kw -> sum
kw = inj
true_ :: forall sum. Inject Boolean sum => sum
true_ = inj true
false_ :: forall sum. Inject Boolean sum => sum
false_ = inj false
data None = None
instance Serialize None where
serialize None = "none"
data Integrator = Integrator
instance Serialize Integrator where
serialize Integrator = "integrator"
data Filter = Filter
instance Serialize Filter where
serialize Filter = "filter"
data FilterExact = FilterExact
instance Serialize FilterExact where
serialize FilterExact = "filterexact"
data Muscle = Muscle
instance Serialize Muscle where
serialize Muscle = "muscle"
data User = User
instance Serialize User where
serialize User = "user"
data Fixed = Fixed
instance Serialize Fixed where
serialize Fixed = "fixed"
data Affine = Affine
instance Serialize Affine where
serialize Affine = "affine"
data Zoh = Zoh
instance Serialize Zoh where
serialize Zoh = "zoh"
data Linear = Linear
instance Serialize Linear where
serialize Linear = "linear"
data Cubic = Cubic
instance Serialize Cubic where
serialize Cubic = "cubic"
data Convex = Convex
instance Serialize Convex where
serialize Convex = "convex"
data Exact = Exact
instance Serialize Exact where
serialize Exact = "exact"
data Legacy = Legacy
instance Serialize Legacy where
serialize Legacy = "legacy"
data Shell = Shell
instance Serialize Shell where
serialize Shell = "shell"
data TwoD = TwoD
instance Serialize TwoD where
serialize TwoD = "2d"
data Cube = Cube
instance Serialize Cube where
serialize Cube = "cube"
data Skybox = Skybox
instance Serialize Skybox where
serialize Skybox = "skybox"
data SRGB = SRGB
instance Serialize SRGB where
serialize SRGB = "sRGB"
data Gradient = Gradient
instance Serialize Gradient where
serialize Gradient = "gradient"
data Checker = Checker
instance Serialize Checker where
serialize Checker = "checker"
data Flat = Flat
instance Serialize Flat where
serialize Flat = "flat"
data Edge = Edge
instance Serialize Edge where
serialize Edge = "edge"
data Cross = Cross
instance Serialize Cross where
serialize Cross = "cross"
data Random = Random
instance Serialize Random where
serialize Random = "random"
data Rgb = Rgb
instance Serialize Rgb where
serialize Rgb = "rgb"
data Normal = Normal
instance Serialize Normal where
serialize Normal = "normal"
data Occlusion = Occlusion
instance Serialize Occlusion where
serialize Occlusion = "occlusion"
data Roughness = Roughness
instance Serialize Roughness where
serialize Roughness = "roughness"
data Metallic = Metallic
instance Serialize Metallic where
serialize Metallic = "metallic"
data Opacity = Opacity
instance Serialize Opacity where
serialize Opacity = "opacity"
data Emissive = Emissive
instance Serialize Emissive where
serialize Emissive = "emissive"
data Orm = Orm
instance Serialize Orm where
serialize Orm = "orm"
data Rgba = Rgba
instance Serialize Rgba where
serialize Rgba = "rgba"
data Auto = Auto
instance Serialize Auto where
serialize Auto = "auto"
data Never = Never
instance Serialize Never where
serialize Never = "never"
data Allowed = Allowed
instance Serialize Allowed where
serialize Allowed = "allowed"
data Init = Init
instance Serialize Init where
serialize Init = "init"
data Free = Free
instance Serialize Free where
serialize Free = "free"
data Ball = Ball
instance Serialize Ball where
serialize Ball = "ball"
data Slide = Slide
instance Serialize Slide where
serialize Slide = "slide"
data Hinge = Hinge
instance Serialize Hinge where
serialize Hinge = "hinge"
data Sphere = Sphere
instance Serialize Sphere where
serialize Sphere = "sphere"
data Capsule = Capsule
instance Serialize Capsule where
serialize Capsule = "capsule"
data Ellipsoid = Ellipsoid
instance Serialize Ellipsoid where
serialize Ellipsoid = "ellipsoid"
data Cylinder = Cylinder
instance Serialize Cylinder where
serialize Cylinder = "cylinder"
data Box = Box
instance Serialize Box where
serialize Box = "box"
data Track = Track
instance Serialize Track where
serialize Track = "track"
data Trackcom = Trackcom
instance Serialize Trackcom where
serialize Trackcom = "trackcom"
data Targetbody = Targetbody
instance Serialize Targetbody where
serialize Targetbody = "targetbody"
data Targetbodycom = Targetbodycom
instance Serialize Targetbodycom where
serialize Targetbodycom = "targetbodycom"
data Perspective = Perspective
instance Serialize Perspective where
serialize Perspective = "perspective"
data Orthographic = Orthographic
instance Serialize Orthographic where
serialize Orthographic = "orthographic"
data Depth = Depth
instance Serialize Depth where
serialize Depth = "depth"
data Distance = Distance
instance Serialize Distance where
serialize Distance = "distance"
data Segmentation = Segmentation
instance Serialize Segmentation where
serialize Segmentation = "segmentation"
data Spot = Spot
instance Serialize Spot where
serialize Spot = "spot"
data Directional = Directional
instance Serialize Directional where
serialize Directional = "directional"
data Point = Point
instance Serialize Point where
serialize Point = "point"
data Image = Image
instance Serialize Image where
serialize Image = "image"
data Plane = Plane
instance Serialize Plane where
serialize Plane = "plane"
data Hfield = Hfield
instance Serialize Hfield where
serialize Hfield = "hfield"
data Mesh = Mesh
instance Serialize Mesh where
serialize Mesh = "mesh"
data Sdf = Sdf
instance Serialize Sdf where
serialize Sdf = "sdf"
data Euler = Euler
instance Serialize Euler where
serialize Euler = "Euler"
data RK4 = RK4
instance Serialize RK4 where
serialize RK4 = "RK4"
data Implicit = Implicit
instance Serialize Implicit where
serialize Implicit = "implicit"
data ImplicitFast = ImplicitFast
instance Serialize ImplicitFast where
serialize ImplicitFast = "implicitfast"
data Pyramidal = Pyramidal
instance Serialize Pyramidal where
serialize Pyramidal = "pyramidal"
data Elliptic = Elliptic
instance Serialize Elliptic where
serialize Elliptic = "elliptic"
data Dense = Dense
instance Serialize Dense where
serialize Dense = "dense"
data Sparse = Sparse
instance Serialize Sparse where
serialize Sparse = "sparse"
data PGS = PGS
instance Serialize PGS where
serialize PGS = "PGS"
data CG = CG
instance Serialize CG where
serialize CG = "CG"
data Newton = Newton
instance Serialize Newton where
serialize Newton = "Newton"
data Enable = Enable
instance Serialize Enable where
serialize Enable = "enable"
data Disable = Disable
instance Serialize Disable where
serialize Disable = "disable"
data Local = Local
instance Serialize Local where
serialize Local = "local"
data Global = Global
instance Serialize Global where
serialize Global = "global"
data Radian = Radian
instance Serialize Radian where
serialize Radian = "radian"
data Degree = Degree
instance Serialize Degree where
serialize Degree = "degree"
data Dist = Dist
instance Serialize Dist where
serialize Dist = "dist"
data Dir = Dir
instance Serialize Dir where
serialize Dir = "dir"
data Origin = Origin
instance Serialize Origin where
serialize Origin = "origin"
data Body = Body
instance Serialize Body where
serialize Body = "body"
data Xbody = Xbody
instance Serialize Xbody where
serialize Xbody = "xbody"
data Geom = Geom
instance Serialize Geom where
serialize Geom = "geom"
data Site = Site
instance Serialize Site where
serialize Site = "site"
data Camera = Camera
instance Serialize Camera where
serialize Camera = "camera"
data Found = Found
instance Serialize Found where
serialize Found = "found"
data Force = Force
instance Serialize Force where
serialize Force = "force"
data Torque = Torque
instance Serialize Torque where
serialize Torque = "torque"
data Pos = Pos
instance Serialize Pos where
serialize Pos = "pos"
data Vel = Vel
instance Serialize Vel where
serialize Vel = "vel"
data Acc = Acc
instance Serialize Acc where
serialize Acc = "acc"
data Tangent = Tangent
instance Serialize Tangent where
serialize Tangent = "tangent"
data MinDist = MinDist
instance Serialize MinDist where
serialize MinDist = "mindist"
data MaxForce = MaxForce
instance Serialize MaxForce where
serialize MaxForce = "maxforce"
data NetForce = NetForce
instance Serialize NetForce where
serialize NetForce = "netforce"
data Real = Real
instance Serialize Real where
serialize Real = "real"
data Positive = Positive
instance Serialize Positive where
serialize Positive = "positive"
data Axis = Axis
instance Serialize Axis where
serialize Axis = "axis"
data Quaternion = Quaternion
instance Serialize Quaternion where
serialize Quaternion = "quaternion"

14
src/Mujoco/MJCF/Meta.purs Normal file
View File

@@ -0,0 +1,14 @@
module Mujoco.MJCF.Meta where
import Mujoco.MJCF.Prelude
type Props_frame :: Row Type
type Props_frame = ()
frame = tag @Props_frame "frame" :: Tag Props_frame
type Props_replicate = (count :: Int, sep :: String, offset :: Vec Number, euler :: Vec Number)
replicate = tag @Props_replicate "replicate" :: Tag Props_replicate
type Props_include = (file :: String)
include = tag @Props_include "include" :: Tag Props_include

View File

@@ -0,0 +1,18 @@
module Mujoco.MJCF.Prelude (module X) where
import Type.Row (type (+)) as X
import Prelude (class Applicative, class Apply, class Bind, class BooleanAlgebra, class Bounded, class Category, class CommutativeRing, class Discard, class DivisionRing, class Eq, class EuclideanRing, class Field, class Functor, class HeytingAlgebra, class Monad, class Monoid, class Ord, class Ring, class Semigroup, class Semigroupoid, class Semiring, class Show, type (~>), Ordering(..), Unit, Void, absurd, add, ap, append, apply, between, bind, bottom, clamp, compare, comparing, compose, conj, const, degree, discard, disj, div, eq, flap, flip, gcd, identity, ifM, join, lcm, liftA1, liftM1, map, max, mempty, min, mod, mul, negate, not, notEq, one, otherwise, pure, recip, show, sub, top, unit, unless, unlessM, void, when, whenM, zero, (#), ($), ($>), (&&), (*), (*>), (+), (-), (/), (/=), (<), (<#>), (<$), (<$>), (<*), (<*>), (<<<), (<=), (<=<), (<>), (<@>), (=<<), (==), (>), (>=), (>=>), (>>=), (>>>), (||)) as X
import Data.Either.Nested ((\/), type (\/)) as X
import Mujoco.MJCF.Common (Named, Oriented, Positioned, Real, Vec, Vec4, Vec5) as X
import Mujoco.MJCF.XML
( tag
, tagNoContent
, Tag
, TagNoContent
) as X
import Mujoco.MJCF.XML.Prop (class Serialize, serialize) as X
import Data.Tuple (Tuple(..)) as X
import Data.Tuple.Nested (type (/\), (/\)) as X
import Data.Maybe (Maybe(..), maybe, fromMaybe) as X

241
src/Mujoco/MJCF/Sensor.purs Normal file
View File

@@ -0,0 +1,241 @@
module Mujoco.MJCF.Sensor where
import Mujoco.MJCF.Prelude
import Mujoco.MJCF.Keyword as Kw
sensor = tag @() "sensor" :: Tag ()
type ObjType = Kw.Body \/ Kw.Xbody \/ Kw.Geom \/ Kw.Site \/ Kw.Camera
type Common' r =
( noise :: Real
, cutoff :: Real
, user :: Array Real
| Named r
)
type Common r =
( nsample :: Int
, interval :: Real /\ Real
, delay :: Real
, interp :: Kw.Zoh \/ Kw.Linear \/ Kw.Cubic
| Common' r
)
type Props_touch = Common (site :: String)
touch = tagNoContent @Props_touch "touch" :: TagNoContent Props_touch
type Props_accelerometer = Common (site :: String)
accelerometer = tagNoContent @Props_accelerometer "accelerometer" :: TagNoContent Props_accelerometer
type Props_velocimeter = Common (site :: String)
velocimeter = tagNoContent @Props_velocimeter "velocimeter" :: TagNoContent Props_velocimeter
type Props_gyro = Common (site :: String)
gyro = tagNoContent @Props_gyro "gyro" :: TagNoContent Props_gyro
type Props_force = Common (site :: String)
force = tagNoContent @Props_force "force" :: TagNoContent Props_force
type Props_torque = Common (site :: String)
torque = tagNoContent @Props_torque "torque" :: TagNoContent Props_torque
type Props_magnetometer = Common (site :: String)
magnetometer = tagNoContent @Props_magnetometer "magnetometer" :: TagNoContent Props_magnetometer
type Props_rangefinder =
( site :: String
, camera :: String
, data :: Array (Kw.Dist \/ Kw.Dir \/ Kw.Origin \/ Kw.Point \/ Kw.Normal \/ Kw.Depth)
| Common ()
)
rangefinder = tagNoContent @Props_rangefinder "rangefinder" :: TagNoContent Props_rangefinder
type Props_camprojection = Common (site :: String, camera :: String)
camprojection = tagNoContent @Props_camprojection "camprojection" :: TagNoContent Props_camprojection
type Props_jointpos = Common (joint :: String)
jointpos = tagNoContent @Props_jointpos "jointpos" :: TagNoContent Props_jointpos
type Props_jointvel = Common (joint :: String)
jointvel = tagNoContent @Props_jointvel "jointvel" :: TagNoContent Props_jointvel
type Props_jointactuatorfrc = Common (joint :: String)
jointactuatorfrc = tagNoContent @Props_jointactuatorfrc "jointactuatorfrc" :: TagNoContent Props_jointactuatorfrc
type Props_ballquat = Common (joint :: String)
ballquat = tagNoContent @Props_ballquat "ballquat" :: TagNoContent Props_ballquat
type Props_ballangvel = Common (joint :: String)
ballangvel = tagNoContent @Props_ballangvel "ballangvel" :: TagNoContent Props_ballangvel
type Props_jointlimitpos = Common (joint :: String)
jointlimitpos = tagNoContent @Props_jointlimitpos "jointlimitpos" :: TagNoContent Props_jointlimitpos
type Props_jointlimitvel = Common (joint :: String)
jointlimitvel = tagNoContent @Props_jointlimitvel "jointlimitvel" :: TagNoContent Props_jointlimitvel
type Props_jointlimitfrc = Common (joint :: String)
jointlimitfrc = tagNoContent @Props_jointlimitfrc "jointlimitfrc" :: TagNoContent Props_jointlimitfrc
type Props_tendonpos = Common (tendon :: String)
tendonpos = tagNoContent @Props_tendonpos "tendonpos" :: TagNoContent Props_tendonpos
type Props_tendonvel = Common (tendon :: String)
tendonvel = tagNoContent @Props_tendonvel "tendonvel" :: TagNoContent Props_tendonvel
type Props_tendonactuatorfrc = Common (tendon :: String)
tendonactuatorfrc = tagNoContent @Props_tendonactuatorfrc "tendonactuatorfrc" :: TagNoContent Props_tendonactuatorfrc
type Props_tendonlimitpos = Common (tendon :: String)
tendonlimitpos = tagNoContent @Props_tendonlimitpos "tendonlimitpos" :: TagNoContent Props_tendonlimitpos
type Props_tendonlimitvel = Common (tendon :: String)
tendonlimitvel = tagNoContent @Props_tendonlimitvel "tendonlimitvel" :: TagNoContent Props_tendonlimitvel
type Props_tendonlimitfrc = Common (tendon :: String)
tendonlimitfrc = tagNoContent @Props_tendonlimitfrc "tendonlimitfrc" :: TagNoContent Props_tendonlimitfrc
type Props_actuatorpos = Common (actuator :: String)
actuatorpos = tagNoContent @Props_actuatorpos "actuatorpos" :: TagNoContent Props_actuatorpos
type Props_actuatorvel = Common (actuator :: String)
actuatorvel = tagNoContent @Props_actuatorvel "actuatorvel" :: TagNoContent Props_actuatorvel
type Props_actuatorfrc = Common (actuator :: String)
actuatorfrc = tagNoContent @Props_actuatorfrc "actuatorfrc" :: TagNoContent Props_actuatorfrc
type Frame r =
( objtype :: ObjType
, objname :: String
, reftype :: ObjType
, refname :: String
| Common r
)
type Props_framepos = Frame ()
framepos = tagNoContent @Props_framepos "framepos" :: TagNoContent Props_framepos
type Props_framequat = Frame ()
framequat = tagNoContent @Props_framequat "framequat" :: TagNoContent Props_framequat
type Props_framexaxis = Frame ()
framexaxis = tagNoContent @Props_framexaxis "framexaxis" :: TagNoContent Props_framexaxis
type Props_frameyaxis = Frame ()
frameyaxis = tagNoContent @Props_frameyaxis "frameyaxis" :: TagNoContent Props_frameyaxis
type Props_framezaxis = Frame ()
framezaxis = tagNoContent @Props_framezaxis "framezaxis" :: TagNoContent Props_framezaxis
type Props_framelinvel = Frame ()
framelinvel = tagNoContent @Props_framelinvel "framelinvel" :: TagNoContent Props_framelinvel
type Props_frameangvel = Frame ()
frameangvel = tagNoContent @Props_frameangvel "frameangvel" :: TagNoContent Props_frameangvel
type Props_framelinacc =
( objtype :: ObjType
, objname :: String
| Common ()
)
framelinacc = tagNoContent @Props_framelinacc "framelinacc" :: TagNoContent Props_framelinacc
type Props_frameangacc =
( objtype :: ObjType
, objname :: String
| Common ()
)
frameangacc = tagNoContent @Props_frameangacc "frameangacc" :: TagNoContent Props_frameangacc
type Props_subtreecom = Common (body :: String)
subtreecom = tagNoContent @Props_subtreecom "subtreecom" :: TagNoContent Props_subtreecom
type Props_subtreelinvel = Common (body :: String)
subtreelinvel = tagNoContent @Props_subtreelinvel "subtreelinvel" :: TagNoContent Props_subtreelinvel
type Props_subtreeangmom = Common (body :: String)
subtreeangmom = tagNoContent @Props_subtreeangmom "subtreeangmom" :: TagNoContent Props_subtreeangmom
type Props_insidesite =
( objtype :: ObjType
, objname :: String
, site :: String
| Common ()
)
insidesite = tagNoContent @Props_insidesite "insidesite" :: TagNoContent Props_insidesite
type Collision r = (geom1 :: String, geom2 :: String, body1 :: String, body2 :: String | Common r)
type Props_distance = Collision ()
distance = tagNoContent @Props_distance "distance" :: TagNoContent Props_distance
type Props_normal = Collision ()
normal = tagNoContent @Props_normal "normal" :: TagNoContent Props_normal
type Props_fromto = Collision ()
fromto = tagNoContent @Props_fromto "fromto" :: TagNoContent Props_fromto
type Props_contact =
( subtree1 :: String
, subtree2 :: String
, site :: String
, num :: Int
, data :: Array (Kw.Found \/ Kw.Force \/ Kw.Torque \/ Kw.Dist \/ Kw.Pos \/ Kw.Normal \/ Kw.Tangent)
, reduce :: Kw.None \/ Kw.MinDist \/ Kw.MaxForce \/ Kw.NetForce
| Collision ()
)
contact = tagNoContent @Props_contact "contact" :: TagNoContent Props_contact
type Props_tactile =
( geom :: String
, mesh :: String
, nsample :: Int
, interp :: Kw.Zoh \/ Kw.Linear \/ Kw.Cubic
, interval :: Real /\ Real
, delay :: Real
, user :: Array Real
| Named ()
)
tactile = tagNoContent @Props_tactile "tactile" :: TagNoContent Props_tactile
type Props_e_potential = Common ()
e_potential = tagNoContent @Props_e_potential "e_potential" :: TagNoContent Props_e_potential
type Props_e_kinetic = Common ()
e_kinetic = tagNoContent @Props_e_kinetic "e_kinetic" :: TagNoContent Props_e_kinetic
type Props_clock = Common ()
clock = tagNoContent @Props_clock "clock" :: TagNoContent Props_clock
type Props_user =
( objtype :: String
, objname :: String
, datatype :: Kw.Real \/ Kw.Positive \/ Kw.Axis \/ Kw.Quaternion
, needstage :: Kw.Pos \/ Kw.Vel \/ Kw.Acc
, dim :: Int
| Common' ()
)
user = tagNoContent @Props_user "user" :: TagNoContent Props_user
type Props_plugin =
( plugin :: String
, instance :: String
, cutoff :: Real
, objtype :: ObjType
, objname :: String
, reftype :: ObjType
, refname :: String
, user :: Array Real
| Named ()
)
plugin = tag @Props_plugin "plugin" :: Tag Props_plugin

View File

@@ -0,0 +1,52 @@
module Mujoco.MJCF.Tendon where
import Mujoco.MJCF.Prelude
import Mujoco.MJCF.Keyword as Kw
tendon = tag @() "tendon" :: Tag ()
type Common r =
( class :: String
, group :: Int
, limited :: Kw.Auto \/ Boolean
, range :: Real /\ Real
, solimplimit :: Vec5 Real
, solimpfriction :: Vec5 Real
, solreflimit :: Real /\ Real
, solreffriction :: Real /\ Real
, margin :: Real
, frictionloss :: Real
, springlength :: Real /\ Real
, stiffness :: Real
, damping :: Real
, user :: Array Real
| Named r
)
type Props_spatial =
( actuatorfrcrange :: Real /\ Real
, actuatorfrclimited :: Kw.Auto \/ Boolean
, width :: Real
, material :: String
, rgba :: Vec4 Real
, armature :: Real
| Common ()
)
spatial = tag @Props_spatial "spatial" :: Tag Props_spatial
type Props_site = (site :: String)
site = tagNoContent @Props_site "site" :: TagNoContent Props_site
type Props_pulley = (divisor :: Real)
pulley = tagNoContent @Props_pulley "pulley" :: TagNoContent Props_pulley
type Props_geom = (geom :: String, sidesite :: String)
geom = tagNoContent @Props_geom "geom" :: TagNoContent Props_geom
type Props_fixed = Common ()
fixed = tag @Props_fixed "fixed" :: Tag Props_fixed
type Props_joint = (joint :: String, coef :: Real)
joint = tagNoContent @Props_joint "joint" :: TagNoContent Props_joint

112
src/Mujoco/MJCF/Visual.purs Normal file
View File

@@ -0,0 +1,112 @@
module Mujoco.MJCF.Visual where
import Mujoco.MJCF.Prelude
visual = tag @() "visual" :: Tag ()
type Props_global =
( cameraid :: Int
, orthographic :: Boolean
, fovy :: Real
, ipd :: Real
, azimuth :: Real
, elevation :: Real
, linewidth :: Real
, glow :: Real
, realtime :: Real
, offwidth :: Int
, offheight :: Int
, ellipsoidinertia :: Boolean
, bvactive :: Boolean
)
global = tagNoContent @Props_global "global" :: TagNoContent Props_global
type Props_quality =
( shadowsize :: Int
, offsamples :: Int
, numslices :: Int
, numstacks :: Int
, numquads :: Int
)
quality = tagNoContent @Props_quality "quality" :: TagNoContent Props_quality
type Props_headlight =
( ambient :: Vec Real
, diffuse :: Vec Real
, specular :: Vec Real
, active :: Int
)
headlight = tagNoContent @Props_headlight "headlight" :: TagNoContent Props_headlight
type Props_map =
( stiffness :: Real
, stiffnessrot :: Real
, force :: Real
, torque :: Real
, alpha :: Real
, fogstart :: Real
, fogend :: Real
, znear :: Real
, zfar :: Real
, haze :: Real
, shadowclip :: Real
, shadowscale :: Real
, actuatortendon :: Real
)
map = tagNoContent @Props_map "map" :: TagNoContent Props_map
type Props_scale =
( forcewidth :: Real
, contactwidth :: Real
, contactheight :: Real
, connect :: Real
, com :: Real
, camera :: Real
, light :: Real
, selectpoint :: Real
, jointlength :: Real
, jointwidth :: Real
, actuatorlength :: Real
, actuatorwidth :: Real
, framelength :: Real
, framewidth :: Real
, constraint :: Real
, slidercrank :: Real
, frustum :: Real
)
scale = tagNoContent @Props_scale "scale" :: TagNoContent Props_scale
type Props_rgba =
( fog :: Vec4 Real
, haze :: Vec4 Real
, force :: Vec4 Real
, inertia :: Vec4 Real
, joint :: Vec4 Real
, actuator :: Vec4 Real
, actuatornegative :: Vec4 Real
, actuatorpositive :: Vec4 Real
, com :: Vec4 Real
, camera :: Vec4 Real
, light :: Vec4 Real
, selectpoint :: Vec4 Real
, connect :: Vec4 Real
, contactpoint :: Vec4 Real
, contactforce :: Vec4 Real
, contactfriction :: Vec4 Real
, contacttorque :: Vec4 Real
, contactgap :: Vec4 Real
, rangefinder :: Vec4 Real
, constraint :: Vec4 Real
, slidercrank :: Vec4 Real
, crankbroken :: Vec4 Real
, frustum :: Vec4 Real
, bv :: Vec4 Real
, bvactive :: Vec4 Real
)
rgba = tagNoContent @Props_rgba "rgba" :: TagNoContent Props_rgba

View File

@@ -1,20 +1,28 @@
module Mujoco.XML.Node.Prop (class Serialize, serialize, serializeProps, class SerializeProps', serializeProps') where
module Mujoco.MJCF.XML.Prop (class Serialize, serialize, serializeProps, class SerializeProps', serializeProps', renames) where
import Prelude
import Data.Array as Array
import Data.Either (Either, either)
import Data.Int as Int
import Data.Number.Format (toString) as Number
import Data.Map (Map)
import Data.Map as Map
import Data.Maybe (fromMaybe)
import Data.Number.Format (precision)
import Data.Number.Format (toString, toStringWith) as Number
import Data.Symbol (class IsSymbol, reflectSymbol)
import Data.Tuple (Tuple)
import Data.Tuple.Nested ((/\))
import Prim.Row (class Cons, class Union)
import Prim.RowList (class RowToList, RowList)
import Prim.RowList as RL
import Record.Unsafe (unsafeSet, unsafeHas, unsafeGet) as Record
import Record.Unsafe (unsafeDelete, unsafeGet, unsafeHas, unsafeSet) as Record
import Type.Prelude (Proxy(..))
import Unsafe.Coerce (unsafeCoerce)
renames :: Map String String
renames = Map.fromFoldable [ "size" /\ "mjcf:size", "class" /\ "mjcf:class" ]
class Serialize a where
serialize :: a -> String
@@ -23,19 +31,21 @@ instance Serialize String where
else instance Serialize Int where
serialize = Int.toStringAs Int.decimal
else instance Serialize Number where
serialize = Number.toString
serialize = Number.toStringWith (precision 5)
else instance Serialize Boolean where
serialize = show
else instance (Serialize a, Serialize b) => Serialize (Either a b) where
serialize = either serialize serialize
else instance Serialize a => Serialize (Array a) where
serialize = Array.intercalate " " <<< map serialize
else instance (Serialize a, Serialize b) => Serialize (Tuple a b) where
serialize (a /\ b) = serialize [serialize a, serialize b]
serialize (a /\ b) = serialize [ serialize a, serialize b ]
serializeProps :: forall @props part missing propsrl. RowToList props propsrl => SerializeProps' props propsrl => Union part missing props => Record part -> {}
serializeProps =
(unsafeCoerce :: Record props -> {})
<<< serializeProps' @props @propsrl
<<< (unsafeCoerce :: Record part -> Record props)
<<< serializeProps' @props @propsrl
<<< (unsafeCoerce :: Record part -> Record props)
class SerializeProps' :: Row Type -> RowList Type -> Constraint
class SerializeProps' p prl | prl -> p where
@@ -48,23 +58,32 @@ instance
, Cons k v p' p
, SerializeProps' p' prl'
, Serialize v
) => SerializeProps' p (RL.Cons k v prl') where
) =>
SerializeProps' p (RL.Cons k v prl') where
serializeProps' =
patchUnsafe @k serialize
<<< remember @k
<<< serializeProps' @p' @prl'
<<< forget @k
<<< remember @k
<<< serializeProps' @p' @prl'
<<< forget @k
instance SerializeProps' () RL.Nil where
serializeProps' = identity
patchUnsafe :: forall (@k :: Symbol) a b @r @lacksK. IsSymbol k => Cons k a lacksK r => (a -> b) -> Record r -> Record r
patchUnsafe f r =
let
k = reflectSymbol $ Proxy @k
k' = reflectSymbol $ Proxy @k
k = fromMaybe k' $ Map.lookup k' renames
btoa = unsafeCoerce :: b -> a
newValue _ = btoa $ f $ Record.unsafeGet k' r
maybeWasRenamed
| k' /= k = Record.unsafeDelete k'
| otherwise = identity
in
if Record.unsafeHas k r then
Record.unsafeSet k (btoa $ f $ Record.unsafeGet k r) r
if Record.unsafeHas k' r then
maybeWasRenamed
$ Record.unsafeSet k (newValue unit)
$ r
else
r

4
src/Mujoco/MJCF/XML.js Normal file
View File

@@ -0,0 +1,4 @@
import ReactDOM from 'react-dom/server.js'
/** @type {(node: import('react').ReactElement) => String} */
export const renderToString = el => ReactDOM.renderToStaticMarkup(el)

View File

@@ -1,4 +1,4 @@
module Mujoco.XML.Node
module Mujoco.MJCF.XML
( Node
, render
, tag
@@ -8,34 +8,50 @@ module Mujoco.XML.Node
, fragment
, empty
, text
, Tag
, TagNoContent
) where
import Prelude
import Data.FoldableWithIndex (foldlWithIndex)
import Data.Map (Map)
import Data.Map as Map
import Data.String as String
import Data.Tuple as Tuple
import Data.Tuple.Nested ((/\))
import Elmish.HTML (empty, fragment, text) as HTML
import Elmish.HTML.Internal (tag, tagNoContent) as HTML
import Elmish.React (ReactElement)
import Elmish.React as React
import Mujoco.XML.Node.Prop (class SerializeProps', serializeProps)
import Mujoco.MJCF.XML.Prop (class SerializeProps', serializeProps)
import Mujoco.MJCF.XML.Prop as Prop
import Prim.Row (class Union)
import Prim.RowList (class RowToList)
import Unsafe.Coerce (unsafeCoerce)
type Tag props
= forall r missing a propsrl
unrenames :: Map String String
unrenames =
let
props = Map.fromFoldable $ map Tuple.swap $ (Map.toUnfoldable Prop.renames :: Array _)
tags = Map.fromFoldable [ "mjcf:option" /\ "option" ]
in
Map.union props tags
foreign import renderToString :: ReactElement -> String
type Tag props =
forall r missing a propsrl
. Children a
=> Union r missing props
=> React.ValidReactProps (Record r)
=> RowToList props propsrl
=> SerializeProps' props propsrl
=> Record r
-> a
-> Node
type TagNoContent props
= forall r missing propsrl
type TagNoContent props =
forall r missing propsrl
. Union r missing props
=> React.ValidReactProps (Record r)
=> RowToList props propsrl
=> SerializeProps' props propsrl
=> Record r
@@ -44,7 +60,15 @@ type TagNoContent props
foreign import data Node :: Type
render :: Node -> String
render = React.renderToString <<< toReact
render =
let
unrename str =
foldlWithIndex unrename' str unrenames
unrename' from str to =
String.replaceAll (String.Pattern from) (String.Replacement to) str
in
unrename <<< renderToString <<< toReact
fromReact :: ReactElement -> Node
fromReact = unsafeCoerce
@@ -57,13 +81,12 @@ tag
. RowToList props propsrl
=> SerializeProps' props propsrl
=> String
-> ( forall r missing a.
Union r missing props
=> Children a
=> React.ValidReactProps (Record r)
=> Record r
-> a
-> Node
-> ( forall r missing a
. Union r missing props
=> Children a
=> Record r
-> a
-> Node
)
tag el p a = fromReact $ HTML.tag el (serializeProps @props p) $ toReact <$> asChildren a
@@ -72,11 +95,10 @@ tagNoContent
. RowToList props propsrl
=> SerializeProps' props propsrl
=> String
-> ( forall r missing.
Union r missing props
=> React.ValidReactProps (Record r)
=> Record r
-> Node
-> ( forall r missing
. Union r missing props
=> Record r
-> Node
)
tagNoContent el p = fromReact $ HTML.tagNoContent el (serializeProps @props p)
@@ -92,7 +114,14 @@ text = fromReact <<< HTML.text
class Children a where
asChildren :: a -> Array Node
instance Children (Array Node) where asChildren = identity
instance Children String where asChildren s = [ unsafeCoerce s ]
instance Children Node where asChildren e = [ e ]
instance Children Unit where asChildren _ = []
instance Children (Array Node) where
asChildren = identity
instance Children String where
asChildren s = [ unsafeCoerce s ]
instance Children Node where
asChildren e = [ e ]
instance Children Unit where
asChildren _ = []

View File

@@ -3,11 +3,41 @@ module Test.Main where
import Prelude
import Effect (Effect)
import Test.Mujoco.XML.Node.Prop as Test.Mujoco.XML.Node.Prop
import Effect.Aff (launchAff_)
import Effect.Class (liftEffect)
import Mujoco.Wasm as Wasm
import Test.Mujoco.MJCF as Test.Mujoco.MJCF
import Test.Mujoco.MJCF.Actuator as Test.Mujoco.MJCF.Actuator
import Test.Mujoco.MJCF.Contact as Test.Mujoco.MJCF.Contact
import Test.Mujoco.MJCF.Custom as Test.Mujoco.MJCF.Custom
import Test.Mujoco.MJCF.Default as Test.Mujoco.MJCF.Default
import Test.Mujoco.MJCF.Deformable as Test.Mujoco.MJCF.Deformable
import Test.Mujoco.MJCF.Equality as Test.Mujoco.MJCF.Equality
import Test.Mujoco.MJCF.Keyframe as Test.Mujoco.MJCF.Keyframe
import Test.Mujoco.MJCF.Sensor as Test.Mujoco.MJCF.Sensor
import Test.Mujoco.MJCF.Tendon as Test.Mujoco.MJCF.Tendon
import Test.Mujoco.MJCF.Util (mjcf)
import Test.Mujoco.MJCF.Visual as Test.Mujoco.MJCF.Visual
import Test.Mujoco.MJCF.XML.Prop as Test.Mujoco.MJCF.XML.Prop
import Test.Spec.Reporter.Console (consoleReporter)
import Test.Spec.Runner.Node (runSpecAndExitProcess)
main :: Effect Unit
main =
runSpecAndExitProcess [consoleReporter] do
Test.Mujoco.XML.Node.Prop.spec
launchAff_ do
mj <- Wasm.loadMujoco
liftEffect
$ runSpecAndExitProcess [consoleReporter]
$ mjcf mj do
Test.Mujoco.MJCF.spec
Test.Mujoco.MJCF.XML.Prop.spec
Test.Mujoco.MJCF.Actuator.spec
Test.Mujoco.MJCF.Contact.spec
Test.Mujoco.MJCF.Custom.spec
Test.Mujoco.MJCF.Deformable.spec
Test.Mujoco.MJCF.Default.spec
Test.Mujoco.MJCF.Equality.spec
Test.Mujoco.MJCF.Keyframe.spec
Test.Mujoco.MJCF.Sensor.spec
Test.Mujoco.MJCF.Tendon.spec
Test.Mujoco.MJCF.Visual.spec

View File

@@ -0,0 +1,102 @@
module Test.Mujoco.MJCF.Actuator where
import Prelude
import Data.Tuple.Nested ((/\))
import Mujoco.MJCF as X
import Mujoco.MJCF.Actuator as Act
import Mujoco.MJCF.XML (Node)
import Test.Mujoco.MJCF.Util (MjcfSpec, parseOk)
import Test.Spec (describe, it)
-- | Model with a body + hinge joint for actuator tests
m :: Node -> Node
m a = X.mujoco {}
[ X.compiler { inertiafromgeom: X.true_ }
, X.worldbody {}
[ X.body { name: "b1", pos: 0.0 /\ 0.0 /\ 0.5 }
[ X.joint { name: "j1", type: X.kw X.Hinge, axis: 1.0 /\ 0.0 /\ 0.0 }
, X.geom { type: X.kw X.Sphere, size: [ 0.1, 0.0, 0.0 ] } unit
]
]
, a
]
spec :: MjcfSpec Unit
spec =
describe "actuator" do
describe "general" do
it "basic" $ parseOk $ m $
Act.actuator {} [ Act.general { name: "a_gen", joint: "j1" } ]
it "dyntype + gaintype + biastype" $ parseOk $ m $
Act.actuator {}
[ Act.general { name: "a_gen2", joint: "j1", dyntype: X.kw X.Filter, gaintype: X.kw X.Fixed, biastype: X.kw X.Affine } ]
it "ctrlrange + forcerange" $ parseOk $ m $
Act.actuator {}
[ Act.general { name: "a_gen3", joint: "j1", ctrllimited: X.true_, ctrlrange: (-1.0) /\ 1.0, forcelimited: X.true_, forcerange: (-100.0) /\ 100.0 } ]
describe "motor" do
it "basic" $ parseOk $ m $
Act.actuator {} [ Act.motor { name: "a_motor", joint: "j1" } ]
it "ctrlrange + gear" $ parseOk $ m $
Act.actuator {}
[ Act.motor { name: "a_motor2", joint: "j1", ctrllimited: X.true_, ctrlrange: (-1.0) /\ 1.0, gear: [ 100.0, 0.0, 0.0, 0.0, 0.0, 0.0 ] } ]
describe "position" do
it "basic" $ parseOk $ m $
Act.actuator {} [ Act.position { name: "a_pos", joint: "j1" } ]
it "kp + kv" $ parseOk $ m $
Act.actuator {}
[ Act.position { name: "a_pos2", joint: "j1", kp: 100.0, kv: 10.0 } ]
describe "velocity" do
it "basic" $ parseOk $ m $
Act.actuator {} [ Act.velocity { name: "a_vel", joint: "j1" } ]
it "kv" $ parseOk $ m $
Act.actuator {}
[ Act.velocity { name: "a_vel2", joint: "j1", kv: 10.0 } ]
describe "intvelocity" do
it "basic" $ parseOk $ m $
Act.actuator {} [ Act.intvelocity { name: "a_iv", joint: "j1" } ]
it "kp + actrange" $ parseOk $ m $
Act.actuator {}
[ Act.intvelocity { name: "a_iv2", joint: "j1", kp: 100.0, actrange: (-1.0) /\ 1.0 } ]
describe "damper" do
it "basic" $ parseOk $ m $
Act.actuator {} [ Act.damper { name: "a_damp", joint: "j1" } ]
it "kv" $ parseOk $ m $
Act.actuator {}
[ Act.damper { name: "a_damp2", joint: "j1", kv: 10.0 } ]
describe "cylinder" do
it "basic" $ parseOk $ m $
Act.actuator {} [ Act.cylinder { name: "a_cyl", joint: "j1" } ]
it "timeconst + area" $ parseOk $ m $
Act.actuator {}
[ Act.cylinder { name: "a_cyl2", joint: "j1", timeconst: 0.1, area: 0.01 } ]
describe "muscle" do
it "basic" $ parseOk $ m $
Act.actuator {} [ Act.muscle { name: "a_musc", joint: "j1" } ]
it "force + range + timeconst" $ parseOk $ m $
Act.actuator {}
[ Act.muscle { name: "a_musc2", joint: "j1", force: 100.0, range: 0.75 /\ 1.05, timeconst: 0.01 /\ 0.04 } ]
describe "adhesion" do
it "basic" $ parseOk $ m $
Act.actuator {} [ Act.adhesion { name: "a_adh", body: "b1" } ]
it "gain" $ parseOk $ m $
Act.actuator {}
[ Act.adhesion { name: "a_adh2", body: "b1", gain: 1.0 } ]

View File

@@ -0,0 +1,47 @@
module Test.Mujoco.MJCF.Contact where
import Prelude
import Data.Tuple.Nested ((/\))
import Mujoco.MJCF as X
import Test.Mujoco.MJCF.Util (MjcfSpec, parseOk)
import Test.Spec (describe, it)
spec :: MjcfSpec Unit
spec =
describe "contact" do
describe "pair" do
it "geom1 + geom2" $ parseOk $ X.mujoco {}
[ X.worldbody {}
[ X.geom { name: "g1", type: X.kw X.Sphere, size: [ 0.1, 0.0, 0.0 ] } unit
, X.geom { name: "g2", type: X.kw X.Sphere, size: [ 0.1, 0.0, 0.0 ], pos: 1.0 /\ 0.0 /\ 0.0 } unit
]
, X.contact {} [ X.pair { geom1: "g1", geom2: "g2" } ]
]
it "condim + friction" $ parseOk $ X.mujoco {}
[ X.worldbody {}
[ X.geom { name: "g1", type: X.kw X.Sphere, size: [ 0.1, 0.0, 0.0 ] } unit
, X.geom { name: "g2", type: X.kw X.Sphere, size: [ 0.1, 0.0, 0.0 ], pos: 1.0 /\ 0.0 /\ 0.0 } unit
]
, X.contact {} [ X.pair { geom1: "g1", geom2: "g2", condim: 3, friction: 1.0 /\ 0.005 /\ 0.0001 /\ 0.005 /\ 0.0001 } ]
]
it "solref + solimp + margin + gap" $ parseOk $ X.mujoco {}
[ X.worldbody {}
[ X.geom { name: "g1", type: X.kw X.Sphere, size: [ 0.1, 0.0, 0.0 ] } unit
, X.geom { name: "g2", type: X.kw X.Sphere, size: [ 0.1, 0.0, 0.0 ], pos: 1.0 /\ 0.0 /\ 0.0 } unit
]
, X.contact {} [ X.pair { geom1: "g1", geom2: "g2", solref: 0.02 /\ 1.0, solimp: 0.9 /\ 0.95 /\ 0.001 /\ 0.5 /\ 2.0, margin: 0.01, gap: 0.0 } ]
]
describe "exclude" do
it "body1 + body2" $ parseOk $ X.mujoco {}
[ X.worldbody {}
[ X.body { name: "b1", pos: 0.0 /\ 0.0 /\ 0.0 }
[ X.geom { type: X.kw X.Sphere, size: [ 0.1, 0.0, 0.0 ] } unit ]
, X.body { name: "b2", pos: 1.0 /\ 0.0 /\ 0.0 }
[ X.geom { type: X.kw X.Sphere, size: [ 0.1, 0.0, 0.0 ] } unit ]
]
, X.contact {} [ X.exclude { body1: "b1", body2: "b2" } ]
]

View File

@@ -0,0 +1,50 @@
module Test.Mujoco.MJCF.Custom where
import Prelude
import Mujoco.MJCF as X
import Mujoco.MJCF.Custom as Custom
import Test.Mujoco.MJCF.Util (MjcfSpec, parseOk)
import Test.Spec (describe, it)
spec :: MjcfSpec Unit
spec =
describe "custom" do
describe "numeric" do
it "name + data" $ parseOk $ X.mujoco {}
[ Custom.custom {}
[ Custom.numeric { name: "params", data: [ 1.0, 2.0, 3.0 ] } ]
, X.worldbody {} unit
]
it "name + size" $ parseOk $ X.mujoco {}
[ Custom.custom {}
[ Custom.numeric { name: "zeros", size: 5 } ]
, X.worldbody {} unit
]
describe "text" do
it "name + data" $ parseOk $ X.mujoco {}
[ Custom.custom {}
[ Custom.text { name: "info", data: "hello world" } ]
, X.worldbody {} unit
]
describe "tuple" do
it "with elements" $ parseOk $ X.mujoco {}
[ Custom.custom {}
[ Custom.tuple { name: "body_list" }
[ Custom.element { objtype: "body", objname: "world", prm: 1.0 } ]
]
, X.worldbody {} unit
]
describe "mixed" do
it "numeric + text + tuple" $ parseOk $ X.mujoco {}
[ Custom.custom {}
[ Custom.numeric { name: "n1", data: [ 0.0 ] }
, Custom.text { name: "t1", data: "value" }
, Custom.tuple { name: "tup1" } unit
]
, X.worldbody {} unit
]

View File

@@ -0,0 +1,87 @@
module Test.Mujoco.MJCF.Default where
import Prelude
import Data.Tuple.Nested ((/\))
import Mujoco.MJCF as X
import Mujoco.MJCF.Default as Def
import Test.Mujoco.MJCF.Util (MjcfSpec, parseOk)
import Test.Spec (describe, it)
spec :: MjcfSpec Unit
spec =
describe "default" do
it "empty" $ parseOk $ X.mujoco {}
[ Def.default {} unit
, X.worldbody {} unit
]
it "with class" $ parseOk $ X.mujoco {}
[ Def.default { class: "main" } unit
, X.worldbody {} unit
]
it "geom defaults" $ parseOk $ X.mujoco {}
[ Def.default {}
[ Def.geom { type: X.kw X.Sphere, size: [ 0.1, 0.0, 0.0 ], rgba: 1.0 /\ 0.0 /\ 0.0 /\ 1.0 } ]
, X.worldbody {}
[ X.geom {} unit ]
]
it "joint defaults" $ parseOk $ X.mujoco {}
[ X.compiler { inertiafromgeom: X.true_ }
, Def.default {}
[ Def.joint { type: X.kw X.Hinge, damping: 1.0, stiffness: 10.0 }
, Def.geom { type: X.kw X.Capsule, size: [ 0.05, 0.2 ] }
]
, X.worldbody {}
[ X.body { pos: 0.0 /\ 0.0 /\ 0.5 }
[ X.joint { axis: 1.0 /\ 0.0 /\ 0.0 }
, X.geom {} unit
]
]
]
it "nested defaults" $ parseOk $ X.mujoco {}
[ X.compiler { inertiafromgeom: X.true_ }
, Def.default {}
[ Def.geom { rgba: 1.0 /\ 1.0 /\ 1.0 /\ 1.0 }
, Def.default { class: "red" }
[ Def.geom { rgba: 1.0 /\ 0.0 /\ 0.0 /\ 1.0 } ]
]
, X.worldbody {}
[ X.geom { type: X.kw X.Sphere, size: [ 0.1, 0.0, 0.0 ] } unit ]
]
it "site defaults" $ parseOk $ X.mujoco {}
[ Def.default {}
[ Def.site { type: X.kw X.Sphere, size: 0.02 /\ 0.02 /\ 0.02, rgba: 0.0 /\ 1.0 /\ 0.0 /\ 1.0 } ]
, X.worldbody {}
[ X.site { name: "s1", pos: 0.0 /\ 0.0 /\ 0.0 } ]
]
it "light defaults" $ parseOk $ X.mujoco {}
[ Def.default {}
[ Def.light { diffuse: 0.8 /\ 0.8 /\ 0.8, castshadow: true } ]
, X.worldbody {}
[ X.light { name: "l1", pos: 0.0 /\ 0.0 /\ 3.0 } ]
]
it "camera defaults" $ parseOk $ X.mujoco {}
[ Def.default {}
[ Def.camera { fovy: 60.0 } ]
, X.worldbody {}
[ X.camera { name: "c1", pos: 0.0 /\ (-2.0) /\ 1.0 } ]
]
it "motor defaults" $ parseOk $ X.mujoco {}
[ X.compiler { inertiafromgeom: X.true_ }
, Def.default {}
[ Def.motor { ctrllimited: X.true_, ctrlrange: (-1.0) /\ 1.0 } ]
, X.worldbody {}
[ X.body { pos: 0.0 /\ 0.0 /\ 0.5 }
[ X.joint { name: "j1", type: X.kw X.Hinge, axis: 1.0 /\ 0.0 /\ 0.0 }
, X.geom { type: X.kw X.Sphere, size: [ 0.1, 0.0, 0.0 ] } unit
]
]
]

View File

@@ -0,0 +1,92 @@
module Test.Mujoco.MJCF.Deformable where
import Prelude
import Data.Tuple.Nested ((/\))
import Mujoco.MJCF as X
import Mujoco.MJCF.Deformable as Deformable
import Mujoco.MJCF.Deformable.Flex as Flex
import Mujoco.MJCF.Deformable.Skin as Skin
import Test.Mujoco.MJCF.Util (MjcfSpec, parseOk)
import Test.Spec (describe, it)
spec :: MjcfSpec Unit
spec =
describe "deformable" do
describe "flex" do
it "basic" $ parseOk $ X.mujoco {}
[ X.compiler { inertiafromgeom: X.true_ }
, Deformable.deformable {}
[ Flex.flex { name: "f1", dim: 3, body: [ "world", "world", "world", "world" ], vertex: [ 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0 ], element: [ 0, 1, 2, 3 ] }
unit
]
, X.worldbody {}
[ X.body { pos: 0.0 /\ 0.0 /\ 0.0 }
[ X.geom { type: X.kw X.Sphere, size: [ 0.1, 0.0, 0.0 ] } unit ]
]
]
it "with edge" $ parseOk $ X.mujoco {}
[ X.compiler { inertiafromgeom: X.true_ }
, Deformable.deformable {}
[ Flex.flex { name: "f2", dim: 3, body: [ "world", "world", "world", "world" ], vertex: [ 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0 ], element: [ 0, 1, 2, 3 ] }
[ Flex.edge { stiffness: 100.0, damping: 1.0 } ]
]
, X.worldbody {}
[ X.body { pos: 0.0 /\ 0.0 /\ 0.0 }
[ X.geom { type: X.kw X.Sphere, size: [ 0.1, 0.0, 0.0 ] } unit ]
]
]
it "with elasticity" $ parseOk $ X.mujoco {}
[ X.compiler { inertiafromgeom: X.true_ }
, Deformable.deformable {}
[ Flex.flex { name: "f3", dim: 3, body: [ "world", "world", "world", "world" ], vertex: [ 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0 ], element: [ 0, 1, 2, 3 ] }
[ Flex.elasticity { young: 1000.0, poisson: 0.3, damping: 0.01 } ]
]
, X.worldbody {}
[ X.body { pos: 0.0 /\ 0.0 /\ 0.0 }
[ X.geom { type: X.kw X.Sphere, size: [ 0.1, 0.0, 0.0 ] } unit ]
]
]
it "with contact" $ parseOk $ X.mujoco {}
[ X.compiler { inertiafromgeom: X.true_ }
, Deformable.deformable {}
[ Flex.flex { name: "f4", dim: 3, body: [ "world", "world", "world", "world" ], vertex: [ 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0 ], element: [ 0, 1, 2, 3 ] }
[ Flex.contact { internal: true } ]
]
, X.worldbody {}
[ X.body { pos: 0.0 /\ 0.0 /\ 0.0 }
[ X.geom { type: X.kw X.Sphere, size: [ 0.1, 0.0, 0.0 ] } unit ]
]
]
it "rgba + group" $ parseOk $ X.mujoco {}
[ X.compiler { inertiafromgeom: X.true_ }
, Deformable.deformable {}
[ Flex.flex { name: "f5", dim: 3, body: [ "world", "world", "world", "world" ], vertex: [ 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0 ], element: [ 0, 1, 2, 3 ], rgba: 1.0 /\ 0.0 /\ 0.0 /\ 1.0, group: 0 }
unit
]
, X.worldbody {}
[ X.body { pos: 0.0 /\ 0.0 /\ 0.0 }
[ X.geom { type: X.kw X.Sphere, size: [ 0.1, 0.0, 0.0 ] } unit ]
]
]
describe "skin" do
it "basic" $ parseOk $ X.mujoco {}
[ Deformable.deformable {}
[ Skin.skin { name: "sk1", vertex: [ 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0, 0.0 ], face: [ 0, 1, 2 ] }
[ Skin.bone { body: "world", bindpos: 0.0 /\ 0.0 /\ 0.0, bindquat: 1.0 /\ 0.0 /\ 0.0 /\ 0.0, vertid: [ 0, 1, 2 ], vertweight: [ 1.0, 1.0, 1.0 ] } ]
]
, X.worldbody {} unit
]
it "inflate + rgba" $ parseOk $ X.mujoco {}
[ Deformable.deformable {}
[ Skin.skin { name: "sk2", vertex: [ 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0, 0.0 ], face: [ 0, 1, 2 ], inflate: 0.01, rgba: 0.5 /\ 0.5 /\ 1.0 /\ 0.8 }
[ Skin.bone { body: "world", bindpos: 0.0 /\ 0.0 /\ 0.0, bindquat: 1.0 /\ 0.0 /\ 0.0 /\ 0.0, vertid: [ 0, 1, 2 ], vertweight: [ 1.0, 1.0, 1.0 ] } ]
]
, X.worldbody {} unit
]

View File

@@ -0,0 +1,73 @@
module Test.Mujoco.MJCF.Equality where
import Prelude
import Data.Tuple.Nested ((/\))
import Mujoco.MJCF as X
import Mujoco.MJCF.Equality as Eq
import Mujoco.MJCF.Tendon as Tendon
import Mujoco.MJCF.XML (Node)
import Test.Mujoco.MJCF.Util (MjcfSpec, parseOk)
import Test.Spec (describe, it)
-- | Model with two bodies, joints, and sites for equality constraint tests
m :: Node -> Node
m e = X.mujoco {}
[ X.compiler { inertiafromgeom: X.true_ }
, X.worldbody {}
[ X.body { name: "b1", pos: 0.0 /\ 0.0 /\ 0.5 }
[ X.joint { name: "j1", type: X.kw X.Hinge, axis: 1.0 /\ 0.0 /\ 0.0 }
, X.geom { type: X.kw X.Sphere, size: [ 0.1, 0.0, 0.0 ] } unit
, X.site { name: "s1", pos: 0.0 /\ 0.0 /\ 0.1, size: 0.01 /\ 0.01 /\ 0.01 }
]
, X.body { name: "b2", pos: 1.0 /\ 0.0 /\ 0.5 }
[ X.joint { name: "j2", type: X.kw X.Hinge, axis: 1.0 /\ 0.0 /\ 0.0 }
, X.geom { type: X.kw X.Sphere, size: [ 0.1, 0.0, 0.0 ] } unit
, X.site { name: "s2", pos: 0.0 /\ 0.0 /\ 0.1, size: 0.01 /\ 0.01 /\ 0.01 }
]
]
, e
]
spec :: MjcfSpec Unit
spec =
describe "equality" do
describe "connect" do
it "body1 + body2 + anchor" $ parseOk $ m $
Eq.equality {} [ Eq.connect { body1: "b1", body2: "b2", anchor: 0.5 /\ 0.0 /\ 0.5 } unit ]
it "site1 + site2" $ parseOk $ m $
Eq.equality {} [ Eq.connect { site1: "s1", site2: "s2" } unit ]
it "solref + solimp" $ parseOk $ m $
Eq.equality {} [ Eq.connect { body1: "b1", body2: "b2", anchor: 0.5 /\ 0.0 /\ 0.5, solref: 0.02 /\ 1.0, solimp: 0.9 /\ 0.95 /\ 0.001 /\ 0.5 /\ 2.0 } unit ]
describe "weld" do
it "body1 + body2" $ parseOk $ m $
Eq.equality {} [ Eq.weld { body1: "b1", body2: "b2" } ]
it "site1 + site2" $ parseOk $ m $
Eq.equality {} [ Eq.weld { site1: "s1", site2: "s2" } ]
it "torquescale" $ parseOk $ m $
Eq.equality {} [ Eq.weld { body1: "b1", body2: "b2", torquescale: 1.0 } ]
describe "joint" do
it "joint1 + joint2" $ parseOk $ m $
Eq.equality {} [ Eq.joint { joint1: "j1", joint2: "j2" } ]
it "polycoef" $ parseOk $ m $
Eq.equality {} [ Eq.joint { joint1: "j1", joint2: "j2", polycoef: 0.0 /\ 1.0 /\ 0.0 /\ 0.0 /\ 0.0 } ]
describe "tendon" do
it "tendon1" $ parseOk $ X.mujoco {}
[ X.compiler { inertiafromgeom: X.true_ }
, X.worldbody {}
[ X.body { name: "b1", pos: 0.0 /\ 0.0 /\ 0.5 }
[ X.joint { name: "j1", type: X.kw X.Hinge, axis: 1.0 /\ 0.0 /\ 0.0 }
, X.geom { type: X.kw X.Sphere, size: [ 0.1, 0.0, 0.0 ] } unit
]
]
, Tendon.tendon {} $ Tendon.fixed { name: "t1" } [ Tendon.joint { joint: "j1", coef: 1.0 } ]
, Eq.equality {} [ Eq.tendon { tendon1: "t1" } ]
]

View File

@@ -0,0 +1,54 @@
module Test.Mujoco.MJCF.Keyframe where
import Prelude
import Data.Tuple.Nested ((/\))
import Mujoco.MJCF as X
import Test.Mujoco.MJCF.Util (MjcfSpec, parseOk)
import Test.Spec (describe, it)
spec :: MjcfSpec Unit
spec =
describe "keyframe" do
it "empty" $ parseOk $ X.mujoco {}
[ X.worldbody {} unit
, X.keyframe {} unit
]
it "key with time" $ parseOk $ X.mujoco {}
[ X.worldbody {} unit
, X.keyframe {}
[ X.key { name: "home", time: 0.0 } ]
]
it "key with qpos + qvel" $ parseOk $ X.mujoco {}
[ X.compiler { inertiafromgeom: X.true_ }
, X.worldbody {}
[ X.body { pos: 0.0 /\ 0.0 /\ 0.5 }
[ X.joint { name: "j1", type: X.kw X.Hinge, axis: 1.0 /\ 0.0 /\ 0.0 }
, X.geom { type: X.kw X.Sphere, size: [ 0.1, 0.0, 0.0 ] } unit
]
]
, X.keyframe {}
[ X.key { name: "init", time: 0.0, qpos: [ 0.5 ], qvel: [ 0.0 ] } ]
]
it "key with ctrl" $ parseOk $ X.mujoco {}
[ X.compiler { inertiafromgeom: X.true_ }
, X.worldbody {}
[ X.body { pos: 0.0 /\ 0.0 /\ 0.5 }
[ X.joint { name: "j1", type: X.kw X.Hinge, axis: 1.0 /\ 0.0 /\ 0.0 }
, X.geom { type: X.kw X.Sphere, size: [ 0.1, 0.0, 0.0 ] } unit
]
]
, X.keyframe {}
[ X.key { name: "start", ctrl: [ 0.0 ] } ]
]
it "multiple keys" $ parseOk $ X.mujoco {}
[ X.worldbody {} unit
, X.keyframe {}
[ X.key { name: "k1", time: 0.0 }
, X.key { name: "k2", time: 1.0 }
]
]

View File

@@ -0,0 +1,166 @@
module Test.Mujoco.MJCF.Sensor where
import Prelude
import Data.Tuple.Nested ((/\))
import Mujoco.MJCF as X
import Mujoco.MJCF.Actuator as Act
import Mujoco.MJCF.Sensor as Sens
import Mujoco.MJCF.XML (Node)
import Test.Mujoco.MJCF.Util (MjcfSpec, parseOk)
import Test.Spec (describe, it)
-- | Model with body, site, joint, tendon, and actuator for sensor tests
m :: Node -> Node
m s = X.mujoco {}
[ X.compiler { inertiafromgeom: X.true_ }
, X.worldbody {}
[ X.body { name: "b1", pos: 0.0 /\ 0.0 /\ 0.5 }
[ X.joint { name: "j1", type: X.kw X.Hinge, axis: 1.0 /\ 0.0 /\ 0.0 }
, X.geom { name: "g1", type: X.kw X.Sphere, size: [ 0.1, 0.0, 0.0 ] } unit
, X.site { name: "s1", pos: 0.0 /\ 0.0 /\ 0.1, size: 0.01 /\ 0.01 /\ 0.01 }
, X.camera { name: "cam1", pos: 0.0 /\ (-1.0) /\ 0.5, fovy: 60.0 }
]
, X.body { name: "b2", pos: 1.0 /\ 0.0 /\ 0.5 }
[ X.joint { name: "j2", type: X.kw X.Hinge, axis: 1.0 /\ 0.0 /\ 0.0 }
, X.geom { name: "g2", type: X.kw X.Sphere, size: [ 0.1, 0.0, 0.0 ] } unit
, X.site { name: "s2", pos: 0.0 /\ 0.0 /\ 0.1, size: 0.01 /\ 0.01 /\ 0.01 }
]
]
, Act.actuator {} [ Act.motor { name: "a1", joint: "j1" } ]
, s
]
spec :: MjcfSpec Unit
spec =
describe "sensor" do
describe "site-based" do
it "touch" $ parseOk $ m $
X.sensor {} [ Sens.touch { name: "se_touch", site: "s1" } ]
it "accelerometer" $ parseOk $ m $
X.sensor {} [ Sens.accelerometer { name: "se_accel", site: "s1" } ]
it "velocimeter" $ parseOk $ m $
X.sensor {} [ Sens.velocimeter { name: "se_velo", site: "s1" } ]
it "gyro" $ parseOk $ m $
X.sensor {} [ Sens.gyro { name: "se_gyro", site: "s1" } ]
it "force" $ parseOk $ m $
X.sensor {} [ Sens.force { name: "se_force", site: "s1" } ]
it "torque" $ parseOk $ m $
X.sensor {} [ Sens.torque { name: "se_torque", site: "s1" } ]
it "magnetometer" $ parseOk $ m $
X.sensor {} [ Sens.magnetometer { name: "se_mag", site: "s1" } ]
it "rangefinder" $ parseOk $ m $
X.sensor {} [ Sens.rangefinder { name: "se_range", site: "s1" } ]
it "camprojection" $ parseOk $ m $
X.sensor {} [ Sens.camprojection { name: "se_camp", site: "s1", camera: "cam1" } ]
describe "joint-based" do
it "jointpos" $ parseOk $ m $
X.sensor {} [ Sens.jointpos { name: "se_jpos", joint: "j1" } ]
it "jointvel" $ parseOk $ m $
X.sensor {} [ Sens.jointvel { name: "se_jvel", joint: "j1" } ]
it "jointactuatorfrc" $ parseOk $ m $
X.sensor {} [ Sens.jointactuatorfrc { name: "se_jafrc", joint: "j1" } ]
it "jointlimitpos" $ parseOk $ m $
X.sensor {} [ Sens.jointlimitpos { name: "se_jlpos", joint: "j1" } ]
it "jointlimitvel" $ parseOk $ m $
X.sensor {} [ Sens.jointlimitvel { name: "se_jlvel", joint: "j1" } ]
it "jointlimitfrc" $ parseOk $ m $
X.sensor {} [ Sens.jointlimitfrc { name: "se_jlfrc", joint: "j1" } ]
describe "actuator-based" do
it "actuatorpos" $ parseOk $ m $
X.sensor {} [ Sens.actuatorpos { name: "se_apos", actuator: "a1" } ]
it "actuatorvel" $ parseOk $ m $
X.sensor {} [ Sens.actuatorvel { name: "se_avel", actuator: "a1" } ]
it "actuatorfrc" $ parseOk $ m $
X.sensor {} [ Sens.actuatorfrc { name: "se_afrc", actuator: "a1" } ]
describe "frame-based" do
it "framepos" $ parseOk $ m $
X.sensor {} [ Sens.framepos { name: "se_fpos", objtype: X.kw X.Body, objname: "b1" } ]
it "framequat" $ parseOk $ m $
X.sensor {} [ Sens.framequat { name: "se_fquat", objtype: X.kw X.Body, objname: "b1" } ]
it "framexaxis" $ parseOk $ m $
X.sensor {} [ Sens.framexaxis { name: "se_fxax", objtype: X.kw X.Body, objname: "b1" } ]
it "frameyaxis" $ parseOk $ m $
X.sensor {} [ Sens.frameyaxis { name: "se_fyax", objtype: X.kw X.Body, objname: "b1" } ]
it "framezaxis" $ parseOk $ m $
X.sensor {} [ Sens.framezaxis { name: "se_fzax", objtype: X.kw X.Body, objname: "b1" } ]
it "framelinvel" $ parseOk $ m $
X.sensor {} [ Sens.framelinvel { name: "se_flv", objtype: X.kw X.Body, objname: "b1" } ]
it "frameangvel" $ parseOk $ m $
X.sensor {} [ Sens.frameangvel { name: "se_fav", objtype: X.kw X.Body, objname: "b1" } ]
it "framelinvel with ref" $ parseOk $ m $
X.sensor {} [ Sens.framelinvel { name: "se_flvr", objtype: X.kw X.Body, objname: "b1", reftype: X.kw X.Body, refname: "b2" } ]
it "framelinacc" $ parseOk $ m $
X.sensor {} [ Sens.framelinacc { name: "se_fla", objtype: X.kw X.Body, objname: "b1" } ]
it "frameangacc" $ parseOk $ m $
X.sensor {} [ Sens.frameangacc { name: "se_faa", objtype: X.kw X.Body, objname: "b1" } ]
describe "subtree" do
it "subtreecom" $ parseOk $ m $
X.sensor {} [ Sens.subtreecom { name: "se_stcom", body: "b1" } ]
it "subtreelinvel" $ parseOk $ m $
X.sensor {} [ Sens.subtreelinvel { name: "se_stlv", body: "b1" } ]
it "subtreeangmom" $ parseOk $ m $
X.sensor {} [ Sens.subtreeangmom { name: "se_stam", body: "b1" } ]
describe "collision" do
it "distance" $ parseOk $ m $
X.sensor {} [ Sens.distance { name: "se_dist", geom1: "g1", geom2: "g2" } ]
it "normal" $ parseOk $ m $
X.sensor {} [ Sens.normal { name: "se_norm", geom1: "g1", geom2: "g2" } ]
it "fromto" $ parseOk $ m $
X.sensor {} [ Sens.fromto { name: "se_ft", geom1: "g1", geom2: "g2" } ]
it "contact" $ parseOk $ m $
X.sensor {} [ Sens.contact { name: "se_cont", body1: "b1", body2: "b2" } ]
describe "other" do
it "insidesite" $ parseOk $ m $
X.sensor {} [ Sens.insidesite { name: "se_inside", objtype: X.kw X.Geom, objname: "g1", site: "s1" } ]
it "e_potential" $ parseOk $ m $
X.sensor {} [ Sens.e_potential { name: "se_ep" } ]
it "e_kinetic" $ parseOk $ m $
X.sensor {} [ Sens.e_kinetic { name: "se_ek" } ]
it "clock" $ parseOk $ m $
X.sensor {} [ Sens.clock { name: "se_clock" } ]
it "user" $ parseOk $ m $
X.sensor {} [ Sens.user { name: "se_user", objtype: "body", objname: "b1", datatype: X.kw X.Real, needstage: X.kw X.Vel, dim: 1 } ]
describe "common props" do
it "noise + cutoff" $ parseOk $ m $
X.sensor {} [ Sens.touch { name: "se_noisy", site: "s1", noise: 0.01, cutoff: 100.0 } ]

View File

@@ -0,0 +1,64 @@
module Test.Mujoco.MJCF.Tendon where
import Prelude
import Data.Tuple.Nested ((/\))
import Mujoco.MJCF as X
import Mujoco.MJCF.Tendon as Tendon
import Mujoco.MJCF.XML (Node)
import Test.Mujoco.MJCF.Util (MjcfSpec, parseOk)
import Test.Spec (describe, it)
-- | Model with two bodies, two hinge joints, and two sites for tendon routing
m :: Node -> Node
m t = X.mujoco {}
[ X.compiler { inertiafromgeom: X.true_ }
, X.worldbody {}
[ X.body { name: "b1", pos: 0.0 /\ 0.0 /\ 0.5 }
[ X.joint { name: "j1", type: X.kw X.Hinge, axis: 1.0 /\ 0.0 /\ 0.0 }
, X.geom { type: X.kw X.Capsule, size: [ 0.05, 0.2 ] } unit
, X.site { name: "s1", pos: 0.0 /\ 0.0 /\ 0.2, size: 0.01 /\ 0.01 /\ 0.01 }
, X.body { name: "b2", pos: 0.0 /\ 0.0 /\ 0.5 }
[ X.joint { name: "j2", type: X.kw X.Hinge, axis: 1.0 /\ 0.0 /\ 0.0 }
, X.geom { type: X.kw X.Capsule, size: [ 0.05, 0.2 ] } unit
, X.site { name: "s2", pos: 0.0 /\ 0.0 /\ 0.2, size: 0.01 /\ 0.01 /\ 0.01 }
]
]
]
, t
]
spec :: MjcfSpec Unit
spec =
describe "tendon" do
describe "spatial" do
it "basic site routing" $ parseOk $ m $ Tendon.tendon {} $
Tendon.spatial { name: "t_spatial" }
[ Tendon.site { site: "s1" }
, Tendon.site { site: "s2" }
]
it "with pulley" $ parseOk $ m $ Tendon.tendon {} $
Tendon.spatial { name: "t_pulley" }
[ Tendon.site { site: "s1" }
, Tendon.pulley { divisor: 2.0 }
, Tendon.site { site: "s2" }
]
it "limited + range + stiffness + damping" $ parseOk $ m $ Tendon.tendon {} $
Tendon.spatial { name: "t_props", limited: X.true_, range: (-1.0) /\ 1.0, stiffness: 100.0, damping: 10.0 }
[ Tendon.site { site: "s1" }
, Tendon.site { site: "s2" }
]
describe "fixed" do
it "basic joint" $ parseOk $ m $ Tendon.tendon {} $
Tendon.fixed { name: "t_fixed" }
[ Tendon.joint { joint: "j1", coef: 1.0 }
, Tendon.joint { joint: "j2", coef: -1.0 }
]
it "stiffness + damping" $ parseOk $ m $ Tendon.tendon {} $
Tendon.fixed { name: "t_fixed2", stiffness: 50.0, damping: 5.0 }
[ Tendon.joint { joint: "j1", coef: 1.0 }
]

View File

@@ -0,0 +1,82 @@
module Test.Mujoco.MJCF.Visual where
import Prelude
import Data.Tuple.Nested ((/\))
import Mujoco.MJCF as X
import Mujoco.MJCF.Visual as Vis
import Mujoco.MJCF.XML (Node)
import Test.Mujoco.MJCF.Util (MjcfSpec, parseOk)
import Test.Spec (describe, it)
v :: Node -> Node
v c = X.mujoco {}
[ Vis.visual {} [ c ]
, X.worldbody {} unit
]
spec :: MjcfSpec Unit
spec =
describe "visual" do
describe "global" do
it "fovy + linewidth" $ parseOk $ v $
Vis.global { fovy: 45.0, linewidth: 2.0 }
it "offwidth + offheight" $ parseOk $ v $
Vis.global { offwidth: 1920, offheight: 1080 }
it "orthographic + ellipsoidinertia" $ parseOk $ v $
Vis.global { orthographic: true, ellipsoidinertia: true }
describe "quality" do
it "shadowsize + offsamples" $ parseOk $ v $
Vis.quality { shadowsize: 4096, offsamples: 8 }
it "numslices + numstacks + numquads" $ parseOk $ v $
Vis.quality { numslices: 32, numstacks: 32, numquads: 4 }
describe "headlight" do
it "ambient + diffuse + specular" $ parseOk $ v $
Vis.headlight { ambient: 0.3 /\ 0.3 /\ 0.3, diffuse: 0.6 /\ 0.6 /\ 0.6, specular: 0.2 /\ 0.2 /\ 0.2 }
it "active" $ parseOk $ v $
Vis.headlight { active: 0 }
describe "map" do
it "stiffness + force + torque" $ parseOk $ v $
Vis.map { stiffness: 100.0, force: 0.05, torque: 0.1 }
it "fogstart + fogend" $ parseOk $ v $
Vis.map { fogstart: 2.0, fogend: 10.0 }
it "znear + zfar" $ parseOk $ v $
Vis.map { znear: 0.01, zfar: 50.0 }
it "shadowclip + shadowscale" $ parseOk $ v $
Vis.map { shadowclip: 0.5, shadowscale: 0.5 }
describe "scale" do
it "forcewidth + contactwidth + contactheight" $ parseOk $ v $
Vis.scale { forcewidth: 0.1, contactwidth: 0.3, contactheight: 0.1 }
it "jointlength + jointwidth" $ parseOk $ v $
Vis.scale { jointlength: 0.5, jointwidth: 0.1 }
it "framelength + framewidth" $ parseOk $ v $
Vis.scale { framelength: 1.0, framewidth: 0.05 }
it "actuatorlength + actuatorwidth" $ parseOk $ v $
Vis.scale { actuatorlength: 0.7, actuatorwidth: 0.1 }
describe "rgba" do
it "fog + haze" $ parseOk $ v $
Vis.rgba { fog: 0.0 /\ 0.0 /\ 0.0 /\ 1.0, haze: 1.0 /\ 1.0 /\ 1.0 /\ 1.0 }
it "force + inertia + joint" $ parseOk $ v $
Vis.rgba { force: 1.0 /\ 0.5 /\ 0.5 /\ 1.0, inertia: 0.8 /\ 0.2 /\ 0.2 /\ 0.6, joint: 0.2 /\ 0.6 /\ 0.8 /\ 1.0 }
it "com + camera + light" $ parseOk $ v $
Vis.rgba { com: 0.0 /\ 1.0 /\ 0.0 /\ 1.0, camera: 0.6 /\ 0.9 /\ 0.6 /\ 1.0, light: 0.9 /\ 0.9 /\ 0.3 /\ 1.0 }
it "contact colors" $ parseOk $ v $
Vis.rgba { contactpoint: 0.9 /\ 0.2 /\ 0.2 /\ 1.0, contactforce: 0.2 /\ 0.9 /\ 0.2 /\ 1.0, contactfriction: 0.2 /\ 0.2 /\ 0.9 /\ 1.0, contacttorque: 0.9 /\ 0.9 /\ 0.2 /\ 1.0 }

View File

@@ -1,12 +1,13 @@
module Test.Mujoco.XML.Node.Prop where
module Test.Mujoco.MJCF.XML.Prop where
import Prelude
import Data.Tuple.Nested (type (/\), (/\))
import Mujoco.XML.Node.Prop (class SerializeProps', serialize, serializeProps)
import Mujoco.MJCF.XML.Prop (class SerializeProps', serialize, serializeProps)
import Prim.Row (class Union)
import Prim.RowList (class RowToList)
import Test.Spec (Spec, describe, it)
import Test.Mujoco.MJCF.Util (MjcfSpec)
import Test.Spec (describe, it)
import Test.Spec.Assertions (shouldEqual)
import Unsafe.Coerce (unsafeCoerce)
@@ -20,19 +21,20 @@ type Props =
, bool :: Boolean
)
spec :: Spec Unit
spec :: MjcfSpec Unit
spec =
describe "Mujoco.XML.Node.Prop" do
describe "Mujoco.MJCF.XML.Prop" do
describe "Serialize" do
it "serializes boolean" $ serialize true `shouldEqual` "true"
it "serializes string" $ serialize "a" `shouldEqual` "a"
it "serializes array" $ serialize ["a", "b"] `shouldEqual` "a b"
it "serializes nested array" $ serialize [["a", "b"], ["c"]] `shouldEqual` "a b c"
it "serializes array" $ serialize [ "a", "b" ] `shouldEqual` "a b"
it "serializes nested array" $ serialize [ [ "a", "b" ], [ "c" ] ] `shouldEqual` "a b c"
it "serializes int" $ serialize 1 `shouldEqual` "1"
it "serializes int array" $ serialize [1, 2] `shouldEqual` "1 2"
it "serializes number array" $ serialize [1.0, 2.0] `shouldEqual` "1 2"
it "serializes int array" $ serialize [ 1, 2 ] `shouldEqual` "1 2"
it "serializes number array" $ serialize [ 1.0, 2.0 ] `shouldEqual` "1 2"
it "serializes tuple" $ serialize (1 /\ 2) `shouldEqual` "1 2"
it "serializes nested tuple" $ serialize (1 /\ 2 /\ 3) `shouldEqual` "1 2 3"
it "serializes real(4)" $ serialize (1.0 /\ 1.0 /\ 0.5 /\ 0.1) `shouldEqual` "1 1 0.5 0.1"
describe "SerializeProps" do
let
@@ -48,4 +50,4 @@ spec =
it "handles empty" $ serializeProps' {} `shouldEqual` {}
it "handles int" $ serializeProps' { int: 1 } `shouldEqual` { int: "1" }
it "handles string" $ serializeProps' { string: "a" } `shouldEqual` { string: "a" }
it "handles array2" $ serializeProps' { array2: [[1, 2, 3], [4, 5]] } `shouldEqual` { array2: "1 2 3 4 5" }
it "handles array2" $ serializeProps' { array2: [ [ 1, 2, 3 ], [ 4, 5 ] ] } `shouldEqual` { array2: "1 2 3 4 5" }

235
test/Mujoco.MJCF.purs Normal file
View File

@@ -0,0 +1,235 @@
module Test.Mujoco.MJCF where
import Prelude
import Data.Tuple.Nested ((/\))
import Mujoco.MJCF as X
import Test.Mujoco.MJCF.Util (MjcfSpec, parseFail, parseOk, w)
import Test.Spec (describe, it)
spec :: MjcfSpec Unit
spec =
describe "MJCF" do
it "</>" $ parseFail $ X.empty
it "<mujoco>" $ parseOk $ X.mujoco {} unit
it "<worldbody>" $ parseOk $ X.mujoco {} $ X.worldbody {} unit
describe "compiler" do
it "empty" $ parseOk $ X.mujoco {}
[ X.compiler {}
, X.worldbody {} unit
]
it "angle=radian" $ parseOk $ X.mujoco {}
[ X.compiler { angle: X.kw X.Radian }
, X.worldbody {} unit
]
it "autolimits + coordinate" $ parseOk $ X.mujoco {}
[ X.compiler { autolimits: true, coordinate: X.kw X.Local }
, X.worldbody {} unit
]
it "inertiafromgeom=auto" $ parseOk $ X.mujoco {}
[ X.compiler { inertiafromgeom: X.kw X.Auto }
, X.worldbody {} unit
]
it "boundmass + boundinertia" $ parseOk $ X.mujoco {}
[ X.compiler { boundmass: 0.01, boundinertia: 0.001 }
, X.worldbody {} unit
]
describe "size" do
it "empty" $ parseOk $ X.mujoco {}
[ X.size {}
, X.worldbody {} unit
]
it "memory" $ parseOk $ X.mujoco {}
[ X.size { memory: "16M" }
, X.worldbody {} unit
]
it "nuser fields" $ parseOk $ X.mujoco {}
[ X.size { nuser_body: 2, nuser_jnt: 1, nuser_geom: 3 }
, X.worldbody {} unit
]
describe "option" do
it "timestep + integrator" $ parseOk $ X.mujoco {}
[ X.option { timestep: 0.001, integrator: X.kw X.RK4 } unit
, X.worldbody {} unit
]
it "gravity + solver" $ parseOk $ X.mujoco {}
[ X.option { gravity: 0.0 /\ 0.0 /\ (-9.81), solver: X.kw X.Newton } unit
, X.worldbody {} unit
]
describe "statistic" do
it "extent + center" $ parseOk $ X.mujoco {}
[ X.statistic { extent: 2.0, center: 0.0 /\ 0.0 /\ 1.0 }
, X.worldbody {} unit
]
describe "asset" do
describe "mesh" do
it "inline vertex" $ parseOk $ X.mujoco {}
[ X.asset {}
[ X.mesh { name: "tetra", vertex: [ 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0 ] } unit ]
, X.worldbody {} unit
]
describe "hfield" do
it "nrow + ncol + size" $ parseOk $ X.mujoco {}
[ X.asset {}
[ X.hfield { name: "terrain", nrow: 10, ncol: 10, size: 1.0 /\ 1.0 /\ 0.5 /\ 0.1 } ]
, X.worldbody {} unit
]
describe "texture" do
it "procedural checker" $ parseOk $ X.mujoco {}
[ X.asset {}
[ X.texture
{ name: "grid"
, type: X.kw X.TwoD
, builtin: X.kw X.Checker
, width: 512
, height: 512
, rgb1: 0.9 /\ 0.9 /\ 0.9
, rgb2: 0.1 /\ 0.1 /\ 0.1
}
]
, X.worldbody {} unit
]
describe "material" do
it "rgba + specular" $ parseOk $ X.mujoco {}
[ X.asset {}
[ X.material { name: "mat1", rgba: 0.8 /\ 0.2 /\ 0.2 /\ 1.0, specular: 0.8 } unit ]
, X.worldbody {} unit
]
describe "body" do
describe "joint" do
it "hinge" $ parseOk $ w $
X.body { name: "b1", pos: 0.0 /\ 0.0 /\ 0.5 }
[ X.joint { type: X.kw X.Hinge, axis: 1.0 /\ 0.0 /\ 0.0 }
, X.geom { type: X.kw X.Sphere, size: [ 0.1, 0.0, 0.0 ] } unit
]
it "slide with range" $ parseOk $ w $
X.body { name: "slider", pos: 0.0 /\ 0.0 /\ 1.0 }
[ X.joint { type: X.kw X.Slide, axis: 0.0 /\ 0.0 /\ 1.0, range: (-1.0) /\ 1.0, limited: X.true_ }
, X.geom { type: X.kw X.Box, size: [ 0.1, 0.1, 0.1 ] } unit
]
it "stiffness + damping" $ parseOk $ w $
X.body { pos: 0.0 /\ 0.0 /\ 0.0 }
[ X.joint { type: X.kw X.Hinge, stiffness: 100.0, damping: 10.0, armature: 0.1 }
, X.geom { type: X.kw X.Sphere, size: [ 0.05, 0.0, 0.0 ] } unit
]
describe "freejoint" do
it "basic" $ parseOk $ w $
X.body { name: "free_body", pos: 0.0 /\ 0.0 /\ 1.0 }
[ X.freejoint { name: "fj" }
, X.geom { type: X.kw X.Sphere, size: [ 0.1, 0.0, 0.0 ] } unit
]
describe "geom" do
it "sphere" $ parseOk $ w $
X.geom { type: X.kw X.Sphere, size: [ 1.0, 0.0, 0.0 ] } unit
it "capsule fromto" $ parseOk $ w $
X.geom { type: X.kw X.Capsule, fromto: [ 0.0, 0.0, 0.0, 0.0, 0.0, 1.0 ], size: [ 0.05, 0.0, 0.0 ] } unit
it "box with material" $ parseOk $ X.mujoco {}
[ X.asset {}
[ X.material { name: "red", rgba: 1.0 /\ 0.0 /\ 0.0 /\ 1.0 } unit ]
, X.worldbody {} $
X.geom { type: X.kw X.Box, size: [ 0.5, 0.5, 0.5 ], material: "red" } unit
]
it "plane" $ parseOk $ w $
X.geom { type: X.kw X.Plane, size: [ 5.0, 5.0, 0.1 ] } unit
it "friction + density" $ parseOk $ w $
X.geom { type: X.kw X.Sphere, size: [ 0.1, 0.0, 0.0 ], friction: 0.5 /\ 0.005 /\ 0.0001, density: 500.0 } unit
describe "site" do
it "basic" $ parseOk $ w $
X.body { pos: 0.0 /\ 0.0 /\ 0.0 }
[ X.geom { type: X.kw X.Sphere, size: [ 0.1, 0.0, 0.0 ] } unit
, X.site { name: "s1", pos: 0.0 /\ 0.0 /\ 0.1, size: 0.01 /\ 0.01 /\ 0.01 }
]
describe "camera" do
it "fixed" $ parseOk $ w $
X.camera { name: "cam1", pos: 0.0 /\ (-2.0) /\ 1.0, fovy: 60.0 }
it "tracking" $ parseOk $ w $
X.body { name: "target_body", pos: 0.0 /\ 0.0 /\ 0.5 }
[ X.geom { type: X.kw X.Sphere, size: [ 0.1, 0.0, 0.0 ] } unit
, X.camera { name: "tracker", mode: X.kw X.Targetbody, target: "target_body", pos: 1.0 /\ 0.0 /\ 0.5 }
]
describe "light" do
it "spotlight" $ parseOk $ w $
X.light { name: "spot1", pos: 0.0 /\ 0.0 /\ 3.0, dir: 0.0 /\ 0.0 /\ (-1.0), diffuse: 1.0 /\ 1.0 /\ 1.0 }
it "directional" $ parseOk $ w $
X.light { name: "sun", type: X.kw X.Directional, dir: 0.0 /\ (-1.0) /\ (-1.0), castshadow: true }
describe "inertial" do
it "explicit mass + diaginertia" $ parseOk $ w $
X.body { pos: 0.0 /\ 0.0 /\ 0.0 }
[ X.inertial { pos: 0.0 /\ 0.0 /\ 0.0, mass: 1.0, diaginertia: 0.01 /\ 0.01 /\ 0.01 }
, X.geom { type: X.kw X.Sphere, size: [ 0.1, 0.0, 0.0 ] } unit
]
describe "composite" do
it "full model"
$ parseOk
$ X.mujoco { model: "test" }
[ X.compiler { angle: X.kw X.Radian, inertiafromgeom: X.true_ }
, X.option { timestep: 0.002, gravity: 0.0 /\ 0.0 /\ (-9.81) }
[ X.flag { contact: X.kw X.Enable } ]
, X.asset {}
[ X.texture { name: "grid", type: X.kw X.TwoD, builtin: X.kw X.Checker, width: 256, height: 256, rgb1: 0.9 /\ 0.9 /\ 0.9, rgb2: 0.1 /\ 0.1 /\ 0.1 }
, X.material { name: "floor_mat", texture: "grid", texrepeat: 5.0 /\ 5.0 } unit
]
, X.worldbody {}
[ X.geom { type: X.kw X.Plane, size: [ 5.0, 5.0, 0.1 ], material: "floor_mat" } unit
, X.light { name: "top", pos: 0.0 /\ 0.0 /\ 3.0, dir: 0.0 /\ 0.0 /\ (-1.0) }
, X.body { name: "ball", pos: 0.0 /\ 0.0 /\ 1.0 }
[ X.freejoint {}
, X.geom { type: X.kw X.Sphere, size: [ 0.1, 0.0, 0.0 ], rgba: 1.0 /\ 0.0 /\ 0.0 /\ 1.0 } unit
]
]
]
it "bodies with joints" $ parseOk $ w
[ X.light { name: "top", pos: 0.0 /\ 0.0 /\ 1.0 }
, X.body {}
[ X.joint { name: "v0_rx", damping: 1.0, stiffness: 10.0, axis: 1.0 /\ 0.0 /\ 0.0, pos: 0.0 /\ 0.0 /\ (-0.5) }
, X.joint { name: "v0_ry", damping: 1.0, stiffness: 10.0, axis: 0.0 /\ 1.0 /\ 0.0, pos: 0.0 /\ 0.0 /\ (-0.5) }
, X.geom { type: X.kw X.Cylinder, size: [ 1.0, 0.05 ] } unit
, X.body { pos: zero /\ zero /\ 0.51 }
[ X.joint { name: "b0a_rx", damping: 1.0, stiffness: 10.0, axis: 1.0 /\ 0.0 /\ 0.0, pos: 0.0 /\ 0.0 /\ (-0.5) }
, X.joint { name: "b0a_ry", damping: 1.0, stiffness: 10.0, axis: 0.0 /\ 1.0 /\ 0.0, pos: 0.0 /\ 0.0 /\ (-0.5) }
, X.geom { type: X.kw X.Cylinder, size: [ 0.05, 0.5 ] } unit
, X.body { pos: zero /\ zero /\ one }
[ X.joint { name: "b0b_rx", damping: 1.0, stiffness: 10.0, axis: 1.0 /\ 0.0 /\ 0.0, pos: 0.0 /\ 0.0 /\ (-0.5) }
, X.joint { name: "b0b_ry", damping: 1.0, stiffness: 10.0, axis: 0.0 /\ 1.0 /\ 0.0, pos: 0.0 /\ 0.0 /\ (-0.5) }
, X.geom { type: X.kw X.Cylinder, size: [ 0.05, 0.5 ] } unit
, X.body { pos: zero /\ zero /\ 0.5 }
[ X.joint { name: "v1_rx", damping: 1.0, stiffness: 10.0, axis: 1.0 /\ 0.0 /\ 0.0, pos: 0.0 /\ 0.0 /\ (-0.5) }
, X.joint { name: "v1_ry", damping: 1.0, stiffness: 10.0, axis: 0.0 /\ 1.0 /\ 0.0, pos: 0.0 /\ 0.0 /\ (-0.5) }
, X.geom { type: X.kw X.Cylinder, size: [ 1.0, 0.05 ] } unit
]
]
]
]
]

10
test/Mujoco.Wasm.js Normal file
View File

@@ -0,0 +1,10 @@
import load_mujoco from 'mujoco_wasm/mujoco_wasm.js'
/** @typedef {import('mujoco_wasm').MainModule} Mujoco */
/** @typedef {import('mujoco_wasm').MjSpec} Spec */
/** @type {() => Promise<Mujoco>} */
export const loadMujoco_ = () => load_mujoco()
/** @type {(mj: Mujoco) => (xml: String) => () => Spec} */
export const parseXMLString = m => xml => () => m.parseXMLString(xml)

15
test/Mujoco.Wasm.purs Normal file
View File

@@ -0,0 +1,15 @@
module Mujoco.Wasm where
import Control.Promise (Promise)
import Control.Promise as Promise
import Effect (Effect)
import Effect.Aff (Aff)
foreign import data Mujoco :: Type
foreign import data Spec :: Type
foreign import loadMujoco_ :: Effect (Promise Mujoco)
foreign import parseXMLString :: Mujoco -> String -> Effect Spec
loadMujoco :: Aff Mujoco
loadMujoco = Promise.toAffE loadMujoco_

View File

@@ -0,0 +1,70 @@
module Test.Mujoco.MJCF.Util where
import Prelude
import Control.Monad.Error.Class (class MonadError, class MonadThrow, try)
import Control.Monad.Reader (class MonadAsk, class MonadReader, ReaderT, ask, local, runReaderT)
import Data.Either (isLeft)
import Data.Identity (Identity)
import Data.Newtype (class Newtype, unwrap)
import Effect.Aff (Aff)
import Effect.Aff.Class (class MonadAff)
import Effect.Class (class MonadEffect, liftEffect)
import Effect.Console as Console
import Effect.Exception (Error)
import Mujoco.MJCF as X
import Mujoco.MJCF.XML (Node)
import Mujoco.MJCF.XML as XML
import Mujoco.Wasm (Mujoco)
import Mujoco.Wasm as Mj
import Test.Assert (assertTrue)
import Test.Spec (SpecT, Spec, hoistSpec)
type T' = { mj :: Mujoco, dbg :: Boolean }
newtype T a = T (ReaderT T' Aff a)
derive instance Newtype (T a) _
derive newtype instance Functor T
derive newtype instance Applicative T
derive newtype instance Apply T
derive newtype instance Bind T
derive newtype instance Monad T
derive newtype instance MonadReader T' T
derive newtype instance MonadAsk T' T
derive newtype instance MonadEffect T
derive newtype instance MonadAff T
derive newtype instance MonadThrow Error T
derive newtype instance MonadError Error T
type MjcfSpec a = SpecT T Unit Identity a
mjcf :: forall a. Mujoco -> MjcfSpec a -> Spec a
mjcf mj = hoistSpec identity (\_ -> runT mj)
runT :: forall a. Mujoco -> T a -> Aff a
runT mj m = runReaderT (unwrap m) { mj, dbg: false }
dbg :: forall a. T a -> T a
dbg = local (_ { dbg = true })
renderSpec :: XML.Node -> T Mj.Spec
renderSpec node = do
{ mj, dbg: dbg' } <- ask
let
xml = XML.render node
when dbg'
$ liftEffect
$ Console.log
$ xml
liftEffect $ Mj.parseXMLString mj xml
parseOk :: XML.Node -> T Unit
parseOk = void <<< renderSpec
parseFail :: XML.Node -> T Unit
parseFail = (liftEffect <<< assertTrue <<< isLeft) <=< (try <<< renderSpec)
w :: forall a. XML.Children a => a -> Node
w = X.mujoco {} <<< X.worldbody {}