volumesculptor.shell.ShapeJSGlobal.java Source code

Java tutorial

Introduction

Here is the source code for volumesculptor.shell.ShapeJSGlobal.java

Source

/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

package volumesculptor.shell;

import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;

import abfab3d.grid.ArrayAttributeGridByte;
import abfab3d.grid.AttributeGrid;
import abfab3d.grid.Grid;
import abfab3d.util.Bounds;
import abfab3d.grid.GridShortIntervals;

import abfab3d.grid.op.GridMaker;
import abfab3d.io.input.STLReader;
import abfab3d.io.input.SVXReader;
import abfab3d.io.input.WaveletRasterizer;
import abfab3d.io.input.X3DReader;
import abfab3d.io.output.GridSaver;
import abfab3d.io.output.MeshMakerMT;
import abfab3d.io.output.STLWriter;
import abfab3d.io.output.SVXWriter;
import abfab3d.io.output.ShellResults;

import abfab3d.io.output.SingleMaterialModelWriter;
import abfab3d.mesh.IndexedTriangleSetBuilder;
import abfab3d.mesh.WingedEdgeTriangleMesh;

import abfab3d.util.BoundingBoxCalculator;
import abfab3d.util.MathUtil;
import abfab3d.util.TriangleProducer;
import abfab3d.util.Units;

import abfab3d.datasources.ImageWrapper;

import org.apache.commons.compress.utils.IOUtils;
import org.mozilla.javascript.*;
import org.web3d.vrml.export.*;
import org.web3d.vrml.sav.BinaryContentHandler;

import static abfab3d.util.Units.MM;

import java.io.*;
import java.lang.IllegalArgumentException;
import java.util.HashMap;
import java.util.Map;

import static abfab3d.util.Output.printf;
import static abfab3d.util.Output.fmt;

/**
 * This class provides for sharing functions across multiple threads.
 * This is of particular interest to server applications.
 *
 * @author Alan Hudson
 */
public class ShapeJSGlobal {
    public static final int MAX_GRID_SIZE = 2000;
    public static final int MAX_TRIANGLE_SIZE = 3000000;
    public static final int MAX_TIME = 120 * 1000;

    public static final String SMOOTHING_WIDTH_VAR = "meshSmoothingWidth";
    public static final String ERROR_FACTOR_VAR = "meshErrorFactor";
    public static final String MESH_MIN_PART_VOLUME_VAR = "meshMinPartVolume";
    public static final String MESH_MAX_PART_COUNT_VAR = "meshMaxPartsCount";
    public static final String MESH_MAX_TRI_COUNT_VAR = "meshMaxTriCount";

    public static final int maxAttribute = 255;

    public static double errorFactorDefault = SingleMaterialModelWriter.errorFactorDefault;
    public static int maxDecimationCountDefault = 10;
    public static double smoothingWidthDefault = SingleMaterialModelWriter.smoothingWidthDefault;
    public static int blockSizeDefault = 30;
    public static double minimumVolumeDefault = SingleMaterialModelWriter.minimumVolumeDefault;
    public static int maxPartsDefault = SingleMaterialModelWriter.maxPartsDefault;
    public static int maxTriCountDefault = Integer.MAX_VALUE;

    private static String outputFolder = "/tmp";
    private static String inputFilePath = "shape.js";
    private static String inputFileName = "shape.js";
    private static String outputType = "x3d";
    private static String outputFileName = "save.x3d";

    private static boolean isLocalRun = false;
    private static boolean isDebugViz = false;
    private static int maxThreadCount;

    public static String getOutputFolder() {
        return outputFolder;
    }

    public static void setOutputFolder(String folder) {
        outputFolder = folder;
    }

    /**
       switch on full set of functionality for application running locally 
     */
    public static void setLocalRun(boolean value) {
        isLocalRun = value;
    }

    public static void setDebugViz(boolean value) {
        isDebugViz = value;
    }

    /**
     * Set the maximum threads to use
     */
    public static void setMaximumThreadCount(int value) {
        maxThreadCount = value;
    }

    public static int getMaxThreadCount() {
        return maxThreadCount;
    }

