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