private Vector3 RandomPointOnPlane(Vector3 position, Vector3 normal, float radius)
    {
        Vector3 randomPoint;
 
        do
        {
            randomPoint = Vector3.Cross(Random.insideUnitSphere, normal);
        } while (randomPoint == Vector3.zero);
       
        randomPoint.Normalize();
        randomPoint *= radius;
        randomPoint += position;
       
        return randomPoint;
    }

function quickSort(ary){
	var partition = [{start:0,end:ary.length-1}],
		pos,		//Current position of the partition
		pivot,		//Main Item to use to compare with the other items
		i,			//For loop, reuse variable
		tmp,		//tmp var to hold item when swopping them in the array
		posStart;	//save starting pos to help partition the current partition

	while(partition.length > 0){
		pos = partition.pop();	//Get a partition to process.
		posStart = pos.start;
		pivot = ary[pos.end];
		
		for(i = pos.start; i < pos.end; i++){
			//Swop the current item with the start item, then move the start position up.
			if(ary[i] <= pivot){
				tmp = ary[i];
				ary[i] = ary[pos.start];
				ary[pos.start] = tmp;
				pos.start++;
			}
		}

		//Now move the first item to the end then save the pivot to the start position
		//Because everything before the new start position should be less then the pivot.
		ary[pos.end] = ary[pos.start];
		ary[pos.start] = pivot;

		//Determine if can divide the current partition into sub partitions.
		if(posStart < pos.start-1)	partition.push( {start:posStart,end:pos.start-1} );	//Left Partition
		if(pos.start+1 < pos.end)	partition.push( {start:pos.start+1,end:pos.end} );	//Right Partition
	}
}




/*
let xhr = new XhrPromise();
xhr.get(url, "json").then( r=>console.log(r) ).catch(err=>console.log(err));
*/
class XhrPromise{
	constructor(){
		this.xhr = new XMLHttpRequest();
		this.xhr.addEventListener("load",		this.onXhrComplete.bind(this)	,false);
		this.xhr.addEventListener("error",		this.onXhrError.bind(this)		,false);
		this.xhr.addEventListener("abort",		this.onXhrAbort.bind(this)		,false);
		this.xhr.addEventListener("timeout",	this.onXhrTimeout.bind(this)	,false);
		
		this.inUse			= false;
		this.promiseReject	= null;
		this.promiseResolve	= null;

		//Start request in promise and save Resolve/Reject reference to call later
		//when the async xhr call is complete.
		this._onPromise = (resolve, reject)=>{
			this.inUse			= true;
			this.promiseResolve	= resolve;
			this.promiseReject	= reject;

			try{		
				this.xhr.send();
			}catch(err){
				console.log("xhr err",err);
				reject(err);
			}
		};
	}

	_complete(isSuccess, err){
		if(isSuccess){
			this.promiseResolve( this.xhr.response );
		}else{
			this.promiseReject( err );
		}

		this.inUse			= false;
		this.promiseReject	= null;
		this.promiseResolve	= null;
	}

	get(url, type="text"){ // "json"
		this.xhr.open("GET", url);
		this.xhr.responseType = type; //https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/responseType
		return new Promise(this._onPromise);
	}

	onXhrComplete(e){	//console.log( e, e.currentTarget.response );
		if(this.xhr.status != 200)
			this._complete( false, "http status : " + this.xhr.status + " " + this.xhr.statusText );
		else
			this._complete( true );
	}
	onXhrError(e){		this._complete( false ); console.log("onXhrError"); }
	onXhrAbort(e){		this._complete( false ); console.log("onXhrAbort"); }
	onXhrTimeout(e){	this._complete( false ); console.log("onXhrTimeout"); }
}