/*
*   ContourGenerator  -- Used to generate 2D contour paths from gridded 3D data.
*
*   Copyright (C) 2000-2002 by Joseph A. Huwaldt <jhuwaldt@knology.net>.
*   All rights reserved.
*   
*   This library is free software; you can redistribute it and/or
*   modify it under the terms of the GNU Library General Public
*   License as published by the Free Software Foundation; either
*   version 2 of the License, or (at your option) any later version.
*   
*   This library is distributed in the hope that it will be useful,
*   but WITHOUT ANY WARRANTY; without even the implied warranty of
*   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
*   Library General Public License for more details.
**/
//package jahuwaldt.plot;

import java.util.*;


/**
*  <p> An object used to generate a list of contour lines
*      or paths from a set of gridded three dimensional data.
*  </p>
*
*  <p> Based on contour_plot.c from NeXTcontour1.4 by Thomas H. Pulliam,
*      pulliam@rft29.nas.nasa.gov, MS 202A-1 NASA Ames Research Center,
*      Moffett Field, CA 94035.
*      I don't know how the original Fortran code looked like or where it came from,
*      other than that NeXTcontour1.4 is based on Pieter Bunings' PLOT3D package
*      for Computational Fluid Dynamics.
*  </p>
*
*  <p> Ported from C to Java by Joseph A. Huwaldt, November 16, 2000.  </p>
*
*  <p>  Modified by:  Joseph A. Huwaldt  </p>
*
*  @author  Joseph A. Huwaldt   Date:  November 11, 2000
*  @version November 23, 2000
**/
public class ContourGenerator {

	//	Debug flag.
	private static final boolean DEBUG = false;
	
	//	Error messages.
	private static final String kCancelMsg = "Method ContourGenerator.getContours() canceled by user.";
	private static final String kInconsistantArrMsg = "Inconsistant array sizes.";
	private static final String kArrSizeMsg = "Data arrays must have more than one row or column.";
	private static final String kNegLogDataMsg = "Function data must be > 0 for logarithmic intervals.";
	
	//	Path buffer size.
	private static final int kBufSize = 1000;
	
	//	The minimum number of points allowed in a contour path.
	private static final int kMinNumPoints = 3;
	
	//	A list of contour paths.
	private List pathList = new ArrayList();
	
	//	A flag to indicate that the contours have been computed or not.
	private boolean cCalculated = false;
	
	//	Data arrays used for generating the contours.
	private double[][] xArray, yArray, funcArray;
	
	//	Data arrays used when generating contours for 1D X & Y arrays.
	private double[] xArr1D, yArr1D;
	
	//	Array of contour attributes, one for each contour level.
	private ContourAttrib[] cAttr;
	
	//	The fraction of the task that is completed.
	private float fracComplete = 0;

	/**
	*  Used to indicate that the user wishes to cancel the calculation
	*  of contours.
	**/
	private boolean isCanceled = false;
	
	
	//	Variables in the original FORTRAN program.
	private double[] pathbufxt, pathbufyt;
	private int[] pathbufia;
	private int lnstrt;				//	lnstrt=1 indicates starting a new line.
	private int ignext;
	private int icont;				//	Current contour level index.
	private double cont;			//	The current contour level.
	private int iss, iee, jss, jee;	//	i & j start and end index values.
	private int ima;				//	ima tells which boundary region we are on.
	private int iae;				//	Index to last element in the IA list.
	private int ibeg, jbeg;
	private int gi, gj;				//	Indexes into data arrays.
	private double fij;				//	Data value at i,j in data array.
	private int idir;				//	Indicates current direction.
	private int np=0;				//	Number of points in current contour line.
	private double wx=0, wy=0;		//	Starting point of a contour line.
	
	
	/**
	*  Construct a ContourGenerator object using the specified data arrays
	*  and the specified attribute array.  This constructor allows you
	*  to use data on an uneven X, Y grid.
	*
	*  @param  xArr   2D array containing the grid x coordinate data.
	*  @param  yArr   2D array containing the grid y coordinate data.
	*  @param  fArr   2D array containing the grid function (z) data.
	*  @param  cAttr  Array containing attributes of the contour levels.
	**/
	public ContourGenerator(double[][] xArr, double[][] yArr, double[][] fArr, ContourAttrib[] cAttr) {

		//	Make sure input data is reasonable.
		if (yArr.length != xArr.length || yArr.length != fArr.length)
			throw new IllegalArgumentException(kInconsistantArrMsg);
		if (yArr[0].length != xArr[0].length || yArr[0].length != fArr[0].length)
			throw new IllegalArgumentException(kInconsistantArrMsg);
		if (xArr.length <= 1 || xArr[0].length <= 1)
			throw new IllegalArgumentException(kArrSizeMsg);

		this.cAttr = cAttr;
		xArray = xArr;
		yArray = yArr;
		funcArray = fArr;
		
	}
	
