博客园 首页 私信博主 显示目录 隐藏目录 管理 动画

javascript自制函数图像生成器

  出于某种目的想做这个东西,顺便可以提供给GMA的用户&&放在博客园。

  实现上只是简单的描点,加上一个相邻两点连线的开关,完全没有技术含量。而且函数图像一旦多起来就会变卡。

  瓶颈在隐函数的绘制,这个超烦,计算量一下子就上去了。我的做法是把画布格成很多个60*60的小方格,先扫一遍方格边框上有没有零点,有就进这个方格绘图,没有就不管它了。绘图的部分也牺牲了精度换取速度,当然相关参数都还给用户自己调。结果还是挺慢的,但还可以接受啦。

  效果还行

  

  

  

  接下来打算兹兹一下js语句做函数表达式,然后再加上一维时间(动态图)。三维空间的话估计等我读了大学才能来做了……空间几何完全不行啊T_T

  诸位聚聚如果有什么更好的函数渲染算法给点点建议啊……

  

<?php
    require_once("style.php");
?>
<style>
[UnSelect=YES]{
      -webkit-touch-callout: none; /* iOS Safari */
    -webkit-user-select: none; /* Chrome/Safari/Opera */
    -khtml-user-select: none; /* Konqueror */
    -moz-user-select: none; /* Firefox */
    -ms-user-select: none; /* Internet Explorer/Edge */
}
#mask{
    width:100%;
    height:100%;
    background:#000;
    opacity:0.5;
    position:fixed;
    top:0;
    left: 0;
    z-index: 100;
    display:none;
}
.notes{
    color:#FFFFFF;
    font-weight:800;
    font-size:20px;
}
</style>
<body>
<div id="mask"></div>
<div id="control" class="ui inverted segment" style="background-color:black;position:fixed;margin:auto;left:0;right:0;top:0;bottom:0;z-index:101;width:600px;height:500px;overflow:auto;display:none;">
    <div id="function">
        <button class="ui green button" onclick="Add()">Add function</button>
          <span class="ui divider"></span>
        <span id="mod" style="display:none" name="0">
              <select class="ui dropdown" style="border-radius:5px;" onchange="FunctionChange(this)">
                  <option value="0">Cartesian</option>
                  <option value="1">Polar</option>
                  <option value="2">Implicit</option>
                  <option value="3">Parameter</option>
            </select>
              <input type="color" style="width:20px;height:20px;border-radius:5px;"/>
              <span style="display:inline">
                <span class="notes">y=</span>
                  <span class="ui input" name="Fun"><input/></span>
                  <span class="notes"></span>
                  <span class="ui input" style="display:none"><input/></span>
            </span>
            <button class="ui red button" onclick="Delete(this.parentNode)">Delete</button>
            <span class="ui checkbox">
                <input type="checkbox" onclick="DrawLine(this.parentNode)"/>
                <label style="color:white;">Draw Line</label>
            </span>
              <span style="display:none;margin-top:6px;font-size:80%;">
                  <span class="notes"></span>
                  <span class="ui input" style="width:50px;"><input/></span>
                  <span class="notes"></span>
                  <span class="ui input" style="width:50px;"><input/></span>
                  <span class="notes"></span>
                  <span class="ui input" style="width:50px;"><input/></span>
            </span>
              <span class="ui divider" style="height:40px"></span>
        </span>
    </div>
      <div class="title" UnSelect="YES" onclick="ShowOption(this)" style="color:white;cursor:pointer;font-size:17px;line-height:35px;font-weight:bold;"><i class="Caret Right icon"></i>Advanced Options</div>
    <div id="option" UnSelect="YES" style="color:white;font-size:50%;font-family: Consolas,Monaco,monospace;display:none;transition:2s;line-height:30px;">
        <span class="notes">X:</span>
        <span  class="ui input"><input id="FunLx" style="width:180px;"/></span>
        <span class="notes">~</span>
        <span  class="ui input"><input id="FunRx" style="width:180px;"/></span>
        <br>
        <span class="notes">Y:</span>
        <span  class="ui input"><input id="FunLy" style="width:180px;"/></span>
        <span class="notes">~</span>
        <span  class="ui input"><input id="FunRy" style="width:180px;"/></span>
        <br>
        <span class="notes">Coordinate:</span>
        <span  class="ui input"><input id="FontStyle" style="width:200px;" value=""/></span>
          <br>
        <span class="ui checkbox">
            <input type="checkbox" id="sizelimit" checked="1"/>
            <label style="color:white;">Size Limit</label>
        </span>
          <br>
        <span class="notes">PointRadii:</span>
        <span  class="ui input"><input id="PointRadii" style="width:50px;" value="1"/></span>
          <br>
        <span class="notes">Scale:</span>
        <span  class="ui input"><input id="Scale" style="width:50px;" value="0.9"/></span>
          <br>
        <span class="ui checkbox">
            <input type="checkbox" id="DrawMoving"  checked=1/>
            <label style="color:white;">Draw while moving</label>
        </span>
          <br>
          <span class="notes" id="size"></span>
    </div>
      <span class="ui divider"></span>
    <button class="ui blue button" onclick="change()">Draw</button>
