fogón

← Fiestas y NavidadObjeto 3Dfogon/arbolito-de-navidad

Arbolito de Navidad

de fogonCC011376 bloques
Arbolito de NavidadArrastrá para girarlo
// «Arbolito de Navidad» · gpt-6-sol (medium)
// A chunky Christmas tree: a cone of three stacked layers of dark and bright green branches with jagged edges, standing in a red pot wrapped with a gold ribbon. Round ornaments in red, gold and blue hang on the layers, a string of small glowing lights spirals around it, and a big five-pointed gold star sits on top.

const CX = 24, CY = 24;

const tiers = [
  { bottom: 9, top: 24, base: 16, tip: 4.5, phase: 0.2 },
  { bottom: 19, top: 32, base: 12.5, tip: 3.2, phase: 1.7 },
  { bottom: 28, top: 39, base: 8.8, tip: 0.7, phase: 3.1 }
];

function tierRadius(t, z, angle) {
  const u = (z - t.bottom) / (t.top - t.bottom);
  let r = t.base + (t.tip - t.base) * u;
  const jag = Math.sin(angle * 11 + t.phase) * 0.55
            + Math.sin(angle * 19 - z * 0.7) * 0.24;
  r += jag * (0.4 + 0.6 * (1 - u));
  if (z <= t.bottom + 2) r += 0.5;
  return r;
}

function treeRadius(z, angle) {
  let r = 0;
  for (const t of tiers)
    if (z >= t.bottom && z <= t.top)
      r = Math.max(r, tierRadius(t, z, angle));
  return r;
}

// Chunky red pot, slightly wider at the rolled lip.
for (let z = 0; z <= 8; z++) {
  const radius = z >= 7 ? 8 : z <= 1 ? 6.5 : 7.3;
  for (let x = 15; x <= 33; x++)
    for (let y = 15; y <= 33; y++) {
      const d = Math.hypot(x - CX, y - CY);
      if (d > radius) continue;
      let color = z >= 7 ? "#ff4242" : "#db202d";
      if (y <= 20) color = z >= 7 ? "#ff6253" : "#f33138";
      if (z <= 1 || d > radius - 1.1) color = "#bb1729";
      if (z === 8 && d > radius - 1.5) color = "#ff7770";
      set(x, y, z, color);
    }
}

// Gold ribbon encircling the pot.
for (let z = 3; z <= 4; z++)
  for (let x = 15; x <= 33; x++)
    for (let y = 15; y <= 33; y++) {
      const d = Math.hypot(x - CX, y - CY);
      if (d >= 6.35 && d <= 7.65)
        set(x, y, z, z === 4 ? "#ffe16a" : "#e9a927");
    }
box(23, 16, 1, 25, 17, 7, "#ffd651");
box(24, 15, 2, 24, 16, 7, "#fff0a0");

// Oversized toy-like ribbon bow on the front.
ellipsoid(20, 15, 5, 3, 1.2, 1.8, "#efad30");
ellipsoid(28, 15, 5, 3, 1.2, 1.8, "#efad30");
ellipsoid(20, 14, 5, 2, 0.8, 1, "#ffe477");
ellipsoid(28, 14, 5, 2, 0.8, 1, "#ffe477");
sphere(24, 14, 5, 1.6, "#ffdc55");
set(24, 13, 6, "#fff2ae");

// Short trunk tucked into the branches.
cylinder(CX, CY, 7, 3, 6, "#87502b");
box(22, 21, 8, 25, 22, 12, "#aa6937");

