BUTTONS FROM CENTER

Working from a center point proves useful for a number of reasons when it comes to automation. In our previous blog we went so far as to use a random number generator to place an object at random locations within a page. With this post we will step back and use the concept of “drawing” from center to create objects other than circles and rectangles. How about a triangle? Perhaps one to be used as a button for navigating an interactive document or states within a multi-state object. You may want to review our previous blog post as we will be borrowing some concepts and handlers from there. Our demonstration script will require knowing some basics of paths in InDesign.

PATHS

A path can be open or closed and is defined by x/y geometric points. Points on a path can be one of four types: corner, line type, smooth, or symmetrical. A list of all of the points within a path is described by the entire path property. For a path that consists of points that are either line type or corner, the entire path will be a list of x-y coordinates. To describe our triangle its path will need three points, possibly equidistant from a center point. Notice how the entire path and the geometric bounds for the triangle are defined in the following.

Simple Triangle

(*Draw Triangle from Center
Assumes user has a document open in Adobe InDesign*)
set cx to 200
set cy to 100
set r to 30
set polypoints to {{cx, cy - r}, {cx + r, cy}, {cx, cy + r}}
set gBounds to {cy - r, cx - r, cy + r, cx + r}
tell application "Adobe InDesign CC 2019"
	set measurement unit of script preferences to points
	set myPage to page 1 of active spread of active window
	tell myPage
		set polyRef to make polygon with properties ¬
{geometric bounds:gBounds, stroke weight:2, fill color:"Black", stroke color:"Black"}
	end tell
	tell path 1 of polyRef
		set entire path to polypoints
		set path type to closed path
	end tell
end tell

The result of this routine is a triangle that “points” to the right. To point the triangle to the left, the x coordinate value for the second point needs to be subtracted from center. We can do this by introducing a second variable (f). See how it is used to define the points for two triangles, one pointing left and one pointing right.

Two Triangles

(*Draws 2 Triangles from Center
Assumes user has a document open in Adobe InDesign*)
set cx to 200
set cy to 100
set r to 30
tell application "Adobe InDesign CC 2019"
    set measurement unit of script preferences to points
    set myPage to page 1 of active spread of active window
    set f to 1 --used to alter x parameter of a point
    my makeTriangle (myPage, cx, cy, r, f)
    set f to -1
    my makeTriangle(myPage, cx, cy, r, f)
end tell
on makeTriangle(pageRef, cx, cy, r, f)
    set gBounds to {cy - r, cx - r, cy + r, cx + r}
    set polyPoints to {{cx, cy - r}, {cx + f * r, cy}, {cx, cy + r}}
    tell application "Adobe InDesign CC 2019"
        tell pageRef
            set polyRef to make polygon with properties ¬
{geometric bounds:gBounds, stroke weight:2, fill color:"Black", stroke color:"Black"}
        end tell
        tell path 1 of polyRef
            set entire path to polyPoints
            set path type to closed path
        end tell
    end tell
end makeTriangle

When you run this script, what you end up with looks like a square that has been rotated.

What we need to add to the script is a value that defines a distance for the space between the triangles coordinates (from center to center). Create a variable d that will represent this value. Add to the top of the script

    set d to 20

Change the calls to the makeTriangle handler to pass this value with the others in the above script.

   set f to 1 
        set triangle1 to my makeTriangle (myPage, cx, cy, r, f, d)
   set f to -1 
        set triangle2 to my makeTriangle (myPage, cx, cy, r, f, d)

With this we can change the makeTriangle handler to read as follows:

Make Triangle

on makeTriangle(pageRef, cx, cy, r, f, d)
    set cx to cx + (d * f)
    set cy to cy
    set gBounds to {cy - r, cx - r, cy + r, cx + r}
    set polyPoints to {{cx, cy - (r - d)}, {cx + f * r, cy}, {cx, cy + (r - d)}}
    tell application "Adobe InDesign CC 2019"
	tell pageRef
	    set polyRef to make polygon with properties ¬
{geometric bounds:gBounds, stroke weight:2, fill color:"Black", stroke color:"Black"}
        end tell
	tell path 1 of polyRef
	    set entire path to polyPoints
	    set path type to closed path
	end tell
    end tell
    return polyRef
end makeTriangle

MULTI STAGE OBJECT

For our demonstration script we will use triangular buttons to navigate a multi stage object.

For this you will need a folder of some images of the same size and resolution and an InDesign document open. To get a list of the files in the folder we can use the following:

set thePrompt to "Choose folder of files for multi state object"
set imagePath to (choose folder with prompt thePrompt) as string
set fileList to list folder imagePath without invisibles

We can then use Image Events to determine the size we will need for our multi state object. Let’s write this as a handler.

Add to the three lines above:

set {wid, hgt} to getImageDimensions(imagePath, item 1 of fileList)
(*Handler returns dimensions of sample image*)
on getImageDimensions(imagePath, sampleItem)
    set imageRef to imagePath & sampleItem
    tell application "Image Events"
	set fileRef to open imageRef
	set theDim to dimensions of fileRef
	set res to resolution of fileRef
	close fileRef
    end tell
    copy theDim to {wid, hgt}
    return {wid, hgt}
end getImageDimensions

Using these dimensions a multi state object can be created. The top of the script can now read as follows:

set thePrompt to "Choose folder of files for multi state object"
set imagePath to (choose folder with prompt thePrompt) as string
set fileList to list folder imagePath without invisibles
set sampleItem to item 1 of fileList
set {wid, hgt} to getImageDimensions(imagePath, sampleItem)
tell application "Adobe InDesign CC 2019"
    set measurement unit of script preferences to points
    set pageRef to page 1 of active spread of active window
    copy bounds of pageRef to {py0, px0, py1, px1}
    set cx to round (px1 / 2)
    set cy to round (py1 / 2)
    set gBounds to {cy - (hgt / 2), cx - (wid / 2), (cy + hgt / 2), (cx + wid / 2)}
    set aliasRef to (imagePath & item 1 of fileList) as alias
    tell pageRef
	set mRef to make multi state object with properties {name:"MSO", geometric bounds:gBounds}
    end tell
end tell

Make sure you have the getImageDimensions handler at the bottom of the script and test. An empty rectangle frame will indicate the object created.

ADD IMAGES

When a multi state object is added to a page, it has two states by default. The script will need to add states so the total number of states will be the same as the number of images.

Add the following to the top of the script just before the last end tell.

set numItems to length of fileList
tell mRef 
    repeat (numItems - 2) times
        make state
    end repeat
end tell

Lastly, the script needs to loop through the list of images and place them into the multi state object. Add the following after the lines added above:

repeat with i from 1 to length of fileList
    set fileName to item i of fileList
    tell state i of mRef
	set rectRef to make rectangle with properties {geometric bounds:gBounds, name:("State" & i)}
    end tell
    tell rectRef to place ((imagePath & fileName) as alias)
end repeat 

Remove the multi state object created in the test above from the document and test the script.

The script will work as anticipated if the images for the document were saved with resolutions of 72 dpi. If not, you will need to make adjustments to the getImageDimensions handler so the effective sizes returned will be at 72 dpi.

NAVIGATION BUTTONS

To allow the user to navigate through the multi state object, the script will now create buttons. For this the following variables will need to be added to the top of the script.

set r to 8 -- the width of the triangle in points
set d to 20 --the distance between the triangles
set oset to 12  --the distance below the bottom of the multi state object and the triangles

The script will add the buttons using two handlers:
1. addButtons – Calls a handler that creates the button
2. makeButton – Creates a triangular button

Start by adding a call to a handler named addButtons. Add this after the last end tell at the top of the script:

set cy to cy + hgt/2 + oset + r/2
addButtons (pageRef, "MSO", cx, cy, r, d)

The addButtons handler calls the handler that creates a button and then sets up the behavior for the individual buttons. Add the following to the bottom of the script:

(*Adds two buttons by calling handler that creates button, adds behavior to each button*)
on addButtons (pageRef, msoName, cx, cy, r, d)
    tell application "Adobe InDesign CC 2019"
        set mRef to multi state object msoName of document 1
        tell pageRef
            set f to -1
	    set btn1 to my makeButton(pageRef, cx, cy, r, f, d)
	    tell btn1
		make goto previous state behavior with properties ¬
{associated multi state object:mRef, enable behavior:true, loops to next or previous:true, behavior event:mouse down}
	    end tell
	    set f to 1
	    set btn2 to my makeButton(pageRef, cx, cy, r, f, d)
	    tell btn2
		make goto next state behavior with properties ¬
{associated multi state object:mRef, enable behavior:true, loops to next or previous:true, behavior event:mouse down}
	    end tell
	end tell
    end tell
end addButtons
(*Creates triangular shaped button using center coordinates*)
on makeButton(pageRef, cx, cy, r, f, d)
    set cx to cx + (d * f)
    set cy to cy
    set gBounds to {cy - r, cx - r, cy + r, cx + r}
    set polyPoints to {{cx - (r * f), cy - r}, {cx + f * r, cy}, {cx - (r * f), cy + r}}
    tell application "Adobe InDesign CC 2019"
	set btnProps to {stroke weight:2, fill color:"Black", stroke color:"Black"}
        tell pageRef
	    set btnRef to make button with properties {geometric bounds:gBounds}
	end tell
	tell state 1 of btnRef
	    set myPoly to make polygon with properties btnProps
	    set entire path of path 1 of myPoly to polyPoints
	end tell
    end tell
    return btnRef
end makeButton

…Our multi state object

Clear the multi state object from the document and test the script. View the interactivity of the completed project using the EPUB Interactivity Preview (Window > Interactive > EPUB Interactivity Preview).

Yes, there is a fair amount of code involved in this script. But it is so useful. Think of the steps required to make a multi state object manually. It can be a little tedious.

UPWARD AND ONWARD

To complete the script, add a trap for errors (user cancels out of choose file dialog, or other.

Additionally, suppose you want your buttons to be placed to the left and right of the multi state object. Just a couple of variable values need to be changed in the script to make this happen. Can you spot them?

Hint: The cy value will be the original value (y coordinate for the multi state object center point). The d value will be one-half of the width of the multi state object plus an offset value.

Try it.

For a real challenge add a dialog box to have the user define the values for the project parameters:

  • size (measurement edit box set for points)
  • enabling groups to determine placement of buttons (below or sides)
  • If below is chosen, then values for offset and distance between are needed.
  • If sides is chosen, value for offset is needed.

You could even provide widgets for color, fill, and stroke, or a dropdown to choose an object style. Be creative.

Disclaimer:
Scripts provided are for demonstration and educational purposes. No representation is made as to their accuracy or completeness. Readers are advised to use the code at their own risk.