Skip to content

Commit

Permalink
fix rand_gauss() offset
Browse files Browse the repository at this point in the history
The previous random_gauss() had a very prominent bias since
the introduction of the new integer RNG. Fixed by converting
the algorithm to float and removing the integer RNG completely.
rand_gauss() was the only consumer of integer random numbers.

https://gna.org/bugs/?19879
  • Loading branch information
martinxyz committed Aug 8, 2012
1 parent 012f7f2 commit 9687c91
Show file tree
Hide file tree
Showing 6 changed files with 21 additions and 205 deletions.
20 changes: 7 additions & 13 deletions helpers.c
Original file line number Diff line number Diff line change
Expand Up @@ -23,20 +23,14 @@

#include "helpers.h"

// Optimized version from one in GIMP (noisify.c), where it was
// adapted from ppmforge.c, which is part of PBMPLUS. The algorithm
// comes from: 'The Science Of Fractal Images'. Peitgen, H.-O., and
// Saupe, D. eds. Springer Verlag, New York, 1988.
float rand_gauss (RngInt * rng)
float rand_gauss (RngDouble * rng)
{
float sum = 0.0;
uint32_t rand1 = rng_int_next(rng);
uint32_t rand2 = rng_int_next(rng);
sum += rand1 & 0x7FFF;
sum += (rand1 >> 16) & 0x7FFF;
sum += rand2 & 0x7FFF;
sum += (rand2 >> 16) & 0x7FFF;
return sum * 5.28596089837e-5 - 3.46410161514;
double sum = 0.0;
sum += rng_double_next(rng);
sum += rng_double_next(rng);
sum += rng_double_next(rng);
sum += rng_double_next(rng);
return sum * 1.73205080757 - 3.46410161514;
}

// stolen from GIMP (gimpcolorspace.c)
Expand Down
4 changes: 2 additions & 2 deletions helpers.h
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#ifndef HELPERS_H
#define HELPERS_H

#include "rng-int.h"
#include "rng-double.h"

#define MAX(a, b) (((a) > (b)) ? (a) : (b))
#define MIN(a, b) (((a) < (b)) ? (a) : (b))
Expand All @@ -23,6 +23,6 @@ hsv_to_rgb_float (float *h_, float *s_, float *v_);
void
rgb_to_hsv_float (float *r_ /*h*/, float *g_ /*s*/, float *b_ /*v*/);

float rand_gauss (RngInt * rng);
float rand_gauss (RngDouble * rng);

#endif // HELPERS_H
30 changes: 12 additions & 18 deletions mypaint-brush.c
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@
#include "mapping.h"
#include "helpers.h"
#include "rng-double.h"
#include "rng-int.h"

// Allow the C99 define from json.h
#undef TRUE
Expand Down Expand Up @@ -62,8 +61,7 @@ struct _MyPaintBrush {

// the states (get_state, set_state, reset) that change during a stroke
float states[MYPAINT_BRUSH_STATES_COUNT];
RngInt * rng_int;
RngDouble * rng_double;
RngDouble * rng;

// Those mappings describe how to calculate the current value for each setting.
// Most of settings will be constant (eg. only their base_value is used).
Expand Down Expand Up @@ -97,8 +95,7 @@ mypaint_brush_new()
for (i=0; i<MYPAINT_BRUSH_SETTINGS_COUNT; i++) {
self->settings[i] = mapping_new(MYPAINT_BRUSH_INPUTS_COUNT);
}
self->rng_int = rng_int_new(1000);
self->rng_double = rng_double_new(1000);
self->rng = rng_double_new(1000);
self->print_inputs = FALSE;

for (i=0; i<MYPAINT_BRUSH_STATES_COUNT; i++) {
Expand All @@ -122,10 +119,8 @@ mypaint_brush_destroy(MyPaintBrush *self)
for (i=0; i<MYPAINT_BRUSH_SETTINGS_COUNT; i++) {
mapping_free(self->settings[i]);
}
rng_int_free (self->rng_int);
rng_double_free (self->rng_double);
self->rng_int = NULL;
self->rng_double = NULL;
rng_double_free (self->rng);
self->rng = NULL;
json_object_put(self->brush_json);
}

Expand Down Expand Up @@ -339,7 +334,7 @@ mypaint_brush_set_state(MyPaintBrush *self, MyPaintBrushState i, float value)
inputs[MYPAINT_BRUSH_INPUT_PRESSURE] = pressure;
inputs[MYPAINT_BRUSH_INPUT_SPEED1] = log(self->speed_mapping_gamma[0] + self->states[MYPAINT_BRUSH_STATE_NORM_SPEED1_SLOW])*self->speed_mapping_m[0] + self->speed_mapping_q[0];
inputs[MYPAINT_BRUSH_INPUT_SPEED2] = log(self->speed_mapping_gamma[1] + self->states[MYPAINT_BRUSH_STATE_NORM_SPEED2_SLOW])*self->speed_mapping_m[1] + self->speed_mapping_q[1];
inputs[MYPAINT_BRUSH_INPUT_RANDOM] = rng_double_next(self->rng_double);
inputs[MYPAINT_BRUSH_INPUT_RANDOM] = rng_double_next(self->rng);
inputs[MYPAINT_BRUSH_INPUT_STROKE] = MIN(self->states[MYPAINT_BRUSH_STATE_STROKE], 1.0);
inputs[MYPAINT_BRUSH_INPUT_DIRECTION] = fmodf (atan2f (self->states[MYPAINT_BRUSH_STATE_DIRECTION_DY], self->states[MYPAINT_BRUSH_STATE_DIRECTION_DX])/(2*M_PI)*360 + 180.0, 180.0);
inputs[MYPAINT_BRUSH_INPUT_TILT_DECLINATION] = self->states[MYPAINT_BRUSH_STATE_DECLINATION];
Expand Down Expand Up @@ -496,8 +491,8 @@ mypaint_brush_set_state(MyPaintBrush *self, MyPaintBrushState i, float value)
if (self->settings_value[MYPAINT_BRUSH_SETTING_OFFSET_BY_RANDOM]) {
float amp = self->settings_value[MYPAINT_BRUSH_SETTING_OFFSET_BY_RANDOM];
if (amp < 0.0) amp = 0.0;
x += rand_gauss (self->rng_int) * amp * base_radius;
y += rand_gauss (self->rng_int) * amp * base_radius;
x += rand_gauss (self->rng) * amp * base_radius;
y += rand_gauss (self->rng) * amp * base_radius;
}


Expand All @@ -506,7 +501,7 @@ mypaint_brush_set_state(MyPaintBrush *self, MyPaintBrushState i, float value)
float radius_log, alpha_correction;
// go back to logarithmic radius to add the noise
radius_log = self->settings_value[MYPAINT_BRUSH_SETTING_RADIUS_LOGARITHMIC];
radius_log += rand_gauss (self->rng_int) * self->settings_value[MYPAINT_BRUSH_SETTING_RADIUS_BY_RANDOM];
radius_log += rand_gauss (self->rng) * self->settings_value[MYPAINT_BRUSH_SETTING_RADIUS_BY_RANDOM];
radius = expf(radius_log);
radius = CLAMP(radius, ACTUAL_RADIUS_MIN, ACTUAL_RADIUS_MAX);
alpha_correction = self->states[MYPAINT_BRUSH_STATE_ACTUAL_RADIUS] / radius;
Expand Down Expand Up @@ -749,8 +744,7 @@ mypaint_brush_set_state(MyPaintBrush *self, MyPaintBrushState i, float value)
dtime = 0.0001;
}

