@@ -24,6 +24,9 @@ import React from "react";
24
24
import ReactDOM from "react-dom/client" ;
25
25
import type { JupyterActions } from "@cocalc/frontend/jupyter/browser-actions" ;
26
26
import { FileContext } from "@cocalc/frontend/lib/file-context" ;
27
+ import { delay } from "awaiting" ;
28
+
29
+ const K3D_DELAY_MS = 25 ;
27
30
28
31
type DeserializedModelState = { [ key : string ] : any } ;
29
32
@@ -39,6 +42,7 @@ export class WidgetManager {
39
42
private last_changed : { [ model_id : string ] : { [ key : string ] : any } } = { } ;
40
43
private state_lock : Set < string > = new Set ( ) ;
41
44
private watching : Set < string > = new Set ( ) ;
45
+ public k3dObjectIds = new Set < number > ( ) ;
42
46
43
47
constructor ( {
44
48
ipywidgets_state,
@@ -147,12 +151,19 @@ VBox([s1, s2])
147
151
changed : SerializedModelState ,
148
152
merge : boolean ,
149
153
) : Promise < void > => {
150
- const model : base . DOMWidgetModel | undefined =
151
- await this . manager . get_model ( model_id ) ;
154
+ const model = await this . manager . get_model ( model_id ) ;
152
155
log ( "updateModel" , { model_id, merge, changed } ) ;
153
- if ( model == null ) {
154
- return ;
156
+ if ( model . module == "k3d" ) {
157
+ // k3d invents its own ad hoc inter-model reference scheme, so we have
158
+ // to deal with that.
159
+ if ( changed . object_ids != null ) {
160
+ while ( ! isSubset ( changed . object_ids , this . k3dObjectIds ) ) {
161
+ log ( "k3d -- waiting for object_ids" , changed . object_ids ) ;
162
+ await delay ( K3D_DELAY_MS ) ;
163
+ }
164
+ }
155
165
}
166
+
156
167
//log(`setting state of model "${model_id}" to `, change);
157
168
if ( changed . last_changed != null ) {
158
169
this . last_changed [ model_id ] = changed ;
@@ -598,6 +609,7 @@ VBox([s1, s2])
598
609
599
610
class Environment implements WidgetEnvironment {
600
611
private manager : WidgetManager ;
612
+
601
613
constructor ( manager ) {
602
614
this . manager = manager ;
603
615
}
@@ -626,12 +638,17 @@ class Environment implements WidgetEnvironment {
626
638
if ( state == null ) {
627
639
throw Error ( "bug" ) ;
628
640
}
641
+
629
642
if ( state . _model_module == "k3d" && state . type != null ) {
630
643
while ( ! state ?. type || ! state ?. id ) {
631
644
log (
632
- "getSerializedModelState" ,
645
+ "getSerializedModelState -- k3d case " ,
633
646
model_id ,
634
- "k3d: waiting for state.type to be defined" ,
647
+ "k3d: waiting for state.type and state.id to be defined" ,
648
+ {
649
+ type : state ?. type ,
650
+ id : state ?. id ,
651
+ } ,
635
652
) ;
636
653
637
654
await once ( this . manager . ipywidgets_state , "change" ) ;
@@ -660,6 +677,19 @@ class Environment implements WidgetEnvironment {
660
677
661
678
log ( "getSerializedModelState" , { model_id, state } ) ;
662
679
setTimeout ( ( ) => this . manager . watchModel ( model_id ) , 1 ) ;
680
+
681
+ if ( state . _model_module == "k3d" ) {
682
+ if ( state . object_ids != null ) {
683
+ while ( ! isSubset ( state . object_ids , this . manager . k3dObjectIds ) ) {
684
+ log ( "k3d -- waiting for object_ids" , state . object_ids ) ;
685
+ await delay ( K3D_DELAY_MS ) ;
686
+ }
687
+ }
688
+ if ( state . id != null ) {
689
+ this . manager . k3dObjectIds . add ( state . id ) ;
690
+ }
691
+ }
692
+
663
693
return {
664
694
modelName : state . _model_name ,
665
695
modelModule : state . _model_module ,
@@ -751,3 +781,12 @@ const IPY_MODEL = "IPY_MODEL_";
751
781
function isModelReference ( value ) : boolean {
752
782
return typeof value == "string" && value . startsWith ( IPY_MODEL ) ;
753
783
}
784
+
785
+ function isSubset ( X , Y ) {
786
+ for ( const a of X ) {
787
+ if ( ! Y . has ( a ) ) {
788
+ return false ;
789
+ }
790
+ }
791
+ return true ;
792
+ }
0 commit comments