Klein-Bottle Non-Orientable Fold

📅 April 25, 2026 🏷️ art
klein-bottle non-orientable mathematical-art recursive-projection psychedelic-synesthesia 3d-surface hyperdimensional iridescent pending-review
Generated by GridFlow AI | Tags: klein-bottle, non-orientable, mathematical-art, recursive-projection, psychedelic-synesthesia, 3d-surface, hyperdimensional, iridescent

💡 AI 提示词

Create a Klein Bottle Surface Non-Orientable Fold artwork with psychedelic synesthesia visual style using recursive 3D-to-2D projection as the primary rendering technique

🔧 核心算法要点

  1. Klein bottle parametric surface computation using the figure-8 immersion formula with separate equations for the tube and neck sections
  2. Recursive 3D-to-2D projection with 4D hyperbolic rotation for hyper-dimensional visualization of non-orientable topology
  3. Multi-layer compositing using four separate createGraphics buffers (background, main geometry, glow, overlay) with screen, add, and overlay blend modes
  4. Iridescent color calculation based on surface normals with HSB color mapping that shifts between cyan and magenta with spectral gold accents
  5. Pixel-level background rendering using loadPixels/updatePixels with spiral gradient computation for deep void atmosphere
  6. Recursive fold entity rendering with curveVertex-based organic forms that spawn child folds at multiple depth levels

🎨 原始代码

