1 package net.sf.openrocket.util;
4 import java.awt.Dimension;
6 import java.awt.Toolkit;
8 import java.util.HashMap;
10 import java.util.prefs.BackingStoreException;
11 import java.util.prefs.Preferences;
13 import net.sf.openrocket.database.Databases;
14 import net.sf.openrocket.document.Simulation;
15 import net.sf.openrocket.material.Material;
16 import net.sf.openrocket.rocketcomponent.BodyComponent;
17 import net.sf.openrocket.rocketcomponent.FinSet;
18 import net.sf.openrocket.rocketcomponent.InternalComponent;
19 import net.sf.openrocket.rocketcomponent.LaunchLug;
20 import net.sf.openrocket.rocketcomponent.MassObject;
21 import net.sf.openrocket.rocketcomponent.RecoveryDevice;
22 import net.sf.openrocket.rocketcomponent.Rocket;
23 import net.sf.openrocket.rocketcomponent.RocketComponent;
24 import net.sf.openrocket.simulation.RK4Simulator;
25 import net.sf.openrocket.simulation.SimulationConditions;
26 import net.sf.openrocket.unit.UnitGroup;
32 * Whether to use the debug-node instead of the normal node.
34 public static final boolean DEBUG = false;
37 * Whether to clear all preferences at application startup. This has an effect only
40 public static final boolean CLEARPREFS = true;
43 * The node name to use in the Java preferences storage.
45 public static final String NODENAME = (DEBUG?"OpenRocket-debug":"OpenRocket");
49 private static final String VERSION = "0.9.0";
52 public static final String BODY_COMPONENT_INSERT_POSITION_KEY = "BodyComponentInsertPosition";
55 public static final String CONFIRM_DELETE_SIMULATION = "ConfirmDeleteSimulation";
59 * Node to this application's preferences.
61 public static final Preferences NODE;
65 Preferences root = Preferences.userRoot();
66 if (DEBUG && CLEARPREFS) {
68 if (root.nodeExists(NODENAME)) {
69 root.node(NODENAME).removeNode();
71 } catch (BackingStoreException e) {
72 throw new RuntimeException("Unable to clear preference node",e);
75 NODE = root.node(NODENAME);
81 ///////// Default component attributes
83 private static final HashMap<Class<?>,String> DEFAULT_COLORS =
84 new HashMap<Class<?>,String>();
86 DEFAULT_COLORS.put(BodyComponent.class, "0,0,240");
87 DEFAULT_COLORS.put(FinSet.class, "0,0,200");
88 DEFAULT_COLORS.put(LaunchLug.class, "0,0,180");
89 DEFAULT_COLORS.put(InternalComponent.class, "170,0,100");
90 DEFAULT_COLORS.put(MassObject.class, "0,0,0");
91 DEFAULT_COLORS.put(RecoveryDevice.class, "255,0,0");
95 private static final HashMap<Class<?>,String> DEFAULT_LINE_STYLES =
96 new HashMap<Class<?>,String>();
98 DEFAULT_LINE_STYLES.put(RocketComponent.class, LineStyle.SOLID.name());
99 DEFAULT_LINE_STYLES.put(MassObject.class, LineStyle.DASHED.name());
103 private static final Material DEFAULT_LINE_MATERIAL =
104 Databases.findMaterial(Material.Type.LINE, "Elastic cord (round 2mm, 1/16 in)", 0.0018);
105 private static final Material DEFAULT_SURFACE_MATERIAL =
106 Databases.findMaterial(Material.Type.SURFACE, "Ripstop nylon", 0.067);
107 private static final Material DEFAULT_BULK_MATERIAL =
108 Databases.findMaterial(Material.Type.BULK, "Cardboard", 680);
111 //////////////////////
114 public static String getVersion() {
120 public static void storeVersion() {
121 NODE.put("OpenRocketVersion", getVersion());
126 * Returns a limited-range integer value from the preferences. If the value
127 * in the preferences is negative or greater than max, then the default value
130 * @param key The preference to retrieve.
131 * @param max Maximum allowed value for the choice.
132 * @param def Default value.
133 * @return The preference value.
135 public static int getChoise(String key, int max, int def) {
136 int v = NODE.getInt(key, def);
137 if ((v<0) || (v>max))
144 * Helper method that puts an integer choice value into the preferences.
146 * @param key the preference key.
147 * @param value the value to store.
149 public static void putChoise(String key, int value) {
150 NODE.putInt(key, value);
156 public static String getString(String key, String def) {
157 return NODE.get(key, def);
160 public static void putString(String key, String value) {
161 NODE.put(key, value);
170 public static File getDefaultDirectory() {
171 String file = NODE.get("defaultDirectory", null);
174 return new File(file);
177 public static void setDefaultDirectory(File dir) {
182 d = dir.getAbsolutePath();
184 NODE.put("defaultDirectory", d);
190 public static Color getDefaultColor(Class<? extends RocketComponent> c) {
191 String color = get("componentColors", c, DEFAULT_COLORS);
195 String[] rgb = color.split(",");
198 int red = MathUtil.clamp(Integer.parseInt(rgb[0]),0,255);
199 int green = MathUtil.clamp(Integer.parseInt(rgb[1]),0,255);
200 int blue = MathUtil.clamp(Integer.parseInt(rgb[2]),0,255);
201 return new Color(red,green,blue);
202 } catch (NumberFormatException ignore) { }
208 public static void setDefaultColor(Class<? extends RocketComponent> c, Color color) {
211 String string = color.getRed() + "," + color.getGreen() + "," + color.getBlue();
212 set("componentColors", c, string);
215 public static Color getMotorBorderColor() {
216 // TODO: MEDIUM: Motor color (settable?)
217 return new Color(0,0,0,200);
221 public static Color getMotorFillColor() {
222 // TODO: MEDIUM: Motor fill color (settable?)
223 return new Color(0,0,0,100);
227 public static LineStyle getDefaultLineStyle(Class<? extends RocketComponent> c) {
228 String value = get("componentStyle", c, DEFAULT_LINE_STYLES);
230 return LineStyle.valueOf(value);
231 } catch (Exception e) {
232 return LineStyle.SOLID;
236 public static void setDefaultLineStyle(Class<? extends RocketComponent> c,
240 set("componentStyle", c, style.name());
245 * Return the DPI setting of the monitor. This is either the setting provided
246 * by the system or a user-specified DPI setting.
248 * @return the DPI setting to use.
250 public static double getDPI() {
251 int dpi = NODE.getInt("DPI", 0); // Tenths of a dpi
254 dpi = Toolkit.getDefaultToolkit().getScreenResolution()*10;
259 return ((double)dpi)/10.0;
263 public static double getDefaultMach() {
264 // TODO: HIGH: implement custom default mach number
271 public static Material getDefaultComponentMaterial(
272 Class<? extends RocketComponent> componentClass,
273 Material.Type type) {
275 String material = get("componentMaterials", componentClass, null);
276 if (material != null) {
278 Material m = Material.fromStorableString(material);
279 if (m.getType() == type)
281 } catch (IllegalArgumentException ignore) { }
286 return DEFAULT_LINE_MATERIAL;
288 return DEFAULT_SURFACE_MATERIAL;
290 return DEFAULT_BULK_MATERIAL;
292 throw new IllegalArgumentException("Unknown material type: "+type);
295 public static void setDefaultComponentMaterial(
296 Class<? extends RocketComponent> componentClass, Material material) {
298 set("componentMaterials", componentClass,
299 material==null ? null : material.toStorableString());
303 public static int getMaxThreadCount() {
304 return Runtime.getRuntime().availableProcessors();
309 public static Point getWindowPosition(Class<?> c) {
311 String pref = NODE.node("windows").get("position." + c.getCanonicalName(), null);
316 if (pref.indexOf(',')<0)
320 x = Integer.parseInt(pref.substring(0,pref.indexOf(',')));
321 y = Integer.parseInt(pref.substring(pref.indexOf(',')+1));
322 } catch (NumberFormatException e) {
325 return new Point(x,y);
328 public static void setWindowPosition(Class<?> c, Point p) {
329 NODE.node("windows").put("position." + c.getCanonicalName(), "" + p.x + "," + p.y);
336 public static Dimension getWindowSize(Class<?> c) {
338 String pref = NODE.node("windows").get("size." + c.getCanonicalName(), null);
343 if (pref.indexOf(',')<0)
347 x = Integer.parseInt(pref.substring(0,pref.indexOf(',')));
348 y = Integer.parseInt(pref.substring(pref.indexOf(',')+1));
349 } catch (NumberFormatException e) {
352 return new Dimension(x,y);
355 public static void setWindowSize(Class<?> c, Dimension d) {
356 NODE.node("windows").put("size." + c.getCanonicalName(), "" + d.width + "," + d.height);
361 //// Background flight data computation
363 public static boolean computeFlightInBackground() {
364 return NODE.getBoolean("backgroundFlight", true);
367 public static Simulation getBackgroundSimulation(Rocket rocket) {
368 Simulation s = new Simulation(rocket);
369 SimulationConditions cond = s.getConditions();
371 cond.setTimeStep(RK4Simulator.RECOMMENDED_TIME_STEP*2);
372 cond.setWindSpeedAverage(1.0);
373 cond.setWindSpeedDeviation(0.1);
374 cond.setLaunchRodLength(5);
381 ///////// Default unit storage
383 public static void loadDefaultUnits() {
384 Preferences prefs = NODE.node("units");
387 for (String key: prefs.keys()) {
388 UnitGroup group = UnitGroup.UNITS.get(key);
392 group.setDefaultUnit(prefs.get(key, null));
395 } catch (BackingStoreException e) {
396 System.err.println("BackingStoreException:");
401 public static void storeDefaultUnits() {
402 Preferences prefs = NODE.node("units");
404 for (String key: UnitGroup.UNITS.keySet()) {
405 UnitGroup group = UnitGroup.UNITS.get(key);
406 if (group == null || group.getUnitCount() < 2)
409 prefs.put(key, group.getDefaultUnit().getUnit());
417 private static String get(String directory,
418 Class<? extends RocketComponent> componentClass,
419 Map<Class<?>, String> defaultMap) {
421 // Search preferences
422 Class<?> c = componentClass;
423 Preferences prefs = NODE.node(directory);
424 while (c!=null && RocketComponent.class.isAssignableFrom(c)) {
425 String value = prefs.get(c.getSimpleName(), null);
428 c = c.getSuperclass();
431 if (defaultMap == null)
436 while (RocketComponent.class.isAssignableFrom(c)) {
437 String value = defaultMap.get(c);
440 c = c.getSuperclass();
447 private static void set(String directory, Class<? extends RocketComponent> componentClass,
449 Preferences prefs = NODE.node(directory);
451 prefs.remove(componentClass.getSimpleName());
453 prefs.put(componentClass.getSimpleName(), value);