-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathValueMap.pde
More file actions
214 lines (179 loc) · 4.8 KB
/
ValueMap.pde
File metadata and controls
214 lines (179 loc) · 4.8 KB
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
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
import java.util.Arrays;
class ValueMap
{
public int height, width;
private float maxValue;
private float[] map;
private float[] snapshot;
// The snapshot will be used for reading values, while edits will be applied to the map.
public ValueMap(PImage sourceMap, float maxValue)
{
this.maxValue = maxValue;
this.height = sourceMap.height;
this.width = sourceMap.width;
// Copy the values from the map image and convert them to floats
sourceMap.loadPixels();
this.map = new float[height*width];
ColorConverter.colorToValue(sourceMap.pixels, this.map, this.maxValue);
// This makes snapshot and map reference the same object; if makesnapshot() is never called, values will be written and read directly on the map.
snapshot = this.map;
}
public ValueMap(int width, int height, int maxValue)
{
this(createImage(width,height, RGB), maxValue);
}
public void draw(PGraphics canvas, Gradient displayGradient)
{
PImage colored = toGradientImage(displayGradient);
canvas.image(colored, 0,0);
}
public void preStep()
{
takeSnapshot();
}
public void postStep()
{
}
public void setValue(int x, int y, float value)
{
int index = width * y + x;
map[index] = value;
}
public void addValue(int index, float value)
{
float current = map[index];
map[index] = constrain(current + value, 0, maxValue);
}
public void addValue(int x, int y, float value)
{
int index = width * y + x;
addValue(index, value);
}
public float getValue(int index)
{
return snapshot[index];
}
public float getValue(int x, int y)
{
int index = width * y + x;
return getValue(index);
}
public float[] getValues()
{
return Arrays.copyOf(map, map.length);
}
public PVector getSurfaceGradient(float x, float y)
{
PVector gradient = new PVector();
int gridX = int(x);
int gridY = int(y);
if (gridX == 0 || gridX == width-1 || gridY == 0 || gridY == height-1) return gradient;
int index = gridY * width + gridX;
int indexSE = index;
float heightNW = snapshot[indexSE - width - 1];
float heightNE = snapshot[indexSE - width];
float heightSW = snapshot[indexSE - 1];
float heightSE = snapshot[indexSE];
float cellX = x - gridX;
float cellY = y - gridY;
gradient.x = -( (heightNE - heightNW) * (1 - cellY) + (heightSE - heightSW) * cellY );
gradient.y = -( (heightSW - heightNW) * (1 - cellX) + (heightSE - heightNE) * cellX );
return gradient;
}
public PImage toGradientImage(Gradient g)
{
PImage colored = createImage(width,height, RGB);
colored.loadPixels();
ColorConverter.valueToGradientImage(colored.pixels, map, maxValue, g);
colored.updatePixels();
return colored;
}
public void applyBrush(int x, int y, ValueBrush brush, boolean erase)
{
// loadPixels();
int x0 = x - brush.getRadius();
int y0 = y - brush.getRadius();
for (int iy=0; iy<brush.getSize(); iy++)
{
int yy = y0 + iy;
if (yy < 0 || yy >= height) continue;
for (int ix=0; ix<brush.getSize(); ix++)
{
int xx = x0 + ix;
if (xx < 0 || xx >= width) continue;
float brushEffect = brush.getMaskValue(ix, iy);
int sign = erase ? -1 : 1;
int i = yy * width + xx;
map[i] += brushEffect * sign;
map[i] = constrain(map[i], 0,maxValue);
}
}
// updatePixels();
}
// Makes a copy of the heightmap at a moment in time.
private void takeSnapshot()
{
if (snapshot == null) snapshot = new float[map.length];
System.arraycopy(map,0, snapshot,0, map.length);
}
}
class ValueBrush
{
private int radius;
private int size;
private float hardness;
private int value;
private float[][] brushMask;
public ValueBrush(int radius, float hardness, int value)
{
this.radius = radius;
this.size = 2 * radius + 1;
this.hardness = hardness;
this.value = value;
brushMask = new float[size][size];
recalculateMask();
}
public int getRadius() { return radius; }
public int getSize() { return size; }
public float getHardness() { return hardness; }
public int getValue() { return value; }
public void setRadius(int radius)
{
this.radius = radius;
this.size = 2 * radius + 1;
brushMask = new float[size][size];
recalculateMask();
}
public void setHardness(float hardness)
{
this.hardness = hardness;
recalculateMask();
}
public void setMultiplier(int value)
{
this.value = value;
recalculateMask();
}
public float getMaskValue(int x, int y)
{
return brushMask[y][x];
}
private void recalculateMask()
{
int sqrRadius = radius * radius;
PVector center = new PVector(radius, radius);
for (int y=0; y<size; y++)
{
for (int x=0; x<size; x++)
{
double sqrDistance = Math.pow(x - center.x, 2) + Math.pow(y - center.y, 2);
if (sqrDistance <= sqrRadius)
{
float pct = (float)sqrDistance / sqrRadius;
float brushEffect = 1 - (float)Math.pow(pct, hardness * 10);
brushMask[y][x] = value * brushEffect;
}
}
}
}
}