1717from .reel import Reel , ReelMode
1818from .scenes import SceneManager
1919from .weather_physics import (
20- BoundingBox ,
2120 Particle ,
2221 Shape ,
2322 compute_render_cells ,
@@ -212,18 +211,17 @@ def __init__(
212211 self ._weather_type : str | None = None
213212 self ._intensity = 0.0
214213 self ._wind_speed = 0.0
215- self ._exclusion_zones : list [BoundingBox ] = []
216214
217215 # Particle SoA arrays
218216 n = self ._batch_size
219- self .p_x = np .zeros (n , dtype = np .float64 )
220- self .p_y = np .zeros (n , dtype = np .float64 )
221- self .p_vx = np .zeros (n , dtype = np .float64 )
222- self .p_vy = np .zeros (n , dtype = np .float64 )
223- self .p_age = np .zeros (n , dtype = np .int64 )
224- self .p_lifetime = np .zeros (n , dtype = np .int64 )
225- self .p_shape_idx = np .zeros (n , dtype = np .int64 )
226- self .p_count = 0
217+ self ._p_x = np .zeros (n , dtype = np .float64 )
218+ self ._p_y = np .zeros (n , dtype = np .float64 )
219+ self ._p_vx = np .zeros (n , dtype = np .float64 )
220+ self ._p_vy = np .zeros (n , dtype = np .float64 )
221+ self ._p_age = np .zeros (n , dtype = np .int64 )
222+ self ._p_lifetime = np .zeros (n , dtype = np .int64 )
223+ self ._p_shape_idx = np .zeros (n , dtype = np .int64 )
224+ self ._p_count = 0
227225
228226 # Ambient clouds
229227 self ._ambient_clouds : list [Particle ] = []
@@ -238,22 +236,10 @@ def __init__(
238236 self ._render_out_shape : np .ndarray = np .zeros (0 , dtype = np .int32 )
239237 self ._render_out_cell : np .ndarray = np .zeros (0 , dtype = np .int32 )
240238
241- # Prewarm shape arrays for all weather types
242- self ._prewarm_shapes ()
243-
244239 @property
245240 def bbox (self ) -> BBox :
246241 return BBox (0 , 0 , self ._width , self ._height )
247242
248- def _prewarm_shapes (self ) -> None :
249- original = self ._weather_type
250- for name in self ._registry .list_names ("weather" ):
251- self ._weather_type = name
252- self ._rebuild_shape_cache ()
253- self ._weather_type = original
254- if original :
255- self ._rebuild_shape_cache ()
256-
257243 def _get_shape (self , name : str ) -> Shape | None :
258244 elem = self ._registry .get ("particles" , name )
259245 if not elem :
@@ -310,14 +296,14 @@ def _build_shape_arrays(self) -> None:
310296 self ._render_out_cell = np .zeros (max_output , dtype = np .int32 )
311297
312298 def _grow_arrays (self ) -> None :
313- old_size = len (self .p_x )
299+ old_size = len (self ._p_x )
314300 new_size = old_size * 2
315- for attr in ("p_x " , "p_y " , "p_vx " , "p_vy " ):
301+ for attr in ("_p_x " , "_p_y " , "_p_vx " , "_p_vy " ):
316302 old = getattr (self , attr )
317303 new = np .zeros (new_size , dtype = np .float64 )
318304 new [:old_size ] = old
319305 setattr (self , attr , new )
320- for attr in ("p_age " , "p_lifetime " , "p_shape_idx " ):
306+ for attr in ("_p_age " , "_p_lifetime " , "_p_shape_idx " ):
321307 old = getattr (self , attr )
322308 new = np .zeros (new_size , dtype = np .int64 )
323309 new [:old_size ] = old
@@ -326,7 +312,7 @@ def _grow_arrays(self) -> None:
326312 def set_weather (self , weather_type : str , intensity : float = 0.6 , wind_speed : float = 0.0 ):
327313 if weather_type != self ._weather_type :
328314 self ._weather_type = weather_type
329- self .p_count = 0
315+ self ._p_count = 0
330316 self ._rebuild_shape_cache ()
331317 self ._intensity = intensity
332318 self ._wind_speed = wind_speed
@@ -343,16 +329,16 @@ def tick(self, **ctx) -> None:
343329 if not self ._weather_type or not self ._shape_cache :
344330 return
345331
346- if self .p_count > 0 :
347- self .p_count = tick_physics_batch (
348- self .p_x ,
349- self .p_y ,
350- self .p_vx ,
351- self .p_vy ,
352- self .p_age ,
353- self .p_lifetime ,
354- self .p_shape_idx ,
355- self .p_count ,
332+ if self ._p_count > 0 :
333+ self ._p_count = tick_physics_batch (
334+ self ._p_x ,
335+ self ._p_y ,
336+ self ._p_vx ,
337+ self ._p_vy ,
338+ self ._p_age ,
339+ self ._p_lifetime ,
340+ self ._p_shape_idx ,
341+ self ._p_count ,
356342 1 ,
357343 float (self ._width ),
358344 float (self ._height ),
@@ -361,7 +347,7 @@ def tick(self, **ctx) -> None:
361347
362348 max_particles = int (self ._intensity * self ._max_particles_base )
363349 spawn_rate = self ._intensity * 2.0
364- spawn_count = min (np .random .poisson (spawn_rate * 3 ), max_particles - self .p_count )
350+ spawn_count = min (np .random .poisson (spawn_rate * 3 ), max_particles - self ._p_count )
365351 if spawn_count > 0 :
366352 self ._spawn_batch (spawn_count )
367353
@@ -399,7 +385,7 @@ def _tick_ambient_clouds(self) -> None:
399385 )
400386
401387 def _spawn_batch (self , count : int ) -> None :
402- while self .p_count + count > len (self .p_x ):
388+ while self ._p_count + count > len (self ._p_x ):
403389 self ._grow_arrays ()
404390
405391 s = self ._speed_multiplier
@@ -417,38 +403,30 @@ def _spawn_batch(self, count: int) -> None:
417403 params = (0 , w , 0 , h , - 0.05 * s , 0.1 * s , - 0.03 * s , 0.06 * s , 60 , 90 )
418404
419405 spawn_particles (
420- self .p_x ,
421- self .p_y ,
422- self .p_vx ,
423- self .p_vy ,
424- self .p_age ,
425- self .p_lifetime ,
426- self .p_shape_idx ,
427- self .p_count ,
406+ self ._p_x ,
407+ self ._p_y ,
408+ self ._p_vx ,
409+ self ._p_vy ,
410+ self ._p_age ,
411+ self ._p_lifetime ,
412+ self ._p_shape_idx ,
413+ self ._p_count ,
428414 count ,
429415 len (self ._shape_cache ),
430416 * params ,
431417 )
432- self .p_count += count
433-
434- def _build_exclusion_set (self ) -> set :
435- blocked = set ()
436- for zone in self ._exclusion_zones :
437- for x in range (zone .x , zone .x + zone .w ):
438- for y in range (zone .y , zone .y + zone .h ):
439- blocked .add ((x , y ))
440- return blocked
418+ self ._p_count += count
441419
442420 def render (self , out_chars : np .ndarray , out_colors : np .ndarray ) -> None :
443- # Build exclusion zones from face sprites in the registry
444- self . _exclusion_zones = []
421+ # Build exclusion set from face sprites in the registry
422+ blocked : set [ tuple [ int , int ]] = set ()
445423 if self ._scene_registry :
446424 for s in self ._scene_registry .alive ():
447425 if isinstance (s , FaceCel ):
448426 b = s .bbox
449- self . _exclusion_zones . append ( BoundingBox ( x = b . x , y = b . y , w = b . w , h = b . h ))
450-
451- blocked = self . _build_exclusion_set ( )
427+ for x in range ( b . x , b . x2 ):
428+ for y in range ( b . y , b . y2 ):
429+ blocked . add (( x , y ) )
452430 color = 15
453431 w , h = self ._width , self ._height
454432
@@ -467,7 +445,7 @@ def render(self, out_chars: np.ndarray, out_colors: np.ndarray) -> None:
467445 out_colors [cy , cx ] = color
468446
469447 # Render JIT particles
470- n = self .p_count
448+ n = self ._p_count
471449 if n == 0 or self ._shape_offsets is None :
472450 return
473451
@@ -481,9 +459,9 @@ def render(self, out_chars: np.ndarray, out_colors: np.ndarray) -> None:
481459 self ._render_out_cell = np .zeros (new_size , dtype = np .int32 )
482460
483461 num_cells = compute_render_cells (
484- self .p_x ,
485- self .p_y ,
486- self .p_shape_idx ,
462+ self ._p_x ,
463+ self ._p_y ,
464+ self ._p_shape_idx ,
487465 n ,
488466 self ._shape_offsets ,
489467 self ._shape_cell_counts ,
@@ -635,8 +613,7 @@ def render(self, out_chars: np.ndarray, out_colors: np.ndarray) -> None:
635613 chars , colors = self ._cache_percent (int_pct )
636614 filled = int (int_pct / 100 * self ._bar_width )
637615 out_chars [self ._y , self ._x : self ._x + self ._bar_width ] = chars
638- out_colors [self ._y , self ._x : self ._x + self ._bar_width ] = colors
639- # Override empty portion color with status color
616+ out_colors [self ._y , self ._x : self ._x + filled ] = colors [:filled ]
640617 out_colors [self ._y , self ._x + filled : self ._x + self ._bar_width ] = empty_color
641618 else :
642619 filled = int (percent / 100 * self ._bar_width )
0 commit comments