def reset(self): self.matrix = Matrix()
def _set_scale(self, scale): rescale = scale * 1.0 / self.scale self.apply_transform(Matrix().scale(rescale, rescale, rescale), post_multiply=True, anchor=self.to_local(*self.center))
def update_projection_matrix(self): m = Matrix() m.perspective(self.fov * 0.5, self.aspect, self.near, self.far) self.projection_matrix = m
def setInitialZoom(self): mat = Matrix().scale(.4, .4, 1) self.scatterInstance.apply_transform(mat, (0,0)) mat = Matrix().translate(200, 100, 0) self.scatterInstance.apply_transform(mat)
class Scatter(Widget): '''Scatter class. See module documentation for more information. :Events: `on_transform_with_touch`: Fired when the scatter has been transformed by user touch or multitouch, such as panning or zooming. `on_bring_to_front`: Fired when the scatter is brought to the front. .. versionchanged:: 1.9.0 Event `on_bring_to_front` added. .. versionchanged:: 1.8.0 Event `on_transform_with_touch` added. ''' __events__ = ('on_transform_with_touch', 'on_bring_to_front') auto_bring_to_front = BooleanProperty(True) '''If True, the widget will be automatically pushed on the top of parent widget list for drawing. :attr:`auto_bring_to_front` is a :class:`~kivy.properties.BooleanProperty` and defaults to True. ''' do_translation_x = BooleanProperty(True) '''Allow translation on the X axis. :attr:`do_translation_x` is a :class:`~kivy.properties.BooleanProperty` and defaults to True. ''' do_translation_y = BooleanProperty(True) '''Allow translation on Y axis. :attr:`do_translation_y` is a :class:`~kivy.properties.BooleanProperty` and defaults to True. ''' def _get_do_translation(self): return (self.do_translation_x, self.do_translation_y) def _set_do_translation(self, value): if type(value) in (list, tuple): self.do_translation_x, self.do_translation_y = value else: self.do_translation_x = self.do_translation_y = bool(value) do_translation = AliasProperty(_get_do_translation, _set_do_translation, bind=('do_translation_x', 'do_translation_y')) '''Allow translation on the X or Y axis. :attr:`do_translation` is an :class:`~kivy.properties.AliasProperty` of (:attr:`do_translation_x` + :attr:`do_translation_y`) ''' translation_touches = BoundedNumericProperty(1, min=1) '''Determine whether translation was triggered by a single or multiple touches. This only has effect when :attr:`do_translation` = True. :attr:`translation_touches` is a :class:`~kivy.properties.NumericProperty` and defaults to 1. .. versionadded:: 1.7.0 ''' do_rotation = BooleanProperty(True) '''Allow rotation. :attr:`do_rotation` is a :class:`~kivy.properties.BooleanProperty` and defaults to True. ''' do_scale = BooleanProperty(True) '''Allow scaling. :attr:`do_scale` is a :class:`~kivy.properties.BooleanProperty` and defaults to True. ''' do_collide_after_children = BooleanProperty(False) '''If True, the collision detection for limiting the touch inside the scatter will be done after dispaching the touch to the children. You can put children outside the bounding box of the scatter and still be able to touch them. .. versionadded:: 1.3.0 ''' scale_min = NumericProperty(0.01) '''Minimum scaling factor allowed. :attr:`scale_min` is a :class:`~kivy.properties.NumericProperty` and defaults to 0.01. ''' scale_max = NumericProperty(1e20) '''Maximum scaling factor allowed. :attr:`scale_max` is a :class:`~kivy.properties.NumericProperty` and defaults to 1e20. ''' transform = ObjectProperty(Matrix()) '''Transformation matrix. :attr:`transform` is an :class:`~kivy.properties.ObjectProperty` and defaults to the identity matrix. .. note:: This matrix reflects the current state of the transformation matrix but setting it directly will erase previously applied transformations. To apply a transformation considering context, please use the :attr:`~Scatter.apply_transform` method. ''' transform_inv = ObjectProperty(Matrix()) '''Inverse of the transformation matrix. :attr:`transform_inv` is an :class:`~kivy.properties.ObjectProperty` and defaults to the identity matrix. ''' def _get_bbox(self): xmin, ymin = xmax, ymax = self.to_parent(0, 0) for point in [(self.width, 0), (0, self.height), self.size]: x, y = self.to_parent(*point) if x < xmin: xmin = x if y < ymin: ymin = y if x > xmax: xmax = x if y > ymax: ymax = y return (xmin, ymin), (xmax - xmin, ymax - ymin) bbox = AliasProperty(_get_bbox, None, bind=('transform', 'width', 'height')) '''Bounding box of the widget in parent space:: ((x, y), (w, h)) # x, y = lower left corner :attr:`bbox` is an :class:`~kivy.properties.AliasProperty`. ''' def _get_rotation(self): v1 = Vector(0, 10) tp = self.to_parent v2 = Vector(*tp(*self.pos)) - tp(self.x, self.y + 10) return -1.0 * (v1.angle(v2) + 180) % 360 def _set_rotation(self, rotation): angle_change = self.rotation - rotation r = Matrix().rotate(-radians(angle_change), 0, 0, 1) self.apply_transform(r, post_multiply=True, anchor=self.to_local(*self.center)) rotation = AliasProperty(_get_rotation, _set_rotation, bind=('x', 'y', 'transform')) '''Rotation value of the scatter in degrees moving in a counterclockwise direction. :attr:`rotation` is an :class:`~kivy.properties.AliasProperty` and defaults to 0.0. ''' def _get_scale(self): p1 = Vector(*self.to_parent(0, 0)) p2 = Vector(*self.to_parent(1, 0)) scale = p1.distance(p2) # XXX float calculation are not accurate, and then, scale can be # throwed again even with only the position change. So to # prevent anything wrong with scale, just avoid to dispatch it # if the scale "visually" didn't change. #947 # Remove this ugly hack when we'll be Python 3 only. if hasattr(self, '_scale_p'): if str(scale) == str(self._scale_p): return self._scale_p self._scale_p = scale return scale def _set_scale(self, scale): rescale = scale * 1.0 / self.scale self.apply_transform(Matrix().scale(rescale, rescale, rescale), post_multiply=True, anchor=self.to_local(*self.center)) scale = AliasProperty(_get_scale, _set_scale, bind=('x', 'y', 'transform')) '''Scale value of the scatter. :attr:`scale` is an :class:`~kivy.properties.AliasProperty` and defaults to 1.0. ''' def _get_center(self): return (self.bbox[0][0] + self.bbox[1][0] / 2.0, self.bbox[0][1] + self.bbox[1][1] / 2.0) def _set_center(self, center): if center == self.center: return False t = Vector(*center) - self.center trans = Matrix().translate(t.x, t.y, 0) self.apply_transform(trans) center = AliasProperty(_get_center, _set_center, bind=('bbox', )) def _get_pos(self): return self.bbox[0] def _set_pos(self, pos): _pos = self.bbox[0] if pos == _pos: return t = Vector(*pos) - _pos trans = Matrix().translate(t.x, t.y, 0) self.apply_transform(trans) pos = AliasProperty(_get_pos, _set_pos, bind=('bbox', )) def _get_x(self): return self.bbox[0][0] def _set_x(self, x): if x == self.bbox[0][0]: return False self.pos = (x, self.y) return True x = AliasProperty(_get_x, _set_x, bind=('bbox', )) def _get_y(self): return self.bbox[0][1] def _set_y(self, y): if y == self.bbox[0][1]: return False self.pos = (self.x, y) return True y = AliasProperty(_get_y, _set_y, bind=('bbox', )) def get_right(self): return self.x + self.bbox[1][0] def set_right(self, value): self.x = value - self.bbox[1][0] right = AliasProperty(get_right, set_right, bind=('x', 'width')) def get_top(self): return self.y + self.bbox[1][1] def set_top(self, value): self.y = value - self.bbox[1][1] top = AliasProperty(get_top, set_top, bind=('y', 'height')) def get_center_x(self): return self.x + self.bbox[1][0] / 2. def set_center_x(self, value): self.x = value - self.bbox[1][0] / 2. center_x = AliasProperty(get_center_x, set_center_x, bind=('x', 'width')) def get_center_y(self): return self.y + self.bbox[1][1] / 2. def set_center_y(self, value): self.y = value - self.bbox[1][1] / 2. center_y = AliasProperty(get_center_y, set_center_y, bind=('y', 'height')) def __init__(self, **kwargs): self._touches = [] self._last_touch_pos = {} super(Scatter, self).__init__(**kwargs) def on_transform(self, instance, value): self.transform_inv = value.inverse() def collide_point(self, x, y): x, y = self.to_local(x, y) return 0 <= x <= self.width and 0 <= y <= self.height def to_parent(self, x, y, **k): p = self.transform.transform_point(x, y, 0) return (p[0], p[1]) def to_local(self, x, y, **k): p = self.transform_inv.transform_point(x, y, 0) return (p[0], p[1]) def _apply_transform(self, m, pos=None): m = self.transform.multiply(m) return super(Scatter, self)._apply_transform(m, (0, 0)) def apply_transform(self, trans, post_multiply=False, anchor=(0, 0)): ''' Transforms the scatter by applying the "trans" transformation matrix (on top of its current transformation state). The resultant matrix can be found in the :attr:`~Scatter.transform` property. :Parameters: `trans`: :class:`~kivy.graphics.transformation.Matrix`. Transformation matix to be applied to the scatter widget. `anchor`: tuple, defaults to (0, 0). The point to use as the origin of the transformation (uses local widget space). `post_multiply`: bool, defaults to False. If True, the transform matrix is post multiplied (as if applied before the current transform). Usage example:: from kivy.graphics.transformation import Matrix mat = Matrix().scale(3, 3, 3) scatter_instance.apply_transform(mat) ''' t = Matrix().translate(anchor[0], anchor[1], 0) t = t.multiply(trans) t = t.multiply(Matrix().translate(-anchor[0], -anchor[1], 0)) if post_multiply: self.transform = self.transform.multiply(t) else: self.transform = t.multiply(self.transform) def transform_with_touch(self, touch): # just do a simple one finger drag changed = False if len(self._touches) == self.translation_touches: # _last_touch_pos has last pos in correct parent space, # just like incoming touch dx = (touch.x - self._last_touch_pos[touch][0]) \ * self.do_translation_x dy = (touch.y - self._last_touch_pos[touch][1]) \ * self.do_translation_y dx = dx / self.translation_touches dy = dy / self.translation_touches self.apply_transform(Matrix().translate(dx, dy, 0)) changed = True if len(self._touches) == 1: return changed # we have more than one touch... list of last known pos points = [ Vector(self._last_touch_pos[t]) for t in self._touches if t is not touch ] # add current touch last points.append(Vector(touch.pos)) # we only want to transform if the touch is part of the two touches # farthest apart! So first we find anchor, the point to transform # around as another touch farthest away from current touch's pos anchor = max(points[:-1], key=lambda p: p.distance(touch.pos)) # now we find the touch farthest away from anchor, if its not the # same as touch. Touch is not one of the two touches used to transform farthest = max(points, key=anchor.distance) if farthest is not points[-1]: return changed # ok, so we have touch, and anchor, so we can actually compute the # transformation old_line = Vector(*touch.ppos) - anchor new_line = Vector(*touch.pos) - anchor if not old_line.length(): # div by zero return changed angle = radians(new_line.angle(old_line)) * self.do_rotation self.apply_transform(Matrix().rotate(angle, 0, 0, 1), anchor=anchor) if self.do_scale: scale = new_line.length() / old_line.length() new_scale = scale * self.scale if new_scale < self.scale_min: scale = self.scale_min / self.scale elif new_scale > self.scale_max: scale = self.scale_max / self.scale self.apply_transform(Matrix().scale(scale, scale, scale), anchor=anchor) changed = True return changed def _bring_to_front(self, touch): # auto bring to front if self.auto_bring_to_front and self.parent: parent = self.parent if parent.children[0] is self: return parent.remove_widget(self) parent.add_widget(self) self.dispatch('on_bring_to_front', touch) def on_touch_down(self, touch): x, y = touch.x, touch.y # if the touch isnt on the widget we do nothing if not self.do_collide_after_children: if not self.collide_point(x, y): return False # let the child widgets handle the event if they want touch.push() touch.apply_transform_2d(self.to_local) if super(Scatter, self).on_touch_down(touch): # ensure children don't have to do it themselves if 'multitouch_sim' in touch.profile: touch.multitouch_sim = True touch.pop() self._bring_to_front(touch) return True touch.pop() # if our child didn't do anything, and if we don't have any active # interaction control, then don't accept the touch. if not self.do_translation_x and \ not self.do_translation_y and \ not self.do_rotation and \ not self.do_scale: return False if self.do_collide_after_children: if not self.collide_point(x, y): return False if 'multitouch_sim' in touch.profile: touch.multitouch_sim = True # grab the touch so we get all it later move events for sure self._bring_to_front(touch) touch.grab(self) self._touches.append(touch) self._last_touch_pos[touch] = touch.pos return True def on_touch_move(self, touch): x, y = touch.x, touch.y # let the child widgets handle the event if they want if self.collide_point(x, y) and not touch.grab_current == self: touch.push() touch.apply_transform_2d(self.to_local) if super(Scatter, self).on_touch_move(touch): touch.pop() return True touch.pop() # rotate/scale/translate if touch in self._touches and touch.grab_current == self: if self.transform_with_touch(touch): self.dispatch('on_transform_with_touch', touch) self._last_touch_pos[touch] = touch.pos # stop propagating if its within our bounds if self.collide_point(x, y): return True def on_transform_with_touch(self, touch): ''' Called when a touch event has transformed the scatter widget. By default this does nothing, but can be overriden by derived classes that need to react to transformations caused by user input. :Parameters: `touch`: The touch object which triggered the transformation. .. versionadded:: 1.8.0 ''' pass def on_bring_to_front(self, touch): ''' Called when a touch event causes the scatter to be brought to the front of the parent (only if :attr:`auto_bring_to_front` is True) :Parameters: `touch`: The touch object which brought the scatter to front. .. versionadded:: 1.9.0 ''' pass def on_touch_up(self, touch): x, y = touch.x, touch.y # if the touch isnt on the widget we do nothing, just try children if not touch.grab_current == self: touch.push() touch.apply_transform_2d(self.to_local) if super(Scatter, self).on_touch_up(touch): touch.pop() return True touch.pop() # remove it from our saved touches if touch in self._touches and touch.grab_state: touch.ungrab(self) del self._last_touch_pos[touch] self._touches.remove(touch) # stop propagating if its within our bounds if self.collide_point(x, y): return True
class RotateLabel(Label): transform = ObjectProperty(Matrix())
def transform_with_touch(self, touch): # just do a simple one finger drag changed = False if len(self._touches) == self.translation_touches: # _last_touch_pos has last pos in correct parent space, # just like incoming touch dx = (touch.x - self._last_touch_pos[touch][0]) * self.do_translation_x dy = (touch.y - self._last_touch_pos[touch][1]) * self.do_translation_y dx = dx / self.translation_touches dy = dy / self.translation_touches m = Matrix().translate(dx, dy, 0) if self.check_trans(m): self.apply_transform(Matrix().translate(dx, dy, 0)) changed = True else: pass if len(self._touches) == 1: return changed # we have more than one touch... list of last known pos points = [ Vector(self._last_touch_pos[t]) for t in self._touches if t is not touch ] # add current touch last points.append(Vector(touch.pos)) # we only want to transform if the touch is part of the two touches # farthest apart! So first we find anchor, the point to transform # around as another touch farthest away from current touch's pos anchor = max(points[:-1], key=lambda p: p.distance(touch.pos)) # now we find the touch farthest away from anchor, if its not the # same as touch. Touch is not one of the two touches used to transform farthest = max(points, key=anchor.distance) if farthest is not points[-1]: return changed # ok, so we have touch, and anchor, so we can actually compute the # transformation old_line = Vector(*touch.ppos) - anchor new_line = Vector(*touch.pos) - anchor if not old_line.length(): # div by zero return changed angle = radians(new_line.angle(old_line)) * self.do_rotation self.apply_transform(Matrix().rotate(angle, 0, 0, 1), anchor=anchor) if self.do_scale: scale = new_line.length() / old_line.length() new_scale = scale * self.scale if new_scale < self.scale_min: scale = self.scale_min / self.scale elif new_scale > self.scale_max: scale = self.scale_max / self.scale m = Matrix().scale(scale, scale, scale) if self.check_trans(m): self.apply_transform(Matrix().scale(scale, scale, scale), anchor=anchor) changed = True else: pass return changed
def _update_glsl(self): asp = self.width / float(self.height) proj = Matrix().view_clip(-asp, asp, -1, 1, 1.5, 100, 1) self.canvas["projection_mat"] = proj
def on_bypass(self, instance, bypass): if bypass: self.transform = Matrix()
def update_glsl(self, *args): asp = self.width / max(float(self.height), 1) proj = Matrix().view_clip(-asp, asp, -1, 1, 1, 100, 1) self.fbo['projection_mat'] = proj
def __init__(self, **kw): self.cube_vertices = [] cube = np.zeros((8, 3), 'f') i = 0 for x in (-1, 1): for y in (-1, 1): for z in (-1, 1): cube[i] = [x, y, z] i += 1 # Shift the cube down below the x-axis cube[:, 0:1] -= 1 cube[:, 1:2] += 1 cube[:, 2:3] -= 1 # Create cubes for each position for x in (-2, 0, 2): c = cube.copy() c[:, 0:1] += x self.cube_vertices.append(c.flatten()) for y in (0, 2, 4): d = c.copy() d[:, 1:2] += y self.cube_vertices.append(d.flatten()) for z in (0, -2, -4): e = d.copy() e[:, 2:3] += z self.cube_vertices.append(e.flatten()) # The indices for each cube self.cube_indices = [ 0, 1, 0, 2, 0, 4, 1, 3, 1, 5, 3, 2, 3, 7, 2, 6, 6, 7, 7, 5, 6, 4, 4, 5 ] self.line_vertices = np.array( [[0.000000, 0.000000, -0.000000], [0.000000, 1, -1.0], [0.000000, 2, -2.0], [0.000000, 3, -3.0], [0.000000, 4, -4.0], [0.000000, 5, -5.0], [0.000000, 6, -6.0], [0.000000, 7, -7.0], [0.000000, 8, -8.0], [0.000000, 9, -9.0], [0.000000, 10, -10.0], [0.000000, 11, -10.0], [0.000000, 12, -10.0], [ 0.000000, 13, -10.0 ], [0.000000, 14, -10.0], [5.000000, 15, -10.0], [5.000000, 16, -10.0], [5.000000, 17, -10.0], [5.000000, 18, -10.0], [5.000000, 19, -10.0], [ 5.000000, 20, -10.0 ], [5.000000, 21, -10.0], [5.000000, 22, -10.0], [5.000000, 23, -10.0], [5.000000, 24, -15.0], [ 5.000000, 25, -15.0 ], [5.000000, 26, -15.0], [5.000000, 27, -15.0], [5.000000, 28, -15.0], [0.000000, 29, -15.0], [ 0.000000, 30, -15.0 ], [0.000000, 31, -15.0], [0.000000, 32, -15.0], [0.000000, 33, -15.0], [0.000000, 34, -15.0], [ 0.000000, 35, -15.0 ], [0.000000, 36, -15.0], [0.000000, 37, -15.0], [0.000000, 38, -15.0], [0.000000, 39, -15.0], [ 0.000000, 40, -15.0 ], [0.000000, 41, -30.0], [0.000000, 42, -30.0], [0.000000, 43, -30.0], [0.000000, 44, -30.0], [ 0.000000, 45, -30.0 ], [0.000000, 46, -30.0], [0.000000, 47, -30.0], [0.000000, 48, -30.0], [0.000000, 49, -30.0], [ 0.000000, 50, -30.0 ], [0.000000, 51, -30.0], [0.000000, 52, -30.0], [0.000000, 53, -30.0], [0.000000, 54, -30.0], [ 0.000000, 55, -30.0 ], [0.000000, 56, -30.0], [0.000000, 57, -30.0], [0.000000, 58, -30.0], [0.000000, 59, -30.0]], 'f') #self.line_vertices = np.array( [ [ 0.000000, 0.000000, 0.000000 ], # [ 0.000000, 0.999998, -0.001745 ], # [ 0.000000, 1.999997, -0.003491 ], # [ 0.000000, 2.999995, -0.005236 ], # [ 0.000000, 3.999994, -0.006981 ], # [ 0.000000, 4.999992, -0.008727 ], # [ 0.000000, 5.999991, -0.010472 ], # [ 0.000000, 6.999989, -0.012217 ], # [ 0.000000, 7.999988, -0.013963 ], # [ 0.000000, 8.999986, -0.015708 ], # [ 0.000000, 9.999985, -0.017453 ], # [ 0.000000, 10.999983, -0.019199 ], # [ 0.000000, 11.999982, -0.020944 ], # [ 0.000000, 12.999980, -0.022689 ], # [ 0.000000, 13.999979, -0.024435 ], # [ 0.000000, 14.999977, -0.026180 ], # [ 0.000000, 15.999976, -0.027925 ], # [ 0.000000, 16.999974, -0.029671 ], # [ 0.000000, 17.999973, -0.031416 ], # [ 0.000000, 18.999971, -0.033161 ], # [ 0.000000, 19.999970, -0.034907 ], # [ 0.000000, 20.999968, -0.036652 ], # [ 0.000000, 21.999966, -0.038397 ], # [ 0.000000, 22.999965, -0.040143 ], # [ 0.000000, 23.999963, -0.041888 ], # [ 0.000000, 24.999962, -0.043633 ], # [ 0.000000, 25.999960, -0.045379 ], # [ 0.000000, 26.999959, -0.047124 ], # [ 0.000000, 27.999957, -0.048869 ], # [ 0.000000, 28.999956, -0.050615 ], # [ 0.000000, 29.999954, -0.052360 ], # [ 0.000000, 30.999953, -0.054105 ], # [ 0.000000, 31.999951, -0.055851 ], # [ 0.000000, 32.999950, -0.057596 ], # [ 0.000000, 33.999948, -0.059341 ], # [ 0.000000, 34.999947, -0.061086 ], # [ 0.000000, 35.999945, -0.062832 ], # [ 0.000000, 36.999944, -0.064577 ], # [ 0.000000, 37.999942, -0.066322 ], # [ 0.000000, 38.999941, -0.068068 ], # [ 0.000000, 39.999939, -0.069813 ], # [ 0.000000, 40.999938, -0.071558 ], # [ 0.000000, 41.999936, -0.073304 ], # [ 0.000000, 42.999935, -0.075049 ], # [ 0.000000, 43.999933, -0.076794 ], # [ 0.000000, 44.999931, -0.078540 ], # [ 0.000000, 45.999930, -0.080285 ], # [ 0.000000, 46.999928, -0.082030 ], # [ 0.000000, 47.999927, -0.083776 ], # [ 0.000000, 48.999925, -0.085521 ], # [ 0.000000, 49.999924, -0.087266 ], # [ 0.000000, 50.999922, -0.089012 ], # [ 0.000000, 51.999921, -0.090757 ], # [ 0.000000, 52.999919, -0.092502 ], # [ 0.000000, 53.999918, -0.094248 ], # [ 0.000000, 54.999916, -0.095993 ], # [ 0.000000, 55.999915, -0.097738 ], # [ 0.000000, 56.999913, -0.099484 ], # [ 0.000000, 57.999912, -0.101229 ], # [ 0.000000, 58.999910, -0.102974 ], # [ 0.000000, 59.999909, -0.104720 ], # [ 0.000000, 60.999907, -0.106465 ], # [ 0.000000, 61.999906, -0.108210 ], # [ 0.000000, 62.999904, -0.109956 ], # [ 0.000000, 63.999903, -0.111701 ], # [ 0.000000, 64.999901, -0.113446 ], # [ 0.000000, 65.999899, -0.115192 ], # [ 0.000000, 66.999898, -0.116937 ], # [ 0.000000, 67.999896, -0.118682 ], # [ 0.000000, 68.999895, -0.120428 ], # [ 0.000000, 69.999893, -0.122173 ], # [ 0.000000, 70.999892, -0.123918 ], # [ 0.000000, 71.999890, -0.125664 ], # [ 0.000000, 72.999889, -0.127409 ], # [ 0.000000, 73.999887, -0.129154 ], # [ 0.000000, 74.999886, -0.130900 ], # [ 0.000000, 75.999884, -0.132645 ], # [ 0.000000, 76.999883, -0.134390 ], # [ 0.000000, 77.999881, -0.136136 ], # [ 0.000000, 78.999880, -0.137881 ], # [ 0.000000, 79.999878, -0.139626 ], # [ 0.000000, 80.999877, -0.141372 ], # [ 0.000000, 81.999875, -0.143117 ], # [ 0.000000, 82.999874, -0.144862 ], # [ 0.000000, 83.999872, -0.146608 ], # [ 0.000000, 84.999871, -0.148353 ], # [ 0.000000, 85.999869, -0.150098 ], # [ 0.000000, 86.999867, -0.151844 ], # [ 0.000000, 87.999866, -0.153589 ], # [ 0.000000, 88.999864, -0.155334 ], # [ 0.000000, 89.999863, -0.157080 ], # [ 0.000000, 90.999861, -0.158825 ], # [ 0.000000, 91.999860, -0.160570 ], # [ 0.000000, 92.999858, -0.162316 ], # [ 0.000000, 93.999857, -0.164061 ], # [ 0.000000, 94.999855, -0.165806 ], # [ 0.000000, 95.999854, -0.167552 ], # [ 0.000000, 96.999852, -0.169297 ], # [ 0.000000, 97.999851, -0.171042 ], # [ 0.000000, 98.999849, -0.172788 ], # [ 0.000000, 99.999848, -0.174533 ], # [ 0.000000, 100.999846, -0.176278 ], # [ 0.000000, 101.999845, -0.178023 ], # [ 0.000000, 102.999843, -0.179769 ], # [ 0.000000, 103.999842, -0.181514 ], # [ 0.000000, 104.999840, -0.183259 ], # [ 0.000000, 105.999839, -0.185005 ], # [ 0.000000, 106.999837, -0.186750 ], # [ 0.000000, 107.999836, -0.188495 ], # [ 0.000000, 108.999834, -0.190241 ], # [ 0.000000, 109.999832, -0.191986 ], # [ 0.000000, 110.999831, -0.193731 ], # [ 0.000000, 111.999829, -0.195477 ], # [ 0.000000, 112.999828, -0.197222 ], # [ 0.000000, 113.999826, -0.198967 ], # [ 0.000000, 114.999825, -0.200713 ], # [ 0.000000, 115.999823, -0.202458 ], # [ 0.000000, 116.999822, -0.204203 ], # [ 0.000000, 117.999820, -0.205949 ], # [ 0.000000, 118.999819, -0.207694 ], # [ 0.000000, 119.999817, -0.209439 ], # [ 0.000000, 120.999816, -0.211185 ], # [ 0.000000, 121.999814, -0.212930 ], # [ 0.000000, 122.999813, -0.214675 ] ], 'f' ) self.line_indices = [] for i in range(0, int(len(self.line_vertices) / 3)): self.line_indices += [i, i + 1] kw['shader_file'] = 'shaders.glsl' self.canvas = RenderContext(compute_normal_mat=True) shader_file = kw.pop('shader_file') self.canvas.shader.source = resource_find(shader_file) self._touches = [] super(Renderer, self).__init__(**kw) with self.canvas: # This controls the camera position, or rather, shifts the world Translate(0, -1, -15) self.rot = Rotate(0, 1, 1, 1) self.rotx = Rotate(0, 1, 0, 0) self.roty = Rotate(0, 0, 1, 0) # This controls the zoom self.scale = Scale(1) # Change the colour of the mesh to red. ChangeState(Kd=(1.0, 0.0, 0.0), Ka=(1.0, 1.0, 0.0), Ks=(.3, .3, .3), Tr=1., Ns=1., intensity=0.5) # Draw the vertices and indices for i in range(len(self.cube_vertices)): Mesh(vertices=self.cube_vertices[i], indices=self.cube_indices, fmt=[(b'v_pos', 3, 'float')], mode='lines') # Change the details of the line, i.e. colour to green ChangeState(Kd=(0.0, 1.0, 0.0), Ka=(1.0, 1.0, 0.0), Ks=(.3, .3, .3), Tr=1., Ns=1., intensity=1.) # Draw the line self.bh_line = Mesh(vertices=self.line_vertices.flatten(), indices=self.line_indices, fmt=[(b'v_pos', 3, 'float')], mode='lines') print(self.bh_line.indices) asp = float(Window.width) / Window.height / 2.0 proj = Matrix().view_clip(-asp, asp, -0.5, 0.5, 1, 100, 1) self.canvas['projection_mat'] = proj Window.request_keyboard(None, self).bind(on_key_down=self.on_keyboard_down)
def on_scale(self, instance, scale): matrix = Matrix() matrix.scale(scale, scale, 1) matrix.translate((-scale + 1) * 0.5 * self.width, -scale * self.height / 4, 0) self.instruction.matrix = matrix
class ImageWidget(BoxLayout): orientation = 'vertical' imageList = None coreImage = None currentPos = 0 imageAngle = NumericProperty(0) imageScale = NumericProperty(1) imageNumber = StringProperty("") imageName = StringProperty("") imageLoader = ImageLoader(threads=4) reflect = Matrix().scale(0.1, 1, 1) buttonSchedule = None def __init__(self, onClick=None, **kwargs): tmp = os.getcwd() os.chdir('ui') Builder.load_file('imagewidget.kv') super().__init__(**kwargs) self.id = 'image' self.onClick = onClick os.chdir(tmp) def setImageList(self, imageList): self.imageList = imageList self.currentPos = 0 self.imageLoader.loadImageList(imageList) self.openImage(self.imageList[self.currentPos][3], self.currentPos) self.imageName = self.imageList[self.currentPos][4] self.imageNumber = "{}/{}".format(self.currentPos + 1, len(self.imageList)) def on_left_press(self, step=0): if self.imageList is not None: amount = 1 if step > 0: amount = int(len(self.imageList) * step / 100.0) self.currentPos -= max(amount, 1) if self.currentPos < 0: self.currentPos = 0 if self.buttonSchedule is not None: Clock.unschedule(self.buttonSchedule) self.buttonSchedule = None self.buttonSchedule = Clock.schedule_once( partial(self.openPicture, self.currentPos), 0.1) self.imageNumber = "{}/{}".format(self.currentPos + 1, len(self.imageList)) self.imageName = self.imageList[self.currentPos][4] def on_right_press(self, step=0): if self.imageList is not None: amount = 1 if step > 0: amount = int(len(self.imageList) * step / 100.0) self.currentPos += max(amount, 1) if self.currentPos >= len(self.imageList): self.currentPos = len(self.imageList) - 1 if self.buttonSchedule is not None: Clock.unschedule(self.buttonSchedule) self.buttonSchedule = None self.buttonSchedule = Clock.schedule_once( partial(self.openPicture, self.currentPos), 0.1) self.imageNumber = "{}/{}".format(self.currentPos + 1, len(self.imageList)) self.imageName = self.imageList[self.currentPos][4] def openPicture(self, *args): pos = args[0] if pos != self.currentPos: return self.openImage(self.imageList[self.currentPos][3], self.currentPos) def reTryOpen(self, *largs): pos = largs[0] name = largs[1] self.openImage(name, pos) def openImage(self, name, pos): print(name) image = self.imageLoader.getImage(pos) if image['status'] != 'loaded': print("waiting") Clock.schedule_once(partial(self.reTryOpen, pos, name), 0.5) else: imData = image tex = self.get_texture(imData) self.ids.image.texture = tex if imData['vflip']: self.ids.image.texture.flip_vertical() if imData['hflip']: self.ids.image.texture.flip_horizontal() self.imageAngle = imData['angle'] if self.imageAngle % 180 != 0: self.imageScale = self.height / self.width else: self.imageScale = 1 def get_texture(self, data): bt = data['image'] full: PillowImage = PillowImage.open(io.BytesIO(bt)) exif_data = full._getexif() angle = 0 vFlip = True hFlip = False # is there a rotation? rotation = 1 if exif_data is not None and 274 in exif_data: rotation = exif_data[274] if rotation == 1: full = full.transpose(PillowImage.FLIP_LEFT_RIGHT) if rotation == 2: full = full.transpose(PillowImage.FLIP_LEFT_RIGHT) elif rotation == 3: full = full.transpose(PillowImage.ROTATE_180) elif rotation == 4: pass elif rotation == 5: full = full.transpose(PillowImage.FLIP_LEFT_RIGHT).transpose( PillowImage.ROTATE_270) #swap 90 and 270 elif rotation == 6: full = full.transpose(PillowImage.FLIP_LEFT_RIGHT).transpose( PillowImage.ROTATE_90) #here too elif rotation == 7: full = full.transpose(PillowImage.ROTATE_90) elif rotation == 8: full = full.transpose(PillowImage.FLIP_LEFT_RIGHT).transpose( PillowImage.ROTATE_270) coreImage = CoreImageData(full.size[0], full.size[1], full.mode.lower(), full.tobytes()) texture = Texture.create_from_data(coreImage) data['angle'] = angle data['vflip'] = vFlip data['hflip'] = not hFlip return texture
def moveRight(self): try: self.scatter.apply_transform(Matrix().translate(x=-100/self.scatter.scale,y=0,z=0)) except Exception as e: handleCrash(e)
def update_glsl(self, *largs): width = self.width if self.width > 1 else 100 height = self.height if self.height > 1 else 100 asp = width / float(height) proj = Matrix().view_clip(-asp, asp, -1, 1, 1, 600, 1) proj = Matrix() proj.perspective(self.perspective_value, asp, 1, 1000) matrix_camera = Matrix().identity() matrix_camera = matrix_camera.look_at(*self.look_at) self.canvas['projection_mat'] = proj self.canvas['camera'] = matrix_camera self.canvas['diffuse_light'] = (0.0, 1.0, 0.0) self.canvas['ambient_light'] = [v for v in self.ambient_light] self.canvas['light_visibility'] = self.light_intensity self.canvas['eye_position'] = [self.look_at[0], self.look_at[1], self.look_at[2]] self.canvas['light_position'] = [self.light_position[0], self.light_position[1], self.light_position[2]] self.canvas['light_orientation'] = [self.light_orientation[0], self.light_orientation[1], self.light_orientation[2]] self.canvas['light_0'] = [self.light_0[0], self.light_0[1], self.light_0[2]] self.canvas['light_1'] = [self.light_1[0], self.light_1[1], self.light_1[2]] if self.shadow: self.canvas['texture1'] = 1 self.canvas['texture2'] = 2 self.canvas["enabled_shadow"] = 1.0 else: self.canvas["enabled_shadow"] = 0.0 self.canvas["texture1"] = 0 depthProjectionMatrix = Matrix().view_clip(-100 * self.shadow_threshold, 100 * self.shadow_threshold, -100 * self.shadow_threshold, 100 * self.shadow_threshold, -100 * self.shadow_threshold, 200 * self.shadow_threshold * 2, 0) _shadow_pos = self._shadow_pos _shadow_target = self._shadow_target depthViewMatrix = Matrix().look_at(_shadow_target[0], _shadow_target[1], _shadow_target[2] + self._shadow_offset, _shadow_pos[0], _shadow_pos[1], _shadow_pos[2], 0, 1, 0) depthModelMatrix = Matrix().identity() depthMVP = depthProjectionMatrix.multiply(depthViewMatrix).multiply(depthModelMatrix) self.canvas['depthMVP'] = depthMVP self.canvas['cond'] = (0.0, 0.7) self.canvas['val_sin'] = (self.alpha, 0.0) if self.shadow: self.update_fbo(largs[0]) # label.text = str(Clock.get_rfps()) if self.parent.parent is None: # del self.parent self.parent.canvas3d = None self.fbo_list.clear() self.fbo = None for a in self.nodes: a.remove_a() self.nodes = [] self.parent._nodes = [] self.parent.clear_widgets() self.adding_queue = [] self.nt.cancel()
def update_img(self, img, force=False): ''' Updates the screen with a new image. :Parameters: `img`: :class:`~ffpyplayer.pic.Image` instance The image to be displayed. ''' from ffpyplayer.tools import get_best_pix_fmt from ffpyplayer.pic import SWScale if img is None: return img_fmt = img.get_pixel_format() self.image_size = img_w, img_h = img.get_size() update = force if self._iw != img_w or self._ih != img_h: update = True if img_fmt not in ('yuv420p', 'rgba', 'rgb24', 'gray', 'bgr24', 'bgra'): swscale = self._swscale if img_fmt != self._sw_src_fmt or swscale is None or update: ofmt = get_best_pix_fmt( img_fmt, ( 'yuv420p', 'rgba', 'rgb24', 'gray', 'bgr24', 'bgra')) self._swscale = swscale = SWScale( iw=img_w, ih=img_h, ifmt=img_fmt, ow=0, oh=0, ofmt=ofmt) self._sw_src_fmt = img_fmt img = swscale.scale(img) img_fmt = img.get_pixel_format() w, h = self.available_size or self.size if (not w) or not h: self.img = img return if self._fmt != img_fmt: self._fmt = img_fmt self._kivy_ofmt = { 'yuv420p': 'yuv420p', 'rgba': 'rgba', 'rgb24': 'rgb', 'gray': 'luminance', 'bgr24': 'bgr', 'bgra': 'bgra'}[img_fmt] update = True if update or w != self._last_w or h != self._last_h or \ self.rotation != self._last_rotation: if self.scale_to_image: rotation = self.rotation rot = self.rotation * math.pi / 180 rot_w = abs(img_w * math.cos(rot)) + abs(img_h * math.sin(rot)) rot_h = abs(img_h * math.cos(rot)) + abs(img_w * math.sin(rot)) scalew, scaleh = w / rot_w, h / rot_h scale = min(min(scalew, scaleh), 1) self.transform = Matrix() self.rotation = rotation self.apply_transform(Matrix().scale(scale, scale, 1), post_multiply=True) self.pos = 0, 0 self._iw, self._ih = img_w, img_h self._last_h = h self._last_w = w self._last_rotation = self.rotation self.img = img kivy_ofmt = self._kivy_ofmt if update: self.canvas.remove_group(str(self) + 'image_display') if kivy_ofmt == 'yuv420p': w2 = int(img_w / 2) h2 = int(img_h / 2) self._tex_y = Texture.create(size=(img_w, img_h), colorfmt='luminance') self._tex_u = Texture.create(size=(w2, h2), colorfmt='luminance') self._tex_v = Texture.create(size=(w2, h2), colorfmt='luminance') with self.canvas: self._fbo = fbo = Fbo(size=(img_w, img_h), group=str(self) + 'image_display') with fbo: BindTexture(texture=self._tex_u, index=1) BindTexture(texture=self._tex_v, index=2) Rectangle(size=fbo.size, texture=self._tex_y) fbo.shader.fs = BufferImage._YUV_RGB_FS fbo['tex_y'] = 0 fbo['tex_u'] = 1 fbo['tex_v'] = 2 tex = self.img_texture = fbo.texture fbo.add_reload_observer(self.reload_buffer) else: tex = self.img_texture = Texture.create( size=(img_w, img_h), colorfmt=kivy_ofmt) tex.add_reload_observer(self.reload_buffer) tex.flip_vertical() if self.flip: tex.flip_horizontal() self.texture_size = img_w, img_h if kivy_ofmt == 'yuv420p': dy, du, dv, _ = img.to_memoryview() self._tex_y.blit_buffer(dy, colorfmt='luminance') self._tex_u.blit_buffer(du, colorfmt='luminance') self._tex_v.blit_buffer(dv, colorfmt='luminance') self._fbo.ask_update() self._fbo.draw() else: self.img_texture.blit_buffer(img.to_memoryview()[0], colorfmt=kivy_ofmt) self.canvas.ask_update()
def _update_labels(self): xlabel = self._xlabel ylabel = self._ylabel x = self.x y = self.y width = self.width height = self.height padding = self.padding x_next = padding + x y_next = padding + y xextent = width + x yextent = height + y ymin = self.ymin ymax = self.ymax xmin = self.xmin precision = self.precision x_overlap = False y_overlap = False # set up x and y axis labels if xlabel: xlabel.text = self.xlabel xlabel.texture_update() xlabel.size = xlabel.texture_size xlabel.pos = int(x + width / 2. - xlabel.width / 2.), int(padding + y) y_next += padding + xlabel.height if ylabel: ylabel.text = self.ylabel ylabel.texture_update() ylabel.size = ylabel.texture_size ylabel.x = padding + x - (ylabel.width / 2. - ylabel.height / 2.) x_next += padding + ylabel.height xpoints = self._ticks_majorx xlabels = self._x_grid_label xlabel_grid = self.x_grid_label ylabel_grid = self.y_grid_label ypoints = self._ticks_majory ylabels = self._y_grid_label # now x and y tick mark labels if len(ylabels) and ylabel_grid: # horizontal size of the largest tick label, to have enough room ylabels[0].text = precision % ypoints[0] ylabels[0].texture_update() y1 = ylabels[0].texture_size y_start = y_next + (padding + y1[1] if len(xlabels) and xlabel_grid else 0) +\ (padding + y1[1] if not y_next else 0) yextent = y + height - padding - y1[1] / 2. if self.ylog: ymax = log10(ymax) ymin = log10(ymin) ratio = (yextent - y_start) / float(ymax - ymin) y_start -= y1[1] / 2. func = (lambda x: 10**x) if self.ylog else lambda x: x y1 = y1[0] for k in range(len(ylabels)): ylabels[k].text = precision % func(ypoints[k]) ylabels[k].texture_update() ylabels[k].size = ylabels[k].texture_size y1 = max(y1, ylabels[k].texture_size[0]) ylabels[k].pos = tuple( map(int, (x_next, y_start + (ypoints[k] - ymin) * ratio))) if len(ylabels) > 1 and ylabels[0].top > ylabels[1].y: y_overlap = True else: x_next += y1 + padding if len(xlabels) and xlabel_grid: func = log10 if self.xlog else lambda x: x # find the distance from the end that'll fit the last tick label xlabels[0].text = precision % func(xpoints[-1]) xlabels[0].texture_update() xextent = x + width - xlabels[0].texture_size[0] / 2. - padding # find the distance from the start that'll fit the first tick label if not x_next: xlabels[0].text = precision % func(xpoints[0]) xlabels[0].texture_update() x_next = padding + xlabels[0].texture_size[0] / 2. xmin = func(xmin) ratio = (xextent - x_next) / float(func(self.xmax) - xmin) func = (lambda x: 10**x) if self.xlog else lambda x: x right = -1 for k in range(len(xlabels)): xlabels[k].text = precision % func(xpoints[k]) # update the size so we can center the labels on ticks xlabels[k].texture_update() xlabels[k].size = xlabels[k].texture_size xlabels[k].pos = tuple( map(int, (x_next + (xpoints[k] - xmin) * ratio - xlabels[k].texture_size[0] / 2., y_next))) if xlabels[k].x < right: x_overlap = True break right = xlabels[k].right if not x_overlap: y_next += padding + xlabels[0].texture_size[1] # now re-center the x and y axis labels if xlabel: xlabel.x = int(x_next + (xextent - x_next) / 2. - xlabel.width / 2.) if ylabel: ylabel.y = int(y_next + (yextent - y_next) / 2. - ylabel.height / 2.) t = Matrix().translate(ylabel.center[0], ylabel.center[1], 0) t = t.multiply(Matrix().rotate(-radians(270), 0, 0, 1)) ylabel.transform = t.multiply(Matrix().translate( -int(ylabel.center_x), -int(ylabel.center_y), 0)) if x_overlap: for k in range(len(xlabels)): xlabels[k].text = '' if y_overlap: for k in range(len(ylabels)): ylabels[k].text = '' return x_next - x, y_next - y, xextent - x, yextent - y
class Scatter(Widget): '''Scatter class. See module documentation for more information. ''' auto_bring_to_front = BooleanProperty(True) '''If True, the widget will be automatically pushed on the top of parent widget list for drawing. :data:`auto_bring_to_front` is a :class:`~kivy.properties.BooleanProperty`, default to True. ''' do_translation_x = BooleanProperty(True) '''Allow translation on X axis :data:`do_translation_x` is a :class:`~kivy.properties.BooleanProperty`, default to True. ''' do_translation_y = BooleanProperty(True) '''Allow translation on Y axis. :data:`do_translation_y` is a :class:`~kivy.properties.BooleanProperty`, default to True. ''' def _get_do_translation(self): return (self.do_translation_x, self.do_translation_y) def _set_do_translation(self, value): if type(value) in (list, tuple): self.do_translation_x, self.do_translation_y = value else: self.do_translation_x = self.do_translation_y = bool(value) do_translation = AliasProperty(_get_do_translation, _set_do_translation, bind=('do_translation_x', 'do_translation_y')) '''Allow translation on X or Y axis. :data:`do_translation` is a :class:`~kivy.properties.AliasProperty` of (:data:`do_translation_x` + :data:`do_translation_y`) ''' do_rotation = BooleanProperty(True) '''Allow rotation. :data:`do_rotation` is a :class:`~kivy.properties.BooleanProperty`, default to True. ''' do_scale = BooleanProperty(True) '''Allow scaling. :data:`do_scale` is a :class:`~kivy.properties.BooleanProperty`, default to True. ''' do_collide_after_children = BooleanProperty(False) '''If True, the collision detection for limiting the touch inside the scatter will be done after dispaching the touch to the children. You can put children outside the bounding box of the scatter, and be able to touch them. .. versionadded:: 1.3.0 ''' scale_min = NumericProperty(0.01) '''Minimum scaling factor allowed. :data:`scale_min` is a :class:`~kivy.properties.NumericProperty`, default to 0.01 ''' scale_max = NumericProperty(1e20) '''Maximum scaling factor allowed. :data:`scale_max` is a :class:`~kivy.properties.NumericProperty`, default to 1e20 ''' transform = ObjectProperty(Matrix()) '''Transformation matrix. :data:`transform` is a :class:`~kivy.properties.ObjectProperty`, default to the identity matrix. ''' transform_inv = ObjectProperty(Matrix()) '''Inverse of the transformation matrix. :data:`transform_inv` is a :class:`~kivy.properties.ObjectProperty`, default to the identity matrix. ''' def _get_bbox(self): xmin, ymin = xmax, ymax = self.to_parent(0, 0) for point in [(self.width, 0), (0, self.height), self.size]: x, y = self.to_parent(*point) if x < xmin: xmin = x if y < ymin: ymin = y if x > xmax: xmax = x if y > ymax: ymax = y return (xmin, ymin), (xmax - xmin, ymax - ymin) bbox = AliasProperty(_get_bbox, None, bind=( 'transform', 'width', 'height')) '''Bounding box of the widget in parent space:: ((x, y), (w, h)) # x, y = lower left corner :data:`bbox` is a :class:`~kivy.properties.AliasProperty`. ''' def _get_rotation(self): v1 = Vector(0, 10) tp = self.to_parent v2 = Vector(*tp(*self.pos)) - tp(self.x, self.y + 10) return -1.0 * (v1.angle(v2) + 180) % 360 def _set_rotation(self, rotation): angle_change = self.rotation - rotation r = Matrix().rotate(-radians(angle_change), 0, 0, 1) self.apply_transform(r, post_multiply=True, anchor=self.to_local(*self.center)) rotation = AliasProperty(_get_rotation, _set_rotation, bind=( 'x', 'y', 'transform')) '''Rotation value of the scatter. :data:`rotation` is a :class:`~kivy.properties.AliasProperty`. ''' def _get_scale(self): p1 = Vector(*self.to_parent(0, 0)) p2 = Vector(*self.to_parent(1, 0)) scale = p1.distance(p2) return float(scale) def _set_scale(self, scale): rescale = scale * 1.0 / self.scale self.apply_transform(Matrix().scale(rescale, rescale, rescale), post_multiply=True, anchor=self.to_local(*self.center)) scale = AliasProperty(_get_scale, _set_scale, bind=('x', 'y', 'transform')) '''Scale value of the scatter. :data:`scale` is a :class:`~kivy.properties.AliasProperty`. ''' def _get_center(self): return (self.bbox[0][0] + self.bbox[1][0] / 2.0, self.bbox[0][1] + self.bbox[1][1] / 2.0) def _set_center(self, center): if center == self.center: return False t = Vector(*center) - self.center trans = Matrix().translate(t.x, t.y, 0) self.apply_transform(trans) center = AliasProperty(_get_center, _set_center, bind=('bbox', )) def _get_pos(self): return self.bbox[0] def _set_pos(self, pos): _pos = self.bbox[0] if pos == _pos: return t = Vector(*pos) - _pos trans = Matrix().translate(t.x, t.y, 0) self.apply_transform(trans) pos = AliasProperty(_get_pos, _set_pos, bind=('bbox', )) def _get_x(self): return self.bbox[0][0] def _set_x(self, x): if x == self.bbox[0][0]: return False self.pos = (x, self.y) return True x = AliasProperty(_get_x, _set_x, bind=('bbox', )) def _get_y(self): return self.bbox[0][1] def _set_y(self, y): if y == self.bbox[0][1]: return False self.pos = (self.x, y) return True y = AliasProperty(_get_y, _set_y, bind=('bbox', )) def get_right(self): return self.x + self.bbox[1][0] def set_right(self, value): self.x = value - self.bbox[1][0] right = AliasProperty(get_right, set_right, bind=('x', 'width')) def get_top(self): return self.y + self.bbox[1][1] def set_top(self, value): self.y = value - self.bbox[1][1] top = AliasProperty(get_top, set_top, bind=('y', 'height')) def get_center_x(self): return self.x + self.bbox[1][0] / 2. def set_center_x(self, value): self.x = value - self.bbox[1][0] / 2. center_x = AliasProperty(get_center_x, set_center_x, bind=('x', 'width')) def get_center_y(self): return self.y + self.bbox[1][1] / 2. def set_center_y(self, value): self.y = value - self.bbox[1][1] / 2. center_y = AliasProperty(get_center_y, set_center_y, bind=('y', 'height')) def __init__(self, **kwargs): self._touches = [] self._last_touch_pos = {} super(Scatter, self).__init__(**kwargs) def on_transform(self, instance, value): self.transform_inv = value.inverse() def collide_point(self, x, y): x, y = self.to_local(x, y) return 0 <= x <= self.width and 0 <= y <= self.height def to_parent(self, x, y, **k): p = self.transform.transform_point(x, y, 0) return (p[0], p[1]) def to_local(self, x, y, **k): p = self.transform_inv.transform_point(x, y, 0) return (p[0], p[1]) def apply_transform(self, trans, post_multiply=False, anchor=(0, 0)): ''' Transforms scatter by trans (on top of its current transformation state). :Parameters: `trans`: transformation matrix from transformation lib. Transformation to be applied to the scatter widget `anchor`: tuple, default to (0, 0) The point to use as the origin of the transformation (uses local widget space) `post_multiply`: bool, default to False If true the transform matrix is post multiplied (as if applied before the current transform) ''' t = Matrix().translate(anchor[0], anchor[1], 0) t = t.multiply(trans) t = t.multiply(Matrix().translate(-anchor[0], -anchor[1], 0)) if post_multiply: self.transform = self.transform.multiply(t) else: self.transform = t.multiply(self.transform) def transform_with_touch(self, touch): # just do a simple one finger drag if len(self._touches) == 1: # _last_touch_pos has last pos in correct parent space, # just like incoming touch dx = (touch.x - self._last_touch_pos[touch][0]) \ * self.do_translation_x dy = (touch.y - self._last_touch_pos[touch][1]) \ * self.do_translation_y self.apply_transform(Matrix().translate(dx, dy, 0)) return # we have more than one touch... points = [Vector(self._last_touch_pos[t]) for t in self._touches] # we only want to transform if the touch is part of the two touches # furthest apart! So first we find anchor, the point to transform # around as the touch farthest away from touch anchor = max(points, key=lambda p: p.distance(touch.pos)) # now we find the touch farthest away from anchor, if its not the # same as touch. Touch is not one of the two touches used to transform farthest = max(points, key=anchor.distance) if points.index(farthest) != self._touches.index(touch): return # ok, so we have touch, and anchor, so we can actually compute the # transformation old_line = Vector(*touch.ppos) - anchor new_line = Vector(*touch.pos) - anchor angle = radians(new_line.angle(old_line)) * self.do_rotation self.apply_transform(Matrix().rotate(angle, 0, 0, 1), anchor=anchor) if self.do_scale: scale = new_line.length() / old_line.length() new_scale = scale * self.scale if new_scale < self.scale_min or new_scale > self.scale_max: scale = 1.0 self.apply_transform(Matrix().scale(scale, scale, scale), anchor=anchor) def _bring_to_front(self): # auto bring to front if self.auto_bring_to_front and self.parent: parent = self.parent parent.remove_widget(self) parent.add_widget(self) def on_touch_down(self, touch): x, y = touch.x, touch.y # if the touch isnt on the widget we do nothing if not self.do_collide_after_children: if not self.collide_point(x, y): return False # let the child widgets handle the event if they want touch.push() touch.apply_transform_2d(self.to_local) if super(Scatter, self).on_touch_down(touch): touch.pop() self._bring_to_front() return True touch.pop() # if our child didn't do anything, and if we don't have any active # interaction control, then don't accept the touch. if not self.do_translation_x and \ not self.do_translation_y and \ not self.do_rotation and \ not self.do_scale: return False if self.do_collide_after_children: if not self.collide_point(x, y): return False # grab the touch so we get all it later move events for sure self._bring_to_front() touch.grab(self) self._touches.append(touch) self._last_touch_pos[touch] = touch.pos return True def on_touch_move(self, touch): x, y = touch.x, touch.y # let the child widgets handle the event if they want if self.collide_point(x, y) and not touch.grab_current == self: touch.push() touch.apply_transform_2d(self.to_local) if super(Scatter, self).on_touch_move(touch): touch.pop() return True touch.pop() # rotate/scale/translate if touch in self._touches and touch.grab_current == self: self.transform_with_touch(touch) self._last_touch_pos[touch] = touch.pos # stop propagating if its within our bounds if self.collide_point(x, y): return True def on_touch_up(self, touch): x, y = touch.x, touch.y # if the touch isnt on the widget we do nothing, just try children if not touch.grab_current == self: touch.push() touch.apply_transform_2d(self.to_local) if super(Scatter, self).on_touch_up(touch): touch.pop() return True touch.pop() # remove it from our saved touches if touch in self._touches and touch.grab_state: touch.ungrab(self) del self._last_touch_pos[touch] self._touches.remove(touch) # stop propagating if its within our bounds if self.collide_point(x, y): return True
def transform_with_touch(self, touch): init_pos = self.center init_scale = self.scale init_touch_len = len(self._touches) #super(ZIScatter, self).transform__with__touch(touch) # just do a simple one finger drag if len(self._touches ) == 1 and self.scale > 1.05: #THIS IS NOT IN ORIGINAL SCATTER: # _last_touch_pos has last pos in correct parent space, # just like incoming touch dx = (touch.x - self._last_touch_pos[touch][0]) \ * self.do_translation_x dy = (touch.y - self._last_touch_pos[touch][1]) \ * self.do_translation_y self.apply_transform(Matrix().translate(dx, dy, 0)) #return elif len( self._touches ) == 1 and self.scale < 1.05: #THIS IS NOT IN ORIGINAL SCATTER: return else: #TO AVOID RETURN IN ORIGINAL SCATTER # we have more than one touch... points = [Vector(self._last_touch_pos[t]) for t in self._touches] # we only want to transform if the touch is part of the two touches # furthest apart! So first we find anchor, the point to transform # around as the touch farthest away from touch anchor = max(points, key=lambda p: p.distance(touch.pos)) # now we find the touch farthest away from anchor, if its not the # same as touch. Touch is not one of the two touches used to transform farthest = max(points, key=anchor.distance) if points.index(farthest) != self._touches.index(touch): return # ok, so we have touch, and anchor, so we can actually compute the # transformation old_line = Vector(*touch.ppos) - anchor new_line = Vector(*touch.pos) - anchor angle = radians(new_line.angle(old_line)) * self.do_rotation self.apply_transform(Matrix().rotate(angle, 0, 0, 1), anchor=anchor) if self.do_scale: scale = new_line.length() / old_line.length() new_scale = scale * self.scale if new_scale < self.scale_min or new_scale > self.scale_max: scale = 1.0 self.apply_transform(Matrix().scale(scale, scale, scale), anchor=anchor) #avoid scatter leaving its box limitx, limity = self.is_leaving_its_box() if limitx or limity: #cancel previous apply_transform if init_touch_len == 1: ddx = ddy = 0 if limitx: ddx = -dx if limity: ddy = -dy self.apply_transform(Matrix().translate(ddx, ddy, 0)) else: if self.do_scale: #self.apply_transform(Matrix().scale(scale/init_scale, scale/init_scale, scale/init_scale), # anchor=anchor) # control #limitx, limity = self.is_leaving_its_box() #if limitx or limity: self.fix_after_leaving_its_box()
class GaussianBlurWindow(ScreenManager): image = DictProperty( { 'texture': None, 'has_alpha': False, 'initial_radius': 0, 'initial_matrix': Matrix(), }, rebind=True) blurring = BooleanProperty(False) use_alpha = BooleanProperty(True) saving = BooleanProperty(False) def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self._last_cb = None self._blur_thread = None def on_image_choose(self, imfile): self.imfile = imfile self.prepare_texture(imfile) self.image.texture = self.texture self.image.has_alpha = self.texture.colorfmt == 'bgra' self.current = 'blur' def on_radius_change(self, radius): self.radius = radius Clock.unschedule(self._last_cb) self._last_cb = partial(self._threaded_gaussian_blur, radius=radius) Clock.schedule_once(self._last_cb, .1) def _threaded_gaussian_blur(self, dt, radius): self._blur_thread = Thread(target=self.gaussian_blur, args=(radius, )) if self._blur_thread.is_alive(): self._blur_thread.join() else: self._blur_thread.start() def prepare_texture(self, imfile): im = np.float32(cv.imread(imfile, -1)) / 255 size = im.shape[1], im.shape[0] colorfmt = 'bgr' if im.shape[-1] == 3 else 'bgra' texture = Texture.create(size=size, colorfmt=colorfmt, bufferfmt='float') buf = cv.flip(im, 0).reshape(-1) self.radius = 0 self.blur = im self.im = im self.imbuf = buf self.texture = texture texture.add_reload_observer(self.populate_texture) self.populate_texture() @mainthread def _set_blurring(self, blurring): self.blurring = blurring @mainthread def populate_texture(self): self.texture.blit_buffer(self.imbuf, bufferfmt=self.texture.bufferfmt, colorfmt=self.texture.colorfmt) self.canvas.ask_update() def gaussian_blur(self, radius, *args): ''' This method here is used as callback for Clock.schedule_once, hence the ``*args`` parameter. ''' self._set_blurring(True) if radius == 0: b = self.im else: b = cv.GaussianBlur(self.im, (0, 0), radius) if self.texture.colorfmt == 'bgra' and self.use_alpha: with np.errstate(divide='ignore', invalid='ignore'): b = cv.merge([b[..., :3] / b[..., -1:], b[..., -1:]]) self.blur = b self.imbuf = cv.flip(b, 0).reshape(-1) self.populate_texture() self._set_blurring(False) def save(self): self.saving = True f, e = path.splitext(self.imfile) alpha = ('.alpha_%s' % ('on' if self.use_alpha else 'off') if self.image.has_alpha else '') fn = f + '.gaussian_blur.raidus_%.1f' % self.radius + alpha + e cv.imwrite( fn, self.blur * 255, (cv.IMWRITE_JPEG_QUALITY, 100, cv.IMWRITE_PNG_COMPRESSION, 9)) self.saving = False
def __init__(self, **kwargs): super(Deflector, self).__init__(**kwargs) # DEFLECTOR LINE: # Here I rotate and translate the deflector line so that it lays exactly under the two fingers # and can be moved and scaled by scatter from now on. Thus I also have to pass the touches to scatter. # First i create the line perfectly horizontal but with the correct length. Then i add the two # drag points at the beginning and the end. self.length_origin = self.length with self.canvas.before: Color(.8, .8, .8) self.deflector_line = Line(points=(self.touch1.x, self.touch1.y - 1, self.touch1.x + self.length, self.touch1.y - 1)) self.deflector_line2 = Line(points=(self.touch1.x, self.touch1.y + 1, self.touch1.x + self.length, self.touch1.y + 1)) ''' self.deflector_line = Image(source='graphics/beta/deflector_blue_beta2.png', allow_stretch=True, keep_ratio=False, size=(self.length, 20), center_y=(self.touch1.y), x=self.touch1.x) ''' # set the right position for the two points: self.point1.center = self.touch1.pos self.point2.center = self.touch1.x + self.length, self.touch1.y self.point_pos_origin = [ self.point1.x, self.point1.y, self.point2.x, self.point2.y ] # rotation: dx = self.touch2.x - self.touch1.x dy = self.touch2.y - self.touch1.y angle = atan2(dy, dx) rotation_matrix = Matrix().rotate(angle, 0, 0, 1) self.apply_transform(rotation_matrix, post_multiply=True, anchor=self.to_local(self.touch1.x, self.touch1.y)) # We have to adjust the bounding box of ourself to the dimension of all the canvas objects (Do we have to?) #self.size = (abs(self.touch2.x - self.touch1.x), abs(self.touch2.y - self.touch1.y)) #self.pos = (min(self.touch1.x, self.touch2.x), min(self.touch1.y, self.touch2.y)) # Now we finally add both touches we received to the _touches list of the underlying scatter class structure. self.touch1.grab(self) self._touches.append(self.touch1) self._last_touch_pos[self.touch1] = self.touch1.pos self.touch2.grab(self) self._touches.append(self.touch2) self._last_touch_pos[self.touch2] = self.touch2.pos self.point1.bind(size=self.size_callback)
def centerCanvas(self, *args): mat = Matrix().translate(Window.width / 2, Window.height / 2, 0) self.scatterInstance.transform = mat
def get_window_matrix(self, x=0, y=0): m = Matrix() m.translate(x, y, 0) return m
def parse_gcode_file(self, fn, target_layer=0, one_layer=False): # open file parse gcode and draw Logger.debug("GcodeViewerScreen: parsing file {}".format(fn)) lastpos = [self.app.wpos[0], self.app.wpos[1], -1] # XYZ, set to initial tool position lastz = None lastdeltaz = None laste = 0 lasts = 1 layer = -1 last_gcode = -1 points = [] max_x = float('nan') max_y = float('nan') min_x = float('nan') min_y = float('nan') has_e = False plane = XY rel_move = False self.is_visible = True if self.laser_mode: self.twod_mode = True # laser mode implies 2D mode self.last_target_layer = target_layer # reset scale and translation m = Matrix() m.identity() self.ids.surface.transform = m # remove all instructions from canvas self.canv.clear() self.canv.add(PushMatrix()) modal_g = 0 cnt = 0 found_layer = False x = lastpos[0] y = lastpos[1] z = lastpos[2] with open(fn) as f: # if self.last_file_pos: # # jump to last read position # f.seek(self.last_file_pos) # self.last_file_pos= None # print('Jumped to Saved position: {}'.format(self.last_file_pos)) for ln in f: cnt += 1 ln = ln.strip() if not ln: continue if ln.startswith(';'): continue if ln.startswith('('): continue p = ln.find(';') if p >= 0: ln = ln[:p] matches = self.extract_gcode.findall(ln) # this handles multiple G codes on one line gcodes = [] d = {} for m in matches: #print(m) if m[0] == 'G' and 'G' in d: # we have another G code on the same line gcodes.append(d) d = {} d[m[0]] = float(m[1]) gcodes.append(d) for d in gcodes: if not d: continue Logger.debug("GcodeViewerScreen: d={}".format(d)) # handle modal commands if 'G' not in d and ('X' in d or 'Y' in d or 'Z' in d or 'S' in d): d['G'] = modal_g gcode = int(d['G']) # G92 E0 resets E if 'G' in d and gcode == 92 and 'E' in d: laste = float(d['E']) has_e = True if 'G' in d and (gcode == 91 or gcode == 90): rel_move = gcode == 91 # only deal with G0/1/2/3 if gcode > 3: continue modal_g = gcode # see if it is 3d printing (ie has an E axis on a G1) if not has_e and ('E' in d and 'G' in d and gcode == 1): has_e = True if rel_move: x += 0 if 'X' not in d else float(d['X']) y += 0 if 'Y' not in d else float(d['Y']) z += 0 if 'Z' not in d else float(d['Z']) else: x = lastpos[0] if 'X' not in d else float(d['X']) y = lastpos[1] if 'Y' not in d else float(d['Y']) z = lastpos[2] if 'Z' not in d else float(d['Z']) i = 0.0 if 'I' not in d else float(d['I']) j = 0.0 if 'J' not in d else float(d['J']) self.rval = 0.0 if 'R' not in d else float(d['R']) e = laste if 'E' not in d else float(d['E']) s = lasts if 'S' not in d else float(d['S']) if not self.twod_mode: # handle layers (when Z changes) if z == -1: # no z seen yet layer = -1 continue if lastz is None: # first layer lastz = z layer = 1 if z != lastz: # count layers layer += 1 lastz = z # wait until we get to the requested layer if layer != target_layer: lastpos[2] = z continue if layer > target_layer and one_layer: # FIXME for some reason this does not work, -- not counting layers #self.last_file_pos= f.tell() #print('Saved position: {}'.format(self.last_file_pos)) break self.current_z = z found_layer = True Logger.debug( "GcodeViewerScreen: x= {}, y= {}, z= {}, s= {}".format( x, y, z, s)) # find bounding box if math.isnan(min_x) or x < min_x: min_x = x if math.isnan(min_y) or y < min_y: min_y = y if math.isnan(max_x) or x > max_x: max_x = x if math.isnan(max_y) or y > max_y: max_y = y # accumulating vertices is more efficient but we need to flush them at some point # Here we flush them if we encounter a new G code like G3 following G1 if last_gcode != gcode: # flush vertices if points: self.canv.add(Color(0, 0, 0)) self.canv.add( Line(points=points, width=1, cap='none', joint='none')) points = [] last_gcode = gcode # in slicer generated files there is no G0 so we need a way to know when to draw, so if there is an E then draw else don't if gcode == 0: #print("move to: {}, {}, {}".format(x, y, z)) # draw moves in dashed red self.canv.add(Color(1, 0, 0)) self.canv.add( Line(points=[lastpos[0], lastpos[1], x, y], width=1, dash_offset=1, cap='none', joint='none')) elif gcode == 1: if ('X' in d or 'Y' in d): if self.laser_mode and s <= 0.01: # do not draw non cutting lines if points: # draw accumulated points upto this point self.canv.add(Color(0, 0, 0)) self.canv.add( Line(points=points, width=1, cap='none', joint='none')) points = [] # for 3d printers (has_e) only draw if there is an E elif not has_e or 'E' in d: # if a CNC gcode file or there is an E in the G1 (3d printing) #print("draw to: {}, {}, {}".format(x, y, z)) # collect points but don't draw them yet if len(points) < 2: points.append(lastpos[0]) points.append(lastpos[1]) points.append(x) points.append(y) else: # a G1 with no E, treat as G0 and draw moves in red #print("move to: {}, {}, {}".format(x, y, z)) if points: # draw accumulated points upto this point self.canv.add(Color(0, 0, 0)) self.canv.add( Line(points=points, width=1, cap='none', joint='none')) points = [] # now draw the move in red self.canv.add(Color(1, 0, 0)) self.canv.add( Line(points=[lastpos[0], lastpos[1], x, y], width=1, cap='none', joint='none')) else: # A G1 with no X or Y, maybe E only move (retract) or Z move (layer change) if points: # draw accumulated points upto this point self.canv.add(Color(0, 0, 0)) self.canv.add( Line(points=points, width=1, cap='none', joint='none')) points = [] elif gcode in [2, 3]: # CW=2,CCW=3 circle # code cribbed from bCNC xyz = [] xyz.append((lastpos[0], lastpos[1], lastpos[2])) uc, vc = self.motionCenter(gcode, plane, lastpos, [x, y, z], i, j) if plane == XY: u0 = lastpos[0] v0 = lastpos[1] w0 = lastpos[2] u1 = x v1 = y w1 = z elif plane == XZ: u0 = lastpos[0] v0 = lastpos[2] w0 = lastpos[1] u1 = x v1 = z w1 = y gcode = 5 - gcode # flip 2-3 when XZ plane is used else: u0 = lastpos[1] v0 = lastpos[2] w0 = lastpos[0] u1 = y v1 = z w1 = x phi0 = math.atan2(v0 - vc, u0 - uc) phi1 = math.atan2(v1 - vc, u1 - uc) try: sagitta = 1.0 - CNC_accuracy / self.rval except ZeroDivisionError: sagitta = 0.0 if sagitta > 0.0: df = 2.0 * math.acos(sagitta) df = min(df, math.pi / 4.0) else: df = math.pi / 4.0 if gcode == 2: if phi1 >= phi0 - 1e-10: phi1 -= 2.0 * math.pi ws = (w1 - w0) / (phi1 - phi0) phi = phi0 - df while phi > phi1: u = uc + self.rval * math.cos(phi) v = vc + self.rval * math.sin(phi) w = w0 + (phi - phi0) * ws phi -= df if plane == XY: xyz.append((u, v, w)) elif plane == XZ: xyz.append((u, w, v)) else: xyz.append((w, u, v)) else: if phi1 <= phi0 + 1e-10: phi1 += 2.0 * math.pi ws = (w1 - w0) / (phi1 - phi0) phi = phi0 + df while phi < phi1: u = uc + self.rval * math.cos(phi) v = vc + self.rval * math.sin(phi) w = w0 + (phi - phi0) * ws phi += df if plane == XY: xyz.append((u, v, w)) elif plane == XZ: xyz.append((u, w, v)) else: xyz.append((w, u, v)) xyz.append((x, y, z)) # plot the points points = [] for t in xyz: x1, y1, z1 = t points.append(x1) points.append(y1) max_x = max(x1, max_x) min_x = min(x1, min_x) max_y = max(y1, max_y) min_y = min(y1, min_y) self.canv.add(Color(0, 0, 0)) self.canv.add( Line(points=points, width=1, cap='none', joint='none')) points = [] # always remember last position lastpos = [x, y, z] laste = e lasts = s if not found_layer: # we hit the end of file before finding the layer we want Logger.info( "GcodeViewerScreen: last layer was at {}".format(lastz)) self.last_target_layer -= 1 return # flush any points not yet drawn if points: # draw accumulated points upto this point self.canv.add(Color(0, 0, 0)) self.canv.add( Line(points=points, width=1, cap='none', joint='none')) points = [] # center the drawing and scale it dx = max_x - min_x dy = max_y - min_y if dx == 0 or dy == 0: Logger.warning( "GcodeViewerScreen: size is bad, maybe need 2D mode") return self.bounds = [dx, dy] dx += 4 dy += 4 Logger.debug("GcodeViewerScreen: dx= {}, dy= {}".format(dx, dy)) # add in the translation to center object self.tx = -min_x - dx / 2 self.ty = -min_y - dy / 2 self.canv.insert(1, Translate(self.tx, self.ty)) Logger.debug("GcodeViewerScreen: tx= {}, ty= {}".format( self.tx, self.ty)) # scale the drawing to fit the screen if abs(dx) > abs(dy): scale = self.ids.surface.width / abs(dx) if abs(dy) * scale > self.ids.surface.height: scale *= self.ids.surface.height / (abs(dy) * scale) else: scale = self.ids.surface.height / abs(dy) if abs(dx) * scale > self.ids.surface.width: scale *= self.ids.surface.width / (abs(dx) * scale) Logger.debug("GcodeViewerScreen: scale= {}".format(scale)) self.scale = scale self.canv.insert(1, Scale(scale)) # translate to center of canvas self.offs = self.ids.surface.center self.canv.insert( 1, Translate(self.ids.surface.center[0], self.ids.surface.center[1])) Logger.debug("GcodeViewerScreen: cx= {}, cy= {}".format( self.ids.surface.center[0], self.ids.surface.center[1])) Logger.debug("GcodeViewerScreen: sx= {}, sy= {}".format( self.ids.surface.size[0], self.ids.surface.size[1])) # axis Markers self.canv.add(Color(0, 1, 0, mode='rgb')) self.canv.add( Line(points=[0, -10, 0, self.ids.surface.height / scale], width=1, cap='none', joint='none')) self.canv.add( Line(points=[-10, 0, self.ids.surface.width / scale, 0], width=1, cap='none', joint='none')) # tool position marker if self.app.is_connected: x = self.app.wpos[0] y = self.app.wpos[1] r = (10.0 / self.ids.surface.scale) / scale self.canv.add(Color(1, 0, 0, mode='rgb', group="tool")) self.canv.add(Line(circle=(x, y, r), group="tool")) # self.canv.add(Rectangle(pos=(x, y-r/2), size=(1/scale, r), group="tool")) # self.canv.add(Rectangle(pos=(x-r/2, y), size=(r, 1/scale), group="tool")) self.canv.add(PopMatrix()) self._loaded_ok = True Logger.debug("GcodeViewerScreen: done loading")
def _set_rotation(self, rotation): angle_change = self.rotation - rotation r = Matrix().rotate(-radians(angle_change), 0, 0, 1) self.apply_transform(r, post_multiply=True, anchor=self.to_local(*self.center))
def _do_reset_scatter(self, val): # move the mat = Matrix().scale(10, 10, 10).translate(0, -150, 0) self._scatter.transform = mat
def _set_center(self, center): if center == self.center: return False t = Vector(*center) - self.center trans = Matrix().translate(t.x, t.y, 0) self.apply_transform(trans)
def build(self): self.init_GPIO() #layout = BoxLayout(orientation='vertical') layout = FloatLayout(size=(800, 480), pos=(0, 0)) self._toggle = ToggleButton(text='LED OFF', size_hint=(0.2, 0.2), pos_hint={'pos': (0.8, 0)}) self._snap = Button(text='Capture', size_hint=(0.2, 0.2), pos_hint={'pos': (0.8, 0.2)}) self._snapref = Button(text='Reference', size_hint=(0.2, 0.2), pos_hint={'pos': (0.8, 0.4)}) # self._demo = Button(text='Demo Results', # size_hint=(0.2,0.1), pos_hint={'pos': (0.0, 0.7)}) self._auto_centroid = ImageButton( size_hint=(0.1, 0.1), pos_hint={'pos': (0.7, 0)}, source='/home/pi/2.131-UI/img/distribution_centroid_off.png') self._object_detection = ImageButton( size_hint=(0.1, 0.1), pos_hint={'pos': (0.7, 0.1)}, source='/home/pi/2.131-UI/img/object_detection_off.png') self._reset_scatter = ImageButton( size_hint=(0.1, 0.1), pos_hint={'pos': (0.7, 0.2)}, source='/home/pi/2.131-UI/img/reset_scatter.png') self._exit = Button(text='X', size_hint=(0.05, 0.05), pos_hint={'pos': (0.95, 0.95)}) self._fps = Label(text='FPS: 0', size_hint=(0.1, 0.1), pos_hint={'pos': (0.8, 0.9)}) self._temp = Label(text='Temp: 0', size_hint=(0.1, 0.1), pos_hint={'pos': (0.6, 0.9)}) dev_ip = self.get_ip_address() self._ip = Label(text='IP: %s' % dev_ip, size_hint=(0.1, 0.1), pos_hint={'pos': (0.3, 0.9)}) self._uploading = Label(text='Uploading...', size_hint=(0.2, 0.1), pos_hint={'pos': (-1, -1)}, color=[0, 0, 1, 0]) self._uploadingAmt = Label(text='', size_hint=(0.2, 0.1), pos_hint={'pos': (-1, -1)}, color=[0, 0, 1, 0]) self._exposure = Label(text='Exposure: 0', size_hint=(0.2, 0.1), pos_hint={'pos': (0, 0)}) self._centroid = Label(text='C:0', size_hint=(0.1, 0.1), pos_hint={'pos': (0.79, 0.83)}, color=[1, 0, 0, 1]) self._exposure_slider = Slider(min=0, max=2500, value=1358, size_hint=(0.5, 0.1), pos_hint={'pos': (0.2, 0)}) self._upload_progress = ProgressBar(max=100, size_hint=(0.5, 0.1), pos_hint={'pos': (-1, -1)}) # self._camera = Camera2131(resolution=(1280,960), # fourcc="GREY", # capture_resolution=(3872, 2764), # capture_fourcc="Y16 ", # size_hint=(1,1), # pos_hint={'pos':(0,0)}, # play=True, ) self._camera = Camera2131( resolution=(640, 480), fourcc="GREY", capture_resolution=(640, 480), capture_fourcc="GREY", size_hint=(1, 1), pos_hint={'pos': (0, 0)}, play=True, ) # self._camera = Camera2131(resolution=(1280,720), # play=True, fourcc="GREY") # self._camera = Camera2131(resolution=(3872, 2764), # play=True, fourcc="Y16 ") # self._camera = Camera2131(resolution=(1920,1080), # play=True, fourcc="GREY") # self._camera = Camera2131(resolution=(2560, 1920), # play=True, fourcc="GREY") self._dropdown = DropDown() # create a big main button # self._imageResultsButton = Button(text='Image Explorer',pos_hint={'pos': (0.0, 0.6)}, size_hint=(0.2, 0.1)) # show the dropdown menu when the main button is released # note: all the bind() calls pass the instance of the caller (here, the # mainbutton instance) as the first argument of the callback (here, # dropdown.open.). # self._imageResultsButton.bind(on_release=self._dropdown.open) # one last thing, listen for the selection in the dropdown list and # assign the data to the button text. # self._dropdown.bind(on_select=lambda instance, x: setattr(self._imageResultsButton, 'text', x)) self._histogram = Histogram(size_hint=(0.2, 0.3), pos_hint={'pos': (0.8, 0.6)}) # self._demo.bind(on_press=self._show_demo_results) self._toggle.bind(on_press=self._led_toggle) self._snap.bind(on_press=self._request_capture) self._snapref.bind(on_press=self._request_ref_capture) self._exit.bind(on_press=self._exit_app) self._auto_centroid.bind(on_press=self._auto_change_exposure) self._object_detection.bind(on_press=self._toggle_object_detection) self._exposure_slider.bind(value=self._change_exposure) self._reset_scatter.bind(on_press=self._do_reset_scatter) #update.bind(on_press=self._update_histogram) self._camera.fbind('on_frame_complete', self._update_histogram) self._scatter = Scatter( size_hint=(None, None), size=(200, 200), ) self._scatter.add_widget(self._camera) #layout.add_widget(self._camera) layoutInner = FloatLayout(size_hint=(0.8, 1), pos_hint={ 'x': 0, 'y': 0 }) layoutInner.add_widget(self._scatter) layout.add_widget(layoutInner) mat = Matrix().scale(10, 10, 10).translate(0, -150, 0) self._scatter.apply_transform(mat) # layout.add_widget(self._imageResultsButton) layout.add_widget(self._uploading) layout.add_widget(self._uploadingAmt) # layout.add_widget(self._demo) layout.add_widget(self._histogram) layout.add_widget(self._snap) layout.add_widget(self._snapref) layout.add_widget(self._exit) layout.add_widget(self._centroid) layout.add_widget(self._exposure_slider) layout.add_widget(self._upload_progress) layout.add_widget(self._auto_centroid) layout.add_widget(self._object_detection) layout.add_widget(self._reset_scatter) layout.add_widget(self._exposure) layout.add_widget(self._fps) layout.add_widget(self._temp) layout.add_widget(self._ip) Clock.schedule_interval(self._update_fps, 2) layout.add_widget(self._toggle) #layout.add_widget(update) self._is_updating = False # self.updateImages() return layout
def transform_with_touch(self, touch): changed = False x = self.bbox[0][0] y = self.bbox[0][1] width = self.bbox[1][0] height = self.bbox[1][1] mid_x = x + width / 2 mid_y = y + height / 2 inner_width = width * 0.5 inner_height = height * 0.5 left = mid_x - (inner_width / 2) right = mid_x + (inner_width / 2) top = mid_y + (inner_height / 2) bottom = mid_y - (inner_height / 2) # just do a simple one finger drag if len(self._touches) == self.translation_touches: # _last_touch_pos has last pos in correct parent space, # just like incoming touch dx = (touch.x - self._last_touch_pos[touch][0]) \ * self.do_translation_x dy = (touch.y - self._last_touch_pos[touch][1]) \ * self.do_translation_y dx = dx / self.translation_touches dy = dy / self.translation_touches if ( touch.x > left and touch.x < right and touch.y < top and touch.y > bottom or self.move_lock ) and not self.scale_lock_left and not self.scale_lock_right and not self.scale_lock_top and not self.scale_lock_bottom: self.move_lock = True self.apply_transform(Matrix().translate(dx, dy, 0)) changed = True change_x = touch.x - self.prev_x change_y = touch.y - self.prev_y anchor_sign = 1 sign = 1 if abs( change_x ) >= 9 and not self.move_lock and not self.scale_lock_top and not self.scale_lock_bottom: if change_x < 0: sign = -1 if (touch.x < left or self.scale_lock_left) and not self.scale_lock_right: self.scale_lock_left = True self.pos = (self.pos[0] + (sign * 10), self.pos[1]) anchor_sign = -1 elif (touch.x > right or self.scale_lock_right) and not self.scale_lock_left: self.scale_lock_right = True self.size[0] = self.size[0] + (sign * anchor_sign * 10) self.prev_x = touch.x changed = True if abs( change_y ) >= 9 and not self.move_lock and not self.scale_lock_left and not self.scale_lock_right: if change_y < 0: sign = -1 if (touch.y > top or self.scale_lock_top) and not self.scale_lock_bottom: self.scale_lock_top = True elif (touch.y < bottom or self.scale_lock_bottom) and not self.scale_lock_top: self.scale_lock_bottom = True self.pos = (self.pos[0], self.pos[1] + (sign * 10)) anchor_sign = -1 self.size[1] = self.size[1] + (sign * anchor_sign * 10) self.prev_y = touch.y changed = True return changed
def build(self): self.init_GPIO() layout = FloatLayout(size=(800, 480), pos=(0, 0)) self._toggle = ToggleButton(text='LED OFF', size_hint=(0.2, 0.2), pos_hint={'pos': (0.8, 0)}) self._snap = Button(text='COUNT', size_hint=(0.2, 0.2), pos_hint={'pos': (0.8, 0.2)}) self._object_detection = ImageButton( size_hint=(0.1, 0.1), pos_hint={'pos': (0.7, 0.0)}, source=self.img_path('object_detection_off.png')) self._reset_scatter = ImageButton( size_hint=(0.1, 0.1), pos_hint={'pos': (0.7, 0.1)}, source=self.img_path('reset_scatter.png')) self._logo = Image(size_hint=(0.3, 0.2), pos_hint={'pos': (-0.03, 0.84)}, source=self.img_path('ids.png')) self._exit = ImageButton(size_hint=(0.05, 0.05), pos_hint={'pos': (0.96, 0.95)}, source=self.img_path('close-white.png')) red, white, kivy_blue = (1, 0, 0, 1), (1, 1, 1, 1), (51. / 255, 164. / 255, 206. / 255, 1) self._fps = Label(text='FPS: 0', size_hint=(0.1, 0.1), pos_hint={'pos': (0.8, 0.9)}, color=red) self._temp = Label(text='Temp: 0', size_hint=(0.1, 0.1), pos_hint={'pos': (0.6, 0.9)}, color=white) dev_ip = self.get_ip_address() self._ip = Label(text='IP: %s' % dev_ip, size_hint=(0.1, 0.1), pos_hint={'pos': (0.4, 0.9)}, color=white) self._exposure = Label(text='Exposure: 0', size_hint=(0.2, 0.1), pos_hint={'pos': (0, 0)}) self._centroid = Label(text='C:0', size_hint=(0.1, 0.1), pos_hint={'pos': (0.79, 0.83)}, color=white) self._exposure_slider = Slider(min=0, max=2500, value=1358, size_hint=(0.5, 0.1), pos_hint={'pos': (0.2, 0)}) # self._camera = Camera2131(resolution=(1280,960), # fourcc="GREY", # capture_resolution=(3872, 2764), # capture_fourcc="Y16 ", # size_hint=(1,1), # pos_hint={'pos':(0,0)}, # play=True, ) # self._camera = CameraIDS(resolution=(640, 480), # fourcc="GREY", # capture_resolution=(640, 480), # capture_fourcc="GREY", # size_hint=(1,1), # pos_hint={'pos':(0,0)}, # play=True) self._histogram = Histogram(size_hint=(0.2, 0.25), pos_hint={'pos': (0.8, 0.65)}) self._tracker_histogram = TrackerHistogram( size_hint=(0.2, 0.25), pos_hint={'pos': (0.8, 0.4)}) # self._roi_tracker = ROITracker(self._camera.resolution, # size_hint=(0.2,0.25), pos_hint={'pos':(0.02,0.6)}) # self._roi_tracker.bind(roi_Offset=self._update_roi) # self._demo.bind(on_press=self._show_demo_results) self._toggle.bind(on_press=self._led_toggle) self._snap.bind(on_press=self._start_count) self._exit.bind(on_press=self._exit_app) self._object_detection.bind(on_press=self._toggle_object_detection) self._exposure_slider.bind(value=self._change_exposure) self._reset_scatter.bind(on_press=self._do_reset_scatter) # self._camera.fbind('on_frame_complete',self._update_histogram) self._scatter = Scatter( size_hint=(None, None), size=(200, 200), ) # self._scatter.add_widget(self._camera) # layoutInner = FloatLayout(size_hint=(0.8, 1), pos_hint={'x':0,'y':0}) # layoutInner.add_widget(self._scatter) layout.add_widget(self._scatter) mat = Matrix().scale(10, 10, 10).translate(0, -150, 0) self._scatter.apply_transform(mat) layout.add_widget(self._histogram) layout.add_widget(self._tracker_histogram) layout.add_widget(self._snap) layout.add_widget(self._exit) layout.add_widget(self._exposure_slider) layout.add_widget(self._object_detection) layout.add_widget(self._reset_scatter) layout.add_widget(self._exposure) layout.add_widget(self._fps) layout.add_widget(self._temp) layout.add_widget(self._ip) Clock.schedule_interval(self._update_fps, 2) layout.add_widget(self._toggle) layout.add_widget(self._logo) # layout.add_widget(self._roi_tracker) self._is_updating = False return layout