Current File : //usr/share/texlive/texmf-dist/tex/generic/pgf/graphdrawing/lua/pgf/gd/lib/Simplifiers.lua |
-- 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$
--- The Simplifiers class is a singleton object.
-- Its methods allow implement methods for simplifying graphs, for instance
-- for removing loops or multiedges or computing spanning trees.
local Simplifiers = {}
-- Namespace
local lib = require "pgf.gd.lib"
lib.Simplifiers = Simplifiers
-- Imports
local Edge = require "pgf.gd.deprecated.Edge"
local Node = require "pgf.gd.deprecated.Node"
--- Algorithm to classify edges of a DFS search tree.
--
-- TODO Jannis: document this algorithm as soon as it is completed and bug-free.
-- TT: Replace this algorithm by something else, perhaps?
--
function Simplifiers:classifyEdges(graph)
local discovered = {}
local visited = {}
local recursed = {}
local completed = {}
local tree_and_forward_edges = {}
local cross_edges = {}
local back_edges = {}
local stack = {}
local function push(node)
table.insert(stack, node)
end
local function peek()
return stack[#stack]
end
local function pop()
return table.remove(stack)
end
local initial_nodes = graph.nodes
for i=#initial_nodes,1,-1 do
local node = initial_nodes[i]
push(node)
discovered[node] = true
end
while #stack > 0 do
local node = peek()
local edges_to_traverse = {}
visited[node] = true
if not recursed[node] then
recursed[node] = true
local out_edges = node:getOutgoingEdges()
for _,edge in ipairs(out_edges) do
local neighbour = edge:getNeighbour(node)
if not discovered[neighbour] then
table.insert(tree_and_forward_edges, edge)
table.insert(edges_to_traverse, edge)
else
if not completed[neighbour] then
if not visited[neighbour] then
table.insert(tree_and_forward_edges, edge)
table.insert(edges_to_traverse, edge)
else
table.insert(back_edges, edge)
end
else
table.insert(cross_edges, edge)
end
end
end
if #edges_to_traverse == 0 then
completed[node] = true
pop()
else
for i=#edges_to_traverse,1,-1 do
local neighbour = edges_to_traverse[i]:getNeighbour(node)
discovered[neighbour] = true
push(neighbour)
end
end
else
completed[node] = true
pop()
end
end
return tree_and_forward_edges, cross_edges, back_edges
end
--
--
-- Loops and Multiedges
--
--
--- Remove all loops from a graph
--
-- This method will remove all loops from a graph.
--
-- @param algorithm An algorithm object
function Simplifiers:removeLoopsOldModel(algorithm)
local graph = algorithm.graph
local loops = {}
for _,edge in ipairs(graph.edges) do
if edge:getHead() == edge:getTail() then
loops[#loops+1] = edge
end
end
for i=1,#loops do
graph:deleteEdge(loops[i])
end
graph[algorithm].loops = loops
end
--- Restore loops that were previously removed.
--
-- @param algorithm An algorithm object
function Simplifiers:restoreLoopsOldModel(algorithm)
local graph = algorithm.graph
for _,edge in ipairs(graph[algorithm].loops) do
graph:addEdge(edge)
edge:getTail():addEdge(edge)
end
graph[algorithm].loops = nil
end
--- Remove all multiedges.
--
-- Every multiedge of the graph will be replaced by a single edge.
--
-- @param algorithm An algorithm object
function Simplifiers:collapseMultiedgesOldModel(algorithm, collapse_action)
local graph = algorithm.graph
local collapsed_edges = {}
local node_processed = {}
for _,node in ipairs(graph.nodes) do
node_processed[node] = true
local multiedge = {}
local function handle_edge (edge)
local neighbour = edge:getNeighbour(node)
if not node_processed[neighbour] then
if not multiedge[neighbour] then
multiedge[neighbour] = Edge.new{ direction = Edge.RIGHT }
collapsed_edges[multiedge[neighbour]] = {}
end
if collapse_action then
collapse_action(multiedge[neighbour], edge, graph)
end
table.insert(collapsed_edges[multiedge[neighbour]], edge)
end
end
for _,edge in ipairs(node:getIncomingEdges()) do
handle_edge(edge)
end
for _,edge in ipairs(node:getOutgoingEdges()) do
handle_edge(edge)
end
for neighbour, multiedge in pairs(multiedge) do
if #collapsed_edges[multiedge] <= 1 then
collapsed_edges[multiedge] = nil
else
for _,subedge in ipairs(collapsed_edges[multiedge]) do
graph:deleteEdge(subedge)
end
multiedge:addNode(node)
multiedge:addNode(neighbour)
graph:addEdge(multiedge)
end
end
end
graph[algorithm].collapsed_edges = collapsed_edges
end
--- Expand multiedges that were previously collapsed
--
-- @param algorithm An algorithm object
function Simplifiers:expandMultiedgesOldModel(algorithm)
local graph = algorithm.graph
for multiedge, subedges in pairs(graph[algorithm].collapsed_edges) do
assert(#subedges >= 2)
graph:deleteEdge(multiedge)
for _,edge in ipairs(subedges) do
-- Copy bend points
for _,p in ipairs(multiedge.bend_points) do
edge.bend_points[#edge.bend_points+1] = p:copy()
end
-- Copy options
for k,v in pairs(multiedge.algorithmically_generated_options) do
edge.algorithmically_generated_options[k] = v
end
for _,node in ipairs(edge.nodes) do
node:addEdge(edge)
end
graph:addEdge(edge)
end
end
graph[algorithm].collapsed_edges = nil
end
-- Done
return Simplifiers