| [c67518] | 1 | /*
 | 
|---|
 | 2 |  * Project: MoleCuilder
 | 
|---|
 | 3 |  * Description: creates and alters molecular systems
 | 
|---|
 | 4 |  * Copyright (C)  2010-2012 University of Bonn. All rights reserved.
 | 
|---|
| [5aaa43] | 5 |  * Copyright (C)  2013 Frederik Heber. All rights reserved.
 | 
|---|
| [94d5ac6] | 6 |  * 
 | 
|---|
 | 7 |  *
 | 
|---|
 | 8 |  *   This file is part of MoleCuilder.
 | 
|---|
 | 9 |  *
 | 
|---|
 | 10 |  *    MoleCuilder 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 2 of the License, or
 | 
|---|
 | 13 |  *    (at your option) any later version.
 | 
|---|
 | 14 |  *
 | 
|---|
 | 15 |  *    MoleCuilder 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 MoleCuilder.  If not, see <http://www.gnu.org/licenses/>.
 | 
|---|
| [c67518] | 22 |  */
 | 
|---|
 | 23 | 
 | 
|---|
 | 24 | /*
 | 
|---|
 | 25 |  * GLMoleculeObject_molecule.cpp
 | 
|---|
 | 26 |  *
 | 
|---|
 | 27 |  *  Created on: Mar 30, 2012
 | 
|---|
 | 28 |  *      Author: ankele
 | 
|---|
 | 29 |  */
 | 
|---|
 | 30 | 
 | 
|---|
 | 31 | 
 | 
|---|
 | 32 | // include config.h
 | 
|---|
 | 33 | #ifdef HAVE_CONFIG_H
 | 
|---|
 | 34 | #include <config.h>
 | 
|---|
 | 35 | #endif
 | 
|---|
 | 36 | 
 | 
|---|
 | 37 | #include "GLMoleculeObject_molecule.hpp"
 | 
|---|
 | 38 | 
 | 
|---|
 | 39 | #include <Qt3D/qglscenenode.h>
 | 
|---|
| [34e7fdb] | 40 | #include <Qt3D/qglbuilder.h>
 | 
|---|
| [c67518] | 41 | 
 | 
|---|
| [494478] | 42 | #include "UIElements/Views/Qt4/Qt3D/GLMoleculeObject_atom.hpp"
 | 
|---|
 | 43 | 
 | 
|---|
| [c67518] | 44 | #include "CodePatterns/MemDebug.hpp"
 | 
|---|
 | 45 | 
 | 
|---|
 | 46 | #include "CodePatterns/Assert.hpp"
 | 
|---|
| [8d5fbf1] | 47 | #include "CodePatterns/IteratorAdaptors.hpp"
 | 
|---|
| [c67518] | 48 | #include "CodePatterns/Log.hpp"
 | 
|---|
| [494478] | 49 | 
 | 
|---|
| [c67518] | 50 | #include "LinearAlgebra/Vector.hpp"
 | 
|---|
| [34e7fdb] | 51 | #include "LinkedCell/PointCloudAdaptor.hpp"
 | 
|---|
 | 52 | #include "LinkedCell/linkedcell.hpp"
 | 
|---|
 | 53 | #include "Tesselation/tesselation.hpp"
 | 
|---|
 | 54 | #include "Tesselation/BoundaryLineSet.hpp"
 | 
|---|
 | 55 | #include "Tesselation/BoundaryTriangleSet.hpp"
 | 
|---|
 | 56 | #include "Tesselation/CandidateForTesselation.hpp"
 | 
|---|
| [2f7988] | 57 | #include "UIElements/Qt4/InstanceBoard/QtObservedInstanceBoard.hpp"
 | 
|---|
| [34e7fdb] | 58 | #include "Atom/TesselPoint.hpp"
 | 
|---|
| [c67518] | 59 | #include "World.hpp"
 | 
|---|
 | 60 | 
 | 
|---|
| [6c16a0] | 61 | static QGLSceneNode *createMoleculeMesh(const QGeometryData &_geo)
 | 
|---|
 | 62 | {
 | 
|---|
| [34e7fdb] | 63 |   // Build a mesh from the geometry.
 | 
|---|
 | 64 |   QGLBuilder builder;
 | 
|---|
| [6c16a0] | 65 |   builder.addTriangles(_geo);
 | 
|---|
| [34e7fdb] | 66 |   QGLSceneNode *mesh = builder.finalizedSceneNode();
 | 
|---|
 | 67 |   return mesh;
 | 
|---|
 | 68 | }
 | 
