Show the simulation name as the chart title. Made some UI concessions because drawin...
[debian/openrocket] / android / src / net / sf / openrocket / android / simulation / SimulationChart.java
1 /**\r
2  * Copyright (C) 2009, 2010 SC 4ViewSoft SRL\r
3  *  \r
4  * Licensed under the Apache License, Version 2.0 (the "License");\r
5  * you may not use this file except in compliance with the License.\r
6  * You may obtain a copy of the License at\r
7  *  \r
8  *      http://www.apache.org/licenses/LICENSE-2.0\r
9  *  \r
10  * Unless required by applicable law or agreed to in writing, software\r
11  * distributed under the License is distributed on an "AS IS" BASIS,\r
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
13  * See the License for the specific language governing permissions and\r
14  * limitations under the License.\r
15  */\r
16 package net.sf.openrocket.android.simulation;\r
17 \r
18 import java.util.List;\r
19 \r
20 import net.sf.openrocket.simulation.FlightDataBranch;\r
21 import net.sf.openrocket.simulation.FlightDataType;\r
22 import net.sf.openrocket.simulation.FlightEvent;\r
23 \r
24 import org.achartengine.ChartFactory;\r
25 import org.achartengine.chart.LineChart;\r
26 import org.achartengine.chart.PointStyle;\r
27 import org.achartengine.chart.XYChart;\r
28 import org.achartengine.model.XYMultipleSeriesDataset;\r
29 import org.achartengine.model.XYSeries;\r
30 import org.achartengine.renderer.XYMultipleSeriesRenderer;\r
31 import org.achartengine.renderer.XYSeriesRenderer;\r
32 \r
33 import android.content.Context;\r
34 import android.content.Intent;\r
35 import android.graphics.Color;\r
36 import android.graphics.Paint.Align;\r
37 import android.util.Log;\r
38 \r
39 /**\r
40  * Multiple temperature demo chart.\r
41  */\r
42 public class SimulationChart {\r
43         \r
44         private final static String TAG = "SimulationChart";\r
45 \r
46         private FlightDataBranch flightDataBranch;\r
47         private FlightDataType series1;\r
48         private FlightDataType series2;\r
49         private final FlightDataType time = FlightDataType.TYPE_TIME;\r
50         private List<FlightEvent> flightEvents;\r
51         private String simulationName;\r
52 \r
53         // Define 4 different colors and point styles to use for the series.\r
54         // For now only 2 series are supported though.\r
55         private final static int[] colors = new int[] { Color.BLUE, Color.YELLOW, Color.GREEN, Color.RED };\r
56         private final static PointStyle[] styles = new PointStyle[] { PointStyle.CIRCLE, PointStyle.DIAMOND,\r
57                 PointStyle.TRIANGLE, PointStyle.SQUARE };\r
58 \r
59         /**\r
60          * @param simulationName the simulationName to set\r
61          */\r
62         public void setSimulationName(String simulationName) {\r
63                 this.simulationName = simulationName;\r
64         }\r
65 \r
66         /**\r
67          * @param flightDataBranch the flightDataBranch to set\r
68          */\r
69         public void setFlightDataBranch(FlightDataBranch flightDataBranch) {\r
70                 this.flightDataBranch = flightDataBranch;\r
71         }\r
72 \r
73         /**\r
74          * @param series1 the series1 to set\r
75          */\r
76         public void setSeries1(FlightDataType series1) {\r
77                 this.series1 = series1;\r
78         }\r
79 \r
80         /**\r
81          * @param series2 the series2 to set\r
82          */\r
83         public void setSeries2(FlightDataType series2) {\r
84                 this.series2 = series2;\r
85         }\r
86 \r
87         /**\r
88          * @param flightEvents the flightEvents to set\r
89          */\r
90         public void setFlightEvents(List<FlightEvent> flightEvents) {\r
91                 this.flightEvents = flightEvents;\r
92         }\r
93 \r
94         private static String formatFlightDataTypeAxisLabel( FlightDataType fdt ) {\r
95                 return fdt.getName() + " (" + fdt.getUnitGroup().getDefaultUnit().toString() + ")";\r
96         }\r
97 \r
98         /**\r
99          * Executes the chart demo.\r
100          * \r
101          * @param context the context\r
102          * @return the built intent\r
103          */\r
104         public Intent execute(Context context) {\r
105 \r
106                 /*\r
107                  * TODO -\r
108                  * Figure out why you can pan all over the place even where there are no visible points.\r
109                  */\r
110                 int seriesCount = 2;\r
111                 // if the same series is selected twice, only plot it once.\r
112                 if ( series1 == series2 ) {\r
113                         seriesCount = 1;\r
114                 }\r
115 \r
116                 XYMultipleSeriesRenderer renderer = new XYMultipleSeriesRenderer(seriesCount);\r
117 \r
118                 renderer.setAxisTitleTextSize(16);\r
119                 renderer.setChartTitleTextSize(20);\r
120                 renderer.setLabelsTextSize(15);\r
121                 renderer.setLegendTextSize(15);\r
122                 renderer.setPointSize(5f);\r
123                 renderer.setXLabels(10);\r
124                 renderer.setYLabels(10);\r
125                 renderer.setShowGrid(true);\r
126                 renderer.setZoomButtonsVisible(true);\r
127                 renderer.setChartTitle(simulationName);\r
128 \r
129                 renderer.setMargins(new int[] { 50, 30, 0, 20 });\r
130                 {\r
131                         for (int i = 0; i < seriesCount; i++) {\r
132                                 XYSeriesRenderer r = new XYSeriesRenderer();\r
133                                 r.setColor(colors[i]);\r
134                                 r.setPointStyle(styles[i]);\r
135                                 r.setFillPoints(true);\r
136                                 renderer.addSeriesRenderer(r);\r
137                                 // setting the YAximMin to 0 locks the origins.\r
138                                 renderer.setYAxisMin(0.0, i);\r
139                         }\r
140                 }\r
141 \r
142                 renderer.setXTitle(formatFlightDataTypeAxisLabel(time));\r
143                 renderer.setXLabelsAlign(Align.RIGHT);\r
144 \r
145                 renderer.setYTitle(formatFlightDataTypeAxisLabel(series1),0);\r
146                 renderer.setYLabelsAlign(Align.RIGHT,0);\r
147 \r
148                 if ( seriesCount > 1 ) {\r
149                         renderer.setYTitle(formatFlightDataTypeAxisLabel(series2), 1);\r
150                         renderer.setYAxisAlign(Align.RIGHT, 1);\r
151                         renderer.setYLabelsAlign(Align.LEFT, 1);\r
152                 }\r
153 \r
154                 renderer.setAxesColor(Color.LTGRAY);\r
155                 renderer.setLabelsColor(Color.LTGRAY);\r
156 \r
157                 XYMultipleSeriesDataset dataset = new XYMultipleSeriesDataset();\r
158 \r
159                 List<Double> timevalues = flightDataBranch.get(time);\r
160                 List<Double> series1values = flightDataBranch.get(series1);\r
161 \r
162                 // compute the axis limits using timevalues and series1values.\r
163                 double xmin = 0;\r
164                 double ymin = 0;\r
165                 renderer.setXAxisMin(xmin);\r
166                 renderer.setYAxisMin(ymin);\r
167 \r
168                 double ymax = computeMaxValueWithPadding( series1values );\r
169                 double xmax = Math.ceil( timevalues.get( timevalues.size()-1));\r
170                 \r
171                 Log.d(TAG,"ymax = " + ymax);\r
172                 renderer.setXAxisMax(xmax);\r
173                 renderer.setYAxisMax(ymax);\r
174 \r
175                 // These configurations don't really work well just now.\r
176                 //renderer.setPanLimits(new double[] { xmin, xmax, ymin, ymax });\r
177                 //renderer.setZoomLimits(new double[] { xmin, xmax, ymin, ymax });\r
178 \r
179                 // Add first series\r
180                 addXYSeries(dataset, series1.getName(), timevalues, series1values, 0);\r
181 \r
182                 if ( seriesCount > 1 ) {\r
183                         // Add second series\r
184                         addXYSeries(dataset, series2.getName(), timevalues, flightDataBranch.get(series2), 1);\r
185                 }\r
186                 Intent intent = getLineChartIntent(context, dataset, renderer,"Simulation");\r
187                 return intent;\r
188         }\r
189 \r
190         private static void addXYSeries(XYMultipleSeriesDataset dataset, String titles, List<Double> xValues, List<Double> yValues, int scale) {\r
191                 XYSeries series = new XYSeries(titles, scale);\r
192                 int datasize = xValues.size();\r
193                 for( int i = 0; i<datasize; i++ ) {\r
194                         series.add(xValues.get(i), yValues.get(i));\r
195                 }\r
196                 dataset.addSeries(series);\r
197 \r
198         }\r
199 \r
200         private static Intent getLineChartIntent(Context context, XYMultipleSeriesDataset dataset,\r
201                         XYMultipleSeriesRenderer renderer, String activityTitle) {\r
202                 //                  checkParameters(dataset, renderer);\r
203                 Intent intent = new Intent(context, GraphicalActivity.class);\r
204                 XYChart chart = new LineChart(dataset, renderer);\r
205                 intent.putExtra(ChartFactory.CHART, chart);\r
206                 intent.putExtra(ChartFactory.TITLE, activityTitle);\r
207                 return intent;\r
208         }\r
209 \r
210         private static double computeMaxValueWithPadding( List<Double> list ) {\r
211                 double max = list.get(0);\r
212                 for( double v : list ) {\r
213                         if ( v > max ) {\r
214                                 max = v;\r
215                         }\r
216                 }\r
217                 if ( max <= 0 ) return 1.0;\r
218 \r
219                 // Do something stupid.\r
220                 // return:\r
221                 //  10 if max <= 10\r
222                 //  next 10 if 10 < max < 1000\r
223                 //  next 100 if 1000 < max < 10,000\r
224                 //  next 1000 if max >= 10,000\r
225                 double numdigits = Math.floor(Math.log10(max));\r
226                 \r
227                 if ( numdigits <= 1.0 ) {\r
228                         return 10.0;\r
229                 } else if ( numdigits <= 3.0 ) {\r
230                         return 10.0 * ( Math.ceil( max/10.0));\r
231                 } else if ( numdigits <= 4.0 ) {\r
232                         return 100.0 * ( Math.ceil( max/ 100.0) );\r
233                 } else {\r
234                         return 1000.0 * ( Math.ceil( max / 1000.0 ));\r
235                 }\r
236                 \r
237         }\r
238         \r
239 }\r