Pudu
TODO: Intro (Claudio). End with what the reader can do: move Age, switch Sex, turn the knobs, stop the walk, try the parts view, export the walk sheet. How it works.
TODO: Intro (Claudio). Terminar con lo que puede hacer el lector: mover la Edad, cambiar el Sexo, girar las perillas, parar la caminata, probar la vista por partes y exportar la hoja de la caminata. Cómo funciona.
The idea in plain words
TODO: Claudio writes this. Core idea in one sentence (a generator that does not draw the pudu, but writes down its parts: body, legs, head, ears, antlers, spots; the parent index turns the list into a skeleton). One or two analogies with their limits (a puppet on strings? a paper doll with split pins?). Invite the reader to stop the walk and switch the parts view to By kind.
Why it matters
TODO: Claudio writes this. Verified facts, with their sources in the list below: (1) the genus Pudu are the smallest living true deer, but the smallest species is the northern pudu, not this one (Hershkovitz 1982); safe wording: 'the smallest deer in Chile' (CONAF 2019). (2) Range: in Chile from Maule (35°10' S) to Aysén (46°45' S), Chiloé included; in Argentina from Neuquén southwards (MMA); dense understory of the Valdivian temperate forest, sea level to 1,700 m. (3) Conservation, resolved: Near Threatened worldwide (IUCN 2016, before that Vulnerable), Vulnerable in Chile (DS 151/2007) and in Argentina (2019), CITES Appendix I; the categories differ because they differ in scope and date, and older pages still say 'IUCN Vulnerable'. (4) Names: pudú, 'pudu, ruco, puyú' in Mapudungun (CONAF 2019). (5) For a game: one description, sprites at 16 and 32 px and a walk sheet from the same seed.
Is there a theorem?
TODO: Claudio writes this, or drops the section. Technical notes: there is no theorem of how a pudu walks. What the generator uses is forward kinematics (a joint's position is the sum of the bone vectors above it), the classic of robotics and skeletal animation. The walk is a fixed gait, a 'lateral sequence' by its leg order: hind and fore on one side, then hind and fore on the other, a quarter cycle apart. Measured gaits of pudus were not found; this is an assumption.
The equations
θthigh = θrest + 0.35 · sin 2π(φ + o)
pi+1 = pi + ℓi (cos αi, sin αi), αi = αi−1 + θi
size = 0.45 + 0.55 · smoothstep(0, 0.8, age)
spots = round(22 · (1 − smoothstep(0.15, 0.55, age)))
Each leg is a chain of three bones: thigh, shin and hoof. The thigh swings with a sine of the walk phase φ, shifted by a quarter cycle per leg (o = 0, ¼, ½, ¾), so the four legs never move together. A joint's angle is added to the angle of the bone above it, and each bone starts where the previous one ends: that sum is forward kinematics, and it is exactly what the parent index of the description records. Age moves the size and the number of spots through smoothstep curves, so the slider never jumps.
What the picture does and does not say
- The height is checked against the sources: the generated adult measures about 39 cm at the withers (sources: 35–45 cm) and the newborn about 19 cm (15–25 cm). So is the body: about 81 cm head and body, measured along the back from rump to nose (sources: about 80 cm, under 85 cm). Until 29 October 2026 it was 62 cm, shortened to keep the silhouette round; now the sprites exaggerate the roundness instead, and the generator keeps the length. The readout under the drawing shows both numbers live.
- The antlers point forward by default, which is wrong: real pudu antlers are spikes "directed straight back" (Hershkovitz 1982). Forward keeps them visible in a side view, clear of the ears. Real angle (back) sets −70°.
- The raised middle of the back is a choice, not a fact. Hershkovitz describes the rump as little or not raised above the withers; the compact look comes from short legs and a short neck.
- The fawn's spots follow one source: three rows along the back and flank (Animal Diversity Web). CONAF describes whitish stripes on the back and spots on the flanks. They are gone at about two months (Hershkovitz: from the sixth week to the end of the second month; CONAF: up to three months); age on this page is not calibrated in months.
- The walk is a single fixed gait at one speed, chosen by eye. Only the near side has spots; females' frontal tufts are not drawn.
How it works
This part builds an animal on the same description the plants of Flora Lab use. It covers:
- a skeleton as a flat list of parts with parent indices;
- four legs that walk, by forward kinematics;
- age as smoothstep curves, and custom knobs in front of the generator;
- pixel sprites on one shared frame, and a walk sheet of four poses.
Plain JavaScript and Canvas 2D, no libraries. The generator is about 140 lines and touches
no DOM, so it runs in Node: a pose takes well under a millisecond. The renderers are the
generic ones of the shared core (src/scripts/artifact/), which know capsules,
discs and colour names, and nothing about deer.
1. A skeleton is a list with parent indices
The pudu is two flat lists, like the araucaria: parts (anything with volume along an axis) and details (eyes, nose, spots). Each part names the part it hangs from by index. In a tree that index means "this branch grows from that trunk piece"; in an animal it means "this shin hangs from that thigh", which is a skeleton. The body is three parts from hip to shoulder, the middle one raised and thickened by the chubby knob.
// 1. The body: three parts from hip to shoulder, the middle one raised (the hunch).
const sag = 0.025 * p.belly, fat = 1 + 0.4 * p.belly;
const spine = [P(-0.15, bodyY + 0.02), P(-0.04, bodyY + 0.05 - sag), P(0.07, bodyY + 0.035 - sag), P(0.15, bodyY)];
const body = [];
for (let i = 0; i < 3; i++)
body.push(addPart(art, { kind: "body", a: spine[i], b: spine[i + 1], r0: [0.9, fat, fat][i] * rb * k, r1: [fat, fat, 0.85][i] * rb * k,
role: "fur", parent: i ? body[i - 1] : -1 })); Switch the parts view to By kind to see the pieces in flat colours.
2. Age as curves
Everything that changes with age comes from one function. The fawn is about half the adult's size, a little leggier in proportion, spotted, and without antlers; antlers grow in males only, late in the curve.
export function stage(p) {
const grow = 0.45 + 0.55 * smoothstep(0, 0.8, p.age); // a fawn is about half the size (by eye)
return {
k: grow * p.shoulder / 0.38, // all lengths below are for a 38 cm adult
legs: 0.24 * (1 + 0.15 * (1 - p.age)), // fawns are leggier
spots: p.spots ?? Math.round(22 * (1 - smoothstep(0.15, 0.55, p.age))),
antler: p.sex === "male" ? p.antler * smoothstep(0.45, 0.9, p.age) : 0,
};
} 3. Legs, by forward kinematics
A leg is three bones. The thigh's angle comes from a sine of the walk phase; the knee bends
when the leg lifts (forward on the front legs, backward on the hind ones, where the joint
that looks like a knee is the hock); the hoof stays nearly flat. Each bone starts at the end
of the previous one and its angle adds to the one above, and parent records the
chain. Untick Walk to freeze the pose.
// Thigh, shin, hoof: angles add up down the chain, which is what the parent index means.
function leg(art, st, k, L, phase, bodyY) {
const w = 2 * Math.PI * (phase + L.off);
const swing = 0.35 * Math.sin(w), lift = Math.max(0, Math.sin(w + Math.PI / 2));
const upper = (L.hind ? 0.25 : -0.05) + swing;
const knee = (L.hind ? -0.75 : 0.25 + 0.6 * lift) - (L.hind ? 0.4 * lift : 0);
const len = [0.45, 0.45, 0.1].map((f) => f * st.legs * k);
const start = [L.at[0] + (L.hind ? 0.03 : -0.02) * k, (bodyY - 0.05) * k, L.z * k];
let a = start, ang = -Math.PI / 2 + upper, parent = L.from;
[["thigh", 0.04, 0.03, "fur"], ["shin", 0.022, 0.016, "fur"], ["hoof", 0.016, 0.014, "hoof"]].forEach(([kind, r0, r1, role], i) => {
if (i === 1) ang += knee;
if (i === 2) ang = -Math.PI / 2 + 0.6 * swing * 0.3; // the hoof stays roughly flat
const b = [a[0] + Math.cos(ang) * len[i], a[1] + Math.sin(ang) * len[i], a[2]];
parent = addPart(art, { kind, a, b, r0: r0 * k, r1: r1 * k, role, parent, group: L.hind ? 1 : 0 });
a = b;
});
} The legs swing, so the lowest hoof changes height with the pose. The last step lifts or lowers the whole animal until it stands on y = 0.
// 6. Stand on the ground: whatever the pose, the lowest hoof touches y = 0.
const ground = Math.min(...art.parts.flatMap((q) => [q.a[1] - q.r0, q.b[1] - q.r1]));
for (const q of art.parts) { q.a = [q.a[0], q.a[1] - ground, q.a[2]]; q.b = [q.b[0], q.b[1] - ground, q.b[2]]; }
for (const d of art.details) d.p = [d.p[0], d.p[1] - ground, d.p[2]]; 4. Spots, with keyed randomness
The spots sit in three rows, each nudged by its own key, u01(seed, index, salt):
a number from the shared SmallXXHash. Changing the number of spots does not reshuffle the
others, because no spot depends on how many random draws came before it.
// 5. A fawn's white spots, on the near flank, fading with age: three rows along the back
// and flank, as ADW describes them, each spot nudged by its own key.
const cols = Math.ceil(st.spots / 3);
for (let i = 0; i < st.spots; i++) {
const row = i % 3, col = Math.floor(i / 3);
const x = -0.14 + 0.28 * (col + 0.5) / cols + 0.02 * (u01(s, i, SALT.spot) - 0.5);
const y = bodyY + 0.075 - 0.028 * row + 0.01 * (u01(s, i, SALT.spotY) - 0.5);
const seg = Math.min(2, Math.max(0, Math.floor((x + 0.15) / 0.1)));
addDetail(art, { kind: "spot", shape: "disc", role: "spot", size: 0.02 * k, p: P(x, y, rb * 0.85), part: body[seg] });
} 5. Custom knobs
A knob is a parameter a designer thinks in, with its own function that turns it into the
generator's parameters. Chubby is one number that moves two: overall roundness and
the girth of the middle. Knobs sit in front of generate(), which stays a pure
function of its parameters; a knob on auto is left out.
export const KNOBS = [
{ id: "spots", label: { en: "spots", es: "pintitas" }, min: 0, max: 40, step: 1, to: (v) => ({ spots: v }) },
// "Guatón": a chubby pudu is rounder all over, and much more so in the middle.
{ id: "chubby", label: { en: "chubby", es: "guatón" }, min: 0, max: 1, step: 0.05, to: (v) => ({ roundness: 1 + 0.12 * v, belly: v }) },
]; export function applyKnobs(params, knobs, values = {}) {
let p = { ...params };
for (const k of knobs) {
const v = values[k.id];
if (v != null) p = { ...p, ...k.to(v, p) };
}
return p;
} 6. One frame for every pose
The generic pixel renderer fits whatever it is given into the sprite. Fitting each pose on its own makes the pudu grow and shrink as its legs move. The page generates the four exported poses on every change and draws all of them inside the union of their boxes.
// The four poses are generated on every change: they give the sprites and a fixed frame
// (the union of their boxes), so the drawing keeps one scale while the legs move.
let poses = [], frame = null, genMs = 0;
function rebuild() {
const t0 = performance.now();
poses = PHASES.map((phase) => generate(params({ phase })));
genMs = (performance.now() - t0) / PHASES.length;
frame = unionFrames(poses.map((a) => frameOf(a)));
} The walk sheet is those four poses in a row, ready for a sprite animator:
// The walk sheet: the four poses in a row, on one shared frame so the body does not jump.
$("fauna-sheet").addEventListener("click", () => {
const size = exportSize(), c = document.createElement("canvas");
c.width = size * PHASES.length; c.height = size;
const g = c.getContext("2d");
poses.forEach((art, i) => g.putImageData(spriteImage(sprite(art, size)), i * size, 0));
download(c, `${fileTag()}_walk4_${size}px.png`);
}); Why four poses?
Four is the smallest cycle in which every leg is at a different moment: each one is a
quarter cycle from the next. At 16 px the legs are one pixel wide, and more frames add
little that can be seen. More poses are one change to PHASES.
Going further
- The sprite (
render-pudu-silhouette.js) starts from the silhouette of the guide figure and draws only the legs per pose. Still to try: a selective outline (lighter where the light falls) and cleaner slopes on the legs. - Port the generator to C# for Islands (pure functions and flat data, as in the method note), with a golden JSON test from this page.
- More gaits (trot, the zigzag escape run) as other functions of the phase, and the huemul as the next animal on the same skeleton.
La idea en palabras simples
TODO: La escribe Claudio. Idea central en una frase (un generador que no dibuja el pudú, sino que anota sus partes: cuerpo, patas, cabeza, orejas, astas, pintitas; el índice de padre convierte la lista en un esqueleto). Una o dos analogías con sus límites (¿una marioneta de hilos?, ¿un muñeco de papel con encuadernadores?). Invitar a parar la caminata y poner la vista por partes en Por tipo.
Por qué importa
TODO: La escribe Claudio. Datos verificados, con sus fuentes en la lista: (1) el género Pudu son los ciervos verdaderos vivos más pequeños, pero la especie más pequeña es el pudú del norte, no este (Hershkovitz 1982); formulación segura: «el ciervo más pequeño de Chile» (CONAF 2019). (2) Distribución: en Chile, del Maule (35°10' S) a Aysén (46°45' S), Chiloé incluida; en Argentina, de Neuquén al sur (MMA); sotobosque denso del bosque templado valdiviano, del nivel del mar a 1.700 m. (3) Conservación, resuelto: Casi amenazado en el mundo (UICN 2016; antes, Vulnerable), Vulnerable en Chile (DS 151/2007) y en Argentina (2019), Apéndice I de CITES; las categorías difieren porque difieren en alcance y fecha, y las páginas antiguas aún dicen «UICN: Vulnerable». (4) Nombres: pudú; «pudu, ruco, puyú» en mapudungun (CONAF 2019). (5) Para un juego: una descripción, sprites de 16 y 32 px y una hoja de caminata desde la misma semilla.
¿Hay un teorema?
TODO: La escribe Claudio, o se quita la sección. Notas técnicas: no hay un teorema de cómo camina un pudú. El generador usa cinemática directa (la posición de una articulación es la suma de los vectores de los huesos que tiene encima), un clásico de la robótica y de la animación con esqueleto. La caminata es un paso fijo, de «secuencia lateral» por el orden de las patas: trasera y delantera de un lado, luego trasera y delantera del otro, a un cuarto de ciclo. No se encontraron mediciones de la marcha del pudú: es una suposición.
Las ecuaciones
θmuslo = θreposo + 0,35 · sen 2π(φ + o)
pi+1 = pi + ℓi (cos αi, sen αi), αi = αi−1 + θi
tamaño = 0,45 + 0,55 · smoothstep(0; 0,8; edad)
pintitas = round(22 · (1 − smoothstep(0,15; 0,55; edad)))
Cada pata es una cadena de tres huesos: muslo, caña y pezuña. El muslo oscila con un seno de la fase de la caminata φ, desplazado un cuarto de ciclo por pata (o = 0, ¼, ½, ¾), así que las cuatro patas nunca se mueven a la vez. El ángulo de una articulación se suma al del hueso de arriba, y cada hueso empieza donde termina el anterior: esa suma es la cinemática directa, y es exactamente lo que guarda el índice de padre de la descripción. La edad mueve el tamaño y el número de pintitas con curvas smoothstep, para que el deslizador nunca salte.
Lo que el dibujo dice y lo que no
- La altura está comprobada con las fuentes: el adulto generado mide unos 39 cm a la cruz (fuentes: 35–45 cm) y el recién nacido unos 19 cm (15–25 cm). El cuerpo también: unos 81 cm de cabeza y cuerpo, medidos a lo largo del lomo, de la grupa a la nariz (fuentes: unos 80 cm, menos de 85 cm). Hasta el 29 de octubre de 2026 medía 62 cm, acortado para que la silueta fuera redonda; ahora son los sprites los que exageran la redondez, y el generador conserva el largo. La lectura bajo el dibujo muestra las dos cifras en vivo.
- Las astas apuntan hacia delante por defecto, y eso es falso: las del pudú son puntas «dirigidas recto hacia atrás» (Hershkovitz 1982). Hacia delante se ven de perfil, sin taparse con las orejas. Ángulo real (atrás) las pone a −70°.
- El centro del lomo alzado es una elección, no un dato. Hershkovitz describe la grupa poco o nada más alta que la cruz; el aspecto compacto viene de las patas y el cuello cortos.
- Las pintitas del cervato siguen una sola fuente: tres filas a lo largo del lomo y el flanco (Animal Diversity Web). CONAF describe rayas blanquecinas en el lomo y manchas en los flancos. Desaparecen hacia los dos meses (Hershkovitz: de la sexta semana al final del segundo mes; CONAF: hasta los tres meses); la edad de esta página no está calibrada en meses.
- La caminata es un solo paso, a una sola velocidad, elegidos a ojo. Solo el lado cercano tiene pintitas, y no se dibujan los mechones frontales de las hembras.
Cómo funciona
Esta parte construye un animal sobre la misma descripción que usan las plantas de Flora Lab. Cubre:
- un esqueleto como lista plana de partes con índices de padre;
- cuatro patas que caminan, con cinemática directa;
- la edad como curvas smoothstep, y perillas a medida delante del generador;
- sprites de píxeles con un encuadre común, y una hoja de caminata de cuatro poses.
JavaScript y Canvas 2D, sin librerías. El generador tiene unas 140 líneas y no toca el DOM,
así que corre en Node: una pose tarda mucho menos de un milisegundo. Los renderizadores son
los genéricos del núcleo compartido (src/scripts/artifact/), que saben de
cápsulas, discos y nombres de color, y nada de ciervos. Los comentarios del código están en
inglés.
1. Un esqueleto es una lista con índices de padre
El pudú son dos listas planas, como la araucaria: partes (todo lo que tiene volumen a lo largo de un eje) y detalles (ojos, nariz, pintitas). Cada parte nombra por índice la parte de la que cuelga. En un árbol, ese índice significa «esta rama sale de este trozo de tronco»; en un animal, «esta caña cuelga de este muslo», y eso es un esqueleto. El cuerpo son tres partes de la cadera al hombro, con la del medio alzada y engrosada por la perilla guatón.
// 1. The body: three parts from hip to shoulder, the middle one raised (the hunch).
const sag = 0.025 * p.belly, fat = 1 + 0.4 * p.belly;
const spine = [P(-0.15, bodyY + 0.02), P(-0.04, bodyY + 0.05 - sag), P(0.07, bodyY + 0.035 - sag), P(0.15, bodyY)];
const body = [];
for (let i = 0; i < 3; i++)
body.push(addPart(art, { kind: "body", a: spine[i], b: spine[i + 1], r0: [0.9, fat, fat][i] * rb * k, r1: [fat, fat, 0.85][i] * rb * k,
role: "fur", parent: i ? body[i - 1] : -1 })); Pon la vista por partes en Por tipo para ver las piezas en colores planos.
2. La edad como curvas
Todo lo que cambia con la edad sale de una función. El cervato mide más o menos la mitad que el adulto, es algo más patilargo en proporción, tiene pintitas y no tiene astas; las astas crecen solo en los machos, al final de la curva.
export function stage(p) {
const grow = 0.45 + 0.55 * smoothstep(0, 0.8, p.age); // a fawn is about half the size (by eye)
return {
k: grow * p.shoulder / 0.38, // all lengths below are for a 38 cm adult
legs: 0.24 * (1 + 0.15 * (1 - p.age)), // fawns are leggier
spots: p.spots ?? Math.round(22 * (1 - smoothstep(0.15, 0.55, p.age))),
antler: p.sex === "male" ? p.antler * smoothstep(0.45, 0.9, p.age) : 0,
};
} 3. Patas, con cinemática directa
Una pata son tres huesos. El ángulo del muslo sale de un seno de la fase de la caminata; la
rodilla se dobla cuando la pata se levanta (hacia delante en las delanteras, hacia atrás en
las traseras, donde la articulación que parece una rodilla es el corvejón), y la pezuña se
mantiene casi plana. Cada hueso empieza al final del anterior y su ángulo se suma al de
arriba, y parent guarda la cadena. Desmarca Caminar para congelar la pose.
// Thigh, shin, hoof: angles add up down the chain, which is what the parent index means.
function leg(art, st, k, L, phase, bodyY) {
const w = 2 * Math.PI * (phase + L.off);
const swing = 0.35 * Math.sin(w), lift = Math.max(0, Math.sin(w + Math.PI / 2));
const upper = (L.hind ? 0.25 : -0.05) + swing;
const knee = (L.hind ? -0.75 : 0.25 + 0.6 * lift) - (L.hind ? 0.4 * lift : 0);
const len = [0.45, 0.45, 0.1].map((f) => f * st.legs * k);
const start = [L.at[0] + (L.hind ? 0.03 : -0.02) * k, (bodyY - 0.05) * k, L.z * k];
let a = start, ang = -Math.PI / 2 + upper, parent = L.from;
[["thigh", 0.04, 0.03, "fur"], ["shin", 0.022, 0.016, "fur"], ["hoof", 0.016, 0.014, "hoof"]].forEach(([kind, r0, r1, role], i) => {
if (i === 1) ang += knee;
if (i === 2) ang = -Math.PI / 2 + 0.6 * swing * 0.3; // the hoof stays roughly flat
const b = [a[0] + Math.cos(ang) * len[i], a[1] + Math.sin(ang) * len[i], a[2]];
parent = addPart(art, { kind, a, b, r0: r0 * k, r1: r1 * k, role, parent, group: L.hind ? 1 : 0 });
a = b;
});
} Las patas oscilan, así que la pezuña más baja cambia de altura con la pose. El último paso sube o baja todo el animal hasta que se apoya en y = 0.
// 6. Stand on the ground: whatever the pose, the lowest hoof touches y = 0.
const ground = Math.min(...art.parts.flatMap((q) => [q.a[1] - q.r0, q.b[1] - q.r1]));
for (const q of art.parts) { q.a = [q.a[0], q.a[1] - ground, q.a[2]]; q.b = [q.b[0], q.b[1] - ground, q.b[2]]; }
for (const d of art.details) d.p = [d.p[0], d.p[1] - ground, d.p[2]]; 4. Pintitas, con azar por clave
Las pintitas van en tres filas, cada una movida un poco por su propia clave,
u01(seed, index, salt): un número del SmallXXHash compartido. Cambiar el número
de pintitas no reordena las demás, porque ninguna depende de cuántas tiradas hubo antes.
// 5. A fawn's white spots, on the near flank, fading with age: three rows along the back
// and flank, as ADW describes them, each spot nudged by its own key.
const cols = Math.ceil(st.spots / 3);
for (let i = 0; i < st.spots; i++) {
const row = i % 3, col = Math.floor(i / 3);
const x = -0.14 + 0.28 * (col + 0.5) / cols + 0.02 * (u01(s, i, SALT.spot) - 0.5);
const y = bodyY + 0.075 - 0.028 * row + 0.01 * (u01(s, i, SALT.spotY) - 0.5);
const seg = Math.min(2, Math.max(0, Math.floor((x + 0.15) / 0.1)));
addDetail(art, { kind: "spot", shape: "disc", role: "spot", size: 0.02 * k, p: P(x, y, rb * 0.85), part: body[seg] });
} 5. Perillas a medida
Una perilla es un parámetro en el que piensa quien diseña, con su propia función para
convertirlo en parámetros del generador. Guatón es un número que mueve dos: la
redondez general y el grosor del medio. Las perillas van delante de generate(),
que sigue siendo una función pura de sus parámetros; una perilla en auto no se aplica.
export const KNOBS = [
{ id: "spots", label: { en: "spots", es: "pintitas" }, min: 0, max: 40, step: 1, to: (v) => ({ spots: v }) },
// "Guatón": a chubby pudu is rounder all over, and much more so in the middle.
{ id: "chubby", label: { en: "chubby", es: "guatón" }, min: 0, max: 1, step: 0.05, to: (v) => ({ roundness: 1 + 0.12 * v, belly: v }) },
]; export function applyKnobs(params, knobs, values = {}) {
let p = { ...params };
for (const k of knobs) {
const v = values[k.id];
if (v != null) p = { ...p, ...k.to(v, p) };
}
return p;
} 6. Un encuadre para todas las poses
El renderizador de píxeles genérico encaja en el sprite lo que se le dé. Si cada pose se encuadra por su cuenta, el pudú crece y se encoge al mover las patas. La página genera las cuatro poses exportadas en cada cambio y las dibuja todas dentro de la unión de sus cajas.
// The four poses are generated on every change: they give the sprites and a fixed frame
// (the union of their boxes), so the drawing keeps one scale while the legs move.
let poses = [], frame = null, genMs = 0;
function rebuild() {
const t0 = performance.now();
poses = PHASES.map((phase) => generate(params({ phase })));
genMs = (performance.now() - t0) / PHASES.length;
frame = unionFrames(poses.map((a) => frameOf(a)));
} La hoja de caminata son esas cuatro poses en fila, lista para un animador de sprites:
// The walk sheet: the four poses in a row, on one shared frame so the body does not jump.
$("fauna-sheet").addEventListener("click", () => {
const size = exportSize(), c = document.createElement("canvas");
c.width = size * PHASES.length; c.height = size;
const g = c.getContext("2d");
poses.forEach((art, i) => g.putImageData(spriteImage(sprite(art, size)), i * size, 0));
download(c, `${fileTag()}_walk4_${size}px.png`);
}); ¿Por qué cuatro poses?
Cuatro es el ciclo más corto en el que cada pata está en un momento distinto: cada una va
a un cuarto de ciclo de la siguiente. A 16 px las patas miden un píxel de ancho, y más
fotogramas añaden poco que se vea. Más poses son un cambio en PHASES.
Para ir más lejos
- El sprite (
render-pudu-silhouette.js) parte de la silueta de la figura guía y solo dibuja las patas en cada pose. Queda por probar: un contorno selectivo (más claro donde da la luz) y pendientes más limpias en las patas. - Portar el generador a C# para Islands (funciones puras y datos planos, como en la nota de método), con una prueba de oro en JSON desde esta página.
- Más pasos (trote, la huida en zigzag) como otras funciones de la fase, y el huemul como siguiente animal sobre el mismo esqueleto.
SourcesFuentes
- P. Hershkovitz, "Neotropical deer (Cervidae) Part I. Pudus, genus Pudu Gray", Fieldiana Zoology n.s. 11 (1982) 1–86. Antlers "directed straight back", the spotted juvenal coat of P. puda (and not of the northern pudu), spots lost by the end of the second month, short legs and rounded ears, the northern pudu as the smaller species. archive.org/details/neotropicaldeerc11hers
- D. Villalobos, G. Svensson, A. Hinojosa (eds.), Plan Nacional de conservación del pudú (Pudu puda) en Chile, CONAF (2019). Size, coat, fawn stripes and spots, Mapudungun names, habitat, and the national and IUCN categories. fondos.mma.gob.cl/wp-content/uploads/2024/08/PNC-Pudu-puda-CONAF-2019.pdf
- Ministerio del Medio Ambiente (Chile), Reglamento de Clasificación de Especies, species sheet "Pudú". Range in Chile and Argentina, habitat, Vulnerable (DS 151/2007). clasificacionespecies.mma.gob.cl/wp-content/uploads/2019/10/Ficha_Web_PUDU.pdf
- E. Silva-Rodríguez, H. Pastore, J. Jiménez, "Pudu puda", IUCN Red List of Threatened Species 2016: e.T18848A22164089. Near Threatened (read through CONAF 2019; the Red List page did not load). iucnredlist.org/species/18848/22164089
- M. Robidoux, "Pudu puda", Animal Diversity Web (2014). Shoulder height 35–45 cm, weight 6.5–13.5 kg, antlers 60–90 mm, three rows of spots on the fawn, lost by about 8 weeks. animaldiversity.org/accounts/Pudu_puda/
- B. Saavedra, J. A. Simonetti, "Archaeological evidence of Pudu pudu (Cervidae) in central Chile", Zeitschrift für Säugetierkunde 56 (1991) 252–253. Shoulder 400 mm, antler beam angled back. archive.org/details/biostor-183396