JaRMoS  1.1
Java Reduced Model Simulations
 All Classes Namespaces Files Functions Variables Enumerator Groups Pages
OutputPlotterActivity.java
Go to the documentation of this file.
1 package jarmos.app.activity.rb;
2 
3 import jarmos.app.R;
5 
6 import java.util.ArrayList;
7 import java.util.List;
8 import java.util.Random;
9 
10 import org.achartengine.GraphicalView;
11 import org.achartengine.chart.PointStyle;
12 import org.achartengine.model.XYMultipleSeriesDataset;
13 import org.achartengine.model.XYSeries;
14 import org.achartengine.renderer.XYMultipleSeriesRenderer;
15 import org.achartengine.renderer.XYSeriesRenderer;
16 
17 import rb.RBContainer;
18 import android.app.Activity;
19 import android.app.AlertDialog;
20 import android.app.Dialog;
21 import android.content.Context;
22 import android.content.DialogInterface;
23 import android.content.Intent;
24 import android.graphics.Color;
25 import android.os.Bundle;
26 import android.os.Handler;
27 import android.os.Message;
28 import android.text.InputType;
29 import android.util.Log;
30 import android.view.KeyEvent;
31 import android.view.Menu;
32 import android.view.MenuInflater;
33 import android.view.MenuItem;
34 import android.view.MotionEvent;
35 import android.view.View;
36 import android.widget.Button;
37 import android.widget.EditText;
38 import android.widget.LinearLayout;
39 import android.widget.TableLayout;
40 import android.widget.TableRow;
41 import android.widget.TableRow.LayoutParams;
42 import android.widget.TextView;
43 import android.widget.Toast;
44 
55 public class OutputPlotterActivity extends Activity {
56  // String for log printing
57  private static final String DEBUG_TAG = "OutputPlotter";
58 
59  // dialog IDs for graphing options
60  static final int AXES_DIALOG_ID = 0;
61  static final int PLOT_DIALOG_ID = 1;
62  static final int LABEL_DIALOG_ID = 2;
63  static final int WARNING_DIALOG_ID = 3;
64  static final int INFO_DIALOG_ID = 4;
65 
66  // The Chart!
67  private GraphicalView mChartView;
68 
69  // An array that stores the colors used in plotting the outputs
70  private int[] mColors;
71 
72  // An array storing which outputs will not be shown
73  private boolean[] plotRemoved;
74 
75  // int to determine whether touch events trigger new sweep points
76  private int add_sweep_pts_toggle; // 0 for adding points, 1 for showing
77  // values, 2 for nothing
78 
79  // stores which series to show data for
80  private int labeled_series;
81 
82  // stores which time point to show data for
83  private int labeled_time_point;
84 
85  // stores the value at which a new sweep point is to be graphed
86  private double progressXVal;
87 
88  // stores the interpolated y-value indicating where a new sweep point is to
89  // be graphed
90  private double progressYVal;
91 
92  // A boolean to indicate whether we are doing a sweep over parameters
93  public int sweepIndex;
94 
95  // Strings for labeling the plot
96  public String title;
97  public String xLabel;
98 
99  // The time step size
100  public double dt;
101 
102  // The x-range
103  public double xMin;
104  public double xMax;
105 
106  // The y-range (determined dynamically by dataset)
107  private double yMin;
108  private double yMax;
109 
110  // Textviews for displaying x-range
111  private TextView xMinView;
112  private TextView xMaxView;
113 
114  // The number of time steps
115  public int n_time_steps;
116 
117  // The number of outputs we will plot
118  public int n_outputs;
119 
120  // The array of time step data
121  double[] time_step_array;
122 
123  // The array of output data
124  public double[][] RB_outputs_all_k;
125 
126  // The upper and lower bounds for each output
127  public double[][] RB_outputs_LB;
128  public double[][] RB_outputs_UB;
129 
130  // saved information from last screen
131  private Bundle extras;
132 
133  private int current_output;
134 
138  @Override
139  public void onCreate(Bundle savedInstanceState) {
140  super.onCreate(savedInstanceState);
141  setContentView(R.layout.rb_output_plotter_layout);
142 
143  extras = getIntent().getExtras();
144 
145  sweepIndex = -1;
146  if (extras.getBoolean("isSweep", false)) {
147  sweepIndex = extras.getInt("sweepIndex");
148  }
149  title = extras.getString("title");
150  dt = extras.getDouble("dt");
151  xMin = extras.getDouble("xMin");
152  xMax = extras.getDouble("xMax");
153  xLabel = extras.getString("xLabel");
154  n_time_steps = extras.getInt("n_time_steps");
155  n_outputs = extras.getInt("n_outputs");
156  // MW
157  // yMin = yMax = 0;
158  add_sweep_pts_toggle = 2;
159  labeled_time_point = -1;
160  progressXVal = xMin - 1;
161  progressYVal = 0;
162  labeled_series = 0;
163 
164  time_step_array = new double[n_time_steps];
165  for (int time_step = 0; time_step < n_time_steps; time_step++) {
166  time_step_array[time_step] = xMin + time_step * dt;
167  }
168 
169  // Get the output and error bound data
170  RB_outputs_all_k = new double[n_outputs][];
171  double[][] RB_output_error_bounds_all_k = new double[n_outputs][];
172  for (int i = 0; i < n_outputs; i++) {
173  RB_outputs_all_k[i] = extras.getDoubleArray("output_data_" + i);
174  RB_output_error_bounds_all_k[i] = extras.getDoubleArray("output_bound_" + i);
175  }
176 
177  // Create the UB and LB array based on
178  // RB_outputs_all_k and RB_output_error_bounds_all_k
179 
180  RB_outputs_LB = new double[n_outputs][n_time_steps];
181  RB_outputs_UB = new double[n_outputs][n_time_steps];
182 
183  for (int i = 0; i < n_outputs; i++) {
184  for (int time_step = 0; time_step < n_time_steps; time_step++) {
185  RB_outputs_LB[i][time_step] = RB_outputs_all_k[i][time_step]
186  - RB_output_error_bounds_all_k[i][time_step];
187  RB_outputs_UB[i][time_step] = RB_outputs_all_k[i][time_step]
188  + RB_output_error_bounds_all_k[i][time_step];
189  }
190  }
191 
192  plotRemoved = new boolean[n_outputs];
193  for (int i = 0; i < n_outputs; i++) {
194  plotRemoved[i] = true;
195  }
196  plotRemoved[0] = false;
197  current_output = 0;
198 
199  LinearLayout layout = (LinearLayout) findViewById(R.id.chart);
200  mChartView = execute(this);
201  layout.addView(mChartView, new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT));
202 
203  // Add the legend
204  TableLayout legendLayout = (TableLayout) findViewById(R.id.legendLayout);
205 
206  int n_labels_per_row = 5; // The number of legend labels on each row
207 
208  TableRow row = new TableRow(this); // Build an initial row
209  row.setLayoutParams(new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT));
210 
211  int count = 0;
212  while (count < n_outputs) {
213  TextView legend_i = new TextView(this);
214  if (count % n_labels_per_row == 0)
215  legend_i.setText(" " + "output " + (count + 1) + " ");
216  else
217  legend_i.setText("output " + (count + 1) + " ");
218  legend_i.setTextColor(mColors[count]);
219  legend_i.setLayoutParams(new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT));
220 
221  // set background color of the label
222  legend_i.setBackgroundColor(Color.WHITE);
223 
224  row.addView(legend_i);
225 
226  // set background color of the row
227  row.setBackgroundColor(Color.WHITE);
228 
229  count++;
230 
231  // Add the current row and construct a new row
232  if ((count + 1) % n_labels_per_row == 0) {
233  legendLayout.addView(row, new TableLayout.LayoutParams(LayoutParams.FILL_PARENT,
234  LayoutParams.WRAP_CONTENT));
235 
236  row = new TableRow(this);
237  row.setLayoutParams(new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT));
238  }
239  }
240  legendLayout.addView(row, new TableLayout.LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT));
241 
242  Button visButton = (Button) findViewById(R.id.unsteadyVisButton);
243  visButton.setOnClickListener(new View.OnClickListener() {
244 
245  public void onClick(View view) {
246  Intent intent = new Intent(OutputPlotterActivity.this, RBVisualization.class);
247  intent.putExtras(OutputPlotterActivity.this.getIntent().getExtras());
248  OutputPlotterActivity.this.startActivity(intent);
249  }
250  });
251  }
252 
264  protected XYMultipleSeriesDataset buildDataset(String[] titles, List<double[]> xValues, List<double[]> yValues) {
265  XYMultipleSeriesDataset dataset = new XYMultipleSeriesDataset();
266  int num_series = 3 * n_outputs;
267 
268  yMin = yMax = (yValues.get(0))[0];
269  for (int i = 0; i < num_series; i++) {
270  XYSeries series = new XYSeries(titles[i]);
271  double[] xV = xValues.get(i);
272  double[] yV = yValues.get(i);
273  int seriesLength = xV.length;
274 
275  for (int k = 0; k < seriesLength; k++) {
276  // determine which values not to plot based on given xMin & xMax
277  // value
278  // do not add values for hidden plots
279  if ((xV[k] >= xMin) && (xV[k] <= xMax) && !plotRemoved[i / 3])
280  series.add(xV[k], yV[k]);
281  if (yV[k] < yMin && !plotRemoved[i / 3])
282  yMin = yV[k];
283  if (yV[k] > yMax && !plotRemoved[i / 3])
284  yMax = yV[k];
285  }
286  dataset.addSeries(series);
287  }
288 
289  // an additional series for displaying what sweep values are to be
290  // computed with a vertical line
291  XYSeries series = new XYSeries("");
292  series.add(progressXVal, yMin);
293  series.add(progressXVal, yMax);
294  if (sweepIndex == -1 || progressYVal > yMax || progressYVal < yMin)
295  progressYVal = yMin; // don't want to stretch graph
296  series.add(progressXVal, progressYVal);
297  dataset.addSeries(series);
298 
299  return dataset;
300  }
301 
311  protected XYMultipleSeriesRenderer buildRenderer() {
312  XYMultipleSeriesRenderer renderer = new XYMultipleSeriesRenderer();
313 
314  mColors = new int[n_outputs];
315  for (int i = 0; i < n_outputs; i++) {
316  XYSeriesRenderer r_out = new XYSeriesRenderer();
317  XYSeriesRenderer r_LB = new XYSeriesRenderer();
318  XYSeriesRenderer r_UB = new XYSeriesRenderer();
319 
320  // Set colors
321  switch (i) {
322  case 0:
323  mColors[0] = Color.BLUE;
324  r_out.setColor(Color.BLUE); // Output
325  r_LB.setColor(Color.BLUE); // LB
326  r_UB.setColor(Color.BLUE); // UB
327  break;
328  case 1:
329  mColors[1] = Color.RED;
330  r_out.setColor(Color.RED);
331  r_LB.setColor(Color.RED);
332  r_UB.setColor(Color.RED);
333  break;
334  case 2:
335  mColors[2] = Color.GREEN;
336  r_out.setColor(Color.GREEN);
337  r_LB.setColor(Color.GREEN);
338  r_UB.setColor(Color.GREEN);
339  break;
340  case 3:
341  mColors[3] = Color.CYAN;
342  r_out.setColor(Color.CYAN);
343  r_LB.setColor(Color.CYAN);
344  r_UB.setColor(Color.CYAN);
345  break;
346  case 4:
347  mColors[4] = Color.YELLOW;
348  r_out.setColor(Color.YELLOW);
349  r_LB.setColor(Color.YELLOW);
350  r_UB.setColor(Color.YELLOW);
351  break;
352  case 5:
353  mColors[5] = Color.MAGENTA;
354  r_out.setColor(Color.MAGENTA);
355  r_LB.setColor(Color.MAGENTA);
356  r_UB.setColor(Color.MAGENTA);
357  break;
358  case 6:
359  mColors[6] = Color.GRAY;
360  r_out.setColor(Color.GRAY);
361  r_LB.setColor(Color.GRAY);
362  r_UB.setColor(Color.GRAY);
363  break;
364  case 7:
365  mColors[7] = Color.BLACK;
366  r_out.setColor(Color.BLACK);
367  r_LB.setColor(Color.BLACK);
368  r_UB.setColor(Color.BLACK);
369  break;
370  default:
371  Random numGen = new Random();
372  mColors[i] = Color.argb(255, numGen.nextInt(256), numGen.nextInt(256), numGen.nextInt(256));
373  r_out.setColor(mColors[i]);
374  r_LB.setColor(mColors[i]);
375  r_UB.setColor(mColors[i]);
376  break;
377  }
378 
379  r_out.setPointStyle(PointStyle.X); // output
380  r_LB.setPointStyle(PointStyle.POINT); // LB
381  r_UB.setPointStyle(PointStyle.POINT); // UB
382 
383  renderer.addSeriesRenderer(r_out);
384  renderer.addSeriesRenderer(r_LB);
385  renderer.addSeriesRenderer(r_UB);
386  }
387 
388  XYSeriesRenderer r_add = new XYSeriesRenderer();
389  r_add.setColor(Color.BLACK);
390  r_add.setPointStyle(PointStyle.POINT);
391  renderer.addSeriesRenderer(r_add);
392 
393  return renderer;
394  }
395 
420  protected void setChartSettings(XYMultipleSeriesRenderer renderer, String title, String xLabel, double xMin,
421  double xMax) {
422  renderer.setChartTitle(title);
423  renderer.setXTitle(xLabel);
424  renderer.setXAxisMin(xMin);
425  renderer.setXAxisMax(xMax);
426  renderer.setAxesColor(Color.BLACK);
427  renderer.setLabelsColor(Color.BLACK);
428 
429  // MW
430  renderer.setDisplayChartValues(true);
431 
432  // Set the approximate number of "ticks" on the x and y axes
433  renderer.setXLabels(10);
434  renderer.setYLabels(10);
435  renderer.setChartValuesTextSize(16);
436 
437  // Set the background color
438  renderer.setApplyBackgroundColor(true);
439  renderer.setBackgroundColor(Color.WHITE);
440 
441  // Turn off the legend
442  renderer.setShowLegend(false);
443  }
444 
452  public GraphicalView execute(Context context) {
453 
454  String[] labels = new String[3 * n_outputs];
455  for (int i = 0; i < n_outputs; i++) {
456  labels[3 * i] = "output " + (i + 1);
457  labels[3 * i + 1] = "LB " + (i + 1);
458  labels[3 * i + 2] = "UB " + (i + 1);
459  }
460 
461  List<double[]> t = new ArrayList<double[]>();
462  List<double[]> output_values = new ArrayList<double[]>();
463 
464  // Add each output and corresponding UB and LB
465  for (int i = 0; i < n_outputs; i++) {
466  t.add(time_step_array);
467  output_values.add(RB_outputs_all_k[i]);
468 
469  t.add(time_step_array);
470  output_values.add(RB_outputs_LB[i]);
471 
472  t.add(time_step_array);
473  output_values.add(RB_outputs_UB[i]);
474  }
475 
476  XYMultipleSeriesRenderer renderer = buildRenderer();
477  setChartSettings(renderer, "", "", xMin, xMax + (xMax - xMin) * .05);
478 
479  // return ChartFactory.getLineChartView(context, buildDataset(labels, t,
480  // output_values), renderer);
481  boolean sweepInProgress = !(progressYVal == yMin);
482  labeled_series = sweepInProgress ? n_outputs * 3 : labeled_series;
483 
484  SingleLabelChart theChart = new SingleLabelChart(buildDataset(labels, t, output_values), renderer,
485  labeled_series, labeled_time_point, sweepInProgress);
486  return new GraphicalView(context, theChart);
487  }
488 
489  public boolean onTouchEvent(final MotionEvent event) {
490  if (event.getAction() == MotionEvent.ACTION_DOWN) {
491  float X = event.getX();
492  // float Y = event.getY();
493  if (add_sweep_pts_toggle == 0)
494  add_sweep_point(X / mChartView.getWidth());
495  else if (add_sweep_pts_toggle == 1)
496  showPoint(X / mChartView.getWidth());
497  Log.d(DEBUG_TAG, "touch point (x) = " + (event.getX() - 15) / 290);
498  }
499  if (event.getAction() == MotionEvent.ACTION_UP) {
500  }
501  return true;
502  }
503 
507  public void add_sweep_point(float xpos) {
508  if (sweepIndex > -1) {
509  // make sure xpos is within the range (0.01, 0.99)
510  xpos = xpos < 0.01f ? 0.01f : xpos;
511  xpos = xpos > 0.99f ? 0.99f : xpos;
512 
513  // xrange is the current range of the graph's x-values (determined
514  // by xMin and xMax)
515  // xcurrent is the x-value of the touch point
516  double xrange = (time_step_array[n_time_steps - 1] - time_step_array[0])
517  * ((xMax - xMin) / (extras.getDouble("xMax") - extras.getDouble("xMin")));
518 
519  double xcurrent = time_step_array[0] + (time_step_array[n_time_steps - 1] - time_step_array[0])
520  * (xMin - extras.getDouble("xMin")) / (extras.getDouble("xMax") - extras.getDouble("xMin"))
521  + xrange * (xpos);
522 
523  // find time step value closest to y-axis
524  int leftmostStep = 0;
525  while (time_step_array[leftmostStep] < xMin) {
526  leftmostStep++;
527  }
528  double xAxisValue = ((xMax - xMin) * xpos) + xMin;
529  // now, adjusting for the current leftmost point, find the smallest
530  // time step greater than the touch point
531  int closestTimestep = 0;
532  while (time_step_array[closestTimestep + leftmostStep] < xAxisValue) {
533  closestTimestep++;
534  }
535 
536  // take the average of the values of the first visible dataset to
537  // get the y axis value for showing
538  // progress label
539  int firstVisible = 0;
540  while (plotRemoved[firstVisible])
541  firstVisible++;
542  if (firstVisible < n_outputs) { // there is a visible output
543 
544  int ts = closestTimestep + leftmostStep;
545  // before computing the value, set progress values to indicate
546  // the computation is taking place
547  progressXVal = xcurrent;
548  progressYVal = RB_outputs_all_k[firstVisible][ts - 1]
549  + ((RB_outputs_all_k[firstVisible][ts] - RB_outputs_all_k[firstVisible][ts - 1]) / (time_step_array[ts] - time_step_array[ts - 1]))
550  * (xcurrent - time_step_array[ts - 1]);
551 
552  // disable new sweep points until computation is complete
553  add_sweep_pts_toggle = 2;
554 
555  // repaint graph
556  LinearLayout layout = (LinearLayout) findViewById(R.id.chart);
557  mChartView = execute(this);
558  layout.removeAllViews();
559  layout.addView(mChartView, new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT));
560 
561  // start separate thread to actually solve for sweep point and
562  // graph it
563  SolveThread st = new SolveThread();
564  st.start();
565  }
566 
567  }
568  }
569 
573  public void showPoint(float xpos) {
574  // make sure xpos is within the range (0.01, 0.99)
575  xpos = xpos < 0.01f ? 0.01f : xpos;
576  xpos = xpos > 0.99f ? 0.99f : xpos;
577 
578  double xAxisValue = ((xMax - xMin) * xpos) + xMin;
579 
580  // find time step value closest to y-axis
581  int leftmostStep = 0;
582  while (time_step_array[leftmostStep] < xMin) {
583  leftmostStep++;
584  }
585 
586  // now, adjusting for the current leftmost point, find the smallest time
587  // step greater than the touch point
588  int closestTimestep = 0;
589  while (time_step_array[closestTimestep + leftmostStep] < xAxisValue) {
590  closestTimestep++;
591  }
592 
593  // again adjusting for the current leftmost point, take either the
594  // previous time step or the one before it
595  if (closestTimestep > 0)
596  closestTimestep = time_step_array[closestTimestep + leftmostStep] - xAxisValue > xAxisValue
597  - time_step_array[closestTimestep + leftmostStep - 1] ? closestTimestep - 1 : closestTimestep;
598 
599  // labeled_time_point is given to the constructor of SingleLabelChart to
600  // determine
601  // which values get labeled instead of all of them (in the default
602  // XYChart)
603  labeled_time_point = closestTimestep * 2;
604  Log.d(DEBUG_TAG, "Closest timestep: " + closestTimestep);
605 
606  // repaint graph
607  LinearLayout layout = (LinearLayout) findViewById(R.id.chart);
608  mChartView = execute(this);
609  layout.removeAllViews();
610  layout.addView(mChartView, new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT));
611 
612  }
613 
617  protected Dialog onCreateDialog(int id) {
618 
619  Dialog dialog;
620 
621  switch (id) {
622  case AXES_DIALOG_ID:
623  dialog = new Dialog(this);
624  dialog.setContentView(R.layout.rb_axes_dialog);
625  dialog.setTitle("Coordinate Axes Options");
626  dialog.setCancelable(false);
627 
628  // initialize text fields
629  TextView xMinLabel = (TextView) dialog.findViewById(R.id.x_min_label);
630  xMinLabel.setText("Minimum x-value");
631  xMinView = (EditText) dialog.findViewById(R.id.x_min_entry);
632  xMinView.setText(String.valueOf(xMin));
633  xMinView.setInputType(InputType.TYPE_CLASS_NUMBER | InputType.TYPE_NUMBER_FLAG_DECIMAL
634  | InputType.TYPE_NUMBER_FLAG_SIGNED);
635 
636  TextView xMaxLabel = (TextView) dialog.findViewById(R.id.x_max_label);
637  xMaxLabel.setText("Maximum x-value");
638  xMaxView = (EditText) dialog.findViewById(R.id.x_max_entry);
639  xMaxView.setText(String.valueOf(xMax));
640  xMaxView.setInputType(InputType.TYPE_CLASS_NUMBER | InputType.TYPE_NUMBER_FLAG_DECIMAL
641  | InputType.TYPE_NUMBER_FLAG_SIGNED);
642 
643  // button for resetting back to default xMin and xMax
644  Button resetButton = (Button) dialog.findViewById(R.id.reset_default);
645  resetButton.setOnClickListener(new View.OnClickListener() {
646 
647  public void onClick(View view) {
648  // change local values and re-plot
649  xMin = extras.getDouble("xMin");
650  xMax = extras.getDouble("xMax");
651  LinearLayout layout = (LinearLayout) findViewById(R.id.chart);
652  mChartView = execute(getApplicationContext());
653  layout.removeAllViews();
654  layout.addView(mChartView, new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT));
655 
656  dismissDialog(AXES_DIALOG_ID);
657  removeDialog(AXES_DIALOG_ID);
658  }
659  });
660 
661  // handle changing local values to user-submitted xMin and xMax
662  // values on clicking the ok button
663  Button okButton = (Button) dialog.findViewById(R.id.axes_okButton);
664  okButton.setOnClickListener(new View.OnClickListener() {
665 
666  public void onClick(View view) {
667  // determine if value in minimum input field is within
668  // acceptable range
669  String userMinString = xMinView.getText().toString();
670  double userMin;
671  try {
672  userMin = Double.parseDouble(userMinString);
673  } catch (NumberFormatException e) {
674  // if user submits non-double, default value is out of
675  // bounds to trigger toast
676  userMin = extras.getDouble("xMin") - 1;
677  }
678  // determine if value in maximum input field is within
679  // acceptable range
680  String userMaxString = xMaxView.getText().toString();
681  double userMax;
682  try {
683  userMax = Double.parseDouble(userMaxString);
684  } catch (NumberFormatException e) {
685  // if user submits non-double, default value is out of
686  // bounds to trigger toast
687  userMax = extras.getDouble("xMax") + 1;
688  }
689  // make sure the given max value is greater than the given
690  // min, and that they are both
691  // within original values
692  if ((userMax > userMin) && (userMax <= extras.getDouble("xMax"))
693  && (userMin >= extras.getDouble("xMin"))) {
694  // change local values
695  xMin = userMin;
696  xMax = userMax;
697  // add data points at new endpoints
698  // add_sweep_point(0.011f);
699  // add_sweep_point(1f);
700  // re-plot
701  LinearLayout layout = (LinearLayout) findViewById(R.id.chart);
702  mChartView = execute(getApplicationContext());
703  layout.removeAllViews();
704  layout.addView(mChartView, new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT));
705  } else {
706  Toast.makeText(getApplicationContext(), "Value out of bounds", Toast.LENGTH_SHORT).show();
707  }
708  dismissDialog(AXES_DIALOG_ID);
709  removeDialog(AXES_DIALOG_ID);
710 
711  }
712  });
713  break;
714  case PLOT_DIALOG_ID:
715  String[] plotStrings = new String[n_outputs];
716 
717  for (int i = 0; i < n_outputs; i++) {
718  plotStrings[i] = "output " + (i + 1);
719  }
720 
721  boolean[] plotBooleans = new boolean[n_outputs];
722  for (int i = 0; i < n_outputs; i++) {
723  if (plotRemoved[i] == true)
724  plotBooleans[i] = false;
725  else
726  plotBooleans[i] = true;
727  }
728 
729  AlertDialog.Builder builder = new AlertDialog.Builder(OutputPlotterActivity.this);
730  builder.setTitle("Enable/Disable Plots");
731  builder.setMultiChoiceItems(plotStrings, plotBooleans, new DialogInterface.OnMultiChoiceClickListener() {
732  public void onClick(DialogInterface dialog, int item, boolean checked) {
733  // reset colors appropriately
734  if (checked) {
735  plotRemoved[item] = false;
736  } else {
737  plotRemoved[item] = true;
738  }
739  }
740  });
741  builder.setNeutralButton("OK", new DialogInterface.OnClickListener() {
742  public void onClick(DialogInterface dialog, int item) {
743  // re-plot the graph
744  LinearLayout layout = (LinearLayout) findViewById(R.id.chart);
745  mChartView = execute(getApplicationContext());
746  layout.removeAllViews();
747  layout.addView(mChartView, new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT));
748  }
749  });
750  dialog = builder.create();
751  break;
752  case LABEL_DIALOG_ID:
753  AlertDialog.Builder builder2 = new AlertDialog.Builder(OutputPlotterActivity.this);
754  if (sweepIndex > -1) {
755  String[] labelStrings = new String[3];
756  labelStrings[0] = "Add sweep points";
757  labelStrings[1] = "Show data labels";
758  labelStrings[2] = "Do nothing";
759 
760  boolean[] labelBooleans = new boolean[3];
761  labelBooleans[add_sweep_pts_toggle] = true;
762 
763  builder2.setTitle("Set on touch behavior");
764  builder2.setMultiChoiceItems(labelStrings, labelBooleans,
765  new DialogInterface.OnMultiChoiceClickListener() {
766  public void onClick(DialogInterface dialog, int item, boolean checked) {
767  if (item == 0) {
768  add_sweep_pts_toggle = 0; // now on touch,
769  // sweep points
770  // will be added
771  }
772  if (item == 1) {
773  add_sweep_pts_toggle = 1; // now on touch,
774  // data values
775  // will show
776 
777  // determine whether more than one output is
778  // showing: if this is the case,
779  // recommend that the user disable all but
780  // one output
781  int visibleCount = 0;
782  for (int i = 0; i < n_outputs; i++) {
783  if (plotRemoved[i] == false)
784  visibleCount++;
785  }
786  if (visibleCount > 1) {
787  showDialog(WARNING_DIALOG_ID);
788  }
789  }
790  if (item == 2) {
791  add_sweep_pts_toggle = 2;
792  }
793  labeled_time_point = -1; // remove the label
794 
795  // re-plot the graph
796  LinearLayout layout = (LinearLayout) findViewById(R.id.chart);
797  mChartView = execute(getApplicationContext());
798  layout.removeAllViews();
799  layout.addView(mChartView, new LayoutParams(LayoutParams.FILL_PARENT,
800  LayoutParams.FILL_PARENT));
801  // have to remove dialog because this is not
802  // automatic for a multi-choice dialog
803  dismissDialog(LABEL_DIALOG_ID);
804  removeDialog(LABEL_DIALOG_ID);
805  }
806  });
807  } else {
808  String[] labelStrings = new String[2];
809  labelStrings[0] = "Show data labels";
810  labelStrings[1] = "Do nothing";
811 
812  boolean[] labelBooleans = new boolean[2];
813  if (add_sweep_pts_toggle == 1)
814  labelBooleans[0] = true;
815  else
816  labelBooleans[1] = true;
817 
818  builder2.setTitle("Set on touch behavior");
819  builder2.setMultiChoiceItems(labelStrings, labelBooleans,
820  new DialogInterface.OnMultiChoiceClickListener() {
821  public void onClick(DialogInterface dialog, int item, boolean checked) {
822  if (item == 1) {
823  add_sweep_pts_toggle = 2; // now on touch,
824  // sweep points
825  // will be added
826  }
827  if (item == 0) {
828  add_sweep_pts_toggle = 1; // now on touch,
829  // data values
830  // will show
831 
832  // determine whether more than one output is
833  // showing: if this is the case,
834  // recommend that the user disable all but
835  // one output
836  int visibleCount = 0;
837  for (int i = 0; i < n_outputs; i++) {
838  if (plotRemoved[i] == false)
839  visibleCount++;
840  }
841  if (visibleCount > 1) {
842  showDialog(WARNING_DIALOG_ID);
843  }
844  }
845  labeled_time_point = -1; // remove the label
846 
847  // re-plot the graph
848  LinearLayout layout = (LinearLayout) findViewById(R.id.chart);
849  mChartView = execute(getApplicationContext());
850  layout.removeAllViews();
851  layout.addView(mChartView, new LayoutParams(LayoutParams.FILL_PARENT,
852  LayoutParams.FILL_PARENT));
853  // have to remove dialog because this is not
854  // automatic for a multi-choice dialog
855  dismissDialog(LABEL_DIALOG_ID);
856  removeDialog(LABEL_DIALOG_ID);
857  }
858  });
859  }
860 
861  dialog = builder2.create();
862  break;
863  case WARNING_DIALOG_ID:
864  String[] plotStrings2 = new String[n_outputs];
865 
866  for (int i = 0; i < n_outputs; i++) {
867  plotStrings2[i] = "output " + (i + 1);
868  }
869 
870  AlertDialog.Builder builder3 = new AlertDialog.Builder(OutputPlotterActivity.this);
871  builder3.setTitle("Show labels for which output?");
872  builder3.setItems(plotStrings2, new DialogInterface.OnClickListener() {
873  public void onClick(DialogInterface dialog, int item) {
874 
875  labeled_series = item;
876 
877  // re-plot the graph
878  LinearLayout layout = (LinearLayout) findViewById(R.id.chart);
879  mChartView = execute(getApplicationContext());
880  layout.removeAllViews();
881  layout.addView(mChartView, new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT));
882  }
883  });
884  dialog = builder3.create();
885  break;
886  case INFO_DIALOG_ID:
887  AlertDialog.Builder infoBuilder = new AlertDialog.Builder(OutputPlotterActivity.this);
888  infoBuilder.setTitle("Current Parameters");
889 
890  String message = "Online N = " + RBActivity.mOnlineNForGui + "\n\n" + "Parameters: \n\n";
891  double[] param = RBActivity.rb.mRbSystem.getParams().getCurrent();
892  for (int i = 0; i < param.length; i++) {
893  if (sweepIndex > -1 && Integer.parseInt(xLabel) == (i + 1))
894  message = message + (i + 1) + ": " + "Sweep\n";
895  else
896  message = message + (i + 1) + ": " + param[i] + "\n";
897  }
898  message = message + "\nChange values?";
899  infoBuilder.setMessage(message);
900  infoBuilder.setPositiveButton("Yes", new DialogInterface.OnClickListener() {
901  public void onClick(DialogInterface dialog, int id) {
902  onBackPressed();
903  }
904  });
905  infoBuilder.setNegativeButton("No", null);
906  dialog = infoBuilder.create();
907  break;
908  default:
909  dialog = null;
910  break;
911  }
912  return dialog;
913 
914  }
915 
916  // populate main menu
917  public boolean onPrepareOptionsMenu(Menu menu) {
918  menu.clear();
919  MenuInflater inflater = getMenuInflater();
920  inflater.inflate(R.menu.rb_plotter_main_menu, menu);
921  return true;
922  }
923 
924  // handle item selection in main menu
925  public boolean onOptionsItemSelected(MenuItem item) {
926  switch (item.getItemId()) {
927  case R.id.plot_info:
928  showDialog(INFO_DIALOG_ID);
929  return true;
930  case R.id.change_axes:
931  showDialog(AXES_DIALOG_ID);
932  return true;
933  case R.id.en_dis_plots:
934  showDialog(PLOT_DIALOG_ID);
935  return true;
936  case R.id.on_touch:
937  showDialog(LABEL_DIALOG_ID);
938  return true;
939  }
940  return false;
941  }
942 
943  // solves for new sweep values separately from UI thread, allowing UI to
944  // indicate that a solve
945  // is being performed
946  private class SolveThread extends Thread {
947 
948  public void run() {
949  int icount = 0;
950  double xcurrent = progressXVal;
951  while ((icount < n_time_steps) && (xcurrent > time_step_array[icount])) {
952  icount++;
953  }
954  // xleft and xright are the numbers of the time steps immediately to
955  // the left and right
956  // of the touch point, respectively
957  int xleft = icount - 1;
958  int xright = icount;
959 
960  // double max_time_step =
961  // time_step_array[xleft+1]-time_step_array[xleft+1];
962  double max_time_step = 0;
963  int i_max_step = xleft + 1;
964  for (int i = xleft; i < xright; i++) {
965  double current_time_step = time_step_array[i + 1] - time_step_array[i];
966  if (max_time_step < current_time_step) {
967  max_time_step = current_time_step;
968  i_max_step = i + 1;
969  }
970  }
971 
972  RBContainer rb = RBActivity.rb;
973  rb.mRbSystem.getParams().setCurrent(sweepIndex, xcurrent);
974  rb.mRbSystem.computeRBSolution(RBActivity.mOnlineNForGui);
975 
976  double[][] new_RB_outputs_all_k = new double[n_outputs][n_time_steps + 1];
977  double[][] new_RB_outputs_LB = new double[n_outputs][n_time_steps + 1];
978  double[][] new_RB_outputs_UB = new double[n_outputs][n_time_steps + 1];
979  double[] new_time_step_array = new double[n_time_steps + 1];
980 
981  new_time_step_array[i_max_step] = xcurrent;
982 
983  for (int j = 0; j < n_time_steps; j++) {
984  int k = (j < i_max_step) ? j : j + 1;
985  new_time_step_array[k] = time_step_array[j];
986  for (int i = 0; i < n_outputs; i++) {
987  new_RB_outputs_all_k[i][k] = RB_outputs_all_k[i][j];
988  new_RB_outputs_LB[i][k] = RB_outputs_LB[i][j];
989  new_RB_outputs_UB[i][k] = RB_outputs_UB[i][j];
990  }
991  }
992 
993  if (rb.mRbSystem.isReal)
994  for (int n = 0; n < n_outputs; n++) {
995  new_RB_outputs_all_k[n][i_max_step] = rb.mRbSystem.RB_outputs[n];
996  double OutputBound = rb.mRbSystem.RB_output_error_bounds[n];
997  new_RB_outputs_LB[n][i_max_step] = new_RB_outputs_all_k[n][i_max_step] - OutputBound;
998  new_RB_outputs_UB[n][i_max_step] = new_RB_outputs_all_k[n][i_max_step] + OutputBound;
999  }
1000  else
1001  for (int n = 0; n < n_outputs / 2; n++) {
1002  new_RB_outputs_all_k[n][i_max_step] = rb.mRbSystem.get_RB_output(n, true);
1003  double OutputBound = rb.mRbSystem.get_RB_output_error_bound(n, true);
1004  new_RB_outputs_LB[n][i_max_step] = new_RB_outputs_all_k[n][i_max_step] - OutputBound;
1005  new_RB_outputs_UB[n][i_max_step] = new_RB_outputs_all_k[n][i_max_step] + OutputBound;
1006 
1007  new_RB_outputs_all_k[n + n_outputs / 2][i_max_step] = rb.mRbSystem.get_RB_output(n, false);
1008  OutputBound = rb.mRbSystem.get_RB_output_error_bound(n, false);
1009  new_RB_outputs_LB[n + n_outputs / 2][i_max_step] = new_RB_outputs_all_k[n + n_outputs / 2][i_max_step]
1010  - OutputBound;
1011  new_RB_outputs_UB[n + n_outputs / 2][i_max_step] = new_RB_outputs_all_k[n + n_outputs / 2][i_max_step]
1012  + OutputBound;
1013  }
1014 
1015  RB_outputs_all_k = new_RB_outputs_all_k;
1016  RB_outputs_LB = new_RB_outputs_LB;
1017  RB_outputs_UB = new_RB_outputs_UB;
1018  time_step_array = new_time_step_array;
1019 
1020  n_time_steps++;
1021 
1022  // hide progress vertical line
1023  progressXVal = xMin - 1;
1024  progressYVal = yMin;
1025 
1026  // re-enable adding sweep points
1027  add_sweep_pts_toggle = 0;
1028 
1029  // update graph
1030  handler.sendEmptyMessage(0);
1031  }
1032 
1033  private Handler handler = new Handler() {
1034  public void handleMessage(Message msg) {
1035  LinearLayout layout = (LinearLayout) findViewById(R.id.chart);
1036  mChartView = execute(getApplicationContext());
1037  layout.removeAllViews();
1038  layout.addView(mChartView, new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT));
1039  }
1040  };
1041  }
1042 
1043  public boolean onKeyDown(int keyCode, KeyEvent event) {
1044  if (event.getAction() == MotionEvent.ACTION_DOWN) {
1045  switch (keyCode) {
1046  case 24: // KEYCODE_VOLUME_UP
1047  {
1048  current_output += 1;
1049  if (current_output == n_outputs)
1050  current_output = 0;
1051  for (int i = 0; i < n_outputs; i++) {
1052  plotRemoved[i] = true;
1053  }
1054  plotRemoved[current_output] = false;
1055 
1056  LinearLayout layout = (LinearLayout) findViewById(R.id.chart);
1057  mChartView = execute(getApplicationContext());
1058  layout.removeAllViews();
1059  layout.addView(mChartView, new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT));
1060 
1061  return true;
1062  }
1063  case 25: // KEYCODE_VOLUME_DOWN
1064  {
1065  current_output -= 1;
1066  if (current_output == -1)
1067  current_output = n_outputs - 1;
1068  for (int i = 0; i < n_outputs; i++) {
1069  plotRemoved[i] = true;
1070  }
1071  plotRemoved[current_output] = false;
1072 
1073  LinearLayout layout = (LinearLayout) findViewById(R.id.chart);
1074  mChartView = execute(getApplicationContext());
1075  layout.removeAllViews();
1076  layout.addView(mChartView, new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT));
1077  return true;
1078  }
1079  default:
1080  return super.onKeyDown(keyCode, event);
1081  }
1082  }
1083  return true;
1084  }
1085 }
GraphicalView execute(Context context)
Executes the chart demo.
XYMultipleSeriesDataset buildDataset(String[] titles, List< double[]> xValues, List< double[]> yValues)
Builds an XY multiple dataset using the provided values.
static int mOnlineNForGui
Array of TextViews and SeekBars for constructing the parameter selection.
Definition: RBActivity.java:95
Main RB visualization activity.
static RBContainer rb
The RB Container with all the system and model data (from JRB)
Base class for RB models and systems.
boolean onKeyDown(int keyCode, KeyEvent event)
void showPoint(float xpos)
Displays information on an individual user-selected point on the graph.
void setChartSettings(XYMultipleSeriesRenderer renderer, String title, String xLabel, double xMin, double xMax)
Sets a few of the series renderer settings.
Activity for output plotting of functions of time using the AChartEngine library. ...
Dialog onCreateDialog(int id)
This function takes care of constructing the dialogs that pop up.
void onCreate(Bundle savedInstanceState)
Constructor.
void add_sweep_point(float xpos)
Adds a new point in non-time-dependent problems on where a touch event has occurred.
A line chart implementation with a single label.
XYMultipleSeriesRenderer buildRenderer()
Builds an XY multiple series renderer.