package ij.macro;
import ij.*;
import ij.process.*;
import ij.gui.*;
import ij.measure.*;
import ij.plugin.*;
import ij.plugin.filter.*;
import ij.plugin.frame.*;
import ij.text.*;
import ij.io.*;
import ij.util.*;
import java.awt.*;
import java.awt.image.*;
import java.util.*;
import java.io.*;
import java.awt.event.KeyEvent;
import java.lang.reflect.*;
import java.net.URL;
import java.awt.datatransfer.*;

/** This class implements the built-in macro functions. */
public class Functions implements MacroConstants, Measurements {
    Interpreter interp;
    Program pgm;
    boolean updateNeeded;
    boolean autoUpdate = true;
    ImagePlus defaultImp;
    ImageProcessor defaultIP;
    int imageType;
    boolean colorSet, fontSet;
    Color defaultColor;
    double defaultValue = Double.NaN;
    Plot plot;
    static int plotID;
    int justification = ImageProcessor.LEFT_JUSTIFY;
    Font font;
    GenericDialog gd;
    PrintWriter writer;
    boolean altKeyDown, shiftKeyDown;
    boolean antialiasedText;
    StringBuffer buffer;
    RoiManager roiManager;
    Properties props;
    
    boolean saveSettingsCalled;
    boolean usePointerCursor, hideProcessStackDialog;
    float divideByZeroValue;
    int jpegQuality;
    int lineWidth;
    boolean doScaling;
    boolean weightedColor;
    double[] weights;
    boolean interpolateScaledImages, open100Percent, blackCanvas;
    boolean useJFileChooser,debugMode;
    Color foregroundColor, backgroundColor, roiColor;
    boolean pointAutoMeasure, requireControlKey, useInvertingLut;
    boolean doubleBuffer, disablePopup;
    int measurements;
    int decimalPlaces;
    boolean blackBackground;
    static Dialog waitForUserDialog;

    Functions(Interpreter interp, Program pgm) {
        this.interp = interp;
        this.pgm = pgm;
    }
 
    void doFunction(int type) {
        switch (type) {
            case RUN: doRun(); break;
            case SELECT: IJ.selectWindow(getStringArg()); resetImage(); break;
            case WAIT: IJ.wait((int)getArg()); break;
            case BEEP: interp.getParens(); IJ.beep(); break;
            case RESET_MIN_MAX: interp.getParens(); IJ.resetMinAndMax(); resetImage(); break;
            case RESET_THRESHOLD: interp.getParens(); IJ.resetThreshold(); resetImage(); break;
            case PRINT: print(); break;
            case WRITE: IJ.write(getStringArg()); break;
            case DO_WAND: IJ.doWand((int)getFirstArg(), (int)getLastArg()); resetImage(); break;
            case SET_MIN_MAX: IJ.setMinAndMax(getFirstArg(), getLastArg()); resetImage(); break;
            case SET_THRESHOLD: setThreshold(); break;
            case SET_TOOL: setTool(); break;
            case SET_FOREGROUND: setForegroundColor(); break;
            case SET_BACKGROUND: setBackgroundColor(); break;
            case SET_COLOR: setColor(); break;
            case MAKE_LINE: makeLine(); break;
            case MAKE_OVAL: makeOval(); break;
            case MAKE_RECTANGLE: makeRectangle(); break;
            case DUMP: interp.dump(); break;
            case LINE_TO: lineTo(); break;
            case MOVE_TO: moveTo(); break;
            case DRAW_LINE: drawLine(); break;
            case REQUIRES: requires(); break;
            case AUTO_UPDATE: autoUpdate = getBooleanArg(); break;
            case UPDATE_DISPLAY: interp.getParens(); updateDisplay(); break;
            case DRAW_STRING: drawString(); break;
            case SET_PASTE_MODE: IJ.setPasteMode(getStringArg()); break;
            case DO_COMMAND: doCommand(); break;
            case SHOW_STATUS: IJ.showStatus(getStringArg()); interp.statusUpdated=true; break;
            case SHOW_PROGRESS: showProgress(); break;
            case SHOW_MESSAGE: showMessage(false); break;
            case SHOW_MESSAGE_WITH_CANCEL: showMessage(true); break;
            case SET_PIXEL: case PUT_PIXEL: setPixel(); break;
            case SNAPSHOT: case RESET: case FILL: doIPMethod(type); break;
            case SET_LINE_WIDTH: getProcessor().setLineWidth((int)getArg()); break;
            case CHANGE_VALUES: changeValues(); break;
            case SELECT_IMAGE: selectImage(); break;
            case EXIT: exit(); break;
            case SET_LOCATION: setLocation(); break;
            case GET_CURSOR_LOC: getCursorLoc(); break;
            case GET_LINE: getLine(); break;
            case GET_VOXEL_SIZE: getVoxelSize(); break;
            case GET_HISTOGRAM: getHistogram(); break;
            case GET_BOUNDING_RECT: case GET_BOUNDS: getBounds(); break;
            case GET_LUT: getLut(); break;
            case SET_LUT: setLut(); break;
            case GET_COORDINATES: getCoordinates(); break;
            case MAKE_SELECTION: makeSelection(); break;
            case SET_RESULT: setResult(); break;
            case UPDATE_RESULTS: updateResults(); break;
            case SET_BATCH_MODE: setBatchMode(); break;
            case PLOT: doPlot(); break;
            case SET_JUSTIFICATION: setJustification(); break;
            case SET_Z_COORDINATE: setZCoordinate(); break;
            case GET_THRESHOLD: getThreshold(); break;
            case GET_PIXEL_SIZE: getPixelSize(); break;
            case SETUP_UNDO: interp.getParens(); Undo.setup(Undo.TRANSFORM, getImage()); break;
            case SAVE_SETTINGS: saveSettings(); break;
            case RESTORE_SETTINGS: restoreSettings(); break;
            case SET_KEY_DOWN: setKeyDown(); break;
            case OPEN: open(); break;
            case SET_FONT: setFont(); break;
            case GET_MIN_AND_MAX: getMinAndMax(); break;
            case CLOSE: close(); break;
            case SET_SLICE: setSlice(); break;
            case NEW_IMAGE: newImage(); break;
            case SAVE: IJ.save(getStringArg()); break;
            case SAVE_AS: saveAs(); break;
            case SET_AUTO_THRESHOLD: setAutoThreshold(); break;
            case RENAME: resetImage(); getImage().setTitle(getStringArg()); break;
            case GET_STATISTICS: getStatistics(true); break;
            case GET_RAW_STATISTICS: getStatistics(false); break;
            case FLOOD_FILL: floodFill(); break;
            case RESTORE_PREVIOUS_TOOL: restorePreviousTool(); break;
            case SET_VOXEL_SIZE: setVoxelSize(); break;
            case GET_LOCATION_AND_SIZE: getLocationAndSize(); break;
            case GET_DATE_AND_TIME: getDateAndTime(); break;
            case SET_METADATA: setMetadata(); break;
            case CALCULATOR: imageCalculator(); break;
            case SET_RGB_WEIGHTS: setRGBWeights(); break;
            case MAKE_POLYGON: makePolygon(); break;
            case SET_SELECTION_NAME: setSelectionName(); break;
            case DRAW_RECT: case FILL_RECT: case DRAW_OVAL: case FILL_OVAL: drawOrFill(type); break;
            case SET_OPTION: setOption(); break;
            case SHOW_TEXT: showText(); break;
            case SET_SELECTION_LOC: setSelectionLocation(); break;
            case GET_DIMENSIONS: getDimensions(); break;
            case WAIT_FOR_USER: waitForUser(); break;
            case MAKE_POINT: makePoint(); break;
        }
    }
    
