Blob Blame Raw
#include <ctime>

#include "generator.h"


Generator::Generator(): Generator(random(time(NULL))) { }

Generator::Generator(ULongInt seed):
	one(1ull << 32),
	max_cache_count(8*1024*1024)
{
	set_seed(seed);
	LongIntVector2 a(1, 2);
	LongIntVector2 b(3);
	m_cache[ULongIntVector2()];
	m_cache_top = m_cache.begin();
	m_cache_top->second.next = m_cache_top->second.previous = m_cache_top;
}

void
Generator::set_seed(ULongInt x) {
	for(int i = 0; i < 2; ++i)
		for(int j = 0; j < 4; ++j)
			x = random( m_seeds[i][j] = x );
}


Real
Generator::random(const ULongIntVector2 &coord, int index) const {
	ULongInt seed = ULongInt(rand()) ^ (ULongInt(rand()) << 16) ^ (ULongInt(rand()) << 32) ^ (ULongInt(rand()) << 48);
	//ULongInt seed = random(coord.x ^ seeds_x()[index]);
	//seed = random(seed ^ coord.y ^ seeds_y()[index]);
	return Real(seed)/Real(ULLONG_MAX);
}

Vector4
Generator::generate(const ULongIntVector2 &coord, bool shrink_cache) const {
	// search value in cache
	
	Cache::iterator i = m_cache.find(coord);
	bool found = i != m_cache.end();
	if (found) {
		i->second.previous->second.next = i->second.next;
		i->second.next->second.previous = i->second.previous;
		assert(!i->second.in_use);
		if (i->second.in_use) return i->second.value;
	} else {
		i = m_cache.insert(Cache::value_type(coord, CacheEntry())).first;
		++m_cache_count;
	}
	i->second.in_use = true;
	i->second.previous = m_cache_top;
	i->second.next = i->second.previous->second.next;
	m_cache_top = i;
	i->second.previous->second.next = i;
	i->second.next->second.previous = i;

	while (shrink_cache && m_cache_count > max_cache_count) {
		Cache::iterator ri = m_cache_top->second.next;
		if (ri->second.in_use) break;
		ri->second.previous->second.next = ri->second.next;
		ri->second.next->second.previous = ri->second.previous;
		m_cache.erase(ri);
		--m_cache_count;
	}
	
	Vector4 &value = i->second.value;
	if (!found) {
		// generate new value
		
		for(int j = 0; j < 4; ++j)
			value[j] = random(coord, j);
		
		ULongInt x_minus_one = coord.x - 1;
		ULongInt y_minus_one = coord.y - 1;
		ULongInt level_x = coord.x ? (coord.x ^ x_minus_one) >> 1 : (ULongInt)-1;
		ULongInt level_y = coord.y ? (coord.y ^ y_minus_one) >> 1 : (ULongInt)-1;
		ULongInt level = (level_x < level_y ? level_x : level_y) + 1;

		if (level < one) {
			value /= (one/level);
			if (level_x == level_y) {
				// square corners
				value += ( generate(ULongIntVector2(coord.x - level, coord.y - level))
						+ generate(ULongIntVector2(coord.x - level, coord.y + level))
						+ generate(ULongIntVector2(coord.x + level, coord.y - level))
						+ generate(ULongIntVector2(coord.x + level, coord.y + level)) )*0.25;
			} else {
				// diamond corners
				value += ( generate(ULongIntVector2(coord.x - level, coord.y))
						+ generate(ULongIntVector2(coord.x + level, coord.y))
						+ generate(ULongIntVector2(coord.x, coord.y - level))
						+ generate(ULongIntVector2(coord.x, coord.y + level)) )*0.25;
			}
		}
	}
	i->second.in_use = false;
	
	return value;
}

Vector4
Generator::generate(const Vector2 &coord, const Vector2 &precision) const {
	const int bits = clz(ULongInt(0));
	
	ULongIntVector2 coord_int(
		(ULongInt)(LongInt)(coord.x*one),
		(ULongInt)(LongInt)(coord.y*one) );
	ULongIntVector2 precision_int(
		(ULongInt)(LongInt)(precision.x*one),
		(ULongInt)(LongInt)(precision.y*one) );
	int level_x = bits - clz(precision_int.x);
	int level_y = bits - clz(precision_int.y);
	coord_int.x = (coord_int.x >> level_x) << level_x;
	coord_int.y = (coord_int.y >> level_y) << level_y;
	return generate(coord_int);
}

void
Generator::generate(Surface &target, const Vector2 &lt, const Vector2 &rb) const {
	if (target.empty()) return;
	Vector2 d = rb - lt;
	d.x /= target.width();
	d.y /= target.height();
	Vector2 precision = d/2.0;
	
	Vector2 p = lt;
	for(int r = 0; r < target.height(); ++r, p.x = lt.x, p.y += d.y) {
		Color *row = target[r];
		for(int c = 0; c < target.width(); ++c, p.x += d.x) {
			Vector4 v = generate(p, precision);
			row[c].r = flip_clamp(v.x);
			row[c].g = flip_clamp(v.y);
			row[c].b = flip_clamp(v.z);
			row[c].a = flip_clamp(v.w);
		}
	}
}