|---|
 | 69 | 
 | 
|---|
| [8d5fbf1] | 70 | GLMoleculeObject_molecule::GLMoleculeObject_molecule(
 | 
|---|
 | 71 |     QObject *parent,
 | 
|---|
| [1b07b1] | 72 |     QtObservedMolecule::ptr &_ObservedMolecule) :
 | 
|---|
| [6c16a0] | 73 |   GLMoleculeObject((QGLSceneNode *)NULL, parent),
 | 
|---|
| [8d5fbf1] | 74 |   hoverAtomId(-1),
 | 
|---|
| [494478] | 75 |   ObservedMolecule(_ObservedMolecule)
 | 
|---|
| [34e7fdb] | 76 | {
 | 
|---|
| [96f14a] | 77 |   init();
 | 
|---|
| [34e7fdb] | 78 | }
 | 
|---|
 | 79 | 
 | 
|---|
| [026bef] | 80 | GLMoleculeObject_molecule::GLMoleculeObject_molecule(
 | 
|---|
 | 81 |     QGLSceneNode *mesh[],
 | 
|---|
 | 82 |     QObject *parent,
 | 
|---|
| [1b07b1] | 83 |     QtObservedMolecule::ptr &_ObservedMolecule) :
 | 
|---|
| [bca99d] | 84 |   GLMoleculeObject(mesh, parent),
 | 
|---|
| [691533] | 85 |   TesselationUptodate(false),
 | 
|---|
| [8d5fbf1] | 86 |   hoverAtomId(-1),
 | 
|---|
| [494478] | 87 |   ObservedMolecule(_ObservedMolecule)
 | 
|---|
| [c67518] | 88 | {
 | 
|---|
| [96f14a] | 89 |   init();
 | 
|---|
| [8d5fbf1] | 90 | }
 | 
|---|
| [7c7c4a] | 91 | 
 | 
|---|
| [96f14a] | 92 | void GLMoleculeObject_molecule::init()
 | 
|---|
| [8d5fbf1] | 93 | {
 | 
|---|
| [96f14a] | 94 |   setObjectId(ObservedMolecule->getMolIndex());
 | 
|---|
| [3b229e] | 95 |   setMaterial(getMaterial(1));
 | 
|---|
| [8c001a] | 96 | 
 | 
|---|
| [96f14a] | 97 |   m_selected = ObservedMolecule->getMolSelected();
 | 
|---|
| [a39d72] | 98 | 
 | 
|---|
| [739ee9] | 99 |   // initially, atoms and bonds should be visible
 | 
|---|
 | 100 |   m_visible = false;
 | 
|---|
 | 101 | 
 | 
|---|
| [691533] | 102 |   connect (ObservedMolecule.get(), SIGNAL(tesselationhullChanged()), this, SLOT(setTesselationOutOfDate()));
 | 
|---|
| [494478] | 103 |   connect (ObservedMolecule.get(), SIGNAL(boundingboxChanged()), this, SLOT(resetBoundingBox()));
 | 
|---|
| [1c0961] | 104 |   connect (ObservedMolecule.get(), SIGNAL(indexChanged(const moleculeId_t, const moleculeId_t)),
 | 
|---|
 | 105 |       this, SLOT(resetIndex(const moleculeId_t, const moleculeId_t)));
 | 
|---|
| [59f1bc] | 106 |   /// these are channeled through GLWorldScene instead to ensure synchronicity
 | 
|---|
 | 107 | //  connect (ObservedMolecule.get(), SIGNAL(atomInserted(QtObservedAtom::ptr)),
 | 
|---|
 | 108 | //      this, SLOT(atomInserted(QtObservedAtom::ptr)) );
 | 
|---|
 | 109 | //  connect (ObservedMolecule.get(), SIGNAL(atomRemoved(const atomId_t)),
 | 
|---|
 | 110 | //      this, SLOT(atomRemoved(const atomId_t)) );
 | 
|---|
| [5cd3a33] | 111 |   connect (ObservedMolecule.get(), SIGNAL(selectedChanged()), this, SLOT(resetSelected()));
 | 
|---|
| [c67518] | 112 | }
 | 
|---|
 | 113 | 
 | 
