1 /*
2 * cGradientCount.cc
3 * Avida
4 *
5 * Copyright 2010-2011 Michigan State University. All rights reserved.
6 * http://avida.devosoft.org/
7 *
8 *
9 * This file is part of Avida.
10 *
11 * Avida is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License
12 * as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
13 *
14 * Avida is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public License along with Avida.
18 * If not, see <http://www.gnu.org/licenses/>.
19 *
20 * Authors: Aaron P Wagner <apwagner@msu.edu>
21 *
22 */
23
24 #include "cGradientCount.h"
25
26 #include "avida/core/WorldDriver.h"
27
28 #include "cAvidaContext.h"
29 #include "cPopulation.h"
30 #include "cStats.h"
31 #include "cWorld.h"
32
33 #include <cmath>
34 #include <iostream>
35
36 using namespace Avida;
37
38
39 /* cGradientCount is designed to give moving peaks of resources. Peaks are <optionally> capped with plateaus. The slope of the peaks
40 is height / distance. Consequently, when height = distance from center of peak, the value at that cell = 1. This was
41 designed this way because the organims used for this could only consume resources when the value is >= 1. Thus, height also
42 gives radius of 'edible' resources (aka the plateau). If plateaus are >1, you get sloped edges leading up to plateau
43 cylinders.
44 Spread gives the radius of the entire resource peak to the outside of the sloped edge. Organisms could detect resources
45 all along the spread, but only consume that portion on the plateau. Thus, spread - plateau = sense radius (smell) while
46 plateau = consumable radius (actual food).
47 Peaks move within the boundaries set by min/max x and y. If the plateau / edible portion of the peak hits the boundary, the peak
48 'bounces' (sign of direction of movement changes).
49 Smoothness of the movement is controlled by move_a_scaler which is the A in eq1 in Morrison & DeJong 1999. A-values
50 need to be between 1 and 4. Values of 1 to ~3 give smooth movements. Larger values should yield chaotic moves. However, beyond
51 establishing that peaks don't move when the value = 1 and do move when the value > 1, the effects of A-values have not really been
52 evaluated.
53 If depletable (via reaction) peaks stop moving when they are first bitten.
54 Depletable peaks will be refreshed when either all edible portions (>=1) are consumed or when the decay timestep (in
55 updates) is reached, whichever comes first.
56 Once bitten, depletable peaks will not move again until refreshed.
57 Peak values are refreshed to match initial height, spread, and plateau, but for non-halo peaks, the placement of the
58 refreshed peak is random within the min/max x and y area. For halo peaks, the peak is currently refreshed at the SE
59 corner of the orbit.
60 cGradientCount cannot access the random number generator at the very first update. Thus, it uses the DefaultContext initially.
61 We use movesign to determine direction of peak movement
62 First, to get smooth movements, for non-halo resources we only allow either the x or y direction change to be evaluated in
63 a single update. For halo resources, we only evaluate either the orbit or the direction in a given update.
64 Second, we then decide the change of direction based on the current direction, e.g. so that non-halo peak movesigns can't 'jump'
65 from -1 to 1, without first changing to 0
66 Finally, we only toy with movement direction when # updates since last change = updatestep.
67 */
68
cGradientCount(cWorld * world,int peakx,int peaky,int height,int spread,double plateau,int decay,int max_x,int max_y,int min_x,int min_y,double move_a_scaler,int updatestep,int worldx,int worldy,int geometry,int halo,int halo_inner_radius,int halo_width,int halo_anchor_x,int halo_anchor_y,int move_speed,double plateau_inflow,double plateau_outflow,double cone_inflow,double cone_outflow,double gradient_inflow,int is_plateau_common,double floor,int habitat,int min_size,int max_size,int config,int count,double init_plat)69 cGradientCount::cGradientCount(cWorld* world, int peakx, int peaky, int height, int spread, double plateau, int decay,
70 int max_x, int max_y, int min_x, int min_y, double move_a_scaler, int updatestep,
71 int worldx, int worldy, int geometry, int halo, int halo_inner_radius, int halo_width,
72 int halo_anchor_x, int halo_anchor_y, int move_speed,
73 double plateau_inflow, double plateau_outflow, double cone_inflow, double cone_outflow,
74 double gradient_inflow, int is_plateau_common, double floor, int habitat, int min_size,
75 int max_size, int config, int count, double init_plat)
76 : m_world(world)
77 , m_peakx(peakx), m_peaky(peaky)
78 , m_height(height), m_spread(spread), m_plateau(plateau), m_decay(decay)
79 , m_max_x(max_x), m_max_y(max_y), m_min_x(min_x), m_min_y(min_y)
80 , m_move_a_scaler(move_a_scaler), m_updatestep(updatestep)
81 , m_halo(halo), m_halo_inner_radius(halo_inner_radius), m_halo_width(halo_width)
82 , m_halo_anchor_x(halo_anchor_x), m_halo_anchor_y(halo_anchor_y), m_move_speed(move_speed)
83 , m_plateau_inflow(plateau_inflow), m_plateau_outflow(plateau_outflow), m_cone_inflow(cone_inflow), m_cone_outflow(cone_outflow)
84 , m_gradient_inflow(gradient_inflow), m_is_plateau_common(is_plateau_common), m_floor(floor)
85 , m_habitat(habitat), m_min_size(min_size), m_max_size(max_size), m_config(config), m_count(count)
86 , m_initial_plat(init_plat)
87 , m_geometry(geometry)
88 , m_initial(false)
89 , m_move_y_scaler(0.5)
90 , m_counter(0)
91 , m_move_counter(1)
92 , m_topo_counter(updatestep)
93 , m_movesignx(0)
94 , m_movesigny(0)
95 , m_old_peakx(peakx)
96 , m_old_peaky(peaky)
97 , m_just_reset(true)
98 , m_past_height(0.0)
99 , m_current_height(0.0)
100 , m_ave_plat_cell_loss(0.0)
101 , m_common_plat_height(0.0)
102 , m_skip_moves(0)
103 , m_skip_counter(0)
104 , m_mean_plat_inflow(plateau_inflow)
105 , m_var_plat_inflow(0)
106 , m_predator(false)
107 , m_pred_odds(0.0)
108 , m_guarded_juvs_per_adult(0)
109 , m_probabilistic(false)
110 , m_min_usedx(-1)
111 , m_min_usedy(-1)
112 , m_max_usedx(-1)
113 , m_max_usedy(-1)
114 {
115 ResetGradRes(m_world->GetDefaultContext(), worldx, worldy);
116 }
117
~cGradientCount()118 cGradientCount::~cGradientCount() { ; }
119
StateAll()120 void cGradientCount::StateAll()
121 {
122 return;
123 }
124
UpdateCount(cAvidaContext & ctx)125 void cGradientCount::UpdateCount(cAvidaContext& ctx)
126 {
127 m_old_peakx = m_peakx;
128 m_old_peaky = m_peaky;
129 if (m_habitat == 2) generateBarrier(m_world->GetDefaultContext());
130 else if (m_habitat == 1) generateHills(m_world->GetDefaultContext());
131 else if (m_probabilistic) UpdateProbabilisticRes();
132 else updatePeakRes(ctx);
133 }
134
updatePeakRes(cAvidaContext & ctx)135 void cGradientCount::updatePeakRes(cAvidaContext& ctx)
136 {
137 bool has_edible = false;
138
139 // determine if there is any edible food left in the peak (don't refresh the peak values until decay kicks in if there is edible food left)
140 // to speed things up, we only check cells within the possible spread of the peak
141 // and we only need to do this if decay > 1 (if decay == 1, we're going to reset everything regardless of the amount left)
142 // if decay = 1 and the resource IS depletable, that means we have a moving depleting resource! Odd, but useful.
143 if (m_decay > 1) {
144 int max_pos_x = min(m_peakx + m_spread + 1, GetX() - 1);
145 int min_pos_x = max(m_peakx - m_spread - 1, 0);
146 int max_pos_y = min(m_peaky + m_spread + 1, GetY() - 1);
147 int min_pos_y = max(m_peaky - m_spread - 1, 0);
148 for (int ii = min_pos_x; ii < max_pos_x + 1; ii++) {
149 for (int jj = min_pos_y; jj < max_pos_y + 1; jj++) {
150 if (Element(jj * GetX() + ii).GetAmount() >= 1) {
151 has_edible = true;
152 break;
153 }
154 }
155 }
156 }
157
158 // once a resource cone has been 'bitten', start the clock that counts down to when the entire peak will be
159 // refreshed (carcass rots for only so long before disappearing)
160 if (has_edible && GetModified()) m_counter++;
161
162 // only update resource values at declared update timesteps if there is resource left in the cone
163 if (has_edible && m_counter < m_decay && GetModified()) {
164 if (m_predator) UpdatePredatoryRes(ctx);
165 return;
166 }
167
168 // before we move anything, if we have a depletable resource, we need to get the current plateau cell values
169 if (m_decay == 1) getCurrentPlatValues();
170
171 // When the counter matches decay, regenerate resource peak
172 if (m_counter == m_decay) generatePeak(ctx);
173
174 // if we are working with moving peaks, calculate the y-scaler
175 if (m_move_a_scaler > 1) m_move_y_scaler = m_move_a_scaler * m_move_y_scaler * (1 - m_move_y_scaler);
176
177 // if working with moving resources, check if we are moving once per update or less frequently
178 if (m_skip_counter == m_skip_moves) moveRes(ctx);
179 else m_skip_counter++;
180
181 // to speed things up, we only check cells within the possible spread of the peak
182 // and we only do this if the resource is set to actually move, has inflow/outflow to update, or
183 // we just reset a non-moving resource
184 if (m_move_a_scaler > 1 || m_plateau_inflow != 0 || m_plateau_outflow != 0 || m_cone_inflow != 0 || m_cone_outflow != 0
185 || m_gradient_inflow != 0 || (m_move_a_scaler == 1 && m_just_reset)) fillinResourceValues();
186
187 m_counter = 0; // reset decay counter after cone resources updated
188
189 if (m_predator) UpdatePredatoryRes(ctx);
190 }
191
generatePeak(cAvidaContext & ctx)192 void cGradientCount::generatePeak(cAvidaContext& ctx)
193 {
194 // Get initial peak cell x, y coordinates and movement directions.
195 cRandom& rng = ctx.GetRandom();
196 int temp_height = 0;
197 if (m_plateau < 0) temp_height = 1;
198 else temp_height = m_height;
199 // If we are not moving the resource we default to the config input m_peakx and m_peaky for 'normal' gradient resources
200 // for non-moving halo's we generate a random location on the orbit,
201 // otherwise we get a random location and direction.
202 if (m_move_a_scaler > 1) {
203 if (!m_halo) {
204 m_peakx = rng.GetUInt(m_min_x + temp_height, m_max_x - temp_height + 1);
205 m_peaky = rng.GetUInt(m_min_y + temp_height, m_max_y - temp_height + 1);
206 // Get a random direction for movement on the x-axis
207 m_movesignx = rng.GetInt(-1,2);
208 // If x-axis movement is 0, we want to make sure y-axis movement is not also 0
209 if (m_movesignx == 0) {
210 m_movesigny = (rng.GetUInt(0,2) == 1) ? -1 : 1;
211 } else {
212 m_movesigny = rng.GetInt(-1,2);
213 }
214 } else if (m_halo) {
215 m_halo_dir = (rng.GetUInt(0,2) == 1) ? -1 : 1;
216 m_changling = (rng.GetUInt(0,2) == 1) ? -1 : 1;
217 }
218 }
219 if (m_halo) {
220 const int chooseUpDown = rng.GetUInt(0,2);
221 if (chooseUpDown == 0) {
222 int chooseEW = rng.GetUInt(0,2);
223 if (chooseEW == 0) {
224 m_peakx = rng.GetUInt(m_halo_anchor_x - m_halo_inner_radius - m_halo_width + temp_height,
225 m_halo_anchor_x - m_halo_inner_radius - temp_height + 1);
226 } else {
227 m_peakx = rng.GetUInt(m_halo_anchor_x + m_halo_inner_radius + temp_height,
228 m_halo_anchor_x + m_halo_inner_radius + m_halo_width - temp_height + 1);
229 }
230 m_peaky = rng.GetUInt(m_halo_anchor_y - m_halo_inner_radius - m_halo_width + temp_height,
231 m_halo_anchor_y + m_halo_inner_radius + m_halo_width - temp_height + 1);
232 }
233 else {
234 int chooseNS = rng.GetUInt(0,2);
235 if (chooseNS == 0) {
236 m_peaky = rng.GetUInt(m_halo_anchor_y - m_halo_inner_radius - m_halo_width + temp_height,
237 m_halo_anchor_y - m_halo_inner_radius - temp_height + 1);
238 } else {
239 m_peaky = rng.GetUInt(m_halo_anchor_y + m_halo_inner_radius + temp_height,
240 m_halo_anchor_y + m_halo_inner_radius + m_halo_width - temp_height + 1);
241 }
242 m_peakx = rng.GetUInt(m_halo_anchor_x - m_halo_inner_radius - m_halo_width + temp_height,
243 m_halo_anchor_x + m_halo_inner_radius + m_halo_width - temp_height + 1);
244 }
245 }
246
247 SetModified(false);
248 m_counter = 0;
249 m_skip_counter = 0;
250 m_just_reset = true;
251 fillinResourceValues();
252 }
253
fillinResourceValues()254 void cGradientCount::fillinResourceValues()
255 {
256 int max_pos_x;
257 int min_pos_x;
258 int max_pos_y;
259 int min_pos_y;
260 resetUsedBounds();
261
262 // if we are resetting a resource, we need to calculate new values for the whole world so we can wipe away any residue
263 if (m_just_reset) {
264 max_pos_x = GetX() - 1;
265 min_pos_x = 0;
266 max_pos_y = GetY() - 1;
267 min_pos_y = 0;
268 } else {
269 // otherwise we only need to update values within the possible range of the peak
270 // we check all the way back to move_speed to make sure we're not leaving any old residue behind
271 max_pos_x = min(m_peakx + m_spread + m_move_speed + 1, GetX() - 1);
272 min_pos_x = max(m_peakx - m_spread - m_move_speed - 1, 0);
273 max_pos_y = min(m_peaky + m_spread + m_move_speed + 1, GetY() - 1);
274 min_pos_y = max(m_peaky - m_spread - m_move_speed - 1, 0);
275 }
276
277 if (m_is_plateau_common == 1 && !m_just_reset && m_world->GetStats().GetUpdate() > 0) {
278 // with common depletion, new peak height is not the plateau heights, but the delta in plateau heights applied to
279 // peak height from the last time
280 m_current_height = m_current_height - m_ave_plat_cell_loss + m_plateau_inflow - (m_current_height * m_plateau_outflow);
281 m_common_plat_height = m_common_plat_height - m_ave_plat_cell_loss + m_plateau_inflow - (m_current_height * m_plateau_outflow);
282 if (m_common_plat_height > m_plateau && m_plateau >= 0) m_common_plat_height = m_plateau;
283 if (m_common_plat_height < 0 && m_plateau >=0) m_common_plat_height = 0;
284 if (m_current_height > m_height && m_plateau >= 0) m_current_height = m_height;
285 if (m_current_height < 0 && m_plateau >= 0) m_current_height = 0;
286 }
287 else {
288 m_current_height = m_height;
289 }
290
291 int plateau_cell = 0;
292 for (int ii = min_pos_x; ii < max_pos_x + 1; ii++) {
293 for (int jj = min_pos_y; jj < max_pos_y + 1; jj++) {
294 double thisheight = 0.0;
295 double thisdist = sqrt((double) (m_peakx - ii) * (m_peakx - ii) + (m_peaky - jj) * (m_peaky - jj));
296 if (m_spread >= thisdist) {
297 // determine theoretical individual cells values and add one to distance from center
298 // (so that center point = radius 1, not 0)
299 // also used to distinguish plateau cells
300
301 thisheight = m_current_height / (thisdist + 1);
302
303 // set the floor values
304 // plateaus will override this so that plateaus can hit 0 when being eaten
305 if (thisheight < m_floor) thisheight = m_floor;
306
307 // create cylindrical profiles of resources whereever thisheight would be >1 (area where thisdist + 1 <= m_height)
308 // and slopes outside of that range
309 // plateau = -1 turns off this option; if activated, causes 'peaks' to be flat plateaus = plateau value
310 bool is_plat_cell = ((m_height / (thisdist + 1)) >= 1);
311 // apply plateau inflow(s) and outflow
312 if ((is_plat_cell && m_plateau >= 0) || (m_plateau < 0 && thisdist == 0 && m_plateau_array.GetSize())) {
313 if (m_just_reset || m_world->GetStats().GetUpdate() <= 0) {
314 m_past_height = m_height;
315 if (m_plateau >= 0.0) {
316 thisheight = m_plateau;
317 }
318 else {
319 thisheight = m_height;
320 }
321 }
322 else {
323 if (m_is_plateau_common == 0) {
324 m_past_height = m_plateau_array[plateau_cell];
325 thisheight = m_past_height + m_plateau_inflow - (m_past_height * m_plateau_outflow);
326 thisheight += m_gradient_inflow / (thisdist + 1);
327 if (thisheight > m_plateau && m_plateau >= 0) {
328 thisheight = m_plateau;
329 }
330 if (m_plateau < 0 && thisdist == 0 && thisheight > m_height) {
331 thisheight = m_height;
332 }
333 }
334 else if (m_is_plateau_common == 1) {
335 thisheight = m_common_plat_height;
336 }
337 }
338 if (m_initial && m_initial_plat != -1) thisheight = m_initial_plat;
339 if (thisheight < 0) thisheight = 0;
340 m_plateau_array[plateau_cell] = thisheight;
341 m_plateau_cell_IDs[plateau_cell] = jj * GetX() + ii;
342 plateau_cell ++;
343 }
344 // now apply any off-plateau inflow(s) and outflow
345 else if (!is_plat_cell && (m_cone_inflow > 0 || m_cone_outflow > 0 || m_gradient_inflow > 0)) {
346 if (!m_just_reset && m_world->GetStats().GetUpdate() > 0) {
347 int offsetx = m_old_peakx - m_peakx;
348 int offsety = m_old_peaky - m_peaky;
349
350 int old_cell_x = ii + offsetx;
351 int old_cell_y = jj + offsety;
352
353 // cone cells that were previously off the world and moved onto world, start at 0
354 if ( old_cell_x < 0 || old_cell_y < 0 || (old_cell_y > (GetY() - 1)) || (old_cell_x > (GetX() - 1)) ) {
355 thisheight = 0;
356 }
357 else {
358 double past_height = Element(old_cell_y * GetX() + old_cell_x).GetAmount();
359 double newheight = past_height;
360 if (m_cone_inflow > 0 || m_cone_outflow > 0) newheight += m_cone_inflow - (past_height * m_cone_outflow);
361 if (m_gradient_inflow > 0) newheight += m_gradient_inflow / (thisdist + 1);
362 // don't exceed expected slope value
363 if (newheight < thisheight) thisheight = newheight;
364 if (thisheight < 0) thisheight = 0;
365 }
366 }
367 }
368 }
369 Element(jj * GetX() + ii).SetAmount(thisheight);
370 if (thisheight > 0) updateBounds(ii, jj);
371 }
372 }
373 SetCurrPeakX(m_peakx);
374 SetCurrPeakY(m_peaky);
375 m_just_reset = false;
376 }
377
getCurrentPlatValues()378 void cGradientCount::getCurrentPlatValues()
379 {
380 int temp_height = 0;
381 if (m_plateau < 0) temp_height = 1;
382 else temp_height = m_height;
383 int plateau_box_min_x = m_peakx - temp_height - 1;
384 int plateau_box_max_x = m_peakx + temp_height + 1;
385 int plateau_box_min_y = m_peaky - temp_height - 1;
386 int plateau_box_max_y = m_peaky + temp_height + 1;
387 int plateau_cell = 0;
388 double amount_devoured = 0.0;
389 for (int ii = plateau_box_min_x; ii < plateau_box_max_x + 1; ii++) {
390 for (int jj = plateau_box_min_y; jj < plateau_box_max_y + 1; jj++) {
391 double thisdist = sqrt((double) (m_peakx - ii) * (double) (m_peakx - ii) + (double) (m_peaky - jj) * (double) (m_peaky - jj));
392 double find_plat_dist = temp_height / (thisdist + 1);
393 if ((find_plat_dist >= 1 && m_plateau >= 0) || (m_plateau < 0 && thisdist == 0 && m_plateau_array.GetSize() > 0)) {
394 double past_cell_height = m_plateau_array[plateau_cell];
395 double pre_move_height = Element(m_plateau_cell_IDs[plateau_cell]).GetAmount();
396 if (pre_move_height < past_cell_height) {
397 m_plateau_array[plateau_cell] = pre_move_height;
398 amount_devoured = amount_devoured + past_cell_height - pre_move_height;
399 }
400 plateau_cell ++;
401 }
402 }
403 }
404 m_ave_plat_cell_loss = amount_devoured / plateau_cell;
405 }
406
moveRes(cAvidaContext & ctx)407 void cGradientCount::moveRes(cAvidaContext& ctx)
408 {
409 // for halo peaks, find current orbit. Add 1 to distance to account for the anchor grid cell
410 int current_orbit = max(abs(m_halo_anchor_x - m_peakx), abs(m_halo_anchor_y - m_peaky)) + 1;
411
412 // if we are working with moving resources and it's time to update direction
413 if (m_move_counter == m_updatestep && m_move_a_scaler > 1) {
414 m_move_counter = 1;
415 if (m_halo == 1) current_orbit = setHaloPeakMovement(ctx, current_orbit);
416 else setPeakMoveMovement(ctx);
417 }
418 else m_move_counter++;
419
420 if (m_move_a_scaler > 1) {
421 if (m_halo == 1 && m_move_a_scaler > 1) moveHaloPeak(current_orbit);
422 else movePeak();
423 }
424 m_skip_counter = 0;
425 }
426
setPeakMoveMovement(cAvidaContext & ctx)427 void cGradientCount::setPeakMoveMovement(cAvidaContext& ctx)
428 {
429 int choosesign = ctx.GetRandom().GetInt(1,3);
430 if (choosesign == 1) {
431 if (m_movesignx == -1) m_movesignx = ctx.GetRandom().GetInt(-1,1);
432 else if (m_movesignx == 1) m_movesignx = ctx.GetRandom().GetInt(0,2);
433 else m_movesignx = ctx.GetRandom().GetInt(-1,2);
434 }
435 else if (choosesign == 2){
436 if (m_movesigny == -1) m_movesigny = ctx.GetRandom().GetInt(-1,1);
437 else if (m_movesigny == 1) m_movesigny = ctx.GetRandom().GetInt(0,2);
438 else m_movesigny = ctx.GetRandom().GetInt(-1,2);
439 }
440 }
441
setHaloPeakMovement(cAvidaContext & ctx,int current_orbit)442 int cGradientCount::setHaloPeakMovement(cAvidaContext& ctx, int current_orbit)
443 {
444 // move cones by moving m_peakx & m_peaky
445 // halo resources orbit at a fixed org walking distance from an anchor point
446 // if halo width > the height of the halo resource, the resource will be bounded inside the halo but the orbit can vary within those bounds
447 // halo's are actually square in avida because, at a given orbit, this keeps a constant distance (in number of steps and org would have to take)
448 // between the anchor point and any orbit
449
450 //choose to change orbit (0) or direction (1)
451 int random_shift = ctx.GetRandom().GetUInt(0,2);
452 // if changing orbit, choose to go in or out one orbit
453 // then figure out if we need change the x or the y to shift orbit (based on what quadrant we're in)
454 int temp_height = 0;
455 if (m_plateau < 0) temp_height = 1;
456 else temp_height = m_height;
457 if (random_shift == 0) {
458 //do nothing unless there's room to change orbit
459 if (m_halo_width > (temp_height * 2)) {
460 int orbit_shift = ctx.GetRandom().GetUInt(0,2);
461 if (orbit_shift == 0) {
462 current_orbit = current_orbit - 1;
463 if (abs(m_halo_anchor_y - m_peaky) > abs(m_halo_anchor_x - m_peakx))
464 m_peaky = m_old_peaky - 1;
465 else
466 m_peakx = m_old_peakx - 1;
467 }
468 else if (orbit_shift == 1) {
469 current_orbit = current_orbit + 1;
470 if (abs(m_halo_anchor_y - m_peaky) > abs(m_halo_anchor_x - m_peakx))
471 m_peaky = m_old_peaky + 1;
472 else
473 m_peakx = m_old_peakx + 1;
474 }
475 // we have to check that we are still going to be within the halo after an orbit change
476 if (current_orbit > (m_halo_inner_radius + m_halo_width - temp_height + 2)) {
477 // if we go out of bounds, we need to go the other way instead (taking two steps back on orbit since we already took one in the wrong direction)
478 current_orbit = current_orbit - 2;
479 if (abs(m_halo_anchor_y - m_old_peaky) > abs(m_halo_anchor_x - m_old_peakx))
480 m_peaky = m_old_peaky + 1;
481 else
482 m_peakx = m_old_peakx + 1;
483 }
484 else if (current_orbit < (m_halo_inner_radius + temp_height + 2)) {
485 current_orbit = current_orbit + 2;
486 if (abs(m_halo_anchor_y - m_old_peaky) > abs(m_halo_anchor_x - m_old_peakx))
487 m_peaky = m_old_peaky - 1;
488 else
489 m_peakx = m_old_peakx - 1;
490 }
491 }
492 // if there was no room to change orbit, change the direction instead of the orbit
493 else random_shift = 1;
494 }
495 // if changing direction of rotation, we just switch sign of rotation
496 else if (random_shift == 1) {
497 m_halo_dir = m_halo_dir * -1;
498 }
499 return current_orbit;
500 }
501
moveHaloPeak(int current_orbit)502 void cGradientCount::moveHaloPeak(int current_orbit)
503 {
504 // what quadrant we are in determines whether we are changing x's or y's (= changling)
505 // if we are on a corner, we just stick with the current changling
506 if (abs(m_halo_anchor_y - m_peaky) > abs(m_halo_anchor_x - m_peakx))
507 m_changling = 1;
508 else if (abs(m_halo_anchor_y - m_peaky) < abs(m_halo_anchor_x - m_peakx))
509 m_changling = -1;
510
511 if (m_changling == 1) {
512 // check to make sure the move will not put peak beyond the bounds (at corner) of the orbit
513 // if it will go beyond the bounds of the orbit, turn the corner (e.g. if move = 5 & space to move on x =2, move 2 on x and 3 on y)
514 int next_posx = m_peakx + (m_halo_dir * m_move_speed);
515 int max_orbit_x = m_halo_anchor_x + current_orbit - 1;
516 int min_orbit_x = m_halo_anchor_x - current_orbit + 1;
517 int current_x = m_peakx;
518 if (next_posx > max_orbit_x) {
519 m_peakx = max_orbit_x;
520 if (m_peaky > m_halo_anchor_y) {
521 // turning this corner means changing the sign of the movement once we switch from moving along x to moving along y
522 m_halo_dir *= -1;
523 m_peaky = m_peaky + m_halo_dir * (m_move_speed - abs(m_peakx - current_x));
524 } else {
525 m_peaky = m_peaky + m_halo_dir * (m_move_speed - abs(m_peakx - current_x));
526 }
527 m_changling *= -1;
528 }
529 else if (next_posx < min_orbit_x) {
530 m_peakx = min_orbit_x;
531 if (m_peaky > m_halo_anchor_y) {
532 m_peaky = m_peaky + m_halo_dir * (m_move_speed - abs(m_peakx - current_x));
533 } else {
534 m_halo_dir *= -1;
535 m_peaky = m_peaky + m_halo_dir * (m_move_speed - abs(m_peakx - current_x));
536 }
537 m_changling *= -1;
538 }
539 else m_peakx = m_peakx + (m_halo_dir * m_move_speed);
540 }
541 else {
542 int next_posy = m_peaky + (m_halo_dir * m_move_speed);
543 int max_orbit_y = m_halo_anchor_y + current_orbit - 1;
544 int min_orbit_y = m_halo_anchor_y - current_orbit + 1;
545 int current_y = m_peaky;
546
547 if (next_posy > max_orbit_y) {
548 m_peaky = max_orbit_y;
549 if (m_peakx < m_halo_anchor_x) {
550 m_peakx = m_peakx + m_halo_dir * (m_move_speed - abs(m_peaky - current_y));
551 } else {
552 m_halo_dir *= -1;
553 m_peakx = m_peakx + m_halo_dir * (m_move_speed - abs(m_peaky - current_y));
554 }
555 m_changling *= -1;
556 } else if (next_posy < min_orbit_y) {
557 m_peaky = min_orbit_y;
558 if (m_peakx < m_halo_anchor_x) {
559 m_halo_dir *= -1;
560 m_peakx = m_peakx + m_halo_dir * (m_move_speed - abs(m_peaky - current_y));
561 } else {
562 m_peakx = m_peakx + m_halo_dir * (m_move_speed - abs(m_peaky - current_y));
563 }
564 m_changling *= -1;
565 } else {
566 m_peaky = m_peaky + (m_halo_dir * m_move_speed);
567 }
568 }
569 }
570
movePeak()571 void cGradientCount::movePeak()
572 {
573 // for non-halo peaks keep cones inside their bounding boxes, bouncing them if they hit the edge
574 int temp_height = 0;
575 if (m_plateau < 0) temp_height = 1;
576 else temp_height = m_height;
577
578 int temp_peakx = m_peakx + (int)(m_move_y_scaler + 0.5) * m_movesignx;
579 int temp_peaky = m_peaky + (int)(m_move_y_scaler + 0.5) * m_movesigny;
580
581 if (temp_peakx > (m_max_x - temp_height)) m_movesignx = -1;
582 if (temp_peakx < (m_min_x + temp_height + 1)) m_movesignx = 1;
583
584 if (temp_peaky > (m_max_y - temp_height)) m_movesigny = -1;
585 if (temp_peaky < (m_min_y + temp_height + 1)) m_movesigny = 1;
586
587 m_peakx = (int) (m_peakx + (m_movesignx * m_move_y_scaler) + .5);
588 m_peaky = (int) (m_peaky + (m_movesigny * m_move_y_scaler) + .5);
589 }
590
generateBarrier(cAvidaContext & ctx)591 void cGradientCount::generateBarrier(cAvidaContext& ctx)
592 // If habitat == 2 we are creating barriers to movement (walls), not really gradient resources
593 {
594 // generate/regenerate walls when counter == config updatestep
595 if (m_topo_counter == m_updatestep) {
596 resetUsedBounds();
597 // reset counter
598 m_topo_counter = 1;
599 // clear any old resource
600 for (int ii = 0; ii < GetX(); ii++) {
601 for (int jj = 0; jj < GetY(); jj++) {
602 Element(jj * GetX() + ii).SetAmount(0);
603 }
604 }
605 m_wall_cells.Resize(0);
606 // generate number barriers equal to count
607 for (int i = 0; i < m_count; i++) {
608 // drop the anchor/first block for current barrier
609 int start_randx = 0;
610 int start_randy = 0;
611 if (m_config == 3 || m_config == 4) {
612 start_randx = m_peakx;
613 start_randy = m_peaky;
614 }
615 else {
616 start_randx = ctx.GetRandom().GetUInt(0, GetX());
617 start_randy = ctx.GetRandom().GetUInt(0, GetY());
618 }
619 Element(start_randy * GetX() + start_randx).SetAmount(m_plateau);
620 if (m_plateau > 0) updateBounds(start_randx, start_randy);
621 m_wall_cells.Push(start_randy * GetX() + start_randx);
622
623 int randx = start_randx;
624 int randy = start_randy;
625 int prev_blockx = randx;
626 int prev_blocky = randy;
627 int cornerx = prev_blockx;
628 int cornery = prev_blocky;
629 bool place_corner = false;
630
631 // decide the size of the current barrier
632 int rand_block_count = ctx.GetRandom().GetUInt(m_min_size, m_max_size + 1);
633 // for vertical or horizontal wall building, pick a random direction once for the whole wall
634 int direction = ctx.GetRandom().GetUInt(0,2);
635
636 for (int num_blocks = 0; num_blocks < rand_block_count; num_blocks++) {
637 // if config == 0, build random shaped walls
638 if (m_config == 0) {
639 prev_blockx = randx;
640 prev_blocky = randy;
641 cornerx = prev_blockx;
642 cornery = prev_blocky;
643 place_corner = false;
644 // move one cell in chosen direction
645 // on diagonals, we need to place a block at chosen spot + 1 block in corner to prevent porous diagonal walls
646 if (direction == 0) {
647 randy = randy - 1;
648 }
649 else if (direction == 1) {
650 randy = randy - 1;
651 randx = randx + 1;
652 place_corner = true;
653 cornery -= 1;
654 }
655 else if (direction == 2) {
656 randx = randx + 1;
657 }
658 else if (direction == 3) {
659 randy = randy + 1;
660 randx = randx + 1;
661 place_corner = true;
662 cornerx += 1;
663 }
664 else if (direction == 4) {
665 randy = randy + 1;
666 }
667 else if (direction == 5) {
668 randy = randy + 1;
669 randx = randx - 1;
670 place_corner = true;
671 cornerx -= 1;
672 }
673 else if (direction == 6) {
674 randx = randx - 1;
675 }
676 else if (direction == 7) {
677 randy = randy - 1;
678 randx = randx - 1;
679 place_corner = true;
680 cornery += 1;
681 }
682 // choose a direction for next block with fixed 90% probability of not changing direction (~1 of 20 blocks will be in new direction)
683 if(ctx.GetRandom().GetUInt(0, 21) == 20) direction = ctx.GetRandom().GetUInt(0, 8);
684 }
685 // if config == 1, build randomly placed vertical walls
686 else if (m_config == 1) {
687 // choose up/down build direction
688 if (direction == 0) randy = randy - 1;
689 else randy = randy + 1;
690 }
691 // if config == 2, build randonly placed horizontal walls
692 else if (m_config == 2) {
693 // choose left/right build direction
694 if (direction == 0) randx = randx - 1;
695 else randx = randx + 1;
696 }
697 // if config == 3, build vertical walls from north to south
698 else if (m_config == 3) randy = randy + 1;
699 // if config == 4, build horizontal walls from west to east
700 else if (m_config == 4) randx = randx + 1;
701
702 bool count_block = true;
703 // place the new block(s) if not off edge of world
704 if (randy < GetY() && randy >= 0 && randx < GetX() && randx >= 0) {
705 // if we are trying to build across an inner_radius
706 // or for random walls, if there is already a block here
707 // don't count or place this one (continue walking across inner_radius)
708 if ((randx < (m_halo_anchor_x + m_halo_inner_radius) &&
709 randy < (m_halo_anchor_y + m_halo_inner_radius) &&
710 randx > (m_halo_anchor_x - m_halo_inner_radius) &&
711 randy > (m_halo_anchor_y - m_halo_inner_radius)) ||
712 (m_config == 0 && Element(randy * GetX() + randx).GetAmount())) {
713 num_blocks --;
714 count_block = false;
715 }
716 if (count_block) {
717 Element(randy * GetX() + randx).SetAmount(m_plateau);
718 if (m_plateau > 0) updateBounds(randx, randy);
719 m_wall_cells.Push(randy * GetX() + randx);
720 if (place_corner) {
721 if (cornery < GetY() && cornery >= 0 && cornerx < GetX() && cornerx >= 0) {
722 if ( ! ((cornerx < (m_halo_anchor_x + m_halo_inner_radius) &&
723 cornery < (m_halo_anchor_y + m_halo_inner_radius) &&
724 cornerx > (m_halo_anchor_x - m_halo_inner_radius) &&
725 cornery > (m_halo_anchor_y - m_halo_inner_radius))) ){
726 Element(cornery * GetX() + cornerx).SetAmount(m_plateau);
727 if (m_plateau > 0) updateBounds(cornerx, cornery);
728 m_wall_cells.Push(randy * GetX() + randx);
729 }
730 }
731 }
732 }
733 }
734 // if the wall is horizontal or vertical build and we went off the world edge, build from the opposite direction
735 else if (m_config == 1 || m_config == 2) {
736 randx = start_randx;
737 randy = start_randy;
738 direction = abs(direction - 1);
739 num_blocks --;
740 }
741 // if a random build and we went off the world edge, backup a block and try again
742 else if (m_config == 0) {
743 randx = prev_blockx;
744 randy = prev_blocky;
745 num_blocks --;
746 }
747 }
748 }
749 }
750 else m_topo_counter++;
751 }
752
generateHills(cAvidaContext & ctx)753 void cGradientCount::generateHills(cAvidaContext& ctx)
754 // If habitat == 1 we are creating hills which slow movement, not really gradient resources
755 {
756 // generate/regenerate hills when counter == config updatestep
757 if (m_topo_counter == m_updatestep) {
758 resetUsedBounds();
759 // reset counter
760 m_topo_counter = 1;
761 // since we are potentially plotting more than one hill per resource, we need to wipe the world before we start
762 for (int ii = 0; ii < GetX(); ii++) {
763 for (int jj = 0; jj < GetY(); jj++) {
764 Element(jj * GetX() + ii).SetAmount(0);
765 }
766 }
767
768 cRandom& rng = ctx.GetRandom();
769 // generate number hills equal to count
770 for (int i = 0; i < m_count; i++) {
771 // decide the size of the current hill
772 int rand_hill_radius = ctx.GetRandom().GetUInt(m_min_size, m_max_size + 1);
773
774 // generate random hills, if config == 0, otherwise generate 1 hill at peakx X peaky
775 if (m_config == 0) {
776 // choose the peak center for current hill, keeping the entire hill outside of any inner_radius
777 int chooseEW = rng.GetUInt(0,2);
778 if (chooseEW == 0) {
779 m_peakx = rng.GetUInt(rand_hill_radius, m_halo_anchor_x - m_halo_inner_radius - rand_hill_radius);
780 } else {
781 m_peakx = rng.GetUInt(m_halo_anchor_x + m_halo_inner_radius + rand_hill_radius, GetX() - 1 - rand_hill_radius);
782 }
783 int chooseNS = rng.GetUInt(0,2);
784 if (chooseNS == 0) {
785 m_peaky = rng.GetUInt(rand_hill_radius, m_halo_anchor_y - m_halo_inner_radius - rand_hill_radius);
786 } else {
787 m_peaky = rng.GetUInt(m_halo_anchor_y + m_halo_inner_radius + rand_hill_radius, GetY() - 1 - rand_hill_radius);
788 }
789 }
790
791 // figure the coordinate extent of each hill (box)
792 int max_pos_x = min(m_peakx + rand_hill_radius + 1, GetX() - 1);
793 int min_pos_x = max(m_peakx - rand_hill_radius - 1, 0);
794 int max_pos_y = min(m_peaky + rand_hill_radius + 1, GetY() - 1);
795 int min_pos_y = max(m_peaky - rand_hill_radius - 1, 0);
796
797 // look to place new cell values within a box around the hill center
798 for (int ii = min_pos_x; ii < max_pos_x + 1; ii++) {
799 for (int jj = min_pos_y; jj < max_pos_y + 1; jj++) {
800 double thisheight = 0.0;
801 double thisdist = sqrt((double) (m_peakx - ii) * (m_peakx - ii) + (m_peaky - jj) * (m_peaky - jj));
802 // only plot values when within set config radius & if no larger amount has already been plotted for another overlapping hill
803 if ((thisdist <= rand_hill_radius) && (Element(jj * GetX() + ii).GetAmount() < m_plateau / (thisdist + 1))) {
804 thisheight = m_plateau / (thisdist + 1);
805 Element(jj * GetX() + ii).SetAmount(thisheight);
806 if (thisheight > 0) updateBounds(ii, jj);
807 }
808 }
809 }
810 }
811 }
812 else m_topo_counter++;
813 }
814
ResetGradRes(cAvidaContext & ctx,int worldx,int worldy)815 void cGradientCount::ResetGradRes(cAvidaContext& ctx, int worldx, int worldy)
816 {
817 if ((m_move_speed >= (2 * (m_halo_inner_radius + m_halo_width))) && ((m_halo_inner_radius + m_halo_width) != 0)
818 && m_move_speed != 0) {
819 m_world->GetDriver().RaiseFatalException(-1, "Move speed greater or equal to 2*Radius");
820 }
821 if (m_halo == 1 && (m_halo_width < (2 * m_height) && m_plateau >= 0)) {
822 m_world->GetDriver().RaiseFatalException(-1, "Halo width < 2 * height (aka plateau radius)");
823 }
824 if (m_move_speed < 0) {
825 m_skip_moves = abs(m_move_speed);
826 m_move_speed = 1;
827 }
828 m_plateau_array.Resize(int(4 * m_height * m_height + 0.5));
829 m_plateau_array.SetAll(0);
830 m_plateau_cell_IDs.Resize(int(4 * m_height * m_height + 0.5));
831 m_plateau_cell_IDs.SetAll(0);
832 m_prob_res_cells.Resize(0);
833 m_wall_cells.Resize(0);
834 m_current_height = m_height;
835 m_common_plat_height = m_plateau;
836 m_mean_plat_inflow = m_plateau_inflow;
837 m_var_plat_inflow = 0;
838 resetUsedBounds();
839
840 m_initial = true;
841 ResizeClear(worldx, worldy, m_geometry);
842 if (m_habitat == 2) {
843 generateBarrier(ctx);
844 }
845 else if (m_habitat == 1) {
846 generateHills(ctx);
847 }
848 else {
849 generatePeak(ctx);
850 UpdateCount(ctx);
851 }
852 // set m_initial to false now that we have reset the resource
853 m_initial = false;
854 }
855
SetGradPlatVarInflow(double mean,double variance,int type)856 void cGradientCount::SetGradPlatVarInflow(double mean, double variance, int type)
857 {
858 if (variance > 0) {
859 m_mean_plat_inflow = mean;
860 m_var_plat_inflow = variance;
861 double the_inflow = 0;
862 if (type == 0) {
863 the_inflow = abs(m_world->GetRandom().GetRandNormal(mean, variance));
864 SetGradPlatInflow(the_inflow);
865 }
866 else if (type < 0) {
867 the_inflow = abs(m_world->GetRandom().GetRandNormal(0, variance));
868 if (mean - the_inflow < 0) the_inflow = mean;
869 SetGradPlatInflow(mean - the_inflow);
870 }
871 else if (type == 1) {
872 the_inflow = abs(m_world->GetRandom().GetRandNormal(0, variance));
873 SetGradPlatInflow(mean + the_inflow);
874 }
875 else if (type == 2) {
876 the_inflow = m_world->GetRandom().GetRandNormal(0, variance);
877 if (mean + the_inflow < 0) the_inflow = mean;
878 SetGradPlatInflow(mean + the_inflow);
879 }
880 }
881 else SetGradPlatInflow(mean);
882 }
883
UpdatePredatoryRes(cAvidaContext & ctx)884 void cGradientCount::UpdatePredatoryRes(cAvidaContext& ctx)
885 {
886 // kill off up to 1 org per update within the predator radius (plateau area), with prob of death for selected prey = m_pred_odds
887 if (m_predator) {
888 for (int i = 0; i < m_plateau_cell_IDs.GetSize(); i ++) {
889 if (Element(m_plateau_cell_IDs[i]).GetAmount() >= 1) {
890 m_world->GetPopulation().ExecutePredatoryResource(ctx, m_plateau_cell_IDs[i], m_pred_odds, m_guarded_juvs_per_adult);
891 }
892 }
893 }
894 }
895
SetPredatoryResource(double odds,int juvsper)896 void cGradientCount::SetPredatoryResource(double odds, int juvsper)
897 {
898 m_predator = true;
899 m_pred_odds = odds;
900 m_guarded_juvs_per_adult = juvsper;
901 }
902
SetProbabilisticResource(cAvidaContext & ctx,double initial,double inflow,double outflow,double lambda,double theta,int x,int y,int num_cells)903 void cGradientCount::SetProbabilisticResource(cAvidaContext& ctx, double initial, double inflow, double outflow, double lambda, double theta, int x, int y, int num_cells)
904 {
905 m_probabilistic = true;
906 m_initial_plat = initial;
907 m_plateau_inflow = inflow;
908 m_plateau_outflow = outflow;
909
910 BuildProbabilisticRes(ctx, lambda, theta, x , y, num_cells);
911 }
912
BuildProbabilisticRes(cAvidaContext & ctx,double lambda,double theta,int x,int y,int num_cells)913 void cGradientCount::BuildProbabilisticRes(cAvidaContext& ctx, double lambda, double theta, int x, int y, int num_cells)
914 {
915 if (m_min_usedx != -1) clearExistingProbRes();
916 resetUsedBounds();
917 int cells_used = 0;
918 const int worldx = GetX();
919 const int worldy = GetY();
920 int world_size = worldx * worldy;
921 int max_idx = world_size - 1;
922 int max_tries = min(1000, world_size);
923
924 tArray <int> cell_id_array;
925 cell_id_array.ResizeClear(world_size);
926 for (int i = 0; i < cell_id_array.GetSize(); i++) cell_id_array[i] = i;
927
928 if (x == -1) m_peakx = ctx.GetRandom().GetUInt(0, worldx);
929 else m_peakx = x;
930 if (y == -1) m_peaky = ctx.GetRandom().GetUInt(0, worldy);
931 else m_peaky = y;
932
933 if (num_cells != -1) m_prob_res_cells.ResizeClear(num_cells);
934
935 // only if theta == 1 do want want a 'hill' with resource for certain in the center
936 if (theta == 0) {
937 Element(m_peaky * worldx + m_peakx).SetAmount(m_initial_plat);
938 if (m_initial_plat > 0) updateBounds(m_peakx, m_peaky);
939 if (m_plateau_outflow > 0 || m_plateau_inflow > 0) {
940 if (num_cells == -1) m_prob_res_cells.Push(m_peaky * worldx + m_peakx);
941 else m_prob_res_cells[cells_used] = m_peaky * worldx + m_peakx;
942 }
943 cells_used++;
944 // no need to pop this cell off the array, just move it and don't check that far anymore
945 cell_id_array.Swap(m_peaky * worldx + m_peakx, max_idx--);
946 }
947
948 // prevent looping when num_cells not specified
949 bool loop_once = false;
950 if (num_cells == -1) {
951 loop_once = true;
952 num_cells = cells_used + 1;
953 }
954
955 int max_unused_idx = max_idx;
956 while (max_tries) { // emergency exit
957 // allow looping through multiple times until num_cells quota is filled
958 if (max_unused_idx == 0) {
959 max_tries--;
960 if (!loop_once) max_unused_idx = max_idx;
961 }
962
963 int cell_idx = m_world->GetRandom().GetUInt(max_unused_idx + 1);
964 int cell_id = cell_id_array[cell_idx];
965 int this_x = cell_id % worldx;
966 int this_y = cell_id / worldx;
967 double cell_dist = sqrt((double) (m_peakx - this_x) * (m_peakx - this_x) + (m_peaky - this_y) * (m_peaky - this_y));
968 // use a half normal
969 double this_prob = (1/lambda) * (sqrt(2 / 3.14159)) * exp(-0.5 * pow(((cell_dist - theta) / lambda), 2));
970
971 if (ctx.GetRandom().P(this_prob)) {
972 Element(cell_id).SetAmount(m_initial_plat);
973 if (m_initial_plat > 0) updateBounds(this_x, this_y);
974 if (m_plateau_outflow > 0 || m_plateau_inflow > 0) {
975 if (loop_once) m_prob_res_cells.Push(cell_id);
976 else m_prob_res_cells[cells_used] = cell_id;
977 }
978 cells_used++;
979 cell_id_array.Swap(cell_idx, max_idx--);
980 }
981 // just push this cell out of the way for this loop, but keep it around for next time
982 else {
983 Element(cell_id).SetAmount(0);
984 cell_id_array.Swap(cell_idx, max_unused_idx--);
985 }
986
987 if (cells_used >= num_cells && !loop_once) break;
988 if (max_unused_idx <= 0 && loop_once) break;
989 if (max_idx <= 0) break;
990 }
991 }
992
UpdateProbabilisticRes()993 void cGradientCount::UpdateProbabilisticRes()
994 {
995 if (m_plateau_outflow > 0 || m_plateau_inflow > 0) {
996 for (int i = 0; i < m_prob_res_cells.GetSize(); i++) {
997 double curr_val = Element(m_prob_res_cells[i]).GetAmount();
998 double amount = curr_val + m_plateau_inflow - (curr_val * m_plateau_outflow);
999 Element(m_prob_res_cells[i]).SetAmount(amount);
1000 if (amount > 0) updateBounds(m_prob_res_cells[i] % GetX(), m_prob_res_cells[i] / GetX());
1001 }
1002 }
1003 }
1004
clearExistingProbRes()1005 void cGradientCount::clearExistingProbRes()
1006 {
1007 for (int x = m_min_usedx; x < m_max_usedx + 1; x ++) {
1008 for (int y = m_min_usedy; y < m_max_usedy + 1; y ++) {
1009 Element(y * GetX() + x).SetAmount(0);
1010 }
1011 }
1012 }
1013
updateBounds(int x,int y)1014 void cGradientCount::updateBounds(int x, int y)
1015 {
1016 if (x < m_min_usedx || m_min_usedx == -1) m_min_usedx = x;
1017 if (y < m_min_usedy || m_min_usedy == -1) m_min_usedy = y;
1018 if (x > m_max_usedx || m_max_usedx == -1) m_max_usedx = x;
1019 if (y > m_max_usedy || m_max_usedy == -1) m_max_usedy = y;
1020 }
1021
resetUsedBounds()1022 void cGradientCount::resetUsedBounds()
1023 {
1024 m_min_usedx = -1;
1025 m_min_usedy = -1;
1026 m_max_usedx = -1;
1027 m_max_usedy = -1;
1028 }
1029