Tulip  5.7.4
Large graphs analysis and drawing
minmaxproperty.cxx
1 /*
2  *
3  * This file is part of Tulip (https://tulip.labri.fr)
4  *
5  * Authors: David Auber and the Tulip development Team
6  * from LaBRI, University of Bordeaux
7  *
8  * Tulip is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU Lesser General Public License
10  * as published by the Free Software Foundation, either version 3
11  * of the License, or (at your option) any later version.
12  *
13  * Tulip is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
16  * See the GNU General Public License for more details.
17  *
18  */
19 #include <tulip/Graph.h>
20 #include <tulip/Coord.h>
21 
22 template <typename nodeType, typename edgeType, typename propType>
24  tlp::Graph *graph, const std::string &name, NODE_VALUE NodeMin, NODE_VALUE NodeMax,
25  EDGE_VALUE EdgeMin, EDGE_VALUE EdgeMax)
26  : AbstractProperty<nodeType, edgeType, propType>(graph, name), _nodeMin(NodeMin),
27  _nodeMax(NodeMax), _edgeMin(EdgeMin), _edgeMax(EdgeMax), needGraphListener(false) {}
28 
29 template <typename nodeType, typename edgeType, typename propType>
31  const tlp::Graph *graph) {
32  if (!graph) {
33  graph = this->propType::graph;
34  }
35 
36  unsigned int graphID = graph->getId();
37  auto it = minMaxNode.find(graphID);
38 
39  return (it == minMaxNode.end()) ? computeMinMaxNode(graph) : it->second;
40 }
41 
42 template <typename nodeType, typename edgeType, typename propType>
44  const tlp::Graph *graph) {
45  if (!graph) {
46  graph = this->propType::graph;
47  }
48 
49  unsigned int graphID = graph->getId();
50  auto it = minMaxEdge.find(graphID);
51 
52  return (it == minMaxEdge.end()) ? computeMinMaxEdge(graph) : it->second;
53 }
54 
55 template <typename nodeType, typename edgeType, typename propType>
56  const MINMAX_PAIR(nodeType) &
58  if (!graph) {
59  graph = this->propType::graph;
60  }
61 
62  NODE_VALUE maxN2 = _nodeMin, minN2 = _nodeMax;
63 
65  for (auto n : graph->nodes()) {
66  CONST_NODE_VALUE tmp = this->getNodeValue(n);
67 
68  if (tmp > maxN2)
69  maxN2 = tmp;
70  if (tmp < minN2)
71  minN2 = tmp;
72  }
73  }
74 
75  if (maxN2 < minN2)
76  maxN2 = minN2 = AbstractProperty<nodeType, edgeType, propType>::nodeDefaultValue;
77 
78  unsigned int sgi = graph->getId();
79 
80  // graph observation is now delayed
81  // until we need to do some minmax computation
82  // this will minimize the graph loading
83  if (minMaxNode.find(sgi) == minMaxNode.end() && minMaxEdge.find(sgi) == minMaxEdge.end()) {
84  // launch graph hierarchy observation
85  graph->addListener(this);
86  }
87 
88  return minMaxNode[sgi] = {minN2, maxN2};
89 }
90 
91 template <typename nodeType, typename edgeType, typename propType>
92  const MINMAX_PAIR(edgeType) &
94  EDGE_VALUE maxE2 = _edgeMin, minE2 = _edgeMax;
95 
97  for (auto ite : graph->edges()) {
98  CONST_EDGE_VALUE tmp = this->getEdgeValue(ite);
99 
100  if (tmp > maxE2)
101  maxE2 = tmp;
102  if (tmp < minE2)
103  minE2 = tmp;
104  }
105  }
106 
107  if (maxE2 < minE2)
108  maxE2 = minE2 = AbstractProperty<nodeType, edgeType, propType>::edgeDefaultValue;
109 
110  unsigned int sgi = graph->getId();
111 
112  // graph observation is now delayed
113  // until we need to do some minmax computation
114  // this will minimize the graph loading time
115  if (minMaxNode.find(sgi) == minMaxNode.end() && minMaxEdge.find(sgi) == minMaxEdge.end()) {
116  // launch graph hierarchy observation
117  graph->addListener(this);
118  }
119 
120  return minMaxEdge[sgi] = {minE2, maxE2};
121 }
122 
123 template <typename nodeType, typename edgeType, typename propType>
125  // we need to clear one of our map
126  // this will invalidate some minmax computations
127  // so the graphs corresponding to these cleared minmax computations
128  // may not have to be longer observed if they have no validated
129  // minmax computation in the other map
130 
131  // loop to remove unneeded graph observation
132  // it is the case if minmax computation
133  //
134  auto it = minMaxNode.begin();
135  auto ite = minMaxNode.end();
136 
137  for (; it != ite; ++it) {
138  unsigned int gi = it->first;
139  auto itg = minMaxEdge.find(gi);
140 
141  if (itg == minMaxEdge.end()) {
142  // no computation in the other map
143  // we can stop observing the current graph
144  Graph *g = (propType::graph->getId() == gi) ? (needGraphListener ? nullptr : propType::graph)
145  : propType::graph->getDescendantGraph(gi);
146 
147  if (g)
148  g->removeListener(this);
149  }
150  }
151 
152  // finally clear the map
153  minMaxNode.clear();
154 }
155 
156 template <typename nodeType, typename edgeType, typename propType>
158  // we need to clear one of our map
159  // this will invalidate some minmax computations
160  // so the graphs corresponding to these cleared minmax computations
161  // may not have to be longer observed if they have no validated
162  // minmax computation in the other map
163 
164  // loop to remove unneeded graph observation
165  // it is the case if minmax computation
166  //
167  auto it = minMaxEdge.begin();
168  auto ite = minMaxEdge.end();
169 
170  for (; it != ite; ++it) {
171  unsigned int gi = it->first;
172  auto itg = minMaxNode.find(gi);
173 
174  if (itg == minMaxNode.end()) {
175  // no computation in the other map
176  // we can stop observing the current graph
177  Graph *g = (propType::graph->getId() == gi) ? (needGraphListener ? nullptr : propType::graph)
178  : propType::graph->getDescendantGraph(gi);
179 
180  if (g)
181  g->removeListener(this);
182  }
183  }
184 
185  // finally clear the map
186  minMaxEdge.clear();
187 }
188 
189 template <typename nodeType, typename edgeType, typename propType>
191  CONST_NODE_VALUE newValue) {
192  auto it = minMaxNode.begin();
193 
194  if (it != minMaxNode.end()) {
195  CONST_NODE_VALUE oldV = this->getNodeValue(n);
196 
197  if (newValue != oldV) {
198  // loop on subgraph min/max
199  for (; it != minMaxNode.end(); ++it) {
200  auto sid = it->first;
201  if (sid) {
202  // check if n belongs to current subgraph
203  auto sg = this->graph->getDescendantGraph(sid);
204  // sg might be null in undo/redo context
205  if (sg && !sg->isElement(n))
206  continue;
207  }
208  // if min/max is ok for the current subgraph
209  // check if min or max has to be updated
210  CONST_NODE_VALUE minV = it->second.first;
211  CONST_NODE_VALUE maxV = it->second.second;
212 
213  // check if min or max has to be updated
214  if ((newValue < minV) || (newValue > maxV) || (oldV == minV) || (oldV == maxV)) {
215  removeListenersAndClearNodeMap();
216  break;
217  }
218  }
219  }
220  }
221 }
222 
223 template <typename nodeType, typename edgeType, typename propType>
225  CONST_EDGE_VALUE newValue) {
226  auto it = minMaxEdge.begin();
227 
228  if (it != minMaxEdge.end()) {
229  CONST_EDGE_VALUE oldV = this->getEdgeValue(e);
230 
231  if (newValue != oldV) {
232  // loop on subgraph min/max
233  for (; it != minMaxEdge.end(); ++it) {
234  auto sid = it->first;
235  if (sid) {
236  // check if e belongs to current subgraph
237  // sg might be null in undo/redo context
238  auto sg = this->graph->getDescendantGraph(sid);
239  if (sg && !sg->isElement(e))
240  continue;
241  }
242  // if min/max is ok for the current subgraph
243  // check if min or max has to be updated
244  CONST_EDGE_VALUE minV = it->second.first;
245  CONST_EDGE_VALUE maxV = it->second.second;
246 
247  // check if min or max has to be updated
248  if ((newValue < minV) || (newValue > maxV) || (oldV == minV) || (oldV == maxV)) {
249  removeListenersAndClearEdgeMap();
250  break;
251  }
252  }
253  }
254  }
255 }
256 
257 template <typename nodeType, typename edgeType, typename propType>
259  CONST_NODE_VALUE newValue) {
260  auto it = minMaxNode.begin();
261  // loop on subgraph min/max
262  MINMAX_PAIR(nodeType) minmax(newValue, newValue);
263 
264  for (; it != minMaxNode.end(); ++it) {
265  unsigned int gid = it->first;
266  minMaxNode[gid] = minmax;
267  }
268 }
269 
270 template <typename nodeType, typename edgeType, typename propType>
272  CONST_EDGE_VALUE newValue) {
273  auto it = minMaxEdge.begin();
274  // loop on subgraph min/max
275  MINMAX_PAIR(edgeType) minmax(newValue, newValue);
276 
277  for (; it != minMaxEdge.end(); ++it) {
278  unsigned int gid = it->first;
279  minMaxEdge[gid] = minmax;
280  }
281 }
282 
283 template <typename nodeType, typename edgeType, typename propType>
285  const GraphEvent *graphEvent = dynamic_cast<const tlp::GraphEvent *>(&ev);
286 
287  if (graphEvent) {
288  tlp::Graph *graph = graphEvent->getGraph();
289 
290  switch (graphEvent->getType()) {
291  case GraphEvent::TLP_ADD_NODE:
292  removeListenersAndClearNodeMap();
293  break;
294 
295  case GraphEvent::TLP_DEL_NODE: {
296  unsigned int sgi = graph->getId();
297  auto it = minMaxNode.find(sgi);
298 
299  if (it != minMaxNode.end()) {
300  CONST_NODE_VALUE oldV = this->getNodeValue(graphEvent->getNode());
301 
302  // check if min or max has to be updated
303  if ((oldV == it->second.first) || (oldV == it->second.second)) {
304  minMaxNode.erase(it);
305 
306  if ((minMaxEdge.find(sgi) == minMaxEdge.end()) &&
307  (!needGraphListener || (graph != propType::graph)))
308  // graph observation is no longer needed
309  graph->removeListener(this);
310  }
311  }
312 
313  break;
314  }
315 
316  case GraphEvent::TLP_ADD_EDGE:
317  removeListenersAndClearEdgeMap();
318  break;
319 
320  case GraphEvent::TLP_DEL_EDGE: {
321  unsigned int sgi = graph->getId();
322  auto it = minMaxEdge.find(sgi);
323 
324  if (it != minMaxEdge.end()) {
325  EDGE_VALUE oldV = this->getEdgeValue(graphEvent->getEdge());
326 
327  // check if min or max has to be updated
328  if ((oldV == it->second.first) || (oldV == it->second.second)) {
329  minMaxEdge.erase(it);
330 
331  if ((minMaxNode.find(sgi) == minMaxNode.end()) &&
332  (!needGraphListener || (graph != propType::graph)))
333  // graph observation is no longer needed
334  graph->removeListener(this);
335  }
336  }
337 
338  break;
339  }
340 
341  default:
342  // we don't care about the rest
343  break;
344  }
345  }
346 }
This class extends upon PropertyInterface, and adds type-safe methods to get and set the node and edg...
bool hasNonDefaultValuatedEdges(const Graph *g=nullptr) const override
Returns whether the property has edges with a non default value. When given a Graph as parameter,...
bool hasNonDefaultValuatedNodes(const Graph *g=nullptr) const override
Returns whether the property has nodes with a non default value. When given a Graph as parameter,...
Event is the base class for all events used in the Observation mechanism.
Definition: Observable.h:52
unsigned int getId() const
Gets the unique identifier of the graph.
Definition: Graph.h:1067
Abstracts the computation of minimal and maximal values on node and edge values of properties.
void updateNodeValue(tlp::node n, CONST_NODE_VALUE newValue)
Updates the value on a node, and updates the minimal/maximal cached values if necessary....
MinMaxProperty(tlp::Graph *graph, const std::string &name, NODE_VALUE NodeMin, NODE_VALUE NodeMax, EDGE_VALUE EdgeMin, EDGE_VALUE EdgeMax)
Constructs a MinMaxProperty.
void updateEdgeValue(tlp::edge e, CONST_EDGE_VALUE newValue)
Updates the value on an edge, and updates the minimal/maximal cached values if necessary....
void updateAllEdgesValues(CONST_EDGE_VALUE newValue)
Updates the value of all edges, setting the maximum and minimum values to this. Should be called by s...
void treatEvent(const tlp::Event &ev) override
This function is called when events are sent to the Listeners, and Listeners only.
void updateAllNodesValues(CONST_NODE_VALUE newValue)
Updates the value of all nodes, setting the maximum and minimum values to this. Should be called by s...
void removeListener(Observable *const listener) const
Removes a listener from this object.
The edge struct represents an edge in a Graph object.
Definition: Edge.h:40
The node struct represents a node in a Graph object.
Definition: Node.h:40