########################################################
# compatibility layer for local weather package
# Thorsten Renk, March 2011
########################################################

# function			purpose
#
# setDefaultCloudsOff		to remove the standard Flightgear 3d clouds
# setVisibility			to set the visibility to a given value
# setLift			to set lift to given value
# setRain			to set rain to a given value
# setSnow			to set snow to a given value
# setTurbulence			to set turbulence to a given value
# setTemperature		to set temperature to a given value
# setPressure			to set pressure to a given value
# setDewpoint			to set the dewpoint to a given value
# setLight			to set light saturation to given value
# setWind			to set wind
# setWindSmoothly 		to set the wind gradually across a second
# smooth_wind_loop		(helper function for setWindSmoothly)
# create_cloud			to place a single cloud into the scenery
# create_cloud_array		to place clouds from storage arrays into the scenery
# get_elevation			to get the terrain elevation at given coordinates
# get_elevation_vector		to get terrain elevation at given coordinate vector



# This file contains portability wrappers for the local weather system: 
#   http://wiki.flightgear.org/index.php/A_local_weather_system
#   
# This module is intended to provide a certain degree of backward compatibility for past 
# FlightGear releases, while sketching out the low level APIs used and required by the 
# local weather system, as these
# are being added to FlightGear.
#
# This file contains various workarounds for doing things that are currently not yet directly 
# supported by the core FlightGear/Nasal APIs (fgfs 2.0).
#
# Some of these workarounds are purely implemented in Nasal space, and may thus not provide sufficient
# performance in some situations.
#
# The goal is to move all such workarounds eventually  into this module, so that the high level weather modules
# only refer to this "compatibility layer" (using an "ideal API"), while this module handles 
# implementation details 
# and differences among different versions of FlightGear, so that key APIs can be ported to C++ space 
# for the sake
# of improving runtime performance and efficiency.
#
# This provides an abstraction layer that isolates the rest of the local weather system from low 
# level implementation details.
# 
# C++ developers who want to help improve the local weather system (or the FlightGear/Nasal 
# interface in general) should 
# check out this file (as well as the wiki page) for APIs or features that shall eventually be 
# re/implemented in C++ space for
# improving the local weather system.
#
# 
# This module provides a handful of helpers for dynamically querying the Nasal API of the running fgfs binary,
# so that it can make use of new APIs (where available), while still working with older fgfs versions.
#
# Note: The point of these helpers is that they should really only be used 
# by this module, and not in other parts/files of the 
# local weather system. Any hard coded special cases should be moved into this module.
#
# The compatibility layer is currently work in progress and will be extended as new Nasal 
# APIs are being added to FlightGear.

var weather_dynamics = nil;
var weather_tile_management = nil;
var compat_layer = nil;
var weather_tiles = nil;


_setlistener("/nasal/local_weather/loaded", func { 

compat_layer = local_weather;
weather_dynamics = local_weather;
weather_tile_management = local_weather;
weather_tiles = local_weather;


var result = "yes";

if (1==0) # no compatibility tests for 2.4 binary, it has the required features
	{
print("Compatibility layer: testing for hard coded support");

if (props.globals.getNode("/rendering/scene/saturation", 0) == nil)
	{result = "no"; features.can_set_light = 0;}
else
	{result = "yes"; features.can_set_light = 1;}
print("* can set light saturation:        "~result);


if (props.globals.getNode("/rendering/scene/scattering", 0) == nil)
	{result = "no"; features.can_set_scattering = 0;}
else
	{result = "yes"; features.can_set_scattering = 1;}
print("* can set horizon scattering:      "~result);

if (props.globals.getNode("/environment/terrain", 0) == nil)
	{result = "no"; features.terrain_presampling = 0;}
else
	{result = "yes"; features.terrain_presampling = 1;setprop("/environment/terrain/area[0]/enabled",1);}
print("* hard coded terrain presampling:  "~result);

if ((props.globals.getNode("/environment/terrain/area[0]/enabled",1).getBoolValue() == 1) and (features.terrain_presampling ==1))
	{result = "yes"; features.terrain_presampling_active = 1;}
else
	{result = "no"; features.terrain_presampling_active = 0;}
print("* terrain presampling initialized: "~result);


if (props.globals.getNode("/environment/config/enabled", 0) == nil)
	{result = "no"; features.can_disable_environment = 0;}
else
	{result = "yes"; features.can_disable_environment = 1;}
print("* can disable global weather:      "~result);


print("Compatibility layer: tests done.");
	}


# features of a 2.4 binary

features.can_set_light = 1;
features.can_set_scattering = 1;
features.terrain_presampling = 1;
features.terrain_presampling_active = 1;
features.can_disable_environment = 1;

# features of a current GIT binary

features.fast_geodinfo = 1;


# do actual startup()
local_weather.updateMenu();
local_weather.startup();

});





