Skip to content

Basic Tutorials

NAS6mixfoolv edited this page Jun 7, 2025 · 90 revisions

Basic Tutorials

Back to Table of contents


Minimal tutorial

  • Quick Start

    • 1. Load the Library

Include the necessary JavaScript files in your HTML. You'll typically need vector.js as core components.
Add other modules like matrix.js, quaternion.js, planet.js, etc., as needed for your specific use case.

HTML

  
<script src="https://nas6mixfoolv.github.io/NAS6LIB/javascripts/nas6lib/vector.js"></script>  
  
  
  • 2. Minimum Sample (2D Vector Addition)
    Here's a simple JavaScript example to get started:

JavaScript

  
var v1 = new N6LVector([1, 2]);  
var v2 = new N6LVector([3, 4]);  
var v3 = v1.Add(v2);  
console.log(v3.x); // Result: [4, 6]  
  

Matrices and Vectors (Fundamental Operations)

Back to Table of contents

* N6LMatrix & N6LVector basics

Loading the Library

HTML

  
<script src="https://nas6mixfoolv.github.io/NAS6LIB/javascripts/nas6lib/vector.js"></script>  
<script src="https://nas6mixfoolv.github.io/NAS6LIB/javascripts/nas6lib/matrix.js"></script>  
<script src="https://nas6mixfoolv.github.io/NAS6LIB/javascripts/nas6lib/quaternion.js"></script>  
  
  

* Creation, initialization

Back to Table of contents

elements order is w-x-y-z-...

JavaScript

//Fourth-order vector
var veca = new N6LVector(4);
//Homo fourth-order vector
var vecb = new N6LVector(4, true);
//Third-order vector
var vecc = new N6LVector(new Array(1, 2, 3));
//x-axis unit homo fourth-order vector
var vecd = new N6LVector(new Array(1, 1, 0, 0), true);
var vece = new N6LVector([1, 1, 0, 0], true);
var vecf = new N6LVector(4, true).UnitVec(1);
//zero homo fourth-order vector
var vecg = new N6LVector(new Array(1, 0, 0, 0), true);
var vech = new N6LVector([1, 0, 0, 0], true);
var veci = new N6LVector(4, true).ZeroVec();
//deep copy
var vecf = new N6LVector(veci);

//4 rows and 4 columns
var mata = new N6LMatrix(4);
//4 rows and 8 columns
var matb = new N6LMatrix(4, 8);
//4 rows and 4 columns unit matrix
var matc = new N6LMatrix(new Array(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1), 4, 4);
var matd = new N6LMatrix(new Array(
      new N6LVector(new Array(1, 0, 0, 0)),
      new N6LVector(new Array(0, 1, 0, 0)), 
      new N6LVector(new Array(0, 0, 1, 0)),
     new N6LVector(new Array(0, 0, 0, 1)) ));
var mate = new N6LMatrix([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]] );
//deep copy
var matf = new N6LMatrix(mate); 
/*
note:Fourth-order or more is considered to be a homogeneous coordinates
If you want to make a fourth-order or more conventional coordinate N6LMatrix.SetHomo(false)**
Please continue to build declaration
*/

* Addition, subtraction, multiplication (vectors and matrices, matrices and matrices), Division (for convenience)

Back to Table of contents

JavaScript

//Vector
/*
When bHomo=true, the w element is skipped.
*/

//[1, 2] + [3, 4] = [4, 6]
var v1 = new N6LVector([1, 2]);
var v2 = new N6LVector([3, 4]);
var v3 = v1.Add(v2);
console.log(v3.x); // Result: [4, 6]  
//[1, 2] + 3 = [4, 5]
var v4 = v1.Add(3);
console.log(v4.x); // Result: [4, 5]  

//[3, 4] - [1, 2] = [2, 2]
var v5 = v2.Sub(v1);
console.log(v5.x); // Result: [2, 2]  
//[3, 4] - 2 = [1, 2]
var v6 = v2.Sub(2);
console.log(v6.x); // Result: [1, 2]  

//[1, 2] * [3, 4] = 11
var n1 = v1.Mul(v2);
console.log(n1); // Result: 11  
//[1, 2] * [[1, 2], [3, 4]] = [7, 10]
var m1 = new N6LMatrix([[1, 2], [3, 4]]);
var v7 = v1.Mul(m1);
console.log(v7.x); // Result: [7, 10]  
//[1, 2] * 2 = [2, 4]
var v8 = v1.Mul(2);
console.log(v8.x); // Result: [2, 4]  

