Recently I've been looking at Povray, pyprocessing, and cfdg (version 3.0) as tools for creating digital images. I have branched two separate blogs where I mainly explore jruby + processing and processing.py

Tuesday, 2 August 2011

3DTurtle interface for my LSystems library

I've created a wrapper for my LSystems library that supports a 3D turtle as a Turtle3D interface, with one implementation RodTurtle. So now instead of rotateX() we have yaw(), rotateY() we have pitch() and rotateZ() we have roll(). RodTurtle relies on processing to hold state, therefore you should use pushMatrix() and popMatrix() to preserve/restore state (as for example in bracketed LSystems). It does not make sense to create a TurtleStack, until you have an implementation that doesn't use processings built in affine transforms (cf the standard Turtle implementation).

import lsystem.util.*;
import lsystem.turtle.*;
import lsystem.collection.*;
import lsystem.*;

import processing.opengl.*;
Grammar grammar;
Turtle3D turtle;
float distance = 300;  
int depth = 2;

// adjust centre of hilbert
float[] adjust = {
  0.0, 0.5, 1.5, 3.5, 7.5
};

// adjust resolution with depth (ie decrease)
int[] detail = {
  36, 24, 18, 15, 12
};
int THETA = 90;   // int for degree float for radians
int PHI = 90;
String production = "";

void setup() { 
  size(800, 600, OPENGL);
  configureOpenGL();
  turtle = new RodTurtle(this);
  LUT.initialize(); 
  setupGrammar();
  float fov = PI/3.0; 
  float cameraZ = (height/2.0) / tan(fov/2.0); 
  perspective(fov, float(width)/float(height), cameraZ/2.0, cameraZ*2.0); 
  noStroke();
}

void configureOpenGL() {
  hint(ENABLE_OPENGL_4X_SMOOTH);
  hint(DISABLE_OPENGL_ERROR_REPORT);
}

/**
 * Encapulates the lsystem rules, and calls the grammar to create the production rules
 * depth is number of repeats, and distance is adjusted according to the number of repeats
 */

void setupGrammar() {
  grammar = new SimpleGrammar(this, "A");   // this only required to allow applet to call dispose()
  grammar.addRule('A', "B>F<CFC<F>D+F-D>F<1+CFC<F<B1^");
  grammar.addRule('B', "A+F-CFB-F-D1->F>D-1>F-B1>FC-F-A1^");
  grammar.addRule('C', "1>D-1>F-B>F<C-F-A1+FA+F-C<F<B-F-D1^");
  grammar.addRule('D', "1>CFB>F<B1>FA+F-A1+FB>F<B1>FC1^");
  production = grammar.createGrammar(depth);
  if (depth > 0) {
    distance *= 1/(pow(2, depth) - 1);
  }
}

void draw() {
  background(20, 20, 200);
  lights();
  pushMatrix();  
  translate(width/2, height/2, 0);
  rotateX(LUT.sin(frameCount));
  rotateY(LUT.cos(frameCount));
  pushMatrix();
  translate(-distance * adjust[depth], distance/2 * adjust[depth], -distance/2 * depth);   
  render();
  popMatrix();
  popMatrix();
}


/**
 * Render wraps the drawing logic; draws a sphere at origin,
 * followed by successive turtle.draw(distance) as a capped
 * cylinder to complete the hilbert according to lsystem rules 
 * (ie whenever there is an 'F').
 */

void render() {
  int repeats = 1;  
  fill(191, 191, 191);
  ambientLight(80, 80, 80);
  directionalLight(100, 100, 100, -1, -1, 1);
  ambient(122, 122, 122); 
  lightSpecular(30, 30, 30); 
  specular(122, 122, 122); 
  shininess(0.7); 
  CharacterIterator it = grammar.getIterator(production);
  for (char ch = it.first(); ch != CharacterIterator.DONE; ch = it.next()) {
    switch (ch) {
    case 'F': 
      turtle.draw(distance); 
      break;
    case '+':
      turtle.yaw(THETA * repeats);
      repeats = 1;
      break;
    case '-':
      turtle.yaw(-THETA * repeats);
      repeats = 1;
      break;  
    case '>':
      turtle.pitch(THETA * repeats);
      repeats = 1;
      break;
    case '<':
      turtle.pitch(-THETA * repeats);
      repeats = 1;
      break;
    case '^':
      turtle.roll(PHI * repeats);
      repeats = 1;
      break;
    case '1':
      repeats += 1;
      break;  
    case 'A':
    case 'B':
    case 'C':
    case 'D': 
      break;
    default:
      System.err.println("character " + ch + " not in grammar");
    }
  }
}


void keyReleased() {
  switch(key) {
  case '+':
    if (depth <= 3) { // guard against a depth we can't handle
      depth++;
      distance = 300;
      setupGrammar();
    }
    break;
  case '-':
    if (depth >= 2) {
      depth--;
      distance = 300;
      setupGrammar();
    }
    break;
  }
}

No comments:

Post a Comment

Followers

Blog Archive

About Me

My photo
Pembrokeshire, United Kingdom
I have developed JRubyArt and propane new versions of ruby-processing for JRuby-9.1.5.0 and processing-3.2.2