    public static String getInputFileName() {
        return inputFileName;
    }

    public static void setInputFilePath(String path) {

        inputFilePath = path;
        int index = path.lastIndexOf('/');
        if (index >= 0) {
            inputFileName = path.substring(index, path.length());
        } else {
            inputFileName = path;
        }
    }

    public static void setOutputType(String type) {
        outputType = type;
    }

    public static String getOutputType() {

        return outputType;

    }

    public static boolean isDebugViz() {
        return isDebugViz;
    }

    public static String getOutputName() {
        return outputFileName;
    }

    /*
    public static void setErrorFactor(double value){
    errorFactor = value;
    }
        
    public static void setMaxDecimationCount(int value){
    maxDecimationCount = value;
    }    
        
    public static void setSmoothingWidth(double value){
    smoothingWidth = value;
    }    
    */
    private static String[] globalFunctionsSecure = { "load", "loadImage", "createGrid", "createDebug" };
    private static String[] globalFunctionsAll = { "load", "loadImage", "createGrid", "writeAsMesh",
            "writeAsSVX", };

    private HashMap<String, Object> globals = new HashMap<String, Object>();

    public ShapeJSGlobal() {
        globals.put("MM", Units.MM);
        globals.put("MM3", Units.MM3);
        globals.put("CM", Units.CM);
        globals.put("IN", Units.IN);
        globals.put("FT", Units.FT);
        globals.put("PI", Math.PI);

        globals.put(ERROR_FACTOR_VAR, errorFactorDefault);
        globals.put(SMOOTHING_WIDTH_VAR, smoothingWidthDefault);
        globals.put(MESH_MIN_PART_VOLUME_VAR, minimumVolumeDefault);
        globals.put(MESH_MAX_PART_COUNT_VAR, maxPartsDefault);
        globals.put(MESH_MAX_TRI_COUNT_VAR, maxTriCountDefault);
    }

    public String[] getFunctions() {
        if (isLocalRun)
            return globalFunctionsAll;
        else
            return globalFunctionsSecure;

    }

    public Map<String, Object> getProperties() {
        return globals;
    }

