#include <cstdlib>
#include <cmath>
using namespace std;
#include "Options.h"
#include "Ring.h"
#include "xpUtil.h"
#include "libplanet/Planet.h"
Ring::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,
Planet *p) : planet_(p),
shade_(shade),
sunLat_(sunlat),
sunLon_(sunlon)
{
r_out = outer_radius * 1.01; // fudge to agree better with Cassini
dr_b = (outer_radius - inner_radius) / num_bright;
dr_t = (outer_radius - inner_radius) / num_trans;
const int innerPadding = 100;
const int outerPadding = 20;
num_b = outerPadding + num_bright + innerPadding;
num_t = outerPadding + num_trans + innerPadding;
// brightness and transparency arrays are from the outside in
radius_b = new double[num_b];
for (int i = 0; i < num_b; i++)
radius_b[i] = r_out - i * dr_b;
brightness = new double[num_b];
// drop the brightness to 0 at the outer radius
for (int i = 0; i < outerPadding; i++)
{
double weight = ((double) i) / (outerPadding - 1);
brightness[i] = weight * ring_brightness[0];
}
for (int i = 0; i < num_bright; i++)
brightness[i + outerPadding] = ring_brightness[i];
for (int i = 0; i < innerPadding; i++)
brightness[outerPadding + num_bright + i] = ring_brightness[num_bright-1];
const double cos_sun_lat = cos(sunLat_);
for (int i = 0; i < num_b; i++)
brightness[i] *= cos_sun_lat;
radius_t = new double[num_t];
for (int i = 0; i < num_t; i++)
radius_t[i] = r_out - i * dr_t;
transparency = new double[num_t];
// bring the transparency up to 1 at the outer radius
for (int i = 0; i < outerPadding; i++)
{
double weight = ((double) i) / (outerPadding - 1);
transparency[i] = (1 - (1 - ring_transparency[0]) * weight);
}
for (int i = 0; i < num_trans; i++)
transparency[i + outerPadding] = ring_transparency[i];
// bring the transparency up to 1 at the inner radius
for (int i = 0; i < innerPadding; i++)
{
double weight = 1 - ((double) i) / (innerPadding - 1);
transparency[outerPadding + num_trans + i] = (1 - (1-ring_transparency[num_trans-1])
* weight);
}
planet_->XYZToPlanetaryXYZ(0, 0, 0, sunX_, sunY_, sunZ_);
}
Ring::~Ring()
{
delete [] radius_b;
delete [] brightness;
delete [] radius_t;
delete [] transparency;
}
/*
Given a subsolar point and a location on the planet surface, check
if the surface location can be in shadow by the rings, and if so,
return the ring radius.
*/
double
Ring::getShadowRadius(double lat, double lon)
{
// If this point is on the same side of the rings as the sun,
// there's no shadow.
if(sunLat_ * lat >= 0) return(-1);
const double rad = planet_->Radius(lat);
planet_->PlanetographicToPlanetocentric(lat, lon);
const double x = rad * cos(lat) * cos(lon);
const double y = rad * cos(lat) * sin(lon);
const double z = rad * sin(lat);
const double dist = z/sunZ_;
const double dx = x - sunX_ * dist;
const double dy = y - sunY_ * dist;
return(sqrt(dx * dx + dy * dy));
}
double
Ring::getBrightness(const double lon, const double r)
{
return(getValue(brightness, num_b, window_b, dr_b, r, lon));
}
double
Ring::getBrightness(const double lon, const double r, const double t)
{
double returnval;
if (t == 1.0)
{
returnval = 0;
}
else
{
returnval = getValue(transparency, num_t, window_t, dr_t, r, lon);
}
return(returnval);
}
double
Ring::getTransparency(const double r)
{
return(getValue(transparency, num_t, window_t, dr_t, r));
}
double
Ring::getValue(const double *array, const int size, const int window,
const double dr, const double r)
{
int i = static_cast<int> ((r_out - r)/dr);
if (i < 0 || i >= size) return(-1.0);
int j1 = i - window;
int j2 = i + window;
if (j1 < 0) j1 = 0;
if (j2 >= size) j2 = size - 1;
double sum = 0;
for (int j = j1; j < j2; j++) sum += array[j];
sum /= (j2 - j1);
return(sum);
}
double
Ring::getValue(const double *array, const int size, const int window,
const double dr, const double r, const double lon)
{
if (cos(lon-sunLon_) > -0.45)
return(getValue(array, size, window, dr, r));
int i = static_cast<int> ((r_out - r)/dr);
if (i < 0 || i >= size) return(-1.0);
int j1 = i - window;
int j2 = i + window;
if (j1 < 0) j1 = 0;
if (j2 >= size) j2 = size - 1;
double sum = 0;
double r0 = r;
const double cosLon = cos(lon);
const double sinLon = sin(lon);
for (int j = j1; j < j2; j++)
{
const double x = r0 * cosLon;
const double y = -r0 * sinLon;
const double z = 0;
if (planet_->IsInMyShadow(x, y, z))
sum += (shade_ * array[j]);
else
sum += array[j];
r0 += dr;
}
sum /= (j2 - j1);
return(sum);
}
// units for dist_per_pixel is planetary radii
void
Ring::setDistPerPixel(const double dist_per_pixel)
{
window_b = static_cast<int> (dist_per_pixel / dr_b + 0.5);
window_t = static_cast<int> (dist_per_pixel / dr_t + 0.5);
window_b = window_b/2 + 1;
window_t = window_t/2 + 1;
}
syntax highlighted by Code2HTML, v. 0.9.1