local RunService = game:GetService("RunService")
local AssetService = game:GetService("AssetService")
local UserInputService = game:GetService("UserInputService")
local eimage = nil
eimage = AssetService:CreateEditableImage()
eimage:DrawRectangle(Vector2.zero, eimage.Size, Color3.new(1, 1, 1), 0, Enum.ImageCombineType.BlendSourceOver)
local points = 25 -- If your using the grid division optimisation, make sure to increase by the next whole sqrt
local cellCount = math.sqrt(points)
local pixelsBuffer = eimage:ReadPixelsBuffer(Vector2.zero, eimage.Size)
local n = 1
local xTile = {0, eimage.Size.X, eimage.Size.X, eimage.Size.X, 0, -eimage.Size.X, -eimage.Size.X, -eimage.Size.X}
local yTile = {eimage.Size.Y, eimage.Size.Y, 0, -eimage.Size.Y, -eimage.Size.Y, -eimage.Size.Y, 0, eimage.Size.Y}
local ImageLabelSource = workspace:WaitForChild("Canvas").SurfaceGui:GetChildren()
function map(value, fromMin, fromMax, toMin, toMax)
return (value - fromMin) / (fromMax - fromMin) * (toMax - toMin) + toMin
end
function createNoise(Px, Py)
for i = 1, eimage.Size.X * eimage.Size.Y do
local x = (i - 1) % eimage.Size.X -- Get the x-coordinate of the pixel
local y = math.floor((i - 1) / eimage.Size.X) -- Get the y-coordinate of the pixel
local distances = {}
local pixelIndex = (i - 1) * 4 -- Each pixel uses 4 bytes (RGBA)
-- Calculate distances from (x, y) to all points
for j = 1, points do
local distance = math.sqrt(math.pow((Px[j] - x), 2) + math.pow((Py[j] - y), 2))
table.insert(distances, distance)
end
-- Sort distances to find the nth smallest
table.sort(distances)
local distance = distances[n] or 0
local noise = map(distance, 0, eimage.Size.X / 2, 255, -200)
-- Write noise as pixel color
buffer.writeu8(pixelsBuffer, pixelIndex, noise) -- Red channel
buffer.writeu8(pixelsBuffer, pixelIndex + 1, noise) -- Green channel
buffer.writeu8(pixelsBuffer, pixelIndex + 2, noise) -- Blue channel
end
-- Write to the image and display it
eimage:WritePixelsBuffer(Vector2.zero, eimage.Size, pixelsBuffer)
end
function displayImage()
for i, label in pairs(ImageLabelSource) do
label.ImageContent = Content.fromObject(eimage)
end
end
function createSpots()
local x = {}
local y = {}
local xPos = 0
local yPos = 0
local xInc = 0
local yInc = 0
for yi = 1, cellCount do
yPos += (eimage.Size.X / cellCount)
for xi = 1, cellCount do
xPos += (eimage.Size.X / cellCount)
table.insert(x, math.random(xInc, xPos))
table.insert(y, math.random(yInc, yPos))
xInc += (eimage.Size.X / cellCount)
end
yInc += (eimage.Size.X / cellCount)
xPos = 0
xInc = 0
end
-- Add tiling offsets
for i = 1, 8 do
for j = 1, points do
-- Add the tile offsets to each spot
table.insert(x, x[j] + xTile[i])
table.insert(y, y[j] + yTile[i])
end
end
return x, y
end
function drawSpots(x, y)
for i = 1, points do
eimage:DrawCircle(Vector2.new(x[i], y[i]), 8, Color3.new(0, 255, 0), 0, Enum.ImageCombineType.BlendSourceOver)
end
game.Workspace:WaitForChild("Canvas"):WaitForChild("SurfaceGui"):WaitForChild("ImageLabel").ImageContent = Content.fromObject(eimage)
return
end
function drawGrid()
local x = eimage.Size.X / cellCount
local xInc = eimage.Size.X / cellCount
for i = 1, (cellCount - 1) do
-- Draw vertical lines
eimage:DrawLine(Vector2.new(x, 1), Vector2.new(x, eimage.Size.Y - 1),
Color3.new(0, 0, 0), 0, Enum.ImageCombineType.BlendSourceOver)
x += xInc
end
local y = eimage.Size.Y / cellCount
local yInc = eimage.Size.Y / cellCount
for i = 1, (cellCount - 1) do
-- Draw horizontal lines
eimage:DrawLine(Vector2.new(1, y), Vector2.new(eimage.Size.X - 1, y),
Color3.new(0, 0, 0), 0, Enum.ImageCombineType.BlendSourceOver)
y += yInc
end
end
UserInputService.InputBegan:Connect(function(input, gameProcessed)
if gameProcessed then return end -- Ignore input if it is already processed (e.g., in a GUI)
if input.KeyCode == Enum.KeyCode.F then
local x, y = createSpots()
createNoise(x, y) -- Call createCanvas when F is pressed
displayImage()
drawSpots(x, y)
drawGrid()
end
end)
Video of it in action:
Basically for createNoise(Px, Py)
, it calculates and stores the distance of each pixel to each point, which I have 25 points so it lags every time I render out a frame. I technique I saw was to split the canvas into an even grid and assign each grid square to a point.
I already managed to create that in createSpots()
but I don’t know how to actually implement the optimization, that is checking every adjacent square of the pixel you desire. Basically, for each pixel, 100% of the time the closest point will either be in the direct square or 8 of the adjacent squares surrounding the initial square you pick. That way instead of looping through 25 points, you only need to loop through 9.
How do I implement this?
Thanks