    /**
     * Load a model into a Grid
     * <p/>
     * This method is defined as a JavaScript function.
     */
    public static Object load(Context cx, Scriptable thisObj, Object[] args, Function funObj) {
        if (args.length < 1) {
            throw Context.reportRuntimeError("No file provided for load() command");
        }
        String filename = Context.toString(args[0]);
        printf("filename: %s\n", filename);
        AttributeGrid grid = null;

        if (filename == null || filename.length() == 0) {
            throw Context.reportRuntimeError("No file provided for load() command");
        }

        double vs = 0.1 * MM;
        if (args.length > 1) {
            Object arg1 = unwrap(args[1]);
            printf("arg[1]: %s\n", arg1);
            if (arg1 instanceof Grid) {
                grid = (AttributeGrid) arg1;
            } else {
                vs = getDouble(arg1);
            }
        }
        double margin = vs;
        if (args.length > 2) {
            Object arg2 = unwrap(args[2]);
            margin = getDouble(arg2);
        }

        printf("load(%s, vs: %7.3f mm, margin: %7.3f mm)\n", filename, vs / MM, margin / MM);

        try {
            BoundingBoxCalculator bb = new BoundingBoxCalculator();
            TriangleProducer tp = null;

            if (filename.endsWith(".x3d") || filename.endsWith(".x3db") || filename.endsWith(".x3dv")) {
                tp = new X3DReader(filename);
                tp.getTriangles(bb);
            } else if (filename.endsWith(".svx")) {
                SVXReader reader = new SVXReader();
                return reader.load(filename);
            } else {
                tp = new STLReader(filename);
                tp.getTriangles(bb);
            }

            double bounds[] = new double[6];
            bb.getBounds(bounds);

            printf("   orig bounds: [ %7.3f, %7.3f], [%7.3f, %7.3f], [%7.3f, %7.3f] mm; vs: %7.3f mm\n",
                    bounds[0] / MM, bounds[1] / MM, bounds[2] / MM, bounds[3] / MM, bounds[4] / MM, bounds[5] / MM,
                    vs / MM);

            // Add a margin around the model to get some space 
            bounds = MathUtil.extendBounds(bounds, margin);
            //
            // round up to the nearest voxel 
            //
            MathUtil.roundBounds(bounds, vs);
            int nx = (int) Math.round((bounds[1] - bounds[0]) / vs);
            int ny = (int) Math.round((bounds[3] - bounds[2]) / vs);
            int nz = (int) Math.round((bounds[5] - bounds[4]) / vs);
            printf("   grid bounds: [ %7.3f, %7.3f], [%7.3f, %7.3f], [%7.3f, %7.3f] mm; vs: %7.3f mm\n",
                    bounds[0] / MM, bounds[1] / MM, bounds[2] / MM, bounds[3] / MM, bounds[4] / MM, bounds[5] / MM,
                    vs / MM);
            printf("  grid size: [%d x %d x %d]\n", nx, ny, nz);

            // range check bounds and voxelSized
            for (int i = 0; i < bounds.length; i++) {
                Float f = new Float(bounds[i]);
                if (f.isNaN()) {
                    throw new IllegalArgumentException("Grid size[" + i + "] is Not a Number.");
                }

            }

            if (nx <= 0) {
                throw new IllegalArgumentException("Grid x size <= 0: " + nx);
            }
            if (ny <= 0) {
                throw new IllegalArgumentException("Grid y size <= 0" + ny);
            }
            if (nz <= 0) {
                throw new IllegalArgumentException("Grid z size <= 0" + nz);
            }

            AttributeGrid dest = null;

            if (grid == null) {
                dest = makeEmptyGrid(new int[] { nx, ny, nz }, vs);
            } else {
                dest = grid;
            }

            dest.setGridBounds(bounds);

            WaveletRasterizer rasterizer = new WaveletRasterizer(bounds, nx, ny, nz);
            rasterizer.setMaxAttributeValue(maxAttribute);

            tp.getTriangles(rasterizer);

            rasterizer.getRaster(dest);

            System.out.println("Loaded: " + filename);

            return dest;
        } catch (Throwable t) {
            t.printStackTrace();
        }
        throw Context.reportRuntimeError("Failed to load file: " + filename);
    }

    /**
       js function to load image
       returns BufferedImage 
     */
    public static Object loadImage(Context cx, Scriptable thisObj, Object[] args, Function funObj) {
        if (args.length < 1) {
            throw Context.reportRuntimeError("No file provided for loadImage() command");
        }
        String filename = Context.toString(args[0]);

        if (filename == null || filename.length() == 0) {
            throw Context.reportRuntimeError("No file provided for load() command");
        }

        printf("loading image file: %s\n", filename);

        BufferedImage image = null;

        try {
            image = ImageIO.read(new File(filename));

        } catch (Exception e) {

        }
        if (image == null) {
            throw Context.reportRuntimeError(fmt("failed to load image file: %s\n", filename));
        }

        return new ImageWrapper(image);

    }

    static Object unwrap(Object arg) {
        if (arg instanceof NativeJavaObject)
            arg = ((NativeJavaObject) arg).unwrap();
        return arg;
    }

