pixi-cull
Version:
a library to visibly cull objects designed to work with pixi.js
154 lines (134 loc) • 4.68 kB
text/typescript
import * as PIXI from 'pixi.js'
import { Viewport } from 'pixi-viewport'
import random from 'yy-random'
import forkMe from 'fork-me-github'
import FPS from 'yy-fps'
import { Simple, SpatialHash, SpatialHashStats } from '../dist/pixi-cull.es.js'
function el(query: string, parentQuery?: string): HTMLElement {
if (parentQuery) {
return document.querySelector(parentQuery)
} else {
return document.querySelector(query)
}
}
const START_X = -25000
const START_Y = -25000
const WIDTH = 50000
const HEIGHT = 50000
const DOTS = 10000
const DOTS_SIZE = 100
let application: PIXI.Application,
viewport: Viewport,
dots: PIXI.Container,
simple: Simple,
hash: SpatialHash,
stats: SpatialHashStats,
fps: FPS
function getMode(): string {
return (el('input[name=cull-types]:checked') as HTMLInputElement).value
}
function ui() {
fps = new FPS({ side: 'bottomLeft' })
el('.buckets').style.display = getMode() === 'hash' ? 'block' : 'none'
el('.choices').addEventListener('change', () => {
const mode = getMode()
if (mode === 'none') {
for (const dot of dots.children) {
dot.visible = true
}
}
updateCull()
el('.buckets').style.display = mode === 'hash' ? 'block' : 'none'
if (mode === 'hash') {
el('.sparseness-buckets').innerHTML = Math.round(hash.getSparseness() * 100) + '%'
el('.largest-bucket').innerHTML = hash.getLargest() + ''
el('.average-bucket').innerHTML = Math.round(hash.getAverageSize() * 100) / 100 + ''
el('.hash').style.display = 'block'
} else {
el('.hash').style.display = 'none'
}
})
const simpleTest = el('.simple-test') as HTMLInputElement
simpleTest.addEventListener('change', () => {
hash.simpleTest = simpleTest.checked
updateCull()
})
const dirtyTest = el('.dirty-test') as HTMLInputElement
dirtyTest.addEventListener('change', () => {
hash.dirtyTest = simple.dirtyTest = dirtyTest.checked
})
el('.instructions').style.opacity = '0';
forkMe(null, { side: 'left' })
}
function pixi() {
const view = el('.pixi') as HTMLCanvasElement
application = new PIXI.Application({
width: view.offsetWidth,
height: view.offsetHeight,
view,
backgroundAlpha: 0
})
viewport = application.stage.addChild(new Viewport())
viewport.drag().pinch().decelerate().wheel()
viewport.resize(view.offsetWidth, view.offsetHeight, WIDTH, HEIGHT)
viewport.fitWidth(5000)
const ticker = PIXI.Ticker
ticker.shared.add(update)
// _test = _viewport.addChild(new PIXI.Graphics())
window.addEventListener('resize', () => {
// weird hack needed for flexbox to work correctly; probably a better way to do this
application.renderer.resize(0, 0)
viewport.resize(view.offsetWidth, view.offsetHeight)
application.renderer.resize(view.offsetWidth, view.offsetHeight)
viewport.dirty = true
})
}
function createDots() {
dots = viewport.addChild(new PIXI.Container())
for (let i = 0; i < DOTS; i++) {
const dot = dots.addChild(new PIXI.Sprite(PIXI.Texture.WHITE))
dot.tint = random.color()
dot.width = dot.height = DOTS_SIZE
dot.position.set(random.range(START_X, WIDTH), random.range(START_Y, HEIGHT))
}
simple = new Simple()
simple.addList(dots.children, true)
hash = new SpatialHash()
hash.addContainer(dots, true)
}
function update() {
if (viewport.dirty) {
updateCull()
viewport.dirty = false
}
fps.frame()
}
function updateCull() {
switch (getMode()) {
case 'simple':
simple.cull(viewport.getVisibleBounds())
stats = simple.stats() as SpatialHashStats
break
case 'hash':
const visible = hash.cull(viewport.getVisibleBounds())
const total = hash.getBuckets().length
el('.visible-buckets').innerHTML = visible + ''
el('.total-buckets').innerHTML = total + ''
el('.culled-buckets').innerHTML = total - visible + ''
stats = hash.stats()
break
case 'none':
stats = { visible: dots.children.length, culled: 0, total: dots.children.length } as SpatialHashStats
break
}
viewport.dirty = false
el('.visible').innerHTML = stats.visible + ''
el('.culled').innerHTML = stats.culled + ''
el('.total').innerHTML = stats.total + ''
}
window.onload = () => {
pixi()
createDots()
ui()
updateCull()
}