1# https://pyrocko.org - GPLv3
2#
3# The Pyrocko Developers, 21st Century
4# ---|P------/S----------~Lg----------
6import numpy as num
8from pyrocko.guts import Int, Float, Bool
9from pyrocko.gui.qt_compat import qw, qc
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
16guts_prefix = 'sparrow'
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)
32 def create(self):
33 element = SpheroidElement()
34 return element
37class SpheroidElement(Element):
39 def __init__(self):
40 Element.__init__(self)
41 self._mesh = None
42 self._controls = None
43 self._params = None
44 self._opacity = None
46 def get_name(self):
47 return 'Spheroid'
49 def bind_state(self, state):
50 Element.bind_state(self, state)
52 for var in [
53 'visible', 'level', 'opacity',
54 'lat', 'lon', 'depth', 'a', 'b', 'c', 'azimuth', 'dip']:
56 self.register_state_listener3(self.update, state, var)
58 def unbind_state(self):
59 self._listeners = []
61 def set_parent(self, parent):
62 Element.set_parent(self, parent)
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()])
72 self.update()
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
81 if self._controls:
82 self._parent.remove_panel(self._controls)
83 self._controls = None
85 self._parent.update_view()
86 self._parent = None
87 self._opacity = None
88 self._params = None
90 def update(self, *args):
91 state = self._state
93 params = (state.level,)
95 if self._mesh and (params != self._params or not state.visible):
96 self._parent.remove_actor(self._mesh.actor)
97 self._mesh = None
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)
105 self._vertices0 = vertices
106 self._mesh = TrimeshPipe(vertices, faces, smooth=True)
107 self._params = params
109 self._parent.add_actor(self._mesh.actor)
111 s = num.array(
112 [state.c, state.b, state.a]) / cake.earthradius
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
128 self._mesh.set_vertices(vertices)
129 self._mesh.prop.SetColor(0.8, 0.2, 0.1)
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
137 self._parent.update_view()
139 def move_here(self):
140 pstate = self._parent.state
141 state = self._state
142 state.lat = pstate.lat
143 state.lon = pstate.lon
145 def _get_controls(self):
146 state = self._state
147 if not self._controls:
148 from ..state import state_bind_slider
150 frame = qw.QFrame()
151 layout = qw.QGridLayout()
152 frame.setLayout(layout)
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.)]:
166 layout.addWidget(qw.QLabel(param.capitalize()), iy, 0)
168 slider = qw.QSlider(qc.Qt.Horizontal)
169 slider.setSizePolicy(qw.QSizePolicy(
170 qw.QSizePolicy.Expanding, qw.QSizePolicy.Fixed))
172 slider.setMinimum(int(round(vmin * 100.)))
173 slider.setMaximum(int(round(vmax * 100.)))
174 layout.addWidget(slider, iy, 1)
176 state_bind_slider(self, state, param, slider, factor=0.01)
177 iy += 1
179 pb = qw.QPushButton('Move Here')
180 layout.addWidget(pb, iy, 0)
181 pb.clicked.connect(self.move_here)
183 iy += 1
185 layout.addWidget(qw.QFrame(), iy, 0, 1, 2)
187 self._controls = frame
189 return self._controls
192__all__ = [
193 'SpheroidElement',
194 'SpheroidState']