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.*; import java.awt.geom.*; /** This class implements the built-in macro functions. */ public class Functions implements MacroConstants, Measurements { Interpreter interp; Program pgm; boolean updateNeeded; boolean autoUpdate = true; ImageProcessor defaultIP; ImagePlus defaultImp; int imageType; boolean fontSet; Color globalColor; double globalValue = Double.NaN; int globalLineWidth; Plot plot; static int plotID; int justification = ImageProcessor.LEFT_JUSTIFY; Font font; GenericDialog gd; PrintWriter writer; boolean altKeyDown, shiftKeyDown; boolean antialiasedText = IJ.isMacOSX()?true:false; //non-antialiased text is broken on macOS boolean nonScalableText; StringBuffer buffer; RoiManager roiManager; Properties props; CurveFitter fitter; boolean showFitDialog; boolean logFitResults; boolean resultsPending; Overlay offscreenOverlay; Overlay overlayClipboard; static Roi roiClipboard; GeneralPath overlayPath; boolean overlayDrawLabels; ResultsTable currentTable; ResultsTable unUpdatedTable; // save/restore settings boolean saveSettingsCalled; boolean usePointerCursor, hideProcessStackDialog; float divideByZeroValue; int jpegQuality; int saveLineWidth; boolean doScaling; boolean weightedColor; double[] weights; boolean interpolateScaledImages, open100Percent, blackCanvas; boolean useJFileChooser,debugMode; Color foregroundColor, backgroundColor, roiColor; boolean pointAutoMeasure, requireControlKey, useInvertingLut; boolean disablePopup; int measurements; int decimalPlaces; boolean blackBackground; boolean autoContrast; static WaitForUserDialog waitForUserDialog; int pasteMode; boolean expandableArrays = true; int plotWidth; int plotHeight; int plotFontSize; boolean plotInterpolate; boolean plotNoGridLines; boolean plotNoTicks; boolean profileVerticalProfile; boolean profileSubPixelResolution; boolean waitForCompletion = true; Functions(Interpreter interp, Program pgm) { this.interp = interp; this.pgm = pgm; } void doFunction(int type) { switch (type) { case RUN: doRun(); break; case SELECT: selectWindow(); 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: case WRITE: print(); break; case DO_WAND: doWand(); break; case SET_MIN_MAX: setMinAndMax(); 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_ARROW: makeArrow(); break; case MAKE_OVAL: makeOval(); break; case MAKE_RECTANGLE: makeRectangle(); break; case MAKE_ROTATED_RECT: makeRotatedRectangle(); 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(); updateNeeded=true; updateDisplay(); break; case DRAW_STRING: drawString(); break; case SET_PASTE_MODE: IJ.setPasteMode(getStringArg()); break; case DO_COMMAND: doCommand(); break; case SHOW_STATUS: showStatus(); 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: 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(true); 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(null); break; case UPDATE_RESULTS: updateResults(); break; case SET_BATCH_MODE: setBatchMode(); 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.MACRO, 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: 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; case MAKE_TEXT: makeText(); break; case MAKE_ELLIPSE: makeEllipse(); break; case GET_DISPLAYED_AREA: getDisplayedArea(); break; case TO_SCALED: toScaled(); break; case TO_UNSCALED: toUnscaled(); 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 MATH: value = doMath(); 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(null); 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(); 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(null); break; case LAST_INDEX_OF: value=getFirstString().lastIndexOf(getLastString()); break; case CHAR_CODE_AT: value=charCodeAt(); 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(null); break; case GET_STRING_WIDTH: value = getStringWidth(); break; case FIT: value = fit(); break; case OVERLAY: value = overlay(); break; case SELECTION_CONTAINS: value = selectionContains(); break; case PLOT: value = doPlot(); 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(); str=getImage().getTitle(); break; case GET_STRING: str = getStringDialog(); break; case SUBSTRING: str=substring(null); 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: case GET_DIR: 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(null); 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; case DEBUG: str = debug(); break; case IJ_CALL: str = doIJ(); break; case GET_RESULT_STRING: str = getResultString(null); break; case TRIM: str = trim(); 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; case ARRAY_FUNC: array = doArray(); break; default: array = null; interp.error("Array function expected"); } return array; } // Functions returning a string must be added // to isStringFunction(String,int). Variable getVariableFunction(int type) { Variable var = null; switch (type) { case TABLE: var = doTable(); break; case ROI: var = doRoi(); break; case ROI_MANAGER2: var = doRoiManager(); break; case PROPERTY: var = doProperty(); break; case IMAGE: var = doImage(); break; case COLOR: var = doColor(); break; default: interp.error("Variable function expected"); } if (var==null) var = new Variable(Double.NaN); return var; } private void setLineWidth(int width) { if (WindowManager.getCurrentImage()!=null) { if (overlayPath!=null && width!=globalLineWidth) addDrawingToOverlay(getImage()); getProcessor().setLineWidth(width); } globalLineWidth = width; } private double doMath() { interp.getToken(); if (interp.token!='.') interp.error("'.' expected"); interp.getToken(); if (!(interp.token==WORD||interp.token==NUMERIC_FUNCTION)) interp.error("Function name expected: "); String name = interp.tokenString; if (name.equals("min")) return Math.min(getFirstArg(), getLastArg()); else if (name.equals("max")) return Math.max(getFirstArg(), getLastArg()); else if (name.equals("pow")) return Math.pow(getFirstArg(), getLastArg()); else if (name.equals("atan2")) return Math.atan2(getFirstArg(), getLastArg()); else if (name.equals("constrain")) return Math.min(Math.max(getFirstArg(), getNextArg()), getLastArg()); else if (name.equals("map")) { double value = getFirstArg(); double fromLow = getNextArg(); double fromHigh = getNextArg(); double toLow = getNextArg(); double toHigh = getLastArg(); return (value-fromLow)*(toHigh-toLow)/(fromHigh-fromLow)+toLow; } double arg = getArg(); if (name.equals("ceil")) return Math.ceil(arg); else if (name.equals("abs")) return Math.abs(arg); else if (name.equals("cos")) return Math.cos(arg); else if (name.equals("exp")) return Math.exp(arg); else if (name.equals("floor")) return Math.floor(arg); else if (name.equals("log")) return Math.log(arg); else if (name.equals("log10")) return Math.log10(arg); else if (name.equals("round")) return Math.round(arg); else if (name.equals("sin")) return Math.sin(arg); else if (name.equals("sqr")) return arg*arg; else if (name.equals("sqrt")) return Math.sqrt(arg); else if (name.equals("tan")) return Math.tan(arg); else if (name.equals("atan")) return Math.atan(arg); else if (name.equals("asin")) return Math.asin(arg); else if (name.equals("acos")) return Math.acos(arg); else if (name.equals("erf")) return IJMath.erf(arg); else if (name.equals("toRadians")) return Math.toRadians(arg); else if (name.equals("toDegrees")) return Math.toDegrees(arg); else interp.error("Unrecognized function name"); return Double.NaN; } 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, v.getArraySize()-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; iupper) 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(); } IJ.run(this.interp, arg1, arg2); resetImage(); IJ.setKeyUp(IJ.ALL_KEYS); shiftKeyDown = altKeyDown = false; } private void selectWindow() { String title = getStringArg(); if (resultsPending && "Results".equals(title)) { ResultsTable rt = ResultsTable.getResultsTable(); if (rt!=null && rt.size()>0) rt.show("Results"); } IJ.selectWindow(title); resetImage(); interp.selectCount++; } void setForegroundColor() { boolean isImage = WindowManager.getCurrentImage()!=null; int lnWidth = 0; if (isImage) lnWidth = getProcessor().getLineWidth(); int red=0, green=0, blue=0; int arg1 = (int)getFirstArg(); if (interp.nextToken()==')') { interp.getRightParen(); red = (arg1&0xff0000)>>16; green = (arg1&0xff00)>>8; blue = arg1&0xff; } else { red = arg1; green = (int)getNextArg(); blue = (int)getLastArg(); } IJ.setForegroundColor(red, green, blue); resetImage(); if (isImage) setLineWidth(lnWidth); globalColor = null; globalValue = Double.NaN; } void setBackgroundColor() { int red=0, green=0, blue=0; int arg1 = (int)getFirstArg(); if (interp.nextToken()==')') { interp.getRightParen(); red = (arg1&0xff0000)>>16; green = (arg1&0xff00)>>8; blue = arg1&0xff; } else { red = arg1; green = (int)getNextArg(); blue = (int)getLastArg(); } IJ.setBackgroundColor(red, green, blue); resetImage(); } void setColor() { interp.getLeftParen(); if (isStringArg()) { globalColor = getColor(); globalValue = Double.NaN; ImagePlus imp = WindowManager.getCurrentImage(); if (imp!=null) { if (overlayPath!=null) addDrawingToOverlay(imp); getProcessor().setColor(globalColor); getProcessor().fillColorSet(true); } 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; globalColor = new Color(red, green, blue); globalValue = Double.NaN; if (WindowManager.getCurrentImage()!=null) { getProcessor().setColor(globalColor); getProcessor().fillColorSet(true); } } 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; } globalValue = value; globalColor = null; ip.fillColorSet(true); } 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 { Polygon points = new Polygon(); points.addPoint((int)Math.round(x1d),(int)Math.round(y1d)); points.addPoint((int)Math.round(x2d),(int)Math.round(y2d)); while (interp.token==',') { int x = (int)Math.round(interp.getExpression()); if (points.npoints==2 && interp.nextToken()==')') { interp.getRightParen(); Roi line = new Line(x1d, y1d, x2d, y2d); line.updateWideLine((float)x); getImage().setRoi(line); return; } interp.getComma(); int y = (int)Math.round(interp.getExpression()); points.addPoint(x,y); interp.getToken(); } getImage().setRoi(new PolygonRoi(points, Roi.POLYLINE)); } resetImage(); } void makeArrow() { String options = ""; double x1 = getFirstArg(); double y1 = getNextArg(); double x2 = getNextArg(); double y2 = getNextArg(); if (interp.nextToken()==',') options = getNextString(); interp.getRightParen(); Arrow arrow = new Arrow(x1, y1, x2, y2); arrow.setStyle(options); getImage().setRoi(arrow); } void makeOval() { Roi previousRoi = getImage().getRoi(); if (shiftKeyDown||altKeyDown) getImage().saveRoi(); IJ.makeOval(getFirstArg(), getNextArg(), getNextArg(), getLastArg()); Roi roi = getImage().getRoi(); if (previousRoi!=null && roi!=null) updateRoi(roi); resetImage(); shiftKeyDown = altKeyDown = false; IJ.setKeyUp(IJ.ALL_KEYS); } void makeRectangle() { Roi previousRoi = getImage().getRoi(); if (shiftKeyDown||altKeyDown) getImage().saveRoi(); double x = getFirstArg(); double y = getNextArg(); double w = getNextArg(); double h = getNextArg(); int arcSize = 0; if (interp.nextToken()==',') { interp.getComma(); arcSize = (int)interp.getExpression(); } interp.getRightParen(); if (arcSize<1) IJ.makeRectangle(x, y, w, h); else { ImagePlus imp = getImage(); imp.setRoi(new Roi(x,y,w,h,arcSize)); } Roi roi = getImage().getRoi(); if (previousRoi!=null && roi!=null) updateRoi(roi); resetImage(); shiftKeyDown = altKeyDown = false; IJ.setKeyUp(IJ.ALL_KEYS); } void makeRotatedRectangle() { getImage().setRoi(new RotatedRectRoi(getFirstArg(), getNextArg(), getNextArg(), getNextArg(), getLastArg())); resetImage(); } ImagePlus getImage() { ImagePlus imp = IJ.getImage(interp); if (imp.getWindow()==null && IJ.getInstance()!=null && !interp.isBatchMode() && WindowManager.getTempCurrentImage()==null) throw new RuntimeException(Macro.MACRO_CANCELED); defaultIP = null; defaultImp = imp; return imp; } void resetImage() { defaultImp = null; defaultIP = null; fontSet = false; } ImageProcessor getProcessor() { if (defaultIP==null) { defaultIP = getImage().getProcessor(); if (globalLineWidth>0) defaultIP.setLineWidth(globalLineWidth); if (globalColor!=null) defaultIP.setColor(globalColor); else if (!Double.isNaN(globalValue)) defaultIP.setValue(globalValue); else defaultIP.setColor(Toolbar.getForegroundColor()); } return defaultIP; } int getType() { imageType = getImage().getType(); return imageType; } void setPixel() { interp.getLeftParen(); int a1 = (int)interp.getExpression(); interp.getComma(); double a2 = interp.getExpression(); interp.getToken(); ImageProcessor ip = getProcessor(); if (interp.token==',') { double a3 = interp.getExpression(); interp.getRightParen(); if (ip instanceof FloatProcessor) ip.putPixelValue(a1, (int)a2, a3); else ip.putPixel(a1, (int)a2, (int)a3); } else { if (interp.token!=')') interp.error("')' expected"); if (ip instanceof ColorProcessor) ip.set(a1, (int)a2); else ip.setf(a1, (float)a2); } updateNeeded = true; } double getPixel() { interp.getLeftParen(); double a1 = interp.getExpression(); ImageProcessor ip = getProcessor(); double value = 0.0; interp.getToken(); if (interp.token==',') { double a2 = interp.getExpression(); interp.getRightParen(); int ia1 = (int)a1; int ia2 = (int)a2; if (a1==ia1 && a2==ia2) { if (ip instanceof FloatProcessor) value = ip.getPixelValue(ia1, ia2); else value = ip.getPixel(ia1, ia2); } else { if (ip instanceof ColorProcessor) value = ip.getPixelInterpolated(a1, a2); else { ImagePlus imp = getImage(); Calibration cal = imp.getCalibration(); imp.setCalibration(null); ip = imp.getProcessor(); value = ip.getInterpolatedValue(a1, a2); imp.setCalibration(cal); } } } else { if (interp.token!=')') interp.error("')' expected"); if (ip instanceof ColorProcessor) value = ip.get((int)a1); else value = ip.getf((int)a1); } return value; } void setZCoordinate() { int z = (int)getArg(); int n = z + 1; ImagePlus imp = getImage(); ImageStack stack = imp.getStack(); int size = stack.size(); if (z<0 || z>=size) interp.error("Z coordinate ("+z+") is out of 0-"+(size-1)+ " range"); this.defaultIP = stack.getProcessor(n); } void moveTo() { interp.getLeftParen(); int a1 = (int)Math.round(interp.getExpression()); interp.getComma(); int a2 = (int)Math.round(interp.getExpression()); interp.getRightParen(); getProcessor().moveTo(a1, a2); } void lineTo() { interp.getLeftParen(); int a1 = (int)Math.round(interp.getExpression()); interp.getComma(); int a2 = (int)Math.round(interp.getExpression()); interp.getRightParen(); ImageProcessor ip = getProcessor(); ip.lineTo(a1, a2); updateAndDraw(); } void drawLine() { interp.getLeftParen(); int x1 = (int)Math.round(interp.getExpression()); interp.getComma(); int y1 = (int)Math.round(interp.getExpression()); interp.getComma(); int x2 = (int)Math.round(interp.getExpression()); interp.getComma(); int y2 = (int)Math.round(interp.getExpression()); interp.getRightParen(); ImageProcessor ip = getProcessor(); ip.drawLine(x1, y1, x2, y2); updateAndDraw(); } 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 (roi==null) { ip.resetRoi(); ip.fill(); } else { ip.setRoi(roi); ip.fill(ip.getMask()); } imp.updateAndDraw(); break; } } void updateAndDraw() { if (autoUpdate) { ImagePlus imp = defaultImp; if (imp==null) imp = getImage(); imp.updateChannelAndDraw(); imp.changes = true; } 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); Color background = null; if (interp.nextToken()==',') { interp.getComma(); background = getColor(); } interp.getRightParen(); ImageProcessor ip = getProcessor(); setFont(ip); ip.setJustification(justification); ip.setAntialiasedText(antialiasedText); if (background!=null) ip.drawString(str, x, y, background); else ip.drawString(str, x, y); updateAndDraw(); } 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=darg1 && v<=darg2; if (Double.isNaN(darg1) && Double.isNaN(darg2) && Double.isNaN(v)) replace = true; if (replace) { if (isFloat) ip.putPixelValue(x, y, darg3); else ip.putPixel(x, y, (int)darg3); } } } if (mask!=null) ip.reset(mask); imp.updateAndDraw(); updateNeeded = false; } void requires() { if (IJ.versionLessThan(getStringArg())) interp.done = true; } Random ran; double random() { double dseed = Double.NaN; boolean gaussian = false; if (interp.nextToken()=='(') { interp.getLeftParen(); if (isStringArg()) { String arg = getString().toLowerCase(Locale.US); if (arg.equals("seed")) { interp.getComma(); dseed = interp.getExpression(); long seed = (long)dseed; if (seed!=dseed) interp.error("Seed not integer"); ran = new Random(seed); ImageProcessor.setRandomSeed(seed); } else if (arg.equals("gaussian")) gaussian = true; else interp.error("'seed' or ''gaussian' expected"); } interp.getRightParen(); if (!Double.isNaN(dseed)) return Double.NaN; } ImageProcessor.setRandomSeed(Double.NaN); interp.getParens(); if (ran==null) ran = new Random(); if (gaussian) return ran.nextGaussian(); else return ran.nextDouble(); } double getResult(ResultsTable rt) { interp.getLeftParen(); String column = getString(); int row = -1; if (interp.nextToken()==',') { interp.getComma(); row = (int)interp.getExpression(); } if (interp.nextToken()==',') { interp.getComma(); String title = getString(); rt = getResultsTable(title); } if (rt==null) rt = getResultsTable(true); interp.getRightParen(); int counter = rt.size(); 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 { double value = rt.getValueAsDouble(col, row); if (Double.isNaN(value)) { String s = rt.getStringValue(col, row); if (s!=null && !s.equals("NaN")) value = Tools.parseDouble(s); } return value; } } String getResultString(ResultsTable rt) { interp.getLeftParen(); String column = getString(); int row = -1; if (interp.nextToken()==',') { interp.getComma(); row = (int)interp.getExpression(); } if (interp.nextToken()==',') { interp.getComma(); String title = getString(); rt = getResultsTable(title); } interp.getRightParen(); if (rt==null) rt = getResultsTable(true); int counter = rt.size(); 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 rt.getStringValue(col, row); else { String label = null; if ("Label".equals(column)) label = rt.getLabel(row); return label!=null?label:"null"; } } String getResultLabel() { int row = (int)getArg(); ResultsTable rt = getResultsTable(true); int counter = rt.size(); if (row<0 || row>=counter) interp.error("Row ("+row+") out of range"); String label = rt.getLabel(row); if (label!=null) return label; else { label = rt.getStringValue("Label", row); return label!=null?label:""; } } private ResultsTable getResultsTable(boolean reportErrors) { ResultsTable rt = Analyzer.getResultsTable(); int size = rt.size(); if (size==0) { Frame frame = WindowManager.getFrontWindow(); if (frame==null || (frame instanceof Editor)) frame = WindowManager.getFrame("Results"); if (frame!=null && (frame instanceof TextWindow)) { TextPanel tp = ((TextWindow)frame).getTextPanel(); rt = tp.getOrCreateResultsTable(); size = rt!=null?rt.size():0; } } if (size==0) { Window win = WindowManager.getActiveTable(); if (win!=null && (win instanceof TextWindow)) { TextPanel tp = ((TextWindow)win).getTextPanel(); rt = tp.getOrCreateResultsTable(); size = rt!=null?rt.size():0; } } if (size==0 && reportErrors) interp.error("No results found"); return rt; } void setResult(ResultsTable rt) { interp.getLeftParen(); String column = getString(); interp.getComma(); int row = (int)interp.getExpression(); interp.getComma(); double value = 0.0; String stringValue = null; boolean isLabel = column.equals("Label"); if (isStringArg() || isLabel) stringValue = getString(); else value = interp.getExpression(); if (interp.nextToken()==',') { interp.getComma(); String title = getString(); rt = getResultsTable(title); } interp.getRightParen(); if (rt==null) { rt = Analyzer.getResultsTable(); resultsPending = true; } else unUpdatedTable = rt; if (row<0 || row>rt.size()) interp.error("Row ("+row+") out of range"); if (row==rt.size()) rt.incrementCounter(); try { if (stringValue!=null) { if (isLabel) rt.setLabel(stringValue, row); else rt.setValue(column, row, stringValue); } else rt.setValue(column, row, value); } catch (Exception e) { interp.error(""+e.getMessage()); } } void updateResults() { interp.getParens(); ResultsTable rt = Analyzer.getResultsTable(); rt.show("Results"); resultsPending = false; } 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() { interp.getLeftParen(); String prompt = getString(); String yesButton = " Yes "; String noButton = " No "; if (interp.nextToken()==',') { yesButton = getNextString(); noButton = getNextString(); } interp.getRightParen(); 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, yesButton, noButton); if (d.cancelPressed()) { interp.done = true; return 0.0; } else if (d.yesPressed()) return 1.0; else return 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(); ImagePlus imp = getImage(); Roi roi = imp.getRoi(); if (roi==null) interp.error("Selection required"); Variable[] xa, ya; if (roi.getType()==Roi.LINE) { xa = new Variable[2]; ya = new Variable[2]; Line line = (Line)roi; xa[0] = new Variable(line.x1d); ya[0] = new Variable(line.y1d); xa[1] = new Variable(line.x2d); ya[1] = new Variable(line.y2d); } else { FloatPolygon fp = roi.getFloatPolygon(); if (fp!=null) { xa = new Variable[fp.npoints]; ya = new Variable[fp.npoints]; for (int i=0; i0 && s2!=null && (s2.equals(",")||s2.equals(";"))) strings = s1.split(s2,-1); else if (s1.length()>0 && s2!=null && s2.length()>=3 && s2.startsWith("(")&&s2.endsWith(")")) { s2 = s2.substring(1,s2.length()-1); strings = s1.split(s2,-1); } else strings = (s2==null||s2.equals(""))?Tools.split(s1):Tools.split(s1, s2); Variable[] array = new Variable[strings.length]; for (int i=0; i