Example #1
0
def draw_line(image: np.ndarray,
              line: Line2,
              color: Tuple[float, float, float],
              thickness: int = 1) -> None:
    image_extents = Rect2(pos=(0, 0), size=image.shape[1::-1])

    start_point = line.eval_at(x=image_extents.x0)
    end_point = line.eval_at(x=image_extents.x1)

    if not image_extents.contains_point(start_point):
        if start_point.y < image_extents.y0:
            y_to_eval = image_extents.y0
        else:
            y_to_eval = image_extents.y1
        start_point = line.eval_at(y=y_to_eval)

    if not image_extents.contains_point(end_point):
        if end_point.y < image_extents.y0:
            y_to_eval = image_extents.y0
        else:
            y_to_eval = image_extents.y1
        end_point = line.eval_at(y=y_to_eval)

    cv2.line(image,
             pt1=tuple(start_point.as_type(int)),
             pt2=tuple(end_point.as_type(int)),
             color=color,
             thickness=thickness)
Example #2
0
    def _get_region_clip(self) -> Optional[Rect2]:
        image_size_hint = self._image_acquisition.get_image_size_hint()
        if image_size_hint is None:
            return None

        return Rect2(
            pos=(0, 0),
            size=image_size_hint,
        )
Example #3
0
    def set_background_image(self, image: Optional[np.ndarray]) -> None:
        if image is None:
            self._background_ro.props.pixbuf = None
            return

        self._background_ro.props.pixbuf = pixbuf_from_array(image)

        self._render.props.canvas_size = image.shape[1::-1]
        self._render.viewport_extents = Rect2(position=(0, 0), size=image.shape[1::-1])
Example #4
0
    def _update_dragging_indicator(self,
                                   current_cursor_pos: Vector2[float]) -> None:
        if not self._model.is_defining:
            self.view.bn_dragging.set(None)
            return

        self.view.bn_dragging.set(
            Rect2(
                pt0=self._model.begin_define_pos,
                pt1=current_cursor_pos,
            ))
Example #5
0
    def commit_define(self, end_pos: Vector2[float]) -> None:
        assert self.is_defining
        start_pos = self._begin_define_pos
        self._begin_define_pos = None

        region = Rect2(
            p0=start_pos,
            p1=end_pos,
        ).as_type(int)

        clip = self._bn_clip.get()

        if clip is not None:
            region = Rect2(
                x0=clamp(region.x0, clip.x0, clip.x1),
                y0=clamp(region.y0, clip.y0, clip.y1),
                x1=clamp(region.x1, clip.x0, clip.x1),
                y1=clamp(region.y1, clip.y0, clip.y1),
            )

        if region.w == 0 or region.h == 0:
            return

        self.bn_region.set(region)
Example #6
0
def draw_angle_marker(image: np.ndarray, vertex_pos: Vector2[float],
                      start_angle: float, delta_angle: float, radius: float,
                      color: Tuple[float, float, float]) -> None:
    if not Rect2(pos=(0, 0),
                 size=image.shape[1::-1]).contains_point(vertex_pos):
        # Vertex is outside of the image, ignore.
        return

    end_angle = start_angle + delta_angle

    start_pos = vertex_pos
    delta_pos = radius * Vector2(math.cos(-end_angle), math.sin(-end_angle))
    end_pos = start_pos + delta_pos

    cv2.line(image,
             pt1=tuple(start_pos.as_type(int)),
             pt2=tuple(end_pos.as_type(int)),
             color=color,
             thickness=1)
