1 | /** \file |
2 | * \brief Tests for ogdf::GraphAttributes. |
3 | * |
4 | * \author Mirko Wagner, Tilo Wiedera |
5 | * |
6 | * \par License: |
7 | * This file is part of the Open Graph Drawing Framework (OGDF). |
8 | * |
9 | * \par |
10 | * Copyright (C)<br> |
11 | * See README.md in the OGDF root directory for details. |
12 | * |
13 | * \par |
14 | * This program is free software; you can redistribute it and/or |
15 | * modify it under the terms of the GNU General Public License |
16 | * Version 2 or 3 as published by the Free Software Foundation; |
17 | * see the file LICENSE.txt included in the packaging of this file |
18 | * for details. |
19 | * |
20 | * \par |
21 | * This program is distributed in the hope that it will be useful, |
22 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
23 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
24 | * GNU General Public License for more details. |
25 | * |
26 | * \par |
27 | * You should have received a copy of the GNU General Public |
28 | * License along with this program; if not, see |
29 | * http://www.gnu.org/copyleft/gpl.html |
30 | */ |
31 | |
32 | #include <ogdf/basic/DualGraph.h> |
33 | #include <ogdf/basic/graph_generators.h> |
34 | #include <ogdf/basic/extended_graph_alg.h> |
35 | #include <resources.h> |
36 | |
37 | using GA = GraphAttributes; |
38 | |
39 | /** |
40 | * Tests getters and setter of an attribute. |
41 | * |
42 | * \param elemFunc Returns a list of elements which properties are to be tested. |
43 | * \param refFunc Returns a non-const reference to the attribute (getter & setter to be tested). |
44 | * \param constRefFunc Returns a copy of the attribute (second getter to be tested). |
45 | * \param defaultValue Value that the attribute is supposed to be initialized to. |
46 | * \param secondValue Differs from \p defaultValue, used for testing setters. |
47 | * \param neededAttributes Attribute flags that are required to enable the attribute. |
48 | * \param attributeName Human-readable name of the property. Used to create a title for the test. |
49 | * \tparam Attribute Type of property to be read/written. |
50 | * \tparam Element Type of the graph element that has the property. |
51 | */ |
52 | template<class Attribute, class Element> |
53 | void testAttribute( |
54 | std::function<List<Element>(const Graph&)> elemFunc, |
55 | std::function<Attribute& (GraphAttributes&, Element)> refFunc, |
56 | std::function<Attribute (const GraphAttributes&, Element)> constRefFunc, |
57 | Attribute defaultValue, |
58 | Attribute secondValue, |
59 | long neededAttributes, |
60 | string attributeName) |
61 | { |
62 | describe(attributeName, [&] { |
63 | Graph graph; |
64 | GraphAttributes attr(graph); |
65 | List<Element> elements; |
66 | |
67 | before_each([&] { |
68 | completeGraph(graph, 7); |
69 | attr.init(neededAttributes); |
70 | elements = elemFunc(graph); |
71 | }); |
72 | |
73 | #ifdef OGDF_USE_ASSERT_EXCEPTIONS |
74 | it("throws an exception on access if the attribute is disabled" , [&] { |
75 | attr.destroyAttributes(neededAttributes); |
76 | AssertThrows(AssertionFailed, refFunc(attr, elements.front())); |
77 | }); |
78 | #endif |
79 | |
80 | it("gets the value" , [&] { |
81 | for(Element elem : elements) { |
82 | AssertThat(constRefFunc(attr, elem), Equals(defaultValue)); |
83 | AssertThat(refFunc(attr, elem), Equals(defaultValue)); |
84 | } |
85 | }); |
86 | |
87 | it("sets the value" , [&] { |
88 | for(Element elem : elements) { |
89 | Attribute &value = refFunc(attr, elem); |
90 | value = secondValue; |
91 | AssertThat(refFunc(attr, elem), Equals(secondValue)); |
92 | AssertThat(constRefFunc(attr, elem), Equals(secondValue)); |
93 | } |
94 | }); |
95 | }); |
96 | } |
97 | |
98 | //! @see testAttribute |
99 | template<class Attribute, class... Args> |
100 | void testNodeAttribute(Args... args) { |
101 | testAttribute<Attribute, node>([](const Graph &graph) { |
102 | List<node> result; |
103 | graph.allNodes(result); |
104 | return result; |
105 | }, std::forward<Args>(args)...); |
106 | } |
107 | |
108 | //! @see testAttribute |
109 | template<class Attribute, class... Args> |
110 | void testEdgeAttribute(Args... args) { |
111 | testAttribute<Attribute, edge>([](const Graph &graph) { |
112 | List<edge> result; |
113 | graph.allEdges(result); |
114 | return result; |
115 | }, std::forward<Args>(args)...); |
116 | } |
117 | |
118 | go_bandit([] { |
119 | describe("graph attributes" , [] { |
120 | it("initializes with no attributes by default" , [] { |
121 | GraphAttributes attr; |
122 | AssertThat(attr.attributes(), Equals(0)); |
123 | }); |
124 | |
125 | it("initializes with a graph and flags" , [] { |
126 | Graph graph; |
127 | GraphAttributes attr(graph, GA::nodeId); |
128 | AssertThat(&attr.constGraph(), Equals(&graph)); |
129 | AssertThat(attr.attributes(), Equals(GA::nodeId)); |
130 | }); |
131 | |
132 | it("initializes with a graph" , [] { |
133 | Graph graph; |
134 | GraphAttributes attr(graph); |
135 | AssertThat(&attr.constGraph(), Equals(&graph)); |
136 | AssertThat(attr.attributes(), Equals(GA::nodeGraphics | GA::edgeGraphics)); |
137 | }); |
138 | |
139 | it("initializes using explicit init" , [] { |
140 | Graph graph; |
141 | GraphAttributes attr; |
142 | attr.init(graph, GA::nodeId); |
143 | AssertThat(&attr.constGraph(), Equals(&graph)); |
144 | AssertThat(attr.attributes(), Equals(GA::nodeId)); |
145 | }); |
146 | |
147 | it("destroys its attributes" , [] { |
148 | Graph graph; |
149 | GraphAttributes attr(graph, GA::nodeGraphics | GA::nodeLabel); |
150 | AssertThat(&attr.constGraph(), Equals(&graph)); |
151 | AssertThat(attr.attributes(), Equals(GA::nodeGraphics | GA::nodeLabel)); |
152 | attr.destroyAttributes(GA::nodeGraphics | GA::nodeId); |
153 | AssertThat(attr.attributes(), Equals(GA::nodeLabel)); |
154 | }); |
155 | |
156 | it("adds new attributes" , [] { |
157 | Graph graph; |
158 | GraphAttributes attr(graph, GA::nodeGraphics | GA::nodeLabel); |
159 | AssertThat(&attr.constGraph(), Equals(&graph)); |
160 | AssertThat(attr.attributes(), Equals(GA::nodeGraphics | GA::nodeLabel)); |
161 | attr.addAttributes(GA::nodeId | GA::nodeLabel); |
162 | AssertThat(attr.attributes(), Equals(GA::nodeGraphics | GA::nodeLabel | GA::nodeId)); |
163 | }); |
164 | |
165 | it("knows its currently enabled attributes" , [] { |
166 | Graph graph; |
167 | GraphAttributes attr(graph, GA::nodeId | GA::nodeLabel); |
168 | AssertThat(attr.has(GA::nodeId | GA::nodeLabel), IsTrue()); |
169 | AssertThat(attr.has(GA::nodeId), IsTrue()); |
170 | AssertThat(attr.has(GA::nodeId | GA::nodeGraphics), IsFalse()); |
171 | AssertThat(attr.has(GA::nodeGraphics), IsFalse()); |
172 | }); |
173 | |
174 | describe("attributes" , [] { |
175 | it("knows if it's directed" , [] { |
176 | Graph graph; |
177 | GraphAttributes attr(graph); |
178 | AssertThat(attr.directed(), IsTrue()); |
179 | attr.directed() = false; |
180 | AssertThat(attr.directed(), IsFalse()); |
181 | }); |
182 | |
183 | testNodeAttribute<double>( |
184 | [](GraphAttributes &a, node v) -> double& { return a.x(v); }, |
185 | [](const GraphAttributes &a, node v) { return a.x(v); }, |
186 | 0, 42, |
187 | GA::nodeGraphics, "x" ); |
188 | |
189 | testNodeAttribute<double>( |
190 | [](GraphAttributes &a, node v) -> double& { return a.xLabel(v); }, |
191 | [](const GraphAttributes &a, node v) { return a.xLabel(v); }, |
192 | 0, 42, |
193 | GA::nodeLabel | GA::nodeLabelPosition, "xLabel" ); |
194 | |
195 | testNodeAttribute<double>( |
196 | [](GraphAttributes &a, node v) -> double& { return a.y(v); }, |
197 | [](const GraphAttributes &a, node v) { return a.y(v); }, |
198 | 0, 42, |
199 | GA::nodeGraphics, "y" ); |
200 | |
201 | testNodeAttribute<double>( |
202 | [](GraphAttributes &a, node v) -> double& { return a.yLabel(v); }, |
203 | [](const GraphAttributes &a, node v) { return a.yLabel(v); }, |
204 | 0, 42, |
205 | GA::nodeLabel | GA::nodeLabelPosition, "yLabel" ); |
206 | |
207 | testNodeAttribute<double>( |
208 | [](GraphAttributes &a, node v) -> double& { return a.z(v); }, |
209 | [](const GraphAttributes &a, node v) { return a.z(v); }, |
210 | 0, 42, |
211 | GA::nodeGraphics | GA::threeD, "z" ); |
212 | |
213 | testNodeAttribute<double>( |
214 | [](GraphAttributes &a, node v) -> double& { return a.zLabel(v); }, |
215 | [](const GraphAttributes &a, node v) { return a.zLabel(v); }, |
216 | 0, 42, |
217 | GA::nodeLabel | GA::nodeLabelPosition | GA::threeD | GA::nodeGraphics, "zLabel" ); |
218 | |
219 | testNodeAttribute<double>( |
220 | [](GraphAttributes &a, node v) -> double& { return a.width(v); }, |
221 | [](const GraphAttributes &a, node v) { return a.width(v); }, |
222 | 20, 42, |
223 | GA::nodeGraphics, "width of a node" ); |
224 | |
225 | testNodeAttribute<int>( |
226 | [](GraphAttributes &a, node v) -> int& { return a.weight(v); }, |
227 | [](const GraphAttributes &a, node v) { return a.weight(v); }, |
228 | 0, 42, |
229 | GA::nodeWeight, "weight of a node" ); |
230 | |
231 | testEdgeAttribute<Graph::EdgeType>( |
232 | [](GraphAttributes &a, edge e) -> Graph::EdgeType& { return a.type(e); }, |
233 | [](const GraphAttributes &a, edge e) { return a.type(e); }, |
234 | Graph::EdgeType::association, Graph::EdgeType::generalization, |
235 | GA::edgeType, "type of an edge" ); |
236 | |
237 | testNodeAttribute<Graph::NodeType>( |
238 | [](GraphAttributes &a, node v) -> Graph::NodeType& { return a.type(v); }, |
239 | [](const GraphAttributes &a, node v) { return a.type(v); }, |
240 | Graph::NodeType::vertex, Graph::NodeType::dummy, |
241 | GA::nodeType, "type of a node" ); |
242 | |
243 | testEdgeAttribute<uint32_t>( |
244 | [](GraphAttributes &a, edge e) -> uint32_t& { return a.subGraphBits(e); }, |
245 | [](const GraphAttributes &a, edge e) { return a.subGraphBits(e); }, |
246 | 0, 42, |
247 | GA::edgeSubGraphs, "SubGraphBits" ); |
248 | |
249 | testEdgeAttribute<float>( |
250 | [](GraphAttributes &a, edge e) -> float& { return a.strokeWidth(e); }, |
251 | [](const GraphAttributes &a, edge e) { return a.strokeWidth(e); }, |
252 | LayoutStandards::defaultEdgeStroke().m_width, 42, |
253 | GA::edgeStyle | GA::edgeGraphics, "strokeWidth edge" ); |
254 | |
255 | testNodeAttribute<float>( |
256 | [](GraphAttributes &a, node v) -> float& { return a.strokeWidth(v); }, |
257 | [](const GraphAttributes &a, node v) { return a.strokeWidth(v); }, |
258 | LayoutStandards::defaultEdgeStroke().m_width, 42, |
259 | GA::nodeStyle | GA::nodeGraphics, "strokeWidth node" ); |
260 | |
261 | testNodeAttribute<StrokeType>( |
262 | [](GraphAttributes &a, node v) -> StrokeType& { return a.strokeType(v); }, |
263 | [](const GraphAttributes &a, node v) { return a.strokeType(v); }, |
264 | LayoutStandards::defaultNodeStroke().m_type, StrokeType::Dot, |
265 | GA::nodeStyle | GA::nodeGraphics, "strokeType node" ); |
266 | |
267 | testEdgeAttribute<StrokeType>( |
268 | [](GraphAttributes &a, edge e) -> StrokeType& { return a.strokeType(e); }, |
269 | [](const GraphAttributes &a, edge e) { return a.strokeType(e); }, |
270 | LayoutStandards::defaultEdgeStroke().m_type, StrokeType::Dot, |
271 | GA::edgeStyle | GA::edgeGraphics, "strokeType edge" ); |
272 | |
273 | testEdgeAttribute<Color>( |
274 | [](GraphAttributes &a, edge e) -> Color& { return a.strokeColor(e); }, |
275 | [](const GraphAttributes &a, edge e) { return a.strokeColor(e); }, |
276 | LayoutStandards::defaultEdgeStroke().m_color, ogdf::Color::Name::Turquoise, |
277 | GA::edgeStyle | GA::edgeGraphics, "strokeColor edge" ); |
278 | |
279 | testNodeAttribute<Color>( |
280 | [](GraphAttributes &a, node v) -> Color& { return a.strokeColor(v); }, |
281 | [](const GraphAttributes &a, node v) { return a.strokeColor(v); }, |
282 | LayoutStandards::defaultEdgeStroke().m_color, ogdf::Color::Name::Turquoise, |
283 | GA::nodeStyle | GA::nodeGraphics, "strokeColor node" ); |
284 | |
285 | testNodeAttribute<Shape>( |
286 | [](GraphAttributes &a, node v) -> Shape& { return a.shape(v); }, |
287 | [](const GraphAttributes &a, node v) { return a.shape(v); }, |
288 | LayoutStandards::defaultNodeShape(), Shape::Rect, |
289 | GA::nodeGraphics, "shape node" ); |
290 | |
291 | testEdgeAttribute<EdgeArrow>( |
292 | [](GraphAttributes &a, edge e) -> EdgeArrow& { return a.arrowType(e); }, |
293 | [](const GraphAttributes &a, edge e) { return a.arrowType(e); }, |
294 | LayoutStandards::defaultEdgeArrow(), EdgeArrow::Both, |
295 | GA::edgeArrow, "arrowType" ); |
296 | |
297 | testEdgeAttribute<double>( |
298 | [](GraphAttributes &a, edge e) -> double& { return a.doubleWeight(e); }, |
299 | [](const GraphAttributes &a, edge e) { return a.doubleWeight(e); }, |
300 | 1.0, 42.0, |
301 | GA::edgeDoubleWeight, "doubleWeight" ); |
302 | |
303 | testNodeAttribute<Color>( |
304 | [](GraphAttributes &a, node v) -> Color& { return a.fillBgColor(v); }, |
305 | [](const GraphAttributes &a, node v) { return a.fillBgColor(v); }, |
306 | LayoutStandards::defaultNodeFill().m_bgColor, ogdf::Color::Name::Turquoise, |
307 | GA::nodeStyle | GA::nodeGraphics, "fillBgColor" ); |
308 | |
309 | testNodeAttribute<Color>( |
310 | [](GraphAttributes &a, node v) -> Color& { return a.fillColor(v); }, |
311 | [](const GraphAttributes &a, node v) { return a.fillColor(v); }, |
312 | LayoutStandards::defaultNodeFill().m_color, Color(ogdf::Color::Name::Turquoise), |
313 | GA::nodeStyle | GA::nodeGraphics, "fillColor" ); |
314 | |
315 | testNodeAttribute<FillPattern>( |
316 | [](GraphAttributes &a, node v) -> FillPattern& { return a.fillPattern(v); }, |
317 | [](const GraphAttributes &a, node v) { return a.fillPattern(v); }, |
318 | LayoutStandards::defaultNodeFill().m_pattern, FillPattern::Cross, |
319 | GA::nodeStyle | GA::nodeGraphics, "fillPattern" ); |
320 | |
321 | testNodeAttribute<int>( |
322 | [](GraphAttributes &a, node v) -> int& { return a.idNode(v); }, |
323 | [](const GraphAttributes &a, node v) { return a.idNode(v); }, |
324 | -1, 42, |
325 | GA::nodeId, "idNode" ); |
326 | |
327 | describe("advanced" , [] { |
328 | Graph graph; |
329 | GraphAttributes attr(graph); |
330 | const GraphAttributes &constAttr = attr; |
331 | |
332 | before_each([&] { |
333 | completeGraph(graph, 7); |
334 | }); |
335 | |
336 | it("(in|add|remove)SubGraph" , [&] { |
337 | edge e = graph.chooseEdge(); |
338 | #ifdef OGDF_USE_ASSERT_EXCEPTIONS |
339 | AssertThrows(AssertionFailed, attr.inSubGraph(e, 13)); |
340 | #endif |
341 | attr.init(GA::edgeSubGraphs); |
342 | AssertThat(constAttr.inSubGraph(e, 13), IsFalse()); |
343 | AssertThat(attr.inSubGraph(e, 13), IsFalse()); |
344 | attr.addSubGraph(e, 13); |
345 | AssertThat(constAttr.inSubGraph(e, 13), IsTrue()); |
346 | AssertThat(attr.inSubGraph(e, 13), IsTrue()); |
347 | attr.removeSubGraph(e, 13); |
348 | AssertThat(constAttr.inSubGraph(e, 13), IsFalse()); |
349 | AssertThat(attr.inSubGraph(e, 13), IsFalse()); |
350 | }); |
351 | |
352 | it("assigns width using a NodeArray" , [&] { |
353 | #ifdef OGDF_USE_ASSERT_EXCEPTIONS |
354 | attr.destroyAttributes(GA::nodeGraphics); |
355 | AssertThrows(AssertionFailed, attr.width()); |
356 | #endif |
357 | attr.init(GA::nodeGraphics); |
358 | node v = graph.chooseNode(); |
359 | NodeArray<double> widthNA(graph, 42); |
360 | AssertThat(constAttr.width().graphOf(), Equals(&graph)); |
361 | AssertThat(attr.width().graphOf(), Equals(&graph)); |
362 | AssertThat(constAttr.width()[v], Equals(LayoutStandards::defaultNodeWidth())); |
363 | AssertThat(attr.width()[v], Equals(LayoutStandards::defaultNodeWidth())); |
364 | attr.width() = widthNA; |
365 | AssertThat(constAttr.width()[v], Equals(42)); |
366 | AssertThat(attr.width()[v], Equals(42)); |
367 | attr.setAllWidth(1337); |
368 | AssertThat(constAttr.width(v), Equals(1337)); |
369 | AssertThat(attr.width(v), Equals(1337)); |
370 | }); |
371 | |
372 | testNodeAttribute<double>( |
373 | [](GraphAttributes &a, node v) -> double& { return a.height(v); }, |
374 | [](const GraphAttributes &a, node v) { return a.height(v); }, |
375 | 20, 42, |
376 | GA::nodeGraphics, "height of a node" ); |
377 | |
378 | it("assigns height using a NodeArray" , [&] { |
379 | #ifdef OGDF_USE_ASSERT_EXCEPTIONS |
380 | attr.destroyAttributes(GA::nodeGraphics); |
381 | AssertThrows(AssertionFailed, attr.height()); |
382 | #endif |
383 | attr.init(GA::nodeGraphics); |
384 | node v = graph.chooseNode(); |
385 | NodeArray<double> heightNA(graph, 42); |
386 | AssertThat(constAttr.height().graphOf(), Equals(&graph)); |
387 | AssertThat(attr.height().graphOf(), Equals(&graph)); |
388 | attr.height() = heightNA; |
389 | AssertThat(constAttr.height()[v], Equals(42)); |
390 | AssertThat(attr.height()[v], Equals(42)); |
391 | attr.setAllHeight(1337); |
392 | AssertThat(constAttr.height(v), Equals(1337)); |
393 | AssertThat(attr.height(v), Equals(1337)); |
394 | }); |
395 | }); |
396 | |
397 | testEdgeAttribute<int>( |
398 | [](GraphAttributes &a, edge e) -> int& { return a.intWeight(e); }, |
399 | [](const GraphAttributes &a, edge e) { return a.intWeight(e); }, |
400 | 1, 42, |
401 | GA::edgeIntWeight, "intWeight" ); |
402 | |
403 | testNodeAttribute<string>( |
404 | [](GraphAttributes &a, node v) -> string& { return a.label(v); }, |
405 | [](const GraphAttributes &a, node v) { return a.label(v); }, |
406 | "" , "ogdf" , |
407 | GA::nodeLabel, "label" ); |
408 | |
409 | testEdgeAttribute<string>( |
410 | [](GraphAttributes &a, edge e) -> string& { return a.label(e); }, |
411 | [](const GraphAttributes &a, edge e) { return a.label(e); }, |
412 | "" , "ogdf" , |
413 | GA::edgeLabel, "label" ); |
414 | |
415 | |
416 | testNodeAttribute<string>( |
417 | [](GraphAttributes &a, node v) -> string& { return a.templateNode(v); }, |
418 | [](const GraphAttributes &a, node v) { return a.templateNode(v); }, |
419 | "" , "ogdf" , |
420 | GA::nodeTemplate, "templateNode" ); |
421 | }); |
422 | |
423 | describe("change position of elements" , [] { |
424 | GraphAttributes attr; |
425 | Graph graph; |
426 | |
427 | before_each([&] { |
428 | completeGraph(graph, 100); |
429 | attr = GraphAttributes(graph, GA::nodeGraphics | GA::edgeGraphics); |
430 | for(node v : graph.nodes) { |
431 | attr.x(v) = ogdf::randomNumber(-100, 100); |
432 | attr.y(v) = ogdf::randomNumber(-100, 100); |
433 | } |
434 | attr.addNodeCenter2Bends(1); |
435 | attr.translateToNonNeg(); |
436 | }); |
437 | |
438 | it("translates to non-negative coordinates" , [&] { |
439 | for(node v : graph.nodes) { |
440 | AssertThat(attr.x(v) - attr.width(v) / 2, IsGreaterThan(0) || Equals(0)); |
441 | AssertThat(attr.y(v) - attr.width(v) / 2, IsGreaterThan(0) || Equals(0)); |
442 | } |
443 | for(edge e : graph.edges) { |
444 | for(DPoint &p : attr.bends(e)) { |
445 | AssertThat(p.m_x, IsGreaterThan(0) || Equals(0)); |
446 | AssertThat(p.m_y, IsGreaterThan(0) || Equals(0)); |
447 | } |
448 | } |
449 | }); |
450 | |
451 | it("translates" , [&] { |
452 | GraphAttributes ga = GraphAttributes(attr); |
453 | attr.translate(1.0, 42.0); |
454 | for(node v : graph.nodes) { |
455 | AssertThat(attr.x(v), Equals(ga.x(v)+1.0)); |
456 | AssertThat(attr.y(v), Equals(ga.y(v)+42.0)); |
457 | } |
458 | for(edge e : graph.edges) { |
459 | DPolyline &bendpoints = ga.bends(e); |
460 | for(DPoint &p_new : attr.bends(e)) { |
461 | DPoint p_old = bendpoints.popFrontRet(); |
462 | AssertThat(p_new.m_x, Equals(p_old.m_x+1.0)); |
463 | AssertThat(p_new.m_y, Equals(p_old.m_y+42.0)); |
464 | } |
465 | } |
466 | }); |
467 | |
468 | it("scales" , [&] { |
469 | GraphAttributes ga = GraphAttributes(attr); |
470 | attr.scale(-1.0, -2.0, true); |
471 | for(node v : graph.nodes) { |
472 | AssertThat(attr.x(v), Equals(-ga.x(v))); |
473 | AssertThat(attr.y(v), Equals(-2.0 * ga.y(v))); |
474 | } |
475 | for(edge e : graph.edges) { |
476 | DPolyline &bendpoints = ga.bends(e); |
477 | for(DPoint &p_new : attr.bends(e)) { |
478 | DPoint p_old = bendpoints.popFrontRet(); |
479 | AssertThat(p_new.m_x, Equals(-p_old.m_x)); |
480 | AssertThat(p_new.m_y, Equals(-2.0 * p_old.m_y)); |
481 | } |
482 | } |
483 | }); |
484 | |
485 | it("scales and then translates" , [&] { |
486 | GraphAttributes ga = GraphAttributes(attr); |
487 | attr.scaleAndTranslate(-1.0, -42.0, 13, 37, true); |
488 | for(node v : graph.nodes) { |
489 | AssertThat(attr.x(v), Equals(-ga.x(v) + 13)); |
490 | AssertThat(attr.y(v), Equals(-42 * ga.y(v) + 37)); |
491 | } |
492 | for(edge e : graph.edges) { |
493 | DPolyline &bendpoints = ga.bends(e); |
494 | for(DPoint &p_new : attr.bends(e)) { |
495 | DPoint p_old = bendpoints.popFrontRet(); |
496 | AssertThat(p_new.m_x, Equals(-p_old.m_x + 13)); |
497 | AssertThat(p_new.m_y, Equals(-42 * p_old.m_y + 37)); |
498 | } |
499 | } |
500 | }); |
501 | |
502 | it("flips vertical within its bounding box" , [&] { |
503 | DRect boundingBox = attr.boundingBox(); |
504 | double height = boundingBox.height(); |
505 | GraphAttributes ga = GraphAttributes(attr); |
506 | attr.flipVertical(); |
507 | for(node v : graph.nodes) { |
508 | AssertThat(attr.x(v), Equals(ga.x(v))); |
509 | AssertThat(attr.y(v), Equals(height - ga.y(v))); |
510 | } |
511 | for(edge e : graph.edges) { |
512 | DPolyline &bendpoints = ga.bends(e); |
513 | for(DPoint &p_new : attr.bends(e)) { |
514 | DPoint p_old = bendpoints.popFrontRet(); |
515 | AssertThat(p_new.m_x, Equals(p_old.m_x)); |
516 | AssertThat(p_new.m_y, Equals(height - p_old.m_y)); |
517 | } |
518 | } |
519 | }); |
520 | |
521 | it("flips vertical with a given box" , [&] { |
522 | GraphAttributes ga = GraphAttributes(attr); |
523 | attr.flipVertical(DRect()); |
524 | for(node v : graph.nodes) { |
525 | AssertThat(attr.x(v), Equals(ga.x(v))); |
526 | AssertThat(attr.y(v), Equals(-ga.y(v))); |
527 | } |
528 | for(edge e : graph.edges) { |
529 | DPolyline &bendpoints = ga.bends(e); |
530 | for(DPoint &p_new : attr.bends(e)) { |
531 | DPoint p_old = bendpoints.popFrontRet(); |
532 | AssertThat(p_new.m_x, Equals(p_old.m_x)); |
533 | AssertThat(p_new.m_y, Equals(-p_old.m_y)); |
534 | } |
535 | } |
536 | }); |
537 | |
538 | it("flips horizontal within its bounding box" , [&] { |
539 | DRect boundingBox = attr.boundingBox(); |
540 | double width = boundingBox.width(); |
541 | GraphAttributes ga = GraphAttributes(attr); |
542 | attr.flipHorizontal(); |
543 | for(node v : graph.nodes) { |
544 | AssertThat(attr.x(v), Equals(width - ga.x(v))); |
545 | AssertThat(attr.y(v), Equals(ga.y(v))); |
546 | } |
547 | for(edge e : graph.edges) { |
548 | DPolyline &bendpoints = ga.bends(e); |
549 | for(DPoint &p_new : attr.bends(e)) { |
550 | DPoint p_old = bendpoints.popFrontRet(); |
551 | AssertThat(p_new.m_x, Equals(width - p_old.m_x)); |
552 | AssertThat(p_new.m_y, Equals(p_old.m_y)); |
553 | } |
554 | } |
555 | }); |
556 | |
557 | it("flips horizontal with a given box" , [&] { |
558 | GraphAttributes ga = GraphAttributes(attr); |
559 | attr.flipHorizontal(DRect()); |
560 | for(node v : graph.nodes) { |
561 | AssertThat(attr.x(v), Equals(-ga.x(v))); |
562 | AssertThat(attr.y(v), Equals(ga.y(v))); |
563 | } |
564 | for(edge e : graph.edges) { |
565 | DPolyline &bendpoints = ga.bends(e); |
566 | for(DPoint &p_new : attr.bends(e)) { |
567 | DPoint p_old = bendpoints.popFrontRet(); |
568 | AssertThat(p_new.m_x, Equals(-p_old.m_x)); |
569 | AssertThat(p_new.m_y, Equals(p_old.m_y)); |
570 | } |
571 | } |
572 | }); |
573 | |
574 | it("rotates left" , [&] { |
575 | GraphAttributes ga = GraphAttributes(attr); |
576 | attr.rotateLeft90(); |
577 | for(node v : graph.nodes) { |
578 | AssertThat(attr.x(v), Equals(ga.y(v))); |
579 | AssertThat(attr.y(v), Equals(-ga.x(v))); |
580 | } |
581 | for(edge e : graph.edges) { |
582 | DPolyline &bendpoints = ga.bends(e); |
583 | for(DPoint &p_new : attr.bends(e)) { |
584 | DPoint p_old = bendpoints.popFrontRet(); |
585 | AssertThat(p_new.m_x, Equals(p_old.m_y)); |
586 | AssertThat(p_new.m_y, Equals(-p_old.m_x)); |
587 | } |
588 | } |
589 | }); |
590 | |
591 | it("rotates right" , [&] { |
592 | GraphAttributes ga = GraphAttributes(attr); |
593 | attr.rotateRight90(); |
594 | for(node v : graph.nodes) { |
595 | AssertThat(attr.x(v), Equals(-ga.y(v))); |
596 | AssertThat(attr.y(v), Equals(ga.x(v))); |
597 | } |
598 | for(edge e : graph.edges) { |
599 | DPolyline &bendpoints = ga.bends(e); |
600 | for(DPoint &p_new : attr.bends(e)) { |
601 | DPoint p_old = bendpoints.popFrontRet(); |
602 | AssertThat(p_new.m_x, Equals(-p_old.m_y)); |
603 | AssertThat(p_new.m_y, Equals(p_old.m_x)); |
604 | } |
605 | } |
606 | }); |
607 | }); |
608 | |
609 | it("knows its bounding box" , [] { |
610 | Graph graph; |
611 | randomGraph(graph, 100, 1000); |
612 | GraphAttributes attr(graph, GA::nodeGraphics | GA::edgeGraphics); |
613 | for(node v : graph.nodes) { |
614 | attr.x(v) = ogdf::randomNumber(-1000, 1000); |
615 | attr.y(v) = ogdf::randomNumber(-1000, 1000); |
616 | } |
617 | attr.addNodeCenter2Bends(1); |
618 | attr.translateToNonNeg(); |
619 | DRect boundBox = attr.boundingBox(); |
620 | AssertThat(boundBox.p1().m_x, Equals(0) || IsGreaterThan(0)); |
621 | AssertThat(boundBox.p1().m_y, Equals(0) || IsGreaterThan(0)); |
622 | AssertThat(boundBox.p2().m_x, Equals(2020) || IsLessThan(2020)); |
623 | AssertThat(boundBox.p2().m_y, Equals(2020) || IsLessThan(2020)); |
624 | for(node v : graph.nodes) { |
625 | AssertThat(boundBox.contains(DPoint(attr.x(v), attr.y(v))), IsTrue()); |
626 | } |
627 | for(edge e : graph.edges) { |
628 | for(DPoint &p : attr.bends(e)) { |
629 | AssertThat(boundBox.contains(p), IsTrue()); |
630 | } |
631 | } |
632 | }); |
633 | |
634 | describe("bends" , [] { |
635 | Graph graph; |
636 | GraphAttributes attr; |
637 | |
638 | before_each([&] { |
639 | completeGraph(graph, 3); |
640 | attr = GraphAttributes(graph, GA::nodeGraphics | GA::edgeGraphics); |
641 | }); |
642 | |
643 | it("clears all bends" , [&] { |
644 | attr.addNodeCenter2Bends(1); |
645 | AssertThat(attr.bends(graph.chooseEdge()).size(), IsGreaterThan(0)); |
646 | attr.clearAllBends(); |
647 | for(edge e : graph.edges) { |
648 | AssertThat(attr.bends(e).size(), Equals(0)); |
649 | } |
650 | }); |
651 | |
652 | it("knows its bends" , [&] { |
653 | for(edge e : graph.edges) { |
654 | AssertThat(attr.bends(e).size(), Equals(0)); |
655 | } |
656 | attr.addNodeCenter2Bends(0); |
657 | for(edge e : graph.edges) { |
658 | AssertThat(attr.bends(e).size(), Equals(2)); |
659 | } |
660 | edge e = graph.chooseEdge(); |
661 | DPolyline dpl; |
662 | dpl.emplaceFront(42, 17); |
663 | attr.bends(e) = dpl; |
664 | AssertThat(attr.bends(e).size(), Equals(1)); |
665 | AssertThat((*attr.bends(e).get(0)).m_x, Equals(42)); |
666 | AssertThat((*attr.bends(e).get(0)).m_y, Equals(17)); |
667 | }); |
668 | }); |
669 | }); |
670 | }); |
671 | |