public class CreateImage {
	
	/** The hashmap of domain images. */
	private HashMap<String, ImagePlus> hashDomFile;
	
	/** The hashmap of sampled value of spatial image. */
	private HashMap<String, Integer> hashSampledValue;
	
	/** The ImageJ image object of composite image. */
	private ImagePlus compoImg;
	
	/** The byte array (raw data) of composite image. */
	private byte[] compoMat;
	
	/** The width of an image. */
	private int width;
	
	/** The height of an image. */
	private int height;
	
	/** The depth of an image. */
	private int depth ;

	/** The composite image converted to stack of images. */
	private ImageStack altimage;


	/**
	 * Instantiates a new CreateImage object with given hashmap of domain images and
	 * hashmap of sampled value of spatial image.
	 *
	 * @param hashDomFile the hashmap of domain images
	 * @param hashSampledValue the hashmap of sampled value of spatial image.
	 */
	public CreateImage(HashMap<String, ImagePlus> hashDomFile, HashMap<String, Integer> hashSampledValue) {
		this.hashSampledValue = hashSampledValue;
		this.hashDomFile = hashDomFile;
		ImagePlus img = hashDomFile.values().iterator().next();
		width = img.getWidth();
		height = img.getHeight();
		depth = img.getStackSize();
		compositeImage();
		System.out.println("width " + width + " height "+ height + " depth " + depth);
		replaceMat();
		compoImg = new ImagePlus("Combined_Image", altimage);
		compoImg.setFileInfo(img.getFileInfo());
		compoImg.setCalibration(img.getCalibration());
		System.out.println("fileinfo " + compoImg.getOriginalFileInfo());
	}
	
	/**
	 * Composite image. All images included in hashDomFile (hashmap of domain images)
	 * will be composed to compoMat[], which is a raw data (1D array).
	 */
	private void compositeImage(){
		Iterator<String> domNames = hashDomFile.keySet().iterator();
		compoMat = new byte[width*height*depth];
		ImagePlus temp;
		byte[] tempMat;
		String imgName;
		
		while(domNames.hasNext()){
			imgName = domNames.next();
			temp = hashDomFile.get(imgName);
			tempMat = getMat(temp);
			cmpImg(temp, tempMat, imgName);
		}
	}

    /**
	 * Creates and returns the raw data (1D array) of given image.
     *
     * @param image the ImageJ image object
     * @return the raw data (1D array) of given image.
     */
    private byte[] getMat(ImagePlus image){
    	byte[] slice = null;   
    	byte[] pixels = new byte[width * height * depth];

		if (image.isInvertedLut()) {
			image.getProcessor().invertLut();
		}
    	ImageStack stack = image.getStack();
    	
    	for(int i = 1 ; i <= depth ; i++){
        	slice = (byte[]) stack.getPixels(i);
        	System.arraycopy(slice, 0, pixels, (i-1) * height * width, height * width);
    	}

    	return pixels;
    }

    /**
     * Composite a given image (imgMat) to compoMat array.
	 * The pixel value of given image (imgMat[i]) will be assigned to
	 * compoMat[i], if the pixel value of given image is larger than
	 * compoMat[i].
     *
     * @param img the ImageJ image object
     * @param imgMat the raw date (1D array) of an image
     * @param name the name of an image
     */
    private void cmpImg(ImagePlus img, byte[] imgMat, String name){
    	int max = imgMat.length;
    	System.out.println("cmpImg " + name);
    	byte pixVal = hashSampledValue.get(name).byteValue();
    	for(int i = 0 ; i < max ; i++){
    		if(imgMat[i] != 0 && (compoMat[i] == 0 || checkVal(compoMat[i],pixVal))){
    			compoMat[i] = pixVal;
    		}		
    	}
    }

   /**
    * Check whether the pixel value of composite image is smaller than the given pixel value.
	* Both composite image pixel and given pixel values are treated as 8bit value.
    *
    * @param compoVal the value of a composite image
    * @param pixVal the value of a pixel
    * @return true, if compoVal(8bit) is smaller than pixVal(8bit)
    */
   private boolean checkVal(byte compoVal , byte pixVal){
	   return (compoVal & 0xFF) < (pixVal & 0xFF);
   }
    
	/**
	 * Convert raw data (1D array) of composite image to altimage, which is an image stack object.
	 */
	private void replaceMat(){
		altimage = new ImageStack(width, height);
		byte[] slice;

		for (int i = 1; i <= depth; i++) {
			slice = new byte[width * height];
			System.arraycopy(compoMat, (i - 1) * height * width, slice, 0, slice.length);
			altimage.addSlice(new ByteProcessor(width, height, slice, null));
		}
	}

	/**
	 * Gets the composite image.
	 *
	 * @return the composite image (ImageJ image object)
	 */
	public ImagePlus getCompoImg() {
		return compoImg;
	}

	/**
	 * Sets the composite image.
	 *
	 * @param compoImg the new composite image (ImageJ image object)
	 */
	public void setCompoImg(ImagePlus compoImg) {
		this.compoImg = compoImg;
	}
} public class Filler {
	
	/** The ImageJ image object. */
	private ImagePlus image;
	
	/** The width of an image. */
	private int width;
	
	/** The height of an image. */
	private int height;
	
	/** The depth of an image. */
	private int depth;
	
	/** The width of an image including padding. */
	private int lwidth;
	
	/** The height of an image including padding. */
	private int lheight;
	
	/** The depth of an image including padding. */
	private int ldepth;
	
	/** The mask which stores the label of each pixel. */
	private int[] mask;
	
	/**
	 * The hashmap of pixel value. &lt;labelnumber, pixel value&gt;.
	 * The domain which has pixel value = 0 will have a label = 1.
	 */
	private HashMap<Integer, Byte> hashPix = new HashMap<Integer, Byte>();		// label number, pixel value
	
	/** The raw data (1D byte array) of the image. */
	private byte[] pixels;
	
	/** The raw data (1D int array) of inverted the image. */
	private int[] invert;
	
	/**
	 * Fill a hole in the given image (ImagePlus object) by morphology operation,
	 * and returns the filled image.
	 *
	 * @param image the ImageJ image object
	 * @return the filled ImageJ image (ImagePlus) object
	 */
	public ImagePlus fill(ImagePlus image){
		this.width = image.getWidth();
		this.height = image.getHeight();
		this.depth = image.getStackSize();
		this.image = image;

		pixels = ImgProcessUtil.copyMat(image);
		invertMat();
		label();
		if (checkHole()) {
			while (checkHole()) {
				fillHole();
				hashPix.clear();
				label();
			}
			ImageStack stack = createStack();
			image.setStack(stack);
			image.updateImage();
		}
		return image;
	}

	/**
	 * Fill a hole in the given image ({@link SpatialImage} object) by morphology operation,
	 * and returns the filled image as ImageJ image object.
	 *
	 * @param spImg the SpatialImage object
	 * @return the filled ImageJ image (ImagePlus) object
	 */
	public ImagePlus fill(SpatialImage spImg){
		this.width = spImg.getWidth();
		this.height = spImg.getHeight();
		this.depth = spImg.getDepth();
		this.image = spImg.getImage();
		this.pixels = spImg.getRaw();
		invertMat();
		label();
		if(checkHole()){
			while(checkHole()){	
				fillHole();
				hashPix.clear();
				label();
			}
			ImageStack stack = createStack();
			image.setStack(stack);
			image.updateImage();
		}
		
		return image;
	}
	
	/**
	 * Creates the stack of images from raw data (1D array) of image (pixels[]),
	 * and returns the stack of images.
	 *
	 * @return the stack of images
	 */
	private ImageStack createStack(){
		ImageStack altimage = new ImageStack(width, height);
		for(int d = 0 ; d < depth ; d++){
			byte[] matrix = new byte[width * height];
			System.arraycopy(pixels, d * height * width, matrix, 0, matrix.length);
			altimage.addSlice(new ByteProcessor(width,height,matrix,null));
		}
		return altimage;
	}
	
	/**
	 * Create an inverted 1D array of an image (invert[]) from 1D array of an image (pixels[]).
	 * Each pixel value will be inverted (0 -> 1, otherwise -> 0). For example, the Black and White
	 * binary image will be converted to a White and Black binary image.
	 */
 	private void invertMat(){
		lwidth = width + 2;
		lheight = height + 2;
		if(depth < 3) ldepth = depth;
		else 			ldepth = depth + 2;
		
		invert = new int[lwidth * lheight * ldepth]; 
		mask = new int[lwidth * lheight * ldepth];
		if (ldepth > depth) {  // 3D image
			for (int d = 0; d < ldepth; d++) {
				for (int h = 0; h < lheight; h++) {
					for (int w = 0; w < lwidth; w++) {
						if (d == 0 || d == ldepth - 1 || h == 0 || h == lheight - 1 || w == 0 || w == lwidth - 1) {
							invert[d * lheight * lwidth + h * lwidth + w] = 1;
							mask[d * lheight * lwidth + h * lwidth + w] = 1;
							continue;
						}

						if (pixels[(d - 1) * height * width + (h - 1) * width + w - 1] == 0)
							invert[d * lheight * lwidth + h * lwidth + w] = 1;
						else
							invert[d * lheight * lwidth + h * lwidth + w] = 0;
					}
				}
			}
		} else { // 2D image
			for (int d = 0; d < ldepth; d++) {
				for (int h = 0; h < lheight; h++) {
					for (int w = 0; w < lwidth; w++) {
						if(h == 0 || h == lheight - 1 || w == 0 || w == lwidth - 1){
							invert[d * lheight * lwidth + h * lwidth + w] = 1;
							mask[d * lheight * lwidth + h * lwidth + w] = 1;
							continue;
						}
							
						if (pixels[d * height * width + (h - 1) * width + w - 1] == 0)
							invert[d * lheight * lwidth + h * lwidth + w] = 1;
						else
							invert[d * lheight * lwidth + h * lwidth + w] = 0;	
					}
				}
			}
		}
	 }
	
	/** The label count. */
	private int labelCount;
	
	/**
	 * Assign a label (label number) to each pixel.
     * The label number will be stored in mask[] array.
	 * The domain which has pixel value = 0 will have a label = 1.
	 */
	public void label(){
		hashPix.put(1, (byte)0);
		labelCount = 2;
		if (ldepth > depth) {
			for (int d = 1; d < ldepth - 1; d++) {
				for (int h = 1; h < lheight - 1; h++) {
					for (int w = 1; w < lwidth - 1; w++) {
						if (invert[d * lheight * lwidth + h * lwidth + w] == 1 && pixels[(d-1) * height * width + (h-1) * width + w - 1] == 0) {
							mask[d * lheight * lwidth + h * lwidth + w] = setLabel(w, h, d, pixels[(d-1) * height * width + (h-1) * width + w - 1]);
						}else{
							mask[d * lheight * lwidth + h * lwidth + w] = setbackLabel(w, h, d, pixels[(d-1) * height * width + (h-1) * width + w - 1]);
						}
					}
				}
			}
		}else{
			for (int d = 0; d < ldepth; d++) {
				for (int h = 1; h < lheight - 1; h++) {
					for (int w = 1; w < lwidth - 1; w++) {
						if (invert[d * lheight * lwidth + h * lwidth + w] == 1 && pixels[d * height * width + (h-1) * width + w - 1] == 0) {
							mask[d * lheight * lwidth + h * lwidth + w] = setLabel(w, h, d, pixels[d * height * width + (h-1) * width + w - 1]);
						}else{
							mask[d * lheight * lwidth + h * lwidth + w] = setbackLabel(w, h, d, pixels[d * height * width + (h-1) * width + w - 1]);
						}
					}
				}
			}
		}
	}
	
	/**
	 * Check whether a hole exists in the hashmap of pixels (HashMap&lt;label number, pixel value&gt;).
	 *
	 * @return true, if a hole exists
	 */
	public boolean checkHole(){
		if(Collections.frequency(hashPix.values(), (byte) 0) > 1)
			return true;
		else		
			return false;
	}
	
	/**
	 * Fill a hole in the hashmap of pixels (HashMap&lt;label number, pixel value&gt; by morphology operation.
	 * The fill operation will be applied to each domain (which has unique label number).
	 */
	public void fillHole(){
		for(Entry<Integer, Byte> e : hashPix.entrySet()){
			if(!e.getKey().equals(1) && e.getValue().equals((byte)0)){
				fill(e.getKey());
			}
		}
	}
	
	/**
	 * Fill a hole in the hashmap of pixels (HashMap&lt;label number, pixel value&gt; by morphology operation.
     * The hole will be filled with the pixel value of adjacent pixel.
	 *
	 * @param labelNum the label number
	 */
	public void fill(int labelNum){
		if (ldepth > depth) { // 3D image
			for (int d = 1; d < ldepth; d++) {
				for (int h = 1; h < lheight - 1; h++) {
					for (int w = 1; w < lwidth - 1; w++) {
						if (mask[d * lheight * lwidth + h * lwidth + w] == labelNum ) {
							pixels[(d-1) * height * width + (h-1) * width + w - 1] = checkAdjacentsLabel(w, h, d, labelNum);
						}
					}
				}
			}
		} else { // 2D image
			for (int d = 0; d < ldepth; d++) {
				for (int h = 1; h < lheight - 1; h++) {
					for (int w = 1; w < lwidth - 1; w++) {
						if (mask[d * lheight * lwidth + h * lwidth + w] == labelNum ) {
							pixels[d * height * width + (h-1) * width + w - 1] = checkAdjacentsLabel(w, h, d, labelNum);
						}
					}
				}
			}
		}
	}
	
	/**
	 * Check adjacent pixels whether it contains the given label (labelNum).
     * If all the adjacent pixels have same label with given label, then return 0.
	 * If the adjacent pixels contain different labels, then returns the pixel
	 * value of most enclosing adjacent domain.
	 *
	 * @param w the x offset
	 * @param h the y offset
	 * @param d the z offset
	 * @param labelNum the label number
	 * @return the pixel value of most enclosing adjacent domain if different domain exists, otherwise 0
	 */
	public byte checkAdjacentsLabel(int w, int h, int d, int labelNum){
		List<Byte> adjVal = new ArrayList<Byte>();
			//check right
			if(mask[d * lheight * lwidth + h * lwidth + w + 1] != labelNum)
				adjVal.add(hashPix.get(mask[d * lheight * lwidth + h * lwidth + w + 1]));
			
			//check left			
			if(mask[d * lheight * lwidth + h * lwidth + w - 1] != labelNum)
				adjVal.add(hashPix.get(mask[d * lheight * lwidth + h * lwidth + w - 1]));
			
			//check down
			if(mask[d * lheight * lwidth + (h+1) * lwidth + w ] != labelNum)
				adjVal.add(hashPix.get(mask[d * lheight * lwidth + (h+1) * lwidth + w]));

			//check up
			if(mask[d * lheight * lwidth + (h-1) * lwidth + w ] != labelNum)
				adjVal.add(hashPix.get(mask[d * lheight * lwidth + (h-1) * lwidth + w]));

			//check above
			if(d != depth - 1 && mask[(d+1) * lheight * lwidth + h * lwidth + w] != labelNum)
				adjVal.add(hashPix.get(mask[(d+1) * lheight * lwidth + h * lwidth + w]));
			
			//check below
			if(d != 0 && mask[(d-1) * lheight * lwidth + h * lwidth + w] != labelNum)
				adjVal.add(hashPix.get(mask[(d - 1) * lheight * lwidth + h * lwidth + w]));
			
			if(adjVal.isEmpty())
				return 0;
		
			int max = 0;
			int count = 0;
			int freq, temp; Byte val = 0;
			for(int n = 0 ; n < adjVal.size() ; n++){
				val = adjVal.get(n);
				if(val == 0)
					continue;
				freq = Collections.frequency(adjVal, val);
				temp = val & 0xFF;
				if(freq > count){
					max = temp;
					count = freq;
				}
					
				if(freq == count && max < temp){
					max = temp;
					count = freq;
				}
			}
		return (byte) max;
	}
	
	/**
	 * Sets the label of the given pixel (x offset, y offset, z offset) with its pixel value.
	 * Checks whether the adjacent pixel has the zero pixel value, and its label is already assigned.
	 *
	 * @param w the x offset
	 * @param h the y offset
	 * @param d the z offset
	 * @param pixVal the pixel value
	 * @return the label as integer value
	 */
	private int setLabel(int w , int h, int d, byte pixVal){
		List<Integer> adjVal = new ArrayList<Integer>();
		//check left			
		if(mask[d * lheight * lwidth + h * lwidth + w - 1] != 0 && hashPix.get(mask[d * lheight * lwidth + h * lwidth + w - 1]) == (byte)0)
			adjVal.add(mask[d * lheight * lwidth + h * lwidth + w - 1]);

		//check up
		if(mask[d * lheight * lwidth + (h-1) * lwidth + w ] != 0 && hashPix.get(mask[d * lheight * lwidth + (h-1) * lwidth + w]) == (byte)0)
			adjVal.add(mask[d * lheight * lwidth + (h-1) * lwidth + w]);

		//check below
		if(d != 0 && mask[(d-1) * lheight * lwidth + h * lwidth + w] != 0 && hashPix.get(mask[(d-1) * lheight * lwidth + h * lwidth + w]) == (byte)0)
			adjVal.add(mask[(d-1) * lheight * lwidth + h * lwidth + w]);
		
		if(adjVal.isEmpty()){
			hashPix.put(labelCount, pixVal);
			return labelCount++;
		}
			
		Collections.sort(adjVal);
		
		//if all element are same or list has only one element 
		if(Collections.frequency(adjVal, adjVal.get(0)) == adjVal.size())
			return adjVal.get(0);
		
		int min = adjVal.get(0);
		for(int i = 1; i < adjVal.size(); i++){
			if(min == adjVal.get(i))
				continue;
			
			rewriteLabel(d, min, adjVal.get(i));
			hashPix.remove(adjVal.get(i));
		}
			return min;
	}

	/**
	 * Sets back the label of the given pixel (x offset, y offset, z offset) with its pixel value.
	 * Checks whether the adjacent pixel has the non-zero pixel value, and its label is already assigned.
	 *
	 * @param w the x offset
	 * @param h the y offset
	 * @param d the z offset
	 * @param pixVal the pixel value
	 * @return the label as integer value
	 */
	private int setbackLabel(int w , int h, int d, byte pixVal){
		List<Integer> adjVal = new ArrayList<Integer>();
		//check left
		if(mask[d * lheight * lwidth + h * lwidth + w - 1] != 0 && hashPix.get(mask[d * lheight * lwidth + h * lwidth + w - 1]) != (byte)0)
			adjVal.add(mask[d * lheight * lwidth + h * lwidth + w - 1]);

		//check up
		if(mask[d * lheight * lwidth + (h-1) * lwidth + w ] != 0 && hashPix.get(mask[d * lheight * lwidth + (h-1) * lwidth + w]) != (byte)0)
			adjVal.add(mask[d * lheight * lwidth + (h-1) * lwidth + w]);

		//check below
		if(d != 0 && mask[(d-1) * lheight * lwidth + h * lwidth + w] != 0 && hashPix.get(mask[(d-1) * lheight * lwidth + h * lwidth + w]) != (byte)0)
			adjVal.add(mask[(d-1) * lheight * lwidth + h * lwidth + w]);
		
		if(adjVal.isEmpty()){
			hashPix.put(labelCount, pixVal);
			return labelCount++;
		}
			
		Collections.sort(adjVal);
	
		//if all element are same or list has only one element 
		if(Collections.frequency(adjVal, adjVal.get(0)) == adjVal.size())
			return adjVal.get(0);
		
		int min = adjVal.get(0);
		for(int i = 1; i < adjVal.size(); i++){
			if(min == adjVal.get(i))
				continue;
			
			hashPix.remove(adjVal.get(i));
			rewriteLabel(d, min, adjVal.get(i));
		}
		return min;
	}
	
	/**
	 * Replace the label of pixels in the spatial image which has "before" to "after".
	 *
	 * @param dEnd the end of the depth
	 * @param after the label to set by this replacement
	 * @param before the label to be replaced
	 */
	private void rewriteLabel(int dEnd, int after, int before){
		if (ldepth > depth) {
			for (int d = 1; d <= dEnd; d++) {
				for (int h = 1; h < lheight - 1; h++) {
					for (int w = 1; w < lwidth - 1; w++) {
						if (mask[d * lheight * lwidth + h * lwidth + w] == before)
							mask[d * lheight * lwidth + h * lwidth + w] = after;					
						}
				}
			}
		}else{
			for (int d = 0; d <= dEnd; d++) {
				for (int h = 1; h < lheight - 1; h++) {
					for (int w = 1; w < lwidth - 1; w++) {
						if (mask[d * lheight * lwidth + h * lwidth + w] == before)
							mask[d * lheight * lwidth + h * lwidth + w] = after;
					}
				}
			}
		}

	}
	
} public class ImageBorder {
	
	/** The width of an image. */
	private int width;
	
	/** The height of an image. */
	private int height;
	
	/** The depth of an image. */
	private int depth;
	
	/** The raw data (1D byte array) of the image. */
	private byte[] raw;
	
	/** The image has safe border. */
	private boolean hasSafeBorder = true;
	
	/** The stack of images which has safe border. */
	private ImageStack altStack;
	
	/**
	 * Instantiates a new ImageBorder object.
	 *
	 * @param spImg the SpatialImage object
	 */
	public ImageBorder(SpatialImage spImg){
		this.width = spImg.getWidth();
		this.height = spImg.getHeight();
		this.depth = spImg.getDepth();
		this.raw = spImg.getRaw();
		
		fixBorder();						// blackens all outside pixel
		hasSafeBorder = isBorderSafe(); 	//depth = 0 or top/bottom slice does not have object
		createNewStack(hasSafeBorder);
		//createNewStack();
	}
	
	/**
	 * Fill with 0 for X and Y border. This method is like a zero-padding, but will not
	 * extend the image size.
	 */
	private void fixBorder() {
		int init = 0, end = depth;
		
		for (int d = init; d < end; d++) {
			for (int h = 0; h < height; h++) {
				for (int w = 0; w < width; w++) {
					if (h == 0 || h == height - 1 || w == 0 || w == width - 1) {
						raw[d * height * width + h * width + w] = 0;
					}
				}
			}
		}
	}

	/**
	 * Checks if border of Z axis is safe (does not contain non zero value on the border layer).
	 *
	 * @return true, if border of Z axis is safe
	 */
	private boolean isBorderSafe(){
		boolean safez = true;
		if(depth > 1)	safez = checkTopBottom();
		return safez;
	}
	
	/**
	 * Check whether top and bottom z-stack image contains non zero value.
     * If the z-stack image contains non zero value, this means that the 3D image is not
	 * surrounded with zero values.
	 *
	 * @return true, if the top and bottom z-stack image only contains zero value
	 */
	private boolean checkTopBottom(){
		int bottomSlice = (depth - 1) * width * height;
		
		for(int h = 0 ; h < height ; h++){
			for(int w = 0 ; w < width ; w++){
				if(raw[bottomSlice + width * h + w] != 0 || raw[width * h + w] != 0)
					return false;
			}
		}
		
		return true;
	}
	
	/**
	 * Creates the new stack of images. If the border of Z axis is not safe,
	 * then add a layer which is filled with zero (border layer).
	 *
	 * @param hasSafeBorder the flag whether the border of Z axis is safe
	 */
	private void createNewStack(boolean hasSafeBorder){
		altStack = new ImageStack(width, height);
		
		if(!hasSafeBorder)
			addBlackSlice(altStack);
		
		for(int i = 1 ; i <= depth ; i++){
			byte[] slice = new byte[height * width];
			System.arraycopy(raw, (i-1) * height * width, slice, 0, height * width);
			altStack.addSlice(new ByteProcessor(width,height,slice,null));
    	} 
		
		if(!hasSafeBorder) 
			addBlackSlice(altStack);
	
		if(!hasSafeBorder) 
			depth += 2;
		
	}
	
	/**
	 * Creates the new stack of images.
	 */
	private void createNewStack(){
		altStack = new ImageStack(width, height);
		for (int h = 0; h < height; h++) {
			for (int w = 0; w < width; w++) {
				raw[ h * width + w] = 0;
				raw[(depth - 1) * height * width + h * width + w] = 0;
			}
		}

		for(int i = 1 ; i <= depth ; i++){
			byte[] slice = new byte[height * width];
			System.arraycopy(raw, (i-1) * height * width, slice, 0, height * width);
			altStack.addSlice(new ByteProcessor(width,height,slice,null));
    	} 
	}
	
	/**
	 * Adds a black slice (a layer which is filled with zero (that is, the border layer)) to the stack of images.
	 *
	 * @param is the stack of images
	 */
	private void addBlackSlice(ImageStack is){
		byte[] blackSlice = new byte[width * height];
		is.addSlice(new ByteProcessor(width,height,blackSlice,null));
	}
	
	/**
	 * Gets the stack of images.
	 *
	 * @return the stack of images
	 */
	public ImageStack getStackImage(){
		return altStack;
	}
} public class ImageEdit {
	
	/** The list of unique pixel values */
	private ArrayList<Integer> labelList;
	
	/** The hashmap of number of labels. HashMap&lt;Integer, Integer&gt;*/
	private HashMap<Integer,Integer> hashLabelNum;
	
	/** The hashmap of domain types. HashMap&lt;String, Integer&gt;*/
	private HashMap<String,Integer> hashDomainTypes;
	
	/** The hashmap of sampled value. HashMap&lt;String, Integer&gt;*/
	private HashMap<String,Integer> hashSampledValue;
	
	/** The width of an image. */
	private int width;
	
	/** The height of an image. */
	private int height;
	
	/** The depth of an image. */
	private int depth;
	
	/** The size of an image. */
	private int size;
	
	/** The raw data (1D array) of an image. */
	private byte[] pixels;
	
	/** The 1D array of labels. */
	private int[] matrix;	
	
	/** The inverted 1D array of an image */
	private int[] invert;
	
	/** The hashmap of pixels. HashMap&lt;Integer label, Integer pixel value&gt; */
	private HashMap<Integer, Integer>  hashPix = new HashMap<Integer,Integer>(); //label + pixel value
	
	/** The hashmap of Point3d objects. HashMap&lt;Integer label, Point3d coordinate&gt; */
	private HashMap<Integer, Point3d> hashLabelPt = new HashMap<Integer,Point3d>();  //label + coordinates
    
	/** The hashmap of domain InteriorPoint of spatial image. HashMap&lt;String domain name, Point3d coordinate&gt; */
    private HashMap<String, Point3d> hashDomInteriorPt = new HashMap<String,Point3d>();  //domain name + coordinates
	
    /**
     * Instantiates a new image edit object with given image object and hashmaps of domain types and sampled value.
     *
     * @param image the ImageJ image object
     * @param hashDomainTypes the hashmap of domain types
     * @param hashSampledValue the hashmap of sampled value of spatial image
     */
    ImageEdit(ImagePlus image,HashMap<String,Integer> hashDomainTypes, HashMap<String,Integer> hashSampledValue){
    	this.width = image.getWidth();
        this.height = image.getHeight();
        this.depth = image.getStackSize();
        this.size = width * height * depth;
        this.hashDomainTypes = hashDomainTypes;
        this.hashSampledValue = hashSampledValue;
        pixels = ImgProcessUtil.copyMat(image);
        listVal();
        invertMat();
        label();
        createMembrane();
    }
    
    /**
     * Instantiates a new image edit with given spatial image object.
     *
     * @param spImg the spatial image object
     */
    public ImageEdit(SpatialImage spImg){
    	spImg.getImage();    	
        this.width = spImg.getWidth();
        this.height = spImg.getHeight();
        this.depth = spImg.getDepth();
        this.size = width * height * depth;
        this.hashDomainTypes = spImg.getHashDomainTypes();
        this.hashSampledValue = spImg.getHashSampledValue();
        this.pixels = spImg.getRaw();

        listVal();
        invertMat();
        label();
        createMembrane();
        spImg.setHashDomainNum(hashDomainNum);
        spImg.setAdjacentsList(adjacentsList);
        createDomInteriorPt();
        spImg.setHashDomInteriorpt(hashDomInteriorPt);
    }
    
    /**
	 * Create a new labelList object, which is a list of unique pixel values.
     */
    private void listVal(){
    	int temp;
    	labelList = new ArrayList<Integer>();
    	for (int i = 0 ; i < size ; i++) {
			temp = pixels[i] & 0xFF;
			if (!labelList.contains(temp)) {	
				labelList.add(new Integer(temp));
			}
		}
    	Collections.sort(labelList);
    }
    
    /**
     * Create hashmap of count number of labels and set the value with given
	 * hashmap of pixels.
     *
     * @param num the hashmap of pixels (HashMap&lt;Integer, Integer&gt;).
     */
    public void domtype(HashMap<Integer,Integer> num){
    	hashLabelNum = new HashMap<Integer,Integer>();
    	for(Entry<Integer, Integer> e : num.entrySet()){
    		if(!hashLabelNum.containsKey(e.getValue()))
    			hashLabelNum.put((Integer) e.getValue(), Collections.frequency(num.values(), e.getValue()));
    	}	
    }

	/**
	 * Create an inverted 1D array of an image (invert[]) from 1D array of an image (pixels[]).
	 * Each pixel value will be inverted (0 -> 1, otherwise -> 0). For example, the Black and White
	 * binary image will be converted to a White and Black binary image.
	 */
 	private void invertMat(){		
		invert = new int[width * height * depth]; 
		matrix = new int[width * height * depth];
			for (int d = 0; d < depth; d++) {
				for (int h = 0; h < height; h++) {
					for (int w = 0; w < width; w++) {
						if (pixels[d * height * width + h * width + w] == 0)
							invert[d * height * width + h * width + w] = 1;
						else
							invert[d * height * width + h * width + w] = 0;
					}
				}
			}
	 }
	 
	/** The count number of labels. */
	private int labelCount = 1;
	
	/**
	 * Set the label of each pixel to the 1D array of labels (matrix).
	 * After the labels are set to all pixels, then count the number of labels
	 * and set to the hashmap of count number of labels.
	 */
	public void label(){
			for (int d = 0; d < depth; d++) {
				for (int h = 0; h < height; h++) {
					for (int w = 0; w < width; w++) {
						matrix[d * height * width + h * width + w] = setLabel(w, h, d, pixels[d * height * width + h * width + w] & 0xFF);
					}
				}
			}
		domtype(hashPix);
	}
	
	/**
	 * Sets the label of the given pixel (x offset, y offset, z offset) with its pixel value.
     * Checks whether the adjacent pixel has the same pixel value, and its label is already assigned.
	 *
	 * @param w the x offset
	 * @param h the y offset
	 * @param d the z offset
	 * @param pixVal the pixel value
	 * @return the label as integer value
	 */
	private int setLabel(int w , int h, int d, int pixVal){
		List<Integer> adjVal = new ArrayList<Integer>();
		//check left			
		if(w != 0 && hashPix.get(matrix[d * height * width + h * width + w - 1]).equals(pixVal))
			adjVal.add(matrix[d * height * width + h * width + w - 1]);

		//check right			
		if(w != width - 1 && matrix[d * height * width + h * width + w + 1] != 0 && hashPix.get(matrix[d * height * width + h * width + w + 1]).equals(pixVal))
			adjVal.add(matrix[d * height * width + h * width + w + 1]);

		//check up
		if(h != 0 && hashPix.get(matrix[d * height * width + (h-1) * width + w]).equals(pixVal))
			adjVal.add(matrix[d * height * width + (h-1) * width + w]);

		//check down
		if(h != height - 1 && matrix[d * height * width + (h+1) * width + w ] != 0 && hashPix.get(matrix[d * height * width + (h+1) * width + w]).equals(pixVal))
			adjVal.add(matrix[d * height * width + (h+1) * width + w]);
		
		//check below
		if(d != 0 && hashPix.get(matrix[(d-1) * height * width + h * width + w]).equals(pixVal))
			adjVal.add(matrix[(d-1) * height * width + h * width + w]);

		//check above
		if(d != depth - 1 && matrix[(d+1) * height * width + h * width + w ] != 0 && hashPix.get(matrix[(d+1) * height * width + h * width + w]).equals(pixVal))
			adjVal.add(matrix[(d+1) * height * width + h * width + w]);
		
		if(adjVal.isEmpty()){
			hashPix.put(labelCount, pixVal);
			hashLabelPt.put(labelCount, new Point3d(w,h,d));
			return labelCount++;
		}
			
		Collections.sort(adjVal);
		
		//if all element are same or list has only one element 
		if(Collections.frequency(adjVal, adjVal.get(0)) == adjVal.size())
			return adjVal.get(0);
		
		int min = adjVal.get(0);
		for(int i = 1; i < adjVal.size(); i++){
			if(min == adjVal.get(i))
				continue;
			
			rewriteLabel(d, min, adjVal.get(i));
			hashPix.remove(adjVal.get(i));
			hashLabelPt.remove(adjVal.get(i));
		}
		return min;
	}

	/**
	 * Replace the label of pixels in the spatial image which has "before" to "after".
	 *
	 * @param dEnd the end of the depth
	 * @param after the label to set by this replacement
	 * @param before the label to be replaced
	 */
	private void rewriteLabel(int dEnd, int after, int before){
		for (int d = 0; d <= dEnd; d++) {
				for (int h = 0; h < height; h++) {
					for (int w = 0; w < width; w++) {
						if (matrix[d * height * width + h * width + w] == before)
							matrix[d * height * width + h * width + w] = after;
					}
				}
			}
	}
    
    /** count number of domains in each domain types and add membrane to adjacents. */
	private HashMap<String,Integer> hashDomainNum;
    
    /**
     * Creates the membrane.
     */
    public void createMembrane(){
    	countDomain();
    	addMembrane();
    }

    /**
     * Count number of domains in each domain types and store the number to
	 * the hashmap (hashDomainNum&lt;String domain type, Integer count number&gt;).
     */
    public void countDomain(){
    	hashDomainNum = new HashMap<String,Integer>();
    	Integer temp;
    	for(Entry<String,Integer> e : hashDomainTypes.entrySet()){
			temp = hashSampledValue.get(e.getKey());
			hashDomainNum.put(e.getKey(), Collections.frequency(hashPix.values(), temp));
		}
    }

    /** The list of adjacent pixels. */
    private ArrayList<ArrayList<Integer>> adjacentsPixel;
    
    /** The list of adjacent domains. */
    private ArrayList<ArrayList<String>> adjacentsList;
    
    /**
     * Adds a membrane between two different domains.
     */
    public void addMembrane(){
    	adjacentsPixel = new ArrayList<ArrayList<Integer>>();
        adjacentsList = new ArrayList<ArrayList<String>>();
        int lower, higher;
        //adds the membrane 					may need changes in the future
		for (int d = 0; d < depth; d++) {
			for (int i = 0; i < height - 1; i++) {
				for (int j = 0; j < width - 1; j++) {
					// right
					if (checkAdjacent(d * height * width + i * width + j, d * height * width + i * width + j + 1)) {
						ArrayList<Integer> temp = new ArrayList<Integer>(2);
						lower = getLowerLabel(matrix[d * height * width + i * width + j + 1], matrix[d * height * width + i * width + j]);
						higher = getHigherLabel(matrix[d * height * width + i * width + j + 1], matrix[d * height * width + i * width + j]);
						temp.add(higher); temp.add(lower);
						adjacentsPixel.add(temp);
						addmem(higher,lower);
					}

					// down
					if (checkAdjacent(d * height * width + i * width + j, d * height * width + (i + 1) * width + j)) {
						ArrayList<Integer> temp = new ArrayList<Integer>(2);
						lower = getLowerLabel(matrix[d * height * width + (i + 1) * width + j], matrix[d * height * width + i * width + j]);
						higher = getHigherLabel(matrix[d * height * width + (i + 1) * width + j], matrix[d * height * width + i * width + j]);
						temp.add(higher); temp.add(lower);
						adjacentsPixel.add(temp);
						addmem(higher,lower);
					}
					
					//above
					if ( d != depth -1 && checkAdjacent(d * height * width + i * width + j, (d + 1) * height * width + i * width + j)) {
						ArrayList<Integer> temp = new ArrayList<Integer>(2);
						lower = getLowerLabel(matrix[(d + 1) * height * width + i * width + j], matrix[d * height * width + i * width + j]);
						higher = getHigherLabel(matrix[(d + 1) * height * width + i * width + j], matrix[d * height * width + i * width + j]);
						temp.add(higher); temp.add(lower);
						adjacentsPixel.add(temp);
						addmem(higher,lower);	
					}
				}
			}
		}
    }
    
    /**
     * Returns the label from given two labels which has lower pixel value.
     *
     * @param dom1 the label of domain 1
	 * @param dom2 the label of domain 2
     * @return the label which has lower pixel value
     */
    private int getLowerLabel(int dom1, int dom2){
    	int min = Math.min(hashPix.get(dom1), hashPix.get(dom2));
    	if(min  == hashPix.get(dom1) )
    		return dom1;
    	else 
    		return dom2;
    }
    
    /**
	 * Returns the label from given two labels which has higher pixel value.
	 *
	 * @param dom1 the label of domain 1
	 * @param dom2 the label of domain 2
	 * @return the label which has higher pixel value
	 */
	private int getHigherLabel(int dom1, int dom2){
    	int max = Math.max(hashPix.get(dom1), hashPix.get(dom2));
    	if(max  == hashPix.get(dom1) )
    		return dom1;
    	else 
    		return dom2;
    }

	/**
	 * Checks the given two labels are already registered in adjacentsPixel.
	 *
	 * @param dom1 the label of domain 1
	 * @param dom2 the label of domain 2
	 * @return true, if the pixel value of domain 1 has higher value than that of domain 2 in adjacentsPixel
	 */
	private boolean hasLabel(int dom1, int dom2) {
		if(adjacentsPixel.isEmpty()) return false;
		
		for (ArrayList<Integer> i : adjacentsPixel) {
			if (i.get(0) == dom1 && i.get(1) == dom2) {
				return true;
			}
		}
		return false;
	}
    
	/**
	 * Check adjacent pixels have different pixel values.
	 * If the adjacent pixel has different label with origin pixel, and the origin pixel and next pixel
	 * are already registered in adjacentsPixel.
	 *
	 * @param org the index of origin pixel in an 1D array
	 * @param next the index of next pixel in an 1D array
	 * @return true, if two pixel has different label and both pixels are registered in adjacentsPixel
	 */
	private boolean checkAdjacent(int org, int next){
		if(matrix[org] != matrix[next] &&
				!hasLabel(getHigherLabel(matrix[next], matrix[org]), getLowerLabel(matrix[next], matrix[org])))
			return true;
		 else 
			return false;
	}

	/**
	 * Add a membrane between given two labels (domains).
	 *
	 * @param bignum the label of pixel which has higher value
	 * @param smallnum the label of pixel which has lower value
	 */
	private void addmem(Integer bignum, Integer smallnum){
		String big ,small;

		big = getKeyFromValue(hashSampledValue, hashPix.get(bignum));
		small = getKeyFromValue(hashSampledValue, hashPix.get(smallnum));
		String buf = big + "_" + small + "_membrane";

		ArrayList<String> adjacentDom = new ArrayList<String>();
		adjacentDom.add(big + getIndexLabel(bignum));
		adjacentDom.add(small + getIndexLabel(smallnum));
		adjacentsList.add(adjacentDom);

		if(!hashDomainTypes.containsKey(buf)){
			if(depth > 1) hashDomainTypes.put(buf,2);
			else hashDomainTypes.put(buf,1);
			hashDomainNum.put(buf,1);
		} else{
			int temp = hashDomainNum.get(buf);
			hashDomainNum.put(buf,++temp);
		}
	}

	/**
	 * Gets the index of given label as string.
	 *
	 * @param label the label of the pixel
	 * @return count.toString() the index of label
	 */
	private String getIndexLabel(int label){
		Integer count = 0;
		
		for(Entry<Integer, Integer> e : hashPix.entrySet()){
			if(e.getKey().equals(label))
				break;
			if(e.getValue().equals(hashPix.get(label)))
				count++;
		}
		
		return count.toString();
	}
	
	/**
	 * Gets the key from given value.
	 *
	 * @param hash the hashmap
	 * @param val the value
	 * @return str the key which contains the given value as string
	 */
	private String getKeyFromValue(HashMap<String, Integer> hash, Integer val){
		String str = "";
		
		for(Entry<String,Integer> e :hash.entrySet()){
			if(e.getValue().equals( val ))
				str = e.getKey();
		}
		
		return str;
	}
	
	/**
	 * Creates the hashmap of domain InteriorPoint of spatial image.
	 */
	private void createDomInteriorPt(){
		for(Entry<Integer,Point3d> e : hashLabelPt.entrySet()){
			int pixelVal = hashPix.get(e.getKey());
			String domName = getKeyFromValue(hashSampledValue, pixelVal) + getIndexLabel(e.getKey());
			hashDomInteriorPt.put(domName, e.getValue());
		}
	}
} @SuppressWarnings("serial")
public class ImageExplorer extends JFrame implements ActionListener{

