----------------------------------------------------------------------
Visual Thought User-Definable Shapes

Last update: 24 July 1998

Confluent Technical Notes
Copyright (c) 1997 Confluent, Inc.  All rights reserved.
Suggestions or questions to 415-764-1000 or vthought@confluent.com.
----------------------------------------------------------------------

Visual Thought contains a powerful library of general- and
multi-purpose shapes suitable for many corporate drawing tasks,
including technical graphics, software design diagramming, network
diagramming, flowcharting and business process charting, and many
more.

This technical note describes Visual Thought's capability to define
new shapes using text description files.


User-Definable Shapes
---------------------
While the default set of shapes provided by Visual Thought suffices
for most purposes, you may occasionally wish to create shapes or
a library of shapes according to your own specifications.

You can create a text document, using a simple file format, to define
shapes as complex (or more so) than Visual Thought's predefined shapes,
with all the properties of the predefined shapes.


Shape Capabilities
------------------
Visual Thought's shapes, whether predefined or user-defined, all have
the following properties and capabilities:

- Shapes can be resized from the graphical interface using either
  direct manipulation (using the 8 selection handles on a selected
  shape), or by other means, such as the Inspector Position Panel.

- The graphical attributes of shapes can be modified at will,
  including colors, line widths, dash types.

- Connecting lines made to shapes intelligently track the perimeters
  of the shapes exactly, no matter how complex the shape.

- Text can be added to shapes with a variety of fonts, colors, styles,
  and sizes, limited only by the operating system.

- Shapes are "dynamic," in that any shape can be changed into any other
  shape at any time using the Inspector Shape Panel.

- Non-graphical attributes can be attached to shapes, such as images
  that display inside the shapes, sounds that can be recorded,
  played, or edited from the Visual Thought interface, or arbitrary
  files, such as HTML documents, spreadsheets, databases, or other
  Visual Thought documents.


Properties of User-Definable Shapes
-----------------------------------
New shape definitions can be created using a simplified
PostScript-like drawing model.  All shape geometry is specified within
a 2-unit by 2-unit box with origin (0,0) at the box's center; the
bottom left vertex of the box is thus at (-1,-1) and the top right
vertex is at (1,1).  This 2x2 unit box is mapped onto the rectangular
area the shape has when the user creates or resizes the shape.

The components of a shape definition include:

- Shape Type (or name): Each new shape definition must have a unique
  name; new shapes defined with the same name as an already existing shape
  are discarded.

- Text Bounds: Each shape has the ability to display text anywhere
  within the shape geometry box.  If not specified, a text bounds that
  covers the entire 2x2 geometry box is assumed.

- Curve Path: The curve path specifies how the shape is to be drawn.
  Primitives in the curve path, such as newPath, lineTo, and stroke,
  are in a PostScript-like format for maximum flexibility and power.

- Clip Path: The clip path is specified using a relevant subset of
  curve path primitives: newPath, moveTo, lineTo, curveTo, rectEllipse,
  closePath.  This path specifies the area inside which images attached
  to shapes are clipped.

- Attractors: The attractors section allows specification of a point
  on the shape to which connections will be "attracted," along with the
  rules by which the attraction will occur.  For example, an attractor
  definition such as:

    (top 0.0 1.0)

  means that a single-segment connection made to a shape with this
  attractor defined will locate its bound end at (0.0 1.0) on the
  shape's unit box as long as the connection's free end is above the
  shape's center.


Directory and File Structures
-----------------------------
Any properly-defined Shape Definition Document (with a .vtdef suffix)
can be opened, or read, at any time from the File->Open... menu item
to read new shape definitions.  These shape definitions immediately
appear in the Inspector Shape Panel and the Selection Dialog.

