@@ -650,32 +650,39 @@ function clearAllViewers() {
650650 clearTimeout ( playTimeoutId ) ;
651651
652652 allStates . forEach ( ( st ) => {
653+ // Guard: skip any state object that doesn’t have a scene
654+ if ( ! st || ! st . scene ) return ; // ← UPDATED: avoid calling traverse on undefined
655+
653656 if ( st . isSampled ) {
654657 // Remove any interpolated meshes (if still present)
655- st . scene . traverse ( ( obj ) => {
656- if ( obj . name && obj . name . startsWith ( `interp_${ st . currentSampleId } _` ) ) {
657- if ( obj . geometry ) obj . geometry . dispose ( ) ;
658- if ( obj . material ) obj . material . dispose ( ) ;
659- st . scene . remove ( obj ) ;
660- }
661- } ) ;
658+ // st.scene.traverse((obj) => {
659+ // if (obj.name && obj.name.startsWith(`interp_${st.currentSampleId}_`)) {
660+ // if (obj.geometry) obj.geometry.dispose();
661+ // if (obj.material) obj.material.dispose();
662+ // st.scene.remove(obj);
663+ // }
664+ // });
662665
663666 // Remove each frame’s InstancedMesh
664- st . frameMeshes . forEach ( ( m ) => {
665- if ( m ) {
666- st . scene . remove ( m ) ;
667- m . geometry . dispose ( ) ;
668- m . material . dispose ( ) ;
669- }
670- } ) ;
667+ if ( Array . isArray ( st . frameMeshes ) ) {
668+ st . frameMeshes . forEach ( ( m ) => {
669+ if ( m ) {
670+ st . scene . remove ( m ) ;
671+ if ( m . geometry ) m . geometry . dispose ( ) ;
672+ if ( m . material ) m . material . dispose ( ) ;
673+ }
674+ } ) ;
675+ }
671676
672677 // Remove any trajectory spheres
673- st . trajectorySpheres . forEach ( ( sphere ) => {
674- if ( sphere . geometry ) sphere . geometry . dispose ( ) ;
675- if ( sphere . material ) sphere . material . dispose ( ) ;
676- st . scene . remove ( sphere ) ;
677- } ) ;
678- st . trajectorySpheres . length = 0 ;
678+ if ( Array . isArray ( st . trajectorySpheres ) ) {
679+ st . trajectorySpheres . forEach ( ( sphere ) => {
680+ if ( sphere . geometry ) sphere . geometry . dispose ( ) ;
681+ if ( sphere . material ) sphere . material . dispose ( ) ;
682+ st . scene . remove ( sphere ) ;
683+ } ) ;
684+ st . trajectorySpheres . length = 0 ;
685+ }
679686 } else {
680687 if ( st . mesh ) {
681688 st . scene . remove ( st . mesh ) ;
@@ -690,7 +697,12 @@ function clearAllViewers() {
690697 }
691698 } ) ;
692699
693- document . querySelector ( '.viewers-container' ) . innerHTML = '' ;
700+ // Also clear out the containers in the DOM
701+ const containerEl = document . querySelector ( '.viewers-container' ) ;
702+ if ( containerEl ) {
703+ containerEl . innerHTML = '' ;
704+ }
705+
694706 inputState = null ;
695707 sampledStates = [ ] ;
696708 allStates = [ ] ;
@@ -1075,6 +1087,29 @@ function selectObject(objName) {
10751087 sampledStates = [ state1 , state2 ] ;
10761088 allStates = [ inputState , state1 , state2 ] ;
10771089
1090+ // ← NEW: Whenever we switch objects, force‐resume playback + rotation + disable trajectories
1091+ isPaused = false ; // ensure we’re playing
1092+ const pauseBtn = document . getElementById ( 'btn-pause' ) ;
1093+ if ( pauseBtn ) {
1094+ pauseBtn . innerText = 'Pause' ;
1095+ }
1096+
1097+ const rotateBtn = document . getElementById ( 'btn-rotate' ) ;
1098+ if ( rotateBtn ) {
1099+ // Restore rotation to “On”
1100+ rotateBtn . innerText = 'Rotate: On' ;
1101+ }
1102+ // Actually enable autoRotate on every viewer’s OrbitControls
1103+ allStates . forEach ( ( st ) => {
1104+ if ( st . controls ) st . controls . autoRotate = true ;
1105+ } ) ;
1106+
1107+ showTrajectories = false ; // disable any lingering trajectories
1108+ const trajBtn = document . getElementById ( 'btn-traj' ) ;
1109+ if ( trajBtn ) {
1110+ trajBtn . innerText = 'Trajectories: Off' ;
1111+ }
1112+
10781113 // Reset frame index & begin animation/resampling loop
10791114 currentFrameIdx = 0 ;
10801115 advanceAllFrames ( currentSession ) ;
@@ -1092,27 +1127,29 @@ window.addEventListener('DOMContentLoaded', () => {
10921127 // “Rotate” button toggles autoRotate:
10931128 const rotateBtn = document . getElementById ( 'btn-rotate' ) ;
10941129 rotateBtn . addEventListener ( 'click' , ( ) => {
1095- const anyAuto = allStates . some ( ( st ) => st . controls . autoRotate ) ;
1130+ const anyAuto = allStates . some ( ( st ) => st . controls ? .autoRotate ) ;
10961131 if ( anyAuto ) {
10971132 allStates . forEach ( ( st ) => {
1098- st . controls . autoRotate = false ;
1133+ if ( st . controls ) st . controls . autoRotate = false ;
10991134 } ) ;
11001135 } else {
11011136 allStates . forEach ( ( st ) => {
1102- st . controls . autoRotate = true ;
1137+ if ( st . controls ) st . controls . autoRotate = true ;
11031138 } ) ;
11041139 }
1140+ // Enable the button again (in case it was disabled during drag)
11051141 rotateBtn . disabled = false ;
1106- const anyAuto2 = allStates . some ( ( st ) => st . controls . autoRotate ) ;
1107- rotateBtn . innerHTML = anyAuto2
1108- ? 'Rotate: On '
1142+
1143+ const anyAuto2 = allStates . some ( ( st ) => st . controls ?. autoRotate ) ;
1144+ rotateBtn . innerText = anyAuto2
1145+ ? 'Rotate: On'
11091146 : 'Rotate: Off' ;
11101147 } ) ;
11111148
11121149 // “Show Trajectories” button (toggle):
11131150 const trajBtn = document . getElementById ( 'btn-traj' ) ;
11141151 trajBtn . innerText = 'Trajectories: Off' ; // initially off
1115- trajBtn . addEventListener ( 'click' , ( ) => { // ← TRAJ
1152+ trajBtn . addEventListener ( 'click' , ( ) => {
11161153 showTrajectories = ! showTrajectories ;
11171154 if ( showTrajectories ) {
11181155 trajBtn . innerText = 'Trajectories: On' ;
@@ -1132,9 +1169,39 @@ window.addEventListener('DOMContentLoaded', () => {
11321169 }
11331170 } ) ;
11341171
1172+ // ← UPDATED: “Play/Pause” button wiring (now also stops/starts autoRotate)
1173+ const pauseBtn = document . getElementById ( 'btn-pause' ) ;
1174+ if ( pauseBtn ) {
1175+ pauseBtn . innerText = 'Pause' ; // default: playing
1176+ pauseBtn . addEventListener ( 'click' , ( ) => {
1177+ isPaused = ! isPaused ;
1178+
1179+ if ( isPaused ) {
1180+ // 1) Switch button label
1181+ pauseBtn . innerText = 'Play' ;
1182+ // 2) Stop the frame‐advance loop
1183+ clearTimeout ( playTimeoutId ) ;
1184+ // 3) Force all viewers to stop rotating
1185+ allStates . forEach ( ( st ) => {
1186+ if ( st . controls ) st . controls . autoRotate = false ;
1187+ } ) ;
1188+ } else {
1189+ // 1) Switch button label
1190+ pauseBtn . innerText = 'Pause' ;
1191+ // 2) Restore autoRotate only if "Rotate" is currently set to On:
1192+ const rotateIsOn = rotateBtn . innerText . includes ( 'On' ) ;
1193+ allStates . forEach ( ( st ) => {
1194+ if ( st . controls ) st . controls . autoRotate = rotateIsOn ;
1195+ } ) ;
1196+ // 3) Re‐start frame‐advance from the current frame
1197+ advanceAllFrames ( currentSession ) ;
1198+ }
1199+ } ) ;
1200+ }
1201+
11351202 // Show the first object by default
11361203 selectObject ( 'partnet_652' ) ;
11371204
11381205 // Start the render loop
11391206 animateAll ( ) ;
1140- } ) ;
1207+ } ) ;
0 commit comments