/* 
 * This code is part of Project Waterloo from King's College London
 * 
 *
 * Copyright King's College London  2011-
 * 
 * @author Malcolm Lidierth, King's College London  [Contact]
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program.  If not, see .
 *
 */
package kcl.waterloo.graphics.plots2D.contour;

import java.awt.BasicStroke;
import java.awt.Paint;
import java.awt.geom.Area;
import java.awt.geom.Path2D;
import java.util.ArrayList;
import java.util.Set;
import java.util.TreeMap;
import kcl.waterloo.graphics.data.GJXYSeries;
import kcl.waterloo.graphics.plots2D.GJContour;
import kcl.waterloo.marker.ShapeUtils;
import kcl.waterloo.math.ArrayMath;
import kcl.waterloo.math.ArrayUtils;

/**
 * Class for representing contour data.
 *
 * @author Malcolm Lidierth, King's College London  [Contact]
 */
public class ContourExtra {

    /**
     * {@code LinkedHashMap} containing the raw data. The keySet contains the
     * level for each contour as a <@code Double>. The values are represented as
     * a {@code GJXYSeries} for each level representing the x-series and
     * y-series data.
     */
    private TreeMap> levelData = null;
    /**
     * {@code ContourSet} of objects representing the contours.
     */
    private final ContourSet contourSet = new ContourSet();
    /**
     * {@code Path2D.Double} objects representing the contours. Each element
     * represents the contour line for a single level. These are stored in a
     * sortable TreeMap such that contour lines for each level are collected
     * together and can be given the same stroke/color in the GJContour
     * paintPlot method. The Path2D objects here are clones of those in
     * contourSet with all contours at the same level appended together.
     */
    private final TreeMap compoundLines = new TreeMap();
    /**
     * Contours as SortedContours return sorted in the order in which they
     * should be rendered.
     */
    private final ArrayList sortedSet = new ArrayList();

    BasicStroke zeroStroke = null;
    private Paint positiveLineColor = null;
    private Paint negativeLineColor = null;

    public ContourExtra() {
    }

    public ArrayList sort() {
        if (sortedSet.isEmpty()) {
            ArrayList sortedList = contourSet.sort();
            Area accumulator = new Area();
            for (int k = 0; k < sortedList.size(); k++) {
                Contour contour = sortedList.get(k);
                int index = getColorIndex(contour.getLevel());
                if (k > 0 && k < sortedList.size() - 1) {
                    if (ShapeUtils.contains(sortedList.get(k - 1).getContourAsArea(), contour.getContourAsArea())) {
                        if (sortedList.get(k + 1).getLevel() > contour.getLevel()) {
                            index = getColorIndex(sortedList.get(k + 1).getLevel());
                        }
                    }
                }
                SortedContour sortedContour = new SortedContour(contour, index);
                if (k > 0 && !ShapeUtils.contains(accumulator, contour.getContourAsArea())
                        && ShapeUtils.intersects(accumulator, contour.getContourAsArea())) {
                    Area area2 = contour.getContourAsArea();
                    area2.subtract(accumulator);
                    sortedContour.setFillArea(area2);
                }
                sortedSet.add(sortedContour);
                accumulator.add(contour.getContourAsArea());
            }
        }
        return sortedSet;
        //}
    }

    /**
     * Returns the index into the fill & edgeColor arrays for a level.
     *
     * @param key the level to find the index for
     * @return integer index
     */
    public final int getColorIndex(Double key) {
        int k = 0;
        for (Double level : levelData.keySet()) {
            if (key.equals(level)) {
                return k;
            } else {
                k++;
            }
        }
        return -1;
    }

    /**
     * Returns the levels for the contours.
     *
     * @return the levels as a Double[]
     */
    public Double[] getLevels() {
        Double[] levels = new Double[levelData.size()];
        int k = 0;
        for (Double level : levelData.keySet()) {
            levels[k] = level;
            k++;
        }
        return levels;
    }

    /**
     *
     * @return the level data as a LinkedHashMap>
     */
    public final TreeMap> getLevelData() {
        return levelData;
    }

    /**
     * Sets the levels for this instance
     *
     * @param levels a TreeMap>
     */
    public final void setLevelData(TreeMap> levels) {
        levelData = levels;
        generateContours();
    }

    public final Area getCommonPath() {
        return contourSet.getCommonArea();
    }

    public final ArrayList getLevel(Double level) {
        return contourSet.get(level);
    }

    public final int size() {
        return levelData.size();
    }

    public final Set keySet() {
        return contourSet.keySet();
    }

    public final TreeMap getCompounds() {
        return compoundLines;
    }

    public final Path2D getCompound(Double level) {
        return (Path2D) compoundLines.get(level).clone();
    }

    public final void generateCompounds() {
        for (Double level : contourSet.keySet()) {
            Path2D path = new Path2D.Double();
            for (Contour c : contourSet.get(level)) {
                path.append(c.getPath(), false);
            }
            compoundLines.put(level, path);
        }
    }

    /**
     * Generates the contour lines from the data stored in {@code levelData}.
     *
     */
    public void generateContours() {
        contourSet.clear();
        for (Double thisLevel : getLevelData().keySet()) {
            ArrayList collection = getLevelData().get(thisLevel);
            for (GJXYSeries series : collection) {
                ArrayList x = series.getX();
                ArrayList y = series.getY();
                if (x.size() > 2) {
                    Double[] x0 = new Double[x.size()];
                    x0 = x.toArray(x0);
                    Double[] y0 = new Double[y.size()];
                    y0 = y.toArray(y0);
                    Contour contour = new Contour(x0, y0, thisLevel);
                    if (!contourSet.containsKey(thisLevel)) {
                        contourSet.put(thisLevel, new ArrayList());
                    }
                    contourSet.get(thisLevel).add(contour);
                }
            }
        }
        generateCompounds();
        sortedSet.clear();
        contourSet.setCommonArea();
    }

