Here is what my code does:
The main is of rank 1. Under rank 1 line there will be 3 lines on rank 2 following these conditions:
The first line's first point is tied to the leftmost vertex of the rank 1 line. This line's other point is defined as such:
x: 1/3 of way across rank 1 line's x (toward the middle)
y: 1/2 of way across rank 1 line's y (toward the middle)

The second line's first point is tied to the rightmost vertex of the rank 1 line. This line's other point is defined as such:
x: 1/3 of way across rank 1 line's x (toward the middle)
y: 1/2 of way across rank 1 line's y (toward the middle)

The line in the middle joins the leftmost and rightmost lines.

Make, under the middle line, a zigzag pattern in fractal for all of the middle lines

the zigzag should extend (either upward or downward) 1/3 of either the height of the left of right line of the same rank (as they are identical)





    // Interactive main line code
    const lineMaterial = new THREE.LineBasicMaterial({ color: 0xffffff });
    const mainLineGeometry = new THREE.BufferGeometry();
    const mainLinePositions = new Float32Array([
      -50, 0, 0,  // Start point
      50, 0, 0    // End point
    ]);
    mainLineGeometry.setAttribute('position', new THREE.BufferAttribute(mainLinePositions, 3));
    const mainLine = new THREE.Line(mainLineGeometry, lineMaterial);
    mainLine.frustumCulled = false;
    scene.add(mainLine);

    // Fractal lines as a single LineSegments object
    const fractalMaterial = new THREE.LineBasicMaterial({ color: 0xffffff });
    const fractalGeometry = new THREE.BufferGeometry();
    const maxRanks = 10; // Parameter to control number of ranks

    // Calculate total number of lines based on maxRanks
    const totalLines = (Math.pow(3, maxRanks + 1) - 3) / 2;
    const fractalPositions = new Float32Array(totalLines * 2 * 3); // Each line has 2 points, each point has 3 coordinates
    fractalGeometry.setAttribute('position', new THREE.BufferAttribute(fractalPositions, 3));
    fractalGeometry.setDrawRange(0, 0); // Initially no fractal lines
    fractalGeometry.attributes.position.setUsage(THREE.DynamicDrawUsage); // Inform Three.js that we'll be updating this buffer frequently
    const fractalLineSegments = new THREE.LineSegments(fractalGeometry, fractalMaterial);
    fractalLineSegments.frustumCulled = false;
    scene.add(fractalLineSegments);

    /**
     * Function to regenerate the fractal lines based on the current main line and maxRanks.
     * This function uses an iterative, rank-by-rank approach to generate all lines up to maxRanks.
     */
    function regenerateFractal() {
      const positions = fractalGeometry.attributes.position.array;
      let offset = 0;

      const startX = mainLinePositions[0];
      const startY = mainLinePositions[1];
      const startZ = mainLinePositions[2];
      const endX = mainLinePositions[3];
      const endY = mainLinePositions[4];
      const endZ = mainLinePositions[5];

      // Initialize array for the current lines (starting with the main line)
      let currentLines = [{
        x1: startX,
        y1: startY,
        z1: startZ,
        x2: endX,
        y2: endY,
        z2: endZ
      }];

      // Iterate through each rank
      for (let rank = 1; rank <= maxRanks; rank++) {
        const nextLines = [];

        // Process each line in the current rank
        for (const line of currentLines) {
          const deltaX = line.x2 - line.x1;
          const deltaY = line.y2 - line.y1;
          const z = line.z1; // Assuming z is constant

          // Calculate intermediate points based on the given conditions
          const x0_2 = line.x1 + deltaX / 3;
          const y0_2 = line.y1 + deltaY / 2;
          const x1_2 = line.x2 - deltaX / 3;
          const y1_2 = line.y2 - deltaY / 2;

          // Line 1: from (x1, y1, z) to (x0_2, y0_2, z)
          positions[offset++] = line.x1;
          positions[offset++] = line.y1;
          positions[offset++] = z;
          positions[offset++] = x0_2;
          positions[offset++] = y0_2;
          positions[offset++] = z;
          nextLines.push({
            x1: line.x1,
            y1: line.y1,
            z1: z,
            x2: x0_2,
            y2: y0_2,
            z2: z
          });

          // Line 2: from (x2, y2, z) to (x1_2, y1_2, z)
          positions[offset++] = line.x2;
          positions[offset++] = line.y2;
          positions[offset++] = z;
          positions[offset++] = x1_2;
          positions[offset++] = y1_2;
          positions[offset++] = z;
          nextLines.push({
            x1: x1_2,
            y1: y1_2,
            z1: z,
            x2: line.x2,
            y2: line.y2,
            z2: z
          });

          // Line 3: from (x0_2, y0_2, z) to (x1_2, y1_2, z)
          positions[offset++] = x0_2;
          positions[offset++] = y0_2;
          positions[offset++] = z;
          positions[offset++] = x1_2;
          positions[offset++] = y1_2;
          positions[offset++] = z;
          nextLines.push({
            x1: x0_2,
            y1: y0_2,
            z1: z,
            x2: x1_2,
            y2: y1_2,
            z2: z
          });
        }

        // Prepare for the next rank
        currentLines = nextLines;
      }

      // Update the draw range based on the number of vertices generated
      fractalGeometry.setDrawRange(0, offset / 3);

      // Mark the attribute as needing an update
      fractalGeometry.attributes.position.needsUpdate = true;

      // Optional: Compute the bounding sphere for better rendering performance (can be skipped if unnecessary)
      fractalGeometry.computeBoundingSphere();

      // Debugging: Log the total number of lines generated
      console.log(`Fractal regenerated with maxRanks=${maxRanks}. Total Lines: ${offset / 6}`);
    }

    // Initial fractal generation
    regenerateFractal();

    /**
     * Function to update fractal lines when the main line changes.
     * This function cleans up any existing fractal lines and regenerates them based on the new main line.
     */
    function updateFractalLines() {
      // Since we're reusing the buffer, simply regenerate the fractal
      regenerateFractal();
    }

    // Set the distance for grabbing the line (in world units)
    const distanceForGrab = 2;