44from trame .app import TrameComponent
55from trame .decorators import controller
66from trame .ui .html import DivLayout
7- from trame .widgets import client , html
8- from trame .widgets import paraview as pvw
7+ from trame .widgets import client , html , rca
98from trame .widgets import vuetify3 as v3
109from trame_dataclass .core import StateDataModel
10+ from vtkmodules .vtkRenderingCore import vtkActor , vtkPolyDataMapper
1111
1212from e3sm_quickview .components import view as tview
1313from e3sm_quickview .presets import COLOR_BLIND_SAFE
@@ -72,38 +72,45 @@ class ViewConfiguration(StateDataModel):
7272class VariableView (TrameComponent ):
7373 def __init__ (self , server , source , variable_name , variable_type ):
7474 super ().__init__ (server )
75- self .config = ViewConfiguration (server , variable = variable_name )
7675 self .source = source
7776 self .variable_name = variable_name
7877 self .variable_type = variable_type
78+ self .disable_render = False
7979 self .name = f"view_{ self .variable_name } "
80+ self .config = ViewConfiguration (server , variable = variable_name )
81+
82+ # ParaView
8083 self .view = simple .CreateRenderView ()
81- self .view .GetRenderWindow ().SetOffScreenRendering (True )
8284 self .view .InteractionMode = "2D"
8385 self .view .OrientationAxesVisibility = 0
8486 self .view .UseColorPaletteForBackground = 0
8587 self .view .BackgroundColorMode = "Gradient"
8688 self .view .CameraParallelProjection = 1
87- self .view .Size = 0 # make the interactive widget non responsive
88- self .representation = simple .Show (
89- proxy = source .views ["atmosphere_data" ],
90- view = self .view ,
91- )
89+
90+ # VTK
91+ self ._camera = self .view .GetActiveCamera ()
92+ self .renderer = self .view .GetRenderer ()
93+ self .render_window = self .view .GetRenderWindow ()
94+ self .render_window .SetOffScreenRendering (True )
95+
96+ input = source .data_reader .vtk_geometry
97+ self .mapper = vtkPolyDataMapper (input_connection = input .output_port )
98+ self .actor = vtkActor (mapper = self .mapper )
99+ self .renderer .AddActor (self .actor )
92100
93101 # Lookup table color management
94- simple .ColorBy (self .representation , ("CELLS" , variable_name ))
95102 self .lut = simple .GetColorTransferFunction (variable_name )
96103 self .lut .NanOpacity = 0.0
97104
98- self .view .ResetActiveCameraToNegativeZ ()
99- self .view .ResetCamera (True , 0.9 )
100- self .disable_render = False
105+ # Color mapping
106+ self .mapper .SetScalarVisibility (1 )
107+ self .mapper .SetScalarModeToUseCellFieldData ()
108+ self .mapper .SelectColorArray (variable_name )
109+ self .mapper .SetLookupTable (self .lut .GetClientSideObject ())
101110
102- # Add annotation to the view
103- # - continents
104- self .view .GetRenderer ().AddActor (source .continent .actor )
105- # - gridlines
106- self .view .GetRenderer ().AddActor (source .grid_lines .actor )
111+ # Add annotation to the view (continents, gridlines)
112+ self .renderer .AddActor (source .continent .actor )
113+ self .renderer .AddActor (source .grid_lines .actor )
107114
108115 # Reactive behavior
109116 self .config .watch (
@@ -119,9 +126,15 @@ def __init__(self, server, source, variable_name, variable_type):
119126 eager = True ,
120127 )
121128
129+ # Initial flush
130+ simple .Render (self .view )
131+
122132 # GUI
123133 self ._build_ui ()
124134
135+ # Need to be after RCA initialization (gui build)
136+ self .reset_camera ()
137+
125138 def render (self ):
126139 if self .disable_render or not self .ctx .has (self .name ):
127140 return
@@ -132,13 +145,11 @@ def set_camera_modified(self, fn):
132145
133146 @property
134147 def camera (self ):
135- return self .view . GetActiveCamera ()
148+ return self ._camera
136149
137150 def reset_camera (self ):
138- self .view .InteractionMode = "2D"
139- self .view .ResetActiveCameraToNegativeZ ()
140151 self .view .ResetCamera (True , 0.9 )
141- self .ctx [ self . name ]. update ()
152+ self .render ()
142153
143154 def update_color_preset (self , name , invert , log_scale , n_colors = 255 ):
144155 self .config .preset = name
@@ -174,6 +185,12 @@ def color_range_str_to_float(self, color_value_min, color_value_max):
174185 if self .config .color_value_min_valid and self .config .color_value_max_valid :
175186 self .config .color_range = [min_value , max_value ]
176187
188+ @property
189+ def data_array (self ):
190+ self .source .data_reader .vtk_geometry .Update ()
191+ ds = self .source .data_reader .vtk_geometry .GetOutput ()
192+ return ds .GetCellData ().GetArray (self .variable_name )
193+
177194 def update_color_range (self , * _ ):
178195 if self .config .override_range :
179196 skip_update = False
@@ -190,12 +207,7 @@ def update_color_range(self, *_):
190207
191208 self .lut .RescaleTransferFunction (* self .config .color_range )
192209 else :
193- self .representation .RescaleTransferFunctionToDataRange (False , True )
194- data_array = (
195- self .source .views ["atmosphere_data" ]
196- .GetCellDataInformation ()
197- .GetArray (self .variable_name )
198- )
210+ data_array = self .data_array
199211 if data_array :
200212 data_range = data_array .GetRange ()
201213 self .config .color_range = data_range
@@ -286,9 +298,16 @@ def _build_ui(self):
286298 """ ,
287299 ),
288300 ):
289- pvw .VtkRemoteView (
290- self .view , interactive_ratio = 1 , ctx_name = self .name
301+ display = rca .RemoteControlledArea (display = "image" )
302+ handler = display .create_view_handler (
303+ self .view .GetRenderWindow (),
304+ encoder = "turbo-jpeg" ,
291305 )
306+ self .ctx [self .name ] = handler
307+
308+ # pvw.VtkRemoteView(
309+ # self.view, interactive_ratio=1, ctx_name=self.name
310+ # )
292311
293312 tview .create_bottom_bar (self .config , self .update_color_preset )
294313
@@ -302,7 +321,7 @@ def __init__(self, server, source):
302321 self ._last_vars = {}
303322 self ._active_configs = {}
304323
305- pvw .initialize (self .server )
324+ rca .initialize (self .server )
306325
307326 self .state .luts_normal = [
308327 {"name" : k , "url" : v ["normal" ], "safe" : k in COLOR_BLIND_SAFE }
@@ -343,10 +362,8 @@ def update_color_range(self):
343362 def get_view (self , variable_name , variable_type ):
344363 view = self ._var2view .get (variable_name )
345364 if view is None :
346- view = self ._var2view .setdefault (
347- variable_name ,
348- VariableView (self .server , self .source , variable_name , variable_type ),
349- )
365+ view = VariableView (self .server , self .source , variable_name , variable_type )
366+ self ._var2view [variable_name ] = view
350367 view .set_camera_modified (self .sync_camera )
351368
352369 return view
0 commit comments