@@ -83,6 +83,7 @@ def __init__(self, server: viser.ViserServer, viewer):
83
83
self ._viewer = viewer
84
84
self ._keyframes : Dict [int , Tuple [Keyframe , viser .CameraFrustumHandle ]] = {}
85
85
self ._keyframe_counter : int = 0
86
+ self ._max_t = 0
86
87
self ._spline : Optional [viser .SceneNodeHandle ] = None
87
88
self ._camera_edit_panel : Optional [viser .Gui3dContainerHandle ] = None
88
89
@@ -97,6 +98,7 @@ def __init__(self, server: viser.ViserServer, viewer):
97
98
# These parameters should be overridden externally.
98
99
self .loop : bool = False
99
100
self .smoothness : float = 0.5 # Tension / alpha term.
101
+ self .constant_speed : bool = True
100
102
self .default_fov : float = 0.0
101
103
self .framerate : float = 0
102
104
self .duration : float = 0
@@ -276,14 +278,15 @@ def interpolate_pose_and_fov(self, normalized_t: float) -> Optional[Tuple[tf.SE3
276
278
keyframe [0 ].override_fov_value if keyframe [0 ].override_fov_enabled else self .default_fov
277
279
for keyframe in self ._keyframes .values ()
278
280
],
281
+ self ._position_spline .grid ,
279
282
tcb = (self .smoothness , 0.0 , 0.0 ),
280
283
endconditions = "closed" if self .loop else "natural" ,
281
284
)
282
285
283
286
assert self ._orientation_spline is not None
284
287
assert self ._position_spline is not None
285
288
assert self ._fov_spline is not None
286
- max_t = len ( self ._keyframes ) if self . loop else len ( self . _keyframes ) - 1
289
+ max_t = self ._max_t
287
290
t = max_t * normalized_t
288
291
quat = self ._orientation_spline .evaluate (t )
289
292
assert isinstance (quat , splines .quaternion .UnitQuaternion )
@@ -317,6 +320,15 @@ def interpolate_pose_and_fov(self, normalized_t: float) -> Optional[Tuple[tf.SE3
317
320
model_poses ,
318
321
)
319
322
323
+ def get_keyframe_times (self ):
324
+ keyframe_positions = [keyframe [0 ].position for keyframe in self ._keyframes .values ()]
325
+ if self .loop is True :
326
+ keyframe_positions += keyframe_positions [:1 ]
327
+ keyframe_distances = onp .linalg .norm (onp .diff (keyframe_positions , axis = 0 ), axis = 1 )
328
+ keyframe_times = onp .concatenate ([[0 ], onp .cumsum (keyframe_distances )])
329
+
330
+ return keyframe_times
331
+
320
332
def update_spline (self ) -> None :
321
333
keyframes = list (self ._keyframes .values ())
322
334
if len (keyframes ) <= 1 :
@@ -329,17 +341,26 @@ def update_spline(self) -> None:
329
341
if num_frames <= 0 :
330
342
return
331
343
344
+ keyframe_times = None
345
+ max_t = len (keyframes ) if self .loop else len (keyframes ) - 1
346
+ if self .constant_speed is True :
347
+ keyframe_times = self .get_keyframe_times ()
348
+ max_t = keyframe_times [- 1 ]
349
+ self ._max_t = max_t
350
+
332
351
# Update internal splines.
333
352
self ._orientation_spline = splines .quaternion .KochanekBartels (
334
353
[
335
354
splines .quaternion .UnitQuaternion .from_unit_xyzw (onp .roll (keyframe [0 ].wxyz , shift = - 1 ))
336
355
for keyframe in keyframes
337
356
],
357
+ keyframe_times ,
338
358
tcb = (self .smoothness , 0.0 , 0.0 ),
339
359
endconditions = "closed" if self .loop else "natural" ,
340
360
)
341
361
self ._position_spline = splines .KochanekBartels (
342
362
[keyframe [0 ].position for keyframe in keyframes ],
363
+ keyframe_times ,
343
364
tcb = (self .smoothness , 0.0 , 0.0 ),
344
365
endconditions = "closed" if self .loop else "natural" ,
345
366
)
@@ -382,9 +403,8 @@ def update_spline(self) -> None:
382
403
))
383
404
384
405
# Update visualized spline.
385
- num_keyframes = len (keyframes ) + 1 if self .loop else len (keyframes )
386
406
points_array = onp .array (
387
- [self ._position_spline .evaluate (t ) for t in onp .linspace (0 , num_keyframes - 1 , num_frames )]
407
+ [self ._position_spline .evaluate (t ) for t in onp .linspace (0 , self . _max_t , num_frames )]
388
408
)
389
409
colors_array = onp .array ([colorsys .hls_to_rgb (h , 0.5 , 1.0 ) for h in onp .linspace (0.0 , 1.0 , len (points_array ))])
390
410
self ._spline = self ._server .add_point_cloud (
@@ -529,7 +549,7 @@ def _(_) -> None:
529
549
530
550
smoothness = server .gui .add_slider (
531
551
"Spline Tension" ,
532
- min = 0 .0 ,
552
+ min = - 1 .0 ,
533
553
max = 1.0 ,
534
554
initial_value = 0.0 ,
535
555
step = 0.01 ,
@@ -541,6 +561,17 @@ def _(_) -> None:
541
561
camera_path .smoothness = smoothness .value
542
562
camera_path .update_spline ()
543
563
564
+ constant_speed_checkbox = server .gui .add_checkbox (
565
+ "Constant Speed" ,
566
+ initial_value = True ,
567
+ hint = "Maintain a constant speed for camera movement" ,
568
+ )
569
+
570
+ @constant_speed_checkbox .on_update
571
+ def _ (_ ) -> None :
572
+ camera_path .constant_speed = constant_speed_checkbox .value
573
+ camera_path .update_spline ()
574
+
544
575
move_checkbox = server .gui .add_checkbox (
545
576
"Move keyframes" ,
546
577
initial_value = False ,
0 commit comments