Definition of classes and functions for 2d cell complexes. More...
Classes | |
class | DivisionData |
Class holding the data needed to actually divide a cell. More... | |
class | InModelDivisionParam |
Class used to define the division parameters from within the model class. More... | |
class | VVComplex |
Class handling the representation and development of 2D cell complex. More... | |
class | VVComplexGraph |
Definition of the bipartite graph with specialized methods for the complex graph. More... | |
Typedefs | |
Types defined by EXPORT_COMPLEX_TYPES(VVComplex) | |
typedef VVComplex::cell | cell |
Type of a cell. | |
typedef VVComplex::cell_edge | cell_edge |
Type of an edge in the cell graph. | |
typedef VVComplex::cell_graph | cell_graph |
Type of the cell graph. | |
typedef VVComplex::cell_junction_edge | cell_junction_edge |
Type of an edge from a cell to a junction. | |
typedef VVComplex::complex_graph | complex_graph |
Type of the complex graph (i.e. linking cells to junctions and vice-versa). | |
typedef VVComplex::const_cell_edge | const_cell_edge |
Type of an edge in the cell graph. | |
typedef VVComplex::const_cell_junction_edge | const_cell_junction_edge |
Type of an edge from a cell to a junction. | |
typedef VVComplex::const_junction_cell_edge | const_junction_cell_edge |
Type of an edge from a junction to a cell. | |
typedef VVComplex::const_wall | const_wall |
Type of a wall (i.e. an edge in the wall graph). | |
typedef VVComplex::junction | junction |
Type of a junction. | |
typedef VVComplex::junction_cell_edge | junction_cell_edge |
Type of an edge from a junction to a cell. | |
typedef VVComplex::wall | wall |
Type of a wall (i.e. an edge in the wall graph). | |
typedef VVComplex::wall_graph | wall_graph |
Type of the wall graph. | |
Functions | |
template<typename Complex > | |
std::vector< typename Complex::junction > | contour (const Complex &T) |
template<typename CplxGraph > | |
void | create_cplxgraph_methods (CplxGraph &G) |
template<class VVComplex > | |
void | FindCenter (const typename VVComplex::cell &c, VVComplex &T) |
Compute the position of the center of a cell. | |
template<class VVComplex > | |
Point3d | findCenter (const typename VVComplex::cell &c, VVComplex &T) |
Return the position of the center of a cell. | |
template<class VVComplex > | |
DivisionData< typename VVComplex::junction_content > | findDivisionPoints (const typename VVComplex::cell &c, VVComplex &T, const InModelDivisionParam ¶m) |
Implementation of the in model division scheme. | |
template<typename VVComplex > | |
void | FindOppositeWall (const typename VVComplex::cell &c, DivisionData< typename VVComplex::junction_content > &result, VVComplex &T, double cellWallMin, bool strictCellWallMin) |
Find the wall opposite to the point already selected. | |
bool | FindWallMin (Point3d &v, const Point3d &v1, const Point3d &v2, double mw, double *displacement=0) |
Move point v to avoid segment borders. | |
template<typename V1 , typename V2 , typename E1 , typename E2 , bool compact> | |
bool | serialization (storage::VVEStorage &store, VVComplexGraph< V1, V2, E1, E2, compact > &G) |
template<typename VVComplex > | |
void | testDivisionOnVertices (const typename VVComplex::cell &c, DivisionData< typename VVComplex::junction_content > &result, VVComplex &T, double epsilon) |
Test if the division point in result is close enough to one of the extremum. |
Definition of classes and functions for 2d cell complexes.
This namespace mainly provides a VVComplex class template handling the creation and evolution of cell complexes.
A 2D cell complex is defined by a set of regions of the space such that two regions neighbor regions share a continuous set of 1D cells (i.e. line segments). We represent here the regions as a bipartite graph where the two group of vertices are the cells and the junctions. The junctions neighbors of a cell are ordered such that two junctions next to each other are linked by a wall. The cells neighbors of a junction are ordered such that two cells sharing a wall will be next to each other. In addition, two useful graphs are maintained: the cell graph, linking cells sharing a wall; and the wall graph, linking junctions parts of the same wall.
Graphs and ordering in 2D cell complexes.
A 2D cell complex needs 6 types and the type of the model. The 6 types are:
For the rest of this page, these types will be called:
CellContent
JunctionContent
WallContent
CellEdgeContent
CellJunctionEdgeContent
JunctionCellEdgeContent
Then, if the type of the model is MyModel
, the cell complex is defined as:
typedef vvcomplex::VVComplex<MyModel,CellContent,JunctionContent,WallContent,CellEdgeContent,CellJunctionEdgeContent,JunctionCellEdgeContent> VVComplex;
All edge content types are optional and defaults to an empty class. Once created, a macro is provided to help exporting all the types:
EXPORT_COMPLEX_TYPES(VVComplex);
is equivalent to:
typedef VVComplex::cell cell; typedef VVComplex::junction junction; typedef VVComplex::wall wall; typedef VVComplex::cell_edge cell_edge; typedef VVComplex::cell_junction_edge cell_junction_edge; typedef VVComplex::junction_cell_edge junction_cell_edge; typedef VVComplex::const_wall const_wall; typedef VVComplex::const_cell_edge const_cell_edge; typedef VVComplex::const_cell_junction_edge const_cell_junction_edge; typedef VVComplex::const_junction_cell_edge const_junction_cell_edge; typedef VVComplex::complex_graph complex_graph; typedef VVComplex::cell_graph cell_graph; typedef VVComplex::wall_graph wall_graph;
Other macro, for partial export or for import in template function or class, are defined in complex.h
2D cell complexes are created by instantiating the type defined in the previous section, specifying at construction time the pointer on the current model. In a function, the declaration will look like:
VVComplex T(&model);
As in the model class, the declaration will look like:
Once created, the first cell can be created by creating first junctions and a cell, then using the method VVComplex::addCell. For example to create a square cell (supposing the junctions have a pos
attributes holding a 3D vector):
junction j1, j2, j3, j4; cell c; j1->pos = Point3d(0,0,0); j2->pos = Point3d(1,0,0); j3->pos = Point3d(1,1,0); j4->pos = Point3d(0,1,0); T.addCell(c, j1, j2, j3, j4);
Another way is to use one of the factory provided in the complex_factory namespace.
You can then continue to edit the cell complex using the VVComplex::addCell, VVComplex::divideCell or VVComplex::deleteCell methods. For finer edition, you can use VVComplex::splitWall or VVComplex::deleteJunction.
For example, given a division algorithm using parameters DivisionParams
, you can divide a cell with:
T.divideCell(c, DivisionParams(), c);
During the division, the updateFromOld
method of the model object will be called. During that called, the members VVComplex::OldS, VVComplex::OldC and VVComplex::OldW will be available with the part of the graph saved (in the example, only the cell c
and its junctions are saved). For example, if you want to increase a generation counter, both for the junctions and the cells:
void updateFromOld(const cell& cl, const cell& cr, const cell& c, const VVComplex::division_data& ddata, VVComplex& T) { cl->generation = cr->generation = c->generation+1; forall( const junction& j , T.S.neighbors(cl)) { if(!T.OldW.contains(j)) j->generation = cl->generation; } }
Here, the generation of a junction is defined as the generation of the cells for which it was created.
At last, it can be useful to know the two ways to iterate over cells and junctions. These notations are pretty much equivalent:
forall(const cell& c,T.C) { ... } forall_named(const cell& c, T.S, cells) { ... } forall(const junction& j,T.W) { ... } forall_named(const junction& j, T.S, junctions) { ... }
The difference is in performance. It is always better to iterate over the graph used in the loop itself.
The following methods must exist in the model class to be able to use the 2d cell complexes.
Returns the position of a cell or a junction.
void setPosition(const cell& c, const Point3d& pos); void setPosition(const junction& j, const Point3d& pos);
Set the position of a cell or a junction.
void setPositionHint(const junction& j, const junction& j1, const junction& j2, double ratio);
Used to preset the position of a newly formed junction during cell division.
Point3d normal(const cell& c);
Returns the normal to a cell.
void updateFromOld(const cell& cl, const cell& cr, const cell& c, const VVComplex::division_data& ddata, VVComplex& T);
Method called after the division occured. cl
and cr
are the two newly created cells. c
is the previous cell. ddata
is the data used for the division. The complex has the old graphs setup with whatever was required to be saved during division.
Implementing a cell division algorithm consists in implementing the function:
template <class VVComplex> vvcomplex::DivisionData<typename VVComplex::junction_content> findDivisionPoints(const typename VVComplex::cell& c, VVComplex& T, const MyDivisionParam& param);
with MyDivisionParams
being a type containing the parameters needed for the division and, most importantly, unique for the division as it will identify which division algorithm to use.
This function must not change the graph! Within the function, it might be convenient to use the macros IMPORT_COMPLEX_TYPES() and IMPORT_COMPLEX_MODEL() to access easily the structure of the cell complex. A good example is the very simple the division algorithm using vvcomplex::InModelDivisionParam as parameters: vvcomplex::findDivisionPoints().
Extending the cell complex can simply consist in adding methods (like the tissue::Tissue class does) or it can also consist in maintaining extra structures, like extra graphs. To allow extra graphs to be maintained without user input, virtual functions can be redefined:
Each of these method must call its parent's method to ensure all graphs are properly maintained.
Also, it is very important for the complex to know the instantiated class. For that purpose, the last argument of the VVComplex class is the leaf class instantiated. If you want to provide a class someone else may inherit from, it is recommended to look at the Tissue class declaration and achieve something similar.
Here is a commented example of a model using the cell complex.
First, we start by defining the data structure for the cell and the vertices. As we don't label the edges, we ignore them for now.
#include <vve.h> #include <geometry/geometry.h> #include <algorithms/cellsystem.h> #include <geometry/area.h> using geometry::Point3d; struct CellContent { Point3d position; int id; }; struct JunctionContent { Point3d position; };
Now, we declare the complex data structure. As it needs to know about the model class used, we need the place a forward declaration of the model class. At last, we export the types defined in the complex class (i.e. the cell, junction, cell_edge, ... types).
class MyModel; typedef vvcomplex::VVComplex<MyModel, CellContent, JunctionContent> MyComplex; EXPORT_COMPLEX_TYPES(MyComplex);
Now, the model should contain an instance of MyComplex and the methods required by the complex class.
struct MyModel : public Model { MyComplex T; Point3d position(const cell& c) { return c->position;} Point3d position(const junction& j) { return j->position;} Point3d setPosition(const cell& c, const Point3d& pos) { c->position = pos; } Point3d setPosition(const junction& j, const Point3d& pos) { j->position = pos; } void setPositionHint(const junction&, const junction&, const junction&, double) {} Point3d normal(const cell& ) { return Point3d(0,0,1); } Point3d normal(const junction& ) { return Point3d(0,0,1); } void updateFromOld(const cell& cl, const cell& cr, const cell& c, const MyComplex::division_data&, MyComplex& ) { cr->id = 2*c->id; cl->id = 2*c->id+1; }
Now, to use the structure, we need to initialise it in the constructor, and to add its first cell.
MyModel(QObject *parent) : Model(parent) , T(this) { // Add a square cell junction j1, j2, j3, j4; j1->position = Point3d(0,0,0); j2->position = Point3d(1,0,0); j3->position = Point3d(1,1,0); j4->position = Point3d(0,1,0); cell c; c->id = 1; c->position = Point3d(.5, .5, 0); T.addCell(c, j1, j2, j3, j4); }
For the step, we'll use the cell system division mechanism alternating the normal from horizontal to vertical.
void step() { static bool horizontal = false; horizontal = !horizontal; cell_system::CellSystemDivisionParams params(Point3d(1,0,0)); if(horizontal) params.angle = M_PI/2; std::vector<cell> cells(T.C.begin(), T.C.end()); // Copy the cells forall(const cell& c,cells) { T.divideCell(c, params); } } // This method is needed for the cell system division double area(const cell& c) { std::vector<Point3d> poss; forall( const junction& j , T.S.neighbors(c)) { poss.push_back(j->position); } return geometry::polygonArea(poss); }
At last, we draw the result simply with lines for the walls.
void draw(Viewer* viewer) { Point3d pmin(HUGE_VAL,HUGE_VAL,HUGE_VAL); Point3d pmax = -pmin; glBegin(GL_LINES); glColor3f(1,1,1); forall(const junction& j,T.W) { const Point3d& p = j->position; if(p.x() < pmin.x()) pmin.x() = p.x(); if(p.y() < pmin.y()) pmin.y() = p.y(); if(p.z() < pmin.z()) pmin.z() = p.z(); if(p.x() > pmax.x()) pmax.x() = p.x(); if(p.y() > pmax.y()) pmax.y() = p.y(); if(p.z() > pmax.z()) pmax.z() = p.z(); forall( const junction& n , T.W.neighbors(j)) { glVertex3dv(p.c_data()); glVertex3dv(n->position.c_data()); } } glEnd(); viewer->setSceneBoundingBox(Vec(pmin), Vec(pmax)); } }; DEFINE_MODEL(MyModel);
typedef VVComplex::cell vvcomplex::cell |
typedef VVComplex::wall vvcomplex::wall |
void vvcomplex::FindCenter | ( | const typename VVComplex::cell & | c, | |
VVComplex & | T | |||
) | [inline] |
Compute the position of the center of a cell.
This function compute the barycenter of a cell using the position of its junctions.
Definition at line 1914 of file complex.h.
References findCenter(), and IMPORT_COMPLEX_MODEL.
01915 { 01916 IMPORT_COMPLEX_MODEL(VVComplex, T); 01917 const Point3d& cpos = findCenter(c, T); 01918 model.setPosition(c, cpos); 01919 }
Point3d vvcomplex::findCenter | ( | const typename VVComplex::cell & | c, | |
VVComplex & | T | |||
) | [inline] |
Return the position of the center of a cell.
This function compute the barycenter of a cell using the position of its junctions.
Definition at line 1872 of file complex.h.
References forall, IMPORT_COMPLEX_MODEL, IMPORT_COMPLEX_VERTICES, graph::VVBiGraph< Vertex1Content, Vertex2Content, Edge1Content, Edge2Content_, compact >::neighbors(), graph::VVBiGraph< Vertex1Content, Vertex2Content, Edge1Content, Edge2Content_, compact >::prevTo(), vvcomplex::VVComplex< Model, CellContent, JunctionContent, WallContent, CellEdgeContent, CellJunctionContent, JunctionCellContent, compact, LeafClass >::S, geometry::triangleArea(), and graph::VVBiGraph< Vertex1Content, Vertex2Content, Edge1Content, Edge2Content_, compact >::valence().
Referenced by FindCenter().
01873 { 01874 IMPORT_COMPLEX_VERTICES(VVComplex); 01875 IMPORT_COMPLEX_MODEL(VVComplex, T); 01876 // First get average of points as estimate to center 01877 Point3d cest; 01878 forall( const junction& n , T.S.neighbors(c)) 01879 { 01880 cest += model.position(n); 01881 } 01882 cest /= T.S.valence(c); 01883 // Average Area * midpoint of each triangle 01884 Point3d cpos; 01885 double sum = 0; 01886 forall( const junction& m , T.S.neighbors(c)) 01887 { 01888 const junction& n = T.S.prevTo(c, m); 01889 const Point3d& mpos = model.position(m); 01890 const Point3d& npos = model.position(n); 01891 double ta = geometry::triangleArea(mpos, npos, cest); 01892 cpos += ta * (cest + ((mpos - cest) + (npos - cest))/3.0); 01893 sum += ta; 01894 } 01895 cpos /= sum; 01896 01897 return cpos; 01898 }
DivisionData<typename VVComplex::junction_content> vvcomplex::findDivisionPoints | ( | const typename VVComplex::cell & | c, | |
VVComplex & | T, | |||
const InModelDivisionParam & | param | |||
) | [inline] |
Implementation of the in model division scheme.
Implementation:
template <class VVComplex> DivisionData<typename VVComplex::junction_content> findDivisionPoints(const typename VVComplex::cell& c, VVComplex& T, const InModelDivisionParam& param) { IMPORT_COMPLEX_MODEL(VVComplex, T); return model.divisionParameters(c, T, param); }
Definition at line 1745 of file complex.h.
References IMPORT_COMPLEX_MODEL.
01748 { 01749 IMPORT_COMPLEX_MODEL(VVComplex, T); 01750 return model.divisionParameters(c, T, param); 01751 }
void vvcomplex::FindOppositeWall | ( | const typename VVComplex::cell & | c, | |
DivisionData< typename VVComplex::junction_content > & | result, | |||
VVComplex & | T, | |||
double | cellWallMin, | |||
bool | strictCellWallMin | |||
) | [inline] |
Find the wall opposite to the point already selected.
This function suppose the first division point was found. The other wall is the one intersecting the line starting from the point result.pu
and going through the cell center. Then, the point is displaced to endure the minimum size of a wall.
[in] | c | Cell to divide |
[in,out] | result | Structure to update with the opposite wall |
[in] | S | VV graph |
[in] | cellWallMin | minimum size of a wall |
[in] | model | Model using the algorithm |
Definition at line 1786 of file complex.h.
References FindWallMin(), forall, IMPORT_COMPLEX_MODEL, IMPORT_COMPLEX_VERTICES, graph::VVBiGraph< Vertex1Content, Vertex2Content, Edge1Content, Edge2Content_, compact >::neighbors(), graph::VVBiGraph< Vertex1Content, Vertex2Content, Edge1Content, Edge2Content_, compact >::nextTo(), geometry::planeLineIntersection(), vvcomplex::DivisionData< JunctionContent >::pu, vvcomplex::DivisionData< JunctionContent >::pv, vvcomplex::VVComplex< Model, CellContent, JunctionContent, WallContent, CellEdgeContent, CellJunctionContent, JunctionCellContent, compact, LeafClass >::S, vvcomplex::DivisionData< JunctionContent >::u1, and vvcomplex::DivisionData< JunctionContent >::v1.
Referenced by tissue::findDivisionPoints().
01790 { 01791 IMPORT_COMPLEX_VERTICES(VVComplex); 01792 IMPORT_COMPLEX_MODEL(VVComplex, T); 01793 double mindis = HUGE_VAL; 01794 const Point3d& cnml = model.normal(c); 01795 Point3d minu = result.pu; 01796 const Point3d& cpos = model.position(c); 01797 junction p_u1 = result.u1; 01798 junction p_u2 = T.S.nextTo(c, p_u1); 01799 forall( const junction& tv1 , T.S.neighbors(c)) 01800 { 01801 const junction& tv2 = T.S.nextTo(c, tv1); 01802 const Point3d& v1 = model.position(tv1); 01803 const Point3d& v2 = model.position(tv2); 01804 if(tv2 == p_u2) 01805 { 01806 continue; 01807 } 01808 // Keep uc close to same plane as v1v2 01809 Point3d uc = cpos - minu; 01810 uc -= (uc * cnml) * cnml; 01811 Point3d n = cnml^uc; 01812 uc += minu; 01813 double s; 01814 Point3d v; 01815 bool found = geometry::planeLineIntersection(v, s, uc, n, v1, v2); 01816 if(found) 01817 { 01818 double dis; 01819 if(FindWallMin(v, v1, v2, cellWallMin, &dis) or not strictCellWallMin) 01820 { 01821 if(dis < mindis) 01822 { 01823 mindis = dis; 01824 result.pv = v; 01825 result.v1 = tv1; 01826 } 01827 if(dis == 0) break; 01828 } 01829 } 01830 } 01831 }
bool vvcomplex::FindWallMin | ( | Point3d & | v, | |
const Point3d & | v1, | |||
const Point3d & | v2, | |||
double | mw, | |||
double * | displacement = 0 | |||
) |
Move point v
to avoid segment borders.
Find out how to move the point v
on the segment [v1,v2]
to be at least at a distance mw
from the v1
and v2
.
[in,out] | v | Point to displace if needed |
[in] | v1 | Extremity of the segment |
[in] | v2 | Extremity of the segment |
[in] | mw | Minimum distance from the extremities |
[out] | displacement | If non-null, it is set to be the displacement of the point. |
Referenced by FindOppositeWall().
void vvcomplex::testDivisionOnVertices | ( | const typename VVComplex::cell & | c, | |
DivisionData< typename VVComplex::junction_content > & | result, | |||
VVComplex & | T, | |||
double | epsilon | |||
) | [inline] |
Test if the division point in result is close enough to one of the extremum.
If so, setup the division to go through the vertex.
Definition at line 1838 of file complex.h.
References vvcomplex::DivisionData< JunctionContent >::divide_at_u1, vvcomplex::DivisionData< JunctionContent >::divide_at_v1, IMPORT_COMPLEX_MODEL, graph::VVBiGraph< Vertex1Content, Vertex2Content, Edge1Content, Edge2Content_, compact >::nextTo(), vvcomplex::DivisionData< JunctionContent >::pu, vvcomplex::DivisionData< JunctionContent >::pv, vvcomplex::VVComplex< Model, CellContent, JunctionContent, WallContent, CellEdgeContent, CellJunctionContent, JunctionCellContent, compact, LeafClass >::S, vvcomplex::DivisionData< JunctionContent >::u1, and vvcomplex::DivisionData< JunctionContent >::v1.
Referenced by tissue::findDivisionPoints(), and cell_system::findDivisionPoints().
01842 { 01843 IMPORT_COMPLEX_MODEL(VVComplex, T); 01844 double eps_sq = epsilon*epsilon; 01845 if(util::normsq(result.pu-model.position(result.u1)) < eps_sq) 01846 { 01847 result.divide_at_u1 = true; 01848 } 01849 else if(util::normsq(result.pu-model.position(T.S.nextTo(c,result.u1))) < eps_sq) 01850 { 01851 result.u1 = T.S.nextTo(c,result.u1); 01852 result.divide_at_u1 = true; 01853 } 01854 if(util::normsq(result.pv-model.position(result.v1)) < eps_sq) 01855 { 01856 result.divide_at_v1 = true; 01857 } 01858 else if(util::normsq(result.pv-model.position(T.S.nextTo(c,result.v1))) < eps_sq) 01859 { 01860 result.v1 = T.S.nextTo(c,result.v1); 01861 result.divide_at_v1 = true; 01862 } 01863 }