//[3, 4] / [1, 2] = 5
var n2 = v2.Div(v1);
console.log(n2); // Result: 5  
//[3, 4] / [[1, 2], [3, 4]] = [4.333..., 2.5]
var v9 = v2.Div(m1);
console.log(v9.x); // Result: [4.333..., 2.5]  
//[3, 4] / 2 = [1.5, 2]
var v10 = v2.Div(2);
console.log(v10.x); // Result: [1.5, 2]  

//Matrix

//[[1, 2], [3, 4]] + [[2, 4], [6, 8]] = [[3, 6], [9, 12]]
var m2 = new N6LMatrix([[2, 4], [6, 8]]);
var m3 = m1.Add(m2);
console.log(m3.x); // Result: [[3, 6], [9, 12]]  
//[[1, 2], [3, 4]] + 2 = [[3, 4], [5, 6]]
var m4 = m1.Add(2);
console.log(m4.x); // Result: [[3, 4], [5, 6]]  

//[[2, 4], [6, 8]] - [[1, 2], [3, 4]] = [[1, 2], [3, 4]]
var m5 = m2.Sub(m1);
console.log(m5.x); // Result: [[1, 2], [3, 4]]  
//[[2, 4], [6, 8]] - 1 = [[1, 3], [5, 7]]
var m6 = m2.Sub(1);
console.log(m6.x); // Result: [[1, 3], [5, 7]]  

//[[1, 2], [3, 4]] * [[2, 4], [6, 8]] = [[14, 20], [30, 44]]
var m7 = m1.Mul(m2);
console.log(m7.x); // Result: [[14, 20], [30, 44]]  
//[[1, 2], [3, 4]] * [1, 2] = [5, 11]
var v11 = m1.Mul(v1);
console.log(v11.x); // Result: [5, 11]  
//[[1, 2], [3, 4]] * 2 = [[2, 4], [6, 8]]
var m8 = m1.Mul(2);
console.log(m8.x); // Result: [[2, 4], [6, 8]]

//[[2, 4], [6, 8]] / [[1, 2], [3, 4]] = [[3.333..., 2], [8.666..., 5]]
var m9 = m2.Div(m1);
console.log(m9.x); // Result: [[3.333..., 2], [8.666..., 5]]  
//[[2, 4], [6, 8]] / [1, 2] = [4, 10]
var v12 = m2.Div(v1);
console.log(v12.x); // Result: [4, 10]  
//[[2, 4], [6, 8]] / 2 = [[1, 2], [3, 4]]
var m10 = m2.Div(2);
console.log(m10.x); // Result: [[1, 2], [3, 4]]  

* Normalization, Absolute Value

Back to Table of contents

JavaScript

//Vector

//[3, 4].Abs() = 5
var v1 = new N6LVector([3, 4]);
var n1 = v1.Abs();  
console.log(n1); // Result: 5  
//[3, 4].Normal() = [3/5=0.6, 4/5=0.8]
var v2 = v1.NormalVec();
console.log(v2.x); // Result: [3/5=0.6, 4/5=0.8]
//[3, 4].Normal([6, 8]) = [3/5=0.6, 4/5=0.8]
var v3 = new N6LVector([6, 8]);
var v4 = v1.NormalVec(v3);
console.log(v4.x); // Result: [3/5=0.6, 4/5=0.8]

//Matrix

//[[2, 0], [0, 2]].Normal() = [[1, 0], [0, 1]]
var m1 = new N6LMatrix([[2, 0], [0, 2]]);
var m2 = m1.NormalMat();
console.log(m2.x); // Result: [[1, 0], [0, 1]]

* Inner product, cross product (Vector)

Back to Table of contents

JavaScript

  
//Vector

//[3, 4].Dot([5, 6]) = 3*5+4*6=39
var v1 = new N6LVector([3, 4]);
var v2 = new N6LVector([5, 6]);
var n1 = v1.Dot(v2);  
console.log(n1); // Result: 3*5+4*6=39  
//[3, 4].Cross([5, 6]) = 3*6-4*5=-2
var n2 = v1.Cross(v2);
console.log(n2); // Result: 3*6-4*5=-2

* Transpose, inverse matrix (Matrix)

Back to Table of contents

JavaScript

//Matrix

