Commit 01707489309c7fd07f3372e43063de936141e4cb

Authored by David Mayerich
1 parent d9547496

implemented a new spherical harmonics renderer

stim/image/image.h
... ... @@ -137,10 +137,6 @@ public:
137 137 std::cout << "Error opening input file in image::load_netpbm()" << std::endl;
138 138 exit(1);
139 139 }
140   - if (sizeof(T) != 1) {
141   - std::cout << "Error in image::load_netpbm() - data type must be 8-bit integer." << std::endl;
142   - exit(1);
143   - }
144 140  
145 141 size_t nc; //allocate space for the number of channels
146 142 char format[2]; //allocate space to hold the image format tag
... ... @@ -187,9 +183,12 @@ public:
187 183 }
188 184 size_t h = atoi(sh.c_str()); //convert the string into an integer
189 185  
190   - allocate(w, h, nc); //allocate space for the image
191   - infile.read((char*)img, size()); //copy the binary data from the file to the image
192   - infile.close();
  186 + allocate(w, h, nc); //allocate space for the image
  187 + unsigned char* buffer = (unsigned char*)malloc(w * h * nc); //create a buffer to store the read data
  188 + infile.read((char*)buffer, size()); //copy the binary data from the file to the image
  189 + infile.close(); //close the file
  190 + for (size_t n = 0; n < size(); n++) img[n] = (T)buffer[n]; //copy the buffer data into the image
  191 + free(buffer); //free the buffer array