	/** The hashmap of domain types. HashMap&lt;String, Integer&gt;*/
	private HashMap<String, Integer> hashDomainTypes;
	
	/** The hashmap of sampled value of spatial image. HashMap&lt;String, Integer&gt;*/
	private HashMap<String, Integer> hashSampledValues;
	
	/** The hashmap of domain file. HashMap&lt;String, ImagePlus&gt; */
	private HashMap<String,ImagePlus> hashDomFile;
	
	/** The file information of composite image. */
	private FileInfo compoInfo;
	
	/** The scrollpane. */
	private JScrollPane scroll; 
	
	/**
	 * Instantiates a new image explorer.
	 */
	public ImageExplorer(){
		super("DomainType Namer");
		setDefaultCloseOperation(DISPOSE_ON_CLOSE);	
		setResizable(false);
		setBounds(100,100,500,240);
		setLocationByPlatform(true);
		setLocationRelativeTo(null);
	}
	
	/**
	 * Instantiates a new image explorer with given hashmap of domain types and hashmap of sampled values.
	 *
	 * @param hashDomainTypes the hashmap of domain types. HashMap&lt;String, Integer&gt;
	 * @param hashSampledValues the hashmap of sampled value of spatial image. HashMap&lt;String, Integer&gt;
	 */
	public ImageExplorer(HashMap<String, Integer> hashDomainTypes, HashMap<String, Integer> hashSampledValues){
		this();
		this.hashDomainTypes = hashDomainTypes;
		this.hashSampledValues = hashSampledValues;
		
		ImageTable table = new ImageTable();
		
		//scrollbar
		scroll = new JScrollPane(table);
		scroll.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);
		scroll.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
		scroll.setName("table");
		
