import { settings } from ".." ; import { Vector } from "../components/vector" ; import { particleContainer } from "../containers" ; import { resolveShapeFactory } from "../systems/shapes" ; import { rotationToNormal } from "../util" ; import { Emitter } from "./emitter" ; import { RenderOptions } from "./options" ; import { Particle } from "./particle" ; /**
 * Represents a renderer used to draw particles to the DOM via HTML
 * elements. Additionally, it is responsible for purging the elements
 * of destroyed particles from the DOM.
 */ export class Renderer { /**
     * The lookup of elements currently handled by the renderer, with the
     * particle ID as key and a HTMLElement as the value.
     */ public elements : Map < symbol , HTMLElement > = new Map ( ) ; /**
     * The normalized direction the light comes from.
     */ public light : Vector = new Vector ( 0 , 0 , 1 ) ; /**
     * The collection of symbols containing the particles that were rendered this frame.
     * This is, for example, used to delete unused particles from the DOM.
     */ private renderedParticles : symbol [ ] ; /**
     * Whether or not the renderer should actually draw particles.
     */ private enabled = true ; public constructor ( ) { this . enabled = ! settings . respectReducedMotion || ! window . matchMedia ( "(prefers-reduced-motion)" ) . matches ; } /**
     * Begins a new render block. During the rendering phase, a list of rendered particles
     * is tracked, so that stale particles can be removed later.
     */ public begin ( ) { this . renderedParticles = [ ] ; } returns public end ( ) { const it = this . elements . keys ( ) ; let result = it . next ( ) ; while ( ! result . done ) { const id = result . value as symbol ; if ( ! this . renderedParticles . includes ( id ) ) { this . elements . get ( id ) . remove ( ) ; this . elements . delete ( id ) ; } result = it . next ( ) ; } return this . renderedParticles . length ; } param particle param emitter public renderParticle ( particle , emitter ) { if ( ! this . enabled ) return ; const options = emitter . renderer ; const element = this . elements . has ( particle . id ) ? this . elements . get ( particle . id ) : this . createParticleElement ( particle , options ) ; if ( options . applyColor ) { options . applyColor ( particle . color , element ) ; } if ( options . applyOpacity ) { options . applyOpacity ( particle . opacity , element ) ; } if ( options . applyLighting ) { const normal = rotationToNormal ( particle . rotation ) ; const lightingCoefficient = normal . dot ( this . light ) ; options . applyLighting ( lightingCoefficient , element ) ; } if ( options . applyTransform ) { options . applyTransform ( particle , element ) ; } this . renderedParticles . push ( particle . id ) ; } /**
     * Creates the HTMLElement for a particle that does not have one already.
     */ private createParticleElement ( particle , options ) { const resolved = resolveShapeFactory ( options . shapeFactory ) ; const element = resolved . cloneNode ( true ) as HTMLElement ; element . style . position = "absolute" ; this . elements . set ( particle . id , particleContainer . current . appendChild ( element ) ) ; return element ; } }