X-Git-Url: https://gitweb.fperrin.net/?p=GpsPrune.git;a=blobdiff_plain;f=tim%2Fprune%2Fsave%2FPovExporter.java;h=e0fc9d35be049168096f68d97c1b7286901ae462;hp=c96d744dbcfc0c35f6886d5e75fbe752cccc7b7a;hb=92dad5df664287acb51728e9ea599f150765d34a;hpb=54b9d8bc8f0025ccf97a67d9dd217ef1f9cf082f diff --git a/tim/prune/save/PovExporter.java b/tim/prune/save/PovExporter.java index c96d744..e0fc9d3 100644 --- a/tim/prune/save/PovExporter.java +++ b/tim/prune/save/PovExporter.java @@ -12,6 +12,9 @@ import java.io.IOException; import java.util.ArrayList; import java.util.Iterator; +import javax.imageio.ImageIO; +import javax.swing.BorderFactory; +import javax.swing.Box; import javax.swing.BoxLayout; import javax.swing.ButtonGroup; import javax.swing.JButton; @@ -25,37 +28,51 @@ import javax.swing.JTextField; import javax.swing.SwingConstants; import tim.prune.App; -import tim.prune.Config; -import tim.prune.GenericFunction; +import tim.prune.FunctionLibrary; import tim.prune.I18nManager; import tim.prune.UpdateMessageBroker; +import tim.prune.config.Config; +import tim.prune.data.NumberUtils; import tim.prune.data.Track; +import tim.prune.function.Export3dFunction; +import tim.prune.function.srtm.LookupSrtmFunction; +import tim.prune.gui.BaseImageDefinitionPanel; +import tim.prune.gui.DialogCloser; +import tim.prune.gui.TerrainDefinitionPanel; +import tim.prune.gui.map.MapSource; +import tim.prune.gui.map.MapSourceLibrary; import tim.prune.load.GenericFileFilter; -import tim.prune.threedee.LineDialog; +import tim.prune.threedee.ImageDefinition; +import tim.prune.threedee.TerrainCache; +import tim.prune.threedee.TerrainDefinition; +import tim.prune.threedee.TerrainHelper; import tim.prune.threedee.ThreeDModel; /** - * Class to export track information - * into a specified Pov file + * Class to export a 3d scene of the track to a specified Pov file */ -public class PovExporter extends GenericFunction +public class PovExporter extends Export3dFunction { private Track _track = null; private JDialog _dialog = null; private JFileChooser _fileChooser = null; private String _cameraX = null, _cameraY = null, _cameraZ = null; private JTextField _cameraXField = null, _cameraYField = null, _cameraZField = null; - private JTextField _fontName = null, _altitudeCapField = null; - private int _altitudeCap = ThreeDModel.MINIMUM_ALTITUDE_CAP; + private JTextField _fontName = null, _altitudeFactorField = null; private JRadioButton _ballsAndSticksButton = null; + /** Panel for defining the base image */ + private BaseImageDefinitionPanel _baseImagePanel = null; + /** Component for defining the terrain */ + private TerrainDefinitionPanel _terrainPanel = null; // defaults private static final double DEFAULT_CAMERA_DISTANCE = 30.0; + private static final double MODEL_SCALE_FACTOR = 20.0; private static final String DEFAULT_FONT_FILE = "crystal.ttf"; /** - * Constructor giving frame and track + * Constructor * @param inApp App object */ public PovExporter(App inApp) @@ -83,23 +100,10 @@ public class PovExporter extends GenericFunction double cameraDist = Math.sqrt(inX*inX + inY*inY + inZ*inZ); if (cameraDist > 0.0) { - _cameraX = "" + (inX / cameraDist * DEFAULT_CAMERA_DISTANCE); - _cameraY = "" + (inY / cameraDist * DEFAULT_CAMERA_DISTANCE); + _cameraX = NumberUtils.formatNumberUk(inX / cameraDist * DEFAULT_CAMERA_DISTANCE, 5); + _cameraY = NumberUtils.formatNumberUk(inY / cameraDist * DEFAULT_CAMERA_DISTANCE, 5); // Careful! Need to convert from java3d (right-handed) to povray (left-handed) coordinate system! - _cameraZ = "" + (-inZ / cameraDist * DEFAULT_CAMERA_DISTANCE); - } - } - - - /** - * @param inAltitudeCap altitude cap to use - */ - public void setAltitudeCap(int inAltitudeCap) - { - _altitudeCap = inAltitudeCap; - if (_altitudeCap < ThreeDModel.MINIMUM_ALTITUDE_CAP) - { - _altitudeCap = ThreeDModel.MINIMUM_ALTITUDE_CAP; + _cameraZ = NumberUtils.formatNumberUk(-inZ / cameraDist * DEFAULT_CAMERA_DISTANCE, 5); } } @@ -109,20 +113,32 @@ public class PovExporter extends GenericFunction */ public void begin() { - // Make dialog window to select angles, colours etc + // Make dialog window to select inputs if (_dialog == null) { _dialog = new JDialog(_parentFrame, I18nManager.getText(getNameKey()), true); _dialog.setLocationRelativeTo(_parentFrame); _dialog.getContentPane().add(makeDialogComponents()); } + // Get exaggeration factor from config + final int exaggFactor = Config.getConfigInt(Config.KEY_HEIGHT_EXAGGERATION); + if (exaggFactor > 0) { + _altFactor = exaggFactor / 100.0; + } // Set angles _cameraXField.setText(_cameraX); _cameraYField.setText(_cameraY); _cameraZField.setText(_cameraZ); - // Set vertical scale - _altitudeCapField.setText("" + _altitudeCap); + _altitudeFactorField.setText("" + _altFactor); + // Pass terrain and image def parameters (if any) to the panels + if (_terrainDef != null) { + _terrainPanel.initTerrainParameters(_terrainDef); + } + if (_imageDef != null) { + _baseImagePanel.initImageParameters(_imageDef); + } + _baseImagePanel.updateBaseImageDetails(); // Show dialog _dialog.pack(); _dialog.setVisible(true); @@ -136,8 +152,10 @@ public class PovExporter extends GenericFunction private Component makeDialogComponents() { JPanel panel = new JPanel(); - panel.setLayout(new BorderLayout()); - panel.add(new JLabel(I18nManager.getText("dialog.exportpov.text")), BorderLayout.NORTH); + panel.setLayout(new BorderLayout(4, 4)); + JLabel introLabel = new JLabel(I18nManager.getText("dialog.exportpov.text")); + introLabel.setBorder(BorderFactory.createEmptyBorder(4, 4, 6, 4)); + panel.add(introLabel, BorderLayout.NORTH); // OK, Cancel buttons JPanel buttonPanel = new JPanel(); buttonPanel.setLayout(new FlowLayout(FlowLayout.RIGHT)); @@ -145,7 +163,14 @@ public class PovExporter extends GenericFunction okButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { - doExport(); + // Need to launch export in new thread + new Thread(new Runnable() { + public void run() + { + doExport(); + _baseImagePanel.getGrouter().clearMapImage(); + } + }).start(); _dialog.dispose(); } }); @@ -154,6 +179,7 @@ public class PovExporter extends GenericFunction cancelButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { + _baseImagePanel.getGrouter().clearMapImage(); _dialog.dispose(); } }); @@ -167,12 +193,13 @@ public class PovExporter extends GenericFunction JLabel fontLabel = new JLabel(I18nManager.getText("dialog.exportpov.font")); fontLabel.setHorizontalAlignment(SwingConstants.TRAILING); centralPanel.add(fontLabel); - String defaultFont = Config.getPovrayFont(); + String defaultFont = Config.getConfigString(Config.KEY_POVRAY_FONT); if (defaultFont == null || defaultFont.equals("")) { defaultFont = DEFAULT_FONT_FILE; } _fontName = new JTextField(defaultFont, 12); _fontName.setAlignmentX(Component.LEFT_ALIGNMENT); + _fontName.addKeyListener(new DialogCloser(_dialog)); centralPanel.add(_fontName); //coordinates of camera JLabel cameraXLabel = new JLabel(I18nManager.getText("dialog.exportpov.camerax")); @@ -190,12 +217,12 @@ public class PovExporter extends GenericFunction centralPanel.add(cameraZLabel); _cameraZField = new JTextField("" + _cameraZ); centralPanel.add(_cameraZField); - // Altitude capping - JLabel altitudeCapLabel = new JLabel(I18nManager.getText("dialog.3d.altitudecap")); + // Altitude exaggeration + JLabel altitudeCapLabel = new JLabel(I18nManager.getText("dialog.3d.altitudefactor")); altitudeCapLabel.setHorizontalAlignment(SwingConstants.TRAILING); centralPanel.add(altitudeCapLabel); - _altitudeCapField = new JTextField("" + _altitudeCap); - centralPanel.add(_altitudeCapField); + _altitudeFactorField = new JTextField("1.0"); + centralPanel.add(_altitudeFactorField); // Radio buttons for style - balls on sticks or tubes JPanel stylePanel = new JPanel(); @@ -215,33 +242,25 @@ public class PovExporter extends GenericFunction group.add(_ballsAndSticksButton); group.add(tubesButton); stylePanel.add(radioPanel); - // add this grid to the holder panel + // Panel for the base image (parent is null because we don't need callback) + _baseImagePanel = new BaseImageDefinitionPanel(null, _dialog, _track); + // Panel for the terrain definition + _terrainPanel = new TerrainDefinitionPanel(); + + // add these panels to the holder panel JPanel holderPanel = new JPanel(); holderPanel.setLayout(new BorderLayout(5, 5)); JPanel boxPanel = new JPanel(); boxPanel.setLayout(new BoxLayout(boxPanel, BoxLayout.Y_AXIS)); boxPanel.add(centralPanel); + boxPanel.add(Box.createVerticalStrut(4)); boxPanel.add(stylePanel); + boxPanel.add(Box.createVerticalStrut(4)); + boxPanel.add(_terrainPanel); + boxPanel.add(Box.createVerticalStrut(4)); + boxPanel.add(_baseImagePanel); holderPanel.add(boxPanel, BorderLayout.CENTER); - // show lines button - JButton showLinesButton = new JButton(I18nManager.getText("button.showlines")); - showLinesButton.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) - { - // Need to scale model to find lines - ThreeDModel model = new ThreeDModel(_track); - model.scale(); - double[] latLines = model.getLatitudeLines(); - double[] lonLines = model.getLongitudeLines(); - LineDialog dialog = new LineDialog(_parentFrame, latLines, lonLines); - dialog.showDialog(); - } - }); - JPanel flowPanel = new JPanel(); - flowPanel.setLayout(new FlowLayout()); - flowPanel.add(showLinesButton); - holderPanel.add(flowPanel, BorderLayout.EAST); panel.add(holderPanel, BorderLayout.CENTER); return panel; } @@ -265,8 +284,8 @@ public class PovExporter extends GenericFunction _fileChooser.setFileFilter(new GenericFileFilter("filetype.pov", new String[] {"pov"})); _fileChooser.setAcceptAllFileFilterUsed(false); // start from directory in config which should be set - File configDir = Config.getWorkingDirectory(); - if (configDir != null) {_fileChooser.setCurrentDirectory(configDir);} + final String configDir = Config.getConfigString(Config.KEY_TRACK_DIR); + if (configDir != null) {_fileChooser.setCurrentDirectory(new File(configDir));} } // Allow choose again if an existing file is selected @@ -277,25 +296,36 @@ public class PovExporter extends GenericFunction if (_fileChooser.showSaveDialog(_parentFrame) == JFileChooser.APPROVE_OPTION) { // OK pressed and file chosen - File file = _fileChooser.getSelectedFile(); - if (!file.getName().toLowerCase().endsWith(".pov")) + File povFile = _fileChooser.getSelectedFile(); + if (!povFile.getName().toLowerCase().endsWith(".pov")) { - file = new File(file.getAbsolutePath() + ".pov"); + povFile = new File(povFile.getAbsolutePath() + ".pov"); } - // Check if file exists and if necessary prompt for overwrite + final int nameLen = povFile.getName().length() - 4; + final File imageFile = new File(povFile.getParentFile(), povFile.getName().substring(0, nameLen) + "_base.png"); + final File terrainFile = new File(povFile.getParentFile(), povFile.getName().substring(0, nameLen) + "_terrain.png"); + final boolean imageExists = _baseImagePanel.getImageDefinition().getUseImage() && imageFile.exists(); + final boolean terrainFileExists = _terrainPanel.getUseTerrain() && terrainFile.exists(); + + // Check if files exist and if necessary prompt for overwrite Object[] buttonTexts = {I18nManager.getText("button.overwrite"), I18nManager.getText("button.cancel")}; - if (!file.exists() || JOptionPane.showOptionDialog(_parentFrame, + if ((!povFile.exists() && !imageExists && !terrainFileExists) + || JOptionPane.showOptionDialog(_parentFrame, I18nManager.getText("dialog.save.overwrite.text"), I18nManager.getText("dialog.save.overwrite.title"), JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE, null, buttonTexts, buttonTexts[1]) == JOptionPane.YES_OPTION) { - // Export the file - if (exportFile(file)) + // Export the file(s) + if (exportFiles(povFile, imageFile, terrainFile)) { - // file saved - // Store directory in config for later - Config.setWorkingDirectory(file.getParentFile()); + // file saved - store directory in config for later + Config.setConfigString(Config.KEY_TRACK_DIR, povFile.getParentFile().getAbsolutePath()); + // also store exaggeration and grid size + Config.setConfigInt(Config.KEY_HEIGHT_EXAGGERATION, (int) (_altFactor * 100)); + if (_terrainPanel.getUseTerrain() && _terrainPanel.getGridSize() > 20) { + Config.setConfigInt(Config.KEY_TERRAIN_GRID_SIZE, _terrainPanel.getGridSize()); + } } else { @@ -314,34 +344,98 @@ public class PovExporter extends GenericFunction /** - * Export the track data to the specified file - * @param inFile File object to save to + * Export the data to the specified file(s) + * @param inPovFile File object to save pov file to + * @param inImageFile file object to save image to + * @param inTerrainFile file object to save terrain to * @return true if successful */ - private boolean exportFile(File inFile) + private boolean exportFiles(File inPovFile, File inImageFile, File inTerrainFile) { FileWriter writer = null; // find out the line separator for this system - String lineSeparator = System.getProperty("line.separator"); + final String lineSeparator = System.getProperty("line.separator"); try { // create and scale model ThreeDModel model = new ThreeDModel(_track); + model.setModelSize(MODEL_SCALE_FACTOR); try { // try to use given altitude cap - _altitudeCap = Integer.parseInt(_altitudeCapField.getText()); - model.setAltitudeCap(_altitudeCap); + double givenFactor = Double.parseDouble(_altitudeFactorField.getText()); + if (givenFactor > 0.0) _altFactor = givenFactor; + } + catch (NumberFormatException nfe) { // parse failed, reset + _altitudeFactorField.setText("" + _altFactor); } - catch (NumberFormatException nfe) {} - model.scale(); + model.setAltitudeFactor(_altFactor); - // Create file and write basics - writer = new FileWriter(inFile); - writeStartOfFile(writer, model.getModelSize(), lineSeparator); + // Write base image if necessary + ImageDefinition imageDef = _baseImagePanel.getImageDefinition(); + boolean useImage = imageDef.getUseImage(); + if (useImage) + { + // Get base image from grouter + MapSource mapSource = MapSourceLibrary.getSource(imageDef.getSourceIndex()); + MapGrouter grouter = _baseImagePanel.getGrouter(); + GroutedImage baseImage = grouter.getMapImage(_track, mapSource, imageDef.getZoom()); + try + { + useImage = ImageIO.write(baseImage.getImage(), "png", inImageFile); + } + catch (IOException ioe) { + System.err.println("Can't write image: " + ioe.getClass().getName()); + useImage = false; + } + if (!useImage) { + _app.showErrorMessage(getNameKey(), "dialog.exportpov.cannotmakebaseimage"); + } + } + + boolean useTerrain = _terrainPanel.getUseTerrain(); + if (useTerrain) + { + TerrainHelper terrainHelper = new TerrainHelper(_terrainPanel.getGridSize()); + // See if there's a previously saved terrain track we can reuse + TerrainDefinition terrainDef = new TerrainDefinition(_terrainPanel.getUseTerrain(), _terrainPanel.getGridSize()); + Track terrainTrack = TerrainCache.getTerrainTrack(_app.getCurrentDataStatus(), terrainDef); + if (terrainTrack == null) + { + // Construct the terrain track according to these extents and the grid size + terrainTrack = terrainHelper.createGridTrack(_track); + // Get the altitudes from SRTM for all the points in the track + LookupSrtmFunction srtmLookup = (LookupSrtmFunction) FunctionLibrary.FUNCTION_LOOKUP_SRTM; + srtmLookup.begin(terrainTrack); + while (srtmLookup.isRunning()) + { + try { + Thread.sleep(750); // just polling in a wait loop isn't ideal but simple + } + catch (InterruptedException e) {} + } + // Fix the voids + terrainHelper.fixVoids(terrainTrack); + + // Store this back in the cache, maybe we'll need it again + TerrainCache.storeTerrainTrack(terrainTrack, _app.getCurrentDataStatus(), terrainDef); + } - // write out lat/long lines using model - writeLatLongLines(writer, model, lineSeparator); + model.setTerrain(terrainTrack); + model.scale(); + + // Call TerrainHelper to write out the data from the model + terrainHelper.writeHeightMap(model, inTerrainFile); + } + else + { + // No terrain required, so just scale the model as it is + model.scale(); + } + + // Create file and write basics + writer = new FileWriter(inPovFile); + writeStartOfFile(writer, lineSeparator, useImage ? inImageFile : null, useTerrain ? inTerrainFile : null); // write out points if (_ballsAndSticksButton.isSelected()) { @@ -354,12 +448,12 @@ public class PovExporter extends GenericFunction // everything worked UpdateMessageBroker.informSubscribers(I18nManager.getText("confirm.save.ok1") + " " + _track.getNumPoints() + " " + I18nManager.getText("confirm.save.ok2") - + " " + inFile.getAbsolutePath()); + + " " + inPovFile.getAbsolutePath()); return true; } catch (IOException ioe) { - JOptionPane.showMessageDialog(_parentFrame, I18nManager.getText("error.save.failed") + ioe.getMessage(), + JOptionPane.showMessageDialog(_parentFrame, I18nManager.getText("error.save.failed") + " : " + ioe.getMessage(), I18nManager.getText("error.save.dialogtitle"), JOptionPane.ERROR_MESSAGE); } finally @@ -378,14 +472,17 @@ public class PovExporter extends GenericFunction /** * Write the start of the Pov file, including base plane and lights * @param inWriter Writer to use for writing file - * @param inModelSize model size * @param inLineSeparator line separator to use + * @param inImageFile image file to reference (or null if none) + * @param inTerrainFile terrain file to reference (or null if none) * @throws IOException on file writing error */ - private void writeStartOfFile(FileWriter inWriter, double inModelSize, String inLineSeparator) + private void writeStartOfFile(FileWriter inWriter, String inLineSeparator, File inImageFile, File inTerrainFile) throws IOException { - inWriter.write("// Pov file produced by Prune - see http://activityworkshop.net/"); + inWriter.write("// Pov file produced by GpsPrune - see https://gpsprune.activityworkshop.net/"); + inWriter.write(inLineSeparator); + inWriter.write("#version 3.6;"); inWriter.write(inLineSeparator); inWriter.write(inLineSeparator); // Select font based on user input @@ -394,6 +491,26 @@ public class PovExporter extends GenericFunction { fontPath = DEFAULT_FONT_FILE; } + else { + Config.setConfigString(Config.KEY_POVRAY_FONT, fontPath); + } + + // Make the definition of the base plane depending on whether there's an image or not + final boolean useImage = (inImageFile != null); + final boolean useImageOnBox = useImage && (inTerrainFile == null); + final String boxDefinition = (useImageOnBox ? + " <0, 0, 0>, <1, 1, 0.001>" + inLineSeparator + + " pigment {image_map { png \"" + inImageFile.getName() + "\" map_type 0 interpolate 2 once } }" + inLineSeparator + + " scale 20.0 rotate <90, 0, 0>" + inLineSeparator + + " translate <-10.0, 0, -10.0>" + : " <-10.0, -0.15, -10.0>," + inLineSeparator + + " <10.0, 0.0, 10.0>" + inLineSeparator + + " pigment { color rgb <0.5 0.75 0.8> }"); + // TODO: Maybe could use the same geometry for the imageless case, would simplify code a bit + + // Definition of terrain shape if any + final String terrainDefinition = makeTerrainString(inTerrainFile, inImageFile, inLineSeparator); + // Set up output String[] outputLines = { "global_settings { ambient_light rgb <4, 4, 4> }", "", @@ -406,42 +523,30 @@ public class PovExporter extends GenericFunction "}", "", // global declares "// Global declares", - "#declare lat_line =", - " cylinder {", - " <-" + inModelSize + ", 0.1, 0>,", - " <" + inModelSize + ", 0.1, 0>,", - " 0.1 // Radius", - " pigment { color rgb <0.5 0.5 0.5> }", - " }", - "#declare lon_line =", - " cylinder {", - " <0, 0.1, -" + inModelSize + ">,", - " <0, 0.1, " + inModelSize + ">,", - " 0.1 // Radius", - " pigment { color rgb <0.5 0.5 0.5> }", - " }", "#declare point_rod =", " cylinder {", " <0, 0, 0>,", " <0, 1, 0>,", " 0.15", " open", - " pigment { color rgb <0.5 0.5 0.5> }", + " texture {", + " pigment { color rgb <0.5 0.5 0.5> }", + useImage ? " } no_shadow" : " }", " }", "", - // TODO: Export rods to POV? How to store in data? + // MAYBE: Export rods to POV? How to store in data? "#declare waypoint_sphere =", " sphere {", " <0, 0, 0>, 0.4", " texture {", " pigment {color rgb <0.1 0.1 1.0>}", " finish { phong 1 }", - " }", + useImage ? " } no_shadow" : " }", " }", "#declare track_sphere0 =", " sphere {", " <0, 0, 0>, 0.3", // size should depend on model size " texture {", - " pigment {color rgb <0.2 1.0 0.2>}", + " pigment {color rgb <0.1 0.6 0.1>}", // dark green " finish { phong 1 }", " }", " }", @@ -449,7 +554,7 @@ public class PovExporter extends GenericFunction " sphere {", " <0, 0, 0>, 0.3", // size should depend on model size " texture {", - " pigment {color rgb <0.6 1.0 0.2>}", + " pigment {color rgb <0.4 0.9 0.2>}", // green " finish { phong 1 }", " }", " }", @@ -457,7 +562,7 @@ public class PovExporter extends GenericFunction " sphere {", " <0, 0, 0>, 0.3", // size should depend on model size " texture {", - " pigment {color rgb <1.0 1.0 0.1>}", + " pigment {color rgb <0.7 0.8 0.2>}", // yellow " finish { phong 1 }", " }", " }", @@ -465,7 +570,7 @@ public class PovExporter extends GenericFunction " sphere {", " <0, 0, 0>, 0.3", // size should depend on model size " texture {", - " pigment {color rgb <1.0 1.0 1.0>}", + " pigment {color rgb <0.5 0.8 0.6>}", // greeny " finish { phong 1 }", " }", " }", @@ -473,7 +578,15 @@ public class PovExporter extends GenericFunction " sphere {", " <0, 0, 0>, 0.3", // size should depend on model size " texture {", - " pigment {color rgb <0.1 1.0 1.0>}", + " pigment {color rgb <0.2 0.9 0.9>}", // cyan + " finish { phong 1 }", + " }", + " }", + "#declare track_sphere5 =", + " sphere {", + " <0, 0, 0>, 0.3", // size should depend on model size + " texture {", + " pigment {color rgb <1.0 1.0 1.0>}", // white " finish { phong 1 }", " }", " }", @@ -488,33 +601,33 @@ public class PovExporter extends GenericFunction "#declare wall_colour = rgbt <0.5, 0.5, 0.5, 0.3>;", "", "// Base plane", "box {", - " <-" + inModelSize + ", -0.15, -" + inModelSize + ">, // Near lower left corner", - " <" + inModelSize + ", 0.15, " + inModelSize + "> // Far upper right corner", - " pigment { color rgb <0.5 0.75 0.8> }", + boxDefinition, "}", "", + // terrain + terrainDefinition, // write cardinals "// Cardinal letters N,S,E,W", "text {", " ttf \"" + fontPath + "\" \"" + I18nManager.getText("cardinal.n") + "\" 0.3, 0", " pigment { color rgb <1 1 1> }", - " translate <0, 0.2, " + inModelSize + ">", + " translate <0, 0.2, 10.0>", "}", "text {", " ttf \"" + fontPath + "\" \"" + I18nManager.getText("cardinal.s") + "\" 0.3, 0", " pigment { color rgb <1 1 1> }", - " translate <0, 0.2, -" + inModelSize + ">", + " translate <0, 0.2, -10.0>", "}", "text {", " ttf \"" + fontPath + "\" \"" + I18nManager.getText("cardinal.e") + "\" 0.3, 0", " pigment { color rgb <1 1 1> }", - " translate <" + (inModelSize * 0.97) + ", 0.2, 0>", + " translate <9.7, 0.2, 0>", "}", "text {", " ttf \"" + fontPath + "\" \"" + I18nManager.getText("cardinal.w") + "\" 0.3, 0", " pigment { color rgb <1 1 1> }", - " translate <-" + (inModelSize * 1.03) + ", 0.2, 0>", + " translate <-10.3, 0.2, 0>", "}", "", - // TODO: Light positions should relate to model size + // MAYBE: Light positions should relate to model size "// lights", "light_source { <-1, 9, -4> color rgb <0.5 0.5 0.5>}", "light_source { <1, 6, -14> color rgb <0.6 0.6 0.6>}", @@ -530,37 +643,32 @@ public class PovExporter extends GenericFunction } } - /** - * Write out all the lat and long lines to the file - * @param inWriter Writer to use for writing file - * @param inModel model object for getting lat/long lines - * @param inLineSeparator line separator to use - * @throws IOException on file writing error + * Make a description of the height_field object for the terrain, depending on terrain and image + * @param inTerrainFile terrain file, or null if none + * @param inImageFile image file, or null if none + * @param inLineSeparator line separator + * @return String for inserting into pov file */ - private void writeLatLongLines(FileWriter inWriter, ThreeDModel inModel, String inLineSeparator) - throws IOException + private static String makeTerrainString(File inTerrainFile, File inImageFile, String inLineSeparator) { - inWriter.write("// Latitude and longitude lines:"); - inWriter.write(inLineSeparator); - int numlines = inModel.getLatitudeLines().length; - for (int i=0; i }"); - inWriter.write(inLineSeparator); + if (inTerrainFile == null) {return "";} + StringBuilder sb = new StringBuilder(); + sb.append("//Terrain").append(inLineSeparator) + .append("height_field {").append(inLineSeparator) + .append("\tpng \"").append(inTerrainFile.getName()).append("\" smooth").append(inLineSeparator) + .append("\tfinish {diffuse 0.7 phong 0.2}").append(inLineSeparator); + if (inImageFile != null) { + sb.append("\tpigment {image_map { png \"").append(inImageFile.getName()).append("\" } rotate x*90}").append(inLineSeparator); } - numlines = inModel.getLongitudeLines().length; - for (int i=0; i }"); - inWriter.write(inLineSeparator); + else { + sb.append("\tpigment {color rgb <0.55 0.7 0.55> }").append(inLineSeparator); } - inWriter.write(inLineSeparator); + sb.append("\tscale 20.0").append(inLineSeparator) + .append("\ttranslate <-10.0, 0, -10.0>").append(inLineSeparator).append("}"); + return sb.toString(); } - /** * Write out all the data points to the file in the balls-and-sticks style * @param inWriter Writer to use for writing file @@ -568,7 +676,7 @@ public class PovExporter extends GenericFunction * @param inLineSeparator line separator to use * @throws IOException on file writing error */ - private void writeDataPointsBallsAndSticks(FileWriter inWriter, ThreeDModel inModel, String inLineSeparator) + private static void writeDataPointsBallsAndSticks(FileWriter inWriter, ThreeDModel inModel, String inLineSeparator) throws IOException { inWriter.write("// Data points:"); @@ -610,13 +718,12 @@ public class PovExporter extends GenericFunction * @param inLineSeparator line separator to use * @throws IOException on file writing error */ - private void writeDataPointsTubesAndWalls(FileWriter inWriter, ThreeDModel inModel, String inLineSeparator) + private static void writeDataPointsTubesAndWalls(FileWriter inWriter, ThreeDModel inModel, String inLineSeparator) throws IOException { inWriter.write("// Data points:"); inWriter.write(inLineSeparator); int numPoints = inModel.getNumPoints(); - int numTrackPoints = 0; // Loop over all points and write out waypoints as balls for (int i=0; i maxHeightCode) return maxHeightCode; return inCode;