import typing import builtins import math from enum import Enum from math import fabs from PIL import Image, ImageFilter import os from typing import NamedTuple, List DIRECTORY = os.path.dirname(os.path.abspath(__file__))+"/" PLOT_EMPTY = Image.open(DIRECTORY+"sprites/emptySprite.png").convert("RGBA") PLOT_GARAGE = Image.open(DIRECTORY+"sprites/Park.png").convert("RGBA") PLOT_CAR = Image.open(DIRECTORY+"sprites/car.png").convert("RGBA") PLOT_GARAGE_CAR = Image.open(DIRECTORY+"sprites/CarOnPark.png").convert("RGBA") OUTPUT_DIR = DIRECTORY + "output/" FULL_GARAGE_COLOR = (1,0.5,0.5,1) full_garage_sprite = PLOT_GARAGE.copy() r,g,b,a = full_garage_sprite.split() r = r.point(lambda i: i * FULL_GARAGE_COLOR[0]) g = g.point(lambda i: i * FULL_GARAGE_COLOR[1]) b = b.point(lambda i: i * FULL_GARAGE_COLOR[2]) a = a.point(lambda i: i * FULL_GARAGE_COLOR[3]) full_garage_sprite = Image.merge("RGBA",(r,g,b,a)) class RenderTaskType(Enum): LINE = 1 # data [x,y,x2,y2,color(r,g,b,a)] class RenderTask(): def __init__(self, type: RenderTaskType, name:str , data :list): self.type = type self.name = name self.data = data class RenderTaskFrame: def __init__(self,tasks:List[RenderTask],become_permanent:bool = False, become_temp:bool = False): self.tasks:List[RenderTask] = tasks self.become_permanent:bool = become_permanent self.become_temp:bool = become_temp class RenderTaskStack: _Tasks:List[RenderTask] = [] _TempStack:List[RenderTask] = [] _SingleFrameRenderTaskList: List[RenderTaskFrame] = [] @staticmethod def has_frame(): return len(RenderTaskStack._SingleFrameRenderTaskList) >0 @staticmethod def clear_all(): RenderTaskStack._Tasks = [] RenderTaskStack._TempStack = [] @staticmethod def add_temp(task:RenderTask): RenderTaskStack._TempStack.append(task) @staticmethod def add_temps(tasks:List[RenderTask]): RenderTaskStack._TempStack += tasks @staticmethod def clear_temp(): RenderTaskStack._TempStack = [] @staticmethod def add_permanent( task: RenderTask): RenderTaskStack._Tasks.append(task) @staticmethod def add_permanents(task: List[RenderTask]): RenderTaskStack._Tasks+=task @staticmethod def add_single_frame_render(frame: RenderTaskFrame): RenderTaskStack._SingleFrameRenderTaskList.append(frame) @staticmethod def pop_single_frame_render()->RenderTaskFrame: return RenderTaskStack._SingleFrameRenderTaskList.pop(0) @staticmethod def get_permanents(): return RenderTaskStack._Tasks.copy() @staticmethod def get_temps(): return RenderTaskStack._TempStack.copy() # füge alle kanten in eine set hinzu, sorte dis liste bei distance und pick die kürzesten die nen neuen knoten hinzufügen class Linedrawer(): @staticmethod def plotLineLow(x0, y0, x1, y1): dx = x1 - x0 dy = y1 - y0 yi = 1 if dy < 0: yi = -1 dy = -dy D = (2 * dy) - dx y = y0 points = [] for x in range( x0,x1): points.append((x, y+1)) if D > 0: y = y + yi D = D + (2 * (dy - dx)) else: D = D + 2*dy return points @staticmethod def plotLineHigh(x0, y0, x1, y1): dx = x1 - x0 dy = y1 - y0 xi = 1 if dx < 0: xi = -1 dx = -dx D = (2 * dx) - dy x = x0 points = [] for y in range(y0, y1): points.append((x, y+1)) if D > 0: x = x + xi D = D + (2 * (dx - dy)) else: D = D + 2*dx return points @staticmethod ## bresenhams line algo ######### def plotLine(x0, y0, x1, y1): if abs(y1 - y0) < abs(x1 - x0): if x0 > x1: return Linedrawer.plotLineLow(x1, y1, x0, y0) else: return Linedrawer.plotLineLow(x0, y0, x1, y1) else: if y0 > y1: return Linedrawer.plotLineHigh(x1, y1, x0, y0) else: return Linedrawer.plotLineHigh(x0, y0, x1, y1) ################# @staticmethod def draw_line_between_points(img:Image, x1,y1,x2,y2,color = (255,255,255))-> Image: if(x1==x2 and y1 == y2): x1+=10 x2-=10 pixels = img.load() # create the pixel map print((x1,y1,x2,y2)) points = Linedrawer.plotLine(x1,y1,x2,y2) for pt in points: if pt[0] >= 0 and pt[0] = 0 and pt[1] list: if(len(self.points)==0): return None point = self.points.pop() self.points.add(point) x_max = point.x y_max = point.y x_min = x_max y_min = y_max for p in self.points: if p.x > x_max: x_max = p.x if p.y > y_max: y_max = p.y if p.x < x_min: x_min = p.x if p.y < y_min: y_min = p.y x_dif = x_max-x_min y_dif = y_max-y_min return [x_max,y_max,x_min,y_min, x_dif +1,y_dif+1] class GarageManager: def __init__(self, garages:set, capacities:dict ={}): self.garages:set[Garage] = garages # To delete self.capacities = capacities self.garageArrays = [] self.currentFillLevel = capacities.copy() # initialize capacities for x in self.currentFillLevel.keys(): self.currentFillLevel[x]=0 def is_full(self, garage:Point): garage_found = False current_garage:Garage for g in self.garages: if g.x == garage.x and g.y == garage.y: current_garage = g garage_found = True break if not garage_found: raise RuntimeError(" garage not found in is Full check") return current_garage.max_capacity <= len(current_garage.cars_parked) def getCurrentCapacity(self, garage:Point): """DEPRECATED""" raise DeprecationWarning() return self.currentFillLevel.get(garage,-99) def getMaxCapacity(self, garage:Point): raise DeprecationWarning() return self.capacities.get(garage,-99) def addToCapacity(self,garage:Point,amount): raise DeprecationWarning() self.currentFillLevel[garage] = self.getCurrentCapacity(garage) + amount class frameGenerator(): def __init__(self, space:MetricSpace, garages:GarageManager): self.space = space self.garages:GarageManager = garages self.garagePoints = [] for g in garages.garages: self.garagePoints.append(g.get_location()) self.car_exists :bool = False self.car:Point = None self.space_limits = space.get_max_min_values() "[ x_max, y_max, x_min, y_min, x_dif, y_dif]" self.frame_width = (self.space_limits[4]*34)-2 # plot +2 pixel for road -2 for end self.frame_height = (self.space_limits[5]*34)-2 # plot +2 pixel for road -2 for end self.time = 0 self.frameNum = 0 def spawnCar(self,point:Point): if(not self.car_exists): self.frameNum = 0 self.time += 1 self.car = point self.car_exists= True self.renderFrame() else: print("ERROR: double cars!!!!") raise IndexError def add_transition(self, point_B:Point): if(self.car_exists): x0,y0 =self._point_to_pixel_center(self.car) x1,y1 =self._point_to_pixel_center(point_B) rend_task = RenderTask(RenderTaskType.LINE, "transition",[x0,y0,x1,y1, (255,100,100,200)]) RenderTaskStack.add_single_frame_render(RenderTaskFrame( [rend_task])) self.renderFrame() self.car = point_B self.renderFrame() self.car = None self.car_exists= False else: print("ERROR: no cars was driven somewhere!!!!") raise IndexError def _point_to_pixel(self, point:Point)->tuple: x = point.x-self.space_limits[2]#xmin y = point.y-self.space_limits[3]#ymin pix_x = x*34 pix_y = y*34 return (pix_x,pix_y) @staticmethod def point_to_pixel_center(space, point: Point) -> tuple: space_limits = space.get_max_min_values() x = point.x - space_limits[2] # xmin y = point.y - space_limits[3] # ymin pix_x = x * 34 + 16 pix_y = y * 34 + 16 return (pix_x, pix_y) def _point_to_pixel_center(self, point:Point)->tuple: x = point.x-self.space_limits[2]#xmin y = point.y-self.space_limits[3]#ymin pix_x = x*34 +16 pix_y = y*34 +16 return (pix_x,pix_y) def _insert_Image(self, frame , pix_x,pix_y, image:Image): img:Image.core.PixelAccess = image.load() fme:Image.core.PixelAccess = frame.load() width, height = image.size for x in range(width): for y in range(height): if img[x,y] != (0,0,0,0): fme[pix_x+x,pix_y+y] = img[x,y] def renderFrame(self, drawLine = False, LineColor = (200,0,0,200), endPoint:Point = None ): frame = Image.new("RGBA",(self.frame_width,self.frame_height),(100,100,100,255)) for p in self.space.points: x,y = self._point_to_pixel(p) img = PLOT_EMPTY if((self.car_exists) and (p.get_location() == self.car.get_location()) and (p.get_location() in self.garagePoints)): img = PLOT_GARAGE_CAR elif(self.car_exists and p.get_location() == self.car.get_location()): img = PLOT_CAR elif(p.get_location() in self.garagePoints): if self.garages.is_full(p): img = full_garage_sprite else: img = PLOT_GARAGE self._insert_Image(frame, x, y,img) # render temp and permanent static_overlay = Image.new("RGBA", (self.frame_width, self.frame_height), (0, 0, 0, 0)) for ta in RenderTaskStack.get_permanents(): if ta.type == RenderTaskType.LINE: car_x, car_y = ta.data[0], ta.data[1] ep_x, ep_y = ta.data[2], ta.data[3] Linedrawer.draw_line_between_points(static_overlay, car_x, car_y, ep_x, ep_y, ta.data[4]) for ta in RenderTaskStack.get_temps(): if ta.type == RenderTaskType.LINE: car_x, car_y = ta.data[0], ta.data[1] ep_x, ep_y = ta.data[2], ta.data[3] Linedrawer.draw_line_between_points(static_overlay, car_x, car_y, ep_x, ep_y, ta.data[4]) frame = Image.alpha_composite(frame, static_overlay) # _Tasks:List[RenderTask] = [] # _TempStack:List[RenderTask] = [] # _SingleFrameRenderTaskList: List[RenderTaskFrame] = [] firstInteration:bool = True frame_src = frame.copy() while (firstInteration or RenderTaskStack.has_frame()): frame = frame_src.copy() passingOverlay = Image.new("RGBA", (self.frame_width, self.frame_height), (0, 0, 0, 0)) if RenderTaskStack.has_frame(): framedata:RenderTaskFrame = RenderTaskStack.pop_single_frame_render() # process task per frame for ta in framedata.tasks: if ta.type == RenderTaskType.LINE: car_x,car_y = ta.data[0],ta.data[1] ep_x,ep_y = ta.data[2],ta.data[3] Linedrawer.draw_line_between_points(passingOverlay,car_x,car_y,ep_x,ep_y, ta.data[4]) # handle migration if framedata.become_temp: RenderTaskStack.add_temps(framedata.tasks) if framedata.become_permanent: RenderTaskStack.add_permanents(framedata.tasks) #add overlay frame = Image.alpha_composite(frame,passingOverlay) # save frame.save(OUTPUT_DIR +f"frame-t{self.time:03d}-f{self.frameNum:02d}.png",format="PNG") self.frameNum += 1 firstInteration = False