	/**
	*  Construct a ContourGenerator object using the specified data arrays
	*  and the specified attribute array.  This constructor allows you
	*  to use data on an evenly spaced grid where "X" values are invarient
	*  with "Y" and "Y" values are invarient with "X".  This often occures
	*  where the data is on an evenly spaced cartesian grid.
	*
	*  @param  xArr   1D array containing the grid x coordinate data.
	*  @param  yArr   1D array containing the grid y coordinate data.
	*  @param  fArr   2D array containing the grid function (z) data.
	*  @param  cAttr  Array containing attributes of the contour levels.
	**/
	public ContourGenerator(double[] xArr, double[] yArr, double[][] fArr, ContourAttrib[] cAttr) {

		//	Make sure input data is reasonable.
		if (yArr.length != fArr.length || xArr.length != fArr[0].length)
			throw new IllegalArgumentException(kInconsistantArrMsg);
		if (xArr.length <= 1)
			throw new IllegalArgumentException(kArrSizeMsg);

		this.cAttr = cAttr;
		xArr1D = xArr;
		yArr1D = yArr;
		funcArray = fArr;
	}
	
	/**
	*  Construct a ContourGenerator object using the specified data arrays.
	*  Contour attributes, including the interval, are generated
	*  automatically.  This constructor allows you to use data on an
	*  uneven X, Y grid.
	*
	*  @param  xArr   2D array containing the grid x coordinate data.
	*  @param  yArr   2D array containing the grid y coordinate data.
	*  @param  fArr   2D array containing the grid function (z) data.
	*  @param  nc     The number of contour levels to generate.
	*  @param  logInterval  Uses a logarithmic contour interval if true, and
	*                       uses a linear interval if false.
	**/
	public ContourGenerator(double[][] xArr, double[][] yArr, double[][] fArr,
								int nc, boolean logInterval) {
		
		//	Make sure input data is reasonable.
		if (yArr.length != xArr.length || yArr.length != fArr.length)
			throw new IllegalArgumentException(kInconsistantArrMsg);
		if (yArr[0].length != xArr[0].length || yArr[0].length != fArr[0].length)
			throw new IllegalArgumentException(kInconsistantArrMsg);
		if (xArr.length <= 1 || xArr[0].length <= 1)
			throw new IllegalArgumentException(kArrSizeMsg);

		xArray = xArr;
		yArray = yArr;
		funcArray = fArr;
		
		if (logInterval)
			findLogIntervals(nc);
		else
			findLinearIntervals(nc);
	}
	
	/**
	*  Construct a ContourGenerator object using the specified data arrays.
	*  Contour attributes, including the interval, are generated
	*  automatically.  This constructor allows you
	*  to use data on an evenly spaced grid where "X" values are invarient
	*  with "Y" and "Y" values are invarient with "X".  This often occures
	*  where the data is on an evenly spaced cartesian grid.
	*
	*  @param  xArr   1D array containing the grid x coordinate data.
	*  @param  yArr   1D array containing the grid y coordinate data.
	*  @param  fArr   2D array containing the grid function (z) data.
	*  @param  nc     The number of contour levels to generate.
	*  @param  logInterval  Uses a logarithmic contour interval if true, and
	*                       uses a linear interval if false.
	**/
	public ContourGenerator(double[] xArr, double[] yArr, double[][] fArr,
								int nc, boolean logInterval) {
		
		//	Make sure input data is reasonable.
		if (yArr.length != fArr.length || xArr.length != fArr[0].length)
			throw new IllegalArgumentException(kInconsistantArrMsg);
		if (xArr.length <= 1)
			throw new IllegalArgumentException(kArrSizeMsg);

		xArr1D = xArr;
		yArr1D = yArr;
		funcArray = fArr;
		
		if (logInterval)
			findLogIntervals(nc);
		else
			findLinearIntervals(nc);
	}
	
	
	/**
	*  Generate the contour paths and return them as an array
	*  of ContourPath objects. If there is a lot of data, this method
	*  method may take a long time, so be patient.  Progress can be
	*  checked from another thread by calling "getProgress()".
	*
	*  @return An array of contour path objects.
	*  @throws InterruptedException if the user cancels this process
	*          (by calling "cancel()" from another thread).
	**/
	public ContourPath[] getContours() throws InterruptedException {
		
		if (!cCalculated) {
			isCanceled = false;
			pathList.clear();
			
			//	Go off an compute the contour paths.
			computeContours();
			
			//	Now turn loose all our data arrays to be garbage collected.
			cAttr = null;
			xArray = yArray = funcArray = null;
			xArr1D = yArr1D = null;
			
			//	Set our "done" flags.
			cCalculated = true;
			fracComplete = 1;
		}
		
		//	Turn our pathList into an array and return the array.
		int size = pathList.size();
		ContourPath[] arr = new ContourPath[size];
		for (int i=0; i < size; ++i)
			arr[i] = (ContourPath)pathList.get(i);
				
		return arr;
	}
	
