aboutsummaryrefslogtreecommitdiff
path: root/demos
diff options
context:
space:
mode:
Diffstat (limited to 'demos')
-rw-r--r--demos/CMakeLists.txt13
-rw-r--r--demos/mandelbrot/CMakeLists.txt21
-rw-r--r--demos/mandelbrot/README10
-rw-r--r--demos/mandelbrot/mandelbrot.cpp213
-rw-r--r--demos/mandelbrot/mandelbrot.h71
-rw-r--r--demos/mix_eigen_and_c/README9
-rw-r--r--demos/mix_eigen_and_c/binary_library.cpp185
-rw-r--r--demos/mix_eigen_and_c/binary_library.h71
-rw-r--r--demos/mix_eigen_and_c/example.c65
-rw-r--r--demos/opengl/CMakeLists.txt20
-rw-r--r--demos/opengl/README13
-rw-r--r--demos/opengl/camera.cpp264
-rw-r--r--demos/opengl/camera.h118
-rw-r--r--demos/opengl/gpuhelper.cpp126
-rw-r--r--demos/opengl/gpuhelper.h207
-rw-r--r--demos/opengl/icosphere.cpp120
-rw-r--r--demos/opengl/icosphere.h30
-rw-r--r--demos/opengl/quaternion_demo.cpp656
-rw-r--r--demos/opengl/quaternion_demo.h114
-rw-r--r--demos/opengl/trackball.cpp59
-rw-r--r--demos/opengl/trackball.h42
21 files changed, 2427 insertions, 0 deletions
diff --git a/demos/CMakeLists.txt b/demos/CMakeLists.txt
new file mode 100644
index 000000000..b0d2eddbb
--- /dev/null
+++ b/demos/CMakeLists.txt
@@ -0,0 +1,13 @@
+project(EigenDemos)
+
+add_custom_target(demos)
+
+if(NOT EIGEN_TEST_NOQT)
+ find_package(Qt4)
+ if(QT4_FOUND)
+ add_subdirectory(mandelbrot)
+ add_subdirectory(opengl)
+ else(QT4_FOUND)
+ message(STATUS "Qt4 not found, so disabling the mandelbrot and opengl demos")
+ endif(QT4_FOUND)
+endif()
diff --git a/demos/mandelbrot/CMakeLists.txt b/demos/mandelbrot/CMakeLists.txt
new file mode 100644
index 000000000..5c500e064
--- /dev/null
+++ b/demos/mandelbrot/CMakeLists.txt
@@ -0,0 +1,21 @@
+find_package(Qt4 REQUIRED)
+
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+
+if (CMAKE_COMPILER_IS_GNUCXX)
+ set ( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O2")
+ add_definitions ( "-DNDEBUG" )
+endif (CMAKE_COMPILER_IS_GNUCXX)
+
+include_directories( ${QT_INCLUDE_DIR} )
+
+set(mandelbrot_SRCS
+ mandelbrot.cpp
+)
+
+qt4_automoc(${mandelbrot_SRCS})
+
+add_executable(mandelbrot ${mandelbrot_SRCS})
+add_dependencies(demos mandelbrot)
+
+target_link_libraries(mandelbrot ${QT_QTCORE_LIBRARY} ${QT_QTGUI_LIBRARY})
diff --git a/demos/mandelbrot/README b/demos/mandelbrot/README
new file mode 100644
index 000000000..a451d6551
--- /dev/null
+++ b/demos/mandelbrot/README
@@ -0,0 +1,10 @@
+*** Mandelbrot demo ***
+
+Controls:
+* Left mouse button to center view at a point.
+* Drag vertically with left mouse button to zoom in and out.
+
+Be sure to enable SSE2 or AltiVec to improve performance.
+
+The number of iterations, and the choice between single and double precision, are
+determined at runtime depending on the zoom level.
diff --git a/demos/mandelbrot/mandelbrot.cpp b/demos/mandelbrot/mandelbrot.cpp
new file mode 100644
index 000000000..5d575d5b6
--- /dev/null
+++ b/demos/mandelbrot/mandelbrot.cpp
@@ -0,0 +1,213 @@
+// This file is part of Eigen, a lightweight C++ template library
+// for linear algebra.
+//
+// Copyright (C) 2008 Benoit Jacob <jacob.benoit.1@gmail.com>
+//
+// This Source Code Form is subject to the terms of the Mozilla
+// Public License v. 2.0. If a copy of the MPL was not distributed
+// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#include "mandelbrot.h"
+#include <iostream>
+#include<QtGui/QPainter>
+#include<QtGui/QImage>
+#include<QtGui/QMouseEvent>
+#include<QtCore/QTime>
+
+void MandelbrotWidget::resizeEvent(QResizeEvent *)
+{
+ if(size < width() * height())
+ {
+ std::cout << "reallocate buffer" << std::endl;
+ size = width() * height();
+ if(buffer) delete[]buffer;
+ buffer = new unsigned char[4*size];
+ }
+}
+
+template<typename T> struct iters_before_test { enum { ret = 8 }; };
+template<> struct iters_before_test<double> { enum { ret = 16 }; };
+
+template<typename Real> void MandelbrotThread::render(int img_width, int img_height)
+{
+ enum { packetSize = Eigen::internal::packet_traits<Real>::size }; // number of reals in a Packet
+ typedef Eigen::Array<Real, packetSize, 1> Packet; // wrap a Packet as a vector
+
+ enum { iters_before_test = iters_before_test<Real>::ret };
+ max_iter = (max_iter / iters_before_test) * iters_before_test;
+ const int alignedWidth = (img_width/packetSize)*packetSize;
+ unsigned char *const buffer = widget->buffer;
+ const double xradius = widget->xradius;
+ const double yradius = xradius * img_height / img_width;
+ const int threadcount = widget->threadcount;
+ typedef Eigen::Array<Real, 2, 1> Vector2;
+ Vector2 start(widget->center.x() - widget->xradius, widget->center.y() - yradius);
+ Vector2 step(2*widget->xradius/img_width, 2*yradius/img_height);
+ total_iter = 0;
+
+ for(int y = id; y < img_height; y += threadcount)
+ {
+ int pix = y * img_width;
+
+ // for each pixel, we're going to do the iteration z := z^2 + c where z and c are complex numbers,
+ // starting with z = c = complex coord of the pixel. pzi and pzr denote the real and imaginary parts of z.
+ // pci and pcr denote the real and imaginary parts of c.
+
+ Packet pzi_start, pci_start;
+ for(int i = 0; i < packetSize; i++) pzi_start[i] = pci_start[i] = start.y() + y * step.y();
+
+ for(int x = 0; x < alignedWidth; x += packetSize, pix += packetSize)
+ {
+ Packet pcr, pci = pci_start, pzr, pzi = pzi_start, pzr_buf;
+ for(int i = 0; i < packetSize; i++) pzr[i] = pcr[i] = start.x() + (x+i) * step.x();
+
+ // do the iterations. Every iters_before_test iterations we check for divergence,
+ // in which case we can stop iterating.
+ int j = 0;
+ typedef Eigen::Matrix<int, packetSize, 1> Packeti;
+ Packeti pix_iter = Packeti::Zero(), // number of iteration per pixel in the packet
+ pix_dont_diverge; // whether or not each pixel has already diverged
+ do
+ {
+ for(int i = 0; i < iters_before_test/4; i++) // peel the inner loop by 4
+ {
+# define ITERATE \
+ pzr_buf = pzr; \
+ pzr = pzr.square(); \
+ pzr -= pzi.square(); \
+ pzr += pcr; \
+ pzi = (2*pzr_buf)*pzi; \
+ pzi += pci;
+ ITERATE ITERATE ITERATE ITERATE
+ }
+ pix_dont_diverge = ((pzr.square() + pzi.square())
+ .eval() // temporary fix as what follows is not yet vectorized by Eigen
+ <= Packet::Constant(4))
+ // the 4 here is not a magic value, it's a math fact that if
+ // the square modulus is >4 then divergence is inevitable.
+ .template cast<int>();
+ pix_iter += iters_before_test * pix_dont_diverge;
+ j++;
+ total_iter += iters_before_test * packetSize;
+ }
+ while(j < max_iter/iters_before_test && pix_dont_diverge.any()); // any() is not yet vectorized by Eigen
+
+ // compute pixel colors
+ for(int i = 0; i < packetSize; i++)
+ {
+ buffer[4*(pix+i)] = 255*pix_iter[i]/max_iter;
+ buffer[4*(pix+i)+1] = 0;
+ buffer[4*(pix+i)+2] = 0;
+ }
+ }
+
+ // if the width is not a multiple of packetSize, fill the remainder in black
+ for(int x = alignedWidth; x < img_width; x++, pix++)
+ buffer[4*pix] = buffer[4*pix+1] = buffer[4*pix+2] = 0;
+ }
+ return;
+}
+
+void MandelbrotThread::run()
+{
+ setTerminationEnabled(true);
+ double resolution = widget->xradius*2/widget->width();
+ max_iter = 128;
+ if(resolution < 1e-4f) max_iter += 128 * ( - 4 - std::log10(resolution));
+ int img_width = widget->width()/widget->draft;
+ int img_height = widget->height()/widget->draft;
+ single_precision = resolution > 1e-7f;
+
+ if(single_precision)
+ render<float>(img_width, img_height);
+ else
+ render<double>(img_width, img_height);
+}
+
+void MandelbrotWidget::paintEvent(QPaintEvent *)
+{
+ static float max_speed = 0;
+ long long total_iter = 0;
+
+ QTime time;
+ time.start();
+ for(int th = 0; th < threadcount; th++)
+ threads[th]->start(QThread::LowPriority);
+ for(int th = 0; th < threadcount; th++)
+ {
+ threads[th]->wait();
+ total_iter += threads[th]->total_iter;
+ }
+ int elapsed = time.elapsed();
+
+ if(draft == 1)
+ {
+ float speed = elapsed ? float(total_iter)*1000/elapsed : 0;
+ max_speed = std::max(max_speed, speed);
+ std::cout << threadcount << " threads, "
+ << elapsed << " ms, "
+ << speed << " iters/s (max " << max_speed << ")" << std::endl;
+ int packetSize = threads[0]->single_precision
+ ? int(Eigen::internal::packet_traits<float>::size)
+ : int(Eigen::internal::packet_traits<double>::size);
+ setWindowTitle(QString("resolution ")+QString::number(xradius*2/width(), 'e', 2)
+ +QString(", %1 iterations per pixel, ").arg(threads[0]->max_iter)
+ +(threads[0]->single_precision ? QString("single ") : QString("double "))
+ +QString("precision, ")
+ +(packetSize==1 ? QString("no vectorization")
+ : QString("vectorized (%1 per packet)").arg(packetSize)));
+ }
+
+ QImage image(buffer, width()/draft, height()/draft, QImage::Format_RGB32);
+ QPainter painter(this);
+ painter.drawImage(QPoint(0, 0), image.scaled(width(), height()));
+
+ if(draft>1)
+ {
+ draft /= 2;
+ setWindowTitle(QString("recomputing at 1/%1 resolution...").arg(draft));
+ update();
+ }
+}
+
+void MandelbrotWidget::mousePressEvent(QMouseEvent *event)
+{
+ if( event->buttons() & Qt::LeftButton )
+ {
+ lastpos = event->pos();
+ double yradius = xradius * height() / width();
+ center = Eigen::Vector2d(center.x() + (event->pos().x() - width()/2) * xradius * 2 / width(),
+ center.y() + (event->pos().y() - height()/2) * yradius * 2 / height());
+ draft = 16;
+ for(int th = 0; th < threadcount; th++)
+ threads[th]->terminate();
+ update();
+ }
+}
+
+void MandelbrotWidget::mouseMoveEvent(QMouseEvent *event)
+{
+ QPoint delta = event->pos() - lastpos;
+ lastpos = event->pos();
+ if( event->buttons() & Qt::LeftButton )
+ {
+ double t = 1 + 5 * double(delta.y()) / height();
+ if(t < 0.5) t = 0.5;
+ if(t > 2) t = 2;
+ xradius *= t;
+ draft = 16;
+ for(int th = 0; th < threadcount; th++)
+ threads[th]->terminate();
+ update();
+ }
+}
+
+int main(int argc, char *argv[])
+{
+ QApplication app(argc, argv);
+ MandelbrotWidget w;
+ w.show();
+ return app.exec();
+}
+
+#include "mandelbrot.moc"
diff --git a/demos/mandelbrot/mandelbrot.h b/demos/mandelbrot/mandelbrot.h
new file mode 100644
index 000000000..a687fd016
--- /dev/null
+++ b/demos/mandelbrot/mandelbrot.h
@@ -0,0 +1,71 @@
+// This file is part of Eigen, a lightweight C++ template library
+// for linear algebra.
+//
+// Copyright (C) 2008 Benoit Jacob <jacob.benoit.1@gmail.com>
+//
+// This Source Code Form is subject to the terms of the Mozilla
+// Public License v. 2.0. If a copy of the MPL was not distributed
+// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#ifndef MANDELBROT_H
+#define MANDELBROT_H
+
+#include <Eigen/Core>
+#include <QtGui/QApplication>
+#include <QtGui/QWidget>
+#include <QtCore/QThread>
+
+class MandelbrotWidget;
+
+class MandelbrotThread : public QThread
+{
+ friend class MandelbrotWidget;
+ MandelbrotWidget *widget;
+ long long total_iter;
+ int id, max_iter;
+ bool single_precision;
+
+ public:
+ MandelbrotThread(MandelbrotWidget *w, int i) : widget(w), id(i) {}
+ void run();
+ template<typename Real> void render(int img_width, int img_height);
+};
+
+class MandelbrotWidget : public QWidget
+{
+ Q_OBJECT
+
+ friend class MandelbrotThread;
+ Eigen::Vector2d center;
+ double xradius;
+ int size;
+ unsigned char *buffer;
+ QPoint lastpos;
+ int draft;
+ MandelbrotThread **threads;
+ int threadcount;
+
+ protected:
+ void resizeEvent(QResizeEvent *);
+ void paintEvent(QPaintEvent *);
+ void mousePressEvent(QMouseEvent *event);
+ void mouseMoveEvent(QMouseEvent *event);
+
+ public:
+ MandelbrotWidget() : QWidget(), center(0,0), xradius(2),
+ size(0), buffer(0), draft(16)
+ {
+ setAutoFillBackground(false);
+ threadcount = QThread::idealThreadCount();
+ threads = new MandelbrotThread*[threadcount];
+ for(int th = 0; th < threadcount; th++) threads[th] = new MandelbrotThread(this, th);
+ }
+ ~MandelbrotWidget()
+ {
+ if(buffer) delete[]buffer;
+ for(int th = 0; th < threadcount; th++) delete threads[th];
+ delete[] threads;
+ }
+};
+
+#endif // MANDELBROT_H
diff --git a/demos/mix_eigen_and_c/README b/demos/mix_eigen_and_c/README
new file mode 100644
index 000000000..21dba8679
--- /dev/null
+++ b/demos/mix_eigen_and_c/README
@@ -0,0 +1,9 @@
+This is an example of how one can wrap some of Eigen into a C library.
+
+To try this with GCC, do:
+
+ g++ -c binary_library.cpp -O2 -msse2 -I ../..
+ gcc example.c binary_library.o -o example -lstdc++
+ ./example
+
+TODO: add CMakeLists, add more explanations here \ No newline at end of file
diff --git a/demos/mix_eigen_and_c/binary_library.cpp b/demos/mix_eigen_and_c/binary_library.cpp
new file mode 100644
index 000000000..15a2d03e9
--- /dev/null
+++ b/demos/mix_eigen_and_c/binary_library.cpp
@@ -0,0 +1,185 @@
+// This file is part of Eigen, a lightweight C++ template library
+// for linear algebra.
+//
+// Copyright (C) 2009 Benoit Jacob <jacob.benoit.1@gmail.com>
+//
+// This Source Code Form is subject to the terms of the Mozilla
+// Public License v. 2.0. If a copy of the MPL was not distributed
+// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+// This C++ file compiles to binary code that can be linked to by your C program,
+// thanks to the extern "C" syntax used in the declarations in binary_library.h.
+
+#include "binary_library.h"
+
+#include <Eigen/Core>
+
+using namespace Eigen;
+
+/************************* pointer conversion methods **********************************************/
+
+////// class MatrixXd //////
+
+inline MatrixXd& c_to_eigen(C_MatrixXd* ptr)
+{
+ return *reinterpret_cast<MatrixXd*>(ptr);
+}
+
+inline const MatrixXd& c_to_eigen(const C_MatrixXd* ptr)
+{
+ return *reinterpret_cast<const MatrixXd*>(ptr);
+}
+
+inline C_MatrixXd* eigen_to_c(MatrixXd& ref)
+{
+ return reinterpret_cast<C_MatrixXd*>(&ref);
+}
+
+inline const C_MatrixXd* eigen_to_c(const MatrixXd& ref)
+{
+ return reinterpret_cast<const C_MatrixXd*>(&ref);
+}
+
+////// class Map<MatrixXd> //////
+
+inline Map<MatrixXd>& c_to_eigen(C_Map_MatrixXd* ptr)
+{
+ return *reinterpret_cast<Map<MatrixXd>*>(ptr);
+}
+
+inline const Map<MatrixXd>& c_to_eigen(const C_Map_MatrixXd* ptr)
+{
+ return *reinterpret_cast<const Map<MatrixXd>*>(ptr);
+}
+
+inline C_Map_MatrixXd* eigen_to_c(Map<MatrixXd>& ref)
+{
+ return reinterpret_cast<C_Map_MatrixXd*>(&ref);
+}
+
+inline const C_Map_MatrixXd* eigen_to_c(const Map<MatrixXd>& ref)
+{
+ return reinterpret_cast<const C_Map_MatrixXd*>(&ref);
+}
+
+
+/************************* implementation of classes **********************************************/
+
+
+////// class MatrixXd //////
+
+
+C_MatrixXd* MatrixXd_new(int rows, int cols)
+{
+ return eigen_to_c(*new MatrixXd(rows,cols));
+}
+
+void MatrixXd_delete(C_MatrixXd *m)
+{
+ delete &c_to_eigen(m);
+}
+
+double* MatrixXd_data(C_MatrixXd *m)
+{
+ return c_to_eigen(m).data();
+}
+
+void MatrixXd_set_zero(C_MatrixXd *m)
+{
+ c_to_eigen(m).setZero();
+}
+
+void MatrixXd_resize(C_MatrixXd *m, int rows, int cols)
+{
+ c_to_eigen(m).resize(rows,cols);
+}
+
+void MatrixXd_copy(C_MatrixXd *dst, const C_MatrixXd *src)
+{
+ c_to_eigen(dst) = c_to_eigen(src);
+}
+
+void MatrixXd_copy_map(C_MatrixXd *dst, const C_Map_MatrixXd *src)
+{
+ c_to_eigen(dst) = c_to_eigen(src);
+}
+
+void MatrixXd_set_coeff(C_MatrixXd *m, int i, int j, double coeff)
+{
+ c_to_eigen(m)(i,j) = coeff;
+}
+
+double MatrixXd_get_coeff(const C_MatrixXd *m, int i, int j)
+{
+ return c_to_eigen(m)(i,j);
+}
+
+void MatrixXd_print(const C_MatrixXd *m)
+{
+ std::cout << c_to_eigen(m) << std::endl;
+}
+
+void MatrixXd_multiply(const C_MatrixXd *m1, const C_MatrixXd *m2, C_MatrixXd *result)
+{
+ c_to_eigen(result) = c_to_eigen(m1) * c_to_eigen(m2);
+}
+
+void MatrixXd_add(const C_MatrixXd *m1, const C_MatrixXd *m2, C_MatrixXd *result)
+{
+ c_to_eigen(result) = c_to_eigen(m1) + c_to_eigen(m2);
+}
+
+
+
+////// class Map_MatrixXd //////
+
+
+C_Map_MatrixXd* Map_MatrixXd_new(double *array, int rows, int cols)
+{
+ return eigen_to_c(*new Map<MatrixXd>(array,rows,cols));
+}
+
+void Map_MatrixXd_delete(C_Map_MatrixXd *m)
+{
+ delete &c_to_eigen(m);
+}
+
+void Map_MatrixXd_set_zero(C_Map_MatrixXd *m)
+{
+ c_to_eigen(m).setZero();
+}
+
+void Map_MatrixXd_copy(C_Map_MatrixXd *dst, const C_Map_MatrixXd *src)
+{
+ c_to_eigen(dst) = c_to_eigen(src);
+}
+
+void Map_MatrixXd_copy_matrix(C_Map_MatrixXd *dst, const C_MatrixXd *src)
+{
+ c_to_eigen(dst) = c_to_eigen(src);
+}
+
+void Map_MatrixXd_set_coeff(C_Map_MatrixXd *m, int i, int j, double coeff)
+{
+ c_to_eigen(m)(i,j) = coeff;
+}
+
+double Map_MatrixXd_get_coeff(const C_Map_MatrixXd *m, int i, int j)
+{
+ return c_to_eigen(m)(i,j);
+}
+
+void Map_MatrixXd_print(const C_Map_MatrixXd *m)
+{
+ std::cout << c_to_eigen(m) << std::endl;
+}
+
+void Map_MatrixXd_multiply(const C_Map_MatrixXd *m1, const C_Map_MatrixXd *m2, C_Map_MatrixXd *result)
+{
+ c_to_eigen(result) = c_to_eigen(m1) * c_to_eigen(m2);
+}
+
+void Map_MatrixXd_add(const C_Map_MatrixXd *m1, const C_Map_MatrixXd *m2, C_Map_MatrixXd *result)
+{
+ c_to_eigen(result) = c_to_eigen(m1) + c_to_eigen(m2);
+}
diff --git a/demos/mix_eigen_and_c/binary_library.h b/demos/mix_eigen_and_c/binary_library.h
new file mode 100644
index 000000000..0b983ad3a
--- /dev/null
+++ b/demos/mix_eigen_and_c/binary_library.h
@@ -0,0 +1,71 @@
+// This file is part of Eigen, a lightweight C++ template library
+// for linear algebra.
+//
+// Copyright (C) 2009 Benoit Jacob <jacob.benoit.1@gmail.com>
+//
+// This Source Code Form is subject to the terms of the Mozilla
+// Public License v. 2.0. If a copy of the MPL was not distributed
+// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+// This is a pure C header, no C++ here.
+// The functions declared here will be implemented in C++ but
+// we don't have to know, because thanks to the extern "C" syntax,
+// they will be compiled to C object code.
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+ // just dummy empty structs to give different pointer types,
+ // instead of using void* which would be type unsafe
+ struct C_MatrixXd {};
+ struct C_Map_MatrixXd {};
+
+ // the C_MatrixXd class, wraps some of the functionality
+ // of Eigen::MatrixXd.
+ struct C_MatrixXd* MatrixXd_new(int rows, int cols);
+ void MatrixXd_delete (struct C_MatrixXd *m);
+ double* MatrixXd_data (struct C_MatrixXd *m);
+ void MatrixXd_set_zero (struct C_MatrixXd *m);
+ void MatrixXd_resize (struct C_MatrixXd *m, int rows, int cols);
+ void MatrixXd_copy (struct C_MatrixXd *dst,
+ const struct C_MatrixXd *src);
+ void MatrixXd_copy_map (struct C_MatrixXd *dst,
+ const struct C_Map_MatrixXd *src);
+ void MatrixXd_set_coeff (struct C_MatrixXd *m,
+ int i, int j, double coeff);
+ double MatrixXd_get_coeff (const struct C_MatrixXd *m,
+ int i, int j);
+ void MatrixXd_print (const struct C_MatrixXd *m);
+ void MatrixXd_add (const struct C_MatrixXd *m1,
+ const struct C_MatrixXd *m2,
+ struct C_MatrixXd *result);
+ void MatrixXd_multiply (const struct C_MatrixXd *m1,
+ const struct C_MatrixXd *m2,
+ struct C_MatrixXd *result);
+
+ // the C_Map_MatrixXd class, wraps some of the functionality
+ // of Eigen::Map<MatrixXd>
+ struct C_Map_MatrixXd* Map_MatrixXd_new(double *array, int rows, int cols);
+ void Map_MatrixXd_delete (struct C_Map_MatrixXd *m);
+ void Map_MatrixXd_set_zero (struct C_Map_MatrixXd *m);
+ void Map_MatrixXd_copy (struct C_Map_MatrixXd *dst,
+ const struct C_Map_MatrixXd *src);
+ void Map_MatrixXd_copy_matrix(struct C_Map_MatrixXd *dst,
+ const struct C_MatrixXd *src);
+ void Map_MatrixXd_set_coeff (struct C_Map_MatrixXd *m,
+ int i, int j, double coeff);
+ double Map_MatrixXd_get_coeff (const struct C_Map_MatrixXd *m,
+ int i, int j);
+ void Map_MatrixXd_print (const struct C_Map_MatrixXd *m);
+ void Map_MatrixXd_add (const struct C_Map_MatrixXd *m1,
+ const struct C_Map_MatrixXd *m2,
+ struct C_Map_MatrixXd *result);
+ void Map_MatrixXd_multiply (const struct C_Map_MatrixXd *m1,
+ const struct C_Map_MatrixXd *m2,
+ struct C_Map_MatrixXd *result);
+
+#ifdef __cplusplus
+} // end extern "C"
+#endif \ No newline at end of file
diff --git a/demos/mix_eigen_and_c/example.c b/demos/mix_eigen_and_c/example.c
new file mode 100644
index 000000000..508eb5467
--- /dev/null
+++ b/demos/mix_eigen_and_c/example.c
@@ -0,0 +1,65 @@
+// This file is part of Eigen, a lightweight C++ template library
+// for linear algebra.
+//
+// Copyright (C) 2009 Benoit Jacob <jacob.benoit.1@gmail.com>
+//
+// This Source Code Form is subject to the terms of the Mozilla
+// Public License v. 2.0. If a copy of the MPL was not distributed
+// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#include "binary_library.h"
+#include "stdio.h"
+
+void demo_MatrixXd()
+{
+ struct C_MatrixXd *matrix1, *matrix2, *result;
+ printf("*** demo_MatrixXd ***\n");
+
+ matrix1 = MatrixXd_new(3, 3);
+ MatrixXd_set_zero(matrix1);
+ MatrixXd_set_coeff(matrix1, 0, 1, 2.5);
+ MatrixXd_set_coeff(matrix1, 1, 0, 1.4);
+ printf("Here is matrix1:\n");
+ MatrixXd_print(matrix1);
+
+ matrix2 = MatrixXd_new(3, 3);
+ MatrixXd_multiply(matrix1, matrix1, matrix2);
+ printf("Here is matrix1*matrix1:\n");
+ MatrixXd_print(matrix2);
+
+ MatrixXd_delete(matrix1);
+ MatrixXd_delete(matrix2);
+}
+
+// this helper function takes a plain C array and prints it in one line
+void print_array(double *array, int n)
+{
+ struct C_Map_MatrixXd *m = Map_MatrixXd_new(array, 1, n);
+ Map_MatrixXd_print(m);
+ Map_MatrixXd_delete(m);
+}
+
+void demo_Map_MatrixXd()
+{
+ struct C_Map_MatrixXd *map;
+ double array[5];
+ int i;
+ printf("*** demo_Map_MatrixXd ***\n");
+
+ for(i = 0; i < 5; ++i) array[i] = i;
+ printf("Initially, the array is:\n");
+ print_array(array, 5);
+
+ map = Map_MatrixXd_new(array, 5, 1);
+ Map_MatrixXd_add(map, map, map);
+ Map_MatrixXd_delete(map);
+
+ printf("Now the array is:\n");
+ print_array(array, 5);
+}
+
+int main()
+{
+ demo_MatrixXd();
+ demo_Map_MatrixXd();
+}
diff --git a/demos/opengl/CMakeLists.txt b/demos/opengl/CMakeLists.txt
new file mode 100644
index 000000000..b98a30c01
--- /dev/null
+++ b/demos/opengl/CMakeLists.txt
@@ -0,0 +1,20 @@
+find_package(Qt4 REQUIRED)
+find_package(OpenGL REQUIRED)
+
+set(QT_USE_QTOPENGL TRUE)
+include(${QT_USE_FILE})
+
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+
+include_directories( ${QT_INCLUDE_DIR} )
+
+set(quaternion_demo_SRCS gpuhelper.cpp icosphere.cpp camera.cpp trackball.cpp quaternion_demo.cpp)
+
+qt4_automoc(${quaternion_demo_SRCS})
+
+add_executable(quaternion_demo ${quaternion_demo_SRCS})
+add_dependencies(demos quaternion_demo)
+
+target_link_libraries(quaternion_demo
+ ${QT_QTCORE_LIBRARY} ${QT_QTGUI_LIBRARY}
+ ${QT_QTOPENGL_LIBRARY} ${OPENGL_LIBRARIES} )
diff --git a/demos/opengl/README b/demos/opengl/README
new file mode 100644
index 000000000..8fb16496c
--- /dev/null
+++ b/demos/opengl/README
@@ -0,0 +1,13 @@
+
+Navigation:
+ left button: rotate around the target
+ middle button: zoom
+ left button + ctrl quake rotate (rotate around camera position)
+ middle button + ctrl walk (progress along camera's z direction)
+ left button: pan (translate in the XY camera's plane)
+
+R : move the camera to initial position
+A : start/stop animation
+C : clear the animation
+G : add a key frame
+
diff --git a/demos/opengl/camera.cpp b/demos/opengl/camera.cpp
new file mode 100644
index 000000000..8a2344c85
--- /dev/null
+++ b/demos/opengl/camera.cpp
@@ -0,0 +1,264 @@
+// This file is part of Eigen, a lightweight C++ template library
+// for linear algebra.
+//
+// Copyright (C) 2008 Gael Guennebaud <gael.guennebaud@inria.fr>
+//
+// This Source Code Form is subject to the terms of the Mozilla
+// Public License v. 2.0. If a copy of the MPL was not distributed
+// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#include "camera.h"
+
+#include "gpuhelper.h"
+#include <GL/glu.h>
+
+#include "Eigen/LU"
+using namespace Eigen;
+
+Camera::Camera()
+ : mViewIsUptodate(false), mProjIsUptodate(false)
+{
+ mViewMatrix.setIdentity();
+
+ mFovY = M_PI/3.;
+ mNearDist = 1.;
+ mFarDist = 50000.;
+
+ mVpX = 0;
+ mVpY = 0;
+
+ setPosition(Vector3f::Constant(100.));
+ setTarget(Vector3f::Zero());
+}
+
+Camera& Camera::operator=(const Camera& other)
+{
+ mViewIsUptodate = false;
+ mProjIsUptodate = false;
+
+ mVpX = other.mVpX;
+ mVpY = other.mVpY;
+ mVpWidth = other.mVpWidth;
+ mVpHeight = other.mVpHeight;
+
+ mTarget = other.mTarget;
+ mFovY = other.mFovY;
+ mNearDist = other.mNearDist;
+ mFarDist = other.mFarDist;
+
+ mViewMatrix = other.mViewMatrix;
+ mProjectionMatrix = other.mProjectionMatrix;
+
+ return *this;
+}
+
+Camera::Camera(const Camera& other)
+{
+ *this = other;
+}
+
+Camera::~Camera()
+{
+}
+
+
+void Camera::setViewport(uint offsetx, uint offsety, uint width, uint height)
+{
+ mVpX = offsetx;
+ mVpY = offsety;
+ mVpWidth = width;
+ mVpHeight = height;
+
+ mProjIsUptodate = false;
+}
+
+void Camera::setViewport(uint width, uint height)
+{
+ mVpWidth = width;
+ mVpHeight = height;
+
+ mProjIsUptodate = false;
+}
+
+void Camera::setFovY(float value)
+{
+ mFovY = value;
+ mProjIsUptodate = false;
+}
+
+Vector3f Camera::direction(void) const
+{
+ return - (orientation() * Vector3f::UnitZ());
+}
+Vector3f Camera::up(void) const
+{
+ return orientation() * Vector3f::UnitY();
+}
+Vector3f Camera::right(void) const
+{
+ return orientation() * Vector3f::UnitX();
+}
+
+void Camera::setDirection(const Vector3f& newDirection)
+{
+ // TODO implement it computing the rotation between newDirection and current dir ?
+ Vector3f up = this->up();
+
+ Matrix3f camAxes;
+
+ camAxes.col(2) = (-newDirection).normalized();
+ camAxes.col(0) = up.cross( camAxes.col(2) ).normalized();
+ camAxes.col(1) = camAxes.col(2).cross( camAxes.col(0) ).normalized();
+ setOrientation(Quaternionf(camAxes));
+
+ mViewIsUptodate = false;
+}
+
+void Camera::setTarget(const Vector3f& target)
+{
+ mTarget = target;
+ if (!mTarget.isApprox(position()))
+ {
+ Vector3f newDirection = mTarget - position();
+ setDirection(newDirection.normalized());
+ }
+}
+
+void Camera::setPosition(const Vector3f& p)
+{
+ mFrame.position = p;
+ mViewIsUptodate = false;
+}
+
+void Camera::setOrientation(const Quaternionf& q)
+{
+ mFrame.orientation = q;
+ mViewIsUptodate = false;
+}
+
+void Camera::setFrame(const Frame& f)
+{
+ mFrame = f;
+ mViewIsUptodate = false;
+}
+
+void Camera::rotateAroundTarget(const Quaternionf& q)
+{
+ Matrix4f mrot, mt, mtm;
+
+ // update the transform matrix
+ updateViewMatrix();
+ Vector3f t = mViewMatrix * mTarget;
+
+ mViewMatrix = Translation3f(t)
+ * q
+ * Translation3f(-t)
+ * mViewMatrix;
+
+ Quaternionf qa(mViewMatrix.linear());
+ qa = qa.conjugate();
+ setOrientation(qa);
+ setPosition(- (qa * mViewMatrix.translation()) );
+
+ mViewIsUptodate = true;
+}
+
+void Camera::localRotate(const Quaternionf& q)
+{
+ float dist = (position() - mTarget).norm();
+ setOrientation(orientation() * q);
+ mTarget = position() + dist * direction();
+ mViewIsUptodate = false;
+}
+
+void Camera::zoom(float d)
+{
+ float dist = (position() - mTarget).norm();
+ if(dist > d)
+ {
+ setPosition(position() + direction() * d);
+ mViewIsUptodate = false;
+ }
+}
+
+void Camera::localTranslate(const Vector3f& t)
+{
+ Vector3f trans = orientation() * t;
+ setPosition( position() + trans );
+ setTarget( mTarget + trans );
+
+ mViewIsUptodate = false;
+}
+
+void Camera::updateViewMatrix(void) const
+{
+ if(!mViewIsUptodate)
+ {
+ Quaternionf q = orientation().conjugate();
+ mViewMatrix.linear() = q.toRotationMatrix();
+ mViewMatrix.translation() = - (mViewMatrix.linear() * position());
+
+ mViewIsUptodate = true;
+ }
+}
+
+const Affine3f& Camera::viewMatrix(void) const
+{
+ updateViewMatrix();
+ return mViewMatrix;
+}
+
+void Camera::updateProjectionMatrix(void) const
+{
+ if(!mProjIsUptodate)
+ {
+ mProjectionMatrix.setIdentity();
+ float aspect = float(mVpWidth)/float(mVpHeight);
+ float theta = mFovY*0.5;
+ float range = mFarDist - mNearDist;
+ float invtan = 1./tan(theta);
+
+ mProjectionMatrix(0,0) = invtan / aspect;
+ mProjectionMatrix(1,1) = invtan;
+ mProjectionMatrix(2,2) = -(mNearDist + mFarDist) / range;
+ mProjectionMatrix(3,2) = -1;
+ mProjectionMatrix(2,3) = -2 * mNearDist * mFarDist / range;
+ mProjectionMatrix(3,3) = 0;
+
+ mProjIsUptodate = true;
+ }
+}
+
+const Matrix4f& Camera::projectionMatrix(void) const
+{
+ updateProjectionMatrix();
+ return mProjectionMatrix;
+}
+
+void Camera::activateGL(void)
+{
+ glViewport(vpX(), vpY(), vpWidth(), vpHeight());
+ gpu.loadMatrix(projectionMatrix(),GL_PROJECTION);
+ gpu.loadMatrix(viewMatrix().matrix(),GL_MODELVIEW);
+}
+
+
+Vector3f Camera::unProject(const Vector2f& uv, float depth) const
+{
+ Matrix4f inv = mViewMatrix.inverse().matrix();
+ return unProject(uv, depth, inv);
+}
+
+Vector3f Camera::unProject(const Vector2f& uv, float depth, const Matrix4f& invModelview) const
+{
+ updateViewMatrix();
+ updateProjectionMatrix();
+
+ Vector3f a(2.*uv.x()/float(mVpWidth)-1., 2.*uv.y()/float(mVpHeight)-1., 1.);
+ a.x() *= depth/mProjectionMatrix(0,0);
+ a.y() *= depth/mProjectionMatrix(1,1);
+ a.z() = -depth;
+ // FIXME /\/|
+ Vector4f b = invModelview * Vector4f(a.x(), a.y(), a.z(), 1.);
+ return Vector3f(b.x(), b.y(), b.z());
+}
diff --git a/demos/opengl/camera.h b/demos/opengl/camera.h
new file mode 100644
index 000000000..15714d2e6
--- /dev/null
+++ b/demos/opengl/camera.h
@@ -0,0 +1,118 @@
+// This file is part of Eigen, a lightweight C++ template library
+// for linear algebra.
+//
+// Copyright (C) 2008 Gael Guennebaud <gael.guennebaud@inria.fr>
+//
+// This Source Code Form is subject to the terms of the Mozilla
+// Public License v. 2.0. If a copy of the MPL was not distributed
+// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#ifndef EIGEN_CAMERA_H
+#define EIGEN_CAMERA_H
+
+#include <Eigen/Geometry>
+#include <QObject>
+// #include <frame.h>
+
+class Frame
+{
+ public:
+ EIGEN_MAKE_ALIGNED_OPERATOR_NEW
+
+ inline Frame(const Eigen::Vector3f& pos = Eigen::Vector3f::Zero(),
+ const Eigen::Quaternionf& o = Eigen::Quaternionf())
+ : orientation(o), position(pos)
+ {}
+ Frame lerp(float alpha, const Frame& other) const
+ {
+ return Frame((1.f-alpha)*position + alpha * other.position,
+ orientation.slerp(alpha,other.orientation));
+ }
+
+ Eigen::Quaternionf orientation;
+ Eigen::Vector3f position;
+};
+
+class Camera
+{
+ public:
+ EIGEN_MAKE_ALIGNED_OPERATOR_NEW
+
+ Camera(void);
+
+ Camera(const Camera& other);
+
+ virtual ~Camera();
+
+ Camera& operator=(const Camera& other);
+
+ void setViewport(uint offsetx, uint offsety, uint width, uint height);
+ void setViewport(uint width, uint height);
+
+ inline uint vpX(void) const { return mVpX; }
+ inline uint vpY(void) const { return mVpY; }
+ inline uint vpWidth(void) const { return mVpWidth; }
+ inline uint vpHeight(void) const { return mVpHeight; }
+
+ inline float fovY(void) const { return mFovY; }
+ void setFovY(float value);
+
+ void setPosition(const Eigen::Vector3f& pos);
+ inline const Eigen::Vector3f& position(void) const { return mFrame.position; }
+
+ void setOrientation(const Eigen::Quaternionf& q);
+ inline const Eigen::Quaternionf& orientation(void) const { return mFrame.orientation; }
+
+ void setFrame(const Frame& f);
+ const Frame& frame(void) const { return mFrame; }
+
+ void setDirection(const Eigen::Vector3f& newDirection);
+ Eigen::Vector3f direction(void) const;
+ void setUp(const Eigen::Vector3f& vectorUp);
+ Eigen::Vector3f up(void) const;
+ Eigen::Vector3f right(void) const;
+
+ void setTarget(const Eigen::Vector3f& target);
+ inline const Eigen::Vector3f& target(void) { return mTarget; }
+
+ const Eigen::Affine3f& viewMatrix(void) const;
+ const Eigen::Matrix4f& projectionMatrix(void) const;
+
+ void rotateAroundTarget(const Eigen::Quaternionf& q);
+ void localRotate(const Eigen::Quaternionf& q);
+ void zoom(float d);
+
+ void localTranslate(const Eigen::Vector3f& t);
+
+ /** Setup OpenGL matrices and viewport */
+ void activateGL(void);
+
+ Eigen::Vector3f unProject(const Eigen::Vector2f& uv, float depth, const Eigen::Matrix4f& invModelview) const;
+ Eigen::Vector3f unProject(const Eigen::Vector2f& uv, float depth) const;
+
+ protected:
+ void updateViewMatrix(void) const;
+ void updateProjectionMatrix(void) const;
+
+ protected:
+
+ uint mVpX, mVpY;
+ uint mVpWidth, mVpHeight;
+
+ Frame mFrame;
+
+ mutable Eigen::Affine3f mViewMatrix;
+ mutable Eigen::Matrix4f mProjectionMatrix;
+
+ mutable bool mViewIsUptodate;
+ mutable bool mProjIsUptodate;
+
+ // used by rotateAroundTarget
+ Eigen::Vector3f mTarget;
+
+ float mFovY;
+ float mNearDist;
+ float mFarDist;
+};
+
+#endif // EIGEN_CAMERA_H
diff --git a/demos/opengl/gpuhelper.cpp b/demos/opengl/gpuhelper.cpp
new file mode 100644
index 000000000..fd236b11c
--- /dev/null
+++ b/demos/opengl/gpuhelper.cpp
@@ -0,0 +1,126 @@
+// This file is part of Eigen, a lightweight C++ template library
+// for linear algebra.
+//
+// Copyright (C) 2008 Gael Guennebaud <gael.guennebaud@inria.fr>
+//
+// This Source Code Form is subject to the terms of the Mozilla
+// Public License v. 2.0. If a copy of the MPL was not distributed
+// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#include "gpuhelper.h"
+#include "icosphere.h"
+#include <GL/glu.h>
+// PLEASE don't look at this old code... ;)
+
+#include <fstream>
+#include <algorithm>
+
+GpuHelper gpu;
+
+GpuHelper::GpuHelper()
+{
+ mVpWidth = mVpHeight = 0;
+ mCurrentMatrixTarget = 0;
+ mInitialized = false;
+}
+
+GpuHelper::~GpuHelper()
+{
+}
+
+void GpuHelper::pushProjectionMode2D(ProjectionMode2D pm)
+{
+ // switch to 2D projection
+ pushMatrix(Matrix4f::Identity(),GL_PROJECTION);
+
+ if(pm==PM_Normalized)
+ {
+ //glOrtho(-1., 1., -1., 1., 0., 1.);
+ }
+ else if(pm==PM_Viewport)
+ {
+ GLint vp[4];
+ glGetIntegerv(GL_VIEWPORT, vp);
+ glOrtho(0., vp[2], 0., vp[3], -1., 1.);
+ }
+
+ pushMatrix(Matrix4f::Identity(),GL_MODELVIEW);
+}
+
+void GpuHelper::popProjectionMode2D(void)
+{
+ popMatrix(GL_PROJECTION);
+ popMatrix(GL_MODELVIEW);
+}
+
+void GpuHelper::drawVector(const Vector3f& position, const Vector3f& vec, const Color& color, float aspect /* = 50.*/)
+{
+ static GLUquadricObj *cylindre = gluNewQuadric();
+ glColor4fv(color.data());
+ float length = vec.norm();
+ pushMatrix(GL_MODELVIEW);
+ glTranslatef(position.x(), position.y(), position.z());
+ Vector3f ax = Matrix3f::Identity().col(2).cross(vec);
+ ax.normalize();
+ Vector3f tmp = vec;
+ tmp.normalize();
+ float angle = 180.f/M_PI * acos(tmp.z());
+ if (angle>1e-3)
+ glRotatef(angle, ax.x(), ax.y(), ax.z());
+ gluCylinder(cylindre, length/aspect, length/aspect, 0.8*length, 10, 10);
+ glTranslatef(0.0,0.0,0.8*length);
+ gluCylinder(cylindre, 2.0*length/aspect, 0.0, 0.2*length, 10, 10);
+
+ popMatrix(GL_MODELVIEW);
+}
+
+void GpuHelper::drawVectorBox(const Vector3f& position, const Vector3f& vec, const Color& color, float aspect)
+{
+ static GLUquadricObj *cylindre = gluNewQuadric();
+ glColor4fv(color.data());
+ float length = vec.norm();
+ pushMatrix(GL_MODELVIEW);
+ glTranslatef(position.x(), position.y(), position.z());
+ Vector3f ax = Matrix3f::Identity().col(2).cross(vec);
+ ax.normalize();
+ Vector3f tmp = vec;
+ tmp.normalize();
+ float angle = 180.f/M_PI * acos(tmp.z());
+ if (angle>1e-3)
+ glRotatef(angle, ax.x(), ax.y(), ax.z());
+ gluCylinder(cylindre, length/aspect, length/aspect, 0.8*length, 10, 10);
+ glTranslatef(0.0,0.0,0.8*length);
+ glScalef(4.0*length/aspect,4.0*length/aspect,4.0*length/aspect);
+ drawUnitCube();
+ popMatrix(GL_MODELVIEW);
+}
+
+void GpuHelper::drawUnitCube(void)
+{
+ static float vertices[][3] = {
+ {-0.5,-0.5,-0.5},
+ { 0.5,-0.5,-0.5},
+ {-0.5, 0.5,-0.5},
+ { 0.5, 0.5,-0.5},
+ {-0.5,-0.5, 0.5},
+ { 0.5,-0.5, 0.5},
+ {-0.5, 0.5, 0.5},
+ { 0.5, 0.5, 0.5}};
+
+ glBegin(GL_QUADS);
+ glNormal3f(0,0,-1); glVertex3fv(vertices[0]); glVertex3fv(vertices[2]); glVertex3fv(vertices[3]); glVertex3fv(vertices[1]);
+ glNormal3f(0,0, 1); glVertex3fv(vertices[4]); glVertex3fv(vertices[5]); glVertex3fv(vertices[7]); glVertex3fv(vertices[6]);
+ glNormal3f(0,-1,0); glVertex3fv(vertices[0]); glVertex3fv(vertices[1]); glVertex3fv(vertices[5]); glVertex3fv(vertices[4]);
+ glNormal3f(0, 1,0); glVertex3fv(vertices[2]); glVertex3fv(vertices[6]); glVertex3fv(vertices[7]); glVertex3fv(vertices[3]);
+ glNormal3f(-1,0,0); glVertex3fv(vertices[0]); glVertex3fv(vertices[4]); glVertex3fv(vertices[6]); glVertex3fv(vertices[2]);
+ glNormal3f( 1,0,0); glVertex3fv(vertices[1]); glVertex3fv(vertices[3]); glVertex3fv(vertices[7]); glVertex3fv(vertices[5]);
+ glEnd();
+}
+
+void GpuHelper::drawUnitSphere(int level)
+{
+ static IcoSphere sphere;
+ sphere.draw(level);
+}
+
+
diff --git a/demos/opengl/gpuhelper.h b/demos/opengl/gpuhelper.h
new file mode 100644
index 000000000..9ff98e9dc
--- /dev/null
+++ b/demos/opengl/gpuhelper.h
@@ -0,0 +1,207 @@
+// This file is part of Eigen, a lightweight C++ template library
+// for linear algebra.
+//
+// Copyright (C) 2008 Gael Guennebaud <gael.guennebaud@inria.fr>
+//
+// This Source Code Form is subject to the terms of the Mozilla
+// Public License v. 2.0. If a copy of the MPL was not distributed
+// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#ifndef EIGEN_GPUHELPER_H
+#define EIGEN_GPUHELPER_H
+
+#include <Eigen/Geometry>
+#include <GL/gl.h>
+#include <vector>
+
+using namespace Eigen;
+
+typedef Vector4f Color;
+
+class GpuHelper
+{
+ public:
+
+ GpuHelper();
+
+ ~GpuHelper();
+
+ enum ProjectionMode2D { PM_Normalized = 1, PM_Viewport = 2 };
+ void pushProjectionMode2D(ProjectionMode2D pm);
+ void popProjectionMode2D();
+
+ /** Multiply the OpenGL matrix \a matrixTarget by the matrix \a mat.
+ Essentially, this helper function automatically calls glMatrixMode(matrixTarget) if required
+ and does a proper call to the right glMultMatrix*() function according to the scalar type
+ and storage order.
+ \warning glMatrixMode() must never be called directly. If your're unsure, use forceMatrixMode().
+ \sa Matrix, loadMatrix(), forceMatrixMode()
+ */
+ template<typename Scalar, int _Flags>
+ void multMatrix(const Matrix<Scalar,4,4, _Flags, 4,4>& mat, GLenum matrixTarget);
+
+ /** Load the matrix \a mat to the OpenGL matrix \a matrixTarget.
+ Essentially, this helper function automatically calls glMatrixMode(matrixTarget) if required
+ and does a proper call to the right glLoadMatrix*() or glLoadIdentity() function according to the scalar type
+ and storage order.
+ \warning glMatrixMode() must never be called directly. If your're unsure, use forceMatrixMode().
+ \sa Matrix, multMatrix(), forceMatrixMode()
+ */
+ template<typename Scalar, int _Flags>
+ void loadMatrix(const Eigen::Matrix<Scalar,4,4, _Flags, 4,4>& mat, GLenum matrixTarget);
+
+ template<typename Scalar, typename Derived>
+ void loadMatrix(
+ const Eigen::CwiseNullaryOp<Eigen::internal::scalar_identity_op<Scalar>,Derived>&,
+ GLenum matrixTarget);
+
+ /** Make the matrix \a matrixTarget the current OpenGL matrix target.
+ Call this function before loadMatrix() or multMatrix() if you cannot guarantee that glMatrixMode()
+ has never been called after the last loadMatrix() or multMatrix() calls.
+ \todo provides a debug mode checking the sanity of the cached matrix mode.
+ */
+ inline void forceMatrixTarget(GLenum matrixTarget) {glMatrixMode(mCurrentMatrixTarget=matrixTarget);}
+
+ inline void setMatrixTarget(GLenum matrixTarget);
+
+ /** Push the OpenGL matrix \a matrixTarget and load \a mat.
+ */
+ template<typename Scalar, int _Flags>
+ inline void pushMatrix(const Matrix<Scalar,4,4, _Flags, 4,4>& mat, GLenum matrixTarget);
+
+ template<typename Scalar, typename Derived>
+ void pushMatrix(
+ const Eigen::CwiseNullaryOp<Eigen::internal::scalar_identity_op<Scalar>,Derived>&,
+ GLenum matrixTarget);
+
+ /** Push and clone the OpenGL matrix \a matrixTarget
+ */
+ inline void pushMatrix(GLenum matrixTarget);
+
+ /** Pop the OpenGL matrix \a matrixTarget
+ */
+ inline void popMatrix(GLenum matrixTarget);
+
+ void drawVector(const Vector3f& position, const Vector3f& vec, const Color& color, float aspect = 50.);
+ void drawVectorBox(const Vector3f& position, const Vector3f& vec, const Color& color, float aspect = 50.);
+ void drawUnitCube(void);
+ void drawUnitSphere(int level=0);
+
+ /// draw the \a nofElement first elements
+ inline void draw(GLenum mode, uint nofElement);
+
+ /// draw a range of elements
+ inline void draw(GLenum mode, uint start, uint end);
+
+ /// draw an indexed subset
+ inline void draw(GLenum mode, const std::vector<uint>* pIndexes);
+
+protected:
+
+ void update(void);
+
+ GLuint mColorBufferId;
+ int mVpWidth, mVpHeight;
+ GLenum mCurrentMatrixTarget;
+ bool mInitialized;
+};
+
+/** Singleton shortcut
+*/
+extern GpuHelper gpu;
+
+
+/** \internal
+*/
+template<bool RowMajor, int _Flags> struct GlMatrixHelper;
+
+template<int _Flags> struct GlMatrixHelper<false,_Flags>
+{
+ static void loadMatrix(const Matrix<float, 4,4, _Flags, 4,4>& mat) { glLoadMatrixf(mat.data()); }
+ static void loadMatrix(const Matrix<double,4,4, _Flags, 4,4>& mat) { glLoadMatrixd(mat.data()); }
+ static void multMatrix(const Matrix<float, 4,4, _Flags, 4,4>& mat) { glMultMatrixf(mat.data()); }
+ static void multMatrix(const Matrix<double,4,4, _Flags, 4,4>& mat) { glMultMatrixd(mat.data()); }
+};
+
+template<int _Flags> struct GlMatrixHelper<true,_Flags>
+{
+ static void loadMatrix(const Matrix<float, 4,4, _Flags, 4,4>& mat) { glLoadMatrixf(mat.transpose().eval().data()); }
+ static void loadMatrix(const Matrix<double,4,4, _Flags, 4,4>& mat) { glLoadMatrixd(mat.transpose().eval().data()); }
+ static void multMatrix(const Matrix<float, 4,4, _Flags, 4,4>& mat) { glMultMatrixf(mat.transpose().eval().data()); }
+ static void multMatrix(const Matrix<double,4,4, _Flags, 4,4>& mat) { glMultMatrixd(mat.transpose().eval().data()); }
+};
+
+inline void GpuHelper::setMatrixTarget(GLenum matrixTarget)
+{
+ if (matrixTarget != mCurrentMatrixTarget)
+ glMatrixMode(mCurrentMatrixTarget=matrixTarget);
+}
+
+template<typename Scalar, int _Flags>
+void GpuHelper::multMatrix(const Matrix<Scalar,4,4, _Flags, 4,4>& mat, GLenum matrixTarget)
+{
+ setMatrixTarget(matrixTarget);
+ GlMatrixHelper<_Flags&Eigen::RowMajorBit, _Flags>::multMatrix(mat);
+}
+
+template<typename Scalar, typename Derived>
+void GpuHelper::loadMatrix(
+ const Eigen::CwiseNullaryOp<Eigen::internal::scalar_identity_op<Scalar>,Derived>&,
+ GLenum matrixTarget)
+{
+ setMatrixTarget(matrixTarget);
+ glLoadIdentity();
+}
+
+template<typename Scalar, int _Flags>
+void GpuHelper::loadMatrix(const Eigen::Matrix<Scalar,4,4, _Flags, 4,4>& mat, GLenum matrixTarget)
+{
+ setMatrixTarget(matrixTarget);
+ GlMatrixHelper<(_Flags&Eigen::RowMajorBit)!=0, _Flags>::loadMatrix(mat);
+}
+
+inline void GpuHelper::pushMatrix(GLenum matrixTarget)
+{
+ setMatrixTarget(matrixTarget);
+ glPushMatrix();
+}
+
+template<typename Scalar, int _Flags>
+inline void GpuHelper::pushMatrix(const Matrix<Scalar,4,4, _Flags, 4,4>& mat, GLenum matrixTarget)
+{
+ pushMatrix(matrixTarget);
+ GlMatrixHelper<_Flags&Eigen::RowMajorBit,_Flags>::loadMatrix(mat);
+}
+
+template<typename Scalar, typename Derived>
+void GpuHelper::pushMatrix(
+ const Eigen::CwiseNullaryOp<Eigen::internal::scalar_identity_op<Scalar>,Derived>&,
+ GLenum matrixTarget)
+{
+ pushMatrix(matrixTarget);
+ glLoadIdentity();
+}
+
+inline void GpuHelper::popMatrix(GLenum matrixTarget)
+{
+ setMatrixTarget(matrixTarget);
+ glPopMatrix();
+}
+
+inline void GpuHelper::draw(GLenum mode, uint nofElement)
+{
+ glDrawArrays(mode, 0, nofElement);
+}
+
+
+inline void GpuHelper::draw(GLenum mode, const std::vector<uint>* pIndexes)
+{
+ glDrawElements(mode, pIndexes->size(), GL_UNSIGNED_INT, &(pIndexes->front()));
+}
+
+inline void GpuHelper::draw(GLenum mode, uint start, uint end)
+{
+ glDrawArrays(mode, start, end-start);
+}
+
+#endif // EIGEN_GPUHELPER_H
diff --git a/demos/opengl/icosphere.cpp b/demos/opengl/icosphere.cpp
new file mode 100644
index 000000000..39444cbb6
--- /dev/null
+++ b/demos/opengl/icosphere.cpp
@@ -0,0 +1,120 @@
+// This file is part of Eigen, a lightweight C++ template library
+// for linear algebra.
+//
+// Copyright (C) 2008 Gael Guennebaud <gael.guennebaud@inria.fr>
+//
+// This Source Code Form is subject to the terms of the Mozilla
+// Public License v. 2.0. If a copy of the MPL was not distributed
+// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#include "icosphere.h"
+
+#include <GL/gl.h>
+#include <map>
+
+using namespace Eigen;
+
+//--------------------------------------------------------------------------------
+// icosahedron data
+//--------------------------------------------------------------------------------
+#define X .525731112119133606
+#define Z .850650808352039932
+
+static GLfloat vdata[12][3] = {
+ {-X, 0.0, Z}, {X, 0.0, Z}, {-X, 0.0, -Z}, {X, 0.0, -Z},
+ {0.0, Z, X}, {0.0, Z, -X}, {0.0, -Z, X}, {0.0, -Z, -X},
+ {Z, X, 0.0}, {-Z, X, 0.0}, {Z, -X, 0.0}, {-Z, -X, 0.0}
+};
+
+static GLint tindices[20][3] = {
+ {0,4,1}, {0,9,4}, {9,5,4}, {4,5,8}, {4,8,1},
+ {8,10,1}, {8,3,10}, {5,3,8}, {5,2,3}, {2,7,3},
+ {7,10,3}, {7,6,10}, {7,11,6}, {11,0,6}, {0,1,6},
+ {6,1,10}, {9,0,11}, {9,11,2}, {9,2,5}, {7,2,11} };
+//--------------------------------------------------------------------------------
+
+IcoSphere::IcoSphere(unsigned int levels)
+{
+ // init with an icosahedron
+ for (int i = 0; i < 12; i++)
+ mVertices.push_back(Map<Vector3f>(vdata[i]));
+ mIndices.push_back(new std::vector<int>);
+ std::vector<int>& indices = *mIndices.back();
+ for (int i = 0; i < 20; i++)
+ {
+ for (int k = 0; k < 3; k++)
+ indices.push_back(tindices[i][k]);
+ }
+ mListIds.push_back(0);
+
+ while(mIndices.size()<levels)
+ _subdivide();
+}
+
+const std::vector<int>& IcoSphere::indices(int level) const
+{
+ while (level>=int(mIndices.size()))
+ const_cast<IcoSphere*>(this)->_subdivide();
+ return *mIndices[level];
+}
+
+void IcoSphere::_subdivide(void)
+{
+ typedef unsigned long long Key;
+ std::map<Key,int> edgeMap;
+ const std::vector<int>& indices = *mIndices.back();
+ mIndices.push_back(new std::vector<int>);
+ std::vector<int>& refinedIndices = *mIndices.back();
+ int end = indices.size();
+ for (int i=0; i<end; i+=3)
+ {
+ int ids0[3], // indices of outer vertices
+ ids1[3]; // indices of edge vertices
+ for (int k=0; k<3; ++k)
+ {
+ int k1 = (k+1)%3;
+ int e0 = indices[i+k];
+ int e1 = indices[i+k1];
+ ids0[k] = e0;
+ if (e1>e0)
+ std::swap(e0,e1);
+ Key edgeKey = Key(e0) | (Key(e1)<<32);
+ std::map<Key,int>::iterator it = edgeMap.find(edgeKey);
+ if (it==edgeMap.end())
+ {
+ ids1[k] = mVertices.size();
+ edgeMap[edgeKey] = ids1[k];
+ mVertices.push_back( (mVertices[e0]+mVertices[e1]).normalized() );
+ }
+ else
+ ids1[k] = it->second;
+ }
+ refinedIndices.push_back(ids0[0]); refinedIndices.push_back(ids1[0]); refinedIndices.push_back(ids1[2]);
+ refinedIndices.push_back(ids0[1]); refinedIndices.push_back(ids1[1]); refinedIndices.push_back(ids1[0]);
+ refinedIndices.push_back(ids0[2]); refinedIndices.push_back(ids1[2]); refinedIndices.push_back(ids1[1]);
+ refinedIndices.push_back(ids1[0]); refinedIndices.push_back(ids1[1]); refinedIndices.push_back(ids1[2]);
+ }
+ mListIds.push_back(0);
+}
+
+void IcoSphere::draw(int level)
+{
+ while (level>=int(mIndices.size()))
+ const_cast<IcoSphere*>(this)->_subdivide();
+ if (mListIds[level]==0)
+ {
+ mListIds[level] = glGenLists(1);
+ glNewList(mListIds[level], GL_COMPILE);
+ glVertexPointer(3, GL_FLOAT, 0, mVertices[0].data());
+ glNormalPointer(GL_FLOAT, 0, mVertices[0].data());
+ glEnableClientState(GL_VERTEX_ARRAY);
+ glEnableClientState(GL_NORMAL_ARRAY);
+ glDrawElements(GL_TRIANGLES, mIndices[level]->size(), GL_UNSIGNED_INT, &(mIndices[level]->at(0)));
+ glDisableClientState(GL_VERTEX_ARRAY);
+ glDisableClientState(GL_NORMAL_ARRAY);
+ glEndList();
+ }
+ glCallList(mListIds[level]);
+}
+
+
diff --git a/demos/opengl/icosphere.h b/demos/opengl/icosphere.h
new file mode 100644
index 000000000..b0210edc5
--- /dev/null
+++ b/demos/opengl/icosphere.h
@@ -0,0 +1,30 @@
+// This file is part of Eigen, a lightweight C++ template library
+// for linear algebra.
+//
+// Copyright (C) 2008 Gael Guennebaud <gael.guennebaud@inria.fr>
+//
+// This Source Code Form is subject to the terms of the Mozilla
+// Public License v. 2.0. If a copy of the MPL was not distributed
+// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#ifndef EIGEN_ICOSPHERE_H
+#define EIGEN_ICOSPHERE_H
+
+#include <Eigen/Core>
+#include <vector>
+
+class IcoSphere
+{
+ public:
+ IcoSphere(unsigned int levels=1);
+ const std::vector<Eigen::Vector3f>& vertices() const { return mVertices; }
+ const std::vector<int>& indices(int level) const;
+ void draw(int level);
+ protected:
+ void _subdivide();
+ std::vector<Eigen::Vector3f> mVertices;
+ std::vector<std::vector<int>*> mIndices;
+ std::vector<int> mListIds;
+};
+
+#endif // EIGEN_ICOSPHERE_H
diff --git a/demos/opengl/quaternion_demo.cpp b/demos/opengl/quaternion_demo.cpp
new file mode 100644
index 000000000..04165619b
--- /dev/null
+++ b/demos/opengl/quaternion_demo.cpp
@@ -0,0 +1,656 @@
+// This file is part of Eigen, a lightweight C++ template library
+// for linear algebra.
+//
+// Copyright (C) 2008 Gael Guennebaud <gael.guennebaud@inria.fr>
+//
+// This Source Code Form is subject to the terms of the Mozilla
+// Public License v. 2.0. If a copy of the MPL was not distributed
+// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#include "quaternion_demo.h"
+#include "icosphere.h"
+
+#include <Eigen/Geometry>
+#include <Eigen/QR>
+#include <Eigen/LU>
+
+#include <iostream>
+#include <QEvent>
+#include <QMouseEvent>
+#include <QInputDialog>
+#include <QGridLayout>
+#include <QButtonGroup>
+#include <QRadioButton>
+#include <QDockWidget>
+#include <QPushButton>
+#include <QGroupBox>
+
+using namespace Eigen;
+
+class FancySpheres
+{
+ public:
+ EIGEN_MAKE_ALIGNED_OPERATOR_NEW
+
+ FancySpheres()
+ {
+ const int levels = 4;
+ const float scale = 0.33;
+ float radius = 100;
+ std::vector<int> parents;
+
+ // leval 0
+ mCenters.push_back(Vector3f::Zero());
+ parents.push_back(-1);
+ mRadii.push_back(radius);
+
+ // generate level 1 using icosphere vertices
+ radius *= 0.45;
+ {
+ float dist = mRadii[0]*0.9;
+ for (int i=0; i<12; ++i)
+ {
+ mCenters.push_back(mIcoSphere.vertices()[i] * dist);
+ mRadii.push_back(radius);
+ parents.push_back(0);
+ }
+ }
+
+ static const float angles [10] = {
+ 0, 0,
+ M_PI, 0.*M_PI,
+ M_PI, 0.5*M_PI,
+ M_PI, 1.*M_PI,
+ M_PI, 1.5*M_PI
+ };
+
+ // generate other levels
+ int start = 1;
+ for (int l=1; l<levels; l++)
+ {
+ radius *= scale;
+ int end = mCenters.size();
+ for (int i=start; i<end; ++i)
+ {
+ Vector3f c = mCenters[i];
+ Vector3f ax0 = (c - mCenters[parents[i]]).normalized();
+ Vector3f ax1 = ax0.unitOrthogonal();
+ Quaternionf q;
+ q.setFromTwoVectors(Vector3f::UnitZ(), ax0);
+ Affine3f t = Translation3f(c) * q * Scaling(mRadii[i]+radius);
+ for (int j=0; j<5; ++j)
+ {
+ Vector3f newC = c + ( (AngleAxisf(angles[j*2+1], ax0)
+ * AngleAxisf(angles[j*2+0] * (l==1 ? 0.35 : 0.5), ax1)) * ax0)
+ * (mRadii[i] + radius*0.8);
+ mCenters.push_back(newC);
+ mRadii.push_back(radius);
+ parents.push_back(i);
+ }
+ }
+ start = end;
+ }
+ }
+
+ void draw()
+ {
+ int end = mCenters.size();
+ glEnable(GL_NORMALIZE);
+ for (int i=0; i<end; ++i)
+ {
+ Affine3f t = Translation3f(mCenters[i]) * Scaling(mRadii[i]);
+ gpu.pushMatrix(GL_MODELVIEW);
+ gpu.multMatrix(t.matrix(),GL_MODELVIEW);
+ mIcoSphere.draw(2);
+ gpu.popMatrix(GL_MODELVIEW);
+ }
+ glDisable(GL_NORMALIZE);
+ }
+ protected:
+ std::vector<Vector3f> mCenters;
+ std::vector<float> mRadii;
+ IcoSphere mIcoSphere;
+};
+
+
+// generic linear interpolation method
+template<typename T> T lerp(float t, const T& a, const T& b)
+{
+ return a*(1-t) + b*t;
+}
+
+// quaternion slerp
+template<> Quaternionf lerp(float t, const Quaternionf& a, const Quaternionf& b)
+{ return a.slerp(t,b); }
+
+// linear interpolation of a frame using the type OrientationType
+// to perform the interpolation of the orientations
+template<typename OrientationType>
+inline static Frame lerpFrame(float alpha, const Frame& a, const Frame& b)
+{
+ return Frame(lerp(alpha,a.position,b.position),
+ Quaternionf(lerp(alpha,OrientationType(a.orientation),OrientationType(b.orientation))));
+}
+
+template<typename _Scalar> class EulerAngles
+{
+public:
+ enum { Dim = 3 };
+ typedef _Scalar Scalar;
+ typedef Matrix<Scalar,3,3> Matrix3;
+ typedef Matrix<Scalar,3,1> Vector3;
+ typedef Quaternion<Scalar> QuaternionType;
+
+protected:
+
+ Vector3 m_angles;
+
+public:
+
+ EulerAngles() {}
+ inline EulerAngles(Scalar a0, Scalar a1, Scalar a2) : m_angles(a0, a1, a2) {}
+ inline EulerAngles(const QuaternionType& q) { *this = q; }
+
+ const Vector3& coeffs() const { return m_angles; }
+ Vector3& coeffs() { return m_angles; }
+
+ EulerAngles& operator=(const QuaternionType& q)
+ {
+ Matrix3 m = q.toRotationMatrix();
+ return *this = m;
+ }
+
+ EulerAngles& operator=(const Matrix3& m)
+ {
+ // mat = cy*cz -cy*sz sy
+ // cz*sx*sy+cx*sz cx*cz-sx*sy*sz -cy*sx
+ // -cx*cz*sy+sx*sz cz*sx+cx*sy*sz cx*cy
+ m_angles.coeffRef(1) = std::asin(m.coeff(0,2));
+ m_angles.coeffRef(0) = std::atan2(-m.coeff(1,2),m.coeff(2,2));
+ m_angles.coeffRef(2) = std::atan2(-m.coeff(0,1),m.coeff(0,0));
+ return *this;
+ }
+
+ Matrix3 toRotationMatrix(void) const
+ {
+ Vector3 c = m_angles.array().cos();
+ Vector3 s = m_angles.array().sin();
+ Matrix3 res;
+ res << c.y()*c.z(), -c.y()*s.z(), s.y(),
+ c.z()*s.x()*s.y()+c.x()*s.z(), c.x()*c.z()-s.x()*s.y()*s.z(), -c.y()*s.x(),
+ -c.x()*c.z()*s.y()+s.x()*s.z(), c.z()*s.x()+c.x()*s.y()*s.z(), c.x()*c.y();
+ return res;
+ }
+
+ operator QuaternionType() { return QuaternionType(toRotationMatrix()); }
+};
+
+// Euler angles slerp
+template<> EulerAngles<float> lerp(float t, const EulerAngles<float>& a, const EulerAngles<float>& b)
+{
+ EulerAngles<float> res;
+ res.coeffs() = lerp(t, a.coeffs(), b.coeffs());
+ return res;
+}
+
+
+RenderingWidget::RenderingWidget()
+{
+ mAnimate = false;
+ mCurrentTrackingMode = TM_NO_TRACK;
+ mNavMode = NavTurnAround;
+ mLerpMode = LerpQuaternion;
+ mRotationMode = RotationStable;
+ mTrackball.setCamera(&mCamera);
+
+ // required to capture key press events
+ setFocusPolicy(Qt::ClickFocus);
+}
+
+void RenderingWidget::grabFrame(void)
+{
+ // ask user for a time
+ bool ok = false;
+ double t = 0;
+ if (!m_timeline.empty())
+ t = (--m_timeline.end())->first + 1.;
+ t = QInputDialog::getDouble(this, "Eigen's RenderingWidget", "time value: ",
+ t, 0, 1e3, 1, &ok);
+ if (ok)
+ {
+ Frame aux;
+ aux.orientation = mCamera.viewMatrix().linear();
+ aux.position = mCamera.viewMatrix().translation();
+ m_timeline[t] = aux;
+ }
+}
+
+void RenderingWidget::drawScene()
+{
+ static FancySpheres sFancySpheres;
+ float length = 50;
+ gpu.drawVector(Vector3f::Zero(), length*Vector3f::UnitX(), Color(1,0,0,1));
+ gpu.drawVector(Vector3f::Zero(), length*Vector3f::UnitY(), Color(0,1,0,1));
+ gpu.drawVector(Vector3f::Zero(), length*Vector3f::UnitZ(), Color(0,0,1,1));
+
+ // draw the fractal object
+ float sqrt3 = internal::sqrt(3.);
+ glLightfv(GL_LIGHT0, GL_AMBIENT, Vector4f(0.5,0.5,0.5,1).data());
+ glLightfv(GL_LIGHT0, GL_DIFFUSE, Vector4f(0.5,1,0.5,1).data());
+ glLightfv(GL_LIGHT0, GL_SPECULAR, Vector4f(1,1,1,1).data());
+ glLightfv(GL_LIGHT0, GL_POSITION, Vector4f(-sqrt3,-sqrt3,sqrt3,0).data());
+
+ glLightfv(GL_LIGHT1, GL_AMBIENT, Vector4f(0,0,0,1).data());
+ glLightfv(GL_LIGHT1, GL_DIFFUSE, Vector4f(1,0.5,0.5,1).data());
+ glLightfv(GL_LIGHT1, GL_SPECULAR, Vector4f(1,1,1,1).data());
+ glLightfv(GL_LIGHT1, GL_POSITION, Vector4f(-sqrt3,sqrt3,-sqrt3,0).data());
+
+ glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, Vector4f(0.7, 0.7, 0.7, 1).data());
+ glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, Vector4f(0.8, 0.75, 0.6, 1).data());
+ glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, Vector4f(1, 1, 1, 1).data());
+ glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, 64);
+
+ glEnable(GL_LIGHTING);
+ glEnable(GL_LIGHT0);
+ glEnable(GL_LIGHT1);
+
+ sFancySpheres.draw();
+ glVertexPointer(3, GL_FLOAT, 0, mVertices[0].data());
+ glNormalPointer(GL_FLOAT, 0, mNormals[0].data());
+ glEnableClientState(GL_VERTEX_ARRAY);
+ glEnableClientState(GL_NORMAL_ARRAY);
+ glDrawArrays(GL_TRIANGLES, 0, mVertices.size());
+ glDisableClientState(GL_VERTEX_ARRAY);
+ glDisableClientState(GL_NORMAL_ARRAY);
+
+ glDisable(GL_LIGHTING);
+}
+
+void RenderingWidget::animate()
+{
+ m_alpha += double(m_timer.interval()) * 1e-3;
+
+ TimeLine::const_iterator hi = m_timeline.upper_bound(m_alpha);
+ TimeLine::const_iterator lo = hi;
+ --lo;
+
+ Frame currentFrame;
+
+ if(hi==m_timeline.end())
+ {
+ // end
+ currentFrame = lo->second;
+ stopAnimation();
+ }
+ else if(hi==m_timeline.begin())
+ {
+ // start
+ currentFrame = hi->second;
+ }
+ else
+ {
+ float s = (m_alpha - lo->first)/(hi->first - lo->first);
+ if (mLerpMode==LerpEulerAngles)
+ currentFrame = ::lerpFrame<EulerAngles<float> >(s, lo->second, hi->second);
+ else if (mLerpMode==LerpQuaternion)
+ currentFrame = ::lerpFrame<Eigen::Quaternionf>(s, lo->second, hi->second);
+ else
+ {
+ std::cerr << "Invalid rotation interpolation mode (abort)\n";
+ exit(2);
+ }
+ currentFrame.orientation.coeffs().normalize();
+ }
+
+ currentFrame.orientation = currentFrame.orientation.inverse();
+ currentFrame.position = - (currentFrame.orientation * currentFrame.position);
+ mCamera.setFrame(currentFrame);
+
+ updateGL();
+}
+
+void RenderingWidget::keyPressEvent(QKeyEvent * e)
+{
+ switch(e->key())
+ {
+ case Qt::Key_Up:
+ mCamera.zoom(2);
+ break;
+ case Qt::Key_Down:
+ mCamera.zoom(-2);
+ break;
+ // add a frame
+ case Qt::Key_G:
+ grabFrame();
+ break;
+ // clear the time line
+ case Qt::Key_C:
+ m_timeline.clear();
+ break;
+ // move the camera to initial pos
+ case Qt::Key_R:
+ resetCamera();
+ break;
+ // start/stop the animation
+ case Qt::Key_A:
+ if (mAnimate)
+ {
+ stopAnimation();
+ }
+ else
+ {
+ m_alpha = 0;
+ connect(&m_timer, SIGNAL(timeout()), this, SLOT(animate()));
+ m_timer.start(1000/30);
+ mAnimate = true;
+ }
+ break;
+ default:
+ break;
+ }
+
+ updateGL();
+}
+
+void RenderingWidget::stopAnimation()
+{
+ disconnect(&m_timer, SIGNAL(timeout()), this, SLOT(animate()));
+ m_timer.stop();
+ mAnimate = false;
+ m_alpha = 0;
+}
+
+void RenderingWidget::mousePressEvent(QMouseEvent* e)
+{
+ mMouseCoords = Vector2i(e->pos().x(), e->pos().y());
+ bool fly = (mNavMode==NavFly) || (e->modifiers()&Qt::ControlModifier);
+ switch(e->button())
+ {
+ case Qt::LeftButton:
+ if(fly)
+ {
+ mCurrentTrackingMode = TM_LOCAL_ROTATE;
+ mTrackball.start(Trackball::Local);
+ }
+ else
+ {
+ mCurrentTrackingMode = TM_ROTATE_AROUND;
+ mTrackball.start(Trackball::Around);
+ }
+ mTrackball.track(mMouseCoords);
+ break;
+ case Qt::MidButton:
+ if(fly)
+ mCurrentTrackingMode = TM_FLY_Z;
+ else
+ mCurrentTrackingMode = TM_ZOOM;
+ break;
+ case Qt::RightButton:
+ mCurrentTrackingMode = TM_FLY_PAN;
+ break;
+ default:
+ break;
+ }
+}
+void RenderingWidget::mouseReleaseEvent(QMouseEvent*)
+{
+ mCurrentTrackingMode = TM_NO_TRACK;
+ updateGL();
+}
+
+void RenderingWidget::mouseMoveEvent(QMouseEvent* e)
+{
+ // tracking
+ if(mCurrentTrackingMode != TM_NO_TRACK)
+ {
+ float dx = float(e->x() - mMouseCoords.x()) / float(mCamera.vpWidth());
+ float dy = - float(e->y() - mMouseCoords.y()) / float(mCamera.vpHeight());
+
+ // speedup the transformations
+ if(e->modifiers() & Qt::ShiftModifier)
+ {
+ dx *= 10.;
+ dy *= 10.;
+ }
+
+ switch(mCurrentTrackingMode)
+ {
+ case TM_ROTATE_AROUND:
+ case TM_LOCAL_ROTATE:
+ if (mRotationMode==RotationStable)
+ {
+ // use the stable trackball implementation mapping
+ // the 2D coordinates to 3D points on a sphere.
+ mTrackball.track(Vector2i(e->pos().x(), e->pos().y()));
+ }
+ else
+ {
+ // standard approach mapping the x and y displacements as rotations
+ // around the camera's X and Y axes.
+ Quaternionf q = AngleAxisf( dx*M_PI, Vector3f::UnitY())
+ * AngleAxisf(-dy*M_PI, Vector3f::UnitX());
+ if (mCurrentTrackingMode==TM_LOCAL_ROTATE)
+ mCamera.localRotate(q);
+ else
+ mCamera.rotateAroundTarget(q);
+ }
+ break;
+ case TM_ZOOM :
+ mCamera.zoom(dy*100);
+ break;
+ case TM_FLY_Z :
+ mCamera.localTranslate(Vector3f(0, 0, -dy*200));
+ break;
+ case TM_FLY_PAN :
+ mCamera.localTranslate(Vector3f(dx*200, dy*200, 0));
+ break;
+ default:
+ break;
+ }
+
+ updateGL();
+ }
+
+ mMouseCoords = Vector2i(e->pos().x(), e->pos().y());
+}
+
+void RenderingWidget::paintGL()
+{
+ glEnable(GL_DEPTH_TEST);
+ glDisable(GL_CULL_FACE);
+ glPolygonMode(GL_FRONT_AND_BACK,GL_FILL);
+ glDisable(GL_COLOR_MATERIAL);
+ glDisable(GL_BLEND);
+ glDisable(GL_ALPHA_TEST);
+ glDisable(GL_TEXTURE_1D);
+ glDisable(GL_TEXTURE_2D);
+ glDisable(GL_TEXTURE_3D);
+
+ // Clear buffers
+ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+
+ mCamera.activateGL();
+
+ drawScene();
+}
+
+void RenderingWidget::initializeGL()
+{
+ glClearColor(1., 1., 1., 0.);
+ glLightModeli(GL_LIGHT_MODEL_LOCAL_VIEWER, 1);
+ glDepthMask(GL_TRUE);
+ glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
+
+ mCamera.setPosition(Vector3f(-200, -200, -200));
+ mCamera.setTarget(Vector3f(0, 0, 0));
+ mInitFrame.orientation = mCamera.orientation().inverse();
+ mInitFrame.position = mCamera.viewMatrix().translation();
+}
+
+void RenderingWidget::resizeGL(int width, int height)
+{
+ mCamera.setViewport(width,height);
+}
+
+void RenderingWidget::setNavMode(int m)
+{
+ mNavMode = NavMode(m);
+}
+
+void RenderingWidget::setLerpMode(int m)
+{
+ mLerpMode = LerpMode(m);
+}
+
+void RenderingWidget::setRotationMode(int m)
+{
+ mRotationMode = RotationMode(m);
+}
+
+void RenderingWidget::resetCamera()
+{
+ if (mAnimate)
+ stopAnimation();
+ m_timeline.clear();
+ Frame aux0 = mCamera.frame();
+ aux0.orientation = aux0.orientation.inverse();
+ aux0.position = mCamera.viewMatrix().translation();
+ m_timeline[0] = aux0;
+
+ Vector3f currentTarget = mCamera.target();
+ mCamera.setTarget(Vector3f::Zero());
+
+ // compute the rotation duration to move the camera to the target
+ Frame aux1 = mCamera.frame();
+ aux1.orientation = aux1.orientation.inverse();
+ aux1.position = mCamera.viewMatrix().translation();
+ float duration = aux0.orientation.angularDistance(aux1.orientation) * 0.9;
+ if (duration<0.1) duration = 0.1;
+
+ // put the camera at that time step:
+ aux1 = aux0.lerp(duration/2,mInitFrame);
+ // and make it look at the target again
+ aux1.orientation = aux1.orientation.inverse();
+ aux1.position = - (aux1.orientation * aux1.position);
+ mCamera.setFrame(aux1);
+ mCamera.setTarget(Vector3f::Zero());
+
+ // add this camera keyframe
+ aux1.orientation = aux1.orientation.inverse();
+ aux1.position = mCamera.viewMatrix().translation();
+ m_timeline[duration] = aux1;
+
+ m_timeline[2] = mInitFrame;
+ m_alpha = 0;
+ animate();
+ connect(&m_timer, SIGNAL(timeout()), this, SLOT(animate()));
+ m_timer.start(1000/30);
+ mAnimate = true;
+}
+
+QWidget* RenderingWidget::createNavigationControlWidget()
+{
+ QWidget* panel = new QWidget();
+ QVBoxLayout* layout = new QVBoxLayout();
+
+ {
+ QPushButton* but = new QPushButton("reset");
+ but->setToolTip("move the camera to initial position (with animation)");
+ layout->addWidget(but);
+ connect(but, SIGNAL(clicked()), this, SLOT(resetCamera()));
+ }
+ {
+ // navigation mode
+ QGroupBox* box = new QGroupBox("navigation mode");
+ QVBoxLayout* boxLayout = new QVBoxLayout;
+ QButtonGroup* group = new QButtonGroup(panel);
+ QRadioButton* but;
+ but = new QRadioButton("turn around");
+ but->setToolTip("look around an object");
+ group->addButton(but, NavTurnAround);
+ boxLayout->addWidget(but);
+ but = new QRadioButton("fly");
+ but->setToolTip("free navigation like a spaceship\n(this mode can also be enabled pressing the \"shift\" key)");
+ group->addButton(but, NavFly);
+ boxLayout->addWidget(but);
+ group->button(mNavMode)->setChecked(true);
+ connect(group, SIGNAL(buttonClicked(int)), this, SLOT(setNavMode(int)));
+ box->setLayout(boxLayout);
+ layout->addWidget(box);
+ }
+ {
+ // track ball, rotation mode
+ QGroupBox* box = new QGroupBox("rotation mode");
+ QVBoxLayout* boxLayout = new QVBoxLayout;
+ QButtonGroup* group = new QButtonGroup(panel);
+ QRadioButton* but;
+ but = new QRadioButton("stable trackball");
+ group->addButton(but, RotationStable);
+ boxLayout->addWidget(but);
+ but->setToolTip("use the stable trackball implementation mapping\nthe 2D coordinates to 3D points on a sphere");
+ but = new QRadioButton("standard rotation");
+ group->addButton(but, RotationStandard);
+ boxLayout->addWidget(but);
+ but->setToolTip("standard approach mapping the x and y displacements\nas rotations around the camera's X and Y axes");
+ group->button(mRotationMode)->setChecked(true);
+ connect(group, SIGNAL(buttonClicked(int)), this, SLOT(setRotationMode(int)));
+ box->setLayout(boxLayout);
+ layout->addWidget(box);
+ }
+ {
+ // interpolation mode
+ QGroupBox* box = new QGroupBox("spherical interpolation");
+ QVBoxLayout* boxLayout = new QVBoxLayout;
+ QButtonGroup* group = new QButtonGroup(panel);
+ QRadioButton* but;
+ but = new QRadioButton("quaternion slerp");
+ group->addButton(but, LerpQuaternion);
+ boxLayout->addWidget(but);
+ but->setToolTip("use quaternion spherical interpolation\nto interpolate orientations");
+ but = new QRadioButton("euler angles");
+ group->addButton(but, LerpEulerAngles);
+ boxLayout->addWidget(but);
+ but->setToolTip("use Euler angles to interpolate orientations");
+ group->button(mNavMode)->setChecked(true);
+ connect(group, SIGNAL(buttonClicked(int)), this, SLOT(setLerpMode(int)));
+ box->setLayout(boxLayout);
+ layout->addWidget(box);
+ }
+ layout->addItem(new QSpacerItem(0,0,QSizePolicy::Minimum,QSizePolicy::Expanding));
+ panel->setLayout(layout);
+ return panel;
+}
+
+QuaternionDemo::QuaternionDemo()
+{
+ mRenderingWidget = new RenderingWidget();
+ setCentralWidget(mRenderingWidget);
+
+ QDockWidget* panel = new QDockWidget("navigation", this);
+ panel->setAllowedAreas((QFlags<Qt::DockWidgetArea>)(Qt::RightDockWidgetArea | Qt::LeftDockWidgetArea));
+ addDockWidget(Qt::RightDockWidgetArea, panel);
+ panel->setWidget(mRenderingWidget->createNavigationControlWidget());
+}
+
+int main(int argc, char *argv[])
+{
+ std::cout << "Navigation:\n";
+ std::cout << " left button: rotate around the target\n";
+ std::cout << " middle button: zoom\n";
+ std::cout << " left button + ctrl quake rotate (rotate around camera position)\n";
+ std::cout << " middle button + ctrl walk (progress along camera's z direction)\n";
+ std::cout << " left button: pan (translate in the XY camera's plane)\n\n";
+ std::cout << "R : move the camera to initial position\n";
+ std::cout << "A : start/stop animation\n";
+ std::cout << "C : clear the animation\n";
+ std::cout << "G : add a key frame\n";
+
+ QApplication app(argc, argv);
+ QuaternionDemo demo;
+ demo.resize(600,500);
+ demo.show();
+ return app.exec();
+}
+
+#include "quaternion_demo.moc"
+
diff --git a/demos/opengl/quaternion_demo.h b/demos/opengl/quaternion_demo.h
new file mode 100644
index 000000000..dbff46c39
--- /dev/null
+++ b/demos/opengl/quaternion_demo.h
@@ -0,0 +1,114 @@
+// This file is part of Eigen, a lightweight C++ template library
+// for linear algebra.
+//
+// Copyright (C) 2008 Gael Guennebaud <gael.guennebaud@inria.fr>
+//
+// This Source Code Form is subject to the terms of the Mozilla
+// Public License v. 2.0. If a copy of the MPL was not distributed
+// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#ifndef EIGEN_QUATERNION_DEMO_H
+#define EIGEN_QUATERNION_DEMO_H
+
+#include "gpuhelper.h"
+#include "camera.h"
+#include "trackball.h"
+#include <map>
+#include <QTimer>
+#include <QtGui/QApplication>
+#include <QtOpenGL/QGLWidget>
+#include <QtGui/QMainWindow>
+
+class RenderingWidget : public QGLWidget
+{
+ Q_OBJECT
+
+ typedef std::map<float,Frame> TimeLine;
+ TimeLine m_timeline;
+ Frame lerpFrame(float t);
+
+ Frame mInitFrame;
+ bool mAnimate;
+ float m_alpha;
+
+ enum TrackMode {
+ TM_NO_TRACK=0, TM_ROTATE_AROUND, TM_ZOOM,
+ TM_LOCAL_ROTATE, TM_FLY_Z, TM_FLY_PAN
+ };
+
+ enum NavMode {
+ NavTurnAround,
+ NavFly
+ };
+
+ enum LerpMode {
+ LerpQuaternion,
+ LerpEulerAngles
+ };
+
+ enum RotationMode {
+ RotationStable,
+ RotationStandard
+ };
+
+ Camera mCamera;
+ TrackMode mCurrentTrackingMode;
+ NavMode mNavMode;
+ LerpMode mLerpMode;
+ RotationMode mRotationMode;
+ Vector2i mMouseCoords;
+ Trackball mTrackball;
+
+ QTimer m_timer;
+
+ void setupCamera();
+
+ std::vector<Vector3f> mVertices;
+ std::vector<Vector3f> mNormals;
+ std::vector<int> mIndices;
+
+ protected slots:
+
+ virtual void animate(void);
+ virtual void drawScene(void);
+
+ virtual void grabFrame(void);
+ virtual void stopAnimation();
+
+ virtual void setNavMode(int);
+ virtual void setLerpMode(int);
+ virtual void setRotationMode(int);
+ virtual void resetCamera();
+
+ protected:
+
+ virtual void initializeGL();
+ virtual void resizeGL(int width, int height);
+ virtual void paintGL();
+
+ //--------------------------------------------------------------------------------
+ virtual void mousePressEvent(QMouseEvent * e);
+ virtual void mouseReleaseEvent(QMouseEvent * e);
+ virtual void mouseMoveEvent(QMouseEvent * e);
+ virtual void keyPressEvent(QKeyEvent * e);
+ //--------------------------------------------------------------------------------
+
+ public:
+ EIGEN_MAKE_ALIGNED_OPERATOR_NEW
+
+ RenderingWidget();
+ ~RenderingWidget() { }
+
+ QWidget* createNavigationControlWidget();
+};
+
+class QuaternionDemo : public QMainWindow
+{
+ Q_OBJECT
+ public:
+ QuaternionDemo();
+ protected:
+ RenderingWidget* mRenderingWidget;
+};
+
+#endif // EIGEN_QUATERNION_DEMO_H
diff --git a/demos/opengl/trackball.cpp b/demos/opengl/trackball.cpp
new file mode 100644
index 000000000..77ac790c8
--- /dev/null
+++ b/demos/opengl/trackball.cpp
@@ -0,0 +1,59 @@
+// This file is part of Eigen, a lightweight C++ template library
+// for linear algebra.
+//
+// Copyright (C) 2008 Gael Guennebaud <gael.guennebaud@inria.fr>
+//
+// This Source Code Form is subject to the terms of the Mozilla
+// Public License v. 2.0. If a copy of the MPL was not distributed
+// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#include "trackball.h"
+#include "camera.h"
+
+using namespace Eigen;
+
+void Trackball::track(const Vector2i& point2D)
+{
+ if (mpCamera==0)
+ return;
+ Vector3f newPoint3D;
+ bool newPointOk = mapToSphere(point2D, newPoint3D);
+
+ if (mLastPointOk && newPointOk)
+ {
+ Vector3f axis = mLastPoint3D.cross(newPoint3D).normalized();
+ float cos_angle = mLastPoint3D.dot(newPoint3D);
+ if ( internal::abs(cos_angle) < 1.0 )
+ {
+ float angle = 2. * acos(cos_angle);
+ if (mMode==Around)
+ mpCamera->rotateAroundTarget(Quaternionf(AngleAxisf(angle, axis)));
+ else
+ mpCamera->localRotate(Quaternionf(AngleAxisf(-angle, axis)));
+ }
+ }
+
+ mLastPoint3D = newPoint3D;
+ mLastPointOk = newPointOk;
+}
+
+bool Trackball::mapToSphere(const Vector2i& p2, Vector3f& v3)
+{
+ if ((p2.x() >= 0) && (p2.x() <= int(mpCamera->vpWidth())) &&
+ (p2.y() >= 0) && (p2.y() <= int(mpCamera->vpHeight())) )
+ {
+ double x = (double)(p2.x() - 0.5*mpCamera->vpWidth()) / (double)mpCamera->vpWidth();
+ double y = (double)(0.5*mpCamera->vpHeight() - p2.y()) / (double)mpCamera->vpHeight();
+ double sinx = sin(M_PI * x * 0.5);
+ double siny = sin(M_PI * y * 0.5);
+ double sinx2siny2 = sinx * sinx + siny * siny;
+
+ v3.x() = sinx;
+ v3.y() = siny;
+ v3.z() = sinx2siny2 < 1.0 ? sqrt(1.0 - sinx2siny2) : 0.0;
+
+ return true;
+ }
+ else
+ return false;
+}
diff --git a/demos/opengl/trackball.h b/demos/opengl/trackball.h
new file mode 100644
index 000000000..1ea842f11
--- /dev/null
+++ b/demos/opengl/trackball.h
@@ -0,0 +1,42 @@
+// This file is part of Eigen, a lightweight C++ template library
+// for linear algebra.
+//
+// Copyright (C) 2008 Gael Guennebaud <gael.guennebaud@inria.fr>
+//
+// This Source Code Form is subject to the terms of the Mozilla
+// Public License v. 2.0. If a copy of the MPL was not distributed
+// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#ifndef EIGEN_TRACKBALL_H
+#define EIGEN_TRACKBALL_H
+
+#include <Eigen/Geometry>
+
+class Camera;
+
+class Trackball
+{
+ public:
+
+ enum Mode {Around, Local};
+
+ Trackball() : mpCamera(0) {}
+
+ void start(Mode m = Around) { mMode = m; mLastPointOk = false; }
+
+ void setCamera(Camera* pCam) { mpCamera = pCam; }
+
+ void track(const Eigen::Vector2i& newPoint2D);
+
+ protected:
+
+ bool mapToSphere( const Eigen::Vector2i& p2, Eigen::Vector3f& v3);
+
+ Camera* mpCamera;
+ Eigen::Vector3f mLastPoint3D;
+ Mode mMode;
+ bool mLastPointOk;
+
+};
+
+#endif // EIGEN_TRACKBALL_H