Skip to content

Commit

Permalink
Feature/refactoring (#6)
Browse files Browse the repository at this point in the history
* Refactoring

* Update README, add VSCode launch configuration, modify theme colors, and refactor tooltip management

* Invert rotation directions for touch interaction; enhance tooltip styling and positioning for mobile devices; implement touch event handling in VisualizationManager

* Fixing iOS issues

* Enhance mobile optimization in DomeVisualizer and SceneManager; adjust geometry segments, camera settings, and rendering behavior for improved performance on mobile devices.

* Refactor StarVisualizer to disable animations on mobile devices for improved performance

* Refactor visualization components to remove animations and optimize rendering for improved performance
  • Loading branch information
dmytrove authored Feb 20, 2025
1 parent 6fd6b44 commit e7aa50d
Show file tree
Hide file tree
Showing 6 changed files with 26 additions and 220 deletions.
3 changes: 1 addition & 2 deletions js/core/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -105,8 +105,7 @@ export class App {

this.lastRenderTime = timestamp;

const time = performance.now() * 0.001;
this.visualizationManager.updateAnimation(time);
// Only update input and render
this.inputManager.update(this.sceneManager.group, config);
this.sceneManager.render();

Expand Down
8 changes: 2 additions & 6 deletions js/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -211,12 +211,7 @@ export class App {

if (intersects.length > 0) {
const star = intersects[0].object;
// Animate the star on click
const originalScale = star.scale.x;
star.scale.setScalar(originalScale * 2);
setTimeout(() => {
star.scale.setScalar(originalScale);
}, 200);
// No animation on click
}
}

Expand All @@ -241,6 +236,7 @@ export class App {

animate() {
requestAnimationFrame(() => this.animate());
// Only update camera controls and render
this.inputManager.update(this.stars, config);
this.renderer.render(this.scene, this.camera);
}
Expand Down
84 changes: 13 additions & 71 deletions js/star-visualizer.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import * as THREE from 'three';
import { latLongToCartesian } from './utils/coordinates.js';
import { TooltipManager } from './ui/TooltipManager.js';
import { DomeVisualizer } from './visualization/DomeVisualizer.js';
import { StarAnimator } from './visualization/StarAnimator.js';
import { TextSprite } from './visualization/TextSprite.js';

export class StarVisualizer {
Expand All @@ -14,14 +13,10 @@ export class StarVisualizer {

this.tooltipManager = new TooltipManager();
this.domeVisualizer = new DomeVisualizer();
this.starAnimator = new StarAnimator();
this.textSpriteManager = new TextSprite();

this.geometryCache = new Map();
this.materialCache = new Map();
this.lastFrameTime = 0;
this.TARGET_FRAME_RATE = 60;
this.FRAME_BUDGET = 1000 / 60; // 16.67ms target frame time
this.isMobileDevice = /iPhone|iPad|iPod|Android/i.test(navigator.userAgent);
this.circleGeometry = null;
}
Expand All @@ -34,8 +29,8 @@ export class StarVisualizer {

let geometry;
if (this.isMobileDevice) {
// Use a circle geometry for mobile (faces camera)
geometry = new THREE.CircleGeometry(0.4, 16);
// Use a larger circle geometry for mobile (faces camera)
geometry = new THREE.CircleGeometry(0.8, 16);
} else {
// Use sphere for desktop
geometry = new THREE.SphereGeometry(0.6, 16, 16);
Expand Down Expand Up @@ -81,7 +76,6 @@ export class StarVisualizer {
const radius = 50;
const showGrid = !this.isMobileDevice && config.showGrid !== false;

// Create dome with optimized geometry for mobile
if (showGrid) {
const dome = this.domeVisualizer.createDome(radius, this.isMobileDevice);
this.group.add(dome);
Expand All @@ -95,7 +89,6 @@ export class StarVisualizer {
const sphereGeometry = this.createSphereGeometry();
this.group.add(this.timelineLines);

// Batch creation for better performance
const starBatch = new THREE.Group();
const batchSize = 50;

Expand All @@ -105,35 +98,30 @@ export class StarVisualizer {
batch.forEach((person, index) => {
const hue = ((i + index) / persons.length);
const color = new THREE.Color().setHSL(hue, 1, 0.5);
const glowColor = new THREE.Color().setHSL(hue, 1, 0.7);

const materialKey = `${color.getHexString()}-${glowColor.getHexString()}`;
const materialKey = color.getHexString();
let material = this.materialCache.get(materialKey);
if (!material) {
material = this.isMobileDevice ?
new THREE.MeshBasicMaterial({
color: color,
transparent: true,
opacity: 0.8,
side: THREE.DoubleSide
}) :
this.starAnimator.createStarMaterial(color, glowColor);
material = new THREE.MeshBasicMaterial({
color: color,
transparent: true,
opacity: 0.8,
side: THREE.DoubleSide
});
this.materialCache.set(materialKey, material);
}

const personStars = this.createPersonStars(person, sphereGeometry, material, radius, config);
starBatch.add(...personStars);
});

// Allow browser to process batch
if (i + batchSize < persons.length) {
await new Promise(resolve => setTimeout(resolve, 0));
}
}

this.group.add(starBatch);

// Reduce timeline opacity on mobile
if (this.isMobileDevice) {
this.timelineLines.children.forEach(line => {
if (line.material) {
Expand All @@ -144,10 +132,6 @@ export class StarVisualizer {

scene.add(this.group);
scene.userData.camera = config.camera;

if (!this.isMobileDevice) {
this.starAnimator.setupLighting(scene);
}
return this.group;
}

Expand All @@ -163,10 +147,7 @@ export class StarVisualizer {
nickname: person.nickname,
year: event.year,
info: event.info,
shortCode: event.shortCode,
originalScale: 1.0,
// Only add pulse phase for desktop
...(this.isMobileDevice ? {} : { pulsePhase: Math.random() * Math.PI * 2 })
shortCode: event.shortCode
};

const labelText = `${person.nickname} | ${event.shortCode} | ${event.year}`;
Expand Down Expand Up @@ -219,15 +200,6 @@ export class StarVisualizer {
}

setupInteractions(scene, config) {
if (!scene.userData.animationAdded) {
scene.userData.animationAdded = true;
const animate = () => {
requestAnimationFrame(animate);
this.starAnimator.updateStarAnimations(this.starMeshes);
};
animate();
}

const raycaster = new THREE.Raycaster();
const mouse = new THREE.Vector2();

Expand Down Expand Up @@ -284,19 +256,13 @@ export class StarVisualizer {
const starYear = star.userData.year;
const isPersonVisible = personVisibility[star.userData.person];
const isInTimeRange = starYear >= startYear && starYear <= endYear;
let scale = 1.0;

if (!isPersonVisible || !isInTimeRange) {
scale = 0.0;
}
const isVisible = isPersonVisible && isInTimeRange;

star.userData.originalScale = scale;
star.visible = scale > 0;
star.visible = isVisible;

const textSprite = this.textSprites[index];
if (textSprite) {
textSprite.visible = scale > 0;
textSprite.material.opacity = scale;
textSprite.visible = isVisible;
}
});

Expand All @@ -314,30 +280,6 @@ export class StarVisualizer {
});
}

// Add frame rate control
updateAnimation(time) {
if (this.isMobileDevice) {
// No animation for mobile devices to improve performance
return;
}

// Desktop animation
const deltaTime = time - this.lastFrameTime;
if (deltaTime < this.FRAME_BUDGET) {
return; // Skip frame if we're running too fast
}
this.lastFrameTime = time;

// Update animations with delta time
this.starMeshes.forEach(mesh => {
if (mesh.userData.pulsePhase !== undefined) {
mesh.userData.pulsePhase += deltaTime * 0.001;
const scale = 1 + Math.sin(mesh.userData.pulsePhase) * 0.1;
mesh.scale.setScalar(scale * mesh.userData.originalScale);
}
});
}

dispose() {
this.clearScene();
this.geometryCache.clear();
Expand Down
80 changes: 0 additions & 80 deletions js/visualization/StarAnimator.js

This file was deleted.

60 changes: 9 additions & 51 deletions js/visualization/StarMesh.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,64 +6,22 @@ export class StarMesh {
}

createGeometry() {
return new THREE.SphereGeometry(0.6, 32, 32);
return new THREE.SphereGeometry(0.6, 16, 16);
}

createMaterial(color, glowColor) {
return new THREE.ShaderMaterial({
uniforms: {
time: { value: 0 },
color: { value: color },
glowColor: { value: glowColor }
},
vertexShader: `
varying vec3 vNormal;
varying vec3 vPosition;
void main() {
vNormal = normalize(normalMatrix * normal);
vPosition = position;
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}
`,
fragmentShader: `
uniform vec3 color;
uniform vec3 glowColor;
uniform float time;
varying vec3 vNormal;
varying vec3 vPosition;
void main() {
float intensity = pow(0.7 - dot(vNormal, vec3(0.0, 0.0, 1.0)), 2.0);
float glow = 0.5 + 0.5 * sin(time * 2.0);
vec3 finalColor = mix(color, glowColor, intensity * glow);
gl_FragColor = vec4(finalColor, 1.0);
}
`,
transparent: true
createMaterial(color) {
return new THREE.MeshBasicMaterial({
color: color,
transparent: true,
opacity: 0.8
});
}

create(position, color, glowColor, userData) {
const material = this.createMaterial(color, glowColor);
create(position, color, _, userData) {
const material = this.createMaterial(color);
const mesh = new THREE.Mesh(this.geometry, material);
mesh.position.copy(position);
mesh.userData = {
...userData,
originalScale: 1.0,
pulsePhase: Math.random() * Math.PI * 2
};
mesh.userData = userData;
return mesh;
}

updateAnimation(star, time) {
if (star.visible) {
const material = star.material;
material.uniforms.time.value = time;

// Pulse animation
const pulseSpeed = 1.5;
const pulseAmount = 0.15;
const scale = 1.0 + Math.sin(time * pulseSpeed + star.userData.pulsePhase) * pulseAmount;
star.scale.setScalar(scale * star.userData.originalScale);
}
}
}
Loading

0 comments on commit e7aa50d

Please sign in to comment.