1 /******************************************************************************
2 
3   This source file is part of the Avogadro project.
4 
5   Copyright 2013 Kitware, Inc.
6 
7   This source code is released under the New BSD License, (the "License").
8 
9   Unless required by applicable law or agreed to in writing, software
10   distributed under the License is distributed on an "AS IS" BASIS,
11   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12   See the License for the specific language governing permissions and
13   limitations under the License.
14 
15 ******************************************************************************/
16 
17 #include "geometryvisitor.h"
18 
19 #include "ambientocclusionspheregeometry.h"
20 #include "linestripgeometry.h"
21 #include "spheregeometry.h"
22 
23 namespace Avogadro {
24 namespace Rendering {
25 
GeometryVisitor()26 GeometryVisitor::GeometryVisitor()
27   : m_center(Vector3f::Zero()), m_radius(0.0f), m_dirty(false)
28 {
29 }
30 
~GeometryVisitor()31 GeometryVisitor::~GeometryVisitor()
32 {
33 }
34 
visit(Drawable &)35 void GeometryVisitor::visit(Drawable&)
36 {
37 }
38 
visit(SphereGeometry & geometry)39 void GeometryVisitor::visit(SphereGeometry& geometry)
40 {
41   const Core::Array<SphereColor>& spheres = geometry.spheres();
42   if (!spheres.size())
43     return;
44 
45   m_dirty = true;
46 
47   Vector3f tmpCenter(Vector3f::Zero());
48   // First find the center of the sphere geometry.
49   std::vector<SphereColor>::const_iterator it = spheres.begin();
50   for (; it != spheres.end(); ++it)
51     tmpCenter += it->center;
52   tmpCenter /= static_cast<float>(spheres.size());
53 
54   // Now find its radius.
55   float tmpRadius(0.0f);
56   if (spheres.size() > 1) {
57     for (it = spheres.begin(); it != spheres.end(); ++it) {
58       float distance = (it->center - tmpCenter).squaredNorm();
59       if (distance > tmpRadius)
60         tmpRadius = distance;
61     }
62   }
63   tmpRadius = std::sqrt(tmpRadius);
64   m_centers.push_back(tmpCenter);
65   m_radii.push_back(tmpRadius);
66 }
67 
visit(AmbientOcclusionSphereGeometry & geometry)68 void GeometryVisitor::visit(AmbientOcclusionSphereGeometry& geometry)
69 {
70   const Core::Array<SphereColor>& spheres = geometry.spheres();
71   if (!spheres.size())
72     return;
73 
74   m_dirty = true;
75 
76   Vector3f tmpCenter(Vector3f::Zero());
77   // First find the center of the sphere geometry.
78   std::vector<SphereColor>::const_iterator it = spheres.begin();
79   for (; it != spheres.end(); ++it)
80     tmpCenter += it->center;
81   tmpCenter /= static_cast<float>(spheres.size());
82 
83   // Now find its radius.
84   float tmpRadius(0.0f);
85   if (spheres.size() > 1) {
86     for (it = spheres.begin(); it != spheres.end(); ++it) {
87       float distance = (it->center - tmpCenter).squaredNorm();
88       if (distance > tmpRadius)
89         tmpRadius = distance;
90     }
91   }
92   tmpRadius = std::sqrt(tmpRadius);
93   m_centers.push_back(tmpCenter);
94   m_radii.push_back(tmpRadius);
95 }
96 
visit(LineStripGeometry & lsg)97 void GeometryVisitor::visit(LineStripGeometry& lsg)
98 {
99   typedef Core::Array<LineStripGeometry::PackedVertex> VertexArray;
100   const VertexArray verts(lsg.vertices());
101   if (!verts.size())
102     return;
103 
104   m_dirty = true;
105 
106   Vector3f tmpCenter(Vector3f::Zero());
107   for (VertexArray::const_iterator it = verts.begin(), itEnd = verts.end();
108        it != itEnd; ++it) {
109     tmpCenter += it->vertex;
110   }
111   tmpCenter /= static_cast<float>(verts.size());
112 
113   float tmpRadius(0.f);
114   for (VertexArray::const_iterator it = verts.begin(), itEnd = verts.end();
115        it != itEnd; ++it) {
116     float distance = (it->vertex - tmpCenter).squaredNorm();
117     if (distance > tmpRadius)
118       tmpRadius = distance;
119   }
120 
121   m_centers.push_back(tmpCenter);
122   m_radii.push_back(std::sqrt(tmpRadius));
123 }
124 
clear()125 void GeometryVisitor::clear()
126 {
127   m_center = Vector3f::Zero();
128   m_radius = 0.0f;
129   m_dirty = false;
130   m_centers.clear();
131   m_radii.clear();
132 }
133 
center()134 Vector3f GeometryVisitor::center()
135 {
136   average();
137   return m_center;
138 }
139 
radius()140 float GeometryVisitor::radius()
141 {
142   average();
143   return m_radius;
144 }
145 
average()146 void GeometryVisitor::average()
147 {
148   if (!m_dirty)
149     return;
150 
151   // Find the average position of the center, then the minimal enclosing radius.
152   m_dirty = false;
153   if (m_centers.size() == 1) {
154     m_center = m_centers[0];
155     m_radius = m_radii[0];
156   } else {
157     m_center = Vector3f::Zero();
158     std::vector<Vector3f>::const_iterator cit;
159     for (cit = m_centers.begin(); cit != m_centers.end(); ++cit)
160       m_center += *cit;
161     m_center /= static_cast<float>(m_centers.size());
162     // Now find the smallest enclosing radius for the new center.
163     m_radius = 0.0f;
164     std::vector<float>::const_iterator rit;
165     for (cit = m_centers.begin(), rit = m_radii.begin();
166          cit != m_centers.end() && rit != m_radii.end(); ++cit, ++rit) {
167       float distance = (m_center - (*cit)).norm() + (*rit);
168       if (distance > m_radius)
169         m_radius = distance;
170     }
171   }
172 }
173 
174 } // End namespace Rendering
175 } // End namespace Avogadro
176