1/* $Id$ $Revision$ */
2/* vim:set shiftwidth=4 ts=8: */
3
4/*************************************************************************
5 * Copyright (c) 2011 AT&T Intellectual Property
6 * All rights reserved. This program and the accompanying materials
7 * are made available under the terms of the Eclipse Public License v1.0
8 * which accompanies this distribution, and is available at
9 * http://www.eclipse.org/legal/epl-v10.html
10 *
11 * Contributors: See CVS logs. Details at http://www.graphviz.org/
12 *************************************************************************/
13
14
15/*
16 * Written by Stephen North
17 * Updated by Emden Gansner
18 */
19
20#include "config.h"
21
22/* if NC changes, a bunch of scanf calls below are in trouble */
23#define NC 3 /* size of HSB color vector */
24
25#include "cgraph.h"
26#include <stdlib.h>
27typedef struct Agnodeinfo_t {
28 Agrec_t h;
29 double relrank; /* coordinate of its rank, smaller means lower rank */
30 double x[NC]; /* color vector */
31} Agnodeinfo_t;
32
33#define ND_relrank(n) (((Agnodeinfo_t*)((n)->base.data))->relrank)
34#define ND_x(n) (((Agnodeinfo_t*)((n)->base.data))->x)
35
36#include "ingraphs.h"
37#include <stdio.h>
38#ifdef HAVE_UNISTD_H
39#include <unistd.h>
40#endif
41
42#include <getopt.h>
43
44double Defcolor[NC] = { 0.0, 0.0, 1.0 }; /* white */
45int Forward = 1; /* how to propagate colors w.r.t. ranks */
46int LR = 0; /* rank orientation */
47
48int AdjustSaturation;
49double MinRankSaturation;
50double MaxRankSaturation;
51
52extern char *colorxlate(char *str, char *buf);
53
54static int cmpf(Agnode_t ** n0, Agnode_t ** n1)
55{
56 double t;
57 t = ND_relrank(*n0) - ND_relrank(*n1);
58 if (t < 0.0)
59 return -1;
60 if (t > 0.0)
61 return 1;
62 return 0;
63}
64
65static void setcolor(char *p, double *v)
66{
67 char buf[64];
68 if ((sscanf(p, "%lf %lf %lf", v, v + 1, v + 2) != 3) && p[0]) {
69 colorxlate(p, buf);
70 sscanf(buf, "%lf %lf %lf", v, v + 1, v + 2);
71 }
72}
73
74static char **Files;
75
76static char *useString = "Usage: gvcolor [-?] <files>\n\
77 -? - print usage\n\
78If no files are specified, stdin is used\n";
79
80static void usage(int v)
81{
82 printf("%s",useString);
83 exit(v);
84}
85
86static void init(int argc, char *argv[])
87{
88 int c;
89
90 opterr = 0;
91 while ((c = getopt(argc, argv, ":")) != -1) {
92 switch (c) {
93 case '?':
94 if (optopt == '?')
95 usage(0);
96 else
97 fprintf(stderr, "gvcolor: option -%c unrecognized - ignored\n",
98 optopt);
99 break;
100 }
101 }
102 argv += optind;
103 argc -= optind;
104
105 if (argc)
106 Files = argv;
107}
108
109static void color(Agraph_t * g)
110{
111 int nn, i, j, cnt;
112 Agnode_t *n, *v, **nlist;
113 Agedge_t *e;
114 char *p;
115 double x, y, maxrank = 0.0;
116 double sum[NC], d, lowsat, highsat;
117
118 if (agattr(g, AGNODE, "pos", 0) == NULL) {
119 fprintf(stderr,
120 "graph must be run through 'dot' before 'gvcolor'\n");
121 exit(1);
122 }
123 aginit(g, AGNODE, "nodeinfo", sizeof(Agnodeinfo_t), TRUE);
124 if (agattr(g, AGNODE, "style", 0) == NULL)
125 agattr(g, AGNODE, "style", "filled");
126 if ((p = agget(g, "Defcolor")))
127 setcolor(p, Defcolor);
128
129 if ((p = agget(g, "rankdir")) && (p[0] == 'L'))
130 LR = 1;
131 if ((p = agget(g, "flow")) && (p[0] == 'b'))
132 Forward = 0;
133 if ((p = agget(g, "saturation"))) {
134 if (sscanf(p, "%lf,%lf", &lowsat, &highsat) == 2) {
135 MinRankSaturation = lowsat;
136 MaxRankSaturation = highsat;
137 AdjustSaturation = 1;
138 }
139 }
140
141 /* assemble the sorted list of nodes and store the initial colors */
142 nn = agnnodes(g);
143 nlist = (Agnode_t **) malloc(nn * sizeof(Agnode_t *));
144 i = 0;
145 for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
146 nlist[i++] = n;
147 if ((p = agget(n, "color")))
148 setcolor(p, ND_x(n));
149 p = agget(n, "pos");
150 sscanf(p, "%lf,%lf", &x, &y);
151 ND_relrank(n) = (LR ? x : y);
152 if (maxrank < ND_relrank(n))
153 maxrank = ND_relrank(n);
154 }
155 if (LR != Forward)
156 for (i = 0; i < nn; i++) {
157 n = nlist[i];
158 ND_relrank(n) = maxrank - ND_relrank(n);
159 }
160 qsort((void *) nlist, (size_t) nn, sizeof(Agnode_t *),
161 (int (*)(const void *, const void *)) cmpf);
162
163 /* this is the pass that pushes the colors through the edges */
164 for (i = 0; i < nn; i++) {
165 n = nlist[i];
166
167 /* skip nodes that were manually colored */
168 cnt = 0;
169 for (j = 0; j < NC; j++)
170 if (ND_x(n)[j] != 0.0)
171 cnt++;
172 if (cnt > 0)
173 continue;
174
175 for (j = 0; j < NC; j++)
176 sum[j] = 0.0;
177 cnt = 0;
178 for (e = agfstedge(g, n); e; e = agnxtedge(g, e, n)) {
179 v = aghead(e);
180 if (v == n)
181 v = agtail(e);
182 d = ND_relrank(v) - ND_relrank(n) - 0.01;
183 if (d < 0) {
184 double t = 0.0;
185 for (j = 0; j < NC; j++) {
186 t += ND_x(v)[j];
187 sum[j] += ND_x(v)[j];
188 }
189 if (t > 0.0)
190 cnt++;
191 }
192 }
193 if (cnt)
194 for (j = 0; j < NC; j++)
195 ND_x(n)[j] = sum[j] / cnt;
196 }
197
198 /* apply saturation adjustment and convert color to string */
199 for (i = 0; i < nn; i++) {
200 double h, s, b, t;
201 char buf[64];
202
203 n = nlist[i];
204
205 t = 0.0;
206 for (j = 0; j < NC; j++)
207 t += ND_x(n)[j];
208 if (t > 0.0) {
209 h = ND_x(n)[0];
210 if (AdjustSaturation) {
211 s = ND_relrank(n) / maxrank;
212 if (!Forward)
213 s = 1.0 - s;
214 s = MinRankSaturation
215 + s * (MaxRankSaturation - MinRankSaturation);
216 } else
217 s = 1.0;
218 s = s * ND_x(n)[1];
219 b = ND_x(n)[2];
220 } else {
221 h = Defcolor[0];
222 s = Defcolor[1];
223 b = Defcolor[2];
224 }
225 sprintf(buf, "%f %f %f", h, s, b);
226 agset(n, "color", buf);
227 }
228 free (nlist);
229}
230
231static Agraph_t *gread(FILE * fp)
232{
233 return agread(fp, (Agdisc_t *) 0);
234}
235
236int main(int argc, char **argv)
237{
238 Agraph_t *g;
239 ingraph_state ig;
240
241 init(argc, argv);
242 newIngraph(&ig, Files, gread);
243
244 while ((g = nextGraph(&ig)) != 0) {
245 color(g);
246 agwrite(g, stdout);
247 fflush(stdout);
248 agclose(g);
249 }
250
251 exit(0);
252}
253