#### Howdy, Stranger!

It looks like you're new here. If you want to get involved, click one of these buttons!

# Perlin noise for plotting n coordinates?

Posts: 1,795
I want to randomly plot an arbitrary number of x,y coordinates, but I don’t want them to look all “white noise”-y, I want them to look nice and clumpy like perlin noise does.

I guess I’m basically trying to do some kind of perlinPlotter function such that:

for i = 1, n do
newRandomPosition = perlinPlotter(i)
end

Is there a way to use the perlin noise generator to do that?

P.S. everything I’ve tried to do so far with the perlin noise generator so far just returns 0, so I must be getting something radically wrong.
Tagged:

• Posts: 9,977

@UberGoober Perlin noise kind of creates random values, but those random values are the same for the same x,z position when run. So each time you use it, you get the same y value for the same x,z . Also, you’ll always get a y value of zero if the x and z values are integers. To get other than zero, you need to use fractional positions for x and z. Run the below code with step 1 so the x,z values are integers. It will print just 0 for y. Change the step to .5 . At the half x,z values you’ll get some value for y. So if you want a lot of different randomness, you need to do small values for step starting and ending at some non integer value for x,z.

Here’s an example for perlin noise.

``````viewer.mode=STANDARD

function setup()
val=2
step=1
h=craft.noise.perlin()
for x=-val,val,step do
for z=-val,val,step do
y=h:getValue(x,0,z)
print("x= "..x.." z= "..z.." y= "..y)
end
end
end
``````
• Posts: 1,795

@dave1707 I tried to apply your code to drawing an entire screen and it worked really well except for one puzzling thing.

``````function setup()

--set up initial variables
p = craft.noise.perlin()
perlinHigh, perlinLow = 0, 0
colorTable = {}

--find increments that turn WIDTH and HEIGHT into ranges 0-1
stepX = 1/WIDTH
stepY = 1/HEIGHT

--using those, get a Perlin z value for every x, y
for x = 0, 1, stepX do
for y = 0, 1, stepY do
z = p:getValue(x, y, 0)
z = math.abs(z)
table.insert(colorTable, z)
if z > perlinHigh then
perlinHigh = z
elseif z < perlinLow then
perlinLow = z
end
end
end

--turn the range of perlin values into a fraction of 255
perlinRange = perlinHigh - perlinLow
colorMultiplier = 255 / perlinRange

--use that fraction to turn each value into a color value
for i, decimal in ipairs(colorTable) do
rgbVal = decimal * colorMultiplier
colorTable[i] = color(rgbVal, rgbVal, rgbVal, rgbVal)
end

--make an image and context
perlinImage = image(WIDTH, HEIGHT)
setContext(perlinImage)

--draw each color to that image at the right place
indexCount = 0
for x = 1, WIDTH do
for y = 1, HEIGHT do
indexCount = indexCount + 1
perlinImage:set(x, y, colorTable[indexCount])
end
end
end

function draw()
background(67, 172, 236)
sprite(perlinImage, WIDTH/2, HEIGHT/2)
end

``````

It works perfectly when run on my iPhone in landscape mode, but when run in portrait mode there’s a weird slash at the lower right (see images).

• Posts: 9,977

@UberGoober Your width height sizes don’t match the color table size, so you’re off each iteration thru the for loops. Make the change below. I increased the width and height by 1.

``````    --draw each color to that image at the right place
indexCount = 0
for x = 1, WIDTH+1 do
for y = 1, HEIGHT+1 do
indexCount = indexCount + 1
perlinImage:set(x, y, colorTable[indexCount])
end
end
``````
• Posts: 9,977

@UberGoober Heres another change to allow shifting of where the perlin noise starts to give a different view. Change the values of xOffset,yOffset to get different views.

``````    --using those, get a Perlin z value for every x, y
xOffset,yOffset=10,30
for x = 0, 1, stepX do
for y = 0, 1, stepY do
z = p:getValue(x+xOffset, y+yOffset, 0)
z = math.abs(z)
table.insert(colorTable, z)
if z > perlinHigh then
perlinHigh = z
elseif z < perlinLow then
perlinLow = z
end
end
end
``````
• Posts: 1,795

@dave1707 thanks for your help. I found that your offset-by-one code throws an out of range error unless amended to:

``````    for x = 1, WIDTH+1 do
for y = 1, HEIGHT+1 do
indexCount = indexCount + 1
if colorTable[indexCount] then
perlinImage:set(x, y, colorTable[indexCount])
end
end
end

``````

But even then, I find that it only works in portrait mode. Attached is an image of the output in landscape mode. Do I have to change the code depending on the orientation? That seems illogical.

• Posts: 1,795

@dave1707 I like your offset trick. By changing the offsets to random values it becomes a lovely little cloud generator. • Posts: 9,977

@UberGoober I guess the problem is what device the code is running on because the step calculations are probably causing problems with different width and height values. Need to change the way things are calculated. Maybe instead of just a straight table of values, you need a multiple table for [x][y].

• Posts: 9,977

@UberGoober Maybe you need a for x=1,WIDTH for y=1,HEIGHT to do calculations. Then in those loops your calculations for perlin will be fractions of x+offset and y+offset and then save the value at [x][y] table position.

• edited April 7 Posts: 1,795

@dave1707 really? I mean, you’re probably right, but if the difference between it working in one direction and it working in another direction is just adding one or not, it just kind of logically seems like there must be a simpler fix than rewriting everything.

• Posts: 9,977

@UberGoober It’s not that it works in one direction and not another, your original code didn’t work right in any direction on my iPad. Here’s some code I put together real quick to give you an example of the for loops and the x,y table. You can add your code for the high and low z values and do your other calculations for the cloud.

``````viewer.mode=FULLSCREEN

function setup()
p=craft.noise.perlin()
w,h=WIDTH,HEIGHT
tab={}

xOffset,yOffset=12,15

for x=1,w do
tab[x]={}
x1=x/w
for y=1,h do
tab[x][y]={}
y1=y/h
z=p:getValue(x1+xOffset,y1+yOffset,0)
tab[x][y]=z
end
end

img=image(w,h)
setContext(img)
for x=1,w do
for y=1,h do
c=(tab[x][y]*255)//1
img:set(x,y,c,c,c,255)
end
end
setContext()
end

function draw()
background(0)
sprite(img,WIDTH/2,HEIGHT/2)
end
``````
• Posts: 1,795

Well that works, no doubt, thanks! A couple things I’m curious about: why are you dividing the X and Y by the width and height, respectively? Is that to generate decimal values? And what does the operator `//` do?

• Posts: 9,977

@UberGoober Dividing by w and h gives me a fraction between 0 and 1 for the width and height loops. Then I use that fraction and add it to the xOffset, yOffset to give me the fractional values from those values. The // is an integer divide so the color value was always an integer otherwise I got an error. The way you did your color values is ok, I was just in a hurry and didn’t try to add all of your code into this.

• Posts: 1,795

Here’s the lovely cloud generator thanks to @dave1707:

``````function setup()

--set up initial variables
p = craft.noise.perlin()
perlinHigh, perlinLow = 0, 0
perlinTable = {}
colorTable = {}

--get a Perlin z value for every x, y
xOffset,yOffset=math.random(10), math.random(30)
for x = 1, WIDTH do
perlinTable[x] = {}
x1 = x/WIDTH
for y = 1, HEIGHT do
perlinTable[x][y] = {}
y1 = y/HEIGHT
z = p:getValue(x1+xOffset, y1+yOffset, 0)
z = math.abs(z)
perlinTable[x][y] = z
if z > perlinHigh then
perlinHigh = z
elseif z < perlinLow then
perlinLow = z
end
end
end

--turn the range of perlin values into a fraction of 255
perlinRange = perlinHigh - perlinLow
colorMultiplier = 255 / perlinRange

--use that fraction to turn each value into a color value
for x, yTable in ipairs(perlinTable) do
colorTable[x] = {}
for y, decimal in ipairs(yTable) do
rgbVal = decimal * colorMultiplier
newColor = color(rgbVal, rgbVal, rgbVal, rgbVal)
colorTable[x][y] = newColor
end
end

--make an image
perlinImage = image(WIDTH, HEIGHT)

--draw each color to that image at the right place
setContext(perlinImage)
for x=1, WIDTH do
for y=1, HEIGHT do
perlinImage:set(x,y,colorTable[x][y])
end
end
setContext()

end

function draw()
background(67, 172, 236)
sprite(perlinImage, WIDTH/2, HEIGHT/2)
end

``````
• edited April 7 Posts: 2,689
@dave1707 @UberGoober - from what I remember generating these images requires images to be squares, so that there is no difference between the lengths of the axes. I think that's why ratioing the dimensions of the axes to 1 works.

If you want to make bigger areas I used to modify the tiles to match, at the borders, by either inverting them or programming to blend in at the overlaps (seamless tiles).
• edited April 7 Posts: 9,977

@UberGoober Here's a version.

``````viewer.mode=STANDARD

function setup()
parameter.integer("xVal",-20,20,0)
parameter.integer("yVal",-20,20,0)
parameter.action("cloud",cloud)
p=craft.noise.perlin()
cloud()
end

function cloud()
local tab={}
local xOffset,yOffset=xVal,yVal
local min,max=0,0
for x=1,WIDTH do
tab[x]={}
local x1=x/WIDTH
for y=1,HEIGHT do
tab[x][y]={}
local y1=y/HEIGHT
local z=p:getValue(x1+xOffset,y1+yOffset,0)
if z>max then
max=z
elseif z<min then
min=z
end
tab[x][y]=z
end
end
local col=256/(max-min)
img=image(WIDTH,HEIGHT)
setContext(img)
for x=1,WIDTH do
for y=1,HEIGHT do
local c=(tab[x][y]+math.abs(min))*col
img:set(x,y,color(c,c,c,c))
end
end
setContext()
end

function draw()
background(33, 140, 204)
sprite(img,WIDTH/2,HEIGHT/2)
end
``````
• Posts: 9,977

@UberGoober Here’s another version. This has a range parameter that will increase the cloud area from 1x1 to 10x10. If you just increase the range parameter by 1 value each time, you’ll notice that the image shrinks to the lower left corner as the range increases.

``````viewer.mode=STANDARD

function setup()
parameter.integer("xVal",-20,20,0)
parameter.integer("yVal",-20,20,0)
parameter.integer("range",1,10,1)
parameter.action("cloud",cloud)
p=craft.noise.perlin()
cloud()
end

function cloud()
local tab={}
local xOffset,yOffset=xVal,yVal
local min,max=0,0
for x=1,WIDTH do
tab[x]={}
local x1=x/(WIDTH/range)
for y=1,HEIGHT do
tab[x][y]={}
local y1=y/(HEIGHT/range)
local z=p:getValue(x1+xOffset,y1+yOffset,0)
if z>max then
max=z
elseif z<min then
min=z
end
tab[x][y]=z
end
end
local col=256/(max-min)
img=image(WIDTH,HEIGHT)
setContext(img)
for x=1,WIDTH do
for y=1,HEIGHT do
local c=(tab[x][y]+math.abs(min))*col
img:set(x,y,color(c,c,c,c))
end
end
setContext()
end

function draw()
background(33, 140, 204)
sprite(img,WIDTH/2,HEIGHT/2)
end
``````
• Posts: 1,795

@dave1707 these are great.

I noticed that even changing the X offset by one produces a dramatically different image, which surprises me, because I had assumed that gradual changes would result in gradual visual differences.

• Posts: 9,977

@UberGoober If you use my latest code and set the range to 10, when you change the x or y value by 1, you’ll see the image shift by 1/10 the image size. At a range of 1, the whole image is shifted by the whole screen size. I used to have some perlin code that covered a larger area so as you changed the x or y values, you would see the image scroll across the screen. I’m not sure where that is anymore.

• Posts: 1,795

It’s visible nice and smoothly when I use these parameters:

``````    parameter.number("xVal",-5,5,0, function()
cloud()
end)
parameter.number("yVal",-5,5,0, function()
cloud()
end)
parameter.integer("range",1,10,1, function()
cloud()
end)

``````

I wonder if the perlin calculations are fast enough to make that into an animation…

• Posts: 9,977

@UberGoober I guess it depends on the screen size. It doesn’t scroll on my iPad, I have to wait for the update each time. Maybe if my iPad was faster.

• Posts: 1,795

This version modularizes the cloud function so it can draw to any image supplied to it.

It also animates the clouds, but verrrrry slowly, because if I tried to make it go any faster it looked really choppy.

``````function setup()
parameter.number("xVal",-5,5,0)
parameter.number("yVal",-5,5,0)
parameter.integer("thisRange",1,10,3)
p=craft.noise.perlin()

imageToUse = image(WIDTH, HEIGHT)
spriteMode(CENTER)
end

function cloud(targetImg, xOffset, yOffset, range)
local tab={}
xOffset, yOffset = xOffset or 1, yOffset or 1
range = range or 1
local min,max=0,0
for x=1, targetImg.width do
tab[x]={}
local x1=x/(targetImg.width/range)
for y=1,targetImg.height do
tab[x][y]={}
local y1=y/(targetImg.height/range)
local z=p:getValue(x1+xOffset,y1+yOffset,0)
tab[x][y]=z
max = math.max(max, z)
min = math.min(min, z)
end
end
local col=256/(max-min)
setContext(targetImg)
for x=1, targetImg.width do
for y=1, targetImg.height do
local c=(tab[x][y]+math.abs(min))*col
targetImg:set(x,y,color(c,c,c,c))
end
end
setContext()
return targetImg
end

function draw()
xVal = xVal + (ElapsedTime * 0.0003)
yVal = yVal + (ElapsedTime * 0.0003)
cloud(imageToUse, xVal, yVal, thisRange)
background(33, 140, 204)
sprite(imageToUse, WIDTH * 0.65, HEIGHT * 0.65, WIDTH / 1.75, HEIGHT/1.75)
end

``````
• edited April 7 Posts: 9,977

@UberGoober That scrolls better.

PS. If I look out my windows right now, those are the kind of clouds I see.