    final double getFunctionValue(int type) {
        double value = 0.0;
        switch (type) {
            case GET_PIXEL: value = getPixel(); break;
            case ABS: case COS: case EXP: case FLOOR: case LOG: case ROUND: 
            case SIN: case SQRT: case TAN: case ATAN: case ASIN: case ACOS:
                value = math(type);
                break;
            case MAX_OF: case MIN_OF: case POW: case ATAN2: value=math2(type); break;
            case GET_TIME: interp.getParens(); value=System.currentTimeMillis(); break;
            case GET_WIDTH: interp.getParens(); value=getImage().getWidth(); break;
            case GET_HEIGHT: interp.getParens(); value=getImage().getHeight(); break;
            case RANDOM: value=random(); break;
            case GET_COUNT: case NRESULTS: value=getResultsCount(); break;
            case GET_RESULT: value=getResult(); break;
            case GET_NUMBER: value=getNumber(); break;
            case NIMAGES: value=getImageCount(); break;
            case NSLICES: value=getStackSize(); break;
            case LENGTH_OF: value=lengthOf(); break;
            case GET_ID: interp.getParens(); resetImage(); value=getImage().getID(); break;
            case BIT_DEPTH: interp.getParens(); value = getImage().getBitDepth(); break;
            case SELECTION_TYPE: value=getSelectionType(); break;
            case IS_OPEN: value=isOpen(); break;
            case IS_ACTIVE: value=isActive(); break;
            case INDEX_OF: value=indexOf(); break;
            case LAST_INDEX_OF: value=getFirstString().lastIndexOf(getLastString()); break;
            case CHAR_CODE_AT: value=getFirstString().charAt((int)getLastArg()); break;
            case GET_BOOLEAN: value=getBoolean(); break;
            case STARTS_WITH: case ENDS_WITH: value = startsWithEndsWith(type); break;
            case IS_NAN: value = Double.isNaN(getArg())?1:0; break;
            case GET_ZOOM: value = getZoom(); break;
            case PARSE_FLOAT: value = parseDouble(getStringArg()); break;
            case PARSE_INT: value = parseInt(); break;
            case IS_KEY_DOWN: value=isKeyDown(); break;
            case GET_SLICE_NUMBER: interp.getParens(); value=getImage().getCurrentSlice(); break;
            case SCREEN_WIDTH: case SCREEN_HEIGHT: value = getScreenDimension(type); break;
            case CALIBRATE: value = getImage().getCalibration().getCValue(getArg()); break;
            case ROI_MANAGER: value = roiManager(); break;
            case TOOL_ID: interp.getParens(); value = Toolbar.getToolId(); break;
            case IS: value = is(); break;
            case GET_VALUE: value = getValue(); break;
            case STACK: value = doStack(); break;
            case MATCHES: value = matches(); break;
            case GET_STRING_WIDTH: value = getStringWidth(); break;
            default:
                interp.error("Numeric function expected");
        }
        return value;
    }

    String getStringFunction(int type) {
        String str;
        switch (type) {
            case D2S: str = d2s(); break;
            case TO_HEX: str = toString(16); break;
            case TO_BINARY: str = toString(2); break;
            case GET_TITLE: interp.getParens(); resetImage(); str=getImage().getTitle(); break;
            case GET_STRING: str = getStringDialog(); break;
            case SUBSTRING: str = substring(); break;
            case FROM_CHAR_CODE: str = fromCharCode(); break;
            case GET_INFO: str = getInfo(); break;          
            case GET_IMAGE_INFO: interp.getParens(); str = getImageInfo(); break;           
            case GET_DIRECTORY: str = getDirectory(); break;
            case GET_ARGUMENT: interp.getParens(); str=interp.argument!=null?interp.argument:""; break;
            case TO_LOWER_CASE: str = getStringArg().toLowerCase(Locale.US); break;
            case TO_UPPER_CASE: str = getStringArg().toUpperCase(Locale.US); break;
            case RUN_MACRO: str = runMacro(false); break;
            case EVAL: str = runMacro(true); break;
            case TO_STRING: str = doToString(); break;
            case REPLACE: str = replace(); break;
            case DIALOG: str = doDialog(); break;
            case GET_METADATA: str = getMetadata(); break;
            case FILE: str = doFile(); break;
            case SELECTION_NAME: str = selectionName(); break;
            case GET_VERSION: interp.getParens();  str = IJ.getVersion(); break;
            case GET_RESULT_LABEL: str = getResultLabel(); break;
            case CALL: str = call(); break;
            case STRING: str = doString(); break;
            case EXT: str = doExt(); break;
            case EXEC: str = exec(); break;
            case LIST: str = doList(); break;
            default:
                str="";
                interp.error("String function expected");
        }
        return str;
    }

    Variable[] getArrayFunction(int type) {
        Variable[] array;
        switch (type) {
            case GET_PROFILE: array=getProfile(); break;
            case NEW_ARRAY: array = newArray(); break;
            case SPLIT: array = split(); break;
            case GET_FILE_LIST: array = getFileList(); break;
            case GET_FONT_LIST: array = getFontList(); break;
            case NEW_MENU: array = newMenu(); break;
            case GET_LIST: array = getList(); break;
            default:
                array = null;
                interp.error("Array function expected");
        }
        return array;
    }