	/**
	*  Returns true if the contour generation process is done.  False if it is not.
	**/
	public boolean done() {
		return cCalculated;
	}
	
	/**
	*  Call this method to cancel the generation of contours.
	**/
	public void cancel() {
		isCanceled = true;
	}
	
	/**
	*  Returns the progress of the currently executing contour generation
	*  process: 0.0 (just starting) to 1.0 (done).
	**/
	public float getProgress() {
		return fracComplete;
	}
	

	/**
	*  Find contour intervals that are linearly spaced through the data.
	**/
	private void findLinearIntervals(int nc) {
	
		//	Find min and max Z values.
		double zMin = Double.MAX_VALUE;	
		double zMax = -zMin;
		int ni = funcArray.length;
		for (int i=0; i < ni; ++i) {
			int nj = funcArray[i].length;
			for (int j=0; j < nj; ++j) {
				double zVal = funcArray[i][j];
				zMin = Math.min(zMin, zVal);
				zMax = Math.max(zMax, zVal);
			}
		}
		
		//	Allocate memory for contour attribute array.
		cAttr = new ContourAttrib[nc];
		
		//	Determine contour levels.
		double delta = (zMax-zMin)/(nc+1);
		for (int i=0; i < nc; i++) {
			cAttr[i] = new ContourAttrib( zMin + (i+1)*delta );
			if (DEBUG)
				System.out.println("level[" + i + "] = " + (zMin + (i+1)*delta));
		}

	}
	
	/**
	*  Find contour intervals that are logarithmically spaced through the data.
	**/
	private void findLogIntervals(int nc) {
	
		//	Find min and max Z values.
		double zMin = Double.MAX_VALUE;	
		double zMax = -zMin;
		int ni = funcArray.length;
		for (int i=0; i < ni; ++i) {
			int nj = funcArray[i].length;
			for (int j=0; j < nj; ++j) {
				double zVal = funcArray[i][j];
				zMin = Math.min(zMin, zVal);
				zMax = Math.max(zMax, zVal);
			}
		}
		
		if (zMin < 0)
			throw new IllegalArgumentException(kNegLogDataMsg);

		//	Allocate memory for contour attribute array.
		cAttr = new ContourAttrib[nc];
		
		//	Determine contour levels.
		double temp = Math.log(zMin);
		double delta = (Math.log(zMax) - temp)/(nc+1);
		for (int i=0; i < nc; i++)
			cAttr[i] = new ContourAttrib( Math.exp(temp + (i+1)*delta) );
		
	}
	

	/**
	*  Computes contour lines for gridded data and stores information about
	*  those contours.  The result of this routine is a list of contour lines
	*  or paths.
	**/
	private void computeContours() throws InterruptedException {

		int ncont = cAttr.length;		//	Number of contour levels.
		
		//	Find the number of data points in "I" and "J" directions.
		int nx=0, ny=0;
		if (xArray != null) {
			ny = xArray.length;
			nx = xArray[0].length;
		} else {
			nx = xArr1D.length;
			ny = yArr1D.length;
		}
		
		//	Allocate temporary storage space for path buffers.
		pathbufxt = new double[kBufSize];
		pathbufyt = new double[kBufSize];
		pathbufia = new int[kBufSize*3];

		//	lnstrt=1 (line start) means we're starting a new line.
		lnstrt = 1;
		ignext = 0;

		//	Loop through each contour level.
		for (icont = 0; icont < ncont; ++icont) {

			//	Check to see if the user has canceled.
			if (isCanceled)
				throw new InterruptedException(kCancelMsg);

			//	Begin working on this contour level.
			cont = cAttr[icont].getLevel();
			iss = 1;
			iee = nx;
			jss = 1;
			jee = ny;

			boolean subDivFlg = false;
/*L110*/	do {
				//	Find where function increases through the contour level.
				FlagContourPassings();

				boolean L10flg = false;
/*L210*/		do {

					if (!L10flg) {
						/*	Search along the boundaries for contour line starts.
						*	IMA tells which boundary of the region we're on.
						*/
						ima = 1;
						ibeg = iss - 1;
						jbeg = jss;
					}

/*L6*/				imaLoop:
					do {

						if (!L10flg) {
							boolean imb = false;
							boolean doneFlg = false;
							do {
							
								switch(ima) {
									case 1:
										++ibeg;
										if (ibeg == iee)
											ima = 2;
										break;
									
									case 2:
										++jbeg;
										if (jbeg == jee)
											ima = 3;
										break;
										
									case 3:
										--ibeg;
										if (ibeg == iss)
											ima = 4;
										break;
										
									case 4:
										--jbeg;
										if (jbeg == jss)
											ima = 5;
										break;
										
									case 5:
										continue imaLoop;
								}
							
								if (funcArray[jbeg -1][ibeg -1] <= cont) {
									imb = true;
									doneFlg = false;
									
								} else if (imb == true)
									doneFlg = true;
								
							} while (!doneFlg);
							
							//	Got a start point.
							gi = ibeg;							//	x index of starting point.
							gj = jbeg;							//	y index of starting point.
							fij = funcArray[jbeg -1][ibeg -1];	//	z value of starting point.

							//	Round the corner if necessary.
							/*	Look different directions to see which way the contour line
							*	went:
							*			  4
							*			1-|-3
							*			  2
							*/
							switch (ima) {
								case 1:
									Routine_L21();
									break;
									
								case 2:
									if (gj != jss) {
										if (!Routine_L31())
											Routine_L21();
									} else
										Routine_L21();
									break;

								case 3:
									if (gi != iee) {
										if (!Routine_L41())
											Routine_L21();
									} else {
										if (!Routine_L31())
											Routine_L21();
									}
									break;

								case 4:
									if (gj != jee) {
										if (!Routine_L51())
											Routine_L21();
									} else {
										if (!Routine_L41())
											Routine_L21();
									}
									break;

								case 5:
									if (!Routine_L51())
										Routine_L21();
									break;
							}
							
						}	//	end if(!L10flg)
					

						//	This is the end of a contour line.  After this, we'll start a
						//	new line.
						L10flg = false;
/*L90*/					lnstrt = 1;						//	Contour line start flag.
						ignext = 0;
						accumContour(np, icont, pathbufxt, pathbufyt, cAttr[icont]);
						
						//	If we're not done looking along the boundaries,
						//	go look there some more.
					} while (ima != 5);
					
					
					//	Otherwise, get the next start out of IA.
/*L91*/				if (iae != 0) {
						int ntmp3 = iae;
						for (int iia = 1; iia <= ntmp3; ++iia) {
							if (pathbufia[iia -1] != 0) {
								//	This is how we start in the middle of the region, using IA.
								gi = pathbufia[iia - 1]/1000;
								gj = pathbufia[iia - 1] - gi*1000;
								fij = funcArray[gj -1][gi -1];
								pathbufia[iia - 1] = 0;
								
								Routine_L21();
								
								L10flg = true;
								break;
							}
						}
					}
					
				} while ( L10flg );
				
				/*	And if there are no more of these, we're done with this region.
				*   If we've subdivided, update the region pointers and go back for more.
				*/
				subDivFlg = false;
				if (iee == nx) {
					if (jee != ny) {
						jss = jee;
						jee = ny;
						subDivFlg = true;
					}
				} else {
					iss = iee;
					iee = nx;
					subDivFlg = true;
				}
				
			} while (subDivFlg);


			//	Update progress information.
			fracComplete = (float)(icont+1)/(float)(ncont);
			
			//	Loop back for the next contour level.
		}	// Next icont


		//	Turn loose temporary arrays used to generate contours.
		pathbufxt = null;
		pathbufyt = null;
		pathbufia = null;		
		
	}


