fogón

← Asian3D assetfogon/estanque-koi

Estanque con koi

by fogonCC07654 blocks
Estanque con koiDrag to turn it
// «Estanque con koi» · gpt-6-sol (medium)
// A round garden pond with a ring of grey stones, dark blue water, lily pads with one pink flower, and two koi fish (orange-and-white and red-and-white) as the moving part, swimming in a circle.

const CX = 24, CY = 24;
const TAU = Math.PI * 2;

// Low stone foundation and deep pond basin.
for (let x = 2; x <= 46; x++) {
  for (let y = 2; y <= 46; y++) {
    const d = Math.hypot(x - CX, y - CY);
    if (d <= 20.8) {
      box(x, y, 0, x, y, 1, d > 18.2 ? "#626b68" : "#354b50");
      if (d < 17.7) {
        set(x, y, 2, "#17364d");
        set(x, y, 3, "#17415a");
        set(x, y, 4, rand() < 0.12 ? "#20516a" : "#123b54");
      }
    }
  }
}

// A chunky ring of individually shaded, uneven grey stones.
const stoneShades = ["#aeb6ad", "#bfc4b8", "#939f9d", "#c9c9bb"];
const sectors = 44;
const sectorColors = Array.from({ length: sectors }, () =>
  Math.floor(rand() * stoneShades.length)
);
const sectorHeights = Array.from({ length: sectors }, () =>
  rand() < 0.28 ? 6 : 5
);

for (let x = 2; x <= 46; x++) {
  for (let y = 2; y <= 46; y++) {
    const dx = x - CX, dy = y - CY;
    const d = Math.hypot(dx, dy);
    if (d < 17.3 || d > 20.9) continue;

    let a = Math.atan2(dy, dx);
    if (a < 0) a += TAU;
    const position = a / TAU * sectors;
    const index = Math.floor(position) % sectors;
    const seam = position - Math.floor(position);
    const top = sectorHeights[index];
    const base = stoneShades[sectorColors[index]];

    for (let z = 2; z <= top; z++) {
      let color = base;
      if (z <= 3) color = "#737e7d";
      else if (seam < 0.065 || seam > 0.955) color = "#64716f";
      else if (z === top && rand() < 0.13) color = "#d6d5c5";
      set(x, y, z, color);
    }
  }
}

// A few broad, quiet glints and broken ripples on the dark water.
for (let i = 0; i < 95; i++) {
  const x = 8 + Math.floor(rand() * 33);
  const y = 8 + Math.floor(rand() * 33);
  const d = Math.hypot(x - CX, y - CY);
  if (d < 16.5 && rand() < 0.65)
    set(x, y, 4, rand() < 0.5 ? "#286179" : "#326d7d");
}

function lilyPad(px, py, radius, notchAngle) {
  // Dark green edge, jade-green upper face, and a clear wedge-shaped notch.
  for (let dx = -3; dx <= 3; dx++) {
    for (let dy = -3; dy <= 3; dy++) {
      const d = Math.hypot(dx, dy);
      if (d > radius) continue;
      let angle = Math.atan2(dy, dx) - notchAngle;
      angle = Math.atan2(Math.sin(angle), Math.cos(angle));
      if (d > 0.7 && Math.abs(angle) < 0.23) continue;
      const x = px + dx, y = py + dy;
      set(x, y, 5, d > radius - 0.55 ? "#204c35" : "#32744c");
      if (d < radius - 0.7 && rand() < 0.28)
        set(x, y, 5, "#58a46a");
    }
  }
  set(px, py, 6, "#76ad70");
}

lilyPad(15, 16, 2.7, -0.6);
lilyPad(34, 16, 2.5, 2.4);
lilyPad(14, 30, 2.5, 0.3);
lilyPad(31, 34, 2.8, -2.1);
lilyPad(23, 12, 2.2, 1.3);

// One open cherry-pink lotus on the front-left pad.
set(15, 16, 7, "#e899ad");
for (let i = 0; i < 8; i++) {
  const a = i * TAU / 8;
  const x = 15 + Math.round(Math.cos(a) * 1.5);
  const y = 16 + Math.round(Math.sin(a) * 1.5);
  set(x, y, 7, "#dc7899");
  set(x, y, 8, i % 2 ? "#f5afbf" : "#f8c0c6");
}
box(15, 16, 8, 15, 16, 9, "#f3d478");

// Both koi belong to one rotating part, centered on the pond.
part("koi", [CX, CY, 5]);

function koi(cx, cy, heading, primary, light, dark) {
  const hx = Math.cos(heading), hy = Math.sin(heading);
  function voxel(along, across, z, color) {
    set(
      Math.round(cx + along * hx - across * hy),
      Math.round(cy + along * hy + across * hx),
      z, color
    );
  }

  // Soft swimming shadow immediately beneath each fish.
  for (let along = -4; along <= 4; along++)
    for (let across = -1; across <= 1; across++)
      voxel(along, across, 4, "#285065");

  // Rounded white body with bold asymmetrical koi markings.
  for (let along = -3; along <= 3; along++) {
    const width = Math.abs(along) <= 1 ? 2 : 1;
    for (let across = -width; across <= width; across++) {
      const patch = along <= -1
        ? (across <= 0 || along === -2)
        : (along === 1 && across >= 0);
      const side = patch ? primary : light;
      voxel(along, across, 5, across === -width ? dark : side);
      voxel(along, across, 6, side);
      if (Math.abs(across) < width || along === 0)
        voxel(along, across, 7, patch ? primary : "#fff4d9");
    }
  }

  // Head, little mouth, and dark eyes.
  for (let across = -1; across <= 1; across++) {
    voxel(4, across, 5, light);
    voxel(4, across, 6, light);
  }
  voxel(5, 0, 6, "#efd2a4");
  voxel(3, -1, 8, "#172b31");
  voxel(3, 1, 8, "#172b31");
  voxel(0, 0, 8, primary); // small dorsal fin

  // Broad pectoral fins and a distinct forked tail.
  for (const side of [-1, 1]) {
    voxel(0, side * 3, 5, light);
    voxel(-1, side * 3, 5, primary);
    voxel(-4, side, 5, primary);
    voxel(-5, side, 5, light);
    voxel(-6, side * 2, 5, primary);
    voxel(-5, side * 2, 6, light);
  }
  voxel(-4, 0, 6, primary);
  voxel(-5, 0, 6, primary);
}

koi(31, 24, Math.PI / 2, "#e77729", "#fff2d7", "#bb5425");
koi(17, 24, -Math.PI / 2, "#ba3d37", "#fff0df", "#8e3036");

animate("koi", { type: "spin", axis: "z", speed: 0.55 });
part("body");