/**
* Name: Boids 3D Motion
* Author: 
* Description: This model shows the movement of boids following a goal, and creating a flock .  
*	The goal agent and the boids will move within the 3D space.
* Tags: gui, skill, 3d
*/
model boids_3D 
global torus: torus_environment{ 
	//Number of boids to create
	int number_of_agents parameter: 'Number of agents' <- 100 min: 1 max: 500;
	//Number of obstacles to create
	int number_of_obstacles parameter: 'Number of obstacles' <- 0 min: 0;
	//Size of the boids
	int boids_size parameter: 'Boids size' <- 50 min: 1;
	//Maximal speed of the boids
	float maximal_speed parameter: 'Maximal speed' <- 15.0 min: 0.1 max: 15.0;
	//Factor for the boids flock
	int cohesion_factor parameter: 'Cohesion Factor' <- 100; 
	int alignment_factor parameter: 'Alignment Factor' <- 100; 
	float minimal_distance parameter: 'Minimal Distance' <- 10.0; 
	//MAximal angle of turn for the boids
	int maximal_turn parameter: 'Maximal Turn' <- 90 min: 0 max: 359; 
	//environment parameters
	int width_and_height_of_environment parameter: 'Width/Height of the Environment' <- 800;  
	int z_max parameter: 'Z max of the Environment' <- 400;  
	bool torus_environment parameter: 'Toroidal Environment ?' <- false; 
	//Experiment parameter
	bool apply_cohesion <- true parameter: 'Apply Cohesion ?';
	bool apply_alignment <- true parameter: 'Apply Alignment ?';   
	bool apply_separation <- true parameter: 'Apply Separation ?';   
	bool apply_goal <- true parameter: 'Follow Goal ?'; 
	bool apply_wind <- true parameter: 'Apply Wind ?';     
	//Wind variable
	point wind_vector <- {0,0,0}  parameter: 'Direction of the wind';   
	//Duration of the goal
	int goal_duration <- 30 update: (goal_duration - 1); 
	//Location of the goal
	point goal <- {rnd (width_and_height_of_environment - 2) + 1, rnd (width_and_height_of_environment -2) + 1 ,(rnd(z_max - 2) + 1)}; 
	gif_file bird0_gif_file <- gif_file("../images/bird.gif");

	geometry shape <- cube(width_and_height_of_environment);
	init {
		//Creation of the boids agents that will be placed randomly within the environment
		create boids number: number_of_agents { 
			location <- {rnd (width_and_height_of_environment - 2) + 1, rnd (width_and_height_of_environment -2) + 1 , (rnd(z_max - 2) + 1)};
		} 
		//Creation of the goal
		create boids_goal {
			location <- goal;
		}
	}
}

//Species boids_goal that will represent the goal agent, using the skill moving
species boids_goal skills: [moving3D] {
	float range init: 20.0;
	
	//Reflex to make the goal agent wander in a certain amplitude and a certain speed, 
	//Respecting the minimal and maximal z values
	reflex wander { 
		do  wander amplitude: 45.0 speed: 20.0; 
		if (location.z) < 0 {
			location <- {location.x,location.y,0};
		} else if (location.z) > z_max {
			location <- {location.x,location.y,z_max};
		}
		goal <- location;
	}
	
	aspect default { 
		draw sphere(10) color: #red ;
	}
} 

//Species boids that will represent the boids agents, using the skill moving
species boids skills: [moving3D] {
	//Attribute for the speed of the boids
	float speed max: maximal_speed <- maximal_speed;
	//Range of sensing of the boids
	float range <- minimal_distance * 2;
	point velocity <- {0,0, 0} ;
	
	//List of the others boids in the range distance of the agent
	list others update: ((boids at_distance range)  - self);
	//Mass center of the "flock" represented as the other boids in the sensing range
	point mass_center update:  (length(others) > 0) ? (mean (others collect (each.location)) )  : location;
	
	//Reflex to apply separation
	reflex separation when: apply_separation {
		point acc <- {0,0,0};
		loop boid over: (boids at_distance (minimal_distance))  {
			acc <- acc - ((location of boid) - location);
		}  
		velocity <- velocity + acc;
	}
	//Reflex to apply alignment
	reflex alignment when: apply_alignment {
		point acc <- (length(others) > 0) ? (mean (others collect (each.velocity))) : {0.0,0.0,0.0};
		acc <- acc - velocity;
		velocity <- velocity + (acc / alignment_factor);
	}
	//Reflex to apply cohesion
	reflex cohesion when: apply_cohesion {
		point acc <- mass_center - location;
		acc <- acc / cohesion_factor;
		velocity <- velocity + acc; 
	}
	//Action to make the agent location within the environment
	action bounding {
		if (location.z) < 0 {
			location <- {location.x,location.y,0};
		} else if (location.z) > z_max {
			location <- {location.x,location.y,z_max};
		}
	}
	//Reflex to make the agent follow the goal
	reflex follow_goal when: apply_goal {
		velocity <- velocity + ((goal - location) / cohesion_factor);
	}
	//Reflex to apply the wind by using the vector of wind
	reflex wind when: apply_wind {
		velocity <- velocity + wind_vector;
	}
	//Action to make the agent moving
	action do_move {  
		if (((velocity.x) as int) = 0) and (((velocity.y) as int) = 0) and (((velocity.z) as int) = 0) {
			velocity <- {(rnd(4)) -2, (rnd(4)) - 2,  ((rnd(4)) - 2)} ; 
		}
		point old_location <- location;
		do goto target: location + velocity;
		velocity <- location - old_location;
	}
	//Reflex to move the agent, calling both bounding and do_move action
	reflex movement {
		do bounding;
		do do_move;
	}
	
	aspect sphere {
		draw sphere(10) color: #green;
	}
	
	aspect image {
		draw bird0_gif_file size: boids_size rotate: heading::(location - boids_goal[0].location) color: #black ;      
	}
}


experiment "3D" type: gui {
	
	
	output synchronized: true {
		
		display Sky1 type:opengl {
			species boids aspect: image;
			species boids_goal;	
		}
		

	}
}