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, 19 August 2009

Domain Specific Language for cfdg

On my other live blog at http://learning-ruby-processing.blogspot.com/ I've just been exploring the cfdg DSL created by Jeremy Ashkenas using ruby processing (an implementation of processing using jruby also created by Jeremy Ashkenas). I'm not exactly sure that it fully adheres to the context free principles but it does allows for a bit more programmatic control over your variables. Furthermore looping can be achieved using the lovely ruby syntax (it reads like just like English). Somewhat surprisingly I have been able make a contribution to the DSL, I have implemented the TRIANGLE primitive, took a couple of attempts but it is working now (as of 25 Aug 2009).

PS I just watched a video talk(from the same ArtAndCode series as Fry and Reas) given by _why (aka 'the lucky stiff') what a legend, and as you might expect he did wear shades.....
Actually given his apparent mental anguish when he wrote his infamous ruby guide he seems a fairly regular guy...

Update as of 2 Sept 2009 _why's gone missing, prescient or what when I made my blog entry _why went missing on 19th of August, all his on line presence has gone missing except as far as possible it has been brought together at http://whymirror.github.com/ I hope he's OK....

Monday, 17 August 2009

Ben Fry and Casey Reas Joint Talk

Here is a link a to talk you will find interesting if you want a bit of background to how processing evolved, and where it might be going from the founders:-

ArtAndCode joint presentation Casey Reas and Ben Fry on Vimeo

http://vimeo.com/5481063

Saturday, 15 August 2009

An alternative way of limiting recursion

