-
Notifications
You must be signed in to change notification settings - Fork 0
/
timeline.js
123 lines (113 loc) · 4.13 KB
/
timeline.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
export {makeTimelines}
import {scientific, getBounds, QNDMedian} from "../presentation.js"
const {log: mlog, max: mmax, min:mmin, floor: mfloor} = Math
let RRR = 0
function makeTimelines({entries, totalEntries, target}) {
RRR++
const {min,max} = getBounds(entries)
const lmin = mlog(min)
const lmax = mlog(max)
const verticalResolution = 1/(lmax - lmin)
const margin = 30
const marginBottom = 25
const totalWidth = Number(document.body.clientWidth) - margin * 2
const dotWidth = totalWidth / totalEntries
const rowHeight = 100
let src = `<svg xmlns="http://www.w3.org/2000/svg"
width="${totalWidth}"
height="${entries.length * (rowHeight + margin + marginBottom) + 3}"
viewBox="0,0,${totalWidth},${entries.length * (rowHeight + margin + marginBottom)}"
style="margin:10px">`
entries.forEach((entry, i) => {
const bottom = (i + 1) * (rowHeight + margin)
const base = bottom
const ceiling = bottom - rowHeight
// title
const name = entry.name || ((source) => source.slice(source.indexOf('{')+1, 60))(entry.run.toString());
src += hLine(bottom + 1, totalWidth, "#888888", {"stroke-dasharray":"1"})
src += hLine(ceiling, totalWidth, "#888888", {"stroke-dasharray":"1"})
src += `<text
x="${totalWidth / 2}"
y="${base-rowHeight - 5}"
text-anchor="end"
style="font-family:sans-serif;font-weight: bold;text-shadow: 1px 1px 1px #fffa, -1px -1px 1px #fffa"
>${name}</text>`
if (dotWidth > 1) {
p("B")
} else {
const threshold = 1/dotWidth
const truncated = mfloor(threshold)
let k = 0
let floorK = 0
const ary = []
for(let j = 0; j< totalWidth; j++) {
let oneMore = mfloor(k + threshold) > floorK + truncated
const entriesPerDot = truncated + oneMore
const target = floorK + entriesPerDot
k += threshold
ary.length = 0
for(; floorK < target; floorK++) {
if (floorK === entry.chronological.length) break
ary.push(entry.chronological[floorK])
}
const bottom = base - (mlog(mmin(...ary))-lmin) * verticalResolution * rowHeight
const top = base - (mlog(mmax(...ary))-lmin) * verticalResolution * rowHeight
if (top < ceiling) {
console.log({top, ceiling}, mmax(...ary), mlog(mmax(...ary))-lmin, 1/verticalResolution)
throw "SDFSDF"
}
src += vLine({x: j, top, bottom, stroke: "#ff0f00"})
const y = QNDMedian(ary)
src += dot({x:j, y:base - (mlog(y)-lmin)*verticalResolution * rowHeight})
if (floorK === entry.chronological.length) break
}
}
// if (RRR > 10) throw "HUGHH"
})
src += `
</svg>`
target.innerHTML=src
void ([...target.querySelectorAll(".median-text")].forEach(x=>{
const bbox = x.getBBox()
if (bbox.x < 0) {
x.setAttribute("x", "0")
x.removeAttribute("text-anchor")
}
if (bbox.x + bbox.width > totalWidth){
x.setAttribute("text-anchor", "end")
x.setAttribute("x", `${totalWidth}`)
}
}))
// throw "SVG"
// return {bounds, min, max}
}
const SVGRect = ({x, y, width, height, fill = "#880000", opacity = 1}) => `
<rect x="${x}" y="${y}" width="${width}" height="${height}"
style="stroke-width:0;fill:${fill};fill-opacity:${opacity}"></rect>
`
const scale = (min, max, y, width) => `
<text
style="font-size: 13px;font-family:sans-serif"
x="0"
y="${y}"
>
${scientific(min)} s
</text>
<text
style="font-size: 13px;font-family:sans-serif"
x="${width}" y="${y}"
text-anchor="end"
>${scientific(max)} s</text>
`
const splat = (options) => Object.entries(options).reduce((res, [k,v])=>res + " " + k+"="+JSON.stringify(v), "")
const hLine = (y, width, stroke, options = {}) => `
<line x1="0", y1="${y}" x2="${width}" y2="${y}"
style="stroke-width:1;stroke:${stroke};fill-opacity:0;stroke-opacity:1"${splat(options)}></line>
`
const vLine = ({x, top, bottom, stroke, klass = ''}) => `
<line class="${klass}" x1="${x}", y1="${top}" x2="${x}" y2="${bottom}"
style="stroke-width:1;stroke:${stroke};fill-opacity:0;stroke-opacity:1"></line>
`
const dot = ({x, y}) => `
<circle cx="${x}" cy="${y}" r="0.6" />
`