#+HTML_MATHJAX: align:left font:Neo-Euler scale:100  path:"https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.0/MathJax.js?config=TeX-AMS_SVG"

#+INCLUDE: theme/style.org
#+TITLE: Computer Graphics with OpenGL 
#+DESCRIPTION: cpp c++ opengl sdl computer graphics 2d 3d 
#+STARTUP: content


* Computer Graphics
** Computer Graphics Overview [DRAFT] 

  *Motivation* 

Outline of common computer graphics applications and a motivation for
the study of this field.

   + 2D and 3D Games

   + Virtual Reality 

   + Cartography

   + Topography 

   + Charts and plotting 

   + Scientific Visualization

   + Data visualization

   + Medical Images
     + See: https://www.computerhistory.org/makesoftware/exhibit/mri/
     + See: [[http://www.gg.caltech.edu/brain/mri_research.html][Magnetic Resonance Imaging for Computer Graphics]]

   + Physics and engineering simulations.

   + Implementation of CAD (Computer Aided Design) systems

   + Animation. See Pixar company that created many movies using computer graphics.
 

  *Books* 

   + [[https://www.amazon.com/OpenGL-Programming-Guide-Official-Learning-ebook/dp/B00C2SBOJE][OpenGL Programming Guide: The Official Guide to Learning OpenG, Version 4.3]]
     + Note: Official Modern OpenGL documentation with lots of details and explanations.

   + [[https://www.amazon.com/Advanced-Graphics-Programming-Kaufmann-Computer/dp/1558606599][Advanced Graphics Programming Using OpenGL]] - (The Morgan Kaufmann
     Series in Computer Graphics) 1st Edition
     + Brief: "This book brings the graphics programmer beyond the
       basics and introduces them to advanced knowledge that is hard
       to obtain outside of an intensive CG work environment. The book
       is about graphics techniques―those that don’t require
       esoteric hardware or custom graphics libraries―that are
       written in a comprehensive style and do useful things. It
       covers graphics that are not covered well in your old graphics
       textbook. But it also goes further, teaching you how to apply
       those techniques in real world applications, filling real world
       needs."
     + Note: Contains useful notes about data visualization and CAD
       implementations.

   + [[https://www.amazon.com/Interactive-Computer-Graphics-Top-Down-Shader-Based/dp/0132545233][Interactive Computer Graphics: A Top-Down Approach With Shader-Based Opengl]] -  6th Edition
     + Brief: "This book is suitable for undergraduate students in
       computer science and engineering, for students in other
       disciplines who have good programming skills, and for
       professionals. Computer animation and graphics–once rare,
       complicated, and comparatively expensive–are now prevalent in
       everyday life from the computer screen to the movie
       screen. Interactive Computer Graphics: A Top-Down Approach with
       Shader-Based OpenGL, 6e, is the only introduction to computer
       graphics text for undergraduates that fully integrates OpenGL
       3.1 and emphasizes application-based programming. Using C and
       C++, the top-down, programming-oriented approach allows for
       coverage of engaging 3D material early in the text so readers
       immediately begin to create their own 3D graphics. Low-level
       algorithms (for topics such as line drawing and filling
       polygons) are presented after readers learn to create
       graphics."

   + [[http://opengl.czweb.org/ewtoc.html][OpenGL Super Bible]] [ONLINE] 
 

  *Videos - Related to Computer Graphics* 

   + [[https://www.youtube.com/watch?v=pQcC2CqReSA][Tutorial 9 - Coordinate Systems in OpenGL]] [BEST]

   + [[https://www.youtube.com/watch?v=vQ60rFwh2ig][The True Power of the Matrix (Transformations in Graphics) - Computerphile]]

   + [[https://www.youtube.com/watch?v=KdyvizaygyY][A universe of Triangles - Computerphile]]

   + [[https://www.youtube.com/watch?v=U93RImC-by4][VOX - Why video games are made of tiny triangles]]

   + [[https://www.youtube.com/watch?v=TPCpXXBHFSA][VOX - Why gamers use WASD to move]]

   + [[https://www.youtube.com/watch?app=desktop&v=TIxt9guMbXo][The World's Most Famous Teapot: The Utah Teapot]]

   + [[https://www.youtube.com/watch?v=3NQuHpUyZbI][Computer - Breaking the Utah Teapot - 1989]] (10 minutes)
     + "Alan Norton's groundbreaking graphics work from 1989 done at
       IBM Research.  The Utah Teapot was the common object used
       within graphics research.  Alan's notion was to tease the
       community by breaking their common object.  This is my video of
       Alan describing his research.  It was a wondrous experience to
       work with him.  The video was for my international multimedia
       magazine, a project I received after convincing an IBM Research
       VP to let me do something other than language research based on
       my fandom music video making."

   + [[https://www.youtube.com/watch?v=LetNCGeB67o][How a Teapot Revolutionized Computer Graphics]] (2:41 minutes)
     + "Once upon a time, the field of computer graphics was just
       getting started, and a simple teapot helped researchers develop
       rendering techniques."

   + [[https://www.youtube.com/watch?v=cvcAjgMUPUA][How Rendering Graphics Works in Games!]] (6:24 minutes)
     + "Going all the way from the bits of vertex coordinates to the
       rasterizing of pixels, let's learn how rendering graphics
       works!"

   + [[https://www.youtube.com/watch?v=_cyVDoyI6NE][CPU vs GPU (What's the Difference?) - Computerphile]] (6:38 minutes)
     + "What does a GPU do differently to a CPU and why don't we use
       them for everything? First of a series from Jem Davies, VP of
       Technology at ARM." 

  *Communities* 

   + https://www.gamedev.net/

   + https://forum.freegamedev.net/

   + https://gamedev.stackexchange.com/

   + https://old.reddit.com/r/gamedev/

   + https://old.reddit.com/r/webgl/

   + https://old.reddit.com/r/opengl/

   + https://old.reddit.com/r/vulkan/

   + https://old.reddit.com/r/GraphicsProgramming

   + https://old.reddit.com/r/raytracing

   + https://old.reddit.com/r/linux_gaming/

   + https://old.reddit.com/r/VFIO/ (Virtualization GPU passthrough.)

** GPU Accelerated Computer Graphics APIs

 *Native Graphics APIs* (exposed as C-subroutines)

  + [[https://www.opengl.org//][OpenGL]] (Khronos Group) - Main OpenGL specification
    + => Open standard, cross-platform and vendor-independent API for
      rendering 2D or 3D computer graphics with GPU (Graphics
      Processing Unit) acceleration. OpenGL can be used for
      implementing computer graphics, games, scientific vizualization,
      virtual reality and CADs - Computer Aided Design
      software. OpenGL API specificiation is maintained as an
      open-standard by the Krhonos Group industry consortium.

    + => OpenGL has two modes, _immediate mode_ (a.k.a fixed-function
      pipeline, legacy-OpenGL) which is being depreacted, and _retained_
      _mode_ (modern OpenGL) that delivers more performance and is based
      on buffer-objects and shaders.

    + => OpenGL Official Specification: [[https://www.khronos.org/registry/OpenGL/index_gl.php][Khronos OpenGL® Registry]]

  + _OpenGL ES_  (Khronos Group)
    + => OpenGL for embedded systems, mobile devices and touch screen
      devices and so on. This API (Application Programming Interface)
      is widely used by many mobile games.

    + => Similar to OpenGL specfication, but supports only the
      _retained-mode_. OpenGL ES does not support _immediate-mode_. As a
      result, calls to legacy OpenGL subroutines such as glBegin(),
      glEnd(), glRotate(), glTranslate(), ..., are not supported.

  + [[https://www.khronos.org/vulkan/][Vulkan]]_(Khronos Group)
    + => Graphics API with GPU acceleration that provides more
      low-level GPU control and less overhead than OpenGL. This API is
      designed for taking more advantag of multi-core CPU
      architectures and performing tasks in parallel.

  + [[https://en.wikipedia.org/wiki/DirectX][DirectX]] / Direct3D (Microsoft inc.) - Windows-only
    + => Microsoft's graphics API for accessing the GPU hardware. It
      is only available on operating systems based Windows-NT kernel
      and Windows-CE kernel (embedded version of Windows-NT).

  + [[https://developer.apple.com/metal/][Metal]] (Apple inc.)
    + => Apple-only API for rendering 2D or 3D computer graphics with
      GPU acceleration. This API is available only on iOS and MacOSX
      operating systems. On iOS and MacOSX, Apple is deprecating and
      phasing out OpenGL in favor of its own  API.

 *Web Computer Graphics APIs* (exposed as Javascript/ECMAScript subroutines)

  + [[https://developer.mozilla.org/en-US/docs/Web/API/WebGL_API/Tutorial/Getting_started_with_WebGL][WebGL]] (Khronos Group)  / Html5 API
    + => Based on OpenGL ES and implemented by major web
      browsers. Unlike OpenGL or OpenGL ES, which are exposed via C
      subroutines, WebGL is exposed to calling codes via JavaScript
      (ECMAScript) and Html5 canvas.

    + => Note: This API does not support OpenGL immediate-mode or
      legacy OpenGL. WebGL only supports _retained-mode_.

  + [[https://en.wikipedia.org/wiki/WebGPU][WebGPU]] (W3C Consortium) - Upcoming Html5 standard.
    + => Upcoming graphics API for web browser, based on Vulkan,
      Metal and DirectX which intends to deliver more low level GPU
      control and higher performance. Unlike WebGL, this API is not a
      direct port of any native graphics API such as Vulkan, Metal and
      DirectX.
    + [[https://gpuweb.github.io/gpuweb/][WebGPU - Editor’s Draft]]
    + [[https://github.com/gpuweb/gpuweb/wiki/Implementation-Status][Implementation Status · gpuweb/gpuweb Wiki · GitHub]]
    + [[https://github.com/gpuweb/gpuweb][GitHub - gpuweb/gpuweb: Where the GPU for the Web work happens!]]
    + [[https://kvark.github.io/web/gpu/native/2020/05/03/point-of-webgpu-native.html][Point of WebGPU on native]]
    + [[https://hacks.mozilla.org/2020/04/experimental-webgpu-in-firefox/][A Taste of WebGPU in Firefox - Mozilla Hacks - the Web developer blog]]


 *Further Reading*

General:

  + [[https://github.com/KhronosGroup/MoltenVK][GitHub - KhronosGroup/MoltenVK]]
    + "MoltenVK is a Vulkan Portability implementation. It layers a
      subset of the high-performance, industry-standard Vulkan
      graphics and compute API over Apple's Metal graphics framework,
      enabling Vulkan applications to run on iOS and macOS."

  + [[https://www.pcgamer.com/doom-benchmarks-return-vulkan-vs-opengl/][Doom benchmarks return: Vulkan vs. OpenGL | PC Gamer]]

  + [[https://linuxhint.com/vulkan-for-linux-users/][Vulkan for Linux Users – Linux Hint]]

  + [[https://stackoverflow.com/questions/56766983/what-can-vulkan-do-specifically-that-opengl-4-6-cannot][What can Vulkan do specifically that OpenGL 4.6+ cannot? - Stack]]

  + [[https://softwareengineering.stackexchange.com/questions/380832/opengl-and-global-state][design - OpenGL and global state - Software Engineering Stack Exchange]]

  + [[https://www.collabora.com/news-and-blog/blog/2018/10/31/introducing-zink-opengl-implementation-vulkan/][ZINK - OpenGL Implementation on Top of Vulkan]] 

GPU Listing:

  + [[https://en.wikipedia.org/wiki/Mali_(GPU)][Mali GPU]] - GPU used by most ARM-based mobile devices with ARM integrated GPUs.

  + [[https://en.wikipedia.org/wiki/List_of_Nvidia_graphics_processing_units][List of Nvidia graphics processing units]]

  + [[https://en.wikipedia.org/wiki/List_of_AMD_graphics_processing_units][List of AMD graphics processing units]]

  + [[https://cacm.acm.org/magazines/2021/3/250710-the-decline-of-computers-as-a-general-purpose-technology/fulltext][The Decline of Computers as a General Purpose Technology]] - ACM Communications.

  + [[https://vulkan.gpuinfo.org/listdevices.php][Devices - Vulkan Hardware Database by Sascha Willems]]

  + https://egpu.io/best-egpu-buyers-guide/


Image Resolution DPI, PPI and so on:

  + [[https://en.wikipedia.org/wiki/Resolution_independence][Resolution Independence]]

  + [[https://en.wikipedia.org/wiki/Pixel_density][Pixel Density]]

  + [[https://en.wikipedia.org/wiki/Vector_graphics][Vector Graphics]]

  + [[https://en.wikipedia.org/wiki/Scalable_Vector_Graphics][SVG - Scalable Vector Graphics]]

  + [[https://en.wikipedia.org/wiki/Comparison_of_graphics_file_formats][Comparison of Graphic File Formats]]

  + [[https://en.wikipedia.org/wiki/Retina_display][Retina Display - Apple]]

  + [[https://www.atiz.com/resources/DPI-PPI-Megapixels-and-Resolution.pdf][Making sense of DPI, PPI, Megapixels and Resolution - Atiz Innovation]]

  + [[https://cft.vanderbilt.edu/wp-content/uploads/sites/59/Image_resolutions.pdf][What Resolution Should Your Images Be?]]

  + [[https://pixelcalculator.com/en][Pixelscalculator - Convert DPI / PPI to Pixels and cmm, cm, inches]]

  + [[https://www.omnicalculator.com/other/pixels-per-inch][PPI - Calculator (Pixels Per Inch)]]

** OpenGL Documentation

 *OpenGL:*

   * [[http://docs.gl/][docs.gl]] [BEST]
     + Allows searching and quickly browsing the OpenGL
       documentation.

   * https://www.opengl.org/

   * [[https://docs.microsoft.com/en-us/windows/win32/opengl/opengl][Microsoft Windows OpenGL Implementation - WGL]]

   * [[https://www.khronos.org/registry/OpenGL/index_gl.php][Khronos OpenGL® Registry]]
     * OpenGL Official Specification.

   * [[https://www.khronos.org/opengl/wiki/Core_Language_(GLSL)][Core Language (GLSL) - OpenGL Wiki]] / Khronos Group
     * Official Documentation of OpenGL shading language.

   * [[https://www.khronos.org/opengl/wiki/Data_Type_(GLSL)][Data Type (GLSL) - OpenGL Wiki]] / Khronos Group
     * Data types of GLSL - OpenGL Shading Language.

   * [[https://thebookofshaders.com/][The Book of Shaders]] - (Patricio Gonzalez and Jen Love)

   * [[https://arm-software.github.io/opengl-es-sdk-for-android/index.html][OpenGL ES SDK for Android Documentation]] (ARM Company)

   * [[https://www.khronos.org/registry/OpenGL-Refpages/gl2.1/xhtml/glEnable.xml][OpenGL Registry (Khronos Group) / glEnable()]] => Documentation of
     OpenGL glEnable options, which sets the capabilities. 

   * [[https://www.khronos.org/opengl/wiki/Common_Mistakes][Common Mistakes]] (Khronos Group) =>> Useful for debugging.

   * [[https://web.archive.org/web/20190629031543/http://antongerdelan.net/teaching/3dprog1/maths_cheat_sheet.pdf][3D Maths Cheat Sheet]] - Reference Card for computer graphics
     linear algebra. (homogeneous coordinates, rotation matrices,
     translation matrices and etc.)

 *WebGL:*

   * [[https://www.khronos.org/webgl/][WebGL Overview - The Khronos Group Inc]]
     * "WebGL is a cross-platform, royalty-free web standard for a
       low-level 3D graphics API based on OpenGL ES, exposed to
       ECMAScript via the HTML5 Canvas element. Developers familiar
       with OpenGL ES 2.0 will recognize WebGL as a Shader-based API
       using GLSL, with constructs that are semantically similar to
       those of the underlying OpenGL ES API. It stays very close to
       the OpenGL ES specification, with some concessions made for
       what developers expect out of memory-managed languages such as
       JavaScript. WebGL 1.0 exposes the OpenGL ES 2.0 feature set;
       WebGL 2.0 exposes the OpenGL ES 3.0 API."

   * [[https://developer.mozilla.org/en-US/docs/Web/API/WebGL_API/Tutorial][WebGL tutorial - Web APIs | MDN]]

   * [[https://developer.mozilla.org/en-US/docs/Web/API/WebGL_API/Tutorial/Getting_started_with_WebGL][Getting started with WebGL - Web APIs | MDN]] - WebGL Documentation.

   * [[https://webglfundamentals.org/][WebGL Fundamentals]]
** Terminology related to OpenGL and  Computer Graphics

  + *SIGGRAPH* - international Association for Computing Machinery's
    Special Interest Group on Computer Graphics and Interactive
    Technique.

  + API - Application Programming Interface

  + [[https://en.wikipedia.org/wiki/Khronos_Group][Khronos Group]] - Industry consortium that takes care of OpenGL and
    Vulkan standards and specifications. 

  + SGI - Silicon Graphics International - Company which developed
    first version of OpenGL. It was known for its SGI workstations.

  + _OpenGL_ - Open Graphics Library

  + [[https://www.khronos.org/opengl/wiki/OpenGL_Context][OpenGL Context]]

  + [[http://glew.sourceforge.net/index.html][GLEW]] - OpenGL Extension Wrangler

  + ARB - OpenGL Architecture Review Board

  + OpenGL _Immediate Mode_ (Fixed-Function Pipeline, Legacy OpenGL)
    + Also known as: Legacy OpenGL, Fixed-Function Pipeline
    + Drawing is mostly performed without storing data on GPU and by
      using subroutines calls to glScale(), glRotate(), glPush(), glPop(),
      glTranslate(), glBegin(), glEnd() and so on.
    + Immediate-mode is not supported by OpenGL ES or WebGL.
    + See: [[https://www.khronos.org/opengl/wiki/Fixed_Function_Pipeline][Fixed Function Pipeline - OpenGL Wiki]]

  + OpenGL _retained mode_ (Programmable Pipeline, Modern OpenGL)
    + Also known as: Modern OpenGL, Programmable Pipeline
    + New and modern OpenGL API => Drawing is performed by storing
      data on the GPU via VBO (Vertex Buffer Objects) and by using
      _shaders_, programs that runs on GPU, for performing geometric
      vertex transformations, color and texturing computations.
    + More peformant than _immediate-mode_ as the data is not sent to
      the GPU every frame.

  + _GPU_ - Graphics Processing Unit

  + _iGPU_ - Integrated GPU - The GPU is in the same processor chip
    silicon die. Most mobile GPUs are integrated. Most desktop
    computers also have dedicated GPUs built in the same chip as the
    CPU cores. This type of GPU is enough for most common tasks such
    as lightweight games, 2D games and watching youtube videos, but
    they are not suitable for heavy AAA (triple-A) games or for
    running CAD applications. Note: Most server-grade processor IC
    (Integrated Circuits), such as Intel Xeon lacks integrated GPU. 

  + _dGPU_ - Dedicated GPU - GPU available as a separated card and
    connected to the motherboard via PCI connection. They are suitable
    for triple-A games and GPGPU (General Purpose GPU Computing). The
    biggest manufacturers of those GPUs are _Nvidia_ and _AMD_ (Advanced
    Micro Devices). 

  + _eGPU_ - External GPU - GPU external to the motherboard and often
    connected via _thunderbolt_ cable. External GPUs can be used on
    laptop machines, that often lack enough space for installing a
    dedicated GPU. Disadvantage: only newer motherboards have support
    for thunderbolt cables.

  + _GPGPU_ - General Purpose Computing on GPU
    + APIs: OpenCL, Cuda, and so on.
    + Parallel non-graphics computations on GPU. GPGPU APIs take
      advantage of GPU parallel computing features for high
      performance computing.

  + AAA - Triple-A games.

  + [[https://www.khronos.org/opengl/wiki/Direct_State_Access][DSA]] - Direct State Access

  + _Vertex_ - 2D or 3D coordinates representing a point in the space.

  + _DOF_ - Degrees Of Freedom

  + _2D_ - 2 dimensions (plane) / 2 coordinates (X, Y)

  + _3D_ - 3 dimensions (space) / 3 coordinates (X, Y, Z)

  + _Homogenous Coordinate_ - Coordinate system using an extra dimension
    for encoding translation coordinate transformation in the same way
    as rotation matrices transformations.
    + 2D homogeneous coordinates: (X, Y, W = 1)
    + 3D homogeneous coordinates: (X, Y, Z, W = 1)

  + _NDC_ - Normalized Device Coordinate
    + Default coordinates used by OpenGL (-1.0 to 1.0) for each
      axis. Any vertex that falls out of this range will not be visible
      on the screen.

  + _MCS_ - Model Coordinate System

  + _CTM_ - Current Transform Matrix

  + [[https://www.khronos.org/opengl/wiki/Buffer_Object][Buffer Object]]

  + _VBO_ - Vertex Buffer Object

  + _VAO_ - Vertex Array Object

  + _FBO_ - Framebuffer Object

  + _IBO_ - Index Buffer Object

  + _UBO_ - Uniform Buffer Object

  + _FPS_ - Frame Per Seconds

  + _Shader_ - Program that runs on the GPU and performs vertex
    computations such as coordinate transformations (matrix
    multiplications), colors and texture computations.

  + _GLSL_ - OpenGL shading programming language - for performing
    computer graphics calculations on the GPU hardware.

  + _HLSL_ (High-Level Shader Language) - Microsft's DirectX shading language.

  + _COP_ - Center Of Projection

  + _CAD_ - Computer Aided Design

  + _CAM_ - Computer Aided Manufacturing

  + _CSG_ - Constructive Solid Geometry

  + Computer Graphics Data Structures
    + [[https://en.wikipedia.org/wiki/Polygon_mesh][Mesh]]
    + Nurb 
    + Voxel
    + Quad tree
    + Octo tree
    + [[https://en.wikipedia.org/wiki/Digital_elevation_model][DEM - Digital Elevation Model]]

  + Common Mesh File Formats (Standardized file formats for mesh storage)
    + [[https://en.wikipedia.org/wiki/Wavefront_.obj_file][.obj File]] - ASCII text file format for mesh storage.
    + [[http://paulbourke.net/dataformats/ply/][.ply File]] - Polygon File Format
    + [[https://en.wikipedia.org/wiki/VRML][.vrml File]] - VRM XML file format created by the W3C consortium.
    + [[https://en.wikipedia.org/wiki/Blender_(software)#File_format][.blend File]] - Blender File format

  + Article Related to Meshes
    + [[https://en.wikipedia.org/wiki/Types_of_mesh][Types of Meshes]] 
    + [[https://en.wikipedia.org/wiki/List_of_common_3D_test_models][Common 3D test models]]
    + [[https://en.wikipedia.org/wiki/Mesh_generation][Mesh Generation]]
    + [[https://en.wikipedia.org/wiki/Polygon_mesh][Polygon Mesh]]
    + [[https://en.wikipedia.org/wiki/Triangulation_(geometry)][Triangulation (Geometry)]]
    + [[https://en.wikipedia.org/wiki/Wire-frame_model][Wire-frame model]]
    + [[https://en.wikipedia.org/wiki/Point_cloud][Point Cloud]]
    + [[http://wiki.gis.com/wiki/index.php/Polygon_mesh][Polygon mesh (GIS)]]

  + _Resolution_
    + PPI - Pixels Per Inch
    + DPI - Dots Per Inch

  + _Display Types_
    + CRT - Cathode Ray Tube
    + LCD - Liquid Crtystal Display
    + PDP - Plasma Display Panel
    + OLED

** OpenGL companion libraries and 3D models
*** OpenGL Companion Libraries

 _OpenGL Loaders:_ [ESSENTIAL]

  + Libraries that abstracts OpenGL function pointers loading in a
    platform-independent way.

  + [[http://glew.sourceforge.net/index.html][GLEW]] - OpenGL Extension Wrangler [MOST USED]
    + "The OpenGL Extension Wrangler Library (GLEW) is a
      cross-platform open-source C/C++ extension loading library. GLEW
      provides efficient run-time mechanisms for determining which
      OpenGL extensions are supported on the target platform. OpenGL
      core and extension functionality is exposed in a single header
      file. GLEW has been tested on a variety of operating systems,
      including Windows, Linux, Mac OS X, FreeBSD, Irix, and Solaris."

  + [[https://glad.dav1d.de/][GLAD]] - [MOST-USED] Multi-Language GL/GLES/EGL/GLX/WGL
    Loader-Generator based on the official specs.

  + [[https://github.com/cginternals/glbinding][GitHub - cginternals/glbinding]]
    + "A C++ binding for the OpenGL API, generated using the gl.xml
      specification."

  + [[https://github.com/anholt/libepoxy][GitHub - anholt/libepoxy]]
    + "Epoxy is a library for handling OpenGL function pointer
      management for you."

  + [[https://github.com/imakris/glatter][GitHub - imakris/glatter]]
    + "An OpenGL loading library, with support for GL, GLES, EGL, GLX and WGL"

  + [[https://galogen.gpfault.net/][Galogen OpenGL Loader Generator]]
    + "Galogen is an OpenGL loader generator. Given an API version and
      a list of extensions, Galogen will produce corresponding headers
      and code that load the exact OpenGL entry points you need. The
      produced code can then be used directly by your C or C++
      application, without having to link against any additional
      libraries."

  + [[https://github.com/SFML/SFML-glLoadGen][GitHub - SFML/SFML-glLoadGen]]
    + Customized glLoadGen for SFML


  _Window System Abstraction_ 

Libraries for window systems, event handling and OpenGL context abstraction: [ESSENTIAL]

  + Abstract platform-specific window system and event handling.

  + [[https://www.glfw.org/][GLFW]] [BEST] [MOST-USED]
    * C library that provides graphics windows for OpenGL, Vulkan,
      OpenGL ES and deals with event handling.

  + [[https://www.libsdl.org/][SDL]] (Simple Direct Media Layer) [BEST] [MOST-USED]
    * Cross-platform C library that provides windows and event
      handling for many computer graphics APIs such as OpenGL, Vulkan
      and DirectX. SLD also has facilities for dealing with audio,
      joystick, CD-ROM, network and threads.

  + [[https://www.sfml-dev.org/][SFML]] (Simple and Fast Multimedia Library) [MOST-USED]
    + "SFML provides a simple interface to the various components of
      your PC, to ease the development of games and multimedia
      applications. It is composed of five modules: system, window,
      graphics, audio and network." 

  + [[http://freeglut.sourceforge.net/][GLUT]] (FreeGlut) - OpenGL Utility Toolkit
    * Deals with window creation, OpenGL initialization, event
      handling and  so on.
    * Docs: https://www.glfw.org/documentation.html  


  _Graphics Math Libraries_ 

OpenGL Math and Computer Graphics Math: [ESSENTIAL]


  + [[https://glm.g-truc.net/0.9.5/api/index.html][GLM]] (OpenGL Mathematics Library) [MOST-USED]
    + Source code: https://github.com/g-truc/glm
    + Header-only C++ library that provides classes for computer
      graphics mathematics such as: 2D, 3D and homogeneous coordinate
      vector; 2D, 3D and homogeneous coordinate transformation
      matrices; quaternions and subroutines for computing camera,
      perspective or orthogonal transformation matrices.

  + [[https://github.com/Kazade/kazmath][GitHub - Kazade/kazmath]] - A C math library targeted at games
    + "Kazmath is a simple 3D maths library written in C. It was
      initially coded for use in my book, Beginning OpenGL Game
      Programming - Second edition, but rapidly gained a life of its
      own. Kazmath is now used by many different projects, and
      apparently is used in 25% of the worlds mobile games (yeah, I
      don't believe it either - but it's used in Cocos2d-x)."

  + [[https://github.com/recp/cglm][GitHub - recp/cglm]] -  Highly Optimized Graphics Math (glm) for C

  + See: [[https://github.com/chunkyguy/Math-Library-Test][GitHub - chunkyguy/Math-Library-Test]] - A comparison of the
    various major math libraries for speed and ease of use.


  _Image Loading for texture_ 

   + [[https://github.com/g-truc/gli][GLI - OpenGL Image]]
     + "OpenGL Image (GLI) is a header only C++ image library for
       graphics software. GLI provides classes and functions to load
       image files (KTX and DDS), facilitate graphics APIs texture
       creation, compare textures, access texture texels, sample
       textures, convert textures, generate mipmaps, etc."

   + [[https://github.com/smoked-herring/sail][smoked-herring/sail]] - Squirrel Abstract Image Library
     + "SAIL is a format-agnostic cross-platform image decoding
       library providing rich APIs, from one-liners to complex use
       cases with custom I/O sources. It enables a client to read and
       write static, animated, multi-paged images along with their
       meta data and ICC profiles."
     + Note: Supports APNG, BMP, GIF, JPEG, PNG and TIFF image formats.

   + [[https://github.com/nothings/stb/blob/master/stb_image.h][stb_image.h]]  - single-file header-only C library for loading
     images from several file formats including, jpeg, bmp, tga, ppm,
     pgm, gif and so on.

   + [[https://github.com/littlstar/soil][SOIL - Simple OpenGL Image Library]]
     + "SOIL is a tiny C library used primarily for uploading textures
       into OpenGL. It is based on stb_image version 1.16, the public
       domain code from Sean Barrett (found here). It has been
       extended to load TGA and DDS files, and to perform common
       functions needed in loading OpenGL textures. SOIL can also be
       used to save and load images in a variety of formats." 

   + [[https://freeimage.sourceforge.io/][freeimage]]
     + "FreeImage is an Open Source library project for developers who
       would like to support popular graphics image formats like PNG,
       BMP, JPEG, TIFF and others as needed by today's multimedia
       applications. FreeImage is easy to use, fast, multithreading
       safe, compatible with all 32-bit or 64-bit versions of Windows,
       and cross-platform (works both with Linux and Mac OS X)."

   + [[https://github.com/cginternals/glraw][gldraw]]
     + "With glraw you can preconvert your texture assets and load
       them without the need of any image library. The generated raw
       files can easily be read. For this, glraw also provides a
       minimal Raw-File reader that you can either source-copy or
       integrate as C++ library into your project. Image to OpenGL
       texture conversion can be done either by glraws command line
       interface, e.g., within an existing tool-chain, or at run-time
       with glraw linked as asset library (requires linking Qt)." 

   + [[https://lodev.org/lodepng/][lodepng]]
     + "LodePNG is a PNG image decoder and encoder, all in one, no
       dependency or linkage to zlib or libpng required. It's made for
       C (ISO C90), and has a C++ wrapper with a more convenient
       interface on top."

   + [[http://www.libpng.org/pub/png/libpng.html][libpng]]
     + "libpng is the official PNG reference library. It supports
       almost all PNG features, is extensible, and has been
       extensively tested for over 23 years. The home site for
       development versions (i.e., may be buggy or subject to change
       or include experimental features) is
       https://libpng.sourceforge.io/, and the place to go for
       questions about the library is the png-mng-implement mailing
       list. libpng is available as ANSI C (C89) source code and
       requires zlib 1.0.4 or later (1.2.5 or later recommended for
       performance and security reasons). The current public release,
       libpng 1.6.37, fixes the use-after-free security vulnerability
       noted below, as well as an ARM NEON memory leak in the
       palette-to-RGB(A) expansion code (png_do_expand_palette())."

   + [[https://libspng.org/][libspng]]
     + "libspng (simple png) is a C library for reading and writing
       Portable Network Graphics (PNG) format files with a focus on
       security and ease of use. It is licensed under the BSD 2-clause
       “Simplified” License."

   + [[https://www.ijg.org/][IJG - Indepedent JPEG Group]]
     + "IJG is an informal group that writes and distributes a widely
       used free library for JPEG image compression. The first version
       was released on 7-Oct-1991. The current version is release 9d
       of 12-Jan-2020. This is a stable and solid foundation for many
       application's JPEG support. You can find our original code and
       some supporting documentation in the directory files. There is
       a Windows format package in zip archive format jpegsr9d.zip and
       a Unix format package in tar.gz archive format
       jpegsrc.v9d.tar.gz. A collection of modified versions with
       adaptions and error fixes for system maintenance is available
       on jpegclub.org in the directory support."

  _Asset/Object Loaders - Mesh Importing Libraries_ [ESSENTIAL]

Object loader / Asset import libraries: 

  * [[http://www.assimp.org/][Assimp - The Open-Asset-Import Library]] [MOST-USED]
    + "The Open Asset Import Library (short name: Assimp) is a
      portable Open-Source library to import various well-known 3D
      model formats in a uniform manner. The most recent version also
      knows how to export 3d files and is therefore suitable as a
      general-purpose 3D model converter. See the feature-list."
    + Note: Allows importing blender-generated models/assets in OpenGL.
    + Repository: https://github.com/assimp/assimp
    + See: https://www.khronos.org/opengl/wiki/Tools/Open_Asset_Import

  * [[https://github.com/Bly7/OBJ-Loader][OBJ-Loader]]
    + "OBJ Loader is a simple, header only, .obj model file loader
      that will take in a path to a file, load it into the Loader
      class object, then allow you to get the data from each mesh
      loaded. This will load each mesh within the model with the
      corresponding data such as vertices, indices, and material. Plus
      a large array of vertices, indices and materials which you can
      do whatever you want with."

  * [[https://github.com/tinyobjloader/tinyobjloader][tinyobjloader/tinyobjloader]]
    * "Tiny but powerful single file wavefront obj loader written in
      C++03. No dependency except for C++ STL. It can parse over 10M
      polygons with moderate memory and time. tinyobjloader is good
      for embedding .obj loader to your (global illumination) renderer
      ;-)."

  * [[https://github.com/codelibs/libdxfrw][codelibs/libdxfrw]]
    * "C++ library to read and write DXF/DWG (Autocad file format)
      files. - libdxfrw is a free C++ library to read and write DXF
      files in both formats, ascii and binary form. Also can read DWG
      files from R14 to the last V2015. It is licensed under the terms
      of the GNU General Public License version 2 (or at you option
      any later version)."


  _Text and Font Rendering_

  + [[https://www.freetype.org/][The FreeType Project]]
    * Brief: "It is written in C, designed to be small, efficient,
      highly customizable, and portable while capable of producing
      high-quality output (glyph images) of most vector and bitmap
      font formats."

  + [[http://oglft.sourceforge.net/][OGLFT: OpenGL-FreeType Library]]
    * Brief: "This C++ library supplies an interface between the fonts
      on your system and an OpenGL or Mesa application. It uses the
      excellent FreeType library to read font faces from their files
      and renders text strings as OpenGL primitives."

  + [[https://github.com/vallentin/glText][GitHub - vallentin/glText]] - [HEADER-ONLY-LIBRARY]
    * Brief: "glText is a simple cross-platform single header text
      rendering library for OpenGL. glText requires no additional
      files (such as fonts or textures) for drawing text, everything
      comes pre-packed in the header."

  + [[https://github.com/MartinPerry/OpenGL-Font-Rendering][GitHub - MartinPerry/OpenGL-Font-Rendering]]
    + Brief: "Rendering UNICODE fonts with OpenGL This library is
      still work-in-progress. This is a working beta version."

  + [[http://nuclear.mutantstargoat.com/sw/libdrawtext/][libdrawtext - OpenGL text rendering library]]
    + Brief: "Libdrawtext uses freetype2 for glyph rasterization. If
      you would rather avoid having freetype2 as a dependency, you can
      optionally compile libdrawtext without it, and use pre-rendered
      glyphmaps. Glyphmaps can be generated by the included
      font2glyphmap tool, or by calling dtx_save_glyphmap."

  + [[https://github.com/codetiger/Font23D][GitHub - codetiger/Font23D]] - Convert any text to a 3d mesh using any font style
    + Brief: "Font23D is a C++ library for creating a 3d mesh of any
      Text in the given True type font."


 _C++ Wrappers_

  + [[https://oglplus.org/][OGplus]] - Self described as C++ Wrapper for modern OpengL.
    + Brief: "OGLplus is a header-only library which implements a thin
      object-oriented facade over the OpenGL® (version 3 and higher)
      C-language API. It provides wrappers which automate resource and
      object management and make the use of OpenGL in C++ safer and
      easier."
    + Repository: https://github.com/matus-chochlik/oglplu2

  + [[https://github.com/Tomius/oglwrap][Oglwrap]]
    + Brief: "Oglwrap is a lightweight, cross-platform,
      object-oriented, header-only C++ wrapper for modern (2.1+)
      OpenGL, that focuses on preventing most of the trivial OpenGL
      errors, and giving as much debug information about the other
      errors, as possible."

  + [[https://globjects.org/][Globjects]] - globjects is a cross-platform C++ wrapper for OpenGL
    API objects
    + Brief: "globjects provides object-oriented interfaces to the
      OpenGL API (3.0 and higher). It reduces the amount of OpenGL
      code required for rendering and facilitates coherent OpenGL use
      by means of an additional abstraction layer to glbinding and
      GLM. Common rendering tasks and processes are automated and
      missing features of specific OpenGL drivers are partially
      simulated or even emulated at run-time."

  _Form-based Graphical User Interface_  

   + [[https://github.com/ocornut/imgui][Dear imgui]]
     + Brief: "Dear ImGui is a bloat-free graphical user interface
       library for C++. It outputs optimized vertex buffers that you
       can render anytime in your 3D-pipeline enabled application. It
       is fast, portable, renderer agnostic and self-contained (no
       external dependencies)."
     + See: [[https://blog.conan.io/2019/06/26/An-introduction-to-the-Dear-ImGui-library.html][An introduction to Dear Imgui Library]]

  _Non-categorized / Miscellaneous_


  + [[https://github.com/bkaradzic/bgfx][bkaradzic/bgfx]]  - "Bring Your Own Engine/Framework"
    + Brief: Library for providing a unified interface to many GPU
      accelerated graphics APIs, including OpenGL, OpenGL ES, WebGL,
      WebGPU (W3C consoritum), DirectX (Microsoft), Metal (Apple) and
      Vulkan.
    + Note: This library also abstracts the shading language, a subset
      of GLSL OpenGL, that works with all mentioned GPU accelerated
      rendering backends. 
    + Documentation:
      * https://bkaradzic.github.io/bgfx/bgfx.html
      * https://bkaradzic.github.io/bgfx/examples.html
    + Community:
      + https://gitter.im/bkaradzic/bgfx
    + See:
      + [[https://software.intel.com/content/www/us/en/develop/articles/shader-debugging-for-bgfx-rendering-engine.html][Shader Debugging for BGFX Rendering Engine]] (Kai Wang)

  + [[http://glsdk.sourceforge.net/docs/html/index.html][GLSDK - Unofficial OpenGL Software Development Kit]]
    + Brief: "The Unofficial OpenGL Software Development Kit is a
      collection of libraries and utilities that will help you get
      started working with OpenGL. It provides a unified,
      cross-platform build system to make compiling the disparate
      libraries easier. Many of the components of the SDK are C++
      libraries. Each component of the SDK specifies the terms under
      which they are distributed. All licenses used by components are
      approximately like the MIT license in permissivity. The parts of
      the SDK responsible for maintaining the build, as well as all
      examples, are distributed under the MIT License."
 
  + [[https://github.com/NXPmicro/gtec-demo-framework][NXPmicro/gtec-demo-framework]]
    + "A multi-platform framework for fast and easy demo
      development. The framework abstracts away all the boilerplate &
      OS specific code of allocating windows, creating the context,
      texture loading, shader compilation, render loop, animation
      ticks, benchmarking graph overlays etc. Thereby allowing the
      demo/benchmark developer to focus on writing the actual 'demo'
      code. Therefore demos can be developed on PC or Android where
      the tool chain and debug facilities often allows for faster
      turnaround time and then compiled and deployed without code
      changes for other supported platforms. The framework also allows
      for ‘real’ comparative benchmarks between the different OS and
      windowing systems, since the exact same demo/benchmark code run
      on all of them."
    + Supported Operating Systems: Android NDK; Linux with various
      windowing systems (Yocto); Ubuntu 18.04; Windows 7+

*** WebGL Companion Libraries

WebGL Companion Libraries: (OpenGL on the WEB)

  + [[https://glmatrix.net/][glMatrix]] - GLM math library ported to Javascript. Computer
    graphics math library, similar to OpenGL GLM math library.

  + [[https://math.gl/docs][Math.GL]] - "math.gl is JavaScript math library focused on
    geospatial and 3D use cases, designed as a composable, modular
    toolbox. math.gl provides a core module with classic vector and
    matrix classes, and a suite of optional modules implementing
    various aspects of geospatial and 3D math. While the math.gl is
    highly optimized for use with the WebGL and WebGPU APIs, math.gl
    itself has no WebGL dependencies."

  + [[https://humbletim.github.io/glm-js/][GLM-JS]] - "glm-js is an experimental JavaScript implementation of
    the OpenGL Mathematics (GLM) C++ Library."

  + [[https://threejs.org/][Three.JS]] - High level wrapper library around WebGL, that provides
    many high level features that includes: camera objects; scene graphs;
    geometry; perspective; forward kinematics, inverse kinematics;
    graphics math library containing, matrices, vectors, quaternions;
    image loaders; animation and more.

  + [[https://github.com/kevinroast/phoria.js][phoria.js]] - [NOT WEBGL] "JavaScript library for simple 3D graphics
    and visualisation on a HTML5 canvas 2D renderer. It does not use
    WebGL. Works on all HTML5 browsers, including desktop, iOS and
    Android."
    + Note: It does use WebGL, but it is an interesting codebase about
      computer graphics algorithms implementation.
*** Sample 3D Models Repositories 

   + https://free3d.com/3d-models/obj-file 

   + [[https://github.com/alecjacobson/common-3d-test-models][common-3d-test-models]]
     + "This is a repository containing common 3D test models in
       original format with original source if known. While a similar
       list exists on wikipedia, it does not host the actual models
       and is incomplete." 

   + https://github.com/pichiliani/ModelsOBJ
     + "ModelsOBJ - .OBJ files with free 3D models (CC license or
       royalty free)"

   + [[https://gist.github.com/noonat/1131091/450ad7bbb6e0c8fba1854cc86a9f6b7a224fca56][Gist - cube.obj]]
     + Gist containing a cube 3D model obj file with vertices and normals.

   + [[https://raw.githubusercontent.com/kevinroast/phoria.js/master/teapot.obj][Teapot.obj]] - Teapot common 3D test model with normals.

Note: 3D models can be created using applications such as Blender,
MeshlLab, Autodesk Maya, Solidworks and so on. 

See also: 

  + [[https://en.wikipedia.org/wiki/List_of_common_3D_test_models][List of common 3D test models]]

  + [[https://gurumulay.github.io/misc/misc-5/][Smoothing .obj 3D models]] (Guru Mulay)

Obj File Documentation:

  + https://docs.fileformat.com/3d/obj/

  + [[https://www.loc.gov/preservation/digital/formats/fdd/fdd000507.shtml][Weverfron OBJ File Format]] - Library of Congress

*** See also

  + [[https://www.khronos.org/opengl/wiki/OpenGL_Loading_Library][OpenGL Loading Library - OpenGL Wiki]] / Khronos Group
    + Brief: "An OpenGL Loading Library is a library that loads
      pointers to OpenGL functions at runtime, core as well as
      extensions. This is required to access functions from OpenGL
      versions above 1.1 on most platforms. Extension loading
      libraries also abstracts away the difference between the loading
      mechanisms on different platforms."

  + [[https://www.khronos.org/opengl/wiki/Load_OpenGL_Functions][Load OpenGL Functions - OpenGL Wiki]]  / Khronos Group
    + Brief: "Loading OpenGL Functions is an important task for
      initializing OpenGL after creating an OpenGL context. You are
      strongly advised to use an OpenGL Loading Library instead of a
      manual process. However, if you want to know how it works
      manually, read on."

  + [[https://www.khronos.org/opengl/wiki/Image_Libraries][Image Libraries - OpenGL Wiki]]  / Khronos Group

  + [[https://stackoverflow.com/questions/27873784/when-do-i-need-to-use-an-opengl-function-loader][When do I need to use an OpenGL function loader? - Stack Overflow]]

  + [[https://keyj.emphy.de/glext/][KeyJ's Blog : Blog Archive » Modern OpenGL with lcc-win32, the hard way]]

  + [[https://apoorvaj.io/loading-opengl-without-glew/][Loading OpenGL without GLEW]]

  + [[https://www.libsdl.org/release/SDL-1.2.15/docs/html/guidevideoopengl.html][Using OpenGL With SDL]] - LibSDL

  + [[https://www.khronos.org/opengl/wiki/Tutorial1:_Creating_a_Cross_Platform_OpenGL_3.2_Context_in_SDL_(C_/_SDL)][Tutorial1: Creating a Cross Platform OpenGL 3.2 Context in SDL (C / SDL) - OpenGL Wiki]]

** Legacy/Obsolete OpenGL APIs / Subroutines

The following OpenGL subroutines are from the OpenGL _immediate mode_
(fixed-function pipeline), which are obsolete and should be avoided as
they incur on a significant overhead and they lack portability since
they are not be available on OpenGL ES.

Note: The best way to check wether a OpenGL subroutine is obsolete is
by searching for its name at http://docs.gl/. Subroutines without ES2,
ES3 or GL4 hyperlinks are obsolete. For instance, by searching for
'glLight' at this web site, there are no ES2, ES3 or GL4 hyperlinks,
which indicates that this subroutine is outdated.


Obsolete Subroutines:

  + Vertex:
    + => Modern OpenGL replacement: VBO (Vertex Buffer Object) and VAO
      (Vertex Array Object) and shader program.
    + glVertex2f()  => 2D coordinate of current vertex
    + glVertex3f()  => 3D Coordinate of current vertex
    + glNormal3f()  => Sets the surface normal vector for the current vertex.
    + glColor3f()   => Sets the color for the current vertex.
    + glColor4ub()  => Sets the color for the current vertex using RGB
      in byte format from 0 to 255.

  + Begin/End:
    + glEnd()
    + glBegin()

  + Colors
    + glColor()   => Modern OpenGL replacement: Fragement shader
    + glMaterial()
    + glVertexPointer()

  + Coordinate Transformation
    + Modern OpenGL Replacement: shader model matrix _uniform variable_
      which is set by the calling code. Since the matrix math (linear
      algebra) functionality is no longer provided by OpenGL, a third
      party math library is necessary.
    + glLoadIdentity()  => Modern OpenGL replacement: glm::mat4(1.0);
    + glRotate()        => Modern OpenGL replacement: glm::rotate();
    + glTranslate()     => Modern OpenGL replacement: glm::translate();
    + glScale()         => Modern OpenGL replacement: glm::scale();
    + glRotate3f()
    + glMatrixMode()
    + glFrustum()       => Modern OpenGL replacement: glm::frustum();
    + gluLookAt()       => Modern OpenGL replacement: glm::lookAt();
    + gluPerspective()  => Modern OpenGL replacement: glm::perspective();
    + glMatrixMode()
    + glViewPort()
    + glOrtho()
    + glMultMatrix()

  + Camera affine transforms:
    + gluLookAt() => Replacement: glm::lookAt()

  + Save Context
    + glPop()
    + glPush()

  + Matrix Stack [DEPRECATED!]
    + Modern OpenGL replacement: OpenGL since 3.0, no longer provides
      a matrix stack. Now the calling code that has to keep track of
      transformation state.
    + glPushMatrix()
    + glPopMatrix()

  + Light and illumination:
    + Modern OpenGL Replacement: Light and illumination model are
      computed on fragment shader or vertex shader.
    + glLight()
    + glLightModel()
    + glMaterial()
    + glNormal3f()

  + Miscellaneous / Non Categorized
    + glEnableClientState()
    + glColorPointer()
    + glVertexPointer(*)
    + glLight*...
    + glMaterial*...
    + glDrawPixels()
    + glPixelZoom()
    + glRasterPos2i()


See:

  + [[https://glm.g-truc.net/0.9.1/api/a00003.html][glm - Deprecated function replacements]]
    + "The OpenGL 3.0 specification deprecated some features, and most
      of these have been removed from the OpenGL 3.1 specfication and
      beyond. GLM provides some replacement functions. Many of these
      functions come from the GLM_GTC_matrix_transform: Matrix
      transform functions. extension." 

  + [[https://github.com/fendevel/Guide-to-Modern-OpenGL-Functions][A Guide to Modern OpenGL Functions]]

  + [[https://stackoverflow.com/questions/13647108/matrix-stacks-in-opengl-deprecated][Matrix stacks in OpenGL deprecated?]]

  + [[https://stackoverflow.com/questions/3755998/what-is-the-point-of-the-matrix-stack-in-opengl][What is the point of the matrix stack in OpenGL?]]

  + [[http://edeleastar.github.io/opengl-programming/topic05/pdf/3.MatrixStacks.pdf][OpenGL Matrix Stacks {PDF}]]

** Operating System Specific 
*** Microsoft Windows NT

  *Command Line Shortcuts for Troubleshooting* 

The following applications can be accessed either from command line
(cmd.exe shell) or via the shortcut Windows Key + R. 

    + $ _dxdiag_ => Tool for DirectX and OpenGL diagnostics.

    + $ _devmgmt.msc_ => Device manager shortcut.

    + $ [[https://support.microsoft.com/en-us/topic/description-of-microsoft-system-information-msinfo32-exe-tool-10d335d8-5834-90b4-8452-42c58e61f9fc][msinfo32]] => View details about hardware.

    + $ _systeminfo_ => View summarized information about operating
      system, hardware, RAM memory, disk space and patches. Note: It
      only works from command line. 

   *GPU details and OpenGL Implementation Information* 

    + [[http://realtech-vr.com/home/glview][OpenGL Extensions Viewer 6 | realtech VR]]
      + Graphical application that allows viewing details about Vulkan
        and OpenGL implementation at the current machine. 
      + Brief: "A reliable software which displays useful information
        about the current OpenGL 3D accelerator and new Vulkan 3D
        API. This program displays the vendor name, the version
        implemented, the renderer name and the extensions of the current
        OpenGL 3D accelerator."

    + [[https://www.geeks3d.com/20210125/gpu-caps-viewer-1-50-0-released/][GPU Caps View - Geeks3D]]
      + Brief: "A new version of GPU Caps Viewer is available. GPU
        Caps Viewer is a graphics card / GPU information and
        monitoring utility that quickly describes the essential
        capabilities of your GPU including GPU type, amount of VRAM ,
        OpenGL, Vulkan, OpenCL and CUDA API support level."

    + [[https://www.geeks3d.com/gputest/][GPU Test]]
      + Brief: "GpuTest is a cross-platform (Windows, Linux and Max OS
        X) GPU stress test and OpenGL benchmark. GpuTest comes with
        several GPU tests including some popular ones from
        Windows'world (FurMark or TessMark)." 

  *Setting Default GPU on Windows* 

    + [[https://on1help.zendesk.com/hc/en-us/articles/115000138471][How to update Video Card Driver and Set Default GPU]] 

*** Linux-Based Operating Systems 

  *Install OpenGL development dependencies*

Install OpenGL development dependencies on Ubuntu or Debian-like
distributions:

#+BEGIN_SRC sh
   $ sudo apt-get install -y libgl1-mesa-dev libglu1-mesa-dev freeglut3-dev  libgl1-mesa-dev libxrandr-dev
   $ sudo apt-get install -y libxinerama-dev libxcursor-dev libxi-dev
#+END_SRC

 *Information about OpenGL and graphics card*

Vendor:

#+BEGIN_SRC sh
   $ >> glxinfo | grep -E "vendor"
  server glx vendor string: SGI
  client glx vendor string: Mesa Project and SGI
  OpenGL vendor string: Intel
#+END_SRC

Rendering:

#+BEGIN_SRC sh
   $ >> glxinfo | grep -E "rendering"
  direct rendering: Yes
#+END_SRC

Version:

#+BEGIN_SRC sh
   $ >> glxinfo | grep -E "version"
  server glx version string: 1.4
  client glx version string: 1.4
  GLX version: 1.4
      Max core profile version: 4.6
      Max compat profile version: 4.6
      Max GLES1 profile version: 1.1
      Max GLES[23] profile version: 3.2
  OpenGL core profile version string: 4.6 (Core Profile) Mesa 20.3.2
  OpenGL core profile shading language version string: 4.60
  OpenGL version string: 4.6 (Compatibility Profile) Mesa 20.3.2
  OpenGL shading language version string: 4.60
  OpenGL ES profile version string: OpenGL ES 3.2 Mesa 20.3.2
  OpenGL ES profile shading language version string: OpenGL ES GLSL ES 3.20
      GL_EXT_shader_implicit_conversions, GL_EXT_shader_integer_mix,
#+END_SRC

Display:

#+BEGIN_SRC sh
   $ >> glxinfo | grep -E "display"
  name of display: :1
  display: :1  screen: 0
#+END_SRC
** Computer Graphics Math
*** Overview

Computer graphics math is based on vector algebra, linear algebra and
affine transforms. Those concepts are not exclusive to OpenGL, they
are essential and universal to all computer graphics APIS -
Application Programming Interfaces, including OpenGL, DirectX, Metal,
Vulkan, WebGL and html5 canvas.
*** Vector Algebra

Given 2 3D vectors $\vec{A} = [ x_A, y_A, z_A ]$ and $\vec{B} = [ x_B, y_B, z_B ]$
, the following properties can be defined:

 *Vector Sum*

\begin{equation}
  \vec{C} = \vec{A} + \vec{B}
          = (x_A + x_B) \hat{i} + (y_A + y_B) \hat{j} + (z_A + z_B) \hat{k}
\end{equation}

 *Vector Difference / Subtraction*

\begin{equation}
  \vec{C} = \vec{B} - \vec{A}
          = (x_B - x_A) \hat{i} + (y_B - y_A) \hat{j} + (z_B - z_A) \hat{k}
\end{equation}

 *Vector Norm*

The _norm_ or _magnitude_ of A, $|| \vec{A} ||$ is given by:

\begin{equation}
   || \vec{A} || = \sqrt{ x_A^2 + y_A^2 + z_A^2 }
\end{equation}


\begin{equation}
   || \vec{A} ||^2 = \vec{A} \cdot \vec{A}
\end{equation}

 *Normalized Vector / Unit Vector*

A _normalized vector_, is a vector with the same direction than a given
vector, but with norm equal to one. A normalized vector of A can be
computed as:

\begin{equation}
    \text{normalized}( \vec{A} )
       = \frac{ \vec{A} }{ || A || }
       = \frac{1}{ \sqrt{ x_A^2 + y_A^2 + z_A^2 } } . \vec{A}
\end{equation}

\begin{equation}
   \text{ normalized }( \vec{A} )
   = \frac{1}{ \sqrt{ x_A^2 + y_A^2 + z_A^2 } } . ( x_A \cdot \hat{i} + y_A \cdot \hat{j} + z_A \cdot \hat{k} )
\end{equation}

Where:
  + $\hat{i}$, $\hat{j}$, $\hat{k}$ are the unit vectors of axis X, Y
    and Z respectively.

\begin{equation}
    || \text{normalized}( \vec{A} ) || = 1
\end{equation}


 *Distance between two position (point) vectors*

Given two vectors A and B which represent the position relative to the
coordinate system origin or point. (0, 0, 0). The distance between A
and B, or the length of the vector difference between A and B is
determined by the following relation.

\begin{eqnarray*}
   \text{distance}( \vec{A}, \vec{B}) &=& || \vec{A}  - \vec{B} || = || \vec{B}  - \vec{A} || \\
   \text{distance}( \vec{A}, \vec{B}) &=& \sqrt{ (x_A - x_B)^2 + (y_A - y_B)^2 + (z_A - z_B)^2 }
\end{eqnarray*}


 *Element-wise Product* 

The element-wise product between two vectors is a vector which the
components are the product between the two vectors components. As
there is no standard mathematical notation for element-wise product
between two vectors, the symbol $\odot$ will be used for denoting this
operation and avoiding confusion with dot product or vector
product. This notation is useful for describing illumination
(lighting) calculations, which most books and introduction materials
present without a clear notation. 


\begin{equation} 
   \vec{A} \odot \vec{B} = \begin{bmatrix} x_A \cdot x_B \\ y_A \cdot y_B \\ z_A \cdot z_B  \end{bmatrix} 
                         = \begin{bmatrix} 
                               x_A &   0  &  0  \\ 
                                 0 & y_A  &  0  \\ 
                                 0 &   0  & z_A  
                           \end{bmatrix}
                           \begin{bmatrix} x_B \\ y_B \\ z_B  \end{bmatrix}
\end{equation}

 *Dot Product (a.k.a - Scalar Product)*

The vector _dot product_ is:

\begin{equation}
  \text{dot}( \vec{A}, \vec{B} )
    = \vec{A} \cdot \vec{B}
    = x_A \cdot x_B + y_A \cdot y_B + z_A \cdot z_B
\end{equation}

  + Where:
    + $|| \vec{A} ||$ is the norm of vector A
    + $|| \vec{B} ||$ is the norm of vector B

  + The vectors $\vec{A}$ and $\vec{B}$ are _orthogonal_, the angle
    between them is 90 degrees, when the dot product is zero.


The dot product has the property:

\begin{equation}
  \text{dot}( \vec{A}, \vec{B} )
    = \vec{A} \cdot \vec{B}
    = || \vec{A} || \cdot || \vec{B} || \cdot \cos \theta
\end{equation}

And also:

\begin{equation}
   \cos \theta =  \frac{ \vec{A} \cdot \vec{B}  }{ || \vec{A} || \cdot || \vec{B} ||  }
\end{equation}

Where:
   + $\theta$ is the angle between vectors A and B.


If the vectors $\vec{A}$ and $\vec{B}$ are expressed as column
matrices, the product can be computed as a matrix multiplication.

  + In the following equations. $A^T$ is the transpose of matrix A.

\begin{equation}
  A = \begin{bmatrix} x_A \\ y_A \\ z_A  \end{bmatrix}
\end{equation}

\begin{equation}
  B = \begin{bmatrix} x_B \\ y_B \\ z_B  \end{bmatrix}
\end{equation}


\begin{equation}
  dot(\vec{A}, \vec{B})
     = \vec{A} \cdot \vec{B}
     = A^T \cdot B
     =
        \begin{bmatrix} x_A & y_A & z_A  \end{bmatrix}
        \cdot
        \begin{bmatrix} x_B \\ y_B \\ z_B  \end{bmatrix}
\end{equation}


  *Cross Product* *(a.k.a - Vector product)*

The cross product between two vectors $\vec{A}$ and $\vec{B}$ results
in a vector which is perpendicular (orthogonal) to both A and B.

\begin{equation}
   \text{cross}( \vec{A}, \vec{B} )

        = \vec{A} \times \vec{B}

        = \begin{bmatrix}
             y_A \cdot z_B - z_A \cdot y_B   \\
             z_A \cdot x_B - x_A \cdot z_B   \\
             x_A \cdot y_B - y_A \cdot x_B   \\
          \end{bmatrix}

       = [A]_{\times} \cdot B
\end{equation}


Where $[A]_{\times}$ is the _cross product matrix_.

\begin{equation}
  [A]_{\times} =
     \begin{bmatrix}
          0   & -z_A  &  y_A  \\
          z_A &  0    & -x_A  \\
         -y_A &  x_A  &  0    \\
     \end{bmatrix}
\end{equation}

The cross product have the following properties:

\begin{equation}
  || \vec{A} \times \vec{B} || = || \vec{A} || \cdot || \vec{B} || \cdot \sin \theta
\end{equation}

\begin{equation}
  \vec{A} \times \vec{B} = || \vec{A} || \cdot || \vec{B} || \cdot \sin \theta \cdot \hat{n}
\end{equation}


\begin{equation}
   \vec{A} \times \vec{B} = - \vec{B} \times \vec{A}
\end{equation}


  + Where:
    + $\vec{n}$ is unit vector with the same direction as the cross
      product vector.
    + $\theta$ is the angle between the two underlying vectors.


  *Vector Triple Product*

\begin{equation}
   ( \vec{u} \times \vec{v} ) \times \vec{w}
         = ( \vec{v} \cdot \vec{w} ) \vec{u} + ( \vec{u} \cdot \vec{w}) \vec{v}
         = \text{dot}( \vec{v}, \vec{w} ) \vec{u} + \text{dot}( \vec{u}, \vec{w}) \vec{v}
\end{equation}


  *Relation between vectors*

 _Orthogonal (perpendicular) vectors:_

Two vectors $\vec{A}$ and $\vec{B}$ are orthogonal, the angle between
them is 90 degrees or PI/2 radians, if their dot product is zero.

\begin{equation}
   \vec{A} \cdot \vec{B} = 0
\end{equation}


 _Parallel vectors_

Two vectors $\vec{A}$ and $\vec{B}$ are parallel when the angle between
them are zero or 180 degrees. When this happens, then:

\begin{equation}
   \vec{A} \times \vec{B} = 0
\end{equation}


  _Same direction_

Let the unit vector that points in the same direction of a vector
$\vec{A}$ be $[\vec{A}]_u$, where $[ \cdot ]_u$ is a operator that
determines the unit vector of a vector.

\begin{equation}
   [\vec{A}]_u = \frac{1}{|| \vec{A} || } \vec{A}
               = \frac{1}{|| \vec{A} || } (x_A \cdot \hat{i} + y_A \cdot \hat{j} + z_A \cdot \hat{k} )
\end{equation}

A pair of vector $\vec{A}$ and $\vec{B}$ have the same direction both
are parallel and the angle between them are zero. When this happens,
both have the same unit vector or then angle between the are zero:

\begin{equation}
   [ \vec{A} ]_u = [ \vec{B} ]_u
\end{equation}

Or: 

\begin{equation}
   \vec{A} \cdot \vec{B} - || \vec{A} || \cdot || \vec{B} || = 0
\end{equation}

Two vectors are parallel and have opposite direction (angle between
them is 180 degrees) if the following relation holds.

\begin{equation}
   \vec{A} \cdot \vec{B} + || \vec{A} || \cdot || \vec{B} || = 0
\end{equation}


  _Angle between two vectors_

The angle between two 3D vectors can be determined by using their dot
product and cross product.

\begin{equation}
  \theta = \text{atan2}( || \vec{A} \times \vec{B} ||, \vec{A} \cdot \vec{B}  )
         = \text{atan2} \left( \text{norm}(  \text{cross}( \vec{A}, \vec{B})) , \text{dot}(\vec{A}, \vec{B})  \right)
\end{equation}
*** Affine Transforms

Affine transforms, which are represented by matrices, are a particular
class of linear transforms which preserves ratios between distances,
colinearity and parallelism. Affine transforms has many applications
that includes, computer graphics, computer vision, image processing,
CAD (Computer Aided Design) and robotics.

Outline of properties preserved by affine transforms:

  + Ratios between distances.

  + Colinearity
    + => Points in the same line, remains in the same line, after the
      transform was applied.

  + Parallelism
    + => Lines that are parallels, remains parallel.


Types of geometric linear transforms:

  * Affine Transforms
    + => Preserves ratios, colinearity and parallelism. Some affine
      transforms are: _identity_, _translation_, _rotation_ and _scaling_.

  * Projection Transforms
    + => They not preserve parallelism. However, they preserve
      colinearity. Affine transforms are a particular case of
      projection transforms.
    + => Some projection transforms are:
      + Orthogonal view transform
      + Projection view transform

  * Rigid body transforms
    + => Are a particular case of affine transforms. The set of rigid
      body transforms comprises: _identity_, _translation_ and
      _rotation_. This set of transforms does not include shear and scaling.
    + => Use cases: Computer graphics, Newtonian mechanics, robotics,
      aerospace and many other cases.


Some affine transforms are:

  + Identity (Identity matrix) => Represents no transform, all points
    or vertices remain the same.

  + Translation

  + Scaling

  + Reflection

  + Rotation

  + Shear  


  *Further Reading*

General:

  + [[http://graphics.cs.cmu.edu/nsp/course/15-462/Spring04/slides/04-transform.pdf][CMU - Transformations]]

  + [[http://www.cs.cornell.edu/courses/cs4620/2013fa/lectures/04transforms2d.pdf][Cornell CS4620 - 2D Geometric Transformations]]

  + [[https://web.iitd.ac.in/~hegde/cad/lecture/L6_3dtrans.pdf][AML170 CAD - 3D Transformations - Lecture 6]]

  + [[https://mycourses.aalto.fi/pluginfile.php/352115/mod_resource/content/1/01_transform.pdf][CS_CS3100 (Introduction to) Computer Graphics]]

Coordinate Systems:

  + [[https://en.m.wikipedia.org/wiki/Coordinate_system][Coordinate system - Wikipedia]]
    + Brief: "n geometry, a coordinate system is a system that uses
      one or more numbers, or coordinates, to uniquely determine the
      position of the points or other geometric elements on a manifold
      such as Euclidean space."

  + [[https://en.m.wikipedia.org/wiki/Homogeneous_coordinates][Homogeneous coordinates - Wikipedia]]
    + Brief: "Homogeneous coordinates have a range of applications,
      including computer graphics and 3D computer vision, where they
      allow affine transformations and, in general, projective
      transformations to be easily represented by a matrix."

Rotation Matrix and right-hand-rule:

  + [[https://en.m.wikipedia.org/wiki/Right-hand_rule][Right-hand rule - Wikipedia]]
    + Brief: "In mathematics and physics, the right-hand rule is a
      common mnemonic for understanding orientation of axes in
      three-dimensional space."

  + [[https://en.m.wikipedia.org/wiki/Rotation_matrix][Rotation matrix - Wikipedia]]
    + Brief: "In linear algebra, a rotation matrix is a transformation
      matrix that is used to perform a rotation in Euclidean space."

  + [[https://en.m.wikipedia.org/wiki/Rotation_formalisms_in_three_dimensions][Rotation formalisms in three dimensions - Wikipedia]]
    + Brief: "In geometry, various formalisms exist to express a
      rotation in three dimensions as a mathematical
      transformation. In physics, this concept is applied to classical
      mechanics where rotational (or angular) kinematics is the
      science of quantitative description of a purely rotational
      motion."

  + [[https://en.m.wikipedia.org/wiki/Axes_conventions][Axes conventions - Wikipedia]]
    + Brief: "In ballistics and flight dynamics, axes conventions are
      standardized ways of establishing the location and orientation
      of coordinate axes for use as a frame of reference."

  + [[https://en.m.wikipedia.org/wiki/Euler_angles][Euler angles - Wikipedia]]
    + Brief: "The Euler angles are three angles introduced by Leonhard
      Euler to describe the orientation of a rigid body with respect
      to a fixed coordinate system."

  + [[https://en.m.wikipedia.org/wiki/Aircraft_principal_axes][Yaw, Pitch, Roll angles - Aircraft principal axes - Wikipedia]]
    + Brief: Rotation angles convention for aircrafts.

  + [[https://en.m.wikipedia.org/wiki/Celestial_coordinate_system][Celestial coordinate system - Wikipedia]]
    + Brief: "In astronomy, a celestial coordinate system (or
      celestial reference system) is a system for specifying positions
      of satellites, planets, stars, galaxies, and other celestial
      objects relative to physical reference points available to a
      situated observer (e.g. the true horizon and north cardinal
      direction to an observer situated on the Earth's surface)"

  + [[https://en.m.wikipedia.org/wiki/Conversion_between_quaternions_and_Euler_angles][Conversion between quaternions and Euler angles - Wikipedia]]
    + Brief: "Spatial rotations in three dimensions can be
      parametrized using both Euler angles and unit quaternions. This
      article explains how to convert between the two
      representations. Actually this simple use of "quaternions" was
      first presented by Euler some seventy years earlier than
      Hamilton to solve the problem of magic squares."

Affine Tranform Matrices in SVG, Html5 Canvas and WebGL:

  + [[https://blog.carbonfive.com/taming-2d-transforms/][Taming 2D Transforms in Html5 Canvas]]

  + [[https://www.w3resource.com/html5-canvas/html5-canvas-matrix-transforms.php][Html5 Canvas: Matrix Transforms]]

  + [[https://www.html5rocks.com/en/tutorials/webgl/webgl_transforms/][Html5 Rocks - WebGL Transforms]] (WebGL - OpenGL for the web)

  + [[https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/transform][MDN - SVG Transform matrices (Mozilla)]]

  + [[https://www.w3.org/TR/SVGTiny12/coords.html][W3C - 7 Coordinate Systems, Transformations and Units]] (SVG)

  + [[https://thesoftwaresimpleton.com/blog/2018/09/25/affine-matrix][SVG Transformations with affine matrices]] (Paul Cowan)

  + [[https://bl.ocks.org/helderdarocha/8b28505082bf1c81977d7dec797686c7][Examples using matrix transforms in SVG]]

Affine Transform Matrices in DirectX (Direct3D):

  + [[https://docs.microsoft.com/en-us/windows/win32/direct3d9/transforms][MSDN - Transforms (Direct3D 9)]]

  + [[https://docs.microsoft.com/en-us/windows/win32/direct3d9/world-transform][MSDN - World Transform]]

  + [[https://docs.microsoft.com/en-us/windows/win32/direct3d9/view-transform][MSDN - View Transform]]

  + [[https://docs.microsoft.com/en-us/windows/win32/direct3d9/projection-transform][MSDN - Projection Transform]]

  + [[https://docs.microsoft.com/en-us/windows/win32/direct3d9/viewports-and-clipping][MSDN - Viewport and Clipping]]

  + [[https://docs.microsoft.com/en-us/windows/win32/direct3d9/d3dxmatrixaffinetransformation][D3DXMatrixAffineTransformation function (D3dx9math.h)]]


Affine Transform Matrices in Java AWT:

  + [[https://docs.oracle.com/javase/7/docs/api/java/awt/geom/AffineTransform.html][Oracle - Class AffineTransform]]

  + [[https://www.developer.com/net/vb/article.php/626051/Java-2D-Graphics-Simple-Affine-Transforms.htm][Java 2D Graphics, Simple Affine Transforms]]

Affine Transform Matrices in non-categorized APIs

 + [[https://brlcad.org/OLD/reports/tr-313/chapt3.html][BRL Cad - Object Motion and Matrix Manipulation]]

 + [[https://docs.geotools.org/latest/userguide/tutorial/affinetransform.html][Affine Transformation Tutorial - GeoTools OSGeo]]

 + [[https://wiki.freecadweb.org/Macro_MatrixTransform][FreeCAD - Macro MatrixTransform]]

 + [[https://cadbooster.com/complete-overview-of-matrix-transformations-in-the-solidworks-api/][A complete overview of matrix transformations in the SOLIDWORKS API]]
   (SolidWorks CAD for mechanical engineering)

*** 2D Canonical Affine Transforms

   *General form of 2D affine transforms*

2D affine transforms, including translation, rotation and scaling can
be represented by matrices with the following format, that transforms
homogeneous coordinates from one coordinate system to
another. _Homogeneous coordinates_, are 2D or 3D coordinates with an
extra pseudo-coordinate, often designated by 'w', for representing
translations affine transforms in the same way as rotations and
scaling.


\begin{equation}
  A =
   \begin{bmatrix}
     a_{11}   & a_{12}   &  a_{13} \\
     a_{21}   & a_{22}   &  a_{23} \\
     0        & 0        &  1 \\
   \end{bmatrix}
\end{equation}

This affine transform performs the coordinate transformation from the
coordinate frame C2 (which axis are x, y), which could an object
local-space coordinate, to coordinate frame C1 (which axis are x', y')
which could be a world-coordinate system. The matrix transforms
homogenous coordinates, which are coordinates with an extra parameter
w = 1 for allowing translation transformation to be expressed in the
same way as rotation transformations.


\begin{equation}
     \begin{bmatrix}
       x' \\ y' \\ w' = 1
      \end{bmatrix}
  =
    \begin{bmatrix}
      a_{11}   & a_{12}   &  a_{13} \\
      a_{21}   & a_{22}   &  a_{23} \\
      0        & 0        &  1      \\
   \end{bmatrix}

     \cdot

     \begin{bmatrix}
       x \\ y \\ w = 1
     \end{bmatrix}

    =

    \begin{bmatrix}
        a_{11} \cdot x + a_{12}  \cdot y + a_{13}  \\
        a_{21} \cdot x + a_{22}  \cdot y + a_{23}  \\
        1
    \end{bmatrix}
\end{equation}

The multiplication between two affine transforms also results in a
affine transform. Consider two affine transforms $A = a_{ij}$ 
and $B = b_{ij}$. The product between these two affine transforms is
also an affine transform. 

\begin{equation} 
   C = A \cdot B = 

   \begin{bmatrix}
     a_{11}   & a_{12}   &  a_{13} \\
     a_{21}   & a_{22}   &  a_{23} \\
     0        & 0        &  1 \\
   \end{bmatrix}

   \begin{bmatrix}
     b_{11}   & b_{12}   &  b_{13} \\
     b_{21}   & b_{22}   &  b_{23} \\
     0        & 0        &  1 \\
   \end{bmatrix}

  = 

   \begin{bmatrix}
       c_{11} & c_{12} & c_{13}  \\ 
       c_{21} & c_{22} & c_{23}  \\
         0    &  0     &  1 
   \end{bmatrix}
\end{equation}

Where: 

  + $c_{11} = a_{11} \cdot b_{11} + a_{12} \cdot b_{21}$
  + $c_{12} = a_{11} \cdot b_{12} + a_{12} \cdot b_{22}$
  + $c_{21} = a_{21} \cdot b_{11} + a_{22} \cdot b_{21}$
  + $c_{22} = a_{21} \cdot b_{12} + a_{22} \cdot b_{22}$
  + $c_{13} = a_{11} \cdot b_{13} + a_{12} \cdot b_{23} + a_{13}$
  + $c_{23} = a_{21} \cdot b_{13} + a_{22} \cdot b_{23} + a_{23}$



  *2D Canonical Affine Transforms*


Identity matrix

  + Causes no coordinate transformation. The result coordinate system
    and vertices remain at the same position. The identity matrix is
    also a reasonable initial default value for the model matrix, view
    matrix and projection matrix. 

\begin{equation}
  I =
   \begin{bmatrix}
     1   & 0   &  0 \\
     0   & 1   &  0 \\
     0   & 0   &  1 \\
   \end{bmatrix}
\end{equation}


\begin{equation}
     \begin{bmatrix}
       x' \\ y' \\ 1
      \end{bmatrix}
  =

     I
     \cdot

   \begin{bmatrix}
       x \\ y \\ w = 1
   \end{bmatrix}
  =

   \begin{bmatrix}
     1   & 0   &  0 \\
     0   & 1   &  0 \\
     0   & 0   &  1 \\
   \end{bmatrix}


   \begin{bmatrix}
       x \\ y \\ 1 
   \end{bmatrix}

  = 
   \begin{bmatrix}
       x  \\ y  \\ 1
   \end{bmatrix}
\end{equation}


Translation:

  + Translate the coordinate system from one position into another and
    translate vertices. 

\begin{equation}
  T =
   \begin{bmatrix}
     1   & 0   &  \Delta_x \\
     0   & 1   &  \Delta_y \\
     0   & 0   &  1        \\
   \end{bmatrix}
\end{equation}


\begin{equation}
     \begin{bmatrix}
       x' \\ y' \\ 1
      \end{bmatrix}
  =

   \begin{bmatrix}
     1   & 0   &  \Delta_x \\
     0   & 1   &  \Delta_y \\
     0   & 0   &  1        \\
   \end{bmatrix}

     \cdot

   \begin{bmatrix}
       x \\ y \\ w = 1
   \end{bmatrix}

  =
   \begin{bmatrix}
       x + \Delta_x \\ y + \Delta_y \\ 1
   \end{bmatrix}
\end{equation}


Scaling:

  + Increases or decreases object size. This transformation allows
    resizing an object without sending the vertices multiple times to
    the GPU in the case of a GPU accelerated graphics API.
  + Where: $s_x$ is the scale for X axis and $s_y$ is the scale for
    the y axis.

\begin{equation}
  S =
   \begin{bmatrix}
     s_x   & 0     &  0 \\
     0     & s_y   &  0 \\
     0     & 0     &  1 \\
   \end{bmatrix}
\end{equation}


\begin{equation}
     \begin{bmatrix}
       x' \\ y' \\ 1
      \end{bmatrix}
  =

   \begin{bmatrix}
     s_x   & 0     &  0 \\
     0     & s_y   &  0 \\
     0     & 0     &  1 \\
   \end{bmatrix}
     \cdot

   \begin{bmatrix}
       x \\ y \\ w = 1
   \end{bmatrix}

  =
   \begin{bmatrix}
       s_x \cdot x \\ s_y \cdot y \\ 1
   \end{bmatrix}
\end{equation}


Shear:

\begin{equation}
  H =
   \begin{bmatrix}
     1     & h_x  &  0 \\
     h_y   & 1    &  0 \\
     0     & 0    &  1 \\
   \end{bmatrix}
\end{equation}


Rotation around Z axis:

  + Where $\theta$ is the angle of counterclockwise rotation around Z
    axis. A negative angle results in a rotation in the opposite
    direction. 

\begin{equation}
  R  =
   \begin{bmatrix}
     \cos \theta     & -\sin \theta  &  0 \\
     \sin \theta     &  \cos \theta  &  0 \\
     0               &  0            &  1 \\
   \end{bmatrix}
\end{equation}


\begin{equation}
     \begin{bmatrix} x' \\ y' \\ 1  \end{bmatrix}

    =
      \begin{bmatrix}
        \cos \theta     & -\sin \theta  &  0 \\
        \sin \theta     &  \cos \theta  &  0 \\
        0               &  0            &  1 \\
      \end{bmatrix}

     \begin{bmatrix} x \\ y \\ 1  \end{bmatrix}
    =
     \begin{bmatrix}
          x \cos \theta - y \sin \theta  \\
          x \sin \theta + y \cos \theta \\
          1
     \end{bmatrix}
\end{equation}
*** 2D Window to Viewport transform

The window-to-viewport affine transform maps coordinates from a
world-space window to a viewport (physical display coordinates). The
world-space window defines what region of the world-space will be
viewed. It is specified using the world-space coordinates $x_{wmin}$,
$x_{wmax}$, $y_{wmin}$ and $y_{wmax}$. The viewport is a rectangle
within the graphics display window to where the world-window
coordinates will be mapped to. The viewport window is defined by the
coordinates: $x_{vmin}$, $x_{vmax}$, $y_{vmin}$, $y_{vmax}$ - given in
screen device-coordinates with origin at the graphics display bottom
left corner.

Note: In OpenGL this transform can be obtained using the subroutines
glViewport() and glm::ortho().

 *Applications*

  + Draw in a limited area of screen. (Note: it is often possible to
    define multiple viewport windows.)

  + Draw using user-defined coordinates and make the drawing
    independent of the screen size.

  + Draw multiple views of the world-space.

  + Draw charts (a.k.a curve plotting).

  + Draw multiple charts on the same screen.

 *Parts:*

  * _World-Window_ (a.k.a clipping window) => Defines _what_ the user wants to
    see from the world-space. Define by: $x_{wmin}$, $x_{wmax}$,
    $y_{wmin}$ and $y_{wmax}$.
    + $x_{wmin}$ - Minimum X axis coordinate from window-space that can be viewed.
    + $x_{wmax}$ - Maximum X axis coordinate from window-space that can be viewed.
    + $y_{wmin}$ - Minimum y axis coordinate from window-space that can be viewed.
    + $y_{wmax}$ - Maximum y axis coordinate from window-space that can be viewed.

  * _Viewport_ => Defines _where_ the user wants to see the _world-window_
    within the graphics display window (a.k.a canvas).
    + $x_{vmin}$  => $0 \leq x_{vmin} \leq w$
    + $x_{vmax}$  => $0 \leq x_{vmin} \leq w$
    + $y_{vmin}$  => $0 \leq y_{vmin} \leq h$
    + $y_{vmax}$  => $0 \leq y_{vmax} \leq h$

  * Default values of viewport:
    + $x_{vmin} = 0$
    + $x_{vmax} = w$
    + $y_{vmin} = 0$
    + $y_{vmax} = h$

Where:
  + h - graphics display screen height, often in pixels.
  + w - graphics display screen width, often in pixelss

#+CAPTION: World-to-viewport transform
[[file:images/world-window-to-viewport.png][file:images/world-window-to-viewport.png]]

The window-to-viewport transform matrix can be computed as:

\begin{equation}
   T_{ W \rightarrow V} =
       \begin{bmatrix}
           s_x  &  0    & t_x \\
           0    &  s_y  & t_y \\
           0    &  0    & 1   \\
       \end{bmatrix}
\end{equation}

Where:

\begin{eqnarray*}
   s_x &=& \frac{\Delta x_{v} }{ \Delta x_{w} }
       = \frac{ x_{vmax} - x_{vmin} }{x_{wmax} - x_{wmin}}  \\

   s_y &=& \frac{\Delta y_{v} }{ \Delta y_{w} }
           = \frac{ y_{vmax} - y_{vmin} }{y_{wmax} - y_{wmin}}  \\
   t_x &=& x_{vmin} - s_x \cdot x_{wmin}                      \\
   t_y &=& y_{vmin} - s_y \cdot y_{wmin}                      \\
\end{eqnarray*}

Coordinates from world-space can be mapped to the screen-device space
by applying the affine transform $T_{ W \rightarrow V}$ to the
world-space coordinates, designated by $x_w$, $y_w$.

\begin{equation}
     \begin{bmatrix} x_v \\ y_v \\ 1 \end{bmatrix}
  =
    T_{ W \rightarrow V} \cdot
    \begin{bmatrix} x_w \\ y_w \\ 1 \end{bmatrix}
\end{equation}

\begin{equation}
     \begin{bmatrix} x_v \\ y_v \\ 1 \end{bmatrix}
  =
    \begin{bmatrix}
       s_x \cdot x_w + t_x \\
       s_y \cdot y_w + t_y \\
       1
    \end{bmatrix}
  =
    \begin{bmatrix}
       s_x (x_w - x_{wmin}) + x_{vmin} \\
       s_y (x_y - y_{wmin}) + y_{vmin} \\
       1
    \end{bmatrix}
\end{equation}

Finally, the coordinates $x_v$ and $y_v$ can be determined in a more
human-friendly way with the following expression:

\begin{eqnarray*}
   x_v &=&  s_x (x_w - x_{wmin}) + x_{vmin} \\
   y_v &=&  s_y (y_w - y_{wmin}) + y_{vmin} \\
\end{eqnarray*}

 *Image distortion*

The world-space propotions will only be preserved when the following
predicate holds. For instance, a square in the world-space will look
like a rectangle if the rations $s_x$ and $s_y$ are not equal.

\begin{equation}
   s_x = s_y
\end{equation}

In orther words,

\begin{equation}
   \frac{ x_{vmax} - x_{vmin} }{x_{wmax} - x_{wmin}} = \frac{ y_{vmax} - y_{vmin} }{y_{wmax} - y_{wmin}}
\end{equation}

 *Upper-left coordinate system*

If the graphics API has a default upper-left coordinate system, where
the origin is at the upper-left corner of the display window and Y
axis is pointing downwards. The viewport matrix transform becomes:

\begin{equation}
   T_{ W \rightarrow VU} =
       \begin{bmatrix}
           s_x  &  0     & t_x      \\
           0    &  -s_y  & h - t_y  \\
           0    &  0     &  1       \\
       \end{bmatrix}
\end{equation}

Then, the $x_v$, $y_v$ coordinates can be computed as: 


\begin{eqnarray*}
   x_v &=&  +s_x (x_w - x_{wmin}) + x_{vmin}       \\
   y_v &=&  -s_y (y_w - y_{wmin}) + (h - y_{vmin}) \\
\end{eqnarray*}

 *References and further reading:*

  + [[https://www.khronos.org/opengl/wiki/Vertex_Post-Processing#Viewport_transform][Khronos Group - Viewport transform]]
    + "The viewport transform defines the transformation of vertex
      positions from NDC space to window space. These are the
      coordinates that are rasterized to the output image. The
      viewport is defined by a number of viewport parameters. These
      parameters are set by these functions: _glViewport_; _glDepthRange_;
      _glDepthRangef_."

  + [[http://mcasjcet.weebly.com/uploads/4/4/7/9/4479347/computer_graphics_second_module_second.pdf][MCA - 301 - Computer Graphics]]

  + [[https://eaymanelshenawy.files.wordpress.com/2016/02/gc-lecture-005-viewingand-clipping.pdf][Computer Graphics Viewing Objective]]

  + [[https://www.sfu.ca/sasdoc/sashtml/iml/chap12/sect7.htm][Windows and viewports]]

  + [[https://web.cse.ohio-state.edu/~shen.94/581/Site/Slides_files/2dDrawing.pdf][Drawing and coordinate system]]

*** 3D Affine Transforms

  *Notations for homogenous coordinate*

Point-Vector - for denoting postion, location, point in space or vertex: 

  + It represents a point or a vertex in space, a coordinate relative
    to the origin of the coordinate system. Some valid affinte
    transform operations are translation, rotation, shear and scaling.
    
\begin{equation}
  p =
   \begin{bmatrix}
     x  \\ y \\ z \\ w = 1
   \end{bmatrix}
  = 
   \begin{bmatrix}
     x  \\ y \\ z \\ 1
   \end{bmatrix}

\end{equation}

Vector - for denoting force, acceleration, or difference between two
point-vectors, and so on:

  + It represents entities, such as difference between two
    point-vectors, force, speed, angular speed, acceleration and so
    on, with a magnitude and direction. It does not make sense to
    apply translation and scaling to those entities, as a result the
    pseudo coordinate w is zero for this case.


\begin{equation}
  v =
   \begin{bmatrix}
     x  \\ y \\ z \\ w = 0 
   \end{bmatrix}
   =
   \begin{bmatrix}
     x  \\ y \\ z \\ 0 
   \end{bmatrix}
\end{equation}


  *3D Affine Transforms*

3D affine transforms can be represented by matrices with the following
format.

\begin{equation}
  A =
   \begin{bmatrix}
     a_{11}   & a_{12}   &  a_{13} & a_{14}  \\
     a_{21}   & a_{22}   &  a_{23} & a_{24}  \\
     a_{31}   & a_{32}   &  a_{33} & a_{34}  \\
     0        & 0        &  0      & 1       \\
   \end{bmatrix}
\end{equation}

An affine transformation maps the coordinate system (X, Y, Z), which
could be an object local coordinate system, to the coordinate system
(X', Y', Z'), which could be a world coordinate system. In a similar
manner to 2D homogeneous coordinates, an extra pseudo-coordinate w = 1
is added for expressing translations transformations as matrix
multiplications, in the same way as rotations.


\begin{equation}
     \begin{bmatrix}
       x' \\ y' \\ z' \\ w' = 1
      \end{bmatrix}
  =
    \begin{bmatrix}
      a_{11}   & a_{12}   &  a_{13} & a_{14} \\
      a_{21}   & a_{22}   &  a_{23} & a_{24} \\
      a_{31}   & a_{32}   &  a_{33} & a_{34} \\
      0        & 0        &  0      &   1    \\
   \end{bmatrix}

     \cdot

     \begin{bmatrix}
       x \\ y \\ z \\ w = 1
     \end{bmatrix}

\end{equation}


\begin{equation}
     \begin{bmatrix}
       x' \\ y' \\ z' \\ 1
      \end{bmatrix}
    =

    \begin{bmatrix}
        a_{11} \cdot x + a_{12}  \cdot y + a_{13} \cdot z + a_{14}  \\
        a_{21} \cdot x + a_{22}  \cdot y + a_{23} \cdot z + a_{24}  \\
        a_{31} \cdot x + a_{32}  \cdot y + a_{33} \cdot z + a_{34}  \\
        1
    \end{bmatrix}
\end{equation}

  _Combination between affine transforms_ 

Consider two affine transforms $A = a_{ij}$ and $B = b_{ij}$. The
product between those two transform is also an affine transform. 

\begin{equation} 
  C = A \cdot B 
   = 
    \left[\begin{matrix}a_{11} & a_{12} & a_{13} & a_{14}\\a_{21} & a_{22} & a_{23} 
         & a_{24}\\a_{31} & a_{32} & a_{33} & a_{34}\\0 & 0 & 0 & 1\end{matrix}\right]

    \left[\begin{matrix}b_{11} & b_{12} & b_{13} & b_{14}\\b_{21} & b_{22} & b_{23} 
         & b_{24}\\b_{31} & b_{32} & b_{33} & b_{34}\\0 & 0 & 0 & 1\end{matrix}\right]
\end{equation}


\begin{equation} 
   C = 
    \left[\begin{matrix}c_{11} & c_{12} & c_{13} & c_{14}\\c_{21} & c_{22} & c_{24} 
         & c_{23}\\c_{31} & c_{32} & c_{33} & c_{34}\\0 & 0 & 0 & 1\end{matrix}\right]
\end{equation}

  _Rotation affine transforms_ 


Rotation affine transforms represents rotation around some axis or
direction have the following form:

\begin{equation}
  R =
   \begin{bmatrix}
     r_{11}   & r_{12}   &  r_{13} & 0  \\
     r_{21}   & r_{22}   &  r_{23} & 0  \\
     r_{31}   & r_{32}   &  r_{33} & 0  \\
     0        & 0        &  0      & 1  \\
   \end{bmatrix}
\end{equation}


Properties of Rotation Matrix Affine Transforms:

 + Rotation matrices are /orthogonal/ matrices and have the following properties: 
    
\begin{equation}
   R^T \cdot R = R \cdot R^T = I 
\end{equation}

\begin{equation}
   det(R) = 1
\end{equation}

\begin{equation}
   R^T = R^{-1}
\end{equation}

 + Where:
   + R is a rotation matrix
   + $R^T$ is the transpose of the rotation matrix R.
   + $R^{-1}$ is the inverse of the rotation matrix R.


  *3D Canonical Affine Transforms*

 _Translation:_

  + Where: $T^{-1}$ is the inverse transform.

\begin{equation}
  T =
   \begin{bmatrix}
     1   & 0   &  0 &  \Delta_x \\
     0   & 1   &  0 &  \Delta_y \\
     0   & 0   &  1 &  \Delta_z \\
     0   & 0   &  0 &   1      \\
   \end{bmatrix}
\end{equation}


\begin{equation}
  T^{-1} =
   \begin{bmatrix}
     1   & 0   &  0 &  -\Delta_x \\
     0   & 1   &  0 &  -\Delta_y \\
     0   & 0   &  1 &  -\Delta_z \\
     0   & 0   &  0 &   1        \\
   \end{bmatrix}
\end{equation}

Translation transform applied to an homogeneous vector (x, y, z, w = 1):


\begin{equation}
     \begin{bmatrix}
       x' \\ y' \\ z' \\ w' = 1
      \end{bmatrix}
  =

   \begin{bmatrix}
     1   & 0   & 0 &  \Delta_x \\
     0   & 1   & 0 &  \Delta_y \\
     0   & 0   & 1 &  \Delta_z \\
     0   & 0   & 0 &    1      \\
   \end{bmatrix}

     \cdot

   \begin{bmatrix}
       x \\ y \\ z \\ w = 1
   \end{bmatrix}

  =
   \begin{bmatrix}
       x + \Delta_x \\ y + \Delta_y \\ z + \Delta_z \\ 1
   \end{bmatrix}
\end{equation}


 _Scaling:_

  + Where: $s_x$,  $s_y$, $s_z$ are the scale factors for axis x, y, z
    and  $S^{-1}$ is the inverse transform (inverse matrix).

\begin{equation}
  S =
   \begin{bmatrix}
     s_x   & 0     &  0   &  0 \\
     0     & s_y   &  0   &  0 \\
     0     & 0     &  s_z &  0 \\
     0     & 0     &  0   &  1  \\
   \end{bmatrix}
\end{equation}


\begin{equation}
  S^{-1} =
   \begin{bmatrix}
     1 / s_x   & 0     &  0       &  0 \\
     0     & 1 / s_y   &  0       &  0 \\
     0     &    0      &  1 / s_z &  0 \\
     0     &    0      &  0       &  1 \\
   \end{bmatrix}
\end{equation}


Scaling transform applied to a homogeneous coordinate vector:

\begin{equation}
     \begin{bmatrix}
       x' \\ y' \\ z' \\ w' = 1
      \end{bmatrix}
  =

   S \cdot

   \begin{bmatrix}
       x \\ y \\ z \\ w = 1
   \end{bmatrix}

  =
   \begin{bmatrix}
       s_x \cdot x \\ s_y \cdot y \\ s_z \cdot z \\ 1
   \end{bmatrix}
\end{equation}



 _Rotation around x axis_

 + $R_x^{-1} = R_x^T$ => The inverse is equal to the transpose.

\begin{equation}
    R_x(\alpha) =
      \begin{bmatrix}
        1   & 0            &  0              & 0  \\
        0   & \cos{\alpha} & -\sin{\alpha}   & 0  \\
        0   & \sin{\alpha} &  \cos{\alpha}   & 0  \\
        0   & 0            &  0              & 1  \\
      \end{bmatrix}
\end{equation}


Transform $R_x$ applied to a homogeneous vector (X, Y, Z, w = 1).

\begin{equation}
     \begin{bmatrix}
       x' \\ y' \\ z' \\ w' = 1
      \end{bmatrix}
  =

   R_x(\alpha) \cdot

   \begin{bmatrix}
       x \\ y \\ z \\ w = 1
   \end{bmatrix}

  =
   \begin{bmatrix}
       x                                            \\
       y \cdot \cos{\alpha} - z \cdot \sin{\alpha}  \\
       y \cdot \sin{\alpha} + z \cdot \cos{\alpha}  \\
       1                                            \\
   \end{bmatrix}
\end{equation}



 _Rotation around y axis_

  + $R_y^{-1} = R_y^T$

\begin{equation}
 R_y(\beta) =
   \begin{bmatrix}
     \cos{\beta}    & 0   & \sin{\beta} & 0 \\
     0              & 1   & 0           & 0 \\
    -\sin{\beta}    & 0   & \cos{\beta} & 0 \\
    0               & 0   & 0           & 1
   \end{bmatrix}
\end{equation}


Transform $R_y$ applied to a homogeneous vector:

\begin{equation}
     \begin{bmatrix}
       x' \\ y' \\ z' \\ w' = 1
      \end{bmatrix}
  =

   R_y(\beta) \cdot

   \begin{bmatrix}
       x \\ y \\ z \\ w = 1
   \end{bmatrix}

  =
   \begin{bmatrix}
       x \cdot \cos{\beta} + z \cdot \sin{\beta} \\
       y                             \\
     - x \cdot \sin{\beta} + z \cdot \cos{\beta} \\
   \end{bmatrix}
\end{equation}

 _Rotation around z axis_

  + $R_z^{-1} = R_z^T$

\begin{equation}
 R_z(\theta) =
   \begin{bmatrix}
      \cos{\theta}  & -\sin{\theta} &  0 & 0 \\
      \sin{\theta}  &  \cos{\theta} &  0 & 0 \\
     0              & 0             &  1 & 0 \\
     0              & 0             &  0 & 1 \\
   \end{bmatrix}
\end{equation}


Transform $R_z$ applied to a homogeneous vector:

\begin{equation}
     \begin{bmatrix}
       x' \\ y' \\ z' \\ w' = 1
      \end{bmatrix}
  =

   R_z(\theta) \cdot

   \begin{bmatrix}
       x \\ y \\ z \\ w = 1
   \end{bmatrix}

  =
   \begin{bmatrix}
       x \cdot \cos{\theta} - y \cdot \sin{\theta}   \\
       x \cdot \sin{\theta} + y \cdot \cos{\theta}   \\
       z                                             \\
       1
   \end{bmatrix}
\end{equation}



 _Rotation around arbirtary axis_

  + Formula 1 : The axis is defined by unit vector $\hat{n}$
    + References:  [[http://www.chrobotics.com/library/understanding-quaternions][(Chrobotics)]], [[https://www.weizmann.ac.il/sci-tea/benari/sites/sci-tea.benari/files/uploads/softwareAndLearningMaterials/quaternion-tutorial-2-0-1.pdf][(Ben-Ari - Weizmann)]] - derived from
      quaternion equations.

\begin{equation}
   T_n = \begin{bmatrix}

             a^2 + b^2 - c^2 - d^2
          &  2 (b \cdot c -  a \cdot d)
          &  2 (b \cdot d +  a \cdot c)
          &  0                            \\

             2 (b \cdot c +  a \cdot d )
          &  a^2 - b^2 + c^2 -d^2
          &  2 ( c \cdot d -  a \cdot b )
          &  0                            \\

             2 ( b \cdot d -  a \cdot c  )
          &  2 ( c \cdot d +  a \cdot b )
          &  a^2 - b^2 - c^2 + d^2
          &  0                            \\

             0 & 0 & 0 & 1 \\
         \end{bmatrix}
\end{equation}

Where:
  + $\hat{n}$ is a unit vector that designates the direction, thus: $\hat{n} = 1$
  + $\hat{n} = [x_n \quad y_n \quad z_n]^T$
    + $x_n$, $y_n$, $z_n$ are the components of vector $\hat{n}$.
  + $a = \cos( \theta / 2)$
  + $b = x_n \cdot \sin (\theta / 2)$
  + $c = y_n \cdot \sin (\theta / 2)$
  + $d = z_n \cdot \sin (\theta / 2)$


 _Rotation around arbirtary axis_
   + Reference:  [[https://cseweb.ucsd.edu/classes/wi18/cse169-a/slides/CSE169_03.pdf][(Steve Rotenberg - Computer Animation)]]


\begin{equation}
  T_n = \begin{bmatrix}
             x_n^2 + C \cdot (1 - x_n^2)
          &  x_n \cdot y_n \cdot (1 - C) - z_n \cdot S
          &  x_n \cdot z_n \cdot (1 - C)  + y_n \cdot S
          &  0
          \\

             x_n \cdot y_n \cdot (1 - C) + z_n \cdot S
          &  y_n^2 + C \cdot (1 - y_n^2)
          &  y_n \cdot z_n \cdot (1 - C) - x_n \cdot S
          &  0
          \\
               x_n \cdot z_n \cdot (1 - C) - y_n \cdot S
           &   y_n \cdot z_n \cdot (1 - C) +  x_n \cdot S
           &  z_n^2 + C \cdot (1 - z_n^2)
           &  0
           \\

           0 & 0 & 0 & 1

        \end{bmatrix}
\end{equation}

Where:
  + $\theta$ is the rotation angle around the unit vector $\hat{n}$
  + $C = \sin \theta$
  + $S = \cos \theta$
  + $\hat{n}$ is a unit vector that designates the direction, thus: $\hat{n} = 1$
  + $\hat{n} = [x_n \quad y_n \quad z_n]^T$
    + $x_n$, $y_n$, $z_n$ are the components of vector $\hat{n}$.


Testing formula in [[https://www.sympy.org/en/index.html][Sympy]] - Python CAS (Computer Algebra System):

#+BEGIN_SRC python
  import sympy

  # t represents the angle theta
  x, y, z, t = symbols('x y z t')
  C, S = symbols("C S")

  row1 = [ x**2 + C * (1 - x**2), x * y * (1 - C) - z * S, x * z * (1 - C)  + y * S ]
  row2 = [ x * y * (1 - C) + z * S, y**2 + C * (1 - y**2), y * z * (1 - C) - x * S ]
  row3 = [ x * z * (1 - C) - y * S,  y * z * (1 - C) +  x * S, z**2 + C * (1 - z**2) ]

  m = Matrix([row1, row2, row3])

  # C = cos(t)
  # S = sin(t)

  In [7]: m
  Out[7]:
  ⎡   ⎛     2⎞    2                                          ⎤
  ⎢ C⋅⎝1 - x ⎠ + x     -S⋅z + x⋅y⋅(1 - C)  S⋅y + x⋅z⋅(1 - C) ⎥
  ⎢                                                          ⎥
  ⎢                       ⎛     2⎞    2                      ⎥
  ⎢S⋅z + x⋅y⋅(1 - C)    C⋅⎝1 - y ⎠ + y     -S⋅x + y⋅z⋅(1 - C)⎥
  ⎢                                                          ⎥
  ⎢                                           ⎛     2⎞    2  ⎥
  ⎣-S⋅y + x⋅z⋅(1 - C)  S⋅x + y⋅z⋅(1 - C)    C⋅⎝1 - z ⎠ + z   ⎦

  # --- Determine rotation matrix around Z axis (Particular case) ------#
  #

  In [8]: m.subs({x: 0, y: 0, z: 1})
  Out[8]:
  ⎡C  -S  0⎤
  ⎢        ⎥
  ⎢S  C   0⎥
  ⎢        ⎥
  ⎣0  0   1⎦

  In [11]: rotZ = m.subs({x: 0, y: 0, z: 1, C: cos(t), S: sin(t) })

  In [12]: rotZ
  Out[12]:
  ⎡cos(t)  -sin(t)  0⎤
  ⎢                  ⎥
  ⎢sin(t)  cos(t)   0⎥
  ⎢                  ⎥
  ⎣  0        0     1⎦


  #---- Determine rotation matrix around X axis (Particular case) ------#
  #
  In [14]: rotX = m.subs({x: 1, y: 0, z: 0, C: cos(t), S: sin(t) })

  In [15]: rotX
  Out[15]:
  ⎡1    0        0   ⎤
  ⎢                  ⎥
  ⎢0  cos(t)  -sin(t)⎥
  ⎢                  ⎥
  ⎣0  sin(t)  cos(t) ⎦

  #---- Determine rotation matrix around Y axis (Particular case) ------#
  #
  In [16]: rotY = m.subs({x: 0, y: 1, z: 0, C: cos(t), S: sin(t) })

  In [17]: rotY
  Out[17]:
  ⎡cos(t)   0  sin(t)⎤
  ⎢                  ⎥
  ⎢   0     1    0   ⎥
  ⎢                  ⎥
  ⎣-sin(t)  0  cos(t)⎦

#+END_SRC

*** Rotation Matrix and Rodrigues' Rotation Formula

The Rodrigues' Rotation formula, named after the mathematician [[https://en.wikipedia.org/wiki/Olinde_Rodrigues][Olinde]] 
Rodrigues, allows rotating a vector in a 3D space, given an axis unit
vector and a rotation angle around this axis. Variants of this formula
can be used for determining the rotation matrix around any axis and
for computing the axis-angle equivalent of a rotation matrix. This
formula has wide variety of applications, including computer graphics,
games, robotics, mechanical engineering and aerospace design.
 
Consider the following picture that contains a vector
$'\boldsymbol{v}$ which is the position vector $\boldsymbol{v}$
rotated by an angle $\theta$ around an axis designated by the unite
vector $\boldsymbol{n}$.  The vector $\boldsymbol{v}_p$ is the
projection of vector $\boldsymbol{v}$ onto the vector $\boldsymbol{n}$
and the vector $\boldsymbol{v}_r$ is the rejection of vector
$\boldsymbol{v}$.

#+CAPTION: Rotation of position vector around an axis
[[file:images/rodriguez-formula-rotation1.png][file:images/rodriguez-formula-rotation1.png]]


According to the previous picture it is possible to find the following
expressions. Where r is the radius of the circle, which is the same as
the lenght of CA.

$\boldsymbol{v}_p$ and $\boldsymbol{v}_r$ that

\begin{equation}
  \| \boldsymbol{v}_p \| =  \| v \| \cos \alpha 
\end{equation}

\begin{equation}
  r = \| \boldsymbol{v}_r \| =  \| v \| \sin \alpha
\end{equation}

The projection vector $\boldsymbol{v_p}$ (OC) can be determined using
the following identity:

\begin{equation}
  v_p = (\boldsymbol{n} \cdot \boldsymbol{v}) \boldsymbol{n}
\end{equation}


The cross product between vectors $\boldsymbol{n}$ and vector
$\boldsymbol{v}$ is computed as:

\begin{equation}
   \| \boldsymbol{n} \times \boldsymbol{v} \| 
          = \| \boldsymbol{n} \| \| \boldsymbol{v} \| \sin \alpha 
          = \| \boldsymbol{v} \| \sin \alpha 
\end{equation}

Knowing that $\| \boldsymbol{v}_r = \| \boldsymbol{v} \| \sin \alpha$, 
the following can be determined:

\begin{equation}
      r = \| \boldsymbol{v}_r \| =  \| \boldsymbol{n} \times \boldsymbol{v} \| 
             = \| \boldsymbol{v} \| \sin \alpha
\end{equation}

The vector $\boldsymbol{n} \times (\boldsymbol{n} \times \boldsymbol{v})$ 
is orthogonal to both $\boldsymbol{n}$ and $\boldsymbol{n} \times \boldsymbol{v}$ 
and has opposite direction to the vector $\boldsymbol{v_r}$. The norm/magnitude of 
this cross product vector is determined by using the cross product
identity.

\begin{equation}
   \| \boldsymbol{n} \times (\boldsymbol{n} \times \boldsymbol{v} ) \|  
     =  \| \boldsymbol{n} \|  \| \boldsymbol{n} \times \boldsymbol{v}   \|
     =  \| \boldsymbol{n} \times \boldsymbol{v} \| 
     = \| \boldsymbol{v}_r \|
\end{equation}

Since the vector $\boldsymbol{n} \times (\boldsymbol{n} \times \boldsymbol{v})$ 
has the same magnitude as the vector $\boldsymbol{v}_r$ and opposite
direction, it is possible to find that.

\begin{equation}
  \boldsymbol{v}_r = - \boldsymbol{n} \times (\boldsymbol{n} \times \boldsymbol{v}) 
\end{equation}

The projection vector $\boldsymbol{v}_p$ can also be computed in terms of rejection
vector $\boldsymbol{v}_r$ by using the expression $\boldsymbol{v} = \boldsymbol{v}_p + \boldsymbol{v}_r$.


\begin{equation}
  \boldsymbol{v}_p = \boldsymbol{v} - \boldsymbol{v}_r
                   = \boldsymbol{v} - (- \boldsymbol{n} \times (\boldsymbol{n} \times \boldsymbol{v})  )
\end{equation}


\begin{equation}
  \boldsymbol{v}_p = \boldsymbol{v}  + \boldsymbol{n} \times (\boldsymbol{n} \times \boldsymbol{v}) 
\end{equation}

The unit vector $\boldsymbol{e}_a$ that has the same direction as the
vector $\boldsymbol{v}_r$ and it can computed  as:

\begin{equation}
  \boldsymbol{e}_a = 
          -  \frac{1}{ \| \boldsymbol{n} \times (\boldsymbol{n} \times \boldsymbol{v}) \| }   
            \boldsymbol{n} \times (\boldsymbol{n} \times \boldsymbol{v}) 

         = -  \frac{1}{ \| \boldsymbol{n} \times \boldsymbol{v} \| }   
            \boldsymbol{n} \times (\boldsymbol{n} \times \boldsymbol{v}) 
\end{equation}

The unit vector $\boldsymbol{e}_b$ (direction CD), the same direction
of vector $\boldsymbol{n} \times \boldsymbol{v}$ is orthogonal to 
$\boldsymbol{n}$, $\boldsymbol{v}$ and $\boldsymbol{v}_r$ is
determined by normalizing the vector $\boldsymbol{n} \times \boldsymbol{v}$.

\begin{equation}
  \boldsymbol{e}_b = \frac{ \boldsymbol{n} \times \boldsymbol{v}  }
                          {\| \boldsymbol{n} \times \boldsymbol{v}   \|}
\end{equation}

By projecting the rotated rejection vector $'\boldsymbol{v}_r$ on the
vectors $\boldsymbol{e}_a$ and $\boldsymbol{e}_b$, the next expression
is found as follow:

\begin{equation}
   \boldsymbol{v}_r'  = r \cos(\theta) \boldsymbol{e}_a + r \sin(\theta) \boldsymbol{e}_b
\end{equation}

\begin{equation}
   \boldsymbol{v}_r'  = \| \boldsymbol{n} \times \boldsymbol{v} \| \cos(\theta) 
                           
                       ( -  \frac{1}{ \| \boldsymbol{n} \times \boldsymbol{v} \| }   
                              \boldsymbol{n} \times (\boldsymbol{n} \times \boldsymbol{v}) )

                         + \| \boldsymbol{n} \times \boldsymbol{v} \|  
                          \sin(\theta) 
                           \frac{ \boldsymbol{n} \times \boldsymbol{v}  }
                               {\| \boldsymbol{n} \times \boldsymbol{v}   \|}
\end{equation}

\begin{equation}
   \boldsymbol{v}_r'  =
           -  \cos(\theta) \boldsymbol{n} \times (\boldsymbol{n} \times \boldsymbol{v}) 
           +  \sin(\theta)  \boldsymbol{n} \times \boldsymbol{v} 
\end{equation}



The rotated vector $\boldsymbol{v}'$ can be found by adding the
rotated rejection vector $\boldsymbol{v}_r'$ and the projection vector
$\boldsymbol{v}_p$, replacing the vector $\boldsymbol{v}_p$ with  
$\boldsymbol{v}  + \boldsymbol{n} \times (\boldsymbol{n} \times \boldsymbol{v})$ and
also replacing the $\boldsymbol{e}_a$ and $\boldsymbol{e}_b$ with its
previous values.

\begin{equation}
  \boldsymbol{v}' = \boldsymbol{v}_p + \boldsymbol{v}_r' 
                  = (\boldsymbol{v}  + \boldsymbol{n} \times (\boldsymbol{n} \times \boldsymbol{v}) )
                 + (
                     -  \cos(\theta) \boldsymbol{n} \times (\boldsymbol{n} \times \boldsymbol{v}) 
                     +  \sin(\theta)  \boldsymbol{n} \times \boldsymbol{v} 
                    )
 
\end{equation}

\begin{equation}
  \boldsymbol{v}' =
                  \boldsymbol{v}  + \boldsymbol{n} \times (\boldsymbol{n} \times \boldsymbol{v}) 
                -  \cos(\theta) \boldsymbol{n} \times (\boldsymbol{n} \times \boldsymbol{v}) 
                +  \sin(\theta)  \boldsymbol{n} \times \boldsymbol{v} 
\end{equation}

Finally, by further simplyfing the previous expression, it possible to
find the _Rodriguez's formula for rotation_:

\begin{equation}
  \boxed{
    \boldsymbol{v}' =
                  \boldsymbol{v}  
                + (1 - \cos \theta )\boldsymbol{n} \times (\boldsymbol{n} \times \boldsymbol{v}) 
                +  ( \sin \theta )  \boldsymbol{n} \times \boldsymbol{v} 
   }
\end{equation}

The cross product can be written as a matrix multiplication by using
the cross product matrix $[\boldsymbol{u}]_{\times}$ operator defined as: 

\begin{equation}
   [\boldsymbol{u}]_{\times} = \begin{pmatrix}
                               0   & -u_z &  u_y \\ 
                               u_z &  0   & -u_x \\
                              -u_y & u_x  &    0 \\
                           \end{pmatrix}
\end{equation}

Cross-product represented as matrix multiplication.

\begin{equation}
    \boldsymbol{n} \times \boldsymbol{v} = [ \boldsymbol{n} ]_{\times} \boldsymbol{v}
\end{equation}

The Rodriguez's formula for rotation can be formulated as a matrix
multiplication by replacing cross-product operations with matrix
multiplication where $\boldsymbol{I}$ is a 3 x 3 identity matrix.

\begin{equation}
    \boldsymbol{v}' =
                  \boldsymbol{v}  
                + (1 - \cos \theta ) [ \boldsymbol{n}]_{\times} ( [\boldsymbol{n}]_{\times} \boldsymbol{v}) 
                +  ( \sin \theta )   [ \boldsymbol{n}]_{\times} \boldsymbol{v} 
\end{equation}


\begin{equation}
  \boldsymbol{v}' =
                  \boldsymbol{I} \boldsymbol{v}  
                + (1 - \cos \theta ) [\boldsymbol{n}]_{\times}^2  \boldsymbol{v}
                +  ( \sin \theta )   [\boldsymbol{n}]_{\times} \boldsymbol{v} 
\end{equation}

\begin{equation}
  \boldsymbol{v}' =
                  \left( 
                    \boldsymbol{I} 
                   + (1 - \cos \theta ) [\boldsymbol{n}]_{\times}^2  
                   +  ( \sin \theta )   [\boldsymbol{n}]_{\times} 
                  \right)
                     \boldsymbol{v}  
\end{equation}

So, the rotation matrix R such that $\boldsymbol{v}' = R
\boldsymbol{v}$ can be found as the following expression, 
where $N = [\boldsymbol{n}]_{\times}$  is the cross product matrix 
of the rotation axis vector and $\boldsymbol{I}$  is a 3 x 3 identity
matrix.  In computer graphics, it is more efficient to compute 
the rotation matrix only once and use to rotate all vertices of an 
object on the GPU side instead of applying the Rodriguez's formula to 
every object vertex.


\begin{equation}
    R =  \boldsymbol{I} 
       + (\sin \theta)      [\boldsymbol{n}]_{\times} 
       + (1 - \cos \theta ) [\boldsymbol{n}]_{\times}^2  
\end{equation}

\begin{equation}
  \boxed{
     R =  \boldsymbol{I} + (\sin \theta) N + (1 - \cos \theta ) N^2  
   }
\end{equation}

  *Deriving the Rotation Matrix in Wolfram Mathemtica*

The general rotation matrix R can be easily found by using a _CAS -_
_Computer Algebra System_ such as [[https://en.wikipedia.org/wiki/Wolfram_Mathematica][Wolfram Mathematica]], [[https://www.sympy.org/][SymPy]] (Python)
library or [[https://maxima.sourceforge.io][GNU Maxima]]. In this case, it is used Wolfram Mathematica
for determining this matrix due to its ease of use and its rule-rewriting
systems that makes it easier to manipulate math formulas and generate
LaTex, Mathml, ascii representation, C, C++ or Fortran code. An
alternative CAS is [[https://mathics.org/][mathics]], a free and open source alternative
implementation of Wolfram programming language, built on top of
Python's sympy library for symbolic math computation.


Run Mathics => Mathematica Computer Algebra System clone for Python:

  + https://mathics.org/

Run interactive shell on command line:

#+BEGIN_SRC sh 
  docker run --rm -it --security-opt=seccomp=unconfined \
          --name=mathics-cli --volume=/tmp:/usr/src/app/data \
          mathicsorg/mathics --mode cli
  
  Mathicscript: 6.0.0, Mathics 6.0.0
  on CPython 3.10.6 (main, Nov 14 2022, 16:10:14) [GCC 11.3.0]
  
  Using:
  SymPy 1.9, mpmath 1.2.1, numpy 1.21.5
  cython Not installed, matplotlib 3.5.1,
  Asymptote version 2.83
  
  Copyright (C) 2011-2023 The Mathics Team.
  This program comes with ABSOLUTELY NO WARRANTY.
  This is free software, and you are welcome to redistribute it
  under certain conditions.
  See the documentation for the full license.
  
  Quit by evaluating Quit[] or by pressing CONTROL-D.
  
  In[1]:=
#+END_SRC

Run graphics notebook - web interface on port 9080 using docker. After
the container initialization, open the URL http://localhost:9080 on
the Web browser.

#+BEGIN_SRC sh 
  $  docker run --rm -it --security-opt=seccomp=unconfined --name=mathics-cli -p=9080:8000 \
       --volume=/tmp:/usr/src/app/data mathicsorg/mathics  --mode ui
#+END_SRC


Wolfram Mathematica or Mathics session:

#+BEGIN_SRC ocaml
 (* Define the rotation axis *)
 n = {nx, ny, nz}
 
 (* Turns a vector in to its cross product matrix *)
 cross[u_] := {{0, -u[[3]], u[[2]] }, { u[[3]], 0, -u[[1]]}, {-u[[2]], u[[1]], 0} }


 In[6]:= I3 = IdentityMatrix[3]
 
 Out[6]= {{1, 0, 0}, {0, 1, 0}, {0, 0, 1}}
 
 In[7]:= I3 // MatrixForm
 
 Out[7]//MatrixForm= 1   0   0
 
                     0   1   0
 
                     0   0   1

  In[8]:= xN = cross[n] (* Cross product matrix of rotation axis n*)
  
  Out[8]= {{0, -nz, ny}, {nz, 0, -nx}, {-ny, nx, 0}}
  
  In[9]:= xN // MatrixForm
  
  Out[9]//MatrixForm= 0     -nz   ny
  
                      nz    0     -nx
  
                      -ny   nx    0


  In[10]:= R = I3 + S * xN + (1 - C) * (xN . xN)
  
                             2     2
  Out[10]= {{1 + (1 - C) (-ny  - nz ), (1 - C) nx ny - nz S, 
   
                                                                      2     2
  >     (1 - C) nx nz + ny S}, {(1 - C) nx ny + nz S, 1 + (1 - C) (-nx  - nz ), 
   
  >     (1 - C) ny nz - nx S}, {(1 - C) nx nz - ny S, (1 - C) ny nz + nx S, 
   
                        2     2
  >     1 + (1 - C) (-nx  - ny )}}
  
  In[11]:= 
  In[11]:= R = I3 + S * xN + (1 - C) * MatrixPower[xN, 2] (* Note: a^2 - does not work with matrices *)
  
                             2     2
  Out[11]= {{1 + (1 - C) (-ny  - nz ), (1 - C) nx ny - nz S, 
   
                                                                      2     2
  >     (1 - C) nx nz + ny S}, {(1 - C) nx ny + nz S, 1 + (1 - C) (-nx  - nz ), 
   
  >     (1 - C) ny nz - nx S}, {(1 - C) nx nz - ny S, (1 - C) ny nz + nx S, 
   
                        2     2
  >     1 + (1 - C) (-nx  - ny )}}



  rotX = { nx -> 1, ny -> 0, nz -> 0,  C -> Cos[ \[Theta] ], S -> Sin[ \[Theta] ] };
  rotY = { nx -> 0, ny -> 1, nz -> 0,  C -> Cos[ \[Theta] ], S -> Sin[ \[Theta] ] };
  rotZ = { nx -> 0, ny -> 0, nz -> 1,  C -> Cos[ \[Theta] ], S -> Sin[ \[Theta] ] };


  (* --------------- Rotation Matrix around X axis  -------------------------------*)

  In[26]:= Rx = R /. rotX
  
  Out[26]= {{1, 0, 0}, {0, Cos[θ], -Sin[θ]}, {0, Sin[θ], Cos[θ]}}
  
  In[27]:= Rx // MatrixForm
  
  Out[27]//MatrixForm= 1          0          0
  
                       0          Cos[θ]    -Sin[θ]
  
                       0          Sin[θ]    Cos[θ]
  
  In[28]:= Rx // MatrixForm // TeXForm
  
  Out[28]//TeXForm= \left(
                    \begin{array}{ccc}
                     1 & 0 & 0 \\
                     0 & \cos (\theta ) & -\sin (\theta ) \\
                     0 & \sin (\theta ) & \cos (\theta ) \\
                    \end{array}
                    \right)

  (* --------------- Rotation Matrix around Y axis  -------------------------------*)

  In[31]:= Ry = R /. rotY
  
  Out[31]= {{Cos[θ], 0, Sin[θ]}, {0, 1, 0}, {-Sin[θ], 0, Cos[θ]}}
  
  In[32]:= Ry // MatrixForm
  
  Out[32]//MatrixForm= Cos[θ]    0          Sin[θ]
  
                       0          1          0
  
                       -Sin[θ]   0          Cos[θ]
  
  In[33]:= Ry // MatrixForm // TeXForm
  
  Out[33]//TeXForm= \left(
                    \begin{array}{ccc}
                     \cos (\theta ) & 0 & \sin (\theta ) \\
                     0 & 1 & 0 \\
                     -\sin (\theta ) & 0 & \cos (\theta ) \\
                    \end{array}
                    \right)
  
  (* --------------- Rotation Matrix around Z axis  -------------------------------*)

  In[34]:= Rz = R /. rotZ
  
  Out[34]= {{Cos[θ], -Sin[θ], 0}, {Sin[θ], Cos[θ], 0}, {0, 0, 1}}
  
  In[35]:= Rz // MatrixForm 
  
  Out[35]//MatrixForm= Cos[θ]    -Sin[θ]   0
  
                       Sin[θ]    Cos[θ]    0
  
                       0          0          1
  
  In[36]:= Rz // MatrixForm // TeXForm
  
  Out[36]//TeXForm= \left(
                    \begin{array}{ccc}
                     \cos (\theta ) & -\sin (\theta ) & 0 \\
                     \sin (\theta ) & \cos (\theta ) & 0 \\
                     0 & 0 & 1 \\
                    \end{array}
                    \right)
                    
  (* -------------- General Rotation Matrix --------------------------------------- *)

 In[41]:= Clear[n]
 In[42]:= Rp = R /. { nx -> Subscript[n, x], ny -> Subscript[n, y], nz -> Subscript[n, z] };

 In[44]:= Rp // MatrixForm // TeXForm

 Out[44]//TeXForm= 
   \left(
   \begin{array}{ccc}
    (1-C) \left(-n_y^2-n_z^2\right)+1 & (1-C) n_x n_y-S n_z & (1-C) n_x n_z+S
      n_y \\
    (1-C) n_x n_y+S n_z & (1-C) \left(-n_x^2-n_z^2\right)+1 & (1-C) n_y n_z-S
      n_x \\
    (1-C) n_x n_z-S n_y & (1-C) n_y n_z+S n_x & (1-C)
      \left(-n_x^2-n_y^2\right)+1 \\
   \end{array}
   \right)
   

  In[52]:= R2 = R /. {-ny^2 - nz^2 -> nx^2 - 1, -nx^2 - nz^2 -> ny^2 - 1, -nx^2 - ny^2 -> nz^2 - 1 }
  
                                 2
  Out[52]= {{1 + (1 - C) (-1 + nx ), (1 - C) nx ny - nz S, 
   
                                                                          2
  >     (1 - C) nx nz + ny S}, {(1 - C) nx ny + nz S, 1 + (1 - C) (-1 + ny ), 
   
  >     (1 - C) ny nz - nx S}, {(1 - C) nx nz - ny S, (1 - C) ny nz + nx S, 
   
                            2
  >     1 + (1 - C) (-1 + nz )}}
  
  
  In[53]:= Rp2 = R2 /. { nx -> Subscript[n, x], ny -> Subscript[n, y], nz -> Subscript[n, z] };
  
  In[54]:= Rp2 
  
                                 2
  Out[54]= {{1 + (1 - C) (-1 + n  ), (1 - C) n  n  - S n , 
                                x             x  y      z
   
                                                                          2
  >     S n  + (1 - C) n  n }, {(1 - C) n  n  + S n , 1 + (1 - C) (-1 + n  ), 
           y            x  z             x  y      z                     y
   
  >     -(S n ) + (1 - C) n  n }, 
             x             y  z
   
                                                                           2
  >    {-(S n ) + (1 - C) n  n , S n  + (1 - C) n  n , 1 + (1 - C) (-1 + n  )}}
             y             x  z     x            y  z                     z
  
  
  In[55]:= Rp2 // MatrixForm // TeXForm
  
  Out[55]//TeXForm= 
     \left(
     \begin{array}{ccc}
      (1-C) \left(n_x^2-1\right)+1 & (1-C) n_x n_y-S n_z & (1-C) n_x n_z+S n_y
        \\
      (1-C) n_x n_y+S n_z & (1-C) \left(n_y^2-1\right)+1 & (1-C) n_y n_z-S n_x
        \\
      (1-C) n_x n_z-S n_y & (1-C) n_y n_z+S n_x & (1-C) \left(n_z^2-1\right)+1
        \\
     \end{array}
     \right)


#+END_SRC


The overall rotation matrix around an arbitrary axis is given by the
following expression where $n_x$, $n_y$ and $n_z$ are components of
the rotation axis vector $\boldsymbol{n}$ (unitary vector); $\theta$
is the rotation angle; $C = \sin \theta$; and $S = \sin \theta$.

\begin{equation}
 R = 
   \begin{bmatrix}
    (1-C) \left(-n_y^2-n_z^2\right)+1 & (1-C) n_x n_y-S n_z & (1-C) n_x n_z+S
      n_y \\
    (1-C) n_x n_y+S n_z & (1-C) \left(-n_x^2-n_z^2\right)+1 & (1-C) n_y n_z-S
      n_x \\
    (1-C) n_x n_z-S n_y & (1-C) n_y n_z+S n_x & (1-C)
      \left(-n_x^2-n_y^2\right)+1 \\
  \end{bmatrix}

\end{equation}

The general rotation matrix can also be expressed in this form that is
obtained by replacing $n_x^2 - 1 = - (n_y^2 + nz^2)$, $n_y^2 - 1 = - (n_x^2 + nz^2)$ 
and $n_z^2 - 1 = - (n_x^2 + ny^2)$.

\begin{equation}
 \boxed{ 
     R = 

     \begin{bmatrix}
       (1-C) \left(n_x^2-1\right)+1 & (1-C) n_x n_y-S n_z & (1-C) n_x n_z+S n_y
         \\
       (1-C) n_x n_y+S n_z & (1-C) \left(n_y^2-1\right)+1 & (1-C) n_y n_z-S n_x
         \\
       (1-C) n_x n_z-S n_y & (1-C) n_y n_z+S n_x & (1-C) \left(n_z^2-1\right)+1
         \\
      \end{bmatrix}
  }
\end{equation}


Rotation matrix around X axis. 

\begin{equation}
  \boxed{
    R_x =  
      \left(
          \begin{array}{ccc}
           1 & 0 & 0 \\
           0 & \cos (\theta ) & -\sin (\theta ) \\
           0 & \sin (\theta ) & \cos (\theta ) \\
          \end{array}
        \right)
   }
\end{equation}

Rotation matrix around Y axis.

\begin{equation}
  \boxed{
     R_y = 
       \left(
         \begin{array}{ccc}
          \cos (\theta ) & 0 & \sin (\theta ) \\
          0 & 1 & 0 \\
          -\sin (\theta ) & 0 & \cos (\theta ) \\
         \end{array}
         \right)
   } 
\end{equation}

Rotation matrix around Z axis.

\begin{equation}
  \boxed{
      R_z = 
           \left(
            \begin{array}{ccc}
             \cos (\theta ) & -\sin (\theta ) & 0 \\
             \sin (\theta ) & \cos (\theta ) & 0 \\
             0 & 0 & 1 \\
            \end{array}
            \right)
   }
\end{equation}

 
  *Rodriguez Formula in Alternative Format*

From previous equation, the unit vector $\boldsymbol{e}_a$ can be
found as:

\begin{equation}
   \boldsymbol{e}_a = \frac{ \boldsymbol{v}_r }{ \| \boldsymbol{v}_r \| } 
                    = \frac{\boldsymbol{v} - (\boldsymbol{v} \cdot \boldsymbol{n}) \boldsymbol{n}}
                           { \| \boldsymbol{n} \times \boldsymbol{v} \| }
\end{equation}

\begin{equation}
   \boldsymbol{v}_r'  = r \cos(\theta) \boldsymbol{e}_a + r \sin(\theta) \boldsymbol{e}_b
\end{equation}

\begin{equation}
   \boldsymbol{v}_r'  = \| \boldsymbol{n} \times \boldsymbol{v} \| \cos(\theta) 
                           
                        ( \frac{\boldsymbol{v} - (\boldsymbol{v} \cdot \boldsymbol{n}) \boldsymbol{n}}
                           { \| \boldsymbol{n} \times \boldsymbol{v} \| } ) 

                         + \| \boldsymbol{n} \times \boldsymbol{v} \|  
                          \sin(\theta) 
                           \frac{ \boldsymbol{n} \times \boldsymbol{v}  }
                               {\| \boldsymbol{n} \times \boldsymbol{v}   \|}
\end{equation}

\begin{equation}
   \boldsymbol{v}_r'  =
              \cos(\theta)  ( \boldsymbol{v} - (\boldsymbol{v} \cdot \boldsymbol{n}) \boldsymbol{n} )   
           +  \sin(\theta)  \boldsymbol{n} \times \boldsymbol{v} 
\end{equation}


Then, rotated vector $\boldsymbol{v}'$ can be found by replacing the
values of previous equations in the next equation.

\begin{equation}
  \boldsymbol{v}' = \boldsymbol{v}_p + \boldsymbol{v}_r' 
\end{equation}

\begin{equation}
   \boldsymbol{v}' 
         =  (\boldsymbol{v} \cdot \boldsymbol{n}) \boldsymbol{n}
           + (
              \cos(\theta)  ( \boldsymbol{v} - (\boldsymbol{v} \cdot \boldsymbol{n}) \boldsymbol{n} )   
           +  \sin(\theta)  \boldsymbol{n} \times \boldsymbol{v} 
            )

\end{equation}

_Alternative Format of Roriguez Formula_: Finally, the alternative
format of Rodriguez Formula for rotation can be determined as follow.

\begin{equation}
   \boldsymbol{v}' 
         = \boldsymbol{v} \cos \theta
           + (1 - \cos \theta) (\boldsymbol{v} \cdot \boldsymbol{n}) \boldsymbol{n} 
           + (\boldsymbol{n} \times \boldsymbol{v}) \sin \theta 
\end{equation}

It can be found that the previous expression can be turned into a
matrix multiplication as in shown in the next equation where $a^T$ is
the tranpose of matrix a; I 3x3 identiy matrix; $N = [\boldsymbol{b}]_{\times}$ 
is the cross product matrix of rotation axis vector $\boldsymbol{n}$. 
In this case, the vectors $\boldsymbol{v}$, $\boldsymbol{v}'$ and $\boldsymbol{n}$ are
treated as 3 x 1 (3 by 1) column matrices.

\begin{equation}
   \boldsymbol{v}' 
         =   I \boldsymbol{v} \cos \theta
           + (1 - \cos \theta)(\boldsymbol{n} \boldsymbol{n}^T) \boldsymbol{v}
           + (N  \sin \theta) \boldsymbol{v}
\end{equation}

\begin{equation}
   \boldsymbol{v}' 
            = I \boldsymbol{v} \cos \theta
           + (1 - \cos \theta)(\boldsymbol{n} \boldsymbol{n}^T) \boldsymbol{v}
           + (N  \sin \theta) \boldsymbol{v}
\end{equation}

\begin{equation}
   \boldsymbol{v}' 
            = \left( 
             I  \cos \theta
           + (1 - \cos \theta)\boldsymbol{n} \boldsymbol{n}^T 
           + N \sin(\theta) 
              \right) \boldsymbol{v} 
\end{equation}

\begin{equation}
   \boldsymbol{v}' 
            = \left(             
             \boldsymbol{n} \boldsymbol{n}^T  
           + (I  - \boldsymbol{n} \boldsymbol{n}^T )  \cos \theta
           + N \sin(\theta) 
              \right) \boldsymbol{v} 
\end{equation}

So, the general rotation matrix R that represents a rotation around an
arbitrary axis such that $\boldsymbol{v}' = R \boldsymbol{v}$ can be
found as:

\begin{equation}
          R = \boldsymbol{n} \boldsymbol{n}^T 
               + (I  - \boldsymbol{n} \boldsymbol{n}^T )  \cos \theta + N \sin \theta
\end{equation}


The rotation matrix can be determined from this expression by using
Wolfram Mathematica computer algebra system.

#+BEGIN_SRC ocaml
  In[1]:= n = { {nx}, {ny}, {nz} }
  
  Out[1]= {{nx}, {ny}, {nz}}
  
  In[2]:= Dimensions[n]
  
  Out[2]= {3, 1}
  
  In[3]:= n // MatrixForm
  
  Out[3]//MatrixForm= nx
  
                      ny
  
                      nz
  
  In[4]:= P = n . Transpose[n]
  
              2                           2                           2
  Out[4]= {{nx , nx ny, nx nz}, {nx ny, ny , ny nz}, {nx nz, ny nz, nz }}
  
  In[5]:= P // MatrixForm
  
  Out[5]//MatrixForm=   2
                      nx      nx ny   nx nz
  
                                2
                      nx ny   ny      ny nz
  
                                        2
                      nx nz   ny nz   nz
  
  
  In[6]:= I3 = IdentityMatrix[3]
  
  Out[6]= {{1, 0, 0}, {0, 1, 0}, {0, 0, 1}}

  In[7]:= SN = {{0, -nz, ny}, {nz, 0, -nx}, {-ny, nx, 0}};
  
  (* C - represents cos(t), S - represents sin(t) *)
  In[8]:= R = P + (I3 - P) * C + SN * S 
  
  
  In[9]:= R // MatrixForm
  
  Out[9]//MatrixForm= 
   
  >     2            2
      nx  + C (1 - nx )        nx ny - C nx ny - nz S   nx nz - C nx nz + ny S
  
                                 2            2
      nx ny - C nx ny + nz S   ny  + C (1 - ny )        ny nz - C ny nz - nx S
  
                                                          2            2
      nx nz - C nx nz - ny S   ny nz - C ny nz + nx S   nz  + C (1 - nz )
  
  
  rotX = { nx -> 1, ny -> 0, nz -> 0,  C -> Cos[ \[Theta] ], S -> Sin[ \[Theta] ] };
  rotY = { nx -> 0, ny -> 1, nz -> 0,  C -> Cos[ \[Theta] ], S -> Sin[ \[Theta] ] };
  rotZ = { nx -> 0, ny -> 0, nz -> 1,  C -> Cos[ \[Theta] ], S -> Sin[ \[Theta] ] };
 

  In[14]:= Rx = R /. rotX
  
  Out[14]= {{1, 0, 0}, {0, Cos[θ], -Sin[θ]}, {0, Sin[θ], Cos[θ]}}
 
 
  In[15]:= Rx // MatrixForm
  
  Out[15]//MatrixForm= 1          0          0
  
                       0          Cos[θ]    -Sin[θ]
  
                       0          Sin[θ]    Cos[θ]
  
  
  In[16]:= Ry = R /. rotY
  
  Out[16]= {{Cos[θ], 0, Sin[θ]}, {0, 1, 0}, {-Sin[θ], 0, Cos[θ]}}
  
  In[17]:= MatrixForm[Ry]
  
  Out[17]//MatrixForm= Cos[θ]    0          Sin[θ]
  
                       0          1          0
  
                       -Sin[θ]   0          Cos[θ]
  

  In[18]:= Rz = R /. rotZ
  
  Out[18]= {{Cos[θ], -Sin[θ], 0}, {Sin[θ], Cos[θ], 0}, {0, 0, 1}}
  
  In[19]:= Rz // MatrixForm
  
  Out[19]//MatrixForm= Cos[θ]    -Sin[θ]   0
  
                       Sin[θ]    Cos[θ]    0
  
                       0          0          1
  
  
  In[27]:= ClearAll[n]
  
  In[28]:= toSubscr =  { nx -> Subscript[n, x], ny -> Subscript[n, y], nz -> Subscript[n, z]};
  
  In[29]:= Rp = R /. toSubscr;


  In[31]:= Rp
  
               2            2
  Out[31]= {{n   + C (1 - n  ), n  n  - C n  n  - S n , 
              x            x     x  y      x  y      z
   
                                                            2            2
  >     S n  + n  n  - C n  n }, {n  n  - C n  n  + S n , n   + C (1 - n  ), 
           y    x  z      x  z     x  y      x  y      z   y            y
   
  >     -(S n ) + n  n  - C n  n }, 
             x     y  z      y  z
   
                                                             2            2
  >    {-(S n ) + n  n  - C n  n , S n  + n  n  - C n  n , n   + C (1 - n  )}}
             y     x  z      x  z     x    y  z      y  z   z            z
  
  
  In[33]:= Rp // MatrixForm // TeXForm
  
  Out[33]//TeXForm= 
     \left(
     \begin{array}{ccc}
      C \left(1-n_x^2\right)+n_x^2 & -C n_x n_y-S n_z+n_x n_y & -C n_x n_z+S
        n_y+n_x n_z \\
      -C n_x n_y+S n_z+n_x n_y & C \left(1-n_y^2\right)+n_y^2 & -C n_y n_z-S
        n_x+n_y n_z \\
      -C n_x n_z-S n_y+n_x n_z & -C n_y n_z+S n_x+n_y n_z & C
        \left(1-n_z^2\right)+n_z^2 \\
     \end{array}
     \right)
#+END_SRC


Finally, from Wolfram mathematica session, the rotation matrix  can be determined as: 

\begin{equation}
 \boxed{
    R = 
    \begin{bmatrix}
    C \left(1-n_x^2\right)+n_x^2 & -C n_x n_y-S n_z+n_x n_y & -C n_x n_z+S
      n_y+n_x n_z \\
    -C n_x n_y+S n_z+n_x n_y & C \left(1-n_y^2\right)+n_y^2 & -C n_y n_z-S
      n_x+n_y n_z \\
    -C n_x n_z-S n_y+n_x n_z & -C n_y n_z+S n_x+n_y n_z & C
      \left(1-n_z^2\right)+n_z^2 \\

   \end{bmatrix}
  }
\end{equation}

 *Computing the General Rotation Matrix using Sympy* 

Install Python  [[https://docs.sympy.org/latest/index.html][Sympy]] library:

#+BEGIN_SRC sh 
  $ pip3 install --user sympy
#+END_SRC

Sympy import: 

#+BEGIN_SRC python
  from sympy import * 
  init_printing('utf-8')
#+END_SRC

Define symbolic variables:

#+BEGIN_SRC python
  nx, ny, nz, q, C, S = symbols("nx ny nz theta C S")
#+END_SRC

Define function that turns vector into cross-product matrix.

#+BEGIN_SRC python
  def crossm(v):
      "Generate cross-product matrix"
      x = v[0]
      y = v[1]
      z = v[2]
      m = Matrix([[0, -z, y], [z, 0, -x], [-y, x, 0]])
      return m
#+END_SRC

Identity matrix.

#+BEGIN_SRC python
  >>> I3 = Matrix([[1, 0, 0], [0, 1, 0], [0, 0, 1]])

  >>> I3
  ⎡1  0  0⎤
  ⎢       ⎥
  ⎢0  1  0⎥
  ⎢       ⎥
  ⎣0  0  1⎦
#+END_SRC

Define rotation axis unit vector $\mathbf{n}$

#+BEGIN_SRC python
  >>> n = Matrix([nx, ny, nz])
 
  >>> n
  ⎡nx⎤
  ⎢  ⎥
  ⎢ny⎥
  ⎢  ⎥
  ⎣nz⎦
#+END_SRC

Cross-product matrix $[\mathbf{n}]_{\times}$, equivalent to the the cross 
product operator $[\mathbf{u}_{\times}] \mathbf{v} = \mathbf{u} \times \mathbf{v}$.

#+BEGIN_SRC python
  >>> xN = crossm(n)
  
  >>> xN
  ⎡ 0   -nz  ny ⎤
  ⎢             ⎥
  ⎢nz    0   -nx⎥
  ⎢             ⎥
  ⎣-ny  nx    0 ⎦
#+END_SRC

Check the cross-product matrix is correctness $\mathbf{n} \times \mathbf{n} = [\mathbf{n}]_{\times} \mathbf{n} = \mathbf{0}$.
The product of the cross-product matrix of a vector by itself should be a zero vector.
Note: The matrix multiplication operator of Sympy is (*), not (@) 'at' like Python's numpy.

#+BEGIN_SRC python
  >>> xN * n
  ⎡0⎤
  ⎢ ⎥
  ⎢0⎥
  ⎢ ⎥
  ⎣0⎦
#+END_SRC

Compute the general rotation matrix.

\begin{equation*} 
      R = \boldsymbol{n} \boldsymbol{n}^T 
               + (I  - \boldsymbol{n} \boldsymbol{n}^T )  \cos \theta + [\mathbf{n}]_{\times} \sin \theta
\end{equation*}

#+BEGIN_SRC python
  >>> R = n * n.T + (I3 - n * n.T) * C + xN * S
  
  >>> R
  ⎡     ⎛      2⎞     2                                                     ⎤
  ⎢   C⋅⎝1 - nx ⎠ + nx      -C⋅nx⋅ny - S⋅nz + nx⋅ny  -C⋅nx⋅nz + S⋅ny + nx⋅nz⎥
  ⎢                                                                         ⎥
  ⎢                              ⎛      2⎞     2                            ⎥
  ⎢-C⋅nx⋅ny + S⋅nz + nx⋅ny     C⋅⎝1 - ny ⎠ + ny      -C⋅ny⋅nz - S⋅nx + ny⋅nz⎥
  ⎢                                                                         ⎥
  ⎢                                                       ⎛      2⎞     2   ⎥
  ⎣-C⋅nx⋅nz - S⋅ny + nx⋅nz  -C⋅ny⋅nz + S⋅nx + ny⋅nz     C⋅⎝1 - nz ⎠ + nz    ⎦
#+END_SRC

Generate latex code of the rotation matrix $R(\mathbf{n}, \theta)$:

#+BEGIN_SRC python
  >>> R_tex = latex(R).replace("nx", "n_x").replace("ny", "n_y").replace("nz", "n_z")

  >>> print(R_tex)
  \left[\begin{matrix}C \left(1 - n_x^{2}\right) + n_x^{2} 
                & - C n_x n_y - S n_z + n_x n_y 
                 & - C n_x n_z + S n_y + n_x n_z \\ 
                - C n_x n_y + S n_z + n_x n_y & C 
             \left(1 - n_y^{2}\right) + n_y^{2} 
            & - C n_y n_z - S n_x + n_y n_z\\- C n_x n_z - S n_y + n_x n_z 
          & - C n_y n_z + S n_x + n_y n_z 
        & C \left(1 - n_z^{2}\right) + n_z^{2}\end{matrix}\right]


#+END_SRC

\begin{equation*} 
  R(\mathbf{n}, \theta) = 

  \left[\begin{matrix}C \left(1 - n_x^{2}\right) + n_x^{2} 
                & - C n_x n_y - S n_z + n_x n_y 
                 & - C n_x n_z + S n_y + n_x n_z \\ 
                - C n_x n_y + S n_z + n_x n_y & C 
             \left(1 - n_y^{2}\right) + n_y^{2} 
            & - C n_y n_z - S n_x + n_y n_z\\- C n_x n_z - S n_y + n_x n_z 
          & - C n_y n_z + S n_x + n_y n_z 
        & C \left(1 - n_z^{2}\right) + n_z^{2}\end{matrix}\right]
\end{equation*}

Rotation matrix around X axis $R_x(\theta)$

#+BEGIN_SRC python
  >>> Rx = R.subs({nx: 1, ny: 0, nz: 0, C: cos(q), S: sin(q)})
  
  >>> Rx
  ⎡1    0        0   ⎤
  ⎢                  ⎥
  ⎢0  cos(θ)  -sin(θ)⎥
  ⎢                  ⎥
  ⎣0  sin(θ)  cos(θ) ⎦
#+END_SRC

Rotation matrix around Y axis $R_y(\theta)$

#+BEGIN_SRC python
  >>> Ry = R.subs({nx: 0, ny: 1, nz: 0, C: cos(q), S: sin(q)})
  
  >>> Ry
  ⎡cos(θ)   0  sin(θ)⎤
  ⎢                  ⎥
  ⎢   0     1    0   ⎥
  ⎢                  ⎥
  ⎣-sin(θ)  0  cos(θ)⎦
#+END_SRC

Rotation matrix around Z axis $R_z(\theta)$:

#+BEGIN_SRC python
  >>> Rz = R.subs({nx: 0, ny: 0, nz: 1, C: cos(q), S: sin(q)})
  
  >>> Rz
  ⎡cos(θ)  -sin(θ)  0⎤
  ⎢                  ⎥
  ⎢sin(θ)  cos(θ)   0⎥
  ⎢                  ⎥
  ⎣  0        0     1⎦
#+END_SRC

Complete Rotation Matrix: 

#+BEGIN_SRC python
  R_ = R.subs({C: cos(q), S: sin(q)})

  for i in range(0, 3):
      for j in range(0, 3):
          print(f"R({i+1},{j+1}) = "); pprint(R_[i, j]); print()
  
  >>> for i in range(0, 3):
  ...     for j in range(0, 3):
  ...         print(f"R({i+1},{j+1}) = "); pprint(R_[i, j]); print()
  ... 
  R(1,1) = 
    2   ⎛      2⎞       
  nx  + ⎝1 - nx ⎠⋅cos(θ)
  
  R(1,2) = 
  -nx⋅ny⋅cos(θ) + nx⋅ny - nz⋅sin(θ)
  
  R(1,3) = 
  -nx⋅nz⋅cos(θ) + nx⋅nz + ny⋅sin(θ)
  
  R(2,1) = 
  -nx⋅ny⋅cos(θ) + nx⋅ny + nz⋅sin(θ)
  
  R(2,2) = 
    2   ⎛      2⎞       
  ny  + ⎝1 - ny ⎠⋅cos(θ)
  
  R(2,3) = 
  -nx⋅sin(θ) - ny⋅nz⋅cos(θ) + ny⋅nz
  
  R(3,1) = 
  -nx⋅nz⋅cos(θ) + nx⋅nz - ny⋅sin(θ)
  
  R(3,2) = 
  nx⋅sin(θ) - ny⋅nz⋅cos(θ) + ny⋅nz
  
  R(3,3) = 
    2   ⎛      2⎞       
  nz  + ⎝1 - nz ⎠⋅cos(θ)

#+END_SRC

CSE - Common Subexpression Elimination for code generation. 

  + Common Subexpression Elimination is a compiler optimization that
    collects multiple subexpressions and evaluates them only once in
    order to increase the performence and avoid evaluating expression
    multiple times. Python's Sympy library for symbolic computing has
    CSE function that can be used for generating code from math
    formulas with subexpressions evaluated just a single time.

#+BEGIN_SRC python
   >>> terms = cse(R_)
   
   >>> len(terms)
   2
   
   >>> len(terms[0])
   14
   
   >>> len(terms[1])
   1

   >>> print(terms)
   ([(x0, nx**2), (x1, cos(theta)), (x2, sin(theta)), (x3, nz*x2), 
   (x4, nx*ny), (x5, x1*x4), (x6, nx*nz), (x7, ny*x2), (x8, x1*x6), 
   (x9, ny**2), (x10, nx*x2), (x11, ny*nz), (x12, x1*x11), (x13, nz**2)], [Matrix([
   [x0 + x1*(1 - x0),  nx*ny - x3 - x5,       x6 + x7 - x8],
   [    x3 + x4 - x5, x1*(1 - x9) + x9,  ny*nz - x10 - x12],
   [ nx*nz - x7 - x8,  x10 + x11 - x12, x1*(1 - x13) + x13]])])
#+END_SRC

Display CSE variables: 

#+BEGIN_SRC python
  >>> for (var, val) in terms[0]: print(f"{var} = {val}")
  ... 
  x0 = nx**2
  x1 = cos(theta)
  x2 = sin(theta)
  x3 = nz*x2
  x4 = nx*ny
  x5 = x1*x4
  x6 = nx*nz
  x7 = ny*x2
  x8 = x1*x6
  x9 = ny**2
  x10 = nx*x2
  x11 = ny*nz
  x12 = x1*x11
  x13 = nz**2
#+END_SRC

General rotation matrix with subexpression elimination.

#+BEGIN_SRC python
   >>> R_cse
   ⎡x₀ + x₁⋅(1 - x₀)  nx⋅ny - x₃ - x₅      x₆ + x₇ - x₈   ⎤
   ⎢                                                      ⎥
   ⎢  x₃ + x₄ - x₅    x₁⋅(1 - x₉) + x₉  ny⋅nz - x₁₀ - x₁₂ ⎥
   ⎢                                                      ⎥
   ⎣nx⋅nz - x₇ - x₈   x₁₀ + x₁₁ - x₁₂   x₁⋅(1 - x₁₃) + x₁₃⎦
   
   for i in range(0, 3):
       for j in range(0, 3):
           print(f"R({i+1},{j+1}) = {R_cse[i, j]}")
   
   >>> for i in range(0, 3):
   ...     for j in range(0, 3):
   ...         print(f"R({i+1},{j+1}) = {R_cse[i, j]}")
   ... 
   
   R(1,1) = x0 + x1*(1 - x0)
   R(1,2) = nx*ny - x3 - x5
   R(1,3) = x6 + x7 - x8
   R(2,1) = x3 + x4 - x5
   R(2,2) = x1*(1 - x9) + x9
   R(2,3) = ny*nz - x10 - x12
   R(3,1) = nx*nz - x7 - x8
   R(3,2) = x10 + x11 - x12
   R(3,3) = x1*(1 - x13) + x13
#+END_SRC

Matlab/Octave-like Pseudo Code for the Rotation Matrix with CSE:

#+BEGIN_SRC octave
  % Initialize R as 3x3 zero matrix
  function [R] rotMatrix(theta, n)
     [nx, ny, nz] = n
     % Create Intermediate Variables %
     x0 = nx**2
     x1 = cos(theta)
     x2 = sin(theta)
     x3 = nz*x2
     x4 = nx*ny
     x5 = x1*x4
     x6 = nx*nz
     x7 = ny*x2
     x8 = x1*x6
     x9 = ny**2
     x10 = nx*x2
     x11 = ny*nz
     x12 = x1*x11
     x13 = nz**2
     % Create Rotation Matrix 
     R = zeros(3, 3)
     R(1,1) = x0 + x1*(1 - x0)
     R(1,2) = nx*ny - x3 - x5
     R(1,3) = x6 + x7 - x8
     R(2,1) = x3 + x4 - x5
     R(2,2) = x1*(1 - x9) + x9
     R(2,3) = ny*nz - x10 - x12
     R(3,1) = nx*nz - x7 - x8
     R(3,2) = x10 + x11 - x12
     R(3,3) = x1*(1 - x13) + x13
  end
#+END_SRC

Code for the Rotation Matrix with CSE in Python's Numpy:

#+BEGIN_SRC python
import numpy as np

def rotMat(theta, n):
   nx, ny, nz = n
   # Create Intermediate Variables %
   x0 = nx**2
   x1 = np.cos(theta)
   x2 = np.sin(theta)
   x3 = nz*x2
   x4 = nx*ny
   x5 = x1*x4
   x6 = nx*nz
   x7 = ny*x2
   x8 = x1*x6
   x9 = ny**2
   x10 = nx*x2
   x11 = ny*nz
   x12 = x1*x11
   x13 = nz**2
   # Create Rotation Matrix 
   R = np.zeros((3, 3))
   R[0,0] = x0 + x1*(1 - x0)
   R[0,1] = nx*ny - x3 - x5
   R[0,2] = x6 + x7 - x8
   R[1,0] = x3 + x4 - x5
   R[1,1] = x1*(1 - x9) + x9
   R[1,2] = ny*nz - x10 - x12
   R[2,0] = nx*nz - x7 - x8
   R[2,1] = x10 + x11 - x12
   R[2,2] = x1*(1 - x13) + x13
   return R
#+END_SRC

Testing: Comparison of Sympy rotation matrix computed by Sympy and
Numpy with the previous code.

#+BEGIN_SRC python
   # 60 degrees (pi/3 rads) rotation around X axis
   >>> rotMat(np.pi / 3, [1, 0, 0]) 
   [[ 1.         0.         0.       ] 
    [ 0.         0.5       -0.8660254] 
    [ 0.         0.8660254  0.5      ]]
   
   # 60 degrees (pi/3 rads) rotation around X axis
   >>> R_.subs({nx: 1, ny: 0, nz: 0, q: np.pi / 3})
   ⎡1          0                  0         ⎤
   ⎢                                        ⎥
   ⎢0         0.5         -0.866025403784439⎥
   ⎢                                        ⎥
   ⎣0  0.866025403784439         0.5        ⎦
   
   
   # 45 degrees (PI/4 radians) rotation around Z axis 
   >>> rotMat(np.pi / 4, [0, 0, 1])
    [[ 0.70710678 -0.70710678  0.        ]
     [ 0.70710678  0.70710678  0.        ]
    [ 0.          0.          1.        ]]
   
   # 45 degrees (PI/4 radians) rotation around Z axis 
   >>> R_.subs({nx: 0, ny: 0, nz: 1, q: np.pi / 4})
   ⎡0.707106781186548  -0.707106781186547  0⎤
   ⎢                                        ⎥
   ⎢0.707106781186547  0.707106781186548   0⎥
   ⎢                                        ⎥
   ⎣        0                  0           1⎦
#+END_SRC


*** Camera View Transform

The camera view transform matrix transforms world-space coordinates to
camera-space coordinates. On old OpenGL versions, this matrix was
can be constructed through the OpenGL routine _gluLookAt_ or the GLM
(OpenGL Mathematics) library routine glm::lookAt.

The GLM function glm::lookAt() has the following type signature.

#+BEGIN_SRC cpp
   glm::mat4 glm::lookAt(
                   // Eye vector - camera position in world-space coordinates
                   glm::vec3 const& eye

                   // At vector - point to where camera is looking at
                   // in world-space coordinates.
                 , glm::vec3 const& at

                   // Up vector - camera's orientation. Often set to vertical axis
                   // or Y axis up = (x = 0, y = 1, z = 0) by default.
                 , glm::vec3 const& up
                 );

     // --------- Usage ------------------------//

     ... ... ... ... ...

   glm::mat4 camera_View_transform = glm::lookAt(  eye_camera_current_position
                                                 , point_to_where_camera_is_looking_at
                                                 , up_vector
                                                 );

     // Get shader ID of shader's view transform uniform variable.
     const GLint u_view_transform = glGetUniformLocation(prog, "u_view_transform");

     // Set View tranform shader uniform variable
     glUniformMatrix4fv(u_view_transform, 1, GL_FALSE, glm::value_ptr(camera_View_transform) );
#+END_SRC


This function has the following algorithm:

\begin{equation}
    T_{\text{view}} =
          \begin{bmatrix}
              X_x & X_y & X_z & -\vec{X} \cdot \vec{\text{eye}}  \\
              Y_x & Y_y & Y_z & -\vec{Y} \cdot \vec{\text{eye}}  \\
              Z_x & Z_y & Z_z & -\vec{Z} \cdot \vec{\text{eye}}  \\
              0   & 0   & 0   &  1
          \end{bmatrix}

     =
          \begin{bmatrix}
              X_x & X_y & X_z & - \text{dot}(\vec{X}, \vec{\text{eye}})  \\
              Y_x & Y_y & Y_z & - \text{dot}(\vec{Y}, \vec{\text{eye}})  \\
              Z_x & Z_y & Z_z & - \text{dot}(\vec{Z}, \vec{\text{eye}})  \\
              0   & 0   & 0   &  1
          \end{bmatrix}
\end{equation}

And the unit vectors, X, Y and Z are computed in the following manner:

\begin{eqnarray*}
    \vec{Z} &=&  \frac{ \vec{eye} - \vec{at} }{\left\| \vec{eye} - \vec{at}  \right\| }
              = \frac{ -\vec{\text{forward}}  }{\left\| \vec{\text{forward}}  \right\| } 
              = -\text{normalize}(  \vec{eye} - \vec{at}  )
              = -\text{normalize}( \vec{\text{forward}} )
               \\

    \vec{X} &=&  \frac{ \vec{up} \times \vec{Z} }{\left\| \vec{up} \times \vec{Z}  \right\| } 
               = \text{normalize}( \text{cross}( \vec{up}, \vec{Z}  ) ) 
               \\

    \vec{Y} &=&  \frac{ \vec{Z} \times \vec{X} }{\left\| \vec{Z} \times \vec{X}  \right\| }  
               =  \text{normalize}( \text{cross}( \vec{Z}, \vec{X}  ) ) 
               \\
\end{eqnarray*}

The vector $\vec{Z}$ is the opposite direction, represented by a unit
vector, to where the camera is looking at. If this vector were an
input, and was directly set by the calling code, rotating that camera
viewing direction  would require just rotating this vector.

Where:
  + $\vec{forward} = \vec{at} - \vec{eye}$ => Direction to where the camera is looking at (a.k.a pointing at).
  + INPUTS:
    + $\vec{eye}$ (Eye vector) => Camera's position in world-space coordinates.
    + $\vec{at}$  (At vector) => Point in world-space coordinate where the camera is lookin at.
    + $\vec{up}$  (Up vector) => Camera orientation, often set by default to Y axis.
  + Intermediate Outputs:
    + $\vec{X} = [X_x \quad X_y \quad X_z]$
    + $\vec{Y} = [Y_x \quad Y_y \quad Y_z]$
    + $\vec{Z} = [Z_x \quad Z_y \quad Z_z]$
  + Notation Used:
    + $\left\| \vec{vector}  \right\|$ - means the vector norm
    + $\vec{A} \times \vec{B}$ - means vector cross product
    + $\vec{A} \cdot \vec{B}$ - means vector dot product.


The previous matrix turns world-space into camera-space coordinates in
the following mode:

\begin{equation}
   \begin{bmatrix}
       x_{\text{camera}} \\ y_{\text{camera}} \\ z_{\text{camera}} \\ 1
   \end{bmatrix}

   =  T_{\text{projection}} \cdot

   \begin{bmatrix}
       x_{\text{world}} \\ y_{\text{world}} \\ z_{\text{world}} \\ 1
   \end{bmatrix}
\end{equation}

Where:

  + The right-side (4 x 1) column vector (input) is the world-space coordinate.

  + The left-side (4 x 1) column vector (ouput) is the camera-space
    coordinate.

 *Pointing camera to specific direction*

The function lookAt() points the camera view to a specific point in
the space _at_ vector in world-space coordinates. The following function
could be defined for pointing the camera at specific direction (vector
or normalized vector) instead of a point-vector.

#+BEGIN_SRC cpp
   glm::mat4
   lookAt_direction(glm::vec3 const& eye, glm::vec3 const& forward, glm::vec3 const& up  )
   {
       glm::vec3 at = eye + forward;
       return lookAt(eye, at, up);
   }
#+END_SRC

Where:

  + _eye_ - (point-verctor) Camera position in world-space coordinates
  + _forward_ - (vector) Direction to where camera is looking at.
  + _up_ (vector) Camera's orientation


 *Algorithm Validation*

Testing GLM (OpenGL Mathematic Library) in CERN's root REPL:

#+BEGIN_SRC cpp
   // -------------- Case  1 --------------------- //
   //
   auto eye = glm::vec3(3.0, 10.0, 20.0);
   auto at  = glm::vec3(50.0, 25.0, 10.0);
   auto up  = glm::vec3(0.0, 1.0, 0.0);

   root [108] auto view_matrix = glm::lookAt(eye, at, up);
   root [109]
   root [109] show_matrix("view = ", view_matrix)

    [MATRIX] view =  =
      0.208  -0.000   0.978 -20.186
     -0.291   0.955   0.062  -9.912
     -0.934  -0.298   0.199   1.808
      0.000   0.000   0.000   1.000
   root [110]

   // ------------ Case 2 --------------------------//
   auto eye = glm::vec3(50.0, -10.0, 0.0);
   auto up = glm::vec3(0.0, 1.0, 0.0);
   auto at = glm::vec3(25.0, 5.615, -200.0);
   auto view_matrix = glm::lookAt(eye, at, up);

   root [114]
   root [114] show_matrix("view = ", view_matrix)

    [MATRIX] view =  =
      0.992   0.000  -0.124 -49.614
      0.010   0.997   0.077   9.491
      0.124  -0.077   0.989  -6.956
      0.000   0.000   0.000   1.000

#+END_SRC

Implement glm::lookAt() algorithm in Julia programming language:

#+BEGIN_SRC julia
  # Return vector v normalized =>> Returns column vector
  normalize(v) = begin
     x, y, z = v
     mag = sqrt(x * x + y * y + z * z)
     [ (x / mag) (y / mag) (z / mag)]'
  end

  # Vector cross product =>> Returns column vector
  function cross(va, vb)
     x, y, z = va
     T = [ 0 -z y ; z 0 -x; -y x 0]
     return T * vb
  end

  # Vector Dot product via vector column X transpose multiplication
  dot(va, vb) = va' * vb

  # Note: The inputs must be column vectors
  function lookAt(eye, at, up)
     # The vector Z is the direction to where the camera is looking at.
     Z = normalize( eye - at      )
     X = normalize( cross(up, Z)  )
     Y = normalize( cross(Z, X )  )
     t = [ dot(eye, X)  dot(eye, Y)  dot(eye, Z) ]

     # Define identity matrix
     matrix = [ 1.0 0.0 0.0 0.0 ; 0.0 1.0 0.0 0.0 ; 0.0 0.0 1.0 0.0; 0.0 0.0 0.0 1.0]

     # Assign vector X to matrix's first row
     matrix[1, 1:3] = X
     # Assign vector Y to matrix's second row
     matrix[2, 1:3] = Y
     # Assign vector Z to matrix's third row
     matrix[3, 1:3] = Z
     # Assign forth (last) column to vector t
     matrix[1:3, 4] = -t
     return matrix
  end
#+END_SRC

Test implementation:

#+BEGIN_SRC julia
   #  --------- Test Case 1 --------------------------#
   # =================================================#
   eye = [3.0 10.0 20.0  ]'
   at  = [50.0 25.0 10.0 ]'
   up  = [0.0   1.0 0.00 ]' # Y axis

   julia> eye
   3×1 LinearAlgebra.Adjoint{Float64,Array{Float64,2}}:
     3.0
    10.0
    20.0

   # Camera's view transform matrix
   julia> Tview = lookAt(eye, at, up)
   4×4 Array{Float64,2}:
    -0.208108  0.0       -0.978106   20.1864
    -0.291457  0.954572   0.062012   -9.91159
     0.933672  0.297981  -0.198654  -52.1466
     0.0       0.0        0.0         1.0

   # Camera's view transform matrix rounded with 3 decimal digits
   julia> map(x -> round(x, digits=3), Tview)
   4×4 Array{Float64,2}:
     0.208   0.0    0.978  -20.186
    -0.291   0.955  0.062   -9.912
    -0.934  -0.298  0.199    1.808
     0.0     0.0    0.0      1.0

   #  --------- Test Case 2 --------------------------#
   # =================================================#
   eye = [50.0 -10.0 0.0 ]'
   up  = [0.0 1.0 0.0 ]'
   at  = [25.0 5.615 -200.0]'

   Tview = lookAt(eye, at, up)

   julia> map(x -> round(x, digits=3), Tview)
   4×4 Array{Float64,2}:
    0.992   0.0    -0.124  -49.614
    0.01    0.997   0.077    9.491
    0.124  -0.077   0.989   -6.956
    0.0     0.0     0.0      1.0

#+END_SRC


 *References*

  + [[http://ivl.calit2.net/wiki/images/2/2c/03_CoordinateSystemsS15.pdf][CSE 167: Introduction to Computer Graphics]] - Lecture 3 - Coordinate Systems.

  + [[https://www.cs.utexas.edu/~fussell/courses/cs354/lectures/lecture11.pdf][Viewing and Modelling - CS354]]
*** Camera Projection Transform

The camera's projection transform matrix turns camera-space
coordinates into clip-space coordinates, which are normalized NDC
(Normalized-Device Coordinates). Any coordinate in the clip-space
coordinate out of the range (-1.0 to 1.0) in the three axis X, Y and Z
will not be shown at the screen, they will be clipped. It is worth
noting that some projection matrices are not affine transforms.


 *Ortographic Projection Transform* - [[https://cseweb.ucsd.edu/classes/wi18/cse167-a/lec4.pdf][(CSE167 - page 25)]] ; [[https://docs.microsoft.com/en-us/windows/win32/opengl/glortho][(Microsoft- WGL)]]

The orthographic perspective transformation preserves parallel lines
and provides no sense of depth. This type of projection is suitable
for charts, engineering drawing, engineering blueprints, CADs
(Computer Aided Design) views and so on.

  + Computed with subroutines:
    + _glm::ortho_ (xmin, xmax, ymin, ymax, zNear, zFar)  => GLM math library
    + _glm::ortho_ (left, right, bottom, top, near, far)
    + glOrtho(left, right, bottom, top, near, far) => Legacy OpenGL


\begin{equation}
   T_{\text{ortho}} =
          \begin{bmatrix}
                  \frac{ 2 }{ x_{max} - x_{min} }
               &  0
               & 0
               & -\frac{ x_{max} + x_{min} }{ x_{max} - x_{min} }
               \\

                  0
               &  \frac{ 2 }{ y_{max} - y_{min} }
               &  0
               &  -\frac{ y_{max} + y_{min} }{ y_{max} - y_{min} }
               \\

                  0
               &  0
               &  - \frac{ 2 }{ z_{far} - z_{near} }
               &  - \frac{ z_{far} + z_{near} }{ z_{far} - z_{near} }
               \\

              0 & 0 & 0 & 1
          \end{bmatrix}
\end{equation}

Where:
  + $x_{min}$ (left)
  + $x_{max}$ (right)
  + $y_{min}$ (bottom)
  + $y_{max}$ (top)
  + $z_{near}$  (near) => Distance from the camera coordinate system to the nearest clipping plane.
  + $z_{far}$  (far)   => Distance from the camera coordinate system to the farthest clipping plane.

Observations:
  + The axis Z points the opposite direction to where the camera is looking at.
  + Any coordinate outside the range ($x_{min}$, $x_{max}$), ($y_{min}$, $y_{max}$) and ($z_{far}$, $z_{near}$) will not be visible.
  + Those parameters are in the camera's coordinate system.


If a camera looking at the negative direction of Z axi is positioned
at (x = 0, y = 0, z = 0) in world-coordinate origin. The ortographic
projection for OpenGL's canonical view volume is the identity matrix.

  + $x_{min} = -1$, $x_{min} = +1$
  + $y_{min} = -1$, $z_{min} = +1$
  + $z_{near} = +1$
  + $z_{far} =  -1$

\begin{equation}
   T_{\text{ortho}} =  \text{ortho}(-1, +1, -1, +1, +1, -1)
        \\  =
          \begin{bmatrix}
                  \frac{ 2 }{ (+1) - (-1) }
               &  0
               & 0
               & -\frac{ (+1) + (-1) }{ (+1) - (-1) }
               \\

                  0
               &  \frac{ 2 }{ (+1) - (-1) }
               &  0
               &  -\frac{ (+1) +  (-1) }{ (+1) - (-1) }
               \\

                  0
               &  0
               &  - \frac{ 2 }{ -(-1) + (+1) }
               &  - \frac{ (-1) + (+1) }{ (-1) - (+1) }
               \\

              0 & 0 & 0 & 1
          \end{bmatrix}

    \\ =   \begin{bmatrix}
            1 & 0 & 0 & 0 \\
            0 & 1 & 0 & 0 \\
            0 & 0 & 1 & 0 \\
            0 & 0 & 0 & 1
         \end{bmatrix}
\end{equation}



  *Ortographic Projection for 2D drawing*

The ortographic projection can be used 2D drawing by setting the
coordinate Z of any vertex to zero, $z_{near} = -1$ and $z_{far} = +1$.
Then, the ortographic projection becomes:


\begin{equation}
   T_{\text{ortho2D}} =
          \begin{bmatrix}
                  \frac{ 2 }{ x_{max} - x_{min} }
               &  0
               & 0
               & -\frac{ x_{max} + x_{min} }{ x_{max} - x_{min} }
               \\

                  0
               &  \frac{ 2 }{ y_{max} - y_{min} }
               &  0
               &  -\frac{ y_{max} + y_{min} }{ y_{max} - y_{min} }
               \\

                  0  &  0  &  1 &  0    \\
                  0 & 0 & 0 & 1
          \end{bmatrix}
\end{equation}

If the transform is applied to any vertex with z = 0, the following
outcome is achieved.

\begin{equation}
   \begin{bmatrix} x' \\ y' \\ z' \\ 1 \end{bmatrix}

  =

  T_{ortho2D}

   \begin{bmatrix} x \\ y \\ z = 0 \\ w = 1 \end{bmatrix}

  =

   \begin{bmatrix}
        \frac{ 2 }{ x_{max} - x_{min} } \cdot x  -  \frac{ x_{max} + x_{min} }{ x_{max} - x_{min} }  \\
        \frac{ 2 }{ y_{max} - y_{min} } \cdot y  -  \frac{ y_{max} + y_{min} }{ y_{min} - y_{min} }  \\
         0  \\
         1  \\
   \end{bmatrix}
\end{equation}



 *Perspective projection matrix* (with FOV - Field-of-View)

  + This matrix is generated by the following subroutines:
    + _glm::perspective_ (FOV, aspect_ratio, zNear, zFar) => GLM library
    + _glPerspective_ (FOV, aspect_ratio, zNear, zFar)  => Old OpenGL.

\begin{equation}
   T_{\text{perspective}} =  \begin{bmatrix}
                 a_{11}&  0     &  0     &  0      \\
                  0    & a_{22} &  0     &  0      \\
                  0    &  0     & a_{33} &  a_{34} \\
                  0    &  0     & -1     &  0      \\
             \end{bmatrix}
\end{equation}

The matrix elements are:

\begin{eqnarray*}
   a_{11} &=& 1 / ( k \cdot \tan( \theta / 2 ) )                           \\
   a_{22} &=& 1 / \tan( \theta / 2 )                                              \\
   a_{33} &=& \frac{ \text{zN} + \text{zF} }{ \text{zN} - \text{zF} }             \\
   a_{43} &=& \frac{ 2 \cdot \text{zN} \cdot \text{zF} }{ \text{zN} - \text{zF} } \\
\end{eqnarray*}


Where:
  + k - is the window aspect ration, the ratio between the window
    width and its height. So:
    + $k = \text{width} / \text{height}$
  + $\theta$ - is the FOV (Field-Of-View angle)
  + $zN$ (zNear) - Distance to near plane.
  + $zF$ (zFar) - Distance to far plane.
  + Only camera-space coordinates within the range $zN$ to $zF$ will
    be displayed on the screen. Anything out of this range will be
    clipped.

This matrix transform coordinates in the following manner:

\begin{equation}
   \begin{bmatrix}
       x_{\text{clip}} \\ y_{\text{clip}} \\ z_{\text{clip}} \\ 1
   \end{bmatrix}

   =  T_{\text{projection}} \cdot

   \begin{bmatrix}
       x_{\text{camera}} \\ y_{\text{camera}} \\ z_{\text{camera}} \\ 1
   \end{bmatrix}
\end{equation}

Where:

  + The right-side (4 x 1) column vector (input) is the camera-space coordinate.

  + The left-side (4 x 1) column vector (ouput) is the clip-space
    coordinate. Any clip-space (NDC coordinates) coordinate out of the
    range -1.0 to 1.0 on each axis will not be displayed on the
    screen.



 *Perspective Projection Matrix* (defined from View frustum) - [[https://cseweb.ucsd.edu/classes/wi18/cse167-a/lec4.pdf][(CSE167)]],

The perspective projection matrix provides a sense of depth, objects
that are far from the camera will look like smaller and objects near
the camera will look like smaller. Note: parallel lines are not
preserved by this transform and also this transform is not affine.


  + This matrix can be computed by using subroutines:
    + _glm::frustum_ (Xmin, Xmax, Ymin, Ymax, Zmin, Zmax)
    + _glm::frustum_ (left, right, bottom, top, near, far)


\begin{equation}
   T_{\text{perspective}} =
          \begin{bmatrix}
                  \frac{ 2 \cdot z_{near} }{ x_{max} - x_{min} }
               &  0
               & \frac{ x_{max} + x_{min} }{ x_{max} - x_{min} }
               & 0
               \\

                  0
               &  \frac{ 2 \cdot z_{near} }{ y_{max} - y_{min} }
               &  \frac{ y_{max} + y_{min} }{ y_{max} - y_{min} }
               &  0
               \\

                  0
               &  0
               &  - \frac{ z_{far} + z_{near} }{ z_{far} - z_{near} }
               &  - \frac{ z_{far} \cdot z_{near} }{ z_{far} - z_{near} }
               \\

              0 & 0 & -1 & 0
          \end{bmatrix}
\end{equation}

Aspect ratio and field of view (FOV) angle:

\begin{eqnarray*}
   \text{aspect ratio} &=& \frac{x_{max} - x_{min}}{ y_{min} - y_{max} }  \\
   \tan( \text{FOV} / 2) &=& \frac{ y_{max} }{ z_{min} }
\end{eqnarray*}

Where:

  + $x_{min}$ => (left)   - Minimum X coordinate
    + Coordinate for the _left_ clipping plane.

  + $x_{max}$ => (right)  - Maximum X coordinate
    + Coordinate for the _right_ clipping plane.

  + $y_{min}$ => (bottom) - Minimum Y coordinate
    + Coordinate for the _bottom_ clipping plane.

  + $y_{max}$ => (top)    - Maximum Y coordinate
    + Coordinate for the _top_ clipping plane.

  + $z_{near}$  => (near)
    + Minimum Z coordinate - distance to the _near_ depth clipping
      plane. The $z_{near}$ value should never be zero, in this case,
      the $z_{near}$ value should as close as possible to zero.

  + $z_{far}$  => (far)
    + Maximum Z coordinate  - distance to the _far_ depth clippling plane.

Notes:
  + The camera is looking at the opposite direction of Z axis.
  + Any point in camera-space coordinate outside of the previous range
    will not be visible on the screen.
  + The parameters $z_{far}$ and $z_{near}$ must be positive.


 *Perspective and orthogonal transform matrix formula validation - in Julia Language*

Running GLM math library in CERN's Root RPL:

#+BEGIN_SRC cpp
   root [0] .I .

   #include <glm/glm.hpp>
   #include <glm/gtc/matrix_transform.hpp>
   #include <glm/gtc/type_ptr.hpp>
   #include <glm/gtx/string_cast.hpp>
   #include <glm/gtx/io.hpp>

   std::cout << std::setprecision(5);

   // Note: GLM matrices are stored in Column-major order
   void show_matrix(const char* label, glm::mat4 const& m){
       std::cout << "\n [MATRIX] " << label << " = " << '\n';
       std::cout << std::fixed << std::setprecision(5);
       for(size_t i = 0; i < 4; i++)
       {
           for(size_t j = 0; j < 4; j++)
           {
               std::cout << std::setw(10) << m[j][i];
           }
           std::cout << '\n';
       }
   }


   float xmin, xmax, ymin, ymax, zmin, zmax;

   // --------- Test Case 1 --------------------------//
   //
   root [58] xmin = 4.0, xmax = 10.0, ymin = 0.1, ymax = 25.0, zmin = 8.0, zmax = 20.0;

   root [59] glm::mat4 T = glm::frustum(xmin, xmax, ymin, ymax, zmin, zmax);

   root [60] std::cout << " T = " << T << '\n'
    T =
   [[    2.667,    0.000,    2.333,    0.000]
    [    0.000,    0.643,    1.008,    0.000]
    [    0.000,    0.000,   -2.333,  -26.667]
    [    0.000,    0.000,   -1.000,    0.000]]


   // ------- Test Case 2 ------------------------------//

   root [93] xmin = -20.0, xmax = 50.0, ymin = -10.0, ymax = 25.0, zmin = 0.1, zmax = 100.0;
   root [94]
   root [94] glm::mat4 T = glm::frustum(xmin, xmax, ymin, ymax, zmin, zmax)
   (glm::mat4 &) @0x7f73c3d5d490
   root [95]
   root [95] show_matrix("T", T)

    [MATRIX] T =
      0.00286   0.00000   0.42857   0.00000
      0.00000   0.00571   0.42857   0.00000
      0.00000   0.00000  -1.00200  -0.20020
      0.00000   0.00000  -1.00000   0.00000
   root [96]


   // ------- Test Case 3 - Ortographics Matrix  -----------//

   root [96] xmin = -20.0, xmax = 50.0, ymin = -10.0, ymax = 25.0, zmin = 0.1, zmax = 100.0;
   root [97]
   root [97] glm::mat4 T = glm::ortho(xmin, xmax, ymin, ymax, zmin, zmax)
   (glm::mat4 &) @0x7f73c3d5d4d0
   root [98]
   root [98] show_matrix("T", T)

    [MATRIX] T =
      0.02857   0.00000   0.00000  -0.42857
      0.00000   0.05714   0.00000  -0.42857
      0.00000   0.00000  -0.02002  -1.00200
      0.00000   0.00000   0.00000   1.00000

#+END_SRC

Test in Julia programming language:

#+BEGIN_SRC julia
   function frustum(xmin, xmax, ymin, ymax, zmin, zmax)
        a11 = 2 * zmin / (xmax - xmin)
        a22 = 2 * zmin / (ymax - ymin)
        a13 = (xmax + xmin) / (xmax - xmin)
        a23 = (ymax + ymin) / (ymax - ymin)
        a33 = -(zmax + zmin) / (zmax - zmin)
        a34 = -2 * zmax * zmin / (zmax - zmin)
        T = [ a11 0 a13 0 ; 0 a22 a23 0; 0 0 a33 a34; 0 0 -1 0]
        return T
    end

    function ortho(xmin, xmax, ymin, ymax, zmin, zmax)
          a11 = 2 / (xmax - xmin)
          a22 = 2 / (ymax - ymin)
          a33 = -2 / (zmax - zmin)
          a14 = -(xmax + xmin ) / (xmax - xmin)
          a24 = -(ymin + ymax) / (ymax - ymin)
          a34 = -(zmax + zmin) / (zmax - zmin)
          T = [ a11 0 0 a14; 0 a22 0 a24 ; 0 0 a33 a34; 0 0 0 1]
          return T
    end


   ## --------- Test Case 1 --------------------------##

   julia> xmin = 4.0; xmax = 10.0; ymin = 0.1; ymax = 25.0; zmin = 8.0; zmax = 20.0;

   julia> frustum(xmin, xmax, ymin, ymax, zmin, zmax)
   4×4 Array{Float64,2}:
    2.66667  0.0       2.33333    0.0
    0.0      0.64257   1.00803    0.0
    0.0      0.0      -2.33333  -26.6667
    0.0      0.0      -1.0        0.0

   ## --------- Test Case 2 --------------------------##

  julia> xmin = -20.0; xmax = 50.0; ymin = -10.0; ymax = 25.0; zmin = 0.1; zmax = 100.0;

  julia> frustum(xmin, xmax, ymin, ymax, zmin, zmax)
  4×4 Array{Float64,2}:
   0.00285714  0.0          0.428571   0.0
   0.0         0.00571429   0.428571   0.0
   0.0         0.0         -1.002     -0.2002
   0.0         0.0         -1.0        0.0

  ## -------- Test Case 3 - Ortographic Projection ---##

  julia> xmin = -20.0; xmax = 50.0; ymin = -10.0; ymax = 25.0; zmin = 0.1; zmax = 100.0;

  julia> ortho(xmin, xmax, ymin, ymax, zmin, zmax)
  4×4 Array{Float64,2}:
   0.0285714  0.0         0.0      -0.428571
   0.0        0.0571429   0.0      -0.428571
   0.0        0.0        -0.02002  -1.002
   0.0        0.0         0.0       1.0

#+END_SRC


  *References*

  + *GlFrustum - Khronos Group* (Documentation of function glFrustum())
    + https://www.khronos.org/registry/OpenGL-Refpages/gl2.1/xhtml/glFrustum.xml

  + *Projection and viewing - Computer Graphics - CSE167 - Lecture 4* [BEST]
    + https://cseweb.ucsd.edu/classes/wi18/cse167-a/lec4.pdf
    + glFrustum() - perspective transform formula located at page 20.

  + *View Frustum Clipping* - University of Texas - CS354 [BEST]
    + https://www.cs.utexas.edu/~fussell/courses/cs354-fall2015/lectures/lecture9.pdf
    + glFrustum() - formula located at page

  + *glOrtho - Khronos Grouo* (Documentation of function glOrtho())
    + https://www.khronos.org/registry/OpenGL-Refpages/gl2.1/xhtml/glOrtho.xml

  + *Ortographic Projection*
    + https://en.wikipedia.org/wiki/Orthographic_projection

  + *Computergrafik - Matthias Zwicker Universität Bern - Herbst 2016*
    + https://www.cs.umd.edu/~zwicker/courses/computergraphics/03_Projection.pdf

  + *Matrix4x4.Ortho - Unity 3D engine*
    + https://docs.unity3d.com/ScriptReference/Matrix4x4.Ortho.html

  + *Microsoft WGL Docs* - glFrustum() [BEST]
    * https://docs.microsoft.com/en-us/windows/win32/opengl/glfrustum

  + *Microsoft WGL Docs* - glOrtho() [BEST]
    * https://docs.microsoft.com/en-us/windows/win32/opengl/glortho

  + *CSE - 420 Computer Graphics* - California State University [BEST]
    * http://cse.csusb.edu/tongyu/courses/cs420/notes/viewing2.php

*** Concatenating Affine Transforms

  *Concatenating / Combinating of affine transforms*

Multiple affine transforms can be combined or concatenated as a single
affine transform, just by multiplying matrices. Considering the
following sequence of 3D affine transforms, such as:

   1. scale(3, 3, 3) - scale by 3 on all axis.
   2. translate(x = 25, y = 10, z = 20) - translate to point (25, 30, 2)
   3. rotX(30) - rotate 30 degrees around X axis
   4. rotZ(54) - rotate by 54 degrees around Z axis.

Let V = [ x y z 1 ]' to be (4 x 1 - 4 rows and 1 column) a column
vector containing the current vertice homogeneous coordinates.

Those transforms can be combined in the following way:

  + Node: the symbol (·) means multiplication.

#+BEGIN_SRC js
    // Where:
    //  =>  scale(3, 3, 3) is the scaling affine transform matrix.
    //  =>  Va is the vertex coordinate after applying rotZ(54)
    //
    Va = rotZ(54) · V

    // Where:
    // => translate(25, 10, 20) is the translation affine transfom matrix.
    // => Vb is the result, vertex coodinate after the transform was applied.
    Vb = rotX(30) · Va

    Vc = translate(25, 10, 20) · Vb
    Vd = scale(3, 3, 3) · Vc

    // V' (V - prime) - overall transformation result (end result)
    V' = Vd
#+END_SRC

By performing algebraics substitution, the overall transform result
becomes:

#+BEGIN_SRC text
   STEP 1:
    Vb = rotX(30) · Va
    Vb = rotX(30) · ( rotZ(54) · Va )

   STEP 2:
     Vc = translate(25, 10, 20) · Vb
     Vc = translate(25, 10, 20) ·  ( rotX(30) · ( rotZ(54) · Va ) )

   STEP 3:
     Vd = scale(3, 3, 3) · Vc
     Vd = scale(3, 3, 3) · ( translate(25, 10, 20) ·  rotX(30) ·  rotZ(54) · Va )

   STEP 4:  - End result
     V' = Vd
     V' = scale(3, 3, 3) · translate(25, 10, 20) ·  rotX(30) ·  rotZ(54) · Va 
#+END_SRC

The final vertex position can be stated as:

#+BEGIN_SRC text
     V' = scale(3, 3, 3) · translate(25, 10, 20) ·  rotX(30) ·  rotZ(54) · Va 

  OR:

     V' =  [ scale(3, 3, 3) · translate(25, 10, 20) ·  rotX(30) ·  rotZ(54) ] · V
#+END_SRC

All the sequence of transformations can be combined as a single
transform, designated by T, which is the multiplication between all
transforms in reverse order that they were applied. The last transform
is multiplied first and first transform is multiplied last. In the
same that matrix multiplication is not commutative, the operation
result depends on the order that of matrices, transform combinations
are not commutative, they also depends on the order that transforms
were applied.

#+BEGIN_SRC text
  V' = T · V

  Where:
     T =  scale(3, 3, 3) · translate(25, 10, 20) ·  rotX(30) ·  rotZ(54)
#+END_SRC

Multiple transforms can be combined as:

#+BEGIN_SRC text
    Tranform_combined = Transform[1] · Transform[2] · ... · Transform[N-1] · Transform[N]
#+END_SRC


  *Combining 2D canonical transforms in Sympy - Python CAS*

Sympy - Python CAS (Computer Algebra System) can be used for debugging
and testing affine transforms in a symbolic way.

Define 2D canonical affine transforms:

#+BEGIN_SRC python
   from sympy import *

   x,  y  = symbols("x y")
   dx, dy = symbols("dx dy")
   sx, sy = symbols("sx sy")
   t      = symbols("t")  # t represents the rotation angle theta (θ)


   # Column vector representing - generic Vertex (aka point) in homogeneous coordinate
   #
   v = Matrix([x, y, 1])

   >>> pprint(v)
   ⎡x⎤
   ⎢ ⎥
   ⎢y⎥
   ⎢ ⎥
   ⎣1⎦

   # Translation of (dx, dy) coordinate transform
   >>> Translate = Matrix([[1, 0, dx], [0, 1, dy], [0, 0, 1]])

   >>> pprint(Translate)
   ⎡1  0  dx⎤
   ⎢        ⎥
   ⎢0  1  dy⎥
   ⎢        ⎥
   ⎣0  0  1 ⎦


   # Scale affine transform
   >>> Scale = Matrix([[sx, 0, 0], [0, sy, 0], [0, 0, 1]])

   >>> pprint(Scale)
   ⎡sx  0   0⎤
   ⎢         ⎥
   ⎢0   sy  0⎥
   ⎢         ⎥
   ⎣0   0   1⎦


   # Rotation around Z axis
   >>> RotZ  = Matrix([[ cos(t), -sin(t), 0], [ sin(t), cos(t), 0], [0, 0, 1]])

   >>> pprint( RotZ )
   ⎡cos(t)  -sin(t)  0⎤
   ⎢                  ⎥
   ⎢sin(t)  cos(t)   0⎥
   ⎢                  ⎥
   ⎣  0        0     1⎦

#+END_SRC

Apply single transforms to vertex *v*.

#+BEGIN_SRC python
  # Apply translation affine transform to vertex V
  #-------------------------------------------
  >>> pprint( Translate * v )
  ⎡dx + x⎤
  ⎢      ⎥
  ⎢dy + y⎥
  ⎢      ⎥
  ⎣  1   ⎦

  # Apply scale to vertex v
  #-------------------------------------------

  >>> Scale * v
  Matrix([
  [sx*x],
  [sy*y],
  [   1]])

  >>> pprint( Scale * v )
  ⎡sx⋅x⎤
  ⎢    ⎥
  ⎢sy⋅y⎥
  ⎢    ⎥
  ⎣ 1  ⎦

  >>> pprint( (Scale * v).subs({sx: 10, sy: 10}) )
  ⎡10⋅x⎤
  ⎢    ⎥
  ⎢10⋅y⎥
  ⎢    ⎥
  ⎣ 1  ⎦


  >>> pprint( (Scale * v).subs({sx: 10, sy: 20}) )
  ⎡10⋅x⎤
  ⎢    ⎥
  ⎢20⋅y⎥
  ⎢    ⎥
  ⎣ 1  ⎦


  # Apply rotation to vertex v
  #------------------------------

  >>> v_rot = RotZ * v

  >>> pprint( v_rot )
  ⎡x⋅cos(t) - y⋅sin(t)⎤
  ⎢                   ⎥
  ⎢x⋅sin(t) + y⋅cos(t)⎥
  ⎢                   ⎥
  ⎣         1         ⎦

  >>> pprint( v_rot.subs(t, 0) )
  ⎡x⎤
  ⎢ ⎥
  ⎢y⎥
  ⎢ ⎥
  ⎣1⎦

  # Rotate by 90 degrees (pi / 2 radians)
  >>> pprint( v_rot.subs(t, pi / 2) )
  ⎡-y⎤
  ⎢  ⎥
  ⎢x ⎥
  ⎢  ⎥
  ⎣1 ⎦

  # Rotate by 60 degreees (pi / 3)
  >>> pprint( v_rot.subs(t, pi / 3) )
  ⎡x   √3⋅y⎤
  ⎢─ - ────⎥
  ⎢2    2  ⎥
  ⎢        ⎥
  ⎢√3⋅x   y⎥
  ⎢──── + ─⎥
  ⎢ 2     2⎥
  ⎢        ⎥
  ⎣   1    ⎦
#+END_SRC

Combine 2 tranforms in multiple orders and check the outcome.

#+BEGIN_SRC python
  # Combine transforms: 1st - translation; 2nd - scale.
  #--------------------------------------------------

    >>> T = Scale * Translate

    >>> pprint(T)
    ⎡sx  0   dx⋅sx⎤
    ⎢             ⎥
    ⎢0   sy  dy⋅sy⎥
    ⎢             ⎥
    ⎣0   0     1  ⎦


    # Apply to vertex
    >>> pprint( T * v )
    ⎡dx⋅sx + sx⋅x⎤
    ⎢            ⎥
    ⎢dy⋅sy + sy⋅y⎥
    ⎢            ⎥
    ⎣     1      ⎦


  # Combine transforms: 1st - scale; 2nd - translation.
  #--------------------------------------------------

     >>> T = Translate * Scale

     >>> pprint( T )
     ⎡sx  0   dx⎤
     ⎢          ⎥
     ⎢0   sy  dy⎥
     ⎢          ⎥
     ⎣0   0   1 ⎦

     # Apply to vertex
     >>> pprint(T * v)
     ⎡dx + sx⋅x⎤
     ⎢         ⎥
     ⎢dy + sy⋅y⎥
     ⎢         ⎥
     ⎣    1    ⎦


  # Combine transforms: 1st - rotation; 2nd - translation.
  #--------------------------------------------------

     >>> T = Translate * RotZ

     >>> pprint( T )
     ⎡cos(t)  -sin(t)  dx⎤
     ⎢                   ⎥
     ⎢sin(t)  cos(t)   dy⎥
     ⎢                   ⎥
     ⎣  0        0     1 ⎦


     # Apply to vertex
     >>> pprint( T * v )
     ⎡dx + x⋅cos(t) - y⋅sin(t)⎤
     ⎢                        ⎥
     ⎢dy + x⋅sin(t) + y⋅cos(t)⎥
     ⎢                        ⎥
     ⎣           1            ⎦

  # Combine transforms: 1st - translation ; 2nd - rotation
  #------------------------------------------------------

     >>> T = RotZ * Translate

     >>> pprint( T )
     ⎡cos(t)  -sin(t)  dx⋅cos(t) - dy⋅sin(t)⎤
     ⎢                                      ⎥
     ⎢sin(t)  cos(t)   dx⋅sin(t) + dy⋅cos(t)⎥
     ⎢                                      ⎥
     ⎣  0        0               1          ⎦


     # Apply to vertex
     >>> pprint( T * v )
     ⎡dx⋅cos(t) - dy⋅sin(t) + x⋅cos(t) - y⋅sin(t)⎤
     ⎢                                           ⎥
     ⎢dx⋅sin(t) + dy⋅cos(t) + x⋅sin(t) + y⋅cos(t)⎥
     ⎢                                           ⎥
     ⎣                     1                     ⎦


#+END_SRC

Combine transforms in the following order: 1st - rotation; 2nd -
scaling; 3rd - translation:

#+BEGIN_SRC python
   >>> T = Translate * Scale * RotZ

   >>> pprint( T )
   ⎡sx⋅cos(t)  -sx⋅sin(t)  dx⎤
   ⎢                         ⎥
   ⎢sy⋅sin(t)  sy⋅cos(t)   dy⎥
   ⎢                         ⎥
   ⎣    0          0       1 ⎦

   >>> pprint( T * v )
   ⎡dx + sx⋅x⋅cos(t) - sx⋅y⋅sin(t)⎤
   ⎢                              ⎥
   ⎢dy + sy⋅x⋅sin(t) + sy⋅y⋅cos(t)⎥
   ⎢                              ⎥
   ⎣              1               ⎦
#+END_SRC
*** 3D Scene Coordinate Transformations 

In a 3D scene, the following typical transformations happens to the
vertex coordinates of some object to be rendered. The model matrix
transform, $T_m$, converts the object vertex coordinates from the
local-space (a.k.a object-space) to world-space, then the view-matrix,
$T_v$, converts vertex world-space coordinates to the camera-space,
finally the projection matrix transform, $T_p$, turns the camera-space
coordinates into clip-space coordinates (NDC - Normalized Device
Coordinates) within the range -1.0 to 1.0 on each axis. Any NDC
coordinate out of the range -1.0 to 1.0 will not be visible on the
canvas, graphics window screen. 


#+BEGIN_SRC tex                                                       Eye
                +---------+              +---------+ Space      +---------------+  Clip-Space
    Local       |         |  World       |         |  Camera    |               |  (NDC - Coordinates)
   Space        |   Model |  Space       |  View   |  Space     |  Projection   | 
      +------->>+  Matrix +------>>----->+ Matrix  +---------->>+   Matrix      +------->>>
    V           |         |   Vw         |         |   Vc       |               |  Vndc
                | Tm      |              |  Tv     |            |    Tp         |
                +---------+              +---------+            +---------------+


      Tm - (4x4) Model matrix transform 
      Tv - (4x4) View matrix transform 
      Tp - (4x4) Projection matrix transform. 

      V    - Vertex coordinate in local space 
      Vw   - Vertex coordinate in world-space
      Vc   - Vertex coordinate in camera-space coordinate (a.k.a eye-space)
      Vndc - Vertex coordinate in clip-space coordinate (NDC)
#+END_SRC


  + Transform from local-space coordinates to world-space coordinates
    + Where:
      + $T_m$ - is the _model-matrix_, often comprised of rotation,
        scaling and translation concatened canonical transforms. 
      + v - Vertex coordinate in local-space.
        + $v = \begin{bmatrix} x & y & z & 1 \end{bmatrix}^T$ 
      + $v_w$ - Vertex coordinate in world-space
        + $v_w = \begin{bmatrix} x_w & y_w & z_w & 1 \end{bmatrix}^T$

\begin{equation} 
   v_w = T_m \cdot v
\end{equation}


  + Transform from world-space coordinates to camera-space coordinates
    (a.k.a eye space).
    + $T_v$ - is the _view matrix_, computed using the eye vector,
      camera position; up vector - camera orientation; at
      point-vector, point which the camera is looking at.

\begin{equation}
  v_c = T_v \cdot v_w 
\end{equation}

  + Transform from camera-space to clip-space coordinates.
    + Where:
      + $T_p$ - is the _projection matrix_ which can be either the
        _perspective_ projection matrix or _ortographic_ projection
        matrix.
      + $v_{\text{ndc}}$ - is the vertex NDC coordinate. 

\begin{equation}
  v_{\text{ndc}} = T_p \cdot v_c 
\end{equation}

  + Overall scene transform from local-space to clip-space
    coordinates. 

\begin{equation}
  v_{\text{ndc}} = \left( T_p \cdot T_v \cdot T_m \right) v
\end{equation}


 + MVP transform - The overall transform is often designated as
   Model-View-Projection transform ( $T_{mvp}$ ).

\begin{eqnarray*}
   v_{\text{ndc}} &=&  T_{mvp} \cdot v \\
   T_{mvp}        &=&  T_p \cdot T_v \cdot T_m 
\end{eqnarray*}

\begin{equation}
     T_{\text{ModelViewProjection}} =T_{\text{Projection}} \cdot T_{\text{View}} \cdot T_{\text{Model}}
\end{equation}
  
 + The _Model-View transform_ is the product between the model and view
    transforms.

\begin{eqnarray*}
   T_{mv}                    &=& T_v \cdot T_m  \\
   T_{\text{ModelView}} &=& T_{\text{View}} \cdot T_{\text{Model}}
\end{eqnarray*}


  + _Normal transform matrix_ ( $T_{normal}$ ) is used for illumination
    calculations, such as Phong illumination model, and is computed as
    the transpose of model-view matrix inverse. This transform maps
    vertex _normal_ vector in the local-space coordinates to
    camera-space coordinates, also known as eye-space coordinates.

    + Where:
      + n - is the normal vector in local-space coordinates.
        + $n = \begin{bmatrix} x_n & y_n & z_n & 0 \end{bmatrix}^T$
      + $n_c$ is the normal vector in camera-space (eye-space) coordinates. 

\begin{equation}
   T_{normal} = \left[ T_{\text{ModelView}}^{-1}  \right]^T = \left[ ( T_{\text{View}} \cdot T_{\text{Model}}  )^{-1}  \right]^T
\end{equation} 

\begin{equation}
   T_{normal} =  \left[ ( T_{\text{View}} )^{-1} \right]^T  \cdot  \left[ ( T_{\text{Model}} )^{-1}  \right]^T  
\end{equation} 

\begin{equation}
   n_c = T_{normal} \cdot n 
\end{equation} 

  *References* 

    + Chapter 8 - OpenGL – A 3D Graphics API 8.1 - Pipeline architecture
      + <https://people.cs.clemson.edu/~dhouse/courses/817/notes/OpenGL-pipeline.pdf>

    + CS315 Lab 3: 3D Transformations
      + <https://www.cs.uregina.ca/Links/class-info/315/WebGL2/Lab3/>

    + Appendix F - Homogeneous Coordinates and Transformation Matrices
      / OpenGL Programming Guide
      + <https://www.glprogramming.com/red/appendixf.html>

    + GLSL Programming/Vertex Transformations - Wikibooks
      + <https://en.wikibooks.org/wiki/GLSL_Programming/Vertex_Transformations>

    + OpenGL Normal Vector Transformation - Song ho Ahn
      + <http://www.songho.ca/opengl/gl_normaltransform.html>

    + The normal matrix - Light House 3D
      + <https://www.lighthouse3d.com/tutorials/glsl-12-tutorial/the-normal-matrix/>

    + Normal Vector Transform
      + <https://www.cs.upc.edu/~robert/teaching/idi/normalsOpenGL.pdf>
*** 2D Scene Transforms  

A 2D scene can be generated as particular case of a 3D scene by
setting the vertex  Z coordinate to always zero, replacing the view
transform matrix with identity matrix and setting the projection matrix
as the ortographic projection matrix. This approach only works if the
graphics API default coordinate system is the same as the OpenGL one,
where the Y and X axis are on the screen and the Z axis positive
direction goes from the screen towards the viewer (computer user).

\begin{equation}
  v_{\text{ndc}} = \left( T_p \cdot T_v \cdot T_m \right) v
\end{equation}

By following the previous approach for a 2D scene, the vertex
transformation becomes: 

\begin{equation}
  v_{\text{ndc}} = \left( T_{\text{ortographic}} \cdot T_{\text{model}} \right)  
                     \begin{bmatrix} x \\ y \\ 0 \\ 1 \end{bmatrix}
\end{equation}

The model matrix is a combination of translation affine transforms
with z set as zero, scaling affine transforms with Z set as zero and
rotations around Z axis. 

Some matrices that can be used for building the model matrix are: 

  + 2D translation 

\begin{equation}
   T_{\text{translation-2D}} = 
   \begin{bmatrix}
     1   & 0   &  0 &  \Delta_x \\
     0   & 1   &  0 &  \Delta_y \\
     0   & 0   &  1 &   0       \\
     0   & 0   &  0 &   1       \\
   \end{bmatrix}
\end{equation}

  + 2D scaling 

\begin{equation}
  T_{\text{scaling-2D}} =
   \begin{bmatrix}
     s_x   & 0     &  0   &  0 \\
     0     & s_y   &  0   &  0 \\
     0     & 0     &  0   &  0 \\
     0     & 0     &  0   &  1  \\
   \end{bmatrix}
\end{equation}

  + 2D rotation (rotation around Z axis)

\begin{equation}
   T_{\text{rotation-2D}} =
     \begin{bmatrix}
        \cos{\theta}  & -\sin{\theta} &  0 & 0 \\
        \sin{\theta}  &  \cos{\theta} &  0 & 0 \\
       0              & 0             &  1 & 0 \\
       0              & 0             &  0 & 1 \\
     \end{bmatrix}
\end{equation}

*** Rotation Matrix and DCM - Direction Cosine Matrix 

Let A and B be two reference frames in euclidean three dimensional
space $\mathbb{R}^3$ whose orthonormal basis vectors are
$\{\hat{a}_1,\hat{a}_2, \hat{a}_2 \}$ and $\{\hat{b}_1, \hat{b}_2, \hat{b}_2 \}$
where $\hat{a}_1$ corresponds to the X axis in the frame A,
$\hat{a}_2$ corresponds to the Y axis in the frame A, and $\hat{a}_3$
corresponds to Z axis in frame A. The basis vectors satisfy the
following properties, since they are orthonormal vectors, which means
that they are unitary orthogonal (aka - also known as perpendicular)
vectors.

#+BEGIN_EXPORT html
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.2" width="156.86mm" height="88.07mm" viewBox="2084 1565 15686 8807" preserveAspectRatio="xMidYMid" fill-rule="evenodd" stroke-width="28.222" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" xmlns:ooo="http://xml.openoffice.org/svg/export" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:presentation="http://sun.com/xmlns/staroffice/presentation" xmlns:smil="http://www.w3.org/2001/SMIL20/" xmlns:anim="urn:oasis:names:tc:opendocument:xmlns:animation:1.0" xml:space="preserve">
 <defs>
  <font id="EmbeddedFont_1" horiz-adv-x="2048">
   <font-face font-family="Liberation Sans embedded" units-per-em="2048" font-weight="normal" font-style="normal" ascent="1852" descent="423"/>
   <missing-glyph horiz-adv-x="2048" d="M 0,0 L 2047,0 2047,2047 0,2047 0,0 Z"/>
   <glyph unicode="‘" horiz-adv-x="239" d="M 127,952 L 127,1098 C 127,1163 133,1220 146,1269 158,1318 178,1364 207,1409 L 328,1409 C 265,1318 233,1230 233,1147 L 322,1147 322,952 127,952 Z"/>
   <glyph unicode="–" horiz-adv-x="1139" d="M 0,451 L 0,588 1138,588 1138,451 0,451 Z"/>
   <glyph unicode="x" horiz-adv-x="1006" d="M 801,0 L 510,444 217,0 23,0 408,556 41,1082 240,1082 510,661 778,1082 979,1082 612,558 1002,0 801,0 Z"/>
   <glyph unicode="s" horiz-adv-x="901" d="M 950,299 C 950,197 912,118 835,63 758,8 650,-20 511,-20 376,-20 273,2 200,47 127,91 79,160 57,254 L 216,285 C 231,227 263,185 311,158 359,131 426,117 511,117 602,117 669,131 712,159 754,187 775,229 775,285 775,328 760,362 731,389 702,416 654,438 589,455 L 460,489 C 357,516 283,542 240,568 196,593 162,624 137,661 112,698 100,743 100,796 100,895 135,970 206,1022 276,1073 378,1099 513,1099 632,1099 727,1078 798,1036 868,994 912,927 931,834 L 769,814 C 759,862 732,899 689,925 645,950 586,963 513,963 432,963 372,951 333,926 294,901 275,864 275,814 275,783 283,758 299,738 315,718 339,701 370,687 401,673 467,654 568,629 663,605 732,583 774,563 816,542 849,520 874,495 898,470 917,442 930,410 943,377 950,340 950,299 Z"/>
   <glyph unicode="r" horiz-adv-x="530" d="M 142,0 L 142,830 C 142,906 140,990 136,1082 L 306,1082 C 311,959 314,886 314,861 L 318,861 C 347,954 380,1017 417,1051 454,1085 507,1102 575,1102 599,1102 623,1099 648,1092 L 648,927 C 624,934 592,937 552,937 477,937 420,905 381,841 342,776 322,684 322,564 L 322,0 142,0 Z"/>
   <glyph unicode="n" horiz-adv-x="874" d="M 825,0 L 825,686 C 825,757 818,813 804,852 790,891 768,920 737,937 706,954 661,963 602,963 515,963 447,933 397,874 347,815 322,732 322,627 L 322,0 142,0 142,851 C 142,977 140,1054 136,1082 L 306,1082 C 307,1079 307,1070 308,1055 309,1040 310,1024 311,1005 312,986 313,950 314,897 L 317,897 C 358,972 406,1025 461,1056 515,1087 582,1102 663,1102 782,1102 869,1073 924,1014 979,955 1006,857 1006,721 L 1006,0 825,0 Z"/>
   <glyph unicode="m" horiz-adv-x="1457" d="M 768,0 L 768,686 C 768,791 754,863 725,903 696,943 645,963 570,963 493,963 433,934 388,875 343,816 321,734 321,627 L 321,0 142,0 142,851 C 142,977 140,1054 136,1082 L 306,1082 C 307,1079 307,1070 308,1055 309,1040 310,1024 311,1005 312,986 313,950 314,897 L 317,897 C 356,974 400,1027 450,1057 500,1087 561,1102 633,1102 715,1102 780,1086 828,1053 875,1020 908,968 927,897 L 930,897 C 967,970 1013,1022 1066,1054 1119,1086 1183,1102 1258,1102 1367,1102 1447,1072 1497,1013 1546,954 1571,856 1571,721 L 1571,0 1393,0 1393,686 C 1393,791 1379,863 1350,903 1321,943 1270,963 1195,963 1116,963 1055,934 1012,876 968,817 946,734 946,627 L 946,0 768,0 Z"/>
   <glyph unicode="i" horiz-adv-x="187" d="M 137,1312 L 137,1484 317,1484 317,1312 137,1312 Z M 137,0 L 137,1082 317,1082 317,0 137,0 Z"/>
   <glyph unicode="f" horiz-adv-x="557" d="M 361,951 L 361,0 181,0 181,951 29,951 29,1082 181,1082 181,1204 C 181,1303 203,1374 246,1417 289,1460 356,1482 445,1482 495,1482 537,1478 572,1470 L 572,1333 C 542,1338 515,1341 492,1341 446,1341 413,1329 392,1306 371,1283 361,1240 361,1179 L 361,1082 572,1082 572,951 361,951 Z"/>
   <glyph unicode="e" horiz-adv-x="980" d="M 276,503 C 276,379 302,283 353,216 404,149 479,115 578,115 656,115 719,131 766,162 813,193 844,233 861,281 L 1019,236 C 954,65 807,-20 578,-20 418,-20 296,28 213,123 129,218 87,360 87,548 87,727 129,864 213,959 296,1054 416,1102 571,1102 889,1102 1048,910 1048,527 L 1048,503 276,503 Z M 862,641 C 852,755 823,838 775,891 727,943 658,969 568,969 481,969 412,940 361,882 310,823 282,743 278,641 L 862,641 Z"/>
   <glyph unicode="c" horiz-adv-x="901" d="M 275,546 C 275,402 298,295 343,226 388,157 457,122 548,122 612,122 666,139 709,174 752,209 778,262 788,334 L 970,322 C 956,218 912,135 837,73 762,11 668,-20 553,-20 402,-20 286,28 207,124 127,219 87,359 87,542 87,724 127,863 207,959 287,1054 402,1102 551,1102 662,1102 754,1073 827,1016 900,959 945,880 964,779 L 779,765 C 770,825 746,873 708,908 670,943 616,961 546,961 451,961 382,929 339,866 296,803 275,696 275,546 Z"/>
   <glyph unicode="b" horiz-adv-x="953" d="M 1053,546 C 1053,169 920,-20 655,-20 573,-20 505,-5 451,25 396,54 352,102 318,168 L 316,168 C 316,147 315,116 312,74 309,31 307,7 306,0 L 132,0 C 136,36 138,110 138,223 L 138,1484 318,1484 318,1061 C 318,1018 317,967 314,908 L 318,908 C 351,977 396,1027 451,1057 506,1087 574,1102 655,1102 792,1102 892,1056 957,964 1021,872 1053,733 1053,546 Z M 864,540 C 864,691 844,800 804,865 764,930 699,963 609,963 508,963 434,928 388,859 341,790 318,680 318,529 318,387 341,282 386,215 431,147 505,113 607,113 698,113 763,147 804,214 844,281 864,389 864,540 Z"/>
   <glyph unicode="a" horiz-adv-x="1060" d="M 414,-20 C 305,-20 224,9 169,66 114,123 87,202 87,302 87,414 124,500 198,560 271,620 390,652 554,656 L 797,660 797,719 C 797,807 778,870 741,908 704,946 645,965 565,965 484,965 426,951 389,924 352,897 330,853 323,793 L 135,810 C 166,1005 310,1102 569,1102 705,1102 807,1071 876,1009 945,946 979,856 979,738 L 979,272 C 979,219 986,179 1000,152 1014,125 1041,111 1080,111 1097,111 1117,113 1139,118 L 1139,6 C 1094,-5 1047,-10 1000,-10 933,-10 885,8 855,43 824,78 807,132 803,207 L 797,207 C 751,124 698,66 637,32 576,-3 501,-20 414,-20 Z M 455,115 C 521,115 580,130 631,160 682,190 723,231 753,284 782,336 797,390 797,445 L 797,534 600,530 C 515,529 451,520 408,504 364,488 330,463 307,430 284,397 272,353 272,299 272,240 288,195 320,163 351,131 396,115 455,115 Z"/>
   <glyph unicode="Z" horiz-adv-x="1139" d="M 1187,0 L 65,0 65,143 923,1253 138,1253 138,1409 1140,1409 1140,1270 282,156 1187,156 1187,0 Z"/>
   <glyph unicode="Y" horiz-adv-x="1298" d="M 777,584 L 777,0 587,0 587,584 45,1409 255,1409 684,738 1111,1409 1321,1409 777,584 Z"/>
   <glyph unicode="R" horiz-adv-x="1244" d="M 1164,0 L 798,585 359,585 359,0 168,0 168,1409 831,1409 C 990,1409 1112,1374 1199,1303 1285,1232 1328,1133 1328,1006 1328,901 1298,813 1237,742 1176,671 1091,626 984,607 L 1384,0 1164,0 Z M 1136,1004 C 1136,1086 1108,1149 1053,1192 997,1235 917,1256 812,1256 L 359,1256 359,736 820,736 C 921,736 999,760 1054,807 1109,854 1136,919 1136,1004 Z"/>
   <glyph unicode="F" horiz-adv-x="1006" d="M 359,1253 L 359,729 1145,729 1145,571 359,571 359,0 168,0 168,1409 1169,1409 1169,1253 359,1253 Z"/>
   <glyph unicode="C" horiz-adv-x="1324" d="M 792,1274 C 636,1274 515,1224 428,1124 341,1023 298,886 298,711 298,538 343,400 434,295 524,190 646,137 800,137 997,137 1146,235 1245,430 L 1401,352 C 1343,231 1262,138 1157,75 1052,12 930,-20 791,-20 649,-20 526,10 423,69 319,128 240,212 186,322 131,431 104,561 104,711 104,936 165,1112 286,1239 407,1366 575,1430 790,1430 940,1430 1065,1401 1166,1342 1267,1283 1341,1196 1388,1081 L 1207,1021 C 1174,1103 1122,1166 1050,1209 977,1252 891,1274 792,1274 Z"/>
   <glyph unicode="B" horiz-adv-x="1112" d="M 1258,397 C 1258,272 1212,174 1121,105 1030,35 903,0 740,0 L 168,0 168,1409 680,1409 C 1011,1409 1176,1295 1176,1067 1176,984 1153,914 1106,857 1059,800 993,762 908,743 1020,730 1106,692 1167,631 1228,569 1258,491 1258,397 Z M 984,1044 C 984,1120 958,1174 906,1207 854,1240 779,1256 680,1256 L 359,1256 359,810 680,810 C 782,810 858,829 909,868 959,906 984,965 984,1044 Z M 1065,412 C 1065,578 948,661 715,661 L 359,661 359,153 730,153 C 847,153 932,175 985,218 1038,261 1065,326 1065,412 Z"/>
   <glyph unicode="A" horiz-adv-x="1377" d="M 1167,0 L 1006,412 364,412 202,0 4,0 579,1409 796,1409 1362,0 1167,0 Z M 685,1265 L 676,1237 C 659,1182 635,1111 602,1024 L 422,561 949,561 768,1026 C 749,1072 731,1124 712,1182 L 685,1265 Z"/>
   <glyph unicode="3" horiz-adv-x="1006" d="M 1049,389 C 1049,259 1008,158 925,87 842,16 724,-20 571,-20 428,-20 315,12 230,77 145,141 94,236 78,362 L 264,379 C 288,212 390,129 571,129 662,129 733,151 785,196 836,241 862,307 862,395 862,472 833,532 774,575 715,618 629,639 518,639 L 416,639 416,795 514,795 C 613,795 689,817 744,860 798,903 825,962 825,1038 825,1113 803,1173 759,1217 714,1260 648,1282 561,1282 482,1282 418,1262 369,1221 320,1180 291,1123 283,1049 L 102,1063 C 115,1178 163,1268 246,1333 328,1398 434,1430 563,1430 704,1430 814,1397 893,1332 971,1266 1010,1174 1010,1057 1010,967 985,894 935,838 884,781 811,743 715,723 L 715,719 C 820,708 902,672 961,613 1020,554 1049,479 1049,389 Z"/>
   <glyph unicode="2" horiz-adv-x="954" d="M 103,0 L 103,127 C 137,205 179,274 228,334 277,393 328,447 382,496 436,544 490,589 543,630 596,671 643,713 686,754 729,795 763,839 790,884 816,929 829,981 829,1038 829,1115 806,1175 761,1218 716,1261 653,1282 572,1282 495,1282 432,1261 383,1220 333,1178 304,1119 295,1044 L 111,1061 C 124,1174 172,1263 255,1330 337,1397 443,1430 572,1430 714,1430 823,1397 900,1330 976,1263 1014,1167 1014,1044 1014,989 1002,935 977,881 952,827 914,773 865,719 816,665 721,581 582,468 505,405 444,349 399,299 354,248 321,200 301,153 L 1036,153 1036,0 103,0 Z"/>
   <glyph unicode="1" horiz-adv-x="927" d="M 156,0 L 156,153 515,153 515,1237 197,1010 197,1180 530,1409 696,1409 696,153 1039,153 1039,0 156,0 Z"/>
   <glyph unicode=" " horiz-adv-x="556"/>
  </font>
 </defs>
 <defs class="EmbeddedBulletChars">
  <g id="bullet-char-template-57356" transform="scale(0.00048828125,-0.00048828125)">
   <path d="M 580,1141 L 1163,571 580,0 -4,571 580,1141 Z"/>
  </g>
  <g id="bullet-char-template-57354" transform="scale(0.00048828125,-0.00048828125)">
   <path d="M 8,1128 L 1137,1128 1137,0 8,0 8,1128 Z"/>
  </g>
  <g id="bullet-char-template-10146" transform="scale(0.00048828125,-0.00048828125)">
   <path d="M 174,0 L 602,739 174,1481 1456,739 174,0 Z M 1358,739 L 309,1346 659,739 1358,739 Z"/>
  </g>
  <g id="bullet-char-template-10132" transform="scale(0.00048828125,-0.00048828125)">
   <path d="M 2015,739 L 1276,0 717,0 1260,543 174,543 174,936 1260,936 717,1481 1274,1481 2015,739 Z"/>
  </g>
  <g id="bullet-char-template-10007" transform="scale(0.00048828125,-0.00048828125)">
   <path d="M 0,-2 C -7,14 -16,27 -25,37 L 356,567 C 262,823 215,952 215,954 215,979 228,992 255,992 264,992 276,990 289,987 310,991 331,999 354,1012 L 381,999 492,748 772,1049 836,1024 860,1049 C 881,1039 901,1025 922,1006 886,937 835,863 770,784 769,783 710,716 594,584 L 774,223 C 774,196 753,168 711,139 L 727,119 C 717,90 699,76 672,76 641,76 570,178 457,381 L 164,-76 C 142,-110 111,-127 72,-127 30,-127 9,-110 8,-76 1,-67 -2,-52 -2,-32 -2,-23 -1,-13 0,-2 Z"/>
  </g>
  <g id="bullet-char-template-10004" transform="scale(0.00048828125,-0.00048828125)">
   <path d="M 285,-33 C 182,-33 111,30 74,156 52,228 41,333 41,471 41,549 55,616 82,672 116,743 169,778 240,778 293,778 328,747 346,684 L 369,508 C 377,444 397,411 428,410 L 1163,1116 C 1174,1127 1196,1133 1229,1133 1271,1133 1292,1118 1292,1087 L 1292,965 C 1292,929 1282,901 1262,881 L 442,47 C 390,-6 338,-33 285,-33 Z"/>
  </g>
  <g id="bullet-char-template-9679" transform="scale(0.00048828125,-0.00048828125)">
   <path d="M 813,0 C 632,0 489,54 383,161 276,268 223,411 223,592 223,773 276,916 383,1023 489,1130 632,1184 813,1184 992,1184 1136,1130 1245,1023 1353,916 1407,772 1407,592 1407,412 1353,268 1245,161 1136,54 992,0 813,0 Z"/>
  </g>
  <g id="bullet-char-template-8226" transform="scale(0.00048828125,-0.00048828125)">
   <path d="M 346,457 C 273,457 209,483 155,535 101,586 74,649 74,723 74,796 101,859 155,911 209,963 273,989 346,989 419,989 480,963 531,910 582,859 608,796 608,723 608,648 583,586 532,535 482,483 420,457 346,457 Z"/>
  </g>
  <g id="bullet-char-template-8211" transform="scale(0.00048828125,-0.00048828125)">
   <path d="M -4,459 L 1135,459 1135,606 -4,606 -4,459 Z"/>
  </g>
  <g id="bullet-char-template-61548" transform="scale(0.00048828125,-0.00048828125)">
   <path d="M 173,740 C 173,903 231,1043 346,1159 462,1274 601,1332 765,1332 928,1332 1067,1274 1183,1159 1299,1043 1357,903 1357,740 1357,577 1299,437 1183,322 1067,206 928,148 765,148 601,148 462,206 346,322 231,437 173,577 173,740 Z"/>
  </g>
 </defs>
 <g class="Page">
  <g class="com.sun.star.drawing.LineShape">
   <g id="id3">
    <rect class="BoundingBox" stroke="none" fill="none" x="4570" y="3271" width="381" height="3328"/>
    <path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 4760,6571 L 4760,3814"/>
    <path fill="rgb(52,101,164)" stroke="none" d="M 4760,3271 L 4571,3840 4950,3840 4760,3271 Z"/>
   </g>
  </g>
  <g class="com.sun.star.drawing.LineShape">
   <g id="id4">
    <rect class="BoundingBox" stroke="none" fill="none" x="4742" y="6395" width="3919" height="353"/>
    <path fill="none" stroke="rgb(191,0,65)" stroke-width="35" stroke-linejoin="round" d="M 4760,6571 L 8155,6571"/>
    <path fill="rgb(191,0,65)" stroke="none" d="M 8660,6571 L 8132,6395 8132,6747 8660,6571 Z"/>
   </g>
  </g>
  <g class="com.sun.star.drawing.LineShape">
   <g id="id5">
    <rect class="BoundingBox" stroke="none" fill="none" x="2860" y="6553" width="1919" height="1819"/>
    <path fill="none" stroke="rgb(0,169,51)" stroke-width="35" stroke-linejoin="round" d="M 4760,6571 L 3226,8024"/>
    <path fill="rgb(0,169,51)" stroke="none" d="M 2860,8371 L 3364,8136 3122,7880 2860,8371 Z"/>
   </g>
  </g>
  <g class="com.sun.star.drawing.TextShape">
   <g id="id6">
    <rect class="BoundingBox" stroke="none" fill="none" x="2084" y="8509" width="3377" height="963"/>
    <text class="TextShape"><tspan class="TextParagraph" font-family="Liberation Sans, sans-serif" font-size="635px" font-weight="400"><tspan class="TextPosition" x="2334" y="9210"><tspan fill="rgb(0,0,0)" stroke="none">a</tspan><tspan font-size="353px" fill="rgb(0,0,0)" stroke="none">1</tspan><tspan fill="rgb(0,0,0)" stroke="none"> – x axis</tspan></tspan></tspan></text>
   </g>
  </g>
  <g class="com.sun.star.drawing.TextShape">
   <g id="id7">
    <rect class="BoundingBox" stroke="none" fill="none" x="6260" y="5309" width="3458" height="963"/>
    <text class="TextShape"><tspan class="TextParagraph" font-family="Liberation Sans, sans-serif" font-size="635px" font-weight="400"><tspan class="TextPosition" x="6510" y="6010"><tspan fill="rgb(0,0,0)" stroke="none">a</tspan><tspan font-size="353px" fill="rgb(0,0,0)" stroke="none">2</tspan><tspan fill="rgb(0,0,0)" stroke="none"> – Y axis</tspan></tspan></tspan></text>
   </g>
  </g>
  <g class="com.sun.star.drawing.TextShape">
   <g id="id8">
    <rect class="BoundingBox" stroke="none" fill="none" x="4860" y="2671" width="3449" height="963"/>
    <text class="TextShape"><tspan class="TextParagraph" font-family="Liberation Sans, sans-serif" font-size="635px" font-weight="400"><tspan class="TextPosition" x="5110" y="3372"><tspan fill="rgb(0,0,0)" stroke="none">a</tspan><tspan font-size="353px" fill="rgb(0,0,0)" stroke="none">3</tspan><tspan fill="rgb(0,0,0)" stroke="none"> – Z axis</tspan></tspan></tspan></text>
   </g>
  </g>
  <g class="com.sun.star.drawing.LineShape">
   <g id="id9">
    <rect class="BoundingBox" stroke="none" fill="none" x="11160" y="3571" width="1428" height="3328"/>
    <path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 12560,6871 L 11372,4071"/>
    <path fill="rgb(52,101,164)" stroke="none" d="M 11160,3571 L 11208,4168 11556,4020 11160,3571 Z"/>
   </g>
  </g>
  <g class="com.sun.star.drawing.TextShape">
   <g id="id10">
    <rect class="BoundingBox" stroke="none" fill="none" x="11260" y="2671" width="3701" height="1001"/>
    <text class="TextShape"><tspan class="TextParagraph" font-family="Liberation Sans, sans-serif" font-size="635px" font-weight="400"><tspan class="TextPosition" x="11510" y="3372"><tspan fill="rgb(0,0,0)" stroke="none">b</tspan><tspan font-size="353px" fill="rgb(0,0,0)" stroke="none">3</tspan><tspan fill="rgb(0,0,0)" stroke="none"> – ‘Z axis</tspan></tspan></tspan></text>
   </g>
  </g>
  <g class="com.sun.star.drawing.LineShape">
   <g id="id11">
    <rect class="BoundingBox" stroke="none" fill="none" x="12542" y="5371" width="3619" height="1519"/>
    <path fill="none" stroke="rgb(191,0,65)" stroke-width="35" stroke-linejoin="round" d="M 12560,6871 L 15694,5565"/>
    <path fill="rgb(191,0,65)" stroke="none" d="M 16160,5371 L 15605,5412 15740,5737 16160,5371 Z"/>
   </g>
  </g>
  <g class="com.sun.star.drawing.LineShape">
   <g id="id12">
    <rect class="BoundingBox" stroke="none" fill="none" x="12542" y="6853" width="579" height="2319"/>
    <path fill="none" stroke="rgb(0,169,51)" stroke-width="35" stroke-linejoin="round" d="M 12560,6871 L 12953,8678"/>
    <path fill="rgb(0,169,51)" stroke="none" d="M 13060,9171 L 13120,8618 12776,8692 13060,9171 Z"/>
   </g>
  </g>
  <g class="com.sun.star.drawing.TextShape">
   <g id="id13">
    <rect class="BoundingBox" stroke="none" fill="none" x="2084" y="8509" width="3377" height="963"/>
    <text class="TextShape"><tspan class="TextParagraph" font-family="Liberation Sans, sans-serif" font-size="635px" font-weight="400"><tspan class="TextPosition" x="2334" y="9210"><tspan fill="rgb(0,0,0)" stroke="none">a</tspan><tspan font-size="353px" fill="rgb(0,0,0)" stroke="none">1</tspan><tspan fill="rgb(0,0,0)" stroke="none"> – x axis</tspan></tspan></tspan></text>
   </g>
  </g>
  <g class="com.sun.star.drawing.TextShape">
   <g id="id14">
    <rect class="BoundingBox" stroke="none" fill="none" x="11360" y="9409" width="3517" height="963"/>
    <text class="TextShape"><tspan class="TextParagraph" font-family="Liberation Sans, sans-serif" font-size="635px" font-weight="400"><tspan class="TextPosition" x="11610" y="10110"><tspan fill="rgb(0,0,0)" stroke="none">b</tspan><tspan font-size="353px" fill="rgb(0,0,0)" stroke="none">1</tspan><tspan fill="rgb(0,0,0)" stroke="none"> – ‘x axis</tspan></tspan></tspan></text>
   </g>
  </g>
  <g class="com.sun.star.drawing.TextShape">
   <g id="id15">
    <rect class="BoundingBox" stroke="none" fill="none" x="14160" y="4371" width="3610" height="963"/>
    <text class="TextShape"><tspan class="TextParagraph" font-family="Liberation Sans, sans-serif" font-size="635px" font-weight="400"><tspan class="TextPosition" x="14410" y="5072"><tspan fill="rgb(0,0,0)" stroke="none">b</tspan><tspan font-size="353px" fill="rgb(0,0,0)" stroke="none">2</tspan><tspan fill="rgb(0,0,0)" stroke="none"> – ‘Y axis</tspan></tspan></tspan></text>
   </g>
  </g>
  <g class="com.sun.star.drawing.TextShape">
   <g id="id16">
    <rect class="BoundingBox" stroke="none" fill="none" x="3217" y="1609" width="4910" height="807"/>
    <text class="TextShape"><tspan class="TextParagraph" font-family="Liberation Sans, sans-serif" font-size="494px" font-weight="400"><tspan class="TextPosition" x="3467" y="2183"><tspan fill="rgb(0,0,0)" stroke="none">Reference Frame A </tspan></tspan></tspan></text>
   </g>
  </g>
  <g class="com.sun.star.drawing.TextShape">
   <g id="id17">
    <rect class="BoundingBox" stroke="none" fill="none" x="10917" y="1565" width="4965" height="807"/>
    <text class="TextShape"><tspan class="TextParagraph" font-family="Liberation Sans, sans-serif" font-size="494px" font-weight="400"><tspan class="TextPosition" x="11167" y="2139"><tspan fill="rgb(0,0,0)" stroke="none">Reference Frame B </tspan></tspan></tspan></text>
   </g>
  </g>
  <g class="com.sun.star.drawing.CustomShape">
   <g id="id18">
    <rect class="BoundingBox" stroke="none" fill="none" x="4616" y="6470" width="304" height="304"/>
    <path fill="rgb(114,159,207)" stroke="none" d="M 4918,6622 C 4918,6648 4911,6674 4898,6697 4885,6720 4866,6739 4843,6752 4820,6765 4794,6772 4768,6772 4741,6772 4715,6765 4692,6752 4669,6739 4650,6720 4637,6697 4624,6674 4617,6648 4617,6622 4617,6595 4624,6569 4637,6546 4650,6523 4669,6504 4692,6491 4715,6478 4741,6471 4768,6471 4794,6471 4820,6478 4843,6491 4866,6504 4885,6523 4898,6546 4911,6569 4918,6595 4918,6622 L 4918,6622 Z"/>
    <path fill="none" stroke="rgb(52,101,164)" d="M 4918,6622 C 4918,6648 4911,6674 4898,6697 4885,6720 4866,6739 4843,6752 4820,6765 4794,6772 4768,6772 4741,6772 4715,6765 4692,6752 4669,6739 4650,6720 4637,6697 4624,6674 4617,6648 4617,6622 4617,6595 4624,6569 4637,6546 4650,6523 4669,6504 4692,6491 4715,6478 4741,6471 4768,6471 4794,6471 4820,6478 4843,6491 4866,6504 4885,6523 4898,6546 4911,6569 4918,6595 4918,6622 Z"/>
   </g>
  </g>
  <g class="com.sun.star.drawing.TextShape">
   <g id="id19">
    <rect class="BoundingBox" stroke="none" fill="none" x="3820" y="6009" width="960" height="963"/>
    <text class="TextShape"><tspan class="TextParagraph" font-family="Liberation Sans, sans-serif" font-size="635px" font-weight="400"><tspan class="TextPosition" x="4070" y="6710"><tspan fill="rgb(0,0,0)" stroke="none">C</tspan></tspan></tspan></text>
   </g>
  </g>
  <g class="com.sun.star.drawing.TextShape">
   <g id="id20">
    <rect class="BoundingBox" stroke="none" fill="none" x="11317" y="6409" width="960" height="963"/>
    <text class="TextShape"><tspan class="TextParagraph" font-family="Liberation Sans, sans-serif" font-size="635px" font-weight="400"><tspan class="TextPosition" x="11567" y="7110"><tspan fill="rgb(0,0,0)" stroke="none">C</tspan></tspan></tspan></text>
   </g>
  </g>
 </g>
</svg>
#+END_EXPORT 


\begin{eqnarray*}
   \| \hat{a}_i \| &=& 1 \quad \| \hat{b}_i \| = 1 \\
   \hat{a}_i \cdot \hat{a}_j &=&  0 \quad \text{if }  i \neq j   \\ 
   \hat{b}_i \cdot \hat{b}_j &=&  0 \quad \text{if } i \neq j   \\ 
\end{eqnarray*}

A DCM - Direction Cosine Matrix, sometimes also called Direct Cosine
Matrix, is a matrix that describes the attitude, also known as
orientation, of a reference frame with respect to another reference
frame. In this case $\text{DCM}_{BA}$ is the matrix that describes the
orientation of a _rotated reference frame_ B with respect to reference frame A, in
other words, the orientation of frame B relative to frame A, as shown
below where the matrix $\text{DCM}_{BA} = c_{ij}$ is expresses the basis vectors of
frame B in terms of basis vectors of frame A.

\begin{eqnarray*}
     \hat{b}_1 &=&  c_{11} \hat{a}_1 + c_{12} \hat{a}_2 + c_{13} \hat{a}_3 \\
     \hat{b}_2 &=&  c_{21} \hat{a}_1 + c_{22} \hat{a}_2 + c_{23} \hat{a}_3 \\
     \hat{b}_2 &=&  c_{31} \hat{a}_1 + c_{32} \hat{a}_2 + c_{33} \hat{a}_3 \\
\end{eqnarray*}

It is noticiable that $c_{ij} = \hat{b}_i \cdot \hat{a}_j$, for instance

\begin{equation}
   c_{23} =  \hat{b}_2 \cdot \hat{a}_3 = (c_{21} \hat{a}_1 + c_{22} \hat{a}_2 + c_{23} \hat{a}_3 ) \cdot \hat{a}_3
\end{equation}

\begin{equation}
   c_{23} =  c_{21} (\hat{a}_1 \cdot \hat{a}_3) + c_{22} (\hat{a}_2 \cdot \hat{a}_3) + c_{23} (\hat{a}_3  \cdot \hat{a}_3)
\end{equation}

\begin{equation}
   c_{23} =  c_{23} 
\end{equation}

By expressing the dot product $\hat{b}_i \cdot \hat{a}_j$ in terms of
the cosine between vector angles, it is possible to find the next relation.

\begin{equation} 
   c_{ij} = \hat{b}_i \cdot \hat{a}_j = \| \hat{b}_i \| \| \hat{a}_j \| \cos(\hat{b}_i, \hat{a}_j)
\end{equation}


\begin{equation} 
   \boxed{ c_{ij} =  \hat{b}_i \cdot \hat{a}_j = \cos(\hat{b}_i, \hat{a}_j) = \cos \theta_{ij} }
\end{equation}

Where: 

 + $\cos(\hat{b}_i, \hat{a}_j)$ is the cosine of the angle between
  the vectors  $\hat{b}_i$ and $\hat{a}_j)$.

 + $\theta_{ij}$ is the angle between vectros $\hat{b}_i$ and $\hat{a}_j$ .


So, the previous expression in matrix form becomes:

\begin{equation}
  \begin{pmatrix} \hat{b}_1 \\ \hat{b}_2  \\ \hat{b}_3 \end{pmatrix} = 
  
  \begin{pmatrix} 
     c_{11} & c_{12} & c_{13} \\
     c_{21} & c_{22} & c_{23} \\ 
     c_{31} & c_{32} & c_{33} \\
  \end{pmatrix}

  \begin{pmatrix} \hat{a}_1 \\ \hat{a}_2 \\ \hat{a}_3 \end{pmatrix}  
\end{equation}


So, it can be found that the matrix  $\text{DCM}_{BA}$ is given by the
following expression where the first formula expresses the matrix in
terms of coefficients $c_{ij}$, the second formula expresses the
matrix in terms of the dot product between reference frames basis
vectors; the third states is defined in terms of the cosine between
basis vectors of reference frames A and B.


\begin{equation}
   \text{DCM}_{BA}  =  \begin{pmatrix}  
                            c_{11} & c_{12} & c_{13} \\
                            c_{21} & c_{22} & c_{23} \\ 
                            c_{31} & c_{32} & c_{33} \\
                        \end{pmatrix}
\end{equation}

\begin{equation}
   \text{DCM}_{BA}  =  \begin{pmatrix}  
                           \\  \hat{b}_1 \cdot \hat{a}_1  &  \hat{b}_1 \cdot \hat{a}_2 &  \hat{b}_1 \cdot \hat{a}_3
                           \\  \hat{b}_2 \cdot \hat{a}_1  &  \hat{b}_2 \cdot \hat{a}_2 &  \hat{b}_2 \cdot \hat{a}_3
                           \\  \hat{b}_3 \cdot \hat{a}_1  &  \hat{b}_3 \cdot \hat{a}_2 &  \hat{b}_3 \cdot \hat{a}_3
                       \end{pmatrix}

                    = \begin{pmatrix}
                          ^A\hat{b}_1^T \\ ^A\hat{b}_2^T  \\ ^A\hat{b}_3^T 
                      \end{pmatrix}
\end{equation}

\begin{equation}
   \text{DCM}_{BA}  =  \begin{pmatrix}  
                           \\  \cos( \hat{b}_1, \hat{a}_1)  &  \cos(\hat{b}_1, \hat{a}_2) &  \cos(\hat{b}_1, \hat{a}_3) 
                           \\  \cos( \hat{b}_2, \hat{a}_1)  &  \cos(\hat{b}_2, \hat{a}_2) &  \cos(\hat{b}_2, \hat{a}_3)
                           \\  \cos( \hat{b}_3, \hat{a}_1)  &  \cos(\hat{b}_3, \hat{a}_2) &  \cos(\hat{b}_3, \hat{a}_3)
                       \end{pmatrix}
\end{equation}


The  $\text{DCM}_{BA}$ could also be expressed in more concise way in
this form.

\begin{equation}
  \boxed{
     \text{DCM}_{BA}
                    = \begin{pmatrix}
                          ^A\hat{b}_1^T \\ ^A\hat{b}_2^T  \\ ^A\hat{b}_3^T 
                      \end{pmatrix}
  }
\end{equation}


Where:

  + $^A\hat{b}_1^T$ is the transpose of frame A coordinates of vector $\hat{b}_1$. 
   In other words, it is projection of $\hat{b}_1$ onto all basis
   vectors of reference frame A. Note: all vectors are assumed to be column vectors by default.

  + $^A\hat{b}_2^T$ is the transpose of frame A coordinates of vector $\hat{b}_2$. 

  + $^A\hat{b}_3^T$ is the transpose of frame A coordinates of vector $\hat{b}_3$. 


_Coordinate Transformation between frames_ - A point-vector
$\boldsymbol{r}$ representing a position in the space can be expressed
in both frames A and B as linear combination of basis vectors.


\begin{equation} 
   \boldsymbol{r} = r_1 \hat{a}_1 + r_2 \hat{a}_2 + r_3 \hat{a}_3 
                  = r_1' \hat{b}_1 + r_2' \hat{b}_2 + r_3' \hat{b}_3
\end{equation}

The coordinates of the vector $\boldsymbol{r}$ in referece frame B can
be found by taking the dot product between the vector and each basis vectors
of frame B.

\begin{eqnarray*}
    \\ r_1' &=& \hat{b}_1 \cdot \boldsymbol{r} = \hat{b}_1 \cdot (r_1 \hat{a}_1 + r_2 \hat{a}_2 + r_3 \hat{a}_3 )
    \\ r_2' &=& \hat{b}_2 \cdot \boldsymbol{r} = \hat{b}_2 \cdot (r_1 \hat{a}_1 + r_2 \hat{a}_2 + r_3 \hat{a}_3 )
    \\ r_3' &=& \hat{b}_3 \cdot \boldsymbol{r} = \hat{b}_3 \cdot (r_1 \hat{a}_1 + r_2 \hat{a}_2 + r_3 \hat{a}_3 )
\end{eqnarray*}



So, the it can be found that the previous expression can be reduced to
the following expression and the matrix $\text{DCM}_{BA}$ is the rotation matrix
${}^{B}_{A}R = \text{DCM}_{BA}$ that maps coordinates from frame A to
frame B.

\begin{equation}
   {}^B\boldsymbol{r} = \text{DCM}_{BA} {}^A\boldsymbol{r}
\end{equation}

Where:

  +  $^B\boldsymbol{r}$ - is the coordinate of position vector in frame B (rotated frame)
  +  $^A\boldsymbol{r}$ - is the coordinate of position vector in frame A.


_Opposite Transformation_  - It is often desirable to find the rotation
matrix that maps coordinates from roted frame B to the frame A. It
can be acomplished by taking the dot product between the previous
position vector $\boldsymbol{r}$ and each basis vector of frame A. 

\begin{eqnarray*}
    \\ r_1 &=& \hat{a}_1 \cdot \boldsymbol{r} = \hat{a}_1 \cdot (r_1' \hat{b}_1 + r_2' \hat{b}_2 + r_3' \hat{b}_3 )
    \\ r_2 &=& \hat{a}_2 \cdot \boldsymbol{r} = \hat{a}_2 \cdot (r_1' \hat{b}_1 + r_2' \hat{b}_2 + r_3' \hat{b}_3 )
    \\ r_3 &=& \hat{a}_3 \cdot \boldsymbol{r} = \hat{a}_3 \cdot (r_1' \hat{b}_1 + r_2' \hat{b}_2 + r_3' \hat{b}_3 )
\end{eqnarray*}

The previous expression can be simplified as: 


\begin{equation}
  \begin{pmatrix} r_1 \\ r_2 \\ r_3 \end{pmatrix} = 
  
  \begin{pmatrix} 
      \\ \hat{a}_1  \cdot \hat{b}_1  &  \hat{a}_1  \cdot \hat{b}_2 &  \hat{a}_1  \cdot \hat{b}_3 
      \\ \hat{a}_2  \cdot \hat{b}_1  &  \hat{a}_2  \cdot \hat{b}_2 &  \hat{a}_2  \cdot \hat{b}_3 
      \\ \hat{a}_3  \cdot \hat{b}_1  &  \hat{a}_3  \cdot \hat{b}_2 &  \hat{a}_3  \cdot \hat{b}_3 
  \end{pmatrix}

  \begin{pmatrix} r_1' \\ r_2' \\ r_3' \end{pmatrix}  
\end{equation}

It is the same as a multiplication by the transpose of the direct
cosine matrix.

\begin{equation}
   {}^A\boldsymbol{r} = (\text{DCM}_{BA})^T {}^A\boldsymbol{r}
\end{equation}

So, it is possible to find that the rotation matrix that maps
coordinates from frame B to frame A is the tranpose of the DCM -
direct cosine matrix $\text{DCM}_{BA}$.

\begin{equation}
     \boxed{ {}^{A}_{B}R  = \text{DCM}_{BA}^T  = \text{DCM}_{AB} }
\end{equation}


The rotation matrix ${}^{A}_{B}R$ can be quick determined using the
following formula where each column of the rotation matrix is a basis
vector of the rotated frame B expressed in terms of basis vectors of
frame A. For instance, the first column of this rotation matrix is a
column vector that contains the coordinates of basis vector
$\hat{b}_1$ in reference frame A, which is the same as the projection
of this basis vector onto each basis vectors of frame A.

\begin{equation}
  \boxed{
    {}^{A}_{B}R = \begin{pmatrix} 
                      ^A\hat{b}_1 & ^A\hat{b}_2 & ^A\hat{b}_3
                  \end{pmatrix}
   }
\end{equation}


 *Example - Rotation around Z axis* 


Given two frames A, whose orthornmal basis vectors are ${a_1, a_2, a_3}$, 
and B whose basis vectors are ${b_1, b_2, b_3}$. The reference
frame A is static (innertial reference frame) and the reference frame
B is the frame A rotated counterclockwise around the Z axis by an angle $\theta$. 

#+BEGIN_EXPORT html
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.2" width="170.84mm" height="114.93mm" viewBox="1527 13977 17084 11493" preserveAspectRatio="xMidYMid" fill-rule="evenodd" stroke-width="28.222" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" xmlns:ooo="http://xml.openoffice.org/svg/export" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:presentation="http://sun.com/xmlns/staroffice/presentation" xmlns:smil="http://www.w3.org/2001/SMIL20/" xmlns:anim="urn:oasis:names:tc:opendocument:xmlns:animation:1.0" xml:space="preserve">
 <defs>
  <font id="EmbeddedFont_1" horiz-adv-x="2048">
   <font-face font-family="Liberation Sans embedded" units-per-em="2048" font-weight="normal" font-style="normal" ascent="1852" descent="423"/>
   <missing-glyph horiz-adv-x="2048" d="M 0,0 L 2047,0 2047,2047 0,2047 0,0 Z"/>
   <glyph unicode="‘" horiz-adv-x="239" d="M 127,952 L 127,1098 C 127,1163 133,1220 146,1269 158,1318 178,1364 207,1409 L 328,1409 C 265,1318 233,1230 233,1147 L 322,1147 322,952 127,952 Z"/>
   <glyph unicode="–" horiz-adv-x="1139" d="M 0,451 L 0,588 1138,588 1138,451 0,451 Z"/>
   <glyph unicode="θ" horiz-adv-x="954" d="M 1033,733 C 1033,479 993,290 913,166 833,42 717,-20 565,-20 416,-20 302,44 224,171 145,298 106,485 106,733 106,1233 261,1483 571,1483 730,1483 847,1422 922,1299 996,1176 1033,987 1033,733 Z M 563,113 C 658,113 728,159 772,250 816,341 840,482 843,673 L 296,673 C 299,489 323,350 366,255 409,160 474,113 563,113 Z M 574,1354 C 479,1354 410,1310 367,1221 324,1132 300,993 296,804 L 843,804 C 840,993 817,1132 776,1221 734,1310 667,1354 574,1354 Z"/>
   <glyph unicode="z" horiz-adv-x="848" d="M 83,0 L 83,137 688,943 117,943 117,1082 901,1082 901,945 295,139 922,139 922,0 83,0 Z"/>
   <glyph unicode="x" horiz-adv-x="1006" d="M 801,0 L 510,444 217,0 23,0 408,556 41,1082 240,1082 510,661 778,1082 979,1082 612,558 1002,0 801,0 Z"/>
   <glyph unicode="u" horiz-adv-x="874" d="M 314,1082 L 314,396 C 314,325 321,269 335,230 349,191 371,162 402,145 433,128 478,119 537,119 624,119 692,149 742,208 792,267 817,350 817,455 L 817,1082 997,1082 997,231 C 997,105 999,28 1003,0 L 833,0 C 832,3 832,12 831,27 830,42 830,59 829,78 828,97 826,132 825,185 L 822,185 C 781,110 733,58 679,27 624,-4 557,-20 476,-20 357,-20 271,10 216,69 161,128 133,225 133,361 L 133,1082 314,1082 Z"/>
   <glyph unicode="t" horiz-adv-x="531" d="M 554,8 C 495,-8 434,-16 372,-16 228,-16 156,66 156,229 L 156,951 31,951 31,1082 163,1082 216,1324 336,1324 336,1082 536,1082 536,951 336,951 336,268 C 336,216 345,180 362,159 379,138 408,127 450,127 474,127 509,132 554,141 L 554,8 Z"/>
   <glyph unicode="s" horiz-adv-x="901" d="M 950,299 C 950,197 912,118 835,63 758,8 650,-20 511,-20 376,-20 273,2 200,47 127,91 79,160 57,254 L 216,285 C 231,227 263,185 311,158 359,131 426,117 511,117 602,117 669,131 712,159 754,187 775,229 775,285 775,328 760,362 731,389 702,416 654,438 589,455 L 460,489 C 357,516 283,542 240,568 196,593 162,624 137,661 112,698 100,743 100,796 100,895 135,970 206,1022 276,1073 378,1099 513,1099 632,1099 727,1078 798,1036 868,994 912,927 931,834 L 769,814 C 759,862 732,899 689,925 645,950 586,963 513,963 432,963 372,951 333,926 294,901 275,864 275,814 275,783 283,758 299,738 315,718 339,701 370,687 401,673 467,654 568,629 663,605 732,583 774,563 816,542 849,520 874,495 898,470 917,442 930,410 943,377 950,340 950,299 Z"/>
   <glyph unicode="r" horiz-adv-x="530" d="M 142,0 L 142,830 C 142,906 140,990 136,1082 L 306,1082 C 311,959 314,886 314,861 L 318,861 C 347,954 380,1017 417,1051 454,1085 507,1102 575,1102 599,1102 623,1099 648,1092 L 648,927 C 624,934 592,937 552,937 477,937 420,905 381,841 342,776 322,684 322,564 L 322,0 142,0 Z"/>
   <glyph unicode="p" horiz-adv-x="953" d="M 1053,546 C 1053,169 920,-20 655,-20 488,-20 376,43 319,168 L 314,168 C 317,163 318,106 318,-2 L 318,-425 138,-425 138,861 C 138,972 136,1046 132,1082 L 306,1082 C 307,1079 308,1070 309,1054 310,1037 312,1012 314,978 315,944 316,921 316,908 L 320,908 C 352,975 394,1024 447,1055 500,1086 569,1101 655,1101 788,1101 888,1056 954,967 1020,878 1053,737 1053,546 Z M 864,542 C 864,693 844,800 803,865 762,930 698,962 609,962 538,962 482,947 442,917 401,887 371,840 350,777 329,713 318,630 318,528 318,386 341,281 386,214 431,147 505,113 607,113 696,113 762,146 803,212 844,277 864,387 864,542 Z"/>
   <glyph unicode="o" horiz-adv-x="980" d="M 1053,542 C 1053,353 1011,212 928,119 845,26 724,-20 565,-20 407,-20 288,28 207,125 126,221 86,360 86,542 86,915 248,1102 571,1102 736,1102 858,1057 936,966 1014,875 1053,733 1053,542 Z M 864,542 C 864,691 842,800 798,868 753,935 679,969 574,969 469,969 393,935 346,866 299,797 275,689 275,542 275,399 298,292 345,221 391,149 464,113 563,113 671,113 748,148 795,217 841,286 864,395 864,542 Z"/>
   <glyph unicode="n" horiz-adv-x="874" d="M 825,0 L 825,686 C 825,757 818,813 804,852 790,891 768,920 737,937 706,954 661,963 602,963 515,963 447,933 397,874 347,815 322,732 322,627 L 322,0 142,0 142,851 C 142,977 140,1054 136,1082 L 306,1082 C 307,1079 307,1070 308,1055 309,1040 310,1024 311,1005 312,986 313,950 314,897 L 317,897 C 358,972 406,1025 461,1056 515,1087 582,1102 663,1102 782,1102 869,1073 924,1014 979,955 1006,857 1006,721 L 1006,0 825,0 Z"/>
   <glyph unicode="m" horiz-adv-x="1457" d="M 768,0 L 768,686 C 768,791 754,863 725,903 696,943 645,963 570,963 493,963 433,934 388,875 343,816 321,734 321,627 L 321,0 142,0 142,851 C 142,977 140,1054 136,1082 L 306,1082 C 307,1079 307,1070 308,1055 309,1040 310,1024 311,1005 312,986 313,950 314,897 L 317,897 C 356,974 400,1027 450,1057 500,1087 561,1102 633,1102 715,1102 780,1086 828,1053 875,1020 908,968 927,897 L 930,897 C 967,970 1013,1022 1066,1054 1119,1086 1183,1102 1258,1102 1367,1102 1447,1072 1497,1013 1546,954 1571,856 1571,721 L 1571,0 1393,0 1393,686 C 1393,791 1379,863 1350,903 1321,943 1270,963 1195,963 1116,963 1055,934 1012,876 968,817 946,734 946,627 L 946,0 768,0 Z"/>
   <glyph unicode="l" horiz-adv-x="187" d="M 138,0 L 138,1484 318,1484 318,0 138,0 Z"/>
   <glyph unicode="j" horiz-adv-x="372" d="M 137,1312 L 137,1484 317,1484 317,1312 137,1312 Z M 317,-134 C 317,-236 297,-310 257,-356 217,-402 157,-425 77,-425 26,-425 -17,-422 -50,-416 L -50,-277 12,-283 C 58,-283 90,-271 109,-247 128,-223 137,-176 137,-107 L 137,1082 317,1082 317,-134 Z"/>
   <glyph unicode="i" horiz-adv-x="187" d="M 137,1312 L 137,1484 317,1484 317,1312 137,1312 Z M 137,0 L 137,1082 317,1082 317,0 137,0 Z"/>
   <glyph unicode="h" horiz-adv-x="874" d="M 317,897 C 356,968 402,1020 457,1053 511,1086 580,1102 663,1102 780,1102 867,1073 923,1015 978,956 1006,858 1006,721 L 1006,0 825,0 825,686 C 825,762 818,819 804,856 790,893 767,920 735,937 703,954 659,963 602,963 517,963 450,934 399,875 348,816 322,737 322,638 L 322,0 142,0 142,1484 322,1484 322,1098 C 322,1057 321,1015 319,972 316,929 315,904 314,897 L 317,897 Z"/>
   <glyph unicode="g" horiz-adv-x="927" d="M 548,-425 C 430,-425 336,-402 266,-356 196,-309 151,-243 131,-158 L 312,-132 C 324,-182 351,-220 392,-248 433,-274 486,-288 553,-288 732,-288 822,-183 822,27 L 822,201 820,201 C 786,132 739,80 680,45 621,10 551,-8 472,-8 339,-8 242,36 180,124 117,212 86,350 86,539 86,730 120,872 187,963 254,1054 355,1099 492,1099 569,1099 635,1082 692,1047 748,1012 791,962 822,897 L 824,897 C 824,917 825,952 828,1001 831,1050 833,1077 836,1082 L 1007,1082 C 1003,1046 1001,971 1001,858 L 1001,31 C 1001,-273 850,-425 548,-425 Z M 822,541 C 822,629 810,705 786,769 762,832 728,881 685,915 641,948 591,965 536,965 444,965 377,932 335,865 293,798 272,690 272,541 272,393 292,287 331,222 370,157 438,125 533,125 590,125 640,142 684,175 728,208 762,256 786,319 810,381 822,455 822,541 Z"/>
   <glyph unicode="f" horiz-adv-x="557" d="M 361,951 L 361,0 181,0 181,951 29,951 29,1082 181,1082 181,1204 C 181,1303 203,1374 246,1417 289,1460 356,1482 445,1482 495,1482 537,1478 572,1470 L 572,1333 C 542,1338 515,1341 492,1341 446,1341 413,1329 392,1306 371,1283 361,1240 361,1179 L 361,1082 572,1082 572,951 361,951 Z"/>
   <glyph unicode="e" horiz-adv-x="980" d="M 276,503 C 276,379 302,283 353,216 404,149 479,115 578,115 656,115 719,131 766,162 813,193 844,233 861,281 L 1019,236 C 954,65 807,-20 578,-20 418,-20 296,28 213,123 129,218 87,360 87,548 87,727 129,864 213,959 296,1054 416,1102 571,1102 889,1102 1048,910 1048,527 L 1048,503 276,503 Z M 862,641 C 852,755 823,838 775,891 727,943 658,969 568,969 481,969 412,940 361,882 310,823 282,743 278,641 L 862,641 Z"/>
   <glyph unicode="d" horiz-adv-x="927" d="M 821,174 C 788,105 744,55 689,25 634,-5 565,-20 484,-20 347,-20 247,26 183,118 118,210 86,349 86,536 86,913 219,1102 484,1102 566,1102 634,1087 689,1057 744,1027 788,979 821,914 L 823,914 821,1035 821,1484 1001,1484 1001,223 C 1001,110 1003,36 1007,0 L 835,0 C 833,11 831,35 829,74 826,113 825,146 825,174 L 821,174 Z M 275,542 C 275,391 295,282 335,217 375,152 440,119 530,119 632,119 706,154 752,225 798,296 821,405 821,554 821,697 798,802 752,869 706,936 633,969 532,969 441,969 376,936 336,869 295,802 275,693 275,542 Z"/>
   <glyph unicode="c" horiz-adv-x="901" d="M 275,546 C 275,402 298,295 343,226 388,157 457,122 548,122 612,122 666,139 709,174 752,209 778,262 788,334 L 970,322 C 956,218 912,135 837,73 762,11 668,-20 553,-20 402,-20 286,28 207,124 127,219 87,359 87,542 87,724 127,863 207,959 287,1054 402,1102 551,1102 662,1102 754,1073 827,1016 900,959 945,880 964,779 L 779,765 C 770,825 746,873 708,908 670,943 616,961 546,961 451,961 382,929 339,866 296,803 275,696 275,546 Z"/>
   <glyph unicode="b" horiz-adv-x="953" d="M 1053,546 C 1053,169 920,-20 655,-20 573,-20 505,-5 451,25 396,54 352,102 318,168 L 316,168 C 316,147 315,116 312,74 309,31 307,7 306,0 L 132,0 C 136,36 138,110 138,223 L 138,1484 318,1484 318,1061 C 318,1018 317,967 314,908 L 318,908 C 351,977 396,1027 451,1057 506,1087 574,1102 655,1102 792,1102 892,1056 957,964 1021,872 1053,733 1053,546 Z M 864,540 C 864,691 844,800 804,865 764,930 699,963 609,963 508,963 434,928 388,859 341,790 318,680 318,529 318,387 341,282 386,215 431,147 505,113 607,113 698,113 763,147 804,214 844,281 864,389 864,540 Z"/>
   <glyph unicode="a" horiz-adv-x="1060" d="M 414,-20 C 305,-20 224,9 169,66 114,123 87,202 87,302 87,414 124,500 198,560 271,620 390,652 554,656 L 797,660 797,719 C 797,807 778,870 741,908 704,946 645,965 565,965 484,965 426,951 389,924 352,897 330,853 323,793 L 135,810 C 166,1005 310,1102 569,1102 705,1102 807,1071 876,1009 945,946 979,856 979,738 L 979,272 C 979,219 986,179 1000,152 1014,125 1041,111 1080,111 1097,111 1117,113 1139,118 L 1139,6 C 1094,-5 1047,-10 1000,-10 933,-10 885,8 855,43 824,78 807,132 803,207 L 797,207 C 751,124 698,66 637,32 576,-3 501,-20 414,-20 Z M 455,115 C 521,115 580,130 631,160 682,190 723,231 753,284 782,336 797,390 797,445 L 797,534 600,530 C 515,529 451,520 408,504 364,488 330,463 307,430 284,397 272,353 272,299 272,240 288,195 320,163 351,131 396,115 455,115 Z"/>
   <glyph unicode="Z" horiz-adv-x="1139" d="M 1187,0 L 65,0 65,143 923,1253 138,1253 138,1409 1140,1409 1140,1270 282,156 1187,156 1187,0 Z"/>
   <glyph unicode="Y" horiz-adv-x="1298" d="M 777,584 L 777,0 587,0 587,584 45,1409 255,1409 684,738 1111,1409 1321,1409 777,584 Z"/>
   <glyph unicode="X" horiz-adv-x="1298" d="M 1112,0 L 689,616 257,0 46,0 582,732 87,1409 298,1409 690,856 1071,1409 1282,1409 800,739 1323,0 1112,0 Z"/>
   <glyph unicode="T" horiz-adv-x="1192" d="M 720,1253 L 720,0 530,0 530,1253 46,1253 46,1409 1204,1409 1204,1253 720,1253 Z"/>
   <glyph unicode="R" horiz-adv-x="1244" d="M 1164,0 L 798,585 359,585 359,0 168,0 168,1409 831,1409 C 990,1409 1112,1374 1199,1303 1285,1232 1328,1133 1328,1006 1328,901 1298,813 1237,742 1176,671 1091,626 984,607 L 1384,0 1164,0 Z M 1136,1004 C 1136,1086 1108,1149 1053,1192 997,1235 917,1256 812,1256 L 359,1256 359,736 820,736 C 921,736 999,760 1054,807 1109,854 1136,919 1136,1004 Z"/>
   <glyph unicode="P" horiz-adv-x="1112" d="M 1258,985 C 1258,852 1215,746 1128,667 1041,588 922,549 773,549 L 359,549 359,0 168,0 168,1409 761,1409 C 919,1409 1041,1372 1128,1298 1215,1224 1258,1120 1258,985 Z M 1066,983 C 1066,1165 957,1256 738,1256 L 359,1256 359,700 746,700 C 959,700 1066,794 1066,983 Z"/>
   <glyph unicode="A" horiz-adv-x="1377" d="M 1167,0 L 1006,412 364,412 202,0 4,0 579,1409 796,1409 1362,0 1167,0 Z M 685,1265 L 676,1237 C 659,1182 635,1111 602,1024 L 422,561 949,561 768,1026 C 749,1072 731,1124 712,1182 L 685,1265 Z"/>
   <glyph unicode="3" horiz-adv-x="1006" d="M 1049,389 C 1049,259 1008,158 925,87 842,16 724,-20 571,-20 428,-20 315,12 230,77 145,141 94,236 78,362 L 264,379 C 288,212 390,129 571,129 662,129 733,151 785,196 836,241 862,307 862,395 862,472 833,532 774,575 715,618 629,639 518,639 L 416,639 416,795 514,795 C 613,795 689,817 744,860 798,903 825,962 825,1038 825,1113 803,1173 759,1217 714,1260 648,1282 561,1282 482,1282 418,1262 369,1221 320,1180 291,1123 283,1049 L 102,1063 C 115,1178 163,1268 246,1333 328,1398 434,1430 563,1430 704,1430 814,1397 893,1332 971,1266 1010,1174 1010,1057 1010,967 985,894 935,838 884,781 811,743 715,723 L 715,719 C 820,708 902,672 961,613 1020,554 1049,479 1049,389 Z"/>
   <glyph unicode="2" horiz-adv-x="954" d="M 103,0 L 103,127 C 137,205 179,274 228,334 277,393 328,447 382,496 436,544 490,589 543,630 596,671 643,713 686,754 729,795 763,839 790,884 816,929 829,981 829,1038 829,1115 806,1175 761,1218 716,1261 653,1282 572,1282 495,1282 432,1261 383,1220 333,1178 304,1119 295,1044 L 111,1061 C 124,1174 172,1263 255,1330 337,1397 443,1430 572,1430 714,1430 823,1397 900,1330 976,1263 1014,1167 1014,1044 1014,989 1002,935 977,881 952,827 914,773 865,719 816,665 721,581 582,468 505,405 444,349 399,299 354,248 321,200 301,153 L 1036,153 1036,0 103,0 Z"/>
   <glyph unicode="1" horiz-adv-x="927" d="M 156,0 L 156,153 515,153 515,1237 197,1010 197,1180 530,1409 696,1409 696,153 1039,153 1039,0 156,0 Z"/>
   <glyph unicode="," horiz-adv-x="239" d="M 385,219 L 385,51 C 385,-20 379,-79 366,-126 353,-173 334,-219 307,-262 L 184,-262 C 247,-171 278,-84 278,0 L 190,0 190,219 385,219 Z"/>
   <glyph unicode=")" horiz-adv-x="557" d="M 555,528 C 555,335 525,162 465,9 404,-144 311,-289 186,-424 L 12,-424 C 137,-284 229,-136 287,19 345,174 374,344 374,530 374,716 345,887 287,1042 228,1197 137,1345 12,1484 L 186,1484 C 312,1348 405,1203 465,1050 525,896 555,723 555,532 L 555,528 Z"/>
   <glyph unicode="(" horiz-adv-x="583" d="M 127,532 C 127,725 157,898 218,1051 278,1204 371,1349 496,1484 L 670,1484 C 545,1345 454,1198 396,1042 337,886 308,715 308,530 308,345 337,175 395,20 452,-135 544,-283 670,-424 L 496,-424 C 370,-288 277,-143 217,11 157,164 127,337 127,528 L 127,532 Z"/>
   <glyph unicode=" " horiz-adv-x="556"/>
  </font>
 </defs>
 <defs class="EmbeddedBulletChars">
  <g id="bullet-char-template-57356" transform="scale(0.00048828125,-0.00048828125)">
   <path d="M 580,1141 L 1163,571 580,0 -4,571 580,1141 Z"/>
  </g>
  <g id="bullet-char-template-57354" transform="scale(0.00048828125,-0.00048828125)">
   <path d="M 8,1128 L 1137,1128 1137,0 8,0 8,1128 Z"/>
  </g>
  <g id="bullet-char-template-10146" transform="scale(0.00048828125,-0.00048828125)">
   <path d="M 174,0 L 602,739 174,1481 1456,739 174,0 Z M 1358,739 L 309,1346 659,739 1358,739 Z"/>
  </g>
  <g id="bullet-char-template-10132" transform="scale(0.00048828125,-0.00048828125)">
   <path d="M 2015,739 L 1276,0 717,0 1260,543 174,543 174,936 1260,936 717,1481 1274,1481 2015,739 Z"/>
  </g>
  <g id="bullet-char-template-10007" transform="scale(0.00048828125,-0.00048828125)">
   <path d="M 0,-2 C -7,14 -16,27 -25,37 L 356,567 C 262,823 215,952 215,954 215,979 228,992 255,992 264,992 276,990 289,987 310,991 331,999 354,1012 L 381,999 492,748 772,1049 836,1024 860,1049 C 881,1039 901,1025 922,1006 886,937 835,863 770,784 769,783 710,716 594,584 L 774,223 C 774,196 753,168 711,139 L 727,119 C 717,90 699,76 672,76 641,76 570,178 457,381 L 164,-76 C 142,-110 111,-127 72,-127 30,-127 9,-110 8,-76 1,-67 -2,-52 -2,-32 -2,-23 -1,-13 0,-2 Z"/>
  </g>
  <g id="bullet-char-template-10004" transform="scale(0.00048828125,-0.00048828125)">
   <path d="M 285,-33 C 182,-33 111,30 74,156 52,228 41,333 41,471 41,549 55,616 82,672 116,743 169,778 240,778 293,778 328,747 346,684 L 369,508 C 377,444 397,411 428,410 L 1163,1116 C 1174,1127 1196,1133 1229,1133 1271,1133 1292,1118 1292,1087 L 1292,965 C 1292,929 1282,901 1262,881 L 442,47 C 390,-6 338,-33 285,-33 Z"/>
  </g>
  <g id="bullet-char-template-9679" transform="scale(0.00048828125,-0.00048828125)">
   <path d="M 813,0 C 632,0 489,54 383,161 276,268 223,411 223,592 223,773 276,916 383,1023 489,1130 632,1184 813,1184 992,1184 1136,1130 1245,1023 1353,916 1407,772 1407,592 1407,412 1353,268 1245,161 1136,54 992,0 813,0 Z"/>
  </g>
  <g id="bullet-char-template-8226" transform="scale(0.00048828125,-0.00048828125)">
   <path d="M 346,457 C 273,457 209,483 155,535 101,586 74,649 74,723 74,796 101,859 155,911 209,963 273,989 346,989 419,989 480,963 531,910 582,859 608,796 608,723 608,648 583,586 532,535 482,483 420,457 346,457 Z"/>
  </g>
  <g id="bullet-char-template-8211" transform="scale(0.00048828125,-0.00048828125)">
   <path d="M -4,459 L 1135,459 1135,606 -4,606 -4,459 Z"/>
  </g>
  <g id="bullet-char-template-61548" transform="scale(0.00048828125,-0.00048828125)">
   <path d="M 173,740 C 173,903 231,1043 346,1159 462,1274 601,1332 765,1332 928,1332 1067,1274 1183,1159 1299,1043 1357,903 1357,740 1357,577 1299,437 1183,322 1067,206 928,148 765,148 601,148 462,206 346,322 231,437 173,577 173,740 Z"/>
  </g>
 </defs>
 <g class="Page">
  <g class="com.sun.star.drawing.LineShape">
   <g id="id3">
    <rect class="BoundingBox" stroke="none" fill="none" x="3482" y="20896" width="2429" height="3928"/>
    <path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 5883,20923 L 3768,24360"/>
    <path fill="rgb(52,101,164)" stroke="none" d="M 3483,24823 L 3942,24438 3620,24240 3483,24823 Z"/>
   </g>
  </g>
  <g class="Group">
   <g class="com.sun.star.drawing.LineShape">
    <g id="id4">
     <rect class="BoundingBox" stroke="none" fill="none" x="5678" y="15223" width="423" height="5742"/>
     <path fill="none" stroke="rgb(191,0,65)" stroke-width="81" stroke-linejoin="round" d="M 5934,20923 L 5888,15826"/>
     <path fill="rgb(191,0,65)" stroke="none" d="M 5883,15223 L 5678,15856 6099,15853 5883,15223 Z"/>
    </g>
   </g>
   <g class="com.sun.star.drawing.LineShape">
    <g id="id5">
     <rect class="BoundingBox" stroke="none" fill="none" x="5842" y="20712" width="6842" height="423"/>
     <path fill="none" stroke="rgb(0,169,51)" stroke-width="81" stroke-linejoin="round" d="M 5883,20923 L 12080,20923"/>
     <path fill="rgb(0,169,51)" stroke="none" d="M 12683,20923 L 12052,20713 12052,21134 12683,20923 Z"/>
    </g>
   </g>
  </g>
  <g class="Group">
   <g class="com.sun.star.drawing.LineShape">
    <g id="id6">
     <rect class="BoundingBox" stroke="none" fill="none" x="2210" y="16570" width="3767" height="4358"/>
     <path fill="none" stroke="rgb(191,0,65)" stroke-width="81" stroke-linejoin="round" d="M 5935,20886 L 2605,17027"/>
     <path fill="rgb(191,0,65)" stroke="none" d="M 2210,16570 L 2464,17186 2782,16911 2210,16570 Z"/>
    </g>
   </g>
   <g class="com.sun.star.drawing.LineShape">
    <g id="id7">
     <rect class="BoundingBox" stroke="none" fill="none" x="5855" y="16522" width="5230" height="4439"/>
     <path fill="none" stroke="rgb(0,169,51)" stroke-width="81" stroke-linejoin="round" d="M 5896,20919 L 10623,16913"/>
     <path fill="rgb(0,169,51)" stroke="none" d="M 11083,16523 L 10465,16770 10738,17092 11083,16523 Z"/>
    </g>
   </g>
  </g>
  <g class="com.sun.star.drawing.TextShape">
   <g id="id8">
    <rect class="BoundingBox" stroke="none" fill="none" x="4372" y="23138" width="10764" height="1822"/>
    <text class="TextShape"><tspan class="TextParagraph" font-family="Liberation Sans, sans-serif" font-size="353px" font-weight="400"><tspan class="TextPosition" x="4622" y="23839"><tspan fill="rgb(0,0,0)" stroke="none">a3, b3</tspan><tspan font-size="635px" fill="rgb(0,0,0)" stroke="none"> –</tspan><tspan font-size="423px" fill="rgb(0,0,0)" stroke="none"> z axis (orthogonal to screen)</tspan></tspan></tspan><tspan class="TextParagraph" font-family="Liberation Sans, sans-serif" font-size="423px" font-weight="400"><tspan class="TextPosition" x="4622" y="24359"><tspan fill="rgb(0,0,0)" stroke="none">This axis goes outside of screen plane</tspan></tspan></tspan></text>
   </g>
  </g>
  <g class="com.sun.star.drawing.TextShape">
   <g id="id9">
    <rect class="BoundingBox" stroke="none" fill="none" x="9157" y="21210" width="2239" height="646"/>
    <text class="TextShape"><tspan class="TextParagraph" font-family="Liberation Sans, sans-serif" font-size="353px" font-weight="400"><tspan class="TextPosition" x="9407" y="21653"><tspan fill="rgb(0,0,0)" stroke="none">a1 – X axis</tspan></tspan></tspan></text>
   </g>
  </g>
  <g class="com.sun.star.drawing.TextShape">
   <g id="id10">
    <rect class="BoundingBox" stroke="none" fill="none" x="9631" y="17785" width="2239" height="646"/>
    <text class="TextShape"><tspan class="TextParagraph" font-family="Liberation Sans, sans-serif" font-size="353px" font-weight="400"><tspan class="TextPosition" x="9881" y="18228"><tspan fill="rgb(0,0,0)" stroke="none">b1 – X axis</tspan></tspan></tspan></text>
   </g>
  </g>
  <g class="com.sun.star.drawing.TextShape">
   <g id="id11">
    <rect class="BoundingBox" stroke="none" fill="none" x="6106" y="15161" width="2230" height="646"/>
    <text class="TextShape"><tspan class="TextParagraph" font-family="Liberation Sans, sans-serif" font-size="353px" font-weight="400"><tspan class="TextPosition" x="6356" y="15604"><tspan fill="rgb(0,0,0)" stroke="none">a2 – Y axis</tspan></tspan></tspan></text>
   </g>
  </g>
  <g class="com.sun.star.drawing.TextShape">
   <g id="id12">
    <rect class="BoundingBox" stroke="none" fill="none" x="2216" y="16181" width="2311" height="646"/>
    <text class="TextShape"><tspan class="TextParagraph" font-family="Liberation Sans, sans-serif" font-size="353px" font-weight="400"><tspan class="TextPosition" x="2466" y="16624"><tspan fill="rgb(0,0,0)" stroke="none">b2 – ‘Y axis</tspan></tspan></tspan></text>
   </g>
  </g>
  <g class="com.sun.star.drawing.EllipseShape">
   <g id="id13">
    <rect class="BoundingBox" stroke="none" fill="none" x="6864" y="20074" width="429" height="841"/>
    <path fill="none" stroke="rgb(51,51,51)" stroke-width="18" stroke-linejoin="round" d="M 6874,20083 C 6973,20159 7057,20248 7122,20348 7228,20508 7283,20689 7283,20873 7283,20883 7283,20894 7282,20904"/>
   </g>
  </g>
  <g class="com.sun.star.drawing.TextShape">
   <g id="id14">
    <rect class="BoundingBox" stroke="none" fill="none" x="7083" y="20023" width="801" height="807"/>
    <text class="TextShape"><tspan class="TextParagraph" font-family="Liberation Sans, sans-serif" font-size="423px" font-weight="400"><tspan class="TextPosition" x="7333" y="20533"><tspan fill="rgb(0,0,0)" stroke="none">θ</tspan></tspan></tspan></text>
   </g>
  </g>
  <g class="com.sun.star.drawing.LineShape">
   <g id="id15">
    <rect class="BoundingBox" stroke="none" fill="none" x="6850" y="20060" width="299" height="86"/>
    <path fill="none" stroke="rgb(17,17,17)" stroke-width="35" stroke-linejoin="round" d="M 6868,20078 L 7130,20127"/>
   </g>
  </g>
  <g class="com.sun.star.drawing.LineShape">
   <g id="id16">
    <rect class="BoundingBox" stroke="none" fill="none" x="6850" y="20061" width="81" height="239"/>
    <path fill="none" stroke="rgb(17,17,17)" stroke-width="35" stroke-linejoin="round" d="M 6868,20079 L 6912,20281"/>
   </g>
  </g>
  <g class="com.sun.star.drawing.LineShape">
   <g id="id17">
    <rect class="BoundingBox" stroke="none" fill="none" x="5936" y="18423" width="2870" height="2458"/>
    <path fill="none" stroke="rgb(28,28,28)" d="M 5937,20879 L 8478,18703"/>
    <path fill="rgb(28,28,28)" stroke="none" d="M 8805,18423 L 8366,18602 8561,18830 8805,18423 Z"/>
   </g>
  </g>
  <g class="com.sun.star.drawing.MeasureShape">
   <g id="id18">
    <rect class="BoundingBox" stroke="none" fill="none" x="8830" y="18466" width="924" height="2434"/>
    <path fill="none" stroke="rgb(0,0,0)" d="M 9549,18760 L 9533,20609"/>
    <path fill="rgb(0,0,0)" stroke="none" d="M 9552,18473 L 9449,18772 9649,18774 9552,18473 Z"/>
    <path fill="rgb(0,0,0)" stroke="none" d="M 9531,20896 L 9634,20597 9434,20595 9531,20896 Z"/>
    <path fill="none" stroke="rgb(0,0,0)" d="M 8852,18467 L 9752,18475"/>
    <path fill="none" stroke="rgb(0,0,0)" d="M 8831,20890 L 9731,20898"/>
    <text class="TextShape" transform="rotate(-89.5 9475 20058)"><tspan class="TextParagraph" font-family="Liberation Sans, sans-serif" font-size="353px" font-weight="400"><tspan class="TextPosition" x="9475" y="20058"><tspan fill="rgb(0,0,0)" stroke="none">sin </tspan></tspan><tspan class="TextPosition" x="9480" y="19512"><tspan fill="rgb(0,0,0)" stroke="none">θ</tspan></tspan></tspan></text>
   </g>
  </g>
  <g class="com.sun.star.drawing.MeasureShape">
   <g id="id19">
    <rect class="BoundingBox" stroke="none" fill="none" x="5517" y="18040" width="3252" height="2793"/>
    <path fill="none" stroke="rgb(0,0,0)" d="M 5867,20410 L 8349,18377"/>
    <path fill="rgb(0,0,0)" stroke="none" d="M 5645,20592 L 5940,20479 5814,20324 5645,20592 Z"/>
    <path fill="rgb(0,0,0)" stroke="none" d="M 8571,18196 L 8275,18308 8402,18463 8571,18196 Z"/>
    <path fill="none" stroke="rgb(0,0,0)" d="M 5841,20831 L 5518,20437"/>
    <path fill="none" stroke="rgb(0,0,0)" d="M 8767,18435 L 8444,18041"/>
    <text class="TextShape" transform="rotate(-39.3 6997 19420)"><tspan class="TextParagraph" font-family="Liberation Sans, sans-serif" font-size="353px" font-weight="400"><tspan class="TextPosition" x="6997" y="19420"><tspan fill="rgb(0,0,0)" stroke="none">1</tspan></tspan></tspan></text>
   </g>
  </g>
  <g class="com.sun.star.drawing.MeasureShape">
   <g id="id20">
    <rect class="BoundingBox" stroke="none" fill="none" x="5925" y="20903" width="2865" height="914"/>
    <path fill="none" stroke="rgb(0,0,0)" d="M 8500,21605 L 6216,21614"/>
    <path fill="rgb(0,0,0)" stroke="none" d="M 8787,21604 L 8487,21505 8487,21705 8787,21604 Z"/>
    <path fill="rgb(0,0,0)" stroke="none" d="M 5929,21615 L 6229,21714 6229,21514 5929,21615 Z"/>
    <path fill="none" stroke="rgb(0,0,0)" d="M 8784,20904 L 8788,21804"/>
    <path fill="none" stroke="rgb(0,0,0)" d="M 5926,20915 L 5930,21815"/>
    <text class="TextShape" transform="rotate(-0.2 6936 21548)"><tspan class="TextParagraph" font-family="Liberation Sans, sans-serif" font-size="353px" font-weight="400"><tspan class="TextPosition" x="6936" y="21548"><tspan fill="rgb(0,0,0)" stroke="none">cos </tspan></tspan><tspan class="TextPosition" x="7584" y="21546"><tspan fill="rgb(0,0,0)" stroke="none">θ</tspan></tspan></tspan></text>
   </g>
  </g>
  <g class="com.sun.star.drawing.LineShape">
   <g id="id21">
    <rect class="BoundingBox" stroke="none" fill="none" x="8774" y="18505" width="3" height="2398"/>
    <path fill="none" stroke="rgb(28,28,28)" d="M 8775,18506 L 8775,18668"/>
    <path fill="none" stroke="rgb(28,28,28)" d="M 8775,18749 L 8775,18910"/>
    <path fill="none" stroke="rgb(28,28,28)" d="M 8775,18991 L 8775,19153"/>
    <path fill="none" stroke="rgb(28,28,28)" d="M 8775,19234 L 8775,19395"/>
    <path fill="none" stroke="rgb(28,28,28)" d="M 8775,19476 L 8775,19638"/>
    <path fill="none" stroke="rgb(28,28,28)" d="M 8775,19719 L 8775,19880"/>
    <path fill="none" stroke="rgb(28,28,28)" d="M 8775,19961 L 8775,20123"/>
    <path fill="none" stroke="rgb(28,28,28)" d="M 8775,20204 L 8775,20366"/>
    <path fill="none" stroke="rgb(28,28,28)" d="M 8775,20446 L 8775,20608"/>
    <path fill="none" stroke="rgb(28,28,28)" d="M 8775,20689 L 8775,20851"/>
   </g>
  </g>
  <g class="com.sun.star.drawing.LineShape">
   <g id="id22">
    <rect class="BoundingBox" stroke="none" fill="none" x="3544" y="18114" width="2395" height="2768"/>
    <path fill="none" stroke="rgb(28,28,28)" d="M 5937,20880 L 3825,18439"/>
    <path fill="rgb(28,28,28)" stroke="none" d="M 3544,18114 L 3725,18552 3952,18356 3544,18114 Z"/>
   </g>
  </g>
  <g class="com.sun.star.drawing.LineShape">
   <g id="id23">
    <rect class="BoundingBox" stroke="none" fill="none" x="2021" y="18174" width="3881" height="47"/>
    <path fill="none" stroke="rgb(28,28,28)" d="M 2022,18175 L 2184,18177"/>
    <path fill="none" stroke="rgb(28,28,28)" d="M 2265,18178 L 2426,18180"/>
    <path fill="none" stroke="rgb(28,28,28)" d="M 2507,18181 L 2669,18182"/>
    <path fill="none" stroke="rgb(28,28,28)" d="M 2750,18183 L 2911,18185"/>
    <path fill="none" stroke="rgb(28,28,28)" d="M 2992,18186 L 3154,18188"/>
    <path fill="none" stroke="rgb(28,28,28)" d="M 3235,18189 L 3396,18191"/>
    <path fill="none" stroke="rgb(28,28,28)" d="M 3477,18192 L 3639,18193"/>
    <path fill="none" stroke="rgb(28,28,28)" d="M 3720,18194 L 3881,18196"/>
    <path fill="none" stroke="rgb(28,28,28)" d="M 3962,18197 L 4124,18199"/>
    <path fill="none" stroke="rgb(28,28,28)" d="M 4205,18200 L 4366,18202"/>
    <path fill="none" stroke="rgb(28,28,28)" d="M 4447,18203 L 4609,18204"/>
    <path fill="none" stroke="rgb(28,28,28)" d="M 4690,18205 L 4852,18207"/>
    <path fill="none" stroke="rgb(28,28,28)" d="M 4932,18208 L 5094,18210"/>
    <path fill="none" stroke="rgb(28,28,28)" d="M 5175,18211 L 5337,18213"/>
    <path fill="none" stroke="rgb(28,28,28)" d="M 5417,18214 L 5579,18215"/>
    <path fill="none" stroke="rgb(28,28,28)" d="M 5660,18216 L 5822,18218"/>
   </g>
  </g>
  <g class="com.sun.star.drawing.EllipseShape">
   <g id="id24">
    <rect class="BoundingBox" stroke="none" fill="none" x="5257" y="19817" width="683" height="277"/>
    <path fill="none" stroke="rgb(51,51,51)" stroke-width="18" stroke-linejoin="round" d="M 5267,20084 C 5319,20040 5375,20001 5434,19967 5585,19881 5755,19833 5929,19827"/>
   </g>
  </g>
  <g class="com.sun.star.drawing.LineShape">
   <g id="id25">
    <rect class="BoundingBox" stroke="none" fill="none" x="5243" y="19929" width="97" height="169"/>
    <path fill="none" stroke="rgb(17,17,17)" stroke-width="35" stroke-linejoin="round" d="M 5261,20079 L 5321,19947"/>
   </g>
  </g>
  <g class="com.sun.star.drawing.LineShape">
   <g id="id26">
    <rect class="BoundingBox" stroke="none" fill="none" x="5243" y="20061" width="173" height="51"/>
    <path fill="none" stroke="rgb(17,17,17)" stroke-width="35" stroke-linejoin="round" d="M 5261,20079 L 5397,20093"/>
   </g>
  </g>
  <g class="com.sun.star.drawing.TextShape">
   <g id="id27">
    <rect class="BoundingBox" stroke="none" fill="none" x="5244" y="19335" width="801" height="807"/>
    <text class="TextShape"><tspan class="TextParagraph" font-family="Liberation Sans, sans-serif" font-size="423px" font-weight="400"><tspan class="TextPosition" x="5494" y="19845"><tspan fill="rgb(0,0,0)" stroke="none">θ</tspan></tspan></tspan></text>
   </g>
  </g>
  <g class="com.sun.star.drawing.MeasureShape">
   <g id="id28">
    <rect class="BoundingBox" stroke="none" fill="none" x="2859" y="18297" width="3008" height="3244"/>
    <path fill="none" stroke="rgb(0,0,0)" d="M 5150,21191 L 3200,18976"/>
    <path fill="rgb(0,0,0)" stroke="none" d="M 5339,21407 L 5216,21115 5066,21247 5339,21407 Z"/>
    <path fill="rgb(0,0,0)" stroke="none" d="M 3010,18761 L 3134,19052 3284,18920 3010,18761 Z"/>
    <path fill="none" stroke="rgb(0,0,0)" d="M 5865,20944 L 5189,21539"/>
    <path fill="none" stroke="rgb(0,0,0)" d="M 3536,18298 L 2860,18893"/>
    <text class="TextShape" transform="rotate(-131.3 4206 20194)"><tspan class="TextParagraph" font-family="Liberation Sans, sans-serif" font-size="353px" font-weight="400"><tspan class="TextPosition" x="4206" y="20194"><tspan fill="rgb(0,0,0)" stroke="none">1</tspan></tspan></tspan></text>
   </g>
  </g>
  <g class="com.sun.star.drawing.MeasureShape">
   <g id="id29">
    <rect class="BoundingBox" stroke="none" fill="none" x="3615" y="17090" width="2326" height="1016"/>
    <path fill="none" stroke="rgb(0,0,0)" d="M 3905,17398 L 5652,17403"/>
    <path fill="rgb(0,0,0)" stroke="none" d="M 3618,17397 L 3918,17498 3919,17298 3618,17397 Z"/>
    <path fill="rgb(0,0,0)" stroke="none" d="M 5938,17404 L 5639,17303 5638,17503 5938,17404 Z"/>
    <path fill="none" stroke="rgb(0,0,0)" d="M 3616,18097 L 3619,17197"/>
    <path fill="none" stroke="rgb(0,0,0)" d="M 5936,18104 L 5939,17204"/>
    <text class="TextShape" transform="rotate(0.1 4412 17346)"><tspan class="TextParagraph" font-family="Liberation Sans, sans-serif" font-size="353px" font-weight="400"><tspan class="TextPosition" x="4412" y="17346"><tspan fill="rgb(0,0,0)" stroke="none">sin </tspan></tspan><tspan class="TextPosition" x="4958" y="17348"><tspan fill="rgb(0,0,0)" stroke="none">θ</tspan></tspan></tspan></text>
   </g>
  </g>
  <g class="com.sun.star.drawing.LineShape">
   <g id="id30">
    <rect class="BoundingBox" stroke="none" fill="none" x="2021" y="20894" width="4034" height="49"/>
    <path fill="none" stroke="rgb(28,28,28)" d="M 2022,20895 L 2184,20897"/>
    <path fill="none" stroke="rgb(28,28,28)" d="M 2265,20898 L 2426,20900"/>
    <path fill="none" stroke="rgb(28,28,28)" d="M 2507,20901 L 2669,20902"/>
    <path fill="none" stroke="rgb(28,28,28)" d="M 2750,20903 L 2911,20905"/>
    <path fill="none" stroke="rgb(28,28,28)" d="M 2992,20906 L 3154,20908"/>
    <path fill="none" stroke="rgb(28,28,28)" d="M 3235,20909 L 3396,20911"/>
    <path fill="none" stroke="rgb(28,28,28)" d="M 3477,20912 L 3639,20913"/>
    <path fill="none" stroke="rgb(28,28,28)" d="M 3720,20914 L 3881,20916"/>
    <path fill="none" stroke="rgb(28,28,28)" d="M 3962,20917 L 4124,20919"/>
    <path fill="none" stroke="rgb(28,28,28)" d="M 4205,20920 L 4366,20922"/>
    <path fill="none" stroke="rgb(28,28,28)" d="M 4447,20923 L 4609,20925"/>
    <path fill="none" stroke="rgb(28,28,28)" d="M 4690,20925 L 4852,20927"/>
    <path fill="none" stroke="rgb(28,28,28)" d="M 4932,20928 L 5094,20930"/>
    <path fill="none" stroke="rgb(28,28,28)" d="M 5175,20931 L 5337,20933"/>
    <path fill="none" stroke="rgb(28,28,28)" d="M 5417,20934 L 5579,20936"/>
    <path fill="none" stroke="rgb(28,28,28)" d="M 5660,20937 L 5822,20938"/>
    <path fill="none" stroke="rgb(28,28,28)" d="M 5903,20939 L 6053,20941"/>
   </g>
  </g>
  <g class="com.sun.star.drawing.MeasureShape">
   <g id="id31">
    <rect class="BoundingBox" stroke="none" fill="none" x="1986" y="18164" width="915" height="2749"/>
    <path fill="none" stroke="rgb(0,0,0)" d="M 2698,18455 L 2688,20624"/>
    <path fill="rgb(0,0,0)" stroke="none" d="M 2699,18169 L 2598,18468 2798,18469 2699,18169 Z"/>
    <path fill="rgb(0,0,0)" stroke="none" d="M 2687,20911 L 2788,20611 2588,20610 2687,20911 Z"/>
    <path fill="none" stroke="rgb(0,0,0)" d="M 1999,18165 L 2899,18169"/>
    <path fill="none" stroke="rgb(0,0,0)" d="M 1987,20907 L 2887,20911"/>
   </g>
  </g>
  <g class="com.sun.star.drawing.TextShape">
   <g id="id32">
    <rect class="BoundingBox" stroke="none" fill="none" x="4479" y="18589" width="1049" height="963"/>
    <text class="TextShape"><tspan class="TextParagraph" font-family="Liberation Sans, sans-serif" font-size="353px" font-weight="400"><tspan class="TextPosition" x="4729" y="19290"><tspan font-size="635px" fill="rgb(0,0,0)" stroke="none">b</tspan><tspan fill="rgb(0,0,0)" stroke="none">2</tspan></tspan></tspan></text>
   </g>
  </g>
  <g class="com.sun.star.drawing.TextShape">
   <g id="id33">
    <rect class="BoundingBox" stroke="none" fill="none" x="7597" y="18873" width="1049" height="963"/>
    <text class="TextShape"><tspan class="TextParagraph" font-family="Liberation Sans, sans-serif" font-size="353px" font-weight="400"><tspan class="TextPosition" x="7847" y="19574"><tspan font-size="635px" fill="rgb(0,0,0)" stroke="none">b</tspan><tspan fill="rgb(0,0,0)" stroke="none">1</tspan></tspan></tspan></text>
   </g>
  </g>
  <g class="com.sun.star.drawing.TextShape">
   <g id="id34">
    <rect class="BoundingBox" stroke="none" fill="none" x="3194" y="13977" width="6878" height="963"/>
    <text class="TextShape"><tspan class="TextParagraph" font-family="Liberation Sans, sans-serif" font-size="635px" font-weight="400"><tspan class="TextPosition" x="3444" y="14678"><tspan fill="rgb(0,0,0)" stroke="none">Rotation around Z axis</tspan></tspan></tspan></text>
   </g>
  </g>
  <g class="com.sun.star.drawing.EllipseShape">
   <g id="id35">
    <rect class="BoundingBox" stroke="none" fill="none" x="14174" y="23727" width="429" height="841"/>
    <path fill="none" stroke="rgb(51,51,51)" stroke-width="18" stroke-linejoin="round" d="M 14184,23736 C 14283,23812 14367,23901 14432,24001 14538,24161 14593,24342 14593,24526 14593,24536 14593,24547 14592,24557"/>
   </g>
  </g>
  <g class="com.sun.star.drawing.TextShape">
   <g id="id36">
    <rect class="BoundingBox" stroke="none" fill="none" x="14393" y="23676" width="801" height="807"/>
    <text class="TextShape"><tspan class="TextParagraph" font-family="Liberation Sans, sans-serif" font-size="423px" font-weight="400"><tspan class="TextPosition" x="14643" y="24186"><tspan fill="rgb(0,0,0)" stroke="none">θ</tspan></tspan></tspan></text>
   </g>
  </g>
  <g class="com.sun.star.drawing.LineShape">
   <g id="id37">
    <rect class="BoundingBox" stroke="none" fill="none" x="14160" y="23713" width="299" height="86"/>
    <path fill="none" stroke="rgb(17,17,17)" stroke-width="35" stroke-linejoin="round" d="M 14178,23731 L 14440,23780"/>
   </g>
  </g>
  <g class="com.sun.star.drawing.LineShape">
   <g id="id38">
    <rect class="BoundingBox" stroke="none" fill="none" x="14160" y="23714" width="81" height="239"/>
    <path fill="none" stroke="rgb(17,17,17)" stroke-width="35" stroke-linejoin="round" d="M 14178,23732 L 14222,23934"/>
   </g>
  </g>
  <g class="com.sun.star.drawing.LineShape">
   <g id="id39">
    <rect class="BoundingBox" stroke="none" fill="none" x="13246" y="22076" width="2870" height="2458"/>
    <path fill="none" stroke="rgb(28,28,28)" d="M 13247,24532 L 15788,22356"/>
    <path fill="rgb(28,28,28)" stroke="none" d="M 16115,22076 L 15676,22255 15871,22483 16115,22076 Z"/>
   </g>
  </g>
  <g class="com.sun.star.drawing.MeasureShape">
   <g id="id40">
    <rect class="BoundingBox" stroke="none" fill="none" x="16140" y="22119" width="924" height="2434"/>
    <path fill="none" stroke="rgb(0,0,0)" d="M 16859,22413 L 16843,24262"/>
    <path fill="rgb(0,0,0)" stroke="none" d="M 16862,22126 L 16759,22425 16959,22427 16862,22126 Z"/>
    <path fill="rgb(0,0,0)" stroke="none" d="M 16841,24549 L 16944,24250 16744,24248 16841,24549 Z"/>
    <path fill="none" stroke="rgb(0,0,0)" d="M 16162,22120 L 17062,22128"/>
    <path fill="none" stroke="rgb(0,0,0)" d="M 16141,24543 L 17041,24551"/>
   </g>
  </g>
  <g class="com.sun.star.drawing.MeasureShape">
   <g id="id41">
    <rect class="BoundingBox" stroke="none" fill="none" x="13235" y="24556" width="2865" height="914"/>
    <path fill="none" stroke="rgb(0,0,0)" d="M 15810,25258 L 13526,25267"/>
    <path fill="rgb(0,0,0)" stroke="none" d="M 16097,25257 L 15797,25158 15797,25358 16097,25257 Z"/>
    <path fill="rgb(0,0,0)" stroke="none" d="M 13239,25268 L 13539,25367 13539,25167 13239,25268 Z"/>
    <path fill="none" stroke="rgb(0,0,0)" d="M 16094,24557 L 16098,25457"/>
    <path fill="none" stroke="rgb(0,0,0)" d="M 13236,24568 L 13240,25468"/>
    <text class="TextShape" transform="rotate(-0.2 14246 25201)"><tspan class="TextParagraph" font-family="Liberation Sans, sans-serif" font-size="353px" font-weight="400"><tspan class="TextPosition" x="14246" y="25201"><tspan fill="rgb(0,0,0)" stroke="none">cos </tspan></tspan><tspan class="TextPosition" x="14894" y="25199"><tspan fill="rgb(0,0,0)" stroke="none">θ</tspan></tspan></tspan></text>
   </g>
  </g>
  <g class="com.sun.star.drawing.LineShape">
   <g id="id42">
    <rect class="BoundingBox" stroke="none" fill="none" x="16084" y="22158" width="3" height="2398"/>
    <path fill="none" stroke="rgb(28,28,28)" d="M 16085,22159 L 16085,22321"/>
    <path fill="none" stroke="rgb(28,28,28)" d="M 16085,22402 L 16085,22563"/>
    <path fill="none" stroke="rgb(28,28,28)" d="M 16085,22644 L 16085,22806"/>
    <path fill="none" stroke="rgb(28,28,28)" d="M 16085,22887 L 16085,23048"/>
    <path fill="none" stroke="rgb(28,28,28)" d="M 16085,23129 L 16085,23291"/>
    <path fill="none" stroke="rgb(28,28,28)" d="M 16085,23372 L 16085,23533"/>
    <path fill="none" stroke="rgb(28,28,28)" d="M 16085,23614 L 16085,23776"/>
    <path fill="none" stroke="rgb(28,28,28)" d="M 16085,23857 L 16085,24019"/>
    <path fill="none" stroke="rgb(28,28,28)" d="M 16085,24099 L 16085,24261"/>
    <path fill="none" stroke="rgb(28,28,28)" d="M 16085,24342 L 16085,24504"/>
   </g>
  </g>
  <g class="com.sun.star.drawing.TextShape">
   <g id="id43">
    <rect class="BoundingBox" stroke="none" fill="none" x="14907" y="22526" width="1049" height="963"/>
    <text class="TextShape"><tspan class="TextParagraph" font-family="Liberation Sans, sans-serif" font-size="353px" font-weight="400"><tspan class="TextPosition" x="15157" y="23227"><tspan font-size="635px" fill="rgb(0,0,0)" stroke="none">b</tspan><tspan fill="rgb(0,0,0)" stroke="none">1</tspan></tspan></tspan></text>
   </g>
  </g>
  <g class="com.sun.star.drawing.LineShape">
   <g id="id44">
    <rect class="BoundingBox" stroke="none" fill="none" x="13313" y="24374" width="4571" height="381"/>
    <path fill="none" stroke="rgb(0,169,51)" stroke-width="53" stroke-linejoin="round" d="M 13340,24524 L 17340,24564"/>
    <path fill="rgb(0,169,51)" stroke="none" d="M 17883,24570 L 17316,24375 17313,24754 17883,24570 Z"/>
   </g>
  </g>
  <g class="com.sun.star.drawing.TextShape">
   <g id="id45">
    
    <rect class="BoundingBox" stroke="none" fill="none" x="11937" y="16094" width="2311" height="646"/>
    <text class="TextShape"><tspan class="TextParagraph" font-family="Liberation Sans, sans-serif" font-size="353px" font-weight="400"><tspan class="TextPosition" x="12187" y="16537"><tspan fill="rgb(0,0,0)" stroke="none">b2 – ‘Y axis</tspan></tspan></tspan></text>
   </g>
  </g>
  <g class="com.sun.star.drawing.LineShape">
   <g id="id46">
    <rect class="BoundingBox" stroke="none" fill="none" x="14067" y="16941" width="2395" height="2768"/>
    <path fill="none" stroke="rgb(28,28,28)" d="M 16460,19707 L 14348,17266"/>
    <path fill="rgb(28,28,28)" stroke="none" d="M 14067,16941 L 14248,17379 14475,17183 14067,16941 Z"/>
   </g>
  </g>
  <g class="com.sun.star.drawing.LineShape">
   <g id="id47">
    <rect class="BoundingBox" stroke="none" fill="none" x="12544" y="17001" width="3881" height="47"/>
    <path fill="none" stroke="rgb(28,28,28)" d="M 12545,17002 L 12707,17004"/>
    <path fill="none" stroke="rgb(28,28,28)" d="M 12788,17005 L 12949,17007"/>
    <path fill="none" stroke="rgb(28,28,28)" d="M 13030,17008 L 13192,17009"/>
    <path fill="none" stroke="rgb(28,28,28)" d="M 13273,17010 L 13434,17012"/>
    <path fill="none" stroke="rgb(28,28,28)" d="M 13515,17013 L 13677,17015"/>
    <path fill="none" stroke="rgb(28,28,28)" d="M 13758,17016 L 13919,17018"/>
    <path fill="none" stroke="rgb(28,28,28)" d="M 14000,17019 L 14162,17020"/>
    <path fill="none" stroke="rgb(28,28,28)" d="M 14243,17021 L 14404,17023"/>
    <path fill="none" stroke="rgb(28,28,28)" d="M 14485,17024 L 14647,17026"/>
    <path fill="none" stroke="rgb(28,28,28)" d="M 14728,17027 L 14889,17029"/>
    <path fill="none" stroke="rgb(28,28,28)" d="M 14970,17030 L 15132,17031"/>
    <path fill="none" stroke="rgb(28,28,28)" d="M 15213,17032 L 15375,17034"/>
    <path fill="none" stroke="rgb(28,28,28)" d="M 15455,17035 L 15617,17037"/>
    <path fill="none" stroke="rgb(28,28,28)" d="M 15698,17038 L 15860,17040"/>
    <path fill="none" stroke="rgb(28,28,28)" d="M 15940,17041 L 16102,17042"/>
    <path fill="none" stroke="rgb(28,28,28)" d="M 16183,17043 L 16345,17045"/>
   </g>
  </g>
  <g class="com.sun.star.drawing.EllipseShape">
   <g id="id48">
    <rect class="BoundingBox" stroke="none" fill="none" x="15780" y="18644" width="683" height="277"/>
    <path fill="none" stroke="rgb(51,51,51)" stroke-width="18" stroke-linejoin="round" d="M 15790,18911 C 15842,18867 15898,18828 15957,18794 16108,18708 16278,18660 16452,18654"/>
   </g>
  </g>
  <g class="com.sun.star.drawing.LineShape">
   <g id="id49">
    <rect class="BoundingBox" stroke="none" fill="none" x="15766" y="18756" width="97" height="169"/>
    <path fill="none" stroke="rgb(17,17,17)" stroke-width="35" stroke-linejoin="round" d="M 15784,18906 L 15844,18774"/>
   </g>
  </g>
  <g class="com.sun.star.drawing.LineShape">
   <g id="id50">
    <rect class="BoundingBox" stroke="none" fill="none" x="15766" y="18888" width="173" height="51"/>
    <path fill="none" stroke="rgb(17,17,17)" stroke-width="35" stroke-linejoin="round" d="M 15784,18906 L 15920,18920"/>
   </g>
  </g>
  <g class="com.sun.star.drawing.TextShape">
   <g id="id51">
    <rect class="BoundingBox" stroke="none" fill="none" x="15767" y="18162" width="801" height="807"/>
    <text class="TextShape"><tspan class="TextParagraph" font-family="Liberation Sans, sans-serif" font-size="423px" font-weight="400"><tspan class="TextPosition" x="16017" y="18672"><tspan fill="rgb(0,0,0)" stroke="none">θ</tspan></tspan></tspan></text>
   </g>
  </g>
  <g class="com.sun.star.drawing.MeasureShape">
   <g id="id52">
    <rect class="BoundingBox" stroke="none" fill="none" x="13382" y="17124" width="3008" height="3244"/>
    <path fill="none" stroke="rgb(0,0,0)" d="M 15673,20018 L 13723,17803"/>
    <path fill="rgb(0,0,0)" stroke="none" d="M 15862,20234 L 15739,19942 15589,20074 15862,20234 Z"/>
    <path fill="rgb(0,0,0)" stroke="none" d="M 13533,17588 L 13657,17879 13807,17747 13533,17588 Z"/>
    <path fill="none" stroke="rgb(0,0,0)" d="M 16388,19771 L 15712,20366"/>
    <path fill="none" stroke="rgb(0,0,0)" d="M 14059,17125 L 13383,17720"/>
    <text class="TextShape" transform="rotate(-131.3 14729 19021)"><tspan class="TextParagraph" font-family="Liberation Sans, sans-serif" font-size="353px" font-weight="400"><tspan class="TextPosition" x="14729" y="19021"><tspan fill="rgb(0,0,0)" stroke="none">1</tspan></tspan></tspan></text>
   </g>
  </g>
  <g class="com.sun.star.drawing.MeasureShape">
   <g id="id53">
    <rect class="BoundingBox" stroke="none" fill="none" x="14138" y="15917" width="2326" height="1016"/>
    <path fill="none" stroke="rgb(0,0,0)" d="M 14428,16225 L 16175,16230"/>
    <path fill="rgb(0,0,0)" stroke="none" d="M 14141,16224 L 14441,16325 14442,16125 14141,16224 Z"/>
    <path fill="rgb(0,0,0)" stroke="none" d="M 16461,16231 L 16162,16130 16161,16330 16461,16231 Z"/>
    <path fill="none" stroke="rgb(0,0,0)" d="M 14139,16924 L 14142,16024"/>
    <path fill="none" stroke="rgb(0,0,0)" d="M 16459,16931 L 16462,16031"/>
    <text class="TextShape" transform="rotate(0.1 14935 16173)"><tspan class="TextParagraph" font-family="Liberation Sans, sans-serif" font-size="353px" font-weight="400"><tspan class="TextPosition" x="14935" y="16173"><tspan fill="rgb(0,0,0)" stroke="none">sin </tspan></tspan><tspan class="TextPosition" x="15481" y="16175"><tspan fill="rgb(0,0,0)" stroke="none">θ</tspan></tspan></tspan></text>
   </g>
  </g>
  <g class="com.sun.star.drawing.MeasureShape">
   <g id="id54">
    <rect class="BoundingBox" stroke="none" fill="none" x="16584" y="17017" width="915" height="2749"/>
    <path fill="none" stroke="rgb(0,0,0)" d="M 17296,17308 L 17286,19477"/>
    <path fill="rgb(0,0,0)" stroke="none" d="M 17297,17022 L 17196,17321 17396,17322 17297,17022 Z"/>
    <path fill="rgb(0,0,0)" stroke="none" d="M 17285,19764 L 17386,19464 17186,19463 17285,19764 Z"/>
    <path fill="none" stroke="rgb(0,0,0)" d="M 16597,17018 L 17497,17022"/>
    <path fill="none" stroke="rgb(0,0,0)" d="M 16585,19760 L 17485,19764"/>
   </g>
  </g>
  <g class="com.sun.star.drawing.TextShape">
   <g id="id55">
    <rect class="BoundingBox" stroke="none" fill="none" x="15002" y="17416" width="1049" height="963"/>
    <text class="TextShape"><tspan class="TextParagraph" font-family="Liberation Sans, sans-serif" font-size="353px" font-weight="400"><tspan class="TextPosition" x="15252" y="18117"><tspan font-size="635px" fill="rgb(0,0,0)" stroke="none">b</tspan><tspan fill="rgb(0,0,0)" stroke="none">2</tspan></tspan></tspan></text>
   </g>
  </g>
  <g class="com.sun.star.drawing.LineShape">
   <g id="id56">
    <rect class="BoundingBox" stroke="none" fill="none" x="16264" y="15078" width="381" height="4702"/>
    <path fill="none" stroke="rgb(255,0,0)" stroke-width="53" stroke-linejoin="round" d="M 16449,19752 L 16454,15621"/>
    <path fill="rgb(255,0,0)" stroke="none" d="M 16455,15078 L 16265,15646 16644,15647 16455,15078 Z"/>
   </g>
  </g>
  <g class="com.sun.star.drawing.TextShape">
   <g id="id57">
    <rect class="BoundingBox" stroke="none" fill="none" x="12632" y="14028" width="4093" height="1362"/>
    <text class="TextShape"><tspan class="TextParagraph" font-family="Liberation Sans, sans-serif" font-size="494px" font-weight="400"><tspan class="TextPosition" x="12882" y="14602"><tspan fill="rgb(0,0,0)" stroke="none">Projection of b2 </tspan></tspan></tspan><tspan class="TextParagraph" font-family="Liberation Sans, sans-serif" font-size="494px" font-weight="400"><tspan class="TextPosition" x="12882" y="15157"><tspan fill="rgb(0,0,0)" stroke="none">onto frame A</tspan></tspan></tspan></text>
   </g>
  </g>
  <g class="com.sun.star.drawing.TextShape">
   <g id="id58">
    <rect class="BoundingBox" stroke="none" fill="none" x="13013" y="20709" width="4093" height="1362"/>
    <text class="TextShape"><tspan class="TextParagraph" font-family="Liberation Sans, sans-serif" font-size="494px" font-weight="400"><tspan class="TextPosition" x="13263" y="21283"><tspan fill="rgb(0,0,0)" stroke="none">Projection of b1 </tspan></tspan></tspan><tspan class="TextParagraph" font-family="Liberation Sans, sans-serif" font-size="494px" font-weight="400"><tspan class="TextPosition" x="13263" y="21838"><tspan fill="rgb(0,0,0)" stroke="none">onto frame A</tspan></tspan></tspan></text>
   </g>
  </g>
  <g class="com.sun.star.drawing.TextShape">
   <g id="id59">
    <rect class="BoundingBox" stroke="none" fill="none" x="1527" y="19345" width="1346" height="646"/>
    <text class="TextShape"><tspan class="TextParagraph" font-family="Liberation Sans, sans-serif" font-size="353px" font-weight="400"><tspan class="TextPosition" x="1777" y="19788"><tspan fill="rgb(0,0,0)" stroke="none">cos </tspan><tspan fill="rgb(0,0,0)" stroke="none">θ</tspan></tspan></tspan></text>
   </g>
  </g>
  <g class="com.sun.star.drawing.TextShape">
   <g id="id60">
    <rect class="BoundingBox" stroke="none" fill="none" x="16640" y="23290" width="1244" height="646"/>
    <text class="TextShape"><tspan class="TextParagraph" font-family="Liberation Sans, sans-serif" font-size="353px" font-weight="400"><tspan class="TextPosition" x="16890" y="23733"><tspan fill="rgb(0,0,0)" stroke="none">sin </tspan><tspan fill="rgb(0,0,0)" stroke="none">θ</tspan></tspan></tspan></text>
   </g>
  </g>
  <g class="com.sun.star.drawing.TextShape">
   <g id="id61">
    <rect class="BoundingBox" stroke="none" fill="none" x="17265" y="18358" width="1346" height="646"/>
    <text class="TextShape"><tspan class="TextParagraph" font-family="Liberation Sans, sans-serif" font-size="353px" font-weight="400"><tspan class="TextPosition" x="17515" y="18801"><tspan fill="rgb(0,0,0)" stroke="none">cos </tspan><tspan fill="rgb(0,0,0)" stroke="none">θ</tspan></tspan></tspan></text>
   </g>
  </g>
 </g>
</svg>
#+END_EXPORT 

_APPROACH 1_ =>  _Finding the Direction Cosine Matrix_  $\text{DCM}_{BA}$ _by using dot product or angle between vectors_

\begin{equation}
   \text{DCM}_{BA}  =  \begin{pmatrix}  
                           \\  \hat{b}_1 \cdot \hat{a}_1  &  \hat{b}_1 \cdot \hat{a}_2 &  \hat{b}_1 \cdot \hat{a}_3
                           \\  \hat{b}_2 \cdot \hat{a}_1  &  \hat{b}_2 \cdot \hat{a}_2 &  \hat{b}_2 \cdot \hat{a}_3
                           \\  \hat{b}_3 \cdot \hat{a}_1  &  \hat{b}_3 \cdot \hat{a}_2 &  \hat{b}_3 \cdot \hat{a}_3
                       \end{pmatrix}

\end{equation}


Find the the dot products between basis vectors.

  + $\hat{b}_1 \cdot \hat{a}_1 =  \cos( \hat{b}_1, \hat{a}_1)  = \cos \theta$
    
  + $\hat{b}_1 \cdot \hat{a}_2 =  \cos( \hat{b}_1, \hat{a}_2)  = \cos(90 - \theta) = \sin \theta$
    
  + $\hat{b}_1 \cdot \hat{a}_4 =  \cos( \hat{b}_1, \hat{a}_3)  = \cos(90) = 0$
    
  + $\hat{b}_2 \cdot \hat{a}_1 =  \cos( \hat{b}_2, \hat{a}_1)  = \cos (90  + \theta) = - \sin \theta$
    
  + $\hat{b}_2 \cdot \hat{a}_2 =  \cos( \hat{b}_2, \hat{a}_2)  = \cos \theta$

  + $\hat{b}_2 \cdot \hat{a}_3 =  \cos( \hat{b}_2, \hat{a}_3)  = \cos 90 = 0$
    
  + $\hat{b}_3 \cdot \hat{a}_1 =  \cos( \hat{b}_3, \hat{a}_1)  = \cos 90 = 0$
    
  + $\hat{b}_3 \cdot \hat{a}_2 =  \cos( \hat{b}_3, \hat{a}_2)  = \cos 90 = 0$
    
  + $\hat{b}_3 \cdot \hat{a}_3 =  \cos( \hat{b}_3, \hat{a}_3)  = \cos 0 = 1$


So, the DCM matrix becomes:

\begin{equation}
     \text{DCM}_{BA}
          = \begin{bmatrix}
                 \cos \theta &  \sin \theta & 0  \\
                -\sin \theta &  \cos \theta & 0  \\
                        0    &   0          & 1  \\
          \end{bmatrix}
\end{equation}

The rotation matrix ${}^{A}_{B}R$ that maps vectors from frame B,
rotated reference frame, to frame A, static reference frame, can be
found by transposing the previous matrix

\begin{equation}
    {}^{A}_{B}R =  \text{DCM}_{BA}^T
                 = \begin{bmatrix}
                        \cos \theta &  -\sin \theta & 0  \\
                        \sin \theta &  \cos \theta & 0  \\
                               0    &   0          & 1  \\
                   \end{bmatrix}

\end{equation}



_APPROACH  2_ => _Finding the Direction Cosine Matrix_  $\text{DCM}_{BA}$ _by projecting basis vectors._


STEP 1 => Write each basis vector of frame B in terms of basis vectors of frame
A, in other words write the frame A coordinates of each basis vector
of B by projecting each of them onto frame A basis vectors. 

\begin{equation}
        ^A\hat{b}_1   = \begin{bmatrix}  \cos \theta \\  \sin \theta \\ 0  \end{bmatrix}

   \quad ^A\hat{b}_2  = \begin{bmatrix} -\sin \theta \\  \cos \theta \\ 0  \end{bmatrix}

   \quad ^A\hat{b}_3  = \begin{bmatrix} 0 \\ 0 \\ 1  \end{bmatrix}
\end{equation}


STEP 2 => Build the DCM matrix using the previous vectors. 

\begin{equation}
    {}^{B}_{A}R  = \text{DCM}_{BA}
                    = \begin{pmatrix}
                          ^A\hat{b}_1^T \\ ^A\hat{b}_2^T  \\ ^A\hat{b}_3^T 
                      \end{pmatrix}

                    = \begin{bmatrix}
                           \cos \theta &  \sin \theta & 0  \\
                          -\sin \theta &  \cos \theta & 0  \\
                                  0    &   0          & 1  \\
                      \end{bmatrix}
\end{equation}


The rotation matrix that maps vectors from frame B to frame A or
${}^{A}_{B}R$ can be found by tranposing the previous matrix.

\begin{equation}
    {}^{A}_{B}R  = \text{DCM}_{BA}^T

                    = \begin{bmatrix}
                           \cos \theta &  -\sin \theta & 0  \\
                           \sin \theta &  \cos \theta & 0  \\
                                  0    &   0          & 1  \\
                      \end{bmatrix}
\end{equation}

The rotation matrix ${}^{A}_{B}R$  could also be found in this way.

\begin{equation}
    {}^{A}_{B}R = \begin{pmatrix} 
                      ^A\hat{b}_1 & ^A\hat{b}_2 & ^A\hat{b}_3
                  \end{pmatrix}
                 = \begin{bmatrix}
                        \cos \theta &  -\sin \theta & 0  \\
                        \sin \theta &  \cos \theta & 0  \\
                               0    &   0          & 1  \\
                   \end{bmatrix}

\end{equation}



 *References:*

 + [PDF] - [[https://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.558.8400&rep=rep1&type=pdf][Lecture Notes - Modelling of Mechanical Systems]]  (Thomas Bak, 2002)

 + [[http://www.starlino.com/dcm_tutorial.html][DCM Tutorial – An Introduction to Orientation Kinematics – Starlino Electronics]]

 + [[https://academicflight.com/articles/kinematics/rotation-formalisms/rotation-matrix/][Rotation Matrix (Direction Cosine Matrix (DCM)) | Academic Flight]]

 + [[http://www.angadhn.com/ComputationalDynamics/kinematics/DCM.html][Direction Cosine Matrix (DCM) — Introduction to Symbolic Computational Dynamics]]

 + [[https://www.vectornav.com/resources/inertial-navigation-primer/math-fundamentals/math-attituderep][Attitude representation in inertial navigation · VectorNav]]

 + [[https://puttym.github.io/space/2019/09/29/direction_cosine_matrix/][Direction cosine matrix · Sea of Tranquility]] 

 + [[https://www.intechopen.com/chapters/70364][Kinematics: On Direction Cosine Matrices | IntechOpen]]

 + [[https://www.mathworks.com/help/aerotbx/ug/angle2dcm.html][Convert rotation angles to direction cosine matrix - MATLAB angle2dcm]]

 + [[https://stevendumble.com/attitude-representations-understanding-direct-cosine-matrices-euler-angles-and-quaternions/#:~:text={v}^a-,Direct%20Cosine%20Matrix%20(DCM),to%20all%20the%20other%20axes.][Attitude Representations – Understanding Direct Cosine Matrices, Euler Angles and Quaternions]] – Steven Dumble | PhD

 + [[https://www.mathworks.com/help/aerotbx/ug/angle2dcm.html][Convert rotation angles to direction cosine matrix - MATLAB angle2dcm]]

 + [[https://www.mathworks.com/help/aerotbx/ug/dcm2angle.html][Create rotation angles from direction cosine matrix - MATLAB dcm2angle]]

*** Default Coordinate Systems

In order to properly use a graphics application programming interface,
one must know the default coordinate system and conventions defined by
the graphics API. This convention includes: the coordinate system
origin, which may be on the upper left corner of drawing window or
lower left corner of drawing window; the default direction of axis and
whether the coordinate system is RHS (Right-Handed System) or LHS
(Left-Handed System).

  *OpenGL Default Coordinate System*

OpenGL API uses a default coordinate system with the origin place at
the screen center, X axis as the horizontal axis, Y axis as the
vertical axis and Z axis going from the screen center towards the
user. This default coordinate system NDC is normalized, each dimension
X, Y and Z has the range from -1.0 to +1.0. Any vertex with any
dimension out of this range is not displayed on the screen.

In this coordinate system, the coordinate of points: point A at the OpenGL
window top right corner is (x = 1, y = 1); the coordinate of point B
is (x = 1,y= -1); the coordinate of the point C is (x = -1, y = -1).

#+CAPTION: OpenGL default coordinate system (NDC - Normalized Device Coordinate)
[[file:images/default-coordinate-system1.png][file:images/default-coordinate-system1.png]]


  *OpenGL Coordinate System X Math-textbook Coordinate System*

OpenGL default coordinate system should not be confused with the
coordinate system commonly found in math and physics textbooks, where
Z axis is the vertical axis, Y axis is the horizontal axis and the X
axis is going from the screen twoards the screen observer (out of
screen). The advantage of OpenGL coordinate system over the coordinate
system used by textbooks is that 2D drawing requires just ignoring the
Z axis, setting any Z coordinate to zero. If one does not like the
OpenGL default coordinate system, it is possible to obtain the math
textbook coordinate system just by applying affine rotation transform
the OpenGL default coordinate system.

#+CAPTION: OpenGL coordinate system versus math or physics coordinate system.
[[file:images/default-coordinate-system2.png][file:images/default-coordinate-system2.png]]


The transform matrix that maps coordinates from the math and physics
style coordinate system (M) to the OpenGL coordinate (C) can be obtained by: 


\begin{equation}
   T_{M -> C} = \text{Rot}_x(-90) \cdot \text{Rot}_z(-90)  
\end{equation}

\begin{equation}
   T_{M -> C} = 
           \begin{bmatrix}
               1 &  0  & 0 & 0  \\
               0 &  0  & 1 & 0  \\ 
               0 & -1  & 0 & 0  \\
               0 &  0  & 0 & 1 
           \end{bmatrix}

           \begin{bmatrix}
               0 &  1  & 0 & 0  \\
              -1 &  0  & 0 & 0  \\ 
               0 &  0  & 1 & 0  \\
               0 &  0  & 0 & 1 
           \end{bmatrix}
\end{equation}


\begin{equation}
   T_{M -> C} = \begin{bmatrix}
                    0 & 1 & 0 & 0  \\
                    0 & 0 & 1 & 0  \\ 
                    1 & 0 & 0 & 0  \\
                    0 & 0 & 0 & 1 
                \end{bmatrix}
\end{equation}


\begin{equation}
    \underset{ \text{OpenGL Coord.} }{ \begin{bmatrix} x_o \\ y_o \\ z_o \\ 1 \end{bmatrix}  }

    = \begin{bmatrix}
          0 & 1 & 0 & 0  \\
          0 & 0 & 1 & 0  \\ 
          1 & 0 & 0 & 0  \\
          0 & 0 & 0 & 1 
      \end{bmatrix}

   \underset{ \text{Math Coord.} }{ \begin{bmatrix} x_m \\ y_m \\ z_m \\ 1 \end{bmatrix}  }
\end{equation}


Where: 

  + $Rot_x$ - Is the X axis rotation matrix.
  + $Rot_z$ - Is the Z axis rotation matrix.
  + $T_{M -> C}$ - Is the affine matrix transform that maps
    coordinates from math-and-physics coordinate system to OpenGL
    coordinate system. 
  + M - designates the coordinate system similar to the
    math-and-physics textbooks as in the right side of the previous
    picture.
  + C - designates the openGL coordinate system.

Coordinates from math textbook style coordinate systems can be mapped
to OpenGL by using the previous affine transform matrix.

\begin{equation} 
   \begin{bmatrix} x_c \\ y_c \\ z_c \\ 1 \end{bmatrix}

     =   T_{M -> C} 
        \begin{bmatrix} x_m \\ y_m \\ z_m \\ 1 \end{bmatrix}
\end{equation}

With the previous equation it is possible to verify that the Y axis,
vector (0, 1, 0) from the M coordinate system corresponds to the axis
X (1, 0, 0) in the openGL (C) coordinate system. 

\begin{equation} 
     \begin{bmatrix} 1 \\ 0 \\ 0 \\ 1 \end{bmatrix}
      = 
         \begin{bmatrix}
             0 & 1 & 0 & 0  \\
             0 & 0 & 1 & 0  \\ 
             1 & 0 & 0 & 0  \\
             0 & 0 & 0 & 1 
         \end{bmatrix}    
        \begin{bmatrix} 0 \\ 1 \\ 0 \\ 1 \end{bmatrix}
\end{equation}

The same procedure can be applied to X axis in (M) coordinate system,
which is mapped to Z axis in OpenGL coordinate system and to Z axis
from (M)  coordinate system which is mapped to Y axis in OpenGL
coordinate system. 


_Alternative Approach - using basis vectors_

The rotation matrix that maps from math and physics reference frame M
to Opengl reference frame, designated as C, is a matrix whose columns  
are the basis vectors (unit vectors) of each axis of reference frame M
expressed in coordinates of reference frame C.

\begin{equation}
         ^C\hat{X}_M   = \begin{pmatrix} 0 \\  0 \\ 1  \end{pmatrix}

   \quad ^C\hat{Y}_M  = \begin{pmatrix}  1 \\ 0 \\ 0  \end{pmatrix}

   \quad ^C\hat{Z}_M  = \begin{pmatrix}  0 \\ 1 \\ 0  \end{pmatrix}
\end{equation}


Where: 

  +  $^C\hat{X}_M$ - is the X axis basis vector of frame M expressed
     in coordinates of reference frame C (OpenGL reference frame).

  +  $^C\hat{Y}_M$ - is the Y axis basis vector of frame M expressed
     in coordinates of reference frame C (OpenGL reference frame).

  +  $^C\hat{Z}_M$ - is the Z axis basis vector of frame M expressed
     in coordinates of reference frame C (OpenGL reference frame).

So, the rotation matrix that maps from frame M to frame C becomes:

\begin{equation}
   \boxed{
      {}^{C}_{M}R  
           = \begin{pmatrix} ^C\hat{X}_M  &  ^C\hat{Y}_M  &  ^C\hat{Z}_M \end{pmatrix}
           =  \begin{pmatrix}
                  0  & 1 & 0 \\
                  0  & 0 & 1 \\
                  1  & 0 & 0 \\ 
              \end{pmatrix}
    }
\end{equation}


The homogenous 4 x 4 by matrix that maps from M frame M to frame C
(OpengL frame) can be obtained as follow: 


\begin{equation}
    \boxed{
      {}^{C}_{M}T = 
           \begin{pmatrix} 
              {}^{C}_{M}R                  & \boldsymbol{0}_{3 \times 1} \\ 
              \boldsymbol{0}_{1 \times 3}  & 1
           \end{pmatrix}
           =            
           \begin{pmatrix} 
               0 & 1 & 0 & 0  \\
               0 & 0 & 1 & 0  \\ 
               1 & 0 & 0 & 0  \\
               0 & 0 & 0 & 1 
           \end{pmatrix}
     }
\end{equation}


   *Coordinate System positions from other computer graphics APIS*

The following diagram shows the possible default coordinate systems
positions from many computer graphics APIS:

#+CAPTION: Default coordinate systems positions
[[file:images/default-coordinate-system3.png][file:images/default-coordinate-system3.png]]

  + (C) => Center of display screen
    + Default position of OpenGL NDC coordinate system (clip-space).

  + (B) => Lower-left corner of display screen
    + Default position of OpenGL screen coordinate system

  + (U) => Upper-left corner of display screen
    + => The Y axis is inverted, it is positive in the downward
      direction instead of upward direction.
    + Default position of most 2D graphics APIs including: Html5
      canvas; SVG; Java AWT 2D.


Transform matrix, $T_{B \rightarrow U}$, that maps coordinates from
lower-left corner (B) coordinate system to upper-left-corner
coordinate system.

\begin{equation}
   T_{B \rightarrow U} =
           \begin{bmatrix}
               1 &  0 & 0  \\
               0 & -1 & h  \\
               0 &  0 & 1
           \end{bmatrix}
\end{equation}

Transform matrix, $T_{C \rightarrow U}$, that maps coordinates from
(C) window-center coordinate system to upper-left corner (U)
coordinate system. This transform is useful for drawing with origin on
screen center when using gaphics APIs which the default coordinate
system is located at the upper-left corner.

\begin{equation}
   T_{C \rightarrow U} =
           \begin{bmatrix}
               1 &  0 & w/2  \\
               0 & -1 & h/2  \\
               0 &  0 & 1
           \end{bmatrix}
\end{equation}

\begin{equation}
   V_U = T_{C \rightarrow U} \cdot V_C
\end{equation}

\begin{equation}
   \begin{bmatrix} x_u \\ y_u \\ 1 \end{bmatrix}
   =
   T_{C \rightarrow U}
   \begin{bmatrix} x_c \\ y_c \\ 1 \end{bmatrix}

   =
   \begin{bmatrix}
        x_c + w/2  \\
       -y_c + h/2  \\
        1
   \end{bmatrix}
\end{equation}

Transform matrix, $T_{B \rightarrow C}$, that maps coordinates from
lower-left corner (B) to screen-center origin, OpenGL normalized
coordinate system (C).

\begin{equation}
   T_{B \rightarrow C} =
           \begin{bmatrix}
               2/w &  0  & -1  \\
               0   & 2/h & -1  \\
               0   &  0  &  1
           \end{bmatrix}
\end{equation}

The matrix transform, $T_{C \rightarrow B}$, that maps from coordinate
system C to coordinate system B can be computed as the inverse of
$T_{B \rightarrow C}$.

\begin{equation}
   T_{C \rightarrow B} = T_{B \rightarrow C}^{-1}
   =
           \begin{bmatrix}
               w/2 &  0  & w/2  \\
               0   &  h/2 & h/2  \\
               0   &  0  &  1
           \end{bmatrix}
\end{equation}

*** Column-Major X Row-major Matrices 

OpenGL ES (embedded version of OpenGL) and WebGL APIs no longer
support matrices in row-major order memory layout, where elements of
rows are contiguous in memory. Instead, those APIs only support
matrices in _column-major_ memory layout, where elements of columns are
contiguous in memory. 

So, in modern OpenGL, the following API no longer works with boolean
value 'true' that indicates that the matrix passed as a argument is in
row-major oder format:

#+BEGIN_SRC cpp 
   // WebGL API - (NO LONGER POSSIBLE =>> ERROR!) 
   gl.uniformMatrix4fv(u_model, true, matrix_in_row_major_order); 
   
    // OpenGL ES - API (NO LONGER POSSIBLE!!!! =>> ERROR!!!)
    // Where u_model is a shader uniform variable 
    glUniformMatrix4fv(u_model, 1, GL_TRUE,  matrix_in_row_major_order );
#+END_SRC

Now, only this following usage works:

#+BEGIN_SRC cpp 
   // WebGL API 
   gl.uniformMatrix4fv(u_model, false, matrix_in_column_major_order); 
   
    // OpenGL ES - API 
    // Where u_model is a shader uniform variable 
    glUniformMatrix4fv(u_model, 1, GL_FALSE,  matrix_in_column_major_order);
#+END_SRC


Further reading: 

  + [[https://en.m.wikipedia.org/wiki/In-place_matrix_transposition][In-place matrix transposition]]

  + [[https://en.m.wikipedia.org/wiki/Row-_and_column-major_order][Row- and column-major order - Wikipedia]]

  + [[https://fgiesen.wordpress.com/2012/02/12/row-major-vs-column-major-row-vectors-vs-column-vectors/][Row major vs. column major, row vectors vs. column vectors | The ryg blog]]

  + [[https://www.scratchapixel.com/lessons/mathematics-physics-for-computer-graphics/geometry/row-major-vs-column-major-vector][Geometry (Row Major vs Column Major Vector)]]

  + [[https://www.mathworks.com/help/coder/ug/what-are-column-major-and-row-major-representation-1.html][Matlab - Row-Major and Column-Major Array Layouts]]

  + [[https://scicomp.stackexchange.com/questions/4796/row-major-versus-column-major-layout-of-matrices][Row major versus Column major layout of matrices]]

  + [[https://seanmiddleditch.github.io/matrices-handedness-pre-and-post-multiplication-row-vs-column-major-and-notations/][Matrices, Handedness, Pre and Post Multiplication, Row vs Column Major, and Notations]]

  + [[https://eli.thegreenplace.net/2015/memory-layout-of-multi-dimensional-arrays][Memory layout of multi-dimensional arrays - Eli Bendersky's website]]

  + [[https://jblevins.org/log/efficient-code][Writing Efficient Code]]


 *Row-major matrices* 

Let the array T of type float[16], containing 16 32 bits floating point
numbers, be a comumn-major matrix represeting some affine transform or
projection transform. 


#+BEGIN_SRC cpp  
  T: float[16] = [ T[0], T[1], T[2], T[3], T[4], T[5], T[6], T[7], T[8], T[9], T[11], T[12], T[13], T[14], T[15] ]
#+END_SRC 

The array T float[16], represents the following matrix in row-major order: 

#+BEGIN_SRC text 
      /                           \
      |  T[00] T[01] T[02]  T[03]  |   
      |  T[04] T[05] T[06]  T[07]  | 
  T = |  T[08] T[09] T[10]  T[11]  |
      |  T[12] T[13] T[14]  T[15]  |
      \                            /
      
      /                             \       
      |  A(1,1) A(1,2) A(1,3) A(1,4) |  
      |  A(2,1) A(2,2) A(2,3) A(2,4) |
  T = |  A(3,1) A(3,2) A(3,3) A(3,4) |
      |  A(4,1) A(4,2) A(4,3) A(4,4) |
      \                             /
      
 It follows that: 
    
   T[00] = A(1,1) ; T[01] = A(1,2) ; T[02] = A(1,3) ; T[03] = A(1,4)
   T[04] = A(2,1) ; T[05] = A(2,2) ; T[06] = A(2,3) ; T[07] = A(2,4)
   T[08] = A(3,1) ; T[09] = A(3,2) ; T[10] = A(3,3) ; T[11] = A(3,4)
   T[12] = A(4,1) ; T[13] = A(4,2) ; T[14] = A(4,3) ; T[15] = A(4,4)
   

  T: float[16] = [  A(1,1), A(1,2), A(1,3), A(1,4)
                  , A(2,1), A(2,2), A(2,3), A(2,4)
                  , A(3,1), A(3,2), A(3,3), A(3,4)
                  , A(4,1), A(4,2), A(4,3), A(4,4)
                ];
#+END_SRC 

 *Column-major matrices* 

Let the array T of type float[16], containing 16 32 bits floating point
numbers, be a comumn-major matrix represeting some affine transform or
projection transform. 


#+BEGIN_SRC cpp  
  T: float[16] = [ T[0], T[1], T[2], T[3], T[4], T[5], T[6], T[7], T[8], T[9], T[11], T[12], T[13], T[14], T[15] ]
#+END_SRC 

The array T represents the following column-major matrix: 

#+BEGIN_SRC text 
               Matrix in Column-Major Order  
         _                                        _ 
        /                                          \
       +                                            +
  T  = | COLUMN[0], COLUMN[1], COLUMN[2], COLUMN[3] |
       +                                            +
        \_                                        _/  
       
      /                           \
      |  T[00] T[04] T[08]  T[12]  |
      |  T[01] T[05] T[09]  T[13]  |
  T = |  T[02] T[06] T[10]  T[14]  |
      |  T[03] T[07] T[11]  T[15]  |
      \                            /
 
    Where:
 
                     | T[0] |               | T[4] |              | T[8]  |              | T[12] |
                     | T[1] |               | T[5] |              | T[9]  |              | T[13] |
         COLUMN[0] = | T[2] |   COLUMN[1] = | T[6] |  COLUMN[2] = | T[10] |  COLUMN[3] = | T[14] |
                     | T[3] |               | T[7] |              | T[11] |              | T[15] |
                     
#+END_SRC

By applying the previous idea to this matrix, it is possible to find:

#+BEGIN_SRC cpp 
      /                             \       
      |  A(1,1) A(1,2) A(1,3) A(1,4) |  
      |  A(2,1) A(2,2) A(2,3) A(2,4) |
  T = |  A(3,1) A(3,2) A(3,3) A(3,4) |
      |  A(4,1) A(4,2) A(4,3) A(4,4) |
      \                             /
 
  T[00] = A(1,1) ; T[01] = A(2,1) ; T[02] = A(3,1) ; T[03] = A(4,1)
  T[04] = A(1,2) ; T[05] = A(2,2) ; T[06] = A(3,2) ; T[07] = A(4,2)
  T[08] = A(1,3) ; T[09] = A(2,3) ; T[10] = A(3,3) ; T[11] = A(4,3)
  T[12] = A(1,4) ; T[13] = A(2,4) ; T[14] = A(3,4) ; T[15] = A(4,4)

 
  T: float[16] = [  A(1,1), A(2,1), A(3,1), A(4,1)
                  , A(1,2), A(2,2), A(3,2), A(4,2)
                  , A(1,3), A(2,3), A(3,3), A(4,3)
                  , A(1,3), A(2,4), A(3,4), A(4,4)
                ];
#+END_SRC

The array T can be converted to a matrix by using the next formula:

#+BEGIN_SRC cpp 
  //----------------------------------//
  // Matrix using 1 as initial index  //
  //----------------------------------//
  // Line 1 
  A(1, 1) = T[0]  
  A(1, 2) = T[4]  
  A(1, 3) = T[8] 
  A(1, 4) = T[12] 
  // Line 2 
  A(2, 1) = T[1]   
  A(2, 2) = T[5] 
  A(2, 3) = T[9] 
  A(2, 4) = T[13]
  // Line 3 
  A(3, 1) = T[2]
  A(3, 2) = T[6]
  A(3, 3) = T[10]
  A(3, 4) = T[14]
  // Line 4 
  A(4, 1) = T[3]
  A(4, 2) = T[7]
  A(4, 3) = T[11]
  A(4, 4) = T[15]
  
  //----------------------------------//
  // Matrix using 0 as initial index  //
  //----------------------------------//
  // Line 1 
  A(0, 0) = T[0]  
  A(0, 1) = T[4]  
  A(0, 2) = T[8] 
  A(0, 3) = T[12] 
  // Line 2 
  A(1, 0) = T[1]   
  A(1, 1) = T[5] 
  A(1, 2) = T[9] 
  A(1, 3) = T[13]
  // Line 3 
  A(2, 0) = T[2]
  A(2, 1) = T[6]
  A(2, 2) = T[10]
  A(2, 3) = T[14]
  // Line 4 
  A(3, 0) = T[3]
  A(3, 1) = T[7]
  A(3, 2) = T[11]
  A(3, 3) = T[15]

#+END_SRC



 *Example:* Given the translation matrix T 

#+BEGIN_SRC cpp  
      | 1  0  0  tx |
      | 0  1  0  ty |
  T = | 0  0  1  tz |
      | 0  0  0   1 |
     
  T[00] = A(1,1) = 1 ; T[01] = A(2,1) = 0 ; T[02] = A(3,1) = 0 ; T[03] = A(4,1) = 0 (column 1)
  T[04] = A(1,2) = 0 ; T[05] = A(2,2) = 1 ; T[06] = A(3,2) = 0 ; T[07] = A(4,2) = 0 (column 2)
  T[08] = A(1,3) = 0 ; T[09] = A(2,3) = 0 ; T[10] = A(3,3) = 1 ; T[11] = A(4,3) = 0 (column 3)
  T[12] = A(1,4) = tx; T[13] = A(2,4) = ty; T[14] = A(3,4) = tz; T[15] = A(4,4) = 1 (column 4)
  
 Hence, 
 
  T: float[16] = {  1,  0,  0,  0
                  , 0,  1,  0,  0
                  , 0,  0,  1,  0
                  , tx, ty, tz, 1 }; 
                  
  T: float[16] = { 1,  0,  0,  0, 0,  1,  0,  0, 0,  0,  1,  0, tx, ty, tz, 1 }
  
  gl.uniformMatrix4fv(u_model, false, T);
#+END_SRC

This matrix in column-major order is represented as:

#+BEGIN_SRC text 
                                  | 1   0   0  0 |
                                  | 0   1   0  0 |
  T             =  TRANSPOSE(T) = | 0   0   1  0 |
   column-major                   | tx ty  tz  1 |
#+END_SRC

*** Quaternions

Quaternions are a generalization of complex numbers, that was proposed
and formulated by the irish mathematician and physicist William Rowan
[[https://en.wikipedia.org/wiki/William_Rowan_Hamilton][Hamilton]], in 1843. This mathematical construct provides a convenient
notation for expressing rotation and has a broad range of
applications, including computer graphics, computer vision, robotics,
orbital mechanics, aerospace and flight dynamics.

A quaternion *q* is defined as:

\begin{equation}
   q = w + x \cdot i + y \cdot j + z \cdot k
\end{equation}

  + Where:
    + w, x, y, z are real numbers.
    + w => is the quaternion's real (scalar) component.
    + [x, y, z] => is the quaternion's vector componet
    + i, j, k are unit vectors (basis elements) that satisfies a set
      of properties.

The quaternion *q* can be represented in the following manner:

\begin{align*}
    q &= [w, x, y, z]  \quad & \text{Tuple representation}         \\
    q &= (u, \vec{v})  \quad & \text{Scalar/vector representation} \\
\end{align*}

   + Where:
     + $\vec{v} = [x, y, z]$ is the quaternion vector component.

The quaternion unit vectors or basis elements satifies the following
properties:

\begin{equation}
   i^2 = j^2 = k^2 = i \cdot j \cdot k = -1
\end{equation}

And also:

\begin{align*}
    i \cdot j = k  & \quad & j \cdot i = -k  \\
    j \cdot k = i  & \quad & k \cdot j = -i  \\
    k \cdot i = j  & \quad & i \cdot k = -j  \\
\end{align*}


 *Types of Quaternion*

 + Real quaternion

Real quaternion, is a quaterion which all elements of vector
components are zero.

\begin{equation}
   q = w + 0 \cdot i + 0 \cdot j + 0 \cdot k
\end{equation}

 + Pure quaternion

A pure quaternion, has the real part equal to zero. It can represent a
vector in the space.

\begin{equation}
  q = 0 + x \cdot i + y \cdot j + z \cdot k
\end{equation}

 + Unit quaternion

A quaternion which the norm or magnitude is equal to one.

\begin{equation}
  || q || = 1
\end{equation}

 + Identity quaternion

An identity quaternion when multiplied by another quaternion results
in no rotation.

\begin{equation}
   q_i = 1 + 0 \cdot i + 0 \cdot j + 0 \cdot k
\end{equation}

In similar way to identity matrices, the following is valid:

\begin{equation}
  q_i \cdot q_a = q_a \cdot q_i = q_a
\end{equation}

 *Quaternion Conjugate*

The conjugate of a quaternion, $q^*$, is defined as:

\begin{equation}
  q^{*} = w - \vec{v}
        = w - (x \cdot i + y \cdot j + z \cdot k)
        = w + (-x) i + (-y) j + (-z) k
\end{equation}

 *Inverse Quaternion* $q^{-1}$

\begin{equation}
  q^{-1} = \frac{q^{*}}{ || q ||^2 }
\end{equation}

The inverse quaternion satifies the following equality:

\begin{equation}
  q^{-1} \cdot q = q \cdot q^{-1} = 1
\end{equation}

 *Quaternion Norm*

The norm (magnitude) of a quaternion is given by:

\begin{equation}
   || q ||^2 = q \cdot q^{*} =  w^2 + x^2 + y^2 + z^2
\end{equation}

 *Unit quaternion*

An unit quaternion $q_u$ has norm equal to one.

\begin{equation}
   || q_u || = 1
\end{equation}

An unit quaternion is equal to its conjugate:

\begin{equation}
   q^* = q^{-1}
\end{equation}

 *Quaternion Dot Product*

A quaternion dot product is similar to vector dot product. The
quaternion dot product between two quaternions q1 and q2 is defined as
the sum of the product between the quaternions components.

\begin{equation}
   \text{dotquat}(q_1, q_2) = q_1 \cdot q_2 = w_1 \cdot w_2 + x_1 \cdot x_2 + y_1 \cdot y_2 + z_1 \cdot z_2
\end{equation}

 *Angle between two quaternion*

The angle between two quaternions q1 and q2 is defined as:

\begin{equation}
    \cos \theta = \frac{ q_1 \cdot q_2 }{ || q_1 || \cdot || q_2 ||  }
                = \frac{ \text{dotquat}(q_1, q_2) }{ \text{norm}( q_1) \cdot \text{norm}(q_2)  }
                = \frac{  w_1 \cdot w_2 + x_1 \cdot x_2 + y_1 \cdot y_2 + z_1 \cdot z_2  }{  || q_1 || \cdot || q_2 ||  }
\end{equation}

 *Quaternion Sum*

The sum of two quaternions q1 and q2 is:

\begin{equation}
   q_1 + q_2 = (w_1 + w_2) + (x_1 + x_2) i + (y_1 + y_2)j + (z_1 + z_2) k
\end{equation}

 *Quaternion Product* (a.k.a Hamilton Product)

The product *p* of two quaternons q1 and q2 can be computed as:

  + Where:
    + p is the quaternion product between q1 and q2
    + $\vec{v_1} \cdot \vec{v_2}$ is the scalar product between $\vec{v_1}$ and $\vec{v_2}$.
    + $\vec{v1} \times \vec{v_2}$ is the cross product between  $\vec{v_1}$ and $\vec{v_2}$.
    + $p_v$ is the vector component of p
    + $p_w$ is the scalar/real component of p.

\begin{align*}
  p &= q_1 \cdot q_2 = ( w_1 \cdot w_2 - \vec{v_1} \cdot \vec{v_2} \quad ; \quad
                       w_1 \cdot \vec{v_2} + w_2 \cdot \vec{v_1} + \vec{v1} \times \vec{v_2} )  \\

    &= ( w_1 \cdot w_2 - \vec{v_1} \cdot \vec{v_2} )  +
                      ( w_1 \cdot \vec{v_2} + w_2 \cdot \vec{v_1} + \vec{v1} \times \vec{v_2} )
\end{align*}

The components of vector p are:

\begin{align*}
    p_w &= w_1 \cdot w_2 - \vec{v_1} \cdot \vec{v_2}                             \\
    p_v &= w_1 \cdot \vec{v_2} + w_2 \cdot \vec{v_1} + \vec{v1} \times \vec{v_2} \\
\end{align*}

The quaternion product is non commuative, as a result, the following
holds:

\begin{equation}
      q_1 \cdot q_2 \neq q_2 \cdot q_2
\end{equation}

The quaternion product satifies the following properties:

\begin{align*}
    || q_1 \cdot q_2 ||  &= ||q_1|| \cdot || q_2 ||   \\
     (q_1 \cdot q_2)^* &= q_2^* \cdot q_1^*
\end{align*}

  *Quaternion product between pure quaternions*

The product between two pure quaternions yields the dot product and
cross simultaneously, the dot product as the real part of the
operation result and cross product as the vector part of the result.

\begin{equation}
    p \cdot q = - (p \cdot q) + (p \times q)
\end{equation}

Where:
  + Note: A pure quaternion, is the same as space vector, the real
    part is zero, w = 0.
  + $p = 0 + x_p \cdot i + y_p \cdot j + z_p \cdot k$
  + $q = 0 + x_q \cdot i + y_q \cdot j + z_q \cdot k$
  + $p \cdot q$ is the dot product between the vector parts of p and q.
  + $p \times q$ is the cross product (vector product) between p and q.


  *Quaternion division* ([[http://www.math.umd.edu/~immortal/MATH431/lecturenotes/ch_quaternions.pdf][MATH432]] - page 11)

The division between two quaternions q1 and q2 is defined as:

\begin{equation}
   \frac{q_1}{q_2} = \frac{q_1 \cdot q_2^* }{ || q_2 ||^2 } = \frac{1}{ || q_2 ||^2 } ( q_1 \cdot q_2^* )
\end{equation}

  *Quaternion product using matrices representation*

The product of two quaternions q1 and q2 can be computed
in a more convenient and faster way by using matrices and column
vectors multiplication, instead of dot product and cross product.

Let $[q]_v$ be the vector column representation of a quaternion:

\begin{equation}
  [q]_v = \begin{bmatrix} w \\ x \\ y \\ z  \end{bmatrix}
\end{equation}

Let $[q]_m$ be the following matrix representation of a quaternion:

\begin{equation}
  [q]_m = \begin{bmatrix}
               w  & -x & -y & -z  \\
               x  &  w & -z &  y  \\
               y  &  z &  w & -x  \\
               z  & -y &  x &  w  \\
          \end{bmatrix}
\end{equation}

The quaternion product between q1 and q2, designated by $[q_r]_v$, can
be expressed as a matrix-column vector multiplication, which is best
suitable for a computer implementation.

\begin{equation}
  [q_r]_v = q_1 \cdot q_2 = [q_1]_m \cdot [q_2]_v
          =
          \begin{bmatrix}
               w_1  & -x_1 & -y_1 & -z_1  \\
               x_1  &  w_1 & -z_1 &  y_1  \\
               y_1  &  z_1 &  w_1 & -x_1  \\
               z_1  & -y_1 &  x_1 &  w_1  \\
          \end{bmatrix}

          \begin{bmatrix}
              w_2 \\ x_2 \\ y_2 \\ z_2 \\
          \end{bmatrix}
\end{equation}

By performing the previous matrix multiplication, the quaternion
product becomes:

\begin{equation}
     [q_r]_v = q_1 \cdot q_2 =
       \begin{bmatrix}
            w_1 \cdot w_2 - x_1 \cdot x_2 - y_1 \cdot y_2 - z_1 \cdot z_2  \\
            x_1 \cdot w_2 + w_1 \cdot x_2 - z_1 \cdot y_2 + y_1 \cdot z_2  \\
            y_1 \cdot w_2 + z_1 \cdot x_2 + w_1 \cdot y_2 - x_1 \cdot z_2  \\
            z_1 \cdot w_2 - y_1 \cdot x_2 + x_1 \cdot y_2 + w_1 \cdot z_2  \\
       \end{bmatrix}
\end{equation}


  *Rotation Quaternion*

A rotation around an axis $\hat{n}$ (unit vector) by angle $\theta$
can be encoded by quaternion $q_r$:

\begin{equation}
   q_r = \cos \frac{\theta}{2}  + \sin \frac{\theta}{2} \cdot \hat{n}
       = \cos \frac{\theta}{2}  + \sin \frac{\theta}{2} ( x_n \cdot i + y_n \cdot j + z_n \cdot k  )
\end{equation}

 + Where:
   + $\hat{n}$ is a unit vector.
   + $\hat{n} = x_n \cdot i + y_n \cdot j + z_n \cdot k$
   + $|| \hat{n} || = 1$

The norm of quaternion $q_r = 1$, thus the following holds:

\begin{equation}
  || q_r || = 1
\end{equation}

The coordinate transformation from a vector in reference frame A to a
reference frame B, obtained by the rotation of coordinate frame A
around a unit vector $\hat{n}$ can be computed as:

\begin{equation}
   q_B = q_r \cdot q_A \cdot q_r^{-1}
       = q_r \cdot q_A \cdot q_r^{*}
\end{equation}

Where:
  + $q_A$ is the coordinate of a vector in the reference frame A (aka
    coordinate system, aka coordinate frame).
  + $q_B$ is the coordinate of a the same vector in the reference frame B.
  + $q_A = 0 + x_A \cdot i + y_A \cdot j + z_A \cdot k$
  + $q_B = 0 + x_B \cdot i + y_B \cdot j + z_B \cdot k$


The previous rotation coordinate transformation can be determined in a
more efficient way by applying the following algorithm. [[https://fgiesen.wordpress.com/2019/02/09/rotating-a-single-vector-using-a-quaternion/][(The-Ryg-Blog)]]


\begin{eqnarray*}
   v_t &=& 2 ( v_r \times v_a)                    \\
   q_B &=&  v_A + w_r \cdot v_t + v_r \times v_t
\end{eqnarray*}

Where:
  + $[ Q ]_w$ - real part of a quaternion Q
  + $[ Q ]_v$ - vector part of a quaterion Q
  + $w_r = \cos ( \theta / 2)$ - real part of rotation quaternion $q_r$
  + $v_r = \hat{n} \cdot \sin( \theta / 2 )$ - vector part of rotation quaternion $q_r$
  + $v_B$ - vector part of quaternion $q_B$
  + $v_A$ - Vector part of quaternion $q_A$
  + $v_r \times v_a$ - Cross product between those two vectors.


  *Determining rotation and axis from a normalized quaternion*

Supposing that the components (w, x, y, z) of normalized quaternion *q*
are known. The represented rotation angle, $\theta$ and rotation axis
(a.k.a orientation) vector, $\hat{n} = [x_n \quad y_n \quad z_n]$, can
be determined in the following way.

Rotation quaternion (known: x, y, z, w)

\begin{eqnarray*}
  q  &=& w + x \cdot i + y \cdot j + z \cdot k  \\
     &=& \cos \frac{\theta}{2} + \sin \frac{\theta}{2}  \hat{n} \\
     &=& \cos \frac{\theta}{2} + \sin \frac{\theta}{2} (x_n \cdot i + y_n \cdot j + z_n \cdot k) \\
\end{eqnarray*}

Rotation angle $\theta$

\begin{equation}
  \theta  = 2 \text{atan2}( || \vec{v} ||, w  ) = 2 \text{atan2}( \sqrt{x^2 + y^2 + z^2} , w  )
\end{equation}

Rotation axis: $\hat{n} = x_n \cdot i + y_n \cdot j + z_n \cdot k$

\begin{eqnarray*}
   x_n &=& x / \sqrt{1 - w^2} \\
   y_n &=& y / \sqrt{1 - w^2} \\
   z_n &=& z / \sqrt{1 - w^2} \\
\end{eqnarray*}

Where:

  + [[https://en.wikipedia.org/wiki/Atan2][Atan2]](Y, X) is the 2-argument tangent function which determines
    the angle quadrant.

  + $q = w + x \cdot i + y \cdot j + z \cdot k$ - is a unitary
    quaternion, which components x, y and z are assumed to be
    known. The quaternion *q* is presumed to be normalized.

  + $\theta$ is the rotation angle. Assumed to be unknown.

  + $\hat{n}$ - is the rotation axis vector. Also assumed to be
    unknown, which components are $x_n$, $y_n$ and $z_n$.
    * $\hat{n} = 0 + x_n \cdot i + y_n + \cdot j + z_n \cdot k$



  *Quaternion to rotation matrix conversion*

The rotation matrix that transforms coordinates from reference frame A
to reference frame B can be computed as:  [[https://www.weizmann.ac.il/sci-tea/benari/sites/sci-tea.benari/files/uploads/softwareAndLearningMaterials/quaternion-tutorial-2-0-1.pdf][(Moti Ben-Ari - Wizmann)]], [[http://barbic.usc.edu/cs520-s12/quaternions/quaternions-cs520.pdf][(Jernej Barbic)]]

\begin{equation}
   R_n = \begin{bmatrix}

             a^2 + b^2 - c^2 - d^2
          &  2 (b \cdot c -  a \cdot d)
          &  2 (b \cdot d +  a \cdot c)  \\

             2 (b \cdot c +  a \cdot d )
          &  a^2 - b^2 + c^2 -d^2
          &  2 ( c \cdot d -  a \cdot b ) \\

             2 ( b \cdot d -  a \cdot c  )
          &  2 ( c \cdot d +  a \cdot b )
          &  a^2 - b^2 - c^2 + d^2     \\

         \end{bmatrix}
\end{equation}

Where:
  + a, b, c, d are the components of the unit quaternion $q_r$
  + $a = \cos( \theta / 2)$
  + $b = x_n \cdot \sin (\theta / 2)$
  + $c = y_n \cdot \sin (\theta / 2)$
  + $d = z_n \cdot \sin (\theta / 2)$

The matrix $R_n$ can also be simplified to: [[https://www.weizmann.ac.il/sci-tea/benari/sites/sci-tea.benari/files/uploads/softwareAndLearningMaterials/quaternion-tutorial-2-0-1.pdf][(Moti Ben-Ari - Wizmann)]]

\begin{equation}
   R_n = 2 \begin{bmatrix}
              a^2 + b^2 - 1/2  &
              b \cdot c - a \cdot d  &
              a \cdot c + b \cdot d  \\

              a \cdot d + b \cdot c  &
              a^2  + c^2 - 1/2 &
              c \cdot d - a \cdot b  \\

              b \cdot d - a \cdot c &
              a \cdot b + c \cdot d &
              a^2 + d^2 - 1/2 \\

          \end{bmatrix}
\end{equation}

Coordinates from frame A can be turned into coordinates from frame B
by performing the following  matrix multiplication.

\begin{equation}
    \begin{bmatrix} x_B \\ y_B \\ z_B \end{bmatrix}
    = R_n  \begin{bmatrix} x_A \\ y_A \\ z_A \end{bmatrix}
\end{equation}


From the previous matrix $R_n$ it is possible to determine the
yaw-pitch-roll (Z-X-Y) Euler's angles. [[http://www.chrobotics.com/library/understanding-quaternions][(chrobotics)]]

\begin{eqnarray*}
    \phi   &=&  \arctan\left( \frac{ 2 a \cdot b + 2 c \cdot d } { a^2 - b^2 - c^2 + d^2 } \right )  \\
    \theta &=& -\arcsin\left( 2 b \cdot d - 2 a \cdot c  \right )                                    \\
    \psi   &=&  \arctan\left( \frac{ 2 a \cdot d + 2 b \cdot c}{ a^2 + b^2 - c^2 - d^2 } \right )    \\
\end{eqnarray*}

Where:
  + $\phi$   - is the yaw angle
  + $\theta$ - is the pitch angle
  + $\psi$   -is the roll angle


  *Quaternion to homogeneous coordinate rotation matrix*

The matrix $R_n$ can be converted to a homogenous coordinate form,
$T_n$, which is more convenient, since homogenous coordinate allows
expressing translations in a similar fashion to rotation
transformations, just as vector-matrix multiplication.


\begin{equation}
   T_n = \begin{bmatrix}

             a^2 + b^2 - c^2 - d^2
          &  2 (b \cdot c -  a \cdot d)
          &  2 (b \cdot d +  a \cdot c)
          &  0                            \\

             2 (b \cdot c +  a \cdot d )
          &  a^2 - b^2 + c^2 -d^2
          &  2 ( c \cdot d -  a \cdot b )
          &  0                            \\

             2 ( b \cdot d -  a \cdot c  )
          &  2 ( c \cdot d +  a \cdot b )
          &  a^2 - b^2 - c^2 + d^2
          &  0                            \\

             0 & 0 & 0 & 1 \\
         \end{bmatrix}
\end{equation}

Homogeneous coordinates from frame A can be turned into coordinates of
frame B by using the following expression:

\begin{equation}
    \begin{bmatrix} x_B \\ y_B \\ z_B \\ 1 \end{bmatrix}
    = T_n  \begin{bmatrix} x_A \\ y_A \\ z_A \\ 1 \end{bmatrix}
\end{equation}


  *Table with Useful Normalized Quaternions (Rotation Quaternions)* - [[http://wiki.ogre3d.org/Quaternion+and+Rotation+Primer][(Ogre3D Game Engine)]]

|--------------+---------------+---------------+---------------+---+----------------------------------------------|
| w            |             x |             y |             z |   | Description                                  |
|--------------+---------------+---------------+---------------+---+----------------------------------------------|
| 1            |             0 |             0 |             0 |   | Identity quaternion, represents no rotation. |
| 0            |             1 |             0 |             0 |   | 180°  (π radians) rotation around X axis     |
| 0            |             0 |             1 |             0 |   | 180° rotation around Y axis                  |
| 0            |             0 |             0 |             1 |   | 180° rotation around Z axis.                 |
|              |               |               |               |   |                                              |
| $\sqrt{1/2}$ |  $\sqrt{1/2}$ |             0 |             0 |   | 90° rotation around X axis.                  |
| $\sqrt{1/2}$ |             0 |  $\sqrt{1/2}$ |             0 |   | 90° rotation around Y axis.                  |
| $\sqrt{1/2}$ |             0 |             0 |  $\sqrt{1/2}$ |   | 90° rotation around Z axis.                  |
|              |               |               |               |   |                                              |
| $\sqrt{1/2}$ | $-\sqrt{1/2}$ |             0 |             0 |   | -90° rotation around X axis.                 |
| $\sqrt{1/2}$ |             0 | $-\sqrt{1/2}$ |             0 |   | -90° rotation around Y axis.                 |
| $\sqrt{1/2}$ |             0 |             0 | $-\sqrt{1/2}$ |   | -90° rotation around Z axis.                 |
|              |               |               |               |   |                                              |
|--------------+---------------+---------------+---------------+---+----------------------------------------------|

Note:
  + $\sqrt{1/2} = \sqrt{0.5} \approx 0.70710$
  + $\pi = pi = \approx 3.14159265$


  *Further Reading*


Quaternions:

   + [[https://en.wikipedia.org/wiki/Quaternion][Quaternions]] - Wikipedia

   + [[https://en.wikipedia.org/wiki/Quaternions_and_spatial_rotation][Quaternions and spatial rotation]]

   + [[https://en.wikipedia.org/wiki/History_of_quaternions][History of Quaternions]]

   + [[https://arxiv.org/pdf/2009.00425.pdf][An algorithm for dividing quaternions]] [PAPER]

   + [[https://web.archive.org/web/20200211220142/http://www.chrobotics.com/library/understanding-quaternions][Understanding Quaternions]]  - ChRobotics

   + [[http://www.chrobotics.com/library/understanding-quaternions][Understanding Euler Angles]] - ChRobotics

   + [[https://www.energid.com/resources/orientation-calculator][Quaternion Calculator]] - Energid / Actin Advanced Control For Robotics
     + Note: Online calculator for Quaternions.

   + [[https://www.weizmann.ac.il/sci-tea/benari/sites/sci-tea.benari/files/uploads/softwareAndLearningMaterials/quaternion-tutorial-2-0-1.pdf][A Tutorial on Euler Angles and Quaternions]] - Weizmann Institute of Science

   + [[http://wiki.ogre3d.org/Quaternion+and+Rotation+Primer][Quaternion Rotation Primer / Ogre3D Game Engine]]

   + https://web.cs.ucdavis.edu/~amenta/3dphoto/quaternion.pdf

   + [[https://www.mathworks.com/help/aerotbx/ug/quatinterp.html][quatinterp() - Quaternion interpolation between two quaternions]] - Matlab Aerospace Toolbox

   + [[https://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/slerp/index.htm][Maths - Quaternion Interpolation (SLERP)]] - Euclidianspace.com

   + [[https://www.3dgep.com/understanding-quaternions/][Understanding Quaternions]] - 3DGEP - 3D Game Engine Programming

   + [[http://www2.cs.uregina.ca/~anima/408/Notes/Quaternions/Quaternions.htm][Quaternion - in computer animation]]

   + [[https://theory.org/software/qfa/writeup/node12.html][Quaternion interpolation]]

   + [PDF] [[http://web.archive.org/web/20130116192343/https://www.crytek.com/download/izfrey_siggraph2011.pdf][Spherical Skinning withDual-Quaternions and QTangents]]  - Spherical
     Skinning withDual-Quaternions and QTangentsIvo Zoltan FreyCrytek
     R&D

   + [PDF] [[https://web.archive.org/web/20210506185442/https://www.cs.utexas.edu/~fussell/courses/cs384g-spring2016/lectures/7a-Quaternions_Orientation.pdf][Orientation & Quaternions - University of Texas]] 

   + [PDF] [[https://atcm.mathandtech.org/EP/2002/ATCMA107/fullpaper.pdf][Quaternions: From Classical Mechanics to Computer Graphics, and Beyond]]

   + [PDF] [[http://www.space.bas.bg/Aerospace_research/arhiv/n%252028/Articles/8.pdf][QUATERNION-BASED AUTOPILOT FOR DODECACOPTERS - PART I]]

   + [PDF] [[https://web.archive.org/web/20210508223059/https://www.tu-berlin.de/fileadmin/fg169/miscellaneous/Quaternions.pdf][Introduction into quaternions for spacecraft attitude representation]] 

   + https://github.com/mattlisle/quaternion-ukf (Kalman-Filter and Quaternion)
     + "This implementation of a UKF for tracking orientation of a
       drone with gyro and accelerometer data follows closely that
       described in the paper "A Quaternion-based Unscented Kalman
       Filter for Orientation Tracking" by Edgar Kraft."

   + https://stackoverflow.com/questions/tagged/quaternions

   + https://stackoverflow.com/questions/tagged/gyroscope


Dual Quaternion:

   + Dual quaternions are a combination of quaternions and dual
     numbers. Unlike ordinary quaternions, which can only express
     rotation, dual quaternions can express both rotation and
     translation in a more concise and efficient way than
     transformation matrices. Some applications of this mathematical
     construct are: computer graphics; robotics; aerospace engineering
     and more.

   + [[https://en.m.wikipedia.org/wiki/Dual_quaternion][Dual Quaternions]]

   + [[https://doc.magnum.graphics/magnum/transformations.html][Feature guide » 2D and 3D transformations]] - Magnum Engine

   + [PDF] - Paper: [[https://web.archive.org/web/20220212092311/https://cs.gmu.edu/~jmlien/teaching/cs451/uploads/Main/dual-quaternion.pdf][A Beginners Guide to Dual-Quaternions]] - What They
     Are, How They Work, and How to Use Them for 3D Character
     Hierarchies

   + [PDF] - Paper: [[https://web.archive.org/web/20210412220257/https://dcsl.gatech.edu/papers/acc18c.pdf][Modeling of Spacecraft-Mounted Robot Dynamics and Control Using Dual Quaternions]]

   + [PDF] - Paper: [[https://web.archive.org/web/20210617042645/https://www.scss.tcd.ie/publications/tech-reports/reports.06/TCD-CS-2006-46.pdf][Dual Quaternions for Rigid Transformation Blending]]

   + [PDF] - Report: [[https://web.archive.org/web/20211018040425/https://faculty.sites.iastate.edu/jia/files/inline-files/dual-quaternion.pdf][Dual Quaternions - Yan-Bin Jia]]

   + [[https://www.mathworks.com/matlabcentral/fileexchange/39288-dual-quaternion-toolbox][Matalab File Exchange - Dual quaternion toolbox]]


Quaternion Computer Implementations:

   * [[http://docs.ros.org/en/kinetic/api/tf/html/c++/classtf_1_1Quaternion.html][t::Quaternion - ROS Framework]] (C++)

   * [[https://doc.qt.io/qt-5/qquaternion.html][Qt5 C++ Framework - QQuaternion class]] (C++)

   * [[https://doc.qt.io/qt-5/qtdatavisualization-rotations-example.html][Qt5 - Rotation Example]]

   * [[https://ivlab.github.io/MinGfx/classmingfx_1_1_quaternion.html][MinGfx C++ - OpenGL Wrapper]] (C++)

   * [[https://docs.unrealengine.com/en-US/API/Runtime/Core/Math/FQuat/index.html][FQuat]] - Unreal Game Engine (C++)
     + Brief: "Floating point quaternion that can represent a rotation
       about an axis in 3-D space."

   * [[https://web.archive.org/web/20210507012307/https://docs.unrealengine.com/en-US/API/Runtime/Core/Math/FTransform/index.html][FTransform]] - Unreal Game Engine (C++)
     + Brief: "Transform composed of Scale, Rotation (as a
       quaternion), and Translation."
     + Note: It has three member variables: Rotation (class FQaut),
       Scale3D (FVector) and Translation (FVector).

   * [[https://web.archive.org/web/20210507005525/https://docs.unrealengine.com/en-US/API/Runtime/Core/Math/FRotator/index.html][FRotator]]  - Unreal Gamer Engine (C++)
     + Brief: "Implements a container for rotation information. All rotation
       values are stored in degrees."

   * [[https://docs.unity3d.com/ScriptReference/Quaternion.html][Unity3D - Quaternion]] (C#, CSharp - Unity Game Engine)
     * Brief: "Quaternions are used to represent rotations. They are
       compact, don't suffer from gimbal lock and can easily be
       interpolated. Unity internally uses Quaternions to represent
       all rotations. They are based on complex numbers and are not
       easy to understand intuitively. You almost never access or
       modify individual Quaternion components (x,y,z,w); most often
       you would just take existing rotations (e.g. from the
       Transform) and use them to construct new rotations (e.g. to
       smoothly interpolate between two rotations). The Quaternion
       functions that you use 99% of the time are:
       Quaternion.LookRotation, Quaternion.Angle, Quaternion.Euler,
       Quaternion.Slerp, Quaternion.FromToRotation, and
       Quaternion.identity. (The other functions are only for exotic
       uses.)"

   * [[https://doc.magnum.graphics/magnum/classMagnum_1_1Math_1_1Quaternion.html][Magnum Graphics Library - Quaternion]] (C++)

   * [[https://docs.microsoft.com/en-us/dotnet/api/system.numerics.quaternion?view=net-5.0][Quaternion Struct]] - Dotnet (C#)

   * [[https://octave.sourceforge.io/quaternion/overview.html][Octave - Quaternion Library]] (OCtave Language, akin to Matlab)

   * [[https://docs.blender.org/api/2.33/Mathutils-module.html][Blender - MathUtils module]]

*** Quaternions - Combining Scale and Position

Quaternions (non-dual quaternions) can only encode and represent
rotations around some orientation, which is not enough to specify
position and scaling. As a result, quaternions rotation matrices need
to be concatenated with translation and scaling transform matrices for
computing an object's model transform matrix, which is often passed to
a shader program via uniform variables.

Assuming that a rendered object has a transform member variable of
type RendererTransform containing a a quaternion, for representing
rotation; a scale vector and a position vector. The method
get_transform() compute the rendered object transform matrix, which is
the concatenation of rotation (quaternion), scaling and translation
transforms. The model transform determined by get_transform() method
can be passed to a shader uniform variable.

Note: Dual-quaternions, which are a combination of _dual-numbers_ and
ordinary quaternions, can express both rotation and translation.


 + Class RendererTransform:


#+BEGIN_SRC cpp
   struct RenderTransform
   {
         // Rotation quaternion
         // Set to identity quaternion by default.
         // Members. The quaternion has the fields (w, x, y, z)
         quaternion rotation {1.0, 0.0, 0.0};

         // Scale vector containing scales (sx, sy, sz) of axis (X, Y, Z)
         vec3 scale(1.0, 1.0, 1.0);

         // Contains object's position
         vec3 position(tx, ty, tz);

         // ??? Get overall (end result) transform matrix that
         // will be passed to shader uniform variable.
         mat4 get_transform() const { ????? }

         void translate(float dx, float dy, float dz)
         {
             this->position.x += dx;
             this->position.y += dy;
             this->position.z += dz;
         }

         void set_position(float x, float y, float z)
         {
             this->position.x = x;
             this->position.y = y;
             this->position.z = z;
         }

         void set_scale(float sx float sy, float sz)
         {
            this->scale.x = sx;
            this->scale.y = sy;
            this->scale.z = sz;
         }
   };
#+END_SRC

The transforms are often applied in the following sequence, first
scaling, then rotation and finally translation.

\begin{equation}
   T = T_t \cdot  T_r \cdot T_s = \text{Translation} \cdot \text{Rotation} \cdot \text{Scaling}
\end{equation}

Where:
  + T is the total transform matrix, which is the product between
    intermediate transforms and later can be passed to a shader
    uniform variable.
  + $T_s$ - is the scale transform, determined from the scale vector.
  + $T_t$ - is the translation transform, computed from the position vector.
  + $T_r$ - is the quaternion rotation transform.


Intermediate transforms:

 + Translation affine transform form position vector.

\begin{equation}
  T_t =
   \begin{bmatrix}
     1   & 0   &  0 &  t_x \\
     0   & 1   &  0 &  t_y \\
     0   & 0   &  1 &  t_z \\
     0   & 0   &  0 &   1  \\
   \end{bmatrix}
\end{equation}

 + Scaling affine transform from scale vector.

\begin{equation}
  T_s =
   \begin{bmatrix}
     s_x   & 0     &  0   &  0 \\
     0     & s_y   &  0   &  0 \\
     0     & 0     &  s_z &  0 \\
     0     & 0     &  0   &  1  \\
   \end{bmatrix}
\end{equation}

 + Rotation affine transform obtainted from rotation quaternion.
   + Note: most linear algebrar or computer graphics math libraries
     provide subroutines (aka functions) or class methods for
     converting quaternions to 4x4 rotation matrices. Those libraries
     also provide subroutines for determining a quaternion's yaw,
     pitch and roll angles and constructing the quaternion from yaw,
     pitch and roll angles.

\begin{equation}
  T_r =
   \begin{bmatrix}
     a_{11}   & a_{12}   &  a_{13} & 0  \\
     a_{21}   & a_{22}   &  a_{23} & 0  \\
     a_{32}   & a_{32}   &  a_{33} & 0  \\
     0        & 0        &  0      & 1  \\
   \end{bmatrix}
\end{equation}

The end transform T is determined by concatenating all intermediate
transforms:

\begin{equation}
     T = T_t \cdot  T_r \cdot T_s
\end{equation}


\begin{equation}
     T =
  \begin{bmatrix}
     1   & 0   &  0 &  t_x \\
     0   & 1   &  0 &  t_y \\
     0   & 0   &  1 &  t_z \\
     0   & 0   &  0 &   1  \\
   \end{bmatrix}

   \begin{bmatrix}
     a_{11}   & a_{12}   &  a_{13} & 0  \\
     a_{21}   & a_{22}   &  a_{23} & 0  \\
     a_{31}   & a_{32}   &  a_{33} & 0  \\
     0        & 0        &  0      & 1  \\
   \end{bmatrix}

   \begin{bmatrix}
     s_x   & 0     &  0   &  0 \\
     0     & s_y   &  0   &  0 \\
     0     & 0     &  s_z &  0 \\
     0     & 0     &  0   &  1  \\
   \end{bmatrix}
\end{equation}

Finally, the model transform becomes:

\begin{equation}

  T =
   \begin{bmatrix}
     s_x \cdot a_{11} & s_y \cdot a_{12}  & s_z \cdot a_{13}  &  t_x \\
     s_x \cdot a_{21} & s_y \cdot a_{22}  & s_z \cdot a_{23}  &  t_y \\
     s_x \cdot a_{31} & s_y \cdot a_{32}  & s_z \cdot a_{33}  &  t_z \\
      0               &   0               &   0               &   1  \\
   \end{bmatrix}

\end{equation}

Then, the algorithm for determing the object model matrix, designated
by the transform T, could be written as the following pseudo-code:

#+BEGIN_SRC cpp
   mat4 RenderTransform::get_transform() const
   {
       // Convert quaternion to a 4x4 rotation matrix (affine transform)
       mat4 model_transform = quaternion_to_rotation_transform( this->quaternion );

       // Multiply all elements of first column by X axis scale
       model_transform.comlumn[0] = scale.x * model_transform.column[0];
       // Multiply all elemnts of second column by Y axis scale
       model_transform.comlumn[1] = scale.y * model_transform.column[1];
       // Multiply all elemnts of third column by Z axis scale
       model_transform.comlumn[2] = scale.z * model_transform.column[2];

       // Set all elements of forth column as the 4x1  column vector
       // column4 = [position.x position.y position.z 1.0 ]
       model_transform.column[4] = vec4( position.x, position.y, position.z, 1.0f );

       return model_transform;
   }
#+END_SRC

To rotate the rendered object, it is only necessary to multiply the
quaternion variable, called 'rotation' by another quaternion. So, a
C++-like pseudo-code for setting or adding a new rotation could be
written as:

#+BEGIN_SRC cpp
   RenderTransform object_transform;

   // Set a new rotation - overriding the old one.
   void RenderTransform::set_rotation(float angle_radians, vec3 axis_direction)
   {
       // Normalize axis
       vec3 axis = axis_direction.normalize();
       // Make explicit assumption the assumption that the quaternion is normalized.
       assert( norm(axis) ~= 1.0  );
       this->object_transform.quaternion = make_quaternion_from_AngleAxis(angle, axis);
   }

   // Add a new rotation, rotate the previous quaternion.
   void RenderTransform::rotate(float angle_radians, vec3 axis_direction)
   {
       vec3 axis = axis_direction.normalize();
       assert( norm(axis) ~= 1.0  );
       quaternon q = make_quaternion_from_AngleAxis(angle, axis);
       this->object_transform.quaternion = this->object_transform.quaternion * q;
   }


   // Set a new Yaw-Pitch-Roll Euler's angles rotation.
   void RenderTransform::set_rotation_YRP(float yaw, float pitch, float roll)
   {
       vec3 axis = axis_direction.normalize();
       assert( norm(axis) ~= 1.0  );
       quaternon q = make_quaternion_from_YAW_PITCH_ROLL(yaw, pitch, roll);
       assert( norm(q) ~= 1.0 );
       this->object_transform.quaternion = q;
   }
#+END_SRC

 *See also:*

  + [[https://tech.metail.com/performance-quaternions-gpu/][Performance of quaternions in the GPU]] - Metail Tech

  + [[https://stackoverflow.com/questions/8594102/matrix-multiply-with-position-quaternion-and-scale-components][c++ - Matrix multiply with position, quaternion and scale components - Stack Overflow]]

  + [[https://www.gamasutra.com/view/feature/131686/rotating_objects_using_quaternions.php][Rotating Objects Using Quaternions]] - Noick Bobic / Gamasutra

*** Quaternion in Julia Language

It is worth prototyping quaternion math in a numerically-oriented programming
language such as [[https://en.wikipedia.org/wiki/Julia_(programming_language)][Julia Language]] is helpful for understanding and
validating the inner works of quaternion computations. This
implementation of quaternion math represents quaternion as an array of
four elements and uses a matrix representation for implemeting
quaternion multiplication.

#+BEGIN_SRC julia
   # Struct for storing quaternion
   # q = w + x·i + y·j + z·k
   struct quatp
      arr::Array{Float64,1}
   end

   function quat(x, y, z, w)
     return quatp([x ; y; z; w])
   end

   # Turns a quaternion into 4x4 matrix representation
   function quat_to_mat4x4(q::quatp)
       w, x, y, z  = q.arr
       mat = [  w -x -y -z
              ; x  w -z  y
              ; y  z  w -x
              ; z -y x w ]
       return mat
    end

   function Base.getproperty(q::quatp, sym::Symbol)
       if sym == :arr
           return getfield(q, :arr)
       elseif sym == :w
           return getfield(q, :arr)[1]
       elseif sym == :x
           return getfield(q, :arr)[2]
       elseif sym == :y
           return getfield(q, :arr)[3]
       elseif sym == :z
           return getfield(q, :arr)[4]
       elseif sym == :v
           # Return only the quaternion (vector part)
           return getfield(q, :arr)[2:end]
       end
   end

   # Norm - quaternion magnitude, akin to vector norm
   function norm(q::quatp)
      w, x, y, z = q.arr
      return sqrt(w * w + x * x + y * y + z * z)
   end

   # Conjugate of a quaternion
   function conj(q::quatp)
     return quat(q.w, -q.x, -q.y, -q.z)
   end

   # Inverse of quaternion q^-1
   function Base.:inv(q::quatp)
      w, x, y, z = q.arr
      norm_square =  w * w +  x * x + y * y + z * z
      return  quat(w, -x, -y, -z) / norm_square
   end

   # Quaternion sum
   Base.:+(q1::quatp, q2::quatp) = quat(q1.w + q2.w, q1.x + q2.x, q1.y + q2.y, q1.z + q2.z)
   Base.:-(q1::quatp, q2::quatp) = quat(q1.w - q2.w, q1.x - q2.x, q1.y - q2.y, q1.z - q2.z)

   Base.:+(q::quatp,   w::Float64) = quat(w + q.w, q.x, q.y, q.z)
   Base.:+(w::Float64, q::quatp  ) = quat(w + q.w, q.x, q.y, q.z)
   Base.:-(q::quatp,   w::Float64) = quat(q.w - w, q.x, q.y, q.z)

   # Mutliplication => scalar X quaternion
   Base.:*(a::Float64, q::quatp) = quat( a * q.w, a * q.x, a * q.y, a * q.z)
   Base.:*(q::quatp, a::Float64) = quat( a * q.w, a * q.x, a * q.y, a * q.z)

   Base.:/(q::quatp, a::Float64) = quat( q.w / a, q.x / a, q.y / a, q.z / a)

   # Quaternion multiplication / product
   Base.:*(q1::quatp, q2::quatp) = begin
        A = quat_to_mat4x4(q1)
        return quatp( A * q2.arr )
   end

   # Unit quaternions i, j, k
   const i = quat(0.0, 1.0, 0.0, 0.0)
   const j = quat(0.0, 0.0, 1.0, 0.0)
   const k = quat(0.0, 0.0, 0.0, 1.0)


   # Obtain a rotation quaternion that represents a rotation
   # by some angle around some direction (normalized vector 'vec')
   # => The angle is given in degrees.
   function rot_quat(angle::Float64, vec)
      # Normalize vector vec and store the result in v
      xv, yv, zv = vec
      x, y, z = vec / sqrt( xv * xv + yv * yv + zv * zv )
      S = cosd(angle / 2)
      C = sind(angle / 2)
      return quat(C, S * x, S * y, S * z)
   end

   ## Vector cross product VA x VB of two column vectors (3 x 1)
   function cross(va, vb)
        x, y, z = va
        T = [ 0 -z y ; z 0 -x; -y x 0]
        T * vb
    end

   ## Apply a rotation quaternion to a vector (column vector 3 x 1)
   ##  =>> Vector 3 rows and 1 column
   ## Algorithm from:
   #     =>> https://fgiesen.wordpress.com/2019/02/09/rotating-a-single-vector-using-a-quaternion/
   function apply_rot(qr::quatp, va)
      wr = qr.w # Scalar/real part of rotation quaternion
      vr = qr.v # Vector part of rotation quaternion
      vt = 2 * cross(vr, va)
      vb = va + wr * vt + cross(vr, vt)
      return vb
   end

   # Obtain a rotation matrix - that represents rotation
   # around an arbitrary axis (vector)
   # Note: The angle is should be in degrees.
   #
   function make_rotmat(angle::Float64, vec)
      # Normalize vector vec and store components in x, y, z
      x, y, z = vec
      x, y, z = vec / sqrt( x * x + y * y + z * z )
      # sin and cos caching
      C = cosd(angle / 2)
      S = sind(angle / 2)
      # Parameters a, b, c, d => Components of the rotation
      # quaternion
      a = C
      b = x * S
      c = y * S
      d = z * S
      # rotation matrix
      mat = [
           ( a^2 + b^2 - c^2 - d^2 )  ( 2 * (b * c - a * d )  ) ( 2 * (b * d + a * c)   )  0
        ;  ( 2 * (b * c + a * d)   )  ( a^2 - b^2 + c^2 - d^2 ) ( 2 * (c * d - a * b)   )  0
        ;  ( 2 * (b * d - a * c)   )  ( 2 * (c * d + a * b)   ) ( a^2 - b^2 - c^2 + d^2 )  0
        ;  0                          0                         0                          1
      ]
      return mat
   end
#+END_SRC

Quaternion components:

#+BEGIN_SRC julia
  # Construct a quaternion

  julia> q = quat(10.5, -25.1, 4.5, 5.6)
  quatp([10.5, -25.1, 4.5, 5.6])

  julia> q.w # Real part
  10.5

  julia> q.x # Imaginary part - i axis component
  -25.1

  julia> q.y # Imaginary part - j axis component
  4.5

  julia> q.z # Imaginary part - k axis component (Z axis)
  5.6

  julia> q.arr # Internal array data representation
  4-element Array{Float64,1}:
    10.5
   -25.1
     4.5
     5.6

  julia> q.v # Vector component (i, j, k) components only
  3-element Array{Float64,1}:
   -25.1
     4.5
     5.6
#+END_SRC

Quaternion conjugate and inverse:

#+BEGIN_SRC julia
   julia> q = quat(1.0, 2.0, 3.0, 4.0)
   quatp([1.0, 2.0, 3.0, 4.0])

   julia> inv(q)
   quatp([0.03333333333333333, -0.06666666666666667, -0.1, -0.13333333333333333])

   # Expected (-1.0)
   julia> q * inv(q)
   quatp([1.0, 0.0, 0.0, 0.0])

   # Expected (-1.0)
   julia> inv(q) * q
   quatp([1.0, -5.551115123125783e-17, -6.938893903907228e-18, 5.551115123125783e-17])
#+END_SRC

Normalize a quaternion:

#+BEGIN_SRC julia
  # Expected normalized to be: (w = 0.18257, x = 0.36515, y = 0.54772, z = 0.7303)
  # Test case from: Matlab Robotics Toolbox - https://www.mathworks.com/help/robotics/ref/quaternion.normalize.html

   julia> q = quat(1.0, 2.0, 3.0, 4.0)
   quatp([1.0, 2.0, 3.0, 4.0])

   julia> qn = normalize(q)
   quatp([0.18257418583505536, 0.3651483716701107, 0.5477225575051661, 0.7302967433402214])

   julia> norm(qn)
   0.9999999999999999

#+END_SRC

Test case 1 - for quaternion multiplication:

  + Test quaternion laws:
  + Expected: i * i = j * j = k * k = -1
  + Expected: i * j = k
  + Expected: j * k = i
  + Expected: k * i = j
  + Expected: j * i = -k

#+BEGIN_SRC julia
   # ---- Show components i, j, k --------#

   julia> i
   quatp([0.0, 1.0, 0.0, 0.0])

   julia> j
   quatp([0.0, 0.0, 1.0, 0.0])

   julia> k
   quatp([0.0, 0.0, 0.0, 1.0])

   # ----- Check multiplication of i, j, k by themselves -----------#
   #

   julia> i * i # Expected -1 or (-1) + 0·i + 0·j + 0·k
   quatp([-1.0, 0.0, 0.0, 0.0])

   julia> j * j # Expected -1
   quatp([-1.0, 0.0, 0.0, 0.0])

   julia> k * k # Expected -1
   quatp([-1.0, 0.0, 0.0, 0.0])

   #------- Check multiplication i * j, j * k, k * i and j * i
   #
   julia> i * j # Expected k
   quatp([0.0, 0.0, 0.0, 1.0])

   julia> j * i # Expected -k
   quatp([0.0, 0.0, 0.0, -1.0])

   julia> k * i # Expected j
   quatp([0.0, 0.0, 1.0, 0.0])

   julia> i * k # Expected -j
   quatp([0.0, 0.0, -1.0, 0.0])

#+END_SRC

Test case 2 - for quaternion multiplication:

  + From:  [[https://www.mathworks.com/help/aerotbx/ug/quatmultiply.html][Matlab Aerospace Toolbox]]
  + q1 = [ 1.0 0.0 1.0 0.00 ]
  + q2 = [ 1.0 0.5 0.5 0.75 ]
  + Expected result =>> q1 * q2 = [ 0.5 1.25 1.5 0.25 ]

#+BEGIN_SRC julia
  # -------- Test A ------------------------#
  julia> q1 = quat(1.0, 0.0, 1.0, 0.0)
  quatp([1.0, 0.0, 1.0, 0.0])

  julia> q2 = quat(1.0, 0.5, 0.5, 0.75)
  quatp([1.0, 0.5, 0.5, 0.75])

  # ------- Test B ---------------------#

  julia> q1 = 1.0 + 0.0i + 1.0j + 0.0k
  quatp([1.0, 0.0, 1.0, 0.0])

  julia> q2 = 1.0 + 0.5i + 0.5j + 0.75k
  quatp([1.0, 0.5, 0.5, 0.75])

  julia> q1 * q2
  quatp([0.5, 1.25, 1.5, 0.25])
#+END_SRC

Test case 3 - for quaternion multiplication:

  + From: [[https://www.maplesoft.com/applications/view.aspx?SID=4886&view=html][Maplesoft - Quaternion]]
  + q1 = ( -6) +  (-8)i +  (-5)j +     7k
  + q2 = (-14) + (-12)i + (-19)j + (-17)k
  + Expected: q1 * q2 = 12 +   402i + (-36)j +   96k
  + Expected: q2 * q1 = 12 + (-34)i +   404j + (-88)k
  + Expected: q2 + q1 = (-20) + (-20)i + (-24)j + (-10)k

#+BEGIN_SRC julia
  julia> q1 = -6.0 + -8.0i + -5.0j + 7.0k
  quatp([-6.0, -8.0, -5.0, 7.0])

  julia> q2 = -14.0 + -12.0i + -19.0j + -17.0k
  quatp([-14.0, -12.0, -19.0, -17.0])

  julia> q1 * q2
  quatp([12.0, 402.0, -36.0, 96.0])

  julia> q2 * q1
  quatp([12.0, -34.0, 404.0, -88.0])

  julia> q1 + q2
  quatp([-20.0, -20.0, -24.0, -10.0])

  julia> q2 + q1
  quatp([-20.0, -20.0, -24.0, -10.0])


#+END_SRC

 *Test algorithm - make_rotmat* that computes rotation matrices around
 an arbitrary axis:

Define functions for computing rotation matrices:

#+BEGIN_SRC julia
   # Rotation around X axis
   function rot_x(angle::Float64)
      C = cosd(angle)
      S = sind(angle)
      return [  1   0   0  0
              ; 0   C  -S  0
              ; 0   S   C  0
              ; 0   0   0  1
             ]
   end

   # Rotation around X axis
   function rot_y(angle::Float64)
      c = cosd(angle)
      s = sind(angle)
      return [  c   0   s  0
              ; 0   1   0  0
              ;-s   0   c  0
              ; 0   0   0  1
             ]
   end

   # Returns homogeneous rotation matrix around Z axis
   # => The angle must be in degrees, not radians
   function rot_z(angle::Float64)
      c = cosd(angle)
      s = sind(angle)
      return [  c  -s  0  0
              ; s   c  0  0
              ; 0   0  1  0
              ; 0   0  0  1
             ]
   end
#+END_SRC

Test rotation matrices:

#+BEGIN_SRC julia
   const axis_x = [1.0 0.0 0.0]';
   const axis_y = [0.0 1.0 0.0]';
   const axis_z = [0.0 0.0 1.0]';

   # -------- Rotation around Z axis ---------- #
   #

   julia> rot_z(90.0)
   4×4 Array{Float64,2}:
    0.0  -1.0  0.0  0.0
    1.0   0.0  0.0  0.0
    0.0   0.0  1.0  0.0
    0.0   0.0  0.0  1.0

   julia> make_rotmat(90.0, axis_z)
   4×4 Array{Float64,2}:
    0.0  -1.0  0.0  0.0
    1.0   0.0  0.0  0.0
    0.0   0.0  1.0  0.0
    0.0   0.0  0.0  1.0


   julia> rot_z(45.0)
   4×4 Array{Float64,2}:
    0.707107  -0.707107  0.0  0.0
    0.707107   0.707107  0.0  0.0
    0.0        0.0       1.0  0.0
    0.0        0.0       0.0  1.0

   julia> make_rotmat(45.0, axis_z)
   ]4×4 Array{Float64,2}:
    0.707107  -0.707107  0.0  0.0
    0.707107   0.707107  0.0  0.0
    0.0        0.0       1.0  0.0
    0.0        0.0       0.0  1.0

   julia> rot_z(125.0)
   4×4 Array{Float64,2}:
    -0.573576  -0.819152  0.0  0.0
     0.819152  -0.573576  0.0  0.0
     0.0        0.0       1.0  0.0
     0.0        0.0       0.0  1.0

   julia> make_rotmat(125.0, axis_z)
   4×4 Array{Float64,2}:
    -0.573576  -0.819152  0.0  0.0
     0.819152  -0.573576  0.0  0.0
     0.0        0.0       1.0  0.0
     0.0        0.0       0.0  1.0


   # ----- Rotation around X axis ------------#
   #
   julia> rot_x(0.0)
   4×4 Array{Float64,2}:
    1.0  0.0   0.0  0.0
    0.0  1.0  -0.0  0.0
    0.0  0.0   1.0  0.0
    0.0  0.0   0.0  1.0

   julia> make_rotmat(0.0, axis_x)
   4×4 Array{Float64,2}:
    1.0  0.0  0.0  0.0
    0.0  1.0  0.0  0.0
    0.0  0.0  1.0  0.0
    0.0  0.0  0.0  1.0


   julia> rot_x(45.0)
   4×4 Array{Float64,2}:
    1.0  0.0        0.0       0.0
    0.0  0.707107  -0.707107  0.0
    0.0  0.707107   0.707107  0.0
    0.0  0.0        0.0       1.0

   julia> make_rotmat(45.0, axis_x)
   4×4 Array{Float64,2}:
    1.0  0.0        0.0       0.0
    0.0  0.707107  -0.707107  0.0
    0.0  0.707107   0.707107  0.0
    0.0  0.0        0.0       1.0


   julia> rot_x(90.0)
   4×4 Array{Float64,2}:
    1.0  0.0   0.0  0.0
    0.0  0.0  -1.0  0.0
    0.0  1.0   0.0  0.0
    0.0  0.0   0.0  1.0

   julia> make_rotmat(90.0, axis_x)
   4×4 Array{Float64,2}:
    1.0  0.0   0.0  0.0
    0.0  0.0  -1.0  0.0
    0.0  1.0   0.0  0.0
    0.0  0.0   0.0  1.0

#+END_SRC

Test rotation quaternions:

#+BEGIN_SRC julia
  # Rotation around X axis
  function rot_xx(angle::Float64)
     C = cosd(angle)
     S = sind(angle)
     return [ 1  0  0  ; 0   C  -S   ; 0   S   C     ]
  end

  julia> va = [6.0 10.5 2.56]'
  3×1 LinearAlgebra.Adjoint{Float64,Array{Float64,2}}:
    6.0
   10.5
    2.56

  angle = 60.0 # 60 degrees

  julia> vb_rotmat = rot_xx(angle) * va
  3×1 Array{Float64,2}:
    6.0
    3.032974966311837
   10.373266739736605

  # Rotation quaternion that represents rotation of 60 degrees around X axis.
  julia> qr = rot_quat(60.0, [1.0 0.0 0.0]')
  quatp([0.8660254037844386, 0.5, 0.0, 0.0])

  # Apply transform to vector va
  julia> vb_qr = apply_rot(qr, va)
  3×1 Array{Float64,2}:
    6.0
    3.032974966311837
   10.373266739736607

#+END_SRC
** Drawing Primitives

OpenGL can draw only points, lines, triangles and quads as
primitives. Complex models, solids, surfaces and curves can be drawn
combining those primitives.

Drawing is perfomed by using the _glDrawArrays()_ subroutine, that draws
using the vertices from the current bound VBO - Vertex Buffer Object
or VAO - Vertex Array Object.

Primitives:

  + _GL_POINTS_ => Draw a point for each vertex.

  + _GL_LINES_ => Unconnected lines - useful for drawing grids and
    coordinate axis. Each two vertices are considered the beginning
    and the end of the current line. For instance, if there are 6
    vertices, V0, V1, V2, V3, V4, V5. The primitive GL_LINES, will
    result in the following lines: Line(V0, V1) - line  from V0 to V1;
    Line(V2, V3); Line(V4, V5).

  + _GL_LINE_STRIP_  => Draw connected lines for each pair of
    consecutive vertices. For each, if there is a set of 4 vertices,
    named V0, V1, V2, V3. This primitive will draw the following
    lines: Line(V0, V1) - line from V0 to V1; Line(V1, V2) and
    Line(V2, V3). q

  + _GL_LINE_LOOP_ => Similar, to GL_LINE_STRIP primitive, although this
    primitive draws a line between the first and the last vertex. This
    primitive is suitable for drawing poligons.

  + _GL_TRIANGLES_ => Draws a triangle for each three vertices. This
    primitive is useful for drawing surfaces and solids. Any complex
    OpenGL surface can be approximated using triangles. This primitive
    draws a triangle for each three vertices. If there is a set of
    vertices V0, V1, V2, V3, V4 and V5, the primitive will draw the
    following triangles Triangle(V0, V1, V2) and Triangle(V3, V4,
    V5).

  + _GL_TRIANGLE_FAN_

  + _GL_TRIANGLE_STRIP_


#+BEGIN_SRC cpp
   glBindVertexArray(my_vao);

   // Draw points lines - using the vertices coordinates from VAO 'my_vao'.
   // (current bounded VAO)
   glDrawArrays(GL_POINTS, 0, n_vertices);


   // Draw unconnected lines - using the vertices coordinates from VAO 'my_vao'.
   glDrawArrays(GL_LINES, 0, n_vertices);

   // Draw connected lines - using the vertices coordinates from VAO 'my_vao'.
   // (current bounded VAO)
   glDrawArrays(GL_LINE_STRIP, 0, n_vertices);
#+END_SRC

** Library GLM - OpenGL Mathematics
*** Overview

The library GLM (OpenGL math library) contains many classes and
subroutines for computer graphics computations, such as: homogeneous
coordinates; quaternios; 1D, 2D, 3D and homogeneous coordinates
vectors; vector-matrix operations and so on. Aside those facilities,
the library also provides the subroutines _glm::lookAt()_, for computing
view matrix transform, that turns world coordinates into camera
coordinates; _glm::perspective()_ - for computing the projection matrix,
that turns camera coordinates into clip-space coordinates (NDC
coordinates with range -1.0 to 1.0) and also _glm::ortho_ for computing
the orthogonal perspective matrix. The GLM library is designed with
syntax based on GLSL (OpenGL shading language).

Web Site:

  + [[https://glm.g-truc.net/0.9.9/index.html][OpenGL Mathematics]]

Repository:

  + https://github.com/g-truc/glm

Other Documentations:

 + https://openframeworks.cc/documentation/glm/


  *Type signature of most relevant GLM functions:*

#+BEGIN_SRC cpp

   // Converts an input angle in degrees to radians.
   //
   //  angle_radians = angle_degrees * (PI / 180.0 )
   //
   float glm::radians(foat  degrees);

   // ------ Basic Matrix Transformation =>> Useful for model matrix ---------------//
   glm::mat4 glm::rotate   ( glm::mat4 const & m, float angle, glm::vec3 const & axis );
   glm::mat4 glm::scale    ( glm::mat4 const & m, glm::vec3 const & factors           );
   glm::mat4 glm::translate( glm::mat4 const & m, glm::vec3 const & translation       );


   // ---- Camera View matrix and Camera's matrix -------------//
   glm::mat4 glm::lookAt( glm::vec3 const & eye, glm::vec3 const & look, glm::vec3 const & up );
   glm::mat4 glm::ortho( float left, float right, float bottom, float top, float near, float far );
   glm::mat4 glm::ortho( float left, float right, float bottom, float top );
   glm::mat4 glm::frustum( float left, float right, float bottom, float top, float near, float far );
   glm::mat4 glm::perspective( float fovy, float aspect, float near, float far);
#+END_SRC

 *Subroutines for vector computations*

  * glm::normalize()
    + => Normalize a vector.

  * glm::dot(vec1, vec2)
    + => Vector dot product.

  * glm::length(vec)
    + => Vector norm, aka magnitude.

  * glm::distance(vec1, vec2)
    + => Distance between two vectors thatrepresents position.

  * glm::cross(vec1, vec2)
    + => Returns the cross product between two vectors.

 *Subroutine for Camera Implementation*

Camera's View Matrix:

  * glm::mat4 _glm::lookAt_ (eye, look, up );
    + Brief: Return *View matrix transform* - which transforms world coordinates to camera's space coordinates.
    + eye  => Vector containing the camera's current position in world coordinates.
    + look => Vector (Position) to where camera is looking at.
    + up   => Camera orientation. Suitable fault value Y axis or (x =  0, y = 1, z = 0).

Camera's Projection Matrix:

  * glm::mat4 _glm::perspective_ ( fovy, float aspect, near, far);
    + Returns projection matrix that transforms camera-space
      coordinates to clip-space coordinates (NDC - Normalized Device
      Coordinates within range -1.0 to 1.0). Anything out of the _near_,
      _far_ range is not visible.
    + fovy => Field view angle in radians.
    + near => Distance to near plane.
    + far  => Distance to far plane.

  * glm::mat4 _glm::ortho_ ( left, right, bottom, top );
    + Returns a projection matrix for ortographic projection. This
      type projection is most suitable for 2D drawing; CAD - Computer
      Aided Design; Technical drawing view and on.


 *GLM functions - transform matrix multiplication order*

Consider the subroutine, glm::translate() - which translate a
coordinate system.

#+BEGIN_SRC
   glm::mat4 SOURCE_MATRIX = something();
   glm::mat4 OUTPUT_MATRIX =  glm::translate( SOURCE_MATRIX, glm::vec3(DX, DY, DZ));
#+END_SRC

GLM computes the OUTPUT_MATRIX by performing the matrix multiplication
in the following way. The same logic is applicable to the subroutines:
glm::rotate, glm::scale and so on.

#+BEGIN_SRC cpp
    OUTPUT_MATRIX = SOURCE_MATRIX * Translate_Transform(DX, DY, DZ)

                                    | 1  0  0  DX |
    OUTPUT_MATRIX = SOURCE_MATRIX * | 0  1  0  DY |
                                    | 0  0  1  DZ |
                                    | 0  0  0  1  |
#+END_SRC

Consider the following sequence of operations:

#+BEGIN_SRC cpp
    const glm::vec4 axis_Z = glm::vec3(0, 0, 1);

   // Reset model matrix to identity matrix
    glm::mat4 model(1.0);

    // Move to (X, Y) position
    // model = model * T_translate(_x, y, 0.0)
    // model = identity * T_translate(_x, y, 0.0) = T_translate(_x, y, 0.0)
    model = glm::translate( model, glm::vec3(_x, _y, 0.0)  );

    // Scale object (increase or decrease object size)
    // model = model * T_scale
    // model = T_translate * T_scale
    model =  glm::scale( model, glm::vec3(_scale, _scale, _scale) );

    // Rotate from a given angle around Z axis at current object X, Y  postion
    // model = model * T_rotation
    // model =  (T_translate * T_scale) * T_rot
    model = glm::rotate( model, glm::radians(_angle),  axis_Z);
#+END_SRC

The final value of the model matrix, is product between the following
affine transforms:

#+BEGIN_SRC text
   model  =  Identity * T_translate * T_scale * T_rotation
   model  =  T_translate * T_scale * T_rotation
#+END_SRC

*** Testing in CERN's Root REPL

  *Download Library Source*

#+BEGIN_SRC sh
   $ mkdir -p /tmp/temp && cd /temp

   # Download source code archive
   $ >> curl -o glm.zip -L https://github.com/g-truc/glm/archive/master.zip

   # Extract code
   $ >> unzip  glm.zip

   # Enter the in the extracted directory
   $ >> cd glm-master/

   # List directory content
   $ >> ls
   cmake/  CMakeLists.txt  copying.txt  doc/  glm/  manual.md  readme.md  test/  util/
#+END_SRC

 *Load the library in CERN's ROOT repl*

#+BEGIN_SRC cpp
    $ >> ~/Applications/root/bin/root
   ERROR in cling::CIFactory::createCI(): cannot extract standard library include paths!
   Invoking:
     LC_ALL=C ccache  -O2 -DNDEBUG -xc++ -E -v /dev/null 2>&1 | sed -n -e '/^.include/,${' -e '/^ \/.*++/p' -e '}'
   Results was:
   With exit code 0
      ------------------------------------------------------------------
     | Welcome to ROOT 6.22/02                        https://root.cern |
     | (c) 1995-2020, The ROOT Team; conception: R. Brun, F. Rademakers |
     | Built for linuxx8664gcc on Aug 17 2020, 12:46:52                 |
     | From tags/v6-22-02@v6-22-02                                      |
     | Try '.help', '.demo', '.license', '.credits', '.quit'/'.q'       |
      ------------------------------------------------------------------

   root [0]

   root [0] .I .

   root [0] .I .
   root [1] #include <glm/glm.hpp>
   root [2] #include <glm/gtc/matrix_transform.hpp>
   root [3] #include <glm/gtc/type_ptr.hpp>
   root [4] #include <glm/gtx/string_cast.hpp>

   // Note: GLM matrices are stored in Column-major order
   void show_matrix(const char* label, glm::mat4 const& m){
       std::cout << "\n [MATRIX] " << label << " = " << '\n';
       std::cout << std::fixed << std::setprecision(3);
       for(size_t i = 0; i < 4; i++)
       {
           for(size_t j = 0; j < 4; j++)
           {
               std::cout << std::setw(8) << m[j][i];
           }
           std::cout << '\n';
       }
   }
#+END_SRC


Construct a matrix from column vectors:

\begin{equation}
   \text{colum}_0 = \begin{bmatrix}  25   \\ 40 \\   7 \\ 0    \end{bmatrix} \quad
   \text{colum}_1 = \begin{bmatrix}   8   \\ 9  \\  10 \\ 0    \end{bmatrix} \quad
   \text{colum}_2 = \begin{bmatrix}  -5   \\ 6  \\   9 \\ 0    \end{bmatrix} \quad
   \text{colum}_3 = \begin{bmatrix} 100   \\ 50 \\  20 \\ 1    \end{bmatrix}
\end{equation}


\begin{equation}
   M = \begin{bmatrix} \text{colum}_0  &\text{colum}_1 & \text{colum}_2 & \text{colum}_3 \end{bmatrix}
\end{equation}

\begin{equation}
   M = \begin{bmatrix}
            25  &  8  & -5  &  100 \\
            40  &  9  &  6  &   50 \\
             7  & 10  &  9  &   20 \\
             0  &  0  &  0  &    1 \\
       \end{bmatrix}
\end{equation}


#+BEGIN_SRC cpp
   auto col0 = glm::vec4(25, 40, 7, 0)
   auto col1 = glm::vec4(8, 9, 10, 0)
   auto col2 = glm::vec4(-5, 6, 9, 0)
   auto col3 = glm::vec4(100, 50, 20, 1)

   root [9] auto M = glm::mat4(col0, col1, col2, col3)

   root [24] show_matrix("M", M)

    [MATRIX] M =
     25.000   8.000  -5.000 100.000
     40.000   9.000   6.000  50.000
      7.000  10.000   9.000  20.000
      0.000   0.000   0.000   1.000
#+END_SRC

This matrix could also be constructed in the following mode:

#+BEGIN_SRC cpp
    auto MM = glm::mat4(25, 40, 7, 0  ,8, 9, 10, 0  ,-5, 6, 9, 0 ,100, 50, 20, 1)

   root [27] show_matrix("MM", MM)

    [MATRIX] MM =
     25.000   8.000  -5.000 100.000
     40.000   9.000   6.000  50.000
      7.000  10.000   9.000  20.000
      0.000   0.000   0.000   1.000

   // Note: It shows the matrix columns, not the matix rows.
   root [39] glm::to_string(M)
   (std::string) "mat4x4((25.000000, 40.000000, 7.000000, 0.000000), (8.000000, 9.000000, 10.000000, 0.000000), (-5.000000, 6.000000, 9.000000, 0.000000), (100.000000, 50.000000, 20.000000, 1.000000))"
   root [40]

#+END_SRC

Extract columns from matrix MM:

#+BEGIN_SRC cpp
   #include <glm/gtc/matrix_access.hpp>

   auto col0 = glm::column(MM, 0)
   auto col1 = glm::column(MM, 1)
   auto col2 = glm::column(MM, 2)
   auto col3 = glm::column(MM, 3)

   root [58] glm::to_string(col0)
   (std::string) "vec4(25.000000, 40.000000, 7.000000, 0.000000)"

   root [60] glm::to_string(col1)
   (std::string) "vec4(8.000000, 9.000000, 10.000000, 0.000000)"

   root [62] glm::to_string(col2)
   (std::string) "vec4(-5.000000, 6.000000, 9.000000, 0.000000)"

   root [64] glm::to_string(col3)
   (std::string) "vec4(100.000000, 50.000000, 20.000000, 1.000000)"

   // Get elements from column 0 (col0)
   root [65] col0[0]
   (float) 25.0000f

   root [66] col0[1]
   (float) 40.0000f

   root [67] col0[2]
   (float) 7.00000f

   root [68] col0[3]
   (float) 0.00000f
#+END_SRC

Extract rows from matrix MM:

#+BEGIN_SRC cpp
   auto row0 = glm::row(MM, 0)
   auto row1 = glm::row(MM, 1)
   auto row2 = glm::row(MM, 2)
   auto row3 = glm::row(MM, 3)

   root [73] glm::to_string(row0)
   (std::string) "vec4(25.000000, 8.000000, -5.000000, 100.000000)"

   root [74] glm::to_string(row1)
   (std::string) "vec4(40.000000, 9.000000, 6.000000, 50.000000)"

   root [75] glm::to_string(row2)
   (std::string) "vec4(7.000000, 10.000000, 9.000000, 20.000000)"

   root [76] glm::to_string(row3)
   (std::string) "vec4(0.000000, 0.000000, 0.000000, 1.000000)"
#+END_SRC

Modify matrix columns:

#+BEGIN_SRC cpp
   root [77] show_matrix("MM", MM)

    [MATRIX] MM =
     25.000   8.000  -5.000 100.000
     40.000   9.000   6.000  50.000
      7.000  10.000   9.000  20.000
      0.000   0.000   0.000   1.000

   root [80] MM[0] = glm::vec4(-90.f, 50.0f, 32.5f, 26.0f)


   root [81] show_matrix("MM", MM)

    [MATRIX] MM =
    -90.000   8.000  -5.000 100.000
     50.000   9.000   6.000  50.000
     32.500  10.000   9.000  20.000
     26.000   0.000   0.000   1.000
#+END_SRC


Obtain transpose matrix:

#+BEGIN_SRC cpp
   root [82] auto tmat = glm::transpose(MM)
   (glm::mat<4, 4, float, glm::qualifier::packed_highp> &) @0x7f7b71f0c20c


   root [83] show_matrix("tmat", tmat)

    [MATRIX] tmat =
    -90.000  50.000  32.500  26.000
      8.000   9.000  10.000   0.000
     -5.000   6.000   9.000   0.000
    100.000  50.000  20.000   1.000

  // ----- Access first column elements (column 0) ----//
  //  =>> The matrix is column-major order (Fortran-like data layout),
  //      not C or C++ data-layout, which is row-major order
  //
  // Tmat[j column-number][i row-number]

  // ----- Column 0 elements ----------//

  root [84] tmat[0][0]
  (float) -90.0000f

  root [85] tmat[1][0]
  (float) 50.0000f

  root [86] tmat[0][0]
  (float) -90.0000f

  root [87] tmat[0][1]
  (float) 8.00000f

  root [88] tmat[0][2]
  (float) -5.00000f

  root [89] tmat[0][3]
  (float) 100.000f
#+END_SRC

Get pointer to first element:

#+BEGIN_SRC cpp
   root [90] float* ptr = glm::value_ptr(tmat)
   (float *) 0x7f7b71f0c20c

   root [91] ptr[0]
   (float) -90.0000f

   root [92] ptr[1]
   (float) 8.00000f

   root [93] ptr[15]
   (float) 1.00000f

   root [94] ptr[10]
   (float) 9.00000f

   root [95] *(ptr + 1)
   (float) 8.00000f

   root [96] *(ptr + 10)
   (float) 9.00000f

   root [97] *(ptr + 15)
   (float) 1.00000f
#+END_SRC

Null 4x4 matrix:

#+BEGIN_SRC cpp
   root [53] auto zero_4x4 = glm::mat4()
   (glm::mat<4, 4, float, glm::qualifier::packed_highp> &) @0x7f904e6c20e0

   root [54] show_matrix("zero_4x4", zero_4x4)

    [MATRIX] zero_4x4 =
      0.000   0.000   0.000   0.000
      0.000   0.000   0.000   0.000
      0.000   0.000   0.000   0.000
      0.000   0.000   0.000   0.000
#+END_SRC

Identity matrix:

#+BEGIN_SRC cpp
  root [55] auto id_4x4 = glm::mat4(1.0)
  (glm::mat<4, 4, float, glm::qualifier::packed_highp> &) @0x7f904e6c2120
  root [56]
  root [56] show_matrix("id_4x4", id_4x4)

   [MATRIX] id_4x4 =
     1.000   0.000   0.000   0.000
     0.000   1.000   0.000   0.000
     0.000   0.000   1.000   0.000
     0.000   0.000   0.000   1.000
#+END_SRC

Matrix translation coordinate transform:

#+BEGIN_SRC cpp
  root [58] auto t1 = glm::translate(id_4x4, glm::vec3(2.0, 10.0, 30.0))
  (glm::mat<4, 4, float, glm::qualifier::packed_highp> &) @0x7f904e6c2160

  root [59] show_matrix("t1", t1)

   [MATRIX] t1 =
     1.000   0.000   0.000   2.000
     0.000   1.000   0.000  10.000
     0.000   0.000   1.000  30.000
     0.000   0.000   0.000   1.000


  root [62] auto t2 = glm::translate(t1, glm::vec3(-10, 100.0, 200.0))
  (glm::mat<4, 4, float, glm::qualifier::packed_highp> &) @0x7f904e6c21a0

  root [63] show_matrix("t2", t2)

   [MATRIX] t2 =
     1.000   0.000   0.000  -8.000
     0.000   1.000   0.000 110.000
     0.000   0.000   1.000 230.000
     0.000   0.000   0.000   1.000
#+END_SRC

Rotation around Z axis of 90 degrees:

  + glm::mat4 glm::rotate(glm::mat4 const& matrix, float angle_radians, glm::vec3 const& axis)
  + Rotate around a given axis. The angle is given in radians.

#+BEGIN_SRC cpp
  root [65] const auto axis_z = glm::vec3(0.0f, 0.0f, 1.0f);
  root [66] const auto axis_y = glm::vec3(0.0f, 1.0f, 0.0f);
  root [67] const auto axis_x = glm::vec3(1.0f, 0.0f, 0.0f);


  /*          | cos(t)  -sin(t)   0   0  |
   *          | sin(t)   cos(t)   0   0  |
   *  Rz(t) = |  0        0       1   0  |
   *          |  0        0       0   1  |
   *
   *
   *  t_rotZ = Rz(90) x id_4x4 = Rz(90)
   */
  root [71] auto t_rotZ = glm::rotate(id_4x4, glm::radians(90.0f), axis_z)
  (glm::mat<4, 4, float, glm::qualifier::packed_highp> &) @0x7f904e6c2204

  root [72] show_matrix("t_rotZ", t_rotZ)

   [MATRIX] t_rotZ =
    -0.000  -1.000   0.000   0.000
     1.000  -0.000   0.000   0.000
     0.000   0.000   1.000   0.000
     0.000   0.000   0.000   1.000
  root [73]

   root [96] glm::to_string(t_rotZ)
   (std::string) "mat4x4((-0.000000, 1.000000, 0.000000, 0.000000), ... "
   root [97]
#+END_SRC

Scaling transformation:

#+BEGIN_SRC cpp
   root [75] auto s1 = glm::scale(id_4x4, glm::vec3(2.0, 2.0, 2.0))
   (glm::mat<4, 4, float, glm::qualifier::packed_highp> &) @0x7f904e6c2244

   root [76] show_matrix("s1", s1)

    [MATRIX] s1 =
      2.000   0.000   0.000   0.000
      0.000   2.000   0.000   0.000
      0.000   0.000   2.000   0.000
      0.000   0.000   0.000   1.000

   root [78] s1 = glm::scale(s1, glm::vec3(5.0, 5.0, 5.0))
   (glm::mat &) @0x7f904e6c2244

   root [79] show_matrix("s1", s1)

    [MATRIX] s1 =
     10.000   0.000   0.000   0.000
      0.000  10.000   0.000   0.000
      0.000   0.000  10.000   0.000
      0.000   0.000   0.000   1.000
   root [80]

   // Apply transform to vector:

   root [82] auto res = s1 * glm::vec4(2.0, 5.0, 10.0, 1.0)
   (glm::vec<4, float, glm::qualifier::packed_highp> &) @0x7f904e6c2284

   root [91] glm::to_string(res)
   (std::string) "vec4(20.000000, 50.000000, 100.000000, 1.000000)"

   root [92] res[0]
   (float) 20.0000f

   root [93] res[1]
   (float) 50.0000f

   root [94] res[2]
   (float) 100.000f

   root [95] res[3]
   (float) 1.00000f
#+END_SRC

Scaling transformation:

#+BEGIN_SRC cpp
   root [97] auto s = glm::scale(id_4x4, glm::vec3(4.0, 5.0, 6.0))
   (glm::mat<4, 4, float, glm::qualifier::packed_highp> &) @0x7f904e6c22a4

   root [98] show_matrix("s", s)

    [MATRIX] s =
      4.000   0.000   0.000   0.000
      0.000   5.000   0.000   0.000
      0.000   0.000   6.000   0.000
      0.000   0.000   0.000   1.000
   root [99]
   root [99] s = glm::translate(s, glm::vec3(4, -5, 9))
   (glm::mat &) @0x7f904e6c22a4


   root [100] show_matrix("s", s)

    [MATRIX] s =
      4.000   0.000   0.000  16.000
      0.000   5.000   0.000 -25.000
      0.000   0.000   6.000  54.000
      0.000   0.000   0.000   1.000
   root [101]


   root [105] s = glm::scale(s, glm::vec3(5.0, 2.0, 3.0))
   (glm::mat &) @0x7f904e6c22a4


   root [106] show_matrix("s", s)

    [MATRIX] s =
     25.000   0.000   0.000  20.000
      0.000   4.000   0.000 -10.000
      0.000   0.000   9.000  27.000
      0.000   0.000   0.000   1.000
#+END_SRC

Inverse matrix:

#+BEGIN_SRC cpp
   root [111] show_matrix("s", s)

    [MATRIX] s =
     25.000   0.000   0.000  20.000
      0.000   4.000   0.000 -10.000
      0.000   0.000   9.000  27.000
      0.000   0.000   0.000   1.000

   root [112] inv_s = glm::inverse(s)
   (glm::mat<4, 4, float, glm::qualifier::packed_highp> &) @0x7f904e6c2324

   root [113] show_matrix("inv_s", inv_s)

    [MATRIX] inv_s =
      0.040  -0.000   0.000  -0.800
     -0.000   0.250  -0.000   2.500
      0.000  -0.000   0.111  -3.000
     -0.000   0.000  -0.000   1.000
   root [114]
   root [114]

#+END_SRC


  *GLM Quaternions*

Create an identity quaternion, which represents no rotation.

#+BEGIN_SRC cpp
   root [26] auto q = glm::quat(1.0, 0.0, 0.0, 0.0)
   (glm::qua<float, glm::qualifier::packed_highp> &) @0x7fc8b47e6020

   root [27] q.x
   (float) 0.00000f
   root [28]
   root [28] q.y
   (float) 0.00000f
   root [29]
   root [29] q.z
   (float) 0.00000f
   root [30]
   root [30] q.w
   (float) 1.00000f
   root [31]
   root [31]
#+END_SRC

Create a quaternion that represents a rotation of 90 degrees around Z
axis:

#+BEGIN_SRC cpp
   root [39] auto qrot_z = glm::angleAxis(glm::radians(90.0f), glm::vec3{0.0f, 0.0f, 1.0f} )
   (glm::qua<float, glm::qualifier::packed_highp> &) @0x7fc8b47e6050

   root [40] glm::to_string(qrot_z)
   (std::string) "quat(0.707107, {0.000000, 0.000000, 0.707107})"

   root [42] qrot_z.w
   (float) 0.707107f

   root [43] qrot_z.x
   (float) 0.00000f

   root [44] qrot_z.y
   (float) 0.00000f

   root [45] qrot_z.z
   (float) 0.707107f
#+END_SRC

Get quaternion angle:

#+BEGIN_SRC cpp

  // Angle in radians.
  root [61] glm::angle(qrot_z)
  (float) 1.57080f

  // Angle in degrees
  root [62] glm::angle(qrot_z) * 180.0 / M_PI
  (double) 90.000003
#+END_SRC

Get quaternion rotation axis vector:

#+BEGIN_SRC cpp
   root [63] auto axis = glm::axis(qrot_z)
   (glm::vec<3, float, glm::qualifier::packed_highp> &) @0x7fc8b47e6150

   root [64] glm::to_string(axis)
   (std::string) "vec3(0.000000, 0.000000, 1.000000)"
#+END_SRC

Determine quaternion conjugate:

#+BEGIN_SRC cpp
   // Conjugate
   root [68] auto res = glm::conjugate(qrot_z)
   (glm::qua<float, glm::qualifier::packed_highp> &) @0x7fc8b47e616c

   root [69] glm::to_string(res)
   (std::string) "quat(0.707107, {-0.000000, -0.000000, -0.707107})"

   root [71] glm::to_string( qrot_z * res )
   (std::string) "quat(1.000000, {0.000000, 0.000000, 0.000000})"
#+END_SRC

Determine quaternion inverse:

#+BEGIN_SRC cpp
  root [72] glm::to_string( glm::inverse(qrot_z) )
  (std::string) "quat(0.707107, {-0.000000, -0.000000, -0.707107})"
#+END_SRC

Computer quaternion norm (aka magnitude):

#+BEGIN_SRC cpp
  root [74] glm::length(qrot_z)
  (float) 1.00000f
#+END_SRC


Turn the previous quaternion in to a rotation matrix:

#+BEGIN_SRC cpp
   root [47] auto TrotZ1 = glm::mat4_cast(qrot_z)

   root [48] show_matrix("TrotZ1", TrotZ1)

    [MATRIX] TrotZ1 =
      0.000  -1.000   0.000   0.000
      1.000   0.000   0.000   0.000
      0.000   0.000   1.000   0.000
      0.000   0.000   0.000   1.000

   // ------------ Confirm result ------------//
   //

   root [51] auto TrotZ2 = glm::rotate(glm::mat4(1.0), glm::radians(90.0f), glm::vec3(0.0, 0.0, 1.0))

   root [52] show_matrix("TrotZ2", TrotZ2)

    [MATRIX] TrotZ2 =
     -0.000  -1.000   0.000   0.000
      1.000  -0.000   0.000   0.000
      0.000   0.000   1.000   0.000
      0.000   0.000   0.000   1.000

#+END_SRC

Rotate a vector with the quaternion _qrot_z_:

#+BEGIN_SRC cpp
   root [57] auto v = glm::vec3{1.0f, 0.0f, 0.0f}
   (glm::vec<3, float, glm::qualifier::packed_highp> &) @0x7fc8b47e6138

   // Rotating a vector just requires multiplication.
   root [59] auto rotated_v = qrot_z * v
   (glm::vec<3, float, glm::qualifier::packed_highp> &) @0x7fc8b47e6144

   root [60] glm::to_string(rotated_v)
   (std::string) "vec3(0.000000, 1.000000, 0.000000)"


   // Rotate the same vector with a rotation matrix.
   root [79] glm::to_string( TrotZ2 * glm::vec4(1.0, 0.0, 0.0, 0.0) )
   (std::string) "vec4(-0.000000, 1.000000, 0.000000, 0.000000)"
#+END_SRC

New rotation around Y axis about 60 degrees.

#+BEGIN_SRC cpp
   root [80] auto q = qrot_z
   (glm::qua<float, glm::qualifier::packed_highp> &) @0x7fc8b47e617c

   // ---------- Before rotation ------------
   //
   root [81] glm::to_string( q )
   (std::string) "quat(0.707107, {0.000000, 0.000000, 0.707107})"

   // -------- After rotation ----------------//
   //
   root [84] q = q * glm::angleAxis(glm::radians(60.0f), glm::vec3{0.0, 0.1, 0.0})
   (glm::qua<float, (qualifier)0U> &) @0x7fc8b47e617c

   root [85] glm::to_string( q )
   (std::string) "quat(0.612372, {-0.035355, 0.035355, 0.612372})"

   // --------- Get Rotation Matrix -----------//

   root [86] auto m = glm::mat4_cast(q)

   root [88] show_matrix("m", m)

    [MATRIX] m =
      0.248  -0.752   0.000   0.000
      0.747   0.248   0.087   0.000
     -0.087   0.000   0.995   0.000
      0.000   0.000   0.000   1.000

   // -------- Get total rotation angle and axis ---------//
   //

   // Get new rotation axis
   root [89] auto axis = glm::axis(q)
   (glm::vec<3, float, glm::qualifier::packed_highp> &) @0x7fc8b47e61cc

   root [90] glm::to_string( axis )
   (std::string) "vec3(-0.044721, 0.044721, 0.774597)"

   // Get rotation angle around new axis in radians
   root [91] glm::angle(q)
   (float) 1.82348f

   // Get rotation angle around new axis in degrees
   root [92] glm::angle(q) * 180.0 / M_PI
   (double) 104.47752


   //--------- Get Yaw, Pitch and Roll angles --------------------//
   //
   root [94] glm::yaw(q) * 180.0 / M_PI
   (double) 4.9681836

   root [95] glm::pitch(q) * 180.0 / M_PI
   (double) 0.0000000

   root [96] glm::roll(q) * 180.0 / M_PI
   (double) 90.000003

#+END_SRC

** [DRAFT] Debugging Computer Graphics Code

Outline of techniques for debugging computer graphics code:

  + Use _right-hand rule_ for finding rotation direction around some rotation axis.
    + See: [[https://en.wikipedia.org/wiki/Right-hand_rule][right-hand-rule]] 

  + Use _right-hand rule_ for finding the result vector of a vector-product operation. 

  + Print/display transform matrices.

  + Use assertions for validating code assumptions during runtime and
    abort the process execution if the assertion predicate does not
    hold.

  + Use print statements for tracing the code execution flow.

  + Draw X, Y, Z axis lines for visual debugging.

  + Check shader compilation error message and error code.

  + Check OpenGL error codes for every OpenGL subroutine calls.

  + Simulate and prototype linear algebra parts in a
    numerically-oriented or scientific programming language such
    Matlab, Octave or Julia.

  + Use a _debugger_, such as Windows Debugger, GDB, LLDB - LVM
    Debugger, in the case of runtime crash due to failed assertions,
    std::abort() calls, uncaught exceptions, Unix signals or
    segmentation faults.

  + Use 3D standard test models for testing the 3D scene rendering. 
    + [[https://en.wikipedia.org/wiki/List_of_common_3D_test_models][List of common 3D test models]]

 *Assertions*

The subroutine glGetUniformLocation() returns -1 as error code when it
fails to find a shader's uniform variable location named
'u_projection'.

#+BEGIN_SRC cpp
    // Get shader uniform variable location for projection matrix
    // See shader code: "uniform mat4 projection;"
    const GLint u_proj  = glGetUniformLocation(prog, "u_projection");
    assert( u_proj >= 0 && "Failed to find u_projection uniform variable" );
#+END_SRC

Assertions should be used for non-recoverable errors, checking
pre-conditions, post-conditons and invariant. Since assertions are
eliminated on non-debug builds, this assertion should be replaced by a
proper error checking. However, in this case, assertion are useful
for a prototyping code and during development time as a scaffolding
for a future error handling.


 *Checking OpenGL error code*

#+BEGIN_SRC cpp
  #define GL_CHECK(funcall)\
      do { \
          (funcall); \
          GLint error = glGetError();  \
          if(error == GL_NO_ERROR){ break; } \
          std::fprintf(stderr, " [OPENGL ERROR] Error code = %d ; line = %d ; call = '%s'  \n" \
                        , error, __LINE__, #funcall ); \
          abort(); \
      } while(0)

 // -------- Usage ---------------//

    GL_CHECK( glUniformMatrix4fv(u_model, 1, GL_FALSE, glm::value_ptr(model) ) );
    GL_CHECK( glBindVertexArray(vao) );
    GL_CHECK( glDrawArrays(draw_type, 0, n_vertices) );

#+END_SRC

** Minimal C++ GLFW project  

The following code draws a square and a triangle using the OpenGL
retained mode API. Before the rendering takes place, data must be
upload to the VBO (Vertex Buffer Object), allocated on the GPU-side,
via _glBufferData()_ call that sends the data to the previous bound VBO
through _glBindBuffer()_ call. Then, on the rendering loop and on every
frame, the buffer data layout must described with
_glVertexAttribPointer()_ before drawing via call to _glDrawArrays()_
subroutine, which draw vertices from the current bound buffer.

Subroutines that modifies global state may cause unintended behavior,
so it is a good practice to unset the affected global state when the
current global state is no longer needed. For instance, if the current
VBO no longer needs to be bound, this global state can be disabled by
calling /glBindBuffer(GL_ARRAY_BUFFER, 0)/.


OpenGL subroutines used:

 + [[https://www.khronos.org/registry/OpenGL-Refpages/gl4/html/glGenBuffers.xhtml][glGenBuffers()]] => Instantiate a buffer object.

#+BEGIN_SRC cpp
  void glGenBuffers(GLsizei n, GLuint* buffers);
#+END_SRC

 + [[https://www.khronos.org/registry/OpenGL-Refpages/gl4/html/glBindBuffer.xhtml][glBindBuffer()]] => Bind some buffer object, a.k.a enable, only one
   buffer can be bound at a time. (The VBO is a global state).

#+BEGIN_SRC cpp
  void glBindBuffer(GLenum target,nt buffer);
#+END_SRC

 + [[https://www.khronos.org/registry/OpenGL-Refpages/gl4/html/glBufferData.xhtml][glBufferData()]]  =>> Send data to current bound buffer object that
   was bound via glBindBuffer() call.

#+BEGIN_SRC cpp
  void glBufferData( GLenum      target,
                     GLsizeiptr  size,
                     const void* data,
                     GLenum      usage
                     );
#+END_SRC

  + [[https://www.khronos.org/registry/OpenGL-Refpages/gl4/html/glEnableVertexAttribArray.xhtml][glEnableVertexAttribArray()]]

#+BEGIN_SRC cpp
  void glEnableVertexAttribArray(GLuint index);
#+END_SRC

 + [[https://www.khronos.org/registry/OpenGL-Refpages/gl4/html/glVertexAttribPointer.xhtml][glVertexAttribPointer()]]
   + Describes the data layout of current bound buffer.

#+BEGIN_SRC cpp
     void glVertexAttribPointer( GLuint      index,
                                 GLint       size,
                                 GLenum      type,
                                 GLboolean   normalized,
                                 GLsizei     stride,
                                 const void* pointer);
#+END_SRC

  + [[https://www.khronos.org/registry/OpenGL-Refpages/gl4/html/glDrawArrays.xhtml][glDrawArrays()]] => Render OpenGL primitives from current bound
    buffer object's data. Those primitives are: lines, triangles or
    quads and so on.

#+BEGIN_SRC cpp
  void glDrawArrays(GLenum mode, GLint first, GLsizei count);
#+END_SRC


  *Screenshot*

[[file:images/opengl-draw2d-raw.png][file:images/opengl-draw2d-raw.png]]

  *Source Code*

  + This CMakeLists.txt building script saves the user from manually
    downloading, building, installing and configuring OpenGL Glew and
    GLFW companion libraries. This file automatically downloads the
    GLFW library, that provides window system abstraction and OpenGL
    context; and a pre-compiled GLEW library (Windows only) that
    provides platform-agnostic OpenGL function pointer loading.

  + The CMakeLists.txt (version 2) contains a macro for defining
    OpenGL applications without the boilerplate needed for linking
    against OpenGL companion libraries and statically linking against
    MINGW runtimme libraries.

File: _CMakeLists.txt_ (Version 1)

#+BEGIN_SRC cmake 
  cmake_minimum_required(VERSION 3.5)
  project(OpenGL_Draw2D_VBO)
  
  set(CMAKE_CXX_STANDARD 17)
  set(CMAKE_CXX_STANDARD_REQUIRED ON)
  set(CMAKE_VERBOSE_MAKEFILE ON)
  
  #================ GLFW Settings  ===============#
  
  find_package(OpenGL REQUIRED)
  
  include(FetchContent)
  
  # Set GLFW Options before FectchContent_MakeAvailable
  set( GLFW_BUILD_EXAMPLES OFF CACHE BOOL  "GLFW lib only" )
  set( GLFW_BUILD_TESTS    OFF CACHE BOOL  "GLFW lib only" )
  set( GLFW_BUILD_DOCS     OFF CACHE BOOL  "GLFW lib only" )
  set( GLFW_BUILD_INSTALL  OFF CACHE BOOL  "GLFW lib only" )
  
  # Donwload GLFW library
  FetchContent_Declare(
    glfwlib
    URL   https://github.com/glfw/glfw/releases/download/3.3.2/glfw-3.3.2.zip
  )
  FetchContent_MakeAvailable(glfwlib)
  
  # Download pre-compiled GLEW when building under Windows NT OS (x64)
  IF(WIN32)
     FetchContent_Declare(
        glew-release 
        URL  https://ufpr.dl.sourceforge.net/project/glew/glew/2.1.0/glew-2.1.0-win32.zip
        # No longer valid URL 
        ## URL     https://megalink.dl.sourceforge.net/project/glew/glew/2.1.0/glew-2.1.0-win32.zip
     )
     FetchContent_MakeAvailable(glew-release)
     include_directories( ${glew-release_SOURCE_DIR}/include  ${glm_SOURCE_DIR} )        
     link_directories(  ${glew-release_SOURCE_DIR}/lib/Release/x64 )
  
     set( GLEW_LIB_PATH1 ${glew-release_SOURCE_DIR}/lib/Release/x64/glew32.lib )
     set( GLEW_LIB_PATH2 ${glew-release_SOURCE_DIR}/lib/Release/x64/glew32s.lib )
  ENDIF()
  
     # ======= T A R G E T S ============================#
     #                                                   #
  
  
         add_executable( draw2d-raw  draw2d-raw.cpp )
  target_link_libraries( draw2d-raw  glfw
                                     OpenGL::GL
                                     ${GLEW_LIB_PATH1} ${GLEW_LIB_PATH2}  )
  
  # For MINGW (GCC Ported to Windows)                              
  IF(MINGW)
     # Statically link against MINGW dependencies
     # for making easier to deploy on other machines. 
     target_link_options( draw2d-raw PRIVATE                                 
                              -static-libgcc
                              -static-libstdc++
  
                              -Wl,-Bstatic,--whole-archive -lwinpthread
                                -Wl,--no-whole-archive                                 
                              )
  ENDIF()       
  
  
  # Copy GLEW DLL shared library to same directory as the executable.                              
  IF(WIN32)                                
     add_custom_command(TARGET draw2d-raw POST_BUILD 
                    COMMAND ${CMAKE_COMMAND} -E copy_if_different
                    "${glew-release_SOURCE_DIR}/bin/Release/x64/glew32.dll"              
                    $<TARGET_FILE_DIR:draw2d-raw>)
  ENDIF()  
#+END_SRC

File: _CMakeLists.txt_ (Version 2) - This version provides a macro for automating the definition of OpenGL projects with _ADD_OPENGL_ command.

#+BEGIN_SRC cmake 
   cmake_minimum_required(VERSION 3.5)
   project(OpenGL_Draw2D_VBO)

   set(CMAKE_CXX_STANDARD 17)
   set(CMAKE_CXX_STANDARD_REQUIRED ON)
   set(CMAKE_VERBOSE_MAKEFILE ON)

   #================ GLFW Settings  ===============#

   find_package(OpenGL REQUIRED)

   include(FetchContent)

   # Set GLFW Options before FectchContent_MakeAvailable
   set( GLFW_BUILD_EXAMPLES OFF CACHE BOOL  "GLFW lib only" )
   set( GLFW_BUILD_TESTS    OFF CACHE BOOL  "GLFW lib only" )
   set( GLFW_BUILD_DOCS     OFF CACHE BOOL  "GLFW lib only" )
   set( GLFW_BUILD_INSTALL  OFF CACHE BOOL  "GLFW lib only" )

   # Donwload GLFW library
   FetchContent_Declare(
     glfwlib
     URL   https://github.com/glfw/glfw/releases/download/3.3.2/glfw-3.3.2.zip
   )
   FetchContent_MakeAvailable(glfwlib)

   # Download pre-compiled GLEW when building under Windows NT OS (x64)
   IF(WIN32)
      FetchContent_Declare(
         glew-release 
         URL  https://ufpr.dl.sourceforge.net/project/glew/glew/2.1.0/glew-2.1.0-win32.zip   
         # https://github.com/nigels-com/glew/archive/glew-2.2.0.zip
      )
      FetchContent_MakeAvailable(glew-release)
      include_directories( ${glew-release_SOURCE_DIR}/include  ${glm_SOURCE_DIR} )        
      link_directories(  ${glew-release_SOURCE_DIR}/lib/Release/x64 )

      set( GLEW_LIB_PATH1 ${glew-release_SOURCE_DIR}/lib/Release/x64/glew32.lib )
      set( GLEW_LIB_PATH2 ${glew-release_SOURCE_DIR}/lib/Release/x64/glew32s.lib )
   ENDIF()


   MACRO(ADD_OPENGL_APP target sources)
      add_executable( ${target} ${sources} )
      message([TRACE] " Add OpenGL executable = ${target} ")

      target_link_libraries( ${target} glfw
                                       OpenGL::GL
                                       ${GLEW_LIB_PATH1}
                                       ${GLEW_LIB_PATH2} )

       IF(MINGW)
           # Statically link against MINGW dependencies
           # for making easier to deploy on other machines. 
           target_link_options( ${target} PRIVATE                                 
                                    -static-libgcc
                                    -static-libstdc++
                                    -Wl,-Bstatic,--whole-archive -lwinpthread
                                      -Wl,--no-whole-archive				     
                                    )
        ENDIF()       


        # Copy GLEW DLL shared library to same directory as the executable.		 
        IF(WIN32)				 
           add_custom_command(TARGET ${target} POST_BUILD 
                          COMMAND ${CMAKE_COMMAND} -E copy_if_different
                          "${glew-release_SOURCE_DIR}/bin/Release/x64/glew32.dll"              
                          $<TARGET_FILE_DIR:${target}>)
        ENDIF()	   				   
   ENDMACRO()     

        # ======= T A R G E T S ============================#
        #                                                   #

   ADD_OPENGL_APP( draw2-raw draw2d-raw.cpp )
#+END_SRC

File: _CMakeLists.txt_ (Version 3) - last resort if the SourceForge link
no longer works. In this case, it is assumed that Glew library was
download manually from SourceForge via the URL [[https://sourceforge.net/projects/glew/files/glew/2.1.0/glew-2.1.0.zip/download][(glew-2.1.0 - download)]].
Then the ZIP  _glew-2.1.0-win32.zip_ is saved in the project root
directory and extracted to a directory named ./glew-2.1.0/

#+BEGIN_SRC cmake 
   # Used when Glew library is download manually 
   cmake_minimum_required(VERSION 3.5)
   project(OpenGL_Draw2D_VBO)
   
   set(CMAKE_CXX_STANDARD 17)
   set(CMAKE_CXX_STANDARD_REQUIRED ON)
   set(CMAKE_VERBOSE_MAKEFILE ON)
   
   #================ GLFW Settings  ===============#
   
   find_package(OpenGL REQUIRED)
   
   include(FetchContent)
   
   # Set GLFW Options before FectchContent_MakeAvailable
   set( GLFW_BUILD_EXAMPLES OFF CACHE BOOL  "GLFW lib only" )
   set( GLFW_BUILD_TESTS    OFF CACHE BOOL  "GLFW lib only" )
   set( GLFW_BUILD_DOCS     OFF CACHE BOOL  "GLFW lib only" )
   set( GLFW_BUILD_INSTALL  OFF CACHE BOOL  "GLFW lib only" )
   
   # Donwload GLFW library
   FetchContent_Declare(
     glfwlib
     URL   https://github.com/glfw/glfw/releases/download/3.3.2/glfw-3.3.2.zip
   )
   FetchContent_MakeAvailable(glfwlib)
   
   # Download pre-compiled GLEW when building under Windows NT OS (x64)
   IF(WIN32)
      include_directories( ${CMAKE_CURRENT_LIST_DIR}/glew-2.1.0/include  ${glm_SOURCE_DIR} )        
      link_directories(  ${CMAKE_CURRENT_LIST_DIR}/glew-2.1.0/lib/Release/x64 )
   
      set( GLEW_LIB_PATH1  ${CMAKE_CURRENT_LIST_DIR}/glew-2.1.0/lib/Release/x64/glew32.lib )
      set( GLEW_LIB_PATH2  ${CMAKE_CURRENT_LIST_DIR}/glew-2.1.0/lib/Release/x64/glew32s.lib )
   ENDIF()
   
      # ======= T A R G E T S ============================#
      #                                                   #
   
          add_executable( draw2d-raw  draw2d-raw.cpp )
   target_link_libraries( draw2d-raw  glfw
                                      OpenGL::GL
                                      ${GLEW_LIB_PATH1} ${GLEW_LIB_PATH2}  )
   
   # For MINGW (GCC Ported to Windows)                              
   IF(MINGW)
      # Statically link against MINGW dependencies
      # for making easier to deploy on other machines. 
      target_link_options( draw2d-raw PRIVATE                                 
                               -static-libgcc
                               -static-libstdc++
   
                               -Wl,-Bstatic,--whole-archive -lwinpthread
                                 -Wl,--no-whole-archive                                 
                               )
   ENDIF()       
   
   
   # Copy GLEW DLL shared library to same directory as the executable.                              
   IF(WIN32)                                
      add_custom_command(TARGET draw2d-raw POST_BUILD 
                     COMMAND ${CMAKE_COMMAND} -E copy_if_different
                     "${CMAKE_CURRENT_LIST_DIR}/glew-2.1.0/bin/Release/x64/glew32.dll"              
                     $<TARGET_FILE_DIR:draw2d-raw>)
   ENDIF()   
#+END_SRC

Downloading glew manually with curl for usage with the previous CMakeLists.txt file: 

#+BEGIN_SRC sh 
  $ curl -L -o glew32.zip  https://ufpr.dl.sourceforge.net/project/glew/glew/2.1.0/glew-2.1.0-win32.zip
 
  # File hash (file signature) - unique identifier for the file  
  $ md5sum glew32.zip 
  32a72e6b43367db8dbea6010cd095355  glew32.zip
 
  # File hash (file signature) - unique identifier for the file  
  $ sha256sum glew32.zip 
  80cfc88fd295426b49001a9dc521da793f8547ac10aebfc8bdc91ddc06c5566c  glew32.zip

  # File hash (file signature) - unique identifier for the file  
  $ sha1sum glew32.zip 
  f505a67ad884b75648639a9b01bf0245bfcfa731  glew32.zip
  
 $ unzip -l glew32.zip 
  Archive:  glew32.zip
    Length      Date    Time    Name
  ---------  ---------- -----   ----
          0  2017-07-31 08:46   glew-2.1.0/
          0  2017-07-31 08:41   glew-2.1.0/bin/
          0  2017-07-31 08:42   glew-2.1.0/bin/Release/
          0  2017-07-31 08:42   glew-2.1.0/bin/Release/Win32/
     389632  2017-07-31 08:42   glew-2.1.0/bin/Release/Win32/glew32.dll
     482304  2017-07-31 08:42   glew-2.1.0/bin/Release/Win32/glewinfo.exe
     294912  2017-07-31 08:42   glew-2.1.0/bin/Release/Win32/visualinfo.exe
          0  2017-07-31 08:42   glew-2.1.0/bin/Release/x64/
     422912  2017-07-31 08:42   glew-2.1.0/bin/Release/x64/glew32.dll
     539648  2017-07-31 08:42   glew-2.1.0/bin/Release/x64/glewinfo.exe
     338432  2017-07-31 08:42   glew-2.1.0/bin/Release/x64/visualinfo.exe
     ... ... ... ... ... ... ... ... ... ... ... ... ... ... .. 
     ... ... ... ... ... ... ... ... ... ... ... ... ... ... .. 
     
          0  2017-07-31 08:42   glew-2.1.0/lib/Release/
          0  2017-07-31 08:46   glew-2.1.0/lib/Release/Win32/
     712098  2017-07-31 08:42   glew-2.1.0/lib/Release/Win32/glew32.lib
    2443636  2017-07-31 08:42   glew-2.1.0/lib/Release/Win32/glew32s.lib
          0  2017-07-31 08:46   glew-2.1.0/lib/Release/x64/
     701288  2017-07-31 08:42   glew-2.1.0/lib/Release/x64/glew32.lib
    2584968  2017-07-31 08:42   glew-2.1.0/lib/Release/x64/glew32s.lib
       3870  2017-07-31 08:46   glew-2.1.0/LICENSE.txt
  ---------                     -------
   10662952                     47 files
  
  $ unzip -x glew32.zip 
  Archive:  glew32.zip
     creating: glew-2.1.0/
     creating: glew-2.1.0/bin/
     creating: glew-2.1.0/bin/Release/
     creating: glew-2.1.0/bin/Release/Win32/
    inflating: glew-2.1.0/bin/Release/Win32/glew32.dll  
    inflating: glew-2.1.0/bin/Release/Win32/glewinfo.exe 
   ... ... ... ... ... ... ... ... ... ... ... ... ... ...  
   ... ... ... ... ... ... ... ... ... ... ... ... ... ...  
     inflating: glew-2.1.0/lib/Release/Win32/glew32.lib  
    inflating: glew-2.1.0/lib/Release/Win32/glew32s.lib  
     creating: glew-2.1.0/lib/Release/x64/
    inflating: glew-2.1.0/lib/Release/x64/glew32.lib  
    inflating: glew-2.1.0/lib/Release/x64/glew32s.lib  
    inflating: glew-2.1.0/LICENSE.txt  
   
#+END_SRC

File: _xmake.lua_  

   + Compilation script for [[https://xmake.io/][Xmake]] building system, that uses [[https://en.wikipedia.org/wiki/Lua_(programming_language)][Lua]] as
     embedded scripting language and as a DSL - Domain-Specific
     Language. The disadvantage of this building system are: poor IDE
     support and the lack of ability for downloading sources directly
     like CMake's _FetchContent_Declare_ statement. Another issue is
     that new packages can only be added by sending pull requests to
     [[https://github.com/xmake-io/xmake-repo/tree/master/packages][Xmake packages repository]].
   + XMake packages used in this sample project: 
     + [[https://github.com/xmake-io/xmake-repo/blob/master/packages/g/glfw/xmake.lua][glfw]], [[https://github.com/xmake-io/xmake-repo/blob/master/packages/g/glew/xmake.lua][glew]], [[https://github.com/xmake-io/xmake-repo/blob/master/packages/g/glm/xmake.lua][glm]] 

#+BEGIN_SRC lua 
  add_rules("mode.debug", "mode.release")
  
  add_requires("glm", "glew", "glfw")
  
  target("draw2d")
    set_kind("binary")
    add_files("./draw2d-raw.cpp")
    add_packages("glew", "glfw", "glm")
#+END_SRC


File: _draw2d-raw.cpp_

#+BEGIN_SRC cpp 
  #include <iostream>
  #include <vector>
  #include <array>
  #include <cmath>
  #include <cassert>

  // -------- OpenGL headers ---------//
  //
  #define GL_GLEXT_PROTOTYPES 1 // Necessary for OpenGL >= 3.0 functions
  #define GL3_PROTOTYPES      1 // Necessary for OpenGL >= 3.0 functions


  #if defined(_WIN32)
     #include <windows.h>
     #include <GL/glew.h>
  #endif

  #include <GL/gl.h>
  #include <GLFW/glfw3.h>

  #include <GL/glu.h>
  // #include <GL/glut.h>


  struct Vertex2D
  {
      GLfloat x;
      GLfloat y;
  };

  int main(int argc, char** argv)
  {
      GLFWwindow* window;

      // Initialize GLFW 
      if (!glfwInit()){ return -1; }

    
      /* Create a windowed mode window and its OpenGL context */
      window = glfwCreateWindow(640, 480, "2D Drawing raw VBO buffers", NULL, NULL);
      if (!window)
      {
          glfwTerminate();
          return -1;
      }

      /* Make the window's context current */
      glfwMakeContextCurrent(window);
      // glClearColor(0.0f, 0.5f, 0.6f, 1.0f);
      glClearColor(0.0f, 0.0f, 0.0, 1.0f);

      // --- GLEW should always be initialized after an OpenGL context.
      #if _WIN32
      glewExperimental = GL_TRUE;
      if (glewInit() != GLEW_OK) {
          std::cerr << "GLEW::Error : failed to initialize GLEW"<< std::endl;
      }
      std::fprintf(stderr, " [TRACE] Glew Initialized Ok. \n");
      #endif
    

      Vertex2D triangle_points[3] = {
          Vertex2D{-0.25,   -0.25}
        , Vertex2D{ 0.00,   0.25}
        , Vertex2D{ 0.25,  -0.25}
      };

      Vertex2D square_points[4] = {
          { -0.3,  -0.3 }
        , { -0.3,   0.3 }
        , {  0.3,   0.3 }
        , {  0.3,  -0.3 }
      };

      // ================== Triangle Buffer ====================//
      //

      GLuint vbo_triangle_ = 0;

      // Create an OpenGL VBO buffer
      // =>> void glGenBuffers (GLsizei n, GLuint *buffers);
      glGenBuffers(  1                // Number of buffers that will be instantiated
                   , &vbo_triangle_   // Address of first element or address of array
                                      // that results will be written to.
                   );
      assert( vbo_triangle_ != 0 );

      // Set this buffer as the current buffer - Only one buffer can be bound at a time.
      // =>> void glBindBuffer (GLenum target, GLuint buffer)
      glBindBuffer(GL_ARRAY_BUFFER, vbo_triangle_);

      // Upload data to current VBO buffer in GPU
      glBufferData(GL_ARRAY_BUFFER, sizeof(triangle_points), triangle_points, GL_STATIC_DRAW);

      // Unbind current buffer in order to avoid unintended behaviors
      // as the subroutine glBindBuffer() modifies has a global state.
      glBindBuffer(GL_ARRAY_BUFFER, 0);

      // ================ Square / Vertex Buffer Object 2 ==================//
      //

      GLuint vbo_square_ = 0;
      // Instiate buffer - gets a handle or token for
      // a buffer allocated on GPU-side.
      glGenBuffers(1, &vbo_square_);
      // Check for error
      assert( vbo_square_ != 0);
      // Bind Current buffer (Affects global state)
      glBindBuffer(GL_ARRAY_BUFFER, vbo_square_);
      // Upload data to the GPU
      glBufferData(GL_ARRAY_BUFFER, sizeof(square_points), square_points, GL_STATIC_DRAW);
      // Unbind buffer
      glBindBuffer(GL_ARRAY_BUFFER, 0);

      GLint shader_attr = 0;

      //  ======= R E N D E R  - L O O P ============//
      //
      while ( !glfwWindowShouldClose(window) )
      {
          glClear(GL_COLOR_BUFFER_BIT);

          // ====== BEGIN RENDERING ============//

              // ------------ Draw triangle --------------//
              //
              glBindBuffer(GL_ARRAY_BUFFER, vbo_triangle_);
              glEnableVertexAttribArray(shader_attr);

              // Describe buffer data layout (binary layout)
              // =>> glVertexAttribPointer ( GLuint index, GLint size, GLenum type
              //                           , GLboolean normalized, GLsizei stride, const void *pointer);
              //
              glVertexAttribPointer(shader_attr // GLint  index         => Shader attribute location, 0 for now
                                  , 2           // GLint  size          => 2 components (X, Y) of type GLfloat
                                  , GL_FLOAT    // GLemum type          => Type of each component
                                  , GL_FALSE    // GLboolean normalized
                                  , 0           // GLsizei stride
                                  , nullptr     // const void* pointer
                                  );
              // Draw arrays using the content of buffer
              // Plot 1 triangle (each triangle has 3 vertices)
              glDrawArrays(GL_TRIANGLES, 0, 3);

              // Disable global state
              glBindBuffer(GL_ARRAY_BUFFER, 0);
              glDisableVertexAttribArray(0);

              //------------ Draw Square -------------------------//
              //

              #if 1
              glBindBuffer(GL_ARRAY_BUFFER, vbo_square_);
              glEnableVertexAttribArray(shader_attr);
              glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, nullptr);
              // Plot 4 vertices
              glDrawArrays(GL_LINE_LOOP, 0, 4);

              // Disable global state
              glBindBuffer(GL_ARRAY_BUFFER, 0);
              glDisableVertexAttribArray(shader_attr);
              #endif

          // ====== END RENDERING ==============//

          /* Swap front and back buffers */
          glfwSwapBuffers(window);
          /* Poll for and process events */
          glfwPollEvents();

          if( glfwGetKey(window, 'Q' ) == GLFW_PRESS )
          {
               std::cout << " [TRACE] User typed Q =>> Shutdown program. Ok. " << '\n';
               break;
          }
      }

      glfwTerminate();
      return 0;
  }

#+END_SRC

  *Building on Linux* 

#+BEGIN_SRC sh 
  $ cmake -H. -B_build_linux -DCMAKE_BUILD_TYPE=Debug
  $ cmake --build _build_linux --target 
 
  # Run 
  $ _build_linux/draw2d-raw 
#+END_SRC

 *Building with dockercross tool (cross compilation for Windows)*

 + See: Cross compiling toolchains in Docker images
   + =>> https://github.com/dockcross/dockcross 

#+BEGIN_SRC sh 
  $ docker run -it --rm -v $PWD:/cwd -w /cwd --entrypoint=bash dockcross/windows-static-x64

  $ cmake -H. -B_build_cross -DCMAKE_BUILD_TYPE=Debug
  $ cmake --build _build_cross --target

#+END_SRC

Building without entering docker shell: 

#+BEGIN_SRC sh 
   $ docker run -it --rm -v $PWD:/cwd -w /cwd --entrypoint=bash dockcross/windows-static-x64 cmake -H. -B_build_cross -DCMAKE_BUILD_TYPE=Debug
   $ docker run -it --rm -v $PWD:/cwd -w /cwd --entrypoint=bash dockcross/windows-static-x64 cmake --build _build_cross --target
#+END_SRC

List build directory:

#+BEGIN_SRC sh 
  $ ls _build_cross
  CMakeCache.txt CMakeFiles  Makefile  _deps  cmake_install.cmake  draw2d-raw.exe  glew32.dll
#+END_SRC

Run executable with *wine* on Linux: 

#+BEGIN_SRC sh 
  $ wine _build_cross/draw2d-raw.exe 
#+END_SRC

 *Building on Windows with MINGW (GCC ported to Windows)* 

#+BEGIN_SRC sh 
  $ cmake -H. -B_build_mingw -G "MinGW Makefiles" -DCMAKE_BUILD_TYPE=Debug
  $ cmake --build _build_mingw --target
#+END_SRC

Run executable: 

#+BEGIN_SRC sh 
  # On Window
  $ _build_mingw\draw2d-raw.exe 

  # on Linux via wine 
  $ wine _build_mingw\draw2d-raw.exe  
#+END_SRC

 *Building with Xmake building system*

Display Xmake version: 

#+BEGIN_SRC sh
  $ xmake --version | grep -i "cross-platform" 2> /dev/null
  xmake v2.6.9+202208081520, A cross-platform build utility based on Lua
#+END_SRC

Enter XMake - Lua interactive REPL (Read-Eval-Print-Loop):

#+BEGIN_SRC lua 
  $ xmake lua 
  xmake> 
  
  xmake> -- Get host operating system
  xmake> os.host()
  < "linux"
  
  xmake> -- Get host architecture
  xmake> os.arch()
  < "x86_64"
  
  xmake> -- Open Root Linux's directory
  xmake> os.execv("xdg-open /")
  < 1: 0
  < 2: nil
  
  xmake> -- Check whether file exists
  xmake> os.exists("./draw2d-raw.cpp")
  < true
  xmake> os.exists("./draw2d-raw.cpp.back")
  < false
  
  xmake> -- Get current directory
  xmake> os.curdir()
  < "/home/user/opengl1"
  
  xmake> -- Get project directory
  xmake> os.projectdir()
  < "/home/user/opengl1"
  
  xmake> -- Get scripting directory 
  xmake> os.scriptdir()
  < "/home/user/.local/share/xmake/plugins/lua"
  
  xmake> -- Download file from http server
  xmake> http = import("net.http")
  xmake> http.download("https://github.com/glfw/glfw/releases/download/3.3.2/glfw-3.3.2.zip", "glfw-source.zip")
  xmake> os.exists("glfw-source.zip")
  < true
  
  xmake> -- Extract archive 
  xmake> r = import("utils.archive")
  xmake> r.extract("./glfw-source.zip", "/tmp/glfw-src")
  
  
  -- Check current platform (All return false in the REPL)
  xmake> is_plat("windows")
  < false
  xmake> is_plat("linux")
  < false
  xmake> is_plat("unix")
  < false
  
  xmake> -- Check whether the current mode is Debug or Release (All fail in the REPL)
  xmake> is_mode("debug")
  < false
  xmake> is_mode("release")
  < false
  
  xmake> -- Join paths 
  xmake> p = path.join( os.projectdir(), "build", "path1", "path2", "app.exe")
  xmake> p
  < "/home/user/opengl1/build/path1/path2/app.exe"


  -- Environment variable 
  xmake> os.getenv("HOME")
  < "/home/user"

  xmake> os.getenv("SHELL")
  < "/bin/bash"

  xmake> os.getenv("TERM")
  < "xterm-256color"
#+END_SRC

 
List repositories: 

  + $ xrepo list-repo 

#+BEGIN_SRC sh 
  $ xrepo list-repo
  global repositories:
      build-artifacts https://gitlab.com/xmake-mirror/build-artifacts.git main 
      xmake-repo https://gitlab.com/tboox/xmake-repo.git master 
      builtin-repo /home/user/.local/share/xmake/repository 
  
  3 repositories were found!
#+END_SRC


Search Xmake packages: 

  + $ xrepo search <PACKAGE>

#+BEGIN_SRC sh 
  $ xrepo search boost
  The package names:
      boost: 
        -> boost-1.79.0: Collection of portable C++ source libraries. (in xmake-repo)
  
  $ xrepo search glut
  The package names:
      glut: 
        -> freeglut-3.2.2: A free-software/open-source alternative to the OpenGL Utility Toolkit (GLUT) library. (in xmake-repo)
        -> glut: OpenGL utility toolkit (in xmake-repo)

#+END_SRC

Install package: 

  + $ xrepo install <PACKAGE>

#+BEGIN_SRC sh 
   $ xrepo install boost
  
  note: install or modify (m) these packages (pass -y to skip confirm)?
  in xmake-repo:
    -> bzip2 1.0.8 [from:boost]
    -> boost 1.79.0 
  please input: y (y/n/m)
  y
    => download https://sourceware.org/pub/bzip2/bzip2-1.0.8.tar.gz .. ok
#+END_SRC


List package information: 
  
  + $ xrepo info <PACKAGE>

#+BEGIN_SRC sh 
   $ >> xrepo info glm
  The package info of project:
      require(glm): 
        -> description: OpenGL Mathematics (GLM)
        -> version: 0.9.9+8
        -> urls:
           -> https://github.com/g-truc/glm/archive/0.9.9.8.tar.gz
              -> 7d508ab72cb5d43227a3711420f06ff99b0a0cb63ee2f93631b162bfe1fe9592
           -> https://github.com/g-truc/glm.git
              -> 0.9.9+8
        -> repo: xmake-repo https://gitlab.com/tboox/xmake-repo.git master
        -> cachedir: /home/user/.xmake/cache/packages/2208/g/glm/0.9.9+8
        -> installdir: /home/user/.xmake/packages/g/glm/0.9.9+8/65b1ad153bda4a43b0454eba7969327f
        -> searchdirs: 
        -> searchnames: 0.9.9.8.tar.gz, glm.git, glm-0.9.9+8.tar.gz, glm-0.9.9+8
        -> fetchinfo: 0.9.9+8
            -> sysincludedirs: /home/user/.xmake/packages/g/glm/0.9.9+8/65b1ad153bda4a43b0454eba7969327f/include
            -> version: 0.9.9+8
        -> platforms: all
        -> requires:
           -> plat: linux
           -> arch: x86_64
           -> configs:
              -> debug: false
              -> pic: true
              -> shared: false
        -> configs:
        -> configs (builtin):
           -> debug: Enable debug symbols. (default: false)
           -> shared: Build shared library. (default: false)
           -> pic: Enable the position independent code. (default: true)
           -> lto: Enable the link-time build optimization. (type: boolean)
           -> vs_runtime: Set vs compiler runtime.
              -> values: {"MT","MTd","MD","MDd"}
           -> toolchains: Set package toolchains only for cross-compilation.
           -> cflags: Set the C compiler flags.
           -> cxflags: Set the C/C++ compiler flags.
           -> cxxflags: Set the C++ compiler flags.
           -> asflags: Set the assembler flags.
        -> references:
           -> 220814: /dev/shm/.xmake1000/220814/xrepo/working
           -> 220813: /home/user/opengl1
#+END_SRC


List project information:

 + $ xmake show 

#+BEGIN_SRC sh
 $ xmake show

 The information of xmake:
     version: 2.6.9+202208081520
     host: linux/x86_64
     programdir: /home/user/.local/share/xmake
     programfile: /home/user/.local/bin/xmake
     globaldir: /home/user/.xmake
     tmpdir: /dev/shm/.xmake1000/220808
     workingdir: /home/user/opengl1
     packagedir: /home/user/.xmake/packages
     packagedir(cache): /home/user/.xmake/cache/packages/2208
 
 The information of project: 
     plat: linux
     arch: x86_64
     mode: release
     buildir: build
     configdir: /home/user/opengl1/.xmake/linux/x86_64
     projectdir: /home/user/opengl1
     projectfile: /home/user/opengl1/xmake.lua
#+END_SRC

Configure building for Linux: 

 + $ xmake f -p linux 

#+BEGIN_SRC sh 
   $ >> xmake f -p linux
  checking for architecture ... x86_64
#+END_SRC


Building: 

  + $ xmake build 

#+BEGIN_SRC sh 
   $ >> xmake build -v
  [ 25%]: ccache compiling.release draw2d-raw.cpp
  /usr/bin/gcc -c -m64 -fvisibility=hidden -fvisibility-inlines-hidden -O3 -DGLEW_NO_GLU -D_GLFW_X11 -DGLFW_INCLUDE_NONE \
                 -isystem /home/user/.xmake/packages/g/glew/2.2.0/2ac0c6940a2140f7b6506fcd8240f406/include \
                 -isystem /home/user/.xmake/packages/g/glfw/3.3.8/aa037e96cc854680a09e144c4b5c8fe0/include \
                 -isystem /usr/include/X11/dri -isystem /home/user/.xmake/packages/g/glm/0.9.9+8/65b1ad153bda4a43b0454eba7969327f/include \
                 -DNDEBUG -o build/.objs/draw2d/linux/x86_64/release/draw2d-raw.cpp.o draw2d-raw.cpp
  
  [ 50%]: linking.release draw2d
  /usr/bin/g++ -o build/linux/x86_64/release/draw2d build/.objs/draw2d/linux/x86_64/release/draw2d-raw.cpp.o -m64 \
                 -L/home/user/.xmake/packages/g/glew/2.2.0/2ac0c6940a2140f7b6506fcd8240f406/lib \
                 -L/home/user/.xmake/packages/g/glfw/3.3.8/aa037e96cc854680a09e144c4b5c8fe0/lib \
                  -L/usr/lib/x86_64-linux-gnu -s \
                  -lglew -lglfw3 -lOpenGL -lXrandr -lXinerama -lXcursor -lXrender -lX11 -lXi -lXfixes \
                  -lXext -lxcb -lssl -lcrypto -lffi -lz -lXau -lXdmcp -lGL -ldl -lpthread
  [100%]: build ok!
#+END_SRC


Inspecting compilation output:

#+BEGIN_SRC sh
  $ tree build
  build
  └── linux
      └── x86_64
          └── release
              └── draw2d
  
  3 directories, 1 file
#+END_SRC

Running the default xmake target: 

 + $ xmake run 

#+BEGIN_SRC sh
  $ xmake run
#+END_SRC

Running a specific xmake target:
  
 + $ xmake run draw2d

#+BEGIN_SRC sh 
  $ xmake run draw2d
#+END_SRC

Generate file containing compilation commands: 

#+BEGIN_SRC sh
  $ xmake project -k compile_commands
  create ok!

  $ cat compile_commands.json 
  [
  {
    "directory": "/home/user/opengl1",
    "arguments": ["/usr/bin/gcc", "-c", "-m64", "-fvisibility=hidden", "-fvisibility-inlines-hidden", "-O3", "-DGLEW_NO_GLU", "-D_GLFW_X11", "-DGLFW_INCLUDE_NONE", "-I", "/home/user/.xmake/packages/g/glew/2.2.0/2ac0c6940a2140f7b6506fcd8240f406/include", "-I", "/home/user/.xmake/packages/g/glfw/3.3.8/aa037e96cc854680a09e144c4b5c8fe0/include", "-I", "/usr/include/X11/dri", "-I", "/home/user/.xmake/packages/g/glm/0.9.9+8/65b1ad153bda4a43b0454eba7969327f/include", "-DNDEBUG", "-o", "build/.objs/draw2d/linux/x86_64/release/draw2d-raw.cpp.o", "draw2d-raw.cpp"],
    "file": "draw2d-raw.cpp"
  }]
#+END_SRC

Generate a CMake project file (note - this file must not be under version control): 

  + $ xmake project -k cmake

#+BEGIN_SRC sh 
  $ xmake project -k cmake
  create ok!
#+END_SRC

CMake file generated by Xmake (CMakeLists.txt): 

#+BEGIN_SRC sh 
  # this is the build file for project 
  # it is autogenerated by the xmake build system.
  # do not edit by hand.
  
  # project
  cmake_minimum_required(VERSION 3.15.0)
  cmake_policy(SET CMP0091 NEW)
  project(draw2d LANGUAGES CXX C)
  
  # target
  add_executable(draw2d "")
  set_target_properties(draw2d PROPERTIES OUTPUT_NAME "draw2d")
  set_target_properties(draw2d PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${CMAKE_SOURCE_DIR}/build/linux/x86_64/release")
  target_include_directories(draw2d PRIVATE
      /home/user/.xmake/packages/g/glew/2.2.0/2ac0c6940a2140f7b6506fcd8240f406/include
      /usr/include/X11/dri
      /home/user/.xmake/packages/g/glfw/3.3.6/703496809fe34548a9dbcf268bfa4db5/include
      /home/user/.xmake/packages/g/glm/0.9.9+8/65b1ad153bda4a43b0454eba7969327f/include
  )
  target_compile_definitions(draw2d PRIVATE
      GLEW_NO_GLU
      _GLFW_X11
      GLFW_INCLUDE_NONE
  )
  target_compile_options(draw2d PRIVATE
      $<$<COMPILE_LANGUAGE:C>:-m64>
      $<$<COMPILE_LANGUAGE:CXX>:-m64>
      $<$<COMPILE_LANGUAGE:C>:-DNDEBUG>
      $<$<COMPILE_LANGUAGE:CXX>:-DNDEBUG>
  )
  if(MSVC)
      target_compile_options(draw2d PRIVATE $<$<CONFIG:Release>:-Ox -fp:fast>)
  else()
      target_compile_options(draw2d PRIVATE -O3)
  endif()
  if(MSVC)
  else()
      target_compile_options(draw2d PRIVATE -fvisibility=hidden)
  endif()
  if(MSVC)
      set_property(TARGET draw2d PROPERTY
          MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")
  endif()
  target_link_libraries(draw2d PRIVATE
      glew
      X11
      xcb
      ssl
      crypto
      ffi
      z
      Xau
      Xdmcp
      glfw3
      OpenGL
      Xrandr
      Xinerama
      Xcursor
      Xrender
      Xi
      Xfixes
      Xext
      GL
      dl
      pthread
  )
  target_link_directories(draw2d PRIVATE
      /home/user/.xmake/packages/g/glew/2.2.0/2ac0c6940a2140f7b6506fcd8240f406/lib
      /usr/lib/x86_64-linux-gnu
      /home/user/.xmake/packages/g/glfw/3.3.6/703496809fe34548a9dbcf268bfa4db5/lib
  )
  target_link_options(draw2d PRIVATE
      -m64
  )
  target_sources(draw2d PRIVATE
      draw2d-raw.cpp
  )

#+END_SRC

Now it is possible to edit the project with any IDE or smart editor
that supports CMake, including _Visual Studio Code - Vscode_ or _CLion_:

#+BEGIN_SRC sh 
  # Edit project with vscode using the CMakeLists.txt as project file
  $ vscode .  
#+END_SRC


 *Building with Xmake building system on Windows* 

Configure XMake for using MinGW (GCC Ported to Windows)
 
#+BEGIN_SRC sh
  $ xmake f -p mingw 
#+END_SRC

Show project information:

#+BEGIN_SRC sh
  $ xmake show
  
  The information of xmake:
      version: 2.7.1+master.1acf248c0
      host: windows/x64
      programdir: C:\Program Files\xmake
      programfile: C:\Program Files\xmake\xmake.exe
      globaldir: C:\Users\ghostuser\AppData\Local\.xmake
      tmpdir: C:\Users\ghostuser\AppData\Local\Temp\.xmake\220902
      workingdir: D:\opengl
      packagedir: C:\Users\ghostuser\AppData\Local\.xmake\packages
      packagedir(cache): C:\Users\ghostuser\AppData\Local\.xmake\cache\packages\2209
  
  The information of project:
      plat: mingw
      arch: x86_64
      mode: release
      buildir: build
      configdir: D:\opengl\.xmake\windows\x64
      projectdir: D:\opengl
      projectfile: D:\opengl\xmake.lua
#+END_SRC 

Build the application showing all compilation and linking commands:

#+BEGIN_SRC sh
  $ xmake build -v

  [ 25%]: cache compiling.release draw2d-raw.cpp
  "C:\\Program Files\\mingw-w64\\x86_64-8.1.0-posix-seh-rt_v6-rev0\\mingw64\\bin\\x86_64-w64-mingw32-g++" -c \
      -m64 -fvisibility=hidden -fvisibility-inlines-hidden -O3 -DGLEW_NO_GLU -DGLEW_STATIC -DGLFW_INCLUDE_NONE \
      -isystem C:\Users\ghostuser\AppData\Local\.xmake\packages\g\glew\2.2.0\f1937cf4c620461cac69417973f0b9e2\include \
      -isystem C:\Users\ghostuser\AppData\Local\.xmake\packages\g\glfw\3.3.8\580b60fd05d941e2abb98f9baa25683e\include \
      -isystem C:\Users\ghostuser\AppData\Local\.xmake\packages\g\glm\0.9.9+8\53c948164ca94a25beb29600f690b730\include -DNDEBUG \
      -o build\.objs\draw2d\mingw\x86_64\release\draw2d-raw.cpp.obj draw2d-raw.cpp
  
  [ 50%]: linking.release draw2d.exe
  "C:\\Program Files\\mingw-w64\\x86_64-8.1.0-posix-seh-rt_v6-rev0\\mingw64\\bin\\x86_64-w64-mingw32-g++" \
      -o build\mingw\x86_64\release\draw2d.exe build\.objs\draw2d\mingw\x86_64\release\draw2d-raw.cpp.obj -m64 \
      -LC:\Users\ghostuser\AppData\Local\.xmake\packages\g\glew\2.2.0\f1937cf4c620461cac69417973f0b9e2\lib \
      -LC:\Users\ghostuser\AppData\Local\.xmake\packages\g\glfw\3.3.8\580b60fd05d941e2abb98f9baa25683e\lib \
      -s -lglew32s -lglfw3 -lopengl32 -lgdi32
  [100%]: build ok!
#+END_SRC

Run the executable:

#+BEGIN_SRC sh
  $ xmake run 
#+END_SRC

Generate CMakeLists.txt:

#+BEGIN_SRC sh 
    $ xmake project -k cmake 
#+END_SRC

Build the project using the CMakeListst.txt file:

#+BEGIN_SRC sh
 $ cmake -H. -B_build -G "MinGW Makefiles" -DCMAKE_BUILD_TYPE=Debug

 $ cmake --build _build --target all
 [ 50%] Building CXX object CMakeFiles/draw2d.dir/draw2d-raw.cpp.obj
 [100%] Linking CXX executable ..\build\mingw\x86_64\release\draw2d.exe
 [100%] Built target draw2d
#+END_SRC 


File: _CMakeLists.txt_ - generated by XMake 

#+BEGIN_SRC cmake 
  # this is the build file for project 
  # it is autogenerated by the xmake build system.
  # do not edit by hand.
  
  # project
  cmake_minimum_required(VERSION 3.15.0)
  cmake_policy(SET CMP0091 NEW)
  project(draw2d LANGUAGES CXX C)
  
  # target
  add_executable(draw2d "")
  set_target_properties(draw2d PROPERTIES OUTPUT_NAME "draw2d")
  set_target_properties(draw2d PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${CMAKE_SOURCE_DIR}/build/mingw/x86_64/release")
  target_include_directories(draw2d PRIVATE
      C:/Users/ghostuser/AppData/Local/.xmake/packages/g/glew/2.2.0/f1937cf4c620461cac69417973f0b9e2/include
      C:/Users/ghostuser/AppData/Local/.xmake/packages/g/glfw/3.3.8/580b60fd05d941e2abb98f9baa25683e/include
      C:/Users/ghostuser/AppData/Local/.xmake/packages/g/glm/0.9.9+8/53c948164ca94a25beb29600f690b730/include
  )
  target_compile_definitions(draw2d PRIVATE
      GLEW_NO_GLU
      GLEW_STATIC
      GLFW_INCLUDE_NONE
  )
  target_compile_options(draw2d PRIVATE
      $<$<COMPILE_LANGUAGE:C>:-m64>
      $<$<COMPILE_LANGUAGE:CXX>:-m64>
      $<$<COMPILE_LANGUAGE:C>:-DNDEBUG>
      $<$<COMPILE_LANGUAGE:CXX>:-DNDEBUG>
  )
  if(MSVC)
      target_compile_options(draw2d PRIVATE $<$<CONFIG:Release>:-Ox -fp:fast>)
  else()
      target_compile_options(draw2d PRIVATE -O3)
  endif()
  if(MSVC)
  else()
      target_compile_options(draw2d PRIVATE -fvisibility=hidden)
  endif()
  if(MSVC)
      set_property(TARGET draw2d PROPERTY
          MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")
  endif()
  target_link_libraries(draw2d PRIVATE
      glew32s
      glfw3
      opengl32
      opengl32
      gdi32
  )
  target_link_directories(draw2d PRIVATE
      C:/Users/ghostuser/AppData/Local/.xmake/packages/g/glew/2.2.0/f1937cf4c620461cac69417973f0b9e2/lib
      C:/Users/ghostuser/AppData/Local/.xmake/packages/g/glfw/3.3.8/580b60fd05d941e2abb98f9baa25683e/lib
  )
  target_link_options(draw2d PRIVATE
      -m64
  )
  target_sources(draw2d PRIVATE
      draw2d-raw.cpp
  )

#+END_SRC

** Minimal Dlang (D) GLFW and XMake project 

This section provides a minimal OpenGL-GLFW project in [[https://dlang.org/][Dlang (D)]]
programming language with [[https://xmake.io/][XMake]] building system, that uses Lua as an
embedded scripting language and as a DSL (Domain Specific Language) for
describing a projects in multiple programming languages including C,
C++, DLang, Fortran, Rust, Zim, Nim and so on. 

The Lua scripting file, xmake.lua describes the project compilation
process that uses multiple programming languages, such as D (DLang)
and C programming language, used by Glew and GLFw libraries.

 *Project Files*

File: _xmake.lua_

#+BEGIN_SRC lua 
  add_rules("mode.debug", "mode.release")
  
  add_requires("glew", "glfw")
  
  target("draw-dlang")
    set_kind("binary")
    add_files("draw_dlang.d")
    add_packages("glew", "glfw")
    -- Customize action $ xmake run draw-dlang 
    on_run(function (target)
      print(" [XMAKE HOOK] Running a DLang - D plus OpenGL application.")
       -- print(" [TARGET] target = ", target)
      print(" [XMAKE HOOK] Running file = ", target:targetfile())
      print(" [XMAKE HOOK] Target directory = ", target:targetdir())
       -- Run executable (target file) generated by compilation.
      os.run(target:targetfile())
   end)
  
  -- Pseudo target for running a script 
  target("script")
    on_run(function()
      print(" [XMAKE-SCRIPT] Running a script in this target.") 
    end)
#+END_SRC

File: _draw_dlang.d_ 

#+BEGIN_SRC cpp 
  import core.stdc.stdio;
  import std.range: empty;
  import str = std.string;
  import std.stdio: writeln;
  
  const     GLFW_RELEASE    = 0;
  const int GLFW_PRESS      = 1;
  const int GL_ARRAY_BUFFER = 0x8892;
  const int GL_LINE_LOOP    = 0x0002;
  const int GL_TRIANGLES    = 0x0004;
  const int GL_FLOAT        = 0x1406;
  const int GL_STATIC_DRAW  = 0x88E4;
  const int GL_FALSE        = 0;
  const int GL_TRUE         = 1;
  
  struct Vertex2D
  {
      GLfloat x;
      GLfloat y;
  }
  
  int main()
  {
      writeln("GLFW with DLang - D Programming language");
  
      if (!glfwInit()){ return -1; }
  
      /* Create a windowed mode window and its OpenGL context */
      GLFWwindow* window = glfwCreateWindow(640, 480, "2D Drawing with Dlang, OpenGL and GLFW", null, null);
  
      if (!window)
      {
          glfwTerminate();
          return -1;
      }
  
       /* Make the window's context current */
      glfwMakeContextCurrent(window);
      // glClearColor(0.0f, 0.5f, 0.6f, 1.0f);
      glClearColor(0.0f, 0.0f, 0.0, 1.0f);
  
      Vertex2D[3] triangle_points = [
          Vertex2D(-0.25,   -0.25)
        , Vertex2D( 0.00,   0.25)
        , Vertex2D( 0.25,  -0.25)
      ];
  
  
      Vertex2D[4] square_points = [
          { -0.3,  -0.3 }
        , { -0.3,   0.3 }
        , {  0.3,   0.3 }
        , {  0.3,  -0.3 }
      ];
  
  
      // Create an OpenGL VBO buffer and send vertices to GPU
      // =>> void glGenBuffers (GLsizei n, GLuint *buffers);
      GLuint vbo_triangle_ = 0;
      glGenBuffers(1, &vbo_triangle_ );
      glBindBuffer(GL_ARRAY_BUFFER, vbo_triangle_);
      glBufferData(GL_ARRAY_BUFFER, triangle_points.sizeof, &triangle_points, GL_STATIC_DRAW);
  
      GLuint vbo_square_ = 0;
      glGenBuffers(1, &vbo_square_);
      assert( vbo_square_ != 0);
      glBindBuffer(GL_ARRAY_BUFFER, vbo_square_);
      // glBufferData(GL_ARRAY_BUFFER, sizeof(square_points), square_points, GL_STATIC_DRAW);
      glBufferData(GL_ARRAY_BUFFER, square_points.sizeof, &square_points, GL_STATIC_DRAW);
      glBindBuffer(GL_ARRAY_BUFFER, 0);
  
      GLint shader_attr = 0;
  
      while ( !glfwWindowShouldClose(window) )
      {
  
          glfwSwapBuffers(window);
          
          glEnableVertexAttribArray(shader_attr);
  
          // ---------- Draw Triangle -----------------//
          //===========================================//
          glBindBuffer(GL_ARRAY_BUFFER, vbo_triangle_);
          glVertexAttribPointer(
              // GLint  index => Shader attribute location, 0 for now
              shader_attr 
              // GLint  size  => 2 components (X, Y) of type GLfloat
            , 2           
              // GLemum type  => Type of each component
            , GL_FLOAT    
              // GLboolean normalized
            , GL_FALSE    
              // GLsizei stride
            , 0           
              // const void* pointer
            , null       
            );
          // Plot 1 triangle (each triangle has 3 vertices)
          glDrawArrays(GL_TRIANGLES, 0, 3);
  
  
          // ----------- Draw Square ------------------// 
          //===========================================//
          glBindBuffer(GL_ARRAY_BUFFER, vbo_square_);
          glEnableVertexAttribArray(shader_attr);
          glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, null);
          // Plot 4 vertices
          glDrawArrays(GL_LINE_LOOP, 0, 4);
  
  
          glfwPollEvents();
  
          if( glfwGetKey(window, 'Q' ) == GLFW_PRESS )
          {
              printf("Shutdown program Ok. \n");
               break;
          }
  
      }
  
  
      printf(" [TRACE] Application started Ok. \n");
      glfwTerminate();
      return 0;
  }
  
     // ------------------ D E F I N I T I O N S   -------------------------//
  
  
  // Opaque types 
  alias GLFWwindow = void;
  alias GLFWmonitor = void;
  
  
  alias GLenum     = uint;
  alias GLboolean  = char; 
  alias GLbitfield = uint;  
  alias GLvoid     = void; 
  alias GLbyte     = char; 
  alias GLshort    = short;
  alias GLint      = int;
  alias GLubyte    = char;
  alias GLushort   = ushort;
  alias GLuint     = uint;
  alias GLsizei    = int;
  alias GLfloat    = float;
  alias GLclampf   = float;
  alias GLdouble   = double;
  alias GLclampd   = double;
  
  alias khronos_ssize_t  = long;
  alias khronos_intptr_t = long;
  alias GLsizeiptr       = khronos_ssize_t;
  alias GLintptr         = khronos_intptr_t;
  
  extern(C) int   glfwInit(); 
  extern(C) void  glfwTerminate();
  extern(C) void  glfwMakeContextCurrent(GLFWwindow* window);
  extern(C) int   glfwWindowShouldClose(GLFWwindow* window);
  extern(C) int   glfwGetKey(GLFWwindow* window, int key);
  extern(C) void  glfwPollEvents();
  extern(C) void  glfwSwapBuffers(GLFWwindow* window);
  extern(C) void  glClear(GLbitfield);
  
  
  extern(C) GLFWwindow* glfwCreateWindow(int width, int height, const char* title
                              , GLFWmonitor* monitor, GLFWwindow* share);
  
  
  // ----- OpenGL APIs ---------------------- //
  //
  extern(C) void glBindBuffer (GLenum target, GLuint buffer);
  extern(C) void glClearColor( GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha );
  extern(C) void glGenBuffers (GLsizei n, GLuint *buffers);
  extern(C) void glEnableVertexAttribArray (GLuint index);
  extern(C) void glDrawArrays( GLenum mode, GLint first, GLsizei count );
  
  extern(C) void glBufferData (GLenum target, GLsizeiptr size
                              , const void *data, GLenum usage);
  
  extern(C) void glVertexAttribPointer (GLuint index, GLint size, GLenum type
                                      , GLboolean normalized, GLsizei stride
                                      , const void *pointer);

#+END_SRC

 
 *Building with XMake* 

Build only the target _draw-dlang_: 

#+BEGIN_SRC sh 
  $ xmake build -v draw-dlang  
  
  checking for dmd ... /home/user/dlang/dmd-2.100.0/linux/bin64/dmd
  checking for the dlang compiler (dc) ... dmd
  checking for /home/user/dlang/dmd-2.100.0/linux/bin64/dmd ... ok
  checking for flags (-O -release -inline -boundscheck=off) ... ok
  [ 25%]: compiling.release draw_dlang.d
  /home/user/dlang/dmd-2.100.0/linux/bin64/dmd -c -m64 -O -release -inline -boundscheck=off \
            -I/home/user/.xmake/packages/g/glew/2.2.0/2ac0c6940a2140f7b6506fcd8240f406/include \
            -I/home/user/.xmake/packages/g/glfw/3.3.8/aa037e96cc854680a09e144c4b5c8fe0/include \
            -I/usr/include/X11/dri -ofbuild/.objs/draw-dlang/linux/x86_64/release/draw_dlang.d.o draw_dlang.d
  checking for dmd ... /home/user/dlang/dmd-2.100.0/linux/bin64/dmd
  checking for the dlang linker (dcld) ... dmd
  
  [ 50%]: linking.release draw-dlang
  /home/user/dlang/dmd-2.100.0/linux/bin64/dmd -m64 \
            -L-L/home/user/.xmake/packages/g/glew/2.2.0/2ac0c6940a2140f7b6506fcd8240f406/lib \
            -L-L/home/user/.xmake/packages/g/glfw/3.3.8/aa037e96cc854680a09e144c4b5c8fe0/lib \
            -L-L/usr/lib/x86_64-linux-gnu -L-s -L-lglew -L-lglfw3 -L-lOpenGL -L-lXrandr -L-lXinerama -L-lXcursor \
            -L-lXrender -L-lX11 -L-lXi -L-lXfixes -L-lXext -L-lxcb -L-lssl -L-lcrypto -L-lffi \
            -L-lz -L-lXau -L-lXdmcp -L-lGL -L-ldl -L-lpthread \
            -ofbuild/linux/x86_64/release/draw-dlang \
            build/.objs/draw-dlang/linux/x86_64/release/draw_dlang.d.o
  [100%]: build ok!
#+END_SRC

Run the scripting target: 

#+BEGIN_SRC sh 
 $ xmake run script
  [XMAKE-SCRIPT] Running a script in this target.
#+END_SRC

Run the target _draw-dlang_:

#+BEGIN_SRC sh 
  $ xmake run draw-dlang
   [XMAKE HOOK] Running a DLang - D plus OpenGL application.
   [XMAKE HOOK] Running file =  build/linux/x86_64/release/draw-dlang
   [XMAKE HOOK] Target directory =  build/linux/x86_64/release
#+END_SRC

Screenshot: 

[[file:images/opengl-draw2d-dlang1.png][file:images/opengl-draw2d-dlang1.png]]

** Minimal SDL project 

The next code uses the SLD (Simple Direct Media Layer) library instead
of GLFW for window system abstraction. The advantage of SDL over GLFW
is that, SDL supports more platforms and is able to deal with audio
and image loading, which are essential features for rich multimedia
applications. This CMake script automatically downloads SDL and
defines a macro for making it easier to create OpenGL apps with
SDL statically linked.  

SDL Web Sites: 

  + https://www.libsdl.org/

  + https://github.com/libsdl-org/SDL (Repository)

  + https://github.com/libSDL2pp/libSDL2pp (C++ Wrapper)


 *Project Files* 

File: _CMakeLists.txt_ 

#+BEGIN_SRC cmake 
   cmake_minimum_required(VERSION 3.5)
   project(OPENGL_SDL_project)

   set(CMAKE_CXX_STANDARD 17)
   set(CMAKE_CXX_STANDARD_REQUIRED ON)
   set(CMAKE_VERBOSE_MAKEFILE ON)

   #================ GLFW Settings  ===============#

   find_package(OpenGL REQUIRED)

   include(FetchContent)
   # Download GLM (OpenGL math library for matrix and vectors transformation)
   FetchContent_Declare(
     glm  
     URL  https://github.com/g-truc/glm/archive/0.9.8.zip
   )
   FetchContent_MakeAvailable(glm)
   include_directories(${glm_SOURCE_DIR} )

   # Donwload library SLD - Simple Direct Media Layer 
   FetchContent_Declare(
     sdl  
     URL    https://github.com/libsdl-org/SDL/archive/refs/tags/release-2.0.14.zip 
   )
   FetchContent_MakeAvailable(sdl)
   # find_package( SDL REQUIRED )

   file(MAKE_DIRECTORY  ${sdl_SOURCE_DIR}/headers/SDL2)
   file(COPY  ${sdl_SOURCE_DIR}/include/ 
              DESTINATION ${sdl_SOURCE_DIR}/headers/SDL2 
              FILES_MATCHING PATTERN "*.h"
              )

                  add_library( sdl-include  INTERFACE )
   target_include_directories( sdl-include  INTERFACE ${sdl_SOURCE_DIR}/headers )
   # Option for making this target IDE-friendly
   IF(false)
   target_sources( sdl-include INTERFACE  
                                 ${sdl_SOURCE_DIR}/headers/SDL2/SDL.h   
                                 ${sdl_SOURCE_DIR}/headers/SDL2/SDL_opengl.h  
                              )

   ENDIF()

   # Download pre-compiled GLEW when building under Windows NT OS (x64)
   IF(WIN32)
      FetchContent_Declare(
         glew-release 
         URL   https://ufpr.dl.sourceforge.net/project/glew/glew/2.1.0/glew-2.1.0-win32.zip 
         # https://github.com/nigels-com/glew/archive/glew-2.2.0.zip
      )
      FetchContent_MakeAvailable(glew-release)
      include_directories( ${glew-release_SOURCE_DIR}/include  ${glm_SOURCE_DIR} )        
      link_directories(  ${glew-release_SOURCE_DIR}/lib/Release/x64 )

      set( GLEW_LIB_PATH1 ${glew-release_SOURCE_DIR}/lib/Release/x64/glew32.lib )
      set( GLEW_LIB_PATH2 ${glew-release_SOURCE_DIR}/lib/Release/x64/glew32s.lib )
   ENDIF()

   MACRO(ADD_OPENGL_APP target sources)
      add_executable( ${target} ${sources} )
      message([TRACE] " Add OpenGL executable = ${target} ")

      target_link_libraries( ${target} OpenGL::GL SDL2-static sdl-include 
                                       ${GLEW_LIB_PATH1}
                                       ${GLEW_LIB_PATH2} )

       IF(MINGW)
           # Statically link against MINGW dependencies
           # for making easier to deploy on other machines. 
           target_link_options( ${target} PRIVATE                                 
                                    -static-libgcc
                                    -static-libstdc++
                                    -Wl,-Bstatic,--whole-archive -lwinpthread
                                      -Wl,--no-whole-archive                                    
                                    )
        ENDIF()       


        # Copy GLEW DLL shared library to same directory as the executable.                 
        IF(WIN32)                           
           add_custom_command(TARGET ${target} POST_BUILD 
                          COMMAND ${CMAKE_COMMAND} -E copy_if_different
                          "${glew-release_SOURCE_DIR}/bin/Release/x64/glew32.dll"              
                          $<TARGET_FILE_DIR:${target}>)
        ENDIF()                                       
   ENDMACRO()     


         #========== Targets Configurations ================#

   ADD_OPENGL_APP( opengl-sdl opengl-sdl.cpp )

#+END_SRC


File: _xmake.lua_  (See: [[https://github.com/xmake-io/xmake-repo/blob/master/packages/l/libsdl/xmake.lua][libsdl]] package)

#+BEGIN_SRC lua 
  add_rules("mode.debug", "mode.release")
  
  add_requires("glm", "glew", "libsdl 2.0.14")
  
  target("draw2d")
    set_kind("binary")
    add_files("./opengl-sdl.cpp")
    add_packages("glm", "glew", "libsdl")
#+END_SRC


File: _opengl-sdl.cpp_ 

#+BEGIN_SRC cpp 
  #include <iostream>
  #include <string> 
  #include <cassert>

  #if defined(_WIN32)
     #include <windows.h>
     #include <GL/glew.h>
  #endif

  #define GL_GLEXT_PROTOTYPES 1
  #define GL3_PROTOTYPES      1
  #include <GL/gl.h> 
  #include <GL/glu.h> 

  #define SDL_MAIN_HANDLED
  #include <SDL2/SDL.h>
  #include <SDL2/SDL_opengl.h>

  // --------- OpenGL Math Library ------------//
  #include <glm/glm.hpp>
  #include <glm/gtc/matrix_transform.hpp>
  #include <glm/gtc/type_ptr.hpp>
  #include <glm/gtx/string_cast.hpp>

  // Wrapper for 2D vertex coordinates
  struct Vertex2D{  GLfloat x, y;  };
  // Wrapper for RGB colors
  struct ColorRGB { GLfloat r, g, b; };

  constexpr ColorRGB COLOR_R = { 1.0, 0.0, 0.0  }; // Red
  constexpr ColorRGB COLOR_G = { 0.0, 1.0, 0.0  }; // Green
  constexpr ColorRGB COLOR_B = { 0.0, 0.0, 1.0  }; // Blue

  void compile_shader(GLuint m_program, const char* code, GLenum type);

  void send_buffer( GLuint* pVao, GLuint* pVbo, GLsizei sizeBuffer
                  , void* pBufffer, GLint   shader_attr, GLint size
                  , GLenum type);

  extern const char* shader_vertex; 
  extern const char* shader_fragment;

  const glm::mat4 matrix_identity = glm::mat4(1.0);


  int main() 
  {
      std::puts(" [TRACE] OpenGL SDL started Ok. ");

      int window_x = 100, window_y = 100, window_w = 500, window_h = 400;
      SDL_Window* window = SDL_CreateWindow(  
                                  "OpenGL with SLD library"
                                 , window_x, window_y  // Window position 
                                 , window_w, window_h  // Window width and height 
                                 , SDL_WINDOW_OPENGL   // Bitwise-OR flags  
                                 );
   
      SDL_GLContext ctx = SDL_GL_CreateContext(window);

      // --- Initialize GLEW library after OpenGL context --------- //
      #if _WIN32
      glewExperimental = GL_TRUE;
      if (glewInit() != GLEW_OK) {
          std::cerr << "GLEW::Error : failed to initialize GLEW" << std::endl;
          std::abort();
      }
      std::fprintf(stderr, " [TRACE] Glew Initialized Ok. \n");
      #endif
    
      // ====== C O M P I L E  - S H A D E R ================== // 

      GLuint prog = glCreateProgram();
      compile_shader(prog, shader_vertex,   GL_VERTEX_SHADER   ) ;
      compile_shader(prog, shader_fragment, GL_FRAGMENT_SHADER );
      glUseProgram(prog);

      // Get shader uniform variable location for projection matrix
      // See shader code: "uniform mat4 projection;"
      const GLint u_proj  = glGetUniformLocation(prog, "u_projection");
      assert( u_proj >= 0 && "Failed to find uniform variable" );

      // Get shader uniform variable  location for model matrix.
      const GLint u_model  = glGetUniformLocation(prog, "u_model");
      assert( u_model >= 0 && "Failed to find uniform variable" );

      // Get shader attribute location - the function glGetAttribLocation
      // returns (-1) on error.
      const GLint attr_position = glGetAttribLocation(prog, "position");
      assert( attr_position >= 0 && "Failed to get attribute location" );
      std::fprintf(stderr, " [TRACE] Attribute location attr_position = %d \n"
                  , attr_position);

      // Get shader index of color attribute variable.
      const GLint attr_color = glGetAttribLocation(prog, "color");
      assert( attr_color >= 0);

      glUniformMatrix4fv(u_model, 1, GL_FALSE, glm::value_ptr(matrix_identity) );
      glUniformMatrix4fv(u_proj,  1, GL_FALSE, glm::value_ptr(matrix_identity) );

      // ======== U P L O A D - T O - G P U ==============================// 

      constexpr size_t NUM_VERTICES = 3;

      float a = 0.25;

      Vertex2D vertices[NUM_VERTICES] = {
        {  0.0  ,   a * sqrt(3.0f) } // Vertex 0  
      , { -2 * a,  -2 * a          } // Vertex 1  
      , {  2 * a,  -2 * a          } // Vertex 2  
      };

      ColorRGB colors[NUM_VERTICES] = {
            {0.4, 0.2, 0.67} 
          , {0.8, 0.6, 0.30}
          , {0.2, 0.6, 0.10}  
      };

      // Always initialize with zero (0), otherwise, the values
      // of those variables will be undefined. (Undefine Behavior)
      GLuint vao_triangle = 0;
      GLuint vbo_vertices = 0;
      GLuint vbo_colors   = 0;

      // Send vertices to GPU VBO (vbo_vertices)
      send_buffer(  &vao_triangle    // Ponter to VAO [OUTPUT]
                  , &vbo_vertices    // Pointer VBO   [OUTPUT]
                  , sizeof(vertices) // Size of buffer sent to GPU (VBO memory)
                  , vertices         // Pointer to buffer
                  , attr_position    // Shader attribute location
                  , 2                // Each vertex has 2 coordinates (X, Y)
                  , GL_FLOAT         // Each vertex coordinate has type GLfloat (float)
                  );

      // Send colors to GPU VBO (vbo_colors)
      send_buffer(  &vao_triangle   // Pointer Vertex Array object
                  , &vbo_colors     // Pointer Vertex buffer object handle (aka token)
                  , sizeof(colors)  // Buffer size
                  , colors          // Pointer to buffer (addrress of first buffer element)
                  , attr_color      // Shader attribute location
                  , 3               // Each color has 3 coordinates (R, G, B)
                  , GL_FLOAT        // Each color coordiante has type GLfloat
                  );

      glm::mat4 model; 

      // ======== R E N D E R I N G - L O O P ============================//

      bool is_running = true; 

      while( is_running )
      {
          SDL_Event event;
          // std::fprintf(stderr, " [TRACE] Waiting SDL event \n"); 

          while( SDL_PollEvent(&event) )
          {
              // std::fprintf(stderr, " [TRACE] Polling event \n");

              if( event.type == SDL_QUIT ){ is_running == false; }
 
              if( event.type == SDL_WINDOWEVENT 
                  && event.window.event == SDL_WINDOWEVENT_CLOSE )
              {  is_running = false; 
                 SDL_Quit(); 
              }
          }

          glViewport(0, 0, window_w, window_h);
          // OpenGL Background color (black for avoiding eye strain)
          glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
          glClear(GL_COLOR_BUFFER_BIT);

          // === BEGIN REDERING ========// 

              // ------ Draw triangle 1 ------ //  
              glBindVertexArray(vao_triangle);
              model = matrix_identity;
              model = glm::translate(model, glm::vec3(0.35, 0.22, 0.0));
              glUniformMatrix4fv(u_model, 1, GL_FALSE, glm::value_ptr(model) );
              glDrawArrays(GL_TRIANGLES, 0, 3);
 
              // ------ Draw triangle 2 ------ //
              glBindVertexArray(vao_triangle);
              model = matrix_identity;
              model = glm::translate(model, glm::vec3(-0.5, -0.4, 0.0));
              model = glm::scale(model, glm::vec3(0.5, 0.5, 0.5 ) );
              model = glm::rotate(model, glm::radians(25.0f), glm::vec3(0.0, 0.0, 1.0) );
              glUniformMatrix4fv(u_model, 1, GL_FALSE, glm::value_ptr(model) );
              glDrawArrays(GL_TRIANGLES, 0, 3);
 
          // === END RENDERING ==========// 

          SDL_GL_SwapWindow(window);
      }

      std::puts(" [TRACE] Shutdown gracefully. ");
      return 0;
  } 

  // ----- I M P L E M E N T A T I O N S --------------// 

      // ====== I M P L E M E N T A T I O N S ==========//

  void compile_shader(GLuint m_program, const char* code, GLenum type)
  {
      GLint shader_id = glCreateShader( type );
      glShaderSource(shader_id, 1, &code, nullptr);
      glCompileShader(shader_id);

      GLint is_compiled = GL_FALSE;
      // Check shader compilation result
      glGetShaderiv(shader_id, GL_COMPILE_STATUS, &is_compiled);

      // If there is any shader compilation result,
      // print the error message.
      if( is_compiled == GL_FALSE)
      {
          GLint length;
          glGetShaderiv(shader_id, GL_INFO_LOG_LENGTH, &length);
          assert( length > 0 );

          std::string out(length + 1, 0x00);
          GLint chars_written;
          glGetShaderInfoLog(shader_id, length, &chars_written, (char*) out.data());
          std::cerr << " [SHADER ERROR] = " << out << '\n';
          // Abort the exection of current process.
          std::abort();
      }

      glAttachShader(m_program, shader_id);
      glDeleteShader(shader_id);
      glLinkProgram(m_program);
      GLint link_status = GL_FALSE;
      glGetProgramiv(m_program, GL_LINK_STATUS, &link_status);
      assert( link_status != GL_FALSE );
      // glUseProgram(m_program);
  }

  // Upload buffer from main memory to GPU VBO
  // =>> Parameters VAO, VBO are allocated by the caller.
  void send_buffer( GLuint* pVao, GLuint* pVbo, GLsizei sizeBuffer
                  , void* pBufffer, GLint   shader_attr, GLint size
                  , GLenum type)
  {
      assert(pVao != nullptr);
      assert(pVbo != nullptr);
      GLuint& vao = *pVao;
      GLuint& vbo = *pVbo;

      // Generate and bind current VAO (Vertex Array Object)
      if(vao == 0){ glGenVertexArrays(1, &vao); }
      glBindVertexArray(vao);

      // Generate and bind current VBO (Vertex Buffer Object)
      glGenBuffers(1, &vbo);
      glBindBuffer(GL_ARRAY_BUFFER, vbo);

      // Upload data to current VBO buffer in GPU
      glBufferData(GL_ARRAY_BUFFER, sizeBuffer, pBufffer, GL_STATIC_DRAW);
      glEnableVertexAttribArray(shader_attr);
      glVertexAttribPointer(shader_attr, size, type, GL_FALSE, 0, nullptr);

      // ------ Disable Global state set by this function -----//
      //
      // Unbind VAO
      glBindVertexArray(0);
      // Unbind VBO
      glBindBuffer(GL_ARRAY_BUFFER, vbo);
      // Disable current shader attribute
      glDisableVertexAttribArray(shader_attr);

  }

  // ----- S H A D E R S ------------------------------// 

  const char* shader_vertex = R"(
      #version 330 core
    
      layout ( location = 2)  in vec2 position;
      layout ( location = 1 ) in vec3 color;

      out vec3 out_color;
      uniform mat4 u_model;
      uniform mat4 u_projection;

      void main()
      {
         gl_Position = u_projection * u_model * vec4(position, 0, 1.0);
         out_color = color;
      }
        
   )";


  const char* shader_fragment = R"( 
      #version 330

      // This color comes from Vertex shader
      in vec3 out_color;

      void main()
      {
          // Set vertex colors
          gl_FragColor = vec4(out_color, 1.0);
      }

  )";
#+END_SRC


  *Building* 

Building on Linux. 

#+BEGIN_SRC sh 
  $ cmkae -H. -B_build_linux -DCMAKE_BUILD_TYPE=Debug
  $ cmkae --build _build_linux --target 
#+END_SRC

Building for Windows NT (cross-compilation) from Linux.

#+BEGIN_SRC sh 
    $ docker run -it --rm -v $PWD:/cwd -w /cwd --entrypoint=bash dockcross/windows-static-x64
    $ cmake -H. -B_build_windows -DCMAKE_BUILD_TYPE=Debug
    $ cmake --build _build_windows --target 

    $ ls 
    CMakeCache.txt	Makefile		 _deps		      glew32.dll
    CMakeFiles	SDL2ConfigVersion.cmake  cmake_install.cmake  opengl-sdl.exe
#+END_SRC

Run with wine: 

#+BEGIN_SRC sh 
  $ wine _build_windows/opengl-sdl.exe 
#+END_SRC

Building with Xmake: 

#+BEGIN_SRC sh 
   $ xmake -P .
  [100%]: build ok!
#+END_SRC

Running the application: (default target)

#+BEGIN_SRC sh 
  $ xmake run -P .
  [TRACE] OpenGL SDL started Ok. 
  [TRACE] Attribute location attr_position = 2 
  [TRACE] Shutdown gracefully. 
#+END_SRC

Generate a CMake file and edit with VSCode or CLion: 

#+BEGIN_SRC sh 
  $ xmake project -k cmake  -P . 
  create ok!
  
  # Edit the project with VSCode 
  $ vscode . 
#+END_SRC

** Minimal Qt5 OpenGL project 

Qt Framework is a better fit for non-games cross-platform OpenGL
graphical applications like 3D drawing akin to Blender, CAD
(Computer-Aided Design), scientific visualization and charts than
GLFW, SDL or SFML as those window abstraction libraries lack widgets,
such as buttons, menus, text entry and so on which are necessary for
rich and complex interaction. Other significant advantage of Qt over
the aforementioned libraries is that the Qt framework already has
everything that is needed for developing OpenGL applications,
including, OpenGL loading, 4x4 matrix, quaternion, OpenGL
widget and scene graph.

Documentation: 

  + [[https://doc.qt.io/qt-5/qopenglwindow.html][QOpenGLWindow class]]

  + [[https://doc.qt.io/qt-5/qopenglwidget.html][QOPenGLWidget class]]

  + [[https://doc.qt.io/qt-5/qopenglshaderprogram.html][QOpenGLShaderProgram]] 

  + [[https://doc.qt.io/qt-5/qvector3d.html][QVector3D class]]

  + [[https://doc.qt.io/qt-5/qvector4d.html][QVector4D class]]

  + [[https://doc.qt.io/qt-5/qquaternion.html][QQuaternion class]]

  + [[https://doc.qt.io/qt-5/qmatrix4x4.html][QMatrix4x4 class]]

  + [[https://doc.qt.io/qt-5/qt3d-index.html][Qt3D Core Module]]

Examples: 

  + https://doc.qt.io/qt-5/qtopengl-cube-example.html

  + https://doc.qt.io/qt-5/qtopengl-2dpainting-example.html

  + https://doc.qt.io/qt-5/qtgui-openglwindow-example.html

  *Screenshot* 

In this sample application, the vertex data is uploaded only once to
the GPU via VBO (Vertex Buffer Object) and used for drawing two
triangles, which the positions are set via affine trasnform
objects. The program has tree buttons that controls the second
triangle by performing the following set of operations: scale
increasing; scale decreasing and rotation.

#+CAPTION: Minimal Qt 3D OpenGL application. 
[[file:images/opengl-qt3d-minimal.png][file:images/opengl-qt3d-minimal.png]]

  *Project Files* 

File: _CMakeLists.txt_ 

#+BEGIN_SRC cmake 
   cmake_minimum_required(VERSION 3.9)
   project(Qt5_Widget_OpenGL)

   #====== Global Configurations ==================#

   set(CMAKE_CXX_STANDARD 17)
   set(CMAKE_VERBOSE_MAKEFILE ON)
   set(CMAKE_INCLUDE_CURRENT_DIR ON)
   set(CMAKE_AUTOUIC ON)
   set(CMAKE_AUTOMOC ON)
   set(CMAKE_AUTORCC ON)
   set(CMAKE_CXX_STANDARD_REQUIRED ON)

   find_package(Qt5 COMPONENTS Core Widgets REQUIRED)

   #=============== Target Configurations ============#
   SET(QT5_LINK_LIBRARIES Qt5::Core Qt5::Gui Qt5::Widgets)

          add_executable( qt-opengl qt-opengl.cpp )
   target_link_libraries( qt-opengl  Qt5::Core Qt5::Gui Qt5::Widgets )

#+END_SRC

File: _meson.build_ (Note: poor IDE support) - For [[https://mesonbuild.com/][Meson]] Building System
  + Docs: [[https://mesonbuild.com/Tutorial.html][meson-tutorial]] [[https://mesonbuild.com/Qt5-module.html][qt5-meson]]

#+BEGIN_SRC python 
  project('qt-opengl-meson', 'cpp')

  qt5      = import('qt5')
  qt5_deps = dependency('qt5', modules: ['Core', 'Gui', 'Widgets'] )

  executable( 'qt-opengl'             # Executable name without extension 
            , 'qt-opengl.cpp'         # Source files
            , dependencies: qt5_deps  # Depencies 
            ) 
#+END_SRC

File: _xmake.lua_ - For [[https://xmake.io/#/][XMake]] building system 

#+BEGIN_SRC lua 
  add_rules("mode.debug", "mode.release")
  
  target("qt-opengl")
      add_rules("qt.widgetapp")
      set_languages("c99", "c++1z")
      add_files("qt-opengl.cpp")
#+END_SRC


File: _qt-opengl.cpp_ 

#+BEGIN_SRC cpp 
  #include <iostream>
  #include <functional>
  #include <cassert>
  #include <cmath>

  #include <QtWidgets>
  #include <QApplication>
  #include <QOpenGLWindow>
  #include <QOpenGLContext>
  #include <QOpenGLBuffer>
  #include <QOpenGLShaderProgram>


  extern const char* shader_vertex;
  extern const char* shader_fragment;

  // Wrapper for 2D vertex coordinates
  struct Vertex2D{  GLfloat x, y;  };
  // Wrapper for RGB colors
  struct ColorRGB { GLfloat r, g, b; };

  struct Mesh
  {
      QOpenGLBuffer*            vbo_vertex  = nullptr;
      QOpenGLBuffer*            vbo_color   = nullptr;
      QOpenGLVertexArrayObject* vao         = nullptr;
  };

  struct Transform
  {
      QVector3D   position = {0.0,  0.0,  0.0,   };
      QVector3D   scale    = {1.0f, 1.0f, 1.0f   };
      QQuaternion rotation = {1.0, 0.0, 0.0, 0.0 };

      // Cached affine trasnform for speeding up computation
      // by tranding off time for space.
      QMatrix4x4 trasnform;

      // When this flag is true, the transform is computed again.
      bool is_changed = false;

      Transform& setPosition(float x, float y, float z)
      {
          position = {x, y, z};
          is_changed = true;
          return *this;
      }

      Transform& displace(float dx, float dy, float dz)
      {
          position = { position.x() + dx, position.y() + dy, position.z() + dz };
          is_changed = true;
          return *this;
      }

      Transform&  setRotationZ(float angle)
      {
          rotation = QQuaternion::fromAxisAndAngle( QVector3D{0.0, 0.0, 1.0}, angle );
          is_changed = true;
          return *this;
      }

      Transform& rotateZ(float angle)
      {
          rotation = rotation * QQuaternion::fromAxisAndAngle( QVector3D{0.0, 0.0, 1.0}, angle );
          is_changed = true;
          return *this;
      }

      Transform& setScale(float f)
      {
          scale = {f, f, f};
          is_changed = true;
          return *this;
      }

      Transform& addScale(float f)
      {
          scale = { scale.x() + f, scale.y() + f, scale.z() + f };
          is_changed = true;
          return *this;
      }

      QMatrix4x4 value()
      {

          if( is_changed )
          {
              std::fprintf(stderr, " [TRACE] Affine transform recalculated. Ok. \n");
              trasnform.setToIdentity();
              trasnform.translate( position );
              trasnform.scale(scale);
              trasnform.rotate(rotation);
              is_changed = false;
          }
          return trasnform;
      }
  };

  class OpenGLCanvas: public QOpenGLWidget
  {
  public:
       using DrawCallback = std::function<void (QOpenGLExtraFunctions* ctx)>;
  private:
      QOpenGLShaderProgram* m_prog 	= nullptr;


      // Shader attribute location
      int attr_position = 0;
      int attr_color    = 0;
      // Shader uniform locations
      int u_model = 0;
      int u_proj  = 0;

      DrawCallback m_callback = [](QOpenGLExtraFunctions* ){ };
      DrawCallback m_init     = [](QOpenGLExtraFunctions* ){ };
  public:

      OpenGLCanvas(DrawCallback init):  m_init(init){ }

      void initializeGL() override
      {
          auto ctx = QOpenGLContext::currentContext()->extraFunctions();
          std::fprintf(stderr, " [TRACE] Initialized Ok. \n");

          m_prog = new QOpenGLShaderProgram;
          m_prog->addShaderFromSourceCode(QOpenGLShader::Vertex, 	 shader_vertex);
          m_prog->addShaderFromSourceCode(QOpenGLShader::Fragment, shader_fragment);
          m_prog->link();
          m_prog->bind();
          // Attribute location
          attr_position = m_prog->attributeLocation("position");
          Q_ASSERT( attr_position != -1 );
          attr_color = m_prog->attributeLocation("color");
          Q_ASSERT( attr_color != -1    );

          // Uniform variables
          u_model = m_prog->uniformLocation("u_model");
          Q_ASSERT( u_model != -1 );
          u_proj  = m_prog->uniformLocation("u_proj");
          Q_ASSERT( u_proj != -1 );

          // Initialize shader uniform variables
          QMatrix4x4 m;
          m.setToIdentity();
          m_prog->setUniformValue(u_model, m);
          m_prog->setUniformValue(u_proj,  m);

          m_init(ctx);
     }

      void setDrawCallback(DrawCallback callback)
      {
          m_callback = callback;
      }

      // Override QOpenGLWidget::paintGL()
      void paintGL() override
      {
          std::fprintf(stderr, " [TRACE] Painting window Ok. \n");

          QOpenGLExtraFunctions* ctx = QOpenGLContext::currentContext()->extraFunctions();
          ctx->glClearColor(0.0, 0.0, 0.0, 1.0);
          ctx->glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );

          m_callback(ctx);
      }

      // Override QOpenGLWidget::resizeGL()
      void resizeGL(int w, int h) override
      {
          std::fprintf(stderr, " [TRACE] Window resized. \n");

          // Forward call to superclass
          this->QOpenGLWidget::resizeGL(w, h);
      }

      // Set model matrix of current object
      void setModel(QMatrix4x4 const& m){ m_prog->setUniformValue(u_model, m); }

      // Upload vertices to GPU
      Mesh upload(size_t num_vertices, Vertex2D* vertices, ColorRGB* colors)
      {
          auto ctx = QOpenGLContext::currentContext()->extraFunctions();

          auto vao = new QOpenGLVertexArrayObject;
          if( vao->create() ){ vao->bind(); }

          // Send vertices positions to GPU
          auto vbo_vertex = new QOpenGLBuffer;
          vbo_vertex->create();
          vbo_vertex->bind();
          vbo_vertex->allocate( vertices , num_vertices * sizeof(Vertex2D) );
          ctx->glEnableVertexAttribArray(attr_position);
          // Set data layout, how the data will be interpreted.
          ctx->glVertexAttribPointer( attr_position, 2, GL_FLOAT, GL_FALSE, 0, nullptr );

          // Send vertices colors to GPU
          auto vbo_color = new QOpenGLBuffer;
          vbo_color->create();
          vbo_color->bind();
          vbo_color->allocate( colors, num_vertices * sizeof(ColorRGB));
          ctx->glEnableVertexAttribArray(attr_color);
          // Set color data layout
          ctx->glVertexAttribPointer( attr_color, 3, GL_FLOAT, GL_FALSE, 0, nullptr );

          Mesh mesh;
          mesh.vbo_vertex = vbo_vertex;
          mesh.vbo_color  = vbo_color;
          mesh.vao = vao;
          return mesh;
      }
  };


  int main(int argc, char** argv)
  {
      std::cout << " [INFO] Starting Application" << std::endl;

      QApplication app(argc, argv);
      app.setApplicationName("Qt OpenGL Cavans");

      constexpr size_t NUM_VERTICES = 3;

      const float a = 0.5f ;

      Vertex2D vertices[NUM_VERTICES] = {
            {  0.0,  a * sqrtf(3.0f) } // Vertex 0
          , {   -a,  0.0             } // Vertex 1
          , {    a,  0.0             } // Vertex 2
      };

      ColorRGB colors[NUM_VERTICES] = {
            {0.4, 0.5, 0.67}
          , {0.8, 0.6, 0.30}
          , {0.2, 0.7, 0.10}
      };


      QWidget window;
      window.resize(500, 400);
      window.setWindowTitle("Qt OpenGL Window is awesome!");

      auto hbox = new QVBoxLayout(&window);

      auto form = new QHBoxLayout();
      auto btn_add_scale = new QPushButton("Increase scale");
      auto btn_dec_scale = new QPushButton("Decrease scale");
      auto btn_rotate = new QPushButton("Rotate");
      form->addWidget(btn_add_scale);
      form->addWidget(btn_dec_scale);
      form->addWidget(btn_rotate);
      hbox->addLayout(form);

      Mesh mesh_triangle;

      Transform trf_triangle1;
      trf_triangle1.setPosition(0.36f, 0.50f, 0.0f)
                   .rotateZ(0)
                   .setScale(0.50);


      Transform trf_triangle2;
      trf_triangle2.setPosition(-0.24, -0.25, 0.0)
                   .setScale(0.80)
                   .rotateZ(35.0);

      qDebug() << " Trasnform 1 = " << trf_triangle1.value();
      qDebug() << " Transform 2 = " << trf_triangle2.value();

      OpenGLCanvas* canvas = new OpenGLCanvas([&](QOpenGLExtraFunctions* ctx)
      {
          mesh_triangle = canvas->upload(NUM_VERTICES, vertices, colors);
      });
      canvas->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
      hbox->addWidget(canvas);

      canvas->setDrawCallback([&](QOpenGLExtraFunctions* ctx)
      {
          // Draw triangle 1
          mesh_triangle.vao->bind();
          // canvas->setModel(model_triangle1);
          canvas->setModel( trf_triangle1.value() );
          ctx->glDrawArrays(GL_TRIANGLES, 0, NUM_VERTICES);

          // Draw triangle 2
          mesh_triangle.vao->bind();
          // canvas->setModel(model_triangle2);
          canvas->setModel( trf_triangle2.value() );
          ctx->glDrawArrays(GL_TRIANGLES, 0, NUM_VERTICES);
      });

      window.show();


      // ----------- Set events ----------------------//

      QObject::connect(btn_add_scale, &QPushButton::clicked,
               [&]{
                     std::fprintf(stderr, " [TRACE] Button to increase scale clicked. Ok. \n"); 
                     trf_triangle2.addScale(+0.10);
                     canvas->repaint();
               });

      QObject::connect(btn_dec_scale, &QPushButton::clicked,
                       [&]{
                          std::fprintf(stderr, " [TRACE] Button to decrease scale clicked. Ok. \n"); 
                          trf_triangle2.addScale(-0.10);
                          canvas->repaint();
                       });

      QObject::connect(btn_rotate, &QPushButton::clicked,
                       [&]{
                           std::fprintf(stderr, " [TRACE] Button to rotate triangle clicked. Ok. \n");
                           trf_triangle2.rotateZ(10.5);
                           canvas->repaint();
                       });

      return app.exec();
  }


  // ===== S H A D E R - P R O G R A M S ==========//


  const char* shader_vertex = R"(
      #version 330 core

      layout ( location = 2)  in vec2 position;
      layout ( location = 1 ) in vec3 color;

      out vec3 out_color;
      uniform mat4 u_model;
      uniform mat4 u_proj;

      void main()
      {
         gl_Position = u_proj * u_model * vec4(position, 0, 1.0);
         out_color = color;
      }

   )";


  const char* shader_fragment = R"(
      #version 330

      // This color comes from Vertex shader
      in vec3 out_color;

      void main()
      {
          // Set vertex colors
          gl_FragColor = vec4(out_color, 1.0);
      }

  )";

#+END_SRC

  *Building with CMake* 

#+BEGIN_SRC sh 
    # Build application 
    $ cmake -H. -B_build -DCMAKE_BUILD_TYPE=Debug
    $ cmake --build _build --target all

    # List build tree directory 
    $ >> ls _build/
   CMakeCache.txt  cmake_install.cmake  qt-opengl*
   CMakeFiles/     Makefile             qt-opengl_autogen/

   # Run application 
   $ >> _build/qt-opengl 
#+END_SRC 

  *Building with Meson* 

#+BEGIN_SRC sh 
   # Build application 
   $ meson setup _build_meson # Name of build directory is arbitrary
   $ meson compile -C _build_meson 

   # List build directory 
   $ >> ls _build_meson/
   build.ninja            meson-info/  meson-private/  qt-opengl.p/
   compile_commands.json  meson-logs/  qt-opengl* 

   # Run application 
   $ >> ./_build_meson/qt-opengl
#+END_SRC

  *Building with XMake*
  
Build project with [[https://xmake.io/#/][Xmake]] lua-based building system showing all
compilation and linking process.

#+BEGIN_SRC sh 
 $  xmake build -v
#+END_SRC

Run application (default target):

#+BEGIN_SRC sh 
  $ xmake run 
#+END_SRC 

Run application specifying the target:

#+BEGIN_SRC sh 
 $ xmake run qt-opengl 
#+END_SRC

Generate an CMakeLists.txt for making IDEs and smart text editors
including, CLion, QtCreator and Visual Studio Code happy. 

#+BEGIN_SRC sh 
  $ >> xmake project -k cmake
  create ok!
  
  # Edit project with Visual Studio Code 
  $ code . 

  # Edit project with CLion 
  =>> Open the project directory with CLion 

  # Edit project with QtCreator 
  =>> Open the project directory with Qt creator 
  $ qt-creator . 

  # Build project with CMakeLists.txt 
  $ cmake -H. -B_build -DCMAKE_BUILD_TYPE=Debug
  $ cmake --build _build --target 
#+END_SRC

** Minimal WebGL-OpenGL project 
   
WebGL is a web standard supported by most modern web browsers for a
low-level 3D graphics API accelerated by GPU based on OpenGL ES. By
using WebGL as in this next code, it is possible to play with OpenGL
3D computer graphics without the hassle of dealing with C or C++
compilation and building systems since WebGL API is already available
in any web browser and the API is exposed as JavaScript API, that
resembles OpenGL C API.

  + Live example available at: https://jsfiddle.net/prxbmqof/

[[file:images/opengl-webgl-basic.png][file:images/opengl-webgl-basic.png]]

 *Complete Html Code* 

#+BEGIN_SRC html 
  <!DOCTYPE html>
  <html> 
     <head> 
         <title> Web Page Title </title>
         <script src="https://somehost.com/jequey.js"></script>
  
     </head>
  
     <body>
         <h1>WebGL Experiment 1 - Draw a triangle</h1>
         <canvas id="glCanvas" width="480" height="480">
     </body>
    <script>
      // Source: https://webglfundamentals.org/webgl/lessons/webgl-fundamentals.html
      function createShader(gl, type, source) 
      
        ... ... ... ... ... ... ... ... ... ... .. 
        ... ... ... ... ... ... ... ... ... ... ... 
  
      // Draw Square 
      console.log(" [TRACE] Draw sqaure in WebGL");
      gl.bindBuffer( gl.ARRAY_BUFFER, vbo_square );
      gl.vertexAttribPointer(attr_position, 2, gl.FLOAT, false, 0, 0);
      gl.drawArrays(gl.LINE_LOOP, 0, 4);
  
      console.log(" [TRACE] Loaded Ok.");
    </script>
  
  </html> 
#+END_SRC

JavaScript part: 

#+BEGIN_SRC js 
  // Source: https://webglfundamentals.org/webgl/lessons/webgl-fundamentals.html
  function createShader(gl, type, source) 
  {
    var shader = gl.createShader(type);
    gl.shaderSource(shader, source);
    gl.compileShader(shader);
    var success = gl.getShaderParameter(shader, gl.COMPILE_STATUS);
    if (success) {
      return shader;
    }
  
    console.log(gl.getShaderInfoLog(shader));
    gl.deleteShader(shader);
  }
  
  // Source:  https://webglfundamentals.org/webgl/lessons/webgl-fundamentals.html
  function createProgram(gl, vertexShader, fragmentShader) 
  {
    var program = gl.createProgram();
    gl.attachShader(program, vertexShader);
    gl.attachShader(program, fragmentShader);
    gl.linkProgram(program);
    var success = gl.getProgramParameter(program, gl.LINK_STATUS);
    if (success) {
      return program;
    }
  
    console.log(gl.getProgramInfoLog(program));
    gl.deleteProgram(program);
  }
  
  
  function resizeCanvasToDisplaySize(canvas, multiplier) 
  {
      multiplier = multiplier || 1;
      const width  = canvas.clientWidth  * multiplier | 0;
      const height = canvas.clientHeight * multiplier | 0;
      if (canvas.width !== width ||  canvas.height !== height) {
        canvas.width  = width;
        canvas.height = height;
        return true;
      }
      return false;
    }
  
  
  const canvas = document.querySelector("#glCanvas");
  const gl = canvas.getContext("webgl");
  
  if(  gl == null ){
    alert(" [ABORT] Unable to initialized WebGL");
  }
  
  // Vertex Shader code => Performs vertex coordinate transforms in the GPU 
  // Defines the a 3D scene or 2D scene (by ignoring the Z axis).
  let code_shader_vertex = `
    attribute vec2 position;
  
    void main(){
       // Coordinates: X, Y, Z = 0, W = 1 => Ignores the Z axis (2D Scene)
       gl_Position = vec4(position, 0, 1.0);
  
    }
  `;
  
  // Fragment shader code => Sets color, lights and illumination 
  let code_shader_fragment = `
    void main(){
      // Color always green 
      // Color in (R, G, B, W) => (Red, Green, Blue, W)
      gl_FragColor = vec4(0.0, 1.0, 0.0, 1.0);
    }
  `;
  
  // Vertices of (x, y) coordinates 
  let vertices_triangle = [
      -0.25, -0.25
    ,  0.00,  0.25
    ,  0.25, -0.25
  ];
  
  let vertices_square = [
      -0.3, -0.3
    , -0.3,  0.3
    ,  0.3,  0.3
    ,  0.3, -0.3
  
  ];
  
  
  let shader_vert = createShader(gl, gl.VERTEX_SHADER, code_shader_vertex);
  let shader_frag = createShader(gl, gl.FRAGMENT_SHADER, code_shader_fragment);
  let prog = createProgram(gl, shader_vert, shader_frag);
  
  let attr_position = gl.getAttribLocation(prog, "position");
  console.log(" [TRACE] attr_position = ", attr_position);
  
  // resizeCanvasToDisplaySize(gl.canvas);
  gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
  
  // Screen background color is black => clearColor(Red intensity from 0 to 1, Green , Blue , Alpha)
  gl.clearColor(0.0, 0.0, 0.0, 1.0);
  gl.clear(gl.COLOR_BUFFER_BIT);
  gl.useProgram(prog);
  
  // -------- Upload triangle to GPU --------------------//
  // GLuint vbo_triangle_ = 0;
  // glGenBuffers(1 , &vbo_triangle_); 
  let vbo_triangle = gl.createBuffer();
  // Set this buffer as current buffer (global state)
  gl.bindBuffer( gl.ARRAY_BUFFER, vbo_triangle );
  // Upload vertices to  GPU 
  gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices_triangle), gl.STATIC_DRAW);
  
  // --------- Upload squares to GPU ----------------------//
  let vbo_square = gl.createBuffer();
  gl.bindBuffer( gl.ARRAY_BUFFER, vbo_square );
  gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices_square), gl.STATIC_DRAW);
  
  // ------ Drawing ----------------------------------------//
  gl.enableVertexAttribArray(attr_position);
  
  // Draw triangle 
  console.log(" [TRACE] Draw triangle in WebGL");
  gl.bindBuffer( gl.ARRAY_BUFFER, vbo_triangle );
  gl.vertexAttribPointer(attr_position, 2, gl.FLOAT, false, 0, 0);
  gl.drawArrays(gl.TRIANGLES, 0, 3);
  
  // Draw Square 
  console.log(" [TRACE] Draw sqaure in WebGL");
  gl.bindBuffer( gl.ARRAY_BUFFER, vbo_square );
  gl.vertexAttribPointer(attr_position, 2, gl.FLOAT, false, 0, 0);
  gl.drawArrays(gl.LINE_LOOP, 0, 4);
  
  console.log(" [TRACE] Loaded Ok.");
#+END_SRC

** Loading OpenGL without Glew 

The next code loads OpenGL function pointers without Glew (OpenGL
extension wrangler) for streamlining the building process. Glew is a
library for loading OpenGL function pointers and OpenGL extensions in
a platform-agnostic way. Despite the usefulness of this library, its
source code is hard to integrate to building systems as the Glew
building systems as Glew library needs Perl and other dependencies for
code generation.

Relevant documentation: 

  + [[https://www.khronos.org/opengl/wiki/Load_OpenGL_Functions][Khronos Group - Load OpenGL Functions]]
    + Brief: "Loading OpenGL Functions is an important task for
      initializing OpenGL after creating an OpenGL context. You are
      strongly advised to use an OpenGL Loading Library instead of a
      manual process. However, if you want to know how it works
      manually, read on." 

  + [[https://stackoverflow.com/questions/47445643/why-are-opengl-functions-loaded-at-runtime-instead-of-dynamically-linked][Why are OpenGL functions loaded at runtime ...?]]

  + [[https://stackoverflow.com/questions/27873784/when-do-i-need-to-use-an-opengl-function-loader][When do I need to use an OpenGL function loader?]] 

  + [[https://docs.microsoft.com/en-us/windows/win32/api/wingdi/nf-wingdi-wglgetprocaddress][MSDN - wglGetProcAddress]] (WGL - Windows NT)
    + Brief: "The wglGetProcAddress function returns the address of an OpenGL
      extension function for use with the current OpenGL rendering
      context."

  + [[https://docs.microsoft.com/en-us/windows/win32/api/wingdi/nf-wingdi-wglgetcurrentcontext][MSDN - wglGetCurrentContext]] (WGL - Windows NT)
    + Brief: "The wglGetCurrentContext function obtains a handle to the
      current OpenGL rendering context of the calling thread."

  + [[https://stackoverflow.com/questions/21769427/wglgetprocaddress-returns-null][wglGetProcAddress returns NULL]] 


  *Screenshot* 

[[file:images/opengl-load-function-ptr.png][file:images/opengl-load-function-ptr.png]]

  *Files* 

File: _CMakeListst.txt_ 

#+BEGIN_SRC cmake 
   cmake_minimum_required(VERSION 3.5)
   project(OpenGL_Load_FPTR)

   set(CMAKE_CXX_STANDARD 17)
   set(CMAKE_CXX_STANDARD_REQUIRED ON)
   set(CMAKE_VERBOSE_MAKEFILE ON)

   #================ GLFW Settings  ===============#

   find_package(OpenGL REQUIRED)

   include(FetchContent)

   # Set GLFW Options before FectchContent_MakeAvailable
   set( GLFW_BUILD_EXAMPLES OFF CACHE BOOL  "GLFW lib only" )
   set( GLFW_BUILD_TESTS    OFF CACHE BOOL  "GLFW lib only" )
   set( GLFW_BUILD_DOCS     OFF CACHE BOOL  "GLFW lib only" )
   set( GLFW_BUILD_INSTALL  OFF CACHE BOOL  "GLFW lib only" )

   # Donwload GLFW library
   FetchContent_Declare(
     glfwlib
     URL   https://github.com/glfw/glfw/releases/download/3.3.2/glfw-3.3.2.zip
   )
   FetchContent_MakeAvailable(glfwlib)

   MACRO(ADD_OPENGL_APP target sources)
      add_executable( ${target} ${sources} )
      message([TRACE] " Add OpenGL executable = ${target} ")

      target_link_libraries( ${target} glfw OpenGL::GL)

       IF(MINGW)
           # Statically link against MINGW dependencies
           # for making easier to deploy on other machines. 
           target_link_options( ${target} PRIVATE                                 
                                    -static-libgcc
                                    -static-libstdc++
                                    -Wl,-Bstatic,--whole-archive -lwinpthread
                                      -Wl,--no-whole-archive                                    
                                    )
        ENDIF()       

   ENDMACRO()     

        # ======= T A R G E T S ============================#
        #                                                   #

   ADD_OPENGL_APP( load-opengl-fptr  load-opengl-fptr.cpp )
#+END_SRC

File: _load-openg-fptr.cpp_ 

#+BEGIN_SRC cpp 
  // ----- Manual Loading of OpenGL function pointers -------//
  #include <iostream>
  #include <vector>
  #include <array>
  #include <cmath>
  #include <cassert>

  // -------- OpenGL headers ---------//
  //
  #define GL_GLEXT_PROTOTYPES 1 // Necessary for OpenGL >= 3.0 functions
  #define GL3_PROTOTYPES      1 // Necessary for OpenGL >= 3.0 functions


  #if defined(_WIN32)
     #include <windows.h>
  #endif

  #include <GL/gl.h>

  #if defined(__linux__) || defined(__bsd__)
      #include <GL/glx.h>  // For glXGetProcAddress() and glXGetProcAddressARB()
      #include <GL/glxext.h>
  #endif

  #include <GL/glu.h>
  #include <GLFW/glfw3.h>


  //======== B O I L E R P L A T E - F O R - L O A D I N G - O P E N G L ==========//
  //

  #define GL_ARRAY_BUFFER  0x8892
  #define GL_STATIC_DRAW   0x88E4

  // Generic void function pointer which can be casted to any other function pointer.
  using FptrVoid = void*;

  /** @brief Load OpenGL function pointer at runtime
    ,* See:
    ,*    -
    ,*    - https://stackoverflow.com/questions/21769427/wglgetprocaddress-returns-null
    ,*/
  FptrVoid load_gl_function(const char* name)
  {
      FptrVoid fp = nullptr;
      std::fprintf(stderr, " [TRACE] Loading pointer to OpenGL subroutine = %s \n", name);

      #if defined(_WIN32)
          assert( wglGetCurrentContext() != nullptr && "Current thread lacks a OpenGL rendering context." );

          fp = (void*) wglGetProcAddress(name);
          // assert( fp != nullptr );
          if( fp == 0
              || (fp == (void*)0x1) || (fp == (void*)0x2) || (fp == (void*)0x3)
              || (fp == (void*)-1) )
            {
              HMODULE module = LoadLibraryA("opengl32.dll");
              fp = (void *) GetProcAddress(module, name);
            }

          if( fp == nullptr)
          {
              std::fprintf(stderr, " [ERROR] OpenGL function = '%s' not found on Windows ", name );
              std::abort();
          }
      #elif defined(__linux__) || defined(__bsd__)
          fp = (FptrVoid) glXGetProcAddressARB( (const unsigned char*) name);

          if( fp == nullptr )
          {
              std::fprintf(stderr, " [ERROR] OpenGL function not found on Linux or BSD " );
              std::abort();
          }
      #endif
      return fp;
  }

  typedef unsigned int	GLenum;
  typedef unsigned char	GLboolean;
  typedef unsigned int	GLbitfield;
  typedef void		GLvoid;
  typedef signed char	GLbyte;		/* 1-byte signed */
  typedef short		GLshort;	/* 2-byte signed */
  typedef int		GLint;		/* 4-byte signed */
  typedef unsigned char	GLubyte;	/* 1-byte unsigned */
  typedef unsigned short	GLushort;	/* 2-byte unsigned */
  typedef unsigned int	GLuint;		/* 4-byte unsigned */
  typedef int		GLsizei;	/* 4-byte signed */
  typedef float		GLfloat;	/* single precision float */
  typedef float		GLclampf;	/* single precision float in [0,1] */
  typedef double	GLdouble;	/* double precision float */
  typedef double	GLclampd;	/* double precision float in [0,1] */

  #ifdef _WIN64
      typedef signed   long long int khronos_intptr_t;
      typedef unsigned long long int khronos_uintptr_t;
      typedef signed   long long int khronos_ssize_t;
      typedef unsigned long long int khronos_usize_t;
  #else
      typedef signed   long  int     khronos_intptr_t;
      typedef unsigned long  int     khronos_uintptr_t;
      typedef signed   long  int     khronos_ssize_t;
      typedef unsigned long  int     khronos_usize_t;
  #endif


  typedef khronos_ssize_t GLsizeiptr;
  typedef khronos_intptr_t GLintptr;

  using glClear_t = void (*) ( unsigned int mask );
  using glClearColor_t = void (*) ( float red, float green, float blue, float alpha );
  using glBindBuffer_t = void (*) (GLenum target, GLuint buffer);
  using glGenBuffers_t = void (*) (GLsizei n, GLuint * buffers);
  using glBufferData_t = void (*) (GLenum target, GLsizeiptr size, const void *data, GLenum usage);
  using glEnableVertexAttribArray_t = void (*) (GLuint index);
  using glDisableVertexAttribArray_t = void (*) (GLuint index);
  using glVertexAttribPointer_t = void (*) (GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void *pointer);
  using glDrawArrays_t = void (*) ( GLenum mode, GLint first, GLsizei count );

  struct OpenGL {

      OpenGL() {
          this->glClear = reinterpret_cast<glClear_t>( load_gl_function("glClear") );
          // It should be used reinterpret_cast<>, but C-style cast is used for breviety.
          this->glClearColor = (glClearColor_t) load_gl_function("glClearColor");
          this->glBindBuffer = (glBindBuffer_t) load_gl_function("glBindBuffer");
          this->glGenBuffers = (glGenBuffers_t) load_gl_function("glGenBuffers");
          this->glBufferData = (glBufferData_t) load_gl_function("glBufferData");
          this->glEnableVertexAttribArray   = (glEnableVertexAttribArray_t) load_gl_function("glEnableVertexAttribArray");
          this->glDisableVertexAttribArray  = (glDisableVertexAttribArray_t) load_gl_function("glDisableVertexAttribArray");
          this->glVertexAttribPointer       = (glVertexAttribPointer_t) load_gl_function("glVertexAttribPointer");
          this->glDrawArrays                = (glDrawArrays_t) load_gl_function("glDrawArrays");

      }

      glClear_t 	    	    glClear;
      glClearColor_t                glClearColor;
      glBindBuffer_t  		    glBindBuffer;
      glGenBuffers_t  		    glGenBuffers;
      glBufferData_t  	            glBufferData;
      glEnableVertexAttribArray_t   glEnableVertexAttribArray;
      glDisableVertexAttribArray_t  glDisableVertexAttribArray;
      glVertexAttribPointer_t  	    glVertexAttribPointer;
      glDrawArrays_t                glDrawArrays;
  };

  // ====== S T A R T - O F - A P P L I C A T I O N - C O D E ============//
  //

  struct Vertex2D
  {
      GLfloat x;
      GLfloat y;
  };

  int main(int argc, char** argv)
  {


      GLFWwindow* window;

      // Initialize GLFW 
      if (!glfwInit()){ return -1; }

      std::fprintf(stderr, " [TRACE] GLFW initialized Ok. \n");


      /* Create a windowed mode window and its OpenGL context */
      window = glfwCreateWindow(640, 480, "Load OpenGL functiion pointers", NULL, NULL);
      if (!window)
      {
          glfwTerminate();
          return -1;
      } 

      // ----- Before defining current OpenGL context ----//
          glfwMakeContextCurrent(window);
      // ----- After defining current OpenGL context -----//

      // GL object Must only be instantiated after an OpenGL context (Window) is obtained
      OpenGL gl;

      gl.glClearColor(0.0f, 0.5f, 0.6f, 1.0f);

      Vertex2D triangle_points[3] = {
          Vertex2D{-0.25,   -0.25}
        , Vertex2D{ 0.00,   0.25}
        , Vertex2D{ 0.25,  -0.25}
      };

      Vertex2D square_points[4] = {
          { -0.3,  -0.3 }
        , { -0.3,   0.3 }
        , {  0.3,   0.3 }
        , {  0.3,  -0.3 }
      };

      // ================== Triangle Buffer ====================//
      //

      GLuint vbo_triangle_ = 0;

      // Create an OpenGL VBO buffer
      gl.glGenBuffers(1, &vbo_triangle_ );
      assert( vbo_triangle_ != 0 );
      gl.glBindBuffer(GL_ARRAY_BUFFER, vbo_triangle_);
      gl.glBufferData(GL_ARRAY_BUFFER, sizeof(triangle_points), triangle_points, GL_STATIC_DRAW);
      gl.glBindBuffer(GL_ARRAY_BUFFER, 0);

      // ================ Square / Vertex Buffer Object 2 ==================//
      //

      GLuint vbo_square_ = 0;
      gl.glGenBuffers(1, &vbo_square_);
      // Check for error
      assert( vbo_square_ != 0);
      gl.glBindBuffer(GL_ARRAY_BUFFER, vbo_square_);
      gl.glBufferData(GL_ARRAY_BUFFER, sizeof(square_points), square_points, GL_STATIC_DRAW);
      gl.glBindBuffer(GL_ARRAY_BUFFER, 0);

      GLint shader_attr = 0;

      //  ======= R E N D E R  - L O O P ============//
      //
      while ( !glfwWindowShouldClose(window) )
      {
              gl.glClear(GL_COLOR_BUFFER_BIT);

          // ====== BEGIN RENDERING ============//

              // ------------ Draw triangle --------------//
              //
              gl.glBindBuffer(GL_ARRAY_BUFFER, vbo_triangle_);
              gl.glEnableVertexAttribArray(shader_attr);

              gl.glVertexAttribPointer(shader_attr, 2, GL_FLOAT, GL_FALSE, 0, nullptr);
              glDrawArrays(GL_TRIANGLES, 0, 3);

              // Disable global state
              gl.glBindBuffer(GL_ARRAY_BUFFER, 0);
              gl.glDisableVertexAttribArray(0);

              //------------ Draw Square -------------------------//
              //

              #if 1
              gl.glBindBuffer(GL_ARRAY_BUFFER, vbo_square_);
              gl.glEnableVertexAttribArray(shader_attr);
              gl.glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, nullptr);
              // Plot 4 vertices
              gl.glDrawArrays(GL_LINE_LOOP, 0, 4);

              // Disable global state
              gl.glBindBuffer(GL_ARRAY_BUFFER, 0);
              gl.glDisableVertexAttribArray(shader_attr);
              #endif

          // ====== END RENDERING ==============//

          /* Swap front and back buffers */
          glfwSwapBuffers(window);
          /* Poll for and process events */
          glfwPollEvents();

          if( glfwGetKey(window, 'Q' ) == GLFW_PRESS )
          {
               std::cout << " [TRACE] User typed Q =>> Shutdown program. Ok. " << '\n';
               break;
          }
      }

      glfwTerminate();
      return 0;
  }

#+END_SRC 

 *Building* 

Building on Linux: 

#+BEGIN_SRC sh 
  $ cmake -H. -B_build_linux -DCMAKE_BUILD_TYPE=Debug 
  $ cmake --build _build_linux --target
#+END_SRC

Building for Windows with Dockercross tool: 

#+BEGIN_SRC sh 
  $ docker run -it --rm -v $PWD:/cwd -w /cwd --entrypoint=bash dockcross/windows-static-x64
  $ cmake -H. -B_build_cross -DCMAKE_BUILD_TYPE=Debug 
  $ cmake --build _build_cross --target

  $ wine _build_cross/load-opengl-fptr.exe 
   [TRACE] GLFW initialized Ok. 
   [TRACE] Loading pointer to OpenGL subroutine = glClear 
   [TRACE] Loading pointer to OpenGL subroutine = glClearColor 
   [TRACE] Loading pointer to OpenGL subroutine = glBindBuffer 
   [TRACE] Loading pointer to OpenGL subroutine = glGenBuffers 
   [TRACE] Loading pointer to OpenGL subroutine = glBufferData 
   [TRACE] Loading pointer to OpenGL subroutine = glEnableVertexAttribArray 
   [TRACE] Loading pointer to OpenGL subroutine = glDisableVertexAttribArray 
   [TRACE] Loading pointer to OpenGL subroutine = glVertexAttribPointer 
   [TRACE] Loading pointer to OpenGL subroutine = glDrawArrays 
#+END_SRC
** 2D - using VAO

A VAO (Vertex Array Object) is an object that stores all the
information necessary to render a VBO (Vertex Buffer Object). For
instance, a VAO allows rendering the data, by performing the following
set of operations only once: 1 - binding the VAO; 2 - binding the
buffer via glBindBuffer() call; 3 - enabling the vertex attribute via
glEnableVertexAttribArray(); 4 - setting the data layout by calling
glVertexAttribPointer() subroutine. Without a VAO, all those
subroutines would need to be called on every frame rendering.

Algorithm for VAO usage:

 + Setup

#+BEGIN_SRC cpp
   // Shader attribute location
   GLint shader_attr = 1;

   // -------------- Setup VAO --------------------//
   //
   GLuint vao;
   glGenVertexArrays(1, &vao); // Instantiate VAO

   GLuint vbo;
   glGenBuffers(1, &vbo);
   // Bind VBO (Recorded by VAO)
   glBindBuffer(GL_ARRAY_BUFFER, vbo);

   // Upload data to GPU
   glBufferData(GL_ARRAY_BUFFER, buffer_size, buffer, GL_STATIC_DRAW);

   glEnableVertexAttribArray(shader_attr);

   // Set buffer data layout (Recorded by VAO)
   glVertexAttribPointer(shader_attr, size, type, GL_FALSE, 0, nullptr);

   // ------- Disable Global state ---------//

   // Unbind VAO
   glBindVertexArray(0);
   // Unbind VBO
   glBindBuffer(GL_ARRAY_BUFFER, vbo);
   // Disable current shader attribute
   glDisableVertexAttribArray(shader_attr);
#+END_SRC

 + Rendering loop

#+BEGIN_SRC cpp
         ... ...         ... ...         ... ...
         ... ...         ... ...         ... ...

    //  ======= R E N D E R  - L O O P ============//
    //                                             //
    while ( !glfwWindowShouldClose(window) )
    {
        glClear(GL_COLOR_BUFFER_BIT);
         ... ...         ... ...         ... ...
         ... ...         ... ...         ... ...

         glBindVertexArray(vao);
         glDrawArrays(GL_LINE_LOOP, 0, NUMBER_OF_VERTICES);

         glBindVertexArray(vao1);
         glDrawArrays(GL_LINE_TRIANGLES, 0, NUMBER_OF_VERTICES_1);

         glBindVertexArray(vao2);
         glDrawArrays(GL_LINES, 0, NUMBER_OF_VERTICES_2);

         ... ...         ... ...         ... ...
         ... ...         ... ...         ... ...

        /* Swap front and back buffers */
        glfwSwapBuffers(window);
        /* Poll for and process events */
        glfwPollEvents();
    }
#+END_SRC


 *Further Reading*

  + [[https://www.khronos.org/opengl/wiki/Tutorial2:_VAOs,_VBOs,_Vertex_and_Fragment_Shaders_(C_/_SDL)][Tutorial2: VAOs, VBOs, Vertex and Fragment Shaders (C / SDL) - OpenGL Wiki]]

  + [[https://android-developers.googleblog.com/2015/10/game-performance-vertex-array-objects.html][Android Developers Blog: Game Performance: Vertex Array Objects]]

  + [[https://android-developers.googleblog.com/2015/03/today-we-want-to-share-some-best.html][Android Developers Blog: Game Performance: Layout Qualifiers]]

  + [[https://stackoverflow.com/questions/28270954/process-of-setting-up-a-vao-in-opengl][c++ - Process of setting up a VAO in OpenGL - Stack Overflow]]

  + [[https://stackoverflow.com/questions/8923174/opengl-vao-best-practices][OpenGL VAO best practices - Stack Overflow]]

  + [[https://computergraphics.stackexchange.com/questions/5895/what-is-an-opengl-vao-in-a-nutshell][vertex buffer object - What is an OpenGL VAO in a nutshell?]]  -
    Computer Graphics Stack Exchange

  + [[https://gamedev.stackexchange.com/questions/92832/in-opengl-whats-quicker-lots-of-smaller-vaos-or-one-large-one-updated-each-fr][performance - In OpenGL what's quicker]] - , lots of smaller VAOs,
    or one large one updated each frame? - Game Development Stack
    Exchange.

  + [[https://stackoverflow.com/questions/18485381/opengl-how-many-vaos][OpenGL How Many VAOs - Stack Overflow]]


  *Screenshot*

#+CAPTION: 2D OpenGL draw using VAO - Vertex Array Object
[[file:images/opengl-draw2-vao.png][file:images/opengl-draw2-vao.png]]

  *Sample Code*

File: CMakeLists.txt

#+BEGIN_SRC cmake
    cmake_minimum_required(VERSION 3.5)
    project(draw2d)

    set(CMAKE_CXX_STANDARD 17)
    set(CMAKE_CXX_STANDARD_REQUIRED ON)
    set(CMAKE_VERBOSE_MAKEFILE ON)

    #================ GLFW Settings  ===============#

    find_package(OpenGL REQUIRED)

    include(FetchContent)

    # Set GLFW Options before FectchContent_MakeAvailable
    set( GLFW_BUILD_EXAMPLES OFF CACHE BOOL  "GLFW lib only" )
    set( GLFW_BUILD_TESTS    OFF CACHE BOOL  "GLFW lib only" )
    set( GLFW_BUILD_DOCS     OFF CACHE BOOL  "GLFW lib only" )
    set( GLFW_BUILD_INSTALL  OFF CACHE BOOL  "GLFW lib only" )

    # Donwload GLFW library
    FetchContent_Declare(
      glfwlib
      URL   https://github.com/glfw/glfw/releases/download/3.3.2/glfw-3.3.2.zip
    )
    FetchContent_MakeAvailable(glfwlib)

           add_executable( draw2d-vao  draw2d-vao.cpp )
    target_link_libraries( draw2d-vao  glfw OpenGL::GL GLU)
#+END_SRC

File: _draw2d-vao.cpp_

#+BEGIN_SRC cpp
  // Draw curve mapping buffer to the GPU.
  #include <iostream>
  #include <vector>
  #include <array>
  #include <cmath>
  #include <cassert>

  // -------- OpenGL headers ---------//
  //
  #define GL_GLEXT_PROTOTYPES 1 // Necessary for OpenGL >= 3.0 functions
  #define GL3_PROTOTYPES      1 // Necessary for OpenGL >= 3.0 functions

  #include <GL/gl.h>
  #include <GLFW/glfw3.h>

  // #include <GL/glew.h>
  #include <GL/glu.h>
  #include <GL/glut.h>


  struct Vertex2D
  {
      GLfloat x;
      GLfloat y;
  };

  // Upload buffer from main memory to GPU VBO
  // =>> Parameters VAO, VBO are allocated by the caller.
  void send_buffer(   GLuint* pVao        // Pointer to VAO (Vertex Array Object) - allocated by caller
                    , GLuint* pVbo        // Pointer to VBO (Vertex Buffer Object) - allocated by caller
                    , GLsizei sizeBuffer  // Total buffer size in bytes
                    , void*   pBufffer    // Pointer to buffer
                    , GLint   shader_attr // Shader attribute location
                    , GLint   size        // Number of coordinates of a given vertex
                    , GLenum  type        // Type of each element coordinate
                    );

  void* map_buffer( GLuint* pVao         // Pointer to VAO - allocated by calling code.
                  , GLuint* pVbo         // Pointer to VBO - allocated by calling code.
                  , GLsizei data_size    // Total size in bytes that is allocated to buffer
                  , GLint   shader_attr  // Shadder attribute location id (0 - zero) is there is no shader.
                  , GLint   size         // Number of component of each vertex
                  , GLenum  type         // Type of each vertex element
                  );


  int main(int argc, char** argv)
  {
      GLFWwindow* window;

      /* Initialize the library */
      if (!glfwInit())
          return -1;

      glutInit(&argc, argv);

      /* Create a windowed mode window and its OpenGL context */
      window = glfwCreateWindow(640, 480, "2D Drawing using VAO", NULL, NULL);
      if (!window)
      {
          glfwTerminate();
          return -1;
      }

      /* Make the window's context current */
      glfwMakeContextCurrent(window);
      // glClearColor(0.0f, 0.5f, 0.6f, 1.0f);
      glClearColor(0.0f, 0.0f, 0.0, 1.0f);

      // Shader attribute location (default 0)
      GLint shader_attr = 0;


      // ================ Square / Vertex Buffer Object 2 ==================//
      //

      Vertex2D square_points[4] = {
          { -0.3,  -0.3 }
        , { -0.3,   0.3 }
        , {  0.3,   0.3 }
        , {  0.3,  -0.3 }
      };

      GLuint vao_square = 0;
      GLuint vbo_square = 0;

      // Upload square_points buffer data to GPU
      send_buffer( &vao_square, &vbo_square, sizeof(square_points)
                  , square_points, shader_attr, 2, GL_FLOAT );

      assert( vao_square != 0 );
      assert( vbo_square != 0 );

      std::fprintf( stderr, " [TRACE] vao_square = %d, vbo_square = %d \n"
                   , vao_square, vbo_square);


      // ============== Circle / Vertex Buffer Object 3 ==================//
      //
      GLuint vao_circle = 0;
      GLuint vbo_circle = 0;

      // Number of vertices to used to render the circle.
      constexpr size_t N_CIRCLE = 100;
      constexpr float  PI       = 3.1415927;
      const     float  radius   = 0.75;

      // C-style cast should not be used here
      Vertex2D* pCursor = (Vertex2D*) map_buffer( &vao_circle, &vbo_circle, N_CIRCLE * sizeof(Vertex2D)
                                                , shader_attr, 2, GL_FLOAT );

      float angle = 0.0;
      const float step = 2 * PI / N_CIRCLE;

      for(size_t n = 0; n < N_CIRCLE; n++)
      {
          pCursor[n].x = radius * cosf( angle );
          pCursor[n].y = radius * sinf( angle );
          angle = angle + step;
      }


      //  ======= R E N D E R  - L O O P ============//
      //                                             //
      while ( !glfwWindowShouldClose(window) )
      {
          glClear(GL_COLOR_BUFFER_BIT);

          // ====== BEGIN RENDERING ============//

              //------------ Draw Square -------------------------//
              //
               glBindVertexArray(vao_square);
              // Plot 4 vertices
              glDrawArrays(GL_LINE_LOOP, 0, 4);
              // Unbind vao (restore global state to default value)
              glBindVertexArray(0);

              //------------ Draw Circle -------------------------//
              //
              glBindVertexArray(vao_circle);
              glDrawArrays(GL_LINE_LOOP, 0, N_CIRCLE);
              glBindVertexArray(0);

          // ====== END RENDERING ==============//

          /* Swap front and back buffers */
          glfwSwapBuffers(window);
          /* Poll for and process events */
          glfwPollEvents();

          if( glfwGetKey(window, 'Q' ) == GLFW_PRESS )
          {
               std::cout << " [TRACE] User typed Q =>> Shutdown program. Ok. " << '\n';
               break;
          }
      }

      // ---------- Dispose Buffer Objects ---------//
      glDeleteBuffers(1, &vbo_square);
      glDeleteBuffers(1, &vbo_circle);

      glfwTerminate();
      return 0;
  }

  // ---------- I M P L E M E N T A T I O N S ------------------//
  //                                                            //

  // Upload buffer from main memory to GPU VBO
  // =>> Parameters VAO, VBO are allocated by the caller.
  void send_buffer(   GLuint* pVao        // Pointer to VAO (Vertex Array Object) - allocated by caller
                    , GLuint* pVbo        // Pointer to VBO (Vertex Buffer Object) - allocated by caller
                    , GLsizei sizeBuffer  // Total buffer size in bytes
                    , void*   pBufffer    // Pointer to buffer
                    , GLint   shader_attr // Shader attribute location id
                    , GLint   size        // Number of coordinates of a given vertex
                    , GLenum  type        // Type of each element coordinate
                    )
  {
      assert(pVao != nullptr);
      assert(pVbo != nullptr);
      GLuint& vao = *pVao;
      GLuint& vbo = *pVbo;

      // Generate and bind current VAO (Vertex Array Object)
      glGenVertexArrays(1, &vao);
      glBindVertexArray(vao);

      // Generate and bind current VBO (Vertex Buffer Object)
      glGenBuffers(1, &vbo);
      glBindBuffer(GL_ARRAY_BUFFER, vbo);

      // Upload data to current VBO buffer in GPU
      glBufferData(GL_ARRAY_BUFFER, sizeBuffer, pBufffer, GL_STATIC_DRAW);

      glEnableVertexAttribArray(shader_attr);

      // Set data layout - how data will be interpreted.
      glVertexAttribPointer(shader_attr, size, type, GL_FALSE, 0, nullptr);

      // ------ Disable Global state set by this function -----//
      //
      // Unbind VAO
      glBindVertexArray(0);
      // Unbind VBO
      glBindBuffer(GL_ARRAY_BUFFER, vbo);
      // Disable current shader attribute
      glDisableVertexAttribArray(shader_attr);

  }

  void* map_buffer( GLuint* pVao         // Pointer to VAO - allocated by calling code.
                  , GLuint* pVbo         // Pointer to VBO - allocated by calling code.
                  , GLsizei data_size    // Total size in bytes that is allocated to buffer
                  , GLint   shader_attr  // Shadder attribute location id (0 - zero) is there is no shader.
                  , GLint   size         // Number of component of each vertex
                  , GLenum  type         // Type of each vertex element
                  )
  {
      GLuint& vao = *pVao;
      GLuint& vbo = *pVbo;

      // Generate and bind current VAO (Vertex Array Object)
      glGenVertexArrays(1, &vao);
      glBindVertexArray(vao);

      // Generate and bind current VBO (Vertex Buffer Object)
      glGenBuffers(1, &vbo);
      glBindBuffer(GL_ARRAY_BUFFER, vbo);

      /** See: https://www.khronos.org/registry/OpenGL-Refpages/gl4/html/glMapBuffer.xhtml
       ,*  Khronos Group: "glMapBuffer and glMapNamedBuffer map the entire data store of
       ,*  a specified buffer object into the client's address space. The data can then be directly
       ,*  read and/or written relative to the returned pointer,
       ,*  depending on the specified access policy."
       ,*/
      glBufferData(GL_ARRAY_BUFFER, data_size, nullptr, GL_STATIC_DRAW);
      void* pbuffer = glMapBuffer( GL_ARRAY_BUFFER, GL_WRITE_ONLY );

      glEnableVertexAttribArray(shader_attr);
      // Set data layout - how data will be interpreted.
      glVertexAttribPointer(shader_attr, size, type, GL_FALSE, 0, nullptr);

      // ------ Disable Global state set by this function -----//
      //
      // Unbind VAO
      glBindVertexArray(0);
      // Unbind VBO
      glBindBuffer(GL_ARRAY_BUFFER, vbo);
      // Disable current shader attribute
      glDisableVertexAttribArray(shader_attr);

      // ------- Return --------------------------------------//
      return pbuffer;
  }
#+END_SRC
** 2D - using shaders

OpenGL retained mode (programmable pipeline) uses shaders, which are
programs that uses GLSL (OpenGL shading language) programming
language, runs on the GPU (Graphics Processing Unit). A vertex shader
performs vertex coordinate transformations on all vertices and colors
received from VBO - Vertex Buffer Objects. The fragment shader runs
after the vertex shader and deals with vertice texture and color
computations.

A vertex shader has the following features:

 + Data types:
   + float => Float IEEE754 32 bits floating point scalar.
   + vec2 => Vector of 2 coordinates
   + vec3 => Vector of 3 coordinates
   + vec4 => Vector of 4 coordinates
   + mat2 => 2x2 matrix
   + mat4 => 4x4 homogeneous coordinate matrix
   + mat3 => 3x3 matrix

 + _uniform variables_ => Shader variables declared with _uniform_ storage
   qualifier. They allow passing data, such as transformation
   matrices, vectors or scalar values, from the C++-side to the shader
   program. Uniform variables can be used for passing the model matrix,
   camera matrix and projection matrix to the shader.
   * Declaration example: 'uniform mat4 u_model;'

 + _attribute variables_ => Used for passing vertices or color
   coordinates from VBOs (Vertex Buffer Objects). Those variables are
   only accessible in the shader.
   * Declaration example: 'layout ( location = 2)  in vec2 position;'

 + _Output_ => Shader programs uses pre-defined global variables for
   output, for instance, _gl_Position_, which sets the current vertex
   position in clip-space (NDC - Normalized Device Coordinates).



 *Documentation*

  + [[https://www.khronos.org/opengl/wiki/Core_Language_(GLSL)][Core Language (GLSL) - OpenGL Wiki]] / Khronos Group.
    + Syntax of GLSL (OpenGL shading language)

  + [[https://www.khronos.org/opengl/wiki/Data_Type_(GLSL)][Data Type (GLSL) - OpenGL Wiki]] / Khronos Group

  + [[https://en.wikipedia.org/wiki/OpenGL_Shading_Language][OpenGL Shading Language - Wikipedia]]

  + [[https://www.khronos.org/opengl/wiki/Shader][Shader - OpenGL Wiki]] / Khronos Group

  + [[https://www.khronos.org/opengl/wiki/Vertex_Shader][Vertex Shader - OpenGL Wiki]] / Khronos Group

  + [[https://www.khronos.org/opengl/wiki/Fragment_Shader][Fragment Shader - OpenGL Wiki]] / Khronos Group

  + [[https://www.khronos.org/opengl/wiki/Uniform_(GLSL)][Uniform (GLSL) - OpenGL Wiki]] / Khronos Group
    + Discusses shader uniform variables.

  + [[https://www.khronos.org/opengl/wiki/Layout_Qualifier_(GLSL)][Layout Qualifier (GLSL) - OpenGL Wiki]] / Khronos Group
    + Shader attribute index (attribute location)

  + [[https://www.khronos.org/opengl/wiki/Geometry_Shader][Geometry Shader - OpenGL Wiki]] / Khronos Group.

  + [[https://www.khronos.org/opengl/wiki/Shader_Compilation][Shader Compilation - OpenGL Wiki]] / Khronos Group.

  + [[https://www.khronos.org/opengl/wiki/Vertex_Rendering][Vertex Rendering - OpenGL Wiki]]

  + [[https://developer.mozilla.org/en-US/docs/Web/API/WebGL_API/Data][Data in WebGL - Web APIs | MDN]] / Mozilla
    + Explanation of shader's attribute variables and shader's uniform
      variables.

  *Source Code*

Summary:

 + This sample application, draws many 4 triangles from a 2 VBO
   (Vertex Buffer Object) and a single VAO (Vertex Array Object). The
   application uses a vertex shader for performing coordinate
   transformation for every triangle and drawing them at different
   position with different rotation angles around Z axis. The triangle
   'triangleD' follows the mouse position while the triangle
   'triangleB' rotates acoording to the mouse position.

 + The vertex shader has two attributes variables ('position' and
   'color') and two uniform variables u_model, that contains the model
   matrix from every rendered object, and u_projection, which contains
   the projection matrix.

Screenshot:

[[file:images/opengl-draw2d-shader.png][file:images/opengl-draw2d-shader.png]]


_Vertex Shader_ - Source Code (Extraced from _draw2d-shader.cpp_)

#+BEGIN_SRC cpp
    #version 330 core

    // Supplied by GPU - attribute location (id = 2)
    // contains a (X, Y) vertex
    layout ( location = 2)  in vec2 position;
    layout ( location = 1 ) in vec3 color;     // Contains (Red, Green, Blue) components

    // Output to fragment shader
    out vec3 out_color;

    // Model matrix => projection coordinates from the local space (model space)
    // to view space .
    // Transform 4x4 matrix - supplied by the C++ side.
    uniform mat4 u_model;

    // Projection matrix => projections camera-space coordinates
    // to clip-space coordinates. (-1.0 to 1.0) range.
    uniform mat4 u_projection;

    void main()
    {
        // gl_Position => Global output variable that holds the
        // the current vertex position. It is a vector of
        // components (X, Y, Z = 0, W = ignored)
        gl_Position = u_projection * u_model * vec4(position, 0, 1.0);

        // Forward to fragment shader
        out_color = color;
    }
#+END_SRC

_Fragment Shader_ -  Source Code (Extracted from _draw2d-shader.cpp_)


#+BEGIN_SRC cpp
   #version 330

   // This color comes from Vertex shader
   in vec3 out_color;

   void main()
   {
       // Set vertex colors
       gl_FragColor = vec4(out_color, 1.0);

   }
#+END_SRC

File: _CMakeLists.txt_

#+BEGIN_SRC cmake
   cmake_minimum_required(VERSION 3.5)
   project(GLFW_project)

   set(CMAKE_CXX_STANDARD 17)
   set(CMAKE_CXX_STANDARD_REQUIRED ON)
   set(CMAKE_VERBOSE_MAKEFILE ON)

   #================ GLFW Settings  ===============#

   find_package(OpenGL REQUIRED)

   include(FetchContent)

   # Set GLFW Options before FectchContent_MakeAvailable
   set( GLFW_BUILD_EXAMPLES OFF CACHE BOOL  "GLFW lib only" )
   set( GLFW_BUILD_TESTS    OFF CACHE BOOL  "GLFW lib only" )
   set( GLFW_BUILD_DOCS     OFF CACHE BOOL  "GLFW lib only" )
   set( GLFW_BUILD_INSTALL  OFF CACHE BOOL  "GLFW lib only" )

   # Donwload GLFW library
   FetchContent_Declare(
     glfwlib
     URL   https://github.com/glfw/glfw/releases/download/3.3.2/glfw-3.3.2.zip
   )
   FetchContent_MakeAvailable(glfwlib)

   # Download GLM (OpenGL math library for matrix and vectors transformation)
   FetchContent_Declare(
     glm
     URL  https://github.com/g-truc/glm/archive/0.9.8.zip
   )
   FetchContent_MakeAvailable(glm)

   message( [DEBUG] " glm_SOURCE_DIR = ${glm_SOURCE_DIR} ")
   include_directories(${glm_SOURCE_DIR})

     # ======= TARGETS ===========================#

          add_executable( draw2d-shader  draw2d-shader.cpp   )
   target_link_libraries( draw2d-shader  glfw OpenGL::GL GLU )
#+END_SRC

File: _draw2d-shader.cpp_

#+BEGIN_SRC cpp
  // Draw many colored triangles from a single VBO (Vertex Buffer Object)
  #include <iostream>
  #include <vector>
  #include <array>
  #include <cmath>
  #include <functional>

  // -------- OpenGL headers ---------//
  //
  #define GL_GLEXT_PROTOTYPES 1 // Necessary for OpenGL >= 3.0 functions
  #define GL3_PROTOTYPES      1 // Necessary for OpenGL >= 3.0 functions

  #include <GL/gl.h>
  #include <GLFW/glfw3.h>

  // #include <GL/glew.h>
  #include <GL/glu.h>

  // --------- OpenGL Math Librar ------------//
  #include <glm/glm.hpp>
  #include <glm/gtc/matrix_transform.hpp>
  #include <glm/gtc/type_ptr.hpp>
  #include <glm/gtx/string_cast.hpp>


  // Compile some  shader
  void compile_shader(GLuint m_program, const char* code, GLenum type);

  // Send data from memory to GPU VBO memory
  void send_buffer(   GLuint* pVao        // Pointer to VAO (Vertex Array Object) - allocated by caller
                    , GLuint* pVbo        // Pointer to VBO (Vertex Buffer Object) - allocated by caller
                    , GLsizei sizeBuffer  // Total buffer size in bytes
                    , void*   pBufffer    // Pointer to buffer
                    , GLint   shader_attr // Shader attribute location id
                    , GLint   size        // Number of coordinates of a given vertex
                    , GLenum  type        // Type of each element coordinate
                    );

  // ------------ Basic Data Structures -----------//

  // Wrapper for 2D vertex coordinates
  struct Vertex2D{  GLfloat x, y;  };
  // Wrapper for RGB colors
  struct ColorRGB { GLfloat r, g, b; };

  constexpr ColorRGB COLOR_R = { 1.0, 0.0, 0.0  }; // Red
  constexpr ColorRGB COLOR_G = { 0.0, 1.0, 0.0  }; // Green
  constexpr ColorRGB COLOR_B = { 0.0, 0.0, 1.0  }; // Blue


  class Model_Triangle
  {
      // Object position in World Coordinates
      float _x = 0.0, _y = 0.0;
      // Scale
      float _scale = 1.0;
      // Rotation angle around z axis
      float _angle = 0.0;

  public:
      Model_Triangle(float scale = 1.0){   }
      void rotate(float angle)          { _angle = angle; }
      void translate(float x , float y) { _x = x; _y = y; }
      void scale(float scale)           { _scale = scale; }
      void zoom(float factor)           {  _scale = factor + _scale; }

      // Render/draw this object
      void render(GLuint vao, GLint  u_model)
      {

          // Reset model matrix to indentiy matrix
          glm::mat4 _model(1.0);
          // Scale object (increase or decrease object size)
          _model =  glm::scale( _model, glm::vec3(_scale, _scale, _scale) );
          // Move to (X, Y) position
          _model = glm::translate( _model, glm::vec3(_x, _y, 0.0)  );
          // Rotate from a given angle around Z axis at current object X, Y  postion
          _model = glm::rotate( _model, glm::radians(_angle), glm::vec3(0, 0, 1) );

          // Set shader uniform variable.
          glUniformMatrix4fv(
              // Shader uniform variable location
              u_model
              // Number of matrices that will be set
              , 1
              // GL_FALSE => Matrix is in column-major order (Fortran matrix layout)
              // GL_TRUE  => Matrix is in row-major order (C, C++ array, matrix layout)
              , GL_FALSE
              // Pointer to first element transform matrix (homogeneous coordinate)
              , glm::value_ptr(_model)
          );
          glBindVertexArray(vao);
          // Draw 3 vertices
          glDrawArrays(GL_TRIANGLES, 0, 3);
          // Disable global state
          glBindVertexArray(0);
      }
  };


  // Global variable [WARNING] - Vulnerable to global "initialization fiasco"
  // undefined behavior.
  auto Cursor_Callback = std::function<void (GLFWwindow* window, double xpos, double ypos)>();

  int main(int argc, char** argv)
  {

      /* Initialize the library */
      if (!glfwInit()){ return -1; }

      // OpenGL Core Profile
      glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
      glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
      glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);

      glfwSetErrorCallback([](int error, const char* description)
                           {
                              std::cerr << " [GLFW ERROR] " << error
                                        << ": " << description
                                        << std::endl;
                           });

      GLFWwindow* window = glfwCreateWindow(640, 480, "2D Drawing with Shader", NULL, NULL);
      if (!window){ glfwTerminate(); return -1; }

      // OpenGL context
      glfwMakeContextCurrent(window);
      glClearColor(0.0f, 0.5f, 0.6f, 1.0f);

      // Mouse move callback
      // Note: It is only possible to pass non-capturing lambda to this callback.
      glfwSetCursorPosCallback( window
                              , [](GLFWwindow* window, double xpos, double ypos)
                                {
                                    // Forward call to global variable std::function<>
                                    // (Function object)
                                    Cursor_Callback(window, xpos, ypos);
                                });

      // ====== S H A D E R - C O M P I L A T I O N ====//
      //                                                //

      // Note: The shader source code is at the end of file.
      extern const char* code_vertex_shader;
      extern const char* code_frag_shader;

      GLuint prog = glCreateProgram();
      // Compile shader code
      compile_shader(prog, code_vertex_shader, GL_VERTEX_SHADER  ) ;
      compile_shader(prog, code_frag_shader,   GL_FRAGMENT_SHADER );

      // Bind current shader (enable it). Only one shader of the same type can
      // be bound at a time. For instance, only a single vertex shader can
      // be enabled at a time
      glUseProgram(prog);

      // Get shader uniform variable location for projection matrix
      // See shader code: "uniform mat4 projection;"
      const GLint u_proj  = glGetUniformLocation(prog, "u_projection");
      assert( u_proj >= 0 && "Failed to find uniform variable" );

      // Get shader uniform variable  location for model matrix.
      const GLint u_model  = glGetUniformLocation(prog, "u_model");
      assert( u_model >= 0 && "Failed to find uniform variable" );

      // Get shader attribute location - the function glGetAttribLocation - returns (-1) on error.
      const GLint attr_position = glGetAttribLocation(prog, "position");
      assert( attr_position >= 0 && "Failed to get attribute location" );
      std::fprintf(stderr, " [TRACE] Attribute location attr_position = %d \n"
                  , attr_position);

      // Get shader index of color attribute variable.
      const GLint attr_color = glGetAttribLocation(prog, "color");
      assert( attr_color >= 0);


      // ====== U P L O A D - TO - G P U =========================//
      //                                                          //

      constexpr size_t NUM_VERTICES = 3;

      // Array of triangle vertex coordinates (X, Y)
      Vertex2D vertices[NUM_VERTICES] = {
        {  0.0,   0.2} // Vertex 0  (x =  0.0 ; y =  0.5)
      , { -0.2,  -0.2} // Vertex 1  (x = -0.5 ; y = -0.5)
      , {  0.2,  -0.2} // Vertex 2  (x =  0.5 ; y = -0.5)
      };

      ColorRGB colors[NUM_VERTICES] = {
            COLOR_R   // Color of vertex 0
          , COLOR_B   // Color of vertex 1
          , COLOR_G   // Color of vertex 2
      };

      // Always initialize with zero (0), otherwise, the values
      // of those variables will be undefined. (Undefine Behavior)
      GLuint vao_triangle = 0;
      GLuint vbo_vertices = 0;
      GLuint vbo_colors   = 0;

      // Send vertices to GPU VBO (vbo_vertices)
      send_buffer(  &vao_triangle    // Ponter to VAO [OUTPUT]
                  , &vbo_vertices    // Pointer VBO   [OUTPUT]
                  , sizeof(vertices) // Size of buffer sent to GPU (VBO memory)
                  , vertices         // Pointer to buffer
                  , attr_position    // Shader attribute location
                  , 2                // Each vertex has 2 coordinates (X, Y)
                  , GL_FLOAT         // Each vertex coordinate has type GLfloat (float)
                  );

      // Send colors to GPU VBO (vbo_colors)
      send_buffer(  &vao_triangle   // Pointer Vertex Array object
                  , &vbo_colors     // Pointer Vertex buffer object handle (aka token)
                  , sizeof(colors)  // Buffer size
                  , colors          // Pointer to buffer (addrress of first buffer element)
                  , attr_color      // Shader attribute location
                  , 3               // Each color has 3 coordinates (R, G, B)
                  , GL_FLOAT        // Each color coordiante has type GLfloat
                  );

      // ============== Set Shader Uniform Variables =============//
      //                                                          //

      int width, height;
      glfwGetWindowSize(window, &width, &height);

      // Window aspect ratio
      float aspect = static_cast<float>(width) / height;

      // Identity matrix
      const auto identity = glm::mat4(1.0);

      // Projection matrix - orthogonal projection for compensating
      // the aspect ratio.
      glm::mat4 Tproj = glm::ortho(-aspect, aspect, -1.0f, 1.0f, -1.0f, 1.0f);

      // Set projection matrix uniform variable
      glUniformMatrix4fv(u_proj, 1, GL_FALSE, glm::value_ptr(Tproj) );
      // Set model matrix uniform variable
      glUniformMatrix4fv(u_model, 1, GL_FALSE, glm::value_ptr(identity) );

      // Instatiate objects that will be rendered
      auto triangleA = Model_Triangle{};
      triangleA.scale(0.50);
      triangleA.rotate(122);

      auto triangleB = Model_Triangle{};
      triangleB.translate(-0.20, 0.30);
      triangleB.rotate(50);
      triangleB.scale(1.8);

      auto triangleC = Model_Triangle{};
      triangleC.translate(0.20, 0.20);
      triangleC.rotate(35);

      // Follows mouse coordinate
      auto triangleD = Model_Triangle{};
      triangleD.translate(-0.60, -0.50);
      triangleD.scale(1.50);
      // triangleD.scale(1.2);
      // triangleD.rotate(40);

      // --------- Set mouse callback ----------//

      constexpr float PI = 3.1415926536f;
      constexpr float FACTOR_RAD_TO_DEG = 180.0 / PI;

      Cursor_Callback = [&](GLFWwindow* window, double xpos, double ypos)
      {
          // Mouse normalized coordinate
          // =>> see: https://stackoverflow.com/questions/23870750/
          float xMouse = -1.0f + 2.0f * xpos / width;
          float yMouse =  1.0f - 2.0f * ypos / height;

          // Angle in degrees between X axis vector(1, 0, 0) and poit (xMouse, yMouse, 0) position.
          // Note: atan2f() returns angle in radians.
          float angle = atan2f(yMouse, xMouse) * FACTOR_RAD_TO_DEG ;
          fprintf(stderr, " [TRACE] Angle = %f \n", angle);

          // Triangle D follows mouse position
          triangleD.translate(xMouse, yMouse);
          triangleD.rotate(angle);

          triangleB.rotate(angle);

          // Show mouse position
           // std::fprintf(stderr, " [MOUSE] x = %f ; y = %f \n", xMouse, yMouse);
      };

      //  ======= R E N D E R  - L O O P ============//
      //                                             //
      while ( !glfwWindowShouldClose(window) )
      {
          glClear(GL_COLOR_BUFFER_BIT);

          // ====== BEGIN RENDERING ============//

              triangleA.render(vao_triangle, u_model);
              triangleB.render(vao_triangle, u_model);
              triangleC.render(vao_triangle, u_model);
              triangleD.render(vao_triangle, u_model);

              //std::fprintf(stderr, " [TRACE] Redraw screen \n");

          // ====== END RENDERING ==============//

          /* Swap front and back buffers */
          glfwSwapBuffers(window);
          /* Poll for and process events */
          glfwPollEvents();

          if( glfwGetKey(window, 'Q' ) == GLFW_PRESS )
          {
               std::cout << " [TRACE] User typed Q =>> Shutdown program. Ok. " << '\n';
               break;
          }

          // Increase scale by 10%
          if( glfwGetKey(window, 'A' ) == GLFW_PRESS ){ triangleD.zoom(+0.10); }
          // Decrease scale by 10%
          if( glfwGetKey(window, 'B' ) == GLFW_PRESS ){ triangleD.zoom(-0.10); }


      }

      glfwTerminate();
      return 0;

  } // --- End of main() -----//


  // Minimal vertex shader =>> Runs on the GPU and processes each vertex.
  const char* code_vertex_shader = R"(
      #version 330 core

      // Supplied by GPU - attribute location (id = 2)
      // contains a (X, Y) vertex
      layout ( location = 2)  in vec2 position;
      layout ( location = 1 ) in vec3 color;     // Contains (Red, Green, Blue) components

      // Output to fragment shader
      out vec3 out_color;

      // Model matrix => projection coordinates from the local space (model space)
      // to view space .
      // Transform 4x4 matrix - supplied by the C++ side.
      uniform mat4 u_model;

      // Projection matrix => projections camera-space coordinates
      // to clip-space coordinates. (-1.0 to 1.0) range.
      uniform mat4 u_projection;

      void main()
      {
          // gl_Position => Global output variable that holds the
          // the current vertex position. It is a vector of
          // components (X, Y, Z = 0, W = ignored)
          gl_Position = u_projection * u_model * vec4(position, 0, 1.0);

          // Forward to fragment shader
          out_color = color;
      }

  )";

  // Fragment shader source code
  const char* code_frag_shader = R"(
      #version 330

      // This color comes from Vertex shader
      in vec3 out_color;

      void main()
      {
          // Set vertex colors
          gl_FragColor = vec4(out_color, 1.0);

      }
  )";

      // ====== I M P L E M E N T A T I O N S ==========//

  void compile_shader(GLuint m_program, const char* code, GLenum type)
  {
      GLint shader_id = glCreateShader( type );
      glShaderSource(shader_id, 1, &code, nullptr);
      glCompileShader(shader_id);

      GLint is_compiled = GL_FALSE;
      // Check shader compilation result
      glGetShaderiv(shader_id, GL_COMPILE_STATUS, &is_compiled);

      // If there is any shader compilation result,
      // print the error message.
      if( is_compiled == GL_FALSE)
      {
          GLint length;
          glGetShaderiv(shader_id, GL_INFO_LOG_LENGTH, &length);
          assert( length > 0 );

          std::string out(length + 1, 0x00);
          GLint chars_written;
          glGetShaderInfoLog(shader_id, length, &chars_written, out.data());
          std::cerr << " [SHADER ERROR] = " << out << '\n';
          // Abort the exection of current process.
          std::abort();
      }

      glAttachShader(m_program, shader_id);
      glDeleteShader(shader_id);
      glLinkProgram(m_program);
      GLint link_status = GL_FALSE;
      glGetProgramiv(m_program, GL_LINK_STATUS, &link_status);
      assert( link_status != GL_FALSE );
      // glUseProgram(m_program);
  }

  // Upload buffer from main memory to GPU VBO
  // =>> Parameters VAO, VBO are allocated by the caller.
  void send_buffer( GLuint* pVao, GLuint* pVbo, GLsizei sizeBuffer
                  , void* pBufffer, GLint   shader_attr, GLint size
                  , GLenum type)
  {
      assert(pVao != nullptr);
      assert(pVbo != nullptr);
      GLuint& vao = *pVao;
      GLuint& vbo = *pVbo;

      // Generate and bind current VAO (Vertex Array Object)
      if(vao == 0){ glGenVertexArrays(1, &vao); }
      glBindVertexArray(vao);

      // Generate and bind current VBO (Vertex Buffer Object)
      glGenBuffers(1, &vbo);
      glBindBuffer(GL_ARRAY_BUFFER, vbo);

      // Upload data to current VBO buffer in GPU
      glBufferData(GL_ARRAY_BUFFER, sizeBuffer, pBufffer, GL_STATIC_DRAW);

      glEnableVertexAttribArray(shader_attr);

      // Set data layout - how data will be interpreted.
      glVertexAttribPointer(shader_attr, size, type, GL_FALSE, 0, nullptr);

      // ------ Disable Global state set by this function -----//
      //
      // Unbind VAO
      glBindVertexArray(0);
      // Unbind VBO
      glBindBuffer(GL_ARRAY_BUFFER, vbo);
      // Disable current shader attribute
      glDisableVertexAttribArray(shader_attr);

  }
#+END_SRC

** 2D - Chart with orthographic projection

This sample applications draws a 2D chart by plotting two curves, the
functions f(x) and g(x). It is possible to adjust the range displayed
by the chart by setting xmin, xmax, ymin and ymax using the
console. By typing 'A' the chart is displayed adjusting the xmax or
ymax for preserving the aspect ratio. By typing 'B' the chart does not
preserve the aspect ratio and shows the full range xmin to xmax on the
X axis, and ymin to ymax on the Y axis.

  + f(x) = 100.0 - x^2
  + g(x) = 50 * sin(x / (2 * PI)) + 10

The significant features on this sample code are:

  + Orthographic projection transform which allows drawing using
    user-defined coordinates (world-space coordinates), instead of NDC
    normalized-device coordinates with range -1.0 to 1.0 on each axis.

  + Sending interleaved vertex attributes, namely, positions and
    colors, to GPU with a single call to glBufferData() and a single
    VBO - Vertex Buffer Object.

  + Usage of threads for allowing the user controlling the displayed
    range by typing in the console (command line).

 *Projection matrix*

The orthogonal projection matrix is used for allowing user-defined
coordinates instead of normalized coordinates in the range -1.0 to
1.0 on for each axis. The orthogonal matrix without aspect-ratio
compensation is generated by using the following call to glm::ortho.

#+BEGIN_SRC cpp
   glm::mat4 proj = ortho(xmin, xmax, ymin, ymax, zNear = +1.0, zFar = -1.0);
#+END_SRC

 *Algorithm for avoiding chart distortion*

Consider the following rations $u_x$ and $u_y$:

\begin{eqnarray*}
   u_x &=& \frac{w}{\Delta_x} = \frac{ w }{ x_{max} - x_{min} } \\
   u_y &=& \frac{h}{\Delta_y} = \frac{ h }{ y_{max} - y_{min} } \\
\end{eqnarray*}

Where:

  + w - screen width in pixels.

  + h - screen height in pixels.

  + $\Delta_x$ screen width in world-space coordinates.

  + $\Delta_y$ screen height in world-space coordinates.

  + $u_y$ - The ratio $u_x$ represents how many pixels (real screen lenght)
    each user-defined unit on world-space coordinate systems
    represents across the X axis.

  + $u_x$ - The ratio $u_y$, is similar to $u_x$, but it is applicable
    to Y axis.

For instance, if the screen has width of 500 pixels and height of 600
pixels and $x_{min} = -50$,  $x_{max} = +50$,  $y_{min} = -200$,
$y_{max} = +200$. The ratios $u_x$ and $u_y$ will be:

\begin{eqnarray*}
   \Delta_x &=& x_{max} - x_{min} = 100 \\
   \Delta_y &=& y_{max} - y_{min} = 400 \\

   u_x &=& \frac{500}{100} = 5   \quad \text{pixels per X axis units} \\
   u_y &=& \frac{600}{400} = 1.5 \quad \text{pixels per Y axis units} \\
\end{eqnarray*}

The result means that any curve, will appear distorted, for instance a
square with side of 10, will be shown on the screen with the width of
10 * ux = 50 pixels and the height of 10 * uy = 10 * 1.5 = 15
pixels. As a result, the square will appear as rectangle. To avoid any
distortion, the ratios $u_x$ and $u_y$ must be equal, which is
expressed as:

\begin{eqnarray*}
   u_x                         &=& u_y                         \\
  \frac{x_{max} - x_{min}}{w}  &=& \frac{y_{max} - y_{min}}{h} \\
\end{eqnarray*}

If the variables w, h, $x_{min}$ and $y_{min}$ are fixed. $x_{max}$ or
$y_{max}$ must be adjusted for making the ratios $u_x$ and $u_y$
equal and avoiding any distortion.

If the adjusting is choosen for $x_{max}$, then its new value will be:

\begin{equation}
   x_{max}' = x_{min} + \frac{w}{h}( y_{max} - y_{min} )
\end{equation}

If the adjusting is choosen for $y_{max}$, then its adjusted value is:

\begin{equation}
   y_{max}' = y_{min} + \frac{h}{w}( x_{max} - x_{min} )
\end{equation}

By applying the adjusting for the previous case for $x_{max}$, its
adjusted value becomes:

\begin{equation}
   x_{max}' = (-50) + \frac{500}{600}( 200 - (-200) ) \approx 283.333
\end{equation}

The ratios $u_x$, $u_y$ become:


\begin{eqnarray*}
   u_x' &=& \frac{ w }{ x_{max}' - x_{min} } = \frac{ 500 }{ 283.333 - (-50) } \approx 1.50  \\
   u_y' &=& \frac{ h }{ y_{max} - y_{min} }  = \frac{ 600 }{ 200 - (-200) }  = 1.50    \\
\end{eqnarray*}

As the ratios became equal, a square of side 10 will not appear
distorted as its width and lenght in pixels, became equal.

 *Screenshots*


[[file:images/opengl-chart-2.png][file:images/opengl-chart-2.png]]

[[file:images/opengl-chart-1.png][file:images/opengl-chart-1.png]]


  *Files and shaders code*

Vertex Shader:

#+BEGIN_SRC cpp
  #version 330 core

  layout ( location = 0) in vec2 position;
  layout ( location = 1) in vec3 color;

  // Forwarded to fragment shader
  out vec3 out_color;

  uniform mat4 u_model;  // Model matrix
  uniform mat4 u_proj;   // Projection matrix

  void main()
  {
      // vec4(position, 0.0, 1.0) means => vec4(x, y, z = 0.0, w =1.0)
      gl_Position = u_proj * u_model * vec4(position, 0.0, 1.0);
      // Forward to fragment shader
      out_color = color;
  }
#+END_SRC

Fragment Shader:

#+BEGIN_SRC cpp
  #version 330

  in vec3 out_color;

  void main()
  {
      // Set vertex colors
      gl_FragColor =  vec4(out_color, 1.0);
  }
#+END_SRC

File: _CMakeLists.txt_

#+BEGIN_SRC cmake
   cmake_minimum_required(VERSION 3.5)
   project(Draw2D-Chart-GLFW)

   set(CMAKE_CXX_STANDARD 17)
   set(CMAKE_CXX_STANDARD_REQUIRED ON)
   set(CMAKE_VERBOSE_MAKEFILE ON)

   #================ GLFW Settings  ===============#

   find_package(OpenGL REQUIRED)

   include(FetchContent)

   # Set GLFW Options before FectchContent_MakeAvailable
   set( GLFW_BUILD_EXAMPLES OFF CACHE BOOL  "GLFW lib only" )
   set( GLFW_BUILD_TESTS    OFF CACHE BOOL  "GLFW lib only" )
   set( GLFW_BUILD_DOCS     OFF CACHE BOOL  "GLFW lib only" )
   set( GLFW_BUILD_INSTALL  OFF CACHE BOOL  "GLFW lib only" )

   # Donwload GLFW library
   FetchContent_Declare(
     glfwlib
     URL   https://github.com/glfw/glfw/releases/download/3.3.2/glfw-3.3.2.zip
   )
   FetchContent_MakeAvailable(glfwlib)

   # Download GLM (OpenGL math library for matrix and vectors transformation)
   FetchContent_Declare(
     glm
     URL  https://github.com/g-truc/glm/archive/0.9.8.zip
   )
   FetchContent_MakeAvailable(glm)

   message( [DEBUG] " glm_SOURCE_DIR = ${glm_SOURCE_DIR} ")
   include_directories(${glm_SOURCE_DIR})

     # ======= TARGETS ===========================#

          add_executable( draw2d-chart  draw2d-chart.cpp   )
   target_link_libraries( draw2d-chart  glfw OpenGL::GL GLU )
#+END_SRC

File: _draw2d-chart.cpp_

#+BEGIN_SRC cpp
  #include <iostream>
  #include <vector>
  #include <array>
  #include <cmath>
  #include <cassert>
  #include <algorithm>
  #include <thread>

  // -------- OpenGL headers ---------//
  //
  #define GL_GLEXT_PROTOTYPES
  #define GL3_PROTOTYPES

  #include <GL/gl.h>
  #include <GLFW/glfw3.h>

  // #include <GL/glew.h>
  #include <GL/glu.h>
  // #include <GL/glut.h>

  // --------- OpenGL Math Library ------------//
  #include <glm/glm.hpp>
  #include <glm/gtc/matrix_transform.hpp>
  #include <glm/gtc/type_ptr.hpp>
  #include <glm/gtx/string_cast.hpp>
  #include <glm/gtc/matrix_access.hpp>

  struct Point2D { GLfloat x, y;  };
  struct ColorRGB{ GLfloat r, g, b; };

  struct Vertex2D
  {
      Point2D  position;
      ColorRGB color;
  };

  constexpr ColorRGB color_red    = { 1.0, 0.0, 0.0 };
  constexpr ColorRGB color_green  = { 0.0, 1.0, 0.0 };
  constexpr ColorRGB color_blue   = { 0.0, 0.0, 1.0 };
  constexpr ColorRGB color_white  = { 1.0, 1.0, 1.0 };
  constexpr ColorRGB color_yellow = { 1.0, 1.0, 0.0 };

  // ------------------------------------//
  // Shader code at the end of file.
  extern const char*  code_vertex_shader;
  extern const char*  code_frag_shader;

  void        compile_shader(GLuint m_program, const char* code, GLenum type);
  GLFWwindow* make_glfwWindow(int  width, int height, const char* title);

  // Upload vertices position and colors to GPU in a single call.
  void send_vertices( GLuint* pVao, GLuint* pVbo
                    , GLint attr_position
                    , GLint attr_color
                    , std::vector<Vertex2D> const& vertices );

  // using resize_callback_t = void (*) (GLFWwindow* window, int width, int height);
  using FuncResizeCallback = std::function<void (GLFWwindow* window, int width, int height)>;
  FuncResizeCallback resize_callback;


  /*  Ajust orthogonal projection matrix for keeping the aspect ratio.
   ,*  If the flag is false, the range (Xmin, Xmax), (Ymax, Ymin) is not adjusted.
   ,*******************************************************************/
  void adjust_window_range( GLFWwindow* window
                          , GLint uniform_projection_id
                          , bool flag
                          , float xmin, float xmax, float ymin, float ymax);


  template<typename Function>
  auto make_curve(  ColorRGB color, float xmin, float xmax
                  , size_t npoints, Function&& fun) -> std::vector<Vertex2D>
  {
      std::vector<Vertex2D> curve;
      // Reserve pre-allocated space in order to avoid multiple allocations
      // via vector.push_back(Item)
      curve.reserve(npoints);
      float x = xmin;
      float y = 0.0;
      float dx = (xmax - xmin) / npoints;

      for(size_t n = 0; n < npoints; n++)
      {
          y = fun(x);
          curve.push_back( Vertex2D{ Point2D{x, y}, color } );
          // std::fprintf(stderr, " [TRACE] x = %f ; y = %f \n", x, y);
          x = x + dx;
      }
      return curve;
  }

  int main()
  {
      // Initialize GLFW
      if (!glfwInit()){ return -1; }
      const char* title = "2D Scientific Chart with Orthogonal projection.";
      GLFWwindow* window = make_glfwWindow(600, 400, title);

      // ========== Shader settings ==================//
      //
      GLuint prog = glCreateProgram();
      // Compile shader code
      compile_shader(prog, code_vertex_shader, GL_VERTEX_SHADER  ) ;
      compile_shader(prog, code_frag_shader,   GL_FRAGMENT_SHADER );
      glUseProgram(prog);
      // ------- Shader attribute locations -------//
      //
      const GLint attr_position = glGetAttribLocation(prog, "position");
      const GLint attr_color    = glGetAttribLocation(prog, "color");
      assert( attr_color >= 0 );
      // --------- Shader Uniform Variables -------//
      //
      const GLint u_proj  = glGetUniformLocation(prog, "u_proj");
      const GLint u_model = glGetUniformLocation(prog, "u_model");
      // Note: The error checking for other uniforms are missing
      // for breviety purposes.
      assert( u_model >= 0 );

      // ------ Default values for uniform variables ----------//
      //
      const auto matrix_identity = glm::mat4(1.0);
      glUniformMatrix4fv(u_model, 1, GL_FALSE, glm::value_ptr(matrix_identity));
      glUniformMatrix4fv(u_proj,  1, GL_FALSE, glm::value_ptr(matrix_identity));

      // Forward callback to global variable.
      glfwSetFramebufferSizeCallback(window, [](GLFWwindow* window, int width, int height){
           resize_callback(window, width, height);
      });

       glm::mat4 model_rectangle = glm::mat4(1.0);
      model_rectangle = glm::translate(model_rectangle, glm::vec3(5.0, 4.0, 0.0));

      // ==== O R T H O G O N A L - P R O J E C T I O N =====//
      //
      float xmin = -20.0f, xmax = +10.0f;
      float ymin = -5.0f, ymax  = +100.0f;
      float zNear = +1.0, zFar = -1.0;

      bool aspect_ratio_flag = true;
      adjust_window_range(window, u_proj, aspect_ratio_flag, xmin, xmax, ymin, ymax);

      resize_callback = [&](GLFWwindow* window, int width, int height)
      {
          // std::fprintf(stderr, " [TRACE] Window resize to width = %d ; height = %d \n", width, height);
          // Resize windo view port to the whole window
          glViewport(0, 0, width, height);
      };

      // --- Console loop thread callback =>> Allows user to adjust chart range
      // using console.
      auto console_thread = std::thread( [&]{
          // glfwMakeContextCurrent(window);
          for(;;){
              std::cout << " Enter chart range (xmin, xmax, ymin, ymax): ";
              std::cin >> xmin >> xmax >> ymin >> ymax;
              // Send an empty envent to the render loop for redrawing the window.
              // It makes the subroutine glfwWaitEvents() return to calling code.
              glfwPostEmptyEvent();

          }
      });

      console_thread.detach();

      // ======= U P L O A D - T O - G P U ===========//
      //

  #if 1
      // X, Y axis lines at point (0, 0)
      auto chart_axis = std::vector<Vertex2D> {
              // X axis Line - color green
              { { -500.0, 0}, color_green }, { {500.0, 0}, color_green }
              // Y axis Line  - color blue
            , { {0, -500.0}, color_blue },  { {0, 500.0}, color_blue }
      };
      GLuint vao_axis = 0, vbo_axis = 0;
      send_vertices(&vao_axis, &vbo_axis, attr_position, attr_color, chart_axis);
  #endif

      auto curve = std::vector<Vertex2D>{};

      GLuint vao_quadratic_curve = 0;
      GLuint vbo_quadratic_curve = 0;
      curve = make_curve( color_red, -20.0, 20.0, 500
                        , [](float x){ return 100.0 - x * x;  });
      GLint quadratic_curve_points = curve.size();
      send_vertices( &vao_quadratic_curve, &vbo_quadratic_curve
                   , attr_position, attr_color, curve );

      // Sine curve f(x) = 25 * sin(x / (2·PI) ) + + 60.0;
      GLuint vao_sine_curve = 0;
      GLuint vbo_sine_curve = 0;
      constexpr float PI_2 = 2.0 * 3.141592653589;
      curve = make_curve(  color_yellow, -50.0, 200.0, 500
                       , [](float x){ return 25.0 * sin(x / PI_2) + 60.0;  } );
      GLint sine_curve_points = curve.size();
      send_vertices( &vao_sine_curve, &vbo_sine_curve
                   , attr_position, attr_color, curve );


      //  ======= R E N D E R  - L O O P ============//
      //                                             //
      while ( !glfwWindowShouldClose(window) )
      {
          glClear(GL_COLOR_BUFFER_BIT  | GL_DEPTH_BUFFER_BIT);

          // ====== BEGIN RENDERING ============//

              // Adjust orthogonal projection and range of coordinates shown in the window.
              adjust_window_range(window, u_proj, aspect_ratio_flag, xmin, xmax, ymin, ymax);

              // Draw lines for X and Y axis
              //-------------------------------//
              glUniformMatrix4fv(u_model, 1, GL_FALSE, glm::value_ptr(matrix_identity));
              glBindVertexArray(vao_axis);
              glDrawArrays(GL_LINES, 0, 4);

              // Draw curve quadratic curve f(x) = 100.0 - x^2
              //-------------------------------//
              glUniformMatrix4fv(u_model, 1, GL_FALSE, glm::value_ptr(matrix_identity));
              glBindVertexArray(vao_quadratic_curve);
              glDrawArrays(GL_POINTS, 0, quadratic_curve_points);

              // Draw sine curve f(x) = 50 * sin(x) + 10.0;
              //-------------------------------//
              glUniformMatrix4fv(u_model, 1, GL_FALSE, glm::value_ptr(matrix_identity));
              glBindVertexArray(vao_sine_curve);
              glDrawArrays(GL_LINE_STRIP, 0, sine_curve_points);


           // ====== END RENDERING ==============//

          // Swap front and back buffers
          glfwSwapBuffers(window);
          // Wait for events - blocks current thread until some event arrives.
          glfwWaitEvents();

          // ====== PROCESS EVENTS =============//

            // std::fprintf(stderr, " [TRACE] Waiting events. \n");

          if( glfwGetKey(window, 'A') == GLFW_PRESS ){  aspect_ratio_flag = true;  }
          if( glfwGetKey(window, 'B') == GLFW_PRESS ){  aspect_ratio_flag = false; }

      } // --- End of render loop --- //

      return 0;
  }

  // --------- S H A D E R S --------------------------------//

  // Minimal vertex shader =>> Runs on the GPU and processes each vertex.
  const char* code_vertex_shader = R"(
      #version 330 core

      layout ( location = 0) in vec2 position;
      layout ( location = 1) in vec3 color;

      // Forwarded to fragment shader
      out vec3 out_color;

      uniform mat4 u_model;  // Model matrix
      uniform mat4 u_proj;   // Projection matrix

      void main()
      {
          // vec4(position, 0.0, 1.0) means => vec4(x, y, z = 0.0, w =1.0)
          gl_Position = u_proj * u_model * vec4(position, 0.0, 1.0);
          // Forward to fragment shader
          out_color = color;
      }

  )";

  // Fragment shader source code
  const char* code_frag_shader = R"(
      #version 330

      in vec3 out_color;

      void main()
      {
          // Set vertex colors
          gl_FragColor =  vec4(out_color, 1.0);
      }
  )";


  // --------- I M P L E M E N T A T I O N S -----------------//

  GLFWwindow*
  make_glfwWindow(int  width, int height, const char* title)
  {

      glfwSetErrorCallback([](int error, const char* description)
                           { std::fprintf( stderr, " [GLFW ERROR] Error = %d ; Description = %s \n"
                                          , error, description);
                           });

      GLFWwindow* window = glfwCreateWindow(width, height, title, NULL, NULL);
      assert( window != nullptr && "Failed  to create Window");

      // OpenGL context
      glfwMakeContextCurrent(window);
      // Pain whole screen as black - dark screen colors are better
      // for avoding eye strain due long hours staring on monitor.
      glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
      // Set - OpenGL Core Profile - version 3.3
      glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
      glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
      glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);

      // Make the window the topmost (Always on top)
      glfwWindowHint(GLFW_FLOATING, GLFW_TRUE);

      glEnable(GL_COLOR_MATERIAL);
      glEnable(GL_DEPTH_TEST);
      glEnable(GL_BLEND);

      return window;
  }


  void compile_shader(GLuint m_program, const char* code, GLenum type)
  {
      GLint shader_id = glCreateShader( type );
      glShaderSource(shader_id, 1, &code, nullptr);
      glCompileShader(shader_id);

      GLint is_compiled = GL_FALSE;
      // Check shader compilation result
      glGetShaderiv(shader_id, GL_COMPILE_STATUS, &is_compiled);

      // If there is any shader compilation result,
      // print the error message.
      if( is_compiled == GL_FALSE)
      {
          std::cerr << " [SHADER ERROR] =>> Abort execution. " << '\n';
          // Abort the exection of current process.
          std::abort();
      }

      glAttachShader(m_program, shader_id);
      glDeleteShader(shader_id);
      glLinkProgram(m_program);
      GLint link_status = GL_FALSE;
      glGetProgramiv(m_program, GL_LINK_STATUS, &link_status);
      assert( link_status != GL_FALSE );
      // glUseProgram(m_program);
  }


  // Upload vertices from main memory to GPU memory.
  // Vertices position and colors are uploaded in a single call.
  void send_vertices( GLuint* pVao, GLuint* pVbo
                    , GLint attr_position
                    , GLint attr_color
                    , std::vector<Vertex2D> const& vertices )
  {
      assert(pVao != nullptr);
      assert(pVbo != nullptr);
      GLuint& vao = *pVao;
      GLuint& vbo = *pVbo;
      // Generate and bind current VAO (Vertex Array Object)
      if(vao == 0){ glGenVertexArrays(1, &vao); }
      glBindVertexArray(vao);
      // Generate and bind current VBO (Vertex Buffer Object)
      glGenBuffers(1, &vbo);
      glBindBuffer(GL_ARRAY_BUFFER, vbo);
      // Upload data to current VBO buffer in GPU
      glBufferData(GL_ARRAY_BUFFER, vertices.size() * sizeof(Vertex2D)
                  , vertices.data(), GL_STATIC_DRAW);

      glEnableVertexAttribArray(attr_position);
      glEnableVertexAttribArray(attr_color);

      static_assert( sizeof(Vertex2D) == 5 * sizeof(float)
                   ,"Vertex2D size assumption does not hold." );

      static_assert( sizeof(Point2D) == offsetof(Vertex2D, color)
                   , "Invalid offsetof() assumption");

      // Set data layout - how data will be interpreted.
      // => Each vertex has 2 coordinates.
      glVertexAttribPointer( attr_position     // Position vertex shader attribute
                            , 2                // Each position has 2 coordinates (X, Y)
                            , GL_FLOAT         // Type of each coordinate
                            , GL_FALSE
                            , sizeof(Vertex2D) // Offset to next coordinates (5 floats in this case)
                            , nullptr
                            );

      glVertexAttribPointer( attr_color
                             // Each color has 3 components R, G, B
                            , 3
                             // Type of each color coordinate
                            , GL_FLOAT
                            , GL_FALSE
                             // Offset to next coordinates (next row)
                            , sizeof(Vertex2D)
                             // Offset to color member variable in class Vertex2D
                            , reinterpret_cast<void*>( offsetof(Vertex2D, color) )
                            );

      // ------ Disable Global state set by this function -----//
      // Unbind VAO
      glBindVertexArray(0);
      // Unbind VBO
      glBindBuffer(GL_ARRAY_BUFFER, vbo);
      // Disable current shader attribute
      glDisableVertexAttribArray(attr_position);
      glDisableVertexAttribArray(attr_color);
  }

  void adjust_window_range( GLFWwindow* window
                          , GLint uniform_projection_id
                          , bool flag
                          , float xmin, float xmax, float ymin, float ymax)
  {
      constexpr float zFar  = -1.0f;
      constexpr float zNear = +1.0f;

      if(flag == false)
      {
          glm::mat4 proj_1 = glm::ortho(xmin, xmax, ymin, ymax, zNear, zFar);
          glUniformMatrix4fv(uniform_projection_id, 1, GL_FALSE, glm::value_ptr(proj_1));
          return;
      }

      int width, height;
      glfwGetWindowSize(window, &width, &height);
      float k_aspect = static_cast<float>(width) / static_cast<float>(height);
      float dx = xmax - xmin;
      float dy = ymax - ymin;

      if(dx > dy){
          ymax =  ymin + (1.0f / k_aspect ) * dx;
          // std::fprintf(stderr, " [TRACE] Adjust range => ymax = %f \n", ymax);
      } else {
          xmax = xmin + k_aspect * dy;
          // std::fprintf(stderr, " [TRACE] Adjust range => xmax = %f \n", ymax);
      }

      dx = xmax - xmin;
      dy = ymax - ymin;
      float ux = width / dx;
      float uy = height / dy;

      // std::fprintf(stderr, " [TRACE] ux = %f ; uy = %f \n", ux, uy);

      glm::mat4 proj_2 = glm::ortho(xmin, xmax, ymin, ymax, zNear, zFar);
      glUniformMatrix4fv(uniform_projection_id, 1, GL_FALSE, glm::value_ptr(proj_2));
  }

#+END_SRC
** 2D - IBO - Index Buffer Object 

This code uses OpenGL IBO (Index Buffer Objects) for drawing triangles
and lines OpenGL primitives. The IBO is useful for drawing complex
objects without repeating vertices, which saves memory and bandwith
and increases performance. 

  *Screenshot* 

#+CAPTION: Triangles IBO - Index Buffer Object 
[[file:images/opengl-ibo-triangles.png][file:images/opengl-ibo-triangles.png]] 


  *Code Highlights* 

Vertices and colors data structures: 

#+BEGIN_SRC cpp 
   struct Point2D  { GLfloat x, y;    };
   struct ColorRGB { GLfloat r, g, b; };
   struct Vertex2D { Point2D  position; ColorRGB color; };

   using VertexArray = std::vector<Vertex2D>;
   using IndexArray  = std::vector<GLuint>;
#+END_SRC

The next blocks defines the triangles vertices and the connection
between those vertices. Each three indices in the 'triangles_indices'
variables represents a triangle. Without using IBO (Index Buffer
Object), there would be duplicated vertices, then the content of array
'vertices' would be [V0, V1, V2, V1, V3, V4, V3, V5, V6, V2, V7, V5], 


#+BEGIN_SRC cpp 
    // Triangle side 
    float a = 1.0 / 5.0; 
    // Triangle height 
    float h = sqrt(3.0) * a;
    
    auto vertices = VertexArray {
          Vertex2D{ { 0,       0  }, color_green   } // V0 - vertex 0 
        , Vertex2D{ { 2 * a,   0  }, color_green   } // V1 
        , Vertex2D{ { a,       h  }, color_green   } // V2 

        , Vertex2D{ { 3 * a,   h  }, color_blue    } // V3
        , Vertex2D{ { 4 * a,   0  }, color_blue    } // V4

        , Vertex2D{ { 2 * a, 2 * h }, color_red    } // V5
        , Vertex2D{ { 4 * a, 2 * h }, color_red    } // V6
        , Vertex2D{ {     0, 2 * h }, color_yellow } // V7 - vertex 7 
    };
    
    // Indices for GL_TRIANGLE drawing primitive. 
    auto triangles_indices = IndexArray {
          0, 1, 2  // Draw triangle with vertices V0, V1, V2 (Triangle A)
        , 1, 3, 4  // Draw triangle with vertices V1, V3, V4 (Triangle B)
        , 3, 5, 6  // Draw triangle with vertices V3, V5, V6 (Triangle C)
        , 2, 7, 5  // Draw triangle with vertices V2, V7, V5 (Triangle D)
    };
#+END_SRC

This index array defines the connections between vertices for
GL_LINES primitives, each 2 indices represents a line. 

#+BEGIN_SRC cpp 
   // Indices for GL_LINES drawing primitive 
    auto lines_indices = IndexArray {
          0, 1 // Line connecting vertices V0 and V1 
        , 0, 2 // Line connecting vertices V0 and V2 
        , 1, 2 // Line connecting vertices V1 and V2 
        
        , 2, 7 
        , 5, 7 
        , 2, 5 

        , 1, 3
        , 1, 4
        , 3, 4

        , 3, 5
        , 3, 6
        , 5, 6
    };
#+END_SRC

In the following code highlight, the vertices indices and data are
sent to the GPU using the subroutines send_vertices() and
send_indices(). These functions uses the following parameters: vao
(Vertex Array Object); vbo (Vertex Buffer Object) and ibo (Index
Buffer Objects), which are supposed to be allocated by calling
code. 

#+BEGIN_SRC cpp 
    // ------- Upload data for drawing traingle primitives --------//
    //
    GLuint vao_triangles = 0, vbo_triangles = 0, ibo_triangles = 0;    

    send_vertices( &vao_triangles, &vbo_triangles
                 , attr_position, attr_color, vertices);

    send_indices(&vao_triangles, &ibo_triangles, triangles_indices);


    // -------- Upload data for drawing lines primitives ----------// 
    //
    GLuint vao_lines = 0, vbo_lines = 0, ibo_lines = 0;  
    send_vertices( &vao_lines, &vbo_lines
                 , attr_position, attr_color, vertices);     
    send_indices(&vao_lines, &ibo_lines, lines_indices);
#+END_SRC

At the rendering loop. The OpenGL glDrawElements() subroutine is used
,instead of glDrawArrays(), for drawing triangles and lines
primitives. This subroutine uses the vertices indices instead of using
the vertices ordering. For instance, if glDrawArrays() was used, each
three vertices from the current bound VBO would be used for drawing a
triangle.

#+BEGIN_SRC cpp 
   // ====== BEGIN RENDERING ============//

       // ---- Draw triangles primitives using IBO -------// 
       glUniformMatrix4fv(u_model, 1, GL_FALSE, glm::value_ptr(model_triangle_primtives));
       glBindVertexArray(vao_triangles);
       // Draw OpenGL primitives between vertices designated by their
       // IBO - Index Buffer Object.         
       glDrawElements(GL_TRIANGLES, triangles_indices.size() , GL_UNSIGNED_INT, nullptr);

       // ----- Draw lines using IBO -----------------------//
       glUniformMatrix4fv(u_model, 1, GL_FALSE, glm::value_ptr(model_lines_primitives));
       glBindVertexArray(vao_lines);
       glDrawElements(GL_LINES, lines_indices.size() , GL_UNSIGNED_INT, nullptr);            

   // ====== END RENDERING ==============//

#+END_SRC

  *Files / Sources* 

File: _CMakeLists.txt_

#+BEGIN_SRC cmake
   cmake_minimum_required(VERSION 3.5)
   project(Draw2D-Chart-GLFW)

   set(CMAKE_CXX_STANDARD 17)
   set(CMAKE_CXX_STANDARD_REQUIRED ON)
   set(CMAKE_VERBOSE_MAKEFILE ON)

   #================ GLFW Settings  ===============#

   find_package(OpenGL REQUIRED)

   include(FetchContent)

   # Set GLFW Options before FectchContent_MakeAvailable
   set( GLFW_BUILD_EXAMPLES OFF CACHE BOOL  "GLFW lib only" )
   set( GLFW_BUILD_TESTS    OFF CACHE BOOL  "GLFW lib only" )
   set( GLFW_BUILD_DOCS     OFF CACHE BOOL  "GLFW lib only" )
   set( GLFW_BUILD_INSTALL  OFF CACHE BOOL  "GLFW lib only" )

   # Donwload GLFW library
   FetchContent_Declare(
     glfwlib
     URL   https://github.com/glfw/glfw/releases/download/3.3.2/glfw-3.3.2.zip
   )
   FetchContent_MakeAvailable(glfwlib)

   # Download GLM (OpenGL math library for matrix and vectors transformation)
   FetchContent_Declare(
     glm
     URL  https://github.com/g-truc/glm/archive/0.9.8.zip
   )
   FetchContent_MakeAvailable(glm)

   message( [DEBUG] " glm_SOURCE_DIR = ${glm_SOURCE_DIR} ")
   include_directories(${glm_SOURCE_DIR})

     # ======= TARGETS ===========================#

          add_executable( draw2d-ibo-triangles  draw2d-ibo-triangles.cpp   )
   target_link_libraries( draw2d-ibo-triangles  glfw OpenGL::GL GLU )
#+END_SRC

File: _draw2d-ibo-triangles.cpp_ 

#+BEGIN_SRC cpp 
  #include <iostream>
  #include <vector> 
  #include <array>
  #include <cmath>
  #include <cassert>

  // -------- OpenGL headers ---------//
  //
  #define GL_GLEXT_PROTOTYPES 1 // Necessary for OpenGL >= 3.0 functions
  #define GL3_PROTOTYPES      1 // Necessary for OpenGL >= 3.0 functions

  #include <GL/gl.h>
  #include <GLFW/glfw3.h>

  #include <GL/gl.h>
  #include <GLFW/glfw3.h>

  // #include <GL/glew.h>
  #include <GL/glu.h>

  // --------- OpenGL Math Library ------------//
  #include <glm/glm.hpp>
  #include <glm/gtc/matrix_transform.hpp>
  #include <glm/gtc/type_ptr.hpp>
  #include <glm/gtx/string_cast.hpp>
  #include <glm/gtc/matrix_access.hpp>

  struct Point2D  { GLfloat x, y;    };
  struct ColorRGB { GLfloat r, g, b; };
  struct Vertex2D { Point2D  position; ColorRGB color; };

  extern const char*  code_vertex_shader;
  extern const char*  code_frag_shader;

  void        compile_shader(GLuint m_program, const char* code, GLenum type);
  GLFWwindow* make_glfwWindow(int  width, int height, const char* title);

  // Send vertices (position and color of each vertex to GPU memory)
  void send_vertices( GLuint* pVao, GLuint* pVbo
                    , GLint attr_position
                    , GLint attr_color 
                    , std::vector<Vertex2D> const& vertices );

  // Send vertices indices to GPU memory. 
  void send_indices(GLuint* pVao, GLuint* pVbi
                  , std::vector<GLuint> const& indices);

  constexpr ColorRGB color_red    = { 1.0, 0.0, 0.0 };
  constexpr ColorRGB color_green  = { 0.0, 1.0, 0.0 };        
  constexpr ColorRGB color_blue   = { 0.0, 0.0, 1.0 };
  constexpr ColorRGB color_white  = { 1.0, 1.0, 1.0 };
  constexpr ColorRGB color_yellow = { 1.0, 1.0, 0.0 };

  int main(int argc, char** argv)
  {
      if (!glfwInit()){ return -1; }

      /* Create a windowed mode window and its OpenGL context */
      GLFWwindow* window = make_glfwWindow(640, 480, "Draw 2D - IBO - Index Buffer Object");

      // ======= S H A D E R - C O M P I L A T I O N ==========// 
      // 
      GLuint prog = glCreateProgram();
      // Compile shader code 
      compile_shader(prog, code_vertex_shader, GL_VERTEX_SHADER  ) ;    
      compile_shader(prog, code_frag_shader,   GL_FRAGMENT_SHADER );
      glUseProgram(prog);    
      // ------- Shader attribute locations -------//
      const GLint attr_position = glGetAttribLocation(prog, "position");
      const GLint attr_color    = glGetAttribLocation(prog, "color");
      assert( attr_position >= 0 );
      assert( attr_color >= 0 );
      // --------- Shader Uniform Variables -------//
      const GLint u_proj  = glGetUniformLocation(prog, "u_proj");
      const GLint u_model = glGetUniformLocation(prog, "u_model");
      assert(u_proj >= 0);
      assert(u_model >= 0);

      // ------ Default values for uniform variables ----------//
      const auto matrix_identity = glm::mat4(1.0);
      glUniformMatrix4fv(u_model, 1, GL_FALSE, glm::value_ptr(matrix_identity));
      glUniformMatrix4fv(u_proj,  1, GL_FALSE, glm::value_ptr(matrix_identity));

    
      //  ======= U P L O A D - T O - G P U =====================//
      //     

      using VertexArray = std::vector<Vertex2D>;
      using IndexArray = std::vector<GLuint>;

      // Triangle side 
      float a = 1.0 / 5.0; 
      // Triangle height 
      float h = sqrt(3.0) * a;
    
      auto vertices = VertexArray {
            Vertex2D{ { 0,       0  }, color_green   } // V0 - vertex 0 
          , Vertex2D{ { 2 * a,   0  }, color_green   } // V1 
          , Vertex2D{ { a,       h  }, color_green   } // V2 

          , Vertex2D{ { 3 * a,   h  }, color_blue    } // V3
          , Vertex2D{ { 4 * a,   0  }, color_blue    } // V4

          , Vertex2D{ { 2 * a, 2 * h }, color_red    } // V5
          , Vertex2D{ { 4 * a, 2 * h }, color_red    } // V6
          , Vertex2D{ {     0, 2 * h }, color_yellow } // V7 - vertex 7 
      };
    
      // Indices for GL_TRIANGLE drawing primitive. 
      auto triangles_indices = IndexArray {
            0, 1, 2  // Draw triangle with vertices V0, V1, V2 (Triangle A)
          , 1, 3, 4  // Draw triangle with vertices V1, V3, V4 (Triangle B)
          , 3, 5, 6  // Draw triangle with vertices V3, V5, V6 (Triangle C)
          , 2, 7, 5  // Draw triangle with vertices V2, V7, V5 (Triangle D)
      };

      // Indices for GL_LINES drawing primitive 
      auto lines_indices = IndexArray {
            0, 1 // Line connecting vertices V0 and V1 
          , 0, 2 // Line connecting vertices V0 and V2 
          , 1, 2 // Line connecting vertices V1 and V2 
        
          , 2, 7 
          , 5, 7 
          , 2, 5 

          , 1, 3
          , 1, 4
          , 3, 4

          , 3, 5
          , 3, 6
          , 5, 6
      };

      // ------- Upload data for drawing traingle primitives --------//
      //
      GLuint vao_triangles = 0, vbo_triangles = 0, ibo_triangles = 0;    

      send_vertices( &vao_triangles, &vbo_triangles
                   , attr_position, attr_color, vertices);

      send_indices(&vao_triangles, &ibo_triangles, triangles_indices);


      // -------- Upload data for drawing lines primitives ----------// 
      //
      GLuint vao_lines = 0, vbo_lines = 0, ibo_lines = 0;  
      send_vertices( &vao_lines, &vbo_lines
                   , attr_position, attr_color, vertices);     
      send_indices(&vao_lines, &ibo_lines, lines_indices);

      // ==== Model matrices for translating drawings =======//
      //      
      glm::mat4 model_triangle_primtives = matrix_identity;
      glm::mat4 model_lines_primitives = matrix_identity;   
      // Translate triangles to point (x = -0.7, y = -0.8, z = 0.0)
      model_triangle_primtives = glm::translate(model_triangle_primtives, glm::vec3(-0.7, -0.8, 0.0));

      //  ======= R E N D E R  - L O O P ===================//
      //                                                    //
      while ( !glfwWindowShouldClose(window) )
      {
          glClear(GL_COLOR_BUFFER_BIT  | GL_DEPTH_BUFFER_BIT);

          // ====== BEGIN RENDERING ============//
        
              // ---- Draw triangles primitives using IBO -------// 
              glUniformMatrix4fv(u_model, 1, GL_FALSE, glm::value_ptr(model_triangle_primtives));
              glBindVertexArray(vao_triangles);
              // Draw OpenGL primitives between vertices designated by their
              // IBO - Index Buffer Object.         
              glDrawElements(GL_TRIANGLES, triangles_indices.size() , GL_UNSIGNED_INT, nullptr);
            
              // ----- Draw lines using IBO -----------------------//
              glUniformMatrix4fv(u_model, 1, GL_FALSE, glm::value_ptr(model_lines_primitives));
              glBindVertexArray(vao_lines);
              glDrawElements(GL_LINES, lines_indices.size() , GL_UNSIGNED_INT, nullptr);            

          // ====== END RENDERING ==============//

          glfwSwapBuffers(window);
          glfwPollEvents();
      }

      glfwTerminate();
      return 0;
  }

  // ---------- S H A D E R S - C O D E  ---------------------// 

  // Minimal vertex shader =>> Runs on the GPU and processes each vertex.
  const char* code_vertex_shader = R"(    
      #version 330 core 

      layout ( location = 0) in vec2 position;
      layout ( location = 1) in vec3 color;   
    
      // Forwarded to fragment shader 
      out vec3 out_color;
    
      uniform mat4 u_model;  // Model matrix 
      uniform mat4 u_proj;   // Projection matrix 

      void main()
      {
          // vec4(position, 0.0, 1.0) means => vec4(x, y, z = 0.0, w =1.0)
          gl_Position = u_proj * u_model * vec4(position, 0.0, 1.0);
        
          // Forward to fragment shader 
          out_color = color; 
      }

  )";

  // Fragment shader source code 
  const char* code_frag_shader = R"(
      #version 330 

      in vec3 out_color;

      void main()
      {
          // Set vertex colors
          gl_FragColor =  vec4(out_color, 1.0);
      }
  )";

  // --------- I M P L E M E N T A T I O N S -----------------//

  GLFWwindow* 
  make_glfwWindow(int  width, int height, const char* title)
  {

      glfwSetErrorCallback([](int error, const char* description)
                           { std::fprintf( stderr, " [GLFW ERROR] Error = %d ; Description = %s \n"
                                          , error, description);
                           });

      GLFWwindow* window = glfwCreateWindow(width, height, title, NULL, NULL);
      assert( window != nullptr && "Failed  to create Window");

      // OpenGL context 
      glfwMakeContextCurrent(window);
      // Set window color 
      glClearColor(0.0f, 0.5f, 3.0f, 1.0f);
      // Set - OpenGL Core Profile - version 3.3
      glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
      glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
      glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);

      // Make the window the topmost (Always on top)
      glfwWindowHint(GLFW_FLOATING, GLFW_TRUE);

      glEnable(GL_COLOR_MATERIAL);
      glEnable(GL_DEPTH_TEST);
      glEnable(GL_BLEND);

      return window;
  }


  void compile_shader(GLuint m_program, const char* code, GLenum type)
  {
      GLint shader_id = glCreateShader( type );
      glShaderSource(shader_id, 1, &code, nullptr);
      glCompileShader(shader_id);
    
      GLint is_compiled = GL_FALSE;
      // Check shader compilation result 
      glGetShaderiv(shader_id, GL_COMPILE_STATUS, &is_compiled);

      // If there is any shader compilation result, 
      // print the error message.
      if( is_compiled == GL_FALSE)
      {
          std::cerr << " [SHADER ERROR] =>> Abort execution. " << '\n';
          // Abort the exection of current process. 
          std::abort();
      }          

      glAttachShader(m_program, shader_id);   
      glDeleteShader(shader_id);
      glLinkProgram(m_program);
      GLint link_status = GL_FALSE;
      glGetProgramiv(m_program, GL_LINK_STATUS, &link_status);
      assert( link_status != GL_FALSE );   
      // glUseProgram(m_program);         
  }

  // Upload vertices from main memory to GPU memory.
  // Vertices position and colors are uploaded in a single call.
  void send_vertices( GLuint* pVao, GLuint* pVbo
                    , GLint attr_position
                    , GLint attr_color 
                    , std::vector<Vertex2D> const& vertices )
  {
      assert(pVao != nullptr);
      assert(pVbo != nullptr);
      GLuint& vao = *pVao;
      GLuint& vbo = *pVbo;
      // Generate and bind current VAO (Vertex Array Object)
      if(vao == 0){ glGenVertexArrays(1, &vao); }
      glBindVertexArray(vao);
      // Generate and bind current VBO (Vertex Buffer Object)
      glGenBuffers(1, &vbo);
      glBindBuffer(GL_ARRAY_BUFFER, vbo);
      // Upload data to current VBO buffer in GPU 
      glBufferData(GL_ARRAY_BUFFER, vertices.size() * sizeof(Vertex2D)
                  , vertices.data(), GL_STATIC_DRAW);   

      glEnableVertexAttribArray(attr_position);    
      glEnableVertexAttribArray(attr_color);    
   
      // Set data layout - how data will be interpreted.
      // => Each vertex has 2 coordinates. 
      glVertexAttribPointer( attr_position, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex2D), nullptr);

      glVertexAttribPointer( attr_color, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex2D)     
                             // Offset to color member variable in class Vertex2D
                            , reinterpret_cast<void*>( offsetof(Vertex2D, color) )
                            );

      // ------ Disable Global state set by this function -----//
      // Unbind VAO 
      glBindVertexArray(0);
      // Unbind VBO 
      glBindBuffer(GL_ARRAY_BUFFER, vbo);
      // Disable current shader attribute 
      glDisableVertexAttribArray(attr_position);
      glDisableVertexAttribArray(attr_color);
  }

  void send_indices(GLuint* pVao, GLuint* pVbi, std::vector<GLuint> const& indices)
  {
      GLuint& vao = *pVao;
      GLuint& vbi = *pVbi;
      glBindVertexArray(vao);
      // Generate index buffer object 
      glGenBuffers(1, &vbi); 
      // Bind this index buffer object - only one IBO can 
      // be bound at a time. (It is a global state!!)
      glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, vbi );
      // Upload indices to GPU - This VBI object is a handle 
      // (token, akin to file descriptor) that refers 
      // to the data sent to the GPU on the next line. 
      glBufferData( GL_ELEMENT_ARRAY_BUFFER, indices.size() * sizeof(GLuint)
                      , indices.data(), GL_STATIC_DRAW);    
      // ----------- Unset global state -------------//
      // 
      glBindVertexArray(0);
      glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, 0 );
  }
#+END_SRC
** 3D - Rotating cube/box

This sample application contains: two cubes; two grid planes for
visual reference; a coordinate axis for visual debugging and a camera
that always points to the origin. The camera has the following
controls: right arrow, rotates the camera's position in a
counterclockwise way around Y axis (red line); if left arrow is typed,
the camera position rotates in a clockwise way around Y axis (red
line). If up arrow is typed, the camera position moves in the positive
direction of Y axis. If down arrow is typed, the reverse
happens.

 *Screenshots*

[[file:images/opengl-draw3d-rotating-cube1.png][file:images/opengl-draw3d-rotating-cube1.png]]

[[file:images/opengl-draw3d-rotating-cube2.png][file:images/opengl-draw3d-rotating-cube2.png]]

  *Project Files*

Vertex Shader Code:

#+BEGIN_SRC cpp
   #version 330 core

   layout ( location = 0)  in vec3 position;
   layout ( location = 1) in vec3 color;
   out vec3 out_color;
   uniform mat4 u_model;       // Model matrix
   uniform mat4 u_view;        // Camera's view matrix
   uniform mat4 u_projection;  // Camera's projection matrix

   void main()
   {
       gl_Position = u_projection * u_view * u_model * vec4(position, 1.0);

       // Forward to fragment shader
       out_color = color; // vec3(0.56, 0.6, 0.0);
   }
#+END_SRC

Fragment Shader Code:

#+BEGIN_SRC cpp
   #version 330

   // This color comes from Vertex shader
   in vec3 out_color;

   void main()
   {
       // Set vertex colors
       gl_FragColor =  vec4(out_color, 1.0);
       // gl_FragColor = vec4(0.3, 0.6, 0.0, 1.0);
   }
#+END_SRC

File: CmakeLists.txt

#+BEGIN_SRC cmake 
  cmake_minimum_required(VERSION 3.5)
  project(GLFW_project)
  
  set(CMAKE_CXX_STANDARD 17)
  set(CMAKE_CXX_STANDARD_REQUIRED ON)
  set(CMAKE_VERBOSE_MAKEFILE ON)
  
  #================ GLFW Settings  ===============#
  
  find_package(OpenGL REQUIRED)
  
  include(FetchContent)
  
  # Set GLFW Options before FectchContent_MakeAvailable
  set( GLFW_BUILD_EXAMPLES OFF CACHE BOOL  "GLFW lib only" )
  set( GLFW_BUILD_TESTS    OFF CACHE BOOL  "GLFW lib only" )
  set( GLFW_BUILD_DOCS     OFF CACHE BOOL  "GLFW lib only" )
  set( GLFW_BUILD_INSTALL  OFF CACHE BOOL  "GLFW lib only" )
  
  # Donwload GLFW library
  FetchContent_Declare(
    glfwlib
    URL   https://github.com/glfw/glfw/releases/download/3.3.2/glfw-3.3.2.zip
  )
  FetchContent_MakeAvailable(glfwlib)
  
  # Download GLM (OpenGL math library for matrix and vectors transformation)
  FetchContent_Declare(
    glm
    URL  https://github.com/g-truc/glm/archive/0.9.8.zip
  )
  FetchContent_MakeAvailable(glm)
  
  # Download pre-compiled GLEW when building under Windows NT OS (x64)
  IF(WIN32)
     FetchContent_Declare(
        glew-release 
        URL  https://ufpr.dl.sourceforge.net/project/glew/glew/2.1.0/glew-2.1.0-win32.zip
     )
     FetchContent_MakeAvailable(glew-release)
     include_directories( ${glew-release_SOURCE_DIR}/include  ${glm_SOURCE_DIR} )        
     link_directories(  ${glew-release_SOURCE_DIR}/lib/Release/x64 )
  
     set( GLEW_LIB_PATH1 ${glew-release_SOURCE_DIR}/lib/Release/x64/glew32.lib )
     set( GLEW_LIB_PATH2 ${glew-release_SOURCE_DIR}/lib/Release/x64/glew32s.lib )
  ENDIF()
  
  
  message( [DEBUG] " glm_SOURCE_DIR = ${glm_SOURCE_DIR} ")
  include_directories(${glm_SOURCE_DIR})
  
  MACRO(ADD_OPENGL_APP target sources)
     add_executable( ${target} ${sources} )
     message([TRACE] " Add OpenGL executable = ${target} ")
  
     target_link_libraries( ${target} glfw
                                      OpenGL::GL
                                      ${GLEW_LIB_PATH1}
                                      ${GLEW_LIB_PATH2} )
  
      IF(MINGW)
          # Statically link against MINGW dependencies
          # for making easier to deploy on other machines. 
          target_link_options( ${target} PRIVATE                                 
                                   -static-libgcc
                                   -static-libstdc++
                                   -Wl,-Bstatic,--whole-archive -lwinpthread
                                   -Wl,--no-whole-archive                                    
                                   )
       ENDIF()       
  
  
       # Copy GLEW DLL shared library to same directory as the executable.                 
       IF(WIN32)                           
          add_custom_command(TARGET ${target} POST_BUILD 
                         COMMAND ${CMAKE_COMMAND} -E copy_if_different
                         "${glew-release_SOURCE_DIR}/bin/Release/x64/glew32.dll"              
                         $<TARGET_FILE_DIR:${target}>)
       ENDIF()                                       
  ENDMACRO()     
  
  
    # ======= TARGETS ===========================#
  
    ADD_OPENGL_APP( draw3d-cube draw3d-cube.cpp )
#+END_SRC

File: draw3d-cube.cpp

#+BEGIN_SRC cpp
  // Draw many colored triangles from a single VBO (Vertex Buffer Object)
  #include <iostream>
  #include <vector>
  #include <array>
  #include <cmath>
  #include <functional>

  // -------- OpenGL headers ---------//
  //
  #define GL_GLEXT_PROTOTYPES 1 // Necessary for OpenGL >= 3.0 functions
  #define GL3_PROTOTYPES      1 // Necessary for OpenGL >= 3.0 functions

  #if defined(_WIN32)
     #include <windows.h>
     #include <GL/glew.h>
  #endif

  #include <GL/gl.h>
  #include <GLFW/glfw3.h>

  // #include <GL/glew.h>
  #include <GL/glu.h>

  // --------- OpenGL Math Librar ------------//
  #include <glm/glm.hpp>
  #include <glm/gtc/matrix_transform.hpp>
  #include <glm/gtc/type_ptr.hpp>
  #include <glm/gtx/string_cast.hpp>

  // #define GLUT_ENABLED

  #if defined(GLUT_ENABLED)
      #include <GL/glut.h>
  #endif

  #define GL_CHECK(funcall)\
      do { \
          (funcall); \
          GLint error = glGetError();  \
          if(error == GL_NO_ERROR){ break; } \
          std::fprintf(stderr, " [OPENGL ERROR] Error code = %d ; line = %d ; call = '%s'  \n" \
                        , error, __LINE__, #funcall ); \
          abort(); \
      } while(0)

  GLFWwindow* make_glfwWindowi(int  width, int height, const char* title);

  // Compile some  shader
  void compile_shader(GLuint m_program, const char* code, GLenum type);

  // Send data from memory to GPU VBO memory
  void send_buffer(   GLuint* pVao        // Pointer to VAO (Vertex Array Object) - allocated by caller
                    , GLuint* pVbo        // Pointer to VBO (Vertex Buffer Object) - allocated by caller
                    , GLsizei sizeBuffer  // Total buffer size in bytes
                    , void*   pBufffer    // Pointer to buffer
                    , GLint   shader_attr // Shader attribute location id
                    , GLint   size        // Number of coordinates of a given vertex
                    , GLenum  type        // Type of each element coordinate
                    );

  // ------------ Basic Data Structures -----------//

  // Wrapper for 2D vertex coordinates
  struct Vertex3D{  GLfloat x, y, z;  };
  // Wrapper for RGB colors
  struct ColorRGB { GLfloat r, g, b; };

  struct Geometry {
      std::vector<Vertex3D> vertices;
      std::vector<ColorRGB> colors;
  };

  struct RenderObject {
      GLuint     u_model;     // Shader uniform variable for model matrix
      GLuint     vao;         // Vertex Array object
      glm::mat4  model;       // Object model matrix
      GLenum     draw_type;   // Draw type
      GLint      n_vertices;  // Number of vertices

      RenderObject(){ model = glm::mat4(1.0);  }
      // Explicit copy constructor
      RenderObject(const RenderObject&) = default;
      // Explicit copy assignment operator
      RenderObject& operator=(const RenderObject&) = default;

      void render()
      {
          glUniformMatrix4fv(u_model, 1, GL_FALSE, glm::value_ptr(model) );
          glBindVertexArray(vao);
          glDrawArrays(draw_type, 0, n_vertices);
      }
  };


  Geometry make_grid_geometry(size_t n_grid, float dx, const ColorRGB& color);
  Geometry make_cube_geometry(float w);

  constexpr ColorRGB color_red   = {1.0, 0.0, 0.0};
  constexpr ColorRGB color_green = {0.0, 1.0, 0.0};
  constexpr ColorRGB color_blue  = {0.0, 0.0, 1.0};
  constexpr ColorRGB color_yelllow    = { 0.80,  1.000, 0.100 };
  constexpr ColorRGB color_gray       = { 0.47,  0.390, 0.380 };
  constexpr ColorRGB color_dark_green = { 0.027, 0.392, 0.050 };
  constexpr ColorRGB color_dark_blue  = { 0.109, 0.066, 0.411 };

  // Send vertices to GPU
  void send_vertices( GLuint* pVao, GLuint* pVbo,  GLint shader_attr
                  ,   std::vector<Vertex3D>& vertices )
  {
      send_buffer(pVao, pVbo, sizeof(Vertex3D) * vertices.size(), vertices.data(), shader_attr, 3, GL_FLOAT );
  }

  // Send color coordinates to GPU
  void send_colors( GLuint* pVao, GLuint* pVbo,  GLint shader_attr
                  ,   std::vector<ColorRGB>& vertices )
  {
      send_buffer(pVao, pVbo, sizeof(Vertex3D) * vertices.size(), vertices.data(), shader_attr, 3, GL_FLOAT );
  }

  struct Camera{
      // Current location of camera in world coordinates
      glm::vec3 cam_eye  = { 2.0, 4.0, 5.0 };
      // Point to where the camera is looking at in world coordinates.
      glm::vec3 cam_targ = { 0.0, 0.0, 0.0  };
      // Current  camera up vector (orientation) - Y axis (default)
      glm::vec3 cam_up = { 0.0, 1.0, 0.0 };

      // Field of view
      float fov_angle = glm::radians(60.0);
      // Aspect ratio
      float aspect   = 1.0;
      float zFar     = 20.0;
      float zNear    = 0.1;

      // ID of shader's view uniform variable - for setting view matrix
      GLuint shader_uniform_view;
      // ID of shader's projection uniform variable for setting projection matrix.
      GLuint shader_uniform_proj;

      Camera(GLuint uniform_view, GLuint uniform_proj, float aspect):
          shader_uniform_view(uniform_view)
        , shader_uniform_proj(uniform_proj)
        , aspect(aspect)
      {  update_view();  }

      void update_view()
      {
          // Create View matrix that maps from world-space coordinates to
          // camera-space coordinates
          auto Tview = glm::lookAt(cam_eye, cam_targ, cam_up);

          // Create projection matrix coordinates that maps from
          // camera-space coordinates to NDC (Normalized Device Coordinates).
          auto Tproj = glm::perspective( fov_angle
                                       , aspect
                                       , zNear
                                       , zFar );
          // Set shader uniform variables.
          glUniformMatrix4fv(shader_uniform_view, 1, GL_FALSE, glm::value_ptr(Tview) );
          glUniformMatrix4fv(shader_uniform_proj, 1, GL_FALSE, glm::value_ptr(Tproj) );

      }

      // Rotate camera around Y axis
      void rotate_y(float angle)
      {
          float a = glm::radians(angle);
          float C = cosf(a), S = sinf(a);
          float x = this->cam_eye.x;
          float y = this->cam_eye.y;
          float z = this->cam_eye.z;
          // Apply Y-axis rotation matrix directly to Y axis.
          this->cam_eye.x = x * C + z * S;
          this->cam_eye.y = y;
          this->cam_eye.z = -x * S + z * C;
          this->update_view();
      }

  };

  int main(int argc, char** argv)
  {

      /* Initialize the library */
      if (!glfwInit()){ return -1; }

      #if defined(GLUT_ENABLED)
          glutInit(&argc, argv);
      #endif

      // ====== S H A D E R - C O M P I L A T I O N ====//
      //                                                //

      GLFWwindow* window = make_glfwWindowi(640, 480, "Draw Cube 3D");

      // Note: The shader source code is at the end of file.
      extern const char* code_vertex_shader;
      extern const char* code_frag_shader;

      GLuint prog = glCreateProgram();
      // Compile shader code
      compile_shader(prog, code_vertex_shader, GL_VERTEX_SHADER  ) ;
      compile_shader(prog, code_frag_shader,   GL_FRAGMENT_SHADER );
      glUseProgram(prog);

      // Get shader uniform variable location for projection matrix
      // See shader code: "uniform mat4 projection;"
      const GLint u_proj  = glGetUniformLocation(prog, "u_projection");
      assert( u_proj >= 0 && "Failed to find u_projection uniform variable" );

      const GLint u_view = glGetUniformLocation(prog, "u_view");
      assert( u_proj >= 0 && "Failed to find u_view uniform variable" );

      // Get shader uniform variable  location for model matrix.
      const GLint u_model  = glGetUniformLocation(prog, "u_model");
      assert( u_model >= 0 && "Failed to find uniform variable" );

      // Get shader attribute location - the function glGetAttribLocation - returns (-1) on error.
      const GLint attr_position = glGetAttribLocation(prog, "position");
      assert( attr_position >= 0 && "Failed to get attribute location" );

      // Get shader attribute of color
      const GLint attr_color = glGetAttribLocation(prog, "color");
      if( attr_color < 0){ std::fprintf(stderr, " [WARNING] Shader color attribute location not found. \n"); };


      // ====== U P L O A D - TO - G P U =========================//
      //                                                          //

      // ----- Upload Cube vertices and colors ----------------//
          Geometry cube_geometry = make_cube_geometry(0.3);
      GLuint vao_cube     = 0;
      GLuint vbo_vertices = 0;
      GLuint vbo_colors   = 0;
      // Upload geometry cube data to GPU
      send_vertices(&vao_cube, &vbo_vertices, attr_position, cube_geometry.vertices);
      send_colors(&vao_cube, &vbo_colors, attr_color, cube_geometry.colors);
      std::fprintf(stderr, " [TRACE] vao_cube = %d \n", vao_cube );

      // ------- Upload grid vertices and colors ------------------//
      Geometry grid_geometry = make_grid_geometry(20, 0.1f, ColorRGB{0.0f, 0.9f, 0.5f});
      GLuint vao_grid = 0;
      GLuint vbo_grid_vertices = 0;
      send_vertices(&vao_grid, &vbo_grid_vertices, attr_position, grid_geometry.vertices);
      send_colors(&vao_grid, &vbo_grid_vertices, attr_color, grid_geometry.colors);

      // ----- X, Y, Z axis for visual debugging ----------------
      // X axis (GREEN) ; Y axis (RED); Z axis (BLUE)
      GLuint vao_axis = 0;
      GLuint vbo_axis_vertices = 0;
      GLuint vbo_axis_colors = 0;
      float axis_len = 5.0;

      // Each two points represents a unconnected line (GL_LINES)
      std::vector<Vertex3D> axis_vertices {
          // Line for X axis - from (0.0, 0.0, 0.0) to (axis_len, 0.0, 0.0)
            Vertex3D{0.0f, 0.0f, 0.0f}, Vertex3D{axis_len, 0.0f, 0.0f}
          // Line for Y axis
          , Vertex3D{0.0f, 0.0f, 0.0f}, Vertex3D{0.0f, axis_len, 0.0f}
          // Line for Z axis
          , Vertex3D{0.0f, 0.0f, 0.0f}, Vertex3D{0.0f, 0.0, axis_len}
      };

      auto axis_colors = std::vector<ColorRGB>{
            // Color of X axis line
            color_green, color_green
            // Color of Y axis line
           , color_red,  color_red
            // Color of Z axis line
           , color_blue, color_blue
      };

      send_vertices(&vao_axis, &vbo_axis_vertices, attr_position, axis_vertices);
      send_colors(&vao_axis,   &vbo_axis_colors,   attr_color,    axis_colors  );

      // ============== Set Shader Uniform Variables =============//
      //                                                          //

      int width, height;
      glfwGetWindowSize(window, &width, &height);
      // Window aspect ratio
      float aspect = static_cast<float>(width) / height;
      // Identity matrix
      const auto identity = glm::mat4(1.0);


      // Set projection matrix uniform variable
      glUniformMatrix4fv(u_proj, 1, GL_FALSE, glm::value_ptr(identity) );

      // ==== R E N D E R I N G - O B J E C T S ==========================//
      //                                                                  //

      RenderObject cube1{};
      cube1.vao        = vao_cube;
      cube1.u_model    = u_model;
      cube1.draw_type  = GL_QUADS;
      cube1.n_vertices = 24;
      // cube1.model = glm::scale(cube1.model, glm::vec3(0.5, 0.5, 0.5));
      cube1.model = glm::translate(cube1.model, glm::vec3(0.2, 1.20, +0.4));
      cube1.model = glm::rotate(cube1.model, glm::radians(45.0f), glm::vec3(1.0, 1.0, 0.0));

      // Call copy constructor (copy all data from cube1)
      RenderObject cube2 = cube1;
      cube2.model = glm::mat4(1.0);
      cube2.model = glm::translate(cube2.model, glm::vec3(-1.20, 1.2, 0.4));
      cube2.model = glm::scale(cube2.model, glm::vec3(1.5, 1.5, 1.5));
      // cube2.model = glm::rotate(cube2.model, glm::radians(60.0f), glm::vec3(1.0, 0.0, 1.0));


      // Grid in the plane containing the axis X and Y
      RenderObject grid_xy{};
      grid_xy.vao        = vao_grid;
      grid_xy.u_model    = u_model;
      grid_xy.draw_type  = GL_LINES; // Unconnected lines
      grid_xy.n_vertices = grid_geometry.vertices.size();
      grid_xy.model = glm::translate(grid_xy.model, glm::vec3(0.0, 1.5, -2.2));
      //grid_xy.model = glm::scale(grid_xy.model, glm::vec3(4.0, 4.0, 4.0));

      // Grid in the plane which contains the axis X and Z
      RenderObject grid_xz = grid_xy;
      grid_xz.model = glm::mat4(1.0);
      grid_xz.model = glm::rotate(grid_xz.model, glm::radians(-90.0f), glm::vec3(1.0, 0.0, 0.0));
       // grid_xz.model = glm::scale(grid_xz.model, glm::vec3(4.0, 4.0, 4.0));


      // Axis which is positioned at the origin of WCS (World Coordinate System)
      RenderObject axis_world;
      axis_world.u_model   = u_model;
      axis_world.vao       = vao_axis;
      axis_world.draw_type = GL_LINES;
      axis_world.n_vertices = axis_vertices.size();


      Camera camera(u_view, u_proj, width / height);

      //  ======= R E N D E R  - L O O P ============//
      //                                             //
      while ( !glfwWindowShouldClose(window) )
      {
          glClear(GL_COLOR_BUFFER_BIT  | GL_DEPTH_BUFFER_BIT);

          // ====== BEGIN RENDERING ============//

              // GL_CHECK( ::glutWireTeapot(0.50) );

              // ------ Draw Grid ----------
              axis_world.render();
              grid_xy.render();
              grid_xz.render();
              cube1.render();
              cube2.render();

          // ====== END RENDERING ==============//

          /* Swap front and back buffers */
          glfwSwapBuffers(window);
          /* Poll for and process events */
          glfwWaitEvents();

          if( glfwGetKey(window, 'Q' ) == GLFW_PRESS )
          {
               std::cout << " [TRACE] User typed Q =>> Shutdown program. Ok. " << '\n';
               break;
          }

          if( glfwGetKey(window, GLFW_KEY_RIGHT ) == GLFW_PRESS ){ camera.rotate_y(+10.0); }
          if( glfwGetKey(window, GLFW_KEY_LEFT  ) == GLFW_PRESS ){ camera.rotate_y(-10.0); }

          // Move camera up (positive Y axis)
          if( glfwGetKey(window, GLFW_KEY_UP  ) == GLFW_PRESS ){
              camera.cam_eye.y += 0.5;
              camera.update_view();
          }

          // Move camera down (negative Y axis)
          if( glfwGetKey(window, GLFW_KEY_DOWN  ) == GLFW_PRESS )
          {
              camera.cam_eye.y -= 0.5;
              camera.update_view();
          }

          // Rotate cube 1 in counter counter clockwise way around Y axis
          if( glfwGetKey(window, 'A'  ) == GLFW_PRESS )
          {
              cube1.model = glm::rotate( cube1.model, glm::radians(+10.0f), glm::vec3(0.0, 1.0, 0.0) );
          }
          // Rotate cube 1 in counter clockwise way around Y axis
          if( glfwGetKey(window, 'S'  ) == GLFW_PRESS )
          {
              cube1.model = glm::rotate( cube1.model, glm::radians(-10.0f), glm::vec3(0.0, 1.0, 0.0) );
          }

          // Rotate cube 1 in counter counter clockwise way around X axis (positive rotation)
          if( glfwGetKey(window, 'Z'  ) == GLFW_PRESS )
          {
              cube1.model = glm::rotate( cube1.model, glm::radians(+10.0f), glm::vec3(1.0, 0.0, 0.0) );
          }
          // Rotate cube 1 in counter counter clockwise way around X axis (negative rotation)
          if( glfwGetKey(window, 'X'  ) == GLFW_PRESS )
          {
              cube1.model = glm::rotate( cube1.model, glm::radians(-10.0f), glm::vec3(1.0, 0.0, 0.0) );
          }


      }

      glfwTerminate();
      return 0;

  } // --- End of main() -----//


  // ---------- S H A D E R - P R O G R A M S  -------------------------//
  //

  // Minimal vertex shader =>> Runs on the GPU and processes each vertex.
  const char* code_vertex_shader = R"(
      #version 330 core

      layout ( location = 0)  in vec3 position;
      layout ( location = 1) in vec3 color;
      out vec3 out_color;
      uniform mat4 u_model;       // Model matrix
      uniform mat4 u_view;        // Camera's view matrix
      uniform mat4 u_projection;  // Camera's projection matrix

      void main()
      {
          gl_Position = u_projection * u_view * u_model * vec4(position, 1.0);

          // Forward to fragment shader
          out_color = color; // vec3(0.56, 0.6, 0.0);
      }

  )";

  // Fragment shader source code
  const char* code_frag_shader = R"(
      #version 330

      // This color comes from Vertex shader
      in vec3 out_color;

      void main()
      {
          // Set vertex colors
          gl_FragColor =  vec4(out_color, 1.0);
          // gl_FragColor = vec4(0.3, 0.6, 0.0, 1.0);
      }
  )";


      // ====== I M P L E M E N T A T I O N S ==========//

  GLFWwindow*
  make_glfwWindowi(int  width, int height, const char* title)
  {

      glfwSetErrorCallback([](int error, const char* description)
                           { std::fprintf( stderr, " [GLFW ERROR] Error = %d ; Description = %s \n"
                                          , error, description);
                           });

      GLFWwindow* window = glfwCreateWindow(width, height, title, NULL, NULL);
      assert( window != nullptr && "Failed  to create Window");

      // OpenGL context
      glfwMakeContextCurrent(window);
      // Pain whole screen as black - dark screen colors are better
      // for avoding eye strain due long hours staring on monitor.
      glClearColor(0.0f, 0.0f, 0.0f, 1.0f);

      // Set - OpenGL Core Profile - version 3.3
      GL_CHECK( glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3) );
      GL_CHECK( glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3) );
      GL_CHECK( glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE) );

      GL_CHECK( glEnable(GL_COLOR_MATERIAL) );
      GL_CHECK( glEnable(GL_DEPTH_TEST)     );
      GL_CHECK( glEnable(GL_BLEND)          );

      return window;
  }

  void compile_shader(GLuint m_program, const char* code, GLenum type)
  {
      GLint shader_id = glCreateShader( type );
      glShaderSource(shader_id, 1, &code, nullptr);
      glCompileShader(shader_id);

      GLint is_compiled = GL_FALSE;
      // Check shader compilation result
      glGetShaderiv(shader_id, GL_COMPILE_STATUS, &is_compiled);

      // If there is any shader compilation result,
      // print the error message.
      if( is_compiled == GL_FALSE)
      {
          GLint length;
          glGetShaderiv(shader_id, GL_INFO_LOG_LENGTH, &length);
          assert( length > 0 );

          std::string out(length + 1, 0x00);
          GLint chars_written;
          glGetShaderInfoLog(shader_id, length, &chars_written, out.data());
          std::cerr << " [SHADER ERROR] = " << out << '\n';
          // Abort the exection of current process.
          std::abort();
      }

      glAttachShader(m_program, shader_id);
      glDeleteShader(shader_id);
      glLinkProgram(m_program);
      GLint link_status = GL_FALSE;
      glGetProgramiv(m_program, GL_LINK_STATUS, &link_status);
      assert( link_status != GL_FALSE );
      // glUseProgram(m_program);
  }

  // Upload buffer from main memory to GPU VBO
  // =>> Parameters VAO, VBO are allocated by the caller.
  void send_buffer( GLuint* pVao, GLuint* pVbo, GLsizei sizeBuffer
                  , void* pBufffer, GLint   shader_attr, GLint size
                  , GLenum type)
  {
      assert(pVao != nullptr);
      assert(pVbo != nullptr);
      GLuint& vao = *pVao;
      GLuint& vbo = *pVbo;
      // Generate and bind current VAO (Vertex Array Object)
      if(vao == 0){ glGenVertexArrays(1, &vao); }
      glBindVertexArray(vao);
      // Generate and bind current VBO (Vertex Buffer Object)
      glGenBuffers(1, &vbo);
      glBindBuffer(GL_ARRAY_BUFFER, vbo);
      // Upload data to current VBO buffer in GPU
      glBufferData(GL_ARRAY_BUFFER, sizeBuffer, pBufffer, GL_STATIC_DRAW);
      glEnableVertexAttribArray(shader_attr);
      // Set data layout - how data will be interpreted.
      glVertexAttribPointer(shader_attr, size, type, GL_FALSE, 0, nullptr);
      // ------ Disable Global state set by this function -----//
      // Unbind VAO
      glBindVertexArray(0);
      // Unbind VBO
      glBindBuffer(GL_ARRAY_BUFFER, vbo);
      // Disable current shader attribute
      glDisableVertexAttribArray(shader_attr);

  }


  Geometry
  make_grid_geometry(size_t n_grid, float dx, const ColorRGB& color)
  {
      std::vector<Vertex3D> grid_vertices;
      std::vector<ColorRGB> grid_colors ;

      float dy = dx;
      float grid_w  = n_grid * dx;
      float grid_h  = n_grid * dy;

      // Draw horizontal lines - parallel to X axis
      for(size_t n = 0; n < 2 * n_grid; n++)
      {
          // Line Vertex A (BEGIN)
          grid_vertices.push_back( Vertex3D{ -grid_w, dy * n - grid_h, 0 } );
          // Color of vertex A
          grid_colors.push_back(color);
          // Line Vertex B (END)
          grid_vertices.push_back( Vertex3D{ +grid_w, dy * n - grid_h, 0  } );
          // Color of vertex B
          grid_colors.push_back(color);
      }

       // Draw horizontal lines - parallel to Y axis
      for(size_t n = 0; n < 2 * n_grid; n++)
      {
          // Line point A (BEGIN)
          grid_vertices.push_back( Vertex3D{ dx * n - grid_w, -grid_h, 0 } );
          // Color of Vertex A
          grid_colors.push_back(color);
          // Line pojnt B (END)
          grid_vertices.push_back( Vertex3D{ dx * n - grid_w, +grid_h, 0  } );
          // Color of Vertex B
          grid_colors.push_back(color);

      }

      return Geometry{ grid_vertices, grid_colors };

  }

  Geometry
  make_cube_geometry(float w)
  {
      // Array of cube vertex coordinates (X, Y)
      auto vertices = std::vector<Vertex3D> {
          // Top face {y = w}
          { w, w, -w}
        , {-w, w, -w}
        , {-w, w,  w}
        , { w, w,  w}

        // Bottom face , {y = -w}
        , { w, -w,  w}
        , {-w, -w,  w}
        , {-w, -w, -w}
        , { w, -w, -w}

        // Front face  , {z = w}
        , { w,  w, w}
        , {-w,  w, w}
        , {-w, -w, w}
        , { w, -w, w}

        // Back face , {z = -w}
        , { w, -w, -w}
        , {-w, -w, -w}
        , {-w,  w, -w}
        , { w,  w, -w}

        // Left face , {x = -w}
        , {-w,  w,  w}
        , {-w,  w, -w}
        , {-w, -w, -w}
        , {-w, -w,  w}

        // Right face , {x = w}
        , {w,  w, -w}
        , {w,  w,  w}
        , {w, -w,  w}
        , {w, -w, -w}
      };

      // std::fprintf(stderr, " [TRACE] Number of cube vertices = %zu \n", cube_vertices.size() );

      auto colors = std::vector<ColorRGB> {
             color_red
          ,  color_red
          ,  color_red
          ,  color_red

          , color_blue
          , color_blue
          , color_blue
          , color_blue

          , color_green
          , color_green
          , color_green
          , color_green

          , color_gray
          , color_gray
          , color_gray
          , color_gray

          , color_dark_green
          , color_dark_green
          , color_dark_green
          , color_dark_green

          , color_dark_blue
          , color_dark_blue
          , color_dark_blue
          , color_dark_blue
      };

      return Geometry{ vertices, colors };
  }
#+END_SRC

 *Building on Linux*

#+BEGIN_SRC sh
  $ cmake -H. -B_build_linux -DCMAKE_BUILD_TYPE=Debug
  $ cmake --build _build_linux --target
  
  $ ls _build_linux/
  CMakeCache.txt  cmake_install.cmake  draw3d-cube*
  CMakeFiles/     _deps/               Makefile
#+END_SRC

Run the application:

#+BEGIN_SRC sh
  $ >> _build_linux/draw3d-cube
  [TRACE] vao_cube = 1 
#+END_SRC

 *Cross-compiling for Windows and testing  with wine*

STEP 1: Cmake configuration step 
 
#+BEGIN_SRC sh
  $ docker run -it --rm --user=$(id -u):$(id -g)  \
         --volume=$PWD:/cwd --workdir=/cwd  dockcross/windows-static-x64 \
         cmake -H. -B_build_cross -DCMAKE_BUILD_TYPE=Debug
#+END_SRC

STEP 2: Cmake building step 

#+BEGIN_SRC sh 
  $ docker run -it --rm --user=(id -u):(id -g)  \
      --volume=$PWD:/cwd --workdir=/cwd  dockcross/windows-static-x64 \
      cmake --build _build_cross --target
#+END_SRC

STEP 3: Check compiled files

#+BEGIN_SRC sh
  $ >> ls _build_cross/
  CMakeCache.txt  cmake_install.cmake  draw3d-cube.exe*  Makefile
  CMakeFiles/     _deps/               glew32.dll*
#+END_SRC

STEP 4: Run the application with Wine 

#+BEGIN_SRC sh
   $ file _build_cross/draw3d-cube.exe 
  _build_cross/draw3d-cube.exe: PE32+ executable (console) x86-64, for MS Windows
  
   $ file _build_cross/glew32.dll 
  _build_cross/glew32.dll: PE32+ executable (DLL) (console) x86-64, for MS Windows
  
  $ wine _build_cross/draw3d-cube.exe
#+END_SRC

** 3D - WebGL 3D Scene with grid and cube 
   
By using WebGL API, it is possible to learn and prototype computer
graphics without the complexity of dealing with building systems,
compilation, memory management and C or C++ programming. This code
takes advantage of the ubiquity and availability of JavaScript and
WebGL on every modern web browser for demonstrating the usage of
OpenGL and WebGL APIs for rendering a simple 3D scene containing a
camera, grid, cube and tetrahedron. The sample WebGL application uses
no external dependencies and implements everything from scratch which
makes it easier to reason about the code and about 3D programming
concepts.


Since the code is written in JavaScript and html, it is possible to
experiment and play with webGL APIs used in the code by opening the
next link.

  + Live example available at:  https://jsfiddle.net/kgqdocma/

In the live example, there are the following controls:

  + 'a' key => rotates the camera view upwards (forward vector)
  + 's' key => rotates the camera view downwards (forward)
  + arrow up => Moves the camera position (eye vector) in the
    direction that camera is _looking at_ (forward vector).
  + arrow down => Moves the camera position (eye vector) in the
    opposite direction that the camera is looking at.
  + arrow right => Rotates the camera around its _up_ vector (orientation).
  + arrow left => Rotates the camera in the opposite direction around its _up_ vector (orientation).
  + 'j' => Move the camera in the positive direction of Y axis
  + 'k' => Move the camera in the negative direction of Y axis.
  + 'q' => Rotates the cube.
  + 'r' => Reset camera position (eye vector) and view (forward vector)
    
Notes:

  + The class Matrix4x4 that represents a mutable 4x4 matrix
    column-major matrix is inspired by the c++ class [[https://doc.qt.io/qt-5/qmatrix4x4.html][QMatrix4x4]] class from
    Qt framework.
    
  + The Quaternion class was inspired by C++ class [[https://doc.qt.io/qt-5/qquaternion.html][QQuaternion]]  from Qt framework.

  + The class Orientation was inspired by the C++ class [[https://web.archive.org/web/20210507012307/https://docs.unrealengine.com/en-US/API/Runtime/Core/Math/FTransform/index.html][FTransform]]
    from Unreal game engine. The idea was copied from public
    information available in the documentation.
    
  + _WebGL_ and OpenGL ES only support matrices in _column-major_ order memory
    layout. Those APIs do not support matrices in _row-major order_. 

  + _WebGL_, which is based on _OpenGL ES_ only supports the _retained_
    _mode_. WebGL does not support _immediate mode_. Therefore there is no
    equivalent in WebGL to the old and deprecated OpenGL retained mode
    APIs, including glVertex3f(), glBegin(), glEnd(), glColor(),
    glMaterial(), glRotate(), glTranslate(), glFrustum() and many
    other deprecated OpenGL subroutines.

  + In modern OpenGL and specially in OpenGL ES and WebGL, it is only
    possible to draw primitive forms such as lines, points and
    triangles, any complex shape requires a good enough amount of
    primitive shapes in order to look like smooth and realistic. 



 *Screenshots*


[[file:images/opengl-webgl-cube3d-1.png][file:images/opengl-webgl-cube3d-1.png]]

[[file:images/opengl-webgl-cube3d-2.png][file:images/opengl-webgl-cube3d-2.png]]   


 *More about WebGL*

  + [[https://blog.scottlogic.com/2019/11/27/drawing-area-polygons-with-webgl.html][Drawing Area Polygons with WebGL]]
    
  + https://learnwebgl.brown37.net/

  + https://webglfundamentals.org/

  + https://stackoverflow.com/questions/tagged/webgl

  + [[https://developer.mozilla.org/en-US/docs/Web/API/WebGL_API][WebGL: 2D and 3D graphics for the web]] - Mozilla

  + https://webgl-shaders.com/

  + https://web.dev/webgl-shaders/

  + [[https://www.obfuscator.io/][JavaScript Obfuscator Tool]]

 
 *Complete Html Code*  (File: webgl2.html)

#+BEGIN_SRC html 
  <!DOCTYPE html>
  <html> 
     <head> 
         <title>WebGL 3D Scene</title>
      </script>
     </head>
  
     <body>
         <h1>WebGL 3D Scene with Cube</h1>
         OpenGL version:  <label id="output1"></label>          <br/>
         Shading language version: <label id="output2"></label> <br/> 
         Camera Position: <label id="output-camera"></label> <br/>
         <canvas id="glCanvas" width="600" height="480">
     </body>
      <script>
  // eye = [3, 10, 20]; at = [50, 25, 10]; up = [0, 1, 0];
  
  // RGB color constants (Red, Gree, Blue) (R, G, B) tuples
  const COLOR_RED        = [1.0, 0.0, 0.0];
  const COLOR_GREEN      = [0.0, 1.0, 0.0];
  const COLOR_BLUE       = [0.0, 0.0, 1.0];
  const COLOR_YELLLOW    = [ 0.80,  1.000, 0.100 ];
  const COLOR_GRAY       = [ 0.47,  0.390, 0.380 ];
  const COLOR_DARK_GREEN = [ 0.027, 0.392, 0.050 ];
  const COLOR_DARK_BLUE  = [ 0.109, 0.066, 0.411 ];   
  
    ... ... ..... . ... .... ... ... ... ... 
    ... ... ..... . ... .... ... ... ... ... 
    
      </script>
    
    </html> 
#+END_SRC

JavaScript part: 

#+BEGIN_SRC js 
   // eye = [3, 10, 20]; at = [50, 25, 10]; up = [0, 1, 0];

   // RGB color constants (Red, Gree, Blue) (R, G, B) tuples
   const COLOR_RED        = [1.0, 0.0, 0.0];
   const COLOR_GREEN      = [0.0, 1.0, 0.0];
   const COLOR_BLUE       = [0.0, 0.0, 1.0];
   const COLOR_YELLLOW    = [ 0.80,  1.000, 0.100 ];
   const COLOR_GRAY       = [ 0.47,  0.390, 0.380 ];
   const COLOR_DARK_GREEN = [ 0.027, 0.392, 0.050 ];
   const COLOR_DARK_BLUE  = [ 0.109, 0.066, 0.411 ];


   // Normalize a vector 3x1 column vector (3 rows and 1 column)  
   function normalize(vector)
   {
     let [x, y, z] = vector;
     let norm = Math.sqrt( x * x + y * y + z * z);
     return [ x / norm, y / norm, z / norm ];
   }

   // Computes the dot product (aka scalar) product between two vectors  
   function dot(vectorA, vectorB)
   {
     let [xa, ya, za] = vectorA;
     let [xb, yb, zb] = vectorB;
     return xa * xb  + ya * yb + za * zb;
   }

   // Computs the cross product between two vectors 
   function cross(vectorA, vectorB)
   {
     let [xa, ya, za] = vectorA;
     let [xb, yb, zb] = vectorB;
     return [ ya * zb - za * yb, za * xb - xa * zb, xa * yb - ya * xb ];
   }

   // Difference between two vectors 
   function diff(vectorA, vectorB)
   {
     let [xa, ya, za] = vectorA;
     let [xb, yb, zb] = vectorB;
     return [xa - xb, ya - yb, za - zb];
   }

   class Quaternion
   {
     constructor(w, x, y, z)
     {
       // Default is the unit quaternion 
       this._quat = [w, x, y, z];
     }

     /** @param {number} w
     /*  @param {number} x 
     /*  @param {number} y 
     /*  @param {number} z 
      */ 
     static create(w, x, y, z)
     {
       return new Quaternion(w, x, y, z);
     }

     /** Create unit quaternion  */
     static createUnit()
     {
       return new Quaternion(1.0, 0.0, 0.0, 0.0);
     }

     /** Create a imaginary quaternion */
     static createFromVector(x, y, z)
     {
       return new Quaternion(0.0, x, y, z);
     }

     /** Create quaternion represeting a rotation around some axis 
      * @param {number}   angle - Rotation angle in degrees 
      * @param {number[]} axis  - Rotation axis
      */
     static createFromAxisAngle(angle, axis)
     {
       let [nx, ny, nz] = normalize(axis);
       // Angle in radians 
       let t = angle / 180.0 * Math.PI / 2;
       let C = Math.cos(t);
       let S = Math.sin(t);
       return Quaternion.create(C, S * nx, S * ny, S * nz);
     }

     negate()
     {
       let [w, x, y, z] = this._quat;
       return new Quaternion(-w, -x, -y, -z);
     }

     conjugate()
     {
       let [w, x, y, z] = this._quat;
       return new Quaternion(w, -x, -y, -z);
     }

     // Note: It is assumed that the current quaternion is a unit quaternion 
     rotateVector(v)
     {
       let qa = this;
       let qb = Quaternion.createFromVector(...v);
       // r1 = qa * qb 
       let r1 = qa.multiply(qb);
       let r2 = r1.multiply(qa.conjugate());
       // r2 = qa * qb * qa.conjugage()
       //let b = q.multiply( qa.conjugate() );
       return r2.getVector();
     }

     /** Get imaginary part of a quaternion */
     getVector()
     {
       let [w, x, y, z] = this._quat;
       return [x, y, z];
     }

     /** Turn the quaternion into a homogeneous rotation matrix */
     toMatrix()
     {
       // The quaternion is assumed to be a unit quaternion 
       // otherwise the matrix result will be invalid
       let [a, b, c, d] = this._quat;
       let m = new Float32Array(16);
       m[0] = a * a - c * c - d * d;
       m[1] = 2 * (b * c + a * d);
       m[2] = 2 * (b * d - a * c);
       m[3] = 0.0;

       m[4] = 2 * (b * c - a * d);
       m[5] = a * a - b * b + c * c -  d * d;
       m[6] = 2 * (c * d + a * b);
       m[7] = 0.0;

       m[8] = 2 * (b * d + a * c);
       m[9] = 2 * (c * d - a * b);
       m[10] = a * a - b * b - c * c + d * d;
       m[11] = 0;
    
       m[12] = 0.0;
       m[13] = 0.0;
       m[14] = 0.0;
       m[15] = 1.0;

       return m;
     }

     /** Multiply a quaternion for another and returns a new quaternion 
      * Note: JavaScript lacks operator overloading
      * Note: Quaternion multiplication is not commutative 
      * @param {Quaternio} q
      **/
     multiply( q )
     {
       let [w1, x1, y1, z1] = this._quat;
       let [w2, x2, y2, z2] = q._quat; 
       let result = [
             w1 * w2 - x1 * x2 - y1 * y2 - z1 * z2 
           , x1 * w2 + w1 * x2 - z1 * y2 + y1 * z2
           , y1 * w2 + z1 * x2 + w1 * y2 - x1 * z2 
           , z1 * w2 - y1 * x2 + x1 * y2 + w1 * z2  
       ];
       return new Quaternion(...result);
     }

   }


  
   class Matrix4x4 
   {

     constructor()
     {
         this.mat = new Float32Array(16);
         this.mat[0] = 1.0;
         this.mat[5] = 1.0;
         this.mat[10] = 1.0;
         this.mat[15] = 1.0;
     }

     static create()
     {
       return new Matrix4x4();
     }

     at(i, j) 
     {
       return this.mat.at(j + 4 * i);
     }

     setToIdentity() 
     {
         this.mat = new Float32Array(16);
         this.mat[0] = 1.0;
         this.mat[5] = 1.0;
         this.mat[10] = 1.0;
         this.mat[15] = 1.0;
         return this;
     }

     // this.matrix = this.matrix * translateMatrix(x, y, z)
     /**
      *  @param {number} x 
      *  @param {number} y 
      *  @param {number} z 
      */ 
     translate(x, y, z)
     {
       let matrix = [   1, 0, 0, 0
                      , 0, 1, 0, 0 
                      , 0, 0, 1, 0 
                      , x, y, z, 1 
                     ];
       this.multiply(matrix);
       return this;
     }

     translateV(vector)
     {
       this.translate(vector[0], vector[1], vector[2]);
       return this;
     }

     /**
      *  @param {number} sx 
      *  @param {number} sy 
      *  @param {number} sz 
      */ 
     scaleV(sx, sy, sz)
     {
       let matrix = [  sx,  0,  0, 0 
                      , 0, sy,  0, 0 
                      , 0,  0, sz, 0 
                      , 0,  0,  0, 1 
                     ];
       this.multiply(matrix);
       return this;
     }

     /**
      *  @param {number} k
      */ 
     scale(k)  
     {
        let m = new Float32Array(16);
        m[0] = k; 
        m[5] = k; 
        m[10] = k; 
        m[15] = 1.0; 
       this.multiply(m);
       return this;
     }

     // Rotation around arbitrary axis (vector) designated by X, Y, Z
     // the angle is in radians 
     // this.matrix = this.matrix * rotate(angle, x, y, z)
     rotate(angle, x, y, z)
     {
       let r = new Float32Array(16);
       let t = angle * Math.PI / 180.0;
       let C = Math.cos(t);
       let S = Math.sin(t);
       let K = 1 - C ;
       r[0] = x*x + (1 - x*x)*C;
       r[4] = x*y*K - z*S;
       r[8] = x*z*K + y*S;
       r[12] = 0;
       r[1] = x*y*K + z*S;
       r[5] = y*y + (1 - y*y)*C
       r[9] = -x*S + y*z*K;
       r[13] = 0;
       r[2] = x*z*K - y*S;
       r[6] = x*S + y*z*K;
       r[10] = z*z + (1 - z*z)*C;
       r[14] = 0;
       r[3] = 0;
       r[7] = 0;
       r[11] = 0;
       r[15] = 1;
       this.multiply(r);
       return this;
     }

     /** Rotate around Z axis 
      * @param {number} angle - Rotation angle in degrees 
      */  
     rotateZ(angle)
     {
       // let r = new Float32Array(16);
       let r = Array(16).fill(0.0);
       let t = angle * Math.PI / 180.0;
       let C = Math.cos(t);
       let S = Math.sin(t);
       r[0] = C; r[4] = -S;
       r[1] = S; r[5] =  C;
       r[10] = 1; r[15] = 1;

       this.multiply(r);
       //this.mat = r;
       return this;
     }

     /** Rotate around some axis 
      * @param  {number}    angle  - rotation angle in degrees
      * @param  {number[]}  axis   - rotation axis as an array of 3 numbers 
      */ 
     rotateAxis(angle, axis)
     {
       let [x, y, z] = axis;
       this.rotate(angle, x, y, z);
       return this;
     }

     /** Rotate current matrix by multiplying it by the quaternion rotation matrix 
      *  this.matrix = this.matrix * quaternion.RotMatrix 
      * @param {Quaternion} q 
      **/
     rotateQuaternion(q)
     {
       let rotmatrix = q.toMatrix();
       this.multiply(rotmatrix);
       return this;
     }

     setLookAt(eye, at, up)
     {
       let Z = normalize( diff(eye, at) );
       let X = normalize( cross(up, Z) );
       let Y = normalize( cross(Z, X) );
       let m = new Float32Array(16);
       m[0]  = X[0]; m[4]  = X[1]; m[8]  = X[2];  m[12]  = -dot(X, eye);
       m[1]  = Y[0]; m[5]  = Y[1]; m[9]  = Y[2];  m[13]  = -dot(Y, eye);
       m[2]  = Z[0]; m[6]  = Z[1]; m[10] = Z[2];  m[14] = -dot(Z, eye);
       m[3] = 0;     m[7] = 0;     m[11] = 0;     m[15] =  1; 
       this.mat = m;
       return this;
     }

     // FOV angle in degrees 
     setPerspective(FOV, aspect, zNear, zFar)
     {
       // Angle in radians
       let t = Math.PI * FOV / 180.0; 
       let k = Math.tan(t / 2);
       let m = new Float32Array(16);
       m[0] = 1 / ( aspect * k ); 
       m[5] = 1 / k;
       m[10] = (zNear + zFar) / (zNear - zFar);
       m[14] = 2 * zNear * zFar / (zNear - zFar);
       m[11] = -1; 
       this.mat = m; 
       return this;
     }

     // In place matrix multiplication 
     multiply( matrix )
     {
       let a = this.mat;
       let b = matrix;
       let c = new Float32Array(16);

     // Matrix multiplication algorithm of column-major matrices borrowed from 
     // https://github.com/yycho0108/Abstraction/blob/master/gl-matrix.min.js
      var d = a[0], e = a[1], g = a[2], f = a[3], h = a[4], i = a[5], j = a[6], k = a[7]
             , l = a[8], o = a[9], m = a[10], n = a[11], p = a[12], r = a[13], s = a[14];

       a = a[15];
       var A = b[0], B = b[1], t = b[2], u = b[3], v = b[4], w = b[5], x = b[6], y = b[7], z = b[8]
           , C = b[9], D = b[10], E = b[11], q = b[12], F = b[13], G = b[14];

       b = b[15];
       c[0] = A * d + B * h + t * l + u * p;
       c[1] = A * e + B * i + t * o + u * r;
       c[2] = A * g + B * j + t * m + u * s;
       c[3] = A * f + B * k + t * n + u * a;
       c[4] = v * d + w * h + x * l + y * p;
       c[5] = v * e + w * i + x * o + y * r;
       c[6] = v * g + w * j + x * m + y * s;
       c[7] = v * f + w * k + x * n + y * a;
       c[8] = z * d + C * h + D * l + E * p;
       c[9] = z * e + C * i + D * o + E * r;
       c[10] = z *
           g + C * j + D * m + E * s;
       c[11] = z * f + C * k + D * n + E * a;
       c[12] = q * d + F * h + G * l + b * p;
       c[13] = q * e + F * i + G * o + b * r;
       c[14] = q * g + F * j + G * m + b * s;
       c[15] = q * f + F * k + G * n + b * a;
       this.mat = c;
     }  

   }


   function assertNotUndefined(variableName, variable)
   {
     if( typeof variable == "undefined" )
     {
       alert(` [ASSERT ERROR] ${variableName} is undefined.`);
     }
   }


       // Based on:  https://webglfundamentals.org/webgl/lessons/webgl-fundamentals.html
   function compileShaderProgram(gl, vertexShaderCode, fragmentShaderCode) 
   {
       function createShader(gl, type, source) 
       {
         assertNotUndefined("gl", gl);
         var shader = gl.createShader(type);
         gl.shaderSource(shader, source);
         gl.compileShader(shader);
         var success = gl.getShaderParameter(shader, gl.COMPILE_STATUS);
         if (success) {
           return shader;
         }

         console.log(" [SHADER ERROR] = \n" + source);
         console.log(" [SHADER ERROR] "  + gl.getShaderInfoLog(shader) );
         // alert(" [SHADER ERORR]  " + gl.getShaderInfoLog(shader) );
         gl.deleteShader(shader);
       }

       let vertexShader = createShader(gl, gl.VERTEX_SHADER, vertexShaderCode);

       let fragmentShader = createShader(gl, gl.FRAGMENT_SHADER, fragmentShaderCode);
       console.log(" [TRACE] Compiled fragmentShader Ok.");

       var program = gl.createProgram();
       assertNotUndefined("program", program);
       gl.attachShader(program, vertexShader);
       console.log(" [TRACE] Attach vertex shader Ok");
       gl.attachShader(program, fragmentShader);
       console.log(" [TRACE] Attach fragment shader Ok");
       gl.linkProgram(program);
       var success = gl.getProgramParameter(program, gl.LINK_STATUS);
       if (success) {
         return program;
       }

       console.error(gl.getProgramInfoLog(program));
       gl.deleteProgram(program);
   }


   function resizeCanvasToDisplaySize(canvas, multiplier) 
   {
       multiplier = multiplier || 1;
       const width  = canvas.clientWidth  * multiplier | 0;
       const height = canvas.clientHeight * multiplier | 0;
       if (canvas.width !== width ||  canvas.height !== height) {
         canvas.width  = width;
         canvas.height = height;
         return true;
       }
       return false;
   }

   // Vertex Buffer Object abstraction 
   class Vbuffer 
   {
     constructor(gl, attr_position, attr_color)
     {
       this._gl = gl;
       this._vbo = gl.createBuffer();   
       this._data = new Float32Array(1);
       this._attr_position = attr_position;
       this._attr_color = attr_color;
       this._vertices = 0;
     }

     unbind()
     {
       let gl = this._gl;
       gl.bindBuffer(gl.ARRAY_BUFFER, null);
     }

     // Release the VBO from the GPU 
     free()
     {
       let gl = this._gl;
       gl.deleteBuffer(this._vbo);
     }

     setData(data, ncomps = 3, stride = 0, istride = 0 )
     {
       let gl = this._gl;
       this._ncomps = ncomps;
       this._stride = stride; 
       this._istide = 0;
       this._vertices = data.length;
       gl.bindBuffer( gl.ARRAY_BUFFER, this._vbo );
       this._data = new Float32Array(data);
       // Send data to GPU
       gl.bufferData(gl.ARRAY_BUFFER, this._data, gl.STATIC_DRAW);
     }

     drawTriangles()
     {
       let gl = this._gl;
       this.draw(gl.TRIANGLES);
     }

     drawLines()
     {
       let gl = this._gl;
       this.draw(gl.LINES);
     }

     drawLineLoop()
     {

       let gl = this._gl;
       this.draw(gl.LINE_LOOP);
     }

     draw(drawType)
     {
       let gl = this._gl;
       gl.bindBuffer( gl.ARRAY_BUFFER, this._vbo );

       // Position data layout for each vertex 
       gl.vertexAttribPointer(  this._attr_position  // Shader attribute 
                              , 3                    // Number of components, coordinates
                              , gl.FLOAT             // Type of each componente
                              , false                // Normalized flag 
                              , 24                   // Stride  = sizeof(Vertex) = sizeof(Position) + sizeof(Color) = 
                                                     // Stride  = 3 * sizeof(Float in bytes) + 3 * sizeof(Float in bytes) 
                                                     // Stride  = 3 * 4 + 3 * 4 = 24  
                              , 0                    // offset  
                             );

       // Color data layout for each vertex  
       gl.vertexAttribPointer(  this._attr_color     // Shader attribute 
                              , 3                    // Number of components, coordinates
                              , gl.FLOAT             // Type of each componente
                              , false                // Normalized flag 
                              , 24                   // Stride 
                              , 12                   // offset - offset of color attribute = 3 * sizeof(float) =  3 * 4 = 12 
                             );


       // Each vertex has 6 coordinates - 3 coordinates for position and 3 coordinates for color
       gl.drawArrays(drawType, 0, this._vertices / 6);

       // Unbind VBO and disable global state 
       gl.bindBuffer( gl.ARRAY_BUFFER, null);
     }
   }

   class Camera
   {
     constructor(gl, uniform_view, uniform_proj, uniform_eye)
     {
       // Current location of camera in world coordinates
       this.eye     = [  2.0, 4.0,  5.0 ];
       // Direction to where camera is looking at.
       this.forward = [ -1.0, 0.0, -1.0 ];
       // UP vector - camera orientation (default Y axis)
       this.up      = [  0.0, 1.0,  0.0 ];
    
       // Field of view angle stated in degrees  
       this.fov_angle = 60.0; 
       // Aspect ratio  
       this.aspect = gl.canvas.clientWidth / gl.canvas.clientHeight;
       // Far 
       this.zFar = 100.0;
       // Near 
       this.zNear = 0.1;

       this.gl = gl;
       this.uniform_view = uniform_view;
       this.uniform_proj = uniform_proj;
       this.uniform_eye  = uniform_eye;

       this.updateView();

       console.log(" [TRACE] Crated Camera Ok.");
     }

     // Reset camera to initial position
     reset()
     {
       // Current location of camera in world coordinates
       this.eye     = [  2.0, 4.0,  5.0 ];
       // Direction to where camera is looking at.
       this.forward = [ -1.0, 0.0, -1.0 ];
       // Direction to where camera is looking at.
       this.up      = [  0.0, 1.0,  0.0 ];
       // Field of view angle stated in radians. It is by default 60 degrees.
       this.fov_angle = 60.0; //* Math.PI / 180.0;
     }

     // Rotate around camera's Up vecto.
     rotateYaw(angle)
     {
       let q = Quaternion.createFromAxisAngle(angle, this.up);
       this.forward = q.rotateVector(this.forward);
       this.updateView();
     }

     // Rotate around camera's pitch axis (X axis) elevate camera view.
     rotatePitch(angle)
     {
       let axis = cross(this.up, this.forward);
       let q = Quaternion.createFromAxisAngle(angle, axis);
       this.forward = q.rotateVector(this.forward);
       this.updateView();
     }

     // Move camera in Y axis 
     moveCameraUpDow(factor)
     {
       // Increase of decrease Y component of Eye vector
       this.eye[1] = this.eye[1] + factor;
       this.updateView();
       return this;
     }

     // Make the vector forward parallel to the plane XZ
     parallelXZ()
     {
       this.forward[1] = 0.0; 
       this.updateView();
       return this;
     }

        // Move at forward vector direction (to where camera is looking at).
     moveForward(factor)
     {
         // this->eye = this->eye + factor * this->forward;
         let [ex, ey, ez] = this.eye; 
         let [fx, fy, fz] = this.forward;
         this.eye = [ex + factor * fx, ey + factor * fz, ez + factor * fz];
         this.updateView();
         return this
     }

     // Set camera position in world coordinates 
     setPosition(position)
     {
       this.eye = position; 
       this.updateView();
       return this;
     }

     setPositonXYZ(x, y, z)
     {
       this.setPosition([x, y, z]);
       return this;
     }

     lookAt(at)
     {
       // Set position to where camera is looking at.
       this.forward = diff(at, this.eye) ;
       this.updateView();
       return this;
     }

     lookAtXYZ(x, y, z)
     {
       this.lookAt([x, y, z]);
       return this;
     }
  

     updateView() 
     {
       // Perspective/Projection matrix 
       // Tp = glm::perspective( fov_angle, aspect, zNear, zFar );
       const Tp = new Matrix4x4(); 
       Tp.setPerspective(this.fov_angle, this.aspect, this.zNear, this.zFar);
       // console.log(" [TRACE] Tp = ", Tp);

       // View matrix 
       // auto Tview = glm::lookAt(eye, cam_at, up);
       const Tv = new Matrix4x4();
       // Location where camera is looking at in world coordinates 
       const at = [ this.eye[0] + this.forward[0], this.eye[1] + this.forward[1], this.eye[2] + this.forward[2] ];
       Tv.setLookAt(this.eye, at, this.up);
       // console.log(" [TRACE] Tv = ", Tv);
  
       // glUniformMatrix4fv(shader_uniform_view, 1, GL_FALSE, glm::value_ptr(Tview) );
       this.gl.uniformMatrix4fv(this.uniform_view, false, Tv.mat);

       // glUniformMatrix4fv(shader_uniform_proj, 1, GL_FALSE, glm::value_ptr(Tproj) );
       this.gl.uniformMatrix4fv(this.uniform_proj, false, Tp.mat);

     }

     setIdentity()
     {
       let m = new Matrix4x4();
       this.gl.uniformMatrix4fv(this.uniform_view, false, m.mat);
       this.gl.uniformMatrix4fv(this.uniform_proj, false, m.mat);
     }

   }
  

   /** Encapsulates the position, scale and  rotation of an object in the space */
   class Orientation
   {
     constructor()
     {
       // Default scale 1.0 - original object size 
       this._scale     = 1.0; 
       // Default position at origin 
       this._position  = [0.0, 0.0, 0.0];
       // Unit quaternion 
       this._rotation = Quaternion.create(1.0, 0.0, 0.0, 0.0);
       this._is_changed = false;
       this._model = Matrix4x4.create()
                             .translateV(this._position)
                             .scale(this._scale)
                             .rotateQuaternion(this._rotation)
     }

     reset()
     {
       this._scale     = 1.0; 
       this._position  = [0.0, 0.0, 0.0];
       this.__rotation = Quaternion.create(1.0, 0.0, 0.0);
       this._is_changed = true;
     }

     setPosition(x, y, z)
     {
       this._position = [x, y, z];
       this._is_changed = true;
     }

     translate(dx, dy, dz)
     {
         this._position[0] += dx;
         this._position[1] += dy;
         this._position[2] += dz;
         this._is_changed = true;
         return this;
     }

     setScale(scale)
     {
         this.scale = scale;
         this.is_changed = true;
         return this;
     }

     rotateZ(angle)
     {
       // console.log(" [TRACE] Rotating angle = ", angle);
       // this.angle = this.angle + angle;
       this.rotate(angle, [0.0, 0.0, 1.0]);
       this._is_changed = true;
       return this;
     }

     rotate(angle, axis)
     {
       let q = Quaternion.createFromAxisAngle(angle, axis);
       this._rotation = this._rotation.multiply(q);
       this._is_changed = true;
       return this;
     }

     getModel()
     {
       if( this._is_changed )
       {
           this._is_changed = false;
           this._model.setToIdentity()
                     .translateV(this._position)
                     .scale(this._scale)
                     .rotateQuaternion(this._rotation);
       }
       return this._model;
     }

   }

   class Entity 
   {
     constructor(gl, vbo, u_model, drawType)
     {
       this.gl = gl;
       this.vbo = vbo; 
       this.u_model = u_model;

       this.is_visible = true;

       // Draw Triangles by default 
       this.drawType = drawType; 
       this.orientaton = new Orientation();
     }

     setDrawTriangles() { this.drawType = this.gl.TRIANGLES; return this; }
     setDrawLines() { this.drawType = this.gl.LINES;         return this; }
     setDrawLineLoop() { this.drawType = this.gl.LINE_LOOP;  return this; }
     setVisible(flag) { this.is_visible = flag;              return this; }
     toggleVisible() { this.is_visible = !this.is_visible;         return this; }
     setPosition(x, y, z) { this.orientaton.setPosition(x, y, z);  return this; }
     translate(dx, dy, dz){ this.orientaton.translate(dx, dy, dz); return this; }
     setScale(k){ this.orientaton.setScale(k);                     return this; }
     rotateZ(angle){ this.orientaton.rotateZ(angle);               return this; }
     rotate(angle, axis){ this.orientaton.rotate(angle, axis);     return this; }

     draw() 
     {
       let model = this.orientaton.getModel();
       if( this.is_visible )
       {
          this.gl.uniformMatrix4fv(u_model, false, model.mat); 
          this.vbo.draw(this.drawType);
       }
     }
   }

   class EntityFactory 
   {

     /** @param {Vbuffer} - vbo */
     constructor(gl, vbo, u_model)
     {
       this.gl = gl; 
       this.vbo = vbo;
       this.u_model = u_model;
       this.drawType = gl.TRIANGLES;
     }

     setDrawTriangles()
     {
       this.drawType = this.gl.TRIANGLES;
       return this;
     }

     setDrawLines()
     {
       this.drawType = this.gl.LINES;
       return this;
     }

     setDrawLineLoop()
     {
       this.drawType = this.gl.LINE_LOOP;
       return this;
     }

     create()
     {
         return new Entity(this.gl, this.vbo, this.u_model, this.drawType);
     }
   }

       // ----- R E N D E R I N  G ------------------//


   // Vertex Shader code => Performs vertex coordinate transforms in the GPU 
   let code_shader_vert =` 
     // precision mediump float;

     // Note: keyword attribute is deprecated 
     attribute vec3 position;
     attribute vec3 color; 

     varying vec3 out_color;

     uniform vec3 u_eye;         // Cameras's position in space
     uniform mat4 u_model;       // Model matrix 
     uniform mat4 u_view;        // Camera's view matrix    
     uniform mat4 u_projection;  // Camera's projection matrix 

     void main(){

       gl_Position = u_projection * u_view * u_model * vec4(position, 1.0);

       // Forward to fragment shader 
       out_color = color; 
    
     }
   `;

   // Fragment shader code => Sets color, lights and illumination 
   let code_shader_frag = `
      // precision mediump float;

      // varying low vec3 out_color;
      varying lowp vec3 out_color;

       void main()
       {
         gl_FragColor = vec4(out_color, 1.0);
         //gl_FragColor = vec4(0, 0, 1.0, 1.0);
       }
   `;

     // ----------- Rendering Starts Here -----------//


   const canvas = document.querySelector("#glCanvas");
   const gl = canvas.getContext("webgl");

   if(  gl == null ){
     alert(" [ABORT] Unable to initialized WebGL");
   }

   console.log(" [DEBUG] OpenGL version = " + gl.getParameter(gl.VERSION) );
   console.log(" [DEBUG] Shading language version = " + gl.getParameter(gl.SHADING_LANGUAGE_VERSION));

   document.querySelector("#output1").textContent = gl.getParameter(gl.VERSION);
   document.querySelector("#output2").textContent =  gl.getParameter(gl.SHADING_LANGUAGE_VERSION);

   let prog =  compileShaderProgram(gl, code_shader_vert, code_shader_frag);
   gl.useProgram(prog);

   // Uniform variables 
   let u_proj       = gl.getUniformLocation( prog, "u_projection");
   let u_view       = gl.getUniformLocation( prog, "u_view");
   let u_model      = gl.getUniformLocation( prog, "u_model");
   let u_camera_eye = gl.getUniformLocation( prog, "u_eye");

   // Attribute Locations 
   let attr_position = gl.getAttribLocation(prog, "position");
   let attr_color    = gl.getAttribLocation(prog, "color");

   let identity = new Matrix4x4();

   // Initialize with identity matrix 
   gl.uniformMatrix4fv(u_model, false, identity.mat); 
   gl.uniformMatrix4fv(u_view,  false, identity.mat); 
   gl.uniformMatrix4fv(u_proj,  false, identity.mat);


   function buildGridPlaneXY(gl, attr_position, attr_color, n , delta)
   {
       let vertices = [];

       let xmax = n * delta; 
       let ymax = n * delta;

       // Draw lines in plane XY parallel to line X 
       for(let i = 0; i < n; i++)
       {
         // Position of first vertex 
         let position1 = [0.0, i * delta, 0.0 ];
         // Positon of first line vertex   
         vertices.push(...position1);    
         // Color of second line vertex 
         vertices.push(...COLOR_BLUE);   

         // Position of second vertex  
         let position2 = [xmax, i * delta, 0.0];
         // Positon of first line vertex   
         vertices.push(...position2);    
         // Color of second line vertex 
         vertices.push(...COLOR_BLUE);   
       }

       // Draw lines in plane XY parallel to line Y
       for(let j = 0; j < n; j++)
       {
         // Position of first vertex 
         let position1 = [j * delta, 0.0, 0.0 ];
         // Positon of first line vertex   
         vertices.push(...position1);    
         // Color of second line vertex 
         vertices.push(...COLOR_GREEN);   

         // Position of second vertex  
         let position2 = [j * delta, ymax, 0.0];
         // Positon of first line vertex   
         vertices.push(...position2);    
         // Color of second line vertex 
         vertices.push(...COLOR_GREEN);   
       }

       let buffer = new Vbuffer(gl, attr_position, attr_color);
       buffer.setData(vertices);
       return buffer;
   }

   function buildTetrahedron(gl, attr_position, attr_color, size)
   {
     let k = size;
     // Position of Tetrahedron vertices 
     let a = [0, 0, k];  // Point in Z axis 
     let b = [k, 0, 0];  // Point in X axis 
     let c = [0, k, 0];  // Point in Y axis 
     let d = [0, 0, 0];  // Point in origin 

     let COLOR_Triangle1 = COLOR_BLUE;
     let COLOR_Triangle2 = COLOR_GREEN;
     let COLOR_Triangle3 = COLOR_RED;
     let COLOR_Triangle4 = COLOR_YELLLOW;

     let vertices = [];
  
     // triangle vertices of face 1
     vertices.push(...a, ...COLOR_Triangle1);
     vertices.push(...b, ...COLOR_Triangle1);
     vertices.push(...c, ...COLOR_Triangle1);

     // triangle vertices of face 2
     vertices.push(...a, ...COLOR_Triangle2);
     vertices.push(...c, ...COLOR_Triangle2);
     vertices.push(...d, ...COLOR_Triangle2);

     // triangle vertices of face 3
     vertices.push(...a, ...COLOR_Triangle3);
     vertices.push(...b, ...COLOR_Triangle3);
     vertices.push(...d, ...COLOR_Triangle3);

     // triangle vertices of face 4
     vertices.push(...d, ...COLOR_Triangle4);
     vertices.push(...c, ...COLOR_Triangle4);
     vertices.push(...b, ...COLOR_Triangle4);

     // console.log(" [TRACE] vertices = ", vertices);

     let buffer = new Vbuffer(gl, attr_position, attr_color);
     buffer.setData(vertices);
     return buffer;
   }

   // Note: OpenGL can only draw triangles and lines. 
   // Therefore, the only way to draw a cube solid is to 
   // draw the triangles for every face of the  cube.
   // As a cube has 6 face and two triangles for every 
   // face, it is necessary to draw 12 triangles. 
   function buildCube(gl, attr_position, attr_color, size)
   {
     let k = size; 
     // Cube vertices 
     //       x  y  z
     let a = [0, 0, k];
     let b = [0, k, k];
     let c = [k, k, k];
     let d = [k, 0, k];
     let e = [k, 0, 0];
     let f = [k, k, 0];
     let g = [0, k, 0];
     let h = [0, 0, 0];

     let vertices = [];

     function triangle(t1, t2, t3, color)
     {
       vertices.push(...t1, ...color);
       vertices.push(...t2, ...color);
       vertices.push(...t3, ...color);
     }
     // Face 1 of cube 
     triangle(a, b, c, COLOR_BLUE);
     triangle(a, c, d, COLOR_BLUE);
     // Face 2 of cube 
     triangle(c, d, e, COLOR_DARK_GREEN);
     triangle(c, f, e, COLOR_DARK_BLUE);
     // Face 3 of cube 
     triangle(a, d, e, COLOR_GREEN);
     triangle(a, h, e, COLOR_GREEN);
     // Face  4 of cube 
     triangle(a, h, g, COLOR_YELLLOW);
     triangle(a, b, g, COLOR_YELLLOW);
     // Face  5 of cube 
     triangle(b, c, g, COLOR_GRAY);
     triangle(c, g, f, COLOR_GRAY);
     // Face 6 of cube 
     triangle(g, h, f, COLOR_RED);
     triangle(h, f, e, COLOR_RED);

     console.log(" [TRACE] vertices = ", vertices);

     let buffer = new Vbuffer(gl, attr_position, attr_color);
     buffer.setData(vertices);
     return buffer;
   }


   // Vertices of (x, y) coordinates 
   let vertices_triangle = [
      // --- Vertices ----- //  // (R, G, B) --- Colors -----//
       -0.25,  -0.25,  0,      1.0, 0.0, 0.0
     ,  0.00,  +0.25,  0,      0.0, 1.0, 0.0
     , +0.25,  -0.25,  0,      0.0, 0.0, 1.0  
   ]; 

   // Upload vertices GPU 

   let vboGrid = buildGridPlaneXY(gl, attr_position, attr_color, 20, 0.1);
   let gridFactory = new EntityFactory(gl, vboGrid, u_model).setDrawLines();
   let gridXY = gridFactory.create();
   let gridXZ = gridFactory.create().rotate(90.0, [1.0, 0.0, 0.0]);


   let vboTetrahedron = buildTetrahedron(gl, attr_position, attr_color, 0.50);
   let TetrahedronFactory = new EntityFactory(gl, vboTetrahedron, u_model);
   let tetrahedron1 = TetrahedronFactory.create();
   tetrahedron1.translate(0.6, 0.25, 0.70).rotate(70, [5, 2, 3]);

   let tetrahedron2 = TetrahedronFactory.create();
   tetrahedron2.setDrawLineLoop();
   tetrahedron2.translate(1.0, 1.25, 0.9).rotate(70, [1, 2, 2]);

   let vboCube = buildCube(gl, attr_position, attr_color, 0.50);
   let cubeFactory = new EntityFactory(gl, vboCube, u_model);
   let cube1 = cubeFactory.create().translate(0.9, 0.60, 0.50).rotate(80, [1, 1, 1]);


   let vboTriangle = new Vbuffer(gl, attr_position, attr_color);
   vboTriangle.setData(vertices_triangle);
   const triangleFactory = new EntityFactory(gl, vboTriangle, u_model);

   let triangle1 = triangleFactory.create();
   triangle1.draw();

   let triangle2 = triangleFactory.create();
   triangle2.setPosition(0.60, 0.50, 0.0);
   triangle2.setScale(1.25);
   triangle2.draw();


   gl.enableVertexAttribArray(attr_position);
   gl.enableVertexAttribArray(attr_color);


   let camera = new Camera(gl, u_view, u_proj, u_camera_eye)
   camera.setPosition( [2, 2, 2 ]).lookAt( [0, 0, 0] );



   function drawScene()
   {
       // resizeCanvasToDisplaySize(gl.canvas);
       gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
       // Screen background color is black 
       // => clearColor(Red intensity from 0 to 1, Green , Blue , Alpha)
       gl.clearColor(0.0, 0.0, 0.0, 1.0);
       gl.clear(gl.COLOR_BUFFER_BIT);
       gl.clearDepth(1.0);         // Clear everything
       gl.enable(gl.DEPTH_TEST);   // Enable depth testing
       gl.depthFunc(gl.LEQUAL);    // Near things obscure far things
       gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);

       triangle1.draw();
       triangle2.draw();
       // triangleA.draw();
       // triangleB.draw();

       gridXY.draw(); 
       gridXZ.draw();

       tetrahedron1.draw();
       // tetrahedron2.draw();
       cube1.draw();

   }

   drawScene();

   const KEY_ARROW_LEFT  = 37; 
   const KEY_ARROW_RIGHT = 39;
   const KEY_ARROW_DOWN  = 40;
   const KEY_ARROW_UP    = 38;

   function precisionRound(vector, digits)
   {
     let factor = Math.pow(10, digits);
     return vector.map(x => Math.round(x * factor) / factor);
   }
 
   function keyboardListener(event)
   {
       /** @param {number} key */ 
       let key = event.keyCode; 

       // console.log(" [TRACE] type(event) = ", typeof(event));
       // console.log(" [TRACE] Event = ", event);

       if( key == KEY_ARROW_LEFT )
       {
         // triangle1.translate(-0.10, 0.0, 0.0);
         camera.rotateYaw(10.0);
       } 

       if( key == KEY_ARROW_RIGHT  )
       {
         // triangle1.translate(+0.10, 0.0, 0.0);
         camera.rotateYaw(-10.0);
       } 

       if( key == KEY_ARROW_UP  )
       {
         // triangle1.translate(0.0, 0.1, 0.0);
         camera.moveForward(0.2);
       } 

       if( key == KEY_ARROW_DOWN  )
       {
         // triangle1.translate(0.0, -0.1, 0.0);
         camera.moveForward(-0.2);
       } 

       // Elevate camera view, rotate the forward vector upwards 
       if( event.key == "a" )
       {
         camera.rotatePitch(10.0);
       }

       // Rotate camera view downwards 
       if( event.key == "s")
       {
         camera.rotatePitch(-10.0);
       }

       if( event.key == "q")
       {
         cube1.rotate(25, [1, 2, 1]); 
       }

       if( event.key == "w")
       {
         console.log(" Rotate -10 degrees ");
         triangle1.rotateZ(-10.0);
       }

       if( event.key == "o")
       {
         // triangle1.setVisible(true);
         cube1.setDrawTriangles();
       }

       if(  event.key == "p" )
       {
         // triangle1.setVisible(false);
         cube1.setDrawLineLoop();
       }

       // Move camera in the positive direction of Y axis 
       if( event.key === "j" ){ camera.moveCameraUpDow(+0.1); }
       // Move camera in the negative direction of Y axis 
       if( event.key === "k" ){ camera.moveCameraUpDow(-0.1); }

       // Make the vector forward parallel to the plane XZ
       if( event.key == "h"){ camera.parallelXZ(); }

       let eye =  precisionRound(camera.eye, 3);
       let up =   precisionRound(camera.up, 3);
       let forward = precisionRound(camera.forward, 3);
       document.querySelector("#output-camera").textContent = `eye = ${eye} ; up = ${up} ; forward = ${forward}`;

       drawScene();
   }

   addEventListener("keydown", keyboardListener);
#+END_SRC

** 3D - Quaternion

The class Transform has the fields, position for setting the object
position in world-coordinates; scale for adjusting object size and
rotation, which is a quaternion for encoding rotation around an
arbitrary axis. The use of quaternions saves the usage of many matrix
multiplications for each transform and also allows easier accumulation
of intermediate rotations.

Quaternions are also used in the camera class for providing
YRP (Yaw-Pitch-Roll) camera rotation and free movement in the
space. The keys arrow-left, arrow-right rotates the direction to where
camera is looking at to right or to the left. The key arrow-up, moves
the camera position in the direction to where the camera is looking
at. The key arrow-down moves the camera in the backward direction to
where the camera is looking at. The key 'S' rotates the camera up, the
key 'D' rotates the camera down.

The scene, has a blue square on the plane XY; a green square on plane
XZ; a teapot and torus. The torus can be moved, scaled and rotated in
the space by just changing its transform object parameters. By typing
the key 'B', the teapot scale is increased, and by typing 'N' its
scale is decrease. The keys 'J', 'L' rotates the teapot around the Y
axis. The key 'P' moves the teapot up across Y axis. The key 'O' moves
the teapot down across the Y axis.

Note: This code requires the GLUT library which provides the sample
wireframe teapot and torus.

  *Screenshots*

 [[file:images/opengl-draw3d-camera1.png][file:images/opengl-draw3d-camera1.png]]

 [[file:images/opengl-draw3d-camera2.png][file:images/opengl-draw3d-camera2.png]]


  *Code Highlights*

Transform class using quaternion:

#+BEGIN_SRC cpp

  // Transform object that combines - translation, scale and rotation (quaternion)
  struct Transform
  {
      glm::vec4 position = {0.0, 0.0, 0.0, 1.0};
      glm::vec3 scale    = {1.0, 1.0, 1.0};
      glm::quat rotation = {1.0, 0.0, 0.0, 0.0};

      void set_scale(float k){ scale[0]  = k; scale[1] = k;  scale[2]  = k;  }
      void add_scale(float k){ scale[0] += k; scale[1] += k; scale[2] += k;  }

      void set_position(float x, float y, float z)
      {  this->position = glm::vec4(x, y, z, 1.0);  };

      void set_rotation(float angle, glm::vec3 const& axis)
      { this->rotation = glm::angleAxis( glm::radians(angle), axis );  }

      void translate(float dx, float dy, float dz)
      {
          this->position[0] += dx;
          this->position[1] += dy;
          this->position[2] += dz;
      }

      // Rotation increment.
      void rotate(float angle, glm::vec3 const& axis)
      {
          auto q = glm::angleAxis( glm::radians(angle), axis );
          this->rotation = q * this->rotation;
      }

      // Get model affine transform - comprised of scale, translation and translation.
      glm::mat4 transform()
      {
          // Get transformation matrix from quaternion
          glm::mat4 trf = glm::mat4_cast(rotation);
          // Multiply all elements of column X axis scale
          trf[0] = scale[0] * glm::column(trf, 0);
          trf[1] = scale[1] * glm::column(trf, 1);
          trf[2] = scale[2] * glm::column(trf, 2);
          trf[3] = position;
          return trf;
      }
  };
#+END_SRC

Camera class using quaternions for rotating the camera:

#+BEGIN_SRC cpp
  struct Camera
  {
      // Current location of camera in world coordinates
      glm::vec3 eye  = { 2.0, 4.0, 5.0 };
      // Direction to where camera is looking at.
      glm::vec3 forward =  {-1.0, 0.0, -1.0 };
      // Current  camera up vector (orientation) - Y axis (default)
      glm::vec3 up = { 0.0, 1.0, 0.0 };

      // Field of view
      float fov_angle = glm::radians(60.0);
      // Aspect ratio
      float aspect   = 1.0;
      float zFar     = 20.0;
      float zNear    = 0.1;

      // ID of shader's view uniform variable - for setting view matrix
      GLuint shader_uniform_view;
      // ID of shader's projection uniform variable for setting projection matrix.
      GLuint shader_uniform_proj;

      Camera(GLuint uniform_view, GLuint uniform_proj, float aspect):
          shader_uniform_view(uniform_view), shader_uniform_proj(uniform_proj), aspect(aspect)
      {  update_view();  }

      void update_view()
      {
          // Point to where camera is looking at.
          auto cam_at = this->eye + this->forward;
          // Create View matrix (maps from world-space to camera-space)
          auto Tview = glm::lookAt(eye, cam_at, up);
          // Create projection matrix maps - camera-space to NDC (Normalized Device Coordinates).
          auto Tproj = glm::perspective( fov_angle, aspect, zNear, zFar );
          // Set shader uniform variables.
          glUniformMatrix4fv(shader_uniform_view, 1, GL_FALSE, glm::value_ptr(Tview) );
          glUniformMatrix4fv(shader_uniform_proj, 1, GL_FALSE, glm::value_ptr(Tproj) );
      }

      // Rotate around camera's Up vecto.
      void rotate_yaw(float angle)
      {
          // Build quaternion from axis angle
          glm::quat q = glm::angleAxis(glm::radians(angle), this->up);
          // Rotate current forward vector
          forward = forward * q;
          std::cout << " [FORWARD VECTOR ] " << glm::to_string(forward) << '\n';
          this->update_view();
      }

      // Rotate around camera's pitch axis (X axis) elevate camera view.
      void rotate_pitch(float angle)
      {
          glm::vec3 axis = glm::cross(this->up, this->forward);
          // Build quaternion from axis angle
          glm::quat q = glm::angleAxis(glm::radians(angle), axis);
          forward = q * forward;
          this->update_view();
      }

      void rotate_roll(float angle)
      {
          glm::quat q = glm::angleAxis(glm::radians(angle), this->forward);
          this->up = q * this->up;
          this->update_view();
      }

      // Move at forward vector direction (to where camera is looking at).
      void move_forward(float factor)
      {
          this->eye = this->eye + factor * this->forward;
          this->update_view();
      }
      // Move camera to specific point in the space.
      void set_position(float dx, float dy, float dz)
      {
          this->eye = glm::vec3(dx, dy, dz);
          this->update_view();
      }

      // Set position to where camera is looking at.
      void look_at(const glm::vec3& at)
      {
          this->forward = at - this->eye;
          this->update_view();
      }

  };
#+END_SRC


  *Files*

Vertex Shader Code:

#+BEGIN_SRC cpp
  #version 330 core

  layout ( location = 0)  in vec3 position;
  // layout ( location = 1) in vec3 color;
  out vec3 out_color;
  uniform mat4 u_model;       // Model matrix
  uniform mat4 u_view;        // Camera's view matrix
  uniform mat4 u_projection;  // Camera's projection matrix
  uniform vec3 u_color;       // Unique color to all vertices set by the C++-side

  void main()
  {
      gl_Position = u_projection * u_view * u_model * vec4(position, 1.0);
      // Forward to fragment shader
      out_color = u_color;  // color; // vec3(0.56, 0.6, 0.0);
  }
#+END_SRC

Fragment Shader Code:

#+BEGIN_SRC cpp
  #version 330

  in vec3 out_color;

  void main()
  {
      // Set vertex colors
      gl_FragColor =  vec4(out_color, 1.0);
  }
#+END_SRC

File: _CMakeLists.txt_


#+BEGIN_SRC cmake
   cmake_minimum_required(VERSION 3.5)
   project(GLFW_project)

   set(CMAKE_CXX_STANDARD 17)
   set(CMAKE_CXX_STANDARD_REQUIRED ON)
   set(CMAKE_VERBOSE_MAKEFILE ON)

   #================ GLFW Settings  ===============#

   find_package(OpenGL REQUIRED)

   include(FetchContent)

   # Set GLFW Options before FectchContent_MakeAvailable
   set( GLFW_BUILD_EXAMPLES OFF CACHE BOOL  "GLFW lib only" )
   set( GLFW_BUILD_TESTS    OFF CACHE BOOL  "GLFW lib only" )
   set( GLFW_BUILD_DOCS     OFF CACHE BOOL  "GLFW lib only" )
   set( GLFW_BUILD_INSTALL  OFF CACHE BOOL  "GLFW lib only" )

   # Donwload GLFW library
   FetchContent_Declare(
     glfwlib
     URL   https://github.com/glfw/glfw/releases/download/3.3.2/glfw-3.3.2.zip
   )
   FetchContent_MakeAvailable(glfwlib)

   # Download GLM (OpenGL math library for matrix and vectors transformation)
   FetchContent_Declare(
     glm
     URL  https://github.com/g-truc/glm/archive/0.9.8.zip
   )
   FetchContent_MakeAvailable(glm)

   message( [DEBUG] " glm_SOURCE_DIR = ${glm_SOURCE_DIR} ")
   include_directories(${glm_SOURCE_DIR})

     # ======= TARGETS ===========================#

          add_executable( draw3d-camera  draw3d-camera.cpp        )
   target_link_libraries( draw3d-camera  glfw OpenGL::GL GLU glut )
#+END_SRC


File: _draw3d-camera.cpp_

#+BEGIN_SRC cpp
  #include <iostream>
  #include <vector>
  #include <array>
  #include <cmath>
  #include <functional>
  #include <iomanip>

  // -------- OpenGL headers ---------//
  //
  #define GL_GLEXT_PROTOTYPES 1 // Necessary for OpenGL >= 3.0 functions
  #define GL3_PROTOTYPES      1 // Necessary for OpenGL >= 3.0 functions

  #include <GL/gl.h>
  #include <GLFW/glfw3.h>

  // #include <GL/glew.h>
  #include <GL/glu.h>

  // --------- OpenGL Math Librar ------------//
  #include <glm/glm.hpp>
  #include <glm/gtc/matrix_transform.hpp>
  #include <glm/gtc/type_ptr.hpp>
  #include <glm/gtx/string_cast.hpp>
  #include <glm/gtc/matrix_access.hpp>

  #define GLUT_ENABLED

  #if defined(GLUT_ENABLED)
      #include <GL/glut.h>
  #endif

  GLFWwindow* make_glfwWindowi(int  width, int height, const char* title);
  // Compile some  shader
  void compile_shader(GLuint m_program, const char* code, GLenum type);

  // ------------ Basic Data Structures -----------//

  // Wrapper for 2D vertex coordinates
  struct Vertex3D{  GLfloat x, y, z;  };
  // Wrapper for RGB colors
  struct ColorRGB { GLfloat r, g, b; };

  // Transform object that combines - translation, scale and rotation (quaternion)
  struct Transform
  {
      glm::vec4 position = {0.0, 0.0, 0.0, 1.0};
      glm::vec3 scale    = {1.0, 1.0, 1.0};
      glm::quat rotation = {1.0, 0.0, 0.0, 0.0};

      void set_scale(float k){ scale[0]  = k; scale[1] = k;  scale[2]  = k;  }
      void add_scale(float k){ scale[0] += k; scale[1] += k; scale[2] += k;  }

      void set_position(float x, float y, float z)
      {  this->position = glm::vec4(x, y, z, 1.0);  };

      void set_rotation(float angle, glm::vec3 const& axis)
      { this->rotation = glm::angleAxis( glm::radians(angle), axis );  }

      void translate(float dx, float dy, float dz)
      {
          this->position[0] += dx;
          this->position[1] += dy;
          this->position[2] += dz;
      }

      // Rotation increment.
      void rotate(float angle, glm::vec3 const& axis)
      {
          auto q = glm::angleAxis( glm::radians(angle), axis );
          this->rotation = q * this->rotation;
      }

      // Get model affine transform - comprised of scale, translation and translation.
      glm::mat4 transform()
      {
          // Get transformation matrix from quaternion
          glm::mat4 trf = glm::mat4_cast(rotation);
          // Multiply all elements of column X axis scale
          trf[0] = scale[0] * glm::column(trf, 0);
          trf[1] = scale[1] * glm::column(trf, 1);
          trf[2] = scale[2] * glm::column(trf, 2);
          trf[3] = position;
          return trf;
      }
  };

  struct Camera
  {
      // Current location of camera in world coordinates
      glm::vec3 eye  = { 2.0, 4.0, 5.0 };
      // Direction to where camera is looking at.
      glm::vec3 forward =  {-1.0, -1.0, -1.0 };
      // Current  camera up vector (orientation) - Y axis (default)
      glm::vec3 up = { 0.0, 1.0, 0.0 };

      // Field of view
      float fov_angle = glm::radians(60.0);
      // Aspect ratio
      float aspect   = 1.0;
      float zFar     = 20.0;
      float zNear    = 0.1;

      // ID of shader's view uniform variable - for setting view matrix
      GLuint shader_uniform_view;
      // ID of shader's projection uniform variable for setting projection matrix.
      GLuint shader_uniform_proj;

      Camera(GLuint uniform_view, GLuint uniform_proj, float aspect):
          shader_uniform_view(uniform_view), shader_uniform_proj(uniform_proj), aspect(aspect)
      {  update_view();  }

      void update_view()
      {
          // Point to where camera is looking at.
          auto cam_at = this->eye + this->forward;
          // Create View matrix (maps from world-space to camera-space)
          auto Tview = glm::lookAt(eye, cam_at, up);
          // Create projection matrix maps - camera-space to NDC (Normalized Device Coordinates).
          auto Tproj = glm::perspective( fov_angle, aspect, zNear, zFar );
          // Set shader uniform variables.
          glUniformMatrix4fv(shader_uniform_view, 1, GL_FALSE, glm::value_ptr(Tview) );
          glUniformMatrix4fv(shader_uniform_proj, 1, GL_FALSE, glm::value_ptr(Tproj) );
      }

      // Rotate around camera's Up vecto.
      void rotate_yaw(float angle)
      {
          // Build quaternion from axis angle
          glm::quat q = glm::angleAxis(glm::radians(angle), this->up);
          // Rotate current forward vector
          forward = forward * q;
          std::cout << " [FORWARD VECTOR ] " << glm::to_string(forward) << '\n';
          this->update_view();
      }

      // Rotate around camera's pitch axis (X axis) elevate camera view.
      void rotate_pitch(float angle)
      {
          glm::vec3 axis = glm::cross(this->up, this->forward);
          // Build quaternion from axis angle
          glm::quat q = glm::angleAxis(glm::radians(angle), axis);
          forward = q * forward;
          this->update_view();
      }

      void rotate_roll(float angle)
      {
          glm::quat q = glm::angleAxis(glm::radians(angle), this->forward);
          this->up = q * this->up;
          this->update_view();
      }

      // Move at forward vector direction (to where camera is looking at).
      void move_forward(float factor)
      {
          this->eye = this->eye + factor * this->forward;
          this->update_view();
      }
      // Move camera to specific point in the space.
      void set_position(float dx, float dy, float dz)
      {
          this->eye = glm::vec3(dx, dy, dz);
          this->update_view();
      }

      // Set position to where camera is looking at.
      void look_at(const glm::vec3& at)
      {
          this->forward = at - this->eye;
          this->update_view();
      }

  };

  void send_buffer( GLuint* pVao, GLuint* pVbo, GLsizei sizeBuffer
          , void* pBufffer, GLint   shader_attr, GLint size, GLenum type);


  constexpr ColorRGB color_red   = {1.0, 0.0, 0.0};
  constexpr ColorRGB color_green = {0.0, 1.0, 0.0};
  constexpr ColorRGB color_blue  = {0.0, 0.0, 1.0};
  constexpr ColorRGB color_yellow    = { 0.80,  1.000, 0.100 };
  constexpr ColorRGB color_gray       = { 0.47,  0.390, 0.380 };
  constexpr ColorRGB color_dark_green = { 0.027, 0.392, 0.050 };
  constexpr ColorRGB color_dark_blue  = { 0.109, 0.066, 0.411 };
  constexpr ColorRGB color_white      = { 1.0, 1.0, 1.0 };

  const glm::vec3 AXIS_X = { 1.0, 0.0, 0.0 };
  const glm::vec3 AXIS_Y = { 0.0, 1.0, 0.0 };
  const glm::vec3 AXIS_Z = { 0.0, 0.0, 1.0 };

  int main(int argc, char** argv)
  {

      /* Initialize the library */
      if (!glfwInit()){ return -1; }

      #if defined(GLUT_ENABLED)
          glutInit(&argc, argv);
      #endif
                                                //
      GLFWwindow* window = make_glfwWindowi(640, 480, "OpenGL quaternion");
      // Note: The shader source code is at the end of file.
      extern const char* code_vertex_shader;
      extern const char* code_frag_shader;

      // ====== S H A D E R - C O M P I L A T I O N ====//
      //
      GLuint prog = glCreateProgram();
      // Compile shader code
      compile_shader(prog, code_vertex_shader, GL_VERTEX_SHADER  ) ;
      compile_shader(prog, code_frag_shader,   GL_FRAGMENT_SHADER );
      glUseProgram(prog);

      // --------- Shader Uniform Variables -------//
      const GLint u_proj  = glGetUniformLocation(prog, "u_projection");
      const GLint u_view  = glGetUniformLocation(prog, "u_view");
      const GLint u_model = glGetUniformLocation(prog, "u_model");
      const GLint u_color = glGetUniformLocation(prog, "u_color");
      assert( u_color >= 0 );
      // ------- Shader attribute locations -------//
      const GLint attr_position = glGetAttribLocation(prog, "position");
      const GLint attr_color    = glGetAttribLocation(prog, "color");


      // ============== Set Shader Uniform Variables =============//
      //                                                          //

      int width, height;
      glfwGetWindowSize(window, &width, &height);
      // Window aspect ratio
      float aspect = static_cast<float>(width) / height;
      // Identity matrix
      const auto identity = glm::mat4(1.0);
      // Set projection matrix uniform variable
      glUniformMatrix4fv(u_proj, 1, GL_FALSE, glm::value_ptr(identity) );

      // ====== U P L O A D - TO - G P U =========================//

      Camera camera(u_view, u_proj, width / height);

      // Plane XY grid (vertical) - contains axis X and Y
      Transform model_grid_xy;
      model_grid_xy.set_position(0.0, 0.0, -2.0);

      // Plane XZ grid (horizontal) - contains axis X and Z
      Transform model_grid_xz;
      model_grid_xz.set_rotation(90.0f, AXIS_X);

      Transform model_teapot;
      model_teapot.set_scale(0.4);
      model_teapot.set_position(0.0, 0.50, 0.2);
      model_teapot.set_rotation(-90.0, glm::vec3(1.0, 0.0, 0.0) );


      auto plane_vertices = std::vector<Vertex3D>{
            {0.0, 0.0, 0.0}, {4.0, 0.0, 0.0}
          , {4.0, 0.0, 4.0} ,{0.0, 0.0, 4.0}
      };

      GLuint vao_plane          = 0;
      GLuint vbo_plane_vertices = 0;
      send_buffer( &vao_plane, &vbo_plane_vertices, sizeof(Vertex3D) * plane_vertices.size()
                  , plane_vertices.data(), attr_position, 3, GL_FLOAT );

      Transform model_plane_xz;
      model_plane_xz.set_position(-2, 0, -2);

      Transform model_plane_xy;
      model_plane_xy.set_position(-2, 0, -2);
      model_plane_xy.set_rotation(-90.0, AXIS_X);

      Transform model_torus;
      model_torus.set_position(-3, 2, 0);
      model_torus.rotate(90.0, AXIS_Y);

      //  ======= R E N D E R  - L O O P ============//
      //                                             //
      while ( !glfwWindowShouldClose(window) )
      {
          glClear(GL_COLOR_BUFFER_BIT  | GL_DEPTH_BUFFER_BIT);

          // ====== BEGIN RENDERING ============//

              // ---- Draw horizontal plane XZ (square) ------------//
              glUniformMatrix4fv(u_model, 1, GL_FALSE, glm::value_ptr(model_plane_xz.transform()) );
              glUniform3fv(u_color, 1, &color_green.r );
              glBindVertexArray(vao_plane);
              glDrawArrays(GL_QUADS, 0, 4 );

              // ---- Draw vertical plane XY (square) ------------//
              glUniformMatrix4fv(u_model, 1, GL_FALSE, glm::value_ptr(model_plane_xy.transform()) );
              glUniform3fv(u_color, 1, &color_blue.r );
              glBindVertexArray(vao_plane);
              glDrawArrays(GL_QUADS, 0, 4 );

              // ----- Draw teapot over plane -----------------------//
              glUniformMatrix4fv(u_model, 1, GL_FALSE, glm::value_ptr(model_teapot.transform()) );
              glUniform3fv(u_color, 1, &color_red.r );
              glutWireTeapot(2.5);

              // ----- Draw Torus ------------------------------------//
              glUniformMatrix4fv(u_model, 1, GL_FALSE, glm::value_ptr(model_torus.transform()) );
              glUniform3fv(u_color, 1, &color_dark_green.r );
              glutWireTorus(1.0f, 2.00f, 32, 32);

          // ====== END RENDERING ==============//

          /* Swap front and back buffers */
          glfwSwapBuffers(window);
          /* Poll for and process events */
          glfwWaitEvents();

          if( glfwGetKey(window, 'Q' ) == GLFW_PRESS )
          {
               std::cout << " [TRACE] User typed Q =>> Shutdown program. Ok. " << '\n';
               break;
          }

          // Rotate camera around its Z axis or forward vector
          if( glfwGetKey(window, 'T') == GLFW_PRESS ){ camera.rotate_roll(+5.0);  }
          if( glfwGetKey(window, 'Y') == GLFW_PRESS ){ camera.rotate_roll(-5.0); }

          // Rotate camera around its local X axis
          if( glfwGetKey(window, 'S') == GLFW_PRESS ){ camera.rotate_pitch(+5.0); }
          if( glfwGetKey(window, 'D') == GLFW_PRESS ){ camera.rotate_pitch(-5.0); }


          // Rotate camera.
          if( glfwGetKey(window, GLFW_KEY_UP ) == GLFW_PRESS   ){ camera.move_forward(+0.2);  }
          if( glfwGetKey(window, GLFW_KEY_DOWN ) == GLFW_PRESS ){ camera.move_forward(-0.2);  }
          if( glfwGetKey(window, GLFW_KEY_RIGHT) == GLFW_PRESS  ){ camera.rotate_yaw(+10.0);   }
          if( glfwGetKey(window, GLFW_KEY_LEFT ) == GLFW_PRESS  ){ camera.rotate_yaw(-10.0);   }

          // Reset camera - look at origin
          if( glfwGetKey(window, 'R' ) == GLFW_PRESS  ){
              camera.up = AXIS_Y;
              camera.set_position(4.0, 3.0, 2.5);
              camera.look_at( {0.0, 0.0, 0.0} );
          }

          // Rotate teapot
          if( glfwGetKey(window,  'H' ) == GLFW_PRESS   ){ model_teapot.rotate(+10.0, AXIS_Y) ;  }
          if( glfwGetKey(window,  'K' ) == GLFW_PRESS   ){ model_teapot.rotate(-10.0, AXIS_Y) ;  }
          // Move teapot up and down across Y axis
          if( glfwGetKey(window,  'P' ) == GLFW_PRESS   ){ model_teapot.translate(0.0, +0.1, 0.0) ;  }
          if( glfwGetKey(window,  'O' ) == GLFW_PRESS   ){ model_teapot.translate(0.0, -0.1, 0.0) ;  }

          // Decrease or increase Teapot size
          if( glfwGetKey(window,  'B' ) == GLFW_PRESS   ){ model_teapot.add_scale(+0.1) ;  }
          if( glfwGetKey(window,  'N' ) == GLFW_PRESS   ){ model_teapot.add_scale(-0.1) ;  }

      }

      glfwTerminate();
      return 0;

  } // --- End of main() -----//

  // ---------- S H A D E R - P R O G R A M S  -------------------------//
  //

  // Minimal vertex shader =>> Runs on the GPU and processes each vertex.
  const char* code_vertex_shader = R"(
      #version 330 core

      layout ( location = 0)  in vec3 position;
      // layout ( location = 1) in vec3 color;
      out vec3 out_color;
      uniform mat4 u_model;       // Model matrix
      uniform mat4 u_view;        // Camera's view matrix
      uniform mat4 u_projection;  // Camera's projection matrix
      uniform vec3 u_color;       // Unique color to all vertices set by the C++-side

      void main()
      {
          gl_Position = u_projection * u_view * u_model * vec4(position, 1.0);
          // Forward to fragment shader
          out_color = u_color;  // color; // vec3(0.56, 0.6, 0.0);
      }

  )";

  // Fragment shader source code
  const char* code_frag_shader = R"(
      #version 330

      in vec3 out_color;

      void main()
      {
          // Set vertex colors
          gl_FragColor =  vec4(out_color, 1.0);
      }
  )";

      // ====== I M P L E M E N T A T I O N S ==========//

  GLFWwindow*
  make_glfwWindowi(int  width, int height, const char* title)
  {

      glfwSetErrorCallback([](int error, const char* description)
                           { std::fprintf( stderr, " [GLFW ERROR] Error = %d ; Description = %s \n"
                                          , error, description);
                           });

      GLFWwindow* window = glfwCreateWindow(width, height, title, NULL, NULL);
      assert( window != nullptr && "Failed  to create Window");

      // OpenGL context
      glfwMakeContextCurrent(window);
      // Pain whole screen as black - dark screen colors are better
      // for avoding eye strain due long hours staring on monitor.
      glClearColor(0.0f, 0.0f, 0.0f, 1.0f);

      // Set - OpenGL Core Profile - version 3.3
      glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
      glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
      glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);

      glEnable(GL_COLOR_MATERIAL);
      glEnable(GL_DEPTH_TEST);
      glEnable(GL_BLEND);

      return window;
  }

  void compile_shader(GLuint m_program, const char* code, GLenum type)
  {
      GLint shader_id = glCreateShader( type );
      glShaderSource(shader_id, 1, &code, nullptr);
      glCompileShader(shader_id);

      GLint is_compiled = GL_FALSE;
      // Check shader compilation result
      glGetShaderiv(shader_id, GL_COMPILE_STATUS, &is_compiled);

      // If there is any shader compilation result,
      // print the error message.
      if( is_compiled == GL_FALSE)
      {
          std::cerr << " [ERROR] Shader compilation error. " << '\n';
          std::abort();
      }

      glAttachShader(m_program, shader_id);
      glDeleteShader(shader_id);
      glLinkProgram(m_program);
      GLint link_status = GL_FALSE;
      glGetProgramiv(m_program, GL_LINK_STATUS, &link_status);
      assert( link_status != GL_FALSE );
      // glUseProgram(m_program);
  }

  // Upload buffer from main memory to GPU VBO
  // =>> Parameters VAO, VBO are allocated by the caller.
  void send_buffer( GLuint* pVao, GLuint* pVbo, GLsizei sizeBuffer
                  , void* pBufffer, GLint   shader_attr, GLint size
                  , GLenum type)
  {
      assert(pVao != nullptr);
      assert(pVbo != nullptr);
      GLuint& vao = *pVao;
      GLuint& vbo = *pVbo;
      // Generate and bind current VAO (Vertex Array Object)
      if(vao == 0){ glGenVertexArrays(1, &vao); }
      glBindVertexArray(vao);
      // Generate and bind current VBO (Vertex Buffer Object)
      glGenBuffers(1, &vbo);
      glBindBuffer(GL_ARRAY_BUFFER, vbo);
      // Upload data to current VBO buffer in GPU
      glBufferData(GL_ARRAY_BUFFER, sizeBuffer, pBufffer, GL_STATIC_DRAW);
      glEnableVertexAttribArray(shader_attr);
      // Set data layout - how data will be interpreted.
      glVertexAttribPointer(shader_attr, size, type, GL_FALSE, 0, nullptr);
      // ------ Disable Global state set by this function -----//
      // Unbind VAO
      glBindVertexArray(0);
      // Unbind VBO
      glBindBuffer(GL_ARRAY_BUFFER, vbo);
      // Disable current shader attribute
      glDisableVertexAttribArray(shader_attr);

  }
#+END_SRC
** 3D - Surface wireframe chart  

This code draws a wireframe surface of a function of two variables
y = f(x, z) using GL_LINE primitives. 

\begin{equation} 
   y = f(x, z) = \frac{10 \cdot \sin x \cdot \sin z}{z}
\end{equation}

 *Screenshot* 

#+CAPTION: 3D surface wireframe chart of f(x, z)
[[file:images/opengl-draw3d-surface-wireframe-plot.png][file:images/opengl-draw3d-surface-wireframe-plot.png]]


 *Code Highlights* 

Data structures: 

#+BEGIN_SRC cpp 

  struct Position { GLfloat x, y, z;  };
  struct Normal   { GLfloat x, y, z;  };
  struct Color    { GLfloat r, g, b;  };

  struct Vertex   {  
      Position position; // Vertex position in space 
      Normal   normal;   // Vertex normal vector for illumination (Future use)
      Color    color;    // Vertex color (RGB)
  };


  // Mesh - set of vertices (position and attributes) that represents 
  // some surface or solid. 
  struct Mesh 
  {
      std::vector<Vertex> vertices{};
      std::vector<GLuint> indices{}; 
      GLfloat line_width = 1.0;
      GLenum  draw_type; 
      GLuint vao = 0, vbo = 0, ibo = 0;
    
      // Draw mesh using indices 
      void draw_indices()
      {
          // Check wheter the mesh was sent to GPU.
          assert( vao != 0 && ibo != 0 );
          // Note: The subroutine glLineWidth is stateful!!
          glLineWidth(line_width);
          glBindVertexArray(vao);
          glDrawElements(draw_type, indices.size(), GL_UNSIGNED_INT, nullptr);
      }

      // Draw mesh using vertices 
      void draw_vertices()
      {
          assert( vao != 0 );
          glLineWidth(line_width);       
          glBindVertexArray(vao);
          glDrawArrays(draw_type, 0, vertices.size());
      }
  };
#+END_SRC

Subroutine for generating surface mesh: 

#+BEGIN_SRC cpp 
  Mesh generate_surface_wireframe_mesh(  
                               float xmin, float xmax
                             , float zmin, float zmax
                             , size_t Nx, size_t Nz
                             , Color color 
                             , SurfaceFunction const& function)
  {
      assert( xmax > xmin );
      assert( zmax > zmin );
      assert( Nx  != 0 && Nz  != 0 );

      std::cout << " [TRACE] Nx = " << Nx << " ; Nz = " << Nz << '\n';

      Mesh mesh;
      mesh.draw_type = GL_LINES;

      float dx = (xmax - xmin) / Nx;
      float dz = (zmax - zmin) / Nz; 

      // Intial allocation for avoiding many dynamic allocations
      mesh.vertices.reserve( Nx * Nz );
      mesh.indices.reserve( 2 * Nx * Nz  );
      float x, y, z, y_h;

      // Draw lines parallel to plane XY  
      for(size_t j = 0; j < Nz; j++)
      {   for(size_t i = 0; i < Nx; i++)
          {
              x = xmin + i * dx;
              z = zmin + j * dz; 
              y = function(x, z);
              y_h = function(x + dx, z);

              // Draw line segments in planes parallel to plane XY.
              //---------------------------------------------------------------------//
              // Initial vertex of line  segment
              mesh.vertices.push_back( Vertex{ Position{x, y, z}, zero_normal, color } );        
              // Final vertex of line segment 
              mesh.vertices.push_back( Vertex{ Position{x + dx, y_h, z}, zero_normal, color } );

          }
      }

      for(size_t i = 0; i < Nx; i++)
      {   for(size_t j = 0; j < Nz; j++)
          {
              x = xmin + i * dx;
              z = zmin + j * dz; 
              y = function(x, z);
              y_h = function(x, z + dz);

              // Draw line segments in planes parallel to plane YZ.
              mesh.vertices.push_back( Vertex{ Position{x, y, z       }, zero_normal, color } );
              mesh.vertices.push_back( Vertex{ Position{x, y_h, z + dz}, zero_normal, color } );

           }
      }
      return mesh;
  }

#+END_SRC

Vertex shader: 

#+BEGIN_SRC cpp 
  #version 330 core 

  layout ( location = 0)  in vec3 position;
  layout ( location = 1) in vec3 color;   

  out vec3 out_color;
  out vec3 out_coord; 
  out vec3 out_eye;

  uniform vec3 u_eye;  // Cameras's position in space
  uniform mat4 u_model;       // Model matrix 
  uniform mat4 u_view;        // Camera's view matrix    
  uniform mat4 u_projection;  // Camera's projection matrix 


  void main()
  {
      gl_Position = u_projection * u_view * u_model * vec4(position, 1.0);

      // Forward to fragment shader 
      out_coord  = position;
      out_color = color; // vec3(0.56, 0.6, 0.0);
      out_eye = u_eye;
  }
#+END_SRC


Fragment shader: 

#+BEGIN_SRC cpp 
  #version 330 

  in vec3 out_color;
  in vec3 out_coord;
  in vec3 out_eye; 

  void main()
  {
      float k;

      if(gl_FrontFacing)
          k = 1.0; // 2.0;
      else 
          k = 0.1; // 0.01;

      float factor = distance(out_eye, out_coord) ;

      // Set vertex colors
      vec3 color = (out_coord / 2.0 / factor ) + k * out_color * ( 20.0 / factor ) ;
      // vec3 color =  k * out_color * ( 20.0 / factor ) ;

      // vec3 color = 1.0 / fac + (out_color / 2.0 + 0.5) * k;

      gl_FragColor =  vec4( color , 1.0);
      // gl_FragColor = vec4(0.3, 0.6, 0.0, 1.0);
  }
#+END_SRC

 *Source code* 

File: _CMakeLists.txt_ 

#+BEGIN_SRC cmake 
   cmake_minimum_required(VERSION 3.5)
   project(OpenGL_3D_Wireframe_Surface_Chart)

   set(CMAKE_CXX_STANDARD 17)
   set(CMAKE_CXX_STANDARD_REQUIRED ON)
   set(CMAKE_VERBOSE_MAKEFILE ON)

   #================ GLFW Settings  ===============#

   find_package(OpenGL REQUIRED)

   include(FetchContent)

   # Set GLFW Options before FectchContent_MakeAvailable
   set( GLFW_BUILD_EXAMPLES OFF CACHE BOOL  "GLFW lib only" )
   set( GLFW_BUILD_TESTS    OFF CACHE BOOL  "GLFW lib only" )
   set( GLFW_BUILD_DOCS     OFF CACHE BOOL  "GLFW lib only" )
   set( GLFW_BUILD_INSTALL  OFF CACHE BOOL  "GLFW lib only" )

   # Donwload GLFW library
   FetchContent_Declare(
     glfwlib
     URL   https://github.com/glfw/glfw/releases/download/3.3.2/glfw-3.3.2.zip
   )
   FetchContent_MakeAvailable(glfwlib)

   # Download GLM (OpenGL math library for matrix and vectors transformation)
   FetchContent_Declare(
     glm
     URL  https://github.com/g-truc/glm/archive/0.9.8.zip
   )
   FetchContent_MakeAvailable(glm)
   include_directories(${glm_SOURCE_DIR})


   # Download pre-compiled GLEW when building under Windows NT OS (x64)
   IF(WIN32)
      FetchContent_Declare(
         glew-release 
         URL    https://ufpr.dl.sourceforge.net/project/glew/glew/2.1.0/glew-2.1.0-win32.zip 
         # https://github.com/nigels-com/glew/archive/glew-2.2.0.zip
      )
      FetchContent_MakeAvailable(glew-release)
      include_directories( ${glew-release_SOURCE_DIR}/include  ${glm_SOURCE_DIR} )        
      link_directories(  ${glew-release_SOURCE_DIR}/lib/Release/x64 )

      set( GLEW_LIB_PATH1 ${glew-release_SOURCE_DIR}/lib/Release/x64/glew32.lib )
      set( GLEW_LIB_PATH2 ${glew-release_SOURCE_DIR}/lib/Release/x64/glew32s.lib )
   ENDIF()


   MACRO(ADD_OPENGL_APP target sources)
      add_executable( ${target} ${sources} )
      message([TRACE] " Add OpenGL executable = ${target} ")

      target_link_libraries( ${target} glfw
                                       OpenGL::GL
                                       ${GLEW_LIB_PATH1}
                                       ${GLEW_LIB_PATH2} )

       IF(MINGW)
           # Statically link against MINGW dependencies
           # for making easier to deploy on other machines. 
           target_link_options( ${target} PRIVATE                                 
                                    -static-libgcc
                                    -static-libstdc++
                                    -Wl,-Bstatic,--whole-archive -lwinpthread
                                      -Wl,--no-whole-archive				     
                                    )
        ENDIF()       


        # Copy GLEW DLL shared library to same directory as the executable.		 
        IF(WIN32)				 
           add_custom_command(TARGET ${target} POST_BUILD 
                          COMMAND ${CMAKE_COMMAND} -E copy_if_different
                          "${glew-release_SOURCE_DIR}/bin/Release/x64/glew32.dll"              
                          $<TARGET_FILE_DIR:${target}>)
        ENDIF()	   				   
   ENDMACRO()     

        # ======= T A R G E T S ============================#
        #                                                   #

   ADD_OPENGL_APP( draw3d-plot-surface draw3d-plot-surface.cpp )
#+END_SRC


File: _draw3d-plot-surface.cpp_ 

#+BEGIN_SRC cpp 
  #include <iostream>
  #include <string> 
  #include <sstream>
  #include <vector> 
  #include <array>
  #include <cmath>
  #include <functional>
  #include <thread>

  // -------- OpenGL headers ---------//
  //
  #define GL_GLEXT_PROTOTYPES 1 // Necessary for OpenGL >= 3.0 functions
  #define GL3_PROTOTYPES      1 // Necessary for OpenGL >= 3.0 functions

  #include <GL/gl.h>
  #include <GLFW/glfw3.h>

  // #include <GL/glew.h>
  #include <GL/glu.h>

  // --------- OpenGL Math Librar ------------//
  #include <glm/glm.hpp>
  #include <glm/gtc/matrix_transform.hpp>
  #include <glm/gtc/type_ptr.hpp>
  #include <glm/gtx/string_cast.hpp>

  // #define GLUT_ENABLED 

  #if defined(GLUT_ENABLED)
      #include <GL/glut.h>
  #endif 

  #define GL_CHECK(funcall)\
      do { \
          (funcall); \
          GLint error = glGetError();  \
          if(error == GL_NO_ERROR){ break; } \
          std::fprintf(stderr, " [OPENGL ERROR] Error code = %d ; line = %d ; call = '%s'  \n" \
                        , error, __LINE__, #funcall ); \
          abort(); \
      } while(0)

  GLFWwindow* make_glfwWindow(int  width, int height, const char* title);

  // Compile some  shader 
  void compile_shader(GLuint m_program, const char* code, GLenum type);

  // ------------ Basic Data Structures -----------// 
  struct Position { GLfloat x, y, z;  };
  struct Normal   { GLfloat x, y, z;  };
  struct Color    { GLfloat r, g, b;  };

  struct Vertex   {  
      Position position; // Vertex position in space 
      Normal   normal;   // Vertex normal vector for illumination (Future use)
      Color    color;    // Vertex color (RGB)
  };

  std::ostream& operator<<(std::ostream& os, Vertex const& v)
  {
      return os << " Vertex{ x = " << v.position.x << " ; " 
                << " y = " << v.position.y << " ; z = " << v.position.z << " } ";
  }

  // Mesh - set of vertices (position and attributes) that represents 
  // some surface or solid. 
  struct Mesh 
  {
      std::vector<Vertex> vertices{};
      std::vector<GLuint> indices{}; 
      GLfloat line_width = 1.0;
      GLenum  draw_type; 
      GLuint vao = 0, vbo = 0, ibo = 0;
    
      // Draw mesh using indices 
      void draw_indices()
      {
          // Check wheter the mesh was sent to GPU.
          assert( vao != 0 && ibo != 0 );
          // Note: The subroutine glLineWidth is stateful!!
          glLineWidth(line_width);
          glBindVertexArray(vao);
          glDrawElements(draw_type, indices.size(), GL_UNSIGNED_INT, nullptr);
      }

      // Draw mesh using vertices 
      void draw_vertices()
      {
          assert( vao != 0 );
          glLineWidth(line_width);       
          glBindVertexArray(vao);
          glDrawArrays(draw_type, 0, vertices.size());
      }
  };


  // Send/upload mesh data to GPU 
  // attr_position =>> Shader attribute location for vertex position 
  // attr_color    =>> Shader attribute location for vertex color 
  void send_mesh( Mesh& mesh , GLint attr_position , GLint attr_color ); 

  constexpr Position origin = { 0.0, 0.0, 0.0};
  constexpr Position position_x(float x){ return {x, 0.0, 0.0};  }
  constexpr Position position_y(float y){ return {0.0, y, 0.0};  }
  constexpr Position position_z(float z){ return {0.0, 0.0, z};  }

  constexpr Normal zero_normal = { 0.0, 0.0 , 0.0};
  constexpr Color  color_red   = {1.0, 0.0, 0.0};
  constexpr Color  color_green = {0.0, 1.0, 0.0};        
  constexpr Color  color_blue  = {0.0, 0.0, 1.0};

  const glm::mat4 matrix_identity = glm::mat4(1.0);


  struct Camera
  {
      // Current location of camera in world coordinates
      glm::vec3 eye  = { 2.0, 4.0, 5.0 };
      // Direction to where camera is looking at.
      glm::vec3 forward =  {-1.0, 0.0, -1.0 };
      // Current  camera up vector (orientation) - Y axis (default)
      glm::vec3 up = { 0.0, 1.0, 0.0 };

      // Field of view
      float fov_angle = glm::radians(60.0);
      // Aspect ratio
      float aspect   = 1.0;
      float zFar     = 100.0;
      float zNear    = 0.1;

      // ID of shader's view uniform variable - for setting view matrix
      GLint shader_uniform_view = -1;
      // ID of shader's projection uniform variable for setting projection matrix.
      GLint shader_uniform_proj = -1;

      GLint shader_uniform_eye = -1;

      Camera(GLuint uniform_view, GLuint uniform_proj, GLuint uniform_eye, float aspect):
           shader_uniform_view(uniform_view)
         , shader_uniform_proj(uniform_proj)
         , shader_uniform_eye(uniform_eye)
         , aspect(aspect)
      {  update_view();  }

      void update_view()
      {

          // Point to where camera is looking at.
          auto cam_at = this->eye + this->forward;
          // Create View matrix (maps from world-space to camera-space)
          auto Tview = glm::lookAt(eye, cam_at, up);
          // Create projection matrix maps - camera-space to NDC (Normalized Device Coordinates).
          auto Tproj = glm::perspective( fov_angle, aspect, zNear, zFar );
          // Set shader uniform variables.
          glUniformMatrix4fv(shader_uniform_view, 1, GL_FALSE, glm::value_ptr(Tview) );
          glUniformMatrix4fv(shader_uniform_proj, 1, GL_FALSE, glm::value_ptr(Tproj) );
          glUniform3fv(shader_uniform_eye, 1, glm::value_ptr(eye));
      }

      // Rotate around camera's Up vecto.
      void rotate_yaw(float angle)
      {
          // Build quaternion from axis angle
          glm::quat q = glm::angleAxis(glm::radians(angle), this->up);
          // Rotate current forward vector
          forward = forward * q;
          // std::cout << " [FORWARD VECTOR ] " << glm::to_string(forward) << '\n';
          this->update_view();
      }

      // Rotate around camera's pitch axis (X axis) elevate camera view.
      void rotate_pitch(float angle)
      {
          glm::vec3 axis = glm::cross(this->up, this->forward);
          // Build quaternion from axis angle
          glm::quat q = glm::angleAxis(glm::radians(angle), axis);
          forward = q * forward;
          this->update_view();
      }

        // Move at forward vector direction (to where camera is looking at).
      void move_forward(float factor)
      {
          this->eye = this->eye + factor * this->forward;
          this->update_view();
      }
      // Move camera to specific point in the space.
      void set_position(float x, float y, float z)
      {
          this->eye = glm::vec3(x, y, z);
          this->update_view();
      }

      // Set position to where camera is looking at.
      void look_at(const glm::vec3& at)
      {
          this->forward = at - this->eye;
          this->update_view();
      }

  };

  using SurfaceFunction = std::function<float (float, float)>;

  // Nx - Number of points in the Xmin, Xmax interval 
  // Ny - Number of points in the Ymin, Ymax interval 
  Mesh generate_surface_wireframe_mesh(  
                               float xmin, float xmax
                             , float zmin, float zmax
                             , size_t Nx, size_t Nz
                             , Color color 
                             , SurfaceFunction const& function);

  int main(int argc, char** argv)
  {

      /* Initialize the library */
      if (!glfwInit()){ return -1; }

      #if defined(GLUT_ENABLED)
          glutInit(&argc, argv);
      #endif

      // ====== S H A D E R - C O M P I L A T I O N ====//
      //                                                // 
    
      GLFWwindow* window = make_glfwWindow(640, 480, "Plot 3D surface");

      // Note: The shader source code is at the end of file.
      extern const char* code_vertex_shader;
      extern const char* code_frag_shader;

      GLuint prog = glCreateProgram();
      // Compile shader code 
      compile_shader(prog, code_vertex_shader, GL_VERTEX_SHADER  ) ;    
      compile_shader(prog, code_frag_shader,   GL_FRAGMENT_SHADER );
      glUseProgram(prog);

      // Get shader uniform variable location for projection matrix
      // See shader code: "uniform mat4 projection;"
      const GLint u_proj  = glGetUniformLocation(prog, "u_projection");
      assert( u_proj >= 0 && "Failed to find u_projection uniform variable" );

      const GLint u_view = glGetUniformLocation(prog, "u_view");
      assert( u_proj >= 0 && "Failed to find u_view uniform variable" );

      // Get shader uniform variable  location for model matrix.
      const GLint u_model  = glGetUniformLocation(prog, "u_model");
      assert( u_model >= 0 && "Failed to find uniform variable" );  

      const GLint u_camera_eye = glGetUniformLocation(prog, "u_eye");
      printf(" [TRACE] u_camera_eye = %d \n", u_camera_eye);
      assert( u_camera_eye >= 0 );  

      // Get shader attribute location - the function glGetAttribLocation - returns (-1) on error.
      const GLint attr_position = glGetAttribLocation(prog, "position");
      assert( attr_position >= 0 && "Failed to get attribute location" );

      // Get shader attribute of color 
      const GLint attr_color = glGetAttribLocation(prog, "color");
      if( attr_color < 0){ std::fprintf(stderr, " [WARNING] Shader color attribute location not found. \n"); };

      glUniformMatrix4fv(u_model, 1, GL_FALSE, glm::value_ptr(matrix_identity) );
      glUniformMatrix4fv(u_proj,  1, GL_FALSE, glm::value_ptr(matrix_identity) );

      // ====== U P L O A D - TO - G P U =========================// 
      //                                                          //

      // ----- X, Y, Z axis for visual debugging ----------------
      // X axis (GREEN) ; Y axis (RED); Z axis (BLUE)
      GLuint vao_axis = 0; 
      GLuint vbo_axis_vertices = 0;
      GLuint vbo_axis_colors = 0;
      float axis_len = 100.0;


      Mesh mesh_axis{};
      mesh_axis.vertices = {
            // X axis line  
            { origin,                  zero_normal,  color_green }
          , { position_x(axis_len),    zero_normal,  color_green }
            // Y axis line 
          , { origin,                   zero_normal,  color_red  }
          , { position_y(axis_len),     zero_normal,  color_red  }  
            //  Z axis line
          , { origin,                   zero_normal,  color_blue }
          , { position_z(axis_len),     zero_normal,  color_blue } 
      };
      mesh_axis.line_width = 10.0;
      mesh_axis.draw_type  = GL_LINES;
      send_mesh(mesh_axis, attr_position, attr_color);
    

      Mesh mesh_surface = generate_surface_wireframe_mesh(
                                -20, +20.0  // Xmax, Xmin 
                              , -20, +20.0  // Zmax, Zmin 
                              ,  200, 200   // Nx, Nz     => Number of points on X and Z axis
                              , color_green // Surface wireframe color 
                              // , [&](float x, float z){ return 25.0 - x * x - z * z ;}
                              , [&](float x, float z){ return 10.0 * sin(x) * sin(z) / z  ; }
                          ); 
      mesh_surface.line_width = 0.25;
      mesh_surface.draw_type  = GL_LINES;
      send_mesh(mesh_surface, attr_position, attr_color);

      // ============== Set Shader Uniform Variables =============// 
      //                                                          // 

      int width, height;
      glfwGetWindowSize(window, &width, &height);
      // Window aspect ratio
      float aspect = static_cast<float>(width) / height;

      // Set projection matrix uniform variable
      // glUniformMatrix4fv(u_proj, 1, GL_FALSE, glm::value_ptr(identity) );
    
      Camera camera(u_view, u_proj, u_camera_eye, width / height);
      camera.set_position(-23.0, 21.0, -22.0);
      camera.look_at({0.0, 0.0, 0.0});

      //  ======= R E N D E R  - L O O P ============//
      //                                             //
      while ( !glfwWindowShouldClose(window) )
      {
          glClear(GL_COLOR_BUFFER_BIT  | GL_DEPTH_BUFFER_BIT);

          // ====== BEGIN RENDERING ============//    

              // Draw axis lines
              mesh_axis.draw_vertices();           

              // Draw surface
              mesh_surface.draw_vertices(); 
              // mesh_surface.draw_indices();
             

          // ====== END RENDERING ==============//

          /* Swap front and back buffers */
          glfwSwapBuffers(window);
          /* Poll for and process events */
          glfwWaitEvents();

          if( glfwGetKey(window, 'Q' ) == GLFW_PRESS )
          {
               std::cout << " [TRACE] User typed Q =>> Shutdown program. Ok. " << '\n';
               break;
          }

          // Move camera to direction to where it is looking at.
          if( glfwGetKey(window, GLFW_KEY_UP ) == GLFW_PRESS    ){ camera.move_forward(+0.5);  }
          if( glfwGetKey(window, GLFW_KEY_DOWN ) == GLFW_PRESS  ){ camera.move_forward(-0.5);  }

          // Rotate around its Y axis 
          if( glfwGetKey(window, GLFW_KEY_RIGHT) == GLFW_PRESS  ){ camera.rotate_yaw(+5.0);   }
          if( glfwGetKey(window, GLFW_KEY_LEFT ) == GLFW_PRESS  ){ camera.rotate_yaw(-5.0);   }

          // Rotate camera around its local X axis
          if( glfwGetKey(window, 'S') == GLFW_PRESS ){ camera.rotate_pitch(+5.0); }
          if( glfwGetKey(window, 'D') == GLFW_PRESS ){ camera.rotate_pitch(-5.0); }

          // Reset camera - look at origin
          if( glfwGetKey(window, 'R' ) == GLFW_PRESS  )
          {
              camera.set_position(4.0, 3.0, 2.5);
              camera.look_at( {0.0, 0.0, 0.0} );
          }

      }

      glfwTerminate();
      return 0;

  } // --- End of main() -----//


  // ---------- S H A D E R - P R O G R A M S  -------------------------//
  //

  // Minimal vertex shader =>> Runs on the GPU and processes each vertex.
  const char* code_vertex_shader = R"(    
      #version 330 core 

      layout ( location = 0)  in vec3 position;
      layout ( location = 1) in vec3 color;   

      out vec3 out_color;
      out vec3 out_coord; 
      out vec3 out_eye;

      uniform vec3 u_eye;  // Cameras's position in space
      uniform mat4 u_model;       // Model matrix 
      uniform mat4 u_view;        // Camera's view matrix    
      uniform mat4 u_projection;  // Camera's projection matrix 
    

      void main()
      {
          gl_Position = u_projection * u_view * u_model * vec4(position, 1.0);
       
          // Forward to fragment shader 
          out_coord  = position;
          out_color = color; // vec3(0.56, 0.6, 0.0);
          out_eye = u_eye;
      }

  )";

  // Fragment shader source code 
  const char* code_frag_shader = R"(
      #version 330 

      in vec3 out_color;
      in vec3 out_coord;
      in vec3 out_eye; 

      void main()
      {
          float k;

          if(gl_FrontFacing)
              k = 1.0; // 2.0;
          else 
              k = 0.1; // 0.01;

          float factor = distance(out_eye, out_coord) ;

          // Set vertex colors
          vec3 color = (out_coord / 2.0 / factor ) + k * out_color * ( 20.0 / factor ) ;
          // vec3 color =  k * out_color * ( 20.0 / factor ) ;
        
          // vec3 color = 1.0 / fac + (out_color / 2.0 + 0.5) * k;

          gl_FragColor =  vec4( color , 1.0);
          // gl_FragColor = vec4(0.3, 0.6, 0.0, 1.0);
      }
  )";


      // ====== I M P L E M E N T A T I O N S ==========// 

  GLFWwindow* 
  make_glfwWindow(int  width, int height, const char* title)
  {

      glfwSetErrorCallback([](int error, const char* description)
                           { std::fprintf( stderr, " [GLFW ERROR] Error = %d ; Description = %s \n"
                                          , error, description);
                           });

      GLFWwindow* window = glfwCreateWindow(width, height, title, NULL, NULL);
      assert( window != nullptr && "Failed  to create Window");

      // OpenGL context 
      glfwMakeContextCurrent(window);
      // Pain whole screen as black - dark screen colors are better 
      // for avoding eye strain due long hours staring on monitor.
      glClearColor(0.0f, 0.0f, 0.0f, 1.0f);

      // Set - OpenGL Core Profile - version 3.3
      GL_CHECK( glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3) ); 
      GL_CHECK( glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3) );
      GL_CHECK( glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE) );

      GL_CHECK( glEnable(GL_COLOR_MATERIAL) );
      GL_CHECK( glEnable(GL_DEPTH_TEST)     );
      GL_CHECK( glEnable(GL_BLEND)          );

      return window;
  }

  void compile_shader(GLuint m_program, const char* code, GLenum type)
  {
      GLint shader_id = glCreateShader( type );
      glShaderSource(shader_id, 1, &code, nullptr);
      glCompileShader(shader_id);
      GLint is_compiled = GL_FALSE;
      // Check shader compilation result 
      glGetShaderiv(shader_id, GL_COMPILE_STATUS, &is_compiled);
      // If there is any shader compilation result, 
      // print the error message.
      if( is_compiled == GL_FALSE)
      {
          GLint length;
          glGetShaderiv(shader_id, GL_INFO_LOG_LENGTH, &length);
          assert( length > 0 );

          std::string out(length + 1, 0x00);
          GLint chars_written;
          glGetShaderInfoLog(shader_id, length, &chars_written, out.data());
          std::cerr << " [SHADER ERROR] = " << out << '\n';
          // Abort the exection of current process. 
          std::abort();
      }          

      glAttachShader(m_program, shader_id);   
      glDeleteShader(shader_id);
      glLinkProgram(m_program);
      GLint link_status = GL_FALSE;
      glGetProgramiv(m_program, GL_LINK_STATUS, &link_status);
      assert( link_status != GL_FALSE );   
      // glUseProgram(m_program);         
  }

  //  Send  mesh data to GPU ----//
  void send_mesh( Mesh& mesh , GLint attr_position , GLint attr_color   ) 
  { 
      GLuint& vao = mesh.vao;
      GLuint& vbo = mesh.vbo;
      GLuint& ibo = mesh.ibo;

      // ------------- Upload vertices --------------//
    
      // Generate and bind current VAO (Vertex Array Object)
      if(vao == 0){ glGenVertexArrays(1, &vao); }
      glBindVertexArray(vao);
      // Generate and bind current VBO (Vertex Buffer Object)
      glGenBuffers(1, &vbo);
      glBindBuffer(GL_ARRAY_BUFFER, vbo);
      // Upload data to current VBO buffer in GPU 
      glBufferData(GL_ARRAY_BUFFER, mesh.vertices.size() * sizeof(Vertex)
                  , mesh.vertices.data(), GL_STATIC_DRAW);   

      glEnableVertexAttribArray(attr_position);    
      glEnableVertexAttribArray(attr_color);    
   
      // Set data layout - how data will be interpreted.
      // => Each vertex has 2 coordinates. 
      glVertexAttribPointer( attr_position, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), nullptr);

      glVertexAttribPointer( attr_color, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex)     
                             // Offset to color member variable in class Vertex2D
                            , reinterpret_cast<void*>( offsetof(Vertex, color) )
                            );

      // -------- Upload indices ----------------------//
      //
      if (!mesh.indices.empty())
      {
          glBindVertexArray(vao);
          glGenBuffers(1, &ibo);
          glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo);
          glBufferData(GL_ELEMENT_ARRAY_BUFFER, mesh.indices.size() * sizeof(GLuint)
                       , mesh.indices.data(), GL_STATIC_DRAW );
      }

      // ------ Disable Global state set by this function -----//
      // Unbind VAO 
      glBindVertexArray(0);
      // Unbind VBO 
      glBindBuffer(GL_ARRAY_BUFFER, 0);
      // Unbind IBO 
      glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, 0);
      // Disable current shader attribute 
      glDisableVertexAttribArray(attr_position);
      glDisableVertexAttribArray(attr_color);

  }


  Mesh generate_surface_wireframe_mesh(  
                               float xmin, float xmax
                             , float zmin, float zmax
                             , size_t Nx, size_t Nz
                             , Color color 
                             , SurfaceFunction const& function)
  {
      assert( xmax > xmin );
      assert( zmax > zmin );
      assert( Nx  != 0 && Nz  != 0 );

      std::cout << " [TRACE] Nx = " << Nx << " ; Nz = " << Nz << '\n';

      Mesh mesh;
      mesh.draw_type = GL_LINES;

      float dx = (xmax - xmin) / Nx;
      float dz = (zmax - zmin) / Nz; 

      // Intial allocation for avoiding many dynamic allocations
      mesh.vertices.reserve( Nx * Nz );
      mesh.indices.reserve( 2 * Nx * Nz  );
      float x, y, z, y_h;

      // Draw lines parallel to plane XY  
      for(size_t j = 0; j < Nz; j++)
      {   for(size_t i = 0; i < Nx; i++)
          {
              x = xmin + i * dx;
              z = zmin + j * dz; 
              y = function(x, z);
              y_h = function(x + dx, z);

              // Draw line segments in planes parallel to plane XY.
              mesh.vertices.push_back( Vertex{ Position{x, y, z}, zero_normal, color } );
              mesh.vertices.push_back( Vertex{ Position{x + dx, y_h, z}, zero_normal, color } );

          }
      }

      for(size_t i = 0; i < Nx; i++)
      {   for(size_t j = 0; j < Nz; j++)
          {
              x = xmin + i * dx;
              z = zmin + j * dz; 
              y = function(x, z);
              y_h = function(x, z + dz);

              // Draw line segments in planes parallel to plane YZ.
              mesh.vertices.push_back( Vertex{ Position{x, y, z       }, zero_normal, color } );
              mesh.vertices.push_back( Vertex{ Position{x, y_h, z + dz}, zero_normal, color } );

           }
      }
      return mesh;
  }

#+END_SRC

** 3D - 3D model loading with illumination  

This sample code loads 3D meshes from obj file with help from
tinyObjLoader library and draws the loaded meshes (3D models)
triangles using ambient and diffuse light. 

Libraries used: 

  + [[https://www.glfw.org/][glfw]]
    + Library for Window Abstraction and dealing with OpenGL context.

  + [[https://github.com/g-truc/glm][GLM math library]]
    + Computer graphics linear algebra library

  + [[http://glew.sourceforge.net/][Glew]]
    + Library for loading OpenGL function pointers. It is used only on
      Windows in Pre-compiled form due to the difficulty to build the
      library from source as it requires perl.

  + [[https://github.com/tinyobjloader/tinyobjloader][tinyobjloader]] 
    + Library for loading 3D meshes from Obj files.

  + [[https://github.com/vector-of-bool/cmrc/][cmrc - Cmake Resource Compiler]]
    + Single-file CMake library for embedding files in the executable,
      shader scripts and 3D models. This Cmake utility makes easier to
      distribute the binary, since just a single file is distributed or
      deployed and the application does not have to deal with file
      system path and relative path.


See also: 

  + [[https://en.wikipedia.org/wiki/List_of_common_3D_test_models][List of common 3D test models]]

 *Screenshot* 

[[file:images/opengl-draw3d-load-3dmodels.png][file:images/opengl-draw3d-load-3dmodels.png]]

 *Files* 

File: _CMakeLists.txt_ 

#+BEGIN_SRC cmake 
   cmake_minimum_required(VERSION 3.5)
   project( OpenGL_3D_Model_Loader )

   set(CMAKE_CXX_STANDARD 17)
   set(CMAKE_CXX_STANDARD_REQUIRED ON)
   set(CMAKE_VERBOSE_MAKEFILE ON)

   #================ GLFW Settings  ===============#

   find_package(OpenGL REQUIRED)

   include(FetchContent)

   # Set GLFW Options before FectchContent_MakeAvailable 
   set( GLFW_BUILD_EXAMPLES OFF CACHE BOOL  "GLFW lib only" )
   set( GLFW_BUILD_TESTS    OFF CACHE BOOL  "GLFW lib only" )
   set( GLFW_BUILD_DOCS     OFF CACHE BOOL  "GLFW lib only" )
   set( GLFW_BUILD_INSTALL  OFF CACHE BOOL  "GLFW lib only" )

   # Donwload GLFW library
   FetchContent_Declare(
     glfwlib
     URL   https://github.com/glfw/glfw/releases/download/3.3.2/glfw-3.3.2.zip
   )
   FetchContent_MakeAvailable(glfwlib)

   # Download GLM (OpenGL math library for matrix and vectors transformation)
   FetchContent_Declare(
     glm  
     URL  https://github.com/g-truc/glm/archive/0.9.8.zip
   )
   FetchContent_MakeAvailable(glm)
   include_directories( ${glm_SOURCE_DIR} )

   FetchContent_Declare(
     tinyobjloader 
     URL   https://github.com/tinyobjloader/tinyobjloader/archive/v2.0.0rc8.zip
   )
   FetchContent_MakeAvailable(tinyobjloader)


   # Download pre-compiled GLEW when building under Windows NT OS (x64)
   IF(WIN32)
      FetchContent_Declare(
         glew-release 
         URL   https://ufpr.dl.sourceforge.net/project/glew/glew/2.1.0/glew-2.1.0-win32.zip 
         # https://github.com/nigels-com/glew/archive/glew-2.2.0.zip
      )
      FetchContent_MakeAvailable(glew-release)
      include_directories( ${glew-release_SOURCE_DIR}/include  ${glm_SOURCE_DIR} )        
      link_directories(  ${glew-release_SOURCE_DIR}/lib/Release/x64 )

      set( GLEW_LIB_PATH1 ${glew-release_SOURCE_DIR}/lib/Release/x64/glew32.lib )
      set( GLEW_LIB_PATH2 ${glew-release_SOURCE_DIR}/lib/Release/x64/glew32s.lib )
   ENDIF()

   ## Fetch Cmake-Resource Compiler
   file(DOWNLOAD "https://raw.githubusercontent.com/vector-of-bool/cmrc/master/CMakeRC.cmake"
                    "${CMAKE_BINARY_DIR}/CMakeRC.cmake")
   include("${CMAKE_BINARY_DIR}/CMakeRC.cmake")


   MACRO(ADD_OPENGL_APP target sources)
      add_executable( ${target} ${sources} )
      message([TRACE] " Add OpenGL executable = ${target} ")

      target_link_libraries( ${target} glfw
                                       OpenGL::GL
                                       ${GLEW_LIB_PATH1}
                                       ${GLEW_LIB_PATH2} )

       IF(MINGW)
           # Statically link against MINGW dependencies
           # for making easier to deploy on other machines. 
           target_link_options( ${target} PRIVATE                                 
                                    -static-libgcc
                                    -static-libstdc++
                                    -Wl,-Bstatic,--whole-archive -lwinpthread
                                      -Wl,--no-whole-archive                                    
                                    )
        ENDIF()       


        # Copy GLEW DLL shared library to same directory as the executable.                 
        IF(WIN32)                           
           add_custom_command(TARGET ${target} POST_BUILD 
                          COMMAND ${CMAKE_COMMAND} -E copy_if_different
                          "${glew-release_SOURCE_DIR}/bin/Release/x64/glew32.dll"              
                          $<TARGET_FILE_DIR:${target}>)
        ENDIF()                                       
   ENDMACRO()     


   # Download Suzanne 3D model 
   file(DOWNLOAD https://raw.githubusercontent.com/alecjacobson/common-3d-test-models/master/data/suzanne.obj
                 ${CMAKE_CURRENT_SOURCE_DIR}/suzanne.obj 
                 )

   # Download Teapot 3D model 
   file(DOWNLOAD https://raw.githubusercontent.com/kevinroast/phoria.js/master/teapot.obj
                 ${CMAKE_CURRENT_SOURCE_DIR}/teapot.obj 
                 )

    # ----------- T A R G E T S ---------------------------$

      ADD_OPENGL_APP( model-loader model-loader.cpp )

      # Define resourse files to be embedded in the executable (model-loader)
      cmrc_add_resource_library( model-loader-resources 
                                 NAMESPACE resource
                                 ./model_loader_vshader.glsl 
                                 ./model_loader_fshader.glsl
                                 ./suzanne.obj 
                                 ./cube.obj  
                                 ./teapot.obj 
                                )

     # Append resources to the executable                            
     target_link_libraries( model-loader
                            model-loader-resources tinyobjloader )


#+END_SRC

File: _model_loader_vshader.glsl_ / Vertex Shader 

#+BEGIN_SRC cpp 
    // Vertex Shader Source Code
    #version 330 core 

    layout ( location = 0 ) in vec3 position;
    layout ( location = 1 ) in vec3 normal;  
    layout ( location = 2 ) in vec3 color;   

    uniform mat4 u_model;       // Model matrix 
    uniform mat4 u_view;        // Camera's view matrix    
    uniform mat4 u_projection;  // Camera's projection matrix 
    uniform mat4 u_normal;      // Normal matrix (transpose of view-model inverse)

    varying highp vec4 out_color;
    varying highp vec4 out_light;
    varying vec3 mv_normal, mv_view, mv_position;

    // ------ Pre-defined colors for debugging -----//
     void main()
    {
        mat4 model_view = u_view * u_model; 
        // Should be computed at the application-side 
        /// mat4 normal_mat  = transpose(inverse(model_view));

        // Convert normal to eye space coordinates (aka camera coordinates)
        // mv_normal = normalize( ( normal_mat * vec4(normal, 0.0)).xyz );
        mv_normal = normalize( (  u_normal * vec4(normal, 0.0)).xyz );  

        // Set #if argument to 1 in order to debug the normal
        #if 0
            vec3 _position = position + abs(mv_normal);
        #else 
            vec3 _position = position;
        #endif 

        gl_Position = u_projection * u_view * u_model * vec4(_position, 1.0);        
        
        // ------------- Lighting -----------------------//

        // Vertex coordinate in eye space (camera-space)
        mv_position = ( model_view * vec4(position, 1.0) ).xyz;
   
        // out_light = vec4(light, 1.0);
        out_color = vec4(color, 1.0);
  }
#+END_SRC

File: _model_loader_fshader.glsl_ / Fragment Shader  

#+BEGIN_SRC cpp 
   // Fragment Shader Source Code
   #version 330
   precision mediump float; 

   varying highp vec4 out_color;
   varying highp vec4 out_light; 
   varying vec3 mv_normal, mv_light, mv_view, mv_position;

   uniform vec3 u_product_diffuse;
   uniform vec3 u_light_position;
   uniform vec3 u_product_ambient;

   // Specular coefficient 
   float spec = 10.0;  

   uniform vec3 u_product_specular;

   const vec3 color_red   = vec3(1.0, 0.0, 0.0);
   const vec3 color_green = vec3(0.0, 1.0, 0.0);  

   void main()
   {

       vec3 _normal = mv_normal; //;normalize( mv_normal );
       // vec3 light_direction = normalize(vec3(100, 100, 100)); 
       vec3 light_direction = normalize( u_light_position - mv_position );

       float lambert = max( dot(_normal, light_direction), -1.0);

       vec3  light_ambient =  u_product_ambient;
       vec3  light_diffuse =  u_product_diffuse * lambert;         

       // View vector for specular light
       vec3 _view   = normalize( mv_view);
       // Halfway vector
       vec3 H = normalize( light_direction + _view );
       vec3 light_specular = pow( max(dot(H, _normal), 0.0), spec) * u_product_specular; 

       vec4 light = vec4(light_ambient + light_diffuse, 1.0);
       // vec4 light = vec4(light_ambient + light_diffuse + light_specular, 1.0); 

       // gl_FragColor = out_light ; 
       gl_FragColor = light * out_color;

   }
#+END_SRC

File: _cube.obj_ / Cube 3D model Obj file 

#+BEGIN_SRC sh 
   # cube.obj
   #
   mtllib cube.mtl
   o cube

   v -0.500000 -0.500000 0.500000
   v 0.500000 -0.500000 0.500000
   v -0.500000 0.500000 0.500000
   v 0.500000 0.500000 0.500000
   v -0.500000 0.500000 -0.500000
   v 0.500000 0.500000 -0.500000
   v -0.500000 -0.500000 -0.500000
   v 0.500000 -0.500000 -0.500000

   vt 0.000000 0.000000
   vt 1.000000 0.000000
   vt 0.000000 1.000000
   vt 1.000000 1.000000

   vn 0.000000 0.000000 1.000000
   vn 0.000000 1.000000 0.000000
   vn 0.000000 0.000000 -1.000000
   vn 0.000000 -1.000000 0.000000
   vn 1.000000 0.000000 0.000000
   vn -1.000000 0.000000 0.000000

   g cube
   usemtl cube
   s 1
   f 1/1/1 2/2/1 3/3/1
   f 3/3/1 2/2/1 4/4/1
   s 2
   f 3/1/2 4/2/2 5/3/2
   f 5/3/2 4/2/2 6/4/2
   s 3
   f 5/4/3 6/3/3 7/2/3
   f 7/2/3 6/3/3 8/1/3
   s 4
   f 7/1/4 8/2/4 1/3/4
   f 1/3/4 8/2/4 2/4/4
   s 5
   f 2/1/5 8/2/5 4/3/5
   f 4/3/5 8/2/5 6/4/5
   s 6
   f 7/1/6 1/2/6 5/3/6
   f 5/3/6 1/2/6 3/4/6

#+END_SRC

File: _model-loader.cpp_ 

#+BEGIN_SRC cpp 
  #include <iostream>
  #include <string> 
  #include <sstream>
  #include <vector> 
  #include <array>
  #include <cmath>
  #include <functional>
  #include <thread>

  // -------- OpenGL headers ---------//
  //
  #define GL_GLEXT_PROTOTYPES 1 // Necessary for OpenGL >= 3.0 functions
  #define GL3_PROTOTYPES      1 // Necessary for OpenGL >= 3.0 functions

  #if defined(_WIN32)
     #include <windows.h>
     #include <GL/glew.h>
  #endif


  #include <GL/gl.h>
  #include <GLFW/glfw3.h>

  // #include <GL/glew.h>
  #include <GL/glu.h>

  // #include <GL/glut.h>

  // --------- OpenGL Math Librar ------------//
  #include <glm/glm.hpp>
  #include <glm/gtc/matrix_transform.hpp>
  #include <glm/gtc/type_ptr.hpp>
  #include <glm/gtx/string_cast.hpp>
  #include <glm/gtx/io.hpp>

  #include <tiny_obj_loader.h>

  #include <cmrc/cmrc.hpp>
  CMRC_DECLARE(resource);

  struct Position { GLfloat x, y, z;  };
  struct Normal   { GLfloat x, y, z;  };
  struct Color    { GLfloat r, g, b;  };

  struct Vertex   {  
      Position position; // Vertex position in space 
      Normal   normal;   // Vertex normal vector for illumination (Future use)
      Color    color;    // Vertex color (RGB)
  };

  // Mesh - set of vertices (position and attributes) that represents 
  // some surface or solid. 
  struct Mesh 
  {
      std::vector<Vertex> vertices{};
      std::vector<GLuint> indices{}; 
      GLfloat line_width = 1.0;
      GLenum  draw_type; 
      GLuint vao = 0, vbo = 0, ibo = 0;
    
      // Draw mesh using indices 
      void draw_indices()
      {
          // Check wheter the mesh was sent to GPU.
          assert( vao != 0 && ibo != 0 );
          // Note: The subroutine glLineWidth is stateful!!
          glLineWidth(line_width);
          glBindVertexArray(vao);
          glDrawElements(draw_type, indices.size(), GL_UNSIGNED_INT, nullptr);
      }

      // Draw mesh using vertices 
      void draw_vertices()
      {
          assert( vao != 0 );
          glLineWidth(line_width);       
          glBindVertexArray(vao);
          glDrawArrays(draw_type, 0, vertices.size());
      }
  };


  GLFWwindow* make_glfwWindow(int  width, int height, const char* title);
  void         compile_shader(GLuint m_program, const char* code, GLenum type);
  void              send_mesh( Mesh& mesh , GLint attr_position , GLint attr_normal, GLint attr_color); 

  // Load  mesh data from obj file, often generated by tools such as Blender or 3D studio max.
  Mesh load_model(const std::string& file, const Color& color);

  void set_uniform_matrix(GLint uniform, glm::mat4 const& m)
  {
      glUniformMatrix4fv(uniform, 1, GL_FALSE, glm::value_ptr(m) );
  }

  // Get shader uniform location and check return status code.
  GLint shader_uniform(GLuint program, const char* name);
  // Get shader attribute location and return status code.
  GLint shader_attr(GLint program, const char* name);

  // Get content of resource file, embedded in the executable file, as string.
  // Requires:  #include <cmrc/cmrc.hpp> 
  std::string get_resource(std::string file_name);

  constexpr Normal zero_normal = { 0.0, 0.0 , 0.0};
  constexpr Color  color_red   = {1.0, 0.0, 0.0};
  constexpr Color  color_green = {0.0, 1.0, 0.0};        
  constexpr Color  color_blue  = {0.0, 0.0, 1.0};
  constexpr Color color_gold  = {1.0, 0.843, 0.0};

  const glm::mat4 matrix_identity = glm::mat4(1.0);


  struct Camera
  {
      // Current location of camera in world coordinates
      glm::vec3 eye  = { 2.0, 4.0, 5.0 };
      // Direction to where camera is looking at.
      glm::vec3 forward =  glm::normalize(glm::vec3{-1.0, 0.0, -1.0 });
      // Current  camera up vector (orientation) - Y axis (default)
      glm::vec3 up = { 0.0, 1.0, 0.0 };

      // Field of view
      float fov_angle = glm::radians(60.0);
      // Aspect ratio
      float aspect   = 1.0;
      float zFar     = 100.0;
      float zNear    = 0.1;

      // ID of shader's view uniform variable - for setting view matrix
      GLint shader_uniform_view = -1;
      // ID of shader's projection uniform variable for setting projection matrix.
      GLint shader_uniform_proj = -1;

      Camera(GLuint uniform_view, GLuint uniform_proj, GLfloat aspect):
           shader_uniform_view(uniform_view)
         , shader_uniform_proj(uniform_proj)
         , aspect(aspect)
      {  update_view();  }

      void update_view()
      {
          // Point to where camera is looking at.
          auto cam_at = this->eye + this->forward;
          std::cerr << "  [CAMERA] Camera coordinate (eye)        = " << eye << '\n';
          std::cerr << "  [CAMERA] Looking at direction (forward) = " << forward << '\n'; 
        
          // Create View matrix (maps from world-space to camera-space)
          auto Tview = glm::lookAt(eye, cam_at, up);
          // Create projection matrix maps - camera-space to NDC (Normalized Device Coordinates).
          auto Tproj = glm::perspective( fov_angle, aspect, zNear, zFar );
          // Set shader uniform variables.
          glUniformMatrix4fv(shader_uniform_view, 1, GL_FALSE, glm::value_ptr(Tview) );
          glUniformMatrix4fv(shader_uniform_proj, 1, GL_FALSE, glm::value_ptr(Tproj) );
        
      }

      // Compute projection matrix 
      glm::mat4 get_proj()
      {   return glm::perspective( fov_angle, aspect, zNear, zFar ); }

      //  Compute view matrix 
      glm::mat4 get_view() 
      {
          // Point to where camera is looking at.
          auto cam_at = this->eye + this->forward;
          auto Tview = glm::lookAt(eye, cam_at, up);
          return Tview;
      }
      // Compute normal view matrix for illumination
      glm::mat4 normal_view(glm::mat4 const& model)
      {
          return glm::transpose( glm::inverse( this->get_view() * model ) );
      }

      // Rotate around camera's Up vecto.
      void rotate_yaw(float angle)
      {
          // Build quaternion from axis angle
          glm::quat q = glm::angleAxis(glm::radians(angle), this->up);
          // Rotate current forward vector
          forward = forward * q;
          // std::cout << " [FORWARD VECTOR ] " << glm::to_string(forward) << '\n';
          this->update_view();
      }

      // Rotate around camera's pitch axis (X axis) elevate camera view.
      void rotate_pitch(float angle)
      {
          glm::vec3 axis = glm::cross(this->up, this->forward);
          // Build quaternion from axis angle
          glm::quat q = glm::angleAxis(glm::radians(angle), axis);
          forward = q * forward;
          this->update_view();
      }

        // Move at forward vector direction (to where camera is looking at).
      void move_forward(float factor)
      {
          this->eye = this->eye + factor * this->forward;
          this->update_view();
      }
      // Move camera to specific point in the space.
      void set_position(float x, float y, float z)
      {
          this->eye = glm::vec3(x, y, z);
          this->update_view();
      }

      // Set position to where camera is looking at.
      void look_at(const glm::vec3& at)
      {
          this->forward = glm::normalize(at - this->eye);
        
          this->update_view();
      }

  };


  // Create mesh for plane (represented by a square) on origin
  Mesh make_plane_mesh(float side, glm::vec3 const& VA, glm::vec3 const& VB
                      , Normal const& normal, Color const& color)
  {
      glm::vec3 B = side * VA, C = side * VB, D = B  + C;
      Position pA = {0, 0, 0}, pB = {B[0], B[1], B[2]}, pC = {C[0], C[1], C[2]}, pD = {D[0], D[1], D[2]} ;
      Mesh mesh_plane;
      mesh_plane.draw_type = GL_TRIANGLES;
      mesh_plane.line_width = 1.0;

      mesh_plane.vertices = {
          // ----- Triagle 1 ------------// 
            { pA, normal, color },{ pB, normal, color }, { pC, normal, color }
          // ----- Triangle 2 -------------//
          , { pB, normal, color }, { pC, normal, color } , Vertex{ pD, normal, color } 
       };  
    
      return mesh_plane;
  }

  int main(int argc, char** argv)
  {
      // glutInit(&argc, argv);

      if (!glfwInit()){ return -1; }
      GLFWwindow* window = make_glfwWindow(640, 480, "Load 3D models. ");

      // ====== S H A D E R - C O M P I L A T I O N ====//
      //                                                //     
      // Note: The shader source code is at the end of file.
      extern const char* code_vertex_shader;
      extern const char* code_frag_shader;

      GLuint prog = glCreateProgram();

      std::string vertex_shader   = get_resource("/model_loader_vshader.glsl");
      std::string fragment_shader = get_resource("/model_loader_fshader.glsl");

      // Compile shader code 
      compile_shader(prog, vertex_shader.c_str(),  GL_VERTEX_SHADER  ) ;    
      compile_shader(prog, fragment_shader.c_str(), GL_FRAGMENT_SHADER );
      glUseProgram(prog);

      const GLint attr_position = shader_attr(prog, "position");
      const GLint attr_normal   = shader_attr(prog, "normal");   
      const GLint attr_color    = shader_attr(prog, "color");
    
      const GLint u_proj        = shader_uniform(prog, "u_projection");
      const GLint u_view        = shader_uniform(prog, "u_view");
      const GLint u_model       = shader_uniform(prog, "u_model");
      const GLint u_normal      = shader_uniform(prog, "u_normal");
      const GLint u_product_ambient  = shader_uniform(prog,  "u_product_ambient");
      const GLint u_product_diffuse  = shader_uniform(prog,  "u_product_diffuse");
      const GLint u_product_specular = shader_uniform(prog, "u_product_specular");
      const GLint u_light_position   = shader_uniform(prog, "u_light_position");

      set_uniform_matrix(u_proj,   matrix_identity);
      set_uniform_matrix(u_view,   matrix_identity);
      set_uniform_matrix(u_model,  matrix_identity);
      set_uniform_matrix(u_normal, matrix_identity); 

      // Ambient light RGB colors  
      glm::vec3 La = {0.0, 1.0, 0.5};
      // Diffuse light RGB colors 
      glm::vec3 Ld = {0.6,  0.4, 0.9};
      // Specular light RGB colors 
      glm::vec3 Ls = {1.0, 1.0, 1.0};

      // ---- Material coefficients ------//
      glm::vec3 ka = {0.5, 0.2, 1.0};
      glm::vec3 kd = {0.8, 1.0, 1.0};
      glm::vec3 ks = {0.4, 0.3, 0.2};
      glm::vec3 light_position = { 1.0, 1.0, 1.0 };

      std::cout << "  [TRACE] Ambient =>> ka * La = " << (ka * La) << '\n';
      std::cout << "  [TRACE] Diffuse =>> kd * Ld = " << (kd * Ld) << '\n';

      // Note: The  product ka * La is an element-wise product.
      // It is not a dot product or cross product.
      glUniform3fv(u_light_position,   1, glm::value_ptr( light_position ) );
      glUniform3fv(u_product_ambient,  1, glm::value_ptr( ka * La ));
      glUniform3fv(u_product_diffuse,  1, glm::value_ptr( kd * Ld ));
      glUniform3fv(u_product_specular, 1, glm::value_ptr( ks * Ls ));    

      Mesh mesh_planeXZ = make_plane_mesh( 10.0, glm::vec3{1.0, 0.0, 0.0}
                                      , glm::vec3{0.0, 0.0, 1.0}, Normal{0.0, 1.0, 0.0}, color_blue );
      send_mesh(mesh_planeXZ, attr_position, attr_normal, attr_color);

      Mesh mesh_planeXY = make_plane_mesh( 10.0, glm::vec3{0.0, 1.0, 0.0}
                                      , glm::vec3{1.0, 0.0, 0.0}, Normal{0.0, 0.0, 1.0}, color_green );
      send_mesh(mesh_planeXY, attr_position, attr_normal, attr_color);


      Mesh mesh_suzanne = load_model("/suzanne.obj", Color{0.51, 0.56, 0.50}); 
      send_mesh(mesh_suzanne, attr_position, attr_normal,  attr_color);
      glm::mat4 transform_suzanne =  matrix_identity;
      transform_suzanne = glm::translate( transform_suzanne, -glm::vec3( -1.182, 1.306, 3.573) );
      transform_suzanne = glm::translate( transform_suzanne, +glm::vec3( 4.0, 3.0, 5.0) );  
      // model_matrix = glm::rotate( model_matrix, glm::radians(45.0f), glm::vec3(0, 0, 1));

      Mesh mesh_cube = load_model("/cube.obj", color_gold);
      send_mesh(mesh_cube, attr_position, attr_normal, attr_color);
      glm::mat4 transform_cube = matrix_identity;
      transform_cube = glm::translate( transform_cube, glm::vec3(4.0, 2.0, 5.0) );
      transform_cube = glm::rotate( transform_cube, glm::radians(35.0f), glm::vec3(1.0, 1.0, 2.0) );
      transform_cube = glm::scale( transform_cube, glm::vec3(1.2, 1.2, 1.2));


      Mesh mesh_teapot = load_model("/teapot.obj", color_red);
      send_mesh(mesh_teapot, attr_position, attr_normal, attr_color);
      glm::mat4 transform_teapot = matrix_identity;
      transform_teapot = glm::translate( transform_teapot, glm::vec3(5.0, 5.0, 5.0) );
      transform_teapot = glm::rotate( transform_teapot, glm::radians(50.0f), glm::vec3(3.0, 1.0, 5.0) );
      transform_teapot = glm::scale( transform_teapot, glm::vec3(1.0 / 8.0, 1.0 / 8.0, 1.0 / 8.0));


      int width, height;
      glfwGetWindowSize(window, &width, &height);
      // Window aspect ratio
      float aspect = static_cast<float>(width) / height;


      // Set projection matrix uniform variable
      // glUniformMatrix4fv(u_proj, 1, GL_FALSE, glm::value_ptr(identity) );
    
      Camera camera(u_view, u_proj, width / height);
      camera.set_position(5.236, 4.078, 11.610);
      camera.look_at( { 4.0, 1.4, 5.166} );

      // Set model matrix and update the normal matrix for 
      // scene illumination calculations. 
      auto set_model_matrix = [&camera, u_normal, u_model](const glm::mat4& model_transform)
      {
          set_uniform_matrix(u_model, model_transform);
          glm::mat4 normal_matrix = glm::transpose( glm::inverse(camera.get_view() * model_transform) );
          set_uniform_matrix(u_normal, normal_matrix);
      };

      //  ======= R E N D E R  - L O O P ============//
      //                                             //
      while ( !glfwWindowShouldClose(window) )
      {
          glClear(GL_COLOR_BUFFER_BIT  | GL_DEPTH_BUFFER_BIT);

          // ====== BEGIN RENDERING ============//    
              // set_uniform_matrix(u_model, matrix_identity);
              set_model_matrix(matrix_identity);
              mesh_planeXZ.draw_vertices();

              // set_uniform_matrix(u_model, matrix_identity);
              set_model_matrix(matrix_identity);
              mesh_planeXY.draw_vertices();           

              set_uniform_matrix(u_model, transform_suzanne);
              mesh_suzanne.draw_vertices();

              set_uniform_matrix(u_model, transform_cube);
              mesh_cube.draw_vertices();          

              set_uniform_matrix(u_model, transform_teapot);
              mesh_teapot.draw_vertices();

          // ====== END RENDERING ==============//

           // Move camera to direction to where it is looking at.
          if( glfwGetKey(window, GLFW_KEY_UP ) == GLFW_PRESS    ){ camera.move_forward(+2.0);  }
          if( glfwGetKey(window, GLFW_KEY_DOWN ) == GLFW_PRESS  ){ camera.move_forward(-2.0);  }

          // Rotate around its Y axis 
          if( glfwGetKey(window, GLFW_KEY_RIGHT) == GLFW_PRESS  ){ camera.rotate_yaw(+5.0);   }
          if( glfwGetKey(window, GLFW_KEY_LEFT ) == GLFW_PRESS  ){ camera.rotate_yaw(-5.0);   }

          // Rotate camera around its local X axis
          if( glfwGetKey(window, 'S') == GLFW_PRESS ){ camera.rotate_pitch(+5.0); }
          if( glfwGetKey(window, 'D') == GLFW_PRESS ){ camera.rotate_pitch(-5.0); }

          if( glfwGetKey(window, 'H') == GLFW_PRESS )
          {
              // Rotate around Z axis
              transform_suzanne = glm::rotate(transform_suzanne, glm::radians(10.0f), glm::vec3(0, 0, 1));
          }
          if( glfwGetKey(window, 'J') == GLFW_PRESS )
          {
              // Rotate around Z axis
              transform_suzanne = glm::rotate(transform_suzanne, glm::radians(10.0f), glm::vec3(1, 0, 0));
          }

          // Reset camera - look at origin
          if( glfwGetKey(window, 'R' ) == GLFW_PRESS  )
          {
              camera.set_position(4.0, 3.0, 2.5);
              camera.look_at( {0.0, 0.0, 0.0} );
          }
        
          // glm::mat4 normal_view_matrix = camera.normal_view( model_matrix ); 
          // set_uniform_matrix( u_normalview, normal_view_matrix );


          /* Swap front and back buffers */
          glfwSwapBuffers(window);
          /* Poll for and process events */
          glfwWaitEvents();

      } // --- End while() --- //

      glfwTerminate();
    
      return 0;
  }


  // ---------------------------------------//

  GLFWwindow* 
  make_glfwWindow(int  width, int height, const char* title)
  {

      glfwSetErrorCallback([](int error, const char* description)
                           { std::fprintf( stderr, " [GLFW ERROR] Error = %d ; Description = %s \n"
                                          , error, description);
                           });

      GLFWwindow* window = glfwCreateWindow(width, height, title, NULL, NULL);
      assert( window != nullptr && "Failed  to create Window");

      // OpenGL context 
      glfwMakeContextCurrent(window);
      // Pain whole screen as black - dark screen colors are better 
      // for avoding eye strain due long hours staring on monitor.
      glClearColor(0.0f, 0.0f, 0.0f, 1.0f);

      // Set - OpenGL Core Profile - version 3.3
      glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); 
      glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
      glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);

      glEnable(GL_COLOR_MATERIAL);
      glEnable(GL_DEPTH_TEST);
      glEnable(GL_BLEND);

      // --- Initialize GLEW library after OpenGL context --------- //
      #if _WIN32
      glewExperimental = GL_TRUE;
      if (glewInit() != GLEW_OK) {
          std::cerr << "GLEW::Error : failed to initialize GLEW" << std::endl;
          std::abort();
      }
      std::fprintf(stderr, " [TRACE] Glew Initialized Ok. \n");
      #endif


      return window;
  }


  // Get shader uniform location and check error.
  GLint shader_uniform(GLuint program, const char* name)
  {
      GLint out = glGetUniformLocation(program, name);
      if(out < 0){ std::fprintf(stderr, " [SHADER ERROR] Uniform location not found = %s \n", name); }
      return out;
  }

  GLint shader_attr(GLint program, const char* name)
  {
      GLint out = glGetAttribLocation(program, name);
      if(out < 0){ std::fprintf(stderr, " [SHADER ERROR] Attribute location not found = %s \n", name); }
      return out;
  }



  void compile_shader(GLuint m_program, const char* code, GLenum type)
  {
      GLint shader_id = glCreateShader( type );
      glShaderSource(shader_id, 1, &code, nullptr);
      glCompileShader(shader_id);
      GLint is_compiled = GL_FALSE;
      // Check shader compilation result 
      glGetShaderiv(shader_id, GL_COMPILE_STATUS, &is_compiled);

      const std::string shader_type = [&]{
          if(type == GL_VERTEX_SHADER   ){ return "VERTEX_SHADER"; }
          if(type == GL_FRAGMENT_SHADER ){ return "FRAGMENT_SHADER"; }
          return "UNKNOWN"; 
      }();

      // If there is any shader compilation result, 
      // print the error message.
      if( is_compiled == GL_FALSE)
      {
          GLint length;
          glGetShaderiv(shader_id, GL_INFO_LOG_LENGTH, &length);
          assert( length > 0 );

          std::string out(length + 1, 0x00);
          GLint chars_written;
          glGetShaderInfoLog(shader_id, length, &chars_written, out.data());
          std::cerr << " [SHADER ERROR] = " << " (" << shader_type << " ) =>> " << out << '\n';
          // Abort the exection of current process. 
          std::abort();
      }          

      glAttachShader(m_program, shader_id);   
      glDeleteShader(shader_id);
      glLinkProgram(m_program);
      GLint link_status = GL_FALSE;
      glGetProgramiv(m_program, GL_LINK_STATUS, &link_status);
      assert( link_status != GL_FALSE );   
      // glUseProgram(m_program);         
  }


  //  Send mesh data to GPU  ----//
  void send_mesh( Mesh& mesh , GLint attr_position , GLint attr_normal, GLint attr_color ) 
  { 
      GLuint& vao = mesh.vao;
      GLuint& vbo = mesh.vbo;
      GLuint& ibo = mesh.ibo;

      // ------------- Upload vertices --------------//
    
      // Generate and bind current VAO (Vertex Array Object)
      if(vao == 0){ glGenVertexArrays(1, &vao); }
      glBindVertexArray(vao);
      // Generate and bind current VBO (Vertex Buffer Object)
      glGenBuffers(1, &vbo);
      glBindBuffer(GL_ARRAY_BUFFER, vbo);

      // Upload vertex attributes (position, normal vector and color ) to GPU in a single call. 
      glBufferData(GL_ARRAY_BUFFER, mesh.vertices.size() * sizeof(Vertex)
                  , mesh.vertices.data(), GL_STATIC_DRAW);   
   
      // Set data layout - how data will be interpreted.
      // => Each vertex has 3 coordinates.
      glEnableVertexAttribArray(attr_position); 
      glVertexAttribPointer( attr_position, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), nullptr);

      #if 1 
      glEnableVertexAttribArray(attr_normal);          
      glVertexAttribPointer( attr_normal, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex)     
                             // Offset to color member variable in class Vertex
                            , reinterpret_cast<void*>( offsetof(Vertex, normal) )
                            );
      #endif 

      #if 1
      glEnableVertexAttribArray(attr_color);    
      glVertexAttribPointer( attr_color, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex)     
                             // Offset to color member variable in class Vertex
                            , reinterpret_cast<void*>( offsetof(Vertex, color) )
                            );
      #endif 

      // -------- Upload indices ----------------------//
      //
      if (!mesh.indices.empty())
      {
          glBindVertexArray(vao);
          glGenBuffers(1, &ibo);
          glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo);
          glBufferData(GL_ELEMENT_ARRAY_BUFFER, mesh.indices.size() * sizeof(GLuint)
                       , mesh.indices.data(), GL_STATIC_DRAW );
      }

      // ------ Disable Global state set by this function -----//
      // Unbind VAO 
      glBindVertexArray(0);
      // Unbind VBO 
      glBindBuffer(GL_ARRAY_BUFFER, 0);
      // Unbind IBO 
      glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, 0);
      // Disable current shader attribute 
      glDisableVertexAttribArray(attr_position);
      glDisableVertexAttribArray(attr_normal);
      glDisableVertexAttribArray(attr_color);

  }

  // Load mesh from embedded resource file 
  Mesh load_model(const std::string& file, const Color& color)
  {
      auto reader_config = tinyobj::ObjReaderConfig{};
      reader_config.mtl_search_path = "./";
      auto reader = tinyobj::ObjReader{};

      std::string data = get_resource(file);

      // Parse from file:
      // if( !reader.ParseFromString(file, reader_config) || !reader.Error().empty() 
      if( !reader.ParseFromString(data, "", reader_config) || !reader.Error().empty() )
      {
          std::cerr << " [ERROR] " << reader.Error() << '\n';
          std::abort();
      }
      if( !reader.Warning().empty() )
      {
          std::cerr << " [WARNING] TinyObjReader = " << reader.Warning();
      }
      auto &attrib = reader.GetAttrib();
      auto &shapes = reader.GetShapes();
      auto &materials = reader.GetMaterials();
      assert( shapes.size() >= 1 && "It needs at least one shape." );

      Mesh mesh;
      mesh.draw_type = GL_TRIANGLES;

      size_t index_offset = 0;
      int i = 0;

      // Positive infinity point 
      glm::vec3 current = {0.0, 0.0, 0.0}, a = {2.0 / 0.0, 2.0, 2.0 / 0.0};

      // Loop over face polygon 
      for( size_t f  = 0; f < shapes[0].mesh.num_face_vertices.size(); f++ )
      {
          int fv = shapes[0].mesh.num_face_vertices[f];
          // Loop over vertices in the face 
          for(size_t v = 0; v < fv; v++)
          {
              tinyobj::index_t idx = shapes[0].mesh.indices[index_offset + v];
              tinyobj::real_t vx = attrib.vertices[3 * idx.vertex_index + 0];
              tinyobj::real_t vy = attrib.vertices[3 * idx.vertex_index + 1];
              tinyobj::real_t vz = attrib.vertices[3 * idx.vertex_index + 2];

              tinyobj::real_t nx = attrib.normals[3 * idx.normal_index + 0];
              tinyobj::real_t ny = attrib.normals[3 * idx.normal_index + 1];
              tinyobj::real_t nz = attrib.normals[3 * idx.normal_index + 2];
              // Add vertex to mesh 
              mesh.vertices.push_back({ Position{vx, vy, vz}, Normal{nx , ny, nz}, color});
              current = {vx, vy, vz};
              if( glm::length( current ) < glm::length(a)){ a = current; }
              // std::fprintf(stderr, " [TRACE] i = %d; x = %f ; y = %f ; z = %f \n", i++, vx, vy, vz); 
              // std::fprintf(stderr, " [TRACE] i = %d; nx = %f ; ny = %f ; nz = %f \n", i++, nx, ny, nz); 
          }

          index_offset += fv;
          shapes[0].mesh.material_ids[f];
      }

      std::cout << " [DEBUG] Point with minimum distance from origin = " << a << '\n';
      return mesh;
  }

  std::string 
  get_resource(std::string file_name)
  {
      std::cout << " [TRACE] Before file " << '\n';
      auto fs = cmrc::resource::get_filesystem();
      std::cout << " [TRACE] Resource = " << fs.is_file(file_name ) << '\n';
      assert( fs.is_file(file_name) && "Unable to resource find file."  );
      auto fd = fs.open(file_name);
      return std::string(fd.begin(), fd.end());
  }
#+END_SRC

  *Building on Linux* 

#+BEGIN_SRC sh 
   $ cmake -H. -B_build_linux -DCMAKE_BUILD_TYPE=Debug
   $ cmake --build _build_linux --target
#+END_SRC

  *Cross-compiling for Windows from Linux via MINGW Docker* 

#+BEGIN_SRC sh 
   $ docker run -it --rm -v $PWD:/cwd -w /cwd --entrypoint=bash dockcross/windows-static-x64
   $ cmake -H. -B_build_windows -DCMAKE_BUILD_TYPE=Debug
   $ cmake --build _build_windows --target
#+END_SRC