    /**
       write grid to a file as a decimated mesh 
     */
    public static void writeAsMesh(Context cx, Scriptable thisObj, Object[] args, Function funObj) {

        AttributeGrid grid = (AttributeGrid) ((NativeJavaObject) args[0]).unwrap();
        String fname = (String) args[1];

        printf("writeAsMesh(%s, %s)\n", grid, fname);

        double vs = grid.getVoxelSize();

        double smoothingWidth = getDouble(thisObj.get(SMOOTHING_WIDTH_VAR, thisObj));
        double errorFactor = getDouble(thisObj.get(ERROR_FACTOR_VAR, thisObj));
        double minimumVolume = getDouble(thisObj.get(MESH_MIN_PART_VOLUME_VAR, thisObj));
        int maxParts = getInteger(thisObj.get(MESH_MAX_PART_COUNT_VAR, thisObj));
        int maxTriCount = getInteger(thisObj.get(MESH_MAX_TRI_COUNT_VAR, thisObj));

        double maxDecimationError = errorFactor * vs * vs;

        MeshMakerMT meshmaker = new MeshMakerMT();
        meshmaker.setBlockSize(blockSizeDefault);
        meshmaker.setThreadCount(Runtime.getRuntime().availableProcessors());
        meshmaker.setSmoothingWidth(smoothingWidth);
        meshmaker.setMaxDecimationError(maxDecimationError);
        meshmaker.setMaxDecimationCount(maxDecimationCountDefault);
        meshmaker.setMaxAttributeValue(maxAttribute);
        meshmaker.setMaxTriangles(maxTriCount);

        IndexedTriangleSetBuilder its = new IndexedTriangleSetBuilder(160000);
        meshmaker.makeMesh(grid, its);

        printf("output mesh vertices %d faces: %d\n", its.getVertexCount(), its.getFaceCount());

        if (its.getFaceCount() > ShapeJSGlobal.MAX_TRIANGLE_SIZE) {
            System.out.println("Maximum triangle count exceeded: " + its.getFaceCount());
            throw Context.reportRuntimeError("Maximum triangle count exceeded.  Max is: "
                    + ShapeJSGlobal.MAX_TRIANGLE_SIZE + " count is: " + its.getFaceCount());
        }

        WingedEdgeTriangleMesh mesh = new WingedEdgeTriangleMesh(its.getVertices(), its.getFaces());

        printf("Mesh Min Volume: %g meshMaxParts: %d \n", minimumVolume, maxParts);

        if (minimumVolume > 0 || maxParts < Integer.MAX_VALUE) {
            ShellResults sr = GridSaver.getLargestShells(mesh, maxParts, minimumVolume);
            mesh = sr.getLargestShell();
            int regions_removed = sr.getShellsRemoved();
            System.out.println("Regions removed: " + regions_removed);
        }

        try {
            if (fname.endsWith(".stl")) {
                STLWriter stl = new STLWriter(fname);
                mesh.getTriangles(stl);
                stl.close();
            } else if (fname.endsWith(".x3d")) {
                GridSaver.writeMesh(mesh, fname);
            }

        } catch (IOException ioe) {
            ioe.printStackTrace();
        }

    }

    /**
       write grid to a SVX file
     */
    public static void writeAsSVX(Context cx, Scriptable thisObj, Object[] args, Function funObj) {

        AttributeGrid grid = (AttributeGrid) ((NativeJavaObject) args[0]).unwrap();
        String fname = (String) args[1];

        printf("writeAsSVX(%s, %s)\n", grid, fname);

        SVXWriter writer = new SVXWriter();
        writer.write(grid, fname);

    }

    /**
     * Create a debug version of the ShapeJS tree
     * <p/>
     * This method is defined as a JavaScript function.
     */
    public static Object createDebug(Context cx, Scriptable thisObj, Object[] args, Function funObj) {

        String filename;
        AttributeGrid grid;
        GridMaker maker;

        if (args.length == 3) {
            if (args[0] instanceof String) {
                filename = (String) args[0];
            } else if (args[0] instanceof NativeJavaObject) {
                filename = (String) ((NativeJavaObject) args[0]).unwrap();
            } else {
                throw new IllegalArgumentException("Unhandled type for filename: " + args[0]);
            }
            if (args[1] instanceof AttributeGrid) {
                grid = (AttributeGrid) args[1];
            } else if (args[1] instanceof NativeJavaObject) {
                grid = (AttributeGrid) ((NativeJavaObject) args[1]).unwrap();
            } else {
                throw new IllegalArgumentException("Unhandled type for grid: " + args[1]);
            }

            if (args[2] instanceof AttributeGrid) {
                maker = (GridMaker) args[2];
            } else if (args[1] instanceof NativeJavaObject) {
                maker = (GridMaker) ((NativeJavaObject) args[2]).unwrap();
            } else {
                throw new IllegalArgumentException("Unhandled type for gridMaker: " + args[2]);
            }
        } else {
            throw new IllegalArgumentException(
                    "Invalid number of arguments to createDebug(filename,Grid,GridMaker)");
        }

        DataSourceX3DViewer viewer = new DataSourceX3DViewer(maker.getDataSource(), maker.getTransform());
        FileOutputStream fos = null;
        BufferedOutputStream bos = null;

        try {
            fos = new FileOutputStream(filename);
            bos = new BufferedOutputStream(fos);
            BinaryContentHandler stream = createX3DWriter("x3dv", bos);

            viewer.generate(grid, stream);
            stream.endDocument();
        } catch (IOException ioe) {
            ioe.printStackTrace();
            return null;
        } finally {
            IOUtils.closeQuietly(bos);
            IOUtils.closeQuietly(fos);
        }
        return null;
    }