		//button
		JButton ok = new JButton("OK"), plus = new JButton("+"), minus = new JButton("-");
		ok.addActionListener(this); plus.addActionListener(this); minus.addActionListener(this);
		JPanel p2 = new JPanel();
		p2.setLayout(new BoxLayout(p2, BoxLayout.LINE_AXIS));
		p2.add(plus); p2.add(minus);
		p2.add(Box.createRigidArea(new Dimension(250, 0))); p2.add(ok);
		
		//set components 
		getContentPane().add(p2, BorderLayout.PAGE_END);
		getContentPane().add(scroll, BorderLayout.CENTER);	
			
		setVisible(true);	
	}

	/**
	 * Sets the datatable(hashmap of domain file) to the hashmap of domain types
	 * and returns the hashmap of domain types.
     *
	 * @return the hashmap of domain types. HashMap&lt;String, Integer&gt;
	 */
	public HashMap<String, Integer> getDomainTypes(){
		int dimension = 3;
		for(Entry<String, ImagePlus> e : hashDomFile.entrySet()){
			if(e.getValue().getNSlices() == 1) dimension = 2;
 				hashDomainTypes.put( e.getKey().toString(), dimension);
		}
		hashDomainTypes.put("Extracellular", dimension);
		return hashDomainTypes;
	}
	
	/**
	 * Sets the hashmap of sampled values and returns the hashmap of sampled values.
     * The sampled value is calculated by the value of its domain type.
     *
	 * @param table the ImageTable object
	 * @return the hashmap of sampled value of spatial image. HashMap&lt;String, Integer&gt;
	 */
	public HashMap<String, Integer> getSampledValues(ImageTable table){
		int pixel = 255;
		int interval = 255 / hashDomFile.size();
		for(int i = 0 ; i < table.getRowCount() ; i++){
			String s = (String) table.getValueAt(i, 0);
			if(hashDomFile.containsKey(s)){
				hashSampledValues.put(s, pixel);
				pixel -= interval;
			}
		}
		hashSampledValues.put("Extracellular", 0);
		return hashSampledValues;
	}

	/**
	 * Example main() method which will launch a GUI and show this JFrame object.
	 *
	 * @param args an array of command-line arguments for the application
	 */
	public static void main(String[] args){	
		HashMap<String, Integer> hashDomainTypes = new HashMap<String, Integer>();
		HashMap<String, Integer> hashSampledValues = new HashMap<String, Integer>();
		new ImageExplorer(hashDomainTypes, hashSampledValues);
	}
	
	/**
	 * Gets the hashmap of domain file. HashMap&lt;String, ImagePlus&gt;
	 *
	 * @return the hashmap of domain file.
	 */
	public HashMap<String, ImagePlus> getDomFile(){
		return hashDomFile;
	}
	
	/**
	 * Gets the file information of composite image.
	 *
	 * @return the file information of composite image
	 */
	public FileInfo getFileInfo(){
		if(compoInfo != null)
			return compoInfo;
		else
			return null;
	}
	
	/**
     * The listener interface for receiving action events.
     * Add a row if "+" is pressed, Remove the selected row if "-" is pressed.
	 * Import the selected image to the spatial model if "OK" is pressed.
	 *
	 * @param e the ActionEvent
	 */
	@Override
	public  void actionPerformed(ActionEvent e) {		
		String input = e.getActionCommand();
		JViewport viewport = scroll.getViewport();
		ImageTable table = (ImageTable) viewport.getView();

		if(input == "+")
			table.addRow();
		
		else if(input == "-")
			table.delRow();
	
		else if(input == "OK" && table.getImgNum() > 0){
			hashDomFile = table.getHashDomFile();
			hashDomainTypes = getDomainTypes();			
			hashSampledValues = getSampledValues(table);
			setVisible(false);		
			dispose();
		} else
			new MessageDialog(new Frame(), "Error", "No Image");
			
	}

} public class Interpolator {
	
	/** The voxel size of x axis. */
	private double voxx;
	
	/** The voxel size of y axis. */
	private double voxy;
	
	/** The voxel size of z axis. */
	private double voxz;
	
	/** The size of zaxis. */
	private double zaxis;
	
	/** The width of an image. */
	private int width;
	
	/** The height of an image. */
	private int height;

	/** The depth of an image. */
	private int depth;
	
	/** The interpolated stack size. */
	private int altz;
	
	/** The ImageJ image object. */
	private ImagePlus image;		//input image
	
	/** The interpolated image object. */
	private ImageStack altimage;	//output pixel image
	
	/** The raw data of spatial image in 1D array. */
	private byte[] pixels;
	
	/** The file information of the spatial image. */
	private FileInfo info;
	
	/**
	 * Instantiates a new interpolator.
	 */
	public Interpolator(){
		
	}
	
	/**
	 * Instantiates a new interpolator with given spatial image.
	 *
	 * @param spImg the spatial image
	 */
	public Interpolator(SpatialImage spImg){
			this.image = spImg.getImage();
			getInfo(image);
			this.pixels = spImg.getRaw();
			
			if (needInterpolate()) {
				nearestNeighbor();
				image.setStack(altimage);
				info.pixelDepth =  zaxis / altz;
				image.setFileInfo(info);
				image.updateImage();
				System.out.println("interpolated voxel size " + voxx + " " + voxy + " " + info.pixelDepth);
			}
	}
	
	/**
     * Obtain following information of the given image object and sets to the corresponding variables.
	 * <ul>
     *     <li>size of the image</li>
	 *     <li>file information</li>
	 *     <li>pixel size of the image</li>
	 *     <li>size of z axis</li>
	 * </ul>
	 *
	 * @param imgPlus the ImageJ image object
	 */
	private void getInfo(ImagePlus imgPlus){
		width = imgPlus.getWidth();
		height = imgPlus.getHeight();
		depth = imgPlus.getImageStackSize();
		info = image.getOriginalFileInfo();
		voxx = info.pixelWidth;
		voxy = info.pixelHeight;
		voxz = info.pixelDepth;
		zaxis = voxz * image.getImageStackSize();
		System.out.println("voxel size " + voxx + " " + voxy + " " + voxz);
	}
	
	/**
	 * Interpolate given image object and return the interpolated image object.
	 *
	 * @param imagePlus the ImageJ image object
	 * @return the interpolated image object
	 */
	public ImagePlus interpolate(ImagePlus imagePlus){
		this.image = imagePlus;
		getInfo(image);
		ImagePlus nImg = new ImagePlus();
		pixels = ImgProcessUtil.copyMat(imagePlus);
		
		if (needInterpolate()) {
			nearestNeighbor();
			nImg.setStack(altimage);
			nImg.setFileInfo(info);
			nImg.getFileInfo().pixelDepth =  zaxis / altz;
			nImg.updateImage();
			System.out.println("interpolated voxel size " + voxx + " " + voxy + " " + info.pixelDepth);
		}else{
			nImg = (ImagePlus) imagePlus.clone();
		}

		return nImg;
	}
	
	/**
	 * Interpolate given spatial image object and return the interpolated spatial image object.
	 *
	 * @param spImg the spatial image object
	 * @return the interpolated spatial image object
	 */
	public SpatialImage interpolate(SpatialImage spImg){
		this.image = spImg.getImage();
		getInfo(image);
		this.pixels = spImg.getRaw();
		System.out.println("voxel size " + voxx + " " + voxy + " " + voxz);
		
		if (needInterpolate()) {
			nearestNeighbor();
			image.resetStack();
			image.setStack(altimage);
			info.pixelDepth =  zaxis / altz;
			image.setFileInfo(info);
			System.out.println("interpolated voxel size " + voxx + " " + voxy + " " + info.pixelDepth);
		}
		image.updateImage();
		spImg.setImage(image);
		return spImg;
	}

	/**
	 * Interpolate given set of ImageJ image objects (as hashmap of domain files, HashMap&lt;String, ImagePlus&gt;).
	 * The value of given hashmap of domain files will be replaced by the interpolated image object.
	 *
	 * @param hashdomFile the hashmap of domain file. HashMap&lt;String, ImagePlus&gt;
	 */
	public void interpolate(HashMap<String, ImagePlus> hashdomFile) {
		for(Entry<String, ImagePlus> e : hashdomFile.entrySet()){
			ImagePlus i = interpolate(e.getValue());
			hashdomFile.put(e.getKey(), i);
		}
	}
	
	/**
	 * Check if the spatial image needs interpolation.
	 * If the voxel size of each x, y and z axis is the same, then we can skip the interpolation.
	 *
	 * @return true, if spatial image needs interpolation
	 */
	private boolean needInterpolate(){
		if(depth == 1) return false;
		if(voxz > voxx || voxz > voxy)
			return true;
		
		return false;
	}

	/**
	 * The Nearest Neighbor algorithm, which is the core implementation of interpolation.
	 * There are several algorithms proposed for interpolation. For example there exist following
	 * non adaptive interpolation algorithms:
	 * <ol>
	 *     <li>Nearest Neighbour Interpolation</li>
	 *     <li>Bilinear Interpolation</li>
	 *     <li>Bicubic Interpolation</li>
	 *     <li>Re-sampling process</li>
	 * </ol>
	 * XitoSBML adopt "Nearest Neighbor Interpolation" for interpolation.
	 * Other algorithms will be implemented in the future.
	 */
	private void nearestNeighbor(){
		altz = (int) (zaxis / voxx);
		System.out.println("interpolated stack size " + altz);
		altimage = new ImageStack(width, height);
		byte matrix[];
		double xdis, ydis, zdis;
		double halfx = voxx /2, halfy = voxy /2,halfz = voxx /2;
		
		for(int d = 0 ; d < altz ; d++){
			matrix = new byte[width * height];
			for(int h = 0 ; h < height ; h++){
				for(int w = 0 ; w < width ; w++){
					// get center
					xdis = w * voxx + halfx;	 
					ydis = h * voxy + halfy;
					zdis = d * voxx + halfz;
					
					//apply to nearest original pixel
					xdis = Math.floor(xdis / voxx);
					ydis = Math.floor(ydis / voxy);
					zdis = Math.floor(zdis / voxz);
					matrix[h * width + w] = pixels[(int) (zdis * width * height + ydis * width + xdis)];	
				}
			}
			altimage.addSlice(new ByteProcessor(width,height,matrix,null));
		}
	}
	
	/**
	 * Gets the interpolated image object.
	 *
	 * @return the interpolated image as ImageJ image object
	 */
	public ImagePlus getInterpolatedImage(){
		return image;
	}

} public class SpatialImage {
	
	/** The raw data of spatial image in 1D array. */
	private byte[] raw;
	
	/** The width of an image. */
	private int width;
	
	/** The height of an image. */
	private int height;
	
	/** The depth of an image. */
	private int depth;
	
	/** The img as an ImageJ object. */
	private ImagePlus img;
	
	/** The hashmap of domain types. HashMap&lt;String, Integer&gt;*/
	private HashMap<String, Integer> hashDomainTypes;
	
	/** The hashmap of sampled value. HashMap&lt;String, Integer&gt;*/
	private HashMap<String, Integer> hashSampledValue;
	
	/** The hashmap of the count number of domains in each domain types. HashMap&lt;String, Integer&gt;*/
	private HashMap<String,Integer> hashDomainNum;
	
	/** The adjacents list of spatial image. */
	private ArrayList<ArrayList<String>> adjacentsList;
	
	/** The title of the image. */
	public String title;
	
	/** The unit of a CoordinateComponent */
	private String unit;
	
	/** The hashmap of domain InteriorPoint of spatial image. HashMap&lt;String domain name, Point3d coordinate&gt; */
	private HashMap<String,Point3d> hashDomInteriorPt;
	
	/** The delta. */
	private Point3d delta = new Point3d();
	
	/**
	 * Instantiates a new spatial image with given image object.
     * SpatialImage object is generated with given image, sampled value
	 * (pixel value of a SampledVolume) and domain types.
	 * Unit of spatial image will be adjusted by reading file information from
	 * given image.
	 *
	 * @param hashSampledValue the hashmap of sampled value, that is a pixel value of a SampledVolume
	 * @param hashDomainTypes the hashmap of domain types
	 * @param img the image as an ImageJ object
	 */
	public SpatialImage(HashMap<String, Integer> hashSampledValue, HashMap<String, Integer> hashDomainTypes, ImagePlus img){
		this.setWidth(img.getWidth());
		this.setHeight(img.getHeight());
		this.setDepth(img.getImageStackSize());
		this.img = img;
		this.setHashSampledValue(hashSampledValue);
		this.setHashDomainTypes(hashDomainTypes);
		delta.x = img.getFileInfo().pixelWidth;
		delta.y = img.getFileInfo().pixelHeight;
		delta.z = img.getFileInfo().pixelDepth;
		setUnit();
		if(img.getFileInfo().unit != null) 
			adjustUnit(img.getFileInfo().unit);
		setRawImage();
	}	
	
	/**
	 * Instantiates a new spatial image given image object.
	 * SpatialImage object is generated with given image and sampled value
	 * (pixel value of a SampledVolume).
	 * Unit of spatial image will be adjusted by reading file information from
	 * given image.
	 *
	 * @param hashSampledValue the hashmap of sampled value, that is a pixel value of a SampledVolume
	 * @param img the image as an ImageJ object
	 */
	public SpatialImage(HashMap<String, Integer> hashSampledValue, ImagePlus img){	//only for model editing
		this.setWidth(img.getWidth());
		this.setHeight(img.getHeight());
		this.setDepth(img.getImageStackSize());
		this.img = img;
		this.setHashSampledValue(hashSampledValue);
		delta.x = img.getFileInfo().pixelWidth;
		delta.y = img.getFileInfo().pixelHeight;
		delta.z = img.getFileInfo().pixelDepth;
		setUnit();
		if(img.getFileInfo().unit != null) 
			adjustUnit(img.getFileInfo().unit);
		setRawImage();
	}	
	
	/**
	 * Converts image object to an 1D array (raw data).
     * Z-stack images (3D image) will also be converted to an 1D array.
	 */
	private void setRawImage(){
		byte[] slice = null;   
		raw = new byte[width * height * depth];
    	ImageStack stack = img.getStack(); 	
    	for(int i = 1 ; i <= depth ; i++){
        	slice = (byte[]) stack.getPixels(i);
        	System.arraycopy(slice, 0, getRaw(), (i-1) * getHeight() * getWidth(), getHeight() * getWidth());
    	} 
	}
	
	/**
	 * Sets the image object and create 1D array of the image and sets to raw object.
	 *
	 * @param image the new image as an ImageJ object
	 */
	public void setImage(ImagePlus image){
		this.img = image;
		setRawImage();
	}

	/**
	 * Gets the ImageJ image object.
	 *
	 * @return the image as an ImageJ object
	 */
	public ImagePlus getImage(){
		return img;
	}

	/**
	 * Update image with given stack of images. The raw data (1D array) will be also updated.
	 *
	 * @param imStack the stack of images (ImageStack) object
	 */
	public void updateImage(ImageStack imStack){
		depth = imStack.getSize();
		img.setStack(imStack);
		setRawImage();
	}

	/**
	 * Gets the width of spatial image.
	 *
	 * @return the width of spatial image
	 */
	public int getWidth() {
		return width;
	}

	/**
	 * Sets the width of spatial image with given width value.
	 *
	 * @param width the new width of spatial image
	 */
	public void setWidth(int width) {
		this.width = width;
	}

	/**
	 * Gets the height of spatial image.
	 *
	 * @return the height of spatial image
	 */
	public int getHeight() {
		return height;
	}

	/**
	 * Sets the height of spatial image with given height value.
	 *
	 * @param height the new height of spatial image
	 */
	public void setHeight(int height) {
		this.height = height;
	}

	/**
	 * Gets the depth of spatial image.
	 *
	 * @return the depth of spatial image
	 */
	public int getDepth() {
		return depth;
	}

	/**
	 * Sets the depth of spatial image with given depth value.
	 *
	 * @param depth the new depth of spatial image
	 */
	public void setDepth(int depth) {
		this.depth = depth;
	}

	/**
	 * Gets the hash sampled value of spatial image.
	 *
	 * @return the hash sampled value of spatial image
	 */
	public HashMap<String, Integer> getHashSampledValue() {
		return hashSampledValue;
	}

	/**
	 * Sets the hashmap of sampled value of spatial image. HashMap&lt;String, Integer&gt;
     *
	 * @param hashSampledValue the hash sampled value of spatial image
	 */
	public void setHashSampledValue(HashMap<String, Integer> hashSampledValue) {
		this.hashSampledValue = hashSampledValue;
	}

	/**
	 * Gets the raw data of spatial image in 1D array.
	 *
	 * @return the raw data (1D array) of spatial image
	 */
	public byte[] getRaw() {
		return raw;
	}

	/**
	 * Sets the raw data of spatial image in 1D array.
	 *
	 * @param raw the new raw data of spatial image in 1D array
	 */
	public void setRaw(byte[] raw) {
		this.raw = raw;
	}

	/**
	 * Gets the hashmap of domain types of spatial image.
	 *
	 * @return the hashmap of domain types of spatial image
	 */
	public HashMap<String, Integer> getHashDomainTypes() {
		return hashDomainTypes;
	}

	/**
	 * Sets the hashmap of domain types. HashMap&lt;String, Integer&gt;
	 *
	 * @param hashDomainTypes the hashmap of domain types of spatial image
	 */
	public void setHashDomainTypes(HashMap<String, Integer> hashDomainTypes) {
		this.hashDomainTypes =  hashDomainTypes;
	}
	
	/**
	 * Creates the hashmap of domain types of spatial image.
	 * The value of domain type (spatialDimensions) will be set depending on
	 * the value of spatial ID reference (SpIdRef).
	 */
	public void createHashDomainTypes() {
		hashDomainTypes = new HashMap<String, Integer>();
		for (String s : hashSampledValue.keySet()) {
			if (s.contains("membrane") && depth > 1)
				hashDomainTypes.put(s, 2);
			else if(s.contains("membrane"))
				hashDomainTypes.put(s, 1);
			else if(depth > 1)
				hashDomainTypes.put(s, 3);
			else
				hashDomainTypes.put(s, 2);
		}
	}
	
	/**
	 * Gets the hashmap of the count number of domains in each domain types. HashMap&lt;String, Integer&gt;
	 *
	 * @return hashDomainNum the hashmap of the count number of domains in each domain types
	 */
	public HashMap<String, Integer> getHashDomainNum() {
		return hashDomainNum;
	}
	
	/**
	 * Sets the hashmap of the count number of domains in each domain types with given HashMap&lt;String, Integer&gt;
	 *
	 * @param hashDomainNum the hashmap of the count number of domains in each domain types
	 */
	public void setHashDomainNum(HashMap<String,Integer> hashDomainNum) {
		this.hashDomainNum = hashDomainNum;
	}

	/**
	 * Gets the list of adjacent of spatial image.
	 *
	 * @return the list of adjacent of spatial image
	 */
	public ArrayList<ArrayList<String>> getAdjacentsList() {
		return adjacentsList;
	}

	/**
	 * Sets the list of adjacent of spatial image.
	 *
	 * @param adjacentsList the new list of adjacent of spatial image
	 */
	public void setAdjacentsList(ArrayList<ArrayList<String>> adjacentsList) {
		this.adjacentsList = adjacentsList;
	}

	/**
	 * Save image as TIFF file. Both 2D and 3D TIFF images are supported.
	 *
	 * @param path the path to the directory
	 * @param name the name of TIFF file
	 */
	public void saveAsImage(String path, String name){
		FileSaver fs = new FileSaver(img);
		if(name == null) return;
		if(depth > 1)
			fs.saveAsTiffStack(path + "/" + name + ".tiff");
		else
			fs.saveAsTiff(path + "/" + name + ".tiff");
	}

	/**
	 * Gets the unit of a CoordinateComponent.
	 *
	 * @return the unit of a CoordinateComponent
	 */
	public String getUnit() {
		return unit;
	}
	
	/**
	 * Sets the unit of a CoordinateComponent.
	 */
	public void setUnit() {
		this.unit = "um";
	}

	/**
	 * Adjust unit of a CoordinateComponent.
	 *
	 * @param unit the unit of a CoodinateComponent
	 */
	//adjust img info to um
	private void adjustUnit(String unit){
		if(unit.equals("nm")){
			delta.x /= 1000;
			delta.y /= 1000;
			delta.z /= 1000;
		}
	}
	
	/**
	 * Gets the delta of spatial image.
	 *
	 * @return the delta of spatial image
	 */
	public Point3d getDelta(){
		return delta;
	}
	
	/**
	 * Sets the delta of spatial image.
	 *
	 * @param delta the new delta of spatial image
	 */
	public void setDelta(Point3d delta) {
		this.delta = delta;
	}
	
	/**
	 * Gets the hashmap of domain InteriorPoint of spatial image.
	 *
	 * @return the hashmap of domain InteriorPoint of spatial image
	 */
	public HashMap<String, org.scijava.vecmath.Point3d> getHashDomInteriorPt() {
		return hashDomInteriorPt;
	}

	/**
	 * Sets the hashmap of domain InteriorPoint of spatial image.
	 *
	 * @param hashDomInteriorPt the hashmap of domain InteriroPoint of spatial image
	 */
	public void setHashDomInteriorpt(HashMap<String,Point3d> hashDomInteriorPt) {
		this.hashDomInteriorPt = hashDomInteriorPt;
	}

} @SuppressWarnings("serial")
public class TabTables extends JFrame implements ActionListener {

