1 /**********************************************************************
2 Cylinder - OpenGL Cylinder drawing class.
3
4 Copyright (C) 2007-2009 Jean Brefort <jean.brefort@normalesup.org>
5 Copyright (C) 2006,2007 Benoit Jacob <jacob@math.jussieu.fr>
6
7 This file is part of the Avogadro molecular editor project.
8 For more information, see <http://avogadro.sourceforge.net/>
9
10 Avogadro is free software; you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation; either version 3 of the License, or
13 (at your option) any later version.
14
15 Avogadro is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License for more details.
19
20 You should have received a copy of the GNU General Public License
21 along with this program; if not, write to the Free Software
22 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
23 02110-1301, USA.
24 **********************************************************************/
25
26 #include "config.h"
27 #include "cylinder.h"
28 #include "matrix.h"
29 #include"vector.h"
30 #include <GL/gl.h>
31 #include <cmath>
32
33 namespace gcu {
34
35 class CylinderPrivate {
36 public:
CylinderPrivate()37 CylinderPrivate () : vertexBuffer (0), normalBuffer (0), displayList (0), isValid (false) {}
38
39 /** Pointer to the buffer storing the vertex array */
40 Vector3f *vertexBuffer;
41 /** Pointer to the buffer storing the normal array */
42 Vector3f *normalBuffer;
43 /** The id of the OpenGL display list */
44 GLuint displayList;
45 /** Equals true if the vertex array has been correctly initialized */
46 bool isValid;
47
48 /** the number of faces of the cylinder. This only
49 * includes the lateral faces, as the base and top faces (the
50 * two discs) are not rendered. */
51 int faces;
52 };
53
Cylinder(int faces)54 Cylinder::Cylinder (int faces) : d (new CylinderPrivate)
55 {
56 setup (faces);
57 }
58
~Cylinder()59 Cylinder::~Cylinder ()
60 {
61 freeBuffers ();
62 if (d->displayList) {
63 glDeleteLists( d->displayList, 1 );
64 }
65 delete d;
66 }
67
freeBuffers()68 void Cylinder::freeBuffers ()
69 {
70 if(d->normalBuffer) {
71 delete[] d->normalBuffer;
72 d->normalBuffer = 0;
73 }
74 if (d->vertexBuffer) {
75 delete[] d->vertexBuffer;
76 d->vertexBuffer = 0;
77 }
78 }
79
setup(int faces)80 void Cylinder::setup (int faces)
81 {
82 if (d->isValid && faces == d->faces)
83 return;
84 d->faces = faces;
85 initialize ();
86 }
87
initialize()88 void Cylinder::initialize()
89 {
90 d->isValid = false;
91 if (d->faces < 0)
92 return;
93
94 // compile display list and free buffers
95 if (!d->displayList)
96 d->displayList = glGenLists (1);
97 if (!d->displayList)
98 return;
99
100 if (d->faces < 3) {
101 glNewList (d->displayList, GL_COMPILE);
102 glLineWidth (1.0);
103 glBegin (GL_LINES);
104 glVertex3f (0, 0, 0);
105 glVertex3f (0, 0, 1);
106 glEnd ();
107 glEndList ();
108 } else {
109 // compute number of vertices
110 int vertexCount = 2 * d->faces + 2;
111
112 // deallocate any previously allocated buffer
113 freeBuffers ();
114
115 // allocate memory for buffers
116 d->vertexBuffer = new Vector3f[vertexCount];
117 if (!d->vertexBuffer)
118 return;
119 d->normalBuffer = new Vector3f[vertexCount];
120 if (!d->normalBuffer)
121 return;
122
123 float baseAngle = 2 * M_PI / d->faces;
124 // build vertex and normal buffers
125 for (int i = 0; i <= d->faces; i++) {
126 float angle = baseAngle * i;
127 Vector3f v (cosf (angle), sinf (angle), 0.0f);
128 d->normalBuffer[2 * i] = v;
129 d->normalBuffer[2 * i + 1] = v;
130 d->vertexBuffer[2 * i] = v;
131 d->vertexBuffer[2 * i + 1] = v;
132 d->vertexBuffer[2 * i].GetRefz () = 1.0f;
133 }
134 glEnableClientState (GL_VERTEX_ARRAY);
135 glEnableClientState (GL_NORMAL_ARRAY);
136 glNewList (d->displayList, GL_COMPILE);
137 glVertexPointer (3, GL_FLOAT, 0, d->vertexBuffer);
138 glNormalPointer (GL_FLOAT, 0, d->normalBuffer);
139 glDrawArrays (GL_QUAD_STRIP, 0, vertexCount);
140 glEndList ();
141 glDisableClientState (GL_VERTEX_ARRAY);
142 glDisableClientState (GL_NORMAL_ARRAY);
143 }
144 freeBuffers ();
145 d->isValid = true;
146 }
147
draw(const Vector & end1,const Vector & end2,double radius) const148 void Cylinder::draw (const Vector &end1, const Vector &end2, double radius) const
149 {
150 // the "axis vector" of the cylinder
151 Vector axis = end2 - end1;
152
153 // construct an orthogonal basis whose first vector is axis, and whose other vectors
154 // have norm equal to 'radius'.
155 Vector axisNormalized = axis / axis.GetLength ();
156 Vector ortho1, ortho2;
157 ortho1 = axisNormalized.CreateOrthogonal ();
158 ortho1 *= radius;
159 ortho2 = axisNormalized.Cross (ortho1);
160
161 // construct the 4D transformation matrix
162 GLMatrix matrix;
163
164 matrix (0, 0) = ortho1.GetX ();
165 matrix (0, 1) = ortho1.GetY ();
166 matrix (0, 2) = ortho1.GetZ ();
167 matrix (0, 3) = 0.0;
168
169 matrix (1, 0) = ortho2.GetX ();
170 matrix (1, 1) = ortho2.GetY ();
171 matrix (1, 2) = ortho2.GetZ ();
172 matrix (1, 3) = 0.0;
173
174 matrix (2, 0) = axis.GetX ();
175 matrix (2, 1) = axis.GetY ();
176 matrix (2, 2) = axis.GetZ ();
177 matrix (2, 3) = 0.0;
178
179 matrix (3, 0) = end1.GetX ();
180 matrix (3, 1) = end1.GetY ();
181 matrix (3, 2) = end1.GetZ ();
182 matrix (3, 3) = 1.0;
183
184 //now we can do the actual drawing !
185 glPushMatrix ();
186 glMultMatrixd (matrix.array ());
187 glCallList (d->displayList);
188 glPopMatrix ();
189 }
190
drawMulti(const Vector & end1,const Vector & end2,double radius,int order,double shift,const Vector & planeNormalVector) const191 void Cylinder::drawMulti( const Vector &end1, const Vector &end2,
192 double radius, int order, double shift,
193 const Vector &planeNormalVector ) const
194 {
195 // the "axis vector" of the cylinder
196 Vector axis = end2 - end1;
197
198 // now we want to construct an orthonormal basis whose first
199 // vector is axis.normalized(). We don't use Eigen's loadOrthoBasis()
200 // for that, because we want one more thing. The second vector in this
201 // basis, which we call ortho1, should be approximately lying in the
202 // z=0 plane if possible. This is to ensure double bonds don't look
203 // like single bonds from the default point of view.
204 double axisNorm = axis.GetLength ();
205 if (axisNorm == 0.0)
206 return;
207 Vector axisNormalized = axis / axisNorm;
208
209 Vector ortho1 = axisNormalized.Cross (planeNormalVector);
210 double ortho1Norm = ortho1.GetLength ();
211 if (ortho1Norm > 0.001)
212 ortho1 /= ortho1Norm;
213 else
214 axisNormalized = ortho1.CreateOrthogonal ();
215 ortho1 *= radius;
216
217 Vector ortho2 = axisNormalized.Cross (ortho1);
218
219 // construct the 4D transformation matrix
220 GLMatrix matrix;
221
222 matrix (0, 0) = ortho1.GetX ();
223 matrix (0, 1) = ortho1.GetY ();
224 matrix (0, 2) = ortho1.GetZ ();
225 matrix (0, 3) = 0.0;
226
227 matrix (1, 0) = ortho2.GetX ();
228 matrix (1, 1) = ortho2.GetY ();
229 matrix (1, 2) = ortho2.GetZ ();
230 matrix (1, 3) = 0.0;
231
232 matrix (2, 0) = axis.GetX ();
233 matrix (2, 1) = axis.GetY ();
234 matrix (2, 2) = axis.GetZ ();
235 matrix (2, 3) = 0.0;
236
237 matrix (3, 0) = end1.GetX ();
238 matrix (3, 1) = end1.GetY ();
239 matrix (3, 2) = end1.GetZ ();
240 matrix (3, 3) = 1.0;
241
242 //now we can do the actual drawing !
243 glPushMatrix ();
244 glMultMatrixd (matrix.array ());
245 if (order == 1)
246 glCallList (d->displayList);
247 else {
248 double angleOffset = 0.0;
249 if (order >= 3) {
250 if (order == 3)
251 angleOffset = 90.0;
252 else angleOffset = 22.5;
253 }
254
255 double displacementFactor = shift / radius;
256 for (int i = 0; i < order; i++) {
257 glPushMatrix ();
258 glRotated (angleOffset + 360.0 * i / order, 0.0, 0.0, 1.0);
259 glTranslated (displacementFactor, 0.0, 0.0);
260 glCallList (d->displayList);
261 glPopMatrix ();
262 }
263 }
264 glPopMatrix ();
265 }
266
267 }
268