fogón

← Fiestas y NavidadObjeto 3Dfogon/baston-de-caramelo

Bastón de caramelo

de fogonCC04417 bloques
Bastón de carameloArrastrá para girarlo
// «Bastón de caramelo» · gpt-6-sol (medium)
// A giant candy cane standing upright on its straight end: a thick round stick with a hook curving at the top, striped diagonally in bright red and white, with a slight shine on one side. A small green ribbon bow tied below the hook.

const cx = 29, cy = 24, radius = 4.25;
const path = [];
let distance = 0;

function point(x, z, tx, tz) {
  if (path.length) {
    const p = path[path.length - 1];
    distance += Math.hypot(x - p.x, z - p.z);
  }
  path.push({ x, z, tx, tz, s: distance });
}

// Straight stick, rounded hook, and short downward-pointing tip.
for (let z = 4; z <= 31; z += 0.4) point(cx, z, 0, 1);
for (let a = 0.045; a <= Math.PI; a += 0.045) {
  point(20 + 9 * Math.cos(a), 31 + 9 * Math.sin(a),
        -Math.sin(a), Math.cos(a));
}
for (let z = 30.6; z >= 27; z -= 0.4) point(11, z, 0, -1);

for (let x = 6; x <= 34; x++) {
  for (let y = 19; y <= 29; y++) {
    for (let z = 0; z <= 45; z++) {
      let nearest, d2 = Infinity;
      for (const p of path) {
        const v = (x - p.x) ** 2 + (z - p.z) ** 2;
        if (v < d2) { d2 = v; nearest = p; }
      }
      const dy = y - cy;
      if (d2 + dy * dy > radius * radius) continue;

      const cross = (x - nearest.x) * nearest.tz -
                    (z - nearest.z) * nearest.tx;
      const angle = Math.atan2(dy, cross);
      let stripe = (nearest.s / 8.3 + angle / (2 * Math.PI)) % 1;
      if (stripe < 0) stripe++;

      const red = stripe < 0.46;
      let color;
      if (red) {
        color = dy > 1.5 ? "#d91930" : "#f3293c";
        if (dy < -2 && cross < -0.8) color = "#ff6470";
      } else {
        color = dy > 1.5 ? "#e5e7e2" : "#fff9ed";
        if (dy < -2 && cross < -0.8) color = "#ffffff";
      }
      set(x, y, z, color);
    }
  }
}

// The two dangling ribbon ends sit in front of the cane.
for (let z = 15; z <= 23; z++) {
  for (const side of [-1, 1]) {
    const center = 29 + side * (23 - z) * 0.76;
    for (let x = Math.floor(center - 2); x <= Math.ceil(center + 2); x++) {
      for (let y = 17; y <= 19; y++) {
        if (Math.abs(x - center) > 1.85) continue;
        // Small V-cut at each ribbon tip.
        if (z === 15 && Math.abs(x - center) < 0.7) continue;
        set(x, y, z, y === 17 ? "#55df72" :
          x < center ? "#28b84f" : "#168c3c");
      }
    }
  }
}

// Puffy, open bow loops.
for (const loop of [
  { x: 23.7, z: 24.7, lean: -0.12 },
  { x: 34.1, z: 24.7, lean: 0.12 }
]) {
  for (let x = 18; x <= 40; x++) {
    for (let z = 20; z <= 29; z++) {
      const u = x - loop.x - loop.lean * (z - loop.z);
      const v = z - loop.z;
      const outer = (u / 5.35) ** 2 + (v / 3.45) ** 2;
      const inner = (u / 2.75) ** 2 + (v / 1.55) ** 2;
      if (outer > 1 || inner < 1) continue;
      for (let y = 15; y <= 18; y++) {
        set(x, y, z, z >= loop.z && y <= 16 ? "#64e77c" :
          y === 18 || z < 23 ? "#168e3d" : "#2bc354");
      }
    }
  }
}

// Rounded knot and a small glossy glint.
ellipsoid(29, 15, 24, 2.5, 2.1, 2.7, "#149c43");
ellipsoid(28.6, 13.7, 24.5, 1.8, 0.9, 2.0, "#43d865");
set(28, 13, 25, "#a5f5a3");