Skip to content

Frog on a lily pad: the agent's report

Round 2. Brief: a cartoon frog character sitting on a lily pad, about 3 units tall, with body, head, two bulging eyes with pupils, a mouth line, four legs with webbed feet, skin texture, a notched lily pad, and materials for skin, eye white, pupil and leaf; iterate until the beauty render is presentable. The agent read only the skill, the two guides, the reference and three examples. Ten render iterations of fourteen.

What follows is the agent's report, verbatim. The "fixed" notes in the index (dogfood/README.md) say what changed in the tool because of it.

1. The finished model

A 4.6 × 3.0 × 4.6 unit scene: a squat green frog (2.9 tall) facing +z on a 4.6-wide lily pad with a wedge notch at the front-left and nine faint radial veins. The frog is two ellipsoids blended with union(k=0.35) and roughened with displace(0.02, 0.25) under a two-tone noise skin material; a pale noise-patterned belly ellipsoid; two 0.42-radius white eyeballs bulging from a skin-coloured socket ridge, with flattened black ellipsoid pupils and a small white glint each; a black smile drawn as a 0.045 tube along a five-point spline on the head surface; two nostril dots; hind legs as two-point tubes (hip → knee at the back, knee → ankle forward) and front legs as one tube each, all ending in three-toed capsule feet with a thin extruded triangle as the web, built once and mirror("x")ed. Every item in the brief is present and reads in the beauty render, which is presentable: soft shadows, mottled skin, readable eyes, a pose that says "sitting". The report says it stands, is one piece, and rests on y = 0. Compared to the plan the only shortfalls are cosmetic: the frog's back is a plain blob, the veins are faint, and the mesh-rasterised views (not the beauty render) dither every material boundary.

