Coverage for /usr/local/lib/python3.11/dist-packages/pyrocko/gui/sparrow/state.py: 73%
188 statements
« prev ^ index » next coverage.py v6.5.0, created at 2024-09-24 10:38 +0000
« prev ^ index » next coverage.py v6.5.0, created at 2024-09-24 10:38 +0000
1# https://pyrocko.org - GPLv3
2#
3# The Pyrocko Developers, 21st Century
4# ---|P------/S----------~Lg----------
6import logging
8import numpy as num
10from pyrocko import util
11from pyrocko.guts import StringChoice, Float, List, Bool, Timestamp, Tuple, \
12 Duration, Object, get_elements, set_elements, path_to_str, clone
14from pyrocko.color import Color, interpolate as interpolate_color
16from pyrocko.gui import talkie
17from ..state import state_bind, state_bind_slider, state_bind_slider_float, \
18 state_bind_spinbox, state_bind_combobox, state_bind_combobox_color, \
19 state_bind_checkbox, state_bind_lineedit # noqa
21from . import common, light
23guts_prefix = 'sparrow'
25logger = logging.getLogger('pyrocko.gui.sparrow.state')
28class FocalPointChoice(StringChoice):
29 choices = ['center', 'target']
32class ShadingChoice(StringChoice):
33 choices = ['flat', 'gouraud', 'phong', 'pbr']
36class LightingChoice(StringChoice):
37 choices = light.get_lighting_theme_names()
40class ViewerGuiState(talkie.TalkieRoot):
41 panels_visible = Bool.T(default=True)
42 size = Tuple.T(2, Float.T(), default=(100., 100.))
43 fixed_size = Tuple.T(2, Float.T(), optional=True)
44 focal_point = FocalPointChoice.T(default='center')
45 detached = Bool.T(default=False)
46 tcursor = Timestamp.T(optional=True)
48 def next_focal_point(self):
49 choices = FocalPointChoice.choices
50 ii = choices.index(self.focal_point)
51 self.focal_point = choices[(ii+1) % len(choices)]
54class Background(Object):
55 color = Color.T(default=Color.D('black'))
57 def vtk_apply(self, ren):
58 ren.GradientBackgroundOff()
59 ren.SetBackground(*self.color.rgb)
61 def __str__(self):
62 return str(self.color)
64 @property
65 def color_top(self):
66 return self.color
68 @property
69 def color_bottom(self):
70 return self.color
72 # def __eq__(self, other):
73 # print('in==', self.color.rgb, other.color.rgb)
74 # return type(self) is type(other) and self.color == other.color
77class BackgroundGradient(Background):
78 color_top = Color.T(default=Color.D('skyblue1'))
79 color_bottom = Color.T(default=Color.D('white'))
81 def vtk_apply(self, ren):
82 ren.GradientBackgroundOn()
83 ren.SetBackground(*self.color_bottom.rgb)
84 ren.SetBackground2(*self.color_top.rgb)
86 def __str__(self):
87 return '%s - %s' % (self.color_top, self.color_bottom)
89 # def __eq__(self, other):
90 # return type(self) is type(other) and \
91 # self.color_top == other.color_top and \
92 # self.color_bottom == other.color_bottom
95def interpolate_background(a, b, blend):
96 if type(a) is Background and type(b) is Background:
97 return Background(color=interpolate_color(a.color, b.color, blend))
98 else:
99 return BackgroundGradient(
100 color_top=interpolate_color(
101 a.color_top, b.color_top, blend),
102 color_bottom=interpolate_color(
103 a.color_bottom, b.color_bottom, blend))
106@talkie.has_computed
107class ViewerState(talkie.TalkieRoot):
108 lat = Float.T(default=0.0)
109 lon = Float.T(default=0.0)
110 depth = Float.T(default=0.0)
111 strike = Float.T(default=90.0)
112 dip = Float.T(default=0.0)
113 distance = Float.T(default=3.0)
114 elements = List.T(talkie.Talkie.T())
115 tmin = Timestamp.T(optional=True)
116 tmax = Timestamp.T(optional=True)
117 tduration = Duration.T(optional=True)
118 tposition = Float.T(default=0.0)
119 lighting = LightingChoice.T(default=LightingChoice.choices[0])
120 background = Background.T(default=Background.D(color=Color('black')))
122 @talkie.computed(['tmin', 'tmax', 'tduration', 'tposition'])
123 def tmin_effective(self):
124 return common.tmin_effective(
125 self.tmin, self.tmax, self.tduration, self.tposition)
127 @talkie.computed(['tmin', 'tmax', 'tduration', 'tposition'])
128 def tmax_effective(self):
129 return common.tmax_effective(
130 self.tmin, self.tmax, self.tduration, self.tposition)
132 def sort_elements(self):
133 self.elements.sort(key=lambda el: el.element_id)
136def state_bind_combobox_background(owner, state, path, widget):
138 def make_funcs():
139 def update_state(widget, state):
140 values = str(widget.currentText()).split(' - ')
141 if len(values) == 1:
142 state.set(
143 path,
144 Background(color=Color(values[0])))
146 elif len(values) == 2:
147 state.set(
148 path,
149 BackgroundGradient(
150 color_top=Color(values[0]),
151 color_bottom=Color(values[1])))
153 def update_widget(state, widget):
154 widget.blockSignals(True)
155 val = str(state.get(path))
156 for i in range(widget.count()):
157 if str(widget.itemText(i)) == val:
158 widget.setCurrentIndex(i)
159 widget.blockSignals(False)
161 return update_state, update_widget
163 update_state, update_widget = make_funcs()
165 state_bind(
166 owner, state, [path], update_state, widget, [widget.activated],
167 update_widget)
170def interpolateables(state_a, state_b):
172 animate = []
173 for tag, path, values in state_b.diff(state_a):
174 if tag == 'set':
175 ypath = path_to_str(path)
176 v_new = get_elements(state_b, ypath)[0]
177 v_old = values
178 for type in [float, Color, Background]:
179 if isinstance(v_old, type) and isinstance(v_new, type):
180 animate.append((ypath, v_old, v_new))
182 return animate
185def interpolate(times, states, times_inter):
187 assert len(times) == len(states)
189 states_inter = []
190 for i in range(len(times) - 1):
192 state_a = states[i]
193 state_b = states[i+1]
194 time_a = times[i]
195 time_b = times[i+1]
197 animate = interpolateables(state_a, state_b)
199 if i == 0:
200 times_inter_this = times_inter[num.logical_and(
201 time_a <= times_inter, times_inter <= time_b)]
202 else:
203 times_inter_this = times_inter[num.logical_and(
204 time_a < times_inter, times_inter <= time_b)]
206 for time_inter in times_inter_this:
207 state = clone(state_b)
208 if time_b == time_a:
209 blend = 0.
210 else:
211 blend = (time_inter - time_a) / (time_b - time_a)
213 for ypath, v_old, v_new in animate:
214 if isinstance(v_old, float) and isinstance(v_new, float):
215 if ypath == 'strike':
216 if v_new - v_old > 180.:
217 v_new -= 360.
218 elif v_new - v_old < -180.:
219 v_new += 360.
221 if ypath != 'distance':
222 v_inter = v_old + blend * (v_new - v_old)
223 else:
224 v_old = num.log(v_old)
225 v_new = num.log(v_new)
226 v_inter = v_old + blend * (v_new - v_old)
227 v_inter = num.exp(v_inter)
229 set_elements(state, ypath, v_inter)
230 else:
231 set_elements(state, ypath, v_new)
233 states_inter.append(state)
235 return states_inter
238class Interpolator(object):
240 def __init__(self, times, states, fps=25.):
242 assert len(times) == len(states)
244 self.dt = 1.0 / fps
245 self.tmin = times[0]
246 self.tmax = times[-1]
247 times_inter = util.arange2(
248 self.tmin, self.tmax, self.dt, error='floor')
249 times_inter[-1] = times[-1]
251 if times_inter.size == 1:
252 self._states_inter = [clone(states[-1])]
253 return
255 states_inter = []
256 for i in range(len(times) - 1):
258 state_a = states[i]
259 state_b = states[i+1]
260 time_a = times[i]
261 time_b = times[i+1]
263 animate = interpolateables(state_a, state_b)
265 if i == 0:
266 times_inter_this = times_inter[num.logical_and(
267 time_a <= times_inter, times_inter <= time_b)]
268 else:
269 times_inter_this = times_inter[num.logical_and(
270 time_a < times_inter, times_inter <= time_b)]
272 for time_inter in times_inter_this:
273 state = clone(state_b)
275 if time_b == time_a:
276 blend = 0.
277 else:
278 blend = (time_inter - time_a) / (time_b - time_a)
280 for ypath, v_old, v_new in animate:
281 if isinstance(v_old, float) and isinstance(v_new, float):
282 if ypath in ('lon', 'strike'):
283 if v_new - v_old > 180.:
284 v_new -= 360.
285 elif v_new - v_old < -180.:
286 v_new += 360.
288 if ypath != 'distance':
289 v_inter = v_old + blend * (v_new - v_old)
290 else:
291 v_old = num.log(v_old)
292 v_new = num.log(v_new)
293 v_inter = v_old + blend * (v_new - v_old)
294 v_inter = num.exp(v_inter)
296 set_elements(state, ypath, v_inter)
298 elif isinstance(v_old, Color) and isinstance(v_new, Color):
299 v_inter = interpolate_color(v_old, v_new, blend)
300 set_elements(state, ypath, v_inter)
302 elif isinstance(v_old, Background) \
303 and isinstance(v_new, Background):
304 v_inter = interpolate_background(v_old, v_new, blend)
305 set_elements(state, ypath, v_inter)
307 else:
308 set_elements(state, ypath, v_new)
310 states_inter.append(state)
312 self._states_inter = states_inter
314 def __call__(self, t):
315 itime = int(round((t - self.tmin) / self.dt))
316 itime = min(max(0, itime), len(self._states_inter)-1)
317 return self._states_inter[itime]