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

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