def drawTextInput(self,touch): start, pos = self.touch_time[touch.id] start = start if start else time.time() elaspsed_time = time.time() - start idu = self.touch_keys[touch.id] Log.debug('Elapsed time:%s' % elaspsed_time) if elaspsed_time >= 1: distance = Vector.distance( Vector(pos.sx, pos.sy), Vector(touch.osxpos, touch.osypos)) Log.debug('Screen coordinate Distance:%f vs %f' % (distance,self.press_and_hold_distance)) _l = len(self.touch_positions[idu]['Cdata']) Log.debug('Num points:%d' % _l) _vd = Vector.distance(\ Vector(*self.touch_positions[idu]['Cdata'][0]),\ Vector(*self.touch_positions[idu]['Cdata'][_l-1])) Log.debug('touch distance :%f and %d' % (_vd, int(_vd))) if distance <= self.press_and_hold_distance and\ (_l < self.travel_limit or int(_vd) < self.travel_limit): txt = ScribbleTextWidget(pos=touch.pos, group=self.session, keyboard=self.keyboard, cls='scribbleKeyboardcss') txt.push_handlers(on_transform=curry(self.on_transform,txt)) self.add_widget(txt) self.disable_all() txt.enable() d = txt.to_dic() self.dispatch_event('on_text_change', d) return True
def on_popup_draw(self): self._xml.root.center = self.get_parent_window().center popup = self._xml.getById('popup') set_color(*self.style.get('bg-color-full')) drawCSSRectangle(pos=Vector(popup.pos) - (10, 10), size=Vector(popup.size) + (20, 20), style=self.style)
def transform_with_touch(self, touch): # just do a simple one finger drag if len(self._touches) == 1: return self._apply_drag(touch) # 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.dpos) - anchor new_line = Vector(*touch.pos) - anchor angle = radians( new_line.angle(old_line) ) * self._do_rotation 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(rotation_matrix(angle, (0, 0, 1)), anchor=anchor) self.apply_transform(scale_matrix(scale), anchor=anchor) #dispatch on_transform with th touch that caused it self.dispatch_event('on_transform', touch)
def collide_point(self, x, y): #A algorithm to find the whether a touch is within a semi ring cx, cy = self.center point_dist = Vector(self.center).distance((x, y)) point_angle = Vector(self._radius_line).angle((x - cx, y - cy)) if point_angle < 0: point_angle = 360. + point_angle if 0 < point_angle > self.sweep_angle: return False return self.radius - self.thickness < point_dist <= self.radius
def get_rigid_rotation(self, dstpts): ''' Extract the rotation to apply to a group of points to minimize the distance to a second group of points. The two groups of points are assumed to be centered. This is a simple version that just pick an angle based on the first point of the gesture. ''' if len(self.strokes) < 1 or len(self.strokes[0].points) < 1: return 0 if len(dstpts.strokes) < 1 or len(dstpts.strokes[0].points) < 1: return 0 target = Vector( [dstpts.strokes[0].points[0].x, dstpts.strokes[0].points[0].y] ) source = Vector( [self.strokes[0].points[0].x, self.strokes[0].points[0].y] ) return source.angle(target)
def transform_with_touch(self, touch): # just do a simple one finger drag if len(self._touches) == 1: return self._apply_drag(touch) # 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.dpos) - anchor new_line = Vector(*touch.pos) - anchor angle = radians(new_line.angle(old_line)) * self._do_rotation 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(rotation_matrix(angle, (0, 0, 1)), anchor=anchor) self.apply_transform(scale_matrix(scale), anchor=anchor) #dispatch on_transform with th touch that caused it self.dispatch_event('on_transform', touch)
def __init__(self, **kwargs): kwargs.setdefault('radius', 200) super(MTVectorSlider, self).__init__(**kwargs) self.radius = kwargs.get('radius') self.vector = Vector(self.x + self.radius, self.y) self.amplitude = 0 self.angle = 0 self.register_event_type('on_amplitude_change') self.register_event_type('on_angle_change') self.register_event_type('on_vector_change')
def _set_pos(self, pos): _pos = self.bbox[0] if pos == _pos: return t = Vector(*pos) - _pos trans = translation_matrix((t.x, t.y, 0)) self.apply_transform(trans)
def apply_angle_scale_trans(self, angle, scale, trans, point=Vector(0, 0)): '''Update matrix transformation by adding new angle, scale and translate. :Parameters: `angle` : float Rotation angle to add `scale` : float Scaling value to add `trans` : Vector Vector translation to add `point` : Vector, default to (0, 0) Point to apply transformation ''' old_scale = self.scale new_scale = old_scale * scale if new_scale < self.scale_min or old_scale > self.scale_max: scale = 1 t = translation_matrix((trans[0] * self._do_translation_x, trans[1] * self._do_translation_y, 0)) t = matrix_multiply(t, translation_matrix((point[0], point[1], 0))) t = matrix_multiply(t, rotation_matrix(angle, (0, 0, 1))) t = matrix_multiply(t, scale_matrix(scale)) t = matrix_multiply(t, translation_matrix((-point[0], -point[1], 0))) self.apply_transform(t) self.dispatch_event('on_transform', None)
def get_rigid_rotation(self, dstpts): ''' Extract the rotation to apply to a group of points to minimize the distance to a second group of points. The two groups of points are assumed to be centered. This is a simple version that just pick an angle based on the first point of the gesture. ''' if len(self.strokes) < 1 or len(self.strokes[0].points) < 1: return 0 if len(dstpts.strokes) < 1 or len(dstpts.strokes[0].points) < 1: return 0 target = Vector( [dstpts.strokes[0].points[0].x, dstpts.strokes[0].points[0].y]) source = Vector( [self.strokes[0].points[0].x, self.strokes[0].points[0].y]) return source.angle(target)
def on_touch_down(self, touch): if self.state == 'dragging': return False if self.collide_point(touch.x, touch.y): self.state = 'dragging' touch.grab(self) touch.userdata['touch_offset'] = Vector(self.pos) - touch.pos return True
def process(self, events): # check if module is disabled if self.timeout == 0: return events d = time.time() for type, touch in events[:]: if type == 'up': events.remove((type, touch)) if touch.uid in self._links: selection = self._links[touch.uid] selection.userdata['__retain_time'] = d self._available.append(selection) del self._links[touch.uid] else: touch.userdata['__retain_time'] = d self._available.append(touch) elif type == 'move': if touch.uid in self._links: selection = self._links[touch.uid] selection.x = touch.x selection.y = touch.y selection.sx = touch.sx selection.sy = touch.sy events.remove((type, touch)) events.append((type, selection)) else: pass elif type == 'down': # new touch, found the nearest one selection = None selection_distance = 99999 for touch2 in self._available: touch_distance = Vector(touch2.spos).distance(touch.spos) if touch_distance > self.distance: continue if touch2.__class__ != touch.__class__: continue if touch_distance < selection_distance: # eligible for continuation selection_distance = touch_distance selection = touch2 if selection is None: continue self._links[touch.uid] = selection self._available.remove(selection) events.remove((type, touch)) for touch in self._available[:]: t = touch.userdata['__retain_time'] if d - t > self.timeout: self._available.remove(touch) events.append(('up', touch)) return events
def rotate(self, angle): g = Gesture() for stroke in self.strokes: tmp = [] for j in stroke.points: v = Vector([j.x, j.y]).rotate(angle) tmp.append(v) g.add_stroke(tmp) g.gesture_product = g.dot_product(g) return g
def find_double_tap(self, ref): '''Find a double tap touch within self.touches. The touch must be not a previous double tap, and the distance must be ok''' for touchid in self.touches: if ref.uid == touchid: continue type, touch = self.touches[touchid] if type != 'up': continue if touch.is_double_tap: continue distance = Vector.distance(Vector(ref.sx, ref.sy), Vector(touch.osxpos, touch.osypos)) if distance > self.double_tap_distance: continue touch.double_tap_distance = distance return touch return None
def on_touch_up(self, touch): # accept only the touch we've got first. if touch.grab_current != self: return touch.ungrab(self) self._touch = None # animate the transition to back to 0 # cover will back to position in nicer way self._animation = True # launch on_select ? if not touch.userdata['coverflow.noclick']: distance = Vector(touch.userdata['coverflow.firstpos']).distance( Vector(touch.pos)) if distance <= self.trigger_distance: self.dispatch_event('on_select', self.children[self._selection]) return True
def draw(self): # extract relative position rx, ry = self.relpos # calculate triangle mx = self.x + rx + self.width * 0.5 + self.trirelpos[0] my = self.y + ry + self.height * 0.5 + self.trirelpos[1] angle = Vector(1, 0).angle(Vector(mx - self.x, my - self.y)) vpos = Vector(mx, my) v1 = Vector(self.trisize, 0).rotate(angle) + vpos v2 = Vector(-self.trisize, 0).rotate(angle) + vpos # draw border if self.bordersize > 0: drawRoundedRectangle( pos=(self.x - self.padding - self.bordersize + rx, self.y - self.padding - self.bordersize + ry), size=(self.width + self.padding * 2 + self.bordersize * 2, self.height + self.padding * 2 + self.bordersize * 2), radius=self.radius, color=self.bordercolor) glEnable(GL_LINE_SMOOTH) glLineWidth(self.bordersize * 2) drawPolygon((self.x, self.y, v1.x, v1.y, v2.x, v2.y), style=GL_LINE_LOOP) # draw background drawRoundedRectangle(pos=(self.x - self.padding + rx, self.y - self.padding + ry), size=(self.width + self.padding * 2, self.height + self.padding * 2), radius=self.radius, color=self.bgcolor) drawPolygon((self.x, self.y, v1.x, v1.y, v2.x, v2.y)) # hack to translate label position with gx_matrix: glTranslatef(rx, ry, 0) super(MTSpeechBubble, self).draw()
def _calculate_angle(self, x, y): cx, cy = self.center self._last_touch = x - cx, y - cy angle = Vector(self._radius_line).angle(self._last_touch) if angle < 0: angle += 360 try: self.value = angle * (self.max - self.min) / \ self.sweep_angle + self.min self._slider_angle = angle except RangeException: pass self.dispatch_event('on_value_change', self._value)
def __init__(self, **kwargs): kwargs.setdefault('radius', 200) super(MTVectorSlider, self).__init__(**kwargs) self.radius = kwargs.get('radius') self.vector = Vector(self.x+self.radius, self.y) self.amplitude = 0 self.angle = 0 self.register_event_type('on_amplitude_change') self.register_event_type('on_angle_change') self.register_event_type('on_vector_change')
def circumcircle(a, b, c): ''' Computes the circumcircle of a triangel defined by a,b,c see: http://en.wikipedia.org/wiki/Circumscribed_circle#Circumscribed_circles_of_triangles :Parameters: `a` : iterable the 1. point of the triangle `b` : iterable the 2. point of the triangle `c` : iterable the 3. point of the triangle :Return: A Circle that defined the tuple : * The first element in the returned touple is the center (tuple x,y) * The second the radius (float) ''' P = Vector(a[0], a[1]) Q = Vector(b[0], b[1]) R = Vector(c[0], c[1]) mPQ = (P + Q) * .5 mQR = (Q + R) * .5 numer = -(-mPQ.y*R.y + mPQ.y*Q.y + mQR.y*R.y - mQR.y*Q.y \ -mPQ.x*R.x + mPQ.x*Q.x + mQR.x*R.x - mQR.x*Q.x) denom = (-Q.x * R.y + P.x * R.y - P.x * Q.y + Q.y * R.x - P.y * R.x + P.y * Q.x) t = numer / denom cx = -t * (Q.y - P.y) + mPQ.x cy = t * (Q.x - P.x) + mPQ.y return ((cx, cy), (P - (cx, cy)).length())
def find_double_tap(self, ref): """Find a double tap touch within self.touches. The touch must be not a previous double tap, and the distance must be ok""" for touchid in self.touches: if ref.id == touchid: continue type, touch = self.touches[touchid] if type != "up": continue if touch.is_double_tap: continue distance = Vector.distance(Vector(ref.sx, ref.sy), Vector(touch.osxpos, touch.osypos)) if distance > self.double_tap_distance: continue touch.double_tap_distance = distance return touch return None
def get_key_at_pos(self, x, y): '''Return the key + size info on the current layout, at the coordinate (x, y)''' mtop, mright, mbottom, mleft = self.style['margin'] w, h = self.width - mleft - mright, self.height - mtop - mbottom kx, ky = self.layout.SIZE keysize = Vector(w / kx, h / ky) if x < mleft or x > self.width - mright or \ y < mbottom or y > self.height - mtop: return None index = ky-int((y - mbottom) / (self.height - mtop - mbottom) * ky) line = self.layout.__getattribute__('%s_%d' % (self.mode, index)) x -= mleft kx = 0 for key in line: kw = keysize.x * key[3] if x >= kx and x < kx + kw: h = (self.height - mtop - mbottom) / ky return (key, (kx, h * (ky-index), kw, h)) kx += kw return None
def should_delete(self,touch_p, line_p): distance = Vector.distance( Vector(*line_p), #IGNORE:W0142 Vector(*touch_p)) #IGNORE:W0142 if distance <= self.delete_distance: return True
def _get_rotation(self): # v1 = vetor from (0,0) to (0,10) # v2 = vector from center to center + (0,10) (in widget space) v1 = Vector(0,10) v2 = Vector(*self.to_parent(*self.pos)) - self.to_parent(self.x, self.y+10) return -1.0 *(v1.angle(v2) + 180) % 360
def on_press(self, touch): self.orig = Vector(self.to_window(*touch.pos))
class ScribbleText(MyTextArea): def __init__(self, **kwargs): kwargs.setdefault('padding_x', 3) kwargs.setdefault('autosize', True) kwargs.setdefault('cls', 'mytextinput') kwargs.setdefault('style',{'font-size': kwargs['font-size']}) super(ScribbleText, self).__init__(**kwargs) self.orig = (0, 0) self.label_obj.options['font_size'] = self.style['font-size'] self.label_obj.refresh() def _recalc_size(self): # We could do this as .size property I suppose, but then we'd # be calculating it all the time when .size is accessed. num = len(self.lines) if not num: return # The following two if statements ensure that the textarea remains # easily clickable even if there's no content. if self.autosize or self.autoheight: self.height = num * self.line_height + self.line_spacing * (num - 1) if (self.autosize or self.autowidth): self.width = max(label.content_width for label in self.line_labels) + 20 def on_press(self, touch): self.orig = Vector(self.to_window(*touch.pos)) def on_release(self, touch): final = Vector(self.to_window(*touch.pos)) if self.orig.distance(final) <= 4: if not self.is_active_input: self.parent.disable_all() #IGNORE:E1101 self._can_deactive = True super(ScribbleText, self).on_release(touch) # def show_keyboard(self): # super(MyTextArea,self).show_keyboard() # to_root = self.keyboard_to_root # if(to_root): # w = self.get_root_window() if to_root else self.get_parent_window() # w.remove_widget(self.keyboard) # #we want to add this keyboard to the innerwindow border # #self.parent.parent.parent.parent.show_keyboard(self.keyboard) # self.parent.parent.parent.parent.add_widget(self.keyboard) # #self.keyboard.pos = self.to_window(self.pos[0], self.pos[1] - self.height - self.keyboard.height) #position of the text input field # # # # def hide_keyboard(self): # if self._is_active_input: # self.parent.parent.parent.parent.set_button_image() # super(ScribbleText, self).hide_keyboard() # p = self.parent # if(p): # pp = p.parent # if(pp): # ppp = pp.parent # if(ppp): # p4 = ppp.parent # if(p4): # #p4.hide_keyboard(self.keyboard) # p4.remove_widget(self.keyboard) def on_touch_down(self, touch): super(ScribbleText, self).on_touch_down(touch) return False
def minimum_bounding_circle(points): ''' Returns the minimum bounding circle for a set of points For a description of the problem being solved see http://en.wikipedia.org/wiki/Smallest_circle_problem The function uses Applet's Algorithm Algorithm, worst case teh runtime is O(h^3 *n), where h= number of points in teh convex hull of the set of points. But it runs in linear time in almost all real world cases. see: http://www.personal.kent.edu/~rmuhamma/Compgeometry/MyCG/CG-Applets/Center/centercli.htm :Parameters: `points` : iterable A list of points (2 tuple with x,y coordinates) :Return: A Circle that defined the tuple : * The first element in the returned touple is the center (tuple x,y) * The second the radius (float) ''' points = [Vector(p[0], p[1]) for p in points] if len(points) == 1: return (points[0].x, points[0].y), 0.0 if len(points) == 2: p1, p2 = points return (p1 + p2) * .5, ((p1 - p2) * .5).length() # determine a point P with the smallest y value P = min(points, key=lambda p: p.y) # find a point Q such that the angle of the line segment # PQ with the x axis is minimal def x_axis_angle(q): if q == P: return 1e10 # max val if teh same, to skip return abs((q - P).angle((1, 0))) Q = min(points, key=x_axis_angle) for p in points: # find R such that angle PRQ is minimal def angle_pq(r): if r in (P, Q): return 1e10 # max val if teh same, to skip return abs((r - P).angle(r - Q)) R = min(points, key=angle_pq) # check for case 1 (angle PRQ is obtuse), the circle is determined # by two points, P and Q. radius = |(P-Q)/2|, center = (P+Q)/2 if angle_pq(R) > 90.0: return (P + Q) * .5, ((P - Q) * .5).length() # if angle RPQ is obtuse, make P = R, and try again if abs((R - P).angle(Q - P)) > 90: P = R continue # if angle PQR is obtuse, make Q = R, and try again if abs((P - Q).angle(R - Q)) > 90: Q = R continue # all angles were acute..we just need teh circle through the # two points furthest apart! break # find teh circumcenter for triangle given by P,Q,R return circumcircle(P, Q, R)
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 _get_rotation(self): v1 = Vector(0, 10) v2 = Vector(*self.to_parent(*self.pos)) - self.to_parent( self.x, self.y + 10) return -1.0 * (v1.angle(v2) + 180) % 360
def _do_update(self, mode=None): # we absolutly want mode to update displaylist. if mode not in ('background', 'keys'): return # don't update background if it's already compiled if mode == 'background' and self._current_cache['background'].is_compiled(): return # calculate margin s = self.scale w, h = self.container_width, self.container_height if mode == 'background': s = 1. w, h = self.size mtop, mright, mbottom, mleft = map(lambda x: x * s, self.style['margin']) self.texsize = Vector(w - mleft - mright, h - mtop - mbottom) kx, ky = self.layout.SIZE self.keysize = Vector(self.texsize.x / kx, self.texsize.y / ky) m = 3 * s x, y = 0, self.texsize.y - self.keysize.y # update display list self._current_cache['usedlabel'] = [] with self._current_cache[mode]: # draw lines for index in xrange(1, ky + 1): line = self.layout.__getattribute__('%s_%d' % (self.mode, index)) # draw keys for key in line: displayed_str, internal_str, internal_action, scale = key kw = self.keysize.x * scale # don't display empty keys if displayed_str is not None: set_color(*self.style['key-color']) if mode == 'background': if internal_action is not None: set_color(*self.style['syskey-color']) drawCSSRectangle( pos=(x+m, y+m), size=(kw-m*2, self.keysize.y-m*2), style=self.style, prefix='key') elif mode == 'keys': font_size = int(14 * s) if font_size < 8: font_size = 8 color = self.style['color'] if internal_action is not None: color = self.style['color-syskey'] drawLabel(label=displayed_str, pos=(x + kw / 2., y + self.keysize.y / 2.), font_size=font_size, bold=False, font_name=self.style.get('font-name'), color=color) self._current_cache['usedlabel'].append(getLastLabel()) # advance X x += kw # advance Y y -= self.keysize.y x = 0 # update completed self._need_update = None
def _set_center(self, center): if center == self.center: return False t = Vector(*center) - self.center trans = translation_matrix((t.x, t.y, 0)) self.apply_transform(trans)
def process_kinetic(self): '''Processing of kinetic, called in draw time.''' dt = getFrameDt() todelete = [] acceleration = self.max_acceleration for touchID in self.touch: ktouch = self.touch[touchID] if abs(ktouch.X) < 0.01: ktouch.X = 0 else: ktouch.X /= 1 + (self.friction * dt) ktouch.X = boundary(ktouch.X, -acceleration, acceleration) if abs(ktouch.Y) < 0.01: ktouch.Y = 0 else: ktouch.Y /= 1 + (self.friction * dt) ktouch.Y = boundary(ktouch.Y, -acceleration, acceleration) if ktouch.mode != 'spinning': continue # process kinetic event = '' ktouch.dxpos = ktouch.x ktouch.dypos = ktouch.y ktouch.x += ktouch.X ktouch.y += ktouch.Y if Vector(ktouch.X, ktouch.Y).length() < self.velstop: # simulation finished event = 'up' getCurrentTouches().remove(ktouch) super(MTKinetic, self).on_touch_up(ktouch) todelete.append(touchID) else: # simulation in progress event = 'move' super(MTKinetic, self).on_touch_move(ktouch) # dispatch ktouch also in grab mode for _wid in ktouch.grab_list[:]: wid = _wid() if wid is None: ktouch.grab_list.remove(_wid) continue ktouch.push() ktouch.x, ktouch.y = self.to_window(*ktouch.pos) ktouch.dxpos, ktouch.dypos = self.to_window(*ktouch.dpos) if wid.parent: ktouch.x, ktouch.y = wid.parent.to_widget( ktouch.x, ktouch.y) ktouch.dxpos, ktouch.dypos = wid.parent.to_widget( ktouch.dxpos, ktouch.dypos) else: ktouch.x, ktouch.y = wid.to_parent( *wid.to_widget(ktouch.x, ktouch.y)) ktouch.dxpos, ktouch.dypos = wid.to_parent( *wid.to_widget(ktouch.dxpos, ktouch.dypos)) ktouch.grab_current = wid ktouch.grab_state = True if event == 'move': wid.dispatch_event('on_touch_move', ktouch) else: # if the widget is not visible, the on_touch_up may have # disabled wid.register_event_type('on_touch_up') wid.dispatch_event('on_touch_up', ktouch) ktouch.grab_state = False ktouch.grab_current = None ktouch.pop() # remove finished event for touchID in todelete: del self.touch[touchID]
class MTVectorSlider(MTWidget): ''' This is a slider that provides an arrow, and allows you to manipulate it just like any other vector, adjusting its angle and amplitude. :Parameters: `radius` : int, default to 200 The radius of the whole widget :Events: `on_amplitude_change`: (amplitude) Fired when amplitude is changed `on_angle_change`: (angle) Fired when angle is changed `on_vector_change`: (amplitude, angle) Fired when vector is changed :Styles: `vector-color` : color Color of the vector `slider-color` : color Color of the triangle `bg-color` : color Background color of the slider ''' def __init__(self, **kwargs): kwargs.setdefault('radius', 200) super(MTVectorSlider, self).__init__(**kwargs) self.radius = kwargs.get('radius') self.vector = Vector(self.x+self.radius, self.y) self.amplitude = 0 self.angle = 0 self.register_event_type('on_amplitude_change') self.register_event_type('on_angle_change') self.register_event_type('on_vector_change') def on_amplitude_change(self, *largs): pass def on_angle_change(self, *largs): pass def on_vector_change(self, *largs): pass def collide_point(self, x, y): '''Because this widget is a circle, and this method as defined in MTWidget is for a square, we have to override it.''' return _get_distance(self.pos, (x, y)) <= self.radius def _calc_stuff(self): '''Recalculated the args for the callbacks''' self.amplitude = self.vector.distance(self.pos) # Make a new vector relative to the origin tvec = [self.vector[0], self.vector[1]] tvec[0] -= self.pos[0] tvec[1] -= self.pos[1] # Incase python throws float div or div by zero exception, # ignore them, we will be close enough try: self.angle = degrees(atan((int(tvec[1])/int(tvec[0])))) except Exception: pass # Ajdust quadrants so we have 0-360 degrees if tvec[0] < 0 and tvec[1] > 0: self.angle = 90 + (90 + self.angle) elif tvec[0] < 0 and tvec[1] < 0: self.angle += 180 elif tvec[0] > 0 and tvec[1] < 0: self.angle = 270 + (self.angle + 90) elif tvec[0] > 0 and tvec[1] > 0: pass def on_touch_down(self, touch): if self.collide_point(touch.x, touch.y): self.vector[0], self.vector[1] = touch.x, touch.y self._calc_stuff() self.dispatch_event('on_aplitude_change', self.amplitude) self.dispatch_event('on_angle_change', self.angle) self.dispatch_event('on_vector_change', self.amplitude, self.angle) return True def on_touch_move(self, touch): if self.collide_point(touch.x, touch.y): self.vector[0], self.vector[1] = touch.x, touch.y self._calc_stuff() self.dispatch_event('on_aplitude_change', self.amplitude) self.dispatch_event('on_angle_change', self.angle) self.dispatch_event('on_vector_change', self.amplitude, self.angle) return True def draw(self): # Background set_color(*self.style.get('bg-color')) drawCircle(self.pos, self.radius) # A good size for the hand, proportional to the size of the widget hd = self.radius / 10 # Draw center of the hand set_color(*self.style.get('vector-color')) drawCircle(self.pos, hd) # Rotate the triangle so its not skewed l = prot((self.pos[0] - hd, self.pos[1]), self.angle-90, self.pos) h = prot((self.pos[0] + hd, self.pos[1]), self.angle-90, self.pos) # Draw triable of the hand with gx_begin(GL_POLYGON): glVertex2f(*l) glVertex2f(*h) glVertex2f(self.vector[0], self.vector[1])
class MTVectorSlider(MTWidget): ''' This is a slider that provides an arrow, and allows you to manipulate it just like any other vector, adjusting its angle and amplitude. :Parameters: `radius` : int, default to 200 The radius of the whole widget :Events: `on_amplitude_change`: (amplitude) Fired when amplitude is changed `on_angle_change`: (angle) Fired when angle is changed `on_vector_change`: (amplitude, angle) Fired when vector is changed :Styles: `vector-color` : color Color of the vector `slider-color` : color Color of the triangle `bg-color` : color Background color of the slider ''' def __init__(self, **kwargs): kwargs.setdefault('radius', 200) super(MTVectorSlider, self).__init__(**kwargs) self.radius = kwargs.get('radius') self.vector = Vector(self.x + self.radius, self.y) self.amplitude = 0 self.angle = 0 self.register_event_type('on_amplitude_change') self.register_event_type('on_angle_change') self.register_event_type('on_vector_change') def on_amplitude_change(self, *largs): pass def on_angle_change(self, *largs): pass def on_vector_change(self, *largs): pass def collide_point(self, x, y): '''Because this widget is a circle, and this method as defined in MTWidget is for a square, we have to override it.''' return _get_distance(self.pos, (x, y)) <= self.radius def _calc_stuff(self): '''Recalculated the args for the callbacks''' self.amplitude = self.vector.distance(self.pos) # Make a new vector relative to the origin tvec = [self.vector[0], self.vector[1]] tvec[0] -= self.pos[0] tvec[1] -= self.pos[1] # Incase python throws float div or div by zero exception, # ignore them, we will be close enough try: self.angle = degrees(atan((int(tvec[1]) / int(tvec[0])))) except Exception: pass # Ajdust quadrants so we have 0-360 degrees if tvec[0] < 0 and tvec[1] > 0: self.angle = 90 + (90 + self.angle) elif tvec[0] < 0 and tvec[1] < 0: self.angle += 180 elif tvec[0] > 0 and tvec[1] < 0: self.angle = 270 + (self.angle + 90) elif tvec[0] > 0 and tvec[1] > 0: pass def on_touch_down(self, touch): if self.collide_point(touch.x, touch.y): self.vector[0], self.vector[1] = touch.x, touch.y self._calc_stuff() self.dispatch_event('on_aplitude_change', self.amplitude) self.dispatch_event('on_angle_change', self.angle) self.dispatch_event('on_vector_change', self.amplitude, self.angle) return True def on_touch_move(self, touch): if self.collide_point(touch.x, touch.y): self.vector[0], self.vector[1] = touch.x, touch.y self._calc_stuff() self.dispatch_event('on_aplitude_change', self.amplitude) self.dispatch_event('on_angle_change', self.angle) self.dispatch_event('on_vector_change', self.amplitude, self.angle) return True def draw(self): # Background set_color(*self.style.get('bg-color')) drawCircle(self.pos, self.radius) # A good size for the hand, proportional to the size of the widget hd = self.radius / 10 # Draw center of the hand set_color(*self.style.get('vector-color')) drawCircle(self.pos, hd) # Rotate the triangle so its not skewed l = prot((self.pos[0] - hd, self.pos[1]), self.angle - 90, self.pos) h = prot((self.pos[0] + hd, self.pos[1]), self.angle - 90, self.pos) # Draw triable of the hand with gx_begin(GL_POLYGON): glVertex2f(*l) glVertex2f(*h) glVertex2f(self.vector[0], self.vector[1])
def _get_rotation(self): v1 = Vector(0, 10) v2 = Vector(*self.to_parent(*self.pos)) - self.to_parent(self.x, self.y + 10) return -1.0 *(v1.angle(v2) + 180) % 360