var setDefaultCloudsOff = func {

if (features.can_disable_environment == 1)
	{
	var layers = props.globals.getNode("/environment/clouds").getChildren("layer");
	
	foreach (l; layers)
		{
		l.getNode("coverage-type").setValue(5);
		}
	}
else
	{
	var layers = props.globals.getNode("/environment/clouds").getChildren("layer");

	foreach (l; layers)
		{
		l.getNode("coverage").setValue("clear");
		}
	}

if (local_weather.hardcoded_clouds_flag == 1) 
	{
	# we store that information ourselves, so this should be zero
	setprop("/environment/clouds/layer[0]/elevation-ft",0.0);
		
	# layer wrapping off
	setprop("/sim/rendering/clouds3d-wrap",0);

	}

}


####################################
# set visibility to given value
####################################

var setVisibility = func (vis) {

if (features.can_disable_environment == 1)
	{
	setprop("/environment/visibility-m",vis);
	}
else
	{
	# this is a workaround for systems which lack hard-coded support
	# essentially we update all entries in config and reinit environment

	var entries_aloft = props.globals.getNode("environment/config/aloft", 1).getChildren("entry");
	foreach (var e; entries_aloft) {
			e.getNode("visibility-m",1).setValue(vis);
			}

	var entries_boundary = props.globals.getNode("environment/config/boundary", 1).getChildren("entry");
	foreach (var e; entries_boundary) {
			e.getNode("visibility-m",1).setValue(vis);
			}
	fgcommand("reinit", props.Node.new({subsystem:"environment"}));
	}
}


var setVisibilitySmoothly = func (vis) {

if (features.can_disable_environment == 0)
	{setVisibility(vis); return;}

visibility_target = vis;
visibility_current = getprop("/environment/visibility-m");

if (smooth_visibility_loop_flag == 0)
	{
	smooth_visibility_loop_flag = 1;
	visibility_loop();
	}
}

var visibility_loop = func {

if (local_weather.local_weather_running_flag == 0) {return;}

if (visibility_target == visibility_current)
	{smooth_visibility_loop_flag = 0; return;}

if (visibility_target < visibility_current)
	{
	var vis_goal = visibility_target;
	if (vis_goal < 0.97 * visibility_current) {vis_goal = 0.97 * visibility_current;}
	}
else
	{
	var vis_goal = visibility_target;
	if (vis_goal > 1.03 * visibility_current) {vis_goal = 1.03 * visibility_current;}
	}
	
setprop("/environment/visibility-m",vis_goal);
visibility_current = vis_goal;	

settimer( func {visibility_loop(); },0);
}


####################################
# set thermal lift to given value
####################################

var setLift = func (lift) {

setprop("/environment/local-weather-lift-fps",lift);
	
}

####################################
# set rain to given value
####################################

var setRain = func (rain) {

if (features.can_disable_environment == 1)
	{
	setprop("/environment/rain-norm", rain);
	}
else
	{
	# setting the lowest cloud layer to 30.000 ft is a workaround
	# as rain is only created below that layer in default

	setprop("/environment/clouds/layer[0]/elevation-ft", 30000.0);
	setprop("/environment/metar/rain-norm",rain);
	}

}

####################################
# set snow to given value
####################################

var setSnow = func (snow) {

if (features.can_disable_environment == 1)
	{
	setprop("/environment/snow-norm", snow);
	}
else
	{
	# setting the lowest cloud layer to 30.000 ft is a workaround
	# as snow is only created below that layer in default

	setprop("environment/clouds/layer[0]/elevation-ft", 30000.0);
	setprop("environment/metar/snow-norm",snow);
	}
}


####################################
# set turbulence to given value
####################################