    /**
     * Generates the contour lines from the data stored in {@code levelData}.
     *
     *
     * @param plot the plot to generate contours for
     */
    public final void generateContours(final GJContour plot) {
        if (plot.getParentGraph() == null) {
            return;
        }
        double xoffset;
        if (plot.getXData().getDimension() <= 0) {
            xoffset = 0;
        } else {
            xoffset = plot.getXData().getRawDataValues()[0];
        }
        double yoffset;
        if (plot.getYData().getDimension() <= 0) {
            yoffset = 0;
        } else {
            yoffset = plot.getYData().getRawDataValues()[0];
        }
        if (getLevelData().keySet().size() > 0) {
            contourSet.clear();
            for (Double thisLevel : getLevelData().keySet()) {
                ArrayList collection = getLevelData().get(thisLevel);
                for (GJXYSeries series : collection) {
                    double[] x0 = ArrayUtils.asDouble(series.getX());
                    ArrayMath.addi(x0, xoffset);
                    x0 = plot.getParentGraph().getXTransform().getData(x0);
                    if (x0.length > 2) {
                        double[] y0 = ArrayUtils.asDouble(series.getY());
                        ArrayMath.addi(y0, yoffset);
                        y0 = plot.getParentGraph().getYTransform().getData(y0);
                        Contour contour = new Contour(x0, y0, thisLevel);
                        if (!contourSet.containsKey(thisLevel)) {
                            contourSet.put(thisLevel, new ArrayList());
                        }
                        contourSet.get(thisLevel).add(contour);
                    }
                }
            }
        }
        generateCompounds();
        contourSet.setCommonArea();
        sortedSet.clear();
    }

    public static ContourExtra createInstance() {
        ContourExtra contours = new ContourExtra();
        contours.levelData = new TreeMap>();
        return contours;
    }

    public void addContour(ArrayList> data, double level) {
        addContour(new GJXYSeries(data), level);
    }

    public void addContour(GJXYSeries data, double level) {
        if (getLevelData().get(level) == null) {
            getLevelData().put(level, new ArrayList());
        }
        getLevelData().get(level).add(data);

    }

    /**
     * Accepts a MATLAB-style contour matrix on input and returns a Waterloo
     * ContourExtra object.
     *
     * If a filled contour might be required, use the MATLAB {@code contourf}
     * function to generate the matrix.
     *
     * @param C MATLAB-style contour matrix
     * @return Waterloo contour object.
     */
    public static ContourExtra createFromMatrix(double[][] C) {
        ContourExtra contours = new ContourExtra();
        contours.levelData = new TreeMap>();
        int k = 0;
        double thisLevel;
        int thisN;
        double[][] data;
        while (k < C[0].length) {
            thisLevel = C[0][k];
            thisN = (int) C[1][k];
            if (thisN > 2) {
                data = new double[2][thisN];
                for (int n = 1; n <= thisN; n++) {
                    data[0][n - 1] = C[0][k + n];
                    data[1][n - 1] = C[1][k + n];
                }
                if (contours.getLevelData().get(thisLevel) == null) {
                    contours.getLevelData().put(thisLevel, new ArrayList());
                }
                contours.getLevelData().get(thisLevel).add(new GJXYSeries(data[0], data[1]));
            }
            k = k + thisN + 1;
        }
        return contours;
    }

    /**
     * @return the sortedSet
     */
    public ArrayList getSortedSet() {
        return sort();
    }

    public void setZeroStroke(BasicStroke s) {
        zeroStroke = s;
    }

    public BasicStroke getZeroStroke() {
        return zeroStroke;
    }

    /**
     * @return the positiveLineColor
     */
    public Paint getPositiveLineColor() {
        return positiveLineColor;
    }

    /**
     * @param positiveLineColor the positiveLineColor to set
     */
    public void setPositiveLineColor(Paint positiveLineColor) {
        this.positiveLineColor = positiveLineColor;
    }

    /**
     * @return the negativeLineColor
     */
    public Paint getNegativeLineColor() {
        return negativeLineColor;
    }

    /**
     * @param negativeLineColor the negativeLineColor to set
     */
    public void setNegativeLineColor(Paint negativeLineColor) {
        this.negativeLineColor = negativeLineColor;
    }

    /**
     *
     */
    public static final class SortedContour {

        private Contour contour = null;
        private int colorIndex = -1;
        private Area fillArea = null;

        private SortedContour(Contour contour, int index) {
            this.contour = contour;
            this.colorIndex = index;
            setFillArea(contour.getContourAsArea());
        }

        /**
         * @return the contour
         */
        public final Contour getContour() {
            return contour;
        }

        /**
         * @param contour the contour to set
         */
        public final void setContour(Contour contour) {
            this.contour = contour;
        }

        /**
         * @return the color
         */
        public final int getColorIndex() {
            return colorIndex;
        }

        /**
         * @param color the color to set
         */
        public final void setColorIndex(int index) {
            colorIndex = index;
        }

        /**
         * @return the fillArea
         */
        public final Area getFillArea() {
            return new Area(fillArea);//(Area) fillArea.clone();
        }

        /**
         * @param fillArea the fillArea to set
         */
        public final void setFillArea(Area fillArea) {
            this.fillArea = new Area(fillArea);//(Area) fillArea.clone();
        }
    }
}

 

Ads help maintain this website.