/ cli / source / datapack / geometry / SpawnTerritoryPoints.ts
SpawnTerritoryPoints.ts
  1  import { PolygonPoint, QuickHull } from '../libraries/quickHull'
  2  import { hachureLines, Line, Point } from '../libraries/hachureLines'
  3  import { RoaringBitmap32, SerializationFormat } from 'roaring'
  4  import { GeometryType, IGeometryGenerator, TerritoryPointsParameters } from './Interfaces'
  5  import * as Buffer from 'buffer'
  6  
  7  export type LinePoint = [ number, number ]
  8  const maxDifference = Math.pow( 2, 16 )
  9  
 10  export class SpawnTerritoryPoints implements IGeometryGenerator {
 11      minX: number = Number.MAX_SAFE_INTEGER
 12      maxX: number = Number.MIN_SAFE_INTEGER
 13  
 14      minY: number = Number.MAX_SAFE_INTEGER
 15      maxY: number = Number.MIN_SAFE_INTEGER
 16  
 17      data: RoaringBitmap32 = new RoaringBitmap32()
 18      distance: number
 19  
 20      constructor( points: Array<LinePoint>, distance: number = 100 ) {
 21          this.distance = distance
 22          let polygonPoints = points.map( ( point: LinePoint ) => {
 23              return {
 24                  x: point[ 0 ],
 25                  y: point[ 1 ]
 26              }
 27          } )
 28  
 29          let vertices : Array<Point> = QuickHull( polygonPoints ).map( ( point: PolygonPoint ) : Point => {
 30              this.minX = Math.min( point.x, this.minX )
 31              this.minY = Math.min( point.y, this.minY )
 32  
 33              this.maxX = Math.max( point.x, this.maxX )
 34              this.maxY = Math.max( point.y, this.maxY )
 35  
 36              return [ point.x, point.y ]
 37          } )
 38  
 39          /*
 40              Due to how data is packed we can only process data difference fitting into 16-bit value (two values fit into 32-bit integer).
 41              Hence, check to see if there is data that would not be able to be processed.
 42           */
 43          let xDifference = Math.abs( this.maxX - this.minX )
 44          let yDifference = Math.abs( this.maxY - this.minY )
 45          if ( xDifference > maxDifference || yDifference > maxDifference ) {
 46              throw new Error( `Unable to process spawn territory with points: ${JSON.stringify( points )}, due to ${JSON.stringify( { xDifference, yDifference, maxDifference } )}` )
 47          }
 48  
 49          this.distance = this.getAdjustedDistance( xDifference, yDifference, distance )
 50  
 51          let minimumDistance: number = this.distance / 2
 52          hachureLines( vertices, this.distance, 0 ).forEach( ( line: Line ) : void => {
 53              return this.getLinePoints( line[ 0 ], line[ 1 ], this.distance, minimumDistance, this.data, this.minX, this.minY )
 54          } )
 55      }
 56  
 57      private getAdjustedDistance( xDifference : number, yDifference : number, distance: number ) : number {
 58          let smallAreaSize = distance * 4
 59          if ( xDifference < smallAreaSize || yDifference < smallAreaSize ) {
 60              return Math.floor( distance * 0.4 )
 61          }
 62  
 63          let extraLargeSize = distance * 100
 64          if ( xDifference > extraLargeSize && yDifference > extraLargeSize ) {
 65              return Math.floor( distance * 2 )
 66          }
 67  
 68          let largeSize = distance * 70
 69          if ( xDifference > largeSize && yDifference > largeSize ) {
 70              return Math.floor( distance * 1.5 )
 71          }
 72  
 73          let mediumSize = distance * 50
 74          if ( xDifference > mediumSize && yDifference > mediumSize ) {
 75              return Math.floor( distance * 1.2 )
 76          }
 77  
 78          return distance
 79      }
 80  
 81      /*
 82          Per https://math.stackexchange.com/questions/175896/finding-a-point-along-a-line-a-certain-distance-away-from-another-point
 83       */
 84      private getLinePoints( start: LinePoint, finish: LinePoint, distance: number, minimumDistance: number, data: RoaringBitmap32, minX: number, minY: number ) : void {
 85          let lineSize = Math.hypot( finish[ 0 ] - start[ 0 ], finish[ 1 ] - start[ 1 ] )
 86  
 87          this.addPoint( start[ 0 ], start[ 1 ], data, minX, minY )
 88  
 89          if ( lineSize < distance && lineSize > minimumDistance ) {
 90              return this.addPoint( finish[ 0 ], finish[ 1 ], data, minX, minY )
 91          }
 92  
 93          let currentDistance = distance
 94  
 95          while ( currentDistance < lineSize ) {
 96              let ratio = currentDistance / lineSize
 97              let x = Math.round( ( 1 - ratio ) * start[ 0 ] + ratio * finish[ 0 ] )
 98              let y = Math.round( ( 1 - ratio ) * start[ 1 ] + ratio * finish[ 1 ] )
 99  
100              this.addPoint( x, y, data, minX, minY )
101  
102              currentDistance += distance
103          }
104      }
105  
106      private addPoint( originX: number, originY: number, data: RoaringBitmap32, minX: number, minY: number ) : void {
107          let x = originX - minX
108          let y = originY - minY
109  
110          data.add( x + ( y << 16 ) )
111      }
112  
113      generatePoints(): Buffer {
114          // @ts-ignore
115          return this.data.serialize( SerializationFormat.croaring )
116      }
117  
118      getName(): string {
119          return 'SpawnTerritory'
120      }
121  
122      getParameters(): TerritoryPointsParameters {
123          let centerX = Math.abs( this.maxX - this.minX ) / 2 + this.minX
124          let centerY = Math.abs( this.maxY - this.minY ) / 2 + this.minY
125  
126          return {
127              centerX,
128              centerY,
129              distance: this.distance,
130              maxX: this.maxX,
131              maxY: this.maxY,
132              minX: this.minX,
133              minY: this.minY,
134              size: this.data.size,
135          }
136      }
137  
138      getSize(): number {
139          return this.data.size
140      }
141  
142      getType(): GeometryType {
143          return GeometryType.SpawnTerritory
144      }
145  
146  }