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

1
HEAD Normal file
View File

@@ -0,0 +1 @@
ref: refs/heads/main

Binary file not shown.

Binary file not shown.

Binary file not shown.

14
_0_next-steps.txt Normal file
View File

@@ -0,0 +1,14 @@
The next steps to implement, ordered by level of urgency
will be the following:
(a) ensuring agents do not get stuck on geometry
during the simulation
(b) ensuring agents are properly spawning during the
simulation after the first time step
(c) making agents in visualization have the correct
size based on their assigned radius
(d) colouring agents in the visualization based on
which grade they are assigned

BIN
_SRS_sim_010542_.sqlite Normal file

Binary file not shown.

BIN
_SRS_sim_013834_.sqlite Normal file

Binary file not shown.

BIN
_SRS_sim_231233_.sqlite Normal file

Binary file not shown.

BIN
_SRS_sim_231809_.sqlite Normal file

Binary file not shown.

BIN
_SRS_sim_232009_.sqlite Normal file

Binary file not shown.

132
_source_/agent_data.py Normal file
View File

@@ -0,0 +1,132 @@
grade_data = {
"Kindergarden":{
#"Door":0,
"Pop Current":34,
"Pop Mean":31.43,
"Pop Std Dev":5.65,
"Speed Mean":1.21,
"Speed Std Dev":0.24,
"Radius":0.407,
#"Spawn Time":None
},
"Grade 1":{
#"Door":0,
"Pop Current":26,
"Pop Mean":32.57,
"Pop Std Dev":6.27,
"Speed Mean":1.35,
"Speed Std Dev":0.26,
"Radius":0.407,
#"Spawn Time":None
},
"Grade 2":{
#"Door":0,
"Pop Current":42,
"Pop Mean":34.43,
"Pop Std Dev":6.80,
"Speed Mean":1.42,
"Speed Std Dev":0.28,
"Radius":0.407,
#"Spawn Time":None
},
"Grade 3":{
#"Door":0,
"Pop Current":39,
"Pop Mean":35.43,
"Pop Std Dev":5.19,
"Speed Mean":1.48,
"Speed Std Dev":0.23,
"Radius":0.407,
#"Spawn Time":None
},
"Grade 4":{
#"Door":1,
"Pop Current":30,
"Pop Mean":34.86,
"Pop Std Dev":6.77,
"Speed Mean":1.58,
"Speed Std Dev":0.26,
"Radius":0.417,
#"Spawn Time":None
},
"Grade 5":{
#"Door":1,
"Pop Current":43,
"Pop Mean":36.71,
"Pop Std Dev":7.09,
"Speed Mean":1.59,
"Speed Std Dev":0.24,
"Radius":0.434,
#"Spawn Time":None
},
"Grade 6":{
#"Door":1,
"Pop Current":29,
"Pop Mean":37.71,
"Pop Std Dev":6.99,
"Speed Mean":1.65,
"Speed Std Dev":0.24,
"Radius":0.454,
#"Spawn Time":None
},
"Grade 7":{
#"Door":2,
"Pop Current":45,
"Pop Mean":40.43,
"Pop Std Dev":6.02,
"Speed Mean":1.61,
"Speed Std Dev":0.25,
"Radius":0.471,
#"Spawn Time":None
},
"Grade 8":{
#"Door":2,
"Pop Current":36,
"Pop Mean":40.43,
"Pop Std Dev":5.50,
"Speed Mean":1.66,
"Speed Std Dev":0.24,
"Radius":0.488,
#"Spawn Time":None
},
"Grade 9":{
#"Door":2,
"Pop Current":44,
"Pop Mean":44.14,
"Pop Std Dev":4.85,
"Speed Mean":1.60,
"Speed Std Dev":0.24,
"Radius":0.500,
#"Spawn Time":None
},
"Grade 10":{
#"Door":2,
"Pop Current":36,
"Pop Mean":46.29,
"Pop Std Dev":6.29,
"Speed Mean":1.57,
"Speed Std Dev":0.23,
"Radius":0.507,
#"Spawn Time":None
},
"Grade 11":{
#"Door":2,
"Pop Current":54,
"Pop Mean":48.29,
"Pop Std Dev":3.30,
"Speed Mean":1.51,
"Speed Std Dev":0.22,
"Radius":0.515,
#"Spawn Time":None
},
"Grade 12":{
#"Door":2,
"Pop Current":46,
"Pop Mean":43.71,
"Pop Std Dev":6.02,
"Speed Mean":1.54,
"Speed Std Dev":0.23,
"Radius":0.520,
#"Spawn Time":None
}}

35
_source_/agent_setup.py Normal file
View File

@@ -0,0 +1,35 @@
from agent_data import grade_data
from dataclasses import dataclass
import numpy as np
from typing import List,Tuple,Dict
@dataclass
class AgentSetup:
id:int
grade:str
speed:float
radius:float
def AgentConfig()->Tuple[List[AgentSetup],Dict[str,int]]:
agent_id = 1
pop_count = {}
all_agents = []
for key in grade_data.keys():
grade = grade_data[key]
#pop_count.append(grade["Pop Current"])
pop_count[key] = grade["Pop Current"]
for _ in range(grade["Pop Current"]):
rng = np.random.default_rng()
agent = AgentSetup(
id=agent_id,
grade=key,
speed=max(0.1,(rng.normal(
grade["Speed Mean"],
grade["Speed Std Dev"]
))),
radius=grade["Radius"]
)
all_agents.append(agent)
agent_id +=1
return all_agents,pop_count

17
_source_/config.py Normal file
View File

@@ -0,0 +1,17 @@
# -*- coding: utf-8 -*-
"""
E.Drake - ENGN-2220
Thu Jan 22 23:48:50 2026
"""
from pathlib import Path
ROOT = Path(__file__).parent.parent
SOURCE_DIR = ROOT/"_source_"
ARCHIVE_DIR = ROOT/"archive"
PATH_DIR = ROOT/"path"
AGENTS_DIR = ROOT/SOURCE_DIR/"agents"
#GEO_DIR = ROOT/SOURCE_DIR/"sim_geometry"
#TEST_DIR = ROOT/SOURCE_DIR/"test"

153
_source_/crosswalk_setup.py Normal file
View File

@@ -0,0 +1,153 @@
from dataclasses import dataclass,field
from enum import Enum
from typing import List,Dict,Tuple,Set
from shapely import Polygon,Point
from geometry_setup import GeometrySetup
import shapely
import numpy as np
class CrosswalkStatus(Enum):
INACTIVE = "inactive"
ACTIVE = "active"
COOLDOWN = "cooldown"
@dataclass(frozen=True)
class CrosswalkConfig:
trigger_dist:float
activation_time:float
cooldown_time:float
min_agents_activation:int
activation_delay:float
crosswalk_area:Polygon
trigger_area:Polygon
@dataclass
class CrosswalkState:
status:CrosswalkStatus
is_active:bool
time_active:float
time_remaining:float
agents_in_trigger:int
agents_waiting:int
activation_count:int
class CrosswalkController:
def __init__(self,config:CrosswalkConfig,current_setup:bool):
self.config = config
self.crosswalk_area = config.crosswalk_area
self.trigger_area = config.trigger_area
self.status = CrosswalkStatus.INACTIVE
self.state_start_time = 0.0
self.current_time = 0.0
self.activation_count = 0
self.agents_in_trigger: Set[int] = set()
self.agents_waiting: Set[int] = set()
self.total_active_time = 0.0
self.agents_served = 0
def update(
self,
agent_pos:Dict[int,Tuple[float,float]],
time_step:float,
current_time:float=None
)->CrosswalkState:
if current_time is not None:
self.current_time = current_time
else:
self.current_time +=time_step
self.update_agents_in_trigger(agent_pos)
self.update_state(time_step)
self.update_statistics(time_step)
return self.get_state()
def update_agents_in_trigger(
self,
agent_pos:Dict[int,Tuple[float,float]]
)->None:
self.agents_in_trigger.clear()
if not agent_pos:
return
agent_ids = list(agent_pos.keys())
positions = np.array(list(agent_pos.values()),dtype=np.float32)
in_trigger = self.points_in_area(positions,self.trigger_area)
for i, agent_id in enumerate(agent_ids):
if in_trigger[i]:
self.agents_in_trigger.add(agent_id)
@staticmethod
def points_in_area(points:np.ndarray,polygon:Polygon)->np.ndarray:
if len(points) ==0:
return np.array([],dtype=bool)
buffered_area = polygon.buffer(1e-9)
inside = np.zeros(len(points),dtype=bool)
for i,(x,y) in enumerate(points):
point = Point(x,y)
inside[i] = buffered_area.contains(point)
return inside
def update_state(self,time_step:float)->None:
elapsed = self.current_time - self.state_start_time
if self.status == CrosswalkStatus.ACTIVE:
if elapsed >= self.config.activation_time:
self.deactivate()
elif self.status == CrosswalkStatus.COOLDOWN:
if elapsed >= self.config.cooldown_time:
self.status = CrosswalkStatus.INACTIVE
self.state_start_time = self.current_time
elif self.status ==CrosswalkStatus.INACTIVE:
if (len(self.agents_in_trigger)>=self.config.min_agents_activation and \
elapsed >= self.config.activation_delay):
self.activate()
def activate(self)->None:
self.status = CrosswalkStatus.ACTIVE
self.state_start_time = self.current_time
self.activation_count +=1
self.agents_served += len(self.agents_waiting)
self.agents_waiting.clear()
def deactivate(self)->None:
self.status = CrosswalkStatus.COOLDOWN
self.state_start_time = self.current_time
def update_statistics(self,time_step:float)->None:
if self.status == CrosswalkStatus.ACTIVE:
self.total_active_time += time_step
def get_state(self)->CrosswalkState:
elapsed = self.current_time-self.state_start_time
if self.status == CrosswalkStatus.ACTIVE:
time_remaining = max(0.0,self.config.activation_time-elapsed)
elif self.status == CrosswalkStatus.COOLDOWN:
time_remaining = max(0.0,self.config.cooldown_time-elapsed)
else:
time_remaining = 0.0
return CrosswalkState(
status=self.status,
is_active=(self.status == CrosswalkStatus.ACTIVE),
time_active=elapsed if self.status == CrosswalkStatus.ACTIVE else 0.0,
time_remaining=time_remaining,
agents_in_trigger=len(self.agents_in_trigger),
agents_waiting=len(self.agents_waiting),
activation_count = self.activation_count
)
def can_agent_cross(self,agent_id:int)->bool:
if self.status == CrosswalkStatus.ACTIVE:
return True
else:
if agent_id in self.agents_in_trigger:
self.agents_waiting.add(agent_id)
return False
@property
def is_active(self)->bool:
return self.status == CrosswalkStatus.ACTIVE
def get_statistics(self)->Dict[str,float]:
return {
"total_activations":self.activation_count,
"total_active_time":self.total_active_time,
"agents_served":self.agents_served,
"current_agents_in_trigger":len(self.agents_in_trigger),
"current_agents_waiting":len(self.agents_waiting)
}

279
_source_/geometry_setup.py Normal file
View File

@@ -0,0 +1,279 @@
import sys
import matplotlib
matplotlib.use('QtAgg')
import matplotlib.pyplot as plt
from matplotlib.figure import Figure
from matplotlib.axes import Axes
from shapely import Polygon
from shapely.plotting import plot_polygon
from typing import Tuple
class GeometrySetup:
def __init__(self,current_setup:bool):
self.current_setup = current_setup
self.spawn_area = self.spawn()
self.queue_area = self.queue()
self.grass_area = self.grass()
self.crosswalk_area = self.crosswalk()
self.entry_area = self.entry_polygon()
self.exit_area = self.exit_polygon()
def spawn(self)->Polygon:
spawn_area = Polygon([
(25.0,16.823),
(25.0,4.569),
(26.163,4.569),
(28.214,5.07),
(28.214,17.761),
(25.787,17.761),
])
return spawn_area
def queue(self)->Polygon:
if self.current_setup is True:
queue_area = Polygon([
(18.767,9.395),
(16.924,9.395),
(15.214,9.395),
(15.214,0),
(16.924,0),
(16.924,4.569),
(25.0,4.569),
(25.0,16.823)
])
else:
queue_area = Polygon([
(19.531,10.306),
(15.214,10.306),
(13.88,10.306),
(12.98,9.896),
(12.98,0),
(15.214,0),
(16.924,0),
(16.924,4.569),
(25.0,4.569),
(25.0,16.823)
])
return queue_area
def grass(self)->Polygon:
if self.current_setup is True:
grass_area = Polygon([
(4,0),
(0,0),
(0,17.761),
(4,17.761)
])
else:
grass_area = Polygon([
(6.23,0),
(0,0),
(0,17.761),
(4,17.761),
(4,10.306),
(5.33,10.306),
(6.23,9.896)
])
return grass_area
def crosswalk(self)->Polygon:
if self.current_setup is True:
crosswalk_area = Polygon([
(4,6.068),
(4,7.896),
(15.214,7.896),
(15.214,6.068)
])
else:
crosswalk_area = Polygon([
(6.23,4.982),
(6.23,8.982),
(12.98,8.982),
(12.98,4.982)
])
return crosswalk_area
def entry_polygon(self)->Polygon:
if self.current_setup is True:
entry_area = Polygon([ # x: 2.9m, y: 3.428m
(15.314,5.268), # dx: 0.1m, dy: 0.8m
(15.314,8.696),
(18.214,8.696),
(18.214,5.268)
])
else:
entry_area = Polygon([ # x: 2.9m, y: 5.6m
(15.98,9.782),
(15.98,4.182),
(13.08,4.182),
(13.08,9.782)
])
return entry_area
def exit_polygon(self)->Polygon:
if self.current_setup is True:
exit_area = Polygon([ # x: 2.9m, y: 3.428m
(1,5.268),
(1,8.696),
(3.9,8.696),
(3.9,5.268)
])
else:
exit_area = Polygon([ # x: 2.9m, y: 5.6m
(3.23,4.182),
(3.23,9.782),
(6.13,9.782),
(6.13,4.182)
])
return exit_area
def plot_all(self)->Tuple[Figure,Axes]:
fig,ax = plt.subplots(figsize=(12,8))
plot_polygon(
self.spawn_area,
color="lightblue",
add_points=False,
ax=ax,
alpha=0.4
)
plot_polygon(
self.queue_area,
color="blue",
add_points=False,
ax=ax,
alpha=0.4
)
plot_polygon(
self.grass_area,
color="blue",
add_points=False,
ax=ax,
alpha=0.4
)
plot_polygon(
self.crosswalk_area,
color="red",
add_points=False,
ax=ax,
alpha=0.4
)
plot_polygon(
self.entry_area,
color="green",
add_points=False,
ax=ax,
alpha=0.4
)
plot_polygon(
self.exit_area,
color="green",
add_points=False,
ax=ax,
alpha=0.4
)
plt.plot(
*self.spawn_area.exterior.xy,
color='black',
alpha=0.7,
linewidth=1
)
plt.plot(
*self.queue_area.exterior.xy,
color='black',
alpha=0.7,
linewidth=1
)
plt.plot(
*self.grass_area.exterior.xy,
color='black',
alpha=0.7,
linewidth=1
)
plt.plot(
*self.crosswalk_area.exterior.xy,
color='black',
alpha=0.7,
linewidth=1
)
plt.plot(
*self.entry_area.exterior.xy,
color='black',
alpha=0.7,
linewidth=1
)
plt.plot(
*self.exit_area.exterior.xy,
color='black',
alpha=0.7,
linewidth=1
)
ax.text(
self.spawn_area.centroid.x,
self.spawn_area.centroid.y,
'Spawn\nArea',
ha='center',
va='center',
fontweight='bold'
)
ax.text(
self.queue_area.centroid.x,
self.queue_area.centroid.y,
'Queue',
ha='center',
va='center',
fontweight='bold'
)
ax.text(
self.crosswalk_area.centroid.x,
self.crosswalk_area.centroid.y,
'Crosswalk',
ha='center',
va='center',
fontweight='bold',
)
ax.text(
self.entry_area.centroid.x,
self.entry_area.centroid.y,
'Crosswalk\nEntry',
ha='center',
va='center',
fontsize=9,
fontweight='bold',
#color="white"
)
ax.text(
self.exit_area.centroid.x,
self.exit_area.centroid.y,
'Crosswalk\nExit',
ha='center',
va='center',
fontsize=9,
fontweight='bold',
#color="white"
)
ax.text(
(self.grass_area.centroid.x-0.9 if not\
self.current_setup else self.grass_area.centroid.x),
self.grass_area.centroid.y+2,
'Grass',
ha='center',
va='center',
fontweight='bold'
)
ax.set_xlabel('X (m)')
ax.set_ylabel('Y (m)')
ax.set_title(
f'{"Current" if self.current_setup else "Smart"} Crosswalk Design',
fontweight='bold',
fontsize=15
)
ax.set_aspect('equal')
ax.grid(True, alpha=0.3)
return fig,ax
if __name__ == "__main__":
from PyQt6 import QtWidgets
app = QtWidgets.QApplication(sys.argv)
fig,ax = GeometrySetup(True).plot_all()
plt.show(block=False)
sys.exit(app.exec())

View File

@@ -0,0 +1,9 @@
matplotlib
PyQt5
numpy
scipy
shapely
pedpy
jupedsim
pandas
plotly

253
_source_/simulation.py Normal file
View File

