1# https://pyrocko.org - GPLv3 

2# 

3# The Pyrocko Developers, 21st Century 

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

5 

6from __future__ import absolute_import, print_function, division 

7 

8import numpy as num 

9 

10from pyrocko.guts import Int, Float, Bool 

11from pyrocko.gui.qt_compat import qw, qc 

12 

13from pyrocko.gui.vtk_util import TrimeshPipe 

14from .base import Element, ElementState 

15from pyrocko import icosphere, moment_tensor as pmt, orthodrome as od, cake 

16from pyrocko.geometry import d2r 

17 

18guts_prefix = 'sparrow' 

19 

20 

21class SpheroidState(ElementState): 

22 level = Int.T(default=4) 

23 visible = Bool.T(default=True) 

24 opacity = Float.T(default=1.0) 

25 lat = Float.T(default=0.0) 

26 lon = Float.T(default=0.0) 

27 depth = Float.T(default=0.0) 

28 a = Float.T(default=10000.) 

29 b = Float.T(default=10000.) 

30 c = Float.T(default=10000.) 

31 azimuth = Float.T(default=0.0) 

32 dip = Float.T(default=0.0) 

33 

34 def create(self): 

35 element = SpheroidElement() 

36 return element 

37 

38 

39class SpheroidElement(Element): 

40 

41 def __init__(self): 

42 Element.__init__(self) 

43 self._mesh = None 

44 self._controls = None 

45 self._params = None 

46 self._opacity = None 

47 

48 def get_name(self): 

49 return 'Spheroid' 

50 

51 def bind_state(self, state): 

52 Element.bind_state(self, state) 

53 

54 for var in [ 

55 'visible', 'level', 'opacity', 

56 'lat', 'lon', 'depth', 'a', 'b', 'c', 'azimuth', 'dip']: 

57 

58 self.register_state_listener3(self.update, state, var) 

59 

60 def unbind_state(self): 

61 self._listeners = [] 

62 

63 def set_parent(self, parent): 

64 Element.set_parent(self, parent) 

65 

66 self._parent.add_panel( 

67 self.get_name(), 

68 self._get_controls(), 

69 visible=True, 

70 title_controls=[ 

71 self.get_title_control_remove(), 

72 self.get_title_control_visible()]) 

73 

74 self.update() 

75 

76 def unset_parent(self): 

77 self.unbind_state() 

78 if self._parent: 

79 if self._mesh: 

80 self._parent.remove_actor(self._mesh.actor) 

81 self._mesh = None 

82 

83 if self._controls: 

84 self._parent.remove_panel(self._controls) 

85 self._controls = None 

86 

87 self._parent.update_view() 

88 self._parent = None 

89 self._opacity = None 

90 self._params = None 

91 

92 def update(self, *args): 

93 state = self._state 

94 

95 params = (state.level,) 

96 

97 if self._mesh and (params != self._params or not state.visible): 

98 self._parent.remove_actor(self._mesh.actor) 

99 self._mesh = None 

100 

101 if state.visible and not self._mesh: 

102 vertices, faces = icosphere.sphere( 

103 state.level, 'icosahedron', 'kind1', 

104 radius=1.0, 

105 triangulate=False) 

106 

107 self._vertices0 = vertices 

108 self._mesh = TrimeshPipe(vertices, faces, smooth=True) 

109 self._params = params 

110 

111 self._parent.add_actor(self._mesh.actor) 

112 

113 s = num.array( 

114 [state.c, state.b, state.a]) / cake.earthradius 

115 

116 if self._mesh: 

117 vertices = self._vertices0 * s[num.newaxis, :] 

118 bc = num.matrix( 

119 [[1, 0, 0], [0, 0, 1], [0, 1, 0]], dtype=num.float64) 

120 rot = num.dot( 

121 bc, 

122 num.dot( 

123 pmt.euler_to_matrix(-d2r*state.azimuth, -d2r*state.dip, 0), 

124 bc.T)) 

125 vertices = num.dot(rot, vertices.T).T 

126 vertices[:, 0] += 1.0 - state.depth / cake.earthradius 

127 rot = od.rot_to_00(state.lat, state.lon).T 

128 vertices = num.dot(rot, vertices.T).T 

129 

130 self._mesh.set_vertices(vertices) 

131 self._mesh.prop.SetColor(0.8, 0.2, 0.1) 

132 

133 if self._mesh and self._opacity != state.opacity: 

134 self._mesh.set_opacity(state.opacity) 

135 self._opacity = state.opacity 

136 else: 

137 self._opacity = None 

138 

139 self._parent.update_view() 

140 

141 def move_here(self): 

142 pstate = self._parent.state 

143 state = self._state 

144 state.lat = pstate.lat 

145 state.lon = pstate.lon 

146 

147 def _get_controls(self): 

148 state = self._state 

149 if not self._controls: 

150 from ..state import state_bind_slider 

151 

152 frame = qw.QFrame() 

153 layout = qw.QGridLayout() 

154 frame.setLayout(layout) 

155 

156 iy = 0 

157 for (param, vmin, vmax) in [ 

158 ('lat', -90., 90.), 

159 ('lon', -180., 180.), 

160 ('depth', -10000., 100000.), 

161 ('a', 100., 100000.), 

162 ('b', 100., 100000.), 

163 ('c', 100., 100000.), 

164 ('azimuth', -180., 180.), 

165 ('dip', -90., 90.), 

166 ('opacity', 0., 1.)]: 

167 

168 layout.addWidget(qw.QLabel(param.capitalize()), iy, 0) 

169 

170 slider = qw.QSlider(qc.Qt.Horizontal) 

171 slider.setSizePolicy(qw.QSizePolicy( 

172 qw.QSizePolicy.Expanding, qw.QSizePolicy.Fixed)) 

173 

174 slider.setMinimum(int(round(vmin * 100.))) 

175 slider.setMaximum(int(round(vmax * 100.))) 

176 layout.addWidget(slider, iy, 1) 

177 

178 state_bind_slider(self, state, param, slider, factor=0.01) 

179 iy += 1 

180 

181 pb = qw.QPushButton('Move Here') 

182 layout.addWidget(pb, iy, 0) 

183 pb.clicked.connect(self.move_here) 

184 

185 iy += 1 

186 

187 layout.addWidget(qw.QFrame(), iy, 0, 1, 2) 

188 

189 self._controls = frame 

190 

191 return self._controls 

192 

193 

194__all__ = [ 

195 'SpheroidElement', 

196 'SpheroidState']