var setTurbulence = func (turbulence) {

if (features.can_disable_environment == 1)
	{
	setprop("/environment/turbulence/magnitude-norm",turbulence);
	setprop("/environment/turbulence/rate-hz",3.0);
	}

else
	{
	# this is a workaround for systems which lack hard-coded support
	# essentially we update all entries in config and reinit environment

	var entries_aloft = props.globals.getNode("environment/config/aloft", 1).getChildren("entry");
	foreach (var e; entries_aloft) {
			e.getNode("turbulence/magnitude-norm",1).setValue(turbulence);
			e.getNode("turbulence/rate-hz",1).setValue(3.0);
			e.getNode("turbulence/factor",1).setValue(1.0);
			}

	# turbulence is slightly reduced in boundary layers

	var entries_boundary = props.globals.getNode("environment/config/boundary", 1).getChildren("entry");
	var i = 1;
	foreach (var e; entries_boundary) {
			e.getNode("turbulence/magnitude-norm",1).setValue(turbulence * 0.25*i);
			e.getNode("turbulence/rate-hz",1).setValue(5.0);
			e.getNode("turbulence/factor",1).setValue(1.0);
			i = i + 1;
			}
	fgcommand("reinit", props.Node.new({subsystem:"environment"}));
	}
}


####################################
# set temperature to given value
####################################

var setTemperature = func (T) {

if (features.can_disable_environment == 1)
	{
	setprop("/environment/temperature-sea-level-degc",T);
	}
else
	{
	# this is a workaround for systems which lack hard-coded support
	# essentially we update the entry in config and reinit environment
	
	setprop(ec~"boundary/entry[0]/temperature-degc",T);
	fgcommand("reinit", props.Node.new({subsystem:"environment"}));
	}
}

####################################
# set pressure to given value
####################################

var setPressure = func (p) {

if (features.can_disable_environment == 1)
	{
	setprop("/environment/pressure-sea-level-inhg",p);
	}
else
	{
	# this is a workaround for systems which lack hard-coded support
	# essentially we update the entry in config and reinit environment

	setprop(ec~"boundary/entry[0]/pressure-sea-level-inhg",p);
	setprop(ec~"aloft/entry[0]/pressure-sea-level-inhg",p);
	fgcommand("reinit", props.Node.new({subsystem:"environment"}));
	}
}

####################################
# set dewpoint to given value
####################################

var setDewpoint = func (D) {

if (features.can_disable_environment == 1)
	{
	setprop("/environment/dewpoint-sea-level-degc",D);
	}
else
	{
	# this is a workaround for systems which lack hard-coded support
	# essentially we update the entry in config and reinit environment

	setprop(ec~"boundary/entry[0]/dewpoint-degc",D);
	fgcommand("reinit", props.Node.new({subsystem:"environment"}));
	}
}

####################################
# set light saturation to given value
####################################

var setLight = func (s) {

if (features.can_set_light == 1)
	{	
	setprop("/rendering/scene/saturation",s);
	}
}

var setLightSmoothly = func (s) {

if (features.can_set_light == 0)
	{return;}

light_target = s;
light_current = getprop("/rendering/scene/saturation");

if (smooth_light_loop_flag == 0)
	{
	smooth_light_loop_flag = 1;
	light_loop();
	}
}

var light_loop = func {

if (local_weather.local_weather_running_flag == 0) {return;}

if (light_target == light_current)
	{smooth_light_loop_flag = 0; return;}

if (light_target < light_current)
	{
	var light_goal = light_target;
	if (light_goal < 0.97 * light_current) {light_goal = 0.97 * light_current;}
	}
else
	{
	var light_goal = light_target;
	if (light_goal > 1.03 * light_current) {light_goal = 1.03 * light_current;}
	}
	
setprop("/rendering/scene/saturation",light_goal);
light_current = light_goal;	

settimer( func {light_loop(); },0);
}


####################################
# set horizon scattering
####################################

var setScattering = func (s) {

if (features.can_set_scattering == 1)
	{	
	setprop("/rendering/scene/scattering",s);
	}
}

####################################
# set overcast haze
####################################

var setOvercast = func (o) {

if (features.can_set_scattering == 1)
	{	
	setprop("/rendering/scene/overcast",o);
	}
}


####################################
# set skydome scattering parameters
####################################

var setSkydomeShader = func (r, m, d) {

setprop("/sim/rendering/rayleigh", r);
setprop("/sim/rendering/mie", m);
setprop("/sim/rendering/dome-density",d);

}

###########################################################
# set wind to given direction and speed
###########################################################


