예제 #1
0
 def _update_on_boundaries_change(self, *args):
     zoom_pos = Vector(self.zoom_pos)
     pos = self._old_zoom_pos_limit[0]
     top_right = self._old_zoom_pos_limit[1] - Vector(self.size)
     hint = (zoom_pos - pos).safe_div(top_right - pos)
     self._old_zoom_pos_limit = self._get_zoom_pos_limit()
     self.set_size(Vector(self.size))
     self._set_zoom_pos_from_hint(hint)
     self.set_pos(Vector(self.zoom_pos))
예제 #2
0
 def _get_zoom_pos_limit(self):
     tz_size = self.uvs_size * self.texture_size
     origin = tz_size * 0.5
     offset = -origin
     offset *= 1.0 / self.zoom
     offset += origin
     offset = offset.safe_div(tz_size) * Vector(self.size)
     scaled_pos = Vector(self.pos_limit[:2]) - offset
     scaled_top_left = scaled_pos + self.container_size + 2 * offset
     return [scaled_pos, scaled_top_left]
예제 #3
0
 def on_touch_move(self, touch):
     if touch.grab_current is self:
         if touch.ud[id(self)].get('zooming', False):
             return True
         elif touch.ud[id(self)].get('resizing', False):
             return super(ZoomBox, self).on_touch_move(touch)
         else:
             zoom_pos = Vector(self.zoom_pos) + Vector(touch.dx, touch.dy)
             self._set_zoom_pos(zoom_pos)
             self.set_pos(Vector(self.zoom_pos))
             return True
     return super(ZoomBox, self).on_touch_move(touch)
예제 #4
0
 def _update_uvs_model(self):
     uvs_size = self.uvs_size
     uvs_origin = uvs_size * 0.5
     uvs = [
         0.0, uvs_size[1], uvs_size[0], uvs_size[1], uvs_size[0], 0.0, 0.0,
         0.0
     ]
     for i in range(0, 8, 2):
         temp = Vector(uvs[i], uvs[i + 1])
         temp -= uvs_origin
         temp *= 1.0 / self.zoom
         temp = temp.rotate(self.angle)
         temp += uvs_origin
         uvs[i] = temp.x
         uvs[i + 1] = temp.y
     self._uvs = uvs
예제 #5
0
 def _compute_uvs_pos(self):
     cont_size = self.container_size
     size = Vector(self.size)
     tex_size = self.texture_size
     zoom_local_pos = Vector(self.zoom_pos) - Vector(self.pos_limit[:2])
     allowed_size = cont_size - size
     dp_pos = zoom_local_pos.safe_div(allowed_size)
     dp_pos.y = 1.0 - dp_pos.y if allowed_size.y != 0 else 0.0
     tex_zoom_size = tex_size.safe_div(cont_size) * size
     allowed_uvs_size = (tex_size - tex_zoom_size).safe_div(tex_size)
     pos = dp_pos * allowed_uvs_size
     if self.angle % 360 != 0:
         uvs_origin = allowed_uvs_size * 0.5
         pos -= uvs_origin
         pos = pos.rotate(self.angle)
         pos += uvs_origin
     return pos
예제 #6
0
 def set_size_and_pos(self, new_size, new_pos):
     self.set_size(new_size)
     if self._resizing:
         self.set_pos(new_pos)
         self._set_zoom_pos(new_pos)
     else:
         # Dragging the zoom box
         self.set_pos(Vector(self.zoom_pos))
예제 #7
0
 def __init__(self, **kwargs):
     pos_limit = [0.0, 0.0, self.default_size[0], self.default_size[1]]
     kwargs.setdefault('pos_limit', pos_limit)
     self._resizing = False
     self._uvs = None
     self._flag_uvs_model = True
     self._old_zoom_pos_limit = [
         Vector(pos_limit[:2]),
         Vector(pos_limit[2:])
     ]
     fbind = self.fbind
     fbind('center', self._trigger_update_callback)
     fbind('zoom_pos', self._trigger_update_callback)
     fbind('container_size', self._set_flag_uvs)
     fbind('size', self._set_flag_uvs)
     fbind('texture', self._set_flag_uvs)
     fbind('zoom', self._set_flag_uvs)
     fbind('angle', self._set_flag_uvs)
     fbind('disabled', self._set_flag_uvs)
     super(ZoomBox, self).__init__(**kwargs)
     self._old_zoom_pos_limit = self._get_zoom_pos_limit()
