Friday, August 30, 2013

make war, not love (for static content)

Updates have been sparse this summer! I guess some of that reflects the work I've been doing, a little less glamorously front-endy and sometimes more proprietary.

One project is using JBoss, which by nature is a bit less free-and-easy than the "here's the exploded war, just go edit the content!" defaults of Tomcat/Apache.  I had a front end to deploy on it that was strictly static content.

The easiest way to make a .war for static content seems to be to have your static html etc in a directory, include an empty "WEB-INF" directory in that,  and then (assuming "webclient" is both the name for the output file .war and the folder itself) from the directory above do:

jar cvf webclient.war -C webclient/ .

Simple enough.

I guess there may well be ways of getting JBoss to let you edit static content in place, but I didn't research them yet.

Sunday, August 4, 2013

PoMan3D: a simple 3d line engine for processing.js

Processingjs is amazing, the voodoo it pulls to get Java code running as Javascript is a wonder. The support for Processing's (already limited) 3D is wonky across browsers, so just for kicks I decided to go to the old fashioned ways of making lines in a kind of fakey-3D space.

Here is the result:

(Here is the source code)

All my engine can do is make lines in 3D space, centered at around an origin at 0,0,0, and rotateable at that origin on the X and Y axis (unlike some folks who do this stuff more seriously, I keep thinking of X and Y as being on the screen, and Z into the screen). It is optimized for working in a cube from -100,-100,-100 to 100,100,100

In setup() the code is:
PoMan3D pm3d;
pm3d = new PoMan3D(500, 500); //constructor given the screen size

Then, for every call of draw() :
  pm3d.setScale(1.0); //optional call, 1 is default
  pm3d.startDraw();
  pm3d.rotateToMouse(); //convenience function, or call setRotateX()/setRotateY()
//add lines here
  pm3d.endDraw();

There are two ways of adding lines: you can start a single endpoint (with x,y,z and color), and then add in a series of (x,y,z) points to make line segments. You can also just add a line with a color and two x,y,z endpoints:

  pm3d.startLinePath(-100,-100,-100,BLACK);
  pm3d.addToLinePath(100,-100,-100);
  pm3d.addToLinePath(100,-100,100);

or
  pm3d.add3Dline(-100, 100, -100, -100, -100, -100, BLACK);

So that's it... pretty easy. Besides rotating and scaling, its painting the line in "painter's order", i.e. back to front. It's not perfect, because it's taking the depth of an average of the endpoint, but in practice it's not terrible. It shouldn't be hard to put in polygons, or make stroke weight a bit smarter...
Anyway, here's the class:

class PoMan3D {
  private float screenCenterX, screenCenterY;
  private float scaler = 1.0;
  private float rotateX,rotateY;
  private ArrayList<scaledLine> linesToDraw;// = new ArrayList<scaledLine>();
  PoMan3D(int w, int h) {
    screenCenterX = w / 2;
    screenCenterY = h / 2;
  }

  void rotateToMouse(){
    setRotateX(map(mouseY,screenCenterX,height,0,-2*PI));
    setRotateX(map(mouseX,screenCenterY,width,0,-2*PI));
    
  }

  void setRotateX(float a){
    rotateX = a;
  }
  void setRotateY(float a){
    rotateY = a;
  }

  void setScale(float s){
     scaler = s; 
  }

  void startDraw() {
    linesToDraw = new ArrayList<scaledLine>();
  } 

  void addScaledLine(scaledLine s) {
    for (int i = 0; i < linesToDraw.size(); i++) {
      scaledLine c = linesToDraw.get(i);
      if (s.scaleval < c.scaleval) {
        linesToDraw.add(i, s);
        return;
      }
    }
    linesToDraw.add(s);
  }
  void endDraw() {
    pushMatrix();
    translate(screenCenterX, screenCenterY);

    for (scaledLine s : linesToDraw) {
      stroke(s.c);
      line(s.s1x, s.s1y, s.s2x, s.s2y);
    }
    popMatrix();
  }

  private float lastX, lastY, lastZ;
  private color lastC; 
  void startLinePath(float x, float y, float z, color c) {
    lastX = x; 
    lastY = y; 
    lastZ = z;
    lastC = c;
  } 
  void addToLinePath(float x, float y, float z) {
    pm3d.add3Dline(lastX, lastY, lastZ, x, y, z, lastC);
    lastX = x; 
    lastY = y; 
    lastZ = z;
  }
  void add3Dline(float x1, float y1, float z1, float x2, float y2, float z2, color c) {
    float f1x = x1, f2x = x2, f1y = y1, f2y = y2, f1z = z1, f2z = z2;

    f1x = rotation1(x1, z1, rotateY);
    f2x = rotation1(x2, z2, rotateY);
    f1z = rotation2(z1, x1, rotateY); 
    f2z = rotation2(z2, x2, rotateY);

    f1y = rotation1(y1, f1z, rotateX);
    f2y = rotation1(y2, f2z, rotateX);
    f1z = rotation2(f1z, y1, rotateX); 
    f2z = rotation2(f2z, y2, rotateX);

    float scale1 = map(f1z, -100, 100, .8 * scaler, 1.2  * scaler);
    float scale2 = map(f2z, -100, 100, .8 * scaler, 1.2 * scaler);
    f1x *= scale1;
    f1y *= scale1;
    f2x *= scale2;
    f2y *= scale2;

    addScaledLine(new scaledLine(f1x, f1y, f2x, f2y, scale1+scale2, c));
  } 
  
  private float rotation1(float a, float b, float ang) {
    return a*cos(ang) - b * sin(ang);
  } 
  private float rotation2(float a, float b, float ang) {
    return a*cos(ang) + b * sin(ang);
  }
  
  
private class scaledLine {
  float s1x, s2x, s1y, s2y, scaleval;
  color c;
  
  scaledLine(float ps1x, float ps1y, float ps2x, float ps2y, float pscale, color pc) {
    c = pc;
    s1x = ps1x; 
    s1y = ps1y;
    s2x = ps2x;
    s2y = ps2y;
    scaleval = pscale;
  }

  void draw() {
    color(c);
    line(s1x, s1y, s2x, s2y);
  }
}

  
  
}