var setWind = func (dir, speed) {

if (features.can_disable_environment == 1)
	{
	setprop("/environment/wind-from-heading-deg",dir);
	setprop("/environment/wind-speed-kt",speed);
	}
else
	{
	# this is a workaround for systems which lack hard-coded support
	# essentially we update all entries in config and reinit environment
	
	var entries_aloft = props.globals.getNode("environment/config/aloft", 1).getChildren("entry");
	foreach (var e; entries_aloft) {
			e.getNode("wind-from-heading-deg",1).setValue(dir);
			e.getNode("wind-speed-kt",1).setValue(speed);
			}

	var entries_boundary = props.globals.getNode("environment/config/boundary", 1).getChildren("entry");
	foreach (var e; entries_boundary) {
			e.getNode("wind-from-heading-deg",1).setValue(dir);
			e.getNode("wind-speed-kt",1).setValue(speed);
			}

	fgcommand("reinit", props.Node.new({subsystem:"environment"}));
	}
}

###########################################################
# set wind smoothly to given direction and speed
# interpolating across several frames
###########################################################


var setWindSmoothly = func (dir, speed) {

if (features.can_disable_environment == 1)
	{
	setWind(dir, speed);
	}
else
	{

	var entries_aloft = props.globals.getNode("environment/config/aloft", 1).getChildren("entry");

	var dir_old = entries_aloft[0].getNode("wind-from-heading-deg",1).getValue();
	var speed_old = entries_aloft[0].getNode("wind-speed-kt",1).getValue();

	var dir = dir * math.pi/180.0;
	var dir_old = dir_old * math.pi/180.0;

	var vx = speed * math.sin(dir);
	var vx_old = speed_old * math.sin(dir_old);

	var vy = speed * math.cos(dir);
	var vy_old = speed_old * math.cos(dir_old);

	smooth_wind_loop(vx,vy,vx_old, vy_old, 4, 4);
	}

}


var smooth_wind_loop = func (vx, vy, vx_old, vy_old, counter, count_max) {

var time_delay = 0.9/count_max;

if (counter == 0) {return;}

var f = (counter -1)/count_max;

var vx_set = f * vx_old + (1-f) * vx;
var vy_set = f * vy_old + (1-f) * vy;

var speed_set = math.sqrt(vx_set * vx_set + vy_set * vy_set);
var dir_set = math.atan2(vx_set,vy_set) * 180.0/math.pi;

setWind(dir_set,speed_set);

settimer( func {smooth_wind_loop(vx,vy,vx_old,vy_old,counter-1, count_max); },time_delay);

}

###########################################################
# place a single cloud 
###########################################################

