Async Animation Sink
An AnimationSink that hands events to a dedicated background writer thread through a bounded queue, so serialization and disk I/O happen off the simulation thread. This is the sink for watching a long run as it executes, where buffering the whole run in memory (as MemoryBufferedAnimationSink does) is not viable.
The simulation thread (producer) calls emit; a single writer thread (consumer) takes events in order and passes each to consume (which serializes/writes it). A bounded BlockingQueue sits between them and absorbs bursts. What happens when the producer outruns the writer and the queue fills is governed by overflowPolicy:
OverflowPolicy.BLOCK (default) — the producer waits for space. Lossless; may briefly slow the simulation under a sustained burst. Preferred for animation because events are state deltas: dropping one corrupts the renderer's reconstructed state downstream.
OverflowPolicy.DROP_NEWEST — discard the incoming event; never slows the sim.
OverflowPolicy.DROP_OLDEST — evict the oldest queued event to make room.
Shutdown is via onExperimentEnd, which enqueues a sentinel and joins the writer thread, guaranteeing every already-emitted event is written before it returns (no tail loss).
Parameters
the bounded queue size (number of events); must be >= 1
what emit does when the queue is full
the write action invoked on the writer thread for each event, in emission order (e.g. { event -> output.write(event) })