← NaturalezaObjeto 3Dfogon/date-palm
Palmera datilera
Arrastrá para girarlo// «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]);
}import { draw } from "./assets/voxel";
import { datePalm } from "./assets/date-palm";
// En cada cuadro: parado sobre (x, y), el centro de su base.
draw(ctx, datePalm, x, y, { size: 64, angle: 0, t: performance.now() / 1000 });
// size: lo que mide en px su lado más largo
// angle: giro sobre el piso, en radianes
// t: segundos; si se mueve, se anima solo con eso# En una terminal, en la carpeta del juego
fogon assets add date-palm
# Con las herramientas de Fogón (conector)
add_assets({ slug: "<tu-juego>", ids: ["date-palm"] })
# Queda en client/assets/date-palm.ts
# https://fogon.gg/object/date-palm