This one has me stumped. I have three classes - a large class called Level that contains, among other things, a multidimensional vector of pointers to Plant instances and a multidimensional vector of pointers to Mob instances; both vectors are meant to classify the mobs and plants according to their position, so that I can search for each in a smaller vector based on their approximate location, rather than cycling through all existing plants/mobs in order to find the one nearest to a given point.
The vectors are as follows, with the smallest std::vectors representing a square area of 128 pixels on each side. Mobs and plants are classed by dividing their X and Y coords by 128 and adding them to the proper sector (I am careful that the resulting values are in fact integers).
std::vector< std::vector< std::vector<Plant*> > >* m_PlantSectors
std::vector< std::vector< std::vector<Mob*> > >* m_AnimalSectors
The mobs sometimes need to find plants. Here is where the problem arises: when the mobs query the multidimensional vector, searching for plants in their approximate area (if the mob's coords/128 are, say, [1,2] it searches m_PlantSectors[2][1]), they sometimes find plants that don't exist.
Not only that, but these plants have impossible positions, on the order of 1.9777e+33 or 3.75853e-39 (for instance). When I try to change the color of the chosen plant to red in order to find it visually, I find that none of the plants on the screen (the only plants are ones I've hand-placed) have changed color.
I've marked all the plants with an integer ID; there are 36 plants, with IDs from 1-36, but the plants my mobs find have IDs like 63 or 429 - ones that can't possibly exist, since no such number of plants was created (there is a single plant creation function that consistently reports how many plants exist, so no plants are being accidentally created). The mobs all run off to the upper-left part of the screen after the imaginary plants, and die of starvation.
So somehow, I an creating ghost plants. So far, I've tried two separate approaches for allowing Mob instances to look for Plant instances. The first looks like this:
float TargetDist = 256 * 256;
Plant* Candidate = 0;
Plant* ForageTarget = 0;
int xSect = m_X / 128;
int ySect = m_Y / 128;
std::vector<Plant*> ThisSect = pLevel->CheckPSector(xSect, ySect);
for (int i = 0; i < ThisSect.size(); ++i)
{
cout << "Searching in Sector (" << ySect << ", " << xSect << ")\n";
Candidate = ThisSect[i];
cout << "Candidate at: " << Candidate->GetX() << ", " << Candidate->GetY() << "\n";
Candidate->Mark();
//Calculate distance
float xDist = Candidate->GetX() - m_X;
float yDist = Candidate->GetY() - m_Y;
float tDist = sqrt(xDist * xDist + yDist * yDist);
if (tDist <= TargetDist)
{
ForageTarget = Candidate;
TargetDist = tDist;
}
}
Where CheckPSector() looks like this:
std::vector<Plant*> Level::CheckPSector(int x, int y)
{
return m_PlantSectors[y][x];
}
The second thing I tried was this:
float TargetDist = 256 * 256;
Plant* Candidate = 0;
Plant* ForageTarget = 0;
int xSect = m_X / 128;
int ySect = m_Y / 128;
std::vector< std::vector< std::vector<Plant*> > >* Sectors = pLevel->AccessPlantSectors();
for (int i = 0; i < (*Sectors)[ySect][xSect].size(); ++i)
{
cout << "Searching in Sector (" << ySect << ", " << xSect << ")\n";
Candidate = (*Sectors)[ySect][xSect][i];
cout << "Candidate at: " << Candidate->GetX() << ", " << Candidate->GetY() << "\n";
Candidate->Mark();
//Calculate distance
float xDist = Candidate->GetX() - m_X;
float yDist = Candidate->GetY() - m_Y;
float tDist = sqrt(xDist * xDist + yDist * yDist);
if (tDist <= TargetDist)
{
ForageTarget = Candidate;
TargetDist = tDist;
}
}
Using this:
std::vector< std::vector< std::vector<Plant*> > >* Level::AccessPlantSectors()
{
return &m_PlantSectors;
}
Both of these, however, result in the animals finding imaginary plants and running off into the void.
I don't want to copy potentially large multidimensional vectors into Mob instances on a regular basis, because there will be many such instances at any one time, and I'd like the program to run somewhat smoothly. Even so, I just tried to do the whole thing by copying the entire vector instead of just the relevant one, and I get the same result: imaginary plants.
I've never quite had a problem like this before; what could be happening here?
EDIT: Perhaps I should mention that getting the chosen plants to self-report their own position and ID fails equally, turning up absurd results, so it isn't just the functions in Plant I use to access private members. Meanwhile, querying all existing plants does not reveal any with the information the ghost plants self-report.
If m_PlantSectors is defined as:
std::vector< std::vector< std::vector<Plant*> > >* m_PlantSectors
Then Level::AccessPlantSectors() should return m_PlantSectors, not &m_PlantSectors because you already have a pointer.
Likewise, Level::CheckPSector(int x, int y) should return (*m_PlantSectors)[y][x] because you need to deference the pointer before invoking the [] operator.
As you've written them, Level::CheckPSector(int x, int y) returns random memory and I'm surprised that Level::AccessPlantSectors() compiles.
Related
I have a question about the vertices of graphs in c++. Like, let's suppose I want to have a graph with vertices as 100,200,300,400 and they are connected in some manner not important but if we are creating an adjacency list graph what we do is.
adj[u].push_back(v);
adj[v].push_back(u);
and let 400 is connected with 200 we are doing adj[400] and creating a large matrix of vectors when all we need was a matrix of size 4 as there are four vertices and here we going till 400 can someone explain this. Is it like in graphs we have all vertices consecutive and must start from some small number? The code works fine when you have vertices like 1,2,3,4,5. We are using vertices as an index and depending on our vertices they can vary by a lot than what we needed.
An adjacency list stores a list of the connected vertices for each vertex in the graph. For example, given this graph:
1---2
|\ |
| \ |
| \|
3---4
You would store:
1: 2, 3, 4
2: 1, 4
3: 1, 4
4: 1, 2, 3
This can be done with a std::vector<std::vector<int>>. Note that you do not need to use the values of the graph as the indexes into these vectors. If the values of the graph were instead 100, 200, 300, 400 you could use a separate map container to convert from vertex value to an index into the adjacency list (std::unordered_map<ValueType, IndexType>). You could also store a Vertex structure such as this:
struct Vertex {
int index; // 0, 1, 2, 3, 4, 5, etc.
int value; // 100, 200, or whatever value you want
};
Not sure what the problem is exactly but i guess si about the speed, the most simple and easy fix is to have a "memory layout" like in a pixel buffer, a index is a implicit value defied by de position since each segment is.
-------------------------------------------------------------------...
| float, float, float, float | float, float, float, float | float,
-------------------------------------------------------------------...
| index 0 | index 1 | index 2
-------------------------------------------------------------------...
As you didn't give a sample code to give a better idea the example asumes a lot if things but basically implements the layout idea; using arrays is not needed, is my preference, vector would give almost no performance penalty the bigges one being the resizement; some of the lines are not intuitive, like why is a operation + a array faster than having an array inside an array, it just is, the memory is slower than te cpu.
Small note, bacause all the "small arrays" are just a big array you need to worrie of overflows and underflow or add a check; if some vertex groups are smaller that the chunk size just waste the space, the time to compact and un compact the data is worst in most cases than having the padding.
#include <iostream>
#include <chrono>
template <typename VAL>
struct Ver_Map {
VAL * base_ptr;
uint32_t map_size;
uint32_t vertex_len;
void alloc_map(uint32_t elem, uint32_t ver_len, VAL in){
base_ptr = new VAL[elem * ver_len] { in };
vertex_len = ver_len;
map_size = elem;
}
void free_map(){
delete base_ptr;
}
VAL * operator()(uint32_t object){
return &base_ptr[(object * vertex_len)];
}
VAL & operator()(uint32_t object, uint32_t vertex){
return base_ptr[(object * vertex_len) + vertex];
}
};
int main (void) {
const uint32_t map_len = 10000;
Ver_Map<float> ver_map;
ver_map.alloc_map(map_len, 4, 0.0f);
// Use case
ver_map(0, 2) = 0.5f;
std::cout << ver_map(0)[1] << std::endl;
std::cout << ver_map(0)[2] << std::endl;
std::cout << ver_map(0, 2) << std::endl;
// Size in memory
std::cout << "Size of struct -> "
<< (map_len * sizeof(float)) + sizeof(Ver_Map<float>)
<< " bytes" << std::endl;
// Time to fully clear
auto start = std::chrono::steady_clock::now();
for(int x=0; x < map_len; x++){
for(int y=0; y < ver_map.vertex_len; y++){
ver_map(x, y) = 1.0f;
}
}
std::cout << "Full write time -> "
<< (uint32_t)std::chrono::duration_cast<std::chrono::microseconds>
(std::chrono::steady_clock::now() - start).count()
<< " microseconds" << std::endl;
ver_map.free_map();
return 0;
}
Background
For a computer vision assignment I've been given the task of implementing RANSAC to fit a plane to a given set of points and filter that input list of points by the consensus model using Eigenvalue Decomposition.
I have spent days trying to tweak my code to achieve correct plane filtering behavior on an input set of test data. All you algorithm junkies, this one's for you.
My implementation uses a vector of a ROS data structure (Point32) as inputs, but this is transparent to the problem at hand.
What I've done
When I test for expected plane filtering behavior (correct elimination of outliers >95-99% of the time), I see in my implementation that I only eliminate outliers and extract the main plane of a test point cloud ~30-40% of the time. Other times, I filter a plane that ~somewhat~ fits the expected model, but leaves a lot of obvious outliers inside the consensus model. The fact that this works at all suggests that I'm doing some things right, and some things wrong.
I've tweaked my constants (distance threshold, max iterations, estimated % points fit) to London and back, and I only see small differences in the consensus model.
Implementation (long)
const float RANSAC_ESTIMATED_FIT_POINTS = .80f; // % points estimated to fit the model
const size_t RANSAC_MAX_ITER = 500; // max RANSAC iterations
const size_t RANDOM_MAX_TRIES = 100; // max RANSAC random point tries per iteration
const float RANSAC_THRESHOLD = 0.0000001f; // threshold to determine what constitutes a close point to a plane
/*
Helper to randomly select an item from a STL container, from stackoverflow.
*/
template <typename I>
I random_element(I begin, I end)
{
const unsigned long n = std::distance(begin, end);
const unsigned long divisor = ((long)RAND_MAX + 1) / n;
unsigned long k;
do { k = std::rand() / divisor; } while (k >= n);
std::advance(begin, k);
return begin;
}
bool run_RANSAC(const std::vector<Point32> all_points,
Vector3f *out_p0, Vector3f *out_n,
std::vector<Point32> *out_inlier_points)
{
for (size_t iterations = 0; iterations < RANSAC_MAX_ITER; iterations ++)
{
Point32 p1,p2,p3;
Vector3f v1;
Vector3f v2;
Vector3f n_hat; // keep track of the current plane model
Vector3f P0;
std::vector<Point32> points_agree; // list of points that agree with model within
bool found = false;
// try RANDOM_MAX_TRIES times to get random 3 points
for (size_t tries = 0; tries < RANDOM_MAX_TRIES; tries ++) // try to get unique random points 100 times
{
// get 3 random points
p1 = *random_element(all_points.begin(), all_points.end());
p2 = *random_element(all_points.begin(), all_points.end());
p3 = *random_element(all_points.begin(), all_points.end());
v1 = Vector3f (p2.x - p1.x,
p2.y - p1.y,
p2.z - p1.z ); //Vector P1P2
v2 = Vector3f (p3.x - p1.x,
p3.y - p1.y,
p3.z - p1.z); //Vector P1P3
if (std::abs(v1.dot(v2)) != 1.f) // dot product != 1 means we've found 3 nonlinear points
{
found = true;
break;
}
} // end try random element loop
if (!found) // could not find 3 random nonlinear points in 100 tries, go to next iteration
{
ROS_ERROR("run_RANSAC(): Could not find 3 random nonlinear points in %ld tries, going on to iteration %ld", RANDOM_MAX_TRIES, iterations + 1);
continue;
}
// nonlinear random points exist past here
// fit a plane to p1, p2, p3
Vector3f n = v1.cross(v2); // calculate normal of plane
n_hat = n / n.norm();
P0 = Vector3f(p1.x, p1.y, p1.z);
// at some point, the original p0, p1, p2 will be iterated over and added to agreed points
// loop over all points, find points that are inliers to plane
for (std::vector<Point32>::const_iterator it = all_points.begin();
it != all_points.end(); it++)
{
Vector3f M (it->x - P0.x(),
it->y - P0.y(),
it->z - P0.z()); // M = (P - P0)
float d = M.dot(n_hat); // calculate distance
if (d <= RANSAC_THRESHOLD)
{ // add to inlier points list
points_agree.push_back(*it);
}
} // end points loop
ROS_DEBUG("run_RANSAC() POINTS AGREED: %li=%f, RANSAC_ESTIMATED_FIT_POINTS: %f", points_agree.size(),
(float) points_agree.size() / all_points.size(), RANSAC_ESTIMATED_FIT_POINTS);
if (((float) points_agree.size()) / all_points.size() > RANSAC_ESTIMATED_FIT_POINTS)
{ // if points agree / total points > estimated % points fitting
// fit to points_agree.size() points
size_t n = points_agree.size();
Vector3f sum(0.0f, 0.0f, 0.0f);
for (std::vector<Point32>::iterator iter = points_agree.begin();
iter != points_agree.end(); iter++)
{
sum += Vector3f(iter->x, iter->y, iter->z);
}
Vector3f centroid = sum / n; // calculate centroid
Eigen::MatrixXf M(points_agree.size(), 3);
for (size_t row = 0; row < points_agree.size(); row++)
{ // build distance vector matrix
Vector3f point(points_agree[row].x,
points_agree[row].y,
points_agree[row].z);
for (size_t col = 0; col < 3; col ++)
{
M(row, col) = point(col) - centroid(col);
}
}
Matrix3f covariance_matrix = M.transpose() * M;
Eigen::EigenSolver<Matrix3f> eigen_solver;
eigen_solver.compute(covariance_matrix);
Vector3f eigen_values = eigen_solver.eigenvalues().real();
Matrix3f eigen_vectors = eigen_solver.eigenvectors().real();
// find eigenvalue that is closest to 0
size_t idx;
// find minimum eigenvalue, get index
float closest_eval = eigen_values.cwiseAbs().minCoeff(&idx);
// find corresponding eigenvector
Vector3f closest_evec = eigen_vectors.col(idx);
std::stringstream logstr;
logstr << "Closest eigenvalue : " << closest_eval << std::endl <<
"Corresponding eigenvector : " << std::endl << closest_evec << std::endl <<
"Centroid : " << std::endl << centroid;
ROS_DEBUG("run_RANSAC(): %s", logstr.str().c_str());
Vector3f all_fitted_n_hat = closest_evec / closest_evec.norm();
// invoke copy constructors for outbound
*out_n = Vector3f(all_fitted_n_hat);
*out_p0 = Vector3f(centroid);
*out_inlier_points = std::vector<Point32>(points_agree);
ROS_DEBUG("run_RANSAC():: Success, total_size: %li, inlier_size: %li, %% agreement %f",
all_points.size(), out_inlier_points->size(), (float) out_inlier_points->size() / all_points.size());
return true;
}
} // end iterations loop
return false;
}
Pseudocode from wikipedia for reference:
Given:
data – a set of observed data points
model – a model that can be fitted to data points
n – minimum number of data points required to fit the model
k – maximum number of iterations allowed in the algorithm
t – threshold value to determine when a data point fits a model
d – number of close data points required to assert that a model fits well to data
Return:
bestfit – model parameters which best fit the data (or nul if no good model is found)
iterations = 0
bestfit = nul
besterr = something really large
while iterations < k {
maybeinliers = n randomly selected values from data
maybemodel = model parameters fitted to maybeinliers
alsoinliers = empty set
for every point in data not in maybeinliers {
if point fits maybemodel with an error smaller than t
add point to alsoinliers
}
if the number of elements in alsoinliers is > d {
% this implies that we may have found a good model
% now test how good it is
bettermodel = model parameters fitted to all points in maybeinliers and alsoinliers
thiserr = a measure of how well model fits these points
if thiserr < besterr {
bestfit = bettermodel
besterr = thiserr
}
}
increment iterations
}
return bestfit
The only difference between my implementation and the wikipedia pseudocode is the following:
thiserr = a measure of how well model fits these points
if thiserr < besterr {
bestfit = bettermodel
besterr = thiserr
}
My guess is that I need to do something related to comparing the (closest_eval) with some sentinel value for the expected minimum eigenvalue corresponding to a normal for planes that tend to fit the model. However this was not covered in class and I have no idea where to start figuring out what's wrong.
Heh, it's funny how thinking about how to present the problem to others can actually solve the problem I'm having.
Solved by simply implementing this with a std::numeric_limits::max() starting best fit eigenvalue. This is because the best fit plane extracted on any n-th iteration of RANSAC is not guaranteed to be THE best fit plane and may have a huge error in consensus amongst each constituent point, so I need to converge on that for each iteration. Woops.
thiserr = a measure of how well model fits these points
if thiserr < besterr {
bestfit = bettermodel
besterr = thiserr
}
I'm having some problems with polymorphism I have a superclass of CEntity, and a subclass of unit type, I am dynamic casting and removing and emplacing a new entity of the dynamic casts type at its place, I have the opposite problem of my previous question.
Problem is the values being set are not changing, it remains with default values, it appeared to work before but now it has stopped working, I'm not sure what has caused the issue.
specifically buildsetup, sets the x-y-z values of the unit subclass's x,y,z, but when I look inside the vector the values remain unchanged, this is strange because it does actually manage to change the values for whether the unit is alive or not.
void builder(int no, string in , int top, int bot, CTeam &team, string owner, string original)
{
for (int i = top; i <= bot; i++)
{
EntityUnit* a;
a = dynamic_cast<EntityUnit*>(AWorld.EntitiesVector[i]);
a->unit_alive;
if (a->unit_alive == false)
{
float x = Player.returncity_add().cit_ret_X();
float y = Player.returncity_add().cit_ret_Y();
float z = Player.returncity_add().cit_ret_Z();
cout << "X:" << x;
cout << "Y:" << y;
cout << "Z:" << z;
float cost = MainAB.UnitTemplates[no]->UDRetCost();
float health = MainAB.UnitTemplates[no]->UDRetMaxHealth();
float damage = MainAB.UnitTemplates[no]->UDRetStrength();
float speed = MainAB.UnitTemplates[no]->UDRetSpeed();
float buildtime = MainAB.UnitTemplates[no]->UDRetBuildTime();
int popcost = MainAB.UnitTemplates[no]->UDRetPop();
a->set_owner(owner);
setmodel(i, x, y, z); // takes an xyz by ref and sets the model
to them then changes the model's localz by -10
Units[i]->SetSkin(setskin(owner, original));
a->BuildSetup(x, y, z, health, damage, speed, buildtime, cost, popcost);
team.inc_popcount(a->UDRetPop());
a->set_unit_alive(true);
sBuildUnit.play();
AWorld.EntitiesVector.erase(AWorld.EntitiesVector.begin() + i);
AWorld.EntitiesVector.emplace(AWorld.EntitiesVector.begin() + i, new EntityUnit(a));
AWorld.EntitiesVector[i]->set_x(x);
AWorld.EntitiesVector[i]->set_y(y);
AWorld.EntitiesVector[i]->set_z(z);
break;
}
}
Entity build setup
void EntityUnit::BuildSetup(float x, float y, float z,float _health, float _damage, float _speed, float _buildtime, float _cost, int _popcost)
{
unit_x = x;
unit_y = y;
unit_z = z;
unit_health[0] = _health;
unit_health[1] = _health;
unit_damage = _damage;
speed = _speed;
buildtime = _buildtime;
cost = _cost;
CUnitType = NA;
pop_req = _popcost;
}
After static debugging it, it most definately emplaces a new unit with the updated -is_alive, and while a's values change at the point of unitbuild, when its emplaced all x,y,z's return to 9999, which was what it was when they were pushed on the vector.
When you call
AWorld.EntitiesVector.erase(AWorld.EntitiesVector.begin() + i);
you destroy the object pointed to by a. The subsequent reference to it on the next line is Undefined Behavior, and anything is possible.
I'm not sure why you erase the entity, then try to put a new one in the same place. If you structure your code right you should be able to just reuse the existing entity (pointed to by a) without the erase and emplace calls.
Ok, Apparently the problem was it was totally unnecessary to erase/emplace, as I get what you mean now returning a pointer, it edited the value... odd it didn't work last time.
So I have a list of 3D points and I want to group together points that are within 1 unit or less from each other. So here's an example of what I'm talking about (I'll use 2D points for the example),
Say we have point 1: (0,0) and point 2: (0,1) which are 1 distance from each other. The program will store both in a vector. Now here's a third point (0,2). This point is 1 distance from point 2 but not from point 1 but the program will still store it since it is within 1 distance from at least 1 point in the vector.
So I want to gather 3D points in "blobs" and everything that is 1 unit or less from this "blob" will be added onto the "blob"
I've tried so many different functions these past few days, tried recursion but always crashes and nested tons of forloops but I can't make this work.
Here's my code (I added in comments next to the code to make it easier to understand)
void combinePoints(vector<Point>& allPoints, vector< vector<Cavity> >& allPointBlobs, vector<Point>& tempBlob)
{
float check;
if(allPoints.size() != 0) //if statement to stop recursion once all the points from "allPoints" vector is checked and removed
{
for(int i = 0; i < allPoints.size(); i++) //3d distance formula checking first point with all other points
{
check = sqrt((allPoints[0].getX() - allPoints[i].getX()) * (allPoints[0].getX() - allPoints[i].getX()) +
(allPoints[0].getY() - allPoints[i].getY()) * (allPoints[0].getY() - allPoints[i].getY()) +
(allPoints[0].getZ() - allPoints[i].getZ()) * (allPoints[0].getZ() - allPoints[i].getZ()));
if ((check <= 1.000) && (check != 0 )) //once a point is found that is 1 distance or less, it is added to tempBlob vector and removed from allPoints
{
tempBlob.push_back(allPoints[0]);
tempBlob.push_back(allPoints[i]);
allPoints.erase(allPoints.begin() + i);
allPoints.erase(connollyPoints.begin());
break;
}
}
if(check > 1.000) //However, if no points are nearby, then tempBlob is finished finding all nearby points and is added to a vector and cleared so it can start finding another blob.
{
allPointBlobs.push_back(tempBlob);
tempBlob.clear();
cout << "Blob Done" << endl;
combinePoints(allPoints, allPointBlobs, tempBlob);
}
else
{
combinePoints2(allPoints, allPointBlobs, tempBlob);
}
}
}
void combinePoints2(vector<Point>& allPoints, vector< vector<Point> >& allPointBlobs, vector<Point>& tempBlob) //combinePoints2 is almost the same as the first one, except I changed the first part where it doesnt have to initiate a vector with first two points. This function will then check all points in the temporary blob against all other points and find ones that are 1 distance or less
{
cout << tempBlob.size() << endl; //I use this just to check if function is working
float check = 0;
if(allPoints.size() != 0)
{
for(int j = 0; j < tempBlob.size(); j++)
{
for(int k = 0; k < allPoints.size(); k++)
{
check = sqrt((tempBlob[j].getX() - allPoints[k].getX()) * (tempBlob[j].getX() - allPoints[k].getX()) +
(tempBlob[j].getY() - allPoints[k].getY()) * (tempBlob[j].getY() - allPoints[k].getY()) +
(tempBlob[j].getZ() - allPoints[k].getZ()) * (tempBlob[j].getZ() - allPoints[k].getZ()));
if ((check <= 1.000) && (check != 0 ))
{
tempBlob.push_back(allPoints[k]);
allPoints.erase(allPoints.begin() + k);
break;
}
}
if ((check <= 1.000) && (check != 0 ))
{
break;
}
}
if(check > 1.000)
{
allPointBlobs.push_back(tempBlob);
tempBlob.clear();
cout << "Blob Done" << endl;
combinePoints(allPoints, allPointBlobs, tempBlob);
}
else
{
combinePoints2(allPoints, allPointBlobs, tempBlob);
}
}
}
I use all the breaks because when a point is deleted from allPoints, it messes up the forloops since I'm using .size() for the amount of times it runs. This makes the program really slow since it has to keep reinitiating the function when it finds 1 point. I'm hoping someone can help me find a simpler way to do this.
I've made many other functions but they crash, this is the only one that is working so far (or at least i hope its working lol, it just doesnt crash which is a good sign).
Write a function that gets two points as arguments and determines if they are within a certain distance or use a class "Point" with a method that checks if a point passed as argument is within distance x of this point.
Store all points within distance of 1 in a simple vector to represent your "blob".
A simple naive class could look like this:
class Point{
public:
bool withinDistance(const Point& other) const;
void addProxyPoint(const Point& other);
private:
double x_cord, y_cord, ...;
std::vector<Point> proximities;
};
Although you can do this in several other ways, especially if you don't want to store the blob on the point object itself.
Or you can write a method that checks if a point is within distance and also adds it to your blob vector.
Your choice of course.
i am looking for a way to collect a set of voxels. A voxel is a 3D cell that can be full/empty/unknown and is built upon a point cloud (for data reduction). The voxels collection once built is never modified (destroyed and rebuilt each round), but different kind of access are required (neighborood, iterative on all, direct).
The voxel space is very very sparse, out of order of 1.000.000 possible voxels in space only at most 1000 are used.
So i decided to use a (unordered since using c++)hashmap to collect them (an octree is an overkill i think) with the voxel ID as a key. Now i need a function to convert in both way a 3D point to voxel ID and the ID to the voxel 3D point centroid.
What i find hard is a very fast way to do it, i'd like to have they key as a single int value like:
unsigned int VoxelsMap::pointToVoxelId(const Vector3f & point){
unsigned int id = 0;
int x = (int)floor(roundpoint[0]);
int y = (int)floor(roundpoint[1]);
int z = (int)floor(roundpoint[2]);
id = A-BIJECTIVE-FUNCTION(x, y, z);
return id;
}
but for the bijective function i cant come up with anything very fast (as for the previous casts etc that i dont like for a function that must be used so often (200FPS x ~1000 x 3) ).
So:
is the hashmap a good datastructure (what worries me is the neighborood search)
what can be a function for A-BIJECTIVE-FUNCTION or for the whole function
Thanks.
#include <iostream>
using namespace std;
int main()
{
int x = 2.1474e+009;
int y = -2097152;
int z = -2048;
int rx = x;
int ry = y << 10;
int rz = z << 20;
int hashed = rx + ry + rz;
x = rx;
y = ry >> 10;
z = rz >> 20;
cout << hashed << endl;
cout << x << " " << y << " " << z << endl;
return 0;
}
This hash/unhash method should be the fastest.
Note that I only use 30 bits out of 32 bits of the integer. This allows a maxmimum world size of 4.2950e+009 x 4194304 x 4096. If you want to extend the world limits, you will have to use more/bigger integers.
Hope this can help.
Do you want to collect them space-wise in a way that links every adjacent voxel? If this is what you want, then you could use the Hoshen-Kopelman algorithm in 3D. Writing the code for that should take like a day or two, and you're done. The exmaple in the link is for 2D; expanding that to 3D isn't an issue at all.
Hope this helps.
Why not using a more elaborate key for your hashmap? Instead of a simple int, you could build a tuple with your x,y,z coordinates or implement you own struct. The later option would require implementing operator==() and a hash function. Some information about a good hash function can be found here.