//[[1, 0, 0, 0], [2, 0, 0, 1], [3, 0, 1, 0], [4, -1, 0, 0]].Transpose() = [[1, 2, 3, 4], [0, 0, 0, -1], [0, 0, 1, 0], [0, 1, 0, 0]]
//[[1, 0, 0, 0], [2, 0, 0, 1], [3, 0, 1, 0], [4, -1, 0, 0]].Inverse() = [[1, 0, 0, 0], [2, 0, 0, -1], [3, 0, 1, 0], [4, 1, 0, 0]]
var m1 = new N6LMatrix([[1, 0, 0, 0], [2, 0, 0, 1], [3, 0, 1, 0], [4, -1, 0, 0]]);
var m2 = m1.TransposedMat();
console.log(m2.x); // Result: [[1, 2, 3, 4], [0, 0, 0, -1], [0, 0, 1, 0], [0, 1, 0, 0]]
var dt = [];
var m3 = m1.InverseMat(dt);
console.log(m3.x); // Result: [[1, 0, 0, 0], [2, 0, 0, -1], [3, 0, 1, 0], [4, 1, 0, 0]]


//[[0, 0, 1], [0, 1, 0], [-1, 0, 0]].Transpose() = [[0, 0, -1], [0, 1, 0], [1, 0, 0]]
//[[0, 0, 1], [0, 1, 0], [-1, 0, 0]].Inverse() = [[0, 0, -1], [0, 1, 0], [1, 0, 0]]
var m4 = new N6LMatrix([[0, 0, 1], [0, 1, 0], [-1, 0, 0]]);
var m5 = m4.TransposedMat();
console.log(m5.x); // Result: [[0, 0, -1], [0, 1, 0], [1, 0, 0]]
var m6 = m4.InverseMat(dt);
console.log(m6.x); // Result: [[0, 0, -1], [0, 1, 0], [1, 0, 0]]

* Coordinate transformation basics

Back to Table of contents

JavaScript

//Matrix

//[[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]].Translate([1, 2, 3, 4]) = [[1, 0, 0, 0], [2, 1, 0, 0], [3, 0, 1, 0], [4, 0, 0, 1]]
var m1 = new N6LMatrix(4).UnitMat();
var v1 = new N6LVector([1, 2, 3, 4], true);
var m2 = m1.TranslatedMat(v1);
console.log(m2.x); // Result: [[1, 0, 0, 0], [2, 1, 0, 0], [3, 0, 1, 0], [4, 0, 0, 1]]

* Creating and applying rotation, translation, and scale matrices

Back to Table of contents

JavaScript

//Matrix

var m1 = new N6LMatrix(4).UnitMat();
var v2 = new N6LVector([Math.PI / 2.0, 0, 1, 0], true);
var v3 = new N6LVector([1, 2, 2, 2],true);
var v4 = new N6LVector([1, 3, 4, 5],true);
var m3 = m1.AffineMat(v3, v2, v4);
console.log(m3.x); // Result: [[1, 0, 0, 0], [3, 0, 0, 2], [4, 0, 2, 0], [5, -2, 0, 0]]
var v5 = new N6LVector([1, 0, 1, 0], true);
var m4 = m3.RotAxis(v5, -Math.PI / 2.0);
console.log(m4.x); // Result: [[1, 0, 0, 0], [3, 2, 0, 0], [4, 0, 2, 0], [5, 0, 0, 2]]

* Conversion between local and world coordinates

Back to Table of contents

JavaScript

//Local & World

//Matrix
var um = new N6LMatrix(4).UnitMat();
var ay = new N6LVector(4, true).UnitVec(2);
var lm1 = um.RotAxis(ay, Math.PI / 2.0);
var tr = new N6LVector([1, 0, 0, 5], true);
var lm2 = lm1.TranslatedMat(tr);
console.log(lm2.x); // Result: Local[[1, 0, 0, 0], [0, 0, 0, 1], [0, 0, 1, 0], [5, -1, 0, 0]]
var lm3 = lm2.TranslatedMat(tr.Mul(-1));
var lm4 = lm3.RotAxis(ay, -Math.PI / 2.0);
console.log(lm4.x); // Result: World[[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]

* Homogeneous Coordinates & the bHomo Flag

Back to Table of contents

This section explains how N6L handles homogeneous coordinates and the significance of the bHomo flag,
which dictates special behaviors within the library.

  • Coordinate System Expectation
    N6L is fundamentally based on DirectX's conventions, therefore, it expects a row-major, left-handed coordinate system.

It's important to note that if you're interacting with other libraries or APIs that adopt a right-handed coordinate system,
you might need to perform transformations like transposition or Z-axis inversion (multiplication by -1) during input and output.
However, as long as you're performing calculations purely within N6L, these external conversions aren't strictly necessary,
provided you consistently align your internal conventions.

  • N6L's Matrix Layout

