diff --git a/src/grasp-seasons/3d-models/sun-earth-line.ts b/src/grasp-seasons/3d-models/sun-earth-line.ts index a80d954..e1fa3db 100644 --- a/src/grasp-seasons/3d-models/sun-earth-line.ts +++ b/src/grasp-seasons/3d-models/sun-earth-line.ts @@ -2,14 +2,24 @@ import * as THREE from "three"; import { IModelParams } from "../types"; import * as c from "./constants"; -const POINTER_RADIUS = 200000 * c.SF; -const POINTER_TUBE = 60000 * c.SF; +const HEIGHT = 1; // arrow will be rescaled dynamically based on earth-sun distance that changes through the year +const RADIUS = 1500000 * c.SF; +const LARGE_HEAD_RADIUS = RADIUS * 7; +const LARGE_HEAD_HEIGHT = HEIGHT * 0.2; +const SMALL_HEAD_RADIUS = RADIUS * 5; +const SMALL_HEAD_HEIGHT = HEIGHT * 0.03; +const SMALL_ARROW_SCALE = 0.15; export default class { _arrow!: THREE.Object3D; + _arrowHead!: THREE.Object3D; + _smallArrowHead!: THREE.Object3D; + _arrowMesh!: THREE.Object3D; _earthRadius: number; _pointerMesh!: THREE.Object3D; _refVector: THREE.Vector3; + _earthPos?: THREE.Vector3; + rootObject!: THREE.Object3D; constructor(props: IModelParams) { const simple = props.type === "orbit-view"; @@ -19,17 +29,17 @@ export default class { this._earthRadius = simple ? c.SIMPLE_EARTH_RADIUS : c.EARTH_RADIUS; this._init3DObjects(simple); + this.setOrbitViewStyle(); } setEarthPos(newPos: THREE.Vector3) { - const sunRealRadius = c.SIMPLE_SUN_RADIUS * 1.41; // match edge of the Sun PNG-based sprite + this._earthPos = newPos.clone(); + // match edge of the Sun PNG-based sprite + const sunRealRadius = c.SIMPLE_SUN_RADIUS * (this._smallArrowHead.visible ? 1.1 : 1.41); const len = newPos.length() - this._earthRadius - sunRealRadius; let angleDiff = newPos.angleTo(this._refVector); if (newPos.z < 0) angleDiff *= -1; this.rootObject.rotation.y = angleDiff; - if (this._pointerMesh) { - this._pointerMesh.position.x = -len; - } this._arrow.position.x = -len - sunRealRadius; this._arrow.scale.x = len; } @@ -37,56 +47,55 @@ export default class { _init3DObjects(simple: boolean) { this._arrow = this._initArrow(simple); - const container = new THREE.Object3D(); - container.add(this._arrow); const pivot = new THREE.Object3D(); - pivot.add(container); - - if (!simple) { - this._pointerMesh = this._initPointer(); - container.add(this._pointerMesh); - } + pivot.add(this._arrow); this.rootObject = pivot; } - _initPointer() { - const container = new THREE.Object3D(); - - for (let i = 2; i < 8; i++) { - const radius = POINTER_RADIUS * Math.pow(i, 1.5); - const material = new THREE.MeshPhongMaterial({ color: c.SUN_COLOR, transparent: true, opacity: 1 - i * 0.125 }); - const geometry = new THREE.TorusGeometry(radius, POINTER_TUBE, 6, 16 * i); - const mesh = new THREE.Mesh(geometry, material); - mesh.rotation.y = Math.PI * 0.5; - // Based on circle equation: x^2 + y^2 = r^2 - mesh.position.x = Math.sqrt(Math.pow(this._earthRadius, 2) - Math.pow(radius, 2)) - this._earthRadius; - container.add(mesh); - } - - return container; - } - _initArrow(simple: boolean) { - const HEIGHT = 1; // arrow will be rescaled dynamically based on earth-sun distance that changes through the year - const RADIUS = simple ? 1500000 * c.SF : 100000 * c.SF; - const HEAD_RADIUS = RADIUS * (simple ? 7 : 2); - const HEAD_HEIGHT = HEIGHT * 0.2; const geometry = new THREE.CylinderGeometry(RADIUS, RADIUS, HEIGHT, 32); - const material = new THREE.MeshPhongMaterial({ color: 0xff0000, emissive: c.SUN_COLOR }); - const mesh = new THREE.Mesh(geometry, material); + const material = new THREE.MeshPhongMaterial({ color: c.SUN_COLOR, emissive: c.SUN_COLOR }); + this._arrowMesh = new THREE.Mesh(geometry, material); + + const arrowHeadGeo = new THREE.CylinderGeometry(0, LARGE_HEAD_RADIUS, LARGE_HEAD_HEIGHT, 32); + this._arrowHead = new THREE.Mesh(arrowHeadGeo, material); + this._arrowHead.position.y = HEIGHT * 0.5; + this._arrowMesh.add(this._arrowHead); - const arrowHeadGeo = new THREE.CylinderGeometry(0, HEAD_RADIUS, HEAD_HEIGHT, 32); - const arrowHeadMesh = new THREE.Mesh(arrowHeadGeo, material); - arrowHeadMesh.position.y = HEIGHT * 0.5; - mesh.add(arrowHeadMesh); + const smallArrowHeadGeo = new THREE.CylinderGeometry(0, SMALL_HEAD_RADIUS, SMALL_HEAD_HEIGHT, 32); + this._smallArrowHead = new THREE.Mesh(smallArrowHeadGeo, material); + this._smallArrowHead.position.y = HEIGHT * 0.5; + this._arrowMesh.add(this._smallArrowHead); - mesh.position.x = 0.5 * HEIGHT + 0.5 * HEAD_HEIGHT; - mesh.rotation.z = Math.PI * 0.5; + this._arrowMesh.position.x = 0.5 * HEIGHT + 0.5 * LARGE_HEAD_HEIGHT; + this._arrowMesh.rotation.z = Math.PI * 0.5; const container = new THREE.Object3D(); - container.add(mesh); + container.add(this._arrowMesh); return container; } + + setCloseUpStyle() { + this._arrowMesh.scale.z = SMALL_ARROW_SCALE; + this._arrowMesh.scale.x = SMALL_ARROW_SCALE; + this._smallArrowHead.visible = true; + this._arrowHead.visible = false; + this._arrowMesh.position.x = 0.5 * HEIGHT + 0.5 * SMALL_HEAD_HEIGHT; + if (this._earthPos) { + this.setEarthPos(this._earthPos); + } + } + + setOrbitViewStyle() { + this._arrowMesh.scale.z = 1; + this._arrowMesh.scale.x = 1; + this._smallArrowHead.visible = false; + this._arrowHead.visible = true; + this._arrowMesh.position.x = 0.5 * HEIGHT + 0.5 * LARGE_HEAD_HEIGHT; + if (this._earthPos) { + this.setEarthPos(this._earthPos); + } + } } diff --git a/src/grasp-seasons/3d-views/orbit-view.ts b/src/grasp-seasons/3d-views/orbit-view.ts index ced46ca..e405fd5 100644 --- a/src/grasp-seasons/3d-views/orbit-view.ts +++ b/src/grasp-seasons/3d-views/orbit-view.ts @@ -132,8 +132,8 @@ export default class OrbitView extends BaseView { setupEarthCloseUpView() { this.registerInteractionHandler(this.latLogDraggingInteraction); - this.sunEarthLine.rootObject.visible = false; this.monthLabels.forEach((label) => label.visible = false); + this.sunEarthLine.setCloseUpStyle(); this.earthAxis.setCloseUpStyle(); this.latLongMarker.setCloseUpStyle(); } @@ -142,6 +142,7 @@ export default class OrbitView extends BaseView { this.registerInteractionHandler(this.earthDraggingInteraction); this.sunEarthLine.rootObject.visible = true; this.monthLabels.forEach((label) => label.visible = true); + this.sunEarthLine.setOrbitViewStyle(); this.earthAxis.setOrbitViewStyle(); this.latLongMarker.setOrbitViewStyle(); }