|
2 | 2 | import math |
3 | 3 | import time |
4 | 4 |
|
5 | | -# Rendering Factory |
6 | | -import vtkmodules.vtkRenderingOpenGL2 # noqa: F401 |
7 | 5 | from paraview.modules.vtkPVVTKExtensionsInteractionStyle import ( |
8 | 6 | vtkPVInteractorStyle, |
9 | 7 | vtkPVTrackballZoom, |
10 | 8 | vtkTrackballPan, |
11 | 9 | ) |
12 | | -from trame.app import TrameComponent, dataclass |
13 | | -from trame.dataclasses.colormaps import ColormapConfig |
| 10 | +from trame.app import TrameComponent |
14 | 11 | from trame.decorators import controller |
15 | 12 | from trame.ui.html import DivLayout |
16 | | -from trame.widgets import client, colormaps, html, rca |
| 13 | +from trame.widgets import client, colormaps, rca |
17 | 14 | from trame.widgets import vuetify3 as v3 |
18 | 15 | from vtkmodules.vtkRenderingCore import ( |
19 | | - vtkActor, |
20 | 16 | vtkCamera, |
21 | | - vtkPolyDataMapper, |
22 | | - vtkRenderer, |
23 | 17 | vtkRenderWindow, |
24 | 18 | vtkRenderWindowInteractor, |
25 | 19 | ) |
26 | 20 |
|
27 | | -from e3sm_quickview.components import view as tview |
28 | 21 | from e3sm_quickview.utils import perf |
| 22 | +from e3sm_quickview.view_panel import VariableView |
29 | 23 |
|
30 | 24 |
|
31 | 25 | def auto_size_to_col(size): |
@@ -56,185 +50,6 @@ def auto_size_to_col(size): |
56 | 50 | } |
57 | 51 |
|
58 | 52 |
|
59 | | -class ViewConfiguration(dataclass.StateDataModel): |
60 | | - # --- View identity --- |
61 | | - variable: str = dataclass.Sync(str) |
62 | | - |
63 | | - # --- Layout --- |
64 | | - order: int = dataclass.Sync(int, 0) |
65 | | - size: int = dataclass.Sync(int, 6) |
66 | | - offset: int = dataclass.Sync(int, 0) |
67 | | - break_row: bool = dataclass.Sync(bool, False) |
68 | | - swap_group: list[str] = dataclass.Sync(list[str], list) |
69 | | - |
70 | | - |
71 | | -class VariableView(TrameComponent): |
72 | | - def __init__(self, server, source, variable_name, variable_type, camera): |
73 | | - super().__init__(server) |
74 | | - self.source = source |
75 | | - self.variable_name = variable_name |
76 | | - self.variable_type = variable_type |
77 | | - self.disable_render = False |
78 | | - self.name = f"view_{self.variable_name}" |
79 | | - self._bounds_key = f"{self.name}_bounds" |
80 | | - self.config = ViewConfiguration(server, variable=variable_name) |
81 | | - self._size = (0, 0) |
82 | | - |
83 | | - # VTK |
84 | | - self.renderer = vtkRenderer( |
85 | | - active_camera=camera, |
86 | | - background=(84 / 255, 89 / 255, 109 / 255), |
87 | | - background2=(0, 0, 42 / 255), |
88 | | - gradient_background=1, |
89 | | - ) |
90 | | - self._camera = camera |
91 | | - |
92 | | - input = source.data_reader.vtk_geometry |
93 | | - self.mapper = vtkPolyDataMapper(input_connection=input.output_port) |
94 | | - self.actor = vtkActor(mapper=self.mapper) |
95 | | - self.renderer.AddActor(self.actor) |
96 | | - |
97 | | - # Add annotation to the view (continents, gridlines) |
98 | | - self.renderer.AddActor(source.continent.actor) |
99 | | - self.renderer.AddActor(source.grid_lines.actor) |
100 | | - |
101 | | - # colormaps module: creates LUT, wires mapper, manages presets/range/ticks |
102 | | - self.colormap = ColormapConfig( |
103 | | - server, |
104 | | - mapper=self.mapper, |
105 | | - data_array_fn=lambda: self.data_array, |
106 | | - ).set_data_array(variable_name, lambda: self.data_array, "cell") |
107 | | - self.colormap.watch(["mapper_change"], lambda *_: self.render()) |
108 | | - |
109 | | - # GUI |
110 | | - self._build_ui() |
111 | | - |
112 | | - @property |
113 | | - def bounds(self): |
114 | | - return self.state[self._bounds_key] |
115 | | - |
116 | | - @bounds.setter |
117 | | - def bounds(self, v): |
118 | | - self.renderer.SetViewport(*v) |
119 | | - with self.state as s: |
120 | | - s[self._bounds_key] = v |
121 | | - |
122 | | - def reset_camera(self): |
123 | | - self.renderer.ResetCameraScreenSpace(0.9) |
124 | | - |
125 | | - def update_size(self, size): |
126 | | - new_size = (int(size["w"] * size["p"]), int(size["h"] * size["p"])) |
127 | | - if self._size != new_size: |
128 | | - self._size = new_size |
129 | | - self.ctrl.size_update() |
130 | | - |
131 | | - @property |
132 | | - def size(self): |
133 | | - return self._size |
134 | | - |
135 | | - def render(self): |
136 | | - if self.ctx.view: |
137 | | - self.ctx.view.update() |
138 | | - |
139 | | - @property |
140 | | - def data_array(self): |
141 | | - self.source.data_reader.vtk_geometry.Update() |
142 | | - ds = self.source.data_reader.vtk_geometry.GetOutput() |
143 | | - return ds.GetCellData().GetArray(self.variable_name) |
144 | | - |
145 | | - def _build_ui(self): |
146 | | - with DivLayout( |
147 | | - self.server, template_name=self.name, connect_parent=False, classes="h-100" |
148 | | - ) as self.ui: |
149 | | - self.ui.root.classes = "h-100" |
150 | | - with v3.VCard( |
151 | | - variant="tonal", |
152 | | - style=( |
153 | | - "active_layout !== 'auto_layout' ? `height: calc(100% - ${toolbar_size?.size?.height || 0}px)` : 'overflow-hidden'", |
154 | | - ), |
155 | | - tile=("active_layout !== 'auto_layout'",), |
156 | | - raw_attrs=[f'data-field-name="{self.variable_name}"'], |
157 | | - ): |
158 | | - with v3.VRow( |
159 | | - dense=True, |
160 | | - classes="ma-0 pa-0 bg-black opacity-90 d-flex align-center flex-nowrap", |
161 | | - ): |
162 | | - tview.create_size_menu(self.name, self.config) |
163 | | - with html.Div( |
164 | | - self.variable_name, |
165 | | - classes="text-subtitle-2 pr-2 text-truncate", |
166 | | - style="user-select: none;", |
167 | | - title=self.variable_name, |
168 | | - ): |
169 | | - with v3.VMenu(activator="parent"): |
170 | | - with v3.VList(density="compact", style="max-height: 40vh;"): |
171 | | - with self.config.provide_as("config"): |
172 | | - v3.VListItem( |
173 | | - subtitle=("name",), |
174 | | - v_for="name, idx in config.swap_group", |
175 | | - key="name", |
176 | | - click=( |
177 | | - self.ctrl.swap_variables, |
178 | | - "[config.variable, name]", |
179 | | - ), |
180 | | - ) |
181 | | - |
182 | | - v3.VIconBtn( |
183 | | - v_tooltip_bottom="'Capture as png'", |
184 | | - icon="mdi-camera-outline", |
185 | | - size="small", |
186 | | - variant="plain", |
187 | | - click=f"utils.quickview.capturePanel('{self.variable_name}')", |
188 | | - style="transform: scale(0.75);", |
189 | | - ) |
190 | | - |
191 | | - v3.VSpacer() |
192 | | - html.Div( |
193 | | - "t = {{ time_idx }}", |
194 | | - classes="text-caption px-1 text-no-wrap", |
195 | | - v_if="timestamps.length > 1", |
196 | | - ) |
197 | | - if self.variable_type == "m": |
198 | | - html.Div( |
199 | | - "[k = {{ midpoint_idx }}]", |
200 | | - classes="text-caption px-1 text-no-wrap", |
201 | | - v_if="midpoints.length > 1", |
202 | | - ) |
203 | | - if self.variable_type == "i": |
204 | | - html.Div( |
205 | | - "[k = {{ interface_idx }}]", |
206 | | - classes="text-caption px-1 text-no-wrap", |
207 | | - v_if="interfaces.length > 1", |
208 | | - ) |
209 | | - v3.VSpacer() |
210 | | - html.Div( |
211 | | - "avg = {{" |
212 | | - f"fields_avgs['{self.variable_name}']?.toExponential(2) || 'N/A'" |
213 | | - "}}", |
214 | | - classes="text-caption px-1 text-no-wrap", |
215 | | - ) |
216 | | - |
217 | | - with html.Div( |
218 | | - style=( |
219 | | - """ |
220 | | - { |
221 | | - aspectRatio: active_layout === 'auto_layout' ? (1.0 / aspect_ratio) : null, |
222 | | - height: active_layout !== 'auto_layout' ? 'calc(100% - 2.4rem)' : null, |
223 | | - pointerEvents: 'none', |
224 | | - } |
225 | | - """, |
226 | | - ), |
227 | | - ): |
228 | | - rca.ImageRegion( |
229 | | - enable_interaction=False, |
230 | | - bounds=(self._bounds_key, (0, 0, 1, 1)), |
231 | | - size=(self.update_size, "[$event]"), |
232 | | - ) |
233 | | - |
234 | | - with self.colormap.provide_as(self.name): |
235 | | - colormaps.HorizontalScalarBar(self.name, popup_location="top") |
236 | | - |
237 | | - |
238 | 53 | class ViewManager(TrameComponent): |
239 | 54 | def __init__(self, server, source): |
240 | 55 | super().__init__(server) |
|
0 commit comments