2020年12月29日 星期二

製作數學公式圖片的工具網站

 製作數學公式圖片的工具網站

前言

  在寫一些程式文章時會需要表達數學公式,但數學公式並不能在 HTML 直接輸入,但我找到有個網站可以編輯數學公式並且將結果做成圖片下載,在此把學習的過程做個紀錄。

 

內容

  使用這個工具要到 [ latex.codecogs.com ] Online LaTeX Equation Editor 裡,使用的畫面如下

Online LaTeX Equation Editor 的使用畫面


在畫面"1"的地方輸入語法來產生等式,結果會顯示在畫面"2"的地方,最後透過畫面"3"的地方可以下載結果。畫面示範了如何使用根號,語法不用記,上方有工具列,透過滑鼠點選需要的語法,如下圖

透過工具列來家語法,以根號為例

圖中以根號為例會加"\sqrt{}",內容放哪要自己試一下,利用下方的顯示結果試一下即可。


參考資料

[ latex.codecogs.com ] Online LaTeX Equation Editor

2020年12月22日 星期二

向量正規化的注意事項

 向量正規化的注意事項

前言

  最近的專案遽然發生了除0中斷,結果追了一下發生的地方,結果在自己寫的向量函示庫裡的向量正規化這裡發生,這裡把學習的過程做個紀錄。


內容

  計算向量正規化時,會先計算該向量長度,長度公式如下

向量長度公式

計算完長度後再將每個數值除以長度就可以完成正規化,但如果這個長度很小或根本是"0"的話就會發生問題,解決的方法是計算完長度後,如果小於"FLT_EPSILON"就放棄計算(或直接在結果填上零向量),"FLT_EPSILON"是 C++ 的標準 Macro ,可以解釋成 float 資料型態的最小精度。


  向量函示庫自己寫就是會碰到這種麻煩,不過也可以在這樣的過程中學習,這次可真是上了一課。


2020年12月15日 星期二

用佚代的方式瀏覽陣列

 用佚代的方式瀏覽陣列

前言

  在較新的 ECMAScript 標準中支援了用佚代的方式來瀏覽陣列,但由於舊的 For 迴圈語法相當強大,所以一直覺得沒必要更換,但由於新標準推行已經很久了,現在看別人的程式碼會看到佚代,就算自己不用也要看得懂別人的程式碼,所以就來研究學習,在此做個紀錄。


內容

  目前常看到的有 forEach() 、 map() 與 filter() ,這次會示範使用的範例與用 for 迴圈地等效範例。


  首先看到 forEach() 的用法,範例如下

let ar = [ 1 , 2 , 3 ];
ar.forEach( function( ele ){
  console.log( ele );
} );
//
//1
//2
//3


for 迴圈的等效程式碼如下

let ar = [ 1 , 2 , 3 ];
for(let i = 0 ; i < ar.length ; i++)
  console.log( ar[i] );
//
//1
//2
//3


相比 for 迴圈的作法, forEach() 比較簡潔,但沒辦法改變瀏覽對象,一定是全部的對象。接著看到 map() ,範例如下

let ar = [ 1 , 2 , 3 ];

let res = ar.map( function(ele){
  return ele ;
} );
console.log( res );
//
//[1, 2, 3]


for 迴圈的等效程式碼如下

let ar = [ 1 , 2 , 3 ];

let res = [];
for( let i = 0 ; i < ar.length ; i++ )
  res.push( ar[i] );
//
console.log( res );
//
//[1, 2, 3]


map() 相比之下少了變數宣告的過程,直接取得回傳值就可以了,但瀏覽的對象一定是全部,而且也沒辦法不回傳要推入的對象,也就是說 map() 的會傳的陣列數量會和原來的陣列一樣。最後看到 filter() ,範例如下

let ar = [ 1 , 2 , 3 ];

let res = ar.filter( function(ele){
  return ele > 1;
} );
console.log( res );
//
//[2, 3]



for 迴圈的等效程式碼如下

let ar = [ 1 , 2 , 3 ];

let res = [];
for( let i = 0 ; i < ar.length ; i++){
  if( ar[i] > 1)
    res.push( ar[i] );
}
  
console.log( res );
//
//[2, 3]


filter() 可以對新產生的陣列做出篩選,但內容無法運算,而 map() 可以對要輸出的對象做運算,但無法篩選要輸出的對象。


參考資料

[ developer.mozilla.org ] forEach

[ developer.mozilla.org ] map

[ developer.mozilla.org ] filter

2020年12月8日 星期二

方便產生 HTML 表格( Table )的工具

 方便產生 HTML 表格( Table )的工具

前言

  在寫 Blog 時有時需要用到表格,但工具列裡沒有表格( Table )的工具可以用,但可以用 HTML 的方式來插入,但 HTML 的表格( Table )直接用非常不方便,所以這次找了可以產生 HTML 的表格( Table )的工具。


內容

  這次介紹的工具是線上工具,到 [ www.tablesgenerator.com ] Tables Generator 就可以使用,進入後可看到下圖

Table Generator 的使用畫面


畫面看起來很複雜,但使用起來並不難懂,在畫面"3"的地方可以預視表格結果,要新增或移除欄位可以在畫面"1"地方就可以操作,在畫面"3"點擊或圈選欄位可以透過畫面"2"的地方修改欄位的屬性如字型、顏色...等,當完成 Table 後,按下"Generator"按鈕,可以在畫面"5"的地方得到 HTML 結果,在畫面"4"的地方記得勾選不要產生 CSS ,不然會產生多餘的 CSS 程式碼。


參考資料

[ www.tablesgenerator.com ] Tables Generator



2020年12月1日 星期二

WebGL的繪製三次方赫密特曲線( Cubic hermite curve )的切線

 WebGL的繪製三次方赫密特曲線( Cubic hermite curve )的切線

前言

  在先前的 WebGL的繪製三次方貝茲曲線( Cubic bezier curve )的切線 裡繪製了貝茲曲線的切線,這次來繪製三次方赫密特曲線( Cubic hermite curve )的切線,在此把學習的過程做個紀錄。


內容

  在先前的 WebGL的繪製三次方赫密特曲線( Cubic hermite curve ) 裡已經有一個繪製曲線的範例,這次的範例會從該專案改出來。在之前不確定"微分"後就可以得到切線,這次算是得到一個驗證,所以如果將赫密特曲線拿去為分會得到以下

切線公式

接著將這個公式套用到先前的範例,程式碼如下

HTML 的部分

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width">
  <title>JS Bin</title>
</head>
<body>
<canvas id="myCanvas1" width=400 height=300></canvas>
<br>
<input id="btnDrawCtrlPoints"type="button" value="DrawCtrlPoints"/>
<br>
<input id="btnDrawCubicHermiteCurve"type="button" value="DrawCubicHermiteCurve"/>
<br>
<input id="btnDrawCubicBezierCurve"type="button" value="DrawCubicBezierCurve"/>
<input type="range" min="2" max="100" value="30" class="slider" id="sliderLerp">
<label id="labelLerpValue">30</label>
<br>
<input type="checkbox" id="isShowTangent" checked><label>Show tangent </label>
<input type="range" min="0" max="1" value="0.5" step="0.001" class="slider" id="sliderTangent">
<label id="labelTangentValue">0.5</label>
</body>
</html>



Javascript 的部分