|---|
 | 114 | GLMoleculeObject_molecule::~GLMoleculeObject_molecule()
 | 
|---|
| [494478] | 115 | {}
 | 
|---|
| [73b13c] | 116 | 
 | 
|---|
| [6c16a0] | 117 | QGeometryData GLMoleculeObject_molecule::updateTesselationHull() const
 | 
|---|
 | 118 | {
 | 
|---|
 | 119 |   QGeometryData geo;
 | 
|---|
 | 120 | 
 | 
|---|
| [494478] | 121 |   const molecule * const molref =
 | 
|---|
 | 122 |       QtObservedMolecule::getMolecule(ObservedMolecule->getMolIndex());
 | 
|---|
| [6c16a0] | 123 |   if (molref == NULL) {
 | 
|---|
| [494478] | 124 |     ELOG(1, "Could not createMoleculeMesh, molecule with id "
 | 
|---|
 | 125 |         << ObservedMolecule->getMolIndex() << " already gone.");
 | 
|---|
| [6c16a0] | 126 |     return geo;
 | 
|---|
 | 127 |   }
 | 
|---|
 | 128 |   double minradius = 2.; // TODO: set to maximum bond length value
 | 
|---|
 | 129 |   LOG(3, "DEBUG: Molecule fits into sphere of radius " << minradius);
 | 
|---|
 | 130 |   // check minimum bond radius in molecule
 | 
|---|
 | 131 |   double minlength = std::numeric_limits<double>::max();
 | 
|---|
| [f1b5ca] | 132 |   size_t NoAtoms = 0;
 | 
|---|
| [6c16a0] | 133 |   for (molecule::const_iterator iter = molref->begin();
 | 
|---|
 | 134 |       iter != molref->end(); ++iter) {
 | 
|---|
 | 135 |     const BondList &ListOfBonds = (*iter)->getListOfBonds();
 | 
|---|
 | 136 |     for (BondList::const_iterator bonditer = ListOfBonds.begin();
 | 
|---|
 | 137 |          bonditer != ListOfBonds.end(); ++bonditer) {
 | 
|---|
 | 138 |       const double bond_distance = (*bonditer)->GetDistance();
 | 
|---|
 | 139 |       minlength = std::min(bond_distance, minlength);
 | 
|---|
 | 140 |     }
 | 
|---|
| [f1b5ca] | 141 |     ++NoAtoms;
 | 
|---|
| [6c16a0] | 142 |   }
 | 
|---|
 | 143 |   minradius = std::max( std::max(minradius, minlength), 1.);
 | 
|---|
 | 144 | 
 | 
|---|
 | 145 |   // we need at least three points for tesselation
 | 
|---|
| [f1b5ca] | 146 |   if (NoAtoms >= 3) {
 | 
|---|
| [6c16a0] | 147 |     // Tesselate the points.
 | 
|---|
 | 148 |     Tesselation T;
 | 
|---|
| [494478] | 149 |     PointCloudAdaptor<molecule> cloud(
 | 
|---|
 | 150 |         const_cast<molecule *>(molref),
 | 
|---|
 | 151 |         ObservedMolecule->getMolName());
 | 
|---|
| [6c16a0] | 152 |     T(cloud, minradius);
 | 
|---|
 | 153 | 
 | 
|---|
 | 154 |     // Fill the points into a Qt geometry.
 | 
|---|
 | 155 |     LinkedCell_deprecated LinkedList(cloud, minradius);
 | 
|---|
 | 156 |     std::map<int, int> indices;
 | 
|---|
 | 157 |     std::map<int, Vector> normals;
 | 
|---|
 | 158 |     int index = 0;
 | 
|---|
 | 159 |     for (PointMap::const_iterator piter = T.PointsOnBoundary.begin();
 | 
|---|
 | 160 |         piter != T.PointsOnBoundary.end(); ++piter) {
 | 
|---|
 | 161 |       const Vector &point = piter->second->getPosition();
 | 
|---|
 | 162 |       // add data to the primitive
 | 
|---|
 | 163 |       geo.appendVertex(QVector3D(point[0], point[1], point[2]));
 | 
|---|
 | 164 |       Vector normalvector;
 | 
|---|
 | 165 |       for (LineMap::const_iterator lineiter = piter->second->lines.begin();
 | 
|---|
 | 166 |           lineiter != piter->second->lines.end(); ++lineiter)
 | 
|---|
 | 167 |         for (TriangleMap::const_iterator triangleiter = lineiter->second->triangles.begin();
 | 
|---|
 | 168 |             triangleiter != lineiter->second->triangles.end(); ++triangleiter)
 | 
|---|
 | 169 |           normalvector +=
 | 
|---|
 | 170 |               triangleiter->second->NormalVector;
 | 
|---|
 | 171 |       normalvector.Normalize();
 | 
|---|
 | 172 |       geo.appendNormal(QVector3D(normalvector[0], normalvector[1], normalvector[2]));
 | 
|---|
 | 173 |       geo.appendColor(QColor(1, 1, 1, 1));
 | 
|---|
 | 174 |       geo.appendTexCoord(QVector2D(0, 0));
 | 
|---|
 | 175 |       indices.insert( std::make_pair( piter->second->getNr(), index++));
 | 
|---|
 | 176 |     }
 | 
|---|
 | 177 | 
 | 
|---|
 | 178 |     // Fill the tesselated triangles into the geometry.
 | 
|---|
 | 179 |     for (TriangleMap::const_iterator runner = T.TrianglesOnBoundary.begin();
 | 
|---|
 | 180 |         runner != T.TrianglesOnBoundary.end(); runner++) {
 | 
|---|
 | 181 |       int v[3];
 | 
|---|
 | 182 |       for (size_t i=0; i<3; ++i)
 | 
|---|
 | 183 |         v[i] = runner->second->endpoints[i]->getNr();
 | 
|---|
 | 184 | 
 | 
|---|
 | 185 |       // Sort the vertices so the triangle is clockwise (relative to the normal vector).
 | 
|---|
 | 186 |       Vector cross = T.PointsOnBoundary[v[1]]->getPosition() - T.PointsOnBoundary[v[0]]->getPosition();
 | 
|---|
 | 187 |       cross.VectorProduct(T.PointsOnBoundary[v[2]]->getPosition() - T.PointsOnBoundary[v[0]]->getPosition());
 | 
|---|
 | 188 |       if (cross.ScalarProduct(runner->second->NormalVector) > 0)
 | 
|---|
 | 189 |         geo.appendIndices(indices[v[0]], indices[v[1]], indices[v[2]]);
 | 
|---|
 | 190 |       else
 | 
|---|
 | 191 |         geo.appendIndices(indices[v[0]], indices[v[2]], indices[v[1]]);
 | 
|---|
 | 192 |     }
 | 
|---|
 | 193 |   }
 | 
|---|
 | 194 | 
 | 
|---|
 | 195 |   return geo;
 | 
|---|
 | 196 | }
 | 
