3

Stackoverflowers,

I am doing a simple project using Android smartphones to create 3D forms. I am using Android Processing to make a simple App.

My code makes a 3D shape and saves it as an .STL file. It works on my laptop and saves the .STL file, but in the App. version, I need it to save to the External storage/SD Card of my phone (HTC Sensation). It does not, because of the way the “save” function (writeSTL) in the Processing library I am using has been written.

I have posted for help here (my code more complete code is here too):

http://forum.processing.org/two/discussion/4809/exporting-geometry-stl-obj-dfx-modelbuilder-and-android

...and Marius Watz who wrote the library says that the writeSTL() code is pretty much standalone and the only thing missing is (or should be) replacing the code creating the output stream, which needs to be modified to work with Android. Basically, this line:

FileOutputStream out=(FileOutputStream)UIO.getOutputStream(p.sketchPath(filename));

I am not a great programmer in that I can usually get Processing to do what I need to do but no more; this problem has me beaten. I am looking for ideas for the correct code to replace the line:...

FileOutputStream out=(FileOutputStream)UIO.getOutputStream(p.sketchPath(filename));

...with something “Android-friendly”. Calling getExternalStorageDirectory() should work but I am at a loss to find the correct structure.

The code for the writeSTL function is below.

import java.io.*;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;

/**
 * Output binary STL file of mesh geometry.
 * @param p Reference to PApplet instance
 * @param filename Name of file to save to
 */

  public void customWriteSTL(UGeometry geo, PApplet p, String filename) {
  byte [] header;
  ByteBuffer buf;
  UFace f;

  try {
    if (!filename.toLowerCase().endsWith("stl")) filename+=".stl";
    FileOutputStream out=(FileOutputStream)UIO.getOutputStream(p.sketchPath(filename));

buf = ByteBuffer.allocate(200);
header=new byte[80];
buf.get(header, 0, 80);
out.write(header);
buf.rewind();

buf.order(ByteOrder.LITTLE_ENDIAN);
buf.putInt(geo.faceNum);
buf.rewind();
buf.get(header, 0, 4);
out.write(header, 0, 4);
buf.rewind();

UUtil.logDivider("Writing STL '"+filename+"' "+geo.faceNum);

buf.clear();
header=new byte[50];
if (geo.bb!=null) UUtil.log(geo.bb.toString());

for (int i=0; i<geo.faceNum; i++) {
  f=geo.face[i];
  if (f.n==null) f.calcNormal();

  buf.rewind();
  buf.putFloat(f.n.x);
  buf.putFloat(f.n.y);
  buf.putFloat(f.n.z);

  for (int j=0; j<3; j++) {
    buf.putFloat(f.v[j].x);
    buf.putFloat(f.v[j].y);
    buf.putFloat(f.v[j].z);
  }

  buf.rewind();
  buf.get(header);
  out.write(header);
}

out.flush();
out.close();
UUtil.log("Closing '"+filename+"'. "+geo.faceNum+" triangles written.\n");
  } 
  catch (Exception e) {
e.printStackTrace();
  }
}

Any suggestions are gratefully received.

Thank you in advance.

PDF
  • 35
  • 3
  • Think that you only have to adapt `p.sketchPath()` to use `getExternalStorageDirectory()` or better yet `getExternalFilesDir()`. Provided `.getOutputStream()` will take a full path. – greenapps Jun 29 '14 at 08:41
  • If not then use `FileOutputStream = new FileOutputStream(p.sketchPath(filename)):`. – greenapps Jun 29 '14 at 09:05
  • Indeed, that (your first answer) was the way I needed to go. Thanks for you input @greenapps – PDF Jul 07 '14 at 05:25

1 Answers1

1

There are a few ways of doing this - some that will just work and some that are proper ... as with all things Processing/Java. It's really not that different from regular Java though - the only quirk is the root SD path, and checking if it exists or not (note that some phones have "internal" rather than "external" storage (i.e. not removable/swappable), but Android should interpret these the same AFAIK.

In classic Java fashion, you should really be checking IF the SD Card is present beforehand... I use the following structure, taken from this answer by @kaolick

    String state = Environment.getExternalStorageState();
    if (state.equals(Environment.MEDIA_MOUNTED)) {
        // Storage is available and writeable - ALL GOOD  
    } else if (state.equals(Environment.MEDIA_MOUNTED_READ_ONLY)) {
        // Storage is only readable - RUH ROH
    } else {
        // Storage is neither readable nor writeable - ABORT    
    }

Note that he provides a full class for you to use, which is great, and has a few convenience functions.

The second thing you might want to look at is creating a custom directory on the SD Card of the device, probably in setup() - something like this:

  try{
    String dirName = "//sdcard//MyAppName";
    File newFile = new File(dirName);

    if(newFile.exists() && newFile.isDirectory()) {
      println("Directory Exists... All Good");
    } 
    else {
      println("Directory Doesn't Exist... We're Making It");
      newFile.mkdirs();
    }
  }
  catch(Exception e) {
    e.printStacktrace();
  }

Of course, instead of HardCoding the Path name, you should do something like

String dirName = Environment.getExternalStorageDirectory().getAbsolutePath() + "/MyAppName";

instead...

Also, note that the above try/catch should go INSIDE the case statement of "if (state.equals(Environment.MEDIA_MOUNTED))" ... or should be wrapped in a separate function anc called from there.

Then, finally, saving it. If you wanted to use a BufferedWriter, it would look like this:

  BufferedWriter writer = new BufferedWriter(new FileWriter(dirName, true));
  writer.write(STL_STUFF);
  writer.flush();
  writer.close();

I've only use a FileOutputStream within a BufferedOutput Stream, and it looked like this:

try {
    String fileName = "SOME_UNIQUE_NAME_PER_FILE";
    String localFile = dirName + "/" +filename;
    OutputStream output = new BufferedOutputStream(newFileOutputStream(localFile));
}
catch(Exception e) {
    e.printStackTrace();
}

Finally, give my regards to Marius if you talk to him! ;-)

Community
  • 1
  • 1
jesses.co.tt
  • 2,689
  • 1
  • 30
  • 49
  • Two upvotes on the Question but none on the Answer ?!? – jesses.co.tt Jul 03 '14 at 05:20
  • Many, many thanks for your answer @jesses.co.tt - you are a wonderful person. I have been out of country and not had a chance to read this. Your comprehensive answer pointed me in exactly the right direction. 100 upvotes are due - sadly I cannot actually upvote it myself :( – PDF Jul 07 '14 at 05:23
  • lol ok thanks... thanks for accepting the answer though. upvote when you get a chance, and tag me directly if you have any P5/Droid questions! – jesses.co.tt Jul 07 '14 at 20:38