@@ -0,0 +1,253 @@
from typing import Tuple,List,Dict
import sys
from datetime import datetime as dt
import matplotlib
matplotlib.use('QtAgg')
import matplotlib.pyplot as plt
from shapely import Polygon,Point,GeometryCollection,get_coordinates
from shapely.plotting import plot_polygon
import jupedsim as jps
import numpy as np
import pathlib
from geometry_setup import GeometrySetup
from spawning import SpawnManager
from crosswalk_setup import CrosswalkStatus,CrosswalkController,CrosswalkState,CrosswalkConfig
from agent_setup import AgentConfig
from dataclasses import dataclass
@dataclass
class SimulationSetup:
simulation_time:float=200.0
time_step:float=0.01
max_iterations:int=50000
trajectory_file:str="crosswalk_sim.sqlite"
current_setup:bool=True
trigger_dist:float=1.0
activation_time:float=30.0
cooldown_time:float=10.0
min_agents_activation:int=1
activation_delay:float=2.0
class CrosswalkSimulation:
def __init__(self,config:SimulationSetup):
self.config = config
self.RED = "\033[1;31m"
self.GREEN = "\033[1;32m"
self.YELLOW = "\033[1;33m"
self.BLUE = "\033[1;34m"
self.PURPLE = "\033[1;35m"
self.CYAN = "\033[1;36m"
self.RESET = "\033[0m"
print(f"{self.YELLOW}\nInitializing Geometry...")
self.geo = GeometrySetup(self.config.current_setup)
self.spawn_area = self.geo.spawn()
self.queue_area = self.geo.queue()
self.grass_area = self.geo.grass()
self.crosswalk_area = self.geo.crosswalk()
self.entry_area = self.geo.entry_polygon()
self.exit_area = self.geo.exit_polygon()
self.walkable_area = GeometryCollection([
self.spawn_area,
self.queue_area,
self.grass_area,
self.crosswalk_area,
])
print(f"{self.GREEN}Geometry Configuration Complete!")
print(f"{self.YELLOW}\nInitializing Agent Setup...")
self.all_agents, pop_count = AgentConfig()
self.total_agents = len(self.all_agents)
self.req_spacing = (max([agent.radius for agent in self.all_agents])*2.2)
print(f"{self.GREEN}Agent Setup Complete!")
print(f"{self.RESET}Generated {self.total_agents} Agents")
print(f"Population Breakdown:\n"+"".join(f"\t{k}: {v}\n" for k,v in pop_count.items()))
print(f"{self.YELLOW}Initializing Spawn Manager...")
self.spawn_manager = SpawnManager(self.spawn_area,min_spacing=self.req_spacing)
self.all_coords = self.spawn_manager.generate_coords()
print(f"{self.GREEN}Spawn Manager Setup Complete!")
print(f"{self.RESET}Generated {len(self.all_coords)} Spawn Coordinates")
print(f"{self.YELLOW}\nInitializing Crosswalk Controller...")
self.crosswalk_controller = CrosswalkController(
CrosswalkConfig(
trigger_dist=self.config.trigger_dist,
activation_time=self.config.activation_time,
cooldown_time=self.config.cooldown_time,
min_agents_activation=self.config.min_agents_activation,
activation_delay=self.config.activation_delay,
crosswalk_area=self.crosswalk_area,
trigger_area=self.entry_area
),
self.config.current_setup
)
print(f"{self.GREEN}Crosswalk Controller Setup Complete!")
print(f"{self.YELLOW}\nInitializing JuPedSim Model...")
self.trajectory_file = self.config.trajectory_file
self.simulation = jps.Simulation(
model=jps.CollisionFreeSpeedModel(),
geometry=self.walkable_area,
trajectory_writer=jps.SqliteTrajectoryWriter(
output_file=pathlib.Path(self.trajectory_file)
),
dt=self.config.time_step
)
self.journey_setup()
self.jps_agent_ids:Dict[int,int] = {}
self.iteration_count = 0
self.current_time = 0.0
print(f"{self.GREEN}JuPedSim Model Initialized{self.RESET}")
print(f"\tTime Step: {self.config.time_step}s")
print(f"\tMax Iterations: {self.config.max_iterations}")
print(f"\tCrosswalk Activation Length: {self.config.activation_time}s")
print(f"\tCrosswalk Cooldown Length: {self.config.cooldown_time}s")
def journey_setup(self):
self.queue_waiting_coords = self.waiting_coords(
self.queue_area,
self.spawn_manager.min_spacing
)
self.queue_stage_id = self.simulation.add_waiting_set_stage(
self.queue_waiting_coords
)
self.queue_stage = self.simulation.get_stage(self.queue_stage_id)
self.queue_stage.state = jps.WaitingSetState.ACTIVE
self.crosswalk_entry_id = self.simulation.add_waypoint_stage((
self.entry_area.centroid.x,
self.entry_area.centroid.y),
self.spawn_manager.min_spacing
)
self.crosswalk_exit_id = self.simulation.add_waypoint_stage((
self.exit_area.centroid.x,
self.exit_area.centroid.y),
self.spawn_manager.min_spacing
)
self.exit_stage_id = self.simulation.add_waypoint_stage((
self.grass_area.centroid.x,
self.grass_area.centroid.y),
self.spawn_manager.min_spacing
)
self.journey = jps.JourneyDescription([
self.queue_stage_id,
self.crosswalk_entry_id,
self.crosswalk_exit_id,
self.exit_stage_id
])
self.journey.set_transition_for_stage(
self.queue_stage_id,
jps.Transition.create_fixed_transition(
self.crosswalk_entry_id
))
self.journey.set_transition_for_stage(
self.crosswalk_entry_id,
jps.Transition.create_fixed_transition(
self.crosswalk_exit_id
))
self.journey.set_transition_for_stage(
self.crosswalk_exit_id,
jps.Transition.create_fixed_transition(
self.exit_stage_id
))
self.journey_id = self.simulation.add_journey(
self.journey)
def waiting_coords(self,area:Polygon,spacing:float)->List[Tuple[float,float]]:
min_x,min_y,max_x,max_y = area.bounds
points = []
x = (min_x+spacing)/2
while x<max_x:
y = (min_y+spacing)/2
while y < max_y:
if area.contains(Point(x, y)):
points.append((x,y))
y += spacing
x += spacing
return points if points else [(min_x+0.5, min_y+0.5)]
def run(self):
print(f"{self.CYAN}\nStarting Simulation")
print(f"Target Agents: {len(self.all_agents)}\n{self.RESET}")
while self.iteration_count < self.config.max_iterations:
if self.spawn_manager.check_spawn_complete(self.all_agents):
new_agent = self.spawn_manager.spawn_agent(self.all_agents)
if new_agent:
jps_agent_params = jps.CollisionFreeSpeedModelAgentParameters(
journey_id=self.journey_id,
stage_id=self.queue_stage_id,
position=self.spawn_manager.get_agent_pos(new_agent.id),
desired_speed=new_agent.speed,
radius=new_agent.radius
)
jps_agent_id = self.simulation.add_agent(jps_agent_params)
self.jps_agent_ids[new_agent.id] = jps_agent_id
#if self.iteration_count % 100 ==0:
# print(f"{self.YELLOW}[Iteration: {self.iteration_count:05d}]{self.RESET} Total Agents Spawned {len(self.jps_agent_ids)}")
print(f"{self.YELLOW}[Iteration: {self.iteration_count:05d}]{self.RESET} Total Agents Spawned {len(self.jps_agent_ids)}")
current_agent_pos = {}
for spawn_id,jps_id in self.jps_agent_ids.items():
try:
agent = self.simulation.agent(jps_id)
current_agent_pos[spawn_id] = (agent.position[0],agent.position[1])
except RuntimeError:
continue
crosswalk_state = self.crosswalk_controller.update(
current_agent_pos,
self.config.time_step,
self.current_time
)
if crosswalk_state.is_active:
if self.queue_stage.state != jps.WaitingSetState.INACTIVE:
self.queue_stage.state = jps.WaitingSetState.INACTIVE
print(f"{self.YELLOW}[Iteration: {self.iteration_count:05d}]{self.RESET} Crosswalk ACTIVATED")
else:
if self.queue_stage.state != jps.WaitingSetState.ACTIVE:
self.queue_stage.state = jps.WaitingSetState.ACTIVE
self.simulation.iterate()
self.current_time += self.config.time_step
self.iteration_count +=1
agents_to_remove = []
for spawn_id, jps_id in list(self.jps_agent_ids.items()):
try:
_ = self.simulation.agent(jps_id)
except RuntimeError:
agents_to_remove.append(spawn_id)
for spawn_id in agents_to_remove:
self.spawn_manager.unfill_coords(spawn_id)
del self.jps_agent_ids[spawn_id]
print(f"{self.YELLOW}[Iteration: {self.iteration_count:05d}]{self.RESET} JOURNEY COMPLETE: Agent {spawn_id}")
if (len(self.jps_agent_ids) ==0 and not self.spawn_manager.check_spawn_complete(self.all_agents)):
print(f"{self.GREEN}\nSimulation Completed at Iteration: {self.iteration_count}")
print(f"Total Simulation Time: {self.current_time:.2f} s")
break
print("\n")
print(f"{self.GREEN}=== SIMULATION COMPLETE ===")
stats = self.crosswalk_controller.get_statistics()
print(f"{self.YELLOW}Crosswalk Activations: {self.RESET}{stats['total_activations']}")
print(f"{self.YELLOW}Agents Served: {self.RESET}{stats['agents_served']}")
print(f"{self.PURPLE}Trajectory File:\n{self.trajectory_file}{self.RESET}\n")
return
def main():
config = SimulationSetup(
simulation_time=200.0,
time_step=0.01,
max_iterations=50000,
trajectory_file=f"_SRS_sim_{dt.now().strftime("%H%M%S")}_.sqlite",
current_setup=True,
trigger_dist=1.0,
activation_time=30.0,
cooldown_time=10.0,
min_agents_activation=1,
activation_delay=2.0,
)
sim = CrosswalkSimulation(config)
sim.run()
if __name__ == "__main__":
main()

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)

47
_source_/visualizer.py Normal file
View File

@@ -0,0 +1,47 @@
import sys
import matplotlib
matplotlib.use('QtAgg')
from PyQt5 import QtWidgets
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
import numpy as np
from jupedsim.internal.notebook_utils import read_sqlite_file
def visualize(trajectory_file):
traj_data,walkable_area = read_sqlite_file(trajectory_file)
df = traj_data.data
fig,ax = plt.subplots(figsize=(12,8))
if hasattr(walkable_area,"polygon"):
x,y = walkable_area.polygon.exterior.xy
ax.plot(x,y,'k-',alpha=0.3)
scatter = ax.scatter([],[],s=20,c="blue",alpha=0.7)
frame_text = ax.text(0.02,0.98,'',transform=ax.transAxes)
frames=sorted(df["frame"].unique())
frames=frames[::5]
def update(frame):
frame_data = df[df["frame"]==frame]
if len(frame_data)>0:
positions=np.column_stack(
[frame_data["x"].values,frame_data["y"].values])
scatter.set_offsets(positions)
frame_text.set_text(f"Frame: {frame}, Agents: {len(frame_data)}")
return scatter, frame_text
anim = FuncAnimation(fig,update,frames=frames,blit=False,interval=1)
app = QtWidgets.QApplication(sys.argv)
plt.show(block=False)
sys.exit(app.exec())
if __name__ == "__main__":
file_1 = "SRS_evac_2025-11-23_23-03-09.sqlite"
file_2 = "SRS_evac_2025-11-24_01-03-06.sqlite"
file_3 = "SRS_evac_2025-11-30_19-43-15.sqlite"
file_4 = "crosswalk_sim.sqlite"
file_5 = "_SRS_sim_232009_.sqlite"
file_6 = "_SRS_sim_231809_.sqlite"
file_7 = "_SRS_sim_231233_.sqlite"
file_8 = "_SRS_sim_010542_.sqlite"
file_9 = "_SRS_sim_013834_.sqlite"
visualize(file_3)

Binary file not shown.

View File

@@ -0,0 +1,286 @@
# -*- coding: utf-8 -*-
"""
Created on Sat Nov 8 22:59:23 2025
@author: ethan
"""
#! /usr/bin/env python3
# SPDX-License-Identifier: LGPL-3.0-or-later
import numpy as np
import pandas as pd
import pathlib
import jupedsim as jps
from shapely import Polygon
from shapely.plotting import plot_polygon
from collections import deque
def main():
grades = {
# mean and standard deviation for
# average population size
# and average speed by grade levels
"Kindergarden":np.array([31.43,5.65,1.21,0.24]),
"Grade 1":np.array([32.57,6.27,1.35,0.26]),
"Grade 2":np.array([34.43,6.80,1.42,0.28]),
"Grade 3":np.array([35.43,5.19,1.48,0.23]),
"Grade 4":np.array([34.86,6.77,1.58,0.26]),
"Grade 5":np.array([36.71,7.09,1.59,0.24]),
"Grade 6":np.array([37.71,6.99,1.65,0.24]),
"Grade 7":np.array([40.43,6.02,1.61,0.25]),
"Grade 8":np.array([40.43,5.50,1.66,0.24]),
"Grade 9":np.array([44.14,4.85,1.60,0.24]),
"Grade 10":np.array([46.29,6.29,1.57,0.23]),
"Grade 11":np.array([48.29,3.30,1.51,0.22]),
"Grade 12":np.array([43.71,6.02,1.54,0.23])
}
df=pd.DataFrame({
"Grade Level":(
list(grades.keys())),
"Pop Mean":[
grades[j][0] for j in grades],
"Pop Std Dev":[
grades[j][1] for j in grades],
"Speed Mean":[
grades[j][2] for j in grades],
"Speed Std Dev":[
grades[j][3] for j in grades]})
rng = np.random.default_rng(seed=42)
A_crosswalk=Polygon([
(-1,1.499),(-1,3.327),
(0,3.327),(11.214, 3.327),
(11.214,1.499),(0,1.499)])
B_queue=Polygon([
(11.214, 0),(22.163, 0),
(22.163, 4.826),(11.214, 4.826)])
C_road_adj_path=Polygon([
(21.787,4.826),(24.214,4.826),
(24.214,40.431),(29.179,40.431),
(29.179,42.511),(24.214,42.511),
(21.787,42.511)])
D_path_k_3=Polygon([
(26.45,42.511),(26.45,52.84),
(26.45,53.84),(29.179,53.84),
(29.179,52.84),(29.179,42.511)])
E_path_4_6=Polygon([
(29.179,42.511),(54.351,42.511),
(60.406,48.842),(60.406,51.22),
(60.406,52.22),(63.49,52.22),
(63.49,51.22),(63.49,47.866),
(56.381,40.431),(29.179,40.431)])
F_path_7_12=Polygon([
(22.163, 0),(39.227, 5.516),
(39.631, 4.267),(39.939,3.315),
(45.099,4.983),(44.792,5.935),
(43.169,10.954),(24.214,4.826),
(22.163,4.826)])
G_extended_queue=Polygon([
(11.214,0),(12.924,0),
(12.924,-4.569),(11.214,-4.569)])
H_angled_path=Polygon([
(21.787,13.192),(21.787,10.527),
(17,4.826),(14.767,4.826)])
enter_k_3=Polygon([
(26.45,52.84),(29.179,52.84),
(29.179,53.84),(26.45,53.84)])
enter_4_6=Polygon([
(60.406,51.22),(60.406,52.22),
(63.49,52.22),(63.49,51.22)])
enter_7_12=Polygon([
(39.631, 4.267),(39.939,3.315),
(45.099,4.983),(44.792,5.935)])
exit_polygon=Polygon([
(0,1.499),(0,3.327),
(-1,3.327),(-1,1.499)])
plot_polygon(
A_crosswalk,color="black",add_points=False)
plot_polygon(
B_queue,color="black",add_points=False)
plot_polygon(
C_road_adj_path, color="blue",add_points=False)
plot_polygon(
D_path_k_3, color="blue",add_points=False)
plot_polygon(
E_path_4_6, color="blue",add_points=False)
plot_polygon(
F_path_7_12, color="blue",add_points=False)
plot_polygon(
G_extended_queue, color="black",add_points=False)
plot_polygon(
H_angled_path, color="black",add_points=False)
plot_polygon(
enter_k_3, color="darkgreen",add_points=False)
plot_polygon(
enter_4_6, color="darkgreen",add_points=False)
plot_polygon(
enter_7_12, color="darkgreen",add_points=False)
plot_polygon(
exit_polygon, color="orangered",add_points=False)
geometry = (A_crosswalk.union(
B_queue).union(
C_road_adj_path).union(
D_path_k_3).union(
E_path_4_6).union(
F_path_7_12).union(
G_extended_queue).union(
H_angled_path))
trajectory_file = "SRS_evac.sqlite"
simulation = jps.Simulation(
model=jps.AnticipationVelocityModel(),
geometry=geometry,
trajectory_writer=jps.SqliteTrajectoryWriter(
output_file=pathlib.Path(trajectory_file)))
exit_id = simulation.add_exit_stage(exit_polygon)
journey = jps.JourneyDescription([exit_id])
journey_id = simulation.add_journey(journey)
grade_pop = {}
door = {}
door_polygon = {
"K-3":enter_k_3,
"4-6":enter_4_6,
"7-12":enter_7_12,
}
platoon_agents = {}
for i, grade in enumerate(df["Grade Level"]):
grade_sample=rng.normal(
loc=df["Pop Mean"][i],
scale=df["Pop Std Dev"][i],size=1)
grade_pop[grade] = int(np.ceil(grade_sample[0]))
x = grade_pop[grade]
if i < 4:
door[grade] = "K-3"
elif i <7:
door[grade] = "4-6"
else:
door[grade] = "7-12"
platoon_a_size = int(x/2)
platoon_b_size = x - platoon_a_size
platoon_a_id = (2*(i+1))-1
platoon_b_id = (2*(i+1))
platoon_agents[platoon_a_id] ={
"Grade Level": grade,
"Platoon Size": platoon_a_size,
"Entry Door":door_polygon[door[grade]]
}
platoon_agents[platoon_b_id] ={
"Grade Level": grade,
"Platoon Size": platoon_b_size,
"Entry Door":door_polygon[door[grade]]
}
spawned_total=0
#============================================================
agent_set = []
for platoon_id, platoon_data in platoon_agents.items():
spawn_time=float(
rng.uniform(5,15)+rng.uniform(0,120))
spawn_time=min(spawn_time,120)
remaining=int(platoon_data["Platoon Size"])
attempts = 0
max_attempts = 50
time_delay = 1.0
batch_size=max(1,min(10,remaining))
while remaining>0 and attempts<max_attempts:
n_try = min(batch_size,remaining)
try:
positions = jps.distribute_by_number(
polygon=platoon_data["Entry Door"],
number_of_agents=n_try,
distance_to_agents=0.45,
distance_to_polygon=0.3,
max_iterations=1500)
placed_count = len(positions)
if placed_count ==0:
attempts +=1
spawn_time+=time_delay
batch_size=max(1,batch_size//2)
continue
offset=0.1
for k, pos in enumerate(positions):
speed=float(rng.normal(
loc=df["Speed Mean"][i],
scale=df["Speed Std Dev"][i],
size=1)[0])
agent = {
"Grade Level":
platoon_data["Grade Level"],
"Entry Point":
(door[platoon_data["Grade Level"]]),
"Platoon":platoon_id,
"Position":(
float(pos[0]),float(pos[1])),
"Speed":speed,
"Spawn Time":float(
spawn_time+(k*offset))}
agent_set.append(agent)
remaining-=placed_count
if remaining>0:
attempts+=1
spawn_time+=time_delay
attempts=0
batch_size=max(1,min(10,remaining))
except Exception as e:
print(
f"Error placing platoon {platoon_id}: {e}")
print("Reducing batch and retrying")
attempts+=1
batch_size=max(1,batch_size//2)
spawn_time+=time_delay
spawned_total+=placed_count
#=========================================
pending=sorted(agent_set,key=lambda a:a["Spawn Time"])
pending=deque(pending)
max_iterations=20000
max_agents_per_step=50
spawn_tolerance=1e-8
while ((simulation.agent_count()>0 or len(pending)>0)
and simulation.iteration_count()<max_iterations):
current_time=simulation.elapsed_time()
agents_this_step=0
while (pending and (pending[0]["Spawn Time"]<=(
current_time+spawn_tolerance))
and agents_this_step<max_agents_per_step):
a = pending.popleft()
pos=tuple(a["Position"])
v0 = float(a["Speed"])
v0 = float(np.clip(v0,0.2,2.5))
agent_params = (
jps.AnticipationVelocityModelAgentParameters(
journey_id=journey_id,
stage_id=exit_id,
position=pos,
radius=0.25,
desired_speed=v0,
anticipation_time=0.5,
reaction_time=0.3,
wall_buffer_distance=0.08))
retry=0
max_retry=25
while retry < max_retry:
try:
simulation.add_agent(agent_params)
spawned_total +=1
agents_this_step+=1
break
except Exception as e:
print("Failed: add_agent")
print(f"For: pos={pos}, speed={v0}")
print(f"{e}: Rescheduling...")
retry +=1
if retry >= max_retry:
print(f"\n\nMax Retries:{max_retry}")
break
adj_pos=(pos[0]+rng.uniform(-0.1,0.1),
pos[1]+rng.uniform(-0.1,0.1))
agent_params.position =adj_pos
simulation.iterate()
iter_count = simulation.iteration_count()
print(f"\nTime Step: {iter_count}")
print(f"Total Agents: {spawned_total}")
print(f"Platoon: {platoon_agents.items()}")
print("\nSimulation Completed!")
print(f"Total Time Steps: {simulation.iteration_count()}")
print(f"Elapsed Time: {simulation.elapsed_time()}")
print(f"{trajectory_file = }")
if __name__ == "__main__":
main()

View File

@@ -0,0 +1,349 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Mon Nov 10 01:02:31 2025
@author: ethan
"""
# SPDX-License-Identifier: LGPL-3.0-or-later
import numpy as np
import pandas as pd
import pathlib
import jupedsim as jps
from shapely import Polygon
from shapely.plotting import plot_polygon
from collections import deque
def main():
grades = {
# mean and standard deviation for
# average population size
# and average speed by grade levels
"Kindergarden":np.array([31.43,5.65,1.21,0.24]),
"Grade 1":np.array([32.57,6.27,1.35,0.26]),
"Grade 2":np.array([34.43,6.80,1.42,0.28]),
"Grade 3":np.array([35.43,5.19,1.48,0.23]),
"Grade 4":np.array([34.86,6.77,1.58,0.26]),
"Grade 5":np.array([36.71,7.09,1.59,0.24]),
"Grade 6":np.array([37.71,6.99,1.65,0.24]),
"Grade 7":np.array([40.43,6.02,1.61,0.25]),
"Grade 8":np.array([40.43,5.50,1.66,0.24]),
"Grade 9":np.array([44.14,4.85,1.60,0.24]),
"Grade 10":np.array([46.29,6.29,1.57,0.23]),
"Grade 11":np.array([48.29,3.30,1.51,0.22]),
"Grade 12":np.array([43.71,6.02,1.54,0.23])
}
df=pd.DataFrame({
"Grade Level":(
list(grades.keys())),
"Pop Mean":[
grades[j][0] for j in grades],
"Pop Std Dev":[
grades[j][1] for j in grades],
"Speed Mean":[
grades[j][2] for j in grades],
"Speed Std Dev":[
grades[j][3] for j in grades]})
rng = np.random.default_rng(seed=42)
A_crosswalk=Polygon([
(-1,1.499),(-1,3.327),
(0,3.327),(11.214, 3.327),
(11.214,1.499),(0,1.499)])
B_queue=Polygon([
(11.214, 0),(22.163, 0),
(22.163, 4.826),(11.214, 4.826)])
C_road_adj_path=Polygon([
(21.787,4.826),(24.214,4.826),
(24.214,40.431),(29.179,40.431),
(29.179,42.511),(24.214,42.511),
(21.787,42.511)])
D_path_k_3=Polygon([
(26.45,42.511),(26.45,52.84),
(26.45,53.84),(29.179,53.84),
(29.179,52.84),(29.179,42.511)])
E_path_4_6=Polygon([
(29.179,42.511),(54.351,42.511),
(60.406,48.842),(60.406,51.22),
(60.406,52.22),(63.49,52.22),
(63.49,51.22),(63.49,47.866),
(56.381,40.431),(29.179,40.431)])
F_path_7_12=Polygon([
(22.163, 0),(39.227, 5.516),
(39.631, 4.267),(39.939,3.315),
(45.099,4.983),(44.792,5.935),
(43.169,10.954),(24.214,4.826),
(22.163,4.826)])
G_extended_queue=Polygon([
(11.214,0),(12.924,0),
(12.924,-4.569),(11.214,-4.569)])
H_angled_path=Polygon([
(21.787,13.192),(21.787,10.527),
(17,4.826),(14.767,4.826)])
enter_k_3=Polygon([
(26.45,52.84),(29.179,52.84),
(29.179,53.84),(26.45,53.84)])
enter_4_6=Polygon([
(60.406,51.22),(60.406,52.22),
(63.49,52.22),(63.49,51.22)])
enter_7_12=Polygon([
(39.631, 4.267),(39.939,3.315),
(45.099,4.983),(44.792,5.935)])
exit_polygon=Polygon([
(0,1.499),(0,3.327),
(-1,3.327),(-1,1.499)])
plot_polygon(
A_crosswalk,color="black",add_points=False)
plot_polygon(
B_queue,color="black",add_points=False)
plot_polygon(
C_road_adj_path, color="blue",add_points=False)
plot_polygon(
D_path_k_3, color="blue",add_points=False)
plot_polygon(
E_path_4_6, color="blue",add_points=False)
plot_polygon(
F_path_7_12, color="blue",add_points=False)
plot_polygon(
G_extended_queue, color="black",add_points=False)
plot_polygon(
H_angled_path, color="black",add_points=False)
plot_polygon(
enter_k_3, color="darkgreen",add_points=False)
plot_polygon(
enter_4_6, color="darkgreen",add_points=False)
plot_polygon(
enter_7_12, color="darkgreen",add_points=False)
plot_polygon(
exit_polygon, color="orangered",add_points=False)
geometry = (A_crosswalk.union(
B_queue).union(
C_road_adj_path).union(
D_path_k_3).union(
E_path_4_6).union(
F_path_7_12).union(
G_extended_queue).union(
H_angled_path))
trajectory_file = "SRS_evac.sqlite"
simulation = jps.Simulation(
model=jps.AnticipationVelocityModel(),
geometry=geometry,
trajectory_writer=jps.SqliteTrajectoryWriter(
output_file=pathlib.Path(trajectory_file)))
exit_id = simulation.add_exit_stage(exit_polygon)
journey = jps.JourneyDescription([exit_id])
journey_id = simulation.add_journey(journey)
grade_pop = {}
door = {}
door_polygon = {
"K-3":enter_k_3,
"4-6":enter_4_6,
"7-12":enter_7_12,
}
platoon_agents = {}
for i, grade in enumerate(df["Grade Level"]):
grade_sample=rng.normal(
loc=df["Pop Mean"][i],
scale=df["Pop Std Dev"][i],size=1)
grade_pop[grade] = int(np.ceil(grade_sample[0]))
x = grade_pop[grade]
if i < 4:
door[grade] = "K-3"
elif i <7:
door[grade] = "4-6"
else:
door[grade] = "7-12"
platoon_a_size = int(x/2)
platoon_b_size = x - platoon_a_size
platoon_a_id = (2*(i+1))-1
platoon_b_id = (2*(i+1))
platoon_agents[platoon_a_id] ={
"Grade Level": grade,
"Platoon Size": platoon_a_size,
"Entry Door":door_polygon[door[grade]]
}
platoon_agents[platoon_b_id] ={
"Grade Level": grade,
"Platoon Size": platoon_b_size,
"Entry Door":door_polygon[door[grade]]
}
#================================================
#================================================
#================================================
#================================================
agent_set = []
for platoon_id, platoon_data in platoon_agents.items():
spawn_time=float(
rng.uniform(5,15)+rng.uniform(0,120))
spawn_time=min(spawn_time,120)
remaining=int(platoon_data["Platoon Size"])
attempts = 0
max_attempts = 10
time_delay = 1.0
batch_size=max(1,min(10,remaining))
while remaining>0 and attempts<max_attempts:
n_try = min(batch_size,remaining)
try:
positions = jps.distribute_by_number(
polygon=platoon_data["Entry Door"],
number_of_agents=n_try,
distance_to_agents=0.45,
distance_to_polygon=0.3,
max_iterations=1500)
placed_count = len(positions)
if placed_count ==0:
attempts +=1
spawn_time+=time_delay
batch_size=max(1,batch_size//2)
continue
offset=0.1
for k, pos in enumerate(positions):
speed=float(rng.normal(
loc=df["Speed Mean"][i],
scale=df["Speed Std Dev"][i],
size=1)[0])
agent = {
"Grade Level":
platoon_data["Grade Level"],
"Entry Point":
(door[platoon_data["Grade Level"]]),
"Platoon":platoon_id,
"Position":(
float(pos[0]),float(pos[1])),
"Speed":speed,
"Spawn Time":float(
spawn_time+(k*offset))}
agent_set.append(agent)
remaining-=placed_count
if remaining>0:
attempts+=1
spawn_time+=time_delay
attempts=0
batch_size=max(1,min(10,remaining))
except Exception as e:
print(
f"Error placing platoon {platoon_id}: {e}")
print("Reducing batch and retrying")
attempts+=1
batch_size=max(1,batch_size//2)
spawn_time+=time_delay
#================================================
#================================================
#================================================
#================================================
# Group platoons by entry door first
door_platoons = {}
for platoon_id, platoon_data in platoon_agents.items():
door = platoon_data["Entry Door"]
if door not in door_platoons:
door_platoons[door] = []
door_platoons[door].append((platoon_id, platoon_data))
# Process each door sequentially
agent_set = []
for door_poly, platoon_list in door_platoons.items():
# Calculate total agents for this door
total_agents = sum(data["Platoon Size"] for _, data in platoon_list)
# Generate all positions for this door at once
try:
all_positions = jps.distribute_by_number(
polygon=door_poly,
number_of_agents=total_agents,
distance_to_agents=0.6, # Increased for safety
distance_to_polygon=0.3,
max_iterations=1500
)
# Distribute positions to platoons
position_index = 0
for platoon_id, platoon_data in platoon_list:
platoon_size = platoon_data["Platoon Size"]
platoon_positions = all_positions[position_index:position_index + platoon_size]
position_index += platoon_size
# Create agents for this platoon
spawn_time = float(rng.uniform(5,15) + rng.uniform(0,120))
spawn_time = min(spawn_time, 120)
offset = 0.1
for k, pos in enumerate(platoon_positions):
speed = float(rng.normal(
loc=df["Speed Mean"][platoon_data["Grade Index"]], # You'll need to store grade index
scale=df["Speed Std Dev"][platoon_data["Grade Index"]],
size=1
)[0])
agent = {
"Grade Level": platoon_data["Grade Level"],
"Entry Point": door_poly, # Or door name if you prefer
"Platoon": platoon_id,
"Position": (float(pos[0]), float(pos[1])),
"Speed": speed,
"Spawn Time": float(spawn_time + (k * offset))
}
agent_set.append(agent)
except Exception as e:
print(f"Error generating positions for door: {e}")
# Fallback: use your original per-platoon approach for this door
#================================================
#================================================
#================================================
#================================================
pending=sorted(agent_set,key=lambda a:a["Spawn Time"])
pending=deque(pending)
max_iterations=1500
spawned_total=0
max_agents_per_step=25
spawn_tolerance=1e-8
while ((simulation.agent_count()>0 or len(pending)>0)
and simulation.iteration_count()<max_iterations):
current_time=simulation.elapsed_time()
agents_this_step=0
while (pending and (pending[0]["Spawn Time"]<=(
current_time+spawn_tolerance))
and agents_this_step<max_agents_per_step):
a = pending.popleft()
pos=tuple(a["Position"])
v0 = float(a["Speed"])
v0 = float(np.clip(v0,0.2,2.5))
agent_params = (
jps.AnticipationVelocityModelAgentParameters(
journey_id=journey_id,
stage_id=exit_id,
position=pos,
radius=0.25,
desired_speed=v0,
anticipation_time=0.5,
reaction_time=0.3,
wall_buffer_distance=0.08))
retry=0
max_retry=10
while retry < max_retry:
try:
simulation.add_agent(agent_params)
spawned_total +=1
agents_this_step+=1
break
except Exception as e:
print("Failed: add_agent")
print(f"For: pos={pos}, speed={v0}")
print(f"{e}: Rescheduling...")
retry +=1
if retry >= max_retry:
print(f"\n\nMax Retries:{max_retry}")
break
adj_pos=(pos[0]+rng.uniform(-0.1,0.1),
pos[1]+rng.uniform(-0.1,0.1))
agent_params.position =adj_pos
simulation.iterate()
iter_count = simulation.iteration_count()
print(f"Iteration Complete: {iter_count}")
print("Simulation Completed!")
print(f"Iterations: {simulation.iteration_count()}")
print(f"Elapsed Time: {simulation.elapsed_time()}")
print(f"{trajectory_file = }")
if __name__ == "__main__":
main()

View File

@@ -0,0 +1,314 @@
"""
Created on Sat Nov 22 14:42:45 2025
@author: ethan
"""
import numpy as np
import pandas as pd
import pathlib
import jupedsim as jps
from shapely import Polygon
from shapely.plotting import plot_polygon
from collections import deque
from datetime import datetime
def main():
grades = {
"Kindergarden":np.array([31.43,5.65,1.21,0.24]),
"Grade 1":np.array([32.57,6.27,1.35,0.26]),
"Grade 2":np.array([34.43,6.80,1.42,0.28]),
"Grade 3":np.array([35.43,5.19,1.48,0.23]),
"Grade 4":np.array([34.86,6.77,1.58,0.26]),
"Grade 5":np.array([36.71,7.09,1.59,0.24]),
"Grade 6":np.array([37.71,6.99,1.65,0.24]),
"Grade 7":np.array([40.43,6.02,1.61,0.25]),
"Grade 8":np.array([40.43,5.50,1.66,0.24]),
"Grade 9":np.array([44.14,4.85,1.60,0.24]),
"Grade 10":np.array([46.29,6.29,1.57,0.23]),
"Grade 11":np.array([48.29,3.30,1.51,0.22]),
"Grade 12":np.array([43.71,6.02,1.54,0.23])
}
df=pd.DataFrame({
"Grade Level":(
list(grades.keys())),
"Pop Mean":[
grades[j][0] for j in grades],
"Pop Std Dev":[
grades[j][1] for j in grades],
"Speed Mean":[
grades[j][2] for j in grades],
"Speed Std Dev":[
grades[j][3] for j in grades]})
rng = np.random.default_rng(seed=42)
A_crosswalk=Polygon([
(-1,1.499),(-1,3.327),
(0,3.327),(11.214, 3.327),
(11.214,1.499),(0,1.499)])
B_queue=Polygon([
(11.214, 0),(22.163, 0),
(22.163, 4.826),(11.214, 4.826)])
C_road_adj_path=Polygon([
(21.787,4.826),(24.214,4.826),
(24.214,40.431),(29.179,40.431),
(29.179,42.511),(24.214,42.511),
(21.787,42.511)])
D_path_k_3=Polygon([
(26.45,42.511),(26.45,52.84),
(26.45,53.84),(29.179,53.84),
(29.179,52.84),(29.179,42.511)])
E_path_4_6=Polygon([
(29.179,42.511),(54.351,42.511),
(60.406,48.842),(60.406,51.22),
(60.406,52.22),(63.49,52.22),
(63.49,51.22),(63.49,47.866),
(56.381,40.431),(29.179,40.431)])
F_path_7_12=Polygon([
(22.163, 0),(39.227, 5.516),
(39.631, 4.267),(39.939,3.315),
(45.099,4.983),(44.792,5.935),
(43.169,10.954),(24.214,4.826),
(22.163,4.826)])
G_extended_queue=Polygon([
(11.214,0),(12.924,0),
(12.924,-4.569),(11.214,-4.569)])
H_angled_path=Polygon([
(21.787,13.192),(21.787,10.527),
(17,4.826),(14.767,4.826)])
enter_k_3=Polygon([
(26.45,52.84),(29.179,52.84),
(29.179,53.84),(26.45,53.84)])
enter_4_6=Polygon([
(60.406,51.22),(60.406,52.22),
(63.49,52.22),(63.49,51.22)])
enter_7_12=Polygon([
(39.631, 4.267),(39.939,3.315),
(45.099,4.983),(44.792,5.935)])
exit_polygon=Polygon([
(0,1.499),(0,3.327),
(-1,3.327),(-1,1.499)])
plot_polygon(
A_crosswalk,color="black",add_points=False)
plot_polygon(
B_queue,color="black",add_points=False)
plot_polygon(
C_road_adj_path, color="blue",add_points=False)
plot_polygon(
D_path_k_3, color="blue",add_points=False)
plot_polygon(
E_path_4_6, color="blue",add_points=False)
plot_polygon(
F_path_7_12, color="blue",add_points=False)
plot_polygon(
G_extended_queue, color="black",add_points=False)
plot_polygon(
H_angled_path, color="black",add_points=False)
plot_polygon(
enter_k_3, color="darkgreen",add_points=False)
plot_polygon(
enter_4_6, color="darkgreen",add_points=False)
plot_polygon(
enter_7_12, color="darkgreen",add_points=False)
plot_polygon(
exit_polygon, color="orangered",add_points=False)
geometry = (A_crosswalk.union(
B_queue).union(
C_road_adj_path).union(
D_path_k_3).union(
E_path_4_6).union(
F_path_7_12).union(
G_extended_queue).union(
H_angled_path))
current_time = datetime.now().strftime('%Y-%m-%d_%H-%M-%S')
trajectory_file = f"SRS_evac_{current_time}.sqlite"
simulation = jps.Simulation(
model=jps.AnticipationVelocityModel(),
geometry=geometry,
trajectory_writer=jps.SqliteTrajectoryWriter(
output_file=pathlib.Path(trajectory_file)))
exit_id = simulation.add_exit_stage(exit_polygon)
journey = jps.JourneyDescription([exit_id])
journey_id = simulation.add_journey(journey)
grade_pop = {}
door = {}
door_polygon = {
"K-3":enter_k_3,
"4-6":enter_4_6,
"7-12":enter_7_12,
}
platoon_agents = {}
for i, grade in enumerate(df["Grade Level"]):
grade_sample=rng.normal(
loc=df["Pop Mean"][i],
scale=df["Pop Std Dev"][i],size=1)
grade_pop[grade] = int(np.ceil(grade_sample[0]))
x = grade_pop[grade]
if i < 4:
door[grade] = "K-3"
elif i <7:
door[grade] = "4-6"
else:
door[grade] = "7-12"
platoon_a_size = int(x/2)
platoon_b_size = x - platoon_a_size
platoon_a_id = (2*(i+1))-1
platoon_b_id = (2*(i+1))
platoon_agents[platoon_a_id] ={
"Grade Level": grade,
"Platoon Size": platoon_a_size,
"Entry Door":door_polygon[door[grade]]
}
platoon_agents[platoon_b_id] ={
"Grade Level": grade,
"Platoon Size": platoon_b_size,
"Entry Door":door_polygon[door[grade]]
}
#================================================
interval = 50
try:
max_iterations = int(input("Number of steps?"))
if max_iterations<=0:
raise ValueError
except ValueError:
print("Please enter a positive integer")
return
agent_set = []
next_planned_id = 0
for platoon_id, platoon_data in platoon_agents.items():
spawn_time=float(
rng.uniform(5,15)+rng.uniform(0,120))
spawn_time=min(spawn_time,120)
remaining=int(platoon_data["Platoon Size"])
attempts = 0
max_attempts = 10
time_delay = 1.0
batch_size=max(1,min(10,remaining))
while remaining>0 and attempts<max_attempts:
n_try = min(batch_size,remaining)
try:
positions = jps.distribute_by_number(
polygon=platoon_data["Entry Door"],
number_of_agents=n_try,
distance_to_agents=0.45,
distance_to_polygon=0.3,
max_iterations=1500)
placed_count = len(positions)
if placed_count ==0:
attempts +=1
spawn_time+=time_delay
batch_size=max(1,batch_size//2)
continue
offset=0.1
for k, pos in enumerate(positions):
speed=float(rng.normal(
loc=df["Speed Mean"][i],
scale=df["Speed Std Dev"][i],
size=1)[0])
agent = {
"Grade Level":
platoon_data["Grade Level"],
"Entry Point":
(door[platoon_data["Grade Level"]]),
"Platoon":platoon_id,
"Position":(
float(pos[0]),float(pos[1])),
"Speed":speed,
"Spawn Time":float(
spawn_time+(k*offset))}
next_planned_id +=1
agent['PlannedID'] = next_planned_id
agent_set.append(agent)
remaining-=placed_count
if remaining>0:
attempts+=1
spawn_time+=time_delay
attempts=0
batch_size=max(1,min(10,remaining))
except Exception as e:
print(
f"Error placing platoon {platoon_id}: {e}")
print("Reducing batch and retrying")
attempts+=1
batch_size=max(1,batch_size//2)
spawn_time+=time_delay
total_agents = next_planned_id
#================================================
pending=sorted(agent_set,key=lambda a:a["Spawn Time"])
pending=deque(pending)
sim_to_planned = {}
max_agents_per_step=25
spawned_total = 0
spawn_tolerance=1e-8
while ((simulation.agent_count()>0 or len(pending)>0)
and simulation.iteration_count()<max_iterations):
current_time=simulation.elapsed_time()
agents_this_step=0
while (pending and (pending[0]["Spawn Time"]<=(
current_time+spawn_tolerance))
and agents_this_step<max_agents_per_step):
a = pending.popleft()
pos=tuple(a["Position"])
v0 = float(a["Speed"])
v0 = float(np.clip(v0,0.2,2.5))
agent_params = (
jps.AnticipationVelocityModelAgentParameters(
journey_id=journey_id,
stage_id=exit_id,
position=pos,
radius=0.25,
desired_speed=v0,
anticipation_time=0.5,
reaction_time=0.3,
wall_buffer_distance=0.08))
try:
add_result = simulation.add_agent(agent_params)
internal_id = None
if add_result is not None:
try:
internal_id = int(add_result)
except Exception:
internal_id = None
if internal_id is not None:
sim_to_planned[internal_id] = a
print(f"Added planned={a['PlannedID']}")
print(f"internal={internal_id}")
print(f"time={current_time:.2f}")
spawned_total +=1
else:
print(f"Added planned={a['PlannedID']}")
print("(no internal_id returned)")
print(f"time={current_time:.2f}")
agents_this_step+=1
break
except Exception as e:
pending_for_door = sum(
1 for x in pending if (
x['Entry Point']==a["Entry Point"]))
print("\nadd_agent FAILED")
print(f"planned={a['PlannedID']}")
print(f"pos={pos}")
print(f"speed={v0:.2f}")
print(f"time={current_time:.2f}")
print(f"pending_for_door={pending_for_door}")
print(f"\n\nerror={e}")
retry_delay = 0.1
a["Spawn Time"] = current_time+retry_delay
pending.appendleft(a)
break
simulation.iterate()
iter_count = simulation.iteration_count()
if iter_count % interval ==0:
print(f"\nIteration: {iter_count}")
print(f"time={simulation.elapsed_time():.2f}")
print(f"active={simulation.agent_count()}")
print(f"pending={len(pending)}")
print(f"spawned={spawned_total}/{total_agents}")
print("Simulation Completed!")
print(f"Iterations: {simulation.iteration_count()}")
print(f"Elapsed Time: {simulation.elapsed_time()}")
print(f"Total Agents: {total_agents}")
print(f"{trajectory_file = }")
if __name__ == "__main__":
main()

View File

@@ -0,0 +1,323 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Sun Nov 30 18:48:13 2025
@author: ethan
"""
import numpy as np
import pandas as pd
import pathlib
import jupedsim as jps
from shapely import Polygon
from shapely.plotting import plot_polygon
from collections import deque
from datetime import datetime
from jupedsim.internal.notebook_utils import animate, read_sqlite_file
import pedpy
def main():
grades = {
"Kindergarden":np.array([31.43,5.65,1.21,0.24]),
"Grade 1":np.array([32.57,6.27,1.35,0.26]),
"Grade 2":np.array([34.43,6.80,1.42,0.28]),
"Grade 3":np.array([35.43,5.19,1.48,0.23]),
"Grade 4":np.array([34.86,6.77,1.58,0.26]),
"Grade 5":np.array([36.71,7.09,1.59,0.24]),
"Grade 6":np.array([37.71,6.99,1.65,0.24]),
"Grade 7":np.array([40.43,6.02,1.61,0.25]),
"Grade 8":np.array([40.43,5.50,1.66,0.24]),
"Grade 9":np.array([44.14,4.85,1.60,0.24]),
"Grade 10":np.array([46.29,6.29,1.57,0.23]),
"Grade 11":np.array([48.29,3.30,1.51,0.22]),
"Grade 12":np.array([43.71,6.02,1.54,0.23])
}
df=pd.DataFrame({
"Grade Level":(
list(grades.keys())),
"Pop Mean":[
grades[j][0] for j in grades],
"Pop Std Dev":[
grades[j][1] for j in grades],
"Speed Mean":[
grades[j][2] for j in grades],
"Speed Std Dev":[
grades[j][3] for j in grades]})
rng = np.random.default_rng(seed=42)
A_crosswalk=Polygon([
(-1,1.499),(-1,3.327),
(0,3.327),(11.214, 3.327),
(11.214,1.499),(0,1.499)])
B_queue=Polygon([
(11.214, 0),(22.163, 0),
(22.163, 4.826),(11.214, 4.826)])
C_road_adj_path=Polygon([
(21.787,4.826),(24.214,4.826),
(24.214,40.431),(29.179,40.431),
(29.179,42.511),(24.214,42.511),
(21.787,42.511)])
D_path_k_3=Polygon([
(26.45,42.511),(26.45,52.84),
(26.45,53.84),(29.179,53.84),
(29.179,52.84),(29.179,42.511)])
E_path_4_6=Polygon([
(29.179,42.511),(54.351,42.511),
(60.406,48.842),(60.406,51.22),
(60.406,52.22),(63.49,52.22),
(63.49,51.22),(63.49,47.866),
(56.381,40.431),(29.179,40.431)])
F_path_7_12=Polygon([
(22.163, 0),(39.227, 5.516),
(39.631, 4.267),(39.939,3.315),
(45.099,4.983),(44.792,5.935),
(43.169,10.954),(24.214,4.826),
(22.163,4.826)])
G_extended_queue=Polygon([
(11.214,0),(12.924,0),
(12.924,-4.569),(11.214,-4.569)])
H_angled_path=Polygon([
(21.787,13.192),(21.787,10.527),
(17,4.826),(14.767,4.826)])
enter_k_3=Polygon([
(26.45,52.84),(29.179,52.84),
(29.179,53.84),(26.45,53.84)])
enter_4_6=Polygon([
(60.406,51.22),(60.406,52.22),
(63.49,52.22),(63.49,51.22)])
enter_7_12=Polygon([
(39.631, 4.267),(39.939,3.315),
(45.099,4.983),(44.792,5.935)])
exit_polygon=Polygon([
(0,1.499),(0,3.327),
(-1,3.327),(-1,1.499)])
plot_polygon(
A_crosswalk,color="black",add_points=False)
plot_polygon(
B_queue,color="black",add_points=False)
plot_polygon(
C_road_adj_path, color="blue",add_points=False)
plot_polygon(
D_path_k_3, color="blue",add_points=False)
plot_polygon(
E_path_4_6, color="blue",add_points=False)
plot_polygon(
F_path_7_12, color="blue",add_points=False)
plot_polygon(
G_extended_queue, color="black",add_points=False)
plot_polygon(
H_angled_path, color="black",add_points=False)
plot_polygon(
enter_k_3, color="darkgreen",add_points=False)
plot_polygon(
enter_4_6, color="darkgreen",add_points=False)
plot_polygon(
enter_7_12, color="darkgreen",add_points=False)
plot_polygon(
exit_polygon, color="orangered",add_points=False)
geometry = (A_crosswalk.union(
B_queue).union(
C_road_adj_path).union(
D_path_k_3).union(
E_path_4_6).union(
F_path_7_12).union(
G_extended_queue).union(
H_angled_path))
current_time = datetime.now().strftime('%Y-%m-%d_%H-%M-%S')
trajectory_file = f"SRS_evac_{current_time}.sqlite"
simulation = jps.Simulation(
model=jps.AnticipationVelocityModel(),
geometry=geometry,
trajectory_writer=jps.SqliteTrajectoryWriter(
output_file=pathlib.Path(trajectory_file)))
exit_id = simulation.add_exit_stage(exit_polygon)
journey = jps.JourneyDescription([exit_id])
journey_id = simulation.add_journey(journey)
grade_pop = {}
door = {}
door_polygon = {
"K-3":enter_k_3,
"4-6":enter_4_6,
"7-12":enter_7_12,
}
platoon_agents = {}
for i, grade in enumerate(df["Grade Level"]):
grade_sample=rng.normal(
loc=df["Pop Mean"][i],
scale=df["Pop Std Dev"][i],size=1)
grade_pop[grade] = int(np.ceil(grade_sample[0]))
x = grade_pop[grade]
if i < 4:
door[grade] = "K-3"
elif i <7:
door[grade] = "4-6"
else:
door[grade] = "7-12"
platoon_a_size = int(x/2)
platoon_b_size = x - platoon_a_size
platoon_a_id = (2*(i+1))-1
platoon_b_id = (2*(i+1))
platoon_agents[platoon_a_id] ={
"Grade Level": grade,
"Platoon Size": platoon_a_size,
"Entry Door":door_polygon[door[grade]]
}
platoon_agents[platoon_b_id] ={
"Grade Level": grade,
"Platoon Size": platoon_b_size,
"Entry Door":door_polygon[door[grade]]
}
#================================================
interval = 50
try:
max_iterations = int(input("Number of steps?"))
if max_iterations<=0:
raise ValueError
except ValueError:
print("Please enter a positive integer")
return
agent_set = []
next_planned_id = 0
for platoon_id, platoon_data in platoon_agents.items():
spawn_time=float(
rng.uniform(5,15)+rng.uniform(0,120))
spawn_time=min(spawn_time,120)
remaining=int(platoon_data["Platoon Size"])
attempts = 0
max_attempts = 10
time_delay = 1.0
batch_size=max(1,min(10,remaining))
while remaining>0 and attempts<max_attempts:
n_try = min(batch_size,remaining)
try:
positions = jps.distribute_by_number(
polygon=platoon_data["Entry Door"],
number_of_agents=n_try,
distance_to_agents=0.45,
distance_to_polygon=0.3,
max_iterations=1500)
placed_count = len(positions)
if placed_count ==0:
attempts +=1
spawn_time+=time_delay
batch_size=max(1,batch_size//2)
continue
offset=0.1
for k, pos in enumerate(positions):
speed=float(rng.normal(
loc=df["Speed Mean"][i],
scale=df["Speed Std Dev"][i],
size=1)[0])
agent = {
"Grade Level":
platoon_data["Grade Level"],
"Entry Point":
(door[platoon_data["Grade Level"]]),
"Platoon":platoon_id,
"Position":(
float(pos[0]),float(pos[1])),
"Speed":speed,
"Spawn Time":float(
spawn_time+(k*offset))}
next_planned_id +=1
agent['PlannedID'] = next_planned_id
agent_set.append(agent)
remaining-=placed_count
if remaining>0:
attempts+=1
spawn_time+=time_delay
attempts=0
batch_size=max(1,min(10,remaining))
except Exception as e:
print(
f"Error placing platoon {platoon_id}: {e}")
print("Reducing batch and retrying")
attempts+=1
batch_size=max(1,batch_size//2)
spawn_time+=time_delay
total_agents = next_planned_id
#================================================
pending=sorted(agent_set,key=lambda a:a["Spawn Time"])
pending=deque(pending)
sim_to_planned = {}
max_agents_per_step=25
spawned_total = 0
spawn_tolerance=1e-8
while ((simulation.agent_count()>0 or len(pending)>0)
and simulation.iteration_count()<max_iterations):
current_time=simulation.elapsed_time()
agents_this_step=0
while (pending and (pending[0]["Spawn Time"]<=(
current_time+spawn_tolerance))
and agents_this_step<max_agents_per_step):
a = pending.popleft()
pos=tuple(a["Position"])
v0 = float(a["Speed"])
v0 = float(np.clip(v0,0.2,2.5))
agent_params = (
jps.AnticipationVelocityModelAgentParameters(
journey_id=journey_id,
stage_id=exit_id,
position=pos,
radius=0.25,
desired_speed=v0,
anticipation_time=0.5,
reaction_time=0.3,
wall_buffer_distance=0.08))
try:
add_result = simulation.add_agent(agent_params)
internal_id = None
if add_result is not None:
try:
internal_id = int(add_result)
except Exception:
internal_id = None
if internal_id is not None:
sim_to_planned[internal_id] = a
print(f"Added planned={a['PlannedID']}")
print(f"internal={internal_id}")
print(f"time={current_time:.2f}")
spawned_total +=1
else:
print(f"Added planned={a['PlannedID']}")
print("(no internal_id returned)")
print(f"time={current_time:.2f}")
agents_this_step+=1
break
except Exception as e:
pending_for_door = sum(
1 for x in pending if (
x['Entry Point']==a["Entry Point"]))
print("\nadd_agent FAILED")
print(f"planned={a['PlannedID']}")
print(f"pos={pos}")
print(f"speed={v0:.2f}")
print(f"time={current_time:.2f}")
print(f"pending_for_door={pending_for_door}")
print(f"\n\nerror={e}")
retry_delay = 0.1
a["Spawn Time"] = current_time+retry_delay
pending.appendleft(a)
break
simulation.iterate()
iter_count = simulation.iteration_count()
if iter_count % interval ==0:
print(f"\nIteration: {iter_count}")
print(f"time={simulation.elapsed_time():.2f}")
print(f"active={simulation.agent_count()}")
print(f"pending={len(pending)}")
print(f"spawned={spawned_total}/{total_agents}")
print("Simulation Completed!")
print(f"Iterations: {simulation.iteration_count()}")
print(f"Elapsed Time: {simulation.elapsed_time()}")
print(f"Total Agents: {total_agents}")
print(f"{trajectory_file = }")
trajectory_data,walkable_area = read_sqlite_file(trajectory_file)
speed=pedpy.compute_individual_speed(traj_data=trajectory_data,frame_step=10)
speed=speed.merge(trajectory_data.data,on=["id","frame"],how="left")
animate(trajectory_data,walkable_area,title_note="Evac",every_nth_frame=5)
if __name__ == "__main__":
main()

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

BIN
archive/geo-setup.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

96
archive/geometry.py Normal file
View File

@@ -0,0 +1,96 @@
import sys
import matplotlib
matplotlib.use('QtAgg')
import matplotlib.pyplot as plt
from shapely import Polygon
from shapely.plotting import plot_polygon
def current():
queue = Polygon([
(18.767,9.395),
(15.214,9.395),
(15.214,4.569),
(15.214,0),
(16.924,0),
(16.924,4.569),
(25.0,4.569),
(25.0,16.823),
#(26.163,4.569),
#(28.214,5.07),
#(28.214,17.761),
#(25.787,17.761),
])
crosswalk = Polygon([
(4,6.068),
(4,7.896),
(4,7.896),
(15.214,7.896),
(15.214,6.068),
(4,6.068)
])
grass = Polygon([
(4,0),
(0,0),
(0,17.761),
(4,17.761)
])
plot_polygon(queue,color="blue",add_points=False)
plot_polygon(crosswalk,color="red",add_points=False)
plot_polygon(grass,color="blue",add_points=False)
def new():
new_queue = Polygon([
(19.531,10.306),
(13.88,10.306),
(12.98,9.896),
(12.98,4.569),
(12.98,0),
(16.924,0),
(16.924,4.569),
(25.0,4.569),
(25.0,16.823),
#(26.163,4.569),
#(28.214,5.07),
#(28.214,17.761),
#(25.787,17.761),
])
new_crosswalk = Polygon([
(6.23,4.982),
(6.23,8.982),
(12.98,8.982),
(12.98,4.982),
])
new_grass = Polygon([
(6.23,0),
(0,0),
(0,17.761),
(4,17.761),
(4,10.306),
(5.33,10.306),
(6.23,9.896)
])
plot_polygon(new_queue,color="blue",add_points=False)
plot_polygon(new_crosswalk,color="red",add_points=False)
plot_polygon(new_grass,color="blue",add_points=False)
return
def spawn():
spawn_area = Polygon([
(25.0,16.823),
(25.0,4.569),
(26.163,4.569),
(28.214,5.07),
(28.214,17.761),
(25.787,17.761),
])
plot_polygon(spawn_area,color="green",add_points=False)
return spawn_area
if __name__ == "__main__":
from PyQt6 import QtWidgets
app = QtWidgets.QApplication(sys.argv)
new()
current()
spawn()
plt.show(block=False)
sys.exit(app.exec())

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,557 @@
I'm creating a simulation using JuPedSim to see how pedestrians interact with a crosswalk system. The crosswalk system will have a user-defined "active" interval, and will be activated by agents being within a predefined distance. All agents will spawn in a specified spawn area, and are attempting to get from one side of the road to the other. They may only use the crosswalk to cross, and can only cross when the crosswalk is "active". Below are provided 4 separate files to create the JuPedSim simulation model:
============================
(1.1)
============================
=>geometry_setup.py(file):
class GeometrySetup(current_setup:bool)
spawn:defines the Polygon for the spawn_area
queue:defines the Polygon for the queue_area
trigger:defines the Polygon for the trigger_area
crosswalk:defines the Polygon for the crosswalk_area
grass:defines the Polygon for the grass_area
============================
(1.2)
============================
=>spawning.py(file):
class SpawnManager(spawn_area:Polygon,min_spacing:float):
__init__:
spawn_area: area for valid spawns
min_spacing: minimum spacing between spawn points
spawn_coords: all coordinates for spawning agents
filled_coords: spawn points currently occupied by agents
spawned_agents: set of agent ids already spawned
agent_pos: dictionary which connects agent ids to a spawn point
generate_coords:
uses Poisson disk method with CDTree from scipy.spatial for all spawn points
get_coords:
provides a tuple[float,float] or None based on if there is room to spawn agents
spawn_agent:
provides AgentSetup object or None depending on if there is room to spawn and there are agents left to spawn
unfill_coords:
returns None, used to indicate a spawn point has been made available by an agent moving
get_agent_pos;
returns (0,0) or the spawn coordinates of agents if they are in agent_pos
check_spawn_complete:
checks whether the number of agents spawned is the same as all_agents list, and if all spawn points are empty.
============================
(1.3)
============================
=>agents(folder):
|>agent_data.py(file):
| dictionary with group keys and values
|>agent_setup.py(file):
defines the AgentSetup class and computes list of all_agents:
"""
@dataclass
class AgentSetup:
id:int
grade:str
speed:float
radius:float
"""
============================
(1.4)
============================
=>crosswalk_setup.py(file):
class CrosswalkStatus(Enum):
INACTIVE
ACTIVE
COOLDOWN
@dataclass(frozen=True)
class CrosswalkConfig:
trigger_dist:float
activation_time:float
cooldown_time:float
min_agents_activation:int
activation_delay:float
crosswalk_area:Polygon
trigger_area:Polygon
@dataclass
class CrosswalkState:
status:CrosswalkStatus
is_active:bool
time_active:float
time_remaining:float
agents_in_trigger:int
agents_waiting:int
activation_count:int
class CrosswalkController:
__init__(self,config:CrosswalkConfig,current_setup:bool):
update: calls the following methods, then returns get_state()
update_agents_in_trigger
update_state
update_statistics
update_agents_in_trigger: updates what agents are in the trigger_area for the crosswalk
@staticmethod
points_in_area:finding what points lie in a given area
update_state:updates crosswalk state to be COOLDOWN,ACTIVE or INACTIVE based on specified parameters
activate:changes the system to the ACTIVE state
deactivate:changes the system to the COOLDOWN state
update_statistics:updates info on crosswalks if status is ACTIVE
get_state: provides the state based on the current time in relation to the activation time, then updates the system
can_agent_cross: checks whether ACTIVE or INACTIVE to either let agent pass or add agent to waiting group
@property
is_active: provides a boolean response whether the system is ACTIVE or not
get_statistics:provides stats from the system at the current time
============
here is the code:
============================
(2.1) (geometry_setup.py)
============================
import sys
import matplotlib
matplotlib.use('QtAgg')
import matplotlib.pyplot as plt
from shapely import Polygon
from shapely.plotting import plot_polygon
class GeometrySetup:
def __init__(self,current_setup:bool):
self.current_setup = current_setup
self.spawn()
self.queue()
self.trigger()
self.crosswalk()
self.grass()
def spawn(self)->Polygon:
spawn_area = Polygon([
(25.0,16.823),
(25.0,4.569),
(26.163,4.569),
(28.214,5.07),
(28.214,17.761),
(25.787,17.761),
])
plot_polygon(spawn_area,color="purple",add_points=False)
return spawn_area
def queue(self)->Polygon:
if self.current_setup is True:
queue_area = Polygon([
(18.767,9.395),
(16.924,9.395),
(16.924,4.569),
(25.0,4.569),
(25.0,16.823)
])
else:
queue_area = Polygon([
(19.531,10.306),
(15.214,10.306),
(15.214,0),
(16.924,0),
(16.924,4.569),
(25.0,4.569),
(25.0,16.823)
])
plot_polygon(queue_area,color="blue",add_points=False)
return queue_area
def trigger(self)->Polygon:
if self.current_setup is True:
trigger_area = Polygon([
(15.214,0),
(15.214,9.395),
(16.924,9.395),
(16.924,0)
])
else:
trigger_area = Polygon([
(15.214,10.306),
(15.214,0),
(12.98,0),
(12.98,9.896),
(13.88,10.306)
])
plot_polygon(trigger_area,color="green",add_points=False)
return trigger_area
def crosswalk(self)->Polygon:
if self.current_setup is True:
crosswalk_area = Polygon([
(4,6.068),
(4,7.896),
(4,7.896),
(15.214,7.896),
(15.214,6.068),
(4,6.068)
])
else:
crosswalk_area = Polygon([
(6.23,4.982),
(6.23,8.982),
(12.98,8.982),
(12.98,4.982)
])
plot_polygon(crosswalk_area,color="red",add_points=False)
return crosswalk_area
def grass(self)->Polygon:
if self.current_setup is True:
grass_area = Polygon([
(4,0),
(0,0),
(0,17.761),
(4,17.761)
])
else:
grass_area = Polygon([
(6.23,0),
(0,0),
(0,17.761),
(4,17.761),
(4,10.306),
(5.33,10.306),
(6.23,9.896)
])
plot_polygon(grass_area,color="blue",add_points=False)
return grass_area
if __name__ == "__main__":
from PyQt6 import QtWidgets
app = QtWidgets.QApplication(sys.argv)
geo_map = GeometrySetup(False)
plt.show(block=False)
sys.exit(app.exec())
============================
(2.2) (spawning.py)
============================
from shapely.geometry import Polygon,Point
from typing import Tuple, List, Dict, Set
from scipy.spatial import cKDTree
import sys
import math
import numpy as np
import config
sys.path.insert(0,str(config.AGENTS_DIR))
from agents.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 = set()
self.agent_pos:dict={}
self.rng = np.random.default_rng()
def generate_coords(self,max_samples:int=1000)->None:
min_x,min_y,max_x,max_y = self.spawn_area.bounds
points = []
while len(points) ==0:
x = self.rng.uniform(min_x,max_x)
y = self.rng.uniform(min_y,max_y)
if self.spawn_area.contains(Point(x,y)):
points.append([x,y])
for _ in range(max_samples):
idx = self.rng.integers(0,len(points),endpoint=True)
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 self.spawn_area.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)
def get_coords(self)->Tuple[float,float]|None:
'''
points = [
pt for pt in self.generate_coords if pt \
not in self.filled_coords.values()
]
return self.rng.choice(points) if points else 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)
============================
(2.3) (agent_setup.py)
============================
from agent_data import grade_data
from dataclasses import dataclass
import numpy as np
from typing import List
@dataclass
class AgentSetup:
id:int
grade:str
speed:float
radius:float
def AgentConfig()->List[List[AgentSetup],List[int]]:
agent_id = 1
pop_list = []
all_agents = []
for idx,key in enumerate(grade_data.keys()):
grade = grade_data[key]
pop_list.append(grade["Pop Current"])
for i in range(pop_list[idx]):
rng = np.random.default_rng()
agent = AgentSetup(
id=agent_id,
grade=key,
speed=rng.normal(
grade["Speed Mean"],
grade["Speed Std Dev"]
),
radius=grade["Radius"]
)
all_agents.append(agent)
agent_id +=1
return all_agents, pop_list
============================
(2.4) (crosswalk_setup.py)
============================
from dataclasses import dataclass,field
from enum import Enum
from typing import List,Dict,Tuple,Set
from shapely import Polygon,Point
from geometry_setup import GeometrySetup
import shapely
import numpy as np
class CrosswalkStatus(Enum):
INACTIVE = "inactive"
ACTIVE = "active"
COOLDOWN = "cooldown"
@dataclass(frozen=True)
class CrosswalkConfig:
trigger_dist:float
activation_time:float
cooldown_time:float
min_agents_activation:int
activation_delay:float
crosswalk_area:Polygon
trigger_area:Polygon
@dataclass
class CrosswalkState:
status:CrosswalkStatus
is_active:bool
time_active:float
time_remaining:float
agents_in_trigger:int
agents_waiting:int
activation_count:int
class CrosswalkController:
def __init__(self,config:CrosswalkConfig,current_setup:bool):
self.config = config
self.crosswalk_area = config.crosswalk_area
self.trigger_area = config.trigger_area
self.status = CrosswalkStatus.INACTIVE
self.state_start_time = 0.0
self.current_time = 0.0
self.activation_count = 0
self.agents_in_trigger: Set[int] = set()
self.agents_waiting: Set[int] = set()
self.total_active_time = 0.0
self.agents_served = 0
def update(
self,
agent_pos:Dict[int,Tuple[float,float]],
time_step:float,
current_time:float=None
)->CrosswalkState:
if current_time is not None:
self.current_time = current_time
else:
self.current_time +=time_step
self.update_agents_in_trigger(agent_pos)
self.update_state(time_step)
self.update_statistics(time_step)
return self.get_state()
def update_agents_in_trigger(
self,
agent_pos:Dict[int,Tuple[float,float]]
)->None:
self.agents_in_trigger.clear()
if not agent_pos:
return
agent_ids = list(agent_pos.keys())
positions = np.array(list(agent_pos.values()),dtype=np.float32)
in_trigger = self.points_in_area(positions,self.trigger_area)
for i, agent_id in enumerate(agent_ids):
if in_trigger[i]:
self.agents_in_trigger.add(agent_id)
@staticmethod
def points_in_area(points:np.ndarray,polygon:Polygon)->np.ndarray:
if len(points) ==0:
return np.array([],dtype=bool)
x,y = points[:,0],points[:,1]
vertices = shapely.get_coordinates(polygon)
n = len(vertices)
inside = np.zeros(len(points),dtype=bool)
j = n-1
for i in range(n):
xi, yi = vertices[i]
xj, yj = vertices[j]
mask = ((yi>y) != (yj>y)) & (x<(xj-xi)*(y-yi)/(yj-yi)+xi)
inside ^= mask
j = i
return inside
def update_state(self,time_step:float)->None:
elapsed = self.current_time - self.state_start_time
if self.status == CrosswalkStatus.ACTIVE:
if elapsed >= self.config.activation_time:
self.deactivate()
elif self.status == CrosswalkStatus.COOLDOWN:
if elapsed >= self.config.cooldown_time:
self.status = CrosswalkStatus.INACTIVE
self.state_start_time = self.current_time
elif self.status ==CrosswalkStatus.INACTIVE:
if (len(self.agents_in_trigger)>=self.config.min_agents_activation and \
elapsed >= self.config.activation_delay):
self.activate()
def activate(self)->None:
self.status = CrosswalkStatus.ACTIVE
self.state_start_time = self.current_time
self.activation_count +=1
self.agents_served += len(self.agents_waiting)
self.agents_waiting.clear()
def deactivate(self)->None:
self.status = CrosswalkStatus.COOLDOWN
self.state_start_time = self.current_time
def update_statistics(self,time_step:float)->None:
if self.status == CrosswalkStatus.ACTIVE:
self.total_active_time += time_step
def get_state(self)->CrosswalkState:
elapsed = self.current_time-self.state_start_time
if self.status == CrosswalkStatus.ACTIVE:
time_remaining = max(0.0,self.config.activation_time-elapsed)
elif self.status == CrosswalkStatus.COOLDOWN:
time_remaining = max(0.0,self.config.cooldown_duration-elapsed)
else:
time_remaining = 0.0
return CrosswalkState(
status=self.status,
is_active=(self.status == CrosswalkStatus.ACTIVE),
time_active=elapsed if self.status == CrosswalkStatus.ACTIVE else 0.0,
time_remaining=time_remaining,
agents_in_trigger=len(self.agents_in_trigger),
agents_waiting=len(self.agents_waiting),
activation_count = self.activation_count
)
def can_agent_cross(self,agent_id:int)->bool:
if self.status == CrosswalkStatus.ACTIVE:
return True
else:
if agent_id in self.agents_in_trigger:
self.agents_waiting.add(agent_id)
return False
@property
def is_active(self)->bool:
return self.status == CrosswalkStatus.ACTIVE
def get_statistics(self)->Dict[str,float]:
return {
"total_activations":self.activation_count,
"total_active_time":self.total_active_time,
"agents_served":self.agents_served,
"current_agents_in_trigger":len(self.agents_in_trigger),
"current_agents_waiting":len(self.agents_waiting)
}
=========================
The last part which needs to be done is using the JuPedSim module to create a full simulation using all these pieces;

View File

@@ -0,0 +1,690 @@
The text file includes all the files which makeup my simulation configuration. could you please run through them and fix any potential errors, delete any unused or unnecessary variables?
===================
(geometry_setup.py)
===================
import sys
import matplotlib
matplotlib.use('QtAgg')
import matplotlib.pyplot as plt
from matplotlib.figure import Figure
from matplotlib.axes import Axes
from shapely import Polygon
from shapely.plotting import plot_polygon
from typing import Tuple
class GeometrySetup:
def __init__(self,current_setup:bool):
self.current_setup = current_setup
self.spawn_area = self.spawn()
self.queue_area = self.queue()
self.grass_area = self.grass()
self.crosswalk_area = self.crosswalk()
self.entry_area = self.entry_polygon()
self.exit_area = self.exit_polygon()
def spawn(self)->Polygon:
spawn_area = Polygon([
(25.0,16.823),
(25.0,4.569),
(26.163,4.569),
(28.214,5.07),
(28.214,17.761),
(25.787,17.761),
])
return spawn_area
def queue(self)->Polygon:
if self.current_setup is True:
queue_area = Polygon([
(18.767,9.395),
(16.924,9.395),
(15.214,9.395), #prev trigger area
(15.214,0), # ||
(16.924,0), # ||
(16.924,4.569),
(25.0,4.569),
(25.0,16.823)
])
else:
queue_area = Polygon([
(19.531,10.306),
(15.214,10.306),
(13.88,10.306), #prev trigger area
(12.98,9.896), # ||
(12.98,0), # ||
(15.214,0), #
(15.214,0),
(16.924,0),
(16.924,4.569),
(25.0,4.569),
(25.0,16.823)
])
return queue_area
def grass(self)->Polygon:
if self.current_setup is True:
grass_area = Polygon([
(4,0),
(0,0),
(0,17.761),
(4,17.761)
])
else:
grass_area = Polygon([
(6.23,0),
(0,0),
(0,17.761),
(4,17.761),
(4,10.306),
(5.33,10.306),
(6.23,9.896)
])
return grass_area
def crosswalk(self)->Polygon:
if self.current_setup is True:
crosswalk_area = Polygon([
(4,6.068),
(4,7.896),
(4,7.896),
(15.214,7.896),
(15.214,6.068),
(4,6.068)
])
else:
crosswalk_area = Polygon([
(6.23,4.982),
(6.23,8.982),
(12.98,8.982),
(12.98,4.982)
])
return crosswalk_area
def entry_polygon(self)->Polygon:
if self.current_setup is True:
entry_area = Polygon([ # x: 2.9m, y: 3.428m
(15.314,5.268), # dx: 0.1m, dy: 0.8m
(15.314,8.696),
(18.214,8.696),
(18.214,5.268)
])
else:
entry_area = Polygon([ # x: 2.9m, y: 5.6m
(15.98,9.782),
(15.98,4.182),
(13.08,4.182),
(13.08,9.782)
])
return entry_area
def exit_polygon(self)->Polygon:
if self.current_setup is True:
exit_area = Polygon([ # x: 2.9m, y: 3.428m
(1,5.268),
(1,8.696),
(3.9,8.696),
(3.9,5.268)
])
else:
exit_area = Polygon([ # x: 2.9m, y: 5.6m
(3.23,4.182),
(3.23,9.782),
(6.13,9.782),
(6.13,4.182)
])
return exit_area
def plot_all(self)->Tuple[Figure,Axes]:
plot_polygon(self.spawn_area,color="green",add_points=False)
plot_polygon(self.queue_area,color="blue",add_points=False)
plot_polygon(self.grass_area,color="blue",add_points=False)
plot_polygon(self.crosswalk_area,color="red",add_points=False)
plot_polygon(self.entry_area,color="black",add_points=False)
plot_polygon(self.exit_area,color="black",add_points=False)
return
if __name__ == "__main__":
from PyQt6 import QtWidgets
app = QtWidgets.QApplication(sys.argv)
GeometrySetup(False).plot_all()
plt.show(block=False)
sys.exit(app.exec())
===================
(agent_setup.py)
===================
from agent_data import grade_data
from dataclasses import dataclass
import numpy as np
from typing import List
@dataclass
class AgentSetup:
id:int
grade:str
speed:float
radius:float
def AgentConfig()->List[List[AgentSetup],List[int]]:
agent_id = 1
pop_list = []
all_agents = []
for idx,key in enumerate(grade_data.keys()):
grade = grade_data[key]
pop_list.append(grade["Pop Current"])
for i in range(pop_list[idx]):
rng = np.random.default_rng()
agent = AgentSetup(
id=agent_id,
grade=key,
speed=rng.normal(
grade["Speed Mean"],
grade["Speed Std Dev"]
),
radius=grade["Radius"]
)
all_agents.append(agent)
agent_id +=1
return all_agents, pop_list
===================
(crosswalk_setup.py)
===================
from dataclasses import dataclass,field
from enum import Enum
from typing import List,Dict,Tuple,Set
from shapely import Polygon,Point
from geometry_setup import GeometrySetup
import shapely
import numpy as np
class CrosswalkStatus(Enum):
INACTIVE = "inactive"
ACTIVE = "active"
COOLDOWN = "cooldown"
@dataclass(frozen=True)
class CrosswalkConfig:
trigger_dist:float
activation_time:float
cooldown_time:float
min_agents_activation:int
activation_delay:float
crosswalk_area:Polygon
trigger_area:Polygon
@dataclass
class CrosswalkState:
status:CrosswalkStatus
is_active:bool
time_active:float
time_remaining:float
agents_in_trigger:int
agents_waiting:int
activation_count:int
class CrosswalkController:
def __init__(self,config:CrosswalkConfig,current_setup:bool):
self.config = config
self.crosswalk_area = config.crosswalk_area
self.trigger_area = config.trigger_area
self.status = CrosswalkStatus.INACTIVE
self.state_start_time = 0.0
self.current_time = 0.0
self.activation_count = 0
self.agents_in_trigger: Set[int] = set()
self.agents_waiting: Set[int] = set()
self.total_active_time = 0.0
self.agents_served = 0
def update(
self,
agent_pos:Dict[int,Tuple[float,float]],
time_step:float,
current_time:float=None
)->CrosswalkState:
if current_time is not None:
self.current_time = current_time
else:
self.current_time +=time_step
self.update_agents_in_trigger(agent_pos)
self.update_state(time_step)
self.update_statistics(time_step)
return self.get_state()
def update_agents_in_trigger(
self,
agent_pos:Dict[int,Tuple[float,float]]
)->None:
self.agents_in_trigger.clear()
if not agent_pos:
return
agent_ids = list(agent_pos.keys())
positions = np.array(list(agent_pos.values()),dtype=np.float32)
in_trigger = self.points_in_area(positions,self.trigger_area)
for i, agent_id in enumerate(agent_ids):
if in_trigger[i]:
self.agents_in_trigger.add(agent_id)
@staticmethod
def points_in_area(points:np.ndarray,polygon:Polygon)->np.ndarray:
if len(points) ==0:
return np.array([],dtype=bool)
x,y = points[:,0],points[:,1]
vertices = shapely.get_coordinates(polygon)
n = len(vertices)
inside = np.zeros(len(points),dtype=bool)
j = n-1
for i in range(n):
xi, yi = vertices[i]
xj, yj = vertices[j]
mask = ((yi>y) != (yj>y)) & (x<(xj-xi)*(y-yi)/(yj-yi)+xi)
inside ^= mask
j = i
return inside
def update_state(self,time_step:float)->None:
elapsed = self.current_time - self.state_start_time
if self.status == CrosswalkStatus.ACTIVE:
if elapsed >= self.config.activation_time:
self.deactivate()
elif self.status == CrosswalkStatus.COOLDOWN:
if elapsed >= self.config.cooldown_time:
self.status = CrosswalkStatus.INACTIVE
self.state_start_time = self.current_time
elif self.status ==CrosswalkStatus.INACTIVE:
if (len(self.agents_in_trigger)>=self.config.min_agents_activation and \
elapsed >= self.config.activation_delay):
self.activate()
def activate(self)->None:
self.status = CrosswalkStatus.ACTIVE
self.state_start_time = self.current_time
self.activation_count +=1
self.agents_served += len(self.agents_waiting)
self.agents_waiting.clear()
def deactivate(self)->None:
self.status = CrosswalkStatus.COOLDOWN
self.state_start_time = self.current_time
def update_statistics(self,time_step:float)->None:
if self.status == CrosswalkStatus.ACTIVE:
self.total_active_time += time_step
def get_state(self)->CrosswalkState:
elapsed = self.current_time-self.state_start_time
if self.status == CrosswalkStatus.ACTIVE:
time_remaining = max(0.0,self.config.activation_time-elapsed)
elif self.status == CrosswalkStatus.COOLDOWN:
time_remaining = max(0.0,self.config.cooldown_duration-elapsed)
else:
time_remaining = 0.0
return CrosswalkState(
status=self.status,
is_active=(self.status == CrosswalkStatus.ACTIVE),
time_active=elapsed if self.status == CrosswalkStatus.ACTIVE else 0.0,
time_remaining=time_remaining,
agents_in_trigger=len(self.agents_in_trigger),
agents_waiting=len(self.agents_waiting),
activation_count = self.activation_count
)
def can_agent_cross(self,agent_id:int)->bool:
if self.status == CrosswalkStatus.ACTIVE:
return True
else:
if agent_id in self.agents_in_trigger:
self.agents_waiting.add(agent_id)
return False
@property
def is_active(self)->bool:
return self.status == CrosswalkStatus.ACTIVE
def get_statistics(self)->Dict[str,float]:
return {
"total_activations":self.activation_count,
"total_active_time":self.total_active_time,
"agents_served":self.agents_served,
"current_agents_in_trigger":len(self.agents_in_trigger),
"current_agents_waiting":len(self.agents_waiting)
}
===================
(spawning.py)
===================
from shapely.geometry import Polygon,Point
from typing import Tuple, List, Dict, Set
from scipy.spatial import cKDTree
import sys
import math
import numpy as np
import config
sys.path.insert(0,str(config.AGENTS_DIR))
from agents.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 = set()
self.agent_pos:dict={}
self.rng = np.random.default_rng()
def generate_coords(self,max_samples:int=1000)->None:
min_x,min_y,max_x,max_y = self.spawn_area.bounds
points = []
while len(points) ==0:
x = self.rng.uniform(min_x,max_x)
y = self.rng.uniform(min_y,max_y)
if self.spawn_area.contains(Point(x,y)):
points.append([x,y])
for _ in range(max_samples):
idx = self.rng.integers(0,len(points),endpoint=True)
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 self.spawn_area.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 points
def get_coords(self)->Tuple[float,float]|None:
'''
points = [
pt for pt in self.generate_coords if pt \
not in self.filled_coords.values()
]
return self.rng.choice(points) if points else 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)
===================
(simulation.py)
===================
from typing import Tuple,List,Dict
import sys
import matplotlib
matplotlib.use('QtAgg')
import matplotlib.pyplot as plt
from shapely import Polygon,Point,GeometryCollection,get_coordinates
from shapely.plotting import plot_polygon
import jupedsim as jps
import numpy as np
import pathlib
from geometry_setup import GeometrySetup
from spawning import SpawnManager
from crosswalk_setup import CrosswalkStatus,CrosswalkController,CrosswalkState,CrosswalkConfig
from agents.agent_setup import AgentConfig
from dataclasses import dataclass
@dataclass
class SimulationSetup:
simulation_time:float=200.0
time_step:float=0.01
max_iterations:int=50000
trajectory_file:str="crosswalk_sim.sqlite"
current_setup:bool=True
trigger_dist:float=1.0
activation_time:float=30.0
cooldown_time:float=10.0
min_agents_activation:int=1
activation_delay:float=2.0
class CrosswalkSimulation:
def __init__(self,config:SimulationSetup):
self.config = config
print("\nInitializing Geometry...")
self.geo = GeometrySetup(self.config.current_setup)
self.spawn_area = self.geo.spawn()
self.queue_area = self.geo.queue()
self.grass_area = self.geo.grass()
self.crosswalk_area = self.geo.crosswalk()
self.entry_area = self.geo.entry_polygon()
self.exit_area = self.geo.exit_polygon()
self.walkable_area = GeometryCollection([
self.spawn_area,
self.queue_area,
self.grass_area,
self.crosswalk_area,
]).unary_union
print("Geometry Configuration Complete!")
print("\nInitializing Spawn Manager...")
self.spawn_manager = SpawnManager(self.spawn_area,min_spacing=0.55)
self.spawn_manager.generate_coords()
print("Spawn Manager Setup Complete!")
print("\nInitializing Crosswalk Controller...")
self.crosswalk_controller = CrosswalkController(
CrosswalkConfig(
trigger_dist=self.config.trigger_dist,
activation_time=self.config.activation_time,
cooldown_time=self.config.cooldown_time,
min_agents_activation=self.config.min_agents_activation,
activation_delay=self.config.activation_delay,
crosswalk_area=self.crosswalk_area,
trigger_area=self.entry_area
),
self.config.current_setup
)
print("Crosswalk Controller Setup Complete!")
print("\nInitializing Agent Setup...")
self.all_agents, pop_list = AgentConfig()
self.total_agents = len(self.all_agents)
print(f"Created {self.total_agents} Agents...")
print("Agent Setup Complete!")
print("\nInitializing JuPedSim Model...")
self.trajectory_file = self.config.trajectory_file
self.simulation = jps.Simulation(
model=jps.CollisionFreeSpeedModel(),
geometry=self.walkable_area,
trajectory_writer=jps.SqliteTrajectoryWriter(
output_file=pathlib.Path(self.trajectory_file)
),
dt=self.config.time_step
)
self.journey_setup()
self.jps_agent_ids = {}
# unsure whether these are necessary
#
#self.agent_targets = {}
#self.active_agents = {}
self.iteration_count = 0
self.current_time = 0.0
print(f"\nJuPedSim Model Initialized with:")
print(f"\tTime Step: {self.config.time_step} s")
print(f"\tMax Iterations: {self.config.max_iterations}")
print(f"\tCrosswalk Activation Length: {self.config.activation_time} s")
print(f"\tCrosswalk Cooldown Length: {self.config.cooldown_time} s")
def journey_setup(self):
self.queue_waiting_coords = self.waiting_coords(
self.queue_area,self.spawn_manager.min_spacing
)
self.queue_stage_id = self.simulation.add_waiting_set_stage(
self.queue_waiting_coords
)
self.queue_stage = self.simulation.get_stage(self.queue_stage_id)
self.queue_stage.state = jps.WaitingSetState.ACTIVE
self.crosswalk_entry_id = self.simulation.add_waypoint_stage(
self.entry_area,
self.spawn_manager.min_spacing
)
self.crosswalk_exit_id = self.simulation.add_waypoint_stage(
self.exit_area,
self.spawn_manager.min_spacing
)
self.journey = jps.JourneyDescription([
self.queue_stage_id,
self.crosswalk_entry_id,
self.crosswalk_exit_id,
])
self.journey.set_transition_for_stage(
self.queue_stage_id,
jps.Transition.create_fixed_transition(
self.crosswalk_entry_id
))
self.journey.set_transition_for_stage(
self.crosswalk_entry_id,
jps.Transition.create_fixed_transition(
self.crosswalk_exit_id
))
self.journey_id = self.simulation.add_journey(
self.journey)
def waiting_coords(self,area:Polygon,spacing:float)->List[Tuple[float,float]]:
min_x,min_y,max_x,max_y = area.bounds
points = []
x = (min_x+spacing)/2
while x<max_x:
y = (min_y+spacing)/2
while y < max_y:
if area.contains(Point(x, y)):
points.append((x,y))
y += spacing
x += spacing
return points if points else [(min_x+0.5, min_y+0.5)]
def run(self,max_iterations:int):
print("\n\nStarting Simulation")
print("Target Agents: {len(self.all_agents)}")
while self.iteration_count < max_iterations:
if self.spawn_manager.check_spawn_complete(self.all_agents):
new_agent = self.spawn_manager.spawn_agent(self.all_agents)
if new_agent:
jps_agent_params = jps.CollisionFreeSpeedModelAgentParameters(
journey_id=self.journey_id,
stage_id=self.queue_stage_id,
position=self.spawn_manager.get_agent_pos(new_agent.id),
v0=new_agent.speed,
radius=new_agent.radius
)
jps_agent_id = self.simulation.add_agent(jps_agent_params)
self.jps_agent_ids[new_agent.id] = jps_agent_id
# unsure whether this is necessary
#
# self.agent_targets[new_agent.id] = None
print(f"Iter {self.iteration_count:05d}: Spawned agent {new_agent.id} (JPS-ID: {jps_agent_id})")
current_agent_pos = {}
for spawn_id,jps_id in self.jps_agent_ids.items():
agent = self.simulation.agent(jps_id)
current_agent_pos[spawn_id] = (agent.position[0],agent.position[1])
crosswalk_state = self.crosswalk_controller.update(
current_agent_pos,self.config.time_step,self.current_time
)
if crosswalk_state.is_active:
if self.queue_stage.stage != jps.WaitingSetState.INACTIVE:
self.queue_stage.stage = jps.WaitingSetState.INACTIVE
print(f"Iter {self.iteration_count:05d}: Crosswalk ACTIVATED, releasing queue.")
else:
if self.queue_stage.stage != jps.WaitingSetState.ACTIVE:
self.queue_stage.stage = jps.WaitingSetState.INACTIVE
print(f"Iter {self.iteration_count:05d}: Crosswalk INACTIVE, holding agents.")
self.simulation.iterate()
self.current_time += self.config.time_step
self.iteration_count +=1
agents_to_remove = []
for spawn_id, jps_id in list(self.jps_agent_ids.items()):
try:
_ = self.simulation.agent(jps_id)
except RuntimeError:
agents_to_remove.append(spawn_id)
self.spawn_manager.unfill_coords(spawn_id)
for spawn_id in agents_to_remove:
del self.jps_agent_ids[spawn_id]
print(f"Iter {self.iteration_count:05d}: Agent {spawn_id} reached exit and was removed.")
if (len(self.jps_agent_ids) ==0 and not self.spawn_manager.check_spawn_complete(self.all_agents)):
print(f"\nSimulation Completed at Iteration: {self.iteration_count}")
print(f"Total Simulation Time: {self.current_time:.2f} seconds.")
break
print("\n" + "="*50)
print("Simulation finished.")
stats = self.crosswalk_controller.get_statistics()
print(f"Crosswalk was activated {stats['total_activations']} times.")
print(f"Agents served by crosswalk: {stats['agents_served']}")
print(f"Trajectory saved to: {self.trajectory_file.absolute()}")

Binary file not shown.

View File

@@ -0,0 +1,17 @@
# -*- coding: utf-8 -*-
"""
E.Drake - ENGN-2220
Thu Jan 22 23:48:50 2026
"""
from pathlib import Path
ROOT = Path(__file__).parent.parent.parent
SOURCE_DIR = ROOT/"source"
ARCHIVE_DIR = ROOT/"archive"
PATH_DIR = ROOT/"path"
AGENTS_DIR = ROOT/SOURCE_DIR/"sim_agents"
GEO_DIR = ROOT/SOURCE_DIR/"sim_geometry"
TEST_DIR = ROOT/SOURCE_DIR/"test"

View File

@@ -0,0 +1,72 @@
import pandas as pd
import numpy as np
from dataclasses import dataclass
grades = {
"Kindergarden":np.array(
[31.43,5.65,1.21,0.24]
),
"Grade 1":np.array(
[32.57,6.27,1.35,0.26]
),
"Grade 2":np.array(
[34.43,6.80,1.42,0.28]
),
"Grade 3":np.array(
[35.43,5.19,1.48,0.23]
),
"Grade 4":np.array(
[34.86,6.77,1.58,0.26]
),
"Grade 5":np.array(
[36.71,7.09,1.59,0.24]
),
"Grade 6":np.array(
[37.71,6.99,1.65,0.24]
),
"Grade 7":np.array(
[40.43,6.02,1.61,0.25]
),
"Grade 8":np.array(
[40.43,5.50,1.66,0.24]
),
"Grade 9":np.array(
[44.14,4.85,1.60,0.24]
),
"Grade 10":np.array(
[46.29,6.29,1.57,0.23]
),
"Grade 11":np.array(
[48.29,3.30,1.51,0.22]
),
"Grade 12":np.array(
[43.71,6.02,1.54,0.23]
)}
df_srs_data=pd.DataFrame({
"Grade Level":(
list(grades.keys())),
"Pop Mean":[
grades[j][0] for j in grades],
"Pop Std Dev":[
grades[j][1] for j in grades],
"Speed Mean":[
grades[j][2] for j in grades],
"Speed Std Dev":[
grades[j][3] for j in grades]})
@dataclass
class AgentConfig:
id:int
grade:str
door:str
speed:float
radius:float

View File

@@ -0,0 +1,91 @@
import numpy as np
from dataclasses import dataclass
from typing import List, Dict, Optional
from collections import defaultdict
##https://censusatschool.ca/data-results/2017-2018/average-height-by-age/
@dataclass
class AgentConfig:
id:int
grade:str
door:int
speed:float
radius:float
def agent_params():
gr_data = {
# [0]: number of students
# [1]: door number
# [2]: speed mean
# [3]: speed standard deviation
# [4]: radius mean
"Kindergarden":np.array(
[34,0,1.21,0.24,0.407]
),
"Grade 1":np.array(
[26,0,1.35,0.26,0.407]
),
"Grade 2":np.array(
[42,0,1.42,0.28,0.407]
),
"Grade 3":np.array(
[39,0,1.48,0.23,0.407]
),
"Grade 4":np.array(
[30,1,1.58,0.26,0.417]
),
"Grade 5":np.array(
[43,1,1.59,0.24,0.434]
),
"Grade 6":np.array(
[29,1,1.65,0.24,0.454]
),
"Grade 7":np.array(
[45,2,1.61,0.25,0.471]
),
"Grade 8":np.array(
[36,2,1.66,0.24,0.488]
),
"Grade 9":np.array(
[44,2,1.60,0.24,0.500]
),
"Grade 10":np.array(
[36,2,1.57,0.23,0.507]
),
"Grade 11":np.array(
[54,2,1.51,0.22,0.515]
),
"Grade 12":np.array(
[46,2,1.54,0.23,0.520]
)}
agent_id = 1
rng = np.random.default_rng(seed=42)
all_agents = []
gr_agents = []
for grade in gr_data:
for num in range(int(gr_data[grade][0])):
door = gr_data[grade][1]
speed = rng.normal(
loc=gr_data[grade][2],
scale=gr_data[grade][3],
size=1)
radius = gr_data[grade][4]
gr_agents.append(
AgentConfig(
id=agent_id,
grade=grade,
door=door,
speed=speed,
radius = radius
))
agent_id += 1
all_agents.append(gr_agents)
gr_agents = []
#for grade in all_agents:
# for agent in grade:
# print(agent)
return all_agents

View File

@@ -0,0 +1,17 @@
# -*- coding: utf-8 -*-
"""
E.Drake - ENGN-2220
Thu Jan 22 23:48:50 2026
"""
from pathlib import Path
ROOT = Path(__file__).parent.parent.parent
SOURCE_DIR = ROOT/"source"
ARCHIVE_DIR = ROOT/"archive"
PATH_DIR = ROOT/"path"
AGENTS_DIR = ROOT/SOURCE_DIR/"sim_agents"
GEO_DIR = ROOT/SOURCE_DIR/"sim_geometry"
TEST_DIR = ROOT/SOURCE_DIR/"test"

View File

@@ -0,0 +1,147 @@
import sys
import matplotlib
matplotlib.use('QtAgg')
import matplotlib.pyplot as plt
from shapely import Polygon
from shapely.plotting import plot_polygon
def geo_current(full_plot:bool=False):
A_crosswalk=Polygon([
(-1,1.499),
(-1,3.327),
(0,3.327),
(11.214, 3.327),
(11.214,1.499),
(0,1.499)
])
B_queue=Polygon([
(11.214, 0),
(22.163, 0),
(22.163, 4.826),
(11.214, 4.826)
])
C_road_adj_path=Polygon([
(21.787,4.826),
(24.214,4.826),
(24.214,40.431),
(29.179,40.431),
(29.179,42.511),
(24.214,42.511),
(21.787,42.511)
])
D_path_k_3=Polygon([
(26.45,42.511),
(26.45,52.84),
(26.45,53.84),
(29.179,53.84),
(29.179,52.84),
(29.179,42.511)
])
E_path_4_6=Polygon([
(29.179,42.511),
(54.351,42.511),
(60.406,48.842),
(60.406,51.22),
(60.406,52.22),
(63.49,52.22),
(63.49,51.22),
(63.49,47.866),
(56.381,40.431),
(29.179,40.431)
])
F_path_7_12=Polygon([
(22.163, 0),
(39.227, 5.516),
(39.631, 4.267),
(39.939,3.315),
(45.099,4.983),
(44.792,5.935),
(43.169,10.954),
(24.214,4.826),
(22.163,4.826)
])
G_extended_queue=Polygon([
(11.214,0),
(12.924,0),
(12.924,-4.569),
(11.214,-4.569)
])
H_angled_path=Polygon([
(21.787,13.192),
(21.787,10.527),
(17,4.826),
(14.767,4.826)
])
enter_k_3=Polygon([
(26.45,52.84),
(29.179,52.84),
(29.179,53.84),
(26.45,53.84)
])
enter_4_6=Polygon([
(60.406,51.22),
(60.406,52.22),
(63.49,52.22),
(63.49,51.22)
])
enter_7_12=Polygon([
(39.631, 4.267),
(39.939,3.315),
(45.099,4.983),
(44.792,5.935)
])
exit_polygon=Polygon([
(0,1.499),
(0,3.327),
(-1,3.327),
(-1,1.499)
])
geometry = (
A_crosswalk.union(
B_queue).union(
C_road_adj_path).union(
D_path_k_3).union(
E_path_4_6).union(
F_path_7_12).union(
G_extended_queue).union(
H_angled_path)
)
doors = [
enter_k_3,
enter_4_6,
enter_7_12,
exit_polygon
]
if full_plot is False:
plot_polygon(A_crosswalk,color="black",add_points=False)
plot_polygon(B_queue,color="black",add_points=False)
plot_polygon(C_road_adj_path, color="blue",add_points=False)
plot_polygon(D_path_k_3, color="blue",add_points=False)
plot_polygon(E_path_4_6, color="blue",add_points=False)
plot_polygon(F_path_7_12, color="blue",add_points=False)
plot_polygon(G_extended_queue, color="black",add_points=False)
plot_polygon(H_angled_path, color="black",add_points=False)
plot_polygon(enter_k_3, color="darkgreen",add_points=False)
plot_polygon(enter_4_6, color="darkgreen",add_points=False)
plot_polygon(enter_7_12, color="darkgreen",add_points=False)
plot_polygon(exit_polygon, color="orangered",add_points=False)
else:
plot_polygon(geometry,color="blue",add_points=False)
plot_polygon(enter_k_3,color="red",add_points=False)
plot_polygon(enter_4_6,color="red",add_points=False)
plot_polygon(enter_7_12,color="red",add_points=False)
return geometry, doors
if __name__ == "__main__":
from PyQt6 import QtWidgets
app = QtWidgets.QApplication(sys.argv)
geometry,doors = geo_current(True)
plt.show(block=False)
sys.exit(app.exec())

View File

@@ -0,0 +1,71 @@
"""
E.Drake - ENGN 2220
Jan 21, 2026
TESTING GEOMETRY
"""
import matplotlib
matplotlib.use('QtAgg')
import matplotlib.pyplot as plt
from PyQt6 import QtWidgets
import sys
import config
#print(str(config.GEO_DIR))
sys.path.insert(0,str(config.GEO_DIR))
sys.path.insert(0,str(config.PATH_DIR))
from geo_current import geo_current
import jupedsim as jps
from matplotlib.patches import Circle
def main_loop():
geometry,doors = geo_current(full_plot = True)
dk_3, d4_6, d7_12, d_exit = doors
model = jps.CollisionFreeSpeedModel()
sim = jps.Simulation(model=model,geometry=geometry)
exit_id = sim.add_exit_stage(d_exit)
journey = jps.JourneyDescription([exit_id])
journey_id = sim.add_journey(journey)
total_sim_time = 60.0
doorways = {
0: dk_3,
1: d4_6,
2: d7_12,
}
# Spawn times for each door (seconds)
spawn_schedule = {
"door_1": [0.0, 5.0, 10.0], # Agents at t=0, 5, 10
"door_2": [2.0, 7.0],
"door_3": [3.0],
}
events = []
for door_name, times in spawn_schedule.items():
for t in times:
events.append((t, doors[door_name]))
events.sort(key=lambda x: x[0])
event_index = 0
while sim.elapsed_time() < total_sim_time:
current_time = sim.elapsed_time()
# Process all events whose time has come
while event_index < len(events) and events[event_index][0] <= current_time:
_, door_pos = events[event_index]
agent_params = jps.CollisionFreeSpeedModelAgentParameters(
position=door_pos,
journey_id=journey_id,
stage_id=exit_id,
radius=0.2,
)
sim.add_agent(agent_params)
event_index += 1
sim.iterate()
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
main_loop()
plt.show(block=False)
sys.exit(app.exec())

View File

@@ -0,0 +1,386 @@
import jupedsim as jps
import shapely
from dataclasses import dataclass
from typing import List, Dict, Tuple
import numpy as np
import matplotlib
matplotlib.use('QtAgg')
import matplotlib.pyplot as plt
from PyQt6 import QtWidgets
import sys
import config
sys.path.insert(0,str(config.GEO_DIR))
from geo_current import geo_current
@dataclass
class AgentSetup:
id:int
grade:str
door:int
speed:float
radius:float
spawn:Tuple[float,float]
@dataclass
class SimSetup:
doorways:Dict[int,shapely.Polygon]
grades:Dict[str,Dict]
min_spacing:float=0.6
total_sim_time:float=200.0
door_capacity:int=10
walkable_area:shapely.Polygon
exit_area:shapely.Polygon
class EvacSim:
def __init__(self,setup:SimSetup):
self.setup = setup
self.all_agents = []
self.all_spawn_events = []
self.simulation = None
self.exit_id = None
self.doorway_system = {}
def run(self):
self.all_agents = self.agent_params()
self.setup_sim_env()
self.spawn_events = self.get_spawn_events()
self.run_sim()
def agent_params(self)->List[AgentSetup]:
agent_id = 1
rng = np.random.default_rng(seed=42)
all_agents = []
for grade in self.setup.grades.keys():
spawn_time = rng.uniform(0.0,115.0)
self.setup.grades[grade]["Spawn Time"] = spawn_time
gr_agent_num = int(self.setup.grades[grade]["Pop Current"])
door = int(self.setup.grades[grade]["Door"])
current_agent = 0
for num in range(gr_agent_num):
speed = rng.normal(
loc=self.setup.grades[grade]["Speed Mean"],
scale=self.setup.grades[grade]["Speed Std Dev"],
size=1)
radius = self.setup.grades[grade]["Radius"]
new_agent = AgentSetup(
id=agent_id,
grade=grade,
door=door,
speed=speed,
radius = radius,
)
all_agents.append(new_agent)
agent_id += 1
current_agent += 1
return all_agents
def setup_sim_env(self):
walkable_area = self.setup.walkable_area
model = jps.CollisionFreeSpeedModel()
self.simulation = jps.Simulation(
model=model,geometry=walkable_area)
self.exit_id = self.simulation.add_exit_stage(
self.setup.exit_polygon)
def doorway_system(self, door_id: int, door_polygon: shapely.Polygon):
def get_waiting_area(self,door_polygon:shapely.Polygon)->shapely.Polygon:
waiting_area = door_polygon.buffer(2.0, join_style=2)
waiting_area = waiting_area.difference(door_polygon)
if waiting_area.geom_type == 'MultiPolygon':
waiting_area = max(waiting_area.geoms, key=lambda p: p.area)
return waiting_area
waiting_area = get_waiting_area(door_polygon)
waiting_set_id = self.simulation.add_waiting_set_stage(waiting_area)
door_centroid = door_polygon.centroid
queue_waypoints = [
self.simulation.add_waypoint_stage((door_centroid.x, door_centroid.y - 1.0), 0.5),
self.simulation.add_waypoint_stage((door_centroid.x, door_centroid.y), 0.5),
self.simulation.add_waypoint_stage((door_centroid.x, door_centroid.y + 1.0), 0.5)
]
journey_stages = [waiting_set_id] + queue_waypoints + [self.exit_id]
journey = jps.JourneyDescription(journey_stages)
journey_id = self.simulation.add_journey(journey)
self.doorway_info[door_id] = {
"waiting_area": waiting_area,
"waiting_set_id": waiting_set_id,
"queue_waypoints": queue_waypoints,
"journey_id": journey_id,
"door_polygon": door_polygon
}
for door_id, door_polygon in self.setup.doorways.items():
self.doorway_system(door_id, door_polygon)
def get_spawn_events(self)->List[Dict]:
events = []
agents_by_grade = {}
def get_spawn_point(self,door:int,num_points:int)->List[Tuple[float,float]]:
polygon = self.setup.doorways[door]
min_x,min_y,max_x,max_y = polygon.bounds
points = []
attempts = 0
max_attempts = num_points * 100
while len(points) < num_points and attempts < max_attempts:
x = random.uniform(min_x, max_x)
y = random.uniform(min_y, max_y)
point = shapely.Point(x, y)
if polygon.contains(point):
too_close = False
for existing in points:
if np.sqrt((x - existing[0])**2 + (y - existing[1])**2) < self.setup.min_spacing:
too_close = True
break
if not too_close:
points.append((x, y))
attempts += 1
return points[:num_points]
for agent in self.all_agents:
for grade_name, grade_info in self.setup.grades.items():
if agent.door == grade_info["Door"]:
if grade_name not in agents_by_grade:
agents_by_grade[grade_name] = []
agents_by_grade[grade_name].append(agent)
break
for grade_name, grade_info in self.setup.grades.items():
door_id = grade_info["Door"]
spawn_time = grade_info["Spawn Time"]
grade_agents = agents_by_group.get(grade_name, [])
if not grade_agents:
continue
door_polygon = self.setup.doorways[door_id]
spawn_positions = self.get_spawn_point(
door_polygon,
len(group_agents))
for agent, position in zip(group_agents, spawn_positions):
events.append({
"time": spawn_time,
"agent": agent,
"position": position,
"grade": grade_name,
"door": door_id
})
events.sort(key=lambda x: x["time"])
return events
def run_sim(self):
spawned_event_indices = set()
agents_in_door_area = {door_id: 0 for door_id in self.config.door_polygons.keys()}
event_index = 0
print("\nStarting Simulation Loop...")
print(f"Total Simulation Time: {self.config.total_simulation_time}s")
print(f"Door Capacity: {self.config.door_capacity} agents per door")
while self.simulation.elapsed_time() < self.config.total_simulation_time:
current_time = self.simulation.elapsed_time()
self._process_spawn_events(
current_time,
event_index,
spawned_event_indices,
agents_in_door_area)
while (event_index < len(self.spawn_events) and \
self.spawn_events[event_index]["time"] <= current_time and \
event_index in spawned_event_indices):
event_index += 1
self.simulation.iterate()
print(f"\nSimulation completed at {self.simulation.elapsed_time():.2f} seconds")
def process_spawn_events(
self,
current_time: float,
event_index: int,
spawned_events: set,
agents_in_door_area: Dict
):
while (event_idx < len(self.spawn_events) and \
self.spawn_events[event_idx]["time"] <= current_time and \
event_idx not in spawned_events):
event = self.spawn_events[event_idx]
door_id = event["door"]
agent = event["agent"]
if agents_in_door_area[door_id] < self.setup.door_capacity:
self.spawn_agent(event,door_id,agent)
agents_in_door_area[door_id] += 1
spawned_events.add(event_idx)
event_index += 1
def spawn_agent(self,event:Dict,door_id:int,agent:AgentSetup):
journey_id = self.doorway_systems[door_id]["journey_id"]
agent_params = jps.CollisionFreeSpeedModelAgentParameters(
position=event["position"],
journey_id=journey_id,
stage_id=self.doorway_system[door_id]["waiting_set_id"],
radius=agent.radius,
v0=agent.speed,
)
agent_id = self.simulation.add_agent(agent_params)
# Optional: Log spawning
if agent_id % 50 == 0: # Log every 50th agent
print(f" Spawned agent {agent_id} (group: {event['group']}, door: {door_id})")
def start_sim_run():
print("Evacuation Simulation")
print("-" * 40)
geometry,[door0,door1,door2,exit_door] = geo_current(full_plot = True)
door_polygons = {
1: door0,
2: door1,
3: door2
}
grade_data = {
"Kindergarden":{
"Door":0,
"Pop Current":34,
"Pop Mean":31.43,
"Pop Std Dev":5.65,
"Speed Mean":1.21,
"Speed Std Dev":0.24,
"Radius":0.407,
"Spawn Time":None
},
"Grade 1":{
"Door":0,
"Pop Current":26,
"Pop Mean":32.57,
"Pop Std Dev":6.27,
"Speed Mean":1.35,
"Speed Std Dev":0.26,
"Radius":0.407,
"Spawn Time":None
},
"Grade 2":{
"Door":0,
"Pop Current":42,
"Pop Mean":34.43,
"Pop Std Dev":6.80,
"Speed Mean":1.42,
"Speed Std Dev":0.28,
"Radius":0.407,
"Spawn Time":None
},
"Grade 3":{
"Door":0,
"Pop Current":39,
"Pop Mean":35.43,
"Pop Std Dev":5.19,
"Speed Mean":1.48,
"Speed Std Dev":0.23,
"Radius":0.407,
"Spawn Time":None
},
"Grade 4":{
"Door":1,
"Pop Current":30,
"Pop Mean":34.86,
"Pop Std Dev":6.77,
"Speed Mean":1.58,
"Speed Std Dev":0.26,
"Radius":0.417,
"Spawn Time":None
},
"Grade 5":{
"Door":1,
"Pop Current":43,
"Pop Mean":36.71,
"Pop Std Dev":7.09,
"Speed Mean":1.59,
"Speed Std Dev":0.24,
"Radius":0.434,
"Spawn Time":None
},
"Grade 6":{
"Door":1,
"Pop Current":29,
"Pop Mean":37.71,
"Pop Std Dev":6.99,
"Speed Mean":1.65,
"Speed Std Dev":0.24,
"Radius":0.454,
"Spawn Time":None
},
"Grade 7":{
"Door":2,
"Pop Current":45,
"Pop Mean":40.43,
"Pop Std Dev":6.02,
"Speed Mean":1.61,
"Speed Std Dev":0.25,
"Radius":0.471,
"Spawn Time":None
},
"Grade 8":{
"Door":2,
"Pop Current":36,
"Pop Mean":40.43,
"Pop Std Dev":5.50,
"Speed Mean":1.66,
"Speed Std Dev":0.24,
"Radius":0.488,
"Spawn Time":None
},
"Grade 9":{
"Door":2,
"Pop Current":44,
"Pop Mean":44.14,
"Pop Std Dev":4.85,
"Speed Mean":1.60,
"Speed Std Dev":0.24,
"Radius":0.500,
"Spawn Time":None
},
"Grade 10":{
"Door":2,
"Pop Current":36,
"Pop Mean":46.29,
"Pop Std Dev":6.29,
"Speed Mean":1.57,
"Speed Std Dev":0.23,
"Radius":0.507,
"Spawn Time":None
},
"Grade 11":{
"Door":2,
"Pop Current":54,
"Pop Mean":48.29,
"Pop Std Dev":3.30,
"Speed Mean":1.51,
"Speed Std Dev":0.22,
"Radius":0.515,
"Spawn Time":None
},
"Grade 12":{
"Door":2,
"Pop Current":46,
"Pop Mean":43.71,
"Pop Std Dev":6.02,
"Speed Mean":1.54,
"Speed Std Dev":0.23,
"Radius":0.520,
"Spawn Time":None
}}
config = SimSetup(
doorways=door_polygons,
grades=grade_data,
total_simulation_time=180.0,
door_capacity=10,
walkable_area=geometry,
exit_area=exit_door
)
sim = EvacSim(config)
return sim.run()
if __name__ == "__main__":
simulation = start_sim_run()
print(f"\nFinal simulation state:")
print(f" Elapsed time: {simulation.elapsed_time():.2f}s")

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,388 @@
import jupedsim as jps
import shapely
import random
from dataclasses import dataclass
from typing import List, Dict, Tuple, Optional
import numpy as np
@dataclass
class AgentConfig:
id: int
grade: str
door: int
speed: float
radius: float
@dataclass
class SimulationConfig:
door_polygons: Dict[int, shapely.Polygon]
groups: Dict[str, Dict]
total_simulation_time: float = 300.0
door_capacity: int = 10
min_spacing: float = 0.6
"""
def __post_init__(self):
'''Set default walkable area and exit polygon if not provided.'''
if self.walkable_area_coords is None:
self.walkable_area_coords = [(0, 0), (50, 0), (50, 30), (0, 30)]
if self.exit_polygon_coords is None:
self.exit_polygon_coords = [(45, 10), (48, 10), (48, 20), (45, 20)]
"""
class PedestrianSimulation:
"""
Main class for running pedestrian simulations with door queues and scheduled spawning.
Architecture Flow:
1. Configuration Setup
2. Agent Configuration Generation
3. Geometry Preparation (Walkable area, Doors, Exit)
4. Door System Setup (Waiting areas, Queues, Journeys)
5. Spawn Event Precomputation
6. Simulation Execution with Dynamic Spawning
7. Results Analysis/Visualization
"""
def __init__(self, config: SimulationConfig):
self.config = config
self.simulation = None
self.door_systems = {}
self.all_agents = []
self.spawn_events = []
self.exit_id = None
def run(self) -> jps.Simulation:
"""
Main orchestrator: Run the complete simulation.
Steps:
1. Create agent configurations
2. Setup simulation environment
3. Precompute spawn events
4. Execute simulation with dynamic spawning
5. Return completed simulation object
"""
print("=" * 60)
print("PEDESTRIAN SIMULATION STARTING")
print("=" * 60)
self.all_agents = self._create_agent_configurations()
print(f"Created {len(self.all_agents)} agent configurations")
self._setup_simulation_environment()
print("Simulation environment setup complete")
self.spawn_events = self._precompute_spawn_events()
print(f"Precomputed {len(self.spawn_events)} spawn events")
self._execute_simulation()
print("Simulation execution complete")
return self.simulation
def _create_agent_configurations(self) -> List[AgentConfig]:
"""Create AgentConfig objects for all agents in all groups."""
all_agents = []
agent_id = 0
for group_name, group_info in self.config.groups.items():
door = group_info["door"]
size = group_info["size"]
for i in range(size):
grade = random.choice(["A", "B", "C", "D", "F"])
speed = random.uniform(1.0, 1.5) # m/s
radius = random.uniform(0.2, 0.3) # meters
all_agents.append(AgentConfig(
id=agent_id,
grade=grade,
door=door,
speed=speed,
radius=radius
))
agent_id += 1
return all_agents
# includes id, grade, door, speed, and radius
def _generate_spawn_points(self, polygon: shapely.Polygon,num_points: int) -> List[Tuple[float, float]]:
"""Generate non-overlapping spawn points within a polygon."""
points = []
min_x, min_y, max_x, max_y = polygon.bounds
attempts = 0
max_attempts = num_points * 100
while len(points) < num_points and attempts < max_attempts:
x = random.uniform(min_x, max_x)
y = random.uniform(min_y, max_y)
point = shapely.Point(x, y)
if polygon.contains(point):
too_close = False
for existing in points:
if np.sqrt((x - existing[0])**2 + (y - existing[1])**2) < self.config.min_spacing:
too_close = True
break
if not too_close:
points.append((x, y))
attempts += 1
return points[:num_points]
# get list of spawn point tuples to provide to agents
def _create_waiting_area(self, door_polygon: shapely.Polygon) -> shapely.Polygon:
"""Create a waiting area adjacent to the door polygon."""
waiting_area = door_polygon.buffer(2.0, join_style=2)
waiting_area = waiting_area.difference(door_polygon)
if waiting_area.geom_type == 'MultiPolygon':
waiting_area = max(waiting_area.geoms, key=lambda p: p.area)
return waiting_area
def _setup_simulation_environment(self):
"""Setup the simulation with door queues and waiting areas."""
# Create walkable area geometry
walkable_area = shapely.Polygon(self.config.walkable_area_coords)
# Create model and simulation
model = jps.CollisionFreeSpeedModel()
self.simulation = jps.Simulation(model=model, geometry=walkable_area)
# Define exit zone
exit_polygon = shapely.Polygon(self.config.exit_polygon_coords)
self.exit_id = self.simulation.add_exit_stage(exit_polygon)
# Create door systems
for door_id, door_polygon in self.config.door_polygons.items():
self._setup_door_system(door_id, door_polygon)
def _setup_door_system(self, door_id: int, door_polygon: shapely.Polygon):
"""Setup queue system for a specific door."""
# Create waiting area
waiting_area = self._create_waiting_area(door_polygon)
waiting_set_id = self.simulation.add_waiting_set_stage(waiting_area)
# Create queue waypoints
door_centroid = door_polygon.centroid
queue_waypoints = [
self.simulation.add_waypoint_stage((door_centroid.x, door_centroid.y - 1.0), 0.5),
self.simulation.add_waypoint_stage((door_centroid.x, door_centroid.y), 0.5),
self.simulation.add_waypoint_stage((door_centroid.x, door_centroid.y + 1.0), 0.5)
]
# Create journey
journey_stages = [waiting_set_id] + queue_waypoints + [self.exit_id]
journey = jps.JourneyDescription(journey_stages)
journey_id = self.simulation.add_journey(journey)
# Store door system
self.door_systems[door_id] = {
"waiting_area": waiting_area,
"waiting_set_id": waiting_set_id,
"queue_waypoints": queue_waypoints,
"journey_id": journey_id,
"door_polygon": door_polygon
}
def _precompute_spawn_events(self) -> List[Dict]:
"""Precompute all spawn events with positions and agent configurations."""
events = []
# Group agents by their assigned group
agents_by_group = {}
for agent in self.all_agents:
for group_name, group_info in self.config.groups.items():
if agent.door == group_info["door"]:
if group_name not in agents_by_group:
agents_by_group[group_name] = []
agents_by_group[group_name].append(agent)
break
# Create events for each group
for group_name, group_info in self.config.groups.items():
door_id = group_info["door"]
spawn_time = group_info["spawn_time"]
group_agents = agents_by_group.get(group_name, [])
if not group_agents:
continue
# Generate spawn positions
door_polygon = self.config.door_polygons[door_id]
spawn_positions = self._generate_spawn_points(
door_polygon,
len(group_agents)
)
# Create events
for agent, position in zip(group_agents, spawn_positions):
events.append({
"time": spawn_time,
"agent_config": agent,
"position": position,
"group": group_name,
"door": door_id
})
# Sort events by time
events.sort(key=lambda x: x["time"])
return events
def _execute_simulation(self):
"""Execute the simulation with dynamic spawning."""
spawned_event_indices = set()
agents_in_door_area = {door_id: 0 for door_id in self.config.door_polygons.keys()}
event_index = 0
'''
print("\nStarting simulation loop...")
print(f"Total simulation time: {self.config.total_simulation_time}s")
print(f"Door capacity: {self.config.door_capacity} agents per door")
'''
while self.simulation.elapsed_time() < self.config.total_simulation_time:
current_time = self.simulation.elapsed_time()
# Process spawn events
self._process_spawn_events(current_time, event_index, spawned_event_indices,
agents_in_door_area)
# Update event index
while (event_index < len(self.spawn_events) and
self.spawn_events[event_index]["time"] <= current_time and
event_index in spawned_event_indices):
event_index += 1
# Iterate simulation
self.simulation.iterate()
print(f"\nSimulation completed at {self.simulation.elapsed_time():.2f} seconds")
def _process_spawn_events(self, current_time: float, event_index: int,
spawned_event_indices: set, agents_in_door_area: Dict):
"""Process all spawn events that should occur at the current time."""
while (event_index < len(self.spawn_events) and
self.spawn_events[event_index]["time"] <= current_time and
event_index not in spawned_event_indices):
event = self.spawn_events[event_index]
door_id = event["door"]
agent_config = event["agent_config"]
# Check door capacity
if agents_in_door_area[door_id] < self.config.door_capacity:
self._spawn_agent(event, door_id, agent_config)
agents_in_door_area[door_id] += 1
spawned_event_indices.add(event_index)
# Move to next event
event_index += 1
def _spawn_agent(self, event: Dict, door_id: int, agent_config: AgentConfig):
"""Spawn a single agent into the simulation."""
journey_id = self.door_systems[door_id]["journey_id"]
agent_params = jps.CollisionFreeSpeedModelAgentParameters(
position=event["position"],
journey_id=journey_id,
stage_id=self.door_systems[door_id]["waiting_set_id"],
radius=agent_config.radius,
v0=agent_config.speed,
)
agent_id = self.simulation.add_agent(agent_params)
# Optional: Log spawning
if agent_id % 50 == 0: # Log every 50th agent
print(f" Spawned agent {agent_id} (group: {event['group']}, door: {door_id})")
# Example usage function
def create_and_run_simulation() -> PedestrianSimulation:
"""
Example function to create and run a complete simulation.
Returns:
PedestrianSimulation: The completed simulation object
"""
# Define door polygons
door_polygons = {
1: shapely.Polygon([(5, 5), (10, 5), (10, 15), (5, 15)]),
2: shapely.Polygon([(20, 5), (25, 5), (25, 15), (20, 15)]),
3: shapely.Polygon([(35, 5), (40, 5), (40, 15), (35, 15)]),
}
# Define groups (example with 4 groups, extend to 13 as needed)
groups = {
"group_1": {"door": 1, "spawn_time": 0.0, "size": 40},
"group_2": {"door": 2, "spawn_time": 5.0, "size": 35},
"group_3": {"door": 3, "spawn_time": 10.0, "size": 30},
"group_4": {"door": 1, "spawn_time": 15.0, "size": 25},
# Add 9 more groups to reach 13 total
}
# Create simulation configuration
config = SimulationConfig(
door_polygons=door_polygons,
groups=groups,
total_simulation_time=200.0, # Adjust as needed
door_capacity=10,
min_spacing=0.6
)
# Create and run simulation
sim_runner = PedestrianSimulation(config)
simulation = sim_runner.run()
return sim_runner
# Quick execution function
def run_simulation_quickstart():
"""Quickstart function for running a basic simulation."""
print("Pedestrian Simulation Quickstart")
print("-" * 40)
# You can modify these parameters
door_polygons = {
1: shapely.Polygon([(2, 2), (6, 2), (6, 8), (2, 8)]),
2: shapely.Polygon([(10, 2), (14, 2), (14, 8), (10, 8)]),
3: shapely.Polygon([(18, 2), (22, 2), (22, 8), (18, 8)]),
}
groups = {
"class_a": {"door": 1, "spawn_time": 0.0, "size": 30},
"class_b": {"door": 2, "spawn_time": 10.0, "size": 25},
"class_c": {"door": 3, "spawn_time": 20.0, "size": 20},
}
config = SimulationConfig(
door_polygons=door_polygons,
groups=groups,
total_simulation_time=100.0,
door_capacity=8
)
sim = PedestrianSimulation(config)
return sim.run()
if __name__ == "__main__":
# Option 1: Use the example function
# sim_runner = create_and_run_simulation()
# Option 2: Use quickstart for testing
simulation = run_simulation_quickstart()
# You can now analyze the simulation results
print(f"\nFinal simulation state:")
print(f" Elapsed time: {simulation.elapsed_time():.2f}s")
# Additional analysis can be added here

View File

@@ -0,0 +1,17 @@
# -*- coding: utf-8 -*-
"""
E.Drake - ENGN-2220
Thu Jan 22 23:48:50 2026
"""
from pathlib import Path
ROOT = Path(__file__).parent.parent.parent
SOURCE_DIR = ROOT/"source"
ARCHIVE_DIR = ROOT/"archive"
PATH_DIR = ROOT/"path"
AGENTS_DIR = ROOT/SOURCE_DIR/"sim_agents"
GEO_DIR = ROOT/SOURCE_DIR/"sim_geometry"
TEST_DIR = ROOT/SOURCE_DIR/"test"

6
config Normal file
View File

@@ -0,0 +1,6 @@
[core]
repositoryformatversion = 0
filemode = false
bare = true
symlinks = false
ignorecase = true

BIN
crosswalk_sim.sqlite Normal file

Binary file not shown.

1
description Normal file
View File

@@ -0,0 +1 @@
Unnamed repository; edit this file 'description' to name the repository.

View File

@@ -0,0 +1,15 @@
#!/bin/sh
#
# An example hook script to check the commit log message taken by
# applypatch from an e-mail message.
#
# The hook should exit with non-zero status after issuing an
# appropriate message if it wants to stop the commit. The hook is
# allowed to edit the commit message file.
#
# To enable this hook, rename this file to "applypatch-msg".
. git-sh-setup
commitmsg="$(git rev-parse --git-path hooks/commit-msg)"
test -x "$commitmsg" && exec "$commitmsg" ${1+"$@"}
:

24
hooks/commit-msg.sample Normal file
View File

@@ -0,0 +1,24 @@
#!/bin/sh
#
# An example hook script to check the commit log message.
# Called by "git commit" with one argument, the name of the file
# that has the commit message. The hook should exit with non-zero
# status after issuing an appropriate message if it wants to stop the
# commit. The hook is allowed to edit the commit message file.
#
# To enable this hook, rename this file to "commit-msg".
# Uncomment the below to add a Signed-off-by line to the message.
# Doing this in a hook is a bad idea in general, but the prepare-commit-msg
# hook is more suited to it.
#
# SOB=$(git var GIT_AUTHOR_IDENT | sed -n 's/^\(.*>\).*$/Signed-off-by: \1/p')
# grep -qs "^$SOB" "$1" || echo "$SOB" >> "$1"
# This example catches duplicate Signed-off-by lines.
test "" = "$(grep '^Signed-off-by: ' "$1" |
sort | uniq -c | sed -e '/^[ ]*1[ ]/d')" || {
echo >&2 Duplicate Signed-off-by lines.
exit 1
}

