babf25e13ff2e01e1f2a3c295f7b84420d3de421
braney
  Thu Aug 6 10:07:35 2026 -0700
Docent: say which height the print scaling actually scales, refs #37892

The k*128 a scaled run asks for is the default heightPer times k, not each
track's own height times k, so a row deliberately configured at 50px or 300px
comes back the same size as everything else instead of in proportion.  Every
track a tour has used took the default, so this has not bitten, and fixing it
properly means reading each row's heightPer out of the cart before asking for k
times it.  Write down the assumption and what the symptom looks like -- a row
that is the wrong size at k and the right size at 1x -- so the next person to
hit it knows where to look rather than rediscovering the constant.

diff --git src/hg/utils/docent/docent.js src/hg/utils/docent/docent.js
index 2c83e02ae75..e3b1bb7d803 100755
--- src/hg/utils/docent/docent.js
+++ src/hg/utils/docent/docent.js
@@ -107,30 +107,36 @@
   Math.abs(b - 8 * SCALE) < Math.abs(a - 8 * SCALE) ? b : a);
 // What every hgTracks nav carries: the image width, plus the font to draw it with at scale.
 const IMGVARS = `pix=${PIX}` + (SCALE > 1 ? `&textSize=${TEXTSIZE}` : '');
 // The tooltip's font-size, forced back to what a 1x run gives it (see SCALE_INIT). hgTracks
 // takes it from the browser text size, which is TEXTSIZE on a scaled run, and then the device
 // pixel ratio scales it a second time.
 const SCALE_ARGS = { k: SCALE, tipPx: Math.round(TEXTSIZE / SCALE) };
 // `pix` makes the image k times WIDER; nothing makes a fixed-height track taller. A bigLolly
 // or wiggle row is a pixel count (`DEFAULT_HEIGHT_PER` = 128 in hg/inc/wiggle.h), read from
 // trackDb/the cart and untouched by `pix` or `textSize` -- so a 128px row that was 15% of an
 // 850px image is 5% of a 2550px one, which is how the ClinVar lollipop row came out a sliver
 // with unreadable y-axis labels. A print run therefore asks for k times the height of every
 // track a `track:` step turns on. It is harmless where it means nothing (a bigBed never reads
 // heightPer) and each track's own `maxHeightPixels` still clamps it, so a track that should
 // stay short does -- raise that ceiling in trackDb for one that should not.
+// The k*128 is the DEFAULT height scaled, not each track's own: a row configured at 50px or
+// 300px gets k*128 too, which is proportional only for the tracks that took the default. That
+// covers every track a tour has used so far, and going further would mean reading each row's
+// heightPer out of the cart before asking for k times it. If a tour ever wants a figure of a
+// deliberately short or tall row, that is the fix -- the symptom is a row that comes back the
+// wrong size in a scaled still and the right size at 1x.
 const HEIGHTPER = SCALE > 1 ? Math.round(128 * SCALE) : 0;
 // FAST: iterate on the FIGURES. Everything that exists only for the video is dropped --
 // the dwells, the cursor animation, the dropdown theatrics, the screen recording and the
 // mp4 transcode. The stills are byte-for-byte what a full run produces, and a run costs
 // roughly a third as long. `fast: true` in the script, DOCENT_FAST=1, or `make FAST=1 BP1`.
 // A scaled run is a figure run: at 3x the video would be a 3000px-wide recording of a tour
 // nobody watches at that size, so the mp4 is skipped and only the stills are produced. Build
 // the video from an unscaled run of the same script.
 const FAST = !!(doc.fast || process.env.DOCENT_FAST || SCALE > 1);
 const PACE = FAST ? 0 : Math.round((doc.pace ?? 1.2) * 1000);   // dwell after each step
 const SHOTHOLD = FAST ? 0 : Math.round((doc.shotHold ?? 2.2) * 1000);  // extra pause at a shot
 
 // ---------- trackDb ----------
 // Docent carries NO table of per-track cart variables. Such a table encodes one snapshot
 // of trackDb and then quietly lies when trackDb changes (this file used to pin