Graphviz 14.1.2~dev.20260118.1035
Loading...
Searching...
No Matches
ink.cpp
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
11#include "config.h"
12
13#include <algorithm>
14#include <cmath>
15#include <cstdlib>
16#include <common/types.h>
17#include <common/globals.h>
18#include <sparse/general.h>
19#include <mingle/ink.h>
20#include <vector>
21
22double ink_count;
23
25{
26 a.x += b.x;
27 a.y += b.y;
28 return a;
29}
30
32{
33 a.x -= b.x;
34 a.y -= b.y;
35 return a;
36}
37
38static point_t scalePoint (point_t a, double d)
39{
40 a.x *= d;
41 a.y *= d;
42 return a;
43}
44
45static double dotPoint(point_t a, point_t b){
46 return a.x*b.x + a.y*b.y;
47}
48
49static const point_t Origin = {0, 0};
50
51/* sumLengths:
52 */
53static double sumLengths_avoid_bad_angle(const std::vector<point_t> &points,
54 point_t end, point_t meeting,
55 double angle_param) {
56 /* avoid sharp turns, we want cos_theta to be as close to -1 as possible */
57 double len0, len, sum = 0;
58 double diff_x, diff_y, diff_x0, diff_y0;
59 double cos_theta, cos_max = -10;
60
61 diff_x0 = end.x-meeting.x;
62 diff_y0 = end.y-meeting.y;
63 len0 = sum = hypot(diff_x0, diff_y0);
64
65 // distance form each of 'points' till 'meeting'
66 for (const point_t &p : points) {
67 diff_x = p.x - meeting.x;
68 diff_y = p.y - meeting.y;
69 len = hypot(diff_x, diff_y);
70 sum += len;
71 cos_theta = (diff_x0 * diff_x + diff_y0 * diff_y)
72 / std::max(len * len0, 0.00001);
73 cos_max = std::max(cos_max, cos_theta);
74 }
75
76 // distance of single line from 'meeting' to 'end'
77 return sum*(cos_max + angle_param);/* straight line gives angle_param - 1, turning angle of 180 degree gives angle_param + 1 */
78}
79static double sumLengths(const std::vector<point_t> &points, point_t end,
80 point_t meeting) {
81 double sum = 0;
82 double diff_x, diff_y;
83
84 // distance form each of 'points' till 'meeting'
85 for (const point_t &p : points) {
86 diff_x = p.x - meeting.x;
87 diff_y = p.y - meeting.y;
88 sum += hypot(diff_x, diff_y);
89 }
90 // distance of single line from 'meeting' to 'end'
91 diff_x = end.x-meeting.x;
92 diff_y = end.y-meeting.y;
93 sum += hypot(diff_x, diff_y);
94 return sum;
95}
96
97/* bestInk:
98 */
99static double bestInk(const std::vector<point_t> &points, point_t begin,
100 point_t end, double prec, point_t *meet,
101 double angle_param) {
102 point_t first, second, third, fourth, diff, meeting;
103 double value1, value2, value3, value4;
104
105 first = begin;
106 fourth = end;
107
108 do {
109 diff = subPoint(fourth,first);
110 second = addPoint(first,scalePoint(diff,1.0/3.0));
111 third = addPoint(first,scalePoint(diff,2.0/3.0));
112
113 if (angle_param < 1){
114 value1 = sumLengths(points, end, first);
115 value2 = sumLengths(points, end, second);
116 value3 = sumLengths(points, end, third);
117 value4 = sumLengths(points, end, fourth);
118 } else {
119 value1 = sumLengths_avoid_bad_angle(points, end, first, angle_param);
120 value2 = sumLengths_avoid_bad_angle(points, end, second, angle_param);
121 value3 = sumLengths_avoid_bad_angle(points, end, third, angle_param);
122 value4 = sumLengths_avoid_bad_angle(points, end, fourth, angle_param);
123 }
124
125 if (value1<value2) {
126 if (value1<value3) {
127 if (value1<value4) {
128 // first is smallest
129 fourth = second;
130 }
131 else {
132 // fourth is smallest
133 first = third;
134 }
135 }
136 else {
137 if (value3<value4) {
138 // third is smallest
139 first = second;
140 }
141 else {
142 // fourth is smallest
143 first = third;
144 }
145 }
146 }
147 else {
148 if (value2<value3) {
149 if (value2<value4) {
150 // second is smallest
151 fourth = third;
152 }
153 else {
154 // fourth is smallest
155 first = third;
156 }
157 }
158 else {
159 if (value3<value4) {
160 // third is smallest
161 first = second;
162 }
163 else {
164 // fourth is smallest
165 first = third;
166 }
167 }
168 }
169 } while (fabs(value1 - value4) / (std::min(value1, value4) + 1e-10) > prec
170 && dotPoint(diff, diff) > 1.e-20);
171
172 meeting = scalePoint(addPoint(first,fourth),0.5);
173 *meet = meeting;
174
175 return sumLengths(points, end, meeting);
176
177}
178
179static double project_to_line(point_t pt, point_t left, point_t right, double angle){
180 /* pt
181 ^ ^
182 . \ \
183 . \ \
184 d . a\ \
185 . \ \
186 . \ \
187 . c \ alpha \ b
188 .<------left:0 ----------------------------> right:1. Find the projection of pt on the left--right line. If the turning angle is small,
189 | |
190 |<-------f---------
191 we should get a negative number. Let a := left->pt, b := left->right, then we are calculating:
192 c = |a| cos(a,b)/|b| b
193 d = a - c
194 f = -ctan(alpha)*|d|/|b| b
195 and the project of alpha degree on the left->right line is
196 c-f = |a| cos(a,b)/|b| b - -ctan(alpha)*|d|/|b| b
197 = (|a| a.b/(|a||b|) + ctan(alpha)|a-c|)/|b| b
198 = (a.b/|b| + ctan(alpha)|a-c|)/|b| b
199 the dimentionless projection is:
200 a.b/|b|^2 + ctan(alpha)|a-c|/|b|
201 = a.b/|b|^2 + ctan(alpha)|d|/|b|
202 */
203
204
205 point_t b, a;
206 double bnorm, dnorm;
207 double alpha, ccord;
208
209 if (angle <=0 || angle >= M_PI) return 2;/* return outside of the interval which should be handled as a sign of infeasible turning angle */
210 alpha = angle;
211
212 assert(alpha > 0 && alpha < M_PI);
213 b = subPoint(right, left);
214 a = subPoint(pt, left);
215 bnorm = std::max(1.e-10, dotPoint(b, b));
216 ccord = dotPoint(b, a)/bnorm;
217 dnorm = dotPoint(a,a)/bnorm - ccord*ccord;
218 if (alpha == M_PI/2){
219 return ccord;
220 }
221 return ccord + sqrt(std::max(0.0, dnorm)) / tan(alpha);
222}
223
224/* ink:
225 * Compute minimal ink used the input edges are bundled.
226 * Assumes tails all occur on one side and heads on the other.
227 */
228double ink(const std::vector<pedge> &edges, int numEdges, int *pick,
229 double *ink0, point_t *meet1, point_t *meet2, double angle_param,
230 double angle) {
231 int i;
232 point_t begin, end, mid, diff;
233 double inkUsed;
234 double eps = 1.0e-2;
235 double cend = 0, cbegin = 0;
236 double wgt = 0;
237
238 ink_count += numEdges;
239
240 *ink0 = 0;
241
242 /* canonicalize so that edges 1,2,3 and 3,2,1 gives the same optimal ink */
243 if (pick) vector_sort_int(numEdges, pick);
244
245 begin = end = Origin;
246 for (i = 0; i < numEdges; i++) {
247 const pedge &e = pick ? edges[pick[i]] : edges[i];
248 const std::vector<double> &x = e.x;
249 point_t source = {x[0], x[1]};
250 point_t target = {x[e.dim * e.npoints - e.dim],
251 x[e.dim * e.npoints - e.dim + 1]};
252 (*ink0) += hypot(source.x - target.x, source.y - target.y);
253 begin = addPoint(begin, scalePoint(source, e.wgt));
254 end = addPoint(end, scalePoint(target, e.wgt));
255 wgt += e.wgt;
256 }
257
258 begin = scalePoint (begin, 1.0/wgt);
259 end = scalePoint (end, 1.0/wgt);
260
261
262 if (numEdges == 1){
263 *meet1 = begin;
264 *meet2 = end;
265 return *ink0;
266 }
267
268 /* shift the begin and end point to avoid sharp turns */
269 std::vector<point_t> sources;
270 std::vector<point_t> targets;
271 for (i = 0; i < numEdges; i++) {
272 const pedge &e = pick ? edges[pick[i]] : edges[i];
273 const std::vector<double> &x = e.x;
274 sources.push_back(point_t{x[0], x[1]});
275 targets.push_back(point_t{x[e.dim * e.npoints - e.dim],
276 x[e.dim * e.npoints - e.dim + 1]});
277 /* begin(1) ----------- mid(0) */
278 if (i == 0){
279 cbegin = project_to_line(sources[i], begin, end, angle);
280 cend = project_to_line(targets[i], end, begin, angle);
281 } else {
282 cbegin = std::max(cbegin, project_to_line(sources[i], begin, end, angle));
283 cend = std::max(cend, project_to_line(targets[i], end, begin, angle));
284 }
285 }
286
287 if (angle > 0 && angle < M_PI){
288 if (cbegin + cend > 1 || cbegin > 1 || cend > 1){
289 /* no point can be found that satisfies the angular constraints, so we give up and set ink to a large value */
290 inkUsed = 1000*(*ink0);
291 return inkUsed;
292 }
293 /* make sure the turning angle is no more than alpha degree */
294 cbegin = std::max(0.0, cbegin);/* make sure the new adjusted point is with in [begin,end] internal */
295 diff = subPoint(end, begin);
296 begin = addPoint(begin, scalePoint(diff, cbegin));
297
298 cend = std::max(0.0, cend);/* make sure the new adjusted point is with in [end,begin] internal */
299 end = subPoint(end, scalePoint(diff, cend));
300 }
301 mid = scalePoint (addPoint(begin,end),0.5);
302
303 inkUsed = bestInk(sources, begin, mid, eps, meet1, angle_param)
304 + bestInk(targets, end, mid, eps, meet2, angle_param);
305
306 return inkUsed;
307}
308
309double ink1(const pedge &e) {
310 double ink0 = 0;
311
312 const std::vector<double> &x = e.x;
313 const double xx = x[0] - x[e.dim * e.npoints - e.dim];
314 const double yy = x[1] - x[e.dim * e.npoints - e.dim + 1];
315 ink0 += hypot(xx, yy);
316 return ink0;
317}
#define M_PI
Definition arith.h:41
#define right(i)
Definition closest.c:74
#define left
Definition dthdr.h:12
void vector_sort_int(int n, int *v)
Definition general.c:114
static double len(glCompPoint p)
Definition glutils.c:138
static gdPoint * points
static point_t addPoint(point_t a, point_t b)
Definition ink.cpp:24
static double project_to_line(point_t pt, point_t left, point_t right, double angle)
Definition ink.cpp:179
static double sumLengths_avoid_bad_angle(const std::vector< point_t > &points, point_t end, point_t meeting, double angle_param)
Definition ink.cpp:53
double ink(const std::vector< pedge > &edges, int numEdges, int *pick, double *ink0, point_t *meet1, point_t *meet2, double angle_param, double angle)
Definition ink.cpp:228
static double dotPoint(point_t a, point_t b)
Definition ink.cpp:45
static point_t scalePoint(point_t a, double d)
Definition ink.cpp:38
static point_t subPoint(point_t a, point_t b)
Definition ink.cpp:31
static double sumLengths(const std::vector< point_t > &points, point_t end, point_t meeting)
Definition ink.cpp:79
double ink_count
Definition ink.cpp:22
static const point_t Origin
Definition ink.cpp:49
static double bestInk(const std::vector< point_t > &points, point_t begin, point_t end, double prec, point_t *meet, double angle_param)
Definition ink.cpp:99
double ink1(const pedge &e)
Definition ink.cpp:309
#define alpha
Definition shapes.c:4058
std::vector< double > x
coordinates of the npoints poly points. Dimension dim*npoints
double wgt
int npoints
Definition ink.h:16
double y
Definition ink.h:17
double x
Definition ink.h:17
graphs, nodes and edges info: Agraphinfo_t, Agnodeinfo_t and Agedgeinfo_t