Symphony No. 6.28 in φ minor. An ode to magic numbers.
Log in to post a comment.
// LL 2021 const systems = 5; // min=1 max=20 step=1 const perspective = 0.15; // min=0 max=1 step=0.01 const curved = 1; // min=0 max=1 step=1 (No, Yes) Canvas.setpenopacity(1); class Page { constructor(x, y, w, h, systems) { this.x = x; this.y = y; this.w = w; this.h = h; this.systems = []; const margin_top = h / 8; const margin_w = w / 10; const margin_h = h / systems / 3; const sx = x + margin_w; const sw = w - margin_w * 2; const sh = (h - margin_top - margin_h * systems) / systems; for (var i=0; i<systems; i++) { const sy = y + margin_top + i * (sh + margin_h); this.systems.push(new System(sx, sy, sw, sh)); } const ty = y + margin_top / 4; const th = margin_top / 2; this.title = new Title(sx, ty, sw, th); } draw() { turtle.jump(this.x, this.y); turtle.goto(this.x + this.w, this.y); turtle.goto(this.x + this.w, this.y + this.h); turtle.goto(this.x, this.y + this.h); turtle.goto(this.x, this.y); const pages = 5; const offset_x = 4/pages, offset_y = 6/pages; const offset_xl = (this.x < 0) ? offset_x : 0; const offset_xr = (this.x >= 0) ? -offset_x : 0; const offset_yl = (this.x < 0) ? offset_y : 0; const offset_yr = (this.x >= 0) ? offset_y : 0; for (var i=1; i<pages; i++) { turtle.jump(this.x + offset_xl * i, this.y + this.h + offset_yl * (i-1)); turtle.goto(this.x + offset_xl * i, this.y + this.h + offset_yl * i); turtle.goto(this.x + this.w + offset_xr * i, this.y + this.h + offset_yr * i); turtle.goto(this.x + this.w + offset_xr * i, this.y + this.h + offset_yr * (i-1)); } this.title.draw(); this.systems.forEach(s => s.draw()); this.systems[0].drawStart(); this.systems[this.systems.length-1].drawEnd(); } } class System { constructor(x, y, w, h) { this.x = x; this.y = y; this.w = w; this.h = h; this.clefs = []; this.clefs.push(new Clef("G", x + 2, y - h/10 , h/2)) this.clefs.push(new Clef("F", x + 2, y + h/1.8, h/2)) this.notes = []; for (var l=0; l<2; l++) { var nx = this.x + this.h; const ny = this.y + l * this.h * 0.7; const ns = this.h / 20; const ox = ns * 4; for (var i=0; i<50; i++) { const pitches = []; const count = 1 + ((Math.random() * 4) | 0); var min = 3, max = 3; for (var c=0; c<count; c++) { if ((nx + (pitches.length+2) * ns * 3) > (this.x + this.w)) break; var pitch = 100; while (Math.abs(pitch-min) > 4 || Math.abs(pitch-max) > 4) { pitch = ((Math.random() * 11) | 0) - 2; } min = Math.min(min, pitch); max = Math.min(max, pitch); pitches.push(pitch); } if (pitches.length < 1) break; //if ((nx + (pitches.length) * ns * 3) > (this.x + this.w)) break; this.notes.push(new Note(nx, ny, ns, pitches)); nx += ox * pitches.length; } } } draw() { const margin = this.h / 3; const offset_y = (this.h - margin) / 9; for (var i=0; i<5; i++) { turtle.jump(this.x, this.y + offset_y*i); turtle.goto(this.x + this.w, this.y + offset_y*i); } for (var i=0; i<5; i++) { turtle.jump(this.x, this.y + offset_y*(i+5) + margin); turtle.goto(this.x + this.w, this.y + offset_y*(i+5) + margin); } const count = 4; const offset_x = this.w / (count-1); for (var i=0; i<count; i++) { turtle.jump(this.x + offset_x*i, this.y); turtle.goto(this.x + offset_x*i, this.y + this.h); } this.clefs.forEach(c => c.draw()); this.notes.forEach(n => n.draw()); } drawStart(end=false) { const count1 = 5, count2 = 4, count3 = 2; const offset_x = 0.125; for (var i=0; i<count1+count2+count3; i++) { if (i > count1 && i < count1+count2) continue; turtle.jump(this.x + (end ? (this.w - offset_x*i) : (offset_x*i)), this.y); turtle.goto(this.x + (end ? (this.w - offset_x*i) : (offset_x*i)), this.y + this.h); } } drawEnd() { this.drawStart(true); } // Clear as mud } class Note { constructor(x, y, scale, pitches) { this.x = x; this.y = y; this.scale = scale; this.pitches = pitches; } draw() { var max = Math.max(...this.pitches); var min = Math.min(...this.pitches); const middle = 3; const dir = (max > middle) ? -1 : 1; const line_y = this.y + (((dir > 0) ? (max + 4) : (max - 4)) + 0) * this.scale * dir; const line_x = []; for (var i=0; i<this.pitches.length; i++) { const cx = this.x + i * this.scale * 3; const cy = this.y + this.pitches[i] * this.scale; turtle.jump(cx, cy); for (var a = 0, r=0; r < this.scale; r += 0.0025, a += 0.1) { const bx = cx + Math.cos(a) * r; const by = cy + Math.sin(a) * r; turtle.goto(bx, by); } turtle.goto(cx + this.scale * -dir, cy); turtle.goto(cx + this.scale * -dir, line_y); line_x.push(cx + this.scale * -dir); } const line_count = Math.random() * 2; for (var i=0; i<line_count; i++) { turtle.up(); line_x.forEach(lx => { turtle.goto(lx, line_y - i * this.scale * dir * 0.5); turtle.down(); }); } } } class Clef { constructor(type, x, y, h) { this.x = x; this.y = y; this.h = h; this.points = getPointsForClef(type); } draw() { // TODO: Draw as curves const scale = this.h / 10; const od = 0.2; for (var o=0; o<2; o++) { for (var i=0; i<this.points.length-1; i++) { const x1 = this.x + this.points[i][0] * scale + o * od; const y1 = this.y + this.h - this.points[i][1] * scale; const x2 = this.x + this.points[i+1][0] * scale + o * od; const y2 = this.y + this.h - this.points[i+1][1] * scale; turtle.jump(x1, y1); turtle.goto(x2, y2); } } } } function getPointsForClef(type) { switch (type) { case "F": return [ [5,5],[6,4],[5,3],[4,5],[5,6],[7,5],[5,2],[4,2] ]; case "G": return [ [5,5],[4,6],[5,6],[6,5],[4,4],[2,5],[5,6],[6,7],[4,8],[4,3],[4,2],[3,3],[4,4] ]; } return []; } class Title { constructor(x, y, w, h) { this.x = x; this.y = y; this.w = w; this.h = h; this.generate(); } draw() { const size_f = this.h / 90; const size_w = size_f * 8; turtle.jump(this.x + this.w / 2 - this.text.length * size_w, this.y + this.h / 2); text.print(turtle, this.text, size_f); } generate() { const articles = [ "A", "The" ]; const adjectives = [ "Red", "Grand", "Little", "Wee", "Brilliant", "Velvet", "Round", "Shiny", "Black", "Gyrating", "Quiet", "Magnificent" ]; const nouns = [ "Boat", "House", "Forest", "River", "Sea", "Turtle", "Teacup", "Castle" ]; const prepositions = [ "In", "Out Of", "Through", "Over", "Underneath", "Behind", "Around" ]; var good_enough = false; while (!good_enough) { const words = []; words.push(articles[(Math.random() * articles.length)|0]) words.push(adjectives[(Math.random() * adjectives.length)|0]) words.push(nouns[(Math.random() * nouns.length)|0]) if (Math.random() > 0.3) { words.push(prepositions[(Math.random() * prepositions.length)|0]) words.push(articles[(Math.random() * articles.length)|0]) words.push(adjectives[(Math.random() * adjectives.length)|0]) words.push(nouns[(Math.random() * nouns.length)|0]) } for (var i=0; i<words.length-1; i++) { if (words[i] == "A" && [ "A", "E", "I", "O", "U", "Y" ].includes(words[i+1][0])) { words[i] += 'n'; } } good_enough = true; for (var i=0; i<words.length; i++) { for (var j=i+1; j<words.length; j++) { if (words[i] === words[j]) good_enough = false; } } if (good_enough) this.text = words.join(' '); } } } class PageTurtle extends Turtle { goto(x, y) { const dest_x = (y === undefined) ? x[0] : x; const dest_y = (y === undefined) ? x[1] : y; const start_x = (this.last_x === undefined) ? 0 : this.last_x; const start_y = (this.last_y === undefined) ? 0 : this.last_y; const distance = /*Math.abs(x-sx);*/ Math.hypot(dest_x - start_x, dest_y - start_y); var segments = 1; const dd = 0.25; while (distance / segments > dd) segments++; for (var t=0; t<=segments; t++) { const tx = start_x + (dest_x - start_x) * t / segments; const ty = start_y + (dest_y - start_y) * t / segments; super.goto(this.transform(tx, ty, t / segments)); } this.last_x = dest_x; this.last_y = dest_y; } x() { return (this.last_x === undefined) ? super.x() : this.last_x; } y() { return (this.last_y === undefined) ? super.y() : this.last_y; } transform(x, y) { const op = 1 - perspective; const px = x * (op + (y+100) / 200 * perspective); const py = (((y + Math.sin(Math.abs(px) / -17) * 5) - 100) * op) + (100 * op); return [px, py]; } } const text = new Text(); const turtle = curved ? new PageTurtle() : new Turtle(); new Page( 0, -80, 90, 160, systems).draw(); new Page(-90, -80, 90, 160, systems).draw(); //////////////////////////////////////////////////////////////// // Text utility code. Created by Reinder Nijhoff 2019 // https://turtletoy.net/turtle/1713ddbe99 //////////////////////////////////////////////////////////////// function Text(){const s="br>eoj^jl<jqirjskrjq>brf^fe<n^ne>`ukZdz<qZjz<dgrg<cmqm>`thZhw<lZlw<qao_l^h^e_caccdeefggmiojpkqmqporlshsercp>^vs^as<f^h`hbgdeeceacaab_d^f^h_k`n`q_s^<olmmlolqnspsrrspsnqlol>]wtgtfsereqfphnmlpjrhsdsbraq`o`makbjifjekckaj_h^f_eaecffhimporqssstrtq>eoj`i_j^k_kajcid>cqnZl\\j_hcghglhqjulxnz>cqfZh\\j_lcmhmllqjuhxfz>brjdjp<egom<ogem>]wjajs<ajsj>fnkojpiojnkokqis>]wajsj>fnjniojpkojn>_usZaz>`ti^f_dbcgcjdofrisksnrpoqjqgpbn_k^i^>`tfbhak^ks>`tdcdbe`f_h^l^n_o`pbpdofmicsqs>`te^p^jfmfogphqkqmppnrkshserdqco>`tm^clrl<m^ms>`to^e^dgefhekenfphqkqmppnrkshserdqco>`tpao_l^j^g_ebdgdlepgrjsksnrppqmqlpingkfjfggeidl>`tq^gs<c^q^>`th^e_dadceegfkgnhpjqlqopqorlshserdqcocldjfhigmfoepcpao_l^h^>`tpeohmjjkikfjdhcecddaf_i^j^m_oapepjoomrjshserdp>fnjgihjikhjg<jniojpkojn>fnjgihjikhjg<kojpiojnkokqis>^vrabjrs>]wagsg<amsm>^vbarjbs>asdcdbe`f_h^l^n_o`pbpdofngjijl<jqirjskrjq>]xofndlcicgdfeehekfmhnknmmnk<icgefhfkgmhn<ocnknmpnrntluiugtdsbq`o_l^i^f_d`bbad`g`jambodqfrislsorqqrp<pcokompn>asj^bs<j^rs<elol>_tc^cs<c^l^o_p`qbqdpfoglh<chlhoipjqlqopqorlscs>`urcqao_m^i^g_eadccfckdnepgrismsorqprn>_tc^cs<c^j^m_oapcqfqkpnopmrjscs>`sd^ds<d^q^<dhlh<dsqs>`rd^ds<d^q^<dhlh>`urcqao_m^i^g_eadccfckdnepgrismsorqprnrk<mkrk>_uc^cs<q^qs<chqh>fnj^js>brn^nnmqlrjshsfreqdndl>_tc^cs<q^cl<hgqs>`qd^ds<dsps>^vb^bs<b^js<r^js<r^rs>_uc^cs<c^qs<q^qs>_uh^f_daccbfbkcndpfrhslsnrppqnrkrfqcpan_l^h^>_tc^cs<c^l^o_p`qbqepgohlici>_uh^f_daccbfbkcndpfrhslsnrppqnrkrfqcpan_l^h^<koqu>_tc^cs<c^l^o_p`qbqdpfoglhch<jhqs>`tqao_l^h^e_caccdeefggmiojpkqmqporlshsercp>brj^js<c^q^>_uc^cmdpfrisksnrppqmq^>asb^js<r^js>^v`^es<j^es<j^os<t^os>`tc^qs<q^cs>asb^jhjs<r^jh>`tq^cs<c^q^<csqs>cqgZgz<hZhz<gZnZ<gznz>cqc^qv>cqlZlz<mZmz<fZmZ<fzmz>brj\\bj<j\\rj>asazsz>fnkcieigjhkgjfig>atpeps<phnfleiegfehdkdmepgrislsnrpp>`sd^ds<dhffhekemfohpkpmopmrkshsfrdp>asphnfleiegfehdkdmepgrislsnrpp>atp^ps<phnfleiegfehdkdmepgrislsnrpp>asdkpkpiognfleiegfehdkdmepgrislsnrpp>eqo^m^k_jbjs<gene>atpepuoxnylzizgy<phnfleiegfehdkdmepgrislsnrpp>ate^es<eihfjemeofpips>fni^j_k^j]i^<jejs>eoj^k_l^k]j^<kekvjyhzfz>are^es<oeeo<ikps>fnj^js>[y_e_s<_ibfdegeifjijs<jimfoeretfuius>ateees<eihfjemeofpips>atiegfehdkdmepgrislsnrppqmqkphnfleie>`sdedz<dhffhekemfohpkpmopmrkshsfrdp>atpepz<phnfleiegfehdkdmepgrislsnrpp>cpgegs<gkhhjfleoe>bsphofleieffehfjhkmlompopporlsisfrep>eqj^jokrmsos<gene>ateeeofrhsksmrpo<peps>brdejs<pejs>_ubefs<jefs<jens<rens>bseeps<pees>brdejs<pejshwfydzcz>bspees<eepe<esps>cqlZj[i\\h^h`ibjckekgii<j[i]i_jakbldlfkhgjkllnlpkrjsiuiwjy<ikkmkojqirhthvixjylz>fnjZjz>cqhZj[k\\l^l`kbjcieigki<j[k]k_jaibhdhfihmjilhnhpirjskukwjy<kkimiojqkrltlvkxjyhz>^vamakbhdgfghhlknlplrksi<akbidhfhhillnmpmrlsisg>brb^bscsc^d^dsese^f^fsgsg^h^hsisi^j^jsksk^l^lsmsm^n^nsoso^p^psqsq^r^rs".split(">").map(s=>[s.charCodeAt(0)-106,s.charCodeAt(1)-106,s.substr(2).split("<").map(s=>{const e=[];for(let p=0;p<s.length;p+=2)e.push(s.substr(p,2).split("").map(s=>s.charCodeAt(0)-106));return e})]);return new class{print(e,p,j=1,h=0,r=1){e.radians();let f=[e.x(),e.y()],o=e.h(),i=f;p.split("").map(p=>{const c=p.charCodeAt(0)-32;if(c<0)f=i=this.rotAdd([0,48*j],i,o);else if(c>96)f=this.rotAdd([16*j,0],i,o);else{const p=s[c],i=p[0]*j,d=p[1]*j;p[2].map(s=>{e.up(),s.map(s=>{e.goto(this.rotAdd([(s[0]-s[1]*h)*j-i,s[1]*j],f,o)),e.down()})}),f=this.rotAdd([(d-i)*r,0],f,o)}})}rotAdd(s,e,p){return[Math.cos(p)*s[0]-Math.sin(p)*s[1]+e[0],Math.cos(p)*s[1]+Math.sin(p)*s[0]+e[1]]}}}