1 /**********************************************************************
2 POVPainter - drawing spheres, cylinders and text in a POVRay scene
4 Copyright (C) 2007 Marcus D. Hanwell
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 **********************************************************************/
25 #include "povpainter.h"
32 class POVPainterPrivate
35 POVPainterPrivate() : pd (0), initialized (false), sharing(0),
36 color(0), output(0), planeNormalVector(0., 0., 0.) { color
.set(0., 0., 0., 0.); }
45 inline bool isValid();
48 * Painters can be shared, we must keep track of this.
54 Vector3d planeNormalVector
;
58 POVPainter::POVPainter() : d (new POVPainterPrivate
)
63 POVPainter::~POVPainter()
68 void POVPainter::setColor (const Color
*color
)
73 void POVPainter::setColor (float red
, float green
, float blue
, float alpha
)
75 d
->color
= Color(red
, green
, blue
, alpha
);
78 void POVPainter::setPlaneNormal (Vector3d planeNormalVector
)
80 // Set the plane normal, multiCylinders are drawn relative to this
81 d
->planeNormalVector
= planeNormalVector
;
84 void POVPainter::drawSphere (const Vector3d
& center
, double radius
)
86 // Write out a POVRay sphere for rendering
87 *(d
->output
) << "sphere {\n"
88 << "\t<" << center
.x() << ", " << center
.y() << ", " << center
.z() << ">, " << radius
89 << "\n\tpigment { rgbf <" << d
->color
.red() << ", " << d
->color
.green() << ", "
90 << d
->color
.blue() << ", " << 1.0 - d
->color
.alpha() << "> }\n}\n";
93 void POVPainter::drawCylinder (const Vector3d
&end1
, const Vector3d
&end2
,
96 // Write out a POVRay cylinder for rendering
97 *(d
->output
) << "cylinder {\n"
98 << "\t<" << end1
.x() << ", " << end1
.y() << ", " << end1
.z() << ">, "
99 << "\t<" << end2
.x() << ", " << end2
.y() << ", " << end2
.z() << ">, " << radius
100 << "\n\tpigment { rgbf <" << d
->color
.red() << ", " << d
->color
.green() << ", "
101 << d
->color
.blue() << ", " << 1.0 - d
->color
.alpha() << "> }\n}\n";
104 void POVPainter::drawMultiCylinder (const Vector3d
&end1
, const Vector3d
&end2
,
105 double radius
, int order
, double)
107 // Just render single bonds with the standard drawCylinder function
110 drawCylinder(end1
, end2
, radius
);
114 // Find the bond axis
115 Vector3d axis
= end2
- end1
;
116 double axisNorm
= axis
.norm();
117 if( axisNorm
== 0.0 ) return;
118 Vector3d axisNormalized
= axis
/ axisNorm
;
120 // Use the plane normal vector for the molecule to draw multicylinders along
121 Vector3d ortho1
= axisNormalized
.cross(d
->planeNormalVector
);
122 double ortho1Norm
= ortho1
.norm();
123 if( ortho1Norm
> 0.001 ) ortho1
/= ortho1Norm
;
124 else ortho1
= axisNormalized
.ortho();
125 // This number seems to work well for drawing the multiCylinder inside
126 ortho1
*= radius
*1.5;
127 Vector3d ortho2
= cross( axisNormalized
, ortho1
);
128 // Use an angle offset of zero for double bonds, 90 for triple and 22.5 for higher order
129 double angleOffset
= 0.0;
132 if( order
== 3 ) angleOffset
= 90.0;
133 else angleOffset
= 22.5;
135 // Actually draw the cylinders
136 for( int i
= 0; i
< order
; i
++)
138 double alpha
= angleOffset
/ 180.0 * M_PI
+ 2.0 * M_PI
* i
/ order
;
139 Vector3d displacement
= cos(alpha
) * ortho1
+ sin(alpha
) * ortho2
;
140 Vector3d displacedEnd1
= end1
+ displacement
;
141 Vector3d displacedEnd2
= end2
+ displacement
;
142 // Write out a POVRay cylinder for rendering
143 *(d
->output
) << "cylinder {\n"
144 << "\t<" << displacedEnd1
.x() << ", "
145 << displacedEnd1
.y() << ", "
146 << displacedEnd1
.z() << ">, "
147 << "\t<" << displacedEnd2
.x() << ", "
148 << displacedEnd2
.y() << ", "
149 << displacedEnd2
.z() << ">, " << radius
150 << "\n\tpigment { rgbf <" << d
->color
.red() << ", " << d
->color
.green() << ", "
151 << d
->color
.blue() << ", " << 1.0 - d
->color
.alpha() << "> }\n}\n";
156 void POVPainter::drawShadedSector(Eigen::Vector3d
, Eigen::Vector3d
,
157 Eigen::Vector3d
, double, bool)
161 void POVPainter::drawArc(Eigen::Vector3d
, Eigen::Vector3d
, Eigen::Vector3d
,
162 double, double, bool)
166 void POVPainter::drawShadedQuadrilateral(Eigen::Vector3d
, Eigen::Vector3d
,
167 Eigen::Vector3d
, Eigen::Vector3d
)
171 void POVPainter::drawQuadrilateral(Eigen::Vector3d
, Eigen::Vector3d
,
172 Eigen::Vector3d
, Eigen::Vector3d
,
177 int POVPainter::drawText (int, int, const QString
&) const
182 int POVPainter::drawText (const QPoint
&, const QString
&) const
187 int POVPainter::drawText (const Vector3d
&, const QString
&) const
192 void POVPainter::begin(QTextStream
*output
, Vector3d planeNormalVector
)
195 d
->planeNormalVector
= planeNormalVector
;
198 void POVPainter::end()
203 POVPainterDevice::POVPainterDevice(const QString
& filename
, const GLWidget
* glwidget
)
205 m_glwidget
= glwidget
;
206 m_painter
= new POVPainter
;
207 m_file
= new QFile(filename
);
208 if (!m_file
->open(QIODevice::WriteOnly
| QIODevice::Text
))
210 m_output
= new QTextStream(m_file
);
211 m_painter
->begin(m_output
, m_glwidget
->normalVector());
213 m_engines
= m_glwidget
->engines();
219 POVPainterDevice::~POVPainterDevice()
228 void POVPainterDevice::initializePOV()
230 // Initialise our POV-Ray scene
231 // The POV-Ray camera basically has the same matrix elements - we just need to translate
232 // FIXME Still working on getting the translation to POV-Ray right...
233 Vector3d cameraT
= m_glwidget
->camera()->modelview().translationVector();
234 Vector3d cameraX
= m_glwidget
->camera()->backTransformedXAxis();
235 Vector3d cameraY
= m_glwidget
->camera()->backTransformedYAxis();
236 Vector3d cameraZ
= m_glwidget
->camera()->backTransformedZAxis();
237 Vector3d light
= cameraT
+ Vector3d(0.8, 0.7, 1.0);
239 // Output the POV-Ray initialisation code
240 *(m_output
) << "global_settings {\n"
241 << "\tambient_light rgb <1,1,1>\n"
242 << "\tmax_trace_level 20\n}\n\n"
243 << "background { color rgb <1,1,1> }\n\n"
246 << "\tlocation <" << cameraT
.x() << ", " << cameraT
.y() << ", " << cameraT
.z() << ">\n"
247 // << "\tright 1.33 * <" << cameraX.x() << ", " << cameraX.y() << ", " << cameraX.z() << ">\n"
248 // << "\tup <" << cameraY.x() << ", " << cameraY.y() << ", " << cameraY.z() << ">\n"
249 // << "\tdirection <" << cameraZ.x() << ", " << cameraZ.y() << ", " << cameraZ.z() << ">\n"
250 << "\tlook_at <0, 0, 0>\n"
251 // << "\tsky y\n}\n\n"
252 << "light_source {\n"
253 << "\t<" << light
.x() << ", " << light
.y() << ", " << light
.z() << ">\n"
254 << "\tcolor rgb<1,1,1>\n"
256 << "#default {\n\tfinish {ambient .4 diffuse .6 specular 0.9 roughness .005 metallic}\n}";
259 void POVPainterDevice::render()
261 // Now render the scene using the active engines
262 foreach( Engine
*engine
, m_engines
) {
263 if ( engine
->isEnabled() )
264 engine
->renderOpaque(this);
265 if ( engine
->isEnabled() && engine
->flags() & Engine::Transparent
)
266 engine
->renderTransparent(this);
271 } // End namespace Avogadro