    final double math(int type) {
        double arg = getArg();
        switch (type) {
            case ABS: return Math.abs(arg);
            case COS: return Math.cos(arg);
            case EXP: return Math.exp(arg);
            case FLOOR: return Math.floor(arg);
            case LOG: return Math.log(arg);
            case ROUND: return Math.floor(arg + 0.5);
            case SIN: return Math.sin(arg);
            case SQRT: return Math.sqrt(arg);
            case TAN: return Math.tan(arg);
            case ATAN: return Math.atan(arg);
            case ASIN: return Math.asin(arg);
            case ACOS: return Math.acos(arg);
            default: return 0.0;
        }
    }

    final double math2(int type) {
        double a1 = getFirstArg();
        double a2 = getLastArg();
        switch (type) {
            case MIN_OF: return Math.min(a1, a2);
            case MAX_OF: return Math.max(a1, a2);
            case POW: return Math.pow(a1, a2);
            case ATAN2: return Math.atan2(a1, a2);
            default: return 0.0;
        }
    }

    final String getString() {
        String str = interp.getStringTerm();
        while (true) {
            interp.getToken();
            if (interp.token=='+')
                str += interp.getStringTerm();
            else {
                interp.putTokenBack();
                break;
            }
        };
        return str;
    }

    final boolean isStringFunction() {
        Symbol symbol = pgm.table[interp.tokenAddress];
        return symbol.type==D2S;
    }

    final double getArg() {
        interp.getLeftParen();
        double arg = interp.getExpression();
        interp.getRightParen();
        return arg;
    }

    final double getFirstArg() {
        interp.getLeftParen();
        return interp.getExpression();
    }

    final double getNextArg() {
        interp.getComma();
        return interp.getExpression();
    }

    final double getLastArg() {
        interp.getComma();
        double arg = interp.getExpression();
        interp.getRightParen();
        return arg;
    }

    String getStringArg() {
        interp.getLeftParen();
        String arg = getString();
        interp.getRightParen();
        return arg;
    }

    final String getFirstString() {
        interp.getLeftParen();
        return getString();
    }

    final String getNextString() {
        interp.getComma();
        return getString();
    }

    final String getLastString() {
        interp.getComma();
        String arg = getString();
        interp.getRightParen();
        return arg;
    }

    boolean getBooleanArg() {
        interp.getLeftParen();
        double arg = interp.getBooleanExpression();
        interp.checkBoolean(arg);
        interp.getRightParen();
        return arg==0?false:true;
    }

    final Variable getVariableArg() {
        interp.getLeftParen();
        Variable v = getVariable();
        interp.getRightParen();
        return v;
    }

    final Variable getFirstVariable() {
        interp.getLeftParen();
        return getVariable();
    }

    final Variable getNextVariable() {
        interp.getComma();
        return getVariable();
    }

    final Variable getLastVariable() {
        interp.getComma();
        Variable v = getVariable();
        interp.getRightParen();
        return v;
    }

    final Variable getVariable() {
        interp.getToken();
        if (interp.token!=WORD)
            interp.error("Variable expected");
        Variable v = interp.lookupLocalVariable(interp.tokenAddress);
        if (v==null)
                v = interp.push(interp.tokenAddress, 0.0, null, interp);
        Variable[] array = v.getArray();
        if (array!=null) {
            int index = interp.getIndex();
            checkIndex(index, 0, array.length-1);
            v = array[index]; 
        }
        return v;
    }

    final Variable getFirstArrayVariable() {
        interp.getLeftParen();
        return getArrayVariable();
    }

    final Variable getNextArrayVariable() {
        interp.getComma();
        return getArrayVariable();
    }

    final Variable getLastArrayVariable() {
        interp.getComma();
        Variable v = getArrayVariable();
        interp.getRightParen();
        return v;
    }

    final Variable getArrayVariable() {
        interp.getToken();
        if (interp.token!=WORD)
            interp.error("Variable expected");
        Variable v = interp.lookupLocalVariable(interp.tokenAddress);
        if (v==null)
                v = interp.push(interp.tokenAddress, 0.0, null, interp);
        return v;
    }

    final double[] getFirstArray() {
        interp.getLeftParen();
        return getNumericArray();
    }

    final double[] getNextArray() {
        interp.getComma();
        return getNumericArray();
    }

    final double[] getLastArray() {
        interp.getComma();
        double[] a = getNumericArray();
        interp.getRightParen();
        return a;
    }

    double[] getNumericArray() {
        Variable[] a1 = getArray();
        double[] a2 = new double[a1.length];
        for (int i=0; i<a1.length; i++)
            a2[i] = a1[i].getValue();
        return a2;
    }

    String[] getStringArray() {
        Variable[] a1 = getArray();
        String[] a2 = new String[a1.length];
        for (int i=0; i<a1.length; i++) {
            String s = a1[i].getString();
            if (s==null) s = "" + a1[i].getValue();
            a2[i] = s;
        }
        return a2;
    }

    Variable[] getArray() {
        interp.getToken();
        boolean newArray = interp.token==ARRAY_FUNCTION && pgm.table[interp.tokenAddress].type==NEW_ARRAY;
        if (!(interp.token==WORD||newArray))
            interp.error("Array expected");
        Variable[] a;
        if (newArray)
            a = getArrayFunction(NEW_ARRAY);
        else {
            Variable v = interp.lookupVariable();
            a= v.getArray();
        }
        if (a==null)
            interp.error("Array expected");
        return a;
    }
        
    Color getColor() {
        String color = getString();
        color = color.toLowerCase(Locale.US);
        if (color.equals("black"))
            return Color.black;
        else if (color.equals("white"))
            return Color.white;
        else if (color.equals("red"))
            return Color.red;
        else if (color.equals("green"))
            return Color.green;
        else if (color.equals("blue"))
            return Color.blue;
        else if (color.equals("cyan"))
            return Color.cyan;
        else if (color.equals("darkgray"))
            return Color.darkGray;
        else if (color.equals("gray"))
            return Color.gray;
        else if (color.equals("lightgray"))
            return Color.lightGray;
        else if (color.equals("magenta"))
            return Color.magenta;
        else if (color.equals("orange"))
            return Color.orange;
        else if (color.equals("yellow"))
            return Color.yellow;
        else if (color.equals("pink"))
            return Color.pink;
        else
            interp.error("'red', 'green', etc. expected");
        return null;
    }

    void checkIndex(int index, int lower, int upper) {
        if (index<lower || index>upper)
            interp.error("Index ("+index+") is outside of the "+lower+"-"+upper+" range");
    }

