def __init__(self, car, angle, x, y): # sensor itself self.direction_relative_to_car = angle self.position_relative_to_car = Point(x, y) self.direction = 0 self.position = Point(0, 0) self.sensor_line_id = None # connection to car self.car = car self.car.sensors.append(self)
def __init__(self, track, brain, color="red"): self.canvas = track.canvas self.canvas_shape_id = None self.track = track self.position = Point(*track.startpoint) self.direction = track.startdirection self.steeringwheel = 0 # -90 ... left, 0 ... forward, 90 ... right self.acceleration = 0 # -10 ... slow down, 0 ... keep, 10 ... speed up self.speed = 0 self.brain = brain self.color = color self.isalive = True self.sensors = [] self.brain.initialize(self)
def update(self): '''Update the car status (brain, sensors, position, speed, direction, ...).''' if self.isalive: self.brain.update() # update speed and direction self.speed = self.speed + self.acceleration self.direction = (self.direction + self.steeringwheel) % 360 # update position relativeposition = Point(0, self.speed).rotate(self.direction) self.position = self.position.move(relativeposition.x, -relativeposition.y) # update sensor for s in self.sensors: s.update() # check position if any([not self.track.intrack(*p) for p in self.points()]): #print("!!!!!!!!!!!!!OUT OF TRACK!!!!!!!!!!!!!") self.isalive = False self.canvas.itemconfig(self.canvas_shape_id, fill="black") elif self.track.ingoal(*self.position): #print("!!!!!!!!!!!!! WON !!!!!!!!!!!!!") self.isalive = False self.canvas.itemconfig(self.canvas_shape_id, fill="blue") self.acceleration = 0 self.steeringwheel = 0
def __init__(self, track, brain, level, color="red"): self.canvas = track.canvas self.canvas_shape_id = None self.track = track self.position = Point(*track.startpoint) self.direction = track.startdirection self.steeringwheel = 0 # -90 ... left, 0 ... forward, 90 ... right self.acceleration = 0 # -10 ... slow down, 0 ... keep, 10 ... speed up self.speed = 0 self.brain = brain self.color = color self.isalive = True self.sensors = [] self.brain.initialize(self) self.totalReward = 0.0 self.reward = 0.0 self.level = level self.middlePoints = [ # level 1 [(100, 535), (100, 100), (700, 100), (700, 500), (200, 500)], # level 2 [(72, 503), (82, 98), (662, 86), (653, 237), (368, 233), (358, 336), (643, 343), (636, 464), (203, 488), (205, 191)], # level 3 [(461, 127), (286, 301), (461, 408), (723, 143), (424, 18), (84, 295), (486, 562)], # level 4 [(24, 372), (129, 252), (220, 165), (272, 148), (321, 148), (386, 195), (429, 270), (446, 337), (481, 391), (532, 405), (604, 377), (646, 337), (679, 279), (747, 215)], # Level 5 [(166, 447), (136, 387), (136, 330), (148, 266), (180, 226), (223, 196), (285, 171), (352, 141), (436, 136), (510, 144), (561, 178), (602, 222), (623, 278), (626, 325), (620, 377), (586, 415), (526, 451), (446, 467), (360, 477)], # Level 6 [(152, 448), (144, 295), (166, 177), (236, 125), (342, 97), (505, 89), (629, 97), (704, 193), (700, 337), (692, 420), (629, 500), (548, 549), (415, 550), (296, 551), (253, 485), (252, 414), (268, 338), (296, 250), (370, 217), (428, 202), (520, 202), (577, 220), (605, 269), (587, 341), (548, 398), (486, 421), (384, 405)] ] self.nextMiddlePoint = self.middlePoints[self.level - 1][0] self.nextMiddlePointIndex = 0
def intersections(self, polynom, frompoint, topoint): '''Return intersections of the line (frompoint,topoint) with the polynom.''' points_of_shape = [Point(x,y) for x,y in [polynom[-1]] + polynom] points = [] for i in range(0,len(points_of_shape)-1): s = Line(frompoint, topoint).intersection(Line(points_of_shape[i], points_of_shape[i+1])) if s is not None: points.append(s) return points
def update(self): self.reward = 0 '''Update the car status (brain, sensors, position, speed, direction, ...).''' if self.isalive: self.brain.update() self.totalReward = self.totalReward - 0.1 self.reward -= 0.1 self.speed = self.speed + self.acceleration self.direction = (self.direction + self.steeringwheel) % 360 relativeposition = Point(0, self.speed).rotate(self.direction) self.position = self.position.move(relativeposition.x, -relativeposition.y) for s in self.sensors: s.update() self.distanceToCheckpoint = sqrt( (self.position.x - self.nextMiddlePoint[0]) * (self.position.x - self.nextMiddlePoint[0]) + (self.position.y - self.nextMiddlePoint[1]) * (self.position.y - self.nextMiddlePoint[1])) if self.distanceToCheckpoint < 100 and self.nextMiddlePointIndex < len( self.middlePoints[self.level - 1]): self.nextMiddlePoint = self.middlePoints[self.level - 1][ self.nextMiddlePointIndex] self.nextMiddlePointIndex = self.nextMiddlePointIndex + 1 self.totalReward = self.totalReward + 1 self.reward = self.reward + 1 # check position if any([not self.track.intrack(*p) for p in self.points()]): #print("!!!!!!!!!!!!!OUT OF TRACK!!!!!!!!!!!!!") self.totalReward = self.totalReward - 10 self.reward = self.reward - 10 self.isalive = False self.canvas.itemconfig(self.canvas_shape_id, fill="black") elif self.track.ingoal(*self.position): #print("!!!!!!!!!!!!! WON !!!!!!!!!!!!!") self.totalReward = self.totalReward + 10 self.reward = self.reward + 10 self.isalive = False self.canvas.itemconfig(self.canvas_shape_id, fill="blue") self.acceleration = 0 self.steeringwheel = 0
def measure(self): '''Measure distance to goal.''' x = self.car.position.x y = self.car.position.y min_distance = 9999 min_distance_point_on_midline = None min_distance_line_index = None midline_lines = [ Line(Point(*self.car.track.midline_points[i]), Point(*self.car.track.midline_points[i + 1])) for i in range(0, len(self.car.track.midline_points) - 1) ] for i in range(0, len(midline_lines)): l = Line( Point(x, y), Point(x, y).move(*Vector.from_points( *midline_lines[i]).normal_vector())) s = midline_lines[i].intersection(l, treat_as_line_segments=False) if s is not None and midline_lines[i].inline(s): d = Vector.from_points(Point(x, y), s).length() if min_distance > d: min_distance = d min_distance_point_on_midline = s min_distance_line_index = i # if outside of line use distance to endpoints ad = Vector.from_points(midline_lines[i].a, Point(x, y)).length() bd = Vector.from_points(midline_lines[i].b, Point(x, y)).length() if min_distance > ad: min_distance = ad min_distance_point_on_midline = midline_lines[i].a min_distance_line_index = i if min_distance > bd: min_distance = bd min_distance_point_on_midline = midline_lines[i].b min_distance_line_index = i # sum up distances distance = Vector.from_points( min_distance_point_on_midline, midline_lines[min_distance_line_index].b).length() for i in range(min_distance_line_index + 1, len(midline_lines)): distance = distance + midline_lines[i].length() return distance - 25 # remove length of goal area
def update(self): '''Update the position of the sensor and its measurements.''' # update direction self.direction = self.car.direction + self.direction_relative_to_car # update position relativesensor = self.position_relative_to_car.rotate( self.car.direction) self.position = self.car.position.move(relativesensor.x, -relativesensor.y) # update distance & obstacle relativeray = Point(0, 1000).rotate(self.direction) ray = self.position.move(relativeray.x, -relativeray.y) nearest_point_meassured = self.measure(self.car.track.shapes(), self.position, ray) if nearest_point_meassured is None: self.distance = 1000 self.obstacle = None else: self.obstacle, self.distance = nearest_point_meassured self.is_obstacle_goal = self.car.track.ingoal(*self.obstacle)
def points(self): '''Points of the car shape.''' return [(self.position.x + x, self.position.y - y) for x, y in [Point(x, y).rotate(self.direction) for x, y in self._shape]]
class Car: '''A car.''' _shape = [(-10, -15), (-10, 15), (10, 15), (10, -15)] def __lt__(self, other): return self.totalReward > other.reward def __init__(self, track, brain, level, color="red"): self.canvas = track.canvas self.canvas_shape_id = None self.track = track self.position = Point(*track.startpoint) self.direction = track.startdirection self.steeringwheel = 0 # -90 ... left, 0 ... forward, 90 ... right self.acceleration = 0 # -10 ... slow down, 0 ... keep, 10 ... speed up self.speed = 0 self.brain = brain self.color = color self.isalive = True self.sensors = [] self.brain.initialize(self) self.totalReward = 0.0 self.reward = 0.0 self.level = level self.middlePoints = [ # level 1 [(100, 535), (100, 100), (700, 100), (700, 500), (200, 500)], # level 2 [(72, 503), (82, 98), (662, 86), (653, 237), (368, 233), (358, 336), (643, 343), (636, 464), (203, 488), (205, 191)], # level 3 [(461, 127), (286, 301), (461, 408), (723, 143), (424, 18), (84, 295), (486, 562)], # level 4 [(24, 372), (129, 252), (220, 165), (272, 148), (321, 148), (386, 195), (429, 270), (446, 337), (481, 391), (532, 405), (604, 377), (646, 337), (679, 279), (747, 215)], # Level 5 [(166, 447), (136, 387), (136, 330), (148, 266), (180, 226), (223, 196), (285, 171), (352, 141), (436, 136), (510, 144), (561, 178), (602, 222), (623, 278), (626, 325), (620, 377), (586, 415), (526, 451), (446, 467), (360, 477)], # Level 6 [(152, 448), (144, 295), (166, 177), (236, 125), (342, 97), (505, 89), (629, 97), (704, 193), (700, 337), (692, 420), (629, 500), (548, 549), (415, 550), (296, 551), (253, 485), (252, 414), (268, 338), (296, 250), (370, 217), (428, 202), (520, 202), (577, 220), (605, 269), (587, 341), (548, 398), (486, 421), (384, 405)] ] self.nextMiddlePoint = self.middlePoints[self.level - 1][0] self.nextMiddlePointIndex = 0 def points(self): '''Points of the car shape.''' return [(self.position.x + x, self.position.y - y) for x, y in [Point(x, y).rotate(self.direction) for x, y in self._shape]] def accelerate(self, units): '''Accelerate the car about units.''' self.acceleration = max(-10, min(10, units)) def turn(self, units): '''Turn the car about units to the left (negative) or right (positive).''' self.steeringwheel = max(-90, min(90, units)) def update(self): self.reward = 0 '''Update the car status (brain, sensors, position, speed, direction, ...).''' if self.isalive: self.brain.update() self.totalReward = self.totalReward - 0.1 self.reward -= 0.1 self.speed = self.speed + self.acceleration self.direction = (self.direction + self.steeringwheel) % 360 relativeposition = Point(0, self.speed).rotate(self.direction) self.position = self.position.move(relativeposition.x, -relativeposition.y) for s in self.sensors: s.update() self.distanceToCheckpoint = sqrt( (self.position.x - self.nextMiddlePoint[0]) * (self.position.x - self.nextMiddlePoint[0]) + (self.position.y - self.nextMiddlePoint[1]) * (self.position.y - self.nextMiddlePoint[1])) if self.distanceToCheckpoint < 100 and self.nextMiddlePointIndex < len( self.middlePoints[self.level - 1]): self.nextMiddlePoint = self.middlePoints[self.level - 1][ self.nextMiddlePointIndex] self.nextMiddlePointIndex = self.nextMiddlePointIndex + 1 self.totalReward = self.totalReward + 1 self.reward = self.reward + 1 # check position if any([not self.track.intrack(*p) for p in self.points()]): #print("!!!!!!!!!!!!!OUT OF TRACK!!!!!!!!!!!!!") self.totalReward = self.totalReward - 10 self.reward = self.reward - 10 self.isalive = False self.canvas.itemconfig(self.canvas_shape_id, fill="black") elif self.track.ingoal(*self.position): #print("!!!!!!!!!!!!! WON !!!!!!!!!!!!!") self.totalReward = self.totalReward + 10 self.reward = self.reward + 10 self.isalive = False self.canvas.itemconfig(self.canvas_shape_id, fill="blue") self.acceleration = 0 self.steeringwheel = 0 def draw(self): '''Draw the car on the canvas.''' if self.canvas_shape_id is None: self.canvas_shape_id = self.canvas.create_polygon(*self.points(), fill=self.color) else: self.canvas.coords( self.canvas_shape_id, *[item for sublist in self.points() for item in sublist]) for s in self.sensors: s.draw() def removeFromCanvas(self): self.canvas.delete(self.canvas_shape_id) for sensor in self.sensors: sensor.removeFromCanvas()
class Track: '''Represents the track.''' def __init__(self, canvas : tkinter.Canvas, draw_midline: bool, midline_points: list((int,int))): self.canvas = canvas self.draw_midline = draw_midline self.midline_points = midline_points self.track_width = 35 self.points = [] self.lines = [] self.startpoint = None self.startdirection = None self.endpoint = None self.endarea_points = None self.track_canvas_id = None self.goal_canvas_id = None self.midline_canvas_ids = None self.calculate_points(self.midline_points) @classmethod def level(cls, canvas : tkinter.Canvas, draw_midline: bool, level_number : int): '''Return the track of the specified level.''' levels = [ # level 1 [(100,535), (100,100),(700,100), (700,500), (200, 500)], # level 2 [(72,503), (82,98), (662,86), (653,237), (368,233), (358,336), (643,343), (636,464), (203,488), (205,191)], # level 3 [(461,127), (286,301), (461,408), (723,143), (424,18), (84,295), (486,562)], # level 4 [(24,372), (129,252), (220,165), (272,148), (321,148), (386,195), (429,270), (446,337), (481,391), (532,405), (604,377), (646,337), (679,279), (747,215)], # Level 5 [(166,447), (136,387), (136,330), (148,266), (180,226), (223,196), (285,171), (352,141), (436,136), (510,144), (561,178), (602,222), (623,278), (626,325), (620,377), (586,415), (526,451), (446,467), (360,477)], # Level 6 [(152,448), (144,295), (166,177), (236,125), (342,97), (505,89), (629,97), (704,193), (700,337), (692,420), (629,500), (548,549), (415,550), (296,551), (253,485), (252,414), (268,338), (296,250), (370,217), (428,202), (520,202), (577,220), (605,269), (587,341), (548,398), (486,421), (384,405)] ] return Track(canvas, draw_midline, levels[level_number-1]) def calculate_points(self,midline_points): '''Calculate the track shape, goal and start point based on the provided midline points.''' inner_lines = [] outer_lines = [] # create inner/outer lines for i in range(0, len(midline_points)-1): a = Point(*midline_points[i]) b = Point(*midline_points[i+1]) v = Vector.from_points(a, b) left_vector = v.normal_vector(direction='left').normalize().multiply(self.track_width) right_vector = v.normal_vector(direction='right').normalize().multiply(self.track_width) outer_line = Line(a.move(left_vector.x, left_vector.y), b.move(left_vector.x, left_vector.y)) inner_line = Line(a.move(right_vector.x, right_vector.y), b.move(right_vector.x, right_vector.y)) inner_lines.append(inner_line) outer_lines.append(outer_line) self.lines = outer_lines + [Line(inner_lines[-1].b, outer_lines[-1].b)] + list(reversed(inner_lines)) + [Line(inner_lines[0].a, outer_lines[0].a)] # intersect lines lines_points = [] for i in range(0, len(self.lines) - 1): l1 = self.lines[i] l2 = self.lines[i+1] s = l1.intersection(l2, treat_as_line_segments=False) lines_points.append(s) # points of track self.points = [self.lines[0].a] + lines_points + [self.lines[-1].b] # start point (25 from start) self.startpoint = Point(*Vector.from_points(Point(*midline_points[0]), Point(*midline_points[1])).normalize().multiply(25)).move(*midline_points[0]) # start direction startvector = Vector.from_points(Point(*midline_points[0]), Point(*midline_points[1])).normalize() self.startdirection = startvector.angle(Vector(0,1)) self.startdirection = -(180-self.startdirection) if startvector.x <= 0 else 180 - self.startdirection # end polynom end_vector = Vector.from_points(Point(*midline_points[-1]), Point(*midline_points[-2])).normalize() end_vector_left = end_vector.normal_vector(direction='left').multiply(self.track_width*0.80) end_vector_right = end_vector.normal_vector(direction='right').multiply(self.track_width*0.80) self.endpoint = Point(*end_vector.multiply(25)).move(*midline_points[-1]) self.endarea_points = [ Point(*midline_points[-1]).move(*end_vector_left).astuple(), self.endpoint.move(*end_vector_left).astuple(), self.endpoint.move(*end_vector_right).astuple(), Point(*midline_points[-1]).move(*end_vector_right).astuple() ] def shapes(self): '''Return the shapes that are used for the track.''' return [self.points, self.endarea_points] def intrack(self, x, y): '''Return True if point (x,y) is within track.''' return self.track_canvas_id in self.canvas.find_overlapping(x,y,x,y) def ingoal(self, x, y): '''Return True if point (x,y) is within goal.''' return self.goal_canvas_id in self.canvas.find_overlapping(x,y,x,y) def intersections(self, polynom, frompoint, topoint): '''Return intersections of the line (frompoint,topoint) with the polynom.''' points_of_shape = [Point(x,y) for x,y in [polynom[-1]] + polynom] points = [] for i in range(0,len(points_of_shape)-1): s = Line(frompoint, topoint).intersection(Line(points_of_shape[i], points_of_shape[i+1])) if s is not None: points.append(s) return points def draw(self): '''Draw the track.''' if self.track_canvas_id is None: self.track_canvas_id = self.canvas.create_polygon(*[p.astuple() for p in self.points], fill="cornsilk2") if self.midline_canvas_ids is None and self.draw_midline: self.midline_canvas_ids = [] for i in range(0, len(self.midline_points)-1): self.midline_canvas_ids.append(self.canvas.create_line(*self.midline_points[i], *self.midline_points[i+1], fill="cornsilk4", dash=(6,6), width=1)) if self.goal_canvas_id is None: self.goal_canvas_id = self.canvas.create_polygon(*self.endarea_points, fill="gray50")
def calculate_points(self,midline_points): '''Calculate the track shape, goal and start point based on the provided midline points.''' inner_lines = [] outer_lines = [] # create inner/outer lines for i in range(0, len(midline_points)-1): a = Point(*midline_points[i]) b = Point(*midline_points[i+1]) v = Vector.from_points(a, b) left_vector = v.normal_vector(direction='left').normalize().multiply(self.track_width) right_vector = v.normal_vector(direction='right').normalize().multiply(self.track_width) outer_line = Line(a.move(left_vector.x, left_vector.y), b.move(left_vector.x, left_vector.y)) inner_line = Line(a.move(right_vector.x, right_vector.y), b.move(right_vector.x, right_vector.y)) inner_lines.append(inner_line) outer_lines.append(outer_line) self.lines = outer_lines + [Line(inner_lines[-1].b, outer_lines[-1].b)] + list(reversed(inner_lines)) + [Line(inner_lines[0].a, outer_lines[0].a)] # intersect lines lines_points = [] for i in range(0, len(self.lines) - 1): l1 = self.lines[i] l2 = self.lines[i+1] s = l1.intersection(l2, treat_as_line_segments=False) lines_points.append(s) # points of track self.points = [self.lines[0].a] + lines_points + [self.lines[-1].b] # start point (25 from start) self.startpoint = Point(*Vector.from_points(Point(*midline_points[0]), Point(*midline_points[1])).normalize().multiply(25)).move(*midline_points[0]) # start direction startvector = Vector.from_points(Point(*midline_points[0]), Point(*midline_points[1])).normalize() self.startdirection = startvector.angle(Vector(0,1)) self.startdirection = -(180-self.startdirection) if startvector.x <= 0 else 180 - self.startdirection # end polynom end_vector = Vector.from_points(Point(*midline_points[-1]), Point(*midline_points[-2])).normalize() end_vector_left = end_vector.normal_vector(direction='left').multiply(self.track_width*0.80) end_vector_right = end_vector.normal_vector(direction='right').multiply(self.track_width*0.80) self.endpoint = Point(*end_vector.multiply(25)).move(*midline_points[-1]) self.endarea_points = [ Point(*midline_points[-1]).move(*end_vector_left).astuple(), self.endpoint.move(*end_vector_left).astuple(), self.endpoint.move(*end_vector_right).astuple(), Point(*midline_points[-1]).move(*end_vector_right).astuple() ]
class Sensor: '''Sensor that measures the distance to the edge of roadway.''' # measured values distance = 1000 obstacle = None is_obstacle_goal = False def __init__(self, car, angle, x, y): # sensor itself self.direction_relative_to_car = angle self.position_relative_to_car = Point(x, y) self.direction = 0 self.position = Point(0, 0) self.sensor_line_id = None # connection to car self.car = car self.car.sensors.append(self) def update(self): '''Update the position of the sensor and its measurements.''' # update direction self.direction = self.car.direction + self.direction_relative_to_car # update position relativesensor = self.position_relative_to_car.rotate( self.car.direction) self.position = self.car.position.move(relativesensor.x, -relativesensor.y) # update distance & obstacle relativeray = Point(0, 1000).rotate(self.direction) ray = self.position.move(relativeray.x, -relativeray.y) nearest_point_meassured = self.measure(self.car.track.shapes(), self.position, ray) if nearest_point_meassured is None: self.distance = 1000 self.obstacle = None else: self.obstacle, self.distance = nearest_point_meassured self.is_obstacle_goal = self.car.track.ingoal(*self.obstacle) def draw(self): '''Draw the sensor.''' if self.sensor_line_id is None: if self.obstacle is not None: self.sensor_line_id = self.car.canvas.create_line( *self.position, *self.obstacle, fill="orange", dash=(1, 1)) elif self.obstacle is not None: self.car.canvas.coords(self.sensor_line_id, *self.position, *self.obstacle) self.car.canvas.itemconfig( self.sensor_line_id, fill="blue" if self.is_obstacle_goal else "orange") def measure(self, shapes, frompoint, topoint): '''Measure the distance from point frompoint to the first intersection of line (frompoint,topoint) with shapes.''' nearest_point = (None, None) for shape in shapes: points = self.car.track.intersections(shape, frompoint, topoint) points = [(p, frompoint.distance(p)) for p in points if p is not None] points.sort(key=lambda p: p[1]) # by distance if len(points) > 0: if nearest_point[1] is None or nearest_point[1] > points[0][1]: nearest_point = points[0] return nearest_point if nearest_point[1] is not None else None def __repr__(self): return f"(sensor position on car: {self.position}, distance: {self.distance})"
class Car: '''A car.''' _shape = [(-10,-15),(-10,15), (10,15), (10,-15)] def __init__(self, track, brain, color="red"): self.canvas = track.canvas self.canvas_shape_id = None self.track = track self.position = Point(*track.startpoint) self.direction = track.startdirection self.steeringwheel = 0 # -90 ... left, 0 ... forward, 90 ... right self.acceleration = 0 # -10 ... slow down, 0 ... keep, 10 ... speed up self.speed = 0 self.brain = brain self.color = color self.isalive = True self.sensors = [] self.brain.initialize(self) self.isInGoal = False def points(self): '''Points of the car shape.''' return [(self.position.x + x, self.position.y - y) for x,y in [Point(x,y).rotate(self.direction) for x,y in self._shape]] def accelerate(self, units): '''Accelerate the car about units.''' self.acceleration = max(-10, min(10, units)) def turn(self, units): '''Turn the car about units to the left (negative) or right (positive).''' self.steeringwheel = max(-90, min(90, units)) def update(self): '''Update the car status (brain, sensors, position, speed, direction, ...).''' if self.isalive: self.brain.update() # update speed and direction self.speed = self.speed + self.acceleration self.direction = (self.direction + self.steeringwheel) % 360 # update position relativeposition = Point(0, self.speed).rotate(self.direction) self.position = self.position.move(relativeposition.x, -relativeposition.y) # update sensor for s in self.sensors: s.update() # check position if any([not self.track.intrack(*p) for p in self.points()]): #print("!!!!!!!!!!!!!OUT OF TRACK!!!!!!!!!!!!!") self.isalive = False self.canvas.itemconfig(self.canvas_shape_id, fill="black") elif self.track.ingoal(*self.position): #print("!!!!!!!!!!!!! WON !!!!!!!!!!!!!") self.isalive = False self.canvas.itemconfig(self.canvas_shape_id, fill="blue") self.acceleration = 0 self.steeringwheel = 0 def draw(self): '''Draw the car on the canvas.''' if self.canvas_shape_id is None: self.canvas_shape_id = self.canvas.create_polygon(*self.points(), fill=self.color) else: self.canvas.coords(self.canvas_shape_id, *[item for sublist in self.points() for item in sublist]) for s in self.sensors: s.draw()