diff --git a/HPCCSystemsGraphViewControl/DotViewCommon.cpp b/HPCCSystemsGraphViewControl/DotViewCommon.cpp index 6ba9033..a5b1fce 100644 --- a/HPCCSystemsGraphViewControl/DotViewCommon.cpp +++ b/HPCCSystemsGraphViewControl/DotViewCommon.cpp @@ -41,6 +41,7 @@ CDotViewCommon::CDotViewCommon() { m_api = NULL; m_g = hpcc::CreateGraph(); + m_buffer = hpcc::CreateGraphBuffer(0, 0); m_hotItem = hpcc::CreateGraphHotItem(); m_selection = hpcc::CreateGraphSelectionBag(); diff --git a/HPCCSystemsGraphViewControl/DotViewWin.cpp b/HPCCSystemsGraphViewControl/DotViewWin.cpp index 4c9fc4c..dd4f3ff 100644 --- a/HPCCSystemsGraphViewControl/DotViewWin.cpp +++ b/HPCCSystemsGraphViewControl/DotViewWin.cpp @@ -123,8 +123,6 @@ void CDotView::OnLButtonUp(UINT nFlags, CPoint point) m_mouseDown = MOUSEDOWN_UNKNOWN; point.Offset(m_ptOffset); hpcc::IGraphItem * selectedItem = m_gr->GetItemAt(point.x, point.y); - if (hpcc::IGraph * graph = dynamic_cast(selectedItem)) - return; bool selChanged = false; if (selectedItem) diff --git a/HPCCSystemsGraphViewControl/DotViewX11.cpp b/HPCCSystemsGraphViewControl/DotViewX11.cpp index 0079337..573aa7d 100644 --- a/HPCCSystemsGraphViewControl/DotViewX11.cpp +++ b/HPCCSystemsGraphViewControl/DotViewX11.cpp @@ -124,8 +124,6 @@ void CDotView::OnLButtonUp(hpcc::PointD point, guint modifierState) m_mouseDown = MOUSEDOWN_UNKNOWN; point.Offset(m_ptOffset); hpcc::IGraphItem * selectedItem = m_gr->GetItemAt(point.x, point.y); - if (hpcc::IGraph * graph = dynamic_cast(selectedItem)) - return; bool selChanged = false; if (selectedItem) diff --git a/graphdb/GraphCluster.cpp b/graphdb/GraphCluster.cpp index a5911cc..6f1cc28 100644 --- a/graphdb/GraphCluster.cpp +++ b/graphdb/GraphCluster.cpp @@ -75,6 +75,13 @@ void CCluster::AppendVertex(IVertex * vertex) m_vertices.insert(vertex); } +void CCluster::RemoveVertex(IVertex * vertex) +{ + IVertexSet::const_iterator found = m_vertices.find(vertex); + if (found != m_vertices.end()) + m_vertices.erase(found); +} + void CCluster::Walk(IClusterVisitor * visitor) { for(IClusterSet::const_iterator itr = m_clusters.begin(); itr != m_clusters.end(); ++itr) diff --git a/graphdb/GraphCluster.h b/graphdb/GraphCluster.h index 493588a..c96a4b0 100644 --- a/graphdb/GraphCluster.h +++ b/graphdb/GraphCluster.h @@ -51,6 +51,7 @@ class CCluster : public ICluster, public CGraphItem void AppendCluster(ICluster * cluster); void RemoveCluster(ICluster * cluster); void AppendVertex(IVertex * vertex); + void RemoveVertex(IVertex * vertex); void Walk(IClusterVisitor * visitor); void Walk(IVertexVisitor * visitor); @@ -65,6 +66,7 @@ class CCluster : public ICluster, public CGraphItem void AppendCluster(ICluster * cluster) { CCluster::AppendCluster(cluster); } \ void RemoveCluster(ICluster * cluster) { CCluster::RemoveCluster(cluster); } \ void AppendVertex(IVertex * vertex) { CCluster::AppendVertex(vertex); } \ + void RemoveVertex(IVertex * vertex) { CCluster::RemoveVertex(vertex); } \ void Walk(IClusterVisitor * visitor) { CCluster::Walk(visitor); } \ void Walk(IVertexVisitor * visitor) { CCluster::Walk(visitor); } \ bool OnlyConatinsOneCluster() const { return CCluster::OnlyConatinsOneCluster(); } \ diff --git a/graphdb/GraphDB.cpp b/graphdb/GraphDB.cpp index c3f7e48..93e35c4 100644 --- a/graphdb/GraphDB.cpp +++ b/graphdb/GraphDB.cpp @@ -43,6 +43,8 @@ const char * const GraphTpl = //"graph[ranksep=\".3\"];\r\n" //"graph[pad=\"0.01, 0.01\"];\r\n" "graph[rankdir=\"TB\"%2%];\r\n" +"graph[remincross=true];\r\n" +"graph[mclimit=\"2.0\"];\r\n" //"graph[sep=\"0.1\"];\r\n" //"graph[overlap=\"scalexy\"];\r\n" //"graph[smoothing=\"spring\"];\r\n" @@ -154,12 +156,8 @@ void WalkClusters(const ICluster * cluster, std::string & dot, int depth) } } -const char * WriteDOT(const IGraph * graph, std::string & dot) +void WalkEdges(const IGraph * graph, std::string & content) { - dot.clear(); - std::string content; - WalkClusters(graph, content, 0); - for(IEdgeSet::const_iterator itr = graph->GetAllEdges().begin(); itr != graph->GetAllEdges().end(); ++itr) { IEdge * e = itr->get(); @@ -203,6 +201,14 @@ const char * WriteDOT(const IGraph * graph, std::string & dot) content += (boost::format(EdgeTpl) % from->GetIDString() % to->GetIDString() % e->GetIDString() % e->GetPropertyString(DOT_LABEL) % props.c_str()).str(); } } +} + +const char * WriteDOT(const IGraph * graph, std::string & dot) +{ + dot.clear(); + std::string content; + WalkClusters(graph, content, 0); + WalkEdges(graph, content); std::string layout = graph->GetPropertyString(PROP_LAYOUT); std::string props; @@ -341,63 +347,129 @@ bool decendent(const ICluster * cluster, const ICluster * item) return decendent(cluster, item->GetParent()); } -const char * WriteLocalisedXGMML(const IGraph * graph, const ICluster * cluster, IGraphItemSet & addedItems, std::string & xgmml) +void BuildVertexEdgeString(IEdge * e, IVertex * v, const IVertexSet & visibleVertices, IGraphItemSet & addedItems, const ICluster * cluster, std::string & clusterXgmml, std::string & externalVertices) { - std::string clusterXgmml, externalVertices; - clusterXgmml += (boost::format("") % cluster->GetProperty("id")).str(); - for(IVertexSet::const_iterator itr = cluster->GetVertices().begin(); itr != cluster->GetVertices().end(); ++itr) + if (visibleVertices.find(v) == visibleVertices.end()) { - std::string vertexStr, attrStr; - BuildVertexString(itr->get(), vertexStr, false); - if (addedItems.find(itr->get()) == addedItems.end()) + if (addedItems.find(v) == addedItems.end()) { - addedItems.insert(itr->get()); - clusterXgmml += vertexStr; + addedItems.insert(v); + std::string vertexStr; + BuildVertexString(v, vertexStr, true); + if (decendent(cluster, v->GetParent())) + clusterXgmml += vertexStr; + else + externalVertices += vertexStr; } } - for(IClusterSet::const_iterator itr = cluster->GetClusters().begin(); itr != cluster->GetClusters().end(); ++itr) - WriteLocalisedXGMML(graph, itr->get(), addedItems, clusterXgmml); - for(IVertexSet::const_iterator v_itr = cluster->GetVertices().begin(); v_itr != cluster->GetVertices().end(); ++v_itr) + + if (addedItems.find((IGraphItem *)e) == addedItems.end()) { - IEdgeSet inEdges = v_itr->get()->GetInEdges(); - for(IEdgeSet::const_iterator itr = inEdges.begin(); itr != inEdges.end(); ++itr) + addedItems.insert((IGraphItem *)e); + std::string edgeStr; + BuildEdgeString(e, edgeStr); + clusterXgmml += edgeStr; + } +} + +typedef std::vector IClusterVector; +void GetAncestors(IVertex * v, IClusterVector & ancestors) +{ + ICluster * parent = v->GetParent(); + while (parent) + { + ancestors.push_back(parent); + parent = parent->GetParent(); + } +} + +ICluster * GetCommonAncestor(IVertex * v1, IVertex * v2) +{ + IClusterVector v1_ancestors, v2_ancestors; + GetAncestors(v1, v1_ancestors); + GetAncestors(v2, v2_ancestors); + IClusterVector::const_reverse_iterator finger1 = v1_ancestors.rbegin(); + IClusterVector::const_reverse_iterator finger2 = v2_ancestors.rbegin(); + ICluster * retVal = NULL; + while(finger1 != v1_ancestors.rend() && finger2 != v2_ancestors.rend() && finger1->get() == finger2->get()) { + retVal = finger1->get(); + ++finger1; + ++finger2; + } + return retVal; +} + +void CalcVisibility(const ICluster * rootCluster, const ICluster * cluster, IVertexSet & externalVertices, IEdgeSet & edges, std::string & content) +{ + std::string childContent; + IVertexSet visibleVertices, pointVertices; + for(IVertexSet::const_iterator itr = cluster->GetVertices().begin(); itr != cluster->GetVertices().end(); ++itr) + { + IVertex * v = itr->get(); + bool v_display = false; + + IVertexSet adjacentVertices; + v->GetAdjacentVertices(adjacentVertices); + for(IVertexSet::const_iterator adj_itr = adjacentVertices.begin(); adj_itr != adjacentVertices.end(); ++adj_itr) { - IVertex * from = itr->get()->GetFromVertex(); - if (!decendent(cluster, from->GetParent())) - WriteLocalisedXGMML(graph, from, addedItems, externalVertices, DIRECTION_IN, 0); + IVertex * v_adjacent = adj_itr->get(); + IEdge * e = v->GetEdge(v_adjacent); - std::string edgeStr; - BuildEdgeString(itr->get(), edgeStr); - if (addedItems.find((IGraphItem *)itr->get()) == addedItems.end()) + // Both vertices are inside rootCluster and their edge crosses rootClusters white space. + if (rootCluster == GetCommonAncestor(v, v_adjacent)) + { + v_display = true; + edges.insert(e); + } // Main vertex is visible and adjacent vertex is external + else if (rootCluster == v->GetParent() && !decendent(rootCluster, v_adjacent->GetParent())) + { + v_display = true; + edges.insert(e); + externalVertices.insert(v_adjacent); + } // both vertices are within a single child cluster of root cluster + else { - addedItems.insert((IGraphItem *)itr->get()); - clusterXgmml += edgeStr; } } - IEdgeSet outEdges = v_itr->get()->GetOutEdges(); - for(IEdgeSet::const_iterator itr = outEdges.begin(); itr != outEdges.end(); ++itr) + if (v_display) { - IVertex * to = itr->get()->GetToVertex(); - if (!decendent(cluster, to->GetParent())) - WriteLocalisedXGMML(graph, to, addedItems, externalVertices, DIRECTION_OUT, 0); - - std::string edgeStr; - BuildEdgeString(itr->get(), edgeStr); - if (addedItems.find((IGraphItem *)itr->get()) == addedItems.end()) - { - addedItems.insert((IGraphItem *)itr->get()); - clusterXgmml += edgeStr; - } + std::string vertexStr; + BuildVertexString(v, vertexStr, v->GetParent() == rootCluster ? false : true); + childContent += vertexStr; } } - clusterXgmml += ""; - xgmml += externalVertices + clusterXgmml; - return xgmml.c_str(); + + for(IClusterSet::const_iterator itr = cluster->GetClusters().begin(); itr != cluster->GetClusters().end(); ++itr) + { + CalcVisibility(rootCluster, itr->get(), externalVertices, edges, childContent); + } + + if (!childContent.empty()) + { + content += (boost::format("") % cluster->GetProperty("id")).str(); + content += childContent; + content += ""; + } } -const char * WriteLocalisedXGMML(const IGraph * graph, std::string & xgmml) +const char * WriteLocalisedXGMML(const IGraph * graph, const ICluster * cluster, IGraphItemSet & addedItems, std::string & xgmml) { + IVertexSet externalVertices; + IEdgeSet edges; + CalcVisibility(cluster, cluster, externalVertices, edges, xgmml); + for (IVertexSet::const_iterator itr = externalVertices.begin(); itr != externalVertices.end(); ++itr) + { + std::string vertexStr; + BuildVertexString(itr->get(), vertexStr, true); + xgmml += vertexStr; + } + for(IEdgeSet::const_iterator itr = edges.begin(); itr != edges.end(); ++itr) + { + std::string edgeStr; + BuildEdgeString(itr->get(), edgeStr); + xgmml += edgeStr; + } return xgmml.c_str(); } @@ -408,8 +480,6 @@ GRAPHDB_API const char * WriteLocalisedXGMML(const IGraph * graph, const IGraphI WriteLocalisedXGMML(graph, vertex, addedItems, xgmml, DIRECTION_UNKNOWN, 3); else if (const IEdge * edge = dynamic_cast(item)) WriteLocalisedXGMML(graph, edge, addedItems, xgmml, DIRECTION_UNKNOWN, 3); - else if (const IGraph * graph = dynamic_cast(item)) - WriteLocalisedXGMML(graph, xgmml); else if (const ICluster * cluster = dynamic_cast(item)) WriteLocalisedXGMML(graph, cluster, addedItems, xgmml); return xgmml.c_str(); diff --git a/graphdb/GraphDB.h b/graphdb/GraphDB.h index 47fda74..78cc519 100644 --- a/graphdb/GraphDB.h +++ b/graphdb/GraphDB.h @@ -26,25 +26,34 @@ namespace hpcc { // === Forward Declarations === +template +struct CompareT +{ + bool operator() (const T & l, const T & r) const + { + return l->GetID() < r ->GetID(); + } +}; + hpcc_interface IGraphItem; typedef CUnknownPtr IGraphItemPtr; -typedef std::set IGraphItemSet; +typedef std::set > IGraphItemSet; hpcc_interface IGraph; typedef CUnknownPtr IGraphPtr; -typedef std::set IGraphSet; +typedef std::set > IGraphSet; hpcc_interface ICluster; typedef CUnknownPtr IClusterPtr; -typedef std::set IClusterSet; +typedef std::set > IClusterSet; hpcc_interface IVertex; typedef CUnknownPtr IVertexPtr; -typedef std::set IVertexSet; +typedef std::set > IVertexSet; hpcc_interface IEdge; typedef CUnknownPtr IEdgePtr; -typedef std::set IEdgeSet; +typedef std::set > IEdgeSet; typedef std::map StringStringMap; @@ -115,6 +124,7 @@ hpcc_interface GRAPHDB_API ICluster : public IGraphItem virtual void AppendCluster(ICluster * cluster) = 0; virtual void RemoveCluster(ICluster * cluster) = 0; virtual void AppendVertex(IVertex * vertex) = 0; + virtual void RemoveVertex(IVertex * vertex) = 0; virtual void Walk(IClusterVisitor * visitor) = 0; @@ -125,11 +135,15 @@ hpcc_interface GRAPHDB_API ICluster : public IGraphItem hpcc_interface GRAPHDB_API IVertex : public IGraphItem { virtual ICluster * GetParent() const = 0; + virtual void SetParent(ICluster * cluster) = 0; virtual unsigned int GetInEdgeCount() const = 0; virtual unsigned int GetOutEdgeCount() const = 0; virtual const IEdgeSet & GetInEdges() const = 0; virtual const IEdgeSet & GetOutEdges() const = 0; + virtual unsigned int GetAdjacentVertices(IVertexSet & adjacentVertices) const = 0; + virtual IEdge * GetEdge(IVertex * adjacentVertex) const = 0; + virtual void AppendInEdge(IEdge * edge) = 0; virtual void AppendOutEdge(IEdge * edge) = 0; }; diff --git a/graphdb/GraphGraph.cpp b/graphdb/GraphGraph.cpp index 04a5e20..eeed45e 100644 --- a/graphdb/GraphGraph.cpp +++ b/graphdb/GraphGraph.cpp @@ -44,6 +44,8 @@ void CGraph::Clear() m_allItems.clear(); m_externalIDs.clear(); m_rexternalIDs.clear(); + SetProperty("id", "0"); + SetExternalID(hpcc::GRAPH_TYPE_CLUSTER, "0", (ICluster *)(CCluster *)this); } ICluster * CGraph::CreateCluster() diff --git a/graphdb/GraphVertex.cpp b/graphdb/GraphVertex.cpp index ece62ea..15d81bb 100644 --- a/graphdb/GraphVertex.cpp +++ b/graphdb/GraphVertex.cpp @@ -37,6 +37,13 @@ ICluster * CVertex::GetParent() const return m_parent; } +void CVertex::SetParent(ICluster * cluster) +{ + cluster->AppendVertex(this); + m_parent->RemoveVertex(this); + m_parent = cluster; +} + unsigned int CVertex::GetInEdgeCount() const { return m_inEdges.size(); @@ -57,6 +64,33 @@ const IEdgeSet & CVertex::GetOutEdges() const return m_outEdges; } +unsigned int CVertex::GetAdjacentVertices(IVertexSet & adjacentVertices) const +{ + for(IEdgeSet::const_iterator itr = m_inEdges.begin(); itr != m_inEdges.end(); ++itr) + adjacentVertices.insert(itr->get()->GetFromVertex()); + + for(IEdgeSet::const_iterator itr = m_outEdges.begin(); itr != m_outEdges.end(); ++itr) + adjacentVertices.insert(itr->get()->GetToVertex()); + + return adjacentVertices.size(); +} + +IEdge * CVertex::GetEdge(IVertex * adjacentVertex) const +{ + for(IEdgeSet::const_iterator itr = m_inEdges.begin(); itr != m_inEdges.end(); ++itr) + { + if (adjacentVertex == itr->get()->GetFromVertex()) + return itr->get(); + } + + for(IEdgeSet::const_iterator itr = m_outEdges.begin(); itr != m_outEdges.end(); ++itr) + { + if (adjacentVertex == itr->get()->GetToVertex()) + return itr->get(); + } + return NULL; +} + void CVertex::AppendInEdge(IEdge * edge) { m_inEdges.insert(edge); diff --git a/graphdb/GraphVertex.h b/graphdb/GraphVertex.h index 67f352f..02bcea0 100644 --- a/graphdb/GraphVertex.h +++ b/graphdb/GraphVertex.h @@ -44,10 +44,13 @@ class CVertex : public IVertex, public CGraphItem // === IVertex === ICluster * GetParent() const; + void SetParent(ICluster * cluster); unsigned int GetInEdgeCount() const; unsigned int GetOutEdgeCount() const; const IEdgeSet & GetInEdges() const; const IEdgeSet & GetOutEdges() const; + unsigned int GetAdjacentVertices(IVertexSet & adjacentVertices) const; + IEdge * GetEdge(IVertex * adjacentVertex) const; void AppendInEdge(IEdge * edge); void AppendOutEdge(IEdge * edge); }; diff --git a/graphdb/SaxParser.h b/graphdb/SaxParser.h index 8d5dd05..3600256 100644 --- a/graphdb/SaxParser.h +++ b/graphdb/SaxParser.h @@ -497,7 +497,7 @@ class CElement : public CUnknown }; typedef CUnknownPtr CElementPtr; typedef std::stack CElementStack; -typedef std::set ElementSet; +typedef std::vector ElementVector; class CStackParser : public CExpatImpl { diff --git a/graphdb/SvgShapes.cpp b/graphdb/SvgShapes.cpp index 54782f9..c6ec0f7 100644 --- a/graphdb/SvgShapes.cpp +++ b/graphdb/SvgShapes.cpp @@ -512,13 +512,20 @@ Path::Path(const std::string& fill, const std::string & stroke, const std::strin assert(itr != tokens.end()); m_dash2 = boost::lexical_cast(*itr); } - try + if (stroke_width.empty()) { - m_stroke_width = boost::lexical_cast(stroke_width); + m_stroke_width = 1.0; } - catch (const boost::bad_lexical_cast &) + else { - m_stroke_width = 1.0; + try + { + m_stroke_width = boost::lexical_cast(stroke_width); + } + catch (const boost::bad_lexical_cast &) + { + m_stroke_width = 1.0; + } } agg::svg::path_tokenizer tok; tok.set_path_str(d.c_str()); diff --git a/graphdb/XgmmlParser.cpp b/graphdb/XgmmlParser.cpp index d42de20..34035f3 100644 --- a/graphdb/XgmmlParser.cpp +++ b/graphdb/XgmmlParser.cpp @@ -137,7 +137,7 @@ class CXgmmlParser : public CStackParser IGraphPtr m_graph; GraphItemStack m_giStack; StringGraphItemMap m_itemLookup; - ElementSet m_edgeElements; + ElementVector m_edgeElements; bool m_merge; public: @@ -244,7 +244,7 @@ class CXgmmlParser : public CStackParser void ProcessEdges() { - for (ElementSet::const_iterator itr = m_edgeElements.begin(); itr != m_edgeElements.end(); ++itr) + for (ElementVector::const_iterator itr = m_edgeElements.begin(); itr != m_edgeElements.end(); ++itr) { CElementPtr e = itr->get(); IEdge * edge = NULL; @@ -374,7 +374,7 @@ class CXgmmlParser : public CStackParser } else if (0 == e->m_tag.compare("edge")) { - m_edgeElements.insert(e); + m_edgeElements.push_back(e); } else if (0 == e->m_tag.compare("att")) { @@ -402,23 +402,58 @@ GRAPHDB_API bool LoadXGMML(IGraph * graph, const std::string & xgmml) parser.Create(); bool retVal = parser.Parse(xgmml.c_str(), xgmml.length()); parser.ProcessEdges(); - IClusterSet clusters = graph->GetAllClusters(); - for(IClusterSet::const_iterator itr = clusters.begin(); itr != clusters.end(); ++itr) + +#define NINJECT_EXTRA_CLUSTER +#ifdef INJECT_EXTRA_CLUSTER { -#ifdef TEST_XGMMLCLEANUP - if (!itr->get()->GetClusters().empty() && !itr->get()->GetVertices().empty()) + IClusterSet clusters(graph->GetAllClusters().begin(), graph->GetAllClusters().end()); + for(IClusterSet::const_iterator itr = clusters.begin(); itr != clusters.end(); ++itr) { - IClusterPtr cluster = graph->CreateCluster(itr->get()); - for (IVertexSet::iterator v_itr = itr->get()->GetVertices().begin(); v_itr != itr->get()->GetVertices().end(); ++v_itr) + if (!itr->get()->GetClusters().empty() && !itr->get()->GetVertices().empty()) { - cluster->AppendVertex(v_itr->get()); + IClusterPtr cluster = graph->CreateCluster(itr->get()); + std::string newID = itr->get()->GetProperty("id"); + newID += "_injected"; + graph->SetExternalID(GRAPH_TYPE_CLUSTER, newID, cluster); + cluster->SetProperty(DOT_ID, newID.c_str()); + for (IVertexSet::iterator v_itr = itr->get()->GetVertices().begin(); v_itr != itr->get()->GetVertices().end(); v_itr = itr->get()->GetVertices().begin()) // Always move the first vertex from the set + { + v_itr->get()->SetParent(cluster); + } + itr->get()->Delete(); } } + } #endif - if (itr->get()->OnlyConatinsOneCluster()) - itr->get()->Delete(); +#define NREMOVE_CLUSTER_OF_CLUSTERS +#ifdef REMOVE_CLUSTER_OF_CLUSTERS + { + IClusterSet clusters(graph->GetAllClusters().begin(), graph->GetAllClusters().end()); + for(IClusterSet::const_iterator itr = clusters.begin(); itr != clusters.end(); ++itr) + { + if (graph != itr->get()->GetParent() && !itr->get()->GetClusters().empty() && itr->get()->GetVertices().empty()) + { + itr->get()->Delete(); + } + } } +#endif + +#define NREMOVE_CLUSTER_OF_ONECLUSTER +#ifdef REMOVE_CLUSTER_OF_ONECLUSTER + { + IClusterSet clusters(graph->GetAllClusters().begin(), graph->GetAllClusters().end()); + for(IClusterSet::const_iterator itr = clusters.begin(); itr != clusters.end(); ++itr) + { + if (itr->get()->OnlyConatinsOneCluster()) + { + itr->get()->Delete(); + } + } + } +#endif + return retVal; }