more shit

This commit is contained in:
orion kindel
2026-02-23 11:40:11 -06:00
parent b1a1264be1
commit 3029ea0e68
14 changed files with 414 additions and 91 deletions

View File

@@ -148,5 +148,3 @@ type Props_model =
)
model = tagNoContent @Props_model "model" :: TagNoContent Props_model
type Props_plugin = (plugin :: String, instance :: String)
plugin = tag @Props_plugin "plugin" :: Tag Props_plugin

View File

@@ -1,7 +1,38 @@
module Mujoco.MJCF.Body where
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.Prelude
import Mujoco.MJCF.Geom (Props_geom, geom) as X
data SleepPolicy = SleepAuto | SleepNever | SleepAllowed | SleepInit
instance Serialize SleepPolicy where
serialize SleepAuto = "auto"
@@ -46,12 +77,6 @@ instance Serialize JointType where
serialize Slide = "slide"
serialize Hinge = "hinge"
data AutoBool = AutoBoolFalse | AutoBoolTrue | AutoBoolAuto
instance Serialize AutoBool where
serialize AutoBoolFalse = "false"
serialize AutoBoolTrue = "true"
serialize AutoBoolAuto = "auto"
type Props_joint =
( name :: String
, class :: String
@@ -66,9 +91,9 @@ type Props_joint =
, solimpfriction :: Vec5 Real
, stiffness :: Real
, range :: Real /\ Real
, limited :: AutoBool
, limited :: Auto \/ Boolean
, actuatorfrcrange :: Real /\ Real
, actuatorfrclimited :: AutoBool
, actuatorfrclimited :: Auto \/ Boolean
, actuatorgravcomp :: Boolean
, margin :: Real
, ref :: Real
@@ -83,64 +108,10 @@ joint = tagNoContent @Props_joint "joint" :: TagNoContent Props_joint
type Props_freejoint =
( name :: String
, group :: Int
, align :: AutoBool
, align :: Auto \/ Boolean
)
freejoint = tagNoContent @Props_freejoint "freejoint" :: TagNoContent Props_freejoint
data GeomType = GPlane | GHfield | GSphere | GCapsule | GEllipsoid | GCylinder | GBox | GMesh | GSdf
instance Serialize GeomType where
serialize GPlane = "plane"
serialize GHfield = "hfield"
serialize GSphere = "sphere"
serialize GCapsule = "capsule"
serialize GEllipsoid = "ellipsoid"
serialize GCylinder = "cylinder"
serialize GBox = "box"
serialize GMesh = "mesh"
serialize GSdf = "sdf"
data FluidShape = FluidNone | FluidEllipsoid
instance Serialize FluidShape where
serialize FluidNone = "none"
serialize FluidEllipsoid = "ellipsoid"
type Props_geom =
( name :: String
, class :: String
, type :: GeomType
, contype :: Int
, conaffinity :: Int
, condim :: Int
, group :: Int
, priority :: Int
, size :: Array Real
, material :: String
, rgba :: Vec4 Real
, friction :: Vec Real
, mass :: Real
, density :: Real
, shellinertia :: Boolean
, solmix :: Real
, solref :: Real /\ Real
, solimp :: Vec5 Real
, margin :: Real
, gap :: Real
, fromto :: Array Real
, pos :: Vec Real
, quat :: Vec4 Real
, axisangle :: Vec4 Real
, xyaxes :: Array Real
, zaxis :: Vec Real
, euler :: Vec Real
, hfield :: String
, mesh :: String
, fitscale :: Real
, fluidshape :: FluidShape
, fluidcoef :: Vec5 Real
, user :: Array Real
)
geom = tag @Props_geom "geom" :: Tag Props_geom
data SiteType = SiteSphere | SiteCapsule | SiteEllipsoid | SiteCylinder | SiteBox
instance Serialize SiteType where
serialize SiteSphere = "sphere"
@@ -246,3 +217,22 @@ type Props_light =
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,24 @@
module Mujoco.MJCF.Common where
import Data.Tuple.Nested (type (/\))
import Mujoco.XML.Node.Prop (class Serialize)
type Named r = (name :: String | r)
type Pos r = (pos :: Vec Real | r)
type Oriented r =
( quat :: Vec4 Real
, axisangle :: Vec4 Real
, xyaxes :: Array Real
, zaxis :: Vec Real
, euler :: Vec Real
| r
)
data Auto = Auto
instance Serialize Auto where
serialize Auto = "auto"
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,27 @@
module Mujoco.MJCF.Contact where
import Mujoco.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,65 @@
module Mujoco.MJCF.Deformable.Flex where
import Mujoco.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,25 @@
module Mujoco.MJCF.Deformable.Skin where
import Mujoco.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.Prelude
deformable = tag @() "deformable" :: Tag ()

View File

@@ -0,0 +1,63 @@
module Mujoco.MJCF.Equality where
import Mujoco.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

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

@@ -0,0 +1,69 @@
module Mujoco.MJCF.Geom where
import Mujoco.Prelude
import Prim hiding (Type)
data FluidShape = FluidNone | FluidEllipsoid
instance Serialize FluidShape where
serialize FluidNone = "none"
serialize FluidEllipsoid = "ellipsoid"
data Type = Plane | Hfield | Sphere | Capsule | Ellipsoid | Cylinder | Box | Mesh | Sdf
instance Serialize Type where
serialize Plane = "plane"
serialize Hfield = "hfield"
serialize Sphere = "sphere"
serialize Capsule = "capsule"
serialize Ellipsoid = "ellipsoid"
serialize Cylinder = "cylinder"
serialize Box = "box"
serialize Mesh = "mesh"
serialize Sdf = "sdf"
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 :: Type
, 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 :: FluidShape
, fluidcoef :: Vec5 Real
, user :: Array Real
| Named
+ Contact
+ ContactSpacing
+ Solver
+ Pos
+ Oriented ()
)
geom = tag @Props_geom "geom" :: Tag Props_geom

