Skip to content

Auto range boundaries are not calculated correctly when using a fixed domain range and a FastXYSeries #45

@Jonathan727

Description

@Jonathan727

When using a FastXYSeries on a plot with fixed domain boundaries but auto range boundaries, the bounds are incorrectly calculated in SeriesUtils.

screenshot

Below is a modified version of SimpleXYPlotActivity which demonstrates the issue.


package com.androidplot.demos;

import android.app.Activity;
import android.graphics.Color;
import android.os.Bundle;

import com.androidplot.xy.BoundaryMode;
import com.androidplot.xy.FastXYSeries;
import com.androidplot.xy.LineAndPointFormatter;
import com.androidplot.xy.RectRegion;
import com.androidplot.xy.XYGraphWidget;
import com.androidplot.xy.XYPlot;
import com.google.common.math.Stats;

import java.text.DecimalFormat;
import java.util.ArrayList;

/**
 * A simple XYPlot
 */
public class SimpleXYPlotActivity extends Activity {

    private XYPlot plot;

    @Override
    public void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.simple_xy_plot_example);

        // initialize our XYPlot reference:
        plot = (XYPlot) findViewById(R.id.plot);

        // create a couple arrays of y-values to plot:
        final ArrayList<Integer> times = new ArrayList<>();
        final ArrayList<Integer> values = new ArrayList<>();


        for (int i = 0; i < 10; i++) {
            times.add(i);
            values.add(2 * i);
        }

        FastXYSeries fastXYSeries = new FastXYSeries(){

            @Override
            public int size() {
                return times.size();
            }

            @Override
            public Number getX(int index) {
                return times.get(index);
            }

            @Override
            public Number getY(int index) {
                return values.get(index);
            }

            @Override
            public String getTitle() {
                return "Isaac's crazy thing";
            }

            @Override
            public RectRegion minMax() {
                Stats yStats = Stats.of(values);
                return new RectRegion(
                        times.get(0),
                        times.get(times.size() - 1),
                        yStats.min(),
                        yStats.max()
                );
            }
        };

        // add a new series' to the xyplot:
        plot.addSeries(fastXYSeries, new LineAndPointFormatter(Color.RED, Color.BLUE, null, null));

        plot.setDomainLowerBoundary(5, BoundaryMode.FIXED);
        plot.setDomainUpperBoundary(9, BoundaryMode.FIXED);
        plot.getGraph().getLineLabelStyle(XYGraphWidget.Edge.BOTTOM).setFormat(new DecimalFormat());
    }
}

SeriesUtils.minMax will return bounds with a minimum Y and maximum Y both set to the maxY or the series.

public static RectRegion minMax(XYConstraints constraints, XYSeries... seriesArray) {

    final RectRegion bounds = new RectRegion();

    // make sure there is series data to iterate over:
    if (seriesArray != null && seriesArray.length > 0) {

        // iterate over each series
        for (XYSeries series : seriesArray) {

            // if this is an advanced xy series then minMax have already been calculated for us:
            if(series instanceof FastXYSeries) {
                final RectRegion seriesBounds = ((FastXYSeries) series).minMax();
                if (seriesBounds == null) {
                    continue;
                }
                if(constraints == null) {
                    bounds.union(seriesBounds);
                } else {
                    //the following condition will be false
                    if (constraints.contains(seriesBounds.getMinX(), seriesBounds.getMinY())) {
                        bounds.union(seriesBounds.getMinX(), seriesBounds.getMinY());
                    }
                    //the following condition will be true
                    if (constraints.contains(seriesBounds.getMaxX(), seriesBounds.getMaxY())) {
                        bounds.union(seriesBounds.getMaxX(), seriesBounds.getMaxY());
                    }
                }

            } else if (series.size() > 0) {
                for (int i = 0; i < series.size(); i++) {
                    final Number xi = series.getX(i);
                    final Number yi = series.getY(i);

                    // if constraints have been set, make sure this xy coordinate exists within them:
                    if (constraints == null || constraints.contains(xi, yi)) {
                        bounds.union(xi, yi);
                    }
                }
            }
        }
    }
    return bounds;
}

I've fixed it by writing a method which constrains the seriesBounds in my fork's branch. I'm not sure this is the right fix though, so I didn't make a pull request. My linked change puts data back on the screen but the y range still includes the full extents of the FastXYSeries instead of the constrained FastXYSeries. I wondered if there shouldn't be a method like Region yMinMax(Number xMin, Number xMax) to better calculate that or if I should just tell my implementations of FastXYSeries to consider the current boundary settings while calculating minmax. Thoughts?

Metadata

Metadata

Assignees

Labels

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions