Java tutorial
package adapters; import org.jfree.chart.axis.NumberAxis; import org.jfree.chart.axis.NumberTickUnit; import java.util.Formatter; import java.util.Locale; /** * Project: DCDMC * Package: adapters * Date: 14/Apr/2015 * Time: 08:58 * System Time: 8:58 AM */ /* * Class: AxisAdapter * Description: An axis of a chart and its properties * Environment: Java * Software: SSJ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal * Organization: DIRO, Universite de Montreal * @author * @since * SSJ is free software: you can redistribute it and/or modify it under * the terms of the GNU General Public License (GPL) as published by the * Free Software Foundation, either version 3 of the License, or * any later version. * SSJ 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. * A copy of the GNU General Public License is available at <a href="http://www.gnu.org/licenses">GPL licence site</a>. */ /** * Represents an axis of a chart encapsulated by * an instance of <TT>XYChart</TT>. * <TT>AxisAdapter</TT> uses the JFreeChart class <TT>NumberAxis</TT> * to store some axis properties. * This class represents the <SPAN CLASS="MATH"><I>x</I></SPAN>-axis or the <SPAN CLASS="MATH"><I>y</I></SPAN>-axis of a <TT>XYChart</TT> * and, consequently, is drawn when calling the {@link #toLatex toLatex} method. * It provides tools to customize the axis in modifying labels and description. * */ public class AxisAdapter { public static final boolean ORIENTATION_VERTICAL = false; public static final boolean ORIENTATION_HORIZONTAL = true; private NumberAxis axis; // Uses JFreeChart API private boolean orientation; // Vertical or horizontal private double twinAxisPosition = 0; private boolean tick0Flag; // if true, label number at origin is shifted // right or above a little to prevent the label from being on the // perpendicular axis. private boolean labelsFlag; // true : enable manual labels private String[] labelsName; // Sets manual labels private double[] labelsValue; // Where putting the labels on the axes /** * Modified Methold which change modifier from "protected" to "public" * * Returns the <TT>NumberAxis</TT> instance (from JFreeChart) linked with the current variable. * * @return the <TT>NumberAxis</TT> instance (from JFreeChart) linked with the current variable. * */ protected NumberAxis getAxis() { return axis; } private double fixStep(double minA, double maxA, double step) { // reset step for labels on axes if it is too large or too small final double H = 10; final double inter = maxA - minA; while (step > inter) // step is too large, make it smaller step /= H; while (step < inter / H) // step is too small, make it larger step *= H; if (step > inter / 2) step /= 2; return step; } /** * Create a new <TT>AxisAdapter</TT> instance from an existing <TT>NumberAxis</TT> * instance with vertical (<SPAN CLASS="MATH"><I>y</I></SPAN>-axis) or horizontal (<SPAN CLASS="MATH"><I>x</I></SPAN>-axis) orientation. * * @param inAxis NumberAxis instance associated to the new variable. * * @param orientation axis direction, horizontal or vertical * */ public AxisAdapter(NumberAxis inAxis, boolean orientation) { this.axis = inAxis; this.orientation = orientation; this.labelsFlag = false; this.labelsValue = null; this.labelsName = null; inAxis.setAutoRangeIncludesZero(false); inAxis.setLowerMargin(0); inAxis.setUpperMargin(0); axis.setTickUnit(new NumberTickUnit(computeAutoTickValue())); } /** * Returns the axis description. * * @return the axis description. * */ public String getLabel() { return axis.getLabel(); } /** * Sets the axis description. This description will be displayed on the chart, near the axis. * * @param label axis label. * * */ public void setLabel(String label) { axis.setLabel(label); } /** * Sets a periodic label display. Labels will be shown every <TT>tick</TT> unit. * This tick unit replaces the default unit. * * @param tick tick unit. * * */ public void setLabels(double tick) { axis.setTickUnit(new NumberTickUnit(tick)); labelsFlag = false; } /** * Calculates and sets an automatic tick unit. * */ public void setLabelsAuto() { axis.setTickUnit(new NumberTickUnit(computeAutoTickValue())); labelsFlag = false; } /** * Not used anymore. * */ @Deprecated public void enableCustomLabels() { labelsFlag = true; } /** * Not used anymore. * */ @Deprecated public void disableCustomLabels() { labelsFlag = false; } /** * Sets the position of each label on this axis. This method requires an array * containing an increasing sequence of numbers representing the positions at * which labels will appear on the axis. It is designed to export the * axis to a LaTeX source code; it has no effect on the chart appearance * displayed with <TT>XYChart.view()</TT>. * * @param position new label positions. * * */ public void setLabels(double[] position) { this.labelsName = null; this.labelsValue = position.clone(); double max = max(position); if (axis.getUpperBound() < max) axis.setUpperBound(max); double min = min(position); if (axis.getLowerBound() > min) axis.setLowerBound(min); labelsFlag = true; } /** * Assigns custom labels to user-defined positions on the axis. * This method requires an array of positions as well as an array of labels. * The label <TT>label[i]</TT> will be used at position <TT>position[i]</TT>. * It is designed to export the axis to a LaTeX source code, * and to use <SPAN CLASS="logo,LaTeX">L<SUP><SMALL>A</SMALL></SUP>T<SMALL>E</SMALL>X</SPAN>/TikZ commands to write prettier characters; it has no * effect on the chart appearance displayed with <TT>XYChart.view()</TT>. * * @param position label series position on the axis. * * @param label label series name on the axis. * * */ public void setLabels(double[] position, String[] label) { if (label.length != position.length) throw new IllegalArgumentException("A label is required for each given position"); this.labelsName = label.clone(); this.labelsValue = position.clone(); double max = max(position); if (axis.getUpperBound() < max) axis.setUpperBound(max); double min = min(position); if (axis.getLowerBound() > min) axis.setLowerBound(min); labelsFlag = true; } /** * Returns the drawing position parameter (default equals 0). * * @return drawing position parameter. * */ public double getTwinAxisPosition() { return this.twinAxisPosition; } /** * Defines where the opposite axis must be drawn on the current axis, * where it should appear, and on which label. * * @param position new drawing position. * * */ public void setTwinAxisPosition(double position) { this.twinAxisPosition = position; } /** * Formats and returns a string containing a <SPAN CLASS="logo,LaTeX">L<SUP><SMALL>A</SMALL></SUP>T<SMALL>E</SMALL>X</SPAN>-compatible * source code which represents this axis and its parameters. * * @return LaTeX source code in a String. * @param scale current axis wished scale. * * */ public String toLatex(double scale) { Formatter formatter = new Formatter(Locale.US); double maxAxis = Math.max(axis.getRange().getUpperBound(), twinAxisPosition); //valeur du label le plus grand double minAxis = Math.min(axis.getRange().getLowerBound(), twinAxisPosition); //valeur du label le plus petit String precisionAffichageLabel; //determine combien de decimales seront affichees pour les labels double pas = axis.getTickUnit().getSize(); //step d'affichage des labels pas = fixStep(minAxis, maxAxis, pas); int puissDix; //echelle des valeurs sur l'axe if (Math.log10(pas) < 0) puissDix = (int) Math.log10(pas) - 1; else puissDix = (int) Math.log10(pas); //Placement des fleches, on pourrait facilement les rendre personnalisables... String arrowLeftType = "latex"; String arrowRightType = "latex"; String arrowLeftMargin = "3mm"; String arrowRightMargin = "3mm"; if (twinAxisPosition == minAxis) { arrowLeftType = ""; arrowLeftMargin = "0mm"; } else if (twinAxisPosition == maxAxis) { arrowRightType = ""; arrowRightMargin = "0mm"; } // Label pour l'axe, avec eventuellement l'echelle String label = axis.getLabel(); if (label == null) label = " "; if (puissDix < -2 || puissDix > 2) label = label + " $(10^{" + puissDix + "})$"; else puissDix = 0; if (orientation) { //on est sur l'axe des abscisses //affichage de l'axe formatter.format("\\draw [%s-%s] ([xshift=-%s] %s,0) -- ([xshift=%s] %s,0) node[right] {%s};%n", arrowLeftType, arrowRightType, arrowLeftMargin, (minAxis - twinAxisPosition) * scale, arrowRightMargin, (maxAxis - twinAxisPosition) * scale, label); if (labelsFlag) { //labels manuels String name; double value; double labelTemp; for (int i = 0; i < labelsValue.length; i++) { value = labelsValue[i]; if (labelsName == null) { labelTemp = (value * 100.0 / Math.pow(10, puissDix)); // if(labelTemp == (int)(labelTemp)) name = Integer.toString((int) (labelTemp / 100.0)); // else // name = Double.toString(Math.round(labelTemp)/100.0); } else name = labelsName[i]; if (value == twinAxisPosition && tick0Flag) formatter.format("\\draw (%s,00) -- +(0mm,1mm) -- +(0mm,-1mm) node[below right] {%s};%n", (value - twinAxisPosition) * scale, name); else formatter.format("\\draw (%s,0) -- +(0mm,1mm) -- +(0mm,-1mm) node[below] {%s};%n", (value - twinAxisPosition) * scale, name); } } else { //labels automatiques double labelTemp; double k = twinAxisPosition; labelTemp = (Math.round(k * 100 / Math.pow(10, puissDix))) / 100.0; if (labelTemp == (int) (labelTemp)) precisionAffichageLabel = "0"; else if (labelTemp * 10 == (int) (labelTemp * 10)) precisionAffichageLabel = "1"; else precisionAffichageLabel = "2"; if (tick0Flag) formatter.format("\\draw (%s,0) -- +(0mm,1mm) -- +(0mm,-1mm) node[below right] {%." + precisionAffichageLabel + "f};%n", (k - twinAxisPosition) * scale, labelTemp); else formatter.format("\\draw (%s,0) -- +(0mm,1mm) -- +(0mm,-1mm) node[below] {%." + precisionAffichageLabel + "f};%n", (k - twinAxisPosition) * scale, labelTemp); k += pas; while (k <= maxAxis) { //cote positif labelTemp = (Math.round(k * 100 / Math.pow(10, puissDix))) / 100.0; if (labelTemp == (int) (labelTemp)) precisionAffichageLabel = "0"; else if (labelTemp * 10 == (int) (labelTemp * 10)) precisionAffichageLabel = "1"; else precisionAffichageLabel = "2"; formatter.format("\\draw (%s,0) -- +(0mm,1mm) -- +(0mm,-1mm) node[below] {%." + precisionAffichageLabel + "f};%n", (k - twinAxisPosition) * scale, labelTemp); k += pas; } k = twinAxisPosition - pas; while (k >= minAxis) { //cote negatif labelTemp = (Math.round(k * 100 / Math.pow(10, puissDix))) / 100.0; if (labelTemp == (int) (labelTemp)) precisionAffichageLabel = "0"; else if (labelTemp * 10 == (int) (labelTemp * 10)) precisionAffichageLabel = "1"; else precisionAffichageLabel = "2"; formatter.format("\\draw (%s,0) -- +(0mm,1mm) -- +(0mm,-1mm) node[below] {%." + precisionAffichageLabel + "f};%n", (k - twinAxisPosition) * scale, labelTemp); k -= pas; } } } else { //On est sur l'axe des ordonnees //affichage de l'axe formatter.format("\\draw [%s-%s] ([yshift=-%s] 0,%s) -- ([yshift=%s] 0, %s) node[above] {%s};%n", arrowLeftType, arrowRightType, arrowLeftMargin, (minAxis - twinAxisPosition) * scale, arrowRightMargin, (maxAxis - twinAxisPosition) * scale, label); if (labelsFlag) { //labels manuels String name; double value; double labelTemp; for (int i = 0; i < labelsValue.length; i++) { value = labelsValue[i]; if (labelsName == null) { labelTemp = (value * scale * 100 / Math.pow(10, puissDix)); if (labelTemp == (int) (labelTemp)) name = Integer.toString((int) (labelTemp / 100.0)); else name = Double.toString(Math.round(labelTemp) / 100.0); } else name = labelsName[i]; if (value == twinAxisPosition && tick0Flag) formatter.format("\\draw (%s,%s) -- +(1mm,0mm) -- +(-1mm,0mm) node[above left] {%s};%n", 0, (value - twinAxisPosition) * scale, name); else formatter.format("\\draw (%s,%s) -- +(1mm,0mm) -- +(-1mm,0mm) node[left] {%s};%n", 0, (value - twinAxisPosition) * scale, name); } } else { //Les labels automatiques double k = twinAxisPosition; double labelTemp; labelTemp = (Math.round(k * 100 / Math.pow(10, puissDix))) / 100.0; if (labelTemp == (int) (labelTemp)) precisionAffichageLabel = "0"; else if (labelTemp * 10 == (int) (labelTemp * 10)) precisionAffichageLabel = "1"; else precisionAffichageLabel = "2"; if (tick0Flag) formatter.format("\\draw (0,%s) -- +(1mm,0mm) -- +(-1mm,0mm) node[above left] {%." + precisionAffichageLabel + "f};%n", (k - twinAxisPosition) * scale, labelTemp); else formatter.format("\\draw (0,%s) -- +(1mm,0mm) -- +(-1mm,0mm) node[left] {%." + precisionAffichageLabel + "f};%n", (k - twinAxisPosition) * scale, labelTemp); k += pas; while (k <= maxAxis) { //cote positif de l'axe labelTemp = (Math.round(k * 100 / Math.pow(10, puissDix))) / 100.0; if (labelTemp == (int) (labelTemp)) precisionAffichageLabel = "0"; else if (labelTemp * 10 == (int) (labelTemp * 10)) precisionAffichageLabel = "1"; else precisionAffichageLabel = "2"; formatter.format("\\draw (0,%s) -- +(1mm,0mm) -- +(-1mm,0mm) node[left] {%." + precisionAffichageLabel + "f};%n", (k - twinAxisPosition) * scale, labelTemp); k += pas; } k = twinAxisPosition - pas; while (k >= minAxis) { //cote negatif de l'axe labelTemp = (Math.round(k * 100 / Math.pow(10, puissDix))) / 100.0; if (labelTemp == (int) (labelTemp)) precisionAffichageLabel = "0"; else if (labelTemp * 10 == (int) (labelTemp * 10)) precisionAffichageLabel = "1"; else precisionAffichageLabel = "2"; formatter.format("\\draw (0,%s) -- +(1mm,0mm) -- +(-1mm,0mm) node[left] {%." + precisionAffichageLabel + "f};%n", (k - twinAxisPosition) * scale, labelTemp); k -= pas; } } } return formatter.toString(); } private double computeAutoTickValue() { // Calcule le pas d'affichage par defaut des labels double pas; double[] bounds = new double[2]; bounds[0] = Math.min(axis.getRange().getLowerBound(), twinAxisPosition); bounds[1] = Math.max(axis.getRange().getUpperBound(), twinAxisPosition); if (bounds[1] - bounds[0] >= 1) { int nbMaxDeFois = (int) (bounds[1] - bounds[0]); int puissDix = (int) Math.log10(nbMaxDeFois); nbMaxDeFois = (int) (nbMaxDeFois / Math.pow(10, puissDix)) + 1; if (nbMaxDeFois > 5) pas = Math.pow(10, puissDix); else if (nbMaxDeFois > 3) pas = 0.5 * Math.pow(10, puissDix); else if (nbMaxDeFois > 1) pas = 0.25 * Math.pow(10, puissDix); else //nbMaxDeFois==1 pas = 0.1 * Math.pow(10, puissDix); } else { double nbMaxDeFois = bounds[1] - bounds[0]; int puissDix = 0; while (nbMaxDeFois < 1) { nbMaxDeFois *= 10; puissDix++; } if (nbMaxDeFois > 5) pas = 1 / Math.pow(10, puissDix); else if (nbMaxDeFois > 3) pas = 0.5 / Math.pow(10, puissDix); else if (nbMaxDeFois > 1) pas = 0.3 / Math.pow(10, puissDix); else //nbMaxDeFois==1 pas = 0.1 / Math.pow(10, puissDix); } return pas; } private static double max(double[] t) { if (t == null) throw new IllegalArgumentException("max: null argument."); double aux = t[0]; for (int i = 1; i < t.length; i++) if (t[i] > aux) aux = t[i]; return aux; } private static double min(double[] t) { if (t == null) throw new IllegalArgumentException("min: null argument."); double aux = t[0]; for (int i = 1; i < t.length; i++) if (t[i] < aux) aux = t[i]; return aux; } void setTick0Flag(boolean flag) { tick0Flag = flag; } }