예제 #8
0
 def on_touch_down(self, touch):
     if self.disabled and self.collide_point(*touch.pos):
         return False
     if self.collide_point(*touch.pos):
         heading = 0.0
         if touch.button == 'scrollup':
             heading = -1.0
         elif touch.button == 'scrolldown':
             heading = 1.0
         if heading:
             touch.ud[id(self)] = {'zooming': True}
             touch.grab(self)
             self.zoom = min(self.zoom_max,
                             max(1.0, self.zoom + heading * self.zoom_step))
             self._set_zoom_pos(Vector(self.zoom_pos))
             return True
     value = super(ZoomBox, self).on_touch_down(touch)
     if value and touch.ud[id(self)].get('resizing', False):
         self._resizing = True
     return value
예제 #9
0
 def on_touch_move(self, touch):
     if touch.grab_current is not self:
         return False
     uid = id(self)
     if touch.ud[uid]['resizing']:
         origin = touch.ud[uid]['origin']
         heading = touch.ud[uid]['heading']
         delta = Vector(touch.pos) - origin
         pos = heading * (origin + delta)
         pos += (Vector.ones() - heading) * (origin + Vector(self.size))
         self.set_size_and_pos(abs(pos - origin), Vector.min(pos, origin))
         self.dispatch('on_resize')
         return True
     else:
         self.set_pos(Vector(self.pos) + Vector(touch.dx, touch.dy))
         self.dispatch('on_drag')
         return True
