``````
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License

// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

// Copyright 2007, Daniel Fontijne, University of Amsterdam -- fontijne@science.uva.nl

#ifdef WIN32
#include <windows.h>
#endif

#include <GL/gl.h>
#include <GL/glut.h>
#include <stdio.h>
#include <stdlib.h>

#include <string>

#include <libgasandbox/e3ga.h>
#include <libgasandbox/e3ga_draw.h>
#include <libgasandbox/e3ga_util.h>
#include <libgasandbox/gl_util.h>
#include <libgasandbox/glut_util.h>

using namespace e3ga;
using namespace mv_draw;

const char *WINDOW_TITLE = "Geometric Algebra, Chapter 6, Example 2: Gram-Schmidt Orthogonalization";

// GLUT state information
int g_viewportWidth = 800;
int g_viewportHeight = 600;

// mouse position on last call to MouseButton() / MouseMotion()
e3ga::vector g_prevMousePos;

// when true, MouseMotion() will rotate the model
bool g_rotateModel = false;
bool g_rotateModelOutOfPlane = false;

// rotation of the model
e3ga::rotor g_modelRotor(_rotor(1.0f));

// when dragging vectors: which one, and at what depth:
float g_dragDistance = -1.0f;
int g_dragObject = -1;

// the three non-orthogonal vectors:
e3ga::vector g_vectors[3] = {
_vector(e1 - e2 - 0.3f * e3),
_vector(e1 + 0.3f * e2 - 0.1f * e3),
_vector(e1 + e3)
};

// the three orthogonal vectors:
e3ga::vector g_orthoVectors[3];

/**
Uses GA to perform Gram-Schmidt orthogonalization.
Throws std::string when input vectors (vIn) are dependent.
Results are returned in 'vOut'.
*/
void GramSchmidtGA(const e3ga::vector vIn[], e3ga::vector vOut[], int nbVectors) {
mv B = 1;

for (int i = 0; i < nbVectors; i++) {
mv newB = vIn[i] ^ B;

// check for dependence of input vectors:
if (_Float(norm_r2(newB)) == 0.0f)
throw std::string("input vectors are dependent");

// compute orthogonal vector 'i':
vOut[i] = _vector(newB * inverse(B));

B = newB;
}
}