    void doRun() {
        interp.getLeftParen();
        String arg1 = getString();
        interp.getToken();
        if (!(interp.token==')' || interp.token==','))
            interp.error("',' or ')'  expected");
        String arg2 = null;
        if (interp.token==',') {
            arg2 = getString();
            interp.getRightParen();
        }
        if (arg2!=null)
            IJ.run(arg1, arg2);
        else
            IJ.run(arg1);
        resetImage();
        IJ.setKeyUp(IJ.ALL_KEYS);
        shiftKeyDown = altKeyDown = false;
    }

    void setForegroundColor() {
        IJ.setForegroundColor((int)getFirstArg(), (int)getNextArg(), (int)getLastArg());
        resetImage(); 
        defaultColor = null;
        defaultValue = Double.NaN;
    }

    void setBackgroundColor() {
        IJ.setBackgroundColor((int)getFirstArg(), (int)getNextArg(), (int)getLastArg());
        resetImage(); 
    }

    void setColor() {
        colorSet = true;
        interp.getLeftParen();
        if (isStringArg()) {
            defaultColor = getColor();
            getProcessor().setColor(defaultColor);
            defaultValue = Double.NaN;
            interp.getRightParen();
            return;
        }
        double arg1 = interp.getExpression();
        if (interp.nextToken()==')')
            {interp.getRightParen(); setColor(arg1); return;}
        int red=(int)arg1, green=(int)getNextArg(), blue=(int)getLastArg();
        if (red<0) red=0; if (green<0) green=0; if (blue<0) blue=0; 
        if (red>255) red=255; if (green>255) green=255; if (blue>255) blue=255;  
        defaultColor = new Color(red, green, blue);
        getProcessor().setColor(defaultColor);
        defaultValue = Double.NaN;
    }
    
    void setColor(double value) {
        ImageProcessor ip = getProcessor();
        ImagePlus imp = getImage();
        switch (imp.getBitDepth()) {
            case 8:
                if (value<0 || value>255)
                    interp.error("Argument out of 8-bit range (0-255)");
                ip.setValue(value);
                break;
            case 16:
                if (imp.getLocalCalibration().isSigned16Bit())
                    value += 32768;
                if (value<0 || value>65535)
                    interp.error("Argument out of 16-bit range (0-65535)");
                ip.setValue(value);
                break;
            default:
                ip.setValue(value);
                break;
        }
        defaultValue = value;
        defaultColor = null;
    }

    void makeLine() {
        double x1d = getFirstArg();
        double y1d = getNextArg();
        double x2d = getNextArg();
        interp.getComma();
        double y2d = interp.getExpression();
        interp.getToken();
        if (interp.token==')')
            IJ.makeLine(x1d, y1d, x2d, y2d);
        else {
            int x1 = (int)Math.round(x1d);
            int y1 = (int)Math.round(y1d);
            int x2 = (int)Math.round(x2d);
            int y2 = (int)Math.round(y2d);
            int max = 200;
            int[] x = new int[max];
            int[] y = new int[max];
            x[0]=x1; y[0]=y1; x[1]=x2; y[1]=y2;
            int n = 2;
            while (interp.token==',' && n<max) {
                x[n] = (int)Math.round(interp.getExpression());
                if (n==2 && interp.nextToken()==')') {
                    interp.getRightParen();
                    Line.setWidth((int)x[n]);
                    IJ.makeLine(x1, y1, x2, y2);
                    return;
                }
                interp.getComma();
                y[n] = (int)Math.round(interp.getExpression());
                interp.getToken();
                n++;
            }
            if (n==max && interp.token!=')')
                interp.error("More than "+max+" points");
            getImage().setRoi(new PolygonRoi(x, y, n, Roi.POLYLINE));
        }
        resetImage(); 
    }

    void makeOval() {
        Roi previousRoi = getImage().getRoi();
        if (shiftKeyDown||altKeyDown) getImage().saveRoi();
        IJ.makeOval((int)getFirstArg(), (int)getNextArg(), (int)getNextArg(), (int)getLastArg());
        Roi roi = getImage().getRoi();
        if (previousRoi!=null && roi!=null)
            updateRoi(roi);
        resetImage();
    }
    
    void makeRectangle() {
        Roi previousRoi = getImage().getRoi();
        if (shiftKeyDown||altKeyDown) getImage().saveRoi();
        IJ.makeRectangle((int)getFirstArg(), (int)getNextArg(), (int)getNextArg(), (int)getLastArg());
        Roi roi = getImage().getRoi();
        if (previousRoi!=null && roi!=null)
            updateRoi(roi);
        resetImage();
    }
    
    ImagePlus getImage() {
        if (defaultImp==null)
            defaultImp = IJ.getImage();
        if (defaultImp==null)
            {interp.error("No image"); return null;}    
        if (defaultImp.getWindow()==null && IJ.getInstance()!=null && !interp.isBatchMode() && WindowManager.getTempCurrentImage()==null)
            throw new RuntimeException(Macro.MACRO_CANCELED);
        return defaultImp;
    }
    
    void resetImage() {
        defaultImp = null;
        defaultIP = null;
        colorSet = fontSet = false;
    }

    ImageProcessor getProcessor() {
        if (defaultIP==null) {
            defaultImp = getImage();
            defaultIP = defaultImp.getProcessor();
        }
        return defaultIP;
    }

    int getType() {
        if (defaultImp==null)
            defaultImp = IJ.getImage();
        imageType = defaultImp.getType();
        return imageType;
    }

    void setPixel() {
        interp.getLeftParen();
        int a1 = (int)interp.getExpression();
        interp.getComma();
        double a2 = interp.getExpression();
        interp.getToken();
        if (interp.token==',') {
            double a3 = interp.getExpression();
            interp.getRightParen();
            if (getType()==ImagePlus.GRAY32)
                getProcessor().putPixelValue(a1, (int)a2, a3);
            else
                getProcessor().putPixel(a1, (int)a2, (int)a3);
        } else {
            if (interp.token!=')') interp.error("')' expected");
            getProcessor().setf(a1, (float)a2);
        }
        updateNeeded = true;
    }

    double getPixel() {
        interp.getLeftParen();
        int a1 = (int)interp.getExpression();
        ImageProcessor ip = getProcessor();
        double value = 0.0;
        interp.getToken();
        if (interp.token==',') {
            int a2 = (int)interp.getExpression();
            interp.getRightParen();
            if (getType()==ImagePlus.GRAY32)
                value = ip.getPixelValue(a1, a2);
            else
                value = ip.getPixel(a1, a2);
        } else {
            if (interp.token!=')') interp.error("')' expected");
            value = ip.getf(a1);
        }
        return value;
    }
    
