let w,h,ratio,el,g,my={};function leastsquaresMain(){let version='0.6';let canvasid="canvas0" my.dragQ=false;my.dragScreenQ=false;w=540;h=360;my.opts={title:'Title',xtitl:'X Values',ytitl:'Y Values',raws:'(1.21,1.69), (3.00,5.89), (5.16,4.11), (8.31,5.49), (10.21,8.65)'} my.graphWd=w;my.graphHt=h;let s='';s+='
';var inps=[{lbl:'X Label',id:'xtitl',ht:20,type:0},{lbl:'Y Label',id:'ytitl',ht:20,type:0},];s+='
';for(var i=0;i';s+='
'+inp.lbl+': 
';s+='  ';s+='
';} s+='
';s+='
';s+='';s+='
';s+='
';s+='';s+='
';s+='';s+='';s+='
';s+='
';s+='
';s+='Zoom:';s+='';s+='';s+=' ' s+='';s+='';s+='
';s+='
';s+='';s+='
';s+='';s+='';s+='
';s+='
';s+='
© 2020 MathsIsFun.com v'+version+'
';s+='';document.write(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);my.currZoom=1;my.sttRect=[-1,-1,12,10];my.coords=new Coords(0,0,w,h,my.sttRect[0],my.sttRect[1],my.sttRect[2],my.sttRect[3],true);this.graph=new Graph(g,my.coords);my.dragNo=0;my.coordsQ=false;my.guidesQ=false;my.shapes=[];my.pts=[];el.addEventListener("mousedown",mouseDownListener,false);el.addEventListener('touchstart',ontouchstart,false);el.addEventListener("mousemove",dopointer,false);document.getElementById('xtitl').value=optGet('xtitl') document.getElementById('ytitl').value=optGet('ytitl') document.getElementById('raws').value=optGet('raws') decodeCSV(optGet('raws')) go();shapesToEdit();} function go(){optSet('xtitl',document.getElementById('xtitl').value) optSet('ytitl',document.getElementById('ytitl').value) optSet('raws',document.getElementById('raws').value) console.log('go',my.opts) g.clearRect(0,0,el.width,el.height);leastsquares();this.graph.drawGraph();drawPts();} function leastsquares(){let sumx=0;let sumy=0 let sumx2=0;let sumxy=0;for(let i=0;i0)s+=', ';s+='(';s+=my.shapes[i].x s+=',';s+=my.shapes[i].y s+=')';} return s;} function decodeCSV(s){s=s.replace(/\s/g,"~");s=s.replace(/\)\,\(/g,"~");s=s.replace(/\)\(/g,"~");s=s.replace(/\)/g,"");s=s.replace(/\(/g,"");s=s.replace(/\,/g,"_");s=s.replace(/\*/g,"_");console.log("decodeCSV",s);decode(s);} function decode(s){let a=s.split("~");my.shapes=[];for(let i=0;i=2){my.shapes.push(new Pt(Number(xy[0]),Number(xy[1])));}} console.log("my.shapes",my.shapes);go();} function fit(){my.coords.fitToPts(my.shapes,1.1);go();} function toggleCoords(){my.coordsQ=!my.coordsQ;btn("coordsBtn",my.coordsQ);go();} function toggleGuides(){my.guidesQ=!my.guidesQ;btn("guidesBtn",my.guidesQ);go();} function btn(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(my.dragQ){my.dragQ=false;window.removeEventListener("touchmove",ontouchmove,false);}} function dopointer(e){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);let overQ=false;for(let i=0;ihighestIndex){my.dragHold={x:mouseX-my.pts[i].x,y:mouseY-my.pts[i].y} highestIndex=i;my.dragNo=i;}}} if(!my.dragQ){my.dragQ=true;my.dragScreenQ=true;} if(evt.touchQ){window.addEventListener('touchmove',ontouchmove,false);}else{window.addEventListener("mousemove",mouseMoveListener,false);} console.log("mouseDownListener",my.dragQ,my.dragScreenQ,highestIndex);go();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){my.dragQ=false;el.addEventListener("mousedown",mouseDownListener,false);window.removeEventListener("mouseup",mouseUpListener,false);window.removeEventListener("mousemove",mouseMoveListener,false);} function mouseMoveListener(evt){let posX;let posY;let shapeRad=my.shapes[my.dragNo].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);if(my.dragScreenQ){let moveX=my.prevX-mouseX;let moveY=mouseY-my.prevY;if(Math.abs(moveX)>1||Math.abs(moveY)>1){my.coords.drag(moveX,moveY);my.prevX=mouseX;my.prevY=mouseY;}}else{posX=mouseX-my.dragHold.x;posX=(posXmaxX)?maxX:posX);posY=mouseY-my.dragHold.y;posY=(posYmaxY)?maxY:posY);my.shapes[my.dragNo].x=fmt(my.coords.toXVal(posX),4) my.shapes[my.dragNo].y=fmt(my.coords.toYVal(posY),4)} go();shapesToEdit();} function hitTest(shape,mx,my){let dx;let dy;dx=mx-shape.x;dy=my-shape.y;return(dx*dx+dy*dymaxNo){currNo=minNo+(currNo-minNo)%range;} return currNo;} function constrain(min,val,max){return(Math.min(Math.max(min,val),max));} class Coords{constructor(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;this.xLogQ=false;this.yLogQ=false;this.xScale;let xLogScale;this.yScale;this.calcScale();} calcScale(){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;}} getXScale(){return this.xScale;} getYScale(){return this.yScale;} scale(factor,xMid,yMid){if(typeof xMid=='undefined') xMid=(this.xStt+this.xEnd)/2;this.xStt=xMid-(xMid-this.xStt)*factor;this.xEnd=xMid+(this.xEnd-xMid)*factor;if(typeof yMid=='undefined') yMid=(this.yStt+this.yEnd)/2;this.yStt=yMid-(yMid-this.yStt)*factor;this.yEnd=yMid+(this.yEnd-yMid)*factor;this.calcScale();} drag(xPix,yPix){this.xStt+=xPix*this.xScale;this.xEnd+=xPix*this.xScale;this.yStt+=yPix*this.yScale;this.yEnd+=yPix*this.yScale;this.calcScale();} newCenter(x,y){let xMid=this.xStt+x*this.xScale;let xhalfspan=(this.xEnd-this.xStt)/2;this.xStt=xMid-xhalfspan;this.xEnd=xMid+xhalfspan;let yMid=this.yEnd-y*this.yScale;let yhalfspan=(this.yEnd-this.yStt)/2;this.yStt=yMid-yhalfspan;this.yEnd=yMid+yhalfspan;this.calcScale();} fitToPts(pts,borderFactor){for(let i=0;i",this.xEnd,this.yEnd);let xMid=(this.xStt+this.xEnd)/2;let xhalfspan=borderFactor*(this.xEnd-this.xStt)/2;this.xStt=xMid-xhalfspan;this.xEnd=xMid+xhalfspan;let yMid=(this.yStt+this.yEnd)/2;let yhalfspan=borderFactor*(this.yEnd-this.yStt)/2;this.yStt=yMid-yhalfspan;this.yEnd=yMid+yhalfspan;console.log("after factor",this.xStt,this.yStt,"-->",this.xEnd,this.yEnd);this.calcScale();} toXPix(val,useCornerQ){if(this.xLogQ){return this.left+(Math.log(val)-Math.log(this.xStt))/this.xLogScale;}else{return this.left+((val-this.xStt)/this.xScale);}} toYPix(val){if(this.yLogQ){return this.top+(Math.log(this.yEnd)-Math.log(val))/this.yLogScale;}else{return this.top+((this.yEnd-val)/this.yScale);}} toPtVal(pt,useCornerQ){return new Pt(this.toXVal(pt.x,useCornerQ),this.toYVal(pt.y,useCornerQ));} toXVal(pix,useCornerQ){if(useCornerQ){return this.xStt+(pix-this.left)*this.xScale;}else{return this.xStt+pix*this.xScale;}} toYVal(pix,useCornerQ){if(useCornerQ){return this.yEnd-(pix-this.top)*this.yScale;}else{return this.yEnd-pix*this.yScale;}} getTicks(stt,span,ratio){let ticks=[];let inter=this.tickInterval(span/ratio,false);let tickStt=Math.ceil(stt/inter)*inter;let i=0;let tick=0;do{tick=tickStt+i*inter;tick=Number(tick.toPrecision(8));ticks.push([tick,1]);i++;}while(tick=5){if(majorQ){return(5*pow10);}else{return(1*pow10);}} if(mantissa>=3){if(majorQ){return(2*pow10);}else{return(0.2*pow10);}} if(mantissa>=1.4){if(majorQ){return(0.5*pow10);}else{return(0.2*pow10);}} if(mantissa>=0.8){if(majorQ){return(0.5*pow10);}else{return(0.1*pow10);}} if(majorQ){return(0.2*pow10);}else{return(0.1*pow10);}}} class Graph{constructor(g){this.g=g;this.xLinesQ=true;this.yLinesQ=true;this.xValsQ=true;this.yValsQ=true;this.vtNumsAlign='right';this.skewQ=false;} drawGraph(){this.hzAxisY=my.coords.toYPix(0);if(this.hzAxisY<0)this.hzAxisY=10;if(this.hzAxisY>my.coords.height)this.hzAxisY=my.coords.height-17 this.hzNumsY=this.hzAxisY+14;if(this.hzAxisY>my.coords.height-17)this.hzNumsY=my.coords.height-3;this.vtAxisX=my.coords.toXPix(0);if(this.vtAxisX<0)this.vtAxisX=10;if(this.vtAxisX>my.coords.width)this.vtAxisX=my.coords.width;this.vtNumsX=this.vtAxisX-5;this.vtNumsAlign='right';this.vtLblX=this.vtAxisX-25 if(this.vtAxisX<=10){this.vtNumsAlign='left';this.vtNumsX=15;this.vtLblX=this.vtAxisX+45} if(my.coords.xLogQ){this.drawLinesLogX();}else{if(this.xLinesQ){this.drawHzLines();}} if(my.coords.yLogQ){this.drawLinesLogY();}else{if(this.yLinesQ){this.drawVtLines();}} g.fillStyle="#0000ff";g.font="28px Arial";g.textAlign="center";g.fillText(my.formula,w/2,24);} drawVtLines(){let g=this.g;g.lineWidth=1;let ticks=my.coords.getTicks(my.coords.xStt,my.coords.xEnd-my.coords.xStt,my.graphWd/100);for(let i=0;i-1);if(isChrome){win.focus();setTimeout(function(){win.print();},500);}else{win.focus();win.print();win.close();}} function pageHTML(dataUrl){let s='Recent';s+='';s+='';s+='';s+='';s+='';return s;} function linearFormula(x1,y1,x2,y2){console.log("calculate",x1,y1,x2,y2);let deltax=x2-x1;let deltay=y2-y1;let pointStr="("+x1+","+y1+"), ("+x2+","+y2+")";let m=0 let b=0 let formula="";let parallel="";let perpendicular="";if(deltax==0){if(deltay==0){formula="Please enter two different points";}else{formula="x = "+x1+" [note: x, not y]";parallel="x = any number";perpendicular="y = any number";}}else{if(deltay==0){m=0;b=y1;formula="y = "+y1;parallel="y = any number";perpendicular="x = any number";}else{m=(y2-y1)/(x2-x1);b=y1-(m*x1);formula="y = "+linearPhrase([m,b]);parallel=linearPhrase([m,0])+" + any number";let newm=-1/m;perpendicular=linearPhrase([newm,0])+" + any number";}} var s='';s+='Points: '+pointStr+'';s+='
Formula (y=mx+b): '+formula+'';s+='
Slope m: '+m+'';s+='
Y-intercept b: '+b+'';s+='
Parallel lines: '+parallel+'';s+='
Perpendicular lines: '+perpendicular+'';return formula} function linearPhrase(a){var s="";for(var k=0;k0){s+=" − ";}else{s+=" −";} v=-v;}else{if(s.length>0){s+=" + ";}} switch(k){case 0:if(v!=1){s+=fmt(v,4);} s+="x";break;case 1:s+=fmt(v,4) break;default:if(v!=1){s+=fmt(v,4)} s+="("+fmt(k,4)+")";break;}}} if(s.length==0){s='0';} return s;}