📄 src/web3/utils/hash.ts
D-OPEN SOVEREIGN

Documentation & Insights

Returns 4 coordinates inside a container.
- Deterministic for a given username
- Adds padding to avoid edges
- Optionally applies a jitter ring to spread points nicer
1// Deterministic coordinates for 4 nodes based on a username
2export type Point = { x: number; y: number };
3
4function fnv1a32(input: string): number {
5  // FNV-1a 32-bit
6  let hash = 0x811c9dc5 >>> 0;
7  for (let i = 0; i < input.length; i++) {
8    hash ^= input.charCodeAt(i);
9    // multiply by FNV prime 16777619
10    hash = Math.imul(hash, 0x01000193) >>> 0;
11  }
12  return hash >>> 0;
13}
14
15function xorshift32(seed: number): () => number {
16  // returns a function producing uint32 values
17  let x = (seed || 0x9e3779b9) >>> 0;
18  return () => {
19    x ^= x << 13;
20    x >>>= 0;
21    x ^= x >>> 17;
22    x >>>= 0;
23    x ^= x << 5;
24    x >>>= 0;
25    return x >>> 0;
26  };
27}
28
29/**
30 * Returns 4 coordinates inside a container.
31 * - Deterministic for a given username
32 * - Adds padding to avoid edges
33 * - Optionally applies a jitter ring to spread points nicer
34 */
35export function hashUsernameToCoords(username: string, width: number, height: number, padding = 16): Point[] {
36  const base = fnv1a32(username);
37  const next = xorshift32(base);
38
39  const innerW = Math.max(0, width - padding * 2);
40  const innerH = Math.max(0, height - padding * 2);
41
42  const points: Point[] = [];
43  for (let i = 0; i < 4; i++) {
44    // produce two uint32, turn them into floats in [0,1)
45    const rx = next() / 0x1_0000_0000;
46    const ry = next() / 0x1_0000_0000;
47
48    // basic mapped coordinates
49    let x = padding + rx * innerW;
50    let y = padding + ry * innerH;
51
52    // small deterministic jitter to reduce overlaps (optional)
53    const angle = (next() % 360) * (Math.PI / 180);
54    const radius = 0.035 * Math.min(innerW, innerH); // ~3.5% of min dimension
55    x = Math.min(width - padding, Math.max(padding, x + Math.cos(angle) * radius));
56    y = Math.min(height - padding, Math.max(padding, y + Math.sin(angle) * radius));
57
58    points.push({ x, y });
59  }
60  return points;
61}
62