    void setZCoordinate() {
        int z = (int)getArg();
        ImagePlus imp = getImage();
        ImageStack stack = imp.getStack();
        int size = stack.getSize();
        if (z<0 || z>=size)
            interp.error("Z coordinate ("+z+") is out of 0-"+(size-1)+ " range");
        this.defaultIP = stack.getProcessor(z+1);       
    }
    
    void moveTo() {
        interp.getLeftParen();
        int a1 = (int)(interp.getExpression()+0.5);
        interp.getComma();
        int a2 = (int)(interp.getExpression()+0.5);
        interp.getRightParen();
        getProcessor().moveTo(a1, a2);
    }
    
    void lineTo() {
        interp.getLeftParen();
        int a1 = (int)(interp.getExpression()+0.5);
        interp.getComma();
        int a2 = (int)(interp.getExpression()+0.5);
        interp.getRightParen();
        ImageProcessor ip = getProcessor();
        if (!colorSet) setForegroundColor(ip);
        ip.lineTo(a1, a2);
        updateAndDraw(defaultImp);
    }

    void drawLine() {
        interp.getLeftParen();
        int x1 = (int)(interp.getExpression()+0.5);
        interp.getComma();
        int y1 = (int)(interp.getExpression()+0.5);
        interp.getComma();
        int x2 = (int)(interp.getExpression()+0.5);
        interp.getComma();
        int y2 = (int)(interp.getExpression()+0.5);
        interp.getRightParen();
        ImageProcessor ip = getProcessor();
        if (!colorSet) setForegroundColor(ip);
        ip.drawLine(x1, y1, x2, y2);
        updateAndDraw(defaultImp);
    }
    
    void setForegroundColor(ImageProcessor ip) {
        if (defaultColor!=null)
            ip.setColor(defaultColor);
        else if (!Double.isNaN(defaultValue))
            ip.setValue(defaultValue);
        else
            ip.setColor(Toolbar.getForegroundColor());
        colorSet = true;
    }

    void doIPMethod(int type) {
        interp.getParens(); 
        ImageProcessor ip = getProcessor();
        switch (type) {
            case SNAPSHOT: ip.snapshot(); break;
            case RESET:
                ip.reset();
                updateNeeded = true;
                break;
            case FILL: 
                ImagePlus imp = getImage();
                Roi roi = imp.getRoi();
                if (!colorSet) setForegroundColor(ip);
                if (roi==null) {
                    ip.resetRoi();
                    ip.fill();
                } else {
                    ip.setRoi(roi);
                    ip.fill(ip.getMask());
                }
                updateAndDraw(imp);
                break;
        }
    }

    void updateAndDraw(ImagePlus imp) {
        if (autoUpdate)
            imp.updateChannelAndDraw();
        else
            updateNeeded = true;
    }
    
    void updateDisplay() {
        if (updateNeeded && WindowManager.getImageCount()>0) {
            ImagePlus imp = getImage();
            imp.updateAndDraw();
            updateNeeded = false;
        }
    }

    void drawString() {
        interp.getLeftParen();
        String str = getString();
        interp.getComma();
        int x = (int)(interp.getExpression()+0.5);
        interp.getComma();
        int y = (int)(interp.getExpression()+0.5);
        interp.getRightParen();
        ImageProcessor ip = getProcessor();
        if (!colorSet)
            setForegroundColor(ip);
        setFont(ip);
        ip.setJustification(justification);
        ip.setAntialiasedText(antialiasedText);
        ip.drawString(str, x, y);
        updateAndDraw(defaultImp);
    }
    
    void setFont(ImageProcessor ip) {
        if (font!=null && !fontSet)
            ip.setFont(font);
        fontSet = true;
    }

    void setJustification() {
        String str = getStringArg().toLowerCase(Locale.US);
        int just = ImageProcessor.LEFT_JUSTIFY;
        if (str.equals("center"))
            just = ImageProcessor.CENTER_JUSTIFY;
        else if (str.equals("right"))
            just = ImageProcessor.RIGHT_JUSTIFY;
        justification = just;
    }

    void changeValues() {
        double darg1 = getFirstArg();
        double darg2 = getNextArg();
        double darg3 = getLastArg();
        ImagePlus imp = getImage();
        ImageProcessor ip = getProcessor();
        Roi roi = imp.getRoi();
        ImageProcessor mask = null;
        if (roi==null || !roi.isArea()) {
            ip.resetRoi();
            roi = null;
        } else {
            ip.setRoi(roi);
            mask = ip.getMask();
            if (mask!=null) ip.snapshot();
        }
        int xmin=0, ymin=0, xmax=imp.getWidth(), ymax=imp.getHeight();
        if (roi!=null) {
            Rectangle r = roi.getBounds();
            xmin=r.x; ymin=r.y; xmax=r.x+r.width; ymax=r.y+r.height;
        }
        boolean isFloat = getType()==ImagePlus.GRAY32;
        if (imp.getBitDepth()==24) {
            darg1 = (int)darg1&0xffffff;
            darg2 = (int)darg2&0xffffff;
        }
        double v;
        for (int y=ymin; y<ymax; y++) {
            for (int x=xmin; x<xmax; x++) {
                v = isFloat?ip.getPixelValue(x,y):ip.getPixel(x,y)&0xffffff;
                if (v>=darg1 && v<=darg2) {
                    if (isFloat)
                        ip.putPixelValue(x, y, darg3);
                    else
                        ip.putPixel(x, y, (int)darg3);
                }
            }
        }
        if (mask!=null) ip.reset(mask);
        if (imp.getType()==ImagePlus.GRAY16 || imp.getType()==ImagePlus.GRAY32)
            ip.resetMinAndMax();
        imp.updateAndDraw();
        updateNeeded = false;
    }

    void requires() {
        if (IJ.versionLessThan(getStringArg()))
            interp.done = true;
    }

    Random ran; 
    double random() {
        interp.getParens();
        if (ran==null)
            ran = new Random();
        return ran.nextDouble();
    }
    
    //void setSeed() {
    //  long seed = (long)getArg();
    //  if (ran==null)
    //      ran = new Random(seed);
    //  else
    //      ran.setSeed(seed);
    //}