rng_int_set_seed (self->rng_int, self->states[MYPAINT_BRUSH_STATE_RNG_SEED]);
rng_double_set_seed (self->rng_double, self->states[MYPAINT_BRUSH_STATE_RNG_SEED]);
rng_double_set_seed (self->rng, self->states[MYPAINT_BRUSH_STATE_RNG_SEED]*0x40000000);

{ // calculate the actual "virtual" cursor position

Expand All @@ -759,8 +753,8 @@ mypaint_brush_set_state(MyPaintBrush *self, MyPaintBrushState i, float value)
// OPTIMIZE: expf() called too often
float base_radius = expf(mapping_get_base_value(self->settings[MYPAINT_BRUSH_SETTING_RADIUS_LOGARITHMIC]));

x += rand_gauss (self->rng_int) * mapping_get_base_value(self->settings[MYPAINT_BRUSH_SETTING_TRACKING_NOISE]) * base_radius;
y += rand_gauss (self->rng_int) * mapping_get_base_value(self->settings[MYPAINT_BRUSH_SETTING_TRACKING_NOISE]) * base_radius;
x += rand_gauss (self->rng) * mapping_get_base_value(self->settings[MYPAINT_BRUSH_SETTING_TRACKING_NOISE]) * base_radius;
y += rand_gauss (self->rng) * mapping_get_base_value(self->settings[MYPAINT_BRUSH_SETTING_TRACKING_NOISE]) * base_radius;
}

float fac = 1.0 - exp_decay (mapping_get_base_value(self->settings[MYPAINT_BRUSH_SETTING_SLOW_TRACKING]), 100.0*dtime);
Expand Down Expand Up @@ -872,7 +866,7 @@ mypaint_brush_set_state(MyPaintBrush *self, MyPaintBrushState i, float value)
//g_print("dist_final = %f\n", states[MYPAINT_BRUSH_STATE_DIST]);

// next seed for the RNG (GRand has no get_state() and states[] must always contain our full state)
self->states[MYPAINT_BRUSH_STATE_RNG_SEED] = rng_int_next(self->rng_int);
self->states[MYPAINT_BRUSH_STATE_RNG_SEED] = rng_double_next(self->rng);

// stroke separation logic (for undo/redo)

Expand Down
126 changes: 0 additions & 126 deletions rng-int.c

This file was deleted.

19 changes: 0 additions & 19 deletions rng-int.h

This file was deleted.

27 changes: 0 additions & 27 deletions tests/test-rng.c
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@

#include "rng-int.h"
#include "rng-double.h"

#include <stdio.h>
Expand All @@ -8,30 +6,6 @@

#include "testutils.h"

int
test_rng_int_smoke(void *user_data)
{
register int m; long a[2009];

RngInt* gen = rng_int_new(310952L);
for (m=0;m<=2009;m++) rng_int_get_array(gen, a,1009);
printf("%ld\n", a[0]);

assert(a[0] == 995235265);

rng_int_free(gen);

gen = rng_int_new(310952L);
for (m=0;m<=1009;m++) rng_int_get_array(gen, a,2009);
printf("%ld\n", a[0]);

assert(a[0] == 995235265);

rng_int_free(gen);

return 1;
}

int
test_rng_double_smoke(void *user_data)
{
Expand Down Expand Up @@ -67,7 +41,6 @@ int
main(int argc, char **argv)
{
TestCase test_cases[] = {
{"/rng/int/smoke", test_rng_int_smoke, NULL},
{"/rng/double/smoke", test_rng_double_smoke, NULL}
};

Expand Down

0 comments on commit 9687c91

Please sign in to comment.