1 #include <cstdlib>
2 #include <cmath>
3 #include <map>
4 using namespace std;
5 
6 #include "Ring.h"
7 #include "xpUtil.h"
8 
9 #include "libplanet/Planet.h"
10 
Ring(const double inner_radius,const double outer_radius,const double * ring_brightness,const int num_bright,const double * ring_transparency,const int num_trans,const double sunlon,const double sunlat,const double shade,map<double,Planet * > & planetsFromSunMap,Planet * p)11 Ring::Ring(const double inner_radius, const double outer_radius,
12            const double *ring_brightness, const int num_bright,
13            const double *ring_transparency, const int num_trans,
14            const double sunlon, const double sunlat,
15            const double shade,
16            map<double, Planet *> &planetsFromSunMap,
17            Planet *p) : planet_(p),
18                         shade_(shade),
19                         sunLat_(sunlat),
20                         sunLon_(sunlon)
21 {
22     r_out = outer_radius * 1.01; // fudge to agree better with Cassini
23     dr_b = (outer_radius - inner_radius) / num_bright;
24     dr_t = (outer_radius - inner_radius) / num_trans;
25 
26     const int innerPadding = 100;
27     const int outerPadding = 20;
28     num_b = outerPadding + num_bright + innerPadding;
29     num_t = outerPadding + num_trans + innerPadding;
30 
31     // brightness and transparency arrays are from the outside in
32     radius_b = new double[num_b];
33     for (int i = 0; i < num_b; i++)
34         radius_b[i] = r_out - i * dr_b;
35 
36     brightness = new double[num_b];
37     // drop the brightness to 0 at the outer radius
38     for (int i = 0; i < outerPadding; i++)
39     {
40         double weight = ((double) i) / (outerPadding - 1);
41         brightness[i] = weight * ring_brightness[0];
42     }
43 
44     for (int i = 0; i < num_bright; i++)
45         brightness[i + outerPadding] = ring_brightness[i];
46 
47     for (int i = 0; i < innerPadding; i++)
48         brightness[outerPadding + num_bright + i] = ring_brightness[num_bright-1];
49 
50     radius_t = new double[num_t];
51     for (int i = 0; i < num_t; i++)
52         radius_t[i] = r_out - i * dr_t;
53 
54     transparency = new double[num_t];
55     // bring the transparency up to 1 at the outer radius
56     for (int i = 0; i < outerPadding; i++)
57     {
58         double weight = ((double) i) / (outerPadding - 1);
59         transparency[i] = (1 - (1 - ring_transparency[0]) * weight);
60     }
61 
62     for (int i = 0; i < num_trans; i++)
63         transparency[i + outerPadding] = ring_transparency[i];
64 
65     // bring the transparency up to 1 at the inner radius
66     for (int i = 0; i < innerPadding; i++)
67     {
68         double weight = 1 - ((double) i) / (innerPadding - 1);
69         transparency[outerPadding + num_trans + i] = (1 - (1-ring_transparency[num_trans-1])
70                                                     * weight);
71     }
72 
73     // make the rings darker as we get closer to equinox
74     const double sin_sun_lat_025 = pow(abs(sin(sunLat_)), 0.25);
75     for (int i = 0; i < num_b; i++)
76         brightness[i] *= sin_sun_lat_025;
77 
78     brightness_dark = new double[num_t];
79     for (int i = 0; i < num_t; i++) brightness_dark[i] = transparency[i] * sin_sun_lat_025;
80 
81     planet_->XYZToPlanetaryXYZ(0, 0, 0, sunX_, sunY_, sunZ_);
82 
83     planetsFromSunMap_.clear();
84     planetsFromSunMap_.insert(planetsFromSunMap.begin(), planetsFromSunMap.end());
85 }
86 
~Ring()87 Ring::~Ring()
88 {
89     delete [] radius_b;
90     delete [] brightness;
91 
92     delete [] radius_t;
93     delete [] transparency;
94 }
95 
96 /*
97   Given a subsolar point and a location on the planet surface, check
98   if the surface location can be in shadow by the rings, and if so,
99   return the ring radius.
100 */
101 double
getShadowRadius(double lat,double lon)102 Ring::getShadowRadius(double lat, double lon)
103 {
104     // If this point is on the same side of the rings as the sun,
105     // there's no shadow.
106     if(sunLat_ * lat >= 0) return(-1);
107 
108     const double rad = planet_->Radius(lat);
109     planet_->PlanetographicToPlanetocentric(lat, lon);
110 
111     const double x = rad * cos(lat) * cos(lon);
112     const double y = rad * cos(lat) * sin(lon);
113     const double z = rad * sin(lat);
114 
115     const double dist = z/sunZ_;
116 
117     const double dx = x - sunX_ * dist;
118     const double dy = y - sunY_ * dist;
119 
120     return(sqrt(dx * dx + dy * dy));
121 }
122 
123 double
getBrightness(const double lon,const double r)124 Ring::getBrightness(const double lon, const double r)
125 {
126     return(getValue(brightness, num_b, window_b, dr_b, r, lon));
127 }
128 
129 double
getBrightness(const double lon,const double r,const double t)130 Ring::getBrightness(const double lon, const double r, const double t)
131 {
132     double returnval;
133     if (t == 1.0)
134     {
135         returnval = 0;
136     }
137     else
138     {
139         returnval = getValue(brightness_dark, num_t, window_t, dr_t, r, lon);
140     }
141     return(returnval);
142 }
143 
144 double
getTransparency(const double r)145 Ring::getTransparency(const double r)
146 {
147     return(getValue(transparency, num_t, window_t, dr_t, r));
148 }
149 
150 double
getValue(const double * array,const int size,const int window,const double dr,const double r)151 Ring::getValue(const double *array, const int size, const int window,
152                const double dr, const double r)
153 {
154     int i = static_cast<int> ((r_out - r)/dr);
155 
156     if (i < 0 || i >= size) return(-1.0);
157 
158     int j1 = i - window;
159     int j2 = i + window;
160     if (j1 < 0) j1 = 0;
161     if (j2 >= size) j2 = size - 1;
162 
163     double sum = 0;
164     for (int j = j1; j < j2; j++) sum += array[j];
165     sum /= (j2 - j1);
166 
167     return(sum);
168 }
169 
170 double
getValue(const double * array,const int size,const int window,const double dr,const double r,const double lon)171 Ring::getValue(const double *array, const int size, const int window,
172                const double dr, const double r, const double lon)
173 {
174     int i = static_cast<int> ((r_out - r)/dr);
175 
176     if (i < 0 || i >= size) return(-1.0);
177 
178     int j1 = i - window;
179     int j2 = i + window;
180     if (j1 < 0) j1 = 0;
181     if (j2 >= size) j2 = size - 1;
182 
183     bool shaded[j2-j1];
184     for (int j = 0; j < j2-j1; j++) shaded[j] = false;
185 
186     const double cosLon = cos(lon);
187     const double sinLon = sin(lon);
188 
189     // planet's distance from the sun
190     double cX, cY, cZ;
191     planet_->getPosition(cX, cY, cZ);
192     double p_dist = sqrt(cX*cX + cY*cY + cZ*cZ);
193 
194     for (map<double, Planet *>::iterator it0 = planetsFromSunMap_.begin();
195          it0 != planetsFromSunMap_.end(); it0++)
196     {
197         Planet *p = it0->second;
198         if (p->Index() == planet_->Index())
199         {
200             // Only check behind the planet for its own shadow
201             if (cos(lon-sunLon_) > -0.45) break;
202         }
203         else if (p->Primary() == planet_->Index())
204         {
205             // Check that the planet-sun-satellite angle is
206             // smaller than the angular size of the rings seen
207             // from the sun
208 
209             double pX, pY, pZ;
210             p->getPosition(pX, pY, pZ);
211 
212             double sep = abs(acos(ndot(pX, pY, pZ, cX, cY, cZ)));
213 
214             if (sep > r_out * planet_->Radius() / p_dist) continue;
215         }
216         else
217         {
218             // Don't worry about this body shadowing the rings
219             continue;
220         }
221 
222         double r0 = r;
223         for (int j = j1; j < j2; j++)
224         {
225             if (shaded[j-j1]) continue;
226 
227             const double rX =  r0 * cosLon;
228             const double rY = -r0 * sinLon;
229             const double rZ =  0;
230 
231             double X, Y, Z;
232             planet_->PlanetaryXYZToXYZ(rX, rY, rZ, X, Y, Z);
233 
234             // convert this point on the rings to this planet's
235             // XYZ coordinate system
236             double thisX, thisY, thisZ;
237             p->XYZToPlanetaryXYZ(X, Y, Z, thisX, thisY, thisZ);
238 
239             if (p->IsInMyShadow(thisX, thisY, thisZ)) shaded[j-j1] = true;
240 
241             r0 += dr;
242         }
243         if (p->Index() == planet_->Index()) break;
244     }
245 
246     double sum = 0;
247     for (int j = j1; j < j2; j++)
248     {
249         if (shaded[j-j1])
250             sum += (shade_ * array[j]);
251         else
252             sum += array[j];
253     }
254     sum /= (j2 - j1);
255 
256     return(sum);
257 }
258 
259 // units for dist_per_pixel is planetary radii
260 void
setDistPerPixel(const double dist_per_pixel)261 Ring::setDistPerPixel(const double dist_per_pixel)
262 {
263     window_b = static_cast<int> (dist_per_pixel / dr_b + 0.5);
264     window_t = static_cast<int> (dist_per_pixel / dr_t + 0.5);
265 
266     window_b = window_b/2 + 1;
267     window_t = window_t/2 + 1;
268 }
269 
270