Graphviz 12.0.1~dev.20240716.0800
Loading...
Searching...
No Matches
taper.c
Go to the documentation of this file.
1
3/*************************************************************************
4 * Copyright (c) 2011 AT&T Intellectual Property
5 * All rights reserved. This program and the accompanying materials
6 * are made available under the terms of the Eclipse Public License v1.0
7 * which accompanies this distribution, and is available at
8 * https://www.eclipse.org/legal/epl-v10.html
9 *
10 * Contributors: Details at https://graphviz.org
11 *************************************************************************/
12
13/*
14 * Tapered edges, based on lines.ps written by Denis Moskowitz.
15 */
16
17#include "config.h"
18#include <assert.h>
19#include <math.h>
20#include <stdbool.h>
21#include <stdint.h>
22#include <stdio.h>
23#include <stdlib.h>
24#include <types.h>
25#include <cgraph/alloc.h>
26#include <cgraph/agxbuf.h>
27#include <cgraph/list.h>
28#include <cgraph/prisize_t.h>
29#include <common/render.h>
30#include <common/utils.h>
31
32 /* sample point size; should be dynamic based on dpi or under user control */
33#define BEZIERSUBDIVISION 20
34
35 /* convert degrees to radians and vice versa */
36#ifndef M_PI
37#define M_PI 3.14159265358979323846
38#endif
39#define D2R(d) (M_PI*(d)/180.0)
40#define R2D(r) (180.0*(r)/M_PI)
41
42static double currentmiterlimit = 10.0;
43
44#define moveto(p,x,y) addto(p,x,y)
45#define lineto(p,x,y) addto(p,x,y)
46
47static void addto (stroke_t* p, double x, double y)
48{
49 pointf pt;
50
52 sizeof(pointf));
53 pt.x = x;
54 pt.y = y;
55 p->vertices[p->nvertices++] = pt;
56}
57
58/*
59 * handle zeros
60 */
61static double myatan (double y, double x)
62{
63 double v;
64 if (x == 0 && y == 0)
65 return 0;
66 v = atan2 (y, x);
67 if (v >= 0) return v;
68 return v + 2 * M_PI;
69}
70
71/*
72 * mod that accepts floats and makes negatives positive
73 */
74static double mymod (double original, double modulus)
75{
76 double v;
77 if (original < 0 || original >= modulus) {
78 v = -floor(original/modulus);
79 return v * modulus + original;
80 }
81 return original;
82}
83
84typedef struct {
85 double x;
86 double y;
88 char type;
89 double dir;
90 double lout;
91 bool bevel;
92 double dir2;
93} pathpoint;
94
96
97static void
98insertArr (vararr_t* arr, pointf p, double l)
99{
100 pathpoint pt = {.x = p.x, .y = p.y, .lengthsofar = l};
101 vararr_append(arr, pt);
102}
103
104#ifdef DEBUG
105static void
106printArr (vararr_t* arr, FILE* fp)
107{
108 fprintf(fp, "size %" PRISIZE_T "\n", vararr_size(arr));
109 for (size_t i = 0; i < vararr_size(arr); i++) {
110 pathpoint pt = vararr_get(arr, i);
111 fprintf (fp, " [%d] x %.02f y %.02f d %.02f\n", i, pt.x, pt.y, pt.lengthsofar);
112 }
113}
114#endif
115
116static double l2dist (pointf p0, pointf p1)
117{
118 double delx = p0.x - p1.x;
119 double dely = p0.y - p1.y;
120 return hypot(delx, dely);
121}
122
123/* analyze current path, creating pathpoints array
124 * turn all curves into lines
125 */
126static vararr_t pathtolines(bezier *bez) {
127 int step;
128 double seglen, linelen = 0;
129 vararr_t arr = {0};
130 pointf p0, p1, V[4];
131 const size_t n = bez->size;
132 pointf* A = bez->list;
133
134 insertArr(&arr, A[0], 0);
135 V[3] = A[0];
136 for (size_t i = 0; i + 3 < n; i += 3) {
137 V[0] = V[3];
138 for (size_t j = 1; j <= 3; j++)
139 V[j] = A[i + j];
140 p0 = V[0];
141 for (step = 1; step <= BEZIERSUBDIVISION; step++) {
142 p1 = Bezier(V, (double) step / BEZIERSUBDIVISION, NULL, NULL);
143 seglen = l2dist(p0, p1);
144 /* If initwid is large, this may never happen, so turn off. I assume this is to prevent
145 * too man points or too small a movement. Perhaps a better test can be made, but for now
146 * we turn it off.
147 */
148 /* if (seglen > initwid/10) { */
149 linelen += seglen;
150 insertArr(&arr, p1, linelen);
151 /* } */
152 p0 = p1;
153 }
154 }
155#ifdef DEBUG
156 printArr(&arr, stderr);
157#endif
158 return arr;
159}
160
161static void drawbevel(double x, double lineout, bool forward, double dir,
162 double dir2, stroke_t *p) {
163 double a2;
164
165 if (forward) {
166 a2 = dir2;
167 } else {
168 a2 = dir;
169 }
170 lineto (p, x + lineout*cos(a2), x + lineout*sin(a2));
171}
172
173typedef double (*radfunc_t) (double curlen, double totallen, double initwid);
174
175/* taper:
176 * Given a B-spline bez, returns a polygon that represents spline as a tapered
177 * edge, starting with width initwid.
178 * The radfunc determines the half-width along the curve. Typically, this will
179 * decrease from initwid to 0 as the curlen goes from 0 to totallen.
180 */
181stroke_t taper(bezier *bez, radfunc_t radfunc, double initwid) {
182 double direction=0, direction_2=0;
183 vararr_t arr = pathtolines(bez);
184 pathpoint cur_point, last_point, next_point;
185 double x=0, y=0, dist;
186 double nx, ny, ndir;
187 double lx, ly, ldir;
188 double lineout=0, linerad=0, linelen=0;
189 double theta, phi;
190
191 size_t pathcount = vararr_size(&arr);
192 pathpoint *pathpoints = vararr_detach(&arr);
193 linelen = pathpoints[pathcount-1].lengthsofar;
194
195 /* determine miter and bevel points and directions */
196 for (size_t i = 0; i < pathcount; i++) {
197 const size_t l = i == 0 ? pathcount - 1 : i - 1;
198 const size_t n = (i + 1) % pathcount;
199
200 cur_point = pathpoints[i];
201 x = cur_point.x;
202 y = cur_point.y;
203 dist = cur_point.lengthsofar;
204
205 next_point = pathpoints[n];
206 nx = next_point.x;
207 ny = next_point.y;
208 ndir = myatan (ny-y, nx-x);
209
210 last_point = pathpoints[l];
211 lx = last_point.x;
212 ly = last_point.y;
213 ldir = myatan (ly-y, lx-x);
214
215 bool bevel = false;
216 direction_2 = 0;
217
218 /* effective line radius at this point */
219 linerad = radfunc(dist, linelen, initwid);
220
221 if (i == 0 || i == pathcount-1) {
222 lineout = linerad;
223 if (i == 0) {
224 direction = ndir + D2R(90);
225 } else {
226 direction = ldir - D2R(90);
227 }
228 direction_2 = direction;
229 } else {
230 theta = ndir-ldir;
231 if (theta < 0) {
232 theta += D2R(360);
233 }
234 phi = D2R(90) - theta / 2;
235 /* actual distance to junction point */
236 if (cos(phi) == 0) {
237 lineout = 0;
238 } else {
239 lineout = linerad / cos(phi);
240 }
241 /* direction to junction point */
242 direction = ndir+D2R(90)+phi;
243 if (lineout > currentmiterlimit * linerad) {
244 bevel = true;
245 lineout = linerad;
246 direction = mymod(ldir-D2R(90),D2R(360));
247 direction_2 = mymod(ndir+D2R(90),D2R(360));
248 if (i == pathcount-1) {
249 bevel = false;
250 }
251 } else {
252 direction_2 = direction;
253 }
254 }
255 pathpoints[i].x = x;
256 pathpoints[i].y = y;
257 pathpoints[i].lengthsofar = dist;
258 pathpoints[i].type = 'l';
259 pathpoints[i].dir = direction;
260 pathpoints[i].lout = lineout;
261 pathpoints[i].bevel = bevel;
262 pathpoints[i].dir2 = direction_2;
263 }
264
265 /* draw line */
266 stroke_t p = {0};
267 /* side 1 */
268 for (size_t i = 0; i < pathcount; i++) {
269 cur_point = pathpoints[i];
270 x = cur_point.x;
271 y = cur_point.y;
272 direction = cur_point.dir;
273 lineout = cur_point.lout;
274 bool bevel = cur_point.bevel;
275 direction_2 = cur_point.dir2;
276 if (i == 0) {
277 moveto(&p, x+cos(direction)*lineout, y+sin(direction)*lineout);
278 } else {
279 lineto(&p, x+cos(direction)*lineout, y+sin(direction)*lineout);
280 }
281 if (bevel) {
282 drawbevel(x, lineout, true, direction, direction_2, &p);
283 }
284 }
285 /* end circle as needed */
286 direction += D2R(180);
287 lineto(&p, x+cos(direction)*lineout, y+sin(direction)*lineout);
288 /* side 2 */
289 assert(pathcount > 0);
290 for (size_t i = pathcount - 2; i != SIZE_MAX; i--) {
291 cur_point = pathpoints[i];
292 x = cur_point.x;
293 y = cur_point.y;
294 direction = cur_point.dir + D2R(180);
295 lineout = cur_point.lout;
296 bool bevel = cur_point.bevel;
297 direction_2 = cur_point.dir2 + D2R(180);
298 lineto(&p, x+cos(direction_2)*lineout, y+sin(direction_2)*lineout);
299 if (bevel) {
300 drawbevel(x, lineout, false, direction, direction_2, &p);
301 }
302 }
303 free(pathpoints);
304 return p;
305}
306
307#ifdef TEST
308static double halffunc (double curlen, double totallen, double initwid)
309{
310 return (1 - curlen / totallen) * initwid / 2.0;
311}
312
313static pointf pts[] = {
314 {100,100},
315 {150,150},
316 {200,100},
317 {250,200},
318};
319
320main ()
321{
322 stroke_t* sp;
323 bezier bez;
324
325 bez.size = sizeof(pts)/sizeof(pointf);
326 bez.list = pts;
327 sp = taper(&bez, halffunc, 20.0);
328 printf ("newpath\n");
329 printf ("%.02f %.02f moveto\n", sp->vertices[0].x, sp->vertices[0].y);
330 for (size_t i = 1; i < sp->nvertices; i++)
331 printf ("%.02f %.02f lineto\n", sp->vertices[i].x, sp->vertices[i].y);
332 printf ("fill showpage\n");
333}
334#endif
Memory allocation wrappers that exit on failure.
static void * gv_recalloc(void *ptr, size_t old_nmemb, size_t new_nmemb, size_t size)
Definition alloc.h:73
pointf Bezier(pointf *V, double t, pointf *Left, pointf *Right)
Definition utils.c:169
#define A(n, t)
Definition expr.h:76
static double dist(int dim, double *x, double *y)
#define V
Definition gdefs.h:5
void free(void *)
#define SIZE_MAX
Definition gmlscan.c:347
node NULL
Definition grammar.y:149
#define DEFINE_LIST(name, type)
Definition list.h:26
#define PRISIZE_T
PRIu64 alike for printing size_t
Definition prisize_t.h:27
Definition types.h:89
size_t size
Definition types.h:91
pointf * list
Definition types.h:90
double dir2
Definition taper.c:92
char type
Definition taper.c:88
double y
Definition taper.c:86
bool bevel
Definition taper.c:91
double dir
Definition taper.c:89
double lout
Definition taper.c:90
double x
Definition taper.c:85
double lengthsofar
Definition taper.c:87
double x
Definition geom.h:29
double y
Definition geom.h:29
pointf * vertices
Definition types.h:175
size_t nvertices
number of points in the stroke
Definition types.h:174
static void drawbevel(double x, double lineout, bool forward, double dir, double dir2, stroke_t *p)
Definition taper.c:161
static void addto(stroke_t *p, double x, double y)
Definition taper.c:47
#define moveto(p, x, y)
Definition taper.c:44
#define D2R(d)
Definition taper.c:39
static double l2dist(pointf p0, pointf p1)
Definition taper.c:116
static double myatan(double y, double x)
Definition taper.c:61
static vararr_t pathtolines(bezier *bez)
Definition taper.c:126
static double mymod(double original, double modulus)
Definition taper.c:74
double(* radfunc_t)(double curlen, double totallen, double initwid)
Definition taper.c:173
#define lineto(p, x, y)
Definition taper.c:45
static double currentmiterlimit
Definition taper.c:42
static void insertArr(vararr_t *arr, pointf p, double l)
Definition taper.c:98
stroke_t taper(bezier *bez, radfunc_t radfunc, double initwid)
Definition taper.c:181
#define M_PI
Definition taper.c:37
#define BEZIERSUBDIVISION
Definition taper.c:33
int main()
graphs, nodes and edges info: Agraphinfo_t, Agnodeinfo_t and Agedgeinfo_t