"editor"
Bootstrap 3.0.0 Snippet by evarevirus

<link href="//netdna.bootstrapcdn.com/bootstrap/3.0.0/css/bootstrap.min.css" rel="stylesheet" id="bootstrap-css"> <script src="//netdna.bootstrapcdn.com/bootstrap/3.0.0/js/bootstrap.min.js"></script> <script src="//code.jquery.com/jquery-1.11.1.min.js"></script> <!------ Include the above in your HEAD tag ----------> <!DOCTYPE html><html lang='en' class=''> <head><script src='//production-assets.codepen.io/assets/editor/live/console_runner-079c09a0e3b9ff743e39ee2d5637b9216b3545af0de366d4b9aad9dc87e26bfd.js'></script><script src='//production-assets.codepen.io/assets/editor/live/events_runner-73716630c22bbc8cff4bd0f07b135f00a0bdc5d14629260c3ec49e5606f98fdd.js'></script><script src='//production-assets.codepen.io/assets/editor/live/css_live_reload_init-2c0dc5167d60a5af3ee189d570b1835129687ea2a61bee3513dee3a50c115a77.js'></script><meta charset='UTF-8'><meta name="robots" content="noindex"><link rel="shortcut icon" type="image/x-icon" href="//production-assets.codepen.io/assets/favicon/favicon-8ea04875e70c4b0bb41da869e81236e54394d63638a1ef12fa558a4a835f1164.ico" /><link rel="mask-icon" type="" href="//production-assets.codepen.io/assets/favicon/logo-pin-f2d2b6d2c61838f7e76325261b7195c27224080bc099486ddd6dccb469b8e8e6.svg" color="#111" /><link rel="canonical" href="https://codepen.io/dmitrynizh/pen/RrwYZX?limit=all&page=25&q=panel" /> <meta name="viewport" content="width=device-width, initial-scale=1"> <link rel='stylesheet prefetch' href='https://codemirror.net/addon/dialog/dialog.css'><link rel='stylesheet prefetch' href='https://codemirror.net/addon/search/matchesonscrollbar.css'><link rel='stylesheet prefetch' href='https://codemirror.net/lib/codemirror.css'><link rel='stylesheet prefetch' href='https://codemirror.net/addon/lint/lint.css'><link rel='stylesheet prefetch' href='https://codemirror.net/addon/hint/show-hint.css'> <style class="cp-pen-styles"> html, body { font-size: 14px; width: 100%; height: 100%; } .split { /*border: 1px solid red;*/ padding: 0px; margins: 0px; -webkit-box-sizing: border-box; -moz-box-sizing: border-box; box-sizing: border-box; overflow-y: hidden; overflow-x: hidden; } /* for extensions, examples of lisp code */ .code_source { display: none; } .content { border: 1px solid #C0C0C0; box-shadow: inset 0 1px 2px #e4e4e4; background-color: #fff; } .gutter { background-color: transparent; background-repeat: no-repeat; background-position: 50%; } .gutter.gutter-horizontal { cursor: col-resize; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAcAAAAnCAIAAABMnMWyAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAAFiUAABYlAUlSJPAAAAAzSURBVDhPY/z//z8DBmCC0qgAJMrIyIhG4laLCUZFIQC7KKlxgQlAoqMxRDtR4mOIgQEAiOAJVSqQeZQAAAAASUVORK5CYII='); } .gutter.gutter-vertical { cursor: row-resize; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAHCAIAAAD1ZpjZAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAAFiUAABYlAUlSJPAAAAAmSURBVDhPY/z//z/DQACoxYyMjBA+kQDuXPI1jvqYXmCk+ZiBAQCRTibzFTZDSgAAAABJRU5ErkJggg=='); } .split.split-horizontal, .gutter.gutter-horizontal { height: 100%; float: left; } .CodeMirror { /*border: 1px solid blue;*/ height: auto; } #code_editors {} .code_box { position: relative; float: left; padding: 6px; border: 1px solid black; border-radius: 6px; /*position: absolute;*/ resize: both; overflow: auto; /*left: 60px; top: 30px;*/ } .split a { cursor: pointer; margin-top: -8px; margin-left: -10px; position: absolute; padding: 1px; font-size: 12px; border: 1px solid black; color: black; height: 14px; background: lightblue; z-index: 10; } #nav_panel { margin-left: -45px; } .split a.a_next_ex { margin-left:12px; } .split a.a_prev_ex { /*margin-left:16px;*/ } #save_panel { right: 0px; top: 9px; } #tidy_js:hover, #Lisp_label:hover, #next_example:hover, #prev_example:hover, #save_result_html:hover, #save_result_gist:hover { color: red; background: orange; } .code_box a:hover, .code_box_last a:hover { color: blue; border: 2px solid blue; padding: 0 4px 0 4px; } .code_box textarea { } #output { height: 100%; border: 1px solid #444; } #output iframe { width: 100%; height: 100%; border: 0; } /* scrollbars: so far only webkit */ ::-webkit-scrollbar { width: 8px; height: 8px; } ::-webkit-scrollbar-button { } ::-webkit-scrollbar-track-piece { background: #f0f0f0 } ::-webkit-scrollbar-thumb { background: #e0e0e0 } ::-webkit-scrollbar-thumb:hover { background: lightblue } /* search box adjustments */ .CodeMirror-dialog { border: 1px solid blue; background: lightblue; white-space:nowrap; font-family: 'Arial Narrow', Arial, sans-serif; } .CodeMirror-dialog input { background: #fff; } .CodeMirror-search-match { background: transparent; border-top: 1px solid orange; border-bottom: 1px solid orange; -moz-box-sizing: border-box;; box-sizing: border-box; opacity: .5; } #lisp_history, #html_template { position: absolute; top: 100px; left: 100px; padding: 10px; width: 580px; border: 3px solid black; background: beige; display: none; resize: both; overflow: auto; } #gist_save_notification { position: absolute; top: 20px; right: 0px; padding: 6px; width: 350px; border: 3px solid black; background: beige; display: none; } #lisp_history_close, #html_template_cancel { position: absolute; top: 8px; right: 10px; } #html_template_ok { position: absolute; top: 8px; right: 70px; } </style><script>window.mylog = console.log;</script><style></style></head><body> <!-- These are now included in pen "settings"... <link rel="stylesheet" href="https://codemirror.net/lib/codemirror.css"> <link rel="stylesheet" href="https://codemirror.net/addon/lint/lint.css"> <link rel="stylesheet" href="https://codemirror.net/addon/hint/show-hint.css"> --> <div id="code_editors" class="split split-horizontal"> <div id="html" class="split content"> <a id="HTML_label">HTML</a> <textarea id="html_ed"></textarea> </div> <div id="css" class="split content"> <a>CSS</a> <textarea id="css_ed"></textarea> </div> <div id="lisp" class="split content"> <a id='Lisp_label'>  Lisp   </a> <textarea id="lisp_ed"></textarea> </div> <div id="js" class="split content"> <a id='tidy_js'>TidyUp JS</a> <textarea id="js_ed"></textarea> </div> </div> <div id="lisp_history"> <span>Lisp Editing history (last 5 edits remembered in Browser's LocalStorage)</span> <button id=lisp_history_close>close</button><br><br> <textarea id="hist0" rows=8 cols=80></textarea> <textarea id="hist1" rows=5 cols=80></textarea> <textarea id="hist2" cols=80></textarea> <textarea id="hist3" cols=80></textarea> <textarea id="hist4" cols=80></textarea> <textarea id="hist5" cols=80></textarea> </div> <div id="html_template"> <span>Template for Output HTML page</span> <button id=html_template_ok>OK</button> <button id=html_template_cancel>Cancel</button><br><br> <textarea id="html_template_ta" cols=80 rows=20></textarea> </div> <div id="gist_save_notification"> <span>Work was saved permanently in this location on the web:</span><br> <a target="_blank" id="gist_url"></a><br> <span>click above to view it</span><br> <a target="_blank" id="run_gist_from_blocks">Run it (opens in a separate tab)</a><br><br> <span id="gist_personal_at_note">Note: anonymous gist; to create/update your own gist,<br> provide gist_pat</span><br> <button id=gist_dialog_close>OK</button> </div> <div id="output" class="split split-horizontal"> <!--<div class='lisp_arrows' id="nav_panel">--> <a id='nav_panel'> <span id='prev_example'> <  </span> Examples <span id='next_example'>  >  </span> </a> <!--</div>--> <a id='save_panel'> <span id='save_result_html'>SaveFile</span>  <span id='save_result_gist'>SaveGist</span> </a> <iframe id="out_iframe"></iframe> </div> <div class="code_source"> <pre><!-- This provides variois macros that extend the basic set provided by Lisp. Currently, load time is completely negligible, under 10ms. In a very unlikely case extension lisp code grows so big it becomes a startup-time slowdown, then some of lisp code can be pre-ranslated. Thus, macros 'when' and 'unless' in JS are var defs = LispTrans.global_macros; // or use LispTrans.specials.defmacro; defs.when = function() { var forms = arr2list(arguments); return new Cons("if", new Cons(forms.car, new Cons(new Cons("progn", forms.cdr)))); }; defs.unless = function() { arguments[0] = list("not", arguments[0]); return LispTrans.global_macros["when"].apply(null, arguments); }; --> <textarea id='extensions'> (defmacro when (test &rest body) `(if ,test (progn ,@body))) (defmacro unless (test &rest body) `(if ,test nil ,@body)) (defmacro cond (&rest body) (let* ((build (lambda (code) (if code (let ((test (car (car code))) (yes (cdr (car code))) (no (build (cdr code)))) `(if ,test (progn ,@yes) ,no)))))) (build body))) ;; js-conscious variant of let*. Variable hoisting makes let body extended to the outer func. ;; Compare: (let ((x 0)(y i)) (foo x y)) and (var x 0 y 1)(foo x y) (defmacro var (&rest expr) `(let* ,(maplist (lambda x (let* ((pair (list (car x) (car (cdr x))))) (if x (set x.cdr (cdr x.cdr))) pair)) expr))) ;; Object/Array comprehension for applicative use. syntax: ;; (for name in expr ... ) or (for (name ..) in expr .... ) ;; examples: ;; (for k in myob count++) (for (k v) in {a b} (console.log k v)) (defmacro for (&rest body) (let* ((par (nth 0 body)) (kwd (nth 1 body)) (obj (nth 2 body))) (if (and par kwd obj (eq kwd "in")(or (symbolp par) (consp par))) (let* ((rest (or (cdr (cdr (cdr body))) (list nil) ))) (if (symbolp par) ;; no obj ref or var obj ref in fbody `(. (Object.keys ,obj) (map (lambda (,par) ,@rest))) (let* ((objv (if (symbolp obj) obj (gensym))) (fbody (cons `(set ,(nth 1 par) (aref ,objv ,(nth 0 par))) rest)) (iter `(. (Object.keys ,objv) (map (lambda ,par ,@fbody))))) (if (symbolp obj) iter ;; no need to close obj over `((lambda (,objv) ,iter ) ,obj))))) this.count-- ;; fall-though branch, tell the expander nothing was expanded. `(for ,@body)))) ;; Array comprehension for use inside [ and ]. Generic format: ;; [expr for param-or-list in expr opt-predicate opt-expr] ;; Examples: ;; [v+v for v in x] ;; [v+i for (v i) in [7 6 5 4] unless v==5] also if,unless etc ;; a predicate on value can be specifid without the expresion as in ;; [v+v for v in x Number.isInteger] ;; nesting can be as deep as desired: [v+v for v in [v+v for v in [v+v for v in [7 6 5 4]]]] (defmacro new$array (ve kw1 par kw2 obj pred test) (if (and ve kw1 par kw2 obj (eq kw1 "for") (eq kw2 "in") (or (symbolp par) (consp par))) (let* ((subj (if (and pred test) ;; pred must take 2nd arg 'true' for if,when,unless `(. ,obj (filter (lambda ,par (,pred ,test true)))) (if pred `(. ,obj (filter ,pred)) obj)))) `(. ,subj (map (lambda ,par ,ve)))) (set specials.new$array_ specials.new$array) `(new$array_ ,@(arr2list arguments)))) ;; Object comprehension for use inside { and }. Generic format: ;; [expr for param-or-list in expr opt-predicate opt-expr]. ;; Examples (predicates can be added as for the array variant, see above): ;; {k (foo k) for k in x} ;; {k k+v for (k v) in {a 'x b 'yy c 'zz}} (defmacro new$object (ke ve kw1 par kw2 obj pred test) (if (and ke ve kw1 par kw2 obj (eq kw1 "for") (eq kw2 "in") (or (symbolp par) (consp par))) (let* ((r (gensym "__g_new")) (setter `(set (aref ,r ,ke) ,ve))) (if pred (set setter (if test `(,pred ,test ,setter) `(if ,pred ,setter)))) (if (symbolp par) ;; no obj ref or var obj ref in fbody `(. (Object.keys ,obj) (reduce (lambda (,r ,par) ,setter ,r) {})) (let* ((objv (if (symbolp obj) obj (gensym "__g_old"))) (ctor `(. (Object.keys ,objv) (reduce (lambda (,r ,@par) (set ,(nth 1 par) (aref ,objv ,(nth 0 par))) ,setter ,r) {})))) (if (symbolp obj) ctor `((lambda (,objv) ,ctor) ,obj))))) this.count-- ;; fall-though branch, tell the expander nothing was expanded. `(new$object ,@(arr2list arguments)))) </textarea> <!-- footnotes 1. the old, more ;classical version of comprehension macros uses &rest body" (defmacro new$array (&rest body) (let* ((ve (nth 0 body)) (kw1 (nth 1 body)) (par (nth 2 body)) (kw2 (nth 3 body)) (obj (nth 4 body))) (if (and ve kw1 par kw2 obj (eq kw1 "for") (eq kw2 "in") (or (symbolp par) (consp par))) ... `(new$array ,@body)))) but it is much mored convenient and efficient to use explicit parameters and JS arguments array instead as in the textarea above. --> </pre></div> <!-- end of lisp extensions section --> <div class="code_source" id='examples'> <pre><textarea id=example1__html><p id=p1>Hello World</p> <script src=https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.5/d3.min.js></script></textarea> <textarea id='example1__css'> body { color: blue; } </textarea> <textarea id=example1>;;;; Generate new random color and use it (defun new_color() (+ "#" (. (Math.random) (toString 16) (slice -6)))) ;;runner (defun tick() (set ;; examples of styles of DOM access (. document (getElementById 'p1) style color) (new_color) (. p1 (aref style 'background-color)) (new_color) document.body.style.background (new_color) p1.innerHTML (new Date)) (setTimeout tick 1000)) (tick) ;; The code in each pane is editatble. CodeMirror is the underlying engine. ;; JS code is checked with Jhint. TidyUp formats it with Beautifier. ;; The Lisp pane provides lisp-oriented formatting and editing commands. ;; In addition to standard Ctrl-a Ctrl-x Ctrl-v, useful Keyboard commands are: ;; Ctrl-f Search ;; Ctrl-F Ctrl-R Replace ;; Ctrl-Space Hints/completion list ;; Shift-Tab Indent lien or selected region ;; RECOVER: "Lisp" button displays history of last 5 edits ;; saved in Local Storage </textarea> </pre> <pre><textarea id=example2>;;;; Fun with pure lambdas part 1 (lambda (x) x) ;; i'm lambda \x.x ;; i'm the same lambda λx.x ;; i'm the same lambda too λxy.y λxyz.x+y+z λx.λy.x+y ;; these lambdas translate to JS functiosn and are much more compact! ;; Aside from that, long chanined expressions with some anonumous functions and some data literals as args, which is typical, ;; are often more compact in lisp ;; d3 selections (white space deliberately ommited, where possible, in each case) ;; js: ... data([{n:"foo",v:1},{n:"bar",v:2}],function(d){return d.v;}).enter() ... ;; lisp: ... (data[{n'foo v 1}{n'bar v 2}]\d.d.v)(enter) ... ;; another comarison from real example ;; body.append('div').attr('id','gui_dock').append(function(){return gui.domElement;}); ;; (.(body.append'div)(attr'id'gui_dock)(append \.gui.domElement)) ;; arithmetic on the first glance is not but lots of expressions can be ;; coded as shorthand terms: ;; js: while (i-->0) { d+=x/2+y/3; x.y.z=p/q-r; } ;; lisp (while i-->0 d+=x/2+y/3 x.y.z=p/q-r) ;; the above works because i-->0 d+=x/2+y/3 x.y.z=p/q-r are terms. </textarea> </pre> <pre><textarea id=example3>;;;; Fun with d3 and lambdas part 1 (. d3 (select 'body) (selectAll "div") (data [{text "aaaa"} {text "bbbb"} ]) (enter) (append 'div) (text \d.d.text) (attr 'id \di.i) (style {'background-color #ffffff 'color #000000 }) (transition) (duration 10000) (style {'background-color #000000 'color #ffffff})) </textarea> </pre> <pre><textarea id='example4__css'> .axis path { fill: none; stroke: #777; shape-rendering: crispEdges; } .axis text { font-family: Lato; font-size: 13px; } </textarea> <textarea id=example4>;;;; D3 chart with Lisp (var price \.(+ 150 (* 60 (Math.random))) years (d3.range 2000 2012) data [{sale (price) year y} for y in years] data2 [{sale (price) year y} for y in years] WIDTH 1000 HEIGHT 500 MARGINS {top 20 right 20 bottom 20 left 50 } vis (.(d3.select'body)(append'svg)(attr {width WIDTH height HEIGHT})) xScale (. d3.scale (linear)(range [ MARGINS.left WIDTH-MARGINS.right])(domain [ 2000 2010])) yScale (. d3.scale (linear)(range [ HEIGHT-MARGINS.top MARGINS.bottom])(domain [ 134 215])) xAxis (. d3.svg (axis)(scale xScale)) yAxis (. d3.svg (axis )(scale yScale)(orient "left")) mkLine (. d3.svg (line) (x \d.(xScale d.year)) (y \d.(yScale d.sale)) (interpolate "basis"))) (. vis (append "svg:g") (attr "class" "x axis") ;;(attr "transform" (+ "translate(0," (String HEIGHT-MARGINS.bottom) ")")) ;; use String() (attr "transform" "translate(0,{(HEIGHT-MARGINS.bottom)})") ;; use interpolation. (call xAxis)) (. vis (append "svg:g") (attr "class" "y axis") (attr "transform" "translate({MARGINS.left},0)") ;; use interpolation. (call yAxis)) (. vis (append "svg:path") (attr 'd (mkLine data)) (attr 'stroke 'green) (attr 'stroke-width 2) (attr 'fill 'none)) (. vis (append "svg:path") (attr 'd (mkLine data2)) (attr 'stroke 'blue) (attr 'stroke-width 2) (attr 'fill 'none)))</textarea> </pre> <pre><textarea id=example5>;;;; Sortable html table generation with d3 & lisp (var ;; the table rows typically loaded from data file using d3.csv movies [{ title "The Godfather" year 1972 length 175 budget 6000000 rating 9.1 } { title "The Shawshank Redemption" year 1994 length 142 budget 25000000 rating 9.1 } { title "The Godfather Part II" year 1974 length 200 budget 13000000 rating 8.9 } { title "Buono il brutto il cattivo Il" year 1966 length 180 budget 1200000 rating 8.8 } { title "Casablanca" year 1942 length 102 budget 950000 rating 8.8 } { title "Pulp Fiction" year 1994 length 168 budget 8000000 rating 8.8 } ] columns [{ head 'Movie d 1 f \d.d.title html \d.d.title} { head 'Year' d 1 f \d.d.year html \d.d.year} { head 'Length d 1 f \d.d.length html \d.(let* ((p (new Date))) (p.setMinutes d.length)((d3.time.format "%_Hh %Mm") p)) } { head 'Budget d 1 f \d.d.budget html \d.((d3.format "$,") d.budget) } { head 'Rating d 1 f \d.d.rating html \d.((d3.format ".1f") d.rating) } ] table (. d3 (select 'body) (append 'table) (style 'border "1px solid black")) rdata \r.(columns.map \c.(c.html r)) ;; helper function mk_table_body \.(. table (select 'tbody) (selectAll 'tr) (data movies)(enter) (append 'tr) (style { 'background-color \di.(if i&1 "#e0e0e0" "#f0f0f0")}) (selectAll 'td) (data rdata) (enter ) (append 'td) (html \d.d))) (. table (append 'tbody)) (. table (append 'thead) (append 'tr) (selectAll 'th)(data columns)(enter) (append 'th) (style {color 'yellow 'background-color 'black}) (text \d.d.head) (on 'click \d.(progn (movies.sort \ab.(if (> (d.f a) (d.f b)) d.d (if (< (d.f a) (d.f b)) -d.d 0))) (set d.d -d.d) (. d3 (select 'tbody) (selectAll 'tr)(remove)) (mk_table_body)))) (mk_table_body) (set p1.innerHTML "Hello Movies")</textarea> </pre> <pre><textarea id=example5aa>;;;; Fun with lisp: how to define a macro that includes lisp code from anothere example: (defmacro load_1 (id) (let* ((elt (document.getElementById id.text)) (text (if elt elt.value "(alert '(bad lib id))"))) (macroexpand (read text)))) ;; load_1 reads the content of html element with given id, and adds it ;; to the code stream being translated. Because load_1 uses the ;; built-in function 'read', only the 1st top lecvel s-expr from id's text is included: (load_1 "example3") ;; this translates & runs the previous example here in place ;; Macro 'load_all' that loops over all forms with (read_from_string text curr) ;; can be trivially defined in similat way but for convenicence ;; this playground provides even more flexible built-in macro 'include' that ;; takes any number of CSS selectors so that multiple groups of mutiple nodes containing ;; lisp code can be included with just one line of code. ;; Example of use: (include #example1 #example4 #example5) </textarea> </pre> <pre><textarea id=example6>;;;; Fun with lambdas part2: Y Combinator (define Y (lambda (f) ((lambda (x) (x x)) (lambda (x) (f (lambda (y) ((x x) y))))))) (define almost_factorial (lambda (f) (lambda (n) (if (= n 0) 1 (* n (f (- n 1))))))) (define factorial (Y almost_factorial)) (define result (factorial 5)) (set p1.innerHTML result) </textarea> </pre> <pre><textarea id=example7>;;;; Fun with lambdas part3: shorthand Y (define Y \f.(\x.(x x) \x.(f \y.((x x) y)))) (define almost_factorial \f.\n.(if (= n 0) 1 (* n (f (- n 1))))) (define factorial (Y almost_factorial)) (define result (factorial 5)) (set p1.innerHTML result) </textarea> </pre> <pre><textarea id=example8>;;;; Fun with lambdas part4 : λ looks good (define almost_factorial λf.λn.(if (= n 0) 1 (* n (f (- n 1))))) ;; one version of Y Combinator (define Y1 λf.(λx.(x x) λx.(f λy.((x x) y)))) ;; a bit longer version of Y (define Y2 λf.(λx.(f λy.((x x) y)) λx.(f λy.((x x) y)))) ;; the famous version of Y (define Y λf.(λx.(f (x x)) λx.(f (x x)))) (+= p1.innerHTML ((Y1 almost_factorial) 5)) (+= p1.innerHTML ((Y2 almost_factorial) 5)) (+= p1.innerHTML ((Y almost_factorial) 5)) ;; explain the result (yes, the last Y is not for strict languages) </textarea> </pre> <pre><textarea id=example9>;;;; Simple Web Playground created with D3 and Lisp (define panels [{lang "HTML" tag 'html text "<h1 id=e>Hello</h1>" } {lang "CSS" tag 'style text "body { color: blue;}"} {lang "JS" tag 'script text "function f(){e.innerHTML+='!';setTimeout(f,3000)}f()"}]) (set p1.innerHTML "Little Web Playground Powered by Lisp") (. d3 (select 'body) (selectAll 'panels) (data panels)(enter)(append 'textarea) (text \d.d.text) (on 'keyup \d.(let* ((doc output.contentDocument)(v "")) (. d3 (selectAll 'textarea) (each \d.(+= v (+ "<"d.tag">"this.value"</"d.tag">")))) (doc.open)(doc.write v)(doc.close)))) (. d3 (select 'body)(append 'iframe) (attr {id 'output width '100% height 400})) ((. d3 (select 'textarea) (on 'keyup))) </textarea> </pre> <pre><textarea id=example10>;;;; Same Web Playground, with its html window showing the ;; famous code for minimalist web playground (~ 120 bytes of html+js code or so) (define panels [{lang "HTML" tag 'html text (. "{body oninput='e.firstChild.srcdoc=t2[v=\"value\"]+\"{script }\"+t0[v]+\"{/script }{style }\"+t1[v]'onload='for(i=3;i--;)e.innerHTML+=\"{textarea id=t\"+i+\" rows=9}\"'id=e}{iframe }" (replace (RegExp "{" 'g ) "<") (replace (RegExp "}" 'g ) ">")) } {lang "CSS" tag 'style text ""} {lang "JS" tag 'script text ""}]) (set p1.innerHTML "Little Web Playground Powered by Lisp, Running MiniCodeEditor") (. d3 (select 'body)(append 'iframe) (attr {id 'output width '100% height 400})) (. d3 (select 'body) (selectAll 'panels) (data panels)(enter)(append 'textarea) (text \d.d.text) (on 'keyup \d.(let* ((doc output.contentDocument)(v "")) (. d3 (selectAll 'textarea) (each \d.(+= v (+ '< d.tag '> this.value '< '/ d.tag '>)))) (doc.open)(doc.write v)(doc.close)))) ((. d3 (select 'textarea) (on 'keyup)))</textarea> </pre> <pre><textarea id=example11>;;;; Same, with v1.2 and using atob btoa to avoid encoding hassles (define panels [{lang "HTML" tag 'html text (atob "PGJvZHkgb25pbnB1dD0nZi5zcmNkb2M9dDBbdj0idmFsdWUiXSsiPHNjcmlwdD4iK3QyW3ZdKyI8L3NjcmlwdD48c3R5bGU+Iit0MVt2XSdvbmxvYWQ9J2ZvcihpPTM7aS0tOylmLm91dGVySFRNTCs9Ijx0ZXh0YXJlYSBpZD10IitpKyIgcm93cz05PiInPjxpZnJhbWUgaWQ9Zj4=") } {lang "CSS" tag 'style text ""} {lang "JS" tag 'script text ""}]) (set p1.innerHTML "Little Web Playground Powered by Lisp, Running MiniCodeEditor v.1.2") (. d3 (select 'body)(append 'iframe) (attr {id 'output width '100% height 400})) (. d3 (select 'body) (selectAll 'panels) (data panels)(enter)(append 'textarea) (text \d.d.text) (on 'keyup \d.(let* ((doc output.contentDocument)(v "")) (. d3 (selectAll 'textarea) (each \d.(+= v (+ '< d.tag '> this.value '< '/ d.tag '>)))) (doc.open)(doc.write v)(doc.close)))) (. d3 (select 'textarea) (on 'keyup)) </textarea> </pre> <pre><textarea id=example12>;;;; Wall Clock in D3 and Lisp. Derived from https://bl.ocks.org/tomgp/6475678 (let* ((radius 180) (radians 2*Math.PI/360) (defScale \n.(. d3.scale (linear) (range [ 0 360-360/n]) (domain [ 0 n-1 ]))) (hours ( defScale 12)) (minutes (defScale 60)) (seconds (defScale 60)) (decimal_hour \d.(let* ((h (d.getHours))(m (d.getMinutes))) h%12+m/70)) ;; 70 so that at 12 we see some animation (hands [{scale hours len {a 1/1.5 b 0} width 1/16 f decimal_hour } {scale minutes len {a 1 b 0.1} width 1/25 f \d(d.getMinutes) } {scale seconds len {a 0.94 b 0.15} width 1/60 f \d(d.getSeconds) }]) (moveHands \.(. (d3.select #clock-hands)(selectAll 'line)(data hands) (transition) (attr 'transform \d.(+ "rotate(" (d.scale d.val) ")")))) (updateData \.(let ((time (new Date))) (. hands (forEach \d.(set d.val (d.f time)))))) ;; next, create all the clock elements (init_them (updateData)) ;; draw them in the correct starting position (svg (. (d3.select "body")(append "svg") (style {'stroke #000}) (attr {width radius*2.8 height radius*2.6}))) (face (. svg (append 'g) (attr {id 'clock-face transform (. "translate(_,_)" (replace /_/g radius*1.3 ))}))) ;; generator of tick marks and label (gen_ticks (lambda (v) (. (face.selectAll '.ticks) (data (d3.range v.from v.to))(enter) (append 'line) (style {'stroke #225 'stroke-width (/ radius v.kstr)}) (attr {x1 0 x2 0 y1 radius y2 radius*v.ky2 transform \d.(+ "rotate(" (v.scale d) ")")})) (. (face.selectAll '.labels) (data (d3.range v.step (+ v.to 1) v.step))(enter) (append 'text) (style {'stroke #225 'font-size (+ radius/v.kfont 'px)}) (attr {'text-anchor 'middle x \d.(radius*v.kt*Math.sin (* radians (v.scale d))) y \d.(- radius/v.kfont/3 (radius*v.kt*Math.cos (* radians (v.scale d))))}) (text \d.d))))) ;; start painting by adding the face plate (. (face.append 'g)(append 'circle)(style {'stroke-width radius/30 fill #e0e0c0}) (attr {x 0 y 0 r 1.18*radius})) ;; next, add the ticks (gen_ticks {scale hours from 0 to 12 step 3 kstr 25 kfont 4 ky2 0.92 kt 0.8}) (if (> radius 80) (gen_ticks {scale seconds from 0 to 60 step 5 kstr 60 kfont 10 ky2 0.96 kt 1.09})) ;; minutes/seconds ;; add hands (. (face.append 'g) (attr 'id 'clock-hands) (selectAll 'line) (data hands) (enter) (append 'line) (style {'stroke-width \d.d.width*radius 'stroke-linecap 'round 'stroke #000}) (attr { x1 0 y1 \d.d.len.b*radius x2 0 y2 \d.d.len.a*-radius}) (attr 'transform \d.(+ "rotate(" (d.scale d.val) ")"))) ;; add cosmetic center cap (. (face.append 'g)(append 'circle)(style {'stroke-width radius/50 fill #ddb}) (attr {x 0 y 0 r radius/20})) (setInterval \.(moveHands (updateData)) 1000) (. (d3.select 'body)(style 'background "url(http://deskpicture.com/DPs/Miscellaneous/StoneWall.jpg)"))) </textarea> </pre> <pre><textarea id=example13>;;;; working with aliases, playing some tricks with .chains when possible ;; showing useful prefix wrappers to avoiding dot expressions ;; note: more efficient wrapping can be done with macros, if there is a need (let* ((random0to10 (d3.random.normal 5 5)) ;; dotted chains can be used applicatively! (df4p2 (d3.format "4.2f")) ;; which is a bit shorter than (. ....) equivalent (cos Math.cos) ;; local aliases such as math functions can speed code up; (s String.prototype) ;; prototype aliases allow to use methods in prefic position (op Object.prototype) ;; which oten requires .call notation as whown below (charAt s.charAt) (ch λsi.(s.charAt.call s i)) ;; .call can be wrapped if performance permits (has_prop λop.(op.hasOwnProperty.call o p)) (concat λ.(s.concat.apply "" arguments)) ;; for multiargs, .apply is better (examples [(s.charAt.call 'qwerty 1) ;; invoke charAt on "qwerty" (charAt.call 'qwerty 1) ;; same but shorter (ch 'qwerty 1) ;; even shorter (s.concat.call 'abcd 'ef 'gh) (concat 'abcd 'ef 'gh) (op.hasOwnProperty.call {} 'toString) (has_prop {} 'toString) ;; same as above but faster (cos 1) (+ "aaa" (+ 1 2) "bbb" 1+2/4 "ccc") ;; + used for string concatenation affects numeric + (+ "aaa" (String 1+2) "bbb") ;; to cope, use suitable number->string wrapper, among which (+ "translate(" (df4p2 3+5/9) "," (df4p2 2+3) ")") ;; d3 formatters are ideal! ])) ;;(console.log examples) (.(d3.select 'body)(selectAll 'sel)(data examples)(enter) (append 'p) (text String))) </textarea> </pre> <pre><textarea id=example13a>;; Fun with macros part 3. Classic Eliza profgram is simple when translated to JS. Macros help. ;; Explanation: the basic code logic and the rules are taken from the PAIP book; ;; macros helped to convert all formats best fitting JS. ;; The original code is very cons-heavy at runtime; here, with the help from a couple of macros, ;; the original cons-based representation is converted to JS arrays and objects. No cons processing happens at runtime and the output JS code has no dependency on Lisp functionality. (set p1.innerHTML "ELIZA, AI DOCTOR") (defun eliza () (let* ((input (read_line_no_punct)) (response (flatten (use_eliza_rules input)))) (print_with_spaces response))) (defun read_line_no_punct () (str_subst [[" i " " I "]["'" "_"]["\\W+" " "]] " {input.value} ")) (defun flatten (x) (aref (aref x 0) 0)) (defun use_eliza_rules (input) (. eliza_rules (map \(rule) (let ((result (input.match rule.pattern))) (if result (substitute {re "var1|var2|var3" vals {"var{(+i+1)}" v for (i v) in (switch_viewpoint result)}} (random_elt rule.responses))))) (filter \v.v))) (defun print_with_spaces (text) (set output.value (+ output.value "\n> " (pprint_input) "\n" (pprint_output text)) output.scrollTop output.scrollHeight input.value "> ")) (defun random_elt (arr) (var range arr.length-1 random_idx (Math.round (* range (Math.random)))) [(aref arr random_idx)]) ;; this helps to convert 'switch_viewpoint' (defmacro sublis (spec arg) (defun mkkey(n v)(string (+ " "(nth n v) " "))) (if (and (consp spec) (eq "quote" spec.car)) (let* ((re)(rep)) (mapcar \v(set re (cons (nth 0 v) re) rep (cons (mkkey 2 v) (cons (mkkey 0 v) rep))) (nth 1 spec) ) (set re (string (+ " " (.(list2arr (reverse re)) (join " | ")) " " ) ) spec (list "new$object" "re" re "vals" (cons "new$object" (reverse rep)))))) `(substitute ,spec ,arg )) (defun switch_viewpoint (words) (words.shift 1) ;; array of matches (sublis '((I . you) (you . I) (me . you) (am . are) (my . your)(mine . your)(I_ve . you_ve)(you_ve . I_ve)(I_m . you_re)) ;; I added few more here (words.map \s(+ " " s " ")))) ;; What remains is to define rules. Take the original pattern-action list, ;; Usa a macro to convert that to array of convenient JS objects, see 'var eliza_rules = ' in the JS panel. (defmacro defparam) ;; clear it first just in case (defmacro defparam (nm val) (if (and (consp val) (eq "quote" val.car)) (let* ((vars {"?x" "var1" "?y" "var2" "?z" "var3"}) (f \r(list "new$object" "pattern" (list "RegExp" (string (.(list2arr (mapcar \p(if (consp p) "(.*)" p) r.car))(join " ") ))) "responses" (cons "new$array" (mapcar \r(string (. (list2arr (mapcar \w(or (aref vars w) w) r) ) (join " "))) r.cdr)) )) (rule_arr (cons "new$array" (mapcar f val.cdr.car)) )) (set val rule_arr))) `(defvar ,nm ,val)) (defparam eliza_rules '((((?* ?x) hello (?* ?y)) (How do you do. Please state your problem.)) (((?* ?x) computer (?* ?y)) (Do computers worry you?) (What do you think about machines?) (Why do you mention computers?) (What do you think machines have to do with your problem?)) (((?* ?x) name (?* ?y)) (I am not interested in names)) (((?* ?x) sorry (?* ?y)) (Please do not apologize) (Apologies are not necessary) (What feelings do you have when you apologize)) (((?* ?x) I remember (?* ?y)) (Do you often think of ?y) (Does thinking of ?y bring anything else to mind?) (What else do you remember) (Why do you recall ?y right now?) (What in the present situation reminds you of ?y) (What is the connection between me and ?y)) (((?* ?x) do you remember (?* ?y)) (Did you think I would forget ?y ?) (Why do you think I should recall ?y now) (What about ?y) (You mentioned ?y)) (((?* ?x) if (?* ?y)) (Do you really think its likely that ?y) (Do you wish that ?y) (What do you think about ?y) (Really-- if ?y)) (((?* ?x) I dreamt (?* ?y)) (Really-- ?y) (Have you ever fantasized ?y while you were awake?) (Have you dreamt ?y before?)) (((?* ?x) dream about (?* ?y)) (How do you feel about ?y in reality?)) (((?* ?x) dream (?* ?y)) (What does this dream suggest to you?) (Do you dream often?) (What persons appear in your dreams?) (Do not you believe that dream has to do with your problem?)) (((?* ?x) my mother (?* ?y)) (Who else in your family ?y) (Tell me more about your family)) (((?* ?x) my father (?* ?y)) (Your father) (Does he influence you strongly?) (What else comes to mind when you think of your father?)) (((?* ?x) I want (?* ?y)) (What would it mean if you got ?y) (Why do you want ?y) (Suppose you got ?y soon)) (((?* ?x) I am glad (?* ?y)) (How have I helped you to be ?y) (What makes you happy just now) (Can you explain why you are suddenly ?y)) (((?* ?x) I am sad (?* ?y)) (I am sorry to hear you are depressed) (I am sure its not pleasant to be sad)) (((?* ?x) are like (?* ?y)) (What resemblance do you see between ?x and ?y)) (((?* ?x) is like (?* ?y)) (In what way is it that ?x is like ?y) (What resemblance do you see?) (Could there really be some connection?) (How?)) (((?* ?x) alike (?* ?y)) (In what way?) (What similarities are there?)) (((?* ?x) same (?* ?y)) (What other connections do you see?)) (((?* ?x) I was (?* ?y)) (Were you really?) (Perhaps I already knew you were ?y) (Why do you tell me you were ?y now?)) (((?* ?x) was I (?* ?y)) (What if you were ?y ?) (Do you thin you were ?y) (What would it mean if you were ?y)) (((?* ?x) I am (?* ?y)) (In what way are you ?y) (Do you want to be ?y ?)) (((?* ?x) am I (?* ?y)) (Do you believe you are ?y) (Would you want to be ?y) (You wish I would tell you you are ?y) (What would it mean if you were ?y)) (((?* ?x) am (?* ?y)) (Why do you say *AM?*) (I do not understand that)) (((?* ?x) are you (?* ?y)) (Why are you interested in whether I am ?y or not?) (Would you prefer if I were not ?y) (Perhaps I am ?y in your fantasies)) (((?* ?x) you are (?* ?y)) (What makes you think I am ?y ?)) (((?* ?x) because (?* ?y)) (Is that the real reason?) (What other reasons might there be?) (Does that reason seem to explain anything else?)) (((?* ?x) were you (?* ?y)) (Perhaps I was ?y) (What do you think?) (What if I had been ?y)) (((?* ?x) I ca not (?* ?y)) (Maybe you could ?y now) (What if you could ?y ?)) (((?* ?x) I feel (?* ?y)) (Do you often feel ?y ?)) (((?* ?x) I felt (?* ?y)) (What other feelings do you have?)) (((?* ?x) I (?* ?y) you (?* ?z)) (Perhaps in your fantasy we ?y each other)) (((?* ?x) why do not you (?* ?y)) (Should you ?y yourself?) (Do you believe I do not ?y) (Perhaps I will ?y in good time)) (((?* ?x) yes (?* ?y)) (You seem quite positive) (You are sure) (I understand)) (((?* ?x) no (?* ?y)) (Why not?) (You are being a bit negative) (Are you saying *NO* just to be negative?)) (((?* ?x) someone (?* ?y)) (Can you be more specific?)) (((?* ?x) everyone (?* ?y)) (surely not everyone) (Can you think of anyone in particular?) (Who for example?) (You are thinking of a special person)) (((?* ?x) always (?* ?y)) (Can you think of a specific example) (When?) (What incident are you thinking of?) (Really-- always)) (((?* ?x) what (?* ?y)) (Why do you ask?) (Does that question interest you?) (What is it you really want to know?) (What do you think?) (What comes to your mind when you ask that?)) (((?* ?x) perhaps (?* ?y)) (You do not seem quite certain)) (((?* ?x) are (?* ?y)) (Did you think they might not be ?y) (Possibly they are ?y)) (((?* ?x)) (Very interesting) (I am not sure I understand you fully) (What does that suggest to you?) (Please continue) (Go on) (Do you feel strongly about discussing such things?)))) ;; a few aux functions go here (defun substitute (spec args) ;; arr->arr (args.map \a(a.replace (RegExp spec.re "g") \m(aref spec.vals m)) args)) (defun str_subst (spec str) ;; str->str (spec.forEach \v(set str (str.replace (RegExp (aref v 0) "g") (aref v 1)))) str) (defun pprint_input () (str_subst [["^\\s*>\\s*" ""]["\\s+" " "]] input.value )) (defun pprint_output (text) (str_subst [["\\s+" " "]["_" "'"]] text)) ;; finally, setup a primitvie dialog env: two text areas. (var sel_elt \(sel) (. (aref (aref sel 0) 0)) div (.(d3.select document.body)(append 'div)) output (sel_elt (. div (append 'textarea) (attr {id 'output_a cols 54 rows 23 readonly 1})) ) input (sel_elt (. div (append 'textarea) (attr {id 'input_a cols 54 rows 2 ;; autofocus -1 }) (text "> I want an apple") (on "keyup" \d(if d3.event.keyCode==13 (eliza) ) )))) ;; ...and a bit of silly animation (. div (transition) (duration 3000) (style 'background "radial-gradient(#662850, #202444, #162030)")) (. (d3.select p1) (transition) (duration 3000)(ease 'bounce) (style {color "red" "letter-spacing" "20px" } )</textarea> <textarea id=example13a__css>body{ background:#33a; } #p1 { border: 4px dashed #7cb4fc; border-radius:10px; position:absolute; top: -20px; left: 20px; width: 800px; height: 46px; color:#7cb4fc; text-align:center; font:900 2em Arial, Helvetica, Geneva, sans-serif; text-transform:uppercase; letter-spacing: 1px; -webkit-filter:blur(2px); filter:blur(2px); text-shadow:#99d3fd 0 4px 5px; } div::before { content: " "; display: block; position: absolute; top: 0; left: 0; bottom: 0; right: 0; background: linear-gradient(rgba(18, 16, 16, 0) 50%, rgba(0, 0, 0, 0.25) 50%), linear-gradient(90deg, rgba(255, 0, 0, 0.06), rgba(0, 255, 0, 0.02), rgba(0, 0, 255, 0.06)); z-index: 2; background-size: 100% 4px, 1px 100%; pointer-events: none; } div { -webkit-border-radius:50px; -moz-border-radius:50px; border-radius:50px; border-left:20px solid #1b1b1b; border-right:20px solid #1c1c1c; border-top:25px solid #0f1310; border-bottom:25px solid #0f1310; position:absolute; overflow:hidden; top: 100px; left: 20px; width: 700px; height: 500px; padding:1.5em 1.5em 1.5em 2em; -webkit-box-shadow:inset 0 0 0 9px rgba(21,21,20,0.6), 0 1px 60px 10px rgba(40,46,40,9), inset 0 1px 80px 5px rgba(0,0,0,0.97); -moz-box-shadow:inset 0 0 0 9px rgba(21,21,20,0.6), 0 1px 60px 10px rgba(40,46,40,9), inset 0 1px 80px 5px rgba(0,0,0,0.97); box-shadow:inset 0 0 0 9px rgba(21,21,20,0.6), 0 1px 60px 10px rgba(40,46,40,9), inset 0 1px 80px 5px rgba(0,0,0,0.97); background-color: #202444; /*#162030;*/ background: radial-gradient(#262860, #202444, #162030); } textarea { background: transparent; border: none; outline: none; font:600 24px VT323, monospace; line-height: 20px; color:#7cb4fc; letter-spacing:-1px; -webkit-filter:blur(0.8px); filter:blur(0.8px); text-shadow:#363860 5px -4px 40px; white-space: pre-wrap; /* css-3 */ white-space: -moz-pre-wrap; /* Mozilla, since 1999 */ white-space: -pre-wrap; /* Opera 4-6 */ white-space: -o-pre-wrap; /* Opera 7 */ word-wrap: break-word; overflow:hidden; resize:none; }</textarea> </pre> <pre><textarea id=example14>;;;; Lisp's prefix notation and underscore.js are a great synergy ;; TBD... ;; Lisp's Array and Object comprehensions & use of _.range: (var x (_.range 7 3 -1) br "<br>" out \s.p1.innerHTML+=br+s) (set p1.innerHTML "") (. [[v+v for v in x] [v+v for v in (_.range 7 3 -1)] [v+v for v in [7 6 5 4]] [v+i for (v i) in x] [v+v for v in x when v%2]] (map out)) (set x { a 'x b 'y c 'z} out \o.p1.innerHTML+=br+o.a+o.b+o.c) (. [{k k+k for k in x} {k k+v for (k v) in x} {k k+k for k in { a 'x b 'y c 'z}} {k k+v for (k v) in { a 'x b 'y c 'z}}] (map out)) </textarea> <textarea id=example14__html><p id=p1>Hello World</p> <script src='https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.2/underscore-min.js'></script> <script src='https://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.min.js'></script> </textarea> </pre> <pre><textarea id=example14a>;;;; Haskel style array ranges with translation time expansion ;; when the parameters are constants. ;; the idea here is to further elaborate the new$array macro (see the 'extensions' code section) ;; with several cases recognizing the following constructs: ;; [1..10] [1 3..22] [x..y] [x y..z] and the same with spaces around .. ;; and expand either as arrays or array generation calls. (defmacro new$array_(v1 v2 v3 v4) (set specials.new$array__ specials.new$array) (defun nddn(v) (and (eq (typeof v) "string")(v.match (RegExp "^([+-]?\\d+[.]?\\d*)\\.\\.([+-]?\\d+[.]?\\d*)$")))) (defun edde(v) (and (eq (typeof v) "string")(v.match (RegExp "(.*(?=\\.\\.))\\.\\.(.*)")))) (defun arr(v1 v2 v3) (cons "new$array__" (arr2list (range v1 v2 v3 1)))) (var alen arguments.length frange "(function(f,s,n){f=+f||0,n=+n||1,null===s&&(s=f,f=0);for(var _=-1,o=Math.max(0,Math.ceil((1+s-f)/(n||1))),a=Array(o);o>++_;)a[_]=f,f+=n;return a;})") ;; case1: (number..number) (if (and alen==1 (set v4 (nddn v1))) (arr (aref v4 1)(aref v4 2) 1) ;; case2: (number number..number) (if (and alen==2 (!isNaN v1) (set v4 (nddn v2))) (arr +v1 (aref v4 2) (- (aref v4 1) v1)) ;; case3 (number .. number) (if (and alen==3 (!isNaN v1) (eq v2 "..") (!isNaN v3)) (arr +v1 +v3 1) ;; case4 (number number .. number) (if (and alen==4 (!isNaN v1) (!isNaN v2) (eq v3 "..") (!isNaN v4)) (arr +v1 +v4 v2-v1) ;; case5: (expr..expr) (if (and alen==1 (set v4 (edde v1))) `(,frange ,(aref v4 1) ,(aref v4 2)) ;; case6: (expr expr..expr) (if (and alen==2 v1 (set v4 (edde v2))) `(,frange ,v1 ,(aref v4 2) (- ,(aref v4 1) ,v1)) ;; case7 (expr .. expr) (if (and alen==3 v1 (eq v2 "..") v3) `(,frange ,v1 ,v3 1) ;; case8 (expr expr .. expr) (if (and alen==4 v1 v2 (eq v3 "..") v4) `(,frange ,v1 ,v4 (- ,v2 ,v1)) `(new$array__ ,@(arr2list arguments))))))))))) ;; now, tests: (when test.innerHTML (var br "<br>" out \s.test.innerHTML+=br+s x 1 y 3) (out [1..10]) (out [1 3..10]) (out [x*1..10+3]) (out [x*1 y+5..10+y]) (out [1 .. 10]) (out [1 3 .. 10]) (out [x*1 .. 10+y]) (out [x*1 y+5 .. 10+y]) ) ;; in a similar fashion, macro range can be defined and be occasionally useful, although the above comprehension does make it redunand. ;; it is mainly here to show case that the generated handler function (named frange above) can be created as a lisp s-expr and ;; inserted in the return s-form with ,f. In such case the pair of parentheses is generated by the translator. The macro range also ;; uses traditional lisp &rest instead of using explicitly named parameters and JS arguments array. (defmacro range) ;; clear it just in case (defmacro range (&rest e) (if (and (eq (list_length e) 3) (!isNaN (nth 0 e)) (eq (nth 1 e) "..") (!isNaN (nth 2 e))) (cons "new$array" (arr2list (range (parseFloat (nth 0 e)) (+ 1 (parseFloat(nth 2 e)))))) (if (and (eq (list_length e) 4) (!isNaN (nth 0 e)) (!isNaN (nth 1 e)) (eq (nth 2 e) "..") (!isNaN (nth 3 e))) (cons "new$array" (arr2list (range (parseFloat (nth 0 e)) (+ 1 (parseFloat(nth 3 e)))(- (parseFloat(nth 1 e)) (parseFloat(nth 0 e)))))) (if (or (and (eq (list_length e)3)(eq (nth 1 e) "..")(set e (list (nth 0 e)(nth 2 e)))) (and (eq (list_length e)4)(eq (nth 2 e) "..")(set e (list (nth 0 e)(nth 3 e)(nth 1 e))))) (let* ((f '(lambda (start limit second step) (if second==null (set second start+1)) (set step second-start) (let* ((length (Math.max (Math.ceil limit/step) 0)) (a (Array length)) (idx 0)) (while length>idx (set (aref a idx) start idx idx+1 start start+step)) a)))) `(,f ,@e)) this.count-- ;; fall-though branch, tell the expander nothing was expanded. `(range ,@e))))) (when test.innerHTML (out (range 0 3 .. 17)) ;; inline JS array expanded at translate time (out (range -3 .. 3)) ; same (var xx 10) (out (range 1 3 .. xx)) ;; func application ) </textarea> <textarea id=example14a__html><p id=test>Range comprehensions</p> </textarea> </pre> <pre><textarea id=example16>;;;; Metal Clock Train test 1. inspired by gears from https://bl.ocks.org/mbostock/1353700 ;; expanded with involute tooth shape modeled with cubic beziers, simple cutouts added. (set gear \(g) (let* ((r g.radius)(n g.teeth) (h g.h||16)(off g.off||h/8)(flat g.flat||h/16) (r0 g.r0||g.radius+off-h/2) (rc1 r+h/16)(rc2 r+h/4) (r1 g.radius+off+h/2-flat) (rax g.rax||8) (r0r g.r0r||r0) (r1r g.r1r||r1)(r0dir g.r0dir||1) (da 2*Math.PI/n) ;; polar/radial width of a complete tooth (dar0 da*0.5)(dar1 da/6)(daq da-dar0-dar1) (a -Math.PI/2)(path ["M" 0 "," -r0 ])(i -1)) (while n>++i (path.push "A" r0r "," r0r " 0 0," r0dir " " (* r0 (Math.cos a+=dar0)) "," (* r0 (Math.sin a)) "L" (* r (Math.cos a)) "," (* r (Math.sin a)) "C" ;; two involute Cs elaborate Mike Bostock’ L-A-L tooth (* rc1 (Math.cos a)) "," (* rc1 (Math.sin a)) " " (* rc2 (Math.cos a)) "," (* rc2 (Math.sin a)) " " (* r1 (Math.cos a+=daq/2)) "," (* r1 (Math.sin a)) "A" r1r "," r1r " 0 0,1 " (* r1 (Math.cos a+=dar1)) "," (* r1 (Math.sin a)) "C" (* rc2 (Math.cos a+=daq/2)) "," (* rc2 (Math.sin a)) " " (* rc1 (Math.cos a)) "," (* rc1 (Math.sin a)) " " (* r (Math.cos a)) "," (* r (Math.sin a)) "L" (* r0 (Math.cos a)) "," (* r0 (Math.sin a)))) (path.push "M0," -rax "A" rax "," rax " 0 0,0 0," rax "A" rax "," rax " 0 0,0 0," -rax "Z") (+ (path.join "") (if g.cuts (\cr ;; gen cutouts (let* ((k1 c.k1||0.2)(k2 c.k2||0.15)(k3 c.k3||0.08) (n c.n||4)(r1 r*c.r1||20)(r2 r*c.r2||90) (da 2*Math.PI/n) ;; (da1 da*k1) (da2 da-da1)(da3 da*k3) (da4 da*k3) (a -Math.PI/2+da1/2)(path [])) (while n-- (path.push "M" (* r1 (Math.cos a)) "," (* r1 (Math.sin a)) "A" r1 "," r1 " 0 0,1 " (* r1 (Math.cos a+=da2)) "," (* r1 (Math.sin a)) "L" (* r2 (Math.cos a+=da3)) "," (* r2 (Math.sin a)) "A" r2 "," r2 " 0 0,0 " (* r2 (Math.cos a-=da2+2*da3)) "," (* r2 (Math.sin a)) "L" (* r1 (Math.cos a+=da3)) "," (* r1 (Math.sin a)) "Z" ) (set a a+da) ) (path.join "") ) g.cuts g.radius||100) "") )) gear_train \(frame spec generator) (let* ((y 0) (idx 0) (k 1) prev (fill #6baed6) axle (axles []) g_offset g_rot) (if !spec (set spec [])) ;; default, empty train, possible placeholder etc (if (isNaN (.(aref spec 0) y)) (spec.unshift {k 1 y 0 nm "driver" g []})) ;; insert default 1st axle (set generator generator||gear) (.(spec.forEach (lambda (g i a) (if g.fill (set fill g.fill)) (if (not (isNaN g.y)) ;; g is axle. g.y is link direction indicator, 1, -1 or 0 (set axle g axle.a (if (isNaN axle.a) 0 axle.a) g (aref a i+1) ;; grab next one. the next spec entry after axle MUST be a gear y (if prev (+ y (* axle.y (+ prev.radius g.radius))) axle.y) axle.k (if (not (isNaN axle.k)) axle.k ;; use specified number (0 is still) or lookup or compute (if (and (eq (typeof axle.k) 'string) (.(axles.filter \v.v.nm==axle.k)(slice -1)(pop))) (.(axles.filter \v.v.nm==axle.k)(slice -1)(pop) k) ;; use the k of k-named axle (if (eq (typeof axle.k) 'function) (axle.k axles k y g) ;; if k is a func, call it -k*prev.teeth/g.teeth))) ;; compute k using theeth numbers k axle.k ;; remember k axle.y y g_offset (.(frame.append'g)(attr'transform "translate(0,{y})")) axle.g [] g_rot (.(g_offset.append'g)(attr'class 'g_rot)(datum axle)) axle (if axle.init (axle.init) axle) ;; if defined, init must return axle (old or new) idx (axles.push axle)) ;; gear or props (if !g.radius nil ;; use same gear gs, same k (set prev g) (.(g_rot.datum) g (push g)) (.(g_rot.append'path)(datum g) (attr {d generator fill fill transform "rotate({axle.a})" })))) nil))) axles)) (let* ((x (Math.sin 2*Math.PI/3)) (y (Math.cos 2*Math.PI/3)) (width 560) (height 500) (radius 300) (offset 0) (speed 3/80) (start (Date.now)) (svg (.(d3.select "body")(append "svg") (attr {width width height height}) (append "g") (attr "transform" (+ "translate(" width/3 "," height/4 ")scale(0.4)")) (append "g"))) (frame (. svg (append "g") (datum {radius Infinity}))) (t1 80)(r1 radius)(k1 1) (t12 8)(r12 r1*t12/t1) (t2 60)(r2 r1*60/80)(k2 -t2/t12)(off2 (+ 4 r2 r12)) (t21 10)(r21 r2*t21/t2) (t3 60)(r3 r2)(off3 (+ 2 off2 r3 r21 )) (k3 -k2*t3/t21) ) (console.log (gear_train frame [{teeth t1 radius r1 k k1 cuts {n 6 r1 0.2 r2 0.94} } {fill #6b8ea6} {teeth t12 radius r12 k k1 } {y 1.02 nm "second" fill #9ecae1} {teeth t2 radius r2 k k2 off 3 cuts {n 4 r1 0.3 r2 0.9 k3 0.07} } {fill #6baed6} {teeth t21 radius r21 k k2} {y 1.01 fill #c6dbef} {teeth 60 radius radius*60/80 k k3 cuts {n 4 r1 0.2 r2 0.84 k1 0.3 k3 0.12}} ;;{y 0 k "driver" nm "hour hand axle" a 45 init \.this } ;;{nm "hour" teeth 3 h 200 off -30 radius 50 flat -200 } ;; iw 0.427 ])) ;; debug (. (frame.selectAll ".g_rot")(each \d( \.(console.log d)) ;;( \.(console.log 'dd)) (d3.timer \.(let* ((angle (*(- (Date.now) start) speed)) (transform \d.(+ "rotate(" d.k*angle")"))) ;;(console.log angle) (. (frame.selectAll ".g_rot")(attr "transform" transform)) ;;(. frame (attr "transform" transform)) nil )))</textarea> <textarea id='example16__css'> path { fill-rule: evenodd; stroke: #333; stroke-width: 2px; } .g1 path { fill: #6baed6; } .g2 path { fill: #9ecae1; } .g3 path { fill: #c6dbef; } </textarea> </pre> <pre><textarea id=example17>;;;; Wooden Clock Gears, test 1. inspired by gears from https://bl.ocks.org/mbostock/1353700 and various wooden clock designs (defmacro defun_gear()(read example16.value)) (defun_gear) (let* ((x (Math.sin 2*Math.PI/3)) (y (Math.cos 2*Math.PI/3)) (width 560) (height 500) (radius 300) (offset 0) (speed 3) (start (Date.now)) (svg (.(d3.select "body")(append "svg") (attr {width width height height}))) (svg_g (. svg (append "g") (attr "transform" (+ "translate(" width/3 "," height/4 ")scale(5)")) (append "g"))) (defs (. svg (append 'defs))) (deftex \(name url) (.(defs.append "pattern") (attr {x 0 y 0 width 1 height 1 id name patternContentUnits "objectBoundingBox" patternUnits 'objectBoundingBox patternTransform "scale(1,1)"}) (append "image") (attr {width 1 height 1 "xlink:href" url preserveAspectRatio "none" ;; allows to use non-square images. }))) (woods (. ["wood-textures-high-quality-1" "wood+pattern+texture+16" "wood+pattern+texture+6" "wood+pattern+texture+17" "wood+pattern+texture+27" "wood+pattern+texture+28" "wood+pattern+texture+21"] (forEach \ni.(deftex (+ "w" i) (+ "http://gdj.gdj.netdna-cdn.com/wp-content/uploads/2013/03/" n ".jpg"))))) (frame (. svg_g (append "g") (datum {radius Infinity}))) (t1 80)(r1 radius)(k1 1) (t12 8)(r12 r1*t12/t1) (t2 60)(r2 r1*60/80)(k2 -t2/t12)(off2 (+ 4 r2 r12)) (t21 10)(r21 r2*t21/t2) (t3 60)(r3 r2)(off3 (+ 2 off2 r3 r21 )) (k3 -k2*t3/t21) ) (. frame (append "g") (append "rect") (attr {x -0.4*radius width 0.8*radius y -1.2*radius height 4*radius fill "url(#w5)"})) (gear_train frame [{fill "url(#w1)"} {teeth t1 radius r1 k k1 cuts {n 6 r1 0.2 r2 0.9 k1 0.56 k3 0.25}} {fill "url(#w5)"} {teeth t12 radius r12 k k1 r0 r12-3 r0r 4 r1r 4 r0dir "0"} {y 1.01 fill "url(#w2)"} {teeth t2 radius r2 k k2 off 3 r0 r2-2 r0r 4 r1r 4 r0dir "0" cuts {n 4 r1 0.3 r2 0.82 k1 0.5 k3 0.2} } {fill "url(#w2)"} {teeth t21 radius r21 k k2 r0 r21-2 r0r 4 r1r 4 r0dir "0" } {y 1.01 fill "url(#w3)"} {teeth 60 radius r3 k k3 r0 r3-2 r0r 4 r1r 4 r0dir "0" cuts {n 5 r1 0.2 r2 0.8 k1 0.73 k3 0.3}} ]) ;;( \.(console.log 'dd)) (d3.timer \.(let* ((angle (*(- (Date.now) start) speed)) (transform \d.(+ "rotate(" d.k*angle/radius ")"))) ;;(console.log angle) (. (frame.selectAll ".g_rot")(attr "transform" transform)) ;;(. frame (attr "transform" transform)) nil )) (if 1.01 (. svg_g (transition)(duration 15000)(ease (d3.ease 'poly 0.2)) (attr "transform" (+ "scale(0.08)")) )) )</textarea> <textarea id='example17__css'> path { fill-rule: evenodd; stroke: #333; stroke-width: 2px; } </textarea> </pre> <pre><textarea id=example15>;;;; Cog Gear Designer. Tooth nomenclature: ;; n number of theeth pr pitch radius ;; rr root radius rrf root radius fill radius rw root fill width ;; or outside radius orf outside radius fill radius ;; the same terms apply to cutouts; a few more added to control beziers and skew (defmacro com2a(command) ;; define X as 2rg path command generator `\ar.(+ ,command (* r (cos a)) "," (* r (sin a)))) (defmacro com2ad(c flag) ;; path or a blank string depending on flag `\ar.(if ,flag (+ ,c (* r (cos a)) "," (* r (sin a))) empty)) (defmacro numval(val dflt) `(if (isNaN ,val) ,dflt ,val)) (let* ((cos Math.cos)(sin Math.sin)(empty "")(keys Object.keys) (object_p \v.(eq (typeof v) "object")) (reduce \(obj beg end fkey fobj fnum) (+ (. (Object.keys obj) (reduce \pk.(set p (+ p (fkey k) (let ((v (aref obj k))) (if (eq (typeof v) "object") (fobj v beg end fkey fobj fnum) (fnum v))) " ")) beg)) end)) ;; draw svg path elements, can take optional stepper param (M (com2a 'M))(L (com2a 'L))(C (com2a "C"))(_ (com2a " "))(LD (com2ad 'L mk_gear.DEBUG)) (A \(a dist rf dir)(+ "A" rf "," rf dir " " (* dist (cos a)) "," (* dist (sin a)))) (mk_cuts \cd.(let* ((PR d.radius+d.off-d.h/2||100)(n (numval c.n 4)) (rw (numval c.rw 0.2))(ow (numval c.ow rw))(skw n*c.skw||0)(iw c.iw||0) (s1skw n*c.s1skw||0)(s2skw n*c.s2skw||0) (rr (numval c.rr 0.2))(rrf (numval c.rrf rr)) (bot_dir (if rrf>0 " 0 0,1 " " 0 0,0 ")) (or c.or||0.9)(orf (numval c.orf or)) (s1r (numval c.s1r rr/2+or/2))(s2r (numval c.s2r s1r)) (inits ;; because of dat.GUI, must put all dervied props back to d. "_" is indicator of derived section (set c._ c._+1||0 c.n n c.rw rw c.ow ow c.skw skw/n c.iw iw c.s1skw s1skw/n c.s2skw s2skw/n c.rr rr c.rrf rrf c.or or c.orf orf c.s1r s1r c.s2r s2r rr PR*rr rrf PR*rrf or PR*or orf PR*orf s1r PR*s1r s2r PR*s2r )) (top_dir (if orf>0 " 0 0,0 " " 0 0,1 ")) (da 2*Math.PI/n) ;; radial size of each cutout (see gear's diametral pitch) (da1 da*rw)(daorw2 (* da/2 (- ow rw)))(dask da*skw) (dap1 da*s1skw)(dap2 da*s2skw) (a -Math.PI/2-da1/2-da*iw) (path []) ) ;; svg is OK with ngative (rrf and orf.... set rrf (Math.abs rrf)) (while n-->0 (path.push (M a rr a+=da1) (A a rr rrf bot_dir) (LD a+dap1 s1r)(LD a rr) (C a+=dap1 s1r a+=daorw2+dask-dap1)(_ a+dap2 s2r)(_ a or ) (LD a+dap2 s2r)(LD a or) (A a-=da1+2*daorw2 or orf top_dir ) ;; (L a rr a+=da) (LD a+dap2 s2r)(LD a or) (C a+dap2 s2r a+=daorw2-dask)(_ a+dap1 s1r)(_ a rr a+=da) (LD a+dap1-da s1r)(LD a-da rr) "Z")) (path.join "") ))) (set mk_gear ;; theeth and h are recommended to always supply \d.(let* ((n (numval d.teeth 30)) (h (numval d.h 16)) (off (numval d.off h/8))(flat (numval d.flat h/16)) (PR (numval d.radius 200)) (rax (numval d.axr h/2)) (rrw (numval d.rrw 0.5))(pw (numval d.pw rrw))(ow (numval d.ow 1/6))(skw n*d.skw||0)(iw d.iw||0) (inits ;; because of dat.GUI, must put all dervied props back to d. "_" is indicator of derived section (set d._ d._+1||0 d.teeth n d.h h d.off off d.flat flat d.radius PR d.axr rax d.rrw rrw d.pw pw d.ow ow d.skw skw/n d.iw iw)) (rr PR+off-h/2)(or PR+off+h/2-flat) (ha or-PR) (rc1 PR+ha/8)(rc2 PR+ha/2) (da 2*Math.PI/n) ;; also known as diametral pitch (darr da*rrw) (dask da*skw) (daor da*ow)(daq da-darr-daor) (darpw da-darr-da*pw) (a -Math.PI/2+da*iw) ;; was: -PI/2+darr/2 which loosk good but is unfriendly for gear trains... (path [(M a rr)])(i -1)) (while n>++i (path.push (A a+=darr rr rr " 0 0,1 " ) (L a+=darpw/2+dask PR) (C a rc1)(_ a rc2 )(_ a+=daq/2-darpw/2 or) (A a+=daor or or " 0 0,1 " ) (C a+=daq/2-darpw/2 rc2) (_ a rc1)(_ a PR) (L a+=darpw/2-dask rr ))) (path.push "M0," -rax "A" rax "," rax " 0 0,0 0," rax "A" rax "," rax " 0 0,0 0," -rax "Z") (reduce d (path.join "") "" \."" \c(mk_cuts c d) \."") ) mk_gear.DEBUG false gear ;; in this section gear used in clocks, hence most are named [{nm"escapement1" teeth 30 h 38 iw -0.35 radius 100 k 1 axr 4 cuts {n 10 rr 0.5 or 0.9 orf 0.95 skw 0.025 rr 0.21 iw 0.3 ow 0.9 rw 0.8} ow 0.05 pw 0.08 axr 8 skw 0.020 off 1 flat 17.5 } {nm"escapement2" teeth 30 h 38 iw -0.35 radius 100 k 1 axr 8 cuts {n 15 rr 0.300 or 0.950 orf 0.950 skw 0.029 iw -0.270 ow 0.873 r2r 0.900 rw 0.719 rrf 0 s1skw 0.001 s2skw 0.015 s1r 0.567 s2r 0.616 } ow 0.050 pw 0.080 skw 0.020 off 1 flat 18 rrw 0.500 } {nm"escapement3" teeth 30.000 h 38.000 iw -0.350 radius 100.149 k 1.000 axr 8.000 cuts {n 10.000 rr 0.266 or 0.932 orf 0.950 skw 0.178 iw -0.150 ow 0.828 r2r 0.900 rw 0.267 rrf 0.000 s1skw 0.011 s2skw -0.044 s1r 0.346 s2r 0.641 _ 4065.000 } ow 0.050 pw 0.080 skw 0.020 off 1.000 flat 18.000 rrw 0.500 _ 4065.000 } {nm"pinion_t10h7r18" teeth 10 h 7 radius 18 off 2 cuts { n 10 rr 0.5 or 0.8 iw 0.3 ow 0.7 rw 0.6} } {nm"intermed_t60h7r6x18" teeth 6*10 h 7 radius 6*18 off 2 cuts {n 10 rr 0.1 rrf -0.569 or 0.960 orf 0.960 rw 0.750 ow 0.943 skw -0.220 s1skw 0.006 s2skw 0.220 s1r 0.148 s2r 0.579 iw 0 } ow 0.170 pw 0.500 rrw 0.500 skw 0 flat 1 axr 4 } {nm"pinion_t6h9r15" teeth 6 radius 15 h 9} {nm"min_gear" teeth 10*6 radius 10*15 h 9 off 2 cuts {n 6 rr 0.465 rrf 0.200 or 0.95 orf 0.95 rw 0.023 ow 0.2 skw 0 s1skw 0 s2skw 0 iw 0 s1r 0.636 s2r 0.920 } cuts2 {n 6 iw -0.500 rr 0.200 rrf 0.200 or 0.95 orf 0.95 rw 0.812 ow 0.67 skw 0 s1skw 0 s2skw 0 s1r 0.677 s2r 0.920 } } {nm"pinion_t12h8r22" teeth 12 radius 22 h 8 off 2 } {nm"intermed_t4x12h8r4x22" teeth 4*12 radius 4*22 h 8 off 2 cuts {n 6 rr 0.2 rrf 0.200 or 0.920 orf 0.900 rw 0.023 ow 0.200 skw 0 s1skw 0 s2skw 0 iw 0 s1r 0.636 s2r 0.920 } cuts2 {n 6 iw -0.500 rr 0.200 rrf 0.200 or 0.920 orf 0.920 rw 0.812 ow 0.643 skw 0 s1skw 0 s2skw 0 s1r 0.677 s2r 0.920 } } {nm"pinion_t12h8r5x22d4" teeth 12 radius 5*22/4 h 8 cuts { n 6 rr 0.1 or 0.2 iw 0.3 ow 0.7 rw 0.6} } {nm"hour_gear_t3x12h8" teeth 3*12 radius 5*3*22/4 off 2 h 8 cuts {n 5 rr 0.12 rrf 0 or 0.93 orf 0.93 rw 0 ow 0.94 skw -0.02 s1skw 0.01 s2skw 0.1223 s1r 0.06 s2r 0.737 iw 0 } } {nm"face_plate" teeth 1 h 0 radius 230 off 0 flat 0 ow 0.5 pw 0.5 axr 17 cuts1 {n 1 rr 0.7 or 0.7 rrf -0.1 orf 0.1 rw 0.5 ow 0.5 } cuts2 {n 4 rr 0.856 rrf -0.1 or 0.856 orf 0.1 rw 0.17 ow 0.17 skw 0 s1skw 0 s2skw 0 } cuts3 {n 12 rr 0.85 rrf -0.01 or 0.85 orf 0.01 rw 0.3 ow 0.3 skw 0 s1skw 0 s2skw 0 } } {nm"min_hand" teeth 1 h 150 radius 191 ow 1.002 pw 1.009 rrw -0.6 iw 0.3 off -110 flat -36.5 axr 3} {nm"" teeth 1 h 200 radius 216 ow 1 pw 1.008 rrw -0.600 iw 0.299 skw 0 off -110 flat -19 axr 9 } ;; iw 0.3 {nm"hour_hand" teeth 1 h 51 radius 142.165 ow 1 pw 1.020 rrw -0.850 iw 0.425 skw -0 off -97 flat -85 axr 5 } ;; iw 0.427 {nm"" teeth 1.000 h 35.300 radius 142.044 ow 1.002 pw 1.016 rrw -0.850 iw 0.425 skw 0.000 off -97.054 flat -85.127 axr 2.000 cuts {n 1.000 or 0.578 rw 0.124 ow 0.000 skw 0.000 iw 0.000 s1skw 0.000 s2skw 0.000 rr 0.648 rrf 1.203 orf 0.160 s1r 0.566 s2r 9.836 _ 1877.000 } _ 1877.000 } {nm"seconds_hand" teeth 1 h 174 radius 200 ow 1 pw 1.003 rrw -0.6 iw 0.3 off -110 flat -26 axr 3} ])) (if window.dat (let* ((geom {width 700 height 600 radius 200 scale 1.5 anch_y 100 anch_r 110}) (body (d3.select "body")) (svg (. body (append "svg")(attr geom) (append "g")(attr 'transform "translate({geom.width/2},{geom.height/2})") (append "g")(attr 'transform "scale({geom.scale})") )) (frame (. svg (append "g") (datum {radius Infinity}))) (g1c {h 15 teeth 55 radius 234 k 1 cuts {n 9 rr 0.120 rrf -0.025 or 0.890 orf 0.496 rw 0.358 ow 0.845 skw -0.020 s1skw -0.001 s2skw 0.061 s1r 0.182 s2r 0.871 } }) (gear (gear.concat [ ;;{h 16 teeth 48 radius 207.886 k 1 cuts {n 6 rr 0.465 rrf 0.200 or 0.920 orf 0.90 rw 0.023 ow 0.200 skw 0.000 s1skw 0 s2skw 0 iw 0 s1r 0.636 s2r 0.92 } cuts2 {n 6 iw -0.500 rr 0.200 rrf 0.200 or 0.92 orf 0.92 rw 0.812 ow 0.643 skw 0.000 s1skw 0.000 s2skw 0.000 s1r 0.677 s2r 0.92 } ow 0.170 pw 0.500 axr 8 skw 0 off 3 flat 1 } {h 13 teeth 49 radius 216.459 k 1 cuts {n 10 rr 0.100 rrf -0.020 or 0.770 orf 0.170 rw 0.430 ow 0.880 skw 0.130 s1skw 0.032 s2skw -0.050 s1r 0.190 s2r 0.370 iw 0 } ow 0.170 pw 0.500 axr 8 skw 0 off 3 flat 1 } {h 15 teeth 60 radius 234 k 1 cuts {n 5 rr 0.149 rrf -0.328 or 0.930 orf 0.930 rw 0 ow 0.943 skw -0.220 s1skw 0.042 s2skw 0.123 s1r 0.143 s2r 0.637 iw 0 } ow 0.170 pw 0.500 rrw 0.500 iw 0 skw 0 off 4 flat 1 axr 8 }{h 16 teeth 55 radius 234 k 1 cuts {n 3 rr 0.239 rrf -0.589 or 0.913 orf 0.910 rw 0.452 ow 0.924 skw 0.169 s1skw 0.038 s2skw -0.026 s1r 0.308 s2r 0.481 iw 0 } ow 0.170 pw 0.500 rrw 0.500 iw 0 skw 0 off 2 flat 1 axr 15 } {h 16 teeth 60 radius 234 k 1 cuts {n 2 rr 0.179 rrf 0.037 or 0.894 orf 0.897 rw 0.176 ow 0.904 skw 0.220 s1skw 0.220 s2skw -0.220 s1r 0.001 s2r 0.437 } off 3 flat 1 rc1 1 rc2 4 } {h 15 teeth 60 radius 234 k 1 cuts {n 4 rr 0.348 rrf -0.316 or 0.904 orf 0.897 rw 0.044 ow 0.960 skw -0.186 s1skw -0.079 s2skw 0.115 s1r 0.424 s2r 0.212 } ow 0.170 pw 0.500 axr 8 skw 0 off 3 flat 1 } {h 14 teeth 60 radius 212.283 k 1 cuts {n 6 rr 0.749 rrf -0.029 or 0.739 orf 0.037 rw 0.631 ow 0.631 skw 0 s1skw 0 s2skw 0 iw 0 s1r 0.731 s2r 0.758 } cuts2 {n 6 iw 0.500 rr 0.400 rrf -0.008 or 0.400 orf 0.060 rw 0.770 ow 0.770 skw 0 s1skw 0 s2skw 0 s1r 0.400 s2r 0.400 } ow 0.170 pw 0.500 axr 8 skw 0 off 2 flat 1 } {h 14.000 teeth 60.000 radius 212.268 k 1.000 cuts {n 3.000 rr 0.650 rrf -0.028 or 0.650 orf 0.038 rw 0.544 ow 0.544 skw 0.000 s1skw 0.000 s2skw 0.000 iw 0.000 s1r 0.650 s2r 0.650 _ 4483.000 } cuts2 {n 3.000 rr 0.211 rrf -0.034 or 0.880 orf 0.880 rw 0.021 ow 0.585 skw 0.000 s1skw 0.000 s2skw 0.000 iw -0.500 s1r 0.764 s2r 0.863 _ 4996.000 } ow 0.170 pw 0.500 axr 12.000 skw 0.000 off 2.000 flat 1.00 } {teeth 1.000 h 242.096 radius 242.592 ow 1.002 pw 1.008 rrw -0.600 iw 0.300 skw 0.000 off -109.746 flat 2.400 axr 3.000 cuts {n 1.000 or 0.957 rw 0.197 ow 0.000 skw 0.000 iw 0.000 s1skw 0.000 s2skw 0.000 rr 0.966 rrf 1.000 orf 0.366 s1r 0.646 s2r 43.414 } } ;; gN ])) (gui)(gear_0 (aref gear 0))(gui_dock (.(body.append'div)(attr'id'gui_dock))) (set_gui \g.(let* ((init (lambda (gui obj was_closed_p) (if !gui nil (set was_closed_p gui.closed) (gui.destroy)) (set dat.GUI.prototype.knob knob gui (new dat.GUI { autoPlace false })) (gui.remember obj) (if was_closed_p (gui.close)) gui)) (repath (lambda() (. frame (selectAll "path")(attr "d" mk_gear)) (. frame (selectAll "circle")(attr {cy \d(if d.skw -geom.anch_y 0) r \d(if d.skw geom.anch_r d.radius)})) (set curr_def.value (print gear_0)))) (knob \(obj prop min max step dflt onchange_f) (let* (ctrl (realval (aref obj prop))) ;; stupid bug in DAT.gui (set realval (numval realval dflt||min/2+max/2) ) (if (eq (typeof realval) 'number) (set min (Math.min min realval) max (Math.max max 1.2*realval) step step||max/1000-min/1000 (aref obj prop) (Math.abs realval))) (set ctrl (this.add obj prop min max)) (if step (ctrl.step step)) (ctrl.setValue realval) ;; fixes stupid bug in DAT.gui (ctrl.onChange onchange_f||repath) this)) cuts tooth_gui) (set gui (init gui g) tooth_gui (gui.addFolder "gear & tooth") geom_gui (gui.addFolder "svg/anchor/env geometry") tooth_gui.closed gui.closed) ;;(console.log gui.knob) (. geom_gui (knob geom 'scale .1 6 0.1 undefined \.(.(svg.attr 'transform (+ "scale("geom.scale")")))) (knob geom 'anch_y 20 300 1 undefined)(knob geom 'anch_r 20 300 1 undefined) ) (. tooth_gui (knob g 'radius 1 400) (knob g 'teeth 1 200 1) (knob g 'h -50 50 ) (knob g 'ow -1 1 nil 1/6) (knob g 'pw -1 1 nil 0.5) (knob g 'rrw -1 1 nil 0.5) (knob g 'iw -1 1 nil 1e-5) ;;stupid dat.gui bug (knob g 'skw -0.22 0.22 nil 1e-5) (knob g 'off -50 50 nil g.h/4) (knob g 'flat -50 50 nil g.h/16) (knob g 'axr 0 40 1 g.h/2)) (reduce g "" "" \k.cuts=k \c(progn (gui.remember c) (. (gui.addFolder cuts) (knob c 'n 0 24 1) (knob c 'rr 0.1 1) (knob c 'rrf -1 1) (knob c 'or 0.1 1) (knob c 'orf -1 1) (knob c 'iw -0.7 0.7 0.01 0) (knob c 'rw 0 1) (knob c 'ow 0 1) (knob c 'skw -0.22 0.22) (knob c 's1skw -0.22 0.22) (knob c 's2skw -0.22 0.22) (knob c 's1r 0 1.2) (knob c 's2r 0 1.2) (knob mk_gear 'DEBUG))) \."" ) (.(gui_dock.html "")(append \.gui.domElement)) (. d3 (selectAll ".dg .property-name") (style 'width "20%")) (. d3 (selectAll ".dg .c") (style 'width "80%")) (set gui.width 200) (set gui.domElement.style.right "200px") )) (format3f (d3.format ".3f"))(format_d (d3.format "d")) (print \g.(reduce g "{" "}" \k(+ k " ") reduce \v.(or nil ;; (format_d v) (format3f v)))) (fg (. frame (append "g")(attr "class" "g1"))) (gg1 (. fg (selectAll 'path) (data [gear_0]) (enter))) (gg1p (. gg1 (append "path")(attr "d" mk_gear))) (txt_display (. body (append 'textarea))) (reinit_aux \d.(let* ((cir (.(fg.selectAll 'circle))) (rm (cir.remove)) (e (.(fg.selectAll 'circle)(data [d])(enter))) ;; (a1 (.(e.append "circle")(attr {cy \d.-1.5*d.radius r \d.1.1*d.radius}))) (a2 (.(e.append "circle")(attr {cy \d(if d.skw -geom.anch_y 0) r \d(if d.skw geom.anch_r d.radius)})))))) (reinit \d.(let* () (set gear_0 d geom.scale 180/gear_0.radius) (. fg (selectAll 'path)(remove)) (console.log d) (. fg (selectAll 'path) (data [d]) (enter) (append "path")(attr {d mk_gear 'stroke-width 2/geom.scale})) (.(svg.attr 'transform "scale({geom.scale})")) (set_gui d) (reinit_aux d) (. txt_display (text (print gear_0))))) ) ;; end let* binds (set_gui gear_0) ;;(. fg (selectAll 'circle) (data [gear_0])(enter) (append "circle")(attr {x 0 y 0 r \d.d.radius})) (. txt_display (attr {id 'curr_def cols 80}) (text (print gear_0)) (on 'keyup \d.(reinit (eval (+ "d=" (Lisp.translate (Lisp.read this.value))))))) (. body (append 'img) (attr {src "http://image.slidesharecdn.com/mahesh-140825084947-phpapp02/95/modelling-of-spur-gear-in-pro-e-software-9-638.jpg" 'width '500px height 'auto})) (. body (insert 'div 'svg) (attr 'class 'example_panel) (selectAll 'a) (data gear)(enter) (append 'a)(text \di.(+ "Example " ++i)) (on 'click reinit)) ))</textarea> <textarea id=example15__html> <script src=https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.5/d3.min.js></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/dat-gui/0.5.1/dat.gui.min.js"></script> <script src="https://codepen.io/dmitrynizh/pen/GogBaw.js"></script> </textarea> <textarea id='example15__css'>body {background: beige; } path { stroke: #333; /*stroke-width: 2px;*/ } circle { stroke: #900; stroke-width: 1px; stroke-dasharray: 8,4; fill: none; } .g1 { fill: lightblue; } #gui_dock { position: absolute; top: 0px; right: -2px; opacity: 0.5; } .example_panel { font: 12px Arial; cursor: pointer; left: 6px; top: 15px; position: absolute; border: 2px solid black; padding: 3px; opacity: 0.5; } .example_panel:hover, #gui_dock:hover { opacity: 1; } .example_panel a { border: 1px solid black; padding: 2px; color: orange; background: #444; display: block; } .example_panel a:hover { border: 1px solid white; color: white; }</textarea> </pre> <!-- code example -------------------------- - --> <pre><textarea id='pendulum_clock'>;;;; Pendulum + Escapement + Geares = Wall Clock! ;; gist:f7248b52ed92b57e442e (defmacro incl_gear_train()(read example16.value))(incl_gear_train) (include "#example15") (\p(set p.appendStd p.append p.appendWithLink \n(let* ((s (this.appendStd n))) s.__ps=this s) p.append p.appendWithLink p.end \.this.__ps ) d3.selection.prototype) (let* ((start (Date.now)) (PI Math.PI)(min Math.min) (w window) (rAF (or w.requestAnimationFrame w.webkitRequestAnimationFrame w.mozRequestAnimationFrame \f(setTimeout \.(f (- (Date.now) start)) 1000/60))) (degr2rad \d.d*PI/180) (rad2degr \r.r*180/PI) lastTime (pendulum { ;; SI meter kg radian mass 1 length 0.99 init_swing (degr2rad 5) TH PI/2 W 0 A 0 I 0 qual_k 1-0.1/60 driveW 150 init \.(set this.I this.mass*this.length*this.length this.TH this.TH-this.init_swing)}) (weight (\w(set w.r w.p/2/PI w.x 1000*w.r w.hmm 1000*w.h w w) ;; radius of the pulley m and mm {h 1.2 ;; in m, from the floor and defines how long the clock will run p 0.1 ;; this gives 0.1m of perimeter y 701 ;; in mm, offset from the pendulum axis mass 5})) (wheel { ;; escapement wheel mass 0.1 R 0.05 ;; *equivalent* gear train mass and radius offset 136 ;; mm, from the pendulum axis TH 0 ;; -3.6 ;; in degrees not radians for convenience W 0 A 0 I 0 limit_TH weight.h/weight.p*60*360 init \.(set this.lock_TH this.TH this.I this.mass*this.R*this.R)}) (gear_a 260) ;; acceleration of the whole of the gear train: Ttrain/Itrain (body (d3.select 'body)) (geom {width 700 height 700 radius 200}) (border (.(body.select "svg")(append'rect)(attr geom)(style{stroke "black" fill #cfc7af}))) (svg_zg (.(body.select "svg")(attr geom) (call (. (d3.behavior.zoom)(scaleExtent [0.1 2]) ;;(scale 0.5);;(center [geom.width/2 geom.height/2]) (on 'zoom \.(progn ;;(d3.event.preventDefault)(d3.event.stopPropagation) (svg_zg.attr 'transform "translate({d3.event.translate}) scale({d3.event.scale})"))))) (append "g"))) (svg_g (.(svg_zg.append "g") (attr "transform" "translate({geom.width/2},{geom.height/16})") (append "g");; (attr "transform" "scale(0.5)") )) (pendulum_g (. svg_g (append 'g))) (weight_g (. svg_g (append 'g)(attr 'transform "translate({weight.x},{weight.y})"))) (gear_train_frame (. svg_g (append 'g)(attr "transform" (+ "translate(0,{wheel.offset})")))) movement ;; our gear train having 4+ axles, see gear_train call below (stopped_by_left true) (stopped_by_right false) (displ p1) (format42 (d3.format "4.2f"))(format_int (d3.format "d")) ;;(wheel_update \.(escapement.attr "transform" (+ "rotate(" (format42 wheel.TH) ")"))) (wheel_update \.(let ((rope_y weight.y-movement.minutes_gear.y-wheel.offset)) (.(gear_train_frame.selectAll '.g_rot)(attr "transform" \d."rotate({d.k*wheel.TH})")) (. (d3.select "#rope") (attr {y -weight.k*wheel.TH-rope_y height weight.k*wheel.TH+rope_y})) (. weight_g (attr "transform" "translate({weight.x},{(weight.k*wheel.TH+weight.y)})")))) (safe_interval \t(if 0.05>t t (if 45>wheel.lock_TH (update_hands)) 0.05)) ;; (tick \(time) (let* ((dt (safe_interval (/ (- time lastTime) 1000))) ;; in secs, normally dt=0.0166 (pendulum_TH_prev pendulum.TH) ;; get the new angle using last tick's data (pth (set pendulum.TH (+ pendulum.TH pendulum.W*dt 0.5*pendulum.A*dt*dt))) ;; svg operates in degrees (degr (- (rad2degr pendulum.TH) 90)) ;; calculate Torque from current position. T = F*R (T (* pendulum.mass 9.81 (Math.cos pendulum.TH) pendulum.length)) ;; angular acceleration a = T/I (A T/pendulum.I)) (set lastTime time) (if logger.log (logger.run time degr A)) ;; https://en.wikipedia.org/wiki/Verlet_integration#Velocity_Verlet ;; Velocity_Verlet: velocity change is dt * Aaverage (set pendulum.W (* (+ pendulum.W (* 0.5 (+ A pendulum.A) dt)) pendulum.qual_k) pendulum.A A) ;;(console.log dt pendulum_TH_prev pendulum.TH pth degr T A pendulum.W) (pendulum_g.attr "transform" "rotate({degr})") (if (> 1 (Math.abs degr)) ;;free wheel zone: about +-1 degree or so (if pendulum.TH>=pendulum_TH_prev ;; swings from right to left (clock-wise) (if stopped_by_left ;; the left-side stopper has just released the wheel. (set stopped_by_left false ;; start moving wheel.release_TH wheel.TH ;; record the start angle. wheel.lock_TH wheel.release_TH+6)) ;; else it must be swinging from left to neutral. (rotating CCW) (if stopped_by_right ;; the right-side stopper has just released the wheel. (set stopped_by_right false ;; start moving wheel.release_TH wheel.TH ;; record the start angle. wheel.lock_TH wheel.release_TH+6)))) (if wheel.TH>wheel.lock_TH ;; just got locked! (progn (if logger.log==2 (console.log wheel.W pendulum.W)) (set wheel.TH wheel.lock_TH) ;; make sure it is set exactly (if wheel.lock_TH>wheel.limit_TH wheel.W=0 ;; do nothing, done rotating! ;; determine who locked (simplification here...) (if degr>0 (set stopped_by_right true ;; the wheel got locked by the right anchor stopped_by_left false) ;; likely redundant (check it) (set stopped_by_left true ;; the wheel got locked by the left side anchor stopped_by_right false)) ;; likely redundant (check it) (wheel_update)))) ;; wheel dynamics update (if stopped_by_left||stopped_by_right ;; do not update a locked wheel. wheel.W=0 ;; else (set wheel.W wheel.W+gear_a*dt wheel.TH (+ wheel.TH wheel.W*dt (* 0.5 gear_a dt dt))) (let ((temp (* pendulum.driveW (Math.abs pendulum.W)))) (if wheel.W>temp ;; wheel drives pendulum (progn (if logger.log==2 (console.log "transfer torque to pendulum")) wheel.W=temp pendulum.W*=1.012))) (wheel_update)) (rAF tick) ;; tick will be passed the curent time from rAF )) (hand_initial_angles \.(let* ( (date (new Date))(hr (date.getHours))(m (date.getMinutes))(s (date.getSeconds))) {hr (* hr%12 360/12) m (* 360/60 m) s (* 360/60 s)}) ) (ha (hand_initial_angles)) (gear (gear.reduce \agi(set (aref a (or g.nm "gear_t{g.teeth}h{g.h}r{g.radius}")) g a a) {})) (ttt (console.log gear)) (gears ;; the set of gears and hands for the clock [ gear.escapement1 {fill #6b8ea6} gear.pinion_t10h7r18 {y 1.03 fill #9ecae1} gear.intermed_t60h7r6x18 {fill #6baed6} gear.pinion_t6h9r15 {y 1.025 fill #c6dbef nm "minutes_gear" } gear.min_gear gear.pinion_t12h8r22 {y 1.04 fill #9ecae1} gear.intermed_t4x12h8r4x22 {fill #6baed6} {teeth 12 radius 5*22/4 h 8 cuts { n 6 rr 0.1 or 0.2 iw 0.3 ow 0.7 rw 0.6} } {y -1.04 fill #c6dbef nm "hours_gear" } gear.hour_gear_t3x12h8 ;; face plate and hands are defined last ONLY because of the SVG layour/zindex reasons. The k of the plate and minutes hand adjusted. {y 0 k 0 nm 'face} ;; k=0 - face must be still gear.face_plate {y 0 k "minutes_gear" nm "minute hand axle" a ha.m+ha.s/60} ;; a sets inital, date-derived angle gear.min_hand ;;{nm "minute" teeth 1 h 200 radius 216 ow 1 pw 1.008 rrw -0.600 iw 0.299 skw 0 off -110 flat -19 axr 9 } ;; iw 0.3 {y 0 k "hours_gear" nm "hour hand axle" a ha.hr+ha.m/12} ;; a sets inital, date-derived angle gear.hour_hand ;;{teeth 1.000 h 35.300 radius 142.044 ow 1.002 pw 1.016 rrw -0.850 iw 0.425 skw 0.000 off -97.054 flat -85.127 axr 2.000 cuts {n 1.000 or 0.578 rw 0.124 ow 0.000 skw 0.000 iw 0.000 s1skw 0.000 s2skw 0.000 rr 0.648 rrf 1.203 orf 0.160 s1r 0.566 s2r 9.836 _ 1877.000 } _ 1877.000 } ;; the seconds hand. it is optional - uncomment this line to see it... {y 0 k "driver" nm "secs hand axle" a ha.s} gear.seconds_hand ] ) (logger { log true last_tick_time 0 degr_max 0 tick_counter 0 degr_max_displayed (rad2degr pendulum.init_swing) swing_msg "" tick_msg "" run \(time degr A) (let* ((logger_time time)) (if degr>0 (set this.degr_max (Math.max degr this.degr_max)) (if this.degr_max>0 this.degr_max_displayed=this.degr_max) (set this.degr_max 0)) (set this.swing_msg (+ "swing: " (format42 this.degr_max_displayed))) (if A>=0&&0>=pendulum.A||0>=A&&pendulum.A>=0 ;; for average tick duration, time with correction for the 1st tick time (let* ((tick_time time-this.last_tick_time) (tick_time_aver logger_time/this.tick_counter)) ++this.tick_counter (if tick_time_aver>1.2*tick_time||0.8*tick_time>=tick_time_aver ;; browser stopped animation in inactive tab/window (set logger_time tick_time*this.tick_counter)) (set this.tick_msg " tick aver: {format42(logger_time/this.tick_counter)} last tick: {format42(tick_time)}" this.last_tick_time time))) (set displ.innerHTML this.swing_msg+this.tick_msg)) }) (update_hands ;; needed because some browsers stop ruuning code in invisible windows \.(let ((ha (hand_initial_angles))) (console.log "page was idle, correcting hands postions...") (movement.forEach \g(if (eq g.nm "minute hand axle") (set g.a ha.m+ha.s/60) (if (eq g.nm "hour hand axle") (set g.a ha.hr+ha.m/12) (if (eq g.nm "secs hand axle") (set g.a ha.s))))))) ) ;; end of let* (pendulum.init) (wheel.init) (. pendulum_g (append 'rect) (attr { x -10 y -10 width 20 height 1000}) (end) (append 'rect) (attr { x -4 y -10 width 8 height 1000}) (style{stroke #772211 'stroke-width "1px" fill #cc5522}) (end) (append 'circle)(style { fill "url(#bob)"}) (attr { cx 0 cy 1000 r 70}) (end) (append 'circle) (attr { cx 0 cy 1000 r 46 }) (style{stroke #331111 'stroke-width "1px" fill #dd7722}) (end) (append 'circle) (attr { cx 0 cy 1000 r 40 }) (style{stroke #331111 'stroke-width "2px" fill #964000}) (end) (append 'path) (attr 'd (let** ((r1 92)(r2 100.6)(r3 18)(a01 47)(a012 12)(adrive 2) (a1 (degr2rad 90-a01))(a2 (degr2rad 90-a01-a012)) (da (degr2rad 2.5)) ;; affects arm 'wrist' width (a3 (degr2rad -90+45))(a4 (degr2rad -90-45)) (a5 (degr2rad 90+a01+a012))(a6 (degr2rad 90+a01+adrive))(a7 (degr2rad 90+a01)) (a8 (degr2rad -90-105))(a9 (degr2rad -90+105)) (a10 (degr2rad 90-a01-adrive)) (M (com2a 'M))(L (com2a 'L)) (A \(r dir a)(+ "A" r "," r " " dir " " (* r (Math.cos a)) "," (* r (Math.sin a)))) ) (+ (M a1 r2) (A r2 "0,0,0" a2)(L a3 r3) ;; right arm, outer (A r3 "0,0,0" a4) ;; top central radius (L a5 r2)(A r2 "0,0,0" a6) ;; right arm, outer (L a7 r1) ;; driving slope, left (A r1 "0,0,1" a5-da) (L a8 r3) (A r3 "0,0,0" a9) ;; bottom center (L a2+da r1) (A r1 "0,0,1" a10) "Z" ;; driving slope, right ) )) (end) (append 'circle) (attr { cx 0 cy 0 r 10 }) (style {stroke #333 'stroke-width '1px}) ) (. weight_g (append 'rect)(style {fill #965 stroke #444 "stroke-width" "0.5px"}) (attr {id 'rope x -1 y -350 width 2 height 350})(end) (append 'rect)(style {fill "url(#weight)" stroke #856300}) (attr {x -15/2 y -15 width 15 height 14})(end) (append 'rect)(style {fill "url(#weight)" stroke #856300}) (attr {x -60/2 y 0 width 60 height 204})) (set movement (gear_train gear_train_frame gears mk_gear)) (movement.forEach \ai(if a.nm (set (aref movement a.nm) a))) (set weight.k movement.minutes_gear.k*100/360) ;; here 100mm is pulley's perimeter (console.log "movement "movement) (.(gear_train_frame.selectAll 'path) (style {fill \d(if d.teeth==1 'black) opacity \d(if d.teeth==1 0)}) ) ;;(.(svg_g.append'rect)(attr {id 'floor x -10000 y weight.y+204+1200 width 20000 height 10}) ) (let* ((floor_y weight.y+weight.hmm+204) (wall {x -3000 y -1000 width 6000 height 1000+floor_y})) (.(svg_g.append'rect)(attr wall)(style {"stroke-width" "10px" fill 'transparent})(end) (append'line)(attr {x1 -10000 x2 -3000 y1 floor_y+2000 y2 floor_y})(end) (append'line)(attr {x1 10000 x2 3000 y1 floor_y+2000 y2 floor_y})(end) (append'line)(attr {x1 -10000 x2 -3000 y1 -4000 y2 -1000})(end) (append'line)(attr {x1 10000 x2 3000 y1 -4000 y2 -1000})) ) (set lastTime (if w.performance&&w.performance.now (w.performance.now) (- (Date.now) start)) logger.last_tick_time lastTime) (var fzoom \di(.(svg_g.transition)(duration 4000)(ease (d3.ease 'linear)) (attr "transform" "scale({d.z})"))) (.(body.insert 'div 'svg) (attr 'class 'example_panel)(selectAll 'a) (data [{z 0.9 txt "zoom closer" f fzoom} {z 2.8 txt "zoom very close" f fzoom} {z 0.5 txt "zoom far" f fzoom} {txt "show face/hands" flg false f \di(set d.flg !d.flg this.innerHTML (if d.flg "hide face" "show face") doit (.(gear_train_frame.selectAll 'path)(transition)(duration 1000) (style {opacity \g(if g.teeth==1 (if d.flg 0.45 0.03))}) ))} {txt "hit pendulum" f \d(set stopped_by_right false stopped_by_left false wheel.lock_TH wheel.limit_TH ;;; 12 hrs of free-wheeling pendulum.driveW 1e8 pendulum.W (+ pendulum.W -10 (* 20 (Math.random))) doit (. pendulum_g (select 'path) (transition)(duration 3000)(ease 'bounce) (attr 'transform "translate(0,930)")))}]) (enter)(append 'a)(text \di.d.txt) (on 'click \d.(d.f d))) ;;(console.log p) ;;(. (d3.select 'body)(style {'background-size "10%" 'background "url(http://previews.123rf.com/images/alexmakarova/alexmakarova1209/alexmakarova120900040/15236036-vector-seamless-vintage-wallpaper-pattern-on-gradient-background-Stock-Vector.jpg)"}))) (tick lastTime))</textarea> <textarea id='pendulum_clock__html'><p id=p1>Hello World</p> <svg> <defs> <linearGradient id="weight" x1="0%" y1="0%" x2="100%" y2="0%"> <stop offset="0%" style="stop-color:#856300;" /> <stop offset="40%" style="stop-color:#f5b800;" /> <stop offset="100%" style="stop-color:#856300;" /> </linearGradient> <radialGradient id="bob" cx="50%" cy="50%" r="50%" fx="50%" fy="50%"> <stop offset="40%" style="stop-color:#222;" /> <stop offset="85%" style="stop-color:#bf5200;" /> <stop offset="100%" style="stop-color:#852600;" /> </radialGradient> </defs> </svg> <p>pendulum clock by dmitrynizh'15</p> <script src=https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.5/d3.min.js></script> </textarea> <textarea id='pendulum_clock__css'>body { color: lightblue; background: #aabbcc; /*url(http://thumbs.dreamstime.com/x/seamless-vintage-wallpaper-pattern-1592949.jpg); background-size: 30%;*/ } p { font-size: 9px; background: #226; margin: 4px 0 4px 0; padding: 2px; } path { fill-rule: evenodd; stroke: #333; stroke-width: 1px; fill: #6baed6; } circle, rect { stroke: #a33; stroke-width: 2px; fill: #bf5200; } line { stroke: #a33; stroke-width: 10px; } .example_panel { cursor: pointer; left: 8px; top: 30px; position: absolute; border: 2px solid black; padding: 3px; opacity: 0.5; } .example_panel:hover { opacity: 1; } .example_panel a { border: 1px solid black; padding: 2px; color: orange; background: #444; display: block; } .example_panel a:hover { border: 1px solid orange; color: white; }</textarea> </pre> <!-- template for new code example --------------------------- <pre> <textarea id=lisp> </textarea> <textarea id='__html'> </textarea> <textarea id='__css'> </textarea> </pre> ------------end of template ------------------------------ --> </div> <!-- end of lisp code examples --> <script src="https://codemirror.net/lib/codemirror.js"></script> <script src="https://codemirror.net/mode/xml/xml.js"></script> <script src="https://codemirror.net/mode/htmlmixed/htmlmixed.js"></script> <script src="https://codemirror.net/mode/css/css.js"></script> <!--<script src="https://codemirror.net/mode/commonlisp/commonlisp.js"></script>--> <script src="https://codemirror.net/mode/javascript/javascript.js"></script> <!--<script src="https://codemirror.net/2/lib/util/formatting.js"></script>--> <script src="https://codemirror.net/addon/edit/matchbrackets.js"></script> <script src="https://jsbeautifier.org/js/lib/beautify.js"></script> <script src="https://ajax.aspnetcdn.com/ajax/jshint/r07/jshint.js"></script> <script src="https://codemirror.net/addon/lint/lint.js"></script> <script src="https://codemirror.net/addon/lint/javascript-lint.js"></script> <script src="https://codemirror.net/addon/hint/show-hint.js"></script> <script src="https://codemirror.net/addon/edit/closebrackets.js"></script> <!-- for search and repace with ctrl-f ctrl-g etc --> <script src="https://codemirror.net/addon/dialog/dialog.js"></script> <script src="https://codemirror.net/addon/search/searchcursor.js"></script> <script src="https://codemirror.net/addon/search/search.js"></script> <script src="https://codemirror.net/addon/scroll/annotatescrollbar.js"></script> <script src="https://codemirror.net/addon/search/matchesonscrollbar.js"></script> <script src="https://codemirror.net/addon/search/jump-to-line.js"></script> <!-- inculded in "Settings" <link rel="stylesheet" href="https://codemirror.net/addon/dialog/dialog.css"> <link rel="stylesheet" href="https://codemirror.net/addon/search/matchesonscrollbar.css"> --> <!--resizeable CM panels, derived from Split.js --> <script src="https://codepen.io/dmitrynizh/pen/GoRbWG.js"></script> <!-- Lisp15 --> <script src="https://codepen.io/dmitrynizh/pen/GogBaw.js"></script> <!-- Lisp15 mode for CodeMirror --> <script src="https://codepen.io/dmitrynizh/pen/zrGZbW.js"></script> <script src='//production-assets.codepen.io/assets/common/stopExecutionOnTimeout-b2a7b3fe212eaa732349046d8416e00a9dec26eb7fd347590fbced3ab38af52e.js'></script><script src='https://stuartpb.github.io/gistachio/gistachio.js'></script> <script >(function() { // get rid of console_runner overwiring stuff.... // delete window.console.log; // this removes just log for (var key in console) {if (window.CP.shouldStopExecution(1)){break;} // all of them if (console.hasOwnProperty(key) && console[key].toString().indexOf("unction ")) { // console.log("deleting console.", key); // delete console[key]; } } window.CP.exitedLoop(1); var location = { hash: "", qs: {}}; // hash string, guery params if (window.location.search) { var qs = window.location.search.substr(1).split('&'); for (var p, i = 0; i < qs.length; i++) {if (window.CP.shouldStopExecution(2)){break;} p = qs[i].split('=', 2); location.qs[p[0]] = (p.length == 1) ? "" : decodeURIComponent(p[1].replace(/\+/g, " ")); } window.CP.exitedLoop(2); } location.hash = window.location.hash.replace("#", ""); //delete window.console.log; //window.console.log(console, "location: ", location);; // Base template var output_page_template = "<!doctype html>\n" + "<html>\n" + " <head>\n" + " <meta charset=\"utf-8\">\n" + " <title>My Web Lisp App</title>\n" + " </head>\n" + " <body>\n" + " </body>\n" + "</html>"; var lisp_panel_title; var mk_tag = function(tag, text, attrs) { attrs = attrs ? (" " + attrs) : ""; return "<" + tag + attrs + ">" + text + "</" + tag + ">"; } var prepareSource = function() { var lisp = lisp_editor.getValue(); var html = html_editor.getValue(); var css = css_editor.getValue(); var js = js_editor.getValue(); var tmp, src = output_page_template; if ((tmp=lisp.match(/\s*;+\s*(\w+.*)\n/))) src = output_page_template = src.replace(/title>(.*)<\/title>/, "title>"+(lisp_panel_title=tmp[1])+"<\/title>"); src = src.replace('</body>', html + '</body>'); css = "<style>" + css + '</style>'; src = src.replace('</head>', css + '</head>'); js = mk_tag('script', js); src = src.replace('</body>', js + '</body>'); //console.log(src); return src; }; var get_elt = function(id) { return (document).getElementById(id); }; var get_text = function(code_elt) { if (code_elt.taName === "textarea") return code_elt.value; return (code_elt.innerText || code_elt.textContent).trim(); }; var render = function() { console.log("render"); var source = prepareSource();; var iframe = document.querySelector('#output iframe'); var section = iframe.parentElement; section.removeChild(iframe); iframe = document.createElement("iframe"); section.appendChild(iframe); iframe_doc = iframe.contentDocument; iframe_doc.open(); iframe_doc.write(source); iframe_doc.close(); }; var html_box = document.querySelector('#html textarea'); var html_editor = CodeMirror.fromTextArea(html_box, { mode: 'text/html', gutter: true, lineNumbers: true, matchBrackets: true //,extraKeys:{"Shift-Tab":autoFormatSelection} }); var css_box = document.querySelector('#css textarea'); var css_editor = CodeMirror.fromTextArea(css_box, { mode: 'css', gutter: true, lineNumbers: true, matchBrackets: true //,extraKeys:{"Shift-Tab":autoFormatSelection} });;; var lisp_box = document.querySelector('#lisp textarea'); var lisp_editor = CodeMirror.fromTextArea(lisp_box, { mode: 'lisp', // commonlisp', gutter: true, lineNumbers: true, matchBrackets: true, autoCloseBrackets: true, extraKeys: { "Alt-F": "findPersistent" // for search extensions , "Ctrl-Space": "autocomplete" // had to extend keymap for # this way. problem is that closebrackets.js // does not allow to have # to double-enter // in predefiend way (such as () etc and as "".... due to the way how // its function handleChar is written. , "'#'": function(cm) { var pos = cm.getCursor(); var line = cm.getLine(pos.line); console.log(line, pos); //todo: maybe more cases.... if (pos.ch == 0 || line.charAt(pos.ch - 1).match(/[\s.(\[]/)) { cm.replaceSelection("##"); cm.setCursor({ line: pos.line, ch: pos.ch + 1 }); } else cm.replaceSelection("#"); } }, undoDepth: 300 //,extraKeys:{"Shift-Tab":autoFormatSelection} }); var js_box = document.querySelector('#js textarea'); var js_editor = CodeMirror.fromTextArea(js_box, { mode: 'javascript' //,gutter: true , lineNumbers: true, matchBrackets: true //,lineWrapping: true , gutters: ["CodeMirror-lint-markers"], lint: true //,extraKeys: {"Ctrl-Space": "autocomplete"} //,extraKeys:{"Shift-Tab":autoFormatSelection} }); Split(['#code_editors', '#output'], { gutterSize: 14, cursor: 'col-resize', onDrag: resize_CM_editors_on_drag, editors: [html_editor, css_editor, lisp_editor, js_editor] }); var all_editors = [html_editor, css_editor, lisp_editor, js_editor]; Split(['#html', '#css', '#lisp', '#js'], { onDrag: resize_CM_editors_on_drag, direction: 'vertical', sizes: [10, 10, 50, 30], gutterSize: 6, editors: all_editors, minSize: 10, cursor: 'row-resize' }); var cms = document.querySelectorAll('.CodeMirror'); for (var i = 0; i < cms.length; i++) {if (window.CP.shouldStopExecution(3)){break;} /*cms[i].style.position = 'absolute'; cms[i].style.top = '0px'; cms[i].style.bottom = '0'; cms[i].style.left = '0'; cms[i].style.right = '0';*/ } window.CP.exitedLoop(3); cms = document.querySelectorAll('.CodeMirror-scroll'); /*for (i = 0; i < cms.length; i++) cms[i].style.height = '50%';*/ // note use_package will make it global global Lisp.range = function(start, stop, step, inclusive) { // these values can be numbers or string holding numbers // console.log("range", start, stop, step); if (stop == null) stop = +start || 0, start = 0; else start = +start||0, stop = +stop; if (inclusive) stop++; step = +step || 1; var length = Math.max(Math.ceil((stop - start) / step), 0); var range = Array(length); for (var idx = 0; idx < length; idx++, start += step) {if (window.CP.shouldStopExecution(4)){break;}range[idx] = start; window.CP.exitedLoop(4); } return range; }; Lisp.use_package("Lisp"); var js_code = []; // macro 'include' needs this var js_out = function(jscode, beautify) { jscode = semicolon_cleanup(jscode); if (!jscode.match(/.+[;\}]\s*$/)) jscode += ";"; if (beautify) jscode = tidyup_js_code(jscode); js_editor.setValue(jscode); return jscode; }; var processToplevels = function(lisp_text, action, end_regexp) { action = action || js_out; end_regexp = end_regexp || /_^/; // default is 'match nothing' var curr = 0; while (!lisp_text.substring(curr).match(end_regexp)) {if (window.CP.shouldStopExecution(5)){break;} LispTrans.resetState(); var res = read_from_string(lisp_text, curr); if (!res) break; curr = res.cdr; action(res.car); } window.CP.exitedLoop(5); }; // Extension: Removal of Option Semicolons var semicolon_cleanup = (function() { LispTrans.setTokens({ //COMMA: ",\n", SEMICOL: ";\n", SEMICOL_RET: " ;\n", RBC_CODE: "}/*{C}*/\n", RBC_OBJ: "}/*{O}*/\n", RBC_FUNC: "}\n/*{F}*/" }); return function(js_code) { // ugly regexp stuff, but fast and helps to keep Jhint happier js_code = js_code.replace(/\n(\/\*[^\*]*\*\/\n);/g, "$1"); // for macros js_code = js_code.replace(/\/\*{F}\*\/;(\w*[^\(])/g, "$1"); // for non application js_code = js_code.replace(/\/\*{[COIW]}\*\/\n*;/g, ""); // for all blocks followed by ; js_code = js_code.replace(/\/\*{[COIWF]}\*\//g, ""); // remove markers return js_code; }; })(); // This loads useful macros extending the basic set provided by Lisp. // Currently, load time is completely negligible, under 10ms. var time = new Date(); processToplevels(get_text(get_elt("extensions")), function(e) { translate(macroexpand(e)); }); console.log("extensions load time, ms:", new Date() - time); LispTrans.setSpecialTerms({"nil":undefined}); //this excludes t:true, use #t or 't or 1 or true or "" // One more useful extension. Example: (include #example15 #example1) LispTrans.global_macros["include"] = LispTrans.global_macros["import"] = function() { // vararg macro! for (var i = 0; i < arguments.length; i++) {if (window.CP.shouldStopExecution(7)){break;} var name = arguments[i]; if (name.constructor === LispString) name = name.text; var node_list = document.querySelectorAll(name), msg = "included:"; for (var j = 0; j < node_list.length; j++) {if (window.CP.shouldStopExecution(6)){break;} var elt = node_list[j]; // console.log(elt); processToplevels(get_text(elt), function(e) { js_code.push(translate(macroexpand(e))); }); msg += " " + elt.id || elt.name || elt.dir || elt.tagName; } window.CP.exitedLoop(6); } window.CP.exitedLoop(7); return "\n/* " + (node_list.length ? msg : "") + " */\n"; }; // Extension: String Interpolation feature (function(prefix) { // $ @ . # or empty Lisp.string_interpolation_enabled = true; // interpolation is new feature in Lisp15 var re = new RegExp("\\" + prefix + "{[^ ;{}]+}", "g"); // alternatives: /\${[^{}]+}/g or /\ LispTrans.setReadStringWrapper(function(str) { if (!Lisp.string_interpolation_enabled) return new LispString(str); var match = str.match(re); if (!match) return new LispString(str); str = str.replace(re, match[0]).split(match[0]); // use it as splitter var plus_args = [new LispString(str[0])]; match.forEach(function(s, i) { plus_args.push(s.slice(1+prefix.length, -1)); plus_args.push(new LispString(str[i+1]))}); plus_args = plus_args.filter(function(e) { return !(e && e.constructor == LispString && e.text == "");}); return cons("+", arr2list(plus_args)); }); })(""); var lisp_to_js = function(lisp_code, beautify) { js_code = []; processToplevels(lisp_code, function(e) { js_code.push(translate(macroexpand(e))) }); js_out(js_code.join(";"), beautify); var totalLines = js_editor.lineCount(); //js_editor.autoFormatRange({line:0, ch:0}, {line:totalLines}); } var tidyup_js_code = function(code) { return js_beautify(code, { indent_size: 2, break_chained_methods: true, wrap_line_length: 40, brace_style: "collapse" }).replace(/ return\n[ ]+/g, // work around horrible bug in js_beautify " return "); }; lisp_editor.on("change", function(cm, change) { //console.log("lisp changed: ", change); for (var i = 5; i>0; i--) {if (window.CP.shouldStopExecution(8)){break;}localStorage["hist" + i] = localStorage["hist" + (i-1)]; window.CP.exitedLoop(8); } var lisp = localStorage["hist0"] = lisp_editor.getValue(); lisp_to_js(lisp); // the following hack prevents losing paren highlighting // in the lisp editor caused by rendering into js editor setTimeout(function() { var cursor = lisp_editor.getCursor() lisp_editor.setCursor({ line: cursor.line, ch: cursor.ch - 1 }); lisp_editor.setCursor(cursor); }, 100);; }); js_editor.on("change", function(cm, change) { //console.log("js changed: ", change); render(); }); get_elt("tidy_js").onclick = function() { var pp_text = tidyup_js_code(js_editor.getValue()); js_editor.setValue(pp_text); }; var popups_zindex = 99; get_elt("Lisp_label").onclick = function() { var popup = get_elt("lisp_history"); for (var i = 5; i>-1; i--) {if (window.CP.shouldStopExecution(9)){break;}get_elt("hist"+i).value = localStorage["hist"+i]; window.CP.exitedLoop(9); } get_elt("hist1").value = localStorage["hist1"]; popup.style.display = "block"; popup.style["z-index"] = popups_zindex++; }; get_elt("lisp_history_close").onclick = function() { var popup = get_elt("lisp_history"); popup.style.display = "none"; }; get_elt("HTML_label").onclick = function() { var popup = get_elt("html_template"); get_elt("html_template_ta").value = output_page_template; popup.style.display = "block"; popup.style["z-index"] = popups_zindex++; }; get_elt("html_template_ok").onclick = function() { output_page_template = get_elt("html_template_ta").value; get_elt("html_template").style.display = "none"; }; get_elt("html_template_cancel").onclick = function() { get_elt("html_template").style.display = "none"; }; function wrap_lisp_code() { return "\n<code style='display:none;'>" + lisp_editor.getValue() + "\n</code>\n"; } get_elt("save_result_html").onclick = function() { var link = document.createElement("a"); link.download = "result.html"; // simple method. problem - sometimes get ^R in JS code. // link.href = "data:text/html," + prepareSource(); // blob method: var blob = new Blob([ prepareSource() ,wrap_lisp_code() ], { type: "octet/stream" }); link.href = window.URL.createObjectURL(blob); link.click(); }; // some additional gist services var your_secure_gist_credentials_api; // see https://developer.github.com/v3/oauth/#web-application-flow var gist_pat = (your_secure_gist_credentials_api && your_secure_gist_credentials_api()) || location.qs.pat || localStorage["pat"] ; // ONLY FOR TESTS! NOT SECURE!! var gist_id; // when both gist id and pat are specified, will 'update' rather than create a new gist. get_elt("save_result_gist").onclick = function() { var files = { 'code.lisp': lisp_editor.getValue() ,'code.html': html_editor.getValue() ,'code.css': css_editor.getValue() ,'index.html': prepareSource() + wrap_lisp_code() }; var ops = { description: lisp_panel_title || "my lisp work"}; if (gist_pat) ops.accessToken = gist_pat; var cb = function(err, gist_info) { console.log("new gist, err:", err, " id: ", gist_info); if (err) ; //alert("console"+ console.constructor + "gist creation error: " + err + " gist_info: " + gist_info); else { if (typeof gist_info === "string") gist_id = gist_info; var popup = get_elt("gist_save_notification"); var link = get_elt("gist_url"); link.innerHTML = link.href = "https://gist.github.com/" + gist_id; link = get_elt("run_gist_from_blocks"); link.href = "https://bl.ocks.org/" + gist_id; get_elt("gist_personal_at_note").style.display = gist_pat ? "none" : "block" ; popup.style.display = "block"; popup.style["z-index"] = popups_zindex++; } }; // end of func cb if (gist_pat && !gist_id) // code snippets from gist storage may contain id; use it. if (gist_id=files["code.lisp"].match(/;;\sgist:(\w+)/)) gist_id = gist_id[1]; if (gist_id) gistachio.patchFiles (gist_id, files, ops, cb); else gistachio.postFiles(files, ops, cb); }; // end of save_result_gist onclick get_elt("gist_dialog_close").onclick = function() { get_elt("gist_save_notification").style.display = "none"; }; (function() { // examples 'rolodeck' with buttons next and prev var load = function(pre_elt) { var lisp_elt = pre_elt.querySelectorAll("textarea:not([id*=__])")[0]; //var example_id = lisp_elt.id; var html_elt = pre_elt.querySelectorAll("textarea[id*=html]")[0] || get_elt("example1__html"); var css_elt = pre_elt.querySelectorAll("textarea[id*=css]")[0] || get_elt("example1__css"); html_editor.setValue(get_text(html_elt)); css_editor.setValue(get_text(css_elt)); lisp_editor.setValue(get_text(lisp_elt)); }; var examples = document.querySelectorAll("#examples pre"); // console.log([examples]); var example_n = 0; var next_example = get_elt("next_example").onclick = function() { var pre_elt = examples[example_n++]; if (!pre_elt) example_n = 1, pre_elt = examples[0]; load(pre_elt); }; get_elt("prev_example").onclick = function() { if (example_n < 2) example_n = examples.length + 1; var pre_elt = examples[--example_n - 1]; load(pre_elt); gist_id = undefined; }; var location_hash = window.location.hash.replace("#", ""); console.log("location_hash", location_hash);;; if (!isNaN(1*location_hash) && (1*location_hash)<1000) next_example(example_n = Number.parseInt(location_hash) - 1); else if (location_hash != "") { // gistid or example name which elt id if (location_hash.length > 16 && !isNaN(parseInt(location_hash,16))) { // gist id gist_id = location_hash; gistachio.getFiles(gist_id, function(err, files) { if (err || !files) console.log("can not read specifyed gist id="+gist_id, err, files); else Object.keys(files).forEach(function(file) { switch (file) { case "code.html": html_editor.setValue(files[file]); break; case "code.css" : css_editor.setValue(files[file]); break; case "code.lisp": lisp_editor.setValue(files[file]); break; } }); }); } else { var txt_a = document.querySelector("#" + location_hash); var pre = txt_a && txt_a.parentElement; for (var i = 0; i < examples.length; i++) {if (window.CP.shouldStopExecution(10)){break;}if (examples[i] == pre) example_n = i; window.CP.exitedLoop(10); } next_example(); } } else { console.log("example_n", example_n);;; next_example(); } })(); // detect css reloading (inspired by codepen, work in progress) var CSSReload = { head: null, init: function() { this._storeHead(), this._listenToPostMessages() }, _storeHead: function() { this.head = document.head || document.getElementsByTagName("head")[0] }, _listenToPostMessages: function() { var e = this; window[this._eventMethod()](this._messageEvent(), function(t) { try { var s = JSON.parse(t.data); "string" == typeof s.css && e._refreshCSS(s) } catch (n) {} }, !1) }, _messageEvent: function() { return "attachEvent" === this._eventMethod() ? "onmessage" : "message" }, _eventMethod: function() { return window.addEventListener ? "addEventListener" : "attachEvent" }, _refreshCSS: function(e) { var t = this._findPrevCPStyle(), s = document.createElement("style"); s.type = "text/css", s.className = "cp-pen-styles", s.styleSheet ? s.styleSheet.cssText = e.css : s.appendChild(document.createTextNode(e.css)), this.head.appendChild(s), t && t.parentNode.removeChild(t), "prefixfree" === e.css_prefix && StyleFix.process() }, _findPrevCPStyle: function() { for (var e = document.getElementsByTagName("style"), t = e.length - 1; t >= 0; t--) {if (window.CP.shouldStopExecution(11)){break;}if ("cp-pen-styles" === e[t].className) return e[t]; window.CP.exitedLoop(11); } return !1 } }; // CSSReload.init(); window.onkeydown = function(e) { var e = e || event; if (e.ctrlKey && !e.shiftKey && e.keyCode == 82) { console.log("got Ctrl-R in LispPlayground, deflect!!!"); e.preventDefault(); e.stopPropagation(); return; } } window.onresize = function() { //console.log("window.onresize"); resize_CM_editors_on_drag(all_editors); }; window.onresize(); // force scrollbars to render on startup // if hash location specified, jump to the given example. }()); // for debugging only function output_window() { return document.querySelector('#output iframe').contentWindow; } //# sourceURL=pen.js </script> </body></html>

Related: See More


Questions / Comments: