I've been trying to create a force-directed graphing algorithm with RBX::Lua. So far, everything seems to be working absolutely fine mathematically, but there are a couple of things that make absolutely no sense at all.
My problem is, the nodes experience an unexpected attraction to one another, whether connected or not. What the program does is, all of the unconnected nodes repel each other like a magnet, and all of the connected nodes attract each other like a spring.
I don't see anything in my code that could be causing this.
-- Creating an icon to represent each node
local n = Instance.new("Part", Instance.new("Humanoid", Instance.new("Model")).Parent)
n.Name = "Head"
local tor = Instance.new("Part", n.Parent)
tor.Name = "Torso"
tor.Anchored = true
tor.FormFactor = "Custom"
tor.Transparency = 1
local h = n.Parent.Humanoid
h.Health = 0
h.MaxHealth = 0
h.Parent.Name = "Node"
n.FormFactor = "Symmetric"
n.Shape = "Ball"
n.TopSurface, n.BottomSurface = "Smooth", "Smooth"
n.Size = Vector3.new(2,2,2)
n.BrickColor = BrickColor.new("Institutional white")
n.Anchored = true
Instance.new("Vector3Value", n).Name = "velocity"
-- List of connections and nodes
local t = {
["Metals"]={"Gold", "Silver", "Steel", "Brass", "Mercury"},
["Alloys"]={"Steel", "Brass"},
["Noble Gasses"]={"Helium", "Argon", "Krypton", "Xenon"},
["Water"]={"Hydrogen", "Oxygen"},
["Liquids"]={"Water", "Mercury"},
["Alone"]={}
}
--[[ -- Separate list for testing, commented out
local t = {
["A"]={"B"},
["B"]={"C"},
["C"]={"D"},
["D"]={"E"},
["E"]={"F"}
}]]
-- Add all of the nodes to the workspace, position them randomly
for _, v in pairs(t) do
local p1 = workspace:findFirstChild(_) or n.Parent:clone()
p1.Name = _
p1.Parent = workspace
p1.Head.CFrame = CFrame.new(Vector3.new(math.random(-100000,100000)/1000,0,math.random(-100000,100000)/1000))
for a, b in ipairs(v) do
if v ~= b then
local p2 = workspace:findFirstChild(b) or n.Parent:clone()
p2.Name = b
p2.Parent = workspace
p2.Head.CFrame = CFrame.new(Vector3.new(math.random(-100000,100000)/1000,0,math.random(-100000,100000)/1000))
local at = p1:findFirstChild(b) or Instance.new("ObjectValue", p1)
at.Name = b
local at2 = at:clone()
at2.Parent = p2
at2.Name = _
local lasso = Instance.new("SelectionPartLasso", p2.Head)
lasso.Name = "Link"
lasso.Humanoid = p2.Humanoid
lasso.Part = p1.Head
lasso.Color = BrickColor.new("Institutional white")
end
end
end
local parts = {} -- List of all of the nodes themselves
-- Add all of the nodes to the list
for _, v in ipairs(workspace:GetChildren()) do
if v.ClassName == "Model" then
table.insert(parts, v.Head)
end
end
while wait() do -- Repeat forever waiting one frame between loops
for _, v in ipairs(parts) do
for a, b in ipairs(parts) do
if v ~= b then
local dif = b.Position-v.Position
local force = 0
if b.Parent:findFirstChild(v.Name) then -- if b is conneted to v
force = (dif.magnitude-30)/100
else
force = force - 1/dif.magnitude^2
end
local add = dif/dif.magnitude*force
add = add - v.Position/v.Position.magnitude/100
v.velocity.Value = v.velocity.Value + add
end
end
v.CFrame = v.CFrame + v.velocity.Value -- Postion the node
v.velocity.Value = v.velocity.Value*.2 -- Damping
v.CFrame = v.CFrame-v.CFrame.p*Vector3.new(0,1,0) -- Force 2D (optional)
v.Parent.Torso.CFrame = v.CFrame -- To display links connecting nodes
end
end
I found the problem. "b.Parent:findFirstChild(v.Name)" should have been "b.Parent:findFirstChild(v.Parent.Name)," otherwise it will just return true every time. This is just because of how I have the display of the nodes set up.