Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 | 1x 1x 1x 6x 6x 18x 18x 18x 18x 18x 18x 6x 6x 6x 6x 6x 6x 4x 2x 2x 2x 2x 2x 1x 5x 5x 5x 1x | /**
* Repère du centre d'orbite — repris du mode de navigation 3D de la veille
* (`/home/simon/GIT/static/veille/src/lib/graph3d.js`) :
*
* - trois axes fins + un anneau matérialisent le **point autour duquel tourne la
* caméra** (la cible d'OrbitControls) : on comprend où l'on tourne ;
* - il suit la cible quand on pan ou quand un cadrage la replace ;
* - taille **constante à l'écran** (mise à l'échelle par la distance caméra),
* car c'est un repère d'interface, pas un objet du graphe ;
* - exclu du raycasting pour ne jamais intercepter un clic.
*
* Les couleurs sont celles de nooloom (slate / teal / amber), pas celles de la veille.
*/
import * as THREE from 'three';
import { makeRingSprite } from './ring';
/** Axes du repère : X slate, Y teal (accent produit), Z amber. */
export const GIZMO_AXES = [
{ direction: [1, 0, 0], color: '#94a3b8' },
{ direction: [0, 1, 0], color: '#0d9488' },
{ direction: [0, 0, 1], color: '#d97706' },
];
/** Taille du repère en fraction de la distance caméra → constante à l'écran. */
const GIZMO_SCREEN_RATIO = 0.09;
const noRaycast = () => {};
/**
* Construit le repère (à ajouter à `graph.scene()`).
* @returns {THREE.Group}
*/
export function createGizmo() {
const group = new THREE.Group();
for (const { direction, color } of GIZMO_AXES) {
const geometry = new THREE.BufferGeometry();
const [x, y, z] = direction;
geometry.setAttribute(
'position',
new THREE.BufferAttribute(new Float32Array([-x, -y, -z, x, y, z]), 3),
);
const line = new THREE.Line(
geometry,
new THREE.LineBasicMaterial({
color,
transparent: true,
opacity: 0.55,
depthWrite: false,
depthTest: false,
}),
);
line.raycast = noRaycast;
group.add(line);
}
const ring = makeRingSprite('#0d9488', { opacity: 0.3, thickness: 4, scale: 0.8 });
Eif (ring) {
ring.raycast = noRaycast;
group.add(ring);
}
group.renderOrder = 10;
return group;
}
/**
* Positionne et dimensionne le repère sur la cible d'orbite.
* @param {THREE.Group|null} gizmo
* @param {{x:number,y:number,z:number}|null} target cible d'OrbitControls
* @param {{position:{distanceTo:Function}}|null} camera
*/
export function updateGizmo(gizmo, target, camera) {
if (!gizmo || !target) return;
gizmo.position.set(target.x, target.y, target.z);
const distance = camera && typeof camera.position?.distanceTo === 'function'
? camera.position.distanceTo(target)
: 0;
gizmo.scale.setScalar(distance * GIZMO_SCREEN_RATIO || 20);
gizmo.visible = true;
}
/** Libère les géométries/textures du repère et le retire de la scène. */
export function disposeGizmo(gizmo) {
if (!gizmo) return;
gizmo.traverse((object) => {
object.geometry?.dispose?.();
object.material?.map?.dispose?.();
object.material?.dispose?.();
});
gizmo.removeFromParent?.();
}
|