NAME
Text::Flowchart - ASCII Flowchart maker
AUTHOR
Jim Thomason, jim3@psynet.net
SYNOPSIS
+-------+ +-------------+
| BEGIN >---+ | |
+-------+ +--> Do you need |
| to make a N------+
+--------Y flowchart? | |
| | | |
| +-------------+ |
| |
| +------------+ |
| | | |
+-----V-------+ | So use it. | |
| | | | |
| Then my | +--^---V-----+ |
| module may | | | |
| help. | | | |
| >----+ | |
+-------------+ | |
| +-----V-------+
| | |
| | Then go do |
+------> something |
| else. |
| |
+-------------+
DESCRIPTION
Text::Flowchart does what the synopsis implies, it makes ASCII flowcharts. It also (hopefully) makes it easy to make ASCII flowcharts.
REQUIRES
Carp
OBJECT METHODS
CREATION
New flowcharts are created with the new constructor.
$object = Text::Flowchart->new();
You can also initialize values at creation time, such as:
$object = Text::Flowchart->new(
"width" => 120,
"debug" => 1
);
ACCESSORS
There aren't any. Well, there aren't any that you really should use.
Why's that, you ask? Because that's how the module is written. Once an object is added into a flowchart, its appearance becomes set in stone. And I mean totally set in stone, you cannot remove a box from a flowchart, it sticks around until the flowchart is destroyed. Even if you poked around in the internals of the module and discovered that ->string is an accessor in the box class, for instance, it wouldn't do you any good since objects are immediately rendered upon their creation. Even if you re-render the object after fiddling with it, it's not going to work since the objects are inserted into the flowchart's internal grid once they're rendered.
So basically, declare everything you need to declare about an object when you create it, and make sure that that's the information you're going to use when you print it out. It's not that hard. :)
Methods
There are several methods you are allowed to invoke upon your flowchart object.
- new (?width? ?height? ?pad? ?debug?)
-
This is the object constructor that actually creates your flowchart object. All parameters are optional and are passed in a hash.
- width
-
Optional. Specifies the width of your flowchart object, or uses the module default (72).
The width of a flowchart must be specified, it cannot grow to accomodate objects placed within it. If you try to place an object beyond the edge of the flowchart, you'll die with an error.
- height
-
Optional. Specifies the height of your flowchart object, or uses the module default (0).
The default height of 0 means that your flowchart will grow in height as much as necessary to accomodate the height of whatever you insert into your flowchart. You can also specify a specific height so that your flowchart will be no taller than 17 rows, for example. If you try to place an object beyond the bottom of the flowchart, you'll die with an error.
- pad
-
Optional. pad allows you to specify a padding character. The padding character is what is used to fill in the space between boxes and lines. The default padding character is a space (\040), but you can set it to anything else you'd like. This is most useful when debugging, IMHO. Very useful in combination with debug (see below). Set to " " by default.
- debug
-
Optional. debug will print out the x & y coordinates of the flowchart. This should greatly help you place your boxes where you want them to be. Off by default, set to 1 to enable.
- directed
-
Optional. directed specifies that your flowchart is direction dependent. This will add on arrows to all of your lines. Flowcharts are non-directed by default. Set this to 1 to use a directed flowchart.
For example:
$flowchart = Text::Flowchart::new->(); #boring flowchart, no options $flowchart = Text::Flowchart::new->( #flowchart specifying new width, a % as the padding character, and using the debugging option "width" => 100, "pad" => "%", "debug" => 1 ); $flowchart = Text::Flowchart::new( #using periods as the padding character "pad" => "." );
- box (string x_coord y_coord ?x_pad? ?y_pad? ?width? ?height?)
-
You use the box method to add a new box into your flowchart object. All parameters are passed in a hash.
The text within a box is wrapped at word boundaries if possible, or at the appropriate width if not possible so that all of your text fits inside your box.
- string
-
Required. string is the message that you'll see within the box. Want your box to say "Hi there!"? "string" => "Hi there!"; Easy as pie.
- x_coord
-
Required. The x coordinate of the upper left corner of the box. Use the debug option to help you find your coordinates.
- y_coord
-
Required. The y coordinate of the upper left corner of the box. Use the debug option to help you find your coordinates.
- x_pad
-
Optional. x_pad allows you to specify the amount of horizontal white space between the edges of the box and the text within. If this option is not specified, it'll use the default of a single space.
- y_pad
-
Optional. y_pad allows you to specify the amount of vertical white space between the edges of the box and the text within. If this option is not specified, it'll use the default of a single line of white space.
- width
-
Optional. width allows you to specify the width of the box. The narrowest possible box has a width of 3. You must take into account your x_pad value, as well as the edges of the box when you specify a width. If no width is specified, it will use the default of 15.
If you specify a width of 0 (zero), then your box will grow horizontally to accomodate all of your text, or until it reaches the width of the flowchart, whichever is lesser. Be careful! This will put all of your box message onto the same line. You almost never want your width to be 0.
- height
-
Optional. height allows you to specify the height of the box. The shortest possible box has a height of 3. You must take into account your y_pad value, as well as the edges of the box when you specify a height. If no height is specified, it will use the default of 0 (zero).
If you specify a height of 0 (zero), then your box will grow vertically to accomodate all of your text, or until it reaches the height of the flowchart, whichever is lesser. You most likely want a box width of some fixed value, and a box height of zero so it will grow to accomodate your message.
If you specify a non-zero box height, then your string will be truncated once the box reaches the height that you specified. It will still be created, you just won't have all of your text in it.
For example:
$example_box = $flowchart->box( #creates a box at (15,0) "string" => "Do you need to make a flowchart?", "x_coord" => 15, "y_coord" => 0 ); Output: +-------------+ | | | Do you need | | to make a | | flowchart? | | | +-------------+ $example_box2 = $flowchart->box( #creates a box at (0,0), with new x_pad and y_pad values "string" => "Do you need to make a flowchart?", "x_coord" => 0, "y_coord" => 0, "x_pad" => 0, "y_pad" => 3 ); Output: +-------------+ | | | | | | |Do you need | |to make a | |flowchart? | | | | | | | +-------------+ $example_box3 = $flowchart->box( #creates a box at (0,0), with new x_pad and y_pad values "string" => "Do you need to make a flowchart?", "x_coord" => 0, "y_coord" => 0, "x_pad" => 2, "y_pad" => 0 ); Output: +-------------+ | Do you | | need to | | make a | | flowchart | | ? | +-------------+
- relate ([from, from side, ?on side?], [to, to side, ?on side?], ?reason?)
-
the relate method connects boxes to each other. Once two boxes are related, a line is automatically drawn between then, listing a reason if given, and arrows if it's a directed flowchart. The relate method spares you the trouble of having to re-position your lines if you move your boxes around. You may still have to do some fiddling (such as changing the sides that are connected), but it's much less trouble than re-arranging the boxes and the lines.
- (from or to)
-
Required. This is the box that you're relating from or to, it's the first item in an anonymous array.
- (from or to) side
-
Required. This is the side of the box that you're relating from or to. This must be either "top", "bottom", "right", or "left". It's the second item in an anonymous array.
- ?on side?
-
Optional. This optional item specifies where on the box side you'd like to draw the line from. If zero, it will take the first available slot starting from the left (if drawing from the top or bottom) or from the top (it drawing from the right or left). If -1, it will take the first available slot starting from the other side (right for top and bottom or bottom for right or left). If any other number, it will draw the line at that position, along the side, again counting from the left or the top.
- reason
-
Optional. reason is a name and value pair that specifies why you would take that route. The value must be one character long, you'll die with an error if it isn't. reasons only make sense in a directed flowchart, they don't appear in non-directed ones. Typical values are "Y" for yes and "N" for no. If no reason is specified, the default direction character will be used.
For example:
$flowchart = Text::Flowchart->new( "width" => 50, "directed" => 1);
$example_box = $flowchart->box( #creates a box at (0,0) "string" => "Do you need to make a flowchart?", "x_coord" => 0, "y_coord" => 2, ); $example_box2 = $flowchart->box( #creates a box at (15,0) "string" => "Yes I do.", "x_coord" => 19, "y_coord" => 0, "width" => 13 ); $example_box3 = $flowchart->box( #creates a box at (15,0) "string" => "No I don't.", "x_coord" => 19, "y_coord" => 7 ); $flowchart->relate( [$example_box, "right"] => [$example_box2, "left"] ); $flowchart->relate( [$example_box, "right", -1] => [$example_box3, "left"] ); $flowchart->draw(); Output: +-----------+ +--> | +-------------+ | | Yes I do. | | >-+ | | | Do you need | +-----------+ | to make a | | flowchart? | | >--+ +-------------+ +-------------+ +-> | | No I don't. | | | +-------------+
$flowchart = Text::Flowchart->new( "width" => 50); #A non-directed chart
$example_box = $flowchart->box( #creates a box at (0,0) "string" => "Do you need to make a flowchart?", "x_coord" => 0, "y_coord" => 2, ); $example_box2 = $flowchart->box( #creates a box at (15,0) "string" => "Yes I do.", "x_coord" => 19, "y_coord" => 0, "width" => 13 ); $example_box3 = $flowchart->box( #creates a box at (15,0) "string" => "No I don't.", "x_coord" => 19, "y_coord" => 7 ); $flowchart->relate( [$example_box, "right"] => [$example_box2, "left"] ); $flowchart->relate( [$example_box, "right", -1] => [$example_box3, "left"] ); $flowchart->draw(); Output: +-----------+ +--+ | +-------------+ | | Yes I do. | | +-+ | | | Do you need | +-----------+ | to make a | | flowchart? | | +--+ +-------------+ +-------------+ +-+ | | No I don't. | | | +-------------+
$flowchart = Text::Flowchart->new( "width" => 50, "directed" => 1 );
$example_box = $flowchart->box( #creates a box at (0,0) "string" => "Do you need to make a flowchart?", "x_coord" => 0, "y_coord" => 2, ); $example_box2 = $flowchart->box( #creates a box at (15,0) "string" => "Yes I do.", "x_coord" => 19, "y_coord" => 0, "width" => 13 ); $example_box3 = $flowchart->box( #creates a box at (15,0) "string" => "No I don't.", "x_coord" => 19, "y_coord" => 7 ); $flowchart->relate( [$example_box, "right"] => [$example_box2, "left"], "reason" => "Y" ); $flowchart->relate( [$example_box, "right", -1] => [$example_box3, "left"], "reason" => "N" ); $flowchart->draw(); Output: +-----------+ +--> | +-------------+ | | Yes I do. | | Y-+ | | | Do you need | +-----------+ | to make a | | flowchart? | | N--+ +-------------+ +-------------+ +-> | | No I don't. | | | +-------------+
- draw (?FILEHANDLE?)
-
the draw method actuall outputs your flowchart. You can optionally give it a glob to a filehandle to re-direct the output to that filehandle. For example: $flowchart->draw(); #draw your flowchart on STDOUT; $flowchart->draw(*FILE); #send it to the FILE filehandle.
Required. This is the box that you're relating from or to, it's the first item in an anonymous array.
HISTORY
EXAMPLE
This code will print out the flowchart shown up in the SYNOPSIS.
use Text::Flowchart;
$flowchart = Text::Flowchart->new(
"width" => 50,
"directed" => 0);
$begin = $flowchart->box(
"string" => "BEGIN",
"x_coord" => 0,
"y_coord" => 0,
"width" => 9,
"y_pad" => 0
);
$start = $flowchart->box(
"string" => "Do you need to make a flowchart?",
"x_coord" => 15,
"y_coord" => 0
);
$yes = $flowchart->box(
"string" => "Then my module may help.",
"x_coord" => 0,
"y_coord" => 10
);
$use = $flowchart->box(
"string" => "So use it.",
"x_coord" => 16,
"y_coord" => 8,
"width" => 14
);
$no = $flowchart->box(
"string" => "Then go do something else.",
"x_coord" => 30,
"y_coord" => 17
);
$flowchart->relate(
[$begin, "right"] => [$start, "left", 1]
);
$flowchart->relate(
[$start, "left", 3] => [$yes, "top", 5],
"reason" => "Y"
);
$flowchart->relate(
[$start, "right", 2] => [$no, "top", 5],
"reason" => "N"
);
$flowchart->relate(
[$yes, "right", 4] => [$use, "bottom", 2]
);
$flowchart->relate(
[$use, "bottom", 6] => [$no, "left", 2]
);
$flowchart->draw();
FAQ
Why in the world did you write this thing?
There's a flowcharting-type program that a couple of people use at work. It is only available on PCs. I got cranky about not being able to use it, so I wrote my own. Admittedly, mine isn't as powerful or as easy to use as theirs is, but I'm quite pleased with it nonetheless. Real programmers don't use software tools. Real programmers write software tools.
Hey! I created a box, and then I tried to change its string and it didn't work! What gives?
Boxes are rendered upon their creation. You cannot change their strings once their are created. I may change this in a future release, but for now just wait until you're sure you know what you want in a box before you create it, okay? That way you won't have to change it later.
Hey! I'm running a memory tracer, and even though I deleted a box, the memory wasn't freed up. Do you have a memory leak or what?
Nope. Boxes are stored internally inside the flowchart object. The box variable that you get back from ->box is just a reference to the box that lives inside the flowchart. Since the flowchart still knows the box exists (even if you don't), the memory is not freed up. Naturally, it will be freed once perl exits. I may add in the ability to remove boxes to a future release.
I want to draw lines from the left side of a box to the left side of another box, but I get an error. Why?
Because you can't draw lines like that. :)
Basically, there are 10 types of lines that can be drawn (all of which are done by the relate method, naturally). They look like:
+---+ +--+ +--+ + + + +-+ +-+ + +
| | | | | | | | |
+--+ +--+ + +--+ +--+ + + +--+ +--+
| |
+ +
Or horizontal, zig-zagging horizontal, vertical, zig-zagging vertical, and corners. Connecting the same sides of boxes would require another type of line, (the "handle", FWIW), and Text::Flowchart can't draw those. If I can think of a good way to implement them I will. Incidentally, you can draw your own lines if you need to, using the ->line method.
$flowchart->line(
"from" => [xfrom, yfrom],
"to" => [yfrom, yto],
"reason"=> "Y"
);
But you really should use relate and not connect the same sides of boxes, it will make your life much simpler.
I made a flowchart, but all of the lines cross over each other and make a mess. Why?
Text::Flowchart has no collision detection. If you try to place a box where another box already was, it'll gladly be drawn over. Whichever boxes or lines are drawn last will take priority. If things are being over-written, then you need to change the coordinates of your boxes so they stop over-writing each other, or change the box sides that are related so they don't draw over boxes.
Squares and rectangles are boring. Can I draw other shapes?
Not yet. The module is actually numerous packages, heavily subclassing each other. Adding in additional shapes is quite easy, actually. You'd just need to define a subclass of the Text::Flowchart::Shape class and define the necessary functions. You'd have to declare a render method to actually create the ASCII object, a relate method that knows how to draw the lines between objects, a get_next_coord method to get the next available coordinate on a given side, and make the necessary modifications to your new constructor (calling ::Shape's init(), of course), and you're done!
It's actually much easier than it sounds, from a programming standpoint. Figuring out how to properly render and relate object, that's tricky. I will be adding more shapes into a future release. If anyone wants to add their own shapes, e-mail 'em to me and I may add them in in the future.
So what is it with these version numbers anyway?
I'm going to try to be consistent in how I number the releases.
The hundredths digit will indicate bug fixes, etc.
The tenths digit will indicate new and/or better functionality, as well as some minor new features.
The ones digit will indicate a major new feature or re-write.
Basically, if you have x.ab and x.ac comes out, you want to get it guaranteed. Same for x.ad, x.ae, etc.
If you have x.ac and x.ba comes out, you'll probably want to get it. Invariably there will be bug fixes from the last "hundredths" release, but it'll also have additional features. These will be the releases to be sure to read up on to make sure that nothing drastic has changes.
If you have x.ac and y.ac comes out, it will be the same as x.ac->x.ba but on a much larger scale.
Anything else you want to tell me?
Sure, anything you need to know. Just drop me a message.
MISCELLANEA
If there's something that you feel would be worthwhile to include, please let me know and I'll consider adding it.
How do you know what's a worthwhile addition? Basically, if it would make your life easier. Am I definitely going to include it? Nope. No way. But I'll certainly consider it, all suggestions are appreciated. I may even be nice and fill you in on some of the ideas I have for a future release, if you ask nicely. :)
COPYRIGHT (again) and LICENSE
Copyright (c) 1999 James A Thomason III (jim3@psynet.net). All rights reserved. This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself. Except if you profit from its distribution. If this module or works based upon this module are distributed in a way that makes you money, you must first contact me to work out a licensing arrangement. I'm just giving the thing away, but I'm not going to allow people to make money off of it unless I get a cut. :) Amounts will be hammered out on an individual basis. CPAN mirrors are exempt from this stipulation. If you're not sure if you're distributing it in a way that makes you money, contact me with details and we'll make a decision.
CONTACT INFO
So you don't have to scroll all the way back to the top, I'm Jim Thomason (jim3@psynet.net) and feedback is appreciated. Bug reports/suggestions/questions/etc. Hell, drop me a line to let me know that you're using the module and that it's made your life easier. :-)
2 POD Errors
The following errors were encountered while parsing the POD:
- Around line 1288:
You forgot a '=back' before '=head1'
- Around line 1376:
Deleting unknown formatting code U<>