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