Roughly, I’d say the intersection idea would work, but is a little finicky. If you can’t figure out which wall is the outer one while you’re generating it, I suppose I’d suggest something like
- Given corner A-B-C
-
ba = a - b
, bc = c - b
- Calculate a vector that goes inbetween them like
between = ba + bc
- Check
LookVector:Dot(between)
for all the walls. If its positive, its an inner wall.
Sure, I could probably figure out the math if I sat down for a sec. But I spent enough time today putting together this demo of the wedges idea so maybe tomorrow
The main part is this math:
type Vertex = {id: string, pos: Vector3, edges: {Edge}}
type Edge = {a: string, b: string}
local poles: {[string]: Vertex} = {}
local walls: {Edge} = {}
-- ...
-- Given a vertex with two thick parts like this:
--[[
\ /
\ /
\ \ / /
\ \/ /
\ /\ /
\ / \ /
\ / \ /
\ / \ /
\ - _ _ - /
\ _ -- _ /
\ _ - - _ /
- -
]]
-- Calculates the amount each part would need to be shortened so that they meet like this instead:
--[[
\ /
\ /
\ \ / /
\ _ - - _ /
\ _ - - _ /
\ _ - - _ /
- -
]]
local function ComputeCornerInset(v: Vertex)
assert(#v.edges == 2)
local e1 = v.edges[1]
local e2 = v.edges[2]
-- find the two vertices that aren't this one
local p1 = poles[e1.a]
if p1.id == v.id then p1 = poles[e1.b] end
local p2 = poles[e2.a]
if p2.id == v.id then p2 = poles[e2.b] end
local v1 = (p1.pos - v.pos).Unit
local v2 = (p2.pos - v.pos).Unit
local bisect = (v1 + v2).Unit
local dot = v1:Dot(bisect)
local h = THICKNESS / (2 * math.sqrt(1 - dot*dot))
local inset = h * dot
return inset, bisect
end
Derived like this:
After you have the inset amount it’s just a bunch of cframing. Here’s the full code, you can stick it in workspace and play around
I tried to keep a data structure similar to the one you proposed. Only difference is that mine is with tables instead of folders, but it’s the same idea
--!strict
-- handles
local a = Instance.new("Part")
a.Anchored = true
a.Size = Vector3.new(2, 6, 2)
a.BrickColor = BrickColor.Red()
a.Position = Vector3.new(-1, 3, -20)
a.Parent = workspace
local b = a:Clone()
b.BrickColor = BrickColor.Green()
b.Position = Vector3.new(-10, 3, -5)
b.Parent = workspace
local c = a:Clone()
c.BrickColor = BrickColor.Blue()
c.Position = Vector3.new(5, 3, 10)
c.Parent = workspace
local d = a:Clone()
d.BrickColor = BrickColor.Yellow()
d.Position = Vector3.new(15, 3, -5)
d.Parent = workspace
-- graph stuff
type Vertex = {id: string, pos: Vector3, edges: {Edge}}
type Edge = {a: string, b: string}
-- map from an ID to a vertex object
local poles: {[string]: Vertex} = {}
local walls: {Edge} = {}
local function AddPole(id, pos)
poles[id] = {id=id, pos=pos, edges={}}
end
local function AddEdge(a: string, b: string)
local w = {a=a, b=b}
table.insert(walls, w)
table.insert(poles[a].edges, w)
table.insert(poles[b].edges, w)
end
-- visuals stuff
local THICKNESS = 3
local HEIGHT = 4
-- Given a vertex with two thick parts like this:
--[[
\ /
\ /
\ \ / /
\ \/ /
\ /\ /
\ / \ /
\ / \ /
\ / \ /
\ - _ _ - /
\ _ -- _ /
\ _ - - _ /
- -
]]
-- Calculates the amount each part would need to be shortened so that they meet like this instead:
--[[
\ /
\ /
\ \ / /
\ _ - - _ /
\ _ - - _ /
\ _ - - _ /
- -
]]
local function ComputeCornerInset(v: Vertex)
assert(#v.edges == 2)
local e1 = v.edges[1]
local e2 = v.edges[2]
-- find the two vertices that aren't this one
local p1 = poles[e1.a]
if p1.id == v.id then p1 = poles[e1.b] end
local p2 = poles[e2.a]
if p2.id == v.id then p2 = poles[e2.b] end
local v1 = (p1.pos - v.pos).Unit
local v2 = (p2.pos - v.pos).Unit
local bisect = (v1 + v2).Unit
local dot = v1:Dot(bisect)
local h = THICKNESS / (2 * math.sqrt(1 - dot*dot))
local inset = h * dot
return inset, bisect
end
-- hacky nonsense so we can easily update parts every render frame,
-- normally you'd wrap all this in some kind of graph object that handles
-- its parts or do something more clever
local model = Instance.new("Model")
model.Name = "Walls"
model.Parent = workspace
local w = Instance.new("WedgePart")
w.Anchored = true
w.CanCollide = false
w.Locked = true
local wi = 1
local wedges = {}
for i = 1, 8 do wedges[i] = w:Clone() wedges[i].BrickColor = BrickColor.Random() wedges[i].Parent = model end
local p = Instance.new("Part")
p.Anchored = true
p.CanCollide = false
p.Locked = true
local pi = 1
local parts = {}
for i = 1, 4 do parts[i] = p:Clone() parts[i].BrickColor = BrickColor.Random() parts[i].Parent = model end
-- end of hacky nonsense
-- Draws a single wall, and possibly a single wedge on the end of it
local function DrawWall(wall: Edge)
local a, b = poles[wall.a], poles[wall.b]
local insetA = 0
local insetB = 0
local bisectA = nil
local bisectB = nil
if #a.edges == 2 then
insetA, bisectA = ComputeCornerInset(a)
local back = b.pos - a.pos
local right = bisectA:Cross(back)
local cf = CFrame.fromMatrix(a.pos, right, back:Cross(right), back)
local w = wedges[wi]
wi = wi % #wedges + 1
w.Size = Vector3.new(HEIGHT, THICKNESS, insetA * 2)
w.CFrame = cf
end
if #b.edges == 2 then
insetB, bisectB = ComputeCornerInset(b)
local back = a.pos - b.pos
local right = bisectB:Cross(back)
local cf = CFrame.fromMatrix(b.pos, right, back:Cross(right), back)
local w = wedges[wi]
wi = wi % #wedges + 1
w.Size = Vector3.new(HEIGHT, THICKNESS, insetB * 2)
w.CFrame = cf
end
local back = a.pos - b.pos
-- TODO pick a better up vector for the wall piece, maybe slerp between the two bisectors?
local up: Vector3 = nil
if bisectA then
up = bisectA:Cross(back)
elseif bisectB then
up = bisectB:Cross(back)
else
up = Vector3.yAxis
end
if up.Y < 0 then up = -up end
local right = up:Cross(back)
local dist = back.Magnitude
local pos = a.pos - back.Unit * (dist + insetA - insetB)/2
local cf = CFrame.fromMatrix(pos, right, up, back)
local p = parts[pi]
pi = pi % #parts + 1
p.Size = Vector3.new(THICKNESS, HEIGHT, dist-insetA-insetB)
p.CFrame = cf
end
AddPole("a", a.Position)
AddPole("b", b.Position)
AddPole("c", c.Position)
AddPole("d", d.Position)
AddEdge("a", "b")
AddEdge("c", "b")
AddEdge("c", "d")
AddEdge("d", "a")
game:GetService("RunService"):BindToRenderStep("gizdraw", 100, function()
poles["a"].pos = a.Position
poles["b"].pos = b.Position
poles["c"].pos = c.Position
poles["d"].pos = d.Position
for _, wall in pairs(walls) do
DrawWall(wall)
end
end)
Again, that wasn’t really what you asked, but I needed to write this down somewhere