Java tutorial
/* * iDMC the interactive Dynamical Model Calculator simulates and performs * graphical and numerical analysis of systems of differential and * difference equations. * * Copyright (C) 2004 Marji Lines and Alfredo Medio. * * Written by Daniele Pizzoni <auouo@tin.it>. * Extended by Alexei Grigoriev <alexei_grigoriev@libero.it>. * * * * The software program was developed within a research project financed * by the Italian Ministry of Universities, the Universities of Udine and * Ca'Foscari of Venice, the Friuli-Venezia Giulia Region. * * 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 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. */ package org.tsho.dmc2.core.chart; import java.util.HashSet; import java.util.Iterator; import java.awt.BasicStroke; import java.awt.Color; import java.awt.Graphics2D; import java.awt.Paint; import java.awt.Shape; import java.awt.Stroke; import java.awt.geom.Rectangle2D; import java.awt.geom.AffineTransform; import java.awt.image.AffineTransformOp; import java.awt.image.ImageObserver; import java.awt.Image; import java.awt.image.BufferedImage; import java.awt.image.DataBufferInt; import java.awt.image.WritableRaster; import org.jfree.chart.LegendItem; import org.jfree.chart.LegendItemCollection; import org.jfree.chart.plot.PlotRenderingInfo; import org.jfree.data.Range; import org.jfree.ui.RectangleEdge; import org.jfree.chart.axis.ValueAxis; import org.tsho.dmc2.core.VariableDoubles; import org.tsho.dmc2.core.dlua.Lua; import org.tsho.dmc2.core.dlua.LuaLyapExp; import org.tsho.dmc2.core.model.Model; import org.tsho.dmc2.core.model.ODE; import org.tsho.dmc2.ui.lyapunov.*; //?14.7.2004 import org.tsho.dmc2.core.util.*; import org.tsho.dmc2.core.util.LyapunovColors; //? public class LyapunovRenderer implements DmcPlotRenderer, ImageObserver { private DmcRenderablePlot plot; private LyapunovComponent lyapunovComponent; // parameters private Model model; private double lower; private double upper; private double epsilon; private VariableDoubles parameters; private VariableDoubles initialPoint; private String firstParLabel; private String secondParLabel; private int iterations; private double stepSize; private double timePeriod; // options private boolean connectWithLines; private boolean bigDots; private LyapunovColors lyapunovColors; // flags private boolean stopped; //? private int pass;//needed for redrawing chart after legend changed in the type=TYPE_AREA case private HashSet signsSet; private BufferedImage image; //? // internal state private int state; private Paint[] paintSequence; private final byte TYPE_VSTIME = 1; private final byte TYPE_VSPAR = 2; private final byte TYPE_AREA = 3; private byte type; public LyapunovRenderer(final DmcRenderablePlot plot, final Model model, LyapunovComponent lyapunovComponent) { this.plot = plot; this.model = model; this.lyapunovComponent = lyapunovComponent; //lyapunovComponent.setDataobject(DataObject dataobject) this.pass = 0; this.parameters = model.getParameters(); this.initialPoint = model.getVariables(); paintSequence = new Paint[model.getNVar()]; for (int i = 0; i < model.getNVar(); i++) { paintSequence[i] = plot.getDrawingSupplier().getNextPaint(); } lyapunovColors = new LyapunovColors(model.getNVar()); signsSet = new HashSet(); } public void initialize() { stopped = false; } public void render(Graphics2D g2, Rectangle2D dataArea, PlotRenderingInfo info) { boolean status; state = STATE_RUNNING; if (type == TYPE_VSTIME) status = renderVsTime(g2, dataArea); else if (type == TYPE_VSPAR) status = renderVsParameter(g2, dataArea); else if (type == TYPE_AREA) status = renderArea(g2, dataArea); else { throw new InternalError("Invalid plot type"); } state = STATE_FINISHED; } public boolean renderVsTime(Graphics2D g2, Rectangle2D dataArea) { final int imageWidth = (int) dataArea.getWidth(); g2.setPaint(Color.red); final int timeStep; if (Math.abs(upper - lower) >= imageWidth) { timeStep = ((int) upper - (int) lower) / imageWidth; } else { timeStep = 1; } int prevX[] = new int[model.getNVar()]; int prevY[] = new int[model.getNVar()]; //? 14.7.2004 //LyapunovExpCalc lyapunovExpCalc; LuaLyapExp integrator = new LuaLyapExp(model, parameters, initialPoint); String colNames[] = new String[model.getNVar() + 1]; colNames[0] = "time"; for (int j = 1; j < model.getNVar() + 1; j++) colNames[j] = "" + (new Integer(j)); Dataset dataset = new Dataset(colNames); lyapunovComponent.setDataobject(dataset); double tmpRow[]; if (model instanceof ODE) { double step = stepSize; // stepSize and timeStep probably mean the same thing, one for discrete another for ODE //checking trajectory for (int i = 0; i < (int) (upper / stepSize); i++) { double[] result = new double[model.getNVar()]; int transX, transY; double[] temp = integrator.evaluateODEStep(step); double tt = integrator.getTime(); for (int h = 0; h < temp.length; h++) { result[h] = temp[h] / tt; } tmpRow = new double[model.getNVar() + 1]; tmpRow[0] = i; for (int a1 = 1; a1 < tmpRow.length; a1++) tmpRow[a1] = result[a1 - 1]; try { dataset.addRow(tmpRow); } catch (DatasetException de) { System.out.println("" + de); } for (int j = 0; j < result.length; j++) { if (Double.isNaN(result[j])) break; transX = (int) plot.getDomainAxis().valueToJava2D(i * stepSize, dataArea, RectangleEdge.BOTTOM); transY = (int) plot.getRangeAxis().valueToJava2D(result[j], dataArea, RectangleEdge.LEFT); g2.setPaint(paintSequence[j]); if (bigDots) { g2.fillRect(transX - 1, transY - 1, 3, 3); } else { g2.fillRect(transX, transY, 1, 1); } if (connectWithLines) { if (i > (int) lower) { g2.drawLine(transX, transY, prevX[j], prevY[j]); } prevX[j] = transX; prevY[j] = transY; } } if (stopped == true) { return false; } } } else {//? 14.7.2004 section of code above for (int i = (int) lower; i < (int) upper; i += timeStep) { double[] result = new double[model.getNVar()]; int transX, transY; result = Lua.evaluateLyapunovExponents(model, parameters, initialPoint, i); tmpRow = new double[model.getNVar() + 1]; tmpRow[0] = i; for (int a1 = 1; a1 < tmpRow.length; a1++) tmpRow[a1] = result[a1 - 1]; try { dataset.addRow(tmpRow); } catch (DatasetException de) { System.out.println("" + de); } for (int j = 0; j < result.length; j++) { if (Double.isNaN(result[j])) break; transX = (int) plot.getDomainAxis().valueToJava2D(i, dataArea, RectangleEdge.BOTTOM); transY = (int) plot.getRangeAxis().valueToJava2D(result[j], dataArea, RectangleEdge.LEFT); g2.setPaint(paintSequence[j]); if (bigDots) { g2.fillRect(transX - 1, transY - 1, 3, 3); } else { g2.fillRect(transX, transY, 1, 1); } if (connectWithLines) { if (i > (int) lower) { g2.drawLine(transX, transY, prevX[j], prevY[j]); } prevX[j] = transX; prevY[j] = transY; } } if (stopped == true) { return false; } } } //? } belongs to else 14.7.2004 return true; } public boolean renderVsParameter(Graphics2D g2, Rectangle2D dataArea) { final int imageWidth = (int) dataArea.getWidth(); g2.setPaint(Color.red); final double parStep; int prevX[] = new int[model.getNVar()]; int prevY[] = new int[model.getNVar()]; //? 28.7.2004 ValueAxis domainAxis = plot.getDomainAxis(); lower = domainAxis.getRange().getLowerBound(); upper = domainAxis.getRange().getUpperBound(); parStep = Math.abs(upper - lower) / imageWidth; String colNames[] = new String[model.getNVar() + 1]; colNames[0] = firstParLabel; for (int j = 1; j < (model.getNVar() + 1); j++) colNames[j] = "" + (new Integer(j)); Dataset dataset = new Dataset(colNames); lyapunovComponent.setDataobject(dataset); double tmpRow[]; if (model instanceof ODE) { double step = stepSize; // stepSize and timeStep probably mean the same thing, one for discrete another for ODE for (double i = lower; i < upper; i += parStep) { double[] result = new double[model.getNVar()];//initializing not needed but compiler too cautious. int transX, transY; parameters.put(firstParLabel, i); result = Lua.evaluateLyapunovExponentsODE(model, parameters, initialPoint, timePeriod, stepSize); tmpRow = new double[model.getNVar() + 1]; tmpRow[0] = i; for (int a1 = 1; a1 < tmpRow.length; a1++) tmpRow[a1] = result[a1 - 1]; try { dataset.addRow(tmpRow); } catch (DatasetException de) { System.out.println("" + de); } for (int j = 0; j < result.length; j++) { if (Double.isNaN(result[j])) break; transX = (int) plot.getDomainAxis().valueToJava2D(i, dataArea, RectangleEdge.BOTTOM); transY = (int) plot.getRangeAxis().valueToJava2D(result[j], dataArea, RectangleEdge.LEFT); g2.setPaint(paintSequence[j]); if (bigDots) { g2.fillRect(transX - 1, transY - 1, 3, 3); } else { g2.fillRect(transX, transY, 1, 1); } if (connectWithLines) { if (i != lower) { g2.drawLine(transX, transY, prevX[j], prevY[j]); } prevX[j] = transX; prevY[j] = transY; } } if (stopped == true) { return false; } } } //? 28.7.2004 else { for (double i = lower; i < upper; i += parStep) { double[] result; int transX, transY; parameters.put(firstParLabel, i); result = Lua.evaluateLyapunovExponents(model, parameters, initialPoint, iterations); tmpRow = new double[model.getNVar() + 1]; tmpRow[0] = i; for (int a1 = 1; a1 < tmpRow.length; a1++) tmpRow[a1] = result[a1 - 1]; try { dataset.addRow(tmpRow); } catch (DatasetException de) { System.out.println("" + de); } for (int j = 0; j < result.length; j++) { if (Double.isNaN(result[j])) break; transX = (int) plot.getDomainAxis().valueToJava2D(i, dataArea, RectangleEdge.BOTTOM); transY = (int) plot.getRangeAxis().valueToJava2D(result[j], dataArea, RectangleEdge.LEFT); g2.setPaint(paintSequence[j]); if (bigDots) { g2.fillRect(transX - 1, transY - 1, 3, 3); } else { g2.fillRect(transX, transY, 1, 1); } if (connectWithLines) { if (i != lower) { g2.drawLine(transX, transY, prevX[j], prevY[j]); } prevX[j] = transX; prevY[j] = transY; } } if (stopped == true) { return false; } } } return true; } public boolean renderArea(Graphics2D g2, Rectangle2D dataArea) { g2.setPaint(plot.paint); if (pass == 1) { if (image != null) { double x = dataArea.getX(); double y = dataArea.getY(); //there is a problem when using Graphics2D with affine transform //and BufferedImage; using subclass of Image returned below. //rescaling needed because adding legend causes dataArea to change. Image scaledImage = image.getScaledInstance((int) dataArea.getWidth() - 1, (int) dataArea.getHeight() - 1, Image.SCALE_DEFAULT); g2.drawImage(scaledImage, (int) x + 1, (int) y + 1, (int) dataArea.getWidth() - 1, (int) dataArea.getHeight() - 1, this); //g2.translate(-1,-1); //g2.drawRect((int) x, (int) y, (int) dataArea.getWidth(),(int) dataArea.getHeight()); //g2.translate(1,1); } return true; } final double parHStep, parVStep; double parHLower = plot.getDomainAxis().getRange().getLowerBound(); double parHUpper = plot.getDomainAxis().getRange().getUpperBound(); double parVLower = plot.getRangeAxis().getRange().getLowerBound(); double parVUpper = plot.getRangeAxis().getRange().getUpperBound(); parHStep = Math.abs(parHUpper - parHLower) / dataArea.getWidth(); parVStep = Math.abs(parVUpper - parVLower) / dataArea.getHeight(); image = new BufferedImage((int) dataArea.getWidth(), (int) dataArea.getHeight(), BufferedImage.TYPE_INT_RGB); WritableRaster raster = image.getRaster(); DataBufferInt dataBuffer = (DataBufferInt) raster.getDataBuffer(); int[] data = dataBuffer.getData(); final double parHStart = parHLower + parHStep / 2; final double parVStart = parVUpper - parVStep / 2; if (model instanceof ODE) { double step = stepSize; // stepSize and timeStep probably mean the same thing, one for discrete another for ODE double[] result = new double[model.getNVar()]; for (int i = 0; i < (int) dataArea.getWidth(); i++) { for (int j = 0; j < (int) dataArea.getHeight(); j++) { parameters.put(firstParLabel, parHStart + i * parHStep); parameters.put(secondParLabel, parVStart - j * parVStep); int color; result = Lua.evaluateLyapunovExponentsODE(model, parameters, initialPoint, timePeriod, stepSize); if (result == null) { System.out.println("i: " + i + " j: " + j); System.out.println("par1: " + parHStart + i * parHStep); System.out.println("par2: " + parVStart + j * parVStep); g2.drawImage(image, null, (int) dataArea.getX() + 1, (int) dataArea.getY() + 1); return false; } int zer = 0; int pos = 0; int neg = 0; int nan = 0; for (int ii = 0; ii < result.length; ii++) { if (Math.abs(result[ii]) == (1.0 / 0.0)) nan++; else if (Math.abs(result[ii]) <= epsilon) zer++; else if (result[ii] > epsilon) pos++; else if (result[ii] < (-epsilon)) neg++; else nan++; } color = (lyapunovColors.getColor(zer, pos, neg, nan)).getRGB(); ExpsSigns es = new ExpsSigns(zer, pos, neg, nan); if (!signsSet.contains(es)) signsSet.add(es); data[i + j * (int) dataArea.getWidth()] = color; if (stopped == true) return false; if (j == (int) dataArea.getHeight() - 1) { g2.drawImage(image, null, (int) dataArea.getX() + 1, (int) dataArea.getY() + 1); } } //end for } //end for } //end if ODE else { for (int i = 0; i < (int) dataArea.getWidth(); i++) { for (int j = 0; j < (int) dataArea.getHeight(); j++) { parameters.put(firstParLabel, parHStart + i * parHStep); parameters.put(secondParLabel, parVStart - j * parVStep); double[] result; int color; result = Lua.evaluateLyapunovExponents(model, parameters, initialPoint, iterations); if (result == null) { System.out.println("i: " + i + " j: " + j); System.out.println("par1: " + parHStart + i * parHStep); System.out.println("par2: " + parVStart + j * parVStep); g2.drawImage(image, null, (int) dataArea.getX() + 1, (int) dataArea.getY() + 1); return false; } int zer = 0; int pos = 0; int neg = 0; int nan = 0; for (int ii = 0; ii < result.length; ii++) { if (Math.abs(result[ii]) == (1.0 / 0.0)) nan++; else if (Math.abs(result[ii]) <= epsilon) zer++; else if (result[ii] > epsilon) pos++; else if (result[ii] < (-epsilon)) neg++; else nan++; } color = (lyapunovColors.getColor(zer, pos, neg, nan)).getRGB(); ExpsSigns es = new ExpsSigns(zer, pos, neg, nan); if (!signsSet.contains(es)) signsSet.add(es); data[i + j * (int) dataArea.getWidth()] = color; if (stopped == true) return false; if (j == (int) dataArea.getHeight() - 1) g2.drawImage(image, null, (int) dataArea.getX() + 1, (int) dataArea.getY() + 1); } //end for } //end for } //end else return true; } public void stop() { stopped = true; } public int getState() { return state; } public void setState(int state) { this.state = state; } public void initializeVsTime(VariableDoubles parameters, VariableDoubles initialValues, Range timeRange) { this.parameters.putAll(parameters); this.initialPoint.putAll(initialValues); this.lower = (int) timeRange.getLowerBound(); this.upper = (int) timeRange.getUpperBound(); this.type = TYPE_VSTIME; } //? public void initializeVsTime(VariableDoubles parameters, VariableDoubles initialValues, Range timeRange, double stepSize) { this.parameters.putAll(parameters); this.initialPoint.putAll(initialValues); this.lower = (int) timeRange.getLowerBound(); this.upper = (int) timeRange.getUpperBound(); this.stepSize = stepSize; this.type = TYPE_VSTIME; } //? public void initializeVsParameter(VariableDoubles parameters, VariableDoubles initialValues, String parLabel, Range parameterRange, int iterations) { this.parameters.putAll(parameters); this.initialPoint.putAll(initialValues); this.firstParLabel = parLabel; this.lower = parameterRange.getLowerBound(); this.upper = parameterRange.getUpperBound(); this.iterations = iterations; this.type = TYPE_VSPAR; } //? public void initializeVsParameter(VariableDoubles parameters, VariableDoubles initialValues, String parLabel, Range parameterRange, double timePeriod, double stepSize) { this.parameters.putAll(parameters); this.initialPoint.putAll(initialValues); this.firstParLabel = parLabel; this.lower = parameterRange.getLowerBound(); this.upper = parameterRange.getUpperBound(); this.timePeriod = timePeriod; this.stepSize = stepSize; this.type = TYPE_VSPAR; } //? public void initializeParArea(VariableDoubles initialValues, VariableDoubles par, // fixed ones String firstParLabel, String secondParLabel, double epsilon, int iterations) { this.parameters.putAll(par); this.initialPoint.putAll(initialValues); this.firstParLabel = firstParLabel; this.secondParLabel = secondParLabel; this.epsilon = epsilon; this.iterations = iterations; plot.getDomainAxis().setLabel(firstParLabel); plot.getRangeAxis().setLabel(secondParLabel); this.type = TYPE_AREA; } //? public void initializeParArea(VariableDoubles initialValues, VariableDoubles par, // fixed ones String firstParLabel, String secondParLabel, double epsilon, double timePeriod, double stepSize) { this.parameters.putAll(par); this.initialPoint.putAll(initialValues); this.firstParLabel = firstParLabel; this.secondParLabel = secondParLabel; this.epsilon = epsilon; this.timePeriod = timePeriod; this.stepSize = stepSize; plot.getDomainAxis().setLabel(firstParLabel); plot.getRangeAxis().setLabel(secondParLabel); this.type = TYPE_AREA; } //? public LegendItemCollection getLegendItems() { if (type != TYPE_AREA) return null; LegendItemCollection legendItems = new LegendItemCollection(); Stroke stroke = new BasicStroke(1.0f, BasicStroke.CAP_SQUARE, BasicStroke.JOIN_BEVEL); Shape shape = new Rectangle2D.Double(-3, -3, 6, 6); Iterator i = signsSet.iterator(); while (i.hasNext()) { ExpsSigns es = (ExpsSigns) i.next(); Color color = lyapunovColors.getColor(es.zer, es.pos, es.neg, es.nan); legendItems.add(new LegendItem(es.toString(), "", shape, true, color, stroke, Color.yellow, stroke)); } if (pass == 0) { signsSet.clear(); } return legendItems; } public boolean isBigDots() { return bigDots; } public boolean isConnectWithLines() { return connectWithLines; } public void setBigDots(boolean b) { bigDots = b; } public void setConnectWithLines(boolean b) { connectWithLines = b; } public void setPass(int pass) { this.pass = pass; } public int getPass() { return pass; } //(x,y) stays fixed, but dataArea width and height get smaller. //this is an auxiliary method for renderArea //eventually not used as gives slightly incorrect output. private AffineTransform createTransform(double x, double y, BufferedImage image, Rectangle2D dataArea) { int wi = image.getWidth(); wi--; int hi = image.getHeight(); hi--; double wd = dataArea.getWidth(); wd--; double hd = dataArea.getHeight(); hd--; double a = wd / wi; double b = hd / hi; double m00 = a; double m01 = 0; double m02 = -x * a + x; double m10 = 0; double m11 = b; double m12 = -y * b + y; AffineTransform at = new AffineTransform(m00, m10, m01, m11, m02, m12); return at; } public boolean imageUpdate(java.awt.Image img, int infoflags, int x, int y, int width, int height) { return true; } class ExpsSigns { int zer; int pos; int neg; int nan; ExpsSigns(int z, int p, int n, int nan) { zer = z; pos = p; neg = n; this.nan = nan; } public int hashCode() { String s = "z" + zer + "p" + pos + "n" + neg + "nan" + nan; return s.hashCode(); } public String toString() { String s = ""; boolean prec = false; if (zer > 0) { s += zer + "zero"; prec = true; } if (pos > 0) { if (prec) s += ", "; s += pos + "positive"; prec = true; } if (neg > 0) { if (prec) s += ", "; s += neg + "negative"; prec = true; } if (nan > 0) { if (prec) s += ", "; s += nan + "NA"; } return s; } public boolean equals(Object o) { try { ExpsSigns es = (ExpsSigns) o; return (es.zer == zer && es.pos == pos && es.neg == neg && es.nan == nan); } catch (Exception e) { return false; } } } }