While homogeneous transformation matrices are typically represented as:

M = |ROT T|
  |0  1|

where ROT is the rotation component and T is the translation component, N6L adopts a slightly different,
though functionally equivalent, row-major layout for its internal representation. This specific arrangement does not cause any calculation issues.

N6L's expected matrix layout (row-major):

M = |1 0 0 0| // Row 0: W-element
  |Tx Xx Xy Xz| // Row 1: Local X-axis component
  |Ty Yx Yy Yz| // Row 2: Local Y-axis component
  |Tz Zx Zy Zz| // Row 3: Local Z-axis component

Key Benefits of Homogeneous Coordinates
Using a homogeneous coordinate system and 4x4 matrices allows various 3D graphics transformations to be handled
as unified linear algebra operations. The benefits are immense:

  • Unified Transformation Representation:

    • Diverse transformations like translation, rotation, scaling, shearing, and even perspective projection
      can all be expressed as a single 4x4 matrix multiplication. This simplifies complex transformation chains
      (e.g., object rotation → translation → camera view transform) into straightforward matrix products,
      significantly streamlining your code. Without homogeneous coordinates, different transformation types
      would require distinct calculation methods, leading to much more complex code.

    • Efficient Inverse Matrix Calculation (Especially for Rotation): You can extract the 3x3 rotation part from a 4x4 homogeneous matrix and leverage its orthogonal matrix properties
      to find its inverse simply by transposing it. This optimization avoids computationally expensive general inverse matrix calculations
      (like Gaussian elimination) and greatly contributes to real-time graphics performance.

    • Perspective Projection Representation:
      The w component of homogeneous coordinates is indispensable for representing perspective projection (depth perception).
      As an object's distance changes, its w component varies, enabling correct perspective in the final 3D-to-2D projection.

    • Distinguishing Points and Vectors (Transformation Characteristics):
      In homogeneous coordinates, points (positions) are typically represented as (x, y, z, 1)
      and direction vectors as (x, y, z, 0). This distinction automatically dictates their behavior
      when a transformation matrix is applied:

    • Points are affected by translation. Direction vectors are not affected by translation (only by rotation and scaling).
      This characteristic is also achieved automatically through a single matrix operation.

  • The bHomo Flag: N6L's Magic Switch

The bHomo flag acts as a special switch within N6L, enabling unique behaviors when set to true.

When bHomo is true, N6L performs specific operations:
For arithmetic operations, transpositions, and other transformations, N6L extracts the 3x3 ROT component (omitting the w component),
performs the operation on this 3x3 sub-matrix, and then recombines the w component afterwards.

This behavior leverages the intrinsic properties of homogeneous coordinate calculations, allowing for a more intuitive
and streamlined way to describe transformations.

Important Note: There are cases where the bHomo flag must be false at the end of a transformation chain
(e.g., when converting back to non-homogeneous 3D coordinates for specific operations).
Forgetting to appropriately toggle or manage the bHomo flag can lead to unexpected visual errors and bugs.
Always ensure bHomo is set to true or false according to the intended use case of the matrix.
By using N6LXXX.SetHomo(rh) // setting the bHomo flag, N6LXXX.ToHomo() // adding the w element,
and N6LXXX.ToNormal() // removing the w element, etc,
you can manipulate the bHomo flag relatively safely.


* Quaternions (N6LQuaternion)

Back to Table of contents

  * Quaternion basics  

* Benefits of quaternions as rotation representation (avoiding gimbal lock, etc.)

The rotation matrix when rotating an object around the zyz axis by angles φθψ is called the Euler angle rotation formula,
but as is well known, this causes the problem of gimbal lock. The reason is that rotating around the zyz axis is
a rotation around a global axis, not a local one, so if you rotate it by 90 degrees, the axes will overlap
and you will not be able to rotate as you intended. This phenomenon is called gimbal lock. How can you avoid gimbal lock?
You can avoid it by rotating around a local axis, that is, an arbitrary axis, instead of a global axis. This rotation
around an arbitrary axis is a quaternion rotation or Rodrigues rotation, so you can use that.


* Creation, initialization

Back to Table of contents

JavaScript

var quta = new N6LQuaternion(1, 0, 0, 0);
var qutb = new N6LQuaternion(1, new Array(0, 0, 0));
var qutc = new N6LQuaternion(new Array(1, 0, 0, 0));
var qutd = new N6LQuaternion(new N6LVector([1, 0, 0, 0]));
var qute = new N6LQuaternion(1, new N6LVector([1, 2, 3, 4], true));
var qutf = new N6LQuaternion([1, 0, 0, 0]);
var qutg = new N6LQuaternion(quta); //deep copy

* Multiplication, normalization

Back to Table of contents

JavaScript

//Quaternion

var q1 = new N6LQuaternion([1, 0, 0, 1]);
var q2 = new N6LQuaternion([1, 0, 1, 0]);
var q3 = q1.Mul(q2);
console.log(q3.q.x); // Result: [1, -1, 1, 1]
var q4 = q3.NormalQuat();
console.log(q4.q.x); // Result: [0.5, -0.5, 0.5, 0.5]

* Mutual conversion with matrices (cooperation between N6LQuaternion, N6LVector and N6LMatrix)

Back to Table of contents

JavaScript

//Vector←→Matrix←→Quaternion

var v1 = new N6LVector([Math.PI / 2.0, 0, 1, 0], true);
var m1 = v1.Matrix();
var q1 = m1.Quaternion();
var m2 = q1.Matrix();
var v2 = m2.Vector();
console.log(v2.x); // Result: [Math.PI / 2.0 = 1.571, 0, 1, 0]

* Application to animation Slerp (spherical linear interpolation), etc.

Back to Table of contents

JavaScript

var q1 = new N6LQuaternion([1, 0, 0, 1]);
var q2 = new N6LQuaternion([1, 0, 1, 0]);
var q3 = q1.Lerp(q2, 0.5);
console.log(q3.q.x); // Result: [0.816, 0, 0.408, 0.408]
var q4 = q1.Lerp(q2, 1);
console.log(q4.q.x); // Result: [0.707, 0, 0.707, 0]
var q5 = q1.Slerp(q2, 0.5);
console.log(q5.q.x); // Result: [0.816, 0, 0.408, 0.408]
var q6 = q1.Slerp(q2, 1);
console.log(q6.q.x); // Result: [0.707, 0, 0.707, 0]
var q7 = q1.Slerp2(q2, 0.5);
console.log(q7.q.x); // Result: [0.816, 0, 0.408, 0.408]
var q8 = q1.Slerp2(q2, 1);
console.log(q8.q.x); // Result: [0.707, 0, 0.707, 0]

* Camera and Projection

Back to Table of contents

JavaScript


  * View Matrix  

* Constructing the LookAt matrix

Back to Table of contents

JavaScript


* Camera movement and rotation

Back to Table of contents

JavaScript


  * Projection Matrix  

* Setting Perspective Projection

Back to Table of contents

JavaScript


* Setting Orthographic Projection

JavaScript


* Time Management with N6LTimerMan  

Building the main thread

Back to Table of contents

HTML

  
<script src="https://nas6mixfoolv.github.io/NAS6LIB/javascripts/nas6lib/timer.js"></script>  
  
  

JavaScript

  
// You do not usually need to wait for DOMContentLoaded to instantiate N6LTimerMan, but  
// If there is processing that depends on other DOM elements, execute it in DOMContentLoaded.  
var TMan = new N6LTimerMan(); // Assumes it is defined in NAS6LIB/javascripts/nas6lib/timer.js  
var GLoopID = -1; //ID of Main Thread  
  
//N6L initialization processing  
function initializeN6L() {  
  // Write the main processing for N6L initialization here.  
  // For example, initializing calculations using N6LMatrix, obtaining Canvas and preparing drawing context.  
  console.log("DOM is fully loaded. Initializing N6L...");  
  
  GLoopID = TMan.add(); // Add a new timer to the timer manager  
  GLoop(GLoopID); // Start the main thread by running it for the first time  
}  
  
//Main thread  
function GLoop(id){  
  // Write the main thread processing here.  
  
  // Finally, reset the main thread to continue the thread, but if you do not reset it with an end condition, the thread will stop.  
  TMan.timer[id].setalerm(function() { GLoop(id); }, 50); // Reset the main thread after 50 milliseconds.  
}  
  
// Add a DOMContentLoaded event listener  
// It is standard to use window.document.addEventListener.  
document.addEventListener('DOMContentLoaded', initializeN6L);  
  
// Alternatively, you can use window.addEventListener('load', initializeN6L);  
// DOMContentLoaded is faster and more suitable for DOM manipulation.  
  

N6LTimerMan Multi-threaded construction Demo & Explanation (External Link)


Back to Table of contents

Clone this wiki locally