View File

@@ -0,0 +1,47 @@
module Mujoco.MJCF.Tendon where
import Mujoco.Prelude
type Common r =
( class :: String
, group :: Int
, limited :: 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 :: 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

View File

@@ -12,7 +12,9 @@ module Mujoco.MJCF
, Props_size
, Props_mujoco
, Props_statistic
, Props_plugin
, Solver(..)
, plugin
, compiler
, flag
, mujoco
@@ -24,8 +26,11 @@ module Mujoco.MJCF
where
import Mujoco.Prelude
import Mujoco.MJCF.Asset as X
import Mujoco.MJCF.Body as X
import Mujoco.MJCF.Common (Auto(..)) as X
import Mujoco.MJCF.Contact as X
import Mujoco.XML.Node (empty, text, fragment) as X
type Props_mujoco = (model :: String)
@@ -184,3 +189,7 @@ type Props_statistic =
)
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

@@ -1,6 +1,9 @@
module Mujoco.Prelude (module X, Real, Vec, Vec4, Vec5) where
module Mujoco.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 as X
import Mujoco.XML.Node
( tag
, tagNoContent
@@ -12,10 +15,4 @@ import Mujoco.XML.Node.Prop (class Serialize, serialize) as X
import Data.Tuple (Tuple(..)) as X
import Data.Tuple.Nested (type (/\), (/\)) as X
import Data.Tuple.Nested (type (/\))
import Data.Maybe (Maybe(..), maybe, fromMaybe) as X
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

@@ -3,6 +3,7 @@ module Mujoco.XML.Node.Prop (class Serialize, serialize, serializeProps, class S
import Prelude
import Data.Array as Array
import Data.Either (Either, either)
import Data.Int as Int
import Data.Map (Map)
import Data.Map as Map
@@ -32,6 +33,8 @@ else instance Serialize Number where
serialize = Number.toString
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

View File

@@ -8,6 +8,7 @@ import Data.Tuple.Nested ((/\))
import Effect.Aff (Aff)
import Effect.Class (liftEffect)
import Mujoco.MJCF as X
import Mujoco.MJCF.Geom as X.G
import Mujoco.Wasm (renderSpec)
import Mujoco.XML.Node (Node)
import Mujoco.XML.Node as XML
@@ -132,52 +133,52 @@ spec =
it "hinge" $ ok $ w $
X.body { name: "b1", pos: 0.0 /\ 0.0 /\ 0.5 }
[ X.joint { type: X.Hinge, axis: 1.0 /\ 0.0 /\ 0.0 }
, X.geom { type: X.GSphere, size: [0.1, 0.0, 0.0] } unit
, X.geom { type: X.G.Sphere, size: [0.1, 0.0, 0.0] } unit
]
it "slide with range" $ ok $ w $
X.body { name: "slider", pos: 0.0 /\ 0.0 /\ 1.0 }
[ X.joint { type: X.Slide, axis: 0.0 /\ 0.0 /\ 1.0, range: (-1.0) /\ 1.0, limited: X.AutoBoolTrue }
, X.geom { type: X.GBox, size: [0.1, 0.1, 0.1] } unit
, X.geom { type: X.G.Box, size: [0.1, 0.1, 0.1] } unit
]
it "stiffness + damping" $ ok $ w $
X.body { pos: 0.0 /\ 0.0 /\ 0.0 }
[ X.joint { type: X.Hinge, stiffness: 100.0, damping: 10.0, armature: 0.1 }
, X.geom { type: X.GSphere, size: [0.05, 0.0, 0.0] } unit
, X.geom { type: X.G.Sphere, size: [0.05, 0.0, 0.0] } unit
]
describe "freejoint" do
it "basic" $ ok $ w $
X.body { name: "free_body", pos: 0.0 /\ 0.0 /\ 1.0 }
[ X.freejoint { name: "fj" }
, X.geom { type: X.GSphere, size: [0.1, 0.0, 0.0] } unit
, X.geom { type: X.G.Sphere, size: [0.1, 0.0, 0.0] } unit
]
describe "geom" do
it "sphere" $ ok $ w $
X.geom { type: X.GSphere, size: [1.0, 0.0, 0.0] } unit
X.geom { type: X.G.Sphere, size: [1.0, 0.0, 0.0] } unit
it "capsule fromto" $ ok $ w $
X.geom { type: X.GCapsule, fromto: [0.0, 0.0, 0.0, 0.0, 0.0, 1.0], size: [0.05, 0.0, 0.0] } unit
X.geom { type: X.G.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" $ ok $ X.mujoco {}
[ X.asset {}
[ X.material { name: "red", rgba: 1.0 /\ 0.0 /\ 0.0 /\ 1.0 } unit ]
, X.worldbody {} $
X.geom { type: X.GBox, size: [0.5, 0.5, 0.5], material: "red" } unit
X.geom { type: X.G.Box, size: [0.5, 0.5, 0.5], material: "red" } unit
]
it "plane" $ ok $ w $
X.geom { type: X.GPlane, size: [5.0, 5.0, 0.1] } unit
X.geom { type: X.G.Plane, size: [5.0, 5.0, 0.1] } unit
it "friction + density" $ ok $ w $
X.geom { type: X.GSphere, size: [0.1, 0.0, 0.0], friction: 0.5 /\ 0.005 /\ 0.0001, density: 500.0 } unit
X.geom { type: X.G.Sphere, size: [0.1, 0.0, 0.0], friction: 0.5 /\ 0.005 /\ 0.0001, density: 500.0 } unit
describe "site" do
it "basic" $ ok $ w $
X.body { pos: 0.0 /\ 0.0 /\ 0.0 }
[ X.geom { type: X.GSphere, size: [0.1, 0.0, 0.0] } unit
[ X.geom { type: X.G.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 }
]
@@ -187,7 +188,7 @@ spec =
it "tracking" $ ok $ w $
X.body { name: "target_body", pos: 0.0 /\ 0.0 /\ 0.5 }
[ X.geom { type: X.GSphere, size: [0.1, 0.0, 0.0] } unit
[ X.geom { type: X.G.Sphere, size: [0.1, 0.0, 0.0] } unit
, X.camera { name: "tracker", mode: X.CamTargetbody, target: "target_body", pos: 1.0 /\ 0.0 /\ 0.5 }
]
@@ -202,7 +203,7 @@ spec =
it "explicit mass + diaginertia" $ ok $ 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.GSphere, size: [0.1, 0.0, 0.0] } unit
, X.geom { type: X.G.Sphere, size: [0.1, 0.0, 0.0] } unit
]
describe "composite" do
@@ -217,11 +218,11 @@ spec =
, X.material { name: "floor_mat", texture: "grid", texrepeat: 5.0 /\ 5.0 } unit
]
, X.worldbody {}
[ X.geom { type: X.GPlane, size: [5.0, 5.0, 0.1], material: "floor_mat" } unit
[ X.geom { type: X.G.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.GSphere, size: [0.1, 0.0, 0.0], rgba: 1.0 /\ 0.0 /\ 0.0 /\ 1.0 } unit
, X.geom { type: X.G.Sphere, size: [0.1, 0.0, 0.0], rgba: 1.0 /\ 0.0 /\ 0.0 /\ 1.0 } unit
]
]
]
@@ -231,19 +232,19 @@ spec =
, 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.GCylinder, size: [1.0, 0.05] } unit
, X.geom { type: X.G.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.GCylinder, size: [0.05, 0.5] } unit
, X.geom { type: X.G.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.GCylinder, size: [0.05, 0.5] } unit
, X.geom { type: X.G.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.GCylinder, size: [1.0, 0.05] } unit
, X.geom { type: X.G.Cylinder, size: [1.0, 0.05] } unit
]
]
]