Coverage for /usr/local/lib/python3.11/dist-packages/pyrocko/gui/sparrow/elements/axes_box.py: 22%

256 statements  

« prev     ^ index     » next       coverage.py v6.5.0, created at 2023-10-06 06:59 +0000

1# https://pyrocko.org - GPLv3 

2# 

3# The Pyrocko Developers, 21st Century 

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

5import vtk 

6import numpy as num 

7 

8from pyrocko import geometry, cake, orthodrome as od 

9from pyrocko.guts import Bool, Float 

10from pyrocko.gui.qt_compat import qw, qc 

11 

12from pyrocko.gui.vtk_util import Color 

13 

14from .base import Element, ElementState 

15 

16import string 

17 

18from .. import common, state as vstate 

19 

20 

21guts_prefix = 'sparrow' 

22 

23km = 1e3 

24 

25 

26class AxisPipe(object): 

27 

28 def __init__(self, start_point, end_point, axis_range, axis_type): 

29 

30 available_types = ['X', 'Y', 'Z'] 

31 if axis_type not in available_types: 

32 raise TypeError('Axis type %s not available!' % axis_type) 

33 

34 self._axis_type = axis_type 

35 

36 self.start_point = start_point 

37 self.end_point = end_point 

38 self.axis_range = axis_range 

39 

40 min_bounds = num.minimum(start_point, end_point) 

41 max_bounds = num.maximum(start_point, end_point) 

42 bounds = num.vstack((min_bounds, max_bounds)).ravel('f') 

43 

44 ax = vtk.vtkAxisActor() 

45 ax.SetPoint1(*start_point) 

46 ax.SetPoint2(*end_point) 

47 ax.SetRange(*axis_range) 

48 ax.SetBounds(*bounds) 

49 

50 self.actor = ax 

51 

52 def vector(self): 

53 return self.start_point - self.end_point 

54 

55 def length(self): 

56 return num.linalg.norm(self.vector()) 

57 

58 def unit_vector(self): 

59 xyz = self.vector() 

60 return xyz / num.linalg.norm(xyz) 

61 

62 def set_label_size(self, label_size): 

63 self.actor.SetTitleScale(label_size) 

64 self.actor.SetLabelScale(label_size) 

65 

66 def set_tick_size(self, tick_size): 

67 self.actor.SetMajorTickSize(tick_size) 

68 

69 def set_ticks(self, delta): 

70 

71 self.actor.SetDeltaRangeMajor(delta) 

72 self.actor.SetMajorRangeStart(self.axis_range[0]) 

73 self.actor.SetMajorStart(0, self.axis_range[0]) 

74 self.actor.SetTickLocationToInside() # Outside/Both/Inside 

75 nticks = int( 

76 round((self.axis_range[1] - self.axis_range[0]) / delta)) + 1 

77 

78 labels = vtk.vtkStringArray() 

79 labels.SetNumberOfTuples(nticks) 

80 for i in range(nticks): 

81 if self._axis_type == 'Z': 

82 tick_value = (self.axis_range[1] - i * delta) / 1e3 

83 else: 

84 tick_value = (self.axis_range[0] + i * delta) / 1e3 

85 

86 labels.SetValue(i, '%0.1f' % tick_value) 

87 

88 self.actor.SetLabels(labels) 

89 self.actor.SetCalculateLabelOffset(0) 

90 

91 def set_colors(self, color): 

92 tprop = self.actor.GetTitleTextProperty() 

93 tprop.SetColor(color.rgb) 

94 tprop.SetBackgroundColor(color.rgb) 

95 tprop.SetFontFamilyToArial() 

96 

97 lprop = self.actor.GetLabelTextProperty() 

98 lprop.SetColor(color.rgb) 

99 lprop.SetFontFamilyToArial() 

100 

101 self.actor.GetAxisLinesProperty().SetColor(color.rgb) 

102 self.actor.GetAxisMajorTicksProperty().SetColor(color.rgb) 

103 

104 def set_linewidth(self, linewidth): 

105 self.actor.GetAxisLinesProperty().SetLineWidth(linewidth) 