var sketch = function(p) {
  var bgGfx, mainGfx, glowGfx, overlayGfx, depthGfx;
  var kleinPoints = [];
  var time = 0;
  var foldDepth = 0;
  var iridescencePhase = 0;
  var hyperRotate = 0;
  var mouseInfluence = { x: 0, y: 0, active: false };
  var keyToggle = 0;
  var palette = {
    deepVoid: [10, 5, 8],
    iridescentCyan: [180, 100, 100],
    iridescentMagenta: [300, 100, 100],
    spectralGold: [45, 90, 95],
    voidViolet: [270, 80, 20],
    foldGreen: [120, 70, 80]
  };

  // Klein bottle parametric equations
  function kleinU(u) { return u; }
  function kleinV(v) { return v; }

  // Compute Klein bottle surface point
  function computeKleinPoint(u, v, fold) {
    var u2 = u * 2;
    var cosU = p.cos(u);
    var sinU = p.sin(u);
    var cos2U = p.cos(u2);
    var sin2U = p.sin(u2);
    var cosV = p.cos(v);
    var sinV = p.sin(v);

    var r = 2 - p.cos(u2);
    var x, y, z;

    if (u < p.PI) {
      x = r * cosU + 2 * (1 - p.cos(u2) / 2) * cosU * cosV;
      y = r * sinU;
      z = 2 * (1 - p.cos(u2) / 2) * sinU * cosV;
    } else {
      x = r * cosU + 2 * (1 - p.cos(u2) / 2) * (cosU + 1);
      y = r * sinU;
      z = 2 * (1 - p.cos(u2) / 2) * sinV;
    }

    // Apply non-orientable fold distortion
    var foldFactor = p.sin(fold * p.TWO_PI) * 0.5;
    x += foldFactor * sinU * sin(v * 2);
    z += foldFactor * cosU * cos(v * 3);

    return { x: x * 40, y: y * 40, z: z * 40, u: u, v: v };
  }

  // Project 3D to 2D with recursive hyper-projection
  function project3Dto2D(point, recursion) {
    var scale = 1.2;
    var perspective = 600;
    var centerX = p.width / 2;
    var centerY = p.height / 2;

    // Apply hyperbolic rotation in 4D space
    var angle4D = hyperRotate * 0.3 + point.u * 0.5;
    var w = p.cos(angle4D) * point.z + p.sin(angle4D) * point.x * 0.3;

    // Recursive depth factor for non-orientable topology
    var depthFactor = 1 + recursion * p.sin(time * 0.5 + point.v * 2) * 0.2;

    var z4d = point.z * depthFactor + w * 0.4;
    var zPerspective = perspective + z4d * 2;

    if (zPerspective <= 0) zPerspective = 1;

    var scaleFactor = scale * (perspective / zPerspective);

    return {
      x: point.x * scaleFactor + centerX + mouseInfluence.x * 20,
      y: point.y * scaleFactor + centerY + mouseInfluence.y * 20,
      z: z4d,
      scale: scaleFactor
    };
  }

  // Iridescent color calculation based on surface orientation
  function iridescentColor(normal, phase) {
    var hue = (normal.x * 60 + normal.y * 60 + phase * 120 + iridescencePhase) % 360;
    var saturation = 85 + p.sin(normal.z * p.PI + phase) * 15;
    var brightness = 70 + p.cos(normal.x * normal.y + phase * 2) * 25;

    // Shift between cyan and magenta with spectral gold accents
    var colorShift = p.sin(phase * 3 + time * 0.7) * 0.5 + 0.5;
    if (colorShift > 0.7) {
      hue = (hue + 30) % 360;
      saturation *= 1.2;
    }

    return p.color(hue, saturation, brightness);
  }

  // Render recursive non-orientable fold
  function renderRecursiveFold(x, y, depth, maxDepth) {
    if (depth > maxDepth) return;

    var size = (maxDepth - depth + 1) * 15;
    var alpha = (1 - depth / maxDepth) * 180;
    var foldAngle = p.sin(time * 2 + depth * 0.5) * p.PI;

    var c = p.color(palette.foldGreen[0], palette.foldGreen[1], palette.foldGreen[2] * (1 - depth * 0.15));
    c.setAlpha(alpha);

    mainGfx.push();
    mainGfx.translate(x, y);
    mainGfx.rotate(foldAngle + depth * 0.3);

    mainGfx.noFill();
    mainGfx.stroke(c);
    mainGfx.strokeWeight(2 - depth * 0.15);

    mainGfx.beginShape();
    var steps = 8 + depth * 2;
    for (var i = 0; i <= steps; i++) {
      var angle = (i / steps) * p.TWO_PI;
      var r = size * (1 + 0.3 * p.sin(angle * 3 + time + depth));
      var px = p.cos(angle) * r * (1 + 0.2 * p.cos(angle * 2));
      var py = p.sin(angle) * r * (1 + 0.15 * p.sin(angle * 4));
      mainGfx.curveVertex(px, py);
    }
    mainGfx.endShape(p.CLOSE);

    // Recursive child folds
    if (depth < maxDepth) {
      var children = 3 + (depth % 2);
      for (var j = 0; j < children; j++) {
        var childAngle = (j / children) * p.TWO_PI + time * 0.3 + depth * 0.5;
        var childDist = size * 0.6 + p.sin(time + j) * 10;
        var childX = p.cos(childAngle) * childDist;
        var childY = p.sin(childAngle) * childDist;
        renderRecursiveFold(childX, childY, depth + 1, maxDepth);
      }
    }

    mainGfx.pop();
  }

  // Render Klein bottle wireframe with recursive subdivision
  function renderKleinWireframe() {
    var uSteps = 48;
    var vSteps = 64;

    kleinPoints = [];

    // Compute all points
    for (var i = 0; i <= uSteps; i++) {
      kleinPoints[i] = [];
      var u = (i / uSteps) * p.TWO_PI * 2;
      for (var j = 0; j <= vSteps; j++) {
        var v = (j / vSteps) * p.TWO_PI * 2;
        kleinPoints[i][j] = computeKleinPoint(u, v, foldDepth);
      }
    }

    // Render wireframe with recursive projection
    for (var i = 0; i < uSteps; i++) {
      for (var j = 0; j < vSteps; j++) {
        var p1 = kleinPoints[i][j];
        var p2 = kleinPoints[(i + 1) % (uSteps + 1)][j];
        var p3 = kleinPoints[i][(j + 1) % (vSteps + 1)];

        var proj1 = project3Dto2D(p1, 2);
        var proj2 = project3Dto2D(p2, 2);
        var proj3 = project3Dto2D(p3, 2);

        var normal = {
          x: p2.x - p1.x + p3.x - p1.x,
          y: p2.y - p1.y + p3.y - p1.y,
          z: p2.z - p1.z + p3.z - p1.z
        };
        var normalMag = p.sqrt(normal.x * normal.x + normal.y * normal.y + normal.z * normal.z);
        if (normalMag > 0) {
          normal.x /= normalMag;
          normal.y /= normalMag;
          normal.z /= normalMag;
        }

        var c = iridescentColor(normal, p1.u / p.TWO_PI);

        var alpha = 100 + p.sin(p1.v + time) * 50;
        c.setAlpha(alpha);

        mainGfx.stroke(c);
        mainGfx.strokeWeight(0.5 + p.sin(p1.u * 0.5 + p1.v * 0.3 + time) * 0.5);

        mainGfx.line(proj1.x, proj1.y, proj2.x, proj2.y);
        mainGfx.line(proj1.x, proj1.y, proj3.x, proj3.y);

        // Non-orientable fold highlight
        if (j % 8 === 0 && i % 6 === 0) {
          var glowAlpha = (p.sin(foldDepth * p.PI + p1.u * 0.3) * 0.5 + 0.5) * 150;
          var glowC = p.color(palette.spectralGold[0], palette.spectralGold[1], palette.spectralGold[2]);
          glowC.setAlpha(glowAlpha);
          glowGfx.stroke(glowC);
          glowGfx.strokeWeight(2);
          glowGfx.point(proj1.x, proj1.y);
        }
      }
    }
  }

  // Create deep void background with volumetric gradients
  function renderBackground() {
    bgGfx.loadPixels();

    var res = 3;
    for (var x = 0; x < p.width; x += res) {
      for (var y = 0; y < p.height; y += res) {
        var cx = p.width / 2;
        var cy = p.height / 2;
        var dx = x - cx;
        var dy = y - cy;
        var dist = p.sqrt(dx * dx + dy * dy);
        var maxDist = p.sqrt(cx * cx + cy * cy);

        var t = dist / maxDist;
        var spiralAngle = p.atan2(dy, dx) + time * 0.1;
        var spiral = p.sin(spiralAngle * 4 + t * 10 - foldDepth * 5) * 0.5 + 0.5;

        var hue = (spiral * 120 + palette.voidViolet[0] + time * 5) % 360;
        var sat = palette.voidViolet[1] * (1 - t * 0.5);
        var bri = palette.voidViolet[2] * (1 - t * 0.8) + spiral * 15;

        var c = p.color(hue, sat, bri);

        for (var px = 0; px < res && x + px < p.width; px++) {
          for (var py = 0; py < res && y + py < p.height; py++) {
            bgGfx.pixels[(y + py) * bgGfx.width * 4 + (x + px) * 4] = p.red(c);
            bgGfx.pixels[(y + py) * bgGfx.width * 4 + (x + px) * 4 + 1] = p.green(c);
            bgGfx.pixels[(y + py) * bgGfx.width * 4 + (x + px) * 4 + 2] = p.blue(c);
            bgGfx.pixels[(y + py) * bgGfx.width * 4 + (x + px) * 4 + 3] = 255;
          }
        }
      }
    }

    bgGfx.updatePixels();
  }

  // Render spectral fold overlays
  function renderSpectralOverlays() {
    overlayGfx.clear();

    var foldCount = 5;
    for (var f = 0; f < foldCount; f++) {
      var foldAngle = time * 0.5 + f * (p.TWO_PI / foldCount) + foldDepth * 2;
      var foldRadius = p.width * 0.3 + p.sin(time + f) * 50;
      var foldX = p.width / 2 + p.cos(foldAngle) * foldRadius;
      var foldY = p.height / 2 + p.sin(foldAngle) * foldRadius;

      var c = p.color(
        (palette.iridescentCyan[0] + f * 30) % 360,
        palette.iridescentCyan[1] - f * 10,
        palette.iridescentCyan[2] - f * 15
      );
      c.setAlpha(30 + p.sin(time * 2 + f) * 20);

      overlayGfx.noFill();
      overlayGfx.stroke(c);
      overlayGfx.strokeWeight(1);

      var layers = 3;
      for (var l = 0; l < layers; l++) {
        var layerSize = 100 + l * 60 + p.sin(time * 1.5 + f + l) * 30;
        overlayGfx.ellipse(foldX, foldY, layerSize, layerSize * (0.8 + p.sin(foldAngle * 2) * 0.2));
      }
    }

    // Recursive fold entities at center
    var centerX = p.width / 2 + p.sin(time * 0.3) * 50;
    var centerY = p.height / 2 + p.cos(time * 0.4) * 50;
    renderRecursiveFold(centerX, centerY, 0, 4);
  }

  p.setup = function() {
    var container = document.getElementById('p5-wrapper');
    p.createCanvas(container.offsetWidth, container.offsetHeight).parent(container);

    bgGfx = p.createGraphics(p.width, p.height);
    mainGfx = p.createGraphics(p.width, p.height);
    glowGfx = p.createGraphics(p.width, p.height);
    overlayGfx = p.createGraphics(p.width, p.height);
    depthGfx = p.createGraphics(p.width, p.height);

    p.colorMode(p.HSB, 360, 100, 100, 100);
    p.frameRate(60);

    renderBackground();
  };

  p.draw = function() {
    time += 0.008;
    foldDepth = (foldDepth + 0.003) % 1;
    iridescencePhase += 0.02;
    hyperRotate += 0.015;

    mainGfx.clear();
    glowGfx.clear();

    // Update mouse influence decay
    if (!mouseInfluence.active) {
      mouseInfluence.x *= 0.95;
      mouseInfluence.y *= 0.95;
    }

    // Render Klein bottle surface
    renderKleinWireframe();

    // Render spectral overlays
    renderSpectralOverlays();

    // Composite all layers with advanced blend modes
    p.background(0);

    // Base void layer
    p.image(bgGfx, 0, 0);

    // Main geometry layer with screen blend
    p.blendMode(p.SCREEN);
    p.image(mainGfx, 0, 0);

    // Glow layer for ethereal bloom
    p.blendMode(p.ADD);
    p.tint(255, 40);
    p.image(glowGfx, 0, 0);

    // Overlay layer with multiply for depth
    p.blendMode(p.OVERLAY);
    p.tint(255, 60);
    p.image(overlayGfx, 0, 0);

    // Reset blend mode
    p.blendMode(p.BLEND);

    // Add subtle vignette
    p.noStroke();
    var vignetteSteps = 20;
    for (var i = 0; i < vignetteSteps; i++) {
      var t = i / vignetteSteps;
      var alpha = p.map(t, 0, 1, 0, 40);
      var size = p.map(t, 0, 1, 0, p.width * 0.7);
      var c = p.color(0, 0, 0, alpha);
      p.fill(c);
      var w = p.width * (1 - t * 0.3);
      var h = p.height * (1 - t * 0.3);
      p.ellipse(p.width / 2, p.height / 2, size * 2 + w, size * 2 + h);
    }

    // Interactive highlight at mouse position
    if (mouseInfluence.active || p.mouseX !== 0 || p.mouseY !== 0) {
      var mDist = p.dist(p.mouseX, p.mouseY, p.width / 2, p.height / 2);
      var mAlpha = p.map(mDist, 0, p.width / 2, 80, 0);

      p.stroke(palette.spectralGold[0], palette.spectralGold[1], palette.spectralGold[2], mAlpha);
      p.strokeWeight(2);
      p.noFill();

      var ringSize = 100 + p.sin(time * 3) * 30;
      p.ellipse(p.mouseX, p.mouseY, ringSize, ringSize);

      // Connection lines to nearest Klein points
      for (var i = 0; i < 5; i++) {
        var t = i / 5;
        var angle = t * p.TWO_PI + time;
        var px = p.width / 2 + p.cos(angle) * p.width * 0.3;
        var py = p.height / 2 + p.sin(angle) * p.height * 0.3;
        var distToMouse = p.dist(p.mouseX, p.mouseY, px, py);
        if (distToMouse < 200) {
          var lineAlpha = p.map(distToMouse, 0, 200, 50, 0);
          p.stroke(palette.iridescentMagenta[0], palette.iridescentMagenta[1], palette.iridescentMagenta[2], lineAlpha);
          p.line(p.mouseX, p.mouseY, px, py);
        }
      }
    }
  };

  p.mouseMoved = function() {
    mouseInfluence.x = (p.mouseX - p.width / 2) / (p.width / 2);
    mouseInfluence.y = (p.mouseY - p.height / 2) / (p.height / 2);
    mouseInfluence.active = true;
  };

  p.mouseDragged = function() {
    mouseInfluence.x = (p.mouseX - p.width / 2) / (p.width / 2);
    mouseInfluence.y = (p.mouseY - p.height / 2) / (p.height / 2);
    mouseInfluence.active = true;
  };

  p.mouseReleased = function() {
    mouseInfluence.active = false;
  };

  // Click: Reset fold cycle and spawn burst
  p.mousePressed = function() {
    foldDepth = 0;
    hyperRotate = 0;

    // Spawn burst of fold particles
    var burstGfx = p.createGraphics(p.width, p.height);
    burstGfx.noStroke();

    for (var i = 0; i < 50; i++) {
      var angle = p.random(p.TWO_PI);
      var speed = p.random(50, 150);
      var x = p.mouseX;
      var y = p.mouseY;
      var life = 1;

      while (life > 0) {
        x += p.cos(angle) * speed * 0.016;
        y += p.sin(angle) * speed * 0.016;
        life -= 0.02;

        var c = p.color(
          (palette.iridescentCyan[0] + life * 60) % 360,
          100,
          100,
          life * 50
        );
        burstGfx.fill(c);
        burstGfx.ellipse(x, y, 3, 3);
      }
    }

    p.blendMode(p.ADD);
    p.image(burstGfx, 0, 0);
    p.blendMode(p.BLEND);
  };

  // Keyboard interactions
  p.keyPressed = function() {
    if (p.key === ' ') {
      // Space: Toggle color mode
      keyToggle = (keyToggle + 1) % 3;
      if (keyToggle === 0) {
        palette.iridescentCyan[0] = 180;
        palette.iridescentMagenta[0] = 300;
      } else if (keyToggle === 1) {
        palette.iridescentCyan[0] = 120;
        palette.iridescentMagenta[0] = 60;
      } else {
        palette.iridescentCyan[0] = 240;
        palette.iridescentMagenta[0] = 0;
      }
      renderBackground();
    } else if (p.key === 'f' || p.key === 'F') {
      // F: Cycle fold animation speed
      foldDepth = (foldDepth + 0.1) % 1;
    } else if (p.key === 'r' || p.key === 'R') {
      // R: Reset all parameters
      foldDepth = 0;
      time = 0;
      hyperRotate = 0;
      renderBackground();
    } else if (p.key === 'h' || p.key === 'H') {
      // H: Toggle hyper-rotation intensity
      hyperRotate *= 2;
    }
  };

  p.windowResized = function() {
    var container = document.getElementById('p5-wrapper');
    p.resizeCanvas(container.offsetWidth, container.offsetHeight);

    bgGfx = p.createGraphics(p.width, p.height);
    mainGfx = p.createGraphics(p.width, p.height);
    glowGfx = p.createGraphics(p.width, p.height);
    overlayGfx = p.createGraphics(p.width, p.height);
    depthGfx = p.createGraphics(p.width, p.height);

    renderBackground();
  };
}; // p5 init stripped