    double getResult() {
        interp.getLeftParen();
        String column = getString();
        int row = -1;
        if (interp.nextNonEolToken()==',') {
            interp.getComma();
            row = (int)interp.getExpression();
        }
        interp.getRightParen();
        ResultsTable rt = Analyzer.getResultsTable();
        int counter = rt.getCounter();
        if (counter==0)
            interp.error("\"Results\" table empty");
        if (row==-1) row = counter-1;
        if (row<0 || row>=counter)
            interp.error("Row ("+row+") out of range");
        int col = rt.getColumnIndex(column);
        if (!rt.columnExists(col))
            return Double.NaN;
        else
            return rt.getValueAsDouble(col, row);
    }

    String getResultLabel() {
        int row = (int)getArg();
        ResultsTable rt = Analyzer.getResultsTable();
        int counter = rt.getCounter();
        if (counter==0)
            interp.error("\"Results\" table empty");
        if (row<0 || row>=counter)
            interp.error("Row ("+row+") out of range");
        String label = rt.getLabel(row);
        return label!=null?label:"";
    }

    void setResult() {
        interp.getLeftParen();
        String column = getString();
        interp.getComma();
        int row = (int)interp.getExpression();
        interp.getComma();
        double value = 0.0;
        String label = null;
        if (column.equals("Label"))
            label = getString();
        else
            value = interp.getExpression();     
        interp.getRightParen();
        ResultsTable rt = Analyzer.getResultsTable();
        if (row<0 || row>rt.getCounter())
            interp.error("Row ("+row+") out of range");
        if (row==rt.getCounter())
            rt.incrementCounter();
        try {
            if (label!=null)
                rt.setLabel(label, row);
            else
                rt.setValue(column, row, value);
        } catch (Exception e) {
            interp.error(""+e.getMessage());
        }
    }
    
    void updateResults() {
        interp.getParens();
        ResultsTable rt = Analyzer.getResultsTable();
        rt.show("Results");
    }

    double getNumber() {
        String prompt = getFirstString();
        double defaultValue = getLastArg();
        String title = interp.macroName!=null?interp.macroName:"";
        if (title.endsWith(" Options"))
            title = title.substring(0, title.length()-8);
        GenericDialog gd = new GenericDialog(title);
        int decimalPlaces = (int)defaultValue==defaultValue?0:2;
        gd.addNumericField(prompt, defaultValue, decimalPlaces);
        gd.showDialog();
        if (gd.wasCanceled()) {
            interp.done = true;
            return defaultValue;
        }
        double v = gd.getNextNumber();
        if (gd.invalidNumber())
            return defaultValue;
        else
            return v;
    }

    double getBoolean() {
        String prompt = getStringArg();
        String title = interp.macroName!=null?interp.macroName:"";
        if (title.endsWith(" Options"))
            title = title.substring(0, title.length()-8);
        YesNoCancelDialog d = new YesNoCancelDialog(IJ.getInstance(), title, prompt);
        if (d.cancelPressed()) {
            interp.done = true;
            return 0.0;
        } else if (d.yesPressed())
            return 1.0;
        else
            return 0.0;
    }

    double getBoolean2() {
        String prompt = getFirstString();
        interp.getComma();
        double defaultValue = interp.getBooleanExpression();
        interp.checkBoolean(defaultValue);
        interp.getRightParen();
        String title = interp.macroName!=null?interp.macroName:"";
        if (title.endsWith(" Options"))
            title = title.substring(0, title.length()-8);
        GenericDialog gd = new GenericDialog(title);
        gd.addCheckbox(prompt, defaultValue==1.0?true:false);
        gd.showDialog();
        if (gd.wasCanceled()) {
            interp.done = true;
            return 0.0;
        }
        return gd.getNextBoolean()?1.0:0.0;
    }
    
    String getStringDialog() {
        interp.getLeftParen();
        String prompt = getString();
        interp.getComma();
        String defaultStr = getString();
        interp.getRightParen();
        
        String title = interp.macroName!=null?interp.macroName:"";
        if (title.endsWith(" Options"))
            title = title.substring(0, title.length()-8);
        GenericDialog gd = new GenericDialog(title);
        gd.addStringField(prompt, defaultStr, 20);
        gd.showDialog();
        String str = "";
        if (gd.wasCanceled())
            interp.done = true;
        else
            str = gd.getNextString();
        return str;
    }

    String d2s() {
        return IJ.d2s(getFirstArg(), (int)getLastArg());
    }

    String toString(int base) {
        int arg = (int)getArg();
        if (base==2)
            return Integer.toBinaryString(arg);
        else
            return Integer.toHexString(arg);
    }
    
    double getStackSize() {
        interp.getParens();
        return getImage().getStackSize();
    }
    
    double getImageCount() {
        interp.getParens();
        return WindowManager.getImageCount();
    }
    
    double getResultsCount() {
        interp.getParens();
        return Analyzer.getResultsTable().getCounter();
    }

    void getCoordinates() {
        Variable xCoordinates = getFirstArrayVariable();
        Variable yCoordinates = getLastArrayVariable();
        resetImage();
        ImagePlus imp = getImage();
        Roi roi = imp.getRoi();
        if (roi==null)
            interp.error("Selection required");
        Polygon p = roi.getPolygon();
        FloatPolygon fp = roi.getFloatPolygon();
        Variable[] xa = new Variable[p.npoints];
        Variable[] ya = new Variable[p.npoints];
        if (fp!=null) { //spline fit polygon
            for (int i=0; i<p.npoints; i++)
            xa[i] = new Variable(fp.xpoints[i]);
            for (int i=0; i<p.npoints; i++)
            ya[i] = new Variable(fp.ypoints[i]);
        } else {
            for (int i=0; i<p.npoints; i++)
            xa[i] = new Variable(p.xpoints[i]);
            for (int i=0; i<p.npoints; i++)
            ya[i] = new Variable(p.ypoints[i]);
        }
        xCoordinates.setArray(xa);
        yCoordinates.setArray(ya);
    }
    
    Variable[] getProfile() {
        interp.getParens();
        ImagePlus imp = getImage();
        if (imp.getRoi()==null)
            interp.error("Selection required");
        ProfilePlot pp = new ProfilePlot(imp, IJ.altKeyDown());
        double[] array = pp.getProfile();
        if (array==null)
            {interp.done=true; return null;}
        else
            return new Variable(array).getArray();
    }

    Variable[] newArray() {
        interp.getLeftParen();
        int next = interp.nextNonEolToken();
        if (next==STRING_CONSTANT || interp.nextNextNonEolToken()==','
        || next=='-' || next==PI)
            return initNewArray();
        int size = (int)interp.getExpression();
        interp.getRightParen();
        Variable[] array = new Variable[size];
        for (int i=0; i<size; i++)
            array[i] = new Variable();
        return array;
    }
    