|---|
 | 197 | 
 | 
|---|
| [691533] | 198 | void GLMoleculeObject_molecule::setTesselationOutOfDate()
 | 
|---|
 | 199 | {
 | 
|---|
 | 200 |   TesselationUptodate = false;
 | 
|---|
 | 201 | }
 | 
|---|
 | 202 | 
 | 
|---|
| [6c16a0] | 203 | void GLMoleculeObject_molecule::resetTesselationHull()
 | 
|---|
 | 204 | {
 | 
|---|
| [691533] | 205 |   if (!TesselationUptodate) {
 | 
|---|
 | 206 |     TesselationHull = updateTesselationHull();
 | 
|---|
 | 207 |     updateMesh(createMoleculeMesh(TesselationHull));
 | 
|---|
 | 208 |     TesselationUptodate = true;
 | 
|---|
 | 209 |   }
 | 
|---|
| [6c16a0] | 210 | }
 | 
|---|
 | 211 | 
 | 
|---|
 | 212 | void GLMoleculeObject_molecule::resetBoundingBox()
 | 
|---|
 | 213 | {
 | 
|---|
| [494478] | 214 |   molecule::BoundingBoxInfo info = ObservedMolecule->getBoundingBox();
 | 
|---|
| [6c16a0] | 215 |   setPosition(QVector3D(info.position[0], info.position[1], info.position[2]));
 | 
|---|
 | 216 |   setScale(info.radius + 0.3); // getBoundingSphere() only sees atoms as points, so make the box a bit bigger
 | 
|---|
 | 217 | }
 | 
|---|
 | 218 | 
 | 
|---|
| [1c0961] | 219 | void GLMoleculeObject_molecule::resetIndex(const moleculeId_t, const moleculeId_t)
 | 