    private static BinaryContentHandler createX3DWriter(String format, OutputStream os) {
        org.web3d.util.ErrorReporter console = new PlainTextErrorReporter();

        int sigDigits = 6; // TODO: was -1 but likely needed

        BinaryContentHandler x3dWriter = null;

        if (format.equals("x3db")) {
            x3dWriter = new X3DBinaryRetainedDirectExporter(os, 3, 0, console,
                    X3DBinarySerializer.METHOD_FASTEST_PARSING, 0.001f, true);
        } else if (format.equals("x3dv")) {
            if (sigDigits > -1) {
                x3dWriter = new X3DClassicRetainedExporter(os, 3, 0, console, sigDigits);
            } else {
                x3dWriter = new X3DClassicRetainedExporter(os, 3, 0, console);
            }
        } else if (format.equals("x3d")) {
            if (sigDigits > -1) {
                x3dWriter = new X3DXMLRetainedExporter(os, 3, 0, console, sigDigits);
            } else {
                x3dWriter = new X3DXMLRetainedExporter(os, 3, 0, console);
            }
        } else {
            throw new IllegalArgumentException("Unhandled file format: " + format);
        }

        x3dWriter.startDocument("", "", "utf8", "#X3D", "V3.0", "");
        x3dWriter.profileDecl("Immersive");
        x3dWriter.startNode("NavigationInfo", null);
        x3dWriter.startField("avatarSize");
        x3dWriter.fieldValue(new float[] { 0.01f, 1.6f, 0.75f }, 3);
        x3dWriter.endNode(); // NavigationInfo

        return x3dWriter;
    }

