1 package net.sf.openrocket.simulation;
3 import java.util.SortedMap;
4 import java.util.TreeMap;
6 import net.sf.openrocket.document.Simulation;
7 import net.sf.openrocket.logging.LogHelper;
8 import net.sf.openrocket.startup.Application;
9 import net.sf.openrocket.unit.FixedUnitGroup;
10 import net.sf.openrocket.unit.UnitGroup;
11 import net.sf.openrocket.util.ArrayList;
12 import de.congrace.exp4j.Calculable;
13 import de.congrace.exp4j.ExpressionBuilder;
17 * Represents a single custom expression
18 * @author Richard Graham
21 public class CustomExpression implements Cloneable{
23 private static final LogHelper log = Application.getLogger();
25 private String name, symbol, unit, expression;
26 private ExpressionBuilder builder;
27 private static Simulation sim = null;
29 // A map of available operator strings (keys) and description of function (value)
30 public static final SortedMap<String, String> AVAILABLE_OPERATORS = new TreeMap<String, String>() {{
31 put("+" , "Addition");
32 put("-" , "Subtraction");
33 put("*" , "Multiplication");
36 put("^" , "Exponentiation");
37 put("abs()" , "Absolute value");
38 put("ceil()" , "Ceiling (next integer value");
39 put("floor()" , "Floor (previous integer value");
40 put("sqrt()" , "Square root");
41 put("cbrt()" , "Cubic root");
42 put("exp()" , "Euler\'s number raised to the value (e^x)");
43 put("log()" , "Natural logarithm");
44 put("sin()" , "Sine");
45 put("cos()" , "Cosine");
46 put("tan()" , "Tangent");
47 put("asin()" , "Arc sine");
48 put("acos()" , "Arc cosine");
49 put("atan()" , "Arc tangent");
50 put("sinh()" , "Hyerbolic sine");
51 put("cosh()" , "Hyperbolic cosine");
52 put("tanh()" , "Hyperbolic tangent");
56 public CustomExpression(){
63 public CustomExpression(Simulation sim){
68 public CustomExpression(Simulation sim, String name, String symbol, String unit, String expression) {
73 setExpression(expression);
78 * Use this to update the simulation this is associated with
80 public void setSimulation(Simulation sim){
81 CustomExpression.sim = sim;
84 public Simulation getSimulation() {
85 return CustomExpression.sim;
89 * Returns the flight data branch 0 for this simulation, or an empty branch
90 * if no simulated data exists
92 private FlightDataBranch getBranch() {
93 if ( sim == null || sim.getSimulatedData().getBranch(0) == null) {
94 return new FlightDataBranch();
97 return sim.getSimulatedData().getBranch(0);
102 public void setName(String name){
106 public void setUnit(String unit){
110 public void setSymbol(String symbol){
111 this.symbol = symbol;
114 public void setExpression(String expression){
115 this.expression = expression;
116 builder = new ExpressionBuilder(expression);
119 // get a list of all the names of all the available variables
120 private ArrayList<String> getAllNames(){
121 ArrayList<String> names = new ArrayList<String>();
122 for (FlightDataType type : FlightDataType.ALL_TYPES)
123 names.add(type.getName());
124 for (CustomExpression exp : sim.getCustomExpressions() ){
126 names.add(exp.getName());
131 // get a list of all the symbols of the available variables ignoring this one
132 private ArrayList<String> getAllSymbols(){
133 ArrayList<String> symbols = new ArrayList<String>();
134 for (FlightDataType type : FlightDataType.ALL_TYPES)
135 symbols.add(type.getSymbol());
136 for (CustomExpression exp : sim.getCustomExpressions() ){
138 symbols.add(exp.getSymbol());
143 public boolean checkSymbol(){
144 if (symbol.trim().isEmpty())
148 for (char c : "0123456789.()[]{}".toCharArray())
149 if (symbol.indexOf(c) != -1 )
152 // No operators (ignoring brackets)
153 for (String s : CustomExpression.AVAILABLE_OPERATORS.keySet()){
154 if (symbol.contains(s.replaceAll("\\(|\\)", "")))
158 // No already defined symbols
159 ArrayList<String> symbols = getAllSymbols().clone();
160 if (symbols.contains(symbol.trim())){
161 int index = symbols.indexOf(symbol.trim());
162 log.user("Symbol "+symbol+" already exists, found "+symbols.get(index));
169 public boolean checkName(){
170 if (name.trim().isEmpty())
173 ArrayList<String> names = getAllNames().clone();
174 if (names.contains(name.trim())){
175 int index = names.indexOf(name.trim());
176 log.user("Symbol "+symbol+" already exists, found "+names.get(index));
183 // Currently no restrictions on unit
184 public boolean checkUnit(){
188 public boolean checkAll(){
189 return checkUnit() && checkSymbol() && checkName();
192 public String getName(){
196 public String getSymbol(){
200 public String getUnit(){
204 public String getExpressionString(){
210 * Check if the current expression is valid
212 public boolean checkExpression(){
214 if (expression.trim().isEmpty()){
218 // Define the available variables as 0
219 for (FlightDataType type : getBranch().getTypes()){
220 builder.withVariable(type.getSymbol(), 0.0);
223 for (String symb : getAllSymbols()){
224 builder.withVariable(symb, 0.0);
230 } catch (Exception e) {
231 log.user("Custom expression invalid : " + e.toString());
240 * Evaluate the expression using the last variable values from the simulation status.
241 * Returns NaN on any error.
243 public Double evaluate(SimulationStatus status){
245 for (FlightDataType type : status.getFlightData().getTypes()){
246 builder.withVariable(type.getSymbol(), status.getFlightData().getLast(type) );
251 calc = builder.build();
252 return new Double(calc.calculate());
253 } catch (Exception e) {
254 log.user("Could not calculate custom expression "+name);
260 * Returns the new flight data type corresponding to this calculated data
262 public FlightDataType getType(){
263 UnitGroup ug = new FixedUnitGroup(unit);
264 return FlightDataType.getType(name, symbol, ug);
268 * Add this expression to the simulation if not already added
270 public void addToSimulation(){
271 if (! sim.getCustomExpressions().contains(this))
272 sim.addCustomExpression( this );
276 * Removes this expression from the simulation, replacing it with a given new expression
278 public void overwrite(CustomExpression newExpression){
279 if (!sim.getCustomExpressions().contains(this))
282 int index = sim.getCustomExpressions().indexOf(this);
283 sim.getCustomExpressions().set(index, newExpression);
288 public String toString(){
289 return "Custom expression : "+this.name.toString()+ " " + this.expression.toString();
294 * Clone method makes a deep copy of everything except the simulation
295 * @see java.lang.Object#clone()
297 public Object clone() {
299 return super.clone();
301 catch( CloneNotSupportedException e )
303 return new CustomExpression( sim ,
304 new String(this.getName()),
305 new String(this.getSymbol()),
306 new String(this.getUnit()),
307 new String(this.getExpressionString()));