]> granicus.if.org Git - graphviz/commitdiff
Add the vpsc library for IPSEPCOLA features
authorerg <devnull@localhost>
Fri, 28 Apr 2006 19:03:46 +0000 (19:03 +0000)
committererg <devnull@localhost>
Fri, 28 Apr 2006 19:03:46 +0000 (19:03 +0000)
lib/vpsc/constraint.h [new file with mode: 0644]
lib/vpsc/csolve_VPSC.cpp [new file with mode: 0644]
lib/vpsc/csolve_VPSC.h [new file with mode: 0644]
lib/vpsc/generate-constraints.cpp [new file with mode: 0644]
lib/vpsc/generate-constraints.h [new file with mode: 0644]

diff --git a/lib/vpsc/constraint.h b/lib/vpsc/constraint.h
new file mode 100644 (file)
index 0000000..00c0076
--- /dev/null
@@ -0,0 +1,64 @@
+/**
+ * \brief A constraint determines a minimum or exact spacing required between
+ * two variables.
+ *
+ * Authors:
+ *   Tim Dwyer <tgdwyer@gmail.com>
+ *
+ * Copyright (C) 2005 Authors
+ *
+ * This version is released under the CPL (Common Public License) with
+ * the Graphviz distribution.
+ * A version is also available under the LGPL as part of the Adaptagrams
+ * project: http://sourceforge.net/projects/adaptagrams.  
+ * If you make improvements or bug fixes to this code it would be much
+ * appreciated if you could also contribute those changes back to the
+ * Adaptagrams repository.
+ */
+
+#ifndef SEEN_REMOVEOVERLAP_CONSTRAINT_H
+#define SEEN_REMOVEOVERLAP_CONSTRAINT_H
+
+#include <iostream>
+#include "variable.h"
+
+class Constraint
+{
+       friend std::ostream& operator <<(std::ostream &os,const Constraint &c);
+public:
+       Variable *left;
+       Variable *right;
+       double gap;
+       double lm;
+       Constraint(Variable *left, Variable *right, double gap, bool equality=false);
+       ~Constraint();
+       inline double Constraint::slack() const { return right->position() - gap - left->position(); }
+       long timeStamp;
+       bool active;
+       bool visited;
+       bool equality;
+};
+#include <float.h>
+#include "block.h"
+static inline bool compareConstraints(Constraint *const &l, Constraint *const &r) {
+       double const sl = 
+               l->left->block->timeStamp > l->timeStamp
+               ||l->left->block==l->right->block
+               ?-DBL_MAX:l->slack();
+       double const sr = 
+               r->left->block->timeStamp > r->timeStamp
+               ||r->left->block==r->right->block
+               ?-DBL_MAX:r->slack();
+       if(sl==sr) {
+               // arbitrary choice based on id
+               if(l->left->id==r->left->id) {
+                       if(l->right->id<r->right->id) return true;
+                       return false;
+               }
+               if(l->left->id<r->left->id) return true;
+               return false;
+       }
+       return sl < sr;
+}
+
+#endif // SEEN_REMOVEOVERLAP_CONSTRAINT_H
diff --git a/lib/vpsc/csolve_VPSC.cpp b/lib/vpsc/csolve_VPSC.cpp
new file mode 100644 (file)
index 0000000..c838011
--- /dev/null
@@ -0,0 +1,130 @@
+/**
+ * \brief Bridge for C programs to access solve_VPSC (which is in C++)
+ *
+ * Authors:
+ *   Tim Dwyer <tgdwyer@gmail.com>
+ *
+ * Copyright (C) 2005 Authors
+ *
+ * This version is released under the CPL (Common Public License) with
+ * the Graphviz distribution.
+ * A version is also available under the LGPL as part of the Adaptagrams
+ * project: http://sourceforge.net/projects/adaptagrams.  
+ * If you make improvements or bug fixes to this code it would be much
+ * appreciated if you could also contribute those changes back to the
+ * Adaptagrams repository.
+ */
+
+#include <iostream>
+#include <variable.h>
+#include <constraint.h>
+#include <generate-constraints.h>
+#include <solve_VPSC.h>
+#include "csolve_VPSC.h"
+extern "C" {
+Variable* newVariable(int id, double desiredPos, double weight) {
+       return new Variable(id,desiredPos,weight);
+}
+Constraint* newConstraint(Variable* left, Variable* right, double gap) {
+       return new Constraint(left,right,gap);
+}
+VPSC* newVPSC(int n, Variable* vs[], int m, Constraint* cs[]) {
+       return new VPSC(n,vs,m,cs);
+}
+VPSC* newIncVPSC(int n, Variable* vs[], int m, Constraint* cs[]) {
+       return (VPSC*)new IncVPSC(n,vs,m,cs);
+}
+
+int genXConstraints(int n, boxf* bb, Variable** vs, Constraint*** cs,int transitiveClosure) {
+       Rectangle* rs[n];
+       for(int i=0;i<n;i++) {
+               rs[i]=new Rectangle(bb[i].LL.x,bb[i].UR.x,bb[i].LL.y,bb[i].UR.y);
+       }
+       int m = generateXConstraints(n,rs,vs,*cs,transitiveClosure);
+       for(int i=0;i<n;i++) {
+               delete rs[i];
+       }
+       return m;
+}
+int genYConstraints(int n, boxf* bb, Variable** vs, Constraint*** cs) {
+       Rectangle* rs[n];
+       for(int i=0;i<n;i++) {
+               rs[i]=new Rectangle(bb[i].LL.x,bb[i].UR.x,bb[i].LL.y,bb[i].UR.y);
+       }
+       int m = generateYConstraints(n,rs,vs,*cs);
+       for(int i=0;i<n;i++) {
+               delete rs[i];
+       }
+       return m;
+}
+
+Constraint** newConstraints(int m) {
+       return new Constraint*[m];
+}
+void deleteConstraints(int m, Constraint **cs) {
+       for(int i=0;i<m;i++) {
+               delete cs[i];
+       }
+       delete [] cs;
+}
+void deleteConstraint(Constraint* c) {
+       delete c;
+}
+void deleteVariable(Variable* v) {
+       delete v;
+}
+void satisfyVPSC(VPSC* vpsc) {
+       try {
+               vpsc->satisfy();
+       } catch(const char *e) {
+               std::cerr << e << std::endl;
+               exit(1);
+       }
+}
+int getSplitCnt(IncVPSC *vpsc) {
+       return vpsc->splitCnt;
+}
+void deleteVPSC(VPSC *vpsc) {
+       assert(vpsc!=NULL);
+       delete vpsc;
+}
+void solveVPSC(VPSC* vpsc) {
+       vpsc->solve();
+}
+void splitIncVPSC(IncVPSC* vpsc) {
+       vpsc->splitBlocks();
+}
+void setVariableDesiredPos(Variable *v, double desiredPos) {
+       v->desiredPosition = desiredPos;
+}
+double getVariablePos(Variable *v) {
+       return v->position();
+}
+void remapInConstraints(Variable *u, Variable *v, double dgap) {
+       for(Constraints::iterator i=u->in.begin();i!=u->in.end();i++) {
+               Constraint* c=*i;       
+               c->right=v;
+               c->gap+=dgap;
+               v->in.push_back(c);
+       }
+       u->in.clear();
+}
+void remapOutConstraints(Variable *u, Variable *v, double dgap) {
+       for(Constraints::iterator i=u->out.begin();i!=u->out.end();i++) {
+               Constraint* c=*i;       
+               c->left=v;
+               c->gap+=dgap;
+               v->out.push_back(c);
+       }
+       u->out.clear();
+}
+int getLeftVarID(Constraint *c) {
+       return c->left->id;
+}
+int getRightVarID(Constraint *c){
+       return c->right->id;
+}
+double getSeparation(Constraint *c){
+       return c->gap;
+}
+}
diff --git a/lib/vpsc/csolve_VPSC.h b/lib/vpsc/csolve_VPSC.h
new file mode 100644 (file)
index 0000000..1d84382
--- /dev/null
@@ -0,0 +1,60 @@
+/**
+ * \brief Bridge for C programs to access solve_VPSC (which is in C++)
+ *
+ * Authors:
+ *   Tim Dwyer <tgdwyer@gmail.com>
+ *
+ * Copyright (C) 2005 Authors
+ *
+ * This version is released under the CPL (Common Public License) with
+ * the Graphviz distribution.
+ * A version is also available under the LGPL as part of the Adaptagrams
+ * project: http://sourceforge.net/projects/adaptagrams.  
+ * If you make improvements or bug fixes to this code it would be much
+ * appreciated if you could also contribute those changes back to the
+ * Adaptagrams repository.
+ */
+#ifndef _CSOLVE_VPSC_H_
+#define _CSOLVE_VPSC_H_
+#ifdef __cplusplus
+extern "C" {
+#endif
+typedef struct Variable Variable;
+Variable* newVariable(int id, double desiredPos, double weight);
+void setVariableDesiredPos(Variable *, double desiredPos);
+double getVariablePos(Variable*);
+
+typedef struct Constraint Constraint;
+Constraint* newConstraint(Variable* left, Variable* right, double gap);
+
+typedef struct VPSC VPSC;
+VPSC* newVPSC(int n, Variable* vs[], int m, Constraint* cs[]);
+void deleteVPSC(VPSC*);
+void deleteConstraint(Constraint*);
+void deleteVariable(Variable*);
+Constraint** newConstraints(int m);
+void deleteConstraints(int m,Constraint**);
+void remapInConstraints(Variable *u, Variable *v, double dgap);
+void remapOutConstraints(Variable *u, Variable *v, double dgap);
+int getLeftVarID(Constraint *c);
+int getRightVarID(Constraint *c);
+double getSeparation(Constraint *c);
+
+#ifndef HAVE_POINTF_S
+typedef struct pointf_s { double x, y; } pointf;
+typedef struct { pointf LL, UR; } boxf;
+#endif
+int genXConstraints(int n, boxf[], Variable** vs, Constraint*** cs,
+               int transitiveClosure);
+int genYConstraints(int n, boxf[], Variable** vs, Constraint*** cs);
+
+void satisfyVPSC(VPSC*);
+void solveVPSC(VPSC*);
+typedef struct IncVPSC IncVPSC;
+VPSC* newIncVPSC(int n, Variable* vs[], int m, Constraint* cs[]);
+void splitIncVPSC(IncVPSC*);
+int getSplitCnt(IncVPSC *vpsc);
+#ifdef __cplusplus
+}
+#endif
+#endif /* _CSOLVE_VPSC_H_ */
diff --git a/lib/vpsc/generate-constraints.cpp b/lib/vpsc/generate-constraints.cpp
new file mode 100644 (file)
index 0000000..a2a69fb
--- /dev/null
@@ -0,0 +1,282 @@
+/**
+ * \brief Functions to automatically generate constraints for the rectangular
+ * node overlap removal problem.
+ *
+ * Authors:
+ *   Tim Dwyer <tgdwyer@gmail.com>
+ *
+ * Copyright (C) 2005 Authors
+ *
+ * This version is released under the CPL (Common Public License) with
+ * the Graphviz distribution.
+ * A version is also available under the LGPL as part of the Adaptagrams
+ * project: http://sourceforge.net/projects/adaptagrams.  
+ * If you make improvements or bug fixes to this code it would be much
+ * appreciated if you could also contribute those changes back to the
+ * Adaptagrams repository.
+ */
+
+#include <set>
+#include <cassert>
+#include "generate-constraints.h"
+#include "constraint.h"
+
+using std::set;
+using std::vector;
+
+std::ostream& operator <<(std::ostream &os, const Rectangle &r) {
+       os << "{"<<r.minX<<","<<r.maxX<<","<<r.minY<<","<<r.maxY<<"},";
+       return os;
+}
+Rectangle::Rectangle(double x, double X, double y, double Y) 
+: minX(x),maxX(X),minY(y),maxY(Y) {
+               assert(x<=X);
+               assert(y<=Y);
+}
+
+struct Node;
+struct CmpNodePos { bool operator()(const Node* u, const Node* v) const; };
+
+typedef set<Node*,CmpNodePos> NodeSet;
+
+struct Node {
+       Variable *v;
+       Rectangle *r;
+       double pos;
+       Node *firstAbove, *firstBelow;
+       NodeSet *leftNeighbours, *rightNeighbours;
+       Node(Variable *v, Rectangle *r, double p) : v(v),r(r),pos(p) {
+               firstAbove=firstBelow=NULL;
+               leftNeighbours=rightNeighbours=NULL;
+               assert(r->width()<1e40);
+       }
+       ~Node() {
+               delete leftNeighbours;
+               delete rightNeighbours;
+       }
+       void addLeftNeighbour(Node *u) {
+               leftNeighbours->insert(u);
+       }
+       void addRightNeighbour(Node *u) {
+               rightNeighbours->insert(u);
+       }
+       void setNeighbours(NodeSet *left, NodeSet *right) {
+               leftNeighbours=left;
+               rightNeighbours=right;
+               for(NodeSet::iterator i=left->begin();i!=left->end();i++) {
+                       Node *v=*(i);
+                       v->addRightNeighbour(this);
+               }
+               for(NodeSet::iterator i=right->begin();i!=right->end();i++) {
+                       Node *v=*(i);
+                       v->addLeftNeighbour(this);
+               }
+       }
+};
+bool CmpNodePos::operator() (const Node* u, const Node* v) const {
+       if (u->pos < v->pos) {
+               return true;
+       }
+       if (v->pos < u->pos) {
+               return false;
+       }
+       return u < v;
+}
+
+NodeSet* getLeftNeighbours(NodeSet &scanline,Node *v) {
+       NodeSet *leftv = new NodeSet;
+       NodeSet::iterator i=scanline.find(v);
+       while(i--!=scanline.begin()) {
+               Node *u=*(i);
+               if(u->r->overlapX(v->r)<=0) {
+                       leftv->insert(u);
+                       return leftv;
+               }
+               if(u->r->overlapX(v->r)<=u->r->overlapY(v->r)) {
+                       leftv->insert(u);
+               }
+       }
+       return leftv;
+}
+NodeSet* getRightNeighbours(NodeSet &scanline,Node *v) {
+       NodeSet *rightv = new NodeSet;
+       NodeSet::iterator i=scanline.find(v);
+       for(i++;i!=scanline.end(); i++) {
+               Node *u=*(i);
+               if(u->r->overlapX(v->r)<=0) {
+                       rightv->insert(u);
+                       return rightv;
+               }
+               if(u->r->overlapX(v->r)<=u->r->overlapY(v->r)) {
+                       rightv->insert(u);
+               }
+       }
+       return rightv;
+}
+
+typedef enum {Open, Close} EventType;
+struct Event {
+       EventType type;
+       Node *v;
+       double pos;
+       Event(EventType t, Node *v, double p) : type(t),v(v),pos(p) {};
+};
+Event **events;
+int compare_events(const void *a, const void *b) {
+       Event *ea=*(Event**)a;
+       Event *eb=*(Event**)b;
+       if(ea->v->r==eb->v->r) {
+               // when comparing opening and closing from the same rect
+               // open must come first
+               if(ea->type==Open) return -1;
+               return 1;
+       } else if(ea->pos > eb->pos) {
+               return 1;
+       } else if(ea->pos < eb->pos) {
+               return -1;
+       }
+       return 0;
+}
+
+/**
+ * Prepares constraints in order to apply VPSC horizontally.  Assumes variables have already been created.
+ * useNeighbourLists determines whether or not a heuristic is used to deciding whether to resolve
+ * all overlap in the x pass, or leave some overlaps for the y pass.
+ */
+int generateXConstraints(const int n, Rectangle** rs, Variable** vars, Constraint** &cs, const bool useNeighbourLists) {
+       events=new Event*[2*n];
+       int i,m,ctr=0;
+       for(i=0;i<n;i++) {
+               vars[i]->desiredPosition=rs[i]->getCentreX();
+               Node *v = new Node(vars[i],rs[i],rs[i]->getCentreX());
+               events[ctr++]=new Event(Open,v,rs[i]->getMinY());
+               events[ctr++]=new Event(Close,v,rs[i]->getMaxY());
+       }
+       qsort((Event*)events, (size_t)2*n, sizeof(Event*), compare_events );
+
+       NodeSet scanline;
+       vector<Constraint*> constraints;
+       for(i=0;i<2*n;i++) {
+               Event *e=events[i];
+               Node *v=e->v;
+               if(e->type==Open) {
+                       scanline.insert(v);
+                       if(useNeighbourLists) {
+                               v->setNeighbours(
+                                       getLeftNeighbours(scanline,v),
+                                       getRightNeighbours(scanline,v)
+                               );
+                       } else {
+                               NodeSet::iterator it=scanline.find(v);
+                               if(it--!=scanline.begin()) {
+                                       Node *u=*it;
+                                       v->firstAbove=u;
+                                       u->firstBelow=v;
+                               }
+                               it=scanline.find(v);
+                               if(++it!=scanline.end()) {
+                                       Node *u=*it;
+                                       v->firstBelow=u;
+                                       u->firstAbove=v;
+                               }
+                       }
+               } else {
+                       // Close event
+                       int r;
+                       if(useNeighbourLists) {
+                               for(NodeSet::iterator i=v->leftNeighbours->begin();
+                                       i!=v->leftNeighbours->end();i++
+                               ) {
+                                       Node *u=*i;
+                                       double sep = (v->r->width()+u->r->width())/2.0;
+                                       constraints.push_back(new Constraint(u->v,v->v,sep));
+                                       r=u->rightNeighbours->erase(v);
+                               }
+                               
+                               for(NodeSet::iterator i=v->rightNeighbours->begin();
+                                       i!=v->rightNeighbours->end();i++
+                               ) {
+                                       Node *u=*i;
+                                       double sep = (v->r->width()+u->r->width())/2.0;
+                                       constraints.push_back(new Constraint(v->v,u->v,sep));
+                                       r=u->leftNeighbours->erase(v);
+                               }
+                       } else {
+                               Node *l=v->firstAbove, *r=v->firstBelow;
+                               if(l!=NULL) {
+                                       double sep = (v->r->width()+l->r->width())/2.0;
+                                       constraints.push_back(new Constraint(l->v,v->v,sep));
+                                       l->firstBelow=v->firstBelow;
+                               }
+                               if(r!=NULL) {
+                                       double sep = (v->r->width()+r->r->width())/2.0;
+                                       constraints.push_back(new Constraint(v->v,r->v,sep));
+                                       r->firstAbove=v->firstAbove;
+                               }
+                       }
+                       r=scanline.erase(v);
+                       delete v;
+               }
+               delete e;
+       }
+       delete [] events;
+       cs=new Constraint*[m=constraints.size()];
+       for(i=0;i<m;i++) cs[i]=constraints[i];
+       return m;
+}
+
+/**
+ * Prepares constraints in order to apply VPSC vertically to remove ALL overlap.
+ */
+int generateYConstraints(const int n, Rectangle** rs, Variable** vars, Constraint** &cs) {
+       events=new Event*[2*n];
+       int ctr=0,i,m;
+       for(i=0;i<n;i++) {
+               vars[i]->desiredPosition=rs[i]->getCentreY();
+               Node *v = new Node(vars[i],rs[i],rs[i]->getCentreY());
+               events[ctr++]=new Event(Open,v,rs[i]->getMinX());
+               events[ctr++]=new Event(Close,v,rs[i]->getMaxX());
+       }
+       qsort((Event*)events, (size_t)2*n, sizeof(Event*), compare_events );
+       NodeSet scanline;
+       vector<Constraint*> constraints;
+       for(i=0;i<2*n;i++) {
+               Event *e=events[i];
+               Node *v=e->v;
+               if(e->type==Open) {
+                       scanline.insert(v);
+                       NodeSet::iterator i=scanline.find(v);
+                       if(i--!=scanline.begin()) {
+                               Node *u=*i;
+                               v->firstAbove=u;
+                               u->firstBelow=v;
+                       }
+                       i=scanline.find(v);
+                       if(++i!=scanline.end())  {
+                               Node *u=*i;
+                               v->firstBelow=u;
+                               u->firstAbove=v;
+                       }
+               } else {
+                       // Close event
+                       Node *l=v->firstAbove, *r=v->firstBelow;
+                       if(l!=NULL) {
+                               double sep = (v->r->height()+l->r->height())/2.0;
+                               constraints.push_back(new Constraint(l->v,v->v,sep));
+                               l->firstBelow=v->firstBelow;
+                       }
+                       if(r!=NULL) {
+                               double sep = (v->r->height()+r->r->height())/2.0;
+                               constraints.push_back(new Constraint(v->v,r->v,sep));
+                               r->firstAbove=v->firstAbove;
+                       }
+                       scanline.erase(v);
+                       delete v;
+               }
+               delete e;
+       }
+       delete [] events;
+       cs=new Constraint*[m=constraints.size()];
+       for(i=0;i<m;i++) cs[i]=constraints[i];
+       return m;
+}
diff --git a/lib/vpsc/generate-constraints.h b/lib/vpsc/generate-constraints.h
new file mode 100644 (file)
index 0000000..5508175
--- /dev/null
@@ -0,0 +1,84 @@
+/**
+ * \brief Functions to automatically generate constraints for the rectangular
+ * node overlap removal problem.
+ *
+ * Authors:
+ *   Tim Dwyer <tgdwyer@gmail.com>
+ *
+ * Copyright (C) 2005 Authors
+ *
+ * This version is released under the CPL (Common Public License) with
+ * the Graphviz distribution.
+ * A version is also available under the LGPL as part of the Adaptagrams
+ * project: http://sourceforge.net/projects/adaptagrams.  
+ * If you make improvements or bug fixes to this code it would be much
+ * appreciated if you could also contribute those changes back to the
+ * Adaptagrams repository.
+ */
+#ifndef SEEN_REMOVEOVERLAP_GENERATE_CONSTRAINTS_H
+#define SEEN_REMOVEOVERLAP_GENERATE_CONSTRAINTS_H
+#include <iostream>
+
+class Rectangle {      
+       friend std::ostream& operator <<(std::ostream &os, const Rectangle &r);
+public:
+       static double xBorder,yBorder;
+       Rectangle(double x, double X, double y, double Y);
+       double getMaxX() const { return maxX+xBorder; }
+       double getMaxY() const { return maxY+yBorder; }
+       double getMinX() const { return minX; }
+       double getMinY() const { return minY; }
+       double getMinD(unsigned const d) const {
+               return ( d == 0 ? getMinX() : getMinY() );
+       }
+       double getMaxD(unsigned const d) const {
+               return ( d == 0 ? getMaxX() : getMaxY() );
+       }
+       double getCentreX() const { return minX+width()/2.0; }
+       double getCentreY() const { return minY+height()/2.0; }
+       double width() const { return getMaxX()-minX; }
+       double height() const { return getMaxY()-minY; }
+       static void setXBorder(double x) {xBorder=x;}
+       static void setYBorder(double y) {yBorder=y;}
+       void moveCentreX(double x) {
+               moveMinX(x-width()/2.0);
+       }
+       void moveCentreY(double y) {
+               moveMinY(y-height()/2.0);
+       }
+       void moveMinX(double x) {
+               maxX=x+width()-xBorder;
+               minX=x;
+       }
+       void moveMinY(double y) {
+               maxY=y+height()-yBorder;
+               minY=y;
+       }
+       inline double overlapX(Rectangle *r) const {
+               if (getCentreX() <= r->getCentreX() && r->minX < getMaxX())
+                       return getMaxX() - r->minX;
+               if (r->getCentreX() <= getCentreX() && minX < r->getMaxX())
+                       return r->getMaxX() - minX;
+               return 0;
+       }
+       inline double overlapY(Rectangle *r) const {
+               if (getCentreY() <= r->getCentreY() && r->minY < getMaxY())
+                       return getMaxY() - r->minY;
+               if (r->getCentreY() <= getCentreY() && minY < r->getMaxY())
+                       return r->getMaxY() - minY;
+               return 0;
+       }
+private:
+       double minX,maxX,minY,maxY;
+};
+
+
+class Variable;
+class Constraint;
+
+// returns number of constraints generated
+int generateXConstraints(const int n, Rectangle** rs, Variable** vars, Constraint** &cs, const bool useNeighbourLists);
+int generateYConstraints(const int n, Rectangle** rs, Variable** vars, Constraint** &cs);
+
+
+#endif // SEEN_REMOVEOVERLAP_GENERATE_CONSTRAINTS_H