-- florilegium-native.lua -- the species' organs in plain Lua, drawn straight to ink.
-- Copyright (c) 2026 Kevin Raymond. MIT License: see LICENSE or florilegium.sty.
--
-- The TikZ library (tikzlibraryflorilegium.code.tex) draws a species' plant in pgfmath.
-- Here the same organs are computed in ordinary floating point and painted with ink as
-- they are computed: the same steps, the same random draws from pgf's generator in the
-- same order, so the same seed gives the same organ, its numbers within a small fraction
-- of a point of the library's. (Where pgfmath's rounding and a double's would fall on
-- different sides of a threshold, a choice can tip; such cases are rare, and the
-- comparison of the two engines' drawings, tools/tikzcheck.lua draw, lists them.) A few
-- numbers that sit on a threshold are TeX's own, from florilegium-pgfmath.lua.
--
-- Some of the library's habits are kept on purpose, because its look was made with them:
-- they are marked "as the library does" where they would otherwise look like slips.
--
-- Lengths are in the organ's own units (a petal is 1 long), angles in degrees, colors
-- points of ink's ramp: tone(t) as \flo@vtone, in the accent's ramp while pen.accent is set.
-- M.draw draws the margin's organs (in cm), M.pic the TikZ library's pics (in pt).

local ink = require("florilegium-ink")
local pm = require("florilegium-pgfmath")

local M = {}
M.version = "2.0.1"                 -- the release; florilegium.lua checks that every file has the same