    Variable[] split() {
        String s1 = getFirstString();
        String s2 = null;
        if (interp.nextToken()==')')
            interp.getRightParen();
        else
            s2 = getLastString();
        if (s1==null) return null;
        String[] strings = (s2==null||s2.equals(""))?Tools.split(s1):Tools.split(s1, s2);
        Variable[] array = new Variable[strings.length];
        for (int i=0; i<strings.length; i++)
            array[i] = new Variable(0, 0.0, strings[i]);
        return array;
    }

    Variable[] getFileList() {
        String dir = getStringArg();
        File f = new File(dir);
        if (!f.exists() || !f.isDirectory())
            return new Variable[0];
        String[] list = f.list();
        if (list==null)
            return new Variable[0];
        if (System.getProperty("os.name").indexOf("Linux")!=-1)
            ij.util.StringSorter.sort(list);
        File f2;
        int hidden = 0;
        for (int i=0; i<list.length; i++) {
            if (list[i].startsWith(".")) {
                list[i] = null;
                hidden++;
            } else {
                f2 = new File(dir, list[i]);
                if (f2.isDirectory())
                    list[i] = list[i] + "/";
            }
        }
        int n = list.length-hidden;
        if (n<=0)
            return new Variable[0];
        if (hidden>0) {
            String[] list2 = new String[n];
            int j = 0;
            for (int i=0; i<list.length; i++) {
                if (list[i]!=null)
                    list2[j++] = list[i];
            }
            list = list2;
        }
        Variable[] array = new Variable[n];
        for (int i=0; i<n; i++)
            array[i] = new Variable(0, 0.0, list[i]);
        return array;
    }
    
    Variable[] initNewArray() {
        Vector vector = new Vector();
        int size = 0;
        boolean stringArray = false;
        do {
            Variable v = new Variable();
            int tok = interp.nextNonEolToken();
            if (tok==STRING_CONSTANT||tok==STRING_FUNCTION||(tok==WORD&&stringArray)) {
                v.setString(getString());
                stringArray = true;
            } else
                v.setValue(interp.getExpression());
            vector.addElement(v);
            size++;
            interp.getToken();              
        } while (interp.token==',');
        if (interp.token!=')')
            interp.error("';' expected");
        Variable[] array = new Variable[size];
        vector.copyInto((Variable[])array);
        return array;
    }

    String fromCharCode() {
        char[] chars = new char[100];
        int count = 0;
        interp.getLeftParen();
        while(interp.nextToken()!=')') {
            int value = (int)interp.getExpression();
            if (value<0 || value>65535)
                interp.error("Value (" + value + ") out of 0-65535 range");
            chars[count++] = (char)value;
            if (interp.nextToken()==',')
                interp.getToken();
        }
        interp.getRightParen();     
        return new String(chars, 0, count);
    }

    String getInfo() {
        if (interp.nextNextNonEolToken()==STRING_CONSTANT
        || (interp.nextNonEolToken()=='('&&interp.nextNextNonEolToken()!=')'))
            return getInfo(getStringArg());
        else {
            interp.getParens();
            return getWindowContents();
        }
    }
    
    String getInfo(String key) {
            if (key.equals("image.subtitle")) {
                ImagePlus imp = getImage();
                ImageWindow win = imp.getWindow();
                return win!=null?win.createSubtitle():"";
            } else if (key.equals("slice.label")) {
                ImagePlus imp = getImage();
                if (imp.getStackSize()==1) return "";
                String label = imp.getStack().getShortSliceLabel(imp.getCurrentSlice());
                return label!=null?label:"";
            } else if (key.equals("window.contents")) {
                return getWindowContents();
            } else if (key.equals("image.description")) {
                String description = "";
                FileInfo fi = getImage().getOriginalFileInfo();
                if (fi!=null) description = fi.description;
                if  (description==null) description = "";
                return description;
            } else {
                String value = "";
                try {value = System.getProperty(key);}
                catch (Exception e) {};
                return value!=null?value:"";
            }
    }
    
    String getWindowContents() {
        Frame frame = WindowManager.getFrontWindow();
        if (frame!=null && frame instanceof TextWindow) {
            TextPanel tp = ((TextWindow)frame).getTextPanel();
            return tp.getText();            
        } else if (frame!=null && frame instanceof Editor) {
            return ((Editor)frame).getText();           
        } else if (frame!=null && frame instanceof Recorder) {
            return ((Recorder)frame).getText();         
        } else
            return getImageInfo();
    }
    
    String getImageInfo() {     
        ImagePlus imp = getImage();
        Info infoPlugin = new Info();
        return infoPlugin.getImageInfo(imp, getProcessor());
    }

    public String getDirectory() {
        String dir = IJ.getDirectory(getStringArg());
        if (dir==null) dir = "";
        return dir;
    }

    double getSelectionType() {
        interp.getParens();
        double type = -1;
        ImagePlus imp = getImage();
        Roi roi = imp.getRoi();
        if (roi!=null)
            type = roi.getType();
        return type;
    }

    void showMessage(boolean withCancel) {
        String message;
        interp.getLeftParen();
        String title = getString();
        if (interp.nextToken()==',') {
            interp.getComma();
            message = getString();
        } else {
            message = title;
            title = "";
        }
        interp.getRightParen();
        if (withCancel)
            IJ.showMessageWithCancel(title, message);
        else
            IJ.showMessage(title, message);
    }
    
    double lengthOf() {
        int length = 0;
        interp.getLeftParen();
        switch (interp.nextToken()) {
            case STRING_CONSTANT:
            case STRING_FUNCTION:
            case USER_FUNCTION:
                length = getString().length();
                break; 
            case WORD:
                if (pgm.code[interp.pc+2]=='[') {
                    length = getString().length();
                    break;
                }
                interp.getToken();
                Variable v = interp.lookupVariable();
                if (v==null) return 0.0;
                String s = v.getString();
                if (s!=null)
                    length = s.length();
                else {
                    Variable[] array = v.getArray();
                    if (array!=null)
                        length = array.length;
                    else
                        interp.error("String or array expected");
                }                   
                break;
            default:
                interp.error("String or array expected");
        }
        interp.getRightParen();
        return length;
    }
    
    void getCursorLoc() {
        Variable x = getFirstVariable();
        Variable y = getNextVariable();
        Variable z = getNextVariable();
        Variable flags = getLastVariable();
        ImagePlus imp = getImage();
        ImageCanvas ic = imp.getCanvas();
        if (ic==null) return;
        Point p = ic.getCursorLoc();
        x.setValue(p.x);
        y.setValue(p.y);
        z.setValue(imp.getCurrentSlice()-1);
        flags.setValue(ic.getModifiers());
    }
    
