-
Notifications
You must be signed in to change notification settings - Fork 75
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Canvas rotation #1983
Canvas rotation #1983
Changes from all commits
bbbdc93
fba75ee
0614424
9144f26
14ab342
ac5fb71
271b133
91a2ec1
8df7619
3870e0c
8c7498c
1137d6a
0d6fbb1
4e6d912
3addc58
7b4e353
6d462aa
688dffd
db7382f
b7c3f57
07a9039
255624f
4850c63
3329ccf
bc20ae4
e326adc
92018ca
1fcc539
1d21a25
94e5d7f
da3637e
023d806
237ea1b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -20,11 +20,26 @@ | |
</v-chip> | ||
</v-row> | ||
|
||
<img class='invert-in-dark' v-if="img_data" :src="`data:image/png;base64,${img_data}`" style="width: 100%; max-width: 400px" /> | ||
<img class='invert-in-dark' v-if="img_data" :src="`data:image/png;base64,${img_data}`" :style="'width: 100%; max-width: 400px; margin-top: 50px; transform: rotateY('+viewer_rotateY(canvas_flip_horizontal)+') rotate('+canvas_angle+'deg)'" /> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🤯 |
||
|
||
</j-tray-plugin> | ||
</template> | ||
|
||
<script> | ||
module.exports = { | ||
methods: { | ||
viewer_rotateY(canvas_flip_horizontal) { | ||
if (canvas_flip_horizontal) { | ||
return '180deg' | ||
} else { | ||
return '0deg' | ||
} | ||
} | ||
} | ||
}; | ||
</script> | ||
|
||
|
||
<style> | ||
.theme--dark .invert-in-dark { | ||
filter: brightness(0.88) invert(1); | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
from .rotate_canvas import * # noqa |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,136 @@ | ||
import os | ||
from traitlets import Bool, Unicode, observe | ||
|
||
from glue_jupyter.common.toolbar_vuetify import read_icon | ||
|
||
from jdaviz.configs.imviz.wcs_utils import get_compass_info | ||
from jdaviz.core.custom_traitlets import FloatHandleEmpty | ||
from jdaviz.core.events import AddDataMessage, RemoveDataMessage, CanvasRotationChangedMessage | ||
from jdaviz.core.registries import tray_registry | ||
from jdaviz.core.template_mixin import PluginTemplateMixin, ViewerSelectMixin | ||
from jdaviz.core.user_api import PluginUserApi | ||
from jdaviz.core.tools import ICON_DIR | ||
|
||
__all__ = ['RotateCanvas'] | ||
|
||
|
||
@tray_registry('imviz-rotate-canvas', label="Canvas Rotation", viewer_requirements='image') | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
class RotateCanvas(PluginTemplateMixin, ViewerSelectMixin): | ||
""" | ||
See the :ref:`Canvas Rotation Plugin Documentation <rotate-canvas>` for more details. | ||
|
||
Only the following attributes and methods are available through the | ||
:ref:`public plugin API <plugin-apis>`: | ||
|
||
* :meth:`~jdaviz.core.template_mixin.PluginTemplateMixin.show` | ||
* :meth:`~jdaviz.core.template_mixin.PluginTemplateMixin.open_in_tray` | ||
* ``viewer`` (:class:`~jdaviz.core.template_mixin.ViewerSelect`): | ||
Viewer to show orientation/compass information. | ||
* ``angle``: | ||
Angle to rotate the axes canvas, clockwise. | ||
* ``flip_horizontal``: | ||
Whether to flip the canvas horizontally, after applying rotation. | ||
* :meth:`reset` | ||
* :meth:`set_north_up_east_left` | ||
* :meth:`set_north_up_east_right` | ||
""" | ||
template_file = __file__, "rotate_canvas.vue" | ||
|
||
angle = FloatHandleEmpty(0).tag(sync=True) # degrees, clockwise | ||
flip_horizontal = Bool(False).tag(sync=True) # horizontal flip applied after rotation | ||
has_wcs = Bool(False).tag(sync=True) | ||
|
||
icon_nuer = Unicode(read_icon(os.path.join(ICON_DIR, 'right-east.svg'), 'svg+xml')).tag(sync=True) # noqa | ||
icon_nuel = Unicode(read_icon(os.path.join(ICON_DIR, 'left-east.svg'), 'svg+xml')).tag(sync=True) # noqa | ||
|
||
def __init__(self, *args, **kwargs): | ||
super().__init__(*args, **kwargs) | ||
|
||
self.hub.subscribe(self, AddDataMessage, handler=self._on_viewer_data_changed) | ||
self.hub.subscribe(self, RemoveDataMessage, handler=self._on_viewer_data_changed) | ||
|
||
@property | ||
def user_api(self): | ||
return PluginUserApi(self, expose=('viewer', 'angle', 'flip_horizontal', 'reset', | ||
'set_north_up_east_right', 'set_north_up_east_left')) | ||
|
||
@property | ||
def ref_data(self): | ||
return self.app.get_viewer_by_id(self.viewer.selected_id).state.reference_data | ||
|
||
def _on_viewer_data_changed(self, msg=None): | ||
if not self.viewer_selected: # pragma: no cover | ||
return | ||
self.has_wcs = getattr(self.ref_data, 'coords', None) is not None | ||
|
||
@observe('viewer_selected') | ||
def _viewer_selected_changed(self, *args, **kwargs): | ||
if not hasattr(self, 'viewer'): | ||
return | ||
vid = self.viewer.selected_id | ||
self.angle = self.app._viewer_item_by_id(vid).get('canvas_angle', 0) | ||
self.flip_horizontal = self.app._viewer_item_by_id(vid).get('canvas_flip_horizontal', False) | ||
|
||
def _get_wcs_angles(self): | ||
if not self.has_wcs: | ||
raise ValueError("reference data does not have WCS, cannot determine orientation") | ||
ref_data = self.ref_data | ||
if ref_data is None: # pragma: no cover | ||
raise ValueError("no data loaded in viewer, cannot determine orientation") | ||
_, _, _, _, _, _, degn, dege, flip = get_compass_info(ref_data.coords, ref_data.shape) | ||
return degn, dege, flip | ||
|
||
def reset(self): | ||
""" | ||
Reset the rotation to an angle of 0 and no flip | ||
""" | ||
self.angle = 0 | ||
self.flip_horizontal = False | ||
|
||
def set_north_up_east_left(self): | ||
""" | ||
Set the rotation angle and flip to achieve North up and East left according to the reference | ||
image WCS. | ||
""" | ||
degn, dege, flip = self._get_wcs_angles() | ||
self.angle = -degn | ||
self.flip_horizontal = flip | ||
|
||
def set_north_up_east_right(self): | ||
""" | ||
Set the rotation angle and flip to achieve North up and East right according to the | ||
reference image WCS. | ||
""" | ||
degn, dege, flip = self._get_wcs_angles() | ||
self.angle = -degn | ||
self.flip_horizontal = not flip | ||
|
||
def vue_reset(self, *args, **kwargs): | ||
self.reset() # pragma: no cover | ||
|
||
def vue_set_north_up_east_left(self, *args, **kwargs): | ||
self.set_north_up_east_left() # pragma: no cover | ||
|
||
def vue_set_north_up_east_right(self, *args, **kwargs): | ||
self.set_north_up_east_right() # pragma: no cover | ||
|
||
@observe('angle') | ||
def _angle_changed(self, *args, **kwargs): | ||
try: | ||
angle = float(self.angle) | ||
except ValueError: # pragma: no cover | ||
# empty string, etc | ||
angle = 0 | ||
|
||
# Rotate selected viewer canvas. This changes zoom too. | ||
self.app._viewer_item_by_id(self.viewer.selected_id)['canvas_angle'] = angle | ||
# broadcast message (used by compass, etc) | ||
self.hub.broadcast(CanvasRotationChangedMessage(self.viewer.selected_id, | ||
angle, self.flip_horizontal, sender=self)) | ||
|
||
@observe('flip_horizontal') | ||
def _flip_changed(self, *args, **kwargs): | ||
self.app._viewer_item_by_id(self.viewer.selected_id)['canvas_flip_horizontal'] = self.flip_horizontal # noqa | ||
self.hub.broadcast(CanvasRotationChangedMessage(self.viewer.selected_id, | ||
self.angle, self.flip_horizontal, | ||
sender=self)) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
<template> | ||
<j-tray-plugin | ||
description="Rotate viewer canvas to any orientation (note: this does not affect the underlying data)." | ||
:link="'https://jdaviz.readthedocs.io/en/'+vdocs+'/'+config+'/plugins.html#canvas-rotation'" | ||
:popout_button="popout_button"> | ||
|
||
<plugin-viewer-select | ||
:items="viewer_items" | ||
:selected.sync="viewer_selected" | ||
label="Viewer" | ||
hint="Select viewer." | ||
/> | ||
|
||
<v-row> | ||
<span style="line-height: 36px">Presets:</span> | ||
<j-tooltip tooltipcontent="reset rotation and flip"> | ||
<v-btn icon @click="reset"> | ||
<v-icon>mdi-restore</v-icon> | ||
</v-btn> | ||
</j-tooltip> | ||
<j-tooltip v-if="has_wcs" tooltipcontent="north up, east right"> | ||
<v-btn icon @click="set_north_up_east_right"> | ||
<img :src="icon_nuer" width="24" class="invert-if-dark" style="opacity: 0.65"/> | ||
</v-btn> | ||
</j-tooltip> | ||
<j-tooltip v-if="has_wcs" tooltipcontent="north up, east left"> | ||
<v-btn icon @click="set_north_up_east_left"> | ||
<img :src="icon_nuel" width="24" class="invert-if-dark" style="opacity: 0.65"/> | ||
</v-btn> | ||
</j-tooltip> | ||
</v-row> | ||
|
||
<v-row> | ||
<v-slider | ||
v-model="angle" | ||
class="align-center" | ||
max="180" | ||
min="-180" | ||
step="1" | ||
color="#00617E" | ||
track-color="#00617E" | ||
thumb-color="#153A4B" | ||
hide-details | ||
> | ||
</v-slider> | ||
</v-row> | ||
|
||
<v-row> | ||
<v-col> | ||
<v-text-field | ||
v-model.number="angle" | ||
type="number" | ||
label="Angle" | ||
hint="Rotation angle in degrees clockwise" | ||
></v-text-field> | ||
</v-col> | ||
</v-row> | ||
|
||
<v-row> | ||
<v-switch v-model="flip_horizontal" label="Flip horizontally after rotation"></v-switch> | ||
</v-row> | ||
|
||
</j-tray-plugin> | ||
</template> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nitpick: Not sure why
# noqa
is needed here.