    /**
     * Create a new grid
     * <p/>
     * This method is defined as a JavaScript function.
     */
    public static Object createGrid(Context cx, Scriptable thisObj, Object[] args, Function funObj) {

        double[] grid_bounds = new double[6];
        double vs = 0.1 * MM;

        for (int i = 0; i < args.length; i++) {
            String st = null;

            if (args[i] instanceof NativeJavaObject) {
                Object o = ((NativeJavaObject) args[i]).unwrap();
                st = "JNO class=" + o.getClass() + " val: " + (o.toString());
            } else {
                st = args[i].toString();
            }
            System.out.println("arg: " + i + " val: " + st);
        }
        if (args.length == 1) { // grid 
            if (args[0] instanceof AttributeGrid) {
                AttributeGrid grid = (AttributeGrid) args[0];
                grid.getGridBounds(grid_bounds);
                vs = grid.getVoxelSize();
            } else if (args[0] instanceof NativeJavaObject) {
                AttributeGrid grid = (AttributeGrid) ((NativeJavaObject) args[0]).unwrap();
                grid.getGridBounds(grid_bounds);
                vs = grid.getVoxelSize();
            }
        } else if (args.length == 2) { // Bounds, voxelSize
            Bounds bounds;
            if (args[0] instanceof Bounds) {
                bounds = (Bounds) args[0];
            } else {
                bounds = (Bounds) ((NativeJavaObject) args[0]).unwrap();
            }
            grid_bounds[0] = bounds.xmin;
            grid_bounds[1] = bounds.xmax;
            grid_bounds[2] = bounds.ymin;
            grid_bounds[3] = bounds.ymax;
            grid_bounds[4] = bounds.zmin;
            grid_bounds[5] = bounds.zmax;
            vs = getDouble(args[1]);
        } else if (args.length == 4) { // grid, sizex, sizey, sizez 
            if (args[0] instanceof AttributeGrid) {
                AttributeGrid grid = (AttributeGrid) args[0];
                grid.getGridBounds(grid_bounds);
                vs = grid.getVoxelSize();
            } else if (args[0] instanceof NativeJavaObject) {
                AttributeGrid grid = (AttributeGrid) ((NativeJavaObject) args[0]).unwrap();
                grid.getGridBounds(grid_bounds);
                vs = grid.getVoxelSize();
            }
            double x = getDouble(args[1]);
            double y = getDouble(args[2]);
            double z = getDouble(args[3]);

            grid_bounds[0] -= x;
            grid_bounds[1] += x;
            grid_bounds[2] -= y;
            grid_bounds[3] += y;
            grid_bounds[4] -= z;
            grid_bounds[5] += z;
        } else if (args.length == 7) { // bounds[], voxleSize
            grid_bounds[0] = getDouble(args[0]);
            grid_bounds[1] = getDouble(args[1]);
            grid_bounds[2] = getDouble(args[2]);
            grid_bounds[3] = getDouble(args[3]);
            grid_bounds[4] = getDouble(args[4]);
            grid_bounds[5] = getDouble(args[5]);

            vs = getDouble(args[6]);
        } else {
            throw new IllegalArgumentException(
                    "Invalid number of arguments to CreateGrid(xmin,xmax,ymin,ymax,zmin,zmax,voxelSize)");
        }

        // range check bounds and voxelSized
        for (int i = 0; i < grid_bounds.length; i++) {
            Float f = new Float(grid_bounds[i]);
            if (f.isNaN()) {
                throw new IllegalArgumentException("Grid size[" + i + "] is Not a Number.");
            }
        }

        if (args.length != 1) {
            // When passed a grid make sure its exactly the same size
            grid_bounds = MathUtil.roundBounds(grid_bounds, vs);
        }
        int[] gs = MathUtil.getGridSize(grid_bounds, vs);

        // range check bounds and voxelSized
        for (int i = 0; i < gs.length; i++) {
            if (gs[i] <= 0) {
                throw new IllegalArgumentException("Grid size[" + i + "] <= 0");
            }
        }

        AttributeGrid dest = makeEmptyGrid(gs, vs);

        System.out.println("Creating grid: " + java.util.Arrays.toString(gs)
                + java.util.Arrays.toString(grid_bounds) + " vs: " + vs);
        dest.setGridBounds(grid_bounds);

        return cx.getWrapFactory().wrapAsJavaObject(cx, funObj.getParentScope(), dest, null);
    }

    /**
     * A number in Javascript might be of several forms.  Handle all of them here
     * @param o
     * @return
     */
    private static Double getDouble(Object o) {
        if (o instanceof Double) {
            return (Double) o;
        }

        if (o instanceof NativeJavaObject) {
            return getDouble(((NativeJavaObject) o).unwrap());
        }

        if (o instanceof Number) {
            return ((Number) o).doubleValue();
        }

        if (o instanceof String) {
            return Double.parseDouble((String) o);
        }

        return null;
    }

    private static Integer getInteger(Object o) {
        if (o instanceof Integer) {
            return (Integer) o;
        }

        if (o instanceof NativeJavaObject) {
            return getInteger(((NativeJavaObject) o).unwrap());
        }

        if (o instanceof Number) {
            return ((Number) o).intValue();
        }

        if (o instanceof String) {
            return Integer.parseInt((String) o);
        }

        return null;
    }

    private static AttributeGrid makeEmptyGrid(int[] gs, double vs) {
        AttributeGrid dest = null;

        long voxels = ((long) (gs[0])) * gs[1] * gs[2];

        printf("Creating grid: %d %d %d\n", gs[0], gs[1], gs[2], voxels);
        long max_voxels = (long) MAX_GRID_SIZE * MAX_GRID_SIZE * MAX_GRID_SIZE;

        if (voxels > max_voxels) {
            System.out.println("Maximum grid size exceeded.  Max is: " + MAX_GRID_SIZE + "^3 grid is: " + gs[0]
                    + " " + gs[1] + " " + gs[2]);
            throw Context.reportRuntimeError("Maximum grid size exceeded.  Max is: " + MAX_GRID_SIZE
                    + "^3 grid is: " + gs[0] + " " + gs[1] + " " + gs[2]);
        }

        long MAX_MEMORY = Integer.MAX_VALUE;
        if (voxels > MAX_MEMORY) {
            dest = new GridShortIntervals(gs[0], gs[1], gs[2], vs, vs);
        } else {
            dest = new ArrayAttributeGridByte(gs[0], gs[1], gs[2], vs, vs);
        }

        return dest;
    }

}