Setting a smallest size is not the only way to limit recursion in context free, the second way is to have an empty rule (ie no shape). I wanted to explore an equivalent situation using processing. Here I have re-written my 'processing spiral' to have a probalistically determined end point (note for that reason you will sometimes not get what you are looking for. Also there is finite chance of getting nothing at all or otherwise hit the recursion limit.

   1 float REDUCE = 0.999;
   2 
   3 void setup(){
   4   size(400, 400);
   5   translate(100, 330); 
   6   rotate(0.3);
   7   fill(255, 0, 0, 0);
   8   background(0);
   9   noStroke();   
  10   smooth();
  11   fill(255, 0, 0, 20); // transparency makes for almost '3d' look
  12   int srand = int(random(0, 999));  
  13   shell(srand, -0.008, 1.5, 25);
  14 }
  15 
  16 void shell(int first, float rot, float disp, float sz){
  17   int sec = int(random(0, 999));
  18   if (sec == first){
  19     saveFrame("probability.png");
  20     //exit();
  21   }
  22   else{   
  23     sz *= REDUCE; 
  24     disp *= REDUCE;
  25     translate(disp, 0);
  26     rotate(rot);
  27     ellipse(disp, 0, sz, sz);
  28     shell(sec, rot, disp, sz); // recursive call with updated random
  29   }
  30 }
  31 


Here is one of the "better" results of running this applet (PS I increased the maximum available memory to 2500 Mb in the processing ide for this run).

Thursday, 13 August 2009

There are many ways to crack an egg

All this business with recursion vs equation got me thinking a bit, and I thought I would have a go at an egg using processing. Now for those of you happy with Bezier curves that is one way to go, but I favour the equation route in this instance, as it is relatively simple, and the placement of yolk does not require experimentation what do you think (by the way kiplings got some fantastic egg shapes using context free, follow the link and scroll down a bit for his chicken and eggs, far too advanced for me).

   1 void setup(){
2 size(300, 400);
3 background(0, 0, 255);
4 smooth();
5 translate(width/2, height/3);
6 noStroke();
7 beginShape();
8 for (int angle = 0; angle < 360; angle++){
9 float x = getX(angle * PI/180, 100);
10 float y;
11 if ((angle < 90)||(angle > 270)){
12 y = getY(angle * PI/180, 100, 60);
13 }
14 else{
15 y = getY(angle * PI/180, 100, 10);
16 }
17 vertex(x, y);
18 }
19 endShape();
20 fill(224, 224, 34);
21 ellipse(0.0, 0.0, 150.0, 152.0);
22 }
23
24 float getY(float theta, float r, float k){
25 return (k + r)*cos(theta);
26 }
27
28 float getX(float theta, float r){
29 return r*sin(theta);
30 }




Deconstructing kiplings chicken and egg, I came up with the following (NB: some of the rules may be spurious/ignored it is after all a hack). The result I thought was rather impressive.

   1 startshape shape
2
3 background{ b -1 }
4
5 rule shape{ egg{ } }
6
7 rule mound{
8 CIRCLE { s 2 1.5 y -1 z -1}
9 mound { s .999 .998 .999 b 0.5 h 60|h 0.03 sat 0.5}
10 }
11
12 rule egg{mound{ |b -.2 b .2 y 1 z 1 s .7 1}
13 }


Variation NVSTOK...



I don't know about you, but I got to see those funky moiré fringes again (I hate to think what it looks like on a CRT monitor), does make my processing effort look rather tame.

And for a less psychedelic version, where the hue orange/yellow does more or less what I expected there is this (sports my completely revised syntax highlighting for jedit, rule/path/shape names are now all highlighted as if they were functions):-

   1 startshape shape 
2
3 background{ b -1 }
4
5 rule shape{ egg { hue 40.4 } }
6
7 rule mound{
8 CIRCLE { s 2 1.5 y -1 z -1 }
9 mound { s .999 .998 .999 b 0.01 sat 0.01 }
10 }
11
12 rule egg{ mound{ |b -.2 b .2 y 1 z 1 s .7 1 }
13 }



Variation SBCPOV



Now that I've hacked kiplings code, I would like to understand it, but the documentation is terse to say the least:-
The key bits are:-
The geometry of shapes is defined by a two dimensional affine transform which is represented internally as a matrix.
No 3D then but we do have a 'z' translation.
The z-axis position determines which shape is on top when they overlap.
The z-axis position is not stored in the affine transform matrix and does not affect the shape of objects.


And in the comment explaining the 3 argument size adjustment eg {s 2 4 1} there is the following:-

The z-axis scale is not stored in the affine transform matrix and does not affect the shape of objects.
Line 8 above has a 'z' translation
Line 9 above has a 'size' adjustment with 3 arguments
Line 12 has a 'z' translation

Tuesday, 11 August 2009

A processing spiral using recursion

Not as smooth as the context free version, but here is my nautilus like shell curve, using recursion in a processing applet.
   1:float REDUCE = 0.999;  
2:float MIN_SIZE = 0.8; // about right for processing
3:
4:void setup(){
5: size(400, 400);
6: translate(100, 330);
7: rotate(0.3);
8: fill(255, 0, 0, 0);
9: background(0);
10: noStroke();
11: smooth();
12: fill(255, 0, 0, 20); // transparency makes for almost '3d' look
13: shell(-0.008, 1.5, 25);
14:}
15:
16:void shell(float rot, float disp, float sz){
17: if (sz < MIN_SIZE){
18: print("done");
19: }
20: else{
21: sz *= REDUCE;
22: disp *= REDUCE;
23: translate(disp, 0);
24: rotate(rot);
25: ellipse(disp, 0, sz, sz);
26: shell(rot, disp, sz); // recursive call
27: }
28:}


Recursion is key to Context Free

I wasn't super happy with resorting to PATH to create my nautilus object (previous post) but I was having difficulty getting to grips with the cfdg grammar. Mountain View Mark was only to pleased to put me right, but his original rules confused the issue a bit (I thought there was a spurious z number, turns out that it can be used to force one layer behind another, quite deep in the documentation kipling put me right on that, NB you should check out some of kiplings creations) I have re-written Marks rules to keep my original primary colors/brightness/saturation, and with my preferred shape names. But the most significant thing that hits you is the recursion, he came up with the size and displacement numbers empirically. There is some underlying math as a result of the recursion, I think it might be nice to derive those numbers mathematically. Here is the modified code:-
   1 startshape nautilus
2 background {b -1}
3 rule nautilus{
4 shell { r -20 hue 360 sat 1 b 1 }
5 septa { r -20 hue 220 sat 1 b 1 }
6 }
7
8 rule shell {
9 CIRCLE { }
10 shell { r 0.50 x 0.25 size 0.9985 }
11 }
12
13 rule septa {
14 divider { r 30 }
15 septa { r 50.5 size 0.859 x 20.7 y 9.3 }
16 }
17
18 rule divider {
19 485 * { r 0.15 x 0.05 size 0.9999 }
20 CIRCLE { size .2 }
21 }

And here is the result which is quite satisfying, with the width of the shell increasing in a natural way (except the way cfdg works it is decreasing in a natural way, infinite recursion is terminated when the size of an element (circle here) falls below a certain value), something that my 'processing' version did not do, I might take a look at that code to and see what I can do there. Also for once there is a bonus in my jedit highlighting mode being unable to differentiate between a size directive and a size as in scale, the blue highlighting makes it really stand out!!!


I experimented with these rules by first removing the rotation from the shell rule, what this does is to essentially unwind the shell spiral. So the size adjustment doesn't seem so be so important (other than to kill the infinite recursion), however on further experimentation using a constant size CIRCLE you only get a circle not the spiral, so you do need all three adjustments:-

   1 startshape nautilus
2 background {b -1}
3 rule nautilus{
4 shell { r -20 hue 360 sat 1 b 1 }
5 septa { r -20 hue 220 sat 1 b 1 }
6 }
7
8 rule shell {
9 CIRCLE { }
10 shell { x 0.25 size 0.9985 }
11 }
12
13 rule septa {
14 divider { r 30 }
15 septa { r 50.5 size 0.859 x 20.7 y 9.3 }
16 }
17
18 rule divider {
19 485 * { r 0.15 x 0.05 size 0.9999 }
20 CIRCLE { size .2 }
21 }



Rule modified to give constant shell thickness (with low probability rule to prevent inifinite recursion

   1 startshape nautilus
2 background {b -1}
3 rule nautilus{
4 shell { r -20 hue 360 sat 1 b 1 }
5 septa { r -20 hue 220 sat 1 b 1 }
6 }
7
8 rule shell 0.001 {} // empty rule to prevent infinite recursion
9
10 rule shell {
11 CIRCLE { }
12 shell { r 0.5 x 0.25 }
13 }
14
15 rule septa {
16 divider { r 30 }
17 septa { r 50.5 size 0.859 x 20.7 y 9.3 }
18 }
19
20 rule divider {
21 485 * { r 0.15 x 0.05 size 0.9999 }
22 CIRCLE { size .2 }
23 }


Sunday, 9 August 2009

Ruby script for cfdg

Well the pre-processor tool is all very well, but you have to be careful writing your define macro, so I thought hmmmmmmmmmmmn! why not use a ruby script (I can think easier in ruby than I can in python, python may be very correct, but I think it is often counter-intuitive, obviously suits Guido/Alex Martelli, great libraries though). Another plus point with ruby vs 'C' pre-compiler approach is no silly radian to degree conversions required, the calculations are done in ruby (note: set precision to suit yourself).
   1 # cfdg_writer.rb
2
3 B = 1.4
4
5 def start(name = "shape")
6 "startshape %s" % name
7 end
8 def simple_rule(rule_name, path_name, parms = "")
9 "rule %s{\n %s{%s}\n}" % [rule_name, path_name, parms]
10 end
11 def two_path_rule(rule_name, path_name, path_two, parms = "")
12 "rule %s{\n %s{%s}\n %s{%s}\n}" % [rule_name, path_name, parms, path_two, parms]
13 end
14 def calculate_x(theta)
15 Math.cos(theta)*Math.exp(theta/Math.tan(B))
16 end
17 def calculate_y(theta)
18 Math.sin(theta)*Math.exp(theta/Math.tan(B))
19 end
20 def move_to(xp, yp)
21 " MOVETO{x %0.4f y %0.4f}" % [xp, yp]
22 end
23 def line_to(xp, yp)
24 " LINETO{x %0.4f y %0.4f}" % [xp, yp]
25 end
26
27 def stroke(param = "")
28 puts " STROKE{%s}" % param
29 end
30
31 def create_shell(path_name)
32 puts "path %s{\n" % path_name
33 puts move_to(0, 0)
34 for step in 0 ... 400
35 puts line_to(calculate_x(step/10.0), calculate_y(step/10.0))
36 end
37 stroke("b 1 hue 360 sat 1 width 7")
38 puts "}"
39 end
40
41 def create_segments(path_name)
42 puts "path %s{\n" % path_name
43
44 for step in 7 ... 41
45 puts move_to(calculate_x(step - 7), calculate_y(step - 7))
46 puts line_to(calculate_x(step), calculate_y(step))
47 end
48 stroke("b 1 hue 220 sat 1 width 5")
49 puts "}"
50 end
51
52 path_name = "spiral"
53 second_path_name = "segments"
54 shape_name = "nautilus"
55 puts start(shape_name)
56 puts "background{b -1}"
57 puts two_path_rule(shape_name, second_path_name, path_name, "r 185 flip 90")
58 create_shell(path_name) # internal puts
59 create_segments(second_path_name)
60


Very crude I know I just piped result to the file rather than console (advantage send result to console first to check output).

ruby cfdg_writer.rb > nautilus.cfdg

When you run the rule in the cfdg program this is what you get, a nice smooth curve (no CURVETO required), best viewed at full size 1000 x 1000. NB in line 32 I have divided by a float to force a float result from my division.



Now I have handed amend my cfdg file to make sure the segment and curve join only a little gap. Script saves masses of typing.... it would be nice to get CURVE to work on the segments.
However this is clearly not the way to draw a nautilus in context free, Mountain View Mark has put me right over at the Context Free Art Gallery, where I posted my nautilus under the author name 'monkstone'. However there is something to be said for the elegance of the single thickness line (you are forced toward a tapering line with the cfdg approach).

Saturday, 8 August 2009

Expressions and constants in context free

Now for something a bit more complicated, you can use the C pre-processor to introduce constants such as π, which you can the use in your defines, I think order might be important here.
Returning to one of my favorite shapes it is the nautilus, I've yet to get to grips with the CURVETO syntax, so here is the LINETO version. Pity the cfdg boys chose to work in degrees, nasty radian to degree conversions required.

   1:startshape nautilus
2:
3:#define PI 3.14159265
4:#define calcX(A) (0.8*cos(((A*0.8)/PI)*180)*exp((A*0.8)/tan((1.4/PI)*180)))
5:#define calcY(A) (0.8*sin(((A*0.8)/PI)*180)*exp((A*0.8)/tan((1.4/PI)*180)))
6:
7:background{b -1}
8:
9:rule nautilus{
10: spiral{r 20}
11:}
12:
13:path spiral{
14: MOVETO{x 0 y 0}
15: LINETO{x (calcX(0)) y (calcY(0))}
16: LINETO{x (calcX(1)) y (calcY(1))}
17: LINETO{x (calcX(2)) y (calcY(2))}
18: LINETO{x (calcX(3)) y (calcY(3))}
19: LINETO{x (calcX(4)) y (calcY(4))}
20: LINETO{x (calcX(5)) y (calcY(5))}
21: LINETO{x (calcX(6)) y (calcY(6))}
22: LINETO{x (calcX(7)) y (calcY(7))}
23: LINETO{x (calcX(8)) y (calcY(8))}
24: LINETO{x (calcX(9)) y (calcY(9))}
25: LINETO{x (calcX(10)) y (calcY(10))}
26: LINETO{x (calcX(11)) y (calcY(11))}
27: LINETO{x (calcX(12)) y (calcY(12))}
28: LINETO{x (calcX(13)) y (calcY(13))}
29: LINETO{x (calcX(14)) y (calcY(14))}
30: LINETO{x (calcX(15)) y (calcY(15))}
31: LINETO{x (calcX(16)) y (calcY(16))}
32: LINETO{x (calcX(17)) y (calcY(17))}
33: LINETO{x (calcX(18)) y (calcY(18))}
34: LINETO{x (calcX(19)) y (calcY(19))}
35: LINETO{x (calcX(20)) y (calcY(20))}
36: LINETO{x (calcX(21)) y (calcY(21))}
37: LINETO{x (calcX(22)) y (calcY(22))}
38: STROKE{b 1 hue 360 sat 1}
39:}




Taking the next step we could replace each instance of the LINETO with a concise pre-processor directive (makes for a long define though). I dare say you could write a macro say 0 to 22 increment x, as it is all static code.

   1:startshape nautilus
2:#define PI 3.14159265
3:#define lineto(A) LINETO{ x (0.8*cos(((A*0.8)/PI)*180)*exp((A*0.8)/tan((1.4/PI)*180))) y (0.8*sin(((A*0.8)/PI)*180)*exp((A*0.8)/tan((1.4/PI)*180)))}
4:background{b -1}
5:rule nautilus{
6:spiral{r 20}
7:}
8:
9:path spiral{
10: MOVETO{x 0 y 0}
11: lineto(0)
12: lineto(1)
13: lineto(2)
14: lineto(3)
15: lineto(4)
16: lineto(5)
17: lineto(6)
18: lineto(7)
19: lineto(8)
20: lineto(9)
21: lineto(10)
22: lineto(11)
23: lineto(12)
24: lineto(13)
25: lineto(14)
26: lineto(15)
27: lineto(16)
28: lineto(17)
29: lineto(18)
30: lineto(19)
31: lineto(20)
32: lineto(21)
33: lineto(22)
34: STROKE{b 1 hue 360 sat 1}
35:}

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