init
This commit is contained in:
+493
@@ -0,0 +1,493 @@
|
||||
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] <img.size[0] and pt[1] >= 0 and pt[1] <img.size[1]:
|
||||
pixels[pt[0],pt[1]] = color
|
||||
pixels[pt[0],pt[1]-1] = color
|
||||
pixels[pt[0],pt[1]+1] = color
|
||||
pixels[pt[0]+1,pt[1]] = color
|
||||
pixels[pt[0]+1,pt[1]] = color
|
||||
|
||||
return img
|
||||
|
||||
class Point():
|
||||
def __init__(self,x,y):
|
||||
self.x = x
|
||||
self.y = y
|
||||
|
||||
def get_location(self):
|
||||
return Point(self.x,self.y)
|
||||
|
||||
def __eq__(self, value):
|
||||
if not isinstance(value,Point): return False
|
||||
return value.x == self.x and value.y == self.y
|
||||
|
||||
def __hash__(self):
|
||||
return hash((self.x, self.y))
|
||||
|
||||
class Car(Point):
|
||||
"""A class which represents a car"""
|
||||
def __init__(self, p:Point, timestamp:int,walk = 0):
|
||||
super().__init__(p.x, p.y)
|
||||
self.timestamp:int = timestamp
|
||||
self.walk = walk
|
||||
|
||||
def get_location(self):
|
||||
return super().get_location()
|
||||
|
||||
def get_walk(self):
|
||||
return self.walk
|
||||
|
||||
def set_walk(self,value):
|
||||
self.walk = value
|
||||
|
||||
def add_to_walk(self,value):
|
||||
self.walk += value
|
||||
|
||||
def __eq__(self, value):
|
||||
if not isinstance(value,Car): return False
|
||||
return value.x == self.x and value.y == self.y and self.timestamp == value.timestamp
|
||||
|
||||
def __hash__(self):
|
||||
return hash((self.x, self.y,self.timestamp))
|
||||
|
||||
class Garage(Point):
|
||||
"""A class which represents a garage"""
|
||||
def __init__(self, p:Point, max_capacity):
|
||||
super().__init__(p.x, p.y)
|
||||
self.max_capacity = max_capacity
|
||||
self.cars_parked = []
|
||||
|
||||
def get_location(self):
|
||||
return super().get_location()
|
||||
|
||||
def __eq__(self, value):
|
||||
if not isinstance(value,Garage): return False
|
||||
return value.x == self.x and value.y == self.y and self.max_capacity == value.max_capacity
|
||||
|
||||
def __hash__(self):
|
||||
return hash((self.x, self.y,self.max_capacity,1))# hash with 1 to avoid collisions witch car class
|
||||
|
||||
|
||||
class DistanceFunction():
|
||||
def apply(self, pointA: Point, pointB: Point):
|
||||
return math.sqrt( ( (pointA.x-pointB.x) **2) + ( (pointA.y-pointB.y) **2) )
|
||||
|
||||
|
||||
class MetricSpace:
|
||||
@staticmethod
|
||||
def getPopulatedSpace(x,y):
|
||||
points = set()
|
||||
for i in range(x):
|
||||
for j in range(y):
|
||||
points.add(Point(i,j))
|
||||
return MetricSpace(points,DistanceFunction())
|
||||
|
||||
|
||||
def __init__(self,points:set, distancefunction:DistanceFunction):
|
||||
self.points: set = points
|
||||
self.distancefunction:DistanceFunction = distancefunction
|
||||
|
||||
def get_max_min_values(self)->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
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user