Compare commits
1 Commits
mimo/code/
...
feat/mnemo
| Author | SHA1 | Date | |
|---|---|---|---|
| 564f72b3d4 |
@@ -133,6 +133,9 @@ const SpatialMemory = (() => {
|
|||||||
let _regionMarkers = {};
|
let _regionMarkers = {};
|
||||||
let _memoryObjects = {};
|
let _memoryObjects = {};
|
||||||
let _connectionLines = [];
|
let _connectionLines = [];
|
||||||
|
let _entityLines = []; // entity resolution lines (issue #1167)
|
||||||
|
let _camera = null; // set by setCamera() for LOD culling
|
||||||
|
const ENTITY_LOD_DIST = 50; // hide entity lines when camera > this from midpoint
|
||||||
let _initialized = false;
|
let _initialized = false;
|
||||||
|
|
||||||
// ─── CRYSTAL GEOMETRY (persistent memories) ───────────
|
// ─── CRYSTAL GEOMETRY (persistent memories) ───────────
|
||||||
@@ -252,6 +255,10 @@ const SpatialMemory = (() => {
|
|||||||
_drawConnections(mem.id, mem.connections);
|
_drawConnections(mem.id, mem.connections);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (mem.entity) {
|
||||||
|
_drawEntityLines(mem.id, mem);
|
||||||
|
}
|
||||||
|
|
||||||
_dirty = true;
|
_dirty = true;
|
||||||
saveToStorage();
|
saveToStorage();
|
||||||
console.info('[Mnemosyne] Spatial memory placed:', mem.id, 'in', region.label);
|
console.info('[Mnemosyne] Spatial memory placed:', mem.id, 'in', region.label);
|
||||||
@@ -298,6 +305,77 @@ const SpatialMemory = (() => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ─── ENTITY RESOLUTION LINES (#1167) ──────────────────
|
||||||
|
// Draw lines between crystals that share an entity or are related entities.
|
||||||
|
// Same entity → thin blue line. Related entities → thin purple dashed line.
|
||||||
|
function _drawEntityLines(memId, mem) {
|
||||||
|
if (!mem.entity) return;
|
||||||
|
const src = _memoryObjects[memId];
|
||||||
|
if (!src) return;
|
||||||
|
|
||||||
|
Object.entries(_memoryObjects).forEach(([otherId, other]) => {
|
||||||
|
if (otherId === memId) return;
|
||||||
|
const otherData = other.data;
|
||||||
|
if (!otherData.entity) return;
|
||||||
|
|
||||||
|
let lineType = null;
|
||||||
|
if (otherData.entity === mem.entity) {
|
||||||
|
lineType = 'same_entity';
|
||||||
|
} else if (mem.related_entities && mem.related_entities.includes(otherData.entity)) {
|
||||||
|
lineType = 'related';
|
||||||
|
} else if (otherData.related_entities && otherData.related_entities.includes(mem.entity)) {
|
||||||
|
lineType = 'related';
|
||||||
|
}
|
||||||
|
if (!lineType) return;
|
||||||
|
|
||||||
|
// Deduplicate — only draw from lower ID to higher
|
||||||
|
if (memId > otherId) return;
|
||||||
|
|
||||||
|
const points = [src.mesh.position.clone(), other.mesh.position.clone()];
|
||||||
|
const geo = new THREE.BufferGeometry().setFromPoints(points);
|
||||||
|
let mat;
|
||||||
|
if (lineType === 'same_entity') {
|
||||||
|
mat = new THREE.LineBasicMaterial({ color: 0x4488ff, transparent: true, opacity: 0.35 });
|
||||||
|
} else {
|
||||||
|
mat = new THREE.LineDashedMaterial({ color: 0x9966ff, dashSize: 0.3, gapSize: 0.2, transparent: true, opacity: 0.25 });
|
||||||
|
const line = new THREE.Line(geo, mat);
|
||||||
|
line.computeLineDistances();
|
||||||
|
line.userData = { type: 'entity_line', from: memId, to: otherId, lineType };
|
||||||
|
_scene.add(line);
|
||||||
|
_entityLines.push(line);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const line = new THREE.Line(geo, mat);
|
||||||
|
line.userData = { type: 'entity_line', from: memId, to: otherId, lineType };
|
||||||
|
_scene.add(line);
|
||||||
|
_entityLines.push(line);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function _updateEntityLines() {
|
||||||
|
if (!_camera) return;
|
||||||
|
const camPos = _camera.position;
|
||||||
|
|
||||||
|
_entityLines.forEach(line => {
|
||||||
|
// Compute midpoint of line
|
||||||
|
const posArr = line.geometry.attributes.position.array;
|
||||||
|
const mx = (posArr[0] + posArr[3]) / 2;
|
||||||
|
const my = (posArr[1] + posArr[4]) / 2;
|
||||||
|
const mz = (posArr[2] + posArr[5]) / 2;
|
||||||
|
const dist = camPos.distanceTo(new THREE.Vector3(mx, my, mz));
|
||||||
|
|
||||||
|
if (dist > ENTITY_LOD_DIST) {
|
||||||
|
line.visible = false;
|
||||||
|
} else {
|
||||||
|
line.visible = true;
|
||||||
|
// Fade based on distance
|
||||||
|
const fade = Math.max(0, 1 - (dist / ENTITY_LOD_DIST));
|
||||||
|
const baseOpacity = line.userData.lineType === 'same_entity' ? 0.35 : 0.25;
|
||||||
|
line.material.opacity = baseOpacity * fade;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// ─── REMOVE A MEMORY ─────────────────────────────────
|
// ─── REMOVE A MEMORY ─────────────────────────────────
|
||||||
function removeMemory(memId) {
|
function removeMemory(memId) {
|
||||||
const obj = _memoryObjects[memId];
|
const obj = _memoryObjects[memId];
|
||||||
@@ -317,6 +395,16 @@ const SpatialMemory = (() => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (let i = _entityLines.length - 1; i >= 0; i--) {
|
||||||
|
const line = _entityLines[i];
|
||||||
|
if (line.userData.from === memId || line.userData.to === memId) {
|
||||||
|
if (line.parent) line.parent.remove(line);
|
||||||
|
line.geometry.dispose();
|
||||||
|
line.material.dispose();
|
||||||
|
_entityLines.splice(i, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
delete _memoryObjects[memId];
|
delete _memoryObjects[memId];
|
||||||
_dirty = true;
|
_dirty = true;
|
||||||
saveToStorage();
|
saveToStorage();
|
||||||
@@ -342,6 +430,8 @@ const SpatialMemory = (() => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
_updateEntityLines();
|
||||||
|
|
||||||
Object.values(_regionMarkers).forEach(marker => {
|
Object.values(_regionMarkers).forEach(marker => {
|
||||||
if (marker.ring && marker.ring.material) {
|
if (marker.ring && marker.ring.material) {
|
||||||
marker.ring.material.opacity = 0.1 + Math.sin(now * 0.001) * 0.05;
|
marker.ring.material.opacity = 0.1 + Math.sin(now * 0.001) * 0.05;
|
||||||
@@ -652,6 +742,11 @@ const SpatialMemory = (() => {
|
|||||||
return _selectedId;
|
return _selectedId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ─── CAMERA REFERENCE (for entity line LOD) ─────────
|
||||||
|
function setCamera(camera) {
|
||||||
|
_camera = camera;
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
init, placeMemory, removeMemory, update,
|
init, placeMemory, removeMemory, update,
|
||||||
getMemoryAtPosition, getRegionAtPosition, getMemoriesInRegion, getAllMemories,
|
getMemoryAtPosition, getRegionAtPosition, getMemoriesInRegion, getAllMemories,
|
||||||
|
|||||||
Reference in New Issue
Block a user