Handwritten notebook style template for Polylux
at main 231 lines 6.3 kB view raw
1#import "@preview/polylux:0.4.0": * 2#import "@preview/suiji:0.4.0" 3#import "@preview/umbra:0.1.1" 4 5#let highlight-color-state = state("jotter-highlight-color", red) 6 7#let setup( 8 header: none, 9 highlight-color: red, 10 binding: true, 11 dots: true, 12 body, 13) = { 14 highlight-color-state.update(highlight-color) 15 set page( 16 paper: "presentation-16-9", 17 margin: (left: 2.3cm, top: 1cm, rest: 1cm), 18 fill: if dots { 19 tiling( 20 spacing: (5mm, 5mm), 21 { 22 place(square(width: 6mm, stroke: none, fill: white)) 23 circle(radius: 1pt, fill: white.darken(10%)) 24 }, 25 ) 26 }, 27 background: if binding { 28 set align(top + left) 29 let gap = 1.5cm 30 let color = rgb("8aa") 31 place(rect(height: 100%, width: 5mm, stroke: none, fill: black)) 32 place(dx: 5mm, rect(height: 100%, width: 5mm, stroke: none, fill: white)) 33 for offset in range(20) { 34 let spiral(t, p) = curve( 35 stroke: (thickness: t, paint: p), 36 curve.move((.5cm, 1cm)), 37 curve.quad((-.5cm, 1.1cm), (1cm, 1.2cm)), 38 ) 39 place(dy: offset * gap, spiral(2pt, color)) 40 place( 41 dy: offset * gap - .2mm, 42 dx: -.2mm, 43 spiral(.5pt, color.darken(50%)), 44 ) 45 place( 46 dx: 1cm - .5mm, 47 dy: offset * gap + 1.2cm - 1.5mm, 48 circle(radius: 1.5mm, stroke: color + .3mm, fill: color.darken(70%)), 49 ) 50 } 51 }, 52 footer: context { 53 set align(right) 54 set text(size: .6em, fill: text.fill.transparentize(30%)) 55 context box( 56 curve( 57 stroke: (thickness: .05em, cap: "round", paint: text.fill), 58 curve.quad((.8em, -.9em), (1em, -2em)), 59 ), 60 ) 61 toolbox.slide-number 62 }, 63 header: context if header != none { 64 set align(right) 65 set text(size: .6em, fill: text.fill.transparentize(30%)) 66 set par(spacing: .5em) 67 header 68 context { 69 let w = measure(header).width 70 curve( 71 stroke: (thickness: .05em, cap: "round", paint: text.fill), 72 curve.quad((.25 * w, -.3em), (1.2 * w, 0em)), 73 ) 74 } 75 }, 76 ) 77 show emph: it => underline( 78 stroke: stroke( 79 thickness: .3em, 80 paint: highlight-color.transparentize(50%), 81 cap: "round", 82 ), 83 offset: -.1em, 84 extent: .1em, 85 evade: false, 86 background: true, 87 it.body, 88 ) 89 show heading.where(level: 1): it => layout(sz => { 90 let w = measure(..sz, it).width 91 it 92 move( 93 dy: -.8em, 94 curve( 95 stroke: (thickness: .04em, cap: "round", paint: text.fill), 96 fill: text.fill, 97 curve.quad((.75 * w, -.5em), (w, 0em)), 98 curve.quad((w + .05em, .03em), (w, .06em)), 99 curve.quad((.75 * w, -.495em), (0em, 0em)), 100 ), 101 ) 102 }) 103 set list(marker: text(fill: highlight-color.lighten(10%), sym.bullet)) 104 set enum( 105 numbering: (..n) => text( 106 fill: highlight-color.lighten(10%), 107 numbering("1.", ..n), 108 ), 109 ) 110 body 111} 112 113#let title-slide(title, extra) = slide({ 114 set align(center + horizon) 115 set page(footer: none, header: none) 116 117 if title != none { 118 set text(1.5em) 119 title 120 context { 121 let w = measure(title).width 122 let c(p) = curve( 123 stroke: (paint: p, thickness: .1em, cap: "round"), 124 curve.cubic((.2 * w, -.4em), (.95 * w, -.5em), (w + 1em, 0em)), 125 curve.cubic(auto, (w + .5em, .1em), (w, .3em)), 126 ) 127 v(-.5em) 128 let hc = highlight-color-state.get() 129 place(center, c(hc.transparentize(20%))) 130 place(center, dx: .1em, dy: .1em, c(hc.transparentize(50%))) 131 } 132 } 133 par(hide[42]) 134 135 extra 136}) 137 138#let len2int(l) = int.from-bytes(l.pt().to-bytes()) 139#let hasher(acc, val) = acc.bit-xor(val) 140#let hashed-position() = { 141 let (x, y) = here().position() 142 let n = counter("logical-slide").get() 143 (len2int(x), len2int(y), ..n).reduce(hasher) 144} 145 146#let framed-block( 147 body, 148 sloppiness: .05, 149 inset: .5em, 150 width: auto, 151 height: auto, 152) = layout(sz => { 153 let blocked-body = block( 154 inset: inset, 155 width: width, 156 height: height, 157 fill: none, 158 stroke: none, 159 body, 160 ) 161 let (width: w, height: h) = measure(..sz, blocked-body) 162 let deflect = sloppiness * (w + h) / 2 163 164 let hash = (hashed-position(), len2int(w), len2int(h)).reduce(hasher) 165 let rng = suiji.gen-rng-f(hash) 166 let (rng, c1) = suiji.random-f(rng) 167 let (rng, c2) = suiji.random-f(rng) 168 let (rng, c3) = suiji.random-f(rng) 169 let (rng, c4) = suiji.random-f(rng) 170 let (rng, s1) = suiji.choice-f(rng, (-1, 1)) 171 let (rng, s2) = suiji.choice-f(rng, (-1, 1)) 172 let (rng, s3) = suiji.choice-f(rng, (-1, 1)) 173 let (rng, s4) = suiji.choice-f(rng, (-1, 1)) 174 175 let c(p) = curve( 176 stroke: stroke(thickness: .1em, cap: "round", paint: p), 177 curve.quad((c1 * w, s1 * deflect), (w + deflect / 3, 0pt)), 178 curve.move((w - deflect / 3, -deflect / 3)), 179 curve.quad((w + s2 * deflect, c2 * h), (w - deflect / 3, h + deflect / 3)), 180 curve.move((w + deflect / 3, h - deflect / 3)), 181 curve.quad((c3 * w, h + s3 * deflect), (0pt, h)), 182 curve.quad((s4 * deflect, c4 * h), (0pt, 0pt)), 183 ) 184 place(dx: .2em, dy: .15em, c(text.fill.transparentize(50%))) 185 place(c(text.fill.transparentize(20%))) 186 blocked-body 187}) 188 189#let post-it( 190 body, 191 fill: rgb("#FDE85F"), 192 angle: -10deg, 193 sloppiness: .2, 194) = context { 195 let hash = hashed-position() 196 let rng = suiji.gen-rng-f(hash) 197 let (rng, angle-deviation) = suiji.uniform-f(rng, low: -1, high: 1) 198 let angle = (1 + sloppiness * angle-deviation) * angle 199 let (rng, color-deviation) = suiji.uniform-f(rng, low: -1, high: 1) 200 let fill = fill.darken(sloppiness * color-deviation * 40%) 201 202 rotate( 203 angle, 204 reflow: false, 205 { 206 place( 207 dx: .0em, 208 dy: .0em, 209 umbra.shadow-path( 210 (1cm, 1cm), 211 (1cm, 5cm), 212 (5cm, 5cm), 213 (5cm, 4.9cm), 214 (4cm, 1cm), 215 correction: 10deg, 216 closed: false, 217 ), 218 ) 219 place( 220 curve( 221 fill: fill, 222 curve.line((5cm, 0cm)), 223 curve.quad((5cm, 2.5cm), (5.2cm, 5cm)), 224 curve.quad((2.5cm, 5.2cm), (.1cm, 5cm)), 225 curve.quad((0cm, 2.5cm), (0cm, 0cm)), 226 ), 227 ) 228 box(inset: .5em, width: 5cm, height: 5cm, clip: true, body) 229 }, 230 ) 231}