예제 #10
0
class CropBox(Widget):

    border_color = ListProperty([1, 0, 0, 1.0])
    border_width = NumericProperty('1dp')
    border_collide_width_min = NumericProperty('10dp')
    pos_limit = ListProperty(
        [-float('inf'), -float('inf'),
         float('inf'),
         float('inf')])
    size_min = ListProperty([dp(10), dp(10)])
    default_size = ListProperty([dp(120), dp(120)])
    default_pos_hint = ListProperty([0.5, 0.5])

    border_width_modifier = AliasProperty(lambda self: 2.0
                                          if self.border_width > 1.0 else 1.0,
                                          None,
                                          bind=['border_width'],
                                          cache=True)

    displayed_border_width = AliasProperty(
        lambda self: self.border_width * self.border_width_modifier,
        None,
        bind=['border_width', 'border_width_modifier'],
        cache=True)

    def _get_current_collide_border_width(self):
        return max(self.displayed_border_width, self.border_collide_width_min)

    current_collide_border_width = AliasProperty(
        _get_current_collide_border_width,
        None,
        bind=['displayed_border_width', 'border_collide_width_min'],
        cache=True)

    container_size = AliasProperty(
        lambda self: Vector(self.pos_limit[2:]) - Vector(self.pos_limit[:2]),
        None,
        bind=['pos_limit'],
        cache=True)

    __events__ = ('on_drag_start', 'on_drag', 'on_drag_stop',
                  'on_resize_start', 'on_resize', 'on_resize_stop')

    def __init__(self, **kwargs):
        kwargs.setdefault('size_hint', (None, None))
        self._first_init = True
        self.trigger_update = trigger = Clock.create_trigger(self._update, -1)
        self.fbind('pos_limit', self._update_on_boundaries_change)
        super(CropBox, self).__init__(**kwargs)
        trigger()

    def _update(self, *args):
        if self._first_init:
            self.set_defaults()
            self._first_init = False

    def _update_on_boundaries_change(self, *args):
        self.set_size_and_pos(Vector(self.size), Vector(self.pos))

    def set_defaults(self):
        self.set_size(Vector(self.default_size))
        hint = Vector(self.default_pos_hint)
        pos = (self.container_size - Vector(self.size)) * hint
        pos += Vector(self.pos_limit[:2])
        self.set_pos(pos)

    def set_size_and_pos(self, new_size, new_pos):
        self.set_size(new_size)
        self.set_pos(new_pos)

    def set_size(self, new_size):
        self.size[:] = Vector.clamp(
            new_size, Vector(self.size_min),
            Vector(self.pos_limit[2:]) - Vector(self.pos_limit[:2]))

    def set_pos(self, new_pos):
        self.pos[:] = Vector.clamp(
            new_pos, Vector(self.pos_limit[:2]),
            Vector(self.pos_limit[2:]) - Vector(self.size))

    def collide_with_inner_box(self, x, y):
        width = Vector.ones() * self.current_collide_border_width
        pos = Vector(self.pos)
        return Vector.in_bbox((x, y), pos + width,
                              pos + Vector(self.size) - width)

    def on_touch_down(self, touch):
        if self.disabled and self.collide_point(*touch.pos):
            return True
        data = {'resizing': False}
        in_outer = self.collide_point(*touch.pos)
        if in_outer and not self.collide_with_inner_box(*touch.pos):
            origin, heading = self._find_origin_point_and_heading(touch)
            data['resizing'] = True
            data['origin'] = origin
            data['heading'] = heading
            touch.ud[id(self)] = data
            touch.grab(self)
            self.dispatch('on_resize_start')
            return True
        if in_outer:
            touch.ud[id(self)] = data
            touch.grab(self)
            self.dispatch('on_drag_start')
            return True
        return False

    def on_touch_move(self, touch):
        if touch.grab_current is not self:
            return False
        uid = id(self)
        if touch.ud[uid]['resizing']:
            origin = touch.ud[uid]['origin']
            heading = touch.ud[uid]['heading']
            delta = Vector(touch.pos) - origin
            pos = heading * (origin + delta)
            pos += (Vector.ones() - heading) * (origin + Vector(self.size))
            self.set_size_and_pos(abs(pos - origin), Vector.min(pos, origin))
            self.dispatch('on_resize')
            return True
        else:
            self.set_pos(Vector(self.pos) + Vector(touch.dx, touch.dy))
            self.dispatch('on_drag')
            return True

    def on_touch_up(self, touch):
        if touch.grab_current is self:
            touch.ungrab(self)
            if touch.ud[id(self)].get('resizing', False):
                self.dispatch('on_resize_stop')
            else:
                self.dispatch('on_drag_stop')
            return True

    def _find_origin_point_and_heading(self, touch):
        pos = Vector(self.pos)
        size = Vector(self.size)
        width = self.current_collide_border_width
        top_right = pos + size
        if pos.x <= touch.x <= pos.x + width:
            if pos.y <= touch.y <= pos.y + width:
                return pos + size, Vector.ones()
            elif top_right.y - width <= touch.y <= top_right.y:
                return Vector(pos.x + size.x, pos.y), Vector.ones()
        if top_right.x - width <= touch.x <= top_right.x:
            if pos.y <= touch.y <= pos.y + width:
                return Vector(pos.x, pos.y + size.y), Vector.ones()
            elif top_right.y - width <= touch.y <= top_right.y:
                return Vector(pos), Vector.ones()
        if pos.x + width <= touch.x <= top_right.x - width:
            if pos.y <= touch.y <= pos.y + width:
                return Vector(pos.x, pos.y + size.y), Vector(0.0, 1.0)
            elif top_right.y - width <= touch.y <= top_right.y:
                return Vector(pos), Vector(0.0, 1.0)
        if pos.y + width <= touch.y <= top_right.y - width:
            if pos.x <= touch.x <= pos.x + width:
                return Vector(pos.x + size.x, pos.y), Vector(1.0, 0.0)
            elif top_right.x - width <= touch.x <= top_right.x:
                return Vector(pos), Vector(1.0, 0.0)

    def on_drag_start(self):
        pass

    def on_drag(self):
        pass

    def on_drag_stop(self):
        pass

    def on_resize_start(self):
        pass

    def on_resize(self):
        pass

    def on_resize_stop(self):
        pass