// unused stuff 

/**
 * <p/>
 * This method is defined as a JavaScript function.
 not used 
 */
/*
public static void _save(Context cx, Scriptable thisObj,
                        Object[] args, Function funObj) {
    
    
    
    AttributeGrid grid = null;
    
    boolean show_slices = false;
    
    if (args.length > 0) {
        if (args[0] instanceof Boolean) {
            show_slices = (Boolean) args[0];
        } else if (args[0] instanceof AttributeGrid) {
            grid = (AttributeGrid) args[0];
        } else if (args[0] instanceof NativeJavaObject) {
            grid = (AttributeGrid) ((NativeJavaObject)args[0]).unwrap();
        }
    }
    
    if (grid == null) {
        System.out.println("No grid specified");
    }
    if (args.length > 1) {
        if (args[1] instanceof Boolean) {
            show_slices = (Boolean) args[0];
        }
    }
    
    double vs = grid.getVoxelSize();
    
    
    if (show_slices) {
        SlicesWriter slicer = new SlicesWriter();
        slicer.setFilePattern("/tmp/slices2/slice_%03d.png");
        slicer.setCellSize(5);
        slicer.setVoxelSize(4);
    
        slicer.setMaxAttributeValue(maxAttribute);
        try {
            slicer.writeSlices(grid);
        } catch(IOException ioe) {
            ioe.printStackTrace();
        }
    }
        
    System.out.println("Saving world2: " + grid + " to triangles");
    
    
    double maxDecimationError = errorFactor * vs * vs;
    
    Object smoothing_width = thisObj.get("SMOOTHING_WIDTH", thisObj);
    
    if (smoothing_width instanceof Number) {
        smoothingWidth = ((Number)smoothing_width).doubleValue();
    }
    System.out.println("Smoothing width: " + smoothingWidth);
    // Write out the grid to an STL file
    MeshMakerMT meshmaker = new MeshMakerMT();
    meshmaker.setBlockSize(blockSize);
    meshmaker.setThreadCount(Runtime.getRuntime().availableProcessors());
    meshmaker.setSmoothingWidth(smoothingWidth);
    meshmaker.setMaxDecimationError(maxDecimationError);
    meshmaker.setMaxDecimationCount(maxDecimationCount);
    meshmaker.setMaxAttributeValue(maxAttribute);
    
    IndexedTriangleSetBuilder its = new IndexedTriangleSetBuilder(160000);
    meshmaker.makeMesh(grid, its);
    
    System.out.println("Vertices: " + its.getVertexCount() + " faces: " + its.getFaceCount());
    WingedEdgeTriangleMesh mesh = new WingedEdgeTriangleMesh(its.getVertices(), its.getFaces());
    
    String path = "/tmp";
    String name = "save.x3d";
    String out = path + "/" + name  ;
    double[] bounds_min = new double[3];
    double[] bounds_max = new double[3];
    
    grid.getGridBounds(bounds_min,bounds_max);
    double max_axis = Math.max(bounds_max[0] - bounds_min[0], bounds_max[1] - bounds_min[1]);
    max_axis = Math.max(max_axis, bounds_max[2] - bounds_min[2]);
    
    double z = 2 * max_axis / Math.tan(Math.PI / 4);
    float[] pos = new float[] {0,0,(float) z};
    
    try {
        GridSaver.writeMesh(mesh, out);
        X3DViewer.viewX3DOM(name, pos);
    } catch(IOException ioe) {
        ioe.printStackTrace();
    }
    
}
*/

/**
 * Stops execution and shows a grid.  TODO:  How to make it stop?
 * <p/>
 * This method is defined as a JavaScript function.
 not used 
 */
