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

Wednesday, 20 January 2010

Exploring using enum for LSystem grammar with processing

Having just experimented with using symbols in ruby processing, it was a natural (well I thought so anyway) to explore enum. Now you can't do this in the ide as yet but here is where I started in NetBeans:-
   1 package lsystem;
   2 
   3 /**
   4  * Grammar.java
   5  * @author Martin Prout
   6  */
   7 public enum Grammar {
   8     FORWARD, PLUS, THREE, FOUR, FIVE, MINUS, SAVE, RESTORE;
   9 }

I had the view that I wasn't out to create a DSL so I would need to create an interface from regular String input to my enum grammar which yielded the following:-

   1 /*
   2  * CharacterInterface.java
   3  */
   4 package lsystem;
   5 
   6 import java.util.Map;
   7 import java.util.HashMap;
   8 import java.util.Collections;
   9 /**
  10  *
  11  * @author Martin Prout
  12  */
  13 public interface CharacterInterface  {
  14     Grammar[] tokens = Grammar.values();
  15     Character[] initial = {'F', '+', '3', '4', '5', '-', '[', ']'};
  16     Map<Character, Grammar> map = X.newMap();
  17 
  18     static class X {
  19 
  20         private X() {
  21         }
  22 
  23         private static Map<Character, Grammar> newMap() {
  24             Map<Character, Grammar> map = new HashMap<Character, Grammar>();
  25             for (int i = 0; i< tokens.length; i++)
  26             map.put(initial[i], tokens[i]);
  27             return Collections.unmodifiableMap(map);
  28         }
  29     }
  30 }

