From 7b8cbb807036ce339c033f972d1759cdad6077ed Mon Sep 17 00:00:00 2001 From: kushalkolar Date: Sun, 21 May 2023 09:28:10 -0400 Subject: [PATCH 1/5] garbage collection of selectors, not tested yet --- fastplotlib/layouts/_base.py | 54 ++++++++++++++++++++++++++---------- 1 file changed, 39 insertions(+), 15 deletions(-) diff --git a/fastplotlib/layouts/_base.py b/fastplotlib/layouts/_base.py index 3408b3a82..02e749b04 100644 --- a/fastplotlib/layouts/_base.py +++ b/fastplotlib/layouts/_base.py @@ -9,13 +9,14 @@ from wgpu.gui.auto import WgpuCanvas from ..graphics._base import Graphic, GraphicCollection -from ..graphics.selectors import LinearSelector +from ..graphics.selectors._base_selector import BaseSelector # dict to store Graphic instances # this is the only place where the real references to Graphics are stored in a Python session # {hex id str: Graphic} GRAPHICS: Dict[str, Graphic] = dict() +SELECTORS: Dict[str, BaseSelector] = dict() class PlotArea: @@ -81,8 +82,9 @@ def __init__( # the real Graphic instances are stored in the ``GRAPHICS`` dict self._graphics: List[str] = list() - # hacky workaround for now to exclude from bbox calculations - self._selectors = list() + # selectors are in their own list so they can be excluded from scene bbox calculations + # managed similar to GRAPHICS for garbage collection etc. + self._selectors: List[str] = list() self.name = name @@ -133,7 +135,7 @@ def controller(self) -> Union[PanZoomController, OrbitController]: return self._controller @property - def graphics(self) -> Tuple[Graphic]: + def graphics(self) -> Tuple[Graphic, ...]: """Graphics in the plot area. Always returns a proxy to the Graphic instances.""" proxies = list() for loc in self._graphics: @@ -142,6 +144,16 @@ def graphics(self) -> Tuple[Graphic]: return tuple(proxies) + @property + def selectors(self) -> Tuple[BaseSelector, ...]: + """Selectors in the plot area. Always returns a proxy to the Graphic instances.""" + proxies = list() + for loc in self._selectors: + p = weakref.proxy(GRAPHICS[loc]) + proxies.append(p) + + return tuple(proxies) + def get_rect(self) -> Tuple[float, float, float, float]: """allows setting the region occupied by the viewport w.r.t. the parent""" raise NotImplementedError("Must be implemented in subclass") @@ -212,8 +224,10 @@ def add_graphic(self, graphic: Graphic, center: bool = True): if graphic.name is not None: # skip for those that have no name self._check_graphic_name_exists(graphic.name) - # TODO: need to refactor LinearSelector entirely - if isinstance(graphic, LinearSelector): + if isinstance(graphic, BaseSelector): + # store in SELECTORS dict + loc = graphic.loc + SELECTORS[loc] = graphic self._selectors.append(graphic) # don't manage garbage collection of LineSliders for now else: # store in GRAPHICS dict @@ -292,10 +306,10 @@ def auto_scale(self, maintain_aspect: bool = False, zoom: float = 0.8): zoom value for the camera after auto-scaling, if zoom = 1.0 then the graphics in the scene will fill the entire canvas. """ - # hacky workaround for now until I figure out how to put it in its own scene - # TODO: remove all selectors from a scene to calculate scene bbox - for slider in self._selectors: - self.scene.remove(slider.world_object) + # hacky workaround for now until we decided if we want to put selectors in their own scene + # remove all selectors from a scene to calculate scene bbox + for selector in self._selectors: + self.scene.remove(selector.world_object) self.center_scene() if not isinstance(maintain_aspect, bool): @@ -307,8 +321,8 @@ def auto_scale(self, maintain_aspect: bool = False, zoom: float = 0.8): else: width, height, depth = (1, 1, 1) - for slider in self._selectors: - self.scene.add(slider.world_object) + for selector in self._selectors: + self.scene.add(selector.world_object) self.camera.width = width self.camera.height = height @@ -346,7 +360,14 @@ def delete_graphic(self, graphic: Graphic): # get location graphic_loc = graphic.loc - if graphic_loc not in self._graphics: + # check which dict it's in + if graphic_loc in self._graphics: + glist = self._graphics + kind = "graphic" + elif graphic in self._selectors: + kind = "selector" + glist = self._selectors + else: raise KeyError(f"Graphic with following address not found in plot area: {graphic_loc}") # remove from scene if necessary @@ -354,7 +375,7 @@ def delete_graphic(self, graphic: Graphic): self.scene.remove(graphic.world_object) # remove from list of addresses - self._graphics.remove(graphic_loc) + glist.remove(graphic_loc) # for GraphicCollection objects # if isinstance(graphic, GraphicCollection): @@ -371,7 +392,10 @@ def delete_graphic(self, graphic: Graphic): # delete world object #del WORLD_OBJECTS[graphic_loc] - del GRAPHICS[graphic_loc] + if kind == "graphic": + del GRAPHICS[graphic_loc] + elif kind == "selector": + del SELECTORS[graphic_loc] def clear(self): """ From 5352694876d86d60667049ee84c7cce40e54bc49 Mon Sep 17 00:00:00 2001 From: kushalkolar Date: Sun, 21 May 2023 22:00:58 -0400 Subject: [PATCH 2/5] selecors gc progress, not there yet but not broken either --- fastplotlib/layouts/_base.py | 42 +++++++++++------------------------- 1 file changed, 13 insertions(+), 29 deletions(-) diff --git a/fastplotlib/layouts/_base.py b/fastplotlib/layouts/_base.py index 02e749b04..1571f51e9 100644 --- a/fastplotlib/layouts/_base.py +++ b/fastplotlib/layouts/_base.py @@ -149,7 +149,7 @@ def selectors(self) -> Tuple[BaseSelector, ...]: """Selectors in the plot area. Always returns a proxy to the Graphic instances.""" proxies = list() for loc in self._selectors: - p = weakref.proxy(GRAPHICS[loc]) + p = weakref.proxy(SELECTORS[loc]) proxies.append(p) return tuple(proxies) @@ -228,7 +228,7 @@ def add_graphic(self, graphic: Graphic, center: bool = True): # store in SELECTORS dict loc = graphic.loc SELECTORS[loc] = graphic - self._selectors.append(graphic) # don't manage garbage collection of LineSliders for now + self._selectors.append(loc) # don't manage garbage collection of LineSliders for now else: # store in GRAPHICS dict loc = graphic.loc @@ -308,7 +308,7 @@ def auto_scale(self, maintain_aspect: bool = False, zoom: float = 0.8): """ # hacky workaround for now until we decided if we want to put selectors in their own scene # remove all selectors from a scene to calculate scene bbox - for selector in self._selectors: + for selector in self.selectors: self.scene.remove(selector.world_object) self.center_scene() @@ -321,7 +321,7 @@ def auto_scale(self, maintain_aspect: bool = False, zoom: float = 0.8): else: width, height, depth = (1, 1, 1) - for selector in self._selectors: + for selector in self.selectors: self.scene.add(selector.world_object) self.camera.width = width @@ -354,48 +354,32 @@ def delete_graphic(self, graphic: Graphic): The graphic to delete """ - - # graphic_loc = hex(id(graphic.__repr__.__self__)) - + # TODO: proper gc of selectors, RAM is freed for regular graphics but not selectors + # TODO: references to selectors must be lingering somewhere # get location - graphic_loc = graphic.loc + loc = graphic.loc # check which dict it's in - if graphic_loc in self._graphics: + if loc in self._graphics: glist = self._graphics kind = "graphic" - elif graphic in self._selectors: + elif loc in self._selectors: kind = "selector" glist = self._selectors else: - raise KeyError(f"Graphic with following address not found in plot area: {graphic_loc}") + raise KeyError(f"Graphic with following address not found in plot area: {loc}") # remove from scene if necessary if graphic.world_object in self.scene.children: self.scene.remove(graphic.world_object) # remove from list of addresses - glist.remove(graphic_loc) - - # for GraphicCollection objects - # if isinstance(graphic, GraphicCollection): - # # clear Group - # graphic.world_object.clear() - # graphic.clear() - # delete all child world objects in the collection - # for g in graphic.graphics: - # subloc = hex(id(g)) - # del WORLD_OBJECTS[subloc] - - # get mem location of graphic - # loc = hex(id(graphic)) - # delete world object - #del WORLD_OBJECTS[graphic_loc] + glist.remove(loc) if kind == "graphic": - del GRAPHICS[graphic_loc] + del GRAPHICS[loc] elif kind == "selector": - del SELECTORS[graphic_loc] + del SELECTORS[loc] def clear(self): """ From 9928dfac18173e950fa095aa5445ee439434ccca Mon Sep 17 00:00:00 2001 From: kushalkolar Date: Sun, 21 May 2023 22:53:06 -0400 Subject: [PATCH 3/5] move events for transparent fill areas --- fastplotlib/graphics/image.py | 28 ++++++++++++----- .../graphics/selectors/_base_selector.py | 30 ++++++++++++++++++- fastplotlib/graphics/selectors/_linear.py | 2 +- .../graphics/selectors/_linear_region.py | 4 ++- 4 files changed, 54 insertions(+), 10 deletions(-) diff --git a/fastplotlib/graphics/image.py b/fastplotlib/graphics/image.py index 5df96b2dd..9ba9a4143 100644 --- a/fastplotlib/graphics/image.py +++ b/fastplotlib/graphics/image.py @@ -14,16 +14,16 @@ class _ImageHeatmapSelectorsMixin: - def add_linear_selector(self, selection: int = None, padding: float = 50, **kwargs) -> LinearSelector: + def add_linear_selector(self, selection: int = None, padding: float = None, **kwargs) -> LinearSelector: """ Adds a linear selector. Parameters ---------- - selection: int + selection: int, optional initial position of the selector - padding: float + padding: float, optional pad the length of the selector kwargs @@ -35,6 +35,12 @@ def add_linear_selector(self, selection: int = None, padding: float = 50, **kwar """ + # default padding is 15% the height or width of the image + if "axis" in kwargs.keys(): + axis = kwargs["axis"] + else: + axis = "x" + bounds_init, limits, size, origin, axis, end_points = self._get_linear_selector_init_args(padding, **kwargs) if selection is None: @@ -56,17 +62,17 @@ def add_linear_selector(self, selection: int = None, padding: float = 50, **kwar return weakref.proxy(selector) - def add_linear_region_selector(self, padding: float = 100.0, **kwargs) -> LinearRegionSelector: + def add_linear_region_selector(self, padding: float = None, **kwargs) -> LinearRegionSelector: """ Add a :class:`.LinearRegionSelector`. Selectors are just ``Graphic`` objects, so you can manage, remove, or delete them from a plot area just like any other ``Graphic``. Parameters ---------- - padding: float, default 100.0 + padding: float, optional Extends the linear selector along the y-axis to make it easier to interact with. - kwargs + kwargs, optional passed to ``LinearRegionSelector`` Returns @@ -85,13 +91,13 @@ def add_linear_region_selector(self, padding: float = 100.0, **kwargs) -> Linear size=size, origin=origin, parent=weakref.proxy(self), + fill_color=(0, 0, 0.35, 0.2), **kwargs ) self._plot_area.add_graphic(selector, center=False) # so that it is above this graphic selector.position.set_z(self.position.z + 3) - selector.fill.material.color = (*selector.fill.material.color[:-1], 0.2) # PlotArea manages this for garbage collection etc. just like all other Graphics # so we should only work with a proxy on the user-end @@ -107,6 +113,14 @@ def _get_linear_selector_init_args(self, padding: float, **kwargs): else: axis = "x" + if padding is None: + if axis == "x": + # based on number of rows + padding = int(data.shape[0] * 0.15) + elif axis == "y": + # based on number of columns + padding = int(data.shape[1] * 0.15) + if axis == "x": offset = self.position.x # x limits, number of columns diff --git a/fastplotlib/graphics/selectors/_base_selector.py b/fastplotlib/graphics/selectors/_base_selector.py index 682e1855b..3a8d51577 100644 --- a/fastplotlib/graphics/selectors/_base_selector.py +++ b/fastplotlib/graphics/selectors/_base_selector.py @@ -110,6 +110,11 @@ def _add_plot_area_hook(self, plot_area): # double-click to enable arrow-key moveable mode wo.add_event_handler(self._toggle_arrow_key_moveable, "double_click") + for fill in self._fill: + if fill.material.color_is_transparent: + pfunc_fill = partial(self._check_fill_pointer_event, fill) + self._plot_area.renderer.add_event_handler(pfunc_fill, "pointer_down") + # when the pointer moves self._plot_area.renderer.add_event_handler(self._move, "pointer_move") @@ -129,6 +134,27 @@ def _add_plot_area_hook(self, plot_area): self._plot_area.renderer.add_event_handler(self._key_up, "key_up") self._plot_area.add_animations(self._key_hold) + def _check_fill_pointer_event(self, event_source: WorldObject, ev): + world_pos = self._plot_area.map_screen_to_world((ev.x, ev.y)) + # outside viewport, ignore + # this shouldn't be possible since the event handler is registered to the fill mesh world object + # but I like sanity checks anyways + if world_pos is None: + return + + bbox = event_source.get_world_bounding_box() + + xmin, ymin, zmin = bbox[0] + xmax, ymax, zmax = bbox[1] + + if not (xmin <= world_pos.x <= xmax): + return + + if not (ymin <= world_pos.y <= ymax): + return + + self._move_start(event_source, ev) + def _move_start(self, event_source: WorldObject, ev): """ Called on "pointer_down" events @@ -136,7 +162,9 @@ def _move_start(self, event_source: WorldObject, ev): Parameters ---------- event_source: WorldObject - event source, for example selection fill area ``Mesh`` an edge ``Line`` or vertex ``Points`` + event source, for example selection fill area ``Mesh`` an edge ``Line`` or vertex ``Points``. + This helps keep the event source within the MoveInfo so that during "pointer_move" events (which are + from the renderer) we know the original source of the "move action". ev: Event pygfx ``Event`` diff --git a/fastplotlib/graphics/selectors/_linear.py b/fastplotlib/graphics/selectors/_linear.py index ec18061d6..ada04a535 100644 --- a/fastplotlib/graphics/selectors/_linear.py +++ b/fastplotlib/graphics/selectors/_linear.py @@ -163,7 +163,7 @@ def __init__( line_data = np.column_stack([xs, ys, zs]) else: - raise ValueError("`axis` must be one of 'v' or 'h'") + raise ValueError("`axis` must be one of 'x' or 'y'") line_data = line_data.astype(np.float32) diff --git a/fastplotlib/graphics/selectors/_linear_region.py b/fastplotlib/graphics/selectors/_linear_region.py index d41c47de8..c62d60adf 100644 --- a/fastplotlib/graphics/selectors/_linear_region.py +++ b/fastplotlib/graphics/selectors/_linear_region.py @@ -229,6 +229,8 @@ def __init__( pygfx.box_geometry(size, 1, 1), pygfx.MeshBasicMaterial(color=pygfx.Color(fill_color)) ) + else: + raise ValueError("`axis` must be one of 'x' or 'y'") # the fill of the selection self.fill = mesh @@ -314,7 +316,7 @@ def __init__( def bounds(self) -> LinearBoundsFeature: """ The current bounds of the selection in world space. These bounds will NOT necessarily correspond to the - indices of the data that are under the selection. Use ``get_selected_indices()` which maps from + indices of the data that are under the selection. Use ``get_selected_indices()`` which maps from world space to data indices. """ return self._bounds From 3528d320469dce3dd60e6faeea74ab76ae528f9a Mon Sep 17 00:00:00 2001 From: kushalkolar Date: Sun, 21 May 2023 23:59:55 -0400 Subject: [PATCH 4/5] rectangle region selector basic functionality works --- fastplotlib/graphics/selectors/__init__.py | 1 + .../graphics/selectors/_base_selector.py | 8 +-- fastplotlib/graphics/selectors/_linear.py | 2 +- .../graphics/selectors/_linear_region.py | 14 +--- .../graphics/selectors/_rectangle_region.py | 66 ++++++++++++++++--- fastplotlib/graphics/selectors/_sync.py | 6 +- 6 files changed, 69 insertions(+), 28 deletions(-) diff --git a/fastplotlib/graphics/selectors/__init__.py b/fastplotlib/graphics/selectors/__init__.py index c67e15e40..8ebcaf053 100644 --- a/fastplotlib/graphics/selectors/__init__.py +++ b/fastplotlib/graphics/selectors/__init__.py @@ -1,3 +1,4 @@ from ._linear import LinearSelector from ._linear_region import LinearRegionSelector +from ._rectangle_region import RectangleRegionSelector from ._sync import Synchronizer diff --git a/fastplotlib/graphics/selectors/_base_selector.py b/fastplotlib/graphics/selectors/_base_selector.py index 3a8d51577..165b322da 100644 --- a/fastplotlib/graphics/selectors/_base_selector.py +++ b/fastplotlib/graphics/selectors/_base_selector.py @@ -207,14 +207,14 @@ def _move(self, ev): self.delta = world_pos.clone().sub(self._move_info.last_position) self._pygfx_event = ev - self._move_graphic(self.delta, ev) + self._move_graphic(self.delta) # update last position self._move_info.last_position = world_pos self._plot_area.controller.enabled = True - def _move_graphic(self, delta, ev): + def _move_graphic(self, delta): raise NotImplementedError("Must be implemented in subclass") def _move_end(self, ev): @@ -248,7 +248,7 @@ def _move_to_pointer(self, ev): else: self._move_info = MoveInfo(last_position=current_position, source=self._edges[0]) - self._move_graphic(self.delta, ev) + self._move_graphic(self.delta) self._move_info = None def _pointer_enter(self, ev): @@ -286,7 +286,7 @@ def _key_hold(self): self._move_info = MoveInfo(last_position=None, source=self._edges[0]) # move the graphic - self._move_graphic(delta=delta, ev=None) + self._move_graphic(delta=delta) self._move_info = None diff --git a/fastplotlib/graphics/selectors/_linear.py b/fastplotlib/graphics/selectors/_linear.py index ada04a535..cb3b7ec27 100644 --- a/fastplotlib/graphics/selectors/_linear.py +++ b/fastplotlib/graphics/selectors/_linear.py @@ -344,7 +344,7 @@ def _get_selected_index(self, graphic): index = self.selection() - offset return int(index) - def _move_graphic(self, delta: Vector3, ev): + def _move_graphic(self, delta: Vector3): """ Moves the graphic diff --git a/fastplotlib/graphics/selectors/_linear_region.py b/fastplotlib/graphics/selectors/_linear_region.py index c62d60adf..142f40677 100644 --- a/fastplotlib/graphics/selectors/_linear_region.py +++ b/fastplotlib/graphics/selectors/_linear_region.py @@ -435,8 +435,7 @@ def get_selected_indices(self, graphic: Graphic = None) -> Union[np.ndarray, Lis ixs = np.arange(*self.bounds(), dtype=int) return ixs - def _move_graphic(self, delta, ev): - # edge-0 bound current world position + def _move_graphic(self, delta): if self.bounds.axis == "x": # new left bound position bound_pos_0 = Vector3(self.bounds()[0]).add(delta) @@ -450,17 +449,8 @@ def _move_graphic(self, delta, ev): # new top bound position bound_pos_1 = Vector3(0, self.bounds()[1]).add(delta) - # workaround because transparent objects are not pickable in pygfx - if ev is not None: - if 2 in ev.buttons: - force_fill_move = True - else: - force_fill_move = False - else: - force_fill_move = False - # move entire selector if source was fill - if self._move_info.source == self.fill or force_fill_move: + if self._move_info.source == self.fill: bound0 = getattr(bound_pos_0, self.bounds.axis) bound1 = getattr(bound_pos_1, self.bounds.axis) # set the new bounds diff --git a/fastplotlib/graphics/selectors/_rectangle_region.py b/fastplotlib/graphics/selectors/_rectangle_region.py index 5188161b9..0810329bd 100644 --- a/fastplotlib/graphics/selectors/_rectangle_region.py +++ b/fastplotlib/graphics/selectors/_rectangle_region.py @@ -76,7 +76,7 @@ def _set(self, value: Tuple[float, float, float, float]): # left line z = self._parent.edges[0].geometry.positions.data[:, -1][0] - self._parent.edges[0].geometry.position.data[:] = np.array( + self._parent.edges[0].geometry.positions.data[:] = np.array( [ [xmin, ymin, z], [xmin, ymax, z] @@ -84,7 +84,7 @@ def _set(self, value: Tuple[float, float, float, float]): ) # right line - self._parent.edges[1].geometry.position.data[:] = np.array( + self._parent.edges[1].geometry.positions.data[:] = np.array( [ [xmax, ymin, z], [xmax, ymax, z] @@ -92,7 +92,7 @@ def _set(self, value: Tuple[float, float, float, float]): ) # bottom line - self._parent.edges[2].geometry.position.data[:] = np.array( + self._parent.edges[2].geometry.positions.data[:] = np.array( [ [xmin, ymin, z], [xmax, ymin, z] @@ -100,7 +100,7 @@ def _set(self, value: Tuple[float, float, float, float]): ) # top line - self._parent.edges[3].geometry.position.data[:] = np.array( + self._parent.edges[3].geometry.positions.data[:] = np.array( [ [xmin, ymax, z], [xmax, ymax, z] @@ -283,6 +283,7 @@ def __init__( edge.position.set_z(-1) self.world_object.add(edge) + self._resizable = resizable self._bounds = RectangleBoundsFeature(self, bounds, axis=axis, limits=limits) BaseSelector.__init__( @@ -297,8 +298,57 @@ def __init__( @property def bounds(self) -> RectangleBoundsFeature: """ - The current bounds of the selection in world space. These bounds will NOT necessarily correspond to the - indices of the data that are under the selection. Use ``get_selected_indices()` which maps from - world space to data indices. + (xmin, xmax, ymin, ymax) The current bounds of the selection in world space. + + These bounds will NOT necessarily correspond to the indices of the data that are under the selection. + Use ``get_selected_indices()` which maps from world space to data indices. """ - return self._bounds \ No newline at end of file + return self._bounds + + def _move_graphic(self, delta): + # new left bound position + xmin_new = Vector3(self.bounds()[0]).add(delta).x + + # new right bound position + xmax_new = Vector3(self.bounds()[1]).add(delta).x + + # new bottom bound position + ymin_new = Vector3(0, self.bounds()[2]).add(delta).y + + # new top bound position + ymax_new = Vector3(0, self.bounds()[3]).add(delta).y + + # move entire selector if source was fill + if self._move_info.source == self.fill: + # set the new bounds + self.bounds = (xmin_new, xmax_new, ymin_new, ymax_new) + return + + # if selector is not resizable do nothing + if not self._resizable: + return + + # if resizable, move edges + + xmin, xmax, ymin, ymax = self.bounds() + + # change only left bound + if self._move_info.source == self.edges[0]: + xmin = xmin_new + + # change only right bound + elif self._move_info.source == self.edges[1]: + xmax = xmax_new + + # change only bottom bound + elif self._move_info.source == self.edges[2]: + ymin = ymin_new + + # change only top bound + elif self._move_info.source == self.edges[3]: + ymax = ymax_new + else: + return + + # set the new bounds + self.bounds = (xmin, xmax, ymin, ymax) diff --git a/fastplotlib/graphics/selectors/_sync.py b/fastplotlib/graphics/selectors/_sync.py index d697b9d07..385f2cea1 100644 --- a/fastplotlib/graphics/selectors/_sync.py +++ b/fastplotlib/graphics/selectors/_sync.py @@ -68,18 +68,18 @@ def _handle_event(self, ev): return if delta is not None: - self._move_selectors(source, delta, ev) + self._move_selectors(source, delta) self.block_event = False - def _move_selectors(self, source, delta, ev): + def _move_selectors(self, source, delta): for s in self.selectors: # must use == and not is to compare Graphics because they are weakref proxies! if s == source: # if it's the source, since it has already movied continue - s._move_graphic(delta, ev) + s._move_graphic(delta) def __del__(self): for s in self.selectors: From fb27dc1f99d2411daf4f396550533fd3fa0d03b9 Mon Sep 17 00:00:00 2001 From: kushalkolar Date: Mon, 22 May 2023 00:16:12 -0400 Subject: [PATCH 5/5] more rectangle region stuff --- .../graphics/selectors/_rectangle_region.py | 53 ++++++++++--------- 1 file changed, 29 insertions(+), 24 deletions(-) diff --git a/fastplotlib/graphics/selectors/_rectangle_region.py b/fastplotlib/graphics/selectors/_rectangle_region.py index 0810329bd..6332f9bcc 100644 --- a/fastplotlib/graphics/selectors/_rectangle_region.py +++ b/fastplotlib/graphics/selectors/_rectangle_region.py @@ -71,6 +71,8 @@ def _set(self, value: Tuple[float, float, float, float]): # change the edge lines + # each edge line is defined by two end points which are stored in the + # geometry.positions # [x0, y0, z0] # [x1, y1, z0] @@ -118,31 +120,34 @@ def _set(self, value: Tuple[float, float, float, float]): # calls any events self._feature_changed(key=None, new_data=value) + # TODO: feature_changed def _feature_changed(self, key: Union[int, slice, Tuple[slice]], new_data: Any): return - - if len(self._event_handlers) < 1: - return - - if self._parent.parent is not None: - selected_ixs = self._parent.get_selected_indices() - selected_data = self._parent.get_selected_data() - else: - selected_ixs = None - selected_data = None - - pick_info = { - "index": None, - "collection-index": self._collection_index, - "world_object": self._parent.world_object, - "new_data": new_data, - "selected_indices": selected_ixs, - "selected_data": selected_data - } - - event_data = FeatureEvent(type="bounds", pick_info=pick_info) - - self._call_event_handlers(event_data) + # if len(self._event_handlers) < 1: + # return + # + # if self._parent.parent is not None: + # selected_ixs = self._parent.get_selected_indices() + # selected_data = self._parent.get_selected_data() + # else: + # selected_ixs = None + # selected_data = None + # + # pick_info = { + # "index": None, + # "collection-index": self._collection_index, + # "world_object": self._parent.world_object, + # "new_data": new_data, + # "selected_indices": selected_ixs, + # "selected_data": selected_data + # "graphic", + # "delta", + # "pygfx_event" + # } + # + # event_data = FeatureEvent(type="bounds", pick_info=pick_info) + # + # self._call_event_handlers(event_data) class RectangleRegionSelector(Graphic, BaseSelector): @@ -280,7 +285,7 @@ def __init__( # add the edge lines for edge in self.edges: - edge.position.set_z(-1) + edge.position.set(*origin, -1) self.world_object.add(edge) self._resizable = resizable