This commit is contained in:
Varyngoth
2026-01-28 13:31:49 -04:00
commit 676659e5b9
239 changed files with 7509 additions and 0 deletions

102
_source_/spawning.py Normal file
View File

@@ -0,0 +1,102 @@
from shapely.geometry import Polygon,Point
from typing import Tuple, List, Dict, Set
from scipy.spatial import cKDTree
import sys
import numpy as np
from agent_setup import AgentSetup
class SpawnManager:
def __init__(self,spawn_area:Polygon,min_spacing:float=0.55):
"""
self.spawn_area: geometry where agents may spawn
self.min_spacing: minimum spacing for spawn_points
self.spawn_coords: all spawn points available
self.filled_coords: spawn points currently filled by agents
self.spawned_agents: all agents already spawned in sim
self.agent_pos: connects agent_id to spawn_index
self.rng: random number generator object
"""
self.spawn_area = spawn_area
self.min_spacing = min_spacing
self.spawn_coords: np.ndarray = np.array([])
self.filled_coords: np.ndarray = np.array([])
self.spawned_agents:Set[int] = set()
self.agent_pos:Dict[int,int] = {}
self.rng = np.random.default_rng()
def generate_coords(self,max_samples:int=1000)->None:
buffered_spawn = self.spawn_area.buffer(-self.min_spacing)
min_x,min_y,max_x,max_y = buffered_spawn.bounds
points = []
while len(points) ==0:
x = self.rng.uniform(min_x,max_x)
y = self.rng.uniform(min_y,max_y)
if buffered_spawn.contains(Point(x,y)):
points.append([x,y])
for _ in range(max_samples):
idx = self.rng.integers(0,len(points))
base = points[idx]
for _ in range(25):
angle = self.rng.uniform(0,2*np.pi)
radius = self.rng.uniform(self.min_spacing,2*self.min_spacing)
x = base[0]+radius*np.cos(angle)
y = base[1]+radius*np.sin(angle)
if not buffered_spawn.contains(Point(x,y)):
continue
if len(points)>0:
tree = cKDTree(points)
distance,_ = tree.query([[x,y]],k=1)
if distance[0]<self.min_spacing:
continue
points.append([x,y])
break
self.spawn_coords = np.array(points)
self.filled_coords = np.zeros(len(points),dtype=bool)
return self.spawn_coords
def get_coords(self)->Tuple[float,float]|None:
free_idx = np.where(~self.filled_coords)[0]
if len(free_idx) == 0:
return None
idx = self.rng.choice(free_idx)
return tuple(self.spawn_coords[idx])
def spawn_agent(self,all_agents:List[AgentSetup])->AgentSetup|None:
if len(self.spawned_agents) >= len(all_agents):
return None
spawn_point = self.get_coords()
if not spawn_point:
return None
free_agents = [agent for agent in all_agents \
if agent.id not in self.spawned_agents
]
if not free_agents:
return None
agent = self.rng.choice(free_agents)
self.spawned_agents.add(agent.id)
distances = np.linalg.norm(self.spawn_coords-spawn_point,axis=1)
spawn_idx = np.argmin(distances)
self.filled_coords[spawn_idx] = True
self.agent_pos[agent.id] = spawn_idx
return agent
def unfill_coords(self,agent_id:int)->None:
if agent_id in self.agent_pos:
spawn_idx = self.agent_pos[agent_id]
self.filled_coords[spawn_idx] = False
del self.agent_pos[agent_id]
self.spawned_agents.discard(agent_id)
def get_agent_pos(self,agent_id:int)->Tuple[float,float]:
if agent_id in self.agent_pos:
spawn_idx = self.agent_pos[agent_id]
return tuple(self.spawn_coords[spawn_idx])
return (0,0)
def check_spawn_complete(self,all_agents:List[AgentSetup])->bool:
return (len(self.spawned_agents)<len(all_agents) and\
np.sum(~self.filled_coords) > 0)