1 package com.billkuker.rocketry.motorsim.io;
\r
3 import java.io.OutputStream;
\r
4 import java.io.PrintWriter;
\r
5 import java.lang.reflect.InvocationTargetException;
\r
6 import java.lang.reflect.Method;
\r
7 import java.net.URLEncoder;
\r
8 import java.text.DecimalFormat;
\r
9 import java.text.NumberFormat;
\r
10 import java.util.Iterator;
\r
12 import javax.measure.quantity.Duration;
\r
13 import javax.measure.quantity.Force;
\r
14 import javax.measure.quantity.Quantity;
\r
15 import javax.measure.unit.SI;
\r
16 import javax.measure.unit.Unit;
\r
18 import org.jscience.physics.amount.Amount;
\r
20 import com.billkuker.rocketry.motorsim.Burn;
\r
21 import com.billkuker.rocketry.motorsim.BurnSummary;
\r
22 import com.billkuker.rocketry.motorsim.GraphSimplifier;
\r
23 import com.billkuker.rocketry.motorsim.RocketScience;
\r
25 public class HTMLExporter {
\r
26 static final int WIDTH=400;
\r
27 static final int HEIGHT=200;
\r
29 @SuppressWarnings("deprecation")
\r
30 private static String encode(String s) {
\r
31 return URLEncoder.encode(s).replace("%B2", "%C2%B2");
\r
34 private static <X extends Quantity, Y extends Quantity> String toChart(
\r
35 final Unit<X> xUnit, final Unit<Y> yUnit, final Object source,
\r
36 final String method, final Iterator<Amount<X>> domain, String title)
\r
37 throws SecurityException, NoSuchMethodException,
\r
38 IllegalArgumentException, IllegalAccessException,
\r
39 InvocationTargetException {
\r
40 NumberFormat nf = new DecimalFormat("#.##");
\r
41 Method f = source.getClass().getMethod(method, Amount.class);
\r
43 double xMin = 0, xMax = 0;
\r
44 double yMin = 0, yMax = 0;
\r
46 StringBuffer xVals = new StringBuffer();
\r
47 StringBuffer yVals = new StringBuffer();
\r
49 while (domain.hasNext()) {
\r
50 Amount<X> aX = domain.next();
\r
51 double x = aX.doubleValue(xUnit);
\r
52 @SuppressWarnings("unchecked")
\r
53 double y = ((Amount<Y>) f.invoke(source, aX)).doubleValue(yUnit);
\r
54 xMin = x < xMin ? x : xMin;
\r
55 xMax = x > xMax ? x : xMax;
\r
56 yMin = y < yMin ? y : yMin;
\r
57 yMax = y > yMax ? y : yMax;
\r
59 xVals.append(nf.format(x));
\r
61 yVals.append(nf.format(y));
\r
64 xVals.deleteCharAt(xVals.length() - 1);
\r
65 yVals.deleteCharAt(yVals.length() - 1);
\r
67 // Get the non-preferred Y Unit
\r
68 Unit<Y> yUnit2 = RocketScience.UnitPreference.SI
\r
69 .getPreferredUnit(yUnit);
\r
70 // if ( yUnit2.equals(yUnit) )
\r
71 yUnit2 = RocketScience.UnitPreference.NONSI.getPreferredUnit(yUnit);
\r
72 double y2Min, y2Max;
\r
73 y2Min = Amount.valueOf(yMin, yUnit).doubleValue(yUnit2);
\r
74 y2Max = Amount.valueOf(yMax, yUnit).doubleValue(yUnit2);
\r
76 // Get the non-preferred X Unit
\r
77 Unit<X> xUnit2 = RocketScience.UnitPreference.SI
\r
78 .getPreferredUnit(xUnit);
\r
79 // if ( xUnit2.equals(xUnit) )
\r
80 xUnit2 = RocketScience.UnitPreference.NONSI.getPreferredUnit(xUnit);
\r
81 double x2Min, x2Max;
\r
82 x2Min = Amount.valueOf(xMin, xUnit).doubleValue(xUnit2);
\r
83 x2Max = Amount.valueOf(xMax, xUnit).doubleValue(xUnit2);
\r
85 boolean x2 = !xUnit.equals(xUnit2);
\r
86 boolean y2 = x2 || !yUnit.equals(yUnit2);
\r
88 StringBuffer sb = new StringBuffer();
\r
89 sb.append("<img src='");
\r
90 sb.append("http://chart.apis.google.com/chart?chxt=x,x,y,y"
\r
91 + (y2 ? ",r,r" : "") + (x2 ? ",t,t" : "")
\r
92 + "&chs="+WIDTH+"x"+HEIGHT+"&cht=lxy&chco=3072F3");
\r
93 sb.append("&chds=" + nf.format(xMin) + "," + nf.format(xMax) + ","
\r
94 + nf.format(yMin) + "," + nf.format(yMax));
\r
95 sb.append("&chxr=" + "0," + nf.format(xMin) + "," + nf.format(xMax)
\r
96 + "|2," + nf.format(yMin) + "," + nf.format(yMax));
\r
98 sb.append("|4," + nf.format(y2Min) + "," + nf.format(y2Max));
\r
100 sb.append("|6," + nf.format(x2Min) + "," + nf.format(x2Max));
\r
101 sb.append("&chd=t:" + xVals.toString() + "|" + yVals.toString());
\r
102 sb.append("&chxl=1:|" + encode(xUnit.toString()) + "|3:|"
\r
103 + encode(yUnit.toString()));
\r
105 sb.append("|5:|" + encode(yUnit2.toString()));
\r
107 sb.append("|7:|" + encode(xUnit2.toString()));
\r
108 sb.append("&chxp=1,50|3,50");
\r
110 sb.append("|5,50");
\r
112 sb.append("|7,50");
\r
113 if ( title != null )
\r
114 sb.append("&chtt=" + title);
\r
115 sb.append("' width='"+WIDTH+"' height='"+HEIGHT+"' alt='" + title + "' />");
\r
117 return sb.toString();
\r
120 public static void export(Burn b, OutputStream os) throws Exception {
\r
121 PrintWriter out = new PrintWriter(os);
\r
123 BurnSummary bs = new BurnSummary(b);
\r
125 out.println("<!--Begin motor " + b.getMotor().getName() + " HTML export from MotorSim-->");
\r
126 out.print("<table class='motor' style='text-align: left; border: 1px solid black'>");
\r
129 out.print("<th colspan='6' class='title'>" + b.getMotor().getName() + "</th>");
\r
130 out.print("</tr>");
\r
132 out.print("<tr class='summary'>");
\r
134 out.print("<th>Rating:</th>");
\r
135 out.print("<td>" + bs.getRating() + "</td>");
\r
138 out.print("<th>Max Pressure:</th>");
\r
140 + RocketScience.ammountToRoundedString(bs.maxPressure())
\r
143 out.print("</tr>");
\r
146 out.print("<tr class='summary'>");
\r
148 out.print("<th>Total Impulse:</th>");
\r
150 + RocketScience.ammountToRoundedString(bs.totalImpulse())
\r
154 out.print("<th>Specific Impulse:</th>");
\r
156 + RocketScience.ammountToRoundedString(bs.specificImpulse())
\r
158 out.print("</tr>");
\r
160 out.print("<tr class='summary'>");
\r
162 out.print("<th>Max Thrust:</th>");
\r
164 + RocketScience.ammountToRoundedString(bs.maxThrust())
\r
167 out.print("<th>Volume Loading:</th>");
\r
169 + (int)(bs.getVolumeLoading()*100)
\r
171 out.print("</tr>");
\r
173 out.print("<tr class='summary'>");
\r
175 out.print("<th>Average Thrust:</th>");
\r
177 + RocketScience.ammountToRoundedString(bs.averageThrust())
\r
179 out.print("<th>Fuel Mass:</th>");
\r
181 + RocketScience.ammountToRoundedString(bs.getPropellantMass())
\r
184 out.print("</tr>");
\r
188 out.print("<td colspan='4' class='thrust'>");
\r
189 GraphSimplifier<Duration, Force> thrust = new GraphSimplifier<Duration, Force>(
\r
190 b, "thrust", b.getData().keySet().iterator());
\r
192 out.print(toChart(SI.SECOND, SI.NEWTON, thrust, "value", thrust
\r
193 .getDomain().iterator(), null));
\r
194 out.print("</td>");
\r
196 out.print("<td colspan='3' class='pressure'>");
\r
197 GraphSimplifier<Duration, Pressure> pressure = new GraphSimplifier<Duration, Pressure>(
\r
198 b, "pressure", b.getData().keySet().iterator());
\r
200 out.print(toChart(SI.SECOND,
\r
201 javax.measure.unit.SI.MEGA(javax.measure.unit.SI.PASCAL),
\r
202 pressure, "value", pressure.getDomain().iterator(), "Pressure"));
\r
204 out.print("</td>");
\r
206 out.print("</tr>");
\r
209 out.print("<th colspan='3'>.ENG File</th>");
\r
210 out.print("<th colspan='3'>MotorSim File</th>");
\r
211 out.print("</tr>");
\r
213 out.print("<td colspan='3'>");
\r
214 out.print("<textarea>");
\r
217 ENGExporter.export(b, os);
\r
219 out.print("</textarea>");
\r
220 out.print("</td>");
\r
221 out.print("<td colspan='3'>");
\r
222 out.print("<textarea>");
\r
223 out.print(MotorIO.writeMotor(b.getMotor()).replace("<", "<").replace(">", ">"));
\r
224 out.print("</textarea>");
\r
225 out.print("</td>");
\r
226 out.print("</tr>");
\r
228 out.print("</table>");
\r
230 out.println("\n<!--End motor " + b.getMotor().getName() + "-->");
\r