独自のWebGLエンジン。 記事2。 マトリックス

記事の続き。



マトリックス



マトリックスの開発を始めたばかりのとき、将来の生活がどれほど簡素化されるか想像もしていませんでした。 マトリックスには多くのプロパティがありますが、タスクではそれらすべてを1つに減らします。つまり、「カツレツからハエの分離」、つまり座標の共通配列からのポイントの配列です。 コードの観点から見ると、これは行の配列の選択になり、各行は点と列の配列、座標x、y、z、またはwのいずれかの配列になります。 単純化されたモデルがあるため、「w」は使用しません。



マトリックスを介してオブジェクトを記述したので、任意の軸に沿ってオブジェクトを簡単に移動して回転でき、オブジェクトの中心をすぐに決定することもできます。



マトリックスクラスを記述する場合、マトリックスの取得元の配列とポイントの次元を知るだけで十分です。 私は3-x、y、zに等しい次元を使用します。



したがって、コード自体。



function botuMatrix (source,columns) { this.source = source; this.columnNumbers = columns; this.rowNumbers = source.length / columns; this.rows = []; this.minval = []; this.maxval = []; this.radius = []; this.center = []; this.column = []; if (source.length > 0) { var count = 0; while(count < source.length) { var currentRow = this.source.slice(count,count + this.columnNumbers); this.rows.push(currentRow); var columnCount = 0; while(columnCount <= this.columnNumbers) { if (!this.column[columnCount]) { this.column[columnCount] = []; } this.column[columnCount].push(currentRow[columnCount]); columnCount += 1; } count = count + this.columnNumbers; } this.rowNumbers = this.rows.length; if (this.rows.length > 0) { count = 0; while(count < this.rows.length) { var tempRow = this.rows[count].slice(0); if (count == 0 ) { this.minval = tempRow.slice(0); this.maxval = tempRow.slice(0); this.radius = tempRow.slice(0); this.center = tempRow.slice(0); } if (count > 0) { var rowcount = 0; while(rowcount < tempRow.length) { this.minval.splice(rowcount,1,Math.min(this.minval[rowcount],tempRow[rowcount])); this.maxval.splice(rowcount,1,Math.max(this.maxval[rowcount],tempRow[rowcount])); this.radius.splice(rowcount,1,(this.maxval[rowcount] - this.minval[rowcount]) / 2); this.center.splice(rowcount,1,this.maxval[rowcount] - this.radius[rowcount]); rowcount = rowcount + 1; } } tempRow = null; count = count + 1; } tempRow = null; } } }
      
      







ここで最初に行と列の配列を定義しました。これは必須の部分です。

次に、マトリックスのいくつかの特性-すべての座標の中心、半径、最大(最小)値、このサイクルは別の方法(関数)で意味をなすか、必要ない場合は削除します。 マトリックスを変更する場合、将来的に「センター」が必要になります。



行列演算


これで、マトリックスの魅力がすべて明らかになりました。



引越し


 move: function(value,xyzw){ this.column[xyzw] = this.column[xyzw].map(function(i){return i+value;}) this.updateByColumn(); }
      
      







移動するとき、オブジェクトの各ポイントで必要な座標を目的の値に変更する必要があります。 xyzw-座標インデックス、値-値。 つまり、オブジェクトを10単位だけ右にシフトする必要がある場合、次のメソッドをオブジェクトのマトリックスに適用するだけで十分です。move(10,0);

変換後、マトリックス全体(updateByColumn)を更新します。



特定のポイントに移動する


 toPoint:function(point){ if (point) { if(point.length == this.columnNumbers) { this.rows = this.rows.map(function(rowArray) { return rowArray.map(function(rowElement,index) { return rowElement + point[index]; }) }); this.updateByRow(); } } }
      
      







すべての座標で一度にこの動き。 非常に便利な方法です。この記事では、特定の点を中心に回転するために使用します。次の記事では、複雑な「複合」プリミティブを構築するときにも役立ちます。





