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