VPTissue Reference Manual
TissueGraphicsView.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 "TissueGraphicsView.h"
21 
22 #include "BackgroundDialog.h"
23 #include "CopyAttributesDialog.h"
24 #include "EditableCellItem.h"
25 #include "EditableEdgeItem.h"
26 #include "EditableNodeItem.h"
27 #include "EditorActions.h"
28 #include "PolygonUtils.h"
29 #include "SelectByIDWidget.h"
30 
31 #include "EditControlLogic.h"
34 #include "slicer/TissueSlicer.h"
35 #include "bio/Cell.h"
36 #include "bio/Edge.h"
37 #include "bio/Node.h"
38 #include "bio/Wall.h"
39 #include "model/ComponentTraits.h"
41 
42 #include <boost/property_tree/ptree_fwd.hpp>
43 #include <QGraphicsPixmapItem>
44 #include <QInputDialog>
45 #include <QLayout>
46 #include <QMenuBar>
47 #include <QPixmap>
48 #include <QPoint>
49 #include <QToolTip>
50 #include <QWheelEvent>
51 
52 #include <algorithm>
53 #include <cassert>
54 #include <iterator>
55 #include <list>
56 #include <memory>
57 #include <string>
58 #include <utility>
59 #include <vector>
60 
61 namespace SimPT_Editor {
62 
63 using std::shared_ptr;
64 using namespace SimPT_Sim;
65 
67  : PanAndZoomView(), m_tissue(nullptr), m_background_item(new QGraphicsPixmapItem()),
68  m_scene(this), m_mode(Mode::NONE), m_cells_transparent(false),// m_colorizer_map(nullptr),
69  m_colorizer_pref("size"), m_tissue_slicer(nullptr),
70  m_background_dialog(new BackgroundDialog(m_background_item, this)),
71  m_select_by_id(nullptr)
72 {
73  setParent(parent);
74 
75  //Editing operations on the scene happens in constant time.
76  //It gives better performance for dynamic scenes.
77  m_scene.setItemIndexMethod(QGraphicsScene::ItemIndexMethod::NoIndex);
78  setScene(&m_scene);
79  setMouseTracking(true);
80  setRenderHints(QPainter::Antialiasing);
81 
82  m_scene.addItem(m_background_item);
83  m_background_item->setVisible(false);
84  m_background_item->setZValue(-1);
85 
86  m_select_by_id = new SelectByIDWidget(this);
87  m_select_by_id->setDisabled(true);
88  connect(m_select_by_id, SIGNAL(IDsSelected(const std::list<unsigned int>&, bool)),
89  this, SLOT(SelectItems(const std::list<unsigned int>&, bool)));
90 
91  connect(&m_scene, SIGNAL(selectionChanged()), this, SLOT(UpdateSelection()));
92 }
93 
95 {
96  Cleanup();
97  //delete m_colorizer_map;
98  //m_colorizer_map = nullptr;
99  m_select_by_id = nullptr;
100 }
101 
102 void TissueGraphicsView::Initialize(shared_ptr<EditControlLogic> tissue, Mode mode)
103 {
104  Cleanup();
105  m_tissue = tissue;
106 
107  for (Node* node : tissue->GetNodes()) {
108  AddNode(node);
109  }
110  for (Cell* cell : tissue->GetCells()) {
111  auto nodes = cell->GetNodes();
112  std::list<EditableNodeItem*> nodeItems;
113  for (auto it = nodes.begin(); it != nodes.end(); it++) {
114  EditableNodeItem* nodeItem = GetEditableNode((*it));
115  nodeItems.push_back(nodeItem);
116  }
117 
118  std::list<EditableEdgeItem*> edgeItems;
119  auto cit = SimPT_Sim::Container::make_const_circular(nodeItems);
120  do {
121  double found = false;
122  for (EditableEdgeItem* edge : m_edges) {
123  if (edge->ContainsEndpoints(*cit, *next(cit))) {
124  edgeItems.push_back(edge);
125  found = true;
126  }
127  }
128  if (!found) {
129  edgeItems.push_back(AddEdge(*cit, *next(cit)));
130  }
131  } while (++cit != nodeItems.begin());
132 
133  AddCell(nodeItems, edgeItems, cell);
134  }
135 
136  connect(m_tissue.get(), SIGNAL(Moved(Node*, double, double)),
137  this, SLOT(UpdateMovedNode()));
138  connect(this, SIGNAL(StatusInfoChanged(const std::string&)),
139  m_tissue.get(), SIGNAL(StatusInfoChanged(const std::string&)));
140  SetMode(mode);
141  SetTransparentCells(m_cells_transparent);
142 }
143 
145 {
146  bool editableItemPresent = false;
147 
148  for (QGraphicsItem* item : m_scene.selectedItems()) {
149  EditableItem* editableItem = dynamic_cast<EditableItem*>(item);
150  if (editableItem != nullptr) {
151  editableItemPresent = true;
152  if (!editableItem->IsAtBoundary()) {
153  return false;
154  }
155  }
156  }
157 
158  return editableItemPresent;
159 }
160 
162 {
163  assert(m_tissue != nullptr && "The tissue isn't initialized.");
164 
165  bool editableItemPresent = false;
166 
167  if (m_mode == Mode::NODE) {
168  for (QGraphicsItem* item : m_scene.selectedItems()) {
169  EditableNodeItem* nodeItem = dynamic_cast<EditableNodeItem*>(item);
170  if (nodeItem != nullptr) {
171  editableItemPresent = true;
172  if (!m_tissue->GetMesh()->IsDeletableNode(nodeItem->Node())) {
173  return false;
174  }
175  }
176  }
177  }
178  else if (m_mode == Mode::CELL) {
179  for (QGraphicsItem* item : m_scene.selectedItems()) {
180  EditableCellItem* cellItem = dynamic_cast<EditableCellItem*>(item);
181  if (cellItem != nullptr) {
182  editableItemPresent = true;
183  if (!m_tissue->GetMesh()->IsDeletableCell(cellItem->Cell())) {
184  return false;
185  }
186  }
187  }
188  }
189 
190  return editableItemPresent;
191 }
192 
194 {
195  if (m_scene.selectedItems().size() != 2 || m_mode != Mode::NODE) {
196  return false;
197  }
198 
199  EditableNodeItem* item1 = dynamic_cast<EditableNodeItem*>(m_scene.selectedItems().front());
200  EditableNodeItem* item2 = dynamic_cast<EditableNodeItem*>(m_scene.selectedItems().back());
201 
202  assert(item1 != nullptr && item2 != nullptr && "Other items than nodes were selected in 'NODE' mode.");
203 
204  return m_tissue->GetMesh()->CanSplitCell(item1->Node(), item2->Node());
205 }
206 
207 EditableCellItem* TissueGraphicsView::AddCell(std::list<EditableNodeItem*> endpoints, std::list<EditableEdgeItem*> edges, Cell* cell)
208 {
209  EditableCellItem* cellItem = new EditableCellItem(endpoints, edges, cell);
210  cellItem->SetTransparent(m_cells_transparent);
211  m_scene.addItem(cellItem);
212  m_cells.push_back(cellItem);
213  return cellItem;
214 }
215 
217 {
218  EditableEdgeItem* edgeItem = new EditableEdgeItem(endpoint1, endpoint2);
219  m_scene.addItem(edgeItem);
220  m_edges.push_back(edgeItem);
221  return edgeItem;
222 }
223 
225 {
226  EditableNodeItem* nodeItem = new EditableNodeItem(node, g_node_radius);
227  m_scene.addItem(nodeItem);
228  m_nodes.push_back(nodeItem);
229  return nodeItem;
230 }
231 
233 {
234  return m_mode;
235 }
236 
238  return m_select_by_id;
239 }
240 
242 {
243  if (m_mode == Mode::DISPLAY && mode != Mode::DISPLAY) {
244  QColor color(255, 255, 255, 255);
245  for (EditableCellItem* item : m_cells) {
246  item->SetColor(color);
247  }
248  } else if (mode == Mode::DISPLAY) {
249  //assert(m_colorizer_map != nullptr && "The colorizer map must be initialized to use 'DISPLAY'-mode.");
250  if (m_colorizer_pref == "<no color>") {
251  for (EditableCellItem* item : m_cells) {
252  QColor color(255, 255, 255, 255);
253  item->SetColor(color);
254  }
255  } else {
256 
257  const auto cell_colorizer = m_factory->CreateCellColor(m_colorizer_pref.toStdString(), m_ptree);
258  for (EditableCellItem* item : m_cells) {
259  const auto c = cell_colorizer(item->Cell());
260  QColor color = QColor::fromRgbF(get<0>(c), get<1>(c), get<2>(c));
261  item->SetColor(color);
262  }
263  }
264  }
265 
266  if (m_mode != mode) {
267  m_mode = mode;
268 
269  if (m_mode == Mode::DISPLAY || m_mode == Mode::NODE || m_mode == Mode::EDGE || m_mode == Mode::CELL) {
270  m_scene.clearSelection();
271  m_tissue->Deselect();
272  SetEditableItemFlags();
273  }
274 
275  setCursor(m_mode == Mode::CELL_CREATE ? Qt::CrossCursor : Qt::ArrowCursor);
276  //setMouseTracking(m_mode == Mode::CELL_SLICE);
277  emit ModeChanged();
278 
279  std::string info;
280  if (m_mode == Mode::DISPLAY) {
281  info = "Mode changed to 'Display'";
282  }
283  else if (m_mode == Mode::NODE) {
284  info = "Mode changed to 'Node'";
285  }
286  else if (m_mode == Mode::EDGE) {
287  info = "Mode changed to 'Edge'";
288  }
289  else if (m_mode == Mode::CELL) {
290  info = "Mode changed to 'Cell'";
291  }
292  emit StatusInfoChanged(info);
293 
294  //Change maximum ID for select by ID.
295  if (m_mode != Mode::NODE && m_mode != Mode::EDGE && m_mode != Mode::CELL) {
296  m_select_by_id->SetMaxID(0); //Reset the text in the text box.
297  m_select_by_id->setDisabled(true);
298  }
299  else {
300  unsigned int maxID = 0;
301  if (m_mode == Mode::NODE) {
302  maxID = m_nodes.size() - 1;
303  }
304  else if (m_mode == Mode::EDGE) {
305  maxID = m_tissue->GetMesh()->GetMesh()->GetWalls().size() - 1;
306  }
307  else if (m_mode == Mode::CELL) {
308  maxID = m_cells.size() - 1;
309  }
310  m_select_by_id->SetMaxID(maxID);
311  m_select_by_id->setDisabled(false);
312  }
313  }
314 }
315 
317 {
318  assert(m_mode == Mode::CELL && "Tried to generate a pattern in a cell in a different mode than 'CELL'.");
319  assert(m_scene.selectedItems().size() == 1 && "Can't generate a pattern in multiple cells.");
320 
321  EditableCellItem* cellItem = dynamic_cast<EditableCellItem*>(m_scene.selectedItems().front());
322  assert(cellItem != nullptr && "An item different from a cell has been selected in mode 'CELL'.");
323 
324  RegularGeneratorDialog dialog(cellItem->polygon(), Scaling(), parentWidget());
325  if (dialog.exec()) {
326  m_tissue->ReplaceCell(cellItem->Cell(), dialog.GetGeneratedPolygons());
327  Initialize(m_tissue, Mode::CELL);
328  }
329 }
330 
332 {
333  assert(m_mode == Mode::CELL && "Tried to generate a pattern in a cell in a different mode than 'CELL'.");
334  assert(m_scene.selectedItems().size() == 1 && "Can't generate a pattern in multiple cells.");
335 
336  EditableCellItem* cellItem = dynamic_cast<EditableCellItem*>(m_scene.selectedItems().front());
337  assert(cellItem != nullptr && "An item different from a cell has been selected in mode 'CELL'.");
338 
339  VoronoiGeneratorDialog dialog(cellItem->polygon(), Scaling(), this);
340  if (dialog.exec()) {
341  m_tissue->ReplaceCell(cellItem->Cell(), dialog.GetGeneratedPolygons());
342  Initialize(m_tissue, Mode::CELL);
343  }
344 }
345 
346 void TissueGraphicsView::SetColorComponent(const boost::property_tree::ptree& parameters)
347 {
348  m_ptree = parameters;
349  const auto model_group = m_ptree.get<string>("model.group", "");
350  m_factory = ComponentFactoryProxy::Create(model_group);
351 }
352 
354 {
355  for (auto item : m_cells) {
356  delete item;
357  }
358  for (auto item : m_edges) {
359  delete item;
360  }
361  for (auto item : m_nodes) {
362  delete item;
363  }
364 
365  m_cells.clear();
366  m_edges.clear();
367  m_nodes.clear();
368 
369  SetMode(Mode::NONE);
370 
371  delete m_tissue_slicer;
372  m_tissue_slicer = nullptr;
373 }
374 
376 {
377 
378  if (m_mode == Mode::CELL_CREATE) {
379  assert(m_scene.selectedItems().size() == 2 && "There should be two nodes selected when in this mode.");
380 
381  m_mode = Mode::NODE;
382  SetEditableItemFlags();
383  setCursor(Qt::ArrowCursor);
384  emit ModeChanged();
385  emit ItemsSelected(2);
386  }
387  else if (m_mode == Mode::CELL_SLICE) {
388  delete m_tissue_slicer;
389  m_tissue_slicer = nullptr;
390 
391  m_mode = Mode::CELL;
392  SetEditableItemFlags();
393  emit ModeChanged();
394  }
395  else if (m_mode == Mode::NODE_COPY) {
396  m_mode = Mode::NODE;
397  emit ModeChanged();
398  emit ItemsSelected(m_scene.selectedItems().size());
399  }
400  else if (m_mode == Mode::EDGE_COPY) {
401  m_mode = Mode::EDGE;
402  emit ModeChanged();
403  emit ItemsSelected(m_scene.selectedItems().size());
404 
405  }
406  else if (m_mode == Mode::CELL_COPY) {
407  m_mode = Mode::CELL;
408  emit ModeChanged();
409  emit ItemsSelected(m_scene.selectedItems().size());
410  }
411  else {
412  assert(false && "This mode doesn't represent an action.");
413  }
414 }
415 
417 {
418  assert(m_scene.selectedItems().size() > 0 && "At least one cell item should be selected.");
419 
420  if (m_mode == Mode::CELL) {
421  SetMode(Mode::CELL_COPY);
422  }
423  else if (m_mode == Mode::EDGE) {
424  SetMode(Mode::EDGE_COPY);
425  }
426  else if (m_mode == Mode::NODE) {
427  SetMode(Mode::NODE_COPY);
428  }
429  else {
430  assert(false && "The mode should be 'CELL', 'EDGE' or 'NODE'.");
431  }
432 }
433 
435 {
436  assert(m_mode == Mode::NODE && m_scene.selectedItems().size() == 2 && "Two node items should be selected in 'NODE'-mode");
437 
438  SetMode(Mode::CELL_CREATE);
439 }
440 
442 {
443  assert(m_scene.selectedItems().size() == 1 && "Can't delete multiple items.");
444 
445  if (m_mode == Mode::NODE) {
446  EditableNodeItem* nodeItem = dynamic_cast<EditableNodeItem*>(m_scene.selectedItems().front());
447  if (nodeItem != nullptr) {
448  DeleteNode(nodeItem);
449  }
450  }
451  else if (m_mode == Mode::CELL) {
452  EditableCellItem* cellItem = dynamic_cast<EditableCellItem*>(m_scene.selectedItems().front());
453  if (cellItem != nullptr) {
454  DeleteCell(cellItem);
455  }
456  }
457 }
458 
460 {
461  assert(m_mode == Mode::EDGE && "Tried to split an edge in a different mode than 'EDGE'.");
462  assert(m_scene.selectedItems().size() == 1 && "Only one edge can be split.");
463 
464  EditableEdgeItem* edgeItem = dynamic_cast<EditableEdgeItem*>(m_scene.selectedItems().front());
465  assert(edgeItem != nullptr && "An item different from an edge has been selected in mode 'EDGE'.");
466 
467  Node* node = m_tissue->SplitEdge(edgeItem->Edge());
468  EditableNodeItem* nodeItem = AddNode(node);
469  auto newEdges = edgeItem->SplitEdge(nodeItem);
470 
471  m_scene.addItem(newEdges.first);
472  m_scene.addItem(newEdges.second);
473  m_scene.removeItem(edgeItem);
474 
475  newEdges.first->setFlags(edgeItem->flags());
476  newEdges.second->setFlags(edgeItem->flags());
477 
478  m_edges.push_back(newEdges.first);
479  m_edges.push_back(newEdges.second);
480  m_edges.remove(edgeItem);
481 
482  delete edgeItem;
483 }
484 
486 {
487  assert(m_mode == Mode::NODE && "Tried to split a cell in a different mode than 'NODE'.");
488  assert(m_scene.selectedItems().size() == 2 && "Two nodes should be selected.");
489 
490  EditableNodeItem* item1 = dynamic_cast<EditableNodeItem*>(m_scene.selectedItems().front());
491  EditableNodeItem* item2 = dynamic_cast<EditableNodeItem*>(m_scene.selectedItems().back());
492 
493  assert(item1 != nullptr && item2 != nullptr && "Other items than nodes were selected in 'NODE' mode.");
494 
495  m_tissue->SplitCell(item1->Node(), item2->Node());
496  Initialize(m_tissue, Mode::NODE);
497 }
498 
500 {
501  assert(m_mode == Mode::NODE && "Can't update a moved node when not in mode 'NODE'.");
502  assert(m_scene.selectedItems().size() == 1 && "Can only update one selected node.");
503 
504  EditableNodeItem* node = dynamic_cast<EditableNodeItem*>(m_scene.selectedItems().front());
505  assert(node != nullptr && "Another item different from a node has been selected in mode 'NODE'.");
506 
507  node->Update();
508 }
509 
511 {
512  m_background_dialog->show();
513  m_background_dialog->activateWindow();
514 }
515 
517 {
518  QStringList items;
519  items.append("<no color>");
520 
521  for (const auto& e : m_factory->ListCellColor()) {
522  items.append(QString::fromStdString(e));
523  }
524  bool ok;
525  QString pref = QInputDialog::getItem(this, "Cell Color scheme preferences",
526  "Select the cell color scheme.", items, items.indexOf(m_colorizer_pref), false, &ok);
527 
528  if (ok) {
529  m_colorizer_pref = pref;
530  if (m_mode == Mode::DISPLAY) {
531  SetMode(Mode::DISPLAY); //Reinitialize the cell colorizer.
532  }
533  }
534 }
535 
537 {
538  //Change transparency
539  m_cells_transparent = transparent;
540 
541  //For every cell currently present on the scene, update transparency to the new transparency
542  for (EditableCellItem* cell : m_cells) {
543  cell->SetTransparent(m_cells_transparent);
544  }
545 
546 }
547 
548 void TissueGraphicsView::UpdateSelection()
549 {
550  if (m_scene.selectedItems().size() == 0) {
551  m_tissue->Deselect();
552  }
553  else {
554  if (m_mode == Mode::NODE) {
555  std::list<Node*> selectedNodes;
556  for (QGraphicsItem* item : m_scene.selectedItems()) {
557  EditableNodeItem* node = dynamic_cast<EditableNodeItem*>(item);
558  assert(node != nullptr && "A node has been selected in another mode than 'NODE'.");
559  selectedNodes.push_back(node->Node());
560  }
561  m_tissue->SelectNodes(selectedNodes);
562  }
563  else if (m_mode == Mode::EDGE) {
564  std::list<Edge> selectedEdges;
565  for (QGraphicsItem* item : m_scene.selectedItems()) {
566  EditableEdgeItem* edge = dynamic_cast<EditableEdgeItem*>(item);
567  assert(edge != nullptr && "An edge has been selected in another mode than 'EDGE'.");
568  selectedEdges.push_back(edge->Edge());
569  }
570  m_tissue->SelectEdges(selectedEdges);
571  }
572  else if (m_mode == Mode::CELL) {
573  std::list<Cell*> selectedCells;
574  for (QGraphicsItem* item : m_scene.selectedItems()) {
575  EditableCellItem* cell = dynamic_cast<EditableCellItem*>(item);
576  assert(cell != nullptr && "A cell has been selected in another mode than 'CELL'.");
577  selectedCells.push_back(cell->Cell());
578  }
579  m_tissue->SelectCells(selectedCells);
580  }
581  }
582 
583  if (m_mode == Mode::NODE || m_mode == Mode::EDGE || m_mode == Mode::CELL) {
584  emit ItemsSelected(m_scene.selectedItems().size());
585  }
586 }
587 
588 void TissueGraphicsView::FinishSliceAction()
589 {
590  assert(m_mode == Mode::CELL_SLICE && "Can't be in another mode than CELL_SLICE.");
591  assert(m_tissue_slicer != nullptr && "Can't be in CELL_SLICE mode if tissue slicer isn't initialized.");
592 
593  m_tissue_slicer->deleteLater();
594  m_tissue_slicer = nullptr;
595 
596  Initialize(m_tissue, Mode::CELL);
597 }
598 
599 void TissueGraphicsView::SelectItems(const std::list<unsigned int>& ids, bool keepItemsSelected)
600 {
601  if (!keepItemsSelected) {
602  m_scene.clearSelection();
603  }
604 
605  std::vector<QGraphicsItem*> selectedItems;
606  if (m_mode == Mode::NODE) {
607  for (unsigned int id : ids) {
608  auto item = std::find_if(m_nodes.begin(), m_nodes.end(), [id](EditableNodeItem* n){return n->Node()->GetIndex() == id;});
609  if (item != m_nodes.end()) {
610  (*item)->setSelected(true);
611  selectedItems.push_back(*item);
612  }
613  }
614 
615  }
616  else if (m_mode == Mode::EDGE) {
617  for (unsigned int id : ids) {
618  for (EditableEdgeItem* e : m_edges) {
619  Wall* w = m_tissue->GetMesh()->GetMesh()->FindWall(e->Edge());
620  if (w->GetIndex() == id) {
621  e->setSelected(true);
622  selectedItems.push_back(e);
623  }
624  }
625  }
626  }
627  else if (m_mode == Mode::CELL) {
628  for (unsigned int id : ids) {
629  auto item = std::find_if(m_cells.begin(), m_cells.end(),
630  [id](EditableCellItem* c){return static_cast<unsigned int>(c->Cell()->GetIndex()) == id;});
631  if (item != m_cells.end()) {
632  (*item)->setSelected(true);
633  selectedItems.push_back(*item);
634  }
635  }
636  }
637  else {
638  assert(false && "This mode is not supported.");
639  }
640 
641  EnsureVisible(selectedItems);
642 }
643 
644 void TissueGraphicsView::DeleteNode(EditableNodeItem* node)
645 {
646  assert(node != nullptr && "Tried to delete a nullptr.");
647 
648  auto contains_node = [node](EditableEdgeItem* edge) {
649  return edge->ContainsEndpoint(node);
650  };
651 
652  //Delete the node in the mesh.
653  if (!m_tissue->DeleteNode(node->Node())) {
654  return;
655  }
656 
657  //Get the two edges the deleted node was connected to.
658  std::list<EditableEdgeItem*> connectedEdges;
659  auto edge = std::find_if(m_edges.begin(), m_edges.end(), contains_node);
660  while (edge != m_edges.end()) {
661  connectedEdges.push_back(*edge);
662  edge = std::find_if(++edge, m_edges.end(), contains_node);
663  }
664  assert(connectedEdges.size() == 2 && "More than two edges are connected to the deleted node.");
665  EditableEdgeItem* oldEdge1 = connectedEdges.front();
666  EditableEdgeItem* oldEdge2 = connectedEdges.back();
667 
668  //Merge the two edges together.
669  EditableEdgeItem* newEdge = oldEdge1->MergeEdge(oldEdge2);
670  m_edges.push_back(newEdge);
671  m_edges.remove(oldEdge1);
672  m_edges.remove(oldEdge2);
673  m_scene.addItem(newEdge);
674 
675  //Destroy the items and remove them from the scene.
676  m_nodes.remove(node);
677  m_scene.removeItem(node);
678  m_scene.removeItem(oldEdge1);
679  m_scene.removeItem(oldEdge2);
680  m_scene.clearSelection();
681 
682  delete oldEdge1;
683  delete oldEdge2;
684  delete node;
685 }
686 
687 void TissueGraphicsView::DeleteCell(EditableCellItem* cell)
688 {
689  assert(cell != nullptr && "Tried to delete a nullptr.");
690  assert(cell->Cell()->HasBoundaryWall());
691 
692  m_tissue->DeleteCells({ cell->Cell() });
693 
694  Initialize(m_tissue, Mode::CELL);
695 }
696 
697 void TissueGraphicsView::EnsureVisible(const std::vector<QGraphicsItem*>& items)
698 {
699  if (items.size() == 0) {
700  return;
701  }
702  else {
703  QRectF current = items.front()->mapRectToScene(items.front()->boundingRect());
704  for (auto it = std::next(items.begin()); it != items.end(); it++) {
705  current = current.united((*it)->mapRectToScene((*it)->boundingRect()));
706  }
707  ensureVisible(current);
708  }
709 }
710 
711 EditableNodeItem* TissueGraphicsView::GetEditableNode(Node* node) const
712 {
713  for (auto it = m_nodes.begin(); it != m_nodes.end(); it++) {
714  if ((*it)->Contains(node)) {
715  return (*it);
716  }
717  }
718  return nullptr;
719 }
720 
721 void TissueGraphicsView::SetEditableItemFlags(bool enable)
722 {
723  bool allowed = (m_mode == Mode::NODE && enable);
724  for (QGraphicsItem* item : m_nodes) {
725  item->setFlag(QGraphicsItem::ItemIsSelectable, allowed);
726  item->setFlag(QGraphicsItem::ItemIsMovable, allowed);
727  }
728 
729  allowed = (m_mode == Mode::EDGE && enable);
730  for (QGraphicsItem* item : m_edges) {
731  item->setFlag(QGraphicsItem::ItemIsSelectable, allowed);
732  }
733 
734  allowed = (m_mode == Mode::CELL && enable);
735  for (QGraphicsItem* item : m_cells) {
736  item->setFlag(QGraphicsItem::ItemIsSelectable, allowed);
737  }
738 }
739 
740 bool TissueGraphicsView::IsConsistent() const
741 {
742  if (m_scene.selectedItems().size() == 1) {
743  EditableNodeItem* selected = dynamic_cast<EditableNodeItem*>(m_scene.selectedItems().front());
744 
745  //Check whether the selected node is on top of another node.
746  if (selected != nullptr) {
747  for (EditableNodeItem* node : m_nodes) {
748  if (selected != node && QLineF(selected->pos(), node->pos()).length() < 2*g_node_radius) {
749  return false;
750  }
751  }
752  }
753 
754  //Check whether the cell is still counter-clockwise (clockwise in the Qt-coordinate system).
755  if (m_cells.size() == 1) {
756  if (PolygonUtils::IsClockwise(m_cells.front()->polygon())) {
757  return false;
758  }
759  }
760 
761  return true;
762  }
763  else {
764  return false;
765  }
766 }
767 
768 void TissueGraphicsView::mouseMoveEvent(QMouseEvent* event)
769 {
770  //Update and show tool tip if located on node in 'Node'-mode.
771  if (m_mode == Mode::NODE) {
772  EditableNodeItem* nodeItem = dynamic_cast<EditableNodeItem*>(itemAt(event->pos()));
773  if (nodeItem != nullptr) {
774  //Get all cells associated with the node.
775  std::list<Cell*> cells;
776  for (auto c : m_tissue->GetMesh()->GetMesh()->GetCells()) {
777  if (std::find_if(c->GetNodes().begin(), c->GetNodes().end(),
778  [nodeItem](Node* n){return n->GetIndex() == nodeItem->Node()->GetIndex();}) != c->GetNodes().end()) {
779  cells.push_back(c);
780  }
781  }
782  cells.sort([](Cell* c1, Cell* c2){return c1->GetIndex() < c2->GetIndex();});
783 
784  //Get all walls associated with the node.
785  std::list<Wall*> walls = m_tissue->GetMesh()->GetMesh()->GetNodeOwningWalls(nodeItem->Node());
786  walls.sort([](Wall* w1, Wall* w2){return w1->GetIndex() < w2->GetIndex();});
787 
788  //Update tool tip.
789  nodeItem->SetToolTip(cells, walls);
790  QToolTip::showText(event->globalPos(), nodeItem->toolTip());
791  }
792  }
793 
794  if (m_mode == Mode::NODE && m_scene.selectedItems().size() == 1) {
795  PanAndZoomView::mouseMoveEvent(event);
796  EditableNodeItem* nodeItem = dynamic_cast<EditableNodeItem*>(m_scene.selectedItems().front());
797 
798  assert(nodeItem != nullptr && "An item different from a node has been selected in mode 'NODE'.");
799  assert(nodeItem->flags().testFlag(QGraphicsItem::ItemIsMovable) && "The node isn't movable.");
800 
801  Node* node = nodeItem->Node();
802  if (nodeItem->pos().x() != (*node)[0] || nodeItem->pos().y() != (*node)[1]) {
803  if (!m_tissue->MoveNode(node, nodeItem->pos().x(), nodeItem->pos().y())) {
804  nodeItem->Revert();
805  }
806  else if (!IsConsistent()) {
807  nodeItem->Revert();
808  m_tissue->MoveNode(node, nodeItem->pos().x(), nodeItem->pos().y());
809  }
810  }
811  }
812  else if (m_mode != Mode::NODE || event->modifiers().testFlag(Qt::ControlModifier)) {
813  //The case of 'NODE' mode should already be handled for the valid cases.
814  //If the mode is still in mode and the view isn't in drag mode (ctrl-modifier), the event may not be passed.
815  PanAndZoomView::mouseMoveEvent(event);
816 
817  if (m_mode == Mode::CELL_SLICE) {
818  assert(m_tissue_slicer != nullptr && "The tissue slicer should exist when in 'CELL_SLICE' mode.");
819  m_tissue_slicer->MoveCut(mapToScene(event->pos()));
820  }
821  }
822 }
823 
824 void TissueGraphicsView::mousePressEvent(QMouseEvent* event)
825 {
826  if (event->button() == Qt::LeftButton && (m_mode == Mode::NODE_COPY || m_mode == Mode::EDGE_COPY || m_mode == Mode::CELL_COPY)) {
827  QGraphicsItem* item = dynamic_cast<QGraphicsItem*>(m_scene.itemAt(mapToScene(event->pos()), QTransform()));
828  if (item != nullptr && item->flags().testFlag(QGraphicsItem::ItemIsSelectable)) {
829  bool isSelected = item->isSelected();
830  if (m_mode == Mode::NODE_COPY) {
831  assert(m_tissue->SelectedNodes().size() > 0 && "There should be at least one node selected.");
832 
833  EditableNodeItem* nodeItem = dynamic_cast<EditableNodeItem*>(item);
834  assert(nodeItem != nullptr && "Another item than a node has been selected in 'NODE_COPY'-mode");
835 
836 
837  nodeItem->SetHighlightColor(QColor(0, 0, 102, 255));
838  nodeItem->setSelected(true);
839 
840  CopyAttributesDialog dialog(nodeItem->Node()->NodeAttributes::ToPtree(), this);
841  if (dialog.exec()) {
842  m_tissue->CopyAttributes(dialog.SelectedAttributes());
843 
844  m_mode = Mode::NODE;
845  emit ModeChanged();
846 
847  nodeItem->SetHighlightColor();
848  nodeItem->setSelected(true);
849  }
850  else {
851  nodeItem->SetHighlightColor();
852  nodeItem->setSelected(isSelected);
853  }
854  }
855  else if (m_mode == Mode::EDGE_COPY) {
856  assert(m_tissue->SelectedWalls().size() > 0 && "There should be at least one edge selected.");
857 
858  EditableEdgeItem* edgeItem = dynamic_cast<EditableEdgeItem*>(item);
859  assert(edgeItem != nullptr && "Another item than a node has been selected in 'EDGE_COPY'-mode");
860 
861  edgeItem->SetHighlightColor(QColor(0, 0, 102, 255));
862  edgeItem->setSelected(true);
863 
864  Wall* wall = m_tissue->GetMesh()->GetMesh()->FindWall(edgeItem->Edge());
865  CopyAttributesDialog dialog(wall->WallAttributes::ToPtree(), this);
866  if (dialog.exec()) {
867  m_tissue->CopyAttributes(dialog.SelectedAttributes());
868 
869  m_mode = Mode::EDGE;
870  emit ModeChanged();
871 
872  edgeItem->SetHighlightColor();
873  edgeItem->setSelected(true);
874  }
875  else {
876  edgeItem->SetHighlightColor();
877  edgeItem->setSelected(isSelected);
878  }
879  }
880  else if (m_mode == Mode::CELL_COPY) {
881  assert(m_tissue->SelectedCells().size() > 0 && "There should be at least one cell selected.");
882 
883  EditableCellItem* cellItem = dynamic_cast<EditableCellItem*>(item);
884  assert(cellItem != nullptr && "Another item than a node has been selected in 'CELL_COPY'-mode");
885 
886  cellItem->SetHighlightColor(QColor(0, 0, 102, 255));
887  cellItem->setSelected(true);
888 
889  CopyAttributesDialog dialog(cellItem->Cell()->CellAttributes::ToPtree(), this);
890  if (dialog.exec()) {
891  m_tissue->CopyAttributes(dialog.SelectedAttributes());
892 
893  m_mode = Mode::CELL;
894  emit ModeChanged();
895 
896  cellItem->SetHighlightColor();
897  cellItem->setSelected(true);
898  }
899  else {
900  cellItem->SetHighlightColor();
901  cellItem->setSelected(isSelected);
902  }
903  }
904  }
905  }
906  else if (event->button() == Qt::LeftButton && event->modifiers().testFlag(Qt::ShiftModifier)) {
907  QGraphicsItem* item = m_scene.itemAt(mapToScene(event->pos()), QTransform());
908  if (item != nullptr) {
909  item->setSelected(!item->isSelected());
910  }
911  }
912  else if (event->button() == Qt::LeftButton && m_mode == Mode::CELL_CREATE) {
913  EditableNodeItem* nodeItem1 = dynamic_cast<EditableNodeItem*>(m_scene.selectedItems().front());
914  EditableNodeItem* nodeItem2 = dynamic_cast<EditableNodeItem*>(m_scene.selectedItems().back());
915 
916  assert(nodeItem1 != nullptr && nodeItem1->Node()->IsAtBoundary()
917  && "There aren't two nodes selected or the nodes aren't located at the boundary.");
918  assert(nodeItem2 != nullptr && nodeItem2->Node()->IsAtBoundary()
919  && "There aren't two nodes selected or the nodes aren't located at the boundary.");
920 
921  QPointF thirdPoint(mapToScene(event->pos()));
922  Cell* cell = m_tissue->CreateCell(nodeItem1->Node(), nodeItem2->Node(), thirdPoint);
923 
924  if (cell != nullptr) {
925  Initialize(m_tissue, Mode::NODE);
926  }
927  }
928  else if (event->button() == Qt::LeftButton && (m_mode != Mode::CELL_SLICE || m_tissue_slicer->CutEnded())) {
929  if (event->modifiers().testFlag(Qt::ControlModifier)) {
930  SetEditableItemFlags(false);
931  }
932  PanAndZoomView::mousePressEvent(event);
933  if (event->modifiers().testFlag(Qt::ControlModifier)) {
934  SetEditableItemFlags(true);
935  }
936  }
937  else if (event->button() == Qt::RightButton) {
938  if (m_mode == Mode::CELL) {
939  m_scene.clearSelection();
940  QGraphicsView::mousePressEvent(event);
941 
942  QPolygonF boundaryPolygon;
943  for (auto node : m_tissue->GetMesh()->GetMesh()->GetBoundaryPolygon()->GetNodes()) {
944  boundaryPolygon.append(QPointF((*node)[0], (*node)[1]));
945  }
946 
947  QPointF point = mapToScene(event->pos());
948  if (!boundaryPolygon.containsPoint(point, Qt::OddEvenFill)) {
949  SetMode(Mode::CELL_SLICE);
950  m_tissue_slicer = new TissueSlicer(m_tissue, &m_scene);
951  connect(m_tissue_slicer, SIGNAL(Finished()), this, SLOT(FinishSliceAction()));
952  m_tissue_slicer->StartCut(mapToScene(event->pos()));
953  }
954  }
955  else if (m_mode == Mode::CELL_SLICE) {
956  QPolygonF boundaryPolygon;
957  for (auto node : m_tissue->GetMesh()->GetMesh()->GetBoundaryPolygon()->GetNodes()) {
958  boundaryPolygon.append(QPointF((*node)[0], (*node)[1]));
959  }
960 
961  QPointF point = mapToScene(event->pos());
962  if (!boundaryPolygon.containsPoint(point, Qt::OddEvenFill) && m_tissue_slicer->MoveCut(point)) {
963  m_tissue_slicer->EndCut();
964  }
965  }
966  }
967 }
968 
969 void TissueGraphicsView::mouseReleaseEvent(QMouseEvent* event)
970 {
971  if (m_mode == Mode::NODE && m_scene.selectedItems().size() == 1) {
972  m_tissue->StopMoveNode();
973  m_scene.selectedItems().front()->setFlag(QGraphicsItem::ItemIsMovable);
974  }
975  PanAndZoomView::mouseReleaseEvent(event);
976 }
977 
978 void TissueGraphicsView::mouseDoubleClickEvent(QMouseEvent*) {}
979 
980 } // namespace
void ModeChanged()
Emitted when the mode has changed.
Dialog for setting the background image.
QGraphicsView with the ability to pan and zoom.
A cell contains walls and nodes.
Definition: Cell.h:48
Interface for an editable graphical representation.
Definition: EditableItem.h:30
Interface for TissueSlicer.
Namespace for SimPT tissue editor package.
Definition: Cell.h:32
bool SelectedIsAtBoundary() const
Checks whether the selected (editable) items are at the boundary of the cell complex.
Node in cell wall.
Definition: Node.h:39
SimPT_Sim::Node * Node() const
Get the node associated with this item.
Combo header for circular iterator.
Interface for RegularGeneratorDialog.
void SetTransparent(bool transparent)
Set the transparency of a cell.
Dialog for generating patterns in a cell using Voronoi tessellation.
static std::shared_ptr< ComponentFactoryProxy > Create(const string &group_name, bool throw_ok=true)
Create a factory proxy.
Mode
Different modes for the graphicsview.
bool CutEnded() const
Checks whether the cut-action has ended.
void SetTransparentCells(bool transparent)
Set background of cells transparent.
The task is being executed.
bool MoveCut(const QPointF &point)
This procedure is for dynamically moving the line in the canvas (the first point is fixed)...
static bool IsClockwise(const QPolygonF &polygon)
Checks whether the vertices of a polygon are ordered clockwise or counterclockwise.
void StatusInfoChanged(const std::string &info)
Emitted when certain info about the view has changed.
double Scaling()
Gets the current scaling of the view (assuming the view was only scaled with ScaleView() and the hori...
Interface for VoronoiGeneratorDialog.
Interface for EditorActions.
TissueGraphicsView(QWidget *parent)
Constructor.
Interface for CopyAttributesDialog.
void SplitEdge()
Split the selected edge.
Namespace for the core simulator.
void EndCut()
End the cut procedure by putting a second point for the cut.
Interface for Cell.
bool SelectedIsDeletable() const
Checks whether the selected (editable) items are deletable.
Class for selecting an item by ID.
bool SelectedIsSplitable() const
Checks whether two selected nodes can split a single cell.
Editable graphical representation for Edge.
void GenerateRegularPattern()
Generates a regular cell pattern in the selected cell.
Interface for SelectByIDWidget.
Editable graphical representation for Node.
void SetColorComponent(const boost::property_tree::ptree &parameters)
Set the colorizer map for coloring the cells in 'NONE'-mode.
virtual bool IsAtBoundary() const =0
Checks whether the item is at the boundary.
EditableCellItem * AddCell(std::list< EditableNodeItem * > endpoints, std::list< EditableEdgeItem * > edges, SimPT_Sim::Cell *cell)
Adds a cell to the scene.
Interface for EditableNodeItem.
Interface for Node.
void SplitCell()
Split a cell through the selected nodes.
Editable graphical representation for Cell.
Interface for GraphicsView of tissue.
int GetIndex() const
Return the index.
Definition: Cell.h:76
virtual ~TissueGraphicsView()
Destructor.
void CopyAttributes()
Copy attributes action (only available in CELL mode).
void ItemsSelected(unsigned int count)
Emitted when the selection has changed.
Interface for Edge.
void SetBackground()
Opens the dialog to set the background image of the workspace.
Interface for BackgroundDialog.
EditableEdgeItem * AddEdge(EditableNodeItem *endpoint1, EditableNodeItem *endpoint2)
Adds an edge to the scene.
void SetMaxID(unsigned int maxID)
Set the maximal ID and reset the text.
void StartCut(const QPointF &point)
Start the cut procedure by putting a starting point for the cut.
SimPT_Sim::Cell * Cell() const
Get the logical cell.
see the online Qt documentation
void UpdateMovedNode()
Update the moved node in the scene.
SelectByIDWidget * GetSelectByIDWidget() const
Returns the widget for selecting items by ID.
Model component .
CircularIterator< T > next(CircularIterator< T > i)
Helper yields the position the iterator would have if moved forward (in circular fashion) by 1 positi...
void SetMode(Mode mode)
Set the editing mode of the view.
EditableNodeItem * AddNode(SimPT_Sim::Node *node)
Adds a node to the scene.
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...
void GenerateVoronoiPattern()
Generates a cell pattern using Voronoi Tesselation in the selected cell.
Interface for PolygonUtils.
Mode GetMode() const
Returns the current mode.
void CancelAction()
Cancel the current action.
Interface for LeafControlLogic.
void Initialize(std::shared_ptr< EditControlLogic > tissue, Mode mode=Mode::DISPLAY)
Adds the items in the tissue to the scene.
void DeleteItem()
Delete the selected item.
see the online Qt documentation
A cell wall, runs between cell corner points and consists of wall elements.
Definition: Wall.h:48
Interface for EditableEdgeItem.
Interface for Wall.
void SetDisplayModePreferences()
Set the preferences of the display mode ('NONE').
Dialog for generating regular patterns in a cell.
Interface for EditableCellItem.