Published
Edited
Nov 28, 2020
2 stars
Insert cell
Insert cell
Insert cell
Insert cell
frame = t => {
// const view = m.lookAt([],[4,10,4],[4,4,4],[0,0,-1]);
const view = m.lookAt([],[6,6,6],[4,4,4],[0,1,0]);
const project = m.ortho([],-4,4,-4,4,-1,1);
//const background = svgt`<rect width=512 height=512 fill='#ffffff'/>`;
const background = svgt`<image href="${backgroundUrl}" width=512 height=512 preserveAspectRatio="xMaxYMid slice"/>`;
const blocks = g(background,...range(8).flatMap(z=>range(8).map(x=>block(z,x))))
//const starField = g(...stars.map(star(t)));
return svgtBox(transform(project,changingView(t),blocks),512,512);
}
Insert cell
Insert cell
block = (z,x) => {
const coords = [[0,0,1],[1,0,1],[1,0,0],[1,1,0],[0,1,0],[0,1,1]];
const attrs = {
fill:(z+x)%2?'black':'white',
}
const lg = x => 1/(1+Math.exp(-x));
//const h = (8-Math.min(cam.pos *x,z))
const y = x;//lg(-z+20*cam.pos-5) * x;
const h = 2.5; //1+5*Math.sin((7-x+z)/14);
return translate([x,y,z],scale([1,h,1],svgt`<polygon points="${coords}" ${attrs}/>`))
}
Insert cell
// a view matrix for looking at [4,4,4] from startPos (t=0)
// moving to endPos (t=1)
changingView = t => {
const e = d3.easeCubicInOut(t);
const startPos = [4,10,4];
const endPos = [10,4,4];
const pivot = [7,7,0];
const startDir = v.sub([],startPos,pivot);
const endDir =v.sub([],endPos,pivot)
const axis = v.cross3([],startDir,endDir);
const endAngle = v.angleBetween3(startDir,endDir);
const camPos = v.add([],pivot,m.mulV([],m.rotationAroundAxis33([],axis,e*endAngle,true),startDir))
const top = m.mulV([],m.rotationAroundAxis33([],[1,0,0],e*0.5*Math.PI,true),[0,0,-1]);
//const top = m.mulV([],m.rotationAroundAxis33([],[0,0,1],t*0.5*Math.PI,true),[1,0,0]);
return m.lookAt([],camPos,[4,4,4],top)
}
Insert cell
stars = range(500).map(() => [Math.random()*40-20, Math.random()*40-20, Math.random()*40-20])
Insert cell
star = t => xyz =>
m.mulV([],changingView(t),[...xyz,0])[2]<0
? svgt`<circle cx="${xyz[0]}" cy="${xyz[1]}" ${xyz[2]} r=5 stroke="black" fill="none"/>`
: svgt``;
Insert cell
backgroundUrl = await FileAttachment('spaceinvaders.jpg').url()
Insert cell
import {svgtScreenBox,svgtBox,svgt,g,translate,scale, transform} from '@sanderevers/svg-transformations'
Insert cell
import {form} from '@mbostock/form-input'
Insert cell
range = n => [...Array(n).keys()]
Insert cell
m = require('https://bundle.run/@thi.ng/matrices@0.6.34')
Insert cell
v = require('https://bundle.run/@thi.ng/vectors@4.4.0')
Insert cell
jszip = require('https://bundle.run/jszip@3.2.0')
Insert cell
import {serialize} from '@mbostock/saving-svg'
Insert cell
backgroundBitmap = createImageBitmap(await FileAttachment("spaceinvaders.jpg").image(),256,0,768,768);

Insert cell
// adapted from @mbostock/saving-svg
function rasterize(svg) {
let resolve, reject;
const promise = new Promise((y, n) => (resolve = y, reject = n));
const image = new Image;
image.onerror = reject;
image.onload = () => {
try {
const s = 256; // makes 512x512 PNGs. Maybe a Mac thing??
const context = DOM.context2d(s, s);
context.drawImage(backgroundBitmap, 0, 0, s, s); // have to do this manually
context.drawImage(image, 0, 0, s, s); // because JPG doesn't load here (browser security?)
context.canvas.toBlob(resolve);
}
catch (e) { mutable debug=e.toString(); reject(e) }
};
image.src = URL.createObjectURL(serialize(svg));
return promise;
}
Insert cell
import {animationControl} from '@sanderevers/animation-control'
Insert cell
d3 = require('d3-ease')
Insert cell
makezip = async () => {
const nframes = 100;
const zip = new jszip();
const frames = zip.folder('frames');
for (const i of range(nframes)) {
const fi = String(1000+i).substring(1);
frames.file(`frame${fi}.png`,await rasterize(frame(i/nframes)));
}
const content = await zip.generateAsync({type:'blob'});
return content;
}
// convert -delay 2 -loop 0 frames/frame*.png out.gif
Insert cell
mutable debug = ''
Insert cell
DOM.download(() => makezip(), 'swirl.zip', "download ZIP")
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more