OPENRNDR & Processing - Loops, shades and randomness

Create a grid of circle pairs. In each position in the grid there is a larger circle and maybe (50% chance) a smaller circle. The larger circle has a shade of pink (either brighter or darker). The smaller circle fill color is picked randomly from a set of 4 colors. That set contains white twice to make it more frequent.

Processing / Java

``````import java.util.Arrays;
int seed = 1111;
int PINK = #FFC0CB;
int WHITE = #FFFFFF;
int BLACK = #000000;
ArrayList<Integer> colors = new ArrayList<Integer>(Arrays.asList(
PINK, WHITE, WHITE, color(PINK, 128)));

void setup() {
size(900, 450);
}

void draw() {
randomSeed(seed);
background(WHITE);
for (int x=0; x<=15; x++) {
for (int y=0; y<=5; y++) {
PVector pos = new PVector(
map(x, 0, 15, 100, width - 100),
map(y, 0, 5, 100, height - 100)
);
stroke(PINK, 102);
float amount = noise(pos.x * 0.007, pos.y * 0.004) * 3 - 0.7;
fill(amount > 1 ? lerpColor(PINK, WHITE, amount-1) :
lerpColor(PINK, BLACK, 1-amount));
circle(pos.x, pos.y, 40);
if (random(1) < 0.5) {
fill(colors.get((int)random(colors.size())));
circle(pos.x, pos.y, 20);
}
}
}
}
``````

OPENRNDR / Kotlin

Imports
``````// imports are added automatically by the IDE
import org.openrndr.application
import org.openrndr.color.ColorRGBa
import org.openrndr.extra.noise.Random
import org.openrndr.math.Vector2
import org.openrndr.math.map
``````
``````fun main() = application {
configure {
width = 900
height = 450
}
program {
val colors = listOf(ColorRGBa.PINK, ColorRGBa.WHITE,
extend {
Random.resetState()
drawer.background(ColorRGBa.WHITE)
for (x in 0..15) {
for (y in 0..5) {
val pos = Vector2(
map(0.0, 15.0, 100.0, width - 100.0, x * 1.0),
map(0.0, 5.0, 100.0, height - 100.0, y * 1.0)
)
drawer.stroke = ColorRGBa.PINK.opacify(0.4)
val amount = Random.simplex(pos.x * 0.004, pos.y * 0.003) + 0.5
drawer.circle(pos, 20.0)
if (Random.bool()) {
drawer.fill = Random.pick(colors)
drawer.circle(pos, 10.0)
}
}
}
}
}
}

``````
Concept Processing OPENRNDR
flip a coin `random(1) < 0.5` `Random.bool()`
pick a random item from a list `colors.get( (int)random(colors.size()))` `Random.pick(colors)`
reset the random number generator `randomSeed(value); //noiseSeed(value);` `Random.resetState()` or `Random.seed = "capybara"`
get a random noise value `noise(x, y)` `Random.perlin(x, y)` or `Random.simplex(x, y)` or `Random.value(x, y)` and even more
count from 0 to 15 `for(int x=0; x<=15; x++)`
`for(int x=0; x<16; x++)`
.
`for(x in 0..15)`
`for(x in 0 until 16)`
`(0..15).foreach`
get a translucent variation of a color `fill(PINK, alpha)` `ColorRGBa.PINK.opacify(0.4)`
get brighter or darker variation of a color `if(amount > 1) lerpColorPINK, WHITE, amount-1) else lerpColor(BLACK, PINK, amount)` `ColorRGBa.PINK.shade(amount)`

Notes:

Different noise values are used to achieve similar color shades in both programs. Noise functions and their arguments produce different aesthetics, so you just need to play with the values to get what you want.

By resetting the random seed I avoid a flickering animation, because I obtain the same sequence of random numbers on each frame. But if you want it to flicker remove the `randomSeed()` / `Random.resetState()`

Share your questions and comments below | Find other OPENRNDR & Processing posts

2 Likes

Looking good! `Random.resetState()` should be called automatically by the `init` block, were you having a different behavior?

Resetting it avoids animation. I didnâ€™t want to get new values on every frame. Iâ€™ll mention that.

There was a discussion in slack about which one of these to use:

• A. `for(x in 0 until 10) { ... }`
• B. `for(x in 0..9) { ... }`
• C. `(0..9).forEach { ... }`

Case A, not including the top value (`x < 10` in Java), is more common than including it (case B, `x <= 10` in Java). The reason I chose B is because I find it less confusing when mapping the looped variable.

For example, if I wanted to map the previous ranges to the range (100.0, 400.0), I would use

• `map(0.0, 9.0, 100.0, 400.0, x * 1.0)`

In case A, we have 10 in the loop but 9.0 in the map (because we count from 0 up to - but not including - 10). In cases B and C we have 9 both in the loop and in the map, which I think helps avoid errors.

Note: `forEach` can also be used to loop but, unlike `for` and `while`, it does not provide `break` nor `continue`.

Hi Abe,

Iâ€™ve been playing a little with your above example. The loop idiom you use takes a little time to understand coming from Processing but that is just a matter of getting accustomed to it. I have however a few questions:

• Regarding the noise functions, Iâ€™m not getting the same behaviour between the two.
• Iâ€™ve added a little harmonic motion, but again Iâ€™m not getting the same motion.
• Is there a rectMode in OPENRNDR? I havenâ€™t found one and Iâ€™m wondering about translations such as rotations for rectanglesâ€¦

Hereâ€™s my P5 code

``````float xGridMax = 100.0;
float yGridMax = 15.0;

void setup() {
size(800, 460);
}

void draw() {

background(255);
noStroke();
for (int x=0; x<=xGridMax; x++) {
for (int y=0; y<=yGridMax; y++) {
PVector pos = new PVector(
map(x, 0, xGridMax, 100, width - 100),
map(y, 0, yGridMax, 100, height - 100)
);

float amount = noise(pos.x * 0.015, pos.y * 0.015);
float alpha = map(amount, 0, 1, 0, 255);
fill(0, 0, 255, alpha);
float rectH = map(sin((pos.y + pos.x) + frameCount*0.075), -1.0, 1.0, -yGridMax, yGridMax);
rect(pos.x, pos.y, 5, rectH);
}
}
}
``````

and hereâ€™s my OPENRNDR

``````import org.openrndr.application
import org.openrndr.color.ColorRGBa
import org.openrndr.extra.noise.Random
import org.openrndr.math.Vector2
import org.openrndr.math.map

fun main() = application {
configure {
width = 800
height = 460
}
program {

var xGridMax = 100.0
var yGridMax = 15.0

extend {
drawer.background(ColorRGBa.WHITE)
drawer.stroke = null

for (x in 0..xGridMax.toInt()) {
for (y in 0..yGridMax.toInt()) {
val pos = Vector2(
map(0.0, xGridMax, 100.0, width - 100.0, x * 1.0),
map(0.0, yGridMax, 100.0, height - 100.0, y * 1.0)
)
val amount = Random.perlin(pos.x * 0.015, pos.y * 0.015)
val alpha = map(0.0, 1.0, 0.0, 255.0, amount)
drawer.fill = ColorRGBa(0.0, 0.0, 255.0, alpha)

val rectH = map(-1.0,1.0,-yGridMax, yGridMax,
Math.sin((pos.y + pos.x) + frameCount*0.075))
drawer.rectangle(pos,5.0, rectH)

}
}
}
}
}
``````

PS: Sorry for the formatting or rather lack of it.

Hi Mark,

• To format your block of code you can add â€ścode fencesâ€ť: thatâ€™s an empty line with three backticks ````` before and after your code (I did that for you). Another option is to use the `</>` button above the editor, but I like it less because it indents everything and apparently it doesnâ€™t do syntax highlighting.
• Colors in OPENRNDR use normalized values. So use values between 0.0 and 1.0 for R, G, B and A.
• Perlin noise returns values between -1.0 and 1.0 in OPENRNDR, so adjust your `map` accordingly.

After changing these I get the same look in both. With one difference though: in Processing it runs at 60 FPS and in OPENRNDR at about 30 FPS in this laptop.

OPENRNDR provides `drawer.rectangles()` which is more efficient and does run at 60 FPS for this program. BUT you canâ€™t set the color of each rectangle manually afaik. Maybe @edwin can provide some suggestions here. The way I would colorize those rectangles to achieve the same look is with a shade style, but thatâ€™s not trivial unless you know about GLSL shaders already. In a nutshell, what the shade style would do is tell all pixels to have a certain alpha based on their position in the screen.

Hi Abe,

thank you for your kind reply and solutions. All makes sense now. From reading a little about your move from Processing to OPENRNDR in another post, you mention shaders and although Iâ€™ve only dabbled in a few with P5, I get the impression that its not the best environment for working with them. I imagine that OPENRNDR is far more capable and perhaps better designed for working with and incorporating GLSL? Having looked at OF and Cinder, I saw a substantial use of shaders and it is something Iâ€™d like to have more time to learn. Perhaps my learning of OPENRNDR could be orientated towards that? Any suggestions?

Best

Mark

Hi, I found it ok to work with shaders in Processing. Only that you canâ€™t use the Processing IDE to edit them. I made a tool to make it a bit easier.

Then OPENRNDR is the only environment I know with shade styles, which let you add shader effects with one line of code.

What you can do with shaders is more or less the same in all environments. Ok, some may let you add your own attributes to vertices, some provide Compute shadersâ€¦ but the basics are the same.

I think I should really record and put my shader workshop online Would you be interested?

Would love to read up more on working with shaders in OPENRNDR.