diff --git a/src/mesquite/pdap/PDAPDiagnosticChart/PDAPDiagnosticChart.java b/src/mesquite/pdap/PDAPDiagnosticChart/PDAPDiagnosticChart.java index 66187eb..e72f92e 100755 --- a/src/mesquite/pdap/PDAPDiagnosticChart/PDAPDiagnosticChart.java +++ b/src/mesquite/pdap/PDAPDiagnosticChart/PDAPDiagnosticChart.java @@ -1 +1 @@ -/* PDAP:PDTREE package for Mesquite copyright 2001-2009 P. Midford & W. Maddison PDAP:PDTREE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY. The web site for PDAP:PDTREE is http://mesquiteproject.org/pdap_mesquite/ This source code and its compiled class files are free and modifiable under the terms of GNU Lesser General Public License. (http://www.gnu.org/copyleft/lesser.html) */ package mesquite.pdap.PDAPDiagnosticChart; /*~~ */ import java.awt.*; import mesquite.lib.*; import mesquite.lib.characters.*; import mesquite.lib.duties.*; import mesquite.pdap.lib.*; import mesquite.cont.lib.*; /* ======================================================================== */ /**=== Class PDAPDiagnosticChart. === * * This is the main container for PDAP displays (not the transforms, exporter, etc.) * * @author Peter E. Midford * created 2001 * * */ public class PDAPDiagnosticChart extends PDAPTreeWinAsstC implements ChartListener { public void getEmployeeNeeds(){ //This gets called on startup to harvest information; override this and inside, call registerEmployeeNeed EmployeeNeed e = registerEmployeeNeed(DrawChart.class, getName() + " needs a method to draw a chart.", "The method to draw a chart is selected initially"); EmployeeNeed e2 = registerEmployeeNeed(CharSourceCoordObed.class, getName() + " needs a method to supply characters.", "The method to supply characters is selected initially"); EmployeeNeed e3 = registerEmployeeNeed(ContrastForChar.class, getName() + " needs a method to calculate contrasts.", "The method to calculate contrasts is selected initially"); EmployeeNeed e4 = registerEmployeeNeed(ResidualsCalculator.class, getName() + " needs a method to calculate residuals.", "The method to calculate residuals is selected initially"); } final static int SCREEN_COUNT = 14; ContrastForChar contrastsStatsTask; CharSourceCoordObed characterSourceTask; ResidualsCalculator residualsTask; public DrawChart charterTask; private Tree tree; int currentX = 0; int currentY = 1; NodeLabeller nsLabeller; boolean separateAxes = true; TreeDisplay treeDisplay; MesquiteCommand setXCommand, setYCommand; Taxa currentTaxa; private PDAPChartWindow window; int screen = 1; // This is the first screen shown when the diagnostic chart is shown MesquiteCommand cstC; MesquiteBoolean[] screenBoolean; PDAPScatterAsst diagnosticsTask; MesquiteBoolean showLegend; MesquiteBoolean verboseQueryMode; MesquiteBoolean showTreeLabels; MesquiteBoolean showSpotNames; MesquiteBoolean vhDefault; MesquiteDouble vh; private int dfReduction; private boolean vhQueryDone = false; private boolean dfQueryDone = false; private boolean ignoreRootTritomies = false; private boolean widthQueryDone; private double width1 = 0.95; private double width2 = 0.9; private boolean quiet; /*.................................................................................................................*/ /** * @param arguments * @param condition * @param hiredByName * @return true unless an employee couldn't be hired */ public boolean startJob(String arguments, Object condition, boolean hiredByName) { charterTask = (DrawChart)hireNamedEmployee(DrawChart.class, "#Scattergram"); if (charterTask == null) return sorry(getName() + " couldn't start because no charting module obtained."); characterSourceTask = (CharSourceCoordObed)hireCompatibleEmployee(CharSourceCoordObed.class, ContinuousState.class, "Source of characters (for PDAP chart)"); if (characterSourceTask == null) { return sorry(getName() + " couldn't start because no source of characters obtained"); } contrastsStatsTask = (ContrastForChar)hireNamedEmployee(ContrastForChar.class, "#FelsContrWithChar"); if (contrastsStatsTask == null) return sorry(getName() + " couldn't start because no calculating module obtained."); residualsTask = (ResidualsCalculator)hireNamedEmployee(ResidualsCalculator.class, "#ContrastResiduals"); if (residualsTask == null) return sorry(getName() + " couldn't start because no calculating module for residuals obtained."); quiet = MesquiteThread.isScripting(); cstC = makeCommand("setCharacterSource", (Commandable)this); characterSourceTask.setHiringCommand(cstC); screenBoolean = new MesquiteBoolean[SCREEN_COUNT]; for (int i=0; i0) verboseQueryMode.setValue("v".equals(prefs[0])); } /*-----------------------------------------*/ public String[] preparePreferencesForFile () { String[] prefs= new String[1]; if (verboseQueryMode.getValue()) prefs[0] = "v"; else prefs[0] = "n"; return prefs; } /*.................................................................................................................*/ /** * This saves the state of the chart to a Snapshot * @param file * @return the new snapshot object */ public Snapshot getSnapshot(MesquiteFile file) { if (window ==null) return null; Snapshot result = new Snapshot(); result.addLine("quiet"); result.addLine( "getCharacterSource " , characterSourceTask); result.addLine("toggleShowLegend " + showLegend.toOffOnString()); result.addLine("toggleTreeLabels " + showTreeLabels.toOffOnString()); result.addLine("toggleDFQueryDone " + MesquiteBoolean.toOffOnString(dfQueryDone)); result.addLine("toggleVhQueryDone " + MesquiteBoolean.toOffOnString(vhQueryDone)); result.addLine("toggleIgnoreRootTritomies " + MesquiteBoolean.toOffOnString(ignoreRootTritomies)); result.addLine("setDFReduction " + dfReduction); if (!vh.isUnassigned()) result.addLine("setVh " + vh.toString()); result.addLine("setWidth1 " + MesquiteDouble.toString(width1)); result.addLine("setWidth2 " + MesquiteDouble.toString(width2)); result.addLine("setScreen " + screen); // don't do a bunch of bogus calculations on screen 9+; set things first result.addLine("setX " + toExternal(currentX)); result.addLine("setY " + toExternal(currentY)); Snapshot fromWindow = window.getSnapshot(file); result.addLine("getWindow"); result.addLine("tell It"); result.incorporate(fromWindow, true); result.addLine("endTell"); result.addLine("toggleSpotNames " + showSpotNames.toOffOnString()); result.addLine("showWindow"); result.addLine("resume"); return result; } /*.................................................................................................................*/ /** fireAssistantIfNeeded is a helper method for hireIfNeeded. The ordering of the tests in the conditional were reversed to take advantage of "short-circuiting" && by putting the cheaper test first */ private void fireAssistantIfNeeded(String name, String toBeHired){ if (!name.equalsIgnoreCase(toBeHired) && charterTask.findEmployeeWithName(name)!=null) charterTask.doCommand("fireEmployee", name,CommandChecker.defaultChecker); } /** hireIfNeeded makes sure the appropriate calculation assistant is hired, after firing any that are inappropriate. */ private Object hireIfNeeded(String name){ fireAssistantIfNeeded("#ScatterRegDiagnostics", name); fireAssistantIfNeeded("#ScatterResXDiagnostics", name); fireAssistantIfNeeded("#ScatterOriDiagnostics", name); fireAssistantIfNeeded("#ScatterRootCIPI", name); fireAssistantIfNeeded("#ScatterRootRegCIPI",name); if (charterTask.findEmployeeWithName(name)==null) { Object obj = charterTask.doCommand("newAssistant", name,CommandChecker.defaultChecker); if (obj !=null && obj instanceof ScatterRegisterableAsst) { ((ScatterRegisterableAsst)obj).setChartModule(this); } return obj; } else { return charterTask.findEmployeeWithName(name); } } /*.................................................................................................................*/ /** This method sets the screen, updates the boolean array that handles check marks in the menu and hires the appropriate diagnostics employee */ private Object setScreen(int ic){ screenBoolean[screen].setValue(false); screen = ic; screenBoolean[screen].setValue(true); Object hired = null; switch (ic){ case 1: case 3: case 5: case 7: case 11: { hired = hireIfNeeded("#ScatterRegDiagnostics"); break; } case 9: { hired = hireIfNeeded("#ScatterOriDiagnostics"); break; } case 10: { hired = hireIfNeeded("#ScatterResXDiagnostics"); break; } case 12: { //screen 9A hired = hireIfNeeded("#ScatterRootCIPI"); break; } case 13: { //screen 9B hired = hireIfNeeded("#ScatterRootRegCIPI"); break; } } diagnosticsTask = (PDAPScatterAsst)hired; return hired; } MesquiteInteger pos = new MesquiteInteger(); /*.................................................................................................................*/ public Object doCommand(String commandName, String arguments, CommandChecker checker) { final Class thisClass = this.getClass(); if (checker.compare(thisClass, "Sets module supplying characters", "[name of module]", commandName, "setCharacterSource")) {//temporary return characterSourceTask.doCommand(commandName, arguments, checker); } else if (checker.compare(thisClass, "Sets whether or not the legend is shown", "[on = show; off]", commandName, "toggleShowLegend")) { showLegend.toggleValue(parser.getFirstToken(arguments)); window.setLegendVisible(showLegend.getValue()); } else if (checker.compare(thisClass, "Sets whether or not the spot names are shown", "[on = show; off]", commandName, "toggleSpotNames")) { showSpotNames.toggleValue(parser.getFirstToken(arguments)); if (window != null){ window.charter.setShowNames(showSpotNames.getValue()); window.contentsChanged(); } } else if (checker.compare(thisClass, "Quiets notices", null, commandName, "quiet")) { quiet = true; return null; } else if (checker.compare(thisClass, "Returns module supplying characters", null, commandName, "resume")) { quiet = false; return null; } else if (checker.compare(thisClass, "Returns module supplying characters", null, commandName, "getCharacterSource")) { return characterSourceTask; } else if (checker.compare(thisClass, "Sets the screen shown", "[screen number, one of 1,3,5,7,9-13]", commandName, "setScreen")) { int ic = MesquiteInteger.fromString(arguments); if (validScreen(ic)) { incrementMenuResetSuppression(); Object o = setScreen(ic); doCounts(); getModuleWindow().contentsChanged(); decrementMenuResetSuppression(); return o; } } else if (checker.compare(thisClass, "Sets which item is shown on the x axis (appropriate where the calculator can supply a series of items, e.g. characters)", "[item number]", commandName, "setX")) { int ic = (int)(MesquiteInteger.fromString(arguments)); if (!MesquiteInteger.isCombinable(ic)) ic = MesquiteInteger.queryInteger(containerOfModule(), "Choose ", "choose:", 1); //TODO have something more intelligent here if (!MesquiteInteger.isCombinable(ic)) return null; int icInternal = ic-1; if ((icInternal>=0) && (icInternal<=getMax())) { //no need to check for combinability, ic was just checked. currentX=icInternal; if (!MesquiteThread.isScripting()){ doCounts(); } } } else if (checker.compare(thisClass, "Sets which item is shown on the y axis (appropriate where the calculator can supply a series of items, e.g. characters)", "[item number]", commandName, "setY")) { int ic = (MesquiteInteger.fromString(arguments)); if (!MesquiteInteger.isCombinable(ic)) ic = MesquiteInteger.queryInteger(containerOfModule(), "Choose an ", "choose:", 1); //TODO have something more intelligent here if (!MesquiteInteger.isCombinable(ic)) return null; int icInternal = ic-1; if ((icInternal>=0) && (icInternal<=getMax())) { //no need to check for combinability, ic was just checked. currentY=icInternal; if (!MesquiteThread.isScripting()){ doCounts(); } } } else if (checker.compare(thisClass, "Sets whether or not the tree labels are shown", "[on or off]", commandName, "toggleTreeLabels")) { //TODO: use MesquiteBoolean checkmark if (StringUtil.blank(arguments)) //not a menu command.. MesquiteMessage.warnProgrammer("toggleShowTreeLabels invoked without parameter"); else showTreeLabels.toggleValue(parser.getFirstToken(arguments)); if (nsLabeller!=null) nsLabeller.setShowLabels(showTreeLabels.getValue()); if (treeDisplay !=null) treeDisplay.pleaseUpdate(false); } else if (checker.compare(thisClass,"Sets the degrees of freedom adjustment", "[integer]",commandName, "setDFReduction")) { int dfr = (int)(MesquiteInteger.fromString(arguments)); if (!MesquiteInteger.isCombinable(dfr)) //not a menu command.. MesquiteMessage.warnProgrammer("setDFReduction invoked without parameter"); else dfReduction = dfr; } else if (checker.compare(thisClass,"Sets the length of the added branch for predication intervals", "[double]",commandName, "setVh")) { double vht = (double)(MesquiteDouble.fromString(arguments)); if (!MesquiteDouble.isCombinable(vht)) //not a menu command.. MesquiteMessage.warnProgrammer("setVh invoked without parameter"); else vh.setValue(vht); } else if (checker.compare(thisClass,"Sets the length of the added branch for predication intervals", "[double]",commandName, "setWidth1")) { double w1 = (double)(MesquiteDouble.fromString(arguments)); if (!MesquiteDouble.isCombinable(w1)) //not a menu command.. MesquiteMessage.warnProgrammer("setWidth1 invoked without parameter"); else width1 = w1; } else if (checker.compare(thisClass,"Sets the length of the added branch for predication intervals", "[double]",commandName, "setWidth2")) { double w2 = (double)(MesquiteDouble.fromString(arguments)); if (!MesquiteDouble.isCombinable(w2)) //not a menu command.. MesquiteMessage.warnProgrammer("setWidth2 invoked without parameter"); else width2 = w2; } else if (checker.compare(thisClass,"Sets state of Vh query done", "[on/off]",commandName, "toggleVhQueryDone")) { if (StringUtil.blank(arguments)) //not a menu command.. MesquiteMessage.warnProgrammer("toggleVhQueryDone invoked without parameter"); else{ String s = parser.getFirstToken(arguments); if ("on".equalsIgnoreCase(s)) vhQueryDone = true; else if ("off".equalsIgnoreCase(s)) vhQueryDone = false; else vhQueryDone = !vhQueryDone; } } else if (checker.compare(thisClass,"Sets state of df query done", "[on/off]",commandName, "toggleDFQueryDone")) { if (StringUtil.blank(arguments)) //not a menu command.. MesquiteMessage.warnProgrammer("toggleDFQueryDone invoked without parameter"); else{ String s = parser.getFirstToken(arguments); if ("on".equalsIgnoreCase(s)) dfQueryDone = true; else if ("off".equalsIgnoreCase(s)) dfQueryDone = false; else dfQueryDone = !dfQueryDone; } } else if (checker.compare(thisClass,"Sets state of ignore root tritomies", "[on/off]",commandName, "toggleIgnoreRootTritomies")) { if (StringUtil.blank(arguments)) //not a menu command.. MesquiteMessage.warnProgrammer("toggleIgnoreRootTritomies invoked without parameter"); else{ String s = parser.getFirstToken(arguments); if ("on".equalsIgnoreCase(s)) ignoreRootTritomies = true; else if ("off".equalsIgnoreCase(s)) ignoreRootTritomies = false; else ignoreRootTritomies = !ignoreRootTritomies; } } else return super.doCommand(commandName, arguments, checker); return null; } /* This checks whether the screenNumber specifies a valid screen; if new screens are added, this needs to be updated */ final private boolean validScreens[] = {false, true, false, true, false, true, false, true, false, true, true, true, true, true}; private boolean validScreen(int screenNumber) { if (screenNumber < 0 || screenNumber > 13) return false; else return validScreens[screenNumber]; } long oldTreeID = -1; long oldTreeVersion = 0; /*.................................................................................................................*/ public void setTree(Tree tree) { if (tree==null) return; if (!((MesquiteTree)tree).allLengthsAssigned(false) && !(MesquiteThread.isScripting() && quiet)) { alert("The tree you want to analyze contains one or more branches with unassigned branch lengths. " + "You should go back to the tree window and specify branch lengths (e.g set to 1, Pagel's method, etc.)."); //iQuit(); //window.dispose(); //return; } if (this.tree !=null && this.tree instanceof NoPolyTree && this.tree !=tree) { // about to set new tree ((NoPolyTree)this.tree).disconnect(); } if (tree instanceof NoPolyTree) this.tree=tree; else this.tree = new NoPolyTree((MesquiteTree)tree); if (oldTreeID==-1 || tree.getID() != oldTreeID || tree.getVersionNumber() != oldTreeVersion){ setDFQueryDone(false); // make sure these are reset before any redraws setVhQueryDone(false); vh.setToUnassigned(); } if (this.tree instanceof Selectionable) charterTask.pointsAreSelectable(true, (Selectionable)this.tree, false); currentTaxa = this.tree.getTaxa(); if (oldTreeID==-1) { if (contrastsStatsTask==null || residualsTask == null) return; //setDFQueryDone(false); //setVhQueryDone(false); //vh.setToUnassigned(); contrastsStatsTask.initialize(tree); residualsTask.initialize(tree); characterSourceTask.initialize(tree.getTaxa()); doCounts(); } else if (tree.getID() != oldTreeID || tree.getVersionNumber() != oldTreeVersion) { //setDFQueryDone(false); //setVhQueryDone(false); //vh.setToUnassigned(); doCounts(); } else { if (window!=null && window.getChart()!=null ) { window.getChart().getField().repaint(); //for selection Only window.getChart().repaint(); } } oldTreeID = tree.getID(); oldTreeVersion = tree.getVersionNumber(); } /*.................................................................................................................*/ public void employeeParametersChanged(MesquiteModule employee, MesquiteModule source, Notification notification) { if (contrastsStatsTask!=null && residualsTask!=null && characterSourceTask !=null) { doCounts(); } } /*.................................................................................................................*/ public CharacterDistribution getObserved1(){ return window.observedStates; } /*.................................................................................................................*/ public CharacterDistribution getObserved2(){ return window.observedStates2; } /*.................................................................................................................*/ public Tree getTree(){ return tree; } /*.................................................................................................................*/ public boolean getDFQueryDone(){ return dfQueryDone; } /*.................................................................................................................*/ public void setDFQueryDone(boolean value){ dfQueryDone = value; } /*.................................................................................................................*/ public int getDFReduction(){ return dfReduction; } public void setDFReduction(int value){ dfReduction = value; } public int queryDFReduce(PDAP2CTStatPak sp, boolean notifyIfZero){ if(!MesquiteThread.isScripting()){ //if scripting use 0 (default) or value set by script sp.countInternalNodes(tree, tree.getRoot()); int polytomies = ((NoPolyTree)tree).countZLBs(sp.getIgnoreRootTritomies()); // Zero-Length-Branches are what PDAP calls "polytomies", count to inform the user if ((polytomies >0)) { int tmp = MesquiteInteger.queryInteger(window, "DF Reduce ", "This tree contains polytomies, indicated by the presence \n" + "of " + polytomies + " zero-length branch(es).\n" + "If some of these collapsed branches indicate\n" + "soft polytomies, then you may want to reduce\n" + "degrees of freedom (see Purvis and Garland, 1993,\n" + "Syst. Biol. 42:569-575; Garland and Diaz-Uriarte, 1999,\n" + "Syst. Biol. 48:547-558).\n\n " + "Enter number of Degrees of Freedom to subtract for soft polytomies", dfReduction, 0, MesquiteInteger.infinite); if (MesquiteInteger.isCombinable(tmp)) { dfReduction = tmp; } } else { if (notifyIfZero) { int tmp = MesquiteInteger.queryInteger(window, "DF Reduce ", "This tree is fully dichotomized and contains no polytomies.\n" + "If, for whatever reason, you wish to decrease\n" + "degrees of freedom (see Purvis and Garland, 1993,\n" + "Syst. Biol. 42:569-575; Garland and Diaz-Uriarte, 1999,\n" + "Syst. Biol. 48:547-558), you may do so here.\n\n" + "Enter degrees of freedom to subtract", dfReduction, 0, MesquiteInteger.infinite); //alert(null,"","The tree has no polytomies, hence there is no need to reduce degrees of freedom to compensate for polytomies."); if (MesquiteInteger.isCombinable(tmp)) dfReduction = tmp; else dfReduction = 0; } } } return dfReduction; } /*-----------------------------------------*/ /** * This prompts the user and sets the CI/PI widths. The results are saved in the width1 and width2 fields. */ public void queryWidths(){ if (!MesquiteThread.isScripting()){ // if scripting, widths either default or set elsewhere MesquiteDouble tmp1 = new MesquiteDouble(width1); MesquiteDouble tmp2 = new MesquiteDouble(width2); MesquiteBoolean ans = new MesquiteBoolean(); MesquiteDouble.queryTwoDoubles(window, "Interval Widths", "Enter the first (2-tailed) p-value width for CI/PI plots", "Enter the second (2-tailed) p-value width for CI/PI plots", ans, tmp1, tmp2); if (tmp1.isCombinable()) { double tp1 = tmp1.getValue(); if ((tp1 <= 1.0) && (tp1 >= 0.0)) width1 = tp1; } if (tmp2.isCombinable()){ double tp2 = tmp2.getValue(); if ((tp2 <= 1.0) && (tp2 >= 0.0)) width2 = tp2; } } } public double getWidth1(){ return width1; } public double getWidth2(){ return width2; } public void setWidthQueryDone(boolean value){ widthQueryDone = value; } public boolean getWidthQueryDone(){ return widthQueryDone; } /*.................................................................................................................*/ public boolean getVhQueryDone(){ return vhQueryDone; } /*.................................................................................................................*/ public void setVhQueryDone(boolean value){ vhQueryDone = value; } public double getVh(){ return vh.getValue(); } /*.................................................................................................................*/ /** This prompts the user and sets Vh */ public double queryVh(PDAP2CTStatPak sp){ double tmp = sp.getMeanHeight(tree); if (!MesquiteThread.isScripting()){ String contempString; if (sp.getContempTips()) contempString = "All tips are contemporaneous in the uncorrected tree."; else contempString = "The tips are not contemporaneous in the uncorrected tree."; tmp = MesquiteDouble.queryDouble(window, "Vh", "The calculations for prediction intervals effectively assume " + "that the species to be predicted is attached at the root " + "(basal node) of the phylogenetic tree (see Garland and Ives, " + "2000, Am. Nat. 155:346-364). By default, the branch length (Vh) " + "leading to the predicted species is assumed to equal the average" + "length of the (uncorrected) branches from root to tips. \n\n" + contempString + "\n\n Enter a value for Vh\n", tmp, 0, MesquiteDouble.infinite); if (MesquiteDouble.isCombinable(tmp)){ vh.setValue(tmp); vhQueryDone = true; } else{ AlertDialog.notice(window, "Vh undefined", "Vh could not be estimated and is not meaningful because one or more " + "branches in the tree have undefined branch lengths. Either assign lengths " + "or use a constant transform to set all branches equal to one. No confidence or " + "prediction intervals will be displayed."); vh.setToUnassigned(); } } else vh.setValue(tmp); return vh.getValue(); } public void queryVerboseQueryMode(){ MesquiteBoolean ok = MesquiteBoolean.queryCheckBox(window, "Set Verbose Query Preference","In verbose mode you will be asked for settings for PDAP root reconstruction frequently; otherwise you change the settings using menu items","Use Verbose Mode", verboseQueryMode.getValue()); if (!ok.isUnassigned()) { verboseQueryMode.setValue(ok.getValue()); storePreferences(); } } public void toggleVerboseQueryMode(String arg){ verboseQueryMode.toggleValue(arg); storePreferences(); } public boolean getVerboseQueryMode(){ return verboseQueryMode.getValue(); } /*................................................................*/ public void queryIgnoreRootTritomies(){ MesquiteBoolean ok = MesquiteBoolean.queryCheckBox(window, "Set Ignore Basal Tritomies", "Some tree inference programs return trees that PDAP sees as having tritomies at the root; checking the box will ignore any tritomies at the root in counting or detecting polytomies for degrees of freedom adjustment", "Ignore Root Tritomies", ignoreRootTritomies); if (!ok.isUnassigned()){ ignoreRootTritomies = ok.getValue(); } } public boolean getIgnoreRootTritomies(){ return ignoreRootTritomies; } public void setIgnoreRootTritomies(boolean value){ ignoreRootTritomies = value; } /*................................................................*/ public String nameForWritableResults(){ return "PDAP Diagnostics from StatPak"; } public boolean suppliesWritableResults(){ return diagnosticsTask.suppliesWritableResults(); } public Object getWritableResults(){ return diagnosticsTask.getWritableResults(); } public Object getResultsHeading(){ return diagnosticsTask.getResultsHeading(); } /*.................................................................................................................*/ private void doCounts() { window.blankChart(); window.setTree(tree); window.recalcChart(); window.contentsChanged(); } /*.................................................................................................................*/ public String getName() { return "PDAP Diagnostic Chart"; } /*.................................................................................................................*/ public String getAuthors() { return "Peter E. Midford, Ted Garland Jr., and Wayne P. Maddison" ; } /*.................................................................................................................*/ public String getVersion() { return "1.15"; } /*.................................................................................................................*/ //Peter: add this method returning true for all citable modules, e.g. main calculations public boolean showCitation(){ return true; } /*.................................................................................................................*/ public boolean isPrerelease() { return false; } /*.................................................................................................................*/ public boolean requestPrimaryChoice() { return true; } /*.................................................................................................................*/ /** returns an explanation of what the module does.*/ public String getExplanation() { return "Shows independent constrasts and related values for nodes of tree via a scattergram." ; } /*.................................................................................................................*/ public void endJob() { if (treeDisplay !=null && nsLabeller !=null) treeDisplay.removeExtra(nsLabeller); super.endJob(); } } /* ======================================================================== */ /**=== Local Class BannerPanel. ===*/ class BannerPanel extends MesquitePanel{ public void paint(Graphics g){ g.setColor(Color.cyan); g.drawString("PDAP: PDTREE module", 16, getBounds().height-4); } } /*========================================================*/ class NodeLabeller extends TreeDisplayExtra { LabelsAtNodes labelsAtNodes; PDAPChartWindow chartWindow; final static Color brightGreen = new Color((float)0.4, (float)1.0, (float)0.4); final static Color brightbrightGreen = new Color((float)0.6, (float)1.0, (float)0.6); boolean showLabels = true; int labelDrawn = -1; public NodeLabeller (MesquiteModule ownerModule, TreeDisplay treeDisplay, PDAPChartWindow chartWindow) { super(ownerModule, treeDisplay); this.chartWindow =chartWindow; } /*.................................................................................................................*/ void drawOneLabel(int N, Tree tree, Graphics g) { makeSureLabelsReady(tree); int nodeX = treeDisplay.getTreeDrawing().x[N]; int nodeY = treeDisplay.getTreeDrawing().y[N]; MesquiteLabel c = (MesquiteLabel)labelsAtNodes.getPanel(N); c.setColor(brightbrightGreen); c.setText("Node " + N); String[] results = chartWindow.getStrings(N); for (int i = 0; itreeDisplay.getBounds().width) c.setLocation(treeDisplay.getBounds().width- w, nodeY + 18); else c.setLocation(nodeX + 16, nodeY + 18); g.setColor(brightGreen); g.setXORMode(Color.white); g.drawLine(nodeX, nodeY, c.getBounds().x, c.getBounds().y); g.drawLine(nodeX, nodeY+1, c.getBounds().x, c.getBounds().y + 1); g.drawLine(nodeX, nodeY+2, c.getBounds().x, c.getBounds().y + 2); g.drawLine(nodeX, nodeY+3, c.getBounds().x, c.getBounds().y + 3); g.setPaintMode(); g.setColor(Color.black); labelDrawn = N; } /*.................................................................................................................*/ void hideOneLabel(int N, Graphics g) { MesquiteLabel c = (MesquiteLabel)labelsAtNodes.getPanel(N); if (c==null || g==null) return; c.setVisible(false); final int nodeX = treeDisplay.getTreeDrawing().x[N]; final int nodeY = treeDisplay.getTreeDrawing().y[N]; g.setColor(brightGreen); g.setXORMode(Color.white); g.drawLine(nodeX, nodeY, c.getBounds().x, c.getBounds().y); g.drawLine(nodeX, nodeY+1, c.getBounds().x, c.getBounds().y + 1); g.drawLine(nodeX, nodeY+2, c.getBounds().x, c.getBounds().y + 2); g.drawLine(nodeX, nodeY+3, c.getBounds().x, c.getBounds().y + 3); g.setPaintMode(); labelDrawn = -1; } /*.................................................................................................................*/ private void drawLabels(int N, Tree tree, Graphics g) { for (int d = tree.firstDaughterOfNode(N); tree.nodeExists(d); d = tree.nextSisterOfNode(d)) drawLabels(d, tree, g); drawOneLabel(N, tree, g); } /*.................................................................................................................*/ void hideLabels() { if (labelsAtNodes==null) return; Graphics g = treeDisplay.getGraphics(); for (int N = 0; N20) chName = chName.substring(0, 17) + "..."; return chName + " (" + template + ")"; } /*.................................................................................................................*/ public void recalcChart() { chart.deassignChart(); if (ownerModule.contrastsStatsTask != null && ownerModule.residualsTask != null) { resetScrolls(); final int numNodes = tree.getNumNodeSpaces(); xArray.resetSize(numNodes); yArray.resetSize(numNodes); //zArray.resetSize(numNodes); //if (ownerModule.contrastsStatsTask instanceof Incrementable) // ((Incrementable)ownerModule.contrastsStatsTask).setCurrent(ownerModule.currentX); // check for polytomies, set warning boolean hasPolytomies; if (tree instanceof NoPolyTree) hasPolytomies = ((NoPolyTree)tree).hasZLBs(); else hasPolytomies = tree.hasPolytomies(tree.getRoot()); //x values int iChar = ownerModule.currentX; // actually this is *only* true for bivariate plots (screens 9&10). switch (ownerModule.screen){ case 1:{ ownerModule.contrastsStatsTask.setOption(ContrastForChar.SD); iChar = ownerModule.currentY; //currentY is the character specified in the single scroll - both axes are functions of this. //Plot of absolute value of contrast vs. standard deviation if (hasPolytomies) legend.setMessage(SCREEN1LEGEND + POLYTOMYWARNING); else legend.setMessage(SCREEN1LEGEND); break; } case 3:{ ownerModule.contrastsStatsTask.setOption(ContrastForChar.NODEVALUE); iChar = ownerModule.currentY; //Plot of absolute value of contrast vs. estimated value of associated node if (hasPolytomies) legend.setMessage(SCREEN3LEGEND + POLYTOMYWARNING); else legend.setMessage(SCREEN3LEGEND); break; } case 5:{ ownerModule.contrastsStatsTask.setOption(ContrastForChar.CORRECTEDHEIGHT); iChar = ownerModule.currentY; //Plot of absolute value of contrast vs. height of associated node if (hasPolytomies) legend.setMessage(SCREEN5LEGEND + POLYTOMYWARNING); else legend.setMessage(SCREEN5LEGEND); break; } case 7:{ ownerModule.contrastsStatsTask.setOption(ContrastForChar.CORRECTEDHEIGHT); iChar = ownerModule.currentY; //Plot of estimated value of associated node vs. its height legend.setMessage(SCREEN7LEGEND); break; } case 9:{ ownerModule.contrastsStatsTask.setOption(ContrastForChar.CONTRAST); iChar = ownerModule.currentX; // character specified in the horizontal scroll //Plot of contrasts vs. positivized contrasts legend.setMessage(SCREEN9LEGEND); break; } case 10:{ ownerModule.contrastsStatsTask.setOption(ContrastForChar.ABSCONTRAST); iChar = ownerModule.currentX; // character specified in the horizontal scroll //Plot of residuals from screen 9 vs. contrast values legend.setMessage(SCREEN10LEGEND); break; } case 11:{ ownerModule.contrastsStatsTask.setOption(ContrastForChar.CORRECTEDHEIGHT); iChar = ownerModule.currentX; //Plot of absolute value of residual vs. height of associated node legend.setMessage(SCREEN11LEGEND); break; } case 12:{ ownerModule.contrastsStatsTask.setOption(ContrastForChar.TIPS); iChar = ownerModule.currentX; //Plot of tip values and confidence intervals for reconstructed root values legend.setMessage(SCREEN12LEGEND);//Peter: what of this depends on diagnostics being active? break; } case 13:{ ownerModule.contrastsStatsTask.setOption(ContrastForChar.TIPS); iChar = ownerModule.currentX; //? //"Plot of phylogenetically correct (OLS) regression and associated CI's" //Peter: what of this depends on diagnostics being active? legend.setMessage(SCREEN13LEGEND); //Peter: what of this depends on diagnostics being active? } } if (true ) { //if one character calculation, don't get character unless needed observedStates = (ContinuousDistribution)ownerModule.characterSourceTask.getCharacter(tree, iChar); } ownerModule.contrastsStatsTask.calculateNumbers(tree, observedStates, xArray, null); // This does the work for the x-axis chart.setXAxisName(axisName(ownerModule.contrastsStatsTask.getParameters(), observedStates, ownerModule.screen, true)); //set to correct name String xItem = ownerModule.contrastsStatsTask.getItemName(); if (ownerModule.contrastsStatsTask instanceof Incrementable) ((Incrementable)ownerModule.contrastsStatsTask).setCurrent(ownerModule.currentY); //y values switch (ownerModule.screen){ case 1: case 3: case 5: { ownerModule.contrastsStatsTask.setOption(ContrastForChar.ABSCONTRAST); break; } case 7:{ ownerModule.contrastsStatsTask.setOption(ContrastForChar.NODEVALUE); break; } case 9:{ ownerModule.contrastsStatsTask.setOption(ContrastForChar.CONTRAST); break; } case 10:{ ownerModule.contrastsStatsTask.setOption(ContrastForChar.RESIDUAL); ownerModule.residualsTask.setOption(0);//raw break; } case 11:{ ownerModule.contrastsStatsTask.setOption(ContrastForChar.ABSRESIDUAL); ownerModule.residualsTask.setOption(1);//absolute break; } case 12: case 13:{ ownerModule.contrastsStatsTask.setOption(ContrastForChar.TIPS); break; } } if (true ) { //if one character calculation, don't get character unless needed TODO: make this work observedStates2 = (ContinuousDistribution)ownerModule.characterSourceTask.getCharacter(tree, ownerModule.currentY); } switch (ownerModule.screen) { case 1: case 3: case 5: case 7: case 9: { //ownerModule.contrastsStatsTask.setXCharForReport(ownerModule.currentX); //ownerModule.contrastsStatsTask.setYCharForReport(ownerModule.currentY); ownerModule.contrastsStatsTask.calculateNumbers(tree, observedStates2, yArray, null); // This does the work for the y-axis break; } case 10: case 11: { ownerModule.residualsTask.calculateNumbers(tree, observedStates, observedStates2, yArray, null); break; } case 12: case 13: { ownerModule.contrastsStatsTask.setXCharForReport(ownerModule.currentX); ownerModule.contrastsStatsTask.setYCharForReport(ownerModule.currentY); ownerModule.contrastsStatsTask.calculateNumbers(tree, observedStates2, yArray, null); break; } } // maybe call it here?? chart.setYAxisName(axisName(ownerModule.contrastsStatsTask.getParameters(), observedStates2, ownerModule.screen, false)); //TODO: this should use string passed back from calculateNumbers ??? boolean showTips = false; if ((ownerModule.screen == 12) || (ownerModule.screen == 13)) showTips = true; else if (ownerModule.screen == 9){ chart.setXAxisName(axisName("Positivized Contrasts - CAUTION if any values are zero", observedStates, ownerModule.screen, true)); //force to correct name chart.setYAxisName(axisName("Contrasts Positivized on the X variable (Garland et al. 1992)", observedStates2, ownerModule.screen, false)); } for (int i=0; i0) verboseQueryMode.setValue("v".equals(prefs[0])); } /*-----------------------------------------*/ public String[] preparePreferencesForFile () { String[] prefs= new String[1]; if (verboseQueryMode.getValue()) prefs[0] = "v"; else prefs[0] = "n"; return prefs; } /*.................................................................................................................*/ /** * This saves the state of the chart to a Snapshot * @param file * @return the new snapshot object */ public Snapshot getSnapshot(MesquiteFile file) { if (window ==null) return null; Snapshot result = new Snapshot(); result.addLine("quiet"); result.addLine( "getCharacterSource " , characterSourceTask); result.addLine("toggleShowLegend " + showLegend.toOffOnString()); result.addLine("toggleTreeLabels " + showTreeLabels.toOffOnString()); result.addLine("toggleDFQueryDone " + MesquiteBoolean.toOffOnString(dfQueryDone)); result.addLine("toggleVhQueryDone " + MesquiteBoolean.toOffOnString(vhQueryDone)); result.addLine("toggleIgnoreRootTritomies " + MesquiteBoolean.toOffOnString(ignoreRootTritomies)); result.addLine("setDFReduction " + dfReduction); if (!vh.isUnassigned()) result.addLine("setVh " + vh.toString()); result.addLine("setWidth1 " + MesquiteDouble.toString(width1)); result.addLine("setWidth2 " + MesquiteDouble.toString(width2)); result.addLine("setScreen " + screen); // don't do a bunch of bogus calculations on screen 9+; set things first result.addLine("setX " + toExternal(currentX)); result.addLine("setY " + toExternal(currentY)); Snapshot fromWindow = window.getSnapshot(file); result.addLine("getWindow"); result.addLine("tell It"); result.incorporate(fromWindow, true); result.addLine("endTell"); result.addLine("toggleSpotNames " + showSpotNames.toOffOnString()); result.addLine("showWindow"); result.addLine("resume"); return result; } /*.................................................................................................................*/ /** fireAssistantIfNeeded is a helper method for hireIfNeeded. The ordering of the tests in the conditional were reversed to take advantage of "short-circuiting" && by putting the cheaper test first */ private void fireAssistantIfNeeded(String name, String toBeHired){ if (!name.equalsIgnoreCase(toBeHired) && charterTask.findEmployeeWithName(name)!=null) charterTask.doCommand("fireEmployee", name,CommandChecker.defaultChecker); } /** hireIfNeeded makes sure the appropriate calculation assistant is hired, after firing any that are inappropriate. */ private Object hireIfNeeded(String name){ fireAssistantIfNeeded("#ScatterRegDiagnostics", name); fireAssistantIfNeeded("#ScatterResXDiagnostics", name); fireAssistantIfNeeded("#ScatterOriDiagnostics", name); fireAssistantIfNeeded("#ScatterRootCIPI", name); fireAssistantIfNeeded("#ScatterRootRegCIPI",name); if (charterTask.findEmployeeWithName(name)==null) { Object obj = charterTask.doCommand("newAssistant", name,CommandChecker.defaultChecker); if (obj !=null && obj instanceof ScatterRegisterableAsst) { ((ScatterRegisterableAsst)obj).setChartModule(this); } return obj; } else { return charterTask.findEmployeeWithName(name); } } /*.................................................................................................................*/ /** This method sets the screen, updates the boolean array that handles check marks in the menu and hires the appropriate diagnostics employee */ private Object setScreen(int ic){ screenBoolean[screen].setValue(false); screen = ic; screenBoolean[screen].setValue(true); Object hired = null; switch (ic){ case 1: case 3: case 5: case 7: case 11: { hired = hireIfNeeded("#ScatterRegDiagnostics"); break; } case 9: { hired = hireIfNeeded("#ScatterOriDiagnostics"); break; } case 10: { hired = hireIfNeeded("#ScatterResXDiagnostics"); break; } case 12: { //screen 9A hired = hireIfNeeded("#ScatterRootCIPI"); break; } case 13: { //screen 9B hired = hireIfNeeded("#ScatterRootRegCIPI"); break; } } diagnosticsTask = (PDAPScatterAsst)hired; return hired; } MesquiteInteger pos = new MesquiteInteger(); /*.................................................................................................................*/ public Object doCommand(String commandName, String arguments, CommandChecker checker) { final Class thisClass = this.getClass(); if (checker.compare(thisClass, "Sets module supplying characters", "[name of module]", commandName, "setCharacterSource")) {//temporary return characterSourceTask.doCommand(commandName, arguments, checker); } else if (checker.compare(thisClass, "Sets whether or not the legend is shown", "[on = show; off]", commandName, "toggleShowLegend")) { showLegend.toggleValue(parser.getFirstToken(arguments)); window.setLegendVisible(showLegend.getValue()); } else if (checker.compare(thisClass, "Sets whether or not the spot names are shown", "[on = show; off]", commandName, "toggleSpotNames")) { showSpotNames.toggleValue(parser.getFirstToken(arguments)); if (window != null){ window.charter.setShowNames(showSpotNames.getValue()); window.contentsChanged(); } } else if (checker.compare(thisClass, "Quiets notices", null, commandName, "quiet")) { quiet = true; return null; } else if (checker.compare(thisClass, "Returns module supplying characters", null, commandName, "resume")) { quiet = false; return null; } else if (checker.compare(thisClass, "Returns module supplying characters", null, commandName, "getCharacterSource")) { return characterSourceTask; } else if (checker.compare(thisClass, "Sets the screen shown", "[screen number, one of 1,3,5,7,9-13]", commandName, "setScreen")) { int ic = MesquiteInteger.fromString(arguments); if (validScreen(ic)) { incrementMenuResetSuppression(); Object o = setScreen(ic); doCounts(); getModuleWindow().contentsChanged(); decrementMenuResetSuppression(); return o; } } else if (checker.compare(thisClass, "Sets which item is shown on the x axis (appropriate where the calculator can supply a series of items, e.g. characters)", "[item number]", commandName, "setX")) { int ic = (int)(MesquiteInteger.fromString(arguments)); if (!MesquiteInteger.isCombinable(ic)) ic = MesquiteInteger.queryInteger(containerOfModule(), "Choose ", "choose:", 1); //TODO have something more intelligent here if (!MesquiteInteger.isCombinable(ic)) return null; int icInternal = ic-1; if ((icInternal>=0) && (icInternal<=getMax())) { //no need to check for combinability, ic was just checked. currentX=icInternal; if (!MesquiteThread.isScripting()){ doCounts(); } } } else if (checker.compare(thisClass, "Sets which item is shown on the y axis (appropriate where the calculator can supply a series of items, e.g. characters)", "[item number]", commandName, "setY")) { int ic = (MesquiteInteger.fromString(arguments)); if (!MesquiteInteger.isCombinable(ic)) ic = MesquiteInteger.queryInteger(containerOfModule(), "Choose an ", "choose:", 1); //TODO have something more intelligent here if (!MesquiteInteger.isCombinable(ic)) return null; int icInternal = ic-1; if ((icInternal>=0) && (icInternal<=getMax())) { //no need to check for combinability, ic was just checked. currentY=icInternal; if (!MesquiteThread.isScripting()){ doCounts(); } } } else if (checker.compare(thisClass, "Sets whether or not the tree labels are shown", "[on or off]", commandName, "toggleTreeLabels")) { //TODO: use MesquiteBoolean checkmark if (StringUtil.blank(arguments)) //not a menu command.. MesquiteMessage.warnProgrammer("toggleShowTreeLabels invoked without parameter"); else showTreeLabels.toggleValue(parser.getFirstToken(arguments)); if (nsLabeller!=null) nsLabeller.setShowLabels(showTreeLabels.getValue()); if (treeDisplay !=null) treeDisplay.pleaseUpdate(false); } else if (checker.compare(thisClass,"Sets the degrees of freedom adjustment", "[integer]",commandName, "setDFReduction")) { int dfr = (int)(MesquiteInteger.fromString(arguments)); if (!MesquiteInteger.isCombinable(dfr)) //not a menu command.. MesquiteMessage.warnProgrammer("setDFReduction invoked without parameter"); else dfReduction = dfr; } else if (checker.compare(thisClass,"Sets the length of the added branch for predication intervals", "[double]",commandName, "setVh")) { double vht = (double)(MesquiteDouble.fromString(arguments)); if (!MesquiteDouble.isCombinable(vht)) //not a menu command.. MesquiteMessage.warnProgrammer("setVh invoked without parameter"); else vh.setValue(vht); } else if (checker.compare(thisClass,"Sets the length of the added branch for predication intervals", "[double]",commandName, "setWidth1")) { double w1 = (double)(MesquiteDouble.fromString(arguments)); if (!MesquiteDouble.isCombinable(w1)) //not a menu command.. MesquiteMessage.warnProgrammer("setWidth1 invoked without parameter"); else width1 = w1; } else if (checker.compare(thisClass,"Sets the length of the added branch for predication intervals", "[double]",commandName, "setWidth2")) { double w2 = (double)(MesquiteDouble.fromString(arguments)); if (!MesquiteDouble.isCombinable(w2)) //not a menu command.. MesquiteMessage.warnProgrammer("setWidth2 invoked without parameter"); else width2 = w2; } else if (checker.compare(thisClass,"Sets state of Vh query done", "[on/off]",commandName, "toggleVhQueryDone")) { if (StringUtil.blank(arguments)) //not a menu command.. MesquiteMessage.warnProgrammer("toggleVhQueryDone invoked without parameter"); else{ String s = parser.getFirstToken(arguments); if ("on".equalsIgnoreCase(s)) vhQueryDone = true; else if ("off".equalsIgnoreCase(s)) vhQueryDone = false; else vhQueryDone = !vhQueryDone; } } else if (checker.compare(thisClass,"Sets state of df query done", "[on/off]",commandName, "toggleDFQueryDone")) { if (StringUtil.blank(arguments)) //not a menu command.. MesquiteMessage.warnProgrammer("toggleDFQueryDone invoked without parameter"); else{ String s = parser.getFirstToken(arguments); if ("on".equalsIgnoreCase(s)) dfQueryDone = true; else if ("off".equalsIgnoreCase(s)) dfQueryDone = false; else dfQueryDone = !dfQueryDone; } } else if (checker.compare(thisClass,"Sets state of ignore root tritomies", "[on/off]",commandName, "toggleIgnoreRootTritomies")) { if (StringUtil.blank(arguments)) //not a menu command.. MesquiteMessage.warnProgrammer("toggleIgnoreRootTritomies invoked without parameter"); else{ String s = parser.getFirstToken(arguments); if ("on".equalsIgnoreCase(s)) ignoreRootTritomies = true; else if ("off".equalsIgnoreCase(s)) ignoreRootTritomies = false; else ignoreRootTritomies = !ignoreRootTritomies; } } else return super.doCommand(commandName, arguments, checker); return null; } /* This checks whether the screenNumber specifies a valid screen; if new screens are added, this needs to be updated */ final private boolean validScreens[] = {false, true, false, true, false, true, false, true, false, true, true, true, true, true}; private boolean validScreen(int screenNumber) { if (screenNumber < 0 || screenNumber > 13) return false; else return validScreens[screenNumber]; } long oldTreeID = -1; long oldTreeVersion = 0; /*.................................................................................................................*/ public void setTree(Tree tree) { if (tree==null) return; if (!((MesquiteTree)tree).allLengthsAssigned(false) && !(MesquiteThread.isScripting() && quiet)) { alert("The tree you want to analyze contains one or more branches with unassigned branch lengths. " + "You should go back to the tree window and specify branch lengths (e.g set to 1, Pagel's method, etc.)."); //iQuit(); //window.dispose(); //return; } if (this.tree !=null && this.tree instanceof NoPolyTree && this.tree !=tree) { // about to set new tree ((NoPolyTree)this.tree).disconnect(); } if (tree instanceof NoPolyTree) this.tree=tree; else this.tree = new NoPolyTree((MesquiteTree)tree); if (oldTreeID==-1 || tree.getID() != oldTreeID || tree.getVersionNumber() != oldTreeVersion){ setDFQueryDone(false); // make sure these are reset before any redraws setVhQueryDone(false); vh.setToUnassigned(); } if (this.tree instanceof Selectionable) charterTask.pointsAreSelectable(true, (Selectionable)this.tree, false); currentTaxa = this.tree.getTaxa(); if (oldTreeID==-1) { if (contrastsStatsTask==null || residualsTask == null) return; //setDFQueryDone(false); //setVhQueryDone(false); //vh.setToUnassigned(); contrastsStatsTask.initialize(tree); residualsTask.initialize(tree); characterSourceTask.initialize(tree.getTaxa()); doCounts(); } else if (tree.getID() != oldTreeID || tree.getVersionNumber() != oldTreeVersion) { //setDFQueryDone(false); //setVhQueryDone(false); //vh.setToUnassigned(); doCounts(); } else { if (window!=null && window.getChart()!=null ) { window.getChart().getField().repaint(); //for selection Only window.getChart().repaint(); } } oldTreeID = tree.getID(); oldTreeVersion = tree.getVersionNumber(); } /*.................................................................................................................*/ public void employeeParametersChanged(MesquiteModule employee, MesquiteModule source, Notification notification) { if (contrastsStatsTask!=null && residualsTask!=null && characterSourceTask !=null) { doCounts(); } } /*.................................................................................................................*/ public CharacterDistribution getObserved1(){ return window.observedStates; } /*.................................................................................................................*/ public CharacterDistribution getObserved2(){ return window.observedStates2; } /*.................................................................................................................*/ public Tree getTree(){ return tree; } /*.................................................................................................................*/ public boolean getDFQueryDone(){ return dfQueryDone; } /*.................................................................................................................*/ public void setDFQueryDone(boolean value){ dfQueryDone = value; } /*.................................................................................................................*/ public int getDFReduction(){ return dfReduction; } public void setDFReduction(int value){ dfReduction = value; } public int queryDFReduce(PDAP2CTStatPak sp, boolean notifyIfZero){ if(!MesquiteThread.isScripting()){ //if scripting use 0 (default) or value set by script sp.countInternalNodes(tree, tree.getRoot()); int polytomies = ((NoPolyTree)tree).countZLBs(sp.getIgnoreRootTritomies()); // Zero-Length-Branches are what PDAP calls "polytomies", count to inform the user if ((polytomies >0)) { int tmp = MesquiteInteger.queryInteger(window, "DF Reduce ", "This tree contains polytomies, indicated by the presence \n" + "of " + polytomies + " zero-length branch(es).\n" + "If some of these collapsed branches indicate\n" + "soft polytomies, then you may want to reduce\n" + "degrees of freedom (see Purvis and Garland, 1993,\n" + "Syst. Biol. 42:569-575; Garland and Diaz-Uriarte, 1999,\n" + "Syst. Biol. 48:547-558).\n\n " + "Enter number of Degrees of Freedom to subtract for soft polytomies", dfReduction, 0, MesquiteInteger.infinite); if (MesquiteInteger.isCombinable(tmp)) { dfReduction = tmp; } } else { if (notifyIfZero) { int tmp = MesquiteInteger.queryInteger(window, "DF Reduce ", "This tree is fully dichotomized and contains no polytomies.\n" + "If, for whatever reason, you wish to decrease\n" + "degrees of freedom (see Purvis and Garland, 1993,\n" + "Syst. Biol. 42:569-575; Garland and Diaz-Uriarte, 1999,\n" + "Syst. Biol. 48:547-558), you may do so here.\n\n" + "Enter degrees of freedom to subtract", dfReduction, 0, MesquiteInteger.infinite); //alert(null,"","The tree has no polytomies, hence there is no need to reduce degrees of freedom to compensate for polytomies."); if (MesquiteInteger.isCombinable(tmp)) dfReduction = tmp; else dfReduction = 0; } } } return dfReduction; } /*-----------------------------------------*/ /** * This prompts the user and sets the CI/PI widths. The results are saved in the width1 and width2 fields. */ public void queryWidths(){ if (!MesquiteThread.isScripting()){ // if scripting, widths either default or set elsewhere MesquiteDouble tmp1 = new MesquiteDouble(width1); MesquiteDouble tmp2 = new MesquiteDouble(width2); MesquiteBoolean ans = new MesquiteBoolean(); MesquiteDouble.queryTwoDoubles(window, "Interval Widths", "Enter the first (2-tailed) p-value width for CI/PI plots", "Enter the second (2-tailed) p-value width for CI/PI plots", ans, tmp1, tmp2); if (tmp1.isCombinable()) { double tp1 = tmp1.getValue(); if ((tp1 <= 1.0) && (tp1 >= 0.0)) width1 = tp1; } if (tmp2.isCombinable()){ double tp2 = tmp2.getValue(); if ((tp2 <= 1.0) && (tp2 >= 0.0)) width2 = tp2; } } } public double getWidth1(){ return width1; } public double getWidth2(){ return width2; } public void setWidthQueryDone(boolean value){ widthQueryDone = value; } public boolean getWidthQueryDone(){ return widthQueryDone; } /*.................................................................................................................*/ public boolean getVhQueryDone(){ return vhQueryDone; } /*.................................................................................................................*/ public void setVhQueryDone(boolean value){ vhQueryDone = value; } public double getVh(){ return vh.getValue(); } /*.................................................................................................................*/ /** This prompts the user and sets Vh */ public double queryVh(PDAP2CTStatPak sp){ double tmp = sp.getMeanHeight(tree); if (!MesquiteThread.isScripting()){ String contempString; if (sp.getContempTips()) contempString = "All tips are contemporaneous in the uncorrected tree."; else contempString = "The tips are not contemporaneous in the uncorrected tree."; tmp = MesquiteDouble.queryDouble(window, "Vh", "The calculations for prediction intervals effectively assume " + "that the species to be predicted is attached at the root " + "(basal node) of the phylogenetic tree (see Garland and Ives, " + "2000, Am. Nat. 155:346-364). By default, the branch length (Vh) " + "leading to the predicted species is assumed to equal the average" + "length of the (uncorrected) branches from root to tips. \n\n" + contempString + "\n\n Enter a value for Vh\n", tmp, 0, MesquiteDouble.infinite); if (MesquiteDouble.isCombinable(tmp)){ vh.setValue(tmp); vhQueryDone = true; } else{ AlertDialog.notice(window, "Vh undefined", "Vh could not be estimated and is not meaningful because one or more " + "branches in the tree have undefined branch lengths. Either assign lengths " + "or use a constant transform to set all branches equal to one. No confidence or " + "prediction intervals will be displayed."); vh.setToUnassigned(); } } else vh.setValue(tmp); return vh.getValue(); } public void queryVerboseQueryMode(){ MesquiteBoolean ok = MesquiteBoolean.queryCheckBox(window, "Set Verbose Query Preference","In verbose mode you will be asked for settings for PDAP root reconstruction frequently; otherwise you change the settings using menu items","Use Verbose Mode", verboseQueryMode.getValue()); if (!ok.isUnassigned()) { verboseQueryMode.setValue(ok.getValue()); storePreferences(); } } public void toggleVerboseQueryMode(String arg){ verboseQueryMode.toggleValue(arg); storePreferences(); } public boolean getVerboseQueryMode(){ return verboseQueryMode.getValue(); } /*................................................................*/ public void queryIgnoreRootTritomies(){ MesquiteBoolean ok = MesquiteBoolean.queryCheckBox(window, "Set Ignore Basal Tritomies", "Some tree inference programs return trees that PDAP sees as having tritomies at the root; checking the box will ignore any tritomies at the root in counting or detecting polytomies for degrees of freedom adjustment", "Ignore Root Tritomies", ignoreRootTritomies); if (!ok.isUnassigned()){ ignoreRootTritomies = ok.getValue(); } } public boolean getIgnoreRootTritomies(){ return ignoreRootTritomies; } public void setIgnoreRootTritomies(boolean value){ ignoreRootTritomies = value; } /*................................................................*/ public String nameForWritableResults(){ return "PDAP Diagnostics from StatPak"; } public boolean suppliesWritableResults(){ return diagnosticsTask.suppliesWritableResults(); } public Object getWritableResults(){ return diagnosticsTask.getWritableResults(); } public Object getResultsHeading(){ return diagnosticsTask.getResultsHeading(); } /*.................................................................................................................*/ private void doCounts() { window.blankChart(); window.setTree(tree); window.recalcChart(); window.contentsChanged(); } /*.................................................................................................................*/ public String getName() { return "PDAP Diagnostic Chart"; } /*.................................................................................................................*/ public String getAuthors() { return "Peter E. Midford, Ted Garland Jr., and Wayne P. Maddison" ; } /*.................................................................................................................*/ public String getVersion() { return "1.15"; } /*.................................................................................................................*/ //Peter: add this method returning true for all citable modules, e.g. main calculations public boolean showCitation(){ return true; } /*.................................................................................................................*/ public boolean isPrerelease() { return false; } /*.................................................................................................................*/ public boolean requestPrimaryChoice() { return true; } /*.................................................................................................................*/ /** returns an explanation of what the module does.*/ public String getExplanation() { return "Shows independent constrasts and related values for nodes of tree via a scattergram." ; } /*.................................................................................................................*/ public void endJob() { if (treeDisplay !=null && nsLabeller !=null) treeDisplay.removeExtra(nsLabeller); super.endJob(); } } /* ======================================================================== */ /**=== Local Class BannerPanel. ===*/ class BannerPanel extends MesquitePanel{ public void paint(Graphics g){ g.setColor(Color.cyan); g.drawString("PDAP: PDTREE module", 16, getBounds().height-4); } } /*========================================================*/ class NodeLabeller extends TreeDisplayExtra { LabelsAtNodes labelsAtNodes; PDAPChartWindow chartWindow; final static Color brightGreen = new Color((float)0.4, (float)1.0, (float)0.4); final static Color brightbrightGreen = new Color((float)0.6, (float)1.0, (float)0.6); boolean showLabels = true; int labelDrawn = -1; public NodeLabeller (MesquiteModule ownerModule, TreeDisplay treeDisplay, PDAPChartWindow chartWindow) { super(ownerModule, treeDisplay); this.chartWindow =chartWindow; } /*.................................................................................................................*/ void drawOneLabel(int N, Tree tree, Graphics g) { makeSureLabelsReady(tree); int nodeX = (int)treeDisplay.getTreeDrawing().getX(N); int nodeY = (int)treeDisplay.getTreeDrawing().getY(N); MesquiteLabel c = (MesquiteLabel)labelsAtNodes.getPanel(N); c.setColor(brightbrightGreen); c.setText("Node " + N); String[] results = chartWindow.getStrings(N); for (int i = 0; itreeDisplay.getBounds().width) c.setLocation(treeDisplay.getBounds().width- w, nodeY + 18); else c.setLocation(nodeX + 16, nodeY + 18); g.setColor(brightGreen); g.setXORMode(Color.white); g.drawLine(nodeX, nodeY, c.getBounds().x, c.getBounds().y); g.drawLine(nodeX, nodeY+1, c.getBounds().x, c.getBounds().y + 1); g.drawLine(nodeX, nodeY+2, c.getBounds().x, c.getBounds().y + 2); g.drawLine(nodeX, nodeY+3, c.getBounds().x, c.getBounds().y + 3); g.setPaintMode(); g.setColor(Color.black); labelDrawn = N; } /*.................................................................................................................*/ void hideOneLabel(int N, Graphics g) { MesquiteLabel c = (MesquiteLabel)labelsAtNodes.getPanel(N); if (c==null || g==null) return; c.setVisible(false); final int nodeX = (int)treeDisplay.getTreeDrawing().getX(N); final int nodeY = (int)treeDisplay.getTreeDrawing().getY(N); g.setColor(brightGreen); g.setXORMode(Color.white); g.drawLine(nodeX, nodeY, c.getBounds().x, c.getBounds().y); g.drawLine(nodeX, nodeY+1, c.getBounds().x, c.getBounds().y + 1); g.drawLine(nodeX, nodeY+2, c.getBounds().x, c.getBounds().y + 2); g.drawLine(nodeX, nodeY+3, c.getBounds().x, c.getBounds().y + 3); g.setPaintMode(); labelDrawn = -1; } /*.................................................................................................................*/ private void drawLabels(int N, Tree tree, Graphics g) { for (int d = tree.firstDaughterOfNode(N); tree.nodeExists(d); d = tree.nextSisterOfNode(d)) drawLabels(d, tree, g); drawOneLabel(N, tree, g); } /*.................................................................................................................*/ void hideLabels() { if (labelsAtNodes==null) return; Graphics g = treeDisplay.getGraphics(); for (int N = 0; N20) chName = chName.substring(0, 17) + "..."; return chName + " (" + template + ")"; } /*.................................................................................................................*/ public void recalcChart() { chart.deassignChart(); if (ownerModule.contrastsStatsTask != null && ownerModule.residualsTask != null) { resetScrolls(); final int numNodes = tree.getNumNodeSpaces(); xArray.resetSize(numNodes); yArray.resetSize(numNodes); //zArray.resetSize(numNodes); //if (ownerModule.contrastsStatsTask instanceof Incrementable) // ((Incrementable)ownerModule.contrastsStatsTask).setCurrent(ownerModule.currentX); // check for polytomies, set warning boolean hasPolytomies; if (tree instanceof NoPolyTree) hasPolytomies = ((NoPolyTree)tree).hasZLBs(); else hasPolytomies = tree.hasPolytomies(tree.getRoot()); //x values int iChar = ownerModule.currentX; // actually this is *only* true for bivariate plots (screens 9&10). switch (ownerModule.screen){ case 1:{ ownerModule.contrastsStatsTask.setOption(ContrastForChar.SD); iChar = ownerModule.currentY; //currentY is the character specified in the single scroll - both axes are functions of this. //Plot of absolute value of contrast vs. standard deviation if (hasPolytomies) legend.setMessage(SCREEN1LEGEND + POLYTOMYWARNING); else legend.setMessage(SCREEN1LEGEND); break; } case 3:{ ownerModule.contrastsStatsTask.setOption(ContrastForChar.NODEVALUE); iChar = ownerModule.currentY; //Plot of absolute value of contrast vs. estimated value of associated node if (hasPolytomies) legend.setMessage(SCREEN3LEGEND + POLYTOMYWARNING); else legend.setMessage(SCREEN3LEGEND); break; } case 5:{ ownerModule.contrastsStatsTask.setOption(ContrastForChar.CORRECTEDHEIGHT); iChar = ownerModule.currentY; //Plot of absolute value of contrast vs. height of associated node if (hasPolytomies) legend.setMessage(SCREEN5LEGEND + POLYTOMYWARNING); else legend.setMessage(SCREEN5LEGEND); break; } case 7:{ ownerModule.contrastsStatsTask.setOption(ContrastForChar.CORRECTEDHEIGHT); iChar = ownerModule.currentY; //Plot of estimated value of associated node vs. its height legend.setMessage(SCREEN7LEGEND); break; } case 9:{ ownerModule.contrastsStatsTask.setOption(ContrastForChar.CONTRAST); iChar = ownerModule.currentX; // character specified in the horizontal scroll //Plot of contrasts vs. positivized contrasts legend.setMessage(SCREEN9LEGEND); break; } case 10:{ ownerModule.contrastsStatsTask.setOption(ContrastForChar.ABSCONTRAST); iChar = ownerModule.currentX; // character specified in the horizontal scroll //Plot of residuals from screen 9 vs. contrast values legend.setMessage(SCREEN10LEGEND); break; } case 11:{ ownerModule.contrastsStatsTask.setOption(ContrastForChar.CORRECTEDHEIGHT); iChar = ownerModule.currentX; //Plot of absolute value of residual vs. height of associated node legend.setMessage(SCREEN11LEGEND); break; } case 12:{ ownerModule.contrastsStatsTask.setOption(ContrastForChar.TIPS); iChar = ownerModule.currentX; //Plot of tip values and confidence intervals for reconstructed root values legend.setMessage(SCREEN12LEGEND);//Peter: what of this depends on diagnostics being active? break; } case 13:{ ownerModule.contrastsStatsTask.setOption(ContrastForChar.TIPS); iChar = ownerModule.currentX; //? //"Plot of phylogenetically correct (OLS) regression and associated CI's" //Peter: what of this depends on diagnostics being active? legend.setMessage(SCREEN13LEGEND); //Peter: what of this depends on diagnostics being active? } } if (true ) { //if one character calculation, don't get character unless needed observedStates = (ContinuousDistribution)ownerModule.characterSourceTask.getCharacter(tree, iChar); } ownerModule.contrastsStatsTask.calculateNumbers(tree, observedStates, xArray, null); // This does the work for the x-axis chart.setXAxisName(axisName(ownerModule.contrastsStatsTask.getParameters(), observedStates, ownerModule.screen, true)); //set to correct name String xItem = ownerModule.contrastsStatsTask.getItemName(); if (ownerModule.contrastsStatsTask instanceof Incrementable) ((Incrementable)ownerModule.contrastsStatsTask).setCurrent(ownerModule.currentY); //y values switch (ownerModule.screen){ case 1: case 3: case 5: { ownerModule.contrastsStatsTask.setOption(ContrastForChar.ABSCONTRAST); break; } case 7:{ ownerModule.contrastsStatsTask.setOption(ContrastForChar.NODEVALUE); break; } case 9:{ ownerModule.contrastsStatsTask.setOption(ContrastForChar.CONTRAST); break; } case 10:{ ownerModule.contrastsStatsTask.setOption(ContrastForChar.RESIDUAL); ownerModule.residualsTask.setOption(0);//raw break; } case 11:{ ownerModule.contrastsStatsTask.setOption(ContrastForChar.ABSRESIDUAL); ownerModule.residualsTask.setOption(1);//absolute break; } case 12: case 13:{ ownerModule.contrastsStatsTask.setOption(ContrastForChar.TIPS); break; } } if (true ) { //if one character calculation, don't get character unless needed TODO: make this work observedStates2 = (ContinuousDistribution)ownerModule.characterSourceTask.getCharacter(tree, ownerModule.currentY); } switch (ownerModule.screen) { case 1: case 3: case 5: case 7: case 9: { //ownerModule.contrastsStatsTask.setXCharForReport(ownerModule.currentX); //ownerModule.contrastsStatsTask.setYCharForReport(ownerModule.currentY); ownerModule.contrastsStatsTask.calculateNumbers(tree, observedStates2, yArray, null); // This does the work for the y-axis break; } case 10: case 11: { ownerModule.residualsTask.calculateNumbers(tree, observedStates, observedStates2, yArray, null); break; } case 12: case 13: { ownerModule.contrastsStatsTask.setXCharForReport(ownerModule.currentX); ownerModule.contrastsStatsTask.setYCharForReport(ownerModule.currentY); ownerModule.contrastsStatsTask.calculateNumbers(tree, observedStates2, yArray, null); break; } } // maybe call it here?? chart.setYAxisName(axisName(ownerModule.contrastsStatsTask.getParameters(), observedStates2, ownerModule.screen, false)); //TODO: this should use string passed back from calculateNumbers ??? boolean showTips = false; if ((ownerModule.screen == 12) || (ownerModule.screen == 13)) showTips = true; else if (ownerModule.screen == 9){ chart.setXAxisName(axisName("Positivized Contrasts - CAUTION if any values are zero", observedStates, ownerModule.screen, true)); //force to correct name chart.setYAxisName(axisName("Contrasts Positivized on the X variable (Garland et al. 1992)", observedStates2, ownerModule.screen, false)); } for (int i=0; i