org.earthtime.UPb_Redux.dateInterpretation.WeightedMeanGraphPanel.java Source code

Java tutorial

Introduction

Here is the source code for org.earthtime.UPb_Redux.dateInterpretation.WeightedMeanGraphPanel.java

Source

/*
 * WeightedMeanGraphPanel.java
 *
 * Created on June 9, 2008, 3:08 PM
 *
 *
 * Copyright 2006-2015 James F. Bowring and www.Earth-Time.org
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */
package org.earthtime.UPb_Redux.dateInterpretation;

import java.awt.AlphaComposite;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Composite;
import java.awt.Cursor;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.Shape;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.font.TextLayout;
import java.awt.geom.AffineTransform;
import java.awt.geom.Line2D;
import java.awt.geom.Path2D;
import java.awt.geom.Rectangle2D;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.UnsupportedEncodingException;
import java.io.Writer;
import java.math.BigDecimal;
import java.text.DecimalFormat;
import java.text.NumberFormat;
import java.util.Collections;
import java.util.Comparator;
import java.util.Map;
import java.util.Random;
import java.util.Vector;
import javax.swing.JPanel;
import org.apache.batik.apps.rasterizer.SVGConverter;
import org.apache.batik.apps.rasterizer.SVGConverterException;
import org.apache.batik.dom.GenericDOMImplementation;
import org.apache.batik.svggen.SVGGraphics2D;
import org.apache.batik.svggen.SVGGraphics2DIOException;
import org.apache.commons.math.special.Gamma;
import org.earthtime.UPb_Redux.ReduxConstants;
import org.earthtime.UPb_Redux.aliquots.UPbReduxAliquot;
import org.earthtime.UPb_Redux.dateInterpretation.concordia.PlottingDetailsDisplayInterface;
import org.earthtime.UPb_Redux.fractions.Fraction;
import org.earthtime.UPb_Redux.samples.Sample;
import org.earthtime.UPb_Redux.user.SampleDateInterpretationGUIOptions;
import org.earthtime.UPb_Redux.utilities.comparators.IntuitiveStringComparator;
import org.earthtime.UPb_Redux.valueModels.SampleDateModel;
import org.earthtime.dataDictionaries.MSWDCoordinates;
import org.earthtime.utilities.TicGeneratorForAxes;
import org.w3c.dom.DOMImplementation;
import org.w3c.dom.Document;

/**
 *
 * @author James F. Bowring
 */