	/**
	*  Flag points in IA where the the function increases through the contour
	*  level, not including the boundaries.  This is so we have a list of at least
	*  one point on each contour line that doesn't intersect a boundary.
	**/
	private void FlagContourPassings() {

		iae = 0;
		int ntmp2 = jee - 1;
		for (int j=jss + 1; j <= ntmp2; ++j) {
			boolean imb = false;
			int iaend = iae;
			int ntmp3 = iee;
			for (int i=iss; i <= ntmp3; ++i) {
				if (funcArray[j -1][i -1] <= cont)
					imb = true;
				else if (imb == true) {
					++iae;
					pathbufia[iae - 1] = i*1000 + j;
					imb = false;

					/*  Check if the IA array is full.  If so, the subdividing
					*   algorithm goes like this:  if we've marked at least one
					*   J row, drop back to the last completed J and call that
					*   the region.  If we haven't even finished one J row, our
					*   region just extends to this I location.
					*/
					if (iae == kBufSize*3) {
						if (j > jss + 1) {
							iae = iaend;
							jee = j;
						} else {
							//	Compute minimum.
							jee = Math.min(j+1, jee);
							iee = i;
						}
						
						//	Break out of i & j loops.
						return;
					}
				}
			}	//	Next i
		}	//	Next j

	}

	/**
	*  This function represents the block of code in the original
	*  FORTRAN program that comes after line 21.
	**/
	private void Routine_L21() {
		while (true) {
			--gi;
			if (gi < iss)
				return;						//	Goto L90.

			idir = 1;
			if (funcArray[gj -1][gi -1] <= cont) {
				//	Wipe this point out of IA if it's in the list.
/*L52*/			if (iae != 0) {
					int ij = gi*1000 + gj + 1000;
					int ntmp3 = iae;
					for (int iia = 1; iia <= ntmp3; ++iia) {
						if (pathbufia[iia - 1] == ij) {
							pathbufia[iia - 1] = 0;
							break;
						}
					}
				}
				doInterpolation();
				return;						//	Goto L90.
			}

			fij = funcArray[gj -1][gi -1];

			if (Routine_L31())	return;		//	Goto L90
		}
	}

	/**
	*  This function represents the block of code in the original
	*  FORTRAN program that comes after line 31.
	**/
	private boolean Routine_L31() {
		--gj;
		if (gj < jss)
			return true;

		idir = 2;
		if (funcArray[gj -1][gi -1] <= cont) {
			doInterpolation();
			return true;
		}

		fij = funcArray[gj -1][gi -1];

		return (Routine_L41());
	}

	/**
	*  This function represents the block of code in the original
	*  FORTRAN program that comes after line 41.
	**/
	private boolean Routine_L41() {
		++gi;
		if (gi > iee)
			return true;

		idir = 3;
		if (funcArray[gj -1][gi -1] <= cont) {
			doInterpolation();
			return true;
		}

		fij = funcArray[gj -1][gi -1];

		return (Routine_L51());
	}

	/**
	*  This function represents the block of code in the original
	*  FORTRAN program that comes after line 51.
	**/
	private boolean Routine_L51() {
		++gj;
		idir = 4;
		if (gj > jee)
			return true;

		if (funcArray[gj -1][gi -1] <= cont) {
			doInterpolation();
			return true;
		}

		fij = funcArray[gj -1][gi -1];

		return false;
	}