106 self.actor.GetAxisMajorTicksProperty().SetLineWidth(linewidth) 

107 

108 def set_axes_base(self, basex, basey, basez): 

109 self.actor.SetAxisBaseForX(*basex) 

110 self.actor.SetAxisBaseForY(*basey) 

111 self.actor.SetAxisBaseForZ(*basez) 

112 

113 getattr(self.actor, "SetAxisTypeTo%s" % self._axis_type)() 

114 

115 

116class BoxPipe(object): 

117 

118 def __init__(self, width, length, height, lat, lon, depth, camera): 

119 

120 self._line_width = None 

121 self._color = Color('white') 

122 

123 origin_xyz = geometry.latlondepth2xyz( 

124 num.atleast_2d(num.array((lat, lon, depth))), 

125 planetradius=cake.earthradius) 

126 z_top = geometry.latlondepth2xyz( 

127 num.atleast_2d(num.array((lat, lon, depth - height))), 

128 planetradius=cake.earthradius) 

129 

130 lat2, lon2 = od.ne_to_latlon(lat, lon, 0, length) 

131 x_east = geometry.latlondepth2xyz( 

132 num.atleast_2d(num.array((lat2, lon2, depth))), 

133 planetradius=cake.earthradius) 

134 

135 lat3, lon3 = od.ne_to_latlon(lat, lon, width, 0) 

136 y_north = geometry.latlondepth2xyz( 

137 num.atleast_2d(num.array((lat3, lon3, depth))), 

138 planetradius=cake.earthradius) 

139 

140 xax_range = (0, length) 

141 yax_range = (0, width) 

142 zax_range = (depth - height, depth) 

143 

144 end_points = [x_east, y_north, z_top] 

145 axis_ranges = [xax_range, yax_range, zax_range] 

146 labels = ['E-Distance [km]', 'N-Distance [km]', 'Depth [km]'] 

147 axis_types = ['X', 'Y', 'Z'] 

148 

149 vector_base = [] 

150 self.axes = [] 

151 for end_point, axis_range, label, axis_type in zip( 

152 end_points, axis_ranges, labels, axis_types): 

153 

154 ax = AxisPipe( 

155 start_point=origin_xyz, 

156 end_point=end_point, 

157 axis_range=axis_range, 

158 axis_type=axis_type) 

159 

160 ax.actor.SetTitle(label) 

161 ax.set_colors(self._color) 

162 ax.actor.SetCamera(camera) 

163 

164 # correct tick rotation around the globe 

165 vector_base.append(ax.unit_vector()) 

166 self.axes.append(ax) 

167 

168 for ax in self.axes: 

169 ax.set_axes_base(*vector_base) 

170 

171 @property 

172 def actor(self): 

173 return [ax.actor for ax in self.axes] 

174 

175 def set_color(self, color): 

176 if color != self._color: 

177 for ax in self.axes: 

178 ax.set_colors(color) 

179 

180 self._color = color 

181 

182 def set_label_size(self, label_size): 

183 for ax in self.axes: 

184 ax.set_label_size(label_size) 

185 

186 self._label_size = label_size 

187 

188 def set_line_width(self, linewidth): 

189 linewidth = float(linewidth) 

190 if self._line_width != linewidth: 

191 for ax in self.axes: 

192 ax.set_linewidth(linewidth) 

193 

194 self._line_width = linewidth 

195 

196 def set_ticks(self, tick_size, delta): 

197 ''' 

198 Set tick size of all axes to smallest tick-size. 

199 ''' 

200 for ax in self.axes: 

201 ax.set_ticks(delta=delta) 

202 ax.set_tick_size(tick_size) 

203 

204 