// Three overlapping, solid, scalloped tiers of evergreen branches.
for (let i = 0; i < tiers.length; i++) {
  const t = tiers[i];
  for (let z = t.bottom; z <= t.top; z++)
    for (let x = 7; x <= 41; x++)
      for (let y = 7; y <= 41; y++) {
        const dx = x - CX, dy = y - CY;
        const angle = Math.atan2(dy, dx);
        const d = Math.hypot(dx, dy);
        const r = tierRadius(t, z, angle);
        if (d > r) continue;

        const nearEdge = d > r - 2.3;
        const groove = Math.sin(angle * 11 + t.phase) < -0.45;
        let color = "#176b3b";
        if (nearEdge) {
          color = groove ? "#12552f" : "#21964b";
          if (z > t.bottom + 2 && rand() < 0.28)
            color = "#32b956";
          if (z <= t.bottom + 1)
            color = groove ? "#104c2e" : "#197c3c";
        } else if (rand() < 0.13) {
          color = "#208746";
        }
        if (y < CY - 5 && nearEdge && rand() < 0.13)
          color = "#4bc768";
        set(x, y, z, color);
      }
}

// A fine golden cord spirals over the foliage.
for (let z = 11.5; z <= 37.5; z += 0.18) {
  const angle = -Math.PI / 2 + (z - 11.5) * 0.79;
  const r = treeRadius(Math.round(z), angle) + 0.5;
  set(CX + Math.cos(angle) * r, CY + Math.sin(angle) * r,
      z, "#d6b853");
}

// Glossy round ornaments, placed mostly on the viewer-facing branches.
const baubles = [
  [12, -2.35, 0], [14, -1.42, 1], [15, -0.58, 2],
  [17, -2.03, 2], [18, -0.95, 0], [20, -1.67, 1],
  [21, -0.37, 0], [23, -2.48, 1], [24, -1.14, 2],
  [26, -0.53, 1], [27, -2.04, 0], [29, -1.27, 1],
  [31, -0.52, 2], [32, -2.22, 0], [34, -1.57, 2],
  [36, -0.86, 0]
];
const ornamentColors = ["#ec3441", "#f5bd34", "#378bdf"];
const highlights = ["#ff9b9c", "#fff0a0", "#a5e2ff"];

for (const [z, angle, kind] of baubles) {
  const r = treeRadius(z, angle) + 0.35;
  const x = CX + Math.cos(angle) * r;
  const y = CY + Math.sin(angle) * r;
  sphere(x, y, z, 1.35, ornamentColors[kind]);
  set(x - 0.5, y - 0.9, z + 0.7, highlights[kind]);
}

// Individual bulbs form a separate animated string.
part("lights", [CX, CY, 24]);
for (let z = 12; z <= 37; z += 0.78) {
  const angle = -Math.PI / 2 + (z - 11.5) * 0.79;
  const r = treeRadius(Math.round(z), angle) + 1.1;
  const x = CX + Math.cos(angle) * r;
  const y = CY + Math.sin(angle) * r;
  sphere(x, y, z, 0.9, "#fff3ac");
}
animate("lights", {
  type: "colors",
  palette: ["#ff535e", "#ffe66e", "#60e883", "#69baff", "#fffaf0"],
  pattern: "flicker",
  glow: 0.8
});

// Thick, upright five-pointed star on the very top.
part("star", [CX, 21, 40]);
const vertices = [];
for (let i = 0; i < 10; i++) {
  const a = Math.PI / 2 + i * Math.PI / 5;
  const r = i % 2 === 0 ? 5 : 2.25;
  vertices.push([CX + Math.cos(a) * r, 40 + Math.sin(a) * r]);
}
function insideStar(x, z) {
  let inside = false;
  for (let i = 0, j = 9; i < 10; j = i++) {
    const [xi, zi] = vertices[i], [xj, zj] = vertices[j];
    if ((zi > z) !== (zj > z) &&
        x < (xj - xi) * (z - zi) / (zj - zi) + xi)
      inside = !inside;
  }
  return inside;
}
for (let x = 18; x <= 30; x++)
  for (let z = 34; z <= 45; z++)
    if (insideStar(x, z)) {
      for (let y = 19; y <= 22; y++)
        set(x, y, z, y === 19 ? "#ffe779" :
            y === 22 ? "#c98c23" : "#f7bd39");
    }
sphere(CX, 18, 40, 1.35, "#fff0a0");
animate("star", {
  type: "colors",
  palette: ["#f8bb39", "#ffe47a", "#fff2ab"],
  pattern: "pulse",
  glow: 0.9
});