-- Copyright 2012 by Till Tantau
--
-- This file may be distributed an/or modified
--
-- 1. under the LaTeX Project Public License and/or
-- 2. under the GNU Public License
--
-- See the file doc/generic/pgf/licenses/LICENSE for more information
-- @release $Header$
---
-- A |Vertex| instance models a node of graphs. Each |Vertex| object can be an
-- element of any number of graphs (whereas an |Arc| object can only be an
-- element of a single graph).
--
-- When a vertex is added to a digraph |g|, two tables are created in
-- the vertex' storage: An array of incoming arcs (with respect to
-- |g|) and an array of outgoing arcs (again, with respect to
-- |g|). The fields are managed by the |Digraph| class and should not
-- be modified directly.
--
-- Note that a |Vertex| is an abstraction of \tikzname\ nodes; indeed
-- the objective is to ensure that, in principle, we can use them
-- independently of \TeX. For this reason, you will not find any
-- references to |tex| inside a |Vertex|; this information is only
-- available in the syntactic digraph.
--
-- One important aspect of vertices are its anchors -- a concept well
-- familiar for users of \tikzname, but since we need to abstract from
-- \tikzname, a separate anchor management is available inside the
-- graph drawing system. It works as follows:
--
-- First of all, every vertex has a path, which is a (typically
-- closed) line around the vertex. The display system will pass down
-- the vertex' path to the graph drawing system and this path will be
-- stored as a |Path| object in the |path| field of the vertex. This
-- path lives in a special ``local'' coordinate system, that is, all
-- coordinates of this path should actually be considered relative to
-- the vertex' |pos| field. Note that the path is typically, but not
-- always, ``centered'' on the origin. A graph drawing algorithm
-- should arrange the vertices in such a way that the origins in the
-- path coordinate systems are aligned.
--
-- To illustrate the difference between the origin and the vertex
-- center, consider a tree drawing algorithm in which a node |root| has
-- three children |a|, |b|, and |g|. Now, if we were to simply center
-- these three letters vertically and arrange them in a line, the
-- letters would appear to ``jump up and down'' since the height of
-- the three letters are quite different. A solution is to shift the
-- letters (and, thus, the paths of the vertices) in such a way that
-- in all three letters the baseline of the letters is exactly at the
-- origin. Now, when a graph drawing algorithm aligns these vertices
-- along the origins, the letters will all have the same baseline.
--
-- Apart from the origin, there may be other positions in the path
-- coordinate system that are of interest -- such as the center of
-- the vertex. As mentioned above, this need not be the origin and
-- although a graph drawing algorithm should align the origins,
-- \emph{edges} between vertices should head toward these vertex
-- centers rather that toward the origins. Other points of interest
-- might be the ``top'' of the node.
--
-- All points of special interest are called ``anchors''. The |anchor|
-- method allows you to retrieve them. By default, you always have
-- access to the |center| anchor, but other anchors may or may not be
-- available also, see the |anchor| method for details.
--
-- @field pos A coordinate object that stores the position where the
-- vertex should be placed on the canvas. The main objective of graph drawing
-- algorithms is to update this coordinate.
--
-- @field name An optional string that is used as a textual representation
-- of the node.
--
-- @field path The path of the vertex's shape. This is a path along
-- the outer line resulting from stroking the vertex's original
-- shape. For instance, if you have a quadratic shape of size 1cm and
-- you stroke the path with a pen of 2mm thickness, this |path| field
-- would store a path of a square of edge length 12mm.
--
-- @field anchors A table of anchors (in the TikZ sense). The table is
-- indexed by the anchor names (strings) and the values are
-- |Coordinate|s. Currently, it is only guaranteed that the |center|
-- anchor is present. Note that the |center| anchor need not lie at
-- the origin: A graph drawing system should align nodes relative to
-- the origin of the path's coordinate system. However, lines going to
-- and from the node will head towards the |center| anchor. See
-- Section~\ref{section-gd-anchors} for details.
--
-- @field options A table of options that contains user-defined options.
--
-- @field animations An array of attribute animations for the
-- node. When an algorithm adds entries to this array, the display
-- layer should try to render these. The syntax is as follows: Each
-- element in the array is a table with a field |attribute|, which must
-- be a string like |"opacity"| or |"translate"|, a field |entries|,
-- which must be an array to be explained in a moment, and field
-- |options|, which must be a table of the same syntax as the
-- |options| field. For the |entries| array, each element must be
-- table with two field: |t| must be set to a number, representing a
-- time in seconds, and |value|, which must be set to a value that
-- the |attribute| should have at the given time. The entries and the
-- options will then be interpreted as described in \pgfname's basic
-- layer animation system, except that where a |\pgfpoint| is expected
-- you provide a |Coordinate| and a where a path is expected you
-- provide a |Path|.
--
-- @field shape A string describing the shape of the node (like |rectangle|
-- or |circle|). Note, however, that this is more ``informative''; the
-- actual information that is used by the graph drawing system for
-- determining the extent of a node, its bounding box, convex hull,
-- and line intersections is the |path| field.
--
-- @field kind A string describing the kind of the node. For instance, a
-- node of type |"dummy"| does not correspond to any real node in
-- the graph but is used by the graph drawing algorithm.
--
-- @field event The |Event| when this vertex was created (may be |nil|
-- if the vertex is not part of the syntactic digraph).
--
-- @field incomings A table indexed by |Digraph| objects. For each
-- digraph, the table entry is an array of all vertices from which
-- there is an |Arc| to this vertex. This field is internal and may
-- not only be accessed by the |Digraph| class.
--
-- @field outgoings Like |incomings|, but for outgoing arcs.
--
local Vertex = {}
Vertex.__index = Vertex
-- Namespace
require("pgf.gd.model").Vertex = Vertex
-- Imports
local Coordinate = require "pgf.gd.model.Coordinate"
local Path = require "pgf.gd.model.Path"
local Storage = require "pgf.gd.lib.Storage"
---
-- Create a new vertex. The |initial| parameter allows you to setup
-- some initial values.
--
-- @usage
--\begin{codeexample}[code only, tikz syntax=false]
--local v = Vertex.new { name = "hello", pos = Coordinate.new(1,1) }
--\end{codeexample}
--
-- @param initial Values to override default node settings. The
-- following are permissible:
-- \begin{description}
-- \item[|pos|] Initial position of the node.
-- \item[|name|] The name of the node. It is optional to define this.
-- \item[|path|] A |Path| object representing the vertex's hull.
-- \item[|anchors|] A table of anchors.
-- \item[|options|] An options table for the vertex.
-- \item[|animations|] An array of generated animation attributes.
-- \item[|shape|] A string describing the shape. If not given, |"none"| is used.
-- \item[|kind|] A kind like |"node"| or |"dummy"|. If not given, |"dummy"| is used.
-- \end{description}
--
-- @return A newly allocated node.
--
function Vertex.new(values)
local new = {
incomings = Storage.new(),
outgoings = Storage.new()
}
for k,v in pairs(values) do
new[k] = v
end
new.path = new.path or Path.new { 0, 0 }
new.shape = new.shape or "none"
new.kind = new.kind or "dummy"
new.pos = new.pos or Coordinate.new(0,0)
new.anchors = new.anchors or { center = Coordinate.new(0,0) }
new.animations = new.animations or {}
return setmetatable (new, Vertex)
end
---
-- Returns a bounding box of a vertex.
--
-- @return |min_x| The minimum $x$ value of the bounding box of the path
-- @return |min_y| The minimum $y$ value
-- @return |max_x|
-- @return |max_y|
-- @return |center_x| The center of the bounding box
-- @return |center_y|
function Vertex:boundingBox()
return self.path:boundingBox()
end
local anchor_cache = Storage.new ()
local directions = {
north = function(min_x, min_y, max_x, max_y)
return (min_x+max_x)/2, max_y
end,
south = function(min_x, min_y, max_x, max_y)
return (min_x+max_x)/2, min_y
end,
east = function(min_x, min_y, max_x, max_y)
return max_x, (min_y+max_y)/2
end,
west = function(min_x, min_y, max_x, max_y)
return min_x, (min_y+max_y)/2
end,
["north west"] = function(min_x, min_y, max_x, max_y)
return min_x, max_y
end,
["north east"] = function(min_x, min_y, max_x, max_y)
return max_x, max_y
end,
["south west"] = function(min_x, min_y, max_x, max_y)
return min_x, min_y
end,
["south east"] = function(min_x, min_y, max_x, max_y)
return max_x, min_y
end,
}
---
-- Returns an anchor position in a vertex. First, we try to look
-- the anchor up in the vertex's |anchors| table. If it is not found
-- there, we test whether it is one of the direction strings |north|,
-- |south east|, and so on. If so, we consider a line from the center
-- of the node to the position on the bounding box that corresponds to
-- the given direction (so |south east| would be the lower right
-- corner). We intersect this line with the vertex's path and return
-- the result. Finally, if the above fails, we try to consider the
-- anchor as a number and return the intersection of a line starting
-- at the vertex's center with the number as its angle and the path of
-- the vertex.
--
-- @param anchor An anchor as detailed above
-- @return A coordinate in the vertex's local coordinate system (so
-- add the |pos| field to arrive at the actual position). If the
-- anchor was not found, |nil| is returned
function Vertex:anchor(anchor)
local c = self.anchors[anchor]
if not c then
local b
local d = directions [anchor]
if d then
b = Coordinate.new(d(self:boundingBox()))
else
local n = tonumber(anchor)
if n then
local x1, y1, x2, y2 = self:boundingBox()
local r = math.max(x2-x1, y2-y1)
b = Coordinate.new(r*math.cos(n/180*math.pi),r*math.sin(n/180*math.pi))
b:shiftByCoordinate(self.anchors.center)
end
end
if not b then
return
end
local p = Path.new {'moveto', self.anchors.center, 'lineto', b}
local intersections = p:intersectionsWith(self.path)
if #intersections > 0 then
c = intersections[1].point
end
end
self.anchors[anchor] = c
return c
end
--
-- Returns a string representation of a vertex. This is mainly for debugging
--
-- @return The Arc as string.
--
function Vertex:__tostring()
return self.name or tostring(self.anchors)
end
-- Done
return Vertex