diff options
-rw-r--r-- | derelict.js | 153 |
1 files changed, 94 insertions, 59 deletions
diff --git a/derelict.js b/derelict.js index e7f1f76..4be97ef 100644 --- a/derelict.js +++ b/derelict.js @@ -5,17 +5,35 @@ import { CSS2DRenderer, CSS2DObject } from 'https://unpkg.com/three@0.126.1/exam const SCALE = 10000 const METERS_IN_AU = 149597871000 const EXPIRY = 1000 * 60 * 10 +const GRID_EXTENT = 100 * SCALE + +class Timeline { + start + finish + current = 0 + + simple_list = [] + + register(wreck) { + // TODO: Consider expiration right away + if (this.start === undefined || wreck.timestamp < this.start) + this.start = wreck.timestamp + if (this.finish === undefined || wreck.timestamp > this.finish) + this.finish = wreck.timestamp + // TODO: Implement more reliable and optimized data structure for timeline. + this.simple_list.push(wreck) + } + + setTo(time) {} // TODO: Implement + moveForward(time) {} // TODO: Implement + moveBackward(time) {} // TODO: Implement +} -class SkirmishGrid { +class Grid { scene = new THREE.Scene() - helper = new THREE.GridHelper(50, 50, 0x303030, 0x222222) - camera - controls - renderer - renderer2d - active = false - - constructor({container, renderer, renderer2d}) { + wrecks = [] + + constructor(wreck, {container, renderer, renderer2d}) { this.container = container this.renderer = renderer this.renderer2d = renderer2d @@ -24,17 +42,32 @@ class SkirmishGrid { this.camera = new THREE.PerspectiveCamera(80, aspect, 0.1, 1000) this.camera.position.set(8, 8, 8) this.camera.lookAt(0, 0, 0) + this.controls = new OrbitControls(this.camera, this.renderer2d.domElement) this.controls.minDistance = 2 this.controls.maxDistance = 30 this.controls.enableDamping = true this.controls.dampingFactor = 0.4 - this.add(this.helper) + const helper = new THREE.GridHelper(50, 50, 0x303030, 0x222222) + this.scene.add(helper) + + this.positionOfReference = wreck.killmail.victim.position + this.locationOfReference = wreck.location + this.scene.add(wreck.object) + this.wrecks.push(wreck) } - add(obj) { - this.scene.add(obj) + tryAdding(wreck) { + if (wreck.location == this.locationOfReference) { + const distance = wreck.killmail.victim.position.distanceTo(this.positionOfReference) + if (distance < GRID_EXTENT) { + this.scene.add(wreck.object) + this.wrecks.push(wreck) + return true + } + } + return false } draw() { @@ -63,14 +96,41 @@ class SkirmishGrid { this.active = false this.controls.enabled = false } + + recenter() { + const center = averagePosition(this.wrecks.map(wreck => wreck.killmail.victim.position)) + this.wrecks.forEach(wreck => wreck.object.position.sub(center).divideScalar(SCALE)) // TODO: rescale + } +} + +class Battle { + timeline = new Timeline() + grids = [] + + constructor(grid_initializer) { + this.grid_initializer = grid_initializer + } + + add(wreck) { + this.timeline.register(wreck) + let added = false + for (const grid of this.grids) { + added = grid.tryAdding(wreck) + if (added) break + } + if (!added) { + const grid = new Grid(wreck, this.grid_initializer) + this.grids.push(grid) + } + } } class Wreck { domElement = document.createElement('div') - point = new THREE.Object3D() + object = new THREE.Object3D() killmail - constructor({killmail, grid, battle}) { + constructor({killmail, battle}) { const labelElement = document.createElement('div') const object2d = new CSS2DObject(this.domElement) const ownerId = "alliance_id" in killmail.victim ? killmail.victim.alliance_id : killmail.victim.corporation_id @@ -88,16 +148,12 @@ class Wreck { this.killmail = killmail this.timestamp = Date.parse(killmail.killmail_time) - this.point.position.copy(killmail.victim.position) - this.point.add(object2d) + this.object.position.copy(killmail.victim.position) + this.object.add(object2d) this.domElement.classList.add('wreck', team) this.domElement.appendChild(labelElement) this.domElement.onclick = () => window.open(`https://zkillboard.com/kill/${this.killmail.killmail_id}/`) - this.domElement.oncontextmenu = () => { - grid.controls.target.copy(this.point.position) - grid.controls.update() - } } toggleKilled(timestamp) { @@ -122,64 +178,43 @@ function vec3FromXYZ({x, y, z}) { return new THREE.Vector3(x, y, z) } -function splitKillmails(clusters, killmail) { - const vec3 = vec3FromXYZ(killmail.victim.position) - const found = clusters.find(cluster => cluster[0].victim.position.distanceTo(vec3) < 100 * SCALE) - killmail.victim.position = vec3 - if (found === undefined) { - clusters.push([killmail]) - } - else { - found.push(killmail) - } - return clusters -} - function averagePosition(positions) { const sum = positions.reduce((sum, pos) => sum.add(pos), new THREE.Vector3()) return sum.divideScalar(positions.length) } function processBattle({battle, renderer, renderer2d, container, toolbar, skybox}) { - const clusters = battle.killmails.reduce(splitKillmails, new Array()) + const battle_obj = new Battle({container, renderer, renderer2d}) + const wrecks = battle.killmails.map(killmail => new Wreck({killmail, battle})) + wrecks.forEach(wreck => wreck.killmail.victim.position = vec3FromXYZ(wreck.killmail.victim.position)) + wrecks.forEach(wreck => battle_obj.add(wreck)) + battle_obj.grids.forEach(grid => window.addEventListener('resize', () => grid.onresize())) + battle_obj.grids.forEach(grid => grid.recenter()) - let elements = [] - let grids = [] const gridSelection = document.getElementById("grid") - clusters.forEach(cluster => { + battle_obj.grids.forEach(grid => { const option = document.createElement("option") const origin = new THREE.Vector3() - origin.copy(cluster[0].victim.position) + origin.copy(grid.wrecks[0].killmail.victim.position) origin.divideScalar(METERS_IN_AU) option.text = `${origin.x.toFixed(1)} AU, ${origin.y.toFixed(1)} AU, ${origin.z.toFixed(1)} AU` - gridSelection.options.add(option) - - const center = averagePosition(cluster.map(km => km.victim.position)) - const grid = new SkirmishGrid({container, renderer, renderer2d}) - cluster.forEach(killmail => { - killmail.victim.position.sub(center).divideScalar(SCALE) - const wreck = new Wreck({killmail, grid, battle}) - grid.add(wreck.point) - elements.push(wreck) - }) - grids.push(grid) - window.addEventListener('resize', () => grid.onresize()) + gridSelection.appendChild(option) }) gridSelection.oninput = () => { - grids.forEach(g => g.disable()) - grids[gridSelection.selectedIndex].enable() - grids[gridSelection.selectedIndex].draw() + battle_obj.grids.forEach(g => g.disable()) + battle_obj.grids.at(gridSelection.selectedIndex).enable() + battle_obj.grids.at(gridSelection.selectedIndex).draw() } gridSelection.oninput() skybox.then(skybox => { const rt = new THREE.WebGLCubeRenderTarget(skybox.image.height) rt.fromEquirectangularTexture(renderer, skybox) - grids.forEach(g => g.scene.background = rt) + battle_obj.grids.forEach(g => g.scene.background = rt) }) - const start = Date.parse(battle.started_at) - EXPIRY - const end = Date.parse(battle.ended_at) + EXPIRY + const start = battle_obj.timeline.start - EXPIRY + const end = battle_obj.timeline.finish + EXPIRY const bottomBar = document.createElement("div") const seekbar = document.createElement("div") @@ -227,7 +262,7 @@ function processBattle({battle, renderer, renderer2d, container, toolbar, skybox else { current += 5000 } - elements.forEach(item => item.toggleKilled(current)) + battle_obj.timeline.simple_list.forEach(item => item.toggleKilled(current)) const norm = (current - start) / (end - start) progress.style.width = `${norm * 100}%` writeTime(current) @@ -245,12 +280,12 @@ function processBattle({battle, renderer, renderer2d, container, toolbar, skybox const timestamp = start + (end - start) * lim if (e.clientX != 0 && current != timestamp) { current = timestamp - elements.forEach(item => item.toggleKilled(timestamp)) + battle_obj.timeline.simple_list.forEach(item => item.toggleKilled(timestamp)) progress.style.width = `${lim * 100}%` writeTime(current) } } - elements.forEach(item => item.toggleKilled(start)) + battle_obj.timeline.simple_list.forEach(item => item.toggleKilled(start)) writeTime(current) seekbar.onclick = seekWithBar |