Search code examples
androidmathandroidplot

Draw mathematical function with "androidPlot" library


I want to draw math-function like y=x^2+1 with androidPlot library.I have "SimpleXYPlot". It works but I don't know how to change it from sin to my function.

Here's the code:

   public class DynamicXYPlotActivity extends Activity {

// redraws a plot whenever an update is received:
private class MyPlotUpdater implements Observer {
    Plot plot;

    public MyPlotUpdater(Plot plot) {
        this.plot = plot;
    }

    @Override
    public void update(Observable o, Object arg) {
        plot.redraw();
    }
}

private XYPlot dynamicPlot;
private MyPlotUpdater plotUpdater;
SampleDynamicXYDatasource data;
private Thread myThread;

@Override
public void onCreate(Bundle savedInstanceState) {

    // android boilerplate stuff
    super.onCreate(savedInstanceState);
    setContentView(R.layout.dynamicxyplot_example);

    // get handles to our View defined in layout.xml:
    dynamicPlot = (XYPlot) findViewById(R.id.dynamicXYPlot);

    plotUpdater = new MyPlotUpdater(dynamicPlot);

    // only display whole numbers in domain labels
    dynamicPlot.getGraphWidget().setDomainValueFormat(new DecimalFormat("0"));

    // getInstance and position datasets:
    data = new SampleDynamicXYDatasource();
    SampleDynamicSeries sine1Series = new SampleDynamicSeries(data, 0, "Sine 1");
    SampleDynamicSeries sine2Series = new SampleDynamicSeries(data, 1, "Sine 2");

    LineAndPointFormatter formatter1 = new LineAndPointFormatter( Color.rgb(0, 0, 0), null, null, null );
    formatter1.getLinePaint().setStrokeJoin(Paint.Join.ROUND);
    formatter1.getLinePaint().setStrokeWidth(10);
    dynamicPlot.addSeries( sine1Series,formatter1 );

    LineAndPointFormatter formatter2 = new LineAndPointFormatter(Color.rgb(0, 0, 200), null, null, null);
    formatter2.getLinePaint().setStrokeWidth(10);
    formatter2.getLinePaint().setStrokeJoin(Paint.Join.ROUND);

    //formatter2.getFillPaint().setAlpha(220);
    dynamicPlot.addSeries(sine2Series, formatter2);

    // hook up the plotUpdater to the data model:
    data.addObserver(plotUpdater);

    // thin out domain tick labels so they dont overlap each other:
    dynamicPlot.setDomainStepMode(XYStepMode.INCREMENT_BY_VAL);
    dynamicPlot.setDomainStepValue(5);

    dynamicPlot.setRangeStepMode(XYStepMode.INCREMENT_BY_VAL);
    dynamicPlot.setRangeStepValue(10);

    dynamicPlot.setRangeValueFormat(new DecimalFormat("###.#"));

    // uncomment this line to freeze the range boundaries:
    dynamicPlot.setRangeBoundaries(-100, 100, BoundaryMode.FIXED);

    // create a dash effect for domain and range grid lines:
    DashPathEffect dashFx = new DashPathEffect(
            new float[] {PixelUtils.dpToPix(3), PixelUtils.dpToPix(3)}, 0);
    dynamicPlot.getGraphWidget().getDomainGridLinePaint().setPathEffect(dashFx);
    dynamicPlot.getGraphWidget().getRangeGridLinePaint().setPathEffect(dashFx);
}

@Override
public void onResume() {
    // kick off the data generating thread:
    myThread = new Thread(data);
    myThread.start();
    super.onResume();
}

@Override
public void onPause() {
    data.stopThread();
    super.onPause();
}

class SampleDynamicXYDatasource implements Runnable {

    // encapsulates management of the observers watching this datasource for update events:
    class MyObservable extends Observable {
        @Override
        public void notifyObservers() {
            setChanged();
            super.notifyObservers();
        }
    }

    private static final double FREQUENCY = 5; // larger is lower frequency
    private static final int MAX_AMP_SEED = 100; //100
    private static final int MIN_AMP_SEED = 10;  //10
    private static final int AMP_STEP = 1;
    public static final int SINE1 = 0;
    public static final int SINE2 = 1;
    private static final int SAMPLE_SIZE = 30;
    private int phase = 0;
    private int sinAmp = 1;
    private MyObservable notifier;
    private boolean keepRunning = false;

    {
        notifier = new MyObservable();
    }

    public void stopThread() {
        keepRunning = false;
    }

    //@Override
    public void run() {
        try {
            keepRunning = true; 
            boolean isRising = true; 
            while (keepRunning) {

                Thread.sleep(100); // decrease or remove to speed up the refresh rate.

                phase++;
                if (sinAmp >= MAX_AMP_SEED) {
                    isRising = false;
                } else if (sinAmp <= MIN_AMP_SEED) {
                    isRising = true;
                }

                if (isRising) {
                    sinAmp += AMP_STEP;
                } else {
                    sinAmp -= AMP_STEP;
                }
                notifier.notifyObservers();
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }


    public int getItemCount(int series) {
        return SAMPLE_SIZE;
    }

    public Number getX(int series, int index) {
        if (index >= SAMPLE_SIZE) {
            throw new IllegalArgumentException();
        }
        return index;
    }

    public Number getY(int series, int index) {
        if (index >= SAMPLE_SIZE) {
            throw new IllegalArgumentException();
        }
        double angle = (index + (phase))/FREQUENCY;
        double amp = sinAmp * Math.sin(angle);
        switch (series) {
            case SINE1:
                return amp;
            case SINE2:
                return -amp;
            default:
                throw new IllegalArgumentException();
        }
    }

    public void addObserver(Observer observer) {
        notifier.addObserver(observer);
    }

    public void removeObserver(Observer observer) {
        notifier.deleteObserver(observer);
    }

}

class SampleDynamicSeries implements XYSeries {
    private SampleDynamicXYDatasource datasource;
    private int seriesIndex;
    private String title;

    public SampleDynamicSeries(SampleDynamicXYDatasource datasource, int seriesIndex, String title) {
        this.datasource = datasource;
        this.seriesIndex = seriesIndex;
        this.title = title;
    }

    @Override
    public String getTitle() {
        return title;
    }

    @Override
    public int size() {
        return datasource.getItemCount(seriesIndex);
    }

    @Override
    public Number getX(int index) {
        return datasource.getX(seriesIndex, index);
    }

    @Override
    public Number getY(int index) {
        return datasource.getY(seriesIndex, index);
    }
    }
    }

=======================================================

After what "Nick" said and other minor addition, I got this result:

Result

but as we know :

https://www.google.com/search?q=y%3Dx%5E2%2B1&oq=y%3Dx%5E2%2B1&aqs=chrome..69i57j0l5.7056j0j7&sourceid=chrome&es_sm=93&ie=UTF-8

Now how to make the left side?


Solution

  • Using the code above can modify SampleDynamicXYDatasource to do what you want. All that code does is generate some data in a sine pattern. I don't know how your x values are going to be generated so here's a modified SampleDynamicXYDatasource.getY(...) that just uses the original code where x=index above and uses your function to generate the y-values:

            public Number getY(int series, int index) {
                if (index >= SAMPLE_SIZE) {
                    throw new IllegalArgumentException();
                }
                Number x = getX(series, index);
                double y = Math.pow(x.doubleValue(), 2) + 1;
                switch (series) {
                    case SINE1:
                        return y;
                    case SINE2:
                        return -y;
                    default:
                        throw new IllegalArgumentException();
                }
            }
    

    You'll notice that when you make this change, the plot appears to no longer be animated. (it actually still is but that's besides the point) This is because y is now purely a function of x and the x values never change.

    As far as how to stop the animation, the plot redraws whenever plot.redraw() is called, which in the example above is in response to an event being continuously fired by events generated by the thread being run on the Runnable instance of SampleDynamicXYDatasource. Using the example above, the simplest way to stop the animation is to replace:

    @Override
    public void onResume() {
        // kick off the data generating thread:
        myThread = new Thread(data);
        myThread.start();
        super.onResume();
    }
    

    with:

    @Override
    public void onResume() {
        dynamicPlot.redraw();
        super.onResume();
    }