2. Friction log

  • ground() on the whole scene lifted the pad off the floor. Line (v1): model = (pad + frog) | ground(). Expected: the pad already sat on y = 0, so nothing moves. What happened: check printed frog ... y -0.11..2.87, then model y 0..2.98, and the full report said Surface extent y 0.113.. — the shins reached y ≈ 0.01, and the union's k=0.1 blend padded the bounds below that, so ground() raised everything 0.11. The full-render report's "Note: the surface floats: its lowest point is at y = 0.114" was the message that made it obvious; check said nothing. Worked around by dropping ground() entirely (model = pad + frog) and keeping the leg bottoms above the pad. Doc gap: the guide says "ground() last" as a style rule, which is wrong advice for a scene with a base already at y = 0 and blended parts whose bounds are loose.
  • check does not warn about what the pictures then show. Same lines as above. check reported no warnings for a model whose surface floated 0.11 above the floor; only the full render report had the note. Expected the "does not rest on y = 0" note (language.md says check prints "every warning" and lists this note among what the tool checks) to appear in check output.
  • Bounds after a blended union(k=) are looser than the parts and this leaks into ground(), into the model's printed bounds (model y -0.1..2.92 while the surface starts at 0) and into check's sizes. language.md says bounds are "exact for primitives and unions" — a k union is not. I had to read the report's "Surface extent" row to learn the truth. Doc gap.
  • Quick-grid warnings ignore the file's set grid and misreport a tube radius as a thickness. Lines: veins = tube(0.035, [...], taper=0.5) | ring(9) and pad = (pad_disc + veins - notch) | paint(leaf). render --quick produced: 'veins' (line 14) is only 0.07 units thin, 0.974 of the 0.072 cell ... (set grid 165) and 'pad' (line 16) has a wall, tube or stroke only 0.035 thick, 0.487 of the 0.072 cell ... (set grid 329). 0.035 is the radius (the tube is 0.07 thick, as the first warning says), the recommended set grid 329 is against the quick pass's own overridden cell, and the file already said set grid 200, which the full render confirmed was enough. Ignored the warnings; they were gone at the full grid. Also: check (at grid 200, cell 0.023) gave no warning for the tapered end of that tube (0.5 × 0.035 = 0.0175 radius, under a cell), so the "tube under a cell" check appears to look at the start radius only.
  • "Watertight: no: N edges" on every full render, unexplained. 20-30 edges "where two surfaces pass through one grid cell" at cell 0.023 with nothing in the model near that thin (thinnest deliberate feature: web 0.07, mouth tube 0.09 diameter). Still 23 edges in the final. Nothing tells me where the edges are or which step owns them, so I could not act on it. Gave up.
  • Material boundaries are dithered in the rasterised views. Lines: eyes = mirror(eye_r, "x") | paint(eyewhite), sockets = ... | paint(skin), mouth = ... | paint(pupil), tummy ... | paint(belly). In front.png and the --focus eyes sheet every junction between two painted shapes is a speckled band of both colours, worst where the socket sphere was nearly coincident with the eyeball (a "beanie" of white with a jagged rim). The beauty render has clean edges. I could not tell from the docs whether this is per-vertex material assignment or a rendering artefact; worked around the worst case by making the two spheres meet at a clear angle (socket_r = sphere(0.36) | move(0.5, 2.22, 0.38)), the rest I accepted.
  • No way to draw a line on a surface. A "mouth line" has no primitive. arc() lies flat on y = 0 only, so a flat arc worked for a straight mouth but a smile needed hand-computed spline points on the ellipsoid (smile = spline([-0.72,1.86,0.66, ...])), solved by doing the ellipsoid algebra myself; the first attempt's ends stuck out of the cheeks as a thin dark line in the beauty render. Doc gap: nothing in the guide about decals, surface lines, or projecting a shape onto a surface.
  • Pupils are geometry, so they bulge. pupil_r = sphere(0.2) gave visible black hemispheres in profile (beauty render, iteration 6); switched to ellipsoid(0.21, 0.21, 0.12). A --focus eyes render showed the glint the same way: a white ball standing proud of the pupil. There is no flat "paint a disc here" facility; a paint limited to an intersection region would have been the natural tool. Guessed and iterated.
  • Legs by rotate + move were a fiddle; tube with two points was the answer, but the docs never suggest it. v1 lines: thigh_r = capsule(0.32, 1.2) | rotate(x=80) | move(1.0, 0.8, 0.1) etc. The right view showed the thigh as a lump with no knee. Replacing with tube(0.32, [hip, knee]) and tube(0.2, [knee, ankle]) gave a folded leg in one go and no angle maths. The guide's "build at the origin, rotate, then move" is the only limb pattern documented.
  • Steps thumbnail for veins draws broken dotted spokes with no caption. The guide says a tiny part gets a "too fine for this thumbnail" note; step 2 VEINS in steps.png just shows dotted lines, which at first read as a broken mesh rather than a thumbnail limit.
  • A detached grey shadow smudge to the right of the pad in every beauty render (around x 700, y 500 in the 768px image), separated from the pad's own shadow by a gap. Probably the eyes' shadow beyond the pad, but nothing in the picture says so. Left it.
  • Negative zeros: the perspective caption reads FLOOR GRID 0.5 AT Y=-0 and the report's surface extent y -0..2.92. Cosmetic, but it makes me doubt the number.
  • Slow enough to matter: full render at set grid 200 is 28-43 s (mesh 7 s, "hierarchy"/atlas 9 s, steps 4-5 s, beauty 3-7 s). The export/atlas step runs even when I only want pictures; there is no flag for "sheet + slices + steps, no OBJ/GLB/viewer".
  • --size 768 scales everything, not just beauty.png; the sheet went to 1548 px and took longer. No separate beauty size.
  • The check output has no warnings section marker when there are none, so I had to grep for it to be sure "no output" meant "no warnings".
  • Minor doc gap: the sheet's "2 WARNINGS IN REPORT.MD" title-bar badge does not appear on check, and --quick's report warnings use the quick cell, so the advice in them is for a grid you are not going to use.

3. What worked well

  • check in 2 s with a per-step bounds table; sizes matched the plan first time and the table is the fastest way to catch a misplaced part.
  • The sheet layout (front/right/top/perspective, whole-unit grids, w × h × d in the title) read correctly on the first look every time; I never needed the individual view PNGs except front.png to look closer.
  • --focus eyes was exactly the tool for the eye problem and I would not change it.
  • steps.png with red frames for orphans; nothing was ever silently missing.
  • slices.png confirmed the belly was fused and the body solid at a glance.
  • mirror("x"), def parts, union(k=), displace, material(color, "noise", color2, scale=), spline + tube, ring and extrude(polygon) all did what the reference said with no surprises; paint before combine kept every material.
  • The beauty render's default lighting and material response made the model look good without any lighting work.

4. Three changes that would have helped most

  1. Make check report what render reports about the surface — the "floats" note, the true surface extent versus loose bounds, and a fix for the blended-union bounds (or a sentence in language.md that k unions have loose bounds and ground() follows the bounds). This was the one real modelling bug and the fast loop hid it.
  2. A way to put a mark on a surface without geometry: a paint restricted to a region (e.g. paint(shape, m, within=other)) or a documented recipe for a flat pupil/mouth/decal, plus an arc/spline note on how to draw a line that lies on a curved surface. Eyes, mouth and nostrils cost most of my iterations and all of the material-boundary dithering.
  3. Warnings that name the grid you will actually render at, and locate the problem: quick-pass thickness warnings should mention the file's set grid (or be suppressed when the full grid already covers it), say "radius" when it is a radius, and the "watertight: no: N edges" line should name the step or a location so it can be acted on or dismissed.