</div>
<div id="main">
    <h1 class="ui header" UnSelect="YES" style="">
        <i class="book icon"></i>
        <div class="content">Tools</div>
    </h1>
    <h1 align="center" UnSelect="YES">函数图像绘制工具</h1>
      <div align="center" style="width:100%;height:70%;">
        <canvas id="graph"></canvas>
    </div>
      <div UnSelect="YES">
        <button class="ui green button" onclick="mask()">Control</button>
        <button class="ui green button" onclick="redraw()">Reflash</button>
        <!--<p id="map"></p>-->
    </div>
    <script>
        function $(id) {
            return document.getElementById(id);
        }
        function getRandomColor(){
            return '#'+('00000'+(Math.random()*0x1000000<<0).toString(16)).substr(-6); 
        }
        function ischar(c){return (c>='a'&&c<='z')||(c>='A'&&c<='Z');}
        function isdigit(c){return c>='0'&&c<='9';}
          function ChangeToPointX(x){return parseInt((x-FunLx)/(FunRx-FunLx)*FunW);}
          function ChangeToPointY(y){return FunH-1-parseInt((y-FunLy)/(FunRy-FunLy)*FunH);}
        function priority(c){
            switch(c){
                case '(':{return 0;break;}
                case '+':{return 1;break;}
                case '-':{return 1;break;}
                case '*':{return 2;break;}
                case '/':{return 2;break;}
                case '^':{return 3;break;}
                default:{return -1;break;}
            }
        }
          function isopt(c){return priority(c)!=-1;}
        function SingleCalc(c,a,b){
            if (c=='+') return a+b;else
            if (c=='-') return a-b;else
            if (c=='*') return a*b;else
            if (c=='/') return a/b;else
            if (c=='^') return Math.pow(a,b);else
            return NaN
        }
        function FunWork(f,x){
            switch(f){
                case "":{return x;break;}
                case "SIN":{return Math.sin(x);break;}
                case "COS":{return Math.cos(x);break;}
                case "TAN":{return Math.tan(x);break;}
                case "ABS":{return Math.abs(x);break;}
                case "SQRT":{return Math.sqrt(x);break;}
                case "LN":{return Math.log(x);break;}
                case "LOG":{return Math.log(x)/Math.LN2;break;}//2为底
                case "LG":{return Math.log(x)/Math.LN10;break;}//10为底
                case "FLOOR":{return Math.floor(x);break;}
                case "CEIL":{return Math.ceil(x);break;}
                case "INT":{return parseInt(x);break;}
                default:{return NaN;break;}
            }
        }
        function FunInit(F){
              F=F.replace(/sin/g,"SIN");
              F=F.replace(/cos/g,"COS");
              F=F.replace(/tan/g,"TAN");
              F=F.replace(/abs/g,"ABS");
              F=F.replace(/sqrt/g,"SQRT");
              F=F.replace(/ln/g,"LN");
              F=F.replace(/log/g,"LOG");
              F=F.replace(/lg/g,"LG");
              F=F.replace(/floor/g,"FLOOR");
              F=F.replace(/ceil/g,"CEIL");
              F=F.replace(/int/g,"INT");
              return F;
        }
        var ca=$("graph"),el=ca.getContext("2d"),fun=$("function"),eps=1e-12;
        var FunW=ca.parentNode.clientWidth,FunH=ca.parentNode.clientHeight,FunLx=-FunW/100,FunRx=FunW/100,FunLy=-FunH/100,FunRy=FunH/100,PR,tableX,tableY,tmp,countX,countY,Funstage=0,MoX,MoY,MIN=1e-4,MAX=1e8,FUN;
          var dir=[[0,1],[1,0],[0,-1],[-1,0],[1,1],[1,-1],[-1,1],[-1,-1]];
          var FontStyle="bold 12px Georgia";
        ca.width=FunW;ca.height=FunH;$("size").innerHTML="Size:"+FunW+"*"+FunH;
        function Calc(fun,X,Value){
            var number=new Array(),opt=new Array(),F=new Array(),now=0,f="",tmp,a,b,sign=1,base=0,j;
            for (var i=0;i<fun.length;i++){
                  for (j=0;j<X.length;j++)
                if (X[j]==fun[i]){
                      if (i==0||isopt(fun[i-1])) now=Value[j];else now*=Value[j];
                      break;
                }
                  if (j==X.length)
                if (fun[i]=='(') F.push(f),f="",opt.push('(');else
                if (fun[i]==')'){
                    number.push(now*sign);now=0;sign=1;base=0;
                    while ((tmp=opt.pop())!='('){
                        b=number.pop();a=number.pop();
                        tmp=SingleCalc(tmp,a,b);
                        number.push(tmp);
                    }
                    now=FunWork(F.pop(),number.pop());
                }else
                if (fun[i]=='.') base=1;else
                if (fun[i]=='+'&&(i==0||priority(fun[i-1])!=-1));else
                if (fun[i]=='-'&&(i==0||priority(fun[i-1])!=-1)) sign=-1;else
                if (fun[i]=='e') if (i==0||isopt(fun[i-1])) now=Math.E;else now*=Math.E;else
                if (fun[i]=='p'&&fun[i+1]=='i'){if (i==0||isopt(fun[i-1])) now=Math.PI;else now*=Math.PI;i+=1;}else
                if (isdigit(fun[i])) if (base==0) now=now*10+(fun[i]-'0');else base/=10,now+=base*(fun[i]-'0');else
                if (ischar(fun[i])) f+=fun[i];else if (isopt(fun[i])){
                    number.push(now*sign);now=0;sign=1;base=0;
                    var s=priority(fun[i]);
                    if (s==-1) return 0;
                    while (s<=priority(opt[opt.length-1])){
                        b=number.pop();a=number.pop();
                        tmp=SingleCalc(opt.pop(),a,b);
                        number.push(tmp);
                    }
                    opt.push(fun[i]);
                }
            }
            number.push(now*sign);
            while (opt.length>0){
                b=number.pop();a=number.pop();
                tmp=SingleCalc(opt.pop(),a,b);
                number.push(tmp);
            }
            return number.pop();
        }
          function drawarc(x,y,R){
            el.beginPath();
            el.arc(x,y,R,0,Math.PI*2);
            el.closePath();
            el.fill();
        }
          function drawline(lx,ly,px,py){
            el.beginPath();
            el.moveTo(lx,ly);
            el.lineTo(px,py);
            el.closePath();
            el.stroke();
        }
        function gettable(){
            tmp=(FunRx-FunLx+eps)/20;
            tableX=1;countX=0;countY=0;
            while(tableX<tmp) tableX*=10;
            while(tableX/10>tmp) tableX/=10,countX++;
            if (tableX>=1) countX=0;
            if (tableX/5>tmp) tableX/=5,countX++;else if (tableX/2>tmp) tableX/=2,countX++;
            for (var i=parseInt(FunLx/tableX)+(FunLx>0);i*tableX<FunRx;i++){
                el.fillStyle=i==0?"#000000":"#CDB7B5";
                tmp=(i*tableX-FunLx)/(FunRx-FunLx)*FunW;
                el.fillRect(tmp,0,1,FunH);
                
                el.fillStyle="#000000";
                el.font=FontStyle;
                el.fillText((i*tableX).toFixed(countX),tmp+2,10);
            }
            
            
            tmp=(FunRy-FunLy+eps)/20;
            tableY=1;
            
            while(tableY<tmp) tableY*=10;
            while(tableY/10>tmp) tableY/=10,countY++;
            if (tableY/5>tmp) tableY/=5,countY++;else if (tableY/2>tmp) tableY/=2,countY++;
            if (tableY>=1) countY=0;
            for (var i=parseInt(FunLy/tableY)+(FunLy>0);i*tableY<FunRy;i++){
                el.fillStyle=i==0?"#000000":"#CDB7B5";
                tmp=(i*tableY-FunLy)/(FunRy-FunLy)*FunH;
                el.fillRect(0,FunH-1-tmp,FunW,1);
                
                el.fillStyle="#000000";
                el.font=FontStyle;
                el.fillText((i*tableY).toFixed(countY),0,FunH-1-tmp);
            }
            //$("map").innerHTML=tableX+" "+tableY;
        }
          function PCalc(i,j){return Calc(FUN,['x','y'],[FunLx+(FunRx-FunLx)/FunW*i,FunRy-(FunRy-FunLy)/FunH*j]);}
          function ImpDraw(x,y,X,Y,jump){
              if (x+X>FunW) X=FunW-x;if (y+Y>FunH) Y=FunH-y;
              var Imp=new Array(),tmp;
              if (X>jump)X/=jump;
            if (Y>jump)Y/=jump;
              for (var i=-1;i<=X;i+=1){
                  Imp[i+1]=new Array();
                for (var j=-1;j<=Y;j+=1) Imp[i+1].push(PCalc(i*jump+x,j*jump+y));
            }
              for (var i=0;i<X;i+=1)
            for (var j=0;j<Y;j+=1)
            for (var k=0;k<4;k++)
            if (Imp[i+1][j+1]*Imp[i+1+dir[k][0]][j+1+dir[k][1]]<0) {drawarc(i*jump+x,j*jump+y,PR);break;}
        }
        function getfunction(){
            var group=document.getElementsByName("Fun"),x,y,R,lax,lay,px,py,color,OutSide,type,ValueL,ValueR,ValueS,DLc,tmp,TMP;
              PR=$("PointRadii").value;
            for (var k=1;k<group.length;k++){
                var gf=group[k].parentNode.parentNode;
                  OutSide=1;type=gf.children[0].value;DLc=gf.children[4].children[0].checked;
                  FUN=FunInit((group[k].children[0].value).toLowerCase());
                  color=gf.children[1].value;
                el.fillStyle=el.strokeStyle=color;
                  switch (type){
                      case '0':{
                        for (var i=0;i<FunW;i++){
                            x=FunLx+(FunRx-FunLx)/FunW*i;
                            y=Calc(FUN,['x'],[x]);
                            if (isNaN(y)) continue;
                            px=i;py=ChangeToPointY(y);
                            if (y>=FunLy&&y<FunRy){
                                drawarc(px,py,PR);
                                  if (DLc) drawline(lax,lay,px,py);
                                OutSide=0;
                            }else{
                                  if (DLc) if (!OutSide) drawline(lax,lay,px,py);
                                OutSide=1;
                            }
                              lax=px;lay=py;
                        }
                          break;
                    }
                      case '1':{
                          ValueL=Calc(gf.children[5].children[1].children[0].value,[],[]);
                          ValueR=Calc(gf.children[5].children[3].children[0].value,[],[]);
                          ValueS=Calc(gf.children[5].children[5].children[0].value,[],[]);
                          for (var i=ValueL;i<ValueR+ValueS-eps;i+=ValueS){
                              if (i>ValueR) i=ValueR;
                              R=Calc(FUN,['t'],[i]);
                              x=R*Math.cos(i);y=R*Math.sin(i);
                              px=ChangeToPointX(x);py=ChangeToPointY(y);
                              if (FunLx<=x&&x<FunRx&&FunLy<=y&&y<FunRy){
                                drawarc(px,py,PR);
                                  if (DLc) drawline(lax,lay,px,py);
                                OutSide=0;
                            }else{
                                  if (DLc) if (!OutSide) drawline(lax,lay,px,py);
                                OutSide=1;
                            }
                              lax=px;lay=py;
                        }
                          break;
                    }
                      case '2':{
                          var SpaceW=Calc(gf.children[5].children[1].children[0].value,[],[]),
                            SpaceH=Calc(gf.children[5].children[3].children[0].value,[],[]),
                            jump=Calc(gf.children[5].children[5].children[0].value,[],[]),Imp=new Array();
                        for (var i=0;i*SpaceW<FunW;i+=1){
                              Imp[i]=new Array();
                              for (var j=0;j*SpaceH<FunH;j+=1) Imp[i].push(0);
                        }
                          for (var i=0,_i=0;i<FunW;i+=SpaceW,_i+=1)
                        for (var j=0,_j=0;j<FunH;j+=SpaceH,_j+=1){
                            //if (!Imp[_i][_j]||(!_i&&!Imp[_i-1][_j]))
                            {
                                  tmp=PCalc(i,j-1);
                                  for (var l=0;l<SpaceH&&j+l<FunH;l++)
                                   if (TMP=tmp,tmp=PCalc(i,j+l),TMP*tmp<0||Math.abs(tmp)<eps) {Imp[_i][_j]=1;if (_i) Imp[_i-1][_j]=1;break;}
                            }
                              //if (!Imp[_i][_j]||(!_j&&!Imp[_i][_j-1]))
                              {
                                tmp=PCalc(i-1,j);
                                  for (var l=0;l<SpaceW&&i+l<FunW;l++)
                                   if (TMP=tmp,tmp=PCalc(i+l,j),TMP*tmp<0||Math.abs(tmp)<eps) {Imp[_i][_j]=1;if (_j) Imp[_i][_j-1]=1;break;}
                            }
                        }
                          for (var i=0;i<Imp.length;i+=1)
                        for (var j=0;j<Imp[i].length;j+=1)
                        if (Imp[i][j]) ImpDraw(i*SpaceW,j*SpaceH,SpaceW,SpaceH,jump);
                          break;
                    }
                      case '3':{
                          ValueL=Calc(gf.children[5].children[1].children[0].value,[],[]);
                          ValueR=Calc(gf.children[5].children[3].children[0].value,[],[]);
                          ValueS=Calc(gf.children[5].children[5].children[0].value,[],[]);
                          _FUN=FunInit((group[k].parentNode.children[3].children[0].value).toLowerCase());
                          for (var i=ValueL;i<ValueR+ValueS-eps;i+=ValueS){
                              if (i>ValueR) i=ValueR;
                              x=Calc(FUN,['t'],[i]);y=Calc(_FUN,['t'],[i]);
                              px=ChangeToPointX(x);py=ChangeToPointY(y);
                              if (FunLx<=x&&x<FunRx&&FunLy<=y&&y<FunRy){
                                drawarc(px,py,PR);
                                  if (DLc) drawline(lax,lay,px,py);
                                OutSide=0;
                            }else{
                                  if (DLc) if (!OutSide) drawline(lax,lay,px,py);
                                OutSide=1;
                            }
                              lax=px;lay=py;
                        }
                          break;
                    }
                }
            }
        }
          function redraw(){
            el.clearRect(0,0,FunW,FunH);
            gettable();
            if (Funstage!=1||$("DrawMoving").checked) getfunction();
        }
        function change(){
            FunLx=parseFloat($("FunLx").value);
            FunRx=parseFloat($("FunRx").value);
            FunLy=parseFloat($("FunLy").value);
            FunRy=parseFloat($("FunRy").value);
              FontStyle=$("FontStyle").value;
            redraw();
        }
        function update(){
            $("FunLx").value=FunLx;
            $("FunRx").value=FunRx;
            $("FunLy").value=FunLy;
            $("FunRy").value=FunRy;
              $("FontStyle").value=FontStyle;
        }
        function Scale(x,y,times){
            if (x<0||x>=FunW||y<0||y>=FunH) return;
              if ($("sizelimit").checked){
                  if (times<1&&(FunRx-FunLx<MIN||FunRy-FunLy<MIN)) return;
                  if (times>1&&(FunRx-FunLx>MAX||FunRy-FunLy>MAX)) return;
            }
            x=FunLx+(FunRx-FunLx)/FunW*x;
            y=FunLy+(FunRy-FunLy)/FunH*y;
            FunLx=x-(x-FunLx)*times;FunRx=x+(FunRx-x)*times;
            FunLy=y-(y-FunLy)*times;FunRy=y+(FunRy-y)*times;
        }
        ca.onmousedown=function(ob){
            MoX=ob.layerX;MoY=ob.layerY;
            Funstage=1;
        }
        ca.onmousemove=function(ob){
            if (Funstage!=1) return;
            var NoX,NoY,det;
            NoX=ob.layerX;NoY=ob.layerY;
            det=(MoX-NoX)/FunW*(FunRx-FunLx);FunLx+=det;FunRx+=det;
            det=(NoY-MoY)/FunH*(FunRy-FunLy);FunLy+=det;FunRy+=det;
            MoX=NoX;MoY=NoY;
            redraw();update();
        }
        ca.onmouseup=function(ob){
              if (Funstage==1){
                Funstage=0;
                  redraw();
            }
        }
        ca.onmouseleave=function(ob){
              if (Funstage==1){
                Funstage=0;
                  redraw();
            }
        }
        ca.onmousewheel=function(ob){
            ob=ob||window.event;ob.preventDefault();
              var ScaleRate=$("Scale").value;
            var detail;
            if(ob.wheelDelta)detail=ob.wheelDelta;else if(ob.detail)detail=ob.detail;
            if (detail>0) Scale(ob.layerX,FunH-1-ob.layerY,ScaleRate);else Scale(ob.layerX,FunH-1-ob.layerY,1/ScaleRate);
            redraw();update();
        }
        function Add(){
            var New=$("mod").cloneNode(true);
            New.style.display="block";
              New.children[1].value=getRandomColor();
            fun.appendChild(New);
        }
        function Delete(node){
            node.parentNode.removeChild(node);
        }
        function DrawLine(ob){
            //if (ob.className=="ui checkbox checked") ob.className="ui checkbox";else ob.className="ui checkbox checked";
            redraw();
        }
          function ShowOption(obj){
              obj=obj.children[0];
              if (obj.className=="Caret Right icon") obj.className="Caret Down icon",$("option").style.display="block";else obj.className="Caret Right icon",$("option").style.display="none";
        }
          function FunctionChange(obj){
              var fi=0,la=2,v=obj.value;
              obj=obj.parentNode;
            obj.children[5].style.display="none";
            obj.children[2].style.display="inline";
            obj.children[2].children[3].style.display="none";
              if (v==0){
                  obj.children[2].children[fi].innerHTML="y=";
                  obj.children[2].children[la].innerHTML="";
            }else if (v==1){
                  obj.children[2].children[fi].innerHTML="r=";
                  obj.children[2].children[la].innerHTML="";
                  obj.children[5].style.display="block";
                  obj.children[5].children[0].innerHTML="t:";
                  obj.children[5].children[1].children[0].value="0";
                  obj.children[5].children[2].innerHTML="~";
                  obj.children[5].children[3].children[0].value="2pi";
                  obj.children[5].children[4].innerHTML=" Space:";
                  obj.children[5].children[5].children[0].value="0.02";
            }else if (v==2){
                  obj.children[2].children[fi].innerHTML="";
                  obj.children[2].children[la].innerHTML="=0";
                  obj.children[5].style.display="block";
                  obj.children[5].children[0].innerHTML="SpaceW:";
                  obj.children[5].children[1].children[0].value="60";
                  obj.children[5].children[2].innerHTML=" SpaceH:";
                  obj.children[5].children[3].children[0].value="60";
                  obj.children[5].children[4].innerHTML=" Jump:";
                  obj.children[5].children[5].children[0].value="3";
            }else if (v==3){
                obj.children[2].style.display="block";
                obj.children[2].children[3].style.display="inline";
                  obj.children[2].children[fi].innerHTML="x=";
                  obj.children[2].children[la].innerHTML=" y=";
                  obj.children[5].style.display="block";
                  obj.children[5].children[0].innerHTML="t:";
                  obj.children[5].children[1].children[0].value="0";
                  obj.children[5].children[2].innerHTML="~";
                  obj.children[5].children[3].children[0].value="1";
                  obj.children[5].children[4].innerHTML=" Space:";
                  obj.children[5].children[5].children[0].value="0.1";
                  
            }
        }
        redraw();update();
        var maskobj=$("mask");
        var control=$("control");
        function mask(){
              if (maskobj.style.display=="block") control.style.display=maskobj.style.display="none";else control.style.display=maskobj.style.display="block";
        }
        maskobj.onclick=function(){
              mask();
        }
    </script>
</div>
</body>
View Code

 

posted @ 2018-05-04 14:42  swm_sxt  阅读(7617)  评论(2编辑  收藏  举报