Drawing Paths: The Basics

In my previous blog post, I presented a fairly complete extension panel for InDesign. This time, I’d like to step back a bit and talk about the process of developing an extension for three Creative Suite applications: Illustrator, InDesign, and Photoshop. We’ll be doing something pretty basic–drawing the same shape in all three applications. While we’re at it, we’ll create a framework we can use to draw any valid shape­. This framework will simplify and standardize the process of drawing a shape, and will work for almost any extension that needs to draw shapes.

In this example, we’ll create a generic extension that can run in any of the above applications, and we’ll keep our application specific code isolated from the overall workings of the extension.

It’s All in the (Application-Specific) Details

The trickiest part of this extension, in fact, is the application-specific code. Illustrator, InDesign, and Photoshop can all draw paths, but each of the applications has a slightly different way of performing the task. In all three applications, you can draw paths using the Pen tool, and the resulting shapes, from a scripting point of view, are made up of paths, which are, in turn, made up of path points. The names and the details of the scripting objects differ a bit between products. In Illustrator and Photoshop, for example, the basic drawing object is a pathItem; in InDesign, it’s a pageItem.

Next, the applications have different ways of dealing with measurement. We’ll need to use measurement values to specify horizontal and vertical coordinates if we’re going to position new path points in a document. In InDesign and Photoshop, you can change measurement units at any time; Illustrator, by contrast, always uses points when it’s being driven by a script. In this example, we’ll be using points, but, for your own scripts, you’ll need to convert measurement values to points before sending them to Illustrator.

In InDesign and Photoshop, path point coordinates are specified relative to the ruler zero point; in Illustrator, they’re specified relative to the lower-left corner of the artboard. Since the default location of the ruler zero point is the upper-left corner of the document in all three applications, this means that the paths in Illustrator will be upside down relative to the paths in the other programs, but we don’t need to worry about that right now.

Finally, Illustrator and InDesign have a number of “shortcuts” for drawing paths with specifc arrangements of points–rectangles, ellipses, regular polygons, etc. We’ll ignore those features in favor of a “one size fits all” drawing routine.

In this tutorial, I’ll present support routines that provide a consistent approach to drawing paths in these applications. The idea is to create a generic drawing function that you can use to build new creative effects (ornamental borders for certificates, for example) or wire into your new data driven graphics feature.

You can download the project here (note that this isn’t really a “finished” extension, it’s just a container for the drawing routines):

Building the Extension

I’ll use the Creative Suite Extension Builder to set up the extension–while you can create the same extension structure without the tooling, I’m lazy, and would prefer to let automation take care of creating the files, adding the required libraries, and editing the XML manifest for the project.

The default panel created by the tooling contains three buttons, one for each application. We’ll edit the XML to use a single button, and we’ll add the AppController and AppModel classes to provide the features and data storage for our extension. Then, we’ll edit the AppController module to take care of making certain that the correct application-specific code gets called. We’ll add a bit of code to the AppModel module to sort out which application is running the extension.

We’ll add a public property to the AppModel class to store the name of the host application, and a method to determine the host application. Note that determining the host application can be as simple as the following:

HostObject.mainExtension

I’ve used a more complete approach (the same as in MakeSideHeads) to allow for future expansion to other Creative Suite applications.

In addition, the AppModel class stores a number of other variables that our drawing routines will call on.

Testing

At this point, you can test the extension. When you click the button in the panel’s user interface, it should create a new document and then draw a 72 point square in the center of the document.

Path Construction Details

As I mentioned earlier, paths are made up of path points. Path points, in turn, contain an anchor, a left direction, and a right direction–these are coordinate pairs defining the location of the point itself (the anchor) and the control handles associated with the point. The left direction contains a coordinate pair defining the location of the incoming (along the direction of the path) control handle; the right direction contains the coordinates of the outgoing control handle. The location of the left direction defines the curve of the line segment coming into the point; the location of the right direction controls the curve of the line segment following the point.

All three applications have a way of setting all of the points on the path at once. InDesign and Illustrator can take a simple array of coordinate pairs and convert them to path points; Photoshop can take an array of PathPointInfo objects. In InDesign, the array can be either an array of two-element arrays, or it can be an array containing arrays containing three two-element arrays. In the former case, the arrays contain only the anchor points of the path points; in the latter, the arrays contain the left direction and right direction in addition to the anchor.

In this extension, I’ve tried to make Illustrator and Photoshop work the way that InDesign works. This means that the application specific code for Photoshop has to generate an array of PathPointInfo objects, and that the Illustrator version has to handle the fully specified (three element) arrays by drawing point-by-point. The Illustrator version will also draw point-by-point when the incoming array contains more than one thousand elements­­—this is to work around what seems to be a limitation of Illustrator scripting: paths created using the setEntirePath method appear to be truncated when the path contains a large number of points.

Further Development

Assuming that your extension is now working, let’s add the ability to draw rectangles, circles, and ellipses. Return to the panel’s user interface–the mxml file–and add a set of radio buttons to specify different shape types, and a pair of edit fields to enter the width and height of the shape. Then we’ll change the AppController slightly to pass different arrays of coordinates to the application specific modules.

I added a simple user interface to the mxml file. It’s not intended to be fancy; just enough UI to draw our demonstration shapes. You can view it in the attached project, if you want–I don’t see the need to quote it here (it doesn’t take much UI to make for a very long blog post).

All we need to do to support these additional features is add new array-generating code to the AppController module—there’s no need for us to make any changes to our application-specific code.

As you can see, there’s no difference between the square/rectangle and circle/ellipse array generation routines—it’s purely a matter of user interface.

More to Come

As you’ve probably guessed, the point of this exercise isn’t to draw squares, rectangles, circles, and ellipses. The point was to come up with a general-purpose way of drawing any shape. We’ve done that. All you need to do is provide a routine that generates an array of coordinate pairs, and hand the array off to the application-specific drawing functions we’ve created.

There’s a lot more that we can do with paths and path points, and I’ll be returning to this topic again in this space the near future.