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

1# https://pyrocko.org - GPLv3 

2# 

3# The Pyrocko Developers, 21st Century 

4# ---|P------/S----------~Lg---------- 

5 

6import logging 

7 

8import numpy as num 

9 

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 

13 

14from pyrocko.color import Color, interpolate as interpolate_color 

15 

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 

20 

21from . import common, light 

22 

23guts_prefix = 'sparrow' 

24 

25logger = logging.getLogger('pyrocko.gui.sparrow.state') 

26 

27 

28class FocalPointChoice(StringChoice): 

29 choices = ['center', 'target'] 

30 

31 

32class ShadingChoice(StringChoice): 

33 choices = ['flat', 'gouraud', 'phong', 'pbr'] 

34 

35 

36class LightingChoice(StringChoice): 

37 choices = light.get_lighting_theme_names() 

38 

39 

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) 

47 

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)] 

52 

53 

54class Background(Object): 

55 color = Color.T(default=Color.D('black')) 

56 

57 def vtk_apply(self, ren): 

58 ren.GradientBackgroundOff() 

59 ren.SetBackground(*self.color.rgb) 

60 

61 def __str__(self): 

62 return str(self.color) 

63 

64 @property 

65 def color_top(self): 

66 return self.color 

67 

68 @property 

69 def color_bottom(self): 

70 return self.color 

71 

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 

75 

76 

77class BackgroundGradient(Background): 

78 color_top = Color.T(default=Color.D('skyblue1')) 

79 color_bottom = Color.T(default=Color.D('white')) 

80 

81 def vtk_apply(self, ren): 

82 ren.GradientBackgroundOn() 

83 ren.SetBackground(*self.color_bottom.rgb) 

84 ren.SetBackground2(*self.color_top.rgb) 

85 

86 def __str__(self): 

87 return '%s - %s' % (self.color_top, self.color_bottom) 

88 

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 

93 

94 

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)) 

104 

105 

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'))) 

121 

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) 

126 

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) 

131 

132 def sort_elements(self): 

133 self.elements.sort(key=lambda el: el.element_id) 

134 

135 

136def state_bind_combobox_background(owner, state, path, widget): 

137 

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]))) 

145 

146 elif len(values) == 2: 

147 state.set( 

148 path, 

149 BackgroundGradient( 

150 color_top=Color(values[0]), 

151 color_bottom=Color(values[1]))) 

152 

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) 

160 

161 return update_state, update_widget 

162 

163 update_state, update_widget = make_funcs() 

164 

165 state_bind( 

166 owner, state, [path], update_state, widget, [widget.activated], 

167 update_widget) 

168 

169 

170def interpolateables(state_a, state_b): 

171 

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)) 

181 

182 return animate 

183 

184 

185def interpolate(times, states, times_inter): 

186 

187 assert len(times) == len(states) 

188 

189 states_inter = [] 

190 for i in range(len(times) - 1): 

191 

192 state_a = states[i] 

193 state_b = states[i+1] 

194 time_a = times[i] 

195 time_b = times[i+1] 

196 

197 animate = interpolateables(state_a, state_b) 

198 

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)] 

205 

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) 

212 

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. 

220 

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) 

228 

229 set_elements(state, ypath, v_inter) 

230 else: 

231 set_elements(state, ypath, v_new) 

232 

233 states_inter.append(state) 

234 

235 return states_inter 

236 

237 

238class Interpolator(object): 

239 

240 def __init__(self, times, states, fps=25.): 

241 

242 assert len(times) == len(states) 

243 

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] 

250 

251 if times_inter.size == 1: 

252 self._states_inter = [clone(states[-1])] 

253 return 

254 

255 states_inter = [] 

256 for i in range(len(times) - 1): 

257 

258 state_a = states[i] 

259 state_b = states[i+1] 

260 time_a = times[i] 

261 time_b = times[i+1] 

262 

263 animate = interpolateables(state_a, state_b) 

264 

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)] 

271 

272 for time_inter in times_inter_this: 

273 state = clone(state_b) 

274 

275 if time_b == time_a: 

276 blend = 0. 

277 else: 

278 blend = (time_inter - time_a) / (time_b - time_a) 

279 

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. 

287 

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) 

295 

296 set_elements(state, ypath, v_inter) 

297 

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) 

301 

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) 

306 

307 else: 

308 set_elements(state, ypath, v_new) 

309 

310 states_inter.append(state) 

311 

312 self._states_inter = states_inter 

313 

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]