1 /******************************************************************************
2 
3   This source file is part of the Avogadro project.
4 
5   Copyright 2008-2009 Marcus D. Hanwell
6   Copyright 2010-2013 Kitware, Inc.
7 
8   This source code is released under the New BSD License, (the "License").
9 
10   Unless required by applicable law or agreed to in writing, software
11   distributed under the License is distributed on an "AS IS" BASIS,
12   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   See the License for the specific language governing permissions and
14   limitations under the License.
15 
16 ******************************************************************************/
17 
18 #include "cube.h"
19 
20 #include "molecule.h"
21 #include "mutex.h"
22 
23 namespace Avogadro {
24 namespace Core {
25 
Cube()26 Cube::Cube()
27   : m_data(0), m_min(0.0, 0.0, 0.0), m_max(0.0, 0.0, 0.0),
28     m_spacing(0.0, 0.0, 0.0), m_points(0, 0, 0), m_minValue(0.0),
29     m_maxValue(0.0), m_lock(new Mutex)
30 {
31 }
32 
~Cube()33 Cube::~Cube()
34 {
35   delete m_lock;
36   m_lock = 0;
37 }
38 
setLimits(const Vector3 & min_,const Vector3 & max_,const Vector3i & points)39 bool Cube::setLimits(const Vector3& min_, const Vector3& max_,
40                      const Vector3i& points)
41 {
42   // We can calculate all necessary properties and initialise our data
43   Vector3 delta = max_ - min_;
44   m_spacing =
45     Vector3(delta.x() / (points.x() - 1), delta.y() / (points.y() - 1),
46             delta.z() / (points.z() - 1));
47   m_min = min_;
48   m_max = max_;
49   m_points = points;
50   m_data.resize(m_points.x() * m_points.y() * m_points.z());
51   return true;
52 }
53 
setLimits(const Vector3 & min_,const Vector3 & max_,double spacing_)54 bool Cube::setLimits(const Vector3& min_, const Vector3& max_, double spacing_)
55 {
56   Vector3 delta = max_ - min_;
57   delta = delta / spacing_;
58   return setLimits(min_, max_, delta.cast<int>());
59 }
60 
setLimits(const Vector3 & min_,const Vector3i & dim,double spacing_)61 bool Cube::setLimits(const Vector3& min_, const Vector3i& dim, double spacing_)
62 {
63   return setLimits(min_, dim, Vector3(spacing_, spacing_, spacing_));
64 }
65 
setLimits(const Vector3 & min_,const Vector3i & dim,const Vector3 & spacing_)66 bool Cube::setLimits(const Vector3& min_, const Vector3i& dim,
67                      const Vector3& spacing_)
68 {
69   Vector3 max_ = Vector3(min_.x() + (dim.x() - 1) * spacing_[0],
70                          min_.y() + (dim.y() - 1) * spacing_[1],
71                          min_.z() + (dim.z() - 1) * spacing_[2]);
72   m_min = min_;
73   m_max = max_;
74   m_points = dim;
75   m_spacing = spacing_;
76   m_data.resize(m_points.x() * m_points.y() * m_points.z());
77   return true;
78 }
79 
setLimits(const Cube & cube)80 bool Cube::setLimits(const Cube& cube)
81 {
82   m_min = cube.m_min;
83   m_max = cube.m_max;
84   m_points = cube.m_points;
85   m_spacing = cube.m_spacing;
86   m_data.resize(m_points.x() * m_points.y() * m_points.z());
87   return true;
88 }
89 
setLimits(const Molecule & mol,double spacing_,double padding)90 bool Cube::setLimits(const Molecule& mol, double spacing_, double padding)
91 {
92   Index numAtoms = mol.atomCount();
93   Vector3 min_, max_;
94   if (numAtoms) {
95     Vector3 curPos = min_ = max_ = mol.atomPositions3d()[0];
96     for (Index i = 1; i < numAtoms; ++i) {
97       curPos = mol.atomPositions3d()[i];
98       if (curPos.x() < min_.x())
99         min_.x() = curPos.x();
100       if (curPos.x() > max_.x())
101         max_.x() = curPos.x();
102       if (curPos.y() < min_.y())
103         min_.y() = curPos.y();
104       if (curPos.y() > max_.y())
105         max_.y() = curPos.y();
106       if (curPos.z() < min_.z())
107         min_.z() = curPos.z();
108       if (curPos.z() > max_.z())
109         max_.z() = curPos.z();
110     }
111   } else {
112     min_ = max_ = Vector3::Zero();
113   }
114 
115   // Now to take care of the padding term
116   min_ += Vector3(-padding, -padding, -padding);
117   max_ += Vector3(padding, padding, padding);
118 
119   return setLimits(min_, max_, spacing_);
120 }
121 
data()122 std::vector<double>* Cube::data()
123 {
124   return &m_data;
125 }
126 
data() const127 const std::vector<double>* Cube::data() const
128 {
129   return &m_data;
130 }
131 
setData(const std::vector<double> & values)132 bool Cube::setData(const std::vector<double>& values)
133 {
134   if (!values.size())
135     return false;
136 
137   if (static_cast<int>(values.size()) ==
138       m_points.x() * m_points.y() * m_points.z()) {
139     m_data = values;
140     // Now to update the minimum and maximum values
141     m_minValue = m_maxValue = m_data[0];
142     for (std::vector<double>::const_iterator it = values.begin();
143          it != values.end(); ++it) {
144       if (*it < m_minValue)
145         m_minValue = *it;
146       else if (*it > m_maxValue)
147         m_maxValue = *it;
148     }
149     return true;
150   } else {
151     return false;
152   }
153 }
154 
addData(const std::vector<double> & values)155 bool Cube::addData(const std::vector<double>& values)
156 {
157   // Initialise the cube to zero if necessary
158   if (!m_data.size())
159     m_data.resize(m_points.x() * m_points.y() * m_points.z());
160   if (values.size() != m_data.size() || !values.size())
161     return false;
162   for (unsigned int i = 0; i < m_data.size(); i++) {
163     m_data[i] += values[i];
164     if (m_data[i] < m_minValue)
165       m_minValue = m_data[i];
166     else if (m_data[i] > m_maxValue)
167       m_maxValue = m_data[i];
168   }
169   return true;
170 }
171 
closestIndex(const Vector3 & pos) const172 unsigned int Cube::closestIndex(const Vector3& pos) const
173 {
174   int i, j, k;
175   // Calculate how many steps each coordinate is along its axis
176   i = int((pos.x() - m_min.x()) / m_spacing.x());
177   j = int((pos.y() - m_min.y()) / m_spacing.y());
178   k = int((pos.z() - m_min.z()) / m_spacing.z());
179   return i * m_points.y() * m_points.z() + j * m_points.z() + k;
180 }
181 
indexVector(const Vector3 & pos) const182 Vector3i Cube::indexVector(const Vector3& pos) const
183 {
184   // Calculate how many steps each coordinate is along its axis
185   int i, j, k;
186   i = int((pos.x() - m_min.x()) / m_spacing.x());
187   j = int((pos.y() - m_min.y()) / m_spacing.y());
188   k = int((pos.z() - m_min.z()) / m_spacing.z());
189   return Vector3i(i, j, k);
190 }
191 
position(unsigned int index) const192 Vector3 Cube::position(unsigned int index) const
193 {
194   int x, y, z;
195   x = int(index / (m_points.y() * m_points.z()));
196   y = int((index - (x * m_points.y() * m_points.z())) / m_points.z());
197   z = index % m_points.z();
198   return Vector3(x * m_spacing.x() + m_min.x(), y * m_spacing.y() + m_min.y(),
199                  z * m_spacing.z() + m_min.z());
200 }
201 
value(int i,int j,int k) const202 double Cube::value(int i, int j, int k) const
203 {
204   unsigned int index = i * m_points.y() * m_points.z() + j * m_points.z() + k;
205   if (index < m_data.size())
206     return m_data[index];
207   else
208     return 0.0;
209 }
210 
value(const Vector3i & pos) const211 double Cube::value(const Vector3i& pos) const
212 {
213   unsigned int index =
214     pos.x() * m_points.y() * m_points.z() + pos.y() * m_points.z() + pos.z();
215   if (index < m_data.size())
216     return m_data[index];
217   else
218     return 6969.0;
219 }
220 
valuef(const Vector3f & pos) const221 float Cube::valuef(const Vector3f& pos) const
222 {
223   // This is a really expensive operation and so should be avoided
224   // Interpolate the value at the supplied vector - trilinear interpolation...
225   Vector3f delta = pos - m_min.cast<float>();
226   // Find the integer low and high corners
227   Vector3i lC(static_cast<int>(delta.x() / m_spacing.x()),
228               static_cast<int>(delta.y() / m_spacing.y()),
229               static_cast<int>(delta.z() / m_spacing.z()));
230   Vector3i hC(lC.x() + 1, lC.y() + 1, lC.z() + 1);
231   // So there are six corners in total - work out the delta of the position
232   // and the low corner
233   const Vector3f lCf(lC.cast<float>());
234   const Vector3f spacingf(m_spacing.cast<float>());
235   Vector3f P((delta.x() - lCf.x() * spacingf.x()) / spacingf.x(),
236              (delta.y() - lCf.y() * spacingf.y()) / spacingf.y(),
237              (delta.z() - lCf.z() * spacingf.z()) / spacingf.z());
238   Vector3f dP = Vector3f(1.0f, 1.0f, 1.0f) - P;
239   // Now calculate and return the interpolated value
240   return static_cast<float>(
241     value(lC.x(), lC.y(), lC.z()) * dP.x() * dP.y() * dP.z() +
242     value(hC.x(), lC.y(), lC.z()) * P.x() * dP.y() * dP.z() +
243     value(lC.x(), hC.y(), lC.z()) * dP.x() * P.y() * dP.z() +
244     value(lC.x(), lC.y(), hC.z()) * dP.x() * dP.y() * P.z() +
245     value(hC.x(), lC.y(), hC.z()) * P.x() * dP.y() * P.z() +
246     value(lC.x(), hC.y(), hC.z()) * dP.x() * P.y() * P.z() +
247     value(hC.x(), hC.y(), lC.z()) * P.x() * P.y() * dP.z() +
248     value(hC.x(), hC.y(), hC.z()) * P.x() * P.y() * P.z());
249 }
250 
value(const Vector3 & pos) const251 double Cube::value(const Vector3& pos) const
252 {
253   // This is a really expensive operation and so should be avoided
254   // Interpolate the value at the supplied vector - trilinear interpolation...
255   Vector3 delta = pos - m_min;
256   // Find the integer low and high corners
257   Vector3i lC(static_cast<int>(delta.x() / m_spacing.x()),
258               static_cast<int>(delta.y() / m_spacing.y()),
259               static_cast<int>(delta.z() / m_spacing.z()));
260   Vector3i hC(lC.x() + 1, lC.y() + 1, lC.z() + 1);
261   // So there are six corners in total - work out the delta of the position
262   // and the low corner
263   Vector3 P((delta.x() - lC.x() * m_spacing.x()) / m_spacing.x(),
264             (delta.y() - lC.y() * m_spacing.y()) / m_spacing.y(),
265             (delta.z() - lC.z() * m_spacing.z()) / m_spacing.z());
266   Vector3 dP = Vector3(1.0, 1.0, 1.0) - P;
267   // Now calculate and return the interpolated value
268   return value(lC.x(), lC.y(), lC.z()) * dP.x() * dP.y() * dP.z() +
269          value(hC.x(), lC.y(), lC.z()) * P.x() * dP.y() * dP.z() +
270          value(lC.x(), hC.y(), lC.z()) * dP.x() * P.y() * dP.z() +
271          value(lC.x(), lC.y(), hC.z()) * dP.x() * dP.y() * P.z() +
272          value(hC.x(), lC.y(), hC.z()) * P.x() * dP.y() * P.z() +
273          value(lC.x(), hC.y(), hC.z()) * dP.x() * P.y() * P.z() +
274          value(hC.x(), hC.y(), lC.z()) * P.x() * P.y() * dP.z() +
275          value(hC.x(), hC.y(), hC.z()) * P.x() * P.y() * P.z();
276 }
277 
setValue(int i,int j,int k,double value_)278 bool Cube::setValue(int i, int j, int k, double value_)
279 {
280   unsigned int index = i * m_points.y() * m_points.z() + j * m_points.z() + k;
281   if (index < m_data.size()) {
282     m_data[index] = value_;
283     if (value_ < m_minValue)
284       m_minValue = value_;
285     else if (value_ > m_maxValue)
286       m_maxValue = value_;
287     return true;
288   } else {
289     return false;
290   }
291 }
292 
293 } // End Core namespace
294 } // End Avogadro namespace
295