fogón

← Parties and Christmas3D assetfogon/globos

Globos

by fogonCC03428 blocks
GlobosDrag to turn it
// «Globos» · gpt-6-sol (medium)
// A bunch of six shiny party balloons in red, yellow, blue, green, pink and purple floating at different heights, each egg-shaped with a small knot at the bottom and a light highlight, their thin white strings gathered down to a small gift-box weight on the floor.

// Gift-box weight
box(20, 21, 0, 28, 27, 4, "#d73343");
box(20, 21, 4, 28, 27, 5, "#f04b56");
box(20, 21, 0, 20, 27, 3, "#ad2537");
box(21, 21, 0, 27, 21, 3, "#ef4050");

// Gold ribbon and bow
box(23, 20, 0, 25, 28, 5, "#f5c950");
box(20, 23, 0, 28, 24, 5, "#f5c950");
box(23, 20, 2, 25, 20, 4, "#ffe58a");
box(21, 23, 5, 27, 24, 5, "#ffe58a");
box(23, 22, 6, 25, 24, 7, "#ffdf70");
box(20, 22, 6, 22, 24, 7, "#ffdf70");
box(26, 22, 6, 28, 24, 7, "#ffdf70");
box(22, 22, 7, 26, 24, 8, "#fff0a6");

const balloons = [
  { x: 9,  y: 20, z: 32, c: ["#b72236", "#f0444e", "#ff6970"] }, // red
  { x: 21, y: 29, z: 37, c: ["#d99620", "#ffd348", "#ffeb86"] }, // yellow
  { x: 38, y: 20, z: 32, c: ["#2365b9", "#3999eb", "#77c8ff"] }, // blue
  { x: 11, y: 32, z: 39, c: ["#258552", "#46c878", "#86eaa0"] }, // green
  { x: 25, y: 15, z: 29, c: ["#c05291", "#f28bc2", "#ffc0df"] }, // pink
  { x: 39, y: 31, z: 39, c: ["#6849aa", "#a17be2", "#cbb2ff"] }  // purple
];

part("balloons", [24, 24, 7]);

// Draw strings first so the balloon bodies sit visibly in front of them.
for (const b of balloons) {
  const kx = b.x, ky = b.y, kz = b.z - 8;
  line(24, 24, 8, 24 + (kx - 24) * 0.27, 24 + (ky - 24) * 0.27,
       15, 0, "#e7e8f0");
  line(24 + (kx - 24) * 0.27, 24 + (ky - 24) * 0.27, 15,
       kx - (kx - 24) * 0.12, ky - (ky - 24) * 0.12,
       kz - 4, 0, "#ffffff");
  line(kx - (kx - 24) * 0.12, ky - (ky - 24) * 0.12,
       kz - 4, kx, ky, kz, 0, "#e7e8f0");
}

for (const b of balloons) {
  const rx = 4.5, ry = 3.7, rz = 6;

  // Rounded upper body, with a brighter upper/front face.
  for (let z = b.z - 5; z <= b.z + 6; z++) {
    for (let y = b.y - 4; y <= b.y + 4; y++) {
      for (let x = b.x - 5; x <= b.x + 5; x++) {
        const d = ((x - b.x) / rx) ** 2 +
                  ((y - b.y) / ry) ** 2 +
                  ((z - b.z) / rz) ** 2;
        if (d > 1) continue;

        let color = b.c[1];
        if (x > b.x + 2 || z < b.z - 2) color = b.c[0];
        if (z >= b.z + 1 && x <= b.x + 1 && y <= b.y)
          color = b.c[2];
        set(x, y, z, color);
      }
    }
  }

  // Tapered neck and the little tied knot.
  ellipsoid(b.x, b.y, b.z - 6, 2, 2, 1, b.c[1]);
  box(b.x - 1, b.y - 1, b.z - 7, b.x + 1, b.y + 1, b.z - 7, b.c[0]);
  box(b.x - 1, b.y - 1, b.z - 8, b.x + 1, b.y + 1, b.z - 8, b.c[2]);

  // Curved, toy-like glint on the front-left surface.
  for (let z = b.z; z <= b.z + 4; z++) {
    const x = b.x - (z >= b.z + 3 ? 1 : 2);
    const inside = 1 - ((x - b.x) / rx) ** 2 -
                       ((z - b.z) / rz) ** 2;
    if (inside <= 0) continue;
    const y = b.y - Math.floor(ry * Math.sqrt(inside));
    set(x, y, z, "#ffffff");
  }
  set(b.x - 3, b.y - 2, b.z - 1, b.c[2]);
}

animate("balloons", { type: "swing", axis: "y", speed: 1.5, amplitude: 0.15 });
animate("balloons", { type: "bob", axis: "z", speed: 2, amplitude: 1 });