diff --git a/core/3d-Test-Junk/net/sf/openrocket/startup/TextureOutputTest.java b/core/3d-Test-Junk/net/sf/openrocket/startup/TextureOutputTest.java
new file mode 100644
index 000000000..bbe525862
--- /dev/null
+++ b/core/3d-Test-Junk/net/sf/openrocket/startup/TextureOutputTest.java
@@ -0,0 +1,59 @@
+package net.sf.openrocket.startup;
+import java.io.File;
+
+import net.sf.openrocket.database.ComponentPresetDatabase;
+import net.sf.openrocket.database.ThrustCurveMotorSetDatabase;
+import net.sf.openrocket.document.OpenRocketDocument;
+import net.sf.openrocket.document.StorageOptions;
+import net.sf.openrocket.file.DatabaseMotorFinder;
+import net.sf.openrocket.file.GeneralRocketLoader;
+import net.sf.openrocket.file.openrocket.OpenRocketSaver;
+import net.sf.openrocket.gui.util.SwingPreferences;
+import net.sf.openrocket.l10n.ResourceBundleTranslator;
+
+/**
+ * An application for quickly testing 3d figure witout all the OpenRocket user
+ * interface
+ *
+ * @author bkuker
+ *
+ */
+public class TextureOutputTest {
+
+ /**
+ * @param args
+ */
+ public static void main(String[] args) throws Exception {
+ Application.setBaseTranslator(new ResourceBundleTranslator("l10n.messages"));
+ Application.setMotorSetDatabase(new ThrustCurveMotorSetDatabase(false) {
+ {
+ startLoading();
+ }
+
+ @Override
+ protected void loadMotors() {
+ }
+ });
+ Application.setPreferences(new SwingPreferences());
+
+ // Must be done after localization is initialized
+ ComponentPresetDatabase componentPresetDao = new ComponentPresetDatabase(true) {
+
+ @Override
+ protected void load() {
+ }
+
+ };
+ Application.setComponentPresetDao(componentPresetDao);
+
+ OpenRocketDocument doc = new GeneralRocketLoader().load(
+ new File("3d-Test-Junk/net/sf/openrocket/startup/al1 Apocalypse_54mmtestFr.rkt.xml"),
+ new DatabaseMotorFinder());
+
+ StorageOptions saver = new StorageOptions();
+ saver.setIncludeDecals(true);
+
+ new OpenRocketSaver().save(new File("3d-Test-Junk/net/sf/openrocket/startup/Apocalypse-ork.zip"), doc, saver);
+
+ }
+}
diff --git a/core/3d-Test-Junk/net/sf/openrocket/startup/TextureTest.java b/core/3d-Test-Junk/net/sf/openrocket/startup/TextureTest.java
index 8b69d561d..4424c0dd5 100644
--- a/core/3d-Test-Junk/net/sf/openrocket/startup/TextureTest.java
+++ b/core/3d-Test-Junk/net/sf/openrocket/startup/TextureTest.java
@@ -1,5 +1,6 @@
package net.sf.openrocket.startup;
import java.awt.BorderLayout;
+import java.io.File;
import java.lang.reflect.Method;
import javax.swing.JFrame;
@@ -10,7 +11,7 @@ import net.sf.openrocket.database.ComponentPresetDatabase;
import net.sf.openrocket.database.ThrustCurveMotorSetDatabase;
import net.sf.openrocket.document.OpenRocketDocument;
import net.sf.openrocket.file.DatabaseMotorFinder;
-import net.sf.openrocket.file.rocksim.importt.RocksimLoader;
+import net.sf.openrocket.file.GeneralRocketLoader;
import net.sf.openrocket.gui.main.componenttree.ComponentTree;
import net.sf.openrocket.gui.scalefigure.RocketPanel;
import net.sf.openrocket.gui.util.GUIUtil;
@@ -47,22 +48,14 @@ public class TextureTest {
@Override
protected void load() {
- ConcurrentComponentPresetDatabaseLoader presetLoader = new ConcurrentComponentPresetDatabaseLoader( this );
- presetLoader.load();
- try {
- presetLoader.await();
- } catch ( InterruptedException iex) {
-
- }
}
};
- componentPresetDao.load("datafiles", ".*csv");
- componentPresetDao.startLoading();
Application.setComponentPresetDao(componentPresetDao);
- OpenRocketDocument doc = new RocksimLoader().load(
- TextureTest.class.getResourceAsStream("al1 Apocalypse_54mmtestFr.rkt.xml"), new DatabaseMotorFinder());
+ OpenRocketDocument doc = new GeneralRocketLoader().load(
+ new File("3d-Test-Junk/net/sf/openrocket/startup/al1 Apocalypse_54mmtestFr.rkt.xml"),
+ new DatabaseMotorFinder());
GUIUtil.setBestLAF();
diff --git a/core/3d-Test-Junk/net/sf/openrocket/startup/al1 Apocalypse_54mmtestFr.rkt.xml b/core/3d-Test-Junk/net/sf/openrocket/startup/al1 Apocalypse_54mmtestFr.rkt.xml
index 03b59ae46..a3ba3fe10 100644
--- a/core/3d-Test-Junk/net/sf/openrocket/startup/al1 Apocalypse_54mmtestFr.rkt.xml
+++ b/core/3d-Test-Junk/net/sf/openrocket/startup/al1 Apocalypse_54mmtestFr.rkt.xml
@@ -233,7 +233,7 @@
Plastic nose cone
0.
0.
- file=(3d-Test-Junk/net/sf/openrocket/startup/Apocalypse_CONE_pointsFrfl.jpg)|position=(0,0,0)|origin=(0.5,0.5,0.5)|scale=(1,1,1)|repeat=(1)|interpolate=(0)|flipr(0)|flips(0)|flipt=(0)|preventseam=(1)
+ file=(Apocalypse_CONE_pointsFrfl.jpg)|position=(0,0,0)|origin=(0.5,0.5,0.5)|scale=(1,1,1)|repeat=(1)|interpolate=(0)|flipr(0)|flips(0)|flipt=(0)|preventseam=(1)
1.
0.
@@ -292,7 +292,7 @@
0.
0.
- file=(3d-Test-Junk/net/sf/openrocket/startup/Apocalypse_logo_HAUT_fr2.jpg)|position=(0,0,0)|origin=(0.5,0.5,0.5)|scale=(2,1,1)|repeat=(1)|interpolate=(0)|flipr(0)|flips(0)|flipt=(0)|preventseam=(0)
+ file=(Apocalypse_logo_HAUT_fr2.jpg)|position=(0,0,0)|origin=(0.5,0.5,0.5)|scale=(2,1,1)|repeat=(1)|interpolate=(0)|flipr(0)|flips(0)|flipt=(0)|preventseam=(0)
1.
0.
@@ -460,7 +460,7 @@
0.
0.
- file=(3d-Test-Junk/net/sf/openrocket/startup/Apocalypse_logo_medium2.jpg)|position=(0,0,0)|origin=(0.5,0.5,0.5)|scale=(2,1,1)|repeat=(1)|interpolate=(0)|flipr(0)|flips(0)|flipt=(0)|preventseam=(0))
+ file=(Apocalypse_logo_medium2.jpg)|position=(0,0,0)|origin=(0.5,0.5,0.5)|scale=(2,1,1)|repeat=(1)|interpolate=(0)|flipr(0)|flips(0)|flipt=(0)|preventseam=(0))
1.
0.
@@ -571,7 +571,7 @@
0.
0.
- file=(3d-Test-Junk/net/sf/openrocket/startup/Apocalypse_logo_bas_coup2.jpg)|position=(0,0,0)|origin=(0.5,0.5,0.5)|scale=(2,1,1)|repeat=(1)|interpolate=(0)|flipr(0)|flips(0)|flipt=(0)|preventseam=(0)
+ file=(Apocalypse_logo_bas_coup2.jpg)|position=(0,0,0)|origin=(0.5,0.5,0.5)|scale=(2,1,1)|repeat=(1)|interpolate=(0)|flipr(0)|flips(0)|flipt=(0)|preventseam=(0)
1.
0.
@@ -851,7 +851,7 @@
0
0.
0.
- file=(3d-Test-Junk/net/sf/openrocket/startup/fin rad-test5.jpg)|position=(0,0,0)|origin=(0.5,0.5,0.5)|scale=(1,1,1)|repeat=(0)|interpolate=(0)|flipr(0)|flips(0)|flipt=(0)|preventseam=(1)
+ file=(fin rad-test5.jpg)|position=(0,0,0)|origin=(0.5,0.5,0.5)|scale=(1,1,1)|repeat=(0)|interpolate=(0)|flipr(0)|flips(0)|flipt=(0)|preventseam=(1)
1.
0.
@@ -1095,7 +1095,7 @@
0
0.
1.5708
- file=(3d-Test-Junk/net/sf/openrocket/startup/fin rad-test5.jpg)|position=(0,0,0)|origin=(0.5,0.5,0.5)|scale=(1,1,1)|repeat=(0)|interpolate=(0)|flipr(0)|flips(0)|flipt=(0)|preventseam=(1)
+ file=(fin rad-test5.jpg)|position=(0,0,0)|origin=(0.5,0.5,0.5)|scale=(1,1,1)|repeat=(0)|interpolate=(0)|flipr(0)|flips(0)|flipt=(0)|preventseam=(1)
1.
0.
@@ -1339,7 +1339,7 @@
0
0.
3.14159
- file=(3d-Test-Junk/net/sf/openrocket/startup/fin rad-test5.jpg)|position=(0,0,0)|origin=(0.5,0.5,0.5)|scale=(1,1,1)|repeat=(0)|interpolate=(0)|flipr(0)|flips(0)|flipt=(0)|preventseam=(1)
+ file=(fin rad-test5.jpg)|position=(0,0,0)|origin=(0.5,0.5,0.5)|scale=(1,1,1)|repeat=(0)|interpolate=(0)|flipr(0)|flips(0)|flipt=(0)|preventseam=(1)
1.
0.
@@ -1583,7 +1583,7 @@
0
0.
-1.5708
- file=(3d-Test-Junk/net/sf/openrocket/startup/fin rad-test5.jpg)|position=(0,0,0)|origin=(0.5,0.5,0.5)|scale=(1,1,1)|repeat=(0)|interpolate=(0)|flipr(0)|flips(0)|flipt=(0)|preventseam=(1)
+ file=(fin rad-test5.jpg)|position=(0,0,0)|origin=(0.5,0.5,0.5)|scale=(1,1,1)|repeat=(0)|interpolate=(0)|flipr(0)|flips(0)|flipt=(0)|preventseam=(1)
1.
0.
diff --git a/core/resources/l10n/messages.properties b/core/resources/l10n/messages.properties
index fd1f9152f..5152407ca 100644
--- a/core/resources/l10n/messages.properties
+++ b/core/resources/l10n/messages.properties
@@ -1041,6 +1041,8 @@ StorageOptChooser.lbl.seconds = seconds
StorageOptChooser.rdbut.Onlyprimfig = Only primary figures
StorageOptChooser.lbl.longC1 = Store only the values shown in the summary table.
StorageOptChooser.lbl.longC2 = This results in the smallest files.
+StorageOptChooser.checkbox.IncludeDecals = Include decals
+StorageOptChooser.lbl.IncludeDecals = "Including decals will produce a compressed zip file"
StorageOptChooser.checkbox.Compfile = Compress file
StorageOptChooser.lbl.UsingComp = Using compression reduces the file size significantly.
StorageOptChooser.lbl.longD1 = An estimate on how large the resulting file would be with the present options.
diff --git a/core/src/net/sf/openrocket/appearance/AppearanceBuilder.java b/core/src/net/sf/openrocket/appearance/AppearanceBuilder.java
index 44a299b0b..2341908ec 100644
--- a/core/src/net/sf/openrocket/appearance/AppearanceBuilder.java
+++ b/core/src/net/sf/openrocket/appearance/AppearanceBuilder.java
@@ -1,7 +1,5 @@
package net.sf.openrocket.appearance;
-import java.net.URL;
-
import net.sf.openrocket.appearance.Decal.EdgeMode;
import net.sf.openrocket.util.AbstractChangeSource;
import net.sf.openrocket.util.Color;
@@ -26,7 +24,7 @@ public class AppearanceBuilder extends AbstractChangeSource {
private double centerU, centerV;
private double scaleU, scaleV;
private double rotation;
- private URL image;
+ private String image;
private Decal.EdgeMode edgeMode;
public AppearanceBuilder() {
@@ -47,7 +45,7 @@ public class AppearanceBuilder extends AbstractChangeSource {
setScale(d.getScale().x, d.getScale().y);
setRotation(d.getRotation());
setEdgeMode(d.getEdgeMode());
- setImage(d.getImageURL());
+ setImage(d.getImage());
}
// TODO Critical the rest of this!
}
@@ -199,11 +197,11 @@ public class AppearanceBuilder extends AbstractChangeSource {
fireChangeEvent();
}
- public URL getImage() {
+ public String getImage() {
return image;
}
- public void setImage(URL image) {
+ public void setImage(String image) {
this.image = image;
fireChangeEvent();
}
diff --git a/core/src/net/sf/openrocket/appearance/Decal.java b/core/src/net/sf/openrocket/appearance/Decal.java
index 84507ac35..e32c6efac 100644
--- a/core/src/net/sf/openrocket/appearance/Decal.java
+++ b/core/src/net/sf/openrocket/appearance/Decal.java
@@ -1,7 +1,5 @@
package net.sf.openrocket.appearance;
-import java.net.URL;
-
import net.sf.openrocket.startup.Application;
import net.sf.openrocket.util.Coordinate;
@@ -26,11 +24,11 @@ public class Decal {
private final Coordinate offset, center, scale;
private final double rotation;
- private final URL image;
+ private final String image;
private final EdgeMode mode;
-
+
Decal(final Coordinate offset, final Coordinate center, final Coordinate scale, final double rotation,
- final URL image, final EdgeMode mode) {
+ final String image, final EdgeMode mode) {
this.offset = offset;
this.center = center;
this.scale = scale;
@@ -59,7 +57,7 @@ public class Decal {
return mode;
}
- public URL getImageURL() {
+ public String getImage() {
return image;
}
diff --git a/core/src/net/sf/openrocket/document/DecalRegistry.java b/core/src/net/sf/openrocket/document/DecalRegistry.java
new file mode 100644
index 000000000..f74c0a45e
--- /dev/null
+++ b/core/src/net/sf/openrocket/document/DecalRegistry.java
@@ -0,0 +1,54 @@
+package net.sf.openrocket.document;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipInputStream;
+
+public class DecalRegistry {
+
+ private File baseFile;
+ private boolean isZipFile = false;
+
+ /* FIXME - Caching ?
+ private Map cache = new HashMap();
+ */
+
+ public void setBaseFile(File baseFile) {
+ this.baseFile = baseFile;
+ }
+
+ public void setIsZipFile( boolean isZipFile ) {
+ this.isZipFile = isZipFile;
+ }
+
+ public InputStream getDecal( String name ) throws FileNotFoundException, IOException {
+ /* FIXME - Caching?
+ byte[] bytes = cache.get(name);
+ if ( bytes != null ) {
+ return new ByteArrayInputStream(bytes);
+ }
+ */
+ if ( isZipFile ) {
+ ZipInputStream zis = new ZipInputStream(new FileInputStream(baseFile));
+ ZipEntry entry = zis.getNextEntry();
+ while ( entry != null ) {
+ if ( entry.getName().equals(name) ) {
+ return zis;
+ }
+ entry = zis.getNextEntry();
+ }
+ }
+
+ if ( baseFile != null ) {
+ File decal = new File(baseFile.getParentFile(), name);
+ // FIXME - update cache
+ return new FileInputStream(decal);
+ }
+ throw new FileNotFoundException( "Unable to locate decal for name " + name );
+ }
+
+}
diff --git a/core/src/net/sf/openrocket/document/OpenRocketDocument.java b/core/src/net/sf/openrocket/document/OpenRocketDocument.java
index 12535220b..4a3851d69 100644
--- a/core/src/net/sf/openrocket/document/OpenRocketDocument.java
+++ b/core/src/net/sf/openrocket/document/OpenRocketDocument.java
@@ -59,8 +59,6 @@ public class OpenRocketDocument implements ComponentChangeListener {
private final ArrayList simulations = new ArrayList();
private ArrayList customExpressions = new ArrayList();
-
-
/*
* The undo/redo variables and mechanism are documented in doc/undo-redo-flow.*
*/
@@ -85,6 +83,7 @@ public class OpenRocketDocument implements ComponentChangeListener {
private String nextDescription = null;
private String storedDescription = null;
+ private DecalRegistry decalRegistry = new DecalRegistry();
private ArrayList undoRedoListeners = new ArrayList(2);
@@ -176,6 +175,15 @@ public class OpenRocketDocument implements ComponentChangeListener {
}
+ public DecalRegistry getDecalRegistry() {
+ return decalRegistry;
+ }
+
+
+ public void setDecalRegistry(DecalRegistry decalRegistry) {
+ this.decalRegistry = decalRegistry;
+ }
+
public File getFile() {
return file;
}
diff --git a/core/src/net/sf/openrocket/file/AbstractRocketLoader.java b/core/src/net/sf/openrocket/file/AbstractRocketLoader.java
index f0c7464df..c4804e3bd 100644
--- a/core/src/net/sf/openrocket/file/AbstractRocketLoader.java
+++ b/core/src/net/sf/openrocket/file/AbstractRocketLoader.java
@@ -15,32 +15,6 @@ public abstract class AbstractRocketLoader implements RocketLoader {
protected final WarningSet warnings = new WarningSet();
- /**
- * Loads a rocket from the specified File object.
- */
- @Override
- public final OpenRocketDocument load(File source, MotorFinder motorFinder) throws RocketLoadException {
- warnings.clear();
- InputStream stream = null;
-
- try {
-
- stream = new BufferedInputStream(new FileInputStream(source));
- return load(stream, motorFinder);
-
- } catch (FileNotFoundException e) {
- throw new RocketLoadException("File not found: " + source);
- } finally {
- if (stream != null) {
- try {
- stream.close();
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- }
- }
-
/**
* Loads a rocket from the specified InputStream.
*/
diff --git a/core/src/net/sf/openrocket/file/GeneralRocketLoader.java b/core/src/net/sf/openrocket/file/GeneralRocketLoader.java
index da6858d89..4a2773a42 100644
--- a/core/src/net/sf/openrocket/file/GeneralRocketLoader.java
+++ b/core/src/net/sf/openrocket/file/GeneralRocketLoader.java
@@ -1,6 +1,8 @@
package net.sf.openrocket.file;
import java.io.BufferedInputStream;
+import java.io.File;
+import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.Charset;
@@ -9,6 +11,7 @@ import java.util.zip.GZIPInputStream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
+import net.sf.openrocket.aerodynamics.WarningSet;
import net.sf.openrocket.document.OpenRocketDocument;
import net.sf.openrocket.file.openrocket.importt.OpenRocketLoader;
import net.sf.openrocket.file.rocksim.importt.RocksimLoader;
@@ -23,8 +26,10 @@ import net.sf.openrocket.util.TextUtil;
*
* @author Sampo Niskanen
*/
-public class GeneralRocketLoader extends AbstractRocketLoader {
+public class GeneralRocketLoader {
+ protected final WarningSet warnings = new WarningSet();
+
private static final int READ_BYTES = 300;
private static final byte[] GZIP_SIGNATURE = { 31, -117 }; // 0x1f, 0x8b
@@ -36,7 +41,46 @@ public class GeneralRocketLoader extends AbstractRocketLoader {
private final RocksimLoader rocksimLoader = new RocksimLoader();
- @Override
+ /**
+ * Loads a rocket from the specified File object.
+ */
+ public final OpenRocketDocument load(File source, MotorFinder motorFinder) throws RocketLoadException {
+ warnings.clear();
+ InputStream stream = null;
+
+ try {
+
+ stream = new BufferedInputStream(new FileInputStream(source));
+ OpenRocketDocument doc = load(stream, source, motorFinder);
+ return doc;
+
+ } catch (Exception e) {
+ throw new RocketLoadException("Exception loading file: " + source,e);
+ } finally {
+ if (stream != null) {
+ try {
+ stream.close();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+ }
+
+ public final OpenRocketDocument load(InputStream source, File file, MotorFinder motorFinder) throws RocketLoadException {
+ try {
+ OpenRocketDocument doc = loadFromStream(source, motorFinder );
+ doc.getDecalRegistry().setBaseFile(file);
+ return doc;
+ } catch (Exception e) {
+ throw new RocketLoadException("Exception loading stream", e);
+ }
+ }
+
+ public final WarningSet getWarnings() {
+ return warnings;
+ }
+
protected OpenRocketDocument loadFromStream(InputStream source, MotorFinder motorFinder) throws IOException,
RocketLoadException {
@@ -56,12 +100,14 @@ public class GeneralRocketLoader extends AbstractRocketLoader {
throw new RocketLoadException("Unsupported or corrupt file.");
}
+
// Detect the appropriate loader
// Check for GZIP
if (buffer[0] == GZIP_SIGNATURE[0] && buffer[1] == GZIP_SIGNATURE[1]) {
OpenRocketDocument doc = loadFromStream(new GZIPInputStream(source), motorFinder);
doc.getDefaultStorageOptions().setCompressionEnabled(true);
+ doc.getDecalRegistry().setIsZipFile(false);
return doc;
}
@@ -77,6 +123,7 @@ public class GeneralRocketLoader extends AbstractRocketLoader {
if (entry.getName().matches(".*\\.[oO][rR][kK]$")) {
OpenRocketDocument doc = loadFromStream(in, motorFinder);
doc.getDefaultStorageOptions().setCompressionEnabled(true);
+ doc.getDecalRegistry().setIsZipFile(true);
return doc;
} else if ( entry.getName().matches(".*\\.[rR][kK][tT]$")) {
OpenRocketDocument doc = loadFromStream(in, motorFinder);
@@ -91,7 +138,9 @@ public class GeneralRocketLoader extends AbstractRocketLoader {
if (buffer[i] == OPENROCKET_SIGNATURE[match]) {
match++;
if (match == OPENROCKET_SIGNATURE.length) {
- return loadUsing(source, openRocketLoader, motorFinder);
+ OpenRocketDocument doc = loadUsing(openRocketLoader, source, motorFinder);
+ doc.getDecalRegistry().setIsZipFile(false);
+ return doc;
}
} else {
match = 0;
@@ -100,12 +149,14 @@ public class GeneralRocketLoader extends AbstractRocketLoader {
byte[] typeIdentifier = ArrayUtils.copyOf(buffer, ROCKSIM_SIGNATURE.length);
if (Arrays.equals(ROCKSIM_SIGNATURE, typeIdentifier)) {
- return loadUsing(source, rocksimLoader, motorFinder);
+ OpenRocketDocument doc = loadUsing(rocksimLoader, source, motorFinder);
+ doc.getDecalRegistry().setIsZipFile(false);
+ return doc;
}
throw new RocketLoadException("Unsupported or corrupt file.");
}
- private OpenRocketDocument loadUsing(InputStream source, RocketLoader loader, MotorFinder motorFinder)
+ private OpenRocketDocument loadUsing(RocketLoader loader, InputStream source, MotorFinder motorFinder)
throws RocketLoadException {
warnings.clear();
OpenRocketDocument doc = loader.load(source, motorFinder);
diff --git a/core/src/net/sf/openrocket/file/RocketLoader.java b/core/src/net/sf/openrocket/file/RocketLoader.java
index 81bcb0ac8..cbe9c724c 100644
--- a/core/src/net/sf/openrocket/file/RocketLoader.java
+++ b/core/src/net/sf/openrocket/file/RocketLoader.java
@@ -1,6 +1,5 @@
package net.sf.openrocket.file;
-import java.io.File;
import java.io.InputStream;
import net.sf.openrocket.aerodynamics.WarningSet;
@@ -8,8 +7,6 @@ import net.sf.openrocket.document.OpenRocketDocument;
public interface RocketLoader {
- public OpenRocketDocument load(File source, MotorFinder motorFinder) throws RocketLoadException;
-
public OpenRocketDocument load(InputStream source, MotorFinder motorFinder) throws RocketLoadException;
public WarningSet getWarnings();
diff --git a/core/src/net/sf/openrocket/file/RocketSaver.java b/core/src/net/sf/openrocket/file/RocketSaver.java
index 5c18bb8de..6ee50071b 100644
--- a/core/src/net/sf/openrocket/file/RocketSaver.java
+++ b/core/src/net/sf/openrocket/file/RocketSaver.java
@@ -36,7 +36,7 @@ public abstract class RocketSaver {
throws IOException {
OutputStream s = new BufferedOutputStream(new FileOutputStream(dest));
try {
- save(s, document, options);
+ save(dest.getName(), s, document, options);
} finally {
s.close();
}
@@ -50,8 +50,8 @@ public abstract class RocketSaver {
* @param doc the document to save.
* @throws IOException in case of an I/O error.
*/
- public final void save(OutputStream dest, OpenRocketDocument doc) throws IOException {
- save(dest, doc, doc.getDefaultStorageOptions());
+ public final void save(String fileName, OutputStream dest, OpenRocketDocument doc) throws IOException {
+ save(fileName, dest, doc, doc.getDefaultStorageOptions());
}
@@ -63,7 +63,7 @@ public abstract class RocketSaver {
* @param options the storage options.
* @throws IOException in case of an I/O error.
*/
- public abstract void save(OutputStream dest, OpenRocketDocument doc,
+ public abstract void save(String fileName, OutputStream dest, OpenRocketDocument doc,
StorageOptions options) throws IOException;
diff --git a/core/src/net/sf/openrocket/file/openrocket/OpenRocketSaver.java b/core/src/net/sf/openrocket/file/openrocket/OpenRocketSaver.java
index 0a5e04790..fb6a411d5 100644
--- a/core/src/net/sf/openrocket/file/openrocket/OpenRocketSaver.java
+++ b/core/src/net/sf/openrocket/file/openrocket/OpenRocketSaver.java
@@ -1,17 +1,26 @@
package net.sf.openrocket.file.openrocket;
import java.io.BufferedWriter;
+import java.io.File;
import java.io.IOException;
+import java.io.InputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.util.ArrayList;
+import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
+import java.util.Map;
import java.util.zip.GZIPOutputStream;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipOutputStream;
import net.sf.openrocket.aerodynamics.Warning;
+import net.sf.openrocket.appearance.Appearance;
+import net.sf.openrocket.appearance.AppearanceBuilder;
+import net.sf.openrocket.appearance.Decal;
import net.sf.openrocket.document.OpenRocketDocument;
import net.sf.openrocket.document.Simulation;
import net.sf.openrocket.document.StorageOptions;
@@ -39,22 +48,22 @@ import net.sf.openrocket.util.TextUtil;
public class OpenRocketSaver extends RocketSaver {
private static final LogHelper log = Application.getLogger();
-
-
+
+
/**
* Divisor used in converting an integer version to the point-represented version.
* The integer version divided by this value is the major version and the remainder is
* the minor version. For example 101 corresponds to file version "1.1".
*/
public static final int FILE_VERSION_DIVISOR = 100;
-
-
+
+
private static final String OPENROCKET_CHARSET = "UTF-8";
-
+
private static final String METHOD_PACKAGE = "net.sf.openrocket.file.openrocket.savers";
private static final String METHOD_SUFFIX = "Saver";
-
-
+
+
// Estimated storage used by different portions
// These have been hand-estimated from saved files
private static final int BYTES_PER_COMPONENT_UNCOMPRESSED = 590;
@@ -63,44 +72,163 @@ public class OpenRocketSaver extends RocketSaver {
private static final int BYTES_PER_SIMULATION_COMPRESSED = 100;
private static final int BYTES_PER_DATAPOINT_UNCOMPRESSED = 350;
private static final int BYTES_PER_DATAPOINT_COMPRESSED = 100;
-
-
+
+
private int indent;
private Writer dest;
-
+
@Override
- public void save(OutputStream output, OpenRocketDocument document, StorageOptions options)
+ public void save(String fileName, OutputStream output, OpenRocketDocument document, StorageOptions options)
throws IOException {
+
+ if (!options.isIncludeDecals()) {
+ saveInternal(output,document,options);
+ return;
+ }
+
+ // tweak the options - no need to double compress:
+ options.setCompressionEnabled(false);
+
+ ZipOutputStream zos = new ZipOutputStream(output);
+ zos.setLevel(9);
+
+ /* if we want a directory ...
+ String path = fileName;
+ int dotlocation = fileName.lastIndexOf('.');
+ if ( dotlocation > 1 ) {
+ path = fileName.substring(dotlocation);
+ }
+ */
+
+ // grab the set of decal images.
+ Map decals = new HashMap();
+ Map decalNameNormalization = new HashMap();
+ try {
+ for( RocketComponent c : document.getRocket() ) {
+
+ if ( c.getAppearance() == null ) {
+ continue;
+ }
+
+ Appearance ap = c.getAppearance();
+
+ if ( ap.getTexture() == null ) {
+ continue;
+ }
+
+ Decal decal = ap.getTexture();
+
+ String decalName = decal.getImage();
+
+ if ( decals.containsKey(decalName) ) {
+ continue;
+ }
+
+ InputStream is = document.getDecalRegistry().getDecal(decalName);
+
+ decals.put(decalName, is);
+
+ // Normalize the name:
+ File fname = new File(decalName);
+ String newName = "decals/" + fname.getName();
+
+ decalNameNormalization.put(decalName, newName);
+
+ }
+ }
+ catch (IOException ex) {
+ for ( InputStream is: decals.values() ) {
+ try {
+ is.close();
+ }
+ catch ( Throwable t ) {
+ }
+ }
+ throw ex;
+ }
+ // Now we have to loop through all the components and update their names.
+
+ for( RocketComponent c : document.getRocket() ) {
+
+ if ( c.getAppearance() == null ) {
+ continue;
+ }
+
+ Appearance ap = c.getAppearance();
+
+ if ( ap.getTexture() == null ) {
+ continue;
+ }
+
+ AppearanceBuilder builder = new AppearanceBuilder(ap);
+
+ builder.setImage( decalNameNormalization.get(ap.getTexture().getImage()));
+
+ c.setAppearance(builder.getAppearance());
+
+ }
+
+ // Fixme - should probably be the same name? Should we put everything in a directory?
+ ZipEntry mainFile = new ZipEntry("rocket.ork");
+ zos.putNextEntry(mainFile);
+ saveInternal(zos,document,options);
+ zos.closeEntry();
+
+ // Now we write out all the decal images files.
+
+ for( Map.Entry image : decals.entrySet() ) {
+
+ String newName = decalNameNormalization.get(image.getKey());
+ ZipEntry decal = new ZipEntry(newName);
+ zos.putNextEntry(decal);
+
+ InputStream is = image.getValue();
+ int bytesRead = 0;
+ byte[] buffer = new byte[2048];
+ while( (bytesRead = is.read(buffer)) > 0 ) {
+ zos.write(buffer, 0, bytesRead);
+ }
+ zos.closeEntry();
+ }
+
+ zos.flush();
+ zos.close();
+
+ }
+
+ public void saveInternal(OutputStream output, OpenRocketDocument document, StorageOptions options)
+ throws IOException {
+
log.info("Saving .ork file");
-
+
if (options.isCompressionEnabled()) {
log.debug("Enabling compression");
output = new GZIPOutputStream(output);
}
-
+
dest = new BufferedWriter(new OutputStreamWriter(output, OPENROCKET_CHARSET));
-
+
// Select file version number
final int fileVersion = calculateNecessaryFileVersion(document, options);
final String fileVersionString =
(fileVersion / FILE_VERSION_DIVISOR) + "." + (fileVersion % FILE_VERSION_DIVISOR);
log.debug("Storing file version " + fileVersionString);
-
-
+
+
this.indent = 0;
-
-
+
+
writeln("");
writeln("");
indent++;
-
+
// Recursively save the rocket structure
saveComponent(document.getRocket());
-
+
writeln("");
-
+
// Save custom expressions;
saveCustomDatatypes(document);
@@ -168,6 +296,8 @@ public class OpenRocketSaver extends RocketSaver {
long size = 0;
+ // FIXME - estimate decals
+
// Size per component
int componentCount = 0;
Rocket rocket = doc.getRocket();
@@ -176,20 +306,20 @@ public class OpenRocketSaver extends RocketSaver {
iterator.next();
componentCount++;
}
-
+
if (options.isCompressionEnabled())
size += componentCount * BYTES_PER_COMPONENT_COMPRESSED;
else
size += componentCount * BYTES_PER_COMPONENT_UNCOMPRESSED;
-
-
+
+
// Size per simulation
if (options.isCompressionEnabled())
size += doc.getSimulationCount() * BYTES_PER_SIMULATION_COMPRESSED;
else
size += doc.getSimulationCount() * BYTES_PER_SIMULATION_UNCOMPRESSED;
-
-
+
+
// Size per flight data point
int pointCount = 0;
double timeSkip = options.getSimulationTimeSkip();
@@ -203,16 +333,16 @@ public class OpenRocketSaver extends RocketSaver {
}
}
}
-
+
if (options.isCompressionEnabled())
size += pointCount * BYTES_PER_DATAPOINT_COMPRESSED;
else
size += pointCount * BYTES_PER_DATAPOINT_UNCOMPRESSED;
-
+
return size;
}
-
-
+
+
/**
* Determine which file version is required in order to store all the features of the
* current design. By default the oldest version that supports all the necessary features
@@ -224,6 +354,9 @@ public class OpenRocketSaver extends RocketSaver {
*/
private int calculateNecessaryFileVersion(OpenRocketDocument document, StorageOptions opts) {
/*
+ * File version 1.6 is required for:
+ * - saving files using appearances and textures
+ *
* File version 1.5 is requires for:
* - saving designs using ComponentPrests
* - recovery device deployment on lower stage separation
@@ -239,14 +372,21 @@ public class OpenRocketSaver extends RocketSaver {
*
* Otherwise use version 1.0.
*/
-
+
+ // Search the rocket for any Appearnces (version 1.6)
+ for (RocketComponent c : document.getRocket()) {
+ if (c.getAppearance() != null) {
+ return FILE_VERSION_DIVISOR + 6;
+ }
+ }
+
// Search the rocket for any ComponentPresets (version 1.5)
for (RocketComponent c : document.getRocket()) {
if (c.getPresetComponent() != null) {
return FILE_VERSION_DIVISOR + 5;
}
}
-
+
// Search for recovery device deployment type LOWER_STAGE_SEPARATION (version 1.5)
for (RocketComponent c : document.getRocket()) {
if (c instanceof RecoveryDevice) {
@@ -260,17 +400,17 @@ public class OpenRocketSaver extends RocketSaver {
if (!document.getCustomExpressions().isEmpty()) {
return FILE_VERSION_DIVISOR + 5;
}
-
+
// Check if design has simulations defined (version 1.4)
if (document.getSimulationCount() > 0) {
return FILE_VERSION_DIVISOR + 4;
}
-
+
// Check for motor definitions (version 1.4)
for (RocketComponent c : document.getRocket()) {
if (!(c instanceof MotorMount))
continue;
-
+
MotorMount mount = (MotorMount) c;
for (String id : document.getRocket().getMotorConfigurationIDs()) {
if (mount.getMotor(id) != null) {
@@ -278,7 +418,7 @@ public class OpenRocketSaver extends RocketSaver {
}
}
}
-
+
// Check for fin tabs (version 1.1)
for (RocketComponent c : document.getRocket()) {
// Check for fin tabs
@@ -289,7 +429,7 @@ public class OpenRocketSaver extends RocketSaver {
return FILE_VERSION_DIVISOR + 1;
}
}
-
+
// Check for components attached to tube coupler
if (c instanceof TubeCoupler) {
if (c.getChildCount() > 0) {
@@ -297,45 +437,45 @@ public class OpenRocketSaver extends RocketSaver {
}
}
}
-
+
// Default (version 1.0)
return FILE_VERSION_DIVISOR + 0;
}
-
-
-
+
+
+
@SuppressWarnings("unchecked")
private void saveComponent(RocketComponent component) throws IOException {
-
+
log.debug("Saving component " + component.getComponentName());
-
+
Reflection.Method m = Reflection.findMethod(METHOD_PACKAGE, component, METHOD_SUFFIX,
"getElements", RocketComponent.class);
if (m == null) {
throw new BugException("Unable to find saving class for component " +
component.getComponentName());
}
-
+
// Get the strings to save
List list = (List) m.invokeStatic(component);
int length = list.size();
-
+
if (length == 0) // Nothing to do
return;
-
+
if (length < 2) {
throw new RuntimeException("BUG, component data length less than two lines.");
}
-
+
// Open element
writeln(list.get(0));
indent++;
-
+
// Write parameters
for (int i = 1; i < length - 1; i++) {
writeln(list.get(i));
}
-
+
// Recursively write subcomponents
if (component.getChildCount() > 0) {
writeln("");
@@ -351,25 +491,25 @@ public class OpenRocketSaver extends RocketSaver {
indent--;
writeln("");
}
-
+
// Close element
indent--;
writeln(list.get(length - 1));
}
-
-
+
+
private void saveSimulation(Simulation simulation, double timeSkip) throws IOException {
SimulationOptions cond = simulation.getOptions();
-
+
writeln("");
indent++;
-
+
writeln("" + escapeXML(simulation.getName()) + "");
// TODO: MEDIUM: Other simulators/calculators
-
+
writeln("RK4Simulator");
writeln("BarrowmanCalculator");
-
+
writeln("");
indent++;
@@ -383,7 +523,7 @@ public class OpenRocketSaver extends RocketSaver {
writeElement("launchlatitude", cond.getLaunchLatitude());
writeElement("launchlongitude", cond.getLaunchLongitude());
writeElement("geodeticmethod", cond.getGeodeticComputation().name().toLowerCase(Locale.ENGLISH));
-
+
if (cond.isISAAtmosphere()) {
writeln("");
} else {
@@ -394,19 +534,19 @@ public class OpenRocketSaver extends RocketSaver {
indent--;
writeln("");
}
-
+
writeElement("timestep", cond.getTimeStep());
-
+
indent--;
writeln("");
-
-
+
+
for (String s : simulation.getSimulationListeners()) {
writeElement("listener", escapeXML(s));
}
-
+
// Write basic simulation data
-
+
FlightData data = simulation.getSimulatedData();
if (data != null) {
String str = "";
writeln(str);
indent++;
-
+
for (Warning w : data.getWarningSet()) {
writeElement("warning", escapeXML(w.toString()));
}
-
+
// Check whether to store data
if (simulation.getStatus() == Simulation.Status.EXTERNAL) // Always store external data
timeSkip = 0;
-
+
if (timeSkip != StorageOptions.SIMULATION_DATA_NONE) {
for (int i = 0; i < data.getBranchCount(); i++) {
FlightDataBranch branch = data.getBranch(i);
saveFlightDataBranch(branch, timeSkip);
}
}
-
+
indent--;
writeln("");
}
-
+
indent--;
writeln("");
-
+
}
-
-
-
+
+
+
private void saveFlightDataBranch(FlightDataBranch branch, double timeSkip)
throws IOException {
double previousTime = -100000;
-
+
if (branch == null)
return;
-
+
// Retrieve the types from the branch
FlightDataType[] types = branch.getTypes();
-
+
if (types.length == 0)
return;
-
+
// Retrieve the data from the branch
List> data = new ArrayList>(types.length);
for (int i = 0; i < types.length; i++) {
data.add(branch.get(types[i]));
}
List timeData = branch.get(FlightDataType.TYPE_TIME);
-
+
// Build the tag
StringBuilder sb = new StringBuilder();
sb.append("");
writeln(sb.toString());
indent++;
-
+
// Write events
for (FlightEvent event : branch.getEvents()) {
writeln("");
}
-
+
// Write the data
int length = branch.getLength();
if (length > 0) {
writeDataPointString(data, 0, sb);
previousTime = timeData.get(0);
}
-
+
for (int i = 1; i < length - 1; i++) {
if (timeData != null) {
if (Math.abs(timeData.get(i) - previousTime - timeSkip) < Math.abs(timeData.get(i + 1) - previousTime - timeSkip)) {
@@ -527,61 +667,61 @@ public class OpenRocketSaver extends RocketSaver {
writeDataPointString(data, i, sb);
}
}
-
+
if (length > 1) {
writeDataPointString(data, length - 1, sb);
}
-
+
indent--;
writeln("");
}
-
-
-
+
+
+
/* TODO: LOW: This is largely duplicated from above! */
private int countFlightDataBranchPoints(FlightDataBranch branch, double timeSkip) {
int count = 0;
-
+
double previousTime = -100000;
-
+
if (branch == null)
return 0;
-
+
// Retrieve the types from the branch
FlightDataType[] types = branch.getTypes();
-
+
if (types.length == 0)
return 0;
-
+
List timeData = branch.get(FlightDataType.TYPE_TIME);
if (timeData == null) {
// If time data not available, store all points
return branch.getLength();
}
-
+
// Write the data
int length = branch.getLength();
if (length > 0) {
count++;
previousTime = timeData.get(0);
}
-
+
for (int i = 1; i < length - 1; i++) {
if (Math.abs(timeData.get(i) - previousTime - timeSkip) < Math.abs(timeData.get(i + 1) - previousTime - timeSkip)) {
count++;
previousTime = timeData.get(i);
}
}
-
+
if (length > 1) {
count++;
}
-
+
return count;
}
-
-
-
+
+
+
private void writeDataPointString(List> data, int index, StringBuilder sb)
throws IOException {
sb.setLength(0);
@@ -594,17 +734,17 @@ public class OpenRocketSaver extends RocketSaver {
sb.append("");
writeln(sb.toString());
}
-
-
-
+
+
+
private void writeElement(String element, Object content) throws IOException {
if (content == null)
content = "";
writeln("<" + element + ">" + content + "" + element + ">");
}
-
-
-
+
+
+
private void writeln(String str) throws IOException {
if (str.length() == 0) {
dest.write("\n");
@@ -616,10 +756,10 @@ public class OpenRocketSaver extends RocketSaver {
s = s + str + "\n";
dest.write(s);
}
-
-
-
-
+
+
+
+
/**
* Return the XML equivalent of an enum name.
*
@@ -629,5 +769,5 @@ public class OpenRocketSaver extends RocketSaver {
public static String enumToXMLName(Enum> e) {
return e.name().toLowerCase(Locale.ENGLISH).replace("_", "");
}
-
+
}
diff --git a/core/src/net/sf/openrocket/file/openrocket/importt/OpenRocketLoader.java b/core/src/net/sf/openrocket/file/openrocket/importt/OpenRocketLoader.java
index 0915a9353..55a0b786f 100644
--- a/core/src/net/sf/openrocket/file/openrocket/importt/OpenRocketLoader.java
+++ b/core/src/net/sf/openrocket/file/openrocket/importt/OpenRocketLoader.java
@@ -13,6 +13,8 @@ import java.util.regex.Pattern;
import net.sf.openrocket.aerodynamics.Warning;
import net.sf.openrocket.aerodynamics.WarningSet;
+import net.sf.openrocket.appearance.AppearanceBuilder;
+import net.sf.openrocket.appearance.Decal.EdgeMode;
import net.sf.openrocket.database.Databases;
import net.sf.openrocket.document.OpenRocketDocument;
import net.sf.openrocket.document.Simulation;
@@ -166,7 +168,7 @@ public class OpenRocketLoader extends AbstractRocketLoader {
class DocumentConfig {
/* Remember to update OpenRocketSaver as well! */
- public static final String[] SUPPORTED_VERSIONS = { "1.0", "1.1", "1.2", "1.3", "1.4", "1.5" };
+ public static final String[] SUPPORTED_VERSIONS = { "1.0", "1.1", "1.2", "1.3", "1.4", "1.5", "1.6"};
/**
* Divisor used in converting an integer version to the point-represented version.
@@ -744,6 +746,7 @@ class DatatypeHandler extends AbstractElementHandler {
}
class CustomExpressionHandler extends AbstractElementHandler {
+ @SuppressWarnings("unused")
private final DocumentLoadingContext context;
private final OpenRocketContentHandler contentHandler;
public CustomExpression currentExpression;
@@ -854,6 +857,9 @@ class ComponentParameterHandler extends AbstractElementHandler {
if (element.equals("subcomponents")) {
return new ComponentHandler(component, context);
}
+ if ( element.equals("appearance")) {
+ return new AppearanceHandler(component,context);
+ }
if (element.equals("motormount")) {
if (!(component instanceof MotorMount)) {
warnings.add(Warning.fromString("Illegal component defined as motor mount."));
@@ -885,7 +891,8 @@ class ComponentParameterHandler extends AbstractElementHandler {
String content, WarningSet warnings) {
if (element.equals("subcomponents") || element.equals("motormount") ||
- element.equals("finpoints") || element.equals("motorconfiguration")) {
+ element.equals("finpoints") || element.equals("motorconfiguration") ||
+ element.equals("appearance")) {
return;
}
@@ -913,6 +920,95 @@ class ComponentParameterHandler extends AbstractElementHandler {
}
}
+class AppearanceHandler extends AbstractElementHandler {
+ @SuppressWarnings("unused")
+ private final DocumentLoadingContext context;
+ private final RocketComponent component;
+
+ private final AppearanceBuilder builder = new AppearanceBuilder();
+ private boolean isInDecal = false;
+ public AppearanceHandler( RocketComponent component, DocumentLoadingContext context ) {
+ this.context = context;
+ this.component = component;
+ }
+ @Override
+ public ElementHandler openElement(String element,HashMap attributes, WarningSet warnings)
+ throws SAXException {
+ if ( "decal".equals(element) ) {
+ String name = attributes.remove("name");
+ builder.setImage(name);
+ double rotation = Double.parseDouble(attributes.remove("rotation"));
+ builder.setRotation(rotation);
+ String edgeModeName = attributes.remove("edgemode");
+ EdgeMode edgeMode = EdgeMode.valueOf(edgeModeName);
+ builder.setEdgeMode(edgeMode);
+ isInDecal = true;
+ return this;
+ }
+ return PlainTextHandler.INSTANCE;
+ }
+ @Override
+ public void closeElement(String element,HashMap attributes, String content, WarningSet warnings) throws SAXException {
+ if ( "ambient".equals(element) ) {
+ int red = Integer.parseInt(attributes.get("red"));
+ int green = Integer.parseInt(attributes.get("green"));
+ int blue = Integer.parseInt(attributes.get("blue"));
+ builder.setAmbient( new Color(red,green,blue));
+ return;
+ }
+ if ( "diffuse".equals(element) ) {
+ int red = Integer.parseInt(attributes.get("red"));
+ int green = Integer.parseInt(attributes.get("green"));
+ int blue = Integer.parseInt(attributes.get("blue"));
+ builder.setDiffuse( new Color(red,green,blue));
+ return;
+ }
+ if ( "specular".equals(element) ) {
+ int red = Integer.parseInt(attributes.get("red"));
+ int green = Integer.parseInt(attributes.get("green"));
+ int blue = Integer.parseInt(attributes.get("blue"));
+ builder.setSpecular( new Color(red,green,blue));
+ return;
+ }
+ if ( isInDecal && "center".equals(element) ) {
+ double x = Double.parseDouble(attributes.get("x"));
+ double y = Double.parseDouble(attributes.get("y"));
+ builder.setCenter(x,y);
+ return;
+ }
+ if ( isInDecal && "offset".equals(element) ) {
+ double x = Double.parseDouble(attributes.get("x"));
+ double y = Double.parseDouble(attributes.get("y"));
+ builder.setOffset(x,y);
+ return;
+ }
+ if ( isInDecal && "scale".equals(element) ) {
+ double x = Double.parseDouble(attributes.get("x"));
+ double y = Double.parseDouble(attributes.get("y"));
+ builder.setScale(x,y);
+ return;
+ }
+ if( isInDecal && "decal".equals(element) ) {
+ isInDecal = false;
+ return;
+ }
+
+ super.closeElement(element, attributes, content, warnings);
+ }
+
+ @Override
+ public void endHandler(String element, HashMap attributes,
+ String content, WarningSet warnings) throws SAXException {
+ if ( "decal".equals(element) ) {
+ isInDecal = false;
+ return;
+ }
+ component.setAppearance(builder.getAppearance());
+ super.endHandler(element, attributes, content, warnings);
+ }
+
+}
+
/**
* A handler that reads the specifications within the freeformfinset's
diff --git a/core/src/net/sf/openrocket/file/openrocket/savers/RocketComponentSaver.java b/core/src/net/sf/openrocket/file/openrocket/savers/RocketComponentSaver.java
index a4892902a..93601af8a 100644
--- a/core/src/net/sf/openrocket/file/openrocket/savers/RocketComponentSaver.java
+++ b/core/src/net/sf/openrocket/file/openrocket/savers/RocketComponentSaver.java
@@ -5,6 +5,9 @@ import java.util.Collections;
import java.util.List;
import java.util.Locale;
+import net.sf.openrocket.appearance.Appearance;
+import net.sf.openrocket.appearance.Decal;
+import net.sf.openrocket.appearance.Decal.EdgeMode;
import net.sf.openrocket.file.RocketSaver;
import net.sf.openrocket.l10n.Translator;
import net.sf.openrocket.material.Material;
@@ -18,51 +21,73 @@ import net.sf.openrocket.rocketcomponent.RocketComponent;
import net.sf.openrocket.startup.Application;
import net.sf.openrocket.util.BugException;
import net.sf.openrocket.util.Color;
+import net.sf.openrocket.util.Coordinate;
import net.sf.openrocket.util.LineStyle;
-
public class RocketComponentSaver {
private static final Translator trans = Application.getTranslator();
-
+
protected RocketComponentSaver() {
// Prevent instantiation from outside the package
}
-
+
protected void addParams(net.sf.openrocket.rocketcomponent.RocketComponent c, List elements) {
elements.add("" + RocketSaver.escapeXML(c.getName()) + "");
-
+
ComponentPreset preset = c.getPresetComponent();
if (preset != null) {
elements.add("");
}
-
-
+
+ Appearance ap = c.getAppearance();
+ if ( ap != null ) {
+ elements.add("");
+ Color ambient = ap.getAmbient();
+ emitColor("ambient",elements,ambient);
+ Color diffuse = ap.getDiffuse();
+ emitColor("diffuse",elements,diffuse);
+ Color specular = ap.getSpecular();
+ emitColor("specular",elements,specular);
+ Decal decal = ap.getTexture();
+ if ( decal != null ) {
+ String name = decal.getImage();
+ double rotation = decal.getRotation();
+ EdgeMode edgeMode = decal.getEdgeMode();
+ elements.add("");
+ Coordinate center = decal.getCenter();
+ elements.add("");
+ Coordinate offset = decal.getOffset();
+ elements.add("");
+ Coordinate scale = decal.getScale();
+ elements.add("");
+ elements.add("");
+ }
+ elements.add("");
+ }
+
// Save color and line style if significant
if (!(c instanceof Rocket || c instanceof ComponentAssembly)) {
Color color = c.getColor();
- if (color != null) {
- elements.add("");
- }
-
+ emitColor("color", elements, color);
+
LineStyle style = c.getLineStyle();
if (style != null) {
// Type names currently equivalent to the enum names except for case.
elements.add("" + style.name().toLowerCase(Locale.ENGLISH) + "");
}
}
-
-
+
+
// Save position unless "AFTER"
if (c.getRelativePosition() != RocketComponent.Position.AFTER) {
// The type names are currently equivalent to the enum names except for case.
String type = c.getRelativePosition().name().toLowerCase(Locale.ENGLISH);
elements.add("" + c.getPositionValue() + "");
}
-
-
+
+
// Overrides
boolean overridden = false;
if (c.isMassOverridden()) {
@@ -77,26 +102,26 @@ public class RocketComponentSaver {
elements.add("" + c.getOverrideSubcomponents()
+ "");
}
-
-
+
+
// Comment
if (c.getComment().length() > 0) {
elements.add("" + RocketSaver.escapeXML(c.getComment()) + "");
}
-
+
}
-
-
-
-
+
+
+
+
protected final String materialParam(Material mat) {
return materialParam("material", mat);
}
-
-
+
+
protected final String materialParam(String tag, Material mat) {
String str = "<" + tag;
-
+
switch (mat.getType()) {
case LINE:
str += " type=\"line\"";
@@ -110,29 +135,29 @@ public class RocketComponentSaver {
default:
throw new BugException("Unknown material type: " + mat.getType());
}
-
+
String baseName = trans.getBaseText("material", mat.getName());
-
+
return str + " density=\"" + mat.getDensity() + "\">" + RocketSaver.escapeXML(baseName) + "" + tag + ">";
}
-
-
+
+
protected final List motorMountParams(MotorMount mount) {
if (!mount.isMotorMount())
return Collections.emptyList();
-
+
String[] motorConfigIDs = ((RocketComponent) mount).getRocket().getMotorConfigurationIDs();
List elements = new ArrayList();
-
+
elements.add("");
-
+
for (String id : motorConfigIDs) {
Motor motor = mount.getMotor(id);
-
+
// Nothing is stored if no motor loaded
if (motor == null)
continue;
-
+
elements.add(" ");
if (motor.getMotorType() != Motor.Type.UNKNOWN) {
elements.add(" " + motor.getMotorType().name().toLowerCase(Locale.ENGLISH) + "");
@@ -146,27 +171,35 @@ public class RocketComponentSaver {
elements.add(" " + RocketSaver.escapeXML(motor.getDesignation()) + "");
elements.add(" " + motor.getDiameter() + "");
elements.add(" " + motor.getLength() + "");
-
+
// Motor delay
if (mount.getMotorDelay(id) == Motor.PLUGGED) {
elements.add(" none");
} else {
elements.add(" " + mount.getMotorDelay(id) + "");
}
-
+
elements.add(" ");
}
-
+
elements.add(" "
+ mount.getIgnitionEvent().name().toLowerCase(Locale.ENGLISH).replace("_", "")
+ "");
-
+
elements.add(" " + mount.getIgnitionDelay() + "");
elements.add(" " + mount.getMotorOverhang() + "");
-
+
elements.add("");
-
+
return elements;
}
-
+
+ private final static void emitColor( String elementName, List elements, Color color ) {
+ if (color != null) {
+ elements.add("<" + elementName+ " red=\"" + color.getRed() + "\" green=\"" + color.getGreen()
+ + "\" blue=\"" + color.getBlue() + "\"/>");
+ }
+
+ }
+
}
diff --git a/core/src/net/sf/openrocket/file/rocksim/export/RocksimSaver.java b/core/src/net/sf/openrocket/file/rocksim/export/RocksimSaver.java
index 36f44d527..c3b4522ff 100644
--- a/core/src/net/sf/openrocket/file/rocksim/export/RocksimSaver.java
+++ b/core/src/net/sf/openrocket/file/rocksim/export/RocksimSaver.java
@@ -1,5 +1,14 @@
package net.sf.openrocket.file.rocksim.export;
+import java.io.BufferedWriter;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.StringWriter;
+
+import javax.xml.bind.JAXBContext;
+import javax.xml.bind.Marshaller;
+
import net.sf.openrocket.document.OpenRocketDocument;
import net.sf.openrocket.document.StorageOptions;
import net.sf.openrocket.file.RocketSaver;
@@ -12,14 +21,6 @@ import net.sf.openrocket.rocketcomponent.Rocket;
import net.sf.openrocket.rocketcomponent.Stage;
import net.sf.openrocket.startup.Application;
-import javax.xml.bind.JAXBContext;
-import javax.xml.bind.Marshaller;
-import java.io.BufferedWriter;
-import java.io.IOException;
-import java.io.OutputStream;
-import java.io.OutputStreamWriter;
-import java.io.StringWriter;
-
/**
* This class is responsible for converting an OpenRocket design to a Rocksim design.
*/
@@ -55,7 +56,7 @@ public class RocksimSaver extends RocketSaver {
}
@Override
- public void save(OutputStream dest, OpenRocketDocument doc, StorageOptions options) throws IOException {
+ public void save(String fileName, OutputStream dest, OpenRocketDocument doc, StorageOptions options) throws IOException {
log.info("Saving .rkt file");
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(dest, "UTF-8"));
diff --git a/core/src/net/sf/openrocket/file/rocksim/importt/RockSimAppearanceBuilder.java b/core/src/net/sf/openrocket/file/rocksim/importt/RockSimAppearanceBuilder.java
index 14734782e..397e63750 100644
--- a/core/src/net/sf/openrocket/file/rocksim/importt/RockSimAppearanceBuilder.java
+++ b/core/src/net/sf/openrocket/file/rocksim/importt/RockSimAppearanceBuilder.java
@@ -82,7 +82,7 @@ public class RockSimAppearanceBuilder extends AppearanceBuilder {
//Find out how to get path of current rocksim file
//so I can look in it's directory
}
- setImage(f.toURI().toURL());
+ setImage(value);
}
} else if ("repeat".equals(name)) {
repeat = "1".equals(value);
@@ -136,6 +136,23 @@ public class RockSimAppearanceBuilder extends AppearanceBuilder {
}
static Color parseColor(String s) {
+ // blue and white came from a real file.
+ if ( "blue".equals(s) ) {
+ return new Color(0,0,255);
+ }
+ if ( "white".equals(s) ) {
+ return new Color(255,255,255);
+ }
+ // I guessed these are valid color names in Rksim.
+ if ( "red".equals(s) ) {
+ return new Color(255,0,0);
+ }
+ if( "green".equals(s) ) {
+ return new Color(0,255,0);
+ }
+ if ( "black".equals(s) ) {
+ return new Color(0,0,0);
+ }
s = s.replace("rgb(", "");
s = s.replace(")", "");
String ss[] = s.split(",");
diff --git a/core/src/net/sf/openrocket/gui/configdialog/AppearancePanel.java b/core/src/net/sf/openrocket/gui/configdialog/AppearancePanel.java
index b147bde45..0a6dc27a4 100644
--- a/core/src/net/sf/openrocket/gui/configdialog/AppearancePanel.java
+++ b/core/src/net/sf/openrocket/gui/configdialog/AppearancePanel.java
@@ -5,8 +5,6 @@ import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.lang.reflect.Method;
-import java.net.MalformedURLException;
-import java.net.URL;
import java.util.EventObject;
import javax.swing.JButton;
@@ -233,25 +231,12 @@ public class AppearancePanel extends JPanel {
choose.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
- URL u = ab.getImage();
File current = lastImageDir;
- if (u != null && u.getProtocol().equals("file")) {
- try {
- current = new File(u.toURI()).getParentFile();
- } catch (Exception e1) {
- e1.printStackTrace();
- }
- }
lastImageDir = current;
JFileChooser fc = new JFileChooser(current);
if (fc.showOpenDialog(AppearancePanel.this) == JFileChooser.APPROVE_OPTION) {
- try {
- ab.setImage(fc.getSelectedFile().toURI().toURL());
- } catch (MalformedURLException e1) {
- // TODO Auto-generated catch block
- e1.printStackTrace();
- }
+ ab.setImage(fc.getSelectedFile().getAbsolutePath());
}
}
diff --git a/core/src/net/sf/openrocket/gui/figure3d/FigureRenderStrategy.java b/core/src/net/sf/openrocket/gui/figure3d/FigureRenderStrategy.java
index a8e25ed6c..594f510b3 100644
--- a/core/src/net/sf/openrocket/gui/figure3d/FigureRenderStrategy.java
+++ b/core/src/net/sf/openrocket/gui/figure3d/FigureRenderStrategy.java
@@ -19,6 +19,10 @@ import net.sf.openrocket.util.Color;
public class FigureRenderStrategy extends RenderStrategy {
private final float[] color = new float[4];
+ public FigureRenderStrategy() {
+ super(null);
+ }
+
@Override
public boolean isDrawn(RocketComponent c) {
return true;
diff --git a/core/src/net/sf/openrocket/gui/figure3d/RealisticRenderStrategy.java b/core/src/net/sf/openrocket/gui/figure3d/RealisticRenderStrategy.java
index 03e8b58d4..45bbf7307 100644
--- a/core/src/net/sf/openrocket/gui/figure3d/RealisticRenderStrategy.java
+++ b/core/src/net/sf/openrocket/gui/figure3d/RealisticRenderStrategy.java
@@ -1,8 +1,7 @@
package net.sf.openrocket.gui.figure3d;
+import java.io.InputStream;
import java.net.URI;
-import java.net.URISyntaxException;
-import java.net.URL;
import java.util.HashMap;
import java.util.Map;
@@ -14,26 +13,34 @@ import javax.media.opengl.GLProfile;
import javax.media.opengl.fixedfunc.GLLightingFunc;
import javax.media.opengl.fixedfunc.GLMatrixFunc;
-import com.jogamp.opengl.util.texture.Texture;
-import com.jogamp.opengl.util.texture.TextureData;
-import com.jogamp.opengl.util.texture.TextureIO;
-
import net.sf.openrocket.appearance.Appearance;
import net.sf.openrocket.appearance.Decal;
+import net.sf.openrocket.document.DecalRegistry;
+import net.sf.openrocket.document.OpenRocketDocument;
import net.sf.openrocket.logging.LogHelper;
import net.sf.openrocket.rocketcomponent.RocketComponent;
import net.sf.openrocket.startup.Application;
import net.sf.openrocket.util.Color;
+import com.jogamp.opengl.util.texture.Texture;
+import com.jogamp.opengl.util.texture.TextureData;
+import com.jogamp.opengl.util.texture.TextureIO;
+
public class RealisticRenderStrategy extends RenderStrategy {
private final float[] colorBlack = { 0, 0, 0, 1 };
private final float[] color = new float[4];
private static final LogHelper log = Application.getLogger();
+ private final DecalRegistry decalLoader;
private boolean needClearCache = false;
- private Map oldTexCache = new HashMap();
- private Map texCache = new HashMap();
+ private Map oldTexCache = new HashMap();
+ private Map texCache = new HashMap();
+
+ public RealisticRenderStrategy(OpenRocketDocument document) {
+ super(document);
+ this.decalLoader = document.getDecalRegistry();
+ }
@Override
public void updateFigure() {
@@ -42,8 +49,8 @@ public class RealisticRenderStrategy extends RenderStrategy {
@Override
public void init(GLAutoDrawable drawable) {
- oldTexCache = new HashMap();
- texCache = new HashMap();
+ oldTexCache = new HashMap();
+ texCache = new HashMap();
}
@Override
@@ -100,7 +107,7 @@ public class RealisticRenderStrategy extends RenderStrategy {
if (t != null && tex != null) {
gl.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MIN_FILTER, GL.GL_LINEAR_MIPMAP_LINEAR);
gl.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MAG_FILTER, GL.GL_LINEAR);
-
+
tex.enable(gl);
tex.bind(gl);
gl.glMatrixMode(GL.GL_TEXTURE);
@@ -138,46 +145,40 @@ public class RealisticRenderStrategy extends RenderStrategy {
private void clearCaches(GL2 gl) {
log.debug("ClearCaches");
- for (Map.Entry e : oldTexCache.entrySet()) {
+ for (Map.Entry e : oldTexCache.entrySet()) {
log.debug("Destroying Texture for " + e.getKey());
if (e.getValue() != null)
e.getValue().destroy(gl);
}
oldTexCache = texCache;
- texCache = new HashMap();
+ texCache = new HashMap();
}
private Texture getTexture(Decal t) {
- URL url = t.getImageURL();
- URI uri; // NEVER use a URL as a key!
- try {
- uri = url.toURI();
- } catch (URISyntaxException e) {
- e.printStackTrace();
- return null;
- }
+ String imageName = t.getImage();
// Return the Cached value if available
- if (texCache.containsKey(uri))
- return texCache.get(uri);
+ if (texCache.containsKey(imageName))
+ return texCache.get(imageName);
// If the texture is in the Old Cache, save it.
- if (oldTexCache.containsKey(uri)) {
- texCache.put(uri, oldTexCache.get(uri));
- oldTexCache.remove(uri);
- return texCache.get(uri);
+ if (oldTexCache.containsKey(imageName)) {
+ texCache.put(imageName, oldTexCache.get(imageName));
+ oldTexCache.remove(imageName);
+ return texCache.get(imageName);
}
// Otherwise load it.
Texture tex = null;
try {
log.debug("Loading texture " + t);
- TextureData data = TextureIO.newTextureData(GLProfile.getDefault(), url.openStream(), true, null);
+ InputStream is = decalLoader.getDecal(imageName);
+ TextureData data = TextureIO.newTextureData(GLProfile.getDefault(), is, true, null);
tex = TextureIO.newTexture(data);
} catch (Throwable e) {
log.error("Error loading Texture", e);
}
- texCache.put(uri, tex);
+ texCache.put(imageName, tex);
return tex;
diff --git a/core/src/net/sf/openrocket/gui/figure3d/RenderStrategy.java b/core/src/net/sf/openrocket/gui/figure3d/RenderStrategy.java
index e73a18405..4908307a9 100644
--- a/core/src/net/sf/openrocket/gui/figure3d/RenderStrategy.java
+++ b/core/src/net/sf/openrocket/gui/figure3d/RenderStrategy.java
@@ -3,9 +3,17 @@ package net.sf.openrocket.gui.figure3d;
import javax.media.opengl.GL2;
import javax.media.opengl.GLAutoDrawable;
+import net.sf.openrocket.document.OpenRocketDocument;
import net.sf.openrocket.rocketcomponent.RocketComponent;
public abstract class RenderStrategy {
+
+ protected final OpenRocketDocument document;
+
+ public RenderStrategy( OpenRocketDocument document ) {
+ this.document = document;
+ }
+
public abstract boolean isDrawn(RocketComponent c);
public abstract boolean isDrawnTransparent(RocketComponent c);
diff --git a/core/src/net/sf/openrocket/gui/figure3d/RocketFigure3d.java b/core/src/net/sf/openrocket/gui/figure3d/RocketFigure3d.java
index 91d963a8e..86661be9c 100644
--- a/core/src/net/sf/openrocket/gui/figure3d/RocketFigure3d.java
+++ b/core/src/net/sf/openrocket/gui/figure3d/RocketFigure3d.java
@@ -31,6 +31,7 @@ import javax.swing.JPopupMenu;
import javax.swing.SwingUtilities;
import javax.swing.event.MouseInputAdapter;
+import net.sf.openrocket.document.OpenRocketDocument;
import net.sf.openrocket.gui.figureelements.CGCaret;
import net.sf.openrocket.gui.figureelements.CPCaret;
import net.sf.openrocket.gui.figureelements.FigureElement;
@@ -65,6 +66,7 @@ public class RocketFigure3d extends JPanel implements GLEventListener {
private static double fovX = Double.NaN;
private static final int CARET_SIZE = 20;
+ private OpenRocketDocument document;
private Configuration configuration;
private GLCanvas canvas;
@@ -87,7 +89,8 @@ public class RocketFigure3d extends JPanel implements GLEventListener {
RocketRenderer rr = new RocketRenderer();
- public RocketFigure3d(Configuration config) {
+ public RocketFigure3d(OpenRocketDocument document, Configuration config) {
+ this.document = document;
this.configuration = config;
this.setLayout(new BorderLayout());
@@ -627,7 +630,7 @@ public class RocketFigure3d extends JPanel implements GLEventListener {
if ( t == TYPE_FIGURE ){
rr.setRenderStrategy(new FigureRenderStrategy());
} else {
- rr.setRenderStrategy(new RealisticRenderStrategy());
+ rr.setRenderStrategy(new RealisticRenderStrategy(document));
}
repaint();
}
diff --git a/core/src/net/sf/openrocket/gui/main/BasicFrame.java b/core/src/net/sf/openrocket/gui/main/BasicFrame.java
index 2b59e9cc6..e84c9968a 100644
--- a/core/src/net/sf/openrocket/gui/main/BasicFrame.java
+++ b/core/src/net/sf/openrocket/gui/main/BasicFrame.java
@@ -113,7 +113,7 @@ public class BasicFrame extends JFrame {
/**
* The RocketLoader instance used for loading all rocket designs.
*/
- private static final RocketLoader ROCKET_LOADER = new GeneralRocketLoader();
+ private static final GeneralRocketLoader ROCKET_LOADER = new GeneralRocketLoader();
private static final RocketSaver ROCKET_SAVER = new OpenRocketSaver();
diff --git a/core/src/net/sf/openrocket/gui/scalefigure/RocketPanel.java b/core/src/net/sf/openrocket/gui/scalefigure/RocketPanel.java
index 2cef70809..6760685de 100644
--- a/core/src/net/sf/openrocket/gui/scalefigure/RocketPanel.java
+++ b/core/src/net/sf/openrocket/gui/scalefigure/RocketPanel.java
@@ -164,7 +164,7 @@ public class RocketPanel extends JPanel implements TreeSelectionListener, Change
// Create figure and custom scroll pane
figure = new RocketFigure(configuration);
- figure3d = new RocketFigure3d(configuration);
+ figure3d = new RocketFigure3d(document, configuration);
figureHolder = new JPanel(new BorderLayout());
diff --git a/core/src/net/sf/openrocket/gui/util/OpenFileWorker.java b/core/src/net/sf/openrocket/gui/util/OpenFileWorker.java
index da193c165..3d93bf568 100644
--- a/core/src/net/sf/openrocket/gui/util/OpenFileWorker.java
+++ b/core/src/net/sf/openrocket/gui/util/OpenFileWorker.java
@@ -12,7 +12,7 @@ import javax.swing.SwingWorker;
import net.sf.openrocket.document.OpenRocketDocument;
import net.sf.openrocket.file.DatabaseMotorFinder;
-import net.sf.openrocket.file.RocketLoader;
+import net.sf.openrocket.file.GeneralRocketLoader;
import net.sf.openrocket.logging.LogHelper;
import net.sf.openrocket.startup.Application;
import net.sf.openrocket.util.MathUtil;
@@ -28,22 +28,22 @@ public class OpenFileWorker extends SwingWorker {
private final File file;
private final InputStream stream;
- private final RocketLoader loader;
+ private final GeneralRocketLoader loader;
- public OpenFileWorker(File file, RocketLoader loader) {
+ public OpenFileWorker(File file, GeneralRocketLoader loader) {
this.file = file;
this.stream = null;
this.loader = loader;
}
- public OpenFileWorker(InputStream stream, RocketLoader loader) {
+ public OpenFileWorker(InputStream stream, GeneralRocketLoader loader) {
this.stream = stream;
this.file = null;
this.loader = loader;
}
- public RocketLoader getRocketLoader() {
+ public GeneralRocketLoader getRocketLoader() {
return loader;
}
@@ -67,7 +67,7 @@ public class OpenFileWorker extends SwingWorker {
is = new ProgressInputStream(is);
try {
- return loader.load(is, new DatabaseMotorFinder());
+ return loader.load(is, file, new DatabaseMotorFinder());
} finally {
try {
is.close();
diff --git a/core/src/net/sf/openrocket/gui/util/SaveFileWorker.java b/core/src/net/sf/openrocket/gui/util/SaveFileWorker.java
index 1ccc3145b..39e883730 100644
--- a/core/src/net/sf/openrocket/gui/util/SaveFileWorker.java
+++ b/core/src/net/sf/openrocket/gui/util/SaveFileWorker.java
@@ -41,8 +41,10 @@ public class SaveFileWorker extends SwingWorker {
};
+ String rawFilename = file.getName();
+
try {
- saver.save(os, document);
+ saver.save(rawFilename, os, document);
} finally {
try {
os.close();
diff --git a/core/test/net/sf/openrocket/IntegrationTest.java b/core/test/net/sf/openrocket/IntegrationTest.java
index b06a6c34c..702e7ebfe 100644
--- a/core/test/net/sf/openrocket/IntegrationTest.java
+++ b/core/test/net/sf/openrocket/IntegrationTest.java
@@ -1,8 +1,13 @@
package net.sf.openrocket;
-import static org.junit.Assert.*;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
import java.awt.event.ActionEvent;
+import java.io.File;
import java.io.IOException;
import java.io.InputStream;
@@ -91,7 +96,7 @@ public class IntegrationTest extends BaseTestCase {
GeneralRocketLoader loader = new GeneralRocketLoader();
InputStream is = this.getClass().getResourceAsStream("simplerocket.ork");
assertNotNull("Problem in unit test, cannot find simplerocket.ork", is);
- document = loader.load(is, new DatabaseMotorFinder());
+ document = loader.load(is, new File("simplerocket.ork"), new DatabaseMotorFinder());
is.close();
undoAction = UndoRedoAction.newUndoAction(document);