import { IPointData } from '@pixi/math' ; import { ListProperty } from '../editor/Types' ; import { SpawnShape } from './SpawnShape' ; interface Segment { p1 : IPointData ; p2 : IPointData ; l : number ; } /**
 * A spawn shape that picks a random position along a series of line segments. If those
 * line segments form a polygon, particles will only be placed on the perimeter of that polygon.
 *
 * Example config:
 * ```javascript
 * {
 *      type: 'polygonalChain',
 *      data: [
 *          [{x: 0, y: 0}, {x: 10, y: 10}, {x: 20, y: 0}],
 *          [{x: 0, y, -10}, {x: 10, y: 0}, {x: 20, y: -10}]
 *      ]
 * }
 * ```
 */ export class PolygonalChain implements SpawnShape { public static type = 'polygonalChain' ; public static editorConfig : ListProperty = null ; /**
     * List of segment objects in the chain.
     */ private segments : Segment [ ] ; /**
     * Total length of all segments of the chain.
     */ private totalLength : number ; /**
     * Total length of segments up to and including the segment of the same index.
     * Used for weighted random selection of segment.
     */ private countingLengths : number [ ] ; param data constructor ( data ) { this . segments = [ ] ; this . countingLengths = [ ] ; this . totalLength = 0 ; this . init ( data ) ; } param data private init ( data ) { if ( ! data || ! data . length ) { this . segments . push ( { p1 : { x : 0 , y : 0 } , p2 : { x : 0 , y : 0 } , l : 0 } ) ; } else if ( Array . isArray ( data [ 0 ] ) ) { for ( let i = 0 ; i < data . length ; ++ i ) { const chain = data [ i ] as IPointData [ ] ; let prevPoint = chain [ 0 ] as IPointData ; for ( let j = 1 ; j < chain . length ; ++ j ) { const second = chain [ j ] as IPointData ; this . segments . push ( { p1 : prevPoint , p2 : second , l : 0 } ) ; prevPoint = second ; } } } else { let prevPoint = data [ 0 ] as IPointData ; for ( let i = 1 ; i < data . length ; ++ i ) { const second = data [ i ] as IPointData ; this . segments . push ( { p1 : prevPoint , p2 : second , l : 0 } ) ; prevPoint = second ; } } for ( let i = 0 ; i < this . segments . length ; ++ i ) { const { p1 , p2 } = this . segments [ i ] ; const segLength = Math . sqrt ( ( ( p2 . x - p1 . x ) * ( p2 . x - p1 . x ) ) + ( ( p2 . y - p1 . y ) * ( p2 . y - p1 . y ) ) ) ; this . segments [ i ] . l = segLength ; this . totalLength += segLength ; this . countingLengths . push ( this . totalLength ) ; } } param out public getRandPos ( out ) { const rand = Math . random ( ) * this . totalLength ; let chosenSeg ; let lerp ; if ( this . segments . length === 1 ) { chosenSeg = this . segments [ 0 ] ; lerp = rand ; } else { for ( let i = 0 ; i < this . countingLengths . length ; ++ i ) { if ( rand < this . countingLengths [ i ] ) { chosenSeg = this . segments [ i ] ; lerp = i === 0 ? rand : rand - this . countingLengths [ i - 1 ] ; break ; } } } lerp /= chosenSeg . l || 1 ; const { p1 , p2 } = chosenSeg ; out . x = p1 . x + ( lerp * ( p2 . x - p1 . x ) ) ; out . y = p1 . y + ( lerp * ( p2 . y - p1 . y ) ) ; } }