mirror of
https://github.com/ConjureETS/LOG750-LAB2.git
synced 2026-03-23 19:11:20 +00:00
Lab 1 End Version
This commit is contained in:
parent
fda113767d
commit
0977688583
43
.gitignore
vendored
Normal file
43
.gitignore
vendored
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
# Generated Bin and debug
|
||||||
|
debug/
|
||||||
|
object_script.*.Debug
|
||||||
|
object_script.*.Release
|
||||||
|
|
||||||
|
# C++ objects and libs
|
||||||
|
|
||||||
|
*.slo
|
||||||
|
*.lo
|
||||||
|
*.o
|
||||||
|
*.a
|
||||||
|
*.la
|
||||||
|
*.lai
|
||||||
|
*.so
|
||||||
|
*.dll
|
||||||
|
*.dylib
|
||||||
|
|
||||||
|
# Qt-es
|
||||||
|
|
||||||
|
/.qmake.cache
|
||||||
|
/.qmake.stash
|
||||||
|
*.pro.user
|
||||||
|
*.pro.user.*
|
||||||
|
*.qbs.user
|
||||||
|
*.qbs.user.*
|
||||||
|
*.moc
|
||||||
|
moc_*.cpp
|
||||||
|
qrc_*.cpp
|
||||||
|
ui_*.h
|
||||||
|
Makefile*
|
||||||
|
*build-*
|
||||||
|
|
||||||
|
# QtCreator
|
||||||
|
|
||||||
|
*.autosave
|
||||||
|
|
||||||
|
# QtCtreator Qml
|
||||||
|
*.qmlproject.user
|
||||||
|
*.qmlproject.user.*
|
||||||
|
|
||||||
|
# QtCtreator CMake
|
||||||
|
CMakeLists.txt.user*
|
||||||
|
|
||||||
297
QGLViewer/ImageInterface.ui
Normal file
297
QGLViewer/ImageInterface.ui
Normal file
@ -0,0 +1,297 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ui version="4.0">
|
||||||
|
<class>ImageInterface</class>
|
||||||
|
<widget class="QDialog" name="ImageInterface">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>298</width>
|
||||||
|
<height>204</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="windowTitle">
|
||||||
|
<string>Image settings</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QVBoxLayout">
|
||||||
|
<property name="spacing">
|
||||||
|
<number>6</number>
|
||||||
|
</property>
|
||||||
|
<property name="margin">
|
||||||
|
<number>9</number>
|
||||||
|
</property>
|
||||||
|
<item>
|
||||||
|
<layout class="QHBoxLayout">
|
||||||
|
<property name="spacing">
|
||||||
|
<number>6</number>
|
||||||
|
</property>
|
||||||
|
<property name="margin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="label_2">
|
||||||
|
<property name="text">
|
||||||
|
<string>Width</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QSpinBox" name="imgWidth">
|
||||||
|
<property name="toolTip">
|
||||||
|
<string>Width of the image (in pixels)</string>
|
||||||
|
</property>
|
||||||
|
<property name="suffix">
|
||||||
|
<string> px</string>
|
||||||
|
</property>
|
||||||
|
<property name="minimum">
|
||||||
|
<number>1</number>
|
||||||
|
</property>
|
||||||
|
<property name="maximum">
|
||||||
|
<number>32000</number>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<spacer>
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>20</width>
|
||||||
|
<height>22</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="label_3">
|
||||||
|
<property name="text">
|
||||||
|
<string>Height</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QSpinBox" name="imgHeight">
|
||||||
|
<property name="toolTip">
|
||||||
|
<string>Height of the image (in pixels)</string>
|
||||||
|
</property>
|
||||||
|
<property name="suffix">
|
||||||
|
<string> px</string>
|
||||||
|
</property>
|
||||||
|
<property name="minimum">
|
||||||
|
<number>1</number>
|
||||||
|
</property>
|
||||||
|
<property name="maximum">
|
||||||
|
<number>32000</number>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<layout class="QHBoxLayout">
|
||||||
|
<property name="spacing">
|
||||||
|
<number>6</number>
|
||||||
|
</property>
|
||||||
|
<property name="margin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="label_4">
|
||||||
|
<property name="text">
|
||||||
|
<string>Image quality</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QSpinBox" name="imgQuality">
|
||||||
|
<property name="toolTip">
|
||||||
|
<string>Between 0 (smallest files) and 100 (highest quality)</string>
|
||||||
|
</property>
|
||||||
|
<property name="minimum">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="maximum">
|
||||||
|
<number>100</number>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<spacer>
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>40</width>
|
||||||
|
<height>20</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<layout class="QHBoxLayout">
|
||||||
|
<property name="spacing">
|
||||||
|
<number>6</number>
|
||||||
|
</property>
|
||||||
|
<property name="margin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="label">
|
||||||
|
<property name="text">
|
||||||
|
<string>Oversampling</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QDoubleSpinBox" name="oversampling">
|
||||||
|
<property name="toolTip">
|
||||||
|
<string>Antialiases image (when larger then 1.0)</string>
|
||||||
|
</property>
|
||||||
|
<property name="prefix">
|
||||||
|
<string>x </string>
|
||||||
|
</property>
|
||||||
|
<property name="decimals">
|
||||||
|
<number>1</number>
|
||||||
|
</property>
|
||||||
|
<property name="minimum">
|
||||||
|
<double>0.100000000000000</double>
|
||||||
|
</property>
|
||||||
|
<property name="maximum">
|
||||||
|
<double>64.000000000000000</double>
|
||||||
|
</property>
|
||||||
|
<property name="singleStep">
|
||||||
|
<double>1.000000000000000</double>
|
||||||
|
</property>
|
||||||
|
<property name="value">
|
||||||
|
<double>1.000000000000000</double>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<spacer>
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>40</width>
|
||||||
|
<height>20</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QCheckBox" name="whiteBackground">
|
||||||
|
<property name="toolTip">
|
||||||
|
<string>Use white as background color</string>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Use white background</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QCheckBox" name="expandFrustum">
|
||||||
|
<property name="toolTip">
|
||||||
|
<string>When image aspect ratio differs from viewer's one, expand frustum as needed. Fits inside current frustum otherwise.</string>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Expand frustum if needed</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<spacer>
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Vertical</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>20</width>
|
||||||
|
<height>16</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<layout class="QHBoxLayout">
|
||||||
|
<property name="spacing">
|
||||||
|
<number>6</number>
|
||||||
|
</property>
|
||||||
|
<property name="margin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<item>
|
||||||
|
<spacer>
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>131</width>
|
||||||
|
<height>31</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="okButton">
|
||||||
|
<property name="text">
|
||||||
|
<string>OK</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="cancelButton">
|
||||||
|
<property name="text">
|
||||||
|
<string>Cancel</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
<resources/>
|
||||||
|
<connections>
|
||||||
|
<connection>
|
||||||
|
<sender>okButton</sender>
|
||||||
|
<signal>clicked()</signal>
|
||||||
|
<receiver>ImageInterface</receiver>
|
||||||
|
<slot>accept()</slot>
|
||||||
|
<hints>
|
||||||
|
<hint type="sourcelabel">
|
||||||
|
<x>135</x>
|
||||||
|
<y>184</y>
|
||||||
|
</hint>
|
||||||
|
<hint type="destinationlabel">
|
||||||
|
<x>96</x>
|
||||||
|
<y>254</y>
|
||||||
|
</hint>
|
||||||
|
</hints>
|
||||||
|
</connection>
|
||||||
|
<connection>
|
||||||
|
<sender>cancelButton</sender>
|
||||||
|
<signal>clicked()</signal>
|
||||||
|
<receiver>ImageInterface</receiver>
|
||||||
|
<slot>reject()</slot>
|
||||||
|
<hints>
|
||||||
|
<hint type="sourcelabel">
|
||||||
|
<x>226</x>
|
||||||
|
<y>184</y>
|
||||||
|
</hint>
|
||||||
|
<hint type="destinationlabel">
|
||||||
|
<x>179</x>
|
||||||
|
<y>282</y>
|
||||||
|
</hint>
|
||||||
|
</hints>
|
||||||
|
</connection>
|
||||||
|
</connections>
|
||||||
|
</ui>
|
||||||
217
QGLViewer/VRenderInterface.ui
Normal file
217
QGLViewer/VRenderInterface.ui
Normal file
@ -0,0 +1,217 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ui version="4.0">
|
||||||
|
<class>VRenderInterface</class>
|
||||||
|
<widget class="QDialog" name="VRenderInterface">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>309</width>
|
||||||
|
<height>211</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="windowTitle">
|
||||||
|
<string>Vectorial rendering options</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QVBoxLayout">
|
||||||
|
<property name="leftMargin">
|
||||||
|
<number>5</number>
|
||||||
|
</property>
|
||||||
|
<property name="topMargin">
|
||||||
|
<number>5</number>
|
||||||
|
</property>
|
||||||
|
<property name="rightMargin">
|
||||||
|
<number>5</number>
|
||||||
|
</property>
|
||||||
|
<property name="bottomMargin">
|
||||||
|
<number>5</number>
|
||||||
|
</property>
|
||||||
|
<item>
|
||||||
|
<widget class="QCheckBox" name="includeHidden">
|
||||||
|
<property name="toolTip">
|
||||||
|
<string>Hidden polygons are also included in the output (usually twice bigger)</string>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Include hidden parts</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QCheckBox" name="cullBackFaces">
|
||||||
|
<property name="toolTip">
|
||||||
|
<string>Back faces (non clockwise point ordering) are removed from the output</string>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Cull back faces</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QCheckBox" name="blackAndWhite">
|
||||||
|
<property name="toolTip">
|
||||||
|
<string>Black and white rendering</string>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Black and white</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QCheckBox" name="colorBackground">
|
||||||
|
<property name="toolTip">
|
||||||
|
<string>Use current background color instead of white</string>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Color background</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QCheckBox" name="tightenBBox">
|
||||||
|
<property name="toolTip">
|
||||||
|
<string>Fit output bounding box to current display</string>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Tighten bounding box</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<layout class="QHBoxLayout">
|
||||||
|
<property name="leftMargin">
|
||||||
|
<number>11</number>
|
||||||
|
</property>
|
||||||
|
<property name="topMargin">
|
||||||
|
<number>11</number>
|
||||||
|
</property>
|
||||||
|
<property name="rightMargin">
|
||||||
|
<number>11</number>
|
||||||
|
</property>
|
||||||
|
<property name="bottomMargin">
|
||||||
|
<number>11</number>
|
||||||
|
</property>
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="sortLabel">
|
||||||
|
<property name="toolTip">
|
||||||
|
<string>Polygon depth sorting method</string>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Sort method:</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QComboBox" name="sortMethod">
|
||||||
|
<property name="toolTip">
|
||||||
|
<string>Polygon depth sorting method</string>
|
||||||
|
</property>
|
||||||
|
<property name="currentIndex">
|
||||||
|
<number>3</number>
|
||||||
|
</property>
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string>No sorting</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string>BSP</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string>Topological</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string>Advanced topological</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<spacer>
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Vertical</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeType">
|
||||||
|
<enum>QSizePolicy::Expanding</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>31</width>
|
||||||
|
<height>41</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<layout class="QHBoxLayout">
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="SaveButton">
|
||||||
|
<property name="text">
|
||||||
|
<string>Save</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="CancelButton">
|
||||||
|
<property name="text">
|
||||||
|
<string>Cancel</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
<layoutdefault spacing="6" margin="11"/>
|
||||||
|
<tabstops>
|
||||||
|
<tabstop>SaveButton</tabstop>
|
||||||
|
<tabstop>CancelButton</tabstop>
|
||||||
|
<tabstop>includeHidden</tabstop>
|
||||||
|
<tabstop>cullBackFaces</tabstop>
|
||||||
|
<tabstop>blackAndWhite</tabstop>
|
||||||
|
<tabstop>colorBackground</tabstop>
|
||||||
|
<tabstop>tightenBBox</tabstop>
|
||||||
|
<tabstop>sortMethod</tabstop>
|
||||||
|
</tabstops>
|
||||||
|
<resources/>
|
||||||
|
<connections>
|
||||||
|
<connection>
|
||||||
|
<sender>SaveButton</sender>
|
||||||
|
<signal>released()</signal>
|
||||||
|
<receiver>VRenderInterface</receiver>
|
||||||
|
<slot>accept()</slot>
|
||||||
|
<hints>
|
||||||
|
<hint type="sourcelabel">
|
||||||
|
<x>16</x>
|
||||||
|
<y>210</y>
|
||||||
|
</hint>
|
||||||
|
<hint type="destinationlabel">
|
||||||
|
<x>35</x>
|
||||||
|
<y>176</y>
|
||||||
|
</hint>
|
||||||
|
</hints>
|
||||||
|
</connection>
|
||||||
|
<connection>
|
||||||
|
<sender>CancelButton</sender>
|
||||||
|
<signal>released()</signal>
|
||||||
|
<receiver>VRenderInterface</receiver>
|
||||||
|
<slot>reject()</slot>
|
||||||
|
<hints>
|
||||||
|
<hint type="sourcelabel">
|
||||||
|
<x>225</x>
|
||||||
|
<y>198</y>
|
||||||
|
</hint>
|
||||||
|
<hint type="destinationlabel">
|
||||||
|
<x>211</x>
|
||||||
|
<y>180</y>
|
||||||
|
</hint>
|
||||||
|
</hints>
|
||||||
|
</connection>
|
||||||
|
</connections>
|
||||||
|
</ui>
|
||||||
2073
QGLViewer/camera.cpp
Normal file
2073
QGLViewer/camera.cpp
Normal file
File diff suppressed because it is too large
Load Diff
505
QGLViewer/camera.h
Normal file
505
QGLViewer/camera.h
Normal file
@ -0,0 +1,505 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
|
||||||
|
Copyright (C) 2002-2014 Gilles Debunne. All rights reserved.
|
||||||
|
|
||||||
|
This file is part of the QGLViewer library version 2.6.3.
|
||||||
|
|
||||||
|
http://www.libqglviewer.com - contact@libqglviewer.com
|
||||||
|
|
||||||
|
This file may be used under the terms of the GNU General Public License
|
||||||
|
versions 2.0 or 3.0 as published by the Free Software Foundation and
|
||||||
|
appearing in the LICENSE file included in the packaging of this file.
|
||||||
|
In addition, as a special exception, Gilles Debunne gives you certain
|
||||||
|
additional rights, described in the file GPL_EXCEPTION in this package.
|
||||||
|
|
||||||
|
libQGLViewer uses dual licensing. Commercial/proprietary software must
|
||||||
|
purchase a libQGLViewer Commercial License.
|
||||||
|
|
||||||
|
This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
|
||||||
|
WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
|
||||||
|
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
#ifndef QGLVIEWER_CAMERA_H
|
||||||
|
#define QGLVIEWER_CAMERA_H
|
||||||
|
|
||||||
|
#include <QMap>
|
||||||
|
#include "keyFrameInterpolator.h"
|
||||||
|
class QGLViewer;
|
||||||
|
|
||||||
|
namespace qglviewer {
|
||||||
|
|
||||||
|
class ManipulatedCameraFrame;
|
||||||
|
|
||||||
|
/*! \brief A perspective or orthographic camera.
|
||||||
|
\class Camera camera.h QGLViewer/camera.h
|
||||||
|
|
||||||
|
A Camera defines some intrinsic parameters (fieldOfView(), position(), viewDirection(),
|
||||||
|
upVector()...) and useful positioning tools that ease its placement (showEntireScene(),
|
||||||
|
fitSphere(), lookAt()...). It exports its associated OpenGL projection and modelview matrices and
|
||||||
|
can interactively be modified using the mouse.
|
||||||
|
|
||||||
|
<h3>Mouse manipulation</h3>
|
||||||
|
|
||||||
|
The position() and orientation() of the Camera are defined by a ManipulatedCameraFrame (retrieved
|
||||||
|
using frame()). These methods are just convenient wrappers to the equivalent Frame methods. This
|
||||||
|
also means that the Camera frame() can be attached to a Frame::referenceFrame() which enables
|
||||||
|
complex Camera setups.
|
||||||
|
|
||||||
|
Different displacements can be performed using the mouse. The list of possible actions is defined
|
||||||
|
by the QGLViewer::MouseAction enum. Use QGLViewer::setMouseBinding() to attach a specific action
|
||||||
|
to an arbitrary mouse button-state key binding. These actions are detailed in the <a
|
||||||
|
href="../mouse.html">mouse page</a>.
|
||||||
|
|
||||||
|
The default button binding are: QGLViewer::ROTATE (left), QGLViewer::ZOOM (middle) and
|
||||||
|
QGLViewer::TRANSLATE (right). With this configuration, the Camera \e observes a scene and rotates
|
||||||
|
around its pivotPoint(). You can switch between this mode and a fly mode using the
|
||||||
|
QGLViewer::CAMERA_MODE (see QGLViewer::toggleCameraMode()) keyboard shortcut (default is 'Space').
|
||||||
|
|
||||||
|
<h3>Other functionalities</h3>
|
||||||
|
|
||||||
|
The type() of the Camera can be Camera::ORTHOGRAPHIC or Camera::PERSPECTIVE (see Type()).
|
||||||
|
fieldOfView() is meaningless with Camera::ORTHOGRAPHIC.
|
||||||
|
|
||||||
|
The near and far planes of the Camera are fitted to the scene and determined from
|
||||||
|
QGLViewer::sceneRadius(), QGLViewer::sceneCenter() and zClippingCoefficient() by the zNear() and
|
||||||
|
zFar() methods. Reasonable values on the scene extends hence have to be provided to the QGLViewer
|
||||||
|
in order for the Camera to correctly display the scene. High level positioning methods also use
|
||||||
|
this information (showEntireScene(), centerScene()...).
|
||||||
|
|
||||||
|
A Camera holds KeyFrameInterpolator that can be used to save Camera positions and paths. You can
|
||||||
|
interactively addKeyFrameToPath() to a given path using the default \c Alt+F[1-12] shortcuts. Use
|
||||||
|
playPath() to make the Camera follow the path (default shortcut is F[1-12]). See the <a
|
||||||
|
href="../keyboard.html">keyboard page</a> for details on key customization.
|
||||||
|
|
||||||
|
Use cameraCoordinatesOf() and worldCoordinatesOf() to convert to and from the Camera frame()
|
||||||
|
coordinate system. projectedCoordinatesOf() and unprojectedCoordinatesOf() will convert from
|
||||||
|
screen to 3D coordinates. convertClickToLine() is very useful for analytical object selection.
|
||||||
|
|
||||||
|
Stereo display is possible on machines with quad buffer capabilities (with Camera::PERSPECTIVE
|
||||||
|
type() only). Test the <a href="../examples/stereoViewer.html">stereoViewer example</a> to check.
|
||||||
|
|
||||||
|
A Camera can also be used outside of a QGLViewer or even without OpenGL for its coordinate system
|
||||||
|
conversion capabilities. Note however that some of them explicitly rely on the presence of a
|
||||||
|
Z-buffer. \nosubgrouping */
|
||||||
|
class QGLVIEWER_EXPORT Camera : public QObject
|
||||||
|
{
|
||||||
|
#ifndef DOXYGEN
|
||||||
|
friend class ::QGLViewer;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
Camera();
|
||||||
|
virtual ~Camera();
|
||||||
|
|
||||||
|
Camera(const Camera& camera);
|
||||||
|
Camera& operator=(const Camera& camera);
|
||||||
|
|
||||||
|
|
||||||
|
/*! Enumerates the two possible types of Camera.
|
||||||
|
|
||||||
|
See type() and setType(). This type mainly defines different Camera projection matrix (see
|
||||||
|
loadProjectionMatrix()). Many other methods (pointUnderPixel(), convertClickToLine(),
|
||||||
|
projectedCoordinatesOf(), pixelGLRatio()...) are affected by this Type. */
|
||||||
|
enum Type { PERSPECTIVE, ORTHOGRAPHIC };
|
||||||
|
|
||||||
|
/*! @name Position and orientation */
|
||||||
|
//@{
|
||||||
|
public:
|
||||||
|
Vec position() const;
|
||||||
|
Vec upVector() const;
|
||||||
|
Vec viewDirection() const;
|
||||||
|
Vec rightVector() const;
|
||||||
|
Quaternion orientation() const;
|
||||||
|
|
||||||
|
void setFromModelViewMatrix(const GLdouble* const modelViewMatrix);
|
||||||
|
void setFromProjectionMatrix(const qreal matrix[12]);
|
||||||
|
|
||||||
|
public Q_SLOTS:
|
||||||
|
void setPosition(const Vec& pos);
|
||||||
|
void setOrientation(const Quaternion& q);
|
||||||
|
void setOrientation(qreal theta, qreal phi);
|
||||||
|
void setUpVector(const Vec& up, bool noMove=true);
|
||||||
|
void setViewDirection(const Vec& direction);
|
||||||
|
//@}
|
||||||
|
|
||||||
|
|
||||||
|
/*! @name Positioning tools */
|
||||||
|
//@{
|
||||||
|
public Q_SLOTS:
|
||||||
|
void lookAt(const Vec& target);
|
||||||
|
void showEntireScene();
|
||||||
|
void fitSphere(const Vec& center, qreal radius);
|
||||||
|
void fitBoundingBox(const Vec& min, const Vec& max);
|
||||||
|
void fitScreenRegion(const QRect& rectangle);
|
||||||
|
void centerScene();
|
||||||
|
void interpolateToZoomOnPixel(const QPoint& pixel);
|
||||||
|
void interpolateToFitScene();
|
||||||
|
void interpolateTo(const Frame& fr, qreal duration);
|
||||||
|
//@}
|
||||||
|
|
||||||
|
|
||||||
|
/*! @name Frustum */
|
||||||
|
//@{
|
||||||
|
public:
|
||||||
|
/*! Returns the Camera::Type of the Camera.
|
||||||
|
|
||||||
|
Set by setType(). Mainly used by loadProjectionMatrix().
|
||||||
|
|
||||||
|
A Camera::PERSPECTIVE Camera uses a classical projection mainly defined by its fieldOfView().
|
||||||
|
|
||||||
|
With a Camera::ORTHOGRAPHIC type(), the fieldOfView() is meaningless and the width and height of
|
||||||
|
the Camera frustum are inferred from the distance to the pivotPoint() using
|
||||||
|
getOrthoWidthHeight().
|
||||||
|
|
||||||
|
Both types use zNear() and zFar() (to define their clipping planes) and aspectRatio() (for
|
||||||
|
frustum shape). */
|
||||||
|
Type type() const { return type_; }
|
||||||
|
|
||||||
|
/*! Returns the vertical field of view of the Camera (in radians).
|
||||||
|
|
||||||
|
Value is set using setFieldOfView(). Default value is pi/4 radians. This value is meaningless if
|
||||||
|
the Camera type() is Camera::ORTHOGRAPHIC.
|
||||||
|
|
||||||
|
The field of view corresponds the one used in \c gluPerspective (see manual). It sets the Y
|
||||||
|
(vertical) aperture of the Camera. The X (horizontal) angle is inferred from the window aspect
|
||||||
|
ratio (see aspectRatio() and horizontalFieldOfView()).
|
||||||
|
|
||||||
|
Use setFOVToFitScene() to adapt the fieldOfView() to a given scene. */
|
||||||
|
qreal fieldOfView() const { return fieldOfView_; }
|
||||||
|
|
||||||
|
/*! Returns the horizontal field of view of the Camera (in radians).
|
||||||
|
|
||||||
|
Value is set using setHorizontalFieldOfView() or setFieldOfView(). These values
|
||||||
|
are always linked by:
|
||||||
|
\code
|
||||||
|
horizontalFieldOfView() = 2.0 * atan ( tan(fieldOfView()/2.0) * aspectRatio() ).
|
||||||
|
\endcode */
|
||||||
|
qreal horizontalFieldOfView() const { return 2.0 * atan ( tan(fieldOfView()/2.0) * aspectRatio() ); }
|
||||||
|
|
||||||
|
/*! Returns the Camera aspect ratio defined by screenWidth() / screenHeight().
|
||||||
|
|
||||||
|
When the Camera is attached to a QGLViewer, these values and hence the aspectRatio() are
|
||||||
|
automatically fitted to the viewer's window aspect ratio using setScreenWidthAndHeight(). */
|
||||||
|
qreal aspectRatio() const { return screenWidth_ / static_cast<qreal>(screenHeight_); }
|
||||||
|
/*! Returns the width (in pixels) of the Camera screen.
|
||||||
|
|
||||||
|
Set using setScreenWidthAndHeight(). This value is automatically fitted to the QGLViewer's
|
||||||
|
window dimensions when the Camera is attached to a QGLViewer. See also QGLWidget::width() [TODO Update with QOpenGLWidget] */
|
||||||
|
int screenWidth() const { return screenWidth_; }
|
||||||
|
/*! Returns the height (in pixels) of the Camera screen.
|
||||||
|
|
||||||
|
Set using setScreenWidthAndHeight(). This value is automatically fitted to the QGLViewer's
|
||||||
|
window dimensions when the Camera is attached to a QGLViewer. See also QGLWidget::height() [TODO Update with QOpenGLWidget] */
|
||||||
|
int screenHeight() const { return screenHeight_; }
|
||||||
|
void getViewport(GLint viewport[4]) const;
|
||||||
|
qreal pixelGLRatio(const Vec& position) const;
|
||||||
|
|
||||||
|
/*! Returns the coefficient which is used to set zNear() when the Camera is inside the sphere
|
||||||
|
defined by sceneCenter() and zClippingCoefficient() * sceneRadius().
|
||||||
|
|
||||||
|
In that case, the zNear() value is set to zNearCoefficient() * zClippingCoefficient() *
|
||||||
|
sceneRadius(). See the zNear() documentation for details.
|
||||||
|
|
||||||
|
Default value is 0.005, which is appropriate for most applications. In case you need a high
|
||||||
|
dynamic ZBuffer precision, you can increase this value (~0.1). A lower value will prevent
|
||||||
|
clipping of very close objects at the expense of a worst Z precision.
|
||||||
|
|
||||||
|
Only meaningful when Camera type is Camera::PERSPECTIVE. */
|
||||||
|
qreal zNearCoefficient() const { return zNearCoef_; }
|
||||||
|
/*! Returns the coefficient used to position the near and far clipping planes.
|
||||||
|
|
||||||
|
The near (resp. far) clipping plane is positioned at a distance equal to zClippingCoefficient() *
|
||||||
|
sceneRadius() in front of (resp. behind) the sceneCenter(). This garantees an optimal use of
|
||||||
|
the z-buffer range and minimizes aliasing. See the zNear() and zFar() documentations.
|
||||||
|
|
||||||
|
Default value is square root of 3.0 (so that a cube of size sceneRadius() is not clipped).
|
||||||
|
|
||||||
|
However, since the sceneRadius() is used for other purposes (see showEntireScene(), flySpeed(),
|
||||||
|
...) and you may want to change this value to define more precisely the location of the clipping
|
||||||
|
planes. See also zNearCoefficient().
|
||||||
|
|
||||||
|
For a total control on clipping planes' positions, an other option is to overload the zNear()
|
||||||
|
and zFar() methods. See the <a href="../examples/standardCamera.html">standardCamera example</a>.
|
||||||
|
|
||||||
|
\attention When QGLViewer::cameraPathAreEdited(), this value is set to 5.0 so that the Camera
|
||||||
|
paths are not clipped. The previous zClippingCoefficient() value is restored back when you leave
|
||||||
|
this mode. */
|
||||||
|
qreal zClippingCoefficient() const { return zClippingCoef_; }
|
||||||
|
|
||||||
|
virtual qreal zNear() const;
|
||||||
|
virtual qreal zFar() const;
|
||||||
|
virtual void getOrthoWidthHeight(GLdouble& halfWidth, GLdouble& halfHeight) const;
|
||||||
|
void getFrustumPlanesCoefficients(GLdouble coef[6][4]) const;
|
||||||
|
|
||||||
|
public Q_SLOTS:
|
||||||
|
void setType(Type type);
|
||||||
|
|
||||||
|
void setFieldOfView(qreal fov);
|
||||||
|
|
||||||
|
/*! Sets the horizontalFieldOfView() of the Camera (in radians).
|
||||||
|
|
||||||
|
horizontalFieldOfView() and fieldOfView() are linked by the aspectRatio(). This method actually
|
||||||
|
calls setFieldOfView(( 2.0 * atan (tan(hfov / 2.0) / aspectRatio()) )) so that a call to
|
||||||
|
horizontalFieldOfView() returns the expected value. */
|
||||||
|
void setHorizontalFieldOfView(qreal hfov) { setFieldOfView( 2.0 * atan (tan(hfov / 2.0) / aspectRatio()) ); }
|
||||||
|
|
||||||
|
void setFOVToFitScene();
|
||||||
|
|
||||||
|
/*! Defines the Camera aspectRatio().
|
||||||
|
|
||||||
|
This value is actually inferred from the screenWidth() / screenHeight() ratio. You should use
|
||||||
|
setScreenWidthAndHeight() instead.
|
||||||
|
|
||||||
|
This method might however be convenient when the Camera is not associated with a QGLViewer. It
|
||||||
|
actually sets the screenHeight() to 100 and the screenWidth() accordingly. See also
|
||||||
|
setFOVToFitScene().
|
||||||
|
|
||||||
|
\note If you absolutely need an aspectRatio() that does not correspond to your viewer's window
|
||||||
|
dimensions, overload loadProjectionMatrix() or multiply the created GL_PROJECTION matrix by a
|
||||||
|
scaled diagonal matrix in your QGLViewer::draw() method. */
|
||||||
|
void setAspectRatio(qreal aspect) { setScreenWidthAndHeight(int(100.0*aspect), 100); }
|
||||||
|
|
||||||
|
void setScreenWidthAndHeight(int width, int height);
|
||||||
|
/*! Sets the zNearCoefficient() value. */
|
||||||
|
void setZNearCoefficient(qreal coef) { zNearCoef_ = coef; projectionMatrixIsUpToDate_ = false; }
|
||||||
|
/*! Sets the zClippingCoefficient() value. */
|
||||||
|
void setZClippingCoefficient(qreal coef) { zClippingCoef_ = coef; projectionMatrixIsUpToDate_ = false; }
|
||||||
|
//@}
|
||||||
|
|
||||||
|
|
||||||
|
/*! @name Scene radius and center */
|
||||||
|
//@{
|
||||||
|
public:
|
||||||
|
/*! Returns the radius of the scene observed by the Camera.
|
||||||
|
|
||||||
|
You need to provide such an approximation of the scene dimensions so that the Camera can adapt
|
||||||
|
its zNear() and zFar() values. See the sceneCenter() documentation.
|
||||||
|
|
||||||
|
See also setSceneBoundingBox().
|
||||||
|
|
||||||
|
Note that QGLViewer::sceneRadius() (resp. QGLViewer::setSceneRadius()) simply call this method
|
||||||
|
(resp. setSceneRadius()) on its associated QGLViewer::camera(). */
|
||||||
|
qreal sceneRadius() const { return sceneRadius_; }
|
||||||
|
|
||||||
|
/*! Returns the position of the scene center, defined in the world coordinate system.
|
||||||
|
|
||||||
|
The scene observed by the Camera should be roughly centered on this position, and included in a
|
||||||
|
sceneRadius() sphere. This approximate description of the scene permits a zNear() and zFar()
|
||||||
|
clipping planes definition, and allows convenient positioning methods such as showEntireScene().
|
||||||
|
|
||||||
|
Default value is (0,0,0) (world origin). Use setSceneCenter() to change it. See also
|
||||||
|
setSceneBoundingBox().
|
||||||
|
|
||||||
|
Note that QGLViewer::sceneCenter() (resp. QGLViewer::setSceneCenter()) simply calls this method
|
||||||
|
(resp. setSceneCenter()) on its associated QGLViewer::camera(). */
|
||||||
|
Vec sceneCenter() const { return sceneCenter_; }
|
||||||
|
qreal distanceToSceneCenter() const;
|
||||||
|
|
||||||
|
public Q_SLOTS:
|
||||||
|
void setSceneRadius(qreal radius);
|
||||||
|
void setSceneCenter(const Vec& center);
|
||||||
|
bool setSceneCenterFromPixel(const QPoint& pixel);
|
||||||
|
void setSceneBoundingBox(const Vec& min, const Vec& max);
|
||||||
|
//@}
|
||||||
|
|
||||||
|
|
||||||
|
/*! @name Pivot Point */
|
||||||
|
//@{
|
||||||
|
public Q_SLOTS:
|
||||||
|
void setPivotPoint(const Vec& point);
|
||||||
|
bool setPivotPointFromPixel(const QPoint& pixel);
|
||||||
|
|
||||||
|
public:
|
||||||
|
Vec pivotPoint() const;
|
||||||
|
|
||||||
|
#ifndef DOXYGEN
|
||||||
|
public Q_SLOTS:
|
||||||
|
void setRevolveAroundPoint(const Vec& point);
|
||||||
|
bool setRevolveAroundPointFromPixel(const QPoint& pixel);
|
||||||
|
public:
|
||||||
|
Vec revolveAroundPoint() const;
|
||||||
|
#endif
|
||||||
|
//@}
|
||||||
|
|
||||||
|
|
||||||
|
/*! @name Associated frame */
|
||||||
|
//@{
|
||||||
|
public:
|
||||||
|
/*! Returns the ManipulatedCameraFrame attached to the Camera.
|
||||||
|
|
||||||
|
This ManipulatedCameraFrame defines its position() and orientation() and can translate mouse
|
||||||
|
events into Camera displacement. Set using setFrame(). */
|
||||||
|
ManipulatedCameraFrame* frame() const { return frame_; }
|
||||||
|
public Q_SLOTS:
|
||||||
|
void setFrame(ManipulatedCameraFrame* const mcf);
|
||||||
|
//@}
|
||||||
|
|
||||||
|
|
||||||
|
/*! @name KeyFramed paths */
|
||||||
|
//@{
|
||||||
|
public:
|
||||||
|
KeyFrameInterpolator* keyFrameInterpolator(unsigned int i) const;
|
||||||
|
|
||||||
|
public Q_SLOTS:
|
||||||
|
void setKeyFrameInterpolator(unsigned int i, KeyFrameInterpolator* const kfi);
|
||||||
|
|
||||||
|
virtual void addKeyFrameToPath(unsigned int i);
|
||||||
|
virtual void playPath(unsigned int i);
|
||||||
|
virtual void deletePath(unsigned int i);
|
||||||
|
virtual void resetPath(unsigned int i);
|
||||||
|
//@}
|
||||||
|
|
||||||
|
|
||||||
|
/*! @name OpenGL matrices */
|
||||||
|
//@{
|
||||||
|
public:
|
||||||
|
virtual void loadProjectionMatrix(bool reset=true) const;
|
||||||
|
virtual void loadModelViewMatrix(bool reset=true) const;
|
||||||
|
void computeProjectionMatrix() const;
|
||||||
|
void computeModelViewMatrix() const;
|
||||||
|
|
||||||
|
virtual void loadModelViewMatrixStereo(bool leftBuffer=true) const;
|
||||||
|
|
||||||
|
void getProjectionMatrix(GLfloat m[16]) const;
|
||||||
|
void getProjectionMatrix(GLdouble m[16]) const;
|
||||||
|
void getProjectionMatrix(QMatrix4x4& m) const;
|
||||||
|
|
||||||
|
void getModelViewMatrix(GLfloat m[16]) const;
|
||||||
|
void getModelViewMatrix(GLdouble m[16]) const;
|
||||||
|
void getModelViewMatrix(QMatrix4x4& m) const;
|
||||||
|
|
||||||
|
void getModelViewProjectionMatrix(GLfloat m[16]) const;
|
||||||
|
void getModelViewProjectionMatrix(GLdouble m[16]) const;
|
||||||
|
void getModelViewProjectionMatrix(QMatrix4x4& m) const;
|
||||||
|
//@}
|
||||||
|
|
||||||
|
|
||||||
|
/*! @name World to Camera coordinate systems conversions */
|
||||||
|
//@{
|
||||||
|
public:
|
||||||
|
Vec cameraCoordinatesOf(const Vec& src) const;
|
||||||
|
Vec worldCoordinatesOf(const Vec& src) const;
|
||||||
|
void getCameraCoordinatesOf(const qreal src[3], qreal res[3]) const;
|
||||||
|
void getWorldCoordinatesOf(const qreal src[3], qreal res[3]) const;
|
||||||
|
//@}
|
||||||
|
|
||||||
|
|
||||||
|
/*! @name 2D screen to 3D world coordinate systems conversions */
|
||||||
|
//@{
|
||||||
|
public:
|
||||||
|
Vec projectedCoordinatesOf(const Vec& src, const Frame* frame=NULL) const;
|
||||||
|
Vec unprojectedCoordinatesOf(const Vec& src, const Frame* frame=NULL) const;
|
||||||
|
void getProjectedCoordinatesOf(const qreal src[3], qreal res[3], const Frame* frame=NULL) const;
|
||||||
|
void getUnprojectedCoordinatesOf(const qreal src[3], qreal res[3], const Frame* frame=NULL) const;
|
||||||
|
void convertClickToLine(const QPoint& pixel, Vec& orig, Vec& dir) const;
|
||||||
|
Vec pointUnderPixel(const QPoint& pixel, bool& found) const;
|
||||||
|
//@}
|
||||||
|
|
||||||
|
|
||||||
|
/*! @name Fly speed */
|
||||||
|
//@{
|
||||||
|
public:
|
||||||
|
qreal flySpeed() const;
|
||||||
|
public Q_SLOTS:
|
||||||
|
void setFlySpeed(qreal speed);
|
||||||
|
//@}
|
||||||
|
|
||||||
|
|
||||||
|
/*! @name Stereo parameters */
|
||||||
|
//@{
|
||||||
|
public:
|
||||||
|
/*! Returns the user's inter-ocular distance (in meters). Default value is 0.062m, which fits most people.
|
||||||
|
|
||||||
|
loadProjectionMatrixStereo() uses this value to define the Camera offset and frustum. See
|
||||||
|
setIODistance(). */
|
||||||
|
qreal IODistance() const { return IODistance_; }
|
||||||
|
|
||||||
|
/*! Returns the physical distance between the user's eyes and the screen (in meters).
|
||||||
|
|
||||||
|
physicalDistanceToScreen() and focusDistance() represent the same distance. The former is
|
||||||
|
expressed in physical real world units, while the latter is expressed in OpenGL virtual world
|
||||||
|
units.
|
||||||
|
|
||||||
|
This is a helper function. It simply returns physicalScreenWidth() / 2.0 / tan(horizontalFieldOfView() / 2.0); */
|
||||||
|
qreal physicalDistanceToScreen() const { return physicalScreenWidth() / 2.0 / tan(horizontalFieldOfView() / 2.0); }
|
||||||
|
|
||||||
|
/*! Returns the physical screen width, in meters. Default value is 0.5m (average monitor width).
|
||||||
|
|
||||||
|
Used for stereo display only (see loadModelViewMatrixStereo() and loadProjectionMatrixStereo()).
|
||||||
|
Set using setPhysicalScreenWidth(). */
|
||||||
|
qreal physicalScreenWidth() const { return physicalScreenWidth_; }
|
||||||
|
|
||||||
|
/*! Returns the focus distance used by stereo display, expressed in OpenGL units.
|
||||||
|
|
||||||
|
This is the distance in the virtual world between the Camera and the plane where the horizontal
|
||||||
|
stereo parallax is null (the stereo left and right cameras' lines of sigth cross at this distance).
|
||||||
|
|
||||||
|
This distance is the virtual world equivalent of the real-world physicalDistanceToScreen().
|
||||||
|
|
||||||
|
\attention This value is modified by QGLViewer::setSceneRadius(), setSceneRadius() and
|
||||||
|
setFieldOfView(). When one of these values is modified, focusDistance() is set to sceneRadius()
|
||||||
|
/ tan(fieldOfView()/2), which provides good results. */
|
||||||
|
qreal focusDistance() const { return focusDistance_; }
|
||||||
|
public Q_SLOTS:
|
||||||
|
/*! Sets the IODistance(). */
|
||||||
|
void setIODistance(qreal distance) { IODistance_ = distance; }
|
||||||
|
|
||||||
|
#ifndef DOXYGEN
|
||||||
|
/*! This method is deprecated. Use setPhysicalScreenWidth() instead. */
|
||||||
|
void setPhysicalDistanceToScreen(qreal distance) { Q_UNUSED(distance); qWarning("setPhysicalDistanceToScreen is deprecated, use setPhysicalScreenWidth instead"); }
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*! Sets the physical screen (monitor or projected wall) width (in meters). */
|
||||||
|
void setPhysicalScreenWidth(qreal width) { physicalScreenWidth_ = width; }
|
||||||
|
|
||||||
|
/*! Sets the focusDistance(), in OpenGL scene units. */
|
||||||
|
void setFocusDistance(qreal distance) { focusDistance_ = distance; }
|
||||||
|
//@}
|
||||||
|
|
||||||
|
|
||||||
|
/*! @name XML representation */
|
||||||
|
//@{
|
||||||
|
public:
|
||||||
|
virtual QDomElement domElement(const QString& name, QDomDocument& document) const;
|
||||||
|
public Q_SLOTS:
|
||||||
|
virtual void initFromDOMElement(const QDomElement& element);
|
||||||
|
//@}
|
||||||
|
|
||||||
|
|
||||||
|
private Q_SLOTS:
|
||||||
|
void onFrameModified();
|
||||||
|
|
||||||
|
private:
|
||||||
|
// F r a m e
|
||||||
|
ManipulatedCameraFrame* frame_;
|
||||||
|
|
||||||
|
// C a m e r a p a r a m e t e r s
|
||||||
|
int screenWidth_, screenHeight_; // size of the window, in pixels
|
||||||
|
qreal fieldOfView_; // in radians
|
||||||
|
Vec sceneCenter_;
|
||||||
|
qreal sceneRadius_; // OpenGL units
|
||||||
|
qreal zNearCoef_;
|
||||||
|
qreal zClippingCoef_;
|
||||||
|
qreal orthoCoef_;
|
||||||
|
Type type_; // PERSPECTIVE or ORTHOGRAPHIC
|
||||||
|
mutable GLdouble modelViewMatrix_[16]; // Buffered model view matrix.
|
||||||
|
mutable bool modelViewMatrixIsUpToDate_;
|
||||||
|
mutable GLdouble projectionMatrix_[16]; // Buffered projection matrix.
|
||||||
|
mutable bool projectionMatrixIsUpToDate_;
|
||||||
|
|
||||||
|
// S t e r e o p a r a m e t e r s
|
||||||
|
qreal IODistance_; // inter-ocular distance, in meters
|
||||||
|
qreal focusDistance_; // in scene units
|
||||||
|
qreal physicalScreenWidth_; // in meters
|
||||||
|
|
||||||
|
// P o i n t s o f V i e w s a n d K e y F r a m e s
|
||||||
|
QMap<unsigned int, KeyFrameInterpolator*> kfi_;
|
||||||
|
KeyFrameInterpolator* interpolationKfi_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace qglviewer
|
||||||
|
|
||||||
|
#endif // QGLVIEWER_CAMERA_H
|
||||||
97
QGLViewer/config.h
Normal file
97
QGLViewer/config.h
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
|
||||||
|
Copyright (C) 2002-2014 Gilles Debunne. All rights reserved.
|
||||||
|
|
||||||
|
This file is part of the QGLViewer library version 2.6.3.
|
||||||
|
|
||||||
|
http://www.libqglviewer.com - contact@libqglviewer.com
|
||||||
|
|
||||||
|
This file may be used under the terms of the GNU General Public License
|
||||||
|
versions 2.0 or 3.0 as published by the Free Software Foundation and
|
||||||
|
appearing in the LICENSE file included in the packaging of this file.
|
||||||
|
In addition, as a special exception, Gilles Debunne gives you certain
|
||||||
|
additional rights, described in the file GPL_EXCEPTION in this package.
|
||||||
|
|
||||||
|
libQGLViewer uses dual licensing. Commercial/proprietary software must
|
||||||
|
purchase a libQGLViewer Commercial License.
|
||||||
|
|
||||||
|
This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
|
||||||
|
WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
|
||||||
|
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////
|
||||||
|
// libQGLViewer configuration file //
|
||||||
|
// Modify these settings according to your local configuration //
|
||||||
|
///////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
#ifndef QGLVIEWER_CONFIG_H
|
||||||
|
#define QGLVIEWER_CONFIG_H
|
||||||
|
|
||||||
|
#define QGLVIEWER_VERSION 0x020603
|
||||||
|
|
||||||
|
// Needed for Qt < 4 (?)
|
||||||
|
#ifndef QT_CLEAN_NAMESPACE
|
||||||
|
# define QT_CLEAN_NAMESPACE
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Get QT_VERSION and other Qt flags
|
||||||
|
#include <qglobal.h>
|
||||||
|
|
||||||
|
#if QT_VERSION < 0x040000
|
||||||
|
Error : libQGLViewer requires a minimum Qt version of 4.0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Win 32 DLL export macros
|
||||||
|
#ifdef Q_OS_WIN32
|
||||||
|
# ifndef M_PI
|
||||||
|
# define M_PI 3.14159265358979323846
|
||||||
|
# endif
|
||||||
|
# ifndef QGLVIEWER_STATIC
|
||||||
|
# ifdef CREATE_QGLVIEWER_DLL
|
||||||
|
# if QT_VERSION >= 0x040500
|
||||||
|
# define QGLVIEWER_EXPORT Q_DECL_EXPORT
|
||||||
|
# else
|
||||||
|
# define QGLVIEWER_EXPORT __declspec(dllexport)
|
||||||
|
# endif
|
||||||
|
# else
|
||||||
|
# if QT_VERSION >= 0x040500
|
||||||
|
# define QGLVIEWER_EXPORT Q_DECL_IMPORT
|
||||||
|
# else
|
||||||
|
# define QGLVIEWER_EXPORT __declspec(dllimport)
|
||||||
|
# endif
|
||||||
|
# endif
|
||||||
|
# endif
|
||||||
|
# ifndef __MINGW32__
|
||||||
|
# pragma warning( disable : 4251 ) // DLL interface, needed with Visual 6
|
||||||
|
# pragma warning( disable : 4786 ) // identifier truncated to 255 in browser information (Visual 6).
|
||||||
|
# endif
|
||||||
|
#endif // Q_OS_WIN32
|
||||||
|
|
||||||
|
// For other architectures, this macro is empty
|
||||||
|
#ifndef QGLVIEWER_EXPORT
|
||||||
|
# define QGLVIEWER_EXPORT
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// OpenGL includes - Included here and hence shared by all the files that need OpenGL headers.
|
||||||
|
# include <QOpenGLWidget>
|
||||||
|
|
||||||
|
// Container classes interfaces changed a lot in Qt.
|
||||||
|
// Compatibility patches are all grouped here.
|
||||||
|
#include <QList>
|
||||||
|
#include <QVector>
|
||||||
|
|
||||||
|
// For deprecated methods
|
||||||
|
// #define __WHERE__ "In file "<<__FILE__<<", line "<<__LINE__<<": "
|
||||||
|
// #define orientationAxisAngle(x,y,z,a) { std::cout << __WHERE__ << "getOrientationAxisAngle()." << std::endl; exit(0); }
|
||||||
|
|
||||||
|
// Patch for gcc version <= 2.95. Seems to no longer be needed with recent Qt versions.
|
||||||
|
// Uncomment these lines if you have error message dealing with operator << on QStrings
|
||||||
|
// #if defined(__GNUC__) && defined(__GNUC_MINOR__) && (__GNUC__ < 3) && (__GNUC_MINOR__ < 96)
|
||||||
|
// # include <iostream>
|
||||||
|
// # include <qstring.h>
|
||||||
|
// std::ostream& operator<<(std::ostream& out, const QString& str)
|
||||||
|
// { out << str.latin1(); return out; }
|
||||||
|
// #endif
|
||||||
|
|
||||||
|
#endif // QGLVIEWER_CONFIG_H
|
||||||
291
QGLViewer/constraint.cpp
Normal file
291
QGLViewer/constraint.cpp
Normal file
@ -0,0 +1,291 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
|
||||||
|
Copyright (C) 2002-2014 Gilles Debunne. All rights reserved.
|
||||||
|
|
||||||
|
This file is part of the QGLViewer library version 2.6.3.
|
||||||
|
|
||||||
|
http://www.libqglviewer.com - contact@libqglviewer.com
|
||||||
|
|
||||||
|
This file may be used under the terms of the GNU General Public License
|
||||||
|
versions 2.0 or 3.0 as published by the Free Software Foundation and
|
||||||
|
appearing in the LICENSE file included in the packaging of this file.
|
||||||
|
In addition, as a special exception, Gilles Debunne gives you certain
|
||||||
|
additional rights, described in the file GPL_EXCEPTION in this package.
|
||||||
|
|
||||||
|
libQGLViewer uses dual licensing. Commercial/proprietary software must
|
||||||
|
purchase a libQGLViewer Commercial License.
|
||||||
|
|
||||||
|
This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
|
||||||
|
WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
|
||||||
|
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
#include "constraint.h"
|
||||||
|
#include "frame.h"
|
||||||
|
#include "camera.h"
|
||||||
|
#include "manipulatedCameraFrame.h"
|
||||||
|
|
||||||
|
using namespace qglviewer;
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Constraint //
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
/*! Default constructor.
|
||||||
|
|
||||||
|
translationConstraintType() and rotationConstraintType() are set to AxisPlaneConstraint::FREE.
|
||||||
|
translationConstraintDirection() and rotationConstraintDirection() are set to (0,0,0). */
|
||||||
|
AxisPlaneConstraint::AxisPlaneConstraint()
|
||||||
|
: translationConstraintType_(FREE), rotationConstraintType_(FREE)
|
||||||
|
{
|
||||||
|
// Do not use set since setRotationConstraintType needs a read.
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Simply calls setTranslationConstraintType() and setTranslationConstraintDirection(). */
|
||||||
|
void AxisPlaneConstraint::setTranslationConstraint(Type type, const Vec& direction)
|
||||||
|
{
|
||||||
|
setTranslationConstraintType(type);
|
||||||
|
setTranslationConstraintDirection(direction);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Defines the translationConstraintDirection(). The coordinate system where \p direction is expressed depends on your class implementation. */
|
||||||
|
void AxisPlaneConstraint::setTranslationConstraintDirection(const Vec& direction)
|
||||||
|
{
|
||||||
|
if ((translationConstraintType()!=AxisPlaneConstraint::FREE) && (translationConstraintType()!=AxisPlaneConstraint::FORBIDDEN))
|
||||||
|
{
|
||||||
|
const qreal norm = direction.norm();
|
||||||
|
if (norm < 1E-8)
|
||||||
|
{
|
||||||
|
qWarning("AxisPlaneConstraint::setTranslationConstraintDir: null vector for translation constraint");
|
||||||
|
translationConstraintType_ = AxisPlaneConstraint::FREE;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
translationConstraintDir_ = direction/norm;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Simply calls setRotationConstraintType() and setRotationConstraintDirection(). */
|
||||||
|
void AxisPlaneConstraint::setRotationConstraint(Type type, const Vec& direction)
|
||||||
|
{
|
||||||
|
setRotationConstraintType(type);
|
||||||
|
setRotationConstraintDirection(direction);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Defines the rotationConstraintDirection(). The coordinate system where \p direction is expressed depends on your class implementation. */
|
||||||
|
void AxisPlaneConstraint::setRotationConstraintDirection(const Vec& direction)
|
||||||
|
{
|
||||||
|
if ((rotationConstraintType()!=AxisPlaneConstraint::FREE) && (rotationConstraintType()!=AxisPlaneConstraint::FORBIDDEN))
|
||||||
|
{
|
||||||
|
const qreal norm = direction.norm();
|
||||||
|
if (norm < 1E-8)
|
||||||
|
{
|
||||||
|
qWarning("AxisPlaneConstraint::setRotationConstraintDir: null vector for rotation constraint");
|
||||||
|
rotationConstraintType_ = AxisPlaneConstraint::FREE;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
rotationConstraintDir_ = direction/norm;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Set the Type() of the rotationConstraintType(). Default is AxisPlaneConstraint::FREE.
|
||||||
|
|
||||||
|
Depending on this value, the Frame will freely rotate (AxisPlaneConstraint::FREE), will only be able
|
||||||
|
to rotate around an axis (AxisPlaneConstraint::AXIS), or will not able to rotate at all
|
||||||
|
(AxisPlaneConstraint::FORBIDDEN).
|
||||||
|
|
||||||
|
Use Frame::setOrientation() to define the orientation of the constrained Frame before it gets
|
||||||
|
constrained.
|
||||||
|
|
||||||
|
\attention An AxisPlaneConstraint::PLANE Type() is not meaningful for rotational constraints and
|
||||||
|
will be ignored. */
|
||||||
|
void AxisPlaneConstraint::setRotationConstraintType(Type type)
|
||||||
|
{
|
||||||
|
if (rotationConstraintType() == AxisPlaneConstraint::PLANE)
|
||||||
|
{
|
||||||
|
qWarning("AxisPlaneConstraint::setRotationConstraintType: the PLANE type cannot be used for a rotation constraints");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
rotationConstraintType_ = type;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// LocalConstraint //
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
/*! Depending on translationConstraintType(), constrain \p translation to be along an axis or
|
||||||
|
limited to a plane defined in the Frame local coordinate system by
|
||||||
|
translationConstraintDirection(). */
|
||||||
|
void LocalConstraint::constrainTranslation(Vec& translation, Frame* const frame)
|
||||||
|
{
|
||||||
|
Vec proj;
|
||||||
|
switch (translationConstraintType())
|
||||||
|
{
|
||||||
|
case AxisPlaneConstraint::FREE:
|
||||||
|
break;
|
||||||
|
case AxisPlaneConstraint::PLANE:
|
||||||
|
proj = frame->rotation().rotate(translationConstraintDirection());
|
||||||
|
translation.projectOnPlane(proj);
|
||||||
|
break;
|
||||||
|
case AxisPlaneConstraint::AXIS:
|
||||||
|
proj = frame->rotation().rotate(translationConstraintDirection());
|
||||||
|
translation.projectOnAxis(proj);
|
||||||
|
break;
|
||||||
|
case AxisPlaneConstraint::FORBIDDEN:
|
||||||
|
translation = Vec(0.0, 0.0, 0.0);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! When rotationConstraintType() is AxisPlaneConstraint::AXIS, constrain \p rotation to be a rotation
|
||||||
|
around an axis whose direction is defined in the Frame local coordinate system by
|
||||||
|
rotationConstraintDirection(). */
|
||||||
|
void LocalConstraint::constrainRotation(Quaternion& rotation, Frame* const)
|
||||||
|
{
|
||||||
|
switch (rotationConstraintType())
|
||||||
|
{
|
||||||
|
case AxisPlaneConstraint::FREE:
|
||||||
|
break;
|
||||||
|
case AxisPlaneConstraint::PLANE:
|
||||||
|
break;
|
||||||
|
case AxisPlaneConstraint::AXIS:
|
||||||
|
{
|
||||||
|
Vec axis = rotationConstraintDirection();
|
||||||
|
Vec quat = Vec(rotation[0], rotation[1], rotation[2]);
|
||||||
|
quat.projectOnAxis(axis);
|
||||||
|
rotation = Quaternion(quat, 2.0*acos(rotation[3]));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case AxisPlaneConstraint::FORBIDDEN:
|
||||||
|
rotation = Quaternion(); // identity
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// WorldConstraint //
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
/*! Depending on translationConstraintType(), constrain \p translation to be along an axis or
|
||||||
|
limited to a plane defined in the world coordinate system by
|
||||||
|
translationConstraintDirection(). */
|
||||||
|
void WorldConstraint::constrainTranslation(Vec& translation, Frame* const frame)
|
||||||
|
{
|
||||||
|
Vec proj;
|
||||||
|
switch (translationConstraintType())
|
||||||
|
{
|
||||||
|
case AxisPlaneConstraint::FREE:
|
||||||
|
break;
|
||||||
|
case AxisPlaneConstraint::PLANE:
|
||||||
|
if (frame->referenceFrame())
|
||||||
|
{
|
||||||
|
proj = frame->referenceFrame()->transformOf(translationConstraintDirection());
|
||||||
|
translation.projectOnPlane(proj);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
translation.projectOnPlane(translationConstraintDirection());
|
||||||
|
break;
|
||||||
|
case AxisPlaneConstraint::AXIS:
|
||||||
|
if (frame->referenceFrame())
|
||||||
|
{
|
||||||
|
proj = frame->referenceFrame()->transformOf(translationConstraintDirection());
|
||||||
|
translation.projectOnAxis(proj);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
translation.projectOnAxis(translationConstraintDirection());
|
||||||
|
break;
|
||||||
|
case AxisPlaneConstraint::FORBIDDEN:
|
||||||
|
translation = Vec(0.0, 0.0, 0.0);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! When rotationConstraintType() is AxisPlaneConstraint::AXIS, constrain \p rotation to be a rotation
|
||||||
|
around an axis whose direction is defined in the world coordinate system by
|
||||||
|
rotationConstraintDirection(). */
|
||||||
|
void WorldConstraint::constrainRotation(Quaternion& rotation, Frame* const frame)
|
||||||
|
{
|
||||||
|
switch (rotationConstraintType())
|
||||||
|
{
|
||||||
|
case AxisPlaneConstraint::FREE:
|
||||||
|
break;
|
||||||
|
case AxisPlaneConstraint::PLANE:
|
||||||
|
break;
|
||||||
|
case AxisPlaneConstraint::AXIS:
|
||||||
|
{
|
||||||
|
Vec quat(rotation[0], rotation[1], rotation[2]);
|
||||||
|
Vec axis = frame->transformOf(rotationConstraintDirection());
|
||||||
|
quat.projectOnAxis(axis);
|
||||||
|
rotation = Quaternion(quat, 2.0*acos(rotation[3]));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case AxisPlaneConstraint::FORBIDDEN:
|
||||||
|
rotation = Quaternion(); // identity
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// CameraConstraint //
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
/*! Creates a CameraConstraint, whose constrained directions are defined in the \p camera coordinate
|
||||||
|
system. */
|
||||||
|
CameraConstraint::CameraConstraint(const Camera* const camera)
|
||||||
|
: AxisPlaneConstraint(), camera_(camera)
|
||||||
|
{}
|
||||||
|
|
||||||
|
/*! Depending on translationConstraintType(), constrain \p translation to be along an axis or
|
||||||
|
limited to a plane defined in the camera() coordinate system by
|
||||||
|
translationConstraintDirection(). */
|
||||||
|
void CameraConstraint::constrainTranslation(Vec& translation, Frame* const frame)
|
||||||
|
{
|
||||||
|
Vec proj;
|
||||||
|
switch (translationConstraintType())
|
||||||
|
{
|
||||||
|
case AxisPlaneConstraint::FREE:
|
||||||
|
break;
|
||||||
|
case AxisPlaneConstraint::PLANE:
|
||||||
|
proj = camera()->frame()->inverseTransformOf(translationConstraintDirection());
|
||||||
|
if (frame->referenceFrame())
|
||||||
|
proj = frame->referenceFrame()->transformOf(proj);
|
||||||
|
translation.projectOnPlane(proj);
|
||||||
|
break;
|
||||||
|
case AxisPlaneConstraint::AXIS:
|
||||||
|
proj = camera()->frame()->inverseTransformOf(translationConstraintDirection());
|
||||||
|
if (frame->referenceFrame())
|
||||||
|
proj = frame->referenceFrame()->transformOf(proj);
|
||||||
|
translation.projectOnAxis(proj);
|
||||||
|
break;
|
||||||
|
case AxisPlaneConstraint::FORBIDDEN:
|
||||||
|
translation = Vec(0.0, 0.0, 0.0);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! When rotationConstraintType() is AxisPlaneConstraint::AXIS, constrain \p rotation to be a rotation
|
||||||
|
around an axis whose direction is defined in the camera() coordinate system by
|
||||||
|
rotationConstraintDirection(). */
|
||||||
|
void CameraConstraint::constrainRotation(Quaternion& rotation, Frame* const frame)
|
||||||
|
{
|
||||||
|
switch (rotationConstraintType())
|
||||||
|
{
|
||||||
|
case AxisPlaneConstraint::FREE:
|
||||||
|
break;
|
||||||
|
case AxisPlaneConstraint::PLANE:
|
||||||
|
break;
|
||||||
|
case AxisPlaneConstraint::AXIS:
|
||||||
|
{
|
||||||
|
Vec axis = frame->transformOf(camera()->frame()->inverseTransformOf(rotationConstraintDirection()));
|
||||||
|
Vec quat = Vec(rotation[0], rotation[1], rotation[2]);
|
||||||
|
quat.projectOnAxis(axis);
|
||||||
|
rotation = Quaternion(quat, 2.0*acos(rotation[3]));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case AxisPlaneConstraint::FORBIDDEN:
|
||||||
|
rotation = Quaternion(); // identity
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
338
QGLViewer/constraint.h
Normal file
338
QGLViewer/constraint.h
Normal file
@ -0,0 +1,338 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
|
||||||
|
Copyright (C) 2002-2014 Gilles Debunne. All rights reserved.
|
||||||
|
|
||||||
|
This file is part of the QGLViewer library version 2.6.3.
|
||||||
|
|
||||||
|
http://www.libqglviewer.com - contact@libqglviewer.com
|
||||||
|
|
||||||
|
This file may be used under the terms of the GNU General Public License
|
||||||
|
versions 2.0 or 3.0 as published by the Free Software Foundation and
|
||||||
|
appearing in the LICENSE file included in the packaging of this file.
|
||||||
|
In addition, as a special exception, Gilles Debunne gives you certain
|
||||||
|
additional rights, described in the file GPL_EXCEPTION in this package.
|
||||||
|
|
||||||
|
libQGLViewer uses dual licensing. Commercial/proprietary software must
|
||||||
|
purchase a libQGLViewer Commercial License.
|
||||||
|
|
||||||
|
This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
|
||||||
|
WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
|
||||||
|
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
#ifndef QGLVIEWER_CONSTRAINT_H
|
||||||
|
#define QGLVIEWER_CONSTRAINT_H
|
||||||
|
|
||||||
|
#include "vec.h"
|
||||||
|
#include "quaternion.h"
|
||||||
|
|
||||||
|
namespace qglviewer {
|
||||||
|
class Frame;
|
||||||
|
class Camera;
|
||||||
|
|
||||||
|
/*! \brief An interface class for Frame constraints.
|
||||||
|
\class Constraint constraint.h QGLViewer/constraint.h
|
||||||
|
|
||||||
|
This class defines the interface for the Constraints that can be applied to a Frame to limit its
|
||||||
|
motion. Use Frame::setConstraint() to associate a Constraint to a Frame (default is a \c NULL
|
||||||
|
Frame::constraint()).
|
||||||
|
|
||||||
|
<h3>How does it work ?</h3>
|
||||||
|
|
||||||
|
The Constraint acts as a filter on the translation and rotation Frame increments.
|
||||||
|
constrainTranslation() and constrainRotation() should be overloaded to specify the constraint
|
||||||
|
behavior: the desired displacement is given as a parameter that can optionally be modified.
|
||||||
|
|
||||||
|
Here is how the Frame::translate() and Frame::rotate() methods use the Constraint:
|
||||||
|
\code
|
||||||
|
Frame::translate(Vec& T)
|
||||||
|
{
|
||||||
|
if (constraint())
|
||||||
|
constraint()->constrainTranslation(T, this);
|
||||||
|
t += T;
|
||||||
|
}
|
||||||
|
|
||||||
|
Frame::rotate(Quaternion& Q)
|
||||||
|
{
|
||||||
|
if (constraint())
|
||||||
|
constraint()->constrainRotation(Q, this);
|
||||||
|
q *= Q;
|
||||||
|
}
|
||||||
|
\endcode
|
||||||
|
|
||||||
|
The default behavior of constrainTranslation() and constrainRotation() is empty (meaning no
|
||||||
|
filtering).
|
||||||
|
|
||||||
|
The Frame which uses the Constraint is passed as a parameter to the constrainTranslation() and
|
||||||
|
constrainRotation() methods, so that they can have access to its current state (mainly
|
||||||
|
Frame::position() and Frame::orientation()). It is not \c const for versatility reasons, but
|
||||||
|
directly modifying it should be avoided.
|
||||||
|
|
||||||
|
\attention Frame::setTranslation(), Frame::setRotation() and similar methods will actually indeed
|
||||||
|
set the frame position and orientation, without taking the constraint into account. Use the \e
|
||||||
|
WithConstraint versions of these methods to enforce the Constraint.
|
||||||
|
|
||||||
|
<h3>Implemented Constraints</h3>
|
||||||
|
|
||||||
|
Classical axial and plane Constraints are provided for convenience: see the LocalConstraint,
|
||||||
|
WorldConstraint and CameraConstraint classes' documentations.
|
||||||
|
|
||||||
|
Try the <a href="../examples/constrainedFrame.html">constrainedFrame</a> and <a
|
||||||
|
href="../examples/constrainedCamera.html">constrainedCamera</a> examples for an illustration.
|
||||||
|
|
||||||
|
<h3>Creating new Constraints</h3>
|
||||||
|
|
||||||
|
The implementation of a new Constraint class simply consists in overloading the filtering methods:
|
||||||
|
\code
|
||||||
|
// This Constraint enforces that the Frame cannot have a negative z world coordinate.
|
||||||
|
class myConstraint : public Constraint
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual void constrainTranslation(Vec& t, Frame * const fr)
|
||||||
|
{
|
||||||
|
// Express t in the world coordinate system.
|
||||||
|
const Vec tWorld = fr->inverseTransformOf(t);
|
||||||
|
if (fr->position().z + tWorld.z < 0.0) // check the new fr z coordinate
|
||||||
|
t.z = fr->transformOf(-fr->position().z); // t.z is clamped so that next z position is 0.0
|
||||||
|
}
|
||||||
|
};
|
||||||
|
\endcode
|
||||||
|
|
||||||
|
Note that the translation (resp. rotation) parameter passed to constrainTranslation() (resp.
|
||||||
|
constrainRotation()) is expressed in the \e local Frame coordinate system. Here, we use the
|
||||||
|
Frame::transformOf() and Frame::inverseTransformOf() method to convert it to and from the world
|
||||||
|
coordinate system.
|
||||||
|
|
||||||
|
Combined constraints can easily be achieved by creating a new class that applies the different
|
||||||
|
constraint filters:
|
||||||
|
\code
|
||||||
|
myConstraint::constrainTranslation(Vec& v, Frame* const fr)
|
||||||
|
{
|
||||||
|
constraint1->constrainTranslation(v, fr);
|
||||||
|
constraint2->constrainTranslation(v, fr);
|
||||||
|
// and so on, with possible branches, tests, loops...
|
||||||
|
}
|
||||||
|
\endcode
|
||||||
|
*/
|
||||||
|
class QGLVIEWER_EXPORT Constraint
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/*! Virtual destructor. Empty. */
|
||||||
|
virtual ~Constraint() {}
|
||||||
|
|
||||||
|
/*! Filters the translation applied to the \p frame. This default implementation is empty (no
|
||||||
|
filtering).
|
||||||
|
|
||||||
|
Overload this method in your own Constraint class to define a new translation constraint. \p
|
||||||
|
frame is the Frame to which is applied the translation. It is not defined \c const, but you
|
||||||
|
should refrain from directly changing its value in the constraint. Use its Frame::position() and
|
||||||
|
update the \p translation accordingly instead.
|
||||||
|
|
||||||
|
\p translation is expressed in local frame coordinate system. Use Frame::inverseTransformOf() to
|
||||||
|
express it in the world coordinate system if needed. */
|
||||||
|
virtual void constrainTranslation(Vec& translation, Frame* const frame) { Q_UNUSED(translation); Q_UNUSED(frame); }
|
||||||
|
/*! Filters the rotation applied to the \p frame. This default implementation is empty (no
|
||||||
|
filtering).
|
||||||
|
|
||||||
|
Overload this method in your own Constraint class to define a new rotation constraint. See
|
||||||
|
constrainTranslation() for details.
|
||||||
|
|
||||||
|
Use Frame::inverseTransformOf() on the \p rotation Quaternion::axis() to express \p rotation in
|
||||||
|
the world coordinate system if needed. */
|
||||||
|
virtual void constrainRotation(Quaternion& rotation, Frame* const frame) { Q_UNUSED(rotation); Q_UNUSED(frame); }
|
||||||
|
};
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\brief An abstract class for Frame Constraints defined by an axis or a plane.
|
||||||
|
\class AxisPlaneConstraint constraint.h QGLViewer/constraint.h
|
||||||
|
|
||||||
|
AxisPlaneConstraint is an interface for (translation and/or rotation) Constraint that are defined
|
||||||
|
by a direction. translationConstraintType() and rotationConstraintType() define how this
|
||||||
|
direction should be interpreted: as an axis (AxisPlaneConstraint::AXIS) or as a plane normal
|
||||||
|
(AxisPlaneConstraint::PLANE). See the Type() documentation for details.
|
||||||
|
|
||||||
|
The three implementations of this class: LocalConstraint, WorldConstraint and CameraConstraint
|
||||||
|
differ by the coordinate system in which this direction is expressed.
|
||||||
|
|
||||||
|
Different implementations of this class are illustrated in the
|
||||||
|
<a href="../examples/constrainedCamera.html">contrainedCamera</a> and
|
||||||
|
<a href="../examples/constrainedFrame.html">constrainedFrame</a> examples.
|
||||||
|
|
||||||
|
\attention When applied, the rotational Constraint may not intuitively follow the mouse
|
||||||
|
displacement. A solution would be to directly measure the rotation angle in screen coordinates,
|
||||||
|
but that would imply to know the QGLViewer::camera(), so that we can compute the projected
|
||||||
|
coordinates of the rotation center (as is done with the QGLViewer::SCREEN_ROTATE binding).
|
||||||
|
However, adding an extra pointer to the QGLViewer::camera() in all the AxisPlaneConstraint
|
||||||
|
derived classes (which the user would have to update in a multi-viewer application) was judged as
|
||||||
|
an overkill. */
|
||||||
|
class QGLVIEWER_EXPORT AxisPlaneConstraint : public Constraint
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
AxisPlaneConstraint();
|
||||||
|
/*! Virtual destructor. Empty. */
|
||||||
|
virtual ~AxisPlaneConstraint() {}
|
||||||
|
|
||||||
|
/*! Type lists the different types of translation and rotation constraints that are available.
|
||||||
|
|
||||||
|
It specifies the meaning of the constraint direction (see translationConstraintDirection() and
|
||||||
|
rotationConstraintDirection()): as an axis direction (AxisPlaneConstraint::AXIS) or a plane
|
||||||
|
normal (AxisPlaneConstraint::PLANE). AxisPlaneConstraint::FREE means no constraint while
|
||||||
|
AxisPlaneConstraint::FORBIDDEN completely forbids the translation and/or the rotation.
|
||||||
|
|
||||||
|
See translationConstraintType() and rotationConstraintType().
|
||||||
|
|
||||||
|
\attention The AxisPlaneConstraint::PLANE Type is not valid for rotational constraint.
|
||||||
|
|
||||||
|
New derived classes can use their own extended enum for specific constraints:
|
||||||
|
\code
|
||||||
|
class MyAxisPlaneConstraint : public AxisPlaneConstraint
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
enum MyType { FREE, AXIS, PLANE, FORBIDDEN, CUSTOM };
|
||||||
|
virtual void constrainTranslation(Vec &translation, Frame *const frame)
|
||||||
|
{
|
||||||
|
// translationConstraintType() is simply an int. CUSTOM Type is handled seamlessly.
|
||||||
|
switch (translationConstraintType())
|
||||||
|
{
|
||||||
|
case MyAxisPlaneConstraint::FREE: ... break;
|
||||||
|
case MyAxisPlaneConstraint::CUSTOM: ... break;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
MyAxisPlaneConstraint* c = new MyAxisPlaneConstraint();
|
||||||
|
// Note the Type conversion
|
||||||
|
c->setTranslationConstraintType(AxisPlaneConstraint::Type(MyAxisPlaneConstraint::CUSTOM));
|
||||||
|
};
|
||||||
|
\endcode */
|
||||||
|
enum Type { FREE, AXIS, PLANE, FORBIDDEN };
|
||||||
|
|
||||||
|
/*! @name Translation constraint */
|
||||||
|
//@{
|
||||||
|
/*! Overloading of Constraint::constrainTranslation(). Empty */
|
||||||
|
virtual void constrainTranslation(Vec& translation, Frame* const frame) { Q_UNUSED(translation); Q_UNUSED(frame); };
|
||||||
|
|
||||||
|
void setTranslationConstraint(Type type, const Vec& direction);
|
||||||
|
/*! Sets the Type() of the translationConstraintType(). Default is AxisPlaneConstraint::FREE. */
|
||||||
|
void setTranslationConstraintType(Type type) { translationConstraintType_ = type; };
|
||||||
|
void setTranslationConstraintDirection(const Vec& direction);
|
||||||
|
|
||||||
|
/*! Returns the translation constraint Type().
|
||||||
|
|
||||||
|
Depending on this value, the Frame will freely translate (AxisPlaneConstraint::FREE), will only
|
||||||
|
be able to translate along an axis direction (AxisPlaneConstraint::AXIS), will be forced to stay
|
||||||
|
into a plane (AxisPlaneConstraint::PLANE) or will not able to translate at all
|
||||||
|
(AxisPlaneConstraint::FORBIDDEN).
|
||||||
|
|
||||||
|
Use Frame::setPosition() to define the position of the constrained Frame before it gets
|
||||||
|
constrained. */
|
||||||
|
Type translationConstraintType() const { return translationConstraintType_; };
|
||||||
|
/*! Returns the direction used by the translation constraint.
|
||||||
|
|
||||||
|
It represents the axis direction (AxisPlaneConstraint::AXIS) or the plane normal
|
||||||
|
(AxisPlaneConstraint::PLANE) depending on the translationConstraintType(). It is undefined for
|
||||||
|
AxisPlaneConstraint::FREE or AxisPlaneConstraint::FORBIDDEN.
|
||||||
|
|
||||||
|
The AxisPlaneConstraint derived classes express this direction in different coordinate system
|
||||||
|
(camera for CameraConstraint, local for LocalConstraint, and world for WorldConstraint). This
|
||||||
|
value can be modified with setTranslationConstraintDirection(). */
|
||||||
|
Vec translationConstraintDirection() const { return translationConstraintDir_; };
|
||||||
|
//@}
|
||||||
|
|
||||||
|
/*! @name Rotation constraint */
|
||||||
|
//@{
|
||||||
|
/*! Overloading of Constraint::constrainRotation(). Empty. */
|
||||||
|
virtual void constrainRotation(Quaternion& rotation, Frame* const frame) { Q_UNUSED(rotation); Q_UNUSED(frame); };
|
||||||
|
|
||||||
|
void setRotationConstraint(Type type, const Vec& direction);
|
||||||
|
void setRotationConstraintType(Type type);
|
||||||
|
void setRotationConstraintDirection(const Vec& direction);
|
||||||
|
|
||||||
|
/*! Returns the rotation constraint Type(). */
|
||||||
|
Type rotationConstraintType() const { return rotationConstraintType_; };
|
||||||
|
/*! Returns the axis direction used by the rotation constraint.
|
||||||
|
|
||||||
|
This direction is defined only when rotationConstraintType() is AxisPlaneConstraint::AXIS.
|
||||||
|
|
||||||
|
The AxisPlaneConstraint derived classes express this direction in different coordinate system
|
||||||
|
(camera for CameraConstraint, local for LocalConstraint, and world for WorldConstraint). This
|
||||||
|
value can be modified with setRotationConstraintDirection(). */
|
||||||
|
Vec rotationConstraintDirection() const { return rotationConstraintDir_; };
|
||||||
|
//@}
|
||||||
|
|
||||||
|
private:
|
||||||
|
// int and not Type to allow for overloading and new types definition.
|
||||||
|
Type translationConstraintType_;
|
||||||
|
Type rotationConstraintType_;
|
||||||
|
|
||||||
|
Vec translationConstraintDir_;
|
||||||
|
Vec rotationConstraintDir_;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/*! \brief An AxisPlaneConstraint defined in the Frame local coordinate system.
|
||||||
|
\class LocalConstraint constraint.h QGLViewer/constraint.h
|
||||||
|
|
||||||
|
The translationConstraintDirection() and rotationConstraintDirection() are expressed in the Frame
|
||||||
|
local coordinate system (see Frame::referenceFrame()).
|
||||||
|
|
||||||
|
See the <a href="../examples/constrainedFrame.html">constrainedFrame</a> example for an illustration. */
|
||||||
|
class QGLVIEWER_EXPORT LocalConstraint : public AxisPlaneConstraint
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/*! Virtual destructor. Empty. */
|
||||||
|
virtual ~LocalConstraint() {};
|
||||||
|
|
||||||
|
virtual void constrainTranslation(Vec& translation, Frame* const frame);
|
||||||
|
virtual void constrainRotation (Quaternion& rotation, Frame* const frame);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*! \brief An AxisPlaneConstraint defined in the world coordinate system.
|
||||||
|
\class WorldConstraint constraint.h QGLViewer/constraint.h
|
||||||
|
|
||||||
|
The translationConstraintDirection() and rotationConstraintDirection() are expressed in world
|
||||||
|
coordinate system.
|
||||||
|
|
||||||
|
See the <a href="../examples/constrainedFrame.html">constrainedFrame</a> and <a
|
||||||
|
href="../examples/multiView.html">multiView</a> examples for an illustration. */
|
||||||
|
class QGLVIEWER_EXPORT WorldConstraint : public AxisPlaneConstraint
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/*! Virtual destructor. Empty. */
|
||||||
|
virtual ~WorldConstraint() {};
|
||||||
|
|
||||||
|
virtual void constrainTranslation(Vec& translation, Frame* const frame);
|
||||||
|
virtual void constrainRotation (Quaternion& rotation, Frame* const frame);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*! \brief An AxisPlaneConstraint defined in the camera coordinate system.
|
||||||
|
\class CameraConstraint constraint.h QGLViewer/constraint.h
|
||||||
|
|
||||||
|
The translationConstraintDirection() and rotationConstraintDirection() are expressed in the
|
||||||
|
associated camera() coordinate system.
|
||||||
|
|
||||||
|
See the <a href="../examples/constrainedFrame.html">constrainedFrame</a> and <a
|
||||||
|
href="../examples/constrainedCamera.html">constrainedCamera</a> examples for an illustration. */
|
||||||
|
class QGLVIEWER_EXPORT CameraConstraint : public AxisPlaneConstraint
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit CameraConstraint(const Camera* const camera);
|
||||||
|
/*! Virtual destructor. Empty. */
|
||||||
|
virtual ~CameraConstraint() {};
|
||||||
|
|
||||||
|
virtual void constrainTranslation(Vec& translation, Frame* const frame);
|
||||||
|
virtual void constrainRotation (Quaternion& rotation, Frame* const frame);
|
||||||
|
|
||||||
|
/*! Returns the associated Camera. Set using the CameraConstraint constructor. */
|
||||||
|
const Camera* camera() const { return camera_; };
|
||||||
|
|
||||||
|
private:
|
||||||
|
const Camera* const camera_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace qglviewer
|
||||||
|
|
||||||
|
#endif // QGLVIEWER_CONSTRAINT_H
|
||||||
161
QGLViewer/domUtils.h
Normal file
161
QGLViewer/domUtils.h
Normal file
@ -0,0 +1,161 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
|
||||||
|
Copyright (C) 2002-2014 Gilles Debunne. All rights reserved.
|
||||||
|
|
||||||
|
This file is part of the QGLViewer library version 2.6.3.
|
||||||
|
|
||||||
|
http://www.libqglviewer.com - contact@libqglviewer.com
|
||||||
|
|
||||||
|
This file may be used under the terms of the GNU General Public License
|
||||||
|
versions 2.0 or 3.0 as published by the Free Software Foundation and
|
||||||
|
appearing in the LICENSE file included in the packaging of this file.
|
||||||
|
In addition, as a special exception, Gilles Debunne gives you certain
|
||||||
|
additional rights, described in the file GPL_EXCEPTION in this package.
|
||||||
|
|
||||||
|
libQGLViewer uses dual licensing. Commercial/proprietary software must
|
||||||
|
purchase a libQGLViewer Commercial License.
|
||||||
|
|
||||||
|
This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
|
||||||
|
WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
|
||||||
|
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
#include <QDomElement>
|
||||||
|
#include <QString>
|
||||||
|
#include <QStringList>
|
||||||
|
#include <QColor>
|
||||||
|
|
||||||
|
#include <math.h>
|
||||||
|
|
||||||
|
#ifndef DOXYGEN
|
||||||
|
|
||||||
|
// QDomElement loading with syntax checking.
|
||||||
|
class DomUtils
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
static void warning(const QString& message)
|
||||||
|
{
|
||||||
|
qWarning("%s", message.toLatin1().constData());
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
static qreal qrealFromDom(const QDomElement& e, const QString& attribute, qreal defValue)
|
||||||
|
{
|
||||||
|
qreal value = defValue;
|
||||||
|
if (e.hasAttribute(attribute)) {
|
||||||
|
const QString s = e.attribute(attribute);
|
||||||
|
bool ok;
|
||||||
|
value = s.toDouble(&ok);
|
||||||
|
if (!ok) {
|
||||||
|
warning(QString("'%1' is not a valid qreal syntax for attribute \"%2\" in initialization of \"%3\". Setting value to %4.")
|
||||||
|
.arg(s).arg(attribute).arg(e.tagName()).arg(QString::number(defValue)));
|
||||||
|
value = defValue;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
warning(QString("\"%1\" attribute missing in initialization of \"%2\". Setting value to %3.")
|
||||||
|
.arg(attribute).arg(e.tagName()).arg(QString::number(value)));
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined(isnan)
|
||||||
|
// The "isnan" method may not be available on all platforms.
|
||||||
|
// Find its equivalent or simply remove these two lines
|
||||||
|
if (isnan(value))
|
||||||
|
warning(QString("Warning, attribute \"%1\" initialized to Not a Number in \"%2\"")
|
||||||
|
.arg(attribute).arg(e.tagName()));
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int intFromDom(const QDomElement& e, const QString& attribute, int defValue)
|
||||||
|
{
|
||||||
|
int value = defValue;
|
||||||
|
if (e.hasAttribute(attribute))
|
||||||
|
{
|
||||||
|
const QString s = e.attribute(attribute);
|
||||||
|
bool ok;
|
||||||
|
value = s.toInt(&ok);
|
||||||
|
if (!ok) {
|
||||||
|
warning(QString("'%1' is not a valid integer syntax for attribute \"%2\" in initialization of \"%3\". Setting value to %4.")
|
||||||
|
.arg(s).arg(attribute).arg(e.tagName()).arg(QString::number(defValue)));
|
||||||
|
value = defValue;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
warning(QString("\"%1\" attribute missing in initialization of \"%2\". Setting value to %3.")
|
||||||
|
.arg(attribute).arg(e.tagName()).arg(QString::number(value)));
|
||||||
|
}
|
||||||
|
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
static unsigned int uintFromDom(const QDomElement& e, const QString& attribute, unsigned int defValue)
|
||||||
|
{
|
||||||
|
unsigned int value = defValue;
|
||||||
|
if (e.hasAttribute(attribute))
|
||||||
|
{
|
||||||
|
const QString s = e.attribute(attribute);
|
||||||
|
bool ok;
|
||||||
|
value = s.toUInt(&ok);
|
||||||
|
if (!ok) {
|
||||||
|
warning(QString("'%1' is not a valid unsigned integer syntax for attribute \"%2\" in initialization of \"%3\". Setting value to %4.")
|
||||||
|
.arg(s).arg(attribute).arg(e.tagName()).arg(QString::number(defValue)));
|
||||||
|
value = defValue;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
warning(QString("\"%1\" attribute missing in initialization of \"%2\". Setting value to %3.")
|
||||||
|
.arg(attribute).arg(e.tagName()).arg(QString::number(value)));
|
||||||
|
}
|
||||||
|
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool boolFromDom(const QDomElement& e, const QString& attribute, bool defValue)
|
||||||
|
{
|
||||||
|
bool value = defValue;
|
||||||
|
if (e.hasAttribute(attribute))
|
||||||
|
{
|
||||||
|
const QString s = e.attribute(attribute);
|
||||||
|
if (s.toLower() == QString("true"))
|
||||||
|
value = true;
|
||||||
|
else if (s.toLower() == QString("false"))
|
||||||
|
value = false;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
warning(QString("'%1' is not a valid boolean syntax for attribute \"%2\" in initialization of \"%3\". Setting value to %4.")
|
||||||
|
.arg(s).arg(attribute).arg(e.tagName()).arg(defValue?"true":"false"));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
warning(QString("\"%1\" attribute missing in initialization of \"%2\". Setting value to %3.")
|
||||||
|
.arg(attribute).arg(e.tagName()).arg(value?"true":"false"));
|
||||||
|
}
|
||||||
|
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void setBoolAttribute(QDomElement& element, const QString& attribute, bool value) {
|
||||||
|
element.setAttribute(attribute, (value ? "true" : "false"));
|
||||||
|
}
|
||||||
|
|
||||||
|
static QDomElement QColorDomElement(const QColor& color, const QString& name, QDomDocument& doc)
|
||||||
|
{
|
||||||
|
QDomElement de = doc.createElement(name);
|
||||||
|
de.setAttribute("red", QString::number(color.red()));
|
||||||
|
de.setAttribute("green", QString::number(color.green()));
|
||||||
|
de.setAttribute("blue", QString::number(color.blue()));
|
||||||
|
return de;
|
||||||
|
}
|
||||||
|
|
||||||
|
static QColor QColorFromDom(const QDomElement& e)
|
||||||
|
{
|
||||||
|
int color[3];
|
||||||
|
QStringList attribute;
|
||||||
|
attribute << "red" << "green" << "blue";
|
||||||
|
for (int i=0; i<attribute.count(); ++i)
|
||||||
|
color[i] = DomUtils::intFromDom(e, attribute[i], 0);
|
||||||
|
return QColor(color[0], color[1], color[2]);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // DOXYGEN
|
||||||
1144
QGLViewer/frame.cpp
Normal file
1144
QGLViewer/frame.cpp
Normal file
File diff suppressed because it is too large
Load Diff
415
QGLViewer/frame.h
Normal file
415
QGLViewer/frame.h
Normal file
@ -0,0 +1,415 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
|
||||||
|
Copyright (C) 2002-2014 Gilles Debunne. All rights reserved.
|
||||||
|
|
||||||
|
This file is part of the QGLViewer library version 2.6.3.
|
||||||
|
|
||||||
|
http://www.libqglviewer.com - contact@libqglviewer.com
|
||||||
|
|
||||||
|
This file may be used under the terms of the GNU General Public License
|
||||||
|
versions 2.0 or 3.0 as published by the Free Software Foundation and
|
||||||
|
appearing in the LICENSE file included in the packaging of this file.
|
||||||
|
In addition, as a special exception, Gilles Debunne gives you certain
|
||||||
|
additional rights, described in the file GPL_EXCEPTION in this package.
|
||||||
|
|
||||||
|
libQGLViewer uses dual licensing. Commercial/proprietary software must
|
||||||
|
purchase a libQGLViewer Commercial License.
|
||||||
|
|
||||||
|
This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
|
||||||
|
WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
|
||||||
|
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
#ifndef QGLVIEWER_FRAME_H
|
||||||
|
#define QGLVIEWER_FRAME_H
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
#include <QString>
|
||||||
|
|
||||||
|
#include "constraint.h"
|
||||||
|
// #include "GL/gl.h" is now included in config.h for ease of configuration
|
||||||
|
|
||||||
|
namespace qglviewer {
|
||||||
|
/*! \brief The Frame class represents a coordinate system, defined by a position and an
|
||||||
|
orientation. \class Frame frame.h QGLViewer/frame.h
|
||||||
|
|
||||||
|
A Frame is a 3D coordinate system, represented by a position() and an orientation(). The order of
|
||||||
|
these transformations is important: the Frame is first translated \e and \e then rotated around
|
||||||
|
the new translated origin.
|
||||||
|
|
||||||
|
A Frame is useful to define the position and orientation of a 3D rigid object, using its matrix()
|
||||||
|
method, as shown below:
|
||||||
|
\code
|
||||||
|
// Builds a Frame at position (0.5,0,0) and oriented such that its Y axis is along the (1,1,1)
|
||||||
|
// direction. One could also have used setPosition() and setOrientation().
|
||||||
|
Frame fr(Vec(0.5,0,0), Quaternion(Vec(0,1,0), Vec(1,1,1)));
|
||||||
|
glPushMatrix();
|
||||||
|
glMultMatrixd(fr.matrix());
|
||||||
|
// Draw your object here, in the local fr coordinate system.
|
||||||
|
glPopMatrix();
|
||||||
|
\endcode
|
||||||
|
|
||||||
|
Many functions are provided to transform a 3D point from one coordinate system (Frame) to an
|
||||||
|
other: see coordinatesOf(), inverseCoordinatesOf(), coordinatesOfIn(), coordinatesOfFrom()...
|
||||||
|
|
||||||
|
You may also want to transform a 3D vector (such as a normal), which corresponds to applying only
|
||||||
|
the rotational part of the frame transformation: see transformOf() and inverseTransformOf(). See
|
||||||
|
the <a href="../examples/frameTransform.html">frameTransform example</a> for an illustration.
|
||||||
|
|
||||||
|
The translation() and the rotation() that are encapsulated in a Frame can also be used to
|
||||||
|
represent a \e rigid \e transformation of space. Such a transformation can also be interpreted as
|
||||||
|
a change of coordinate system, and the coordinate system conversion functions actually allow you
|
||||||
|
to use a Frame as a rigid transformation. Use inverseCoordinatesOf() (resp. coordinatesOf()) to
|
||||||
|
apply the transformation (resp. its inverse). Note the inversion.
|
||||||
|
|
||||||
|
<h3>Hierarchy of Frames</h3>
|
||||||
|
|
||||||
|
The position and the orientation of a Frame are actually defined with respect to a
|
||||||
|
referenceFrame(). The default referenceFrame() is the world coordinate system (represented by a \c
|
||||||
|
NULL referenceFrame()). If you setReferenceFrame() to a different Frame, you must then
|
||||||
|
differentiate:
|
||||||
|
|
||||||
|
\arg the \e local translation() and rotation(), defined with respect to the referenceFrame(),
|
||||||
|
|
||||||
|
\arg the \e global position() and orientation(), always defined with respect to the world
|
||||||
|
coordinate system.
|
||||||
|
|
||||||
|
A Frame is actually defined by its translation() with respect to its referenceFrame(), and then by
|
||||||
|
a rotation() of the coordinate system around the new translated origin.
|
||||||
|
|
||||||
|
This terminology for \e local (translation() and rotation()) and \e global (position() and
|
||||||
|
orientation()) definitions is used in all the methods' names and should be sufficient to prevent
|
||||||
|
ambiguities. These notions are obviously identical when the referenceFrame() is \c NULL, i.e. when
|
||||||
|
the Frame is defined in the world coordinate system (the one you are in at the beginning of the
|
||||||
|
QGLViewer::draw() method, see the <a href="../introduction.html">introduction page</a>).
|
||||||
|
|
||||||
|
Frames can hence easily be organized in a tree hierarchy, which root is the world coordinate
|
||||||
|
system. A loop in the hierarchy would result in an inconsistent (multiple) Frame definition.
|
||||||
|
settingAsReferenceFrameWillCreateALoop() checks this and prevents setReferenceFrame() from
|
||||||
|
creating such a loop.
|
||||||
|
|
||||||
|
This frame hierarchy is used in methods like coordinatesOfIn(), coordinatesOfFrom()... which allow
|
||||||
|
coordinates (or vector) conversions from a Frame to any other one (including the world coordinate
|
||||||
|
system).
|
||||||
|
|
||||||
|
However, one must note that this hierarchical representation is internal to the Frame classes.
|
||||||
|
When the Frames represent OpenGL coordinates system, one should map this hierarchical
|
||||||
|
representation to the OpenGL GL_MODELVIEW matrix stack. See the matrix() documentation for
|
||||||
|
details.
|
||||||
|
|
||||||
|
<h3>Constraints</h3>
|
||||||
|
|
||||||
|
An interesting feature of Frames is that their displacements can be constrained. When a Constraint
|
||||||
|
is attached to a Frame, it filters the input of translate() and rotate(), and only the resulting
|
||||||
|
filtered motion is applied to the Frame. The default constraint() is \c NULL resulting in no
|
||||||
|
filtering. Use setConstraint() to attach a Constraint to a frame.
|
||||||
|
|
||||||
|
Constraints are especially usefull for the ManipulatedFrame instances, in order to forbid some
|
||||||
|
mouse motions. See the <a href="../examples/constrainedFrame.html">constrainedFrame</a>, <a
|
||||||
|
href="../examples/constrainedCamera.html">constrainedCamera</a> and <a
|
||||||
|
href="../examples/luxo.html">luxo</a> examples for an illustration.
|
||||||
|
|
||||||
|
Classical constraints are provided for convenience (see LocalConstraint, WorldConstraint and
|
||||||
|
CameraConstraint) and new constraints can very easily be implemented.
|
||||||
|
|
||||||
|
<h3>Derived classes</h3>
|
||||||
|
|
||||||
|
The ManipulatedFrame class inherits Frame and implements a mouse motion convertion, so that a
|
||||||
|
Frame (and hence an object) can be manipulated in the scene with the mouse.
|
||||||
|
|
||||||
|
\nosubgrouping */
|
||||||
|
class QGLVIEWER_EXPORT Frame : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
Frame();
|
||||||
|
|
||||||
|
/*! Virtual destructor. Empty. */
|
||||||
|
virtual ~Frame() {}
|
||||||
|
|
||||||
|
Frame(const Frame& frame);
|
||||||
|
Frame& operator=(const Frame& frame);
|
||||||
|
|
||||||
|
Q_SIGNALS:
|
||||||
|
/*! This signal is emitted whenever the position() or the orientation() of the Frame is modified.
|
||||||
|
|
||||||
|
Connect this signal to any object that must be notified:
|
||||||
|
\code
|
||||||
|
QObject::connect(myFrame, SIGNAL(modified()), myObject, SLOT(update()));
|
||||||
|
\endcode
|
||||||
|
Use the QGLViewer::QGLViewerPool() to connect the signal to all the viewers.
|
||||||
|
|
||||||
|
\note If your Frame is part of a Frame hierarchy (see referenceFrame()), a modification of one
|
||||||
|
of the parents of this Frame will \e not emit this signal. Use code like this to change this
|
||||||
|
behavior (you can do this recursively for all the referenceFrame() until the \c NULL world root
|
||||||
|
frame is encountered):
|
||||||
|
\code
|
||||||
|
// Emits the Frame modified() signal when its referenceFrame() is modified().
|
||||||
|
connect(myFrame->referenceFrame(), SIGNAL(modified()), myFrame, SIGNAL(modified()));
|
||||||
|
\endcode
|
||||||
|
|
||||||
|
\attention Connecting this signal to a QGLWidget::update() slot (or a method that calls it) [TODO Update with QOpenGLWidget]
|
||||||
|
will prevent you from modifying the Frame \e inside your QGLViewer::draw() method as it would
|
||||||
|
result in an infinite loop. However, QGLViewer::draw() should not modify the scene.
|
||||||
|
|
||||||
|
\note Note that this signal might be emitted even if the Frame is not actually modified, for
|
||||||
|
instance after a translate(Vec(0,0,0)) or a setPosition(position()). */
|
||||||
|
void modified();
|
||||||
|
|
||||||
|
/*! This signal is emitted when the Frame is interpolated by a KeyFrameInterpolator.
|
||||||
|
|
||||||
|
See the KeyFrameInterpolator documentation for details.
|
||||||
|
|
||||||
|
If a KeyFrameInterpolator is used to successively interpolate several Frames in your scene,
|
||||||
|
connect the KeyFrameInterpolator::interpolated() signal instead (identical, but independent of
|
||||||
|
the interpolated Frame). */
|
||||||
|
void interpolated();
|
||||||
|
|
||||||
|
public:
|
||||||
|
/*! @name World coordinates position and orientation */
|
||||||
|
//@{
|
||||||
|
Frame(const Vec& position, const Quaternion& orientation);
|
||||||
|
|
||||||
|
void setPosition(const Vec& position);
|
||||||
|
void setPosition(qreal x, qreal y, qreal z);
|
||||||
|
void setPositionWithConstraint(Vec& position);
|
||||||
|
|
||||||
|
void setOrientation(const Quaternion& orientation);
|
||||||
|
void setOrientation(qreal q0, qreal q1, qreal q2, qreal q3);
|
||||||
|
void setOrientationWithConstraint(Quaternion& orientation);
|
||||||
|
|
||||||
|
void setPositionAndOrientation(const Vec& position, const Quaternion& orientation);
|
||||||
|
void setPositionAndOrientationWithConstraint(Vec& position, Quaternion& orientation);
|
||||||
|
|
||||||
|
Vec position() const;
|
||||||
|
Quaternion orientation() const;
|
||||||
|
|
||||||
|
void getPosition(qreal& x, qreal& y, qreal& z) const;
|
||||||
|
void getOrientation(qreal& q0, qreal& q1, qreal& q2, qreal& q3) const;
|
||||||
|
//@}
|
||||||
|
|
||||||
|
|
||||||
|
public:
|
||||||
|
/*! @name Local translation and rotation w/r reference Frame */
|
||||||
|
//@{
|
||||||
|
/*! Sets the translation() of the frame, locally defined with respect to the referenceFrame().
|
||||||
|
Emits the modified() signal.
|
||||||
|
|
||||||
|
Use setPosition() to define the world coordinates position(). Use
|
||||||
|
setTranslationWithConstraint() to take into account the potential constraint() of the Frame. */
|
||||||
|
void setTranslation(const Vec& translation) { t_ = translation; Q_EMIT modified(); }
|
||||||
|
void setTranslation(qreal x, qreal y, qreal z);
|
||||||
|
void setTranslationWithConstraint(Vec& translation);
|
||||||
|
|
||||||
|
/*! Set the current rotation Quaternion. See rotation() and the different Quaternion
|
||||||
|
constructors. Emits the modified() signal. See also setTranslation() and
|
||||||
|
setRotationWithConstraint(). */
|
||||||
|
|
||||||
|
/*! Sets the rotation() of the Frame, locally defined with respect to the referenceFrame().
|
||||||
|
Emits the modified() signal.
|
||||||
|
|
||||||
|
Use setOrientation() to define the world coordinates orientation(). The potential
|
||||||
|
constraint() of the Frame is not taken into account, use setRotationWithConstraint()
|
||||||
|
instead. */
|
||||||
|
void setRotation(const Quaternion& rotation) { q_ = rotation; Q_EMIT modified(); }
|
||||||
|
void setRotation(qreal q0, qreal q1, qreal q2, qreal q3);
|
||||||
|
void setRotationWithConstraint(Quaternion& rotation);
|
||||||
|
|
||||||
|
void setTranslationAndRotation(const Vec& translation, const Quaternion& rotation);
|
||||||
|
void setTranslationAndRotationWithConstraint(Vec& translation, Quaternion& rotation);
|
||||||
|
|
||||||
|
/*! Returns the Frame translation, defined with respect to the referenceFrame().
|
||||||
|
|
||||||
|
Use position() to get the result in the world coordinates. These two values are identical
|
||||||
|
when the referenceFrame() is \c NULL (default).
|
||||||
|
|
||||||
|
See also setTranslation() and setTranslationWithConstraint(). */
|
||||||
|
Vec translation() const { return t_; }
|
||||||
|
/*! Returns the Frame rotation, defined with respect to the referenceFrame().
|
||||||
|
|
||||||
|
Use orientation() to get the result in the world coordinates. These two values are identical
|
||||||
|
when the referenceFrame() is \c NULL (default).
|
||||||
|
|
||||||
|
See also setRotation() and setRotationWithConstraint(). */
|
||||||
|
|
||||||
|
/*! Returns the current Quaternion orientation. See setRotation(). */
|
||||||
|
Quaternion rotation() const { return q_; }
|
||||||
|
|
||||||
|
void getTranslation(qreal& x, qreal& y, qreal& z) const;
|
||||||
|
void getRotation(qreal& q0, qreal& q1, qreal& q2, qreal& q3) const;
|
||||||
|
//@}
|
||||||
|
|
||||||
|
public:
|
||||||
|
/*! @name Frame hierarchy */
|
||||||
|
//@{
|
||||||
|
/*! Returns the reference Frame, in which coordinates system the Frame is defined.
|
||||||
|
|
||||||
|
The translation() and rotation() of the Frame are defined with respect to the referenceFrame()
|
||||||
|
coordinate system. A \c NULL referenceFrame() (default value) means that the Frame is defined in
|
||||||
|
the world coordinate system.
|
||||||
|
|
||||||
|
Use position() and orientation() to recursively convert values along the referenceFrame() chain
|
||||||
|
and to get values expressed in the world coordinate system. The values match when the
|
||||||
|
referenceFrame() is \c NULL.
|
||||||
|
|
||||||
|
Use setReferenceFrame() to set this value and create a Frame hierarchy. Convenient functions
|
||||||
|
allow you to convert 3D coordinates from one Frame to an other: see coordinatesOf(),
|
||||||
|
localCoordinatesOf(), coordinatesOfIn() and their inverse functions.
|
||||||
|
|
||||||
|
Vectors can also be converted using transformOf(), transformOfIn, localTransformOf() and their
|
||||||
|
inverse functions. */
|
||||||
|
const Frame* referenceFrame() const { return referenceFrame_; }
|
||||||
|
void setReferenceFrame(const Frame* const refFrame);
|
||||||
|
bool settingAsReferenceFrameWillCreateALoop(const Frame* const frame);
|
||||||
|
//@}
|
||||||
|
|
||||||
|
|
||||||
|
/*! @name Frame modification */
|
||||||
|
//@{
|
||||||
|
void translate(Vec& t);
|
||||||
|
void translate(const Vec& t);
|
||||||
|
// Some compilers complain about "overloading cannot distinguish from previous declaration"
|
||||||
|
// Simply comment out the following method and its associated implementation
|
||||||
|
void translate(qreal x, qreal y, qreal z);
|
||||||
|
void translate(qreal& x, qreal& y, qreal& z);
|
||||||
|
|
||||||
|
void rotate(Quaternion& q);
|
||||||
|
void rotate(const Quaternion& q);
|
||||||
|
// Some compilers complain about "overloading cannot distinguish from previous declaration"
|
||||||
|
// Simply comment out the following method and its associated implementation
|
||||||
|
void rotate(qreal q0, qreal q1, qreal q2, qreal q3);
|
||||||
|
void rotate(qreal& q0, qreal& q1, qreal& q2, qreal& q3);
|
||||||
|
|
||||||
|
void rotateAroundPoint(Quaternion& rotation, const Vec& point);
|
||||||
|
void rotateAroundPoint(const Quaternion& rotation, const Vec& point);
|
||||||
|
|
||||||
|
void alignWithFrame(const Frame* const frame, bool move=false, qreal threshold=0.0);
|
||||||
|
void projectOnLine(const Vec& origin, const Vec& direction);
|
||||||
|
//@}
|
||||||
|
|
||||||
|
|
||||||
|
/*! @name Coordinate system transformation of 3D coordinates */
|
||||||
|
//@{
|
||||||
|
Vec coordinatesOf(const Vec& src) const;
|
||||||
|
Vec inverseCoordinatesOf(const Vec& src) const;
|
||||||
|
Vec localCoordinatesOf(const Vec& src) const;
|
||||||
|
Vec localInverseCoordinatesOf(const Vec& src) const;
|
||||||
|
Vec coordinatesOfIn(const Vec& src, const Frame* const in) const;
|
||||||
|
Vec coordinatesOfFrom(const Vec& src, const Frame* const from) const;
|
||||||
|
|
||||||
|
void getCoordinatesOf(const qreal src[3], qreal res[3]) const;
|
||||||
|
void getInverseCoordinatesOf(const qreal src[3], qreal res[3]) const;
|
||||||
|
void getLocalCoordinatesOf(const qreal src[3], qreal res[3]) const;
|
||||||
|
void getLocalInverseCoordinatesOf(const qreal src[3], qreal res[3]) const;
|
||||||
|
void getCoordinatesOfIn(const qreal src[3], qreal res[3], const Frame* const in) const;
|
||||||
|
void getCoordinatesOfFrom(const qreal src[3], qreal res[3], const Frame* const from) const;
|
||||||
|
//@}
|
||||||
|
|
||||||
|
/*! @name Coordinate system transformation of vectors */
|
||||||
|
// A frame is as a new coordinate system, defined with respect to a reference frame (the world
|
||||||
|
// coordinate system by default, see the "Composition of frame" section).
|
||||||
|
|
||||||
|
// The transformOf() (resp. inverseTransformOf()) functions transform a 3D vector from (resp.
|
||||||
|
// to) the world coordinates system. This section defines the 3D vector transformation
|
||||||
|
// functions. See the Coordinate system transformation of 3D points above for the transformation
|
||||||
|
// of 3D points. The difference between the two sets of functions is simple: for vectors, only
|
||||||
|
// the rotational part of the transformations is taken into account, while translation is also
|
||||||
|
// considered for 3D points.
|
||||||
|
|
||||||
|
// The length of the resulting transformed vector is identical to the one of the source vector
|
||||||
|
// for all the described functions.
|
||||||
|
|
||||||
|
// When local is prepended to the names of the functions, the functions simply transform from
|
||||||
|
// (and to) the reference frame.
|
||||||
|
|
||||||
|
// When In (resp. From) is appended to the names, the functions transform from (resp. To) the
|
||||||
|
// frame that is given as an argument. The frame does not need to be in the same branch or the
|
||||||
|
// hierarchical tree, and can be \c NULL (the world coordinates system).
|
||||||
|
|
||||||
|
// Combining any of these functions with its inverse (in any order) leads to the identity.
|
||||||
|
//@{
|
||||||
|
Vec transformOf(const Vec& src) const;
|
||||||
|
Vec inverseTransformOf(const Vec& src) const;
|
||||||
|
Vec localTransformOf(const Vec& src) const;
|
||||||
|
Vec localInverseTransformOf(const Vec& src) const;
|
||||||
|
Vec transformOfIn(const Vec& src, const Frame* const in) const;
|
||||||
|
Vec transformOfFrom(const Vec& src, const Frame* const from) const;
|
||||||
|
|
||||||
|
void getTransformOf(const qreal src[3], qreal res[3]) const;
|
||||||
|
void getInverseTransformOf(const qreal src[3], qreal res[3]) const;
|
||||||
|
void getLocalTransformOf(const qreal src[3], qreal res[3]) const;
|
||||||
|
void getLocalInverseTransformOf(const qreal src[3], qreal res[3]) const;
|
||||||
|
void getTransformOfIn(const qreal src[3], qreal res[3], const Frame* const in) const;
|
||||||
|
void getTransformOfFrom(const qreal src[3], qreal res[3], const Frame* const from) const;
|
||||||
|
//@}
|
||||||
|
|
||||||
|
|
||||||
|
/*! @name Constraint on the displacement */
|
||||||
|
//@{
|
||||||
|
/*! Returns the current constraint applied to the Frame.
|
||||||
|
|
||||||
|
A \c NULL value (default) means that no Constraint is used to filter Frame translation and
|
||||||
|
rotation. See the Constraint class documentation for details.
|
||||||
|
|
||||||
|
You may have to use a \c dynamic_cast to convert the result to a Constraint derived class. */
|
||||||
|
Constraint* constraint() const { return constraint_; }
|
||||||
|
/*! Sets the constraint() attached to the Frame.
|
||||||
|
|
||||||
|
A \c NULL value means no constraint. The previous constraint() should be deleted by the calling
|
||||||
|
method if needed. */
|
||||||
|
void setConstraint(Constraint* const constraint) { constraint_ = constraint; }
|
||||||
|
//@}
|
||||||
|
|
||||||
|
/*! @name Associated matrices */
|
||||||
|
//@{
|
||||||
|
public:
|
||||||
|
const GLdouble* matrix() const;
|
||||||
|
void getMatrix(GLdouble m[4][4]) const;
|
||||||
|
void getMatrix(GLdouble m[16]) const;
|
||||||
|
|
||||||
|
const GLdouble* worldMatrix() const;
|
||||||
|
void getWorldMatrix(GLdouble m[4][4]) const;
|
||||||
|
void getWorldMatrix(GLdouble m[16]) const;
|
||||||
|
|
||||||
|
void setFromMatrix(const GLdouble m[4][4]);
|
||||||
|
void setFromMatrix(const GLdouble m[16]);
|
||||||
|
//@}
|
||||||
|
|
||||||
|
/*! @name Inversion of the transformation */
|
||||||
|
//@{
|
||||||
|
Frame inverse() const;
|
||||||
|
/*! Returns the inverse() of the Frame world transformation.
|
||||||
|
|
||||||
|
The orientation() of the new Frame is the Quaternion::inverse() of the original orientation.
|
||||||
|
Its position() is the negated and inverse rotated image of the original position.
|
||||||
|
|
||||||
|
The result Frame has a \c NULL referenceFrame() and a \c NULL constraint().
|
||||||
|
|
||||||
|
Use inverse() for a local (i.e. with respect to referenceFrame()) transformation inverse. */
|
||||||
|
Frame worldInverse() const { return Frame(-(orientation().inverseRotate(position())), orientation().inverse()); }
|
||||||
|
//@}
|
||||||
|
|
||||||
|
/*! @name XML representation */
|
||||||
|
//@{
|
||||||
|
public:
|
||||||
|
virtual QDomElement domElement(const QString& name, QDomDocument& document) const;
|
||||||
|
public Q_SLOTS:
|
||||||
|
virtual void initFromDOMElement(const QDomElement& element);
|
||||||
|
//@}
|
||||||
|
|
||||||
|
private:
|
||||||
|
// P o s i t i o n a n d o r i e n t a t i o n
|
||||||
|
Vec t_;
|
||||||
|
Quaternion q_;
|
||||||
|
|
||||||
|
// C o n s t r a i n t s
|
||||||
|
Constraint* constraint_;
|
||||||
|
|
||||||
|
// F r a m e c o m p o s i t i o n
|
||||||
|
const Frame* referenceFrame_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace qglviewer
|
||||||
|
|
||||||
|
#endif // QGLVIEWER_FRAME_H
|
||||||
549
QGLViewer/keyFrameInterpolator.cpp
Normal file
549
QGLViewer/keyFrameInterpolator.cpp
Normal file
@ -0,0 +1,549 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
|
||||||
|
Copyright (C) 2002-2014 Gilles Debunne. All rights reserved.
|
||||||
|
|
||||||
|
This file is part of the QGLViewer library version 2.6.3.
|
||||||
|
|
||||||
|
http://www.libqglviewer.com - contact@libqglviewer.com
|
||||||
|
|
||||||
|
This file may be used under the terms of the GNU General Public License
|
||||||
|
versions 2.0 or 3.0 as published by the Free Software Foundation and
|
||||||
|
appearing in the LICENSE file included in the packaging of this file.
|
||||||
|
In addition, as a special exception, Gilles Debunne gives you certain
|
||||||
|
additional rights, described in the file GPL_EXCEPTION in this package.
|
||||||
|
|
||||||
|
libQGLViewer uses dual licensing. Commercial/proprietary software must
|
||||||
|
purchase a libQGLViewer Commercial License.
|
||||||
|
|
||||||
|
This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
|
||||||
|
WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
|
||||||
|
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
#include "domUtils.h"
|
||||||
|
#include "qglviewer.h" // for QGLViewer::drawAxis and Camera::drawCamera
|
||||||
|
|
||||||
|
using namespace qglviewer;
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
/*! Creates a KeyFrameInterpolator, with \p frame as associated frame().
|
||||||
|
|
||||||
|
The frame() can be set or changed using setFrame().
|
||||||
|
|
||||||
|
interpolationTime(), interpolationSpeed() and interpolationPeriod() are set to their default
|
||||||
|
values. */
|
||||||
|
KeyFrameInterpolator::KeyFrameInterpolator(Frame* frame)
|
||||||
|
: frame_(NULL), period_(40), interpolationTime_(0.0), interpolationSpeed_(1.0), interpolationStarted_(false),
|
||||||
|
closedPath_(false), loopInterpolation_(false), pathIsValid_(false), valuesAreValid_(true), currentFrameValid_(false)
|
||||||
|
// #CONNECTION# Values cut pasted initFromDOMElement()
|
||||||
|
{
|
||||||
|
setFrame(frame);
|
||||||
|
for (int i=0; i<4; ++i)
|
||||||
|
currentFrame_[i] = new QMutableListIterator<KeyFrame*>(keyFrame_);
|
||||||
|
connect(&timer_, SIGNAL(timeout()), SLOT(update()));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Virtual destructor. Clears the keyFrame path. */
|
||||||
|
KeyFrameInterpolator::~KeyFrameInterpolator()
|
||||||
|
{
|
||||||
|
deletePath();
|
||||||
|
for (int i=0; i<4; ++i)
|
||||||
|
delete currentFrame_[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Sets the frame() associated to the KeyFrameInterpolator. */
|
||||||
|
void KeyFrameInterpolator::setFrame(Frame* const frame)
|
||||||
|
{
|
||||||
|
if (this->frame())
|
||||||
|
disconnect(this, SIGNAL( interpolated() ), this->frame(), SIGNAL( interpolated() ));
|
||||||
|
|
||||||
|
frame_ = frame;
|
||||||
|
|
||||||
|
if (this->frame())
|
||||||
|
connect(this, SIGNAL( interpolated() ), this->frame(), SIGNAL( interpolated() ));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Updates frame() state according to current interpolationTime(). Then adds
|
||||||
|
interpolationPeriod()*interpolationSpeed() to interpolationTime().
|
||||||
|
|
||||||
|
This internal method is called by a timer when interpolationIsStarted(). It can be used for
|
||||||
|
debugging purpose. stopInterpolation() is called when interpolationTime() reaches firstTime() or
|
||||||
|
lastTime(), unless loopInterpolation() is \c true. */
|
||||||
|
void KeyFrameInterpolator::update()
|
||||||
|
{
|
||||||
|
interpolateAtTime(interpolationTime());
|
||||||
|
|
||||||
|
interpolationTime_ += interpolationSpeed() * interpolationPeriod() / 1000.0;
|
||||||
|
|
||||||
|
if (interpolationTime() > keyFrame_.last()->time())
|
||||||
|
{
|
||||||
|
if (loopInterpolation())
|
||||||
|
setInterpolationTime(keyFrame_.first()->time() + interpolationTime_ - keyFrame_.last()->time());
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Make sure last KeyFrame is reached and displayed
|
||||||
|
interpolateAtTime(keyFrame_.last()->time());
|
||||||
|
stopInterpolation();
|
||||||
|
}
|
||||||
|
Q_EMIT endReached();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
if (interpolationTime() < keyFrame_.first()->time())
|
||||||
|
{
|
||||||
|
if (loopInterpolation())
|
||||||
|
setInterpolationTime(keyFrame_.last()->time() - keyFrame_.first()->time() + interpolationTime_);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Make sure first KeyFrame is reached and displayed
|
||||||
|
interpolateAtTime(keyFrame_.first()->time());
|
||||||
|
stopInterpolation();
|
||||||
|
}
|
||||||
|
Q_EMIT endReached();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*! Starts the interpolation process.
|
||||||
|
|
||||||
|
A timer is started with an interpolationPeriod() period that updates the frame()'s position and
|
||||||
|
orientation. interpolationIsStarted() will return \c true until stopInterpolation() or
|
||||||
|
toggleInterpolation() is called.
|
||||||
|
|
||||||
|
If \p period is positive, it is set as the new interpolationPeriod(). The previous
|
||||||
|
interpolationPeriod() is used otherwise (default).
|
||||||
|
|
||||||
|
If interpolationTime() is larger than lastTime(), interpolationTime() is reset to firstTime()
|
||||||
|
before interpolation starts (and inversely for negative interpolationSpeed()).
|
||||||
|
|
||||||
|
Use setInterpolationTime() before calling this method to change the starting interpolationTime().
|
||||||
|
|
||||||
|
See the <a href="../examples/keyFrames.html">keyFrames example</a> for an illustration.
|
||||||
|
|
||||||
|
You may also be interested in QGLViewer::animate() and QGLViewer::startAnimation().
|
||||||
|
|
||||||
|
\attention The keyFrames must be defined (see addKeyFrame()) \e before you startInterpolation(),
|
||||||
|
or else the interpolation will naturally immediately stop. */
|
||||||
|
void KeyFrameInterpolator::startInterpolation(int period)
|
||||||
|
{
|
||||||
|
if (period >= 0)
|
||||||
|
setInterpolationPeriod(period);
|
||||||
|
|
||||||
|
if (!keyFrame_.isEmpty())
|
||||||
|
{
|
||||||
|
if ((interpolationSpeed() > 0.0) && (interpolationTime() >= keyFrame_.last()->time()))
|
||||||
|
setInterpolationTime(keyFrame_.first()->time());
|
||||||
|
if ((interpolationSpeed() < 0.0) && (interpolationTime() <= keyFrame_.first()->time()))
|
||||||
|
setInterpolationTime(keyFrame_.last()->time());
|
||||||
|
timer_.start(interpolationPeriod());
|
||||||
|
interpolationStarted_ = true;
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*! Stops an interpolation started with startInterpolation(). See interpolationIsStarted() and toggleInterpolation(). */
|
||||||
|
void KeyFrameInterpolator::stopInterpolation()
|
||||||
|
{
|
||||||
|
timer_.stop();
|
||||||
|
interpolationStarted_ = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*! Stops the interpolation and resets interpolationTime() to the firstTime().
|
||||||
|
|
||||||
|
If desired, call interpolateAtTime() after this method to actually move the frame() to
|
||||||
|
firstTime(). */
|
||||||
|
void KeyFrameInterpolator::resetInterpolation()
|
||||||
|
{
|
||||||
|
stopInterpolation();
|
||||||
|
setInterpolationTime(firstTime());
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Appends a new keyFrame to the path, with its associated \p time (in seconds).
|
||||||
|
|
||||||
|
The keyFrame is given as a pointer to a Frame, which will be connected to the
|
||||||
|
KeyFrameInterpolator: when \p frame is modified, the KeyFrameInterpolator path is updated
|
||||||
|
accordingly. This allows for dynamic paths, where keyFrame can be edited, even during the
|
||||||
|
interpolation. See the <a href="../examples/keyFrames.html">keyFrames example</a> for an
|
||||||
|
illustration.
|
||||||
|
|
||||||
|
\c NULL \p frame pointers are silently ignored. The keyFrameTime() has to be monotonously
|
||||||
|
increasing over keyFrames.
|
||||||
|
|
||||||
|
Use addKeyFrame(const Frame&, qreal) to add keyFrame by values. */
|
||||||
|
void KeyFrameInterpolator::addKeyFrame(const Frame* const frame, qreal time)
|
||||||
|
{
|
||||||
|
if (!frame)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (keyFrame_.isEmpty())
|
||||||
|
interpolationTime_ = time;
|
||||||
|
|
||||||
|
if ( (!keyFrame_.isEmpty()) && (keyFrame_.last()->time() > time) )
|
||||||
|
qWarning("Error in KeyFrameInterpolator::addKeyFrame: time is not monotone");
|
||||||
|
else
|
||||||
|
keyFrame_.append(new KeyFrame(frame, time));
|
||||||
|
connect(frame, SIGNAL(modified()), SLOT(invalidateValues()));
|
||||||
|
valuesAreValid_ = false;
|
||||||
|
pathIsValid_ = false;
|
||||||
|
currentFrameValid_ = false;
|
||||||
|
resetInterpolation();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Appends a new keyFrame to the path, with its associated \p time (in seconds).
|
||||||
|
|
||||||
|
The path will use the current \p frame state. If you want the path to change when \p frame is
|
||||||
|
modified, you need to pass a \e pointer to the Frame instead (see addKeyFrame(const Frame*,
|
||||||
|
qreal)).
|
||||||
|
|
||||||
|
The keyFrameTime() have to be monotonously increasing over keyFrames. */
|
||||||
|
void KeyFrameInterpolator::addKeyFrame(const Frame& frame, qreal time)
|
||||||
|
{
|
||||||
|
if (keyFrame_.isEmpty())
|
||||||
|
interpolationTime_ = time;
|
||||||
|
|
||||||
|
if ( (!keyFrame_.isEmpty()) && (keyFrame_.last()->time() > time) )
|
||||||
|
qWarning("Error in KeyFrameInterpolator::addKeyFrame: time is not monotone");
|
||||||
|
else
|
||||||
|
keyFrame_.append(new KeyFrame(frame, time));
|
||||||
|
|
||||||
|
valuesAreValid_ = false;
|
||||||
|
pathIsValid_ = false;
|
||||||
|
currentFrameValid_ = false;
|
||||||
|
resetInterpolation();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*! Appends a new keyFrame to the path.
|
||||||
|
|
||||||
|
Same as addKeyFrame(const Frame* frame, qreal), except that the keyFrameTime() is set to the
|
||||||
|
previous keyFrameTime() plus one second (or 0.0 if there is no previous keyFrame). */
|
||||||
|
void KeyFrameInterpolator::addKeyFrame(const Frame* const frame)
|
||||||
|
{
|
||||||
|
qreal time;
|
||||||
|
if (keyFrame_.isEmpty())
|
||||||
|
time = 0.0;
|
||||||
|
else
|
||||||
|
time = lastTime() + 1.0;
|
||||||
|
|
||||||
|
addKeyFrame(frame, time);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Appends a new keyFrame to the path.
|
||||||
|
|
||||||
|
Same as addKeyFrame(const Frame& frame, qreal), except that the keyFrameTime() is automatically set
|
||||||
|
to previous keyFrameTime() plus one second (or 0.0 if there is no previous keyFrame). */
|
||||||
|
void KeyFrameInterpolator::addKeyFrame(const Frame& frame)
|
||||||
|
{
|
||||||
|
qreal time;
|
||||||
|
if (keyFrame_.isEmpty())
|
||||||
|
time = 0.0;
|
||||||
|
else
|
||||||
|
time = keyFrame_.last()->time() + 1.0;
|
||||||
|
|
||||||
|
addKeyFrame(frame, time);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Removes all keyFrames from the path. The numberOfKeyFrames() is set to 0. */
|
||||||
|
void KeyFrameInterpolator::deletePath()
|
||||||
|
{
|
||||||
|
stopInterpolation();
|
||||||
|
qDeleteAll(keyFrame_);
|
||||||
|
keyFrame_.clear();
|
||||||
|
pathIsValid_ = false;
|
||||||
|
valuesAreValid_ = false;
|
||||||
|
currentFrameValid_ = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void KeyFrameInterpolator::updateModifiedFrameValues()
|
||||||
|
{
|
||||||
|
Quaternion prevQ = keyFrame_.first()->orientation();
|
||||||
|
KeyFrame* kf;
|
||||||
|
for (int i=0; i<keyFrame_.size(); ++i)
|
||||||
|
{
|
||||||
|
kf = keyFrame_.at(i);
|
||||||
|
if (kf->frame())
|
||||||
|
kf->updateValuesFromPointer();
|
||||||
|
kf->flipOrientationIfNeeded(prevQ);
|
||||||
|
prevQ = kf->orientation();
|
||||||
|
}
|
||||||
|
|
||||||
|
KeyFrame* prev = keyFrame_.first();
|
||||||
|
kf = keyFrame_.first();
|
||||||
|
int index = 1;
|
||||||
|
while (kf)
|
||||||
|
{
|
||||||
|
KeyFrame* next = (index < keyFrame_.size()) ? keyFrame_.at(index) : NULL;
|
||||||
|
index++;
|
||||||
|
if (next)
|
||||||
|
kf->computeTangent(prev, next);
|
||||||
|
else
|
||||||
|
kf->computeTangent(prev, kf);
|
||||||
|
prev = kf;
|
||||||
|
kf = next;
|
||||||
|
}
|
||||||
|
valuesAreValid_ = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Returns the Frame associated with the keyFrame at index \p index.
|
||||||
|
|
||||||
|
See also keyFrameTime(). \p index has to be in the range 0..numberOfKeyFrames()-1.
|
||||||
|
|
||||||
|
\note If this keyFrame was defined using a pointer to a Frame (see addKeyFrame(const Frame*
|
||||||
|
const)), the \e current pointed Frame state is returned. */
|
||||||
|
Frame KeyFrameInterpolator::keyFrame(int index) const
|
||||||
|
{
|
||||||
|
const KeyFrame* const kf = keyFrame_.at(index);
|
||||||
|
return Frame(kf->position(), kf->orientation());
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Returns the time corresponding to the \p index keyFrame.
|
||||||
|
|
||||||
|
See also keyFrame(). \p index has to be in the range 0..numberOfKeyFrames()-1. */
|
||||||
|
qreal KeyFrameInterpolator::keyFrameTime(int index) const
|
||||||
|
{
|
||||||
|
return keyFrame_.at(index)->time();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Returns the duration of the KeyFrameInterpolator path, expressed in seconds.
|
||||||
|
|
||||||
|
Simply corresponds to lastTime() - firstTime(). Returns 0.0 if the path has less than 2 keyFrames.
|
||||||
|
See also keyFrameTime(). */
|
||||||
|
qreal KeyFrameInterpolator::duration() const
|
||||||
|
{
|
||||||
|
return lastTime() - firstTime();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Returns the time corresponding to the first keyFrame, expressed in seconds.
|
||||||
|
|
||||||
|
Returns 0.0 if the path is empty. See also lastTime(), duration() and keyFrameTime(). */
|
||||||
|
qreal KeyFrameInterpolator::firstTime() const
|
||||||
|
{
|
||||||
|
if (keyFrame_.isEmpty())
|
||||||
|
return 0.0;
|
||||||
|
else
|
||||||
|
return keyFrame_.first()->time();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Returns the time corresponding to the last keyFrame, expressed in seconds.
|
||||||
|
|
||||||
|
Returns 0.0 if the path is empty. See also firstTime(), duration() and keyFrameTime(). */
|
||||||
|
qreal KeyFrameInterpolator::lastTime() const
|
||||||
|
{
|
||||||
|
if (keyFrame_.isEmpty())
|
||||||
|
return 0.0;
|
||||||
|
else
|
||||||
|
return keyFrame_.last()->time();
|
||||||
|
}
|
||||||
|
|
||||||
|
void KeyFrameInterpolator::updateCurrentKeyFrameForTime(qreal time)
|
||||||
|
{
|
||||||
|
// Assertion: times are sorted in monotone order.
|
||||||
|
// Assertion: keyFrame_ is not empty
|
||||||
|
|
||||||
|
// TODO: Special case for loops when closed path is implemented !!
|
||||||
|
if (!currentFrameValid_)
|
||||||
|
// Recompute everything from scrach
|
||||||
|
currentFrame_[1]->toFront();
|
||||||
|
|
||||||
|
while (currentFrame_[1]->peekNext()->time() > time)
|
||||||
|
{
|
||||||
|
currentFrameValid_ = false;
|
||||||
|
if (!currentFrame_[1]->hasPrevious())
|
||||||
|
break;
|
||||||
|
currentFrame_[1]->previous();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!currentFrameValid_)
|
||||||
|
*currentFrame_[2] = *currentFrame_[1];
|
||||||
|
|
||||||
|
while (currentFrame_[2]->peekNext()->time() < time)
|
||||||
|
{
|
||||||
|
currentFrameValid_ = false;
|
||||||
|
if (!currentFrame_[2]->hasNext())
|
||||||
|
break;
|
||||||
|
currentFrame_[2]->next();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!currentFrameValid_)
|
||||||
|
{
|
||||||
|
*currentFrame_[1] = *currentFrame_[2];
|
||||||
|
if ((currentFrame_[1]->hasPrevious()) && (time < currentFrame_[2]->peekNext()->time()))
|
||||||
|
currentFrame_[1]->previous();
|
||||||
|
|
||||||
|
*currentFrame_[0] = *currentFrame_[1];
|
||||||
|
if (currentFrame_[0]->hasPrevious())
|
||||||
|
currentFrame_[0]->previous();
|
||||||
|
|
||||||
|
*currentFrame_[3] = *currentFrame_[2];
|
||||||
|
if (currentFrame_[3]->hasNext())
|
||||||
|
currentFrame_[3]->next();
|
||||||
|
|
||||||
|
currentFrameValid_ = true;
|
||||||
|
splineCacheIsValid_ = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// cout << "Time = " << time << " : " << currentFrame_[0]->peekNext()->time() << " , " <<
|
||||||
|
// currentFrame_[1]->peekNext()->time() << " , " << currentFrame_[2]->peekNext()->time() << " , " << currentFrame_[3]->peekNext()->time() << endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
void KeyFrameInterpolator::updateSplineCache()
|
||||||
|
{
|
||||||
|
Vec delta = currentFrame_[2]->peekNext()->position() - currentFrame_[1]->peekNext()->position();
|
||||||
|
v1 = 3.0 * delta - 2.0 * currentFrame_[1]->peekNext()->tgP() - currentFrame_[2]->peekNext()->tgP();
|
||||||
|
v2 = -2.0 * delta + currentFrame_[1]->peekNext()->tgP() + currentFrame_[2]->peekNext()->tgP();
|
||||||
|
splineCacheIsValid_ = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Interpolate frame() at time \p time (expressed in seconds). interpolationTime() is set to \p
|
||||||
|
time and frame() is set accordingly.
|
||||||
|
|
||||||
|
If you simply want to change interpolationTime() but not the frame() state, use
|
||||||
|
setInterpolationTime() instead.
|
||||||
|
|
||||||
|
Emits the interpolated() signal and makes the frame() emit the Frame::interpolated() signal. */
|
||||||
|
void KeyFrameInterpolator::interpolateAtTime(qreal time)
|
||||||
|
{
|
||||||
|
setInterpolationTime(time);
|
||||||
|
|
||||||
|
if ((keyFrame_.isEmpty()) || (!frame()))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!valuesAreValid_)
|
||||||
|
updateModifiedFrameValues();
|
||||||
|
|
||||||
|
updateCurrentKeyFrameForTime(time);
|
||||||
|
|
||||||
|
if (!splineCacheIsValid_)
|
||||||
|
updateSplineCache();
|
||||||
|
|
||||||
|
qreal alpha;
|
||||||
|
qreal dt = currentFrame_[2]->peekNext()->time() - currentFrame_[1]->peekNext()->time();
|
||||||
|
if (dt == 0.0)
|
||||||
|
alpha = 0.0;
|
||||||
|
else
|
||||||
|
alpha = (time - currentFrame_[1]->peekNext()->time()) / dt;
|
||||||
|
|
||||||
|
// Linear interpolation - debug
|
||||||
|
// Vec pos = alpha*(currentFrame_[2]->peekNext()->position()) + (1.0-alpha)*(currentFrame_[1]->peekNext()->position());
|
||||||
|
Vec pos = currentFrame_[1]->peekNext()->position() + alpha * (currentFrame_[1]->peekNext()->tgP() + alpha * (v1+alpha*v2));
|
||||||
|
Quaternion q = Quaternion::squad(currentFrame_[1]->peekNext()->orientation(), currentFrame_[1]->peekNext()->tgQ(),
|
||||||
|
currentFrame_[2]->peekNext()->tgQ(), currentFrame_[2]->peekNext()->orientation(), alpha);
|
||||||
|
frame()->setPositionAndOrientationWithConstraint(pos, q);
|
||||||
|
|
||||||
|
Q_EMIT interpolated();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Returns an XML \c QDomElement that represents the KeyFrameInterpolator.
|
||||||
|
|
||||||
|
The resulting QDomElement holds the KeyFrameInterpolator parameters as well as the path keyFrames
|
||||||
|
(if the keyFrame is defined by a pointer to a Frame, use its current value).
|
||||||
|
|
||||||
|
\p name is the name of the QDomElement tag. \p doc is the \c QDomDocument factory used to create
|
||||||
|
QDomElement.
|
||||||
|
|
||||||
|
Use initFromDOMElement() to restore the ManipulatedFrame state from the resulting QDomElement.
|
||||||
|
|
||||||
|
See Vec::domElement() for a complete example. See also Quaternion::domElement(),
|
||||||
|
Camera::domElement()...
|
||||||
|
|
||||||
|
Note that the Camera::keyFrameInterpolator() are automatically saved by QGLViewer::saveStateToFile()
|
||||||
|
when a QGLViewer is closed. */
|
||||||
|
QDomElement KeyFrameInterpolator::domElement(const QString& name, QDomDocument& document) const
|
||||||
|
{
|
||||||
|
QDomElement de = document.createElement(name);
|
||||||
|
int count = 0;
|
||||||
|
Q_FOREACH (KeyFrame* kf, keyFrame_)
|
||||||
|
{
|
||||||
|
Frame fr(kf->position(), kf->orientation());
|
||||||
|
QDomElement kfNode = fr.domElement("KeyFrame", document);
|
||||||
|
kfNode.setAttribute("index", QString::number(count));
|
||||||
|
kfNode.setAttribute("time", QString::number(kf->time()));
|
||||||
|
de.appendChild(kfNode);
|
||||||
|
++count;
|
||||||
|
}
|
||||||
|
de.setAttribute("nbKF", QString::number(keyFrame_.count()));
|
||||||
|
de.setAttribute("time", QString::number(interpolationTime()));
|
||||||
|
de.setAttribute("speed", QString::number(interpolationSpeed()));
|
||||||
|
de.setAttribute("period", QString::number(interpolationPeriod()));
|
||||||
|
DomUtils::setBoolAttribute(de, "closedPath", closedPath());
|
||||||
|
DomUtils::setBoolAttribute(de, "loop", loopInterpolation());
|
||||||
|
return de;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Restores the KeyFrameInterpolator state from a \c QDomElement created by domElement().
|
||||||
|
|
||||||
|
Note that the frame() pointer is not included in the domElement(): you need to setFrame() after
|
||||||
|
this method to attach a Frame to the KeyFrameInterpolator.
|
||||||
|
|
||||||
|
See Vec::initFromDOMElement() for a complete code example.
|
||||||
|
|
||||||
|
See also Camera::initFromDOMElement() and Frame::initFromDOMElement(). */
|
||||||
|
void KeyFrameInterpolator::initFromDOMElement(const QDomElement& element)
|
||||||
|
{
|
||||||
|
qDeleteAll(keyFrame_);
|
||||||
|
keyFrame_.clear();
|
||||||
|
QDomElement child=element.firstChild().toElement();
|
||||||
|
while (!child.isNull())
|
||||||
|
{
|
||||||
|
if (child.tagName() == "KeyFrame")
|
||||||
|
{
|
||||||
|
Frame fr;
|
||||||
|
fr.initFromDOMElement(child);
|
||||||
|
qreal time = DomUtils::qrealFromDom(child, "time", 0.0);
|
||||||
|
addKeyFrame(fr, time);
|
||||||
|
}
|
||||||
|
|
||||||
|
child = child.nextSibling().toElement();
|
||||||
|
}
|
||||||
|
|
||||||
|
// #CONNECTION# Values cut pasted from constructor
|
||||||
|
setInterpolationTime(DomUtils::qrealFromDom(element, "time", 0.0));
|
||||||
|
setInterpolationSpeed(DomUtils::qrealFromDom(element, "speed", 1.0));
|
||||||
|
setInterpolationPeriod(DomUtils::intFromDom(element, "period", 40));
|
||||||
|
setClosedPath(DomUtils::boolFromDom(element, "closedPath", false));
|
||||||
|
setLoopInterpolation(DomUtils::boolFromDom(element, "loop", false));
|
||||||
|
|
||||||
|
// setFrame(NULL);
|
||||||
|
pathIsValid_ = false;
|
||||||
|
valuesAreValid_ = false;
|
||||||
|
currentFrameValid_ = false;
|
||||||
|
|
||||||
|
stopInterpolation();
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifndef DOXYGEN
|
||||||
|
|
||||||
|
//////////// KeyFrame private class implementation /////////
|
||||||
|
KeyFrameInterpolator::KeyFrame::KeyFrame(const Frame& fr, qreal t)
|
||||||
|
: time_(t), frame_(NULL)
|
||||||
|
{
|
||||||
|
p_ = fr.position();
|
||||||
|
q_ = fr.orientation();
|
||||||
|
}
|
||||||
|
|
||||||
|
KeyFrameInterpolator::KeyFrame::KeyFrame(const Frame* fr, qreal t)
|
||||||
|
: time_(t), frame_(fr)
|
||||||
|
{
|
||||||
|
updateValuesFromPointer();
|
||||||
|
}
|
||||||
|
|
||||||
|
void KeyFrameInterpolator::KeyFrame::updateValuesFromPointer()
|
||||||
|
{
|
||||||
|
p_ = frame()->position();
|
||||||
|
q_ = frame()->orientation();
|
||||||
|
}
|
||||||
|
|
||||||
|
void KeyFrameInterpolator::KeyFrame::computeTangent(const KeyFrame* const prev, const KeyFrame* const next)
|
||||||
|
{
|
||||||
|
tgP_ = 0.5 * (next->position() - prev->position());
|
||||||
|
tgQ_ = Quaternion::squadTangent(prev->orientation(), q_, next->orientation());
|
||||||
|
}
|
||||||
|
|
||||||
|
void KeyFrameInterpolator::KeyFrame::flipOrientationIfNeeded(const Quaternion& prev)
|
||||||
|
{
|
||||||
|
if (Quaternion::dot(prev, q_) < 0.0)
|
||||||
|
q_.negate();
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif //DOXYGEN
|
||||||
351
QGLViewer/keyFrameInterpolator.h
Normal file
351
QGLViewer/keyFrameInterpolator.h
Normal file
@ -0,0 +1,351 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
|
||||||
|
Copyright (C) 2002-2014 Gilles Debunne. All rights reserved.
|
||||||
|
|
||||||
|
This file is part of the QGLViewer library version 2.6.3.
|
||||||
|
|
||||||
|
http://www.libqglviewer.com - contact@libqglviewer.com
|
||||||
|
|
||||||
|
This file may be used under the terms of the GNU General Public License
|
||||||
|
versions 2.0 or 3.0 as published by the Free Software Foundation and
|
||||||
|
appearing in the LICENSE file included in the packaging of this file.
|
||||||
|
In addition, as a special exception, Gilles Debunne gives you certain
|
||||||
|
additional rights, described in the file GPL_EXCEPTION in this package.
|
||||||
|
|
||||||
|
libQGLViewer uses dual licensing. Commercial/proprietary software must
|
||||||
|
purchase a libQGLViewer Commercial License.
|
||||||
|
|
||||||
|
This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
|
||||||
|
WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
|
||||||
|
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
#ifndef QGLVIEWER_KEY_FRAME_INTERPOLATOR_H
|
||||||
|
#define QGLVIEWER_KEY_FRAME_INTERPOLATOR_H
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
#include <QTimer>
|
||||||
|
|
||||||
|
#include "quaternion.h"
|
||||||
|
// Not actually needed, but some bad compilers (Microsoft VS6) complain.
|
||||||
|
#include "frame.h"
|
||||||
|
|
||||||
|
// If you compiler complains about incomplete type, uncomment the next line
|
||||||
|
// #include "frame.h"
|
||||||
|
// and comment "class Frame;" 3 lines below
|
||||||
|
|
||||||
|
namespace qglviewer {
|
||||||
|
class Camera;
|
||||||
|
class Frame;
|
||||||
|
/*! \brief A keyFrame Catmull-Rom Frame interpolator.
|
||||||
|
\class KeyFrameInterpolator keyFrameInterpolator.h QGLViewer/keyFrameInterpolator.h
|
||||||
|
|
||||||
|
A KeyFrameInterpolator holds keyFrames (that define a path) and a pointer to a Frame of your
|
||||||
|
application (which will be interpolated). When the user startInterpolation(), the
|
||||||
|
KeyFrameInterpolator regularly updates the frame() position and orientation along the path.
|
||||||
|
|
||||||
|
Here is a typical utilization example (see also the <a href="../examples/keyFrames.html">keyFrames
|
||||||
|
example</a>):
|
||||||
|
\code
|
||||||
|
|
||||||
|
|
||||||
|
init()
|
||||||
|
{
|
||||||
|
// The KeyFrameInterpolator kfi is given the Frame that it will drive over time.
|
||||||
|
kfi = new KeyFrameInterpolator( new Frame() );
|
||||||
|
kfi->addKeyFrame( Frame( Vec(1,0,0), Quaternion() ) );
|
||||||
|
kfi->addKeyFrame( new Frame( Vec(2,1,0), Quaternion() ) );
|
||||||
|
// ...and so on for all the keyFrames.
|
||||||
|
|
||||||
|
// Ask for a display update after each update of the KeyFrameInterpolator
|
||||||
|
connect(kfi, SIGNAL(interpolated()), SLOT(update()));
|
||||||
|
|
||||||
|
kfi->startInterpolation();
|
||||||
|
}
|
||||||
|
|
||||||
|
draw()
|
||||||
|
{
|
||||||
|
glPushMatrix();
|
||||||
|
glMultMatrixd( kfi->frame()->matrix() );
|
||||||
|
// Draw your object here. Its position and orientation are interpolated.
|
||||||
|
glPopMatrix();
|
||||||
|
}
|
||||||
|
\endcode
|
||||||
|
|
||||||
|
The keyFrames are defined by a Frame and a time, expressed in seconds. The Frame can be provided
|
||||||
|
as a const reference or as a pointer to a Frame (see the addKeyFrame() methods). In the latter
|
||||||
|
case, the path will automatically be updated when the Frame is modified (using the
|
||||||
|
Frame::modified() signal).
|
||||||
|
|
||||||
|
The time has to be monotonously increasing over keyFrames. When interpolationSpeed() equals 1.0
|
||||||
|
(default value), these times correspond to actual user's seconds during interpolation (provided
|
||||||
|
that your main loop is fast enough). The interpolation is then real-time: the keyFrames will be
|
||||||
|
reached at their keyFrameTime().
|
||||||
|
|
||||||
|
<h3>Interpolation details</h3>
|
||||||
|
|
||||||
|
When the user startInterpolation(), a timer is started which will update the frame()'s position
|
||||||
|
and orientation every interpolationPeriod() milliseconds. This update increases the
|
||||||
|
interpolationTime() by interpolationPeriod() * interpolationSpeed() milliseconds.
|
||||||
|
|
||||||
|
Note that this mechanism ensures that the number of interpolation steps is constant and equal to
|
||||||
|
the total path duration() divided by the interpolationPeriod() * interpolationSpeed(). This is
|
||||||
|
especially useful for benchmarking or movie creation (constant number of snapshots).
|
||||||
|
|
||||||
|
During the interpolation, the KeyFrameInterpolator emits an interpolated() signal, which will
|
||||||
|
usually be connected to the QGLViewer::update() slot. The interpolation is stopped when
|
||||||
|
interpolationTime() is greater than the lastTime() (unless loopInterpolation() is \c true) and the
|
||||||
|
endReached() signal is then emitted.
|
||||||
|
|
||||||
|
Note that a Camera has Camera::keyFrameInterpolator(), that can be used to drive the Camera along a
|
||||||
|
path, or to restore a saved position (a path made of a single keyFrame). Press Alt+Fx to define a
|
||||||
|
new keyFrame for path x. Pressing Fx plays/pauses path interpolation. See QGLViewer::pathKey() and
|
||||||
|
the <a href="../keyboard.html">keyboard page</a> for details.
|
||||||
|
|
||||||
|
\attention If a Constraint is attached to the frame() (see Frame::constraint()), it should be
|
||||||
|
deactivated before interpolationIsStarted(), otherwise the interpolated motion (computed as if
|
||||||
|
there was no constraint) will probably be erroneous.
|
||||||
|
|
||||||
|
<h3>Retrieving interpolated values</h3>
|
||||||
|
|
||||||
|
This code defines a KeyFrameInterpolator, and displays the positions that will be followed by the
|
||||||
|
frame() along the path:
|
||||||
|
\code
|
||||||
|
KeyFrameInterpolator kfi( new Frame() );
|
||||||
|
// calls to kfi.addKeyFrame() to define the path.
|
||||||
|
|
||||||
|
const qreal deltaTime = 0.04; // output a position every deltaTime seconds
|
||||||
|
for (qreal time=kfi.firstTime(); time<=kfi.lastTime(); time += deltaTime)
|
||||||
|
{
|
||||||
|
kfi.interpolateAtTime(time);
|
||||||
|
cout << "t=" << time << "\tpos=" << kfi.frame()->position() << endl;
|
||||||
|
}
|
||||||
|
\endcode
|
||||||
|
You may want to temporally disconnect the \c kfi interpolated() signal from the
|
||||||
|
QGLViewer::update() slot before calling this code. \nosubgrouping */
|
||||||
|
class QGLVIEWER_EXPORT KeyFrameInterpolator : public QObject
|
||||||
|
{
|
||||||
|
// todo closedPath, insertKeyFrames, deleteKeyFrame, replaceKeyFrame
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
KeyFrameInterpolator(Frame* fr=NULL);
|
||||||
|
virtual ~KeyFrameInterpolator();
|
||||||
|
|
||||||
|
Q_SIGNALS:
|
||||||
|
/*! This signal is emitted whenever the frame() state is interpolated.
|
||||||
|
|
||||||
|
The emission of this signal triggers the synchronous emission of the frame()
|
||||||
|
Frame::interpolated() signal, which may also be useful.
|
||||||
|
|
||||||
|
This signal should especially be connected to your QGLViewer::update() slot, so that the display
|
||||||
|
is updated after every update of the KeyFrameInterpolator frame():
|
||||||
|
\code
|
||||||
|
connect(myKeyFrameInterpolator, SIGNAL(interpolated()), SLOT(update()));
|
||||||
|
\endcode
|
||||||
|
Use the QGLViewer::QGLViewerPool() to connect the signal to all the viewers.
|
||||||
|
|
||||||
|
Note that the QGLViewer::camera() Camera::keyFrameInterpolator() created using QGLViewer::pathKey()
|
||||||
|
have their interpolated() signals automatically connected to the QGLViewer::update() slot. */
|
||||||
|
void interpolated();
|
||||||
|
|
||||||
|
/*! This signal is emitted when the interpolation reaches the first (when interpolationSpeed()
|
||||||
|
is negative) or the last keyFrame.
|
||||||
|
|
||||||
|
When loopInterpolation() is \c true, interpolationTime() is reset and the interpolation
|
||||||
|
continues. It otherwise stops. */
|
||||||
|
void endReached();
|
||||||
|
|
||||||
|
/*! @name Path creation */
|
||||||
|
//@{
|
||||||
|
public Q_SLOTS:
|
||||||
|
void addKeyFrame(const Frame& frame);
|
||||||
|
void addKeyFrame(const Frame& frame, qreal time);
|
||||||
|
|
||||||
|
void addKeyFrame(const Frame* const frame);
|
||||||
|
void addKeyFrame(const Frame* const frame, qreal time);
|
||||||
|
|
||||||
|
void deletePath();
|
||||||
|
//@}
|
||||||
|
|
||||||
|
/*! @name Associated Frame */
|
||||||
|
//@{
|
||||||
|
public:
|
||||||
|
/*! Returns the associated Frame and that is interpolated by the KeyFrameInterpolator.
|
||||||
|
|
||||||
|
When interpolationIsStarted(), this Frame's position and orientation will regularly be updated
|
||||||
|
by a timer, so that they follow the KeyFrameInterpolator path.
|
||||||
|
|
||||||
|
Set using setFrame() or with the KeyFrameInterpolator constructor. */
|
||||||
|
Frame* frame() const { return frame_; }
|
||||||
|
|
||||||
|
public Q_SLOTS:
|
||||||
|
void setFrame(Frame* const frame);
|
||||||
|
//@}
|
||||||
|
|
||||||
|
/*! @name Path parameters */
|
||||||
|
//@{
|
||||||
|
public:
|
||||||
|
Frame keyFrame(int index) const;
|
||||||
|
qreal keyFrameTime(int index) const;
|
||||||
|
/*! Returns the number of keyFrames used by the interpolation. Use addKeyFrame() to add new keyFrames. */
|
||||||
|
int numberOfKeyFrames() const { return keyFrame_.count(); }
|
||||||
|
qreal duration() const;
|
||||||
|
qreal firstTime() const;
|
||||||
|
qreal lastTime() const;
|
||||||
|
//@}
|
||||||
|
|
||||||
|
/*! @name Interpolation parameters */
|
||||||
|
//@{
|
||||||
|
public:
|
||||||
|
/*! Returns the current interpolation time (in seconds) along the KeyFrameInterpolator path.
|
||||||
|
|
||||||
|
This time is regularly updated when interpolationIsStarted(). Can be set directly with
|
||||||
|
setInterpolationTime() or interpolateAtTime(). */
|
||||||
|
qreal interpolationTime() const { return interpolationTime_; }
|
||||||
|
/*! Returns the current interpolation speed.
|
||||||
|
|
||||||
|
Default value is 1.0, which means keyFrameTime() will be matched during the interpolation
|
||||||
|
(provided that your main loop is fast enough).
|
||||||
|
|
||||||
|
A negative value will result in a reverse interpolation of the keyFrames. See also
|
||||||
|
interpolationPeriod(). */
|
||||||
|
qreal interpolationSpeed() const { return interpolationSpeed_; }
|
||||||
|
/*! Returns the current interpolation period, expressed in milliseconds.
|
||||||
|
|
||||||
|
The update of the frame() state will be done by a timer at this period when
|
||||||
|
interpolationIsStarted().
|
||||||
|
|
||||||
|
This period (multiplied by interpolationSpeed()) is added to the interpolationTime() at each
|
||||||
|
update, and the frame() state is modified accordingly (see interpolateAtTime()). Default value
|
||||||
|
is 40 milliseconds. */
|
||||||
|
int interpolationPeriod() const { return period_; }
|
||||||
|
/*! Returns \c true when the interpolation is played in an infinite loop.
|
||||||
|
|
||||||
|
When \c false (default), the interpolation stops when interpolationTime() reaches firstTime()
|
||||||
|
(with negative interpolationSpeed()) or lastTime().
|
||||||
|
|
||||||
|
interpolationTime() is otherwise reset to firstTime() (+ interpolationTime() - lastTime()) (and
|
||||||
|
inversely for negative interpolationSpeed()) and interpolation continues.
|
||||||
|
|
||||||
|
In both cases, the endReached() signal is emitted. */
|
||||||
|
bool loopInterpolation() const { return loopInterpolation_; }
|
||||||
|
#ifndef DOXYGEN
|
||||||
|
/*! Whether or not (default) the path defined by the keyFrames is a closed loop. When \c true,
|
||||||
|
the last and the first KeyFrame are linked by a new spline segment.
|
||||||
|
|
||||||
|
Use setLoopInterpolation() to create a continuous animation over the entire path.
|
||||||
|
\attention The closed path feature is not yet implemented. */
|
||||||
|
bool closedPath() const { return closedPath_; }
|
||||||
|
#endif
|
||||||
|
public Q_SLOTS:
|
||||||
|
/*! Sets the interpolationTime().
|
||||||
|
|
||||||
|
\attention The frame() state is not affected by this method. Use this function to define the
|
||||||
|
starting time of a future interpolation (see startInterpolation()). Use interpolateAtTime() to
|
||||||
|
actually interpolate at a given time. */
|
||||||
|
void setInterpolationTime(qreal time) { interpolationTime_ = time; }
|
||||||
|
/*! Sets the interpolationSpeed(). Negative or null values are allowed. */
|
||||||
|
void setInterpolationSpeed(qreal speed) { interpolationSpeed_ = speed; }
|
||||||
|
/*! Sets the interpolationPeriod(). */
|
||||||
|
void setInterpolationPeriod(int period) { period_ = period; }
|
||||||
|
/*! Sets the loopInterpolation() value. */
|
||||||
|
void setLoopInterpolation(bool loop=true) { loopInterpolation_ = loop; }
|
||||||
|
#ifndef DOXYGEN
|
||||||
|
/*! Sets the closedPath() value. \attention The closed path feature is not yet implemented. */
|
||||||
|
void setClosedPath(bool closed=true) { closedPath_ = closed; }
|
||||||
|
#endif
|
||||||
|
//@}
|
||||||
|
|
||||||
|
|
||||||
|
/*! @name Interpolation */
|
||||||
|
//@{
|
||||||
|
public:
|
||||||
|
/*! Returns \c true when the interpolation is being performed. Use startInterpolation(),
|
||||||
|
stopInterpolation() or toggleInterpolation() to modify this state. */
|
||||||
|
bool interpolationIsStarted() const { return interpolationStarted_; }
|
||||||
|
public Q_SLOTS:
|
||||||
|
void startInterpolation(int period = -1);
|
||||||
|
void stopInterpolation();
|
||||||
|
void resetInterpolation();
|
||||||
|
/*! Calls startInterpolation() or stopInterpolation(), depending on interpolationIsStarted(). */
|
||||||
|
void toggleInterpolation() { if (interpolationIsStarted()) stopInterpolation(); else startInterpolation(); }
|
||||||
|
virtual void interpolateAtTime(qreal time);
|
||||||
|
//@}
|
||||||
|
|
||||||
|
/*! @name XML representation */
|
||||||
|
//@{
|
||||||
|
public:
|
||||||
|
virtual QDomElement domElement(const QString& name, QDomDocument& document) const;
|
||||||
|
virtual void initFromDOMElement(const QDomElement& element);
|
||||||
|
//@}
|
||||||
|
|
||||||
|
private Q_SLOTS:
|
||||||
|
virtual void update();
|
||||||
|
virtual void invalidateValues() { valuesAreValid_ = false; pathIsValid_ = false; splineCacheIsValid_ = false; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
// Copy constructor and opertor= are declared private and undefined
|
||||||
|
// Prevents everyone from trying to use them
|
||||||
|
// KeyFrameInterpolator(const KeyFrameInterpolator& kfi);
|
||||||
|
// KeyFrameInterpolator& operator=(const KeyFrameInterpolator& kfi);
|
||||||
|
|
||||||
|
void updateCurrentKeyFrameForTime(qreal time);
|
||||||
|
void updateModifiedFrameValues();
|
||||||
|
void updateSplineCache();
|
||||||
|
|
||||||
|
#ifndef DOXYGEN
|
||||||
|
// Internal private KeyFrame representation
|
||||||
|
class KeyFrame
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
KeyFrame(const Frame& fr, qreal t);
|
||||||
|
KeyFrame(const Frame* fr, qreal t);
|
||||||
|
|
||||||
|
Vec position() const { return p_; }
|
||||||
|
Quaternion orientation() const { return q_; }
|
||||||
|
Vec tgP() const { return tgP_; }
|
||||||
|
Quaternion tgQ() const { return tgQ_; }
|
||||||
|
qreal time() const { return time_; }
|
||||||
|
const Frame* frame() const { return frame_; }
|
||||||
|
void updateValuesFromPointer();
|
||||||
|
void flipOrientationIfNeeded(const Quaternion& prev);
|
||||||
|
void computeTangent(const KeyFrame* const prev, const KeyFrame* const next);
|
||||||
|
private:
|
||||||
|
Vec p_, tgP_;
|
||||||
|
Quaternion q_, tgQ_;
|
||||||
|
qreal time_;
|
||||||
|
const Frame* const frame_;
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// K e y F r a m e s
|
||||||
|
mutable QList<KeyFrame*> keyFrame_;
|
||||||
|
QMutableListIterator<KeyFrame*>* currentFrame_[4];
|
||||||
|
QList<Frame> path_;
|
||||||
|
|
||||||
|
// A s s o c i a t e d f r a m e
|
||||||
|
Frame* frame_;
|
||||||
|
|
||||||
|
// R h y t h m
|
||||||
|
QTimer timer_;
|
||||||
|
int period_;
|
||||||
|
qreal interpolationTime_;
|
||||||
|
qreal interpolationSpeed_;
|
||||||
|
bool interpolationStarted_;
|
||||||
|
|
||||||
|
// M i s c
|
||||||
|
bool closedPath_;
|
||||||
|
bool loopInterpolation_;
|
||||||
|
|
||||||
|
// C a c h e d v a l u e s a n d f l a g s
|
||||||
|
bool pathIsValid_;
|
||||||
|
bool valuesAreValid_;
|
||||||
|
bool currentFrameValid_;
|
||||||
|
bool splineCacheIsValid_;
|
||||||
|
Vec v1, v2;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace qglviewer
|
||||||
|
|
||||||
|
#endif // QGLVIEWER_KEY_FRAME_INTERPOLATOR_H
|
||||||
469
QGLViewer/manipulatedCameraFrame.cpp
Normal file
469
QGLViewer/manipulatedCameraFrame.cpp
Normal file
@ -0,0 +1,469 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
|
||||||
|
Copyright (C) 2002-2014 Gilles Debunne. All rights reserved.
|
||||||
|
|
||||||
|
This file is part of the QGLViewer library version 2.6.3.
|
||||||
|
|
||||||
|
http://www.libqglviewer.com - contact@libqglviewer.com
|
||||||
|
|
||||||
|
This file may be used under the terms of the GNU General Public License
|
||||||
|
versions 2.0 or 3.0 as published by the Free Software Foundation and
|
||||||
|
appearing in the LICENSE file included in the packaging of this file.
|
||||||
|
In addition, as a special exception, Gilles Debunne gives you certain
|
||||||
|
additional rights, described in the file GPL_EXCEPTION in this package.
|
||||||
|
|
||||||
|
libQGLViewer uses dual licensing. Commercial/proprietary software must
|
||||||
|
purchase a libQGLViewer Commercial License.
|
||||||
|
|
||||||
|
This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
|
||||||
|
WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
|
||||||
|
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
#include "domUtils.h"
|
||||||
|
#include "manipulatedCameraFrame.h"
|
||||||
|
#include "qglviewer.h"
|
||||||
|
|
||||||
|
#include <QMouseEvent>
|
||||||
|
|
||||||
|
using namespace qglviewer;
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
/*! Default constructor.
|
||||||
|
|
||||||
|
flySpeed() is set to 0.0 and sceneUpVector() is (0,1,0). The pivotPoint() is set to (0,0,0).
|
||||||
|
|
||||||
|
\attention Created object is removeFromMouseGrabberPool(). */
|
||||||
|
ManipulatedCameraFrame::ManipulatedCameraFrame()
|
||||||
|
: driveSpeed_(0.0), sceneUpVector_(0.0, 1.0, 0.0), rotatesAroundUpVector_(false), zoomsOnPivotPoint_(false)
|
||||||
|
{
|
||||||
|
setFlySpeed(0.0);
|
||||||
|
removeFromMouseGrabberPool();
|
||||||
|
connect(&flyTimer_, SIGNAL(timeout()), SLOT(flyUpdate()));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Equal operator. Calls ManipulatedFrame::operator=() and then copy attributes. */
|
||||||
|
ManipulatedCameraFrame& ManipulatedCameraFrame::operator=(const ManipulatedCameraFrame& mcf)
|
||||||
|
{
|
||||||
|
ManipulatedFrame::operator=(mcf);
|
||||||
|
|
||||||
|
setFlySpeed(mcf.flySpeed());
|
||||||
|
setSceneUpVector(mcf.sceneUpVector());
|
||||||
|
setRotatesAroundUpVector(mcf.rotatesAroundUpVector_);
|
||||||
|
setZoomsOnPivotPoint(mcf.zoomsOnPivotPoint_);
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Copy constructor. Performs a deep copy of all members using operator=(). */
|
||||||
|
ManipulatedCameraFrame::ManipulatedCameraFrame(const ManipulatedCameraFrame& mcf)
|
||||||
|
: ManipulatedFrame(mcf)
|
||||||
|
{
|
||||||
|
removeFromMouseGrabberPool();
|
||||||
|
connect(&flyTimer_, SIGNAL(timeout()), SLOT(flyUpdate()));
|
||||||
|
(*this)=(mcf);
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
/*! Overloading of ManipulatedFrame::spin().
|
||||||
|
|
||||||
|
Rotates the ManipulatedCameraFrame around its pivotPoint() instead of its origin. */
|
||||||
|
void ManipulatedCameraFrame::spin()
|
||||||
|
{
|
||||||
|
rotateAroundPoint(spinningQuaternion(), pivotPoint());
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifndef DOXYGEN
|
||||||
|
/*! Called for continuous frame motion in fly mode (see QGLViewer::MOVE_FORWARD). Emits
|
||||||
|
manipulated(). */
|
||||||
|
void ManipulatedCameraFrame::flyUpdate()
|
||||||
|
{
|
||||||
|
static Vec flyDisp(0.0, 0.0, 0.0);
|
||||||
|
switch (action_)
|
||||||
|
{
|
||||||
|
case QGLViewer::MOVE_FORWARD:
|
||||||
|
flyDisp.z = -flySpeed();
|
||||||
|
translate(localInverseTransformOf(flyDisp));
|
||||||
|
break;
|
||||||
|
case QGLViewer::MOVE_BACKWARD:
|
||||||
|
flyDisp.z = flySpeed();
|
||||||
|
translate(localInverseTransformOf(flyDisp));
|
||||||
|
break;
|
||||||
|
case QGLViewer::DRIVE:
|
||||||
|
flyDisp.z = flySpeed() * driveSpeed_;
|
||||||
|
translate(localInverseTransformOf(flyDisp));
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Needs to be out of the switch since ZOOM/fastDraw()/wheelEvent use this callback to trigger a final draw().
|
||||||
|
// #CONNECTION# wheelEvent.
|
||||||
|
Q_EMIT manipulated();
|
||||||
|
}
|
||||||
|
|
||||||
|
Vec ManipulatedCameraFrame::flyUpVector() const {
|
||||||
|
qWarning("flyUpVector() is deprecated. Use sceneUpVector() instead.");
|
||||||
|
return sceneUpVector();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ManipulatedCameraFrame::setFlyUpVector(const Vec& up) {
|
||||||
|
qWarning("setFlyUpVector() is deprecated. Use setSceneUpVector() instead.");
|
||||||
|
setSceneUpVector(up);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*! This method will be called by the Camera when its orientation is changed, so that the
|
||||||
|
sceneUpVector (private) is changed accordingly. You should not need to call this method. */
|
||||||
|
void ManipulatedCameraFrame::updateSceneUpVector()
|
||||||
|
{
|
||||||
|
sceneUpVector_ = inverseTransformOf(Vec(0.0, 1.0, 0.0));
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// S t a t e s a v i n g a n d r e s t o r i n g //
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
/*! Returns an XML \c QDomElement that represents the ManipulatedCameraFrame.
|
||||||
|
|
||||||
|
Adds to the ManipulatedFrame::domElement() the ManipulatedCameraFrame specific informations in a \c
|
||||||
|
ManipulatedCameraParameters child QDomElement.
|
||||||
|
|
||||||
|
\p name is the name of the QDomElement tag. \p doc is the \c QDomDocument factory used to create
|
||||||
|
QDomElement.
|
||||||
|
|
||||||
|
Use initFromDOMElement() to restore the ManipulatedCameraFrame state from the resulting
|
||||||
|
\c QDomElement.
|
||||||
|
|
||||||
|
See Vec::domElement() for a complete example. See also Quaternion::domElement(),
|
||||||
|
Frame::domElement(), Camera::domElement()... */
|
||||||
|
QDomElement ManipulatedCameraFrame::domElement(const QString& name, QDomDocument& document) const
|
||||||
|
{
|
||||||
|
QDomElement e = ManipulatedFrame::domElement(name, document);
|
||||||
|
QDomElement mcp = document.createElement("ManipulatedCameraParameters");
|
||||||
|
mcp.setAttribute("flySpeed", QString::number(flySpeed()));
|
||||||
|
DomUtils::setBoolAttribute(mcp, "rotatesAroundUpVector", rotatesAroundUpVector());
|
||||||
|
DomUtils::setBoolAttribute(mcp, "zoomsOnPivotPoint", zoomsOnPivotPoint());
|
||||||
|
mcp.appendChild(sceneUpVector().domElement("sceneUpVector", document));
|
||||||
|
e.appendChild(mcp);
|
||||||
|
return e;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Restores the ManipulatedCameraFrame state from a \c QDomElement created by domElement().
|
||||||
|
|
||||||
|
First calls ManipulatedFrame::initFromDOMElement() and then initializes ManipulatedCameraFrame
|
||||||
|
specific parameters. */
|
||||||
|
void ManipulatedCameraFrame::initFromDOMElement(const QDomElement& element)
|
||||||
|
{
|
||||||
|
// No need to initialize, since default sceneUpVector and flySpeed are not meaningful.
|
||||||
|
// It's better to keep current ones. And it would destroy constraint() and referenceFrame().
|
||||||
|
// *this = ManipulatedCameraFrame();
|
||||||
|
ManipulatedFrame::initFromDOMElement(element);
|
||||||
|
|
||||||
|
QDomElement child=element.firstChild().toElement();
|
||||||
|
while (!child.isNull())
|
||||||
|
{
|
||||||
|
if (child.tagName() == "ManipulatedCameraParameters")
|
||||||
|
{
|
||||||
|
setFlySpeed(DomUtils::qrealFromDom(child, "flySpeed", flySpeed()));
|
||||||
|
setRotatesAroundUpVector(DomUtils::boolFromDom(child, "rotatesAroundUpVector", false));
|
||||||
|
setZoomsOnPivotPoint(DomUtils::boolFromDom(child, "zoomsOnPivotPoint", false));
|
||||||
|
|
||||||
|
QDomElement schild=child.firstChild().toElement();
|
||||||
|
while (!schild.isNull())
|
||||||
|
{
|
||||||
|
if (schild.tagName() == "sceneUpVector")
|
||||||
|
setSceneUpVector(Vec(schild));
|
||||||
|
|
||||||
|
schild = schild.nextSibling().toElement();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
child = child.nextSibling().toElement();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// M o u s e h a n d l i n g //
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
#ifndef DOXYGEN
|
||||||
|
/*! Protected internal method used to handle mouse events. */
|
||||||
|
void ManipulatedCameraFrame::startAction(int ma, bool withConstraint)
|
||||||
|
{
|
||||||
|
ManipulatedFrame::startAction(ma, withConstraint);
|
||||||
|
|
||||||
|
switch (action_)
|
||||||
|
{
|
||||||
|
case QGLViewer::MOVE_FORWARD:
|
||||||
|
case QGLViewer::MOVE_BACKWARD:
|
||||||
|
case QGLViewer::DRIVE:
|
||||||
|
flyTimer_.setSingleShot(false);
|
||||||
|
flyTimer_.start(10);
|
||||||
|
break;
|
||||||
|
case QGLViewer::ROTATE:
|
||||||
|
constrainedRotationIsReversed_ = transformOf(sceneUpVector_).y < 0.0;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ManipulatedCameraFrame::zoom(qreal delta, const Camera * const camera) {
|
||||||
|
const qreal sceneRadius = camera->sceneRadius();
|
||||||
|
if (zoomsOnPivotPoint_) {
|
||||||
|
Vec direction = position() - camera->pivotPoint();
|
||||||
|
if (direction.norm() > 0.02 * sceneRadius || delta > 0.0)
|
||||||
|
translate(delta * direction);
|
||||||
|
} else {
|
||||||
|
const qreal coef = qMax(fabs((camera->frame()->coordinatesOf(camera->pivotPoint())).z), 0.2 * sceneRadius);
|
||||||
|
Vec trans(0.0, 0.0, -coef * delta);
|
||||||
|
translate(inverseTransformOf(trans));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*! Overloading of ManipulatedFrame::mouseMoveEvent().
|
||||||
|
|
||||||
|
Motion depends on mouse binding (see <a href="../mouse.html">mouse page</a> for details). The
|
||||||
|
resulting displacements are basically inverted from those of a ManipulatedFrame. */
|
||||||
|
void ManipulatedCameraFrame::mouseMoveEvent(QMouseEvent* const event, Camera* const camera)
|
||||||
|
{
|
||||||
|
// #CONNECTION# QGLViewer::mouseMoveEvent does the update().
|
||||||
|
switch (action_)
|
||||||
|
{
|
||||||
|
case QGLViewer::TRANSLATE:
|
||||||
|
{
|
||||||
|
const QPoint delta = prevPos_ - event->pos();
|
||||||
|
Vec trans(delta.x(), -delta.y(), 0.0);
|
||||||
|
// Scale to fit the screen mouse displacement
|
||||||
|
switch (camera->type())
|
||||||
|
{
|
||||||
|
case Camera::PERSPECTIVE :
|
||||||
|
trans *= 2.0 * tan(camera->fieldOfView()/2.0) *
|
||||||
|
fabs((camera->frame()->coordinatesOf(pivotPoint())).z) / camera->screenHeight();
|
||||||
|
break;
|
||||||
|
case Camera::ORTHOGRAPHIC :
|
||||||
|
{
|
||||||
|
GLdouble w,h;
|
||||||
|
camera->getOrthoWidthHeight(w, h);
|
||||||
|
trans[0] *= 2.0 * w / camera->screenWidth();
|
||||||
|
trans[1] *= 2.0 * h / camera->screenHeight();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
translate(inverseTransformOf(translationSensitivity()*trans));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case QGLViewer::MOVE_FORWARD:
|
||||||
|
{
|
||||||
|
Quaternion rot = pitchYawQuaternion(event->x(), event->y(), camera);
|
||||||
|
rotate(rot);
|
||||||
|
//#CONNECTION# wheelEvent MOVE_FORWARD case
|
||||||
|
// actual translation is made in flyUpdate().
|
||||||
|
//translate(inverseTransformOf(Vec(0.0, 0.0, -flySpeed())));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case QGLViewer::MOVE_BACKWARD:
|
||||||
|
{
|
||||||
|
Quaternion rot = pitchYawQuaternion(event->x(), event->y(), camera);
|
||||||
|
rotate(rot);
|
||||||
|
// actual translation is made in flyUpdate().
|
||||||
|
//translate(inverseTransformOf(Vec(0.0, 0.0, flySpeed())));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case QGLViewer::DRIVE:
|
||||||
|
{
|
||||||
|
Quaternion rot = turnQuaternion(event->x(), camera);
|
||||||
|
rotate(rot);
|
||||||
|
// actual translation is made in flyUpdate().
|
||||||
|
driveSpeed_ = 0.01 * (event->y() - pressPos_.y());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case QGLViewer::ZOOM:
|
||||||
|
{
|
||||||
|
zoom(deltaWithPrevPos(event, camera), camera);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case QGLViewer::LOOK_AROUND:
|
||||||
|
{
|
||||||
|
Quaternion rot = pitchYawQuaternion(event->x(), event->y(), camera);
|
||||||
|
rotate(rot);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case QGLViewer::ROTATE:
|
||||||
|
{
|
||||||
|
Quaternion rot;
|
||||||
|
if (rotatesAroundUpVector_) {
|
||||||
|
// Multiply by 2.0 to get on average about the same speed as with the deformed ball
|
||||||
|
qreal dx = 2.0 * rotationSensitivity() * (prevPos_.x() - event->x()) / camera->screenWidth();
|
||||||
|
qreal dy = 2.0 * rotationSensitivity() * (prevPos_.y() - event->y()) / camera->screenHeight();
|
||||||
|
if (constrainedRotationIsReversed_) dx = -dx;
|
||||||
|
Vec verticalAxis = transformOf(sceneUpVector_);
|
||||||
|
rot = Quaternion(verticalAxis, dx) * Quaternion(Vec(1.0, 0.0, 0.0), dy);
|
||||||
|
} else {
|
||||||
|
Vec trans = camera->projectedCoordinatesOf(pivotPoint());
|
||||||
|
rot = deformedBallQuaternion(event->x(), event->y(), trans[0], trans[1], camera);
|
||||||
|
}
|
||||||
|
//#CONNECTION# These two methods should go together (spinning detection and activation)
|
||||||
|
computeMouseSpeed(event);
|
||||||
|
setSpinningQuaternion(rot);
|
||||||
|
spin();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case QGLViewer::SCREEN_ROTATE:
|
||||||
|
{
|
||||||
|
Vec trans = camera->projectedCoordinatesOf(pivotPoint());
|
||||||
|
|
||||||
|
const qreal angle = atan2(event->y() - trans[1], event->x() - trans[0]) - atan2(prevPos_.y()-trans[1], prevPos_.x()-trans[0]);
|
||||||
|
|
||||||
|
Quaternion rot(Vec(0.0, 0.0, 1.0), angle);
|
||||||
|
//#CONNECTION# These two methods should go together (spinning detection and activation)
|
||||||
|
computeMouseSpeed(event);
|
||||||
|
setSpinningQuaternion(rot);
|
||||||
|
spin();
|
||||||
|
updateSceneUpVector();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case QGLViewer::ROLL:
|
||||||
|
{
|
||||||
|
const qreal angle = M_PI * (event->x() - prevPos_.x()) / camera->screenWidth();
|
||||||
|
Quaternion rot(Vec(0.0, 0.0, 1.0), angle);
|
||||||
|
rotate(rot);
|
||||||
|
setSpinningQuaternion(rot);
|
||||||
|
updateSceneUpVector();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case QGLViewer::SCREEN_TRANSLATE:
|
||||||
|
{
|
||||||
|
Vec trans;
|
||||||
|
int dir = mouseOriginalDirection(event);
|
||||||
|
if (dir == 1)
|
||||||
|
trans.setValue(prevPos_.x() - event->x(), 0.0, 0.0);
|
||||||
|
else if (dir == -1)
|
||||||
|
trans.setValue(0.0, event->y() - prevPos_.y(), 0.0);
|
||||||
|
|
||||||
|
switch (camera->type())
|
||||||
|
{
|
||||||
|
case Camera::PERSPECTIVE :
|
||||||
|
trans *= 2.0 * tan(camera->fieldOfView()/2.0) *
|
||||||
|
fabs((camera->frame()->coordinatesOf(pivotPoint())).z) / camera->screenHeight();
|
||||||
|
break;
|
||||||
|
case Camera::ORTHOGRAPHIC :
|
||||||
|
{
|
||||||
|
GLdouble w,h;
|
||||||
|
camera->getOrthoWidthHeight(w, h);
|
||||||
|
trans[0] *= 2.0 * w / camera->screenWidth();
|
||||||
|
trans[1] *= 2.0 * h / camera->screenHeight();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
translate(inverseTransformOf(translationSensitivity()*trans));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case QGLViewer::ZOOM_ON_REGION:
|
||||||
|
case QGLViewer::NO_MOUSE_ACTION:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (action_ != QGLViewer::NO_MOUSE_ACTION)
|
||||||
|
{
|
||||||
|
prevPos_ = event->pos();
|
||||||
|
if (action_ != QGLViewer::ZOOM_ON_REGION)
|
||||||
|
// ZOOM_ON_REGION should not emit manipulated().
|
||||||
|
// prevPos_ is used to draw rectangle feedback.
|
||||||
|
Q_EMIT manipulated();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*! This is an overload of ManipulatedFrame::mouseReleaseEvent(). The QGLViewer::MouseAction is
|
||||||
|
terminated. */
|
||||||
|
void ManipulatedCameraFrame::mouseReleaseEvent(QMouseEvent* const event, Camera* const camera)
|
||||||
|
{
|
||||||
|
if ((action_ == QGLViewer::MOVE_FORWARD) || (action_ == QGLViewer::MOVE_BACKWARD) || (action_ == QGLViewer::DRIVE))
|
||||||
|
flyTimer_.stop();
|
||||||
|
|
||||||
|
if (action_ == QGLViewer::ZOOM_ON_REGION)
|
||||||
|
camera->fitScreenRegion(QRect(pressPos_, event->pos()));
|
||||||
|
|
||||||
|
ManipulatedFrame::mouseReleaseEvent(event, camera);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! This is an overload of ManipulatedFrame::wheelEvent().
|
||||||
|
|
||||||
|
The wheel behavior depends on the wheel binded action. Current possible actions are QGLViewer::ZOOM,
|
||||||
|
QGLViewer::MOVE_FORWARD, QGLViewer::MOVE_BACKWARD. QGLViewer::ZOOM speed depends on
|
||||||
|
wheelSensitivity() while QGLViewer::MOVE_FORWARD and QGLViewer::MOVE_BACKWARD depend on flySpeed().
|
||||||
|
See QGLViewer::setWheelBinding() to customize the binding. */
|
||||||
|
void ManipulatedCameraFrame::wheelEvent(QWheelEvent* const event, Camera* const camera)
|
||||||
|
{
|
||||||
|
//#CONNECTION# QGLViewer::setWheelBinding, ManipulatedFrame::wheelEvent.
|
||||||
|
switch (action_)
|
||||||
|
{
|
||||||
|
case QGLViewer::ZOOM:
|
||||||
|
{
|
||||||
|
zoom(wheelDelta(event), camera);
|
||||||
|
Q_EMIT manipulated();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case QGLViewer::MOVE_FORWARD:
|
||||||
|
case QGLViewer::MOVE_BACKWARD:
|
||||||
|
//#CONNECTION# mouseMoveEvent() MOVE_FORWARD case
|
||||||
|
translate(inverseTransformOf(Vec(0.0, 0.0, 0.2*flySpeed()*event->delta())));
|
||||||
|
Q_EMIT manipulated();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// #CONNECTION# startAction should always be called before
|
||||||
|
if (previousConstraint_)
|
||||||
|
setConstraint(previousConstraint_);
|
||||||
|
|
||||||
|
// The wheel triggers a fastDraw. A final update() is needed after the last wheel event to
|
||||||
|
// polish the rendering using draw(). Since the last wheel event does not say its name, we use
|
||||||
|
// the flyTimer_ to trigger flyUpdate(), which emits manipulated. Two wheel events
|
||||||
|
// separated by more than this delay milliseconds will trigger a draw().
|
||||||
|
const int finalDrawAfterWheelEventDelay = 400;
|
||||||
|
|
||||||
|
// Starts (or prolungates) the timer.
|
||||||
|
flyTimer_.setSingleShot(true);
|
||||||
|
flyTimer_.start(finalDrawAfterWheelEventDelay);
|
||||||
|
|
||||||
|
// This could also be done *before* manipulated is emitted, so that isManipulated() returns false.
|
||||||
|
// But then fastDraw would not be used with wheel.
|
||||||
|
// Detecting the last wheel event and forcing a final draw() is done using the timer_.
|
||||||
|
action_ = QGLViewer::NO_MOUSE_ACTION;
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
/*! Returns a Quaternion that is a rotation around current camera Y, proportionnal to the horizontal mouse position. */
|
||||||
|
Quaternion ManipulatedCameraFrame::turnQuaternion(int x, const Camera* const camera)
|
||||||
|
{
|
||||||
|
return Quaternion(Vec(0.0, 1.0, 0.0), rotationSensitivity()*(prevPos_.x()-x)/camera->screenWidth());
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Returns a Quaternion that is the composition of two rotations, inferred from the
|
||||||
|
mouse pitch (X axis) and yaw (sceneUpVector() axis). */
|
||||||
|
Quaternion ManipulatedCameraFrame::pitchYawQuaternion(int x, int y, const Camera* const camera)
|
||||||
|
{
|
||||||
|
const Quaternion rotX(Vec(1.0, 0.0, 0.0), rotationSensitivity()*(prevPos_.y()-y)/camera->screenHeight());
|
||||||
|
const Quaternion rotY(transformOf(sceneUpVector()), rotationSensitivity()*(prevPos_.x()-x)/camera->screenWidth());
|
||||||
|
return rotY * rotX;
|
||||||
|
}
|
||||||
233
QGLViewer/manipulatedCameraFrame.h
Normal file
233
QGLViewer/manipulatedCameraFrame.h
Normal file
@ -0,0 +1,233 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
|
||||||
|
Copyright (C) 2002-2014 Gilles Debunne. All rights reserved.
|
||||||
|
|
||||||
|
This file is part of the QGLViewer library version 2.6.3.
|
||||||
|
|
||||||
|
http://www.libqglviewer.com - contact@libqglviewer.com
|
||||||
|
|
||||||
|
This file may be used under the terms of the GNU General Public License
|
||||||
|
versions 2.0 or 3.0 as published by the Free Software Foundation and
|
||||||
|
appearing in the LICENSE file included in the packaging of this file.
|
||||||
|
In addition, as a special exception, Gilles Debunne gives you certain
|
||||||
|
additional rights, described in the file GPL_EXCEPTION in this package.
|
||||||
|
|
||||||
|
libQGLViewer uses dual licensing. Commercial/proprietary software must
|
||||||
|
purchase a libQGLViewer Commercial License.
|
||||||
|
|
||||||
|
This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
|
||||||
|
WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
|
||||||
|
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
#ifndef QGLVIEWER_MANIPULATED_CAMERA_FRAME_H
|
||||||
|
#define QGLVIEWER_MANIPULATED_CAMERA_FRAME_H
|
||||||
|
|
||||||
|
#include "manipulatedFrame.h"
|
||||||
|
|
||||||
|
namespace qglviewer {
|
||||||
|
/*! \brief The ManipulatedCameraFrame class represents a ManipulatedFrame with Camera specific mouse bindings.
|
||||||
|
\class ManipulatedCameraFrame manipulatedCameraFrame.h QGLViewer/manipulatedCameraFrame.h
|
||||||
|
|
||||||
|
A ManipulatedCameraFrame is a specialization of a ManipulatedFrame, designed to be set as the
|
||||||
|
Camera::frame(). Mouse motions are basically interpreted in a negated way: when the mouse goes to
|
||||||
|
the right, the ManipulatedFrame translation goes to the right, while the ManipulatedCameraFrame
|
||||||
|
has to go to the \e left, so that the \e scene seems to move to the right.
|
||||||
|
|
||||||
|
A ManipulatedCameraFrame rotates around its pivotPoint(), which corresponds to the
|
||||||
|
associated Camera::pivotPoint().
|
||||||
|
|
||||||
|
A ManipulatedCameraFrame can also "fly" in the scene. It basically moves forward, and turns
|
||||||
|
according to the mouse motion. See flySpeed(), sceneUpVector() and the QGLViewer::MOVE_FORWARD and
|
||||||
|
QGLViewer::MOVE_BACKWARD QGLViewer::MouseAction.
|
||||||
|
|
||||||
|
See the <a href="../mouse.html">mouse page</a> for a description of the possible actions that can
|
||||||
|
be performed using the mouse and their bindings.
|
||||||
|
\nosubgrouping */
|
||||||
|
class QGLVIEWER_EXPORT ManipulatedCameraFrame : public ManipulatedFrame
|
||||||
|
{
|
||||||
|
#ifndef DOXYGEN
|
||||||
|
friend class Camera;
|
||||||
|
friend class ::QGLViewer;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
ManipulatedCameraFrame();
|
||||||
|
/*! Virtual destructor. Empty. */
|
||||||
|
virtual ~ManipulatedCameraFrame() {}
|
||||||
|
|
||||||
|
ManipulatedCameraFrame(const ManipulatedCameraFrame& mcf);
|
||||||
|
ManipulatedCameraFrame& operator=(const ManipulatedCameraFrame& mcf);
|
||||||
|
|
||||||
|
/*! @name Pivot point */
|
||||||
|
//@{
|
||||||
|
public:
|
||||||
|
/*! Returns the point the ManipulatedCameraFrame pivot point, around which the camera rotates.
|
||||||
|
|
||||||
|
It is defined in the world coordinate system. Default value is (0,0,0).
|
||||||
|
|
||||||
|
When the ManipulatedCameraFrame is associated to a Camera, Camera::pivotPoint() also
|
||||||
|
returns this value. This point can interactively be changed using the mouse (see
|
||||||
|
Camera::setPivotPointFromPixel() and QGLViewer::RAP_FROM_PIXEL and QGLViewer::RAP_IS_CENTER
|
||||||
|
in the <a href="../mouse.html">mouse page</a>). */
|
||||||
|
Vec pivotPoint() const { return pivotPoint_; }
|
||||||
|
/*! Sets the pivotPoint(), defined in the world coordinate system. */
|
||||||
|
void setPivotPoint(const Vec& point) { pivotPoint_ = point; }
|
||||||
|
|
||||||
|
#ifndef DOXYGEN
|
||||||
|
Vec revolveAroundPoint() const { qWarning("revolveAroundPoint() is deprecated, use pivotPoint() instead"); return pivotPoint(); }
|
||||||
|
void setRevolveArountPoint(const Vec& point) { qWarning("setRevolveAroundPoint() is deprecated, use setPivotPoint() instead"); setPivotPoint(point); }
|
||||||
|
#endif
|
||||||
|
//@}
|
||||||
|
|
||||||
|
/*! @name Camera manipulation */
|
||||||
|
//@{
|
||||||
|
public:
|
||||||
|
/*! Returns \c true when the frame's rotation is constrained around the sceneUpVector(),
|
||||||
|
and \c false otherwise, when the rotation is completely free (default).
|
||||||
|
|
||||||
|
In free mode, the associated camera can be arbitrarily rotated in the scene, along its
|
||||||
|
three axis, thus possibly leading to any arbitrary orientation.
|
||||||
|
|
||||||
|
When you setRotatesAroundUpVector() to \c true, the sceneUpVector() defines a
|
||||||
|
'vertical' direction around which the camera rotates. The camera can rotate left
|
||||||
|
or right, around this axis. It can also be moved up or down to show the 'top' and
|
||||||
|
'bottom' views of the scene. As a result, the sceneUpVector() will always appear vertical
|
||||||
|
in the scene, and the horizon is preserved and stays projected along the camera's
|
||||||
|
horizontal axis.
|
||||||
|
|
||||||
|
Note that setting this value to \c true when the sceneUpVector() is not already
|
||||||
|
vertically projected will break these invariants. It will also limit the possible movement
|
||||||
|
of the camera, possibly up to a lock when the sceneUpVector() is projected horizontally.
|
||||||
|
Use Camera::setUpVector() to define the sceneUpVector() and align the camera before calling
|
||||||
|
this method to ensure this does not happen. */
|
||||||
|
bool rotatesAroundUpVector() const { return rotatesAroundUpVector_; }
|
||||||
|
/*! Sets the value of rotatesAroundUpVector().
|
||||||
|
|
||||||
|
Default value is false (free rotation). */
|
||||||
|
void setRotatesAroundUpVector(bool constrained) { rotatesAroundUpVector_ = constrained; }
|
||||||
|
|
||||||
|
/*! Returns whether or not the QGLViewer::ZOOM action zooms on the pivot point.
|
||||||
|
|
||||||
|
When set to \c false (default), a zoom action will move the camera along its Camera::viewDirection(),
|
||||||
|
i.e. back and forth along a direction perpendicular to the projection screen.
|
||||||
|
|
||||||
|
setZoomsOnPivotPoint() to \c true will move the camera along an axis defined by the
|
||||||
|
Camera::pivotPoint() and its current position instead. As a result, the projected position of the
|
||||||
|
pivot point on screen will stay the same during a zoom. */
|
||||||
|
bool zoomsOnPivotPoint() const { return zoomsOnPivotPoint_; }
|
||||||
|
/*! Sets the value of zoomsOnPivotPoint().
|
||||||
|
|
||||||
|
Default value is false. */
|
||||||
|
void setZoomsOnPivotPoint(bool enabled) { zoomsOnPivotPoint_ = enabled; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
#ifndef DOXYGEN
|
||||||
|
void zoom(qreal delta, const Camera * const camera);
|
||||||
|
#endif
|
||||||
|
//@}
|
||||||
|
|
||||||
|
/*! @name Fly parameters */
|
||||||
|
//@{
|
||||||
|
public Q_SLOTS:
|
||||||
|
/*! Sets the flySpeed(), defined in OpenGL units.
|
||||||
|
|
||||||
|
Default value is 0.0, but it is modified according to the QGLViewer::sceneRadius() when the
|
||||||
|
ManipulatedCameraFrame is set as the Camera::frame(). */
|
||||||
|
void setFlySpeed(qreal speed) { flySpeed_ = speed; }
|
||||||
|
|
||||||
|
/*! Sets the sceneUpVector(), defined in the world coordinate system.
|
||||||
|
|
||||||
|
Default value is (0,1,0), but it is updated by the Camera when this object is set as its Camera::frame().
|
||||||
|
Using Camera::setUpVector() instead is probably a better solution. */
|
||||||
|
void setSceneUpVector(const Vec& up) { sceneUpVector_ = up; }
|
||||||
|
|
||||||
|
public:
|
||||||
|
/*! Returns the fly speed, expressed in OpenGL units.
|
||||||
|
|
||||||
|
It corresponds to the incremental displacement that is periodically applied to the
|
||||||
|
ManipulatedCameraFrame position when a QGLViewer::MOVE_FORWARD or QGLViewer::MOVE_BACKWARD
|
||||||
|
QGLViewer::MouseAction is proceeded.
|
||||||
|
|
||||||
|
\attention When the ManipulatedCameraFrame is set as the Camera::frame(), this value is set
|
||||||
|
according to the QGLViewer::sceneRadius() by QGLViewer::setSceneRadius(). */
|
||||||
|
qreal flySpeed() const { return flySpeed_; }
|
||||||
|
|
||||||
|
/*! Returns the up vector of the scene, expressed in the world coordinate system.
|
||||||
|
|
||||||
|
In 'fly mode' (corresponding to the QGLViewer::MOVE_FORWARD and QGLViewer::MOVE_BACKWARD
|
||||||
|
QGLViewer::MouseAction bindings), horizontal displacements of the mouse rotate
|
||||||
|
the ManipulatedCameraFrame around this vector. Vertical displacements rotate always around the
|
||||||
|
Camera \c X axis.
|
||||||
|
|
||||||
|
This value is also used when setRotationIsConstrained() is set to \c true to define the up vector
|
||||||
|
(and incidentally the 'horizon' plane) around which the camera will rotate.
|
||||||
|
|
||||||
|
Default value is (0,1,0), but it is updated by the Camera when this object is set as its Camera::frame().
|
||||||
|
Camera::setOrientation() and Camera::setUpVector()) direclty modify this value and should be used
|
||||||
|
instead. */
|
||||||
|
Vec sceneUpVector() const { return sceneUpVector_; }
|
||||||
|
|
||||||
|
#ifndef DOXYGEN
|
||||||
|
Vec flyUpVector() const;
|
||||||
|
void setFlyUpVector(const Vec& up);
|
||||||
|
#endif
|
||||||
|
//@}
|
||||||
|
|
||||||
|
/*! @name Mouse event handlers */
|
||||||
|
//@{
|
||||||
|
protected:
|
||||||
|
virtual void mouseReleaseEvent(QMouseEvent* const event, Camera* const camera);
|
||||||
|
virtual void mouseMoveEvent (QMouseEvent* const event, Camera* const camera);
|
||||||
|
virtual void wheelEvent (QWheelEvent* const event, Camera* const camera);
|
||||||
|
//@}
|
||||||
|
|
||||||
|
/*! @name Spinning */
|
||||||
|
//@{
|
||||||
|
protected Q_SLOTS:
|
||||||
|
virtual void spin();
|
||||||
|
//@}
|
||||||
|
|
||||||
|
/*! @name XML representation */
|
||||||
|
//@{
|
||||||
|
public:
|
||||||
|
virtual QDomElement domElement(const QString& name, QDomDocument& document) const;
|
||||||
|
public Q_SLOTS:
|
||||||
|
virtual void initFromDOMElement(const QDomElement& element);
|
||||||
|
//@}
|
||||||
|
|
||||||
|
#ifndef DOXYGEN
|
||||||
|
protected:
|
||||||
|
virtual void startAction(int ma, bool withConstraint=true); // int is really a QGLViewer::MouseAction
|
||||||
|
#endif
|
||||||
|
|
||||||
|
private Q_SLOTS:
|
||||||
|
virtual void flyUpdate();
|
||||||
|
|
||||||
|
private:
|
||||||
|
void updateSceneUpVector();
|
||||||
|
Quaternion turnQuaternion(int x, const Camera* const camera);
|
||||||
|
Quaternion pitchYawQuaternion(int x, int y, const Camera* const camera);
|
||||||
|
|
||||||
|
private:
|
||||||
|
// Fly mode data
|
||||||
|
qreal flySpeed_;
|
||||||
|
qreal driveSpeed_;
|
||||||
|
Vec sceneUpVector_;
|
||||||
|
QTimer flyTimer_;
|
||||||
|
|
||||||
|
bool rotatesAroundUpVector_;
|
||||||
|
// Inverse the direction of an horizontal mouse motion. Depends on the projected
|
||||||
|
// screen orientation of the vertical axis when the mouse button is pressed.
|
||||||
|
bool constrainedRotationIsReversed_;
|
||||||
|
|
||||||
|
bool zoomsOnPivotPoint_;
|
||||||
|
|
||||||
|
Vec pivotPoint_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace qglviewer
|
||||||
|
|
||||||
|
#endif // QGLVIEWER_MANIPULATED_CAMERA_FRAME_H
|
||||||
550
QGLViewer/manipulatedFrame.cpp
Normal file
550
QGLViewer/manipulatedFrame.cpp
Normal file
@ -0,0 +1,550 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
|
||||||
|
Copyright (C) 2002-2014 Gilles Debunne. All rights reserved.
|
||||||
|
|
||||||
|
This file is part of the QGLViewer library version 2.6.3.
|
||||||
|
|
||||||
|
http://www.libqglviewer.com - contact@libqglviewer.com
|
||||||
|
|
||||||
|
This file may be used under the terms of the GNU General Public License
|
||||||
|
versions 2.0 or 3.0 as published by the Free Software Foundation and
|
||||||
|
appearing in the LICENSE file included in the packaging of this file.
|
||||||
|
In addition, as a special exception, Gilles Debunne gives you certain
|
||||||
|
additional rights, described in the file GPL_EXCEPTION in this package.
|
||||||
|
|
||||||
|
libQGLViewer uses dual licensing. Commercial/proprietary software must
|
||||||
|
purchase a libQGLViewer Commercial License.
|
||||||
|
|
||||||
|
This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
|
||||||
|
WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
|
||||||
|
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
#include "domUtils.h"
|
||||||
|
#include "manipulatedFrame.h"
|
||||||
|
#include "manipulatedCameraFrame.h"
|
||||||
|
#include "qglviewer.h"
|
||||||
|
#include "camera.h"
|
||||||
|
|
||||||
|
#include <cstdlib>
|
||||||
|
|
||||||
|
#include <QMouseEvent>
|
||||||
|
|
||||||
|
using namespace qglviewer;
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
/*! Default constructor.
|
||||||
|
|
||||||
|
The translation is set to (0,0,0), with an identity rotation (0,0,0,1) (see Frame constructor
|
||||||
|
for details).
|
||||||
|
|
||||||
|
The different sensitivities are set to their default values (see rotationSensitivity(),
|
||||||
|
translationSensitivity(), spinningSensitivity() and wheelSensitivity()). */
|
||||||
|
ManipulatedFrame::ManipulatedFrame()
|
||||||
|
: action_(QGLViewer::NO_MOUSE_ACTION), keepsGrabbingMouse_(false)
|
||||||
|
{
|
||||||
|
// #CONNECTION# initFromDOMElement and accessor docs
|
||||||
|
setRotationSensitivity(1.0);
|
||||||
|
setTranslationSensitivity(1.0);
|
||||||
|
setSpinningSensitivity(0.3);
|
||||||
|
setWheelSensitivity(1.0);
|
||||||
|
setZoomSensitivity(1.0);
|
||||||
|
|
||||||
|
isSpinning_ = false;
|
||||||
|
previousConstraint_ = NULL;
|
||||||
|
|
||||||
|
connect(&spinningTimer_, SIGNAL(timeout()), SLOT(spinUpdate()));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Equal operator. Calls Frame::operator=() and then copy attributes. */
|
||||||
|
ManipulatedFrame& ManipulatedFrame::operator=(const ManipulatedFrame& mf)
|
||||||
|
{
|
||||||
|
Frame::operator=(mf);
|
||||||
|
|
||||||
|
setRotationSensitivity(mf.rotationSensitivity());
|
||||||
|
setTranslationSensitivity(mf.translationSensitivity());
|
||||||
|
setSpinningSensitivity(mf.spinningSensitivity());
|
||||||
|
setWheelSensitivity(mf.wheelSensitivity());
|
||||||
|
setZoomSensitivity(mf.zoomSensitivity());
|
||||||
|
|
||||||
|
mouseSpeed_ = 0.0;
|
||||||
|
dirIsFixed_ = false;
|
||||||
|
keepsGrabbingMouse_ = false;
|
||||||
|
action_ = QGLViewer::NO_MOUSE_ACTION;
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Copy constructor. Performs a deep copy of all attributes using operator=(). */
|
||||||
|
ManipulatedFrame::ManipulatedFrame(const ManipulatedFrame& mf)
|
||||||
|
: Frame(mf), MouseGrabber()
|
||||||
|
{
|
||||||
|
(*this)=mf;
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
/*! Implementation of the MouseGrabber main method.
|
||||||
|
|
||||||
|
The ManipulatedFrame grabsMouse() when the mouse is within a 10 pixels region around its
|
||||||
|
Camera::projectedCoordinatesOf() position().
|
||||||
|
|
||||||
|
See the <a href="../examples/mouseGrabber.html">mouseGrabber example</a> for an illustration. */
|
||||||
|
void ManipulatedFrame::checkIfGrabsMouse(int x, int y, const Camera* const camera)
|
||||||
|
{
|
||||||
|
const int thresold = 10;
|
||||||
|
const Vec proj = camera->projectedCoordinatesOf(position());
|
||||||
|
setGrabsMouse(keepsGrabbingMouse_ || ((fabs(x-proj.x) < thresold) && (fabs(y-proj.y) < thresold)));
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// S t a t e s a v i n g a n d r e s t o r i n g //
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
/*! Returns an XML \c QDomElement that represents the ManipulatedFrame.
|
||||||
|
|
||||||
|
Adds to the Frame::domElement() the ManipulatedFrame specific informations in a \c
|
||||||
|
ManipulatedParameters child QDomElement.
|
||||||
|
|
||||||
|
\p name is the name of the QDomElement tag. \p doc is the \c QDomDocument factory used to create
|
||||||
|
QDomElement.
|
||||||
|
|
||||||
|
Use initFromDOMElement() to restore the ManipulatedFrame state from the resulting \c QDomElement.
|
||||||
|
|
||||||
|
See Vec::domElement() for a complete example. See also Quaternion::domElement(),
|
||||||
|
Camera::domElement()... */
|
||||||
|
QDomElement ManipulatedFrame::domElement(const QString& name, QDomDocument& document) const
|
||||||
|
{
|
||||||
|
QDomElement e = Frame::domElement(name, document);
|
||||||
|
QDomElement mp = document.createElement("ManipulatedParameters");
|
||||||
|
mp.setAttribute("rotSens", QString::number(rotationSensitivity()));
|
||||||
|
mp.setAttribute("transSens", QString::number(translationSensitivity()));
|
||||||
|
mp.setAttribute("spinSens", QString::number(spinningSensitivity()));
|
||||||
|
mp.setAttribute("wheelSens", QString::number(wheelSensitivity()));
|
||||||
|
mp.setAttribute("zoomSens", QString::number(zoomSensitivity()));
|
||||||
|
e.appendChild(mp);
|
||||||
|
return e;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Restores the ManipulatedFrame state from a \c QDomElement created by domElement().
|
||||||
|
|
||||||
|
Fields that are not described in \p element are set to their default values (see
|
||||||
|
ManipulatedFrame()).
|
||||||
|
|
||||||
|
First calls Frame::initFromDOMElement() and then initializes ManipulatedFrame specific parameters.
|
||||||
|
Note that constraint() and referenceFrame() are not restored and are left unchanged.
|
||||||
|
|
||||||
|
See Vec::initFromDOMElement() for a complete code example. */
|
||||||
|
void ManipulatedFrame::initFromDOMElement(const QDomElement& element)
|
||||||
|
{
|
||||||
|
// Not called since it would set constraint() and referenceFrame() to NULL.
|
||||||
|
// *this = ManipulatedFrame();
|
||||||
|
Frame::initFromDOMElement(element);
|
||||||
|
|
||||||
|
stopSpinning();
|
||||||
|
|
||||||
|
QDomElement child=element.firstChild().toElement();
|
||||||
|
while (!child.isNull())
|
||||||
|
{
|
||||||
|
if (child.tagName() == "ManipulatedParameters")
|
||||||
|
{
|
||||||
|
// #CONNECTION# constructor default values and accessor docs
|
||||||
|
setRotationSensitivity (DomUtils::qrealFromDom(child, "rotSens", 1.0));
|
||||||
|
setTranslationSensitivity(DomUtils::qrealFromDom(child, "transSens", 1.0));
|
||||||
|
setSpinningSensitivity (DomUtils::qrealFromDom(child, "spinSens", 0.3));
|
||||||
|
setWheelSensitivity (DomUtils::qrealFromDom(child, "wheelSens", 1.0));
|
||||||
|
setZoomSensitivity (DomUtils::qrealFromDom(child, "zoomSens", 1.0));
|
||||||
|
}
|
||||||
|
child = child.nextSibling().toElement();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// M o u s e h a n d l i n g //
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
/*! Returns \c true when the ManipulatedFrame is being manipulated with the mouse.
|
||||||
|
|
||||||
|
Can be used to change the display of the manipulated object during manipulation.
|
||||||
|
|
||||||
|
When Camera::frame() of the QGLViewer::camera() isManipulated(), QGLViewer::fastDraw() is used in
|
||||||
|
place of QGLViewer::draw() for scene rendering. A simplified drawing will then allow for
|
||||||
|
interactive camera displacements. */
|
||||||
|
bool ManipulatedFrame::isManipulated() const
|
||||||
|
{
|
||||||
|
return action_ != QGLViewer::NO_MOUSE_ACTION;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Starts the spinning of the ManipulatedFrame.
|
||||||
|
|
||||||
|
This method starts a timer that will call spin() every \p updateInterval milliseconds. The
|
||||||
|
ManipulatedFrame isSpinning() until you call stopSpinning(). */
|
||||||
|
void ManipulatedFrame::startSpinning(int updateInterval)
|
||||||
|
{
|
||||||
|
isSpinning_ = true;
|
||||||
|
spinningTimer_.start(updateInterval);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Rotates the ManipulatedFrame by its spinningQuaternion(). Called by a timer when the
|
||||||
|
ManipulatedFrame isSpinning(). */
|
||||||
|
void ManipulatedFrame::spin()
|
||||||
|
{
|
||||||
|
rotate(spinningQuaternion());
|
||||||
|
}
|
||||||
|
|
||||||
|
/* spin() and spinUpdate() differ since spin can be used by itself (for instance by
|
||||||
|
QGLViewer::SCREEN_ROTATE) without a spun emission. Much nicer to use the spinningQuaternion() and
|
||||||
|
hence spin() for these incremental updates. Nothing special to be done for continuous spinning
|
||||||
|
with this design. */
|
||||||
|
void ManipulatedFrame::spinUpdate()
|
||||||
|
{
|
||||||
|
spin();
|
||||||
|
Q_EMIT spun();
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifndef DOXYGEN
|
||||||
|
/*! Protected internal method used to handle mouse events. */
|
||||||
|
void ManipulatedFrame::startAction(int ma, bool withConstraint)
|
||||||
|
{
|
||||||
|
action_ = (QGLViewer::MouseAction)(ma);
|
||||||
|
|
||||||
|
// #CONNECTION# manipulatedFrame::wheelEvent, manipulatedCameraFrame::wheelEvent and mouseReleaseEvent()
|
||||||
|
// restore previous constraint
|
||||||
|
if (withConstraint)
|
||||||
|
previousConstraint_ = NULL;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
previousConstraint_ = constraint();
|
||||||
|
setConstraint(NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (action_)
|
||||||
|
{
|
||||||
|
case QGLViewer::ROTATE:
|
||||||
|
case QGLViewer::SCREEN_ROTATE:
|
||||||
|
mouseSpeed_ = 0.0;
|
||||||
|
stopSpinning();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case QGLViewer::SCREEN_TRANSLATE:
|
||||||
|
dirIsFixed_ = false;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Updates mouse speed, measured in pixels/milliseconds. Should be called by any method which wants to
|
||||||
|
use mouse speed. Currently used to trigger spinning in mouseReleaseEvent(). */
|
||||||
|
void ManipulatedFrame::computeMouseSpeed(const QMouseEvent* const e)
|
||||||
|
{
|
||||||
|
const QPoint delta = (e->pos() - prevPos_);
|
||||||
|
const qreal dist = sqrt(qreal(delta.x()*delta.x() + delta.y()*delta.y()));
|
||||||
|
delay_ = last_move_time.restart();
|
||||||
|
if (delay_ == 0)
|
||||||
|
// Less than a millisecond: assume delay = 1ms
|
||||||
|
mouseSpeed_ = dist;
|
||||||
|
else
|
||||||
|
mouseSpeed_ = dist/delay_;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Return 1 if mouse motion was started horizontally and -1 if it was more vertical. Returns 0 if
|
||||||
|
this could not be determined yet (perfect diagonal motion, rare). */
|
||||||
|
int ManipulatedFrame::mouseOriginalDirection(const QMouseEvent* const e)
|
||||||
|
{
|
||||||
|
static bool horiz = true; // Two simultaneous manipulatedFrame require two mice !
|
||||||
|
|
||||||
|
if (!dirIsFixed_)
|
||||||
|
{
|
||||||
|
const QPoint delta = e->pos() - pressPos_;
|
||||||
|
dirIsFixed_ = abs(delta.x()) != abs(delta.y());
|
||||||
|
horiz = abs(delta.x()) > abs(delta.y());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dirIsFixed_)
|
||||||
|
if (horiz)
|
||||||
|
return 1;
|
||||||
|
else
|
||||||
|
return -1;
|
||||||
|
else
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
qreal ManipulatedFrame::deltaWithPrevPos(QMouseEvent* const event, Camera* const camera) const {
|
||||||
|
qreal dx = qreal(event->x() - prevPos_.x()) / camera->screenWidth();
|
||||||
|
qreal dy = qreal(event->y() - prevPos_.y()) / camera->screenHeight();
|
||||||
|
|
||||||
|
qreal value = fabs(dx) > fabs(dy) ? dx : dy;
|
||||||
|
return value * zoomSensitivity();
|
||||||
|
}
|
||||||
|
|
||||||
|
qreal ManipulatedFrame::wheelDelta(const QWheelEvent* event) const {
|
||||||
|
static const qreal WHEEL_SENSITIVITY_COEF = 8E-4;
|
||||||
|
return event->delta() * wheelSensitivity() * WHEEL_SENSITIVITY_COEF;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ManipulatedFrame::zoom(qreal delta, const Camera * const camera) {
|
||||||
|
Vec trans(0.0, 0.0, (camera->position() - position()).norm() * delta);
|
||||||
|
|
||||||
|
trans = camera->frame()->orientation().rotate(trans);
|
||||||
|
if (referenceFrame())
|
||||||
|
trans = referenceFrame()->transformOf(trans);
|
||||||
|
translate(trans);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // DOXYGEN
|
||||||
|
|
||||||
|
/*! Initiates the ManipulatedFrame mouse manipulation.
|
||||||
|
|
||||||
|
Overloading of MouseGrabber::mousePressEvent(). See also mouseMoveEvent() and mouseReleaseEvent().
|
||||||
|
|
||||||
|
The mouse behavior depends on which button is pressed. See the <a href="../mouse.html">QGLViewer
|
||||||
|
mouse page</a> for details. */
|
||||||
|
void ManipulatedFrame::mousePressEvent(QMouseEvent* const event, Camera* const camera)
|
||||||
|
{
|
||||||
|
Q_UNUSED(camera);
|
||||||
|
|
||||||
|
if (grabsMouse())
|
||||||
|
keepsGrabbingMouse_ = true;
|
||||||
|
|
||||||
|
// #CONNECTION setMouseBinding
|
||||||
|
// action_ should no longer possibly be NO_MOUSE_ACTION since this value is not inserted in mouseBinding_
|
||||||
|
//if (action_ == QGLViewer::NO_MOUSE_ACTION)
|
||||||
|
//event->ignore();
|
||||||
|
|
||||||
|
prevPos_ = pressPos_ = event->pos();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Modifies the ManipulatedFrame according to the mouse motion.
|
||||||
|
|
||||||
|
Actual behavior depends on mouse bindings. See the QGLViewer::MouseAction enum and the <a
|
||||||
|
href="../mouse.html">QGLViewer mouse page</a> for details.
|
||||||
|
|
||||||
|
The \p camera is used to fit the mouse motion with the display parameters (see
|
||||||
|
Camera::screenWidth(), Camera::screenHeight(), Camera::fieldOfView()).
|
||||||
|
|
||||||
|
Emits the manipulated() signal. */
|
||||||
|
void ManipulatedFrame::mouseMoveEvent(QMouseEvent* const event, Camera* const camera)
|
||||||
|
{
|
||||||
|
switch (action_)
|
||||||
|
{
|
||||||
|
case QGLViewer::TRANSLATE:
|
||||||
|
{
|
||||||
|
const QPoint delta = event->pos() - prevPos_;
|
||||||
|
Vec trans(delta.x(), -delta.y(), 0.0);
|
||||||
|
// Scale to fit the screen mouse displacement
|
||||||
|
switch (camera->type())
|
||||||
|
{
|
||||||
|
case Camera::PERSPECTIVE :
|
||||||
|
trans *= 2.0 * tan(camera->fieldOfView()/2.0) * fabs((camera->frame()->coordinatesOf(position())).z) / camera->screenHeight();
|
||||||
|
break;
|
||||||
|
case Camera::ORTHOGRAPHIC :
|
||||||
|
{
|
||||||
|
GLdouble w,h;
|
||||||
|
camera->getOrthoWidthHeight(w, h);
|
||||||
|
trans[0] *= 2.0 * w / camera->screenWidth();
|
||||||
|
trans[1] *= 2.0 * h / camera->screenHeight();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Transform to world coordinate system.
|
||||||
|
trans = camera->frame()->orientation().rotate(translationSensitivity()*trans);
|
||||||
|
// And then down to frame
|
||||||
|
if (referenceFrame()) trans = referenceFrame()->transformOf(trans);
|
||||||
|
translate(trans);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case QGLViewer::ZOOM:
|
||||||
|
{
|
||||||
|
zoom(deltaWithPrevPos(event, camera), camera);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case QGLViewer::SCREEN_ROTATE:
|
||||||
|
{
|
||||||
|
Vec trans = camera->projectedCoordinatesOf(position());
|
||||||
|
|
||||||
|
const qreal prev_angle = atan2(prevPos_.y()-trans[1], prevPos_.x()-trans[0]);
|
||||||
|
const qreal angle = atan2(event->y()-trans[1], event->x()-trans[0]);
|
||||||
|
|
||||||
|
const Vec axis = transformOf(camera->frame()->inverseTransformOf(Vec(0.0, 0.0, -1.0)));
|
||||||
|
Quaternion rot(axis, angle-prev_angle);
|
||||||
|
//#CONNECTION# These two methods should go together (spinning detection and activation)
|
||||||
|
computeMouseSpeed(event);
|
||||||
|
setSpinningQuaternion(rot);
|
||||||
|
spin();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case QGLViewer::SCREEN_TRANSLATE:
|
||||||
|
{
|
||||||
|
Vec trans;
|
||||||
|
int dir = mouseOriginalDirection(event);
|
||||||
|
if (dir == 1)
|
||||||
|
trans.setValue(event->x() - prevPos_.x(), 0.0, 0.0);
|
||||||
|
else if (dir == -1)
|
||||||
|
trans.setValue(0.0, prevPos_.y() - event->y(), 0.0);
|
||||||
|
|
||||||
|
switch (camera->type())
|
||||||
|
{
|
||||||
|
case Camera::PERSPECTIVE :
|
||||||
|
trans *= 2.0 * tan(camera->fieldOfView()/2.0) * fabs((camera->frame()->coordinatesOf(position())).z) / camera->screenHeight();
|
||||||
|
break;
|
||||||
|
case Camera::ORTHOGRAPHIC :
|
||||||
|
{
|
||||||
|
GLdouble w,h;
|
||||||
|
camera->getOrthoWidthHeight(w, h);
|
||||||
|
trans[0] *= 2.0 * w / camera->screenWidth();
|
||||||
|
trans[1] *= 2.0 * h / camera->screenHeight();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Transform to world coordinate system.
|
||||||
|
trans = camera->frame()->orientation().rotate(translationSensitivity()*trans);
|
||||||
|
// And then down to frame
|
||||||
|
if (referenceFrame())
|
||||||
|
trans = referenceFrame()->transformOf(trans);
|
||||||
|
|
||||||
|
translate(trans);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case QGLViewer::ROTATE:
|
||||||
|
{
|
||||||
|
Vec trans = camera->projectedCoordinatesOf(position());
|
||||||
|
Quaternion rot = deformedBallQuaternion(event->x(), event->y(), trans[0], trans[1], camera);
|
||||||
|
trans = Vec(-rot[0], -rot[1], -rot[2]);
|
||||||
|
trans = camera->frame()->orientation().rotate(trans);
|
||||||
|
trans = transformOf(trans);
|
||||||
|
rot[0] = trans[0];
|
||||||
|
rot[1] = trans[1];
|
||||||
|
rot[2] = trans[2];
|
||||||
|
//#CONNECTION# These two methods should go together (spinning detection and activation)
|
||||||
|
computeMouseSpeed(event);
|
||||||
|
setSpinningQuaternion(rot);
|
||||||
|
spin();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case QGLViewer::MOVE_FORWARD:
|
||||||
|
case QGLViewer::MOVE_BACKWARD:
|
||||||
|
case QGLViewer::LOOK_AROUND:
|
||||||
|
case QGLViewer::ROLL:
|
||||||
|
case QGLViewer::DRIVE:
|
||||||
|
case QGLViewer::ZOOM_ON_REGION:
|
||||||
|
// These MouseAction values make no sense for a manipulatedFrame
|
||||||
|
break;
|
||||||
|
|
||||||
|
case QGLViewer::NO_MOUSE_ACTION:
|
||||||
|
// Possible when the ManipulatedFrame is a MouseGrabber. This method is then called without startAction
|
||||||
|
// because of mouseTracking.
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (action_ != QGLViewer::NO_MOUSE_ACTION)
|
||||||
|
{
|
||||||
|
prevPos_ = event->pos();
|
||||||
|
Q_EMIT manipulated();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Stops the ManipulatedFrame mouse manipulation.
|
||||||
|
|
||||||
|
Overloading of MouseGrabber::mouseReleaseEvent().
|
||||||
|
|
||||||
|
If the action was a QGLViewer::ROTATE QGLViewer::MouseAction, a continuous spinning is possible if
|
||||||
|
the speed of the mouse cursor is larger than spinningSensitivity() when the button is released.
|
||||||
|
Press the rotate button again to stop spinning. See startSpinning() and isSpinning(). */
|
||||||
|
void ManipulatedFrame::mouseReleaseEvent(QMouseEvent* const event, Camera* const camera)
|
||||||
|
{
|
||||||
|
Q_UNUSED(event);
|
||||||
|
Q_UNUSED(camera);
|
||||||
|
|
||||||
|
keepsGrabbingMouse_ = false;
|
||||||
|
|
||||||
|
if (previousConstraint_)
|
||||||
|
setConstraint(previousConstraint_);
|
||||||
|
|
||||||
|
if (((action_ == QGLViewer::ROTATE) || (action_ == QGLViewer::SCREEN_ROTATE)) && (mouseSpeed_ >= spinningSensitivity()))
|
||||||
|
startSpinning(delay_);
|
||||||
|
|
||||||
|
action_ = QGLViewer::NO_MOUSE_ACTION;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Overloading of MouseGrabber::mouseDoubleClickEvent().
|
||||||
|
|
||||||
|
Left button double click aligns the ManipulatedFrame with the \p camera axis (see alignWithFrame()
|
||||||
|
and QGLViewer::ALIGN_FRAME). Right button projects the ManipulatedFrame on the \p camera view
|
||||||
|
direction. */
|
||||||
|
void ManipulatedFrame::mouseDoubleClickEvent(QMouseEvent* const event, Camera* const camera)
|
||||||
|
{
|
||||||
|
if (event->modifiers() == Qt::NoModifier)
|
||||||
|
switch (event->button())
|
||||||
|
{
|
||||||
|
case Qt::LeftButton: alignWithFrame(camera->frame()); break;
|
||||||
|
case Qt::RightButton: projectOnLine(camera->position(), camera->viewDirection()); break;
|
||||||
|
default: break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Overloading of MouseGrabber::wheelEvent().
|
||||||
|
|
||||||
|
Using the wheel is equivalent to a QGLViewer::ZOOM QGLViewer::MouseAction. See
|
||||||
|
QGLViewer::setWheelBinding(), setWheelSensitivity(). */
|
||||||
|
void ManipulatedFrame::wheelEvent(QWheelEvent* const event, Camera* const camera)
|
||||||
|
{
|
||||||
|
//#CONNECTION# QGLViewer::setWheelBinding
|
||||||
|
if (action_ == QGLViewer::ZOOM)
|
||||||
|
{
|
||||||
|
zoom(wheelDelta(event), camera);
|
||||||
|
Q_EMIT manipulated();
|
||||||
|
}
|
||||||
|
|
||||||
|
// #CONNECTION# startAction should always be called before
|
||||||
|
if (previousConstraint_)
|
||||||
|
setConstraint(previousConstraint_);
|
||||||
|
|
||||||
|
action_ = QGLViewer::NO_MOUSE_ACTION;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
/*! Returns "pseudo-distance" from (x,y) to ball of radius size.
|
||||||
|
\arg for a point inside the ball, it is proportional to the euclidean distance to the ball
|
||||||
|
\arg for a point outside the ball, it is proportional to the inverse of this distance (tends to
|
||||||
|
zero) on the ball, the function is continuous. */
|
||||||
|
static qreal projectOnBall(qreal x, qreal y)
|
||||||
|
{
|
||||||
|
// If you change the size value, change angle computation in deformedBallQuaternion().
|
||||||
|
const qreal size = 1.0;
|
||||||
|
const qreal size2 = size*size;
|
||||||
|
const qreal size_limit = size2*0.5;
|
||||||
|
|
||||||
|
const qreal d = x*x + y*y;
|
||||||
|
return d < size_limit ? sqrt(size2 - d) : size_limit/sqrt(d);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifndef DOXYGEN
|
||||||
|
/*! Returns a quaternion computed according to the mouse motion. Mouse positions are projected on a
|
||||||
|
deformed ball, centered on (\p cx,\p cy). */
|
||||||
|
Quaternion ManipulatedFrame::deformedBallQuaternion(int x, int y, qreal cx, qreal cy, const Camera* const camera)
|
||||||
|
{
|
||||||
|
// Points on the deformed ball
|
||||||
|
qreal px = rotationSensitivity() * (prevPos_.x() - cx) / camera->screenWidth();
|
||||||
|
qreal py = rotationSensitivity() * (cy - prevPos_.y()) / camera->screenHeight();
|
||||||
|
qreal dx = rotationSensitivity() * (x - cx) / camera->screenWidth();
|
||||||
|
qreal dy = rotationSensitivity() * (cy - y) / camera->screenHeight();
|
||||||
|
|
||||||
|
const Vec p1(px, py, projectOnBall(px, py));
|
||||||
|
const Vec p2(dx, dy, projectOnBall(dx, dy));
|
||||||
|
// Approximation of rotation angle
|
||||||
|
// Should be divided by the projectOnBall size, but it is 1.0
|
||||||
|
const Vec axis = cross(p2,p1);
|
||||||
|
const qreal angle = 5.0 * asin(sqrt(axis.squaredNorm() / p1.squaredNorm() / p2.squaredNorm()));
|
||||||
|
return Quaternion(axis, angle);
|
||||||
|
}
|
||||||
|
#endif // DOXYGEN
|
||||||
335
QGLViewer/manipulatedFrame.h
Normal file
335
QGLViewer/manipulatedFrame.h
Normal file
@ -0,0 +1,335 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
|
||||||
|
Copyright (C) 2002-2014 Gilles Debunne. All rights reserved.
|
||||||
|
|
||||||
|
This file is part of the QGLViewer library version 2.6.3.
|
||||||
|
|
||||||
|
http://www.libqglviewer.com - contact@libqglviewer.com
|
||||||
|
|
||||||
|
This file may be used under the terms of the GNU General Public License
|
||||||
|
versions 2.0 or 3.0 as published by the Free Software Foundation and
|
||||||
|
appearing in the LICENSE file included in the packaging of this file.
|
||||||
|
In addition, as a special exception, Gilles Debunne gives you certain
|
||||||
|
additional rights, described in the file GPL_EXCEPTION in this package.
|
||||||
|
|
||||||
|
libQGLViewer uses dual licensing. Commercial/proprietary software must
|
||||||
|
purchase a libQGLViewer Commercial License.
|
||||||
|
|
||||||
|
This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
|
||||||
|
WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
|
||||||
|
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
#ifndef QGLVIEWER_MANIPULATED_FRAME_H
|
||||||
|
#define QGLVIEWER_MANIPULATED_FRAME_H
|
||||||
|
|
||||||
|
#include "frame.h"
|
||||||
|
#include "mouseGrabber.h"
|
||||||
|
#include "qglviewer.h"
|
||||||
|
|
||||||
|
#include <QString>
|
||||||
|
#include <QTimer>
|
||||||
|
#include <QDateTime>
|
||||||
|
|
||||||
|
namespace qglviewer {
|
||||||
|
/*! \brief A ManipulatedFrame is a Frame that can be rotated and translated using the mouse.
|
||||||
|
\class ManipulatedFrame manipulatedFrame.h QGLViewer/manipulatedFrame.h
|
||||||
|
|
||||||
|
It converts the mouse motion into a translation and an orientation updates. A ManipulatedFrame is
|
||||||
|
used to move an object in the scene. Combined with object selection, its MouseGrabber properties
|
||||||
|
and a dynamic update of the scene, the ManipulatedFrame introduces a great reactivity in your
|
||||||
|
applications.
|
||||||
|
|
||||||
|
A ManipulatedFrame is attached to a QGLViewer using QGLViewer::setManipulatedFrame():
|
||||||
|
\code
|
||||||
|
init() { setManipulatedFrame( new ManipulatedFrame() ); }
|
||||||
|
|
||||||
|
draw()
|
||||||
|
{
|
||||||
|
glPushMatrix();
|
||||||
|
glMultMatrixd(manipulatedFrame()->matrix());
|
||||||
|
// draw the manipulated object here
|
||||||
|
glPopMatrix();
|
||||||
|
}
|
||||||
|
\endcode
|
||||||
|
See the <a href="../examples/manipulatedFrame.html">manipulatedFrame example</a> for a complete
|
||||||
|
application.
|
||||||
|
|
||||||
|
Mouse events are normally sent to the QGLViewer::camera(). You have to press the QGLViewer::FRAME
|
||||||
|
state key (default is \c Control) to move the QGLViewer::manipulatedFrame() instead. See the <a
|
||||||
|
href="../mouse.html">mouse page</a> for a description of mouse button bindings.
|
||||||
|
|
||||||
|
<h3>Inherited functionalities</h3>
|
||||||
|
|
||||||
|
A ManipulatedFrame is an overloaded instance of a Frame. The powerful coordinate system
|
||||||
|
transformation functions (Frame::coordinatesOf(), Frame::transformOf(), ...) can hence be applied
|
||||||
|
to a ManipulatedFrame.
|
||||||
|
|
||||||
|
A ManipulatedFrame is also a MouseGrabber. If the mouse cursor gets within a distance of 10 pixels
|
||||||
|
from the projected position of the ManipulatedFrame, the ManipulatedFrame becomes the new
|
||||||
|
QGLViewer::mouseGrabber(). It can then be manipulated directly, without any specific state key,
|
||||||
|
object selection or GUI intervention. This is very convenient to directly move some objects in the
|
||||||
|
scene (typically a light). See the <a href="../examples/mouseGrabber.html">mouseGrabber
|
||||||
|
example</a> as an illustration. Note that QWidget::setMouseTracking() needs to be enabled in order
|
||||||
|
to use this feature (see the MouseGrabber documentation).
|
||||||
|
|
||||||
|
<h3>Advanced functionalities</h3>
|
||||||
|
|
||||||
|
A QGLViewer can handle at most one ManipulatedFrame at a time. If you want to move several objects
|
||||||
|
in the scene, you simply have to keep a list of the different ManipulatedFrames, and to activate
|
||||||
|
the right one (using QGLViewer::setManipulatedFrame()) when needed. This can for instance be done
|
||||||
|
according to an object selection: see the <a href="../examples/luxo.html">luxo example</a> for an
|
||||||
|
illustration.
|
||||||
|
|
||||||
|
When the ManipulatedFrame is being manipulated using the mouse (mouse pressed and not yet
|
||||||
|
released), isManipulated() returns \c true. This might be used to trigger a specific action or
|
||||||
|
display (as is done with QGLViewer::fastDraw()).
|
||||||
|
|
||||||
|
The ManipulatedFrame also emits a manipulated() signal each time its state is modified by the
|
||||||
|
mouse. This signal is automatically connected to the QGLViewer::update() slot when the
|
||||||
|
ManipulatedFrame is attached to a viewer using QGLViewer::setManipulatedFrame().
|
||||||
|
|
||||||
|
You can make the ManipulatedFrame spin() if you release the rotation mouse button while moving the
|
||||||
|
mouse fast enough (see spinningSensitivity()). See also translationSensitivity() and
|
||||||
|
rotationSensitivity() for sensitivity tuning. \nosubgrouping */
|
||||||
|
class QGLVIEWER_EXPORT ManipulatedFrame : public Frame, public MouseGrabber
|
||||||
|
{
|
||||||
|
#ifndef DOXYGEN
|
||||||
|
friend class Camera;
|
||||||
|
friend class ::QGLViewer;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
ManipulatedFrame();
|
||||||
|
/*! Virtual destructor. Empty. */
|
||||||
|
virtual ~ManipulatedFrame() {}
|
||||||
|
|
||||||
|
ManipulatedFrame(const ManipulatedFrame& mf);
|
||||||
|
ManipulatedFrame& operator=(const ManipulatedFrame& mf);
|
||||||
|
|
||||||
|
Q_SIGNALS:
|
||||||
|
/*! This signal is emitted when ever the ManipulatedFrame is manipulated (i.e. rotated or
|
||||||
|
translated) using the mouse. Connect this signal to any object that should be notified.
|
||||||
|
|
||||||
|
Note that this signal is automatically connected to the QGLViewer::update() slot, when the
|
||||||
|
ManipulatedFrame is attached to a viewer using QGLViewer::setManipulatedFrame(), which is
|
||||||
|
probably all you need.
|
||||||
|
|
||||||
|
Use the QGLViewer::QGLViewerPool() if you need to connect this signal to all the viewers.
|
||||||
|
|
||||||
|
See also the spun(), modified(), interpolated() and KeyFrameInterpolator::interpolated()
|
||||||
|
signals' documentations. */
|
||||||
|
void manipulated();
|
||||||
|
|
||||||
|
/*! This signal is emitted when the ManipulatedFrame isSpinning().
|
||||||
|
|
||||||
|
Note that for the QGLViewer::manipulatedFrame(), this signal is automatically connected to the
|
||||||
|
QGLViewer::update() slot.
|
||||||
|
|
||||||
|
Connect this signal to any object that should be notified. Use the QGLViewer::QGLViewerPool() if
|
||||||
|
you need to connect this signal to all the viewers.
|
||||||
|
|
||||||
|
See also the manipulated(), modified(), interpolated() and KeyFrameInterpolator::interpolated()
|
||||||
|
signals' documentations. */
|
||||||
|
void spun();
|
||||||
|
|
||||||
|
/*! @name Manipulation sensitivity */
|
||||||
|
//@{
|
||||||
|
public Q_SLOTS:
|
||||||
|
/*! Defines the rotationSensitivity(). */
|
||||||
|
void setRotationSensitivity(qreal sensitivity) { rotationSensitivity_ = sensitivity; }
|
||||||
|
/*! Defines the translationSensitivity(). */
|
||||||
|
void setTranslationSensitivity(qreal sensitivity) { translationSensitivity_ = sensitivity; }
|
||||||
|
/*! Defines the spinningSensitivity(), in pixels per milliseconds. */
|
||||||
|
void setSpinningSensitivity(qreal sensitivity) { spinningSensitivity_ = sensitivity; }
|
||||||
|
/*! Defines the wheelSensitivity(). */
|
||||||
|
void setWheelSensitivity(qreal sensitivity) { wheelSensitivity_ = sensitivity; }
|
||||||
|
/*! Defines the zoomSensitivity(). */
|
||||||
|
void setZoomSensitivity(qreal sensitivity) { zoomSensitivity_ = sensitivity; }
|
||||||
|
|
||||||
|
public:
|
||||||
|
/*! Returns the influence of a mouse displacement on the ManipulatedFrame rotation.
|
||||||
|
|
||||||
|
Default value is 1.0. With an identical mouse displacement, a higher value will generate a
|
||||||
|
larger rotation (and inversely for lower values). A 0.0 value will forbid ManipulatedFrame mouse
|
||||||
|
rotation (see also constraint()).
|
||||||
|
|
||||||
|
See also setRotationSensitivity(), translationSensitivity(), spinningSensitivity() and
|
||||||
|
wheelSensitivity(). */
|
||||||
|
qreal rotationSensitivity() const { return rotationSensitivity_; }
|
||||||
|
/*! Returns the influence of a mouse displacement on the ManipulatedFrame translation.
|
||||||
|
|
||||||
|
Default value is 1.0. You should not have to modify this value, since with 1.0 the
|
||||||
|
ManipulatedFrame precisely stays under the mouse cursor.
|
||||||
|
|
||||||
|
With an identical mouse displacement, a higher value will generate a larger translation (and
|
||||||
|
inversely for lower values). A 0.0 value will forbid ManipulatedFrame mouse translation (see
|
||||||
|
also constraint()).
|
||||||
|
|
||||||
|
\note When the ManipulatedFrame is used to move a \e Camera (see the ManipulatedCameraFrame
|
||||||
|
class documentation), after zooming on a small region of your scene, the camera may translate
|
||||||
|
too fast. For a camera, it is the Camera::pivotPoint() that exactly matches the mouse
|
||||||
|
displacement. Hence, instead of changing the translationSensitivity(), solve the problem by
|
||||||
|
(temporarily) setting the Camera::pivotPoint() to a point on the zoomed region (see the
|
||||||
|
QGLViewer::RAP_FROM_PIXEL mouse binding in the <a href="../mouse.html">mouse page</a>).
|
||||||
|
|
||||||
|
See also setTranslationSensitivity(), rotationSensitivity(), spinningSensitivity() and
|
||||||
|
wheelSensitivity(). */
|
||||||
|
qreal translationSensitivity() const { return translationSensitivity_; }
|
||||||
|
/*! Returns the minimum mouse speed required (at button release) to make the ManipulatedFrame
|
||||||
|
spin().
|
||||||
|
|
||||||
|
See spin(), spinningQuaternion() and startSpinning() for details.
|
||||||
|
|
||||||
|
Mouse speed is expressed in pixels per milliseconds. Default value is 0.3 (300 pixels per
|
||||||
|
second). Use setSpinningSensitivity() to tune this value. A higher value will make spinning more
|
||||||
|
difficult (a value of 100.0 forbids spinning in practice).
|
||||||
|
|
||||||
|
See also setSpinningSensitivity(), translationSensitivity(), rotationSensitivity() and
|
||||||
|
wheelSensitivity(). */
|
||||||
|
qreal spinningSensitivity() const { return spinningSensitivity_; }
|
||||||
|
|
||||||
|
/*! Returns the zoom sensitivity.
|
||||||
|
|
||||||
|
Default value is 1.0. A higher value will make the zoom faster.
|
||||||
|
Use a negative value to invert the zoom in and out directions.
|
||||||
|
|
||||||
|
See also setZoomSensitivity(), translationSensitivity(), rotationSensitivity() wheelSensitivity()
|
||||||
|
and spinningSensitivity(). */
|
||||||
|
qreal zoomSensitivity() const { return zoomSensitivity_; }
|
||||||
|
/*! Returns the mouse wheel sensitivity.
|
||||||
|
|
||||||
|
Default value is 1.0. A higher value will make the wheel action more efficient (usually meaning
|
||||||
|
a faster zoom). Use a negative value to invert the zoom in and out directions.
|
||||||
|
|
||||||
|
See also setWheelSensitivity(), translationSensitivity(), rotationSensitivity() zoomSensitivity()
|
||||||
|
and spinningSensitivity(). */
|
||||||
|
qreal wheelSensitivity() const { return wheelSensitivity_; }
|
||||||
|
//@}
|
||||||
|
|
||||||
|
|
||||||
|
/*! @name Spinning */
|
||||||
|
//@{
|
||||||
|
public:
|
||||||
|
/*! Returns \c true when the ManipulatedFrame is spinning.
|
||||||
|
|
||||||
|
During spinning, spin() rotates the ManipulatedFrame by its spinningQuaternion() at a frequency
|
||||||
|
defined when the ManipulatedFrame startSpinning().
|
||||||
|
|
||||||
|
Use startSpinning() and stopSpinning() to change this state. Default value is \c false. */
|
||||||
|
bool isSpinning() const { return isSpinning_; }
|
||||||
|
/*! Returns the incremental rotation that is applied by spin() to the ManipulatedFrame
|
||||||
|
orientation when it isSpinning().
|
||||||
|
|
||||||
|
Default value is a null rotation (identity Quaternion). Use setSpinningQuaternion() to change
|
||||||
|
this value.
|
||||||
|
|
||||||
|
The spinningQuaternion() axis is defined in the ManipulatedFrame coordinate system. You can use
|
||||||
|
Frame::transformOfFrom() to convert this axis from an other Frame coordinate system. */
|
||||||
|
Quaternion spinningQuaternion() const { return spinningQuaternion_; }
|
||||||
|
public Q_SLOTS:
|
||||||
|
/*! Defines the spinningQuaternion(). Its axis is defined in the ManipulatedFrame coordinate
|
||||||
|
system. */
|
||||||
|
void setSpinningQuaternion(const Quaternion& spinningQuaternion) { spinningQuaternion_ = spinningQuaternion; }
|
||||||
|
virtual void startSpinning(int updateInterval);
|
||||||
|
/*! Stops the spinning motion started using startSpinning(). isSpinning() will return \c false
|
||||||
|
after this call. */
|
||||||
|
virtual void stopSpinning() { spinningTimer_.stop(); isSpinning_ = false; }
|
||||||
|
protected Q_SLOTS:
|
||||||
|
virtual void spin();
|
||||||
|
private Q_SLOTS:
|
||||||
|
void spinUpdate();
|
||||||
|
//@}
|
||||||
|
|
||||||
|
/*! @name Mouse event handlers */
|
||||||
|
//@{
|
||||||
|
protected:
|
||||||
|
virtual void mousePressEvent (QMouseEvent* const event, Camera* const camera);
|
||||||
|
virtual void mouseMoveEvent (QMouseEvent* const event, Camera* const camera);
|
||||||
|
virtual void mouseReleaseEvent (QMouseEvent* const event, Camera* const camera);
|
||||||
|
virtual void mouseDoubleClickEvent(QMouseEvent* const event, Camera* const camera);
|
||||||
|
virtual void wheelEvent (QWheelEvent* const event, Camera* const camera);
|
||||||
|
//@}
|
||||||
|
|
||||||
|
public:
|
||||||
|
/*! @name Current state */
|
||||||
|
//@{
|
||||||
|
bool isManipulated() const;
|
||||||
|
/*! Returns the \c MouseAction currently applied to this ManipulatedFrame.
|
||||||
|
|
||||||
|
Will return QGLViewer::NO_MOUSE_ACTION unless a mouse button is being pressed
|
||||||
|
and has been bound to this QGLViewer::MouseHandler.
|
||||||
|
|
||||||
|
The binding between mouse buttons and key modifiers and MouseAction is set using
|
||||||
|
QGLViewer::setMouseBinding(Qt::Key key, Qt::KeyboardModifiers modifiers, Qt::MouseButton buttons, MouseHandler handler, MouseAction action, bool withConstraint).
|
||||||
|
*/
|
||||||
|
QGLViewer::MouseAction currentMouseAction() const { return action_; }
|
||||||
|
//@}
|
||||||
|
|
||||||
|
/*! @name MouseGrabber implementation */
|
||||||
|
//@{
|
||||||
|
public:
|
||||||
|
virtual void checkIfGrabsMouse(int x, int y, const Camera* const camera);
|
||||||
|
//@}
|
||||||
|
|
||||||
|
/*! @name XML representation */
|
||||||
|
//@{
|
||||||
|
public:
|
||||||
|
virtual QDomElement domElement(const QString& name, QDomDocument& document) const;
|
||||||
|
public Q_SLOTS:
|
||||||
|
virtual void initFromDOMElement(const QDomElement& element);
|
||||||
|
//@}
|
||||||
|
|
||||||
|
#ifndef DOXYGEN
|
||||||
|
protected:
|
||||||
|
Quaternion deformedBallQuaternion(int x, int y, qreal cx, qreal cy, const Camera* const camera);
|
||||||
|
|
||||||
|
QGLViewer::MouseAction action_;
|
||||||
|
Constraint* previousConstraint_; // When manipulation is without Contraint.
|
||||||
|
|
||||||
|
virtual void startAction(int ma, bool withConstraint=true); // int is really a QGLViewer::MouseAction
|
||||||
|
void computeMouseSpeed(const QMouseEvent* const e);
|
||||||
|
int mouseOriginalDirection(const QMouseEvent* const e);
|
||||||
|
|
||||||
|
/*! Returns a screen scaled delta from event's position to prevPos_, along the
|
||||||
|
X or Y direction, whichever has the largest magnitude. */
|
||||||
|
qreal deltaWithPrevPos(QMouseEvent* const event, Camera* const camera) const;
|
||||||
|
/*! Returns a normalized wheel delta, proportionnal to wheelSensitivity(). */
|
||||||
|
qreal wheelDelta(const QWheelEvent* event) const;
|
||||||
|
|
||||||
|
// Previous mouse position (used for incremental updates) and mouse press position.
|
||||||
|
QPoint prevPos_, pressPos_;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void zoom(qreal delta, const Camera * const camera);
|
||||||
|
|
||||||
|
#endif // DOXYGEN
|
||||||
|
|
||||||
|
private:
|
||||||
|
// Sensitivity
|
||||||
|
qreal rotationSensitivity_;
|
||||||
|
qreal translationSensitivity_;
|
||||||
|
qreal spinningSensitivity_;
|
||||||
|
qreal wheelSensitivity_;
|
||||||
|
qreal zoomSensitivity_;
|
||||||
|
|
||||||
|
// Mouse speed and spinning
|
||||||
|
QTime last_move_time;
|
||||||
|
qreal mouseSpeed_;
|
||||||
|
int delay_;
|
||||||
|
bool isSpinning_;
|
||||||
|
QTimer spinningTimer_;
|
||||||
|
Quaternion spinningQuaternion_;
|
||||||
|
|
||||||
|
// Whether the SCREEN_TRANS direction (horizontal or vertical) is fixed or not.
|
||||||
|
bool dirIsFixed_;
|
||||||
|
|
||||||
|
// MouseGrabber
|
||||||
|
bool keepsGrabbingMouse_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace qglviewer
|
||||||
|
|
||||||
|
#endif // QGLVIEWER_MANIPULATED_FRAME_H
|
||||||
76
QGLViewer/mouseGrabber.cpp
Normal file
76
QGLViewer/mouseGrabber.cpp
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
|
||||||
|
Copyright (C) 2002-2014 Gilles Debunne. All rights reserved.
|
||||||
|
|
||||||
|
This file is part of the QGLViewer library version 2.6.3.
|
||||||
|
|
||||||
|
http://www.libqglviewer.com - contact@libqglviewer.com
|
||||||
|
|
||||||
|
This file may be used under the terms of the GNU General Public License
|
||||||
|
versions 2.0 or 3.0 as published by the Free Software Foundation and
|
||||||
|
appearing in the LICENSE file included in the packaging of this file.
|
||||||
|
In addition, as a special exception, Gilles Debunne gives you certain
|
||||||
|
additional rights, described in the file GPL_EXCEPTION in this package.
|
||||||
|
|
||||||
|
libQGLViewer uses dual licensing. Commercial/proprietary software must
|
||||||
|
purchase a libQGLViewer Commercial License.
|
||||||
|
|
||||||
|
This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
|
||||||
|
WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
|
||||||
|
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
#include "mouseGrabber.h"
|
||||||
|
|
||||||
|
using namespace qglviewer;
|
||||||
|
|
||||||
|
// Static private variable
|
||||||
|
QList<MouseGrabber*> MouseGrabber::MouseGrabberPool_;
|
||||||
|
|
||||||
|
/*! Default constructor.
|
||||||
|
|
||||||
|
Adds the created MouseGrabber in the MouseGrabberPool(). grabsMouse() is set to \c false. */
|
||||||
|
MouseGrabber::MouseGrabber()
|
||||||
|
: grabsMouse_(false)
|
||||||
|
{
|
||||||
|
addInMouseGrabberPool();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Adds the MouseGrabber in the MouseGrabberPool().
|
||||||
|
|
||||||
|
All created MouseGrabber are automatically added in the MouseGrabberPool() by the constructor.
|
||||||
|
Trying to add a MouseGrabber that already isInMouseGrabberPool() has no effect.
|
||||||
|
|
||||||
|
Use removeFromMouseGrabberPool() to remove the MouseGrabber from the list, so that it is no longer
|
||||||
|
tested with checkIfGrabsMouse() by the QGLViewer, and hence can no longer grab mouse focus. Use
|
||||||
|
isInMouseGrabberPool() to know the current state of the MouseGrabber. */
|
||||||
|
void MouseGrabber::addInMouseGrabberPool()
|
||||||
|
{
|
||||||
|
if (!isInMouseGrabberPool())
|
||||||
|
MouseGrabber::MouseGrabberPool_.append(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Removes the MouseGrabber from the MouseGrabberPool().
|
||||||
|
|
||||||
|
See addInMouseGrabberPool() for details. Removing a MouseGrabber that is not in MouseGrabberPool()
|
||||||
|
has no effect. */
|
||||||
|
void MouseGrabber::removeFromMouseGrabberPool()
|
||||||
|
{
|
||||||
|
if (isInMouseGrabberPool())
|
||||||
|
MouseGrabber::MouseGrabberPool_.removeAll(const_cast<MouseGrabber*>(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Clears the MouseGrabberPool().
|
||||||
|
|
||||||
|
Use this method only if it is faster to clear the MouseGrabberPool() and then to add back a few
|
||||||
|
MouseGrabbers than to remove each one independently. Use QGLViewer::setMouseTracking(false) instead
|
||||||
|
if you want to disable mouse grabbing.
|
||||||
|
|
||||||
|
When \p autoDelete is \c true, the MouseGrabbers of the MouseGrabberPool() are actually deleted
|
||||||
|
(use this only if you're sure of what you do). */
|
||||||
|
void MouseGrabber::clearMouseGrabberPool(bool autoDelete)
|
||||||
|
{
|
||||||
|
if (autoDelete)
|
||||||
|
qDeleteAll(MouseGrabber::MouseGrabberPool_);
|
||||||
|
MouseGrabber::MouseGrabberPool_.clear();
|
||||||
|
}
|
||||||
264
QGLViewer/mouseGrabber.h
Normal file
264
QGLViewer/mouseGrabber.h
Normal file
@ -0,0 +1,264 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
|
||||||
|
Copyright (C) 2002-2014 Gilles Debunne. All rights reserved.
|
||||||
|
|
||||||
|
This file is part of the QGLViewer library version 2.6.3.
|
||||||
|
|
||||||
|
http://www.libqglviewer.com - contact@libqglviewer.com
|
||||||
|
|
||||||
|
This file may be used under the terms of the GNU General Public License
|
||||||
|
versions 2.0 or 3.0 as published by the Free Software Foundation and
|
||||||
|
appearing in the LICENSE file included in the packaging of this file.
|
||||||
|
In addition, as a special exception, Gilles Debunne gives you certain
|
||||||
|
additional rights, described in the file GPL_EXCEPTION in this package.
|
||||||
|
|
||||||
|
libQGLViewer uses dual licensing. Commercial/proprietary software must
|
||||||
|
purchase a libQGLViewer Commercial License.
|
||||||
|
|
||||||
|
This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
|
||||||
|
WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
|
||||||
|
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
#ifndef QGLVIEWER_MOUSE_GRABBER_H
|
||||||
|
#define QGLVIEWER_MOUSE_GRABBER_H
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
#include <QEvent>
|
||||||
|
|
||||||
|
class QGLViewer;
|
||||||
|
|
||||||
|
namespace qglviewer {
|
||||||
|
class Camera;
|
||||||
|
|
||||||
|
/*! \brief Abstract class for objects that grab mouse focus in a QGLViewer.
|
||||||
|
\class MouseGrabber mouseGrabber.h QGLViewer/mouseGrabber.h
|
||||||
|
|
||||||
|
MouseGrabber are objects which react to the mouse cursor, usually when it hovers over them. This
|
||||||
|
abstract class only provides an interface for all these objects: their actual behavior has to be
|
||||||
|
defined in a derived class.
|
||||||
|
|
||||||
|
<h3>How does it work ?</h3>
|
||||||
|
|
||||||
|
All the created MouseGrabber are grouped in a MouseGrabberPool(). The QGLViewers parse this pool,
|
||||||
|
calling all the MouseGrabbers' checkIfGrabsMouse() methods that setGrabsMouse() if desired.
|
||||||
|
|
||||||
|
When a MouseGrabber grabsMouse(), it becomes the QGLViewer::mouseGrabber(). All the mouse events
|
||||||
|
(mousePressEvent(), mouseReleaseEvent(), mouseMoveEvent(), mouseDoubleClickEvent() and
|
||||||
|
wheelEvent()) are then transmitted to the QGLViewer::mouseGrabber() instead of being normally
|
||||||
|
processed. This continues while grabsMouse() (updated using checkIfGrabsMouse()) returns \c true.
|
||||||
|
|
||||||
|
If you want to (temporarily) disable a specific MouseGrabbers, you can remove it from this pool
|
||||||
|
using removeFromMouseGrabberPool(). You can also disable a MouseGrabber in a specific QGLViewer
|
||||||
|
using QGLViewer::setMouseGrabberIsEnabled().
|
||||||
|
|
||||||
|
<h3>Implementation details</h3>
|
||||||
|
|
||||||
|
In order to make MouseGrabber react to mouse events, mouse tracking has to be activated in the
|
||||||
|
QGLViewer which wants to use MouseGrabbers:
|
||||||
|
\code
|
||||||
|
init() { setMouseTracking(true); }
|
||||||
|
\endcode
|
||||||
|
Call \c QGLWidget::hasMouseTracking() to get the current state of this flag. [TODO Update with QOpenGLWidget]
|
||||||
|
|
||||||
|
The \p camera parameter of the different mouse event methods is a pointer to the
|
||||||
|
QGLViewer::camera() of the QGLViewer that uses the MouseGrabber. It can be used to compute 2D to
|
||||||
|
3D coordinates conversion using Camera::projectedCoordinatesOf() and
|
||||||
|
Camera::unprojectedCoordinatesOf().
|
||||||
|
|
||||||
|
Very complex behaviors can be implemented using this framework: auto-selected objects (no need to
|
||||||
|
press a key to use them), automatic drop-down menus, 3D GUI, spinners using the wheelEvent(), and
|
||||||
|
whatever your imagination creates. See the <a href="../examples/mouseGrabber.html">mouseGrabber
|
||||||
|
example</a> for an illustration.
|
||||||
|
|
||||||
|
Note that ManipulatedFrame are MouseGrabber: see the <a href="../examples/keyFrames.html">keyFrame
|
||||||
|
example</a> for an illustration. Every created ManipulatedFrame is hence present in the
|
||||||
|
MouseGrabberPool() (note however that ManipulatedCameraFrame are not inserted).
|
||||||
|
|
||||||
|
<h3>Example</h3>
|
||||||
|
|
||||||
|
Here is for instance a draft version of a MovableObject class. Instances of these class can freely
|
||||||
|
be moved on screen using the mouse, as movable post-it-like notes:
|
||||||
|
\code
|
||||||
|
class MovableObject : public MouseGrabber
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
MovableObject() : pos(0,0), moved(false) {}
|
||||||
|
|
||||||
|
void checkIfGrabsMouse(int x, int y, const qglviewer::Camera* const)
|
||||||
|
{
|
||||||
|
// MovableObject is active in a region of 5 pixels around its pos.
|
||||||
|
// May depend on the actual shape of the object. Customize as desired.
|
||||||
|
// Once clicked (moved = true), it keeps grabbing mouse until button is released.
|
||||||
|
setGrabsMouse( moved || ((pos-QPoint(x,y)).manhattanLength() < 5) );
|
||||||
|
}
|
||||||
|
|
||||||
|
void mousePressEvent( QMouseEvent* const e, Camera* const) { prevPos = e->pos(); moved = true; }
|
||||||
|
|
||||||
|
void mouseMoveEvent(QMouseEvent* const e, const Camera* const)
|
||||||
|
{
|
||||||
|
if (moved)
|
||||||
|
{
|
||||||
|
// Add position delta to current pos
|
||||||
|
pos += e->pos() - prevPos;
|
||||||
|
prevPos = e->pos();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void mouseReleaseEvent(QMouseEvent* const, Camera* const) { moved = false; }
|
||||||
|
|
||||||
|
void draw()
|
||||||
|
{
|
||||||
|
// The object is drawn centered on its pos, with different possible aspects:
|
||||||
|
if (grabsMouse())
|
||||||
|
if (moved)
|
||||||
|
// Object being moved, maybe a transparent display
|
||||||
|
else
|
||||||
|
// Object ready to be moved, maybe a highlighted visual feedback
|
||||||
|
else
|
||||||
|
// Normal display
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
QPoint pos, prevPos;
|
||||||
|
bool moved;
|
||||||
|
};
|
||||||
|
\endcode
|
||||||
|
Note that the different event callback methods are called only once the MouseGrabber grabsMouse().
|
||||||
|
\nosubgrouping */
|
||||||
|
class QGLVIEWER_EXPORT MouseGrabber
|
||||||
|
{
|
||||||
|
#ifndef DOXYGEN
|
||||||
|
friend class ::QGLViewer;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
public:
|
||||||
|
MouseGrabber();
|
||||||
|
/*! Virtual destructor. Removes the MouseGrabber from the MouseGrabberPool(). */
|
||||||
|
virtual ~MouseGrabber() { MouseGrabber::MouseGrabberPool_.removeAll(this); }
|
||||||
|
|
||||||
|
/*! @name Mouse grabbing detection */
|
||||||
|
//@{
|
||||||
|
public:
|
||||||
|
/*! Pure virtual method, called by the QGLViewers before they test if the MouseGrabber
|
||||||
|
grabsMouse(). Should setGrabsMouse() according to the mouse position.
|
||||||
|
|
||||||
|
This is the core method of the MouseGrabber. It has to be overloaded in your derived class.
|
||||||
|
Its goal is to update the grabsMouse() flag according to the mouse and MouseGrabber current
|
||||||
|
positions, using setGrabsMouse().
|
||||||
|
|
||||||
|
grabsMouse() is usually set to \c true when the mouse cursor is close enough to the MouseGrabber
|
||||||
|
position. It should also be set to \c false when the mouse cursor leaves this region in order to
|
||||||
|
release the mouse focus.
|
||||||
|
|
||||||
|
\p x and \p y are the mouse cursor coordinates (Qt coordinate system: (0,0) corresponds to the upper
|
||||||
|
left corner).
|
||||||
|
|
||||||
|
A typical implementation will look like:
|
||||||
|
\code
|
||||||
|
// (posX,posY) is the position of the MouseGrabber on screen.
|
||||||
|
// Here, distance to mouse must be less than 10 pixels to activate the MouseGrabber.
|
||||||
|
setGrabsMouse( sqrt((x-posX)*(x-posX) + (y-posY)*(y-posY)) < 10);
|
||||||
|
\endcode
|
||||||
|
|
||||||
|
If the MouseGrabber position is defined in 3D, use the \p camera parameter, corresponding to
|
||||||
|
the calling QGLViewer Camera. Project on screen and then compare the projected coordinates:
|
||||||
|
\code
|
||||||
|
Vec proj = camera->projectedCoordinatesOf(myMouseGrabber->frame()->position());
|
||||||
|
setGrabsMouse((fabs(x-proj.x) < 5) && (fabs(y-proj.y) < 2)); // Rectangular region
|
||||||
|
\endcode
|
||||||
|
|
||||||
|
See examples in the <a href="#_details">detailed description</a> section and in the <a
|
||||||
|
href="../examples/mouseGrabber.html">mouseGrabber example</a>. */
|
||||||
|
virtual void checkIfGrabsMouse(int x, int y, const Camera* const camera) = 0;
|
||||||
|
|
||||||
|
/*! Returns \c true when the MouseGrabber grabs the QGLViewer's mouse events.
|
||||||
|
|
||||||
|
This flag is set with setGrabsMouse() by the checkIfGrabsMouse() method. */
|
||||||
|
bool grabsMouse() const { return grabsMouse_; }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
/*! Sets the grabsMouse() flag. Normally used by checkIfGrabsMouse(). */
|
||||||
|
void setGrabsMouse(bool grabs) { grabsMouse_ = grabs; }
|
||||||
|
//@}
|
||||||
|
|
||||||
|
|
||||||
|
/*! @name MouseGrabber pool */
|
||||||
|
//@{
|
||||||
|
public:
|
||||||
|
/*! Returns a list containing pointers to all the active MouseGrabbers.
|
||||||
|
|
||||||
|
Used by the QGLViewer to parse all the MouseGrabbers and to check if any of them grabsMouse()
|
||||||
|
using checkIfGrabsMouse().
|
||||||
|
|
||||||
|
You should not have to directly use this list. Use removeFromMouseGrabberPool() and
|
||||||
|
addInMouseGrabberPool() to modify this list.
|
||||||
|
|
||||||
|
\attention This method returns a \c QPtrList<MouseGrabber> with Qt 3 and a \c QList<MouseGrabber> with Qt 2. */
|
||||||
|
static const QList<MouseGrabber*>& MouseGrabberPool() { return MouseGrabber::MouseGrabberPool_; }
|
||||||
|
|
||||||
|
/*! Returns \c true if the MouseGrabber is currently in the MouseGrabberPool() list.
|
||||||
|
|
||||||
|
Default value is \c true. When set to \c false using removeFromMouseGrabberPool(), the
|
||||||
|
QGLViewers no longer checkIfGrabsMouse() on this MouseGrabber. Use addInMouseGrabberPool() to
|
||||||
|
insert it back. */
|
||||||
|
bool isInMouseGrabberPool() const { return MouseGrabber::MouseGrabberPool_.contains(const_cast<MouseGrabber*>(this)); }
|
||||||
|
void addInMouseGrabberPool();
|
||||||
|
void removeFromMouseGrabberPool();
|
||||||
|
void clearMouseGrabberPool(bool autoDelete=false);
|
||||||
|
//@}
|
||||||
|
|
||||||
|
|
||||||
|
/*! @name Mouse event handlers */
|
||||||
|
//@{
|
||||||
|
protected:
|
||||||
|
/*! Callback method called when the MouseGrabber grabsMouse() and a mouse button is pressed.
|
||||||
|
|
||||||
|
|
||||||
|
The MouseGrabber will typically start an action or change its state when a mouse button is
|
||||||
|
pressed. mouseMoveEvent() (called at each mouse displacement) will then update the MouseGrabber
|
||||||
|
accordingly and mouseReleaseEvent() (called when the mouse button is released) will terminate
|
||||||
|
this action.
|
||||||
|
|
||||||
|
Use the \p event QMouseEvent::state() and QMouseEvent::button() to test the keyboard
|
||||||
|
and button state and possibly change the MouseGrabber behavior accordingly.
|
||||||
|
|
||||||
|
See the <a href="#_details">detailed description section</a> and the <a
|
||||||
|
href="../examples/mouseGrabber.html">mouseGrabber example</a> for examples.
|
||||||
|
|
||||||
|
See the \c QGLWidget::mousePressEvent() and the \c QMouseEvent documentations for details. [TODO Update with QOpenGLWidget] */
|
||||||
|
virtual void mousePressEvent(QMouseEvent* const event, Camera* const camera) { Q_UNUSED(event); Q_UNUSED(camera); }
|
||||||
|
/*! Callback method called when the MouseGrabber grabsMouse() and a mouse button is double clicked.
|
||||||
|
|
||||||
|
See the \c QGLWidget::mouseDoubleClickEvent() and the \c QMouseEvent documentations for details. [TODO Update with QOpenGLWidget] */
|
||||||
|
virtual void mouseDoubleClickEvent(QMouseEvent* const event, Camera* const camera) { Q_UNUSED(event); Q_UNUSED(camera); }
|
||||||
|
/*! Mouse release event callback method. See mousePressEvent(). */
|
||||||
|
virtual void mouseReleaseEvent(QMouseEvent* const event, Camera* const camera) { Q_UNUSED(event); Q_UNUSED(camera); }
|
||||||
|
/*! Callback method called when the MouseGrabber grabsMouse() and the mouse is moved while a
|
||||||
|
button is pressed.
|
||||||
|
|
||||||
|
This method will typically update the state of the MouseGrabber from the mouse displacement. See
|
||||||
|
the mousePressEvent() documentation for details. */
|
||||||
|
virtual void mouseMoveEvent(QMouseEvent* const event, Camera* const camera) { Q_UNUSED(event); Q_UNUSED(camera); }
|
||||||
|
/*! Callback method called when the MouseGrabber grabsMouse() and the mouse wheel is used.
|
||||||
|
|
||||||
|
See the \c QGLWidget::wheelEvent() and the \c QWheelEvent documentations for details. [TODO Update with QOpenGLWidget] */
|
||||||
|
virtual void wheelEvent(QWheelEvent* const event, Camera* const camera) { Q_UNUSED(event); Q_UNUSED(camera); }
|
||||||
|
//@}
|
||||||
|
|
||||||
|
private:
|
||||||
|
// Copy constructor and opertor= are declared private and undefined
|
||||||
|
// Prevents everyone from trying to use them
|
||||||
|
MouseGrabber(const MouseGrabber&);
|
||||||
|
MouseGrabber& operator=(const MouseGrabber&);
|
||||||
|
|
||||||
|
bool grabsMouse_;
|
||||||
|
|
||||||
|
// Q G L V i e w e r p o o l
|
||||||
|
static QList<MouseGrabber*> MouseGrabberPool_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace qglviewer
|
||||||
|
|
||||||
|
#endif // QGLVIEWER_MOUSE_GRABBER_H
|
||||||
359
QGLViewer/qglviewer-icon.xpm
Normal file
359
QGLViewer/qglviewer-icon.xpm
Normal file
@ -0,0 +1,359 @@
|
|||||||
|
/* XPM */
|
||||||
|
static const char * qglviewer_icon[] = {
|
||||||
|
"100 100 256 2",
|
||||||
|
" c None",
|
||||||
|
". c #0A0B27",
|
||||||
|
"+ c #090B2C",
|
||||||
|
"@ c #150C12",
|
||||||
|
"# c #080F34",
|
||||||
|
"$ c #1A0E1A",
|
||||||
|
"% c #220D0B",
|
||||||
|
"& c #260B0B",
|
||||||
|
"* c #230E1C",
|
||||||
|
"= c #12113E",
|
||||||
|
"- c #2C0D10",
|
||||||
|
"; c #2F0D09",
|
||||||
|
"> c #310D14",
|
||||||
|
", c #1A123A",
|
||||||
|
"' c #261025",
|
||||||
|
") c #17134B",
|
||||||
|
"! c #151453",
|
||||||
|
"~ c #1B1733",
|
||||||
|
"{ c #14135E",
|
||||||
|
"] c #281430",
|
||||||
|
"^ c #0E1867",
|
||||||
|
"/ c #3B1215",
|
||||||
|
"( c #32132A",
|
||||||
|
"_ c #1D1849",
|
||||||
|
": c #121C58",
|
||||||
|
"< c #431014",
|
||||||
|
"[ c #141974",
|
||||||
|
"} c #3A152E",
|
||||||
|
"| c #201A5E",
|
||||||
|
"1 c #1D204C",
|
||||||
|
"2 c #401431",
|
||||||
|
"3 c #261960",
|
||||||
|
"4 c #48171C",
|
||||||
|
"5 c #411B21",
|
||||||
|
"6 c #371B45",
|
||||||
|
"7 c #2A1C6B",
|
||||||
|
"8 c #0E229B",
|
||||||
|
"9 c #52171D",
|
||||||
|
"0 c #441A35",
|
||||||
|
"a c #1B2582",
|
||||||
|
"b c #65150F",
|
||||||
|
"c c #2D2076",
|
||||||
|
"d c #4D1B3A",
|
||||||
|
"e c #57182F",
|
||||||
|
"f c #3E1F54",
|
||||||
|
"g c #5E1924",
|
||||||
|
"h c #2E2567",
|
||||||
|
"i c #002BD1",
|
||||||
|
"j c #002AD9",
|
||||||
|
"k c #6D1A13",
|
||||||
|
"l c #36237A",
|
||||||
|
"m c #002FCD",
|
||||||
|
"n c #701A0D",
|
||||||
|
"o c #462060",
|
||||||
|
"p c #651C23",
|
||||||
|
"q c #322581",
|
||||||
|
"r c #552429",
|
||||||
|
"s c #581E41",
|
||||||
|
"t c #671E1B",
|
||||||
|
"u c #771911",
|
||||||
|
"v c #6C1B2B",
|
||||||
|
"w c #562341",
|
||||||
|
"x c #342A79",
|
||||||
|
"y c #3C2484",
|
||||||
|
"z c #062FE6",
|
||||||
|
"A c #68212B",
|
||||||
|
"B c #4D236B",
|
||||||
|
"C c #612045",
|
||||||
|
"D c #42267B",
|
||||||
|
"E c #811B10",
|
||||||
|
"F c #7C1E0E",
|
||||||
|
"G c #0033F1",
|
||||||
|
"H c #0032F9",
|
||||||
|
"I c #3A298E",
|
||||||
|
"J c #472872",
|
||||||
|
"K c #72202A",
|
||||||
|
"L c #4F2774",
|
||||||
|
"M c #652449",
|
||||||
|
"N c #442890",
|
||||||
|
"O c #1F37A8",
|
||||||
|
"P c #87200E",
|
||||||
|
"Q c #852014",
|
||||||
|
"R c #402B98",
|
||||||
|
"S c #6C244D",
|
||||||
|
"T c #7D2230",
|
||||||
|
"U c #8F1F12",
|
||||||
|
"V c #70254A",
|
||||||
|
"W c #542A7E",
|
||||||
|
"X c #212FF2",
|
||||||
|
"Y c #6E2E28",
|
||||||
|
"Z c #442E9B",
|
||||||
|
"` c #772834",
|
||||||
|
" . c #971E15",
|
||||||
|
".. c #552B86",
|
||||||
|
"+. c #693132",
|
||||||
|
"@. c #702A46",
|
||||||
|
"#. c #7B2645",
|
||||||
|
"$. c #862435",
|
||||||
|
"%. c #79264E",
|
||||||
|
"&. c #5A2B88",
|
||||||
|
"*. c #1B3AE9",
|
||||||
|
"=. c #9A2211",
|
||||||
|
"-. c #4E2EA0",
|
||||||
|
";. c #1E3BDE",
|
||||||
|
">. c #722F3D",
|
||||||
|
",. c #5E2C91",
|
||||||
|
"'. c #592F91",
|
||||||
|
"). c #7A322A",
|
||||||
|
"!. c #1D42D7",
|
||||||
|
"~. c #902539",
|
||||||
|
"{. c #7D2A52",
|
||||||
|
"]. c #862E26",
|
||||||
|
"^. c #822853",
|
||||||
|
"/. c #922B20",
|
||||||
|
"(. c #4E34AA",
|
||||||
|
"_. c #A62411",
|
||||||
|
":. c #5D309A",
|
||||||
|
"<. c #59387B",
|
||||||
|
"[. c #5631AB",
|
||||||
|
"}. c #5533A6",
|
||||||
|
"|. c #912840",
|
||||||
|
"1. c #8C2B3F",
|
||||||
|
"2. c #AE2215",
|
||||||
|
"3. c #862C57",
|
||||||
|
"4. c #5D32A8",
|
||||||
|
"5. c #882C53",
|
||||||
|
"6. c #5C34A2",
|
||||||
|
"7. c #6231A2",
|
||||||
|
"8. c #A32A18",
|
||||||
|
"9. c #5C398B",
|
||||||
|
"0. c #4D4480",
|
||||||
|
"a. c #982A3F",
|
||||||
|
"b. c #8D2B5A",
|
||||||
|
"c. c #962C44",
|
||||||
|
"d. c #902C56",
|
||||||
|
"e. c #7A3B43",
|
||||||
|
"f. c #3249C3",
|
||||||
|
"g. c #B42812",
|
||||||
|
"h. c #583E9B",
|
||||||
|
"i. c #BA2516",
|
||||||
|
"j. c #524297",
|
||||||
|
"k. c #55448B",
|
||||||
|
"l. c #5E3BA3",
|
||||||
|
"m. c #9E2C47",
|
||||||
|
"n. c #5A4676",
|
||||||
|
"o. c #873847",
|
||||||
|
"p. c #404E94",
|
||||||
|
"q. c #932F59",
|
||||||
|
"r. c #BE2810",
|
||||||
|
"s. c #992D5B",
|
||||||
|
"t. c #5941A4",
|
||||||
|
"u. c #8A3F36",
|
||||||
|
"v. c #9D2F58",
|
||||||
|
"w. c #78415D",
|
||||||
|
"x. c #A72D4D",
|
||||||
|
"y. c #A2304B",
|
||||||
|
"z. c #C72815",
|
||||||
|
"A. c #C12C13",
|
||||||
|
"B. c #654298",
|
||||||
|
"C. c #654780",
|
||||||
|
"D. c #8F385F",
|
||||||
|
"E. c #98345D",
|
||||||
|
"F. c #2F51E3",
|
||||||
|
"G. c #A93724",
|
||||||
|
"H. c #A0325B",
|
||||||
|
"I. c #CA2B10",
|
||||||
|
"J. c #A93054",
|
||||||
|
"K. c #A6305D",
|
||||||
|
"L. c #D42A00",
|
||||||
|
"M. c #D22914",
|
||||||
|
"N. c #89415D",
|
||||||
|
"O. c #6546A5",
|
||||||
|
"P. c #AB3255",
|
||||||
|
"Q. c #DC2806",
|
||||||
|
"R. c #A3355D",
|
||||||
|
"S. c #AA325A",
|
||||||
|
"T. c #A13F2F",
|
||||||
|
"U. c #6A4A8E",
|
||||||
|
"V. c #A53754",
|
||||||
|
"W. c #DE2A00",
|
||||||
|
"X. c #CF300A",
|
||||||
|
"Y. c #D62C0E",
|
||||||
|
"Z. c #A8385B",
|
||||||
|
"`. c #4B54C3",
|
||||||
|
" + c #964356",
|
||||||
|
".+ c #E82903",
|
||||||
|
"++ c #8F4757",
|
||||||
|
"@+ c #DF2C14",
|
||||||
|
"#+ c #D93011",
|
||||||
|
"$+ c #A23D62",
|
||||||
|
"%+ c #8C4E44",
|
||||||
|
"&+ c #E32F00",
|
||||||
|
"*+ c #6E4CA5",
|
||||||
|
"=+ c #6E4F9B",
|
||||||
|
"-+ c #A04259",
|
||||||
|
";+ c #9D4266",
|
||||||
|
">+ c #D03716",
|
||||||
|
",+ c #E3300D",
|
||||||
|
"'+ c #6A51A7",
|
||||||
|
")+ c #BA3F2F",
|
||||||
|
"!+ c #EC2E07",
|
||||||
|
"~+ c #A9405F",
|
||||||
|
"{+ c #EB2E13",
|
||||||
|
"]+ c #EF3100",
|
||||||
|
"^+ c #4A5CDC",
|
||||||
|
"/+ c #984A68",
|
||||||
|
"(+ c #B54536",
|
||||||
|
"_+ c #4563C9",
|
||||||
|
":+ c #F62E03",
|
||||||
|
"<+ c #EF310B",
|
||||||
|
"[+ c #E73411",
|
||||||
|
"}+ c #AA4B3E",
|
||||||
|
"|+ c #F62E10",
|
||||||
|
"1+ c #CE401F",
|
||||||
|
"2+ c #D33D23",
|
||||||
|
"3+ c #7455A7",
|
||||||
|
"4+ c #C54426",
|
||||||
|
"5+ c #A5496A",
|
||||||
|
"6+ c #F93106",
|
||||||
|
"7+ c #A45048",
|
||||||
|
"8+ c #CD412C",
|
||||||
|
"9+ c #F2350E",
|
||||||
|
"0+ c #AA4A64",
|
||||||
|
"a+ c #5266C7",
|
||||||
|
"b+ c #DE411E",
|
||||||
|
"c+ c #EF3C18",
|
||||||
|
"d+ c #755FAD",
|
||||||
|
"e+ c #C94C34",
|
||||||
|
"f+ c #C14F3F",
|
||||||
|
"g+ c #DB452E",
|
||||||
|
"h+ c #AB526E",
|
||||||
|
"i+ c #EB421F",
|
||||||
|
"j+ c #AA566A",
|
||||||
|
"k+ c #A55B6B",
|
||||||
|
"l+ c #AA5873",
|
||||||
|
"m+ c #E94824",
|
||||||
|
"n+ c #D94D35",
|
||||||
|
"o+ c #DB4F31",
|
||||||
|
"p+ c #5B71DE",
|
||||||
|
"q+ c #A85F76",
|
||||||
|
"r+ c #EC4C31",
|
||||||
|
"s+ c #E2522E",
|
||||||
|
"t+ c #E84F30",
|
||||||
|
"u+ c #CD5D42",
|
||||||
|
"v+ c #6976CD",
|
||||||
|
"w+ c #D9583F",
|
||||||
|
"x+ c #E55537",
|
||||||
|
"y+ c #E7573E",
|
||||||
|
"z+ c #D65F4C",
|
||||||
|
"A+ c #CA6457",
|
||||||
|
"B+ c #E35C3E",
|
||||||
|
"C+ c #D1634E",
|
||||||
|
"D+ c #E26447",
|
||||||
|
"E+ c #E1674E",
|
||||||
|
"F+ c #DE6F57",
|
||||||
|
"G+ c #DE705E",
|
||||||
|
" ",
|
||||||
|
" ",
|
||||||
|
" C+ ",
|
||||||
|
" ` >.o. 1+X.u+ ",
|
||||||
|
" e.A K K T $.T ++ 1+L.L.X. ",
|
||||||
|
" A A v ` T T T 1.~.++ X.L.L.L.W.1+ ",
|
||||||
|
" g A K K T T $.$.1.$.1. >+L.L.L.L.L.L. ",
|
||||||
|
" r g A v v T T $.1.$.$.|.|. + u+X.L.L.L.L.W.W.W.2+ ",
|
||||||
|
" f r g g K K v T T T |.|.1.1.c.-+ u+X.L.L.L.Q.L.Q.Q.W.W. ",
|
||||||
|
" 6 o B C. 4 9 p p v T T T 1.~.$.1.~.a.~.1. u+X.L.L.L.Q.L.W.L.W.W.W.o+ ",
|
||||||
|
" f o B C. 4 9 9 g v v T T $.1.1.~.|.c.c.c.m. 4+L.L.L.Q.L.Q.Q.W.&+W.W.,+b+ ",
|
||||||
|
" f o J L U. 9 9 A v K T $.1.1.$.|.|.c.c.c.a.a.q+ 4+L.L.L.L.L.Q.W.W.&+L.&+&+&+W. ",
|
||||||
|
" f o B W L <. < 9 g A e.` ` $.$.|.$.1.~.a.a.c.c.a.k+ 1+L.L.Q.Q.W.L.W.L.&+&+&+,+&+&+w+ ",
|
||||||
|
" 6 f B L L .. 4 4 9 o.$.|.$.c.a.c.c.m.m.y.c.q+ u+L.W.Q.L.W.&+&+W.&+,+&+&+&+.+s+ ",
|
||||||
|
" p. f o L W W .. / < r 1.|.|.c.c.c.c.y.m.y.a.k+ X.L.W.L.,+,+&+,+&+&+&+[+&+&+b+ ",
|
||||||
|
" 3 6 B L W W &.9. / o.|.|.c.a.m.y.a.a.y.y.q+ e+W.W.&+W.W.,+&+&+&+&+.+&+!+b+ ",
|
||||||
|
" a : f o L L ....&.B. / ++a.a.m.c.a.y.y.a.y.y.q+ u+W.W.W.&+.+&+{+&+[+&+!+&+]+&+ ",
|
||||||
|
" O ^ 1 f J W ....&.'.U. / 5 k+c.m.c.m.y.m.y.y.y.y. L.&+&+&+&+&+&+!+]+!+[+!+!+!+ ",
|
||||||
|
" f.[ ) $ <.L W ..&.'.'. / k+c.m.a.m.y.a.y.y.y.y. b+&+,+&+.+&+&+&+[+]+&+!+&+!+E+ ",
|
||||||
|
" `.8 ! = $ <.W ..&.'.'.B. - k+y.a.y.y.a.y.x.y.x.V. b+.+&+!+&+[+!+!+]+!+!+!+9+!+B+ ",
|
||||||
|
" a+m { = # $ ....'.'.'.,.=+ - k+m.y.y.y.V.y.x.y.x.V. w+!+&+!+]+&+[+&+!+[+<+!+]+&+B+ ",
|
||||||
|
" v+m 8 ) # $ U...'.'.'.,.:. - -+m.m.y.V.x.y.y.x.m.0+ s+&+!+[+&+!+]+<+!+]+]+[+<+9+B+ ",
|
||||||
|
" ;.m ) = + $ 9.&.'.l.l.,.*+ & -+y.y.x.x.J.x.y.x.y.0+ B+&+]+!+<+!+!+[+]+!+]+<+]+<+B+ ",
|
||||||
|
" ;.i [ = + . $ &.'.,.,.,.:.*+ & 0+y.y.x.y.y.y.J.V.x.l+ s+!+!+!+[+<+]+]+!+]+]+]+!+]+B+ ",
|
||||||
|
" ;.z ^+1 # + $ =+l.'.l.7.:.'. % y.x.y.x.x.P.y.x.P.y. B+.+[+]+!+[+]+]+<+]+]+]+]+<+B+ ",
|
||||||
|
" ;.z ;. + + . $ '.:.:.:.:.:.3+ & V.y.x.y.x.J.P.P.V.Z. x+<+]+]+]+:+]+]+]+9+<+9+9+]+ ",
|
||||||
|
" p+*.G G p+ + . @ U.:.:.7.:.6.7. % h+P.V.P.P.y.J.P.x.y.0+ s+]+9+]+]+9+9+]+]+]+]+]+9+<+ ",
|
||||||
|
" f. p+F.H H G F. . . . $ l.:.:.:.6.:.O. & 0+x.J.J.V.P.x.x.P.P.h+ x+<+]+<+]+]+]+9+]+]+]+9+]+<+ ",
|
||||||
|
" i G X X X G H ;. . . $ =+:.:.6.l.6.:.3+ % V.y.y.x.P.V.V.P.P.J. m+!+]+]+]+9+]+|+9+|+]+9+]+c+ ",
|
||||||
|
" m z G H G H G p+ . . * O.6.6.:.7.6.:. & V.P.P.P.x.P.P.V.V.V. i+]+]+<+|+:+]+]+:+]+:+:+]+m+ ",
|
||||||
|
" i z G G G G F. . . * 3+7.7.6.6.6.6.3+ % 0+x.P.x.V.x.x.x.x.P.0+ [+9+]+]+]+|+]+]+]+9+9+9+]+m+ ",
|
||||||
|
" i z G G H *. . . * :.6.6.7.6.7.:. & 0+P.P.P.P.P.P.P.P.V. :+:+]+]+9+]+9+9+|+:+:+:+]+B+ ",
|
||||||
|
" i z G G G p+ . * *+7.}.6.4.6.6.*+ % V.y.P.x.P.x.P.P.P.V. F+]+|+9+:+]+:+:+]+]+:+|+|+:+B+ ",
|
||||||
|
" i z G G ^+ . ' n.4.6.7.4.7.4.6. & 0+x.P.P.P.P.P.P.P.P.~+ s+9+]+:+]+|+6+]+]+|+9+]+]+9+ ",
|
||||||
|
" i z G F. . * O.4.6.6.6.6.4.*+ & ~+P.P.P.P.P.P.P.P.Z.l+ s+6+9+|+]+9+|+|+]+6+:+6+6+9+ ",
|
||||||
|
" j z !. . ] J 4.6.4.[.4.[.l. & P.P.P.P.P.P.P.P.P.V. c+9+6+]+|+6+9+]+6+9+|+9+|+i+ ",
|
||||||
|
" a+i ;. . ' l.[.(.6.6.6.4.*+ & h+P.P.S.P.S.P.P.P.P.0+ :+|+]+]+|+6+|+9+6+]+6+]+9+t+ ",
|
||||||
|
" a+!. . ] t.(.6.7.4.4.[.6. & S.P.P.P.P.P.S.S.S.S.q+ D+9+9+|+6+]+9+]+6+|+6+9+6+:+E+ ",
|
||||||
|
" . 3+7.[.6.(.6.6.6.*+ & h+S.S.P.S.~+P.P.P.P.~+ r+6+]+9+6+6+6+9+]+9+6+9+6+9+ ",
|
||||||
|
" . 6.(.6.4.4.4.(.6. & ~+P.P.P.P.P.P.P.P.P.h+ c+6+|+6+9+6+9+6+|+6+|+6+9+9+ ",
|
||||||
|
" . l.[.[.(.6.(.4.(.3+ % l+S.V.~+P.P.P.~+P.P.S. 9+]+|+6+9+6+|+9+9+]+|+6+9+t+ ",
|
||||||
|
" ~ *+(.6.4.4.4.6.4.l. % ~+P.S.P.S.P.S.P.S.S.0+ B+9+6+9+9+6+|+9+6+6+|+]+]+|+D+ ",
|
||||||
|
" ~ '+(.[.(.(.(.4.4.6.d+ & S.P.S.P.S.P.S.P.P.~+ i+9+6+6+9+6+6+:+9+:+6+|+6+c+ ",
|
||||||
|
" = '+(.[.(.(.[.4.[.(.O. % ~+~+S.Z.S.Z.S.P.Z.S.~+ 9+6+6+9+6+9+:+9+6+6+9+6+]+m+ ",
|
||||||
|
" _ '+Z }.}.4.6.(.(.[.4. & q+S.S.S.~+S.~+S.S.S.S.l+ x+]+9+6+9+6+9+6+9+9+6+9+|+6+B+ ",
|
||||||
|
" ) h h.Z -.-.}.(.(.[.[.(.'+ & R.Z.Z.S.S.S.S.~+Z.Z.R. r+|+6+|+6+9+6+|+6+6+9+6+9+]+F+ ",
|
||||||
|
" _ 3 x k.j.I Z -.Z Z }.4.(.(.4.t. % l+S.S.Z.Z.Z.S.S.S.S.Z.h+ F+]+9+6+9+6+9+6+9+9+6+9+6+6+c+ ",
|
||||||
|
" ) 3 7 q I I Z N }.}.}.(.}.}.(. & Z.Z.S.Z.Z.S.Z.Z.Z.Z.~+ B+6+6+9+6+|+6+9+6+6+|+6+6+9+y+ ",
|
||||||
|
" = ! 7 c q I N Z Z Z Z -.}.}.}.'+ % l+S.Z.S.S.Z.S.S.S.S.Z.h+ c+9+6+9+6+9+6+|+9+6+9+9+6+6+ ",
|
||||||
|
" _ ) 3 l q N N Z N -.}.Z -.(.O. & R.R.R.S.K.S.Z.Z.Z.V.$+ D+6+6+9+6+9+6+9+6+6+9+6+6+9+m+ ",
|
||||||
|
" _ 3 3 l q I N Z Z -.-.}.}.Z & $+S.S.Z.Z.Z.K.K.K.K.S.h+ i+9+6+9+6+9+6+9+9+6+9+9+6+9+B+ ",
|
||||||
|
" _ | 7 l y I I N N Z -.(.-.d+ % l+R.R.R.R.S.K.Z.S.S.S.~+ F+9+6+9+|+6+6+9+6+6+9+|+6+6+9+ ",
|
||||||
|
" _ 3 7 c q I R Z :.Z Z -.'+ & R.K.K.K.R.Z.S.Z.Z.Z.K.l+ r+9+6+6+9+6+6+6+9+6+6+9+9+6+t+ ",
|
||||||
|
" ) | 7 l y y I N R Z -.D & ;+R.Z.Z.S.K.K.R.Z.S.S.5+ G+6+6+9+9+6+9+9+9+6+6+9+6+6+]+E+ ",
|
||||||
|
" 3 x c I y I Z Z Z h.' & 5+K.K.R.R.S.K.S.K.R.R.S. t+9+6+|+9+6+6+6+6+9+6+9+|+|+i+ ",
|
||||||
|
" | c l q I R I Z h. $ & $+R.s.K.R.R.Z.R.K.K.K.5+ 9+6+9+6+6+6+c+6+9+6+9+|+6+6+x+ ",
|
||||||
|
" h 7 l q y N N j. * & H.H.R.s.K.H.H.K.Z.K.S.Z. r+9+6+9+6+6+6+6+6+6+6+9+9+9+c+ ",
|
||||||
|
" 0.l q y I y '+ ' ; ;+v.v.K.R.H.K.K.v.H.Z.R.5+ G+9+|+6+c+6+c+6+c+6+6+9+6+|+6+B+ ",
|
||||||
|
" 0.x D j. ' & ;+s.R.s.K.H.s.s.R.K.K.K.$+ m+9+9+|+9+6+9+6+6+c+6+6+9+9+6+ ",
|
||||||
|
" ( ( ; ;+v.$+s.R.v.H.R.K.K.R.s.R. E+9+|+9+6+6+c+6+c+6+6+9+9+6+6+t+ ",
|
||||||
|
" ( } & ; /+5.v.v.v.s.H.R.H.H.s.K.R.5+ i+9+|+9+9+6+6+6+c+6+6+6+9+6+9+ ",
|
||||||
|
" ( } - ; /+q.s.s.s.E.H.v.K.v.R.K.R.$+ y+|+|+9+9+|+9+c+6+6+c+9+9+|+9+x+ ",
|
||||||
|
" ( 2 0 - ; D.d.q.q.E.v.v.s.s.H.s.R.v.$+ 9+9+|+|+9+6+6+9+c+6+6+6+c+6+9+ ",
|
||||||
|
" } 0 0 ; ; 3.q.b.q.q.s.E.s.H.H.v.K.v.H.5+ r+9+|+9+9+6+c+9+6+6+c+9+9+|+|+x+ ",
|
||||||
|
" } 0 d d ; < N.5.3.q.q.q.q.s.E.E.E.s.v.s.H.5+ D+9+9+|+9+9+6+c+9+6+|+6+9+9+9+9+ ",
|
||||||
|
" } 2 s s s M w.w.e @.{.^.3.3.b.d.q.q.q.s.v.v.s.H.s.$+ i+9+|+9+|+c+6+|+6+9+9+9+9+6+|+y+ ",
|
||||||
|
" 0 0 d s C M V V V ^.^.5.5.5.3.d.q.q.q.q.s.v.E.R.E. r+9+c+|+9+|+9+c+c+|+|+9+6+9+6+i+ ",
|
||||||
|
" 2 2 s w C C V V %.{.{.5.b.5.q.3.d.q.q.s.E.E.v.s.q+ w+9+9+9+9+9+9+|+|+|+9+9+|+9+|+9+E+ ",
|
||||||
|
" 0 0 d w C C S V V {.%.#.3.5.d.d.d.q.q.s.s.E.E.q+ E+c+9+9+c+9+c+9+|+9+|+9+9+9+9+|+i+ ",
|
||||||
|
" } d s s C M V S %.%.^.3.3.3.3.3.d.d.q.d.q.s./+ i+!+c+9+9+9+|+9+9+|+9+|+9+|+|+9+G+ ",
|
||||||
|
" 0 d d e M M V S {.{.^.3.3.d.d.D.d.d.q.q.v./+ r+[+|+{+9+c+9+9+9+9+9+9+|+9+9+9+B+ ",
|
||||||
|
" d s s s V S V S %.{.{.5.3.3.d.d.q.q.q./+ o+[+!+c+9+|+9+c+9+c+9+c+9+9+|+9+m+ ",
|
||||||
|
" 0 s C s V V %.V {.^.{.5.5.3.3.D.d.q./+ n+{+[+{+[+c+c+|+9+9+9+|+9+c+9+|+|+G+ ",
|
||||||
|
" d w s M M M V %.%.{.3.{.3.d.d.d.q./+ w+[+[+{+&+{+{+{+9+c+9+c+9+c+|+9+c+B+ ",
|
||||||
|
" w s @.S S S %.%.{.5.5.^.3.b.D. G+@+[+[+[+c+[+<+[+9+c+|+c+9+9+c+|+m+ ",
|
||||||
|
" w s C M V V S %.^.{.{.3.5.N. C+@+[+{+&+{+&+<+[+{+{+{+9+9+9+c+9+c+G+ ",
|
||||||
|
" w M M S V {.V ^.5.5.{. w+,+,+@+[+[+{+[+{+c+9+<+[+9+9+9+9+{+D+ ",
|
||||||
|
" M @.V V {.{.#.++ w+@+@+@+[+{+[+[+{+[+&+[+[+{+c+{+{+{+x+ ",
|
||||||
|
" w.>.p t b ). 8+@+,+[+,+@+{+[+[+&+{+{+{+[+!+[+9+9+b+ ",
|
||||||
|
" +.b b n u %+ 2+#+,+@+Q.@+[+@+,+[+[+[+[+[+<+[+<+{+i+ ",
|
||||||
|
" b b k u u u+M.M.#+#+@+,+@+[+@+[+{+[+[+{+[+[+[+[+{+z+ ",
|
||||||
|
" b b n u u /. A+2+M.#+#+#+,+@+@+,+,+[+@+,+[+{+[+{+{+[+<+w+ ",
|
||||||
|
" t k k F u P /.7+ A+e+z.#+#+@+M.#+#+,+#+@+Q.[+@+@+,+{+&+[+{+[+B+ ",
|
||||||
|
" Y n u u E Q U U T.7+ f+4+I.M.>+M.M.Y.@+M.@+#+@+,+@+@+[+[+[+[+[+[+[+n+ ",
|
||||||
|
" k n F Q Q U .=._.8.G.(+(+)+g.i.z.r.I.M.I.>+I.#+#+#+#+#+#+,+,+Q.,+@+,+@+{+{+o+ ",
|
||||||
|
" k u u E U U U =.=._.2.g.i.i.i.A.r.z.z.I.M.Y.#+>+#+#+#+Q.@+@+@+@+,+[+@+[+,+s+ ",
|
||||||
|
" ).n E E P U =.=.=.8._._.g.g.i.i.A.A.z.I.z.M.Y.M.M.M.#+#+#+,+@+@+@+[+,+@+g+ ",
|
||||||
|
" u u Q U U U =._._.2.g.g.g.A.A.A.z.A.z.M.I.I.Y.#+#+#+#+@+#+,+,+,+,+@+o+ ",
|
||||||
|
" ).F Q U U =.=.=.8._.2.2.g.r.r.r.I.I.I.z.M.I.M.#+M.#+#+#+#+@+@+@+,+w+ ",
|
||||||
|
" Q Q P U U =._._.2._.g.g.g.i.A.A.A.A.I.I.M.X.M.#+@+#+@+@+@+,+#+z+ ",
|
||||||
|
" u.U /.U U 8._.8._.2.2.g.g.i.A.A.I.z.I.I.M.I.Y.>+@+#+#+#+#+#+A+ ",
|
||||||
|
" ].U U =.=.=.2.2._.g.i.i.A.i.A.A.I.z.I.>+Y.>+Y.Y.#+#+#+n+ ",
|
||||||
|
" /.U .=.=._._.2.2.g.i.A.A.z.A.I.I.M.I.M.M.M.M.Y.M.z+ ",
|
||||||
|
" /.=.8._._.2._.g.g.g.g.i.A.z.A.z.I.I.Y.I.X.#+8+ ",
|
||||||
|
" /.8.8._._.2.2.i.i.A.i.A.z.z.I.I.I.M.M.8+A+ ",
|
||||||
|
" }+8._.2.2.g.2.A.i.A.A.A.>+z.z.z.4+A+ ",
|
||||||
|
" T.)+_.g.2.A.r.A.A.r.A.4+z+ ",
|
||||||
|
" }+}+(+(+f+f+A+ ",
|
||||||
|
" ",
|
||||||
|
" "};
|
||||||
3171
QGLViewer/qglviewer.cpp
Normal file
3171
QGLViewer/qglviewer.cpp
Normal file
File diff suppressed because it is too large
Load Diff
1278
QGLViewer/qglviewer.h
Normal file
1278
QGLViewer/qglviewer.h
Normal file
File diff suppressed because it is too large
Load Diff
BIN
QGLViewer/qglviewer.icns
Normal file
BIN
QGLViewer/qglviewer.icns
Normal file
Binary file not shown.
BIN
QGLViewer/qglviewer_fr.qm
Normal file
BIN
QGLViewer/qglviewer_fr.qm
Normal file
Binary file not shown.
608
QGLViewer/qglviewer_fr.ts
Normal file
608
QGLViewer/qglviewer_fr.ts
Normal file
@ -0,0 +1,608 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!DOCTYPE TS>
|
||||||
|
<TS version="2.1" language="fr_FR">
|
||||||
|
<context>
|
||||||
|
<name>ImageInterface</name>
|
||||||
|
<message>
|
||||||
|
<source>Image settings</source>
|
||||||
|
<translation>Réglages d'image</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Width</source>
|
||||||
|
<translation>Largeur</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source> px</source>
|
||||||
|
<translation> px</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Width of the image (in pixels)</source>
|
||||||
|
<translation>Largeur de l'image (en pixels)</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Height</source>
|
||||||
|
<translation>Hauteur</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Height of the image (in pixels)</source>
|
||||||
|
<translation>Hauteur de l'image (en pixels)</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Image quality</source>
|
||||||
|
<translation>Qualité d'image</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Between 0 (smallest files) and 100 (highest quality)</source>
|
||||||
|
<translation>Entre 0 (taille de fichier minimale) et 100 (qualité maximale)</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Oversampling</source>
|
||||||
|
<translation>Suréchantillonage</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>x </source>
|
||||||
|
<translation>x </translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Antialiases image (when larger then 1.0)</source>
|
||||||
|
<translation>Anti-alliassage de l'image (si supérieur à 1.0)</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Use white background</source>
|
||||||
|
<translation>Fond blanc</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Use white as background color</source>
|
||||||
|
<translation>Mettre du blanc en couleur de fond</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Expand frustum if needed</source>
|
||||||
|
<translation>Etendre la pyramide de vue ( frustum) si nécessaire</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>When image aspect ratio differs from viewer's one, expand frustum as needed. Fits inside current frustum otherwise.</source>
|
||||||
|
<translation>Lorsque le rapport de dimensions de l'image diffère de celui de la fenêtre, étendre la pyramide de vue (frustum) en conséquence. L'image est ajustée à l'intérieur de la vue actuelle sinon. </translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>OK</source>
|
||||||
|
<translation>Ok</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Cancel</source>
|
||||||
|
<translation>Annuler</translation>
|
||||||
|
</message>
|
||||||
|
</context>
|
||||||
|
<context>
|
||||||
|
<name>QGLViewer</name>
|
||||||
|
<message>
|
||||||
|
<source>snapshot</source>
|
||||||
|
<comment>Default snapshot file name</comment>
|
||||||
|
<translation>capture</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>%1Hz</source>
|
||||||
|
<comment>Frames per seconds, in Hertz</comment>
|
||||||
|
<translation>%1Hz</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Toggles the display of the FPS</source>
|
||||||
|
<comment>DISPLAY_FPS action description</comment>
|
||||||
|
<translation>Active ou non l'affichage de la fréquence d'affiichage</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Saves a screenshot</source>
|
||||||
|
<comment>SAVE_SCREENSHOT action description</comment>
|
||||||
|
<translation>Sauvegarde une capture d'écran</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Toggles full screen display</source>
|
||||||
|
<comment>FULL_SCREEN action description</comment>
|
||||||
|
<translation>Passe ou non en mode plein écran</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Toggles the display of the world axis</source>
|
||||||
|
<comment>DRAW_AXIS action description</comment>
|
||||||
|
<translation>Affiche ou non le repère du monde</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Toggles the display of the XY grid</source>
|
||||||
|
<comment>DRAW_GRID action description</comment>
|
||||||
|
<translation>Affiche ou non la grille XY</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Changes camera mode (observe or fly)</source>
|
||||||
|
<comment>CAMERA_MODE action description</comment>
|
||||||
|
<translation>Change le mode de la caméra (observateur ou vol)</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Toggles stereo display</source>
|
||||||
|
<comment>STEREO action description</comment>
|
||||||
|
<translation>Affiche ou non en stéréo</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Opens this help window</source>
|
||||||
|
<comment>HELP action description</comment>
|
||||||
|
<translation>Ouvre la fenêtre d'aide</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Starts/stops the animation</source>
|
||||||
|
<comment>ANIMATION action description</comment>
|
||||||
|
<translation>Démarre/arrête l'animation</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Toggles camera paths display</source>
|
||||||
|
<comment>EDIT_CAMERA action description</comment>
|
||||||
|
<translation>Affiche ou non les chemins de caméra</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Toggles the display of the text</source>
|
||||||
|
<comment>ENABLE_TEXT action description</comment>
|
||||||
|
<translation>Affiche ou non les textes</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Exits program</source>
|
||||||
|
<comment>EXIT_VIEWER action description</comment>
|
||||||
|
<translation>Quitte l'application</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Moves camera left</source>
|
||||||
|
<comment>MOVE_CAMERA_LEFT action description</comment>
|
||||||
|
<translation>Déplace la caméra sur la gauche</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Moves camera right</source>
|
||||||
|
<comment>MOVE_CAMERA_RIGHT action description</comment>
|
||||||
|
<translation>Déplace la caméra sur la droite</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Moves camera up</source>
|
||||||
|
<comment>MOVE_CAMERA_UP action description</comment>
|
||||||
|
<translation>Déplace la caméra vers le haut</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Moves camera down</source>
|
||||||
|
<comment>MOVE_CAMERA_DOWN action description</comment>
|
||||||
|
<translation>Déplace la caméra vers le bas</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Increases fly speed</source>
|
||||||
|
<comment>INCREASE_FLYSPEED action description</comment>
|
||||||
|
<translation>Augmente la vitesse de vol</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Decreases fly speed</source>
|
||||||
|
<comment>DECREASE_FLYSPEED action description</comment>
|
||||||
|
<translation>Diminue la vitesse de vol</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Copies a snapshot to clipboard</source>
|
||||||
|
<comment>SNAPSHOT_TO_CLIPBOARD action description</comment>
|
||||||
|
<translation>Place une capture d'écran dans le presse-papier</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Stereo not supported</source>
|
||||||
|
<comment>Message box window title</comment>
|
||||||
|
<translation>Stéréo non supportée</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Stereo is not supported on this display.</source>
|
||||||
|
<translation>Affichage en stéréo non supporté sur cette machine.</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Rotates</source>
|
||||||
|
<comment>ROTATE mouse action</comment>
|
||||||
|
<translation>Tourne</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Zooms</source>
|
||||||
|
<comment>ZOOM mouse action</comment>
|
||||||
|
<translation>Zoome</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Translates</source>
|
||||||
|
<comment>TRANSLATE mouse action</comment>
|
||||||
|
<translation>Translate</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Moves backward</source>
|
||||||
|
<comment>MOVE_BACKWARD mouse action</comment>
|
||||||
|
<translation>Recule</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Horizontally/Vertically translates</source>
|
||||||
|
<comment>SCREEN_TRANSLATE mouse action</comment>
|
||||||
|
<translation>Translate horizontalement/verticalement</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Moves forward</source>
|
||||||
|
<comment>MOVE_FORWARD mouse action</comment>
|
||||||
|
<translation>Avance</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Looks around</source>
|
||||||
|
<comment>LOOK_AROUND mouse action</comment>
|
||||||
|
<translation>Regarde</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Rotates in screen plane</source>
|
||||||
|
<comment>SCREEN_ROTATE mouse action</comment>
|
||||||
|
<translation>Pivote dans le plan écran</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Rolls</source>
|
||||||
|
<comment>ROLL mouse action</comment>
|
||||||
|
<translation>Pivote</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Drives</source>
|
||||||
|
<comment>DRIVE mouse action</comment>
|
||||||
|
<translation>Avance</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Zooms on region for</source>
|
||||||
|
<comment>ZOOM_ON_REGION mouse action</comment>
|
||||||
|
<translation>Zoome sur la région pour</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Zooms on pixel</source>
|
||||||
|
<comment>ZOOM_ON_PIXEL click action</comment>
|
||||||
|
<translation>Zoome sur le pixel</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Zooms to fit scene</source>
|
||||||
|
<comment>ZOOM_TO_FIT click action</comment>
|
||||||
|
<translation>Zoome pour ajuster à la scène</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Selects</source>
|
||||||
|
<comment>SELECT click action</comment>
|
||||||
|
<translation>Sélectionne</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Sets pivot point</source>
|
||||||
|
<comment>RAP_FROM_PIXEL click action</comment>
|
||||||
|
<translation>Définit le point de rotation</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Resets pivot point</source>
|
||||||
|
<comment>RAP_IS_CENTER click action</comment>
|
||||||
|
<translation>Restaure le point de rotation</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Centers manipulated frame</source>
|
||||||
|
<comment>CENTER_FRAME click action</comment>
|
||||||
|
<translation>Centre le repère manipulé</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Centers scene</source>
|
||||||
|
<comment>CENTER_SCENE click action</comment>
|
||||||
|
<translation>Centre la scène</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Shows entire scene</source>
|
||||||
|
<comment>SHOW_ENTIRE_SCENE click action</comment>
|
||||||
|
<translation>Affiche toute la scène</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Aligns manipulated frame</source>
|
||||||
|
<comment>ALIGN_FRAME click action</comment>
|
||||||
|
<translation>Aligne le repère manipulé</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Aligns camera</source>
|
||||||
|
<comment>ALIGN_CAMERA click action</comment>
|
||||||
|
<translation>Aligne la caméra</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Camera paths are controlled using the %1 keys (noted <i>Fx</i> below):</source>
|
||||||
|
<comment>Help window key tab camera keys</comment>
|
||||||
|
<translation>Les chemins de caméra sont contrôlés avec les touches %1 (notées <i>Fx</i> ci-dessous) :</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Key(s)</source>
|
||||||
|
<comment>Keys column header in help window mouse tab</comment>
|
||||||
|
<translation>Touche(s)</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Description</source>
|
||||||
|
<comment>Description column header in help window mouse tab</comment>
|
||||||
|
<translation>Description</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Standard viewer keys</source>
|
||||||
|
<comment>In help window keys tab</comment>
|
||||||
|
<translation>Raccourcis standards</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Fx</source>
|
||||||
|
<comment>Generic function key (F1..F12)</comment>
|
||||||
|
<translation>Fx</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Plays path (or resets saved position)</source>
|
||||||
|
<translation>Joue le chemin (ou restaure la position sauvegardée)</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Adds a key frame to path (or defines a position)</source>
|
||||||
|
<translation>Ajoute une position clef au chemin (ou définit une position)</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Deletes path (or saved position)</source>
|
||||||
|
<translation>Supprime le chemin (ou la position)</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Button(s)</source>
|
||||||
|
<comment>Buttons column header in help window mouse tab</comment>
|
||||||
|
<translation>Bouton(s)</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Standard mouse bindings</source>
|
||||||
|
<comment>In help window mouse tab</comment>
|
||||||
|
<translation>Actions souris standards</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Wheel</source>
|
||||||
|
<comment>Mouse wheel</comment>
|
||||||
|
<translation>Molette</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>&Help</source>
|
||||||
|
<comment>Help window tab title</comment>
|
||||||
|
<translation>&Aide</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>&Keyboard</source>
|
||||||
|
<comment>Help window tab title</comment>
|
||||||
|
<translation>&Clavier</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>&Mouse</source>
|
||||||
|
<comment>Help window tab title</comment>
|
||||||
|
<translation>&Souris</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Help</source>
|
||||||
|
<comment>Help window title</comment>
|
||||||
|
<translation>Aide</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Path %1 deleted</source>
|
||||||
|
<comment>Feedback message</comment>
|
||||||
|
<translation>Chemin %1 supprimé</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Position %1 deleted</source>
|
||||||
|
<comment>Feedback message</comment>
|
||||||
|
<translation>Position %1 supprimée</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Path %1, position %2 added</source>
|
||||||
|
<comment>Feedback message</comment>
|
||||||
|
<translation>Chemin %1, position %2 ajoutée</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Position %1 saved</source>
|
||||||
|
<comment>Feedback message</comment>
|
||||||
|
<translation>Position %1 sauvegardée</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Camera in observer mode</source>
|
||||||
|
<comment>Feedback message</comment>
|
||||||
|
<translation>Caméra en mode observateur</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Camera in fly mode</source>
|
||||||
|
<comment>Feedback message</comment>
|
||||||
|
<translation>Caméra en mode vol</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Save to file error</source>
|
||||||
|
<comment>Message box window title</comment>
|
||||||
|
<translation>Erreur lors de la sauvegarde</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>State file name (%1) references a directory instead of a file.</source>
|
||||||
|
<translation>Le nom du fichier d'état (%1) référence un répértoire et non un fichier.</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Unable to create directory %1</source>
|
||||||
|
<translation>Le répértoire %1 ne peut être créé</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Unable to save to file %1</source>
|
||||||
|
<translation>Impossible de sauvegarder le fichier %1</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Problem in state restoration</source>
|
||||||
|
<comment>Message box window title</comment>
|
||||||
|
<translation>Problème lors de la restauration de l'état</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>File %1 is not readable.</source>
|
||||||
|
<translation>Le fichier %1 n'est pas lisible.</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Open file error</source>
|
||||||
|
<comment>Message box window title</comment>
|
||||||
|
<translation>Erreur d'ouverture de fichier</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Unable to open file %1</source>
|
||||||
|
<translation>Le fichier %1 ne peut être ouvert</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Left</source>
|
||||||
|
<comment>left mouse button</comment>
|
||||||
|
<translation>Gauche</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Middle</source>
|
||||||
|
<comment>middle mouse button</comment>
|
||||||
|
<translation>Milieu</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Right</source>
|
||||||
|
<comment>right mouse button</comment>
|
||||||
|
<translation>Droit</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source> double click</source>
|
||||||
|
<comment>Suffix after mouse button</comment>
|
||||||
|
<translation>Double clic </translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>camera</source>
|
||||||
|
<comment>Suffix after action</comment>
|
||||||
|
<translation>la caméra</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>manipulated frame</source>
|
||||||
|
<comment>Suffix after action</comment>
|
||||||
|
<translation>le repère manipulé</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source> with </source>
|
||||||
|
<comment>As in : Left button with Ctrl pressed</comment>
|
||||||
|
<translation> avec </translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source> pressed</source>
|
||||||
|
<comment>As in : Left button with Ctrl pressed</comment>
|
||||||
|
<translation> enfoncé</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>%1%2%3%4%5%6</source>
|
||||||
|
<comment>Modifier / button or wheel / double click / with / button / pressed</comment>
|
||||||
|
<translation>%1%3%2%4%5%6</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>No help available.</source>
|
||||||
|
<translation>Pas d'aide disponible.</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Exporter error</source>
|
||||||
|
<comment>Message box window title</comment>
|
||||||
|
<translation>Erreur d'export</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Unable to open file %1.</source>
|
||||||
|
<translation>Impossible d'ouvrir le ficher %1.</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>BSP Construction</source>
|
||||||
|
<translation>Construction du BSP</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Exporting to file %1</source>
|
||||||
|
<translation>Export vers le fichier %1</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Parsing feedback buffer.</source>
|
||||||
|
<translation>Parcours du feedback buffer.</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Topological sort</source>
|
||||||
|
<translation>Tri topologique</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Advanced topological sort</source>
|
||||||
|
<translation>Tri topologique avancé</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Rendering...</source>
|
||||||
|
<translation>Rendu...</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Visibility optimization</source>
|
||||||
|
<translation>Optimisation de la visibilité</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>&About</source>
|
||||||
|
<comment>Help window about title</comment>
|
||||||
|
<translation>À &propos</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source><h1>libQGLViewer</h1><h3>Version %1</h3><br>A versatile 3D viewer based on OpenGL and Qt<br>Copyright 2002-%2 Gilles Debunne<br><code>%3</code></source>
|
||||||
|
<translation><h1>libQGLViewer</h1><h3>Version %1</h3><br>Un afficheur 3D généraliste basé sur OpenGL et Qt<br>Copyright 2002-%2 Gilles Debunne<br><code>%3</code></translation>
|
||||||
|
</message>
|
||||||
|
</context>
|
||||||
|
<context>
|
||||||
|
<name>VRenderInterface</name>
|
||||||
|
<message>
|
||||||
|
<source>Vectorial rendering options</source>
|
||||||
|
<translation>Options de rendu vectoriel</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Include hidden parts</source>
|
||||||
|
<translation>Inclure les parties cachées</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Cull back faces</source>
|
||||||
|
<translation>Supprimer les faces arrières</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Back faces (non clockwise point ordering) are removed from the output</source>
|
||||||
|
<translation>Les faces orientées vers l'arrière (points ordonnés dans le sens anti-horaire) sont supprimées du résultat (Back Face Culling)</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Black and white</source>
|
||||||
|
<translation>Noir et blanc</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Black and white rendering</source>
|
||||||
|
<translation>Rendu en noir et blanc</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Color background</source>
|
||||||
|
<translation>Fond avec une couleur</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Use current background color instead of white</source>
|
||||||
|
<translation>Utiliser la couleur de fond actuelle à la place du blanc</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Tighten bounding box</source>
|
||||||
|
<translation>Ajuster la boîte englobante</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Fit output bounding box to current display</source>
|
||||||
|
<translation>Ajuster la boîte englobante de la sortie à ce qui est actuellement affiché</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Polygon depth sorting method</source>
|
||||||
|
<translation>Méthode de tri de la profondeur des polygônes</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>No sorting</source>
|
||||||
|
<translation>Pas de tri</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Topological</source>
|
||||||
|
<translation>Topologique</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Advanced topological</source>
|
||||||
|
<translation>Topologique avancé</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Save</source>
|
||||||
|
<translation>Sauvegarder</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Cancel</source>
|
||||||
|
<translation>Annuler</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Hidden polygons are also included in the output (usually twice bigger)</source>
|
||||||
|
<translation>Inclure les polygônes cachés dans le résultat (alors habituellement deux fois plus gros)</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Sort method:</source>
|
||||||
|
<translation>Méthode de tri :</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>BSP</source>
|
||||||
|
<translation>BSP</translation>
|
||||||
|
</message>
|
||||||
|
</context>
|
||||||
|
</TS>
|
||||||
552
QGLViewer/quaternion.cpp
Normal file
552
QGLViewer/quaternion.cpp
Normal file
@ -0,0 +1,552 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
|
||||||
|
Copyright (C) 2002-2014 Gilles Debunne. All rights reserved.
|
||||||
|
|
||||||
|
This file is part of the QGLViewer library version 2.6.3.
|
||||||
|
|
||||||
|
http://www.libqglviewer.com - contact@libqglviewer.com
|
||||||
|
|
||||||
|
This file may be used under the terms of the GNU General Public License
|
||||||
|
versions 2.0 or 3.0 as published by the Free Software Foundation and
|
||||||
|
appearing in the LICENSE file included in the packaging of this file.
|
||||||
|
In addition, as a special exception, Gilles Debunne gives you certain
|
||||||
|
additional rights, described in the file GPL_EXCEPTION in this package.
|
||||||
|
|
||||||
|
libQGLViewer uses dual licensing. Commercial/proprietary software must
|
||||||
|
purchase a libQGLViewer Commercial License.
|
||||||
|
|
||||||
|
This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
|
||||||
|
WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
|
||||||
|
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
#include "domUtils.h"
|
||||||
|
#include "quaternion.h"
|
||||||
|
#include <stdlib.h> // RAND_MAX
|
||||||
|
|
||||||
|
// All the methods are declared inline in Quaternion.h
|
||||||
|
using namespace qglviewer;
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
/*! Constructs a Quaternion that will rotate from the \p from direction to the \p to direction.
|
||||||
|
|
||||||
|
Note that this rotation is not uniquely defined. The selected axis is usually orthogonal to \p from
|
||||||
|
and \p to, minimizing the rotation angle. This method is robust and can handle small or almost identical vectors. */
|
||||||
|
Quaternion::Quaternion(const Vec& from, const Vec& to)
|
||||||
|
{
|
||||||
|
const qreal epsilon = 1E-10;
|
||||||
|
|
||||||
|
const qreal fromSqNorm = from.squaredNorm();
|
||||||
|
const qreal toSqNorm = to.squaredNorm();
|
||||||
|
// Identity Quaternion when one vector is null
|
||||||
|
if ((fromSqNorm < epsilon) || (toSqNorm < epsilon))
|
||||||
|
{
|
||||||
|
q[0]=q[1]=q[2]=0.0;
|
||||||
|
q[3]=1.0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Vec axis = cross(from, to);
|
||||||
|
const qreal axisSqNorm = axis.squaredNorm();
|
||||||
|
|
||||||
|
// Aligned vectors, pick any axis, not aligned with from or to
|
||||||
|
if (axisSqNorm < epsilon)
|
||||||
|
axis = from.orthogonalVec();
|
||||||
|
|
||||||
|
qreal angle = asin(sqrt(axisSqNorm / (fromSqNorm * toSqNorm)));
|
||||||
|
|
||||||
|
if (from*to < 0.0)
|
||||||
|
angle = M_PI-angle;
|
||||||
|
|
||||||
|
setAxisAngle(axis, angle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Returns the image of \p v by the Quaternion inverse() rotation.
|
||||||
|
|
||||||
|
rotate() performs an inverse transformation. Same as inverse().rotate(v). */
|
||||||
|
Vec Quaternion::inverseRotate(const Vec& v) const
|
||||||
|
{
|
||||||
|
return inverse().rotate(v);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Returns the image of \p v by the Quaternion rotation.
|
||||||
|
|
||||||
|
See also inverseRotate() and operator*(const Quaternion&, const Vec&). */
|
||||||
|
Vec Quaternion::rotate(const Vec& v) const
|
||||||
|
{
|
||||||
|
const qreal q00 = 2.0 * q[0] * q[0];
|
||||||
|
const qreal q11 = 2.0 * q[1] * q[1];
|
||||||
|
const qreal q22 = 2.0 * q[2] * q[2];
|
||||||
|
|
||||||
|
const qreal q01 = 2.0 * q[0] * q[1];
|
||||||
|
const qreal q02 = 2.0 * q[0] * q[2];
|
||||||
|
const qreal q03 = 2.0 * q[0] * q[3];
|
||||||
|
|
||||||
|
const qreal q12 = 2.0 * q[1] * q[2];
|
||||||
|
const qreal q13 = 2.0 * q[1] * q[3];
|
||||||
|
|
||||||
|
const qreal q23 = 2.0 * q[2] * q[3];
|
||||||
|
|
||||||
|
return Vec((1.0 - q11 - q22)*v[0] + ( q01 - q23)*v[1] + ( q02 + q13)*v[2],
|
||||||
|
( q01 + q23)*v[0] + (1.0 - q22 - q00)*v[1] + ( q12 - q03)*v[2],
|
||||||
|
( q02 - q13)*v[0] + ( q12 + q03)*v[1] + (1.0 - q11 - q00)*v[2] );
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Set the Quaternion from a (supposedly correct) 3x3 rotation matrix.
|
||||||
|
|
||||||
|
The matrix is expressed in European format: its three \e columns are the images by the rotation of
|
||||||
|
the three vectors of an orthogonal basis. Note that OpenGL uses a symmetric representation for its
|
||||||
|
matrices.
|
||||||
|
|
||||||
|
setFromRotatedBasis() sets a Quaternion from the three axis of a rotated frame. It actually fills
|
||||||
|
the three columns of a matrix with these rotated basis vectors and calls this method. */
|
||||||
|
void Quaternion::setFromRotationMatrix(const qreal m[3][3])
|
||||||
|
{
|
||||||
|
// Compute one plus the trace of the matrix
|
||||||
|
const qreal onePlusTrace = 1.0 + m[0][0] + m[1][1] + m[2][2];
|
||||||
|
|
||||||
|
if (onePlusTrace > 1E-5)
|
||||||
|
{
|
||||||
|
// Direct computation
|
||||||
|
const qreal s = sqrt(onePlusTrace) * 2.0;
|
||||||
|
q[0] = (m[2][1] - m[1][2]) / s;
|
||||||
|
q[1] = (m[0][2] - m[2][0]) / s;
|
||||||
|
q[2] = (m[1][0] - m[0][1]) / s;
|
||||||
|
q[3] = 0.25 * s;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Computation depends on major diagonal term
|
||||||
|
if ((m[0][0] > m[1][1])&(m[0][0] > m[2][2]))
|
||||||
|
{
|
||||||
|
const qreal s = sqrt(1.0 + m[0][0] - m[1][1] - m[2][2]) * 2.0;
|
||||||
|
q[0] = 0.25 * s;
|
||||||
|
q[1] = (m[0][1] + m[1][0]) / s;
|
||||||
|
q[2] = (m[0][2] + m[2][0]) / s;
|
||||||
|
q[3] = (m[1][2] - m[2][1]) / s;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
if (m[1][1] > m[2][2])
|
||||||
|
{
|
||||||
|
const qreal s = sqrt(1.0 + m[1][1] - m[0][0] - m[2][2]) * 2.0;
|
||||||
|
q[0] = (m[0][1] + m[1][0]) / s;
|
||||||
|
q[1] = 0.25 * s;
|
||||||
|
q[2] = (m[1][2] + m[2][1]) / s;
|
||||||
|
q[3] = (m[0][2] - m[2][0]) / s;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
const qreal s = sqrt(1.0 + m[2][2] - m[0][0] - m[1][1]) * 2.0;
|
||||||
|
q[0] = (m[0][2] + m[2][0]) / s;
|
||||||
|
q[1] = (m[1][2] + m[2][1]) / s;
|
||||||
|
q[2] = 0.25 * s;
|
||||||
|
q[3] = (m[0][1] - m[1][0]) / s;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
normalize();
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifndef DOXYGEN
|
||||||
|
void Quaternion::setFromRotationMatrix(const float m[3][3])
|
||||||
|
{
|
||||||
|
qWarning("setFromRotationMatrix now expects a double[3][3] parameter");
|
||||||
|
|
||||||
|
qreal mat[3][3];
|
||||||
|
for (int i=0; i<3; ++i)
|
||||||
|
for (int j=0; j<3; ++j)
|
||||||
|
mat[i][j] = qreal(m[i][j]);
|
||||||
|
|
||||||
|
setFromRotationMatrix(mat);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Quaternion::setFromRotatedBase(const Vec& X, const Vec& Y, const Vec& Z)
|
||||||
|
{
|
||||||
|
qWarning("setFromRotatedBase is deprecated, use setFromRotatedBasis instead");
|
||||||
|
setFromRotatedBasis(X,Y,Z);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*! Sets the Quaternion from the three rotated vectors of an orthogonal basis.
|
||||||
|
|
||||||
|
The three vectors do not have to be normalized but must be orthogonal and direct (X^Y=k*Z, with k>0).
|
||||||
|
|
||||||
|
\code
|
||||||
|
Quaternion q;
|
||||||
|
q.setFromRotatedBasis(X, Y, Z);
|
||||||
|
// Now q.rotate(Vec(1,0,0)) == X and q.inverseRotate(X) == Vec(1,0,0)
|
||||||
|
// Same goes for Y and Z with Vec(0,1,0) and Vec(0,0,1).
|
||||||
|
\endcode
|
||||||
|
|
||||||
|
See also setFromRotationMatrix() and Quaternion(const Vec&, const Vec&). */
|
||||||
|
void Quaternion::setFromRotatedBasis(const Vec& X, const Vec& Y, const Vec& Z)
|
||||||
|
{
|
||||||
|
qreal m[3][3];
|
||||||
|
qreal normX = X.norm();
|
||||||
|
qreal normY = Y.norm();
|
||||||
|
qreal normZ = Z.norm();
|
||||||
|
|
||||||
|
for (int i=0; i<3; ++i)
|
||||||
|
{
|
||||||
|
m[i][0] = X[i] / normX;
|
||||||
|
m[i][1] = Y[i] / normY;
|
||||||
|
m[i][2] = Z[i] / normZ;
|
||||||
|
}
|
||||||
|
|
||||||
|
setFromRotationMatrix(m);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Returns the axis vector and the angle (in radians) of the rotation represented by the Quaternion.
|
||||||
|
See the axis() and angle() documentations. */
|
||||||
|
void Quaternion::getAxisAngle(Vec& axis, qreal& angle) const
|
||||||
|
{
|
||||||
|
angle = 2.0 * acos(q[3]);
|
||||||
|
axis = Vec(q[0], q[1], q[2]);
|
||||||
|
const qreal sinus = axis.norm();
|
||||||
|
if (sinus > 1E-8)
|
||||||
|
axis /= sinus;
|
||||||
|
|
||||||
|
if (angle > M_PI)
|
||||||
|
{
|
||||||
|
angle = 2.0 * qreal(M_PI) - angle;
|
||||||
|
axis = -axis;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Returns the normalized axis direction of the rotation represented by the Quaternion.
|
||||||
|
|
||||||
|
It is null for an identity Quaternion. See also angle() and getAxisAngle(). */
|
||||||
|
Vec Quaternion::axis() const
|
||||||
|
{
|
||||||
|
Vec res = Vec(q[0], q[1], q[2]);
|
||||||
|
const qreal sinus = res.norm();
|
||||||
|
if (sinus > 1E-8)
|
||||||
|
res /= sinus;
|
||||||
|
return (acos(q[3]) <= M_PI/2.0) ? res : -res;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Returns the angle (in radians) of the rotation represented by the Quaternion.
|
||||||
|
|
||||||
|
This value is always in the range [0-pi]. Larger rotational angles are obtained by inverting the
|
||||||
|
axis() direction.
|
||||||
|
|
||||||
|
See also axis() and getAxisAngle(). */
|
||||||
|
qreal Quaternion::angle() const
|
||||||
|
{
|
||||||
|
const qreal angle = 2.0 * acos(q[3]);
|
||||||
|
return (angle <= M_PI) ? angle : 2.0*M_PI - angle;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Returns an XML \c QDomElement that represents the Quaternion.
|
||||||
|
|
||||||
|
\p name is the name of the QDomElement tag. \p doc is the \c QDomDocument factory used to create
|
||||||
|
QDomElement.
|
||||||
|
|
||||||
|
When output to a file, the resulting QDomElement will look like:
|
||||||
|
\code
|
||||||
|
<name q0=".." q1=".." q2=".." q3=".." />
|
||||||
|
\endcode
|
||||||
|
|
||||||
|
Use initFromDOMElement() to restore the Quaternion state from the resulting \c QDomElement. See
|
||||||
|
also the Quaternion(const QDomElement&) constructor.
|
||||||
|
|
||||||
|
See the Vec::domElement() documentation for a complete QDomDocument creation and saving example.
|
||||||
|
|
||||||
|
See also Frame::domElement(), Camera::domElement(), KeyFrameInterpolator::domElement()... */
|
||||||
|
QDomElement Quaternion::domElement(const QString& name, QDomDocument& document) const
|
||||||
|
{
|
||||||
|
QDomElement de = document.createElement(name);
|
||||||
|
de.setAttribute("q0", QString::number(q[0]));
|
||||||
|
de.setAttribute("q1", QString::number(q[1]));
|
||||||
|
de.setAttribute("q2", QString::number(q[2]));
|
||||||
|
de.setAttribute("q3", QString::number(q[3]));
|
||||||
|
return de;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Restores the Quaternion state from a \c QDomElement created by domElement().
|
||||||
|
|
||||||
|
The \c QDomElement should contain the \c q0, \c q1 , \c q2 and \c q3 attributes. If one of these
|
||||||
|
attributes is missing or is not a number, a warning is displayed and these fields are respectively
|
||||||
|
set to 0.0, 0.0, 0.0 and 1.0 (identity Quaternion).
|
||||||
|
|
||||||
|
See also the Quaternion(const QDomElement&) constructor. */
|
||||||
|
void Quaternion::initFromDOMElement(const QDomElement& element)
|
||||||
|
{
|
||||||
|
Quaternion q(element);
|
||||||
|
*this = q;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Constructs a Quaternion from a \c QDomElement representing an XML code of the form
|
||||||
|
\code< anyTagName q0=".." q1=".." q2=".." q3=".." />\endcode
|
||||||
|
|
||||||
|
If one of these attributes is missing or is not a number, a warning is displayed and the associated
|
||||||
|
value is respectively set to 0, 0, 0 and 1 (identity Quaternion).
|
||||||
|
|
||||||
|
See also domElement() and initFromDOMElement(). */
|
||||||
|
Quaternion::Quaternion(const QDomElement& element)
|
||||||
|
{
|
||||||
|
QStringList attribute;
|
||||||
|
attribute << "q0" << "q1" << "q2" << "q3";
|
||||||
|
for (int i=0; i<attribute.size(); ++i)
|
||||||
|
q[i] = DomUtils::qrealFromDom(element, attribute[i], ((i<3)?0.0:1.0));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Returns the Quaternion associated 4x4 OpenGL rotation matrix.
|
||||||
|
|
||||||
|
Use \c glMultMatrixd(q.matrix()) to apply the rotation represented by Quaternion \c q to the
|
||||||
|
current OpenGL matrix.
|
||||||
|
|
||||||
|
See also getMatrix(), getRotationMatrix() and inverseMatrix().
|
||||||
|
|
||||||
|
\attention The result is only valid until the next call to matrix(). Use it immediately (as shown
|
||||||
|
above) or consider using getMatrix() instead.
|
||||||
|
|
||||||
|
\attention The matrix is given in OpenGL format (row-major order) and is the transpose of the
|
||||||
|
actual mathematical European representation. Consider using getRotationMatrix() instead. */
|
||||||
|
const GLdouble* Quaternion::matrix() const
|
||||||
|
{
|
||||||
|
static GLdouble m[4][4];
|
||||||
|
getMatrix(m);
|
||||||
|
return (const GLdouble*)(m);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Fills \p m with the OpenGL representation of the Quaternion rotation.
|
||||||
|
|
||||||
|
Use matrix() if you do not need to store this matrix and simply want to alter the current OpenGL
|
||||||
|
matrix. See also getInverseMatrix() and Frame::getMatrix(). */
|
||||||
|
void Quaternion::getMatrix(GLdouble m[4][4]) const
|
||||||
|
{
|
||||||
|
const qreal q00 = 2.0 * q[0] * q[0];
|
||||||
|
const qreal q11 = 2.0 * q[1] * q[1];
|
||||||
|
const qreal q22 = 2.0 * q[2] * q[2];
|
||||||
|
|
||||||
|
const qreal q01 = 2.0 * q[0] * q[1];
|
||||||
|
const qreal q02 = 2.0 * q[0] * q[2];
|
||||||
|
const qreal q03 = 2.0 * q[0] * q[3];
|
||||||
|
|
||||||
|
const qreal q12 = 2.0 * q[1] * q[2];
|
||||||
|
const qreal q13 = 2.0 * q[1] * q[3];
|
||||||
|
|
||||||
|
const qreal q23 = 2.0 * q[2] * q[3];
|
||||||
|
|
||||||
|
m[0][0] = 1.0 - q11 - q22;
|
||||||
|
m[1][0] = q01 - q23;
|
||||||
|
m[2][0] = q02 + q13;
|
||||||
|
|
||||||
|
m[0][1] = q01 + q23;
|
||||||
|
m[1][1] = 1.0 - q22 - q00;
|
||||||
|
m[2][1] = q12 - q03;
|
||||||
|
|
||||||
|
m[0][2] = q02 - q13;
|
||||||
|
m[1][2] = q12 + q03;
|
||||||
|
m[2][2] = 1.0 - q11 - q00;
|
||||||
|
|
||||||
|
m[0][3] = 0.0;
|
||||||
|
m[1][3] = 0.0;
|
||||||
|
m[2][3] = 0.0;
|
||||||
|
|
||||||
|
m[3][0] = 0.0;
|
||||||
|
m[3][1] = 0.0;
|
||||||
|
m[3][2] = 0.0;
|
||||||
|
m[3][3] = 1.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Same as getMatrix(), but with a \c GLdouble[16] parameter. See also getInverseMatrix() and Frame::getMatrix(). */
|
||||||
|
void Quaternion::getMatrix(GLdouble m[16]) const
|
||||||
|
{
|
||||||
|
static GLdouble mat[4][4];
|
||||||
|
getMatrix(mat);
|
||||||
|
int count = 0;
|
||||||
|
for (int i=0; i<4; ++i)
|
||||||
|
for (int j=0; j<4; ++j)
|
||||||
|
m[count++] = mat[i][j];
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Fills \p m with the 3x3 rotation matrix associated with the Quaternion.
|
||||||
|
|
||||||
|
See also getInverseRotationMatrix().
|
||||||
|
|
||||||
|
\attention \p m uses the European mathematical representation of the rotation matrix. Use matrix()
|
||||||
|
and getMatrix() to retrieve the OpenGL transposed version. */
|
||||||
|
void Quaternion::getRotationMatrix(qreal m[3][3]) const
|
||||||
|
{
|
||||||
|
static GLdouble mat[4][4];
|
||||||
|
getMatrix(mat);
|
||||||
|
for (int i=0; i<3; ++i)
|
||||||
|
for (int j=0; j<3; ++j)
|
||||||
|
// Beware of transposition
|
||||||
|
m[i][j] = qreal(mat[j][i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Returns the associated 4x4 OpenGL \e inverse rotation matrix. This is simply the matrix() of the
|
||||||
|
inverse().
|
||||||
|
|
||||||
|
\attention The result is only valid until the next call to inverseMatrix(). Use it immediately (as
|
||||||
|
in \c glMultMatrixd(q.inverseMatrix())) or use getInverseMatrix() instead.
|
||||||
|
|
||||||
|
\attention The matrix is given in OpenGL format (row-major order) and is the transpose of the
|
||||||
|
actual mathematical European representation. Consider using getInverseRotationMatrix() instead. */
|
||||||
|
const GLdouble* Quaternion::inverseMatrix() const
|
||||||
|
{
|
||||||
|
static GLdouble m[4][4];
|
||||||
|
getInverseMatrix(m);
|
||||||
|
return (const GLdouble*)(m);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Fills \p m with the OpenGL matrix corresponding to the inverse() rotation.
|
||||||
|
|
||||||
|
Use inverseMatrix() if you do not need to store this matrix and simply want to alter the current
|
||||||
|
OpenGL matrix. See also getMatrix(). */
|
||||||
|
void Quaternion::getInverseMatrix(GLdouble m[4][4]) const
|
||||||
|
{
|
||||||
|
inverse().getMatrix(m);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Same as getInverseMatrix(), but with a \c GLdouble[16] parameter. See also getMatrix(). */
|
||||||
|
void Quaternion::getInverseMatrix(GLdouble m[16]) const
|
||||||
|
{
|
||||||
|
inverse().getMatrix(m);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! \p m is set to the 3x3 \e inverse rotation matrix associated with the Quaternion.
|
||||||
|
|
||||||
|
\attention This is the classical mathematical rotation matrix. The OpenGL format uses its
|
||||||
|
transposed version. See inverseMatrix() and getInverseMatrix(). */
|
||||||
|
void Quaternion::getInverseRotationMatrix(qreal m[3][3]) const
|
||||||
|
{
|
||||||
|
static GLdouble mat[4][4];
|
||||||
|
getInverseMatrix(mat);
|
||||||
|
for (int i=0; i<3; ++i)
|
||||||
|
for (int j=0; j<3; ++j)
|
||||||
|
// Beware of transposition
|
||||||
|
m[i][j] = qreal(mat[j][i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*! Returns the slerp interpolation of Quaternions \p a and \p b, at time \p t.
|
||||||
|
|
||||||
|
\p t should range in [0,1]. Result is \p a when \p t=0 and \p b when \p t=1.
|
||||||
|
|
||||||
|
When \p allowFlip is \c true (default) the slerp interpolation will always use the "shortest path"
|
||||||
|
between the Quaternions' orientations, by "flipping" the source Quaternion if needed (see
|
||||||
|
negate()). */
|
||||||
|
Quaternion Quaternion::slerp(const Quaternion& a, const Quaternion& b, qreal t, bool allowFlip)
|
||||||
|
{
|
||||||
|
qreal cosAngle = Quaternion::dot(a, b);
|
||||||
|
|
||||||
|
qreal c1, c2;
|
||||||
|
// Linear interpolation for close orientations
|
||||||
|
if ((1.0 - fabs(cosAngle)) < 0.01)
|
||||||
|
{
|
||||||
|
c1 = 1.0 - t;
|
||||||
|
c2 = t;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Spherical interpolation
|
||||||
|
qreal angle = acos(fabs(cosAngle));
|
||||||
|
qreal sinAngle = sin(angle);
|
||||||
|
c1 = sin(angle * (1.0 - t)) / sinAngle;
|
||||||
|
c2 = sin(angle * t) / sinAngle;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use the shortest path
|
||||||
|
if (allowFlip && (cosAngle < 0.0))
|
||||||
|
c1 = -c1;
|
||||||
|
|
||||||
|
return Quaternion(c1*a[0] + c2*b[0], c1*a[1] + c2*b[1], c1*a[2] + c2*b[2], c1*a[3] + c2*b[3]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Returns the slerp interpolation of the two Quaternions \p a and \p b, at time \p t, using
|
||||||
|
tangents \p tgA and \p tgB.
|
||||||
|
|
||||||
|
The resulting Quaternion is "between" \p a and \p b (result is \p a when \p t=0 and \p b for \p
|
||||||
|
t=1).
|
||||||
|
|
||||||
|
Use squadTangent() to define the Quaternion tangents \p tgA and \p tgB. */
|
||||||
|
Quaternion Quaternion::squad(const Quaternion& a, const Quaternion& tgA, const Quaternion& tgB, const Quaternion& b, qreal t)
|
||||||
|
{
|
||||||
|
Quaternion ab = Quaternion::slerp(a, b, t);
|
||||||
|
Quaternion tg = Quaternion::slerp(tgA, tgB, t, false);
|
||||||
|
return Quaternion::slerp(ab, tg, 2.0*t*(1.0-t), false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Returns the logarithm of the Quaternion. See also exp(). */
|
||||||
|
Quaternion Quaternion::log()
|
||||||
|
{
|
||||||
|
qreal len = sqrt(q[0]*q[0] + q[1]*q[1] + q[2]*q[2]);
|
||||||
|
|
||||||
|
if (len < 1E-6)
|
||||||
|
return Quaternion(q[0], q[1], q[2], 0.0);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
qreal coef = acos(q[3]) / len;
|
||||||
|
return Quaternion(q[0]*coef, q[1]*coef, q[2]*coef, 0.0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Returns the exponential of the Quaternion. See also log(). */
|
||||||
|
Quaternion Quaternion::exp()
|
||||||
|
{
|
||||||
|
qreal theta = sqrt(q[0]*q[0] + q[1]*q[1] + q[2]*q[2]);
|
||||||
|
|
||||||
|
if (theta < 1E-6)
|
||||||
|
return Quaternion(q[0], q[1], q[2], cos(theta));
|
||||||
|
else
|
||||||
|
{
|
||||||
|
qreal coef = sin(theta) / theta;
|
||||||
|
return Quaternion(q[0]*coef, q[1]*coef, q[2]*coef, cos(theta));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Returns log(a. inverse() * b). Useful for squadTangent(). */
|
||||||
|
Quaternion Quaternion::lnDif(const Quaternion& a, const Quaternion& b)
|
||||||
|
{
|
||||||
|
Quaternion dif = a.inverse()*b;
|
||||||
|
dif.normalize();
|
||||||
|
return dif.log();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Returns a tangent Quaternion for \p center, defined by \p before and \p after Quaternions.
|
||||||
|
|
||||||
|
Useful for smooth spline interpolation of Quaternion with squad() and slerp(). */
|
||||||
|
Quaternion Quaternion::squadTangent(const Quaternion& before, const Quaternion& center, const Quaternion& after)
|
||||||
|
{
|
||||||
|
Quaternion l1 = Quaternion::lnDif(center,before);
|
||||||
|
Quaternion l2 = Quaternion::lnDif(center,after);
|
||||||
|
Quaternion e;
|
||||||
|
for (int i=0; i<4; ++i)
|
||||||
|
e.q[i] = -0.25 * (l1.q[i] + l2.q[i]);
|
||||||
|
e = center*(e.exp());
|
||||||
|
|
||||||
|
// if (Quaternion::dot(e,b) < 0.0)
|
||||||
|
// e.negate();
|
||||||
|
|
||||||
|
return e;
|
||||||
|
}
|
||||||
|
|
||||||
|
ostream& operator<<(ostream& o, const Quaternion& Q)
|
||||||
|
{
|
||||||
|
return o << Q[0] << '\t' << Q[1] << '\t' << Q[2] << '\t' << Q[3];
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Returns a random unit Quaternion.
|
||||||
|
|
||||||
|
You can create a randomly directed unit vector using:
|
||||||
|
\code
|
||||||
|
Vec randomDir = Quaternion::randomQuaternion() * Vec(1.0, 0.0, 0.0); // or any other Vec
|
||||||
|
\endcode
|
||||||
|
|
||||||
|
\note This function uses rand() to create pseudo-random numbers and the random number generator can
|
||||||
|
be initialized using srand().*/
|
||||||
|
Quaternion Quaternion::randomQuaternion()
|
||||||
|
{
|
||||||
|
// The rand() function is not very portable and may not be available on your system.
|
||||||
|
// Add the appropriate include or replace by an other random function in case of problem.
|
||||||
|
qreal seed = rand()/(qreal)RAND_MAX;
|
||||||
|
qreal r1 = sqrt(1.0 - seed);
|
||||||
|
qreal r2 = sqrt(seed);
|
||||||
|
qreal t1 = 2.0 * M_PI * (rand()/(qreal)RAND_MAX);
|
||||||
|
qreal t2 = 2.0 * M_PI * (rand()/(qreal)RAND_MAX);
|
||||||
|
return Quaternion(sin(t1)*r1, cos(t1)*r1, sin(t2)*r2, cos(t2)*r2);
|
||||||
|
}
|
||||||
311
QGLViewer/quaternion.h
Normal file
311
QGLViewer/quaternion.h
Normal file
@ -0,0 +1,311 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
|
||||||
|
Copyright (C) 2002-2014 Gilles Debunne. All rights reserved.
|
||||||
|
|
||||||
|
This file is part of the QGLViewer library version 2.6.3.
|
||||||
|
|
||||||
|
http://www.libqglviewer.com - contact@libqglviewer.com
|
||||||
|
|
||||||
|
This file may be used under the terms of the GNU General Public License
|
||||||
|
versions 2.0 or 3.0 as published by the Free Software Foundation and
|
||||||
|
appearing in the LICENSE file included in the packaging of this file.
|
||||||
|
In addition, as a special exception, Gilles Debunne gives you certain
|
||||||
|
additional rights, described in the file GPL_EXCEPTION in this package.
|
||||||
|
|
||||||
|
libQGLViewer uses dual licensing. Commercial/proprietary software must
|
||||||
|
purchase a libQGLViewer Commercial License.
|
||||||
|
|
||||||
|
This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
|
||||||
|
WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
|
||||||
|
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
#ifndef QGLVIEWER_QUATERNION_H
|
||||||
|
#define QGLVIEWER_QUATERNION_H
|
||||||
|
|
||||||
|
#include "vec.h"
|
||||||
|
#include <math.h>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
namespace qglviewer {
|
||||||
|
/*! \brief The Quaternion class represents 3D rotations and orientations.
|
||||||
|
\class Quaternion quaternion.h QGLViewer/quaternion.h
|
||||||
|
|
||||||
|
The Quaternion is an appropriate (although not very intuitive) representation for 3D rotations and
|
||||||
|
orientations. Many tools are provided to ease the definition of a Quaternion: see constructors,
|
||||||
|
setAxisAngle(), setFromRotationMatrix(), setFromRotatedBasis().
|
||||||
|
|
||||||
|
You can apply the rotation represented by the Quaternion to 3D points using rotate() and
|
||||||
|
inverseRotate(). See also the Frame class that represents a coordinate system and provides other
|
||||||
|
conversion functions like Frame::coordinatesOf() and Frame::transformOf().
|
||||||
|
|
||||||
|
You can apply the Quaternion \c q rotation to the OpenGL matrices using:
|
||||||
|
\code
|
||||||
|
glMultMatrixd(q.matrix());
|
||||||
|
// equvalent to glRotate(q.angle()*180.0/M_PI, q.axis().x, q.axis().y, q.axis().z);
|
||||||
|
\endcode
|
||||||
|
|
||||||
|
Quaternion is part of the \c qglviewer namespace, specify \c qglviewer::Quaternion or use the qglviewer
|
||||||
|
namespace: \code using namespace qglviewer; \endcode
|
||||||
|
|
||||||
|
<h3>Internal representation</h3>
|
||||||
|
|
||||||
|
The internal representation of a Quaternion corresponding to a rotation around axis \c axis, with an angle
|
||||||
|
\c alpha is made of four qreals (i.e. doubles) q[i]:
|
||||||
|
\code
|
||||||
|
{q[0],q[1],q[2]} = sin(alpha/2) * {axis[0],axis[1],axis[2]}
|
||||||
|
q[3] = cos(alpha/2)
|
||||||
|
\endcode
|
||||||
|
|
||||||
|
Note that certain implementations place the cosine term in first position (instead of last here).
|
||||||
|
|
||||||
|
The Quaternion is always normalized, so that its inverse() is actually its conjugate.
|
||||||
|
|
||||||
|
See also the Vec and Frame classes' documentations.
|
||||||
|
\nosubgrouping */
|
||||||
|
class QGLVIEWER_EXPORT Quaternion
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/*! @name Defining a Quaternion */
|
||||||
|
//@{
|
||||||
|
/*! Default constructor, builds an identity rotation. */
|
||||||
|
Quaternion()
|
||||||
|
{ q[0]=q[1]=q[2]=0.0; q[3]=1.0; }
|
||||||
|
|
||||||
|
/*! Constructor from rotation axis (non null) and angle (in radians). See also setAxisAngle(). */
|
||||||
|
Quaternion(const Vec& axis, qreal angle)
|
||||||
|
{
|
||||||
|
setAxisAngle(axis, angle);
|
||||||
|
}
|
||||||
|
|
||||||
|
Quaternion(const Vec& from, const Vec& to);
|
||||||
|
|
||||||
|
/*! Constructor from the four values of a Quaternion. First three values are axis*sin(angle/2) and
|
||||||
|
last one is cos(angle/2).
|
||||||
|
|
||||||
|
\attention The identity Quaternion is Quaternion(0,0,0,1) and \e not Quaternion(0,0,0,0) (which is
|
||||||
|
not unitary). The default Quaternion() creates such identity Quaternion. */
|
||||||
|
Quaternion(qreal q0, qreal q1, qreal q2, qreal q3)
|
||||||
|
{ q[0]=q0; q[1]=q1; q[2]=q2; q[3]=q3; }
|
||||||
|
|
||||||
|
/*! Copy constructor. */
|
||||||
|
Quaternion(const Quaternion& Q)
|
||||||
|
{ for (int i=0; i<4; ++i) q[i] = Q.q[i]; }
|
||||||
|
|
||||||
|
/*! Equal operator. */
|
||||||
|
Quaternion& operator=(const Quaternion& Q)
|
||||||
|
{
|
||||||
|
for (int i=0; i<4; ++i)
|
||||||
|
q[i] = Q.q[i];
|
||||||
|
return (*this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Sets the Quaternion as a rotation of axis \p axis and angle \p angle (in radians).
|
||||||
|
|
||||||
|
\p axis does not need to be normalized. A null \p axis will result in an identity Quaternion. */
|
||||||
|
void setAxisAngle(const Vec& axis, qreal angle)
|
||||||
|
{
|
||||||
|
const qreal norm = axis.norm();
|
||||||
|
if (norm < 1E-8)
|
||||||
|
{
|
||||||
|
// Null rotation
|
||||||
|
q[0] = 0.0; q[1] = 0.0; q[2] = 0.0; q[3] = 1.0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
const qreal sin_half_angle = sin(angle / 2.0);
|
||||||
|
q[0] = sin_half_angle*axis[0]/norm;
|
||||||
|
q[1] = sin_half_angle*axis[1]/norm;
|
||||||
|
q[2] = sin_half_angle*axis[2]/norm;
|
||||||
|
q[3] = cos(angle / 2.0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Sets the Quaternion value. See the Quaternion(qreal, qreal, qreal, qreal) constructor documentation. */
|
||||||
|
void setValue(qreal q0, qreal q1, qreal q2, qreal q3)
|
||||||
|
{ q[0]=q0; q[1]=q1; q[2]=q2; q[3]=q3; }
|
||||||
|
|
||||||
|
#ifndef DOXYGEN
|
||||||
|
void setFromRotationMatrix(const float m[3][3]);
|
||||||
|
void setFromRotatedBase(const Vec& X, const Vec& Y, const Vec& Z);
|
||||||
|
#endif
|
||||||
|
void setFromRotationMatrix(const qreal m[3][3]);
|
||||||
|
void setFromRotatedBasis(const Vec& X, const Vec& Y, const Vec& Z);
|
||||||
|
//@}
|
||||||
|
|
||||||
|
|
||||||
|
/*! @name Accessing values */
|
||||||
|
//@{
|
||||||
|
Vec axis() const;
|
||||||
|
qreal angle() const;
|
||||||
|
void getAxisAngle(Vec& axis, qreal& angle) const;
|
||||||
|
|
||||||
|
/*! Bracket operator, with a constant return value. \p i must range in [0..3]. See the Quaternion(qreal, qreal, qreal, qreal) documentation. */
|
||||||
|
qreal operator[](int i) const { return q[i]; }
|
||||||
|
|
||||||
|
/*! Bracket operator returning an l-value. \p i must range in [0..3]. See the Quaternion(qreal, qreal, qreal, qreal) documentation. */
|
||||||
|
qreal& operator[](int i) { return q[i]; }
|
||||||
|
//@}
|
||||||
|
|
||||||
|
|
||||||
|
/*! @name Rotation computations */
|
||||||
|
//@{
|
||||||
|
/*! Returns the composition of the \p a and \p b rotations.
|
||||||
|
|
||||||
|
The order is important. When applied to a Vec \c v (see operator*(const Quaternion&, const Vec&)
|
||||||
|
and rotate()) the resulting Quaternion acts as if \p b was applied first and then \p a was
|
||||||
|
applied. This is obvious since the image \c v' of \p v by the composited rotation satisfies: \code
|
||||||
|
v'= (a*b) * v = a * (b*v) \endcode
|
||||||
|
|
||||||
|
Note that a*b usually differs from b*a.
|
||||||
|
|
||||||
|
\attention For efficiency reasons, the resulting Quaternion is not normalized. Use normalize() in
|
||||||
|
case of numerical drift with small rotation composition. */
|
||||||
|
friend Quaternion operator*(const Quaternion& a, const Quaternion& b)
|
||||||
|
{
|
||||||
|
return Quaternion(a.q[3]*b.q[0] + b.q[3]*a.q[0] + a.q[1]*b.q[2] - a.q[2]*b.q[1],
|
||||||
|
a.q[3]*b.q[1] + b.q[3]*a.q[1] + a.q[2]*b.q[0] - a.q[0]*b.q[2],
|
||||||
|
a.q[3]*b.q[2] + b.q[3]*a.q[2] + a.q[0]*b.q[1] - a.q[1]*b.q[0],
|
||||||
|
a.q[3]*b.q[3] - b.q[0]*a.q[0] - a.q[1]*b.q[1] - a.q[2]*b.q[2]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Quaternion rotation is composed with \p q.
|
||||||
|
|
||||||
|
See operator*(), since this is equivalent to \c this = \c this * \p q.
|
||||||
|
|
||||||
|
\note For efficiency reasons, the resulting Quaternion is not normalized.
|
||||||
|
You may normalize() it after each application in case of numerical drift. */
|
||||||
|
Quaternion& operator*=(const Quaternion &q)
|
||||||
|
{
|
||||||
|
*this = (*this)*q;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Returns the image of \p v by the rotation \p q.
|
||||||
|
|
||||||
|
Same as q.rotate(v). See rotate() and inverseRotate(). */
|
||||||
|
friend Vec operator*(const Quaternion& q, const Vec& v)
|
||||||
|
{
|
||||||
|
return q.rotate(v);
|
||||||
|
}
|
||||||
|
|
||||||
|
Vec rotate(const Vec& v) const;
|
||||||
|
Vec inverseRotate(const Vec& v) const;
|
||||||
|
//@}
|
||||||
|
|
||||||
|
|
||||||
|
/*! @name Inversion */
|
||||||
|
//@{
|
||||||
|
/*! Returns the inverse Quaternion (inverse rotation).
|
||||||
|
|
||||||
|
Result has a negated axis() direction and the same angle(). A composition (see operator*()) of a
|
||||||
|
Quaternion and its inverse() results in an identity function.
|
||||||
|
|
||||||
|
Use invert() to actually modify the Quaternion. */
|
||||||
|
Quaternion inverse() const { return Quaternion(-q[0], -q[1], -q[2], q[3]); }
|
||||||
|
|
||||||
|
/*! Inverses the Quaternion (same rotation angle(), but negated axis()).
|
||||||
|
|
||||||
|
See also inverse(). */
|
||||||
|
void invert() { q[0] = -q[0]; q[1] = -q[1]; q[2] = -q[2]; }
|
||||||
|
|
||||||
|
/*! Negates all the coefficients of the Quaternion.
|
||||||
|
|
||||||
|
This results in an other representation of the \e same rotation (opposite rotation angle, but with
|
||||||
|
a negated axis direction: the two cancel out). However, note that the results of axis() and
|
||||||
|
angle() are unchanged after a call to this method since angle() always returns a value in [0,pi].
|
||||||
|
|
||||||
|
This method is mainly useful for Quaternion interpolation, so that the spherical
|
||||||
|
interpolation takes the shortest path on the unit sphere. See slerp() for details. */
|
||||||
|
void negate() { invert(); q[3] = -q[3]; }
|
||||||
|
|
||||||
|
/*! Normalizes the Quaternion coefficients.
|
||||||
|
|
||||||
|
This method should not need to be called since we only deal with unit Quaternions. This is however
|
||||||
|
useful to prevent numerical drifts, especially with small rotational increments. See also
|
||||||
|
normalized(). */
|
||||||
|
qreal normalize()
|
||||||
|
{
|
||||||
|
const qreal norm = sqrt(q[0]*q[0] + q[1]*q[1] + q[2]*q[2] + q[3]*q[3]);
|
||||||
|
for (int i=0; i<4; ++i)
|
||||||
|
q[i] /= norm;
|
||||||
|
return norm;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Returns a normalized version of the Quaternion.
|
||||||
|
|
||||||
|
See also normalize(). */
|
||||||
|
Quaternion normalized() const
|
||||||
|
{
|
||||||
|
qreal Q[4];
|
||||||
|
const qreal norm = sqrt(q[0]*q[0] + q[1]*q[1] + q[2]*q[2] + q[3]*q[3]);
|
||||||
|
for (int i=0; i<4; ++i)
|
||||||
|
Q[i] = q[i] / norm;
|
||||||
|
return Quaternion(Q[0], Q[1], Q[2], Q[3]);
|
||||||
|
}
|
||||||
|
//@}
|
||||||
|
|
||||||
|
|
||||||
|
/*! @name Associated matrix */
|
||||||
|
//@{
|
||||||
|
const GLdouble* matrix() const;
|
||||||
|
void getMatrix(GLdouble m[4][4]) const;
|
||||||
|
void getMatrix(GLdouble m[16]) const;
|
||||||
|
|
||||||
|
void getRotationMatrix(qreal m[3][3]) const;
|
||||||
|
|
||||||
|
const GLdouble* inverseMatrix() const;
|
||||||
|
void getInverseMatrix(GLdouble m[4][4]) const;
|
||||||
|
void getInverseMatrix(GLdouble m[16]) const;
|
||||||
|
|
||||||
|
void getInverseRotationMatrix(qreal m[3][3]) const;
|
||||||
|
//@}
|
||||||
|
|
||||||
|
|
||||||
|
/*! @name Slerp interpolation */
|
||||||
|
//@{
|
||||||
|
static Quaternion slerp(const Quaternion& a, const Quaternion& b, qreal t, bool allowFlip=true);
|
||||||
|
static Quaternion squad(const Quaternion& a, const Quaternion& tgA, const Quaternion& tgB, const Quaternion& b, qreal t);
|
||||||
|
/*! Returns the "dot" product of \p a and \p b: a[0]*b[0] + a[1]*b[1] + a[2]*b[2] + a[3]*b[3]. */
|
||||||
|
static qreal dot(const Quaternion& a, const Quaternion& b) { return a[0]*b[0] + a[1]*b[1] + a[2]*b[2] + a[3]*b[3]; }
|
||||||
|
|
||||||
|
Quaternion log();
|
||||||
|
Quaternion exp();
|
||||||
|
static Quaternion lnDif(const Quaternion& a, const Quaternion& b);
|
||||||
|
static Quaternion squadTangent(const Quaternion& before, const Quaternion& center, const Quaternion& after);
|
||||||
|
//@}
|
||||||
|
|
||||||
|
/*! @name Random Quaternion */
|
||||||
|
//@{
|
||||||
|
static Quaternion randomQuaternion();
|
||||||
|
//@}
|
||||||
|
|
||||||
|
/*! @name XML representation */
|
||||||
|
//@{
|
||||||
|
explicit Quaternion(const QDomElement& element);
|
||||||
|
QDomElement domElement(const QString& name, QDomDocument& document) const;
|
||||||
|
void initFromDOMElement(const QDomElement& element);
|
||||||
|
//@}
|
||||||
|
|
||||||
|
#ifdef DOXYGEN
|
||||||
|
/*! @name Output stream */
|
||||||
|
//@{
|
||||||
|
/*! Output stream operator. Enables debugging code like:
|
||||||
|
\code
|
||||||
|
Quaternion rot(...);
|
||||||
|
cout << "Rotation=" << rot << endl;
|
||||||
|
\endcode */
|
||||||
|
std::ostream& operator<<(std::ostream& o, const qglviewer::Vec&);
|
||||||
|
//@}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
private:
|
||||||
|
/*! The internal data representation is private, use operator[] to access values. */
|
||||||
|
qreal q[4];
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
std::ostream& operator<<(std::ostream& o, const qglviewer::Quaternion&);
|
||||||
|
|
||||||
|
#endif // QGLVIEWER_QUATERNION_H
|
||||||
494
QGLViewer/saveSnapshot.cpp
Normal file
494
QGLViewer/saveSnapshot.cpp
Normal file
@ -0,0 +1,494 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
|
||||||
|
Copyright (C) 2002-2014 Gilles Debunne. All rights reserved.
|
||||||
|
|
||||||
|
This file is part of the QGLViewer library version 2.6.3.
|
||||||
|
|
||||||
|
http://www.libqglviewer.com - contact@libqglviewer.com
|
||||||
|
|
||||||
|
This file may be used under the terms of the GNU General Public License
|
||||||
|
versions 2.0 or 3.0 as published by the Free Software Foundation and
|
||||||
|
appearing in the LICENSE file included in the packaging of this file.
|
||||||
|
In addition, as a special exception, Gilles Debunne gives you certain
|
||||||
|
additional rights, described in the file GPL_EXCEPTION in this package.
|
||||||
|
|
||||||
|
libQGLViewer uses dual licensing. Commercial/proprietary software must
|
||||||
|
purchase a libQGLViewer Commercial License.
|
||||||
|
|
||||||
|
This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
|
||||||
|
WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
|
||||||
|
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
#include "qglviewer.h"
|
||||||
|
|
||||||
|
#include "ui_ImageInterface.h"
|
||||||
|
|
||||||
|
// Output format list
|
||||||
|
# include <QImageWriter>
|
||||||
|
|
||||||
|
#include <qfileinfo.h>
|
||||||
|
#include <qfiledialog.h>
|
||||||
|
#include <qmessagebox.h>
|
||||||
|
#include <qapplication.h>
|
||||||
|
#include <qmap.h>
|
||||||
|
#include <qinputdialog.h>
|
||||||
|
#include <qprogressdialog.h>
|
||||||
|
#include <qcursor.h>
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
////// Static global variables - local to this file //////
|
||||||
|
// List of available output file formats, formatted for QFileDialog.
|
||||||
|
static QString formats;
|
||||||
|
// Converts QFileDialog resulting format to Qt snapshotFormat.
|
||||||
|
static QMap<QString, QString> Qtformat;
|
||||||
|
// Converts Qt snapshotFormat to QFileDialog menu string.
|
||||||
|
static QMap<QString, QString> FDFormatString;
|
||||||
|
// Converts snapshotFormat to file extension
|
||||||
|
static QMap<QString, QString> extension;
|
||||||
|
|
||||||
|
|
||||||
|
/*! Sets snapshotFileName(). */
|
||||||
|
void QGLViewer::setSnapshotFileName(const QString& name)
|
||||||
|
{
|
||||||
|
snapshotFileName_ = QFileInfo(name).absoluteFilePath();
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifndef DOXYGEN
|
||||||
|
const QString& QGLViewer::snapshotFilename() const
|
||||||
|
{
|
||||||
|
qWarning("snapshotFilename is deprecated. Use snapshotFileName() (uppercase N) instead.");
|
||||||
|
return snapshotFileName();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
/*! Opens a dialog that displays the different available snapshot formats.
|
||||||
|
|
||||||
|
Then calls setSnapshotFormat() with the selected one (unless the user cancels).
|
||||||
|
|
||||||
|
Returns \c false if the user presses the Cancel button and \c true otherwise. */
|
||||||
|
bool QGLViewer::openSnapshotFormatDialog()
|
||||||
|
{
|
||||||
|
bool ok = false;
|
||||||
|
QStringList list = formats.split(";;", QString::SkipEmptyParts);
|
||||||
|
int current = list.indexOf(FDFormatString[snapshotFormat()]);
|
||||||
|
QString format = QInputDialog::getItem(this, "Snapshot format", "Select a snapshot format", list, current, false, &ok);
|
||||||
|
if (ok)
|
||||||
|
setSnapshotFormat(Qtformat[format]);
|
||||||
|
return ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Finds all available Qt output formats, so that they can be available in
|
||||||
|
// saveSnapshot dialog. Initialize snapshotFormat() to the first one.
|
||||||
|
void QGLViewer::initializeSnapshotFormats()
|
||||||
|
{
|
||||||
|
QList<QByteArray> list = QImageWriter::supportedImageFormats();
|
||||||
|
QStringList formatList;
|
||||||
|
for (int i=0; i < list.size(); ++i)
|
||||||
|
formatList << QString(list.at(i).toUpper());
|
||||||
|
// qWarning("Available image formats: ");
|
||||||
|
// QStringList::Iterator it = formatList.begin();
|
||||||
|
// while( it != formatList.end() )
|
||||||
|
// qWarning((*it++).); QT4 change this. qWarning no longer accepts QString
|
||||||
|
|
||||||
|
// Check that the interesting formats are available and add them in "formats"
|
||||||
|
// Unused formats: XPM XBM PBM PGM
|
||||||
|
QStringList QtText, MenuText, Ext;
|
||||||
|
QtText += "JPEG"; MenuText += "JPEG (*.jpg)"; Ext += "jpg";
|
||||||
|
QtText += "PNG"; MenuText += "PNG (*.png)"; Ext += "png";
|
||||||
|
QtText += "EPS"; MenuText += "Encapsulated Postscript (*.eps)"; Ext += "eps";
|
||||||
|
QtText += "PS"; MenuText += "Postscript (*.ps)"; Ext += "ps";
|
||||||
|
QtText += "PPM"; MenuText += "24bit RGB Bitmap (*.ppm)"; Ext += "ppm";
|
||||||
|
QtText += "BMP"; MenuText += "Windows Bitmap (*.bmp)"; Ext += "bmp";
|
||||||
|
QtText += "XFIG"; MenuText += "XFig (*.fig)"; Ext += "fig";
|
||||||
|
|
||||||
|
QStringList::iterator itText = QtText.begin();
|
||||||
|
QStringList::iterator itMenu = MenuText.begin();
|
||||||
|
QStringList::iterator itExt = Ext.begin();
|
||||||
|
|
||||||
|
while (itText != QtText.end())
|
||||||
|
{
|
||||||
|
//QMessageBox::information(this, "Snapshot ", "Trying format\n"+(*itText));
|
||||||
|
if (formatList.contains((*itText)))
|
||||||
|
{
|
||||||
|
//QMessageBox::information(this, "Snapshot ", "Recognized format\n"+(*itText));
|
||||||
|
if (formats.isEmpty())
|
||||||
|
setSnapshotFormat(*itText);
|
||||||
|
else
|
||||||
|
formats += ";;";
|
||||||
|
formats += (*itMenu);
|
||||||
|
Qtformat[(*itMenu)] = (*itText);
|
||||||
|
FDFormatString[(*itText)] = (*itMenu);
|
||||||
|
extension[(*itText)] = (*itExt);
|
||||||
|
}
|
||||||
|
// Synchronize parsing
|
||||||
|
itText++;
|
||||||
|
itMenu++;
|
||||||
|
itExt++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns false if the user refused to use the fileName
|
||||||
|
static bool checkFileName(QString& fileName, QWidget* widget, const QString& snapshotFormat)
|
||||||
|
{
|
||||||
|
if (fileName.isEmpty())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Check that extension has been provided
|
||||||
|
QFileInfo info(fileName);
|
||||||
|
|
||||||
|
if (info.suffix().isEmpty())
|
||||||
|
{
|
||||||
|
// No extension given. Silently add one
|
||||||
|
if (fileName.right(1) != ".")
|
||||||
|
fileName += ".";
|
||||||
|
fileName += extension[snapshotFormat];
|
||||||
|
info.setFile(fileName);
|
||||||
|
}
|
||||||
|
else if (info.suffix() != extension[snapshotFormat])
|
||||||
|
{
|
||||||
|
// Extension is not appropriate. Propose a modification
|
||||||
|
QString modifiedName = info.absolutePath() + '/' + info.baseName() + "." + extension[snapshotFormat];
|
||||||
|
QFileInfo modifInfo(modifiedName);
|
||||||
|
int i=(QMessageBox::warning(widget,"Wrong extension",
|
||||||
|
info.fileName()+" has a wrong extension.\nSave as "+modifInfo.fileName()+" instead ?",
|
||||||
|
QMessageBox::Yes,
|
||||||
|
QMessageBox::No,
|
||||||
|
QMessageBox::Cancel));
|
||||||
|
if (i==QMessageBox::Cancel)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (i==QMessageBox::Yes)
|
||||||
|
{
|
||||||
|
fileName = modifiedName;
|
||||||
|
info.setFile(fileName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
class ImageInterface: public QDialog, public Ui::ImageInterface
|
||||||
|
{
|
||||||
|
public: ImageInterface(QWidget *parent) : QDialog(parent) { setupUi(this); }
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// Pops-up an image settings dialog box and save to fileName.
|
||||||
|
// Returns false in case of problem.
|
||||||
|
bool QGLViewer::saveImageSnapshot(const QString& fileName)
|
||||||
|
{
|
||||||
|
static ImageInterface* imageInterface = NULL;
|
||||||
|
|
||||||
|
if (!imageInterface)
|
||||||
|
imageInterface = new ImageInterface(this);
|
||||||
|
|
||||||
|
imageInterface->imgWidth->setValue(width());
|
||||||
|
imageInterface->imgHeight->setValue(height());
|
||||||
|
|
||||||
|
imageInterface->imgQuality->setValue(snapshotQuality());
|
||||||
|
|
||||||
|
if (imageInterface->exec() == QDialog::Rejected)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
// Hide closed dialog
|
||||||
|
qApp->processEvents();
|
||||||
|
|
||||||
|
setSnapshotQuality(imageInterface->imgQuality->value());
|
||||||
|
|
||||||
|
QColor previousBGColor = backgroundColor();
|
||||||
|
if (imageInterface->whiteBackground->isChecked())
|
||||||
|
setBackgroundColor(Qt::white);
|
||||||
|
|
||||||
|
QSize finalSize(imageInterface->imgWidth->value(), imageInterface->imgHeight->value());
|
||||||
|
|
||||||
|
qreal oversampling = imageInterface->oversampling->value();
|
||||||
|
QSize subSize(int(this->width()/oversampling), int(this->height()/oversampling));
|
||||||
|
|
||||||
|
qreal aspectRatio = width() / static_cast<qreal>(height());
|
||||||
|
qreal newAspectRatio = finalSize.width() / static_cast<qreal>(finalSize.height());
|
||||||
|
|
||||||
|
qreal zNear = camera()->zNear();
|
||||||
|
//qreal zFar = camera()->zFar();
|
||||||
|
|
||||||
|
qreal xMin, yMin;
|
||||||
|
bool expand = imageInterface->expandFrustum->isChecked();
|
||||||
|
if (camera()->type() == qglviewer::Camera::PERSPECTIVE)
|
||||||
|
if ((expand && (newAspectRatio>aspectRatio)) || (!expand && (newAspectRatio<aspectRatio)))
|
||||||
|
{
|
||||||
|
yMin = zNear * tan(camera()->fieldOfView() / 2.0);
|
||||||
|
xMin = newAspectRatio * yMin;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
xMin = zNear * tan(camera()->fieldOfView() / 2.0) * aspectRatio;
|
||||||
|
yMin = xMin / newAspectRatio;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
camera()->getOrthoWidthHeight(xMin, yMin);
|
||||||
|
if ((expand && (newAspectRatio>aspectRatio)) || (!expand && (newAspectRatio<aspectRatio)))
|
||||||
|
xMin = newAspectRatio * yMin;
|
||||||
|
else
|
||||||
|
yMin = xMin / newAspectRatio;
|
||||||
|
}
|
||||||
|
|
||||||
|
QImage image(finalSize.width(), finalSize.height(), QImage::Format_ARGB32);
|
||||||
|
|
||||||
|
if (image.isNull())
|
||||||
|
{
|
||||||
|
QMessageBox::warning(this, "Image saving error",
|
||||||
|
"Unable to create resulting image",
|
||||||
|
QMessageBox::Ok, QMessageBox::NoButton);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ProgressDialog disabled since it interfers with the screen grabing mecanism on some platforms. Too bad.
|
||||||
|
// ProgressDialog::showProgressDialog(this);
|
||||||
|
|
||||||
|
qreal scaleX = subSize.width() / static_cast<qreal>(finalSize.width());
|
||||||
|
qreal scaleY = subSize.height() / static_cast<qreal>(finalSize.height());
|
||||||
|
|
||||||
|
//qreal deltaX = 2.0 * xMin * scaleX;
|
||||||
|
//qreal deltaY = 2.0 * yMin * scaleY;
|
||||||
|
|
||||||
|
int nbX = finalSize.width() / subSize.width();
|
||||||
|
int nbY = finalSize.height() / subSize.height();
|
||||||
|
|
||||||
|
// Extra subimage on the right/bottom border(s) if needed
|
||||||
|
if (nbX * subSize.width() < finalSize.width())
|
||||||
|
nbX++;
|
||||||
|
if (nbY * subSize.height() < finalSize.height())
|
||||||
|
nbY++;
|
||||||
|
|
||||||
|
makeCurrent();
|
||||||
|
|
||||||
|
// tileRegion_ is used by startScreenCoordinatesSystem to appropriately set the local
|
||||||
|
// coordinate system when tiling
|
||||||
|
tileRegion_ = new TileRegion();
|
||||||
|
qreal tileXMin, tileWidth, tileYMin, tileHeight;
|
||||||
|
if ((expand && (newAspectRatio>aspectRatio)) || (!expand && (newAspectRatio<aspectRatio)))
|
||||||
|
{
|
||||||
|
qreal tileTotalWidth = newAspectRatio * height();
|
||||||
|
tileXMin = (width() - tileTotalWidth) / 2.0;
|
||||||
|
tileWidth = tileTotalWidth * scaleX;
|
||||||
|
tileYMin = 0.0;
|
||||||
|
tileHeight = height() * scaleY;
|
||||||
|
tileRegion_->textScale = 1.0 / scaleY;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
qreal tileTotalHeight = width() / newAspectRatio;
|
||||||
|
tileYMin = (height() - tileTotalHeight) / 2.0;
|
||||||
|
tileHeight = tileTotalHeight * scaleY;
|
||||||
|
tileXMin = 0.0;
|
||||||
|
tileWidth = width() * scaleX;
|
||||||
|
tileRegion_->textScale = 1.0 / scaleX;
|
||||||
|
}
|
||||||
|
|
||||||
|
int count=0;
|
||||||
|
for (int i=0; i<nbX; i++)
|
||||||
|
for (int j=0; j<nbY; j++)
|
||||||
|
{
|
||||||
|
preDraw();
|
||||||
|
|
||||||
|
tileRegion_->xMin = tileXMin + i * tileWidth;
|
||||||
|
tileRegion_->xMax = tileXMin + (i+1) * tileWidth;
|
||||||
|
tileRegion_->yMin = tileYMin + j * tileHeight;
|
||||||
|
tileRegion_->yMax = tileYMin + (j+1) * tileHeight;
|
||||||
|
|
||||||
|
draw();
|
||||||
|
postDraw();
|
||||||
|
|
||||||
|
// ProgressDialog::hideProgressDialog();
|
||||||
|
// qApp->processEvents();
|
||||||
|
|
||||||
|
QImage snapshot = grabFramebuffer();
|
||||||
|
|
||||||
|
// ProgressDialog::showProgressDialog(this);
|
||||||
|
// ProgressDialog::updateProgress(count / (qreal)(nbX*nbY),
|
||||||
|
// "Generating image ["+QString::number(count)+"/"+QString::number(nbX*nbY)+"]");
|
||||||
|
// qApp->processEvents();
|
||||||
|
|
||||||
|
QImage subImage = snapshot.scaled(subSize, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
|
||||||
|
|
||||||
|
// Copy subImage in image
|
||||||
|
for (int ii=0; ii<subSize.width(); ii++)
|
||||||
|
{
|
||||||
|
int fi = i*subSize.width() + ii;
|
||||||
|
if (fi == image.width())
|
||||||
|
break;
|
||||||
|
for (int jj=0; jj<subSize.height(); jj++)
|
||||||
|
{
|
||||||
|
int fj = j*subSize.height() + jj;
|
||||||
|
if (fj == image.height())
|
||||||
|
break;
|
||||||
|
image.setPixel(fi, fj, subImage.pixel(ii,jj));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool saveOK = image.save(fileName, snapshotFormat().toLatin1().constData(), snapshotQuality());
|
||||||
|
|
||||||
|
// ProgressDialog::hideProgressDialog();
|
||||||
|
// setCursor(QCursor(Qt::ArrowCursor));
|
||||||
|
|
||||||
|
delete tileRegion_;
|
||||||
|
tileRegion_ = NULL;
|
||||||
|
|
||||||
|
if (imageInterface->whiteBackground->isChecked())
|
||||||
|
setBackgroundColor(previousBGColor);
|
||||||
|
|
||||||
|
return saveOK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*! Saves a snapshot of the current image displayed by the widget.
|
||||||
|
|
||||||
|
Options are set using snapshotFormat(), snapshotFileName() and snapshotQuality(). For non vectorial
|
||||||
|
image formats, the image size is equal to the current viewer's dimensions (see width() and
|
||||||
|
height()). See snapshotFormat() for details on supported formats.
|
||||||
|
|
||||||
|
If \p automatic is \c false (or if snapshotFileName() is empty), a file dialog is opened to ask for
|
||||||
|
the file name.
|
||||||
|
|
||||||
|
When \p automatic is \c true, the file name is set to \c NAME-NUMBER, where \c NAME is
|
||||||
|
snapshotFileName() and \c NUMBER is snapshotCounter(). The snapshotCounter() is automatically
|
||||||
|
incremented after each snapshot saving. This is useful to create videos from your application:
|
||||||
|
\code
|
||||||
|
void Viewer::init()
|
||||||
|
{
|
||||||
|
resize(720, 576); // PAL DV format (use 720x480 for NTSC DV)
|
||||||
|
connect(this, SIGNAL(drawFinished(bool)), SLOT(saveSnapshot(bool)));
|
||||||
|
}
|
||||||
|
\endcode
|
||||||
|
Then call draw() in a loop (for instance using animate() and/or a camera() KeyFrameInterpolator
|
||||||
|
replay) to create your image sequence.
|
||||||
|
|
||||||
|
If you want to create a Quicktime VR panoramic sequence, simply use code like this:
|
||||||
|
\code
|
||||||
|
void Viewer::createQuicktime()
|
||||||
|
{
|
||||||
|
const int nbImages = 36;
|
||||||
|
for (int i=0; i<nbImages; ++i)
|
||||||
|
{
|
||||||
|
camera()->setOrientation(2.0*M_PI/nbImages, 0.0); // Theta-Phi orientation
|
||||||
|
showEntireScene();
|
||||||
|
update(); // calls draw(), which emits drawFinished(), which calls saveSnapshot()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
\endcode
|
||||||
|
|
||||||
|
If snapshotCounter() is negative, no number is appended to snapshotFileName() and the
|
||||||
|
snapshotCounter() is not incremented. This is useful to force the creation of a file, overwriting
|
||||||
|
the previous one.
|
||||||
|
|
||||||
|
When \p overwrite is set to \c false (default), a window asks for confirmation if the file already
|
||||||
|
exists. In \p automatic mode, the snapshotCounter() is incremented (if positive) until a
|
||||||
|
non-existing file name is found instead. Otherwise the file is overwritten without confirmation.
|
||||||
|
|
||||||
|
The VRender library was written by Cyril Soler (Cyril dot Soler at imag dot fr). If the generated
|
||||||
|
PS or EPS file is not properly displayed, remove the anti-aliasing option in your postscript viewer.
|
||||||
|
|
||||||
|
\note In order to correctly grab the frame buffer, the QGLViewer window is raised in front of
|
||||||
|
other windows by this method. */
|
||||||
|
void QGLViewer::saveSnapshot(bool automatic, bool overwrite)
|
||||||
|
{
|
||||||
|
// Ask for file name
|
||||||
|
if (snapshotFileName().isEmpty() || !automatic)
|
||||||
|
{
|
||||||
|
QString fileName;
|
||||||
|
QString selectedFormat = FDFormatString[snapshotFormat()];
|
||||||
|
fileName = QFileDialog::getSaveFileName(this, "Choose a file name to save under", snapshotFileName(), formats, &selectedFormat,
|
||||||
|
overwrite?QFileDialog::DontConfirmOverwrite:QFlags<QFileDialog::Option>(0));
|
||||||
|
setSnapshotFormat(Qtformat[selectedFormat]);
|
||||||
|
|
||||||
|
if (checkFileName(fileName, this, snapshotFormat()))
|
||||||
|
setSnapshotFileName(fileName);
|
||||||
|
else
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QFileInfo fileInfo(snapshotFileName());
|
||||||
|
|
||||||
|
if ((automatic) && (snapshotCounter() >= 0))
|
||||||
|
{
|
||||||
|
// In automatic mode, names have a number appended
|
||||||
|
const QString baseName = fileInfo.baseName();
|
||||||
|
QString count;
|
||||||
|
count.sprintf("%.04d", snapshotCounter_++);
|
||||||
|
QString suffix;
|
||||||
|
suffix = fileInfo.suffix();
|
||||||
|
if (suffix.isEmpty())
|
||||||
|
suffix = extension[snapshotFormat()];
|
||||||
|
fileInfo.setFile(fileInfo.absolutePath()+ '/' + baseName + '-' + count + '.' + suffix);
|
||||||
|
|
||||||
|
if (!overwrite)
|
||||||
|
while (fileInfo.exists())
|
||||||
|
{
|
||||||
|
count.sprintf("%.04d", snapshotCounter_++);
|
||||||
|
fileInfo.setFile(fileInfo.absolutePath() + '/' +baseName + '-' + count + '.' + fileInfo.suffix());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool saveOK;
|
||||||
|
if (automatic)
|
||||||
|
{
|
||||||
|
QImage snapshot = frameBufferSnapshot();
|
||||||
|
saveOK = snapshot.save(fileInfo.filePath(), snapshotFormat().toLatin1().constData(), snapshotQuality());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
saveOK = saveImageSnapshot(fileInfo.filePath());
|
||||||
|
|
||||||
|
if (!saveOK)
|
||||||
|
QMessageBox::warning(this, "Snapshot problem", "Unable to save snapshot in\n"+fileInfo.filePath());
|
||||||
|
}
|
||||||
|
|
||||||
|
QImage QGLViewer::frameBufferSnapshot()
|
||||||
|
{
|
||||||
|
// Viewer must be on top of other windows.
|
||||||
|
makeCurrent();
|
||||||
|
raise();
|
||||||
|
// Hack: Qt has problems if the frame buffer is grabbed after QFileDialog is displayed.
|
||||||
|
// We grab the frame buffer before, even if it might be not necessary (vectorial rendering).
|
||||||
|
// The problem could not be reproduced on a simple example to submit a Qt bug.
|
||||||
|
// However, only grabs the backgroundImage in the eponym example. May come from the driver.
|
||||||
|
return grabFramebuffer();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Same as saveSnapshot(), except that it uses \p fileName instead of snapshotFileName().
|
||||||
|
|
||||||
|
If \p fileName is empty, opens a file dialog to select the name.
|
||||||
|
|
||||||
|
Snapshot settings are set from snapshotFormat() and snapshotQuality().
|
||||||
|
|
||||||
|
Asks for confirmation when \p fileName already exists and \p overwrite is \c false (default).
|
||||||
|
|
||||||
|
\attention If \p fileName is a char* (as is "myFile.jpg"), it may be casted into a \c bool, and the
|
||||||
|
other saveSnapshot() method may be used instead. Pass QString("myFile.jpg") as a parameter to
|
||||||
|
prevent this. */
|
||||||
|
void QGLViewer::saveSnapshot(const QString& fileName, bool overwrite)
|
||||||
|
{
|
||||||
|
const QString previousName = snapshotFileName();
|
||||||
|
const int previousCounter = snapshotCounter();
|
||||||
|
setSnapshotFileName(fileName);
|
||||||
|
setSnapshotCounter(-1);
|
||||||
|
saveSnapshot(true, overwrite);
|
||||||
|
setSnapshotFileName(previousName);
|
||||||
|
setSnapshotCounter(previousCounter);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Takes a snapshot of the current display and pastes it to the clipboard.
|
||||||
|
|
||||||
|
This action is activated by the KeyboardAction::SNAPSHOT_TO_CLIPBOARD enum, binded to \c Ctrl+C by default.
|
||||||
|
*/
|
||||||
|
void QGLViewer::snapshotToClipboard()
|
||||||
|
{
|
||||||
|
QClipboard *cb = QApplication::clipboard();
|
||||||
|
cb->setImage(frameBufferSnapshot());
|
||||||
|
}
|
||||||
|
|
||||||
164
QGLViewer/vec.cpp
Normal file
164
QGLViewer/vec.cpp
Normal file
@ -0,0 +1,164 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
|
||||||
|
Copyright (C) 2002-2014 Gilles Debunne. All rights reserved.
|
||||||
|
|
||||||
|
This file is part of the QGLViewer library version 2.6.3.
|
||||||
|
|
||||||
|
http://www.libqglviewer.com - contact@libqglviewer.com
|
||||||
|
|
||||||
|
This file may be used under the terms of the GNU General Public License
|
||||||
|
versions 2.0 or 3.0 as published by the Free Software Foundation and
|
||||||
|
appearing in the LICENSE file included in the packaging of this file.
|
||||||
|
In addition, as a special exception, Gilles Debunne gives you certain
|
||||||
|
additional rights, described in the file GPL_EXCEPTION in this package.
|
||||||
|
|
||||||
|
libQGLViewer uses dual licensing. Commercial/proprietary software must
|
||||||
|
purchase a libQGLViewer Commercial License.
|
||||||
|
|
||||||
|
This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
|
||||||
|
WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
|
||||||
|
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
#include "domUtils.h"
|
||||||
|
#include "vec.h"
|
||||||
|
|
||||||
|
// Most of the methods are declared inline in vec.h
|
||||||
|
|
||||||
|
using namespace qglviewer;
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
/*! Projects the Vec on the axis of direction \p direction that passes through the origin.
|
||||||
|
|
||||||
|
\p direction does not need to be normalized (but must be non null). */
|
||||||
|
void Vec::projectOnAxis(const Vec& direction)
|
||||||
|
{
|
||||||
|
#ifndef QT_NO_DEBUG
|
||||||
|
if (direction.squaredNorm() < 1.0E-10)
|
||||||
|
qWarning("Vec::projectOnAxis: axis direction is not normalized (norm=%f).", direction.norm());
|
||||||
|
#endif
|
||||||
|
|
||||||
|
*this = (((*this)*direction) / direction.squaredNorm()) * direction;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Projects the Vec on the plane whose normal is \p normal that passes through the origin.
|
||||||
|
|
||||||
|
\p normal does not need to be normalized (but must be non null). */
|
||||||
|
void Vec::projectOnPlane(const Vec& normal)
|
||||||
|
{
|
||||||
|
#ifndef QT_NO_DEBUG
|
||||||
|
if (normal.squaredNorm() < 1.0E-10)
|
||||||
|
qWarning("Vec::projectOnPlane: plane normal is not normalized (norm=%f).", normal.norm());
|
||||||
|
#endif
|
||||||
|
|
||||||
|
*this -= (((*this)*normal) / normal.squaredNorm()) * normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Returns a Vec orthogonal to the Vec. Its norm() depends on the Vec, but is zero only for a
|
||||||
|
null Vec. Note that the function that associates an orthogonalVec() to a Vec is not continous. */
|
||||||
|
Vec Vec::orthogonalVec() const
|
||||||
|
{
|
||||||
|
// Find smallest component. Keep equal case for null values.
|
||||||
|
if ((fabs(y) >= 0.9*fabs(x)) && (fabs(z) >= 0.9*fabs(x)))
|
||||||
|
return Vec(0.0, -z, y);
|
||||||
|
else
|
||||||
|
if ((fabs(x) >= 0.9*fabs(y)) && (fabs(z) >= 0.9*fabs(y)))
|
||||||
|
return Vec(-z, 0.0, x);
|
||||||
|
else
|
||||||
|
return Vec(-y, x, 0.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Constructs a Vec from a \c QDomElement representing an XML code of the form
|
||||||
|
\code< anyTagName x=".." y=".." z=".." />\endcode
|
||||||
|
|
||||||
|
If one of these attributes is missing or is not a number, a warning is displayed and the associated
|
||||||
|
value is set to 0.0.
|
||||||
|
|
||||||
|
See also domElement() and initFromDOMElement(). */
|
||||||
|
Vec::Vec(const QDomElement& element)
|
||||||
|
{
|
||||||
|
QStringList attribute;
|
||||||
|
attribute << "x" << "y" << "z";
|
||||||
|
for (int i=0; i<attribute.size(); ++i)
|
||||||
|
#ifdef QGLVIEWER_UNION_NOT_SUPPORTED
|
||||||
|
this->operator[](i) = DomUtils::qrealFromDom(element, attribute[i], 0.0);
|
||||||
|
#else
|
||||||
|
v_[i] = DomUtils::qrealFromDom(element, attribute[i], 0.0);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Returns an XML \c QDomElement that represents the Vec.
|
||||||
|
|
||||||
|
\p name is the name of the QDomElement tag. \p doc is the \c QDomDocument factory used to create
|
||||||
|
QDomElement.
|
||||||
|
|
||||||
|
When output to a file, the resulting QDomElement will look like:
|
||||||
|
\code
|
||||||
|
<name x=".." y=".." z=".." />
|
||||||
|
\endcode
|
||||||
|
|
||||||
|
Use initFromDOMElement() to restore the Vec state from the resulting \c QDomElement. See also the
|
||||||
|
Vec(const QDomElement&) constructor.
|
||||||
|
|
||||||
|
Here is complete example that creates a QDomDocument and saves it into a file:
|
||||||
|
\code
|
||||||
|
Vec sunPos;
|
||||||
|
QDomDocument document("myDocument");
|
||||||
|
QDomElement sunElement = document.createElement("Sun");
|
||||||
|
document.appendChild(sunElement);
|
||||||
|
sunElement.setAttribute("brightness", sunBrightness());
|
||||||
|
sunElement.appendChild(sunPos.domElement("sunPosition", document));
|
||||||
|
// Other additions to the document hierarchy...
|
||||||
|
|
||||||
|
// Save doc document
|
||||||
|
QFile f("myFile.xml");
|
||||||
|
if (f.open(IO_WriteOnly))
|
||||||
|
{
|
||||||
|
QTextStream out(&f);
|
||||||
|
document.save(out, 2);
|
||||||
|
f.close();
|
||||||
|
}
|
||||||
|
\endcode
|
||||||
|
|
||||||
|
See also Quaternion::domElement(), Frame::domElement(), Camera::domElement()... */
|
||||||
|
QDomElement Vec::domElement(const QString& name, QDomDocument& document) const
|
||||||
|
{
|
||||||
|
QDomElement de = document.createElement(name);
|
||||||
|
de.setAttribute("x", QString::number(x));
|
||||||
|
de.setAttribute("y", QString::number(y));
|
||||||
|
de.setAttribute("z", QString::number(z));
|
||||||
|
return de;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Restores the Vec state from a \c QDomElement created by domElement().
|
||||||
|
|
||||||
|
The \c QDomElement should contain \c x, \c y and \c z attributes. If one of these attributes is
|
||||||
|
missing or is not a number, a warning is displayed and the associated value is set to 0.0.
|
||||||
|
|
||||||
|
To restore the Vec state from an xml file, use:
|
||||||
|
\code
|
||||||
|
// Load DOM from file
|
||||||
|
QDomDocument doc;
|
||||||
|
QFile f("myFile.xml");
|
||||||
|
if (f.open(IO_ReadOnly))
|
||||||
|
{
|
||||||
|
doc.setContent(&f);
|
||||||
|
f.close();
|
||||||
|
}
|
||||||
|
// Parse the DOM tree and initialize
|
||||||
|
QDomElement main=doc.documentElement();
|
||||||
|
myVec.initFromDOMElement(main);
|
||||||
|
\endcode
|
||||||
|
|
||||||
|
See also the Vec(const QDomElement&) constructor. */
|
||||||
|
void Vec::initFromDOMElement(const QDomElement& element)
|
||||||
|
{
|
||||||
|
const Vec v(element);
|
||||||
|
*this = v;
|
||||||
|
}
|
||||||
|
|
||||||
|
ostream& operator<<(ostream& o, const Vec& v)
|
||||||
|
{
|
||||||
|
return o << v.x << '\t' << v.y << '\t' << v.z;
|
||||||
|
}
|
||||||
|
|
||||||
390
QGLViewer/vec.h
Normal file
390
QGLViewer/vec.h
Normal file
@ -0,0 +1,390 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
|
||||||
|
Copyright (C) 2002-2014 Gilles Debunne. All rights reserved.
|
||||||
|
|
||||||
|
This file is part of the QGLViewer library version 2.6.3.
|
||||||
|
|
||||||
|
http://www.libqglviewer.com - contact@libqglviewer.com
|
||||||
|
|
||||||
|
This file may be used under the terms of the GNU General Public License
|
||||||
|
versions 2.0 or 3.0 as published by the Free Software Foundation and
|
||||||
|
appearing in the LICENSE file included in the packaging of this file.
|
||||||
|
In addition, as a special exception, Gilles Debunne gives you certain
|
||||||
|
additional rights, described in the file GPL_EXCEPTION in this package.
|
||||||
|
|
||||||
|
libQGLViewer uses dual licensing. Commercial/proprietary software must
|
||||||
|
purchase a libQGLViewer Commercial License.
|
||||||
|
|
||||||
|
This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
|
||||||
|
WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
|
||||||
|
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
#ifndef QGLVIEWER_VEC_H
|
||||||
|
#define QGLVIEWER_VEC_H
|
||||||
|
|
||||||
|
#include <math.h>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
# include <QDomElement>
|
||||||
|
|
||||||
|
// Included by all files as vec.h is at the end of the include hierarchy
|
||||||
|
#include "config.h" // Specific configuration options.
|
||||||
|
|
||||||
|
namespace qglviewer {
|
||||||
|
|
||||||
|
/*! \brief The Vec class represents 3D positions and 3D vectors.
|
||||||
|
\class Vec vec.h QGLViewer/vec.h
|
||||||
|
|
||||||
|
Vec is used as a parameter and return type by many methods of the library. It provides classical
|
||||||
|
algebraic computational methods and is compatible with OpenGL:
|
||||||
|
|
||||||
|
\code
|
||||||
|
// Draws a point located at 3.0 OpenGL units in front of the camera
|
||||||
|
Vec pos = camera()->position() + 3.0 * camera()->viewDirection();
|
||||||
|
glBegin(GL_POINTS);
|
||||||
|
glVertex3fv(pos);
|
||||||
|
glEnd();
|
||||||
|
\endcode
|
||||||
|
|
||||||
|
This makes of Vec a good candidate for representing positions and vectors in your programs. Since
|
||||||
|
it is part of the \c qglviewer namespace, specify \c qglviewer::Vec or use the qglviewer
|
||||||
|
namespace:
|
||||||
|
\code
|
||||||
|
using namespace qglviewer;
|
||||||
|
\endcode
|
||||||
|
|
||||||
|
<h3>Interface with other vector classes</h3>
|
||||||
|
|
||||||
|
Vec implements a universal explicit converter, based on the \c [] \c operator.
|
||||||
|
Everywhere a \c const \c Vec& argument is expected, you can use your own vector type
|
||||||
|
instead, as long as it implements this operator (see the Vec(const C& c) documentation).
|
||||||
|
|
||||||
|
See also the Quaternion and the Frame documentations.
|
||||||
|
\nosubgrouping */
|
||||||
|
class QGLVIEWER_EXPORT Vec
|
||||||
|
{
|
||||||
|
|
||||||
|
// If your compiler complains the "The class "qglviewer::Vec" has no member "x"."
|
||||||
|
// Add your architecture Q_OS_XXXX flag (see qglobal.h) in this list.
|
||||||
|
#if defined (Q_OS_IRIX) || defined (Q_OS_AIX) || defined (Q_OS_HPUX)
|
||||||
|
# define QGLVIEWER_UNION_NOT_SUPPORTED
|
||||||
|
#endif
|
||||||
|
|
||||||
|
public:
|
||||||
|
/*! The internal data representation is public. One can use v.x, v.y, v.z. See also operator[](). */
|
||||||
|
#if defined (DOXYGEN) || defined (QGLVIEWER_UNION_NOT_SUPPORTED)
|
||||||
|
qreal x, y, z;
|
||||||
|
#else
|
||||||
|
union
|
||||||
|
{
|
||||||
|
struct { qreal x, y, z; };
|
||||||
|
qreal v_[3];
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*! @name Setting the value */
|
||||||
|
//@{
|
||||||
|
/*! Default constructor. Value is set to (0,0,0). */
|
||||||
|
Vec() : x(0.0), y(0.0), z(0.0) {}
|
||||||
|
|
||||||
|
/*! Standard constructor with the x, y and z values. */
|
||||||
|
Vec(qreal X, qreal Y, qreal Z) : x(X), y(Y), z(Z) {}
|
||||||
|
|
||||||
|
/*! Universal explicit converter from any class to Vec. You can use your own vector class everywhere
|
||||||
|
a \c const \c Vec& parameter is required, as long as it implements the \c operator[ ]:
|
||||||
|
|
||||||
|
\code
|
||||||
|
class MyVec
|
||||||
|
{
|
||||||
|
// ...
|
||||||
|
qreal operator[](int i) const { returns x, y or z when i=0, 1 or 2; }
|
||||||
|
}
|
||||||
|
|
||||||
|
MyVec v(...);
|
||||||
|
camera()->setPosition(v);
|
||||||
|
\endcode
|
||||||
|
|
||||||
|
Note that standard vector types (STL, \c qreal[3], ...) implement this operator and can hence
|
||||||
|
be used in place of Vec. See also operator const qreal*() .*/
|
||||||
|
template <class C>
|
||||||
|
explicit Vec(const C& c) : x(c[0]), y(c[1]), z(c[2]) {}
|
||||||
|
// Should NOT be explicit to prevent conflicts with operator<<.
|
||||||
|
|
||||||
|
// ! Copy constructor
|
||||||
|
// Vec(const Vec& v) : x(v.x), y(v.y), z(v.z) {}
|
||||||
|
|
||||||
|
/*! Equal operator. */
|
||||||
|
Vec& operator=(const Vec& v)
|
||||||
|
{
|
||||||
|
x = v.x; y = v.y; z = v.z;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Set the current value. May be faster than using operator=() with a temporary Vec(x,y,z). */
|
||||||
|
void setValue(qreal X, qreal Y, qreal Z)
|
||||||
|
{ x=X; y=Y; z=Z; }
|
||||||
|
|
||||||
|
// Universal equal operator which allows the use of any type in place of Vec,
|
||||||
|
// as long as the [] operator is implemented (v[0]=v.x, v[1]=v.y, v[2]=v.z).
|
||||||
|
// template <class C>
|
||||||
|
// Vec& operator=(const C& c)
|
||||||
|
// {
|
||||||
|
// x=c[0]; y=c[1]; z=c[2];
|
||||||
|
// return *this;
|
||||||
|
// }
|
||||||
|
//@}
|
||||||
|
|
||||||
|
/*! @name Accessing the value */
|
||||||
|
//@{
|
||||||
|
/*! Bracket operator, with a constant return value. \p i must range in [0..2]. */
|
||||||
|
qreal operator[](int i) const {
|
||||||
|
#ifdef QGLVIEWER_UNION_NOT_SUPPORTED
|
||||||
|
return (&x)[i];
|
||||||
|
#else
|
||||||
|
return v_[i];
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Bracket operator returning an l-value. \p i must range in [0..2]. */
|
||||||
|
qreal& operator[](int i) {
|
||||||
|
#ifdef QGLVIEWER_UNION_NOT_SUPPORTED
|
||||||
|
return (&x)[i];
|
||||||
|
#else
|
||||||
|
return v_[i];
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifndef DOXYGEN
|
||||||
|
/*! This method is deprecated since version 2.0. Use operator const double* instead. */
|
||||||
|
const double* address() const { qWarning("Vec::address() is deprecated, use operator const double* instead."); return operator const double*(); }
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*! Conversion operator returning the memory address of the vector.
|
||||||
|
|
||||||
|
Very convenient to pass a Vec pointer as a parameter to \c GLdouble OpenGL functions:
|
||||||
|
\code
|
||||||
|
Vec pos, normal;
|
||||||
|
glNormal3dv(normal);
|
||||||
|
glVertex3dv(pos);
|
||||||
|
\endcode */
|
||||||
|
operator const double*() const {
|
||||||
|
#ifdef QGLVIEWER_UNION_NOT_SUPPORTED
|
||||||
|
return &x;
|
||||||
|
#else
|
||||||
|
return v_;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Non const conversion operator returning the memory address of the vector.
|
||||||
|
|
||||||
|
Useful to pass a Vec to a method that requires and fills a \c double*, as provided by certain libraries. */
|
||||||
|
operator double*() {
|
||||||
|
#ifdef QGLVIEWER_UNION_NOT_SUPPORTED
|
||||||
|
return &x;
|
||||||
|
#else
|
||||||
|
return v_;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Conversion operator returning the memory address of the vector.
|
||||||
|
|
||||||
|
Very convenient to pass a Vec pointer as a \c float parameter to OpenGL functions:
|
||||||
|
\code
|
||||||
|
Vec pos, normal;
|
||||||
|
glNormal3fv(normal);
|
||||||
|
glVertex3fv(pos);
|
||||||
|
\endcode
|
||||||
|
\note The returned float array is a static shared by all \c Vec instances. */
|
||||||
|
operator const float*() const {
|
||||||
|
static float* const result = new float[3];
|
||||||
|
result[0] = (float)x;
|
||||||
|
result[1] = (float)y;
|
||||||
|
result[2] = (float)z;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
//@}
|
||||||
|
|
||||||
|
/*! @name Algebraic computations */
|
||||||
|
//@{
|
||||||
|
/*! Returns the sum of the two vectors. */
|
||||||
|
friend Vec operator+(const Vec &a, const Vec &b)
|
||||||
|
{
|
||||||
|
return Vec(a.x+b.x, a.y+b.y, a.z+b.z);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Returns the difference of the two vectors. */
|
||||||
|
friend Vec operator-(const Vec &a, const Vec &b)
|
||||||
|
{
|
||||||
|
return Vec(a.x-b.x, a.y-b.y, a.z-b.z);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Unary minus operator. */
|
||||||
|
friend Vec operator-(const Vec &a)
|
||||||
|
{
|
||||||
|
return Vec(-a.x, -a.y, -a.z);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Returns the product of the vector with a scalar. */
|
||||||
|
friend Vec operator*(const Vec &a, qreal k)
|
||||||
|
{
|
||||||
|
return Vec(a.x*k, a.y*k, a.z*k);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Returns the product of a scalar with the vector. */
|
||||||
|
friend Vec operator*(qreal k, const Vec &a)
|
||||||
|
{
|
||||||
|
return a*k;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Returns the division of the vector with a scalar.
|
||||||
|
|
||||||
|
Too small \p k values are \e not tested (unless the library was compiled with the "debug" Qt \c
|
||||||
|
CONFIG flag) and may result in \c NaN values. */
|
||||||
|
friend Vec operator/(const Vec &a, qreal k)
|
||||||
|
{
|
||||||
|
#ifndef QT_NO_DEBUG
|
||||||
|
if (fabs(k) < 1.0E-10)
|
||||||
|
qWarning("Vec::operator / : dividing by a null value (%f)", k);
|
||||||
|
#endif
|
||||||
|
return Vec(a.x/k, a.y/k, a.z/k);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Returns \c true only when the two vector are not equal (see operator==()). */
|
||||||
|
friend bool operator!=(const Vec &a, const Vec &b)
|
||||||
|
{
|
||||||
|
return !(a==b);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Returns \c true when the squaredNorm() of the difference vector is lower than 1E-10. */
|
||||||
|
friend bool operator==(const Vec &a, const Vec &b)
|
||||||
|
{
|
||||||
|
const qreal epsilon = 1.0E-10;
|
||||||
|
return (a-b).squaredNorm() < epsilon;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Adds \p a to the vector. */
|
||||||
|
Vec& operator+=(const Vec &a)
|
||||||
|
{
|
||||||
|
x += a.x; y += a.y; z += a.z;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Subtracts \p a to the vector. */
|
||||||
|
Vec& operator-=(const Vec &a)
|
||||||
|
{
|
||||||
|
x -= a.x; y -= a.y; z -= a.z;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Multiply the vector by a scalar \p k. */
|
||||||
|
Vec& operator*=(qreal k)
|
||||||
|
{
|
||||||
|
x *= k; y *= k; z *= k;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Divides the vector by a scalar \p k.
|
||||||
|
|
||||||
|
An absolute \p k value lower than 1E-10 will print a warning if the library was compiled with the
|
||||||
|
"debug" Qt \c CONFIG flag. Otherwise, no test is performed for efficiency reasons. */
|
||||||
|
Vec& operator/=(qreal k)
|
||||||
|
{
|
||||||
|
#ifndef QT_NO_DEBUG
|
||||||
|
if (fabs(k)<1.0E-10)
|
||||||
|
qWarning("Vec::operator /= : dividing by a null value (%f)", k);
|
||||||
|
#endif
|
||||||
|
x /= k; y /= k; z /= k;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Dot product of the two Vec. */
|
||||||
|
friend qreal operator*(const Vec &a, const Vec &b)
|
||||||
|
{
|
||||||
|
return a.x*b.x + a.y*b.y + a.z*b.z;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Cross product of the two vectors. Same as cross(). */
|
||||||
|
friend Vec operator^(const Vec &a, const Vec &b)
|
||||||
|
{
|
||||||
|
return cross(a,b);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Cross product of the two Vec. Mind the order ! */
|
||||||
|
friend Vec cross(const Vec &a, const Vec &b)
|
||||||
|
{
|
||||||
|
return Vec(a.y*b.z - a.z*b.y,
|
||||||
|
a.z*b.x - a.x*b.z,
|
||||||
|
a.x*b.y - a.y*b.x);
|
||||||
|
}
|
||||||
|
|
||||||
|
Vec orthogonalVec() const;
|
||||||
|
//@}
|
||||||
|
|
||||||
|
/*! @name Norm of the vector */
|
||||||
|
//@{
|
||||||
|
#ifndef DOXYGEN
|
||||||
|
/*! This method is deprecated since version 2.0. Use squaredNorm() instead. */
|
||||||
|
qreal sqNorm() const { return x*x + y*y + z*z; }
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*! Returns the \e squared norm of the Vec. */
|
||||||
|
qreal squaredNorm() const { return x*x + y*y + z*z; }
|
||||||
|
|
||||||
|
/*! Returns the norm of the vector. */
|
||||||
|
qreal norm() const { return sqrt(x*x + y*y + z*z); }
|
||||||
|
|
||||||
|
/*! Normalizes the Vec and returns its original norm.
|
||||||
|
|
||||||
|
Normalizing a null vector will result in \c NaN values. */
|
||||||
|
qreal normalize()
|
||||||
|
{
|
||||||
|
const qreal n = norm();
|
||||||
|
#ifndef QT_NO_DEBUG
|
||||||
|
if (n < 1.0E-10)
|
||||||
|
qWarning("Vec::normalize: normalizing a null vector (norm=%f)", n);
|
||||||
|
#endif
|
||||||
|
*this /= n;
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Returns a unitary (normalized) \e representation of the vector. The original Vec is not modified. */
|
||||||
|
Vec unit() const
|
||||||
|
{
|
||||||
|
Vec v = *this;
|
||||||
|
v.normalize();
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
//@}
|
||||||
|
|
||||||
|
/*! @name Projection */
|
||||||
|
//@{
|
||||||
|
void projectOnAxis(const Vec& direction);
|
||||||
|
void projectOnPlane(const Vec& normal);
|
||||||
|
//@}
|
||||||
|
|
||||||
|
/*! @name XML representation */
|
||||||
|
//@{
|
||||||
|
explicit Vec(const QDomElement& element);
|
||||||
|
QDomElement domElement(const QString& name, QDomDocument& document) const;
|
||||||
|
void initFromDOMElement(const QDomElement& element);
|
||||||
|
//@}
|
||||||
|
|
||||||
|
#ifdef DOXYGEN
|
||||||
|
/*! @name Output stream */
|
||||||
|
//@{
|
||||||
|
/*! Output stream operator. Enables debugging code like:
|
||||||
|
\code
|
||||||
|
Vec pos(...);
|
||||||
|
cout << "Position=" << pos << endl;
|
||||||
|
\endcode */
|
||||||
|
std::ostream& operator<<(std::ostream& o, const qglviewer::Vec&);
|
||||||
|
//@}
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
std::ostream& operator<<(std::ostream& o, const qglviewer::Vec&);
|
||||||
|
|
||||||
|
#endif // QGLVIEWER_VEC_H
|
||||||
73
log750-lab.pro
Normal file
73
log750-lab.pro
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
#-------------------------------------------------
|
||||||
|
#
|
||||||
|
# Project created by QtCreator
|
||||||
|
#
|
||||||
|
#-------------------------------------------------
|
||||||
|
|
||||||
|
QT += core gui xml opengl
|
||||||
|
|
||||||
|
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
|
||||||
|
|
||||||
|
TARGET = simpleViewer
|
||||||
|
TEMPLATE = app
|
||||||
|
|
||||||
|
QMAKE_CXXFLAGS += -std=c++0x
|
||||||
|
|
||||||
|
SOURCES += QGLViewer/camera.cpp \
|
||||||
|
QGLViewer/constraint.cpp \
|
||||||
|
QGLViewer/frame.cpp \
|
||||||
|
QGLViewer/keyFrameInterpolator.cpp \
|
||||||
|
QGLViewer/manipulatedCameraFrame.cpp \
|
||||||
|
QGLViewer/manipulatedFrame.cpp \
|
||||||
|
QGLViewer/mouseGrabber.cpp \
|
||||||
|
QGLViewer/qglviewer.cpp \
|
||||||
|
QGLViewer/quaternion.cpp \
|
||||||
|
QGLViewer/saveSnapshot.cpp \
|
||||||
|
QGLViewer/vec.cpp \
|
||||||
|
src/main.cpp \
|
||||||
|
src/window/mainwindow.cpp \
|
||||||
|
src/viewer/simpleViewer.cpp \
|
||||||
|
src/glnodes/glnode.cpp \
|
||||||
|
src/glnodes/shapes.cpp \
|
||||||
|
src/glnodes/scenegroup.cpp
|
||||||
|
|
||||||
|
HEADERS += QGLViewer/camera.h \
|
||||||
|
QGLViewer/config.h \
|
||||||
|
QGLViewer/constraint.h \
|
||||||
|
QGLViewer/domUtils.h \
|
||||||
|
QGLViewer/frame.h \
|
||||||
|
QGLViewer/keyFrameInterpolator.h \
|
||||||
|
QGLViewer/manipulatedCameraFrame.h \
|
||||||
|
QGLViewer/manipulatedFrame.h \
|
||||||
|
QGLViewer/mouseGrabber.h \
|
||||||
|
QGLViewer/qglviewer.h \
|
||||||
|
QGLViewer/quaternion.h \
|
||||||
|
QGLViewer/vec.h \
|
||||||
|
src/window/mainwindow.h \
|
||||||
|
src/viewer/simpleViewer.h \
|
||||||
|
src/glnodes/glnode.h \
|
||||||
|
src/glnodes/shapes.h \
|
||||||
|
src/interfaces/ivisitable.h \
|
||||||
|
src/interfaces/visitor.h \
|
||||||
|
src/glnodes/scenegroup.h
|
||||||
|
|
||||||
|
DISTFILES += src/shaders/basicShader.vert \
|
||||||
|
src/shaders/basicShader.frag
|
||||||
|
|
||||||
|
FORMS += QGLViewer/ImageInterface.ui mainwindow.ui
|
||||||
|
|
||||||
|
|
||||||
|
CONFIG *= debug_and_release console qt opengl warn_on thread create_prl rtti
|
||||||
|
|
||||||
|
DEFINES *= QGLVIEWER_STATIC
|
||||||
|
win32 {
|
||||||
|
DEFINES *= NOMINMAX
|
||||||
|
}
|
||||||
|
|
||||||
|
win32 {
|
||||||
|
contains ( QT_MAJOR_VERSION, 5 ) {
|
||||||
|
greaterThan( QT_MINOR_VERSION, 4) {
|
||||||
|
LIBS *= -lopengl32
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
176
mainwindow.ui
Normal file
176
mainwindow.ui
Normal file
@ -0,0 +1,176 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ui version="4.0">
|
||||||
|
<class>MainWindow</class>
|
||||||
|
<widget class="QMainWindow" name="MainWindow">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>588</width>
|
||||||
|
<height>499</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="windowTitle">
|
||||||
|
<string>MainWindow</string>
|
||||||
|
</property>
|
||||||
|
<widget class="QWidget" name="centralWidget">
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||||
|
<item>
|
||||||
|
<widget class="QFrame" name="frame">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
|
||||||
|
<horstretch>1</horstretch>
|
||||||
|
<verstretch>1</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="minimumSize">
|
||||||
|
<size>
|
||||||
|
<width>500</width>
|
||||||
|
<height>500</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="maximumSize">
|
||||||
|
<size>
|
||||||
|
<width>500</width>
|
||||||
|
<height>500</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="frameShape">
|
||||||
|
<enum>QFrame::StyledPanel</enum>
|
||||||
|
</property>
|
||||||
|
<property name="frameShadow">
|
||||||
|
<enum>QFrame::Raised</enum>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<layout class="QGridLayout" name="gridLayout">
|
||||||
|
<item row="0" column="0">
|
||||||
|
<widget class="QLabel" name="label">
|
||||||
|
<property name="text">
|
||||||
|
<string>Forme</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="1">
|
||||||
|
<spacer name="verticalSpacer">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Vertical</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>20</width>
|
||||||
|
<height>40</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="0">
|
||||||
|
<widget class="QLabel" name="label_2">
|
||||||
|
<property name="text">
|
||||||
|
<string>Couleur</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="0">
|
||||||
|
<spacer name="verticalSpacer_2">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Vertical</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>20</width>
|
||||||
|
<height>40</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
<item row="0" column="1">
|
||||||
|
<widget class="QComboBox" name="comboBox">
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string>Vide</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string>Triangle</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string>Carré</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string>Cercle</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="1">
|
||||||
|
<widget class="QPushButton" name="pushButton">
|
||||||
|
<property name="text">
|
||||||
|
<string>Couleur</string>
|
||||||
|
</property>
|
||||||
|
<property name="default">
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
<property name="flat">
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
<widget class="QMenuBar" name="menuBar">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>588</width>
|
||||||
|
<height>20</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<widget class="QMenu" name="menu_File">
|
||||||
|
<property name="title">
|
||||||
|
<string>&File</string>
|
||||||
|
</property>
|
||||||
|
<addaction name="action_Quit"/>
|
||||||
|
</widget>
|
||||||
|
<addaction name="menu_File"/>
|
||||||
|
</widget>
|
||||||
|
<widget class="QStatusBar" name="statusBar"/>
|
||||||
|
<action name="action_Quit">
|
||||||
|
<property name="text">
|
||||||
|
<string>&Quit</string>
|
||||||
|
</property>
|
||||||
|
<property name="shortcut">
|
||||||
|
<string>Esc</string>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
|
</widget>
|
||||||
|
<layoutdefault spacing="6" margin="11"/>
|
||||||
|
<resources/>
|
||||||
|
<connections>
|
||||||
|
<connection>
|
||||||
|
<sender>action_Quit</sender>
|
||||||
|
<signal>triggered()</signal>
|
||||||
|
<receiver>MainWindow</receiver>
|
||||||
|
<slot>close()</slot>
|
||||||
|
<hints>
|
||||||
|
<hint type="sourcelabel">
|
||||||
|
<x>-1</x>
|
||||||
|
<y>-1</y>
|
||||||
|
</hint>
|
||||||
|
<hint type="destinationlabel">
|
||||||
|
<x>199</x>
|
||||||
|
<y>149</y>
|
||||||
|
</hint>
|
||||||
|
</hints>
|
||||||
|
</connection>
|
||||||
|
</connections>
|
||||||
|
</ui>
|
||||||
17
src/glnodes/glnode.cpp
Normal file
17
src/glnodes/glnode.cpp
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
#include "glnode.h"
|
||||||
|
#include <qopengl.h>
|
||||||
|
|
||||||
|
|
||||||
|
//GlNode& GlNode::getChild() {
|
||||||
|
// GlNode* tempNode = new GlNode();
|
||||||
|
// return *tempNode;
|
||||||
|
//}
|
||||||
|
|
||||||
|
//bool GlNode::hasNext(){
|
||||||
|
// return false;
|
||||||
|
//}
|
||||||
|
|
||||||
|
//void GlNode::accept(Visitor &v) {
|
||||||
|
|
||||||
|
// //v.visit(this);
|
||||||
|
//}
|
||||||
14
src/glnodes/glnode.h
Normal file
14
src/glnodes/glnode.h
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
#ifndef GLNODE
|
||||||
|
#define GLNODE
|
||||||
|
#include <QMatrix4x4>
|
||||||
|
#include "../interfaces/ivisitable.h"
|
||||||
|
#include "../interfaces/visitor.h"
|
||||||
|
class Viewer;
|
||||||
|
class GlNode : public IVisitable {
|
||||||
|
public:
|
||||||
|
|
||||||
|
QMatrix4x4 transform;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // GLNODE
|
||||||
|
|
||||||
42
src/glnodes/scenegroup.cpp
Normal file
42
src/glnodes/scenegroup.cpp
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
#include "scenegroup.h"
|
||||||
|
|
||||||
|
SceneGroup::SceneGroup()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<GlNode*>* SceneGroup::getChildren() {
|
||||||
|
return &children;
|
||||||
|
}
|
||||||
|
|
||||||
|
GlNode* SceneGroup::childAt(int i) {
|
||||||
|
return children.at(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
GlNode* SceneGroup::getChild(){
|
||||||
|
// Automatically loops
|
||||||
|
if(childIndex >= children.size() || childIndex < 0){ //just in case
|
||||||
|
childIndex = 0;
|
||||||
|
}
|
||||||
|
return children.at(childIndex++);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SceneGroup::hasNext()
|
||||||
|
{
|
||||||
|
if(childIndex >= children.size() || childIndex < 0) {
|
||||||
|
childIndex = 0;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SceneGroup::addChild(GlNode* child){
|
||||||
|
|
||||||
|
children.push_back(child);
|
||||||
|
//children.push_back(std::shared_ptr<GlNode>(child));
|
||||||
|
}
|
||||||
|
|
||||||
|
void SceneGroup::accept(Visitor &v) {
|
||||||
|
v.visit(*this);
|
||||||
|
}
|
||||||
26
src/glnodes/scenegroup.h
Normal file
26
src/glnodes/scenegroup.h
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
#ifndef SCENEGROUP_H
|
||||||
|
#define SCENEGROUP_H
|
||||||
|
|
||||||
|
#include "glnode.h"
|
||||||
|
#include "../interfaces/ivisitable.h"
|
||||||
|
#include "../interfaces/visitor.h"
|
||||||
|
|
||||||
|
class SceneGroup : public GlNode
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
std::vector<GlNode*> children;
|
||||||
|
int childIndex = 0;
|
||||||
|
public:
|
||||||
|
void addChild(GlNode* c);
|
||||||
|
GlNode* getChild();
|
||||||
|
bool hasNext();
|
||||||
|
|
||||||
|
std::vector<GlNode*>* getChildren();
|
||||||
|
|
||||||
|
GlNode* childAt(int i);
|
||||||
|
|
||||||
|
void accept(Visitor& v) override;
|
||||||
|
SceneGroup();
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // SCENEGROUP_H
|
||||||
44
src/glnodes/shapes.cpp
Normal file
44
src/glnodes/shapes.cpp
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
#include "shapes.h"
|
||||||
|
#include <QColor>
|
||||||
|
#include <QVector4D>
|
||||||
|
|
||||||
|
void Square::accept(Visitor &v) {
|
||||||
|
v.visit(*this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Circle::accept(Visitor &v) {
|
||||||
|
v.visit(*this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Triangle::accept(Visitor &v) {
|
||||||
|
v.visit(*this);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ***
|
||||||
|
|
||||||
|
void Square::setColor(QColor& c) {
|
||||||
|
color = QColor(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Circle::setColor(QColor& c) {
|
||||||
|
color = QColor(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Triangle::setColor(QColor& c) {
|
||||||
|
color = QColor(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
QColor Triangle::getColor(){
|
||||||
|
return color;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
QColor Circle::getColor(){
|
||||||
|
return color;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
QColor Square::getColor(){
|
||||||
|
return color;
|
||||||
|
};
|
||||||
41
src/glnodes/shapes.h
Normal file
41
src/glnodes/shapes.h
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
#ifndef SHAPES_H
|
||||||
|
#define SHAPES_H
|
||||||
|
#include "glnode.h"
|
||||||
|
#include <QVector4D>
|
||||||
|
#include <QColor>
|
||||||
|
#include "../interfaces/visitor.h"
|
||||||
|
class Shape : public GlNode
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual void setColor(QColor& c) = 0;
|
||||||
|
virtual QColor getColor() = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
class Circle : public Shape
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Circle(){}
|
||||||
|
QColor color;
|
||||||
|
void accept(Visitor& v) override;
|
||||||
|
void setColor(QColor& c);
|
||||||
|
QColor getColor();
|
||||||
|
};
|
||||||
|
class Triangle : public Shape
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Triangle(){}
|
||||||
|
QColor color;
|
||||||
|
void accept(Visitor& v) override;
|
||||||
|
void setColor(QColor& c);
|
||||||
|
QColor getColor();
|
||||||
|
};
|
||||||
|
class Square : public Shape
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Square(){}
|
||||||
|
QColor color;
|
||||||
|
void accept(Visitor& v) override;
|
||||||
|
void setColor(QColor& c);
|
||||||
|
QColor getColor();
|
||||||
|
};
|
||||||
|
#endif // SHAPES_H
|
||||||
10
src/interfaces/ivisitable.h
Normal file
10
src/interfaces/ivisitable.h
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
#ifndef IVISITABLE_H
|
||||||
|
#define IVISITABLE_H
|
||||||
|
class Visitor;
|
||||||
|
|
||||||
|
class IVisitable {
|
||||||
|
public:
|
||||||
|
virtual void accept(Visitor& v) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // IVISITABLE_H
|
||||||
15
src/interfaces/visitor.h
Normal file
15
src/interfaces/visitor.h
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
#ifndef VISITOR_H
|
||||||
|
#define VISITOR_H
|
||||||
|
class SceneGroup;
|
||||||
|
class Square;
|
||||||
|
class Circle;
|
||||||
|
class Triangle;
|
||||||
|
|
||||||
|
class Visitor {
|
||||||
|
public:
|
||||||
|
virtual void visit(SceneGroup &n) = 0;
|
||||||
|
virtual void visit(Square &s) = 0;
|
||||||
|
virtual void visit(Circle &s) = 0;
|
||||||
|
virtual void visit(Triangle &s) = 0;
|
||||||
|
};
|
||||||
|
#endif // VISITOR_H
|
||||||
29
src/main.cpp
Normal file
29
src/main.cpp
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
#include "src/viewer/simpleViewer.h"
|
||||||
|
#include "src/window/mainwindow.h"
|
||||||
|
#include <QApplication>
|
||||||
|
#include <QDesktopWidget>
|
||||||
|
#include <QColorDialog>
|
||||||
|
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
QApplication a(argc, argv);
|
||||||
|
QDesktopWidget dw;
|
||||||
|
|
||||||
|
// Set the core profile and version of OpenGL shaders
|
||||||
|
QSurfaceFormat fmt;
|
||||||
|
fmt.setVersion(4, 0);
|
||||||
|
fmt.setProfile(QSurfaceFormat::CoreProfile);
|
||||||
|
QSurfaceFormat::setDefaultFormat(fmt);
|
||||||
|
|
||||||
|
MainWindow w;
|
||||||
|
|
||||||
|
// Instantiate and layout the viewer.
|
||||||
|
Viewer *v = new Viewer();
|
||||||
|
w.addViewer(v);
|
||||||
|
|
||||||
|
//w.setFixedSize(dw.width() * 0.7, dw.height() * 0.7);
|
||||||
|
|
||||||
|
w.show();
|
||||||
|
|
||||||
|
return a.exec();
|
||||||
|
}
|
||||||
9
src/shaders/basicShader.frag
Normal file
9
src/shaders/basicShader.frag
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
#version 400 core
|
||||||
|
in vec4 ifColor;
|
||||||
|
out vec4 fColor;
|
||||||
|
|
||||||
|
void
|
||||||
|
main()
|
||||||
|
{
|
||||||
|
fColor = ifColor;
|
||||||
|
}
|
||||||
14
src/shaders/basicShader.vert
Normal file
14
src/shaders/basicShader.vert
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
#version 400 core
|
||||||
|
uniform mat4 mvMatrix;
|
||||||
|
uniform mat4 projMatrix;
|
||||||
|
uniform vec4 color;
|
||||||
|
in vec4 vPosition;
|
||||||
|
out vec4 ifColor;
|
||||||
|
|
||||||
|
void
|
||||||
|
main()
|
||||||
|
{
|
||||||
|
gl_Position = projMatrix * mvMatrix * vPosition;
|
||||||
|
ifColor = color;
|
||||||
|
}
|
||||||
|
|
||||||
444
src/viewer/simpleViewer.cpp
Normal file
444
src/viewer/simpleViewer.cpp
Normal file
@ -0,0 +1,444 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
|
||||||
|
Copyright (C) 2002-2008 Gilles Debunne. All rights reserved.
|
||||||
|
|
||||||
|
This file is part of the QGLViewer library version 2.3.6.
|
||||||
|
|
||||||
|
http://www.libqglviewer.com - contact@libqglviewer.com
|
||||||
|
|
||||||
|
This file may be used under the terms of the GNU General Public License
|
||||||
|
versions 2.0 or 3.0 as published by the Free Software Foundation and
|
||||||
|
appearing in the LICENSE file included in the packaging of this file.
|
||||||
|
In addition, as a special exception, Gilles Debunne gives you certain
|
||||||
|
additional rights, described in the file GPL_EXCEPTION in this package.
|
||||||
|
|
||||||
|
libQGLViewer uses dual licensing. Commercial/proprietary software must
|
||||||
|
purchase a libQGLViewer Commercial License.
|
||||||
|
|
||||||
|
This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
|
||||||
|
WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
|
||||||
|
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
#include "simpleViewer.h"
|
||||||
|
#include "../interfaces/visitor.h"
|
||||||
|
#include "../glnodes/scenegroup.h"
|
||||||
|
#include "../glnodes/shapes.h"
|
||||||
|
|
||||||
|
#include <qopengl.h>
|
||||||
|
#include <QOpenGLShaderProgram>
|
||||||
|
|
||||||
|
#include <qopengl.h>
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
#include <stack>
|
||||||
|
#include <QMouseEvent>
|
||||||
|
#include <typeinfo>
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
#define BUFFER_OFFSET(i) ((char *)NULL + (i))
|
||||||
|
#define GRID_SIZE 10;
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
const int numVerticesSquare = 5;
|
||||||
|
const int numVerticesTriangle = 3;
|
||||||
|
const int numVerticesCircle = 26;
|
||||||
|
const double frame_limit = 5;
|
||||||
|
const double inc_mult = 5;
|
||||||
|
const double inc_offset = 1.05;
|
||||||
|
}
|
||||||
|
|
||||||
|
Viewer::Viewer()
|
||||||
|
{
|
||||||
|
activeColor = new QColor(255, 255, 255, 255);
|
||||||
|
activeCell = nullptr;
|
||||||
|
activeShape = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
Viewer::~Viewer()
|
||||||
|
{
|
||||||
|
cleanup();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Viewer::cleanup()
|
||||||
|
{
|
||||||
|
makeCurrent();
|
||||||
|
|
||||||
|
// Delete shaders
|
||||||
|
delete m_program;
|
||||||
|
m_program = 0;
|
||||||
|
|
||||||
|
// Delete buffers
|
||||||
|
glDeleteBuffers(NumBuffers, m_Buffers);
|
||||||
|
glDeleteVertexArrays(NumVAOs, m_VAOs);
|
||||||
|
|
||||||
|
doneCurrent();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Viewer::draw()
|
||||||
|
{
|
||||||
|
// Bind our vertex/fragment shaders
|
||||||
|
m_program->bind();
|
||||||
|
|
||||||
|
// Get projection and camera transformations
|
||||||
|
QMatrix4x4 projectionMatrix;
|
||||||
|
QMatrix4x4 modelViewMatrix;
|
||||||
|
camera()->getProjectionMatrix(projectionMatrix);
|
||||||
|
camera()->getModelViewMatrix(modelViewMatrix);
|
||||||
|
|
||||||
|
// Prepare a transformation stack
|
||||||
|
|
||||||
|
// stack<QMatrix4x4> modelStack;
|
||||||
|
|
||||||
|
modelViewMatrix.translate(-4.5, -4.5);
|
||||||
|
modelViewMatrix.scale(0.95);
|
||||||
|
|
||||||
|
m_program->setUniformValue(m_projMatrixLocation, projectionMatrix);
|
||||||
|
m_program->setUniformValue(m_mvMatrixLocation, modelViewMatrix);
|
||||||
|
|
||||||
|
// Traverse the Scene in order to draw its components
|
||||||
|
|
||||||
|
modelStack.push(modelViewMatrix);
|
||||||
|
root.accept(*this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Viewer::mouseMoveEvent(QMouseEvent* e) {
|
||||||
|
cout << "Viewer::mouseMoveEvent(QMouseEvent* e)" << endl;
|
||||||
|
// Normal QGLViewer behavior.
|
||||||
|
//QGLViewer::mouseMoveEvent(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Viewer::mousePressEvent(QMouseEvent* e) {
|
||||||
|
|
||||||
|
cout << "Viewer::mouseMoveEvent(QMouseEvent* e) : " << e->button() << endl;
|
||||||
|
|
||||||
|
if(e->button() == 1){ // LMB
|
||||||
|
|
||||||
|
int truY = 10 - (e->y()) / 500.0 * GRID_SIZE;
|
||||||
|
int truX = (e->x() / 500.0) * GRID_SIZE;
|
||||||
|
|
||||||
|
cout << " -->Getting cell at " << truX << " : " << truY << endl;
|
||||||
|
|
||||||
|
SceneGroup* row = dynamic_cast<SceneGroup*> (root.childAt(truY));
|
||||||
|
cout << " -->" << row << endl;
|
||||||
|
SceneGroup* cell = dynamic_cast<SceneGroup*> (row->childAt(truX));
|
||||||
|
cout << " -->" << cell << endl;
|
||||||
|
|
||||||
|
if(e->modifiers() & Qt::ShiftModifier){
|
||||||
|
// add a shape
|
||||||
|
if(!cell->getChildren()->size()){
|
||||||
|
|
||||||
|
// WARNING: CODE DEGEULASSE
|
||||||
|
|
||||||
|
Shape* s = nullptr;
|
||||||
|
if(activeShape == 1){
|
||||||
|
s = new Triangle();
|
||||||
|
}else if(activeShape == 2){
|
||||||
|
s = new Square();
|
||||||
|
}else if(activeShape == 3){
|
||||||
|
s = new Circle();
|
||||||
|
}
|
||||||
|
|
||||||
|
// WARNING: END OF CODE DEGEULASSE
|
||||||
|
|
||||||
|
//activeCell->getChildren()->at(0) = s;
|
||||||
|
if(s != nullptr){
|
||||||
|
s->setColor(*activeColor);
|
||||||
|
cell->addChild(s);
|
||||||
|
this->update();
|
||||||
|
deselect();
|
||||||
|
activeCell = cell;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(e->modifiers() & Qt::ControlModifier){
|
||||||
|
// select a shape
|
||||||
|
deselect();
|
||||||
|
activeCell = cell;
|
||||||
|
if(activeCell != nullptr && activeCell->getChildren()->size()){
|
||||||
|
std::cout << "Cell has children..." << endl;
|
||||||
|
Shape* shape = dynamic_cast<Shape*> (activeCell->childAt(0));
|
||||||
|
QColor newColor = shape->getColor();
|
||||||
|
std::cout << newColor.Rgb << endl;
|
||||||
|
newColor.setAlpha(255);
|
||||||
|
shape->setColor(newColor);
|
||||||
|
//emit shapeSelected(getTypeIndex(typeof activeCell->childAt(0)));
|
||||||
|
int shapeId = 0;
|
||||||
|
if(typeid(*(activeCell->getChildren()->at(0))) == typeid(Triangle))
|
||||||
|
shapeId = 1;
|
||||||
|
if(typeid(*(activeCell->getChildren()->at(0))) == typeid(Square))
|
||||||
|
shapeId = 2;
|
||||||
|
if(typeid(*(activeCell->getChildren()->at(0))) == typeid(Circle))
|
||||||
|
shapeId = 3;
|
||||||
|
emit shapeSelected(shapeId);
|
||||||
|
}else{
|
||||||
|
emit shapeSelected(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Viewer::deselect(){
|
||||||
|
std::cout << "Deselecting cell " << activeCell << endl;
|
||||||
|
if(activeCell != nullptr && activeCell->getChildren()->size()){
|
||||||
|
std::cout << "Cell has children..." << endl;
|
||||||
|
Shape* shape = dynamic_cast<Shape*> (activeCell->childAt(0));
|
||||||
|
QColor newColor = shape->getColor();
|
||||||
|
std::cout << newColor.Rgb << endl;
|
||||||
|
newColor.setAlpha(180);
|
||||||
|
shape->setColor(newColor);
|
||||||
|
}else{
|
||||||
|
std::cout << "Cell has no children, moving on" << endl;
|
||||||
|
}
|
||||||
|
this->update();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Viewer::mouseReleaseEvent(QMouseEvent* e) {
|
||||||
|
cout << "Viewer::mouseReleaseEvent(QMouseEvent* e)" << endl;
|
||||||
|
//QGLViewer::mouseReleaseEvent(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Viewer::init()
|
||||||
|
{
|
||||||
|
// We want to restrict ourselves to a 2D viewer.
|
||||||
|
camera()->setType(qglviewer::Camera::ORTHOGRAPHIC);
|
||||||
|
/*setMouseBinding(Qt::NoModifier, Qt::LeftButton, CAMERA, SCREEN_ROTATE);
|
||||||
|
setMouseBinding(Qt::AltModifier, Qt::LeftButton, CAMERA, NO_MOUSE_ACTION);*/
|
||||||
|
setMouseBinding(Qt::NoModifier, Qt::MouseButton(Qt::LeftButton + Qt::MidButton), CAMERA, NO_MOUSE_ACTION);
|
||||||
|
setMouseBinding(Qt::ControlModifier, Qt::MouseButton(Qt::LeftButton + Qt::MidButton), CAMERA, NO_MOUSE_ACTION);
|
||||||
|
setMouseBinding(Qt::ShiftModifier, Qt::MouseButton(Qt::LeftButton + Qt::MidButton), CAMERA, NO_MOUSE_ACTION);
|
||||||
|
|
||||||
|
// Our scene will be from -5 to 5 in X and Y (the grid will be 10x10).
|
||||||
|
setSceneRadius(5);
|
||||||
|
showEntireScene();
|
||||||
|
|
||||||
|
// Init OpenGL objects
|
||||||
|
connect(context(), &QOpenGLContext::aboutToBeDestroyed, this, &Viewer::cleanup);
|
||||||
|
initializeOpenGLFunctions();
|
||||||
|
|
||||||
|
// Init shaders & geometry
|
||||||
|
initShaders();
|
||||||
|
initGeometries();
|
||||||
|
initGrid();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Viewer::initShaders()
|
||||||
|
{
|
||||||
|
// Load vertex and fragment shaders
|
||||||
|
m_program = new QOpenGLShaderProgram;
|
||||||
|
if (!m_program->addShaderFromSourceFile(QOpenGLShader::Vertex, "src/shaders/basicShader.vert")) {
|
||||||
|
cerr << "Unable to load Shader" << endl
|
||||||
|
<< "Log file:" << endl;
|
||||||
|
qDebug() << m_program->log();
|
||||||
|
}
|
||||||
|
if (!m_program->addShaderFromSourceFile(QOpenGLShader::Fragment, "src/shaders/basicShader.frag")) {
|
||||||
|
cerr << "Unable to load Shader" << endl
|
||||||
|
<< "Log file:" << endl;
|
||||||
|
qDebug() << m_program->log();
|
||||||
|
}
|
||||||
|
m_program->link();
|
||||||
|
m_program->bind(); // Note: This is equivalent to glUseProgram(programId());
|
||||||
|
|
||||||
|
// Specify shader input paramters
|
||||||
|
// The strings "vPosition", "mvMatrix", etc. have to match an attribute name in the vertex shader.
|
||||||
|
if ((m_vPositionLocation = m_program->attributeLocation("vPosition")) < 0)
|
||||||
|
qDebug() << "Unable to find shader location for " << "vPosition";
|
||||||
|
|
||||||
|
if ((m_colorLocation = m_program->uniformLocation("color")) < 0)
|
||||||
|
qDebug() << "Unable to find shader location for " << "color";
|
||||||
|
|
||||||
|
if ((m_mvMatrixLocation = m_program->uniformLocation("mvMatrix")) < 0)
|
||||||
|
qDebug() << "Unable to find shader location for " << "mvMatrix";
|
||||||
|
|
||||||
|
if ((m_projMatrixLocation = m_program->uniformLocation("projMatrix")) < 0)
|
||||||
|
qDebug() << "Unable to find shader location for " << "projMatrix";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Creates the basic shapes in memory. We only have 3, so we just prep them all in advance.
|
||||||
|
void Viewer::initGeometries()
|
||||||
|
{
|
||||||
|
|
||||||
|
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glEnable( GL_BLEND ); glClearColor(0.0,0.0,0.0,0.0);
|
||||||
|
|
||||||
|
// Create our VertexArrays Objects and VertexBuffer Objects
|
||||||
|
glGenVertexArrays(NumVAOs, m_VAOs);
|
||||||
|
glGenBuffers(NumBuffers, m_Buffers);
|
||||||
|
|
||||||
|
// Create our pentagone object, store its vertices on the graphic card, and
|
||||||
|
// bind the data to the vPosition attribute of the shader
|
||||||
|
GLfloat verticesSquare[numVerticesSquare][3] = {
|
||||||
|
{ -0.5, 0.5, 0 },
|
||||||
|
{ -0.5, -0.5, 0 },
|
||||||
|
{ 0.5, -0.5, 0 },
|
||||||
|
{ 0.5, 0.5, 0 },
|
||||||
|
{ -0.5, 0.5, 0 }
|
||||||
|
};
|
||||||
|
|
||||||
|
glBindVertexArray(m_VAOs[VAO_Square]);
|
||||||
|
glBindBuffer(GL_ARRAY_BUFFER, m_Buffers[VBO_Square]);
|
||||||
|
glBufferData(GL_ARRAY_BUFFER, sizeof(verticesSquare), verticesSquare, GL_STATIC_DRAW);
|
||||||
|
|
||||||
|
glVertexAttribPointer(m_vPositionLocation, 3, GL_FLOAT, GL_FALSE, 0, BUFFER_OFFSET(0));
|
||||||
|
glEnableVertexAttribArray(m_vPositionLocation);
|
||||||
|
|
||||||
|
// Create our triangle object, store its vertices on the graphic card, and
|
||||||
|
// bind the data to the vPosition attribute of the shader
|
||||||
|
GLfloat verticesTriangle[numVerticesTriangle][3] = {
|
||||||
|
{ -0.5, -0.5, 0.0 },
|
||||||
|
{ 0.5, -0.5, 0.0 },
|
||||||
|
{ 0.0, 0.5, 0.0 }
|
||||||
|
};
|
||||||
|
|
||||||
|
glBindVertexArray(m_VAOs[VAO_Triangle]);
|
||||||
|
glBindBuffer(GL_ARRAY_BUFFER, m_Buffers[VBO_Triangle]);
|
||||||
|
glBufferData(GL_ARRAY_BUFFER, sizeof(verticesTriangle), verticesTriangle, GL_STATIC_DRAW);
|
||||||
|
|
||||||
|
glVertexAttribPointer(m_vPositionLocation, 3, GL_FLOAT, GL_FALSE, 0, BUFFER_OFFSET(0));
|
||||||
|
glEnableVertexAttribArray(m_vPositionLocation);
|
||||||
|
|
||||||
|
// Create our Circle Object, and store its vertices with its bindings
|
||||||
|
|
||||||
|
GLfloat verticesCircle[numVerticesCircle][3];
|
||||||
|
|
||||||
|
const float PI = 3.1415926f;
|
||||||
|
double increment = 2.0f * PI / (numVerticesCircle - 2);
|
||||||
|
|
||||||
|
verticesCircle[0][0] = 0;
|
||||||
|
verticesCircle[0][1] = 0;
|
||||||
|
verticesCircle[0][2] = 0;
|
||||||
|
|
||||||
|
for(int i = 1; i < numVerticesCircle; i++) {
|
||||||
|
double angle = increment * (i);
|
||||||
|
|
||||||
|
verticesCircle[i][0] = 0.5 * cos(angle);
|
||||||
|
verticesCircle[i][1] = 0.5 * sin(angle);
|
||||||
|
verticesCircle[i][2] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
glBindVertexArray(m_VAOs[VAO_Circle]);
|
||||||
|
glBindBuffer(GL_ARRAY_BUFFER, m_Buffers[VBO_Circle]);
|
||||||
|
glBufferData(GL_ARRAY_BUFFER, sizeof(verticesCircle), verticesCircle, GL_STATIC_DRAW);
|
||||||
|
|
||||||
|
glVertexAttribPointer(m_vPositionLocation, 3, GL_FLOAT, GL_FALSE, 0, BUFFER_OFFSET(0));
|
||||||
|
glEnableVertexAttribArray(m_vPositionLocation);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Viewer::initGrid()
|
||||||
|
{
|
||||||
|
// Prepare construction loop. Also contains necessary translations
|
||||||
|
int i,j, limit;
|
||||||
|
limit = GRID_SIZE;
|
||||||
|
|
||||||
|
for(i = 0; i < limit ; i++) {
|
||||||
|
SceneGroup *row = new SceneGroup();
|
||||||
|
|
||||||
|
row->transform.translate(0, inc_offset * i);
|
||||||
|
|
||||||
|
|
||||||
|
for(j = 0; j < limit ; j++) {
|
||||||
|
SceneGroup *cell = new SceneGroup();
|
||||||
|
|
||||||
|
cell->transform.translate(inc_offset * j, 0);
|
||||||
|
|
||||||
|
row->addChild(cell);
|
||||||
|
}
|
||||||
|
|
||||||
|
root.addChild(row);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Viewer::visit(Square &s)
|
||||||
|
{
|
||||||
|
QMatrix4x4 modelViewMatrix = modelStack.top() * QMatrix4x4(s.transform);
|
||||||
|
|
||||||
|
glBindVertexArray(m_VAOs[VAO_Square]);
|
||||||
|
m_program->setUniformValue(m_mvMatrixLocation, modelViewMatrix);
|
||||||
|
m_program->setUniformValue(m_colorLocation, s.color);
|
||||||
|
|
||||||
|
glDrawArrays(GL_TRIANGLE_FAN, 0, numVerticesSquare);
|
||||||
|
}
|
||||||
|
void Viewer::visit(Circle &s)
|
||||||
|
{
|
||||||
|
QMatrix4x4 modelViewMatrix = modelStack.top() * QMatrix4x4(s.transform);
|
||||||
|
|
||||||
|
glBindVertexArray(m_VAOs[VAO_Circle]);
|
||||||
|
m_program->setUniformValue(m_mvMatrixLocation, modelViewMatrix);
|
||||||
|
m_program->setUniformValue(m_colorLocation, s.color);
|
||||||
|
|
||||||
|
glDrawArrays(GL_TRIANGLE_FAN, 0, numVerticesCircle);
|
||||||
|
}
|
||||||
|
void Viewer::visit(Triangle &s)
|
||||||
|
{
|
||||||
|
QMatrix4x4 modelViewMatrix = modelStack.top() * QMatrix4x4(s.transform);
|
||||||
|
|
||||||
|
glBindVertexArray(m_VAOs[VAO_Triangle]);
|
||||||
|
m_program->setUniformValue(m_mvMatrixLocation, modelViewMatrix);
|
||||||
|
m_program->setUniformValue(m_colorLocation, s.color);
|
||||||
|
|
||||||
|
glDrawArrays(GL_TRIANGLES, 0, numVerticesTriangle);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Viewer::visit(SceneGroup &s)
|
||||||
|
{
|
||||||
|
// Build compound transformation matrix
|
||||||
|
QMatrix4x4 currentMatrix = modelStack.top() * QMatrix4x4(s.transform);
|
||||||
|
modelStack.push(currentMatrix);
|
||||||
|
|
||||||
|
while(s.hasNext()) {
|
||||||
|
// Get next leaf
|
||||||
|
GlNode* current = s.getChild();
|
||||||
|
|
||||||
|
// Draw/Traverse child
|
||||||
|
current->accept(*this);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return model matrix to previous state
|
||||||
|
modelStack.pop();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Viewer::changeColor(QColor c){
|
||||||
|
if(activeCell->getChildren()->size()){
|
||||||
|
Shape* shape = dynamic_cast<Shape*> (activeCell->childAt(0));
|
||||||
|
shape->setColor(c);
|
||||||
|
}
|
||||||
|
activeColor = new QColor(c);
|
||||||
|
this->update();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Viewer::changeShape(int s){
|
||||||
|
std::cout << "Chaging active shape from " << activeShape << " to " << s << endl;
|
||||||
|
|
||||||
|
if(activeCell != nullptr && activeCell->getChildren()->size()){
|
||||||
|
std::cout << "Chaging cell-bound shape... " << endl;
|
||||||
|
Shape* shape = dynamic_cast<Shape*> (activeCell->childAt(0));
|
||||||
|
QColor c = shape->getColor();
|
||||||
|
|
||||||
|
// WARNING: CODE DEGEULASSE
|
||||||
|
|
||||||
|
Shape* newShape = nullptr;
|
||||||
|
if(s == 1){
|
||||||
|
newShape = new Triangle();
|
||||||
|
}else if(s == 2){
|
||||||
|
newShape = new Square();
|
||||||
|
}else if(s == 3){
|
||||||
|
newShape = new Circle();
|
||||||
|
}
|
||||||
|
|
||||||
|
if(newShape != nullptr){
|
||||||
|
newShape->setColor(c);
|
||||||
|
activeCell->getChildren()->at(0) = newShape;
|
||||||
|
}else{
|
||||||
|
activeCell->getChildren()->clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
// WARNING: END OF CODE DEGEULASSE
|
||||||
|
|
||||||
|
this->update();
|
||||||
|
}else{
|
||||||
|
std::cout << "Cell has no children, ignoring change." << endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
activeShape = s;
|
||||||
|
};
|
||||||
95
src/viewer/simpleViewer.h
Normal file
95
src/viewer/simpleViewer.h
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
|
||||||
|
Copyright (C) 2002-2008 Gilles Debunne. All rights reserved.
|
||||||
|
|
||||||
|
This file is part of the QGLViewer library version 2.3.6.
|
||||||
|
|
||||||
|
http://www.libqglviewer.com - contact@libqglviewer.com
|
||||||
|
|
||||||
|
This file may be used under the terms of the GNU General Public License
|
||||||
|
versions 2.0 or 3.0 as published by the Free Software Foundation and
|
||||||
|
appearing in the LICENSE file included in the packaging of this file.
|
||||||
|
In addition, as a special exception, Gilles Debunne gives you certain
|
||||||
|
additional rights, described in the file GPL_EXCEPTION in this package.
|
||||||
|
|
||||||
|
libQGLViewer uses dual licensing. Commercial/proprietary software must
|
||||||
|
purchase a libQGLViewer Commercial License.
|
||||||
|
|
||||||
|
This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
|
||||||
|
WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
|
||||||
|
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
#ifndef SIMPLEVIEWER_H
|
||||||
|
#define SIMPLEVIEWER_H
|
||||||
|
|
||||||
|
#include <QOpenGLFunctions_4_0_Core>
|
||||||
|
#include <QOpenGLVertexArrayObject>
|
||||||
|
#include <QOpenGLBuffer>
|
||||||
|
#include <QMatrix4x4>
|
||||||
|
#include <QGLViewer/qglviewer.h>
|
||||||
|
#include "../interfaces/visitor.h"
|
||||||
|
#include "../glnodes/glnode.h"
|
||||||
|
#include "../glnodes/scenegroup.h"
|
||||||
|
#include "../glnodes/shapes.h"
|
||||||
|
#include <stack>
|
||||||
|
|
||||||
|
QT_FORWARD_DECLARE_CLASS(QOpenGLShaderProgram)
|
||||||
|
|
||||||
|
class Viewer : public QGLViewer, protected QOpenGLFunctions_4_0_Core, public Visitor
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
Viewer();
|
||||||
|
~Viewer();
|
||||||
|
|
||||||
|
virtual void visit(SceneGroup &s);
|
||||||
|
virtual void visit(Square &s);
|
||||||
|
virtual void visit(Circle &s);
|
||||||
|
virtual void visit(Triangle &s);
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void cleanup();
|
||||||
|
void changeColor(QColor);
|
||||||
|
void changeShape(int);
|
||||||
|
|
||||||
|
signals:
|
||||||
|
int shapeSelected(int);
|
||||||
|
|
||||||
|
protected :
|
||||||
|
virtual void draw();
|
||||||
|
virtual void init();
|
||||||
|
|
||||||
|
virtual void mouseMoveEvent(QMouseEvent* e);
|
||||||
|
virtual void mouseReleaseEvent(QMouseEvent* e);
|
||||||
|
virtual void mousePressEvent(QMouseEvent *e);
|
||||||
|
|
||||||
|
SceneGroup root;
|
||||||
|
std::stack<QMatrix4x4> modelStack;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void initShaders();
|
||||||
|
void initGeometries();
|
||||||
|
void initGrid();
|
||||||
|
void deselect();
|
||||||
|
|
||||||
|
QOpenGLShaderProgram *m_program;
|
||||||
|
int m_vPositionLocation;
|
||||||
|
int m_colorLocation;
|
||||||
|
int m_projMatrixLocation;
|
||||||
|
int m_mvMatrixLocation;
|
||||||
|
|
||||||
|
SceneGroup* activeCell;
|
||||||
|
QColor* activeColor;
|
||||||
|
int activeShape;
|
||||||
|
|
||||||
|
enum VAO_IDs { VAO_Square, VAO_Triangle, VAO_Circle, NumVAOs };
|
||||||
|
enum Buffer_IDs { VBO_Square, VBO_Triangle, VBO_Circle, NumBuffers };
|
||||||
|
|
||||||
|
GLuint m_VAOs[NumVAOs];
|
||||||
|
GLuint m_Buffers[NumBuffers];
|
||||||
|
|
||||||
|
Shape* generateShapeFromIndex(int);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // SIMPLEVIEWER_H
|
||||||
35
src/window/mainwindow.cpp
Normal file
35
src/window/mainwindow.cpp
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
#include "mainwindow.h"
|
||||||
|
#include "ui_mainwindow.h"
|
||||||
|
#include <QBoxLayout>
|
||||||
|
#include <QColorDialog>
|
||||||
|
|
||||||
|
MainWindow::MainWindow(QWidget *parent) :
|
||||||
|
QMainWindow(parent),
|
||||||
|
ui(new Ui::MainWindow),
|
||||||
|
colordialog(new QColorDialog)
|
||||||
|
{
|
||||||
|
ui->setupUi(this);
|
||||||
|
|
||||||
|
connect(ui->pushButton, SIGNAL(clicked(bool)), this, SLOT(onColorPickerActivate()));
|
||||||
|
}
|
||||||
|
|
||||||
|
MainWindow::~MainWindow()
|
||||||
|
{
|
||||||
|
delete ui;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::addViewer(Viewer* viewer)
|
||||||
|
{
|
||||||
|
QLayout *layout = new QHBoxLayout;
|
||||||
|
layout->addWidget(viewer);
|
||||||
|
ui->frame->setLayout(layout);
|
||||||
|
|
||||||
|
connect(colordialog, SIGNAL(colorSelected(QColor)), viewer, SLOT(changeColor(QColor)));
|
||||||
|
connect(ui->comboBox, SIGNAL(currentIndexChanged(int)), viewer, SLOT(changeShape(int)));
|
||||||
|
connect(viewer, SIGNAL(shapeSelected(int)), ui->comboBox, SLOT(setCurrentIndex(int)));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::onColorPickerActivate(){
|
||||||
|
colordialog->open();
|
||||||
|
}
|
||||||
30
src/window/mainwindow.h
Normal file
30
src/window/mainwindow.h
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
#ifndef MAINWINDOW_H
|
||||||
|
#define MAINWINDOW_H
|
||||||
|
|
||||||
|
#include <QMainWindow>
|
||||||
|
#include <QColorDialog>
|
||||||
|
#include "src/viewer/simpleViewer.h"
|
||||||
|
|
||||||
|
namespace Ui {
|
||||||
|
class MainWindow;
|
||||||
|
}
|
||||||
|
|
||||||
|
class MainWindow : public QMainWindow
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit MainWindow(QWidget *parent = 0);
|
||||||
|
~MainWindow();
|
||||||
|
|
||||||
|
void addViewer(Viewer* viewer);
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void onColorPickerActivate();
|
||||||
|
|
||||||
|
private:
|
||||||
|
Ui::MainWindow *ui;
|
||||||
|
QColorDialog *colordialog;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // MAINWINDOW_H
|
||||||
Loading…
x
Reference in New Issue
Block a user