74dc80fd3bd2a70371fa75218347ddc45a556dde braney Wed Aug 5 07:28:59 2026 -0700 Docent: render a tour at print resolution, and make zoom wait for its redraw refs #37892 scale: k (DOCENT_SCALE=k, make hires [SCALE=3]) renders the same tour with k times the pixels, for figures that have to print -- a screen still is about 120 dpi across a journal column. Nothing is upscaled; each layer that draws is asked for more: * deviceScaleFactor: k with the viewport left at its 1x CSS size, so the page lays out exactly as at 1x -- same line breaks, same jQuery-dialog width, same tooltip placement -- and rasterizes with k times the pixels, * pix x k so the server draws a wider browser image, with textSize stepped up its ladder to match (3x lands on 24) so hgTracks makes the same layout decisions in it: same tick spacing, same room for labels, same packing of features into rows, * zoom: 1/k on the image table, handing that wider image the 1x amount of layout space, so one image pixel falls on one device pixel. A scaled run is stills-only (no mp4), and DOCENT_STILLS names a different parent so a print render lands beside the screen stills instead of over them. Everything hgTracks reports about the image -- map-box coords, mouseOver spans, insideX, a px: drag endpoint -- is in the pixels the SERVER drew, which is not the displayed size once the image is scaled. Those now go through the image's natural-to-displayed ratio (1 at 1x). Without it a named mouseover: lands k times off and pins a different feature. zoom: in|out waited only for #imgTbl, which the buttons never remove (they redraw in place via ajax), so the following step could read the previous window's map boxes and report an item "not found" that was simply not in view yet. It now waits for the window itself to change. Pre-existing, hidden by the dwell that FAST removes -- and every hires run is FAST. An item-not-found error also names the current window and the items that ARE in that row, which is what told the two cases apart. Co-Authored-By: Claude Opus 5 (1M context) diff --git src/hg/utils/docent/README.md src/hg/utils/docent/README.md index 9ee143b9a7c..ddf8043147b 100644 --- src/hg/utils/docent/README.md +++ src/hg/utils/docent/README.md @@ -42,38 +42,84 @@ comment at the top of that file. ## Top of file (all optional) ```yaml target: genome-test # or rr, hgwdev, hgwbeta, hgwdev-, or a full https://.../cgi-bin db: hg38 # source assembly position: chr7:155.8M # starting position (tracked from then on; `go:` sets it too) reset: true # cartReset first (clean cart + fresh quickLift hub) size: [1000, 760] # viewport pix: 850 # browser image width pace: 1.2 # seconds to dwell after each step shotHold: 2.2 # extra seconds the video pauses at a shot trackAnim: false # skip the visible dropdown gesture on `track:` (nav only) fast: false # true = figures only: no dwells, no cursor animation, no mp4 +scale: 1 # >1 = print-resolution stills (see Print resolution) pinMouseovers: false # record every mouseover for `pinShot:` (see below) mp4: ../myname.mp4 # override output paths if you want stills: stills/AP1 ``` `target:` defaults to genome-test, so a script that forgets to say where it runs will not quietly hit someone's personal sandbox. +## Print resolution + +A screen still is about 120 dpi across a journal's 7-inch column — fine on a monitor, +too coarse to print. `scale:` (or `DOCENT_SCALE=k`, or `make hires`) renders **the same +tour with k times the pixels**, which is not the same thing as enlarging the stills +afterwards: a still only ever has the pixels it was drawn with, so every layer is asked +to draw more of them. + +``` +make hires # every scenario at 3x -> stills.hires// +make hires SCALE=2 # 2x +make hires BASES=BP1 # one scenario +DOCENT_SCALE=3 DOCENT_STILLS=stills.hires DOCENT_FAST=1 node docent.js BP1.docent.yaml +``` + +What k does: + +- **deviceScaleFactor: k**, with the viewport left at its 1x CSS size. The page lays out + exactly as at 1x — same line breaks, same jQuery-dialog width, same tooltip placement — + and all of it rasterizes with k times the pixels. +- **`pix` × k, `textSize` stepped up with it** (hgTracks' size ladder: 6, 8, 10, 12, 14, + 18, 24, 34; 3x lands exactly on 24). The server draws a genuinely wider image, and the + bigger font keeps its layout decisions proportional — tick spacing, room for labels, + how features pack into rows. A wider image with an 8px font would be a different + picture, not a bigger one. +- **`zoom: 1/k` on the image table**, handing that wider image the 1x amount of layout + space, so one image pixel lands on one device pixel. Native resolution, no resampling. + +Everything hgTracks reports about the image — map-box coords, mouseOver spans, `insideX` — +is in the pixels the server drew, so it goes through the image's natural-to-displayed ratio +before it becomes a page coordinate. That is why a `mouseover: {item: ...}` lands on the +same feature at 3x as at 1x. + +Two things to expect: + +- **No mp4.** A scaled run is a figure run; build the video from an unscaled run of the + same script. +- **Densely packed tracks come out shorter.** hgTracks' font metrics are not perfectly + linear across the size ladder, so a 24px font is a little narrower than three 8px ones + and `pack` fits the same features into fewer rows. Same window, same tracks, same items, + slightly roomier picture. + +Use `DOCENT_STILLS=` (what `make hires` does) so a print render lands beside the +screen stills instead of overwriting them. + ## Steps `steps:` is an ordered list. Each item is a bare verb or a one-key map. | Step | What it does | |------|--------------| | `gateway` | Open hgGateway for `db`. | | `go` | Click **GO** with the default position (no typing). | | `go: chr7:155799529-155812871` | Go straight to a position in hgTracks. | | `goShow: BRCA1` | **Demonstrates the position change through the UI** (vs. `go:` which navs there directly): the cursor glides to the **position box** and the term is **typed on screen**, then the page finishes the job — **Search** on hgTracks, the arrow on hgGateway — so one verb covers either page. Takes a **gene name** or a **position** (or anything else the box accepts: HGVS, an accession). Bare string, or map form `goShow: {gene: SHH, shot: source}` / `goShow: {position: "chr7:155,799,529-155,812,871", shot: source}` — quote coords in the map form (unquoted commas split a `{..}` flow map). | | `goShow: {gene: TP53, pick: "NR_176326"}` | A **gene name** goes through the browser's own **suggestion menu**, the way a user does it: Docent waits out the hgSuggest ajax, then clicks the matching row, so you land on the gene (not the search-results page) and the video shows the dropdown. Default match is the exact gene symbol; `pick:` selects a specific row by a substring of its text when the term is ambiguous (a `pick:` that matches nothing warns and falls back to the plain gene match). If nothing ever matches, the typed term is submitted as-is — that may land on the **search-results page**, which is legal, and a `click:` can take it from there. | | `hide: all` | Click **Hide all**. | | `track: {mane: pack}` | Set a track's visibility (`hide/dense/squish/pack/full`). The mouse visibly glides to that track's control dropdown, opens it, and picks the mode (then the state is applied). | | `track: {dbSnp155Common: pack}` | A subtrack by name: trackDb's view and composite above it come along, so this is the whole "common dbSNP" config. (`dbSnp155` is a *different*, off-by-default subtrack — all variants — not an alias for the composite.) | | `track: {clinvar: pack, clinvarCnv: hide}` | Composite: the container's mode reaches its subtracks, so name only the deviations. Docent adds the containers above and the `_sel` checkbox from trackDb — see **Track names come from trackDb**. |