Public
Edited
Feb 9, 2023
3 stars
Insert cell
Insert cell
{
let ar=new Array(length).fill(0)
for (let i=0;i<length;i+=2) ar[i]=1
return maketable([ar])
}
Insert cell
Insert cell
{
let ar=new Array(length).fill(0)
for(let k=2; k<length*2; k*=2) {
for(let i=k/2-1;i<length;i+=k)
ar[i]=Math.log2(k)
yield maketable([ar])
await Promises.tick(1000)
}
}
Insert cell
Insert cell
function* genseq(){
yield 1
for (let x of genseq()){yield x+1; yield 1}
}
Insert cell
showSequence(genseq())
Insert cell
Insert cell
Insert cell
showGrid((x,y)=>(x+y)%2)
Insert cell
Insert cell
Insert cell
function tile(i,j){
if (i==0 && j==0)return 0 // Special case for the top left corner - consider it to be infinite
if ((i+j)%2)
return 1
else
return 1+tile((i+j)/2,(i-j)/2) //rotate and scale
}
Insert cell
showGrid(tile)
Insert cell
Insert cell
Insert cell
{//ldluluurdrruruluruululldrddldlullururul
while(true){
// Greedily search for appropriate non-intersecting paths
let seen = {}
function valid(i,j,n) {return !seen[[i,j]] && tile(i,j)==n}
let pos = [0,0]
let path = ""
for(let val of genseq()){
let opts=[[1,0,"r"],[-1,0,"l"],[0,1,"d"],[0,-1,"u"]]
shuffle(opts)
let stop=true
for (let dir of opts){
let nx = [pos[0]+dir[0],pos[1]+dir[1]]
if (valid(...nx,val)){
pos=nx
seen[[pos[0],pos[1]]]=true
stop=false
path+=dir[2]
break
}
}
if(stop)break
}
yield html`${pathOnGrid(turtle(path))}`
await Promises.delay(1000)
}
}
Insert cell
Insert cell
{
let path = "uldllurululdldl"
let res = pathOnGrid(turtle(path))
let svg=res.children[1]
yield res
while (true){
for (let tr of ["rotate(.125turn) scale(0.707)","","rotate( -.125turn) scale(1.414)",""]){
await Promises.delay(1000)
svg.style.transform=tr
await Promises.delay(1000)
}
}
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
function* rewrite(seq,sys){
for(let sym of seq) {
if (sys[sym]!=undefined) yield* sys[sym]
else yield sym
}
}
Insert cell
{
let sys = {"a":"ba","b":"a|"}
let seq = ["a"]
while(true){
yield seq.join("")
await Promises.delay(1000)
seq = Array.from(rewrite(seq,sys))
if(seq.length>30) seq=["a"]
}
}
Insert cell
Insert cell
{
let lsystem = {
get f() {return Math.random()>.5?"7f2f7":"1f6f1"}
// 1 means a right turn by 1/8 of a cirle
// so 7 means a left turn by that much
}
return drawSystem("f",lsystem)
}
Insert cell
Insert cell
drawSystem("l",{ "l":"7l2r7","r":"1l6r1"})
Insert cell
Insert cell
drawSystem("f",{ "f":"7f2f7"},[5,height*3/4])
Insert cell
Insert cell
Insert cell
{//TODO: figure out how to fit this onto the grid
let path = [[0,0],[0,1]]
while(true){
let res = pathOnGrid(turtle(path))
//res.children[1].style.transform="scale(0.25)"
yield res
await Promises.delay(1000)
res.children[1].style.transform="rotate(-.125turn) scale(1.414)"
await Promises.delay(1250)
path=path.map(p => [p[0]+p[1],p[1]-p[0]])
//Build the data
let options = [] //the choices of corner available for the corner inserted between path[i]<->path[i+1]
let candidates = {} //the edges
for(let i=0; i<path.length-1; i++){
options.push(
[[path[i][0],path[i+1][1]], [path[i+1][0],path[i][1]]]
)
for(let p of options[i]){
if(!candidates[p])candidates[p]=[i]
else candidates[p].push(i)
}
}
function attempt(i,dir){
let tmp = options[i]
options[i]=[dir]
for(let j of candidates[dir]){
if (j!=i){
let safe=true
let o = options[j]
if (o.length==1) {
safe = (o[0]+""!=""+dir)
} else {
safe = attempt(j,o[o[0]+""==""+dir?1:0])
}
if(!safe){
options[i]=tmp
return false
}
}
}
return true//permanently modify options[i]
}
let newpath = [[0,0]]
for(let i=1; i<path.length; i++){
let stop=true
for (var tst of options[i-1]){
if (attempt(i-1,tst)){
stop=false
break
}
}
if (stop) {//show something interesting then restart
for(;i<path.length;i++){
if(options[i-1].length==1) newpath.push(options[i-1][0])
newpath.push(path[i])
}
yield pathOnGrid(turtle(newpath))
await Promises.delay(5000)
newpath = [[0,0],[0,1]]
break
}
newpath.push(tst,path[i])
}
path=newpath
}
}
Insert cell
Insert cell
drawSystemWithSquares("f",{ "f":"7f2f7"},[5,height*3/4])
Insert cell
Insert cell
drawSystem("f",{"f":"7f2f2f2f2g2g7","g":"7g2g7"},undefined,30000)
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
html`<svg width=100% viewbox="-700 -2800 5500 4200">${turtle(z310)}</svg>`
Insert cell
Insert cell
answers = {
let symbs = "abcd"
let dat = [["b",7,"c"],["a",7,"a"],["a",7,"a"],["a",7,"a"]] // A representation of the L-System currently being checked
let results = []
function toSys(dat){
let sys = {}
for (let s of symbs){
let d = dat[symbs.indexOf(s)]
let o = (16 - 2*d[1])%8
sys[s] = d[1]+d[0]+o+d[2]+d[1]
}
return sys
}
while(true){
if (Math.random()>0.999) yield dat+""
//adat
for (let i=1;;i++){
if(i==4) {
yield results
return
}
else {
dat[i][0] = symbs[(symbs.indexOf(dat[i][0])+1)%4]
if(dat[i][0]!="a") break;
dat[i][1] = 8-dat[i][1]
if(dat[i][1]!=7) break;
dat[i][2] = symbs[(symbs.indexOf(dat[i][2])+1)%4]
if(dat[i][2]!="a") break;
}
}
let sys = toSys(dat)
// collision test
let seq = ["a"]
let collides=false;
for(let i=0;i<7;i++){
seq = Array.from(rewrite(seq,sys))
let vstd = {}
let d = 0
let p=[0,0]
for (let s of seq){
if (isNaN(s)){
p[0]+=(d&2)*((d&4)-2); p[1]+=((~d)&2)*((d&4)-2);
if (vstd[p]) {
collides=true; break
}
else {
vstd[p]=true
}
}
else d= (s-(-d))%8
}
if (collides) break
}
if(!collides) results.push(sys)
}
}
Insert cell
drawSystem("a",answers[1],undefined,10000)
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
async function* drawSystem(start,sys,offset,lim){
offset=offset||[5,height/2]
lim=lim||300
let seq=[start]
let i=0
while(true){
yield pathOnGrid(turtle(seq.map(x=>isNaN(x)?"f":x),
[cs*Math.pow(1.414,6-i),0]),
[offset[0],offset[1]])
await Promises.delay(1000)
i+=1
if(seq.length>lim) {
seq=[start]
i=0
await Promises.delay(2000)
}else {seq = Array.from(rewrite(seq,sys))}
}
}
Insert cell
async function* drawSystemWithSquares(start,sys,offset){
//TODO: refactor so the line uses the same method as squares
// Also combine with drawSystem
offset=offset||[5,height/2]
let seq=[start]
let i=0
while(true){
let t = turtle(seq.map(x=>isNaN(x)?"f":x), [cs*Math.pow(1.414,6-i),0]).outerHTML
let pts = Array.from(turtpoints(seq.map((x,i)=>isNaN(x)?"f":x), [cs*Math.pow(1.414,6-i),0]))
for(let j=0;j<pts.length-1;j++){
t+=mkSquare(pts[j],pts[j+1])
}
yield pathOnGrid(t,[offset[0],offset[1]])
await Promises.delay(1000)
seq = Array.from(rewrite(seq,sys))
i+=1
if(seq.length>60) {
seq=[start]
i=0
}
}
}
Insert cell
function readkey(){//for debugging TODO: consider removing
return new Promise(resolve => window.addEventListener("keypress",resolve,{"once":true}))
}
Insert cell
function mkSquare(c0,c1){
c0=c0.map(Math.round)
c1=c1.map(Math.round)
let a = angles(c0,c1)
return `<polygon fill="rgba(0,0,0,0.5)" points="${[c0,a[0],c1,a[1]]}" />`
}
Insert cell
function mkPath(verts){
}
Insert cell
//pathOnGrid(mkSquare([[4,5],[60,70]]))
Insert cell
function angles(p,q){//given opposite verticies of a square, return the other pair of vertices
let mid = [(p[0]+q[0])/2,(p[1]+q[1])/2]
let delta=[mid[0]-p[0], mid[1]-p[1]]
return [[mid[0]+delta[1],mid[1]-delta[0]],
[mid[0]-delta[1],mid[1]+delta[0]]]
}
Insert cell
function turtle(instrs,dir){// Turtle
//instrutions: u d l r [3,3] f 7
// up down left right goto[3,3] forward rotate(7*pi/4)
dir=dir||[cs,0]
let axes = ["v-","v", "h-", "h"]
let path = "M 0 0"
let lg=""
for (let i of instrs)
if (i=="f") path+=`l ${dir[0]} ${dir[1]}`
else if("udlr".indexOf(i)!=-1)path+=axes["udlr".indexOf(i)]+cs
else if (i.length==2) path+=`L ${i[0]*cs} ${i[1]*cs}`
else dir=rotate(dir,i)
//console.log(lg)
return svg`<path stroke-width=2px stroke="black" fill=transparent d="${path}" />`
}
Insert cell
function pathOnGrid(path,pos,h,l){// Turtle graphics
h = h || height
l = l || length
pos = pos || [l/2,h/2]
pos=pos.map(Math.floor)
return html`
${showGrid((i,j)=>tile(i-pos[0],j-pos[1]))}
<svg style="position:absolute;left:0;top:0;height:${cs*h}px;transition:1s" viewbox="-${cs*(pos[0]+0.5)} -${cs*(pos[1]+0.5)} ${cs*l} ${cs*h}" xmlns="http://www.w3.org/2000/svg">
${path}
</svg>`
}
Insert cell
function* turtpoints(instrs,dir){
//instrutions: u d l r [3,3] f 7
// up down left right goto[3,3] forward rotate(7*pi/4)
dir=dir||[cs,0]
let pos = [0,0]
yield pos.slice()
for (let i of instrs) {
let yld=true
switch (i){
case 'f':
pos[0]+=dir[0]
pos[1]+=dir[1]
break;
case 'u':
pos[1]-=cs; break
case 'd':
pos[1]+=cs; break
case 'l':
pos[0]-=cs; break
case 'r':
pos[0]+=cs; break
default:
if (i.length==2) pos=i.map(x=>x*cs)
else {
dir=rotate(dir,i)
yld=false
}
}
if (yld) yield pos.slice();
}
}
Insert cell
maketable([[2,3,4],[4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20]])
Insert cell
function showSequence(f){
if(!f.next)f=(function* (g){let i=0;while(true){yield g(i);i++}})(f)
let ar=new Array(length)
for(let i=0;i<length;i++) ar[i]=f.next().value
return maketable([ar])
}
Insert cell
function showGrid(f,h,l){
if(!h)h=height
if(!l)l=length
let ar=new Array(h)
for (let y=0;y<h;y++){
ar[y]=new Array(l);
for(let x=0;x<l;x++){
ar[y][x]=f(x,y)
}
}
return maketable(ar)
}
Insert cell
function maketable(data){
style;//depends on style
var s=`<table style=width:inherit>`
for (const row of data){
s+='<tr>'
for (const col of row){
s+=`<td class="c${col}">${col}</td>`
}
s+="</tr>"
}
s+="</table>"
return html`${s}`
}
Insert cell
style ={
let s=""
for(let i=0;i<20;i++) s+=`.c${i}{background-color:${getCol(i)}}`+"\n"
return html`<style>
td{
//padding-top:0px;
width:${cs-2}px;
min-width:${cs-2}px;
padding:1px!important;
}
tr{
line-height:${cs-2}px;
text-align:center;
}
tr:not(:last-child){
border-bottom:0px; // Something not to like about Observable
}
${s}
</style>`
}
Insert cell
function rotate(vec,theta){//rotate a vector by theta*45 degrees
theta*=Math.PI/4
return [vec[0]*Math.cos(theta)-vec[1]*Math.sin(theta) , vec[0]*Math.sin(theta)+vec[1]*Math.cos(theta)]
}
Insert cell
length=19
Insert cell
height=17
Insert cell
//colours=["#fff2f2", "#fededd", "#fdcdc8", "#fab69f", "#f5b67a", "#edd15c", "#bce443", "#4fd731", "#23c778", "#1983b3", "#22129e", "#770e87"]
Insert cell
showSequence(genseq())
Insert cell
function getCol(i){
return `hsl(${280-i*25},${60}%,${100-10*i+i*i/1.7}%)`
}
Insert cell
showGrid(tile)
Insert cell
{
let cols = []
for (let i=0;i<12;i++)cols[i]=getCol(i)
return cols
}
Insert cell
function shuffle(l){
for(let k=0;k<l.length;k++){
let i=k+Math.floor(Math.random()*(l.length-k))%(l.length-k)
let t=l[k]
l[k]=l[i]
l[i]=t
}
}
Insert cell
cs=21
Insert cell
Insert cell

One platform to build and deploy the best data apps

Experiment and prototype by building visualizations in live JavaScript notebooks. Collaborate with your team and decide which concepts to build out.
Use Observable Framework to build data apps locally. Use data loaders to build in any language or library, including Python, SQL, and R.
Seamlessly deploy to Observable. Test before you ship, use automatic deploy-on-commit, and ensure your projects are always up-to-date.
Learn more