All Downloads are FREE. Search and download functionalities are using the official Maven repository.

jdplus.toolkit.desktop.plugin.ui.chart3d.functions.JFunctions2DChart Maven / Gradle / Ivy

/*
 * Copyright 2013 National Bank of Belgium
 *
 * Licensed under the EUPL, Version 1.1 or – as soon they will be approved 
 * by the European Commission - subsequent versions of the EUPL (the "Licence");
 * You may not use this work except in compliance with the Licence.
 * You may obtain a copy of the Licence at:
 * 
 * http://ec.europa.eu/idabc/eupl
 * 
 * Unless required by applicable law or agreed to in writing, software 
 * distributed under the Licence is distributed on an "AS IS" basis,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the Licence for the specific language governing permissions and 
 * limitations under the Licence.
 */
package jdplus.toolkit.desktop.plugin.ui.chart3d.functions;

import jdplus.toolkit.base.api.data.DoubleSeq;
import jdplus.toolkit.desktop.plugin.components.TimeSeriesComponent;
import jdplus.toolkit.desktop.plugin.components.parts.*;
import jdplus.toolkit.desktop.plugin.components.tools.JChartPanel;
import jdplus.main.desktop.design.SwingComponent;
import jdplus.toolkit.desktop.plugin.jfreechart.BasicXYDataset;
import jdplus.toolkit.desktop.plugin.jfreechart.TsCharts;
import jdplus.toolkit.base.api.timeseries.TsInformationType;
import ec.util.chart.ColorScheme;
import ec.util.chart.ColorScheme.KnownColor;
import ec.util.chart.swing.Charts;
import ec.util.chart.swing.SwingColorSchemeSupport;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.geom.Ellipse2D;
import javax.swing.JComponent;
import jdplus.toolkit.base.core.data.DataBlock;
import jdplus.toolkit.base.core.math.functions.IFunction;
import jdplus.toolkit.base.core.math.functions.IFunctionPoint;
import jdplus.toolkit.base.core.math.functions.IParametersDomain;
import nbbrd.design.SkipProcessing;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.axis.NumberAxis;
import org.jfree.chart.block.LineBorder;
import org.jfree.chart.plot.DatasetRenderingOrder;
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer;
import org.jfree.chart.title.LegendTitle;
import org.jfree.ui.RectangleEdge;
import org.jfree.ui.RectangleInsets;

/**
 * 2D Chart panel used when the likelihood function contains only 1 parameter
 *
 * @author Mats Maggi
 */
@SwingComponent
@SkipProcessing(target = SwingComponent.class, reason = "parameters in constructor")
public final class JFunctions2DChart extends JComponent implements TimeSeriesComponent, HasTs, HasColorScheme {

    private final JChartPanel panel;
    private final JFreeChart chart;
    private final IFunction function;
    private final IFunctionPoint maxFunction;
    private float epsilon = 0.2f;
    private int steps;
    private final XYLineAndShapeRenderer functionRenderer;
    private final XYLineAndShapeRenderer optimumRenderer;

    @lombok.experimental.Delegate
    private final HasTs m_ts = HasTsSupport.of(this::firePropertyChange, TsInformationType.Data);
    
    @lombok.experimental.Delegate
    private final HasColorScheme colorScheme = HasColorSchemeSupport.of(this::firePropertyChange);

    private final HasColorSchemeResolver colorSchemeResolver = new HasColorSchemeResolver(colorScheme, this::onColorSchemeChange);

    public JFunctions2DChart(IFunction f, IFunctionPoint maxF, int steps) {
        super();

        setLayout(new BorderLayout());

        function = f;
        maxFunction = maxF;

        if (steps < ConfigurationToolBar.MIN_STEPS || steps > ConfigurationToolBar.MAX_STEPS) {
            throw new IllegalArgumentException("Number of steps must be between "
                    + ConfigurationToolBar.MIN_STEPS + " and " + ConfigurationToolBar.MAX_STEPS + " !");
        }

        this.steps = steps;

        SwingColorSchemeSupport themeSupport = colorSchemeResolver.resolve();
        
        functionRenderer = new XYLineAndShapeRenderer(true, false);
        functionRenderer.setAutoPopulateSeriesPaint(false);
        functionRenderer.setBasePaint(themeSupport.getLineColor(KnownColor.BLUE));

        optimumRenderer = new XYLineAndShapeRenderer(false, true);
        optimumRenderer.setAutoPopulateSeriesPaint(false);
        optimumRenderer.setAutoPopulateSeriesShape(false);
        optimumRenderer.setBaseShape(new Ellipse2D.Double(-2, -2, 4, 4));
        optimumRenderer.setBasePaint(themeSupport.getLineColor(KnownColor.RED));
        optimumRenderer.setBaseShapesFilled(true);

        panel = new JChartPanel(null);
        chart = createChart();

        enableProperties();
    }

    private void enableProperties() {
        this.addPropertyChangeListener(evt -> {
            switch (evt.getPropertyName()) {
                case HasColorScheme.COLOR_SCHEME_PROPERTY:
                    onColorSchemeChange();
                    break;
            }
        });
    }

