Recently I’ve been playing around quite a bit with Processing.org, and one of the things that I often want to do is add a bit of noise to variables that represent positions, sizes, colours, etc. Most of the Processing functions that actually draw something (like lines, ellipses, etc) take their arguments in the form of floats
, but most floating number operations in Java/Groovy return BigDecimal
data types so we have to use casts a lot. Also, there’s no concise way to get a floating random number in a certain range that can be either negative or positive, so in order to randomly nudge a number up or down we end up with something like:
myNoisyNumber = (float) (myNumber - 10) + (random.nextFloat() * 20)
which is not exactly concise. If we want to call a Processing function that takes several such variables as arguments, then it gets worse:
// draw a slightly-random ellipse
ellipse(
(float)(x-10)+(random.nextFloat()*20),
(float)(y-10)+(random.nextFloat()*20),
(float)(size-5)+(random.nextFloat()*10),
(float)(size-5)+(random.nextFloat()*10)
)
Thinking about a Groovy solution to the problem I remembered reading about the meta-programming features that let us dynamically add methods to core Java classes at runtime. So here’s my solution – let’s modify the Integer
, Float
and BigDecimal
classes to include an extra method, jiggle()
, that will add or subtract a bit of random variation to the number and return it as a float
. Here is the magic code, that must be called at runtime (the setup()
method of a processing applet does the job very nicely):
Float.metaClass.jiggle = { def amount ->
double rand = random.nextFloat() * amount
if (random.nextBoolean()){
rand = rand * -1
}
return (Float) delegate + rand
}
Integer.metaClass.jiggle = { def amount ->
double rand = random.nextFloat() * amount
if (random.nextBoolean()){
rand = rand * -1
}
return (Float) delegate + rand
}
BigDecimal.metaClass.jiggle = { def amount ->
double rand = random.nextFloat() * amount
if (random.nextBoolean()){
rand = rand * -1
}
return (Float) delegate + rand
}
The jiggle()
method (actually it is a closure) itself is pretty straightforward. We take random number, scale it by the argument, invert it half the time, add it to the number, and cast the result to a Float
before returning it.
Two things are of interest here; firstly, we need to have a Random
object called random
in the scope where this code is run (we could get round this by creating a new Random
object inside the closure, but I find it easier for debugging to have a single Random
object for the whole sketch, making it easy to reproduce). Secondly, when we want to refer to the object to which the closure belongs (i.e. the Integer
, Float
or BigDecimal
) we use delegate
.
Having run this little snippet, our little ellipse example becomes much more concise:
ellipse(x.jiggle(10),y.jiggle(10),size.jiggle(5),size.jiggle(5))
and since we have ensured that all our new methods return Floats
, we can happily through any type of number we like into the mix and be sure that the jiggled number will come out OK. We can even make use of Groovy’s everything-is-an-object syntax to do cool stuff like:
ellipse(10.jiggle(5), 5.5.jiggle(20), 100.jiggle(3.1415), 0.7.jiggle(0.002))
Subscribe to articles from the programming category via RSS or ATOM
Comments
comments powered by Disqus