View File

@@ -0,0 +1,174 @@
#!/usr/bin/perl
use strict;
use warnings;
use IPC::Open2;
# An example hook script to integrate Watchman
# (https://facebook.github.io/watchman/) with git to speed up detecting
# new and modified files.
#
# The hook is passed a version (currently 2) and last update token
# formatted as a string and outputs to stdout a new update token and
# all files that have been modified since the update token. Paths must
# be relative to the root of the working tree and separated by a single NUL.
#
# To enable this hook, rename this file to "query-watchman" and set
# 'git config core.fsmonitor .git/hooks/query-watchman'
#
my ($version, $last_update_token) = @ARGV;
# Uncomment for debugging
# print STDERR "$0 $version $last_update_token\n";
# Check the hook interface version
if ($version ne 2) {
die "Unsupported query-fsmonitor hook version '$version'.\n" .
"Falling back to scanning...\n";
}
my $git_work_tree = get_working_dir();
my $retry = 1;
my $json_pkg;
eval {
require JSON::XS;
$json_pkg = "JSON::XS";
1;
} or do {
require JSON::PP;
$json_pkg = "JSON::PP";
};
launch_watchman();
sub launch_watchman {
my $o = watchman_query();
if (is_work_tree_watched($o)) {
output_result($o->{clock}, @{$o->{files}});
}
}
sub output_result {
my ($clockid, @files) = @_;
# Uncomment for debugging watchman output
# open (my $fh, ">", ".git/watchman-output.out");
# binmode $fh, ":utf8";
# print $fh "$clockid\n@files\n";
# close $fh;
binmode STDOUT, ":utf8";
print $clockid;
print "\0";
local $, = "\0";
print @files;
}
sub watchman_clock {
my $response = qx/watchman clock "$git_work_tree"/;
die "Failed to get clock id on '$git_work_tree'.\n" .
"Falling back to scanning...\n" if $? != 0;
return $json_pkg->new->utf8->decode($response);
}
sub watchman_query {
my $pid = open2(\*CHLD_OUT, \*CHLD_IN, 'watchman -j --no-pretty')
or die "open2() failed: $!\n" .
"Falling back to scanning...\n";
# In the query expression below we're asking for names of files that
# changed since $last_update_token but not from the .git folder.
#
# To accomplish this, we're using the "since" generator to use the
# recency index to select candidate nodes and "fields" to limit the
# output to file names only. Then we're using the "expression" term to
# further constrain the results.
my $last_update_line = "";
if (substr($last_update_token, 0, 1) eq "c") {
$last_update_token = "\"$last_update_token\"";
$last_update_line = qq[\n"since": $last_update_token,];
}
my $query = <<" END";
["query", "$git_work_tree", {$last_update_line
"fields": ["name"],
"expression": ["not", ["dirname", ".git"]]
}]
END
# Uncomment for debugging the watchman query
# open (my $fh, ">", ".git/watchman-query.json");
# print $fh $query;
# close $fh;
print CHLD_IN $query;
close CHLD_IN;
my $response = do {local $/; <CHLD_OUT>};
# Uncomment for debugging the watch response
# open ($fh, ">", ".git/watchman-response.json");
# print $fh $response;
# close $fh;
die "Watchman: command returned no output.\n" .
"Falling back to scanning...\n" if $response eq "";
die "Watchman: command returned invalid output: $response\n" .
"Falling back to scanning...\n" unless $response =~ /^\{/;
return $json_pkg->new->utf8->decode($response);
}
sub is_work_tree_watched {
my ($output) = @_;
my $error = $output->{error};
if ($retry > 0 and $error and $error =~ m/unable to resolve root .* directory (.*) is not watched/) {
$retry--;
my $response = qx/watchman watch "$git_work_tree"/;
die "Failed to make watchman watch '$git_work_tree'.\n" .
"Falling back to scanning...\n" if $? != 0;
$output = $json_pkg->new->utf8->decode($response);
$error = $output->{error};
die "Watchman: $error.\n" .
"Falling back to scanning...\n" if $error;
# Uncomment for debugging watchman output
# open (my $fh, ">", ".git/watchman-output.out");
# close $fh;
# Watchman will always return all files on the first query so
# return the fast "everything is dirty" flag to git and do the
# Watchman query just to get it over with now so we won't pay
# the cost in git to look up each individual file.
my $o = watchman_clock();
$error = $output->{error};
die "Watchman: $error.\n" .
"Falling back to scanning...\n" if $error;
output_result($o->{clock}, ("/"));
$last_update_token = $o->{clock};
eval { launch_watchman() };
return 0;
}
die "Watchman: $error.\n" .
"Falling back to scanning...\n" if $error;
return 1;
}
sub get_working_dir {
my $working_dir;
if ($^O =~ 'msys' || $^O =~ 'cygwin') {
$working_dir = Win32::GetCwd();
$working_dir =~ tr/\\/\//;
} else {
require Cwd;
$working_dir = Cwd::cwd();
}
return $working_dir;
}

8
hooks/post-update.sample Normal file
View File

@@ -0,0 +1,8 @@
#!/bin/sh
#
# An example hook script to prepare a packed repository for use over
# dumb transports.
#
# To enable this hook, rename this file to "post-update".
exec git update-server-info

View File

@@ -0,0 +1,14 @@
#!/bin/sh
#
# An example hook script to verify what is about to be committed
# by applypatch from an e-mail message.
#
# The hook should exit with non-zero status after issuing an
# appropriate message if it wants to stop the commit.
#
# To enable this hook, rename this file to "pre-applypatch".
. git-sh-setup
precommit="$(git rev-parse --git-path hooks/pre-commit)"
test -x "$precommit" && exec "$precommit" ${1+"$@"}
:

49
hooks/pre-commit.sample Normal file
View File

@@ -0,0 +1,49 @@
#!/bin/sh
#
# An example hook script to verify what is about to be committed.
# Called by "git commit" with no arguments. The hook should
# exit with non-zero status after issuing an appropriate message if
# it wants to stop the commit.
#
# To enable this hook, rename this file to "pre-commit".
if git rev-parse --verify HEAD >/dev/null 2>&1
then
against=HEAD
else
# Initial commit: diff against an empty tree object
against=$(git hash-object -t tree /dev/null)
fi
# If you want to allow non-ASCII filenames set this variable to true.
allownonascii=$(git config --type=bool hooks.allownonascii)
# Redirect output to stderr.
exec 1>&2
# Cross platform projects tend to avoid non-ASCII filenames; prevent
# them from being added to the repository. We exploit the fact that the
# printable range starts at the space character and ends with tilde.
if [ "$allownonascii" != "true" ] &&
# Note that the use of brackets around a tr range is ok here, (it's
# even required, for portability to Solaris 10's /usr/bin/tr), since
# the square bracket bytes happen to fall in the designated range.
test $(git diff --cached --name-only --diff-filter=A -z $against |
LC_ALL=C tr -d '[ -~]\0' | wc -c) != 0
then
cat <<\EOF
Error: Attempt to add a non-ASCII file name.
This can cause problems if you want to work with people on other platforms.
To be portable it is advisable to rename the file.
If you know what you are doing you can disable this check using:
git config hooks.allownonascii true
EOF
exit 1
fi
# If there are whitespace errors, print the offending file names and fail.
exec git diff-index --check --cached $against --

View File

@@ -0,0 +1,13 @@
#!/bin/sh
#
# An example hook script to verify what is about to be committed.
# Called by "git merge" with no arguments. The hook should
# exit with non-zero status after issuing an appropriate message to
# stderr if it wants to stop the merge commit.
#
# To enable this hook, rename this file to "pre-merge-commit".
. git-sh-setup
test -x "$GIT_DIR/hooks/pre-commit" &&
exec "$GIT_DIR/hooks/pre-commit"
:

53
hooks/pre-push.sample Normal file
View File

@@ -0,0 +1,53 @@
#!/bin/sh
# An example hook script to verify what is about to be pushed. Called by "git
# push" after it has checked the remote status, but before anything has been
# pushed. If this script exits with a non-zero status nothing will be pushed.
#
# This hook is called with the following parameters:
#
# $1 -- Name of the remote to which the push is being done
# $2 -- URL to which the push is being done
#
# If pushing without using a named remote those arguments will be equal.
#
# Information about the commits which are being pushed is supplied as lines to
# the standard input in the form:
#
# <local ref> <local oid> <remote ref> <remote oid>
#
# This sample shows how to prevent push of commits where the log message starts
# with "WIP" (work in progress).
remote="$1"
url="$2"
zero=$(git hash-object --stdin </dev/null | tr '[0-9a-f]' '0')
while read local_ref local_oid remote_ref remote_oid
do
if test "$local_oid" = "$zero"
then
# Handle delete
:
else
if test "$remote_oid" = "$zero"
then
# New branch, examine all commits
range="$local_oid"
else
# Update to existing branch, examine new commits
range="$remote_oid..$local_oid"
fi
# Check for WIP commit
commit=$(git rev-list -n 1 --grep '^WIP' "$range")
if test -n "$commit"
then
echo >&2 "Found WIP commit in $local_ref, not pushing"
exit 1
fi
fi
done
exit 0

169
hooks/pre-rebase.sample Normal file
View File

@@ -0,0 +1,169 @@
#!/bin/sh
#
# Copyright (c) 2006, 2008 Junio C Hamano
#
# The "pre-rebase" hook is run just before "git rebase" starts doing
# its job, and can prevent the command from running by exiting with
# non-zero status.
#
# The hook is called with the following parameters:
#
# $1 -- the upstream the series was forked from.
# $2 -- the branch being rebased (or empty when rebasing the current branch).
#
# This sample shows how to prevent topic branches that are already
# merged to 'next' branch from getting rebased, because allowing it
# would result in rebasing already published history.
publish=next
basebranch="$1"
if test "$#" = 2
then
topic="refs/heads/$2"
else
topic=`git symbolic-ref HEAD` ||
exit 0 ;# we do not interrupt rebasing detached HEAD
fi
case "$topic" in
refs/heads/??/*)
;;
*)
exit 0 ;# we do not interrupt others.
;;
esac
# Now we are dealing with a topic branch being rebased
# on top of master. Is it OK to rebase it?
# Does the topic really exist?
git show-ref -q "$topic" || {
echo >&2 "No such branch $topic"
exit 1
}
# Is topic fully merged to master?
not_in_master=`git rev-list --pretty=oneline ^master "$topic"`
if test -z "$not_in_master"
then
echo >&2 "$topic is fully merged to master; better remove it."
exit 1 ;# we could allow it, but there is no point.
fi
# Is topic ever merged to next? If so you should not be rebasing it.
only_next_1=`git rev-list ^master "^$topic" ${publish} | sort`
only_next_2=`git rev-list ^master ${publish} | sort`
if test "$only_next_1" = "$only_next_2"
then
not_in_topic=`git rev-list "^$topic" master`
if test -z "$not_in_topic"
then
echo >&2 "$topic is already up to date with master"
exit 1 ;# we could allow it, but there is no point.
else
exit 0
fi
else
not_in_next=`git rev-list --pretty=oneline ^${publish} "$topic"`
/usr/bin/perl -e '
my $topic = $ARGV[0];
my $msg = "* $topic has commits already merged to public branch:\n";
my (%not_in_next) = map {
/^([0-9a-f]+) /;
($1 => 1);
} split(/\n/, $ARGV[1]);
for my $elem (map {
/^([0-9a-f]+) (.*)$/;
[$1 => $2];
} split(/\n/, $ARGV[2])) {
if (!exists $not_in_next{$elem->[0]}) {
if ($msg) {
print STDERR $msg;
undef $msg;
}
print STDERR " $elem->[1]\n";
}
}
' "$topic" "$not_in_next" "$not_in_master"
exit 1
fi
<<\DOC_END
This sample hook safeguards topic branches that have been
published from being rewound.
The workflow assumed here is:
* Once a topic branch forks from "master", "master" is never
merged into it again (either directly or indirectly).
* Once a topic branch is fully cooked and merged into "master",
it is deleted. If you need to build on top of it to correct
earlier mistakes, a new topic branch is created by forking at
the tip of the "master". This is not strictly necessary, but
it makes it easier to keep your history simple.
* Whenever you need to test or publish your changes to topic
branches, merge them into "next" branch.
The script, being an example, hardcodes the publish branch name
to be "next", but it is trivial to make it configurable via
$GIT_DIR/config mechanism.
With this workflow, you would want to know:
(1) ... if a topic branch has ever been merged to "next". Young
topic branches can have stupid mistakes you would rather
clean up before publishing, and things that have not been
merged into other branches can be easily rebased without
affecting other people. But once it is published, you would
not want to rewind it.
(2) ... if a topic branch has been fully merged to "master".
Then you can delete it. More importantly, you should not
build on top of it -- other people may already want to
change things related to the topic as patches against your
"master", so if you need further changes, it is better to
fork the topic (perhaps with the same name) afresh from the
tip of "master".
Let's look at this example:
o---o---o---o---o---o---o---o---o---o "next"
/ / / /
/ a---a---b A / /
/ / / /
/ / c---c---c---c B /
/ / / \ /
/ / / b---b C \ /
/ / / / \ /
---o---o---o---o---o---o---o---o---o---o---o "master"
A, B and C are topic branches.
* A has one fix since it was merged up to "next".
* B has finished. It has been fully merged up to "master" and "next",
and is ready to be deleted.
* C has not merged to "next" at all.
We would want to allow C to be rebased, refuse A, and encourage
B to be deleted.
To compute (1):
git rev-list ^master ^topic next
git rev-list ^master next
if these match, topic has not merged in next at all.
To compute (2):
git rev-list master..topic
if this is empty, it is fully merged to "master".
DOC_END

24
hooks/pre-receive.sample Normal file
View File

@@ -0,0 +1,24 @@
#!/bin/sh
#
# An example hook script to make use of push options.
# The example simply echoes all push options that start with 'echoback='
# and rejects all pushes when the "reject" push option is used.
#
# To enable this hook, rename this file to "pre-receive".
if test -n "$GIT_PUSH_OPTION_COUNT"
then
i=0
while test "$i" -lt "$GIT_PUSH_OPTION_COUNT"
do
eval "value=\$GIT_PUSH_OPTION_$i"
case "$value" in
echoback=*)
echo "echo from the pre-receive-hook: ${value#*=}" >&2
;;
reject)
exit 1
esac
i=$((i + 1))
done
fi

View File

@@ -0,0 +1,42 @@
#!/bin/sh
#
# An example hook script to prepare the commit log message.
# Called by "git commit" with the name of the file that has the
# commit message, followed by the description of the commit
# message's source. The hook's purpose is to edit the commit
# message file. If the hook fails with a non-zero status,
# the commit is aborted.
#
# To enable this hook, rename this file to "prepare-commit-msg".
# This hook includes three examples. The first one removes the
# "# Please enter the commit message..." help message.
#
# The second includes the output of "git diff --name-status -r"
# into the message, just before the "git status" output. It is
# commented because it doesn't cope with --amend or with squashed
# commits.
#
# The third example adds a Signed-off-by line to the message, that can
# still be edited. This is rarely a good idea.
COMMIT_MSG_FILE=$1
COMMIT_SOURCE=$2
SHA1=$3
/usr/bin/perl -i.bak -ne 'print unless(m/^. Please enter the commit message/..m/^#$/)' "$COMMIT_MSG_FILE"
# case "$COMMIT_SOURCE,$SHA1" in
# ,|template,)
# /usr/bin/perl -i.bak -pe '
# print "\n" . `git diff --cached --name-status -r`
# if /^#/ && $first++ == 0' "$COMMIT_MSG_FILE" ;;
# *) ;;
# esac
# SOB=$(git var GIT_COMMITTER_IDENT | sed -n 's/^\(.*>\).*$/Signed-off-by: \1/p')
# git interpret-trailers --in-place --trailer "$SOB" "$COMMIT_MSG_FILE"
# if test -z "$COMMIT_SOURCE"
# then
# /usr/bin/perl -i.bak -pe 'print "\n" if !$first_line++' "$COMMIT_MSG_FILE"
# fi

View File

@@ -0,0 +1,78 @@
#!/bin/sh
# An example hook script to update a checked-out tree on a git push.
#
# This hook is invoked by git-receive-pack(1) when it reacts to git
# push and updates reference(s) in its repository, and when the push
# tries to update the branch that is currently checked out and the
# receive.denyCurrentBranch configuration variable is set to
# updateInstead.
#
# By default, such a push is refused if the working tree and the index
# of the remote repository has any difference from the currently
# checked out commit; when both the working tree and the index match
# the current commit, they are updated to match the newly pushed tip
# of the branch. This hook is to be used to override the default
# behaviour; however the code below reimplements the default behaviour
# as a starting point for convenient modification.
#
# The hook receives the commit with which the tip of the current
# branch is going to be updated:
commit=$1
# It can exit with a non-zero status to refuse the push (when it does
# so, it must not modify the index or the working tree).
die () {
echo >&2 "$*"
exit 1
}
# Or it can make any necessary changes to the working tree and to the
# index to bring them to the desired state when the tip of the current
# branch is updated to the new commit, and exit with a zero status.
#
# For example, the hook can simply run git read-tree -u -m HEAD "$1"
# in order to emulate git fetch that is run in the reverse direction
# with git push, as the two-tree form of git read-tree -u -m is
# essentially the same as git switch or git checkout that switches
# branches while keeping the local changes in the working tree that do
# not interfere with the difference between the branches.
# The below is a more-or-less exact translation to shell of the C code
# for the default behaviour for git's push-to-checkout hook defined in
# the push_to_deploy() function in builtin/receive-pack.c.
#
# Note that the hook will be executed from the repository directory,
# not from the working tree, so if you want to perform operations on
# the working tree, you will have to adapt your code accordingly, e.g.
# by adding "cd .." or using relative paths.
if ! git update-index -q --ignore-submodules --refresh
then
die "Up-to-date check failed"
fi
if ! git diff-files --quiet --ignore-submodules --
then
die "Working directory has unstaged changes"
fi
# This is a rough translation of:
#
# head_has_history() ? "HEAD" : EMPTY_TREE_SHA1_HEX
if git cat-file -e HEAD 2>/dev/null
then
head=HEAD
else
head=$(git hash-object -t tree --stdin </dev/null)
fi
if ! git diff-index --quiet --cached --ignore-submodules $head --
then
die "Working directory has staged changes"
fi
if ! git read-tree -u -m "$commit"
then
die "Could not update working tree to new HEAD"
fi

View File

@@ -0,0 +1,77 @@
#!/bin/sh
# An example hook script to validate a patch (and/or patch series) before
# sending it via email.
#
# The hook should exit with non-zero status after issuing an appropriate
# message if it wants to prevent the email(s) from being sent.
#
# To enable this hook, rename this file to "sendemail-validate".
#
# By default, it will only check that the patch(es) can be applied on top of
# the default upstream branch without conflicts in a secondary worktree. After
# validation (successful or not) of the last patch of a series, the worktree
# will be deleted.
#
# The following config variables can be set to change the default remote and
# remote ref that are used to apply the patches against:
#
# sendemail.validateRemote (default: origin)
# sendemail.validateRemoteRef (default: HEAD)
#
# Replace the TODO placeholders with appropriate checks according to your
# needs.
validate_cover_letter () {
file="$1"
# TODO: Replace with appropriate checks (e.g. spell checking).
true
}
validate_patch () {
file="$1"
# Ensure that the patch applies without conflicts.
git am -3 "$file" || return
# TODO: Replace with appropriate checks for this patch
# (e.g. checkpatch.pl).
true
}
validate_series () {
# TODO: Replace with appropriate checks for the whole series
# (e.g. quick build, coding style checks, etc.).
true
}
# main -------------------------------------------------------------------------
if test "$GIT_SENDEMAIL_FILE_COUNTER" = 1
then
remote=$(git config --default origin --get sendemail.validateRemote) &&
ref=$(git config --default HEAD --get sendemail.validateRemoteRef) &&
worktree=$(mktemp --tmpdir -d sendemail-validate.XXXXXXX) &&
git worktree add -fd --checkout "$worktree" "refs/remotes/$remote/$ref" &&
git config --replace-all sendemail.validateWorktree "$worktree"
else
worktree=$(git config --get sendemail.validateWorktree)
fi || {
echo "sendemail-validate: error: failed to prepare worktree" >&2
exit 1
}
unset GIT_DIR GIT_WORK_TREE
cd "$worktree" &&
if grep -q "^diff --git " "$1"
then
validate_patch "$1"
else
validate_cover_letter "$1"
fi &&
if test "$GIT_SENDEMAIL_FILE_COUNTER" = "$GIT_SENDEMAIL_FILE_TOTAL"
then
git config --unset-all sendemail.validateWorktree &&
trap 'git worktree remove -ff "$worktree"' EXIT &&
validate_series
fi

128
hooks/update.sample Normal file
View File

@@ -0,0 +1,128 @@
#!/bin/sh
#
# An example hook script to block unannotated tags from entering.
# Called by "git receive-pack" with arguments: refname sha1-old sha1-new
#
# To enable this hook, rename this file to "update".
#
# Config
# ------
# hooks.allowunannotated
# This boolean sets whether unannotated tags will be allowed into the
# repository. By default they won't be.
# hooks.allowdeletetag
# This boolean sets whether deleting tags will be allowed in the
# repository. By default they won't be.
# hooks.allowmodifytag
# This boolean sets whether a tag may be modified after creation. By default
# it won't be.
# hooks.allowdeletebranch
# This boolean sets whether deleting branches will be allowed in the
# repository. By default they won't be.
# hooks.denycreatebranch
# This boolean sets whether remotely creating branches will be denied
# in the repository. By default this is allowed.
#
# --- Command line
refname="$1"
oldrev="$2"
newrev="$3"
# --- Safety check
if [ -z "$GIT_DIR" ]; then
echo "Don't run this script from the command line." >&2
echo " (if you want, you could supply GIT_DIR then run" >&2
echo " $0 <ref> <oldrev> <newrev>)" >&2
exit 1
fi
if [ -z "$refname" -o -z "$oldrev" -o -z "$newrev" ]; then
echo "usage: $0 <ref> <oldrev> <newrev>" >&2
exit 1
fi
# --- Config
allowunannotated=$(git config --type=bool hooks.allowunannotated)
allowdeletebranch=$(git config --type=bool hooks.allowdeletebranch)
denycreatebranch=$(git config --type=bool hooks.denycreatebranch)
allowdeletetag=$(git config --type=bool hooks.allowdeletetag)
allowmodifytag=$(git config --type=bool hooks.allowmodifytag)
# check for no description
projectdesc=$(sed -e '1q' "$GIT_DIR/description")
case "$projectdesc" in
"Unnamed repository"* | "")
echo "*** Project description file hasn't been set" >&2
exit 1
;;
esac
# --- Check types
# if $newrev is 0000...0000, it's a commit to delete a ref.
zero=$(git hash-object --stdin </dev/null | tr '[0-9a-f]' '0')
if [ "$newrev" = "$zero" ]; then
newrev_type=delete
else
newrev_type=$(git cat-file -t $newrev)
fi
case "$refname","$newrev_type" in
refs/tags/*,commit)
# un-annotated tag
short_refname=${refname##refs/tags/}
if [ "$allowunannotated" != "true" ]; then
echo "*** The un-annotated tag, $short_refname, is not allowed in this repository" >&2
echo "*** Use 'git tag [ -a | -s ]' for tags you want to propagate." >&2
exit 1
fi
;;
refs/tags/*,delete)
# delete tag
if [ "$allowdeletetag" != "true" ]; then
echo "*** Deleting a tag is not allowed in this repository" >&2
exit 1
fi
;;
refs/tags/*,tag)
# annotated tag
if [ "$allowmodifytag" != "true" ] && git rev-parse $refname > /dev/null 2>&1
then
echo "*** Tag '$refname' already exists." >&2
echo "*** Modifying a tag is not allowed in this repository." >&2
exit 1
fi
;;
refs/heads/*,commit)
# branch
if [ "$oldrev" = "$zero" -a "$denycreatebranch" = "true" ]; then
echo "*** Creating a branch is not allowed in this repository" >&2
exit 1
fi
;;
refs/heads/*,delete)
# delete branch
if [ "$allowdeletebranch" != "true" ]; then
echo "*** Deleting a branch is not allowed in this repository" >&2
exit 1
fi
;;
refs/remotes/*,commit)
# tracking branch
;;
refs/remotes/*,delete)
# delete tracking branch
if [ "$allowdeletebranch" != "true" ]; then
echo "*** Deleting a tracking branch is not allowed in this repository" >&2
exit 1
fi
;;
*)
# Anything else (is there anything else?)
echo "*** Update hook: unknown type of update to ref $refname of type $newrev_type" >&2
exit 1
;;
esac
# --- Finished
exit 0

6
info/exclude Normal file
View File

@@ -0,0 +1,6 @@
# git ls-files --others --exclude-from=.git/info/exclude
# Lines that start with '#' are comments.
# For a project mostly in C, the following would be a good set of
# exclude patterns (uncomment them if you want to use them):
# *.[oa]
# *~

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,2 @@
x<01><>Mn<4D>0 <0C><><EFBFBD>><3E>kEB UU7]<5D>&q %?O<>jO_x=AW<1E><>'<27><>1z<31>nTO\<5C>@<40>v<EFBFBD>f<EFBFBD><66>۞<04>]<5D><><EFBFBD><EFBFBD>R;E<>:<3A><>;J b<>f<EFBFBD>Hb<10>9<EFBFBD><39>$P<>$[i<>n<EFBFBD><6E>aTV<54><56><EFBFBD><EFBFBD><EFBFBD>\<5C><>WL<57>J<EFBFBD>Yl<59><6C>ޗ<EFBFBD>><3E><><1C>@ zRreϭj<CFAD><6A>t<EFBFBD>yL<79><06>H<EFBFBD><48><EFBFBD><EFBFBD><EFBFBD>
<EFBFBD><1C>l>-0Lf<4C>A! <0C><>l\a5<61>N<EFBFBD><4E><EFBFBD><EFBFBD>#X<>~Ip`<05><02><>n<EFBFBD><6E><EFBFBD>'$<24><> <0C><><EFBFBD>.<2E><>W~<7E><><EFBFBD>Z <1B>|9<><39>

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,3 @@
x<01>W<EFBFBD>n<EFBFBD>0<10>9_A<5F>d !n<><15><>ni<6E><69><17> [<5B><>Z]ZF<5A><46>/<2F>X<EFBFBD><58>&q<11><>7<EFBFBD><37>o<EFBFBD>^<5E>r(<28><>%<1F><>!?<3F><><19>R<><52><EFBFBD>,<2C><><EFBFBD><EFBFBD>2e<32><50>U<EFBFBD><55><EFBFBD><EFBFBD>=;<3B>'<27>
<EFBFBD>[!<21><>I)Qճͩ,<2C><><EFBFBD><EFBFBD>J<EFBFBD>ry<72><79>G1<47><31><02><>1[+y<<3C><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>4<EFBFBD><34>J<1A><>C<>X<EFBFBD>Ρ<EFBFBD>QDIbb<11> <20><11> <0C>)<29><>7<EFBFBD>M
<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>I<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><10>c

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Some files were not shown because too many files have changed in this diff Show More