    void getLine() {
        Variable vx1 = getFirstVariable();
        Variable vy1 = getNextVariable();
        Variable vx2 = getNextVariable();
        Variable vy2 = getNextVariable();
        Variable lineWidth = getLastVariable();
        resetImage();
        ImagePlus imp = getImage();
        double x1=-1, y1=-1, x2=-1, y2=-1;
        Roi roi = imp.getRoi();
        if (roi!=null && roi.getType()==Roi.LINE) {
            Line line = (Line)roi;
            x1=line.x1d; y1=line.y1d; x2=line.x2d; y2=line.y2d;
        }
        vx1.setValue(x1);
        vy1.setValue(y1);
        vx2.setValue(x2);
        vy2.setValue(y2);               
        lineWidth.setValue(Line.getWidth());                
    }
    
    void getVoxelSize() {
        Variable width = getFirstVariable();
        Variable height = getNextVariable();
        Variable depth = getNextVariable();
        Variable unit = getLastVariable();
        resetImage();
        ImagePlus imp = getImage();
        Calibration cal = imp.getCalibration();
        width.setValue(cal.pixelWidth);
        height.setValue(cal.pixelHeight);
        depth.setValue(cal.pixelDepth);
        unit.setString(cal.getUnits());
    }
    
    void getHistogram() {
        interp.getLeftParen();
        Variable values = null;
        if (interp.nextToken()==NUMBER)
            interp.getExpression();
        else
            values = getArrayVariable();
        Variable counts = getNextArrayVariable();
        interp.getComma();
        int nBins = (int)interp.getExpression();
        ImagePlus imp = getImage();
        double histMin=0.0, histMax=0.0;
        boolean setMinMax = false;
        int bitDepth = imp.getBitDepth();
        if (interp.nextToken()==',') {
            histMin = getNextArg();
            histMax = getLastArg();
            if (bitDepth==8 || bitDepth==24)
                interp.error("16 or 32-bit image required to set histMin and histMax");
            setMinMax = true;
        } else 
            interp.getRightParen();
        if ((bitDepth==8||bitDepth==24) && nBins!=256)
            interp.error("Bin count ("+nBins+") must be 256 for 8-bit and RGB images");
        if (nBins==65536 && bitDepth==16) {
            Variable[] array = counts.getArray();
            int[] hist = getProcessor().getHistogram();
            if (array!=null && array.length==nBins) {
                for (int i=0; i<nBins; i++)
                    array[i].setValue(hist[i]);
            } else
                counts.setArray(new Variable(hist).getArray());
            return;
        }
        ImageStatistics stats;
        if (setMinMax)
            stats = imp.getStatistics(AREA+MEAN+MODE+MIN_MAX, nBins, histMin, histMax);
        else
            stats = imp.getStatistics(AREA+MEAN+MODE+MIN_MAX, nBins);
        if (values!=null) {
            Calibration cal = imp.getCalibration();
            double[] array = new double[nBins];
            double value = cal.getCValue(stats.histMin);
            double inc = 1.0;
            if (bitDepth==16 || bitDepth==32 || cal.calibrated())
                inc = (cal.getCValue(stats.histMax) - cal.getCValue(stats.histMin))/stats.nBins;
            for (int i=0; i<nBins; i++) {
                array[i] = value;
                value += inc;
            }
            values.setArray(new Variable(array).getArray());
        }
        Variable[] array = counts.getArray();
        if (array!=null && array.length==nBins) {
            for (int i=0; i<nBins; i++)
                array[i].setValue(stats.histogram[i]);
        } else
            counts.setArray(new Variable(stats.histogram).getArray());
    }
    
    void getLut() {
        Variable reds = getFirstArrayVariable();
        Variable greens = getNextArrayVariable();
        Variable blues = getLastArrayVariable();
        resetImage();
        ImagePlus imp = getImage();
        IndexColorModel cm = null;
        if (imp.isComposite())
            cm = ((CompositeImage)imp).getChannelLut();
        else {
            ImageProcessor ip = imp.getProcessor();
            if (ip instanceof ColorProcessor)
                interp.error("Non-RGB image expected");
            cm = (IndexColorModel)ip.getColorModel();
        }
        int mapSize = cm.getMapSize();
        byte[] rLUT = new byte[mapSize];
        byte[] gLUT = new byte[mapSize];
        byte[] bLUT = new byte[mapSize];
        cm.getReds(rLUT); 
        cm.getGreens(gLUT); 
        cm.getBlues(bLUT);
        reds.setArray(new Variable(rLUT).getArray());
        greens.setArray(new Variable(gLUT).getArray());
        blues.setArray(new Variable(bLUT).getArray());
    }

    void setLut() {
        double[] reds = getFirstArray();
        double[] greens = getNextArray();
        double[] blues = getLastArray();
        int length = reds.length;       
        if (greens.length!=length || blues.length!=length)
            interp.error("Arrays are not the same length");
        resetImage();
        ImagePlus imp = getImage();
        if (imp.getBitDepth()==24)
            interp.error("Non-RGB image expected");
        ImageProcessor ip = getProcessor();
        byte[] r = new byte[length];
        byte[] g = new byte[length];
        byte[] b = new byte[length];
        for (int i=0; i<length; i++) {
            r[i] = (byte)reds[i];
            g[i] = (byte)greens[i];
            b[i] = (byte)blues[i];
        }
        LUT lut = new LUT(8, length, r, g, b);
        if (imp.isComposite())
            ((CompositeImage)imp).setChannelLut(lut);
        else
            ip.setColorModel(lut);
        imp.updateAndDraw();
        updateNeeded = false;
    }

    void getThreshold() {
        Variable lower = getFirstVariable();
        Variable upper = getLastVariable();
        ImagePlus imp = getImage();
        ImageProcessor ip = getProcessor();
        double t1 = ip.getMinThreshold();
        double t2 = ip.getMaxThreshold();
        if (t1==ImageProcessor.NO_THRESHOLD) {
            t1 = -1;
            t2 = -1;
        } else if (imp.getBitDepth()==16) {
            Calibration cal = imp.getCalibration();
            t1 = cal.getCValue(t1); 
            t2 = cal.getCValue(t2); 
        }

        lower.setValue(t1);
        upper.setValue(t2);
    }

    void getPixelSize() {
        Variable unit = getFirstVariable();
        Variable width = getNextVariable();
        Variable height = getNextVaria