|---|
| [6c16a0] | 220 | {
 | 
|---|
| [494478] | 221 |   const atomId_t newId = ObservedMolecule->getMolIndex();
 | 
|---|
| [6c16a0] | 222 |   const size_t oldId = objectId();
 | 
|---|
 | 223 |   ASSERT( newId != oldId,
 | 
|---|
 | 224 |       "GLMoleculeObject_molecule::resetIndex() - index "+toString(newId)+" did not change.");
 | 
|---|
 | 225 |   LOG(4, "INFO: GLMoleculeObject_molecule: new index is "+toString(newId)+".");
 | 
|---|
 | 226 |   setObjectId(newId);
 | 
|---|
| [d6203a] | 227 | }
 | 
|---|
 | 228 | 
 | 
|---|
| [5cd3a33] | 229 | void GLMoleculeObject_molecule::resetSelected()
 | 
|---|
| [015f8c] | 230 | {
 | 
|---|
| [5cd3a33] | 231 |   const bool new_selected = ObservedMolecule->getMolSelected();
 | 
|---|
 | 232 |   m_selected = new_selected;
 | 
|---|
| [015f8c] | 233 | 
 | 
|---|
 | 234 |   emit changed();
 | 
|---|
 | 235 | }
 | 
|---|
 | 236 | 
 | 
|---|
| [8c001a] | 237 | void GLMoleculeObject_molecule::initialize(QGLView *view, QGLPainter *painter)
 | 
|---|
 | 238 | {
 | 
|---|
 | 239 |   // Initialize all of the mesh objects that we have as children.
 | 
|---|
| [2b596f] | 240 |   if (m_visible) {
 | 
|---|
 | 241 |     GLMoleculeObject::initialize(view, painter);
 | 
|---|
 | 242 |   } else {
 | 
|---|
| [8c001a] | 243 |    foreach (QObject *obj, children()) {
 | 
|---|
 | 244 |      GLMoleculeObject *meshobj = qobject_cast<GLMoleculeObject *>(obj);
 | 
|---|
 | 245 |        if (meshobj)
 | 
|---|
 | 246 |          meshobj->initialize(view, painter);
 | 
|---|
 | 247 |    }
 | 
|---|
| [2b596f] | 248 |   }
 | 
|---|
| [8c001a] | 249 | }
 | 
|---|
 | 250 | 
 | 
|---|
 | 251 | void GLMoleculeObject_molecule::draw(QGLPainter *painter, const QVector4D &cameraPlane)
 | 
