Compare commits

...

10 commits

Author SHA1 Message Date
Norman Köhring
bc569f1c16 more star info, spectral color display 2023-01-28 01:59:11 +01:00
Norman Köhring
30d64a2942 raise visibility with a lot of stars 2023-01-25 11:53:01 +01:00
Norman Köhring
4549aeaf16 info display 2023-01-23 18:31:00 +01:00
Norman Köhring
38f9c76c9d verbose star types 2023-01-23 17:38:58 +01:00
Norman Köhring
748a2839c1 remove obsolete imports 2023-01-23 16:48:11 +01:00
Norman Köhring
f0928994a5 add all the stars, better visibility 2023-01-23 16:15:23 +01:00
Norman Köhring
bf1b43cf4c clickable labels 2023-01-23 12:27:27 +01:00
Norman Köhring
890c86e654 floating labels 2023-01-23 12:09:52 +01:00
Norman Köhring
4a671e8019 always select closest to pointer 2023-01-22 22:50:27 +01:00
Norman Köhring
3bac2bd865 adds key controls, star info and more stars 2023-01-22 21:49:34 +01:00
13 changed files with 4112 additions and 259 deletions

View file

@ -11,29 +11,136 @@
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;
}
#info {
position: absolute;
top: 1em;
right: 1em;
text-align: right;
top: 0;
left: 0;
width: 22rem;
height: 100vh;
background: #3368;
backdrop-filter: blur(4px);
border-right: 2px solid #336;
transform: translate(0, 0);
transition: transform .2s ease-out;
}
#info > button {
margin: 0 .1em;
border: 1px solid #DDD;
border-radius: .5em;
#info.hidden {
transform: translate(-22rem, 0);
}
#info > button.highlighted {
border-color: yellow;
#info > header {
position: relative;
height: 8rem;
margin: 1em 0 0 0;
overflow: hidden;
}
#info > header h1 {
font-size: 1.5rem;
margin: 6rem 0 0 0;
text-align: center;
mix-blend-mode: difference;
}
#info > header > .title-bg {
position: absolute;
top: 0;
left: -50%;
width: 44rem;
height: 44rem;
border-radius: 100%;
}
#info > p {
margin: 1em;
}
label {
position: fixed;
top: 0;
left: 0;
margin-top: -.5em;
margin-left: .5em;
font-weight: bold;
color: white;
transform: translate(0, 0);
background: #0008;
line-height: 1;
cursor: pointer;
transition: opacity .2s ease;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
label.highlighted {
color: yellow;
z-index: 10000;
}
label.dimmed {
opacity: .3;
z-index: 0;
}
.spectral-class-o {
background: rgb(222,221,237);
background: radial-gradient(circle, #dedded 50%, #3774c1 90%);
}
.spectral-class-b {
background: rgb(222,221,237);
background: radial-gradient(circle, #dedded 50%, #91b3df 90%);
}
.spectral-class-a {
background: rgb(222,221,237);
background: radial-gradient(circle, #dedded 50%, #c7d2e6 90%);
}
.spectral-class-f {
background: rgb(222,221,237);
background: radial-gradient(circle, #dedded 50%, #edecf4 90%);
}
.spectral-class-g, .spectral-class-d {
background: rgb(222,221,237);
background: radial-gradient(circle, #dedded 40%, #fbebaf 90%);
}
.spectral-class-k {
background: rgb(222,221,237);
background: radial-gradient(circle, #dedded 40%, #f9cf97 90%);
}
.spectral-class-m {
background: rgb(222,221,237);
background: radial-gradient(circle, #dedded 40%, #f6936b 90%);
}
.spectral-class-l, .spectral-class-t, .spectral-class-y {
background: rgb(222,221,237);
background: radial-gradient(circle, #f6936b 40%, #532d1e 90%);
}
</style>
</head>
<body>
<div id="info"></div>
<div id="info">
<header>
<div class="title-bg spectral-class-{{ spectral-class }}">
<h1>{{ name }}</h1>
</div>
</header>
<p>
<span>Star Type:<span>
<strong>{{ type }}</strong>
</p>
<p>
<span>Spectral Type:<span>
<strong>{{ spectral }}</strong>
</p>
<p>
<span>Additional Types:<span>
<ul>
{{ all-types }}
</ul>
</p>
<p>
<span>Distance:<span>
<strong>{{ distance }}</strong>
</p>
</div>
<div id="labels"></div>
<script type="module" src="/src/main.ts"></script>
</body>
</html>

3693
input.csv Normal file

File diff suppressed because it is too large Load diff

11
input.example.csv Normal file
View file

@ -0,0 +1,11 @@
1 ;* 61 Cyg B;Eruptive Variable ;Rotating Variable,Eruptive Variable,High Proper Motion Star,Variable Star,High Proper Motion Star,Eruptive Variable,Double or Multiple Star,Star,Star,Infra-Red Source,X-ray Source ;082.3171310715589 -05.8262372280102;K7V ;3.4964
2 ;* 61 Cyg A;BY Dra Variable ;Rotating Variable,BY Dra Variable,High Proper Motion Star,Variable Star,High Proper Motion Star,Eruptive Variable,Double or Multiple Star,Star,Star,Infra-Red Source,UV-emission Source,X-ray Source,X-ray Source ;082.3197403091576 -05.8181041502094;K5V ;3.4965
3 ;GAT 1383 ;High Proper Motion Star;High Proper Motion Star,High Proper Motion Star,High Proper Motion Star,Star,Infra-Red Source ;085.6450087237446 -07.3769487234860;M5.5V ;14.5094
4 ;BD+40 4631;High Proper Motion Star;High Proper Motion Star,High Proper Motion Star,Star,Star,Infra-Red Source ;089.4905214280362 -08.8259946066449;K8V ;19.837
5 ;V* EV Lac ;Eruptive Variable ;High Proper Motion Star,High Proper Motion Star,Eruptive Variable,High Proper Motion Star,Double or Multiple Star,Variable Star,High Proper Motion Star,Star,Star,Infra-Red Source,Radio Source,UV-emission Source,X-ray Source,X-ray Source,X-ray Source;100.6067176270883 -13.0693645782006;M4.0Ve;5.0515
6 ;G 216-43 ;High Proper Motion Star;High Proper Motion Star,High Proper Motion Star,Double or Multiple Star,Star,Infra-Red Source ;107.6831520259098 -14.9929080409418;M4.5 ;35.6304
7 ;BD+45 4378;Spectroscopic Binary ;High Proper Motion Star,High Proper Motion Star,High Proper Motion Star,Spectroscopic Binary,Variable Star,High Proper Motion Star,Spectroscopic Binary,Double or Multiple Star,Star,Infra-Red Source ;113.6133003656408 -15.1866691700906;K7V ;17.1213
8 ;LP 149-14 ;High Proper Motion Star;High Proper Motion Star,High Proper Motion Star,High Proper Motion Star,Star,Infra-Red Source ;114.0239531748840 -14.2146886796646;M5.0V ;19.189
9 ;HD 38B ;High Proper Motion Star;High Proper Motion Star,Double or Multiple Star,High Proper Motion Star,Star,Infra-Red Source ;114.6501340218375 -16.3243260935338;M0.5V ;11.518
10;HD 38A ;Spectroscopic Binary ;High Proper Motion Star,Double or Multiple Star,Spectroscopic Binary,High Proper Motion Star,Star,Infra-Red Source ;114.6505214717521 -16.3226459980874;K6V ;11.5207
11;BD+44 4548;High Proper Motion Star;High Proper Motion Star,High Proper Motion Star,High Proper Motion Star,High Proper Motion Star,Double or Multiple Star,Star,Infra-Red Source,UV-emission Source,UV-emission Source ;114.5559641502924 -16.3315501979801;M2Ve ;11.5035
1 1 * 61 Cyg B Eruptive Variable Rotating Variable,Eruptive Variable,High Proper Motion Star,Variable Star,High Proper Motion Star,Eruptive Variable,Double or Multiple Star,Star,Star,Infra-Red Source,X-ray Source 082.3171310715589 -05.8262372280102 K7V 3.4964
2 2 * 61 Cyg A BY Dra Variable Rotating Variable,BY Dra Variable,High Proper Motion Star,Variable Star,High Proper Motion Star,Eruptive Variable,Double or Multiple Star,Star,Star,Infra-Red Source,UV-emission Source,X-ray Source,X-ray Source 082.3197403091576 -05.8181041502094 K5V 3.4965
3 3 GAT 1383 High Proper Motion Star High Proper Motion Star,High Proper Motion Star,High Proper Motion Star,Star,Infra-Red Source 085.6450087237446 -07.3769487234860 M5.5V 14.5094
4 4 BD+40 4631 High Proper Motion Star High Proper Motion Star,High Proper Motion Star,Star,Star,Infra-Red Source 089.4905214280362 -08.8259946066449 K8V 19.837
5 5 V* EV Lac Eruptive Variable High Proper Motion Star,High Proper Motion Star,Eruptive Variable,High Proper Motion Star,Double or Multiple Star,Variable Star,High Proper Motion Star,Star,Star,Infra-Red Source,Radio Source,UV-emission Source,X-ray Source,X-ray Source,X-ray Source 100.6067176270883 -13.0693645782006 M4.0Ve 5.0515
6 6 G 216-43 High Proper Motion Star High Proper Motion Star,High Proper Motion Star,Double or Multiple Star,Star,Infra-Red Source 107.6831520259098 -14.9929080409418 M4.5 35.6304
7 7 BD+45 4378 Spectroscopic Binary High Proper Motion Star,High Proper Motion Star,High Proper Motion Star,Spectroscopic Binary,Variable Star,High Proper Motion Star,Spectroscopic Binary,Double or Multiple Star,Star,Infra-Red Source 113.6133003656408 -15.1866691700906 K7V 17.1213
8 8 LP 149-14 High Proper Motion Star High Proper Motion Star,High Proper Motion Star,High Proper Motion Star,Star,Infra-Red Source 114.0239531748840 -14.2146886796646 M5.0V 19.189
9 9 HD 38B High Proper Motion Star High Proper Motion Star,Double or Multiple Star,High Proper Motion Star,Star,Infra-Red Source 114.6501340218375 -16.3243260935338 M0.5V 11.518
10 10 HD 38A Spectroscopic Binary High Proper Motion Star,Double or Multiple Star,Spectroscopic Binary,High Proper Motion Star,Star,Infra-Red Source 114.6505214717521 -16.3226459980874 K6V 11.5207
11 11 BD+44 4548 High Proper Motion Star High Proper Motion Star,High Proper Motion Star,High Proper Motion Star,High Proper Motion Star,Double or Multiple Star,Star,Infra-Red Source,UV-emission Source,UV-emission Source 114.5559641502924 -16.3315501979801 M2Ve 11.5035

View file

@ -15,6 +15,7 @@
"@types/three": "^0.148.0",
"@typescript-eslint/eslint-plugin": "^5.48.2",
"@typescript-eslint/parser": "^5.48.2",
"csv-parse": "^5.3.3",
"eslint": "^8.32.0",
"eslint-config-prettier": "^8.6.0",
"eslint-plugin-prettier": "^4.2.1",

41
simbad4csv2json.js Normal file
View file

@ -0,0 +1,41 @@
import { parse } from 'csv-parse'
import { readFile, writeFile } from 'fs/promises'
;(async () => {
const content = await readFile('input.csv')
const records = []
const columns = ['id', 'name', 'type', 'allTypes', 'coords', 'spectral', 'distance']
const parser = parse(content, {
bom: true,
delimiter: ';',
trim: true,
columns,
})
parser.on('readable', () => {
let record
while ((record = parser.read()) !== null) {
// objects without spectral class are probably not stars
if (record.spectral === '~') continue
const [phi, theta] = record.coords.split(' ').map((n) => parseFloat(n))
// lots of duplicates in the allTypes field...
const allTypes = [...new Set(record.allTypes.split(','))]
records.push({
id: parseInt(record.id),
name: record.name,
type: record.type,
spectral: record.spectral,
radius: parseFloat(record.distance),
phi,
theta,
allTypes,
})
}
})
parser.on('end', async () => {
await writeFile('output.json', JSON.stringify(records))
console.log('wrote', records.length, 'records to output.json')
})
})()

View file

@ -1,153 +0,0 @@
<!DOCTYPE html>
<!-- https://discourse.threejs.org/t/line-segment-coordinates/4358/3 -->
<!-- http://jsfiddle.net/prisoner849/go0dfwo5/ -->
<head>
<title> Interaction with Points </title>
<meta charset="utf-8" />
<style>
body {
overflow: hidden;
margin: 0;
}
</style>
</head>
<body>
</body>
<script src="../js/three.min.97.js"></script>
<script src="../js/OrbitControls.js"></script>
<script> // @author prisoner849
Object.assign(THREE.PlaneBufferGeometry.prototype, {
toGrid: function() {
let segmentsX = this.parameters.widthSegments || 1;
let segmentsY = this.parameters.heightSegments || 1;
let indices = [];
for (let i = 0; i < segmentsY + 1; i++) {
let index11 = 0;
let index12 = 0;
for (let j = 0; j < segmentsX; j++) {
index11 = (segmentsX + 1) * i + j;
index12 = index11 + 1;
let index21 = index11;
let index22 = index11 + (segmentsX + 1);
indices.push(index11, index12);
if (index22 < ((segmentsX + 1) * (segmentsY + 1) - 1)) {
indices.push(index21, index22);
}
}
if ((index12 + segmentsX + 1) <= ((segmentsX + 1) * (segmentsY + 1) - 1)) {
indices.push(index12, index12 + segmentsX + 1);
}
}
this.setIndex(indices);
return this;
}
});
</script>
<script>
//@author prisoner849
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 1, 1000);
camera.position.set(1.25, 7, 7);
camera.lookAt(scene.position);
var renderer = new THREE.WebGLRenderer({antialias: true});
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
var controls = new THREE.OrbitControls(camera, renderer.domElement);
var geometry = new THREE.PlaneBufferGeometry(10, 10, 10, 10).toGrid();
geometry.rotateX(-Math.PI * 0.5);
var plane = new THREE.LineSegments(geometry, new THREE.MeshBasicMaterial({
color: "red"
}));
scene.add(plane);
var points = new THREE.Points(geometry, new THREE.PointsMaterial({
size: 0.25,
color: "yellow"
}));
scene.add(points);
var raycaster = new THREE.Raycaster();
raycaster.params.Points.threshold = 0.25;
var mouse = new THREE.Vector2();
var intersects = null;
var plane = new THREE.Plane();
var planeNormal = new THREE.Vector3();
var currentIndex = null;
var planePoint = new THREE.Vector3();
var dragging = false;
window.addEventListener("mousedown", mouseDown, false);
window.addEventListener("mousemove", mouseMove, false);
window.addEventListener("mouseup", mouseUp, false);
function mouseDown(event) {
setRaycaster(event);
getIndex();
dragging = true;
}
function mouseMove(event) {
if (dragging && currentIndex !== null) {
setRaycaster(event);
raycaster.ray.intersectPlane(plane, planePoint);
geometry.attributes.position.setXYZ(currentIndex, planePoint.x, planePoint.y, planePoint.z);
geometry.attributes.position.needsUpdate = true;
}
}
function mouseUp(event) {
dragging = false;
currentIndex = null;
controlsEnabled(true);
}
function getIndex() {
intersects = raycaster.intersectObject(points);
if (intersects.length === 0) {
currentIndex = null;
return;
}
controlsEnabled(false);
currentIndex = intersects[0].index;
setPlane(intersects[0].point);
}
function setPlane(point) {
planeNormal.subVectors(camera.position, point).normalize();
plane.setFromNormalAndCoplanarPoint(planeNormal, point);
}
function setRaycaster(event) {
getMouse(event);
raycaster.setFromCamera(mouse, camera);
}
function getMouse(event) {
mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
}
function controlsEnabled(state){
controls.enableZoom = state;
controls.enableRotate = state;
controls.enablePan = state;
}
render();
function render() {
requestAnimationFrame(render);
renderer.render(scene, camera);
}
</script>
</html>

33
src/info-display.ts Normal file
View file

@ -0,0 +1,33 @@
import type { StarData } from './stars'
export class InfoDisplay {
private container = document.getElementById('info')!
private template = this.container.innerHTML
constructor() {}
render(data: StarData) {
const name = data.name.replace(/^NAME /, '')
const ly = Math.round(data.radius * 3.2615637 * 100) / 100
const distance = `${data.radius} pc / ${ly} ly`
const allTypes = '<li>' + data.allTypes.join('</li><li>') + '</li>'
const html = this.template
.replace('{{ name }}', name)
.replace('{{ type }}', data.type)
.replace('{{ spectral }}', data.spectral)
.replace('{{ spectral-class }}', data.spectral[0].toLowerCase())
.replace('{{ all-types }}', allTypes)
.replace('{{ distance }}', distance)
this.container.innerHTML = html
}
show() {
this.container.classList.remove('hidden')
}
hide() {
this.container.classList.add('hidden')
}
}

View file

@ -9,13 +9,15 @@ import {
} from 'three'
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js'
import { planeGeometry } from './plane'
import { renderStars } from './stars'
import { renderStars, Star } from './stars'
import { InfoDisplay } from './info-display'
function init() {
async function init() {
const w = window.innerWidth
const h = window.innerHeight
const radius = 50
const radius = 5
const infoDisplay = new InfoDisplay()
infoDisplay.hide()
const renderer = new WebGLRenderer({ antialias: true })
renderer.setSize(w, h)
@ -24,23 +26,25 @@ function init() {
const camera = new PerspectiveCamera(30, w / h, 0.01, 1001)
camera.position.x = 0
camera.position.y = radius
camera.position.z = radius * 2
camera.position.z = radius * 5
const controls = new OrbitControls(camera, renderer.domElement)
controls.enableZoom = true
controls.enableRotate = true
controls.enablePan = false
controls.maxDistance = radius * 2.5
controls.enablePan = true
controls.maxDistance = radius * 5
controls.minDistance = 0.1
controls.listenToKeyEvents(window)
const plane = planeGeometry(radius)
const stars = renderStars(radius)
const stars = await renderStars(radius)
scene.add(stars)
scene.add(plane)
const pointer = new Vector2()
const raycaster = new Raycaster()
const intersections: Intersection<Object3D<Event>>[] = []
window.addEventListener('resize', () => {
camera.aspect = window.innerWidth / window.innerHeight
@ -52,18 +56,67 @@ function init() {
pointer.x = (event.clientX / window.innerWidth) * 2 - 1
pointer.y = -(event.clientY / window.innerHeight) * 2 + 1
})
document.addEventListener('click', () => {
for (let star of stars.children as Star[]) {
star.highlighted = false
}
const intersections: Intersection<Object3D<Event>>[] = []
let closest: Intersection<Object3D<Event>> | null = null
for (let i of intersections) {
if (i.distanceToRay === undefined) continue // ignore Lines
if (closest === null || i.distanceToRay < (closest.distanceToRay ?? 0)) {
closest = i
}
}
if (closest === null) {
infoDisplay.hide()
return
}
const star = closest.object.parent as Star
if (star.isStar) {
star.highlighted = true
infoDisplay.render(star.starData)
infoDisplay.show()
}
})
for (let child of stars.children as Star[]) {
child.labelEl.addEventListener('click', (event) => {
event.stopPropagation()
for (let star of stars.children as Star[]) {
star.highlighted = false
}
const star = child as Star
star.highlighted = true
infoDisplay.render(star.starData)
infoDisplay.show()
})
}
renderer.setAnimationLoop(() => {
raycaster.setFromCamera(pointer, camera)
intersections.length = 0
raycaster.intersectObject(stars, false, intersections)
if (intersections.length) console.log(intersections)
raycaster.intersectObject(stars, true, intersections)
renderer.render(scene, camera)
const distanceToPlane = camera.position.distanceTo(plane.children[0].position)
// updating HTML space and the stars' color
// Attention: This has to happen after the render call, to avoid flickering
for (let star of stars.children as Star[]) {
star.setLabelPos(camera, w, h)
star.dimmed = camera.position.distanceTo(star.coords) > distanceToPlane
}
})
document.body.appendChild(renderer.domElement)
document.body.prepend(renderer.domElement)
}
init()

View file

@ -1,11 +1,32 @@
import { BufferGeometry, Group, Line, LineBasicMaterial, Shape, Vector3 } from 'three'
import {
BufferGeometry,
Group,
Line,
LineBasicMaterial,
MeshBasicMaterial,
Shape,
ShapeGeometry,
Mesh,
Vector3,
DoubleSide,
} from 'three'
export function planeGeometry(radius: number, n = 5) {
const material = new LineBasicMaterial({
color: 0xa0a0a0,
const lineMaterial = new LineBasicMaterial({ color: 0x205020 })
const shapeMaterial = new MeshBasicMaterial({
color: 0x0,
transparent: true,
opacity: 0.8,
side: DoubleSide,
})
const plane = new Group()
const shape = new Shape()
shape.moveTo(0, 0).absarc(0, 0, radius, 0, Math.PI * 2, false)
const shapeGeometry = new ShapeGeometry(shape)
shapeGeometry.rotateX(Math.PI / 2)
plane.add(new Mesh(shapeGeometry, shapeMaterial))
const xLine = new BufferGeometry().setFromPoints([
new Vector3(-radius, 0, 0),
new Vector3(radius, 0, 0),
@ -21,9 +42,9 @@ export function planeGeometry(radius: number, n = 5) {
new Vector3(0, 0, radius),
])
plane.add(new Line(xLine, material))
plane.add(new Line(yLine, material))
plane.add(new Line(zLine, material))
plane.add(new Line(xLine, lineMaterial))
plane.add(new Line(yLine, lineMaterial))
plane.add(new Line(zLine, lineMaterial))
const step = Math.round(radius / n)
@ -31,7 +52,7 @@ export function planeGeometry(radius: number, n = 5) {
const shape = new Shape().moveTo(0, r).absarc(0, 0, r, 0, Math.PI * 2, false)
shape.autoClose = true
const geometry = new BufferGeometry().setFromPoints(shape.getPoints())
const line = new Line(geometry, material)
const line = new Line(geometry, lineMaterial)
line.rotateX(Math.PI / 2)
plane.add(line)
}

1
src/stars.json Normal file

File diff suppressed because one or more lines are too long

View file

@ -8,80 +8,162 @@ import {
LineBasicMaterial,
Vector3,
Spherical,
Camera,
} from 'three'
import data from './testdata.json'
export interface StarData {
id: number
name: string
type: string
allTypes: string[]
spectral: string
radius: number
phi: number
theta: number
}
class Star extends Group {
private tangentialCoords = new Vector3()
private cartesianCoords = new Vector3()
private sphericalCoords = new Spherical()
export class Star extends Group {
public isStar = true
public starData: StarData
public labelEl = document.createElement('label')
public coords = new Vector3()
private isHighlighted = false
private isDimmed = false
private normalPointSize: number
private highlightedPointSize: number
private lineMaterial = new LineBasicMaterial({ color: 0xffffff })
private pointMaterial = new PointsMaterial({ size: 1, vertexColors: true })
private pointGeometry = new BufferGeometry()
private whiteColor = new Float32BufferAttribute([255, 255, 255], 3)
private grayColor = new Float32BufferAttribute([51, 51, 51], 3)
private yellowColor = new Float32BufferAttribute([255, 255, 0], 3)
private poleLine = new Line(new BufferGeometry(), this.lineMaterial)
private point: Points<BufferGeometry, PointsMaterial>
private pole: Line<BufferGeometry, LineBasicMaterial>
constructor(star: StarData) {
constructor(starData: StarData, maxRadius: number) {
super()
const { radius, phi, theta } = star
this.sphericalCoords.set(radius, phi, theta)
this.cartesianCoords.setFromSpherical(this.sphericalCoords)
this.starData = starData
this.normalPointSize = maxRadius / 25
this.highlightedPointSize = this.normalPointSize * 1.5
this.pointMaterial.setValues({ size: this.normalPointSize })
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
this.pole = new Line(new BufferGeometry(), this.lineMaterial)
this.pole.geometry.setFromPoints([this.coords, tangentialCoords])
this.add(this.pole)
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')!
// stars with a proper name start with NAME, so lets strip that away
this.labelEl.innerText = starData.name.replace(/^NAME /, '')
container.appendChild(this.labelEl)
}
setHighlight(isHighlight = true) {
this.pointGeometry.setAttribute('color', isHighlight ? this.yellowColor : this.whiteColor)
private setAttributes() {
if (this.isDimmed && !this.isHighlighted) {
this.pointMaterial.setValues({ size: this.normalPointSize })
this.pointGeometry.setAttribute('color', this.grayColor)
this.lineMaterial.setValues({ color: 0x333333 })
this.labelEl.classList.remove('highlighted')
this.labelEl.classList.add('dimmed')
this.labelEl.style.zIndex = '0' // dimmed always in the back
} else if (this.isHighlighted) {
this.pointGeometry.setAttribute('color', this.yellowColor)
this.pointMaterial.setValues({ size: this.highlightedPointSize })
this.lineMaterial.setValues({ color: 0xffff00 })
this.labelEl.classList.remove('dimmed')
this.labelEl.classList.add('highlighted')
this.labelEl.style.zIndex = '10000' // highlights always on top
} else {
this.pointGeometry.setAttribute('color', this.whiteColor)
this.pointMaterial.setValues({ size: this.normalPointSize })
this.lineMaterial.setValues({ color: 0xffffff })
this.labelEl.classList.remove('highlighted')
this.labelEl.classList.remove('dimmed')
}
}
public get highlighted() {
return this.isHighlighted
}
public set highlighted(isHighlighted) {
this.isHighlighted = isHighlighted
this.setAttributes()
}
public get dimmed() {
return this.isDimmed
}
public set dimmed(isDimmed) {
this.isDimmed = isDimmed
this.setAttributes()
}
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)`
if (!this.isDimmed && !this.isHighlighted) {
const zIndex = `${10000000 - Math.round(pos.z * 10000000)}` // ridiculous
this.labelEl.style.zIndex = zIndex
}
}
}
export function renderStars(maxRadius: number) {
export async function renderStars(maxRadius: number) {
const group = new Group()
const infoEl = document.getElementById('info')!
const data: StarData[] = (await import('./stars.json')).default
const sol = new Star(
{
id: 0,
name: 'Sol',
type: 'White Dwarf',
allTypes: ['White Dwarf', 'Star'],
spectral: 'G2V',
radius: 0.0,
phi: 0.0,
theta: 0.0,
},
maxRadius
)
group.add(sol) // lets not forget our beloved sun
data.forEach((starData) => {
if (starData.radius > maxRadius) return
const star = new Star(starData)
const star = new Star(starData, maxRadius)
group.add(star)
let highlighted = false
const btnEl = document.createElement('button')
btnEl.addEventListener('click', () => {
highlighted = !highlighted
star.setHighlight(highlighted)
btnEl.classList.toggle('highlighted', highlighted)
})
btnEl.innerText = starData.name
infoEl.appendChild(btnEl)
})
return group

View file

@ -1,42 +0,0 @@
[
{
"name": "* 61 Cyg B",
"type": "Eruptive Variable",
"theta": 82.3171310715589,
"phi": -5.8262372280102,
"spectral": "K7V",
"radius": 3.4964
},
{
"name": "* 61 Cyg A",
"type": "BY Dra Variable",
"theta": 82.3197403091576,
"phi": -5.8181041502094,
"spectral": "K5V",
"radius": 3.4965
},
{
"name": "GAT 1383",
"type": "High Proper Motion Star",
"theta": 85.6450087237446,
"phi": -7.3769487234860,
"spectral": "M5.5V",
"radius": 14.5094
},
{
"name": "BD+40 4631",
"type": "High Proper Motion Star",
"theta": 89.4905214280362,
"phi": -8.8259946066449,
"spectral": "K8V",
"radius": 19.837
},
{
"name": "V* EV Lac",
"type": "Eruptive Variable",
"theta": 100.6067176270883,
"phi": -13.0693645782006,
"spectral": "M4.0Ve",
"radius": 5.0515
}
]

View file

@ -383,6 +383,11 @@ cross-spawn@^7.0.2:
shebang-command "^2.0.0"
which "^2.0.1"
csv-parse@^5.3.3:
version "5.3.3"
resolved "https://registry.yarnpkg.com/csv-parse/-/csv-parse-5.3.3.tgz#3b75d2279e2edb550cbc54c65b25cbbf3d0033ad"
integrity sha512-kEWkAPleNEdhFNkHQpFHu9RYPogsFj3dx6bCxL847fsiLgidzWg0z/O0B1kVWMJUc5ky64zGp18LX2T3DQrOfw==
debug@^4.1.1, debug@^4.3.2, debug@^4.3.4:
version "4.3.4"
resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865"