@@ -14,6 +14,46 @@ describe("Mirabuf Parser Tests", () => {
1414 const rn = [ ...t . rigidNodes . values ( ) ]
1515
1616 expect ( filterNonPhysicsNodes ( rn , spikeMira ! ) . length ) . toBe ( 7 )
17+
18+ // Validate joints
19+ const jointValidation = validateJoints ( spikeMira ! )
20+ expect ( jointValidation . isValid ) . toBe ( true )
21+ expect ( jointValidation . jointCount ) . toBe ( 6 )
22+ expect ( jointValidation . wheelJoints ) . toBe ( 6 )
23+ expect ( jointValidation . allJoints ) . toContain ( mirabuf . joint . JointMotion . REVOLUTE ) // Wheels are revolute joints
24+ expect ( jointValidation . allJoints ) . not . toContain ( mirabuf . joint . JointMotion . SLIDER ) // Dozer has no slider joints
25+ } )
26+
27+ /*
28+ * Multi-Joint Wheels robot contains
29+ * - 4 wheels (4 revolute joints)
30+ * - 2 additional revolute joints
31+ * - 2 slider joints
32+ * Mira File: https://synthesis.autodesk.com/api/mira/private/Multi-Joint_Wheels_v0.mira
33+ */
34+ test ( "Generate Rigid Nodes (Multi-Joint Wheels)" , async ( ) => {
35+ const spikeMira = await MirabufCachingService . cacheRemote (
36+ "/api/mira/private/Multi-Joint_Wheels_v0.mira" ,
37+ MiraType . ROBOT
38+ ) . then ( x => MirabufCachingService . get ( x ! . id , MiraType . ROBOT ) )
39+
40+ const t = new MirabufParser ( spikeMira ! )
41+ const rn = [ ...t . rigidNodes . values ( ) ]
42+
43+ expect ( filterNonPhysicsNodes ( rn , spikeMira ! ) . length ) . toBe ( 9 )
44+
45+ // Validate joints
46+ const jointValidation = validateJoints ( spikeMira ! )
47+ expect ( jointValidation . isValid ) . toBe ( true )
48+ expect ( jointValidation . jointCount ) . toBe ( 8 )
49+
50+ // Validate joint type distribution
51+ const revoluteJoints = jointValidation . allJoints . filter ( j => j === mirabuf . joint . JointMotion . REVOLUTE )
52+ const sliderJoints = jointValidation . allJoints . filter ( j => j === mirabuf . joint . JointMotion . SLIDER )
53+
54+ expect ( revoluteJoints . length ) . toBe ( 6 ) // Should have 6 revolute joints (4 wheels + 2 additional)
55+ expect ( sliderJoints . length ) . toBe ( 2 ) // Should have 2 slider joints
56+ expect ( jointValidation . wheelJoints ) . toBe ( 4 ) // Should have 4 wheel joints
1757 } )
1858
1959 test ( "Generate Rigid Nodes (FRC Field 2018_v13.mira)" , async ( ) => {
@@ -40,6 +80,116 @@ function filterNonPhysicsNodes(nodes: RigidNodeReadOnly[], mira: mirabuf.Assembl
4080 } )
4181}
4282
83+ interface JointValidationResult {
84+ isValid : boolean
85+ jointCount : number
86+ allJoints : mirabuf . joint . JointMotion [ ]
87+ wheelJoints : number
88+ errors : string [ ]
89+ warnings : string [ ]
90+ }
91+
92+ function validateJoints ( assembly : mirabuf . Assembly ) : JointValidationResult {
93+ const result : JointValidationResult = {
94+ isValid : true ,
95+ jointCount : 0 ,
96+ allJoints : [ ] ,
97+ wheelJoints : 0 ,
98+ errors : [ ] ,
99+ warnings : [ ] ,
100+ }
101+
102+ const jointData = assembly . data ?. joints
103+ if ( ! jointData ) {
104+ result . errors . push ( "No joint data found in assembly" )
105+ result . isValid = false
106+ return result
107+ }
108+
109+ // Validate joint definitions and instances
110+ const jointDefinitions = jointData . jointDefinitions || { }
111+ const jointInstances = jointData . jointInstances || { }
112+
113+ // Count non-grounded joints
114+ const nonGroundedJoints = Object . entries ( jointInstances ) . filter ( ( [ key ] ) => key !== "grounded" )
115+ result . jointCount = nonGroundedJoints . length
116+
117+ // Validate each joint
118+ for ( const [ jointId , jointInstance ] of nonGroundedJoints ) {
119+ try {
120+ // Check if joint definition exists
121+ const jointDef = jointDefinitions [ jointInstance . jointReference ! ]
122+ if ( ! jointDef ) {
123+ result . errors . push (
124+ `Joint instance '${ jointId } ' references missing definition '${ jointInstance . jointReference } '`
125+ )
126+ result . isValid = false
127+ continue
128+ }
129+
130+ // Get all joints
131+ if ( jointDef . jointMotionType !== null && jointDef . jointMotionType !== undefined )
132+ result . allJoints . push ( jointDef . jointMotionType )
133+
134+ // Check for wheel joints
135+ if (
136+ jointDef . userData ?. data ?. wheel === "true" ||
137+ ( jointDef . jointMotionType === mirabuf . joint . JointMotion . REVOLUTE &&
138+ jointDef . userData ?. data ?. wheelType !== undefined )
139+ ) {
140+ result . wheelJoints ++
141+ }
142+
143+ // Validate joint motion type specific properties
144+ switch ( jointDef . jointMotionType ) {
145+ case mirabuf . joint . JointMotion . REVOLUTE :
146+ if ( ! jointDef . rotational ) {
147+ result . errors . push ( `Revolute joint '${ jointId } ' missing rotational definition` )
148+ result . isValid = false
149+ }
150+ break
151+ case mirabuf . joint . JointMotion . SLIDER :
152+ if ( ! jointDef . prismatic ) {
153+ result . errors . push ( `Slider joint '${ jointId } ' missing prismatic definition` )
154+ result . isValid = false
155+ }
156+ break
157+ case mirabuf . joint . JointMotion . BALL :
158+ // Note: Ball joint properties are validated differently in the mirabuf format
159+ if ( ! jointDef . custom ) {
160+ result . warnings . push ( `Ball joint '${ jointId } ' may be missing ball-specific configuration` )
161+ }
162+ break
163+ case mirabuf . joint . JointMotion . CUSTOM :
164+ if ( ! jointDef . custom ) {
165+ result . errors . push ( `Custom joint '${ jointId } ' missing custom definition` )
166+ result . isValid = false
167+ }
168+ break
169+ }
170+
171+ // Validate joint has an origin
172+ if ( ! jointDef . origin ) {
173+ result . warnings . push ( `Joint '${ jointId } ' has no origin defined` )
174+ }
175+ } catch ( error ) {
176+ result . errors . push ( `Error validating joint '${ jointId } ': ${ error } ` )
177+ result . isValid = false
178+ }
179+ }
180+
181+ // Validate rigid groups if they exist
182+ if ( jointData . rigidGroups ) {
183+ for ( const rigidGroup of jointData . rigidGroups ) {
184+ if ( ! rigidGroup . occurrences || rigidGroup . occurrences . length < 2 ) {
185+ result . warnings . push ( `Rigid group '${ rigidGroup . name } ' has fewer than 2 occurrences` )
186+ }
187+ }
188+ }
189+
190+ return result
191+ }
192+
43193// function printRigidNodeParts(nodes: RigidNodeReadOnly[], mira: mirabuf.Assembly) {
44194// nodes.forEach(x => {
45195// console.log(`[ ${x.name} ]:`);
0 commit comments