public class WeightedMeanGraphPanel extends JPanel
        implements MouseListener, MouseMotionListener, PlottingDetailsDisplayInterface {

    // Class Variables
    private int graphWidth;
    private int graphHeight;
    private int topMargin = 1;//5;
    private int leftMargin = 15;
    // Instance Variables
    private Sample sample;
    private Object[][] selectedSampleDateModels;
    private double minX;
    private double maxX;
    private double minY;
    private double maxY;
    private double displayOffsetY;
    private double displayOffsetX;
    private double minT;
    private double maxT;
    private int zoomMinX;
    private int zoomMinY;
    private int zoomMaxX;
    private int zoomMaxY;
    private Cursor weightedMeanCursor;
    private Map<String, String> weightedMeanOptions;
    //    private boolean inRandomMode;
    // PAN, ZOOM, EDIT
    private String imageMode;
    private final NumberFormat formatter2DecPlaces = new DecimalFormat("#####0.00");
    private final NumberFormat formatter1DecPlace = new DecimalFormat("#####0.0");

    /**
     * Creates a new instance of WeightedMeanGraphPanel
     *
     * @param sample
     */
    public WeightedMeanGraphPanel(Sample sample) {
        super();

        this.sample = sample;

        setOpaque(true);

        setBackground(Color.white);

        graphWidth = 775;
        graphHeight = 600;

        selectedSampleDateModels = new Object[0][0];

        weightedMeanCursor = Cursor.getDefaultCursor();
        imageMode = "PAN";
        //        setInRandomMode( false );
        weightedMeanOptions = new SampleDateInterpretationGUIOptions().getWeightedMeanOptions();

        addMouseListener(this);
        addMouseMotionListener(this);
    }

    /**
     *
     * @param g
     */
    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);

        paint((Graphics2D) g);
    }

    private double mapX(double x, double minX, double rangeX, int widthG) {

        return (((x - minX) / rangeX) * (double) widthG) + (double) getLeftMargin();
    }

    private double mapY(double y, double maxY, double rangeY, int heightG) {

        return (((maxY - y) / rangeY) * (double) heightG) + (double) getTopMargin();
    }

    /**
     *
     * @param g2d
     */
    public void paint(Graphics2D g2d) {

        // setup painting parameters
        String fractionSortOrder = "name"; //random, weight, date
        if (getWeightedMeanOptions().containsKey("fractionSortOrder")) {
            fractionSortOrder = getWeightedMeanOptions().get("fractionSortOrder");
        }

        double rangeX = (getMaxX_Display() - getMinX_Display());
        double rangeY = (getMaxY_Display() - getMinY_Display());

        g2d.setClip(getLeftMargin(), getTopMargin(), getGraphWidth(), getGraphHeight());
        RenderingHints rh = g2d.getRenderingHints();
        rh.put(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        rh.put(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
        g2d.setRenderingHints(rh);

        // walk the sampleDateInterpretations and produce graphs
        g2d.setPaint(Color.BLACK);
        g2d.setStroke(new BasicStroke(2.0f));
        g2d.setFont(new Font("SansSerif", Font.BOLD, 10));

        double barWidth = 15.0;
        double barGap = 10.0;
        double startSamX = 10.0;
        double saveStartSamX = 0.0;
        double samSpace = 3.0;

        for (int i = 0; i < selectedSampleDateModels.length; i++) {
            for (int j = 1; j < 9; j++) {
                if (selectedSampleDateModels[i][j] instanceof SampleDateModel) {

                    final SampleDateModel SAM = ((SampleDateModel) selectedSampleDateModels[i][j]);
                    double wMean = SAM.getValue().movePointLeft(6).doubleValue();
                    double wMeanOneSigma = SAM.getOneSigmaAbs().movePointLeft(6).doubleValue();

                    Path2D mean = new Path2D.Double(Path2D.WIND_NON_ZERO);

                    // july 2008
                    // modified to show de-selected fractions as gray
                    // this means a new special list of fractionIDs is created fromall non-rejected fractions
                    // and each instance is tested for being included
                    // should eventually refactor
                    Vector<String> allFIDs = new Vector<String>();
                    for (String f : ((UPbReduxAliquot) SAM.getAliquot()).getAliquotFractionIDs()) {
                        // test added for Sample-based wm
                        if (SAM.fractionDateIsPositive(//
                                ((UPbReduxAliquot) SAM.getAliquot()).getAliquotFractionByName(f))) {
                            allFIDs.add(f);
                        }
                    }

                    final int iFinal = i;
                    if (fractionSortOrder.equalsIgnoreCase("weight")) {
                        Collections.sort(allFIDs, new Comparator<String>() {

                            public int compare(String fID1, String fID2) {
                                double invertOneSigmaF1 = //
                                        1.0 //
                                                / ((UPbReduxAliquot) selectedSampleDateModels[iFinal][0])//
                                                        .getAliquotFractionByName(fID1)//
                                                        .getRadiogenicIsotopeDateByName(SAM.getDateName())//
                                                        .getOneSigmaAbs().movePointLeft(6).doubleValue();
                                double invertOneSigmaF2 = //
                                        1.0 //
                                                / ((UPbReduxAliquot) selectedSampleDateModels[iFinal][0])//
                                                        .getAliquotFractionByName(fID2)//
                                                        .getRadiogenicIsotopeDateByName(SAM.getDateName())//
                                                        .getOneSigmaAbs().movePointLeft(6).doubleValue();

                                return Double.compare(invertOneSigmaF2, invertOneSigmaF1);
                            }
                        });
                    } else if (fractionSortOrder.equalsIgnoreCase("date")) {
                        Collections.sort(allFIDs, new Comparator<String>() {

                            public int compare(String fID1, String fID2) {
                                double dateF1 = //
                                        ((UPbReduxAliquot) selectedSampleDateModels[iFinal][0])//
                                                .getAliquotFractionByName(fID1)//
                                                .getRadiogenicIsotopeDateByName(SAM.getDateName())//
                                                .getValue().doubleValue();
                                double dateF2 = //
                                        ((UPbReduxAliquot) selectedSampleDateModels[iFinal][0])//
                                                .getAliquotFractionByName(fID2)//
                                                .getRadiogenicIsotopeDateByName(SAM.getDateName())//
                                                .getValue().doubleValue();

                                return Double.compare(dateF1, dateF2);
                            }
                        });
                    } else if ( /* ! isInRandomMode() &&*/fractionSortOrder.equalsIgnoreCase("random")) {
                        Collections.shuffle(allFIDs, new Random());
                    } else if (fractionSortOrder.equalsIgnoreCase("name")) {
                        // default to alphabetic by name
                        //Collections.sort(allFIDs);
                        // april 2010 give same lexigraphic ordering that UPbFractions get
                        Collections.sort(allFIDs, new IntuitiveStringComparator<String>());
                    } else {
                        // do nothing
                    }

                    double actualWidthX = (allFIDs.size()) * (barWidth + barGap);//; + barGap;

                    // plot 2-sigma of mean
                    mean.moveTo((float) mapX(startSamX, getMinX_Display(), rangeX, graphWidth),
                            (float) mapY(wMean + 2.0 * wMeanOneSigma, getMaxY_Display(), rangeY, graphHeight));
                    mean.lineTo((float) mapX(startSamX + actualWidthX, getMinX_Display(), rangeX, graphWidth),
                            (float) mapY(wMean + 2.0 * wMeanOneSigma, getMaxY_Display(), rangeY, graphHeight));
                    mean.lineTo((float) mapX(startSamX + actualWidthX, getMinX_Display(), rangeX, graphWidth),
                            (float) mapY(wMean - 2.0 * wMeanOneSigma, getMaxY_Display(), rangeY, graphHeight));
                    mean.lineTo((float) mapX(startSamX, getMinX_Display(), rangeX, graphWidth),
                            (float) mapY(wMean - 2.0 * wMeanOneSigma, getMaxY_Display(), rangeY, graphHeight));
                    mean.closePath();

                    g2d.setColor(ReduxConstants.mySampleYellowColor);
                    g2d.fill(mean);
                    g2d.setPaint(Color.BLACK);

                    // plot 1-sigma of mean
                    mean.reset();
                    mean.moveTo((float) mapX(startSamX, getMinX_Display(), rangeX, graphWidth),
                            (float) mapY(wMean + wMeanOneSigma, getMaxY_Display(), rangeY, graphHeight));
                    mean.lineTo((float) mapX(startSamX + actualWidthX, getMinX_Display(), rangeX, graphWidth),
                            (float) mapY(wMean + wMeanOneSigma, getMaxY_Display(), rangeY, graphHeight));
                    mean.lineTo((float) mapX(startSamX + actualWidthX, getMinX_Display(), rangeX, graphWidth),
                            (float) mapY(wMean - wMeanOneSigma, getMaxY_Display(), rangeY, graphHeight));
                    mean.lineTo((float) mapX(startSamX, getMinX_Display(), rangeX, graphWidth),
                            (float) mapY(wMean - wMeanOneSigma, getMaxY_Display(), rangeY, graphHeight));
                    mean.closePath();

                    g2d.setColor(ReduxConstants.ColorOfRedux);
                    g2d.fill(mean);
                    g2d.setPaint(Color.BLACK);

                    // plot mean
                    mean.reset();
                    mean.moveTo((float) mapX(startSamX, getMinX_Display(), rangeX, graphWidth),
                            (float) mapY(wMean, getMaxY_Display(), rangeY, graphHeight));
                    mean.lineTo((float) mapX(startSamX + actualWidthX, getMinX_Display(), rangeX, graphWidth),
                            (float) mapY(wMean, getMaxY_Display(), rangeY, graphHeight));
                    g2d.setStroke(new BasicStroke(1.0f));
                    g2d.draw(mean);
                    g2d.setStroke(new BasicStroke(2.0f));

                    saveStartSamX = startSamX;

                    // plot fraction bars
                    double minPoint = 5000.0;
                    double maxWeight = 0.0;
                    double totalWeight = 0.0;

                    int barNum = 0;

                    for (String fID : allFIDs) {
                        // the dateModel has an associated aliquot, but in sample mode, it is a
                        // standin aliquot for the sample.  to get the aliquot number for
                        // use in coloring fractions, we need to query the fraction itself
                        String aliquotName = sample.getAliquotNameByFractionID(fID);

                        Color includedFillColor = new Color(0, 0, 0);
                        if (sample.getSampleDateInterpretationGUISettings().getAliquotOptions().get(aliquotName)
                                .containsKey("includedFillColor")) {
                            String[] temp = //
                                    sample.getSampleDateInterpretationGUISettings().getAliquotOptions()
                                            .get(aliquotName).get("includedFillColor").split(",");
                            includedFillColor = buildRGBColor(temp);
                        }

                        Fraction f = ((UPbReduxAliquot) selectedSampleDateModels[i][0])
                                .getAliquotFractionByName(fID);

                        double date = f.//((UPbReduxAliquot) selectedSampleDateModels[i][0]).getAliquotFractionByName(fID).//
                                getRadiogenicIsotopeDateByName(SAM.getDateName()).getValue().movePointLeft(6)
                                .doubleValue();
                        double twoSigma = f.//((UPbReduxAliquot) selectedSampleDateModels[i][0]).getAliquotFractionByName(fID).//
                                getRadiogenicIsotopeDateByName(SAM.getDateName()).getTwoSigmaAbs().movePointLeft(6)
                                .doubleValue();

                        if ((date - twoSigma) < minPoint) {
                            minPoint = (date - twoSigma);
                        }

                        double invertedOneSigma = //
                                1.0 //
                                        / f.//((UPbReduxAliquot) selectedSampleDateModels[i][0]).getAliquotFractionByName(fID).//
                                                getRadiogenicIsotopeDateByName(SAM.getDateName()).getOneSigmaAbs()
                                                .movePointLeft(6).doubleValue();

                        if (invertedOneSigma > maxWeight) {
                            maxWeight = invertedOneSigma;
                        }

                        Path2D bar = new Path2D.Double(Path2D.WIND_NON_ZERO);
                        bar.moveTo(
                                (float) mapX(saveStartSamX + ((barGap / 2.0) + barNum * (barWidth + barGap)),
                                        getMinX_Display(), rangeX, graphWidth),
                                (float) mapY(date + twoSigma, getMaxY_Display(), rangeY, graphHeight));
                        bar.lineTo(
                                (float) mapX(
                                        saveStartSamX + ((barGap / 2.0) + barNum * (barWidth + barGap)) + barWidth,
                                        getMinX_Display(), rangeX, graphWidth),
                                (float) mapY(date + twoSigma, getMaxY_Display(), rangeY, graphHeight));
                        bar.lineTo(
                                (float) mapX(
                                        saveStartSamX + ((barGap / 2.0) + barNum * (barWidth + barGap)) + barWidth,
                                        getMinX_Display(), rangeX, graphWidth),
                                (float) mapY(date - twoSigma, getMaxY_Display(), rangeY, graphHeight));
                        bar.lineTo(
                                (float) mapX(saveStartSamX + ((barGap / 2.0) + barNum * (barWidth + barGap)),
                                        getMinX_Display(), rangeX, graphWidth),
                                (float) mapY(date - twoSigma, getMaxY_Display(), rangeY, graphHeight));
                        bar.closePath();

                        Composite originalComposite = g2d.getComposite();

                        if (SAM.getIncludedFractionIDsVector().contains(fID)) {
                            g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.8f));
                            totalWeight += Math.pow(invertedOneSigma, 2.0);

                            // april 2014 experiment
                            if (f.getRgbColor() != 0) {
                                includedFillColor = new Color(f.getRgbColor());
                            }

                        } else {
                            g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.2f));
                        }

                        g2d.setPaint(includedFillColor);

                        g2d.draw(bar);
                        //restore composite
                        g2d.setComposite(originalComposite);

                        g2d.setColor(Color.black);

                        // label fraction at top
                        g2d.rotate(-Math.PI / 4.0,
                                (float) mapX(saveStartSamX + ((barGap / 2.0) + barNum * (barWidth + barGap)),
                                        getMinX_Display(), rangeX, graphWidth),
                                (float) mapY(date + twoSigma, getMaxY_Display(), rangeY, graphHeight));

                        g2d.drawString(
                                ((UPbReduxAliquot) selectedSampleDateModels[i][0]).getAliquotFractionByName(fID)
                                        .getFractionID(),
                                (float) mapX(saveStartSamX + ((barGap / 2.0) + barNum * (barWidth + barGap)),
                                        getMinX_Display(), rangeX, graphWidth) + 15,
                                (float) mapY(date + twoSigma, getMaxY_Display(), rangeY, graphHeight));

                        g2d.rotate(Math.PI / 4.0,
                                (float) mapX(saveStartSamX + ((barGap / 2.0) + barNum * (barWidth + barGap)),
                                        getMinX_Display(), rangeX, graphWidth),
                                (float) mapY(date + twoSigma, getMaxY_Display(), rangeY, graphHeight));

                        barNum++;
                        // startSamX += 2 * barWidth;
                        startSamX += barWidth + barGap;
                    }

                    // display three info boxes below weighted means
                    // each tic is the height of one calculated y-axis tic
                    // determine the y axis tic
                    double minYtic = Math.ceil(getMinY_Display() * 100) / 100;
                    double maxYtic = Math.floor(getMaxY_Display() * 100) / 100;
                    double deltay = Math.rint((maxYtic - minYtic) * 10 + 0.5);
                    double yTic = deltay / 100;

                    double yTopSummary = minPoint - yTic / 2.0;// wMeanOneSigma;
                    //double specialYTic = yTic;
                    double yTopWeights = yTopSummary - yTic * 1.1;
                    double yTopMSWD_PDF = yTopWeights - yTic * 1.1;

                    // summary box
                    Path2D box = new Path2D.Double(Path2D.WIND_NON_ZERO);
                    box.moveTo((float) mapX(saveStartSamX, getMinX_Display(), rangeX, graphWidth),
                            (float) mapY(yTopSummary, getMaxY_Display(), rangeY, graphHeight));
                    box.lineTo((float) mapX(saveStartSamX + actualWidthX, getMinX_Display(), rangeX, graphWidth),
                            (float) mapY(yTopSummary, getMaxY_Display(), rangeY, graphHeight));
                    box.lineTo((float) mapX(saveStartSamX + actualWidthX, getMinX_Display(), rangeX, graphWidth),
                            (float) mapY(yTopSummary - yTic, getMaxY_Display(), rangeY, graphHeight));
                    box.lineTo((float) mapX(saveStartSamX, getMinX_Display(), rangeX, graphWidth),
                            (float) mapY(yTopSummary - yTic, getMaxY_Display(), rangeY, graphHeight));
                    box.closePath();

                    g2d.setStroke(new BasicStroke(1.5f));
                    g2d.draw(box);

                    // Info Box
                    g2d.drawString(//
                            SAM.getAliquot().getAliquotName(),
                            (float) mapX(saveStartSamX, getMinX_Display(), rangeX, graphWidth) + 4f,
                            (float) mapY(yTopSummary, getMaxY_Display(), rangeY, graphHeight) + 13f);
                    g2d.drawString(//
                            SAM.getName(), (float) mapX(saveStartSamX, getMinX_Display(), rangeX, graphWidth) + 4f,
                            (float) mapY(yTopSummary, getMaxY_Display(), rangeY, graphHeight) + 25f);
                    g2d.drawString(//
                            SAM.FormatValueAndTwoSigmaABSThreeWaysForPublication(6, 2),
                            (float) mapX(saveStartSamX, getMinX_Display(), rangeX, graphWidth) + 4f,
                            (float) mapY(yTopSummary, getMaxY_Display(), rangeY, graphHeight) + 36f);
                    g2d.drawString(//
                            SAM.ShowCustomMSWDwithN(),
                            (float) mapX(saveStartSamX, getMinX_Display(), rangeX, graphWidth) + 4f,
                            (float) mapY(yTopSummary, getMaxY_Display(), rangeY, graphHeight) + 48f);

                    // weights box
                    box.reset();
                    box.moveTo((float) mapX(saveStartSamX, getMinX_Display(), rangeX, graphWidth),
                            (float) mapY(yTopWeights, getMaxY_Display(), rangeY, graphHeight));
                    box.lineTo((float) mapX(saveStartSamX + actualWidthX, getMinX_Display(), rangeX, graphWidth),
                            (float) mapY(yTopWeights, getMaxY_Display(), rangeY, graphHeight));
                    box.lineTo((float) mapX(saveStartSamX + actualWidthX, getMinX_Display(), rangeX, graphWidth),
                            (float) mapY(yTopWeights - yTic, getMaxY_Display(), rangeY, graphHeight));
                    box.lineTo((float) mapX(saveStartSamX, getMinX_Display(), rangeX, graphWidth),
                            (float) mapY(yTopWeights - yTic, getMaxY_Display(), rangeY, graphHeight));
                    box.closePath();

                    g2d.setStroke(new BasicStroke(1.5f));
                    g2d.draw(box);

                    // plot fraction weights
                    double artificialXRange = allFIDs.size();
                    double count = 0;
                    //double weightWidth = Math.min(3.0 * barWidth, (yTic / rangeY * graphHeight)) - 15;//yTic;//barWidth * 2.0;
                    double weightWidth = (barWidth + barGap) * 0.9;

                    for (String fID : allFIDs) {
                        // the dateModel has an associated aliquot, but in sample mode, it is a
                        // standin aliquot for the sample.  to get the aliquot number for
                        // use in coloring fractions, we need to query the fraction itself
                        String aliquotName = sample.getAliquotNameByFractionID(fID);

                        Fraction f = ((UPbReduxAliquot) selectedSampleDateModels[i][0])
                                .getAliquotFractionByName(fID);

                        Color includedFillColor = new Color(0, 0, 0);
                        if (sample.getSampleDateInterpretationGUISettings().getAliquotOptions().get(aliquotName)
                                .containsKey("includedFillColor")) {
                            String[] temp = //
                                    sample.getSampleDateInterpretationGUISettings().getAliquotOptions()
                                            .get(aliquotName).get("includedFillColor").split(",");
                            includedFillColor = buildRGBColor(temp);
                        }

                        double invertOneSigma = //
                                1.0 //
                                        / ((UPbReduxAliquot) selectedSampleDateModels[i][0])
                                                .getAliquotFractionByName(fID)//
                                                .getRadiogenicIsotopeDateByName(SAM.getDateName()).getOneSigmaAbs()
                                                .movePointLeft(6).doubleValue();

                        Path2D weight = new Path2D.Double(Path2D.WIND_NON_ZERO);
                        weight.moveTo(
                                (float) mapX(saveStartSamX + (count + 0.5) / artificialXRange * actualWidthX,
                                        getMinX_Display(), rangeX, graphWidth) //
                                        - (float) (invertOneSigma / maxWeight / 2.0 * weightWidth),
                                (float) mapY(yTopWeights - (yTic / 2.0), getMaxY_Display(), rangeY, graphHeight) //
                                        + (float) (invertOneSigma / maxWeight / 2.0 * weightWidth) - 5f);

                        weight.lineTo(
                                (float) mapX(saveStartSamX + (count + 0.5) / artificialXRange * actualWidthX,
                                        getMinX_Display(), rangeX, graphWidth) //
                                        + (float) (invertOneSigma / maxWeight / 2.0 * weightWidth),
                                (float) mapY(yTopWeights - (yTic / 2.0), getMaxY_Display(), rangeY, graphHeight) //
                                        + (float) (invertOneSigma / maxWeight / 2.0 * weightWidth) - 5f);

                        weight.lineTo(
                                (float) mapX(saveStartSamX + (count + 0.5) / artificialXRange * actualWidthX,
                                        getMinX_Display(), rangeX, graphWidth) //
                                        + (float) (invertOneSigma / maxWeight / 2.0 * weightWidth),
                                (float) mapY(yTopWeights - (yTic / 2.0), getMaxY_Display(), rangeY, graphHeight) //
                                        - (float) (invertOneSigma / maxWeight / 2.0 * weightWidth) - 5f);

                        weight.lineTo(
                                (float) mapX(saveStartSamX + (count + 0.5) / artificialXRange * actualWidthX,
                                        getMinX_Display(), rangeX, graphWidth) //
                                        - (float) (invertOneSigma / maxWeight / 2.0 * weightWidth),
                                (float) mapY(yTopWeights - (yTic / 2.0), getMaxY_Display(), rangeY, graphHeight) //
                                        - (float) (invertOneSigma / maxWeight / 2.0 * weightWidth) - 5f);

                        weight.closePath();

                        g2d.setStroke(new BasicStroke(2.5f));

                        // test for included or not == black or gray
                        String weightPerCent = "   0";//0.0%";

                        //                        g2d.setPaint(includedFillColor);
                        Composite originalComposite = g2d.getComposite();

                        if (SAM.getIncludedFractionIDsVector().contains(fID)) {
                            g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.8f));
                            weightPerCent = formatter1DecPlace
                                    .format(Math.pow(invertOneSigma, 2.0) / totalWeight * 100.0);// + "%";

                            // april 2014 experiment
                            if (f.getRgbColor() != 0) {
                                includedFillColor = new Color(f.getRgbColor());
                            }
                        } else {
                            g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.2f));
                        }

                        g2d.setPaint(includedFillColor);

                        g2d.fill(weight);
                        //restore composite
                        g2d.setComposite(originalComposite);

                        // write percent of total weight
                        g2d.drawString(weightPerCent,
                                (float) mapX(saveStartSamX + (count + 0.5) / artificialXRange * actualWidthX,
                                        getMinX_Display(), rangeX, graphWidth) //
                                        - (float) (invertOneSigma / maxWeight / 2.0 * weightWidth),
                                (float) mapY(yTopWeights - yTic, getMaxY_Display(), rangeY, graphHeight) - 5f);
                        g2d.setColor(Color.black);

                        count += 1.0;

                    }

                    // double box height for graph
                    yTic *= 2.0;

                    // plot MSWD_PDF
                    // store  function x,y values
                    Vector<Double> xVals = new Vector<Double>();
                    Vector<Double> yVals = new Vector<Double>();

                    double f = SAM.getIncludedFractionIDsVector().size() - 1;
                    if (f > 1.0) {
                        g2d.setStroke(new BasicStroke(1.0f));

                        double yRange = MSWDCoordinates.valuesByPointCount[(int) f][5] * 1.03; // alitle air at the top of curve
                        double xStart = MSWDCoordinates.valuesByPointCount[(int) f][1];
                        double xRange = MSWDCoordinates.valuesByPointCount[(int) f][4] - xStart;
                        double xStep = 0.005;

                        Path2D MSWD_PDF = new Path2D.Double(Path2D.WIND_NON_ZERO);
                        Path2D MSWD_right = new Path2D.Double(Path2D.WIND_NON_ZERO);

                        // start at lower left corner of box  (may or may not be 0,0 )
                        MSWD_PDF.moveTo(//
                                (float) mapX((double) saveStartSamX, getMinX_Display(), rangeX, graphWidth),
                                (float) mapY(yTopMSWD_PDF - yTic, getMaxY_Display(), rangeY, graphHeight));

                        // setup MSWD to paint last
                        Path2D MSWD = null;

                        // calculate function values
                        for (double x = xStart; x < xRange; x += xStep) {
                            xVals.add((((x - xStart) / xRange) * actualWidthX) + (double) saveStartSamX);

                            double y = //
                                    Math.pow(2, -1.0 * f / 2.0)//
                                            * Math.exp(-1.0 * f * x / 2.0)//
                                            * Math.pow(f, f / 2.0)//
                                            * Math.pow(x, (-1.0 + f / 2.0))//
                                            / Math.exp(Gamma.logGamma(f / 2.0));

                            yVals.add(((y / yRange) * yTic) + yTopMSWD_PDF - yTic);

                            MSWD_PDF.lineTo(//
                                    (float) mapX(xVals.lastElement(), getMinX_Display(), rangeX, graphWidth),
                                    (float) mapY(yVals.lastElement(), getMaxY_Display(), rangeY, graphHeight));

                            // test for location of left RED zone
                            if ((MSWDCoordinates.valuesByPointCount[(int) f][2] >= x)
                                    && (MSWDCoordinates.valuesByPointCount[(int) f][2] < (x + xStep))) {

                                double leftX = MSWDCoordinates.valuesByPointCount[(int) f][2];
                                xVals.add((((leftX - xStart) / xRange) * actualWidthX) + (double) saveStartSamX);

                                double leftY = //
                                        Math.pow(2, -1.0 * f / 2.0)//
                                                * Math.exp(-1.0 * f * leftX / 2.0)//
                                                * Math.pow(f, f / 2.0)//
                                                * Math.pow(leftX, (-1.0 + f / 2.0))//
                                                / Math.exp(Gamma.logGamma(f / 2.0));
                                yVals.add(((leftY / yRange) * yTic) + yTopMSWD_PDF - yTic);

                                MSWD_PDF.lineTo(//
                                        (float) mapX(xVals.lastElement(), getMinX_Display(), rangeX, graphWidth),
                                        (float) mapY(yVals.lastElement(), getMaxY_Display(), rangeY, graphHeight));

                                Path2D ciLower = new Path2D.Double(Path2D.WIND_NON_ZERO);
                                ciLower.append(MSWD_PDF.getPathIterator(new AffineTransform()), true);

                                ciLower.lineTo(//
                                        (float) mapX(xVals.lastElement(), getMinX_Display(), rangeX, graphWidth),
                                        (float) mapY(yTopMSWD_PDF - yTic, getMaxY_Display(), rangeY, graphHeight));
                                ciLower.closePath();
                                g2d.setColor(Color.RED);
                                g2d.fill(ciLower);

                                // draw right hand border line to compensate for a bug in the filler
                                Line2D right = new Line2D.Double(//
                                        mapX(xVals.lastElement(), getMinX_Display(), rangeX, graphWidth),
                                        mapY(yVals.lastElement(), getMaxY_Display(), rangeY, graphHeight),
                                        mapX(xVals.lastElement(), getMinX_Display(), rangeX, graphWidth),
                                        mapY(yTopMSWD_PDF - yTic, getMaxY_Display(), rangeY, graphHeight));
                                g2d.setStroke(new BasicStroke(0.5f));
                                g2d.draw(right);
                                g2d.setStroke(new BasicStroke(1.0f));

                                g2d.setColor(Color.BLACK);

                                System.out.println("Left Red = (" + leftX + ", " + leftY + ")");
                            }

                            // test for location of right RED zone
                            if ((MSWDCoordinates.valuesByPointCount[(int) f][3] >= x)
                                    && (MSWDCoordinates.valuesByPointCount[(int) f][3] < (x + xStep))) {

                                double rightX = MSWDCoordinates.valuesByPointCount[(int) f][3];
                                xVals.add((((rightX - xStart) / xRange) * actualWidthX) + (double) saveStartSamX);

                                double rightY = //
                                        Math.pow(2, -1.0 * f / 2.0)//
                                                * Math.exp(-1.0 * f * rightX / 2.0)//
                                                * Math.pow(f, f / 2.0)//
                                                * Math.pow(rightX, (-1.0 + f / 2.0))//
                                                / Math.exp(Gamma.logGamma(f / 2.0));
                                yVals.add(((rightY / yRange) * yTic) + yTopMSWD_PDF - yTic);

                                MSWD_PDF.lineTo(//
                                        (float) mapX(xVals.lastElement(), getMinX_Display(), rangeX, graphWidth),
                                        (float) mapY(yVals.lastElement(), getMaxY_Display(), rangeY, graphHeight));

                                // here the strategy is to draw the curve and then reset it to record the remainder
                                g2d.setStroke(new BasicStroke(1.0f));
                                g2d.draw(MSWD_PDF);
                                MSWD_PDF = new Path2D.Double(Path2D.WIND_NON_ZERO);
                                MSWD_PDF.moveTo(//
                                        (float) mapX(xVals.lastElement(), getMinX_Display(), rangeX, graphWidth),
                                        (float) mapY(yVals.lastElement(), getMaxY_Display(), rangeY, graphHeight));

                                MSWD_right.moveTo(//
                                        (float) mapX(xVals.lastElement(), getMinX_Display(), rangeX, graphWidth),
                                        (float) mapY(yTopMSWD_PDF - yTic, getMaxY_Display(), rangeY, graphHeight));
                                MSWD_right.lineTo(//
                                        (float) mapX(xVals.lastElement(), getMinX_Display(), rangeX, graphWidth),
                                        (float) mapY(yVals.lastElement(), getMaxY_Display(), rangeY, graphHeight));

                                System.out.println("Right Red = (" + rightX + ", " + rightY + ")");

                            }

                            // test for location of MSWD AND paint last
                            if ((SAM.getMeanSquaredWeightedDeviation().doubleValue() >= x)
                                    && (SAM.getMeanSquaredWeightedDeviation().doubleValue() < (x + xStep))) {
                                MSWD = new Path2D.Double(Path2D.WIND_NON_ZERO);
                                MSWD.moveTo(//
                                        (float) mapX(xVals.lastElement(), getMinX_Display(), rangeX, graphWidth),
                                        (float) mapY(yTopMSWD_PDF - yTic, getMaxY_Display(), rangeY, graphHeight));
                                MSWD.lineTo(//
                                        (float) mapX(xVals.lastElement(), getMinX_Display(), rangeX, graphWidth),
                                        (float) mapY(yVals.lastElement(), getMaxY_Display(), rangeY, graphHeight));

                            }
                        }
                        g2d.setStroke(new BasicStroke(1.0f));
                        // merge with border of right RED and fill
                        MSWD_right.append(MSWD_PDF.getPathIterator(new AffineTransform()), true);
                        g2d.setColor(Color.RED);
                        g2d.fill(MSWD_right);
                        g2d.setColor(Color.BLACK);
                        // draw the remaining curves
                        g2d.draw(MSWD_PDF);
                        // MSWD may be off the graph and hence not exist
                        try {
                            g2d.draw(MSWD);
                        } catch (Exception e) {
                        }

                        // label 95% conf interval and MSWD
                        g2d.drawString(//
                                "95% CI: ("
                                        + formatter2DecPlaces.format(MSWDCoordinates.valuesByPointCount[(int) f][2])
                                        + ", "
                                        + formatter2DecPlaces.format(MSWDCoordinates.valuesByPointCount[(int) f][3])
                                        + ")",
                                (float) mapX(saveStartSamX + (actualWidthX / 2.0), getMinX_Display(), rangeX,
                                        graphWidth) - 30f,
                                (float) mapY(yTopMSWD_PDF, getMaxY_Display(), rangeY, graphHeight) + 15f);

                        // determine if MSWD is out of range
                        String mswdAlert = "";
                        if (SAM.getMeanSquaredWeightedDeviation()
                                .doubleValue() > MSWDCoordinates.valuesByPointCount[(int) f][4]) {
                            mswdAlert = "\n !Out of Range!";
                        }
                        g2d.drawString(//
                                "MSWD = "
                                        + formatter2DecPlaces
                                                .format(SAM.getMeanSquaredWeightedDeviation().doubleValue())
                                        + ", n = " + (int) (f + 1) + mswdAlert,
                                (float) mapX(saveStartSamX + (actualWidthX / 2.0), getMinX_Display(), rangeX,
                                        graphWidth) - 15f,
                                (float) mapY(yTopMSWD_PDF, getMaxY_Display(), rangeY, graphHeight) + 30f);

                    } else {
                        g2d.drawString("need more data...",
                                (float) mapX((double) saveStartSamX, getMinX_Display(), rangeX, graphWidth) + 4f,
                                (float) mapY(yTopMSWD_PDF - yTic, getMaxY_Display(), rangeY, graphHeight) - 10f);
                    }

                    // MSWD_PDF box
                    box.reset();
                    box.moveTo((float) mapX(saveStartSamX, getMinX_Display(), rangeX, graphWidth),
                            (float) mapY(yTopMSWD_PDF, getMaxY_Display(), rangeY, graphHeight));
                    box.lineTo((float) mapX(saveStartSamX + actualWidthX, getMinX_Display(), rangeX, graphWidth),
                            (float) mapY(yTopMSWD_PDF, getMaxY_Display(), rangeY, graphHeight));
                    box.lineTo((float) mapX(saveStartSamX + actualWidthX, getMinX_Display(), rangeX, graphWidth),
                            (float) mapY(yTopMSWD_PDF - yTic, getMaxY_Display(), rangeY, graphHeight));
                    box.lineTo((float) mapX(saveStartSamX, getMinX_Display(), rangeX, graphWidth),
                            (float) mapY(yTopMSWD_PDF - yTic, getMaxY_Display(), rangeY, graphHeight));
                    box.closePath();

                    g2d.setStroke(new BasicStroke(1.5f));
                    g2d.draw(box);

                    // MSWD_PDF x-axis tics
                    if (f > 1.0) {
                        g2d.setStroke(new BasicStroke(1.0f));
                        double xStart = (MSWDCoordinates.valuesByPointCount[(int) f][1] <= 0.5) ? 0.5 : 1.0;
                        double xRange = MSWDCoordinates.valuesByPointCount[(int) f][4]
                                - MSWDCoordinates.valuesByPointCount[(int) f][1];
                        double xStep = 0.5;

                        for (double x = xStart; x < xRange; x += xStep) {
                            double xPlot = (((x - MSWDCoordinates.valuesByPointCount[(int) f][1]) / xRange)
                                    * actualWidthX) + (double) saveStartSamX;
                            Line2D line = new Line2D.Double(mapX(xPlot, getMinX_Display(), rangeX, graphWidth),
                                    mapY(yTopMSWD_PDF - yTic, getMaxY_Display(), rangeY, graphHeight),
                                    mapX(xPlot, getMinX_Display(), rangeX, graphWidth),
                                    mapY(yTopMSWD_PDF - yTic, getMaxY_Display(), rangeY, graphHeight) + 7);
                            g2d.draw(line);

                            g2d.rotate(-Math.PI / 2.0, (float) mapX(xPlot, getMinX_Display(), rangeX, graphWidth),
                                    (float) mapY(yTopMSWD_PDF - yTic, getMaxY_Display(), rangeY, graphHeight));
                            g2d.drawString(formatter1DecPlace.format(x),
                                    (float) mapX(xPlot, getMinX_Display(), rangeX, graphWidth) - 30f,
                                    (float) mapY(yTopMSWD_PDF - yTic, getMaxY_Display(), rangeY, graphHeight) + 5f);
                            g2d.rotate(Math.PI / 2.0, (float) mapX(xPlot, getMinX_Display(), rangeX, graphWidth),
                                    (float) mapY(yTopMSWD_PDF - yTic, getMaxY_Display(), rangeY, graphHeight));
                        }
                    }

                    // set counters
                    barNum += samSpace;
                    startSamX += 2 * samSpace * barWidth;
                }
            }
        }
        //        // prevents re-randomization
        //        setInRandomMode( true );

        drawAxesAndTicks(g2d, rangeX, rangeY);

        // draw zoom box if in use
        if ((Math.abs(zoomMaxX - zoomMinX) * Math.abs(zoomMinY - zoomMaxY)) > 0.0) {
            g2d.setStroke(new BasicStroke(2.0f));
            g2d.setColor(Color.red);
            g2d.drawRect(//
                    Math.min(zoomMinX, zoomMaxX), Math.min(zoomMaxY, zoomMinY), Math.abs(zoomMaxX - zoomMinX),
                    Math.abs(zoomMinY - zoomMaxY));
        }
    }

    private Color buildRGBColor(String[] rgbComponents) {
        return new Color((int) Integer.parseInt(rgbComponents[0].trim()),
                (int) Integer.parseInt(rgbComponents[1].trim()), (int) Integer.parseInt(rgbComponents[2].trim()));
    }

    private void drawAxesAndTicks(Graphics2D g2d, double rangeX, double rangeY) {

        // oct 2014 new tic logic
        // reset the clip bounds to paint axis and numbers
        g2d.setClip(0, 0, getWidth(), getHeight());

        g2d.setFont(new Font("Monospaced", Font.BOLD, 14));
        g2d.setPaint(Color.BLACK);
        g2d.setStroke(new BasicStroke(2.0f));

        // determine the axis ticks
        BigDecimal[] tics = TicGeneratorForAxes.generateTics(getMinY_Display(), getMaxY_Display(), 12);
        // trap for bad plot
        if (tics.length <= 1) {
            tics = new BigDecimal[0];
        }
        double minXDisplay = 0.0;
        int yAxisTicWidth = 8;
        int yTicLabelFrequency = 2;
        int labeledTicCountYAxis = 0;

        g2d.setPaint(Color.black);
        for (int i = 0; i < tics.length; i++) {

            double y = tics[i].doubleValue();

            if ((y > getMinY_Display()) // dont print across mappedX axis
                    && (y < getMaxY_Display())) // dont print across top border
            {
                try {
                    Shape ticMark = new Line2D.Double( //
                            mapX(getMinX_Display(), getMinX_Display(), rangeX, graphWidth),
                            mapY(y, getMaxY_Display(), rangeY, graphHeight),
                            mapX(getMinX_Display(), getMinX_Display(), rangeX, graphWidth) + 7,
                            mapY(y, getMaxY_Display(), rangeY, graphHeight));
                    g2d.draw(ticMark);

                    String intString = "00000" + tics[i].toPlainString().replace(".", "");
                    int lastPlace = Integer.parseInt(intString.substring(intString.length() - 4));

                    if (lastPlace % yTicLabelFrequency == 0) {
                        if (labeledTicCountYAxis % yTicLabelFrequency == 0) {

                            TextLayout mLayout = //
                                    new TextLayout(tics[i].toPlainString(), g2d.getFont(),
                                            g2d.getFontRenderContext());

                            Rectangle2D bounds = mLayout.getBounds();

                            //if (isyAxisHorizontalTicLabels()) {
                            //                            g2d.drawString(tics[i].toPlainString(),//
                            //                                    (float) mapX(getMinX_Display(), getMinX_Display(), rangeX, graphWidth) - 4f,
                            //                                    (float) mapY(y, getMaxY_Display(), rangeY, graphHeight) + 30f);
                            //                            } else {
                            float yLabelCenterOffset = (float) mLayout.getBounds().getWidth() / 2f;

                            g2d.rotate(-Math.PI / 2.0,
                                    (float) mapX(getMinX_Display(), getMinX_Display(), rangeX, graphWidth) - 4f,
                                    (float) mapY(y, getMaxY_Display(), rangeY, graphHeight) + yLabelCenterOffset);
                            g2d.drawString(tics[i].toPlainString(),
                                    (float) mapX(getMinX_Display(), getMinX_Display(), rangeX, graphWidth) - 4f,
                                    (float) mapY(y, getMaxY_Display(), rangeY, graphHeight) + yLabelCenterOffset);
                            g2d.rotate(Math.PI / 2.0,
                                    (float) mapX(getMinX_Display(), getMinX_Display(), rangeX, graphWidth) - 4f,
                                    (float) mapY(y, getMaxY_Display(), rangeY, graphHeight) + yLabelCenterOffset);
                        }

                        labeledTicCountYAxis++;
                    } else {

                        if (labeledTicCountYAxis > 0) {
                            labeledTicCountYAxis++;
                        }
                    }
                } catch (Exception e) {
                }
            }
        }

        ////        // reset the clip bounds to paint axis and numbers
        ////        g2d.setClip(0, 0, getWidth(), getHeight());
        ////
        ////        g2d.setFont(new Font("Monospaced", Font.BOLD, 14));
        ////        g2d.setPaint(Color.BLACK);
        ////        g2d.setStroke(new BasicStroke(2.0f));
        ////
        ////        // determine the axis ticks
        ////        double minYtick = Math.ceil(getMinY_Display() * 100) / 100;
        ////        double maxYtick = Math.floor(getMaxY_Display() * 100) / 100;
        ////
        ////        int count = 0;
        ////        double deltay = Math.rint((maxYtick - minYtick) * 10 + 0.5);
        ////        double stepYtick = deltay / 100;
        ////
        ////        for (double y = minYtick; y
        ////                < maxYtick; y
        ////                += stepYtick) {
        ////            Line2D line = new Line2D.Double(
        ////                    mapX(getMinX_Display(), getMinX_Display(), rangeX, graphWidth),
        ////                    mapY(y, getMaxY_Display(), rangeY, graphHeight),
        ////                    mapX(getMinX_Display(), getMinX_Display(), rangeX, graphWidth) + 7,
        ////                    mapY(y, getMaxY_Display(), rangeY, graphHeight));
        ////            g2d.draw(line);
        ////
        ////            if ((count % 2) == 1) {
        ////                NumberFormat yFormat = null;
        ////                String temp = null;
        ////
        ////                yFormat
        ////                        = new DecimalFormat("0.00");
        ////                temp
        ////                        = yFormat.format(y);
        ////
        ////                g2d.setPaint(Color.black);
        ////                g2d.rotate(
        ////                        -Math.PI / 2.0,
        ////                        (float) mapX(getMinX_Display(), getMinX_Display(), rangeX, graphWidth) - 4f,
        ////                        (float) mapY(y, getMaxY_Display(), rangeY, graphHeight) + 30f);
        ////                g2d.drawString(
        ////                        temp,
        ////                        (float) mapX(getMinX_Display(), getMinX_Display(), rangeX, graphWidth) - 4f,
        ////                        (float) mapY(y, getMaxY_Display(), rangeY, graphHeight) + 30f);
        ////                g2d.rotate(
        ////                        Math.PI / 2.0,
        ////                        (float) mapX(getMinX_Display(), getMinX_Display(), rangeX, graphWidth) - 4f,
        ////                        (float) mapY(y, getMaxY_Display(), rangeY, graphHeight) + 30f);
        ////
        ////            }
        ////
        ////            count++;
        ////
        ////        }
        // draw and label axes
        g2d.setFont(new Font("Monospaced", Font.BOLD, 20));
        g2d.drawRect(getLeftMargin(), getTopMargin(), getGraphWidth() - 1, getGraphHeight() - 1);

    }

    /**
     *
     */
    public void refreshPanel() {
        preparePanel();
        repaint();
    }

    /**
     *
     */
    public void preparePanel() {

        zoomMaxX = 0;
        zoomMaxY = 0;
        zoomMinX = 0;
        zoomMinY = 0;

        setDisplayOffsetY(0.0);
        setDisplayOffsetX(0.0);

        this.removeAll();

        // walk selectedDateModels and get min and max of both sample date weighted mean graphs
        // X-axis is merely for layout, so map it to the size of panel
        setMinX(0.0);
        setMaxX(getGraphWidth());

        // Y-axis is going to be the age associated with the weighted means
        setMinY(ReduxConstants.MAX_DATE_ANNUM);//4.5E9 );
        setMaxY(0.0);

        for (int i = 0; i < selectedSampleDateModels.length; i++) {
            for (int j = 1; j < 9; j++) {
                if (selectedSampleDateModels[i][j] instanceof SampleDateModel) {

                    double wmMax = ((SampleDateModel) selectedSampleDateModels[i][j]).//
                            DetermineMaxDatePlusTwoSigma();
                    if (wmMax > maxY) {
                        setMaxY(wmMax);
                    }

                    double wmMin = ((SampleDateModel) selectedSampleDateModels[i][j]).//
                            DetermineMinDateLessTwoSigma();
                    if (wmMin < minY) {
                        setMinY(wmMin);
                    }

                }
            }
        }

        // convert to Ma
        setMinY(minY / 1.0E6);
        setMaxY(maxY / 1.0E6);

        // force margin Y
        double rangeY = (getMaxY_Display() - getMinY_Display());
        setMaxY(maxY + 0.15 * rangeY);

        // determine the y axis tic
        double minYtic = Math.ceil(getMinY_Display() * 100) / 100;
        double maxYtic = Math.floor(getMaxY_Display() * 100) / 100;
        double deltay = Math.rint((maxYtic - minYtic) * 10 + 0.5);
        double yTic = deltay / 100;
        setMinY((minY - 0.25 * rangeY) - 9 * yTic);

        //        setInRandomMode( false );
        //        System.out.println("Init " + minX + "  " + minY + "  " + maxX + "   " + maxY);
    }

    /**
     *
     * @param file
     */
    public void outputToSVG(File file) {

        // Get a DOMImplementation.
        DOMImplementation domImpl = GenericDOMImplementation.getDOMImplementation();

        // Create an instance of org.w3c.dom.Document.
        String svgNS = "http://www.w3.org/2000/svg";
        Document document = domImpl.createDocument(svgNS, "svg", null);

        // Create an instance of the SVG Generator.
        SVGGraphics2D svgGenerator = new SVGGraphics2D(document);

        // Ask the test to render into the SVG Graphics2D implementation.
        paint(svgGenerator);

        // Finally, stream out SVG to the standard output using
        // UTF-8 encoding.
        boolean useCSS = true; // we want to use CSS style attributes

        Writer out = null;
        try {
            out = new OutputStreamWriter(new FileOutputStream(file), "UTF-8");
        } catch (FileNotFoundException fileNotFoundException) {
        } catch (UnsupportedEncodingException unsupportedEncodingException) {
        }
        try {
            svgGenerator.stream(out, useCSS);
        } catch (SVGGraphics2DIOException sVGGraphics2DIOException) {
        }
    }

    /**
     *
     * @param file
     */
    public void outputToPDF(File file) {
        SVGConverter myConv = new SVGConverter();
        myConv.setDestinationType(org.apache.batik.apps.rasterizer.DestinationType.PDF);
        try {
            myConv.setSources(new String[] { file.getCanonicalPath() });

        } catch (IOException iOException) {
        }
        myConv.setWidth((float) getWidth() + 2);
        myConv.setHeight((float) getHeight() + 2);

        try {
            myConv.execute();

        } catch (SVGConverterException sVGConverterException) {
            System.out.println("Error in pdf conversion: " + sVGConverterException.getMessage());
        }

    }

    /**
     *
     * @return
     */
    public int getGraphWidth() {
        return graphWidth;
    }

    /**
     *
     * @param graphWidth
     */
    public void setGraphWidth(int graphWidth) {
        this.graphWidth = graphWidth;
    }

    /**
     *
     * @return
     */
    public int getGraphHeight() {
        return graphHeight;
    }

    /**
     *
     * @param graphHeight
     */
    public void setGraphHeight(int graphHeight) {
        this.graphHeight = graphHeight;
    }

    /**
     *
     * @return
     */
    public int getTopMargin() {
        return topMargin;
    }

    /**
     *
     * @param topMargin
     */
    public void setTopMargin(int topMargin) {
        this.topMargin = topMargin;
    }

    /**
     *
     * @return
     */
    public int getLeftMargin() {
        return leftMargin;
    }

    /**
     *
     * @param leftMargin
     */
    public void setLeftMargin(int leftMargin) {
        this.leftMargin = leftMargin;
    }

    /**
     *
     * @return
     */
    public double getMinX_Display() {
        return minX + displayOffsetX;
    }

    /**
     *
     * @param minX
     */
    public void setMinX(double minX) {
        this.minX = minX;
    }

    /**
     *
     * @return
     */
    public double getMaxX_Display() {
        return maxX + displayOffsetX;
    }

    /**
     *
     * @param maxX
     */
    public void setMaxX(double maxX) {
        this.maxX = maxX;
    }

    /**
     *
     * @return
     */
    public double getMinY_Display() {
        return minY + displayOffsetY;
    }

    /**
     *
     * @param minY
     */
    public void setMinY(double minY) {
        this.minY = minY;
    }

    /**
     *
     * @return
     */
    public double getMaxY_Display() {
        return maxY + displayOffsetY;
    }

    /**
     *
     * @param maxY
     */
    public void setMaxY(double maxY) {
        this.maxY = maxY;
    }

    /**
     *
     * @return
     */
    public double getDisplayOffsetY() {
        return displayOffsetY;
    }

    /**
     *
     * @param displayOffsetY
     */
    public void setDisplayOffsetY(double displayOffsetY) {
        //System.out.println("displayOffsetY  " + displayOffsetY);
        this.displayOffsetY = displayOffsetY;
    }

    /**
     *
     * @return
     */
    public double getDisplayOffsetX() {
        return displayOffsetX;
    }

    /**
     *
     * @param displayOffsetX
     */
    public void setDisplayOffsetX(double displayOffsetX) {
        //System.out.println("displayOffsetX  " + displayOffsetX);
        this.displayOffsetX = displayOffsetX;
    }

    /**
     *
     * @return
     */
    public double getRangeX() {
        return maxX - minX;
    }

    /**
     *
     * @return
     */
    public double getRangeY() {
        return maxY - minY;
    }

    /**
     *
     * @return
     */
    public double getRangeX_Display() {
        return getMaxX_Display() - getMinX_Display();
    }

    /**
     *
     * @return
     */
    public double getRangeY_Display() {
        return getMaxY_Display() - getMinY_Display();
    }

    /**
     *
     * @return
     */
    public double getMinX() {
        return minX;
    }

    /**
     *
     * @return
     */
    public double getMinY() {
        return minY;
    }

    /**
     *
     * @return
     */
    public double getMaxX() {
        return maxX;
    }

    /**
     *
     * @return
     */
    public double getMaxY() {
        return maxY;
    }

    /**
     *
     * @return
     */
    public double getMinT() {
        return minT;
    }

    /**
     *
     * @param minT
     */
    public void setMinT(double minT) {
        this.minT = minT;
    }

    /**
     *
     * @return
     */
    public double getMaxT() {
        return maxT;
    }

    /**
     *
     * @param maxT
     */
    public void setMaxT(double maxT) {
        this.maxT = maxT;
    }

    // Mouse events ************************************************************
    /**
     *
     * @param evt
     */
    public void mouseClicked(MouseEvent evt) {
    }

    /**
     *
     * @param evt
     */
    public void mousePressed(MouseEvent evt) {
        zoomMinX = evt.getX();
        zoomMinY = evt.getY();
    }

    /**
     *
     * @param evt
     */
    @Override
    public void mouseReleased(MouseEvent evt) {
        zoomMaxX = evt.getX();
        zoomMaxY = evt.getY();

        if (imageMode.equalsIgnoreCase("ZOOM") && (zoomMaxX != zoomMinX) && (zoomMaxY != zoomMinY)) {
            setMinX(convertMouseXToValue(Math.min(zoomMinX, zoomMaxX)));
            setMinY(convertMouseYToValue(Math.max(zoomMinY, zoomMaxY)));
            // hack to correct a bug in the reported x,y from mouse release vs mouse click
            setMaxX(convertMouseXToValue(Math.max(zoomMaxX, zoomMinX) - 65));
            setMaxY(convertMouseYToValue(Math.min(zoomMaxY, zoomMinY) + 60));

            setDisplayOffsetX(0.0);
            setDisplayOffsetY(0.0);

            zoomMaxX = zoomMinX;
            zoomMaxY = zoomMinY;

            repaint();
        }
    }

    /**
     *
     * @param evt
     */
    public void mouseEntered(MouseEvent evt) {
        // this is not very sensitive, so have forced cursor at mode selection below
        if ((evt.getX() >= getLeftMargin()) && (evt.getY() >= getTopMargin())
                && (evt.getY() <= graphHeight + getTopMargin())) {
            setCursor(getWeightedMeanCursor());
        }

    }

    /**
     *
     * @param evt
     */
    @Override
    public void mouseExited(MouseEvent evt) {
        // setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
    }

    /**
     *
     * @param evt
     */
    @Override
    public void mouseDragged(MouseEvent evt) {
        zoomMaxX = evt.getX();
        zoomMaxY = evt.getY();

        if (imageMode.equalsIgnoreCase("PAN")) {
            setDisplayOffsetX(getDisplayOffsetX() //
                    + (convertMouseXToValue(zoomMinX) - convertMouseXToValue(zoomMaxX)));
            setDisplayOffsetY(getDisplayOffsetY() //
                    + (convertMouseYToValue(zoomMinY) - convertMouseYToValue(zoomMaxY)));
            // System.out.println("move from " + zoomMinX + ", " + zoomMinY + "  to  " + zoomMaxX + ", " + zoomMaxY);
            zoomMinX = zoomMaxX;
            zoomMinY = zoomMaxY;
        }

        repaint();
    }

    /**
     *
     * @param evt
     */
    @Override
    public void mouseMoved(MouseEvent evt) {
    }

    private double convertMouseXToValue(int x) {
        return //
        ((x - getLeftMargin()) / (double) getGraphWidth()) //
                * getRangeX_Display()//
                + getMinX_Display();
    }

    private double convertMouseYToValue(int y) {
        return //
        (1.0 - ((double) (y - getTopMargin()) / getGraphHeight())) //
                * getRangeY_Display()//
                + getMinY_Display();
    }

    /**
     *
     * @return
     */
    public String getImageMode() {
        return imageMode;
    }

    /**
     *
     * @param imageMode
     */
    public void setImageMode(String imageMode) {
        this.imageMode = imageMode;
        if (imageMode.equalsIgnoreCase("PAN")) {
            weightedMeanCursor = new Cursor((Cursor.HAND_CURSOR));
        } else if (imageMode.equalsIgnoreCase("ZOOM")) {
            weightedMeanCursor = new Cursor((Cursor.CROSSHAIR_CURSOR));
        }
        // force issue
        setCursor(weightedMeanCursor);
    }

    /**
     *
     * @return
     */
    public Cursor getWeightedMeanCursor() {
        return weightedMeanCursor;
    }

    /**
     *
     * @param weightedMeanCursor
     */
    public void setWeightedMeanCursor(Cursor weightedMeanCursor) {
        this.weightedMeanCursor = weightedMeanCursor;
    }

    /**
     *
     * @return
     */
    public Object[][] getSelectedSampleDateModels() {
        return selectedSampleDateModels;
    }

    /**
     *
     * @param selectedSampleDateModels
     */
    public void setSelectedSampleDateModels(Object[][] selectedSampleDateModels) {
        this.selectedSampleDateModels = selectedSampleDateModels;
    }

    /**
     *
     * @return
     */
    public Map<String, String> getWeightedMeanOptions() {
        if (weightedMeanOptions == null) {
            weightedMeanOptions = new SampleDateInterpretationGUIOptions().getWeightedMeanOptions();
        }
        return weightedMeanOptions;
    }

    /**
     *
     * @param weightedMeanOptions
     */
    public void setWeightedMeanOptions(Map<String, String> weightedMeanOptions) {
        this.weightedMeanOptions = weightedMeanOptions;
    }

    //    /**
    //     * 
    //     * @return
    //     */
    //    public boolean isInRandomMode () {
    //        return inRandomMode;
    //    }
    //
    //    /**
    //     * 
    //     * @param inRandomMode
    //     */
    //    public void setInRandomMode ( boolean inRandomMode ) {
    //        this.inRandomMode = inRandomMode;
    //    }
    /**
     * @return the sample
     */
    public Sample getSample() {
        return sample;
    }

    /**
     * @param sample the sample to set
     */
    public void setSample(Sample sample) {
        this.sample = sample;
    }

    @Override
    public void resetPanel() {
        refreshPanel();
    }
}