From 890c86e65480c08a4bd3b116be00143a26ade829 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Norman=20K=C3=B6hring?= Date: Mon, 23 Jan 2023 12:09:52 +0100 Subject: [PATCH] floating labels --- index.html | 16 +++++++++++++++- src/main.ts | 13 ++++++++++--- src/plane.ts | 4 +--- src/stars.ts | 50 ++++++++++++++++++++++++++++++++++++-------------- 4 files changed, 62 insertions(+), 21 deletions(-) diff --git a/index.html b/index.html index 3f14e0f..1aa681d 100644 --- a/index.html +++ b/index.html @@ -11,7 +11,7 @@ display: flex; place-items: center; min-height: 100vh; - font: 16/1.5 sans-serif normal; + font: 16px monospace; color-scheme: light dark; color: #EEE; background-color: #222; @@ -30,10 +30,24 @@ #info > button.highlighted { border-color: yellow; } + label { + position: fixed; + top: 0; + left: 0; + margin-top: -.5em; + margin-left: .5em; + font-weight: bold; + color: white; + pointer-events: none; + transform: translate(0, 0); + background: #0008; + line-height: 1; + }
Click a star to get options.
+
diff --git a/src/main.ts b/src/main.ts index 37cd9a6..17632ab 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,4 +1,3 @@ -import { IncomingMessage } from 'http' import { WebGLRenderer, Scene, @@ -64,7 +63,7 @@ function init() { let closest: Intersection> | null = null for (let i of intersections) { - if (i.distanceToRay === undefined) continue + if (i.distanceToRay === undefined) continue // ignore Lines if (closest === null || i.distanceToRay < (closest.distanceToRay ?? 0)) { closest = i } @@ -86,8 +85,16 @@ function init() { raycaster.intersectObject(stars, true, intersections) renderer.render(scene, camera) + + // update label positions in HTML space + // Attention: This has to happen after the render call, to avoid flickering + for (let star of stars.children) { + ;(star as Star).setLabelPos(camera, w, h) + // set label z-index to distance to make them overlap intuitively + } }) - document.body.appendChild(renderer.domElement) + + document.body.prepend(renderer.domElement) } init() diff --git a/src/plane.ts b/src/plane.ts index b0d92c3..9361630 100644 --- a/src/plane.ts +++ b/src/plane.ts @@ -1,9 +1,7 @@ import { BufferGeometry, Group, Line, LineBasicMaterial, Shape, Vector3 } from 'three' export function planeGeometry(radius: number, n = 5) { - const material = new LineBasicMaterial({ - color: 0x303030, - }) + const material = new LineBasicMaterial({ color: 0x205020 }) const plane = new Group() const xLine = new BufferGeometry().setFromPoints([ diff --git a/src/stars.ts b/src/stars.ts index 71e8e49..684e07f 100644 --- a/src/stars.ts +++ b/src/stars.ts @@ -8,6 +8,7 @@ import { LineBasicMaterial, Vector3, Spherical, + Camera, } from 'three' import data from './testdata.json' @@ -25,9 +26,7 @@ export class Star extends Group { public isStar = true public starData: StarData - private tangentialCoords = new Vector3() - private cartesianCoords = new Vector3() - private sphericalCoords = new Spherical() + private coords = new Vector3() private isHighlighted = false private normalPointSize = 2 @@ -40,30 +39,40 @@ export class Star extends Group { private whiteColor = new Float32BufferAttribute([255, 255, 255], 3) private yellowColor = new Float32BufferAttribute([255, 255, 0], 3) - private poleLine = new Line(new BufferGeometry(), this.lineMaterial) + private labelEl = document.createElement('label') + + private point: Points constructor(starData: StarData) { super() - const { radius, phi, theta } = starData this.starData = starData - this.sphericalCoords.set(radius, phi, theta) - this.cartesianCoords.setFromSpherical(this.sphericalCoords) - const { x, z } = this.cartesianCoords - this.tangentialCoords.set(x, 0, z) + const { radius, phi, theta } = starData + const sphericalCoords = new Spherical(radius, phi, theta) + this.coords.setFromSpherical(sphericalCoords) - this.poleLine.geometry.setFromPoints([this.cartesianCoords, this.tangentialCoords]) + const { x, z } = this.coords + const tangentialCoords = new Vector3(x, 0, z) - this.add(this.poleLine) + // distance indicator / pole + const poleLine = new Line(new BufferGeometry(), this.lineMaterial) + poleLine.geometry.setFromPoints([this.coords, tangentialCoords]) + this.add(poleLine) - const coords = [this.cartesianCoords.x, this.cartesianCoords.y, this.cartesianCoords.z] + // the actual "star" + const coords = [this.coords.x, this.coords.y, this.coords.z] this.pointGeometry.setAttribute('position', new Float32BufferAttribute(coords, 3)) this.pointGeometry.setAttribute('color', this.whiteColor) this.pointGeometry.computeBoundingSphere() - const point = new Points(this.pointGeometry, this.pointMaterial) - this.add(point) + this.point = new Points(this.pointGeometry, this.pointMaterial) + this.add(this.point) + + // label + const container = document.getElementById('labels')! + this.labelEl.innerText = starData.name + container.appendChild(this.labelEl) } private setHighlight(isHighlight = true) { @@ -87,6 +96,19 @@ export class Star extends Group { this.isHighlighted = !this.isHighlighted this.setHighlight(this.isHighlighted) } + + public setLabelPos(camera: Camera, width: number, height: number) { + const dpr = window.devicePixelRatio + const pos = this.coords.clone() + pos.project(camera) + + pos.x = Math.round((0.5 + pos.x / 2) * (width / dpr)) + pos.y = Math.round((0.5 - pos.y / 2) * (height / dpr)) + + this.labelEl.style.transform = `translate(${pos.x}px, ${pos.y}px)` + const zIndex = `${10000000 - Math.round(pos.z * 10000000)}` + this.labelEl.style.zIndex = zIndex + } } export function renderStars(maxRadius: number) {