Upon startup, Visual Thought searches for Shape Definition Documents
(SDD's) in the following directories:

   examples/definitions  : the system shape definition directory
   .vthought/definitions : the user shape definition directory

The examples directory is in one of the following locations (assuming
Visual Thought has been installed in the default location):

  UNIX:

    /usr/local/confluent/vt-1.4/share/examples

  Windows:

    C:\Program Files\Confluent\Visual Thought 1.4\examples

The .vthought directory is in one of the following locations:

  UNIX:

    ~/.vthought

  Windows:

    The first of the following directories that is available:

      %HOME%\.vthought
      %HOMEDRIVE%%HOMEPATH%\.vthought
      C:\Program Files\Confluent\Visual Thought 1.4\.vthought

Visual Thought will read all Shape Definition Documents (SDD's) it
finds in the system directory first, then all SDD's from the user
directory.  Since shape types (or names) must be unique, a _newer_
shape definition read with the same type (or name) as an older
definition will be discarded.


File Format Description
-----------------------
The Shape Definition Document format (.vtdef) is very similar to those
of the Visual Thought Document (.vthought) or Visual Thought Palette
(.vpalette) formats.  For a complete discussion of the Visual Thought
file format, please see Appendix H of the Visual Thought User's Guide.

A .vtdef document directory must contain a single file named
vthought.txt.  This text file is very much like LISP, using ";" for
comments and parentheses for delimiters.

The grammar of the vthought.txt file format is defined as a series
of expressions.  Each expression definition is of the form:

  expression_name ->
    expression_expansion

The name of each expression is to the left of the arrow; the expansion
of that expression is to the right.  Keywords are normally associated
with a value in angle brackets <>, such as:

  (keyword <value>)

where <value> can be any of:

  <string>  : a string of characters delimited by double quotes (e.g, "foo")
  <number>  : an integer or decimal number (e.g., 1 or 2.0)
  <boolean> : either T (true) or F (false)

Optional constructs in the expression expansions are surrounded by
square brackets [].

document ->
  header_section
  shape_definitions_section

header_section ->
  (header
    (program <string>)
    (version <string>)
    [(buildDate <string>)]
    [(writeDate <string>)]
    [(documentPath <string>)]
  )

shape_definitions_section ->
  (shapeDefinitions
    (genericShape
      (type <string>)
      [(native <boolean>)]
      [(textBounds <number> <number> <number> <number>)]
      curve_path_specification
      [clip_path_specification]
      [attractors_specification]
    )
  )

curve_path_specification ->
  (curvePath
     (newPath <number> <number>)
     [(setLineWidth <number>)]
     [(setColor <number> <number> <number>)]
     (moveTo <number> <number> [<boolean>])
     (lineTo <number> <number> [<boolean>])
     (curveTo <number> <number> <number> <number> <number> <number> [<boolean>])
     (rectEllipse <number> <number> <number> <number>)
     (closePath)
     (stroke <boolean>)
     (fill <boolean>)
   )

clip_path_specification ->
  (clipPath
    (newPath <number> <number>)
    [(lineTo <number> <number>)]
    [(curveTo <number> <number> <number> <number> <number> <number>)]
    [(rectEllipse <number> <number> <number> <number>)]
    (closePath)
  )

attractors_specification ->
  (attractors
    (default <number> <number>)
    (top <number> <number>)
    (bottom <number> <number>)
    (left <number> <number>)
    (right <number> <number>)
    (ulQuadrant <number> <number>)
    (urQuadrant <number> <number>)
    (llQuadrant <number> <number>)
    (lrQuadrant <number> <number>)
    (angular <number> <number> <number> <number>)
  )


Keyword Definitions
-------------------

This section defines all the keywords referenced above.  Each keyword
definition is in the following form:

   [keyword_name]
      value: expression | literal
      default: literal
      used by: keyword*

"keyword_name" is enclosed in square brackets if it is optional.
"value" is an expression or a literal (a string, number, or boolean).
If the value of a keyword is a literal, the default value, if any,
will be listed.  Any optional keyword with a default literal value
will have the default value assumed if the keyword does not appear in
the document.  The "used by" field lists zero or more keywords of the
expressions that that can contain the keyword.  Finally, the keyword
is described in the text below the "used by" field.

----------------------------------------------------------------------
header
   value: expression+
   default:
   used by:

Introduces expressions describing the document.
----------------------------------------------------------------------
program
   value: string
   default:
   used by: header

The program for which the document was written.  The value "vthought"
is expected.
----------------------------------------------------------------------
version
   value: string
   default:
   used by: header

The version number of the program for which the document was written.
The value "1.0" is expected.
----------------------------------------------------------------------
[buildDate]
   value: string
   default:
   used by: header

The date on which the program that wrote the document was built.
----------------------------------------------------------------------
[writeDate]
   value: string
   default:
   used by: header

The date on which the document was written.
----------------------------------------------------------------------
[documentPath]
   value: string
   default:
   used by: header

The file system path to the document.  For example, "C:\My Documents\
test.vthought".
----------------------------------------------------------------------
shapeDefinitions
   value: expression+
   default:
   used by:

Introduces the section containing shape definitions.
----------------------------------------------------------------------
genericShape
   value: expression+
   default:
   used by: shapeDefinitions

Introduces a single generic shape definition.
----------------------------------------------------------------------
type
   value: string
   default:
   used by: genericShape

The type or name of the shape definition.  Each shape definition's
type must be unique.  If a shape definition's type is the same as
that of an existing shape definition, the _newer_ shape's definition
is discarded.
----------------------------------------------------------------------
[native]
   value: boolean
   default: F
   used by: genericShape

To ensure that relevant shape definitions are always available
wherever they are used, shapes that are used in a document but are not
natively defined by Visual Thought have their definitions written out
into the document when saved.  Native shape definitions, however, are
never written because their definitions are always available from
Visual Thought itself.

This flag is provided to aid efficiency.  If, for example, a set of
shapes will be used throughout a site, their definitions may be placed
in the examples/definitions directory to be automatically loaded
whenever any user at the site starts up Visual Thought.  But if these
definitions are always loaded, the site administrator may decide to
mark such definitions as native to reduce the time and space required
to load and save documents using those definitions.
----------------------------------------------------------------------
[textBounds]
   value: number number number number
   default: -1.0 -1.0 1.0 1.0
   used by: genericShape

The textBounds determine the rectangle inside which text may appear in
the shape.  The four values are min x, min y, max x, max y,
respectively, and they map directly onto a shape's 2x2 unit box
centered over the origin at (0, 0).
----------------------------------------------------------------------
curvePath
   value: expression+
   default:
   used by: genericShape

Introduces the section that determines how the shape will be drawn.
This section uses a simplified PostScript-like drawing model.
----------------------------------------------------------------------
newPath
   value: number number
   default:
   used by: curvePath, clipPath

Starts a new path at the given (x, y) location.
----------------------------------------------------------------------
[setLineWidth]
   value: number
   default: -1
   used by: curvePath

Sets the line width, in points, of the current path.  Use a line width
of -1 to specify the default line width used to draw the shape.  The
default line width is user-specified via the Inspector Style Panel at
the time the shape is drawn.
----------------------------------------------------------------------
[setColor]
   value: number number number
   default:
   used by: curvePath

Sets the color of the current path.  The numbers are the color's r, g,
and b values, respectively, and range from 0 to 65535.  Use a value of
(-1 0 0) to specify the default stroke color, and (0 -1 0) to specify
the default fill color.  The default fill and stroke colors are
user-specified via the Inspector Style Panel at the time the shape is
drawn.
----------------------------------------------------------------------
[moveTo]
   value: number number [boolean]
   default: * * F
   used by: curvePath

Adds to the current path a straight line that is _not_ drawn from the
current point to the given (x, y) location.  The optional boolean
value indicates whether the line should be regarded as "intersectable"
for determining whether connections made to the shape should test the
line during shape perimeter tracking.
----------------------------------------------------------------------
[lineTo]
   value: number number [boolean]
   default: * * T
   used by: curvePath, clipPath

Adds to the current path a straight line from the current point to the
given (x, y) location.  The optional boolean value indicates whether
the line should be regarded as "intersectable" for determining whether
connections made to the shape should test the line during shape
perimeter tracking.
----------------------------------------------------------------------
[curveTo]
   value: number number number number number number [boolean]
   default: * * * * * * T
   used by: curvePath, clipPath

Adds to the current path a Bezier curve, using the current point as
the zero'th control point and the given (x1, y1, x2, y2, x3, y3)
locations as the other three control points.  The optional boolean
value indicates whether the curve should be regarded as "intersectable"
for determining whether connections made to the shape should test the
curve during shape perimeter tracking.
----------------------------------------------------------------------
[rectEllipse]
   value: number number number number
   default:
   used by: curvePath, clipPath

Adds to the current path an ellipse inscribed with the box defined by
the given (x1, y1, x2, y2) points.  The ellipse is not "intersectable"
in that connections made to the shape will not test the ellipse during
shape perimeter tracking.
----------------------------------------------------------------------
[closePath]
   value:
   default:
   used by: curvePath, clipPath

Closes the current path.
----------------------------------------------------------------------
[stroke]
   value: boolean
   default: F
   used by: curvePath

Strokes the current path.  A boolean value of F indicates that the
stroke should only occur if stroking is turned on when the shape is
drawn; a T value indicates that the stroke should only occur if
_filling_ is turned on when the shape is drawn.  Stroking and filling
are turned on and off via the Inspector Style Panel at the time the
shape is drawn.
----------------------------------------------------------------------
[fill]
   value: boolean
   default: F
   used by: curvePath

Fills the current path.  A boolean value of F indicates that the
fill should only occur if filling is turned on when the shape is
drawn; a T value indicates that the fill should only occur if
_stroking_ is turned on when the shape is drawn.  Stroking and filling
are turned on and off via the Inspector Style Panel at the time the
shape is drawn.
----------------------------------------------------------------------
[clipPath]
   value: expression+
   default:
   used by: genericShape

Introduces the section that determines the area within which images
attached to shapes will be clipped.  If no clip path is specified,
the entire 2x2 unit box for the shape will be the clip path.
----------------------------------------------------------------------
[attractors]
   value: expression+
   default:
   used by: genericShape

Introduces the section containing a list of "attractor" points.
Normal shapes force attached connections to point always to the
shapes' centers; attractors, however, "attract" connections to
specific points under certain conditions.  That is, connections to a
shape with attractor points defined "snap" to certain points on that
shape.

Each kind of attractor is activated under different circumstances.  In
every case, however, it is the geometry of connecting lines relative
to the shape containing the attractors that determines the activation
conditions.  If a connecting line is made to a shape, the position of
the vertex next to the endpoint connected to the shape (the
"penultimate vertex") is used to determine which attractor is
activated.

Multiple attractor points may be specified for any shape, in which
case the attractor points are tested in the order they are specified.
For example, a specification of:

   (attractors
     (top 0.0 1.0)
     (default 0.0 -1.0)
   )

means that a single-segment connection made to a shape with these
attractors defined will locate its bound end at (0.0 1.0) on the
shape's unit box as long as the connection's free end is above the
shape's center, and at (0.0 -1.0) otherwise.
----------------------------------------------------------------------
[default]
   value: number number
   default:
   used by: attractors

Defines a default attractor.  A connection made to a shape containing
only a default attractor will always snap to the (x, y) coordinates
defined by the attractor.  Other attractors specified prior to the
default attractor in the shape's attractor list will override this
behavior.
----------------------------------------------------------------------
[top]
   value: number number
   default:
   used by: attractors

Defines a "top" attractor.  A connection made to a shape containing a
top attractor will snap to the (x, y) coordinates defined by the
attractor as long as the "penultimate vertex" (see the discussion of
the attractors keyword above) is above the midpoint of the shape.
----------------------------------------------------------------------
[bottom]
   value: number number
   default:
   used by: attractors

Defines a "bottom" attractor.  A connection made to a shape containing a
bottom attractor will snap to the (x, y) coordinates defined by the
attractor as long as the "penultimate vertex" (see the discussion of
the attractors keyword above) is below the midpoint of the shape.
----------------------------------------------------------------------
[left]
   value: number number
   default:
   used by: attractors

Defines a "left" attractor.  A connection made to a shape containing a
left attractor will snap to the (x, y) coordinates defined by the
attractor as long as the "penultimate vertex" (see the discussion of
the attractors keyword above) is to the left of the midpoint of the
shape.
----------------------------------------------------------------------
[right]
   value: number number
   default:
   used by: attractors

Defines a "right" attractor.  A connection made to a shape containing a
right attractor will snap to the (x, y) coordinates defined by the
attractor as long as the "penultimate vertex" (see the discussion of
the attractors keyword above) is to the right of the midpoint of the
shape.
----------------------------------------------------------------------
[ulQuadrant]
   value: number number
   default:
   used by: attractors

A connection made to a shape containing an UL quadrant attractor will
snap to the (x, y) coordinates defined by the attractor as long as the
"penultimate vertex" (see the discussion of the attractors keyword
above) is in the upper left quadrant, using the coordinate system with
the origin at the shape's center.
----------------------------------------------------------------------
[urQuadrant]
   value: number number
   default:
   used by: attractors

A connection made to a shape containing an UR quadrant attractor will
snap to the (x, y) coordinates defined by the attractor as long as the
"penultimate vertex" (see the discussion of the attractors keyword
above) is in the upper right quadrant, using the coordinate system
with the origin at the shape's center.
----------------------------------------------------------------------
[llQuadrant]
   value: number number
   default:
   used by: attractors

A connection made to a shape containing an LL quadrant attractor will
snap to the (x, y) coordinates defined by the attractor as long as the
"penultimate vertex" (see the discussion of the attractors keyword
above) is in the lower left quadrant, using the coordinate system with
the origin at the shape's center.
----------------------------------------------------------------------
[lrQuadrant]
   value: number number
   default:
   used by: attractors

A connection made to a shape containing an LR quadrant attractor will
snap to the (x, y) coordinates defined by the attractor as long as the
"penultimate vertex" (see the discussion of the attractors keyword
above) is in the lower right quadrant, using the coordinate system with
the origin at the shape's center.
----------------------------------------------------------------------
[angular]
   value: number number number number
   default:
   used by: attractors

The values required by the angular attractor are (x, y, a1, a2).  A
connection made to a shape containing an angular attractor will snap
to the (x, y) coordinates defined by the attractor as long as the
"penultimate vertex" (see the discussion of the attractors keyword
above) is in the angular range between a1 and a2 degrees, using the
coordinate system with the origin at the shape's center.  Zero degrees
is along the horizontal x axis (pointing to the right).


User-Definable Shapes Example
-----------------------------

As an example, the following is a complete file that defines some
shapes.  To load this file into Visual Thought, you would simply save
the lines from the "; Visual Thought 1.0" onward to a file named
vthought.txt, place the vthought.txt file inside a directory named
test.vtdef, and load test.vtdef using the File->Open... menu item from
Visual Thought.

---- cut here ---- cut here ---- cut here ---- cut here ----
; Visual Thought 1.0

(header
  (program "vthought")
  (version "1.3")
  (buildDate "$Date: 1997/02/12 20:53:42 $")
  (writeDate "Tue Mar 4, 1997 5:20:22 PM PST")
  (documentPath "/home/users/william/vthought/test/testdef.vthought")
)

(shapeDefinitions
 (genericShape
  (type "testshape1")
  (native F)
  (textBounds -1.0 -1.0 0.5 0.5)
  (curvePath
   (newPath -1.0 -1.0)
   (setColor -1 0 0)
   (lineTo -0.5 0.5 T)
   (lineTo 1.0 1.0 T)
   (lineTo 0.5 -0.5 T)
   (lineTo -1.0 -1.0 T)
   (stroke F)
   (newPath 1.0 -1.0)
   (setColor -1 0 0)
   (curveTo 0.0 -1.0 -1.0 0.0 -1.0 1.0 T)
   (curveTo 0.0 1.0 1.0 0.0 1.0 -1.0 T)
   (stroke F)
   (newPath 0.0 0.0)
   (setColor 0 -1 0)
   (rectEllipse -0.5 -0.5 0.5 0.5)
   (fill F)
   (setColor -1 0 0)
   (rectEllipse -0.5 -0.5 0.5 0.5)
   (stroke F)
  )
 )
 (genericShape
  (type "testshape2")
  (native F)
  (textBounds -0.75 -0.75 0.75 0.75)
  (curvePath
   (newPath -1.0 -1.0)
   (setColor 0 -1 0)
   (lineTo -0.5 0.5 T)
   (lineTo 1.0 1.0 T)
   (lineTo 0.5 -0.5 T)
   (lineTo -1.0 -1.0 T)
   (closePath)
   (fill F)
   (newPath 1.0 -1.0)
   (setColor 0 -1 0)
   (curveTo 0.0 -1.0 -1.0 0.0 -1.0 1.0 T)
   (curveTo 0.0 1.0 1.0 0.0 1.0 -1.0 T)
   (fill F)
   (newPath -1.0 -1.0)
   (setColor -1 0 0)
   (lineTo -0.5 0.5 T)
   (lineTo 1.0 1.0 T)
   (lineTo 0.5 -0.5 T)
   (lineTo -1.0 -1.0 T)
   (moveTo 1.0 -1.0 F)
   (curveTo 0.0 -1.0 -1.0 0.0 -1.0 1.0 T)
   (curveTo 0.0 1.0 1.0 0.0 1.0 -1.0 T)
   (stroke F)
   (newPath 0.0 0.0)
   (setColor -1 0 0)
   (rectEllipse -0.5 -0.5 0.5 0.5)
   (stroke F)
  )
  (attractors
   (top 0.0 1.0)
   (bottom 0.0 -1.0)
  )
 )
)
