def get_color_at_position(self, x, y, ignore_mask=False): """Converts an `x`, `y` position to a color. Ordinarily, this implmentation uses any active mask to limit the colors which can be clicked on. Set `ignore_mask` to disable this added behaviour. """ sup = HueSaturationWheelMixin if ignore_mask or not self.mask_toggle.get_active(): return sup.get_color_at_position(self, x, y) voids = self.get_mask_voids() if not voids: return sup.get_color_at_position(self, x, y) isects = [] for vi, void in enumerate(voids): # If we're inside a void, use the unchanged value if geom.point_in_convex_poly((x, y), void): return sup.get_color_at_position(self, x, y) # If outside, find the nearest point on the nearest void's edge for p1, p2 in geom.pairwise(void): isect = geom.nearest_point_in_segment(p1, p2, (x, y)) if isect is not None: d = math.sqrt((isect[0] - x)**2 + (isect[1] - y)**2) isects.append((d, isect)) # Above doesn't include segment ends, so add those d = math.sqrt((p1[0] - x)**2 + (p1[1] - y)**2) isects.append((d, p1)) # Determine the closest point. if isects: isects.sort() x, y = isects[0][1] return sup.get_color_at_position(self, x, y)
def get_color_at_position(self, x, y, ignore_mask=False): """Converts an `x`, `y` position to a color. Ordinarily, this implmentation uses any active mask to limit the colors which can be clicked on. Set `ignore_mask` to disable this added behaviour. """ sup = HueSaturationWheelMixin if ignore_mask or not self.mask_toggle.get_active(): return sup.get_color_at_position(self, x, y) voids = self.get_mask_voids() if not voids: return sup.get_color_at_position(self, x, y) isects = [] for vi, void in enumerate(voids): # If we're inside a void, use the unchanged value if geom.point_in_convex_poly((x, y), void): return sup.get_color_at_position(self, x, y) # If outside, find the nearest point on the nearest void's edge for p1, p2 in geom.pairwise(void): isect = geom.nearest_point_in_segment(p1, p2, (x, y)) if isect is not None: d = math.sqrt((isect[0]-x)**2 + (isect[1]-y)**2) isects.append((d, isect)) # Above doesn't include segment ends, so add those d = math.sqrt((p1[0]-x)**2 + (p1[1]-y)**2) isects.append((d, p1)) # Determine the closest point. if isects: isects.sort() x, y = isects[0][1] return sup.get_color_at_position(self, x, y)
def _get_zone_at_pos(self, x, y): """Gets the hover zone for a pointer position""" if self._drag_start is not None: return _EditZone.OUTSIDE elif self.viewport_overlay_shapes: points = [] for shape in self.viewport_overlay_shapes: points.extend(shape) model_xy = self.tdw.display_to_model(x, y) viewport_shape = geom.convex_hull(points) if geom.point_in_convex_poly(model_xy, viewport_shape): return _EditZone.INSIDE return _EditZone.OUTSIDE
def __cleanup_mask(self): mask = self.get_mask() # Drop points from all shapes which are not part of the convex hulls. for shape in mask: if len(shape) <= 3: continue points = [self.get_pos_for_color(c) for c in shape] edge_points = geom.convex_hull(points) for col, point in zip(shape, points): if point in edge_points: continue shape.remove(col) # Drop shapes smaller than the minimum size. newmask = [] min_size = self.get_radius() * self.min_shape_size for shape in mask: points = [self.get_pos_for_color(c) for c in shape] void = geom.convex_hull(points) size = self._get_void_size(void) if size >= min_size: newmask.append(shape) mask = newmask # Drop shapes whose points entirely lie within other shapes newmask = [] maskvoids = [ (shape, geom.convex_hull([self.get_pos_for_color(c) for c in shape])) for shape in mask ] for shape1, void1 in maskvoids: shape1_subsumed = True for p1 in void1: p1_subsumed = False for shape2, void2 in maskvoids: if shape1 is shape2: continue if geom.point_in_convex_poly(p1, void2): p1_subsumed = True break if not p1_subsumed: shape1_subsumed = False break if not shape1_subsumed: newmask.append(shape1) mask = newmask self.set_mask(mask) self.queue_draw()
def __cleanup_mask(self): mask = self.get_mask() # Drop points from all shapes which are not part of the convex hulls. for shape in mask: if len(shape) <= 3: continue points = [self.get_pos_for_color(c) for c in shape] edge_points = geom.convex_hull(points) for col, point in zip(shape, points): if point in edge_points: continue shape.remove(col) # Drop shapes smaller than the minimum size. newmask = [] min_size = self.get_radius() * self.min_shape_size for shape in mask: points = [self.get_pos_for_color(c) for c in shape] void = geom.convex_hull(points) size = self._get_void_size(void) if size >= min_size: newmask.append(shape) mask = newmask # Drop shapes whose points entirely lie within other shapes newmask = [] maskvoids = [(shape, geom.convex_hull([self.get_pos_for_color(c) for c in shape])) for shape in mask] for shape1, void1 in maskvoids: shape1_subsumed = True for p1 in void1: p1_subsumed = False for shape2, void2 in maskvoids: if shape1 is shape2: continue if geom.point_in_convex_poly(p1, void2): p1_subsumed = True break if not p1_subsumed: shape1_subsumed = False break if not shape1_subsumed: newmask.append(shape1) mask = newmask self.set_mask(mask) self.queue_draw()
def _button_press_cb(self, widget, event): if not self.show_viewfinder: return False if not self._drag_start and event.button == 1: if self.viewport_overlay_shapes: points = [] for shape in self.viewport_overlay_shapes: points.extend(shape) pmx, pmy = self.tdw.display_to_model(event.x, event.y) cmx, cmy = self._main_tdw.get_center_model_coords() shape = geom.convex_hull(points) if geom.point_in_convex_poly((pmx, pmy), shape): self._drag_start = (cmx, cmy, pmx, pmy) self._set_cursor(self._cursor_drag_active) self._button_pressed = event.button return True
def _motion_notify_cb(self, widget, event): if not self.show_viewfinder: return False pmx, pmy = self.tdw.display_to_model(event.x, event.y) if self._drag_start: cmx0, cmy0, pmx0, pmy0 = self._drag_start dmx, dmy = pmx-pmx0, pmy-pmy0 self._main_tdw.recenter_on_model_coords(cmx0+dmx, cmy0+dmy) self.app.doc.notify_view_changed(prioritize=True) else: cursor = None if self.viewport_overlay_shapes: points = [] for shape in self.viewport_overlay_shapes: points.extend(shape) shape = geom.convex_hull(points) if geom.point_in_convex_poly((pmx, pmy), shape): cursor = self._cursor_drag_ready else: cursor = self._cursor_move_here self._set_cursor(cursor) return True
def __update_active_objects(self, x, y): # Decides what a click or a drag at (x, y) would do, and updates the # mouse cursor and draw state to match. assert self.__drag_func is None self.__active_shape = None self.__active_ctrlpoint = None self.__tmp_new_ctrlpoint = None self.queue_draw() # yes, always # Possible mask void manipulations mask = self.get_mask() for mask_idx in xrange(len(mask)): colors = mask[mask_idx] if len(colors) < 3: continue # If the pointer is near an existing control point, clicking and # dragging will move it. void = [] for col_idx in xrange(len(colors)): col = colors[col_idx] px, py = self.get_pos_for_color(col) dp = math.sqrt((x - px)**2 + (y - py)**2) if dp <= self.__ctrlpoint_grab_radius: mask.remove(colors) mask.insert(0, colors) self.__active_shape = colors self.__active_ctrlpoint = col_idx self.__set_cursor(None) return void.append((px, py)) # If within a certain distance of an edge, dragging will create and # then move a new control point. void = geom.convex_hull(void) for p1, p2 in geom.pairwise(void): isect = geom.nearest_point_in_segment(p1, p2, (x, y)) if isect is not None: ix, iy = isect di = math.sqrt((ix - x)**2 + (iy - y)**2) if di <= self.__ctrlpoint_grab_radius: newcol = self.get_color_at_position(ix, iy) self.__tmp_new_ctrlpoint = newcol mask.remove(colors) mask.insert(0, colors) self.__active_shape = colors self.__set_cursor(None) return # If the mouse is within a mask void, then dragging would move that # shape around within the mask. if geom.point_in_convex_poly((x, y), void): mask.remove(colors) mask.insert(0, colors) self.__active_shape = colors self.__set_cursor(None) return # Away from shapes, clicks and drags manipulate the entire mask: adding # cutout voids to it, or rotating the whole mask around its central # axis. alloc = self.get_allocation() cx, cy = self.get_center(alloc=alloc) radius = self.get_radius(alloc=alloc) dx, dy = x - cx, y - cy r = math.sqrt(dx**2 + dy**2) if r < radius * (1.0 - self.min_shape_size): if len(mask) < self.__max_num_shapes: d = self.__dist_to_nearest_shape(x, y) minsize = radius * self.min_shape_size if d is None or d > minsize: # Clicking will result in a new void self.__set_cursor(self.__add_cursor) else: # Click-drag to rotate the entire mask self.__set_cursor(self.__rotate_cursor)
def __update_active_objects(self, x, y): # Decides what a click or a drag at (x, y) would do, and updates the # mouse cursor and draw state to match. assert self.__drag_func is None self.__active_shape = None self.__active_ctrlpoint = None self.__tmp_new_ctrlpoint = None self.queue_draw() # yes, always # Possible mask void manipulations mask = self.get_mask() for mask_idx in xrange(len(mask)): colors = mask[mask_idx] if len(colors) < 3: continue # If the pointer is near an existing control point, clicking and # dragging will move it. void = [] for col_idx in xrange(len(colors)): col = colors[col_idx] px, py = self.get_pos_for_color(col) dp = math.sqrt((x-px)**2 + (y-py)**2) if dp <= self.__ctrlpoint_grab_radius: mask.remove(colors) mask.insert(0, colors) self.__active_shape = colors self.__active_ctrlpoint = col_idx self.__set_cursor(None) return void.append((px, py)) # If within a certain distance of an edge, dragging will create and # then move a new control point. void = geom.convex_hull(void) for p1, p2 in geom.pairwise(void): isect = geom.nearest_point_in_segment(p1, p2, (x, y)) if isect is not None: ix, iy = isect di = math.sqrt((ix-x)**2 + (iy-y)**2) if di <= self.__ctrlpoint_grab_radius: newcol = self.get_color_at_position(ix, iy) self.__tmp_new_ctrlpoint = newcol mask.remove(colors) mask.insert(0, colors) self.__active_shape = colors self.__set_cursor(None) return # If the mouse is within a mask void, then dragging would move that # shape around within the mask. if geom.point_in_convex_poly((x, y), void): mask.remove(colors) mask.insert(0, colors) self.__active_shape = colors self.__set_cursor(None) return # Away from shapes, clicks and drags manipulate the entire mask: adding # cutout voids to it, or rotating the whole mask around its central # axis. alloc = self.get_allocation() cx, cy = self.get_center(alloc=alloc) radius = self.get_radius(alloc=alloc) dx, dy = x-cx, y-cy r = math.sqrt(dx**2 + dy**2) if r < radius*(1.0-self.min_shape_size): if len(mask) < self.__max_num_shapes: d = self.__dist_to_nearest_shape(x, y) minsize = radius * self.min_shape_size if d is None or d > minsize: # Clicking will result in a new void self.__set_cursor(self.__add_cursor) else: # Click-drag to rotate the entire mask self.__set_cursor(self.__rotate_cursor)