Graphviz 12.0.1~dev.20240716.0800
Loading...
Searching...
No Matches
rescale_layout.c
Go to the documentation of this file.
1/*************************************************************************
2 * Copyright (c) 2011 AT&T Intellectual Property
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the Eclipse Public License v1.0
5 * which accompanies this distribution, and is available at
6 * https://www.eclipse.org/legal/epl-v10.html
7 *
8 * Contributors: Details at https://graphviz.org
9 *************************************************************************/
10
12// //
13// This file contains the functions //
14// for distorting the layout. //
15// //
16// Four methods are available: //
17// 1) Uniform denisity - rectilinear //
18// 2) Uniform denisity - polar //
19// 3) Fisheye - rectilinear //
20// 4) Fisheye - Ploar //
21// //
23
24#include <assert.h>
25#include <cgraph/alloc.h>
26#include <cgraph/sort.h>
27#include <limits.h>
28#include <stdio.h>
29#include <stdlib.h>
30#include <string.h>
31#include <math.h>
32#include <time.h>
33#include <neatogen/matrix_ops.h>
34#include <neatogen/delaunay.h>
35#include <common/arith.h>
36#include <topfish/hierarchy.h>
37
38static double *compute_densities(v_data *graph, size_t n, double *x, double *y)
39{
40// compute density of every node by calculating the average edge length in a 2-D layout
41 int j, neighbor;
42 double sum;
43 double *densities = gv_calloc(n, sizeof(double));
44
45 for (size_t i = 0; i < n; i++) {
46 sum = 0;
47 for (j = 1; j < graph[i].nedges; j++) {
48 neighbor = graph[i].edges[j];
49 sum += hypot(x[i] - x[neighbor], y[i] - y[neighbor]);
50 }
51 densities[i] = sum / graph[i].nedges;
52 }
53 return densities;
54}
55
56static double *smooth_vec(double *vec, int *ordering, size_t n, int interval) {
57// smooth 'vec' by setting each components to the average of is 'interval'-neighborhood in 'ordering'
58 assert(interval >= 0);
59 double sum;
60 double *smoothed_vec = gv_calloc(n, sizeof(double));
61 size_t n1 = MIN(1 + (size_t)interval, n);
62 sum = 0;
63 for (size_t i = 0; i < n1; i++) {
64 sum += vec[ordering[i]];
65 }
66
67 size_t len = n1;
68 for (size_t i = 0; i < MIN(n, (size_t)interval); i++) {
69 smoothed_vec[ordering[i]] = sum / (double)len;
70 if (len < n) {
71 sum += vec[ordering[len++]];
72 }
73 }
74 if (n <= (size_t)interval) {
75 return smoothed_vec;
76 }
77
78 for (size_t i = (size_t)interval; i < n - (size_t)interval - 1; i++) {
79 smoothed_vec[ordering[i]] = sum / (double)len;
80 sum +=
81 vec[ordering[i + (size_t)interval + 1]] - vec[ordering[i - (size_t)interval]];
82 }
83 for (size_t i = MAX(n - (size_t)interval - 1, (size_t)interval); i < n; i++) {
84 smoothed_vec[ordering[i]] = sum / (double)len;
85 sum -= vec[ordering[i - (size_t)interval]];
86 len--;
87 }
88 return smoothed_vec;
89}
90
91static int cmp(const void *a, const void *b, void *context) {
92 const int *x = a;
93 const int *y = b;
94 const double *place = context;
95
96 if (place[*x] < place[*y]) {
97 return -1;
98 }
99 if (place[*x] > place[*y]) {
100 return 1;
101 }
102 return 0;
103}
104
105void quicksort_place(double *place, int *ordering, size_t size) {
106 gv_sort(ordering, size, sizeof(ordering[0]), cmp, place);
107}
108
109#define DIST(x1, y1, x2, y2) hypot((x1) - (x2), (y1) - (y2))
110
111static void rescale_layout_polarFocus(v_data *graph, size_t n, double *x_coords,
112 double *y_coords, double x_focus,
113 double y_focus, int interval,
114 double distortion) {
115 // Polar distortion - auxiliary function
116 double *densities = NULL;
117 double *distances = gv_calloc(n, sizeof(double));
118 double *orig_distances = gv_calloc(n, sizeof(double));
119 double ratio;
120
121 for (size_t i = 0; i < n; i++)
122 {
123 distances[i] = DIST(x_coords[i], y_coords[i], x_focus, y_focus);
124 }
125 assert(n <= INT_MAX);
126 copy_vector((int)n, distances, orig_distances);
127
128 int *ordering = gv_calloc(n, sizeof(int));
129 for (size_t i = 0; i < n; i++)
130 {
131 ordering[i] = (int)i;
132 }
133 quicksort_place(distances, ordering, n);
134
135 densities = compute_densities(graph, n, x_coords, y_coords);
136 double *smoothed_densities = smooth_vec(densities, ordering, n, interval);
137
138 // rescale distances
139 if (distortion < 1.01 && distortion > 0.99)
140 {
141 for (size_t i = 1; i < n; i++)
142 {
143 distances[ordering[i]] = distances[ordering[i - 1]] + (orig_distances[ordering[i]] -
144 orig_distances[ordering
145 [i -
146 1]]) / smoothed_densities[ordering[i]];
147 }
148 } else
149 {
150 double factor;
151 // just to make milder behavior:
152 if (distortion >= 0)
153 {
154 factor = sqrt(distortion);
155 }
156 else
157 {
158 factor = -sqrt(-distortion);
159 }
160 for (size_t i = 1; i < n; i++)
161 {
162 distances[ordering[i]] =
163 distances[ordering[i - 1]] + (orig_distances[ordering[i]] -
164 orig_distances[ordering
165 [i -
166 1]]) /
167 pow(smoothed_densities[ordering[i]], factor);
168 }
169 }
170
171 // compute new coordinate:
172 for (size_t i = 0; i < n; i++)
173 {
174 if (orig_distances[i] == 0)
175 {
176 ratio = 0;
177 }
178 else
179 {
180 ratio = distances[i] / orig_distances[i];
181 }
182 x_coords[i] = x_focus + (x_coords[i] - x_focus) * ratio;
183 y_coords[i] = y_focus + (y_coords[i] - y_focus) * ratio;
184 }
185
186 free(densities);
187 free(smoothed_densities);
188 free(distances);
189 free(orig_distances);
190 free(ordering);
191}
192
193void
194rescale_layout_polar(double *x_coords, double *y_coords,
195 double *x_foci, double *y_foci, int num_foci,
196 size_t n, int interval, double width,
197 double height, double distortion) {
198 // Polar distortion - main function
199 double minX, maxX, minY, maxY;
200 double aspect_ratio;
201 v_data *graph;
202 double scaleX;
203 double scale_ratio;
204
205 // compute original aspect ratio
206 minX = maxX = x_coords[0];
207 minY = maxY = y_coords[0];
208 for (size_t i = 1; i < n; i++)
209 {
210 minX = fmin(minX, x_coords[i]);
211 minY = fmin(minY, y_coords[i]);
212 maxX = fmax(maxX, x_coords[i]);
213 maxY = fmax(maxY, y_coords[i]);
214 }
215 aspect_ratio = (maxX - minX) / (maxY - minY);
216
217 // construct mutual neighborhood graph
218 assert(n <= INT_MAX);
219 graph = UG_graph(x_coords, y_coords, (int)n);
220
221 if (num_foci == 1)
222 { // accelerate execution of most common case
223 rescale_layout_polarFocus(graph, n, x_coords, y_coords, x_foci[0],
224 y_foci[0], interval, distortion);
225 } else
226 {
227 // average-based rescale
228 double *final_x_coords = gv_calloc(n, sizeof(double));
229 double *final_y_coords = gv_calloc(n, sizeof(double));
230 double *cp_x_coords = gv_calloc(n, sizeof(double));
231 double *cp_y_coords = gv_calloc(n, sizeof(double));
232 assert(n <= INT_MAX);
233 for (int i = 0; i < num_foci; i++) {
234 copy_vector((int)n, x_coords, cp_x_coords);
235 copy_vector((int)n, y_coords, cp_y_coords);
236 rescale_layout_polarFocus(graph, n, cp_x_coords, cp_y_coords,
237 x_foci[i], y_foci[i], interval, distortion);
238 scadd(final_x_coords, (int)n - 1, 1.0 / num_foci, cp_x_coords);
239 scadd(final_y_coords, (int)n - 1, 1.0 / num_foci, cp_y_coords);
240 }
241 copy_vector((int)n, final_x_coords, x_coords);
242 copy_vector((int)n, final_y_coords, y_coords);
243 free(final_x_coords);
244 free(final_y_coords);
245 free(cp_x_coords);
246 free(cp_y_coords);
247 }
248 free(graph[0].edges);
249 free(graph);
250
251 minX = maxX = x_coords[0];
252 minY = maxY = y_coords[0];
253 for (size_t i = 1; i < n; i++) {
254 minX = fmin(minX, x_coords[i]);
255 minY = fmin(minY, y_coords[i]);
256 maxX = fmax(maxX, x_coords[i]);
257 maxY = fmax(maxY, y_coords[i]);
258 }
259
260 // shift points:
261 for (size_t i = 0; i < n; i++) {
262 x_coords[i] -= minX;
263 y_coords[i] -= minY;
264 }
265
266 // rescale x_coords to maintain aspect ratio:
267 scaleX = aspect_ratio * (maxY - minY) / (maxX - minX);
268 for (size_t i = 0; i < n; i++) {
269 x_coords[i] *= scaleX;
270 }
271
272
273 // scale the layout to fit full drawing area:
274 scale_ratio =
275 MIN((width) / (aspect_ratio * (maxY - minY)),
276 (height) / (maxY - minY));
277 for (size_t i = 0; i < n; i++) {
278 x_coords[i] *= scale_ratio;
279 y_coords[i] *= scale_ratio;
280 }
281}
Memory allocation wrappers that exit on failure.
static void * gv_calloc(size_t nmemb, size_t size)
Definition alloc.h:26
#define MIN(a, b)
Definition arith.h:28
v_data * UG_graph(double *x, double *y, int n)
Definition delaunay.c:785
static double len(glCompPoint p)
Definition glutils.c:150
void free(void *)
node NULL
Definition grammar.y:149
Agraph_t * graph(char *name)
Definition gv.cpp:31
#define neighbor(t, i, edim, elist)
Definition make_map.h:37
void scadd(double *vec1, int end, double fac, double *vec2)
Definition matrix_ops.c:220
void copy_vector(int n, const double *source, double *dest)
Definition matrix_ops.c:322
static double * compute_densities(v_data *graph, size_t n, double *x, double *y)
static void rescale_layout_polarFocus(v_data *graph, size_t n, double *x_coords, double *y_coords, double x_focus, double y_focus, int interval, double distortion)
void rescale_layout_polar(double *x_coords, double *y_coords, double *x_foci, double *y_foci, int num_foci, size_t n, int interval, double width, double height, double distortion)
void quicksort_place(double *place, int *ordering, size_t size)
static double * smooth_vec(double *vec, int *ordering, size_t n, int interval)
#define DIST(x1, y1, x2, y2)
static int cmp(const void *a, const void *b, void *context)
qsort with carried along context
static void gv_sort(void *base, size_t nmemb, size_t size, int(*compar)(const void *, const void *, void *), void *arg)
qsort with an extra state parameter, ala qsort_r
Definition sort.h:35
#define MAX(a, b)
Definition write.c:31