예제 #11
0
 def set_pos(self, new_pos):
     self.pos[:] = Vector.clamp(
         new_pos, Vector(self.pos_limit[:2]),
         Vector(self.pos_limit[2:]) - Vector(self.size))
예제 #12
0
 def set_defaults(self):
     self.set_size(Vector(self.default_size))
     hint = Vector(self.default_pos_hint)
     pos = (self.container_size - Vector(self.size)) * hint
     pos += Vector(self.pos_limit[:2])
     self.set_pos(pos)
예제 #13
0
class ZoomBox(CropBox):

    angle = NumericProperty(0.0)
    zoom = NumericProperty(2.0)
    zoom_max = NumericProperty(5.0)
    zoom_step = NumericProperty(0.5)
    zoom_pos = ListProperty([0.0, 0.0])
    texture = ObjectProperty(None, allownone=True)

    texture_size = AliasProperty(lambda self: Vector(self.texture.size)
                                 if self.texture else Vector(),
                                 None,
                                 bind=['texture'],
                                 cache=True)

    def _get_uvs_size(self):
        tex_size = self.texture_size
        tz_size = tex_size.safe_div(self.container_size) * Vector(self.size)
        return tz_size.safe_div(tex_size)

    uvs_size = AliasProperty(_get_uvs_size,
                             None,
                             bind=['container_size', 'texture_size', 'size'],
                             cache=True)

    _vertices = ListProperty(
        create_mesh_vertices((0, 0), (100, 100), DEFAULT_UVS))

    def __init__(self, **kwargs):
        pos_limit = [0.0, 0.0, self.default_size[0], self.default_size[1]]
        kwargs.setdefault('pos_limit', pos_limit)
        self._resizing = False
        self._uvs = None
        self._flag_uvs_model = True
        self._old_zoom_pos_limit = [
            Vector(pos_limit[:2]),
            Vector(pos_limit[2:])
        ]
        fbind = self.fbind
        fbind('center', self._trigger_update_callback)
        fbind('zoom_pos', self._trigger_update_callback)
        fbind('container_size', self._set_flag_uvs)
        fbind('size', self._set_flag_uvs)
        fbind('texture', self._set_flag_uvs)
        fbind('zoom', self._set_flag_uvs)
        fbind('angle', self._set_flag_uvs)
        fbind('disabled', self._set_flag_uvs)
        super(ZoomBox, self).__init__(**kwargs)
        self._old_zoom_pos_limit = self._get_zoom_pos_limit()

    def set_defaults(self):
        self.set_size(Vector(self.default_size))
        self._set_zoom_pos_from_hint(Vector(self.default_pos_hint))
        self.set_pos(Vector(self.zoom_pos))

    def on_touch_down(self, touch):
        if self.disabled and self.collide_point(*touch.pos):
            return False
        if self.collide_point(*touch.pos):
            heading = 0.0
            if touch.button == 'scrollup':
                heading = -1.0
            elif touch.button == 'scrolldown':
                heading = 1.0
            if heading:
                touch.ud[id(self)] = {'zooming': True}
                touch.grab(self)
                self.zoom = min(self.zoom_max,
                                max(1.0, self.zoom + heading * self.zoom_step))
                self._set_zoom_pos(Vector(self.zoom_pos))
                return True
        value = super(ZoomBox, self).on_touch_down(touch)
        if value and touch.ud[id(self)].get('resizing', False):
            self._resizing = True
        return value

    def on_touch_move(self, touch):
        if touch.grab_current is self:
            if touch.ud[id(self)].get('zooming', False):
                return True
            elif touch.ud[id(self)].get('resizing', False):
                return super(ZoomBox, self).on_touch_move(touch)
            else:
                zoom_pos = Vector(self.zoom_pos) + Vector(touch.dx, touch.dy)
                self._set_zoom_pos(zoom_pos)
                self.set_pos(Vector(self.zoom_pos))
                return True
        return super(ZoomBox, self).on_touch_move(touch)

    def on_touch_up(self, touch):
        if touch.grab_current is self \
                and touch.ud[id(self)].get('resizing', False):
            self._resizing = False
        return super(ZoomBox, self).on_touch_up(touch)

    def _trigger_update_callback(self, *args):
        self.trigger_update()

    def _update(self, *args):
        super(ZoomBox, self)._update(*args)
        self._update_vertices()

    def _set_flag_uvs(self, *args):
        self._flag_uvs_model = True
        self.trigger_update()

    def _set_zoom_pos_from_hint(self, hint):
        zoom_pos_limit = self._get_zoom_pos_limit()
        pos = zoom_pos_limit[0]
        top_right = zoom_pos_limit[1] - Vector(self.size)
        zoom_pos = (top_right - pos) * hint + pos
        self._set_zoom_pos(zoom_pos)

    def _update_on_boundaries_change(self, *args):
        zoom_pos = Vector(self.zoom_pos)
        pos = self._old_zoom_pos_limit[0]
        top_right = self._old_zoom_pos_limit[1] - Vector(self.size)
        hint = (zoom_pos - pos).safe_div(top_right - pos)
        self._old_zoom_pos_limit = self._get_zoom_pos_limit()
        self.set_size(Vector(self.size))
        self._set_zoom_pos_from_hint(hint)
        self.set_pos(Vector(self.zoom_pos))

    def set_size_and_pos(self, new_size, new_pos):
        self.set_size(new_size)
        if self._resizing:
            self.set_pos(new_pos)
            self._set_zoom_pos(new_pos)
        else:
            # Dragging the zoom box
            self.set_pos(Vector(self.zoom_pos))

    def _set_zoom_pos(self, new_zoom_pos):
        zoom_pos_limit = self._get_zoom_pos_limit()
        self.zoom_pos[:] = Vector.clamp(new_zoom_pos, zoom_pos_limit[0],
                                        zoom_pos_limit[1] - Vector(self.size))

    def _get_zoom_pos_limit(self):
        tz_size = self.uvs_size * self.texture_size
        origin = tz_size * 0.5
        offset = -origin
        offset *= 1.0 / self.zoom
        offset += origin
        offset = offset.safe_div(tz_size) * Vector(self.size)
        scaled_pos = Vector(self.pos_limit[:2]) - offset
        scaled_top_left = scaled_pos + self.container_size + 2 * offset
        return [scaled_pos, scaled_top_left]

    def _update_vertices(self, *args):
        if self.disabled:
            return
        uvs_pos = self._compute_uvs_pos()
        if self._flag_uvs_model:
            self._update_uvs_model()
            self._flag_uvs_model = False
        uvs = self._uvs[:]
        for i in range(0, 8, 2):
            uvs[i] = uvs[i] + uvs_pos.x
            uvs[i + 1] = uvs[i + 1] + uvs_pos.y
        self._vertices[:] = create_mesh_vertices(self.pos, self.size, uvs)

    def _update_uvs_model(self):
        uvs_size = self.uvs_size
        uvs_origin = uvs_size * 0.5
        uvs = [
            0.0, uvs_size[1], uvs_size[0], uvs_size[1], uvs_size[0], 0.0, 0.0,
            0.0
        ]
        for i in range(0, 8, 2):
            temp = Vector(uvs[i], uvs[i + 1])
            temp -= uvs_origin
            temp *= 1.0 / self.zoom
            temp = temp.rotate(self.angle)
            temp += uvs_origin
            uvs[i] = temp.x
            uvs[i + 1] = temp.y
        self._uvs = uvs

    def _compute_uvs_pos(self):
        cont_size = self.container_size
        size = Vector(self.size)
        tex_size = self.texture_size
        zoom_local_pos = Vector(self.zoom_pos) - Vector(self.pos_limit[:2])
        allowed_size = cont_size - size
        dp_pos = zoom_local_pos.safe_div(allowed_size)
        dp_pos.y = 1.0 - dp_pos.y if allowed_size.y != 0 else 0.0
        tex_zoom_size = tex_size.safe_div(cont_size) * size
        allowed_uvs_size = (tex_size - tex_zoom_size).safe_div(tex_size)
        pos = dp_pos * allowed_uvs_size
        if self.angle % 360 != 0:
            uvs_origin = allowed_uvs_size * 0.5
            pos -= uvs_origin
            pos = pos.rotate(self.angle)
            pos += uvs_origin
        return pos
