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 = ""); } - + 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 + ""); } - - - + + + 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) + ""; } - - + + 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);