Nori

include/nori/dpdf.h

Go to the documentation of this file.
00001 /*
00002     This file is part of Nori, a simple educational ray tracer
00003 
00004     Copyright (c) 2012 by Wenzel Jakob and Steve Marschner.
00005 
00006     Nori is free software; you can redistribute it and/or modify
00007     it under the terms of the GNU General Public License Version 3
00008     as published by the Free Software Foundation.
00009 
00010     Nori is distributed in the hope that it will be useful,
00011     but WITHOUT ANY WARRANTY; without even the implied warranty of
00012     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
00013     GNU General Public License for more details.
00014 
00015     You should have received a copy of the GNU General Public License
00016     along with this program. If not, see <http://www.gnu.org/licenses/>.
00017 */
00018 
00019 #if !defined(__DISCRETE_PDF_H)
00020 #define __DISCRETE_PDF_H
00021 
00022 #include <nori/common.h>
00023 
00024 NORI_NAMESPACE_BEGIN
00025 
00026 /**
00027  * \brief Discrete probability distribution
00028  * 
00029  * This data structure can be used to transform uniformly distributed
00030  * samples to a stored discrete probability distribution.
00031  * 
00032  * \ingroup libcore
00033  */
00034 struct DiscretePDF {
00035 public:
00036         /// Allocate memory for a distribution with the given number of entries
00037         explicit inline DiscretePDF(size_t nEntries = 0) {
00038                 reserve(nEntries);
00039                 clear();
00040         }
00041 
00042         /// Clear all entries
00043         inline void clear() {
00044                 m_cdf.clear();
00045                 m_cdf.push_back(0.0f);
00046                 m_normalized = false;
00047         }
00048 
00049         /// Reserve memory for a certain number of entries
00050         inline void reserve(size_t nEntries) {
00051                 m_cdf.reserve(nEntries+1);
00052         }
00053 
00054         /// Append an entry with the specified discrete probability
00055         inline void append(float pdfValue) {
00056                 m_cdf.push_back(m_cdf[m_cdf.size()-1] + pdfValue);
00057         }
00058 
00059         /// Return the number of entries so far
00060         inline size_t size() const {
00061                 return m_cdf.size()-1;
00062         }
00063 
00064         /// Access an entry by its index
00065         inline float operator[](size_t entry) const {
00066                 return m_cdf[entry+1] - m_cdf[entry];
00067         }
00068 
00069         /// Have the probability densities been normalized?
00070         inline bool isNormalized() const {
00071                 return m_normalized;
00072         }
00073 
00074         /**
00075          * \brief Return the original (unnormalized) sum of all PDF entries
00076          *
00077          * This assumes that \ref normalize() has previously been called
00078          */
00079         inline float getSum() const {
00080                 return m_sum;
00081         }
00082 
00083         /**
00084          * \brief Return the normalization factor (i.e. the inverse of \ref getSum())
00085          *
00086          * This assumes that \ref normalize() has previously been called
00087          */
00088         inline float getNormalization() const {
00089                 return m_normalization;
00090         }
00091 
00092         /**
00093          * \brief Normalize the distribution
00094          *
00095          * \return Sum of the (previously unnormalized) entries
00096          */
00097         inline float normalize() {
00098                 m_sum = m_cdf[m_cdf.size()-1];
00099                 if (m_sum > 0) {
00100                         m_normalization = 1.0f / m_sum;
00101                         for (size_t i=1; i<m_cdf.size(); ++i) 
00102                                 m_cdf[i] *= m_normalization;
00103                         m_cdf[m_cdf.size()-1] = 1.0f;
00104                         m_normalized = true;
00105                 } else {
00106                         m_normalization = 0.0f;
00107                 }
00108                 return m_sum;
00109         }
00110 
00111         /**
00112          * \brief %Transform a uniformly distributed sample to the stored distribution
00113          * 
00114          * \param[in] sampleValue
00115          *     An uniformly distributed sample on [0,1]
00116          * \return
00117          *     The discrete index associated with the sample
00118          */
00119         inline size_t sample(float sampleValue) const {
00120                 std::vector<float>::const_iterator entry = 
00121                                 std::lower_bound(m_cdf.begin(), m_cdf.end(), sampleValue);
00122                 size_t index = (size_t) std::max((ptrdiff_t) 0, entry - m_cdf.begin() - 1);
00123                 return std::min(index, m_cdf.size()-2);
00124         }
00125 
00126         /**
00127          * \brief %Transform a uniformly distributed sample to the stored distribution
00128          * 
00129          * \param[in] sampleValue
00130          *     An uniformly distributed sample on [0,1]
00131          * \param[out] pdf
00132          *     Probability value of the sample
00133          * \return
00134          *     The discrete index associated with the sample
00135          */
00136         inline size_t sample(float sampleValue, float &pdf) const {
00137                 size_t index = sample(sampleValue);
00138                 pdf = operator[](index);
00139                 return index;
00140         }
00141 
00142         /**
00143          * \brief %Transform a uniformly distributed sample to the stored distribution
00144          * 
00145          * The original sample is value adjusted so that it can be "reused".
00146          *
00147          * \param[in, out] sampleValue
00148          *     An uniformly distributed sample on [0,1]
00149          * \return
00150          *     The discrete index associated with the sample
00151          */
00152         inline size_t sampleReuse(float &sampleValue) const {
00153                 size_t index = sample(sampleValue);
00154                 sampleValue = (sampleValue - m_cdf[index])
00155                         / (m_cdf[index + 1] - m_cdf[index]);
00156                 return index;
00157         }
00158 
00159         /**
00160          * \brief %Transform a uniformly distributed sample. 
00161          * 
00162          * The original sample is value adjusted so that it can be "reused".
00163          *
00164          * \param[in,out]
00165          *     An uniformly distributed sample on [0,1]
00166          * \param[out] pdf
00167          *     Probability value of the sample
00168          * \return
00169          *     The discrete index associated with the sample
00170          */
00171         inline size_t sampleReuse(float &sampleValue, float &pdf) const {
00172                 size_t index = sample(sampleValue, pdf);
00173                 sampleValue = (sampleValue - m_cdf[index])
00174                         / (m_cdf[index + 1] - m_cdf[index]);
00175                 return index;
00176         }
00177 
00178         /**
00179          * \brief Turn the underlying distribution into a
00180          * human-readable string format
00181          */
00182         QString toString() const {
00183                 QString result = QString("DiscretePDF[sum=%1, "
00184                         "normalized=%2, pdf = {").arg(m_sum).arg(m_normalized);
00185 
00186                 for (size_t i=0; i<m_cdf.size(); ++i) {
00187                         result += QString("%1").arg(operator[](i));
00188                         if (i != m_cdf.size()-1)
00189                                 result += QString(", ");
00190                 }
00191                 return result + QString("}]");
00192         }
00193 private:
00194         std::vector<float> m_cdf;
00195         float m_sum, m_normalization;
00196         bool m_normalized;
00197 };
00198 
00199 NORI_NAMESPACE_END
00200 
00201 #endif /* __DISCRETE_PDF_H */
 All Classes Files Functions Variables Typedefs Enumerations Enumerator Defines