frameWeb function

OrbFrame frameWeb(
  1. double size,
  2. double t,
  3. ModeOpts o
)

Web mode: connecting — a constellation wires itself with packets.

Implementation

OrbFrame frameWeb(double size, double t, ModeOpts o) {
  final cx = size / 2.0;
  final cy = size / 2.0;
  final R = (size / 2.0) * 0.8 * (o['spread'] ?? 1.0);
  final pt = makeProj(t * 0.12, 0.32, cx, cy, R);
  final rs = radiusScale(size, o['rsPow'] ?? 0.6);

  final nodeN = (o['nodeN'] ?? 30.0).toInt();
  final thr = o['thr'] ?? 0.72;
  final nodeR = o['nodeR'] ?? 1.4;
  final nodeRDepth = o['nodeRDepth'] ?? 1.8;

  final nodes = <(double, double, double)>[];
  for (int i = 0; i < nodeN; i++) {
    final d = fibDir(i, nodeN);
    final x = d.$1 + 0.3 * (vnoise(i * 0.31 + 9.0, t * 0.24) - 0.5) * 2.0;
    final y = d.$2 + 0.3 * (vnoise(i * 0.53 + 27.0, t * 0.21) - 0.5) * 2.0;
    final z = d.$3 + 0.3 * (vnoise(i * 0.77 + 55.0, t * 0.27) - 0.5) * 2.0;
    final l = math.sqrt(x * x + y * y + z * z);
    nodes.add((x / l, y / l, z / l));
  }

  final lines = <Line>[];
  final dots = <Dot>[];

  // Edges between close neighbours
  for (int i = 0; i < nodeN; i++) {
    for (int j = i + 1; j < nodeN; j++) {
      final dx = nodes[i].$1 - nodes[j].$1;
      final dy = nodes[i].$2 - nodes[j].$2;
      final dz = nodes[i].$3 - nodes[j].$3;
      final dist = math.sqrt(dx * dx + dy * dy + dz * dz);
      if (dist >= thr) continue;

      final (x1, y1, z1) = pt(nodes[i].$1, nodes[i].$2, nodes[i].$3);
      final (x2, y2, z2) = pt(nodes[j].$1, nodes[j].$2, nodes[j].$3);
      final depth = ((z1 + z2) / 2.0 + 1.0) / 2.0;

      lines.add(Line(
        x1: x1,
        y1: y1,
        x2: x2,
        y2: y2,
        white: 0.42,
        a: (1.0 - dist / thr) * (0.3 + 0.55 * depth),
        w: math.max(0.6, (o['lineW'] ?? 0.8) * rs),
      ));
    }
  }

  // Node dots
  for (int i = 0; i < nodeN; i++) {
    final (px, py, z) = pt(nodes[i].$1, nodes[i].$2, nodes[i].$3);
    final depth = (z + 1.0) / 2.0;
    final pulse = 1.0 + 0.25 * math.sin(t * 1.4 + i * 2.7);

    dots.add(Dot(
      x: px,
      y: py,
      z: z,
      r: (nodeR + nodeRDepth * depth) * pulse * rs,
      white: 0.55 - 0.45 * depth,
    ));
  }

  // Signal packets
  final signals = (o['signals'] ?? 5.0).toInt();
  for (int s = 0; s < signals; s++) {
    final seg = (t * 0.55 + s * 7.31).floor();
    final a = (hashD(seg.toDouble(), s * 3.1 + 1.7) * nodeN).floor();
    final b = (hashD(seg.toDouble(), s * 5.7 + 4.2) * nodeN).floor();
    if (a == b) continue;

    final f = frac(t * 0.55 + s * 7.31);
    final x = lerp(nodes[a].$1, nodes[b].$1, f);
    final y = lerp(nodes[a].$2, nodes[b].$2, f);
    final z = lerp(nodes[a].$3, nodes[b].$3, f);
    final l = math.max(1e-6, math.sqrt(x * x + y * y + z * z));
    final (px, py, zr) = pt(x / l, y / l, z / l);
    final depth = (zr + 1.0) / 2.0;

    dots.add(Dot(
      x: px,
      y: py,
      z: zr,
      r: (nodeR * 1.5 + nodeRDepth * depth) * rs,
      white: 0.05,
      a: 0.5 + 0.5 * depth,
    ));
  }

  return finalizeFrame(dots, lines, o['rMin'] ?? 0.3);
}