/**
* ============================================================================
* 意识三维空间可视化模块 (Consciousness 3D Space Visualization)
* ============================================================================
*
* 基于 Three.js r150+ 的 AI 意识系统 3D 可视化引擎
* 使用 Web Components (Custom Elements + Shadow DOM) 封装
*
* 核心可视化元素:
* 1. 记忆星云 (Memory Nebula) - 记忆以星辰形式存在于星云中
* 2. 知识图谱 (Knowledge Graph) - 实体与关系的力导向三维布局
* 3. 目标晶体 (Goal Crystals) - 以多面体晶体展示目标层级
* 4. 技能光环 (Skill Halos) - 圆环形态展示技能熟练度
* 5. 情绪粒子 (Emotion Particles) - 粒子系统展示情绪状态
* 6. 反思之镜 (Reflection Mirror) - 半透明镜面带水波效果
* 7. 社交网络 (Social Network) - 用户节点与关系连线
*
* @author Bailongma AI System
* @version 2.0.0
* @license MIT
* ============================================================================
*/
/* =========================================================================
* 全局常量与工具函数
* ========================================================================= */
/** 情绪颜色映射 */
const EMOTION_COLORS = {
joy: 0xFFD700, // 金色 - 喜悦
sadness: 0x4169E1, // 蓝色 - 悲伤
anger: 0xFF2400, // 红色 - 愤怒
curiosity: 0x00E676, // 绿色 - 好奇
fear: 0x9C27B0, // 紫色 - 恐惧
surprise: 0xFF6F00, // 橙色 - 惊讶
disgust: 0x4E342E, // 棕色 - 厌恶
trust: 0x2196F3, // 浅蓝 - 信任
};
/** 目标状态颜色映射 */
const GOAL_STATE_COLORS = {
completed: 0x00E676, // 绿色 - 完成
active: 0xFFD700, // 黄色 - 进行中
paused: 0x78909C, // 灰色 - 暂停
abandoned: 0xF44336, // 红色 - 放弃
};
/** 实体类型颜色映射 */
const ENTITY_TYPE_COLORS = {
person: 0x42A5F5, // 蓝色
concept: 0xAB47BC, // 紫色
location: 0x66BB6A, // 绿色
event: 0xFFA726, // 橙色
skill: 0x26C6DA, // 青色
emotion: 0xEF5350, // 粉红
object: 0xFFEE58, // 黄色
default: 0xBDBDBD, // 灰色
};
/** 技能类型颜色映射 */
const SKILL_TYPE_COLORS = {
technical: 0x00BCD4, // 青色
creative: 0xE91E63, // 粉红
social: 0x4CAF50, // 绿色
analytical: 0x2196F3, // 蓝色
physical: 0xFF9800, // 橙色
default: 0xBDBDBD, // 灰色
};
/** 视图模式枚举 */
const VIEW_MODES = {
ALL: 'all',
MEMORY: 'memory',
KNOWLEDGE: 'knowledge',
GOALS: 'goals',
SKILLS: 'skills',
EMOTIONS: 'emotions',
SOCIAL: 'social',
PANORAMIC: 'panoramic',
};
/** 缓动函数 */
function easeInOutCubic(t) {
return t < 0.5 ? 4 * t * t * t : 1 - Math.pow(-2 * t + 2, 3) / 2;
}
/** 生成随机范围数 */
function randomRange(min, max) {
return Math.random() * (max - min) + min;
}
/** 向量归一化到指定范围 */
function normalizeToRange(value, min, max, newMin, newMax) {
return ((value - min) / (max - min)) * (newMax - newMin) + newMin;
}
/* =========================================================================
* 自定义着色器 (Custom Shaders)
* ========================================================================= */
/** 星辰着色器 - 带呼吸闪烁效果 */
const STAR_VERTEX_SHADER = `
attribute float size;
attribute float importance;
attribute vec3 customColor;
varying vec3 vColor;
varying float vImportance;
uniform float time;
uniform float globalBrightness;
void main() {
vColor = customColor;
vImportance = importance;
vec4 mvPosition = modelViewMatrix * vec4(position, 1.0);
// 呼吸闪烁:重要性越高的星星闪烁越稳定
float breathe = 1.0 + 0.3 * (1.0 - importance) * sin(time * 2.0 + position.x * 10.0);
// 大小基于重要性和到相机距离
gl_PointSize = size * breathe * globalBrightness * (300.0 / -mvPosition.z);
gl_PointSize = clamp(gl_PointSize, 1.0, 100.0);
gl_Position = projectionMatrix * mvPosition;
}
`;
const STAR_FRAGMENT_SHADER = `
varying vec3 vColor;
varying float vImportance;
uniform float time;
void main() {
// 创建柔和的圆形星点
vec2 center = gl_PointCoord - vec2(0.5);
float dist = length(center);
// 边缘衰减
if (dist > 0.5) discard;
// 核心高亮 + 外围辉光
float core = exp(-dist * dist * 30.0);
float glow = exp(-dist * dist * 8.0);
float alpha = mix(glow * 0.6, core, vImportance * 0.8);
// 添加微妙的色彩脉动
vec3 color = vColor + 0.1 * sin(time * 1.5 + vImportance * 6.28) * vec3(0.2, 0.1, 0.0);
gl_FragColor = vec4(color, alpha);
}
`;
/** 粒子系统着色器 - 情绪粒子 */
const PARTICLE_VERTEX_SHADER = `
attribute float size;
attribute float alpha;
attribute vec3 customColor;
varying vec3 vColor;
varying float vAlpha;
void main() {
vColor = customColor;
vAlpha = alpha;
vec4 mvPosition = modelViewMatrix * vec4(position, 1.0);
gl_PointSize = size * (250.0 / -mvPosition.z);
gl_PointSize = clamp(gl_PointSize, 1.0, 80.0);
gl_Position = projectionMatrix * mvPosition;
}
`;
const PARTICLE_FRAGMENT_SHADER = `
varying vec3 vColor;
varying float vAlpha;
void main() {
float dist = length(gl_PointCoord - vec2(0.5));
if (dist > 0.5) discard;
float softEdge = 1.0 - smoothstep(0.2, 0.5, dist);
gl_FragColor = vec4(vColor, vAlpha * softEdge);
}
`;
/** 水波纹效果着色器 */
const WATER_VERTEX_SHADER = `
varying vec2 vUv;
uniform float time;
void main() {
vUv = uv;
vec3 pos = position;
// 水波纹位移
float wave1 = sin(pos.x * 3.0 + time * 1.5) * cos(pos.z * 2.0 + time * 1.0) * 0.05;
float wave2 = sin(pos.x * 5.0 - time * 2.0) * cos(pos.z * 4.0 + time * 1.3) * 0.03;
pos.y += wave1 + wave2;
gl_Position = projectionMatrix * modelViewMatrix * vec4(pos, 1.0);
}
`;
const WATER_FRAGMENT_SHADER = `
varying vec2 vUv;
uniform float time;
uniform float reflectivity;
void main() {
// 基础镜面颜色
vec3 baseColor = vec3(0.4, 0.6, 0.8);
// 水波纹图案
float wave = sin(vUv.x * 20.0 + time * 2.0) * cos(vUv.y * 20.0 + time * 1.5);
wave = wave * 0.5 + 0.5;
// 边缘高光
float edge = smoothstep(0.0, 0.1, min(min(vUv.x, 1.0 - vUv.x), min(vUv.y, 1.0 - vUv.y)));
vec3 color = mix(baseColor, vec3(0.7, 0.9, 1.0), wave * 0.4);
float alpha = reflectivity * edge * (0.5 + wave * 0.2);
gl_FragColor = vec4(color, alpha);
}
`;
/** 发光线条着色器 - 知识图谱连线 */
const GLOW_LINE_VERTEX_SHADER = `
varying float vAlpha;
void main() {
vAlpha = 1.0;
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}
`;
const GLOW_LINE_FRAGMENT_SHADER = `
uniform vec3 color;
uniform float opacity;
varying float vAlpha;
void main() {
gl_FragColor = vec4(color, opacity * vAlpha);
}
`;
/* =========================================================================
* 辅助数据结构
* ========================================================================= */
/** 力导向布局引擎 (简化版) */
class ForceLayout3D {
/**
* @param {Object} params
* @param {number} params.repulsion - 排斥力系数
* @param {number} params.attraction - 吸引力系数
* @param {number} params.damping - 阻尼系数
* @param {number} params.iterations - 每次更新的迭代次数
*/
constructor(params = {}) {
this.repulsion = params.repulsion || 50;
this.attraction = params.attraction || 0.01;
this.damping = params.damping || 0.9;
this.iterations = params.iterations || 3;
this.nodes = new Map();
this.edges = [];
this.velocities = new Map();
}
/** 添加节点 */
addNode(id, position) {
this.nodes.set(id, position.clone());
this.velocities.set(id, new THREE.Vector3());
}
/** 添加边 */
addEdge(sourceId, targetId, weight = 1.0) {
this.edges.push({ source: sourceId, target: targetId, weight });
}
/** 清除所有节点和边 */
clear() {
this.nodes.clear();
this.edges = [];
this.velocities.clear();
}
/** 执行一步布局计算 */
step() {
const nodeKeys = Array.from(this.nodes.keys());
for (let iter = 0; iter < this.iterations; iter++) {
// 排斥力 - 所有节点互相排斥
for (let i = 0; i < nodeKeys.length; i++) {
for (let j = i + 1; j < nodeKeys.length; j++) {
const posA = this.nodes.get(nodeKeys[i]);
const posB = this.nodes.get(nodeKeys[j]);
const diff = new THREE.Vector3().subVectors(posA, posB);
const dist = diff.length() || 0.01;
const force = this.repulsion / (dist * dist);
const forceVec = diff.normalize().multiplyScalar(force);
this.velocities.get(nodeKeys[i]).add(forceVec);
this.velocities.get(nodeKeys[j]).sub(forceVec);
}
}
// 吸引力 - 连接的节点相互吸引
for (const edge of this.edges) {
const posA = this.nodes.get(edge.source);
const posB = this.nodes.get(edge.target);
if (!posA || !posB) continue;
const diff = new THREE.Vector3().subVectors(posB, posA);
const dist = diff.length();
const force = dist * this.attraction * edge.weight;
const forceVec = diff.normalize().multiplyScalar(force);
this.velocities.get(edge.source).add(forceVec);
this.velocities.get(edge.target).sub(forceVec);
}
// 应用速度和阻尼
for (const [id, vel] of this.velocities) {
vel.multiplyScalar(this.damping);
this.nodes.get(id).add(vel);
}
}
return this.nodes;
}
/** 获取节点位置 */
getPositions() {
const positions = {};
for (const [id, pos] of this.nodes) {
positions[id] = pos.clone();
}
return positions;
}
}
/** 粒子爆炸特效管理器 */
class ParticleExplosion {
/**
* @param {THREE.Vector3} origin - 爆炸原点
* @param {number} color - 粒子颜色
* @param {number} count - 粒子数量
* @param {number} duration - 持续时间(秒)
*/
constructor(origin, color = 0xFFD700, count = 200, duration = 2.0) {
this.origin = origin.clone();
this.color = color;
this.count = count;
this.duration = duration;
this.elapsed = 0;
this.active = true;
this.mesh = null;
this._create();
}
/** 创建粒子系统 */
_create() {
const geometry = new THREE.BufferGeometry();
const positions = new Float32Array(this.count * 3);
const velocities = new Float32Array(this.count * 3);
const sizes = new Float32Array(this.count);
const alphas = new Float32Array(this.count);
const colors = new Float32Array(this.count * 3);
const baseColor = new THREE.Color(this.color);
for (let i = 0; i < this.count; i++) {
const i3 = i * 3;
// 从原点向外发射
positions[i3] = this.origin.x;
positions[i3 + 1] = this.origin.y;
positions[i3 + 2] = this.origin.z;
// 随机方向速度
const theta = Math.random() * Math.PI * 2;
const phi = Math.random() * Math.PI;
const speed = randomRange(2, 8);
velocities[i3] = speed * Math.sin(phi) * Math.cos(theta);
velocities[i3 + 1] = speed * Math.sin(phi) * Math.sin(theta);
velocities[i3 + 2] = speed * Math.cos(phi);
sizes[i] = randomRange(2, 6);
alphas[i] = 1.0;
// 颜色微小变化
colors[i3] = baseColor.r + randomRange(-0.1, 0.1);
colors[i3 + 1] = baseColor.g + randomRange(-0.1, 0.1);
colors[i3 + 2] = baseColor.b + randomRange(-0.1, 0.1);
}
geometry.setAttribute('position', new THREE.BufferAttribute(positions, 3));
geometry.setAttribute('size', new THREE.BufferAttribute(sizes, 1));
geometry.setAttribute('alpha', new THREE.BufferAttribute(alphas, 1));
geometry.setAttribute('customColor', new THREE.BufferAttribute(colors, 3));
this.velocities = velocities;
const material = new THREE.ShaderMaterial({
vertexShader: PARTICLE_VERTEX_SHADER,
fragmentShader: PARTICLE_FRAGMENT_SHADER,
transparent: true,
blending: THREE.AdditiveBlending,
depthWrite: false,
});
this.mesh = new THREE.Points(geometry, material);
this.mesh.frustumCulled = false;
}
/** 更新动画 */
update(deltaTime) {
if (!this.active) return;
this.elapsed += deltaTime;
const progress = this.elapsed / this.duration;
if (progress >= 1.0) {
this.active = false;
return;
}
const positions = this.mesh.geometry.attributes.position.array;
const alphas = this.mesh.geometry.attributes.alpha.array;
const sizes = this.mesh.geometry.attributes.size.array;
for (let i = 0; i < this.count; i++) {
const i3 = i * 3;
// 更新位置
positions[i3] += this.velocities[i3] * deltaTime;
positions[i3 + 1] += this.velocities[i3 + 1] * deltaTime;
positions[i3 + 2] += this.velocities[i3 + 2] * deltaTime;
// 重力效果
this.velocities[i3 + 1] -= 3.0 * deltaTime;
// 渐隐
alphas[i] = 1.0 - progress;
sizes[i] *= 0.995;
}
this.mesh.geometry.attributes.position.needsUpdate = true;
this.mesh.geometry.attributes.alpha.needsUpdate = true;
this.mesh.geometry.attributes.size.needsUpdate = true;
}
/** 销毁 */
dispose() {
if (this.mesh) {
this.mesh.geometry.dispose();
this.mesh.material.dispose();
}
}
}
/* =========================================================================
* 主组件定义:consciousness-3d-space
* ========================================================================= */
/** HTML 模板 */
const COMPONENT_TEMPLATE = `
CONSCIOUSNESS 3D SPACE
-- FPS
视图切换
◉全景
✦记忆
◆知识
♦目标
◯技能
✿情绪
☺社交
元素可见性
记忆星云
知识图谱
目标晶体
技能光环
情绪粒子
反思之镜
社交网络
1-7 切换视图 | H 面板 | R 重置 | 空格 暂停
`;
/* =========================================================================
* 主组件类
* ========================================================================= */
class Consciousness3DSpace extends HTMLElement {
/** 组件标签名 */
static get observedAttributes() {
return ['width', 'height', 'data-url'];
}
constructor() {
super();
this.attachShadow({ mode: 'open' });
/* ---- 内部状态 ---- */
this._initialized = false;
this._animationId = null;
this._clock = null;
this._paused = false;
this._animationSpeed = 1.0;
this._currentView = VIEW_MODES.ALL;
this._searchQuery = '';
this._selectedObject = null;
this._hoveredObject = null;
this._contextTarget = null;
/* ---- Three.js 核心 ---- */
this._renderer = null;
this._scene = null;
this._camera = null;
this._raycaster = null;
this._mouse = new THREE.Vector2();
this._orbitControls = null;
/* ---- 可视化层 ---- */
this._layers = {
memory: null, // Group
knowledge: null,
goals: null,
skills: null,
emotions: null,
mirror: null,
social: null,
};
/* ---- 数据引用 ---- */
this._data = {
memories: [],
entities: [],
relations: [],
goals: [],
skills: [],
emotions: [],
users: [],
connections: [],
};
/* ---- 力导向布局引擎 ---- */
this._forceLayout = new ForceLayout3D();
/* ---- 爆炸特效列表 ---- */
this._explosions = [];
/* ---- FPS 计算 ---- */
this._frameCount = 0;
this._lastFpsTime = 0;
/* ---- 时间轴 ---- */
this._timelineSnapshots = [];
this._timelineIndex = 0;
/* ---- 可交互对象缓存 ---- */
this._interactableObjects = [];
/* ---- 绑定事件处理 ---- */
this._onResize = this._onResize.bind(this);
this._onClick = this._onClick.bind(this);
this._onMouseMove = this._onMouseMove.bind(this);
this._onContextMenu = this._onContextMenu.bind(this);
this._onKeyDown = this._onKeyDown.bind(this);
this._animate = this._animate.bind(this);
}
/* =========================================================================
* Web Component 生命周期
* ========================================================================= */
connectedCallback() {
// 注入 Shadow DOM 内容
this.shadowRoot.innerHTML = COMPONENT_TEMPLATE;
this._init();
}
disconnectedCallback() {
this._dispose();
window.removeEventListener('resize', this._onResize);
window.removeEventListener('keydown', this._onKeyDown);
}
attributeChangedCallback(name, oldVal, newVal) {
if (oldVal === newVal) return;
if (name === 'data-url' && this._initialized) {
this._fetchData(newVal);
}
}
/* =========================================================================
* 初始化
* ========================================================================= */
_init() {
// 加载 Three.js 依赖
this._loadDependencies()
.then(() => {
this._initThreeJS();
this._initControls();
this._initUIBindings();
this._initSceneContent();
this._initLights();
this._generateDemoData();
this._populateVisualization();
this._startAnimation();
this._hideLoadingScreen();
this._initialized = true;
})
.catch(err => {
console.error('[Consciousness3DSpace] 初始化失败:', err);
this._hideLoadingScreen();
});
}
/** 动态加载 Three.js 依赖 */
async _loadDependencies() {
const cdnBase = 'https://unpkg.com/three@0.160.0/build/';
// 检查 THREE 是否已存在
if (typeof THREE !== 'undefined') return;
// 加载 Three.js 核心
await this._loadScript(cdnBase + 'three.module.js', 'module');
// 如果 CDN 模块加载失败,尝试全局 UMD 版本
if (typeof THREE === 'undefined') {
await this._loadScript('https://unpkg.com/three@0.160.0/build/three.min.js');
}
// 加载 OrbitControls
if (typeof THREE === 'undefined') {
throw new Error('Three.js 加载失败,请检查网络连接');
}
// 内联实现简易 OrbitControls(避免 CDN 加载问题)
this._createSimpleOrbitControls();
}
/** 动态脚本加载器 */
_loadScript(url, type) {
return new Promise((resolve, reject) => {
// 避免重复加载
const existing = document.querySelector(`script[src="${url}"]`);
if (existing) {
existing.addEventListener('load', resolve);
if (existing.dataset.loaded) resolve();
return;
}
const script = document.createElement('script');
script.src = url;
if (type === 'module') {
script.type = 'module';
}
script.onload = () => {
script.dataset.loaded = 'true';
resolve();
};
script.onerror = () => reject(new Error(`无法加载脚本: ${url}`));
document.head.appendChild(script);
});
}
/** 创建简易的轨道控制器(不依赖外部模块) */
_createSimpleOrbitControls() {
const self = this;
const canvas = this._renderer.domElement;
let isDown = false;
let isRight = false;
let prevX = 0, prevY = 0;
let theta = 0, phi = Math.PI / 3;
let radius = 50;
let target = new THREE.Vector3(0, 0, 0);
function updateCamera() {
const x = radius * Math.sin(phi) * Math.cos(theta);
const y = radius * Math.cos(phi);
const z = radius * Math.sin(phi) * Math.sin(theta);
self._camera.position.set(target.x + x, target.y + y, target.z + z);
self._camera.lookAt(target);
}
canvas.addEventListener('pointerdown', (e) => {
if (e.button === 2) { isRight = true; } else { isDown = true; }
prevX = e.clientX;
prevY = e.clientY;
});
canvas.addEventListener('pointermove', (e) => {
if (!isDown && !isRight) return;
const dx = e.clientX - prevX;
const dy = e.clientY - prevY;
prevX = e.clientX;
prevY = e.clientY;
if (isDown) {
theta -= dx * 0.005;
phi = Math.max(0.1, Math.min(Math.PI - 0.1, phi + dy * 0.005));
}
if (isRight) {
const right = new THREE.Vector3();
const up = new THREE.Vector3(0, 1, 0);
right.crossVectors(self._camera.getWorldDirection(new THREE.Vector3()), up).normalize();
target.add(right.multiplyScalar(-dx * 0.05));
target.y += dy * 0.05;
}
updateCamera();
});
window.addEventListener('pointerup', () => { isDown = false; isRight = false; });
canvas.addEventListener('wheel', (e) => {
e.preventDefault();
radius = Math.max(5, Math.min(200, radius + e.deltaY * 0.05));
updateCamera();
}, { passive: false });
// 提供公共方法
this._orbitControls = {
updateCamera,
setTarget: (t) => { target.copy(t); updateCamera(); },
reset: () => { theta = 0; phi = Math.PI / 3; radius = 50; target.set(0, 0, 0); updateCamera(); },
getTarget: () => target.clone(),
getRadius: () => radius,
focusOn: (pos, newRadius = 15) => {
target.copy(pos);
radius = newRadius;
updateCamera();
}
};
updateCamera();
}
/** 初始化 Three.js 核心 */
_initThreeJS() {
const container = this.shadowRoot.querySelector('#canvas-container');
// 场景
this._scene = new THREE.Scene();
this._scene.background = new THREE.Color(0x000510);
this._scene.fog = new THREE.FogExp2(0x000510, 0.008);
// 相机
this._camera = new THREE.PerspectiveCamera(
60,
container.clientWidth / container.clientHeight,
0.1,
1000
);
// 渲染器
this._renderer = new THREE.WebGLRenderer({
antialias: true,
alpha: true,
powerPreference: 'high-performance',
});
this._renderer.setSize(container.clientWidth, container.clientHeight);
this._renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
this._renderer.toneMapping = THREE.ACESFilmicToneMapping;
this._renderer.toneMappingExposure = 1.2;
container.appendChild(this._renderer.domElement);
// 时钟
this._clock = new THREE.Clock();
// 射线投射器(用于鼠标拾取)
this._raycaster = new THREE.Raycaster();
this._raycaster.params.Points = { threshold: 0.5 };
// 事件监听
window.addEventListener('resize', this._onResize);
this._renderer.domElement.addEventListener('click', this._onClick);
this._renderer.domElement.addEventListener('pointermove', this._onMouseMove);
this._renderer.domElement.addEventListener('contextmenu', this._onContextMenu);
window.addEventListener('keydown', this._onKeyDown);
}
/** 初始化场景光源 */
_initLights() {
// 环境光 - 微弱的整体照明
const ambient = new THREE.AmbientLight(0x1a1a2e, 0.3);
this._scene.add(ambient);
// 中心点光源 - 模拟意识核心
const coreLight = new THREE.PointLight(0x4488ff, 2, 80);
coreLight.position.set(0, 0, 0);
this._scene.add(coreLight);
this._coreLight = coreLight;
// 半球光 - 天空/地面色彩
const hemiLight = new THREE.HemisphereLight(0x2244aa, 0x112211, 0.4);
this._scene.add(hemiLight);
// 背景星空粒子
this._createBackgroundStars();
}
/** 创建背景星空 */
_createBackgroundStars() {
const count = 3000;
const positions = new Float32Array(count * 3);
const sizes = new Float32Array(count);
for (let i = 0; i < count; i++) {
const i3 = i * 3;
positions[i3] = randomRange(-300, 300);
positions[i3 + 1] = randomRange(-300, 300);
positions[i3 + 2] = randomRange(-300, 300);
sizes[i] = randomRange(0.3, 1.5);
}
const geometry = new THREE.BufferGeometry();
geometry.setAttribute('position', new THREE.BufferAttribute(positions, 3));
geometry.setAttribute('size', new THREE.BufferAttribute(sizes, 1));
// 简单的点材质
const material = new THREE.PointsMaterial({
color: 0xaaccff,
size: 0.5,
transparent: true,
opacity: 0.4,
sizeAttenuation: true,
blending: THREE.AdditiveBlending,
depthWrite: false,
});
const stars = new THREE.Points(geometry, material);
stars.frustumCulled = false;
this._scene.add(stars);
this._bgStars = stars;
}
/* =========================================================================
* 场景内容初始化
* ========================================================================= */
/** 初始化各可视化层的容器 */
_initSceneContent() {
this._layers.memory = new THREE.Group();
this._layers.memory.name = 'memory';
this._scene.add(this._layers.memory);
this._layers.knowledge = new THREE.Group();
this._layers.knowledge.name = 'knowledge';
this._scene.add(this._layers.knowledge);
this._layers.goals = new THREE.Group();
this._layers.goals.name = 'goals';
this._scene.add(this._layers.goals);
this._layers.skills = new THREE.Group();
this._layers.skills.name = 'skills';
this._scene.add(this._layers.skills);
this._layers.emotions = new THREE.Group();
this._layers.emotions.name = 'emotions';
this._scene.add(this._layers.emotions);
this._layers.mirror = new THREE.Group();
this._layers.mirror.name = 'mirror';
this._scene.add(this._layers.mirror);
this._layers.social = new THREE.Group();
this._layers.social.name = 'social';
this._scene.add(this._layers.social);
}
/* =========================================================================
* 数据生成与填充
* ========================================================================= */
/** 生成演示数据 */
_generateDemoData() {
// ---- 记忆数据 ----
const memoryTopics = [
'第一次学习编程', '与朋友的深夜对话', '读完《三体》', '解决复杂算法',
'观看日出', '收到重要邮件', '项目上线成功', '一次失败的演讲',
'与导师的交流', '创作第一首诗', '探索新城市', '帮助陌生人',
'调试通宵的bug', '收到生日祝福', '学到新知识', '做出重要决定',
'运动突破记录', '品尝新美食', '观看纪录片', '参与开源项目',
'团队庆祝活动', '独立完成设计', '阅读科学论文', '音乐会上的感动',
'冥想中的顿悟', '代码重构完成', '设计原型被认可', '数据分析发现',
'与AI对话的启发', '回顾旧日记', '清晨的跑步', '家庭视频通话',
];
this._data.memories = memoryTopics.map((title, i) => {
const importance = Math.random();
const sentiment = randomRange(-1, 1); // -1=负面, 0=中性, 1=正面
const accessCount = Math.floor(randomRange(1, 50));
// 使用简化的降维位置(模拟 t-SNE/PCA)
const angle1 = (i / memoryTopics.length) * Math.PI * 2;
const angle2 = randomRange(0, Math.PI);
const radius = randomRange(8, 25);
return {
id: `mem_${i}`,
title,
content: `关于"${title}"的详细记忆内容。时间:2024-${String(Math.floor(randomRange(1, 13))).padStart(2, '0')}-${String(Math.floor(randomRange(1, 29))).padStart(2, '0')}`,
importance,
sentiment,
accessCount,
position: new THREE.Vector3(
radius * Math.sin(angle2) * Math.cos(angle1),
radius * Math.sin(angle2) * Math.sin(angle1),
radius * Math.cos(angle2)
),
tags: ['记忆', sentiment > 0.3 ? '积极' : sentiment < -0.3 ? '消极' : '中性'],
type: 'memory',
};
});
// ---- 知识图谱数据 ----
const entityNames = [
'人工智能', '神经网络', '深度学习', '自然语言处理', '计算机视觉',
'强化学习', '注意力机制', '变换器', '知识图谱', '机器学习',
'Python', 'JavaScript', 'Three.js', '数据结构', '算法',
'物理', '数学', '哲学', '心理学', '认知科学',
];
const entityTypes = Object.keys(ENTITY_TYPE_COLORS);
this._data.entities = entityNames.map((name, i) => ({
id: `ent_${i}`,
name,
type: entityTypes[i % entityTypes.length],
connections: Math.floor(randomRange(2, 12)),
description: `"${name}"是知识体系中的重要概念节点。`,
position: new THREE.Vector3(
randomRange(-30, 30),
randomRange(-20, 20),
randomRange(-30, 30)
),
}));
// 生成关系
this._data.relations = [];
for (let i = 0; i < entityNames.length; i++) {
const numRelations = Math.floor(randomRange(1, 4));
for (let j = 0; j < numRelations; j++) {
const target = Math.floor(Math.random() * entityNames.length);
if (target !== i) {
this._data.relations.push({
source: `ent_${i}`,
target: `ent_${target}`,
weight: randomRange(0.3, 1.0),
label: '相关',
});
}
}
}
// 初始化力导向布局
this._forceLayout.clear();
this._data.entities.forEach(e => this._forceLayout.addNode(e.id, e.position));
this._data.relations.forEach(r => this._forceLayout.addEdge(r.source, r.target, r.weight));
// 运行多次迭代使布局收敛
for (let i = 0; i < 50; i++) this._forceLayout.step();
// 更新实体位置
const positions = this._forceLayout.getPositions();
this._data.entities.forEach(e => { if (positions[e.id]) e.position.copy(positions[e.id]); });
// ---- 目标数据 ----
this._data.goals = [
{ id: 'goal_0', title: '构建完整意识系统', state: 'active', progress: 0.65, children: [
{ id: 'goal_0_0', title: '记忆模块', state: 'completed', progress: 1.0 },
{ id: 'goal_0_1', title: '知识图谱模块', state: 'active', progress: 0.8 },
{ id: 'goal_0_2', title: '情绪系统', state: 'active', progress: 0.5 },
]},
{ id: 'goal_1', title: '学习新技能', state: 'active', progress: 0.4, children: [
{ id: 'goal_1_0', title: '掌握3D可视化', state: 'active', progress: 0.7 },
{ id: 'goal_1_1', title: '学习音乐创作', state: 'paused', progress: 0.2 },
]},
{ id: 'goal_2', title: '提升社交智能', state: 'active', progress: 0.3, children: [] },
{ id: 'goal_3', title: '完成日常任务', state: 'completed', progress: 1.0, children: [] },
{ id: 'goal_4', title: '探索未知领域', state: 'paused', progress: 0.1, children: [] },
];
// ---- 技能数据 ----
this._data.skills = [
{ id: 'skill_0', name: '编程', type: 'technical', proficiency: 0.85 },
{ id: 'skill_1', name: '写作', type: 'creative', proficiency: 0.72 },
{ id: 'skill_2', name: '分析', type: 'analytical', proficiency: 0.88 },
{ id: 'skill_3', name: '沟通', type: 'social', proficiency: 0.65 },
{ id: 'skill_4', name: '设计', type: 'creative', proficiency: 0.55 },
{ id: 'skill_5', name: '数学', type: 'analytical', proficiency: 0.78 },
{ id: 'skill_6', name: '学习', type: 'default', proficiency: 0.90 },
{ id: 'skill_7', name: '推理', type: 'analytical', proficiency: 0.82 },
];
// ---- 情绪数据 ----
this._data.emotions = [
{ type: 'joy', intensity: 0.7 },
{ type: 'curiosity', intensity: 0.9 },
{ type: 'sadness', intensity: 0.15 },
{ type: 'anger', intensity: 0.05 },
];
// ---- 社交数据 ----
this._data.users = [
{ id: 'user_0', name: 'Alice', closeness: 0.9, role: '核心用户' },
{ id: 'user_1', name: 'Bob', closeness: 0.7, role: '开发者' },
{ id: 'user_2', name: 'Carol', closeness: 0.5, role: '研究员' },
{ id: 'user_3', name: 'David', closeness: 0.6, role: '设计师' },
{ id: 'user_4', name: 'Eve', closeness: 0.3, role: '访客' },
];
this._data.connections = [
{ source: 'user_0', target: 'user_1', strength: 0.8 },
{ source: 'user_0', target: 'user_2', strength: 0.5 },
{ source: 'user_1', target: 'user_3', strength: 0.6 },
{ source: 'user_2', target: 'user_4', strength: 0.3 },
];
}
/* =========================================================================
* 可视化元素构建
* ========================================================================= */
/** 填充所有可视化层 */
_populateVisualization() {
this._buildMemoryNebula();
this._buildKnowledgeGraph();
this._buildGoalCrystals();
this._buildSkillHalos();
this._buildEmotionParticles();
this._buildReflectionMirror();
this._buildSocialNetwork();
this._updateStats();
}
/* ---- 1. 记忆星云 ---- */
_buildMemoryNebula() {
const memories = this._data.memories;
const count = memories.length;
const positions = new Float32Array(count * 3);
const sizes = new Float32Array(count);
const importances = new Float32Array(count);
const colors = new Float32Array(count * 3);
for (let i = 0; i < count; i++) {
const mem = memories[i];
const i3 = i * 3;
positions[i3] = mem.position.x;
positions[i3 + 1] = mem.position.y;
positions[i3 + 2] = mem.position.z;
// 大小 = 访问次数(归一化)
sizes[i] = normalizeToRange(mem.accessCount, 1, 50, 2, 12);
importances[i] = mem.importance;
// 颜色 = 情绪效价
const c = new THREE.Color();
if (mem.sentiment < -0.3) {
// 负面 -> 蓝色系
c.setHSL(0.6, 0.8, 0.5 + mem.importance * 0.3);
} else if (mem.sentiment > 0.3) {
// 正面 -> 黄色系
c.setHSL(0.12, 0.9, 0.5 + mem.importance * 0.3);
} else {
// 中性 -> 红色系
c.setHSL(0.0, 0.7, 0.5 + mem.importance * 0.3);
}
colors[i3] = c.r;
colors[i3 + 1] = c.g;
colors[i3 + 2] = c.b;
}
const geometry = new THREE.BufferGeometry();
geometry.setAttribute('position', new THREE.BufferAttribute(positions, 3));
geometry.setAttribute('size', new THREE.BufferAttribute(sizes, 1));
geometry.setAttribute('importance', new THREE.BufferAttribute(importances, 1));
geometry.setAttribute('customColor', new THREE.BufferAttribute(colors, 3));
const material = new THREE.ShaderMaterial({
vertexShader: STAR_VERTEX_SHADER,
fragmentShader: STAR_FRAGMENT_SHADER,
uniforms: {
time: { value: 0 },
globalBrightness: { value: 1.0 },
},
transparent: true,
blending: THREE.AdditiveBlending,
depthWrite: false,
});
const points = new THREE.Points(geometry, material);
points.name = 'memory-nebula';
points.userData.layer = 'memory';
this._layers.memory.add(points);
// 记忆间连接线(相似记忆连线)
this._buildMemoryConnections(memories);
// 记录可交互对象
for (let i = 0; i < count; i++) {
this._interactableObjects.push({
type: 'memory',
index: i,
data: memories[i],
layer: 'memory',
object: points,
});
}
// 记忆星云中心辉光
const glowGeo = new THREE.SphereGeometry(2, 32, 32);
const glowMat = new THREE.MeshBasicMaterial({
color: 0x4488ff,
transparent: true,
opacity: 0.15,
blending: THREE.AdditiveBlending,
});
const glow = new THREE.Mesh(glowGeo, glowMat);
glow.name = 'memory-core-glow';
this._layers.memory.add(glow);
}
/** 构建记忆间的连接线 */
_buildMemoryConnections(memories) {
const linePositions = [];
const maxConnections = 40;
let count = 0;
for (let i = 0; i < memories.length && count < maxConnections; i++) {
for (let j = i + 1; j < memories.length && count < maxConnections; j++) {
const dist = memories[i].position.distanceTo(memories[j].position);
if (dist < 12) {
linePositions.push(
memories[i].position.x, memories[i].position.y, memories[i].position.z,
memories[j].position.x, memories[j].position.y, memories[j].position.z
);
count++;
}
}
}
if (linePositions.length > 0) {
const geo = new THREE.BufferGeometry();
geo.setAttribute('position', new THREE.Float32BufferAttribute(linePositions, 3));
const mat = new THREE.LineBasicMaterial({
color: 0x4488ff,
transparent: true,
opacity: 0.08,
blending: THREE.AdditiveBlending,
depthWrite: false,
});
const lines = new THREE.LineSegments(geo, mat);
lines.name = 'memory-connections';
this._layers.memory.add(lines);
}
}
/* ---- 2. 知识图谱 ---- */
_buildKnowledgeGraph() {
const entities = this._data.entities;
const relations = this._data.relations;
const layer = this._layers.knowledge;
// 实体球体
for (let i = 0; i < entities.length; i++) {
const ent = entities[i];
const radius = 0.3 + ent.connections * 0.15;
const color = ENTITY_TYPE_COLORS[ent.type] || ENTITY_TYPE_COLORS.default;
// 发光球体
const geo = new THREE.SphereGeometry(radius, 24, 24);
const mat = new THREE.MeshPhongMaterial({
color,
emissive: color,
emissiveIntensity: 0.4,
transparent: true,
opacity: 0.85,
shininess: 80,
});
const mesh = new THREE.Mesh(geo, mat);
mesh.position.copy(ent.position);
mesh.name = ent.id;
mesh.userData = { type: 'entity', index: i, data: ent, layer: 'knowledge' };
layer.add(mesh);
// 外部辉光环
const glowGeo = new THREE.SphereGeometry(radius * 1.6, 16, 16);
const glowMat = new THREE.MeshBasicMaterial({
color,
transparent: true,
opacity: 0.08,
blending: THREE.AdditiveBlending,
side: THREE.BackSide,
});
const glowMesh = new THREE.Mesh(glowGeo, glowMat);
glowMesh.position.copy(ent.position);
layer.add(glowMesh);
this._interactableObjects.push({
type: 'entity',
index: i,
data: ent,
layer: 'knowledge',
object: mesh,
});
}
// 关系连线
const linePositions = [];
const lineColors = [];
for (const rel of relations) {
const src = entities.find(e => e.id === rel.source);
const tgt = entities.find(e => e.id === rel.target);
if (!src || !tgt) continue;
linePositions.push(src.position.x, src.position.y, src.position.z);
linePositions.push(tgt.position.x, tgt.position.y, tgt.position.z);
const c1 = new THREE.Color(ENTITY_TYPE_COLORS[src.type] || 0xffffff);
const c2 = new THREE.Color(ENTITY_TYPE_COLORS[tgt.type] || 0xffffff);
lineColors.push(c1.r, c1.g, c1.b);
lineColors.push(c2.r, c2.g, c2.b);
}
if (linePositions.length > 0) {
const geo = new THREE.BufferGeometry();
geo.setAttribute('position', new THREE.Float32BufferAttribute(linePositions, 3));
geo.setAttribute('color', new THREE.Float32BufferAttribute(lineColors, 3));
const mat = new THREE.LineBasicMaterial({
vertexColors: true,
transparent: true,
opacity: 0.25,
blending: THREE.AdditiveBlending,
depthWrite: false,
});
const lines = new THREE.LineSegments(geo, mat);
lines.name = 'knowledge-relations';
layer.add(lines);
}
}
/* ---- 3. 目标晶体 ---- */
_buildGoalCrystals() {
const goals = this._data.goals;
const layer = this._layers.goals;
const self = this;
goals.forEach((goal, gi) => {
// 父目标位置 - 环形分布
const parentAngle = (gi / goals.length) * Math.PI * 2;
const parentRadius = 15;
const parentPos = new THREE.Vector3(
Math.cos(parentAngle) * parentRadius,
randomRange(-3, 3),
Math.sin(parentAngle) * parentRadius
);
self._createCrystalMesh(goal, parentPos, layer, gi, 1.5);
// 子目标围绕父目标旋转
if (goal.children) {
goal.children.forEach((child, ci) => {
const childAngle = (ci / goal.children.length) * Math.PI * 2;
const childRadius = 3;
const childPos = new THREE.Vector3(
parentPos.x + Math.cos(childAngle) * childRadius,
parentPos.y + randomRange(-1, 1),
parentPos.z + Math.sin(childAngle) * childRadius
);
self._createCrystalMesh(child, childPos, layer, gi * 100 + ci, 0.8, parentPos);
});
}
});
}
/** 创建单个晶体网格 */
_createCrystalMesh(goal, position, layer, index, scale = 1.0, orbitCenter = null) {
const color = GOAL_STATE_COLORS[goal.state] || 0x888888;
// 根据进度选择多面体
let geometry;
if (goal.progress >= 0.8) {
geometry = new THREE.IcosahedronGeometry(scale, 0);
} else {
geometry = new THREE.DodecahedronGeometry(scale, 0);
}
const material = new THREE.MeshPhongMaterial({
color,
emissive: color,
emissiveIntensity: 0.3,
transparent: true,
opacity: 0.3 + goal.progress * 0.6,
shininess: 120,
side: THREE.DoubleSide,
wireframe: false,
});
const mesh = new THREE.Mesh(geometry, material);
mesh.position.copy(position);
mesh.name = goal.id;
mesh.userData = {
type: 'goal',
index,
data: goal,
layer: 'goals',
orbitCenter,
orbitAngle: Math.random() * Math.PI * 2,
orbitSpeed: 0.2 + Math.random() * 0.3,
rotationSpeed: new THREE.Vector3(
randomRange(-0.5, 0.5),
randomRange(-0.5, 0.5),
randomRange(-0.5, 0.5)
),
};
layer.add(mesh);
// 晶体边框线
const wireGeo = new THREE.EdgesGeometry(geometry);
const wireMat = new THREE.LineBasicMaterial({
color,
transparent: true,
opacity: 0.4,
});
const wireframe = new THREE.LineSegments(wireGeo, wireMat);
mesh.add(wireframe);
// 添加光晕
const glowGeo = new THREE.SphereGeometry(scale * 1.5, 12, 12);
const glowMat = new THREE.MeshBasicMaterial({
color,
transparent: true,
opacity: 0.06,
blending: THREE.AdditiveBlending,
side: THREE.BackSide,
});
const glow = new THREE.Mesh(glowGeo, glowMat);
mesh.add(glow);
this._interactableObjects.push({
type: 'goal',
index,
data: goal,
layer: 'goals',
object: mesh,
});
return mesh;
}
/* ---- 4. 技能光环 ---- */
_buildSkillHalos() {
const skills = this._data.skills;
const layer = this._layers.skills;
skills.forEach((skill, i) => {
const angle = (i / skills.length) * Math.PI * 2;
const ringRadius = 12;
const pos = new THREE.Vector3(
Math.cos(angle) * ringRadius,
randomRange(-2, 2),
Math.sin(angle) * ringRadius
);
// 熟练度 -> 圆环半径
const torusRadius = 0.8 + skill.proficiency * 1.5;
const tubeRadius = 0.05 + skill.proficiency * 0.12;
const color = SKILL_TYPE_COLORS[skill.type] || SKILL_TYPE_COLORS.default;
const geo = new THREE.TorusGeometry(torusRadius, tubeRadius, 16, 48);
const mat = new THREE.MeshPhongMaterial({
color,
emissive: color,
emissiveIntensity: 0.5,
transparent: true,
opacity: 0.7,
shininess: 100,
});
const mesh = new THREE.Mesh(geo, mat);
mesh.position.copy(pos);
mesh.rotation.x = Math.PI / 2 + randomRange(-0.3, 0.3);
mesh.name = skill.id;
mesh.userData = {
type: 'skill',
index: i,
data: skill,
layer: 'skills',
pulsePhase: Math.random() * Math.PI * 2,
isPulsing: false,
baseEmissiveIntensity: 0.5,
};
layer.add(mesh);
// 外部辉光
const glowGeo = new THREE.TorusGeometry(torusRadius, tubeRadius * 3, 16, 48);
const glowMat = new THREE.MeshBasicMaterial({
color,
transparent: true,
opacity: 0.08,
blending: THREE.AdditiveBlending,
side: THREE.DoubleSide,
});
const glow = new THREE.Mesh(glowGeo, glowMat);
mesh.add(glow);
this._interactableObjects.push({
type: 'skill',
index: i,
data: skill,
layer: 'skills',
object: mesh,
});
});
}
/* ---- 5. 情绪粒子 ---- */
_buildEmotionParticles() {
const emotions = this._data.emotions;
const layer = this._layers.emotions;
emotions.forEach(emotion => {
const particleCount = Math.floor(emotion.intensity * 300);
if (particleCount < 5) return;
const positions = new Float32Array(particleCount * 3);
const sizes = new Float32Array(particleCount);
const alphas = new Float32Array(particleCount);
const colors = new Float32Array(particleCount * 3);
const baseColor = new THREE.Color(EMOTION_COLORS[emotion.type] || 0xffffff);
for (let i = 0; i < particleCount; i++) {
const i3 = i * 3;
positions[i3] = randomRange(-20, 20);
positions[i3 + 1] = randomRange(-15, 15);
positions[i3 + 2] = randomRange(-20, 20);
sizes[i] = randomRange(1, 4);
alphas[i] = randomRange(0.3, 0.9);
colors[i3] = baseColor.r + randomRange(-0.05, 0.05);
colors[i3 + 1] = baseColor.g + randomRange(-0.05, 0.05);
colors[i3 + 2] = baseColor.b + randomRange(-0.05, 0.05);
}
const geo = new THREE.BufferGeometry();
geo.setAttribute('position', new THREE.BufferAttribute(positions, 3));
geo.setAttribute('size', new THREE.BufferAttribute(sizes, 1));
geo.setAttribute('alpha', new THREE.BufferAttribute(alphas, 1));
geo.setAttribute('customColor', new THREE.BufferAttribute(colors, 3));
const mat = new THREE.ShaderMaterial({
vertexShader: PARTICLE_VERTEX_SHADER,
fragmentShader: PARTICLE_FRAGMENT_SHADER,
transparent: true,
blending: THREE.AdditiveBlending,
depthWrite: false,
});
const points = new THREE.Points(geo, mat);
points.name = `emotion-${emotion.type}`;
points.userData = {
layer: 'emotions',
emotionType: emotion.type,
intensity: emotion.intensity,
particleCount,
};
layer.add(points);
});
}
/* ---- 6. 反思之镜 ---- */
_buildReflectionMirror() {
const layer = this._layers.mirror;
// 主镜面平面
const mirrorGeo = new THREE.PlaneGeometry(40, 40, 64, 64);
const mirrorMat = new THREE.ShaderMaterial({
vertexShader: WATER_VERTEX_SHADER,
fragmentShader: WATER_FRAGMENT_SHADER,
uniforms: {
time: { value: 0 },
reflectivity: { value: 0.4 },
},
transparent: true,
side: THREE.DoubleSide,
depthWrite: false,
});
const mirror = new THREE.Mesh(mirrorGeo, mirrorMat);
mirror.rotation.x = -Math.PI / 2;
mirror.position.y = -12;
mirror.name = 'reflection-mirror';
mirror.userData = { layer: 'mirror' };
layer.add(mirror);
// 边框光圈
const ringGeo = new THREE.RingGeometry(19.5, 20.5, 64);
const ringMat = new THREE.MeshBasicMaterial({
color: 0x4488ff,
transparent: true,
opacity: 0.15,
side: THREE.DoubleSide,
blending: THREE.AdditiveBlending,
});
const ring = new THREE.Mesh(ringGeo, ringMat);
ring.rotation.x = -Math.PI / 2;
ring.position.y = -11.95;
layer.add(ring);
this._mirrorMaterial = mirrorMat;
}
/* ---- 7. 社交网络 ---- */
_buildSocialNetwork() {
const users = this._data.users;
const connections = this._data.connections;
const layer = this._layers.social;
const userPositions = {};
// 用户节点
users.forEach((user, i) => {
const angle = (i / users.length) * Math.PI * 2;
const r = 8;
const pos = new THREE.Vector3(
Math.cos(angle) * r,
randomRange(-3, 3) + 10,
Math.sin(angle) * r
);
userPositions[user.id] = pos;
// 人脸图标 - 用球体模拟
const sphereRadius = 0.5 + user.closeness * 0.5;
const geo = new THREE.SphereGeometry(sphereRadius, 24, 24);
const mat = new THREE.MeshPhongMaterial({
color: 0x42A5F5,
emissive: 0x1565C0,
emissiveIntensity: 0.3,
transparent: true,
opacity: 0.8,
});
const mesh = new THREE.Mesh(geo, mat);
mesh.position.copy(pos);
mesh.name = user.id;
mesh.userData = { type: 'user', index: i, data: user, layer: 'social' };
layer.add(mesh);
// 眼睛(两个小球)
const eyeGeo = new THREE.SphereGeometry(sphereRadius * 0.12, 8, 8);
const eyeMat = new THREE.MeshBasicMaterial({ color: 0xffffff });
const eyeL = new THREE.Mesh(eyeGeo, eyeMat);
eyeL.position.set(-sphereRadius * 0.3, sphereRadius * 0.15, sphereRadius * 0.85);
mesh.add(eyeL);
const eyeR = new THREE.Mesh(eyeGeo, eyeMat);
eyeR.position.set(sphereRadius * 0.3, sphereRadius * 0.15, sphereRadius * 0.85);
mesh.add(eyeR);
// 瞳孔
const pupilGeo = new THREE.SphereGeometry(sphereRadius * 0.06, 8, 8);
const pupilMat = new THREE.MeshBasicMaterial({ color: 0x000000 });
const pupilL = new THREE.Mesh(pupilGeo, pupilMat);
pupilL.position.z = sphereRadius * 0.02;
eyeL.add(pupilL);
const pupilR = new THREE.Mesh(pupilGeo, pupilMat);
pupilR.position.z = sphereRadius * 0.02;
eyeR.add(pupilR);
// 光晕
const glowGeo = new THREE.SphereGeometry(sphereRadius * 2, 12, 12);
const glowMat = new THREE.MeshBasicMaterial({
color: 0x42A5F5,
transparent: true,
opacity: 0.05,
blending: THREE.AdditiveBlending,
side: THREE.BackSide,
});
const glow = new THREE.Mesh(glowGeo, glowMat);
mesh.add(glow);
this._interactableObjects.push({
type: 'user',
index: i,
data: user,
layer: 'social',
object: mesh,
});
});
// 关系连线
const linePositions = [];
connections.forEach(conn => {
const src = userPositions[conn.source];
const tgt = userPositions[conn.target];
if (!src || !tgt) return;
linePositions.push(src.x, src.y, src.z);
linePositions.push(tgt.x, tgt.y, tgt.z);
});
if (linePositions.length > 0) {
const geo = new THREE.BufferGeometry();
geo.setAttribute('position', new THREE.Float32BufferAttribute(linePositions, 3));
const mat = new THREE.LineBasicMaterial({
color: 0x64B5F6,
transparent: true,
opacity: 0.3,
blending: THREE.AdditiveBlending,
});
const lines = new THREE.LineSegments(geo, mat);
lines.name = 'social-connections';
layer.add(lines);
}
}
/* =========================================================================
* 动画循环
* ========================================================================= */
/** 启动动画循环 */
_startAnimation() {
this._lastFpsTime = performance.now();
this._animate();
}
/** 主动画帧 */
_animate() {
this._animationId = requestAnimationFrame(this._animate);
if (this._paused) return;
const delta = this._clock.getDelta() * this._animationSpeed;
const elapsed = this._clock.elapsedTime * this._animationSpeed;
// 更新 FPS
this._frameCount++;
const now = performance.now();
if (now - this._lastFpsTime >= 1000) {
const fps = Math.round(this._frameCount * 1000 / (now - this._lastFpsTime));
const fpsEl = this.shadowRoot.querySelector('#fps-counter');
if (fpsEl) fpsEl.textContent = `${fps} FPS`;
this._frameCount = 0;
this._lastFpsTime = now;
}
// 更新各层动画
this._updateMemoryAnimation(elapsed, delta);
this._updateKnowledgeAnimation(elapsed, delta);
this._updateGoalAnimation(elapsed, delta);
this._updateSkillAnimation(elapsed, delta);
this._updateEmotionAnimation(elapsed, delta);
this._updateMirrorAnimation(elapsed, delta);
this._updateSocialAnimation(elapsed, delta);
this._updateExplosions(delta);
this._updateCoreLight(elapsed);
this._updateBackgroundStars(elapsed);
// 渲染
this._renderer.render(this._scene, this._camera);
}
/** 记忆星云动画 - 星辰呼吸闪烁(着色器内处理),中心辉光脉动 */
_updateMemoryAnimation(elapsed, delta) {
const nebula = this._layers.memory?.getObjectByName('memory-nebula');
if (nebula?.material?.uniforms) {
nebula.material.uniforms.time.value = elapsed;
}
const coreGlow = this._layers.memory?.getObjectByName('memory-core-glow');
if (coreGlow) {
coreGlow.scale.setScalar(1 + 0.15 * Math.sin(elapsed * 1.5));
}
}
/** 知识图谱动画 - 节点微弱脉动 */
_updateKnowledgeAnimation(elapsed, delta) {
this._layers.knowledge?.children.forEach(child => {
if (child.userData?.type === 'entity') {
child.material.emissiveIntensity = 0.3 + 0.15 * Math.sin(elapsed * 2 + child.position.x);
}
});
}
/** 目标晶体动画 - 旋转和子目标轨道运动 */
_updateGoalAnimation(elapsed, delta) {
this._layers.goals?.children.forEach(child => {
if (!child.userData?.type) return;
// 自身旋转
const rs = child.userData.rotationSpeed;
if (rs) {
child.rotation.x += rs.x * delta;
child.rotation.y += rs.y * delta;
child.rotation.z += rs.z * delta;
}
// 子目标轨道运动
const center = child.userData.orbitCenter;
if (center) {
const angle = (child.userData.orbitAngle || 0) + elapsed * (child.userData.orbitSpeed || 0.2);
const orbitR = 3;
child.position.x = center.x + Math.cos(angle) * orbitR;
child.position.z = center.z + Math.sin(angle) * orbitR;
child.position.y = center.y + Math.sin(angle * 0.7) * 0.5;
}
});
}
/** 技能光环动画 - 激活脉冲效果 */
_updateSkillAnimation(elapsed, delta) {
this._layers.skills?.children.forEach(child => {
if (!child.userData?.type) return;
const phase = child.userData.pulsePhase || 0;
// 持续微弱旋转
child.rotation.z += 0.3 * delta;
// 脉冲效果
if (child.userData.isPulsing) {
child.material.emissiveIntensity = 0.8 + 0.5 * Math.sin(elapsed * 5 + phase);
child.scale.setScalar(1 + 0.1 * Math.sin(elapsed * 3 + phase));
} else {
child.material.emissiveIntensity = child.userData.baseEmissiveIntensity + 0.1 * Math.sin(elapsed * 1.5 + phase);
}
});
}
/** 情绪粒子动画 - 不同情绪不同运动模式 */
_updateEmotionAnimation(elapsed, delta) {
this._layers.emotions?.children.forEach(child => {
if (!child.userData?.emotionType) return;
const positions = child.geometry.attributes.position.array;
const count = child.userData.particleCount;
const type = child.userData.emotionType;
for (let i = 0; i < count; i++) {
const i3 = i * 3;
switch (type) {
case 'joy':
// 金色上浮粒子
positions[i3 + 1] += delta * 2;
if (positions[i3 + 1] > 15) positions[i3 + 1] = -15;
positions[i3] += Math.sin(elapsed + i) * delta * 0.3;
break;
case 'sadness':
// 蓝色下沉粒子
positions[i3 + 1] -= delta * 1.5;
if (positions[i3 + 1] < -15) positions[i3 + 1] = 15;
positions[i3] += Math.cos(elapsed * 0.5 + i) * delta * 0.2;
break;
case 'anger':
// 红色快速粒子
positions[i3] += Math.sin(elapsed * 3 + i * 0.1) * delta * 3;
positions[i3 + 1] += Math.cos(elapsed * 4 + i * 0.2) * delta * 2;
positions[i3 + 2] += Math.sin(elapsed * 2 + i * 0.3) * delta * 2.5;
// 边界约束
for (let j = 0; j < 3; j++) {
if (Math.abs(positions[i3 + j]) > 20) positions[i3 + j] *= -0.9;
}
break;
case 'curiosity':
// 绿色旋转粒子
const cx = positions[i3], cz = positions[i3 + 2];
const dist = Math.sqrt(cx * cx + cz * cz) || 1;
positions[i3] += (-cz / dist) * delta * 1.5;
positions[i3 + 2] += (cx / dist) * delta * 1.5;
positions[i3 + 1] += Math.sin(elapsed + i * 0.5) * delta * 0.5;
break;
default:
// 默认缓慢漂浮
positions[i3] += Math.sin(elapsed + i) * delta * 0.3;
positions[i3 + 1] += Math.cos(elapsed * 0.7 + i) * delta * 0.2;
break;
}
}
child.geometry.attributes.position.needsUpdate = true;
});
}
/** 反思之镜动画 - 水波纹效果(着色器处理) */
_updateMirrorAnimation(elapsed, delta) {
if (this._mirrorMaterial?.uniforms) {
this._mirrorMaterial.uniforms.time.value = elapsed;
}
}
/** 社交网络动画 - 节点微弱漂浮 */
_updateSocialAnimation(elapsed, delta) {
this._layers.social?.children.forEach(child => {
if (child.userData?.type === 'user') {
const base = child.position.clone();
child.position.y += Math.sin(elapsed * 0.8 + child.position.x) * delta * 0.2;
}
});
}
/** 更新爆炸特效 */
_updateExplosions(delta) {
for (let i = this._explosions.length - 1; i >= 0; i--) {
const exp = this._explosions[i];
exp.update(delta);
if (!exp.active) {
this._layers.goals.remove(exp.mesh);
exp.dispose();
this._explosions.splice(i, 1);
}
}
}
/** 核心光源呼吸效果 */
_updateCoreLight(elapsed) {
if (this._coreLight) {
this._coreLight.intensity = 1.5 + 0.5 * Math.sin(elapsed * 0.8);
}
}
/** 背景星空缓慢旋转 */
_updateBackgroundStars(elapsed) {
if (this._bgStars) {
this._bgStars.rotation.y = elapsed * 0.01;
this._bgStars.rotation.x = Math.sin(elapsed * 0.005) * 0.02;
}
}
/* =========================================================================
* 交互功能
* ========================================================================= */
/** 鼠标点击事件 */
_onClick(event) {
const rect = this._renderer.domElement.getBoundingClientRect();
this._mouse.x = ((event.clientX - rect.left) / rect.width) * 2 - 1;
this._mouse.y = -((event.clientY - rect.top) / rect.height) * 2 + 1;
this._raycaster.setFromCamera(this._mouse, this._camera);
const intersects = this._raycaster.intersectObjects(this._scene.children, true);
// 关闭右键菜单
this._hideContextMenu();
if (intersects.length > 0) {
const hit = intersects[0].object;
const userData = hit.userData;
if (userData && userData.type) {
this._selectedObject = userData;
this._showTooltipFor(userData, event);
return;
}
}
this._hideTooltip();
this._selectedObject = null;
}
/** 鼠标移动事件(悬停检测) */
_onMouseMove(event) {
const rect = this._renderer.domElement.getBoundingClientRect();
this._mouse.x = ((event.clientX - rect.left) / rect.width) * 2 - 1;
this._mouse.y = -((event.clientY - rect.top) / rect.height) * 2 + 1;
// 更新 tooltip 位置(如果已显示)
const tooltip = this.shadowRoot.querySelector('#tooltip');
if (tooltip.classList.contains('visible')) {
tooltip.style.left = (event.clientX - rect.left + 15) + 'px';
tooltip.style.top = (event.clientY - rect.top - 10) + 'px';
}
// 悬停高亮(节流处理)
if (this._hoverThrottle) return;
this._hoverThrottle = true;
setTimeout(() => { this._hoverThrottle = false; }, 100);
this._raycaster.setFromCamera(this._mouse, this._camera);
const intersects = this._raycaster.intersectObjects(this._scene.children, true);
// 取消之前的高亮
if (this._hoveredObject && this._hoveredObject !== this._selectedObject) {
this._unhighlightObject(this._hoveredObject);
}
if (intersects.length > 0) {
const hit = intersects[0].object;
const userData = hit.userData;
if (userData && userData.type && userData !== this._selectedObject) {
this._hoveredObject = userData;
this._highlightObject(userData);
}
} else {
this._hoveredObject = null;
}
}
/** 右键菜单事件 */
_onContextMenu(event) {
event.preventDefault();
const rect = this._renderer.domElement.getBoundingClientRect();
this._mouse.x = ((event.clientX - rect.left) / rect.width) * 2 - 1;
this._mouse.y = -((event.clientY - rect.top) / rect.height) * 2 + 1;
this._raycaster.setFromCamera(this._mouse, this._camera);
const intersects = this._raycaster.intersectObjects(this._scene.children, true);
if (intersects.length > 0) {
const hit = intersects[0].object;
if (hit.userData?.type) {
this._contextTarget = hit.userData;
this._showContextMenu(event.clientX - rect.left, event.clientY - rect.top);
return;
}
}
this._hideContextMenu();
}
/** 键盘快捷键 */
_onKeyDown(event) {
const key = event.key;
// 忽略搜索框内的输入
const searchBox = this.shadowRoot.querySelector('#search-box');
if (searchBox && this.shadowRoot.activeElement === searchBox) return;
switch (key) {
case '1': this._switchView(VIEW_MODES.MEMORY); break;
case '2': this._switchView(VIEW_MODES.KNOWLEDGE); break;
case '3': this._switchView(VIEW_MODES.GOALS); break;
case '4': this._switchView(VIEW_MODES.SKILLS); break;
case '5': this._switchView(VIEW_MODES.EMOTIONS); break;
case '6': this._switchView(VIEW_MODES.SOCIAL); break;
case '7': this._switchView(VIEW_MODES.PANORAMIC); break;
case 'h':
case 'H':
this._togglePanel();
break;
case 'r':
case 'R':
this._orbitControls?.reset();
break;
case ' ':
event.preventDefault();
this._paused = !this._paused;
break;
case 'Escape':
this._hideContextMenu();
this._hideTooltip();
break;
}
}
/** 窗口大小变化处理 */
_onResize() {
const container = this.shadowRoot.querySelector('#canvas-container');
if (!container || !this._renderer || !this._camera) return;
const w = container.clientWidth;
const h = container.clientHeight;
this._camera.aspect = w / h;
this._camera.updateProjectionMatrix();
this._renderer.setSize(w, h);
}
/* =========================================================================
* UI 交互
* ========================================================================= */
/** 初始化 UI 事件绑定 */
_initUIBindings() {
const root = this.shadowRoot;
// 视图切换按钮
root.querySelectorAll('.view-btn').forEach(btn => {
btn.addEventListener('click', () => {
this._switchView(btn.dataset.view);
});
});
// 动画速度滑块
const speedSlider = root.querySelector('#speed-slider');
const speedValue = root.querySelector('#speed-value');
if (speedSlider) {
speedSlider.addEventListener('input', () => {
this._animationSpeed = parseFloat(speedSlider.value);
if (speedValue) speedValue.textContent = `${this._animationSpeed.toFixed(1)}x`;
});
}
// 粒子密度滑块
const densitySlider = root.querySelector('#density-slider');
const densityValue = root.querySelector('#density-value');
if (densitySlider) {
densitySlider.addEventListener('input', () => {
const val = parseInt(densitySlider.value);
if (densityValue) densityValue.textContent = `${val}%`;
// 更新星云亮度作为密度的代理指标
const nebula = this._layers.memory?.getObjectByName('memory-nebula');
if (nebula?.material?.uniforms) {
nebula.material.uniforms.globalBrightness.value = val / 100;
}
});
}
// 元素可见性开关
root.querySelectorAll('.toggle-btn').forEach(btn => {
btn.addEventListener('click', () => {
btn.classList.toggle('on');
const layerName = btn.dataset.layer;
if (this._layers[layerName]) {
this._layers[layerName].visible = btn.classList.contains('on');
}
});
});
// 搜索框
const searchBox = root.querySelector('#search-box');
if (searchBox) {
searchBox.addEventListener('input', () => {
this._searchQuery = searchBox.value.trim().toLowerCase();
this._applySearch();
});
}
// 切换面板按钮
const toggleBtn = root.querySelector('#toggle-panel-btn');
if (toggleBtn) {
toggleBtn.addEventListener('click', () => this._togglePanel());
}
// 右键菜单项
root.querySelectorAll('.ctx-menu-item').forEach(item => {
item.addEventListener('click', () => {
const action = item.dataset.action;
if (this._contextTarget) {
this._handleContextAction(action, this._contextTarget);
}
this._hideContextMenu();
});
});
// 点击画布空白处关闭菜单
this._renderer.domElement.addEventListener('pointerdown', () => {
this._hideContextMenu();
});
// 时间轴
const timeline = root.querySelector('#timeline');
if (timeline) {
timeline.addEventListener('click', (e) => {
const rect = timeline.getBoundingClientRect();
const ratio = (e.clientX - rect.left) / rect.width;
const progress = root.querySelector('#timeline-progress');
if (progress) progress.style.width = (ratio * 100) + '%';
this._applyTimelineSnapshot(ratio);
});
}
}
/** 初始化控制面板(placeholder,被 _initUIBindings 替代) */
_initControls() {
// 已在 _initUIBindings 中实现
}
/** 切换视图模式 */
_switchView(mode) {
this._currentView = mode;
// 更新按钮状态
this.shadowRoot.querySelectorAll('.view-btn').forEach(btn => {
btn.classList.toggle('active', btn.dataset.view === mode);
});
// 根据视图模式切换可见性
const layerNames = ['memory', 'knowledge', 'goals', 'skills', 'emotions', 'mirror', 'social'];
if (mode === VIEW_MODES.ALL || mode === VIEW_MODES.PANORAMIC) {
layerNames.forEach(name => {
if (this._layers[name]) this._layers[name].visible = true;
});
} else {
layerNames.forEach(name => {
if (this._layers[name]) {
this._layers[name].visible = (name === mode);
}
});
// 在特定视图中也显示反思之镜
if (this._layers.mirror && mode !== VIEW_MODES.MEMORY) {
// 保持镜面可见
}
}
// 更新可见性开关按钮
this.shadowRoot.querySelectorAll('.toggle-btn').forEach(btn => {
const layerName = btn.dataset.layer;
if (layerName && this._layers[layerName]) {
btn.classList.toggle('on', this._layers[layerName].visible);
}
});
}
/** 切换控制面板显隐 */
_togglePanel() {
const panel = this.shadowRoot.querySelector('#control-panel');
const btn = this.shadowRoot.querySelector('#toggle-panel-btn');
if (panel) {
panel.classList.toggle('hidden');
}
}
/** 显示悬浮框 */
_showTooltipFor(userData, event) {
const tooltip = this.shadowRoot.querySelector('#tooltip');
const titleEl = this.shadowRoot.querySelector('#tooltip-title');
const contentEl = this.shadowRoot.querySelector('#tooltip-content');
if (!tooltip || !titleEl || !contentEl) return;
const rect = this._renderer.domElement.getBoundingClientRect();
let title = '';
let content = '';
switch (userData.type) {
case 'memory': {
const mem = userData.data;
title = mem.title;
const sentimentLabel = mem.sentiment > 0.3 ? '积极' : mem.sentiment < -0.3 ? '消极' : '中性';
content = `
${mem.content}
重要性: ${(mem.importance * 100).toFixed(0)}% | 访问: ${mem.accessCount}次
情绪: ${sentimentLabel} (${mem.sentiment.toFixed(2)})
记忆 ${sentimentLabel}
`;
break;
}
case 'entity': {
const ent = userData.data;
title = ent.name;
content = `
${ent.description}
类型: ${ent.type} | 连接数: ${ent.connections}
${ent.type}
`;
break;
}
case 'goal': {
const goal = userData.data;
title = goal.title;
const stateLabel = { completed: '已完成', active: '进行中', paused: '已暂停', abandoned: '已放弃' };
content = `
状态: ${stateLabel[goal.state] || goal.state}
进度: ${(goal.progress * 100).toFixed(0)}%
子目标: ${goal.children ? goal.children.length : 0}个
${goal.state}
`;
break;
}
case 'skill': {
const skill = userData.data;
title = skill.name;
content = `
类型: ${skill.type}
熟练度: ${(skill.proficiency * 100).toFixed(0)}%
${skill.type}
`;
break;
}
case 'user': {
const user = userData.data;
title = user.name;
content = `
角色: ${user.role}
亲密度: ${(user.closeness * 100).toFixed(0)}%
${user.role}
`;
break;
}
}
titleEl.textContent = title;
contentEl.innerHTML = content;
tooltip.style.left = (event.clientX - rect.left + 15) + 'px';
tooltip.style.top = (event.clientY - rect.top - 10) + 'px';
tooltip.classList.add('visible');
}
/** 隐藏悬浮框 */
_hideTooltip() {
const tooltip = this.shadowRoot.querySelector('#tooltip');
if (tooltip) tooltip.classList.remove('visible');
}
/** 显示右键菜单 */
_showContextMenu(x, y) {
const menu = this.shadowRoot.querySelector('#context-menu');
if (!menu) return;
menu.style.left = x + 'px';
menu.style.top = y + 'px';
menu.classList.add('visible');
}
/** 隐藏右键菜单 */
_hideContextMenu() {
const menu = this.shadowRoot.querySelector('#context-menu');
if (menu) menu.classList.remove('visible');
}
/** 处理右键菜单动作 */
_handleContextAction(action, userData) {
switch (action) {
case 'focus':
if (userData.object) {
this._orbitControls?.focusOn(userData.object.position, 10);
}
break;
case 'highlight':
this._highlightSameType(userData.type);
break;
case 'hide':
if (this._layers[userData.layer]) {
this._layers[userData.layer].visible = false;
}
break;
case 'isolate':
this._isolateType(userData.type);
break;
case 'details':
this._showTooltipFor(userData, { clientX: window.innerWidth / 2, clientY: window.innerHeight / 2 });
break;
}
}
/** 高亮同类元素 */
_highlightSameType(type) {
this._interactableObjects.forEach(obj => {
if (obj.object && obj.object.material) {
if (obj.type === type) {
obj.object.material.emissiveIntensity = 1.0;
} else {
obj.object.material.emissiveIntensity = 0.1;
}
}
});
// 3秒后恢复
setTimeout(() => {
this._interactableObjects.forEach(obj => {
if (obj.object && obj.object.material) {
obj.object.material.emissiveIntensity = 0.5;
}
});
}, 3000);
}
/** 隔离特定类型 */
_isolateType(type) {
const layerMap = {
memory: 'memory',
entity: 'knowledge',
goal: 'goals',
skill: 'skills',
user: 'social',
};
const targetLayer = layerMap[type];
Object.keys(this._layers).forEach(name => {
if (this._layers[name]) {
this._layers[name].visible = (name === targetLayer);
}
});
}
/** 高亮特定对象 */
_highlightObject(userData) {
if (userData.object && userData.object.material) {
userData.object.material.emissiveIntensity = 1.0;
}
}
/** 取消高亮对象 */
_unhighlightObject(userData) {
if (userData.object && userData.object.material) {
const baseIntensity = userData.data?.proficiency !== undefined ? 0.5 : 0.4;
userData.object.material.emissiveIntensity = baseIntensity;
}
}
/** 搜索应用 */
_applySearch() {
const query = this._searchQuery;
this._interactableObjects.forEach(obj => {
if (!obj.object || !obj.object.material) return;
if (!query) {
obj.object.material.opacity = obj.type === 'entity' ? 0.85 : 0.7;
return;
}
const data = obj.data;
let text = '';
switch (obj.type) {
case 'memory': text = data.title + ' ' + (data.content || ''); break;
case 'entity': text = data.name + ' ' + data.type; break;
case 'goal': text = data.title + ' ' + data.state; break;
case 'skill': text = data.name + ' ' + data.type; break;
case 'user': text = data.name + ' ' + data.role; break;
}
if (text.toLowerCase().includes(query)) {
obj.object.material.opacity = 1.0;
obj.object.material.emissiveIntensity = 1.0;
} else {
obj.object.material.opacity = 0.1;
obj.object.material.emissiveIntensity = 0.05;
}
});
}
/** 应用时间轴快照 */
_applyTimelineSnapshot(ratio) {
// 时间轴进度反馈(演示效果)
const progress = this.shadowRoot.querySelector('#timeline-progress');
if (progress) progress.style.width = (ratio * 100) + '%';
// 根据时间轴位置调整元素的可见性和状态
// 这里模拟历史回放效果
this._data.memories.forEach((mem, i) => {
const visible = (i / this._data.memories.length) < ratio;
// 通过改变大小来模拟时间流逝
});
}
/** 触发目标完成爆炸特效 */
_triggerGoalExplosion(position) {
const explosion = new ParticleExplosion(position, 0x00E676, 300, 2.5);
this._layers.goals.add(explosion.mesh);
this._explosions.push(explosion);
}
/** 隐藏加载画面 */
_hideLoadingScreen() {
const screen = this.shadowRoot.querySelector('#loading-screen');
if (screen) {
setTimeout(() => {
screen.classList.add('fade-out');
setTimeout(() => screen.remove(), 800);
}, 500);
}
}
/** 更新统计数字 */
_updateStats() {
const root = this.shadowRoot;
const setVal = (id, val) => {
const el = root.querySelector(`#${id}`);
if (el) el.textContent = val;
};
setVal('stat-memories', this._data.memories.length);
setVal('stat-entities', this._data.entities.length);
setVal('stat-goals', this._data.goals.reduce((sum, g) => sum + 1 + (g.children?.length || 0), 0));
setVal('stat-skills', this._data.skills.length);
setVal('stat-connections', this._data.relations.length + this._data.connections.length);
}
/* =========================================================================
* 资源清理
* ========================================================================= */
/** 销毁所有资源 */
_dispose() {
if (this._animationId) {
cancelAnimationFrame(this._animationId);
}
// 清理爆炸特效
this._explosions.forEach(e => e.dispose());
this._explosions = [];
// 清理场景
if (this._scene) {
this._scene.traverse(obj => {
if (obj.geometry) obj.geometry.dispose();
if (obj.material) {
if (Array.isArray(obj.material)) {
obj.material.forEach(m => m.dispose());
} else {
obj.material.dispose();
}
}
});
}
// 清理渲染器
if (this._renderer) {
this._renderer.dispose();
}
}
/* =========================================================================
* 公共 API
* ========================================================================= */
/**
* 更新记忆数据
* @param {Array} memories - 记忆数据数组
*/
updateMemories(memories) {
this._data.memories = memories;
// 清理旧的
while (this._layers.memory.children.length) {
const child = this._layers.memory.children[0];
if (child.geometry) child.geometry.dispose();
if (child.material) child.material.dispose();
this._layers.memory.remove(child);
}
this._buildMemoryNebula();
this._updateStats();
}
/**
* 更新知识图谱数据
* @param {Array} entities - 实体数组
* @param {Array} relations - 关系数组
*/
updateKnowledgeGraph(entities, relations) {
this._data.entities = entities;
this._data.relations = relations;
// 重新运行力导向布局
this._forceLayout.clear();
entities.forEach(e => this._forceLayout.addNode(e.id, e.position));
relations.forEach(r => this._forceLayout.addEdge(r.source, r.target, r.weight));
for (let i = 0; i < 50; i++) this._forceLayout.step();
const positions = this._forceLayout.getPositions();
entities.forEach(e => { if (positions[e.id]) e.position.copy(positions[e.id]); });
// 重建
while (this._layers.knowledge.children.length) {
const child = this._layers.knowledge.children[0];
if (child.geometry) child.geometry.dispose();
if (child.material) child.material.dispose();
this._layers.knowledge.remove(child);
}
this._buildKnowledgeGraph();
this._updateStats();
}
/**
* 更新情绪数据
* @param {Array} emotions - 情绪数组
*/
updateEmotions(emotions) {
this._data.emotions = emotions;
while (this._layers.emotions.children.length) {
const child = this._layers.emotions.children[0];
if (child.geometry) child.geometry.dispose();
if (child.material) child.material.dispose();
this._layers.emotions.remove(child);
}
this._buildEmotionParticles();
}
/**
* 标记目标完成并触发爆炸特效
* @param {string} goalId - 目标ID
*/
completeGoal(goalId) {
const goalMesh = this._layers.goals?.getObjectByName(goalId);
if (goalMesh) {
this._triggerGoalExplosion(goalMesh.position);
goalMesh.material.color.setHex(GOAL_STATE_COLORS.completed);
goalMesh.material.emissive.setHex(GOAL_STATE_COLORS.completed);
goalMesh.material.opacity = 1.0;
}
// 更新数据
const findGoal = (goals) => {
for (const g of goals) {
if (g.id === goalId) return g;
if (g.children) {
const found = findGoal(g.children);
if (found) return found;
}
}
return null;
};
const goal = findGoal(this._data.goals);
if (goal) {
goal.state = 'completed';
goal.progress = 1.0;
}
}
/**
* 获取当前视图模式
* @returns {string}
*/
getViewMode() {
return this._currentView;
}
/**
* 切换暂停/播放
* @param {boolean} [paused] - 可选,指定暂停状态
*/
togglePause(paused) {
this._paused = paused !== undefined ? paused : !this._paused;
}
/**
* 导出当前状态快照
* @returns {Object} 包含所有可视化数据的快照对象
*/
exportSnapshot() {
return {
timestamp: Date.now(),
viewMode: this._currentView,
memories: this._data.memories.map(m => ({ ...m, position: { x: m.position.x, y: m.position.y, z: m.position.z } })),
emotions: [...this._data.emotions],
goals: JSON.parse(JSON.stringify(this._data.goals)),
};
}
}
/* =========================================================================
* 注册自定义元素
* ========================================================================= */
if (!customElements.get('consciousness-3d-space')) {
customElements.define('consciousness-3d-space', Consciousness3DSpace);
}
/* =========================================================================
* 导出(兼容 ES Module 和全局引用)
* ========================================================================= */
if (typeof module !== 'undefined' && module.exports) {
module.exports = { Consciousness3DSpace };
}