マトリックス回転


  rotate:function(angle,point,xyzType){ function multPointByValue(point,value){ return point.map(function(val){return value * val}); } this.toPoint(multPointByValue(point,-1)); var rotateSource = []; var radians = angle * Math.PI / 180.0; switch(xyzType){ case "byX": rotateSource = [1,0,0, 0,Math.cos(radians),Math.sin(radians), 0,-1 * Math.sin(radians),Math.cos(radians) ]; break; case "byY": rotateSource = [Math.cos(radians),0,-1 * Math.sin(radians), 0,1,0, Math.sin(radians),0,Math.cos(radians) ]; break; case "byZ": rotateSource = [Math.cos(radians),Math.sin(radians),0, -1 * Math.sin(radians),Math.cos(radians),0, 0,0,1]; break; } var rotateMatrix = new botuMatrix(rotateSource,3); this.rows = this.rows.map(function(irow){ return vectorByMatrix(irow,rotateMatrix); }); rotateMatrix = null; rotateSource = null; this.updateByRow(); this.toPoint(point); }
      
      







特定のxyzType軸に沿って、特定の角度で特定のポイントポイントを中心に回転します。 最初に、マトリックスを回転のあるポイントに移動し、次に回転のあるxyzType軸に応じて回転マトリックスを形成します。 オブジェクトの各ポイント(ライン)を展開し、その後、展開されたマトリックスを開始ポイントに移動します。



マトリックス更新


マトリックスには3つの主要な変数があります。 配列全体、ポイント行(行)の配列、座標列(列)の配列。 これらの配列の1つを変更する場合、他の配列を更新する必要があります。この2つの方法が使用されます



 updateByColumn:function(){ var columnCount = 0; while(columnCount < this.columnNumbers) { var rowCount = 0; while(rowCount < this.rowNumbers) { this.rows[rowCount][columnCount] = this.column[columnCount][rowCount]; this.source[columnCount + rowCount * this.columnNumbers] = this.column[columnCount][rowCount]; rowCount++; } columnCount++; } }, updateByRow:function(){ var rowCount = 0; while(rowCount < this.rowNumbers) { var columnCount = 0; while(columnCount < this.columnNumbers) { this.column[columnCount][rowCount] = this.rows[rowCount][columnCount]; this.source[columnCount + rowCount * this.columnNumbers] = this.column[columnCount][rowCount]; columnCount++; } columnCount = null; rowCount++; } columnCount = null; rowCount = null; },
      
      