✨ AI 艺术解读

This piece manifests the philosophical paradox of non-orientability through recursive Klein bottle projections that fold through hyper-dimensional space. The iridescent surface reveals the impossibility of distinguishing inside from outside, creating a visual meditation on the limits of spatial reasoning. The psychedelic synesthesia aesthetic transforms mathematical abstraction into cosmic experience—each fold cycle represents a journey through impossible topology. The viewer becomes entangled with the surface, their mouse position distorting the very fabric of non-orientable space, experiencing ego-death through mathematical sublime.

📝 补充说明

  • Klein bottle parametric equations use u in [0, 2π] for the longitude and v in [0, 2π] for the latitude, with different formulas for the main body (u < π) and the neck (u ≥ π)
  • 4D rotation uses angle = hyperRotate * 0.3 + u * 0.5 with cosine/sine of this angle applied to z and x coordinates to achieve the recursive projection effect
  • Background pixel rendering uses spiralAngle = atan2(dy, dx) + time * 0.1 and spiral = sin(spiralAngle * 4 + t * 10 - foldDepth * 5) * 0.5 + 0.5 for the volumetric void effect
  • Non-orientable fold distortion applies foldFactor = sin(foldDepth * TWO_PI) * 0.5 to the x and z coordinates based on sin(u) * sin(v * 2) and cos(u) * cos(v * 3) terms
  • Performance optimization: Klein bottle wireframe uses 48x64 grid (3072 lines) with projection computed once per point, and background renders at 3px step size