Download code
From LiteratePrograms
Back to Turtle_graphics_(JavaScript)
Download for Windows: single file, zip
Download for UNIX: single file, zip, tar.gz, tar.bz2
terrapin.htm
1 <html><head> 2 <title>a buggy concatenative turtle</title> 3 <!-- 20070131 [DL] swap renamed flip. improve tokenization --> 4 <!-- 20060822 [DL] lame kludge for Opera 9.01 --> 5 <!-- 20060820 [DL] written --> 6 <script type="text/javascript"><!-- 7 function codevec(l) 8 { 9 var v = l.slice(0) 10 v.sarg = function() { return checkarg(this.shift()) } 11 v.varg = function() { return [].concat(this.sarg()) } 12 v.call = function(l) { 13 for(var w = codevec(l); w.length; this.unshift(w.pop())); } 14 return v 15 } 16 17 var plan = {} // source tokens for user-defined words 18 var user = {} // functions for user-defined words 19 var dict = { // functions for system-defined words 20 'pen': function(ctx,v) { ctx.penState = !ctx.penState }, 21 'mirror': function(ctx,v) { ctx.scale(1,-1) }, 22 'reverse': function(ctx,v) { ctx.scale(-1,1) }, 23 'turn': function(ctx,v) { ctx.rotate(v.sarg()*3.1415926/180) }, 24 'forward': function(ctx,v) { ctx.translate(v.sarg(),0); ctx.displace() }, 25 'scale': function(ctx,v) { 26 var sc = v.sarg() 27 ctx.lineWidth /= sc 28 ctx.scale(sc,sc) 29 }, 30 'repeat': function(ctx,v) { 31 var n = v.sarg() 32 var rv = v.varg() 33 if(n < 0) { n = -n; rv = invert(rv) } 34 while(n--) { interp(ctx, rv) } 35 ///////////////////// 36 // if(n == 0) return 37 // v.call(rv.concat(['repeat',n-1,rv])) 38 ///////////////////// 39 }, 40 'poly': function(ctx, v) { 41 var n = v.sarg() 42 var a = 360/(n<0?-n:n) 43 v.call(['repeat',n,v.varg().concat(['turn',a])]) }, 44 'def': function(ctx,v) { 45 var name = v.shift() 46 plan[name] = v.varg() 47 user[name] = function(ctx,v) { v.call(plan[name]) } 48 }, 49 'inverse': function(ctx,v) { v.call(invert(v.varg())) }, 50 'identity': function(ctx,v) { v.call( v.varg() ) }, 51 'under': function(ctx,v) { 52 var av = v.varg() 53 var uv = v.varg() 54 v.call(av.concat(uv.concat(invert(av)))) 55 }, 56 'within': function(ctx,v) { 57 var av = v.varg() 58 var wv = v.varg() 59 v.call(av.concat(wv.concat(av))) 60 }, 61 'flip': function(ctx,v) { 62 var fn = v.sarg() 63 var bv = v.varg() 64 var av = v.varg() 65 v.call([fn,av,bv]) 66 }, 67 '': ignore 68 69 } 70 function invert(ov) 71 { 72 var ov = codevec(ov) 73 var nv = [] 74 while(ov.length) { 75 var e = ov.sarg() 76 switch(e) { 77 case 'pen': 78 case 'mirror': 79 case 'reverse': 80 nv.unshift(e) 81 break 82 case 'turn': 83 case 'forward': 84 nv = [e,-ov.sarg()].concat(nv) 85 break 86 case 'scale': 87 nv = [e,1/ov.sarg()].concat(nv) 88 break 89 case 'repeat': 90 case 'poly': 91 var c = ov.sarg() 92 var ev = ov.varg() 93 nv = [e,-c,ev].concat(nv) 94 break 95 case 'inverse': 96 nv = ['identity',ov.varg()].concat(nv) 97 break 98 case 'identity': 99 nv = ['inverse',ov.varg()].concat(nv) 100 break 101 case 'under': 102 var av = ov.varg() 103 var uv = ov.varg() 104 nv = [e,av,invert(uv)].concat(nv) 105 break 106 case 'within': 107 var av = ov.varg() 108 var wv = ov.varg() 109 nv = [e,invert(av),invert(wv)].concat(nv) 110 break 111 case 'flip': 112 var fn = ov.sarg() 113 var bv = ov.varg() 114 var av = ov.varg() 115 e = [fn,av,bv] 116 // fall through 117 default: 118 return invert(e.concat(ov)).concat(nv) 119 } 120 } 121 return nv 122 } 123 124 125 126 function checkarg(e) { return (plan[e] || e) } 127 function checkop(e) { return (dict[e] || user[e] || ignore) } 128 129 function ignore(ctx, v) { } 130 function donext(ctx, v) { checkop(v.shift())(ctx,v) } 131 function interp(ctx, v) { for(v = codevec(v); v.length; donext(ctx,v)); } 132 133 function tokens(s) { return s.replace(/([\[\]])/g," $1 ").split(/\s+/) } 134 function tree(v) 135 { 136 var t = [] 137 v.nxt = function() { return this.length ? this.shift() : ']' } 138 for(var e = v.nxt(); e != ']'; e = v.nxt()) { t.push(e == '[' ? tree(v) : e) } 139 return(t) 140 } 141 142 function turtle(t,c) 143 { 144 ///////////////////////// 145 if(document.getElementById("kludge").checked) { 146 dict['forward'] = function(ctx,v) { 147 var x = v.sarg() 148 if(ctx.penState) { 149 ctx.beginPath() 150 ctx.moveTo(0,0) 151 ctx.lineTo(x,0) 152 ctx.stroke() 153 } 154 ctx.translate(x,0) 155 } 156 } else { 157 dict['forward'] = function(ctx,v) { 158 ctx.translate(v.sarg(),0); ctx.displace() } 159 } 160 ///////////////////////// 161 162 var txt = document.getElementById(t) 163 var cvs = document.getElementById(c) 164 var src = txt.value 165 var ctx = cvs.getContext("2d") 166 167 ctx.save() 168 ctx.clearRect(0,0,cvs.width,cvs.height) 169 ctx.translate(cvs.width/2,cvs.height/2) 170 ctx.beginPath() // start track display 171 ctx.moveTo(0,0) 172 173 ctx.penState = 1 174 ctx.displace = function() { this.penState ? ctx.lineTo(0,0) 175 : ctx.moveTo(0,0) } 176 plan = {} 177 user = {} 178 interp(ctx, tree(tokens(src))) 179 180 ctx.stroke() // show the track 181 ctx.beginPath() // start turtle display 182 ctx.moveTo(0,-5) 183 ctx.lineTo(20,0) 184 ctx.lineTo(0, 5) 185 ctx.closePath() 186 ctx.fill() // show the turtle 187 ctx.restore() 188 } 189 190 function init() { document.getElementById("kludge").checked = !!window.opera } 191 192 //--></script> 193 <style type="text/css"> 194 body { font-family: arial; font-size: 10pt; 195 background-color: #CCC; color: #000; } 196 h1 { font-size: 14pt; } 197 canvas { background-color: #EEE; } 198 textarea { background-color: #EEE; } 199 .doc { float: right; border: 1px solid #000; background-color: #FEC; } 200 </style></head><body onLoad="init();turtle('txt','cvs')"> 201 <h1>a buggy concatenative turtle <i>(mobilis terrapin)</i></h1> 202 <canvas height=256 width=256 id="cvs" onClick="turtle('txt','cvs')"></canvas> 203 <button onClick="turtle('txt','cvs')"><< draw </button> 204 <textarea cols=60 rows=20 id="txt" onPaste="turtle('txt','cvs')"> 205 def jog [ forward 30 under [ turn 45 ] [ forward 10 ] ] 206 def joggle [ under jog reverse reverse ] 207 poly 16 [ poly 4 joggle ] 208 </textarea> 209 <p>Requires javascript and <canvas> support <small> (Safari,Opera,Firefox?)</small>. Userproofing not included.</p> 210 <p>My Opera can't hack matrix changes while drawing — if you don't see any output, enable 211 <label for="kludge"> the 212 <input id="kludge" type="checkbox" onClick="turtle('txt','cvs')"/> 213 bletcherous kludge</label></p> 214 <hr> 215 <div class="doc"> 216 <table> 217 <tr><td>pen </td><td> toggle pen up/down</td></tr> 218 <tr><td>mirror </td><td> toggle left/right</td></tr> 219 <tr><td>reverse </td><td> toggle forward/back</td></tr> 220 <tr><td>turn X </td><td> rotate X degrees</td></tr> 221 <tr><td>forward X </td><td> translate X units</td></tr> 222 <tr><td>scale X </td><td> multiply unit size</td></tr> 223 <tr><td></td></tr> 224 <tr><td>repeat N [ ] </td><td> repeat a plan N times</td></tr> 225 <tr><td>poly N [ ] </td><td> repeat with implicit turning</td></tr> 226 <tr><td>within [ ] [ ] </td><td> sandwich a plan<br>(within a b == a b a)</td></tr> 227 <tr><td></td></tr> 228 <tr><td>inverse [ ] </td><td> produce the opposite of a plan <br>(inverse a == a<sup>-1</sup>)</td></tr> 229 <tr><td>under [ ] [ ] </td><td> make a do/undo sandwich<br>(under a b == a b a<sup>-1</sup>)</td></tr> 230 <tr><td></td></tr> 231 <tr><td>flip NAME </td><td> switch argument order<br>(flip f b a == f a b)</td></tr> 232 <tr><td>def NAME [ ] </td><td> define a subplan<br>(sorry, no arguments)</td></tr> 233 </table> 234 </div> 235 repeat 4 [ forward 100 turn 90 ]<br> 236 <hr> 237 poly 6 [ poly 36 [ reverse pen forward 20 ] ]<br> 238 <hr> 239 def spot [ poly 6 [ forward 10 ] ]<br> 240 def arm [ under [ forward 70 turn -60 ] ]<br> 241 poly 12 [ arm spot ]<br> 242 <hr> 243 def coil [ repeat 300 [ turn 5 forward 5 scale .99 ] ]<br> 244 def branch [ flip under [ ] ]<br> 245 poly 2 [ branch coil ]<br> 246 <hr> 247 def jog [ forward 30 under [ turn 45 ] [ forward 10 ] ]<br> 248 def joggle [ under jog reverse reverse ]<br> 249 poly 6 [ poly 5 joggle ]<br> 250 <hr> 251 def t [ turn 90 ]<br> 252 def f [ forward 60 ]<br> 253 def h [ forward 30 ]<br> 254 def x [ under [ under t f ] h ]<br> 255 <br> 256 def u [ under t f h ]<br> 257 def q [ under [ repeat 2 u ] ]<br> 258 def w [ under [ repeat 1 u ] ]<br> 259 def e [ under [ repeat 0 u ] ]<br> 260 <br> 261 poly 4 [ q [<br> 262 poly 4 [ w [<br> 263 poly 4 [ e [ <br> 264 x inverse x<br> 265 ] ] ] ] ] ]<br> 266 </body> </html> 267