let canvas1 = document.getElementById('myCanvas1');
let glCTX1 = canvas1.getContext('webgl');
let vboPrimitiveCon = 0;
let vbo=createDynamicBuffer(glCTX1);
let shaderProg = createShader(glCTX1);
let curveType = 0;
//
function createShader(glContext){
  let vertShader = glContext.createShader(glContext.VERTEX_SHADER);
  glContext.shaderSource(
    vertShader , 
    'attribute vec3 pos;void main(void){gl_Position=vec4(pos, 1.0);}'
  );
  glContext.compileShader(vertShader);
  let fragShader = glContext.createShader(glContext.FRAGMENT_SHADER);
  glContext.shaderSource(
    fragShader, 
    'void main(void){gl_FragColor=vec4(1,1,1,1);}'
  );
  glContext.compileShader(fragShader);
  let prog = glContext.createProgram();
  glContext.attachShader(prog, vertShader);
  glContext.attachShader(prog, fragShader);
  glContext.linkProgram(prog);
  glContext.useProgram(prog);  
  
  return prog;
}
function createDynamicBuffer(glContext){
  let vertexBuf = glContext.createBuffer();
  glContext.bindBuffer(glContext.ARRAY_BUFFER, vertexBuf);
  let dataArray=new Float32Array([ 
       0.0, 0.5, 0.0,  
      -0.5,-0.5, 0.0,  
      -0.5,-0.5, 0.0,
       0.5,-0.5, 0.0,
       0.5,-0.5, 0.0,
       0.0, 0.5, 0.0
    ]);
  glContext.bufferData(
    glContext.ARRAY_BUFFER, 
    3000, 
    glContext.DYNAMIC_DRAW
  );
  //write deafult data...
  glCTX1.bufferSubData(glCTX1.ARRAY_BUFFER,0,dataArray);
  vboPrimitiveCon = dataArray.length / 3;
  return vertexBuf;
}
function simpleDraw(glContext){
  glContext.useProgram(shaderProg);
  //
  glContext.viewport(0,0,glContext.canvas.width,glContext.canvas.height);
  glContext.clearColor(0, 0, 1, 1);
  glContext.clear(glContext.COLOR_BUFFER_BIT);
  //
  glContext.bindBuffer(glContext.ARRAY_BUFFER, vbo);
  let posLoc = glContext.getAttribLocation(shaderProg, "pos");
  glContext.vertexAttribPointer(posLoc, 3, glContext.FLOAT, false, 0, 0);
  glContext.enableVertexAttribArray(posLoc);

  glContext.drawArrays(glContext.LINES, 0, vboPrimitiveCon);
}
function generateLineListData(ar){
  let tagAr=[];
  let mod=ar.length%3;
  let elementAmount=(ar.length-mod)/3;
  if(elementAmount>=2 && mod===0){
    tagAr.push(ar[0]);
    tagAr.push(ar[1]);
    tagAr.push(ar[2]);
    //
    for(let i=3;i<(ar.length-3);i+=3){
      tagAr.push(ar[i]);
      tagAr.push(ar[i+1]);
      tagAr.push(ar[i+2]);
      //
      tagAr.push(ar[i]);
      tagAr.push(ar[i+1]);
      tagAr.push(ar[i+2]);
    }
    //
    tagAr.push(ar[ar.length-3]);
    tagAr.push(ar[ar.length-2]);
    tagAr.push(ar[ar.length-1]);
  }
  return new Float32Array(tagAr);
}
function generateLineListArrayData(ar){
  let tagAr=[];
  let mod=ar.length%3;
  let elementAmount=(ar.length-mod)/3;
  if(elementAmount>=2 && mod===0){
    tagAr.push(ar[0]);
    tagAr.push(ar[1]);
    tagAr.push(ar[2]);
    //
    for(let i=3;i<(ar.length-3);i+=3){
      tagAr.push(ar[i]);
      tagAr.push(ar[i+1]);
      tagAr.push(ar[i+2]);
      //
      tagAr.push(ar[i]);
      tagAr.push(ar[i+1]);
      tagAr.push(ar[i+2]);
    }
    //
    tagAr.push(ar[ar.length-3]);
    tagAr.push(ar[ar.length-2]);
    tagAr.push(ar[ar.length-1]);
  }
  return tagAr;
}
function generateBezierCurve(p0,p1,p2,lerp){
  if(lerp < 2)
    return [];
  //
  let tagAr = [];
  for(let i=0;i < lerp;i++){
    let t = i/(lerp-1);
    let invT = 1.0-t;
    let part0Value = invT * invT;
    let part1Value = 2 * t * invT;
    let part2Value = t * t;
    let part0 = [part0Value*p0[0], part0Value*p0[1], part0Value*p0[2] ];
    let part1 = [part1Value*p1[0], part1Value*p1[1], part1Value*p1[2] ];
    let part2 = [part2Value*p2[0], part2Value*p2[1], part2Value*p2[2] ];
    tagAr.push(part0[0] + part1[0] + part2[0]);
    tagAr.push(part0[1] + part1[1] + part2[1]);
    tagAr.push(part0[2] + part1[2] + part2[2]);
  }
  return tagAr;
}
function generateCubicBezierCurve(p0,p1,p2,p3,lerp){
  if(lerp < 2)
    return [];
  //
  let tagAr = [];
  for(let i=0;i < lerp;i++){
    let t = i/(lerp-1);
    let invT = 1.0-t;
    let part0Value = invT * invT * invT;
    let part1Value = 3 * t * invT * invT;
    let part2Value = 3 * t * t * invT;
    let part3Value = t * t * t;
    let part0 = [part0Value*p0[0], part0Value*p0[1], part0Value*p0[2] ];
    let part1 = [part1Value*p1[0], part1Value*p1[1], part1Value*p1[2] ];
    let part2 = [part2Value*p2[0], part2Value*p2[1], part2Value*p2[2] ];
    let part3 = [part3Value*p3[0], part3Value*p3[1], part3Value*p3[2] ];
    tagAr.push(part0[0] + part1[0] + part2[0] + part3[0]);
    tagAr.push(part0[1] + part1[1] + part2[1] + part3[1]);
    tagAr.push(part0[2] + part1[2] + part2[2] + part3[2]);
  }
  return tagAr;
}
function calCubicHermiteCurvePosition(p0,s0,p1,s1,t){
  let invT = 1.0-t;
  let part0Value = (1 + ( 2 * t ) ) * ( invT * invT );
  let part1Value = t * ( ( invT * invT ) );
  let part2Value = ( t * t ) * ( 3 - ( 2 * t) );
  let part3Value = ( t * t ) * ( t - 1.0 );
  let part0 = [part0Value*p0[0], part0Value*p0[1], part0Value*p0[2] ];
  let part1 = [part1Value*s0[0], part1Value*s0[1], part1Value*s0[2] ];
  let part2 = [part2Value*p1[0], part2Value*p1[1], part2Value*p1[2] ];
  let part3 = [part3Value*s1[0], part3Value*s1[1], part3Value*s1[2] ];
  let position = [
    part0[0] + part1[0] + part2[0] + part3[0],
    part0[1] + part1[1] + part2[1] + part3[1],
    part0[2] + part1[2] + part2[2] + part3[2]
  ];
  return position;
}
function generateCubicHermiteCurve(p0,s0,p1,s1,lerp){
  if(lerp < 2)
    return [];
  //
  let tagAr = [];
  for(let i=0;i < lerp;i++){
    let t = i/(lerp-1);
    let pos = calCubicHermiteCurvePosition(p0,s0,p1,s1,t);
    tagAr.push( pos[0] );
    tagAr.push( pos[1] );
    tagAr.push( pos[2] );
  }
  return tagAr;
}
function calCubicHermiteCurveTangent(p0,s0,p1,s1,t){
  let part0Value = ( 6 * t * t ) - ( 6 * t);
  let part1Value = ( 3 * t * t ) - ( 4 * t) + 1;
  let part2Value = ( 6 * t) - ( 6 * t * t );
  let part3Value = ( 3 * t * t ) - ( 2 * t );
  let part0 = [part0Value*p0[0], part0Value*p0[1], part0Value*p0[2] ];
  let part1 = [part1Value*s0[0], part1Value*s0[1], part1Value*s0[2] ];
  let part2 = [part2Value*p1[0], part2Value*p1[1], part2Value*p1[2] ];
  let part3 = [part3Value*s1[0], part3Value*s1[1], part3Value*s1[2] ];
  let tangent = [
    part0[0] + part1[0] + part2[0] + part3[0],
    part0[1] + part1[1] + part2[1] + part3[1],
    part0[2] + part1[2] + part2[2] + part3[2]
  ];
  return tangent;
}
function myRender(){
  simpleDraw(glCTX1);
  //
  window.requestAnimationFrame(myRender);
}
//
let ctrlPointList=[
  [-0.9,0.0,0.0],
  [-0.45,0.9,0.0],
  [0.45,0.9,0.0],
  [0.9,0.0,0.0],
];
let tagLerpValue=document.getElementById("sliderLerp").value;
let tagTangentValue=document.getElementById("sliderTangent").value;
function UpdateCubicBezierCurveData(){
  let data=generateCubicBezierCurve(
    ctrlPointList[0],
    ctrlPointList[1],
    ctrlPointList[2],
    ctrlPointList[3],
    tagLerpValue);
  let dataArray=generateLineListData(data);
  glCTX1.bindBuffer(glCTX1.ARRAY_BUFFER, vbo);
  glCTX1.bufferSubData(glCTX1.ARRAY_BUFFER,0,dataArray);
  vboPrimitiveCon = dataArray.length / 3;
}
function UpdateCubicHermiteCurveData(){
  let data=generateCubicHermiteCurve(
    ctrlPointList[0],
    ctrlPointList[1],
    ctrlPointList[3],
    ctrlPointList[2],
    tagLerpValue);
  //
  let dataArray=generateLineListArrayData(data);
  let isShowTangent = document.getElementById("isShowTangent").checked;
  if(isShowTangent){    
    let tagT = tagTangentValue;
    let pos = calCubicHermiteCurvePosition(
      ctrlPointList[0],
      ctrlPointList[1],
      ctrlPointList[3],
      ctrlPointList[2],
      tagT
    );
    let tangent = calCubicHermiteCurveTangent(
      ctrlPointList[0],
      ctrlPointList[1],
      ctrlPointList[3],
      ctrlPointList[2],
      tagT
    );
    dataArray.push(pos[0]);
    dataArray.push(pos[1]);
    dataArray.push(pos[2]);
    dataArray.push(pos[0]+tangent[0]);
    dataArray.push(pos[1]+tangent[1]);
    dataArray.push(pos[2]+tangent[2]);
  }

  
  let tagDataArray = new Float32Array( dataArray );
  glCTX1.bindBuffer(glCTX1.ARRAY_BUFFER, vbo);
  glCTX1.bufferSubData(glCTX1.ARRAY_BUFFER,0,tagDataArray);
  vboPrimitiveCon = dataArray.length / 3;
}
document.getElementById("btnDrawCtrlPoints").onclick=function(evt){
  let data = [];
  for(let i=0;i<ctrlPointList.length;i++)
    data.push(...ctrlPointList[i]);
  //
  let dataArray=generateLineListData(data);
  glCTX1.bindBuffer(glCTX1.ARRAY_BUFFER, vbo);
  glCTX1.bufferSubData(glCTX1.ARRAY_BUFFER,0,dataArray);
  vboPrimitiveCon = dataArray.length / 3;
}
document.getElementById("btnDrawCubicBezierCurve").onclick=function(evt){
  UpdateCubicBezierCurveData();
  curveType = 1;
}
document.getElementById("btnDrawCubicHermiteCurve").onclick=function(evt){
  UpdateCubicHermiteCurveData();
  curveType = 2;
}
document.getElementById("sliderLerp").oninput=function(evt){
  tagLerpValue=this.value;
  //UpdateCurveData();
  switch( curveType ){
    case 1:
      UpdateCubicBezierCurveData();
      break;
    case 2:
      UpdateCubicHermiteCurveData();
      break;
      
  }
  document.getElementById("labelLerpValue").innerHTML = this.value;
}
document.getElementById("isShowTangent").onclick=function(){
  UpdateCubicHermiteCurveData();
}
document.getElementById("sliderTangent").oninput=function(evt){
  tagTangentValue=this.value;
  UpdateCubicHermiteCurveData();
  document.getElementById("labelTangentValue").innerHTML = this.value;
}
window.onload = function(){
  window.requestAnimationFrame(myRender);
  
}


執行結果如下

範例的執行結果


這次的改法跟 WebGL的繪製三次方貝茲曲線( Cubic bezier curve )的切線 的改法是一樣的,所以做不多做說明。


  這次算是對微分後得到切線方程式做一個驗證,以後碰到的曲線方程式都可以利用微分得到切線,算是這次的收穫。


參考資料

[ wiki ] Cubic Hermite spline


相關文章與資料

WebGL的繪製三次方赫密特曲線( Cubic hermite curve )

WebGL的繪製三次方貝茲曲線( Cubic bezier curve )的切線