-
Notifications
You must be signed in to change notification settings - Fork 0
/
Automaton.hpp
149 lines (115 loc) · 3.16 KB
/
Automaton.hpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
#pragma once
#include <vector>
namespace {
enum update_mode : int { ALL, CURSOR };
}
#include <Linear.hpp>
#include <Cellular.hpp>
#include <Probabilistic.hpp>
// grid: a macro-topology of the automaton.
// takes a function, which can read and/or write to it
template <int D, typename F> struct Grid;
template <typename F>
struct Grid<4, F> {
F func;
const int width;
const int height;
inline Grid(F &&func, int w, int h):
func(func),
width(w), height(h)
{}
struct Row {
F func;
const int y;
const int width;
inline Row(F &&func, int y, int w):
func(func), y(y), width(w)
{}
inline decltype(auto) operator[](int x) {
return func(y, x);
}
};
inline Row operator[](int y) {
return Row(std::forward<F>(func), y, width);
}
};
template <int D, typename F, typename... As>
decltype(auto) make_grid(F &&func, As... args) {
return Grid<4, F>(std::forward<F>(func), args...);
}
enum storage_mode {
// on the device, as a texture
TEXTURES,
// on the host, as a 1D buffer
HOSTBUFFER,
NO_STORAGE_MODES
};
// storage: underlying data representation of a topological representation
template <int D, storage_mode StorageMode, class T> struct Storage;
template <storage_mode StorageMode> using RenderStorage = Storage<4, StorageMode, uint8_t>;
template <typename T>
struct Storage<4, storage_mode::HOSTBUFFER, T> {
int w=0, h=0;
static constexpr int dim = 4;
using value_type = T;
std::vector<value_type> buffer;
Storage()
{}
// create 1d buffer
void init(int ww, int hh) {
w=ww,h=hh;
buffer.resize(w*h);
buffer.shrink_to_fit();
}
value_type *data() {
return buffer.data();
}
const value_type *data() const {
return buffer.data();
}
// delete data
void clear() {
buffer.clear();
}
bool empty() {
return buffer.empty();
}
~Storage()
{}
};
// ways to access the storage (differential topology)
// sometimes this is cleaner than using macro-topology
enum access_mode {
// as arectangle
bounded, looped
};
template <typename AUT, typename StorageT, access_mode AccessMode> struct Access;
template <typename AUT, typename T>
struct Access<AUT, Storage<4, storage_mode::HOSTBUFFER, T>, access_mode::bounded> {
using StorageT = Storage<4, storage_mode::HOSTBUFFER, T>;
static typename StorageT::value_type access(const StorageT &s, int i) {
return access(s, i / s.w, i % s.w);
}
static typename StorageT::value_type access(const StorageT &s, int y, int x) {
int w=s.w,h=s.h;
if(y < 0 || y >= h || x < 0 || x >= w) {
return AUT::outside_state;
}
return s.buffer[y * w + x];
}
};
template <typename AUT, typename T>
struct Access<AUT, Storage<4, storage_mode::HOSTBUFFER, T>, access_mode::looped> {
using StorageT = Storage<4, storage_mode::HOSTBUFFER, T>;
static typename StorageT::value_type access(const StorageT &s, int i) {
return access(s, i / s.w, i % s.w);
}
static typename StorageT::value_type access(const StorageT &s, int y, int x) {
int w=s.w,h=s.h;
if(y < 0 || y >= h || x < 0 || x >= w) {
y = (y < 0) ? y + h : y % h;
x = (x < 0) ? x + w : x % w;
}
return s.buffer[y * w + x];
}
};