Example #7
0
class Render(Gtk.DrawingArea, protocol.Render):
    _canvas_size = Vector2(1, 1)
    _viewport_extents = Rect2(position=(0, 0),
                              size=_canvas_size)  # type: Rect2[float]
    _viewport_stretch = protocol.Render.ViewportStretch.FIT

    STYLE = """
        .render {
            border: 1px solid white;
        }
    """

    _STYLE_PROV = Gtk.CssProvider()
    _STYLE_PROV.load_from_data(bytes(STYLE, 'utf-8'))

    class RenderObjectContainer:
        def __init__(self, render_object: protocol.RenderObject,
                     handler_ids: Sequence[int]) -> None:
            self.render_object = render_object
            self.handler_ids = tuple(handler_ids)

    def __init__(self, *, can_focus=True, **options) -> None:
        super().__init__(focus_on_click=True, can_focus=can_focus, **options)
        self._ro_containers = [
        ]  # type: MutableSequence[Render.RenderObjectContainer]

        self.add_events(Gdk.EventMask.POINTER_MOTION_MASK
                        | Gdk.EventMask.BUTTON_PRESS_MASK
                        | Gdk.EventMask.BUTTON_RELEASE_MASK
                        | Gdk.EventMask.FOCUS_CHANGE_MASK
                        | Gdk.EventMask.ENTER_NOTIFY_MASK
                        | Gdk.EventMask.LEAVE_NOTIFY_MASK
                        | Gdk.EventMask.KEY_PRESS_MASK)

        self.get_style_context().add_class('render')
        self.get_style_context().add_provider(self._STYLE_PROV,
                                              Gtk.STYLE_PROVIDER_PRIORITY_USER)

    def do_draw(self, cr: cairo.Context) -> None:
        viewport_widget_extents = self.props.viewport_widget_extents

        with cairo_saved(cr):
            cr.rectangle(*viewport_widget_extents.position,
                         *viewport_widget_extents.size)
            cr.clip()
            for ro in self._render_objects:
                ro.draw(cr)

        if self.has_focus():
            # Draw focus indicator
            stroke_width = 1
            rectangle_pos = viewport_widget_extents.position + (
                stroke_width / 2, stroke_width / 2)
            rectangle_size = viewport_widget_extents.size - (stroke_width,
                                                             stroke_width)
            cr.rectangle(*rectangle_pos, *rectangle_size)
            cr.set_source_rgb(70 / 255, 142 / 255, 220 / 255)
            cr.set_line_width(stroke_width)
            cr.stroke()

    @property
    def _render_objects(self) -> Sequence[protocol.RenderObject]:
        return sorted(
            (container.render_object for container in self._ro_containers),
            key=lambda ro: ro.props.z_index,
        )

    def add_render_object(self, ro: protocol.RenderObject) -> None:
        handler_ids = (ro.connect('request-draw',
                                  lambda _: self.queue_draw()), )
        self._ro_containers.append(
            self.RenderObjectContainer(render_object=ro,
                                       handler_ids=handler_ids))
        ro.set_parent(self)

        self.queue_draw()

    def remove_render_object(self, ro: protocol.RenderObject) -> None:
        container = self._ro_container_from_ro(ro)

        for handler_id in container.handler_ids:
            ro.disconnect(handler_id)
        self._ro_containers.remove(container)

        self.queue_draw()

    def _ro_container_from_ro(
            self, ro: protocol.RenderObject) -> 'Render.RenderObjectContainer':
        for container in self._ro_containers:
            if container.render_object is ro:
                return container
        else:
            raise ValueError('No container found for {}.'.format(ro))

    def do_button_press_event(self, event: Gdk.EventButton) -> None:
        self.emit('cursor-down-event',
                  self._canvas_coord_from_widget(Vector2(event.x, event.y)))

        if self.props.can_focus:
            self.grab_focus()

    def do_button_release_event(self, event: Gdk.EventButton) -> None:
        self.emit('cursor-up-event',
                  self._canvas_coord_from_widget(Vector2(event.x, event.y)))

    def do_motion_notify_event(self, event: Gdk.EventButton) -> None:
        self.emit('cursor-motion-event',
                  self._canvas_coord_from_widget(Vector2(event.x, event.y)))

    def do_key_press_event(self, event: Gdk.EventKey) -> bool:
        if event.keyval == Gdk.KEY_Tab:
            # Allow user to use the tab key to cycle focus to another widget
            return False

        # Stop event propagation
        return True

    # Cursor signals

    @GObject.Signal(arg_types=(object, ))
    def cursor_down_event(self, pos: Vector2[float]) -> None:
        pass

    @GObject.Signal(arg_types=(object, ))
    def cursor_up_event(self, pos: Vector2[float]) -> None:
        pass

    @GObject.Signal(arg_types=(object, ))
    def cursor_motion_event(self, pos: Vector2[float]) -> None:
        pass

    # Coordinate transform functions

    def _widget_coord_from_canvas(
            self, coord_canvas: Tuple[float, float]) -> Vector2[float]:
        coord_canvas = Vector2(*coord_canvas)

        viewport_extents = self.props.viewport_extents
        viewport_widget_extents = self.props.viewport_widget_extents

        coord_viewport = coord_canvas - viewport_extents.position
        coord_viewport_pct = Vector2(
            coord_viewport.x / viewport_extents.size.x,
            coord_viewport.y / viewport_extents.size.y)
        coord_viewport_widget = Vector2(
            coord_viewport_pct.x * viewport_widget_extents.size.x,
            coord_viewport_pct.y * viewport_widget_extents.size.y)
        coord_widget = viewport_widget_extents.position + coord_viewport_widget

        return coord_widget

    def _canvas_coord_from_widget(
            self, coord_widget: Tuple[float, float]) -> Vector2[float]:
        coord_widget = Vector2(*coord_widget)

        viewport_extents = self.props.viewport_extents
        viewport_widget_extents = self.props.viewport_widget_extents

        coord_viewport_widget = coord_widget - viewport_widget_extents.position
        coord_viewport_pct = Vector2(
            coord_viewport_widget.x / viewport_widget_extents.size.x,
            coord_viewport_widget.y / viewport_widget_extents.size.y)
        coord_viewport = Vector2(
            coord_viewport_pct.x * viewport_extents.size.x,
            coord_viewport_pct.y * viewport_extents.size.y)
        coord_canvas = viewport_extents.position + coord_viewport

        return coord_canvas

    def _widget_dist_from_canvas(
            self, dist_canvas: Tuple[float, float]) -> Vector2[float]:
        dist_canvas = Vector2(*dist_canvas)

        viewport_size = self.props.viewport_extents.size
        viewport_widget_size = self.props.viewport_widget_extents.size

        scale = Vector2(viewport_widget_size.x / viewport_size.x,
                        viewport_widget_size.y / viewport_size.y)
        dist_widget = Vector2(scale.x * dist_canvas.x, scale.y * dist_canvas.y)

        return dist_widget

    def _canvas_dist_from_widget(
            self, dist_widget: Tuple[float, float]) -> Vector2[float]:
        dist_widget = Vector2(*dist_widget)

        viewport_size = self.props.viewport_extents.size
        viewport_widget_size = self.props.viewport_widget_extents.size

        scale = Vector2(viewport_size.x / viewport_widget_size.x,
                        viewport_size.y / viewport_widget_size.y)
        dist_canvas = Vector2(scale.x * dist_widget.x, scale.y * dist_widget.y)

        return dist_canvas

    # Canvas geometry properties

    @GObject.Property
    def canvas_size(self) -> Vector2[float]:
        return self._canvas_size

    @canvas_size.setter
    def canvas_size(self, new_size: Vector2[float]) -> None:
        self._canvas_size = new_size
        self.queue_draw()

    @GObject.Property
    def viewport_extents(self) -> Rect2[float]:
        return self._viewport_extents

    @viewport_extents.setter
    def viewport_extents(self, new_extents: Rect2[float]) -> None:
        self._viewport_extents = new_extents
        self.queue_draw()

    @GObject.Property
    def viewport_widget_extents(self) -> Rect2[float]:
        return Rect2(position=self._viewport_widget_pos,
                     size=self._viewport_widget_size)

    @property
    def _viewport_widget_size(self) -> Vector2[float]:
        widget_size = Vector2(self.get_allocated_width(),
                              self.get_allocated_height())
        return self._calculate_stretch_size(
            stretch=self._viewport_stretch,
            reference_size=widget_size,
            child_size=self._viewport_extents.size)

    @property
    def _viewport_widget_pos(self) -> Vector2[float]:
        widget_size = Vector2(self.get_allocated_width(),
                              self.get_allocated_height())
        return self._calculate_offset_for_centred_rectangles(
            reference_size=widget_size, child_size=self._viewport_widget_size)

    @classmethod
    def _calculate_stretch_size(cls, stretch: protocol.Render.ViewportStretch,
                                reference_size: Vector2[float],
                                child_size: Vector2[float]) -> Vector2[float]:
        reference_aspect = reference_size[0] / reference_size[1]
        child_aspect = child_size[0] / child_size[1]

        if stretch is cls.ViewportStretch.FILL and (reference_aspect > child_aspect) \
                or stretch is cls.ViewportStretch.FIT and (reference_aspect <= child_aspect):
            scale_factor = reference_size[0] / child_size[0]
        else:
            scale_factor = reference_size[1] / child_size[1]

        return child_size * scale_factor

    @staticmethod
    def _calculate_offset_for_centred_rectangles(reference_size: Vector2[float], child_size: Vector2[float]) \
            -> Vector2[float]:
        offset = reference_size / 2 - child_size / 2
        return offset
Example #8
0
 def viewport_widget_extents(self) -> Rect2[float]:
     return Rect2(position=self._viewport_widget_pos,
                  size=self._viewport_widget_size)