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 __dist_to_nearest_shape(self, x, y): # Distance from `x`, `y` to the nearest edge or vertex of any shape. dists = [] for hull in self.get_mask_voids(): # cx, cy = geom.poly_centroid(hull) for p1, p2 in geom.pairwise(hull): np = geom.nearest_point_in_segment(p1, p2, (x, y)) if np is not None: nx, ny = np d = math.sqrt((x - nx)**2 + (y - ny)**2) dists.append(d) # Segment end too d = math.sqrt((p1[0] - x)**2 + (p1[1] - y)**2) dists.append(d) if not dists: return None dists.sort() return dists[0]
def __dist_to_nearest_shape(self, x, y): # Distance from `x`, `y` to the nearest edge or vertex of any shape. dists = [] for hull in self.get_mask_voids(): # cx, cy = geom.poly_centroid(hull) for p1, p2 in geom.pairwise(hull): nearest_point = geom.nearest_point_in_segment(p1, p2, (x, y)) if nearest_point is not None: nx, ny = nearest_point d = math.sqrt((x-nx)**2 + (y-ny)**2) dists.append(d) # Segment end too d = math.sqrt((p1[0]-x)**2 + (p1[1]-y)**2) dists.append(d) if not dists: return None dists.sort() return dists[0]
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)