@@ -6,6 +6,7 @@ import { solveCCD } from "../src/ik.js";
66import { poseFor } from "../src/poses.js" ;
77import { buildProps } from "../src/props.js" ;
88import { applyGroundLock , groundFigure } from "../src/groundlock.js" ;
9+ import { levelPlantedFeet } from "../src/contacts.js" ;
910import { parse , eulerRomFor } from "posecode-parser" ;
1011
1112const DEG = Math . PI / 180 ;
@@ -751,3 +752,38 @@ describe("spline interpolation (L2)", () => {
751752 expect ( v ) . toBeLessThan ( 0.2 ) ;
752753 } ) ;
753754} ) ;
755+
756+ describe ( "foot-flat correction (L3.1)" , ( ) => {
757+ it ( "rests a squatting foot flatter on the floor (not on the toes)" , ( ) => {
758+ const src = [
759+ 'posecode exercise "sq"' ,
760+ " rig humanoid" ,
761+ " pose start = standing" ,
762+ ' step "Descend" 1s settle:' ,
763+ " hips: flex 80" ,
764+ " knees: flex 95" ,
765+ " pelvis: hinge 25" ,
766+ " ground-lock: feet" ,
767+ ] . join ( "\n" ) ;
768+ const { ir } = parse ( src ) ;
769+ const tl = buildTimeline ( ir ! ) ;
770+ const m = buildMannequin ( ) ;
771+ tl . sample ( 1 , m . bones ) ;
772+ m . root . updateMatrixWorld ( true ) ;
773+ groundFigure ( m ) ;
774+ applyGroundLock ( m , [ "feet" ] ) ;
775+ const soleDot = ( ) => {
776+ const ankle = m . bones . get ( "ankle_left" ) ! ;
777+ return new THREE . Vector3 ( 0 , - 1 , 0 )
778+ . applyQuaternion ( ankle . getWorldQuaternion ( new THREE . Quaternion ( ) ) )
779+ . normalize ( )
780+ . dot ( new THREE . Vector3 ( 0 , - 1 , 0 ) ) ;
781+ } ;
782+ const before = soleDot ( ) ;
783+ levelPlantedFeet ( m , [ "feet" ] ) ;
784+ m . root . updateMatrixWorld ( true ) ;
785+ // Leveling makes the sole markedly flatter than raw ground-lock leaves it.
786+ expect ( soleDot ( ) ) . toBeGreaterThan ( before ) ;
787+ expect ( soleDot ( ) ) . toBeGreaterThan ( 0.9 ) ;
788+ } ) ;
789+ } ) ;
0 commit comments