Inkscape
Vector Graphics Editor
Loading...
Searching...
No Matches
output_svg.cpp
Go to the documentation of this file.
1/*
2 * vim: ts=4 sw=4 et tw=0 wm=0
3 *
4 * libcola - A library providing force-directed network layout using the
5 * stress-majorization method subject to separation constraints.
6 *
7 * Copyright (C) 2006-2008 Monash University
8 *
9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Lesser General Public
11 * License as published by the Free Software Foundation; either
12 * version 2.1 of the License, or (at your option) any later version.
13 * See the file LICENSE.LGPL distributed with the library.
14 *
15 * This library is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
18 *
19*/
20
21#include <string>
22#include <iostream>
23#include <sstream>
24#include <list>
25
26#include "libcola/output_svg.h"
27#include "libcola/cola.h"
29
30using namespace cola;
31using vpsc::Rectangle;
32using std::endl;
33using std::cout;
34using std::ios;
35using std::max;
36using std::min;
37using std::ofstream;
38using std::vector;
39using std::list;
40
42 unsigned E=es.size();
43 bool cleanupRoutes=false;
44 if(routes==nullptr) {
45 cleanupRoutes=true;
46 routes = new vector<straightener::Route*>(E);
47 for(unsigned i=0;i<E;i++) {
49 r->xs[0]=rs[es[i].first]->getCentreX();
50 r->ys[0]=rs[es[i].first]->getCentreY();
51 r->xs[1]=rs[es[i].second]->getCentreX();
52 r->ys[1]=rs[es[i].second]->getCentreY();
53 (*routes)[i]=r;
54 }
55 }
56
57#if defined (CAIRO_HAS_SVG_SURFACE) && defined (CAIRO_HAS_PDF_SURFACE)
58 double width,height,r=2;
59 if(rects) r=rs[0]->width()/2;
60 double xmin=DBL_MAX, ymin=xmin;
61 double xmax=-DBL_MAX, ymax=xmax;
62 for (unsigned i=0;i<rs.size();i++) {
63 double x=rs[i]->getCentreX(), y=rs[i]->getCentreY();
64 xmin=min(xmin,x);
65 ymin=min(ymin,y);
66 xmax=max(xmax,x);
67 ymax=max(ymax,y);
68 }
69 xmax+=2*r;
70 ymax+=2*r;
71 xmin-=2*r;
72 ymin-=2*r;
73 width=xmax-xmin;
74 height=ymax-ymin;
75
76 Cairo::RefPtr<Cairo::Context> cr;
78
79 /* set background colour
80 cr->save(); // save the state of the context
81 cr->set_source_rgb(0.86, 0.85, 0.47);
82 cr->paint(); // fill image with the color
83 cr->restore(); // color is back to black now
84 */
85
86 cr->set_line_width(1.);
87 cr->set_font_size(8);
88 cr->save();
89 if(rc) for(Clusters::const_iterator c=rc->clusters.begin();c!=rc->clusters.end();c++) {
90 draw_cluster_boundary(cr,**c,xmin,ymin);
91 }
92 if(curvedEdges)
93 draw_curved_edges(cr,es,xmin,ymin);
94 else
95 draw_edges(cr,*routes,xmin,ymin);
96 Cairo::TextExtents te;
97 for (unsigned i=0;i<rs.size();i++) {
98 if(!rects) {
99 double x=rs[i]->getCentreX()-xmin, y=rs[i]->getCentreY()-ymin;
100 cr->arc(x,y,r, 0.0, 2.0 * M_PI);
101 cr->fill();
102 } else {
103 double x=rs[i]->getMinX()-xmin+0.5, y=rs[i]->getMinY()-ymin+0.5;
104 std::string str;
105 if(labels.size()==rs.size()) {
106 str=labels[i];
107 } else {
108 std::stringstream s; s<<i;
109 str=s.str();
110 }
111 cr->get_text_extents(str,te);
112 /*
113 double llx = x-te.width/2.-1;
114 double lly = y-te.height/2.-1;
115 cr->rectangle(llx,lly,te.width+2,te.height+2);
116 */
117 cr->rectangle(x,y,
118 rs[i]->width()-1,rs[i]->height()-1);
119 cr->stroke_preserve();
120 cr->save();
121 cr->set_source_rgba(245./255., 233./255., 177./255., 0.6);
122 cr->fill();
123 cr->restore();
124 if(labels.size()==rs.size()) {
125 cr->move_to(x-te.x_bearing+te.width/2.,y-te.y_bearing+te.height/2.);
126 cr->show_text(str);
127 }
128 cr->stroke();
129 }
130 }
131
132 cr->show_page();
133
134 std::cout << "Wrote file \"" << fname << "\"" << std::endl;
135
136#else
137 std::cout <<
138 "WARNING: cola::OutputFile::generate(): No SVG file produced." <<
139 std::endl <<
140 " You must have cairomm (and cairo with SVG support) " <<
141 "this to work." << std::endl;
142#endif
143
144 if(cleanupRoutes) {
145 for(unsigned i=0;i<E;i++) {
146 delete (*routes)[i];
147 }
148 delete routes;
149 }
150}
151
152#ifdef HAVE_CAIROMM
153void OutputFile::draw_cluster_boundary(Cairo::RefPtr<Cairo::Context> const &cr,
154 Cluster &c,
155 const double xmin,
156 const double ymin) {
157 c.computeBoundary(rs);
158 cr->save();
159 // background
160 cr->set_source_rgb(0.7, 0.7, 224./255.);
161 cr->move_to(c.hullX[0]-xmin,c.hullY[0]-ymin);
162 for(unsigned i=1;i<c.hullX.size();i++) {
163 cr->line_to(c.hullX[i]-xmin,c.hullY[i]-ymin);
164 }
165 cr->line_to(c.hullX[0]-xmin,c.hullY[0]-ymin);
166 cr->fill();
167 cr->restore();
168 // outline
169 cr->move_to(c.hullX[0]-xmin,c.hullY[0]-ymin);
170 for(unsigned i=1;i<c.hullX.size();i++) {
171 cr->line_to(c.hullX[i]-xmin,c.hullY[i]-ymin);
172 }
173 cr->line_to(c.hullX[0]-xmin,c.hullY[0]-ymin);
174 cr->stroke();
175}
176
177void OutputFile::draw_edges(Cairo::RefPtr<Cairo::Context> &cr,
178 vector<straightener::Route*> const & es, double const xmin, double const ymin) {
179 cr->save();
180 // background
181 cr->set_source_rgba(0,0,1,0.5);
182 for (unsigned i=0;i<es.size();i++) {
183 const straightener::Route* r=es[i];
184 cr->move_to(r->xs[0]-xmin,r->ys[0]-ymin);
185 for (unsigned j=1;j<r->n;j++) {
186 cr->line_to(r->xs[j]-xmin,r->ys[j]-ymin);
187 }
188 cr->stroke();
189 }
190 cr->restore();
191}
192
193namespace bundles {
194struct CEdge {
195 unsigned startID, endID;
196 double x0,y0,x1,y1,x2,y2,x3,y3;
197};
198struct CBundle;
199struct CNode {
200 double x,y;
201 vector<CEdge*> edges;
202 list<CBundle*> bundles;
203};
204double vangle(double ax,double ay, double bx, double by) {
205 double ab=ax*bx+ay*by;
206 double len=sqrt(ax*ax+ay*ay)*sqrt(bx*bx+by*by);
207 double angle=acos(ab/len);
208 //printf("ab=%f len=%f angle=%f\n",ab,len,angle);
209 return angle;
210}
211struct CBundle {
212 unsigned w;
213 double x0, y0;
214 double sx,sy;
215 vector<CEdge*> edges;
216 CBundle(CNode const &u) : w(u.edges.size()), x0(u.x), y0(u.y), sx(w*u.x), sy(w*u.y) { }
217 void addEdge(CEdge *e) {
218 if(x0==e->x0 && y0==e->y0) {
219 sx+=e->x3; sy+=e->y3;
220 } else {
221 sx+=e->x0; sy+=e->y0;
222 }
223 edges.push_back(e);
224 }
225 double x1() const {
226 return sx/(w+edges.size());
227 }
228 double y1() const {
229 return sy/(w+edges.size());
230 }
231 double angle(CBundle* const &b) const {
232 double ax=x1()-x0;
233 double ay=y1()-y0;
234 double bx=b->x1()-b->x0;
235 double by=b->y1()-b->y0;
236 return vangle(ax,ay,bx,by);
237 }
238 double yangle() const {
239 double x=x1()-x0;
240 double y=y1()-y0;
241 double o=x<0?1:-1;
242 return vangle(0,1,x,y)*o+M_PI;
243 }
244 void merge(CBundle* b) {
245 for(unsigned i=0;i<b->edges.size();i++) {
246 addEdge(b->edges[i]);
247 }
248 }
249 void dump() {
250 printf("Bundle: ");
251 for(unsigned i=0;i<edges.size();i++) {
252 printf("(%d,%d) ",edges[i]->startID,edges[i]->endID);
253 }
254 }
255};
256struct clockwise {
257 bool operator() (CBundle* const &a, CBundle* const &b) {
258 return a->yangle()<b->yangle();
259 }
260};
261} //namespace bundles
262
263/*
264 * draw edges bundled. That is, edges are drawn as splines, with the control points
265 * between adjacent edges outgoing from a particular node shared if the angle between them
266 * is less than pi/8
267 */
268void OutputFile::draw_curved_edges(Cairo::RefPtr<Cairo::Context> &cr,
269 vector<cola::Edge> const & es,
270 const double xmin,
271 const double ymin) {
272 using namespace bundles;
273 vector<CNode> nodes(rs.size());
274 vector<CEdge> edges(es.size());
275 for (unsigned i=0;i<es.size();i++) {
276 CEdge *e=&edges[i];
277 unsigned start=es[i].first;
278 unsigned end=es[i].second;
279 e->startID=start;
280 e->endID=end;
281 nodes[start].x=rs[start]->getCentreX()-xmin;
282 nodes[start].y=rs[start]->getCentreY()-ymin;
283 nodes[end].x=rs[end]->getCentreX()-xmin;
284 nodes[end].y=rs[end]->getCentreY()-ymin;
285 e->x0=nodes[start].x;
286 e->x1=nodes[start].x;
287 e->x2=nodes[end].x;
288 e->x3=nodes[end].x;
289 e->y0=nodes[start].y;
290 e->y1=nodes[start].y;
291 e->y2=nodes[end].y;
292 e->y3=nodes[end].y;
293 nodes[end].edges.push_back(e);
294 nodes[start].edges.push_back(e);
295 }
296
297 for (unsigned i=0;i<nodes.size();i++) {
298 CNode u=nodes[i];
299 if(u.edges.size()<2) continue;
300 for (unsigned j=0;j<u.edges.size();j++) {
301 CBundle* b=new CBundle(u);
302 b->addEdge(u.edges[j]);
303 u.bundles.push_back(b);
304 }
305 u.bundles.sort(clockwise());
306 /*
307 printf("Sorted: \n");
308 list<CBundle*>::iterator i,j;
309 for(list<CBundle*>::iterator i=u.bundles.begin();i!=u.bundles.end();i++) {
310 CBundle* a=*i;
311 a->dump();
312 printf(" angle=%f\n",a->yangle());
313 }
314 printf("---------\n");
315 */
316 while(true) {
317 double minAngle=DBL_MAX;
318 list<CBundle*>::iterator mini,minj,i,j;
319 for(i=u.bundles.begin();i!=u.bundles.end();i++) {
320 j=i;
321 if(++j==u.bundles.end()) {
322 j=u.bundles.begin();
323 }
324 CBundle* a=*i;
325 CBundle* b=*j;
326 double angle=b->yangle()-a->yangle();
327 if(angle<0) angle+=2*M_PI;
328 //printf("between ");
329 //a->dump(); b->dump();
330 //printf(" angle=%f\n",angle);
331 if(angle<minAngle) {
332 minAngle=angle;
333 mini=i;
334 minj=j;
335 }
336 }
337 if(minAngle>cos(M_PI/8.)) break;
338 CBundle* a=*mini;
339 CBundle* b=*minj;
340 //a->dump();
341 //b->dump();
342 b->merge(a);
343 //printf("***Merged on %f***: ",minAngle);
344 //b->dump();
345 //printf("\n");
346 u.bundles.erase(mini);
347 if(u.bundles.size() < 2) break;
348 }
349 for(list<CBundle*>::iterator i=u.bundles.begin();i!=u.bundles.end();i++) {
350 CBundle* b=*i;
351 for(unsigned i=0;i<b->edges.size();i++) {
352 CEdge* e=b->edges[i];
353 if(e->x0==u.x&&e->y0==u.y) {
354 e->x1=b->x1();
355 e->y1=b->y1();
356 } else {
357 e->x2=b->x1();
358 e->y2=b->y1();
359 }
360 }
361 }
362 }
363
364 cr->save();
365 // background
366 cr->set_source_rgba(0,0,1,0.2);
367 for (unsigned i=0;i<edges.size();i++) {
368 CEdge &e=edges[i];
369 cr->move_to(e.x0,e.y0);
370 cr->curve_to(e.x1,e.y1,e.x2,e.y2,e.x3,e.y3);
371 cr->stroke();
372 }
373 cr->restore();
374}
375void OutputFile::openCairo(Cairo::RefPtr<Cairo::Context> &cr, double width, double height) {
376 if(fname.rfind("pdf") == (fname.length()-3) ) {
377 printf("writing pdf file: %s\n",fname.c_str());
378 Cairo::RefPtr<Cairo::PdfSurface> pdfsurface =
379 Cairo::PdfSurface::create(fname, width, height);
380 cr = Cairo::Context::create(pdfsurface);
381 } else {
382 printf("writing svg file: %s\n",fname.c_str());
383 Cairo::RefPtr<Cairo::SvgSurface> svgsurface =
384 Cairo::SvgSurface::create(fname, width, height);
385 cr = Cairo::Context::create(svgsurface);
386 }
387}
388
389#endif // HAVE_CAIROMM
constexpr Coord x() const noexcept
Definition point.h:104
std::string const fname
Definition output_svg.h:38
void draw_curved_edges(Cairo::RefPtr< Cairo::Context > &cr, std::vector< cola::Edge > const &es, const double xmin, const double ymin)
std::vector< std::string > labels
Definition output_svg.h:78
std::vector< straightener::Route * > * routes
Definition output_svg.h:36
std::vector< cola::Edge > const & es
Definition output_svg.h:35
bool curvedEdges
Definition output_svg.h:40
void generate()
void openCairo(Cairo::RefPtr< Cairo::Context > &cr, double width, double height)
void draw_cluster_boundary(Cairo::RefPtr< Cairo::Context > const &cr, cola::Cluster &c, const double xmin, const double ymin)
std::vector< vpsc::Rectangle * > const & rs
Definition output_svg.h:34
cola::RootCluster const * rc
Definition output_svg.h:37
void draw_edges(Cairo::RefPtr< Cairo::Context > &cr, std::vector< straightener::Route * > const &es, double const xmin, double const ymin)
A cluster defines a hierarchical partitioning over the nodes which should be kept disjoint by the lay...
Definition cluster.h:51
std::vector< Cluster * > clusters
Definition cluster.h:116
A rectangle represents a fixed-size shape in the diagram that may be moved to prevent overlaps and sa...
Definition rectangle.h:78
const double w
Definition conic-4.cpp:19
vector< Edge > es
Geom::IntPoint size
double c[8][4]
Geom::Point start
Geom::Point end
double angle(std::vector< Point > const &A)
ConvexHull merge(ConvexHull a, ConvexHull b)
double vangle(double ax, double ay, double bx, double by)
libcola: Force-directed network layout subject to separation constraints library.
Definition box.cpp:25
auto len
Definition safe-printf.h:21
Edges edges(Path const &p, Crossings const &cr, unsigned ix)
Definition sanitize.cpp:36
double height
double width