fogón

← Nature3D assetfogon/tuna

Tuna

by fogonCC04933 blocks
TunaDrag to turn it
// «Tuna» · gpt-6-sol (medium)
// A prickly pear cactus: several flat oval green pads growing out of each other, with tiny spines and a few red-purple fruits on the edges.

// Flat, overlapping prickly pear pads, facing the viewer (-y).
const pads = [
  // This lower pad extends to z = 0, so the cactus rests on the floor.
  { x: 24, y: 25, z: 9,  rx: 7,   ry: 3.3, rz: 10, angle:  0.03 },
  { x: 13, y: 25, z: 20, rx: 7.5, ry: 2.8, rz: 10, angle: -0.17 },
  { x: 35, y: 26, z: 20, rx: 8,   ry: 2.9, rz: 10, angle:  0.15 },
  { x: 24, y: 24, z: 24, rx: 9,   ry: 3.2, rz: 12, angle: -0.04 },
  { x: 15, y: 24, z: 31, rx: 6,   ry: 2.6, rz: 8,  angle: -0.15 },
  { x: 33, y: 25, z: 32, rx: 6,   ry: 2.6, rz: 7,  angle:  0.16 }
];

function padCoordinates(p, x, z) {
  const dx = x - p.x, dz = z - p.z;
  const c = Math.cos(p.angle), s = Math.sin(p.angle);
  return {
    u: (dx * c + dz * s) / p.rx,
    v: (-dx * s + dz * c) / p.rz
  };
}

for (const p of pads) {
  for (let x = Math.floor(p.x - p.rx - 3); x <= Math.ceil(p.x + p.rx + 3); x++) {
    for (let z = Math.max(0, Math.floor(p.z - p.rz - 3)); z <= Math.ceil(p.z + p.rz + 3); z++) {
      const { u, v } = padCoordinates(p, x, z);
      const oval = u * u + v * v;
      if (oval > 1) continue;

      const halfDepth = p.ry * Math.sqrt(1 - oval);
      for (let y = Math.ceil(p.y - halfDepth); y <= Math.floor(p.y + halfDepth); y++) {
        const depth = Math.abs(y - p.y) / p.ry;
        const rim = oval > 0.80 || depth > 0.82;
        let color;

        if (rim) color = z > p.z ? "#36aa50" : "#258d43";
        else if (z > p.z + 2) color = rand() < 0.18 ? "#86e583" : "#67d773";
        else color = rand() < 0.16 ? "#68d878" : "#46c961";

        if (y > p.y + 1 && rand() < 0.45) color = "#35ae51";
        set(x, y, z, color);
      }
    }
  }
}

// Tiny pale areoles and prickles on the exposed front of each pad.
for (const p of pads) {
  for (let x = Math.floor(p.x - p.rx + 2); x <= p.x + p.rx - 2; x += 4) {
    for (let z = Math.floor(p.z - p.rz + 2); z <= p.z + p.rz - 2; z += 4) {
      const px = Math.round(x + ((Math.floor(z / 4) % 2) ? 1 : 0));
      const pz = Math.round(z);
      const { u, v } = padCoordinates(p, px, pz);
      if (u * u + v * v > 0.75) continue;

      let front;
      for (let y = 17; y <= 29; y++) {
        if (get(px, y, pz)) { front = y; break; }
      }
      if (front === undefined) continue;

      set(px, front - 1, pz, "#e9dc9b");
      if (rand() < 0.78) set(px, front - 2, pz, "#fff0bd");
      if (rand() < 0.42) {
        set(px + (rand() < 0.5 ? -1 : 1), front - 1, pz + 1, "#f5e9ae");
      }
    }
  }
}

// Plump red-purple fruits perched along the pad edges.
function fruit(x, y, z) {
  ellipsoid(x, y, z, 2.2, 2.1, 3.3, "#ac3158");
  ellipsoid(x, y - 0.5, z + 0.3, 1.7, 1.5, 2.7, "#e94b70");
  box(x - 1, y - 2, z + 1, x, y - 2, z + 2, "#fa8195");
  set(x, y - 2, z - 1, "#f77e92");
  set(x + 1, y - 2, z, "#b42e59");
  box(x - 1, y - 1, z + 3, x + 1, y + 1, z + 3, "#8d3153");
  set(x, y, z + 4, "#78304b");
}

fruit(9, 24, 34);
fruit(16, 24, 40);
fruit(34, 25, 40);
fruit(40, 26, 31);