void display() {
doIntelWarning(); // warn for possible problems with pciking on Intel graphics chipsets

// update the orthogonal vectors
try {
GramSchmidtGA(g_vectors, g_orthoVectors, 3);
} catch (const std::string &str) {
printf("Error: %s\n", str.c_str());
}

// setup projection & transform for the vectors:
glViewport(0, 0, g_viewportWidth, g_viewportHeight);
glMatrixMode(GL_MODELVIEW);
glMatrixMode(GL_PROJECTION);
const float screenWidth = 1600.0f;
GLpick::g_frustumWidth = 2.0 *  (double)g_viewportWidth / screenWidth;
GLpick::g_frustumHeight = 2.0 *  (double)g_viewportHeight / screenWidth;
glFrustum(
-GLpick::g_frustumWidth / 2.0, GLpick::g_frustumWidth / 2.0,
-GLpick::g_frustumHeight / 2.0, GLpick::g_frustumHeight / 2.0,
GLpick::g_frustumNear, GLpick::g_frustumFar);
glMatrixMode(GL_MODELVIEW);
glTranslatef(0.0f, 0.0f, -12.0f);

glClearColor(0.3f, 0.3f, 0.3f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

glEnable(GL_DEPTH_TEST);
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
glEnable(GL_LIGHTING);
glEnable(GL_LIGHT0);

glMatrixMode(GL_MODELVIEW);
glPushMatrix();

const float horDistance = 2.5f;

glTranslatef(-horDistance, 0.0f, 0.0f);

glPushMatrix();
rotorGLMult(g_modelRotor);

// draw vector 1
glColor3fm(1.0f, 0.0f, 0.0f);
draw(g_vectors[0]);

// draw vector 2
glColor3fm(0.0f, 1.0f, 0.0f);
draw(g_vectors[1]);

// draw vector 3
glColor3fm(0.0f, 0.0f, 1.0f);
draw(g_vectors[2]);

glPopMatrix();

if (!GLpick::g_pickActive) {
glTranslatef(2.0f * horDistance, 0.0f, 0.0f);

glPushMatrix();
rotorGLMult(g_modelRotor);

// draw ortho vector 1
glColor3fm(1.0f, 0.0f, 0.0f);
draw(g_orthoVectors[0]);

// draw ortho vector 2
glColor3fm(0.0f, 1.0f, 0.0f);
draw(g_orthoVectors[1]);

// draw ortho vector 3
glColor3fm(0.0f, 0.0f, 1.0f);
draw(g_orthoVectors[2]);

glPopMatrix();
}

glPopMatrix();

if (!GLpick::g_pickActive) {
glViewport(0, 0, g_viewportWidth, g_viewportHeight);
glMatrixMode(GL_PROJECTION);
glOrtho(0, g_viewportWidth, 0, g_viewportHeight, -100.0, 100.0);
glMatrixMode(GL_MODELVIEW);

glDisable(GL_LIGHTING);
glColor3f(1.0f, 1.0f, 1.0f);
void *font = GLUT_BITMAP_HELVETICA_12;
renderBitmapString(g_viewportWidth / 4 - 50, g_viewportHeight - 20, font, "NON-ORTHOGONAL");
renderBitmapString(g_viewportWidth / 4 - 110, 20, font, "-use mouse to drag vectors and orbit scene");
renderBitmapString(g_viewportWidth * 3 / 4 - 50, g_viewportHeight - 20, font, "ORTHOGONAL");
}

if (!GLpick::g_pickActive) {
glutSwapBuffers();
}
}

void reshape(GLint width, GLint height) {
g_viewportWidth = width;
g_viewportHeight = height;

// redraw viewport
glutPostRedisplay();
}

e3ga::vector vectorAtDepth(double depth, const e3ga::vector &v2d) {
if ((GLpick::g_frustumWidth <= 0) || (GLpick::g_frustumHeight <= 0) ||
(GLpick::g_frustumNear <= 0) || (GLpick::g_frustumFar <= 0)) {
return e3ga::vector();
}

return _vector((depth * (double)v2d.e1() * GLpick::g_frustumWidth) / (g_viewportWidth * GLpick::g_frustumNear) * e1 +
(depth * (double)v2d.e2() * GLpick::g_frustumHeight) / (g_viewportHeight * GLpick::g_frustumNear) * e2);
}

e3ga::vector mousePosToVector(int x, int y) {
x -= g_viewportWidth / 2;
y -= g_viewportHeight / 2;
return e3ga::_vector((float)x * e3ga::e1 - (float)y * e3ga::e2);
}

void MouseButton(int button, int state, int x, int y) {
g_rotateModel = false;

g_prevMousePos = mousePosToVector(x, y);

g_dragObject = pick(x, g_viewportHeight - y, display, &g_dragDistance);
if (g_dragObject < 0) {
e3ga::vector mousePos = mousePosToVector(x, y);
g_rotateModel = true;
if ((_Float(norm_e(mousePos)) / _Float(norm_e(g_viewportWidth * e1 + g_viewportHeight * e2))) < 0.2)
g_rotateModelOutOfPlane = true;
else g_rotateModelOutOfPlane = false;
}
}

void MouseMotion(int x, int y) {
// get mouse position, motion
e3ga::vector mousePos = mousePosToVector(x, y);
e3ga::vector motion = _vector(mousePos - g_prevMousePos);
if (g_rotateModel) {
// update rotor
if (g_rotateModelOutOfPlane)
g_modelRotor = _rotor(e3ga::exp(0.005f * (motion ^ e3ga::e3)) * g_modelRotor);
else g_modelRotor = _rotor(e3ga::exp(0.00001f * (motion ^ mousePos)) * g_modelRotor);
}
else if ((g_dragObject >= 1) && (g_dragObject <= 3)) {
e3ga::vector T = vectorAtDepth(g_dragDistance, motion);
T = _vector(inverse(g_modelRotor) * T * g_modelRotor);
g_vectors[g_dragObject-1] += T;
}

// remember mouse pos for next motion:
g_prevMousePos = mousePos;

// redraw viewport
glutPostRedisplay();

}

int main(int argc, char*argv[]) {
// profiling for Gaigen 2:
e3ga::g2Profiling::init();

// GLUT Window Initialization:
glutInit (&argc, argv);
glutInitWindowSize(g_viewportWidth, g_viewportHeight);
glutInitDisplayMode( GLUT_RGB | GLUT_ALPHA | GLUT_DOUBLE | GLUT_DEPTH);
glutCreateWindow(WINDOW_TITLE);

// Register callbacks:
glutDisplayFunc(display);
glutReshapeFunc(reshape);
glutMouseFunc(MouseButton);
glutMotionFunc(MouseMotion);

glutMainLoop();

return 0;
}

``````