fogón

← Nature3D assetfogon/date-palm

Palmera datilera

by fogonCC02482 blocks
Palmera datileraDrag to turn it
// «Palmera datilera» · propio
// A date palm: a slightly leaning ringed trunk, a crown of eleven long drooping green fronds, and a cluster of orange dates under the crown.

// A date palm: a ringed trunk that leans a little, a crown of eleven long drooping fronds and clusters of orange
// dates under it.
const bark = ["#6f5332", "#84643d", "#977549", "#a98656", "#b99665"];
const groove = "#57401f";
const leaf = ["#2f5a22", "#3d6f2a", "#4f8734", "#63a042", "#7ab552"];
const dates = ["#b5611c", "#d17a22", "#e8962e", "#f2ad45"];
const clampi = (v, lo, hi) => Math.max(lo, Math.min(hi, v));
const TOP = 31;
// The trunk's centre at each height: it leans to the right as it goes up.
const centre = (z) => [20.5 + 4.5 * Math.pow(z / TOP, 1.6), 24];

for (let z = 0; z <= TOP; z++) {
  const [cx, cy] = centre(z);
  const r = z < 4 ? 3.9 - z * 0.3 : 2.8 - (z / TOP) * 0.5;
  for (let x = Math.floor(cx - r - 1); x <= Math.ceil(cx + r + 1); x++)
    for (let y = Math.floor(cy - r - 1); y <= Math.ceil(cy + r + 1); y++) {
      const d = Math.hypot(x - cx, y - cy);
      if (d > r) continue;
      // Scales left by the old fronds: rings, each a little wider at its top.
      const ring = z % 3 === 0;
      let tone = 2 + (x < cx && y <= cy ? 1 : 0) - (x > cx + 0.5 && y > cy ? 1 : 0) - (z % 3 === 1 ? 1 : 0);
      if (rand() < 0.12) tone--;
      set(x, y, z, ring && d > r - 1.2 ? groove : bark[clampi(tone, 0, 4)]);
      if (z % 3 === 2 && d > r - 0.7) {
        const a = Math.atan2(y - cy, x - cx);
        set(x + Math.round(Math.cos(a)), y + Math.round(Math.sin(a)), z, bark[clampi(tone + 1, 0, 4)]);
      }
    }
}
const [CX, CY] = centre(TOP);
// The heart of the crown.
sphere(CX, CY, TOP + 1.5, 3, bark[1]);
sphere(CX, CY, TOP + 3, 2.2, leaf[2]);

// The fronds: each rises a little and then hangs, narrower towards its tip, with a pale rib and cut edges.
const N = 11, LEN = 19.5;
for (let i = 0; i < N; i++) {
  const a = (i / N) * Math.PI * 2 + 0.2 + (rand() - 0.5) * 0.18;
  const dx = Math.cos(a), dy = Math.sin(a);
  const lift = i % 2 ? 1.5 : -1;
  for (let r = 1.5; r <= LEN; r += 0.5) {
    const t = r / LEN;
    const zc = TOP + 3 + lift * (1 - t) + 6.4 * (0.95 * Math.sin(t * 2.2) - 1.95 * t * t);
    const w = 0.5 + 3.1 * (1 - t * 0.82) * Math.min(1, t * 5);
    for (let s = -w; s <= w; s += 0.5) {
      const edge = Math.abs(s) / w;
      // Leaflets: gaps along the edges.
      if (edge > 0.55 && Math.floor(r * 1.1) % 2 === 0) continue;
      const x = CX + dx * r - dy * s, y = CY + dy * r + dx * s;
      const z = zc - Math.abs(s) * 0.55;
      let tone = Math.abs(s) < 0.5 ? 4 : 2 + (Math.floor(r) % 2) - (edge > 0.7 ? 1 : 0);
      if (t > 0.75) tone--;
      if (-dx - dy > 0.6) tone++;
      set(x, y, z, leaf[clampi(tone, 0, 4)]);
      if (Math.abs(s) < 0.5) set(x, y, z - 1, leaf[1]);
    }
  }
}
// Dates: bunches hanging on their stalks under the crown.
for (const [a, drop] of [[0.6, 5], [2.3, 6], [3.8, 5], [5.3, 6]]) {
  const x = CX + Math.cos(a) * 3.6, y = CY + Math.sin(a) * 3.6;
  line(CX + Math.cos(a) * 1.5, CY + Math.sin(a) * 1.5, TOP + 1, x, y, TOP - 1, 0, "#c9a04a");
  for (let k = 0; k < drop; k++) {
    const r = k < 2 ? 1.2 : k < 4 ? 1.7 : 1.1;
    for (let q = 0; q < 14; q++) {
      const b = rand() * Math.PI * 2, e = rand() * r;
      set(x + Math.cos(b) * e, y + Math.sin(b) * e, TOP - 1 - k, dates[clampi(1 + Math.floor(rand() * 3) - (k > 3 ? 1 : 0), 0, 3)]);
    }
  }
}
// Roots spreading at the foot.
for (const a of [0.4, 1.9, 3.3, 4.6, 5.6]) {
  line(20.5, 24, 2, 20.5 + Math.cos(a) * 5, 24 + Math.sin(a) * 5, 0, 0.8, bark[1]);
}