nandi/frqpublic Fork 0
16df4e0
Commits
Clone
git clone https://git.rickub.com/nandi/frq.git
git clone ssh://git@rickub.com/nandi/frq.git

Host key fingerprint (ed25519): SHA256:iycHnxEyq0Q7uyVpB7JlznP0G7JrTPXLYRcAU5CSLhc — verify it before your first connect.

Let the tiles actually grow

The ceiling was a fixed 240 points, which two people in a 520-wide window
were already against — so dragging the window to twice the size changed the
tiles by six points, and the resizing looked broken because in every way
that mattered it was.

The ceiling now comes from the wall's share of the height, so a bigger
window really does make the faces bigger: three people go from 153 points
at 520x700 to 277 at 900x700 to 449 on a 1080p screen. The fixed cap is
still there but generous, and only answers on a screen tall enough that the
height never binds.

Columns are picked by trying every arrangement from one row to one column
and keeping the roomiest, because neither axis alone decides it — more
columns buy height by spending width, and which is worth more depends on
the shape of the window.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
nandi committed 2026-08-30T17:47:08-07:00 Browse files
16df4e0 parent: 4917912
modified src/frq/av.jolt +40 -37
@@ -495,11 +495,14 @@
495495 ;; is people looking at each other, not a contact sheet.
496496 (def ^:private want-tile 160)
497497
498-;; A tile narrower than this is not a face, it is a thumbnail of one. The
499-;; ceiling is where a tile stops being a tile and starts being the screen at
500-;; which point the person wants the picture, not the call.
498+;; A tile narrower than this is not a face, it is a thumbnail of one.
501499 (def ^:private min-tile 96)
502-(def ^:private max-tile 240)
500+
501+;; And an upper bound, so that one person alone does not become a wall-sized
502+;; portrait in a maximised window. Generous rather than tight what actually
503+;; stops tiles growing is the height budget below, and this is only here so
504+;; there is an answer on a screen tall enough not to bind.
505+(def ^:private max-tile 480)
503506
504507 ;; What a tile costs in height beyond its picture: the name under it, and the
505508 ;; gap to the row below.
@@ -510,46 +513,45 @@
510513 ;; being that and starts being a video app with a chat box attached.
511514 (def ^:private wall-share 0.34)
512515
513-(defn- tile-height [width] (+ (long (* width 0.75)) tile-label))
514-
515516 (defn- rows-for [n cols] (max 1 (quot (+ n (dec cols)) cols)))
516517
517518 (defn tile-width
518- "How wide each of `n` tiles across should be in a window of `width` points.
519-
520- A wider window makes everyone bigger, which is the point of dragging one
521- wider during a call; a second person appearing shrinks the first rather than
522- pushing them off the edge."
523- [width n]
519+ "How wide a tile is with `n` across and `rows` down, in a window of `width`
520+ by `height` points.
521+
522+ Bounded by both axes, and the height is usually the one that binds. That is
523+ deliberate: a fixed ceiling meant a window dragged from half the screen to
524+ all of it changed the tiles by six points, because they were already against
525+ it. Deriving the ceiling from the wall's share of the height means a taller
526+ window really does make the faces bigger."
527+ [width height n rows]
524528 (let [n (max 1 n)
525- usable (max 0 (- (or width 0) wall-chrome (* tile-gap (dec n))))]
526- (-> (quot usable n)
529+ rows (max 1 rows)
530+ across (max 0 (- (or width 0) wall-chrome (* tile-gap (dec n))))
531+ by-width (quot across n)
532+ ;; What the row height allows, once the name and the gap are paid for.
533+ down (- (quot (long (* (or height 0) wall-share)) rows) tile-gap tile-label)
534+ by-height (long (/ (max 0 down) 0.75))]
535+ (-> (min by-width by-height)
527536 (max min-tile)
528537 (min max-tile))))
529538
530539 (defn columns
531- "How many tiles to put across a window of `width` by `height` points.
532-
533- Two pressures, and the second is why this is not simply a division. Across:
534- tiles are kept near the size they want rather than crammed to the minimum,
535- because four faces at a hundred points each is worse than two rows of two at
536- twice that. Down: the whole wall has to fit its share of the window, so when
537- the rows that follow from a comfortable width would be taller than that, it
538- takes more columns and smaller tiles instead — which is what stops five
539- people in a narrow window from stacking five rows deep and pushing the
540- conversation off the screen."
540+ "How many tiles to put across, for the biggest tiles the window allows.
541+
542+ Every arrangement from one row to one column is tried and the roomiest wins,
543+ because neither axis alone decides it: more columns buy height by spending
544+ width, and which is worth more depends on the shape of the window. Ties go to
545+ fewer columns, which is the arrangement with fewer rows."
541546 [width height n]
542- (let [n (max 1 n)
543- usable (- (or width 0) wall-chrome)
544- want (-> (quot (+ usable tile-gap) (+ want-tile tile-gap)) (max 1) (min n))
545- budget (* (or height 0) wall-share)
546- fits? (fn [cols]
547- (<= (* (rows-for n cols) (+ (tile-height (tile-width width cols)) tile-gap))
548- budget))]
549- ;; From the width we would like, widen until the wall fits its share. The
550- ;; last candidate is every tile on one row, which is as short as the wall
551- ;; can be made if that still does not fit, it is the best on offer.
552- (or (first (filter fits? (range want (inc n)))) n)))
547+ (let [n (max 1 n)]
548+ (reduce (fn [best cols]
549+ (if (> (tile-width width height cols (rows-for n cols))
550+ (tile-width width height best (rows-for n best)))
551+ cols
552+ best))
553+ 1
554+ (range 2 (inc n)))))
553555
554556 (defn tile-rows
555557 "The tiles as `[width [[key ...] ...]]` — one width, and the rows to draw.
@@ -559,6 +561,7 @@
559561 as though something had gone wrong rather than as a layout."
560562 ([] (tile-rows @window-width @window-height (tiles)))
561563 ([width height keys]
562- (let [cols (columns width height (count keys))]
563- [(tile-width width cols)
564+ (let [n (count keys)
565+ cols (columns width height n)]
566+ [(tile-width width height cols (rows-for n cols))
564567 (mapv vec (partition-all cols keys))])))
@@ -495,11 +495,14 @@
495 ;; is people looking at each other, not a contact sheet.495 ;; is people looking at each other, not a contact sheet.
496 (def ^:private want-tile 160)496 (def ^:private want-tile 160)
497 497
498-;; A tile narrower than this is not a face, it is a thumbnail of one. The498+;; A tile narrower than this is not a face, it is a thumbnail of one.
499-;; ceiling is where a tile stops being a tile and starts being the screen at
500-;; which point the person wants the picture, not the call.
501 (def ^:private min-tile 96)499 (def ^:private min-tile 96)
502-(def ^:private max-tile 240)500+
501+;; And an upper bound, so that one person alone does not become a wall-sized
502+;; portrait in a maximised window. Generous rather than tight what actually
503+;; stops tiles growing is the height budget below, and this is only here so
504+;; there is an answer on a screen tall enough not to bind.
505+(def ^:private max-tile 480)
503 506
504 ;; What a tile costs in height beyond its picture: the name under it, and the507 ;; What a tile costs in height beyond its picture: the name under it, and the
505 ;; gap to the row below.508 ;; gap to the row below.
@@ -510,46 +513,45 @@
510 ;; being that and starts being a video app with a chat box attached.513 ;; being that and starts being a video app with a chat box attached.
511 (def ^:private wall-share 0.34)514 (def ^:private wall-share 0.34)
512 515
513-(defn- tile-height [width] (+ (long (* width 0.75)) tile-label))
514-
515 (defn- rows-for [n cols] (max 1 (quot (+ n (dec cols)) cols)))516 (defn- rows-for [n cols] (max 1 (quot (+ n (dec cols)) cols)))
516 517
517 (defn tile-width518 (defn tile-width
518- "How wide each of `n` tiles across should be in a window of `width` points.519+ "How wide a tile is with `n` across and `rows` down, in a window of `width`
519-520+ by `height` points.
520- A wider window makes everyone bigger, which is the point of dragging one521+
521- wider during a call; a second person appearing shrinks the first rather than522+ Bounded by both axes, and the height is usually the one that binds. That is
522- pushing them off the edge."523+ deliberate: a fixed ceiling meant a window dragged from half the screen to
523- [width n]524+ all of it changed the tiles by six points, because they were already against
525+ it. Deriving the ceiling from the wall's share of the height means a taller
526+ window really does make the faces bigger."
527+ [width height n rows]
524 (let [n (max 1 n)528 (let [n (max 1 n)
525- usable (max 0 (- (or width 0) wall-chrome (* tile-gap (dec n))))]529+ rows (max 1 rows)
526- (-> (quot usable n)530+ across (max 0 (- (or width 0) wall-chrome (* tile-gap (dec n))))
531+ by-width (quot across n)
532+ ;; What the row height allows, once the name and the gap are paid for.
533+ down (- (quot (long (* (or height 0) wall-share)) rows) tile-gap tile-label)
534+ by-height (long (/ (max 0 down) 0.75))]
535+ (-> (min by-width by-height)
527 (max min-tile)536 (max min-tile)
528 (min max-tile))))537 (min max-tile))))
529 538
530 (defn columns539 (defn columns
531- "How many tiles to put across a window of `width` by `height` points.540+ "How many tiles to put across, for the biggest tiles the window allows.
532-541+
533- Two pressures, and the second is why this is not simply a division. Across:542+ Every arrangement from one row to one column is tried and the roomiest wins,
534- tiles are kept near the size they want rather than crammed to the minimum,543+ because neither axis alone decides it: more columns buy height by spending
535- because four faces at a hundred points each is worse than two rows of two at544+ width, and which is worth more depends on the shape of the window. Ties go to
536- twice that. Down: the whole wall has to fit its share of the window, so when545+ fewer columns, which is the arrangement with fewer rows."
537- the rows that follow from a comfortable width would be taller than that, it
538- takes more columns and smaller tiles instead — which is what stops five
539- people in a narrow window from stacking five rows deep and pushing the
540- conversation off the screen."
541 [width height n]546 [width height n]
542- (let [n (max 1 n)547+ (let [n (max 1 n)]
543- usable (- (or width 0) wall-chrome)548+ (reduce (fn [best cols]
544- want (-> (quot (+ usable tile-gap) (+ want-tile tile-gap)) (max 1) (min n))549+ (if (> (tile-width width height cols (rows-for n cols))
545- budget (* (or height 0) wall-share)550+ (tile-width width height best (rows-for n best)))
546- fits? (fn [cols]551+ cols
547- (<= (* (rows-for n cols) (+ (tile-height (tile-width width cols)) tile-gap))552+ best))
548- budget))]553+ 1
549- ;; From the width we would like, widen until the wall fits its share. The554+ (range 2 (inc n)))))
550- ;; last candidate is every tile on one row, which is as short as the wall
551- ;; can be made if that still does not fit, it is the best on offer.
552- (or (first (filter fits? (range want (inc n)))) n)))
553 555
554 (defn tile-rows556 (defn tile-rows
555 "The tiles as `[width [[key ...] ...]]` — one width, and the rows to draw.557 "The tiles as `[width [[key ...] ...]]` — one width, and the rows to draw.
@@ -559,6 +561,7 @@
559 as though something had gone wrong rather than as a layout."561 as though something had gone wrong rather than as a layout."
560 ([] (tile-rows @window-width @window-height (tiles)))562 ([] (tile-rows @window-width @window-height (tiles)))
561 ([width height keys]563 ([width height keys]
562- (let [cols (columns width height (count keys))]564+ (let [n (count keys)
563- [(tile-width width cols)565+ cols (columns width height n)]
566+ [(tile-width width height cols (rows-for n cols))
564 (mapv vec (partition-all cols keys))])))567 (mapv vec (partition-all cols keys))])))