let my={} function init(){let version='0.84' my.wd=300 my.ht=my.wd my.sz=250 my.alps=[0.1,0.25,0.5,0.75,1] my.thks=[1,3,6,9,18,40] my.clrs=[['Black','#000000'],['White','#ffffff'],['Blue','#0000FF'],['Red','#FF0000'],['Green','#00cc00'],['Violet','#EE82EE'],['Orange','#FFA500'],['Light Salmon','#FFA07A'],['Slate Blue','#6A5ACD'],['Yellow','#FFFF00'],['Aquamarine','#7FFFD4'],['Pink','#FFC0CB'],['Coral','#FF7F50'],['Lime','#00FF00'],['Pale Green','#98FB98'],['Spring Green','#00FF7F'],['Teal','#008080'],['Hot Pink','#FF69B4'],['Aqua','#00ffff'],['Gold','#ffd700'],['Khaki','#F0E68C'],['Thistle','#D8BFD8'],['Med Purple','#aa00aa'],['Light Blue','#ADD8E6'],['Sky Blue','#87CEEB'],['Navy','#000080'],['Purple','#800080'],['Wheat','#F5DEB3'],['Tan','#D2B48C'],['Antique White','#FAEBD7'],['Silver','#C0C0C0'],] my.presets=[['Doubled',[20,2],[40,-2],[60,2],[80,3]],['Yarn',[61,1],[122,-1.23],[0,0],[0,0]],['Cardioid',[120,1],[60,2],[0,0],[0,0]],['Astroid',[90,1],[30,-3],[0,0],[0,0]],['4 Petals',[90,1],[90,-3],[0,0],[0,0]],['Straight Line',[90,1],[90,-1],[0,0],[0,0]],['Ellipse',[30,1],[90,-1],[0,0],[0,0]],['Square-ish',[15,3],[101,-1],[0,0],[0,0]],['Spirals',[65,6],[60,6.5],[0,0],[0,0]],] my.instr='The values are [radius, radiusAdd, radiusMultiply, speed, speedAdd, speedMultiply] for each circle.' my.txtClr='darkblue' my.pats=[new Pat('lines','#000000',2,5,140,150),new Pat('lines','#000000',2,5,160,150)] my.pat=0 my.styles=['lines','grid','circles','radials'] let lt=362 my.bgClr='#def' let s='' s+='
' s+='' s+='' s+='
' let lblStyle='display: inline-block; font:15px Arial; width: 80px; text-align: right; margin-right:10px;' let txtStyle='display: inline-block; width:50px; font: 18px Arial; color: #6600cc; text-align: left;' s+='
' let temp=[] for(let i=0;iThickness:
' s+='' s+='
2
' s+='
' s+='
Space:
' s+='' s+='
2
' s+='
' s+='
' s+='
Alpha:
' s+='' s+='
2
' s+='
' s+='
' s+='
Color:
' s+=getClrHTML() s+='
' s+='
Speed:
' s+='' s+='
1
' s+='
' s+='
' s+='' s+='
' s+='
' s+=my.instr s+='
' s+='
' s+='' s+='' s+='
' s+='
' s+='
' s+='
' s+=getPlayHTML(36) s+='
' s+='' s+='' s+=wrap({cls:'copyrt',pos:'abs',style:'left:5px; bottom:3px'},`© 2022 Rod Pierce v${version}`) s+='' docInsert(s) my.can=new Can('gfx1',my.wd,my.ht,2) my.can2=new Can('gfx2',my.wd,my.ht,2) chgClr(0) my.mid={x:my.wd/2,y:my.ht/2,} my.prev={} my.props=[['radius','rad'],['speed','w'],] document.getElementById('pat'+0).checked=true setPat(0) my.speed=document.getElementById('r5').value document.getElementById('speed').innerHTML=my.speed my.playQ=false togglePlay()} function Pat(style,clr,thk,spc,x,y){this.style=style this.clr=clr this.thk=thk this.spc=spc this.alp=1 this.ang=0 this.x=x this.y=y} function getRadioHTML(prompt,id,lbls,func){let s='' s+='
' s+=prompt+':' for(let i=0;i' s+=''} s+='
' return s} function chgPat(n){console.log('chgPat='+n) my.pat=n setPat(my.pat) drawCircs()} function setPat(n){console.log('setPat',my.pats[n],my.pats[n].style) document.getElementById('r1').value=my.pats[n].thk document.getElementById('thk').innerHTML=document.getElementById('r1').value document.getElementById('r2').value=my.pats[n].spc document.getElementById('spc').innerHTML=document.getElementById('r2').value document.getElementById('r3').value=my.pats[n].alpha document.getElementById('alp').innerHTML=document.getElementById('r3').value let styleNo=my.styles.indexOf(my.pats[n].style) document.getElementById('style'+styleNo).checked=true let clrNo=my.clrs.map(function(e){return e[1]}).indexOf(my.pats[n].clr) console.log('clrNo',clrNo) radioPress(my.clrs,'clr',clrNo)} function chgStyle(n){console.log('chgStyle='+n) my.pats[my.pat].style=my.styles[n]} function onSizeChg(n,v){console.log('onSizeChg='+n,v,my.pat,my.pats) v=Number(v) if(v<0.1)v=0.1 my.pats[my.pat].thk=v drawCircs() document.getElementById('thk').innerHTML=v} function onSpcChg(n,v){console.log('onSpcChg='+n,v,my.pat) v=Number(v) if(v<0.1)v=0.1 my.pats[my.pat].spc=v drawCircs() document.getElementById('spc').innerHTML=v} function onAlpChg(n,v){console.log('onAlpChg='+n,v,my.pat) v=Number(v) my.pats[my.pat].alp=v drawCircs() document.getElementById('alp').innerHTML=v} function onSpeedChg(n,v){console.log('onSpeedChg='+n,v,my.pat) v=Number(v) my.speed=v drawCircs() document.getElementById('speed').innerHTML=v} function anim(){if(my.playQ){drawCircs() requestAnimationFrame(anim)}} function drawCircs(){my.can.clear() my.can2.clear() let m0=my.pats[0] let m1=my.pats[1] m0.ang+=my.speed*0.0005 m1.ang-=my.speed*0.0005 drawWheel(my.can.g,m0) drawWheel(my.can2.g,m1)} function drawWheel(g,moire){let style=moire.style let thick=moire.thk let space=moire.spc let rot=moire.ang g.strokeStyle=moire.clr g.lineWidth=thick let both=thick+space if(both<0.2)return let lineCount=my.sz/both switch(style){case 'circles':drawCircles(g,moire.x,moire.y,both,lineCount/2) break case 'lines':drawParaLines(g,moire.x,moire.y,my.sz,rot,both,lineCount) break case 'grid':drawParaLines(g,moire.x,moire.y,my.sz,rot,both,lineCount) drawParaLines(g,moire.x,moire.y,my.sz,-Math.PI/2+rot,both,lineCount) break case 'radials':drawRadials(g,moire.x,moire.y,rot,both) break default:}} function drawCircles(g,x,y,space,lineCount){for(let i=0;i'+clr[0]+''} s+='' return s} function getDropdownHTML(opts,funcName,id){let s='' s+='' return s} function getPlayHTML(w){let s='' s+='' s+='' return s} function togglePlay(){let btn='playBtn' if(my.playQ){my.playQ=false document.getElementById(btn).classList.add('play') document.getElementById(btn).classList.remove('pause')}else{my.playQ=true document.getElementById(btn).classList.add('pause') document.getElementById(btn).classList.remove('play') anim()} if(my.colNo") win.document.location='#'} function canvasPrint(){let can=canvasAdd(document.getElementById('gfx1'),document.getElementById('gfx2')) let dataUrl=can.toDataURL('image/png') let win=window.open() win.document.write("") win.document.location='#' win.setTimeout(function(){win.focus() win.print()},500)} function Clr(rr,gg,bb){this.clrs=[rr<<0,gg<<0,bb<<0] this.dirs=[1,1,1]} Clr.prototype.rand=function(){for(let i=0;i<3;i++){this.clrs[i]=(Math.random()*256)<<0}} Clr.prototype.getHex=function(){let s='#'+hex2(this.clrs[0])+hex2(this.clrs[1])+hex2(this.clrs[2]) return s} Clr.prototype.setHex=function(clr){let result=/^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(clr) this.clrs=result?[parseInt(result[1],16),parseInt(result[2],16),parseInt(result[3],16)]:null} Clr.prototype.randChg=function(){for(let i=0;i<3;i++){if(Math.random()<0.01)this.dirs[i]*=-1 if(this.clrs[i]>=255)this.dirs[i]=-1 if(this.clrs[i]<=0)this.dirs[i]=1 this.clrs[i]+=this.dirs[i]}} function hex2(n){let s=n.toString(16) if(s.length==1)s='0'+s return s} function Pt(ix,iy){this.x=ix this.y=iy this.rad=9 this.color='lightblue'} function docInsert(s){let div=document.createElement('div') div.innerHTML=s let script=document.currentScript script.parentElement.insertBefore(div,script)} class Can{constructor(id,wd,ht,ratio){this.id=id this.ratio=ratio this.el=document.getElementById(id) this.g=this.el.getContext('2d') this.resize(wd,ht) return this} resize(wd,ht){this.wd=wd this.ht=ht this.el.width=wd*this.ratio this.el.style.width=wd+'px' this.el.height=ht*this.ratio this.el.style.height=ht+'px' this.g.setTransform(this.ratio,0,0,this.ratio,0,0)} clear(){this.g.clearRect(0,0,this.wd,this.ht)} mousePos(ev){let bRect=this.el.getBoundingClientRect() let mouseX=(ev.clientX-bRect.left)*(this.el.width/this.ratio/bRect.width) let mouseY=(ev.clientY-bRect.top)*(this.el.height/this.ratio/bRect.height) return[mouseX,mouseY]}} function wrap({id='',cls='',pos='rel',style='',txt='',tag='div',lbl='',fn='',opts=[]},...mores){let s='' s+='\n' txt+=mores.join('') s+={btn:()=>{if(cls.length==0)cls='btn' return '