205box_ranges = { 

206 'lat': {'min': -90., 'max': 90., 'step': 1, 'ini': 0.}, 

207 'lon': {'min': -180., 'max': 180., 'step': 1, 'ini': 0.}, 

208 'depth': {'min': 0., 'max': 600 * km, 'step': 1 * km, 'ini': 10. * km}, 

209 'width': {'min': 0.1, 'max': 1000. * km, 'step': 1 * km, 'ini': 10. * km}, 

210 'length': {'min': 0.1, 'max': 1000. * km, 'step': 1 * km, 'ini': 50. * km}, 

211 'height': {'min': 0.1, 'max': 500. * km, 'step': 1 * km, 'ini': 10. * km}, 

212 'delta': {'min': 0.01, 'max': 250. * km, 'step': 1 * km, 'ini': 5. * km}} 

213 

214 

215class AxesBoxState(ElementState): 

216 visible = Bool.T(default=True) 

217 width = Float.T(default=20.0 * km) 

218 length = Float.T(default=20.0 * km) 

219 height = Float.T(default=10.0 * km) 

220 lat = Float.T(default=0.0) 

221 lon = Float.T(default=0.0) 

222 depth = Float.T(default=5. * km) 

223 color = Color.T(default=Color.D('white')) 

224 line_width = Float.T(default=1.0) 

225 label_size = Float.T(default=0.0001) 

226 tick_size = Float.T(default=0.0001) 

227 delta = Float.T(default=5 * km) 

228 

229 def create(self): 

230 element = AxesBoxElement() 

231 return element 

232 

233 

234class AxesBoxElement(Element): 

235 

236 def __init__(self): 

237 Element.__init__(self) 

238 self._pipe = [] 

239 self._controls = None 

240 

241 def bind_state(self, state): 

242 Element.bind_state(self, state) 

243 

244 self.talkie_connect( 

245 state, 

246 ['visible', 'width', 'length', 'height', 'lat', 'lon', 'depth', 

247 'color', 'line_width', 'label_size', 'tick_size', 'delta'], 

248 self.update) 

249 

250 def _state_bind_box(self, *args, **kwargs): 

251 vstate.state_bind(self, self._state, *args, **kwargs) 

252 

253 def get_name(self): 

254 return 'AxesBox' 

255 

256 def set_parent(self, parent): 

257 self._parent = parent 

258 

259 self._parent.add_panel( 

260 self.get_title_label(), 

261 self._get_controls(), 

262 visible=True, 

263 title_controls=[ 

264 self.get_title_control_remove(), 

265 self.get_title_control_visible()]) 

266 

267 self.update() 

268 

269 def unset_parent(self): 

270 self.unbind_state() 

271 if self._parent: 

272 

273 if self._pipe: 

274 for pipe in self._pipe: 

275 if isinstance(pipe.actor, list): 

276 for act in pipe.actor: 

277 self._parent.remove_actor(act) 

278 else: 

279 self._parent.remove_actor(pipe.actor) 

280 self._pipe = [] 

281 

282 if self._controls is not None: 

283 self._parent.remove_panel(self._controls) 

284 self._controls = None 

285 

286 self._parent.update_view() 

287 self._parent = None 

288 

289 def update_box(self): 

290 

291 state = self._state 

292 

293 camera = self._parent.ren.GetActiveCamera() 

294 box_pipe = BoxPipe( 

295 state.width, state.length, state.height, 

296 state.lat, state.lon, state.depth, camera) 

297 box_pipe.set_color(state.color) 

298 box_pipe.set_line_width(state.line_width) 

299 box_pipe.set_ticks(state.tick_size, state.delta) 

300 box_pipe.set_label_size(state.label_size) 

301 

302 self._pipe.append(box_pipe) 

303 

304 for actor in box_pipe.actor: 

305 self._parent.add_actor(actor) 

306 

307 def update(self, *args): 

308 state = self._state 

309 

310 if self._pipe: 

311 for pipe in self._pipe: 

312 if isinstance(pipe.actor, list): 

313 for actor in pipe.actor: 

314 self._parent.remove_actor(actor) 

315 else: 

316 self._parent.remove_actor(pipe.actor) 

317 

318 self._pipe = [] 

319 

320 if state.visible: 

321 self.update_box() 

322 

323 self._parent.update_view() 

324 

325 def _get_controls(self): 

326 if not self._controls: 

327 from ..state import state_bind_slider, state_bind_combobox_color 

328 

329 frame = qw.QFrame() 

330 layout = qw.QGridLayout() 

331 frame.setLayout(layout) 

332 

333 def state_to_lineedit(state, attribute, widget): 

334 sel = getattr(state, attribute) 

335 

336 widget.setText('%g' % sel) 

337 # if sel: 

338 # widget.selectAll() 

339 

340 def lineedit_to_state(widget, state, attribute): 

341 s = float(widget.text()) 

342 try: 

343 setattr(state, attribute, s) 

344 except Exception: 

345 raise ValueError( 

346 'Value of %s needs to be a float or integer' 

347 % string.capwords(attribute)) 

348 

349 for il, label in enumerate(box_ranges.keys()): 

350 layout.addWidget(qw.QLabel( 

351 string.capwords(label) + ':'), il, 0) 

352 

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

354 slider.setSizePolicy( 

355 qw.QSizePolicy( 

356 qw.QSizePolicy.Expanding, qw.QSizePolicy.Fixed)) 

357 slider.setMinimum( 

358 int(round(box_ranges[label]['min']))) 

359 slider.setMaximum( 

360 int(round(box_ranges[label]['max']))) 

361 slider.setSingleStep( 

362 int(round(box_ranges[label]['step']))) 

363 slider.setPageStep( 

364 int(round(box_ranges[label]['step']))) 

365 

366 layout.addWidget(slider, il, 1) 

367 

368 try: 

369 state_bind_slider( 

370 self, self._state, label, slider, 

371 factor=box_ranges[label]['fac']) 

372 except Exception: 

373 state_bind_slider( 

374 self, self._state, label, slider) 

375 

376 le = qw.QLineEdit() 

377 layout.addWidget(le, il, 2) 

378 

379 self._state_bind_box( 

380 [label], lineedit_to_state, le, 

381 [le.editingFinished, le.returnPressed], 

382 state_to_lineedit, attribute=label) 

383 

384 # color 

385 il += 1 

386 layout.addWidget(qw.QLabel('Color'), il, 0) 

387 

388 cb = common.strings_to_combobox( 

389 ['black', 'white']) 

390 

391 layout.addWidget(cb, il, 1) 

392 state_bind_combobox_color( 

393 self, self._state, 'color', cb) 

394 

395 # linewidth 

396 il += 1 

397 layout.addWidget(qw.QLabel('Line width'), il, 0) 

398 

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

400 slider.setSizePolicy( 

401 qw.QSizePolicy( 

402 qw.QSizePolicy.Expanding, qw.QSizePolicy.Fixed)) 

403 slider.setMinimum(0) 

404 slider.setMaximum(100) 

405 layout.addWidget(slider, il, 1) 

406 state_bind_slider( 

407 self, self._state, 'line_width', slider, factor=0.1) 

408 

409 # tick size 

410 il += 1 

411 layout.addWidget(qw.QLabel('tick size'), il, 0) 

412 

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

414 slider.setSizePolicy( 

415 qw.QSizePolicy( 

416 qw.QSizePolicy.Expanding, qw.QSizePolicy.Fixed)) 

417 slider.setMinimum(1) 

418 slider.setMaximum(1000) 

419 layout.addWidget(slider, il, 1) 

420 state_bind_slider( 

421 self, self._state, 'tick_size', slider, factor=0.00001) 

422 

423 # label size 

424 il += 1 

425 layout.addWidget(qw.QLabel('label size'), il, 0) 

426 

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

428 slider.setSizePolicy( 

429 qw.QSizePolicy( 

430 qw.QSizePolicy.Expanding, qw.QSizePolicy.Fixed)) 

431 slider.setMinimum(1) 

432 slider.setMaximum(1000) 

433 layout.addWidget(slider, il, 1) 

434 state_bind_slider( 

435 self, self._state, 'label_size', slider, factor=0.00001) 

436 

437 self._controls = frame 

438 

439 return self._controls 

440 

441 

442__all__ = [ 

443 'AxesBoxElement', 

444 'AxesBoxState' 

445]