View Single Post

The inner workings - the .graffleshapes commands:

So now that we've seen the basic for of a .graffleshapes document, lets
look at the formating and commands to create our own. To begin, select
everything in the shapestutorial file and delete it. Now we are ready to
create our own file.

All .graffleshapes documents open with a {. After this we can define
either shapes or arrow heads, by stating one of the following

shapes = (
arrowheads = (

We'll first take a look at creating shapes. To create a shape, we start
with a {, and then enter in the information about the shape. Inside of
these braces there are a number of options that can be set.

ShouldExport: This can be set to YES or NO. Since you are creating a
third party shape, this should most likely set to YES, so that the
information needed to create your shape will be saved along with any file
that contains one of your shapes.

InspectorGroup: This can be set as any real number. The
InspectorGroup is the position of your shape in relation to the other
shapes in the Shape Info panel. If you set this value to be 40+ your
shape will be added to the end of the list. NOTE: this can be a decimal
value. 8.473 comes before 9, so this allows you to add shapes in a
specific position without reordering all of the other shapes.

ShapeName: This should be the name of your shape. This isn't a big
deal, but should be a reminder to you as to what shape is defined here.

StrokePath: This is where we actually define the border of the shape
itself. The definition of the StrokePath looks something like,

StrokePath = {
elements = (

The data for the StrokePath consists of elements, which can take one of
four value types: MOVETO, LINETO, CURVETO, and CLOSE. All of these
values, except close also require at least one point value (CURVETO has
several requirements which will be discussed later). It is important to
note that a StrokePath doesn't necessary have to be closed. However if
you do not close a StrokePath you must define a FillPath which will be
defined later or your fill will be the area created with the MOVETO
command followed by a CLOSE command. This can have unwanted results so I
recommend using a FillPath.

points: points are the x and y coordinates that you refer too. The
positive x-axis points to the right of the origin, while the positive
y-axis points down from the origin. This is opposite of standard x, y
convention so if you draw out your shapes before hand remember to add a
negative in front of the y value. The syntax for declaring points is as

point = "{x , -y}"; NOTE: I have added a negative in front of the
y for convenience.

For conventions sake it is best to keep your x and y values between 0.5
for basic points, with a limit of 1 for control points which will be
discussed in the CURVETO section. This also makes it much easier to
position your TextBounds which will be discussed later.

MOVETO: This value takes a point to which the cursor is moved to.
This will always be the first element that is defined. The format for
MOVETO is as follows:

{element = MOVETO; point = "{x , -y}"; }

LINETO: This value takes a point to which a line will be drawn from
the current position. This can follow any of the other element types
except CLOSE. If the point that LINETO is drawing to is the original
point of the shape then this can also be the last element listed for your
StrokePath in place of CLOSE. The format for LINETO is as follows:

{element = LINETO; point = "{x , -y}"; }

CURVETO: This is the most complicated part of creating a shape.
Curves in graffleshapes are all Bezier curves. While I won't go into
great detail, I will begin the discussion of the CURVETO value with an
intro to Bezier curves.

Bezier curves are defined by four points. These are the start point, (x0,
y0) and end point, (x3, y3) of the curve and the two control points, (x1,
y1) and (x2, y2) of the curve. The bezier curve itself is defined by two
equations based off the variable t and our four points. One equation is
for x values and the other for y values. Both curves are evaluated for
all values of t between 0 and 1.

Adobe's PostScript references defines the equations as:

x(t) = axt3 + bxt2 + cxt + x0
y(t) = ayt3 + byt2 + cyt + y0

From these values of a, b, and c and the start and end points you can
calculate the control points using these equations.

x3 = x0 + ax + bx + cx
x1 = x0 + cx / 3
x2 = x1 + (cx + bx) / 3

y3 = y0 + ay + by + cy
y1 = y0 + cy / 3
y2 = y1 + (cy + by) / 3

In OmniGraffle, you will need to use the positive y value (since our axis
is inverted) in your calculations. To properly form the bezier curve, the
CURVETO element requires both of the control points be defined before it
is executed. This in done in the following manner:

control1 = "{x1 , -y1}";
control2 = "{x2 , -y2}";

Then the CURVETO command can be called like we did with MOVETO and LINETO
as shown below:

element = CURVETO; point = "{x3 , -y3}";

Curly Braces surround both the definition of the control points and the
CURVETO element. As you can see it is not an easy process to define lines,
and can be rather time consuming. However I have found several tricks
that can be used to make symmetric lines. When you sketch out your curve,
pick the control points to be on the outside of your curve and make the
first control point be symmetrically to the second point in relation to
the start and end points. When you think you have a good curve, test it
out and then make changes as necessary.

CURVETO can be used in place of the CLOSE command if the end point of the
curve is the original starting point of the shape.

CLOSE: This value simply draws a line from the current point to the
original point of the shape and closes the shape. This should be the last
element that you list if you use it. Both LINETO, and CURVETO can be use
in place of CLOSE if their end point is the starting point of the shape.
The format for CLOSE is very simple as shown below:

{element = CLOSE;}

FillPath: A FillPath is only needed for a shape if you do not close
the StrokePath and want to specify a specific fill. The format for the
FillPath command is very similar to the StrokePath as seen below.

FillPath = {
elements = (

The data for the FillPath consists of the same elements that are used in
the StrokePath. However unlike the StrokePath the fill path must always
end with either the CLOSE element, or a LINETO or CURVETO element with it'
s end point being the starting point of the FillPath.

TextBounds: TextBounds define the text box to be used when someone
tries to enter text into your shape. TextBounds requires an x and y
position, a height and a width. The x and y values are the position of
the upper left corner of the text box measured from the upper left corner
of the drawing box. If we use the standard convention, this means we are
measuring the distance to the right of -0.5, and down from -0.5. The x
and y values should be between (0 , 0) and ( [1-width] , [1-height] ) if
you want the box to be completely inside of your shape, but this doesn't
have to be the case. The width and the height of the box are the distance
to the right of x and down from y that you want the box to be. The format
for defining you TextBounds is:

TextBounds = "{{x , y}, {width, height}}";
"Vroom! Vroom!!"