fogón

← AnimalesObjeto 3Dfogon/tortuga-marina

Tortuga marina

de fogonCC012150 bloques
Tortuga marinaArrastrá para girarlo
// «Tortuga marina» · gpt-6-sol (medium)
// A cartoon sea turtle swimming, horizontal along y, head at the front (-y): a flatter oval shell in green and brown hexagon plates, a head with a beak and kind eyes, two big front flippers like wings and two small back flippers, skin in olive green with lighter scales.

// A chunky sea turtle, facing -y.
const skin = "#729b4c";
const skinLight = "#91b963";
const skinDark = "#52783c";

function oval(cx, cy, cz, rx, ry, rz, base, top) {
  ellipsoid(cx, cy, cz, rx, ry, rz, base);
  for (let x = Math.ceil(cx - rx); x <= cx + rx; x++)
    for (let y = Math.ceil(cy - ry); y <= cy + ry; y++)
      for (let z = Math.ceil(cz); z <= cz + rz; z++)
        if (get(x, y, z) === base && rand() < 0.35)
          set(x, y, z, top);
}

// Belly and broad, low shell.
ellipsoid(24, 27, 8, 11, 12, 8, "#c5b777");
ellipsoid(24, 27, 6, 10, 11, 5, "#a99d64");
ellipsoid(24, 27, 14, 13, 14, 9, "#405b30");
ellipsoid(24, 27, 11, 14, 15, 3, "#54743a");

// Hexagonal scutes on the domed shell. The dark gaps make the plates readable.
const plates = ["#819744", "#a18348", "#718d40", "#ae8a4c",
                "#92743e", "#779346", "#9a8246"];
for (let x = 11; x <= 37; x++) {
  for (let y = 13; y <= 41; y++) {
    const dx = (x - 24) / 13, dy = (y - 27) / 14;
    const radius2 = dx * dx + dy * dy;
    if (radius2 > 0.87) continue;
    const z = Math.round(14 + 9 * Math.sqrt(1 - radius2));

    let nearest = Infinity, second = Infinity, qBest = 0, rBest = 0;
    for (let q = -3; q <= 3; q++) {
      for (let r = -3; r <= 3; r++) {
        const px = 24 + 6 * (q + r / 2);
        const py = 27 + 5.2 * r;
        const d = (x - px) ** 2 + (y - py) ** 2;
        if (d < nearest) {
          second = nearest;
          nearest = d;
          qBest = q;
          rBest = r;
        } else if (d < second) second = d;
      }
    }
    const index = ((qBest * 3 + rBest * 5) % plates.length + plates.length) % plates.length;
    let color = second - nearest < 8 ? "#405a30" : plates[index];
    if (second - nearest >= 8 && nearest < 5) color = "#b1a45b";
    if (second - nearest >= 8 && rand() < 0.055) color = "#b3a263";
    set(x, y, z, color);
    if (radius2 < 0.72) set(x, y, z - 1, color);
  }
}

// Little rear paddles, peeking out from behind the shell.
for (const side of [-1, 1]) {
  const cx = 24 + side * 12;
  line(24 + side * 8, 36, 8, cx, 41, 7, 3, skinDark);
  ellipsoid(cx, 40, 7, 4, 5, 2, skin);
  ellipsoid(cx, 40, 9, 3, 3, 1, skinLight);
  set(cx - side, 42, 9, "#b1c879");
  set(cx + side, 39, 9, "#b1c879");
}

// Neck and oversized, friendly head.
ellipsoid(24, 15, 11, 6, 7, 5, skinDark);
oval(24, 9, 13, 7, 7, 6, skin, skinLight);
ellipsoid(19, 7, 11, 3, 4, 3, "#83aa58");
ellipsoid(29, 7, 11, 3, 4, 3, "#83aa58");

// Short hooked beak and a simple smile.
ellipsoid(24, 3, 10, 4, 3, 2, "#8dad5c");
box(22, 0, 10, 26, 2, 11, "#9cba67");
box(23, 0, 9, 25, 1, 10, "#688745");
line(21, 3, 9, 27, 3, 9, 0, "#4e6338");
set(20, 3, 12, "#456039");
set(28, 3, 12, "#456039");

// Eyes sit high so they also read from above.
for (const x of [20, 28]) {
  ellipsoid(x, 5, 17, 2, 2, 2, "#f3e9bd");
  ellipsoid(x, 4, 18, 1, 1, 1, "#202b22");
  set(x, 3, 19, "#ffffff");
  set(x, 6, 19, "#a5c675");
}
for (const x of [18, 30]) {
  set(x, 9, 18, "#b3cd7c");
  set(x, 11, 17, "#a6c674");
}

// Filled polygon for each broad front paddle.
function inside(px, py, polygon) {
  let hit = false;
  for (let i = 0, j = polygon.length - 1; i < polygon.length; j = i++) {
    const a = polygon[i], b = polygon[j];
    if ((a[1] > py) !== (b[1] > py) &&
        px < (b[0] - a[0]) * (py - a[1]) / (b[1] - a[1]) + a[0])
      hit = !hit;
  }
  return hit;
}
const wing = [[0,16], [3,13], [7,10], [10,9], [10,14],
              [9,19], [7,23], [3,26], [0,25]];

for (const side of [-1, 1]) {
  const name = side < 0 ? "leftFlipper" : "rightFlipper";
  const pivotX = side < 0 ? 12 : 36;
  part(name, [pivotX, 20, 11]);
  for (let u = 0; u <= 10; u++) {
    for (let y = 9; y <= 26; y++) {
      if (!inside(u + 0.5, y + 0.5, wing)) continue;
      const x = pivotX + side * u;
      const top = 12 - Math.floor(u * 0.34);
      for (let z = top - 3; z <= top; z++)
        set(x, y, z, z === top ? skin : skinDark);
      if (rand() < 0.22) set(x, y, top, skinLight);
      // Bold, sparse scales on the upper paddle.
      if (u > 2 && u < 9 && (u + 2 * y) % 7 === 0 &&
          y > 12 && y < 23)
        set(x, y, top, "#b4ce7b");
    }
  }
  ellipsoid(pivotX, 20, 11, 2, 4, 3, skin);
  animate(name, { type: "swing", axis: "y",
                  speed: side < 0 ? 3 : -3, amplitude: 0.4 });
}
part("body", [24, 24, 12]);