5f5a6487479cfd78eb4de907573b5478343b22c1
[sw/motorsim] / src / com / billkuker / rocketry / motorsim / visual / Chart.java
1 package com.billkuker.rocketry.motorsim.visual;\r
2 import java.awt.BorderLayout;\r
3 import java.awt.Color;\r
4 import java.lang.reflect.InvocationTargetException;\r
5 import java.lang.reflect.Method;\r
6 import java.util.Collection;\r
7 import java.util.Iterator;\r
8 \r
9 import javax.measure.quantity.Area;\r
10 import javax.measure.quantity.Length;\r
11 import javax.measure.quantity.Quantity;\r
12 import javax.measure.quantity.Volume;\r
13 import javax.measure.unit.SI;\r
14 import javax.measure.unit.Unit;\r
15 import javax.swing.JFrame;\r
16 import javax.swing.JPanel;\r
17 \r
18 import org.jfree.chart.ChartFactory;\r
19 import org.jfree.chart.ChartPanel;\r
20 import org.jfree.chart.JFreeChart;\r
21 import org.jfree.chart.plot.Marker;\r
22 import org.jfree.chart.plot.PlotOrientation;\r
23 import org.jfree.chart.plot.ValueMarker;\r
24 import org.jfree.data.xy.XYSeries;\r
25 import org.jfree.data.xy.XYSeriesCollection;\r
26 import org.jscience.physics.amount.Amount;\r
27 \r
28 import com.billkuker.rocketry.motorsim.RocketScience;\r
29 import com.billkuker.rocketry.motorsim.grain.CoredCylindricalGrain;\r
30 \r
31 public class Chart<X extends Quantity, Y extends Quantity> extends JPanel  {\r
32         private static final long serialVersionUID = 1L;\r
33 \r
34         public class IntervalDomain implements Iterable<Amount<X>>{\r
35                 \r
36                 Amount<X> low, high, delta;\r
37                 int steps = 100;\r
38 \r
39                 public IntervalDomain(Amount<X> low, Amount<X> high) {\r
40                         this.low = low;\r
41                         this.high = high;\r
42                         delta = high.minus(low).divide(steps);\r
43                 }\r
44 \r
45                 public IntervalDomain(Amount<X> low, Amount<X> high, int steps) {\r
46                         this.steps = steps;\r
47                         this.low = low;\r
48                         this.high = high;\r
49                         delta = high.minus(low).divide(steps);\r
50                 }\r
51 \r
52 \r
53                 public Iterator<Amount<X>> iterator() {\r
54                         return new Iterator<Amount<X>>(){\r
55                                 Amount<X> current = low;\r
56                                 \r
57                                 public boolean hasNext() {\r
58                                         return current.isLessThan(high.plus(delta));\r
59                                 }\r
60 \r
61                                 public Amount<X> next() {\r
62                                         Amount<X> ret = current;\r
63                                         current = current.plus(delta);\r
64                                         return ret;\r
65                                 }\r
66                                 \r
67                                 public final void remove() {\r
68                                         throw new UnsupportedOperationException("Chart domain iterators are not modifiable.");\r
69                                 }\r
70                         };\r
71                 }\r
72                 \r
73         }\r
74         \r
75 \r
76         XYSeries series;\r
77         XYSeriesCollection dataset = new XYSeriesCollection();\r
78         JFreeChart chart;\r
79 \r
80         Unit<X> xUnit;\r
81         Unit<Y> yUnit;\r
82 \r
83         Object source;\r
84         Method f;\r
85 \r
86 \r
87         @SuppressWarnings("unchecked")\r
88         public Chart(Unit<X> xUnit, Unit<Y> yUnit, Object source, String method)\r
89                         throws NoSuchMethodException {\r
90                 super(new BorderLayout());\r
91                 f = source.getClass().getMethod(method, Amount.class);\r
92 \r
93                 series = new XYSeries(f.getName());\r
94                 this.source = source;\r
95 \r
96                 dataset.addSeries(series);\r
97 \r
98                 this.xUnit = RocketScience.UnitPreference.preference.getPreferredUnit(xUnit);\r
99                 this.yUnit = RocketScience.UnitPreference.preference.getPreferredUnit(yUnit);\r
100                 \r
101                 chart = ChartFactory.createXYLineChart(\r
102                                 method.substring(0,1).toUpperCase() + method.substring(1), // Title\r
103                                 this.xUnit.toString(), // x-axis Label\r
104                                 this.yUnit.toString(), // y-axis Label\r
105                                 dataset,\r
106                                 PlotOrientation.VERTICAL, // Plot Orientation\r
107                                 false, // Show Legend\r
108                                 true, // Use tool tips\r
109                                 false // Configure chart to generate URLs?\r
110                                 );\r
111                 add(new ChartPanel(chart));\r
112         }\r
113         \r
114         private Marker marker;\r
115         public void mark(Amount<X> m){\r
116                 if ( marker != null )\r
117                         chart.getXYPlot().removeDomainMarker(marker);\r
118                 if ( m != null ){\r
119                marker = new ValueMarker(m.doubleValue(xUnit));\r
120                marker.setPaint(Color.blue);\r
121                marker.setAlpha(0.8f);\r
122                         chart.getXYPlot().addDomainMarker(marker);\r
123                 }\r
124         }\r
125 \r
126         @SuppressWarnings("unchecked")\r
127         public void setDomain(Iterable<Amount<X>> d) {\r
128                 int skip = 1;\r
129                 int sz = 0;\r
130                 if ( d instanceof Collection ){\r
131                         sz = ((Collection)d).size();\r
132                         if ( sz > 200 )\r
133                                 skip = sz / 200;\r
134                 }\r
135                 series.clear();\r
136                 int cnt = 0;\r
137                 for( Amount<X> ax: d){\r
138                         try {\r
139                                 if ( cnt % skip == 0 || cnt == sz-1 ){\r
140                                         Amount<Y> y = (Amount<Y>)f.invoke(source, ax);\r
141                                         add(ax, y);\r
142                                 }\r
143                                 cnt++;\r
144                         } catch (IllegalArgumentException e) {\r
145                                 // TODO Auto-generated catch block\r
146                                 e.printStackTrace();\r
147                         } catch (IllegalAccessException e) {\r
148                                 // TODO Auto-generated catch block\r
149                                 e.printStackTrace();\r
150                         } catch (InvocationTargetException e) {\r
151                                 // TODO Auto-generated catch block\r
152                                 e.printStackTrace();\r
153                         }\r
154                 }\r
155         }\r
156 \r
157         private void add(Amount<X> x, Amount<Y> y) {\r
158                 series.add(x.doubleValue(xUnit), y.doubleValue(yUnit));\r
159         }\r
160         \r
161         public void show(){\r
162                 new JFrame(){\r
163                         private static final long serialVersionUID = 1L;\r
164                         {\r
165                                 setContentPane(Chart.this);\r
166                                 setSize(640,480);\r
167                                 setDefaultCloseOperation(DISPOSE_ON_CLOSE);\r
168                         }\r
169                 }.setVisible(true);\r
170         }\r
171 \r
172         public static void main(String args[]) throws Exception {\r
173                 CoredCylindricalGrain g = new CoredCylindricalGrain();\r
174                 g.setLength(Amount.valueOf(70, SI.MILLIMETER));\r
175                 g.setOD(Amount.valueOf(30, SI.MILLIMETER));\r
176                 g.setID(Amount.valueOf(10, SI.MILLIMETER));\r
177 \r
178                 Chart<Length, Area> c = new Chart<Length, Area>(SI.MILLIMETER,\r
179                                 SI.MILLIMETER.pow(2).asType(Area.class), g, "surfaceArea");\r
180 \r
181                 c.setDomain(c.new IntervalDomain(Amount.valueOf(0, SI.CENTIMETER), g\r
182                                 .webThickness()));\r
183 \r
184                 c.show();\r
185                 \r
186                 Chart<Length, Volume> v = new Chart<Length, Volume>(SI.MILLIMETER,\r
187                                 SI.MILLIMETER.pow(3).asType(Volume.class), g, "volume");\r
188 \r
189                 v.setDomain(c.new IntervalDomain(Amount.valueOf(0, SI.CENTIMETER), g\r
190                                 .webThickness()));\r
191 \r
192                 v.show();\r
193         }\r
194 \r
195 }\r