/**
* Name: Eroding Vulcano
* Author: Alexis Drogoul - 2021
* Description: This is a model that shows how the physics engine works, especially with the definition of uneven terrains, the dynamic
* change of shapes of agents and the callback actions when contacts occur between agents. 
* A vulcano, situated at the highest point of a DEM, erupts and the lava, falling down on the ground, erodes every patch of terrain it touches. The 
* slope of the terrain evolves as more and more lava is produced (the epicenter of the eruption even changing when higher patches appear).
* Tags: physics_engine, 3D, grid
*/


model Vulcano
/**
 * The model is inheriting from 'physical_world' a special model species that provides access to the physics engine -- and the possibility
 * to manage physical agents. In this model, the world itself is not a physical agent
 */
global parent: physical_world {
	bool use_native <- true;
	//Step (in #sec) passed to the physics engine. The same step is used for the simulation and the physics engine. The accuracy and synchronization
	//between the two can be controlled by max_substeps. A too large step (e.g. 1#sec) would make the lava 'pass through' the ground (tunnel effect).
	//A too small (e.g. 0.01), while more accurate, would, given the velocity of the lava, slow everything down on useless computations.
	float step <-  0.05;
	//A boolean that controls whether or not the lava will erode the ground
	bool erosion;
	float uncertainty -> {rnd(10.0) - 5};
	// Support for display parameters
	bool show_legend;
	bool draw_inside;
	//Every step the world creates a lava agent near the top of the highest patch in the terrain. It is provided with an initial high vertical velocity.
	reflex flow {
		patches highest <- patches with_max_of each.grid_value;
		ask highest {
			create lava number: 1 {
				location <- {highest.location.x + uncertainty, highest.location.y + uncertainty, highest.grid_value + uncertainty};
				velocity <- velocity + {0,0,rnd(60) - 20};
			}
		}
	}
}

/**
 * The patches come as a grid created after a simple DEM file and each cell is provided with a 'static body' in the physical world. The whole grid represents
 * an approximate terrain (or heightmap). Since the patches are agents, they can individually respond to events or have their own behavior, making the whole 
 * a powerful way to describe dynamic environments.
 */
grid patches file: grid_file("../images/DEM/Volcano DEM.asc") skills: [static_body] {
	float friction <- 0.5;
	float restitution <- 0.2;
	
	//This action is a 'callback' action, one of the two (with 'contact_removed_with') called by the physics engine when a contact occurs between two agents. 
	// When redefined, it allows agents to react to contacts. Here, every new contact with a lava agent makes a patch decrease its height (grid_value) 
	// and that of its neigbors by a small amount, as well as stop the lava agent quite brutally (clearing all the forces applied to it) to imitate "stickiness"
	action contact_added_with (agent other) {
		if (erosion) {
			grid_value <- grid_value - 0.01;
			ask neighbors {
				grid_value <- grid_value - 0.005;
				do update_body;
			}
			do update_body;
		}
	}
	
	aspect default {
		if (draw_inside) {draw aabb wireframe: true border: #white;}
	}
}

/**
 * Species that represents the lava erupting from the vulcano. Their physical body will be a sphere, weighting 4#kg, offering no restitution but a lot of friction.
 */
species lava skills: [dynamic_body] {
	geometry shape <- sphere(0.75);
	float mass <- 1.0;
	rgb color <- one_of (brewer_colors("Reds"));
	float restitution <- 0.2;
	float friction <- 0.3;
	float damping <- 0.1;
	float angular_damping<-0.1;

	//When a lava agent falls from the edges of the world, it is removed from the simulation (and the physical world as well).		
	reflex manage_location when: location.z < -20 {
		do die;
	}
	
	aspect default {
		draw shape color: color; 
		if (draw_inside) {
			draw aabb color: #lightblue wireframe: true;
			draw line(location, location+velocity) color: #yellow end_arrow: 1 width: 1;
		}
	}

} 


experiment "3D view" type: gui {
	font title  <- font("Helvetica", 12, #bold);
	parameter "Show inside structures (velocities and aabbs)" var: draw_inside <- false;
	parameter "Better collision detection" var: accurate_collision_detection <- false;
	parameter "Enable erosion" var: erosion <- false;
	parameter "Show legend" var: show_legend <- true;
	output {
		
		display "3D" type: opengl axes: false background: #black camera:#from_up_front antialias: false {
			graphics title {
				if (show_legend) {
					draw "Average height " + (patches mean_of each.grid_value) with_precision 2 + " / # of lava agents " + length(lava) color: #white font: title at: {world.location.x, 100, patches max_of each.grid_value + 10} anchor: #center depth: 2 rotate: -20::{1,0,0};
				}
			}
			//The terrain is represented as a field (but could be equally represented as a grid		
			mesh patches  texture: image_file("../images/DEM/Volcano Texture.jpg") triangulation: true smooth: true;
			//We add to the representation the individual 'aabb's (axis-aligned bounding boxes) of the patches if 'draw_inside' is true
		 	species patches;
		 	//Finally, each lava agent is represented (with its velocity if 'draw_inside' is true)
			species lava;
		}

	}

}