{"id":"56f4faadcaa008f2","slug":"seven-eight-fourteen","trashed":false,"description":"","likes":14,"publish_level":"public","forks":0,"fork_of":{"id":"5a5ab60b2251893d","slug":"six-seven-eight","title":"Six, Seven, Eight","owner":{"id":"898d35f8860c7f03","avatar_url":"https://avatars.observableusercontent.com/avatar/0d7defa821f38094c03bad23b9b360a5364e6e97e21fc238c39ddc48db7994ad","login":"mootari","name":"Fabian Iwand","bio":"Web dev and tinkerer.","home_url":"https://twitter.com/mootari","type":"team","tier":"starter_2024"},"version":670},"has_importers":false,"update_time":"2020-02-03T09:09:10.946Z","first_public_version":null,"paused_version":null,"publish_time":"2020-02-02T21:09:59.091Z","publish_version":732,"latest_version":732,"thumbnail":"68a948b80da7edfa5d32542b02e2ec393bcb7833dec401c3f18c9c9b1f38a99d","default_thumbnail":"963722e02381b844e9dc0a967e12bca9cc6eccd10e17cfec14dcc13c10f7105d","roles":[],"sharing":null,"owner":{"id":"898d35f8860c7f03","avatar_url":"https://avatars.observableusercontent.com/avatar/0d7defa821f38094c03bad23b9b360a5364e6e97e21fc238c39ddc48db7994ad","login":"mootari","name":"Fabian Iwand","bio":"Web dev and tinkerer.","home_url":"https://twitter.com/mootari","type":"team","tier":"starter_2024"},"creator":{"id":"07362516b5994994","avatar_url":"https://avatars.observableusercontent.com/avatar/0d7defa821f38094c03bad23b9b360a5364e6e97e21fc238c39ddc48db7994ad","login":"mootari","name":"Fabian Iwand","bio":"Web dev and tinkerer.","home_url":"https://mootari.de/","tier":"pro"},"authors":[{"id":"07362516b5994994","avatar_url":"https://avatars.observableusercontent.com/avatar/0d7defa821f38094c03bad23b9b360a5364e6e97e21fc238c39ddc48db7994ad","name":"Fabian Iwand","login":"mootari","bio":"Web dev and tinkerer.","home_url":"https://mootari.de/","tier":"pro","approved":true,"description":""}],"collections":[{"id":"414312a67ea30ac3","type":"public","slug":"generative","title":"Generative","description":"Stuff with knobs to make it look more or less pretty.","update_time":"2019-08-24T08:59:06.382Z","pinned":true,"ordered":false,"custom_thumbnail":null,"default_thumbnail":"79bb0336ecb16ea71310e28b7f5ffc1c077b45f4fdc27ada83d98653018a8daf","thumbnail":"79bb0336ecb16ea71310e28b7f5ffc1c077b45f4fdc27ada83d98653018a8daf","listing_count":59,"parent_collection_count":0,"owner":{"id":"898d35f8860c7f03","avatar_url":"https://avatars.observableusercontent.com/avatar/0d7defa821f38094c03bad23b9b360a5364e6e97e21fc238c39ddc48db7994ad","login":"mootari","name":"Fabian Iwand","bio":"Web dev and tinkerer.","home_url":"https://twitter.com/mootari","type":"team","tier":"starter_2024"}},{"id":"aa0dd33c29664cca","type":"public","slug":"optical-illusions","title":"Optical Illusions","description":"","update_time":"2019-06-04T21:37:51.882Z","pinned":true,"ordered":false,"custom_thumbnail":null,"default_thumbnail":"68a948b80da7edfa5d32542b02e2ec393bcb7833dec401c3f18c9c9b1f38a99d","thumbnail":"68a948b80da7edfa5d32542b02e2ec393bcb7833dec401c3f18c9c9b1f38a99d","listing_count":5,"parent_collection_count":0,"owner":{"id":"898d35f8860c7f03","avatar_url":"https://avatars.observableusercontent.com/avatar/0d7defa821f38094c03bad23b9b360a5364e6e97e21fc238c39ddc48db7994ad","login":"mootari","name":"Fabian Iwand","bio":"Web dev and tinkerer.","home_url":"https://twitter.com/mootari","type":"team","tier":"starter_2024"}}],"files":[],"comments":[],"commenting_lock":null,"suggestion_from":null,"suggestions_to":[],"version":732,"title":"Seven, Eight, Fourteen","license":null,"copyright":"","nodes":[{"id":0,"value":"md`# Seven, Eight, Fourteen\n\n\nhttps://twitter.com/mootari/status/1224076549927899141\n`","pinned":false,"mode":"js","data":null,"name":null},{"id":562,"value":"viewof gif = Object.assign(html`<label><input type=checkbox style=\"vertical-align:baseline\"> Render a GIF`, {\n  value: false,\n  oninput(e) { this.value = e.target.checked }\n})","pinned":false,"mode":"js","data":null,"name":null},{"id":664,"value":"{\n  const duration = 5000, draw = render({duration});\n  if(gif) return yield* renderGif(invalidation, t => draw(t), duration, {\n    fps: 40, preview: draw(0),\n    filename: `seven-eight-fourteen-${Date.now()/1000|0}`,\n  });\n  const to = Date.now();\n  while(true) yield draw(((Date.now() - to) / duration) % 1);\n}","pinned":true,"mode":"js","data":null,"name":null},{"id":4,"value":"function render(options = {}) {\n  const {\n    size = 600,\n    outerStroke = 15,\n    innerStroke = 5,\n    fillColor = 'hsl(30,40%,98%)',\n    outerColor = 'hsl(30,40%,80%)',\n    innerColor = 'hsl(30,40%,30%)',\n    duration = 5000,\n    ease = easeInOut(2),\n  } = options;\n  const boxSize = 50, boxOffset = 100;\n\n  const PI = Math.PI, PIQ = PI / 2;\n  const ctx = DOM.context2d(size, size, 1);\n  const illo = new Zdog.Illustration({\n    element: ctx.canvas,\n    zoom: size/300,\n    rotate: { x: -PI/5.1 , y:-PI/4 },\n  });\n  illo.pixelRatio = 1;\n  ctx.canvas.width = illo.width = size;\n  ctx.canvas.height = illo.height = size;\n  ctx.canvas.style.maxWidth = '100%';\n  ctx.canvas.style.height = 'auto';\n  \n  function createCube([x, y, z]) {\n      const origin = new Zdog.Anchor({translate: {x: x*boxOffset, y: y*boxOffset, z: z*boxOffset}});\n      const box = new Zdog.Box({\n        addTo: new Zdog.Group({addTo: origin, updateSort: true}),\n        width: boxSize, height: boxSize, depth: boxSize, fill: false\n      });\n      return {origin, box};\n  }\n\n  function createCubeSet(offsets) {\n    const root = new Zdog.Anchor();\n    const boxes = offsets.map(createCube);\n    for(let b of boxes) root.addChild(b.origin);\n    return {root, boxes};\n  }\n  \n  const axesOffsets = offsets => offsets.map(s => [0, 1, 2].map(o => [0, 1, 2].map(i => i === o ? s : 0))).flat();\n  const cornerOffsets = offsets => offsets.map(x => [-.5, .5].map(y => [-.5, .5].map(z => [x, y, z]))).flat(2);\n  \n  const sets = [\n    {\n      rotate: [-1, 0, 0],\n      ...createCubeSet(axesOffsets([-1, 1]).concat([[0,0,0]])),\n    },\n    {\n      rotate: [0, 0, 1],\n      ...createCubeSet(cornerOffsets([-.5, .5])),\n    },\n    {\n      rotate: [0, -1, 0],\n      ...createCubeSet(cornerOffsets([-.5, .5]).concat(axesOffsets([-.5, .5]))),\n    },\n  ];\n  \n  const inactive = new Zdog.Anchor();\n  const noop = () => {}, clear = function(x, y, w, h) {\n    this.fillStyle = fillColor;\n    this.fillRect(x, y, w, h);\n  };\n  \n  return t => {\n    for(let s of sets) inactive.addChild(s.root);\n    const {root, rotate, boxes} = sets[t * sets.length | 0];\n    illo.addChild(root);\n    const [x, y, z] = rotate, a = ease((t * sets.length) % 1) * PIQ;\n    root.rotate = { x: x * a, y: y * a, z: z * a };\n\n    for(let {box} of boxes) box.color = outerColor, box.stroke = outerStroke;\n    ctx.clearRect = clear; illo.updateRenderGraph();\n    for(let {box} of boxes) box.color = innerColor, box.stroke = innerStroke;\n    ctx.clearRect = noop; illo.updateRenderGraph();\n    return illo.element;\n  }\n}","pinned":true,"mode":"js","data":null,"name":null},{"id":93,"value":"function easeInOut(a) {\n  return t => t ** a / (t ** a + (1 - t) ** a);\n}","pinned":true,"mode":"js","data":null,"name":null},{"id":610,"value":"md`---\n## Dependencies`","pinned":false,"mode":"js","data":null,"name":null},{"id":2,"value":"Zdog = require('zdog@1.1.1/dist/zdog.dist.min.js')","pinned":false,"mode":"js","data":null,"name":null},{"id":489,"value":"import {renderGif} from '88a8246df5677bae'","pinned":false,"mode":"js","data":null,"name":null}],"resolutions":[],"schedule":null,"last_view_time":null}