var create_cloud = func(path, lat, long, alt, heading) {

var tile_counter = getprop(lw~"tiles/tile-counter");
var buffer_flag = getprop(lw~"config/buffer-flag");
var d_max = weather_tile_management.cloud_view_distance + 1000.0;


# check if we deal with a convective cloud

var convective_flag = 0;

if (find("cumulus",path) != -1)
	{
	if ((find("alto",path) != -1) or (find("cirro", path) != -1) or (find("strato", path) != -1))
		{convective_flag = 0;}
	else if ((find("small",path) != -1) or (find("whisp",path) != -1)) 
		{convective_flag = 1;}
	else if (find("bottom",path) != -1) 
		{convective_flag = 4;}
	else	
		{convective_flag = 2;}
	
	}
else if (find("congestus",path) != -1)
	{
	if (find("bottom",path) != -1) 
		{convective_flag = 5;}
	else
		{convective_flag = 3;}
	} 

#print("path: ", path, " flag: ", convective_flag);

# first check if the cloud should be stored in the buffer
# we keep it if it is in visual range or at high altitude (where visual range is different)

if (buffer_flag == 1)
	{
	# calculate the distance to the aircraft
	var pos = geo.aircraft_position();
	var cpos = geo.Coord.new();
	cpos.set_latlon(lat,long,0.0);
	var d = pos.distance_to(cpos);
	
	if ((d > d_max) and (alt < 20000.0)) # we buffer the cloud
		{
		var b = weather_tile_management.cloudBuffer.new(lat, long, alt, path, heading, tile_counter, convective_flag);
		if (local_weather.dynamics_flag ==1) 
			{
			b.timestamp = weather_dynamics.time_lw;
			if (convective_flag !=0) # Cumulus clouds get some extra info
				{
				b.evolution_timestamp = cloud_evolution_timestamp;
				b.flt = cloud_flt;
				b.rel_alt = alt - cloud_mean_altitude;
				}
			}
		append(weather_tile_management.cloudBufferArray,b);
		return;
		}
	}

# now check if we are writing from the buffer, in this case change tile index
# to buffered one

if (getprop(lw~"tmp/buffer-status") == "placing")
	{
	#tile_counter = getprop(lw~"tmp/buffer-tile-index");
	tile_counter = buffered_tile_index;
	}



# if the cloud is not buffered, get property tree nodes and write it 
# into the scenery

var n = props.globals.getNode("local-weather/clouds", 1);
var c = n.getChild("tile",tile_counter,1);


var cloud_number = n.getNode("placement-index").getValue();
		for (var i = cloud_number; 1; i += 1)
			if (c.getChild("cloud", i, 0) == nil)
				break;
	cl = c.getChild("cloud", i, 1);
	n.getNode("placement-index").setValue(i);

	var placement_index = i;

var model_number = n.getNode("model-placement-index").getValue();
var m = props.globals.getNode("models", 1);
		for (var i = model_number; 1; i += 1)
			if (m.getChild("model", i, 0) == nil)
				break;
	model = m.getChild("model", i, 1);
	n.getNode("model-placement-index").setValue(i);	



var latN = cl.getNode("position/latitude-deg", 1); latN.setValue(lat);
var lonN = cl.getNode("position/longitude-deg", 1); lonN.setValue(long);
var altN = cl.getNode("position/altitude-ft", 1); altN.setValue(alt);
var hdgN = cl.getNode("orientation/true-heading-deg", 1); hdgN.setValue(heading);

cl.getNode("tile-index",1).setValue(tile_counter);

model.getNode("path", 1).setValue(path);
model.getNode("latitude-deg-prop", 1).setValue(latN.getPath());
model.getNode("longitude-deg-prop", 1).setValue(lonN.getPath());
model.getNode("elevation-ft-prop", 1).setValue(altN.getPath());
model.getNode("heading-deg-prop", 1).setValue(hdgN.getPath());
model.getNode("tile-index",1).setValue(tile_counter);
model.getNode("load", 1).remove();




# sort the cloud into the cloud hash array

if (buffer_flag == 1)
	{
	var cs = weather_tile_management.cloudScenery.new(tile_counter, convective_flag, cl, model);
	append(weather_tile_management.cloudSceneryArray,cs);
	}

# if weather dynamics is on, also create a timestamp property and sort the cloud hash into quadtree

if (local_weather.dynamics_flag == 1)
	{
	cs.timestamp = weather_dynamics.time_lw;
	cs.write_index = placement_index;

	if (convective_flag !=0) # Cumulus clouds get some extra info
			{
			cs.evolution_timestamp = cloud_evolution_timestamp;
			cs.flt = cloud_flt;
			cs.rel_alt = alt - cloud_mean_altitude;
			cs.target_alt = alt;
			}

	if (getprop(lw~"tmp/buffer-status") == "placing")
		{
		var blat = buffered_tile_latitude;
		var blon = buffered_tile_longitude;
		var alpha = buffered_tile_alpha;
		}
	else
		{
		var blat = getprop(lw~"tiles/tmp/latitude-deg");
		var blon = getprop(lw~"tiles/tmp/longitude-deg");
		var alpha = getprop(lw~"tmp/tile-orientation-deg");
		}
	weather_dynamics.sort_into_quadtree(blat, blon, alpha, lat, long, weather_dynamics.cloudQuadtrees[tile_counter-1], cs); 
	}

}


###########################################################
# place a single cloud using hard-coded system
###########################################################

var create_cloud_new = func(c) {



var tile_counter = getprop(lw~"tiles/tile-counter");
cloud_index = cloud_index + 1;

c.index = tile_counter;
c.cloud_index = cloud_index;

# write the actual cloud into the scenery


var p = props.Node.new({ "layer" : 0,
                         "index": cloud_index,
                         "lat-deg": c.lat,
                         "lon-deg": c.lon,
			 "min-sprite-width-m": c.min_width,
			 "max-sprite-width-m": c.max_width,
			 "min-sprite-height-m": c.min_height,
			 "max-sprite-height-m": c.max_height,
			 "num-sprites": c.n_sprites,
			 "bottom-shade": c.bottom_shade,
			 "texture": c.texture_sheet,
			 "num-textures-x": c.num_tex_x,
			 "num-textures-y": c.num_tex_y,
			 "min-cloud-width-m": c.min_cloud_width,
			 "max-cloud-width-m": c.min_cloud_width,
			 "min-cloud-height-m": c.min_cloud_height,	
			 "max-cloud-height-m": c.min_cloud_height,	
			 "z-scale": c.z_scale,
			 "height-map-texture": 0,
                         "alt-ft" :  c.alt});
fgcommand("add-cloud", p);

# print("alt: ", c.alt);

# add other management properties to the hash if dynamics is on

if (local_weather.dynamics_flag == 1)
	{
	c.timestamp = weather_dynamics.time_lw;
	}


# add cloud to array

append(weather_tile_management.cloudArray,c);
	

}



###########################################################
# place a cloud layer from arrays, split across frames 
###########################################################

var create_cloud_array = func (i, clouds_path, clouds_lat, clouds_lon, clouds_alt, clouds_orientation) {

if (getprop(lw~"tmp/thread-status") != "placing") {return;}
if (getprop(lw~"tmp/convective-status") != "idle") {return;}
if ((i < 0) or (i==0)) 
	{
	if (local_weather.debug_output_flag == 1) 
		{print("Cloud placement from array finished!"); }
	setprop(lw~"tmp/thread-status", "idle");

	# now set flag that tile has been completely processed
	var dir_index = props.globals.getNode(lw~"tiles/tmp/dir-index").getValue();

	props.globals.getNode(lw~"tiles").getChild("tile",dir_index).getNode("generated-flag").setValue(2);
	
	return;
	}


var k_max = 30;
var s = size(clouds_path);  

if (s < k_max) {k_max = s;}

for (var k = 0; k < k_max; k = k+1)
	{
	if (getprop(lw~"config/dynamics-flag") ==1)
		{
		cloud_mean_altitude = local_weather.clouds_mean_alt[s-k-1];
		cloud_flt = local_weather.clouds_flt[s-k-1];
		cloud_evolution_timestamp = local_weather.clouds_evolution_timestamp[s-k-1];
		}
	create_cloud(clouds_path[s-k-1], clouds_lat[s-k-1], clouds_lon[s-k-1], clouds_alt[s-k-1], clouds_orientation[s-k-1]);
	#create_cloud_new(clouds_path[s-k-1], clouds_lat[s-k-1], clouds_lon[s-k-1], clouds_alt[s-k-1], clouds_orientation[s-k-1]);
	}

setsize(clouds_path,s-k_max);
setsize(clouds_lat,s-k_max);
setsize(clouds_lon,s-k_max);
setsize(clouds_alt,s-k_max);
setsize(clouds_orientation,s-k_max);

if (getprop(lw~"config/dynamics-flag") ==1)
		{
		setsize(local_weather.clouds_mean_alt,s-k_max);
		setsize(local_weather.clouds_flt,s-k_max);
		setsize(local_weather.clouds_evolution_timestamp,s-k_max);
		}

settimer( func {create_cloud_array(i - k, clouds_path, clouds_lat, clouds_lon, clouds_alt, clouds_orientation ) }, 0 );
};








###########################################################
# get terrain elevation
###########################################################

var get_elevation = func (lat, lon) {

var info = geodinfo(lat, lon);
	if (info != nil) {var elevation = info[0] * local_weather.m_to_ft;}
	else {var elevation = -1.0; }


return elevation;
}

###########################################################
# get terrain elevation vector
###########################################################

var get_elevation_array = func (lat, lon) {

var elevation = [];
var n = size(lat);


for(var i = 0; i < n; i=i+1)
	{
	append(elevation, get_elevation(lat[i], lon[i]));
	}
	

return elevation;
}



############################################################
# global variables
############################################################

# some common abbreviations

var lw = "/local-weather/";
var ec = "/environment/config/";

# storage arrays for model vector

var mvec = [];
var msize = 0;

# loop flags and variables

var smooth_visibility_loop_flag = 0;

var visibility_target = 0.0;
var visibility_current = 0.0;

var smooth_light_loop_flag = 0;

var light_target = 0.0;
var light_current = 0.0;

# available hard-coded support

var features = {};

# globals to transmit info if clouds are written from buffer

var buffered_tile_latitude = 0.0;
var buffered_tile_longitude = 0.0;
var buffered_tile_alpha = 0.0;
var buffered_tile_index = 0;

# globals to handle additional info for Cumulus cloud dynamics

var cloud_mean_altitude = 0.0;
var cloud_flt = 0.0;
var cloud_evolution_timestamp = 0.0;

# globals to handle new cloud indexing

var cloud_index = 0;