diff --git a/src/main/java/com/vitco/app/export/SlicesExporter.java b/src/main/java/com/vitco/app/export/SlicesExporter.java new file mode 100644 index 00000000..cedbf77d --- /dev/null +++ b/src/main/java/com/vitco/app/export/SlicesExporter.java @@ -0,0 +1,81 @@ +package com.vitco.app.export; + +import com.vitco.app.core.data.Data; +import com.vitco.app.core.data.container.Voxel; +import com.vitco.app.layout.content.console.ConsoleInterface; +import com.vitco.app.util.components.progressbar.ProgressDialog; + +import javax.imageio.ImageIO; +import java.awt.image.BufferedImage; +import java.io.File; +import java.io.IOException; + +/** + * Export cross-section slices of the voxel into + * series of bitmap files. + */ +public class SlicesExporter extends AbstractExporter { + + public SlicesExporter(File exportTo, Data data, ProgressDialog dialog, ConsoleInterface console) throws IOException { + super(exportTo, data, dialog, console); + } + + private int ax1 = -1; + private int ax2 = -1; + private int ax3 = -1; + public void setSliceDirection(String sliceAxis) { + ax1 = sliceAxis.charAt(0) - 120; // x = 0, y = 1, z = 2 + ax2 = sliceAxis.equals("x") ? 2 : 0; + ax3 = sliceAxis.equals("y") ? 2 : 1; + } + + private String exportFormat = "png"; + public void setExportFormat(String format) { + this.exportFormat = format; + } + + private boolean invertOrder = false; + public void setInvertOrder(boolean invert) { + this.invertOrder = invert; + } + + // allow explicit access since multiple files are generated + public boolean generateImages() throws IOException { + int[] size = getSize(); + int nSlices = size[ax1]; // number of slices + int width = size[ax2]; // width of slice bitmaps + int height = size[ax3]; // height of slice bitmaps + + // create images + BufferedImage[] slices = new BufferedImage[nSlices]; + for (int idx = 0; idx < nSlices; idx++) { + slices[idx] = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB); + } + + // paint images + int[] min = getMin(); + for (Voxel voxel : this.data.getVisibleLayerVoxel()) { + int[] pos = voxel.getPosAsInt(); + slices[pos[ax1] - min[ax1]].setRGB(pos[ax2] - min[ax2], pos[ax3] - min[ax3], voxel.getColor().getRGB()); + } + + // save images + for (int idx = 0; idx < nSlices; idx++) { + String fileName = String.format( + "%s_%d.%s", + exportTo.getAbsolutePath(), + invertOrder ? slices.length - idx : idx + 1, + exportFormat + ); + System.out.println("Creating file: " + fileName); + ImageIO.write(slices[idx], exportFormat, new File(fileName)); + } + + return true; // success + } + + @Override + protected boolean writeFile() { + throw new UnsupportedOperationException("Not implemented yet"); + } +} diff --git a/src/main/java/com/vitco/app/layout/content/menu/MainMenuLogic.java b/src/main/java/com/vitco/app/layout/content/menu/MainMenuLogic.java index dae93d1a..fbd6c858 100644 --- a/src/main/java/com/vitco/app/layout/content/menu/MainMenuLogic.java +++ b/src/main/java/com/vitco/app/layout/content/menu/MainMenuLogic.java @@ -631,9 +631,38 @@ protected Object doInBackground() throws Exception { // --------------- + // add bitmap slices exporter + FieldSet slicesExporter = new FieldSet("slices_format", "Sliced model (png/gif)"); + // add information for the exporter + LabelModule slicesInfo = new LabelModule("Info: Generates series of bitmap images. Each image corresponds to " + + "cross-section of visible voxels at each coordinate of selected axis.\nImportant: Can generate a large " + + "number of files!"); + slicesExporter.addComponent(slicesInfo); + slicesExporter.addComponent(new SeparatorModule("Slice Along")); + slicesExporter.addComponent(new SeparatorModule("Slice Along")); + ComboBoxModule sliceAxis = new ComboBoxModule("axis", new String[][]{ + new String[]{"x", "x-axis"}, + new String[]{"y", "y-axis"}, + new String[]{"z", "z-axis"} + }, 0); + slicesExporter.addComponent(sliceAxis); + + slicesExporter.addComponent(new SeparatorModule("Output Format")); + ComboBoxModule formatSelect = new ComboBoxModule("export_format", new String[][]{ + new String[]{"png", "*.png"}, + new String[]{"gif", "*.gif"}, + }, 0); + slicesExporter.addComponent(formatSelect); + slicesExporter.addComponent(new SeparatorModule("Misc")); + slicesExporter.addComponent(new CheckBoxModule("invert", "Invert direction", false)); + LabelModule invertInfo = new LabelModule("This option effectively inverts order of the numbers in file names."); + slicesExporter.addComponent(invertInfo); + + // --------------- + // add all formats dialog.addComboBox("export_type", new FieldSet[] { - collada, magicaVoxelExporter, voxVoxLapExporter, kv6Exporter, pnxExporter, qbExporter, imageRenderer + collada, magicaVoxelExporter, voxVoxLapExporter, kv6Exporter, pnxExporter, qbExporter, slicesExporter, imageRenderer }, 0); // --------------- @@ -1112,6 +1141,49 @@ protected Object doInBackground() throws Exception { // =========== } + else if (dialog.is("export_type=slices_format")) { + + // =========== + // -- handle sliced file format + + // create progress dialog + final ProgressDialog progressDialog = new ProgressDialog(frame); + + // do the exporting + progressDialog.start(new ProgressWorker() { + @Override + protected Object doInBackground() { + + final File exportTo = new File(baseName); + + // export sliced files format + boolean success; + long time = System.currentTimeMillis(); + try { + SlicesExporter exporter = new SlicesExporter(exportTo, data, progressDialog, console); + exporter.setSliceDirection(dialog.getValue("slices_format.axis")); + exporter.setExportFormat(dialog.getValue("slices_format.export_format")); + exporter.setInvertOrder(dialog.is("slices_format.invert=true")); + + success = exporter.generateImages(); + } catch (IOException ignored) { + success = false; + } + if (success) { + console.addLine( + String.format(langSelector.getString("export_file_successful"), + System.currentTimeMillis() - time) + ); + } else { + console.addLine(langSelector.getString("export_file_error")); + } + + return null; + } + }); + + // =========== + } // ----- // store serialization preferences.storeObject("export_dialog_serialization", dialog.getSerialization());