Skip to content

Commit

Permalink
Merge pull request #371 from jfoster17/add-polygon-selection
Browse files Browse the repository at this point in the history
Add polygon selection
  • Loading branch information
astrofrog authored Jul 14, 2023
2 parents eff2a12 + b12d66a commit c848e4f
Show file tree
Hide file tree
Showing 4 changed files with 217 additions and 3 deletions.
91 changes: 90 additions & 1 deletion glue_jupyter/bqplot/common/tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
from contextlib import nullcontext

import numpy as np
from bqplot import PanZoom
from bqplot import PanZoom, Lines
from bqplot.interacts import BrushSelector, BrushIntervalSelector
from bqplot_image_gl.interacts import BrushEllipseSelector
from glue import __version__ as glue_version
Expand Down Expand Up @@ -178,6 +178,95 @@ def activate(self):
super().activate()


@viewer_tool
class BqplotPolygonMode(BqplotSelectionTool):
"""
Since Bqplot LassoSelector does not allow us to get the coordinates of the
selection (see https://github.com/bqplot/bqplot/pull/674), we simply use
a callback on the default viewer MouseInteraction and a patch to
display the selection.
"""
icon = 'glue_lasso'
tool_id = 'bqplot:polygon'
action_text = 'Polygonal ROI'
tool_tip = ('Lasso a region of interest\n')

def __init__(self, viewer, roi=None, finalize_callback=None, **kwargs):

super().__init__(viewer, **kwargs)

self.patch = Lines(x=[[]], y=[[]], fill_colors=[INTERACT_COLOR], colors=[INTERACT_COLOR],
opacities=[0.6], fill='inside', close_path=True,
scales={'x': self.viewer.scale_x, 'y': self.viewer.scale_y})
if roi is not None:
self.update_from_roi(roi)
self.finalize_callback = finalize_callback

def update_from_roi(self, roi):
"""
While other tools allow the user to click and drag to reposition a selection,
this probably does not make sense for a polygonal selection, so we do not do
not support this.
"""
pass

def activate(self):
"""
We do not call super().activate() because we don't have a separate interact,
instead we just add a callback to the default viewer MouseInteraction.
"""

# We need to make sure any existing callbacks associated with this
# viewer are cleared. This can happen if the user switches between
# different viewers without deactivating the tool.
try:
self.viewer.remove_event_callback(self.on_msg)
except KeyError:
pass

# Disable any active tool in other viewers
if self.viewer.session.application.get_setting('single_global_active_tool'):
for viewer in self.viewer.session.application.viewers:
if viewer is not self.viewer:
viewer.toolbar.active_tool = None
self.viewer.add_event_callback(self.on_msg, events=['dragstart', 'dragmove', 'dragend'])

def deactivate(self):
try:
self.viewer.remove_event_callback(self.on_msg)
except KeyError:
pass
super().deactivate()

def on_msg(self, event):
name = event['event']
domain = event['domain']
x, y = domain['x'], domain['y']
if name == 'dragstart':
self.original_marks = list(self.viewer.figure.marks)
self.viewer.figure.marks = self.original_marks + [self.patch]
self.patch.x = [x]
self.patch.y = [y]
elif name == 'dragmove':
self.patch.x = np.append(self.patch.x, x)
self.patch.y = np.append(self.patch.y, y)
elif name == 'dragend':
roi = PolygonalROI(vx=self.patch.x, vy=self.patch.y)
self.viewer.apply_roi(roi)

new_marks = []
for mark in self.viewer.figure.marks:
if mark == self.patch:
pass
else:
new_marks.append(mark)
self.viewer.figure.marks = new_marks
self.patch.x = [[]]
self.patch.y = [[]]
if self.finalize_callback is not None:
self.finalize_callback()


@viewer_tool
class BqplotCircleMode(BqplotSelectionTool):

Expand Down
2 changes: 1 addition & 1 deletion glue_jupyter/bqplot/image/viewer.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ class BqplotImageView(BqplotBaseView):
_state_cls = BqplotImageViewerState
_options_cls = ImageViewerStateWidget

tools = ['bqplot:home', 'bqplot:panzoom', 'bqplot:rectangle', 'bqplot:circle']
tools = ['bqplot:home', 'bqplot:panzoom', 'bqplot:rectangle', 'bqplot:circle', 'bqplot:polygon']

def __init__(self, session):

Expand Down
2 changes: 1 addition & 1 deletion glue_jupyter/bqplot/scatter/viewer.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ class BqplotScatterView(BqplotBaseView):
_layer_style_widget_cls = ScatterLayerStateWidget

tools = ['bqplot:home', 'bqplot:panzoom', 'bqplot:rectangle', 'bqplot:circle',
'bqplot:xrange', 'bqplot:yrange']
'bqplot:xrange', 'bqplot:yrange', 'bqplot:polygon']

def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
Expand Down
125 changes: 125 additions & 0 deletions glue_jupyter/icons/glue_lasso.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit c848e4f

Please sign in to comment.