Example #1
0
    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)
Example #2
0
    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)
Example #3
0
 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
Example #4
0
 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
Example #5
0
    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()
Example #6
0
    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()
Example #7
0
 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
Example #8
0
 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
Example #9
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)
Example #10
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)