VPTissue Reference Manual
SliceItem.cpp
Go to the documentation of this file.
1 /*
2  * Copyright 2011-2016 Universiteit Antwerpen
3  *
4  * Licensed under the EUPL, Version 1.1 or as soon they will be approved by
5  * the European Commission - subsequent versions of the EUPL (the "Licence");
6  * You may not use this work except in compliance with the Licence.
7  * You may obtain a copy of the Licence at: http://ec.europa.eu/idabc/eupl5
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the Licence is distributed on an "AS IS" basis,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the Licence for the specific language governing
13  * permissions and limitations under the Licence.
14  */
20 #include "SliceItem.h"
21 
22 #include "bio/Cell.h"
23 #include "bio/Edge.h"
24 #include "bio/NeighborNodes.h"
25 #include "bio/Node.h"
27 
28 #include <QLineF>
29 #include <utility>
30 
31 class QColor;
32 class QGraphicsScene;
33 namespace SimPT_Shell { class EditControlLogic; }
34 namespace SimPT_Sim { class Wall; }
35 
36 namespace SimPT_Editor {
37 
38 using namespace SimPT_Sim;
39 using namespace SimPT_Sim::Container;
40 
41 SliceItem::SliceItem(const QPolygonF& polygon, const QLineF& cut, std::shared_ptr<EditControlLogic> tissue, QGraphicsScene* scene)
42  : QGraphicsPolygonItem(polygon), m_cut(cut), m_tissue(tissue), m_scene(scene)
43 {
44  setZValue(0);
45  setBrush(QColor(220,220,220,255));
46  setAcceptHoverEvents(true);
47  setFlag(GraphicsItemFlag::ItemIsSelectable);
48 }
49 
51 
52 QVariant SliceItem::itemChange(GraphicsItemChange change, const QVariant& value)
53 {
54  if (change == QGraphicsItem::ItemSelectedHasChanged && isSelected()) {
55  TruncateLeaf();
56  emit Truncated();
57  }
58  return QGraphicsPolygonItem::itemChange(change, value);
59 }
60 
61 void SliceItem::hoverEnterEvent(QGraphicsSceneHoverEvent* )
62 {
63  setBrush(QColor(51, 102, 0, 255));
64 }
65 
66 void SliceItem::hoverLeaveEvent(QGraphicsSceneHoverEvent* )
67 {
68  setBrush(QColor(220,220,220,255));
69 }
70 
71 void SliceItem::TruncateLeaf()
72 {
73  std::list<Cell*> cellsToBeDeleted;
74  std::list<Cell*> cellsToBeSliced;
75  auto mesh = m_tissue->GetMesh()->GetMesh();
76 
77  for (auto cell : m_tissue->GetCells()) {
78  bool keep = false;
79  bool fullyContained = true;
80  for (Node* node : cell->GetNodes()) {
81  QPointF point((*node)[0], (*node)[1]);
82  if (polygon().containsPoint(point, Qt::OddEvenFill) || polygon().contains(point)) {
83  keep = true;
84  }
85  else {
86  fullyContained = false;
87  }
88  }
89  if (!keep) {
90  cellsToBeDeleted.push_back(cell);
91  }
92  else if (!fullyContained) {
93  cellsToBeSliced.push_back(cell);
94  }
95  }
96 
97  std::vector<QPointF> corners = polygon().toStdVector();
98  std::map<Cell*, std::list<Node*>> newNodes;
99  std::map<Node*, std::list<NeighborNodes>> newOwners;
100  for (auto cell : cellsToBeSliced) {
101  newNodes.insert(std::make_pair(cell, std::list<Node*>()));
102  auto node = make_const_circular(cell->GetNodes());
103  do {
104  Node* n1 = *node;
105  Node* n2 = *next(node);
106  QLineF line((*n1)[0], (*n1)[1], (*n2)[0], (*n2)[1]);
107  QPointF intersection;
108 
109  if (m_cut.intersect(line, &intersection) == QLineF::BoundedIntersection) {
110  if (find(newNodes[cell].begin(), newNodes[cell].end(), *node) == newNodes[cell].end()) {
111  if (intersection == QPointF((*n1)[0], (*n1)[1])) {
112  newNodes[cell].push_back(n1);
113  }
114  else if (intersection == QPointF((*n2)[0], (*n2)[1])) {
115  newNodes[cell].push_back(n2);
116  }
117  else {
118  auto owner = find_if(newOwners.begin(), newOwners.end(),
119  [intersection](std::pair<Node*, std::list<NeighborNodes>> entry)
120  {return intersection == QPointF((*entry.first)[0], (*entry.first)[1]);});
121  if (owner == newOwners.end()) {
122  Edge edge(n1, n2);
123  assert(mesh->GetEdgeOwners(edge).size() > 0 && "Edge doesn't exist in the mesh.");
124 
125  auto newNode = mesh->BuildNode({{intersection.x(), intersection.y(), 0}}, mesh->IsAtBoundary(&edge));
126  assert(newOwners.find(newNode) == newOwners.end() && "The created node already existed.");
127 
128  newOwners[newNode] = std::list<NeighborNodes>();
129  for (NeighborNodes neighbor : mesh->GetEdgeOwners(edge)) {
130  if (std::find_if(newOwners[newNode].begin(), newOwners[newNode].end(),
131  [neighbor](NeighborNodes nb) {return nb.GetCell() == neighbor.GetCell();})
132  == newOwners[newNode].end()) {
133 
134  newOwners[newNode].push_back(
135  NeighborNodes(neighbor.GetCell(), n1, n2));
136  }
137  }
138  newNodes[cell].push_back(newNode);
139  }
140  else {
141  newNodes[cell].push_back(owner->first);
142  }
143  }
144  }
145  }
146  } while (++node != cell->GetNodes().begin());
147  }
148 
149  for (auto entry : newOwners) {
150  for (auto nb : entry.second) {
151  mesh->AddNodeToCell(nb.GetCell(), entry.first, nb.GetNb1(), nb.GetNb2());
152  }
153  }
154 
155  // Update node owning walls.
156  std::list<Wall*> tmpWalls;
157  for (auto cell : cellsToBeSliced) {
158  for (Wall* w : cell->GetWalls()) {
159  if (std::find(tmpWalls.begin(), tmpWalls.end(), w) == tmpWalls.end()) {
160  tmpWalls.push_back(w);
161  mesh->UpdateNodeOwningWalls(w);
162  }
163  }
164  }
165 
166 
167  // Lambda to compare two nodes by their distance to the first endpoint of the cut.
168  auto cmp = [this](Node* p1, Node* p2) {
169  return pow(m_cut.p1().x() - (*p1)[0], 2) + pow(m_cut.p1().y() - (*p1)[1], 2)
170  < pow(m_cut.p1().x() - (*p2)[0], 2) + pow(m_cut.p1().y() - (*p2)[1], 2);
171  };
172 
173  // Lambda to check whether a point is contained in the polygon that contained the point.
174  auto contains_point = [this](const QPointF& p) {
175  QPolygonF rect(QRectF(p.x() - g_accuracy / 4, p.y() - g_accuracy / 4, g_accuracy / 2, g_accuracy / 2)); //Lower-bound to get the diagonal less than g_accuracy.
176  return (polygon().contains(p) || polygon().containsPoint(p, Qt::OddEvenFill) || polygon().intersected(rect).size() > 0);
177  };
178 
179  //Split the cells and delete the parts outside the slice.
180  auto it = newNodes.begin();
181  while (it != newNodes.end()) {
182  std::list<Cell*> currentCells({it->first});
183  auto& nodes = it->second;
184  if (nodes.size() > 1) {
185  nodes.sort(cmp);
186  auto current = nodes.begin();
187  auto next = std::next(nodes.begin());
188  while (next != nodes.end()) {
189  auto currentCell = std::find_if(currentCells.begin(), currentCells.end(), [current, next](Cell* c){
190  auto first = std::find(c->GetNodes().begin(), c->GetNodes().end(), *current);
191  auto second = std::find(c->GetNodes().begin(), c->GetNodes().end(), *next);
192  return (first != c->GetNodes().end() && second != c->GetNodes().end());
193  });
194 
195  if (currentCell != currentCells.end()) {
196  std::vector<Cell*> newCells = m_tissue->SplitCell(*currentCell, *current, *next);
197  if (newCells.size() > 1) {
198  assert(newCells.size() == 2 && "The cell should be split into two parts.");
199 
200  currentCells.erase(currentCell);
201  currentCells.push_back(newCells.front());
202  currentCells.push_back(newCells.back());
203 
204  current = nodes.erase(current);
205  next++;
206  }
207  else {
208  current++;
209  next++;
210  }
211  }
212  else { //When the points are in two different cells.
213  current++;
214  next++;
215  }
216  }
217  }
218 
219  for (auto c : currentCells) {
220  Node* nonCutNode = nullptr;
221  for (auto n : c->GetNodes()) {
222  if (std::find(nodes.begin(), nodes.end(), n) == nodes.end()) {
223  nonCutNode = n;
224  break;
225  }
226  }
227  assert(nonCutNode != nullptr && "There has to be at least one node in this cell that isn't located on the cut.");
228 
229  if (!contains_point(QPointF((*nonCutNode)[0], (*nonCutNode)[1]))) {
230  cellsToBeDeleted.push_back(c);
231  }
232  }
233 
234  it++;
235  }
236 
237  m_tissue->DeleteCells(cellsToBeDeleted);
238 }
239 
240 } // namespace
A cell contains walls and nodes.
Definition: Cell.h:48
Namespace for SimPT tissue editor package.
Definition: Cell.h:32
Node in cell wall.
Definition: Node.h:39
Cell * GetCell() const
Return the cell of this Neighbor pair.
Combo header for circular iterator.
Namespace for SimPT shell package.
Definition: Client.cpp:50
see the online Qt documentation
Interface for NeighborNodes.
Namespace for the core simulator.
An Edge connects two nodes and is ambidextrous.
Definition: Edge.h:31
Interface for Cell.
Namespace for container related classes.
SliceItem(const QPolygonF &polygon, const QLineF &cut, std::shared_ptr< EditControlLogic > tissue, QGraphicsScene *scene)
Constructor.
Definition: SliceItem.cpp:41
virtual ~SliceItem()
Destructor.
Definition: SliceItem.cpp:50
Node * GetNb1() const
Return first node of this Neighbor pair.
Structure of neighboring nodes: two neighboring nodes from standpoint of a given cell with an orderin...
Definition: NeighborNodes.h:34
Interface for Node.
Node * GetNb2() const
Return second node of this Neighbor pair.
Interface for Edge.
CircularIterator< T > next(CircularIterator< T > i)
Helper yields the position the iterator would have if moved forward (in circular fashion) by 1 positi...
ConstCircularIterator< typename T::const_iterator > make_const_circular(const T &c)
Helper produces const circular iterator whose range corresponds to the begin and end iterators of a c...
Interface for SliceItem.
A cell wall, runs between cell corner points and consists of wall elements.
Definition: Wall.h:48