Demonstrated in the Cover Art of the upcoming Issue of International PHP Magazine

Project K++ is an exploration of computer-generated abstract art. Eventually, the project aims to provide a framework for gaining a deeper understanding of what people find aesthetically pleasing in a quantified way. For now, it is an interesting example of using PHP and Ming to dynamically generate SWF images.
In June 2004, the first proof-of-concept release of project K++ was posted as an entry to the DotGeek Summer Contest (1). Within hours, it was slashdotted (2). 12,000 hits later that day, the topic had generated a lot of controversy: "Is this art?", "Is it useful?", "Why do this in PHP?".
The project was carried out in PHP because eventually being able to generate images and display them on the Web will help us to collect data about what people consider to be art. This data can help train the software to generate images that people are more likely to find interesting.
Is it art? Is it science? If nothing else, it's an interesting idea.
It is also a very useful way to learn about the Ming library's PHP API. Because it is class based and has been written in both PHP 4 and PHP 5, K++ can also be useful for learning about the differences between PHP 4 and PHP 5.
In
Issue 05.04 of International PHP Magazine, we will look at this project in detail. For now, let's peek under the hood of K++ to see how easy it is to create SWF images using PHP.
Listing 1: the RandomShape Class <?PHP
/* note: this function is just an easy way to be able to call rand with *
* two numbers and not have to worry about which is bigger. As a result, *
* I use it everywhere - even when it's not absolutely necessary. */
function safe_rand($i,$j) {
if($i<$j) return rand($i,$j);
else if($i>$j) return rand($j,$i);
else if($i==$j) return $i;
else return FALSE;
}
class randomShape extends SWFShape {
var $sides;
var $color;
var $color2;
function randomShape
($color=array(0x00,0x00,0x00,0xff),
$color2=array(0xff,0xff,0xff,0x00),
$CURVES=1,$GRADIENT=0) {
$this->SWFShape();
$this->color = $color;
$this->color2 = $color2;
$this->sides = safe_rand(3,9);
if($GRADIENT) {
$g = new SWFGradient();
$g->addEntry($color[0],$color[1],$color[2],$color[3]);
$g->addEntry($color2[0],$color2[1],$color2[2],$color[3]);
$f = $this->addFill($g, SWFFILL_LINEAR_GRADIENT);
$f->scaleTo(1/safe_rand(100,1000));
$f->moveTo(safe_rand(0,9),safe_rand(0,9));
} else {
$f = $this->addFill($color[0],$color[1],$color[2],$color[3]);
}
$this->setRightFill($f);
$this->movePenTo(0,0);
$initial_x = safe_rand(0,9);
$initial_y = safe_rand(0,9);
$initial_cx = safe_rand(0,9);
$initial_cy = safe_rand(0,9);
if(safe_rand(0,1)) {
$this->drawLine($initial_x,$initial_y);
} else if($CURVES) {
$this->drawCurve($initial_cx,$initial_cy,$initial_x,$initial_y);
}
$this->sides -= 1;
while($this->sides >= 1) {
$next_x = safe_rand(-$initial_x,$initial_x);
$next_y = safe_rand(0,-$initial_y);
$next_cx = safe_rand(-$initial_cx,$initial_cx);
$next_cy = safe_rand(0,-$initial_cy);
if(safe_rand(0,1)) {
$this->drawLine($next_x,$next_y);
} else if($CURVES) {
$this->drawCurve($next_cx,$next_cy,$next_x,$next_y);
}
$this->sides -= 1;
}
$this->drawLineTo(0,0);
}
}
?>
Unfortunately, the current versions of the Ming extensions for PHP do not seem to properly support extending their base classes (3). So, in order to extend a base class, all the "working parts" of the new class must exist within the constructor. In PHP 4, the constructor has the same name as the class itself. In PHP 5, the special method __construct() is used instead.
Listing 2: The differences in RandomShape in PHP 5 <?PHP
class randomShape extends SWFShape {
private $sides;
private $color;
private $color2;
public function __construct($color=array(0x00,0x00,0x00,0xff),
$color2=array(0xff,0xff,0xff,0x00),
$CURVES=1,$GRADIENT=0) {
parent::__construct();
//...
?>
Because this class only has one method, the differences between PHP versions 4 and 5 all happen at the beginning of the code. Here we are declaring the state variables in the class to be private. This means that you won't be able to call $randomShape->sides anymore. This can be useful for security purposes and to restrict access to the inner working parts of your classes when you hand them over to someone else to use. Here it is done as a teaching example.
Notice also that the special method __construct() is labeled public. This is redundant - it is publicly accessible by default (otherwise you couldn't instantiate it!) but it is sometimes considered good form to label it public to avoid confusion.
OK, enough on PHP 4 versus 5 - let's see what this class does. First, we construct the parent class using $this->SWFShape() or, more appropriately in PHP 5, parent::__construct(). Next, we set the state variables to the variables we have been passed using the special variable $this. By saying $this->color we refer to the $color variable declared at the top of our code. Likewise for $color2 and $sides, which is currently a random integer between 3 and 9.
Next, we check to see if we are using a normal or gradient fill on this shape. If it is a gradient, we instantiate a gradient object and assign it two colors using the addEntry method. Then we can add the fill to the object and get back an SWFFill object (variable $f). By moving and scaling the SWFFill object, we can change the positioning and length of the transition between the two colors.
Otherwise, we just add a solid fill using the four-color attributes (red, green, blue and alpha -all hex values between 0x00 and 0xff). Now we're ready to use the SWFShape->setRightFill() method to add the SWFFill to our shape. The difference between setRightFill and setLeftFill is the direction you intend to draw the shape. For clockwise shapes, use setRightFill - since you are drawing the shape around to the right the fill object needs to know to fill in the space inside the shape. Otherwise, you can get some pretty strange effects.
How do we actually draw one of these sides? First, we decide if we want a straight line or a curve. Straight lines can be drawn from the current pen point to a new anchor point using SWFShape->drawLine(x,y) where x and y are the control points.
Curves can be drawn using the drawCurve method with two additional x,y coordinates (four total). The two additional coordinates represent the anchor point. Using the method drawCurve with four points says, "head towards the control point, then smoothly turn to the anchor point." (4) So, modifying the anchor point and control point affects the shape of the curve.
The rest of the program loops through a randomly generated number of sides, drawing a new line or curve each time until there is only one side remaining. That final side is drawn back to the origin point. We now have a random shape with a random number and length of straight and/or curved sides.
How do we add it to the movie? Listing 3 shows the simplest way.
Listing 3: Adding a Random Shape To A Movie <?PHP
$color = array(rand(0x00,0xff),rand(0x00,0xff),rand(0x00,0xff),rand(0x00,0xff));
$color2 = array(rand(0x00,0xff),rand(0x00,0xff),rand(0x00,0xff),rand(0x00,0xff));
$curves = 1;
$gradients = rand(0,1);
$m = new SWFMovie();
$p = new randomShape($color,$color2,$curves,$gradients);
$i = $m->add($p);
/* ... here you could transform the SWFDisplayItem $i */
$i->moveTo(100,100);
$i->scale(10,10);
header('Content-type: application/x-shockwave-flash');
$m->output();
?>
Here we create an SWFMovie, instantiate a new randomShape with two colors, a Boolean value for allowing curves or not and another Boolean value to use a gradient fill or not. Then we simply add using the SWFMovie->add() method, which returns a very useful SWFDisplayItem. We use this object to scale it up a bit and move it closer to the center of the movie.
The SWFDisplayItem object can do a variety of things to the already added object, including scaling, skewing, moving, rotating, and even applying color transformations. All of this becomes extremely useful when you want to programmatically generate frame-by-frame animation using while loops. For now, we're keeping it simple and just adding the shape to our movie. Then, we override PHP's text/plain header with our own header, which that tells the browser that an SWF movie is on its way, and output that movie. For an example of the output, see:
http://www.robertpeake.com/CannedKandinsky/teaser.phpIt's beautiful! Well, not exactly. But the ease of using the Ming library certainly is beautiful. Check out the upcoming issue of International PHP Magazine for a more detailed account of the K++ package, including circles, "symphonic clusters" of shapes, and the colorTheme class that simulates a more humanly pleasing color palette. The article will also cover adding ActionScript behavior so you can drag the randomly generated shapes around to make your own art.
About the AuthorRobert Peake is a regular contributor to PHP Magazine (International Edition) and the creator of K++. He is also the owner and lead consultant for Peake Professional Consulting in Los Angeles. Send comments to:
robert@peakepro.com.
References: