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 import java.util.concurrent.ExecutorService;
\r
9 import java.util.concurrent.Executors;
\r
11 import javax.measure.quantity.Area;
\r
12 import javax.measure.quantity.Length;
\r
13 import javax.measure.quantity.Quantity;
\r
14 import javax.measure.quantity.Volume;
\r
15 import javax.measure.unit.SI;
\r
16 import javax.measure.unit.Unit;
\r
17 import javax.swing.JFrame;
\r
18 import javax.swing.JPanel;
\r
20 import org.jfree.chart.ChartFactory;
\r
21 import org.jfree.chart.ChartPanel;
\r
22 import org.jfree.chart.JFreeChart;
\r
23 import org.jfree.chart.plot.Marker;
\r
24 import org.jfree.chart.plot.PlotOrientation;
\r
25 import org.jfree.chart.plot.ValueMarker;
\r
26 import org.jfree.data.xy.XYSeries;
\r
27 import org.jfree.data.xy.XYSeriesCollection;
\r
28 import org.jscience.physics.amount.Amount;
\r
30 import com.billkuker.rocketry.motorsim.RocketScience;
\r
31 import com.billkuker.rocketry.motorsim.grain.CoredCylindricalGrain;
\r
33 public class Chart<X extends Quantity, Y extends Quantity> extends JPanel {
\r
34 private static final long serialVersionUID = 1L;
\r
36 private static ExecutorService fast = Executors.newFixedThreadPool(2) ;
\r
37 private static ExecutorService slow = Executors.newFixedThreadPool(2);
\r
39 public class IntervalDomain implements Iterable<Amount<X>>{
\r
41 Amount<X> low, high, delta;
\r
44 public IntervalDomain(Amount<X> low, Amount<X> high) {
\r
47 delta = high.minus(low).divide(steps);
\r
50 public IntervalDomain(Amount<X> low, Amount<X> high, int steps) {
\r
54 delta = high.minus(low).divide(steps);
\r
58 public Iterator<Amount<X>> iterator() {
\r
59 return new Iterator<Amount<X>>(){
\r
60 Amount<X> current = low;
\r
62 public boolean hasNext() {
\r
63 return current.isLessThan(high.plus(delta));
\r
66 public Amount<X> next() {
\r
67 Amount<X> ret = current;
\r
68 current = current.plus(delta);
\r
72 public final void remove() {
\r
73 throw new UnsupportedOperationException("Chart domain iterators are not modifiable.");
\r
82 XYSeriesCollection dataset = new XYSeriesCollection();
\r
92 @SuppressWarnings("unchecked")
\r
93 public Chart(Unit<X> xUnit, Unit<Y> yUnit, Object source, String method)
\r
94 throws NoSuchMethodException {
\r
95 super(new BorderLayout());
\r
96 f = source.getClass().getMethod(method, Amount.class);
\r
98 series = new XYSeries(f.getName());
\r
99 this.source = source;
\r
101 dataset.addSeries(series);
\r
103 this.xUnit = RocketScience.UnitPreference.preference.getPreferredUnit(xUnit);
\r
104 this.yUnit = RocketScience.UnitPreference.preference.getPreferredUnit(yUnit);
\r
106 chart = ChartFactory.createXYLineChart(
\r
107 method.substring(0,1).toUpperCase() + method.substring(1), // Title
\r
108 this.xUnit.toString(), // x-axis Label
\r
109 this.yUnit.toString(), // y-axis Label
\r
111 PlotOrientation.VERTICAL, // Plot Orientation
\r
112 false, // Show Legend
\r
113 true, // Use tool tips
\r
114 false // Configure chart to generate URLs?
\r
116 add(new ChartPanel(chart));
\r
119 private Marker marker;
\r
120 public void mark(Amount<X> m){
\r
121 if ( marker != null )
\r
122 chart.getXYPlot().removeDomainMarker(marker);
\r
124 marker = new ValueMarker(m.doubleValue(xUnit));
\r
125 marker.setPaint(Color.blue);
\r
126 marker.setAlpha(0.8f);
\r
127 chart.getXYPlot().addDomainMarker(marker);
\r
132 public void setDomain(final Iterable<Amount<X>> d) {
\r
135 fast.submit(new Thread(){
\r
140 slow.submit(new Thread(){
\r
147 @SuppressWarnings("unchecked")
\r
148 private void fill(Iterable<Amount<X>> d, int skip) {
\r
150 if (d instanceof Collection) {
\r
151 sz = ((Collection) d).size();
\r
152 int sk2 = sz / 200;
\r
160 Amount<X> last = null;
\r
161 for (Amount<X> ax : d) {
\r
163 if (cnt % skip == 0) {
\r
164 Amount<Y> y = (Amount<Y>) f.invoke(source, ax);
\r
169 Amount<Y> y = (Amount<Y>) f.invoke(source, last);
\r
171 } catch (IllegalArgumentException e) {
\r
172 // TODO Auto-generated catch block
\r
173 e.printStackTrace();
\r
174 } catch (IllegalAccessException e) {
\r
175 // TODO Auto-generated catch block
\r
176 e.printStackTrace();
\r
177 } catch (InvocationTargetException e) {
\r
178 // TODO Auto-generated catch block
\r
179 e.printStackTrace();
\r
183 private void add(Amount<X> x, Amount<Y> y) {
\r
184 series.add(x.doubleValue(xUnit), y.doubleValue(yUnit));
\r
187 public void show(){
\r
189 private static final long serialVersionUID = 1L;
\r
191 setContentPane(Chart.this);
\r
193 setDefaultCloseOperation(DISPOSE_ON_CLOSE);
\r
195 }.setVisible(true);
\r
198 public static void main(String args[]) throws Exception {
\r
199 CoredCylindricalGrain g = new CoredCylindricalGrain();
\r
200 g.setLength(Amount.valueOf(70, SI.MILLIMETER));
\r
201 g.setOD(Amount.valueOf(30, SI.MILLIMETER));
\r
202 g.setID(Amount.valueOf(10, SI.MILLIMETER));
\r
204 Chart<Length, Area> c = new Chart<Length, Area>(SI.MILLIMETER,
\r
205 SI.MILLIMETER.pow(2).asType(Area.class), g, "surfaceArea");
\r
207 c.setDomain(c.new IntervalDomain(Amount.valueOf(0, SI.CENTIMETER), g
\r
212 Chart<Length, Volume> v = new Chart<Length, Volume>(SI.MILLIMETER,
\r
213 SI.MILLIMETER.pow(3).asType(Volume.class), g, "volume");
\r
215 v.setDomain(c.new IntervalDomain(Amount.valueOf(0, SI.CENTIMETER), g
\r