/** * Copyright (C) 2008-2011 Daniel Senff * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ package de.danielsenff.imageflow.imagej; import java.util.ArrayList; import java.util.Collection; import visualap.Node; import de.danielsenff.imageflow.models.MacroElement; import de.danielsenff.imageflow.models.connection.Input; import de.danielsenff.imageflow.models.connection.Output; import de.danielsenff.imageflow.models.datatype.DataTypeFactory; import de.danielsenff.imageflow.models.datatype.ImageDataType; import de.danielsenff.imageflow.models.unit.ForGroupUnitElement; import de.danielsenff.imageflow.models.unit.GroupUnitElement; import de.danielsenff.imageflow.models.unit.UnitElement; import de.danielsenff.imageflow.models.unit.UnitList; import de.danielsenff.imageflow.models.unit.UnitElement.Type; /** * Based on a cleaned {@link UnitList} the MacroGenerator creates * the Macro-Code for ImageJ. * @author Daniel Senff * */ public class MacroGenerator { // leave nodes out, which: // have no input connected // have no input marked // which are source, but no output and no display private Collection<Node> unitList; private ArrayList<ImageJResult> openedImages; private String macroText; private int unitAmount; private int currentUnit; private boolean extendedMacro = true; /** * MacroGenerator based on a List of unitElements. * @param unitElements */ public MacroGenerator(final Collection<Node> unitElements) { this.unitList = unitElements; this.unitAmount = unitList.size(); this.openedImages = new ArrayList<ImageJResult>(); this.macroText = ""; } /** * @return the openedImages */ public final ArrayList<ImageJResult> getOpenedImages() { return openedImages; } /** * Generates a macro. * @param extendedMacro determines if callback functions are put into macro code * @return */ public String generateMacro(boolean extendedMacro, boolean silent) { // reset in case somebody has the mad idea to run this twice this.macroText = ""; this.macroText += "setBatchMode(true); \n"; this.extendedMacro = extendedMacro; // reset progressBar to 0% if (extendedMacro) { // TODO such stuff could be moved to a MacroString Model this.macroText += "call(\"de.danielsenff.imageflow.tasks.RunMacroTask.setProgress\", \"0\")"; } // loop over all units // they have to be presorted so they are in the right order for (Node node : this.unitList) { generateUnitMacroCode(node, 0); } if (silent) { this.macroText += "\n"; this.macroText += "// delete all images \n"; this.macroText += deleteAllImages(); } else { // delete all images that are not to be displayed this.macroText += "\n"; this.macroText += "// delete unwanted images \n"; this.macroText += deleteImages(); // rename remaining images to a more verbose name this.macroText += "// human understandable names \n"; this.macroText += renameImages(); } this.macroText += "\nsetBatchMode(\"exit and display\"); "; return macroText; } /** * Generates the macro code for one node. * @param node * @param i */ private void generateUnitMacroCode(Node node, int i) { macroText += " \n"; macroText += "// " + node.getLabel() + "\n"; macroText += " \n"; try { if (node instanceof GroupUnitElement) { final ForGroupUnitElement unit = (ForGroupUnitElement) node; //addForUnitElement(unit, i, extendedMacro); } else if(node instanceof UnitElement) { final UnitElement unit = (UnitElement) node; addProcessingUnit(unit, i); } } catch (Exception e) { macroText += "// An Error has occured for node "+node.getLabel()+", this unit will not be processed \n"; e.printStackTrace(); } // update progressBar if (extendedMacro) { currentUnit++; Double currentProgress = (1.0*currentUnit) / unitAmount; macroText += "call(\"de.danielsenff.imageflow.tasks.RunMacroTask.setProgress\", \""+ currentProgress +"\");\n"; } } /** * we assume embedded units are already in the correct order * @param unit */ private void addForUnitElement(ForGroupUnitElement unit, int i, boolean extendedMacro) { int begin = 0; int step = 1; int end = 10; /* * for-header */ macroText += "// For-Loop \n"; // duplicate input images if necessary macroText += duplicateImages(unit); macroText += "for (i="+begin+"; i<"+end+"; i+="+step+") { Unit_7_Output_2 = i;" + "rename(\"Unit_7_Output_1_\"+"+i+");" + "ID_Unit_7_Output_1 = getImageID();" + "selectImage(ID_Unit_7_Output_1); "; // renameOutputImages(unit, i); /* * content middle section */ // iterate over all units i this for-group for (Node node : unit.getNodes()) { System.out.println("process nodes in loop"); // process unit as usual generateUnitMacroCode(node, i); for (int j = begin; j < end; i+= step) { /* * now we iterate over all outputs of this unit. Each output creates * a unique image/data, which can be read multiple times by later units. */ renameOutputImages(unit, i); } } /* * closing footer */ // close all images create in the loop /*selectImage(ID_Unit_9_Output_1); //run("Duplicate...", "title=Title_Temp_ID_Unit_7_Output_1"); //rename("Unit_7_Output_1"); */ // close for loop macroText += "} \n"; macroText += "// close all loop images here" +"for (i=0; i<"+end+"; i+=1) { " +"selectImage(\"Unit_7_Output_1_\"+"+i+");" +"close();" +"}"; //prepare output renameOutputImages(unit, end+1); } private void addFunctionUnit(UnitElement unit, int i) throws Exception { final MacroElement macroElement = ((MacroElement)unit.getObject()); macroElement.reset(); // duplicate input images if necessary macroText += duplicateImages(unit); // parse the command string for wildcards, that need to be replaced // int i is needed for correct generation of identifiers according to loops macroElement.parseParameters(unit.getParameters()); macroElement.parseStack(unit.getInputs()); if(!unit.hasRequiredInputsConnected()) throw new Exception("not all required Inputs connected"); macroElement.parseInputs(unit.getInputs(), i); macroElement.parseOutputs(unit.getOutputs(), i); macroElement.parseAttributes(unit.getInputs(), unit.getParameters(), i); // parse the command string for TITLE_x tags that need to be replaced String parameterString, searchString; Input input; for (int in = 0; in < unit.getInputsCount(); in++) { input = unit.getInput(in); searchString = "TITLE_" + (in+1); parameterString = input.isNeedToCopyInput() ? getNeedCopyTitle(input.getImageID()+"_"+i) : input.getImageTitle()+"_"+i; macroElement.replace(searchString, parameterString); } // if a module needs the image id, just use ID_TITLE_x macroText += macroElement.getCommandSyntax(); } /** * for Processing Units * @param unit * @throws Exception */ private void addProcessingUnit(UnitElement unit, int i) throws Exception { addFunctionUnit(unit, i); printNumbers(unit, i); // FIXME duplicates always // maybe use rename(name) // only works for one output /* * now we iterate over all outputs of this unit. Each output creates * a unique image/data, which can be read multiple times by later units. */ renameOutputImages(unit, i); /* * Most units require the needCopy-flag true. So when they read their input * and begin processing, they duplicate their input in order not to * change the original output data. * However if we work with a sink, ie unit without outputs. The image taken * by the unit needs to be closed again. */ String inputID; if(unit.getUnitType() == Type.SINK) { for (final Input input : unit.getInputs()) { if(input.isNeedToCopyInput()) { inputID = input.getImageID()+"_"+i; macroText += "selectImage(" + getNeedCopyTitle(inputID) + "); \n"; macroText += "close(); \n"; } } } } private void renameOutputImages(UnitElement unit, int i) { String outputTitle, outputID; for (Output output : unit.getOutputs()) { outputTitle = output.getOutputTitle()+"_"+0; // FIXME append i? outputID = output.getOutputID()+"_"+i; if((output.getDataType() instanceof ImageDataType)) { macroText += "rename(\"" + outputTitle + "\"); \n" + outputID + " = getImageID(); \n" + "selectImage("+outputID+"); \n"; openedImages.add(new ImageJResult(output, i)); if (output.isDoDisplayAny()) { UnitElement originalUnit = output.getParent().getOriginalUnit(); macroText += "call(\"de.danielsenff.imageflow.tasks.RunMacroTask.setOutputImage\", "+originalUnit.getNodeID()+", "+output.getIndex()+");\n"; } } } } /** * Rename the remaining displayed images, so they don't have the cryptic * identifier-string, but a human-readable title. * @return */ private String renameImages() { String macroText = ""; for (ImageJResult result : this.openedImages) { macroText += "selectImage("+ result.id +"); \n" + "rename(\"" + result.parentOutput.getDisplayName() + "\"); \n"; } return macroText; } private String deleteAllImages() { String macroText = ""; for (final ImageJResult result : this.openedImages) { macroText += "selectImage("+ result.id +"); \n" + "close(); \n"; } this.openedImages.clear(); return macroText; } private String deleteImages() { String macroText = ""; ArrayList<ImageJResult> removeAfterwards = new ArrayList<ImageJResult>(); for (ImageJResult result : this.openedImages) { if (!result.display) { macroText += "selectImage("+ result.id +"); \n" + "close(); \n"; removeAfterwards.add(result); } } for (ImageJResult image : removeAfterwards) { this.openedImages.remove(image); } return macroText; } private static String duplicateImages(final UnitElement unit) { String code = ""; String inputID; int binaryComparison; for (final Input input : unit.getInputs()) { if (input.getDataType() instanceof ImageDataType) { inputID = input.getImageID()+"_"+0; code += "selectImage(" + inputID + "); \n"; if(input.isNeedToCopyInput()) { // Stacks need an additional Parameter 'duplicate' in the Duplicate-command binaryComparison = ((ImageDataType)input.getFromOutput().getDataType()).getImageBitDepth() & (ij.plugin.filter.PlugInFilter.DOES_STACKS); if (binaryComparison != 0) code += "run(\"Duplicate...\", \"title="+ getNeedCopyTitle(inputID) +" duplicate\"); \n"; else code += "run(\"Duplicate...\", \"title="+ getNeedCopyTitle(inputID) +"\"); \n"; } } } return code; } private static String getNeedCopyTitle(String inputID) { return "Title_Temp_"+ inputID; } /** * If Units have an {@link Output} of Type Double/Integer/Number and are set to Display * their result values are printed to the Log. There is no need for an extra Print-Unit. * @param unit * @param i */ private void printNumbers(UnitElement unit, int i) { for (Output output : unit.getOutputs()) { if (output.getDataType() instanceof DataTypeFactory.Double || output.getDataType() instanceof DataTypeFactory.Integer || output.getDataType() instanceof DataTypeFactory.Number) { String outputTitle = output.getOutputTitle()+ "_" + i; UnitElement originalUnit = output.getParent().getOriginalUnit(); if (output.getParent().isDisplay()) { macroText += "print(" + outputTitle + "); \n"; } System.out.println(extendedMacro); if (output.getParent().isDisplayAny() && extendedMacro) { macroText += "call(\"de.danielsenff.imageflow.tasks.RunMacroTask.setOutputData\", "+originalUnit.getNodeID()+", "+output.getIndex()+", "+outputTitle+");\n"; } } } } /** * * @author dahie * */ public class ImageJResult { public String id; public String title; public Output parentOutput; public Node node; public boolean display; public boolean displaySilent; public ImageJResult(Output output, int i) { this.id = output.getOutputID()+"_"+i; this.title = output.getOutputTitle()+"_"+i; this.parentOutput = output; this.display = output.isDoDisplay(); this.displaySilent = output.isDoDisplaySilent(); this.node = output.getParent().getOriginalUnit(); } } }