/tags/xplanet-1.0.0/src/Ring.cpp

# · C++ · 288 lines · 215 code · 51 blank · 22 comment · 33 complexity · 7c9ae14c4debe38616fc10be448fa4f1 MD5 · raw file

  1. #include <cstdlib>
  2. #include <cmath>
  3. using namespace std;
  4. #include "Options.h"
  5. #include "Ring.h"
  6. #include "View.h"
  7. #include "xpUtil.h"
  8. #include "libplanet/Planet.h"
  9. Ring::Ring(const double inner_radius, const double outer_radius,
  10. const double planet_radius,
  11. const double *ring_brightness, const int num_bright,
  12. const double *ring_transparency, const int num_trans,
  13. const double sunlon, const double sunlat,
  14. const double shade,
  15. Planet *p, View *view) : shade_(shade)
  16. {
  17. r_out = outer_radius/planet_radius;
  18. dr_b = (outer_radius - inner_radius) / (num_bright * planet_radius);
  19. dr_t = (outer_radius - inner_radius) / (num_trans * planet_radius);
  20. const int innerPadding = 100;
  21. const int outerPadding = 20;
  22. num_b = outerPadding + num_bright + innerPadding;
  23. num_t = outerPadding + num_trans + innerPadding;
  24. // brightness and transparency arrays are from the outside in
  25. radius_b = new double[num_b];
  26. for (int i = 0; i < num_b; i++)
  27. radius_b[i] = r_out - i * dr_b;
  28. brightness = new double[num_b];
  29. // drop the brightness to 0 at the outer radius
  30. for (int i = 0; i < outerPadding; i++)
  31. {
  32. double weight = ((double) i) / (outerPadding - 1);
  33. brightness[i] = weight * ring_brightness[0];
  34. }
  35. for (int i = 0; i < num_bright; i++)
  36. brightness[i + outerPadding] = ring_brightness[i];
  37. for (int i = 0; i < innerPadding; i++)
  38. brightness[outerPadding + num_bright + i] = ring_brightness[num_bright-1];
  39. const double cos_sun_lat = cos(sunlat);
  40. for (int i = 0; i < num_b; i++)
  41. brightness[i] *= cos_sun_lat;
  42. radius_t = new double[num_t];
  43. for (int i = 0; i < num_t; i++)
  44. radius_t[i] = r_out - i * dr_t;
  45. transparency = new double[num_t];
  46. // bring the transparency up to 1 at the outer radius
  47. for (int i = 0; i < outerPadding; i++)
  48. {
  49. double weight = ((double) i) / (outerPadding - 1);
  50. transparency[i] = (1 - (1 - ring_transparency[0]) * weight);
  51. }
  52. for (int i = 0; i < num_trans; i++)
  53. transparency[i + outerPadding] = ring_transparency[i];
  54. // bring the transparency up to 1 at the inner radius
  55. for (int i = 0; i < innerPadding; i++)
  56. {
  57. double weight = 1 - ((double) i) / (innerPadding - 1);
  58. transparency[outerPadding + num_trans + i] = (1 - (1-ring_transparency[num_trans-1])
  59. * weight);
  60. }
  61. planet = p;
  62. sun_lon = sunlon;
  63. sun_lat = sunlat;
  64. sun_x = cos(sun_lat) * cos(sun_lon);
  65. sun_y = cos(sun_lat) * sin(sun_lon);
  66. sun_z = sin(sun_lat);
  67. sun_view = view;
  68. num_s = 180;
  69. shadow_cosangle = new double[num_s];
  70. shadow_radius = new double[num_s];
  71. }
  72. Ring::~Ring()
  73. {
  74. delete [] radius_b;
  75. delete [] brightness;
  76. delete [] radius_t;
  77. delete [] transparency;
  78. delete [] shadow_cosangle;
  79. delete [] shadow_radius;
  80. }
  81. // Compute the distance of the edge of planet's shadow on the rings as
  82. // a function of longitude.
  83. void
  84. Ring::buildShadowRadiusTable()
  85. {
  86. int i = 0, start_index = 0;
  87. double d_angle = M_PI/num_s;
  88. for (double angle = M_PI; angle > 0; angle -= d_angle)
  89. {
  90. shadow_cosangle[i] = cos(angle);
  91. shadow_radius[i] = 0;
  92. for (int j = start_index; j < num_t; j++)
  93. {
  94. if (isInShadow(sun_lon + angle, radius_t[j]))
  95. {
  96. shadow_radius[i] = radius_t[j];
  97. start_index = j;
  98. break;
  99. }
  100. }
  101. if (shadow_radius[i] == 0)
  102. {
  103. num_s = i;
  104. shadow_radius[i] = radius_t[num_t - 1];
  105. break;
  106. }
  107. i++;
  108. }
  109. }
  110. /*
  111. If the part of the ring at the specified longitude and radius
  112. isn't visible from the Sun, it's in shadow.
  113. */
  114. bool
  115. Ring::isInShadow(const double lon, double r)
  116. {
  117. double X, Y, Z;
  118. planet->PlanetocentricToXYZ(X, Y, Z, 0, lon, r);
  119. sun_view->XYZToPixel(0, X, Y, Z, X, Y, Z);
  120. double distsq = X*X + Y*Y;
  121. if (distsq < 1) return(true);
  122. return(false);
  123. }
  124. /*
  125. Given a subsolar point and a location on the planet surface, check
  126. if the surface location can be in shadow by the rings, and if so,
  127. return the ring radius.
  128. */
  129. double
  130. Ring::getShadowRadius(const double lat, const double lon)
  131. {
  132. // If this point is on the same side of the rings as the sun,
  133. // there's no shadow.
  134. if(sun_lat * lat >= 0) return(-1);
  135. const double x = cos(lat) * cos(lon);
  136. const double y = cos(lat) * sin(lon);
  137. const double z = sin(lat);
  138. const double dist = z/sun_z;
  139. const double dx = x - sun_x * dist;
  140. const double dy = y - sun_y * dist;
  141. return(sqrt(dx * dx + dy * dy));
  142. }
  143. /*
  144. Given a cos(longitude), return the radius of the outermost point of the
  145. planet's shadow on the ring.
  146. */
  147. double Ring::getShadowRadius(const double x)
  148. {
  149. for (int i = 0; i < num_s; i++)
  150. {
  151. if (shadow_cosangle[i] > x)
  152. {
  153. double frac = ((x - shadow_cosangle[i-1])
  154. /(shadow_cosangle[i] - shadow_cosangle[i-1]));
  155. double returnval = (shadow_radius[i-1]
  156. + frac * (shadow_radius[i]
  157. - shadow_radius[i-1]));
  158. return(returnval);
  159. }
  160. }
  161. return(0);
  162. }
  163. double
  164. Ring::getBrightness(const double lon, const double r)
  165. {
  166. return(getValue(brightness, num_b, window_b, dr_b, r, lon));
  167. }
  168. double
  169. Ring::getBrightness(const double lon, const double r, const double t)
  170. {
  171. double returnval;
  172. if (t == 1.0)
  173. {
  174. returnval = 0;
  175. }
  176. else
  177. {
  178. returnval = getValue(transparency, num_t, window_t, dr_t, r, lon);
  179. }
  180. return(returnval);
  181. }
  182. double
  183. Ring::getTransparency(const double r)
  184. {
  185. return(getValue(transparency, num_t, window_t, dr_t, r));
  186. }
  187. double
  188. Ring::getValue(const double *array, const int size, const int window,
  189. const double dr, const double r)
  190. {
  191. int i = (int) ((r_out - r)/dr);
  192. if (i < 0 || i >= size) return(-1.0);
  193. int j1 = i - window;
  194. int j2 = i + window;
  195. if (j1 < 0) j1 = 0;
  196. if (j2 >= size) j2 = size - 1;
  197. double sum = 0;
  198. for (int j = j1; j < j2; j++) sum += array[j];
  199. sum /= (j2 - j1);
  200. return(sum);
  201. }
  202. double
  203. Ring::getValue(const double *array, const int size, const int window,
  204. const double dr, const double r, const double lon)
  205. {
  206. double cos_lon = cos(lon-sun_lon);
  207. if (cos_lon > -0.45) return(getValue(array, size, window, dr, r));
  208. int i = (int) ((r_out - r)/dr);
  209. if (i < 0 || i >= size) return(-1.0);
  210. int j1 = i - window;
  211. int j2 = i + window;
  212. const int interval = j2 - j1;
  213. if (j1 < 0) j1 = 0;
  214. if (j2 >= size) j2 = size - 1;
  215. double shadow_rad = getShadowRadius(cos_lon);
  216. double r0 = r;
  217. double sum = 0;
  218. for (int j = j1; j < j2; j++)
  219. {
  220. if (r0 < shadow_rad)
  221. sum += (shade_ * array[j]);
  222. else
  223. sum += array[j];
  224. r0 += dr;
  225. }
  226. sum /= interval;
  227. return(sum);
  228. }
  229. // units for dist_per_pixel is planetary radii
  230. void
  231. Ring::setDistPerPixel(const double dist_per_pixel)
  232. {
  233. window_b = (int) (dist_per_pixel / dr_b + 0.5);
  234. window_t = (int) (dist_per_pixel / dr_t + 0.5);
  235. window_b = window_b/2 + 1;
  236. window_t = window_t/2 + 1;
  237. }