1 /**********************************************************************
2 Cylinder - OpenGL Cylinder drawing class.
4 Copyright (C) 2006,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 **********************************************************************/
28 using namespace Eigen
;
32 class CylinderPrivate
{
34 CylinderPrivate() : vertexBuffer(0), normalBuffer(0), displayList(0), isValid(false) {}
36 /** Pointer to the buffer storing the vertex array */
37 Eigen::Vector3f
*vertexBuffer
;
38 /** Pointer to the buffer storing the normal array */
39 Eigen::Vector3f
*normalBuffer
;
40 /** The id of the OpenGL display list */
42 /** Equals true if the vertex array has been correctly initialized */
45 /** the number of faces of the cylinder. This only
46 * includes the lateral faces, as the base and top faces (the
47 * two discs) are not rendered. */
51 Cylinder::Cylinder(int faces
) : d(new CylinderPrivate
)
59 if( d
->displayList
) {
60 glDeleteLists( d
->displayList
, 1 );
65 void Cylinder::freeBuffers()
69 delete [] d
->normalBuffer
;
74 delete [] d
->vertexBuffer
;
79 void Cylinder::setup( int faces
)
81 if( d
->isValid
&& faces
== d
->faces
) return;
86 void Cylinder::initialize()
89 if( d
->faces
< 0 ) return;
91 // compile display list and free buffers
92 if( ! d
->displayList
) d
->displayList
= glGenLists( 1 );
93 if( ! d
->displayList
) return;
97 glNewList( d
->displayList
, GL_COMPILE
);
107 // compute number of vertices
108 int vertexCount
= 2 * d
->faces
+ 2;
110 // deallocate any previously allocated buffer
113 // allocate memory for buffers
114 d
->vertexBuffer
= new Vector3f
[vertexCount
];
115 if( ! d
->vertexBuffer
) return;
116 d
->normalBuffer
= new Vector3f
[vertexCount
];
117 if( ! d
->normalBuffer
) return;
119 float baseAngle
= 2 * M_PI
/ d
->faces
;
120 // build vertex and normal buffers
121 for( int i
= 0; i
<= d
->faces
; i
++ )
123 float angle
= baseAngle
* i
;
124 Vector3f
v( cosf(angle
), sinf(angle
), 0.0f
);
125 d
->normalBuffer
[ 2 * i
] = v
;
126 d
->normalBuffer
[ 2 * i
+ 1 ] = v
;
127 d
->vertexBuffer
[ 2 * i
] = v
;
128 d
->vertexBuffer
[ 2 * i
+ 1 ] = v
;
129 d
->vertexBuffer
[ 2 * i
].z() = 1.0f
;
131 glEnableClientState( GL_VERTEX_ARRAY
);
132 glEnableClientState( GL_NORMAL_ARRAY
);
133 glNewList( d
->displayList
, GL_COMPILE
);
134 glVertexPointer( 3, GL_FLOAT
, 0, d
->vertexBuffer
);
135 glNormalPointer( GL_FLOAT
, 0, d
->normalBuffer
);
136 glDrawArrays( GL_QUAD_STRIP
, 0, vertexCount
);
138 glDisableClientState( GL_VERTEX_ARRAY
);
139 glDisableClientState( GL_NORMAL_ARRAY
);
145 void Cylinder::draw( const Vector3d
&end1
, const Vector3d
&end2
,
146 double radius
) const
148 // the "axis vector" of the cylinder
149 Vector3d axis
= end2
- end1
;
151 // construct an orthogonal basis whose first vector is axis, and whose other vectors
152 // have norm equal to 'radius'.
153 Vector3d axisNormalized
= axis
.normalized();
154 Vector3d ortho1
, ortho2
;
155 ortho1
.loadOrtho(axisNormalized
);
157 axisNormalized
.cross( ortho1
, &ortho2
);
159 // construct the 4D transformation matrix
162 matrix(0, 0) = ortho1(0);
163 matrix(1, 0) = ortho1(1);
164 matrix(2, 0) = ortho1(2);
167 matrix(0, 1) = ortho2(0);
168 matrix(1, 1) = ortho2(1);
169 matrix(2, 1) = ortho2(2);
172 matrix(0, 2) = axis(0);
173 matrix(1, 2) = axis(1);
174 matrix(2, 2) = axis(2);
177 matrix(0, 3) = end1(0);
178 matrix(1, 3) = end1(1);
179 matrix(2, 3) = end1(2);
182 //now we can do the actual drawing !
184 glMultMatrixd( matrix
.array() );
185 glCallList( d
->displayList
);
189 void Cylinder::drawMulti( const Vector3d
&end1
, const Vector3d
&end2
,
190 double radius
, int order
, double shift
,
191 const Vector3d
&planeNormalVector
) const
194 // the "axis vector" of the cylinder
195 Vector3d axis
= end2
- end1
;
197 // now we want to construct an orthonormal basis whose first
198 // vector is axis.normalized(). We don't use Eigen's loadOrthoBasis()
199 // for that, because we want one more thing. The second vector in this
200 // basis, which we call ortho1, should be approximately lying in the
201 // z=0 plane if possible. This is to ensure double bonds don't look
202 // like single bonds from the default point of view.
203 double axisNorm
= axis
.norm();
204 if( axisNorm
== 0.0 ) return;
205 Vector3d axisNormalized
= axis
/ axisNorm
;
207 Vector3d ortho1
= axisNormalized
.cross(planeNormalVector
);
208 double ortho1Norm
= ortho1
.norm();
209 if( ortho1Norm
> 0.001 ) ortho1
/= ortho1Norm
;
210 else ortho1
= axisNormalized
.ortho();
213 Vector3d ortho2
= cross( axisNormalized
, ortho1
);
215 // construct the 4D transformation matrix
218 matrix(0, 0) = ortho1(0);
219 matrix(1, 0) = ortho1(1);
220 matrix(2, 0) = ortho1(2);
223 matrix(0, 1) = ortho2(0);
224 matrix(1, 1) = ortho2(1);
225 matrix(2, 1) = ortho2(2);
228 matrix(0, 2) = axis(0);
229 matrix(1, 2) = axis(1);
230 matrix(2, 2) = axis(2);
233 matrix(0, 3) = end1(0);
234 matrix(1, 3) = end1(1);
235 matrix(2, 3) = end1(2);
238 //now we can do the actual drawing !
240 glMultMatrixd( matrix
.array() );
242 glCallList( d
->displayList
);
245 double angleOffset
= 0.0;
248 if( order
== 3 ) angleOffset
= 90.0;
249 else angleOffset
= 22.5;
252 double displacementFactor
= shift
/ radius
;
253 for( int i
= 0; i
< order
; i
++)
256 glRotated( angleOffset
+ 360.0 * i
/ order
,
258 glTranslated( displacementFactor
, 0.0, 0.0 );
259 glCallList( d
->displayList
);