|---|
 | 252 | {
 | 
|---|
| [739ee9] | 253 |   // draw either molecule's mesh or all atoms and bonds
 | 
|---|
 | 254 |   if (m_visible) {
 | 
|---|
| [6c16a0] | 255 |     resetTesselationHull();
 | 
|---|
| [7b5984] | 256 | 
 | 
|---|
| [34e7fdb] | 257 |     painter->modelViewMatrix().push();
 | 
|---|
 | 258 | 
 | 
|---|
 | 259 |     // Apply the material and effect to the painter.
 | 
|---|
 | 260 |     QGLMaterial *material;
 | 
|---|
 | 261 |     if (m_hovering)
 | 
|---|
 | 262 |         material = m_hoverMaterial;
 | 
|---|
 | 263 |     else if (m_selected)
 | 
|---|
 | 264 |         material = m_selectionMaterial;
 | 
|---|
 | 265 |     else
 | 
|---|
 | 266 |         material = m_material;
 | 
|---|
 | 267 | 
 | 
|---|
 | 268 |     ASSERT(material, "GLMoleculeObject::draw: chosen material is NULL");
 | 
|---|
 | 269 | 
 | 
|---|
 | 270 |     painter->setColor(material->diffuseColor());
 | 
|---|
 | 271 |     painter->setFaceMaterial(QGL::AllFaces, material);
 | 
|---|
 | 272 |     if (m_effect)
 | 
|---|
 | 273 |         painter->setUserEffect(m_effect);
 | 
|---|
 | 274 |     else
 | 
|---|
 | 275 |         painter->setStandardEffect(QGL::LitMaterial);
 | 
|---|
 | 276 | 
 | 
|---|
 | 277 |     // Mark the object for object picking purposes.
 | 
|---|
 | 278 |     int prevObjectId = painter->objectPickId();
 | 
|---|
 | 279 |     if (m_objectId != -1)
 | 
|---|
 | 280 |         painter->setObjectPickId(m_objectId);
 | 
|---|
 | 281 | 
 | 
|---|
 | 282 |     m_mesh[0]->draw(painter);
 | 
|---|
 | 283 | 
 | 
|---|
 | 284 |     // Turn off the user effect, if present.
 | 
|---|
 | 285 |     if (m_effect)
 | 
|---|
 | 286 |         painter->setStandardEffect(QGL::LitMaterial);
 | 
|---|
 | 287 | 
 | 
|---|
 | 288 |     // Revert to the previous object identifier.
 | 
|---|
 | 289 |     painter->setObjectPickId(prevObjectId);
 | 
|---|
 | 290 | 
 | 
|---|
 | 291 |     // Restore the modelview matrix.
 | 
|---|
 | 292 |     painter->modelViewMatrix().pop();
 | 
|---|
 | 293 | 
 | 
|---|
 | 294 |     //    GLMoleculeObject::draw(painter, cameraPlane);
 | 
|---|
| [739ee9] | 295 |   } else {
 | 
|---|
 | 296 |     // Draw all of the mesh objects that we have as children.
 | 
|---|
 | 297 |     foreach (QObject *obj, children()) {
 | 
|---|
 | 298 |       GLMoleculeObject *meshobj = qobject_cast<GLMoleculeObject *>(obj);
 | 
|---|
 | 299 |       if (meshobj)
 | 
|---|
 | 300 |         meshobj->draw(painter, cameraPlane);
 | 
|---|
 | 301 |     }
 | 
|---|
| [2b596f] | 302 | 
 | 
|---|
 | 303 |     // update bounding box prior to selection
 | 
|---|
| [6c16a0] | 304 |     resetBoundingBox();
 | 
|---|
| [2b596f] | 305 | 
 | 
|---|
 | 306 |     painter->modelViewMatrix().push();
 | 
|---|
 | 307 |     painter->modelViewMatrix().translate(m_position);
 | 
|---|
 | 308 |     if (m_rotationAngle != 0.0f)
 | 
|---|
 | 309 |       painter->modelViewMatrix().rotate(m_rotationAngle, m_rotationVector);
 | 
|---|
| [f47efd4] | 310 |     if ((m_scaleX != 1.0f) || (m_scaleY != 1.0f) || (m_scaleZ != 1.0f))
 | 
|---|
 | 311 |       painter->modelViewMatrix().scale(m_scaleX, m_scaleY, m_scaleZ);
 | 
|---|
| [2b596f] | 312 | 
 | 
|---|
 | 313 |     // Draw a box around the mesh, if selected.
 | 
|---|
 | 314 |     if (m_selected)
 | 
|---|
 | 315 |       drawSelectionBox(painter);
 | 
|---|
 | 316 | 
 | 
|---|
 | 317 |     // Restore the modelview matrix.
 | 
|---|
 | 318 |     painter->modelViewMatrix().pop();
 | 
|---|
| [739ee9] | 319 |   }
 | 
|---|
| [8c001a] | 320 | }
 | 
|---|
 | 321 | 
 | 
|---|
| [34e7fdb] | 322 | void GLMoleculeObject_molecule::setVisible(bool value)
 | 
|---|
 | 323 | {
 | 
|---|
 | 324 |   // first update the mesh if we are going to be visible now
 | 
|---|
 | 325 |   if (value)
 | 
|---|
| [7b5984] | 326 |     updateTesselationHull();
 | 
|---|
| [34e7fdb] | 327 |   // then emit onward
 | 
|---|
 | 328 |   GLMoleculeObject::setVisible(value);
 | 
|---|
| [8d5fbf1] | 329 | 
 | 
|---|
 | 330 |   emit changed();
 | 
|---|
 | 331 |   emit changeOccured();
 | 
|---|
| [34e7fdb] | 332 | }
 | 
|---|
 | 333 | 
 | 
|---|
| [8c001a] | 334 | std::ostream &operator<<(std::ostream &ost, const GLMoleculeObject_molecule::BondIds &t)
 | 
|---|
 | 335 | {
 | 
|---|
 | 336 |   ost << t.first << "," << t.second;
 | 
|---|
 | 337 |   return ost;
 | 
|---|
 | 338 | }
 | 
|---|