import {bodyMatrices,jointValue} from './vendor/kinematics.js'; export type MotionJoint={id:string;label?:string|null;name?:string|null;type?:string;home:number;far:number;range?:number[];body0?:string;body1?:string;partial?:boolean}; export function motionTitle(joint:MotionJoint,index:number){ const label=(joint.label||joint.name||'').replace(/_/g,' ').replace(/([a-z])([A-Z])/g,'$1 $2').trim(); if(/^hinged part\s*\d*$/i.test(label))return `Rotation ${index+1}`; if(/^sliding part\s*\d*$/i.test(label))return `Slide ${index+1}`; if(label&&!/^j\d+$|^joint\s*\d*$/i.test(label))return label[0].toUpperCase()+label.slice(1); return `${joint.type==='prismatic'?'Slide':'Rotation'} ${index+1}`; } export function motionValue(joint:MotionJoint,progress:number){ const value=jointValue(joint,progress); return joint.type==='prismatic'?`${Math.round(value*1000)/10} cm`:`${Math.round(value)}°`; } export function selectedPose(values:Record,id:string,progress:number){ return {...values,[id]:Math.min(1,Math.max(0,Number.isFinite(progress)?progress:0))}; } /** Compare resolved kinematics, so mimic joints and descendants are highlighted too. */ export function affectedBodies(articulation:any,id:string,values:Record={}){ const start=bodyMatrices(articulation,selectedPose(values,id,0)) as Record; const samples=[.37,1].map(progress=>bodyMatrices(articulation,selectedPose(values,id,progress)) as Record); return new Set(Object.keys(start).filter(body=>samples.some(sample=>sample[body].some((v:number,i:number)=>Math.abs(v-start[body][i])>1e-7)))); }