예제 #14
0
 def _set_zoom_pos(self, new_zoom_pos):
     zoom_pos_limit = self._get_zoom_pos_limit()
     self.zoom_pos[:] = Vector.clamp(new_zoom_pos, zoom_pos_limit[0],
                                     zoom_pos_limit[1] - Vector(self.size))
예제 #15
0
 def _find_origin_point_and_heading(self, touch):
     pos = Vector(self.pos)
     size = Vector(self.size)
     width = self.current_collide_border_width
     top_right = pos + size
     if pos.x <= touch.x <= pos.x + width:
         if pos.y <= touch.y <= pos.y + width:
             return pos + size, Vector.ones()
         elif top_right.y - width <= touch.y <= top_right.y:
             return Vector(pos.x + size.x, pos.y), Vector.ones()
     if top_right.x - width <= touch.x <= top_right.x:
         if pos.y <= touch.y <= pos.y + width:
             return Vector(pos.x, pos.y + size.y), Vector.ones()
         elif top_right.y - width <= touch.y <= top_right.y:
             return Vector(pos), Vector.ones()
     if pos.x + width <= touch.x <= top_right.x - width:
         if pos.y <= touch.y <= pos.y + width:
             return Vector(pos.x, pos.y + size.y), Vector(0.0, 1.0)
         elif top_right.y - width <= touch.y <= top_right.y:
             return Vector(pos), Vector(0.0, 1.0)
     if pos.y + width <= touch.y <= top_right.y - width:
         if pos.x <= touch.x <= pos.x + width:
             return Vector(pos.x + size.x, pos.y), Vector(1.0, 0.0)
         elif top_right.x - width <= touch.x <= top_right.x:
             return Vector(pos), Vector(1.0, 0.0)
