/* erode.c - erosion and cresting functions * * Copyright (C) 2001-2005 Patrice St-Gelais * patrstg@users.sourceforge.net * www.oricom.ca/patrice.st-gelais * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "hf.h" #include "erode.h" #ifndef MAX_CACHE #define MAX_CACHE 32768 #endif static gdouble **cache; static gboolean if_cache_init=FALSE; rain_erosion_struct *rain_erosion_struct_new (gint drops, gint threshold, gint strength, gint hardness, gboolean progressive_refresh, gint interval, gboolean old_algorithm) { rain_erosion_struct *res; res = (rain_erosion_struct *) malloc(sizeof(rain_erosion_struct)); res->drops = drops; res->threshold = threshold; res->strength = MIN(100,MAX(strength,0)); res->hardness = MIN(100,MAX(hardness,25)); res->refresh = progressive_refresh; res->interval = interval; res->old_algorithm = old_algorithm; return res; } void rain_erosion_struct_free (rain_erosion_struct *res) { if (res) free(res); } gravity_struct *gravity_struct_new (gint steps, gint slope_threshold) { gravity_struct *ges; ges = (gravity_struct *) malloc(sizeof(gravity_struct)); ges->steps = steps; ges->threshold = slope_threshold; return ges; } void gravity_struct_free (gravity_struct *ges){ if (ges) free(ges); } oriented_gravity_struct *oriented_gravity_struct_new (gint steps, gint threshold, gint direction) { oriented_gravity_struct *ogs; ogs = (oriented_gravity_struct *) malloc(sizeof(oriented_gravity_struct)); ogs->steps = steps; ogs->threshold = threshold; ogs->direction = direction; return ogs; } void oriented_gravity_struct_free (oriented_gravity_struct *ogs) { if (ogs) free(ogs); } whimsical_erosion_struct *whimsical_erosion_struct_new (gint radius, gint smooth_ribs, gboolean auto_contrast) { whimsical_erosion_struct *wes; wes = (whimsical_erosion_struct *) malloc(sizeof(whimsical_erosion_struct)); wes->radius = radius; wes->smooth_ribs = smooth_ribs; wes->auto_contrast = auto_contrast; return wes; } void whimsical_erosion_struct_free (whimsical_erosion_struct *wes){ if (wes) free(wes); } craters_erosion_struct *craters_erosion_struct_new (gint qty, gint display_interval, gint peak_threshold, gint slope_threshold) { craters_erosion_struct *ces; ces = (craters_erosion_struct *) malloc(sizeof(craters_erosion_struct)); ces->qty = qty; ces->display_interval = display_interval; ces->peak_threshold = peak_threshold; ces->slope_threshold = slope_threshold; ces->crater_str = crater_struct_new(); return ces; } void craters_erosion_struct_free (craters_erosion_struct *ces) { if (!ces) return; if (ces->crater_str) crater_struct_free(ces->crater_str); free(ces); } void hf_eroded_ribs (hf_type *hf_in, hf_type *hf_out, gint max) { // Build a "ribbed" (eroded) template by taking the minimum // absolute value of the difference between each pixel and its neighborus // Neighbours are numbered from 1 to 8, clockwise, 1 being (1,0) // The default for this version of the program is to wrap static gint z1, z2, z3, z4, z5, z6, z7, z8; hf_type value; static gint x,y, i, val; for (y=0; ymax_x); for (step=0; stepmax_y; y++) { for (x=0; xmax_x; x++) { switch (direction) { case NORTH: // For N and S, swap x & y i = VECTORIZE(x,hf->max_x-y-1,hf->max_x); iprec = VECTORIZE (x, WRAP2(hf->max_x-y,hf->max_x), hf->max_x); break; case SOUTH: i = VECTORIZE(x,y,hf->max_x); iprec = VECTORIZE ( x, WRAP2(y-1,hf->max_x), hf->max_x); break; case WEST: i = VECTORIZE(hf->max_x-x-1,y,hf->max_x); iprec = VECTORIZE ( WRAP2(hf->max_x-x,hf->max_x), y, hf->max_x); break; default: // East i = VECTORIZE(x,y,hf->max_x); iprec = VECTORIZE ( WRAP2(x-1,hf->max_x), y, hf->max_x); } v1 = *(hf->hf_buf+iprec); v2 = ((gint) *(hf->hf_buf+i)) + slope; if ( v1 > v2 ) { *(hf->hf_buf+iprec) -= (v1-v2) /2; *(hf->hf_buf+i) += (v1-v2) / 2 ; } } } } } gboolean relax_hex (hf_type *hf, gint max_x, gint max_y, gint slope_threshold, gint wrap) { // Gravity relaxation, on an hexagonally sampled buffer // Variation over cresting, more natural-like (I hope!) // Written 2005-02 for sand / wind processes // hf: buffer to transform // x,y: pixel to relax // max_x, max_y: size of the buffer (so it could work with non-square buffers!) // slope_threshold: maximum allowed slope, in absolute HF units (not degrees!) // max_steps: don't do more than this number of steps // The function can stop before if all slopes are lowver than max_slope // even, shift_right: hexagonal sampling parameters (see comments in other functions) // Returns: TRUE is relaxation done, FALSE if no relaxation to do // // 2006-11-08 Modified for randomizing the hexagonal approximation // static gint v0[6] = {0,0,0,0,0,0}; static gint x,y,v[6],z1, z2, z3, z4, z5, z6, i, ii, nx, ny, ninf, shift, sumdif, axis, even, xx, yy; long int value; gboolean test, found=FALSE; // The beginning is the same as find_all_neighbours // Then we take all the neighbour pixels inferior to the threshold // We average the difference between these pixels and the threshold // Then the current pixel is decreased and the "soil" is evenly // spread on the selected pixels even = TRUE; axis = H_AXIS; for (x=0; x0) && ((y+1)0)) { v[ninf] = ii; ninf++; sumdif += value-z4; } } if (axis==H_AXIS) { xx = x-1+shift; yy = y-1; } else { xx = x-1; yy = y-1+shift; } if (wrap) ii=VECTORIZE(WRAP2(xx,max_x), WRAP2(yy,max_x),max_x) ; else ii=VECTORIZE(MAX(xx,0), MAX(yy,0),max_x) ; z5 = *(hf+ii) ; if (z50) && ((x+shift)>0)) ) { v[ninf] = ii; ninf++; sumdif += value-z5; } } if (axis==H_AXIS) { xx = x+shift; yy = y-1; } else { xx = x-1; yy = y+shift; } if (wrap) ii=VECTORIZE(WRAP2(xx,max_x),WRAP2(yy,max_y),max_x) ; else ii=VECTORIZE(MIN(MAX(0,xx),max_x-1),MAX(yy,0),max_x) ; z6 = *(hf+ii); if (z60) && ((x+shift)max_x); for (steps=0; stepshf_buf, hf->max_x, hf->max_y, slope, hf->if_tiles)) break; } // printf("Steps: %d / %d\n",steps, max_steps); } gboolean find_all_neighbours_hex( gint x, gint y, gint *newx, gint *newy, gint max, hf_type *hf, gint threshold) { // Find all the inferior neighbours of (x,y) in hf // Randomly select one of the neighbours, // given its height difference with the minimum is < to threshold // Returns the result in newx and newy // If (x,y) is a minimum, returns FALSE // This version emulates an hexagonal symmetry (six neighbours) // We need to know if even or odd rows have been shifted (even=TRUE) // and if they have been shifted right or left // Neighbours are counted clockwise from 1 to 6, 1 being (1,0) // 2, 3, 5 and 6 are being shifted on x, depending on "even" // // 5 | 6 // 4 | 0 | 1 // 3 | 2 // // 2006.11.07: when axis is V_AXIS, assume that Y is shifted instead of X // Axis and even/ood shift are randomized, for some kind of antialiasing // This is a better replacement of the preceding simili-hexagonal sampling // static gint v[6],z1, z2, z3, z4, z5, z6, i, ii, nx, ny, ninf, ok, shift; long int value; long int lower= (long int) 0xFFFF; gint even, axis; gboolean test; ninf = 0; ok = 0; value = *(hf+VECTORIZE(x,y,max)) ; if (rand()%2) axis = V_AXIS; else axis = H_AXIS; even = rand()%2; // TRUE or FALSE if (axis==H_AXIS) test = y & (gint) 1; // test = TRUE if value is odd else test = x & (gint) 1; // test = TRUE if value is odd if ( (test && even) || ((!test) && (!even)) ) // XOR shift = 1; else shift = 0; if (axis==H_AXIS) ii=VECTORIZE(WRAP2(x+1,max),y,max); else ii=VECTORIZE(x,WRAP2(y+1,max),max); z1 = *(hf+ii); if (z1=max) || (yi>=max) ) continue; else i = VECTORIZE(xi,yi,max); } qty = ((gdouble) remainder) * matrix[m][n] * ratio; // Flow up to the point where the target value is equal to // the source value // Keep the remainder for the target center to_value = *(hf+i); if (from_value>to_value) { *(hf+i) = (hf_type) MIN(from_value,MAX(0,(gint) to_value + (gint) qty)); qtytot -= (*(hf+i) - to_value); } else qtytot -= (gint) qty; } if (qtytot>0) { // printf("SOLDE QTYTOT (%d, %d): %d; NCHUNK: %d\n",x,y,qtytot, nchunk); *(hf+VECTORIZE(tox,toy,max)) -= qtytot; } } void flow(gint x, gint y, gint tox, gint toy, hf_type *hf, gint max, gint hardness, gint strength, gboolean wrap) { // Flow some part of the H difference between (x,y) and (tox,toy) // from (x,y) to (tox,toy) // Define a 3x3 matrix - proportion of the flowing chunk to displace // static gint matrix[3][3]={1,1,1,1,4,1,1,1,1}; // static gint matrix[3][3]={0,1,0,1,2,1,0,1,0}; static gint matrix[3][3]={1,2,1,1,4,1,1,2,1}; gint i, toi, m, n, qty, xi, yi, qtytot=0; hf_type nchunk, to_value, from_value, value; gdouble ratio; // nchunk == soil (delta) to move nchunk = *(hf+VECTORIZE(x,y,max)) - *(hf+VECTORIZE(tox,toy,max)); // nchunk = nchunk / 12; nchunk = ((hf_type) ((((gfloat) nchunk) * ((gfloat) strength)) / 100.0)) >> 4; // if (x==64) // printf("NCHUNK (%d,%d): %d\n",x,y,nchunk); ratio = ((gdouble) hardness) / 100.0; // Remove soil at (x,y) for (m=0; m<3; m++) for (n=0; n<3; n++) { if (wrap) i = VECTORIZE(WRAP2(x-1+n,max),WRAP2(y-1+m,max),max); else { xi = x-1+n; yi = y-1+m; if ( (xi<0) || (yi<0) || (xi>=max) || (yi>=max) ) continue; else i = VECTORIZE(xi,yi,max); } qty = nchunk * matrix[m][n]; qtytot += qty; *(hf+i) = (hf_type) MIN(0xFFFF,MAX(0,(gint) *(hf+i) - qty)); } // Add soil at (tox,toy) from_value = *(hf+VECTORIZE(x,y,max)); // if (x==64) // printf("FROM_VALUE: %d; TO_VALUE: %d; qtytot: %d\n",from_value, *(hf+VECTORIZE(tox,toy,max)),qtytot); for (m=0; m<3; m++) for (n=0; n<3; n++) { if (wrap) i = VECTORIZE(WRAP2(tox-1+n,max),WRAP2(toy-1+m,max),max); else { xi = tox-1+n; yi = toy-1+m; if ( (xi<0) || (yi<0) || (xi>=max) || (yi>=max) ) continue; else i = VECTORIZE(xi,yi,max); } qty = nchunk * matrix[m][n]; // Flow up to the point where the target value is equal to // the source value // Keep the remainder for the target center to_value = *(hf+i); if (from_value>to_value) { *(hf+i) = (hf_type) MIN(from_value,MAX(0,(gint) to_value + qty)); qtytot -= (*(hf+i) - to_value); } else qtytot -= qty; } if (qtytot>0) { // printf("SOLDE QTYTOT (%d, %d): %d; NCHUNK: %d\n",x,y,qtytot, nchunk); value = (hf_type) (((gdouble) qtytot) * ratio); *(hf+VECTORIZE(tox,toy,max)) -= value; gflow_add_part((hf_type) qtytot - value, from_value, tox, toy, hf, max, wrap); } } void rain_erosion_hex (hf_struct_type *hf, gint drops, gint threshold, gint strength, gpointer (*display_function) (gpointer), gpointer display_data) { static gint x,y, i, ii, s, val, nextx, nexty; unsigned long int steps; } void hf_rain_erosion_hex (hf_struct_type *hf, rain_erosion_struct *res, gpointer (*display_function) (gpointer), gpointer display_data) { // Simple iterative erosion // using hexagonal sampling for a more natural looking result // We generate random drops and follow their path // We don't flow a drop when the slope is more than 2 power threshold // Neighbours are numbered from 1 to 6, clockwise, 1 being (1,0) // Not extensively tested when the HF doesn't wrap static gint x,y, i, ii, s, val, threshold, nextx, nexty, lngpath, axis, steps; hf_type *tmp; gboolean even=TRUE, odd=FALSE; // threshold is the future chunk... it must be at least 16, because the center // of the 3x3 averaging matrix == 4 and we divide the chunk by 16 before applying it // threshold = 15 + (gint) pow(2.0,(double) thresh); threshold = MAX(16,iget_absolute_slope(res->threshold,hf->max_x)); // printf("RAIN: Threshold (slope): %d = %d\n",slope_threshold,threshold); // In the 1st version, drops were given as an exponent // We could fallback to this in the future... // drops = (long int) pow(2.0,(gdouble) dr_log); if (!hf->tmp_buf) hf_backup(hf); // printf("RAIN EROSION, DROPS: %d; THRESHOLD: %d\n", drops, threshold); for (s=0; sdrops; s++) { if (s & (gint) 1) axis = H_AXIS; else axis = V_AXIS; x = rand()%hf->max_x; y = rand()%hf->max_y; // Make the (x,y) drop flow until it encounters a minimum steps = 0; // printf("********** DROP: %u; (x,y): (%d,%d)\n",s,x,y); while (find_all_neighbours_hex(x,y,&nextx,&nexty, hf->max_x,hf->hf_buf,threshold)) { if (!hf->if_tiles) if ( (x==0) || (y==0) || (x==(hf->max_x-1)) || (y==(hf->max_y-1)) ) break; flow(x,y,nextx,nexty,hf->hf_buf,hf->max_x, res->hardness, res->strength, hf->if_tiles); ii = VECTORIZE(nextx,nexty,hf->max_x); y = nexty; x = nextx; steps++; if (steps>hf->max_x) break; } if (res->refresh && res->interval) if ((!(s%res->interval)) && display_function && display_data) (*display_function) (display_data); } } gboolean find_all_neighbours_hex_old( gint x, gint y, gint *newx, gint *newy, gint max, hf_type *hf, gint threshold, gboolean even, gboolean shift_right) { // Find all the inferior neighbours of (x,y) in hf // Randomly select one of the neighbours, // given its height difference with the minimum is < to threshold // Returns the result in newx and newy // If (x,y) is a minimum, returns FALSE // This version works on an hexagonally samplec HF (six neighbours) // We need to know if even or odd rows have been shifted (even=TRUE) // and if they have been shifted right or left // Neighbours are counted clockwise from 1 to 6, 1 being (1,0) // 2, 3, 5 and 6 are being shifted on x, depending on "even" and "shift_right" // // 5 | 6 // 4 | 0 | 1 // 3 | 2 // static gint v[6],z1, z2, z3, z4, z5, z6, i, ii, nx, ny, ninf, ok, shift; long int value; long int lower= (long int) 0xFFFF; gboolean test; ninf = 0; ok = 0; value = *(hf+VECTORIZE(x,y,max)) ; test = y & (gint) 1; // test = TRUE if value is odd if ( (test && even) || ((!test) && (!even)) ) // XOR shift = 1; else shift = 0; ii=VECTORIZE(WRAP2(x+1,max),y,max); z1 = *(hf+ii); if (z1> 2; // nchunk = nchunk >> 4; // Remove soil at (x,y) for (m=0; m<3; m++) for (n=0; n<3; n++) { if (wrap) i = VECTORIZE(WRAP2(x-1+n,max),WRAP2(y-1+m,max),max); else { xi = x-1+n; yi = y-1+m; if ( (xi<0) || (yi<0) || (xi>=max) || (yi>=max) ) continue; else i = VECTORIZE(xi,yi,max); } qty = nchunk * matrix[m][n]; *(hf+i) = (hf_type) MIN(0xFFFF,MAX(0,(gint) *(hf+i) - qty)); } // Add soil at (tox,toy) for (m=0; m<3; m++) for (n=0; n<3; n++) { if (wrap) i = VECTORIZE(WRAP2(tox-1+n,max),WRAP2(toy-1+m,max),max); else { xi = tox-1+n; yi = toy-1+m; if ( (xi<0) || (yi<0) || (xi>=max) || (yi>=max) ) continue; else i = VECTORIZE(xi,yi,max); } qty = nchunk * matrix[m][n]; *(hf+i) = (hf_type) MIN(0xFFFF,MAX(0,(gint) *(hf+i) + qty)); } } void hf_rain_erosion_hex_old (hf_struct_type *hf, rain_erosion_struct *res, gpointer (*display_function) (gpointer), gpointer display_data) { // Simple iterative erosion // using hexagonal sampling for a more natural looking result // We generate random drops and follow their path // We don't flow a drop when the slope is more than 2 power thresh // Neighbours are numbered from 1 to 6, clockwise, 1 being (1,0) // Not extensively tested when the HF doesn't wrap static gint x,y, i, ii, s, val, threshold, nextx, nexty, lngpath; unsigned long int steps; hf_type *tmp; gboolean even=TRUE; // threshold is the future chunk... it must be at least 16, because the center // of the 3x3 averaging matrix == 4 and we divide the chunk by 16 before applying it // threshold = 15 + (gint) pow(2.0,(double) thresh); threshold = MAX(16,iget_absolute_slope(res->threshold,hf->max_x)); // printf("RAIN: Threshold (slope): %d = %d\n",slope_threshold,threshold); // In the 1st version, drops were given as an exponent // We could fallback to this in the future... // drops = (long int) pow(2.0,(gdouble) dr_log); if (!hf->tmp_buf) hf_backup(hf); tmp = hf->hf_buf; hf->hf_buf = hexagonal_row_sampling (hf->hf_buf, hf->max_x, hf->if_tiles, even, TRUE); free(tmp); for (s=0; sdrops; s++) { x = rand()%hf->max_x; y = rand()%hf->max_y; // Make the (x,y) drop flow until it encounters a minimum steps = 0; while (find_all_neighbours_hex_old(x,y,&nextx,&nexty, hf->max_x,hf->hf_buf,res->threshold, TRUE, TRUE)) { if (!hf->if_tiles) if ( (x==0) || (y==0) || (x==(hf->max_x-1)) || (y==(hf->max_y-1)) ) break; flow_old(x,y,nextx,nexty,hf->hf_buf,hf->max_x, hf->if_tiles); ii = VECTORIZE(nextx,nexty,hf->max_x); y = nexty; x = nextx; steps++; if (steps>hf->max_x) break; } if (res->refresh && res->interval) if ((!(s%res->interval)) && display_function && display_data) (*display_function) (display_data); } // Restore the rectangular sampled hf tmp = hf->hf_buf; hf->hf_buf = hexagonal_row_sampling (hf->hf_buf, hf->max_x, hf->if_tiles, even, FALSE); free(tmp); } void hf_crests_erosion_hex (hf_struct_type *hf, gint steps, gint slope_threshold) { // Simple iterative erosion, giving crested mountains // Hexagonally sampled version (see hf_rain_erosion_hex) 2005-02 // 2006-02 Replaced by randomized axis / even / odd flow // We find the minimum neighbour // If its value is superior to the current pixel one, // we add half the difference to the current pixel // If the minimum neighbour is inferior, we subtract half the current pixel // This is like if half the difference was "flowing" from or to the current pixel // Neighbours are numbered from 1 to 6, clockwise, 1 being (1,0) // 2004-03-11 Corrected to deal with wrapping static gint z1, z2, z3, z4, z5, z6, x, y, xx, yy; static gint i, s, val, ii, minii, lower, threshold, shift, axis; hf_type value, *tmp; gboolean test, even; gfloat ratio; // Factor for dividing the weight of the minimum pixel, when it is on the diagonals // Slope_threshold is in degrees threshold = iget_absolute_slope(slope_threshold,hf->max_x); // printf("Threshold (slope): %d = %d\n",slope_threshold,threshold); if (!hf->tmp_buf) hf_backup(hf); test = y & (gint) 1; // test = TRUE if value is odd if ( (test && even) || ((!test) && (!even)) ) // XOR shift = 1; else shift = 0; // We need a second temporary buffer for storing // the result of each step if (!hf->result_buf) { hf->result_buf = (hf_type *)malloc(hf->max_x*hf->max_y*sizeof(hf_type)); } // printf("CRESTS EROSION, STEPS: %d; THRESHOLD: %d\n", steps, threshold); memcpy(hf->result_buf, hf->tmp_buf, hf->max_x*hf->max_y*sizeof(hf_type)); for (s=0; smax_y; y++) { for (x=0; xmax_x; x++) { even = rand()%2; if (rand()%2) axis = V_AXIS; else axis = H_AXIS; i = VECTORIZE(x,y,hf->max_x); value = *(hf->hf_buf + i); if (axis==H_AXIS) { xx = x+1; yy = y; } else { xx = x; yy = y+1; } if (hf->if_tiles) ii=VECTORIZE(WRAP2(xx,hf->max_x),WRAP2(yy,hf->max_y), hf->max_x); else ii=VECTORIZE(MIN(xx,hf->max_x-1),MIN(yy,hf->max_y-1), hf->max_x); z1 = *(hf->hf_buf+ii); minii = ii; lower = z1; ratio = 1.0; if (axis==H_AXIS) { xx = x+shift; yy = y+1; } else { xx = x+1; yy = y+shift; } if (hf->if_tiles) ii=VECTORIZE(WRAP2(xx,hf->max_x), WRAP2(yy,hf->max_y),hf->max_x) ; else ii=VECTORIZE(MIN(xx,hf->max_x-1), MIN(yy,hf->max_y-1),hf->max_x) ; z2 = *(hf->hf_buf+ii); if (z2if_tiles) ii =VECTORIZE( WRAP2(xx,hf->max_x), WRAP2(yy,hf->max_y),hf->max_x) ; else ii = VECTORIZE(MIN(MAX(xx,0),hf->max_x-1),MAX(0,yy),hf->max_x) ; z3 = *(hf->hf_buf+ii) ; if (z3if_tiles) ii=VECTORIZE(WRAP2(xx,hf->max_x), WRAP2(yy,hf->max_x) , hf->max_x) ; else ii=VECTORIZE(MAX(xx,0), MAX(yy,0), hf->max_x) ; z4 = *(hf->hf_buf+ii); if (z4if_tiles) ii=VECTORIZE(WRAP2(xx,hf->max_x), WRAP2(yy,hf->max_x),hf->max_x) ; else ii=VECTORIZE(MAX(xx,0), MAX(yy,0) ,hf->max_x) ; z5 = *(hf->hf_buf+ii) ; if (z5if_tiles) ii=VECTORIZE(WRAP2(xx,hf->max_x),WRAP2(yy,hf->max_y),hf->max_x) ; else ii=VECTORIZE(MIN(MAX(0,xx),hf->max_x-1),MAX(yy,0),hf->max_x) ; z6 = *(hf->hf_buf+ii); if (z6hf_buf+minii)) - (gint) value ) / 2); if (threshold < ABS(val)) { *(hf->result_buf+i) = value + (gint) ( ((gfloat) val) / ratio); // *(hf->hf_buf+minii) -= (gint) ( ((gfloat) val) / ratio); } else *(hf->result_buf+i) = value; } } memcpy(hf->hf_buf, hf->result_buf, hf->max_x*hf->max_y*sizeof(hf_type)); } // printf("MAX: %d; MIN: %d; DELTA: %d\n",hf->max, hf->min, hf->max-hf->min); } gboolean dfind_all_neighbours( gint x, gint y, gint *newx, gint *newy, gint max, gdouble *hf, gdouble threshold) { // Find all the inferior neighbours of (x,y) in hf // Randomly select one of the neighbours, // given its height difference with the minimum is < to threshold // Returns the result in newx and newy // If (x,y) is a minimum, returns FALSE static gint v[8], i, ii, nx, ny, ninf, ok; gdouble value,z1, z2, z3, z4, z5, z6, z7, z8; gdouble lower= (gdouble) MAX_HF_VALUE; ninf = 0; ok = 0; value = *(hf+VECTORIZE(x,y,max)) ; ii=VECTORIZE(WRAP2(x+1,max),y,max); z1 = *(hf+ii); if (z1 LOWER: %5.2f; THRESHOLD: %5.2f; VALUE: %5.2f\n", x, y, lower, threshold, value); if ( (lower+threshold) < value) { // Find all the pixels not farther away than "threshold" than the lower one for (i=0; i0.0) { count ++; sum += *(water+i); } } printf("WATER PIX > 0: %d; SUM: %5.2f; AVRG: %5.2f\n",count, sum, sum / (gdouble) count); } void check_hf (gdouble *hf, gint max) { gint i, countneg=0, countoverflow=0; gdouble sumo=0.0, sumn=0.0; for (i=0; i(gdouble)MAX_HF_VALUE) { countoverflow ++; sumo += *(hf+i) - (gdouble) MAX_HF_VALUE; } } printf("HF < 1: %d; AVRG: %7.2f HF > MAX: %d; AVRG: %7.2f\n",countneg, sumn / (gdouble) countneg, countoverflow, sumo / (gdouble) countoverflow); } void hf_water_erosion (hf_struct_type *hf, gint steps, gint slope_threshold) { // Water / fluvial erosion // Simple model without speed and direction matrix // Derivated from the rain erosion // Basically, we add drops in a water matrix // We never spread water explicitly // Howvever, we spread soil with a gaussian matrix which size depends // on the water weight // Buffers: // input, output: height field converted to gdouble, 0 - 0xFFFF -> 0 - 1.0 // water = water mass; gdouble threshold, w, diff; gdouble *water,*input, *output; gint i,s,x,y, nextx, nexty; gboolean even=TRUE; gdouble vpow2[15] = {1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 2.0, 2.0, 2.0, 2.0, 4.0, 4.0 ,8.0}; // for (i=0; i<15; i++) // vpow2[i] /= (gdouble) MAX_HF_VALUE; if (!if_cache_init) { // printf("@@@@@@@@@@@@@@@@ Initializing CACHE @@@@@@@@@@@@@@@@\n"); cache = (gdouble **) malloc(MAX_CACHE*sizeof(gdouble *)); for (i=0; imax_x); input = alloc_double_hf(hf->max_x); water = calloc_double_hf(hf->max_x); hf_type_to_double (hf->hf_buf, hf->max_x, hf->max_y, input); // For each step, for each pixel: // 1. Add rain drops to the current water mass // Choice at random // water mass = power of 2, from 0 to 3), small chunks more frequent // 2. Flow the water mass with a simple rule, like we do with rain // Modify the water matrix // Add / subtract the soil from input to output for (s=0; smax_x, hf->max_y, vpow2, 15); for (y=0; ymax_y; y++) { for (x=0; xmax_x; x++) { i = VECTORIZE(x,y,hf->max_x); if (dfind_all_neighbours(x,y,&nextx,&nexty, hf->max_x,input,threshold)) { if (!hf->if_tiles) if ( (x==0) || (y==0) || (x==(hf->max_x-1)) || (y==(hf->max_y-1)) ) break; // printf("Flowing from (%d, %d) to (%d, %d)\n",x,y,nextx,nexty); dflow_map(x,y,nextx,nexty,input,water,hf->max_x, hf->if_tiles); } } } for (i=0; imax_x*hf->max_y; i++) *(input+i) = *(water+i) + *(input+i); // check_water (water, hf->max_x); // check_hf (input, hf->max_x); } double_clamp (hf->hf_buf, hf->max_x, hf->max_y, 0, MAX_HF_VALUE, water); // double_clamp (hf->hf_buf, hf->max_x, hf->max_y, 0, MAX_HF_VALUE, input); free(input); free(water); }