main.js (7606B)
1 const m = require("lib/mithril"); 2 const stream = require("lib/mithril-stream"); 3 const zimg = require("./zoomimg.js"); 4 const custom_file_upload = require("./custom_file.js"); 5 6 const example_graydata = require("data/data.js").example_grayscale; 7 const example_colordata = require("data/data.js").example_color; 8 9 async function base64_to_img(txt) { 10 return new Promise((res, rej) => { 11 const img = new Image(); 12 img.src = "data:;base64," + txt; 13 img.addEventListener("load", () => res(img)); 14 }); 15 } 16 17 const img_color = await base64_to_img(example_colordata); 18 const img_gray = await base64_to_img(example_graydata); 19 20 // store all update streams in a container to avoid having a pile of local 21 // variables 22 const stream_container = {}; 23 // select a stream from the container 24 const zoom_stream = (st) => { 25 if (stream_container[st] == undefined) 26 stream_container[st] = stream(); 27 return stream_container[st]; 28 }; 29 const zoom_stream_list = () => Object.keys(stream_container); 30 31 // list of mithril components to be mounted in their containers 32 const components = [ 33 {view: (vnode) => m(zimg, { 34 size: 601, 35 image: (x, y) => 0, 36 noise: (x, y, val) => { 37 let color = 255 * ((x + y) % 2); 38 return [color, color, color]; 39 }, 40 update: zoom_stream("demo1-checker"), 41 })}, 42 {view: (vnode) => m(zimg, { 43 size: 601, 44 image: (x, y) => { 45 const square = 0.2 <= x && x < 0.6 && 0.4 <= y && y < 0.8; 46 const circle = ((x - 0.6) ** 2 + (y - 0.4) ** 2) < 0.2**2; 47 return (square + circle) % 2; // xor 48 }, 49 noise: (x, y, val) => { 50 let color = 255 * ((x + y + val) % 2); 51 return [color, color, color]; 52 }, 53 update: zoom_stream("demo2-checkerinvert"), 54 })}, 55 {view: (vnode) => m(zimg, { 56 size: 600, 57 image: (x, y) => x >= 0.5, 58 noise: (x, y, val) => { 59 let color = val? 128 : Math.random() * 255; 60 return [color, color, color]; 61 }, 62 update: zoom_stream("demo3-grayrandom"), 63 })}, 64 {view: (vnode) => m(zimg, { 65 size: 601, 66 image: (x, y) => { 67 return 0.3 <= x && x < 0.7 && 0.3 <= y && y < 0.7; 68 }, 69 noise: (x, y, val) => { 70 let color = 128; 71 if (val) color = (x + y) % 2 * 256; 72 return [color, color, color]; 73 }, 74 update: zoom_stream("demo4-checkergray"), 75 })}, 76 {view: (vnode) => m(zimg, { 77 size: 601, 78 image: (x, y) => { 79 return 0.3 <= x && x < 0.7 && 0.3 <= y && y < 0.7; 80 }, 81 noise: (x, y, val) => { 82 const RAND = 16; 83 let color = 128; 84 if (val) color = RAND + (x + y) % 2 * (256 - RAND * 2); 85 color = color - RAND + Math.random() * RAND * 2; 86 return [color, color, color]; 87 }, 88 update: zoom_stream("demo5-checkergrayrandom"), 89 })}, 90 {view: (vnode) => m(zimg, { 91 size: 400, 92 image: (x, y) => { 93 return 0.3 <= x && x < 0.7 && 0.3 <= y && y < 0.7; 94 }, 95 noise: (x, y, val) => { 96 const RAND = 16; 97 let color = 128; 98 if (val) color = RAND + (x + y) % 2 * (256 - RAND * 2); 99 color = color - RAND + Math.random() * RAND * 2; 100 return [color, color, color]; 101 }, 102 update: zoom_stream("demo5-checkergrayrandom"), 103 })}, 104 {view: (vnode) => m(zimg, { 105 size: 601, 106 image: (x, y) => { 107 return 0.3 <= x && x < 0.7 && 0.3 <= y && y < 0.7; 108 }, 109 noise: (x, y, val) => { 110 const RAND = 64; 111 let color = 150; 112 // let color = 181; // = 256 / sqrt(2) 113 if (val) color = RAND + (x + y + 1) % 2 * (256 - RAND * 2); 114 color = color - RAND + Math.random() * RAND * 2; 115 return [color, color, color]; 116 }, 117 })}, 118 {view: (vnode) => m(zimg, { 119 size: 601, 120 image: (x, y) => { 121 return 0.3 <= x && x < 0.7 && 0.3 <= y && y < 0.7; 122 }, 123 noise: (x, y, val) => { 124 let color = 128; 125 const RAND = 80; 126 let colordiff = Math.random(); 127 if (val) { 128 if ((x + y + 1) % 2 == 0) 129 colordiff = Math.max(colordiff, Math.random()); 130 else 131 colordiff = Math.min(colordiff, Math.random()); 132 } 133 color = color - RAND + colordiff * 2 * RAND; 134 return [color, color, color]; 135 }, 136 update: zoom_stream("demo6-reordering"), 137 })}, 138 {view: (vnode) => m(zimg, { 139 size: 601, 140 // image: (x, y) => { 141 // if (x < 0.5) return 0.3 <= x && 0.3 <= y && y < 0.7; 142 // return 1 - y; 143 // }, 144 image: img_gray, 145 noise: (x, y, val) => { 146 val = val[0] / 256; 147 // val: input color; 0 <= val < 1 148 let color = 128; 149 const RAND = 100; 150 // imagine a square. in x direction are the gray levels. in 151 // y direction are the infinitesimal probabilities. the 152 // square is cut by a point-symmetric line. the area above 153 // is for dark pixels on the checkerboard - below is for 154 // light pixels. if we hit randomly in one of those areas 155 // we get a color with the correct probabilities 156 let xp = Math.random(); 157 let yp = Math.random(); 158 // the dividing line is linear (this was chosen to ease 159 // computation but it may be a bad choice) 160 // it can be described by the function: 161 // -> f(x) = (1-2*val) * x + val 162 // for val = 0.5: f(x) = 0.5 163 // for val = 0: f(x) = x 164 // for val = 1: f(x) = 1 - x 165 // if we hit in the wrong area mirror at the central point 166 // != for xor 167 if ((x + y + 1) % 2 == 0 != yp < (1-2*val)*xp+val) { 168 xp = 1 - xp; 169 // yp = 1 - yp; 170 } 171 color = color - RAND + xp * 2 * RAND; 172 return [color, color, color]; 173 }, 174 update: zoom_stream("demo7-reordering-grayscale"), 175 })}, 176 {view: (vnode) => m(custom_file_upload, {color: false})}, 177 {view: (vnode) => m(zimg, { 178 size: 601, 179 image: img_color, 180 noise: (x, y, val) => { 181 return val.map(valc => { 182 valc = valc / 256; 183 let color = 128; 184 const RAND = 100; 185 let xp = Math.random(); 186 let yp = Math.random(); 187 if ((x + y + 1) % 2 == 0 != yp < (1-2*valc)*xp+valc) { 188 xp = 1 - xp; 189 } 190 return color - RAND + xp * 2 * RAND; 191 }); 192 }, 193 update: zoom_stream("demo8-color"), 194 })}, 195 {view: (vnode) => m(custom_file_upload, {color: true})}, 196 ]; 197 198 Array.from(document.querySelectorAll("div.js-container")).forEach((c, i) => { 199 m.mount(c, components[i]); 200 }); 201 202 // link the links (connect <a> tags and interactive frames) 203 zoom_stream_list().forEach(st => { 204 Array.from(document.querySelectorAll("." + st)).forEach(a => { 205 // if (!a.innerHTML.endsWith("x")) console.error("broken link text"); 206 const zoomlevel = parseFloat(a.innerHTML.slice(0, -1)); 207 a.onclick = () => { 208 zoom_stream(st)(zoomlevel); 209 // manual redraw is neccessary for handlers defined outside of 210 // mithril 211 m.redraw(); 212 }; 213 }); 214 });