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 self.talkie_connect( 

52 state, 

53 ['visible', 'level', 'opacity', 'lat', 'lon', 'depth', 'a', 'b', 

54 'c', 'azimuth', 'dip'], 

55 self.update) 

56 

57 def set_parent(self, parent): 

58 Element.set_parent(self, parent) 

59 

60 self._parent.add_panel( 

61 self.get_title_label(), 

62 self._get_controls(), 

63 visible=True, 

64 title_controls=[ 

65 self.get_title_control_remove(), 

66 self.get_title_control_visible()]) 

67 

68 self.update() 

69 

70 def unset_parent(self): 

71 self.unbind_state() 

72 if self._parent: 

73 if self._mesh: 

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

75 self._mesh = None 

76 

77 if self._controls: 

78 self._parent.remove_panel(self._controls) 

79 self._controls = None 

80 

81 self._parent.update_view() 

82 self._parent = None 

83 self._opacity = None 

84 self._params = None 

85 

86 def update(self, *args): 

87 state = self._state 

88 

89 params = (state.level,) 

90 

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

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

93 self._mesh = None 

94 

95 if state.visible and not self._mesh: 

96 vertices, faces = icosphere.sphere( 

97 state.level, 'icosahedron', 'kind1', 

98 radius=1.0, 

99 triangulate=False) 

100 

101 self._vertices0 = vertices 

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

103 self._params = params 

104 

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

106 

107 s = num.array( 

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

109 

110 if self._mesh: 

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

112 bc = num.matrix( 

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

114 rot = num.dot( 

115 bc, 

116 num.dot( 

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

118 bc.T)) 

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

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

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

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

123 

124 self._mesh.set_vertices(vertices) 

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

126 

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

128 self._mesh.set_opacity(state.opacity) 

129 self._opacity = state.opacity 

130 else: 

131 self._opacity = None 

132 

133 self._parent.update_view() 

134 

135 def move_here(self): 

136 pstate = self._parent.state 

137 state = self._state 

138 state.lat = pstate.lat 

139 state.lon = pstate.lon 

140 

141 def _get_controls(self): 

142 state = self._state 

143 if not self._controls: 

144 from ..state import state_bind_slider 

145 

146 frame = qw.QFrame() 

147 layout = qw.QGridLayout() 

148 frame.setLayout(layout) 

149 

150 iy = 0 

151 for (param, vmin, vmax) in [ 

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

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

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

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

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

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

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

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

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

161 

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

163 

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

165 slider.setSizePolicy(qw.QSizePolicy( 

166 qw.QSizePolicy.Expanding, qw.QSizePolicy.Fixed)) 

167 

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

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

170 layout.addWidget(slider, iy, 1) 

171 

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

173 iy += 1 

174 

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

176 layout.addWidget(pb, iy, 0) 

177 pb.clicked.connect(self.move_here) 

178 

179 iy += 1 

180 

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

182 

183 self._controls = frame 

184 

185 return self._controls 

186 

187 

188__all__ = [ 

189 'SpheroidElement', 

190 'SpheroidState']