X-Git-Url: http://gitweb.fperrin.net/?p=GpsPrune.git;a=blobdiff_plain;f=tim%2Fprune%2Fsave%2FPovExporter.java;h=6f8a32202ef227633ffa533dc62df2f6159e2007;hp=1afa255f1c7bd39852ed50764c0d167de2a033b4;hb=52bf9e8686c916be37a26a0b75340393d4478b05;hpb=ca9bdb3916f9c39adbbf95d06ac95c21dafbb4e6 diff --git a/tim/prune/save/PovExporter.java b/tim/prune/save/PovExporter.java index 1afa255..6f8a322 100644 --- a/tim/prune/save/PovExporter.java +++ b/tim/prune/save/PovExporter.java @@ -9,7 +9,11 @@ import java.awt.event.ActionListener; import java.io.File; import java.io.FileWriter; import java.io.IOException; +import java.util.ArrayList; +import java.util.Iterator; +import javax.swing.BoxLayout; +import javax.swing.ButtonGroup; import javax.swing.JButton; import javax.swing.JDialog; import javax.swing.JFileChooser; @@ -17,13 +21,15 @@ import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JOptionPane; import javax.swing.JPanel; +import javax.swing.JRadioButton; import javax.swing.JTextField; import javax.swing.SwingConstants; -import javax.swing.filechooser.FileFilter; +import tim.prune.Config; import tim.prune.I18nManager; import tim.prune.UpdateMessageBroker; import tim.prune.data.Track; +import tim.prune.load.GenericFileFilter; import tim.prune.threedee.LineDialog; import tim.prune.threedee.ThreeDModel; @@ -41,11 +47,11 @@ public class PovExporter private JTextField _cameraXField = null, _cameraYField = null, _cameraZField = null; private JTextField _fontName = null, _altitudeCapField = null; private int _altitudeCap = ThreeDModel.MINIMUM_ALTITUDE_CAP; + private JRadioButton _ballsAndSticksButton = null; // defaults private static final double DEFAULT_CAMERA_DISTANCE = 30.0; private static final String DEFAULT_FONT_FILE = "crystal.ttf"; - // alternative font: DejaVuSans-Bold.ttf /** @@ -158,7 +164,11 @@ public class PovExporter JLabel fontLabel = new JLabel(I18nManager.getText("dialog.exportpov.font")); fontLabel.setHorizontalAlignment(SwingConstants.TRAILING); centralPanel.add(fontLabel); - _fontName = new JTextField(DEFAULT_FONT_FILE, 12); + String defaultFont = Config.getPovrayFont(); + if (defaultFont == null || defaultFont.equals("")) { + defaultFont = DEFAULT_FONT_FILE; + } + _fontName = new JTextField(defaultFont, 12); _fontName.setAlignmentX(Component.LEFT_ALIGNMENT); centralPanel.add(_fontName); //coordinates of camera @@ -184,8 +194,32 @@ public class PovExporter _altitudeCapField = new JTextField("" + _altitudeCap); centralPanel.add(_altitudeCapField); - JPanel flowPanel = new JPanel(); - flowPanel.add(centralPanel); + // Radio buttons for style - balls on sticks or tubes + JPanel stylePanel = new JPanel(); + stylePanel.setLayout(new GridLayout(0, 2, 10, 4)); + JLabel styleLabel = new JLabel(I18nManager.getText("dialog.exportpov.modelstyle")); + styleLabel.setHorizontalAlignment(SwingConstants.TRAILING); + stylePanel.add(styleLabel); + JPanel radioPanel = new JPanel(); + radioPanel.setLayout(new BoxLayout(radioPanel, BoxLayout.Y_AXIS)); + _ballsAndSticksButton = new JRadioButton(I18nManager.getText("dialog.exportpov.ballsandsticks")); + _ballsAndSticksButton.setSelected(false); + radioPanel.add(_ballsAndSticksButton); + JRadioButton tubesButton = new JRadioButton(I18nManager.getText("dialog.exportpov.tubesandwalls")); + tubesButton.setSelected(true); + radioPanel.add(tubesButton); + ButtonGroup group = new ButtonGroup(); + group.add(_ballsAndSticksButton); group.add(tubesButton); + stylePanel.add(radioPanel); + + // add this grid 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(stylePanel); + holderPanel.add(boxPanel, BorderLayout.CENTER); // show lines button JButton showLinesButton = new JButton(I18nManager.getText("button.showlines")); @@ -201,8 +235,11 @@ public class PovExporter dialog.showDialog(); } }); + JPanel flowPanel = new JPanel(); + flowPanel.setLayout(new FlowLayout()); flowPanel.add(showLinesButton); - panel.add(flowPanel, BorderLayout.CENTER); + holderPanel.add(flowPanel, BorderLayout.EAST); + panel.add(holderPanel, BorderLayout.CENTER); return panel; } @@ -219,19 +256,15 @@ public class PovExporter // OK pressed, so choose output file if (_fileChooser == null) + { _fileChooser = new JFileChooser(); - _fileChooser.setDialogType(JFileChooser.SAVE_DIALOG); - _fileChooser.setFileFilter(new FileFilter() { - public boolean accept(File f) - { - return (f != null && (f.isDirectory() || f.getName().toLowerCase().endsWith(".pov"))); - } - public String getDescription() - { - return I18nManager.getText("dialog.exportpov.filetype"); - } - }); - _fileChooser.setAcceptAllFileFilterUsed(false); + _fileChooser.setDialogType(JFileChooser.SAVE_DIALOG); + _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);} + } // Allow choose again if an existing file is selected boolean chooseAgain = false; @@ -258,6 +291,8 @@ public class PovExporter if (exportFile(file)) { // file saved + // Store directory in config for later + Config.setWorkingDirectory(file.getParentFile()); } else { @@ -306,7 +341,12 @@ public class PovExporter writeLatLongLines(writer, model, lineSeparator); // write out points - writeDataPoints(writer, model, lineSeparator); + if (_ballsAndSticksButton.isSelected()) { + writeDataPointsBallsAndSticks(writer, model, lineSeparator); + } + else { + writeDataPointsTubesAndWalls(writer, model, lineSeparator); + } // everything worked UpdateMessageBroker.informSubscribers(I18nManager.getText("confirm.save.ok1") @@ -433,7 +473,16 @@ public class PovExporter " pigment {color rgb <0.1 1.0 1.0>}", " finish { phong 1 }", " }", - " }", "", + " }", + "#declare track_sphere_t =", + " sphere {", + " <0, 0, 0>, 0.25", // size should depend on model size + " texture {", + " pigment {color rgb <0.6 1.0 0.2>}", + " finish { phong 1 }", + " } no_shadow", + " }", + "#declare wall_colour = rgbt <0.5, 0.5, 0.5, 0.3>;", "", "// Base plane", "box {", " <-" + inModelSize + ", -0.15, -" + inModelSize + ">, // Near lower left corner", @@ -510,13 +559,13 @@ public class PovExporter /** - * Write out all the data points to the file + * Write out all the data points to the file in the balls-and-sticks style * @param inWriter Writer to use for writing file * @param inModel model object for getting data points * @param inLineSeparator line separator to use * @throws IOException on file writing error */ - private void writeDataPoints(FileWriter inWriter, ThreeDModel inModel, String inLineSeparator) + private void writeDataPointsBallsAndSticks(FileWriter inWriter, ThreeDModel inModel, String inLineSeparator) throws IOException { inWriter.write("// Data points:"); @@ -551,6 +600,159 @@ public class PovExporter } + /** + * Write out all the data points to the file in the tubes-and-walls style + * @param inWriter Writer to use for writing file + * @param inModel model object for getting data points + * @param inLineSeparator line separator to use + * @throws IOException on file writing error + */ + private 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 }"); + // vertical rod (if altitude positive) + if (inModel.getScaledAltValue(i) > 0.0) + { + inWriter.write(inLineSeparator); + inWriter.write("object { point_rod translate <" + inModel.getScaledHorizValue(i) + ",0," + + inModel.getScaledVertValue(i) + "> scale <1," + inModel.getScaledAltValue(i) + ",1> }"); + } + inWriter.write(inLineSeparator); + } + else {numTrackPoints++;} + } + inWriter.write(inLineSeparator); + + // Loop over all the track segments + ArrayList segmentList = getSegmentList(inModel); + Iterator segmentIterator = segmentList.iterator(); + while (segmentIterator.hasNext()) + { + ModelSegment segment = (ModelSegment) segmentIterator.next(); + int segLength = segment.getNumTrackPoints(); + + // if the track segment is long enough, do a cubic spline sphere sweep + if (segLength <= 1) + { + // single point in segment - just draw sphere + int index = segment.getStartIndex(); + inWriter.write("object { track_sphere_t" + + " translate <" + inModel.getScaledHorizValue(index) + "," + inModel.getScaledAltValue(index) + + "," + inModel.getScaledVertValue(index) + "> }"); + // maybe draw some kind of polygon too or rod? + } + else + { + writeSphereSweep(inWriter, inModel, segment, inLineSeparator); + } + + // Write wall underneath segment + if (segLength > 1) + { + writePolygonWall(inWriter, inModel, segment, inLineSeparator); + } + } + } + + + /** + * Write out a single sphere sweep using either cubic spline or linear spline + * @param inWriter Writer to use for writing file + * @param inModel model object for getting data points + * @param inSegment model segment to draw + * @param inLineSeparator line separator to use + * @throws IOException on file writing error + */ + private static void writeSphereSweep(FileWriter inWriter, ThreeDModel inModel, ModelSegment inSegment, String inLineSeparator) + throws IOException + { + // 3d sphere sweep + inWriter.write("// Sphere sweep:"); + inWriter.write(inLineSeparator); + String splineType = inSegment.getNumTrackPoints() < 5?"linear_spline":"cubic_spline"; + inWriter.write("sphere_sweep { "); inWriter.write(splineType); + inWriter.write(" " + inSegment.getNumTrackPoints() + ","); + inWriter.write(inLineSeparator); + // Loop over all points in this segment and write out sphere sweep + for (int i=inSegment.getStartIndex(); i<=inSegment.getEndIndex(); i++) + { + if (inModel.getPointType(i) != ThreeDModel.POINT_TYPE_WAYPOINT) + { + inWriter.write(" <" + inModel.getScaledHorizValue(i) + "," + inModel.getScaledAltValue(i) + + "," + inModel.getScaledVertValue(i) + ">, 0.25"); + inWriter.write(inLineSeparator); + } + } + inWriter.write(" tolerance 0.1"); + inWriter.write(inLineSeparator); + inWriter.write(" texture { pigment {color rgb <0.6 1.0 0.2>} finish {phong 1} }"); + inWriter.write(inLineSeparator); + inWriter.write(" no_shadow"); + inWriter.write(inLineSeparator); + inWriter.write("}"); + inWriter.write(inLineSeparator); + } + + + /** + * Write out a single polygon-based wall for the tubes-and-walls style + * @param inWriter Writer to use for writing file + * @param inModel model object for getting data points + * @param inSegment model segment to draw + * @param inLineSeparator line separator to use + * @throws IOException on file writing error + */ + private static void writePolygonWall(FileWriter inWriter, ThreeDModel inModel, ModelSegment inSegment, String inLineSeparator) + throws IOException + { + // wall + inWriter.write(inLineSeparator); + inWriter.write("// wall between sweep and floor:"); + inWriter.write(inLineSeparator); + // Loop over all points in this segment again and write out polygons + int prevIndex = -1; + for (int i=inSegment.getStartIndex(); i<=inSegment.getEndIndex(); i++) + { + if (inModel.getPointType(i) != ThreeDModel.POINT_TYPE_WAYPOINT) + { + if (prevIndex >= 0) + { + double xDiff = inModel.getScaledHorizValue(i) - inModel.getScaledHorizValue(prevIndex); + double yDiff = inModel.getScaledVertValue(i) - inModel.getScaledVertValue(prevIndex); + double dist = Math.sqrt(xDiff * xDiff + yDiff * yDiff); + if (dist > 0) + { + inWriter.write("polygon {"); + inWriter.write(" 5, <" + inModel.getScaledHorizValue(prevIndex) + ", 0.0, " + inModel.getScaledVertValue(prevIndex) + ">,"); + inWriter.write(" <" + inModel.getScaledHorizValue(prevIndex) + ", " + inModel.getScaledAltValue(prevIndex) + ", " + + inModel.getScaledVertValue(prevIndex) + ">,"); + inWriter.write(" <" + inModel.getScaledHorizValue(i) + ", " + inModel.getScaledAltValue(i) + ", " + + inModel.getScaledVertValue(i) + ">,"); + inWriter.write(" <" + inModel.getScaledHorizValue(i) + ", 0.0, " + inModel.getScaledVertValue(i) + ">,"); + inWriter.write(" <" + inModel.getScaledHorizValue(prevIndex) + ", 0.0, " + inModel.getScaledVertValue(prevIndex) + ">"); + inWriter.write(" pigment { color wall_colour } no_shadow"); + inWriter.write("}"); + inWriter.write(inLineSeparator); + } + } + prevIndex = i; + } + } + } + + /** * @param inCode height code to check * @return validated height code within range 0 to max @@ -579,4 +781,46 @@ public class PovExporter catch (Exception e) {} // ignore parse failures return "" + value; } + + /** + * Go through the points making a list of the segment starts and the number of track points in each segment + * @param inModel model containing data + * @return list of ModelSegment objects + */ + private static ArrayList getSegmentList(ThreeDModel inModel) + { + ArrayList segmentList = new ArrayList(); + if (inModel != null && inModel.getNumPoints() > 0) + { + ModelSegment currSegment = null; + int numTrackPoints = 0; + for (int i=0; i 0) + { + currSegment.setEndIndex(inModel.getNumPoints()-1); + currSegment.setNumTrackPoints(numTrackPoints); + segmentList.add(currSegment); + } + } + return segmentList; + } }