richardhcli
make new folder, subfolder of git, since images is too annoying to deal with
b012e8c
Raw
History Blame Contribute Delete
7.26 kB
class SceneManager {
constructor(config = {}) {
// Scene setup
this.scene = new THREE.Scene();
this.scene.background = new THREE.Color(config.backgroundColor || 0x0a0e27);
this.scene.fog = new THREE.Fog(config.backgroundColor || 0x0a0e27, 100, 1000);
// Camera setup
this.camera = new THREE.PerspectiveCamera(
75,
window.innerWidth / window.innerHeight,
0.1,
1000
);
this.camera.position.set(0, 0, config.cameraDistance || 15);
// Renderer setup
this.renderer = new THREE.WebGLRenderer({
canvas: document.getElementById('canvas'),
antialias: true,
alpha: true
});
this.renderer.setSize(window.innerWidth, window.innerHeight);
this.renderer.setPixelRatio(window.devicePixelRatio);
this.renderer.shadowMap.enabled = true;
// Lighting
this.setupLighting();
// Managers
this.sphereManager = new SphereManager(this.scene);
this.connectionManager = new ConnectionManager(this.scene);
// Controls
this.controls = new THREE.OrbitControls(this.camera, this.renderer.domElement);
this.controls.enableDamping = true;
this.controls.dampingFactor = 0.05;
this.controls.screenSpacePanning = false;
this.controls.minDistance = 5;
this.controls.maxDistance = 500;
this.controls.maxPolarAngle = Math.PI;
// Store initial camera state for reset
this.initialCameraPosition = this.camera.position.clone();
this.initialControlsTarget = new THREE.Vector3(0, 0, 0);
// Window resize listener
window.addEventListener('resize', () => this.onWindowResize());
// reset camera view button
const resetCameraButton = document.getElementById('resetCameraButton');
if (resetCameraButton) {
resetCameraButton.addEventListener('click', () => {
this.resetCameraView();
});
}
// Start animation loop
this.animate();
console.log('✓ Scene3D initialized');
}
/**
* Setup lighting for the scene
*/
setupLighting() {
const ambientLight = new THREE.AmbientLight(0xffffff, 0.6);
this.scene.add(ambientLight);
// Directional light
const directionalLight = new THREE.DirectionalLight(0xffffff, 0.8);
directionalLight.position.set(10, 10, 10);
directionalLight.castShadow = true;
directionalLight.shadow.mapSize.width = 2048;
directionalLight.shadow.mapSize.height = 2048;
this.scene.add(directionalLight);
// Point lights for atmosphere
const pointLight1 = new THREE.PointLight(0xff0080, 0.3, 100);
pointLight1.position.set(-10, 10, 10);
this.scene.add(pointLight1);
const pointLight2 = new THREE.PointLight(0x0080ff, 0.3, 100);
pointLight2.position.set(10, -10, 10);
this.scene.add(pointLight2);
}
/**
* Initialize scene from JSON configuration
* @param {Object} jsonConfig - Configuration with spheres and connections arrays
*
* Example:
* {
* spheres: [
* { name: 'A', radius: 1, color: '#ff0000', position: { x: 0, y: 0, z: 0 } },
* { name: 'B', radius: 1, color: '#00ff00', position: { x: 5, y: 0, z: 0 } }
* ],
* connections: [
* { id: 'AB', from: 'A', to: 'B', color: '#ffff00', lineWidth: 2 }
* ]
* }
*/
loadFromJSON(jsonConfig) {
// Clear existing scene
this.sphereManager.clearAll();
this.connectionManager.clearAll();
// Add spheres
if (jsonConfig.spheres && Array.isArray(jsonConfig.spheres)) {
jsonConfig.spheres.forEach(sphereConfig => {
const position = new THREE.Vector3(
sphereConfig.position?.x || 0,
sphereConfig.position?.y || 0,
sphereConfig.position?.z || 0
);
this.sphereManager.addSphere(sphereConfig.name, {
radius: sphereConfig.radius || 1,
color: sphereConfig.color || '#ff0000',
position: position,
opacity: sphereConfig.opacity || 1
});
});
}
// Add connections
if (jsonConfig.connections && Array.isArray(jsonConfig.connections)) {
jsonConfig.connections.forEach(connConfig => {
const fromSphere = this.sphereManager.getSphere(connConfig.from);
const toSphere = this.sphereManager.getSphere(connConfig.to);
if (fromSphere && toSphere) {
this.connectionManager.addConnection(
connConfig.id || `conn_${connConfig.from}_${connConfig.to}`,
fromSphere.position,
toSphere.position,
{
color: connConfig.color || '#00ff00',
lineWidth: connConfig.lineWidth || 2,
fromSphere: connConfig.from,
toSphere: connConfig.to
}
);
} else {
console.warn(`Connection ${connConfig.id}: sphere(s) not found`);
}
});
}
console.log('✓ Scene loaded from JSON');
}
/**
* Handle window resize
*/
onWindowResize() {
this.camera.aspect = window.innerWidth / window.innerHeight;
this.camera.updateProjectionMatrix();
this.renderer.setSize(window.innerWidth, window.innerHeight);
}
resetCameraView() {
// Disable damping temporarily to stop momentum
const wasDamping = this.controls.enableDamping;
this.controls.enableDamping = false;
// Reset to initial positions
this.camera.position.copy(this.initialCameraPosition);
this.controls.target.copy(this.initialControlsTarget);
// Reset OrbitControls internal state (removes any stored rotations/pans)
this.controls.reset();
// Force immediate update without damping
this.controls.update();
// Re-enable damping
this.controls.enableDamping = wasDamping;
console.log('✓ Camera view reset');
}
/**
* Main animation loop
*/
animate() {
requestAnimationFrame(() => this.animate());
// Update controls
this.controls.update();
// Update connection positions dynamically
this.connectionManager.getAllConnections().forEach(conn => {
const fromSphere = this.sphereManager.getSphere(conn.fromSphere);
const toSphere = this.sphereManager.getSphere(conn.toSphere);
if (fromSphere && toSphere) {
this.connectionManager.updateConnectionPositions(
conn.id,
fromSphere.position,
toSphere.position
);
}
});
this.renderer.render(this.scene, this.camera);
}
}