tile.go RawHistoryFolder

1package hints
2
3const (
4	// maxRegions is the protocol limit of regions per request.
5	maxRegions = 64
6	// wholeMaxSide is the longest frame side sent whole when the frame is not
7	// 16:9: up to there the square letterbox of the detector loses little.
8	wholeMaxSide = 2560
9	// tileMaxH is the tallest tile; its width is 16/9 of it.
10	tileMaxH = 1440
11	// tileCutMargin is the distance, in physical pixels, to a tile's inner
12	// edge under which a box counts as cut by it.
13	tileCutMargin = 2
14)
15
16// region is a crop of the frame, in physical pixels.
17type region struct{ x, y, w, h int }
18
19// tileRegions returns the crops to detect separately on a width x height
20// frame, or nil for one whole-frame detection (a 16:9 frame, or one whose
21// long side is at most wholeMaxSide). Otherwise the frame is covered by exact
22// 16:9 tiles (w*9 == h*16; explicit crops always use square letterboxing) of
23// height min(height, 1440) rounded down to a multiple of 9, overlapping by at
24// least 10 % of the tile side on each axis; the last tile of a row or column
25// is aligned to the frame edge. Tiles are listed row by row. A frame that
26// would need more than maxRegions tiles is detected whole.
27func tileRegions(width, height int) []region {
28	if width <= 0 || height <= 0 || width*9 == height*16 || max(width, height) <= wholeMaxSide {
29		return nil
30	}
31	th := min(height, tileMaxH) / 9 * 9
32	tw := th / 9 * 16
33	if tw > width { // a tall frame: the width bounds the tile
34		tw = width / 16 * 16
35		th = tw / 16 * 9
36	}
37	if tw == 0 || th == 0 {
38		return nil
39	}
40	xs, ys := tileStarts(width, tw), tileStarts(height, th)
41	if len(xs)*len(ys) > maxRegions {
42		return nil
43	}
44	out := make([]region, 0, len(xs)*len(ys))
45	for _, y := range ys {
46		for _, x := range xs {
47			out = append(out, region{x, y, tw, th})
48		}
49	}
50	return out
51}
52
53// tileStarts returns the start offsets of tiles of size t covering length l,
54// evenly spread, the last one flush with the end, with consecutive tiles
55// overlapping by at least a tenth of t.
56func tileStarts(l, t int) []int {
57	if l <= t {
58		return []int{0}
59	}
60	d := l - t
61	maxStep := t * 9 / 10
62	n := (d+maxStep-1)/maxStep + 1
63	out := make([]int, n)
64	for i := range out {
65		out[i] = (d*i + n - 2) / (n - 1) // ceil(d*i/(n-1)): steps never exceed maxStep
66	}
67	return out
68}
69
70// dropCutBoxes drops the boxes that a tile's inner edge cut (an edge of the
71// tile that is not an edge of the frame) when another tile saw the same
72// element uncut: the cut box would otherwise survive as a smaller box nested
73// in the whole one. A box is "the same element" when the other box covers
74// nestRatio of it. Without regions the boxes are returned as they are.
75func dropCutBoxes(dets []detection, regions []region) []detection {
76	if len(regions) == 0 {
77		return dets
78	}
79	fw, fh := 0, 0
80	for _, r := range regions {
81		fw, fh = max(fw, r.x+r.w), max(fh, r.y+r.h)
82	}
83	cut := func(d detection) bool {
84		if d.region < 0 || d.region >= len(regions) {
85			return false
86		}
87		r := regions[d.region]
88		m := float32(tileCutMargin)
89		return (r.x > 0 && d.x1 < float32(r.x)+m) || (r.x+r.w < fw && d.x2 > float32(r.x+r.w)-m) ||
90			(r.y > 0 && d.y1 < float32(r.y)+m) || (r.y+r.h < fh && d.y2 > float32(r.y+r.h)-m)
91	}
92	isCut := make([]bool, len(dets))
93	for i, d := range dets {
94		isCut[i] = cut(d)
95	}
96	out := make([]detection, 0, len(dets))
97	for i, d := range dets {
98		drop := false
99		if isCut[i] {
100			for j, o := range dets {
101				if j != i && !isCut[j] && o.region != d.region && covered(d, o) {
102					drop = true
103					break
104				}
105			}
106		}
107		if !drop {
108			out = append(out, d)
109		}
110	}
111	return out
112}
113
114// covered reports whether o covers at least nestRatio of d's area.
115func covered(d, o detection) bool {
116	w := min(d.x2, o.x2) - max(d.x1, o.x1)
117	h := min(d.y2, o.y2) - max(d.y1, o.y1)
118	if w <= 0 || h <= 0 {
119		return false
120	}
121	return float64(w*h) >= nestRatio*float64((d.x2-d.x1)*(d.y2-d.y1))
122}

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 23

Tags 10

↑↓ moveenter openesc close