protected void Init( int dimension_cells, int dimension_cells_vertical, int cellSize_mm, int localisationRadius_mm, int maxMappingRange_mm, float vacancyWeighting) { cell = new particleGridCellBase[dimension_cells][][]; for (int x = 0; x < dimension_cells; x++) cell[x] = new particleGridCellBase[dimension_cells][]; localisation_test = new bool[dimension_cells][]; for (int x = 0; x < dimension_cells; x++) localisation_test[x] = new bool[dimension_cells]; this.dimension_cells = dimension_cells; this.dimension_cells_vertical = dimension_cells_vertical; this.cellSize_mm = cellSize_mm; this.localisation_search_cells = localisationRadius_mm / cellSize_mm; this.max_mapping_range_cells = maxMappingRange_mm / cellSize_mm; this.vacancy_weighting = vacancyWeighting; // make a lookup table for gaussians - saves doing a lot of floating point maths gaussianLookup = stereoModel.createHalfGaussianLookup(10); // create a lookup table for log odds calculations, to avoid having // to run slow Log calculations at runtime LogOdds = probabilities.CreateLogOddsLookup(LOG_ODDS_LOOKUP_LEVELS); }
/// <summary> /// distill an individual grid particle /// </summary> /// <param name="hypothesis">the grid particle to be distilled</param> public void Distill(particleGridCell hypothesis) { int z = hypothesis.z; // create the distilled array if (distilled == null) { distilled = new particleGridCellBase[Hypothesis.Length]; } bool initialised = false; if (distilled[z] == null) { // create a new distilled particle distilled[z] = new particleGridCellBase(); distilled[z].colour = new byte[3]; initialised = true; } // update an existing distilled value distilled[z].probabilityLogOdds += hypothesis.probabilityLogOdds; // and update the distilled colour value for (int col = 2; col >= 0; col--) { if (initialised) { distilled[z].colour[col] = hypothesis.colour[col]; } else { distilled[z].colour[col] = (byte)((hypothesis.colour[col] + distilled[z].colour[col]) / 2); } } }
/// <summary> /// distill a given probability value /// </summary> /// <param name="z">z coordinate</param> /// <param name="probability">probability value in the range 0.0-1.0</param> /// <param name="r">red colour component in the range 0-255</param> /// <param name="g">green colour component in the range 0-255</param> /// <param name="b">blue colour component in the range 0-255</param> public void Distill( int z, float probability, int r, int g, int b) { // create the distilled array if (distilled == null) { distilled = new particleGridCellBase[Hypothesis.Length]; } if (distilled[z] == null) { // create a new distilled particle distilled[z] = new particleGridCellBase(); distilled[z].colour = new byte[3]; } // update an existing distilled value distilled[z].probabilityLogOdds = probabilities.LogOdds(probability); distilled[z].colour[0] = (byte)r; distilled[z].colour[1] = (byte)g; distilled[z].colour[2] = (byte)b; }
public void LoadTile(byte[] data) { // read the bounding box int array_index = 0; const int int32_bytes = 4; int tx = BitConverter.ToInt32(data, 0); int ty = BitConverter.ToInt32(data, int32_bytes); int bx = BitConverter.ToInt32(data, int32_bytes * 2); int by = BitConverter.ToInt32(data, int32_bytes * 3); array_index = int32_bytes * 4; // dimensions of the box int w1 = bx - tx; int w2 = by - ty; //Read binary index as a byte array int no_of_bits = w1 * w2; int no_of_bytes = no_of_bits / 8; if (no_of_bytes * 8 < no_of_bits) no_of_bytes++; byte[] indexData = new byte[no_of_bytes]; for (int i = 0; i < no_of_bytes; i++) indexData[i] = data[array_index + i]; bool[] binary_index = ArrayConversions.ToBooleanArray(indexData); array_index += no_of_bytes; int n = 0; int occupied_cells = 0; for (int y = ty; y < by; y++) { for (int x = tx; x < bx; x++) { if (binary_index[n]) { cell[x][y] = new occupancygridCellMultiHypothesis(dimension_cells_vertical); occupied_cells++; } n++; } } if (occupied_cells > 0) { const int float_bytes = 4; // read occupancy values no_of_bytes = occupied_cells * dimension_cells_vertical * float_bytes; byte[] occupancyData = new byte[no_of_bytes]; for (int i = 0; i < no_of_bytes; i++) occupancyData[i] = data[array_index + i]; array_index += no_of_bytes; float[] occupancy = ArrayConversions.ToFloatArray(occupancyData); // read colour values no_of_bytes = occupied_cells * dimension_cells_vertical * 3; byte[] colourData = new byte[no_of_bytes]; for (int i = 0; i < no_of_bytes; i++) colourData[i] = data[array_index + i]; array_index += no_of_bytes; // insert the data into the grid n = 0; for (int y = ty; y < by; y++) { for (int x = tx; x < bx; x++) { if (cell[x][y] != null) { particleGridCellBase[] distilled = new particleGridCellBase[dimension_cells_vertical]; for (int z = 0; z < dimension_cells_vertical; z++) { // set the probability value int index = (n * dimension_cells_vertical) + z; float probLogOdds = occupancy[index]; if (probLogOdds != occupancygridCellMultiHypothesis.NO_OCCUPANCY_EVIDENCE) { // create a distilled grid particle distilled[z] = new particleGridCellBase(); distilled[z].probabilityLogOdds = probLogOdds; // set the colour distilled[z].colour = new byte[3]; for (int col = 0; col < 3; col++) distilled[z].colour[col] = colourData[(index * 3) + col]; } } // insert the distilled particles into the grid cell cell[x][y].SetDistilledValues(distilled); // update the navigable space updateNavigableSpace(null, x, y); n++; } } } } }
/// <summary> /// sets distilled probability values /// This is used when loading a grid from file /// </summary> /// <param name="distilled"></param> public void SetDistilledValues(particleGridCellBase[] distilled) { this.distilled = distilled; }
/// <summary> /// distill an individual grid particle /// </summary> /// <param name="hypothesis">the grid particle to be distilled</param> public void Distill(particleGridCell hypothesis) { int z = hypothesis.z; // create the distilled array if (distilled == null) distilled = new particleGridCellBase[Hypothesis.Length]; bool initialised = false; if (distilled[z] == null) { // create a new distilled particle distilled[z] = new particleGridCellBase(); distilled[z].colour = new byte[3]; initialised = true; } // update an existing distilled value distilled[z].probabilityLogOdds += hypothesis.probabilityLogOdds; // and update the distilled colour value for (int col = 2; col >= 0; col--) { if (initialised) distilled[z].colour[col] = hypothesis.colour[col]; else distilled[z].colour[col] = (byte)((hypothesis.colour[col] + distilled[z].colour[col]) / 2); } }
/// <summary> /// distill a given probability value /// </summary> /// <param name="z">z coordinate</param> /// <param name="probability">probability value in the range 0.0-1.0</param> /// <param name="r">red colour component in the range 0-255</param> /// <param name="g">green colour component in the range 0-255</param> /// <param name="b">blue colour component in the range 0-255</param> public void Distill( int z, float probability, int r, int g, int b) { // create the distilled array if (distilled == null) distilled = new particleGridCellBase[Hypothesis.Length]; if (distilled[z] == null) { // create a new distilled particle distilled[z] = new particleGridCellBase(); distilled[z].colour = new byte[3]; } // update an existing distilled value distilled[z].probabilityLogOdds = probabilities.LogOdds(probability); distilled[z].colour[0] = (byte)r; distilled[z].colour[1] = (byte)g; distilled[z].colour[2] = (byte)b; }
/// <summary> /// inserts the given ray into the grid /// There are three components to the sensor model used here: /// two areas determining the probably vacant area and one for /// the probably occupied space /// </summary> /// <param name="ray">ray object to be inserted into the grid</param> /// <param name="sensormodel_lookup">the sensor model to be used</param> /// <param name="left_camera_location">location and pose of the left camera</param> /// <param name="right_camera_location">location and pose of the right camera</param> /// <param name="localiseOnly">if true does not add any mapping particles (pure localisation)</param> /// <returns>matching probability, expressed as log odds</returns> public float Insert( evidenceRay ray, rayModelLookup sensormodel_lookup, pos3D left_camera_location, pos3D right_camera_location, bool localiseOnly) { // some constants to aid readability const int OCCUPIED_SENSORMODEL = 0; const int VACANT_SENSORMODEL_LEFT_CAMERA = 1; const int VACANT_SENSORMODEL_RIGHT_CAMERA = 2; const int X_AXIS = 0; const int Y_AXIS = 1; float inverse_cellSize_mm = 1.0f / cellSize_mm; // which disparity index in the lookup table to use // we multiply by 2 because the lookup is in half pixel steps int sensormodel_index = (int)Math.Round(ray.disparity * 2); // the initial models are blank, so just default to the one disparity pixel model bool small_disparity_value = false; if (sensormodel_index < 2) { sensormodel_index = 2; small_disparity_value = true; } // beyond a certain disparity the ray model for occupied space // is always only going to be only a single grid cell if (sensormodel_index >= sensormodel_lookup.probability.GetLength(0)) sensormodel_index = sensormodel_lookup.probability.GetLength(0) - 1; float xdist_mm=0, ydist_mm=0, zdist_mm=0, xx_mm=0, yy_mm=0, zz_mm=0; float occupied_dx = 0, occupied_dy = 0, occupied_dz = 0; //float intersect_x = 0, intersect_y = 0, intersect_z = 0; float centre_prob = 0, prob = 0, prob_localisation = 0; // probability values at the centre axis and outside float matchingScore = NO_OCCUPANCY_EVIDENCE; // total localisation matching score int rayWidth = 0; // widest point in the ray in cells int widest_point; // widest point index int step_size = 1; // ray width at the fattest point in cells rayWidth = (int)Math.Round(ray.width / (cellSize_mm * 2)); // calculate the centre position of the grid in millimetres int half_grid_width_mm = dimension_cells * cellSize_mm / 2; //int half_grid_width_vertical_mm = dimension_cells_vertical * cellSize_mm / 2; int grid_centre_x_mm = (int)(x - half_grid_width_mm); int grid_centre_y_mm = (int)(y - half_grid_width_mm); int grid_centre_z_mm = (int)z; int max_dimension_cells = dimension_cells - rayWidth; // in turbo mode only use a single vacancy ray int max_modelcomponent = VACANT_SENSORMODEL_RIGHT_CAMERA; if (TurboMode) max_modelcomponent = VACANT_SENSORMODEL_LEFT_CAMERA; if (localiseOnly) max_modelcomponent = OCCUPIED_SENSORMODEL; float[][] sensormodel_lookup_probability = sensormodel_lookup.probability; // consider each of the three parts of the sensor model for (int modelcomponent = OCCUPIED_SENSORMODEL; modelcomponent <= max_modelcomponent; modelcomponent++) { // the range from the cameras from which insertion of data begins in grid cells // for vacancy rays this will be zero, but will be non-zero for the occupancy area int starting_range_cells = 0; switch (modelcomponent) { case OCCUPIED_SENSORMODEL: { // distance between the beginning and end of the probably // occupied area occupied_dx = ray.vertices[1].x - ray.vertices[0].x; occupied_dy = ray.vertices[1].y - ray.vertices[0].y; occupied_dz = ray.vertices[1].z - ray.vertices[0].z; //intersect_x = ray.vertices[0].x + (occupied_dx * ray.fattestPoint); //intersect_y = ray.vertices[0].y + (occupied_dy * ray.fattestPoint); //intersect_z = ray.vertices[0].z + (occupied_dz * ray.fattestPoint); xdist_mm = occupied_dx; ydist_mm = occupied_dy; zdist_mm = occupied_dz; // begin insertion at the beginning of the // probably occupied area xx_mm = ray.vertices[0].x; yy_mm = ray.vertices[0].y; zz_mm = ray.vertices[0].z; break; } case VACANT_SENSORMODEL_LEFT_CAMERA: { // distance between the left camera and the left side of // the probably occupied area of the sensor model xdist_mm = ray.vertices[0].x - left_camera_location.x; ydist_mm = ray.vertices[0].y - left_camera_location.y; zdist_mm = ray.vertices[0].z - left_camera_location.z; // begin insertion from the left camera position xx_mm = left_camera_location.x; yy_mm = left_camera_location.y; zz_mm = left_camera_location.z; //step_size = 2; break; } case VACANT_SENSORMODEL_RIGHT_CAMERA: { // distance between the right camera and the right side of // the probably occupied area of the sensor model xdist_mm = ray.vertices[0].x - right_camera_location.x; ydist_mm = ray.vertices[0].y - right_camera_location.y; zdist_mm = ray.vertices[0].z - right_camera_location.z; // begin insertion from the right camera position xx_mm = right_camera_location.x; yy_mm = right_camera_location.y; zz_mm = right_camera_location.z; //step_size = 2; break; } } // which is the longest axis ? int longest_axis = X_AXIS; float longest = Math.Abs(xdist_mm); if (Math.Abs(ydist_mm) > longest) { // y has the longest length longest = Math.Abs(ydist_mm); longest_axis = Y_AXIS; } // ensure that the vacancy model does not overlap // the probably occupied area // This is crude and could potentially leave a gap // longest -= ray.width; int steps = (int)(longest * inverse_cellSize_mm); if (steps < 1) steps = 1; float inverse_steps = 1.0f / steps; // calculate the range from the cameras to the start of the ray in grid cells if (modelcomponent == OCCUPIED_SENSORMODEL) { if (longest_axis == Y_AXIS) starting_range_cells = (int)((ray.vertices[0].y - ray.observedFrom.y) * inverse_cellSize_mm); else starting_range_cells = (int)((ray.vertices[0].x - ray.observedFrom.x) * inverse_cellSize_mm); // always positive if (starting_range_cells < 0) starting_range_cells = -starting_range_cells; // what is the widest point of the ray in cells widest_point = (int)(ray.fattestPoint * steps); } else { // what is the widest point of the ray in cells widest_point = steps; } // calculate increment values in millimetres float x_incr_mm = xdist_mm * inverse_steps; float y_incr_mm = ydist_mm * inverse_steps; float z_incr_mm = zdist_mm * inverse_steps; // step through the ray, one grid cell at a time int grid_step = 0; while (grid_step < steps) { // is this position inside the maximum mapping range bool withinMappingRange = true; if (grid_step + starting_range_cells > max_mapping_range_cells) { withinMappingRange = false; if ((grid_step==0) && (modelcomponent == OCCUPIED_SENSORMODEL)) { grid_step = steps; modelcomponent = 9999; } } // calculate the width of the ray in cells at this point // using a diamond shape ray model int ray_wdth = 0; if (rayWidth > 0) { if (grid_step < widest_point) ray_wdth = grid_step * rayWidth / widest_point; else { if (!small_disparity_value) // most disparity values tail off to some point in the distance ray_wdth = (steps - grid_step + widest_point) * rayWidth / (steps - widest_point); else // for very small disparity values the ray model has an infinite tail // and maintains its width after the widest point ray_wdth = rayWidth; } } // localisation rays are wider, to enable a more effective matching score // which is not too narrowly focussed and brittle int ray_wdth_localisation = ray_wdth + 1; //localisation_search_cells; xx_mm += x_incr_mm*step_size; yy_mm += y_incr_mm*step_size; zz_mm += z_incr_mm*step_size; // convert the x millimetre position into a grid cell position int x_cell = (int)(((xx_mm - grid_centre_x_mm) * inverse_cellSize_mm) + 0.5f); if ((x_cell > ray_wdth_localisation) && (x_cell < dimension_cells - ray_wdth_localisation)) { // convert the y millimetre position into a grid cell position int y_cell = (int)(((yy_mm - grid_centre_y_mm) * inverse_cellSize_mm) + 0.5f); if ((y_cell > ray_wdth_localisation) && (y_cell < dimension_cells - ray_wdth_localisation)) { // convert the z millimetre position into a grid cell position int z_cell = (int)(((zz_mm - grid_centre_z_mm) * inverse_cellSize_mm) + 0.5f); if ((z_cell >= 0) && (z_cell < dimension_cells_vertical)) { int x_cell2 = x_cell; int y_cell2 = y_cell; // get the probability at this point // for the central axis of the ray using the inverse sensor model if (modelcomponent == OCCUPIED_SENSORMODEL) { centre_prob = 0.5f + (sensormodel_lookup_probability[sensormodel_index][grid_step] * 0.5f); } else { // calculate the probability from the vacancy model centre_prob = vacancyFunction(grid_step * inverse_steps, steps); } // width of the localisation ray for (int width = -ray_wdth_localisation; width <= ray_wdth_localisation; width++) { // is the width currently inside the mapping area of the ray ? bool isInsideMappingRayWidth = false; if ((width >= -ray_wdth) && (width <= ray_wdth)) isInsideMappingRayWidth = true; // adjust the x or y cell position depending upon the // deviation from the main axis of the ray if (longest_axis == Y_AXIS) x_cell2 = x_cell + width; else y_cell2 = y_cell + width; // probability at the central axis prob = centre_prob; prob_localisation = centre_prob; // probabilities are symmetrical about the axis of the ray // this multiplier implements a gaussian distribution around the centre if (width != 0) // don't bother if this is the centre of the ray { if (ray_wdth > 0) { if (modelcomponent == OCCUPIED_SENSORMODEL) { prob_localisation = 0.5f + ((prob_localisation - 0.5f) * gaussianLookup[Math.Abs(width) * 9 / ray_wdth_localisation]); // the probability used for narrower mapping rays if (isInsideMappingRayWidth) prob = 0.5f + ((prob - 0.5f) * gaussianLookup[Math.Abs(width) * 9 / ray_wdth]); } else { prob_localisation *= gaussianLookup[Math.Abs(width) * 9 / ray_wdth_localisation]; if (isInsideMappingRayWidth) prob *= gaussianLookup[Math.Abs(width) * 9 / ray_wdth]; } } } if ((withinMappingRange) && (localiseOnly)) { // only localise using occupancy, not vacancy if (modelcomponent == OCCUPIED_SENSORMODEL) { // test localisation_test[x_cell2][y_cell2] = true; if (cell[x_cell2][y_cell2] != null) { if (cell[x_cell2][y_cell2][z_cell] != null) { //if (cell[x_cell2][y_cell2][z_cell].probabilityLogOdds > 0) { // update the matching score, by combining the probability // of the grid cell with the probability from the localisation ray float score = NO_OCCUPANCY_EVIDENCE; if (longest_axis == X_AXIS) score = matchingProbability(x_cell2, y_cell2, z_cell, prob_localisation, ray.colour); if (longest_axis == Y_AXIS) score = matchingProbability(x_cell2, y_cell2, z_cell, prob_localisation, ray.colour); if (score != NO_OCCUPANCY_EVIDENCE) { if (matchingScore != NO_OCCUPANCY_EVIDENCE) matchingScore += score; else matchingScore = score; } } } } } } if ((isInsideMappingRayWidth) && (withinMappingRange) && (!localiseOnly)) { // generate a grid cell if necessary if (cell[x_cell2][y_cell2] == null) cell[x_cell2][y_cell2] = new particleGridCellBase[dimension_cells_vertical]; if (cell[x_cell2][y_cell2][z_cell] == null) cell[x_cell2][y_cell2][z_cell] = new particleGridCellBase(); particleGridCellBase c = cell[x_cell2][y_cell2][z_cell]; c.probabilityLogOdds += probabilities.LogOdds(prob); c.colour = ray.colour; // this is simplistic, but we'll live with it } } } //else grid_step = steps; // its the end of the ray, break out of the loop } //else grid_step = steps; // its the end of the ray, break out of the loop } //else grid_step = steps; // time to bail out chaps! grid_step += step_size; } } return (matchingScore); }
/// <summary> /// inserts a simulated doorway into the grid /// </summary> /// <param name="tx_cell">start x cell coordinate</param> /// <param name="ty_cell">start y cell coordinate</param> /// <param name="bx_cell">end x cell coordinate</param> /// <param name="by_cell">end y cell coordinate</param> /// <param name="wall_height_cells">height of the wall in cells</param> /// <param name="door_height_cells">height of the doorway in cells</param> /// <param name="door_width_cells">width of the doorway in cells</param> /// <param name="thickness_cells">thickness of the wall in cells</param> /// <param name="probability_variance">variation of probabilities, typically less than 0.2</param> /// <param name="r">red colour component in the range 0-255</param> /// <param name="g">green colour component in the range 0-255</param> /// <param name="b">blue colour component in the range 0-255</param> public override void InsertDoorwayCells( int tx_cell, int ty_cell, int bx_cell, int by_cell, int wall_height_cells, int door_height_cells, int door_width_cells, int thickness_cells, float probability_variance, int r, int g, int b) { Random rnd = new Random(0); if (wall_height_cells >= dimension_cells_vertical) wall_height_cells = dimension_cells_vertical - 1; if (thickness_cells < 1) thickness_cells = 1; if (door_height_cells > wall_height_cells) door_height_cells = wall_height_cells; int dx = bx_cell - tx_cell; int dy = by_cell - ty_cell; int length_cells = (int)Math.Sqrt(dx*dx + dy*dy); int door_start_index = (length_cells/2) - (door_width_cells/2); int door_end_index = door_start_index + door_width_cells; for (int i = 0; i < length_cells; i++) { int x_cell = tx_cell + (i * dx / length_cells); if ((x_cell > -1) && (x_cell < dimension_cells)) { int y_cell = ty_cell + (i * dy / length_cells); if ((y_cell > -1) && (y_cell < dimension_cells)) { if (cell[x_cell][y_cell] == null) cell[x_cell][y_cell] = new particleGridCellBase[dimension_cells_vertical]; for (int z_cell = 0; z_cell < wall_height_cells; z_cell++) { if (cell[x_cell][y_cell][z_cell] == null) cell[x_cell][y_cell][z_cell] = new particleGridCellBase(); float prob = 0.8f + ((float)rnd.NextDouble() * probability_variance * 2) - probability_variance; if (prob > 0.99f) prob = 0.99f; if (prob < 0.5f) prob = 0.5f; if ((i >= door_start_index) && (i <= door_end_index) && (z_cell < door_height_cells)) { prob = 0.2f + ((float)rnd.NextDouble() * probability_variance * 2) - probability_variance; if (prob > 0.4f) prob = 0.4f; if (prob < 0.01f) prob = 0.01f; } particleGridCellBase c = cell[x_cell][y_cell][z_cell]; c.probabilityLogOdds = probabilities.LogOdds(prob); c.colour = new byte[3]; c.colour[0] = (byte)r; c.colour[1] = (byte)g; c.colour[1] = (byte)b; } } } } }
/// <summary> /// inserts a simulated wall into the grid /// </summary> /// <param name="tx_cell">start x cell coordinate</param> /// <param name="ty_cell">start y cell coordinate</param> /// <param name="bx_cell">end x cell coordinate</param> /// <param name="by_cell">end y cell coordinate</param> /// <param name="height_cells">height of the wall in cells</param> /// <param name="thickness_cells">thickness of the wall in cells</param> /// <param name="probability_variance">variation of probabilities, typically less than 0.2</param> /// <param name="r">red colour component in the range 0-255</param> /// <param name="g">green colour component in the range 0-255</param> /// <param name="b">blue colour component in the range 0-255</param> public override void InsertWallCells( int tx_cell, int ty_cell, int bx_cell, int by_cell, int height_cells, int thickness_cells, float probability_variance, int r, int g, int b) { Random rnd = new Random(0); if (height_cells >= dimension_cells_vertical) height_cells = dimension_cells_vertical - 1; if (thickness_cells < 1) thickness_cells = 1; int dx = bx_cell - tx_cell; int dy = by_cell - ty_cell; int length_cells = (int)Math.Sqrt(dx*dx + dy*dy); for (int i = 0; i < length_cells; i++) { int x_cell = tx_cell + (i * dx / length_cells); if ((x_cell > -1) && (x_cell < dimension_cells)) { int y_cell = ty_cell + (i * dy / length_cells); if ((y_cell > -1) && (y_cell < dimension_cells)) { if (cell[x_cell][y_cell] == null) cell[x_cell][y_cell] = new particleGridCellBase[dimension_cells_vertical]; for (int z_cell = 0; z_cell < height_cells; z_cell++) { if (cell[x_cell][y_cell][z_cell] == null) cell[x_cell][y_cell][z_cell] = new particleGridCellBase(); float prob = 0.8f + ((float)rnd.NextDouble() * probability_variance * 2) - probability_variance; if (prob > 0.99f) prob = 0.99f; if (prob < 0.5f) prob = 0.5f; particleGridCellBase c = cell[x_cell][y_cell][z_cell]; c.probabilityLogOdds = probabilities.LogOdds(prob); c.colour = new byte[3]; c.colour[0] = (byte)r; c.colour[1] = (byte)g; c.colour[1] = (byte)b; } } } } }
/// <summary> /// inserts a simulated block into the grid /// </summary> /// <param name="tx_cell">start x cell coordinate</param> /// <param name="ty_cell">start y cell coordinate</param> /// <param name="bx_cell">end x cell coordinate</param> /// <param name="by_cell">end y cell coordinate</param> /// <param name="bottom_height_cells">bottom height of the block in cells</param> /// <param name="top_height_cells">bottom height of the block in cells</param> /// <param name="probability_variance">variation of probabilities, typically less than 0.2</param> /// <param name="r">red colour component in the range 0-255</param> /// <param name="g">green colour component in the range 0-255</param> /// <param name="b">blue colour component in the range 0-255</param> public override void InsertBlockCells( int tx_cell, int ty_cell, int bx_cell, int by_cell, int bottom_height_cells, int top_height_cells, float probability_variance, int r, int g, int b) { Random rnd = new Random(0); if (top_height_cells >= dimension_cells_vertical) top_height_cells = dimension_cells_vertical - 1; if (bottom_height_cells < 0) bottom_height_cells = 0; for (int x_cell = tx_cell; x_cell <= bx_cell; x_cell++) { if ((x_cell > -1) && (x_cell < dimension_cells)) { for (int y_cell = ty_cell; y_cell <= by_cell; y_cell++) { if ((y_cell > -1) && (y_cell < dimension_cells)) { if (cell[x_cell][y_cell] == null) cell[x_cell][y_cell] = new particleGridCellBase[dimension_cells_vertical]; for (int z_cell = bottom_height_cells; z_cell <= top_height_cells; z_cell++) { if ((z_cell > -1) && (z_cell < dimension_cells_vertical)) { if (cell[x_cell][y_cell][z_cell] == null) cell[x_cell][y_cell][z_cell] = new particleGridCellBase(); float prob = 0.8f + ((float)rnd.NextDouble() * probability_variance * 2) - probability_variance; if (prob > 0.99f) prob = 0.99f; if (prob < 0.5f) prob = 0.5f; particleGridCellBase c = cell[x_cell][y_cell][z_cell]; c.probabilityLogOdds = probabilities.LogOdds(prob); c.colour = new byte[3]; c.colour[0] = (byte)r; c.colour[1] = (byte)g; c.colour[1] = (byte)b; } } } } } } }