vectorByMatrix関数は、ベクトルとマトリックスの乗算です。マトリックスを回転するときに使用し、マトリックスクラスの外側に移動しました。この関数は静的であると考えました。 ベクトルのクラスを別に作成する必要がある場合、この関数はベクトルのプロトタイプになります。



 function vectorByMatrix(vector,matrix) { //alert(vector); var resultVector = []; if (vector.length == matrix.rowNumbers) { var columnCount = 0; while(columnCount < matrix.columnNumbers){ var rowCount = 0; var value = 0; while(rowCount < matrix.rowNumbers) { value += vector[rowCount] * matrix.column[columnCount][rowCount]; rowCount++; } //alert(value); resultVector.push(value); columnCount++; } } return resultVector; }
      
      







完全なコード:

 /*  vector   matrix.   - resultVector*/ function vectorByMatrix(vector,matrix) { var resultVector = []; if (vector.length == matrix.rowNumbers) { var columnCount = 0; while(columnCount < matrix.columnNumbers){ var rowCount = 0; var value = 0; while(rowCount < matrix.rowNumbers) { value += vector[rowCount] * matrix.column[columnCount][rowCount]; rowCount++; } resultVector.push(value); columnCount++; } } return resultVector; } /* .  ,  source  - ,       - columns*/ function botuMatrix (source,columns) { this.source = source; //   this.columnNumbers = columns; //  .   this.rowNumbers = source.length / columns; // .   this.rows = []; // .  . this.minval = []; //        this.maxval = []; //        this.radius = []; //        this.center = []; // -. this.column = []; //   - . this.column[0] -    x   . /*        .*/ if (source.length > 0) { var count = 0; /*   - rows    column*/ while(count < source.length) { /* */ var currentRow = this.source.slice(count,count + this.columnNumbers); /*     */ this.rows.push(currentRow); var columnCount = 0; /*  ,     -     . */ while(columnCount <= this.columnNumbers) { /*   */ if (!this.column[columnCount]) { this.column[columnCount] = []; } /*       .*/ this.column[columnCount].push(currentRow[columnCount]); columnCount += 1; } /*   */ count = count + this.columnNumbers; } this.rowNumbers = this.rows.length; /*   - ,  ,  */ if (this.rows.length > 0) { count = 0; /*    - */ while(count < this.rows.length) { /*      */ var tempRow = this.rows[count].slice(0); if (count == 0 ) { this.minval = tempRow.slice(0); this.maxval = tempRow.slice(0); this.radius = tempRow.slice(0); this.center = tempRow.slice(0); } /*      .*/ if (count > 0) { var rowcount = 0; while(rowcount < tempRow.length) { this.minval.splice(rowcount,1,Math.min(this.minval[rowcount],tempRow[rowcount])); this.maxval.splice(rowcount,1,Math.max(this.maxval[rowcount],tempRow[rowcount])); this.radius.splice(rowcount,1,(this.maxval[rowcount] - this.minval[rowcount]) / 2); this.center.splice(rowcount,1,this.maxval[rowcount] - this.radius[rowcount]); rowcount = rowcount + 1; } } /*       */ tempRow = undefined; count = count + 1; } /* .*/ tempRow = undefined; } } } /*  */ botuMatrix.prototype = { /* */ move: function(value,xyzw){ /*      */ this.column[xyzw] = this.column[xyzw].map(function(i){return i+value;}) /*     */ this.updateByColumn(); }, /*    */ updateByColumn:function(){ var columnCount = 0; /*   */ while(columnCount < this.columnNumbers) { var rowCount = 0; /*         ,     */ while(rowCount < this.rowNumbers) { this.rows[rowCount][columnCount] = this.column[columnCount][rowCount]; this.source[columnCount + rowCount * this.columnNumbers] = this.column[columnCount][rowCount]; rowCount++; } columnCount++; } }, /*    */ updateByRow:function(){ var rowCount = 0; /*   */ while(rowCount < this.rowNumbers) { var columnCount = 0; /*         ,     */ while(columnCount < this.columnNumbers) { this.column[columnCount][rowCount] = this.rows[rowCount][columnCount]; this.source[columnCount + rowCount * this.columnNumbers] = this.column[columnCount][rowCount]; columnCount++; } columnCount = undefined; rowCount++; } columnCount = undefined; rowCount = undefined; }, /*     - point*/ toPoint:function(point){ if (point) { if(point.length == this.columnNumbers) { /* -      - point. */ this.rows = this.rows.map(function(rowArray){ return rowArray.map(function(rowElement,index) { return rowElement + point[index]; } ) }); /*     */ this.updateByRow(); } } }, /*  this   angle,   point   - xyzType*/ rotate:function(angle,point,xyzType){ /*   */ function multPointByValue(point,value){ return point.map(function(val){return value * val}); } /*  ,   point,        [0,0,0]*/ this.toPoint(multPointByValue(point,-1)); //    var rotateSource = []; //     var radians = angle * Math.PI / 180.0; //      switch(xyzType){ case "byX": //    ,   X rotateSource = [1,0,0, 0,Math.cos(radians),Math.sin(radians), 0,-1 * Math.sin(radians),Math.cos(radians) ]; break; case "byY": //    ,   y rotateSource = [Math.cos(radians),0,-1 * Math.sin(radians), 0,1,0, Math.sin(radians),0,Math.cos(radians) ]; break; case "byZ": //    ,   Z rotateSource = [Math.cos(radians),Math.sin(radians),0, -1 * Math.sin(radians),Math.cos(radians),0, 0,0,1]; break; } //   var rotateMatrix = new botuMatrix(rotateSource,3); //  ,    -  rows[i]    this.rows = this.rows.map(function(irow){ return vectorByMatrix(irow,rotateMatrix); }); //       .   .   .     . rotateMatrix = null; //     rotateSource = null; /*    -,   */ this.updateByRow(); //    . this.toPoint(point); } }
      
      







おわりに


botuMatrixクラスは、プリミティブのヘルパーです。 このマトリックスで説明されたすべてのメソッドは、プリミティブのメソッド内で使用されます。



次の記事では、キューブ、ボール、平面などのプリミティブについて検討します。



All Articles