	/** The JTabbedPane object. */
	private JTabbedPane tabbedpane = new JTabbedPane();
	
	/** The list of SBaseTable (SpeciesTable, ReactionTable, ParameterTable, etc.). */
	private ArrayList<SBaseTable> sbaseList = new ArrayList<SBaseTable>();
	
	/** is running. Set to false when exporting to SBML file is done. */
	private boolean isRunning = true;	

	/** The SBML model. */
	private Model model;
	
	/**
	 * Instantiates a new tab tables.
	 */
	public TabTables() {
		super("Parameter and Species Table");
		setDefaultCloseOperation(DISPOSE_ON_CLOSE);
		setSize(500, 250);
		setResizable(false);
		setLocationByPlatform(true);
		setLocationRelativeTo(null);
	}

	/**
	 * Instantiates a new tab tables with given SBML model.
	 *
	 * @param model the SBML model
	 */
	public TabTables(Model model) {
		this();
		this.model = model;
		ListOf<Parameter> lop = model.getListOfParameters();
		ListOf<Species> los = model.getListOfSpecies();
		ListOf<Reaction> lor = model.getListOfReactions();
//		ListOf<UnitDefinition> loud = model.getListOfUnitDefinitions();
		
		SpeciesTable stable = new SpeciesTable(los);		
		ParameterTable ptable = new ParameterTable(lop);		
		AdvectionTable atable = new AdvectionTable(lop);		
		BoundaryConditionTable bctable = new BoundaryConditionTable(lop);
		DiffusionTable dtable = new DiffusionTable(lop);
		ReactionTable rtable = new ReactionTable(lor);
	//	UnitDefinitionTable udtable = new UnitDefinitionTable(loud);
		
		sbaseList.add(stable);
		sbaseList.add(ptable);
		sbaseList.add(atable);
		sbaseList.add(bctable);
		sbaseList.add(dtable);
		sbaseList.add(rtable);
	//	sbaseList.add(udtable);
		
		tabbedpane.addTab("Species", stable.getPane());
		tabbedpane.addTab("Parameter", ptable.getPane());
		tabbedpane.addTab("Advection", atable.getPane());
		tabbedpane.addTab("Boundary Condition", bctable.getPane());
		tabbedpane.addTab("Diffusion", dtable.getPane());
		tabbedpane.addTab("Reaction", rtable.getPane());
		//tabbedpane.addTab("Unit Definition", udtable.getPane());
		
		// button
		JButton ok = new JButton("OK"), add = new JButton("add"), del = new JButton("delete"), edit = new JButton("edit");
		ok.addActionListener(this);
		add.addActionListener(this);
		edit.addActionListener(this);
		del.addActionListener(this);
		
		JPanel p2 = new JPanel();
		p2.setLayout(new BoxLayout(p2, BoxLayout.LINE_AXIS));
		p2.add(add);
		p2.add(del);
		p2.add(edit);
		p2.add(Box.createRigidArea(new Dimension(180, 0)));
		p2.add(ok);
		
		// set components
		getContentPane().add(p2, BorderLayout.PAGE_END);
		getContentPane().add(tabbedpane, BorderLayout.CENTER);

		setVisible(true);
	}
	