	/**
	*  Do interpolation for X, Y coordinates.
	*
	*  This function represents the block of code in the original
	*  FORTRAN program that comes after line 60.
	**/
	private void doInterpolation() {

		//	Do interpolation for X,Y coordinates.
		double func = funcArray[gj -1][gi -1];
		double xyf = (cont - func)/(fij - func);
		
		/*  This tests for a contour point coinciding with a grid point.  In this case
		 *  the contour routine comes up with the same physical coordinate twice.  If
		 *  If we don't trap it, it can (in some cases significantly) increase the
		 *  number of points in a contour line.  Also, if this happens on the first
		 *  point in a line, the second point could be misinterpreted as the end of a
		 *   (circling) contour line.
		 */
		if (xyf == 0)
			++ignext;

		double wxx=0, wyy=0;
		double xVal=0, yVal=0;
		if (xArray != null) {
			//	We have 2D arrays for the X & Y grid points.
			xVal = xArray[gj -1][gi -1];
			yVal = yArray[gj -1][gi -1];
			switch (idir) {
				case 1:				//	East
					wxx = xVal + xyf*(xArray[gj -1][gi + 1 -1] - xVal);
					wyy = yVal + xyf*(yArray[gj -1][gi + 1 -1] - yVal);
					break;

				case 2:				//	North
					wxx = xVal + xyf*(xArray[gj + 1 -1][gi -1] - xVal);
					wyy = yVal + xyf*(yArray[gj + 1 -1][gi -1] - yVal);
					break;

				case 3:				//	West
					wxx = xVal + xyf*(xArray[gj -1][gi - 1 -1] - xVal);
					wyy = yVal + xyf*(yArray[gj -1][gi - 1 -1] - yVal);
					break;

				case 4:				//	South
					wxx = xVal + xyf*(xArray[gj - 1 -1][gi -1] - xVal);
					wyy = yVal + xyf*(yArray[gj - 1 -1][gi -1] - yVal);
					break;
			}
			
		} else {
			//	We have 1D arrays for the X & Y grid points.
			xVal = xArr1D[gi -1];
			yVal = yArr1D[gj -1];
			switch (idir) {
				case 1:				//	East
					wxx = xVal + xyf*(xArr1D[gi + 1 -1] - xVal);
					wyy = yVal;
					break;

				case 2:				//	North
					wxx = xVal;
					wyy = yVal + xyf*(yArr1D[gj + 1 -1] - yVal);
					break;

				case 3:				//	West
					wxx = xVal + xyf*(xArr1D[gi - 1 -1] - xVal);
					wyy = yVal;
					break;

				case 4:				//	South
					wxx = xVal;
					wyy = yVal + xyf*(yArr1D[gj - 1 -1] - yVal);
					break;
			}
		}	

		if (DEBUG) {
			System.out.println("i, j = " + gi + "," + gj);
			System.out.println("cont = " + (float)cont + ",  fij = " + (float)fij +
									",  func = " + (float)func + ",  xyf = " + (float)xyf);
			System.out.println("xVal = " + (float)xVal + ",  yVal = " + (float)yVal);
			System.out.println("wxx = " + (float)wxx + ",  wyy = " + (float)wyy);
		}
		
		//	Figure out what to do with this point.
		if (lnstrt == 1) {
			//	This is the 1st point in the contour line.

			np = 1;
			pathbufxt[np -1] = wxx;
			pathbufyt[np -1] = wyy;

			//	Save starting point as wx, wy.
			wx = wxx;
			wy = wyy;

			//	Clear the first point flag, we've got one now.
			lnstrt = 0;
			
		} else {

			boolean skipFlg = false;
			
			//	Second point and after comes here.
			//	Add a point to this line.  Check for duplicate point first.
			if (ignext == 2) {
				if (wxx == pathbufxt[np -1] && wyy == pathbufyt[np -1]) {
					ignext = 0;
					skipFlg = true;
					
				} else
					ignext = 1;
			}

			if (!skipFlg) {
			
				//	Increment # of points in contour.
				++np;
				pathbufxt[np -1] = wxx;
				pathbufyt[np -1] = wyy;

				//	See if the temporary array xt, yt are full.
				if (np == kBufSize) {
					accumContour(np, icont, pathbufxt, pathbufyt, cAttr[icont]);

					//	Last point becomes 1st point to continue.
					pathbufxt[0] = pathbufxt[np -1];
					pathbufyt[0] = pathbufyt[np -1];
					np =1;
				}
	
				//	Check to see if we're back to the intial point.
				if (wxx == wx && wyy == wy)
					return;
			}

		}

		//	Search for the next point on this line.
/*L67*/		switch(idir) {
			case 1:
				++gi;
				if (!Routine_L51())
					Routine_L21();
				break;

			case 2:
				++gj;
				Routine_L21();
				break;

			case 3:
				--gi;
				if (!Routine_L31())
					Routine_L21();
				break;

			case 4:
				--gj;
				if (!Routine_L41())
					Routine_L21();
				break;
		}
		
		return;
	}
	
	/**
	*  Accumulate contour paths, as they are generated, into
	*  an overall list of contours.
	*
	*  @param  np      The number of points in the contour path buffers.
	*  @param  icont   The index to the current contour level.
	*  @param  x,y     Buffers containing x & y coordinates of contour points.
	*  @param  cAttr   The attributes for this particular contour level.
	**/
	private void accumContour(int np, int icont, double[] x, double[] y, ContourAttrib cAttr) {
		
		//	To few points for a contour line.
		if (np < kMinNumPoints)	return;
		
		//	Copy over coordinate points from buffers to their own arrays.
		double[] xArr = new double[np];
		double[] yArr = new double[np];
		System.arraycopy(x, 0, xArr, 0, np);
		System.arraycopy(y, 0, yArr, 0, np);
		
		//	Create a new contour path and add it to the list.
		ContourPath path = new ContourPath(cAttr, icont, xArr, yArr);
		pathList.add(path);
		
	}
	
}