Now I've broken out of the processing ide I'm making full use of the java collections library (no support for generics using the ide, though interestingly it doesn't complain about generics if you stuff them in a library as per my lsystem library, the problem is with antlr).
Here is the implementing class that produces the Grammar from the input axiom and rules:-

   1 /*
   2  * SimpleGrammar.java
   3  */
   4 package lsystem;
   5 
   6 import java.util.*;
   7 import java.text.CharacterIterator;
   8 import java.text.StringCharacterIterator;
   9 
  10 /**
  11  * Implements Grammar interface
  12  * SimpleGrammar class that provides convenience method for working with l-systems
  13  * NB 'map' comes from Character Interface
  14  * @author Martin Prout
  15  */
  16 public class SimpleGrammar implements CharacterInterface {
  17 
  18     private Grammar[] axiom;
  19     private HashMap<Grammar, Grammar[]> rules;
  20 
  21     /**
  22      * @param axiom String
  23      */
  24     public SimpleGrammar(String axiom) {
  25         this.axiom = stringToArray(axiom);
  26         rules = new HashMap<Grammar, Grammar[]>();
  27     }
  28 
  29     private Grammar[] stringToArray(String aString) {
  30         List<Grammar> ruleArray = new ArrayList<Grammar>();
  31 
  32         CharacterIterator it = new StringCharacterIterator(aString);
  33         for (char ch = it.first(); ch != CharacterIterator.DONE; ch = it.next()) {
  34             ruleArray.add(map.get(ch));
  35         }
  36         return ruleArray.toArray(new Grammar[ruleArray.size()]);
  37     }
  38 
  39     public void addRule(char premise, String rule) {
  40         Grammar[] ruleArray = stringToArray(rule);
  41         rules.put(map.get(premise), ruleArray);
  42     }
  43 
  44     public Grammar[] getRule(Grammar premise) {
  45         return rules.get(premise);
  46     }
  47 
  48     public boolean hasKey(Grammar premise) {
  49         return rules.containsKey(premise);
  50     }
  51 
  52     /**
  53      * Private parseRules helper function
  54      * @param prod Grammar[]
  55      * @return production Grammar[]
  56      */
  57     private Grammar[] parseRules(Grammar[] production) {
  58         List<Grammar> prod = new ArrayList<Grammar>();
  59         for (int i = 0; i < production.length; i++) {
  60             if (hasKey(production[i])) {
  61                 prod.addAll(Arrays.asList(getRule(production[i])));
  62             } else {
  63                 prod.add(production[i]);
  64             }
  65         }
  66         return prod.toArray(new Grammar[prod.size()]);
  67     }
  68 
  69     public Grammar[] createGrammar(int repeats) {
  70         Grammar[] production = axiom;
  71         for (int i = 0; i < repeats; i++) {
  72             production = parseRules(production);
  73         }
  74         return production;
  75     }
  76 
  77     public Grammar[] createGrammar() {
  78         return createGrammar(0);
  79     }
  80 
  81     public static <Grammar> Grammar[] concat(Grammar[] first, Grammar[] second) {
  82         Grammar[] result = Arrays.copyOf(first, first.length + second.length);
  83         System.arraycopy(second, 0, result, first.length, second.length);
  84         return result;
  85     }
  86 }

Here is my penrose snowflake:-

   1 // PenroseSnowflake.java
   2 package lsystem;
   3 
   4 import java.util.*;
   5 import processing.core.*;
   6 
   7 public class PenroseSnowflake extends PApplet {
   8 
   9     float drawLength;
  10     float xpos;
  11     float ypos;
  12     float DELTA = PI / 10; // 18 degrees
  13     float theta = 0;
  14     SimpleGrammar grammar;
  15     Grammar[] production;
  16 
  17     @Override
  18     public void setup() {
  19         size(1000, 1000);
  20         createLSystem();
  21         background(0);
  22         stroke(255, 0, 0);
  23         noFill();
  24         smooth();
  25         translateRules();
  26         noLoop();
  27     }
  28 
  29     public void createLSystem() {
  30         int generations = 3;                 // set no of recursions
  31         String axiom = "F3-F3-F3-F3-F3-";  // NB  '-' without prefix repeat = 1 (so 3 means 4 repeats or 72 degrees)
  32         grammar = new SimpleGrammar(axiom);  // initialize custom library
  33         grammar.addRule('F', "F3-F3-F45-F++F3-F"); // add rule
  34         float startLength = 480;
  35         production = grammar.createGrammar(generations);
  36         drawLength = startLength * pow(0.4f, generations);
  37     }
  38 
  39     public void translateRules() {
  40         xpos = width / 4;
  41         ypos = height * 0.9f;
  42         int repeats = 1;
  43         for (int i = 0; i < production.length; i++) {
  44             switch (production[i]) {
  45                 case FORWARD:
  46                     line(xpos, ypos, xpos = getNextX(xpos, drawLength, theta, repeats), 
  47                       ypos = getNextY(ypos, drawLength, theta, repeats));
  48                     repeats = 1;
  49                     break;
  50                 case PLUS:
  51                     theta += (DELTA * repeats);
  52                     repeats = 1;
  53                     break;
  54                 case MINUS:
  55                     theta -= (DELTA * repeats);
  56                     repeats = 1;
  57                     break;
  58 
  59                 case THREE:
  60                     repeats += 3;
  61                     break;
  62                 case FOUR:
  63                     repeats += 4;
  64                     break;
  65                 case FIVE:
  66                     repeats += 5;
  67                     break;
  68                 default:
  69                     System.err.println("character " + production[i] + " not in grammar");
  70             }
  71         }
  72     }
  73 
  74     private float getNextX(float xpos, float drawLength, float angle, float repeats){
  75         xpos += drawLength * repeats * cos(angle);
  76         return xpos;
  77     }
  78 
  79     private float getNextY(float ypos, float drawLength, float angle, float repeats){
  80         ypos += drawLength * repeats * sin(angle);
  81         return ypos;
  82     }
  83 
  84     static public void main(String args[]) {
  85         PApplet.main(new String[]{"--present", "--bgcolor=#666666", "--stop-color=#cccccc", "lsystem.PenroseSnowflake"});
  86     }
  87 }

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