1# https://pyrocko.org - GPLv3 

2# 

3# The Pyrocko Developers, 21st Century 

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

5 

6import numpy as num 

7 

8from pyrocko.guts import Int, Float, Bool 

9from pyrocko.gui.qt_compat import qw, qc 

10 

11from pyrocko.gui.vtk_util import TrimeshPipe 

12from .base import Element, ElementState 

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

14from pyrocko.geometry import d2r 

15 

16guts_prefix = 'sparrow' 

17 

18 

19class SpheroidState(ElementState): 

20 level = Int.T(default=4) 

21 visible = Bool.T(default=True) 

22 opacity = Float.T(default=1.0) 

23 lat = Float.T(default=0.0) 

24 lon = Float.T(default=0.0) 

25 depth = Float.T(default=0.0) 

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

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

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

29 azimuth = Float.T(default=0.0) 

30 dip = Float.T(default=0.0) 

31 

32 def create(self): 

33 element = SpheroidElement() 

34 return element 

35 

36 

37class SpheroidElement(Element): 

38 

39 def __init__(self): 

40 Element.__init__(self) 

41 self._mesh = None 

42 self._controls = None 

43 self._params = None 

44 self._opacity = None 

45 

46 def get_name(self): 

47 return 'Spheroid' 

48 

49 def bind_state(self, state): 

50 Element.bind_state(self, state) 

51 

52 for var in [ 

53 'visible', 'level', 'opacity', 

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

55 

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

57 

58 def unbind_state(self): 

59 self._listeners = [] 

60 

61 def set_parent(self, parent): 

62 Element.set_parent(self, parent) 

63 

64 self._parent.add_panel( 

65 self.get_name(), 

66 self._get_controls(), 

67 visible=True, 

68 title_controls=[ 

69 self.get_title_control_remove(), 

70 self.get_title_control_visible()]) 

71 

72 self.update() 

73 

74 def unset_parent(self): 

75 self.unbind_state() 

76 if self._parent: 

77 if self._mesh: 

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

79 self._mesh = None 

80 

81 if self._controls: 

82 self._parent.remove_panel(self._controls) 

83 self._controls = None 

84 

85 self._parent.update_view() 

86 self._parent = None 

87 self._opacity = None 

88 self._params = None 

89 

90 def update(self, *args): 

91 state = self._state 

92 

93 params = (state.level,) 

94 

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

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

97 self._mesh = None 

98 

99 if state.visible and not self._mesh: 

100 vertices, faces = icosphere.sphere( 

101 state.level, 'icosahedron', 'kind1', 

102 radius=1.0, 

103 triangulate=False) 

104 

105 self._vertices0 = vertices 

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

107 self._params = params 

108 

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

110 

111 s = num.array( 

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

113 

114 if self._mesh: 

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

116 bc = num.matrix( 

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

118 rot = num.dot( 

119 bc, 

120 num.dot( 

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

122 bc.T)) 

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

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

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

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

127 

128 self._mesh.set_vertices(vertices) 

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

130 

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

132 self._mesh.set_opacity(state.opacity) 

133 self._opacity = state.opacity 

134 else: 

135 self._opacity = None 

136 

137 self._parent.update_view() 

138 

139 def move_here(self): 

140 pstate = self._parent.state 

141 state = self._state 

142 state.lat = pstate.lat 

143 state.lon = pstate.lon 

144 

145 def _get_controls(self): 

146 state = self._state 

147 if not self._controls: 

148 from ..state import state_bind_slider 

149 

150 frame = qw.QFrame() 

151 layout = qw.QGridLayout() 

152 frame.setLayout(layout) 

153 

154 iy = 0 

155 for (param, vmin, vmax) in [ 

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

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

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

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

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

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

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

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

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

165 

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

167 

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

169 slider.setSizePolicy(qw.QSizePolicy( 

170 qw.QSizePolicy.Expanding, qw.QSizePolicy.Fixed)) 

171 

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

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

174 layout.addWidget(slider, iy, 1) 

175 

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

177 iy += 1 

178 

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

180 layout.addWidget(pb, iy, 0) 

181 pb.clicked.connect(self.move_here) 

182 

183 iy += 1 

184 

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

186 

187 self._controls = frame 

188 

189 return self._controls 

190 

191 

192__all__ = [ 

193 'SpheroidElement', 

194 'SpheroidState']