1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
|
-- Copyright 2014 by Ida Bruhns
--
-- This file may be distributed and/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
--- This is a subclass of ForceTemplate, which is used to implement forces between
-- vertex pairs. The forces depend on the canvas distance of the vertices in
-- the pair. This class is e.~g.~ used for electric forces.
-- Imports
local ForceTemplate = require "pgf.gd.force.jedi.base.ForceTemplate"
local lib = require "pgf.gd.lib"
local Preprocessing = require "pgf.gd.force.jedi.base.Preprocessing"
-- Localize math functions
local max = math.max
local sqrt = math.sqrt
local min = math.min
-- Implementation starts here:
local ForceCanvasDistance = lib.class { base_class = ForceTemplate }
function ForceCanvasDistance:constructor ()
ForceTemplate.constructor(self)
self.p = {}
end
-- This force class works on all pairwise disjoint vertex pairs. This
-- function generates a new graph object containing all vertices from the
-- original graph and arcs between all pairwise disjoint vertex pairs. The
-- arcs-table of this new object will be saved in the variable |p|.
--
-- @param v The vertices of the graph we are trying to find a layout for.
function ForceCanvasDistance:preprocess(v)
self.p = Preprocessing.allPairs(v)
end
-- Applying the force to the vertices and adding the effect to the passed net
-- force array
--
-- @param data The parameters needed to aplly the force: The options table,
-- the current time stamp, an array containing the summed up net
-- forces
function ForceCanvasDistance:applyTo(data)
-- locals for speed
local cap = self.force.cap
local fun_u = self.force.fun_u
local fun_v = self.force.fun_v
local net_forces = data.net_forces
local t_max = self.options["maximum time"]
local t_now = data.t_now
local k = data.k
local p = self.p
local time_fun = self.force.time_fun
local fw_attributes = self.fw_attributes
-- Evaluate time function
local time_factor = time_fun(t_max, t_now)
if time_factor == 0 then
return
end
if not fun_v then
local data = { k = k, attributes = fw_attributes }
for _, i in ipairs(p) do
-- dereference
local p2 = i.head
local p1 = i.tail
local p2_pos = p2.pos
local p1_pos = p1.pos
-- calculate distance between two points
local x = p2_pos.x - p1_pos.x
local y = p2_pos.y - p1_pos.y
local d = max(sqrt(x*x+y*y),0.1)
-- apply force function
data.u = p2
data.v = p1
data.d = d
local e = fun_u(data)
-- Include time function
local f = e * time_factor / d
-- calculate effect on x/y
local g = x * f
local h = y * f
-- cap effect if necessary
if cap then
if g <= 0 then
x = max(-cap, g)
else
x = min(cap, g)
end
if h <= 0 then
y = max(-cap, h)
else
y = min(cap, h)
end
else
x = g
y = h
end
-- add calculated effect to net forces
local c1 = net_forces[p1]
c1.x = c1.x - x
c1.y = c1.y - y
local c2 = net_forces[p2]
c2.x = c2.x + x
c2.y = c2.y + y
end
else
-- There are different functions for head and tail vertex
local data = { k = k, attributes = fw_attributes }
for _, i in ipairs(p) do
-- dereference
local p2 = i.head
local p1 = i.tail
local p2_pos = p2.pos
local p1_pos = p1.pos
-- calculate distance between two points
local x = p2_pos.x - p1_pos.x
local y = p2_pos.y - p1_pos.y
local d = max(sqrt(x*x+y*y),0.1)
-- apply force function to distance and k (natural spring length
data.u = p2
data.v = p1
data.d = d
local e_head = fun_u(data)
local e_tail = fun_v(data)
-- Include time function
local f_head = time_factor * e_head / d
local f_tail = time_factor * e_tail / d
-- calculate effect on x/y
local g_head = x * f_head
local g_tail = x * f_tail
local h_head = y * f_head
local h_tail = y * f_tail
-- cap effect if necessary
local x_head, x_tail, y_head, y_tail
if cap then
if g_head <= 0 then
x_head = max(-cap, g_head)
else
x_head = min(cap, g_head)
end
if g_tail <= 0 then
x_tail = max(-cap, g_tail)
else
x_tail = min(cap, g_tail)
end
if h_head <= 0 then
y_head = max(-cap, h_head)
else
y_head = min(cap, h_head)
end
if h_tail <= 0 then
y_tail = max(-cap, h_tail)
else
y_tail = min(cap, h_tail)
end
else
x_head = g_head
x_tail = g_tail
y_head = h_head
y_tail = h_tail
end
-- add calculated effect to net forces
local c1 = net_forces[p1]
c1.x = c1.x - x_tail
c1.y = c1.y - y_tail
local c2 = net_forces[p2]
c2.x = c2.x + x_head
c2.y = c2.y + y_head
end
end
end
return ForceCanvasDistance
|