new file: Files/flashplayer_32_sa.exe new file: favicon.ico new file: globe.gif new file: imgs/download.png new file: imgs/zuck.jpg new file: index.html new file: other.ico new file: script.js new file: site.webmanifest new file: sitemap.html new file: styles/backround.css new file: styles/border.css new file: styles/fonts/Titillium_Web/OFL.txt new file: styles/fonts/Titillium_Web/TitilliumWeb-Black.ttf new file: styles/fonts/Titillium_Web/TitilliumWeb-Bold.ttf new file: styles/fonts/Titillium_Web/TitilliumWeb-BoldItalic.ttf new file: styles/fonts/Titillium_Web/TitilliumWeb-ExtraLight.ttf new file: styles/fonts/Titillium_Web/TitilliumWeb-ExtraLightItalic.ttf new file: styles/fonts/Titillium_Web/TitilliumWeb-Italic.ttf new file: styles/fonts/Titillium_Web/TitilliumWeb-Light.ttf new file: styles/fonts/Titillium_Web/TitilliumWeb-LightItalic.ttf new file: styles/fonts/Titillium_Web/TitilliumWeb-Regular.ttf new file: styles/fonts/Titillium_Web/TitilliumWeb-SemiBold.ttf new file: styles/fonts/Titillium_Web/TitilliumWeb-SemiBoldItalic.ttf new file: styles/fonts/webfontkit-20221027-163353/generator_config.txt new file: styles/fonts/webfontkit-20221027-163353/specimen_files/grid_12-825-55-15.css new file: styles/fonts/webfontkit-20221027-163353/specimen_files/specimen_stylesheet.css new file: styles/fonts/webfontkit-20221027-163353/stylesheet.css new file: styles/fonts/webfontkit-20221027-163353/titilliumweb-extralight-demo.html new file: styles/fonts/webfontkit-20221027-163353/titilliumweb-extralight-webfont.woff new file: styles/fonts/webfontkit-20221027-163353/titilliumweb-extralight-webfont.woff2 new file: styles/fonts/webfontkit-20221027-165950/generator_config.txt new file: styles/fonts/webfontkit-20221027-165950/specimen_files/grid_12-825-55-15.css new file: styles/fonts/webfontkit-20221027-165950/specimen_files/specimen_stylesheet.css new file: styles/fonts/webfontkit-20221027-165950/stylesheet.css new file: styles/fonts/webfontkit-20221027-165950/titilliumweb-bold-demo.html new file: styles/fonts/webfontkit-20221027-165950/titilliumweb-bold-webfont.woff new file: styles/fonts/webfontkit-20221027-165950/titilliumweb-bold-webfont.woff2 new file: styles/style.css new file: tools/2048/.gitignore new file: tools/2048/.jshintrc new file: tools/2048/CONTRIBUTING.md new file: tools/2048/LICENSE.txt new file: tools/2048/README.md new file: tools/2048/Rakefile new file: tools/2048/favicon.ico new file: tools/2048/index.html new file: tools/2048/js/animframe_polyfill.js new file: tools/2048/js/application.js new file: tools/2048/js/bind_polyfill.js new file: tools/2048/js/classlist_polyfill.js new file: tools/2048/js/game_manager.js new file: tools/2048/js/grid.js new file: tools/2048/js/html_actuator.js new file: tools/2048/js/keyboard_input_manager.js new file: tools/2048/js/local_storage_manager.js new file: tools/2048/js/tile.js new file: tools/2048/meta/apple-touch-icon.png new file: tools/webretro/cores/neocd_libretro.js new file: tools/webretro/cores/neocd_libretro.wasm new file: tools/webretro/cores/nestopia_libretro.js new file: tools/webretro/cores/nestopia_libretro.wasm new file: tools/webretro/cores/o2em_libretro.js new file: tools/webretro/cores/o2em_libretro.wasm new file: tools/webretro/cores/opera_libretro.js new file: tools/webretro/cores/opera_libretro.wasm
526 lines
22 KiB
JavaScript
526 lines
22 KiB
JavaScript
let s,i,my={}
|
|
function init(imode){my.version='0.891'
|
|
my.mode=typeof imode!=='undefined'?imode:'equn'
|
|
my.modes=[]
|
|
my.modes['equn']={extendQ:true,slopeQ:false}
|
|
my.modes['slope']={extendQ:true,slopeQ:true}
|
|
my.modes['intery']={extendQ:true,slopeQ:false}
|
|
my.modes['pt']={extendQ:true,slopeQ:false}
|
|
my.modes['mid']={extendQ:false,slopeQ:false}
|
|
canvasid='canvas4'
|
|
titleid='title4'
|
|
infoid='info4'
|
|
dragging=false
|
|
w=540
|
|
h=360
|
|
let s=''
|
|
s+='<style>'
|
|
s+='.togglebtn { display: inline-block; position: relative; text-align: center; margin: 2px; text-decoration: none; font: bold 14px/25px Arial, sans-serif; color: #19667d; border: 1px solid #88aaff; border-radius: 10px; cursor: pointer; background: linear-gradient(to top right, rgba(170,190,255,1) 0%, rgba(255,255,255,1) 100%); }'
|
|
s+='.togglebtn:hover { background: linear-gradient(to top, rgba(255,255,0,1) 0%, rgba(255,255,255,1) 100%); }'
|
|
s+='.hi { border: solid 2px #eeeeaa; background: linear-gradient(to top, rgba(130,220,255,1) 0%, rgba(255,255,255,1) 100%); }'
|
|
s+='.lo { border: solid 1px #888888; background: linear-gradient(to top, rgba(170,170,170,1) 0%, rgba(205,205,205,1) 100%); }'
|
|
s+='</style>'
|
|
s+='<div style="position:relative; width:'+w+'px; height:'+h+'px; border: none; border-radius: 9px; margin:auto; display:block; box-shadow: 0px 0px 19px 10px rgba(0,0,68,0.46); ">'
|
|
s+='<canvas id="'+canvasid+'" width="'+w+'" height="'+h+'" style="z-index:10;"></canvas>'
|
|
s+='<div id="descr" style="position:absolute; left:0px; top:10px; width:'+w+'px; font: bold 30px Arial; text-align: center;"></div>'
|
|
s+='<div id="btns2" style="position:absolute; right:3px; bottom:5px;">'
|
|
if(my.mode=='pt'){my.slope=0.5
|
|
s+='<span style="font: bold 20px arial;">Slope:</span>'
|
|
s+='<input type="range" id="r1" value="'+my.slope+'" min="-4" max="4" step="0.1" style="z-index:2; width:200px; height:10px; border: none; " autocomplete="off" oninput="onSlopeChg(0,this.value)" onchange="onSlopeChg(1,this.value)" />'
|
|
s+='<div id="slope" style="display: inline-block; width:50px; font: bold 20px Arial; color: #6600cc; text-align: left;">'+my.slope+'</div>'}
|
|
s+='<button id="coordsBtn" onclick="toggleCoords()" style="z-index:2;" class="togglebtn hi" >Coords</button>'
|
|
s+='<button id="resetBtn" onclick="reset()" style="z-index:2;" class="togglebtn" >Reset</button>'
|
|
s+='</div>'
|
|
s+='<div id="copyrt" style="position: absolute; right:0px; top:40px; font: 10px Arial; color: #6600cc; transform: rotate(-90deg); transform-origin: right bottom 0;">© 2018 MathsIsFun.com v'+my.version+'</div>'
|
|
s+='</div>'
|
|
docInsert(s)
|
|
el=document.getElementById(canvasid)
|
|
ratio=2
|
|
el.width=w*ratio
|
|
el.height=h*ratio
|
|
el.style.width=w+'px'
|
|
el.style.height=h+'px'
|
|
g=el.getContext('2d')
|
|
g.setTransform(ratio,0,0,ratio,0,0)
|
|
this.coords=new Coords(0,0,w,h,-2,-1.9,15,11,true)
|
|
this.graph=new Graph(g,coords)
|
|
this.coordsQ=true
|
|
shapes=[]
|
|
makeShapes()
|
|
drawPts()
|
|
el.addEventListener('mousedown',mouseDownListener,false)
|
|
el.addEventListener('touchstart',ontouchstart,false)
|
|
el.addEventListener('mousemove',domousemove,false)
|
|
doType()}
|
|
function onSlopeChg(n,v){v=Number(v)
|
|
my.slope=v
|
|
document.getElementById('slope').innerHTML=v
|
|
update()}
|
|
function reset(){makeShapes()
|
|
update()}
|
|
function update(){doType()}
|
|
function toggleCoords(){this.coordsQ=!this.coordsQ
|
|
toggleBtn('coordsBtn',this.coordsQ)
|
|
update()}
|
|
function toggleBtn(btn,onq){if(onq){document.getElementById(btn).classList.add('hi')
|
|
document.getElementById(btn).classList.remove('lo')}else{document.getElementById(btn).classList.add('lo')
|
|
document.getElementById(btn).classList.remove('hi')}}
|
|
function ontouchstart(evt){let touch=evt.targetTouches[0]
|
|
evt.clientX=touch.clientX
|
|
evt.clientY=touch.clientY
|
|
evt.touchQ=true
|
|
mouseDownListener(evt)}
|
|
function ontouchmove(evt){let touch=evt.targetTouches[0]
|
|
evt.clientX=touch.clientX
|
|
evt.clientY=touch.clientY
|
|
evt.touchQ=true
|
|
mouseMoveListener(evt)
|
|
evt.preventDefault()}
|
|
function ontouchend(evt){el.addEventListener('touchstart',ontouchstart,false)
|
|
window.removeEventListener('touchend',ontouchend,false)
|
|
if(dragging){dragging=false
|
|
window.removeEventListener('touchmove',ontouchmove,false)}}
|
|
function domousemove(e){document.body.style.cursor='default'
|
|
let bRect=el.getBoundingClientRect()
|
|
let mouseX=(e.clientX-bRect.left)*(el.width/ratio/bRect.width)
|
|
let mouseY=(e.clientY-bRect.top)*(el.height/ratio/bRect.height)
|
|
for(let i=0;i<shapes.length;i++){if(hitTest(shapes[i],mouseX,mouseY)){dragNo=i
|
|
document.body.style.cursor='pointer'}}}
|
|
function mouseDownListener(evt){let i
|
|
let highestIndex=-1
|
|
let bRect=el.getBoundingClientRect()
|
|
let mouseX=(evt.clientX-bRect.left)*(el.width/ratio/bRect.width)
|
|
let mouseY=(evt.clientY-bRect.top)*(el.height/ratio/bRect.height)
|
|
for(i=0;i<shapes.length;i++){if(hitTest(shapes[i],mouseX,mouseY)){dragNo=i
|
|
dragging=true
|
|
if(i>highestIndex){dragHoldX=mouseX-shapes[i].x
|
|
dragHoldY=mouseY-shapes[i].y
|
|
highestIndex=i
|
|
dragIndex=i}}}
|
|
if(dragging){if(evt.touchQ){window.addEventListener('touchmove',ontouchmove,false)}else{window.addEventListener('mousemove',mouseMoveListener,false)}
|
|
doType()}
|
|
if(evt.touchQ){el.removeEventListener('touchstart',ontouchstart,false)
|
|
window.addEventListener('touchend',ontouchend,false)}else{el.removeEventListener('mousedown',mouseDownListener,false)
|
|
window.addEventListener('mouseup',mouseUpListener,false)}
|
|
if(evt.preventDefault){evt.preventDefault()}
|
|
else if(evt.returnValue){evt.returnValue=false}
|
|
return false}
|
|
function mouseUpListener(evt){el.addEventListener('mousedown',mouseDownListener,false)
|
|
window.removeEventListener('mouseup',mouseUpListener,false)
|
|
if(dragging){dragging=false
|
|
window.removeEventListener('mousemove',mouseMoveListener,false)}}
|
|
function mouseMoveListener(evt){let posX
|
|
let posY
|
|
let shapeRad=shapes[dragIndex].rad
|
|
let minX=shapeRad
|
|
let maxX=el.width-shapeRad
|
|
let minY=shapeRad
|
|
let maxY=el.height-shapeRad
|
|
let bRect=el.getBoundingClientRect()
|
|
let mouseX=(evt.clientX-bRect.left)*(el.width/ratio/bRect.width)
|
|
let mouseY=(evt.clientY-bRect.top)*(el.height/ratio/bRect.height)
|
|
posX=mouseX-dragHoldX
|
|
posX=posX<minX?minX:posX>maxX?maxX:posX
|
|
posY=mouseY-dragHoldY
|
|
posY=posY<minY?minY:posY>maxY?maxY:posY
|
|
shapes[dragIndex].x=posX
|
|
shapes[dragIndex].y=posY
|
|
doType()}
|
|
function hitTest(shape,mx,my){let dx
|
|
let dy
|
|
dx=mx-shape.x
|
|
dy=my-shape.y
|
|
return dx*dx+dy*dy<shape.rad*shape.rad}
|
|
function doType(){g.clearRect(0,0,el.width,el.height)
|
|
this.graph.drawGraph()
|
|
drawPts()}
|
|
function makeShapes(){let xys=[]
|
|
if(my.mode=='pt'){xys=[[312,140]]}else{xys=[[145,223],[312,140],]}
|
|
shapes=[]
|
|
for(let i=0;i<xys.length;i++){let xy=xys[i]
|
|
shapes.push(new Pt(xy[0],xy[1]))}}
|
|
function drawPts(){let i
|
|
g.strokeStyle='rgba(0, 0, 150, 0.8)'
|
|
g.fillStyle='rgba(255, 255, 100, 0.1)'
|
|
g.lineWidth=2
|
|
g.beginPath()
|
|
if(my.mode=='slope'){g.strokeStyle='blue'
|
|
g.beginPath()
|
|
g.moveTo(shapes[0].x,shapes[0].y)
|
|
g.lineTo(shapes[1].x,shapes[0].y)
|
|
g.stroke()
|
|
g.strokeStyle='red'
|
|
g.beginPath()
|
|
g.moveTo(shapes[1].x,shapes[0].y)
|
|
g.lineTo(shapes[1].x,shapes[1].y)
|
|
g.stroke()
|
|
g.beginPath()}
|
|
let ln
|
|
if(my.mode=='pt'){let shape1=new Pt(shapes[0].x+100,shapes[0].y-100*my.slope)
|
|
ln=new Line(shapes[0],shape1)}else{ln=new Line(shapes[0],shapes[1])}
|
|
if(my.mode=='mid'){let midPt=new Pt((shapes[0].x+shapes[1].x)/2,(shapes[0].y+shapes[1].y)/2)
|
|
let lbl='M'
|
|
if(this.coordsQ){let xLbl=round2((round1(this.coords.toXVal(shapes[0].x))+round1(this.coords.toXVal(shapes[1].x)))/2)
|
|
let yLbl=round2((round1(this.coords.toYVal(shapes[0].y))+round1(this.coords.toYVal(shapes[1].y)))/2)
|
|
lbl+=' = ('+xLbl+','+yLbl+')'}
|
|
ptDraw(midPt,true,lbl,'hsla(0,0%,30%,0.4)')}else{ln.setLen(1500,true)}
|
|
g.strokeStyle='rgba(0, 150, 0, 0.8)'
|
|
g.moveTo(ln.a.x,ln.a.y)
|
|
g.lineTo(ln.b.x,ln.b.y)
|
|
g.stroke()
|
|
let pt0,pt1,slope,b
|
|
if(my.mode=='pt'){let x1=round1(this.coords.toXVal(shapes[0].x))
|
|
let y1=round1(this.coords.toYVal(shapes[0].y))
|
|
let xstr
|
|
if(x1<0){xstr='(x + '+-x1+')'}else{if(x1>0){xstr='(x − '+x1+')'}else{xstr='x'}}
|
|
let ystr
|
|
if(y1<0){ystr=' + '+-y1}else{if(y1>0){ystr=' − '+y1}else{ystr=''}}
|
|
if(my.slope==1){let simplifyQ=false
|
|
if(simplifyQ){let diff=round2(y1-x1)
|
|
if(diff<0){s='y = x − '+-diff}else{if(diff>0){s='y = x + '+diff}else{s='y = x'}}}else{s='y '+ystr+' = '+my.slope+xstr}}else{if(my.slope==0){s='y = '+y1}else{s='y '+ystr+' = '+my.slope+xstr}}
|
|
document.getElementById('descr').innerHTML=s}else{pt0=new Pt(round1(this.coords.toXVal(shapes[0].x)),round1(this.coords.toYVal(shapes[0].y)))
|
|
pt1=new Pt(round1(this.coords.toXVal(shapes[1].x)),round1(this.coords.toYVal(shapes[1].y)))
|
|
let dx=pt1.x-pt0.x
|
|
let dy=pt1.y-pt0.y
|
|
slope=dy/dx
|
|
b=pt0.y-pt0.x*slope}
|
|
if(my.mode=='equn'){let s=''
|
|
if(pt0.x==pt1.x){if(pt0.y==pt1.y){s='Need 2 different points'}else{s='x = '+pt0.x}}else{s='y = '+linearPhrase([round2(slope),round2(b)])}
|
|
document.getElementById('descr').innerHTML=s}
|
|
if(my.mode=='slope'){let s=''
|
|
if(pt0.x==pt1.x){if(pt0.y==pt1.y){s='Need 2 different points'}else{s='?'}}else{s='slope = '+'<span class="intbl"><em style="color:red;">'+round2(dy)+'</em><strong style="color:blue;">'+round2(dx)+'</strong></span> = '+round2(slope)
|
|
g.fillStyle='blue'
|
|
g.fillStyle='blue'
|
|
g.textAlign='center'
|
|
g.fillText(round2(dx).toString(),(shapes[0].x+shapes[1].x)/2,shapes[0].y+20)
|
|
g.fillStyle='red'
|
|
g.textAlign='left'
|
|
g.fillText(round2(dy),shapes[1].x+5,(shapes[0].y+shapes[1].y)/2)}
|
|
document.getElementById('descr').innerHTML=s}
|
|
if(my.mode=='intery'){let s=''
|
|
if(pt0.x==pt1.x){if(pt0.y==pt1.y){s='Need 2 different points'}else{s='?'}}else{if(b>this.coords.yStt&&b<this.coords.yEnd){s='y-intercept = (0,'+round2(b)+')'
|
|
let slope1=(shapes[1].y-shapes[0].y)/(shapes[1].x-shapes[0].x)
|
|
let b1=shapes[0].y-(shapes[0].x-this.coords.toXPix(0))*slope1
|
|
let ptb=new Pt(this.coords.toXPix(0),b1)
|
|
g.fillStyle='blue'
|
|
g.beginPath()
|
|
g.arc(ptb.x,ptb.y,3,0,2*Math.PI,false)
|
|
g.fill()
|
|
g.strokeStyle='blue'
|
|
g.beginPath()
|
|
g.moveTo(ptb.x+10,ptb.y)
|
|
g.lineTo(ptb.x+45,ptb.y)
|
|
g.drawArrow(ptb.x+10,ptb.y,15,2,20,10,Math.PI,10,false)
|
|
g.stroke()
|
|
g.textAlign='left'
|
|
g.fillText('(0,'+round2(b).toString()+')',ptb.x+50,ptb.y+10)}}
|
|
document.getElementById('descr').innerHTML=s}
|
|
for(i=0;i<shapes.length;i++){ptDraw(shapes[i],!this.coordsQ,String.fromCharCode(65+i),'rgba(0, 0, 255, 0.4)')}}
|
|
function ptDraw(pt,lblQ,lbl,clr){g.fillStyle=clr
|
|
g.beginPath()
|
|
g.arc(pt.x,pt.y,10,0,2*Math.PI,false)
|
|
g.closePath()
|
|
g.fill()
|
|
g.fillStyle='orange'
|
|
g.beginPath()
|
|
g.arc(pt.x,pt.y,2,0,2*Math.PI,false)
|
|
g.closePath()
|
|
g.fill()
|
|
g.textAlign='left'
|
|
if(lblQ){g.font='14px Arial'
|
|
g.fillText(lbl,pt.x+5,pt.y-9)}else{g.font='bold 14px Arial'
|
|
let txt='('
|
|
txt+=round1(this.coords.toXVal(pt.x))
|
|
txt+=','
|
|
txt+=round1(this.coords.toYVal(pt.y))
|
|
txt+=')'
|
|
g.fillText(txt,pt.x+14,pt.y+3)}}
|
|
function round1(v){return Math.round(v*10)/10}
|
|
function round2(v){return Math.round(v*100)/100}
|
|
function linearPhrase(a){let s=''
|
|
for(let k=0;k<a.length;k++){let v=a[k]
|
|
if(v!=0){if(v<0){if(s.length>0){s+=' − '}else{s+=' −'}
|
|
v=-v}else{if(s.length>0){s+=' + '}}
|
|
switch(k){case 0:if(v!=1){s+=v}
|
|
s+='x'
|
|
break
|
|
case 1:s+=v
|
|
break
|
|
default:if(v!=1){s+=v}
|
|
s+='('+k+')'
|
|
break}}}
|
|
if(s.length==0){s='0'}
|
|
return s}
|
|
function Pt(ix,iy){this.x=ix
|
|
this.y=iy
|
|
this.rad=9
|
|
this.color='rgb('+0+','+0+','+255+')'
|
|
angleIn=0
|
|
angleOut=0}
|
|
Pt.prototype.setxy=function(ix,iy){this.x=ix
|
|
this.y=iy}
|
|
Pt.prototype.getAngle=function(){return this.angleOut-this.angleIn}
|
|
Pt.prototype.drawMe=function(g){g.fillStyle='rgba(0, 0, 255, 0.3)'
|
|
g.beginPath()
|
|
g.arc(this.x,this.y,20,0,2*Math.PI,false)
|
|
g.closePath()
|
|
g.fill()}
|
|
Pt.prototype.getAvg=function(pts){let xSum=0
|
|
let ySum=0
|
|
for(let i=0;i<pts.length;i++){xSum+=pts[i].x
|
|
ySum+=pts[i].y}
|
|
let newPt=new Pt(xSum/pts.length,ySum/pts.length)
|
|
newPt.x=xSum/pts.length
|
|
newPt.y=ySum/pts.length
|
|
return newPt}
|
|
Pt.prototype.setAvg=function(pts){this.setPrevPt()
|
|
let newPt=this.getAvg(pts)
|
|
this.x=newPt.x
|
|
this.y=newPt.y
|
|
validPtQ=true}
|
|
Pt.prototype.interpolate=function(pt1,pt2,f){this.setPrevPt()
|
|
this.x=pt1.x*f+pt2.x*(1-f)
|
|
this.y=pt1.y*f+pt2.y*(1-f)
|
|
validPtQ=true}
|
|
Pt.prototype.translate=function(pt,addQ){addQ=typeof addQ!=='undefined'?addQ:true
|
|
t=new Pt(this.x,this.y)
|
|
t.x=this.x
|
|
t.y=this.y
|
|
if(addQ){t.x+=pt.x
|
|
t.y+=pt.y}else{t.x-=pt.x
|
|
t.y-=pt.y}
|
|
return t}
|
|
Pt.prototype.multiply=function(fact){return new Pt(this.x*fact,this.y*fact)}
|
|
Pt.prototype.multiplyMe=function(fact){this.x*=fact
|
|
this.y*=fact}
|
|
function dist(dx,dy){return Math.sqrt(dx*dx+dy*dy)}
|
|
function loop(currNo,minNo,maxNo,incr){currNo+=incr
|
|
let range=maxNo-minNo+1
|
|
if(currNo<minNo){currNo=maxNo-((-currNo+maxNo)%range)}
|
|
if(currNo>maxNo){currNo=minNo+((currNo-minNo)%range)}
|
|
return currNo}
|
|
function constrain(min,val,max){return Math.min(Math.max(min,val),max)}
|
|
function Coords(left,top,width,height,xStt,yStt,xEnd,yEnd,uniScaleQ){this.left=left
|
|
this.top=top
|
|
this.width=width
|
|
this.height=height
|
|
this.xStt=xStt
|
|
this.yStt=yStt
|
|
this.xEnd=xEnd
|
|
this.yEnd=yEnd
|
|
this.uniScaleQ=uniScaleQ
|
|
let xLogQ=false
|
|
let yLogQ=false
|
|
let skewQ=true
|
|
this.xScale
|
|
let xLogScale
|
|
this.yScale
|
|
console.log('Coords: ',this.xStt,this.yStt,this.xEnd,this.yEnd)
|
|
this.calcScale()}
|
|
Coords.prototype.calcScale=function(){console.log('calcScale: ',this.xStt,this.yStt,this.xEnd,this.yEnd)
|
|
if(this.xLogQ){if(this.xStt<=0)this.xStt=1
|
|
if(this.xEnd<=0)this.xEnd=1}
|
|
if(this.yLogQ){if(this.yStt<=0)this.yStt=1
|
|
if(this.yEnd<=0)this.yEnd=1}
|
|
let temp
|
|
if(this.xStt>this.xEnd){temp=this.xStt
|
|
this.xStt=this.xEnd
|
|
this.xEnd=temp}
|
|
if(this.yStt>this.yEnd){temp=this.yStt
|
|
this.yStt=this.yEnd
|
|
this.yEnd=temp}
|
|
let xSpan=this.xEnd-this.xStt
|
|
if(xSpan<=0)xSpan=0.1
|
|
this.xScale=xSpan/this.width
|
|
this.xLogScale=(Math.log(this.xEnd)-Math.log(this.xStt))/this.width
|
|
let ySpan=this.yEnd-this.yStt
|
|
if(ySpan<=0)ySpan=0.1
|
|
this.yScale=ySpan/this.height
|
|
this.yLogScale=(Math.log(this.yEnd)-Math.log(this.yStt))/this.height
|
|
if(this.uniScaleQ&&!this.xLogQ&&!this.yLogQ){let newScale=Math.max(this.xScale,this.yScale)
|
|
this.xScale=newScale
|
|
xSpan=this.xScale*this.width
|
|
let xMid=(this.xStt+this.xEnd)/2
|
|
this.xStt=xMid-xSpan/2
|
|
this.xEnd=xMid+xSpan/2
|
|
this.yScale=newScale
|
|
ySpan=this.yScale*this.height
|
|
let yMid=(this.yStt+this.yEnd)/2
|
|
this.yStt=yMid-ySpan/2
|
|
this.yEnd=yMid+ySpan/2}}
|
|
Coords.prototype.getXScale=function(){return this.xScale}
|
|
Coords.prototype.getYScale=function(){return this.yScale}
|
|
Coords.prototype.toXPix=function(val){if(this.xLogQ){return this.left+(Math.log(val)-Math.log(xStt))/xLogScale}else{return this.left+(val-this.xStt)/this.xScale}}
|
|
Coords.prototype.toYPix=function(val){if(this.yLogQ){return this.top+(Math.log(yEnd)-Math.log(val))/yLogScale}else{return this.top+(this.yEnd-val)/this.yScale}}
|
|
Coords.prototype.toPtVal=function(pt,useCornerQ){return new Pt(toXVal(pt.x,useCornerQ),toYVal(pt.y,useCornerQ))}
|
|
Coords.prototype.toXVal=function(pix,useCornerQ){if(useCornerQ){return this.xStt+(pix-this.left)*this.xScale}else{return this.xStt+pix*this.xScale}}
|
|
Coords.prototype.toYVal=function(pix,useCornerQ){if(useCornerQ){return this.yEnd-(pix-this.top)*this.yScale}else{return this.yEnd-pix*this.yScale}}
|
|
Coords.prototype.getTicks=function(stt,span){let ticks=[]
|
|
let inter=this.tickInterval(span/5,false)
|
|
let tickStt=Math.ceil(stt/inter)*inter
|
|
let i=0
|
|
let tick
|
|
do{tick=i*inter
|
|
tick=Number(tick.toPrecision(5))
|
|
ticks.push([tickStt+tick,1])
|
|
i++}while(tick<span)
|
|
inter=this.tickInterval(span/4,true)
|
|
for(i=0;i<ticks.length;i++){let t=ticks[i][0]
|
|
if(Math.abs(Math.round(t/inter)-t/inter)<0.001){ticks[i][1]=0}}
|
|
return ticks}
|
|
Coords.prototype.tickInterval=function(span,majorQ){let pow10=Math.pow(10,Math.floor(Math.log(span)*Math.LOG10E))
|
|
let mantissa=span/pow10
|
|
if(mantissa>=5){if(majorQ){return 5*pow10}else{return 2*pow10}}
|
|
if(mantissa>=2){if(majorQ){return 2*pow10}else{return 1*pow10}}
|
|
if(mantissa>=1){if(majorQ){return 1*pow10}else{return 0.2*pow10}}
|
|
if(majorQ){return 1*pow10}else{return 0.2*pow10}}
|
|
Coords.prototype.xTickInterval=function(tickDensity,majorQ){return this.tickInterval((this.xEnd-this.xStt)/tickDensity,majorQ)}
|
|
Coords.prototype.yTickInterval=function(tickDensity,majorQ){return this.tickInterval((this.yEnd-this.yStt)/tickDensity,majorQ)}
|
|
function Graph(g,coords){this.g=g
|
|
this.coords=coords
|
|
let xClr=0x4444ff
|
|
let yClr=0xff4444
|
|
this.xLinesQ=true
|
|
this.yLinesQ=true
|
|
this.xArrowQ=true
|
|
this.yArrowQ=true
|
|
this.xValsQ=true
|
|
this.yValsQ=true
|
|
this.skewQ=false}
|
|
Graph.prototype.drawGraph=function(){if(coords.xLogQ){this.drawLinesLogX()}else{if(this.xLinesQ){this.drawLinesX()}}
|
|
if(coords.yLogQ){this.drawLinesLogY()}else{if(this.yLinesQ){this.drawLinesY()}}}
|
|
Graph.prototype.drawLinesX=function(){let xAxisPos=coords.toYPix(0)
|
|
let yAxisPos=coords.toXPix(0)
|
|
let numAtAxisQ=yAxisPos>=0&&yAxisPos<coords.width
|
|
let g=this.g
|
|
g.lineWidth=1
|
|
let ticks=coords.getTicks(coords.xStt,coords.xEnd-coords.xStt)
|
|
for(let i=0;i<ticks.length;i++){let tick=ticks[i]
|
|
let xVal=tick[0]
|
|
let tickLevel=tick[1]
|
|
if(tickLevel==0){g.strokeStyle='rgba(0,0,256,0.3)'}else{g.strokeStyle='rgba(0,0,256,0.1)'}
|
|
let xPix=coords.toXPix(xVal,false)
|
|
g.beginPath()
|
|
g.moveTo(xPix,coords.toYPix(coords.yStt,false))
|
|
g.lineTo(xPix,coords.toYPix(coords.yEnd,false))
|
|
g.stroke()
|
|
if(tickLevel==0&&this.xValsQ){g.fillStyle='#0000ff'
|
|
g.font='12px Verdana'
|
|
g.textAlign='center'
|
|
let lbly=0
|
|
if(numAtAxisQ&&xAxisPos>0&&xAxisPos<coords.height){lbly=xAxisPos+15}else{lbly=coords.height-20}
|
|
g.fillText(fmt(xVal),xPix,xAxisPos+15)}}
|
|
if(this.skewQ)return
|
|
if(yAxisPos>=0&&yAxisPos<coords.width){g.lineWidth=2
|
|
g.strokeStyle='rgba(256,0,0,0.4)'
|
|
g.beginPath()
|
|
g.moveTo(yAxisPos,coords.toYPix(coords.yStt,false))
|
|
g.lineTo(yAxisPos,coords.toYPix(coords.yEnd,false))
|
|
g.stroke()
|
|
g.beginPath()
|
|
g.fillStyle=g.strokeStyle
|
|
g.drawArrow(yAxisPos,coords.toYPix(coords.yEnd),15,2,20,10,Math.PI/2,10,false)
|
|
g.stroke()
|
|
g.fill()
|
|
g.font='bold 24px Arial'
|
|
g.fillText('y',yAxisPos+12,coords.toYPix(coords.yEnd)+15)}}
|
|
Graph.prototype.drawLinesY=function(){let xAxisPos=coords.toYPix(0)
|
|
let yAxisPos=coords.toXPix(0)
|
|
let numAtAxisQ=xAxisPos>=0&&xAxisPos<coords.height
|
|
let g=this.g
|
|
g.lineWidth=1
|
|
let ticks=coords.getTicks(coords.yStt,coords.yEnd-coords.yStt)
|
|
for(let i=0;i<ticks.length;i++){let tick=ticks[i]
|
|
let yVal=tick[0]
|
|
let tickLevel=tick[1]
|
|
if(tickLevel==0){g.strokeStyle='rgba(0,0,256,0.3)'}else{g.strokeStyle='rgba(0,0,256,0.1)'}
|
|
let yPix=coords.toYPix(yVal,false)
|
|
g.beginPath()
|
|
g.moveTo(coords.toXPix(coords.xStt,false),yPix)
|
|
g.lineTo(coords.toXPix(coords.xEnd,false),yPix)
|
|
g.stroke()
|
|
if(tickLevel==0&&this.yValsQ){g.fillStyle='#ff0000'
|
|
g.font='12px Verdana'
|
|
g.textAlign='right'
|
|
g.fillText(fmt(yVal),yAxisPos-5,yPix+5)}}
|
|
if(this.skewQ)return
|
|
if(xAxisPos>=0&&xAxisPos<coords.height){g.lineWidth=2
|
|
g.strokeStyle='rgba(0,0,256,0.4)'
|
|
g.beginPath()
|
|
g.moveTo(coords.toXPix(coords.xStt,false),xAxisPos)
|
|
g.lineTo(coords.toXPix(coords.xEnd,false),xAxisPos)
|
|
g.stroke()
|
|
g.beginPath()
|
|
g.fillStyle=g.strokeStyle
|
|
g.drawArrow(coords.toXPix(coords.xEnd,false),xAxisPos,15,2,20,10,0,10,false)
|
|
g.stroke()
|
|
g.fill()
|
|
g.font='bold 24px Arial'
|
|
g.fillText('x',coords.toXPix(coords.xEnd,false)-5,xAxisPos-7)}}
|
|
function Line(pt1,pt2){this.a=pt1
|
|
this.b=pt2}
|
|
Line.prototype.getLength=function(){let dx=this.b.x-this.a.x
|
|
let dy=this.b.y-this.a.y
|
|
return Math.sqrt(dx*dx+dy*dy)}
|
|
Line.prototype.setLen=function(newLen,fromMidQ){let len=this.getLength()
|
|
if(fromMidQ){let midPt=this.getMidPt()
|
|
let halfPt=new Pt(this.a.x-midPt.x,this.a.y-midPt.y)
|
|
halfPt.multiplyMe(newLen/len)
|
|
this.a=midPt.translate(halfPt)
|
|
this.b=midPt.translate(halfPt,false)}else{let diffPt=new Pt(this.a.x-this.b.x,this.a.y-this.b.y)
|
|
diffPt.multiplyMe(newLen/len)
|
|
this.b=this.a.translate(diffPt,false)}}
|
|
Line.prototype.getMidPt=function(){return new Pt((this.a.x+this.b.x)/2,(this.a.y+this.b.y)/2)}
|
|
CanvasRenderingContext2D.prototype.drawArrow=function(x0,y0,totLen,shaftHt,headLen,headHt,angle,sweep,invertQ){let g=this
|
|
let pts=[[0,0],[-headLen,-headHt/2],[-headLen+sweep,-shaftHt/2],[-totLen,-shaftHt/2],[-totLen,shaftHt/2],[-headLen+sweep,shaftHt/2],[-headLen,headHt/2],[0,0],]
|
|
if(invertQ){pts.push([0,-headHt/2],[-totLen,-headHt/2],[-totLen,headHt/2],[0,headHt/2])}
|
|
for(let i=0;i<pts.length;i++){let cosa=Math.cos(-angle)
|
|
let sina=Math.sin(-angle)
|
|
let xPos=pts[i][0]*cosa+pts[i][1]*sina
|
|
let yPos=pts[i][0]*sina-pts[i][1]*cosa
|
|
if(i==0){g.moveTo(x0+xPos,y0+yPos)}else{g.lineTo(x0+xPos,y0+yPos)}}}
|
|
function fmt(num,digits){digits=14
|
|
if(num==Number.POSITIVE_INFINITY)return 'undefined'
|
|
if(num==Number.NEGATIVE_INFINITY)return 'undefined'
|
|
num=num.toPrecision(digits)
|
|
num=num.replace(/0+$/,'')
|
|
if(num.charAt(num.length-1)=='.')num=num.substr(0,num.length-1)
|
|
if(Math.abs(num)<1e-15)num=0
|
|
return num}
|
|
function docInsert(s){let div=document.createElement('div')
|
|
div.innerHTML=s
|
|
let script=document.currentScript
|
|
console.log('docInsert',script.parentElement)
|
|
script.parentElement.insertBefore(div,script)}
|
|
function wrap({id='',cls='',pos='rel',style='',txt='',tag='div',lbl='',fn='',opts=[]},...mores){let s=''
|
|
s+='\n'
|
|
txt+=mores.join('')
|
|
let noProp='event.stopPropagation(); '
|
|
let tags={btn:{stt:'<button '+(fn.length>0?' onclick="'+noProp+fn+'" ':''),cls:'btn',fin:'>'+txt+'</button>'},can:{stt:'<canvas ',cls:'',fin:'></canvas>'},div:{stt:'<div '+(fn.length>0?' onclick="'+fn+'" ':''),cls:'',fin:' >'+txt+'</div>'},edit:{stt:'<textarea onkeyup="'+fn+'" onchange="'+fn+'"',cls:'',fin:' >'+txt+'</textarea>'},inp:{stt:'<input value="'+txt+'"'+(fn.length>0?' oninput="'+fn+'" onchange="'+fn+'"':''),cls:'input',fin:'>'+(lbl.length>0?'</label>':'')},out:{stt:'<span ',cls:'output',fin:' >'+txt+'</span>'+(lbl.length>0?'</label>':'')},radio:{stt:'<div ',cls:'radio',fin:'>\n'},sel:{stt:'<select '+(fn.length>0?' onchange="'+fn+'"':''),cls:'select',fin:'>\n'},sld:{stt:'<input type="range" '+txt+' oninput="'+noProp+fn+'" onchange="'+noProp+fn+'"',cls:'select',fin:'>'+(lbl.length>0?'</label>':'')+'<span id="'+id+'0">0</span>'},}
|
|
let type=tags[tag]
|
|
if(lbl.length>0)s+='<label class="label">'+lbl+' '
|
|
s+=type.stt
|
|
if(cls.length==0)cls=type.cls
|
|
if(tag=='div')style+=fn.length>0?' cursor:pointer;':''
|
|
if(id.length>0)s+=' id="'+id+'"'
|
|
if(cls.length>0)s+=' class="'+cls+'"'
|
|
if(pos=='dib')s+=' style="position:relative; display:inline-block;'+style+'"'
|
|
if(pos=='rel')s+=' style="position:relative; '+style+'"'
|
|
if(pos=='abs')s+=' style="position:absolute; '+style+'"'
|
|
s+=type.fin
|
|
if(tag=='radio'){for(let i=0;i<opts.length;i++){let chk=''
|
|
if(i==0)chk='checked'
|
|
let idi=id+i
|
|
let lbl=opts[i]
|
|
s+='<input id="'+idi+'" type="radio" name="'+id+'" value="'+lbl.name+'" onclick="'+fn+'('+i+');" '+chk+' >'
|
|
s+='<label for="'+idi+'">'+lbl.name+' </label>'}
|
|
s+='</div>'}
|
|
if(tag=='sel'){for(let i=0;i<opts.length;i++){let opt=opts[i]
|
|
let idStr=id+i
|
|
let chkStr=opt.descr==txt?' selected ':''
|
|
s+='<option id="'+idStr+'" value="'+opt.name+'"'+chkStr+'>'+opt.descr+'</option>\n'}
|
|
s+='</select>'
|
|
if(lbl.length>0)s+='</label>'}
|
|
s+='\n'
|
|
return s.trim()}
|
|
init() |