1 /**********************************************************************
2 Camera - Class for representing the view.
4 Copyright (C) 2007 Benoit Jacob
6 This file is part of the Avogadro molecular editor project.
7 For more information, see <http://avogadro.sourceforge.net/>
9 Avogadro is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 2 of the License, or
12 (at your option) any later version.
14 Avogadro is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
19 You should have received a copy of the GNU General Public License
20 along with this program; if not, write to the Free Software
21 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
23 **********************************************************************/
26 #include <avogadro/camera.h>
27 #include <avogadro/glwidget.h>
29 using namespace Eigen
;
38 MatrixP3d modelview
, projection
;
39 const GLWidget
*parent
;
43 Camera::Camera(const GLWidget
*parent
, double angleOfViewY
) : d(new CameraPrivate
)
45 d
->modelview
.loadIdentity();
46 d
->projection
.loadIdentity();
48 d
->angleOfViewY
= angleOfViewY
;
56 Camera::Camera(const Camera
*camera
) : d(new CameraPrivate
)
58 d
->modelview
= camera
->d
->modelview
;
59 d
->projection
= camera
->d
->projection
;
60 d
->parent
= camera
->d
->parent
;
61 d
->angleOfViewY
= camera
->d
->angleOfViewY
;
64 void Camera::setParent(const GLWidget
*parent
)
69 void Camera::normalize()
73 d
->modelview
.getLinearComponent(&m
);
80 c1
-= dot(c0
, c1
) * c0
;
85 c2
-= dot(c0
, c2
) * c0
;
86 c2
-= dot(c1
, c2
) * c1
;
90 d
->modelview
.setLinearComponent(m
);
91 d
->modelview
.matrix().setRow(3, Vector4d(0.0, 0.0, 0.0, 1.0));
94 const GLWidget
*Camera::parent() const
99 void Camera::setAngleOfViewY(double angleOfViewY
)
101 d
->angleOfViewY
= angleOfViewY
;
104 double Camera::angleOfViewY() const
106 return d
->angleOfViewY
;
109 void Camera::translate(const Eigen::Vector3d
&vector
)
111 d
->modelview
.translate(vector
);
114 void Camera::pretranslate(const Eigen::Vector3d
&vector
)
116 d
->modelview
.pretranslate(vector
);
119 void Camera::rotate(const double &angle
, const Eigen::Vector3d
&axis
)
121 d
->modelview
.rotate3(angle
, axis
);
125 void Camera::prerotate(const double &angle
, const Eigen::Vector3d
&axis
)
127 d
->modelview
.prerotate3(angle
, axis
);
131 const double Camera::distance(const Eigen::Vector3d
& point
) const
133 return ( d
->modelview
* point
).norm();
136 void Camera::setModelview(const Eigen::MatrixP3d
&matrix
)
138 d
->modelview
= matrix
;
141 const Eigen::MatrixP3d
& Camera::modelview() const
146 Eigen::MatrixP3d
& Camera::modelview()
151 void Camera::initializeViewPoint()
153 d
->modelview
.loadIdentity();
154 if( d
->parent
== 0 ) return;
155 if( d
->parent
->molecule() == 0 ) return;
157 // if the molecule is empty, we want to look at its center
158 // (which is probably at the origin, but who knows) from some distance
160 if( d
->parent
->molecule()->NumAtoms() == 0 )
162 d
->modelview
.translate( d
->parent
->center() - Vector3d( 0, 0, 10 ) );
166 // if we're here, the molecule is not empty, i.e. has atoms.
167 // we want a top-down view on it, i.e. the molecule should fit as well as
168 // possible in the (X,Y)-plane. Equivalently, we want the Z axis to be parallel
169 // to the normal vector of the molecule's fitting plane.
170 // Thus we construct a suitable base-change rotation.
172 rotation
.setRow(2, d
->parent
->normalVector());
173 rotation
.setRow(0, rotation
.row(2).ortho());
174 rotation
.setRow(1, rotation
.row(2).cross(rotation
.row(0)));
176 // set the camera's matrix to be (the 4x4 version of) this rotation.
177 setModelview(rotation
);
179 // now we want to move backwards, in order
180 // to view the molecule from a distance, not from inside it.
181 // This translation must be applied after the above rotation, so we
182 // want a left-multiplication here. Whence pretranslate().
183 const Vector3d
Zaxis(0,0,1);
184 pretranslate( - 3.0 * ( d
->parent
->radius() + CAMERA_NEAR_DISTANCE
) * Zaxis
);
186 // the above rotation is meant to be a rotation around the molecule's
187 // center. So before this rotation is applied, the molecule's center
188 // must be brought to the origin of the coordinate systemby a translation.
189 // As this translation must be applied first, we want a right-multiplication here.
190 // Whence translate().
191 translate( - d
->parent
->center() );
194 void Camera::applyPerspective() const
196 if( d
->parent
== 0 ) return;
197 if( d
->parent
->molecule() == 0 ) return;
199 double molRadius
= d
->parent
->radius() + CAMERA_MOL_RADIUS_MARGIN
;
200 double distanceToMolCenter
= distance( d
->parent
->center() );
201 double zNear
= std::max( CAMERA_NEAR_DISTANCE
, distanceToMolCenter
- molRadius
);
202 double zFar
= distanceToMolCenter
+ molRadius
;
203 double aspectRatio
= static_cast<double>(d
->parent
->width()) / d
->parent
->height();
204 gluPerspective( d
->angleOfViewY
, aspectRatio
, zNear
, zFar
);
205 glGetDoublev(GL_PROJECTION_MATRIX
, d
->projection
.array());
208 void Camera::applyModelview() const
210 glMultMatrixd( d
->modelview
.array() );
213 Eigen::Vector3d
Camera::unProject(const Eigen::Vector3d
& v
) const
215 GLint viewport
[4] = {0, 0, parent()->width(), parent()->height() };
217 gluUnProject(v
.x(), parent()->height() - v
.y(), v
.z(),
218 d
->modelview
.array(), d
->projection
.array(), viewport
, &pos
.x(), &pos
.y(), &pos
.z());
222 Eigen::Vector3d
Camera::unProject(const QPoint
& p
, const Eigen::Vector3d
& ref
) const
224 return unProject( Eigen::Vector3d( p
.x(), p
.y(), project(ref
).z() ));
227 Eigen::Vector3d
Camera::unProject(const QPoint
& p
) const
229 return unProject(p
, parent()->center());
232 Eigen::Vector3d
Camera::project(const Eigen::Vector3d
& v
) const
234 GLint viewport
[4] = {0, 0, parent()->width(), parent()->height() };
236 gluProject(v
.x(), v
.y(), v
.z(),
237 d
->modelview
.array(), d
->projection
.array(), viewport
, &pos
.x(), &pos
.y(), &pos
.z());
239 pos
.y() = parent()->height() - pos
.y();
243 Eigen::Vector3d
Camera::backTransformedXAxis() const
245 return Eigen::Vector3d( d
->modelview(0, 0),
247 d
->modelview(0, 2) );
250 Eigen::Vector3d
Camera::backTransformedYAxis() const
252 return Eigen::Vector3d( d
->modelview(1, 0),
254 d
->modelview(1, 2) );
257 Eigen::Vector3d
Camera::backTransformedZAxis() const
259 return Eigen::Vector3d( d
->modelview(2, 0),
261 d
->modelview(2, 2) );
264 Eigen::Vector3d
Camera::transformedXAxis() const
266 // The x unit vector in space coordinates
267 return Eigen::Vector3d( d
->modelview(0, 0),
269 d
->modelview(2, 0) );
272 Eigen::Vector3d
Camera::transformedYAxis() const
274 // The y unit vector in space coordinates
275 return Eigen::Vector3d( d
->modelview(0, 1),
277 d
->modelview(2, 1) );
280 Eigen::Vector3d
Camera::transformedZAxis() const
282 // The z unit vector in space coordinates
283 return Eigen::Vector3d( d
->modelview(0, 2),
285 d
->modelview(2, 2) );
288 } // end namespace Avogadro