def get_angle_container(item): x, y = item.center if item.isParent: if len(item.children_refs) > 0: x_total = 0 y_total = 0 for child in item.children_refs: child = child() x_total += child.center_x y_total += child.center_y x_moy = x_total / len(item.children_refs) y_moy = y_total / len(item.children_refs) v1 = Vector(x - x_moy, y - y_moy) angle = v1.angle((-1, 0)) angle = angle - 90 else: angle = 0 elif hasattr(item, 'parent_node') and item.parent_node != None: x0, y0 = item.parent_node().center v1 = Vector(x0 - x, y0 - y) angle = v1.angle((-1, 0)) angle += 90 return angle
def On_Rotate(self, touch) -> None: """ handles the rotation event """ if Globals.oTheScreen.GuiIsBlocked(): return if not self.bInit: self.xx = self.x self.yy = self.y self.bInit = True points = [Vector(self._last_touch_pos[t]) for t in self._touches] if len(points) == 0: # LogError(u'cRotateScatter: On_Rotate shouldnt get called') return anchor = Vector(self.xx + self.width / 2, self.yy + self.height / 2) farthest = max(points, key=anchor.distance) if points.index(farthest) != self._touches.index(touch): return old_line = Vector(*touch.ppos) - anchor new_line = Vector(*touch.pos) - anchor iRad = radians(new_line.angle(old_line)) * self.do_rotation self.SetValueSub(iRad) self.dispatch('on_widget_turned')
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 if angle: changed = True 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 update_widget_graphics(self, *l): if not self.activated: return if self.widget is None: self.grect.size = 0, 0 return gr = self.grect widget = self.widget # determine rotation a = Vector(1, 0) if widget is self.win: b = Vector(widget.to_window(0, 0)) c = Vector(widget.to_window(1, 0)) else: b = Vector(widget.to_window(*widget.to_parent(0, 0))) c = Vector(widget.to_window(*widget.to_parent(1, 0))) - b angle = -a.angle(c) # determine scale scale = c.length() # apply transform gr.size = widget.size if widget is self.win: self.gtranslate.xy = Vector(widget.to_window(0, 0)) else: self.gtranslate.xy = Vector(widget.to_window(*widget.pos)) self.grotate.angle = angle # fix warning about scale property deprecation self.gscale.xyz = (scale, ) * 3
def on_touch_move(self, touch): if touch.grab_current is not self.transform: return touch_point_in_parent = self.transform.to_parent(*touch.pos) if len(self.touches) == 1 and self.do_translation: ptx, pty = self.transform.pos px0, py0 = self.transform.to_parent(touch.px, touch.py) px1, py1 = touch_point_in_parent pdx, pdy = px1 - px0, py1 - py0 self.transform.pos = ptx + pdx, pty + pdy else: touch_point = Vector(touch_point_in_parent) points = [self.prev_pos[t] for t in self.touches] pivot = max(points, key=touch_point.distance) farthest = max(points, key=pivot.distance) if points.index(farthest) == self.touches.index(touch): old_line = Vector(self.transform.to_parent(*touch.ppos)) - pivot new_line = Vector(touch_point_in_parent) - pivot vx, vy = self.transform.viewport_pos local_pivot = self.transform.to_local(*pivot) self.transform.viewport_pos = vx-local_pivot[0], vy-local_pivot[1] if self.do_rotation: self.transform.do_rotate(radians(new_line.angle(old_line))) if self.do_scale: ratio = new_line.length() / old_line.length() self.transform.do_scale(ratio, ratio) self.transform.viewport_pos = vx, vy self.prev_pos[touch] = Vector(touch_point_in_parent)
def update_widget_graphics(self, *l): if not self.activated: return if self.widget is None: self.grect.size = 0, 0 return gr = self.grect widget = self.widget # determine rotation a = Vector(1, 0) if widget is self.win: b = Vector(widget.to_window(0, 0)) c = Vector(widget.to_window(1, 0)) else: b = Vector(widget.to_window(*widget.to_parent(0, 0))) c = Vector(widget.to_window(*widget.to_parent(1, 0))) - b angle = -a.angle(c) # determine scale scale = c.length() # apply transform gr.size = widget.size if widget is self.win: self.gtranslate.xy = Vector(widget.to_window(0, 0)) else: self.gtranslate.xy = Vector(widget.to_window(*widget.pos)) self.grotate.angle = angle # fix warning about scale property deprecation self.gscale.xyz = (scale,) * 3
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 if angle: changed = True 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 check_deflector_collision(self, deflector): # Here we have a collision Bullet <--> Deflector-bounding-box. But that doesn't mean # that there's a collision with the deflector LINE yet. So here's some math stuff # for the freaks :) It includes vector calculations, distance problems and trigonometry # first thing to do is: we need a vector describing the bullet. Length isn't important. bullet_position = Vector(self.center) bullet_direction = Vector(1, 0).rotate(self.angle * 360 / (2 * pi)) deflector_point1 = Vector( deflector.to_parent(deflector.point1.center[0], deflector.point1.center[1])) deflector_point2 = Vector( deflector.to_parent(deflector.point2.center[0], deflector.point2.center[1])) # then we need a vector describing the deflector line. deflector_vector = Vector(deflector_point2 - deflector_point1) # now we do a line intersection with the deflector line: intersection = Vector.line_intersection( bullet_position, bullet_position + bullet_direction, deflector_point1, deflector_point2) # now we want to proof if the bullet comes from the 'right' side. # Because it's possible that the bullet is colliding with the deflectors bounding box but # would miss / has already missed the deflector line. # We do that by checking if the expected intersection point is BEHIND the bullet position. # ('behind' means the bullets direction vector points AWAY from the vector # [bullet -> intersection]. That also means the angle between these two vectors is not 0 # -> due to some math-engine-internal inaccuracies, i have to check if the angle is greater than one: if abs(bullet_direction.angle(intersection - bullet_position)) > 1: # if the bullet missed the line already - NO COLLISION return False # now we finally check if the bullet is close enough to the deflector line: distance = abs( sin(radians(bullet_direction.angle(deflector_vector)) % (pi / 2))) * Vector(intersection - bullet_position).length() if distance < (self.width / 2): # there is a collision! # kill the animation! self.animation.unbind(on_complete=self.on_collision_with_edge) self.animation.stop(self) # call the collision handler self.on_collision_with_deflector(deflector, deflector_vector)
def check_deflector_collision(self, deflector): # Here we have a collision Bullet <--> Deflector-bounding-box. But that doesn't mean # that there's a collision with the deflector LINE yet. So here's some math stuff # for the freaks :) It includes vector calculations, distance problems and trigonometry # first thing to do is: we need a vector describing the bullet. Length isn't important. bullet_position = Vector(self.center) bullet_direction = Vector(1, 0).rotate(self.angle * 360 / (2*pi)) deflector_point1 = Vector(deflector.to_parent(deflector.point1.center[0], deflector.point1.center[1])) deflector_point2 = Vector(deflector.to_parent(deflector.point2.center[0], deflector.point2.center[1])) # then we need a vector describing the deflector line. deflector_vector = Vector(deflector_point2 - deflector_point1) # now we do a line intersection with the deflector line: intersection = Vector.line_intersection(bullet_position, bullet_position + bullet_direction, deflector_point1, deflector_point2) # now we want to proof if the bullet comes from the 'right' side. # Because it's possible that the bullet is colliding with the deflectors bounding box but # would miss / has already missed the deflector line. # We do that by checking if the expected intersection point is BEHIND the bullet position. # ('behind' means the bullets direction vector points AWAY from the vector # [bullet -> intersection]. That also means the angle between these two vectors is not 0 # -> due to some math-engine-internal inaccuracies, i have to check if the angle is greater than one: if abs(bullet_direction.angle(intersection - bullet_position)) > 1: # if the bullet missed the line already - NO COLLISION return False # now we finally check if the bullet is close enough to the deflector line: distance = abs(sin(radians(bullet_direction.angle(deflector_vector)) % (pi/2))) * Vector(intersection - bullet_position).length() if distance < (self.width / 2): # there is a collision! # kill the animation! self.animation.unbind(on_complete=self.on_collision_with_edge) self.animation.stop(self) # call the collision handler self.on_collision_with_deflector(deflector, deflector_vector)
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 p = dstpts.strokes[0].points[0] target = Vector([p.x, p.y]) source = Vector([p.x, p.y]) return source.angle(target)
def setup_mode_free(self): """Setup the keyboard in free mode. Free mode is designed to let the user control the position and orientation of the keyboard. The only real usage is for a multiuser environment, but you might found other ways to use it. If a :data:`target` is set, it will place the vkeyboard under the target. .. note:: Don't call this method directly, use :meth:`setup_mode` instead. """ self.do_translation = True self.do_rotation = True self.do_scale = True target = self.target if not target: return # NOTE all math will be done in window point of view # determine rotation of the target a = Vector(1, 0) b = Vector(target.to_window(0, 0)) c = Vector(target.to_window(1, 0)) - b self.rotation = -a.angle(c) # determine the position of center/top of the keyboard dpos = Vector(self.to_window(self.width / 2.0, self.height)) # determine the position of center/bottom of the target cpos = Vector(target.to_window(target.center_x, target.y)) # the goal now is to map both point, calculate the diff between them diff = dpos - cpos # we still have an issue, self.pos represent the bounding box, not the # 0,0 coordinate of the scatter. we need to apply also the diff between # them (inside and outside coordinate matrix). It's hard to explain, but # do a scheme on a paper, wrote all the vector i'm calculating, and # you'll understand. :) diff2 = Vector(self.x + self.width / 2.0, self.y + self.height) - Vector( self.to_parent(self.width / 2.0, self.height) ) diff -= diff2 # now we have a good "diff", set it as a pos. self.pos = -diff
def setup_mode_free(self): '''Setup the keyboard in free mode. Free mode is designed to let the user control the position and orientation of the keyboard. The only real usage is for a multiuser environment, but you might found other ways to use it. If a :attr:`target` is set, it will place the vkeyboard under the target. .. note:: Don't call this method directly, use :meth:`setup_mode` instead. ''' self.do_translation = True self.do_rotation = True self.do_scale = True target = self.target if not target: return # NOTE all math will be done in window point of view # determine rotation of the target a = Vector(1, 0) b = Vector(target.to_window(0, 0)) c = Vector(target.to_window(1, 0)) - b self.rotation = -a.angle(c) # determine the position of center/top of the keyboard dpos = Vector(self.to_window(self.width / 2., self.height)) # determine the position of center/bottom of the target cpos = Vector(target.to_window(target.center_x, target.y)) # the goal now is to map both point, calculate the diff between them diff = dpos - cpos # we still have an issue, self.pos represent the bounding box, # not the 0,0 coordinate of the scatter. we need to apply also # the diff between them (inside and outside coordinate matrix). # It's hard to explain, but do a scheme on a paper, write all # the vector i'm calculating, and you'll understand. :) diff2 = Vector(self.x + self.width / 2., self.y + self.height) - \ Vector(self.to_parent(self.width / 2., self.height)) diff -= diff2 # now we have a good "diff", set it as a pos. self.pos = -diff
def highlight_at(self, *largs): '''A function to highlight the current self.widget''' gr = self.grect widget = self.widget # determine rotation a = Vector(1, 0) b = Vector(widget.to_window(*widget.to_parent(0, 0))) c = Vector(widget.to_window(*widget.to_parent(1, 0))) - b angle = -a.angle(c) # determine scale scale = c.length() # apply transform gr.size = widget.size self.gtranslate.xy = Vector(widget.to_window(*widget.pos)) self.grotate.angle = angle self.gscale.scale = scale
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 on_touch_move(self, touch): if (self._MarkedMoveDirection == None): self._MarkedMoveDirection = Vector(touch.px - touch.x, touch.py - touch.y) return # Whats the current move direction currentMoveDir = Vector(touch.px - touch.x, touch.py - touch.y) # Rotation between this vector and our marked angleBetween = currentMoveDir.angle(self._MarkedMoveDirection) if (angleBetween >= 90 or angleBetween <= -90): # Do things self._MarkedMoveDirection = currentMoveDir self._CatPettingCounter += 1 if (self._CatPettingCounter >= 7): # Perform pet self._CatPettingCounter = 0 self.cat_label = "Hapiness+" self._LabelPulse = .5 self.Hapiness += 1
def update_widget_graphics(self, *l): if not self.activated: return if self.widget is None: self.grect.size = 0, 0 return gr = self.grect widget = self.widget # determine rotation a = Vector(1, 0) b = Vector(widget.to_window(*widget.to_parent(0, 0))) c = Vector(widget.to_window(*widget.to_parent(1, 0))) - b angle = -a.angle(c) # determine scale scale = c.length() # apply transform gr.size = widget.size self.gtranslate.xy = Vector(widget.to_window(*widget.pos)) self.grotate.angle = angle self.gscale.scale = scale
def On_Rotate(self, touch): ''' handles the rotation event ''' if not self.bInit: self.xx=self.x self.yy=self.y self.bInit=True points = [Vector(self._last_touch_pos[t]) for t in self._touches] if len(points)==0: # LogError(u'cRotateScatter: On_Rotate shouldnt get called') return anchor= Vector(self.xx+self.width/2,self.yy+self.height/2) farthest = max(points, key=anchor.distance) if points.index(farthest) != self._touches.index(touch): return old_line = Vector(*touch.ppos) - anchor new_line = Vector(*touch.pos) - anchor iRad = radians(new_line.angle(old_line)) * self.do_rotation self.SetValueSub(iRad) self.dispatch('on_widget_turned')
class Eyes(Scatter): active = BooleanProperty(False) alive = BooleanProperty(True) navigating = BooleanProperty(False) # Max level 8 obese_lvl = BoundedNumericProperty(1, min=0, max=8) def __init__(self, image="images/eyes_normal.png", box=[0, 0, 100, 100], **kwargs): self.direction = Vector(-1, 0) self.angle = 5 self.size = (700, 700) self.box = box self.center = (Window.width / 2, Window.height) self.image = Image(source=image, allow_stretch=True, size=self.size) self.image.texture = self.image.texture.get_region(0, 0, 316, 168) self.target_pos = self.center super(Eyes, self).__init__(**kwargs) self.add_widget(self.image) self.register_event_type('on_death') # Every living creature consumes own self # self.bind(active=lambda instance, value: Clock.schedule_interval(instance.consume_calories, 0.5) if value else Clock.unschedule(instance.consume_calories)) # Dynamic entry self.bind(active=lambda instance, value: Animation( y=Window.height - 520, t="out_back", d=1.2).start(instance) if value else True) # Too many calories make you obese # self.bind(total_calories=self.lvlup) def swim(self, dt): anim = Animation(center=self.target_pos, d=0.1) anim.start(self) def on_death(self): self.alive = False self.active = False def on_touch_down(self, touch): if not self.collide_point(touch.x, touch.y): return False if self.active and self.alive: Clock.schedule_interval(self.swim, 0.1) self.navigating = True def on_touch_move(self, touch): if not self.alive: return False # Bounding box x = touch.x if touch.x >= self.box[2]: x = self.box[2] elif touch.x <= self.box[0]: x = self.box[0] y = touch.y if touch.y >= self.box[3]: y = self.box[3] elif touch.y <= self.box[1]: y = self.box[1] self.target_pos = (x, y) def on_touch_up(self, touch): if not self.navigating: return False self.navigating = False Clock.unschedule(self.swim) speed = Vector((0, 0)).distance((touch.dsx, touch.dsy)) * 5000 angle = self.direction.angle((touch.dsx, touch.dsy)) if angle < 0: angle = 360 + angle angle = 270 - angle anim = Animation( center=(self.target_pos[0] + sin(radians(angle)) * speed, self.target_pos[1] - cos(radians(angle)) * speed), t="out_cubic", d=0.6) anim.start(self)
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()
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
class Car(RelativeLayout): _ROTATIONS = (0, 20, -20) def __init__(self, car_idx, initial_destination, args): self.pos = (100, 100) self._idx = car_idx self._sand_speed = _PADDING / 5.0 self._full_speed = _PADDING / 4.0 self._velocity = self._full_speed self._last_action = 0 # index of _ROTATIONS self._direction = Vector(-1, 0) self._scores = [] self._orientation = 0.0 self._distance = 0.0 self._current_destination = initial_destination self._write_status_file = args.write_status_file if self._write_status_file: self._status_file = open("car{}_status".format(car_idx), "w") RelativeLayout.__init__(self) with self.canvas.before: PushMatrix() self._rotation = Rotate() with self.canvas.after: PopMatrix() self._center = Center() self._body = Body(Vector(-5, -5), _IDX_TO_COLOR[self._idx][0]) self._mid_sensor = Sensor(Vector(-30, -5), RGBAColor.RED, self._rotation) self._right_sensor = Sensor(Vector(-20, 10), RGBAColor.GREEN, self._rotation) self._left_sensor = Sensor(Vector(-20, -20), RGBAColor.BLUE, self._rotation) if args.use_pytorch: from torch_ai import Brain else: from simple_ai import Brain self._brain = Brain(len(self._state), len(self._ROTATIONS), args) def build(self): self.add_widget(self._body) self.add_widget(self._mid_sensor) self.add_widget(self._right_sensor) self.add_widget(self._left_sensor) self.add_widget(self._center) @property def _state(self): return ( self._left_sensor.signal, self._mid_sensor.signal, self._right_sensor.signal, self._orientation, -self._orientation, ) @property def position(self): return Vector(*self.pos) + self._center.position def _rotate(self, angle_of_rotation): self._rotation.angle += angle_of_rotation self._direction = self._direction.rotate(angle_of_rotation) def _write_status(self, reward): self._status_file.seek(0) self._status_file.write("Car color : {}\n".format( _IDX_TO_COLOR[self._idx][1])) self._status_file.write("Destination : {}, ({:>4d}, {:>4d})\n".format( self._current_destination, self._current_destination.position.x, self._current_destination.position.y, )) self._status_file.write("Distance : {:>9.4f}\n".format( self._distance)) self._status_file.write("Orientation : {:>9.4f}\n".format( self._orientation)) self._status_file.write("Reward : {: >9.4f}\n".format(reward)) self._status_file.write( "Middle sensor: {: 2.4f}, ({:>9.4f}, {:>9.4f})\n".format( self._mid_sensor.signal, self._mid_sensor.abs_pos.x, self._mid_sensor.abs_pos.y, )) self._status_file.write( "Right sensor : {: 2.4f}, ({:>9.4f}, {:>9.4f})\n".format( self._right_sensor.signal, self._right_sensor.abs_pos.x, self._right_sensor.abs_pos.y, )) self._status_file.write( "Left sensor : {: 2.4f}, ({:>9.4f}, {:>9.4f})\n".format( self._left_sensor.signal, self._left_sensor.abs_pos.x, self._left_sensor.abs_pos.y, )) def _set_collision_signal_value(self, sensor): if (sensor.abs_pos.x >= self.parent.width - _PADDING or sensor.abs_pos.x <= _PADDING or sensor.abs_pos.y >= self.parent.height - _PADDING or sensor.abs_pos.y <= _PADDING): sensor.signal = 1. def _get_reward(self, approached_destination): reward = 0.0 if self.position.x < _PADDING: self.pos = (_PADDING, self.pos[1]) reward = -1.0 if self.position.x > self.parent.width - _PADDING: self.pos = (self.parent.width - _PADDING, self.pos[1]) reward = -1.0 if self.position.y < _PADDING: self.pos = (self.pos[0], _PADDING) reward = -1.0 if self.position.y > self.parent.height - _PADDING: self.pos = (self.pos[0], self.parent.height - _PADDING) reward = -1.0 if reward < 0.0: if approached_destination: if self.parent.sand[int(self.position.x), int(self.position.y)] > 0: self._velocity = self._sand_speed reward += 0.1 else: self._velocity = self._full_speed reward += 0.3 else: if approached_destination: if self.parent.sand[int(self.position.x), int(self.position.y)] > 0: self._velocity = self._sand_speed reward -= 0.2 else: self._velocity = self._full_speed reward += 0.6 return reward def move(self): self._rotate(self._ROTATIONS[self._last_action]) self.pos = self._direction * self._velocity + self.pos new_distance = self.position.distance( self._current_destination.position) self._orientation = self._direction.angle( self._current_destination.position - self.position) / 180. self._left_sensor.signal = numpy.sum( self.parent.sand[int(self._left_sensor.abs_pos.x) - _SIGNAL_RADIUS:int(self._left_sensor.abs_pos.x) + _SIGNAL_RADIUS, int(self._left_sensor.abs_pos.y) - _SIGNAL_RADIUS:int(self._left_sensor.abs_pos.y) + _SIGNAL_RADIUS, ]) / 400. self._mid_sensor.signal = numpy.sum( self.parent.sand[int(self._mid_sensor.abs_pos.x) - _SIGNAL_RADIUS:int(self._mid_sensor.abs_pos.x) + _SIGNAL_RADIUS, int(self._mid_sensor.abs_pos.y) - _SIGNAL_RADIUS:int(self._mid_sensor.abs_pos.y) + _SIGNAL_RADIUS, ]) / 400. self._right_sensor.signal = numpy.sum( self.parent.sand[int(self._right_sensor.abs_pos.x) - _SIGNAL_RADIUS:int(self._right_sensor.abs_pos.x) + _SIGNAL_RADIUS, int(self._right_sensor.abs_pos.y) - _SIGNAL_RADIUS:int(self._right_sensor.abs_pos.y) + _SIGNAL_RADIUS, ]) / 400. self._set_collision_signal_value(self._left_sensor) self._set_collision_signal_value(self._right_sensor) self._set_collision_signal_value(self._mid_sensor) reward = self._get_reward(new_distance < self._distance) self._last_action = self._brain.update( reward, self._state, ) self._distance = new_distance if self._distance < _PADDING * 2: if isinstance(self._current_destination, Airport): self._current_destination = self.parent.downtown else: self._current_destination = self.parent.airport self._scores.append(self._brain.score) if len(self._scores) > 1000: del self._scores[0] if self._write_status_file: self._write_status(reward) def save_brain(self): self._brain.save("car{}_brain".format(self._idx)) def load_brain(self): self._brain.load("car{}_brain".format(self._idx)) @property def scores(self): return self._scores @property def body_color(self): return self._body.color
def process(self, touches, strokes): uid = touches[0].uid v = Vector(touches[0].x, touches[0].y) - self.initial_touches[uid] a = v.angle((1, 0)) self.get_app().fire_event("on_gesture_swipe", v, self.initial_touches[uid])
def collide_wall(self, wall): # don't collide with this wall if we just did so; this # eliminates a huge class of weird behaviors if self.last_bounced_wall == wall and self.last_bounced_ticks < 5: return deflect_edge = None velocity_v = Vector(self.velocity) pos_v = Vector(self.pos) edge_points = zip(wall.quad_points[0::2], wall.quad_points[1::2]) edges = [ (edge_points[0], edge_points[1]), (edge_points[1], edge_points[2]), (edge_points[2], edge_points[3]), (edge_points[3], edge_points[0]), ] closest_point = None for point in edge_points: if (pos_v - Vector(point)).length() < self.r: if ( not closest_point or (pos_v - Vector(point)).length() < (Vector(closest_point) - Vector(point)).length() ): closest_point = point if closest_point: # take the deflection edge to be the normal of here to the corner deflect_edge = (pos_v - Vector(point)).rotate(90) else: for edge in edges: e0 = Vector(edge[0]) e1 = Vector(edge[1]) ortho_v = (e0 - e1).rotate(90).normalize() dist_v = Vector.line_intersection(self.pos, pos_v + ortho_v, edge[0], edge[1]) # dist_v will be None if we happen to be parallel if not dist_v: continue dist_from_edge = (pos_v - dist_v).length() # if the shot touches the wall here if ( min(e0[0], e1[0]) <= dist_v[0] <= max(e0[0], e1[0]) and min(e0[1], e1[1]) <= dist_v[1] <= max(e0[1], e1[1]) and dist_from_edge < self.r + (wall.thickness / 2.0) ): if not deflect_edge: deflect_edge = e0 - e1 dist_from_deflect_edge = dist_from_edge elif dist_from_edge < dist_from_deflect_edge: deflect_edge = e0 - e1 dist_from_deflect_edge = dist_from_edge if deflect_edge: self.velocity = velocity_v.rotate(-2 * velocity_v.angle(deflect_edge)) self.last_bounced_wall = wall self.last_bounced_ticks = 0
class Fish(Scatter): active = BooleanProperty(False) alive = BooleanProperty(True) navigating = BooleanProperty(False) box = ListProperty([]) calories = BoundedNumericProperty(1000, min=0, max=1000) total_calories = NumericProperty(0) junk_swallowed = NumericProperty(0) # Max level 8 obese_lvl = BoundedNumericProperty(1, min=0, max=8) # Immutable properties # # How many calories will be consumed per second each level calories_consumption = [7, 16, 20, 29, 32, 35, 42, 50] # Eat that much calories (in total) and you level up! lvlup_on_calories = [150, 350, 550, 900, 1400, 2100, 3000, 4100] # Relative size increase upon each lvlup size_increment = [1, 1.2, 1.2, 1.2, 1.4, 1.1, 1.1, 1.1] # Every level has a rank! rank = [ "a fry", "a cat", "a car", "a whale", "a candy store", "an oil tanker", "the Iceland", "the Indian Ocean itself!", 'the "MAFIAA"' ] def __init__(self, image="images/fish.png", box=[0, 0, 100, 100], **kwargs): self.direction = Vector(-1, 0) self.angle = 1 self.size = (48, 48) self.box = box self.center = (Window.width / 2, Window.height) self.image = Image(source=image, allow_stretch=True, size=self.size) # Can't be arsed to 'rotate' texture 'properly', this is so frikin more simple self.texture_left = self.image.texture.get_region(0, 0, 194, 192) self.texture_right = self.image.texture.get_region(205, 0, 194, 192) self.image.texture = self.texture_left self.target_pos = self.center super(Fish, self).__init__(**kwargs) self.add_widget(self.image) self.register_event_type('on_death') # Every living creature consumes own self self.bind(active=lambda instance, value: Clock.schedule_interval( instance.consume_calories, 0.5) if value else Clock.unschedule(instance.consume_calories)) # Dynamic entry self.bind(active=lambda instance, value: Animation( y=Window.height - 400, t="out_back", d=1.2).start(instance) if value else True) # Too many calories make you obese self.bind(total_calories=self.lvlup) def eat(self, stuff): self.calories = self.calories + stuff.calories if self.calories + stuff.calories <= 1000 else 1000 # Scrap food does not count into total calories if stuff.calories > 0: self.total_calories += stuff.calories if isinstance(stuff, Junk): self.junk_swallowed += 1 def consume_calories(self, *args): self.calories -= self.calories_consumption[self.obese_lvl - 1] def lvlup(self, instance, value): if self.total_calories >= self.lvlup_on_calories[self.obese_lvl]: self.obese_lvl += 1 print(self.obese_lvl) self.image.size = (self.image.width * self.size_increment[self.obese_lvl - 1], self.image.height * self.size_increment[self.obese_lvl - 1]) self.size = self.image.size def swim(self, dt): if self.angle > 0: self.image.texture = self.texture_left else: self.image.texture = self.texture_right anim = Animation(center=self.target_pos, d=0.1) anim.start(self) def on_death(self): self.alive = False self.active = False def on_touch_down(self, touch): if not self.collide_point(touch.x, touch.y): return False if self.active and self.alive: Clock.schedule_interval(self.swim, 0.1) self.navigating = True def on_touch_move(self, touch): if not self.alive: return False # Facing to the left will be positive, to right - negative deg values angle = self.direction.angle((touch.dsx, touch.dsy)) self.angle = cos(radians(angle)) * 180 # TODO: solve facing glitch problem with Clock, which sets facing_change cooldown timer for half a sec # Bounding box x = touch.x if touch.x >= self.box[2]: x = self.box[2] elif touch.x <= self.box[0]: x = self.box[0] y = touch.y if touch.y >= self.box[3]: y = self.box[3] elif touch.y <= self.box[1]: y = self.box[1] self.target_pos = (x, y) def on_touch_up(self, touch): if not self.navigating: return False self.navigating = False Clock.unschedule(self.swim) speed = Vector((0, 0)).distance((touch.dsx, touch.dsy)) * 5000 angle = self.direction.angle((touch.dsx, touch.dsy)) if angle < 0: angle = 360 + angle angle = 270 - angle anim = Animation( center=(self.target_pos[0] + sin(radians(angle)) * speed, self.target_pos[1] - cos(radians(angle)) * speed), t="out_cubic", d=0.6) anim.start(self)
def calc_mesh_vertices(self, step = None, update_mesh=True, preserve_uv=True): """Calculate Mesh.vertices and indices from the ControlPoints If step omitted, uses ControlPoints at current position, otherwise vertices at that animation step (ControlPoint.positions[step]). Central vertice at center is added as first item in list if mesh_mode=='triangle_fan' preserve_uv: Do not overwrite the uv coordinates in the current Mesh.vertices (only has an effect if update_mesh==True and vertices are already set) returns vertices, indices """ if step is not None and not isinstance(step, basestring): raise ValueError('step must be a string') num = len(self.control_points) if num == 0: Logger.warning("AnimationConstructor: Called calc_mesh_vertices without any control_points") return [], [] triangle_fan_mode = self.mesh_mode == 'triangle_fan' verts = [] cent_x, cent_y, cent_u, cent_v = 0.0, 0.0, 0.0, 0.0 # Need to calculate Centroid first, then do pass through to calculate vertices for cp in self.control_points: tx, ty = cp.get_tex_coords(step) cent_x += tx cent_y += ty cent_x /= num cent_y /= num # step may be None if moving points if triangle_fan_mode and self.animation_step==setup_step: # Sort by angle from centroid in case user didn't place around perimeter in order cent_vec = Vector(cent_x, cent_y) ref = Vector(1, 0) # Reference vector to measure angle from for cp in self.control_points: cp.centroid_angle = ref.angle(Vector(cp.get_tex_coords(step)) - cent_vec) # ListProperty.sort does not exist #self.control_points.sort(key = lambda cp: cp.centroid_angle) self.control_points = sorted(self.control_points, key = lambda cp: cp.centroid_angle) # TODO Need to figure out similar solution if using triangle_strip # Create vertices list # centroid added as first vertex in triangle-fan mode start = 1 if triangle_fan_mode else 0 # enumerate always goes through all items, start is just where the count starts for index, cp in enumerate(self.control_points, start=start): coords = cp.calc_vertex_coords(pos_index=step) # Need to calculate u, v centroid still cent_u += coords[2] cent_v += coords[3] cp.vertex_index = index verts.extend(coords) cent_u /= num cent_v /= num if triangle_fan_mode: # Calculate mean centroid and add to beginning of vertices verts.insert(0, cent_v) verts.insert(0, cent_u) verts.insert(0, cent_y) verts.insert(0, cent_x) # PERF: Technically don't need to recalculate indices except step 0, but not bothering with optimization now indices = range(1, num + 1) indices.insert(0, 0) indices.append(1) else: indices = range(num) if update_mesh: mesh_verts = self.mesh.vertices num_mesh_verts = len(mesh_verts) # preserve_uv: Do not overwrite the uv coordinates in the current Mesh.vertices # None: False if self.animation_step == setup_step # specified: False if step == setup_step # Refactored this way earlier, but went back because Animation uses None and when animating # back to setup_step we want preserve_uv false # preserve_uv = True # if step is None and self.animation_step == setup_step: # preserve_uv = False # elif step == setup_step: # preserve_uv = False if preserve_uv and num_mesh_verts > 0: if num_mesh_verts != len(verts): raise AssertionError('Number of calculated vertices (%d) != number Mesh.vertices (%d) step=%s' %(len(verts), num_mesh_verts, step)) # Only overwrite x, y mesh coords for x in range(0, num_mesh_verts, 4): mesh_verts[x] = verts[x] mesh_verts[x+1] = verts[x+1] self.mesh.vertices = mesh_verts else: self.mesh.vertices = verts self.mesh.indices = indices return verts, indices
def update(self, dt): # collisions: # stick collide with the balls: if self.stick.collide_widget( self.white_ball) and self.shoot_power != 0: print("collide with white ball") dx = cos(self.stick.angle * pi / 180 + pi / 2) * self.shoot_power dy = sin(self.stick.angle * pi / 180 + pi / 2) * self.shoot_power self.white_ball.dx = dx self.white_ball.dy = dy try: self.anim.cancel(self.stick) except: pass # collisions with borders: for ball in self.balls: if ball.top > self.table.top: ball.top = self.table.top ball.dy *= -1 if ball.y < self.table.y: ball.y = self.table.y ball.dy *= -1 if ball.right > self.table.right: ball.right = self.table.right ball.dx *= -1 if ball.x < self.table.x: ball.x = self.table.x ball.dx *= -1 # collision ball1-ball2: for ball1, ball2 in [(self.balls[0], self.balls[1]), (self.balls[0], self.balls[2]), (self.balls[1], self.balls[2])]: if ball1.collide_widget(ball2): ball1_pos = Vector(*ball1.pos) print(ball2) ball2_pos = Vector(*ball2.pos) dist_ball1_ball2 = ball1_pos.distance(ball2_pos) offset = max(ball1.width - dist_ball1_ball2, 0) ball1_vel = Vector(ball1.dx, ball1.dy) half_speed = ball1_vel.length() / 2 angle_vel = ball1_vel.angle(Vector(1, 0)) angle_ball1_ball2 = Vector(ball2.x - ball1.x, ball2.y - ball1.y).angle( Vector(1, 0)) angle_desv_perp_collision = angle_ball1_ball2 + (90 - angle_vel) angle_ball1 = angle_desv_perp_collision + angle_ball1_ball2 + 90 ball1_vel = Vector(half_speed, 0).rotate(angle_ball1) ball1.dx = ball1_vel.x ball1.dy = ball1_vel.y angle_ball2 = angle_ball1_ball2 + 90 - angle_desv_perp_collision ball_1 = Vector(half_speed, 0).rotate(angle_ball2) ball2.dx = ball_1.x ball2.dy = ball_1.y vector_offset = Vector(offset, 0).rotate(angle_ball1) ball1.x += vector_offset.x ball1.y += vector_offset.y for ball in self.balls: print("ball", ball, ball.x, ball.y, ball.dx, ball.dy) # Collide with holes: for hole in self.holes: if hole.collide_point(*ball.center): self.remove_widget(ball) # Friction: ball.frictionx = ball.dx * BALL_FRICTION * dt ball.frictiony = ball.dy * BALL_FRICTION * dt # kinematic equations: ball.x += ball.dx * dt ball.y += ball.dy * dt ball.dx -= ball.frictionx ball.dy -= ball.frictiony if abs(ball.dx) < 0.01: ball.dx = 0 if abs(ball.dy) < 0.01: ball.dy = 0
def collide_wall(self, wall): # don't collide with this wall if we just did so; this # eliminates a huge class of weird behaviors if self.last_bounced_wall == wall and self.last_bounced_ticks < 5: return deflect_edge = None velocity_v = Vector(self.velocity) pos_v = Vector(self.pos) edge_points = zip(wall.quad_points[0::2], wall.quad_points[1::2]) edges = [ (edge_points[0], edge_points[1]), (edge_points[1], edge_points[2]), (edge_points[2], edge_points[3]), (edge_points[3], edge_points[0]), ] closest_point = None for point in edge_points: if (pos_v - Vector(point)).length() < self.r: if not closest_point or \ (pos_v - Vector(point)).length() < (Vector(closest_point) - Vector(point)).length(): closest_point = point if closest_point: # take the deflection edge to be the normal of here to the corner deflect_edge = (pos_v - Vector(point)).rotate(90) else: for edge in edges: e0 = Vector(edge[0]) e1 = Vector(edge[1]) ortho_v = (e0 - e1).rotate(90).normalize() dist_v = Vector.line_intersection(self.pos, pos_v + ortho_v, edge[0], edge[1]) # dist_v will be None if we happen to be parallel if not dist_v: continue dist_from_edge = (pos_v - dist_v).length() # if the shot touches the wall here if min(e0[0], e1[0]) <= dist_v[0] <= max(e0[0], e1[0]) and \ min(e0[1], e1[1]) <= dist_v[1] <= max(e0[1], e1[1]) and \ dist_from_edge < self.r + (wall.thickness / 2.): if not deflect_edge: deflect_edge = e0 - e1 dist_from_deflect_edge = dist_from_edge elif dist_from_edge < dist_from_deflect_edge: deflect_edge = e0 - e1 dist_from_deflect_edge = dist_from_edge if deflect_edge: self.velocity = velocity_v.rotate(-2 * velocity_v.angle(deflect_edge)) self.last_bounced_wall = wall self.last_bounced_ticks = 0
class Fish(Scatter): active = BooleanProperty(False) alive = BooleanProperty(True) navigating = BooleanProperty(False) box = ListProperty([]) calories = BoundedNumericProperty(1000, min=0, max=1000) total_calories = NumericProperty(0) junk_swallowed = NumericProperty(0) # Max level 8 obese_lvl = BoundedNumericProperty(1, min=0, max=8) # Immutable properties # # How many calories will be consumed per second each level calories_consumption = [7, 16, 20, 29, 32, 35, 42, 50] # Eat that much calories (in total) and you level up! lvlup_on_calories = [150, 350, 550, 900, 1400, 2100, 3000, 4100] # Relative size increase upon each lvlup size_increment = [1, 1.2, 1.2, 1.2, 1.4, 1.1, 1.1, 1.1] # Every level has a rank! rank = ["a fry", "a cat", "a car", "a whale", "a candy store", "an oil tanker", "the Iceland", "the Pacific Ocean itself!", 'the "MAFIAA"'] def __init__(self, image = "images/fish.png", box = [0, 0, 100, 100], **kwargs): self.direction = Vector(-1, 0) self.angle = 1 self.size = (48,48) self.box = box self.center = (Window.width / 2, Window.height) self.image = Image(source=image, allow_stretch=True, size=self.size) # Can't be arsed to 'rotate' texture 'properly', this is so frikin more simple self.texture_left = self.image.texture.get_region(0, 0, 194, 192) self.texture_right = self.image.texture.get_region(205, 0, 194, 192) self.image.texture = self.texture_left self.target_pos = self.center super(Fish, self).__init__(**kwargs) self.add_widget(self.image) self.register_event_type('on_death') # Every living creature consumes own self self.bind(active=lambda instance, value: Clock.schedule_interval(instance.consume_calories, 0.5) if value else Clock.unschedule(instance.consume_calories)) # Dynamic entry self.bind(active=lambda instance, value: Animation(y=Window.height - 400, t="out_back", d=1.2).start(instance) if value else True) # Too many calories make you obese self.bind(total_calories=self.lvlup) def eat(self, stuff): try: self.calories = self.calories + stuff.calories if self.calories + stuff.calories <= 1000 else 1000 except: self.calories = 0 self.dispatch("on_death") # Scrap food does not count into total calories if stuff.calories > 0: self.total_calories += stuff.calories if isinstance(stuff, Junk): self.junk_swallowed += 1 def consume_calories(self, *args): try: self.calories -= self.calories_consumption[self.obese_lvl-1] except: self.calories = 0 self.dispatch("on_death") def lvlup(self, instance, value): try: #TODO: will there be lvl limit? if self.total_calories >= self.lvlup_on_calories[self.obese_lvl]: self.obese_lvl += 1 print self.obese_lvl self.image.size = (self.image.width * self.size_increment[self.obese_lvl-1], self.image.height * self.size_increment[self.obese_lvl-1]) self.size = self.image.size except: pass def swim(self, dt): if self.angle > 0: self.image.texture = self.texture_left else: self.image.texture = self.texture_right anim = Animation(center=self.target_pos, d=0.1) anim.start(self) def on_death(self): self.alive = False self.active = False def on_touch_down(self, touch): if not self.collide_point(touch.x, touch.y): return False if self.active and self.alive: Clock.schedule_interval(self.swim, 0.1) self.navigating = True def on_touch_move(self, touch): if not self.alive: return False # Facing to the left will be positive, to right - negative deg values angle = self.direction.angle((touch.dsx, touch.dsy)) self.angle = cos(radians(angle)) * 180 # TODO: solve facing glitch problem with Clock, which sets facing_change cooldown timer for half a sec # Bounding box x = touch.x if touch.x >= self.box[2]: x = self.box[2] elif touch.x <= self.box[0]: x = self.box[0] y = touch.y if touch.y >= self.box[3]: y = self.box[3] elif touch.y <= self.box[1]: y = self.box[1] self.target_pos = (x, y) def on_touch_up(self, touch): if not self.navigating: return False self.navigating = False Clock.unschedule(self.swim) speed = Vector((0,0)).distance((touch.dsx, touch.dsy)) * 5000 angle = self.direction.angle((touch.dsx, touch.dsy)) if angle < 0: angle = 360 + angle angle = 270 - angle anim = Animation(center=(self.target_pos[0] + sin(radians(angle)) * speed,self.target_pos[1] - cos(radians(angle)) * speed), t="out_cubic", d=0.6) anim.start(self)