From c85865e799fe01eb3a53eca532d4687f4dd44d36 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20H=C3=B8xbro=20Hansen?= Date: Fri, 28 Apr 2023 18:25:00 +0200 Subject: [PATCH 1/4] Firt iteration of quad support --- holoviews/plotting/bokeh/callbacks.py | 14 ++++---- holoviews/plotting/bokeh/links.py | 52 +++++---------------------- 2 files changed, 15 insertions(+), 51 deletions(-) diff --git a/holoviews/plotting/bokeh/callbacks.py b/holoviews/plotting/bokeh/callbacks.py index e43b87d61b..b30e8a7ed7 100644 --- a/holoviews/plotting/bokeh/callbacks.py +++ b/holoviews/plotting/bokeh/callbacks.py @@ -1167,20 +1167,20 @@ def _path_initialize(self): data = cds.data element = self.plot.current_frame - xs, ys, widths, heights = [], [], [], [] + l, b, r, t = [], [], [], [] for x, y in zip(data['xs'], data['ys']): x0, x1 = (np.nanmin(x), np.nanmax(x)) y0, y1 = (np.nanmin(y), np.nanmax(y)) - xs.append((x0+x1)/2.) - ys.append((y0+y1)/2.) - widths.append(x1-x0) - heights.append(y1-y0) - data = {'x': xs, 'y': ys, 'width': widths, 'height': heights} + l.append(x0) + b.append(y0) + r.append(x1) + t.append(y1) + data = {'left': l, 'bottom': b, 'right': r, 'top': t} data.update({vd.name: element.dimension_values(vd, expanded=False) for vd in element.vdims}) cds.data.update(data) style = self.plot.style[self.plot.cyclic_index] style.pop('cmap', None) - r1 = plot.state.rect('x', 'y', 'width', 'height', source=cds, **style) + r1 = plot.state.rect('left', 'bottom', 'right', 'top', source=cds, **style) if plot.handles['glyph_renderer'] in self.plot.state.renderers: self.plot.state.renderers.remove(plot.handles['glyph_renderer']) data = self._process_msg({'data': data})['data'] diff --git a/holoviews/plotting/bokeh/links.py b/holoviews/plotting/bokeh/links.py index 1228b3dc61..9095f1317a 100644 --- a/holoviews/plotting/bokeh/links.py +++ b/holoviews/plotting/bokeh/links.py @@ -233,53 +233,17 @@ class RectanglesTableLinkCallback(DataLinkCallback): on_target_changes = ['patching'] source_code = """ - var xs = source_cds.data[source_glyph.x.field] - var ys = source_cds.data[source_glyph.y.field] - var ws = source_cds.data[source_glyph.width.field] - var hs = source_cds.data[source_glyph.height.field] - - var x0 = [] - var x1 = [] - var y0 = [] - var y1 = [] - for (var i = 0; i < xs.length; i++) { - var hw = ws[i]/2. - var hh = hs[i]/2. - x0.push(xs[i]-hw) - x1.push(xs[i]+hw) - y0.push(ys[i]-hh) - y1.push(ys[i]+hh) - } - target_cds.data[columns[0]] = x0 - target_cds.data[columns[1]] = y0 - target_cds.data[columns[2]] = x1 - target_cds.data[columns[3]] = y1 + target_cds.data[columns[0]] = source_cds.data[source_glyph.left.field] + target_cds.data[columns[1]] = source_cds.data[source_glyph.bottom.field] + target_cds.data[columns[2]] = source_cds.data[source_glyph.right.field] + target_cds.data[columns[3]] = source_cds.data[source_glyph.top.field] """ target_code = """ - var x0s = target_cds.data[columns[0]] - var y0s = target_cds.data[columns[1]] - var x1s = target_cds.data[columns[2]] - var y1s = target_cds.data[columns[3]] - - var xs = [] - var ys = [] - var ws = [] - var hs = [] - for (var i = 0; i < x0s.length; i++) { - var x0 = Math.min(x0s[i], x1s[i]) - var y0 = Math.min(y0s[i], y1s[i]) - var x1 = Math.max(x0s[i], x1s[i]) - var y1 = Math.max(y0s[i], y1s[i]) - xs.push((x0+x1)/2.) - ys.push((y0+y1)/2.) - ws.push(x1-x0) - hs.push(y1-y0) - } - source_cds.data['x'] = xs - source_cds.data['y'] = ys - source_cds.data['width'] = ws - source_cds.data['height'] = hs + source_cds.data['left'] = target_cds.data[columns[0]] + source_cds.data['bottom'] = target_cds.data[columns[1]] + source_cds.data['right'] = target_cds.data[columns[2]] + source_cds.data['top'] = target_cds.data[columns[3]] """ def __init__(self, root_model, link, source_plot, target_plot=None): From 649c5fd26fa91607f75e94e2294fb95aef893ffb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20H=C3=B8xbro=20Hansen?= Date: Mon, 1 May 2023 12:56:26 +0200 Subject: [PATCH 2/4] Disable BoxEditToolbox for now --- holoviews/plotting/bokeh/callbacks.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/holoviews/plotting/bokeh/callbacks.py b/holoviews/plotting/bokeh/callbacks.py index b30e8a7ed7..0342d477f4 100644 --- a/holoviews/plotting/bokeh/callbacks.py +++ b/holoviews/plotting/bokeh/callbacks.py @@ -8,7 +8,7 @@ from bokeh.models import ( CustomJS, FactorRange, DatetimeAxis, Range1d, DataRange1d, - PolyDrawTool, BoxEditTool, PolyEditTool, FreehandDrawTool, + PolyDrawTool, PolyEditTool, FreehandDrawTool, PointDrawTool ) from panel.io.state import state @@ -1205,8 +1205,9 @@ def initialize(self, plot_id=None): renderer = self._path_initialize() if stream.styles: self._create_style_callback(cds, renderer.glyph) - box_tool = BoxEditTool(renderers=[renderer], **kwargs) - self.plot.state.tools.append(box_tool) + # BoxEditTool does not support Quad type only Rect + # box_tool = BoxEditTool(renderers=[renderer], **kwargs) + # self.plot.state.tools.append(box_tool) self._update_cds_vdims(cds.data) super(CDSCallback, self).initialize() From 5778e2ad0e18c83ef851b3cb0f280d28d705be04 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20H=C3=B8xbro=20Hansen?= Date: Mon, 1 May 2023 12:56:45 +0200 Subject: [PATCH 3/4] Rewrite _process_msg --- holoviews/plotting/bokeh/callbacks.py | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) diff --git a/holoviews/plotting/bokeh/callbacks.py b/holoviews/plotting/bokeh/callbacks.py index 0342d477f4..d17de8689d 100644 --- a/holoviews/plotting/bokeh/callbacks.py +++ b/holoviews/plotting/bokeh/callbacks.py @@ -1215,19 +1215,12 @@ def _process_msg(self, msg): data = super()._process_msg(msg) if 'data' not in data: return {} - data = data['data'] - x0s, x1s, y0s, y1s = [], [], [], [] - for (x, y, w, h) in zip(data['x'], data['y'], data['width'], data['height']): - x0s.append(x-w/2.) - x1s.append(x+w/2.) - y0s.append(y-h/2.) - y1s.append(y+h/2.) - values = {} - for col in data: - if col in ('x', 'y', 'width', 'height'): - continue - values[col] = data[col] - msg = {'data': dict(values, x0=x0s, x1=x1s, y0=y0s, y1=y1s)} + values = dict(data['data']) + values['x0'] = values.pop("left") + values['y0'] = values.pop("bottom") + values['x1'] = values.pop("right") + values['y1'] = values.pop("top") + msg = {'data': values} self._update_cds_vdims(msg['data']) return self._transform(msg) From 054526d389b370b9ca66b7272b556710ffbc4664 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20H=C3=B8xbro=20Hansen?= Date: Mon, 1 May 2023 14:48:27 +0200 Subject: [PATCH 4/4] Update tests --- holoviews/tests/plotting/bokeh/test_callbacks.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/holoviews/tests/plotting/bokeh/test_callbacks.py b/holoviews/tests/plotting/bokeh/test_callbacks.py index 43ce1e8a0d..30d422a9fb 100644 --- a/holoviews/tests/plotting/bokeh/test_callbacks.py +++ b/holoviews/tests/plotting/bokeh/test_callbacks.py @@ -260,7 +260,7 @@ def test_box_edit_callback(self): self.assertEqual(source.data['bottom'], [-0.5]) self.assertEqual(source.data['right'], [0.5]) self.assertEqual(source.data['top'], [0.5]) - data = {'x': [0, 1], 'y': [0, 1], 'width': [0.5, 2], 'height': [2, 0.5]} + data = {'left': [-0.25, 0], 'bottom': [-1, 0.75], 'right': [0.25, 2], 'top': [1, 1.25]} callback.on_msg({'data': data}) element = Rectangles([(-0.25, -1, 0.25, 1), (0, 0.75, 2, 1.25)]) self.assertEqual(box_edit.element, element) @@ -272,11 +272,11 @@ def test_box_edit_callback_legacy(self): self.assertIsInstance(plot.callbacks[0], BoxEditCallback) callback = plot.callbacks[0] source = plot.handles['cds'] - self.assertEqual(source.data['x'], [0]) - self.assertEqual(source.data['y'], [0]) - self.assertEqual(source.data['width'], [1]) - self.assertEqual(source.data['height'], [1]) - data = {'x': [0, 1], 'y': [0, 1], 'width': [0.5, 2], 'height': [2, 0.5]} + self.assertEqual(source.data['left'], [-0.5]) + self.assertEqual(source.data['bottom'], [-0.5]) + self.assertEqual(source.data['right'], [0.5]) + self.assertEqual(source.data['top'], [0.5]) + data = {'left': [-0.25, 0], 'bottom': [-1, 0.75], 'right': [0.25, 2], 'top': [1, 1.25]} callback.on_msg({'data': data}) element = Polygons([Box(0, 0, (0.5, 2)), Box(1, 1, (2, 0.5))]) self.assertEqual(box_edit.element, element)