예제 #16
0
 def _update_on_boundaries_change(self, *args):
     self.set_size_and_pos(Vector(self.size), Vector(self.pos))
예제 #17
0
 def _get_uvs_size(self):
     tex_size = self.texture_size
     tz_size = tex_size.safe_div(self.container_size) * Vector(self.size)
     return tz_size.safe_div(tex_size)
예제 #18
0
 def set_size(self, new_size):
     self.size[:] = Vector.clamp(
         new_size, Vector(self.size_min),
         Vector(self.pos_limit[2:]) - Vector(self.pos_limit[:2]))
예제 #19
0
 def set_defaults(self):
     self.set_size(Vector(self.default_size))
     self._set_zoom_pos_from_hint(Vector(self.default_pos_hint))
     self.set_pos(Vector(self.zoom_pos))
예제 #20
0
 def collide_with_inner_box(self, x, y):
     width = Vector.ones() * self.current_collide_border_width
     pos = Vector(self.pos)
     return Vector.in_bbox((x, y), pos + width,
                           pos + Vector(self.size) - width)
예제 #21
0
 def _set_zoom_pos_from_hint(self, hint):
     zoom_pos_limit = self._get_zoom_pos_limit()
     pos = zoom_pos_limit[0]
     top_right = zoom_pos_limit[1] - Vector(self.size)
     zoom_pos = (top_right - pos) * hint + pos
     self._set_zoom_pos(zoom_pos)