Issues

Issue #26 open

Ascii STL files reader "patch"

Anonymous created an issue

In case there is a need for it, here is a very simple not very elegant STL Reader for Ascii files.

import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.io.StreamTokenizer;

import toxi.geom.Vec3D; import toxi.geom.mesh.Mesh3D; import toxi.geom.mesh.TriangleMesh; import android.util.Log;

public class STLAsciiReader {

private StreamTokenizer t;

private Mesh3D tmpMesh;

private ParserState state = new HeadState();

public Mesh3D load(String fileName) throws FileNotFoundException {
    return load(new FileInputStream(fileName));
}

public Mesh3D load(InputStream fis) {
    tmpMesh = new TriangleMesh();
    Log.i("STLAsciiReader", "parsing...");
    t = new StreamTokenizer(fis);
    t.resetSyntax();
    t.wordChars(33, 126);
    t.whitespaceChars(0, 32);

    while ( state != null) {
        Log.i("STLAsciiReader", "parsing...");
        state = state.next();
    }

    return tmpMesh;

}



public interface ParserState {
    ParserState next() throws IllegalStateException;
}

public class HeadState implements ParserState {

    @Override
    public ParserState next() throws IllegalStateException {
        try {
            // solid
            t.nextToken();
            if (!"solid".equalsIgnoreCase(t.sval))
                throw new IllegalStateException(t.sval);
            // name
            t.nextToken();
            return new FacetState();
        } catch (IOException e) {
            throw new IllegalStateException(e.getMessage());
        }
    }
}

class FacetState implements ParserState {
    @Override
    public ParserState next() throws IllegalStateException {
        try {
            t.nextToken();
            if (!"facet".equalsIgnoreCase(t.sval))
                throw new IllegalStateException(t.sval);
            t.nextToken();
            if (!"normal".equalsIgnoreCase(t.sval))
                throw new IllegalStateException(t.sval);
            t.nextToken();
            Log.i("STLAsciiReader", "Token:" + t.sval);
            t.nextToken();
            Log.i("STLAsciiReader", "Token:" + t.sval);
            t.nextToken();
            Log.i("STLAsciiReader", "Token:" + t.sval);
            // vertex
            t.nextToken();
            if (!"outer".equalsIgnoreCase(t.sval))
                throw new IllegalStateException(t.sval);
            t.nextToken();
            Log.i("STLAsciiReader", "Token:" + t.sval);
            if (!"loop".equalsIgnoreCase(t.sval))
                throw new IllegalStateException(t.sval);
            float[] vertex = new float[3];
            Vec3D[] vector = new Vec3D[3];
            for (int j = 0; j < 3; j++) {
                t.nextToken();
                if (!"vertex".equalsIgnoreCase(t.sval))
                    throw new IllegalStateException(t.sval);
                t.nextToken();
                vertex[0] = Float.parseFloat(t.sval);
                t.nextToken();
                vertex[1] = Float.parseFloat(t.sval);
                t.nextToken();
                vertex[2] = Float.parseFloat(t.sval);
                vector[j] = new Vec3D(vertex);
            }
            tmpMesh.addFace(vector[0], vector[2], vector[1]);
            t.nextToken();
            if (!"endloop".equalsIgnoreCase(t.sval))
                throw new IllegalStateException(t.sval);
            t.nextToken();
            if (!"endfacet".equalsIgnoreCase(t.sval))
                throw new IllegalStateException(t.sval);

            // next state transition
            t.nextToken();
            if ("facet".equalsIgnoreCase(t.sval)) {
                t.pushBack();
                return new FacetState();
            }
            if ("endsolid".equalsIgnoreCase(t.sval)) {
                t.pushBack();
                return new FinalState();
            }
            throw new IllegalStateException();
        } catch (IOException e) {
            throw new IllegalStateException(e.getMessage());
        } catch (NumberFormatException e) {
            throw new IllegalStateException(e.getMessage());
        }
    }
}

class FinalState implements ParserState {

    @Override
    public ParserState next() throws IllegalStateException {
        return null;
    }

}

}

I have not adapted it very much to the toxiclibs coding convention, but feel free to integrate it, if you need it. It currently ignores reading normal vectors, but that can be added easily.

Regards Markus Klink (justjoheinz@gmail.com)

Comments (3)

  1. Karsten Schmidt repo owner

    Thank you very much for this! Haven't decided yet if to integrate this or not but might as well since I already got the binary reader. Out of interest, why are you using ASCII if binary is much more compact (especially important on Android)...?

  2. Alexey Stysin

    I have been testing the above code for a while and found a small bug. In the HeadState there is the following code

    override public ParserState next() throws IllegalStateException { 
         try { 
    
    
             solid t.nextToken(); 
             if (!"solid".equalsIgnoreCase(t.sval)) throw new IllegalStateException(t.sval);        
    
    
             //name 
            t.nextToken(); 
    
            return new FacetState(); 
    
           } 
    
           catch (IOException e) 
    
          { 
    
             throw new IllegalStateException(e.getMessage()); 
    
    } } }
    

    Error occurs when the optional name of model contains more than one token. In this case we will get to FacetState earlier then needed.

    I suggest to use something like this to read all tokens in name

    // name (may contain spaces)
                    while(!"facet".equalsIgnoreCase(t.sval))
                    {
                        t.nextToken();
                    }
                    t.pushBack();
    
  3. Log in to comment