    /**
     * Generates the data and displays it in the chart. Calculates the points
     * ("steps" total points) and the optimum of the function
     */
    public void generateData() {
        if (function == null || maxFunction == null) {
            throw new IllegalArgumentException("The given functions can't be null !");
        }

        BasicXYDataset dataset = new BasicXYDataset();
        BasicXYDataset optimumDataset = new BasicXYDataset();
        double[] dataX = new double[steps];
        double[] dataY = new double[steps];

        final DoubleSeq parameters = maxFunction.getParameters();
        DataBlock p = DataBlock.of(parameters);
        final IParametersDomain d = function.getDomain();

        float xMin = ((float) p.get(0) - epsilon);
        double dMin = d.lbound(0);
        if (Double.isFinite(dMin) && xMin < dMin) {
            xMin = (float) dMin;
        }
        float xMax = ((float) p.get(0) + epsilon);
        double dMax = d.ubound(0);
        if (Double.isFinite(dMax) && xMax > dMax) {
            xMax = (float) dMax;
        }
        float stepX = (xMax - xMin) / (steps - 1);    // Calculates the "distance" between each point

        // Optimum point of the max likelihood function
        double optiX = parameters.get(0);
        double optiY = maxFunction.getValue();

        for (int i = 0; i < steps; i++) {
            // Value on the x axis (min X value + index* (distance between points)
            float x = xMin + i * stepX;
            float y = Float.NaN;
            p.set(0, x);    // Setting new value of the 1st param (X)

            // Calculating the Y value
            try {
                if (d.checkBoundaries(p)) {
                    y = (float) function.evaluate(p).getValue();
                }
            } catch (Exception err) {
                y = Float.NaN;
            }

            if (Float.isInfinite(y)) {
                y = Float.NaN;
            }

            dataX[i] = x;
            dataY[i] = y;
        }

        // Creates the 2 datasets (function + optimum point)
        BasicXYDataset.Series serie = BasicXYDataset.Series.of("f(" + d.getDescription(0) + ")", dataX, dataY);
        BasicXYDataset.Series optimum = BasicXYDataset.Series.of("Optimum", new double[]{optiX}, new double[]{optiY});
        dataset.addSeries(serie);
        optimumDataset.addSeries(optimum);

        XYPlot plot = chart.getXYPlot();
        configureAxis(plot);
        plot.setDataset(0, dataset);
        plot.setDataset(1, optimumDataset);

        panel.setChart(chart);
        add(panel, BorderLayout.CENTER);

        onColorSchemeChange();
    }

    private void configureAxis(XYPlot plot) {
        NumberAxis xAxis = new NumberAxis();
        xAxis.setAutoRange(true);
        xAxis.setAutoRangeIncludesZero(false);
        plot.setDomainAxis(xAxis);

        NumberAxis yAxis = new NumberAxis();
        yAxis.setAutoRange(true);
        yAxis.setAutoRangeIncludesZero(false);
        plot.setRangeAxis(yAxis);
    }

    private JFreeChart createChart() {
        XYPlot plot = new XYPlot();

        plot.setDataset(0, Charts.emptyXYDataset());
        plot.setRenderer(0, functionRenderer);
        plot.mapDatasetToDomainAxis(0, 0);
        plot.mapDatasetToRangeAxis(0, 0);

        plot.setDataset(1, Charts.emptyXYDataset());
        plot.setRenderer(1, optimumRenderer);
        plot.mapDatasetToDomainAxis(1, 0);
        plot.mapDatasetToRangeAxis(1, 0);

        plot.setDatasetRenderingOrder(DatasetRenderingOrder.FORWARD);

        JFreeChart result = new JFreeChart("", TsCharts.CHART_TITLE_FONT, plot, false);

        LegendTitle legend = new LegendTitle(result.getPlot());
        legend.setMargin(new RectangleInsets(1.0, 1.0, 1.0, 1.0));
        legend.setFrame(new LineBorder());
        legend.setBackgroundPaint(Color.white);
        legend.setPosition(RectangleEdge.BOTTOM);
        result.addLegend(legend);

        result.setPadding(TsCharts.CHART_PADDING);
        return result;
    }

    /**
     * Sets the value of the epsilon parameter. Epsilon = range of the function
     * (X values from max-eps to max+eps)
     *
     * @param eps
     */
    public void setEpsilon(float eps) {
        if (eps < ConfigurationToolBar.MIN_EPS) {
            throw new IllegalArgumentException("Epsilon must be greater than"
                    + ConfigurationToolBar.MIN_EPS + " !");
        }
//        if (eps < ConfigurationToolBar.MIN_EPS || eps > ConfigurationToolBar.MAX_EPS) {
//            throw new IllegalArgumentException("Epsilon must be between " 
//                    + ConfigurationToolBar.MIN_EPS + " and " + ConfigurationToolBar.MAX_EPS + " !");
//        }
        epsilon = eps;
        generateData();
    }

    /**
     * Sets the number of points to calculate and display the function
     *
     * @param steps
     */
    public void setSteps(int steps) {
        if (steps < ConfigurationToolBar.MIN_STEPS || steps > ConfigurationToolBar.MAX_STEPS) {
            throw new IllegalArgumentException("Number of steps must be between "
                    + ConfigurationToolBar.MIN_STEPS + " and " + ConfigurationToolBar.MAX_STEPS + " !");
        }
        this.steps = steps;
        generateData();
    }

    private void onColorSchemeChange() {
        SwingColorSchemeSupport themeSupport = colorSchemeResolver.resolve();

        functionRenderer.setBasePaint(themeSupport.getLineColor(ColorScheme.KnownColor.BLUE));
        optimumRenderer.setBasePaint(themeSupport.getLineColor(KnownColor.RED));

        XYPlot mainPlot = chart.getXYPlot();
        mainPlot.setBackgroundPaint(themeSupport.getPlotColor());
        mainPlot.setDomainGridlinePaint(themeSupport.getGridColor());
        mainPlot.setRangeGridlinePaint(themeSupport.getGridColor());
        chart.setBackgroundPaint(themeSupport.getBackColor());
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy