Graphviz 14.1.3~dev.20260126.0926
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 "config.h"
25
26#include <assert.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#include <util/alloc.h>
38#include <util/sort.h>
39
40static double *compute_densities(v_data *graph, size_t n, double *x, double *y)
41{
42// compute density of every node by calculating the average edge length in a 2-D layout
43 int j, neighbor;
44 double sum;
45 double *densities = gv_calloc(n, sizeof(double));
46
47 for (size_t i = 0; i < n; i++) {
48 sum = 0;
49 for (j = 1; j < graph[i].nedges; j++) {
50 neighbor = graph[i].edges[j];
51 sum += hypot(x[i] - x[neighbor], y[i] - y[neighbor]);
52 }
53 densities[i] = sum / graph[i].nedges;
54 }
55 return densities;
56}
57
58static double *smooth_vec(double *vec, int *ordering, size_t n, int interval) {
59// smooth 'vec' by setting each components to the average of is 'interval'-neighborhood in 'ordering'
60 assert(interval >= 0);
61 double sum;
62 double *smoothed_vec = gv_calloc(n, sizeof(double));
63 size_t n1 = MIN(1 + (size_t)interval, n);
64 sum = 0;
65 for (size_t i = 0; i < n1; i++) {
66 sum += vec[ordering[i]];
67 }
68
69 size_t len = n1;
70 for (size_t i = 0; i < MIN(n, (size_t)interval); i++) {
71 smoothed_vec[ordering[i]] = sum / (double)len;
72 if (len < n) {
73 sum += vec[ordering[len++]];
74 }
75 }
76 if (n <= (size_t)interval) {
77 return smoothed_vec;
78 }
79
80 for (size_t i = (size_t)interval; i < n - (size_t)interval - 1; i++) {
81 smoothed_vec[ordering[i]] = sum / (double)len;
82 sum +=
83 vec[ordering[i + (size_t)interval + 1]] - vec[ordering[i - (size_t)interval]];
84 }
85 for (size_t i = MAX(n - (size_t)interval - 1, (size_t)interval); i < n; i++) {
86 smoothed_vec[ordering[i]] = sum / (double)len;
87 sum -= vec[ordering[i - (size_t)interval]];
88 len--;
89 }
90 return smoothed_vec;
91}
92
93static int cmp(const void *a, const void *b, void *context) {
94 const int *x = a;
95 const int *y = b;
96 const double *place = context;
97
98 if (place[*x] < place[*y]) {
99 return -1;
100 }
101 if (place[*x] > place[*y]) {
102 return 1;
103 }
104 return 0;
105}
106
107void quicksort_place(double *place, int *ordering, size_t size) {
108 gv_sort(ordering, size, sizeof(ordering[0]), cmp, place);
109}
110
111#define DIST(x1, y1, x2, y2) hypot((x1) - (x2), (y1) - (y2))
112
113static void rescale_layout_polarFocus(v_data *graph, size_t n, double *x_coords,
114 double *y_coords, double x_focus,
115 double y_focus, int interval,
116 double distortion) {
117 // Polar distortion - auxiliary function
118 double *densities = NULL;
119 double *distances = gv_calloc(n, sizeof(double));
120 double *orig_distances = gv_calloc(n, sizeof(double));
121
122 for (size_t i = 0; i < n; i++)
123 {
124 distances[i] = DIST(x_coords[i], y_coords[i], x_focus, y_focus);
125 }
126 assert(n <= INT_MAX);
127 copy_vector((int)n, distances, orig_distances);
128
129 int *ordering = gv_calloc(n, sizeof(int));
130 for (size_t i = 0; i < n; i++)
131 {
132 ordering[i] = (int)i;
133 }
134 quicksort_place(distances, ordering, n);
135
136 densities = compute_densities(graph, n, x_coords, y_coords);
137 double *smoothed_densities = smooth_vec(densities, ordering, n, interval);
138
139 // rescale distances
140 if (distortion < 1.01 && distortion > 0.99)
141 {
142 for (size_t i = 1; i < n; i++)
143 {
144 distances[ordering[i]] = distances[ordering[i - 1]] + (orig_distances[ordering[i]] -
145 orig_distances[ordering
146 [i -
147 1]]) / smoothed_densities[ordering[i]];
148 }
149 } else
150 {
151 // just to make milder behavior:
152 const double factor = (signbit(distortion) ? -1 : 1) * sqrt(fabs(distortion));
153 for (size_t i = 1; i < n; i++)
154 {
155 distances[ordering[i]] =
156 distances[ordering[i - 1]] + (orig_distances[ordering[i]] -
157 orig_distances[ordering
158 [i -
159 1]]) /
160 pow(smoothed_densities[ordering[i]], factor);
161 }
162 }
163
164 // compute new coordinate:
165 for (size_t i = 0; i < n; i++)
166 {
167 const double ratio =
168 orig_distances[i] > 0 ? distances[i] / orig_distances[i] : 0;
169 x_coords[i] = x_focus + (x_coords[i] - x_focus) * ratio;
170 y_coords[i] = y_focus + (y_coords[i] - y_focus) * ratio;
171 }
172
173 free(densities);
174 free(smoothed_densities);
175 free(distances);
176 free(orig_distances);
177 free(ordering);
178}
179
180void
181rescale_layout_polar(double *x_coords, double *y_coords,
182 double *x_foci, double *y_foci, int num_foci,
183 size_t n, int interval, double width,
184 double height, double distortion) {
185 // Polar distortion - main function
186 double minX, maxX, minY, maxY;
187 double aspect_ratio;
188 v_data *graph;
189 double scaleX;
190 double scale_ratio;
191
192 // compute original aspect ratio
193 minX = maxX = x_coords[0];
194 minY = maxY = y_coords[0];
195 for (size_t i = 1; i < n; i++)
196 {
197 minX = fmin(minX, x_coords[i]);
198 minY = fmin(minY, y_coords[i]);
199 maxX = fmax(maxX, x_coords[i]);
200 maxY = fmax(maxY, y_coords[i]);
201 }
202 aspect_ratio = (maxX - minX) / (maxY - minY);
203
204 // construct mutual neighborhood graph
205 assert(n <= INT_MAX);
206 graph = UG_graph(x_coords, y_coords, (int)n);
207
208 if (num_foci == 1)
209 { // accelerate execution of most common case
210 rescale_layout_polarFocus(graph, n, x_coords, y_coords, x_foci[0],
211 y_foci[0], interval, distortion);
212 } else
213 {
214 // average-based rescale
215 double *final_x_coords = gv_calloc(n, sizeof(double));
216 double *final_y_coords = gv_calloc(n, sizeof(double));
217 double *cp_x_coords = gv_calloc(n, sizeof(double));
218 double *cp_y_coords = gv_calloc(n, sizeof(double));
219 assert(n <= INT_MAX);
220 for (int i = 0; i < num_foci; i++) {
221 copy_vector((int)n, x_coords, cp_x_coords);
222 copy_vector((int)n, y_coords, cp_y_coords);
223 rescale_layout_polarFocus(graph, n, cp_x_coords, cp_y_coords,
224 x_foci[i], y_foci[i], interval, distortion);
225 scadd(final_x_coords, (int)n - 1, 1.0 / num_foci, cp_x_coords);
226 scadd(final_y_coords, (int)n - 1, 1.0 / num_foci, cp_y_coords);
227 }
228 copy_vector((int)n, final_x_coords, x_coords);
229 copy_vector((int)n, final_y_coords, y_coords);
230 free(final_x_coords);
231 free(final_y_coords);
232 free(cp_x_coords);
233 free(cp_y_coords);
234 }
235 free(graph[0].edges);
236 free(graph);
237
238 minX = maxX = x_coords[0];
239 minY = maxY = y_coords[0];
240 for (size_t i = 1; i < n; i++) {
241 minX = fmin(minX, x_coords[i]);
242 minY = fmin(minY, y_coords[i]);
243 maxX = fmax(maxX, x_coords[i]);
244 maxY = fmax(maxY, y_coords[i]);
245 }
246
247 // shift points:
248 for (size_t i = 0; i < n; i++) {
249 x_coords[i] -= minX;
250 y_coords[i] -= minY;
251 }
252
253 // rescale x_coords to maintain aspect ratio:
254 scaleX = aspect_ratio * (maxY - minY) / (maxX - minX);
255 for (size_t i = 0; i < n; i++) {
256 x_coords[i] *= scaleX;
257 }
258
259
260 // scale the layout to fit full drawing area:
261 scale_ratio =
262 MIN((width) / (aspect_ratio * (maxY - minY)),
263 (height) / (maxY - minY));
264 for (size_t i = 0; i < n; i++) {
265 x_coords[i] *= scale_ratio;
266 y_coords[i] *= scale_ratio;
267 }
268}
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
#define MAX(a, b)
Definition arith.h:33
v_data * UG_graph(double *x, double *y, int n)
Definition delaunay.c:592
static double len(glCompPoint p)
Definition glutils.c:138
void free(void *)
node NULL
Definition grammar.y:181
Agraph_t * graph(char *name)
Definition gv.cpp:34
#define neighbor(t, i, edim, elist)
Definition make_map.h:41
void copy_vector(int n, const double *restrict source, double *restrict dest)
Definition matrix_ops.c:318
void scadd(double *vec1, int end, double fac, double *vec2)
Definition matrix_ops.c:216
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:24