fogón

← Animals3D assetfogon/mariposa

Mariposa

by fogonCC04905 blocks
MariposaDrag to turn it
// «Mariposa» · gpt-6-sol (medium)
// A big cartoon butterfly seen from above but standing on the floor, body along y: a thin black body with antennae and four big wings in bright orange with black veins and white spots on the edges (monarch style).

const black = "#211d21";
const edge = "#17191d";
const white = "#fff7e5";

const fore = [
  [20,14], [16,11], [10,8], [6,8], [4,10], [2,15],
  [3,19], [7,23], [13,26], [20,26]
];
const hind = [
  [20,25], [13,25], [8,27], [5,31], [6,36],
  [10,40], [16,41], [19,38], [20,34]
];

function inside(x, y, poly) {
  let hit = false;
  for (let i = 0, j = poly.length - 1; i < poly.length; j = i++) {
    const a = poly[i], b = poly[j];
    if ((a[1] > y) !== (b[1] > y) &&
        x < (b[0] - a[0]) * (y - a[1]) / (b[1] - a[1]) + a[0]) hit = !hit;
  }
  return hit;
}

function edgeDistance(x, y, poly) {
  let best = 100;
  for (let i = 0; i < poly.length; i++) {
    const a = poly[i], b = poly[(i + 1) % poly.length];
    const dx = b[0] - a[0], dy = b[1] - a[1];
    const t = Math.max(0, Math.min(1,
      ((x - a[0]) * dx + (y - a[1]) * dy) / (dx * dx + dy * dy)));
    best = Math.min(best, Math.hypot(x - a[0] - t * dx, y - a[1] - t * dy));
  }
  return best;
}

function wing(side, kind) {
  const front = kind === "fore";
  const poly = front ? fore : hind;
  const mirror = x => side === "left" ? x : 48 - x;
  const pivot = [mirror(20), front ? 20 : 30, 5];
  const name = `${side}_${kind}`;
  part(name, pivot);

  for (let y = 7; y <= 42; y++) {
    for (let x = 2; x <= 20; x++) {
      if (!inside(x + 0.5, y + 0.5, poly)) continue;
      const d = edgeDistance(x + 0.5, y + 0.5, poly);
      const border = d < 2.1;
      const outerOrange = d < 3.5;
      const X = mirror(x);
      set(X, y, 3, border ? edge : "#a94914");
      set(X, y, 4, border ? edge : "#d76618");
      set(X, y, 5, border ? black : "#ee831c");
      set(X, y, 6, border ? edge :
        outerOrange ? "#dc6816" :
        rand() < 0.13 ? "#ffac32" : rand() < 0.13 ? "#e97419" : "#f68d20");

      // Warm golden patches near the root of each wing.
      const rootY = front ? 22 : 28;
      if (!border && x > 13 && Math.hypot(x - 20, (y - rootY) * 1.2) < 7)
        set(X, y, 6, rand() < 0.2 ? "#ffcf55" : "#ffac2d");
    }
  }

  // Fine black monarch veins, radiating from the wing joints.
  const veins = front ? [
    [[19,23],[7,10]], [[19,23],[4,14]], [[19,23],[4,19]],
    [[19,23],[9,24]], [[16,18],[11,9]]
  ] : [
    [[19,27],[8,28]], [[19,27],[7,33]], [[19,27],[10,38]],
    [[19,27],[16,39]], [[13,31],[7,35]]
  ];
  for (const [a, b] of veins) {
    const steps = Math.ceil(Math.hypot(b[0] - a[0], b[1] - a[1]) * 3);
    for (let i = 0; i <= steps; i++) {
      const t = i / steps;
      const x = Math.round(a[0] + (b[0] - a[0]) * t);
      const y = Math.round(a[1] + (b[1] - a[1]) * t);
      if (inside(x + 0.5, y + 0.5, poly)) set(mirror(x), y, 7, black);
    }
  }

  // Oversized ivory spots along the black outer margins.
  const spots = front
    ? [[7,10],[5,13],[4,17],[5,20],[8,22],[11,24],[12,11],[16,14]]
    : [[9,28],[7,31],[8,35],[11,38],[15,39],[18,36]];
  for (const [sx, sy] of spots) {
    for (let dy = -1; dy <= 1; dy++)
      for (let dx = -1; dx <= 1; dx++) {
        const x = sx + dx, y = sy + dy;
        if (dx * dx + dy * dy <= 1 && inside(x + 0.5, y + 0.5, poly))
          set(mirror(x), y, 7, white);
      }
    if (inside(sx + 0.5, sy + 0.5, poly))
      set(mirror(sx), sy, 8, "#ffffff");
  }

  animate(name, {
    type: "swing", axis: "y",
    speed: side === "left" ? 8 : -8, amplitude: 0.6
  });
}

wing("left", "hind");
wing("right", "hind");
wing("left", "fore");
wing("right", "fore");

part("body", [24,24,5]);

// Little feet hold the butterfly above the floor.
for (const y of [19,29]) {
  for (const x of [21,27]) {
    line(24, y, 5, x, y + 2, 0, 0, black);
    box(x - 1, y + 1, 0, x + 1, y + 3, 0, "#30272a");
  }
}

// Slim segmented body, with a round toy-like head.
ellipsoid(24, 31, 6, 3, 8, 3, "#30262a");
ellipsoid(24, 22, 7, 4, 6, 3, "#30282a");
ellipsoid(24, 13, 7, 4, 5, 4, "#45332c");
ellipsoid(24, 12, 9, 3, 4, 2, "#574037");

for (const y of [28,32,36])
  box(22, y, 9, 26, y, 9, "#71503a");
line(24, 18, 10, 24, 26, 10, 0, "#765038");

// Simple, bright eyes read clearly from above.
for (const x of [22,26]) {
  box(x - 1, 11, 11, x, 13, 11, "#151619");
  set(x - 1, 11, 12, "#ffffff");
}

// Curled antennae reach forward of the head.
line(23, 9, 9, 20, 5, 9, 0, black);
line(20, 5, 9, 17, 4, 9, 0, black);
line(25, 9, 9, 28, 5, 9, 0, black);
line(28, 5, 9, 31, 4, 9, 0, black);
sphere(17, 4, 9, 1, "#342428");
sphere(31, 4, 9, 1, "#342428");