193 192 }
194 193  
195 194  
... ...
stim/math/spharmonics.h
... ... @@ -2,280 +2,348 @@
2 2 #define STIM_SPH_HARMONICS
3 3  
4 4 #include <complex>
5   -#include <stim/math/vector.h>
6 5 #include <boost/math/special_functions/spherical_harmonic.hpp>
7 6 #include <stim/math/constants.h>
8 7 #include <stim/math/random.h>
9 8 #include <vector>
10 9  
11 10 #define WIRE_SCALE 1.001
12   -namespace stim{
  11 +namespace stim {
13 12  
14   -template<class T>
15   -class spharmonics{
  13 + template<class T>
  14 + class spharmonics {
16 15  
17   -public:
18   - std::vector<T> C; //list of SH coefficients
  16 + public:
  17 + std::vector<T> C; //list of SH coefficients
19 18  
20   -protected:
  19 + protected:
  20 + unsigned int mcN; //number of Monte-Carlo samples
  21 + unsigned int coeff_1d(unsigned int l, int m) { //convert (l,m) to i (1D coefficient index)
  22 + return pow(l + 1, 2) - (l - m) - 1;
  23 + }
  24 + void coeff_2d(size_t c, unsigned int& l, int& m) { //convert a 1D coefficient index into (l, m)
  25 + l = (unsigned int)ceil(sqrt((double)c + 1)) - 1; //the major index is equal to sqrt(c) - 1
  26 + m = (int)(c - (size_t)(l * l)) - (int)l; //the minor index is calculated by finding the difference
  27 + }
21 28  
  29 + public:
  30 + spharmonics() {
  31 + mcN = 0;
  32 + }
  33 + spharmonics(size_t c) : spharmonics() {
  34 + resize(c);
  35 + }
22 36  
23   - unsigned int mcN; //number of Monte-Carlo samples
  37 + void push(T c) {
  38 + C.push_back(c);
  39 + }
24 40  
25   - //calculate the value of the SH basis function (l, m) at (theta, phi)
26   - //here, theta = [0, PI], phi = [0, 2*PI]
27   - double SH(int l, int m, double theta, double phi){
28   - //std::complex<T> result = boost::math::spherical_harmonic(l, m, phi, theta);
29   - //return result.imag() + result.real();
30   -
31   - //this calculation is based on calculating the real spherical harmonics:
32   - // https://en.wikipedia.org/wiki/Spherical_harmonics#Addition_theorem
33   - if (m < 0) {
34   - return sqrt(2.0) * pow(-1, m) * boost::math::spherical_harmonic(l, abs(m), phi, theta).imag();
  41 + void resize(unsigned int n) {
  42 + C.resize(n);
35 43 }
36   - else if (m == 0) {
37   - return boost::math::spherical_harmonic(l, m, phi, theta).real();
  44 +
  45 + void setc(unsigned int l, int m, T value) {
  46 + unsigned int c = coeff_1d(l, m);
  47 + C[c] = value;
38 48 }
39   - else {
40   - return sqrt(2.0) * pow(-1, m) * boost::math::spherical_harmonic(l, m, phi, theta).real();
  49 +
  50 + T getc(unsigned int l, int m) {
  51 + unsigned int c = coeff_1d(l, m);
  52 + return C[c];
41 53 }
42   - }
43 54  
44   - unsigned int coeff_1d(unsigned int l, int m){
45   - return pow(l + 1, 2) - (l - m) - 1;
46   - }
  55 + void setc(unsigned int c, T value) {
  56 + C[c] = value;
  57 + }
47 58  
48   -
  59 + unsigned int getSize() const {
  60 + return C.size();
  61 + }
49 62  
  63 + std::vector<T> getC() const {
  64 + return C;
  65 + }
  66 + //calculate the value of the SH basis function (l, m) at (theta, phi)
  67 + //here, theta = [0, PI], phi = [0, 2*PI]
  68 + T SH(unsigned int l, int m, T theta, T phi) {
  69 + //std::complex<T> result = boost::math::spherical_harmonic(l, m, phi, theta);
  70 + //return result.imag() + result.real();
  71 +
  72 + //this calculation is based on calculating the real spherical harmonics:
  73 + // https://en.wikipedia.org/wiki/Spherical_harmonics#Addition_theorem
  74 + if (m < 0) {
  75 + return sqrt(2.0) * pow(-1, m) * boost::math::spherical_harmonic(l, abs(m), phi, theta).imag();
  76 + }
  77 + else if (m == 0) {
  78 + return boost::math::spherical_harmonic(l, m, phi, theta).real();
  79 + }
  80 + else {
  81 + return sqrt(2.0) * pow(-1, m) * boost::math::spherical_harmonic(l, m, phi, theta).real();
  82 + }
  83 + }
50 84  
51   -public:
52   - spharmonics() {
53   - mcN = 0;
54   - }
55   - spharmonics(size_t c) : spharmonics() {
56   - resize(c);
57   - }
  85 + /// Calculate the spherical harmonic result given a 1D coefficient index
  86 + T SH(size_t c, T theta, T phi) {
  87 + unsigned int l;
  88 + int m;
  89 + coeff_2d(c, l, m);
  90 + return SH(l, m, theta, phi);
  91 + }
58 92  
59   - void push(double c){
60   - C.push_back(c);
61   - }
62 93  
63   - void resize(unsigned int n){
64   - C.resize(n);
65   - }
66 94  
67   - void setc(unsigned int l, int m, T value){
68   - unsigned int c = coeff_1d(l, m);
69   - C[c] = value;
70   - }
  95 + /// Initialize Monte-Carlo sampling of a function using N spherical harmonics coefficients
71 96  
72   - void setc(unsigned int c, T value){
73   - C[c] = value;
74   - }
  97 + /// @param N is the number of spherical harmonics coefficients used to represent the user function
  98 + void mcBegin(unsigned int coefficients) {
  99 + C.resize(coefficients, 0);
  100 + mcN = 0;
  101 + }
75 102  
76   - unsigned int getSize() const{
77   - return C.size();
78   - }
  103 + void mcBegin(unsigned int l, int m) {
  104 + unsigned int c = pow(l + 1, 2) - (l - m);
  105 + mcBegin(c);
  106 + }
79 107  
80   - std::vector<T> getC() const{
81   - return C;
82   - }
  108 + void mcSample(T theta, T phi, T val) {
83 109  
84   -
  110 + int l, m;
  111 + T sh;
85 112  
86   - /// Initialize Monte-Carlo sampling of a function using N spherical harmonics coefficients
  113 + l = m = 0;
  114 + for (unsigned int i = 0; i < C.size(); i++) {
87 115  
88   - /// @param N is the number of spherical harmonics coefficients used to represent the user function
89   - void mcBegin(unsigned int coefficients){
90   - C.resize(coefficients, 0);
91   - mcN = 0;
92   - }
  116 + sh = SH(l, m, theta, phi);
  117 + C[i] += sh * val;
93 118  
94   - void mcBegin(unsigned int l, int m){
95   - unsigned int c = pow(l + 1, 2) - (l - m);
96   - mcBegin(c);
97   - }
  119 + m++; //increment m
98 120  
99   - void mcSample(double theta, double phi, double val){
  121 + //if we're in a new tier, increment l and set m = -l
  122 + if (m > l) {
  123 + l++;
  124 + m = -l;
  125 + }
  126 + } //end for all coefficients
100 127  
101   - int l, m;
102   - double sh;
  128 + //increment the number of samples
  129 + mcN++;
103 130  
104   - l = m = 0;
105   - for(unsigned int i = 0; i < C.size(); i++){
  131 + } //end mcSample()
106 132  
107   - sh = SH(l, m, theta, phi);
108   - C[i] += sh * val;
  133 + void mcEnd() {
109 134  
110   - m++; //increment m
  135 + //divide all coefficients by the number of samples
  136 + for (unsigned int i = 0; i < C.size(); i++)
  137 + C[i] /= mcN;
  138 + }
111 139  
112   - //if we're in a new tier, increment l and set m = -l
113   - if(m > l){
114   - l++;
115   - m = -l;
  140 + /// Generates a PDF describing the probability distribution of points on a spherical surface
  141 + /// @param sph_pts is a list of points in spherical coordinates (theta, phi) where theta = [0, 2pi] and phi = [0, pi]
  142 + /// @param l is the maximum degree of the spherical harmonic function
  143 + /// @param m is the maximum order
  144 + void pdf(std::vector<stim::vec3<T> > sph_pts, unsigned int l, int m) {
  145 + mcBegin(l, m); //begin spherical harmonic sampling
  146 + unsigned int nP = sph_pts.size();
  147 + for (unsigned int p = 0; p < nP; p++) {
  148 + mcSample(sph_pts[p][1], sph_pts[p][2], 1.0);
116 149 }
117   - } //end for all coefficients
  150 + mcEnd();
  151 + }
  152 +
  153 + void pdf(std::vector<stim::vec3<T> > sph_pts, size_t c) {
  154 + unsigned int l;
  155 + int m;
  156 + coeff_2d(c, l, m);
  157 + pdf(sph_pts, l, m);
  158 + }
  159 +
  160 + /// Project a set of samples onto a spherical harmonic basis
  161 + void project(std::vector<stim::vec3<T> > sph_pts, unsigned int l, int m) {
  162 + mcBegin(l, m); //begin spherical harmonic sampling
  163 + unsigned int nP = sph_pts.size();
  164 + for (unsigned int p = 0; p < nP; p++) {
  165 + mcSample(sph_pts[p][1], sph_pts[p][2], sph_pts[p][0]);
  166 + }
  167 + mcEnd();
  168 + }
  169 + void project(std::vector<stim::vec3<T> > sph_pts, size_t c) {
  170 + unsigned int l;
  171 + int m;
  172 + coeff_2d(c, l, m);
  173 + project(sph_pts, l, m);
  174 + }
118 175  
119   - //increment the number of samples
120   - mcN++;
  176 + /// Generates a PDF describing the density distribution of points on a sphere
  177 + /// @param sph_pts is a list of points in cartesian coordinates
  178 + /// @param l is the maximum degree of the spherical harmonic function
  179 + /// @param m is the maximum order
  180 + /// @param c is the centroid of the points in sph_pts. DEFAULT 0,0,0
  181 + /// @param n is the number of points of the surface of the sphere used to create the PDF. DEFAULT 1000
  182 + /// @param norm, a boolean that sets where the output vectors will be normalized between 0 and 1.
  183 + /// @param
  184 + /*void pdf(std::vector<stim::vec3<T> > sph_pts, unsigned int l, int m, stim::vec3<T> c = stim::vec3<T>(0, 0, 0), unsigned int n = 1000, bool norm = true, std::vector<T> w = std::vector<T>())
  185 + {
  186 + std::vector<double> weights; ///the weight at each point on the surface of the sphere.
  187 + // weights.resize(n);
  188 + unsigned int nP = sph_pts.size();
  189 + std::vector<stim::vec3<T> > sphere = stim::Random<T>::sample_sphere(n, 1.0, stim::TAU);
  190 + if (w.size() < nP)
  191 + w = std::vector<T>(nP, 1.0);
  192 +
  193 + for (int i = 0; i < n; i++)
  194 + {
  195 + T val = 0;
  196 + for (int j = 0; j < nP; j++)
  197 + {
  198 + stim::vec3<T> temp = sph_pts[j] - c;
  199 + if (temp.dot(sphere[i]) > 0)
  200 + val += pow(temp.dot(sphere[i]), 4)*w[j];
  201 + }
  202 + weights.push_back(val);
  203 + }
121 204  
122   - } //end mcSample()
  205 + mcBegin(l, m); //begin spherical harmonic sampling
123 206  
124   - void mcEnd(){
  207 + if (norm)
  208 + {
  209 + T min = *std::min_element(weights.begin(), weights.end());
  210 + T max = *std::max_element(weights.begin(), weights.end());
  211 + for (unsigned int i = 0; i < n; i++)
  212 + {
  213 + stim::vec3<T> sph = sphere[i].cart2sph();
  214 + mcSample(sph[1], sph[2], (weights[i] - min) / (max - min));
  215 + }
125 216  
126   - //divide all coefficients by the number of samples
127   - for(unsigned int i = 0; i < C.size(); i++)
128   - C[i] /= mcN;
129   - }
  217 + }
  218 + else {
  219 + for (unsigned int i = 0; i < n; i++)
  220 + {
  221 + stim::vec3<T> sph = sphere[i].cart2sph();
  222 + mcSample(sph[1], sph[2], weights[i]);
  223 + }
  224 + }
  225 + mcEnd();
  226 + }*/
130 227  
131   - /// Generates a PDF describing the probability distribution of points on a spherical surface
132   - /// @param sph_pts is a list of points in spherical coordinates (theta, phi) where theta = [0, 2pi] and phi = [0, pi]
133   - /// @param l is the maximum degree of the spherical harmonic function
134   - /// @param m is the maximum order
  228 + std::string str() {
135 229  
136   - void pdf(std::vector<stim::vec<double> > sph_pts, unsigned int l, int m){
137   -
138   - mcBegin( l, m ); //begin spherical harmonic sampling
  230 + std::stringstream ss;
139 231  
140   - unsigned int nP = sph_pts.size();
  232 + int l, m;
  233 + l = m = 0;
  234 + for (unsigned int i = 0; i < C.size(); i++) {
141 235  
142   - for(unsigned int p = 0; p < nP; p++){
143   - mcSample(sph_pts[p][1], sph_pts[p][2], 1.0);
144   - }
  236 + ss << C[i] << '\t';
145 237  
146   - mcEnd();
147   - }
148   -
149   - /// Generates a PDF describing the probability distribution of points on a spherical surface
150   - /// @param sph_pts is a list of points in cartesian coordinates
151   - /// @param l is the maximum degree of the spherical harmonic function
152   - /// @param m is the maximum order
153   - /// @param c is the centroid of the points in sph_pts. DEFAULT 0,0,0
154   - /// @param n is the number of points of the surface of the sphere used to create the PDF. DEFAULT 1000
155   - void pdf(std::vector<stim::vec3<double> > sph_pts, unsigned int l, int m, stim::vec3<double> c = stim::vec3<double>(0,0,0), unsigned int n = 1000)
156   - {
157   - std::vector<double> weights; ///the weight at each point on the surface of the sphere.
158   -// weights.resize(n);
159   - unsigned int nP = sph_pts.size();
160   - std::vector<stim::vec3<double> > sphere = stim::Random<double>::sample_sphere(n, 1.0, stim::TAU);
161   - for(int i = 0; i < n; i++)
162   - {
163   - double val = 0;
164   - for(int j = 0; j < nP; j++)
165   - {
166   - stim::vec3<double> temp = sph_pts[j] - c;
167   - if(temp.dot(sphere[i]) > 0)
168   - val += pow(temp.dot(sphere[i]),4);
169   - }
170   - weights.push_back(val);
171   - }
172   -
173   -
174   - mcBegin(l, m); //begin spherical harmonic sampling
175   -
176   - for(unsigned int i = 0; i < n; i++)
177   - {
178   - stim::vec3<double> sph = sphere[i].cart2sph();
179   - mcSample(sph[1], sph[2], weights[i]);
180   - }
  238 + m++; //increment m
181 239  
182   - mcEnd();
183   - }
  240 + //if we're in a new tier, increment l and set m = -l
  241 + if (m > l) {
  242 + l++;
  243 + m = -l;
184 244  
185   - std::string str(){
  245 + ss << std::endl;
186 246  
187   - std::stringstream ss;
  247 + }
  248 + }
188 249  
189   - int l, m;
190   - l = m = 0;
191   - for(unsigned int i = 0; i < C.size(); i++){
192   -
193   - ss<<C[i]<<'\t';
  250 + return ss.str();
194 251  
195   - m++; //increment m
196 252  
197   - //if we're in a new tier, increment l and set m = -l
198   - if(m > l){
199   - l++;
200   - m = -l;
  253 + }
201 254  
202   - ss<<std::endl;
203   -
  255 + /// Returns the value of the function at coordinate (theta, phi)
  256 + T p(T theta, T phi) {
  257 + T fx = 0;
  258 +
  259 + int l = 0;
  260 + int m = 0;
  261 + for (unsigned int i = 0; i < C.size(); i++) {
  262 + fx += C[i] * SH(l, m, theta, phi);
  263 + m++;
  264 + if (m > l) {
  265 + l++;
  266 + m = -l;
  267 + }
204 268 }
  269 + return fx;
205 270 }
206 271  
207   - return ss.str();
  272 + /// Returns the derivative of the spherical function with respect to theta
  273 + /// return value is in cartesian coordinates
  274 + vec3<T> dtheta(T theta, T phi, T d = 0.01) {
  275 + T r = p(theta, phi); //calculate the value of the spherical function at three points
  276 + T rt = p(theta + d, phi);
  277 + //double rp = p(theta, phi + d);
208 278  
  279 + vec3<T> s(r, theta, phi); //get the spherical coordinate position for all three points
  280 + vec3<T> st(rt, theta + d, phi);
  281 + //vec3<double> sp(rp, theta, phi + d);
209 282  
210   - }
  283 + vec3<T> c = s.sph2cart();
  284 + vec3<T> ct = st.sph2cart();
  285 + //vec3<double> cp = sp.sph2cart();
211 286  
212   - /// Returns the value of the function at the coordinate (theta, phi)
  287 + vec3<T> dt = (ct - c)/d; //calculate the derivative
  288 + return dt;
  289 + }
213 290  
214   - /// @param theta = [0, 2pi]
215   - /// @param phi = [0, pi]
216   - double operator()(double theta, double phi){
  291 + /// Returns the derivative of the spherical function with respect to phi
  292 + /// return value is in cartesian coordinates
  293 + vec3<T> dphi(T theta, T phi, T d = 0.01) {
  294 + T r = p(theta, phi); //calculate the value of the spherical function at three points
  295 + //double rt = p(theta + d, phi);
  296 + T rp = p(theta, phi + d);
217 297  
218   - double fx = 0;
  298 + vec3<T> s(r, theta, phi); //get the spherical coordinate position for all three points
  299 + //vec3<double> st(rt, theta + d, phi);
  300 + vec3<T> sp(rp, theta, phi + d);
219 301  
220   - int l = 0;
221   - int m = 0;
222   - for(unsigned int i = 0; i < C.size(); i++){
223   - fx += C[i] * SH(l, m, theta, phi);
224   - m++;
225   - if(m > l){
226   - l++;
227   - m = -l;
228   - }
  302 + vec3<T> c = s.sph2cart();
  303 + //vec3<double> ct = st.sph2cart();
  304 + vec3<T> cp = sp.sph2cart();
229 305  
  306 + vec3<T> dp = (cp - c) / d; //calculate the derivative
  307 + return dp;
230 308 }
231   -
232   - return fx;
233   - }
234   -
235   - /// Fill an NxN grid with the spherical function for theta = [0 2pi] and phi = [0 pi]
236   - void grid(T* data, size_t N){
237   - double dt = stim::TAU / (double)N; //calculate the step size in each direction
238   - double dp = stim::PI / (double)(N - 1);
239   - for(size_t ti = 0; ti < N; ti++){
240   - for(size_t pi = 0; pi < N){
241   - data[pi * N + ti] = (*this)((double)ti * dt, (double)pi * dp);
242   - }
  309 +
  310 + /// Returns the value of the function at the coordinate (theta, phi)
  311 + /// @param theta = [0, 2pi]
  312 + /// @param phi = [0, pi]
  313 + T operator()(T theta, T phi) {
  314 + return p(theta, phi);
243 315 }
244   - }
245   -/*
246   - //overload arithmetic operations
247   - spharmonics<T> operator*(T rhs) const {
248   - spharmonics<T> result(C.size()); //create a new spherical harmonics object
249   - for (size_t c = 0; c < C.size(); c++) //for each coefficient
250   - result.C[c] = C[c] * rhs; //calculate the factor and store the result in the new spharmonics object
251   - return result;
252   - }
253   -
254   - spharmonics<T> operator+(spharmonics<T> rhs) {
255   - size_t low = std::min(C.size(), rhs.C.size()); //store the number of coefficients in the lowest object
256   - size_t high = std::max(C.size(), rhs.C.size()); //store the number of coefficients in the result
257   - bool rhs_lowest = false; //true if rhs has the lowest number of coefficients
258   - if (rhs.C.size() < C.size()) rhs_lowest = true; //if rhs has a lower number of coefficients, set the flag
259   -
260   - spharmonics<T> result(high); //create a new object
261   - size_t c;
262   - for (c = 0; c < low; c++) //perform the first batch of additions
263   - result.C[c] = C[c] + rhs.C[c]; //perform the addition
264   -
265   - for (c = low; c < high; c++) {
266   - if (rhs_lowest)
267   - result.C[c] = C[c];
268   - else
269   - result.C[c] = rhs.C[c];
  316 + /// Fill an NxN grid with the spherical function for theta = [0 2pi] and phi = [0 pi]
  317 + void get_func(T* data, size_t X, size_t Y) {
  318 + T dt = stim::TAU / (T)X; //calculate the step size in each direction
  319 + T dp = stim::PI / (T)(Y - 1);
  320 + for (size_t ti = 0; ti < X; ti++) {
  321 + for (size_t pi = 0; pi < Y; pi++) {
  322 + data[pi * X + ti] = (*this)((T)ti * dt, (T)pi * dp);
  323 + }
  324 + }
270 325 }
271   - return result;
272   - }
273 326  
274   - spharmonics<T> operator-(spharmonics<T> rhs) {
275   - return (*this) + (rhs * (T)(-1));
276   - }
277   -*/
278   -}; //end class sph_harmonics
  327 + /// Project a spherical function onto the basis using C coefficients
  328 + /// @param data is a pointer to the function values in (theta, phi) coordinates
  329 + /// @param N is the number of samples along each axis, where theta = [0 2pi), phi = [0 pi]
  330 + void project(T* data, size_t x, size_t y, size_t nc) {
  331 + stim::cpu2image(data, "test.ppm", x, y, stim::cmBrewer);
  332 + C.resize(nc, 0); //resize the coefficient array to store the necessary coefficients
  333 + T dtheta = stim::TAU / (T)(x - 1); //calculate the grid spacing along theta
  334 + T dphi = stim::PI / (T)y; //calculate the grid spacing along phi
  335 + T theta, phi;
  336 + for (size_t c = 0; c < nc; c++) { //for each coefficient
  337 + for (size_t theta_i = 0; theta_i < x; theta_i++) { //for each coordinate in the provided array
  338 + theta = theta_i * dtheta; //calculate theta
  339 + for (size_t phi_i = 0; phi_i < y; phi_i++) {
  340 + phi = phi_i * dphi; //calculate phi
  341 + C[c] += data[phi_i * x + theta_i] * SH(c, theta, phi) * dtheta * dphi * sin(phi);
  342 + }
  343 + }
  344 + }
  345 + }
  346 + }; //end class sph_harmonics
279 347  
280 348  
281 349  
... ... @@ -283,4 +351,4 @@ public:
283 351 }
284 352  
285 353  
286   -#endif
  354 +#endif
287 355 \ No newline at end of file
... ...
stim/visualization/gl_spharmonics-dep.h 0 โ†’ 100644
  1 +#ifndef STIM_GL_SPHARMONICS_H
  2 +#define STIM_GL_SPHARMONICS_H
  3 +
  4 +#include <GL/gl.h>
  5 +
  6 +#include <stim/gl/error.h>
  7 +#include <stim/visualization/colormap.h>
  8 +#include <stim/math/spharmonics.h>
  9 +
  10 +namespace stim{
  11 +
  12 +
  13 +template <typename T>
  14 +class gl_spharmonics : public spharmonics<T>{
  15 +
  16 +protected:
  17 +
  18 + using stim::spharmonics<T>::SH;
  19 + stim::spharmonics<T> surface; //harmonic that stores the surface information
  20 + stim::spharmonics<T> color; //harmonic that stores the color information
  21 + T* func_surface; //stores the raw function data (samples at each point)
  22 + T* func_color; //stores the raw color data (samples at each point)
  23 + GLuint color_tex; //texture map that acts as a colormap for the spherical function
  24 + unsigned int N; //resolution of the spherical grid
  25 +
  26 + ///generates the
  27 + void gen_surface(){
  28 + //allocate memory and initialize the function to zero
  29 + func_surface = (T*) malloc(N * N * sizeof(T));
  30 + memset(func_surface, 0, sizeof(T) * N * N);
  31 +
  32 + double theta, phi;
  33 + double result;
  34 + int l, m;
  35 +
  36 +
  37 + l = m = 0;
  38 + //for each coefficient
  39 + for(unsigned int c = 0; c < surface.C.size(); c++){
  40 +
  41 + //iterate through the entire 2D grid representing the function
  42 + for(unsigned int xi = 0; xi < N; xi++){
  43 + for(unsigned int yi = 0; yi < N; yi++){
  44 +
  45 + //get the spherical coordinates for each grid point
  46 + theta = (2 * PI) * ((double)xi / (N-1));
  47 + phi = PI * ((double)yi / (N-1));
  48 +
  49 + //sum the contribution of the current spherical harmonic based on the coefficient
  50 + result = surface.C[c] * SH(l, m, theta, phi);
  51 +
  52 + //store the result in a 2D array (which will later be used as a texture map)
  53 + func_surface[yi * N + xi] += result;
  54 + }
  55 + }
  56 +
  57 + //keep track of m and l here
  58 + m++; //increment m
  59 + //if we're in a new tier, increment l and set m = -l
  60 + if(m > l){
  61 + l++;
  62 + m = -l;
  63 + }
  64 + }
  65 + }
  66 +
  67 + ///generates the color function
  68 + void gen_color(){
  69 +
  70 + //initialize the function to zero
  71 + func_color = (T*) malloc(N * N * sizeof(T));
  72 + memset(func_color, 0, sizeof(T) * N * N);
  73 +
  74 + double theta, phi;
  75 + double result;
  76 + int l, m;
  77 +
  78 +
  79 + l = m = 0;
  80 + //for each coefficient
  81 + for(unsigned int c = 0; c < color.C.size(); c++){
  82 +
  83 + //iterate through the entire 2D grid representing the function
  84 + for(unsigned int xi = 0; xi < N; xi++){
  85 + for(unsigned int yi = 0; yi < N; yi++){
  86 +
  87 + //get the spherical coordinates for each grid point
  88 + theta = (2 * PI) * ((double)xi / (N-1));
  89 + phi = PI * ((double)yi / (N-1));
  90 +
  91 + //sum the contribution of the current spherical harmonic based on the coefficient
  92 + result = color.C[c] * SH(l, m, theta, phi);
  93 +
  94 + //store the result in a 2D array (which will later be used as a texture map)
  95 + func_color[yi * N + xi] += result;
  96 + }
  97 + }
  98 +
  99 + //keep track of m and l here
  100 + m++; //increment m
  101 + //if we're in a new tier, increment l and set m = -l
  102 + if(m > l){
  103 + l++;
  104 + m = -l;
  105 + }
  106 + }
  107 + }
  108 +
  109 + void gl_prep_draw(){
  110 +
  111 + //enable depth testing
  112 + //this has to be used instead of culling because the sphere can have negative values
  113 + glEnable(GL_DEPTH_TEST);
  114 + glDepthMask(GL_TRUE);
  115 + glEnable(GL_TEXTURE_2D); //enable 2D texture mapping
  116 + }
  117 +
  118 + //draw a texture mapped sphere representing the function surface
  119 + void gl_draw_sphere() {
  120 +
  121 + //bind the 2D texture representing the color map
  122 + glBindTexture(GL_TEXTURE_2D, color_tex);
  123 +
  124 + //Draw the Sphere
  125 + int i, j;
  126 +
  127 + for(i = 1; i <= N-1; i++) {
  128 + double phi0 = PI * ((double) (i - 1) / (N-1));
  129 + double phi1 = PI * ((double) i / (N-1));
  130 +
  131 + glBegin(GL_QUAD_STRIP);
  132 + for(j = 0; j <= N; j++) {
  133 +
  134 + //calculate the indices into the function array
  135 + int phi0_i = i-1;
  136 + int phi1_i = i;
  137 + int theta_i = j;
  138 + if(theta_i == N)
  139 + theta_i = 0;
  140 +
  141 + double v0 = func_surface[phi0_i * N + theta_i];
  142 + double v1 = func_surface[phi1_i * N + theta_i];
  143 +
  144 + v0 = fabs(v0);
  145 + v1 = fabs(v1);
  146 +
  147 +
  148 + double theta = 2 * PI * (double) (j - 1) / N;
  149 + double x0 = v0 * cos(theta) * sin(phi0);
  150 + double y0 = v0 * sin(theta) * sin(phi0);
  151 + double z0 = v0 * cos(phi0);
  152 +
  153 + double x1 = v1 * cos(theta) * sin(phi1);
  154 + double y1 = v1 * sin(theta) * sin(phi1);
  155 + double z1 = v1 * cos(phi1);
  156 +
  157 + glTexCoord2f(theta / (2 * PI), phi0 / PI);
  158 + glVertex3f(x0, y0, z0);
  159 +
  160 + glTexCoord2f(theta / (2 * PI), phi1 / PI);
  161 + glVertex3f(x1, y1, z1);
  162 + }
  163 + glEnd();
  164 + }
  165 +
  166 + glBindTexture(GL_TEXTURE_2D, 0);
  167 + }
  168 +
  169 + void gen_texture()
  170 + {
  171 + //allocate space for the color map
  172 + unsigned int bytes = N * N * sizeof(unsigned char) * 3;
  173 + unsigned char* color_image;
  174 + color_image = (unsigned char*) malloc(bytes);
  175 +
  176 + //generate a colormap from the function
  177 + stim::cpu2cpu<double>(func_color, color_image, N*N, stim::cmBrewer);
  178 +
  179 + //prep everything for drawing
  180 + gl_prep_draw();
  181 +
  182 + //generate an OpenGL texture map in the current context
  183 + glGenTextures(1, &color_tex);
  184 + //bind the texture
  185 + glBindTexture(GL_TEXTURE_2D, color_tex);
  186 +
  187 + //copy the color data from the buffer to the GPU
  188 + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, N, N, 0, GL_RGB, GL_UNSIGNED_BYTE, color_image);
  189 +
  190 + //initialize all of the texture parameters
  191 + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
  192 + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
  193 + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
  194 + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
  195 + glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
  196 +
  197 + //free the buffer
  198 + free(color_image);
  199 + }
  200 +
  201 + void gl_init(unsigned int n){
  202 +
  203 + //set the sphere resolution
  204 + N = n;
  205 +
  206 + //set up the surface data.
  207 + gen_surface();
  208 +
  209 + //set up the color data and generate the corresponding texture.
  210 + gen_color();
  211 + gen_texture();
  212 +
  213 + }
  214 +
  215 +public:
  216 + gl_spharmonics<T>(unsigned int n = 256) {
  217 + gl_init(n);
  218 + }
  219 +
  220 + gl_spharmonics<T>( stim::spharmonics<T> in_function, unsigned int n = 256)
  221 + {
  222 + surface = in_function;
  223 + color = in_function;
  224 + gl_init(n);
  225 + }
  226 +
  227 + gl_spharmonics<T>( stim::spharmonics<T> in_function, stim::spharmonics<T> in_color, unsigned int n = 256)
  228 + {
  229 + surface = in_function;
  230 + color = in_color;
  231 + gl_init(n);
  232 + }
  233 +
  234 + gl_spharmonics<T>& operator=(const spharmonics<T> rhs) {
  235 + gl_spharmonics<T> result(rhs.C.size());
  236 + result.C = spharmonics<T>::rhs.C;
  237 + return result;
  238 + }
  239 +
  240 + void glRender(){
  241 + //set all OpenGL parameters required for drawing
  242 + gl_prep_draw();
  243 +
  244 + //draw the sphere
  245 + gl_draw_sphere();
  246 + }
  247 +
  248 + void glInit(unsigned int n = 256){
  249 + gl_init(n);
  250 + }
  251 +}; //end gl_spharmonics
  252 +
  253 +
  254 +}; //end namespace stim
  255 +
  256 +
  257 +
  258 +
  259 +#endif
... ...
stim/visualization/gl_spharmonics.h
1 1 #ifndef STIM_GL_SPHARMONICS_H
2 2 #define STIM_GL_SPHARMONICS_H
3 3  
4   -#include <GL/gl.h>
5   -
  4 +#include <stim/math/spharmonics.h>
6 5 #include <stim/gl/error.h>
  6 +#include <stim/math/vec3.h>
  7 +#include <stim/math/constants.h>
7 8 #include <stim/visualization/colormap.h>
8   -#include <stim/math/spharmonics.h>
9   -
10   -namespace stim{
11   -
12   -
13   -template <typename T>
14   -class gl_spharmonics : public spharmonics<T>{
15   -
16   -protected:
17   - using spharmonics<T>::C;
18   - using spharmonics<T>::SH;
19   -
20   - T* func; //stores the raw function data (samples at each point)
21   -
22   - GLuint color_tex; //texture map that acts as a colormap for the spherical function
23   -
24   - unsigned int N; //resolution of the spherical grid
25   -
26   - void gen_function(){
27   -
28   - //initialize the function to zero
29   - memset(func, 0, sizeof(double) * N * N);
30   -
31   - double theta, phi;
32   - double result;
33   - int l, m;
34 9  
  10 +namespace stim {
  11 +
  12 + template<typename T>
  13 + class gl_spharmonics {
  14 +
  15 + GLuint dlist;
  16 + GLuint tex;
  17 +
  18 + bool displacement;
  19 + bool colormap;
  20 + bool magnitude;
  21 +
  22 + void init_tex() {
  23 + T* sfunc = (T*)malloc(N * N * sizeof(T)); //create a 2D array to store the spherical function
  24 + Sc.get_func(sfunc, N, N); //generate the spherical function based on the Sc coefficients
  25 + unsigned char* tex_buffer = (unsigned char*)malloc(3 * N * N); //create a buffer to store the texture map
  26 + stim::cpu2cpu<T>(sfunc, tex_buffer, N * N, stim::cmBrewer); //create a Brewer colormap based on the spherical function
  27 + stim::buffer2image(tex_buffer, "sfunc.ppm", N, N);
  28 +
  29 + if (tex) glDeleteTextures(1, &tex); //if a texture already exists, delete it
  30 + glGenTextures(1, &tex); //create a new texture and store the ID
  31 + glBindTexture(GL_TEXTURE_2D, tex); //bind the texture
  32 + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, N, N, 0, GL_RGB, GL_UNSIGNED_BYTE, tex_buffer); //copy the color data from the buffer to the GPU
  33 + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); //initialize all of the texture parameters
  34 + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
  35 + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
  36 + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
  37 + glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
  38 + }
35 39  
36   - l = m = 0;
37   - //for each coefficient
38   - for(unsigned int c = 0; c < C.size(); c++){
  40 + public:
  41 + stim::spharmonics<T> Sc; //spherical harmonic representing the color component
  42 + stim::spharmonics<T> Sd; //spherical harmonic representing the displacement component
  43 + size_t N;
  44 +
  45 + gl_spharmonics(size_t slices) {
  46 + N = slices;
  47 + dlist = 0; //initialize the display list index to zero (no list)
  48 + tex = 0; //initialize the texture index to zero (no texture)
  49 + displacement = true;
  50 + colormap = true;
  51 + magnitude = true;
  52 + }
39 53  
40   - //iterate through the entire 2D grid representing the function
41   - for(unsigned int xi = 0; xi < N; xi++){
42   - for(unsigned int yi = 0; yi < N; yi++){
  54 + ~gl_spharmonics() {
  55 + if (dlist) glDeleteLists(dlist, 1); //delete the display list when the object is destroyed
  56 + }
43 57  
44   - //get the spherical coordinates for each grid point
45   - theta = (2 * PI) * ((double)xi / (N-1));
46   - phi = PI * ((double)yi / (N-1));
  58 + /// This function renders the spherical harmonic to the current OpenGL context
  59 + void render() {
  60 + //glShadeModel(GL_FLAT);
  61 + glPushAttrib(GL_ENABLE_BIT);
  62 + glDisable(GL_CULL_FACE);
  63 +
  64 + if (!tex) {
  65 + init_tex();
  66 + }
47 67  
48   - //sum the contribution of the current spherical harmonic based on the coefficient
49   - result = C[c] * SH(l, m, theta, phi);
  68 + if (colormap) {
  69 + glEnable(GL_TEXTURE_2D);
  70 + glBindTexture(GL_TEXTURE_2D, tex);
  71 + }
50 72  
51   - //store the result in a 2D array (which will later be used as a texture map)
52   - func[yi * N + xi] += result;
  73 + if (!dlist) {
  74 + dlist = glGenLists(1);
  75 + glNewList(dlist, GL_COMPILE);
  76 + glPushAttrib(GL_ENABLE_BIT);
  77 + glEnable(GL_NORMALIZE);
  78 +
  79 + //Draw the Sphere
  80 + size_t theta_i, phi_i;
  81 + T d_theta = (T)stim::TAU / (T)N;
  82 + T d_phi = (T)stim::PI / (T)(N-1);
  83 +
  84 + for (phi_i = 1; phi_i < N; phi_i++) {
  85 + T phi = phi_i * d_phi;
  86 + glBegin(GL_QUAD_STRIP);
  87 + for (theta_i = 0; theta_i <= N; theta_i++) {
  88 + T theta = (N - theta_i) * d_theta;
  89 + float theta_t = 1 - (float)theta_i / (float)N;
  90 +
  91 + T r;
  92 + if (!displacement) r = 1; //if no displacement, set the r value to 1 (renders a unit sphere)
  93 + else r = Sd(theta, phi); //otherwise calculate the displacement value
  94 +
  95 + glColor3f(1.0f, 1.0f, 1.0f);
  96 + if (!colormap) { //if no colormap is being rendered
  97 + if (r < 0) glColor3f(1.0, 0.0, 0.0); //if r is negative, render it red
  98 + else glColor3f(0.0, 1.0, 0.0); //otherwise render in green
  99 + }
  100 + if (magnitude) { //if the magnitude is being displaced, calculate the magnitude of r
  101 + if (r < 0) r = -r;
  102 + }
  103 + stim::vec3<T> s(r, theta, phi);
  104 + stim::vec3<T> c = s.sph2cart();
  105 + stim::vec3<T> n; //allocate a value to store the normal
  106 + if (!displacement) n = c; //if there is no displacement, the normal is spherical
  107 + else n = Sd.dphi(theta, phi).cross(Sd.dtheta(theta, phi)); //otherwise calculate the normal as the cross product of derivatives
  108 +
  109 + glTexCoord2f(theta_t, (float)phi_i / (float)N);
  110 + //std::cout << theta_t <<" "<<(float)phi_i / (float)N << "----------------";
  111 + glNormal3f(n[0], n[1], n[2]);
  112 + glVertex3f(c[0], c[1], c[2]);
  113 +
  114 + T r1;
  115 + if (!displacement) r1 = 1;
  116 + else r1 = Sd(theta, phi - d_phi);
  117 + if (!colormap) { //if no colormap is being rendered
  118 + if (r1 < 0) glColor3f(1.0, 0.0, 0.0); //if r1 is negative, render it red
  119 + else glColor3f(0.0, 1.0, 0.0); //otherwise render in green
  120 + }
  121 + if (magnitude) { //if the magnitude is being rendered, calculate the magnitude of r
  122 + if (r1 < 0) r1 = -r1;
  123 + }
  124 + stim::vec3<T> s1(r1, theta, phi - d_phi);
  125 + stim::vec3<T> c1 = s1.sph2cart();
  126 + stim::vec3<T> n1;
  127 + if (!displacement) n1 = c1;
  128 + else n1 = Sd.dphi(theta, phi - d_phi).cross(Sd.dtheta(theta, phi - d_phi));
  129 +
  130 + //std::cout << theta_t << " " << (float)(phi_i - 1) / (float)N << std::endl;
  131 + glTexCoord2f(theta_t, 1.0/(2*(N)) + (float)(phi_i-1) / (float)N);
  132 + glNormal3f(n1[0], n1[1], n1[2]);
  133 + glVertex3f(c1[0], c1[1], c1[2]);
  134 + }
  135 + glEnd();
53 136 }
  137 + glPopAttrib();
  138 + glEndList();
54 139 }
  140 + glCallList(dlist); //call the display list to render
55 141  
56   - //keep track of m and l here
57   - m++; //increment m
58   - //if we're in a new tier, increment l and set m = -l
59   - if(m > l){
60   - l++;
61   - m = -l;
62   - }
  142 + glPopAttrib();
63 143 }
64   - }
65   -
66   - void gl_prep_draw(){
67   -
68   - //enable depth testing
69   - //this has to be used instead of culling because the sphere can have negative values
70   - glEnable(GL_DEPTH_TEST);
71   - glDepthMask(GL_TRUE);
72   - glEnable(GL_TEXTURE_2D); //enable 2D texture mapping
73   - }
74   -
75   - //draw a texture mapped sphere representing the function surface
76   - void gl_draw_sphere() {
77   -
78   - //bind the 2D texture representing the color map
79   - glBindTexture(GL_TEXTURE_2D, color_tex);
80   -
81   - //Draw the Sphere
82   - int i, j;
83   -
84   - for(i = 1; i <= N-1; i++) {
85   - double phi0 = PI * ((double) (i - 1) / (N-1));
86   - double phi1 = PI * ((double) i / (N-1));
87   -
88   - glBegin(GL_QUAD_STRIP);
89   - for(j = 0; j <= N; j++) {
90 144  
91   - //calculate the indices into the function array
92   - int phi0_i = i-1;
93   - int phi1_i = i;
94   - int theta_i = j;
95   - if(theta_i == N)
96   - theta_i = 0;
97   -
98   - double v0 = func[phi0_i * N + theta_i];
99   - double v1 = func[phi1_i * N + theta_i];
100   -
101   - v0 = fabs(v0);
102   - v1 = fabs(v1);
103   -
104   -
105   - double theta = 2 * PI * (double) (j - 1) / N;
106   - double x0 = v0 * cos(theta) * sin(phi0);
107   - double y0 = v0 * sin(theta) * sin(phi0);
108   - double z0 = v0 * cos(phi0);
109   -
110   - double x1 = v1 * cos(theta) * sin(phi1);
111   - double y1 = v1 * sin(theta) * sin(phi1);
112   - double z1 = v1 * cos(phi1);
113   -
114   - glTexCoord2f(theta / (2 * PI), phi0 / PI);
115   - glVertex3f(x0, y0, z0);
116   -
117   - glTexCoord2f(theta / (2 * PI), phi1 / PI);
118   - glVertex3f(x1, y1, z1);
119   - }
120   - glEnd();
  145 + /// Push a coefficient to the spherical harmonic - by default, push applies the component to both the displacement and color SH
  146 + void push(T coeff) {
  147 + Sd.push(coeff);
  148 + Sc.push(coeff);
121 149 }
122   - }
123   -
124   - void gl_init(unsigned int n){
125   -
126   - //set the sphere resolution
127   - N = n;
128   -
129   - //allocate space for the color map
130   - unsigned int bytes = N * N * sizeof(unsigned char) * 3;
131   - unsigned char* color_image;
132   - color_image = (unsigned char*) malloc(bytes);
133   -
134   - //allocate space for the function
135   - func = (double*) malloc(N * N * sizeof(double));
136 150  
137   - //generate a functional representation that will be used for the texture map and vertices
138   - gen_function();
139   -
140   - //generate a colormap from the function
141   - stim::cpu2cpu<double>(func, color_image, N*N, stim::cmBrewer);
142   -
143   - //prep everything for drawing
144   - gl_prep_draw();
145   -
146   - //generate an OpenGL texture map in the current context
147   - glGenTextures(1, &color_tex);
148   - //bind the texture
149   - glBindTexture(GL_TEXTURE_2D, color_tex);
150   -
151   - //copy the color data from the buffer to the GPU
152   - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, N, N, 0, GL_RGB, GL_UNSIGNED_BYTE, color_image);
  151 + /// Resize the spherical harmonic coefficient array
  152 + void resize(size_t s) {
  153 + Sd.resize(s);
  154 + Sc.resize(s);
  155 + }
153 156  
154   - //initialize all of the texture parameters
155   - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
156   - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
157   - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
158   - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
159   - glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
160   -
161   - //free the buffer
162   - free(color_image);
163   - }
164   -
165   -public:
166   - gl_spharmonics<T>() {
167   -
168   - }
169   -
170   - gl_spharmonics<T>(const spharmonics<T> copy) : gl_spharmonics<T>() {
171   - C = copy.C;
172   - }
173   -
174   - gl_spharmonics<T>& operator=(const spharmonics<T> rhs) {
175   - gl_spharmonics<T> result(rhs.C.size());
176   - result.C = spharmonics<T>::rhs.C;
177   - return result;
178   - }
179   -
180   - void glRender(){
181   - //set all OpenGL parameters required for drawing
182   - gl_prep_draw();
183   -
184   - //draw the sphere
185   - gl_draw_sphere();
186   - }
187   -
188   - void glInit(unsigned int n = 256){
189   - gl_init(n);
190   - gen_function();
191   - }
192   -
193   - //overload arithmetic operations
194   - gl_spharmonics<T> operator*(T rhs) const {
195   - gl_spharmonics<T> result(C.size()); //create a new sph erical harmonics object
196   - for (size_t c = 0; c < C.size(); c++) //for each coefficient
197   - result.C[c] = C[c] * rhs; //calculat e the factor and store the result in the new spharmonics object
198   - return result;
199   - }
200   -
201   - gl_spharmonics<T> operator+(gl_spharmonics<T> rhs) {
202   - size_t low = std::min(C.size(), rhs.C.size()); //store th e number of coefficients in the lowest object
203   - size_t high = std::max(C.size(), rhs.C.size()); //store th e number of coefficients in the result
204   - bool rhs_lowest = false; //true if rhs has the lowest number of coefficients
205   - if (rhs.C.size() < C.size()) rhs_lowest = true; //if rhs has a low er number of coefficients, set the flag
206   -
207   - gl_spharmonics<T> result(high); //create a new object
208   - size_t c;
209   - for (c = 0; c < low; c++) //perform the first batch of additions
210   - result.C[c] = C[c] + rhs.C[c]; // perform the addition
211   -
212   - for (c = low; c < high; c++) {
213   - if (rhs_lowest)
214   - result.C[c] = C[c];
215   - else
216   - result.C[c] = rhs.C[c];
  157 + /// Set a spherical harmonic coefficient to the given value
  158 + void setc(unsigned int c, T value) {
  159 + Sd.setc(c, value);
  160 + Sc.setc(c, value);
217 161 }
218   - return result;
219   - }
220 162  
221   - gl_spharmonics<T> operator-(gl_spharmonics<T> rhs) {
222   - return (*this) + (rhs * (T)(-1));
223   - }
  163 + void project(T* data, size_t x, size_t y, size_t nc) {
  164 + Sd.project(data, x, y, nc);
  165 + Sc = Sd;
  166 + }
224 167  
  168 + /// Project a set of samples onto the basis
  169 + void project(std::vector<vec3<float>>& vlist, size_t nc) {
  170 + Sd.project(vlist, nc);
  171 + Sc = Sd;
  172 + }
225 173  
  174 + /// Calculate a density function from a list of points in spherical coordinates
  175 + void pdf(std::vector<stim::vec3<T>>& vlist, size_t nc) {
  176 + Sd.pdf(vlist, nc);
  177 + Sc = Sd;
  178 + }
226 179  
227   -}; //end gl_spharmonics
  180 + void slices(size_t s) {
  181 + N = s;
  182 + }
228 183  
  184 + size_t slices() {
  185 + return N;
  186 + }
229 187  
230   -}; //end namespace stim
  188 + void rendermode(bool displace, bool color, bool mag = true) {
  189 + displacement = displace;
  190 + colormap = color;
  191 + magnitude = mag;
  192 + }
231 193  
  194 + };
  195 +}
232 196  
233 197  
234 198  
235   -#endif
  199 +#endif
236 200 \ No newline at end of file
... ...