I am new to C++ and I really need some help on this. I am trying to create a structure to interface with the GSL Monte-Carlo algorithms (a fact that is really not important for this example). I have searched all of the C++ tutorials, the stackoverflow posts and the GSL documentation with no luck. I am using the armadillo package for matrix manipulation; it is very robust. I am unable to use a dynamic array within the structure, as per the documentation, so I am trying to find a way to make my structure variable *M point to the values in my array *L[]. I am sure that this would be better with a vector but 1) the rest of the code (in bad form) uses pointers already, and 2) I am looking at this as a learning experience. I am surprised that the addresses for *M and *L[] are not the same in my code. I am also, less importantly, surprised that my std::cout prints a different number of spaces for each line. The code exits before printing the last std::cout as shown in the output below.
Thanks for your help!
#include "pch.h"
#include "stdio.h"
#include "complex"
#include "new"
#include "armadillo"
using namespace arma;
class Link
{
public:
arma::Mat<cx_double>::fixed<3, 3>* dir[4]; // pointer to directional SU(3) matrices
Link(); // default constructor
};
Link::Link() // default constructor - all directional matrices are the identity
{
for (size_t hcount = 0; hcount < 4; hcount++)
{
dir[hcount] = new arma::Mat<cx_double>::fixed<3, 3>{ fill::eye }; // create directional matrix in direction hcount
}
}
struct Param
{
Link* M;
};
int main()
{
const int size = 10;
Param* Parameters = new Param{ NULL };
Link* L[size];
arma::Mat<cx_double>::fixed<3, 3> One{ fill::eye };
for (size_t hcount = 0; hcount < 10; hcount++)
{
L[hcount] = new Link();
*L[hcount]->dir[1] = *L[hcount]->dir[1] + hcount * One; // Make each array element #1 unique
}
Parameters->M = L[0];
std::cout << "&L = " << &L << std::endl;
std::cout << "&Parameters->M = " << &Parameters->M << std::endl; // surprised that addresses are not the same
std::cout << std::endl;
std::cout << "&L[0] = " << &L[0] << std::endl;
std::cout << "&Parameters->M[0] = " << &Parameters->M[0] << std::endl;
std::cout << std::endl;
std::cout << "&L[5] = " << &L[5] << std::endl;
std::cout << "&Parameters->M[5] = " << &Parameters->M[5] << std::endl;
std::cout << std::endl;
std::cout << "&L[5]->dir[1] = " << &L[5]->dir[1] << std::endl;
std::cout << "&Parameters->M[5].dir[1] = " << &Parameters->M[5].dir[1] << std::endl;
std::cout << std::endl;
std::cout << "*L[5]->dir[1] = " << *L[5]->dir[1] << std::endl; // This works
std::cout << "*Parameters->M[5].dir[1] = " << *Parameters->M[5].dir[1] << std::endl; // This does not
std::cout << std::endl;
}
OUTPUT
&L = 0024F7CC
&Parameters->M = 004EEFD8
&L[0] = 0024F7CC
&Parameters->M[0] = 004E0578
&L[5] = 0024F7E0
&Parameters->M[5] = 004E05C8
&L[5]->dir[1] = 004E50C4
&Parameters->M[5].dir[1] = 004E05CC
*L[5]->dir[1] = (+6.000e+00,+0.000e+00) (0,0) (0,0)
(0,0) (+6.000e+00,+0.000e+00) (0,0)
(0,0) (0,0) (+6.000e+00,+0.000e+00)
*Parameters->M[5].dir[1] =
&L is the adress of L, so it's the adress of the pointer to the first element not the adress of the first elemenr itself. Same for & Parameters->M. That is the adress of thd the Member M from Parameters. You want to compare L[0] with Parameters->M except when M should not point to the element that L[0] refers to but to the start of the array itself, then you want to compare it with L. But then you also have to change the assignment.
I find it a bit weird that you use an array of pointers. Just use an array of Links.
Related
Suppose I have a string array like this:
string registers[2] = {"r0","r1"}
And a struct like this:
struct cpu {
int r1;
}
I tried something like this:
cpu *p = (cpu*)malloc(sizeof(cpu));
cout << p->registers[1] << endl;
But this gave a compilation error. How to implement this?
EDIT: Problem description
I need to access the member of a cpu class using an index so I thought I could just put the names of the memebers in an array and then get the name of the member using the index
Your code is completely illformed. Expression p->registers uses default operator -> which requires left-hand operator to be a pointer to a type which would have a member with name used as right-hand operator (registers). Your cpu doesn't contain registers.
Steps to emulate behaviour like one you desire for whatever reason that is:
design a way to associate particular string to a member of cpu, by index or pointer to member.
map the string value to an index or a pointer to member.
C++ allows to encapsulate that using member operators,which would result in something like p["r0"] yielding a reference to r0.
In simplest case, when all elements are of same type, you may just use a std::map or a class designed similarly, e.g.
struct cpu {
std::map<std::string, int> registers;
// constructor initializes the map
cpu() : registers ( { {"r0", 0},
{"r1", 0}
})
{}
};
Here an expression p->registers["r0"] would give you reference to value associated with "r0" key, etc.
NB The creation of cpu object should be
cpu *p = new cpu();
Here is an example that uses a mapping of string to cpu member:
#include <iostream>
#include <string>
#include <map>
using namespace std;
struct cpu {
int r0;
int r1;
};
map<string,int cpu::*> registers = {
{"r0",&cpu::r0},
{"r1",&cpu::r1}
};
int main()
{
cpu x{0, 1};
cout << " x.r1 = " << x.r1 << ", x.*registers[\"r1\"] = " << x.*registers["r1"] << "\n";
x.*registers["r1"] = 2;
cout << " x.r1 = " << x.r1 << ", x.*registers[\"r1\"] = " << x.*registers["r1"] << "\n";
cpu *p = &x;
p->*registers["r1"] = 3;
cout << "p->r1 = " << p->r1 << ", p->*registers[\"r1\"] = " << p->*registers["r1"] << "\n";
return 0;
}
Try it here: https://onlinegdb.com/d2KqBH0Mo
And here is one that, like your original example, uses an integer index into an array:
#include <iostream>
#include <string>
#include <map>
using namespace std;
struct cpu {
int r0;
int r1;
};
int cpu::* registers[] = {
&cpu::r0,
&cpu::r1
};
int main()
{
cpu x{0, 1};
cout << " x.r1 = " << x.r1 << ", x.*registers[1] = " << x.*registers[1] << "\n";
x.*registers[1] = 2;
cout << " x.r1 = " << x.r1 << ", x.*registers[1] = " << x.*registers[1] << "\n";
cpu *p = &x;
p->*registers[1] = 3;
cout << "p->r1 = " << p->r1 << ", p->*registers[1] = " << p->*registers[1] << "\n";
return 0;
}
Try it here: https://onlinegdb.com/3hWpmEijs
I am trying to load freetype chars, stuff them into a texture as subimages and then render them instanced.
While most of it seems to work, right now I have a problem with storing the texture coordinates into a glm::mat2x4 matrix.
As can be seen below each character has a struct with information I right now deem necessary, including a matrix called face, which should store the texture coordinates.
But when it comes to assigning the coordinates, after leaving the loop in which it takes place, suddenly all the values go crazy, without any (wanted/ intended) operation taking place from my side.
After creating the texture atlas with freetype and putting all my structs into the map, I assign the width and height of my texture aw & ah to a storage class called c_atlas.
I calculate the texture coordinates in the loop shown below, make the glm::mat2x4 a 0.0f matrix and then stuff them into it. Couting them into the console gives the values I want.
After leaving the for loop I start another one, browsing over the matrix and cout them into the console, which gives me more or less random values in the range of e^-23 to e^32.
All of this happens in namespace foo and is called in a constructor of a class in the same namespace (sth. like this:)
foo::class::constructor()
{
call_function();
}
int main()
{
foo::class c;
c.call_function();
}
I crafted a minimum working example, but unfortunatly I am not able to replicate the error.
So I have the following loop running (a part of call_function():
namespace foo
{
namespace alphabet
{
const char path_arial[] = "res/font/consola.ttf";
class character
{
public:
glm::vec2 advance;
glm::vec2 bearing;
glm::vec2 size;
glm::vec2 offset;
glm::mat2x4 face;
};
std::map<char, character> char_map;
FT_Library m_ftlib;
FT_Face m_ftface;
GLuint m_VBO, m_VAO;
}
c_atlas ascii;
}
void foo::call_function()
{
//creating all the charactur structs with freetype and store them in the char_map
std::ofstream f("atlas_data.csv", std::ios::openmode::_S_app);
f << "letter;topleft.x;topleft.y;topright.x;topright.y;bottomright.x;bottomright.y;bottomleft.x;bottomleft.y" << std::endl;
for(auto c : alphabet::char_map)
{
std::cout << "b4: " << c.second.offset.x;
c.second.offset /= glm::vec2(aw,ah);
std::cout << "\nafter: " << c.second.offset.x << std::endl;
glm::vec2 ts = c.second.size/glm::vec2(aw,ah);
//couts the right values
uint16_t n = 0;
c.second.face = glm::mat2x4(0.0f);
for(uint16_t i = 0; i < 4; ++i)
{
std::cout << c.first << " at init:\n";
std::cout << c.second.face[0][i] << "\n";
std::cout << c.second.face[1][i] << std::endl;
}
//couts the right values
c.second.face[0][n++] = c.second.offset.x;
c.second.face[0][n++] = c.second.offset.y;
c.second.face[0][n++] = c.second.offset.x+ts.x;
c.second.face[0][n++] = c.second.offset.y;
n = 0;
c.second.face[1][n++]= c.second.offset.x+ts.x;
c.second.face[1][n++] = c.second.offset.y+ts.y;
c.second.face[1][n++] = c.second.offset.x;
c.second.face[1][n++]= c.second.offset.y+ts.y;
for(uint16_t i = 0; i < 4; ++i)
{
std::cout << c.first << " assigned:\n";
std::cout << c.second.face[0][i] << "\n";
std::cout << c.second.face[1][i] << std::endl;
}
//still couts the right values
f << (char)c.first << ";" << c.second.face[0].x << ";" << c.second.face[0].y << ";" << c.second.face[0].z << ";" << c.second.face[0].w << ";" << c.second.face[1].x << ";" << c.second.face[1].y << ";" << c.second.face[1].z << ";" << c.second.face[1].w << std::endl;
//the file also have the right values
}
f.close();
glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
//yet here all the values totally off track, i.e. e^32 or e^-23 (while they should all be between 0.01f - 1.0f)
for(auto i : alphabet::char_map)
{
std::cout << "\ntopleft:\n";
std::cout << "X: " << i.second.face[0].x << " | " << "Y: " << i.second.face[0].x;
std::cout << "\ntopright:\n";
std::cout << "X: " << i.second.face[0].z << " | " << "Y: " << i.second.face[0].w;
std::cout << "\nbotleft:\n";
std::cout << "X: " << i.second.face[1].x << " | " << "Y: " << i.second.face[1].x;
std::cout << "\nbotright:\n";
std::cout << "X: " << i.second.face[1].z << " | " << "Y: " << i.second.face[1].w;
}
}
my mwe:
#include <iostream>
#include <string>
#include "glm/glm.hpp"
#include "GL/gl.h"
#include <map>
struct bin
{
glm::mat2x4 mat;
};
int main( int argc, char *argv[] )
{
std::map<char, bin> bucket;
uint16_t r = 0;
for(uint16_t n = 0; n < 7; ++n)
{
glm::vec4 v = glm::vec4(0.12128f, 0.12412f, 0.15532f, 0.23453f);
bin b;
r = 0;
b.mat[0][r++] = v.x;
b.mat[0][r++] = v.y;
b.mat[0][r++] = v.z;
b.mat[0][r++] = v.w;
r = 0;
b.mat[1][r++] = v.x;
b.mat[1][r++] = v.y;
b.mat[1][r++] = v.z;
b.mat[1][r++] = v.w;
bucket[n] = b;
}
for(auto it : bucket)
{
r = 0;
std::cout << "0:\t" << it.second.mat[0][0] << "\t" << it.second.mat[0][1] << "\t" << it.second.mat[0][2] << "\t" << it.second.mat[0][3] << "\n";
r = 0;
std::cout << "1:\t" << it.second.mat[1][0] << "\t" << it.second.mat[1][1] << "\t" << it.second.mat[1][2] << "\t" << it.second.mat[1][3] << std::endl;
}
return 0;
}
Right now I am totally lost, especially as my mwe works fine.
I am clueless what goes wrong after leaving the for-loop, so thanks for any thought on that!
Indeed, I could just rewrite that section and hope it would work - as my mwe does. But I would like to find out/ get help on finding out what exactly happens between the "assign" for loop and the "retrieve" for loop. Any ideas on that?
I made it work for me now:
Appartenly assigning the values this way:
for(auto c : alphabet::char_map)
{
c.second.face[0][n++] = c.second.offset.x;
//and so on
}
Did not work properly (for whatever reason..)
Changing this into a for(uint16_t i = 32; i < 128; ++i) worked for me. Also it was just the assigning loop, the auto-iterating ofer the map elsewhere works just fine.
I am doing a project named wireless Network Toplogy. It use graphs as a data structure. I have make pointers but am facing heap leak problems. Please can anyone help fix this error? Where to call the delete operator? The cpp code is attached:
#include <string>
#include <ctime>
#include <cstdlib>
#include <iostream>
#include "myGraph.h"
#include "wirelessNetwork.h"
using namespace std;
void main()
{
srand(time(NULL));
/* First part of the experiments */
for (int i = 500; i <= 950; i += 50)
{
wirelessNetwork *g = new wirelessNetwork(10, i);
std::cout << (L"For network with n=") << i << (L": ") << std::endl;
double average = (static_cast<double>(g->graph->numEdges) / (static_cast<double>(g->graph->numVertices)));
std::cout << (L" The average degree is ") << average << std::endl;
std::cout << (L" The maximum degree is ") << g->getMaxDegree() << std::endl;
/* Perform topology control */
g->topologyControl();
std::cout << (L" After topology control: ") << std::endl;
average = (static_cast<double>(g->graph->numEdges)) / (static_cast<double>(g->graph->numVertices));
std::cout << (L" The average degree is ") << average << std::endl;
std::cout << (L" The maximum degree is ") << g->getMaxDegree() << std::endl;
std::cout << std::endl;
}
/* Second part of the experiments */
wirelessNetwork *g = new wirelessNetwork(10, 1000);
std::cout << (L"***********************************") << std::endl;
for (int i = 1; i <= 10; i++)
{
/* Randomly pick two vertices as the source and destination */
int i1 = static_cast<int>(1000 * rand());
int i2 = static_cast<int>(1000 * rand());
string nameTemp = "a";
string node1 = nameTemp + std::to_string(i1);
string node2 = nameTemp + std::to_string(i2);
//ORIGINAL LINE: String[] route = g.compassRouting(node1, node2);
string *route = g->compassRouting(node1, node2);
std::cout << std::endl;
std::cout << (L"Path generated from ") << node1 << (L" to ") << node2 << (L":") << std::endl;
for (int k = 0; k < route->length(); k++)
{
std::cout << (L" ") << route[k];
}
if (node2 != route[route->length() - 1])
{
std::cout << std::endl;
std::cout << (L" No route found...") << std::endl;
}
std::cout << std::endl;
std::cout << (L" Length of the path generated is ") << (route->length() - 1) << std::endl;
}
/* Third part of the experiments */
g->topologyControl();
std::cout << std::endl;
std::cout << (L"***********************************") << std::endl;
std::cout << (L"After topology control...") << std::endl;
for (int i = 1; i <= 10; i++)
{
int i1 = static_cast<int>(1000 * rand());
int i2 = static_cast<int>(1000 * rand());
string nameTemp = "a";
string node1 = nameTemp + std::to_string(i1);
string node2 = nameTemp + std::to_string(i2);
//ORIGINAL LINE: String[] route = g.compassRouting(node1, node2);
string *route = g->compassRouting(node1, node2);
std::cout << std::endl;
std::cout << (L"Path generated from ") << node1 << (L" to ") << node2 << (L":") << std::endl;
for (int k = 0; k < route->length(); k++)
{
std::cout << (L" ") << route[k];
}
if (node2 != route[route->length() - 1])
{
std::cout << std::endl;
std::cout << (L" No route found...");
}
std::cout << std::endl;
std::cout << (L" Length of the path is ") << (route->length() - 1) << std::endl;
}
}
`
One way of identifying when to call delete operator is to check when that pointer variable goes out of scope and to delete it just before it goes out of scope.
void func()
{
ClassA* ptr1 = new ClassA;
//do NULL check for ptr1 and do stuff
delete ptr1;
//here ptr1 will lose scope.
//If not deleted, it will become a memory leak.
}
There can be scenarios where we cannot delete a pointer even if it goes out of scope. For example if we store the new pointer value in some other pointer variable, and the 2nd variable is still not out of scope.
void func(bool IsReady)
{
ClassA* ptr1 = NULL;
if(IsReady)
{
ClassA* ptr2 = new ClassA;
//do stuff
ptr1 = ptr2;
//in this case, even though ptr2 will lose scope outside this block,
//the ptr2 value is assigned to ptr1 which is does not lose scope outside this block.
}
//here ptr1 will still contain the data we copied in the if block,
//else it will be NULL. After use of ptr1 is complete, do a NULL check and delete.
if(ptr1 != NULL)
delete ptr1;
}
In your case I see only the first type, and only 2 places the new operator is called.
Have called the delete operator at the appropriate place for the first case, just before g goes out of scope:
/* First part of the experiments */
for (int i = 500; i <= 950; i += 50)
{
wirelessNetwork *g = new wirelessNetwork(10, i);
std::cout << (L"For network with n=") << i << (L": ") << std::endl;
double average = (static_cast<double>(g->graph->numEdges) / (static_cast<double>(g->graph->numVertices)));
std::cout << (L" The average degree is ") << average << std::endl;
std::cout << (L" The maximum degree is ") << g->getMaxDegree() << std::endl;
/* Perform topology control */
g->topologyControl();
std::cout << (L" After topology control: ") << std::endl;
average = (static_cast<double>(g->graph->numEdges)) / (static_cast<double>(g->graph->numVertices));
std::cout << (L" The average degree is ") << average << std::endl;
std::cout << (L" The maximum degree is ") << g->getMaxDegree() << std::endl;
std::cout << std::endl;
//here g loses scope and does not seem to be stored in any other pointer
delete g;
}
For the second case, call delete at the end of the function.
Hope this solves your issue of when to call the delete in your code.
However, you can avoid calling both new and delete in your case by replacing:
wirelessNetwork *g = new wirelessNetwork(10, i);
with
wirelessNetwork g(10, i);
And for accessing the member variables use g. instead of g->.
For example: g->topologyControl(); should be g.topologyControl();
Hope this answer helps.
The best answer is you shouldn't be using pointers or the new operator. As others suggested, just construct the object directly in the scope where it is needed, so it will be destroyed automatically when it goes out of scope.
A very common (but I think very poor) answer to this question, is to delete the object immediately before the pointer goes out of scope and/or put all your delete operations at the same nesting level as the corresponding new operations. As in this case, when that approach is practical, you almost always didn't need the pointer or the new at all. When it is not practical for the lifetime of the object to match the scope in which it is created, then you really need a pointer and use of the new operator and managing the lifetime of the object is one of the harder aspects of C++ programming. There are no generic answers in those cases. But until you have such a problem, don't create it by pointless use of the new operator.
I'm having a problem where the objects in my array are lost when I pass the array through a constructor. My first guess was that I needed to change it to an array of pointers but that resulted in a segfault. My next guess was that I needed to copy the array data after passing it but that also didn't work. Here's the problem code:
Universe Object:
class Universe {
public:
Star stars[]; int starsLength;
Planet planets[]; int planetsLength;
public:
Universe(Star st[], int stl, Planet pl[], int pll) {
stars < st; starsLength = stl;
planets < pl; planetsLength = pll;
}
Universe() {
}
public:
void render() {
for(int i = 0;i < starsLength;i++) {
//std::cout << "STAR: " << stars[i].location.x << "," << stars[i].location.y << " " << stars[i].size << " " << stars[i].color.r << "," << stars[i].color.g << "," << stars[i].color.b << "\n";
renderCircle(stars[i].location, stars[i].size, stars[i].color);
}
for(int i = 0;i < planetsLength;i++) {
renderCircle(planets[i].location, planets[i].size, planets[i].color);
}
}
void renderCircle(Point location, float size, Color color) {
glBegin(GL_LINES);
glColor3f(color.r,color.g,color.b);
glVertex2f(location.x+size, location.y+size);
glVertex2f(location.x-size, location.y-size);
glVertex2f(location.x-size, location.y+size);
glVertex2f(location.x+size, location.y-size);
glEnd();
}
};
Method that creates the Universe and gives it the array:
Universe buildUniverse(int size, int seed) {
Point bounds = Point{static_cast <float> (size),static_cast <float> (size)}; //0,0 to size,size
int starCount = min(size/10,random(size/5));
int planetCount = min(size/3,random(size));
Star stars[starCount];
Planet planets[planetCount];
//std::cout << "-- Created " << starCount << " stars and " << planetCount << " planets...\n";
for(int i = 0;i < starCount;i++) {
Point location = {random(bounds.x),random(bounds.y)};
Point velocity = {random(bounds.x/100.0f),random(bounds.y/100.0f)};
float size = random(bounds.x/100.0f);
float mass = random(size*(random(1.0f)+0.5f));
Color color = {1.0f,1.0f,1.0f};
stars[i].setStar(location,velocity,size,mass,color);
}
for(int i = 0;i < planetCount;i++) {
Point location = {random(bounds.x),random(bounds.y)};
Point velocity = {random(bounds.x/100.0f),random(bounds.y/100.0f)};
float size = random(bounds.x/100.0f);
float mass = random(size*(random(1.0f)+0.5f));
Color color = {random(1.0f),random(1.0f),random(1.0f)};
planets[i].setPlanet(location,velocity,size,mass,color);
}
Universe uni = {stars, starCount, planets, planetCount};
std::cout << "Star in array: " << stars[0].location.x << "," << stars[0].location.y << " " << stars[0].size << " " << stars[0].color.r << "," << stars[0].color.g << "," << stars[0].color.b << "\n";
std::cout << "Star passed to uni in an array: " << uni.stars[0].location.x << "," << uni.stars[0].location.y << " " << uni.stars[0].size << " " << uni.stars[0].color.r << "," << uni.stars[0].color.g << "," << uni.stars[0].color.b << "\n";
return uni;
}
Output of of the program:
Building universe...
Star in array: 39.922,39.155 0.167611 1,1,8.85715e-39
Star passed to uni in an array: 7.00649e-45,2.24208e-44 0.0282954 5.90446e-39,1.4013e-45,1.4013e-45
Initializing threaded renderer...
Starting simulation...
What am I doing wrong?
First, your code is not valid C++. Declaring empty arrays using [] does not exist in C++.
So the first thing is to turn this into valid C++ that still preserves what you're trying to accomplish. One solution is to use std::vector:
#include <vector>
class Universe {
public:
std::vector<Star> stars;
std::vector<Planet> planets;
public:
Universe(const std::vector<Star>& st,
const std::vector<Planet>& pl) : stars(st), planets(pl) {}
};
Note the replacement of the non-C++ code with std::vector. Also note that we initialize the vectors using the initializer-list.
Last, note that we no longer need to keep the sizes as separate member variables. Why? Because a vector knows its size by calling the vector::size() member function. For example:
for(int i = 0;i < starsLength;i++) {
can be replaced with
for(int i = 0;i < stars.size();i++) {
In your buildUniverse function, use the following changes:
Universe buildUniverse(int size, int seed) {
Point bounds = Point{static_cast <float> (size),static_cast <float> (size)}; //0,0 to size,size
int starCount = min(size/10,random(size/5));
int planetCount = min(size/3,random(size));
std::vector<Star> stars(starCount);
std::vector<Planet> planets(planetCount);
//...
Universe uni(stars, planets);
The rest of the code stays the same. Now, if after the call to create the Universe, you see that the vectors didn't pass the correct information, then look further. The code above conforms to "normal" C++, such that we can go further and figure out the issue.
I have never used CGAL and have got almost no C/C++ experience. But following
Google I have however managed to compile the example "Alpha_shapes_3"
(\CGAL-4.1-beta1\examples\Alpha_shapes_3) on a Windows 7 64bit machine using
visual studio 2010.
Now if we check the source code for the program "ex_alpha_shapes_3" we
notice that a data file called "bunny_1000" is red where the 3d point
cluster resides.
Now my question is how can I change the source code so that after the alpha
shape is computed for the given points, surface mesh of the alpha shape is
saved/wrote in an external file. It can be simply the list of polygons and
their respective 3D vertices. I guess these polygons will be defining the
surface mesh of the alpha shape. If I can do that I can see the output of
the alpha shape generation program in an external tool I am familiar with.
I know this is very straightforward but I could not figure this out with my
limited knowledge of CGAL.
I know you gueys have the code but I am pasting it again for completion.
#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
#include <CGAL/Delaunay_triangulation_3.h>
#include <CGAL/Alpha_shape_3.h>
#include <fstream>
#include <list>
#include <cassert>
typedef CGAL::Exact_predicates_inexact_constructions_kernel Gt;
typedef CGAL::Alpha_shape_vertex_base_3<Gt> Vb;
typedef CGAL::Alpha_shape_cell_base_3<Gt> Fb;
typedef CGAL::Triangulation_data_structure_3<Vb,Fb> Tds;
typedef CGAL::Delaunay_triangulation_3<Gt,Tds> Triangulation_3;
typedef CGAL::Alpha_shape_3<Triangulation_3> Alpha_shape_3;
typedef Gt::Point_3 Point;
typedef Alpha_shape_3::Alpha_iterator Alpha_iterator;
int main()
{
std::list<Point> lp;
//read input
std::ifstream is("./data/bunny_1000");
int n;
is >> n;
std::cout << "Reading " << n << " points " << std::endl;
Point p;
for( ; n>0 ; n--) {
is >> p;
lp.push_back(p);
}
// compute alpha shape
Alpha_shape_3 as(lp.begin(),lp.end());
std::cout << "Alpha shape computed in REGULARIZED mode by default"
<< std::endl;
// find optimal alpha value
Alpha_iterator opt = as.find_optimal_alpha(1);
std::cout << "Optimal alpha value to get one connected component is "
<< *opt << std::endl;
as.set_alpha(*opt);
assert(as.number_of_solid_components() == 1);
return 0;
}
After searching a lot in the internet I found that probably we need to use something like
std::list<Facet> facets;
alpha_shape.get_alpha_shape_facets
(
std::back_inserter(facets),Alpha_shape::REGULAR
);
But I am still completely clueless how to use this in the above code!
As documented here, a facet is a pair (Cell_handle c,int i) defined as the facet in c opposite to the vertex of index i.
On this page, you have the description of how the vertex indices of a cell are.
In the following code sample, I added a small output that prints an OFF file on cout by duplicating the vertices. To do something clean, you can either use a std::map<Alpha_shape_3::Vertex_handle,int> to associate a unique index per vertex or add an info to the vertices like in those examples.
/// collect all regular facets
std::vector<Alpha_shape_3::Facet> facets;
as.get_alpha_shape_facets(std::back_inserter(facets), Alpha_shape_3::REGULAR);
std::stringstream pts;
std::stringstream ind;
std::size_t nbf=facets.size();
for (std::size_t i=0;i<nbf;++i)
{
//To have a consistent orientation of the facet, always consider an exterior cell
if ( as.classify( facets[i].first )!=Alpha_shape_3::EXTERIOR )
facets[i]=as.mirror_facet( facets[i] );
CGAL_assertion( as.classify( facets[i].first )==Alpha_shape_3::EXTERIOR );
int indices[3]={
(facets[i].second+1)%4,
(facets[i].second+2)%4,
(facets[i].second+3)%4,
};
/// according to the encoding of vertex indices, this is needed to get
/// a consistent orienation
if ( facets[i].second%2==0 ) std::swap(indices[0], indices[1]);
pts <<
facets[i].first->vertex(indices[0])->point() << "\n" <<
facets[i].first->vertex(indices[1])->point() << "\n" <<
facets[i].first->vertex(indices[2])->point() << "\n";
ind << "3 " << 3*i << " " << 3*i+1 << " " << 3*i+2 << "\n";
}
std::cout << "OFF "<< 3*nbf << " " << nbf << " 0\n";
std::cout << pts.str();
std::cout << ind.str();
Here is my code, which outputs vtk file for visualization in Paraview. Comparing with slorior's solutions, no duplicated points are saved in the file. But my code is just for the visualization, if you need to figure out the exterior or interior simplexes, you should modify the code to get these results.
void writevtk(Alpha_shape_3 &as, const std::string &asfile) {
// http://cgal-discuss.949826.n4.nabble.com/Help-with-filtration-and-filtration-with-alpha-values-td4659524.html#a4659549
std::cout << "Information of the Alpha_Complex:\n";
std::vector<Alpha_shape_3::Cell_handle> cells;
std::vector<Alpha_shape_3::Facet> facets;
std::vector<Alpha_shape_3::Edge> edges;
// tetrahedron = cell, they should be the interior, it is inside the 3D space
as.get_alpha_shape_cells(std::back_inserter(cells), Alpha_shape_3::INTERIOR);
// triangles
// for the visualiization, don't need regular because tetrahedron will show it
//as.get_alpha_shape_facets(std::back_inserter(facets), Alpha_shape_3::REGULAR);
as.get_alpha_shape_facets(std::back_inserter(facets), Alpha_shape_3::SINGULAR);
// edges
as.get_alpha_shape_edges(std::back_inserter(edges), Alpha_shape_3::SINGULAR);
std::cout << "The alpha-complex has : " << std::endl;
std::cout << cells.size() << " cells as tetrahedrons" << std::endl;
std::cout << facets.size() << " triangles" << std::endl;
std::cout << edges.size() << " edges" << std::endl;
size_t tetra_num, tri_num, edge_num;
tetra_num = cells.size();
tri_num = facets.size();
edge_num = edges.size();
// vertices: points <-> id
std::map<Point, size_t> points;
size_t index = 0;
// finite_.. is from DT class
for (auto v_it = as.finite_vertices_begin(); v_it != as.finite_vertices_end(); v_it++) {
points[v_it->point()] = index;
index++;
}
// write
std::ofstream of(asfile);
of << "# vtk DataFile Version 2.0\n\nASCII\nDATASET UNSTRUCTURED_GRID\n\n";
of << "POINTS " << index << " float\n";
for (auto v_it = as.finite_vertices_begin(); v_it != as.finite_vertices_end(); v_it++) {
of << v_it->point() << std::endl;
}
of << std::endl;
of << "CELLS " << tetra_num + tri_num + edge_num << " " << 5 * tetra_num + 4 * tri_num + 3 * edge_num << std::endl;
for (auto cell:cells) {
size_t v0 = points.find(cell->vertex(0)->point())->second;
size_t v1 = points.find(cell->vertex(1)->point())->second;
size_t v2 = points.find(cell->vertex(2)->point())->second;
size_t v3 = points.find(cell->vertex(3)->point())->second;
of << "4 " << v0 << " " << v1 << " " << v2 << " " << v3 << std::endl;
}
// https://doc.cgal.org/latest/TDS_3/classTriangulationDataStructure__3.html#ad6a20b45e66dfb690bfcdb8438e9fcae
for (auto tri_it = facets.begin(); tri_it != facets.end(); ++tri_it) {
of << "3 ";
auto tmp_tetra = tri_it->first;
for (int i = 0; i < 4; i++) {
if (i != tri_it->second) {
of << points.find(tmp_tetra->vertex(i)->point())->second << " ";
}
}
of << std::endl;
}
// https://doc.cgal.org/latest/TDS_3/classTriangulationDataStructure__3.html#af31db7673a6d7d28c0bb90a3115ac695
for (auto e : edges) {
of << "2 ";
auto tmp_tetra = e.get<0>();
int p1, p2;
p1 = e.get<1>();
p2 = e.get<2>();
of << points.find(tmp_tetra->vertex(p1)->point())->second << " "
<< points.find(tmp_tetra->vertex(p2)->point())->second << std::endl;
}
of << std::endl;
of << "CELL_TYPES " << tetra_num + tri_num + edge_num << std::endl;
for (int i = 0; i < tetra_num; i++) {
of << "10 ";
}
for (int i = 0; i < tri_num; i++) {
of << "5 ";
}
for (int i = 0; i < edge_num; i++) {
of << "3 ";
}
of << std::endl;
of.close();
}