local floor, sqrt, abs, min, max = math.floor, math.sqrt, math.abs, math.min, math.max
local CM = pm.sp("28.45276")      -- pt in a cm as TeX holds it, in sp (TeX's arithmetic below)
local RAD = math.pi / 180
local function sin(a) return math.sin(a * RAD) end          -- degrees, as pgfmath takes them
local function cos(a) return math.cos(a * RAD) end
local moveto, lineto, curveto, close, circle = ink.moveto, ink.lineto, ink.curveto, ink.close, ink.circle
local fill, stroke, smooth = ink.fill, ink.stroke, ink.smooth

-- ---------------------------------------------------------------- random numbers
-- pgf's generator (ink.pgfrng): rnd in [0,1] and rand in [-1,1] are whole scaled points,
-- so a draw is the library's draw exactly; only what is computed from it is a double
local G
function M.use(rng) G = rng end
local function range(a, b) return a + (b - a) * G.rnd() end                -- \flo@range
local function sym(a) return a * G.rand() end                              -- \flo@sym
local function int(a, b) return G.random(a, b) end                         -- \pgfmathrandominteger
-- a constant as TeX holds it (a whole number of sp), for comparing a number TeX also holds
-- exactly (an organ's length is one): 0.6 is 0.600006, and a 0.6 cm leaf is not above it
local function K(x) return floor(x * 65536 + 0.5) / 65536 end
-- \flo@chance: rnd < p, with p as TeX holds the constant
local function chance(p) return G.rnd() * 65536 < floor(p * 65536 + 0.5) end
-- \numexpr's division of integers: rounded, halves away from zero
local function nediv(a, b)
  local q = (2 * abs(a) + abs(b)) // (2 * abs(b))
  return ((a < 0) ~= (b < 0)) and -q or q
end

-- ---------------------------------------------------------------- the pen
-- lift: \flo@lift (a ghost's or a fading flower's paleness, 0..1); accent: the accent's
-- ramp (\flo@rampname = flo@aramp). Colors are looked up once per organ.
local pen = { lift = 0, accent = false }
M.pen = pen
local ramps = { [false] = {}, [true] = {} }
function M.fresh() ramps = { [false] = {}, [true] = {} } end              -- ink's colors may have changed
local function ramp(accent, ti)
  local r = ramps[accent]
  local c = r[ti]
  if not c then
    if accent then ink.in_accent(function() c = ink.tone(ti / 100) end) else c = ink.tone(ti / 100) end
    r[ti] = c
  end
  return c
end
-- \flo@vtone
local function tone(t)
  local l = pen.lift
  if l ~= 0 then t = t + (1 - t) * l end
  return ramp(pen.accent, max(0, min(100, floor(t * 100 + 0.5))))
end
-- \flo@vaccent: the accent toward white (k > 0) or black (k < 0)
local function accent(k) return ink.accent(k) end
local WHITE = ink.white

-- ---------------------------------------------------------------- the species
-- the plant being drawn: a table from florilegium-species (its traits by name)
local S
function M.species(sp) S = sp end

-- ---------------------------------------------------------------- paths
-- \flo@vtaper: a closed tapered stroke along one cubic, widths wa at its start, wb at its end
local function nrm(dx, dy)
  local l = sqrt(dx * dx + dy * dy)
  if l < 1.5e-5 then return 0, 0 end                                       -- under 1 sp: none
  return -dy / l, dx / l
end
local function taper(x0, y0, x1, y1, x2, y2, x3, y3, wa, wb)
  local ax, ay = nrm(x1 - x0, y1 - y0)
  local bx, by = nrm(x2 - x0, y2 - y0)
  local cx, cy = nrm(x3 - x1, y3 - y1)
  local dx, dy = nrm(x3 - x2, y3 - y2)
  local ha, hb, hc, hd = wa / 2, (2 * wa + wb) / 6, (wa + 2 * wb) / 6, wb / 2
  moveto(x0 + ax * ha, y0 + ay * ha)
  curveto(x1 + bx * hb, y1 + by * hb, x2 + cx * hc, y2 + cy * hc, x3 + dx * hd, y3 + dy * hd)
  lineto(x3 - dx * hd, y3 - dy * hd)
  curveto(x2 - cx * hc, y2 - cy * hc, x1 - bx * hb, y1 - by * hb, x0 - ax * ha, y0 - ay * ha)
  close()
end
-- \flo@vblob: n points around (x, y), radii rx ry, roughness k (a closed smooth curve's points)
local function blob(x, y, rx, ry, k, n)
  local ph, pi, rot = range(0, 360), range(0, 360), range(0, 360)
  local cr, sr = cos(rot), sin(rot)
  local pts = {}
  for i = 0, n - 1 do
    local a = 360 * i / n
    local s = sym(1)
    local b = 1 + k * (0.6 * sin(2 * a + ph) + 0.4 * sin(3 * a + pi)) + k * 0.5 * s
    local px, py = rx * b * cos(a), ry * b * sin(a)
    pts[i + 1] = { x + px * cr - py * sr, y + px * sr + py * cr }
  end
  return pts
end
-- \flo@vmottle: blotches and specks over (x0,y0)-(x1,y1), inside the clip. With `drift`
-- the x range moves, as the library's does: it passes the range by the names \flo@xa and
-- \flo@xb, which each blotch's curve (\flo@smoothprep) sets to its sixth and first points
-- and each speck to two of its corners, so only the first blotch spans the petal. With
-- drift "xy" the top moves too: the strap leaf passes it as \flo@yb, which the curve sets
-- to its first point's y and a speck to its second corner's
local function mottle(x0, y0, x1, y1, n, size, dark, light, nspk, drift)
  local xa, xb, yb = x0, x1, y1
  for _ = 1, n do
    local mx, my = range(xa, xb), range(y0, yb)
    local mr = size * range(0.5, 1.4)
    local ms = mr * range(0.5, 1)
    local pts = blob(mx, my, mr, ms, 0.35, 7)
    if drift then xa, xb = pts[6][1], pts[1][1] end
    if drift == "xy" then yb = pts[1][2] end
    if chance(0.6) then
      local c = tone(dark + sym(0.06))
      smooth(pts, true) fill(c, range(0.10, 0.22), true)
    else
      local c = tone(light + sym(0.06))
      smooth(pts, true) fill(c, range(0.12, 0.26), false)
    end
  end
  if nspk > 0 then
    for _ = 1, nspk do
      local mx, my = range(xa, xb), range(y0, yb)
      local mr = size * range(0.03, 0.08)
      local ma = range(0, 180)
      local sc, ss = mr * cos(ma), mr * sin(ma)
      moveto(mx + sc, my + ss) lineto(mx - 0.6 * ss, my + 0.6 * sc)
      lineto(mx - sc, my - ss) lineto(mx + 0.6 * ss, my - 0.6 * sc) close()
      if drift then xa, xb = mx + sc, mx - 0.6 * ss end
      if drift == "xy" then yb = my + 0.6 * sc end
    end
    local c = tone(dark - 0.08)
    fill(c, range(0.3, 0.5), true)
  end
end

-- ---------------------------------------------------------------- the petal (flora.petal)
-- the half-width at v of a petal pW wide, pointed or round (\flo@vhalf)
local function half(pointed, pW, v)
  if pointed then
    if v > 0 then return pW * 2 * max(0, sin(180 * v ^ 0.8)) ^ 0.85 * (1 - 0.3 * v) end
    return 0
  end
  if v < 0.62 then
    if v > 0 then return pW * (0.16 + 0.84 * sin(90 * v / 0.62) ^ 1.3) end
    return pW * 0.16
  end
  return pW * sqrt(max(0, 1 - ((v - 0.62) / 0.38) ^ 2))
end
-- three tables of 256 noise gradients (\flo@vfbm), and their sum at x (\flo@vwave)
local function fbm()
  local g = {}
  for k = 1, 3 do
    local t = {}
    for i = 0, 255 do t[i] = G.rand() end
    g[k] = t
  end
  return g
end
local function wave(g, x)
  local v, a, f = 0, 1, 1
  for k = 1, 3 do
    local wx = x * f + 17 * k
    local i = floor(wx)
    local r = wx - i
    local t = g[k]
    local ga, gb = t[i % 256], t[(i + 1) % 256]
    v = v + a * 2 * (ga * r + (gb * (r - 1) - ga * r) * r * r * (3 - 2 * r))
    a, f = a * 0.5, f * 2
  end
  return v
end
-- the outline's points: base, the left side up, the tip, the right side down
local function petal_outline(pointed, pW, bend, notch, g, bn)
  local pts = {}
  local k = pointed and 0.012 or 0.03
  for i = 0, bn do
    local v = i / bn
    local va, vb = max(0, i - 1) / bn, min(bn, i + 1) / bn
    local nx, ny = nrm(bend * (vb * vb - va * va), vb - va)
    local h = half(pointed, pW, v)
    local e1, e2 = k * wave(g, v * 6) * v, k * wave(g, v * 6 + 40) * v
    if notch > 0 and v > 0.9 then
      local d = notch * (v - 0.9) * 10 * (1 - (v - 0.9) * 10)
      e1, e2 = e1 - d, e2 - d
    end
    local ea, eb = max(0, h + e1), max(0, h + e2)
    local cx = bend * v * v
    pts[i + 1] = { cx + nx * ea, v + ny * ea }
    if i > 0 and i < bn then pts[2 * bn + 1 - i] = { cx - nx * eb, v - ny * eb } end
  end
  return pts
end
-- \flo@vpetal: along +y, length 1. o: pointed, W (width; nil draws one), bend (nil draws
-- one), tone, ns (white streaks; nil draws a count), heart, u (cm per unit: the level of
-- detail), notchp and notchk (a round petal's notch: chance and depth)
function M.petal(o)
  local pointed = o.pointed
  local pW = o.W or (pointed and range(0.15, 0.2) or range(0.40, 0.48))
  local bend = o.bend or sym(0.08)
  local tz = o.tone
  local fine = K(o.u) > K(0.45)                 -- u as TeX holds it, in whole sp (\ifdim)
  local g = fbm()
  local notch = 0
  if not pointed and chance(o.notchp) then notch = range(0.03, 0.06) * o.notchk end
  -- a petal under 1.2 mm is tiny: a coarser outline, no heart, mottle or pooled rim (specks
  -- no printer resolves at that size), and at most five streaks
  local tiny = K(o.u) < K(0.12)
  local bn = K(o.u) > K(0.5) and 28 or (tiny and 8 or 18)
  local outline = petal_outline(pointed, pW, bend, notch, g, bn)
  smooth(outline, true) ink.clip()
  ink.poly({ { -1, -0.1 }, { 1, -0.1 }, { 1, 1.2 }, { -1, 1.2 } }, true) fill(tone(tz), 1)
  if o.heart and not tiny then
    for k = 1, 8 do
      smooth(blob(0, 0, pW * (0.25 + 0.1 * k), 0.06 + 0.065 * k, 0.28, 9), true) fill(WHITE, 0.1)
    end
  end
  local msz = pointed and 0.06 or 0.12
  if fine then mottle(-pW * 1.2, 0.12, pW * 1.2, 1.05, 15, msz, tz - 0.2, tz + 0.2, 12, true)
  elseif not tiny then mottle(-pW * 1.2, 0.12, pW * 1.2, 1.05, 6, msz, tz - 0.2, tz + 0.2, 0, true) end
  -- darker brush strokes running out from the base
  if fine then
    for _ = 1, int(5, 8) do
      local f, va, vb = sym(0.95), range(0.15, 0.4), range(0.7, 1.0)
      local xa = f * half(pointed, pW, va) * 0.8 + bend * va * va
      local xb = f * half(pointed, pW, vb) * 0.9 + bend * vb * vb
      local twa = range(0.03, 0.07)
      local c = tone(tz - 0.25)
      taper(xa, va, xa + (xb - xa) * 0.3, va + (vb - va) * 0.35, xb * 0.95, va + (vb - va) * 0.7, xb, vb, twa, 0.005)
      fill(c, range(0.12, 0.25), true)
    end
  end
  -- pooling: the outline restroked wide and soft
  if not tiny then smooth(outline, true) stroke(tone(0.08), 1.6 * o.u, 0.3, true) end
  -- white streaks lifted out, fanning from the base
  local ns = o.ns or (pointed and int(3, 5) or int(9, 13))
  if tiny and ns > 5 then ns = 5 end
  for k = 1, ns do
    local f = (k - 0.5) / ns * 2 - 1 + sym(0.05)
    local len = (pointed and range(0.4, 0.85) or range(0.35, 0.8)) * (1 - 0.35 * f * f)
    if pointed and k == nediv(ns, 2) then f, len = 0, range(0.8, 0.92) end
    local xb = f * half(pointed, pW, len) * 0.85 + bend * len * len
    local mx = f * half(pointed, pW, len * 0.5) * 0.6 + sym(0.02) + bend * len * len * 0.3
    local twa = (pointed and 0.6 or 1) * range(0.022, 0.045)
    local op = range(0.55, 0.9)
    taper(f * 0.03, 0, mx * 0.5, len * 0.3, mx, len * 0.6, xb, len, twa, 0.002)
    fill(WHITE, op)
  end
  -- a few fine dark veins, following the petal
  if fine then
    for _ = 1, int(4, 7) do
      local f, va, vb = sym(0.85), range(0.2, 0.35), range(0.75, 0.95)
      local xa = f * half(pointed, pW, va) * 0.7 + bend * va * va
      local xb = f * half(pointed, pW, vb) * 0.9 + bend * vb * vb
      local b = ink.bezier3(xa, va, xa + (xb - xa) * 0.2, va + (vb - va) * 0.4, xb * 0.97, va + (vb - va) * 0.75, xb, vb)
      local pts = {}
      for i = 0, 5 do pts[i + 1] = { b(i / 5) } end
      local c = tone(0.1)
      smooth(pts, false) stroke(c, 0.15, range(0.18, 0.32), true)
    end
  end
  ink.unclip()
end

-- ---------------------------------------------------------------- the heart and the flower
-- \flo@vheart: the center of a flower of n petals
function M.heart(n)
  for k = 1, 2 * n do
    local a = (k - 0.5) * 360 / (2 * n) + sym(8)
    local len = range(0.24, 0.36)
    ink.push() ink.rotate(a)
    taper(0, 0, 0.01, 0.1, -0.01, 0.2, 0, len, 0.06, 0.004) fill(WHITE, 0.85)
    ink.pop()
  end
  circle(0, 0, 0.13) fill(WHITE, 0.9)
  local c, d = tone(0.1), tone(0.05)
  for _ = 1, 14 do
    local a, r = range(0, 360), range(0.05, 0.12)
    local dx, dy = r * cos(a), r * sin(a)
    moveto(0, 0) lineto(dx, dy) stroke(c, 0.2, 0.7, false)
    circle(dx, dy, 0.008) fill(d, 0.9)
  end
  circle(0, 0, 0.042) fill(accent(-0.15), 1)
  circle(0, 0, 0.022) fill(accent(0.3), 1)
end
-- \flo@vflower: n broad petals seen from the front, petal length 1; u its cm per unit.
-- o: pointed, tone (nil draws one), W, ns (nil: the petal draws them), notchp, notchk
function M.flower(u, n, o)
  local spin = range(0, 72)
  local tq = o.tone
  if not tq then if chance(0.25) then tq = range(0.2, 0.28) else tq = range(0.3, 0.46) end end
  local p = { pointed = o.pointed, W = o.W, ns = o.ns, heart = true, u = u, notchp = o.notchp, notchk = o.notchk }
  for i = 0, n - 1 do
    local a = spin + i * 360 / n + sym(6)
    local js = range(0.9, 1.06)
    p.tone = tq + sym(0.04)
    ink.push() ink.rotate(a) ink.scale(js) ink.translate(0, 0.04)
    M.petal(p)
    ink.pop()
  end
  ink.push() ink.rotate(spin) M.heart(n) ink.pop()
end
-- \flo@sdaisy: a daisy head of `rays` rays, diameter 1; d its diameter in pt.
-- o: tone (nil draws one), notchp, notchk
function M.daisy(d, rays, o)
  local spin = range(0, 360)
  local tq = o.tone or range(0.3, 0.46)
  local p = { pointed = false, W = min(0.17, 1.6 / rays), ns = 2, heart = false, u = d * 0.36 / 28.45276,
    notchp = o.notchp, notchk = o.notchk }
  for k = 1, rays do
    local a = spin + k * 360 / rays + sym(4) - 90
    local js = range(0.9, 1.05)
    p.tone = tq + sym(0.04)
    ink.push() ink.rotate(a) ink.translate(0, 0.1) ink.scale(0.4) ink.scale(js)
    M.petal(p)
    ink.pop()
  end
  local was = pen.accent
  pen.accent = true
  circle(0, 0, 0.105) fill(tone(0.3), 1)
  -- the disc florets on Vogel's spiral (H. Vogel, Mathematical Biosciences 44, 1979):
  -- floret k at radius c*sqrt(k/n), turned k times 137.508 degrees
  local nd = floor(min(90, max(24, d * 1.2)))
  local c = tone(0.68)
  for k = 1, nd do
    local r, a = 0.095 * sqrt(k / nd), 137.508 * k
    circle(r * cos(a), r * sin(a), 0.004 + 0.006 * sqrt(k / nd))
  end
  fill(c, 0.9)
  pen.accent = was
end
-- \flo@vfloret: a floret of n petals, radius 1; r its radius in cm. o: star (nil draws it), tone
function M.floret(r, n, o)
  local star = o.star
  if star == nil then star = chance(0.5) end
  if r < 0.07 then
    -- a star or a round flower as one polygon
    local ph = range(0, 360)
    local pts = {}
    for k = 0, 2 * n - 1 do
      local a = ph + k * 180 / n
      local q = k % 2 == 1 and (star and 0.32 or 0.62) or 1
      pts[k + 1] = { q * cos(a), q * sin(a) }
    end
    local c = tone(o.tone or range(0.22, 0.42))
    ink.poly(pts, true) fill(c, 1)
    ink.poly({ { 0.2, 0 }, { 0, 0.2 }, { -0.2, 0 }, { 0, -0.2 } }, true) fill(tone(0.05), 1)
  else
    local ph = range(0, 360)
    local shape, tk
    if star then shape, tk = { { 0, 0 }, { 0.45, 0.156 }, { 1, 0 }, { 0.45, -0.156 } }, 0.5
    else shape, tk = { { 0, 0 }, { 0.3, 0.42 }, { 0.85, 0.336 }, { 1, 0 }, { 0.85, -0.336 }, { 0.3, -0.42 } }, 1 end
    for k = 0, n - 1 do
      local a = ph + k * 360 / n + sym(8.6)
      local len = range(0.85, 1.1)
      local c, s = cos(a), sin(a)
      local pts = {}
      for i, p in ipairs(shape) do pts[i] = { len * (p[1] * c - p[2] * s), len * (p[1] * s + p[2] * c) } end
      smooth(pts, true, tk)
    end
    fill(tone(o.tone or range(0.22, 0.42)), 1)
    if r > 0.05 then circle(0, 0, 0.35) fill(WHITE, 0.75) end
    circle(0, 0, 0.16) fill(tone(0.05), 1)
  end
end
-- \flo@vblossom: flora.blossom, n round petals, radius 1; r its radius in cm. o: tone
function M.blossom(r, n, o)
  local spin = range(0, 72)
  local tq = o.tone or range(0.30, 0.45)
  for k = 0, n - 1 do
    local a = spin + k * 360 / n + sym(8)
    local js = range(0.9, 1.08)
    local pW = range(0.36, 0.44)
    ink.push() ink.rotate(a) ink.scale(js)
    -- the outline: up the left side, down the right
    local pts = {}
    for i = 0, 18 do pts[i + 1] = { -half(false, pW, i / 18), i / 18 } end
    for i = 17, 1, -1 do pts[#pts + 1] = { half(false, pW, i / 18), i / 18 } end
    local c = tone(tq + sym(0.05))
    smooth(pts, true) fill(c, 1)
    smooth(pts, true) ink.clip()
    local mx, my = sym(0.2), range(0.5, 0.9)
    local b = blob(mx, my, 0.35, 0.3, 0.3, 8)
    smooth(b, true) fill(tone(tq - 0.2), 0.3, true)
    taper(0, 0, 0.02, 0.2, -0.02, 0.4, sym(0.05), 0.55, 0.12, 0.01) fill(WHITE, 0.7)
    smooth(pts, true) stroke(tone(0.08), 1.2 * r, 0.25, true)
    ink.unclip()
    ink.pop()
  end
  circle(0, 0, 0.22) fill(WHITE, 0.85)
  circle(0, 0, 0.09) fill(tone(0.08), 1)
end
-- \flo@vboss: the rose family's ring of stamens, face on; exserted: longer filaments
function M.boss(exserted)
  local bl = exserted and 0.04 or 0
  local n = int(36, 52)
  local ph = range(0, 360)
  local fx, fy, ax, ay, ar = {}, {}, {}, {}, {}
  for k = 1, n do
    local a = ph + 360 / n * k + sym(4)
    local r = range(0.09, 0.17) + bl
    fx[k], fy[k] = 0.035 * cos(a), 0.035 * sin(a)
    ax[k], ay[k] = r * cos(a), r * sin(a)
    ar[k] = range(0.007, 0.011)
  end
  for k = 1, n do moveto(fx[k], fy[k]) lineto(ax[k], ay[k]) end
  stroke(tone(0.14), 0.2, 0.8, false)
  local was = pen.accent
  pen.accent = true
  for k = 1, n do circle(ax[k], ay[k], ar[k]) end
  fill(tone(0.42), 1)
  circle(0, 0, 0.045) fill(tone(0.66), 1)
  pen.accent = was
  circle(0.014, 0.009, 0.009) circle(-0.012, 0.013, 0.009) circle(-0.002, -0.015, 0.009)
  fill(tone(0.2), 0.9)
end

-- ================================================================ M8f2: the rest of the flower
-- ---------------------------------------------------------------- helpers
local function xscale(s) ink.scale(s, 1) end
local function yscale(s) ink.scale(1, s) end
-- \flo@upright: +y up on the page, the scale and the handedness kept
local function upright()
  local t = ink.get_transform()
  local p, q = sqrt(t[1] * t[1] + t[2] * t[2]), sqrt(t[3] * t[3] + t[4] * t[4])
  local s = sqrt(p) * sqrt(q)
  ink.set_transform({ (t[1] / p) * (t[4] / q) - (t[2] / p) * (t[3] / q) < 0 and -s or s, 0, 0, s, t[5], t[6] })
end
-- \flo@normals: the left normals of a polyline by central differences (one under 1 sp is
-- left unnormalized, as the library leaves it)
local function normals(pts)
  local n, out = #pts, {}
  for i = 1, n do
    local a, b = pts[i == 1 and 1 or i - 1], pts[i == n and n or i + 1]
    local dx, dy = b[1] - a[1], b[2] - a[2]
    local l = sqrt(dx * dx + dy * dy)
    if l < 1.5e-5 then l = 1 end
    out[i] = { -dy / l, dx / l }
  end
  return out
end
-- a blade around a spine (\flo@sides, \flo@bladeassemble): w(i, v) the half-width at point i;
-- the outline goes up the left side and down the right, the tip and the base once each
local function blade(spine, w)
  local nr, n = normals(spine), #spine - 1
  local left, right = {}, {}
  for i = 1, n + 1 do
    local h = w(i, (i - 1) / n)
    local p, q = spine[i], nr[i]
    left[i] = { p[1] + q[1] * h, p[2] + q[2] * h }
    right[i] = { p[1] - q[1] * h, p[2] - q[2] * h }
  end
  local out = left
  for i = n, 2, -1 do out[#out + 1] = right[i] end
  return out, nr
end
-- the closed outline of a band along the points, up one side and back down the other:
-- halfw(s, acc) the half-width at a point s along them, acc their whole length (taken as 1
-- when every point is the same one, so s / acc is 0, not 0 / 0)
local function ribbonpts(pts, halfw)
  local nr, n = normals(pts), #pts
  local s, acc = { 0 }, 0
  for i = 2, n do
    local dx, dy = pts[i][1] - pts[i - 1][1], pts[i][2] - pts[i - 1][2]
    acc = acc + sqrt(dx * dx + dy * dy)
    s[i] = acc
  end
  if acc == 0 then acc = 1 end
  local out = {}
  for i = 1, n do
    local h, p, q = halfw(s[i], acc), pts[i], nr[i]
    out[i] = { p[1] + q[1] * h, p[2] + q[2] * h }
    out[2 * n + 1 - i] = { p[1] - q[1] * h, p[2] - q[2] * h }
  end
  return out
end
-- \flo@feribbon, and \flo@vstem's filled band: a ribbon along the points, widths w0 to w1,
-- in color c
local function ribbon(pts, w0, w1, c)
  smooth(ribbonpts(pts, function(s, acc) return (w0 + (w1 - w0) * s / acc) / 2 end), true) fill(c, 1)
end
-- \flo@vstreak: a ribbon along the points, widths w0 to w1, thin at its start
local function streak(pts, w0, w1, color, op)
  smooth(ribbonpts(pts, function(s, acc)
    local u = s / acc
    return (w0 + (w1 - w0) * u) * (u < 0.12 and 0.4 + 5 * u or 1) / 2
  end), true) fill(color, op)
end
-- the six points of a cubic (\flo@vbezier), for an open smooth curve (\flo@openpath)
local function bezier6(x0, y0, x1, y1, x2, y2, x3, y3)
  local b, pts = ink.bezier3(x0, y0, x1, y1, x2, y2, x3, y3), {}
  for i = 0, 5 do pts[i + 1] = { b(i / 5) } end
  return pts
end

-- ---------------------------------------------------------------- the leaflet (flora.leaflet)
-- \flo@vleaflet: along +y, length 1. o: W (nil draws one), tone (nil draws one), u (cm per unit)
function M.leaflet(o)
  local lW = o.W or range(0.16, 0.24)
  local bend = sym(0.2)
  local lt = o.tone or range(0.2, 0.34)
  local spine = {}
  for i = 0, 18 do local v = i / 18 spine[i + 1] = { bend * v * v, v } end
  local outline, nr = blade(spine, function(_, v)
    if v > 0 and v < 1 then return lW * 2.2 * v ^ 0.5 * (1 - v) ^ 0.9 end
    return 0
  end)
  smooth(outline, true) fill(tone(lt), 1)
  if o.u > K(0.25) then
    smooth(outline, true) ink.clip()
    -- the darker half: the midrib out, then back along a line one unit to one side
    local sd = chance(0.5) and 1 or -1
    moveto(spine[1][1], spine[1][2])
    for i = 2, 19 do lineto(spine[i][1], spine[i][2]) end
    for i = 19, 1, -1 do lineto(spine[i][1] + sd * nr[i][1], spine[i][2] + sd * nr[i][2]) end
    close()
    fill(tone(0.08), 0.3, true)
    local mid = {}
    for i = 0, 10 do local v = i / 10 mid[i + 1] = { bend * v * v, v } end
    streak(mid, 0.03, 0.003, WHITE, 0.65)
    smooth(outline, true) stroke(tone(0.06), 1.3 * o.u, 0.3, true)
    ink.unclip()
  end
end

-- ---------------------------------------------------------------- the bud and the fruit
-- \flo@vbud: a teardrop and three sepals, base at the origin, +y, length 1; u its cm per
-- unit, tq its tone (nil draws one)
function M.bud(u, tq)
  local bend = sym(0.1)
  local spine = {}
  for i = 0, 18 do local v = i / 18 spine[i + 1] = { bend * v * v, v } end
  local outline = blade(spine, function(_, v)
    if v > 0 then return max(0, 0.3 * max(0, sin(180 * v ^ 0.6)) ^ 0.8) end
    return 0
  end)
  tq = tq or range(0.3, 0.42)
  smooth(outline, true) fill(tone(tq), 1)
  smooth(outline, true) ink.clip()
  mottle(-0.3, 0, 0.3, 1, 5, 0.1, tq - 0.2, tq + 0.2, 0, false)
  taper(-0.08, 0.05, -0.1, 0.35, -0.05, 0.65, bend * 0.8, 0.95, 0.06, 0.005) fill(WHITE, 0.7)
  smooth(outline, true) stroke(tone(0.06), 1.4 * u, 0.3, true)
  ink.unclip()
  -- the sepals: three small blades, one path
  for i = 0, 2 do
    local k = i - 1
    local a = k * 24 + sym(6)
    local sy = k == 0 and 0.22 or 0.32
    local sp = {}
    for j = 0, 8 do local v = j / 8 sp[j + 1] = { -k * 0.4 * v * v, v } end
    local pts = blade(sp, function(_, v) if v > 0 then return max(0, sin(180 * v ^ 0.6)) end return 0 end)
    local ca, sa = cos(a), sin(a)
    for j, p in ipairs(pts) do
      local x, y = 0.06 * p[1], sy * p[2]
      pts[j] = { k * 0.05 + x * ca - y * sa, -0.02 + x * sa + y * ca }
    end
    smooth(pts, true)
  end
  fill(tone(0.14), 0.9)
end
-- \flo@vclock: a seed clock (flora.seedhead), radius 1 around the origin
local function clock()
  local n, gone = int(18, 26), range(0, 360)
  for k = 1, n do
    local a = 360 * (k + sym(0.3)) / n
    local t = G.rnd()
    local kept = not (abs((a - gone + 540) % 360 - 180) < 30 and t < 0.7)
    local L = range(0.72, 0.86)
    if kept then
      local xb, yb = L * cos(a), L * sin(a)
      moveto(0.1 * cos(a), 0.1 * sin(a)) lineto(xb, yb)
      for j = -2, 2 do
        local b = a + 18 * j
        moveto(xb, yb) lineto(xb + 0.16 * cos(b), yb + 0.16 * sin(b))
      end
    end
  end
  stroke(tone(0.16), 0.22, 0.85, false)
  circle(0, 0, 0.13) fill(tone(0.12), 1)
end
-- \flo@fruit: the species' fruit along +y, length 1; fu its cm per unit
function M.fruit(fu)
  local fr = S.fruit
  if fr == "hip" then
    local was = pen.accent
    pen.accent = true
    ink.push() ink.translate(0, 1) ink.rotate(180) M.bud(fu, 0.36) ink.pop()
    pen.accent = was
  elseif fr == "capsule" then
    M.bud(fu, 0.38)
    taper(-0.06, 0.15, -0.1, 0.4, -0.08, 0.7, -0.02, 0.95, 0.025, 0.004) fill(WHITE, 0.55)
    taper(0.06, 0.15, 0.1, 0.4, 0.08, 0.7, 0.02, 0.95, 0.025, 0.004) fill(WHITE, 0.4)
  elseif fr == "pappus" then
    ink.push() ink.translate(0, 0.5) clock() ink.pop()
  elseif fr == "silique" then
    M.leaflet({ W = 0.07, tone = range(0.3, 0.4), u = fu })
  elseif fr == "silicle" then
    ink.push() ink.scale(0.6) M.leaflet({ W = 0.32, tone = range(0.42, 0.52), u = fu }) ink.pop()
  elseif fr == "schizocarp" then
    ink.push() ink.scale(0.7) ink.rotate(12) M.leaflet({ W = 0.18, u = fu }) ink.pop()
    ink.push() ink.scale(0.7) ink.rotate(-12) M.leaflet({ W = 0.18, u = fu }) ink.pop()
  else
    ink.push() ink.scale(0.55) M.leaflet({ W = 0.22, tone = range(0.12, 0.2), u = fu }) ink.pop()
  end
end

-- ---------------------------------------------------------------- the lily and its corona
-- \flo@vlily: six pointed tepals seen from above at a tilt, petal length 1; u its cm per
-- unit, tq its tone (nil draws one). Returns the tilt (the corona is drawn in it)
local function lily(u, tq)
  local tilt, spin = range(0.45, 0.62), range(0, 60)
  tq = tq or range(0.30, 0.38)
  local O = {}
  for k = 1, 6 do O[k] = spin + (k - 1) * 60 + sym(8) end
  -- the far tepals first (a bubble sort on sin, as the library sorts them)
  for k = 1, 5 do
    for j = 1, 6 - k do
      if sin(O[j + 1]) > sin(O[j]) then O[j], O[j + 1] = O[j + 1], O[j] end
    end
  end
  for k = 1, 6 do
    local a = O[k]
    local back = sin(a)
    local js, lw = range(0.88, 1.05), range(0.11, 0.14)
    local tp = tq + 0.1 * back * -0.8 + sym(0.03)
    local sg = chance(0.5) and 1 or -1
    local pbend = sg * range(0.04, 0.14)
    local pns = int(3, 4)
    ink.push() yscale(tilt) ink.rotate(a - 90) ink.scale(js)
    M.petal({ pointed = true, W = lw, bend = pbend, ns = pns, heart = false, tone = tp, u = u })
    ink.pop()
  end
  for k = 1, 4 do
    local t = 0.05 + 0.05 * k
    smooth(blob(0, 0, t, t * tilt, 0.2, 9), true) fill(WHITE, 0.22)
  end
  for k = 1, 6 do
    local a = (k - 3.5) * 16 + sym(6)
    local len = range(0.3, 0.48)
    local ex, ey = len * sin(a), len * cos(a)
    smooth(bezier6(0, 0, ex * 0.2, ey * 0.5, ex * 0.8, ey * 0.85, ex, ey), false) stroke(tone(0.12), 0.25, 0.85, false)
    local t = -a + sym(30)
    ink.push() ink.translate(ex, ey) ink.rotate(t)
    smooth(blob(0, 0, 0.022, 0.04, 0.1, 6), true) fill(tone(0.05), 1)
    ink.pop()
    if chance(0.5) then
      local xa = ex + sym(0.03)
      local ya = ey + range(0.02, 0.05)
      circle(xa, ya, 0.008) fill(accent(0), 1)
    end
  end
  circle(0, 0, 0.035) fill(accent(-0.2), 1)
  return tilt
end
-- \flo@vturkscap: a lily whose tepals sweep back (reflex under -40), held sideways or nodding,
-- seen from the side and a little from below: from the center at the origin six tepals
-- leave a little forward (+y, the way the flower faces), then curl back toward the stalk,
-- the more the further the species' reflex; the far ones first, darker, then the stamens
-- hanging out of the flower, then the near ones; the species' dots on them. Tepal length
-- about 1; u its cm per unit, tq its tone (nil draws one)
local function turkscap(u, tq)
  local q = tq or range(0.30, 0.38)
  local spin, beta = range(0, 60), range(18, 32)
  local cb, sb = cos(beta), sin(beta)
  local curl = 60 - 1.6 * S.reflex                -- 124 to 188 degrees
  local A, J, W, C, Q, O = {}, {}, {}, {}, {}, {}
  for k = 1, 6 do
    A[k] = spin + (k - 1) * 60 + sym(8)
    J[k] = range(0.88, 1.05)
    W[k] = range(0.13, 0.17)
    C[k] = curl * range(0.85, 1.1)
    Q[k] = q + 0.08 * sin(A[k]) + sym(0.03)
    O[k] = k
  end
  for k = 1, 5 do                               -- the far tepals first
    for j = 1, 6 - k do
      if sin(A[O[j + 1]]) < sin(A[O[j]]) then O[j], O[j + 1] = O[j + 1], O[j] end
    end
  end
  local function tepal(t)
    local ca, sa = cos(A[t]), sin(A[t])
    local x, y, z = 0, 0, 0
    local P, H = { { 0, 0, 0 } }, { 0 }
    for i = 1, 10 do
      local f = -15 + C[t] * ((i - 0.5) / 10) ^ 1.3
      local r, b = cos(f) * J[t] / 10, -sin(f) * J[t] / 10
      x, y, z = x + r * ca, y + b, z + r * sa
      P[i + 1] = { x, y, z }
      H[i + 1] = i < 10 and W[t] * J[t] * sin(180 * (i / 10) ^ 0.55) ^ 0.9 or 0
    end
    local function at(i, h)                     -- spine point i moved h across, on the page
      local p = P[i]
      local px, py, pz = p[1] - h * sa, p[2], p[3] + h * ca
      return { px, py * cb - pz * sb }
    end
    local out, mid = {}, {}
    for i = 1, 11 do out[i] = at(i, H[i]) end
    for i = 10, 2, -1 do out[#out + 1] = at(i, -H[i]) end
    for i = 2, 10 do mid[i - 1] = at(i, 0) end
    smooth(out, true) fill(tone(Q[t]), 1)
    if u > 0.15 then
      smooth(out, true) ink.clip()
      streak(mid, 0.03, 0.004, WHITE, 0.5)
      if S.guides ~= "none" then
        for _ = 1, 5 do
          local i, o = int(2, 5), sym(0.6)
          local c = at(i, o * H[i])
          circle(c[1], c[2], range(0.012, 0.022))
        end
        fill(tone(0.06), 0.8, true)
      end
      smooth(out, true) stroke(tone(0.06), 1.3 * u, 0.3, true)
      ink.unclip()
    end
    smooth(out, true) stroke(tone(0.08), 0.18, 0.35, false)
  end
  local k = 1
  while k <= 6 and sin(A[O[k]]) < 0 do tepal(O[k]) k = k + 1 end
  for j = 1, 6 do                               -- the stamens and their anthers
    local a = (j - 3.5) * 12 + sym(5)
    local len = range(0.5, 0.75)
    local ex, ey = len * sin(a), len * cos(a)
    smooth(bezier6(0, 0, ex * 0.2, ey * 0.5, ex * 0.8, ey * 0.85, ex, ey), false) stroke(tone(0.12), 0.25, 0.85, false)
    local t = -a + sym(30)
    ink.push() ink.translate(ex, ey) ink.rotate(t)
    smooth(blob(0, 0, 0.022, 0.045, 0.1, 6), true) fill(accent(-0.1), 1)
    ink.pop()
  end
  circle(0, 0, 0.05) fill(tone(0.1), 1)
  while k <= 6 do tepal(O[k]) k = k + 1 end
end
-- \flo@vcorona: a lily's cup or trumpet, in the lily's frame (its tilt) after its star;
-- tq the flower's tone (nil: the corona draws one)
local function corona(u, tilt, tq)
  local h, b, R
  if S.corona == "trumpet" then h, b, R = range(0.4, 0.6), 0.1, range(0.24, 0.32)
  else h, b, R = range(0.12, 0.2), 0.14, range(0.2, 0.28) end
  local q = tq and tq - 0.08 or range(0.24, 0.32)
  local cn, cp = int(9, 14), range(0, 360)
  local function rim(a) local r = 1 + 0.09 * sin(cn * a + cp) return R * r * cos(a), h + R * r * tilt * sin(a) end
  local pts = {}
  for k = 0, 4 do local a = 180 + 45 * k pts[k + 1] = { b * cos(a), b * tilt * sin(a) } end
  for k = 1, 3 do local v = k / 4 pts[k + 5] = { b + (R - b) * v ^ 1.6, h * v } end
  for k = 0, 6 do pts[k + 9] = { rim(-30 * k) } end
  for k = 1, 3 do local v = 1 - k / 4 pts[k + 15] = { -b - (R - b) * v ^ 1.6, h * v } end
  smooth(pts, true, 0.9) fill(tone(q), 1)
  if u > 0.12 then
    smooth(pts, true, 0.9) ink.clip()
    for k = -2, 2 do
      local f = k / 2.6
      local xa, xd = f * b, f * R + sym(0.02)
      taper(xa, -0.02, 0.7 * xa + 0.3 * xd, 0.4 * h, 0.3 * xa + 0.7 * xd, 0.75 * h, xd, h, 0.025, 0.008)
      if k % 2 ~= 0 then fill(tone(0.08), 0.25, true) else fill(WHITE, 0.5) end
    end
    ink.unclip()
  end
  local ring = {}
  for k = 1, 16 do ring[k] = { rim(22.5 * k) } end
  smooth(ring, true) fill(tone(q + 0.12), 1)
  local t = 0.55 * R
  smooth(blob(0, h - 0.15 * R * tilt, t, t * tilt, 0.15, 8), true) fill(tone(q - 0.12), 0.6, true)
  local c = tone(0.08)
  smooth(ring, true) stroke(c, 0.2, 0.6, false)
  smooth(pts, true, 0.9) stroke(c, 0.2, 0.35, false)
end

-- ---------------------------------------------------------------- irises, orchids, columbines
-- \flo@ivpart: a part at angle a, length l: a haft for the fraction c of it, then flora's
-- petal (o: pointed, W, bend, tone, heart, ns, notchp; u the flower's cm per unit)
local function ivpart(a, l, c, u, o)
  ink.push() ink.rotate(a - 90) ink.scale(l)
  if c > 0 then
    local col = tone(o.tone)
    local t = c * 0.5
    taper(0, 0, 0, t, 0, t, 0, c + 0.01, 0.3 * o.W, 0.34 * o.W * (1 - c)) fill(col, 1)
    taper(0, 0.02, 0, t, 0, t, 0, c * 0.95, 0.02, 0.004) fill(WHITE, 0.6)
    ink.translate(0, c) ink.scale(1 - c)
  end
  o.notchk, o.u = 1.6, u * l * (1 - c)
  M.petal(o)
  ink.pop()
end
local function part(pointed, W, bend, tq, heart, ns, notchp)
  return { pointed = pointed, W = W, bend = bend, tone = tq, heart = heart, ns = ns, notchp = notchp }
end
-- \flo@viris: an iris three-quarters from the front, diameter 1; d its diameter in pt
local function iris(d, tq)
  local u = d / 28.45276
  local q = tq or range(0.28, 0.38)
  local cf = 0.7 * min(0.45, S.claw)
  local c = 0.55 * cf
  -- the standards: the back one, then two either side
  local a = 90 + sym(6)
  local len, b = range(0.46, 0.52), sym(0.06)
  ivpart(a, len, c, u, part(false, 0.27, b, q + 0.04, false, 5, 0.5))
  for k = 0, 1 do
    local sg = k == 0 and -1 or 1
    a = -90 + sg * range(20, 45)
    len = range(0.52, 0.6)
    b = sg * range(0.15, 0.3)
    local tp = q + sym(0.03)
    ivpart(a, len, cf, u, part(false, 0.34, b, tp, false, nil, 0.3))
    len = range(0.26, 0.32)
    ivpart(a, len, 0, u, part(false, 0.24, 0, q + 0.18, false, 3, 0.9))
  end
  c = 0.55 * cf
  for k = 0, 1 do
    local sg = k == 0 and -1 or 1
    a = 90 - sg * range(8, 28)
    len = range(0.48, 0.56)
    b = -sg * range(0.15, 0.3)
    local tp = q + 0.02 + sym(0.03)
    ivpart(a, len, c, u, part(false, 0.27, b, tp, false, 5, 0.5))
  end
  -- the front fall, its signal and beard, and its crest
  a = -90 + sym(6)
  len, b = range(0.56, 0.64), sym(0.08)
  ivpart(a, len, cf, u, part(false, 0.36, b, q, false, nil, 0.3))
  if u * len > 0.15 then
    ink.push() ink.rotate(a - 90) ink.scale(len)
    local g = S.guides
    if g == "lines" then
      local col = tone(0.06)
      for k = -2, 2 do
        local f = k * 0.07 + sym(0.03)
        local ya = range(0.25, 0.38)
        taper(f, 0.4, 0.6 * f, 0.35 * ya + 0.4, 1.1 * f, 0.7 * ya + 0.4, 1.4 * f, ya + 0.4, 0.035, 0.004)
        fill(col, 0.45, true)
      end
    end
    if g == "blotch" then
      smooth(blob(0, 0.62, 0.16, 0.12, 0.25, 9), true) fill(WHITE, 0.7)
      smooth(blob(0, 0.62, 0.07, 0.05, 0.3, 7), true) fill(tone(0.06), 0.5, true)
    end
    if g == "dots" then
      for _ = 1, 9 do
        local x, y = sym(0.16), range(0.45, 0.8)
        circle(x, y, range(0.012, 0.024))
      end
      fill(tone(0.06), 0.7, true)
    end
    for k = 1, 14 do
      local x = sym(0.035)
      circle(x, 0.38 + 0.024 * k, range(0.012, 0.022))
    end
    fill(accent(0.1), 0.9)
    ink.pop()
  end
  len = range(0.26, 0.32)
  ivpart(a, len, 0, u, part(false, 0.24, 0, q + 0.18, false, 3, 0.9))
end
-- \flo@vorchid: an orchid face on, diameter 1, upright; u its cm per unit
local function orchid(u, tq)
  local q = tq or range(0.28, 0.38)
  for k = 0, 1 do                               -- the lateral sepals
    local sg = k == 0 and -1 or 1
    local a = -90 + sg * range(45, 60)
    local len, b = range(0.42, 0.5), sym(0.08)
    ivpart(a, len, 0, u, part(true, 0.12, b, q + 0.02 + sym(0.03), false, 3, 0))
  end
  local a = 90 + sym(6)                         -- the dorsal sepal
  local len, b = range(0.4, 0.48), sym(0.08)
  ivpart(a, len, 0, u, part(true, 0.13, b, q + 0.02, false, 3, 0))
  for k = 0, 1 do                               -- the petals
    local sg = k == 0 and -1 or 1
    a = 90 - sg * range(45, 65)
    len = range(0.36, 0.44)
    b = sg * range(0, 0.12)
    ivpart(a, len, 0, u, part(false, 0.2, b, q + sym(0.03), false, 5, 0.3))
  end
  local tp = q - 0.06
  for k = 0, 1 do                               -- the lip's side lobes
    local sg = k == 0 and -1 or 1
    a = -90 + sg * range(40, 55)
    len = range(0.18, 0.24)
    ivpart(a, len, 0, u, part(false, 0.35, 0, tp, false, 3, 0))
  end
  a = -90 + sym(6)                              -- the lip
  len, b = range(0.44, 0.54), sym(0.06)
  ivpart(a, len, 0, u, part(false, 0.38, b, tp, true, 5, 0.85))
  if u * len > 0.15 then
    ink.push() ink.rotate(a - 90) ink.scale(len)
    local g = S.guides
    if g == "lines" then
      local col = tone(0.06)
      for k = -2, 2 do
        local f = k * 0.09 + sym(0.03)
        local ya = range(0.35, 0.5)
        taper(f, 0.12, 0.7 * f, 0.35 * ya + 0.12, 1.2 * f, 0.7 * ya + 0.12, 1.5 * f, ya + 0.12, 0.035, 0.004)
        fill(col, 0.45, true)
      end
    end
    if g == "blotch" then
      smooth(blob(0, 0.4, 0.2, 0.15, 0.3, 9), true) fill(WHITE, 0.65)
      for _ = 1, 8 do
        local x, y = sym(0.14), range(0.3, 0.5)
        circle(x, y, range(0.015, 0.03))
      end
      fill(tone(0.06), 0.7, true)
    end
    if g == "dots" then
      for _ = 1, 11 do
        local x, y = sym(0.24), range(0.2, 0.75)
        circle(x, y, range(0.014, 0.028))
      end
      fill(tone(0.06), 0.7, true)
    end
    ink.pop()
  end
  local col = blob(0, 0.05, 0.05, 0.085, 0.15, 8)     -- the column
  smooth(col, true) fill(WHITE, 0.85)
  smooth(col, true) stroke(tone(0.1), 0.2, 0.5, false)
  circle(0, 0.11, 0.022) fill(tone(0.06), 1)
end

-- ---------------------------------------------------------------- tubes, cups and lips
-- \flo@vtube: a bell, urn, tube or funnel in profile (flora.bell generalized), length about
-- 1; u its cm per unit; form the species' unless given (salvertube, liptube, aqcup: the
-- tube under a salver's face, a two-lipped flower's tube, a columbine's cup); tq the tone
-- (nil draws one); lp: a two-lipped flower's length and bend
local function tube(u, form, tq, lp)
  form = form or S.form
  local a, b, c, w, e, h, L, tt, sep = 0.1, 0.24, 1.3, 0, 0, 0, 0.85, 0.14, true
  local f = range(0.06, 0.12)
  local n = floor(S.merosity / 2 + 1.5)
  if n == 1 then n = 0 end                      -- no parts (merosity 0): a rim without lobes
  local bd = sym(0.08)
  if form == "campanulate" then a, b, h, e, f = 0.06, 0, 0.26, 0.07, 0.35 * f end   -- a shoulder, then the rim flares
  if form == "urceolate" then a, b, w, L, tt, f = 0.09, 0, 0.25, 0.8, 0.08, 0.07 end
  if form == "tubular" then a, b, c, w, e, L, tt, f, bd = 0.07, 0.07, 1.5, 0.025, 0.035, 0.95, 0.08, 0.05, 1.8 * bd end
  if form == "funnelform" then a, b, c, L, tt, f = 0.05, 0.4, 2.4, 0.85, 0.1, 0.1 end
  if form == "lilytube" then a, b, c, L, tt, f, n = 0.045, 0.32, 2.8, 0.95, 0.16, 0.08, 4 end   -- a trumpet lily's
  if form == "salvertube" then a, b, c, L, n = 0.035, 0.02, 1, 0.55, 0 end
  if form == "liptube" then a, b, c, L, n, bd, sep = 0.04, 0.06, 1.6, lp.L, 0, lp.b, false end
  if form == "aqcup" then a, b, c, L, tt, sep = 0.12, 0.04, 1, 0.32, 0.08, false end
  local tk = 0.7
  if S.apex == "rounded" or S.apex == "emarginate" then tk, tt = 1, 0.7 * tt end
  local g, wob = nil, 0
  if u > 0.45 then g, wob = fbm(), 0.015 end
  local function rad(v)                         -- \flo@turad: the radius at v
    local r = a
    if v > 0 then
      if b > 0 then r = r + b * v ^ c end
      if w > 0 then r = r + w * sin(180 * min(1, v / 0.92) ^ 0.85) end
      if h > 0 then r = r + h * (1 - (1 - v) ^ 2.5) end
      if e > 0 then r = r + e * v ^ 6 end
    end
    return r
  end
  local pts = {}
  for k = 0, 10 do
    local v = k / 10
    local x = rad(v) + bd * v * v
    if wob > 0 then x = x + wob * wave(g, v * 6) * v end
    pts[k + 1] = { x, L * v }
  end
  local Rr = rad(1) + f
  if n > 0 then                                 -- the lobes of the rim
    for k = 1, n do
      local v = 1 - 2 * (k - 1) / (n - 1)
      local x = Rr * v + bd + sym(0.03)
      local y = L + tt * (1 - 0.55 * v * v) + sym(0.02)
      pts[#pts + 1] = { x, y }
      if k < n then
        x = Rr * 0.85 * (v - 1 / (n - 1)) + bd
        pts[#pts + 1] = { x, L + 0.025 + sym(0.015) }
      end
    end
  end
  for k = 0, 10 do
    local v = (10 - k) / 10
    local x = bd * v * v - rad(v)
    if wob > 0 then x = x + wob * wave(g, v * 6 + 40) * v end
    pts[#pts + 1] = { x, L * v }
  end
  local q = tq or range(0.3, 0.4)
  smooth(pts, true, tk) fill(tone(q), 1)
  if u > 0.15 then
    smooth(pts, true, tk) ink.clip()
    if u > 0.45 then mottle(-0.5, 0, 0.5, L + 0.2, 10, 0.12, q - 0.2, q + 0.2, 10, false)
    else mottle(-0.5, 0, 0.5, L + 0.2, 5, 0.12, q - 0.2, q + 0.2, 0, false) end
    local sd = chance(0.5) and 1 or -1           -- the shaded side
    local xc = 2 * sd
    local col = tone(0.12)
    for _, s in ipairs({ { 0.12, 0.2 }, { 0.6, 0.16 } }) do   -- the shade's edge follows the body's curve
      local sp = { { sd * s[1] * a, -0.1 } }
      for k = 0, 10 do
        local v = k / 10
        sp[#sp + 1] = { sd * s[1] * rad(v) + bd * v * v, L * v }
      end
      sp[#sp + 1] = { sd * s[1] * Rr + bd, L + tt + 0.1 }
      sp[#sp + 1] = { xc, L + tt + 0.1 }
      sp[#sp + 1] = { xc, -0.1 }
      ink.poly(sp, true) fill(col, s[2], true)
    end
    for k = -2, 2 do                            -- streaks down the tube, dark and light
      local fk = k / 2.5
      local xa, ya = fk * rad(0.05) * 0.9 + bd * 0.0025, 0.05 * L
      local xb, yb = fk * rad(0.4) * 0.9 + bd * 0.16, 0.4 * L
      local xc2, yc = fk * rad(0.75) * 0.9 + bd * 0.5625, 0.75 * L
      local xd = fk * Rr * 0.95 + bd + sym(0.03)
      taper(xa, ya, xb, yb, xc2, yc, xd, L + 0.06, 0.02, 0.012)
      if k % 2 ~= 0 then fill(tone(0.08), 0.3, true) else fill(WHITE, 0.55) end
    end
    smooth(pts, true, tk) stroke(tone(0.06), 1.5 * u, 0.3, true)
    ink.unclip()
  end
  smooth(pts, true, tk) stroke(tone(0.08), 0.18, 0.35, false)
  if S.stamens == "exserted" and n > 0 then
    for k = -1, 1 do
      local xa, ya = k * 0.02 + 0.36 * bd, 0.6 * L
      local xb = k * 0.08 + 1.2 * bd + sym(0.02)
      local yb = L + range(0.18, 0.32)
      moveto(xa, ya) lineto(xb, yb) stroke(tone(0.12), 0.25, 0.85, false)
      circle(xb, yb, 0.018) fill(tone(0.05), 1)
    end
  end
  if sep then                                   -- the sepals
    local col = tone(0.1)
    for k = -1, 1 do
      local xd = k * (a + 0.07) + sym(0.03)
      taper(k * 0.04, -0.03, k * 0.07, 0.06, 0.9 * xd, 0.14, xd, 0.22, 0.07, 0.004) fill(col, 0.95)
    end
    circle(0, -0.02, 0.05) fill(tone(0.08), 1)
  end
end
-- \flo@vcup: a calyx in profile, about 0.27 long times k; u its cm per unit
local function cup(u, k)
  local pts = { { 0.03, -0.03 }, { 0.068, 0.08 * k }, { 0.085, 0.17 * k } }
  pts[4] = { 0.11, 0.25 * k + sym(0.02) }
  pts[5] = { 0.055, 0.2 * k }
  pts[6] = { 0.013, 0.27 * k + sym(0.02) }
  pts[7] = { -0.03, 0.2 * k }
  pts[8] = { -0.08, 0.26 * k + sym(0.02) }
  pts[9], pts[10], pts[11] = { -0.085, 0.15 * k }, { -0.064, 0.06 * k }, { -0.03, -0.03 }
  smooth(pts, true, 0.7) fill(tone(0.12), 0.95)
  if u > 0.15 then
    taper(0.034, 0.01, 0.055, 0.08 * k, 0.068, 0.14 * k, 0.085, 0.22 * k, 0.03, 0.004) fill(WHITE, 0.4)
    smooth(pts, true, 0.7) stroke(tone(0.06), 0.18, 0.4, false)
  end
  circle(0, -0.02, 0.045) fill(tone(0.08), 1)
end
-- \flo@vpea: a pea flower in profile, the standard on the +x side; u its cm per unit,
-- F the flower's knobs (tone, notchp, notchk)
local function pea(u, F)
  local q = F.tone or range(0.3, 0.4)
  local a, l, w = range(8, 28), range(0.55, 0.7), range(0.75, 0.95)
  -- the standard, broad and notched, with dark guide lines
  ink.push() ink.translate(0.04, 0.2) ink.rotate(-90 - a) ink.scale(l) xscale(w)
  M.petal({ pointed = false, W = 0.5, heart = true, notchp = 0.8, notchk = 1.4, tone = q, u = u * l })
  if S.guides ~= "none" and u * l > 0.12 then
    local col = tone(0.06)
    for k = -2, 2 do
      local f = k * 0.1 + sym(0.03)
      local len = range(0.3, 0.45) * (1 - 1.5 * f * f)
      taper(0.2 * f, 0.05, 0.6 * f, 0.35 * len + 0.05, 0.9 * f, 0.7 * len + 0.05, 1.1 * f, len + 0.05, 0.04, 0.004)
      fill(col, 0.45, true)
    end
  end
  ink.pop()
  -- the keel, a paler pointed boat
  local k, t = range(0.58, 0.68), range(8, 16)
  ink.push() ink.translate(-0.03, 0.2) ink.rotate(t) ink.scale(k)
  M.petal({ pointed = true, W = 0.12, bend = range(0.22, 0.32), heart = false, tone = q + 0.12, u = u * k,
    notchp = F.notchp, notchk = F.notchk })
  ink.pop()
  -- the near wing
  local n = range(0.44, 0.52)
  t = range(0, 8)
  ink.push() ink.translate(0.02, 0.21) ink.rotate(t) ink.scale(n)
  M.petal({ pointed = false, W = 0.26, bend = range(0, 0.08), notchp = 0, notchk = F.notchk, ns = 3, heart = false,
    tone = q - 0.03, u = u * n })
  ink.pop()
  cup(u, 0.9)
end
-- \flo@vlips: a two-lipped flower in profile, the hood on the +x side; u its cm per unit
local function lips(u, tq)
  local q = tq or range(0.3, 0.4)
  local L, lb = range(0.55, 0.68), sym(0.06)
  -- the lower lip, broad and drooping, with the species' dots or lines
  local a, l = range(35, 55), range(0.3, 0.4)
  ink.push() ink.translate(lb - 0.05, L - 0.03) ink.rotate(a) ink.scale(l)
  M.petal({ pointed = false, W = 0.42, bend = range(-0.25, -0.1), notchp = 0.6, notchk = 1.4, ns = 5, heart = false,
    tone = q + 0.05, u = u * l })
  if u * l > 0.1 then
    local g = S.guides
    if g == "dots" then
      for _ = 1, 7 do
        local x, y = sym(0.22), range(0.12, 0.55)
        circle(x, y, range(0.025, 0.045))
      end
      fill(tone(0.06), 0.7, true)
    end
    if g == "lines" then
      local col = tone(0.06)
      for k = -1, 1 do
        local f = k * 0.12 + sym(0.03)
        local len = range(0.35, 0.5)
        taper(0, 0.05, 0.5 * f, 0.35 * len + 0.05, 0.8 * f, 0.7 * len + 0.05, f, len + 0.05, 0.035, 0.004)
        fill(col, 0.5, true)
      end
    end
  end
  ink.pop()
  -- the hood, rising steeply and curling forward
  a, l = range(-80, -68), range(0.36, 0.44)
  ink.push() ink.translate(lb + 0.05, L - 0.04) ink.rotate(a) ink.scale(l)
  M.petal({ pointed = false, W = 0.26, bend = range(-1.1, -0.85), notchp = 0.4, notchk = 1, ns = 3, heart = false,
    tone = q, u = u * l })
  ink.pop()
  if S.stamens == "exserted" then
    for k = 0, 1 do
      local xa, ya = lb + 0.02, L
      local xb = xa + range(0.1, 0.2) + 0.04 * k
      local yb = ya + range(0.3, 0.42) - 0.05 * k
      moveto(xa, ya) lineto(xb, yb) stroke(tone(0.12), 0.25, 0.85, false)
      circle(xb, yb, 0.018) fill(tone(0.05), 1)
    end
  end
  tube(u, "liptube", tq, { L = L, b = lb })
  cup(u, 1.1)
end
-- \flo@vaquilegia: a columbine in profile; u its cm per unit
local function aquilegia(u, tq)
  local q = tq or range(0.3, 0.4)
  for k = 0, 1 do                               -- the back sepals
    local sg = k == 0 and -1 or 1
    local a = 90 - sg * range(105, 135)
    local len, b = range(0.32, 0.4), sym(0.08)
    ink.push() ink.translate(0, 0.12)
    ivpart(a, len, 0, u, part(true, 0.14, b, q - 0.04, false, 3, 0))
    ink.pop()
  end
  local h = range(0, 0.16)
  for k = -1, 1 do                              -- the spurs, each with its knob
    local len = range(0.38, 0.58)
    local t = sym(0.03)
    local xa, xb, yb = k * 0.08, k * 0.16 + t, 0.16 - 0.4 * len
    local xc, yc = k * 0.14 + t, 0.16 - 0.8 * len
    local xd, yd = k * (0.16 - h) + t, 0.16 - len
    local tp = k == 0 and q - 0.06 or q
    local col = tone(tp)
    taper(xa, 0.16, xb, yb, xc, yc, xd, yd, 0.12, 0.024) fill(col, 1)
    if u > 0.15 then taper(xa + 0.025, 0.14, xb + 0.018, yb, xc + 0.01, yc, xd, yd, 0.018, 0.024) fill(WHITE, 0.35) end
    circle(xd, yd, 0.03) fill(tone(tp - 0.12), 1)
  end
  for k = 0, 1 do                               -- the front sepals
    local sg = k == 0 and -1 or 1
    local a = 90 - sg * range(45, 70)
    local len, b = range(0.3, 0.38), sym(0.08)
    local tp = q + sym(0.03)
    ink.push() ink.translate(0, 0.12)
    ivpart(a, len, 0, u, part(true, 0.14, b, tp, false, 3, 0))
    ink.pop()
  end
  ink.push() ink.translate(0, 0.08) tube(u, "aqcup", q + 0.1) ink.pop()
end
-- \flo@vcalyx: a nodding flower's calyx, seen from below
local function calyx()
  local n = floor(max(3, min(6, S.merosity)))
  local a0 = range(0, 360)
  local col = tone(0.1)
  for k = 1, n do
    local a = a0 + 360 * k / n + sym(8)
    local t = range(0.26, 0.34)
    local xa, ya = t * cos(a), t * sin(a)
    taper(0, 0, 0.4 * xa - 0.06 * ya, 0.4 * ya + 0.06 * xa, 0.75 * xa - 0.04 * ya, 0.75 * ya + 0.04 * xa, xa, ya, 0.08, 0.004)
    fill(col, 0.95)
  end
  circle(0, 0, 0.05) fill(tone(0.08), 1)
end

-- ---------------------------------------------------------------- the species' flower
-- \flo@stone: the flower's tone from its color (nil: the organs draw their own), and the
-- accent's ramp for red and orange
local STONE = { violet = { 0.18, 0.26 }, pink = { 0.34, 0.42 }, white = { 0.46, 0.54 },
  yellow = { 0.4, 0.48 }, green = { 0.38, 0.46 }, red = { 0.3, 0.4, true }, orange = { 0.42, 0.52, true } }
local function stone()
  local s = STONE[S.color]
  pen.accent = false
  if not s then return nil end
  if s[3] then pen.accent = true end
  return range(s[1], s[2])
end
-- \flo@sflower: the species' flower face on, diameter 1; d its diameter in pt. F: tone
-- (nil: each organ draws one), notchp, notchk (a round petal's notch)
function M.sflower(d, F)
  local form, nn = S.form, S.merosity
  if S.merovar == 1 then
    local t = int(1, 10)
    if t == 1 then nn = nn - 1 end
    if t > 7 then nn = nn + 1 end
  end
  local pointed = S.petalshape == "lanceolate" or S.petalshape == "linear"
  if form == "head" then
    M.daisy(d, S.merosity, F)
  elseif form == "lily" then
    ink.push() ink.scale(0.48)
    local u = d * 0.48 / 28.45276
    local tilt = lily(u, F.tone)
    if S.corona ~= "none" then corona(u, tilt, F.tone) end
    ink.pop()
  elseif form == "iris" and d >= 8 then
    iris(d, F.tone)
  elseif d < 22 then
    local fp = floor(max(4, min(6, nn)))
    ink.push() ink.scale(0.5)
    if d < 8 then M.floret(d * 0.5 / 28.45276, fp, { star = pointed, tone = F.tone })
    else M.blossom(d * 0.5 / 28.45276, fp, { tone = F.tone }) end
    ink.pop()
  else
    local np = S.clade == "monocot" and 2 * nn or nn
    local W
    if not pointed then W = form == "cruciform" and 0.3 or max(0.28, min(0.5, 0.66 / S.petalaspect)) end
    local ns
    if S.guides == "none" and not pointed then ns = 5 end
    local notchp, notchk = F.notchp, F.notchk
    if form == "rosaceous" then ns, notchp, notchk = 3, 0.85, 1.6 end
    ink.push() ink.scale(0.45)
    M.flower(d * 0.45 / 28.45276, np, { pointed = pointed, tone = F.tone, W = W, ns = ns, notchp = notchp, notchk = notchk })
    ink.pop()
    if form == "rosaceous" then M.boss(S.stamens == "exserted") end
  end
end
-- \flo@vsalver: a salver's slender tube with its face tilted on top
local function salver(d, F)
  tube(d, "salvertube", F.tone)
  local t = range(0.3, 0.45)
  ink.push() ink.translate(0, 0.55) yscale(t) ink.rotate(sym(30))
  M.sflower(d * 28.45276, F)
  ink.pop()
end
-- \flo@bview: how the flower is seen, from its form and orientation, and its turn. o: side
-- (1 left, -1 right; nil draws one), fan (a head's flowers, already pointing out)
local TUBES = { campanulate = true, urceolate = true, tubular = true, funnelform = true }
local function bview(o)
  local orient, fm = S.orient, S.form
  local vw
  if orient == "erect" then vw = "face"
  else
    vw = orient == "nodding" and "back" or "tilt"
    if fm == "lily" then
      vw = "lily"
      if S.reflex < -40 then vw = "turk" elseif S.cup > K(0.6) then vw = "trumpet" end
    end
    if fm == "salverform" then vw = "salver" end
  end
  if TUBES[fm] then vw = "tube" end
  if fm == "papilionaceous" then vw = "pea" end
  if fm == "bilabiate" then vw = "lips" end
  if fm == "orchid" then vw = "orchid" end
  if orient ~= "erect" and fm == "spurred" then vw = "spur" end
  local turn = 0
  if orient == "erect" then
    if vw == "tube" then turn = sym(15) end
  else
    local side = o.side
    if side == nil then side = chance(0.5) and 1 or -1 end
    local t
    if o.fan then t = range(5, 25)
    elseif orient == "nodding" then t = range(100, 140) else t = range(50, 80) end
    turn = side * t
  end
  return vw, turn
end
-- \flo@bloom: the species' flower d cm across at a stage: a bud below 0.3, the fruit above
-- 1.15, past its best above 1; seen as bview says. o: side, fan (see bview)
function M.bloom(d, st, o)
  local was = pen.accent
  local tq = stone()
  local vw, turn = bview(o)
  if st < 0.3 then
    local k = 0.55 * d
    ink.push()
    if vw ~= "face" then ink.rotate(turn) end
    ink.scale(k) M.bud(k, tq)
    ink.pop()
  elseif st > 1.15 then
    local fu = 0.35 * d
    ink.push() ink.scale(fu) M.fruit(fu) ink.pop()
  else
    local t = sym(30)
    local bd = d * min(1, 0.55 + (st - 0.3) * 0.9)
    if st > 1 then bd = bd * (1 - 0.8 * (st - 1)) end
    local lift = pen.lift
    local F = { tone = tq, notchp = o.notchp or 0.4, notchk = o.notchk or 1 }
    ink.push()
    if vw ~= "face" then ink.rotate(turn) end
    if st > 1 then pen.lift = 1 - (1 - pen.lift) * (1 - 3 * (st - 1)) end
    if vw == "tube" then
      ink.scale(0.8 * bd) tube(0.8 * bd, nil, tq)
    elseif vw == "trumpet" then
      ink.scale(0.62 * bd) tube(0.62 * bd, "lilytube", tq)
    elseif vw == "turk" then
      ink.scale(0.42 * bd) turkscap(0.42 * bd, tq)
    elseif vw == "salver" then
      ink.scale(bd) salver(bd, F)
    elseif vw == "pea" then
      ink.scale(0.8 * bd)
      if turn < 0 then xscale(-1) end
      pea(0.8 * bd, F)
    elseif vw == "lips" then
      ink.scale(bd)
      if turn < 0 then xscale(-1) end
      lips(bd, tq)
    elseif vw == "orchid" then
      upright() ink.rotate(0.4 * t) xscale(range(0.75, 0.95))
      ink.scale(bd) orchid(bd, tq)
    elseif vw == "spur" then
      ink.scale(0.75 * bd) aquilegia(0.75 * bd, tq)
    else
      local ys = 1
      if vw == "tilt" then ys = range(0.5, 0.7) end
      if vw == "back" then ys = range(0.55, 0.75) pen.lift = 1 - (1 - pen.lift) * 0.85 end
      if ys < 1 then ink.translate(0, 0.12 * bd) yscale(ys) end
      local isiris = S.form == "iris"
      if isiris then upright() t = 0.6 * t end
      ink.rotate(t)
      if isiris then xscale(range(0.72, 1)) end
      ink.scale(bd)
      M.sflower(bd * 28.45276, F)
      if vw == "back" then calyx() end
    end
    pen.lift = lift
    ink.pop()
  end
  pen.accent = was
end

-- ================================================================ M8f3: clusters and stages
-- ---------------------------------------------------------------- stems
-- \flo@vstem: a filled ribbon along the points (cm), widths w0 to w1, or a stroke under 0.7 bp
local function stem(pts, w0, w1, t)
  local w = (w0 + w1) / 2 * 28.3465
  local c = tone(t)
  if w < 0.7 then smooth(pts, false) stroke(c, w, 1, false) return end
  ribbon(pts, w0, w1, c)
end
local function line(x1, y1, x2, y2, c, w, op) moveto(x1, y1) lineto(x2, y2) stroke(c, w, op, false) end
-- \flo@hair: a pedicel
local function hair(x1, y1, x2, y2) line(x1, y1, x2, y2, tone(0.12), 0.25, 0.9) end
-- \pgfpathellipse: center, first and second axis
local KAPPA = 0.55228475
local function ellipse(cx, cy, ax, ay, bx, by)
  local k = KAPPA
  moveto(cx + ax, cy + ay)
  curveto(cx + ax + k * bx, cy + ay + k * by, cx + bx + k * ax, cy + by + k * ay, cx + bx, cy + by)
  curveto(cx + bx - k * ax, cy + by - k * ay, cx - ax + k * bx, cy - ay + k * by, cx - ax, cy - ay)
  curveto(cx - ax - k * bx, cy - ay - k * by, cx - bx - k * ax, cy - by - k * ay, cx - bx, cy - by)
  curveto(cx - bx + k * ax, cy - by + k * ay, cx + ax - k * bx, cy + ay - k * by, cx + ax, cy + ay)
  close()
end
local function atan2(y, x) return math.atan(y, x) / RAD end
-- \flo@walk: n steps from (x, y), heading h turning by dh in all, len long; the n+1 points
local function walk(x, y, h, dh, len, n)
  local pts, ds = { { x, y } }, len / n
  for k = 1, n do
    local a = h + dh * (k - 0.5) / n
    x, y = x + ds * cos(a), y + ds * sin(a)
    pts[k + 1] = { x, y }
  end
  return pts, x, y
end
-- an axis of `steps` steps bending away to one side, as flora's cyme bends (raceme, curl)
local function bent(len, steps, h, side, turn)
  local pts, x, y, ds = { { 0, 0 } }, 0, 0, len / steps
  for k = 1, steps do
    local v = (k - 0.5) / steps
    local a = h + side * turn * v * v * sqrt(sqrt(v))
    x, y = x + ds * cos(a), y + ds * sin(a)
    pts[k + 1] = { x, y }
  end
  return pts
end
-- \flo@fdiam: the flower's diameter in a cluster at scale k
local function fdiam(k) return min(2.8, 0.7 + 0.032 * S.size) * k end

-- ---------------------------------------------------------------- the flowers of a cluster
-- a cluster collects its flowers (\flo@fadd) and draws them after its stalks (\flo@fdraw);
-- \flo@fat: a dot below stage -0.25, else the bloom, its side from its angle
local FAN = false                               -- \flo@bfan: a head's flowers point out already
local function flowers() return {} end
local function fadd(list, x, y, a, d, st) list[#list + 1] = { x, y, a, d, st } end
local function fdraw(list)
  for _, f in ipairs(list) do
    local x, y, a, d, st = f[1], f[2], f[3], f[4], f[5]
    ink.push() ink.translate(x, y) ink.rotate(a)
    if st < -0.25 then circle(0, 0, 0.3 * d) fill(tone(0.2), 1)
    else M.bloom(d, st, { side = a < 0 and -1 or 1, fan = FAN }) end
    ink.pop()
  end
end
-- a flower on a pedicel from (px, py) to (qx, qy): a dot when f > lim, the bloom otherwise
local function pedicel(px, py, qx, qy, s, st, sg, f, lim, dot)
  ink.push() ink.translate(qx, qy) ink.rotate(atan2(qy - py, qx - px) - 90)
  if f > lim then circle(0, 0, dot * s) fill(tone(0.2), 1)
  else M.bloom(s, st, { side = sg }) end
  ink.pop()
end
-- a stalk from the origin to (x, y) (\flo@blossoms, \flo@budding)
local function stalkto(dd, x, y)
  moveto(0, 0) curveto(0, 0.4 * dd, 0.6 * x, 0.7 * y, x, y) stroke(tone(0.1), 0.35, 1, false)
end

-- ---------------------------------------------------------------- single flowers and buds
-- \flo@blossoms: two to five flowers on curved stalks, a bud at the fork
local function blossoms(d, st)
  local center = S.openorder == "center"
  local n = int(2, 4)
  if S.count > 9 then n = n + 1 end
  for k = 1, n do
    local f = k - (n + 1) / 2
    local a = f * 38 + sym(10)
    local dd = d * range(0.55, 0.95)
    if k > 1 then dd = dd * range(0.85, 1.1) end
    local x, y = dd * sin(a), dd * cos(a)
    stalkto(dd, x, y)
    local m = max(1, (n - 1) / 2)
    local t = center and st - 0.3 * abs(f) / m or st - 0.3 * (1 - abs(f) / m)
    local s = d * range(0.8, 1.05)
    ink.push() ink.translate(x, y) M.bloom(s, t, { side = a < 0 and 1 or -1 }) ink.pop()
  end
  if chance(0.6) then
    local t, s = sym(50), 0.45 * d
    local was = pen.accent
    ink.push() ink.rotate(t) ink.scale(s)
    M.bud(s, stone())
    ink.pop()
    pen.accent = was
  end
end
-- \flo@budding: two to four buds on stalks; d the flowers' diameter
function M.budding(d)
  local was = pen.accent
  local n = int(2, 4)
  local tq = stone()
  for k = 1, n do
    local a = (k - (n + 1) / 2) * 34 + sym(10)
    local dd = d * range(0.5, 0.85)
    local x, y = dd * sin(a), dd * cos(a)
    stalkto(dd, x, y)
    local s = d * range(0.45, 0.6)
    ink.push() ink.translate(x, y) ink.rotate(-0.6 * a) ink.scale(s) M.bud(s, tq) ink.pop()
  end
  pen.accent = was
end

-- ---------------------------------------------------------------- umbels and racemes
-- \flo@umbelunit: a compound umbel, rays fanning upward to umbellets of starry florets
local function cumbel(reach, st)
  local tq = stone()
  local nr = floor(max(8, min(13, S.count + 3)))
  local ns = floor(max(5, min(8, S.rays / 2)))
  local lo, hi = 0.25, 0.42
  if tq then lo, hi = min(0.5, tq - 0.05), min(0.62, tq + 0.05) end
  for g = 1, nr do
    local a = 90 + (g - (nr + 1) / 2) * (110 / nr) + sym(4)
    local L = reach * range(0.78, 1) * (1 - 0.15 * abs(a - 90) / 55)
    local ex, ey = L * cos(a), L * sin(a)
    moveto(0, 0) curveto(0.15 * ex, 0.5 * ey, 0.6 * ex, 0.95 * ey, ex, ey) stroke(tone(0.12), 0.25, 0.9, false)
    for h = 1, ns do
      local b = a + (h - (ns + 1) / 2) * 16 + sym(5)
      local l = reach * range(0.12, 0.2)
      local fx, fy = ex + l * cos(b), ey + l * sin(b)
      line(ex, ey, fx, fy, tone(0.14), 0.18, 0.85)
      local r = reach * range(0.035, 0.05)
      local t = range(lo, hi)
      ink.push() ink.translate(fx, fy)
      if st > 1.15 then                       -- the fruit: a pair of seeds
        ink.rotate(b - 90) ink.scale(r)
        ellipse(-0.35, 0.9, 0.32, 0, 0, 0.9) ellipse(0.35, 0.9, 0.32, 0, 0, 0.9)
        fill(tone(0.16), 1)
      elseif st < 0.3 then circle(0, 0, 0.45 * r) fill(tone(0.2), 1)
      else ink.scale(r) M.floret(r, 5, { star = true, tone = t }) end
      ink.pop()
    end
  end
  for g = -1, 1 do                               -- the bracts
    local a = g * 40 + sym(10) + 180
    local s = reach * range(0.15, 0.22)
    ink.push() ink.rotate(a) ink.scale(s) M.leaflet({ W = 0.06, u = s }) ink.pop()
  end
end
-- \flo@racemeunit: flowers along a bending axis, the lowest oldest; ped the pedicel's length
local function raceme(len, ped, bend, st)
  local side = chance(0.5) and 1 or -1
  local turn = bend * range(0.75, 1.25)
  local h = 90 + sym(8)
  local pts = bent(len, 24, h, side, turn)
  local nr = normals(pts)
  stem(pts, 0.03, 0.006, 0.1)
  stone()
  local fd = min(1.0, 0.5 + 0.03 * S.size) * 0.8
  if bend > 150 then fd = min(0.32, fd) end
  local n = floor(min(S.count, int(8, 12)))
  for g = 1, n do
    local v = g / n
    local f = 0.1 + 0.88 * sqrt(v) * sqrt(sqrt(v))
    local ri = floor(max(2, min(25, floor(f * 24 + 1.5))))
    local sg = g % 2 == 1 and -1 or 1
    local px, py, nx, ny = pts[ri][1], pts[ri][2], nr[ri][1], nr[ri][2]
    local stalk = (ped * (1 - f) + 0.03 * f) * range(0.8, 1.2)
    local qx, qy = px + (nx * sg * 0.75 + ny * 0.65) * stalk, py + (ny * sg * 0.75 - nx * 0.65) * stalk
    if stalk > 0.03 then hair(px, py, qx, qy) end
    local s = fd * (1 - 0.5 * f) * range(0.85, 1.15)
    pedicel(px, py, qx, qy, s, st + 0.35 - 1.1 * f, sg, f, 0.86, 0.18)
  end
end

-- ---------------------------------------------------------------- cymes and the rest
-- \flo@curl: a coiled cyme from (x, y), its flowers outside the coil or alternating
local function curl(x, y, len, w, late, h, side, n, fd, st)
  local turn = (100 + 170 * S.coil) * range(0.85, 1.15)
  local pts = bent(len, 36, h, side, turn)
  for _, p in ipairs(pts) do p[1], p[2] = p[1] + x, p[2] + y end
  local nr = normals(pts)
  stem(pts, w, 0.006, 0.1)
  local same = S.siderule == "same"
  for g = 1, n do
    local v = g / n
    local f = 0.18 + 0.82 * sqrt(v) * sqrt(sqrt(v))
    local ri = floor(max(2, min(37, floor(f * 36 + 1.5))))
    local sg
    if same then sg = -side elseif g % 2 == 1 then sg = -1 else sg = 1 end
    local px, py, nx, ny = pts[ri][1], pts[ri][2], nr[ri][1], nr[ri][2]
    local stalk = (0.1 - 0.08 * f) * range(0.7, 1.2)
    local qx, qy = px + nx * sg * stalk, py + ny * sg * stalk
    if stalk > 0.03 then hair(px, py, qx, qy) end
    local s = fd * (1 - 0.74 * f) * range(0.85, 1.15)
    pedicel(px, py, qx, qy, s, st + 0.55 - f - late, sg, f, 0.8, 0.3)
  end
  return pts
end
-- \flo@curlunit: flora's cyme as the species' coiled cyme, sometimes forked
local function monochasium(len, st)
  stone()
  local side = chance(0.5) and 1 or -1
  local h = 90 + sym(10)
  local fd = min(0.4, 0.17 + 0.014 * S.size)
  local n = floor(max(9, min(17, S.count + 2)))
  local pts = curl(0, 0, len, 0.026, 0, h, side, n, fd, st)
  if chance(0.6) then
    local ci = floor(37 * range(0.25, 0.4))
    local xa, ya, xb, yb = pts[ci][1], pts[ci][2], pts[ci + 1][1], pts[ci + 1][2]
    h = atan2(yb - ya, xb - xa) - side * range(30, 50)
    local L = len * range(0.45, 0.65)
    curl(xa, ya, L, 0.018, 0.15, h, -side, floor(max(6, n * K(0.7))), fd, st)   -- 0.7 as TeX holds it: 10 flowers, then 6
  end
end
-- \flo@forkunit: flora's sprig as the species' forked cyme, a flower in every fork
local function dichasium(len, st)
  stone()
  local list = flowers()
  local depth = floor(min(3, S.depth + 1))
  local fd = min(0.45, 0.16 + 0.012 * S.size)
  local B = { { x = 0, y = 0, h = 90 + sym(8), l = len * 0.4, w = 0.024, v = 0 } }
  local i = 1
  while i <= #B do
    local b = B[i]
    local h, l, w, v = b.h, b.l, b.w, b.v
    local db = sym(20)
    local pts, x, y = walk(b.x, b.y, h, db, l, 6)
    local we = 0.72 * w
    stem(pts, w, we, 0.1)
    h = h + db
    local t = st - 0.3 * v + sym(0.05)
    local s = fd * (1 - 0.1 * v) * range(0.85, 1.1)
    if v < depth then
      if v < 2 and chance(0.4) then             -- a leaflet at the fork
        local sg = chance(0.5) and 1 or -1
        local a = h - 90 + sg * range(40, 70)
        local ls = l * range(0.35, 0.55)
        ink.push() ink.translate(x, y) ink.rotate(a) ink.scale(ls) M.leaflet({ W = 0.14, u = ls }) ink.pop()
      end
      local k = chance(0.2) and 3 or 2
      for j = 1, k do
        local a
        if k == 2 then
          a = range(16, 34)
          if j == 1 then a = -a end
        else a = (j - 2) * range(22, 36) end
        B[#B + 1] = { x = x, y = y, h = h + a, l = l * range(0.62, 0.8), w = we, v = v + 1 }
      end
      local xa, ya = x + 0.22 * l * cos(h), y + 0.22 * l * sin(h)
      hair(x, y, xa, ya)
      fadd(list, xa, ya, h - 90, s, t)
    else
      fadd(list, x, y, h - 90, s, t)
    end
    i = i + 1
  end
  fdraw(list)
end
-- \flo@axis: a raceme-like axis; \flo@axisat: its point, normal and heading at fraction f
local function axis(len, bend, w)
  local side = chance(0.5) and 1 or -1
  local turn = bend * range(0.75, 1.25)
  local pts = bent(len, 24, 90 + sym(8), side, turn)
  local nr = normals(pts)
  stem(pts, w, 0.006, 0.1)
  return pts, nr
end
local function axisat(pts, nr, f)
  local i = floor(max(2, min(25, floor(f * 24 + 1.5))))
  local nx, ny = nr[i][1], nr[i][2]
  return pts[i][1], pts[i][2], nx, ny, atan2(-nx, ny)
end
-- \flo@panicleunit: branches up an axis, each with a flower at its end and a few on hairs
local function panicle(len, st)
  stone()
  local list = flowers()
  local fd = min(0.34, 0.15 + 0.01 * S.size)
  local A, AN = axis(len, 30, 0.026)
  local m = floor(max(3, min(7, S.count / 4 + 1)))
  local sg = chance(0.5) and 1 or -1
  for g = 1, m do
    local f = 0.1 + 0.66 * (g - 1) / max(1, m - 1) + sym(0.03)
    local px, py, _, _, ah = axisat(A, AN, f)
    sg = -sg
    local ang = range(40, 58)
    local h = ah + sg * ang
    local l = len * 0.42 * (1 - 0.7 * f) * range(0.8, 1.2)
    local db = -sg * 0.55 * ang
    local pts, x, y = walk(px, py, h, db, l, 8)
    local nr = normals(pts)
    fadd(list, x, y, h + db - 90, fd * (1 - 0.3 * f), st + 0.2 - 0.9 * f)
    local k = int(1, 3)
    if S.depth == 2 and f < 0.45 then k = k + 1 end
    for j = 1, k do
      local v = 0.3 + 0.5 * (j - 1) / max(1, k - 1)
      local q = floor(1 + floor(v * 8 + 0.5))
      local s2 = j % 2 == 1 and 1 or -1
      local xa, ya, nx, ny = pts[q][1], pts[q][2], nr[q][1], nr[q][2]
      local t = range(0.1, 0.18)
      local xb, yb = xa + (nx * s2 * 0.8 + ny * 0.6) * t, ya + (ny * s2 * 0.8 - nx * 0.6) * t
      hair(xa, ya, xb, yb)
      fadd(list, xb, yb, atan2(yb - ya, xb - xa) - 90, fd * (0.9 - 0.3 * f), st + 0.3 - 0.9 * f - 0.25 * (1 - v))
    end
    stem(pts, 0.012, 0.005, 0.12)
  end
  local _, _, _, _, ah = axisat(A, AN, 1)
  fadd(list, A[25][1], A[25][2], ah - 90, 0.6 * fd, st - 0.6)
  fdraw(list)
end
-- \flo@thyrseunit: little forked cymes in pairs up an axis
local function thyrse(len, st)
  stone()
  local list = flowers()
  local fd = min(0.42, 0.15 + 0.012 * S.size)
  local A, AN = axis(len, 20, 0.028)
  local m = floor(max(6, min(11, S.count / 2.5)))
  for g = 1, m do
    local f = 0.08 + 0.84 * (g - 1) / max(1, m - 1)
    local px, py, _, _, ah = axisat(A, AN, f)
    for n = 0, 1 do
      local sg = n == 0 and 1 or -1
      local h = ah + sg * range(45, 65)
      local l = len * 0.17 * (1 - 0.75 * f) * range(0.8, 1.2)
      local xa, ya = px + l * cos(h), py + l * sin(h)
      hair(px, py, xa, ya)
      local t, s = st + 0.3 - 1.0 * f, fd * (1 - 0.4 * f)
      if f < 0.8 then
        for j = 0, 1 do
          local a = h + (j == 0 and 1 or -1) * range(30, 50)
          local xb, yb = xa + 0.55 * l * cos(a), ya + 0.55 * l * sin(a)
          hair(xa, ya, xb, yb)
          fadd(list, xb, yb, a - 90, 0.85 * s, t - 0.2)
        end
      end
      fadd(list, xa + 0.15 * l * cos(h), ya + 0.15 * l * sin(h), h - 90, s, t)
    end
  end
  local _, _, _, _, ah = axisat(A, AN, 1)
  fadd(list, A[25][1], A[25][2], ah - 90, 0.6 * fd, st - 0.75)
  fdraw(list)
end
-- \flo@corymbunit: stalks of falling length, the flowers level at the top; fd the flowers' size;
-- none at length 0 (its width divides)
local function corymb(len, fd, st)
  if len == 0 then return end
  stone()
  local list = flowers()
  local cfd = min(0.6, 0.3 * fd)
  local m = floor(max(3, min(9, min(S.count, 2.6 / cfd))))
  local W, top, ca = len * 0.7, len * 0.92, len * 0.5
  local t = sym(0.06)
  moveto(0, 0) curveto(t, 0.35 * ca, t, 0.7 * ca, 0, ca) stroke(tone(0.1), 0.5, 1, false)
  local sg = chance(0.5) and 1 or -1
  for g = 1, m do
    local v = (g - 1) / max(1, m - 1)
    sg = -sg
    local ya = ca * (0.12 + 0.88 * v)
    local xb = sg * W * (1 - 0.85 * v) * range(0.75, 1)
    local yb = top - 0.32 * xb * xb / W + sym(0.08)
    moveto(0, ya) curveto(0.55 * xb, ya + 0.25 * (yb - ya), 0.95 * xb, yb - 0.45 * (yb - ya), xb, yb)
    stroke(tone(0.12), 0.3, 0.9, false)
    local s = cfd * range(0.85, 1.1)
    fadd(list, xb, yb, -20 * xb / W, s, st - 0.35 * v)
  end
  local c = tone(0.12)
  local xb = sym(0.04)
  line(0, ca, xb, top, c, 0.3, 0.9)
  fadd(list, xb, top, 0, 0.9 * cfd, st - 0.42)
  fdraw(list)
end
-- \flo@spikeunit: flowers close along an axis, in threes
local function spike(len, st)
  stone()
  local list = flowers()
  local fd = min(0.5, 0.2 + 0.014 * S.size)
  local A, AN = axis(len, 14, 0.03)
  local m = floor(max(14, min(30, S.count + 4)))
  for g = 1, m do
    local f = 0.06 + 0.92 * (g - 1) / max(1, m - 1) + sym(0.01)
    local px, py, nx, ny, ah = axisat(A, AN, f)
    local s = fd * (1 - 0.45 * f) * range(0.85, 1.1)
    local t = st + 0.35 - 1.2 * f
    local k = g % 3
    if k == 0 then fadd(list, px, py, ah - 90 + sym(10), s, t)
    else
      local sg = k == 1 and 1 or -1
      local a = ah - 90 + sg * range(30, 55)
      fadd(list, px + nx * sg * 0.42 * s, py + ny * sg * 0.42 * s, a, s, t)
    end
  end
  fdraw(list)
end
-- \flo@umbelsunit: a simple umbel, its rays opening from the middle or the edge
local function umbel(len, st)
  stone()
  local list = flowers()
  local center = S.openorder == "center"
  local fd = min(0.8, 0.24 + 0.02 * S.size)
  local m = floor(max(4, min(12, S.count)))
  local ang = range(50, 75)
  for g = 1, m do
    local k = g % 2 == 1 and nediv(g + 1, 2) or m + 1 - nediv(g, 2)
    local f = (k - (m + 1) / 2) / max(1, (m - 1) / 2)
    local a = 90 + f * ang + sym(5)
    local R = len * range(0.88, 1.05) * (1 - 0.12 * abs(f))
    local xb, yb = R * cos(a), R * sin(a)
    moveto(0, 0) curveto(0.15 * xb, 0.5 * yb, 0.6 * xb, 0.95 * yb, xb, yb) stroke(tone(0.12), 0.25, 0.9, false)
    local t = center and st - 0.35 * abs(f) or st - 0.35 * (1 - abs(f))
    t = t + sym(0.05)
    local s = fd * range(0.85, 1.1)
    fadd(list, xb, yb, a - 90, s, t)
  end
  for g = -1, 1 do                               -- the bracts
    local a = g * 40 + sym(10) + 180
    local s = len * range(0.12, 0.18)
    ink.push() ink.rotate(a) ink.scale(s) M.leaflet({ W = 0.06, u = s }) ink.pop()
  end
  fdraw(list)
end
-- \flo@headunit: a head of flowers on Vogel's spiral at the top of a stalk
local function head(len, st)
  stone()
  local list = flowers()
  local A, AN = axis(len, 18, 0.024)
  local px, py, nx, ny = axisat(A, AN, 1)
  local r = (0.26 + 0.006 * S.count) * range(0.9, 1.1)
  local cx, cy = px + 0.75 * r * ny, py - 0.75 * r * nx
  local m = floor(max(18, min(34, S.count + 6)))
  for g = 1, m do
    local k = m + 1 - g
    local v, a = sqrt(k / m), 137.508 * k
    local x, y = cx + 0.8 * r * v * cos(a), cy + 0.72 * r * v * sin(a)
    local s = r * 0.65 * (0.7 + 0.3 * v) * range(0.9, 1.1)
    fadd(list, x, y, a - 90, s, st - 0.45 * (1 - v))
  end
  FAN = true
  fdraw(list)
  FAN = false
end
-- \flo@catkinunit: hanging tassels from a twig, fruit in autumn
local function catkin(len, st)
  local tq = stone()
  local h, db = 90 + sym(10), sym(15)
  local A = walk(0, 0, h, db, len, 6)
  stem(A, 0.028, 0.016, 0.1)
  local m = int(2, 4)
  local w = 0.11 + 0.02 * S.size
  local we, t0 = 0.4 * w, tq or 0.4
  for g = 1, m do
    local xa, ya = A[8 - g][1], A[8 - g][2]
    local sg = g % 2 == 1 and 1 or -1
    local hd = h + db + sg * range(40, 80)
    if st > 1.15 then
      local xb, yb = xa + 0.25 * cos(hd), ya + 0.25 * sin(hd)
      hair(xa, ya, xb, yb)
      ink.push() ink.translate(xb, yb) ink.rotate(hd - 90) ink.scale(0.3) M.fruit(0.3) ink.pop()
    else
      local tg = sg == 1 and 270 or -90
      local L = len * 2.2 * range(0.7, 1.1) * min(1, 0.45 + 0.6 * st)
      local ph, r = sym(180), hd - tg
      local pts, x, y, ds = { { xa, ya } }, xa, ya, L / 18
      for k = 1, 18 do
        local v = (k - 0.5) / 18
        local a = tg + r * (1 - min(1, v * 3)) + 7 * sin(360 * v + ph)
        x, y = x + ds * cos(a), y + ds * sin(a)
        pts[k + 1] = { x, y }
      end
      smooth({ pts[1], pts[2], pts[3], pts[4], pts[5] }, false) stroke(tone(0.1), 0.4, 1, false)
      local tassel = {}
      for k = 4, 19 do tassel[k - 3] = pts[k] end
      local nr = normals(tassel)
      stem(tassel, 0.3 * we, 0.1 * we, 0.12)         -- the axis, under its scales
      -- scales on either side by turns, each leaning down and out over the next, lit on one
      -- side and shaded on the other, an anther at its tip; the paper shows between them
      local ca, cb, c = tone(t0 + 0.06), tone(t0 - 0.06), tone(0.16)
      for k = 2, 16 do
        local s2 = k % 2 == 1 and 1 or -1
        local s = w * 0.5 * (1 - 0.55 * (k - 1) / 15)
        local px, py, nx, ny = tassel[k][1], tassel[k][2], nr[k][1], nr[k][2]
        local dx, dy = 0.91 * ny + 0.42 * s2 * nx, 0.42 * s2 * ny - 0.91 * nx
        local cx, cy = px + s2 * 0.35 * s * nx, py + s2 * 0.35 * s * ny
        local l = min(1.04 * s, max(0.75 * s, 1.1 * ds))   -- over the next on its side, if it can
        ellipse(cx, cy, l * dx, l * dy, -0.65 * s * dy, 0.65 * s * dx)
        fill(s2 == 1 and ca or cb, 0.95)
        circle(cx + l * dx, cy + l * dy, range(0.008, 0.014)) fill(c, 0.9)
      end
    end
  end
end

-- ---------------------------------------------------------------- the inflorescence
-- \flo@unitdraw: the species' inflorescence at scale k, at the origin along +y; st the
-- stage of its oldest flower
function M.unit(k, st)
  local was = pen.accent
  local fd = fdiam(k)
  local f = S.inflo
  if f == "solitary" then M.bloom(fd, st, {})
  elseif f == "cumbel" then cumbel(range(0.95, 1.35) * k, st)
  else
    local L = range(1.5, 2.3) * k
    if f == "raceme" then raceme(L, 0.3, 40, st)
    elseif f == "spike" then spike(L, st)
    elseif f == "panicle" then panicle(L, st)
    elseif f == "thyrse" then thyrse(L, st)
    elseif f == "monochasium" then monochasium(L, st)
    elseif f == "catkin" then catkin(0.3 * L, st)
    elseif f == "dichasium" then dichasium(0.8 * L, st)
    elseif f == "corymb" then corymb(0.75 * L, fd, st)
    elseif f == "umbel" then umbel(0.55 * L, st)
    elseif f == "head" then head(0.35 * L, st)
    else blossoms(0.85 * fd, st) end
  end
  pen.accent = was
end

-- ---------------------------------------------------------------- tendrils
-- \flo@vcurl: a tendril, each its own (run, side, turns, tightness): 45 points
local function vcurl(size)
  local hand = chance(0.5) and 1 or -1
  local d = size * range(0.2, 0.9) / 8
  local db = sym(4)
  local t = G.rnd()
  local T = hand * (4 + 21 * t * sqrt(t))
  local r = size * range(0.18, 0.35)
  local q = range(0.12, 0.45)
  local a, x, y = 90, 0, 0
  local pts = { { 0, 0 } }
  for k = 1, 8 do
    a = a + db
    x, y = x + d * cos(a), y + d * sin(a)
    pts[k + 1] = { x, y }
  end
  for k = 1, 36 do
    a = a + T
    local s = r * q ^ (k / 36) * abs(T) * 0.0174533
    x, y = x + s * cos(a), y + s * sin(a)
    pts[k + 9] = { x, y }
  end
  return pts
end
M.vcurl = vcurl
-- \flo@vtendril: a climber's tendril on a stalk of its own
function M.tendril(size)
  stem(vcurl(1.6 * size), 0.022, 0.004, 0.12)
end

-- ================================================================ M8f4: leaves
-- ---------------------------------------------------------------- the monocot's strap
-- \flo@vstrap: a parallel-veined leaf in flora's manner, along +y, length 1; lW its half
-- width's scale, bend (nil draws one), u its cm per unit. A leaf a centimeter long or more
-- may arch over (#5077), its spine turning by T degrees toward its bend, more toward the
-- tip (the species' curl makes it likelier); some of those twist past spine point ka,
-- turning edge on, and show their paler underside beyond it (its twist makes it likelier)
local function strap(lW, bend, u)
  local b = sym(0.25)
  if bend then b = bend end
  local lt = range(0.24, 0.34)
  local T, ka = 0, 25
  if u > K(1) and chance(max(0.1, min(0.65, 0.3 + S.curl))) then
    T = range(30, 85) * min(1, 0.07 / lW)       -- a broad blade arches less, and twists seldom
    if b < 0 then T = -T end
    if chance((0.3 + 0.01 * S.twist) * max(0, min(1, (0.1 - lW) / 0.05))) then ka = int(13, 17) end
  end
  local vt = ka < 25 and (ka - 1.5) / 24 or 2   -- the twist's middle, edge on over 0.3 of the length
  local spine = {}
  if T == 0 then
    for i = 0, 24 do local v = i / 24 spine[i + 1] = { b * v * v, v } end
  else
    local x, y = 0, 0
    spine[1] = { 0, 0 }
    for i = 1, 24 do
      local a = T * ((i - 0.5) / 24) ^ 2
      x, y = x + sin(a), y + cos(a)
      spine[i + 1] = { x / 24, y / 24 }
    end
  end
  local function hw(v)
    if v <= 0 or v >= 1 then return 0 end
    local h = lW * 2.2 * v ^ 0.25 * (1 - v) ^ 0.6
    local t = (v - vt + 0.15) / 0.3              -- beyond it, the underside turned a little away
    if t >= 1 then return 0.8 * h end
    if t > 0 then h = h * max(0.12, (t > 0.5 and 0.8 or 1) * abs(cos(180 * t * t * (3 - 2 * t)))) end
    return h
  end
  local nr = normals(spine)
  local function piece(i0, i1)                  -- the outline over spine points i0..i1
    local left, right = {}, {}
    for i = i0, i1 do
      local h, p, q = hw((i - 1) / 24), spine[i], nr[i]
      left[#left + 1] = { p[1] + q[1] * h, p[2] + q[2] * h }
      right[#right + 1] = { p[1] - q[1] * h, p[2] - q[2] * h }
    end
    local out, n = left, #right
    if hw((i1 - 1) / 24) > 0 then out[#out + 1] = right[n] end
    for k = n - 1, 2, -1 do out[#out + 1] = right[k] end
    if hw((i0 - 1) / 24) > 0 then out[#out + 1] = right[1] end
    return out
  end
  local outline = piece(1, ka)
  smooth(outline, true) fill(tone(lt), 1)
  smooth(outline, true) ink.clip()
  local sd = chance(0.5) and 1 or -1
  local x0, x1, y1 = -lW * 2.5, lW * 2.5, 1
  if T == 0 then
    for i = 0, 12 do
      local v = i / 12
      if i == 0 then moveto(b * v * v, v) else lineto(b * v * v, v) end
    end
    lineto(sd * 2, 1) lineto(sd * 2, 0) close()
  else                                          -- the darker half follows the spine's turn
    for i = 1, ka do
      if i == 1 then moveto(0, 0) else lineto(spine[i][1], spine[i][2]) end
    end
    for i = ka, 1, -1 do
      local h, p, q = sd * (1.3 * hw((i - 1) / 24) + 0.01), spine[i], nr[i]
      lineto(p[1] + q[1] * h, p[2] + q[2] * h)
    end
    close()
    local xa, xb, yb = 0, 0, 0
    for i = 1, 25 do
      local p = spine[i]
      xa, xb, yb = min(xa, p[1]), max(xb, p[1]), max(yb, p[2])
    end
    x0, x1, y1 = xa - lW * 2.5, xb + lW * 2.5, yb + lW
  end
  fill(tone(0.12), 0.28, true)
  mottle(x0, 0, x1, y1, 10, 0.07, lt - 0.2, lt + 0.22, 8, "xy")
  smooth(outline, true) stroke(tone(0.06), 1.3 * u, 0.22, true)
  -- the midrib in white, and faint parallel veins either side
  local mid = {}
  if T == 0 then
    for i = 0, 10 do local v = 0.02 + 0.93 * i / 10 mid[i + 1] = { b * v * v, v } end
  else
    for i = 1, 24 do mid[i] = spine[i] end
  end
  streak(mid, 0.016, 0.002, WHITE, 0.7)
  for k = 1, 4 do
    local f = (k < 3 and k - 3 or k - 2) * 0.38
    local pts = {}
    if T == 0 then
      for i = 0, 10 do
        local v = 0.05 + 0.85 * i / 10
        pts[i + 1] = { b * v * v + f * hw(v), v }
      end
    else
      for i = 1, 11 do
        local p, q, h = spine[2 * i], nr[2 * i], f * hw((2 * i - 1) / 24)
        pts[i] = { p[1] + q[1] * h, p[2] + q[2] * h }
      end
    end
    streak(pts, 0.007, 0.001, WHITE, range(0.18, 0.32))
  end
  ink.unclip()
  if ka < 25 then                               -- past the twist, the underside
    local under = piece(ka, 25)
    smooth(under, true) fill(tone(min(0.64, lt + 0.26)), 1)
    smooth(under, true) ink.clip()
    local sp = {}
    for i = ka, 24 do sp[#sp + 1] = spine[i] end
    streak(sp, 0.014, 0.002, WHITE, 0.6)
    smooth(under, true) stroke(tone(0.06), 1.3 * u, 0.3, true)
    ink.unclip()
  end
end

-- ---------------------------------------------------------------- the species' blade
-- \flo@sshape: the outline's numbers, the base first, then the shape (which may fix the
-- base), then the apex: lvm the widest point, lkb/lka the base's and apex's superellipse
-- exponents, ldrop basal lobes, lhl outward lobes, lobl an oblique base, lacu a drip tip,
-- ltip a notch or a point, lround a round tip whatever the apex
local LBASE = {
  cuneate = function(s) s.kb = 1.15 end,
  rounded = function(s) s.kb = 2 end,
  truncate = function(s) s.kb = 3.5 end,
  cordate = function(s) s.kb, s.drop = 2, 1 end,
  auriculate = function(s) s.kb = 1.5 end,
  oblique = function(s) s.kb, s.obl = 1.8, 0.08 end,
}
local LSHAPE = {
  linear = function(s) s.vm, s.kb = 0.45, max(s.kb, 2.2) end,
  lanceolate = function(s) s.vm = 0.3 end,
  elliptic = function(s) s.vm = 0.5 end,
  ovate = function(s) s.vm = 0.3 end,
  obovate = function(s) s.vm = 0.68 end,
  oblong = function(s) s.vm, s.kb = 0.5, max(s.kb, 2.4) end,
  orbicular = function(s) s.vm, s.kb, s.round = 0.5, 2, true end,
  cordate = function(s) s.vm = 0.3 end,
  reniform = function(s) s.vm, s.kb, s.drop, s.round = 0.42, 2, 0.8, true end,
  deltoid = function(s) s.vm, s.kb = 0.1, 3.5 end,
  sagittate = function(s) s.vm, s.kb, s.drop = 0.32, 1.8, 2 end,
  hastate = function(s) s.vm, s.kb, s.hl, s.drop = 0.38, 1.7, 1.05, 0.2 end,
}
local LAPEX = {
  acute = function(s) s.ka = 1.25 end,
  acuminate = function(s) s.ka, s.acu = 1.5, true end,
  obtuse = function(s) s.ka = 2 end,
  rounded = function(s) s.ka = 2.6 end,
  emarginate = function(s) s.ka, s.tip = 2.6, -1 end,
  mucronate = function(s) s.ka, s.tip = 2.3, 1 end,
}
local function sshape(leaflet)
  local s = { W = max(0.035, min(0.6, 0.5 / S.leafaspect)), vm = 0.35, drop = 0, hl = 0, obl = 0, acu = false, tip = 0,
    round = false }
  LBASE[S.leafbase](s) LSHAPE[S.leafshape](s) LAPEX[S.leafapex](s)
  if s.round then s.ka, s.acu = max(s.ka, 2.2), false end
  if not leaflet and S.attachment == "clasping" and s.drop == 0 then s.drop, s.kb = 0.7, max(s.kb, 2) end
  return s
end
-- \flo@shw: the blade's half-width at v along the spine, and the drop below it
local function shw(s, v)
  local h, dy
  if v < s.vm then
    local t = (s.vm - v) / s.vm
    h = t > 0.9995 and 0 or s.W * (1 - t ^ s.kb) ^ (1 / s.kb)
    dy = s.drop * s.W * sin(180 * (1 - t)) * t
  else
    local t = (v - s.vm) / (1 - s.vm)
    h = t > 0.9995 and 0 or s.W * (1 - t ^ s.ka) ^ (1 / s.ka)
    if s.acu and t > 0.2 then
      t = min(1, (t - 0.2) / 0.65)
      h = h * (1 - 0.7 * t * t * (3 - 2 * t))
    end
    dy = 0
  end
  if s.hl > 0 then
    local t = max(0, 1 - abs(v - 0.11) / 0.11)
    if t > 0 then h = max(h, s.hl * s.W * t) end
  end
  return h, dy
end
-- \flo@smargin: the body's margin samples { v, profile, left scale, right scale }, and
-- the teeth's depth and the outline's tension
local LMARGIN = {
  undulate = function(m) m.k = 1 end,
  serrate = function(m) m.k, m.tk, m.td = 2, 0.5, 1.2 end,
  doubly = function(m) m.k, m.tk, m.td = 2, 0.5, 1.3 end,
  dentate = function(m) m.k, m.tk, m.td = 2, 0.5, 1.3 end,
  crenate = function(m) m.k, m.td = 2, 1.5 end,
}
local TOOTH = {                   -- each point's phase (plus the tooth's jitter where true) and profile
  serrate = { { 0, 0 }, { 0.75, 1, true } },
  doubly = { { 0, 0 }, { 0.38, 0.55 }, { 0.52, 0.3 }, { 0.82, 1, true } },
  dentate = { { 0, 0 }, { 0.5, 1, true } },
  crenate = { { 0, 0 }, { 0.2, 0.72 }, { 0.5, 1, true }, { 0.8, 0.72 } },
}
local function smargin(s, depth, bn, u, over)
  local m = { k = 0, tk = 0.8, dep = 0, td = 1 }
  local name = S.leafmargin
  if over then name = over end
  if depth <= 0 and LMARGIN[name] then LMARGIN[name](m) end
  if m.k == 2 and S.teeth < 1 then m.k, m.tk = 0, 0.8 end    -- a toothed margin with no teeth: smooth
  local ms = {}
  if m.k < 2 then
    local n
    if m.k == 1 then n, m.dep = int(3, 5), (0.05 + S.dissection) * s.W * 2.4 end
    for k = 0, bn do
      local v = 0.5 - 0.5 * cos(180 * k / bn)
      ms[#ms + 1] = { v, m.k == 1 and 0.5 + 0.5 * sin(360 * n * v) or 1, 1, 1 }
    end
  else
    if over then m.dep = min(0.25, S.dissection) * s.W * m.td
    else m.dep = S.dissection * s.W * m.td end
    local tn = floor(min(S.teeth, max(4, floor(4 + 8 * u + 0.5))))
    local tw = 0.81 / tn
    for k = 0, 4 do ms[#ms + 1] = { 0.12 * (1 - cos(90 * k / 5)), 1, 1, 1 } end
    for k = 1, tn do
      local jl, jr, jp = range(0.65, 1.35), range(0.65, 1.35), sym(0.06)
      local vo = 0.12 + tw * (k - 1)
      for _, p in ipairs(TOOTH[name]) do ms[#ms + 1] = { vo + tw * (p[1] + (p[3] and jp or 0)), p[2], jl, jr } end
    end
    ms[#ms + 1] = { 0.93, 0, 1, 1 }
    for k = 1, 5 do ms[#ms + 1] = { 0.93 + 0.07 * sin(90 * k / 5), 1, 1, 1 } end
  end
  return ms, m
end
-- \flo@sblade: the blade along +y, length 1, in flora.leaf's hand with the species' outline.
-- o: bend (nil draws one), lobes, depth, tone (nil draws one), u (cm per unit), leaflet,
-- over (a leaflet's margin in place of the species')
local function sblade(s, o)
  local lbend = o.bend or sym(0.25)
  local lobes, depth = o.lobes, o.depth
  local lt = o.tone or range(0.26, 0.38)
  local u = o.u
  local fine, bn = false, 24
  if u > K(0.6) then fine, bn = true, 36 end
  -- a leaflet's level of detail: under 1.4 cm a coarser edge, no mottle or side veins,
  -- under 4 mm a flat shape
  local lod = 2
  if o.leaflet and u < K(1.4) then
    lod, fine, bn = 1, false, 16
    if u < K(0.4) then lod, bn = 0, 12 end
  end
  local bodyw, ent = 1, true
  if depth > 0 then bodyw, ent = 1 - 0.75 * depth, false end
  -- the spine, along a curled tip's circle beyond hv
  local hk, hv, hl, hth, hp, ht, hn, hr = false
  local function spine0(v) return lbend * v * v - 0.3 * lbend * v, v end
  local function spine(v)
    if hk and v > hv then
      local a = (v - hv) / hl * hth
      local sa, ca = sin(a), 1 - cos(a)
      return hp[1] + hr * (sa * ht[1] + ca * hn[1]), hp[2] + hr * (sa * ht[2] + ca * hn[2])
    end
    return spine0(v)
  end
  local function tangent(v)
    local ax, ay = spine(max(0, v - 0.01))
    local bx, by = spine(min(1, v + 0.01))
    local dx, dy = bx - ax, by - ay
    local l = sqrt(dx * dx + dy * dy)
    return dx / l, dy / l
  end
  -- a curled tip, on an unlobed leaf drawn in full 8 mm long or more, hairless unless broad
  if lod == 2 and ent and (S.hairy == 0 or s.W >= K(0.15)) and u > K(0.8) then
    if chance(min(0.55, max(0, S.curl + 0.25))) then
      hl = range(0.25, 0.4)
      hv = 1 - hl
      local h = shw(s, hv)
      hth = min(range(120, 190), hl / (1.8 * max(0.01, h)) * 57.29578)
      if hth > 45 then
        hk = true
        local hs = lbend < 0 and -1 or 1
        hp = { spine0(hv) }
        local tx = 2 * lbend * hv - 0.3 * lbend
        local l = sqrt(tx * tx + 1)
        ht = { tx / l, 1 / l }
        hn = { hs * ht[2], -hs * ht[1] }
        hr = hl / hth * 57.29578
      end
    end
  end
  -- the body along its margin's samples
  local ms, m = smargin(s, depth, bn, u, o.over)
  local mn = #ms
  local pts = {}
  for i = 1, mn do pts[i] = { spine(ms[i][1]) } end
  local nr = normals(pts)
  local left, right = {}, {}
  for i = 1, mn do
    local v, sdd = ms[i][1], ms[i][2]
    local h, dy = shw(s, v)
    local cut = m.dep * min(1, h / (0.45 * s.W)) * (1 - sdd)
    -- a curled tip narrows as it turns edge-on, the more the species' leaves twist
    local hwf = 1
    if hk and v > hv then
      local t = min(1, (v - hv) / hl)
      hwf = 1 - (0.25 + 0.008 * S.twist) * min(1, hth / 150) * t * t * (3 - 2 * t)
    end
    local p, q = pts[i], nr[i]
    local e = max(0, h * bodyw - cut * ms[i][3]) * hwf
    left[i] = { p[1] + q[1] * e, p[2] + q[2] * e - dy }
    if s.obl > 0 then h, dy = shw(s, max(0, (v - s.obl) / (1 - s.obl))) end
    e = max(0, h * bodyw - cut * ms[i][4]) * hwf
    right[i] = { p[1] - q[1] * e, p[2] - q[2] * e - dy }
  end
  -- the tip: notched (emarginate) or a short point (mucronate)
  if s.tip < 0 then left[mn][2] = left[mn][2] - 0.25 * s.W end
  if s.tip > 0 then left[mn][2] = left[mn][2] + 0.04 end
  local outline = {}
  for i = 1, mn do outline[i] = left[i] end
  for i = mn - 1, 2, -1 do outline[#outline + 1] = right[i] end
  local shapes = { outline }
  local tks = { m.tk }
  -- a hairy leaf's fringe
  if S.hairy == 1 and fine then
    local on = normals(outline)
    local c = tone(0.1)
    for i = 1, #outline, 2 do
      local h = range(0.008, 0.02)
      local p, q = outline[i], on[i]
      moveto(p[1], p[2]) lineto(p[1] + q[1] * h, p[2] + q[2] * h)
    end
    stroke(c, 0.15, 0.55, false)
  end
  -- the lobes, grown out of the body along the spine
  local veins = {}
  if depth > 0 then
    local pal = S.leafmargin == "palmatelobed"
    local wf, nl = 1, lobes
    if pal then wf, nl = 1.3, floor(max(1, floor(lobes / 2))) end
    for kc = 0, 1 do
      local side = kc == 0 and -1 or 1
      for ka = 1, nl do
        local lv, L, ang, reach
        if pal then
          lv = 0.04 + 0.025 * ka
        else
          lv = 0.1 + 0.72 * (ka - (side > 0 and 0.25 or 0.75)) / lobes
        end
        local lx, ly = spine(lv)
        local tx, ty = tangent(lv)
        if pal then
          L = 0.8 * (1 - 0.3 * (ka - 1) / nl) * range(0.9, 1.1)
          ang = range(30, 40) + 45 * (ka - 1) / max(1, nl - 1)
        else
          reach = shw(s, min(0.95, lv + 0.08)) * range(0.95, 1.15)
          ang = range(38, 58)
        end
        local c, sn = cos(ang), sin(ang) * side
        local dx, dy = tx * c - ty * sn, tx * sn + ty * c
        if not pal then L = reach / sin(ang) * 0.95 end
        local lb = sym(0.25) - side * 0.15
        local function at(t) return lx + dx * L * t - dy * lb * L * t * t, ly + dy * L * t + dx * lb * L * t * t end
        local lp = {}
        for k = 0, 11 do lp[k + 1] = { at(k / 11) } end
        local ln = normals(lp)
        local ll, lr = {}, {}
        for k = 0, 11 do
          local v = k / 11
          local e = 0
          if v > 0.2 then
            local t = (v * 1.4 + ka) * 9
            e = 0.006 * (1 - abs(2 * (t - floor(t)) - 1)) * L * 4
          end
          local g = v > 0 and max(0, sin(180 * v ^ 0.62)) ^ 0.75 or 0
          local wa = max(0, L * range(0.26, 0.3) * wf * g + e)
          local wb = max(0, L * range(0.26, 0.3) * wf * g + e)
          local p, q = lp[k + 1], ln[k + 1]
          ll[k + 1] = { p[1] + q[1] * wa, p[2] + q[2] * wa }
          lr[k + 1] = { p[1] - q[1] * wb, p[2] - q[2] * wb }
        end
        local mx, my = at(0.45)
        local ex, ey = at(0.92)
        veins[#veins + 1] = { lx, ly, mx, my, ex, ey }
        local lo = {}
        for k = 1, 12 do lo[k] = ll[k] end
        for k = 11, 2, -1 do lo[#lo + 1] = lr[k] end
        shapes[#shapes + 1] = lo
        tks[#tks + 1] = 0.8
      end
    end
  end
  local function trace() for i, sh in ipairs(shapes) do smooth(sh, true, tks[i]) end end
  trace() fill(tone(lt), 1)
  if lod == 0 then return end
  trace() ink.clip()
  -- the darker half
  local sd = chance(0.5) and 1 or -1
  for k = 0, 30 do
    local x, y = spine(k / 30)
    if k == 0 then moveto(x, y) else lineto(x, y) end
  end
  lineto(sd * 2, 1) lineto(sd * 2, -0.3) lineto(0, -0.3) close()
  fill(tone(0.12), 0.28, true)
  if lod == 2 then
    if fine then mottle(-s.W * 2.5, -0.2, s.W * 2.5, 1, 13, 0.11, lt - 0.2, lt + 0.22, 12, true)
    else mottle(-s.W * 2.5, -0.2, s.W * 2.5, 1, 6, 0.11, lt - 0.2, lt + 0.22, 0, true) end
  end
  trace() stroke(tone(0.06), 1.3 * u, 0.22, true)
  -- midrib and veins in white
  local mid = {}
  for k = 0, 10 do mid[k + 1] = { spine(k / 10) } end
  streak(mid, 0.03, 0.003, WHITE, 0.78)
  for _, v in ipairs(veins) do
    local op = range(0.5, 0.75)
    taper(v[1], v[2], v[3], v[4], v[3] + (v[5] - v[3]) * 0.5, v[4] + (v[6] - v[4]) * 0.5, v[5], v[6], 0.017, 0.002)
    fill(WHITE, op)
  end
  -- veins: pinnate, arcuate (curving up toward the tip) or palmate (two each side from the base)
  if #veins == 0 and lod == 2 then
    local vn = S.venation
    local ka0, arc = 1, vn == "arcuate"
    if vn == "palmate" then
      ka0 = 5
      local lx, ly = spine(0.03)
      local tx, ty = tangent(0.03)
      for kc = 0, 3 do
        local side = kc < 2 and -1 or 1
        local t, bl = 32, 1.5
        if kc % 2 == 1 then t, bl = 64, 1.1 end
        local ang = range(t - 5, t + 5)
        local c, sn = cos(ang), sin(ang) * side
        local dx, dy = tx * c - ty * sn, tx * sn + ty * c
        local L = s.W * bl * range(0.85, 1)
        local op = range(0.45, 0.7)
        taper(lx, ly, lx + dx * L * 0.4, ly + dy * L * 0.4, lx + dx * L * 0.75 + tx * L * 0.25, ly + dy * L * 0.75 + ty * L * 0.25,
          lx + dx * L * 0.9 + tx * L * 0.6, ly + dy * L * 0.9 + ty * L * 0.6, 0.016, 0.002)
        fill(WHITE, op)
      end
    end
    for ka = ka0, 9 do
      local v = (ka - 0.3) / 10 * 0.9
      local sg = ka % 2 == 1 and -1 or 1
      local lx, ly = spine(v)
      local tx, ty = tangent(v)
      local nx, ny = -ty * sg, tx * sg
      local L = shw(s, v) * bodyw * range(0.75, 0.95)
      local a, b, c, d, e, f
      if arc then
        a, b = lx + nx * L * 0.3 + tx * L * 0.12, ly + ny * L * 0.3 + ty * L * 0.12
        c, d = lx + nx * L * 0.62 + tx * L * 0.55, ly + ny * L * 0.62 + ty * L * 0.55
        e, f = lx + nx * L * 0.72 + tx * L * 1.15, ly + ny * L * 0.72 + ty * L * 1.15
      else
        a, b = lx + nx * L * 0.4, ly + ny * L * 0.4
        c, d = lx + nx * L * 0.75 + tx * L * 0.3, ly + ny * L * 0.75 + ty * L * 0.3
        e, f = lx + nx * L * 0.95 + tx * L * 0.6, ly + ny * L * 0.95 + ty * L * 0.6
      end
      local op = range(0.45, 0.7)
      taper(lx, ly, a, b, c, d, e, f, 0.014, 0.002) fill(WHITE, op)
    end
  end
  ink.unclip()
  if ent then smooth(outline, true, m.tk) stroke(tone(0.05), 0.18, 0.4, false) end
  -- the curl's underside: the blade from just past its middle to the tip, paler
  if hk and hth > 95 then
    local hva = hv + 0.45 * hl
    local ka = 0
    for i = 1, mn do if ka == 0 and ms[i][1] > hva then ka = i end end
    if ka > 1 and ka < mn then
      local under = {}
      for i = ka, mn do under[#under + 1] = left[i] end
      for i = mn - 1, ka, -1 do under[#under + 1] = right[i] end
      smooth(under, true, m.tk) fill(tone(min(0.64, lt + 0.26)), 1)
      smooth(under, true, m.tk) ink.clip()
      local sp = {}
      for k = 0, 10 do sp[k + 1] = { spine(hva + (1 - hva) * k / 10) } end
      streak(sp, 0.02, 0.003, WHITE, 0.6)
      smooth(under, true, m.tk) stroke(tone(0.06), 1.3 * u, 0.3, true)
      ink.unclip()
    end
  end
end
-- \flo@sleaf: stipules, a petiolate leaf's stalk, then the blade. o: bend, lobes, depth, u
local function sleaf(o)
  local s = sshape(false)
  local lsc = min(1, 0.3 / s.W)
  if S.leafmargin == "palmatelobed" then lsc = min(1, 0.3 / max(s.W, 0.55)) end
  local u = o.u
  ink.push()
  if lsc < 1 then ink.scale(lsc) u = u * lsc end
  if S.stipules ~= "none" and u > K(0.8) then
    for kc = 0, 1 do
      local t = range(40, 65)
      if kc == 1 then t = -t end
      local ts = range(0.1, 0.15)
      ink.push() ink.translate(0, 0.01) ink.rotate(t) ink.scale(ts)
      M.leaflet({ W = 0.3, tone = 0.3, u = u * ts })
      ink.pop()
    end
  end
  local lpf = S.attachment == "petiolate" and range(0.1, 0.2) or 0
  if lpf > 0 then
    local c = tone(0.16)
    taper(0, 0, 0, lpf / 3, 0, 2 * lpf / 3, 0, lpf + 0.01, 0.024, 0.014) fill(c, 1)
  end
  ink.push()
  if lpf > 0 then ink.translate(0, lpf) ink.scale(1 - lpf) u = u * (1 - lpf) end
  sblade(s, { bend = o.bend, lobes = o.lobes, depth = o.depth, u = u })
  ink.pop()
  ink.pop()
end
-- \flo@sleaflet: a leaflet in the species' hand, no stalk or stipules, two or three lobes a
-- side; +y, length 1; lt its tone, u its length on the page in cm
local LOBED = { lobed = true, pinnatifid = true, palmatelobed = true }
local function sleaflet(lt, u)
  local depth, lobes, over = 0, 3, nil
  local lm = S.leafmargin
  if LOBED[lm] then
    -- under 7 mm, scallops instead of lobes (a pinnatifid leaflet's: deep teeth)
    if u < K(0.7) then over = lm == "pinnatifid" and "dentate" or "crenate"
    else depth, lobes = min(0.85, max(0.3, S.dissection)), floor(max(2, min(3, S.teeth))) end
  end
  local s = sshape(true)
  local lsc = min(1, 0.3 / s.W)
  if lm == "palmatelobed" then lsc = min(1, 0.3 / max(s.W, 0.55)) end
  ink.push()
  if lsc < 1 then ink.scale(lsc) u = u * lsc end
  sblade(s, { tone = lt, depth = depth, lobes = lobes, u = u, leaflet = true, over = over })
  ink.pop()
end

-- ---------------------------------------------------------------- compound leaves
-- the rachis and stalks are stroked with the pen compoundleaf sets: RP.c, RP.w (bp)
local RP = {}
local function rachis(y0, y1) moveto(0, y0) lineto(0, y1) stroke(RP.c, RP.w, 1, false) end
-- a leaflet's length on the page in cm, k of a leaf lpt pt long; for a pic's leaf (RP.pt, its
-- length in sp as TeX holds it) as pgfmath finds it: a 1 cm leaf's leaflets of 0.4 are 4 mm,
-- the threshold of their level of detail, and TeX's product can land either side of it.
-- pgfmath's lpt*k/28.45276 (a negative k, as TeX expands it, takes in the quotient)
function RP.u(lpt, k)
  if RP.pt then
    local a, b = floor(lpt * 65536 + 0.5), floor(k * 65536 + 0.5)
    if b < 0 then return pm.mulf(a, -pm.div(-b, CM)) / 65536 end
    return pm.div(pm.mulf(a, b), CM) / 65536
  end
  return lpt * k / 28.45276
end
-- \flo@pinnate: pairs sized to peak mid-rachis, then a terminal leaflet or a tendril;
-- lpt the leaf's length in pt, n pairs, pl the longest leaflet, lt the tone, tend tendrils
local function pinnate(lpt, n, pl, lt, tend)
  local plw = max(0.035, min(0.6, 0.5 / S.leafaspect))
  local pend, gap, pmax, pterm
  if tend then
    pend, gap = 0.92, 0.68 / max(1, n - 1)
    pmax = min(pl, 0.45 * gap / plw)
  else
    pmax = min(pl, 0.3465 / (max(1, n - 1) * plw + 0.495))
    pterm = min(pl, 1.1 * pmax)
    pend = 1 - pterm
    gap = (pend - 0.23) / max(1, n - 1)
  end
  rachis(0, pend)
  for j = 1, n do
    local v = 0.2 + gap * (j - 1)
    local ll = pmax * (0.72 + 0.28 * sin(180 * (j - 0.5) / n))
    for k = 0, 1 do
      local ang = range(50, 68) - 14 * (j - 1) / n
      local dv = sym(0.015)
      ink.push() ink.translate(0, v + dv) ink.rotate(k == 0 and ang or -ang) ink.scale(ll)
      sleaflet(range(lt - 0.07, lt + 0.07), RP.u(lpt, ll))
      ink.pop()
    end
  end
  if tend then
    local c, w = tone(0.22), 0.25 * 72 / 72.27
    local tn = chance(0.4) and 2 or 1
    for q = 1, tn do
      ink.push() ink.translate(0, pend)
      local t
      if tn == 1 then t = sym(15)
      else
        t = range(10, 30)
        if q == 2 then t = -t end
      end
      ink.rotate(t)
      smooth(vcurl(range(0.35, 0.55)), false) stroke(c, w, 1, false)
      ink.pop()
    end
  else
    ink.push() ink.translate(0, pend) ink.scale(pterm)
    sleaflet(lt, RP.u(lpt, pterm))
    ink.pop()
  end
end
-- \flo@bipinnate: pinnae that are themselves odd-pinnate, and a terminal pinna
local function bipinnate(lpt, lt)
  local bk = floor(min(3, max(2, (S.leaflets + 1) / 3)))
  local bl = floor(min(7, max(3, S.leaflets)))
  lt = lt + 0.06
  rachis(0, 0.7)
  for m = 1, bk do
    local v = 0.16 + 0.44 * (m - 1) / max(1, bk - 1)
    local s = 0.48 * (1 - 0.5 * (m - 1) / bk)
    local n = floor(max(2, bl - m + 1))
    for p = 0, 1 do
      local ang = range(48, 64) - 12 * (m - 1) / bk
      ink.push() ink.translate(0, v) ink.rotate(p == 0 and ang or -ang) ink.scale(s)
      pinnate(lpt * s, n, 0.4, lt, false)
      ink.pop()
    end
  end
  ink.push() ink.translate(0, 0.66) ink.scale(0.34)
  pinnate(lpt * 0.34, floor(max(2, bl - bk)), 0.4, lt, false)
  ink.pop()
end
-- \flo@palmate: leaflets from a petiole's tip, the center one the largest, from the outside in
local function palmate(lpt, n, lt)
  local pp = range(0.3, 0.38)
  rachis(0, pp)
  local hs = min(135, 20 * n - 10)
  for q = 1, n do
    local j = q % 2 == 1 and (q + 1) // 2 or n + 1 - q // 2
    local f = (j - (n + 1) / 2) / max(1, (n - 1) / 2)
    local ang = -f * hs + sym(6)
    local ll = (1 - pp) * (1 - 0.45 * f * f)
    ink.push() ink.translate(0, pp) ink.rotate(ang) ink.scale(ll)
    sleaflet(range(lt - 0.06, lt + 0.06), lpt * ll / 28.45276)
    ink.pop()
  end
end
-- \flo@trifoliate: two side leaflets and a larger end one on a short stalk
local function trifoliate(lpt, lt)
  local pp, ps = range(0.3, 0.4), range(0, 0.1)
  rachis(0, pp + ps)
  for n = 0, 2 do
    local ang, ll, pv
    if n < 2 then
      ang = range(48, 68)
      if n == 1 then ang = -ang end
      ll, pv = 0.8 * (1 - pp), pp
    else ang, ll, pv = 0, 1 - pp - ps, pp + ps end
    ink.push() ink.translate(0, pv) ink.rotate(ang) ink.scale(ll)
    sleaflet(range(lt - 0.06, lt + 0.06), lpt * ll / 28.45276)
    ink.pop()
  end
end
-- the compound branch of \flo@leaf: stipules, the pen for rachis and stalks, the leaf
local function compoundleaf(lpt)
  local lt = range(0.24, 0.4)
  if S.stipules ~= "none" then
    local a, b, W = 0.05, 0.08, 0.3
    if S.stipules == "leafy" then a, b, W = 0.1, 0.16, 0.36 end
    for kc = 0, 1 do
      local t = range(55, 80)
      if kc == 1 then t = -t end
      local ts = range(a, b)
      ink.push() ink.translate(0, 0.01) ink.rotate(t) ink.scale(ts)
      M.leaflet({ W = W, tone = 0.3, u = lpt * ts / 28.45276 })
      ink.pop()
    end
  end
  RP.c, RP.w = tone(0.07), max(0.25, 0.006 * lpt)
  local cp = S.compound
  if cp == "pinnate" then pinnate(lpt, floor(min(7, S.leaflets)), 0.4, lt, S.tendrils == 1) end
  if cp == "bipinnate" then bipinnate(lpt, lt) end
  if cp == "palmate" then palmate(lpt, S.leaflets, lt) end
  if cp == "trifoliate" then trifoliate(lpt, lt) end
end

-- ================================================================ M8f5: ferns
-- the rachis (\flo@feribbon) is drawn by ribbon, above
-- the frond's outline (\flo@feol@): bm the widest pinna's place, lo the foot, kx the tip's
-- exponent, wr the longest pinna; the pinna's tip (\flo@feap@); its teeth (\flo@fem@)
local FOL = { lanceolate = { 0.3, 0.45, 0.8, 0.26 }, oblong = { 0.12, 0.7, 0.35, 0.2 },
  ovate = { 0.2, 0.75, 0.9, 0.32 }, deltoid = { 0.04, 0.9, 1.3, 0.42 } }
local FAP = { acute = { 1.6, 0.9 }, obtuse = { 3, 0.6 }, rounded = { 5, 0.45 } }
local FMG = { entire = 0, crenate = 0.2, serrate = 0.26 }
-- the shape's left half, its first n segments, then back down the midrib (\flo@fehalf)
local function fehalf(pts, n, fb, tk)
  local k = (tk or 1) / 6
  local m = #pts
  local function at(i) return pts[(i - 1) % m + 1] end
  moveto(pts[1][1], pts[1][2])
  for i = 1, n do
    local p0, p1, p2, p3 = at(i - 1), at(i), at(i + 1), at(i + 2)
    curveto(p1[1] + (p2[1] - p0[1]) * k, p1[2] + (p2[2] - p0[2]) * k, p2[1] - (p3[1] - p1[1]) * k, p2[2] - (p3[2] - p1[2]) * k,
      p2[1], p2[2])
  end
  for i = 1, 8 do local v = 1 - i / 8 lineto(fb * v * v, v) end
  close()
end
-- the frond being drawn: its pinna's numbers (\flo@fe...); FE.tex while a pic draws (M.pic)
local FE = {}
-- where along the frond its pinnae stand, and how wide each may be, come from pow(x, 0.85),
-- the width from the difference of two of them. pgfmath's pow is good to about 1e-4, and in
-- that difference its error grows fifty times, so a pinna drawn from doubles would be up to
-- 1% wider or narrower than the library's (0.6 bp on a large frond). These two use
-- pgfmath's own pow; everything else is a double. A pic's frond (FE.tex), 10 cm long where
-- the margin's is 4, also takes its spine's 32 steps as TeX truncates them (32 sp short at
-- the tip, 0.2 bp) and its pinnae's lengths from pgfmath's pow (2% off at some numbers)
local function ppow(x, y) return pm.pow(floor(x * 65536 + 0.5), floor(y * 65536 + 0.5)) / 65536 end
-- \flo@fesori: sori under a pinna (its outline pts)
local function fesori(pts)
  local c, sr = accent(-0.35), S.sori
  if sr == "marginal" then smooth(pts, true) stroke(c, min(0.5, 0.3 * FE.pcm), 0.75, false) end
  if sr == "dots" then
    local n = floor(min(7, 2 + 4 * FE.pcm))
    for k = 1, n do
      local v = 0.14 + 0.66 * (k - 1) / max(1, n - 1)
      local x, w, r = FE.fb * v * v, 0.5 * FE.W * (1 - 0.5 * v), 0.3 * FE.W * (1 - 0.4 * v)
      circle(x - w, v, r) circle(x + w, v, r)
    end
    fill(c, 0.9)
  end
  if sr == "lines" then
    local n = floor(min(6, 2 + 3 * FE.pcm))
    for k = 1, n do
      local v = 0.12 + 0.62 * (k - 1) / max(1, n - 1)
      local x, w = FE.fb * v * v, 0.7 * FE.W * (1 - 0.5 * v)
      moveto(x - 0.15 * w, v) lineto(x - w, v + 0.6 * w) moveto(x + 0.15 * w, v) lineto(x + w, v + 0.6 * w)
    end
    stroke(c, 0.45, 0.85, false)
  end
end
-- \flo@fepinna: a pinna along +y, length 1, FE.W its half-width
local function fepinna()
  FE.fb = range(0.05, 0.16)
  local t = FE.tone + sym(0.03)
  local n, tk = 10, 0
  if FE.pcm < 0.3 then n = 6 end
  if FE.pcm > 0.6 and FE.td > 0 then tk, n = FE.td, 2 * floor(min(9, 3 + 4 * FE.pcm)) end
  local cs = 0
  if tk > 0 and S.leafmargin == "serrate" then cs = 0.35 / n end
  local spine = {}
  for k = 0, n do
    local v = k / n
    if k % 2 == 1 and k < n then v = v + cs end
    spine[k + 1] = { FE.fb * v * v, v }
  end
  local pts = blade(spine, function(i)
    local v = spine[i][2]
    if v > 0 and v < 1 then
      local w = FE.W * min(1, v / 0.12) ^ 0.45 * max(0.0001, 1 - v ^ FE.kp) ^ FE.kq * (1 - FE.nar * v)
      if (i - 1) % 2 == 0 then w = w * (1 - tk) end
      return w
    end
    return 0
  end)
  smooth(pts, true) fill(tone(t), 1)
  if FE.pcm > 0.2 then fehalf(pts, n, FE.fb) fill(tone(0.08), 0.28, true) end
  if FE.pcm > 0.35 then
    taper(0, 0, 0, 0.2733, FE.fb * 0.82 * 0.82 / 3, 0.5467, FE.fb * 0.82 * 0.82, 0.82, 0.03, 0.004) fill(WHITE, 0.6)
  end
  if FE.under and FE.pcm > 0.35 then fesori(pts) end
end
-- \flo@fecomb: a pinna cut into pinnules, along +y, length 1
local function fecomb()
  FE.fb = range(0.04, 0.12)
  local fb = FE.fb
  local t = FE.tone + sym(0.03)
  local n = floor(max(3, min(9, FE.pcm / 0.15)))
  local d = 0.9 / n
  local s = 0.35 * d
  local L = { { 0, 0 } }                         -- the left side, up to the tip
  local function cpt(x, v) L[#L + 1] = { fb * v * v - x, v } end
  for k = 1, n do
    local v = 0.05 + d * (k - 0.5)
    local l = FE.W * (1 - 0.72 * v ^ 1.5) * range(0.9, 1.06)
    if FE.div > 2 then
      cpt(0.12 * FE.W, v - 0.5 * d) cpt(0.5 * l, v - 0.4 * d + 0.3 * s)
      cpt(0.62 * l, v - 0.2 * d + 0.55 * s) cpt(l, v + s)
      cpt(0.66 * l, v + 0.24 * d + 0.7 * s) cpt(0.52 * l, v + 0.38 * d + 0.5 * s)
    else
      cpt(0.12 * FE.W, v - 0.5 * d) cpt(0.58 * l, v - 0.36 * d + 0.5 * s)
      cpt(l, v + s) cpt(0.62 * l, v + 0.36 * d + 0.6 * s)
    end
  end
  local h = #L
  local pts = {}
  for i = 1, h do pts[i] = L[i] end
  pts[h + 1] = { fb, 1 }
  for k = 2, h do                                -- the right side, mirrored across the midrib
    local p = L[h + 2 - k]
    pts[h + k] = { 2 * fb * p[2] * p[2] - p[1], p[2] }
  end
  smooth(pts, true) fill(tone(t), 1)
  fehalf(pts, h, fb) fill(tone(0.08), 0.28, true)
  taper(0, 0, 0, 0.3, fb * 0.9 * 0.9 / 3, 0.6, fb * 0.9 * 0.9, 0.9, 0.025, 0.004) fill(WHITE, 0.55)
  if FE.under then
    local c, sr = accent(-0.35), S.sori
    if sr == "marginal" then smooth(pts, true) stroke(c, min(0.4, 0.22 * FE.pcm), 0.75, false)
    else
      for k = 1, n do
        local v = 0.05 + d * (k - 0.5)
        local l = FE.W * (1 - 0.72 * v ^ 1.5)
        local ya = v + 0.35 * s
        local x = fb * ya * ya
        if sr == "dots" then
          local r = 0.16 * min(d, l)
          circle(x - 0.45 * l, ya, r) circle(x + 0.45 * l, ya, r)
        elseif sr == "lines" then
          local yb = ya + 0.6 * s
          moveto(x - 0.45 * l, ya) lineto(x - 0.7 * l, yb) moveto(x + 0.45 * l, ya) lineto(x + 0.7 * l, yb)
        end
      end
      if sr == "dots" then fill(c, 0.9) elseif sr == "lines" then stroke(c, 0.45, 0.85, false) else ink.discard() end
    end
  end
end
-- \flo@febeads: a fertile pinna, a row of beads along +y, length 1
local function febeads()
  local fb = range(0.02, 0.08)
  local n = floor(max(2, min(7, 2 + 5 * FE.pcm)))
  local c = tone(0.12)
  moveto(0, 0) curveto(0, 0.3, fb * 0.9 * 0.9 / 3, 0.6, fb * 0.9 * 0.9, 0.9) stroke(c, 0.3, 0.8, false)
  local beads = {}
  for k = 1, n do
    local v = 0.95 * (k - 0.3) / n
    beads[k] = { fb * v * v, v, 0.6 / n * (1 - 0.4 * v) * range(0.85, 1.1) }
  end
  for _, b in ipairs(beads) do circle(b[1], b[2], b[3]) end
  fill(tone(0.32 + sym(0.04)), 1)
  for _, b in ipairs(beads) do circle(b[1], b[2], b[3]) end
  stroke(tone(0.08), 0.25, 0.45, true)
end
-- \flo@frond: a fern's frond along +y, length 1; cm its length on the page. o: unroll (0..1),
-- arch (degrees; nil draws one), fertile, under (its underside shown)
function M.frond(cm, o)
  local arch = o.arch
  if arch == nil then arch = sym(1) * (20 + 70 * S.curl) end
  local un = o.unroll
  if un >= K(0.97) then un = 1 end
  local was = pen.accent
  if o.fertile then pen.accent = true end
  local st, np, div = S.stipe, S.pinnae, S.division
  local ol, ap = FOL[S.outline], FAP[S.leafapex]
  FE.td, FE.kp, FE.kq, FE.div = FMG[S.leafmargin], ap[1], ap[2], div
  FE.nar = S.leafshape == "lanceolate" and 0.3 or 0
  FE.under = o.under
  local fo = st + (1 - st) * (un < 1 and 0.3 + 0.7 * un or 1)
  -- the spine, arching; then, unrolling, the crozier
  local P = { { 0, 0 } }
  local x, y = 0, 0
  for k = 1, 32 do
    local a = 90 + arch * (fo * (k - 0.5) / 32) ^ 1.5
    if FE.tex then                               -- a pic's frond: each step as TeX truncates it
      local step = floor(floor(fo * 65536 + 0.5) / 32)
      local dx, dy = step * cos(a), step * sin(a)
      x = x + (dx < 0 and -floor(-dx) or floor(dx)) / 65536
      y = y + (dy < 0 and -floor(-dy) or floor(dy)) / 65536
    else x, y = x + fo / 32 * cos(a), y + fo / 32 * sin(a) end
    P[k + 1] = { x, y }
  end
  local sg, cr = 1, 0
  if un < 1 then
    if arch < 0 then sg = -1 end
    local cT = 100 + 440 * (1 - un)
    cr = 0.65 * (1 - fo) / (cT * 0.0174533 * 0.541)
    local a = 90 + arch * fo ^ 1.5
    for k = 1, 24 do
      a = a + sg * cT / 24
      local t = cr * 0.25 ^ ((k - 0.5) / 24) * cT / 24 * 0.0174533
      x, y = x + t * cos(a), y + t * sin(a)
      P[k + 33] = { x, y }
    end
  end
  local function at(t)                           -- where a pinna leaves the rachis
    local i = floor(t)
    local f = t - i
    local a, b = P[i + 1], P[i + 2]
    return a[1] + (b[1] - a[1]) * f, a[2] + (b[2] - a[2]) * f
  end
  local pm_ = (1 - st) * ol[4] * range(0.9, 1.1)
  if o.fertile then pm_ = 0.45 * pm_ end
  FE.tone = range(0.24, 0.34)
  if o.under then FE.tone = FE.tone + 0.14 end
  for g = 1, np do
    for e = 0, 1 do
      local b = 0.93 * ppow((g - 0.5 + 0.42 * e) / np, 0.85)
      local s = st + (1 - st) * b
      local ee
      if b < ol[1] then ee = ol[2] + (1 - ol[2]) * sin(90 * b / ol[1])
      else
        ee = max(0.0001, cos(90 * (b - ol[1]) / (1 - ol[1])))
        if FE.tex then ee = ppow(ee, ol[3]) else ee = ee ^ ol[3] end
      end
      if un < 1 then ee = ee * max(0, min(1, 0.3 + 0.7 * (fo - s) / 0.15)) end
      local l = pm_ * ee * range(0.92, 1.06)
      local al = 70 - 30 * b + sym(4)
      FE.pcm = l * cm
      if s < fo - 0.004 and FE.pcm > 0.08 then
        local t = 0.93 * (1 - st) * (ppow((g + 0.5) / np, 0.85) - ppow((g - 0.5) / np, 0.85))
        FE.W = min(0.22, 0.34 * t * sin(al) / l)
        local px, py = at(s / fo * 32)
        local a = 90 + arch * s ^ 1.5
        ink.push() ink.translate(px, py)
        ink.rotate(e == 0 and a + al - 90 or a - al - 90)
        if e == 1 then xscale(-1) end
        ink.scale(l)
        if o.fertile then febeads()
        elseif div > 1 then if FE.pcm > 0.45 then fecomb() else fepinna() end
        else fepinna() end
        ink.pop()
      end
    end
  end
  if un >= 1 and not o.fertile then              -- the pinna-like tip
    local s, l = st + 0.91 * (1 - st), 0.1 * (1 - st)
    local px, py = at(s * 32)
    local a = 90 + arch * s ^ 1.5
    FE.W, FE.pcm = 0.13, l * cm
    ink.push() ink.translate(px, py) ink.rotate(a - 90) ink.scale(l) fepinna() ink.pop()
  end
  local spine = {}
  for k = 1, 33 do spine[k] = P[k] end
  ribbon(spine, 0.012, 0.0025 + 0.006 * (1 - un), tone(0.1))
  if S.hairy == 1 then                           -- scales on the stipe
    for k = 2, 32 do
      if fo * (k - 1) / 32 < 0.9 * st and k % 2 == 0 then
        local dx, dy = P[k + 1][1] - P[k][1], P[k + 1][2] - P[k][2]
        local nx, ny = nrm(dx, dy)
        local t = k % 4 == 0 and 1 or -1
        local ts = range(0.012, 0.02)
        local xa, ya = P[k][1] + t * nx * 0.005, P[k][2] + t * ny * 0.005
        moveto(xa, ya) lineto(xa + t * nx * ts + dx * 12 * ts, ya + t * ny * ts + dy * 12 * ts)
      end
    end
    stroke(accent(-0.3), 0.35, 0.85, false)
  end
  if un < 1 then                                 -- the crozier: ribbon, scalloped lobes, a pale line
    local c = {}
    for k = 1, 25 do c[k] = P[k + 32] end
    local nr = normals(c)
    local lobes = {}
    for k = 2, 24 do
      local r = cr * 0.25 ^ ((k - 1) / 24)
      lobes[#lobes + 1] = { c[k][1] - sg * nr[k][1] * 0.33 * r, c[k][2] - sg * nr[k][2] * 0.33 * r, (k % 2 == 1 and 0.15 or 0.21) * r }
    end
    ribbon(c, 0.56 * cr, 0.16 * cr, tone(0.27))
    for _, b in ipairs(lobes) do circle(b[1], b[2], b[3]) end
    fill(tone(0.2), 1)
    for _, b in ipairs(lobes) do circle(b[1], b[2], b[3]) end
    stroke(tone(0.06), 0.25, 0.35, true)
    local pale = {}
    for k = 1, 22 do pale[k] = P[k + 32] end
    streak(pale, 0.14 * cr, 0.002, WHITE, 0.5)
  end
  pen.accent = was
end

-- ---------------------------------------------------------------- the leaf
-- the leaf's size on the page in cm (\flo@vl@u) as TeX finds it: its length (cm, held in sp)
-- times 28.45276 and divided back, both in pgfmath, which can lose a scaled point. Only this
-- one number is TeX's own: it decides a leaf's teeth and detail, and the layout gives some
-- leaves round lengths (19/16 cm) that sit exactly on those thresholds
local function leafu(len)
  return pm.div(pm.mulf(floor(len * 65536 + 0.5), CM), CM) / 65536    -- (len*28.45276)/28.45276
end
-- \flo@leaf: the species' leaf along +y, length 1; len its length in cm, bend its bend, from a
-- sheath's top where it has one (sheath: its direction from the leaf's, degrees, and width);
-- pt, the pic's: the length in pt (sp) instead, as \flo@leaf takes it there
function M.leaf(len, bend, sheath, pt)
  if sheath and S.attachment == "sheathing" then
    local l = range(0.14, 0.22)
    local x, y = -l * sin(sheath[1]), l * cos(sheath[1])
    local w = sheath[2]
    taper(0, 0, x / 3, y / 3, 2 * x / 3, 2 * y / 3, x, y, w, 0.8 * w) fill(tone(0.34), 1)
    taper(0, 0, x / 3, y / 3, 2 * x / 3, 2 * y / 3, x, y, w, 0.8 * w) stroke(tone(0.08), 0.2, 0.6, false)
    ink.push() ink.translate(x, y) M.leaf(len, bend, nil, pt) ink.pop()
    return
  end
  local u, lpt
  if pt then u, lpt = pm.div(pt, CM) / 65536, pt / 65536 else u, lpt = leafu(len), len * 28.45276 end
  local cp = S.compound
  if cp == "simple" then
    if S.venation == "parallel" then strap(max(0.025, min(0.12, 0.42 / S.leafaspect)), bend, u)
    else
      local depth, lobes = 0, 3
      if LOBED[S.leafmargin] then depth, lobes = min(0.85, max(0.3, S.dissection)), floor(max(2, min(6, S.teeth))) end
      sleaf({ bend = bend, lobes = lobes, depth = depth, u = u })
    end
  elseif cp == "frond" then
    M.frond(u, { unroll = 1, fertile = false, under = false })
  else
    RP.pt = pt
    compoundleaf(lpt)
    RP.pt = nil
  end
end

-- ================================================================ M8f5: visitors
-- The animals that come to the plants (\florilegiumvisitor, \flo@visitor): butterflies and
-- moths, bees and hoverflies, dragonflies and damselflies, beetles and ladybirds, a
-- caterpillar on its leaf, a snail, a bird. Their helpers sit in a function of their own,
-- whose locals count afresh (the main chunk is near Lua's limit of 200).
local visitor = (function()
  local species = require("florilegium-species")
  local X                                   -- the visitor's traits as TeX holds them (\flo@x@<trait>)
  local fine = false                        -- \flo@fine: large enough on the page for detail
  -- a tone from the ink's ramp, on a copper visitor too; white() is \flo@bgwhite's paint
  local function plain(t)
    local was = pen.accent
    pen.accent = false
    local c = tone(t)
    pen.accent = was
    return c
  end
  local function white() return plain(0.93) end
  -- \flo@lgseg: a straight tapered stroke from (x0, y0) to (x1, y1), widths wa to wb
  local function seg(x0, y0, x1, y1, wa, wb)
    local dx, dy = x1 - x0, y1 - y0
    taper(x0, y0, x0 + dx / 3, y0 + dy / 3, x0 + dx * 2 / 3, y0 + dy * 2 / 3, x1, y1, wa, wb)
  end
  -- \flo@bgell: an ellipse along the axes
  local function ell(cx, cy, rx, ry) ellipse(cx, cy, rx, 0, 0, ry) end
  -- \flo@bgband: a band across from y hi down to lo, its middle bowed toward the tail
  local function band(hi, lo, bow)
    local ya, yb = hi - bow, lo - bow
    moveto(-0.5, hi) curveto(-0.17, ya, 0.17, ya, 0.5, hi)
    lineto(0.5, lo) curveto(0.17, yb, -0.17, yb, -0.5, lo) close()
  end
  -- \flo@vlegs: the legs of a side, drawn to the right, from table t (per leg its root, knee,
  -- foot and the end of its tarsus), in color c; LGP holds \flo@lg...: the scale x, the
  -- height y, ex out from the middle, the jitter j, ph the feet raised (the middle pair
  -- lowered), the width w
  local LG = {
    o = { { 0.025, 0.03, 0.06, 0.06, 0.07, 0.11, 0.065, 0.15 }, { 0.03, 0, 0.075, 0.02, 0.095, 0.07, 0.095, 0.1 },
      { 0.03, -0.03, 0.085, -0.03, 0.11, 0.01, 0.115, 0.045 } },
    b = { { 0.06, 0.08, -0.06, 0.13, 0.02, 0.27, 0.04, 0.34 }, { 0.08, -0.04, 0, -0.02, 0.1, -0.1, 0.15, -0.14 },
      { 0.08, -0.12, -0.02, -0.24, 0.06, -0.42, 0.08, -0.5 } },
  }
  local LGP = {}
  local function vlegs(t, c)
    local x, y, ex, j = LGP.x, LGP.y, LGP.ex, LGP.j
    local legs = {}
    for i = 1, 3 do
      local p = LG[t][i]
      local lift = i == 2 and -LGP.ph or LGP.ph
      local kx = p[3] * x + ex + sym(j)
      local ky = y + p[4] * x + sym(j)
      local fx = p[5] * x + ex + sym(j)
      local fy = y + p[6] * x + lift + sym(j)
      legs[i] = { p[1] * x, y + p[2] * x, kx, ky, fx, fy, fx + (p[7] - p[5]) * x, fy + (p[8] - p[6]) * x }
    end
    local w = LGP.w
    for i = 1, 3 do
      local q = legs[i]
      seg(q[1], q[2], q[3], q[4], w, w * 0.8)
      seg(q[3], q[4], q[5], q[6], w * 0.75, w * 0.45)
      seg(q[5], q[6], q[7], q[8], w * 0.4, w * 0.2)
    end
    fill(c, 1)
  end

  -- ---------------------------------------------------------------- butterflies and moths
  local WG = {}                             -- the one being drawn (\flo@wg...)
  -- the wings' points, forewing and hindwing; an apex other than a rounded one moves some
  local WSHAPE = {
    fw = { { 0.05, 0.1 }, { 0.3, 0.45 }, { 0.65, 0.68 }, { 0.92, 0.66 }, { 1, 0.5 }, { 0.88, 0.25 }, { 0.55, 0.05 },
      { 0.15, -0.02 } },
    hw = { { 0.06, -0.02 }, { 0.45, -0.02 }, { 0.72, -0.22 }, { 0.7, -0.5 }, { 0.5, -0.72 }, { 0.25, -0.7 }, { 0.08, -0.35 } },
  }
  local APEX = {
    rounded = {},
    angular = { { "fw", 3, 0.66, 0.7 }, { "fw", 4, 1, 0.72 }, { "fw", 5, 0.98, 0.52 } },
    falcate = { { "fw", 3, 0.66, 0.72 }, { "fw", 4, 1.04, 0.78 }, { "fw", 5, 0.9, 0.55 } },
    pointed = { { "fw", 1, 0.05, 0.08 }, { "fw", 2, 0.35, 0.36 }, { "fw", 3, 0.72, 0.42 }, { "fw", 4, 1, 0.38 },
      { "fw", 5, 0.97, 0.28 }, { "fw", 6, 0.7, 0.16 }, { "fw", 7, 0.38, 0.04 }, { "fw", 8, 0.12, -0.02 },
      { "hw", 2, 0.4, -0.02 }, { "hw", 3, 0.55, -0.12 }, { "hw", 4, 0.52, -0.28 }, { "hw", 5, 0.38, -0.38 },
      { "hw", 6, 0.2, -0.36 }, { "hw", 7, 0.07, -0.2 } },
  }
  -- \flo@wgshape, \flo@wgjit: the wings' points, each moved a little
  local function wgshape()
    for _, w in ipairs({ "fw", "hw" }) do
      local t = {}
      for i, p in ipairs(WSHAPE[w]) do t[i] = { p[1], p[2] } end
      WG[w] = t
    end
    for _, p in ipairs(APEX[X.apex] or APEX.rounded) do WG[p[1]][p[2]] = { p[3], p[4] } end
    for _, w in ipairs({ "fw", "hw" }) do
      for _, p in ipairs(WG[w]) do
        p[1] = p[1] + sym(0.03)
        p[2] = p[2] + sym(0.03)
      end
    end
  end
  local TAIL = { { -0.05, -0.04 }, { -0.06, -0.19 }, { -0.1, -0.25 }, { -0.15, -0.2 }, { -0.13, -0.04 } }
  -- \flo@wgoutline: a wing's outline through its n points, a swallowtail's tail after point
  -- WG.tail, the edge between points m0 and m1 scalloped by WG.dep
  local function wgoutline(w, n, m0, m1)
    local p, pts = WG[w], {}
    for a = 1, n do
      pts[#pts + 1] = { p[a][1], p[a][2] }
      if WG.tail == a then
        local hx, hy = WG.hw[5][1], WG.hw[5][2]
        for _, q in ipairs(TAIL) do pts[#pts + 1] = { hx + q[1], hy + q[2] } end
      end
      if WG.dep > 0 and a < m1 and a > m0 - 1 then
        local b = a + 1
        for j = 1, 3 do
          local t = j / 4
          local x, y = p[a][1] + (p[b][1] - p[a][1]) * t, p[a][2] + (p[b][2] - p[a][2]) * t
          if j % 2 == 1 then x, y = x * (1 - WG.dep), y * (1 - WG.dep) end
          pts[#pts + 1] = { x, y }
        end
      end
    end
    return pts
  end
  -- \flo@wgat: the point a fraction s along the edge from point m0 to m1, drawn in by f
  local function wgat(w, m0, m1, s, f)
    local m = m1 - m0
    local q = s * m
    local k = min(m - 1, floor(q))
    local r = q - k
    local a, b = WG[w][m0 + k], WG[w][m0 + k + 1]
    return (a[1] + (b[1] - a[1]) * r) * f, (a[2] + (b[2] - a[2]) * r) * f
  end
  -- \flo@wgeye: an eyespot at (x, y), radius r: its rings, pupil and the arcs outside it
  local function wgeye(x, y, r)
    circle(x, y, r) fill(tone(0.08), 0.85)
    if X.rings > 1 then circle(x, y, r * 0.68) fill(WHITE, 0.85) end
    circle(x, y, r * (X.rings > 2 and 0.46 or 0.34)) fill(accent(0), 1)
    if X.rings > 2 then
      circle(x, y, r * 0.22) fill(tone(0.06), 1)
      circle(x + r * 0.1, y + r * 0.1, r * 0.08) fill(WHITE, 0.9)
    end
    if X.parafocal == 1 then
      local a, wr = atan2(y, x), r * 1.5
      taper(x + wr * cos(a - 55), y + wr * sin(a - 55), x + wr * 1.15 * cos(a - 20), y + wr * 1.15 * sin(a - 20),
        x + wr * 1.15 * cos(a + 20), y + wr * 1.15 * sin(a + 20), x + wr * cos(a + 55), y + wr * sin(a + 55), r * 0.22, r * 0.22)
      fill(tone(0.08), 0.6, true)
    end
  end
  local BASE = { fw = { 0.12, 0.12 }, hw = { 0.12, -0.12 } }
  local ROOTY, SPOTR = { fw = 0.04, hw = -0.04 }, { fw = 0.03, hw = 0.034 }
  -- \flo@wgpaint: a wing (w: fw or hw, its n points, its edge's spots from point m0 to m1):
  -- the ground, then in its clip the groundplan's pattern systems and the veins
  local function wgpaint(w, n, m0, m1)
    local o, t, cj, cw = WG["o" .. w], WG.t, WG.cj, WG.cw
    smooth(o, true) fill(tone(t), WG.op)
    if WG.blur then return end
    smooth(o, true) ink.clip()
    if fine then mottle(-1.2, -1.2, 1.2, 1.2, 9, 0.14, t - 0.2, t + 0.2, 8)
    else mottle(-1.2, -1.2, 1.2, 1.2, 4, 0.14, t - 0.2, t + 0.2, 0) end
    if X.basal == 1 then
      local b = blob(BASE[w][1], BASE[w][2], 0.2, 0.15, 0.3, 9)
      smooth(b, true) fill(tone(t - 0.18), 0.45, true)
    end
    if X.central == "band" then
      if w == "fw" then taper(0.5, 0.66, 0.47 + cj, 0.45, 0.42 + cj, 0.25, 0.36, 0.04, cw, cw * 0.8)
      else taper(0.42, -0.02, 0.37 + cj, -0.2, 0.28 + cj, -0.38, 0.14, -0.55, cw, cw * 0.8) end
      fill(tone(t - 0.2), 0.6, true)
    end
    if X.central == "spots" then
      local xa, xb, ya, yb
      if w == "fw" then xa, xb, ya, yb = 0.47 + cj, 0.42 + cj, 0.45, 0.25 else xa, xb, ya, yb = 0.37 + cj, 0.28 + cj, -0.2, -0.38 end
      circle(xa, ya, 0.045) circle(xb, yb, 0.045) circle((xa + xb) / 2, (ya + yb) / 2, 0.04)
      fill(tone(t - 0.22), 0.65, true)
    end
    if X.discal == 1 then
      if w == "fw" then ell(0.47 + cj * 0.5, 0.42, 0.03, 0.045) else ell(0.34 + cj * 0.5, -0.27, 0.03, 0.045) end
      fill(tone(t - 0.25), 0.75, true)
    end
    local border = X.border
    if border == "band" then smooth(o, true) stroke(tone(t - 0.22), 6.8 * WG.u, 0.55, true) end
    smooth(o, true) stroke(tone(0.05), 7.37 * WG.u, 0.55, true)
    if border == "spots" then
      local c = tone(0.06)
      for b = 1, X.eyes do
        local x, y = wgat(w, m0, m1, (b - 0.5) / X.eyes, 0.86)
        circle(x, y, SPOTR[w]) fill(c, 0.8)
      end
    end
    if border == "eyes" then
      for b = 1, WG["ne" .. w] do
        local x, y = wgat(w, m0, m1, WG["es" .. w] + (b - 1) * WG["ed" .. w], 0.78)
        wgeye(x, y, WG["er" .. w])
      end
    end
    -- lunules: white arcs along the edge, a row or two
    if X.lunules > 0 then
      local nc = 2 * (m1 - m0)
      for k = 1, X.lunules do
        local f = 0.95 - 0.07 * (k - 1)
        for b = 1, nc do
          local xc, yc = wgat(w, m0, m1, (b - 0.85) / nc, f)
          local xd, yd = wgat(w, m0, m1, (b - 0.15) / nc, f)
          taper(xc, yc, (xc + (xd - xc) / 3) * 0.96, (yc + (yd - yc) / 3) * 0.96,
            (xc + (xd - xc) * 2 / 3) * 0.96, (yc + (yd - yc) * 2 / 3) * 0.96, xd, yd, 0.03, 0.008)
          fill(WHITE, 0.55)
        end
      end
    end
    -- the veins lifted out white from the wing's root, and on a wing with no border a dot
    -- at each vein's end
    if X.veins == 1 then
      local rx, ry = 0.04, ROOTY[w]
      for b = 1, n do
        local p = WG[w][b]
        local f = range(0.7, 0.88)
        local xd, yd = rx + (p[1] - rx) * f, ry + (p[2] - ry) * f
        local pts = { { rx, ry }, { rx + (xd - rx) * 0.3, ry + (yd - ry) * 0.4 }, { rx + (xd - rx) * 0.7, ry + (yd - ry) * 0.75 },
          { xd, yd } }
        streak(pts, 0.035, 0.004, WHITE, range(0.35, 0.6))
        if border == "none" and fine and b > 1 and b < n then
          circle(rx + (p[1] - rx) * 0.9, ry + (p[2] - ry) * 0.9, range(0.025, 0.04)) fill(WHITE, 0.8)
        end
      end
    end
    ink.unclip()
    -- two thread tails off the hindwing
    if X.tails == 2 and w == "hw" then
      local xb, yb = WG.hw[5][1], WG.hw[5][2]
      taper(xb - 0.05, yb - 0.02, xb - 0.04, yb - 0.1, xb - 0.025, yb - 0.18, xb - 0.02, yb - 0.26, 0.022, 0.006)
      taper(xb - 0.14, yb, xb - 0.14, yb - 0.08, xb - 0.135, yb - 0.14, xb - 0.13, yb - 0.2, 0.022, 0.006)
      fill(tone(0.08), 0.9)
    end
  end
  -- \flo@wgwing: a wing turned by rot about its root
  local function wgwing(w, rot, n, m0, m1)
    ink.push() ink.translate(0.04, 0) ink.rotate(rot) ink.translate(-0.04, 0)
    wgpaint(w, n, m0, m1)
    ink.pop()
  end
  -- \flo@wgbody: the abdomen and its bands, the thorax and its hair, the head, the feelers,
  -- the legs of a butterfly seen from the side, a highlight
  local function wgbody()
    local aw, al, tw = WG.aw, WG.al, WG.tw
    local pts = {}
    for b = 0, 10 do
      local v = b / 10
      local h, y = aw * sin(180 * v ^ 0.7), -al + al * v
      pts[b + 1] = { -h, y }
      if b > 0 and b < 10 then pts[21 - b] = { h, y } end
    end
    smooth(pts, true) fill(tone(0.06), 1)
    for b = 1, X.bands do
      local y = -al * b / (X.bands + 1)
      taper(-aw * 0.9, y, -aw * 0.3, y, aw * 0.3, y, aw * 0.9, y, 0.03, 0.03) fill(WHITE, 0.45)
    end
    ell(0, 0.08, tw, 0.13) fill(tone(0.04), 1)
    if X.hair > 0 then
      local c = tone(0.12)
      for _ = 1, floor(4 + 12 * X.hair) do
        local a, t = range(0, 360), range(0.03, 0.06)
        seg(tw * cos(a), 0.08 + 0.13 * sin(a), (tw + t) * cos(a), 0.08 + (0.13 + t) * sin(a), 0.022, 0.003)
        fill(c, 0.7)
      end
    end
    circle(0, 0.27, WG.hr) fill(tone(0.04), 1)
    local lw = max(0.3, 0.5 * WG.u)
    local ant = X.antenna
    for b = 1, 2 do
      local sx = (WG.sides == 1 or b == 2) and 1 or -1
      local ex, ey
      if ant == "clubbed" then ex, ey = range(0.22, 0.3), range(0.78, 0.9)
      elseif ant == "feathery" then ex, ey = range(0.24, 0.32), range(0.62, 0.72)
      else ex, ey = range(0.26, 0.36), range(0.72, 0.88) end       -- a thread, and any other kind's feeler
      if WG.sides == 1 and b == 2 then ex, ey = ex * 0.8, ey * 0.95 end
      ex = sx * ex
      local xa, xb, xc, yc = sx * 0.02, sx * 0.05, ex * 0.6, ey * 0.9
      local c = tone(0.08)
      moveto(xa, 0.31) curveto(xb, 0.5, xc, yc, ex, ey) stroke(c, lw, 1, false)
      if ant == "clubbed" then circle(ex, ey, 0.025) fill(tone(0.06), 1) end
      if ant == "feathery" then
        -- barbs across the feeler's curve, shorter toward its tip
        for k = 3, 9 do
          local v = k / 10
          local f = 1 - v
          local x = f * f * f * xa + 3 * f * f * v * xb + 3 * f * v * v * xc + v * v * v * ex
          local y = f * f * f * 0.31 + 3 * f * f * v * 0.5 + 3 * f * v * v * yc + v * v * v * ey
          local nx, ny = nrm(3 * f * f * (xb - xa) + 6 * f * v * (xc - xb) + 3 * v * v * (ex - xc),
            3 * f * f * 0.19 + 6 * f * v * (yc - 0.5) + 3 * v * v * (ey - yc))
          local r = 0.05 * (1.1 - v)
          moveto(x + nx * r, y + ny * r) lineto(x - nx * r, y - ny * r)
        end
        stroke(c, 0.5 * lw, 1, false)
      end
    end
    if WG.sides == 1 then
      for b = 0, 2 do
        local y = 0.16 - 0.08 * b
        moveto(-0.03, y) lineto(-0.12, y + 0.03) lineto(-0.2, y - 0.12)
      end
      stroke(tone(0.08), lw, 1, false)
    end
    if fine and X.kind == "butterfly" then
      local t = al / 0.62
      streak({ { -0.015, -0.55 * t }, { -0.02, -0.35 * t }, { -0.02, -0.1 * t }, { -0.01, 0 } }, 0.015, 0.004, WHITE, 0.45)
    end
  end
  -- \flo@lepid: a butterfly or moth, wingspan 2; u its wingspan on the page / 2 (cm),
  -- as TeX finds it (\flo@wgu)
  local function lepid(u)
    WG.u = u
    fine = u > K(0.3)
    local kind, pose, ground = X.kind, X.pose, X.ground
    local t = X.tone
    if ground == "pale" then t = min(0.78, t + 0.32) end
    if ground == "dark" then t = t - 0.12 end
    if ground == "copper" then pen.accent = true end
    -- seen from the side (wings closed over the back), spread flat, in a moth's tent, or
    -- hovering with its wings a blur at both ends of their beat
    local sides, xs, rf, rh, pass = 2, 1, 0, 0, 1
    WG.op, WG.blur = 1, false
    if pose == "closed" then sides, rf, rh, t = 1, 18, 18, min(0.8, t + 0.12) end
    if pose == "flat" then rf, rh = -18, -10 end
    if pose == "tent" then rf, rh, xs = -58, -38, 0.8 end
    if pose == "hover" then WG.op, WG.blur, pass = 0.3, true, 2 end
    WG.t, WG.sides = t, sides
    WG.aw, WG.al, WG.tw, WG.hr = 0.05, 0.62, 0.075, 0.06
    if kind == "moth" then WG.aw, WG.tw, WG.hr = 0.075, 0.095, 0.07 end
    if kind == "hawkmoth" then WG.aw, WG.al, WG.tw, WG.hr = 0.075, 0.85, 0.09, 0.07 end
    if sides == 1 then WG.aw = WG.aw * 0.8 end
    wgshape()
    WG.dep = X.wingedge == "scalloped" and 0.045 or X.wingedge == "toothed" and 0.09 or 0
    WG.tail = 0
    WG.ofw = wgoutline("fw", 8, 4, 6)
    if X.tails == 1 then WG.tail = 5 end
    WG.ohw = wgoutline("hw", 7, 3, 6)
    WG.cw, WG.cj = range(0.07, 0.11), sym(0.04)
    -- eyespots: a row on the hindwing, one near the forewing's tip when there are three or more
    WG.nehw, WG.nefw = X.eyes, X.eyes > 2 and 1 or 0
    if X.eyes > 0 then WG.erhw, WG.eshw, WG.edhw = min(0.1, 0.34 / X.eyes), 0.5 / X.eyes, 1 / X.eyes end
    WG.erfw, WG.esfw, WG.edfw = 0.065, 0.22, 0
    if sides == 1 then ink.rotate(90) end
    for a = 1, sides do
      ink.push()
      if a == 2 then xscale(-1) end
      if xs < 1 then xscale(xs) end
      for d = 1, pass do
        if pass == 2 then
          rf = d == 1 and 14 or -14
          rh = rf
        end
        if sides == 1 then wgwing("fw", rf, 8, 4, 6) wgwing("hw", rh, 7, 3, 6)
        else wgwing("hw", rh, 7, 3, 6) wgwing("fw", rf, 8, 4, 6) end
      end
      ink.pop()
    end
    wgbody()
  end

  -- ---------------------------------------------------------------- bees and hoverflies
  local BG = {}                             -- the one being drawn (\flo@bg...)
  -- \flo@bgpale: the pale bands on a part (where: t the thorax, a the abdomen) in color c, and a
  -- bumblebee's white tail
  local function bgpale(where, c)
    for b = 1, X.bands do
      if BG.bp[b] == where then band(BG.bh[b], BG.bl[b], 0.02) fill(c, 0.95) end
    end
    if where == "a" and X.paletail == 1 then band(BG.tl, -2, 0.02) fill(white(), 1) end
  end
  -- \flo@bgcolorat: the hair's color at height y on a part: 1 dark, 2 pale, 3 white
  local function bgcolorat(y, where)
    local c = 1
    if BG.kind == "bumblebee" then
      for d = 1, X.bands do
        if BG.bp[d] == where and y < BG.bh[d] and y > BG.bl[d] then c = 2 end
      end
      if where == "a" and X.paletail == 1 and y < BG.tl then c = 3 end
    end
    return c
  end
  -- \flo@bgfur: n hairs round the ellipse at (0, cy), half-width rx, ra above and rb below
  local function bgfur(cy, rx, n, ra, rb, where)
    local hairs = { {}, {}, {} }
    local hl = 0.5 + X.hair
    for _ = 1, n do
      local a, t, b = range(0, 360), range(0.02, 0.045), sym(25)
      t = t * hl + 0.014
      local v = sin(a)
      local r = (v < 0 and rb or ra) - 0.014
      local xa, ya = (rx - 0.014) * cos(a), cy + r * v
      local h = hairs[bgcolorat(ya, where)]
      h[#h + 1] = { xa, ya, xa + t * cos(a + b), ya + t * sin(a + b) }
    end
    local function paint(t, c, op)
      if #t == 0 then return end
      for _, h in ipairs(t) do seg(h[1], h[2], h[3], h[4], 0.013, 0.003) end
      fill(c, op)
    end
    paint(hairs[1], tone(BG.fd), 0.8)
    paint(hairs[2], tone(BG.pt), 0.9)
    paint(hairs[3], white(), 0.9)
  end
  -- \flo@bgabdomen: the abdomen's outline, a squared egg
  local function bgabdomen()
    local pts = {}
    for a = 1, 16 do
      local wa = 22.5 * a - 11.25
      local t, v = cos(wa), sin(wa)
      pts[a] = { BG.rx * (t < 0 and -1 or 1) * abs(t) ^ BG.sq, BG.ay + BG.ry * v * (v < 0 and BG.egg or 1) }
    end
    return pts
  end
  -- \flo@bgpaintab: the abdomen, its pattern (\flo@bgab@<kind>), mottle, rim and highlight
  local function bgpaintab()
    local ab, kind = BG.ab, BG.kind
    smooth(ab, true) fill(tone(BG.abt), 1)
    smooth(ab, true) ink.clip()
    if kind == "honeybee" then
      local gap = BG.la / (X.bands + 0.6)
      local bt = 0.42 * gap
      for b = 1, X.bands do
        local ya = BG.at - gap * (b + 0.15) + bt / 2
        band(ya, ya - bt, 0.03)
      end
      fill(tone(0.05), 0.88)
    elseif kind == "bumblebee" then bgpale("a", tone(BG.pt))
    elseif kind == "solitarybee" then
      for b = 1, X.bands do
        local ya = BG.at - BG.la * (0.12 + 0.17 * b)
        band(ya, ya - 0.035, 0.025)
      end
      fill(WHITE, 0.6)                      -- with no bands, an empty path, as the library paints it
    else                                    -- a hoverfly's bands, broken in the middle
      local gap = BG.la / (X.bands + 0.5)
      local c = BG.ground == "copper" and accent(0.1) or tone(min(0.86, BG.t + 0.4))
      for b = 1, X.bands do
        local y = BG.at - gap * (b - 0.2)
        local wa = (b == 1 and 0.3 or 0.42) * gap
        for s = -1, 1, 2 do
          local x = s * BG.rx
          taper(s * 0.03, y + 0.01, x * 0.4, y + 0.03, x * 0.75, y, x * 1.05, y - 0.04, wa, 0.15 * gap)
        end
      end
      fill(c, 0.92)
    end
    if fine then mottle(-BG.rx, BG.at - BG.la, BG.rx, BG.at, 4, 0.06, BG.abt - 0.18, BG.abt + 0.17, BG.spk) end
    smooth(ab, true) stroke(tone(0.05), 3.4 * BG.u, BG.rim, true)
    local t, v, ay = BG.rx / 0.19, BG.ry / 0.3, BG.ay
    streak({ { -0.1 * t, ay - 0.18 * v }, { -0.13 * t, ay - 0.05 * v }, { -0.12 * t, ay + 0.08 * v }, { -0.08 * t, ay + 0.2 * v } },
      0.04, 0.008, WHITE, 0.5)
    ink.unclip()
  end
  -- \flo@bgthorax
  local function bgthorax()
    local th = blob(0, BG.ty, BG.tr, BG.tr * 0.93, 0.1, 12)
    smooth(th, true) fill(tone(BG.tt), 1)
    smooth(th, true) ink.clip()
    if BG.kind == "bumblebee" then bgpale("t", tone(BG.pt)) end
    if fine then mottle(-BG.tr, BG.ty - BG.tr, BG.tr, BG.ty + BG.tr, 3, 0.05, BG.tt - 0.04, BG.tt + 0.24, 6) end
    ink.unclip()
  end
  -- \flo@bghead: the head and its eyes
  local function bghead()
    ell(0, BG.hy, BG.hx, BG.hr) fill(tone(BG.ht), 1)
    local ya = BG.hy + 0.008
    local xa, xb, yb
    if BG.kind == "hoverfly" then xa, xb, yb = 0.055, 0.068, 0.08 else xa, xb, yb = BG.hx - 0.022, 0.028, 0.05 end
    ell(-xa, ya, xb, yb) ell(xa, ya, xb, yb) fill(tone(0.02), 1)
    if fine then
      local xc, yc = xa - 0.3 * xb, ya + 0.35 * yb
      circle(-xc, yc, 0.011) circle(xc, yc, 0.011) fill(WHITE, 0.7)
    end
  end
  -- \flo@bgfeelers: a bee's elbowed feelers, a hoverfly's short ones
  local function bgfeelers()
    local c, hy = tone(0.06), BG.hy
    if BG.kind == "hoverfly" then
      local ya, yb, yc = hy + 0.07, hy + 0.11, hy + 0.14
      moveto(-0.02, ya) lineto(-0.035, yb) moveto(0.02, ya) lineto(0.035, yb) stroke(c, BG.w, 1, false)
      circle(-0.035, yb, 0.014) circle(0.035, yb, 0.014) fill(c, 1)
      if fine then moveto(-0.035, yb) lineto(-0.075, yc) moveto(0.035, yb) lineto(0.075, yc) stroke(c, 0.5 * BG.w, 1, false) end
    else
      local ya = hy + 0.05
      for b = 1, 2 do
        local sx = b == 1 and -1 or 1
        local xb, yb, xc, yc = range(0.05, 0.08), range(0.11, 0.15), range(0.11, 0.17), range(0.17, 0.23)
        moveto(sx * 0.03, ya) lineto(sx * xb, hy + yb) lineto(sx * xc, hy + yc)
      end
      stroke(c, BG.w, 1, false)
    end
  end
  -- the legs of a side: root, knee and foot, spread on a flower (v) or hanging in flight (f)
  local BGL = {
    v = { { 0.05, 0.06, 0.17, 0.1, 0.2, 0.22 }, { 0.06, 0, 0.23, -0.03, 0.3, -0.15 }, { 0.06, -0.06, 0.28, -0.16, 0.32, -0.42 } },
    f = { { 0.05, 0.04, 0.13, 0, 0.15, -0.1 }, { 0.06, 0, 0.17, -0.08, 0.18, -0.24 }, { 0.06, -0.05, 0.21, -0.16, 0.22, -0.44 } },
  }
  -- \flo@bglegs: the legs of a side, and the pollen on the hind leg
  local function bglegs()
    local legs, lx, ty = {}, BG.lx, BG.ty
    for i = 1, 3 do
      local p = BGL[BG.lp][i]
      local kx = p[3] * lx + sym(0.02)
      local ky = ty + p[4] + sym(0.02)
      local fx = p[5] * lx + sym(0.025)
      legs[i] = { p[1] * lx, ty + p[2], kx, ky, fx, ty + p[6] + sym(0.025) }
    end
    local fw = BG.fw
    for i = 1, 3 do
      local q = legs[i]
      seg(q[1], q[2], q[3], q[4], fw, fw * 0.8)
      seg(q[3], q[4], q[5], q[6], fw * 0.8, fw * 0.35)
    end
    fill(tone(0.08), 1)
    if X.pollen == 1 then
      local q, pr = legs[3], BG.pr
      local da, db = q[5] - q[3], q[6] - q[4]
      local nx, ny = nrm(da, db)
      local xa, ya = q[3] + 0.45 * da, q[4] + 0.45 * db
      local xb, yb, xc, yc = ny * 0.065 * pr, -nx * 0.065 * pr, nx * 0.042 * pr, ny * 0.042 * pr
      ellipse(xa, ya, xb, yb, xc, yc) fill(accent(0), 1)
      if fine then circle(xa + 0.35 * xc - 0.25 * xb, ya + 0.35 * yc - 0.25 * yb, 0.012) fill(WHITE, 0.6) end
    end
  end
  -- \flo@bgwingshape: a wing's outline, a teardrop l long and w wide each way
  local BGW = { { 0.02, 0 }, { 0.15, 0.55 }, { 0.45, 0.95 }, { 0.8, 0.85 }, { 1, 0.25 }, { 0.92, -0.45 }, { 0.6, -0.8 },
    { 0.25, -0.55 } }
  local function bgwingshape(l, w)
    local pts = {}
    for i, p in ipairs(BGW) do
      local x = p[1] * l + sym(0.012)
      pts[i] = { x, p[2] * w + sym(0.012) }
    end
    return pts
  end
  -- the veins of a forewing (f), a hindwing (h) and a hoverfly's wing (v), in its length and width
  local BGV = {
    f = { { { 0.03, 0.3 }, { 0.35, 0.72 }, { 0.62, 0.78 } }, { { 0.03, 0.05 }, { 0.3, 0.2 }, { 0.62, 0.25 }, { 0.85, 0.32 } },
      { { 0.05, -0.15 }, { 0.35, -0.25 }, { 0.6, -0.3 } }, { { 0.62, 0.78 }, { 0.62, 0.25 }, { 0.6, -0.3 } } },
    h = { { { 0.03, 0.05 }, { 0.55, 0.15 } } },
    v = { { { 0.03, 0.35 }, { 0.5, 0.8 }, { 0.85, 0.62 } }, { { 0.03, 0 }, { 0.5, 0.1 }, { 0.78, 0.22 }, { 0.92, 0.05 } },
      { { 0.05, -0.2 }, { 0.45, -0.32 }, { 0.72, -0.28 }, { 0.86, -0.08 } }, { { 0.5, 0.8 }, { 0.5, 0.1 }, { 0.45, -0.32 } } },
  }
  -- \flo@bgwing: a wing at (x, y) turned by rot, a pale film; with its rim, and its veins
  -- (wl long, ww wide) when the bee is large enough
  local function bgwing(pts, x, y, rot, op, rim, veins, wl, ww)
    local was = pen.accent
    pen.accent = false
    ink.push() ink.translate(x, y) ink.rotate(rot)
    smooth(pts, true) fill(tone(0.85), op)
    if rim then
      smooth(pts, true) stroke(tone(0.15), BG.lw, 0.75, false)
      if fine then
        for _, l in ipairs(BGV[veins]) do
          moveto(l[1][1] * wl, l[1][2] * ww)
          for i = 2, #l do lineto(l[i][1] * wl, l[i][2] * ww) end
        end
        stroke(tone(0.2), BG.vw, 0.6, false)
      end
    end
    ink.pop()
    pen.accent = was
  end
  -- \flo@bgwings: the wings of a side, at rest or three times over in their beat
  local function bgwings()
    local wf = bgwingshape(BG.Fl, BG.Fw)
    local wh = BG.Hl > 0 and bgwingshape(BG.Hl, BG.Hw)
    local yf, yh = BG.ty + 0.05, BG.ty - 0.01
    if BG.blur then
      for b = 1, 3 do
        if wh then bgwing(wh, 0.06, yh, BG.ah + (b - 2) * BG.dl, 0.16, false) end
        bgwing(wf, 0.06, yf, BG.af + (b - 2) * BG.dl, 0.16, false)
      end
    else
      if wh then bgwing(wh, 0.06, yh, BG.ah, 0.55, true, "h", BG.Hl, BG.Hw) end
      bgwing(wf, 0.06, yf, BG.af, 0.55, true, BG.vn, BG.Fl, BG.Fw)
    end
  end
  -- \flo@bgtrail: flora's flight path, dotted, looping away behind
  local function bgtrail()
    local tc, tn = range(0.16, 0.22), range(1, 1.3)
    local sx = chance(0.5) and 1 or -1
    local w, gap = max(0.6, 1.1 * BG.u), max(2.2, 3.2 * BG.u)
    for b = 0, 40 do
      local a = b / 40 * tn * 360
      local x, y = sx * 0.32 * sin(a), BG.tip - 0.06 - tc * a * 0.0174533 + 0.192 * (cos(a) - 1)
      if b == 0 then moveto(x, y) else lineto(x, y) end
    end
    stroke(tone(0.18), w, 0.7, false, string.format("[%.2f %.2f] %.2f", 0, gap, 0))
  end
  -- \flo@bee: a bee or a hoverfly, length 1; u its length on the page in cm, as TeX finds it
  local function bee(u)
    BG.u = u
    fine = u > K(0.25)
    local kind, pose = X.kind, X.pose
    BG.kind, BG.ground = kind, X.ground
    BG.t, BG.pt = X.tone, 0.74
    if X.ground == "dark" then BG.t = BG.t - 0.12 end
    if X.ground == "pale" then BG.t = min(0.78, BG.t + 0.32) end    -- fixed: paler, as a butterfly's
    if X.ground == "copper" then pen.accent = true BG.pt = 0.6 end
    local t = BG.t
    BG.rx, BG.ry, BG.sq, BG.egg = 0.48 * X.build, 0.3, 1, 1.12
    BG.tr, BG.hx, BG.hr = 0.13, 0.09, 0.075
    BG.Fl, BG.Fw, BG.Hl, BG.Hw, BG.vn = 0.52, 0.12, 0.38, 0.08, "f"
    BG.fw, BG.pr = 0.032, 1
    BG.abt, BG.tt, BG.ht, BG.fd = t, 0.06, 0.04, t
    BG.spk, BG.rim = 4, 0.4
    if kind == "honeybee" then BG.tt = max(0.06, t - 0.24) end
    if kind == "bumblebee" then
      BG.egg, BG.tr, BG.hx, BG.Fl, BG.Fw = 1, 0.16, 0.1, 0.48, 0.11
      BG.Hl, BG.fw, BG.pr, BG.abt, BG.tt = 0.34, 0.036, 1.2, 0.06, 0.05
      BG.fd, BG.spk, BG.rim = 0.08, 14, 0.15
    end
    if kind == "solitarybee" then
      BG.ry, BG.tr, BG.hx, BG.pr = 0.32, 0.11, 0.08, 0.9
      BG.abt = max(0.05, t - 0.24)
    end
    if kind == "hoverfly" then
      BG.rx, BG.sq, BG.egg, BG.tr, BG.hx = 0.42 * X.build, 0.8, 1.2, 0.12, 0.13
      BG.hr, BG.Fl, BG.Hl, BG.vn, BG.fw = 0.085, 0.62, 0, "v", 0.022
      BG.abt, BG.tt, BG.ht, BG.fd = 0.08, 0.08, 0.1, 0.25
    end
    local ty, tr, ry = 0.15, BG.tr, BG.ry
    local at = ty - tr + 0.09
    local la = ry * (1 + 0.98 * BG.egg)
    BG.ty, BG.hy, BG.at, BG.ay, BG.la, BG.tip = ty, ty + tr + 0.04, at, at - ry, la, at - la
    BG.lx = 0.6 + 2 * BG.rx
    -- the bands' parts and their tops and bottoms; a bumblebee's white tail from tl down
    BG.bp = { "t", "a", "t", "a" }
    BG.bh = { ty + tr + 0.03, at - 0.3 * la, ty - 0.5 * tr, at + 0.03 }
    BG.bl = { ty + 0.4 * tr, at - 0.5 * la, ty - tr - 0.03, at - 0.17 * la }
    BG.tl = at - 0.7 * la
    BG.w, BG.lw, BG.vw = max(0.25, 0.45 * u), max(0.2, 0.35 * u), max(0.15, 0.25 * u)
    BG.lp, BG.blur, BG.af, BG.ah, BG.dl = "v", false, -70, -78, 0
    if pose == "fly" then BG.lp, BG.blur, BG.af, BG.ah, BG.dl = "f", true, 14, -4, 16 end
    if pose == "rest" then BG.af = -45 end
    if pose == "hover" then BG.lp, BG.blur, BG.af, BG.dl = "f", true, 10, 22 end
    if pose == "fly" then bgtrail() end
    for a = 1, 2 do
      ink.push()
      if a == 2 then xscale(-1) end
      bglegs()
      ink.pop()
    end
    BG.ab = bgabdomen()
    bgpaintab()
    if kind ~= "hoverfly" and kind ~= "bumblebee" then             -- the sting
      local ya, yb = BG.tip + 0.04, BG.tip - 0.05
      moveto(-0.022, ya) lineto(0, yb) lineto(0.022, ya) close() fill(tone(0.05), 1)
    end
    if kind == "bumblebee" then bgfur(BG.ay, BG.rx, floor(24 + 60 * X.hair), ry, ry * BG.egg, "a") end
    bgthorax()
    local rb = BG.tr * 0.93
    bgfur(ty, BG.tr, floor(6 + 40 * X.hair), rb, rb, "t")
    bghead() bgfeelers()
    for a = 1, 2 do
      ink.push()
      if a == 2 then xscale(-1) end
      bgwings()
      ink.pop()
    end
  end

  -- ---------------------------------------------------------------- dragonflies and damselflies
  local OD = {}                             -- the one being drawn (\flo@od...)
  -- the wings' outlines (along, across in units of the width) and their long veins (across
  -- at the root, middle and tip; 0 and 5 outside the wing)
  local ODW = {
    f = { { 0.01, 0.35 }, { 0.12, 0.75 }, { 0.45, 0.95 }, { 0.8, 0.9 }, { 0.97, 0.55 }, { 1, 0.1 }, { 0.93, -0.45 }, { 0.7, -0.75 },
      { 0.35, -0.85 }, { 0.08, -0.6 } },
    h = { { 0.01, 0.35 }, { 0.15, 0.8 }, { 0.5, 0.95 }, { 0.85, 0.85 }, { 1, 0.3 }, { 0.96, -0.35 }, { 0.75, -0.75 }, { 0.45, -1 },
      { 0.2, -1.45 }, { 0.04, -0.8 } },
    d = { { 0, 0.1 }, { 0.2, 0.2 }, { 0.45, 0.8 }, { 0.78, 0.98 }, { 0.98, 0.6 }, { 1, 0 }, { 0.94, -0.6 }, { 0.75, -0.9 },
      { 0.45, -0.6 }, { 0.2, -0.2 } },
  }
  local ODV = {
    f = { [0] = { 1.3, 1.3, 1.3 }, { 0.45, 0.72, 0.5 }, { 0.15, 0.3, 0.05 }, { -0.1, -0.3, -0.45 }, { -0.3, -0.7, -0.8 },
      { -1.6, -1.6, -1.6 } },
    h = { [0] = { 1.3, 1.3, 1.3 }, { 0.45, 0.72, 0.5 }, { 0.15, 0.3, 0.05 }, { -0.1, -0.35, -0.45 }, { -0.4, -0.9, -0.8 },
      { -1.6, -1.6, -1.6 } },
    d = { [0] = { 1.3, 1.3, 1.3 }, { 0.06, 0.6, 0.4 }, { 0.02, 0.2, 0 }, { -0.02, -0.25, -0.4 }, { -0.06, -0.5, -0.7 },
      { -1.6, -1.6, -1.6 } },
  }
  -- \flo@odvat: long vein k across the wing at u along it
  local function odvat(k, u)
    local v = ODV[OD.wk][k]
    if u < 0.5 then return v[1] + (v[2] - v[1]) * 2 * u end
    return v[2] + (v[3] - v[2]) * (2 * u - 1)
  end
  -- \flo@odwingshape
  local function odwingshape(t, l, w)
    local pts = {}
    for i, p in ipairs(ODW[t]) do
      local x = p[1] * l + sym(0.008)
      pts[i] = { x, p[2] * w + sym(0.008) }
    end
    return pts
  end
  -- \flo@odwing: a wing at (x, y) turned by rot: the film, a tint, the stigma, the long veins
  -- and on a large one the net between them, the rim
  local function odwing(pts, x, y, rot)
    local wl, ww, was = OD.wl, OD.ww, pen.accent
    ink.push() ink.translate(x, y) ink.rotate(rot)
    pen.accent = false
    smooth(pts, true) fill(tone(OD.wt), OD.wo)
    pen.accent = was
    smooth(pts, true) ink.clip()
    if X.tint == "tinted" then ell(0.1 * wl, -0.2 * ww, 0.2 * wl, 1.2 * ww) fill(tone(0.45), 0.3) end
    if X.stigma == 1 then
      local xa, xb, ya, yb = 0.83 * wl, 0.91 * wl, 1.3 * ww, 0.55 * ww
      moveto(xa, ya) lineto(xb, ya) lineto(xb, yb) lineto(xa, yb) close() fill(tone(0.06), 0.9)
    end
    if fine then
      pen.accent = false
      for c = 1, 4 do
        local v = ODV[OD.wk][c]
        moveto(0, v[1] * ww) lineto(0.5 * wl, v[2] * ww) lineto(wl, v[3] * ww)
      end
      stroke(tone(0.15), OD.vw, 0.6, false)
      if OD.net then
        for c = 1, 5 do
          for d = 1, 22 do
            local u = (d - 0.5 + sym(0.3)) / 22 * 0.96 + 0.02
            local ya, yb = odvat(c - 1, u) * ww, odvat(c, u) * ww
            moveto(u * wl, ya) lineto((u + sym(0.01)) * wl, yb)
          end
        end
        stroke(tone(0.2), OD.nw, 0.45, false)
      end
      pen.accent = was
    end
    ink.unclip()
    if fine then
      pen.accent = false
      smooth(pts, true) stroke(tone(0.15), OD.lw, 0.7, false)
      pen.accent = was
    end
    ink.pop()
  end
  -- \flo@odwings: the hindwing and forewing of a side
  local function odwings()
    local wh = odwingshape(OD.hk, OD.Hl, OD.Hw)
    local wf = odwingshape(OD.fk, OD.Fl, OD.Fw)
    OD.wk, OD.wl, OD.ww = OD.hk, OD.Hl, OD.Hw
    odwing(wh, OD.rx, OD.ty - OD.hd, OD.ah)
    OD.wk, OD.wl, OD.ww = OD.fk, OD.Fl, OD.Fw
    odwing(wf, OD.rx, OD.ty + OD.fd, OD.af)
  end
  -- \flo@odabdomen: long and thin, swollen at its root
  local function odabdomen()
    local pts = {}
    for a = 0, 11 do
      local v = a / 11
      local h = OD.aw * (1 + OD.bul * math.exp(-min(9, v * v / 0.012))) * min(1, (1.04 - v) * 5)
      local y = OD.at + (OD.tip - OD.at) * v
      pts[a + 1] = { h, y }
      pts[24 - a] = { -h, y }
    end
    return pts
  end
  -- \flo@odseg: the end of segment e down the abdomen, as a fraction of its length
  local function odseg(e) if e < 1 then return 0 end return (e / 10) ^ 1.12 end
  -- \flo@odpaintab: the abdomen, dark bands on its last segments, the joints, a highlight,
  -- the rim and the claspers
  local function odpaintab()
    local ab, at, tip = OD.ab, OD.at, OD.tip
    smooth(ab, true) fill(tone(OD.t), 1)
    smooth(ab, true) ink.clip()
    if X.bands > 0 then
      for b = 1, X.bands do
        local n = 10 - X.bands + b
        local sa, sv = odseg(n - 1), odseg(n)
        band(at + (tip - at) * (sa + 0.6 * (sv - sa)), at + (tip - at) * sv - 0.01, 0.004)
      end
      fill(tone(0.04), 0.85)
    end
    if fine then
      for b = 1, 9 do
        local y = at + (tip - at) * odseg(b)
        moveto(-0.1, y) lineto(0.1, y)
      end
      stroke(tone(0.05), OD.jw, 0.5, false)
      local t, aw = tip - at, OD.aw
      streak({ { -0.4 * aw, at + 0.06 * t }, { -0.45 * aw, at + 0.3 * t }, { -0.45 * aw, at + 0.6 * t }, { -0.35 * aw, at + 0.85 * t } },
        0.5 * aw, 0.004, WHITE, 0.45)
    end
    smooth(ab, true) stroke(tone(0.05), 1.4 * OD.u, 0.35, true)
    ink.unclip()
    if fine then
      local ya, yb, xa, xb = tip + 0.012, tip - 0.022, 0.5 * OD.aw, 0.9 * OD.aw
      moveto(-xa, ya) lineto(-xb, yb) moveto(xa, ya) lineto(xb, yb) stroke(tone(0.05), OD.jw, 1, false)
    end
  end
  -- \flo@odthorax
  local function odthorax()
    local th = blob(0, OD.ty, OD.tr, OD.th, 0.06, 10)
    smooth(th, true) fill(tone(OD.tt), 1)
    smooth(th, true) ink.clip()
    if fine then
      for s = -1, 1, 2 do
        seg(s * 0.3 * OD.tr, OD.ty + 0.75 * OD.th, s * 0.6 * OD.tr, OD.ty - 0.45 * OD.th, 0.3 * OD.tr, 0.12 * OD.tr)
      end
      fill(WHITE, 0.35)
    end
    smooth(th, true) stroke(tone(0.05), 1.2 * OD.u, 0.4, true)
    ink.unclip()
  end
  -- \flo@odhead: the head and its great eyes
  local function odhead()
    ell(0, OD.hy, OD.hx, OD.hr) fill(tone(0.06), 1)
    local ya, ex, er = OD.hy + OD.ey, OD.ex, OD.er
    ell(-ex, ya, er, er) ell(ex, ya, er, er) fill(tone(OD.et), 1)
    if fine then
      local xa, yb, r = ex - 0.35 * er, ya + 0.4 * er, 0.22 * er
      circle(-xa, yb, r) circle(xa, yb, r) fill(WHITE, 0.7)
    end
  end
  -- \flo@odo: a dragonfly or a damselfly, length 1; u its length on the page in cm
  local function odo(u)
    OD.u = u
    fine, OD.net = u > K(0.4), u > K(1.5)
    local kind = X.kind
    local t = X.tone
    if X.ground == "dark" then t = t - 0.12 end
    if X.ground == "copper" then pen.accent = true end
    OD.t, OD.tt, OD.et = t, max(0.05, t - 0.08), max(0.06, t - 0.1)
    if X.tint == "tinted" then OD.wt, OD.wo = 0.7, 0.42 else OD.wt, OD.wo = 0.88, 0.28 end
    OD.aw, OD.bul, OD.at, OD.tip = 0.032, 0.7, 0.16, -0.62
    OD.ty, OD.tr, OD.th = 0.235, 0.066, 0.08
    OD.hy, OD.hx, OD.hr, OD.ex, OD.ey, OD.er = 0.34, 0.075, 0.048, 0.038, 0.006, 0.046
    OD.fk, OD.hk, OD.Fl, OD.Fw, OD.Hl, OD.Hw = "f", "h", 0.62, 0.065, 0.6, 0.075
    OD.rx, OD.fd, OD.hd, OD.af, OD.ah = 0.02, 0.03, 0.035, 8, -6
    LGP.x, LGP.w = 1, 0.014
    if kind == "damselfly" then
      OD.aw, OD.bul, OD.at, OD.tip = 0.016, 0.5, 0.2, -0.66
      OD.ty, OD.tr, OD.th = 0.26, 0.04, 0.058
      OD.hy, OD.hx, OD.hr, OD.ex, OD.ey, OD.er = 0.34, 0.075, 0.028, 0.058, 0, 0.026
      OD.fk, OD.hk, OD.Fl, OD.Fw, OD.Hl, OD.Hw = "d", "d", 0.5, 0.06, 0.48, 0.06
      OD.rx, OD.fd, OD.hd, OD.af, OD.ah = 0.012, 0.02, 0.02, -84, -88
      LGP.x, LGP.w = 0.75, 0.01
      -- its wings spread, or folded along the abdomen and seen edge on
      if X.pose == "spread" then OD.af, OD.ah = -38, -50 else OD.Fw, OD.Hw = 0.032, 0.032 end
    end
    OD.lw, OD.vw, OD.nw, OD.jw = max(0.18, 0.3 * u), max(0.12, 0.22 * u), max(0.08, 0.1 * u), max(0.1, 0.18 * u)
    LGP.y, LGP.ex, LGP.j, LGP.ph = OD.ty, 0, 0.006, 0
    local c = tone(0.06)
    for a = 1, 2 do
      ink.push()
      if a == 2 then xscale(-1) end
      vlegs("o", c)
      ink.pop()
    end
    OD.ab = odabdomen()
    odpaintab() odthorax() odhead()
    for a = 1, 2 do
      ink.push()
      if a == 2 then xscale(-1) end
      odwings()
      ink.pop()
    end
  end

  -- ---------------------------------------------------------------- beetles and ladybirds
  local BT = {}                             -- the one being drawn (\flo@bt...)
  -- \flo@btelytra: the wing cases' outline
  local function btelytra()
    local pts = {}
    for a = 1, 16 do
      local wa = 22.5 * a - 11.25
      local t, v = cos(wa), sin(wa)
      pts[a] = { BT.ew * (t < 0 and -1 or 1) * abs(t) ^ BT.sq, BT.ey + v * (v < 0 and BT.rb or BT.rt) }
    end
    return pts
  end
  -- \flo@btshield: the shield behind the head, pb wide at its back, pf at its front, pl long
  local function btshield(pb, pf, pl)
    local sa = BT.et - BT.so
    local sb = BT.et + pl
    local sl = sb - sa
    BT.sa, BT.sl = sa, sl
    local p = {}
    p[1], p[10] = { 0.55 * pb, sa - 0.01 }, { -0.55 * pb, sa - 0.01 }
    p[2], p[9] = { pb, sa }, { -pb, sa }
    p[3], p[8] = { BT.sw * pb, sa + 0.45 * sl }, { -BT.sw * pb, sa + 0.45 * sl }
    p[4], p[7] = { pf, sb - 0.15 * sl }, { -pf, sb - 0.15 * sl }
    p[5], p[6] = { 0.5 * pf, sb }, { -0.5 * pf, sb }
    return p
  end
  -- \flo@bthw: the wing cases' half-width at y
  local function bthw(y)
    local t = (y - BT.ey) / (y > BT.ey and BT.rt or BT.rb)
    return BT.ew * sqrt(max(0, 1 - t * t))
  end
  -- \flo@btstripes: four stripes down the wing cases, in color c
  local function btstripes(c)
    local t = BT.et - BT.tip
    for b = 1, 4 do
      local x = (b < 3 and 1 or -1) * (b % 2 == 1 and 0.36 or 0.72) * BT.ew
      taper(x, BT.et - 0.03, x * 1.04, BT.et - 0.35 * t, x * 0.9, BT.et - 0.7 * t, x * 0.35, BT.tip + 0.06, 0.045, 0.012)
    end
    fill(c, 0.8)
  end
  -- \flo@btspot: a spot at (x, y), about r across
  local function btspot(x, y, r, c)
    local sr = r * range(0.85, 1.1)
    smooth(blob(x, y, sr, sr * 0.92, 0.1, 8), true) fill(c, 0.95)
  end
  -- \flo@btspots: the spots in pairs, in rows of one to three a side, an odd one on the seam
  local function btspots(c)
    local m = X.spots // 2
    local r = X.spotsize * BT.ew * 1.6 / sqrt(max(1, m))
    local cols = m < 4 and 1 or m < 8 and 2 or 3
    local rows = (m + cols - 1) // cols
    local top, tip = BT.et - BT.so, BT.tip
    if X.spots - 2 * m > 0 then btspot(0, top - 0.6 * r, 1.15 * r, c) end
    for b = 1, m do
      local row = (b - 1) // cols
      local col = b - 1 - cols * row
      local v = (row + 0.5) / rows + sym(0.04)
      local u
      if cols == 1 then u = m == 1 and 0.5 or (row == 1 and 0.4 or 0.62)
      elseif cols == 2 then
        u = col == 0 and 0.33 or 0.7
        if b == m and col == 0 then u = 0.5 end
      else u = 0.2 + 0.3 * col end
      u = u + sym(0.05)
      local y = top - 0.04 - v * (top - tip - 0.08)
      local x = u * bthw(y)
      btspot(x, y, r, c) btspot(-x, y, r, c)
    end
  end
  -- \flo@btmark: the pattern's color
  local function btmark()
    if BT.ground == "dark" then
      if BT.kind == "ladybird" then return accent(0) end
      return tone(0.8)
    end
    return tone(0.04)
  end
  -- \flo@btpaintel: the wing cases, their pattern, mottle, rim, highlights and seam
  local function btpaintel()
    local el = BT.el
    smooth(el, true) fill(tone(BT.t), 1)
    smooth(el, true) ink.clip()
    if X.pattern == "stripes" then btstripes(btmark()) end
    if X.pattern == "spots" then btspots(btmark()) end
    if fine then mottle(-BT.ew, BT.tip, BT.ew, BT.et, 3, 0.08, BT.t - 0.06, BT.t + 0.2, 0) end
    smooth(el, true) stroke(tone(0.04), 2.6 * BT.u, 0.35, true)
    if fine then
      local ln, top, ew = (BT.et - BT.tip) * BT.gl, BT.et - BT.so, BT.ew
      for b = 1, 2 do
        local sx, op = b == 1 and -1 or 1, b == 1 and 0.55 or 0.3
        streak({ { sx * 0.5 * ew, top - 0.06 * ln }, { sx * 0.56 * ew, top - 0.23 * ln }, { sx * 0.54 * ew, top - 0.43 * ln },
          { sx * 0.45 * ew, top - 0.6 * ln } }, 0.14 * ew, 0.01, WHITE, op)
      end
    end
    ink.unclip()
    moveto(0, BT.et) lineto(0, BT.tip) stroke(tone(0.04), BT.w, 0.8, false)
  end
  -- \flo@btfeeler: a feeler, threadlike, clubbed or combed, in color c (another kind's,
  -- fixed, as a thread)
  local function btfeeler(c)
    local ant, fs = X.antenna, BT.fs
    local xa, ya = 0.35 * BT.hx, BT.hy + 0.6 * BT.hr
    local xb, yb, xc, yc
    if ant == "clubbed" then xb, yb, xc, yc = range(0.06, 0.08), range(0.06, 0.08), range(0.1, 0.13), range(0.12, 0.15)
    elseif ant == "comb" then xb, yb, xc, yc = range(0.08, 0.1), range(0.08, 0.1), range(0.16, 0.2), range(0.18, 0.22)
    else xb, yb, xc, yc = range(0.1, 0.14), range(0.1, 0.14), range(0.2, 0.28), range(0.24, 0.32) end   -- a thread, and any other kind's feeler
    xb, yb, xc, yc = xa + xb * fs, ya + yb * fs, xa + xc * fs, ya + yc * fs
    moveto(xa, ya) lineto(xb, yb) lineto(xc, yc) stroke(c, BT.w, 1, false)
    local da, db = xc - xb, yc - yb
    if ant == "clubbed" then
      local nx, ny = nrm(da, db)
      ellipse(xc, yc, ny * 0.034 * fs, -nx * 0.034 * fs, nx * 0.02 * fs, ny * 0.02 * fs) fill(c, 1)
    end
    if ant == "comb" and fine then
      local nx, ny = nrm(da, db)
      for k = 1, 6 do
        local v = k / 6
        local xd, yd = xb + v * da, yb + v * db
        moveto(xd, yd) lineto(xd - nx * 0.035 * fs, yd - ny * 0.035 * fs)
      end
      stroke(c, 0.6 * BT.w, 1, false)
    end
  end
  -- \flo@bthead: the head; a ladybird's white cheeks, or a beetle's eyes and jaws
  local function bthead()
    local hx, hy, hr = BT.hx, BT.hy, BT.hr
    ell(0, hy, hx, hr) fill(tone(BT.ht), 1)
    if BT.kind == "ladybird" then
      if fine then
        local xa, ya, r = 0.45 * hx, hy + 0.3 * hr, 0.22 * hx
        circle(-xa, ya, r) circle(xa, ya, r) fill(white(), 0.9)
      end
    else
      local xa, ya, xb, yb = 0.82 * hx, hy + 0.1 * hr, 0.24 * hx, 0.42 * hr
      ell(-xa, ya, xb, yb) ell(xa, ya, xb, yb) fill(tone(0.02), 1)
      if fine then
        for s = -1, 1, 2 do
          taper(s * 0.4 * hx, hy + 0.8 * hr, s * 0.5 * hx, hy + 1.25 * hr, s * 0.3 * hx, hy + 1.55 * hr, s * 0.08 * hx,
            hy + 1.6 * hr, 0.3 * hx, 0.004)
        end
        fill(tone(0.05), 1)
      end
    end
  end
  -- \flo@btpaintpn: the shield, a ladybird's white patches, a highlight, the rim
  local function btpaintpn()
    local pn, pb, sa, sl = BT.pn, BT.pb, BT.sa, BT.sl
    smooth(pn, true) fill(tone(BT.pt), 1)
    smooth(pn, true) ink.clip()
    if BT.kind == "ladybird" then
      local xa, ya, xb, yb = 0.72 * pb, sa + 0.62 * sl, 0.3 * pb, 0.4 * sl
      ell(-xa, ya, xb, yb) ell(xa, ya, xb, yb) fill(white(), 0.95)
    end
    if fine then
      streak({ { -0.55 * pb, sa + 0.3 * sl }, { -0.4 * pb, sa + 0.62 * sl }, { -0.2 * pb, sa + 0.8 * sl } }, 0.12 * sl, 0.006, WHITE, 0.4)
    end
    smooth(pn, true) stroke(tone(0.03), 2 * BT.u, 0.35, true)
    ink.unclip()
  end
  -- \flo@beetle: a beetle or a ladybird, length 1; u its length on the page in cm
  local function beetle(u)
    BT.u = u
    fine = u > K(0.25)
    local kind = X.kind
    BT.kind, BT.ground = kind, X.ground
    local t = X.tone
    if X.ground == "dark" then
      t = max(0.04, t - 0.22)
      if kind == "ladybird" then t = 0.06 end
    end
    if X.ground == "copper" then pen.accent = true end
    BT.t = t
    local ew = 0.5 * X.build
    BT.ew, BT.et, BT.tip, BT.rt, BT.sq = ew, 0.14, -0.56, 0.12, 0.7
    BT.pb, BT.pf, BT.pl, BT.so, BT.sw = 0.88 * ew, 0.62 * ew, 0.17, 0.012, 1.02
    BT.hx, BT.hr, BT.hy, BT.fs, BT.gl = 0.36 * ew, 0.065, 0.36, 1, 1
    BT.pt, BT.ht = max(0.04, t - 0.06), max(0.04, t - 0.1)
    LGP.w, LGP.j, LGP.x, LGP.ex = 0.03, 0.015, 1, ew
    local ly = 0
    if kind == "ladybird" then
      BT.et, BT.tip, BT.rt, BT.sq = 0.22, -0.5, 0.36, 1
      BT.pb, BT.pf, BT.pl, BT.so, BT.sw = 0.6 * ew, 0.4 * ew, 0.05, 0.075, 0.92
      BT.hx, BT.hr, BT.hy, BT.fs, BT.gl = 0.3 * ew, 0.055, 0.27, 0.45, 0.55
      BT.pt, BT.ht = 0.04, 0.04
      LGP.w, LGP.j, LGP.x, LGP.ex = 0.032, 0.01, 0.7, 0.8 * ew
      ly = 0.12
    end
    BT.ey = BT.et - BT.rt
    BT.rb = BT.ey - BT.tip
    BT.w = max(0.25, 0.4 * u)
    LGP.y = BT.et - ly
    local c = tone(0.05)
    for a = 1, 2 do
      ink.push()
      if a == 2 then xscale(-1) LGP.ph = -0.04 else LGP.ph = 0.04 end
      vlegs("b", c)
      ink.pop()
    end
    for a = 1, 2 do
      ink.push()
      if a == 2 then xscale(-1) end
      btfeeler(c)
      ink.pop()
    end
    bthead()
    BT.el = btelytra()
    btpaintel()
    BT.pn = btshield(BT.pb, BT.pf, BT.pl)
    btpaintpn()
    if kind ~= "ladybird" then                                    -- the scutellum
      local ya, yb = BT.et + 0.004, BT.et - 0.05
      moveto(-0.035, ya) lineto(0.035, ya) lineto(0, yb) close() fill(tone(BT.pt), 1)
    end
  end

  -- ---------------------------------------------------------------- caterpillars
  local CT = {}                             -- the one being drawn (\flo@ct...) and its leaf (\flo@hf...)
  -- \flo@hfw: the host leaf's half-width at v along it, and its midrib's x there
  local function hfw(v) return CT.hW * sin(180 * v ^ 0.8) ^ 0.85, CT.hb * v * v end
  -- \flo@hfoutline: the leaf, with the bites taken from its edge
  local function hfoutline()
    local pts = { { 0, 0 } }
    pts[25] = { CT.hb, CT.hL }
    for a = 1, 23 do
      local v = a / 24
      local h, x = hfw(v)
      local y = v * CT.hL
      local xa = x + h
      for b = 1, CT.nb do
        local t, r = abs(y - CT.by[b]), CT.br[b]
        if t < r then xa = min(xa, CT.bx[b] - sqrt(max(0, r * r - t * t))) end
      end
      pts[a + 1] = { xa, y }
      pts[49 - a] = { x - h, y }
    end
    return pts
  end
  -- \flo@hfpaint: the leaf: its stalk, its darker half, mottle, the midrib and side veins, rim
  local function hfpaint()
    local l, hL, was = CT.l, CT.hL, pen.accent
    pen.accent = false
    taper(0, -0.14, 0, -0.09, 0, -0.05, 0, 0.02, 0.028, 0.02) fill(tone(0.2), 1)
    smooth(l, true) fill(tone(CT.hft), 1)
    smooth(l, true) ink.clip()
    local c = tone(CT.hft - 0.12)
    moveto(-1, 0) lineto(0, 0)
    for a = 1, 8 do
      local v = a / 8
      local _, x = hfw(v)
      lineto(x, v * hL)
    end
    lineto(-1, hL) close() fill(c, 0.35, true)
    if fine then
      mottle(-CT.hW, 0, CT.hW, hL, 6, 0.12, CT.hft - 0.16, CT.hft + 0.18, 8)
      local _, xb = hfw(0.33)
      local _, xc = hfw(0.67)
      local _, xd = hfw(0.97)
      taper(0, 0, xb, 0.33 * hL, xc, 0.67 * hL, xd, 0.97 * hL, 0.022, 0.004) fill(WHITE, 0.5)
      for a = 1, 10 do
        local sx = a % 2 == 1 and 1 or -1
        local v = 0.1 + 0.15 * ((a - 1) // 2)
        local h, x = hfw(v)
        local y = v * hL
        local xd2, yd = x + sx * 0.85 * h, y + 0.09 * hL
        taper(x, y, x + (xd2 - x) * 0.4, y + (yd - y) * 0.15, x + (xd2 - x) * 0.75, y + (yd - y) * 0.55, xd2, yd, 0.014, 0.002)
      end
      fill(WHITE, 0.4)
    end
    smooth(l, true) stroke(tone(0.06), 0.9 * CT.u, 0.4, true)
    ink.unclip()
    pen.accent = was
  end
  -- \flo@ctangle: the body's heading at s along it: looping, curled or wavering
  local function ctangle(s)
    if CT.pose == "loop" then return 105 * cos(180 * s) end
    if CT.pose == "curl" then return 150 - 300 * s end
    return CT.wa * sin(540 * s + CT.ph)
  end
  -- \flo@ctspine: the body's 27 points, their headings, normals and half-widths
  local function ctspine()
    local x, y, a, nx, ny, w = { [0] = 0 }, { [0] = 0 }, {}, {}, {}, {}
    for k = 0, 26 do
      local v = k / 26
      if k > 0 then
        local h = ctangle((k - 0.5) / 26)
        x[k], y[k] = x[k - 1] + cos(h) / 26, y[k - 1] + sin(h) / 26
      end
      local h = ctangle(v)
      a[k], nx[k], ny[k] = h, -sin(h), cos(h)
      w[k] = CT.W * (0.7 + 0.3 * sin(180 * min(1, 1.6 * v))) * (v > 0.92 and 1 - (v - 0.92) * 2.5 or 1)
    end
    CT.x, CT.y, CT.a, CT.nx, CT.ny, CT.w = x, y, a, nx, ny, w
  end
  -- \flo@ctat: the point f half-widths across the body at point k, and g along it
  local function ctat(k, f, g)
    local w, nx, ny = CT.w[k], CT.nx[k], CT.ny[k]
    return CT.x[k] + f * w * nx + g * w * ny, CT.y[k] + f * w * ny - g * w * nx
  end
  -- \flo@ctoutline
  local function ctoutline()
    local pts = {}
    for a = 0, 26 do
      pts[a + 1] = { ctat(a, -1, 0) }
      pts[54 - a] = { ctat(a, 1, 0) }
    end
    pts[55] = { ctat(0, 0, -0.7) }
    return pts
  end
  -- \flo@ctleg: a leg at point k, from a to b half-widths out, g along, widths wa to wb
  local function ctleg(k, a, b, g, wa, wb)
    local x0, y0 = ctat(k, -a, 0)
    local x1, y1 = ctat(k, -b, g)
    seg(x0, y0, x1, y1, wa, wb)
  end
  -- \flo@ctline: the points from a to b by step, f half-widths across
  local function ctline(a, b, step, f)
    local pts = {}
    for i = 0, (b - a) // step do pts[i + 1] = { ctat(a + step * i, f, 0) } end
    return pts
  end
  local CTPRO = { 1, 9, 11, 13, 15 }
  -- \flo@ctbody: the prolegs and true legs, the body, its darker side, stripes, segments,
  -- mottle, highlight and rim
  local function ctbody()
    local W, t = CT.W, CT.t
    local c = tone(max(0.05, t - 0.08))
    for a = 1, 5 do
      if not (CT.pose == "loop" and a > 2) then ctleg(CTPRO[a], 0.5, 1.45, -0.1, 0.9 * W, 0.65 * W) end
    end
    fill(c, 1)
    c = tone(0.08)
    for a = 0, 2 do ctleg(21 + 2 * a, 0.6, 1.55, 0.35, 0.35 * W, 0.06 * W) end
    fill(c, 1)
    local b = ctoutline()
    smooth(b, true) fill(tone(t), 1)
    smooth(b, true) ink.clip()
    c = tone(t - 0.12)
    for a = 0, 26 do
      local x, y = ctat(a, 0, 0)
      if a == 0 then moveto(x, y) else lineto(x, y) end
    end
    for a = 0, 26 do lineto(ctat(26 - a, -1.4, 0)) end
    close() fill(c, 0.35, true)
    if X.stripes == "side" then
      streak(ctline(1, 25, 2, -0.2), 0.45 * W, 0.3 * W, WHITE, 0.6)
      if fine then
        c = tone(0.04)
        for a = 1, 9 do
          local x, y = ctat(1 + 2 * a, 0.08, 0)
          ell(x, y, 0.09 * W, 0.13 * W)
        end
        fill(c, 0.85)
      end
    end
    if X.stripes == "back" then streak(ctline(1, 25, 2, 0.72), 0.42 * W, 0.3 * W, tone(0.05), 0.7) end
    if fine then
      for a = 1, 12 do moveto(ctat(2 * a, -1.3, 0)) lineto(ctat(2 * a, 1.3, 0)) end
      stroke(tone(0.05), CT.jw, 0.4, true)
      mottle(-0.2, -0.4, 1.1, 0.8, 5, 0.05, t - 0.14, t + 0.18, 10)
      streak(ctline(2, 24, 2, 0.45), 0.3 * W, 0.004, WHITE, 0.4)
    end
    smooth(b, true) stroke(tone(0.05), 0.9 * CT.u, 0.6, true)
    ink.unclip()
  end
  -- \flo@cthead: the head at the front end, its eye, a highlight, the jaw
  local function cthead()
    local W = CT.W
    local hx, hy = ctat(26, 0, 0.5)
    local nx, ny = CT.nx[26], CT.ny[26]
    local xb, yb, xc, yc = ny * 0.92 * W, -nx * 0.92 * W, nx * 0.85 * W, ny * 0.85 * W
    ellipse(hx, hy, xb, yb, xc, yc) fill(tone(CT.ht), 1)
    if fine then
      circle(hx + 0.35 * xb + 0.15 * xc, hy + 0.35 * yb + 0.15 * yc, 0.13 * W) fill(tone(0.02), 1)
      circle(hx - 0.25 * xb + 0.45 * xc, hy - 0.25 * yb + 0.45 * yc, 0.1 * W) fill(WHITE, 0.6)
      seg(hx + 0.8 * xb - 0.45 * xc, hy + 0.8 * yb - 0.45 * yc, hx + 1.2 * xb - 0.75 * xc, hy + 1.2 * yb - 0.75 * yc, 0.25 * W, 0.05 * W)
      fill(tone(0.04), 1)
    end
  end
  -- \flo@cthorn: a hawkmoth caterpillar's tail horn
  local function cthorn()
    local xe, ye = ctat(5, 0.8, 0)
    local xd, yd = ctat(5, 2.4, -1.6)
    local xb, yb = ctat(5, 1.7, 0)
    local xc, yc = ctat(5, 2.4, -0.7)
    taper(xe, ye, xb, yb, xc, yc, xd, yd, 0.5 * CT.W, 0.05 * CT.W) fill(tone(0.08), 1)
  end
  -- \flo@cthair: a hair at point k, len half-widths long, g along, turned deg from square
  local function cthair(k, len, g, deg)
    local W = CT.W
    local x, y = ctat(k, 0.85, g)
    local t = CT.a[k] + 90 + deg
    seg(x, y, x + len * W * cos(t), y + len * W * sin(t), 0.1 * W, 0.02 * W)
  end
  -- \flo@cttufts: four white tufts along the back, dark pencils at both ends
  local function cttufts()
    local c = white()
    for a = 1, 4 do
      for b = 1, 9 do
        local l, g = range(1.2, 1.8), range(-0.25, 0.25)
        cthair(9 + 2 * a, l, g, (b - 5) * 8)
      end
    end
    fill(c, 0.9)
    c = tone(0.08)
    for a = 1, 5 do
      cthair(24, range(2.4, 3), 0, a * 6 - 48)
      cthair(2, range(2, 2.6), 0, a * 6 + 12)
    end
    fill(c, 0.9)
  end
  -- \flo@caterpillar: a caterpillar on its host leaf, length 1; u its length on the page in cm
  local function caterpillar(u)
    CT.u = u
    fine = u > K(0.25)
    CT.pose = X.pose
    local t = X.tone
    if X.ground == "pale" then t = min(0.6, t + 0.22) end
    CT.t, CT.W = t, X.build / 2
    CT.ht, CT.jw = max(0.05, t - 0.16), max(0.1, 0.16 * u)
    CT.wa, CT.ph = range(3, 7), range(0, 360)
    ctspine()
    -- the leaf, and where on it the caterpillar sits, eating from its edge
    CT.hL, CT.hW, CT.hb, CT.hft = 1.5, 0.36, sym(0.12), 0.66
    local h, x = hfw(0.1)
    local ox, oy = x + h, 0.1 * CT.hL
    h, x = hfw(0.62)
    local a = atan2(0.62 * CT.hL - oy, x + h - ox)
    -- straight or looping, the body is mirrored across its axis (y to -y, after the turn),
    -- so its legs hold the edge and its back is turned away from the leaf, tail to head
    -- along the edge; curled, it lies on the leaf from its tail, its legs turned in
    local curled = CT.pose == "curl"
    local rot, hy = a, oy + CT.x[26] * sin(a) + CT.y[26] * cos(a)
    if not curled then
      rot = a + atan2(CT.y[26], CT.x[26])
      hy = oy + CT.x[26] * sin(rot) - CT.y[26] * cos(rot)
    end
    CT.nb = chance(0.5) and 3 or 2
    CT.br, CT.by, CT.bx = {}, {}, {}
    local y = min(0.86 * CT.hL, max(0.3 * CT.hL, hy + 0.04))
    for b = 1, CT.nb do
      CT.br[b], CT.by[b] = range(0.07, 0.11), y
      local hh, xx = hfw(y / CT.hL)
      CT.bx[b] = xx + hh + 0.3 * CT.br[b]
      y = y + range(0.15, 0.22)
      if y > K(1.38) then y = K(1.38) end
    end
    CT.l = hfoutline()
    hfpaint()
    if X.ground == "copper" then pen.accent = true end
    ink.push() ink.translate(ox, oy) ink.rotate(rot)
    if not curled then ink.scale(1, -1) end
    ctbody() cthead()
    if X.horn == 1 then cthorn() end
    if X.tufts == 1 then cttufts() end
    ink.pop()
  end

  -- ---------------------------------------------------------------- snails
  local GS = {}                             -- the one being drawn (\flo@gs...)
  local GSBODY = { { 0, -0.78 }, { -0.01, -0.3 }, { 0, 0.3 }, { 0.04, 0.58 }, { 0.12, 0.7 }, { 0.22, 0.66 }, { 0.24, 0.5 },
    { 0.2, 0.32 }, { 0.17, 0 }, { 0.12, -0.45 }, { 0.05, -0.7 } }
  -- \flo@gsbody: the foot, longer the farther it is out
  local function gsbody()
    local pts = {}
    for i, p in ipairs(GSBODY) do
      local x = p[1] * (1 + sym(0.08))
      pts[i] = { x, p[2] * (p[2] < 0 and 0.5 + 0.5 * GS.x or 0.75 + 0.3 * GS.x) }
    end
    local tb = range(0.5, 0.6)
    GS.tb = tb
    smooth(pts, true) fill(tone(tb), 1)
    smooth(pts, true) ink.clip()
    if fine then mottle(-0.1, -0.8, 0.3, 0.8, 5, 0.1, tb - 0.25, tb + 0.15, 10) end
    moveto(-0.05, -0.8) lineto(-0.05, 0.7) lineto(0.04, 0.7) lineto(0.04, -0.8) close() fill(tone(0.15), 0.35, true)
    smooth(pts, true) stroke(tone(0.08), 0.1 * GS.u * 28.3465, 0.4, true)
    ink.unclip()
  end
  -- \flo@gsfeeler: a feeler from (x0, y0) toward (x1, y1), w wide; its tip
  local function gsfeeler(x0, y0, x1, y1, w)
    local ya, t = y0 * (0.75 + 0.3 * GS.x), 0.55 + 0.5 * GS.x
    local xd, yd = x0 + (x1 - x0) * t, ya + (y1 - y0) * t
    moveto(x0, ya) curveto(x0 + 0.02, ya + 0.12, xd - 0.04, yd - 0.1, xd, yd) stroke(tone(0.12), w, 1, false)
    return xd, yd
  end
  -- \flo@gsfeelers: two long feelers with eyes, one short
  local function gsfeelers()
    local w, v = max(0.35, 0.3 * GS.u), max(0.28, 0.24 * GS.u)
    local x, y = gsfeeler(0.17, 0.66, 0.36, 1.02, w)
    circle(x, y, 0.028) fill(tone(0.05), 1)
    x, y = gsfeeler(0.1, 0.7, 0.18, 1.04, w)
    circle(x, y, 0.028) fill(tone(0.05), 1)
    gsfeeler(0.05, 0.68, 0, 0.84, v)
  end
  -- \flo@gsshell: the shell as Raup's coiled tube (the shells report, part A, steps 1-8). A
  -- ring 1 across the coil and S along it, y0 out from the coiling axis and x0 along it,
  -- sweeps through the whorls, W times larger a turn; seen from the spire's side, the axis
  -- tilted al toward the viewer, a ring shows as an ellipse whose two extremes across the
  -- coil are the whorl's edges there. The whorls are painted from the mouth in, each older one
  -- over the one after it, so its outer edge is the suture on the next. The spire leans back
  -- and the mouth faces the foot.
  local GSTEP = 18                          -- rings a whorl
  -- \flo@gsring: at growth angle th (0 the mouth) the ring's center and the offset from it to
  -- the whorl's outer edge (the inner edge is the other way), with the edge's wobble
  local function gsring(th)
    local lam = GS.W ^ (th / 360)
    local a = th + GS.ph
    local c, s = cos(a), sin(a)
    local u0, v0 = GS.y0 * c, GS.y0 * s * GS.ca - GS.x0 * GS.sa
    local tx, ty = GS.k * u0 - GS.y0 * s, GS.k * v0 + GS.y0 * c * GS.ca
    local l = sqrt(tx * tx + ty * ty)
    local p, q = (ty * c - tx * s * GS.ca) / l, -tx * GS.S * GS.sa / l
    local h = sqrt(p * p + q * q)
    local w = lam * (1 + 0.025 * sin(3 * a + GS.w1) + 0.018 * sin(5 * a + GS.w2)) / h
    return lam * u0, lam * v0, w * c * p, w * (s * GS.ca * p + GS.S * GS.sa * q), tx, ty, p / h, q / h
  end
  -- \flo@gswhorl: whorl j (1 the body whorl, ending at the mouth): its outer edge O, inner edge
  -- I, the outer edge of the whorl before it, which shows on this one as the suture (S), and
  -- the light on it (L, the outward normal toward the upper left of the page)
  local function gswhorl(j)
    local t0, t1 = max(-360 * GS.n, -360 * j), -360 * (j - 1)
    local O, I, U, L = {}, {}, {}, {}
    for b = 0, GSTEP do
      local th = t0 + (t1 - t0) * b / GSTEP
      local cx, cy, ex, ey, tx, ty = gsring(th)
      O[b + 1], I[b + 1] = { cx + ex, cy + ey }, { cx - ex, cy - ey }
      local sx, sy, fx, fy = gsring(th - 360)
      U[b + 1] = { sx + fx, sy + fy }
      local l = sqrt(tx * tx + ty * ty)
      L[b + 1] = (ty * GS.lx - tx * GS.ly) / l
    end
    return { O = O, I = I, S = U, L = L, t0 = t0, t1 = t1 }
  end
  -- the points f of the way from the suture to the outer edge, from b0 to b1
  local function gsacross(w, f, b0, b1)
    local pts = {}
    for b = b0, b1 do
      local o, q = w.O[b], w.S[b]
      pts[#pts + 1] = { q[1] + f * (o[1] - q[1]), q[2] + f * (o[2] - q[2]) }
    end
    return pts
  end
  local function gsshell()
    local W, n = X.expansion, X.whorls
    local D = X.opencoil == 1 and 1.15 / W or 0.03
    GS.W, GS.n, GS.S = W, n, X.aperture
    GS.y0 = (1 + D) / (1 - D)
    GS.x0 = 3 * X.spire / W * GS.y0           -- Raup's T: a fast-widening coil stands lower
    GS.k = math.log(W) / 6.28319
    local al = range(34, 50)
    GS.ca, GS.sa = cos(al), sin(al)
    GS.ph = -35
    GS.w1, GS.w2 = range(0, 360), range(0, 360)
    -- the light from the upper left of the page, in the shell's frame (turned by 130 degrees)
    GS.lx, GS.ly = cos(265), sin(265)
    local J = math.ceil(n)
    local whorls = {}
    for j = 1, J do whorls[j] = gswhorl(j) end
    -- the mouth: the ring at th = 0, an ellipse c + a1 cos f + a2 sin f (\flo@gsmouth); the
    -- body whorl's outer edge ends at f = fs, and its far side runs on in the direction of growth
    local mcx, mcy, _, _, mtx, mty, cp, sp = gsring(0)
    local a1x, a1y, a2y = cos(GS.ph), sin(GS.ph) * GS.ca, GS.S * GS.sa
    local function ring(f, k) return { mcx + k * cos(f) * a1x, mcy + k * (cos(f) * a1y + sin(f) * a2y) } end
    local fs = atan2(sp, cp)
    local dir = ((-a1x * sp) * mtx + (-a1y * sp + a2y * cp) * mty > 0) and 30 or -30
    local mouth = {}
    for b = 0, 11 do mouth[b + 1] = ring(30 * b, 1) end
    -- the frame: the spire toward -40 degrees on the page; the coil 0.98 across (a tall spire
    -- reaches farther, up to 1.4), centered where the shell has always been
    local x0, x1, y0, y1, u0, u1, v0, v1 = 1e9, -1e9, 1e9, -1e9, 1e9, -1e9, 1e9, -1e9
    local function bound(p)
      local x, y = p[1] * cos(-130) - p[2] * sin(-130), p[1] * sin(-130) + p[2] * cos(-130)
      x0, x1, y0, y1 = min(x0, x), max(x1, x), min(y0, y), max(y1, y)
      u0, u1, v0, v1 = min(u0, p[1]), max(u1, p[1]), min(v0, p[2]), max(v1, p[2])
    end
    for j = 1, J do for _, p in ipairs(whorls[j].O) do bound(p) end end
    for _, p in ipairs(mouth) do bound(p) end
    local sc = 0.98 / max(u1 - u0, 0.7 * (v1 - v0))
    ink.push()
    ink.translate(0.4 - (x0 + x1) / 2 * sc, -0.03 - (y0 + y1) / 2 * sc)
    ink.rotate(-130) ink.scale(sc)
    local u = GS.u * sc                        -- cm on the page per unit of the shell's frame
    local lw = 0.06 * GS.u * 28.3465
    local tn = GS.tn
    for j = 1, J do
      local w = whorls[j]
      local poly = {}
      for b = 1, GSTEP + 1 do poly[b] = w.O[b] end
      if j == 1 then
        for b = 1, 5 do poly[#poly + 1] = ring(fs + dir * b, 1) end      -- the mouth's far side
      end
      for b = GSTEP + 1, 1, -1 do poly[#poly + 1] = w.I[b] end
      local size = (GS.y0 + 1) * GS.W ^ (-(j - 1)) * u          -- the whorl across, in cm
      local t = tn + sym(0.03)
      if size > K(0.02) then
        smooth(poly, true) fill(tone(t), 1)
        smooth(poly, true) ink.clip()
        local big = size > K(0.12)
        if big then
          -- a few blotches; the shadow under the suture; the light on the shoulder
          local bx0, bx1, by0, by1 = 1e9, -1e9, 1e9, -1e9
          for _, p in ipairs(poly) do bx0, bx1, by0, by1 = min(bx0, p[1]), max(bx1, p[1]), min(by0, p[2]), max(by1, p[2]) end
          if j <= 2 then mottle(bx0, by0, bx1, by1, fine and 4 or 2, 0.2 * (bx1 - bx0), t - 0.16, t + 0.2, fine and 6 or 0) end
          local sw = 0.0
          for b = 1, GSTEP + 1 do
            local o, q = w.O[b], w.S[b]
            sw = sw + sqrt((o[1] - q[1]) ^ 2 + (o[2] - q[2]) ^ 2)
          end
          sw = sw / (GSTEP + 1)                                     -- the band's visible width
          streak(w.S, sw * (0.15 + 0.3 * X.suture), sw * (0.15 + 0.3 * X.suture), tone(0.08), 0.12 + 0.2 * X.suture)
          local b0, b1 = nil, nil
          for b = 1, GSTEP + 1 do
            if w.L[b] > K(0.15) then b0 = b0 or b b1 = b end
          end
          if j <= 3 and b0 and b1 - b0 >= 3 then
            streak(gsacross(w, 0.62, b0, b1), sw * 0.06, sw * 0.24, WHITE, j == 1 and 0.6 or 0.42)
          end
          -- color bands along the body whorl and the one before it
          if j <= 2 and X.shellbands > 0 then
            for a = 1, X.shellbands do
              streak(gsacross(w, 0.15 + 0.7 * a / (X.shellbands + 1), 1, GSTEP + 1), 0.003, sw * 0.7 / (X.shellbands + 1), tone(0.06), 0.38)
            end
          end
          -- sculpture: ribs across the whorl where they stand 0.5 mm apart, or cords along it
          if X.sculpture == "ribs" and X.ribs > 0 and size * 3.14159 / X.ribs > K(0.05) then
            local nr = j < J and X.ribs or floor(X.ribs * (w.t1 - w.t0) / 360 + 0.5)
            for r = 1, nr do
              local bb = 1 + nediv(GSTEP * (2 * r - 1), 2 * nr)
              local o, q = w.O[bb], w.S[bb]
              moveto(q[1], q[2]) lineto(o[1], o[2]) stroke(tone(0.05), 0.25 * lw, 0.3, true)
            end
          elseif X.sculpture == "cords" and size > K(0.25) then
            for a = 1, 3 do
              smooth(gsacross(w, a / 4, 1, GSTEP + 1), false) stroke(tone(0.05), 0.3 * lw, 0.35, true)
            end
          end
          -- growth lines near the mouth, parallel to its lip
          if j == 1 and fine then
            for g = 1, 3 do
              local bb = GSTEP + 1 - g - (chance(0.375) and 1 or 0)
              local o, q = w.O[bb], w.S[bb]
              moveto(q[1], q[2]) lineto(o[1], o[2]) stroke(tone(0.1), 0.3 * lw, 0.18, true)
            end
          end
        end
        smooth(w.O, false) stroke(tone(0.05), lw, 0.55, true)
        ink.unclip()
      end
      if j == 1 then
        -- the mouth: the snail's body in it, or the pale seal of one withdrawn; the lip
        local inner = {}
        for b = 1, 12 do inner[b] = { mcx + 0.7 * (mouth[b][1] - mcx), mcy + 0.7 * (mouth[b][2] - mcy) } end
        smooth(inner, true)
        if X.withdrawn == 1 then fill(plain(0.9), 0.95) else fill(plain(GS.tb - 0.18), 1) end
        smooth(inner, true) stroke(tone(0.05), 0.7 * lw, 0.55, true)
        if X.lip == 1 then
          local lip = {}
          for b = 0, 6 do lip[b + 1] = ring(fs + dir * b, 0.9) end
          streak(lip, 0.03, 0.06, WHITE, 0.8)
        end
      end
    end
    ink.pop()
  end
  -- \flo@snail: a snail, its shell 1 wide; u the shell's width on the page in cm. A snail
  -- withdrawn into its shell shows no foot and no feelers
  local function snail(u)
    GS.u = u
    fine = u > K(0.25)
    GS.x = X.extension
    local tn = X.tone - 0.06
    if X.ground == "pale" then tn = min(0.72, tn + 0.25) end
    GS.tn = tn
    GS.tb = 0.55
    ink.push()
    if X.hand == "sinistral" then xscale(-1) end
    local was = pen.accent
    pen.accent = false
    if X.withdrawn == 0 then gsbody() gsfeelers() end
    pen.accent = was
    if X.ground == "copper" then pen.accent = true end
    gsshell()
    ink.pop()
  end

  -- ---------------------------------------------------------------- birds
  local AV = {}                             -- the one being drawn (\flo@av...)
  local AVF = { 0.12, 0.35, 0.6, 0.82, 0.95 }
  -- \flo@avfeather: a tail feather len long, along +y
  local function avfeather(len)
    local pts = { { 0, 0 } }
    pts[7] = { 0, len }
    for b = 1, 5 do
      local v = AVF[b]
      local x = 0.055 * sin(180 * v ^ 0.4) ^ 0.6
      pts[b + 1], pts[13 - b] = { x, v * len }, { -x, v * len }
    end
    return pts
  end
  local AVBODY = { { 0.37, 0.56 }, { 0.3, 0.665 }, { 0.15, 0.64 }, { -0.03, 0.54 }, { -0.22, 0.4 }, { -0.31, 0.3 }, { -0.27, 0.22 },
    { -0.08, 0.12 }, { 0.12, 0.12 }, { 0.27, 0.27 }, { 0.35, 0.42 } }
  -- \flo@avbody: the tail's four feathers fanned, the body, its breast, mottle, a nectar
  -- bird's bright throat or a seed-eater's streaks, the cap, the rim
  local function avbody()
    AV.lw = max(0.3, 0.3 * AV.u)
    for a = 1, 4 do
      local len = range(X.tail - 0.03, X.tail + 0.03)
      local ang = range(-150, -140) + AV.fan * a - 90
      ink.push() ink.translate(-0.26, 0.28) ink.rotate(ang)
      local f = avfeather(len)
      smooth(f, true) fill(tone(0.12 + 0.05 * a), 1)
      smooth(f, true) stroke(tone(0.05), AV.lw, 0.5, false)
      ink.pop()
    end
    local b = {}
    for i, p in ipairs(AVBODY) do
      local x = p[1] + sym(0.012)
      b[i] = { x, p[2] + sym(0.012) }
    end
    local t = AV.t
    smooth(b, true) fill(tone(t), 1)
    smooth(b, true) ink.clip()
    for a = 1, 4 do smooth(blob(0.17, 0.27, 0.08 + 0.04 * a, 0.06 + 0.03 * a, 0.2, 9), true) fill(WHITE, 0.2) end
    if fine then mottle(-0.35, 0.1, 0.4, 0.7, 10, 0.07, t - 0.2, t + 0.2, 10)
    else mottle(-0.35, 0.1, 0.4, 0.7, 4, 0.07, t - 0.2, t + 0.2, 0) end
    if X.kind == "hummingbird" then
      smooth(blob(0.3, 0.47, 0.075, 0.05, 0.15, 8), true) fill(accent(0), 0.85)
      if fine then smooth(blob(0.31, 0.49, 0.03, 0.016, 0.2, 7), true) fill(WHITE, 0.5) end
    elseif fine then
      for _ = 1, 14 do
        local x, y, a = range(0.08, 0.3), range(0.16, 0.42), range(-20, 20)
        ellipse(x, y, 0.012 * cos(a), 0.012 * sin(a), -0.022 * sin(a), 0.022 * cos(a))
      end
      fill(tone(0.1), 0.7)
    end
    smooth(blob(0.24, 0.66, 0.15, 0.08, 0.1, 9), true) fill(tone(0.08), 0.6, true)
    smooth(b, true) stroke(tone(0.05), 0.07 * AV.u * 28.3465, 0.5, true)
    ink.unclip()
  end
  local AVWING = { { 0.17, 0.47 }, { 0.04, 0.52 }, { -0.12, 0.45 }, { -0.28, 0.33 }, { -0.44, 0.22 }, { -0.35, 0.23 },
    { -0.38, 0.19 }, { -0.26, 0.21 }, { -0.28, 0.17 }, { -0.12, 0.23 }, { 0.05, 0.31 }, { 0.15, 0.4 } }
  -- \flo@avwing: the folded wing, mottled, its flight feathers lifted out
  local function avwing()
    local tw = AV.t - 0.1
    smooth(AVWING, true, 0.8) fill(tone(tw), 1)
    smooth(AVWING, true, 0.8) ink.clip()
    mottle(-0.45, 0.15, 0.2, 0.6, fine and 6 or 3, 0.07, tw - 0.15, tw + 0.2, 0)
    for a = 1, 5 do
      local y = 0.28 + 0.04 * a
      local op = range(0.45, 0.7)
      taper(0.12, y + 0.05, 0, y + 0.03, -0.15, y - 0.03, -0.3 - 0.02 * a, y - 0.1 - 0.01 * a, 0.025, 0.003) fill(WHITE, op)
    end
    smooth(AVWING, true, 0.8) stroke(tone(0.04), 0.06 * AV.u * 28.3465, 0.5, true)
    ink.unclip()
  end
  local AVHOVER = { { 0, 0.02 }, { 0.12, 0.07 }, { 0.32, 0.085 }, { 0.5, 0.05 }, { 0.56, 0 }, { 0.48, -0.03 }, { 0.28, -0.045 },
    { 0.08, -0.03 } }
  -- \flo@avhover: a hovering bird's wing, a blur in three places
  local function avhover()
    local tw = AV.t - 0.1
    for a = 1, 3 do
      ink.push() ink.translate(0.06, 0.5) ink.rotate(70 + 25 * a)
      if a == 2 then
        smooth(AVHOVER, true) fill(tone(tw), 0.4)
        smooth(AVHOVER, true) stroke(tone(0.05), AV.lw, 0.4, false)
      else smooth(AVHOVER, true) fill(tone(tw), 0.18) end
      ink.pop()
    end
  end
  -- \flo@avcrest
  local function avcrest()
    for a = 1, 4 do
      local len = range(0.08, 0.12)
      local x, y, t = 0.26 - 0.04 * a, 0.65 - 0.01 * a, 140 + 8 * a
      seg(x, y, x + len * cos(t), y + len * sin(t), 0.03, 0.004)
    end
    fill(tone(0.1), 1)
  end
  -- \flo@avbill: a seed-eater's cone, a nectar bird's straight or curved one
  local function avbill()
    local b, c = X.bill, tone(0.05)
    if b == "conical" then
      moveto(0.355, 0.585) lineto(0.47, 0.545) lineto(0.36, 0.51) close() fill(c, 1)
      moveto(0.36, 0.548) lineto(0.45, 0.546) stroke(WHITE, 0.2, 0.6, false)
    end
    if b == "straight" then moveto(0.35, 0.575) lineto(0.69, 0.55) lineto(0.352, 0.538) close() fill(c, 1) end
    if b == "curved" then taper(0.35, 0.556, 0.46, 0.562, 0.58, 0.535, 0.66, 0.475, 0.036, 0.006) fill(c, 1) end
  end
  -- \flo@bird: a bird, length 1; u its length on the page in cm
  local function bird(u)
    AV.u = u
    fine = u > K(0.3)
    local t = X.tone
    if X.ground == "dark" then t = t - 0.12 end
    if X.ground == "copper" then pen.accent = true end
    AV.t, AV.fan = t, 7
    local hover = X.pose == "hover"
    ink.push()
    if hover then
      AV.fan = 14
      ink.translate(0.05, 0.35) ink.rotate(22) ink.translate(-0.05, -0.35)
    end
    avbody()
    if X.crest == 1 then avcrest() end
    if hover then avhover() else avwing() end
    avbill()
    circle(0.285, 0.585, 0.03) fill(WHITE, 0.95)
    circle(0.29, 0.585, 0.018) fill(tone(0.03), 1)
    circle(0.284, 0.592, 0.006) fill(WHITE, 1)
    if not hover then                                             -- the feet on the perch
      moveto(0.02, 0.14) lineto(0, 0) moveto(-0.05, 0) lineto(0.05, 0.005)
      moveto(0.1, 0.14) lineto(0.08, 0) moveto(0.03, 0) lineto(0.13, 0.005)
      stroke(tone(0.06), max(0.4, 0.3 * u), 1, false)
    end
    ink.pop()
  end

  -- ---------------------------------------------------------------- the visitor
  -- \flo@visitor as the margin's organ: the size in cm (the wingspan of a butterfly or
  -- moth), as TeX holds it; the visitor's unit on the page in cm is found as TeX finds it
  -- (the size in pt, then divided back), since it decides the level of detail. With pic
  -- (the pic's \flo@visitor), o.size is in pt already, in sp, the drawing in pt, and
  -- o.draw (if given) is the drawing's seed, as \flo@vispicsetup takes it
  local DRAW = { bee = bee, fly = bee, odonate = odo, beetle = beetle, larva = caterpillar, snail = snail, bird = bird }
  local HALFCM = pm.sp("56.90552")              -- half a wingspan: pt in two cm, as TeX holds it
  local sampled = {}
  return function(o, pic)
    local role = o.role or "any"
    local key = string.format("%d/%s/%s", o.seed, role, o.traits or "")
    X = sampled[key]
    if not X then
      -- \florilegiumvisitor: its traits (some fixed), the reals held to the scaled point
      X = {}
      for k, v in pairs(species.vsample(o.seed, role, nil, o.traits and species.fixes(o.traits, true))) do
        X[k] = math.type(v) == "float" and K(v) or v
      end
      sampled[key] = X
    end
    local d = pic and o.draw or (o.seed == 2147483646 and 1 or o.seed + 1)   -- drawn from seed + 1
    G = ink.pgfrng(1) G.load(d) G.step() G.step()
    local pt, k = o.size, o.size / 65536
    if not pic then pt, k = pm.mulf(floor(o.size * 65536 + 0.5), CM), o.size end    -- size*28.45276
    if X.group == "lepidoptera" then
      ink.scale(k / 2)
      lepid(pm.div(pt, HALFCM) / 65536)                                            -- (pt)/56.90552
    else
      ink.scale(k)
      DRAW[X.group](pm.div(pt, CM) / 65536)                                        -- (pt)/28.45276
    end
  end
end)()

-- ================================================================ organs
-- the layout's questions, in cm: the disc an inflorescence at scale k fills (its center's
-- offset along the axis, its radius, its kind), and how much later than the top one at
-- height h (0..1) opens
local KIND = { solitary = "solitary", cumbel = "umbel", umbel = "umbel", corymb = "umbel", dichasium = "umbel",
  raceme = "raceme", spike = "raceme", panicle = "raceme", thyrse = "raceme", monochasium = "raceme",
  head = "head", catkin = "catkin" }
function M.unitdisc(s, k)
  local kind = KIND[s.inflo] or "blossoms"
  local fd = min(2.8, 0.7 + 0.032 * s.size) * k
  if kind == "umbel" then return 0.69 * k, 0.86 * k, kind end
  if kind == "raceme" then return 0.95 * k, 0.75 * k, kind end
  if kind == "blossoms" then return 0.5 * fd, 0.8 * fd, kind end
  if kind == "head" then return 0.8 * k, 0.5 * k, kind end
  if kind == "catkin" then return 0.2 * k, 0.9 * k, kind end
  return 0, 0.5 * fd, kind
end
-- whether its flowers are seen in profile (bells, urns, tubes, trumpets, pea and two-lipped
-- flowers, a columbine held sideways): they reach out from the stalk, not round it
function M.profile(s)
  local fm = s.form
  return TUBES[fm] or fm == "papilionaceous" or fm == "bilabiate" or fm == "spurred" and s.orient ~= "erect" or false
end
local BELOW = { raceme = true, spike = true, catkin = true }
function M.lateness(s, h) return BELOW[s.inflo] and 0.4 * h or 0.4 * (1 - h) end

-- a species given by seed and role only (and traits fixed, "compound=palmate, ...") is
-- sampled (once)
local species = require("florilegium-species")
local sampled = {}
local function full(s)
  if s.plan then return s end
  local key = s.seed .. "/" .. s.role .. "/" .. (s.traits or "")
  if not sampled[key] then sampled[key] = species.sample(s.seed, s.role, nil, s.traits and species.fixes(s.traits)) end
  return sampled[key]
end
-- One organ of species s (a table from florilegium-species, or just its seed and role),
-- drawn from pgf seed z at the origin with its axis along +y, in cm, under ink's current
-- transform. o by kind:
--   unit     k (scale), season, late: the inflorescence
--   budding  k: buds on stalks       tendril  size     bloom  d (diameter cm), stage
--   ghost    d: a plain pale flower of the species, as the garden's ghost
--   leaf     len (cm), bend, lc (0..1, the autumn draw), season, fore (a foreshortening),
--            sheath ({direction from the leaf's in degrees, width in the leaf's units})
--   frond    len (cm), unroll (0..1), arch (degrees), fertile, under, lc, season: a fern's
--   visitor  seed, role, size (cm; a wingspan for butterflies and moths): the visitor
--            \florilegiumvisitor{seed}{role} samples, drawn from seed + 1 (s and z unused)
-- Returns false for a kind not drawn here.
local DRAWN = { unit = true, budding = true, tendril = true, bloom = true, ghost = true, leaf = true, frond = true,
  visitor = true }
-- the organ's numbers as TeX holds them, in whole scaled points (K): a length the layout
-- caps at 19/16 cm then gives the library's count of teeth, not one less
local function held(o)
  local h = {}
  for k, v in pairs(o) do
    if type(v) == "number" then h[k] = K(v)
    elseif type(v) == "table" then h[k] = { K(v[1]), K(v[2]) }
    else h[k] = v end
  end
  return h
end
-- a ghost: a plain rotate flower of the species, d cm across (no more than five petals),
-- in the pale the caller set; the margin's ghost and the plants pic's
local function ghostflower(d)
  local tq = stone()
  local g = {}
  for k, v in pairs(S) do g[k] = v end
  g.form = "rotate"
  if g.merosity > 6 then g.merosity = 5 end
  S = g
  ink.scale(d)
  M.sflower(d * 28.45276, { tone = tq, notchp = 0.4, notchk = 1 })
end
function M.draw(kind, s, z, o)
  if not DRAWN[kind] then return false end
  o = held(o)
  if kind == "visitor" then
    M.fresh()
    pen.lift, pen.accent = 0, false
    ink.push() visitor(o) ink.pop()
    pen.accent = false
    return true
  end
  S = full(s)
  G = ink.pgfrng(z) G.step() G.step()             -- \flo@drawseed
  M.fresh()
  pen.lift, pen.accent = 0, false
  ink.push()
  if kind == "unit" then
    local st = 1.2 * o.season - o.late + sym(0.08) + S.phenology
    M.unit(o.k, st)
  elseif kind == "budding" then
    M.budding(min(1.1, fdiam(o.k)))
  elseif kind == "tendril" then
    ink.rotate(90) M.tendril(o.size)
  elseif kind == "bloom" then
    M.bloom(o.d, o.stage, {})
  elseif kind == "leaf" or kind == "frond" then
    ink.scale(o.len)
    if o.fore then yscale(o.fore) end
    -- autumn turns some leaves copper; a spring leaf is young and pale
    if S.turns == 1 and o.lc < max(0, o.season - 0.9) then pen.accent = true end
    if o.season < 0.5 then pen.lift = 0.3 * (1 - o.season / 0.5) end
    if kind == "leaf" then M.leaf(o.len, o.bend, o.sheath)
    else M.frond(leafu(o.len), { unroll = o.unroll, arch = o.arch, fertile = o.fertile, under = o.under }) end
  else                                            -- the ghost: a plain rotate flower, pale
    pen.lift = 0.62
    ghostflower(o.d)
  end
  ink.pop()
  return true
end

-- ================================================================ M8f7: the pics
-- The TikZ library's pics, drawn here as its TikZ code draws them: v1.3's leaf
-- (\flo@vleaf), a fern's crown (\flo@ferncrown), the garden of v1.3's plants (\flo@garden)
-- and the pics that wrap them and the organs above. In a function of its own, whose locals
-- count afresh (the main chunk's 200).
local pic = (function()
  -- \flo@calc{x/28.45276}: a pic's length in pt (sp) in cm, as TeX divides it
  local function sizecm(pt) return pm.div(pt, CM) / 65536 end
  -- a count TeX takes from a length the user gives (a 6 cm plant's \flo@pH, held in sp),
  -- which sits on the count's thresholds: int(x/d + a), d and a in sp, as pgfmath finds it
  local function texint(x, d, a)
    return pm.int(pm.add(pm.div(floor(x * 65536 + 0.5), d), a or 0)) // 65536
  end
  local function drawseed(z) G = ink.pgfrng(1) G.load(z) G.step() G.step() end   -- \flo@drawseed

  -- ---------------------------------------------------------------- one stream per organ
  local HALF = 1073741823
  -- \flo@seedorgan: the layout's generator a step on; the organ's own seed from there
  local function seedorgan()
    G.step()
    local z = G.save()
    return z > HALF and z - HALF or z + HALF
  end
  -- \flo@organ: f drawn from seed z in a group of its own (the pen as it was after it), the
  -- layout's generator where it was
  local function organ(z, f)
    local saved, lift, acc = G.save(), pen.lift, pen.accent
    G.load(z) G.step() G.step()
    ink.push() f() ink.pop()
    pen.lift, pen.accent = lift, acc
    G.load(saved)
  end
  -- \flo@autumnleaf: copper in autumn (lc the leaf's draw), young and pale in spring
  local function autumnleaf(lc, season)
    if S.turns == 1 and lc < max(0, season - K(0.9)) then pen.accent = true end
    if season < K(0.5) then pen.lift = 0.3 * (1 - season / 0.5) end
  end

  -- ---------------------------------------------------------------- v1.3's leaf
  -- \flo@vleaf: flora.leaf along +y, length 1, its width, bend, lobes, their depth and its
  -- tone all drawn; u its cm per unit
  local function vleaf(u)
    local lW = range(0.20, 0.27)
    local lbend = sym(0.25)
    local lobes = int(3, 5)
    local depth = range(0.3, 0.7)
    local lt = range(0.26, 0.38)
    local fine, bn = false, 24
    if u > K(0.6) then fine, bn = true, 36 end
    local bodyw, ent = 1, true
    if depth > 0 then bodyw, ent = 1 - 0.75 * depth, false end
    local function spine(v) return lbend * v * v - 0.3 * lbend * v, v end              -- \flo@lspine
    -- \flo@lenv, in pgfmath's own pow, as the fronds' pinnae are placed: it is 2% off near
    -- 1 - v = 0.13, where it sets a top lobe's reach (0.3 bp on a 4 cm leaf)
    local function env(v)
      if v > 0 and v < 1 then return lW * 2.3 * ppow(v, 0.55) * ppow(1 - v, 0.95) end
      return 0
    end
    local function teeth(v) return 0.006 * (1 - abs(2 * (v * 9 - floor(v * 9)) - 1)) end   -- \flo@lteeth
    local function tangent(v)                                                         -- \flo@ltan
      local ax, ay = spine(max(0, v - 0.01))
      local bx, by = spine(min(1, v + 0.01))
      local dx, dy = bx - ax, by - ay
      local l = sqrt(dx * dx + dy * dy)
      return dx / l, dy / l
    end
    -- the body: a blade along the spine, toothed when it has no lobes
    local ps = {}
    for k = 0, bn do ps[k + 1] = { spine(k / bn) } end
    local body = blade(ps, function(_, v) return max(0, env(v) * bodyw + (ent and teeth(v * 3) or 0)) end)
    local shapes, veins = { body }, {}
    -- the lobes, grown out of the body along the spine
    if depth > 0 then
      for kc = 0, 1 do
        local side = kc == 0 and -1 or 1
        for ka = 1, lobes do
          local lv = 0.1 + 0.72 * (ka - (side > 0 and 0.25 or 0.75)) / lobes
          local lx, ly = spine(lv)
          local tx, ty = tangent(lv)
          local reach = env(min(0.95, lv + 0.08)) * range(0.95, 1.15)
          local ang = range(38, 58)
          local c, sn = cos(ang), sin(ang) * side
          local dx, dy = tx * c - ty * sn, tx * sn + ty * c
          local L = reach / sin(ang) * 0.95
          local lb = sym(0.25) - side * 0.15
          local function at(t) return lx + dx * L * t - dy * lb * L * t * t, ly + dy * L * t + dx * lb * L * t * t end
          local lp = {}
          for k = 0, 11 do lp[k + 1] = { at(k / 11) } end
          local ln = normals(lp)
          local ll, lr = {}, {}
          for k = 0, 11 do
            local v = k / 11
            local e = v > 0.2 and teeth(v * 1.4 + ka) * L * 4 or 0
            local g = v > 0 and max(0, sin(180 * v ^ 0.62)) ^ 0.75 or 0
            local wa = max(0, L * range(0.26, 0.3) * g + e)
            local wb = max(0, L * range(0.26, 0.3) * g + e)
            local p, q = lp[k + 1], ln[k + 1]
            ll[k + 1] = { p[1] + q[1] * wa, p[2] + q[2] * wa }
            lr[k + 1] = { p[1] - q[1] * wb, p[2] - q[2] * wb }
          end
          -- the lobe's vein: the base, the middle and the end of its spine
          local mx, my = at(0.45)
          local ex, ey = at(0.92)
          veins[#veins + 1] = { lx, ly, mx, my, ex, ey }
          for k = 11, 2, -1 do ll[#ll + 1] = lr[k] end
          shapes[#shapes + 1] = ll
        end
      end
    end
    local function trace() for _, s in ipairs(shapes) do smooth(s, true, 0.8) end end
    trace() fill(tone(lt), 1)
    trace() ink.clip()
    -- the darker half
    local sd = chance(0.5) and 1 or -1
    for k = 0, 30 do
      local x, y = spine(k / 30)
      if k == 0 then moveto(x, y) else lineto(x, y) end
    end
    lineto(sd * 2, 1) lineto(sd * 2, 0) close()
    fill(tone(0.12), 0.28, true)
    if fine then mottle(-lW * 2.5, 0, lW * 2.5, 1, 13, 0.11, lt - 0.2, lt + 0.22, 12, true)
    else mottle(-lW * 2.5, 0, lW * 2.5, 1, 6, 0.11, lt - 0.2, lt + 0.22, 0, true) end
    trace() stroke(tone(0.06), 1.3 * u, 0.22, true)
    -- midrib and veins in white
    local mid = {}
    for k = 0, 10 do mid[k + 1] = { spine(k / 10) } end
    streak(mid, 0.03, 0.003, WHITE, 0.78)
    for _, v in ipairs(veins) do
      local op = range(0.5, 0.75)
      taper(v[1], v[2], v[3], v[4], v[3] + (v[5] - v[3]) * 0.5, v[4] + (v[6] - v[4]) * 0.5, v[5], v[6], 0.017, 0.002)
      fill(WHITE, op)
    end
    if #veins == 0 then
      for ka = 1, 9 do
        local v = (ka - 0.3) / 10 * 0.9
        local sg = ka % 2 == 1 and -1 or 1
        local lx, ly = spine(v)
        local tx, ty = tangent(v)
        local nx, ny = -ty * sg, tx * sg
        local L = env(v) * bodyw * range(0.75, 0.95)
        local op = range(0.45, 0.7)
        taper(lx, ly, lx + nx * L * 0.4, ly + ny * L * 0.4, lx + nx * L * 0.75 + tx * L * 0.3, ly + ny * L * 0.75 + ty * L * 0.3,
          lx + nx * L * 0.95 + tx * L * 0.6, ly + ny * L * 0.95 + ty * L * 0.6, 0.014, 0.002)
        fill(WHITE, op)
      end
    end
    ink.unclip()
    if ent then smooth(body, true, 0.8) stroke(tone(0.05), 0.18, 0.4, false) end
  end

  -- ---------------------------------------------------------------- a fern's crown
  -- \flo@ferncrown: three to five fronds from a crown at the origin, the longest gH cm, and a
  -- dimorphic fern's fertile one from late spring; in cm
  local function ferncrown(gH, season)
    ink.scale(28.45276)
    local nf = int(3, 5)
    for ga = 1, nf do
      local f = (ga - (nf + 1) / 2) / ((nf - 1) / 2)
      local la = 90 - 44 * f + sym(8)
      local ac = (f < 0 and 1 or -1) * (20 + 70 * S.curl) * range(0.8, 1.2) * (0.4 + 0.6 * abs(f))
      local ll = gH * (1 - 0.22 * abs(f)) * range(0.85, 1)
      local un = max(0, min(1, (season - 0.1 - S.phenology - range(0, 0.3)) / 0.4))
      local uf = chance(0.4)
      local lc = G.rnd()
      organ(seedorgan(), function()
        local under = false
        if un >= K(0.97) then under = uf end
        ink.translate(0.06 * f, 0) ink.rotate(la - 90) ink.scale(ll)
        autumnleaf(lc, season)
        M.frond(leafu(ll), { unroll = un, arch = ac, fertile = false, under = under })
      end)
    end
    if S.dimorphic == 1 and season > K(0.55) then
      local la = 90 + sym(6)
      local ac = sym(12)
      local ll = gH * range(0.55, 0.7)
      organ(seedorgan(), function()
        ink.rotate(la - 90) ink.scale(ll)
        M.frond(leafu(ll), { unroll = 1, arch = ac, fertile = true, under = false })
      end)
    end
  end

  -- ---------------------------------------------------------------- the garden
  -- its macros (R.pH is \flo@pH ...), shared as TeX shares them; the 5 mm cells taken on
  -- this layer's walk, and the layer
  local R, ROOM, layer
  local LSTEM, LLEAF, LAIRY, LBLOOM = 2, 3, 4, 5
  local function at(l) return layer == l end
  -- \flo@room: the 5 mm cells whose centers lie in the disc, marked (mark) or counted: how
  -- many are taken, of how many
  local function room(mark, x, y, r)
    local ox, oy, o = floor(x * 10 + 1000), floor(y * 10 + 1000), floor(max(3, r * 10))
    local n, t = 0, 0
    for i = nediv(ox - o - 2, 5), nediv(ox + o - 2, 5) do
      for j = nediv(oy - o - 2, 5), nediv(oy + o - 2, 5) do
        local dx, dy = 5 * i + 2 - ox, 5 * j + 2 - oy
        if dx * dx + dy * dy <= o * o then
          local key = i * 65536 + j
          if mark then ROOM[key] = true
          else
            t = t + 1
            if ROOM[key] then n = n + 1 end
          end
        end
      end
    end
    return n, t
  end
  -- \flo@takenat: the share of the disc's cells taken, divided as TeX divides
  local function takenat(x, y, r)
    local n, t = room(false, x, y, r)
    if t == 0 then return 0 end
    return (n * 65536 // t) / 65536
  end
  -- \flo@stemx: the main stem's x at height h
  local function stemx(h)
    local sb = min(1, max(0, h / 3))
    return R.root + sb * sb * (3 - 2 * sb) * (R.pc - R.root + R.pA * (0.62 * sin(360 * h / R.plam + R.pph)
      + 0.38 * sin(766.8 * h / R.plam + R.pps)))
  end
  -- \flo@stemtan: its tangent at h, and its x
  local function stemtan(h)
    local xa, xb = stemx(h + 0.05), stemx(h - 0.05)
    local t = sqrt((xa - xb) * (xa - xb) + 0.01)
    return (xa - xb) / t, 0.1 / t, stemx(h)
  end
  -- \flo@drawstem, \flo@markstem
  local function drawstem()
    local n = max(2, texint(R.pH, pm.sp("0.25"), 65536))   -- two points at least, under 2.5 mm
    local pts = {}
    for k = 1, n do
      local y = R.pH * (k - 1) / (n - 1)
      pts[k] = { stemx(y), y }
    end
    stem(pts, R.pw, 0.028, 0.07)
  end
  local function markstem()
    for k = 1, texint(R.pH, pm.sp("0.6")) do
      local y = 0.6 * k - 0.3
      room(true, stemx(y), y, 0.2)
    end
  end
  -- \flo@unit: the species' inflorescence at (x, y) along ax, scale k, late later than the
  -- top one: its room marked on every walk, drawn on its layer
  local function unit(x, y, ax, k, late)
    local uo, udr, kind = M.unitdisc(S, k)
    room(true, x + uo * cos(ax), y + uo * sin(ax), 0.85 * udr)
    local st = 1.2 * R.season - late + sym(0.08) + S.phenology
    local z = seedorgan()
    if at((kind == "umbel" or kind == "raceme") and LAIRY or LBLOOM) then
      organ(z, function() ink.translate(x, y) ink.rotate(ax - 90) M.unit(k, st) end)
    end
  end
  -- \flo@plantsetup: plant ga's place, stem, species and generator; \flo@stemload
  local function plantsetup(ga)
    R.root, R.pc = (ga - 2) * 0.25, (ga - 2) * 0.35
    if R.Np == 1 then R.root, R.pc = 0, 0 end
    R.pw = ga == 1 and 0.085 or 0.065
    R.pH = R.H[ga]
    S = full({ seed = R.s[ga], role = R.r[ga], traits = R.traits })
    G.load(R.w[ga])
  end
  local function stemload(ga) local f = R.f[ga] R.pph, R.pps, R.plam = f[1], f[2], f[3] end
  -- \flo@passstem: the stem's wave, its room, the stem, the top inflorescence
  local function passstem(ga)
    R.pph = range(0, 360) R.pps = range(0, 360) R.plam = range(5, 8)
    R.f[ga] = { R.pph, R.pps, R.plam }
    markstem()
    if at(LSTEM) then drawstem() end
    local top = M.lateness(S, 1)
    local tx, ty, sx = stemtan(R.pH)
    unit(sx, R.pH, atan2(ty, tx), K(1.15), top)
  end
  -- \flo@leafcof: a broad leaf's length factor
  local function leafcof()
    if S.compound == "simple" then R.leafc = min(1.45, max(1, sqrt(S.leafaspect / 2.2))) else R.leafc = K(1.35) end
  end
  -- \flo@nodeat: the stem at height R.ny: x, tangent, its angle, width, height on the plant
  local function nodeat()
    R.nyy = R.ny R.u = R.nyy / R.pH
    R.ntx, R.nty, R.nx = stemtan(R.nyy)
    R.ta = atan2(R.nty, R.ntx)
    R.nw = R.pw + (0.028 - R.pw) * R.u
  end
  -- \flo@placeleaf: a leaf at R.la degrees, R.lf cm, or shorter where its room is taken
  -- (three tries), or none; z its seed
  local function placeleaf(x, y, z)
    local ok = false
    for _ = 1, 3 do
      if not ok then
        local mx, my = x + cos(R.la) * R.lf * R.fore * 0.55, y + sin(R.la) * R.lf * R.fore * 0.55
        if takenat(mx, my, 0.24 * R.lf) < K(0.2) then ok = true else R.lf = 0.75 * R.lf end
      end
    end
    if R.lf < K(0.45) then ok = false end
    if ok then
      for d = 1, 3 do
        local t = (0.05 + 0.25 * d) * R.lf * R.fore
        room(true, x + cos(R.la) * t, y + sin(R.la) * t, 0.2 * R.lf)
      end
      if at(LLEAF) then
        local la, lf, fore, lc, bend = R.la, R.lf, R.fore, R.lc, R.sbend
        local sheath = R.lsd and { R.lsd, 1.8 * R.nw / lf }
        organ(z, function()
          ink.translate(x, y) ink.rotate(la - 90) ink.scale(lf)
          if fore < 1 then yscale(fore) end
          autumnleaf(lc, R.season)
          M.leaf(lf, bend, sheath)
        end)
      end
    end
    R.fore, R.lsd = 1, nil
  end
  -- \flo@nodeleaves: the leaves at a node (one, a pair or a whorl of three), the node kept
  local function nodeleaves(ga)
    nodeat()
    R.side = -R.side
    R.nd[ga][R.Ge] = { R.nyy, R.side }
    for b = 1, R.per do
      local ls = b == 2 and -R.side or R.side
      local t = range(35, 60)
      if b == 3 then t = range(8, 20) end
      -- decussate: every other opposite pair close to the stem, foreshortened
      if R.per == 2 and R.Ge % 2 == 0 then t, R.fore = 0.55 * t, K(0.55) end
      R.la = R.ta + ls * t
      R.lsd = R.ta - R.la
      R.lf = 2.6 * (1 - (1 - S.taper) * R.u) * range(0.85, 1.1) * R.leafk * R.leafc * (b == 3 and 0.7 or 1)
      R.sbend = ls * range(0.05, 0.3)
      R.lc = G.rnd()
      placeleaf(R.nx, R.nyy, seedorgan())
    end
  end
  -- \flo@nodes, \flo@rosette, \flo@eudicotleaves, \flo@monocotleaves, \flo@passleaves
  local function nodes(ga)
    while R.ny < R.nlast do
      R.Ge = R.Ge + 1 nodeleaves(ga)
      R.ny = R.ny + range(1.2, 1.9) * R.gap
    end
  end
  local function rosette()
    local nb = int(3, 5)
    for b = 1, nb do
      local f = (b - (nb + 1) / 2) / max(1, (nb - 1) / 2)
      R.la = 90 - 62 * f + sym(8)
      R.lf = range(1.6, 2.3)
      R.sbend = (f < 0 and 1 or -1) * range(0.05, 0.3)
      R.lf = R.lf * R.leafc
      R.lc = G.rnd()
      placeleaf(R.root, K(0.1), seedorgan())
    end
  end
  local function eudicotleaves(ga)
    local arr = S.arrangement
    R.per, R.gap, R.leafk = 1, 1, 1
    if arr == "opposite" then R.per, R.gap = 2, K(1.3) end
    if arr == "whorled" then R.per, R.gap = 3, K(1.4) end
    if arr == "rosette" then rosette() R.gap, R.leafk = K(1.6), K(0.4) end
    R.ny = range(0.7, 1.3)
    R.side = chance(0.5) and 1 or -1
    R.nlast = R.pH - 1.6
    nodes(ga)
  end
  local function monocotleaves(ga)
    local nb = int(3, 5)
    for b = 1, nb do
      local f = (b - (nb + 1) / 2) / max(1, (nb - 1) / 2)
      R.la = 90 - 24 * f + sym(5)
      R.lf = min(7, R.pH * range(0.3, 0.46)) * (1 - 0.25 * abs(f))
      R.sbend = (f < 0 and -1 or 1) * range(0.1, 0.35)
      R.lc = G.rnd()
      local z = seedorgan()
      placeleaf(R.root + 0.12 * f, 0, z)
    end
    R.side = chance(0.5) and 1 or -1
    R.per, R.leafk = 1, K(0.75)
    R.ny = range(0.22, 0.3) * R.pH R.Ge = R.Ge + 1 nodeleaves(ga)
    R.ny = range(0.42, 0.52) * R.pH R.Ge = R.Ge + 1 nodeleaves(ga)
  end
  local function passleaves(ga)
    stemload(ga) R.Ge = 0 leafcof()
    R.nd[ga] = R.nd[ga] or {}
    local cl, arr = S.clade, S.arrangement
    if cl == "monocot" and (arr == "alternate" or arr == "whorled") then cl = "leafy" end
    if cl == "monocot" then monocotleaves(ga) else eudicotleaves(ga) end
    R.ndn[ga] = R.Ge
  end
  -- \flo@cubicstem: a branch along the cubic B, widths w0 to w1 (cm), a stroke under 0.7 bp
  local function cubicstem(B, w0, w1)
    local w = (w0 + w1) / 2 * 28.3465
    local c = tone(0.07)
    if w < K(0.7) then
      moveto(B[1], B[2]) curveto(B[3], B[4], B[5], B[6], B[7], B[8]) stroke(c, w, 1, false)
    else
      taper(B[1], B[2], B[3], B[4], B[5], B[6], B[7], B[8], w0, w1) fill(c, 1)
    end
  end
  -- \flo@branchto: the branch from the node to (R.bx, R.by) (\flo@branchpath, flora's
  -- branch_path), a leaflet or two along it, what it carries
  local function branchto()
    local x0, y0, x1, y1 = R.nx, R.nyy, R.bx, R.by
    local dx, dy = x1 - x0, y1 - y0
    local d = sqrt(dx * dx + dy * dy)
    local s = R.ntx * dx + R.nty * dy < -0.3 * d and -1 or 1
    local tx, ty = s * R.ntx, s * R.nty
    local turn = range(28, 48)
    turn = (tx * dy - ty * dx > 0 and 1 or -1) * turn
    local ox, oy = tx * cos(turn) - ty * sin(turn), tx * sin(turn) + ty * cos(turn)
    local B = { x0, y0, x0 + ox * d * 0.45, y0 + oy * d * 0.45, x1 - R.ax * d * 0.4, y1 - R.ay * d * 0.4, x1, y1 }
    local function cubicat(t)                   -- \flo@cubicat: the point at t, and the angle there
      local u = 1 - t
      local a, b, c, e = u * u * u, 3 * u * u * t, 3 * u * t * t, t * t * t
      local cx = a * B[1] + b * B[3] + c * B[5] + e * B[7]
      local cy = a * B[2] + b * B[4] + c * B[6] + e * B[8]
      a, b, c = u * u, 2 * u * t, t * t
      local ddx = a * (B[3] - B[1]) + b * (B[5] - B[3]) + c * (B[7] - B[5])
      local ddy = a * (B[4] - B[2]) + b * (B[6] - B[4]) + c * (B[8] - B[6])
      return cx, cy, atan2(ddy, ddx)
    end
    for k = 1, 3 do
      local cx, cy = cubicat(0.25 * k)
      room(true, cx, cy, 0.15)
    end
    local w = min(0.7 * R.nw, 0.05)
    local wb = max(0.012, 0.45 * w)
    if at(LSTEM) then cubicstem(B, w, wb) end
    if d > K(1.2) then
      local nl = d > K(2.2) and 2 or 1
      for gb = 1, nl do
        local cx, cy, ca = cubicat(0.25 + 0.3 * gb / nl)
        local la = ca + (gb == 1 and -1 or 1) * R.side * range(40, 60) - 90
        R.lf = range(0.45, 0.75)
        local z = seedorgan()
        if at(LLEAF) then
          local lf = R.lf
          organ(z, function() ink.translate(cx, cy) ink.rotate(la) ink.scale(lf) M.leaflet({ u = lf }) end)
        end
      end
    end
    local what, bx, by, a = R.what, R.bx, R.by, R.a
    if what == 1 then unit(bx, by, a, R.usc, R.ulate) return end
    room(true, R.bdx, R.bdy, 0.85 * R.bdr)
    if what == 2 then
      local z = seedorgan()
      if at(LBLOOM) then
        organ(z, function() ink.translate(bx, by) ink.rotate(a - 90) M.budding(min(1.1, fdiam(K(0.6)))) end)
      end
    elseif what == 3 then
      local z = seedorgan()
      if at(LAIRY) then organ(z, function() ink.translate(bx, by) ink.rotate(a) M.tendril(range(0.3, 0.5)) end) end
    else
      R.sbend = R.side * range(0.05, 0.3) R.lc = G.rnd()
      local z = seedorgan()
      if at(LLEAF) then
        local lf, bend, lc = R.lf, R.sbend, R.lc
        organ(z, function()
          ink.translate(bx, by) ink.rotate(a - 90) ink.scale(lf)
          autumnleaf(lc, R.season)
          M.leaf(lf, bend)
        end)
      end
    end
  end
  -- \flo@branch: what the branch carries, then the freest of three places for it
  local function branch()
    local late = M.lateness(S, R.u)
    local mk = G.rnd()
    local climb = S.habit == "climbing" or S.tendrils == 1
    if R.filling then mk = mk * 1.25 - 0.25 end
    if mk < 0 then R.what = 4 leafcof() R.lf = range(K(1.1), 2) * R.leafc
    elseif mk < K(0.6) then R.what = 1 R.usc = range(0.7, 0.9) R.ulate = late
    elseif mk < K(0.82) then R.what = 2
    elseif climb then R.what = 3
    else R.what, R.usc, R.ulate = 1, K(0.6), late + 0.1 end
    local best = 2
    for _ = 1, 3 do
      local cd = range(1.4, 3.2) * (1 - 0.25 * R.u)
      local cg = 90 + R.side * range(25, 75)
      local cbx = max(R.pxmin, min(R.pxmax, R.nx - cd * cos(cg)))
      local cby = R.nyy + cd * sin(cg)
      local cax, cay = cbx - R.nx, cby - R.nyy
      local t = sqrt(cax * cax + cay * cay)
      cax, cay = cax / t, cay / t + 0.6
      t = sqrt(cax * cax + cay * cay)
      cax, cay = cax / t, cay / t
      local caa = atan2(cay, cax)
      local what, udx, udy, udr = R.what
      if what == 1 then
        local uo, r = M.unitdisc(S, R.usc)
        udx, udy, udr = cbx + uo * cos(caa), cby + uo * sin(caa), r
      elseif what == 2 then udx, udy, udr = cbx + 0.35 * cax, cby + 0.35 * cay, 0.5
      elseif what == 3 then udx, udy, udr = cbx, cby, 0.35
      else udx, udy, udr = cbx + 0.5 * R.lf * cax, cby + 0.5 * R.lf * cay, 0.32 * R.lf end
      local taken = takenat(udx, udy, udr)
      if taken < best then
        best = taken
        R.bx, R.by, R.ax, R.ay, R.a = cbx, cby, cax, cay, caa
        R.bdx, R.bdy, R.bdr = udx, udy, udr
      end
    end
    if best < K(0.3) then branchto() end
  end
  -- \flo@passbranches: a branch at a node now and then; \flo@passfill: then anywhere
  local function passbranches(ga)
    stemload(ga)
    for e = 1, R.ndn[ga] do
      local nd = R.nd[ga][e]
      R.ny, R.side = nd[1], nd[2]
      nodeat()
      if G.rnd() < K(e == 1 and 0.35 or 0.85) then branch() end
    end
  end
  local function passfill(ga)
    stemload(ga) R.filling = true
    for _ = 1, R.ftries do
      R.ny = range(0.12, 0.93) * R.pH
      R.side = chance(0.5) and 1 or -1
      nodeat() branch()
    end
    R.filling = false
  end
  -- \flo@garden: plants (seed, role) from one root, gH cm high, reaching about gW, walked
  -- once per layer; a pale ghost flower behind them
  local function garden(plants, gH, gW, ghost, season, traits)
    local np = #plants
    if np == 0 then return end                  -- plants names no plant (TeX's error)
    ink.scale(28.45276)
    R = { season = season, Np = np, H = {}, z = {}, w = {}, f = {}, nd = {}, ndn = {}, s = {}, r = {}, fore = 1,
      filling = false, traits = traits }
    for k, p in ipairs(plants) do R.s[k], R.r[k] = p[1], p[2] end
    drawseed(R.s[1])
    for ga = 1, np do
      local t = 1
      if ga == 2 then t = range(0.6, 0.72) elseif ga == 3 then t = range(0.36, 0.48) elseif ga > 3 then t = range(0.5, 0.58) end
      R.H[ga] = gH * t
      R.z[ga] = seedorgan()
    end
    local gx = gW * (range(0.35, 0.65) - 0.5)
    local gy = gH * range(0.15, 0.3)
    local gdiam = range(2.6, 3.2)
    local gos = seedorgan()
    R.pxmin, R.pxmax = -gW / 2 + 0.7, gW / 2 - 0.7
    R.pA = np == 1 and 0.5 or 0.2 * gW
    for l = 1, 5 do
      layer, ROOM = l, {}
      for ga = 1, np do R.w[ga] = R.z[ga] end
      if ghost then
        room(true, gx, gy, 0.25 * gdiam)
        if l == 1 then
          -- a plain flower of the first plant's species, as v1.3's ghosts are
          S = full({ seed = R.s[1], role = R.r[1], traits = R.traits })
          organ(gos, function()
            pen.lift = K(0.62)
            ink.translate(gx, gy)
            ghostflower(gdiam)
          end)
        end
      end
      for ga = 1, np do plantsetup(ga) passstem(ga) R.w[ga] = G.save() end
      for ga = 1, np do plantsetup(ga) passleaves(ga) R.w[ga] = G.save() end
      for ga = 1, np do plantsetup(ga) passbranches(ga) R.w[ga] = G.save() end
      for _ = 1, 3 do
        for ga = 1, np do
          plantsetup(ga) R.ftries = texint(R.pH, 131072) passfill(ga) R.w[ga] = G.save()
        end
      end
    end
  end

  -- ---------------------------------------------------------------- the pics
  -- \flo@picsetup: the species, then the drawing's own stream (o.draw, or the seed + 1)
  local function setup(o, role)
    S = full({ seed = o.seed, role = role or o.role or "any", traits = o.traits })
    drawseed(o.draw or (o.seed == 2147483646 and 1 or o.seed + 1))
  end
  return function(kind, o)
    local season = (o.season or 49152) / 65536            -- 0.75 if not given
    if kind == "flower" then
      setup(o) ink.scale(o.size / 65536)
      local tq = stone()
      M.sflower(o.size / 65536, { tone = tq, notchp = 0.4, notchk = 1 })
    elseif kind == "leaf" then
      setup(o) ink.scale(o.size / 65536) M.leaf(nil, nil, nil, o.size)
    elseif kind == "v13 flower" then
      setup(o) ink.scale(o.size / 65536)
      M.flower(sizecm(o.size), 5, { pointed = false, notchp = 0.4, notchk = 1 })
    elseif kind == "v13 leaf" then
      setup(o) ink.scale(o.size / 65536) vleaf(sizecm(o.size))
    elseif kind == "inflorescence" then
      setup(o) ink.scale(28.45276)
      M.unit(o.scale / 65536, 1.2 * season + sym(0.08) + S.phenology)
    elseif kind == "plant" then
      setup(o)
      if S.clade == "fern" then ferncrown(sizecm(o.size), season)
      else garden({ { o.seed, o.role or "any" } }, sizecm(o.size), sizecm(o.width), false, season, o.traits) end
    elseif kind == "fern" then
      setup(o, "fern") ferncrown(sizecm(o.size), season)
    elseif kind == "visitor" then
      visitor(o, true)
    elseif kind == "plants" then
      garden(o.plants, sizecm(o.size), sizecm(o.width), o.ghost ~= false, season, o.traits)
    else
      error("florilegium-native: no pic " .. tostring(kind))
    end
  end
end)()
-- A pic of the TikZ library (\pic {florilegium <kind>={...}}), drawn with ink at the origin
-- under ink's current transform, in pt: kind flower, leaf, v13 flower, v13 leaf,
-- inflorescence, plant, plants, fern or visitor; o as M.tikz builds it
-- (seed, role, draw; size and width in sp of pt; season and scale in sp; ghost; plants =
-- { {seed, role}, ... })
function M.pic(kind, o)
  M.fresh()
  pen.lift, pen.accent = 0, false
  FE.tex = true
  ink.push()
  local ok, err = pcall(pic, kind, o)
  ink.pop()
  FE.tex = nil
  pen.lift, pen.accent = 0, false
  if not ok then error(err, 0) end
end

-- ================================================================ the TikZ library's pics
-- \flo@luapic: a pic of the TikZ library (engine=lua) drawn here, straight into the picture.
-- ink follows pgf's current transformation (a pic's units are pt), the colors are the
-- document's ink and accent, and pgf learns the drawing's extent for the picture's size.
-- TeX has read the keys as the TikZ code reads them (\flo@luapic): the seeds are whole
-- numbers, the season and the scale pgfmath's results, read here into sp
-- the library and these files are found one by one (a set in ~/texmf, newer files beside
-- the document), so the first pic checks that they come from one release
local checked = false
local function check(lib)
  checked = true
  local v = lib and lib:match("v(%d[%d.]*)")
  if not v then return end
  for _, m in ipairs({ { "florilegium-native", M.version }, { "florilegium-pgfmath", pm.version },
    { "florilegium-species", species.version }, { "florilegium-ink", ink.version } }) do
    if m[2] ~= v then
      error(string.format("florilegium: %s.lua is from %s, tikzlibraryflorilegium.code.tex from release %s. "
        .. "Copy the library and every florilegium*.lua from one release.", m[1],
        m[2] and ("release " .. m[2]) or "an earlier release", v), 0)
    end
  end
end
local function rgb(s)
  local r, g, b = s:match("^%s*([%d.]+)%s*,%s*([%d.]+)%s*,%s*([%d.]+)")
  return tonumber(r), tonumber(g), tonumber(b)
end
-- kind: flower, leaf, v13 flower, v13 leaf, inflorescence, plant, plants, fern, visitor;
-- seed and draw (or "") whole numbers, size and width in sp, season and scale decimals,
-- plants "seed/role,seed/role,", ghost "true" or "false"; inkrgb, accrgb "r,g,b"; lib the
-- library's \ProvidesFile line
function M.tikz(kind, seed, role, draw, size, width, season, plants, ghost, scale, inkrgb, accrgb, lib, traits)
  if not checked then check(lib) end
  local o = { role = role, size = size, width = width, season = pm.sp(season), ghost = ghost == "true",
    scale = pm.sp(scale), seed = math.tointeger(tonumber(seed)) }
  if draw ~= "" then o.draw = math.tointeger(tonumber(draw)) end
  if kind == "plants" then
    o.plants = {}
    for s, r in plants:gmatch("([^/,]+)/([^,]*),") do o.plants[#o.plants + 1] = { math.tointeger(tonumber(s)), r } end
  end
  if traits and traits:match("%S") then
    -- a trait the genome lacks, a value it cannot draw, or one the plant's kind cannot (a
    -- fern's on a flowering plant, M.refused, which sampling raises): TeX's error, once,
    -- and the pic as sampled, as the library's \flo@fixset and \flo@planfixcheck do
    local ok, fix = pcall(species.fixes, traits, kind == "visitor")
    local err = not ok and fix or nil
    if ok and kind ~= "visitor" then
      for _, p in ipairs(o.plants or { { o.seed, role } }) do
        local drawn, e = pcall(species.sample, p[1], p[2], nil, fix)
        if not drawn then ok, err = false, e break end
        -- the fern pic draws a fern (\flo@fernpic)
        if kind == "fern" and e.clade ~= "fern" then
          ok, err = false, string.format("florilegium: the fern pic draws a fern, and plan=%s is a flowering plant's", e.plan)
          break
        end
      end
    end
    if ok then o.traits = traits else tex.error(err) end
  end
  ink.set_ink(rgb(inkrgb))
  ink.set_accent(rgb(accrgb))
  local k, bp = 72 / 72.27, 72 / 72.27 / 65536
  ink.set_transform({ tonumber(token.get_macro("pgf@pt@aa")) * k, tonumber(token.get_macro("pgf@pt@ab")) * k,
    tonumber(token.get_macro("pgf@pt@ba")) * k, tonumber(token.get_macro("pgf@pt@bb")) * k,
    tex.dimen["pgf@pt@x"] * bp, tex.dimen["pgf@pt@y"] * bp })
  local lift, pale, layer = ink.set_lift(0), ink.set_pale(0), ink.layer(5)
  ink.track(true)
  -- a pic that fails leaves ink as it found it, so the next pic or margin page draws in its
  -- own layer and pale, and what it drew is dropped (a clip it opened may still be open);
  -- then TeX reports the error
  local ok, err = pcall(M.pic, kind, o)
  if not ok then ink.drop() end
  local box = ink.track(false)
  ink.flush()
  ink.set_lift(lift) ink.set_pale(pale) ink.layer(layer)
  if not ok then error(err, 0) end
  if box and box[1] <= box[3] then
    local f = 72.27 / 72
    local cct = luatexbase and luatexbase.registernumber and luatexbase.registernumber("catcodetable@atletter")
    local s = string.format("\\pgf@protocolsizes{%.4fpt}{%.4fpt}\\pgf@protocolsizes{%.4fpt}{%.4fpt}",
      box[1] * f, box[2] * f, box[3] * f, box[4] * f)
    if cct then tex.sprint(cct, s) else tex.sprint(s) end
  end
end

return M
