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