	/**
	 * Example main() method which will read an SBML file and create a GUI for editing SBML model.
	 * This example requires a specific SBML model "sam2d.xml" located under the specific directory.
	 * @param args an array of command-line arguments for the application
	 */
	public static void main(String[] args) {
		SBMLDocument d;
		try {
			d = SBMLReader.read(new File("/Users/ii/Desktop/sam2d.xml"));
			new TabTables(d.getModel());
		} catch (XMLStreamException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	/**
	 * Checks if is running.
	 * The isRunning value will be set to false when exporting to SBML file is done.
	 *
	 * @return true, if running
	 */
	public boolean isRunning(){
		return isRunning;
	}
	
	/**
     * Invoked when an action occurs.
     * This action listener will handle events when a user pressed
	 * [add], [edit], [delete] or [OK] button. When an [OK] button is pressed,
	 * the model will be exported to an SBML document, and sent to stdout.
	 * @param e the ActionEvent
	 */
	@Override
	public void actionPerformed(ActionEvent e) {
		String input = e.getActionCommand();
		int paneIndex = tabbedpane.getSelectedIndex();
		JScrollPane scroll = (JScrollPane) tabbedpane.getComponent(paneIndex);
		JViewport viewport = scroll.getViewport();
		JTable table = (JTable) viewport.getView();

		try{
			if (input.contentEquals("add")) {
				sbaseList.get(paneIndex).add();
			}else if (input.contentEquals("edit")) {
				sbaseList.get(paneIndex).edit(table.getSelectedRow());
			}else if (input.contentEquals("delete")) {
				SBase sbase = sbaseList.get(paneIndex).removeFromList(table.getSelectedRow());
				sbaseList.get(paneIndex).removeSelectedRowFromTable(table);
				model.unregister(sbase);
                                String Id = sbase.getId();//added by Morita
                                Id += "_initialConcentration";
                                SBase IC = model.getElementBySId(Id);
                                if( IC != null )
                                  model.unregister(IC);//added by Morita
			}
		} catch(IllegalArgumentException ex){
			if(ex.getCause() == null){
				JOptionPane.showMessageDialog(this, "No species in model");
			} else {
				JOptionPane.showMessageDialog(this, "Duplicate Id");
			}
			ex.printStackTrace();
		} catch (IdentifierException e1) {
			e1.printStackTrace();
		} catch (ParseException e1) {
			e1.printStackTrace();
		}
		
		if (input.equals("OK")) {
			try {
				SBMLWriter.write(model.getSBMLDocument(), System.out, ' ', (short)2);
			} catch (SBMLException e1) {
				e1.printStackTrace();
			} catch (XMLStreamException e1) {
				e1.printStackTrace();
			}
			
			isRunning = false;
			dispose();
			return;
		}
	}
} public class DomainStruct {

	/** The dimension of CoordinateComponent. */
	private int dimension;
	
	/** The list of DomainTypes. */
	private ListOf<DomainType> lodt;
	
	/** The list of Domains. */
	private ListOf<Domain> lod;
	
	/** The list of AdjacentDomains. */
	private ListOf<AdjacentDomains> load;
		
	/** The ordered list of DomainTypes. */
	private List<String> orderedList = new ArrayList<String>();
	
	/** The graph structure of an inclusion relationship of domains. */
	private GraphStruct graphStruct;
	
	/**
	 * Visualize the graph structure of an inclusion relationship of domains.
	 * Currently it supports the domain order of SampledFieldGeometry and AnalyticGeometry.
	 *
	 * @param geometry the geometry obtained from SBML model
	 */
	public void show(Geometry geometry){
		this.lodt = geometry.getListOfDomainTypes();
		this.lod = geometry.getListOfDomains();
		this.load = geometry.getListOfAdjacentDomains();
		this.dimension = geometry.getListOfCoordinateComponents().size();
		
		graphStruct = new GraphStruct();
		addVertex(lod);
		GeometryDefinition gd = geometry.getListOfGeometryDefinitions().get(0); 	//TODO multiple definitions
		
		if(gd instanceof SampledFieldGeometry){
			SampledFieldGeometry sfg = (SampledFieldGeometry) gd;
			createSampledDomainOrder(sfg.getListOfSampledVolumes());

		} else if(gd instanceof AnalyticGeometry){
			AnalyticGeometry ag = (AnalyticGeometry) gd;
			createAnalyticDomainOrder(ag.getListOfAnalyticVolumes());
		}
		addEdge(load);
		graphStruct.visualize();
	}

	/**
	 * Creates the analytic domain order.
	 *
	 * @param listOf the list of AnalyticVolume
	 */
	private void createAnalyticDomainOrder(ListOf<AnalyticVolume> listOf){
		int numDom = (int) listOf.size();
		
		for(int i = 0 ; i < numDom ; i++){
			for(int j = 0; j < numDom ; j++){
				AnalyticVolume	av = listOf.get(j);
				if(av.getOrdinal() == i){
					orderedList.add(av.getDomainType());
				}
			}
		}
	}
	
	/**
	 * Creates the sampled domain order.
	 *
	 * @param losv the list of SampledVolume
	 */
	private void createSampledDomainOrder(ListOf<SampledVolume> losv){
		int numDom = (int) losv.size();
		List<Double> sampleList = new ArrayList<Double>();
		for(int i = 0; i < numDom ; i++){
			sampleList.add(losv.get(i).getSampledValue());
		}
		Collections.sort(sampleList);
		for(int i = 0; i < numDom ; i++){
			for(int j = 0 ; j < numDom ; j++){
				SampledVolume sv = losv.get(j);
				if(sampleList.get(i) == sv.getSampledValue()){
					orderedList.add(sv.getDomainType());		
				}
			}
		}
	}
		
	/**
	 * Add edges to the graph structure of an inclusion relationship of domains.
	 * TODO change to a better algorithm since this assumes the order of list of AdjacentDomains to be specified
	 *
	 * @param load the list of AdjacentDomains
	 */
	public void addEdge(ListOf<AdjacentDomains> load){
		for(int i = 0; i < load.size(); i+=2){
			String dom1 = ((AdjacentDomains)load.get(i)).getDomain2();
			String dom2 = ((AdjacentDomains)load.get(i+1)).getDomain2();
			lod.get(dom1);
			if(getOrder(dom1, dom2))
				graphStruct.addEdge(dom1, dom2);
			else
				graphStruct.addEdge(dom2, dom1);
		}
	}

	/**
	 * Add vertices to the graph structure of an inclusion relationship of domains.
	 *
	 * @param lod the list of Domains
	 */
	public void addVertex(ListOf<Domain> lod) {
		Domain dom;
		for (int i = 0; i < lod.size(); i++) {
			dom = lod.get(i);
			if(getDomainType(dom.getDomainType()).getSpatialDimensions() == dimension)
				graphStruct.addVertex(dom.getSpatialId());
		}
	}

	/**
	 * Get domain type by given SpatialId.
	 *
	 * @param spId the SpatialId of domain type as String
	 * @return DomainType object if found, otherwise null
	 */
	public DomainType getDomainType(String spId){
	   	 for(DomainType d: lodt){
   		 if(d.getSpatialId().equals(spId)){
   			 return d;
   		 }
   	 }
		return null;
	}

	/**
	 * Compare the order of given DomainType1 and 2, and then returns true if
	 * the order of DomainType1 is larger than that of DomainType2.
	 *
	 * @param domType1 the DomainType 1
	 * @param domType2 the DomainType 2
	 * @return the order
	 */
	boolean getOrder(String domType1, String domType2){
		 domType1 = domType1.replaceAll("[0-9]","");
		 domType2 = domType2.replaceAll("[0-9]","");
		if(orderedList.indexOf(domType1) > orderedList.indexOf(domType2))
			return true; 
		else	
			return false;
	}

	/**
	 * Gets the graph structure.
	 *
	 * @return the graph structure
	 */
	public GraphStruct getGraphStruct() {
		return graphStruct;
	}

	/**
	 * Sets the graph structure.
	 *
	 * @param graphStruct the new graph structure
	 */
	public void setGraphStruct(GraphStruct graphStruct) {
		this.graphStruct = graphStruct;
	}

	/**
	 * Example main() method which will launch a GUI and draw an inclusion relationship of domains as a graph.
     * This example requires a specific SBML model "analytic_3d.xml" located under the "sample/" directory.
	 * @param args an array of command-line arguments for the application
	 */
	public static void main(String[] args){
		try {
			SBMLDocument d = SBMLReader.read(new File("sample/analytic_3d.xml"));
			Model m = d.getModel();
			SpatialModelPlugin smp = (SpatialModelPlugin) m.getPlugin("spatial");
			Geometry g = smp.getGeometry();
			DomainStruct ds = new DomainStruct();
			ds.show(g);
		} catch (XMLStreamException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
	}
	
} public class Viewer {
	
	/** The Image3DUniverse, which is used in ImageJ 3D Viewer. */
	private Image3DUniverse univ;
	
	/** The colors. */
	private Vector<Color3f> colors = new Vector<Color3f>();
	
	/** The width of an image. */
	private int width;
	
	/** The height of an image. */
	private	int height;
	
	/** The depth of an image. */
	private int depth;
	
	/** The hashmap of sampled value of spatial image.  */
	private HashMap<String, Integer> hashDoms;
	
	/** The hashmap of ImageJ image object (ImagePlus). */
	private HashMap<String, ImagePlus> hashImg = new HashMap<String, ImagePlus>();
	
	/** The raw data of spatial image in 1D array. */
	private byte[] rawMat;
	
	/** The hashmap of vertices. */
	private HashMap<String, List<Point3d>> hashVertices = new HashMap<String, List<Point3d>>();
	
	/** The hashmap of boundary. */
	private HashMap<String, Point3d> hashBound = new HashMap<String, Point3d>();
	
	/**
	 * Visualize SpatialImage object on ImageJ 3D Viewer
	 *
	 * @param spImg the The SpatialImage, which is a class for handling spatial image in XitoSBML.
	 */
	public void view(SpatialImage spImg){
		univ = new Image3DUniverse();
		this.width = spImg.getWidth();
		this.height = spImg.getHeight();
		this.depth = spImg.getDepth();
		this.hashDoms = spImg.getHashSampledValue();
		this.rawMat = spImg.getRaw();
		separateImg();
		setColors(hashImg.size());
		setImages();
	}
	
	/**
	 * Sets images to Image3DUniverse, and show the 3D space with ImageJ 3D Viewer.
	 */
	private void setImages(){
		int i = 0;
		for(Entry<String, ImagePlus> e : hashImg.entrySet()){
			Content c = univ.addMesh(e.getValue(), colors.get(i++), e.getKey(), 0, new boolean[] {true,true,true}, 1);
			c.setShaded(false);
		}
		univ.getSelected();
		univ.show();
	}
	
	/**
	 * Find points in the 3D universe.
	 * If the 3D space contains MethGroup, then add all the vertices of the mesh to hashmap of vertices.
	 * Minimum and Maximum boundary is also assigned as points.
	 */
	public void findPoints() {
		for (Entry<String, ImagePlus> e : hashImg.entrySet()) {
			Content c = univ.getContent(e.getKey());
			ContentNode node = c.getContent();
			// 各メッシュの頂点を取ってくる、メッシュは三角形を形成する
			if (node instanceof MeshGroup) {
				CustomMesh cm = ((MeshGroup) node).getMesh();
				@SuppressWarnings("unchecked")
				List<Point3d> vertices = cm.getMesh();
				hashVertices.put(c.getName(), vertices);
			}
			// get min max coordinates
			Point3d p = new Point3d();
			c.getContent().getMax(p);
			setMaxBound(p);
			p = new Point3d();
			c.getContent().getMin(p);
			setMinBound(p);
		}
	}
	
	/**
	 * Sets the maximum boundary to the hashmap of boundary.
	 *
	 * @param p the new maximum boundary
	 */
	private void setMaxBound(Point3d p){
		if(!hashBound.containsKey("max")) hashBound.put("max", p);
		else{
			Point3d tempMax = hashBound.get("max");
			if(tempMax.x < p.x) tempMax.x = p.x;
			if(tempMax.y < p.y) tempMax.y = p.y;
			if(tempMax.z < p.z) tempMax.z = p.z;
		}
	}
	
	/**
	 * Sets the minimum boundary to the hashmap of boundary.
	 *
	 * @param p the new minimum boundary
	 */
	private void setMinBound(Point3d p){
		if(!hashBound.containsKey("min")) hashBound.put("min", p);
		else{
			Point3d tempMin = hashBound.get("min");
			if(tempMin.x > p.x) tempMin.x = p.x;
			if(tempMin.y > p.y) tempMin.y = p.y;
			if(tempMin.z > p.z) tempMin.z = p.z;
		}
	}
	
	/**
	 * Sets the colors.
	 *
	 * @param size the new colors
	 */
	private void setColors(int size){
		for(int i = 0 ; i <= size; i++){
			switch(i){
				case 0:
					colors.add(new Color3f(1f,0,0));
					break;
				case 1:
					colors.add(new Color3f(0,1f,0));
					break;
				case 2:
					colors.add(new Color3f(0,0,1f));
					break;
				case 3:
					colors.add(new Color3f(1f,1f,0));
					break;
				case 4:
					colors.add(new Color3f(0,1f,1f));
					break;
				case 5:
					colors.add(new Color3f(1f,0,1f));
					break;
				default:	
					colors.add(new Color3f(0,0,0));
					break;
			}
		}
	}
	
	/**
	 * Split images to each domain images, and then convert it to label image,
	 * which is a binarised image of each domain.
	 */
	private void separateImg(){
		for(Entry<String, Integer> e : hashDoms.entrySet()){
			if(e.getValue().equals(0))
				continue;
			ImagePlus tempimg = new ImagePlus();
			tempimg.setStack(createLabelImage(e.getValue()));
			hashImg.put(e.getKey(), tempimg);
		}
	}

	/**
	 * Creates the label image.
	 * If a pixel contains an identical value with given pixVal, then replace its value to 255.
	 * This procedure will create a binarised image, which represents the domain with white pixels.
	 *
	 * @param pixVal the pixel value of domain
	 * @return the image stack
	 */
	private ImageStack createLabelImage(int pixVal){
		byte[] pixels = new byte[width*height*depth];
		for (int d = 0; d < depth; d++) {
			for (int h = 0; h < height; h++) {
				for (int w = 0; w < width; w++) {
					if(rawMat[d * height * width + h * width + w] == (byte)pixVal)
						pixels[d * height * width + h * width + w] = (byte)255;
				}
			}
		}
		return setStack(pixels);
	}
	
	/**
	 * Sets the stack with given 1D array of pixel values.
	 *
	 * @param pixels the 1D array of pixel values, which represents the 3D space
	 * @return the image stack
	 */
	private ImageStack setStack(byte[] pixels){
		ImageStack imstack = new ImageStack(width, height);
		for(int d = 0 ; d < depth ; d++){
			byte[] matrix = new byte[width * height];
			System.arraycopy(pixels, d * height * width, matrix, 0, matrix.length);
			imstack.addSlice(new ByteProcessor(width,height,matrix,null));
		}
		return imstack;
	}
	
	/**
	 * Gets the hashmap of vertices.
	 *
	 * @return the hashmap of vertices
	 */
	public HashMap<String, List<Point3d>> gethashVertices(){
		return hashVertices;
	}

	/**
	 * Gets the hashmap of boundary.
	 *
	 * @return the hashmap of boundary
	 */
	public HashMap<String, Point3d> gethashBound(){
		return hashBound;
	}

}
import java.util.HashMap;
import java.util.Map.Entry;
import javax.xml.stream.XMLStreamException;
import org.sbml.jsbml.ListOf;
import org.sbml.jsbml.Model;
import org.sbml.jsbml.Parameter;
import org.sbml.jsbml.SBMLDocument;
import org.sbml.jsbml.SBMLException;
import org.sbml.jsbml.Species;
import org.sbml.jsbml.TidySBMLWriter;
import org.sbml.jsbml.ext.spatial.Geometry;
import org.sbml.jsbml.ext.spatial.SpatialModelPlugin;
import ij.ImagePlus;
import ij.plugin.PlugIn;
import jp.ac.keio.bio.fun.xitosbml.image.CreateImage;
import jp.ac.keio.bio.fun.xitosbml.image.Filler;
import jp.ac.keio.bio.fun.xitosbml.image.ImageBorder;
import jp.ac.keio.bio.fun.xitosbml.image.ImageEdit;
import jp.ac.keio.bio.fun.xitosbml.image.ImageExplorer;
import jp.ac.keio.bio.fun.xitosbml.image.Interpolator;
import jp.ac.keio.bio.fun.xitosbml.image.SpatialImage;
import jp.ac.keio.bio.fun.xitosbml.pane.TabTables;
import jp.ac.keio.bio.fun.xitosbml.visual.DomainStruct;
import jp.ac.keio.bio.fun.xitosbml.visual.Viewer;
package jp.ac.keio.bio.fun.xitosbml.xitosbml;





/**
 * The class MainSpatial, which creates a GUI for XitoSBML.
 * Date Created: Feb 21, 2017
 *
 * @author Kaito Ii &lt;ii@fun.bio.keio.ac.jp&gt;
 * @author Akira Funahashi &lt;funa@bio.keio.ac.jp&gt;
 */
public abstract class MainSpatial implements PlugIn{

	/** The SBML document. */
	protected SBMLDocument document;
	
	/** The SBML model. */
	protected Model model;
	
	/** The SBML spatialplugin. */
	protected SpatialModelPlugin spatialplugin;

	/** The ImageExplorer. */
	protected ImageExplorer imgexp;
	
	/** The hashmap of domain types. */
	private HashMap<String, Integer> hashDomainTypes;
	
	/** The hashmap of sampled value of spatial image. */
	protected HashMap<String, Integer> hashSampledValue;
	
	/** The viewer. */
	protected Viewer viewer;
	
	/** The SpatialImage, which is a class for handling spatial image in XitoSBML. */
	protected SpatialImage spImg;
	
	/**
	 * Create a GUI which allows users to specify the correspondence between each image
	 * and the region in the cell.
	 */
	protected void gui() {
		hashDomainTypes = new HashMap<String, Integer>();
		hashSampledValue = new HashMap<String, Integer> ();
		imgexp = new ImageExplorer(hashDomainTypes,hashSampledValue);
		while (imgexp.isVisible()) {
			synchronized (hashDomainTypes) {
				synchronized (hashSampledValue) {
					
				}
			}
		}
	}
	
	/**
	 * Following process is performed to an image:
	 * <ol>
	 * <li>Interpolate an image if it is a Z-stack image (3D image) and the voxel size of each axis (x, y and z) is not equal</li>
	 * <li>Fill holes (blank pixels) in the image by morphology operation if exists</li>
	 * <li>Add a membrane between two different domains if exists</li>
	 * </ol>
	 *
	 * The converted image will be generated as {@link jp.ac.keio.bio.fun.xitosbml.image.SpatialImage},
	 * which is a base class for representing spatial image in XitoSBML.
	 */
	protected void computeImg(){
		Interpolator interpolator = new Interpolator();
		HashMap<String, ImagePlus> hashDomFile = imgexp.getDomFile();
		interpolator.interpolate(hashDomFile);
		Filler fill = new Filler();