/*
public static void _show(Context cx, Scriptable thisObj,
                        Object[] args, Function funObj) {
    
    
    
    AttributeGrid grid = null;
    
    boolean show_slices = false;
    
    if (args.length > 0) {
        if (args[0] instanceof Boolean) {
            show_slices = (Boolean) args[0];
        } else if (args[0] instanceof AttributeGrid) {
            grid = (AttributeGrid) args[0];
        } else if (args[0] instanceof NativeJavaObject) {
            grid = (AttributeGrid) ((NativeJavaObject)args[0]).unwrap();
        }
    }
    
    if (grid == null) {
        System.out.println("No grid specified");
    }
    if (args.length > 1) {
        if (args[1] instanceof Boolean) {
            show_slices = (Boolean) args[0];
        }
    }
    
    double vs = grid.getVoxelSize();
    
    
    if (show_slices) {
        SlicesWriter slicer = new SlicesWriter();
        slicer.setFilePattern("/tmp/slices2/slice_%03d.png");
        slicer.setCellSize(5);
        slicer.setVoxelSize(4);
    
        slicer.setMaxAttributeValue(maxAttribute);
        try {
            slicer.writeSlices(grid);
        } catch(IOException ioe) {
            ioe.printStackTrace();
        }
    }
    
    System.out.println("Saving world: " + grid + " to triangles");
    
    Object smoothing_width = thisObj.get(SMOOTHING_WIDTH_VAR, thisObj);
    double sw;
    double ef;
    
    if (smoothing_width instanceof Number) {
        sw = ((Number)smoothing_width).doubleValue();
    } else {
        sw = smoothingWidth;
    }
    
    Object error_factor = thisObj.get(ERROR_FACTOR_VAR, thisObj);
    
    if (smoothing_width instanceof Number) {
        ef = ((Number)error_factor).doubleValue();
    } else {
        ef = errorFactor;
    }
    
    double maxDecimationError = ef * vs * vs;
    // Write out the grid to an STL file
    MeshMakerMT meshmaker = new MeshMakerMT();
    meshmaker.setBlockSize(blockSize);
    meshmaker.setThreadCount(Runtime.getRuntime().availableProcessors());
    meshmaker.setSmoothingWidth(sw);
    meshmaker.setMaxDecimationError(maxDecimationError);
    meshmaker.setMaxDecimationCount(maxDecimationCount);
    meshmaker.setMaxAttributeValue(maxAttribute);
    
    IndexedTriangleSetBuilder its = new IndexedTriangleSetBuilder(160000);
    meshmaker.makeMesh(grid, its);
    
    System.out.println("Vertices: " + its.getVertexCount() + " faces: " + its.getFaceCount());
    
    System.out.println("Bigger then max?" + (its.getFaceCount() > MAX_TRIANGLE_SIZE));
    if (its.getFaceCount() > MAX_TRIANGLE_SIZE) {
        System.out.println("Maximum triangle count exceeded: " + its.getFaceCount());
        throw Context.reportRuntimeError(
                "Maximum triangle count exceeded.  Max is: " + MAX_TRIANGLE_SIZE + " count is: " + its.getFaceCount());
    }
    
    WingedEdgeTriangleMesh mesh = new WingedEdgeTriangleMesh(its.getVertices(), its.getFaces());
    
    
    try {
        String path = "/tmp";
        String name = "save.x3d";
        String out = path + "/" + name  ;
        double[] bounds_min = new double[3];
        double[] bounds_max = new double[3];
    
        grid.getGridBounds(bounds_min,bounds_max);
        double max_axis = Math.max(bounds_max[0] - bounds_min[0], bounds_max[1] - bounds_min[1]);
        max_axis = Math.max(max_axis, bounds_max[2] - bounds_min[2]);
    
        double z = 2 * max_axis / Math.tan(Math.PI / 4);
        float[] pos = new float[] {0,0,(float) z};
    
        GridSaver.writeMesh(mesh, out);
        X3DViewer.viewX3DOM(name, pos);
    } catch(IOException ioe) {
        ioe.printStackTrace();
    }
    
    throw new ShowException();
}
*/