12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556 |
- /*************************************************************************
- * Copyright (c) 2011 AT&T Intellectual Property
- * All rights reserved. This program and the accompanying materials
- * are made available under the terms of the Eclipse Public License v1.0
- * which accompanies this distribution, and is available at
- * https://www.eclipse.org/legal/epl-v10.html
- *
- * Contributors: Details at https://graphviz.org
- *************************************************************************/
- /* TODO:
- * In dot, prefer bottom or top routing
- * In general, prefer closest side to closest side routing.
- * Edge labels
- * Ports/compass points
- * ordering attribute
- * Weights on edges in nodes
- * Edge concentrators?
- */
- #include "config.h"
- #define DEBUG
- #include <assert.h>
- #include <float.h>
- #include <math.h>
- #include <stdbool.h>
- #include <stddef.h>
- #include <ortho/maze.h>
- #include <ortho/fPQ.h>
- #include <ortho/ortho.h>
- #include <common/geomprocs.h>
- #include <common/globals.h>
- #include <common/render.h>
- #include <common/pointset.h>
- #include <util/alloc.h>
- #include <util/exit.h>
- #include <util/unused.h>
- typedef struct {
- int d;
- Agedge_t* e;
- } epair_t;
- #ifdef DEBUG
- #define DEBUG_FN /* nothing */
- #else
- #define DEBUG_FN UNUSED
- #endif
- static DEBUG_FN void emitSearchGraph(FILE *fp, sgraph *sg);
- static DEBUG_FN void emitGraph(FILE *fp, maze *mp, size_t n_edges,
- route *route_list, epair_t[]);
- #ifdef DEBUG
- int odb_flags;
- #endif
- #define CELL(n) ((cell*)ND_alg(n))
- #define MID(a,b) (((a)+(b))/2.0)
- #define SC 1
- /* cellOf:
- * Given 2 snodes sharing a cell, return the cell.
- */
- static cell*
- cellOf (snode* p, snode* q)
- {
- cell* cp = p->cells[0];
- if (cp == q->cells[0] || cp == q->cells[1]) return cp;
- else return p->cells[1];
- }
- static pointf
- midPt (cell* cp)
- {
- pointf p;
- p.x = MID(cp->bb.LL.x,cp->bb.UR.x);
- p.y = MID(cp->bb.LL.y,cp->bb.UR.y);
- return p;
- }
- /* sidePt:
- * Given a cell and an snode on one of its sides, return the
- * midpoint of the side.
- */
- static pointf
- sidePt (snode* ptr, cell* cp)
- {
- pointf pt;
- if (cp == ptr->cells[1]) {
- if (ptr->isVert) {
- pt.x = cp->bb.LL.x;
- pt.y = MID(cp->bb.LL.y,cp->bb.UR.y);
- }
- else {
- pt.x = MID(cp->bb.LL.x,cp->bb.UR.x);
- pt.y = cp->bb.LL.y;
- }
- }
- else {
- if (ptr->isVert) {
- pt.x = cp->bb.UR.x;
- pt.y = MID(cp->bb.LL.y,cp->bb.UR.y);
- }
- else {
- pt.x = MID(cp->bb.LL.x,cp->bb.UR.x);
- pt.y = cp->bb.UR.y;
- }
- }
- return pt;
- }
- /* setSet:
- * Initialize and normalize segments.
- * p1 stores smaller value
- * Assume b1 != b2
- */
- static void
- setSeg (segment* sp, bool dir, double fix, double b1, double b2, int l1, int l2)
- {
- sp->isVert = dir;
- sp->comm_coord = fix;
- if (b1 < b2) {
- sp->p.p1 = b1;
- sp->p.p2 = b2;
- sp->l1 = l1;
- sp->l2 = l2;
- }
- else {
- sp->p.p2 = b1;
- sp->p.p1 = b2;
- sp->l2 = l1;
- sp->l1 = l2;
- }
- }
- /* Convert route in shortest path graph to route
- * of segments. This records the first and last cells,
- * plus cells where the path bends.
- * Note that the shortest path will always have at least 4 nodes:
- * the two dummy nodes representing the center of the two real nodes,
- * and the two nodes on the boundary of the two real nodes.
- */
- static route
- convertSPtoRoute (sgraph* g, snode* fst, snode* lst)
- {
- route rte;
- snode* ptr;
- snode* next;
- snode* prev; /* node in shortest path just previous to next */
- size_t sz = 0;
- cell* cp;
- cell* ncp;
- segment seg;
- double fix, b1, b2;
- int l1, l2;
- pointf bp1, bp2, prevbp = {0.0,0.0}; /* bend points */
- /* count no. of nodes in shortest path */
- for (ptr = fst; ptr; ptr = N_DAD(ptr)) sz++;
- rte.n = 0;
- assert(sz >= 2);
- rte.segs = gv_calloc(sz - 2, sizeof(segment)); /* at most sz-2 segments */
- seg.prev = seg.next = 0;
- ptr = prev = N_DAD(fst);
- next = N_DAD(ptr);
- if (IsNode(ptr->cells[0]))
- cp = ptr->cells[1];
- else
- cp = ptr->cells[0];
- bp1 = sidePt (ptr, cp);
- while (N_DAD(next)!=NULL) {
- ncp = cellOf (prev, next);
- updateWts (g, ncp, N_EDGE(ptr));
- /* add seg if path bends or at end */
- if (ptr->isVert != next->isVert || N_DAD(next) == lst) {
- if (ptr->isVert != next->isVert)
- bp2 = midPt (ncp);
- else
- bp2 = sidePt(next, ncp);
- if (ptr->isVert) { /* horizontal segment */
- if (ptr == N_DAD(fst)) l1 = B_NODE;
- else if (prevbp.y > bp1.y) l1 = B_UP;
- else l1 = B_DOWN;
- if (ptr->isVert != next->isVert) {
- if (next->cells[0] == ncp) l2 = B_UP;
- else l2 = B_DOWN;
- }
- else l2 = B_NODE;
- fix = cp->bb.LL.y;
- b1 = cp->bb.LL.x;
- b2 = ncp->bb.LL.x;
- }
- else { /* vertical segment */
- if (ptr == N_DAD(fst)) l1 = B_NODE;
- else if (prevbp.x > bp1.x) l1 = B_RIGHT;
- else l1 = B_LEFT;
- if (ptr->isVert != next->isVert) {
- if (next->cells[0] == ncp) l2 = B_RIGHT;
- else l2 = B_LEFT;
- }
- else l2 = B_NODE;
- fix = cp->bb.LL.x;
- b1 = cp->bb.LL.y;
- b2 = ncp->bb.LL.y;
- }
- setSeg (&seg, !ptr->isVert, fix, b1, b2, l1, l2);
- rte.segs[rte.n++] = seg;
- cp = ncp;
- prevbp = bp1;
- bp1 = bp2;
- if (ptr->isVert != next->isVert && N_DAD(next) == lst) {
- bp2 = sidePt(next, ncp);
- l2 = B_NODE;
- if (next->isVert) { /* horizontal segment */
- if (prevbp.y > bp1.y) l1 = B_UP;
- else l1 = B_DOWN;
- fix = cp->bb.LL.y;
- b1 = cp->bb.LL.x;
- b2 = ncp->bb.LL.x;
- }
- else {
- if (prevbp.x > bp1.x) l1 = B_RIGHT;
- else l1 = B_LEFT;
- fix = cp->bb.LL.x;
- b1 = cp->bb.LL.y;
- b2 = ncp->bb.LL.y;
- }
- setSeg (&seg, !next->isVert, fix, b1, b2, l1, l2);
- rte.segs[rte.n++] = seg;
- }
- ptr = next;
- }
- prev = next;
- next = N_DAD(next);
- }
- rte.segs = gv_recalloc(rte.segs, sz - 2, rte.n, sizeof(segment));
- for (size_t i=0; i<rte.n; i++) {
- if (i > 0)
- rte.segs[i].prev = rte.segs + (i-1);
- if (i < rte.n-1)
- rte.segs[i].next = rte.segs + (i+1);
- }
- return rte;
- }
- typedef struct {
- Dtlink_t link;
- double v;
- Dt_t* chans;
- } chanItem;
- static void freeChannel(void *chan) {
- channel *cp = chan;
- free_graph (cp->G);
- seg_list_free(&cp->seg_list);
- free (cp);
- }
- static void freeChanItem(void *item) {
- chanItem *cp = item;
- dtclose (cp->chans);
- free (cp);
- }
- /* chancmpid:
- * Compare intervals. Two intervals are equal if one contains
- * the other. Otherwise, the one with the smaller p1 value is
- * less.
- * This combines two separate functions into one. Channels are
- * disjoint, so we really only need to key on p1.
- * When searching for a channel containing a segment, we rely on
- * interval containment to return the correct channel.
- */
- static int chancmpid(void *k1, void *k2) {
- const paird *key1 = k1;
- const paird *key2 = k2;
- if (key1->p1 > key2->p1) {
- if (key1->p2 <= key2->p2) return 0;
- else return 1;
- }
- else if (key1->p1 < key2->p1) {
- if (key1->p2 >= key2->p2) return 0;
- else return -1;
- }
- else return 0;
- }
- static int dcmpid(void *k1, void *k2) {
- const double *key1 = k1;
- const double *key2 = k2;
- if (*key1 > *key2) return 1;
- else if (*key1 < *key2) return -1;
- else return 0;
- }
- static Dtdisc_t chanDisc = {
- offsetof(channel,p),
- sizeof(paird),
- offsetof(channel,link),
- 0,
- freeChannel,
- chancmpid,
- };
- static Dtdisc_t chanItemDisc = {
- offsetof(chanItem,v),
- sizeof(double),
- offsetof(chanItem,link),
- 0,
- freeChanItem,
- dcmpid,
- };
- static void
- addChan (Dt_t* chdict, channel* cp, double j)
- {
- chanItem* subd = dtmatch (chdict, &j);
- if (!subd) {
- subd = gv_alloc(sizeof(chanItem));
- subd->v = j;
- subd->chans = dtopen (&chanDisc, Dtoset);
- dtinsert (chdict, subd);
- }
- dtinsert (subd->chans, cp);
- }
- static Dt_t*
- extractHChans (maze* mp)
- {
- int i;
- snode* np;
- Dt_t* hchans = dtopen (&chanItemDisc, Dtoset);
- for (i = 0; i < mp->ncells; i++) {
- channel* chp;
- cell* cp = mp->cells+i;
- cell* nextcp;
- if (IsHScan(cp)) continue;
- /* move left */
- while ((np = cp->sides[M_LEFT]) && (nextcp = np->cells[0]) &&
- !IsNode(nextcp)) {
- cp = nextcp;
- }
- chp = gv_alloc(sizeof(channel));
- chp->cp = cp;
- chp->p.p1 = cp->bb.LL.x;
- /* move right */
- cp->flags |= MZ_HSCAN;
- while ((np = cp->sides[M_RIGHT]) && (nextcp = np->cells[1]) &&
- !IsNode(nextcp)) {
- cp = nextcp;
- cp->flags |= MZ_HSCAN;
- }
- chp->p.p2 = cp->bb.UR.x;
- addChan (hchans, chp, chp->cp->bb.LL.y);
- }
- return hchans;
- }
- static Dt_t*
- extractVChans (maze* mp)
- {
- int i;
- snode* np;
- Dt_t* vchans = dtopen (&chanItemDisc, Dtoset);
- for (i = 0; i < mp->ncells; i++) {
- channel* chp;
- cell* cp = mp->cells+i;
- cell* nextcp;
- if (IsVScan(cp)) continue;
- /* move down */
- while ((np = cp->sides[M_BOTTOM]) && (nextcp = np->cells[0]) &&
- !IsNode(nextcp)) {
- cp = nextcp;
- }
- chp = gv_alloc(sizeof(channel));
- chp->cp = cp;
- chp->p.p1 = cp->bb.LL.y;
- /* move up */
- cp->flags |= MZ_VSCAN;
- while ((np = cp->sides[M_TOP]) && (nextcp = np->cells[1]) &&
- !IsNode(nextcp)) {
- cp = nextcp;
- cp->flags |= MZ_VSCAN;
- }
- chp->p.p2 = cp->bb.UR.y;
- addChan (vchans, chp, chp->cp->bb.LL.x);
- }
- return vchans;
- }
- static void
- insertChan (channel* chan, segment* seg)
- {
- seg->ind_no = seg_list_size(&chan->seg_list);
- seg_list_append(&chan->seg_list, seg);
- }
- static channel*
- chanSearch (Dt_t* chans, segment* seg)
- {
- channel* cp;
- chanItem* chani = dtmatch (chans, &seg->comm_coord);
- assert (chani);
- cp = dtmatch (chani->chans, &seg->p);
- assert (cp);
- return cp;
- }
- static void
- assignSegs (size_t nrtes, route* route_list, maze* mp)
- {
- channel* chan;
- for (size_t i=0;i<nrtes;i++) {
- route rte = route_list[i];
- for (size_t j=0;j<rte.n;j++) {
- segment* seg = rte.segs+j;
- if (seg->isVert)
- chan = chanSearch(mp->vchans, seg);
- else
- chan = chanSearch(mp->hchans, seg);
- insertChan (chan, seg);
- }
- }
- }
- /* addLoop:
- * Add two temporary nodes to sgraph corresponding to two ends of a loop at cell cp, i
- * represented by dp and sp.
- */
- static void
- addLoop (sgraph* sg, cell* cp, snode* dp, snode* sp)
- {
- int i;
- int onTop;
- for (i = 0; i < cp->nsides; i++) {
- snode* onp = cp->sides[i];
- if (onp->isVert) continue;
- if (onp->cells[0] == cp) {
- onTop = 1;
- }
- else {
- onTop = 0;
- }
- if (onTop)
- createSEdge (sg, sp, onp, 0); /* FIX weight */
- else
- createSEdge (sg, dp, onp, 0); /* FIX weight */
- }
- sg->nnodes += 2;
- }
- /* addNodeEdges:
- * Add temporary node to sgraph corresponding to cell cp, represented
- * by np.
- */
- static void
- addNodeEdges (sgraph* sg, cell* cp, snode* np)
- {
- int i;
- for (i = 0; i < cp->nsides; i++) {
- snode* onp = cp->sides[i];
- createSEdge (sg, np, onp, 0); /* FIX weight */
- }
- sg->nnodes++;
- #ifdef DEBUG
- np->cells[0] = np->cells[1] = cp;
- #endif
- }
- static char* bendToStr (bend b)
- {
- char* s = NULL;
- switch (b) {
- case B_NODE :
- s = "B_NODE";
- break;
- case B_UP :
- s = "B_UP";
- break;
- case B_LEFT :
- s = "B_LEFT";
- break;
- case B_DOWN :
- s = "B_DOWN";
- break;
- default:
- assert(b == B_RIGHT);
- s = "B_RIGHT";
- break;
- }
- return s;
- }
- static void putSeg (FILE* fp, segment* seg)
- {
- if (seg->isVert)
- fprintf (fp, "((%f,%f),(%f,%f)) %s %s", seg->comm_coord, seg->p.p1,
- seg->comm_coord, seg->p.p2, bendToStr (seg->l1), bendToStr (seg->l2));
- else
- fprintf (fp, "((%f,%f),(%f,%f)) %s %s", seg->p.p1,seg->comm_coord,
- seg->p.p2, seg->comm_coord, bendToStr (seg->l1), bendToStr (seg->l2));
- }
- static DEBUG_FN void dumpChanG(channel *cp, double v) {
- if (seg_list_size(&cp->seg_list) < 2) return;
- fprintf (stderr, "channel %.0f (%f,%f)\n", v, cp->p.p1, cp->p.p2);
- for (size_t k = 0; k < seg_list_size(&cp->seg_list); ++k) {
- const adj_list_t adj = cp->G->vertices[k].adj_list;
- if (adj_list_is_empty(&adj)) continue;
- putSeg(stderr, seg_list_get(&cp->seg_list, k));
- fputs (" ->\n", stderr);
- for (size_t i = 0; i < adj_list_size(&adj); ++i) {
- fputs (" ", stderr);
- putSeg(stderr, seg_list_get(&cp->seg_list, adj_list_get(&adj, i)));
- fputs ("\n", stderr);
- }
- }
- }
- static void
- assignTrackNo (Dt_t* chans)
- {
- Dt_t* lp;
- Dtlink_t* l1;
- Dtlink_t* l2;
- channel* cp;
- for (l1 = dtflatten (chans); l1; l1 = dtlink(chans,l1)) {
- lp = ((chanItem*)l1)->chans;
- for (l2 = dtflatten (lp); l2; l2 = dtlink(lp,l2)) {
- cp = (channel*)l2;
- if (!seg_list_is_empty(&cp->seg_list)) {
- #ifdef DEBUG
- if (odb_flags & ODB_CHANG) dumpChanG (cp, ((chanItem*)l1)->v);
- #endif
- top_sort (cp->G);
- for (size_t k = 0; k < seg_list_size(&cp->seg_list); ++k)
- seg_list_get(&cp->seg_list, k)->track_no = cp->G->vertices[k].topsort_order+1;
- }
- }
- }
- }
- static void
- create_graphs(Dt_t* chans)
- {
- Dt_t* lp;
- Dtlink_t* l1;
- Dtlink_t* l2;
- channel* cp;
- for (l1 = dtflatten (chans); l1; l1 = dtlink(chans,l1)) {
- lp = ((chanItem*)l1)->chans;
- for (l2 = dtflatten (lp); l2; l2 = dtlink(lp,l2)) {
- cp = (channel*)l2;
- cp->G = make_graph(seg_list_size(&cp->seg_list));
- }
- }
- }
- static int
- eqEndSeg (bend S1l2, bend S2l2, bend T1, bend T2)
- {
- if ((S1l2==T2 && S2l2!=T2) || (S1l2==B_NODE && S2l2==T1))
- return 0;
- else
- return -1;
- }
- static int
- overlapSeg (segment* S1, segment* S2, bend T1, bend T2)
- {
- if(S1->p.p2<S2->p.p2) {
- if(S1->l2==T1&&S2->l1==T2) return(-1);
- else if(S1->l2==T2&&S2->l1==T1) return(1);
- else return(0);
- }
- if (S1->p.p2 > S2->p.p2) {
- if(S2->l1==T2&&S2->l2==T2) return(-1);
- else if (S2->l1==T1&&S2->l2==T1) return(1);
- else return(0);
- }
- if (S2->l1 == T2) return eqEndSeg (S1->l2, S2->l2, T1, T2);
- return -1 * eqEndSeg(S2->l2, S1->l2, T1, T2);
- }
- static int
- ellSeg (bend S1l1, bend S1l2, bend T)
- {
- if (S1l1 == T) {
- if (S1l2== T) return -1;
- else return 0;
- }
- else return 1;
- }
- static int
- segCmp (segment* S1, segment* S2, bend T1, bend T2)
- {
- /* no overlap */
- if (S1->p.p2 < S2->p.p1 || S1->p.p1 > S2->p.p2) return 0;
- /* left endpoint of S2 inside S1 */
- if(S1->p.p1<S2->p.p1&&S2->p.p1<S1->p.p2)
- return overlapSeg (S1, S2, T1, T2);
- /* left endpoint of S1 inside S2 */
- if (S2->p.p1 < S1->p.p1 && S1->p.p1 < S2->p.p2)
- return -1*overlapSeg (S2, S1, T1, T2);
- if (S1->p.p1 == S2->p.p1) {
- if(S1->p.p2==S2->p.p2) {
- if (S1->l1 == S2->l1 && S1->l2 == S2->l2)
- return 0;
- if (S2->l1 == S2->l2) {
- if (S2->l1 == T1) return 1;
- if (S2->l1 == T2) return -1;
- if (S1->l1 != T1 && S1->l2 != T1) return 1;
- if (S1->l1 != T2 && S1->l2 != T2) return -1;
- return 0;
- }
- if (S2->l1 == T1 && S2->l2 == T2) {
- if (S1->l1 != T1 && S1->l2 == T2) return 1;
- if (S1->l1 == T1 && S1->l2 != T2) return -1;
- return 0;
- }
- if (S2->l2 == T1 && S2->l1 == T2) {
- if (S1->l2 != T1 && S1->l1 == T2) return 1;
- if (S1->l2 == T1 && S1->l1 != T2) return -1;
- return 0;
- }
- if (S2->l1 == B_NODE && S2->l2 == T1) {
- return ellSeg (S1->l1, S1->l2, T1);
- }
- if (S2->l1 == B_NODE && S2->l2 == T2) {
- return -1*ellSeg (S1->l1, S1->l2, T2);
- }
- if (S2->l1 == T1 && S2->l2 == B_NODE) {
- return ellSeg (S1->l2, S1->l1, T1);
- }
- /* ((S2->l1==T2)&&(S2->l2==B_NODE)) */
- return -1 * ellSeg(S1->l2, S1->l1, T2);
- }
- if (S1->p.p2 < S2->p.p2) {
- if(S1->l2==T1)
- return eqEndSeg (S2->l1, S1->l1, T1, T2);
- return -1 * eqEndSeg(S2->l1, S1->l1, T1, T2);
- }
- /* S1->p.p2>S2->p.p2 */
- if (S2->l2 == T2)
- return eqEndSeg(S1->l1, S2->l1, T1, T2);
- return -1 * eqEndSeg(S1->l1, S2->l1, T1, T2);
- }
- if (S1->p.p2 == S2->p.p1) {
- if (S1->l2 == S2->l1) return 0;
- if (S1->l2 == T2) return 1;
- return -1;
- }
- /* S1->p.p1==S2->p.p2 */
- if (S1->l1 == S2->l2) return 0;
- if (S1->l1 == T2) return 1;
- return -1;
- }
- /* Function seg_cmp returns
- * -1 if S1 HAS TO BE to the right/below S2 to avoid a crossing,
- * 0 if a crossing is unavoidable or there is no crossing at all or
- * the segments are parallel,
- * 1 if S1 HAS TO BE to the left/above S2 to avoid a crossing
- * -2 if S1 and S2 are incomparable
- *
- * Note: This definition means horizontal segments have track numbers
- * increasing as y decreases, while vertical segments have track numbers
- * increasing as x increases. It would be good to make this consistent,
- * with horizontal track numbers increasing with y. This can be done by
- * switching B_DOWN and B_UP in the first call to segCmp. At present,
- * though, I'm not sure what assumptions are made in handling parallel
- * segments, so we leave the code alone for the time being.
- */
- static int
- seg_cmp(segment* S1, segment* S2)
- {
- if(S1->isVert!=S2->isVert||S1->comm_coord!=S2->comm_coord) {
- agerrorf("incomparable segments !! -- Aborting\n");
- return -2;
- }
- if(S1->isVert)
- return segCmp (S1, S2, B_RIGHT, B_LEFT);
- else
- return segCmp (S1, S2, B_DOWN, B_UP);
- }
- static int
- add_edges_in_G(channel* cp)
- {
- seg_list_t *seg_list = &cp->seg_list;
- const size_t size = seg_list_size(&cp->seg_list);
- rawgraph* G = cp->G;
- for (size_t x = 0; x + 1 < size; ++x) {
- for (size_t y = x + 1; y < size; ++y) {
- const int cmp = seg_cmp(seg_list_get(seg_list, x),
- seg_list_get(seg_list, y));
- if (cmp == -2) {
- return -1;
- } else if (cmp > 0) {
- insert_edge(G,x,y);
- } else if (cmp == -1) {
- insert_edge(G,y,x);
- }
- }
- }
- return 0;
- }
- static int
- add_np_edges (Dt_t* chans)
- {
- Dt_t* lp;
- Dtlink_t* l1;
- Dtlink_t* l2;
- channel* cp;
- for (l1 = dtflatten (chans); l1; l1 = dtlink(chans,l1)) {
- lp = ((chanItem*)l1)->chans;
- for (l2 = dtflatten (lp); l2; l2 = dtlink(lp,l2)) {
- cp = (channel*)l2;
- if (!seg_list_is_empty(&cp->seg_list))
- if (add_edges_in_G(cp)) {
- return -1;
- }
- }
- }
- return 0;
- }
- static segment*
- next_seg(segment* seg, int dir)
- {
- assert(seg);
- if (!dir)
- return seg->prev;
- else
- return seg->next;
- }
- /* propagate_prec propagates the precedence relationship along
- * a series of parallel segments on 2 edges
- */
- static int
- propagate_prec(segment* seg, int prec, int hops, int dir)
- {
- int x;
- int ans=prec;
- segment* next;
- segment* current;
- current = seg;
- for(x=1;x<=hops;x++) {
- next = next_seg(current, dir);
- if(!current->isVert) {
- if(next->comm_coord==current->p.p1) {
- if(current->l1==B_UP) ans *= -1;
- }
- else {
- if(current->l2==B_DOWN) ans *= -1;
- }
- }
- else {
- if(next->comm_coord==current->p.p1) {
- if(current->l1==B_RIGHT) ans *= -1;
- }
- else {
- if(current->l2==B_LEFT) ans *= -1;
- }
- }
- current = next;
- }
- return ans;
- }
- static bool
- is_parallel(segment* s1, segment* s2)
- {
- assert (s1->comm_coord==s2->comm_coord);
- return s1->p.p1 == s2->p.p1 &&
- s1->p.p2 == s2->p.p2 &&
- s1->l1 == s2->l1 &&
- s1->l2 == s2->l2;
- }
- /* decide_point returns (through ret) the number of hops needed in the given
- * directions along the 2 edges to get to a deciding point (or NODES) and also
- * puts into prec the appropriate dependency (follows same convention as
- * seg_cmp)
- */
- static int
- decide_point(pair *ret, segment* si, segment* sj, int dir1, int dir2)
- {
- int prec = 0, ans = 0, temp;
- segment* np1;
- segment *np2 = NULL;
-
- while ((np1 = next_seg(si,dir1)) && (np2 = next_seg(sj,dir2)) &&
- is_parallel(np1, np2)) {
- ans++;
- si = np1;
- sj = np2;
- }
- if (!np1)
- prec = 0;
- else if (!np2)
- assert(0); /* FIXME */
- else {
- temp = seg_cmp(np1, np2);
- if (temp == -2) {
- return -1;
- }
- prec = propagate_prec(np1, temp, ans+1, 1-dir1);
- }
-
- ret->a = ans;
- ret->b = prec;
- return 0;
- }
- /* sets the edges for a series of parallel segments along two edges starting
- * from segment i, segment j. It is assumed that the edge should be from
- * segment i to segment j - the dependency is appropriately propagated
- */
- static void
- set_parallel_edges (segment* seg1, segment* seg2, int dir1, int dir2, int hops,
- maze* mp)
- {
- int x;
- channel* chan;
- channel* nchan;
- segment* prev1;
- segment* prev2;
- if (seg1->isVert)
- chan = chanSearch(mp->vchans, seg1);
- else
- chan = chanSearch(mp->hchans, seg1);
- insert_edge(chan->G, seg1->ind_no, seg2->ind_no);
- for (x=1;x<=hops;x++) {
- prev1 = next_seg(seg1, dir1);
- prev2 = next_seg(seg2, dir2);
- if(!seg1->isVert) {
- nchan = chanSearch(mp->vchans, prev1);
- if(prev1->comm_coord==seg1->p.p1) {
- if(seg1->l1==B_UP) {
- if(edge_exists(chan->G, seg1->ind_no, seg2->ind_no))
- insert_edge(nchan->G, prev2->ind_no, prev1->ind_no);
- else
- insert_edge(nchan->G, prev1->ind_no, prev2->ind_no);
- }
- else {
- if(edge_exists(chan->G, seg1->ind_no, seg2->ind_no))
- insert_edge(nchan->G, prev1->ind_no, prev2->ind_no);
- else
- insert_edge(nchan->G, prev2->ind_no, prev1->ind_no);
- }
- }
- else {
- if(seg1->l2==B_UP) {
- if(edge_exists(chan->G, seg1->ind_no, seg2->ind_no))
- insert_edge(nchan->G,prev1->ind_no, prev2->ind_no);
- else
- insert_edge(nchan->G,prev2->ind_no, prev1->ind_no);
- }
- else {
- if(edge_exists(chan->G, seg1->ind_no, seg2->ind_no))
- insert_edge(nchan->G, prev2->ind_no, prev1->ind_no);
- else
- insert_edge(nchan->G, prev1->ind_no, prev2->ind_no);
- }
- }
- }
- else {
- nchan = chanSearch(mp->hchans, prev1);
- if(prev1->comm_coord==seg1->p.p1) {
- if(seg1->l1==B_LEFT) {
- if(edge_exists(chan->G, seg1->ind_no, seg2->ind_no))
- insert_edge(nchan->G, prev1->ind_no, prev2->ind_no);
- else
- insert_edge(nchan->G, prev2->ind_no, prev1->ind_no);
- }
- else {
- if(edge_exists(chan->G, seg1->ind_no, seg2->ind_no))
- insert_edge(nchan->G, prev2->ind_no, prev1->ind_no);
- else
- insert_edge(nchan->G, prev1->ind_no, prev2->ind_no);
- }
- }
- else {
- if(seg1->l2==B_LEFT) {
- if(edge_exists(chan->G, seg1->ind_no, seg2->ind_no))
- insert_edge(nchan->G, prev2->ind_no, prev1->ind_no);
- else
- insert_edge(nchan->G, prev1->ind_no, prev2->ind_no);
- }
- else {
- if(edge_exists(chan->G, seg1->ind_no, seg2->ind_no))
- insert_edge(nchan->G, prev1->ind_no, prev2->ind_no);
- else
- insert_edge(nchan->G, prev2->ind_no, prev1->ind_no);
- }
- }
- }
- chan = nchan;
- seg1 = prev1;
- seg2 = prev2;
- }
- }
- /* removes the edge between segments after the resolution of a conflict
- */
- static void
- removeEdge(segment* seg1, segment* seg2, int dir, maze* mp)
- {
- segment* ptr1;
- segment* ptr2;
- channel* chan;
- ptr1 = seg1;
- ptr2 = seg2;
- while(is_parallel(ptr1, ptr2)) {
- ptr1 = next_seg(ptr1, 1);
- ptr2 = next_seg(ptr2, dir);
- }
- if(ptr1->isVert)
- chan = chanSearch(mp->vchans, ptr1);
- else
- chan = chanSearch(mp->hchans, ptr1);
- remove_redge (chan->G, ptr1->ind_no, ptr2->ind_no);
- }
- static int
- addPEdges (channel* cp, maze* mp)
- {
- /* dir[1,2] are used to figure out whether we should use prev
- * pointers or next pointers -- 0 : decrease, 1 : increase
- */
- int dir;
- /* number of hops along the route to get to the deciding points */
- pair hops;
- /* precedences of the deciding points : same convention as
- * seg_cmp function
- */
- int prec1, prec2;
- pair p;
- rawgraph* G = cp->G;
- seg_list_t *segs = &cp->seg_list;
- for(size_t i = 0; i + 1 < seg_list_size(&cp->seg_list); ++i) {
- for(size_t j = i + 1; j < seg_list_size(&cp->seg_list); ++j) {
- if (!edge_exists(G,i,j) && !edge_exists(G,j,i)) {
- if (is_parallel(seg_list_get(segs, i), seg_list_get(segs, j))) {
- /* get_directions */
- if (seg_list_get(segs, i)->prev == 0) {
- if (seg_list_get(segs, j)->prev == 0)
- dir = 0;
- else
- dir = 1;
- }
- else if (seg_list_get(segs, j)->prev == 0) {
- dir = 1;
- }
- else {
- if (seg_list_get(segs, i)->prev->comm_coord ==
- seg_list_get(segs, j)->prev->comm_coord)
- dir = 0;
- else
- dir = 1;
- }
- if (decide_point(&p, seg_list_get(segs, i), seg_list_get(segs, j), 0, dir)
- != 0) {
- return -1;
- }
- hops.a = p.a;
- prec1 = p.b;
- if (decide_point(&p, seg_list_get(segs, i), seg_list_get(segs, j), 1,
- 1 - dir) != 0) {
- return -1;
- }
- hops.b = p.a;
- prec2 = p.b;
- if (prec1 == -1) {
- set_parallel_edges(seg_list_get(segs, j), seg_list_get(segs, i), dir, 0,
- hops.a, mp);
- set_parallel_edges(seg_list_get(segs, j), seg_list_get(segs, i), 1 - dir, 1,
- hops.b, mp);
- if(prec2==1)
- removeEdge(seg_list_get(segs, i), seg_list_get(segs, j), 1 - dir, mp);
- } else if (prec1 == 0) {
- if (prec2 == -1) {
- set_parallel_edges(seg_list_get(segs, j), seg_list_get(segs, i), dir, 0,
- hops.a, mp);
- set_parallel_edges(seg_list_get(segs, j), seg_list_get(segs, i), 1 - dir,
- 1, hops.b, mp);
- } else if (prec2 == 0) {
- set_parallel_edges(seg_list_get(segs, i), seg_list_get(segs, j), 0, dir,
- hops.a, mp);
- set_parallel_edges(seg_list_get(segs, i), seg_list_get(segs, j), 1,
- 1 - dir, hops.b, mp);
- } else if (prec2 == 1) {
- set_parallel_edges(seg_list_get(segs, i), seg_list_get(segs, j), 0, dir,
- hops.a, mp);
- set_parallel_edges(seg_list_get(segs, i), seg_list_get(segs, j), 1,
- 1 - dir, hops.b, mp);
- }
- } else if (prec1 == 1) {
- set_parallel_edges(seg_list_get(segs, i), seg_list_get(segs, j), 0, dir,
- hops.a, mp);
- set_parallel_edges(seg_list_get(segs, i), seg_list_get(segs, j), 1, 1 - dir,
- hops.b, mp);
- if(prec2==-1)
- removeEdge(seg_list_get(segs, i), seg_list_get(segs, j), 1 - dir, mp);
- }
- }
- }
- }
- }
- return 0;
- }
- static int
- add_p_edges (Dt_t* chans, maze* mp)
- {
- Dt_t* lp;
- Dtlink_t* l1;
- Dtlink_t* l2;
- for (l1 = dtflatten (chans); l1; l1 = dtlink(chans,l1)) {
- lp = ((chanItem*)l1)->chans;
- for (l2 = dtflatten (lp); l2; l2 = dtlink(lp,l2)) {
- if (addPEdges((channel*)l2, mp) != 0) {
- return -1;
- }
- }
- }
- return 0;
- }
- static int
- assignTracks (maze* mp)
- {
- /* Create the graphs for each channel */
- create_graphs(mp->hchans);
- create_graphs(mp->vchans);
- /* add edges between non-parallel segments */
- if (add_np_edges(mp->hchans) != 0) {
- return -1;
- }
- if (add_np_edges(mp->vchans) != 0) {
- return -1;
- }
- /* add edges between parallel segments + remove appropriate edges */
- if (add_p_edges(mp->hchans, mp) != 0) {
- return -1;
- }
- if (add_p_edges(mp->vchans, mp) != 0) {
- return -1;
- }
- /* Assign the tracks after a top sort */
- assignTrackNo (mp->hchans);
- assignTrackNo (mp->vchans);
- return 0;
- }
- static double
- vtrack (segment* seg, maze* m)
- {
- channel* chp = chanSearch(m->vchans, seg);
- const double f = (double)seg->track_no / ((double)seg_list_size(&chp->seg_list) + 1);
- double left = chp->cp->bb.LL.x;
- double right = chp->cp->bb.UR.x;
- return left + f*(right-left);
- }
- static double htrack(segment *seg, maze *m) {
- channel* chp = chanSearch(m->hchans, seg);
- double f = 1.0 - (double)seg->track_no / ((double)seg_list_size(&chp->seg_list) + 1);
- double lo = chp->cp->bb.LL.y;
- double hi = chp->cp->bb.UR.y;
- return round(lo + f * (hi - lo));
- }
- static pointf
- addPoints(pointf p0, pointf p1)
- {
- p0.x += p1.x;
- p0.y += p1.y;
- return p0;
- }
- static void attachOrthoEdges(maze *mp, size_t n_edges, route* route_list,
- splineInfo *sinfo, epair_t es[], bool doLbls) {
- int ipt;
- pointf* ispline = 0;
- size_t splsz = 0;
- pointf p, p1, q1;
- route rte;
- segment* seg;
- Agedge_t* e;
- textlabel_t* lbl;
- for (size_t irte = 0; irte < n_edges; irte++) {
- e = es[irte].e;
- p1 = addPoints(ND_coord(agtail(e)), ED_tail_port(e).p);
- q1 = addPoints(ND_coord(aghead(e)), ED_head_port(e).p);
- rte = route_list[irte];
- size_t npts = 1 + 3*rte.n;
- if (npts > splsz) {
- free (ispline);
- ispline = gv_calloc(npts, sizeof(pointf));
- splsz = npts;
- }
-
- seg = rte.segs;
- if (seg->isVert) {
- p.x = vtrack(seg, mp);
- p.y = p1.y;
- }
- else {
- p.y = htrack(seg, mp);
- p.x = p1.x;
- }
- ispline[0] = ispline[1] = p;
- ipt = 2;
- for (size_t i = 1;i<rte.n;i++) {
- seg = rte.segs+i;
- if (seg->isVert)
- p.x = vtrack(seg, mp);
- else
- p.y = htrack(seg, mp);
- ispline[ipt+2] = ispline[ipt+1] = ispline[ipt] = p;
- ipt += 3;
- }
- if (seg->isVert) {
- p.x = vtrack(seg, mp);
- p.y = q1.y;
- }
- else {
- p.y = htrack(seg, mp);
- p.x = q1.x;
- }
- ispline[ipt] = ispline[ipt+1] = p;
- if (Verbose > 1)
- fprintf(stderr, "ortho %s %s\n", agnameof(agtail(e)),agnameof(aghead(e)));
- clip_and_install(e, aghead(e), ispline, npts, sinfo);
- if (doLbls && (lbl = ED_label(e)) && !lbl->set)
- addEdgeLabels(e);
- }
- free(ispline);
- }
- static int
- edgeLen (Agedge_t* e)
- {
- pointf p = ND_coord(agtail(e));
- pointf q = ND_coord(aghead(e));
- return (int)DIST2(p,q);
- }
- static int edgecmp(const void *x, const void *y) {
- const epair_t *e0 = x;
- const epair_t *e1 = y;
- if (e0->d > e1->d) {
- return 1;
- }
- if (e0->d < e1->d) {
- return -1;
- }
- return 0;
- }
- static bool spline_merge(node_t * n)
- {
- (void)n;
- return false;
- }
- static bool swap_ends_p(edge_t * e)
- {
- (void)e;
- return false;
- }
- static splineInfo sinfo = { swap_ends_p, spline_merge, true, true };
- /* orthoEdges:
- * For edges without position information, construct an orthogonal routing.
- * If useLbls is true, use edge label info when available to guide routing,
- * and set label pos for those edges for which this info is not available.
- */
- void orthoEdges(Agraph_t *g, bool useLbls) {
- sgraph* sg;
- maze* mp;
- route* route_list;
- int gstart;
- Agnode_t* n;
- Agedge_t* e;
- snode* sn;
- snode* dn;
- epair_t* es = gv_calloc(agnedges(g), sizeof(epair_t));
- cell* start;
- cell* dest;
- PointSet* ps = NULL;
- textlabel_t* lbl;
- if (Concentrate)
- ps = newPS();
- #ifdef DEBUG
- {
- char* s = agget(g, "odb");
- char c;
- odb_flags = 0;
- if (s && *s != '\0') {
- while ((c = *s++)) {
- switch (c) {
- case 'c' :
- odb_flags |= ODB_CHANG; // emit channel graph
- break;
- case 'i' :
- odb_flags |= (ODB_SGRAPH|ODB_IGRAPH); // emit search graphs
- break;
- case 'm' :
- odb_flags |= ODB_MAZE; // emit maze
- break;
- case 'r' :
- odb_flags |= ODB_ROUTE; // emit routes in maze
- break;
- case 's' :
- odb_flags |= ODB_SGRAPH; // emit search graph
- break;
- default:
- break;
- }
- }
- }
- }
- #endif
- if (useLbls) {
- agwarningf("Orthogonal edges do not currently handle edge labels. Try using xlabels.\n");
- useLbls = false;
- }
- mp = mkMaze(g);
- sg = mp->sg;
- #ifdef DEBUG
- if (odb_flags & ODB_SGRAPH) emitSearchGraph (stderr, sg);
- #endif
- /* store edges to be routed in es, along with their lengths */
- size_t n_edges = 0;
- for (n = agfstnode (g); n; n = agnxtnode(g, n)) {
- for (e = agfstout(g, n); e; e = agnxtout(g,e)) {
- if (Nop == 2 && ED_spl(e)) continue;
- if (Concentrate) {
- int ti = AGSEQ(agtail(e));
- int hi = AGSEQ(aghead(e));
- if (ti <= hi) {
- if (isInPS (ps,ti,hi)) continue;
- else addPS (ps,ti,hi);
- }
- else {
- if (isInPS (ps,hi,ti)) continue;
- else addPS (ps,hi,ti);
- }
- }
- es[n_edges].e = e;
- es[n_edges].d = edgeLen (e);
- n_edges++;
- }
- }
- route_list = gv_calloc(n_edges, sizeof(route));
- qsort(es, n_edges, sizeof(epair_t), edgecmp);
- gstart = sg->nnodes;
- PQgen (sg->nnodes+2);
- sn = &sg->nodes[gstart];
- dn = &sg->nodes[gstart+1];
- for (size_t i = 0; i < n_edges; i++) {
- #ifdef DEBUG
- if (i > 0 && (odb_flags & ODB_IGRAPH)) emitSearchGraph (stderr, sg);
- #endif
- e = es[i].e;
- start = CELL(agtail(e));
- dest = CELL(aghead(e));
- if (useLbls && (lbl = ED_label(e)) && lbl->set) {
- }
- else {
- if (start == dest)
- addLoop (sg, start, dn, sn);
- else {
- addNodeEdges (sg, dest, dn);
- addNodeEdges (sg, start, sn);
- }
- if (shortPath (sg, dn, sn)) goto orthofinish;
- }
-
- route_list[i] = convertSPtoRoute(sg, sn, dn);
- reset (sg);
- }
- PQfree ();
- mp->hchans = extractHChans (mp);
- mp->vchans = extractVChans (mp);
- assignSegs (n_edges, route_list, mp);
- if (assignTracks(mp) != 0)
- goto orthofinish;
- #ifdef DEBUG
- if (odb_flags & ODB_ROUTE) emitGraph (stderr, mp, n_edges, route_list, es);
- #endif
- attachOrthoEdges(mp, n_edges, route_list, &sinfo, es, useLbls);
- orthofinish:
- if (Concentrate)
- freePS (ps);
- for (size_t i=0; i < n_edges; i++)
- free (route_list[i].segs);
- free (route_list);
- freeMaze (mp);
- free (es);
- }
- #include <common/arith.h>
- #define TRANS 10
- static char* prolog2 =
- "%%!PS-Adobe-2.0\n\
- %%%%BoundingBox: (atend)\n\
- /point {\n\
- /Y exch def\n\
- /X exch def\n\
- newpath\n\
- X Y 3 0 360 arc fill\n\
- } def\n\
- /cell {\n\
- /Y exch def\n\
- /X exch def\n\
- /y exch def\n\
- /x exch def\n\
- newpath\n\
- x y moveto\n\
- x Y lineto\n\
- X Y lineto\n\
- X y lineto\n\
- closepath stroke\n\
- } def\n\
- /node {\n\
- /u exch def\n\
- /r exch def\n\
- /d exch def\n\
- /l exch def\n\
- newpath l d moveto\n\
- r d lineto r u lineto l u lineto\n\
- closepath fill\n\
- } def\n\
- \n";
- static char* epilog2 =
- "showpage\n\
- %%%%Trailer\n\
- %%%%BoundingBox: %.f %.f %.f %.f\n";
- static pointf coordOf(cell *cp, snode *np) {
- pointf p;
- if (cp->sides[M_TOP] == np) {
- p.x = (cp->bb.LL.x + cp->bb.UR.x)/2;
- p.y = cp->bb.UR.y;
- }
- else if (cp->sides[M_BOTTOM] == np) {
- p.x = (cp->bb.LL.x + cp->bb.UR.x)/2;
- p.y = cp->bb.LL.y;
- }
- else if (cp->sides[M_LEFT] == np) {
- p.y = (cp->bb.LL.y + cp->bb.UR.y)/2;
- p.x = cp->bb.LL.x;
- }
- else if (cp->sides[M_RIGHT] == np) {
- p.y = (cp->bb.LL.y + cp->bb.UR.y)/2;
- p.x = cp->bb.UR.x;
- }
- else {
- agerrorf("Node not adjacent to cell -- Aborting\n");
- graphviz_exit(EXIT_FAILURE);
- }
- return p;
- }
- static boxf
- emitEdge (FILE* fp, Agedge_t* e, route rte, maze* m, boxf bb)
- {
- double x, y;
- boxf n = CELL(agtail(e))->bb;
- segment* seg = rte.segs;
- if (seg->isVert) {
- x = vtrack(seg, m);
- y = (n.UR.y + n.LL.y)/2;
- }
- else {
- y = htrack(seg, m);
- x = (n.UR.x + n.LL.x)/2;
- }
- bb.LL.x = fmin(bb.LL.x, SC * x);
- bb.LL.y = fmin(bb.LL.y, SC * y);
- bb.UR.x = fmax(bb.UR.x, SC * x);
- bb.UR.y = fmax(bb.UR.y, SC * y);
- fprintf(fp, "newpath %.0f %.0f moveto\n", SC * x, SC * y);
- for (size_t i = 1;i<rte.n;i++) {
- seg = rte.segs+i;
- if (seg->isVert) {
- x = vtrack(seg, m);
- }
- else {
- y = htrack(seg, m);
- }
- bb.LL.x = fmin(bb.LL.x, SC * x);
- bb.LL.y = fmin(bb.LL.y, SC * y);
- bb.UR.x = fmax(bb.UR.x, SC * x);
- bb.UR.y = fmax(bb.UR.y, SC * y);
- fprintf(fp, "%.0f %.0f lineto\n", SC * x, SC * y);
- }
- n = CELL(aghead(e))->bb;
- if (seg->isVert) {
- x = vtrack(seg, m);
- y = (n.UR.y + n.LL.y)/2;
- }
- else {
- y = htrack(seg, m);
- x = (n.LL.x + n.UR.x)/2;
- }
- bb.LL.x = fmin(bb.LL.x, SC * x);
- bb.LL.y = fmin(bb.LL.y, SC * y);
- bb.UR.x = fmax(bb.UR.x, SC * x);
- bb.UR.y = fmax(bb.UR.y, SC * y);
- fprintf(fp, "%.0f %.0f lineto stroke\n", SC * x, SC * y);
- return bb;
- }
- /**
- * @brief dumps in dot format @ref snode::cells and @ref edges of
- * @ref sgraph for debugging
- *
- * The routine uses coordinates of @ref cells calculated
- * from @ref gcells.
- * Coordinates of @ref gcellg are calculated by original
- * specified graph layout engine.
- */
- static DEBUG_FN void emitSearchGraph(FILE *fp, sgraph *sg) {
- cell* cp;
- snode* np;
- sedge* ep;
- pointf p;
- int i;
- fputs ("graph G {\n", fp);
- fputs (" node[shape=point]\n", fp);
- fputs (" layout=neato\n", fp);
- for (i = 0; i < sg->nnodes; i++) {
- np = sg->nodes+i;
- cp = np->cells[0];
- if (cp == np->cells[1]) {
- p = midPt(cp);
- }
- else {
- if (IsNode(cp)) cp = np->cells[1];
- p = coordOf (cp, np);
- }
- fprintf (fp, " %d [pos=\"%.0f,%.0f!\"]\n", i, p.x, p.y);
- }
- for (i = 0; i < sg->nedges; i++) {
- ep = sg->edges+i;
- fprintf (fp, " %d -- %d[label=\"%f\"]\n", ep->v1, ep->v2, ep->weight);
- }
- fputs ("}\n", fp);
- }
- static DEBUG_FN void emitGraph(FILE *fp, maze *mp, size_t n_edges,
- route *route_list, epair_t es[]) {
- boxf absbb = {.LL = {.x = DBL_MAX, .y = DBL_MAX},
- .UR = {.x = -DBL_MAX, .y = -DBL_MAX}};
- fprintf (fp, "%s", prolog2);
- fprintf (fp, "%d %d translate\n", TRANS, TRANS);
- fputs ("0 0 1 setrgbcolor\n", fp);
- for (int i = 0; i < mp->ngcells; i++) {
- const boxf bb = mp->gcells[i].bb;
- fprintf (fp, "%f %f %f %f node\n", bb.LL.x, bb.LL.y, bb.UR.x, bb.UR.y);
- }
- for (size_t i = 0; i < n_edges; i++) {
- absbb = emitEdge (fp, es[i].e, route_list[i], mp, absbb);
- }
-
- fputs ("0.8 0.8 0.8 setrgbcolor\n", fp);
- for (int i = 0; i < mp->ncells; i++) {
- const boxf bb = mp->cells[i].bb;
- fprintf (fp, "%f %f %f %f cell\n", bb.LL.x, bb.LL.y, bb.UR.x, bb.UR.y);
- absbb.LL.x = fmin(absbb.LL.x, bb.LL.x);
- absbb.LL.y = fmin(absbb.LL.y, bb.LL.y);
- absbb.UR.x = fmax(absbb.UR.x, bb.UR.x);
- absbb.UR.y = fmax(absbb.UR.y, bb.UR.y);
- }
- const boxf bbox = {
- .LL = {.x = absbb.LL.x + TRANS,
- .y = absbb.LL.y + TRANS},
- .UR = {.x = absbb.UR.x + TRANS,
- .y = absbb.UR.y + TRANS}};
- fprintf (fp, epilog2, bbox.LL.x, bbox.LL.y, bbox.UR.x, bbox.UR.y);
- }
|