Coloring connecting walls together

I’m trying to color connecting walls to match as they get created. Basically, I have nodes on a grid, grouped together


I generate line segments between A-B

as you can see each line generates a left and right line. I want to calculate that its connecting at a node, and calculate the connecting lines, to color them accordingly. My code atm just colors the same segments together (I don’t want that)

This is what it SHOULD look like

local Nodes = workspace:WaitForChild("Nodes")
local WALL_OFFSET = 0.45
local WALL_THICKNESS = 0.05
local WALL_HEIGHT = 14

-- Global state for unique coloring
local globalColorIndex = 1

local function VecKey(vec)
	return string.format("%.3f,%.3f", vec.X, vec.Z)
end

local function CreateWallPart(startPos, endPos, sideOffset, name)
	local Direction = (endPos - startPos)
	local Length = Direction.Magnitude
	local MidPoint = (startPos + endPos) / 2
	local LookCFrame = CFrame.lookAt(MidPoint, endPos, Vector3.yAxis)
	local Offset = LookCFrame.RightVector * sideOffset

	local Part = Instance.new("Part")
	Part.Name = name
	Part.Size = Vector3.new(WALL_THICKNESS, WALL_HEIGHT, Length)
	Part.Anchored = true
	Part.CanCollide = true
	Part.TopSurface = Enum.SurfaceType.Smooth
	Part.BottomSurface = Enum.SurfaceType.Smooth
	Part.CFrame = LookCFrame + Offset

	return Part, LookCFrame.LookVector, Offset
end

local function SignedAngle2D(a: Vector3, b: Vector3): number
	local dot = a.X * b.X + a.Z * b.Z
	local det = a.X * b.Z - a.Z * b.X
	return math.deg(math.atan2(det, dot))
end

local function GetUniqueColor(index)
	local palette = {
		Color3.fromRGB(255, 0, 0), Color3.fromRGB(0, 0, 255), Color3.fromRGB(0, 255, 0),
		Color3.fromRGB(255, 255, 0), Color3.fromRGB(255, 0, 255), Color3.fromRGB(0, 255, 255),
		Color3.fromRGB(128, 0, 128), Color3.fromRGB(255, 165, 0), Color3.fromRGB(165, 42, 42),
		Color3.fromRGB(75, 0, 130)
	}
	return palette[(index - 1) % #palette + 1]
end

local Segments = {}
local NodeConnections = {}

local function AddToNodeMap(key, segment)
	NodeConnections[key] = NodeConnections[key] or {}
	table.insert(NodeConnections[key], segment)
end

local PartToColorIndex = {}
local ColorIndexToColor = {}

local function TryAssignColorToPart(part, nodeKey, basePos)
	local dir = (part.Position - basePos).Unit
	local angle = math.atan2(dir.Z, dir.X)

	local bestMatch = nil
	local bestAngleDiff = math.huge

	local connections = NodeConnections[nodeKey]
	if not connections then return end

	for _, seg in ipairs(connections) do
		local candidates = {seg.Left, seg.Right}

		for _, otherPart in ipairs(candidates) do
			if otherPart == part then continue end
			if not otherPart:IsDescendantOf(workspace) then continue end

			local otherBase = (seg.StartKey == nodeKey) and seg.StartPos or seg.EndPos
			local otherDir = (otherPart.Position - otherBase).Unit
			local otherAngle = math.atan2(otherDir.Z, otherDir.X)

			local diff = math.abs(math.atan2(math.sin(angle - otherAngle), math.cos(angle - otherAngle)))
			if diff < bestAngleDiff then
				bestAngleDiff = diff
				bestMatch = otherPart
			end
		end
	end

	if bestMatch and PartToColorIndex[bestMatch] then
		local colorIndex = PartToColorIndex[bestMatch]
		part.Color = ColorIndexToColor[colorIndex]
		PartToColorIndex[part] = colorIndex
	else
		-- New color
		local newIndex = globalColorIndex
		globalColorIndex += 1
		local color = GetUniqueColor(newIndex)
		part.Color = color
		PartToColorIndex[part] = newIndex
		ColorIndexToColor[newIndex] = color
	end
end

for _, Node in ipairs(Nodes:GetChildren()) do
	local A = Node:FindFirstChild("A")
	local B = Node:FindFirstChild("B")
	if not (A and B and A:IsA("BasePart") and B:IsA("BasePart")) then continue end

	local APos, BPos = A.Position, B.Position
	local AKey, BKey = VecKey(APos), VecKey(BPos)
	local Dir = (BPos - APos).Unit

	local WallModel = Instance.new("Model")
	WallModel.Name = Node.Name .. "_Wall"
	WallModel.Parent = Node

	local Left, _, _ = CreateWallPart(APos, BPos, -WALL_OFFSET / 2, "Left")
	local Right, _, _ = CreateWallPart(APos, BPos, WALL_OFFSET / 2, "Right")
	Left.Parent = WallModel
	Right.Parent = WallModel

	local Segment = {
		StartPos = APos,
		EndPos = BPos,
		StartKey = AKey,
		EndKey = BKey,
		Dir = Dir,
		Left = Left,
		Right = Right,
		Model = WallModel,
	}
	table.insert(Segments, Segment)
	AddToNodeMap(AKey, Segment)
	AddToNodeMap(BKey, Segment)

	TryAssignColorToPart(Left, AKey, APos)
	TryAssignColorToPart(Right, BKey, BPos)

	task.wait()
end
1 Like