zoukankan      html  css  js  c++  java
  • 在程序内部使用winGraphviz进行图形自动布局

    winGraphviz支持內部圖形形狀進行佈局圖輸出。當然,在我們程序內部並不需要這樣的一個圖,因為我們的圖可能需要其它的繪製元素,而且我們還會在圖形上進行拖動、放大、縮小等功能,一張簡單的佈局圖是不符合我們的要求的。

    幸好winGraphviz支持佈局格局的輸出,牠可以告訴我們每一個具體的圖形的位置,每一條連線的位置,只要我們獲取到該位置,即可以用該位置進行設置我們內部的圖形的位置了,這樣,我們就可以達到我們的目的了。

    對於圖形坐標的輸出,winGraphviz提供有兩個接口,一個是ToPlain,另一個是ToSvg。我在研究StarUML時,發現牠是使用ToPlain的,但我在程序內部使用ToPlain時,發現坐標出入甚大,不明其原理如何,且因StarUML使用DELPHI編寫,代碼也稍顯複雜,而且基於時間問題,我并未深入研究,總之ToPlain並不能產生確切的坐標,可能還需要其它處理。不過ToSvg則提供了正確的坐標位置,使用它將可以得到我們想要的坐標,確切的坐標。牠是一個XML格式,讀者可以使用一個簡單的圖形,如Graphic g {a -> b -> c; } 來查看輸出的Svg格式是如何樣子,當然接下來我會告訴讀者如何使用COM進行加載winGraphviz,可以先看看我用程序自己畫的佈局圖:

    左邊是winGraphviz生成的,右邊是我自己畫的。

     

    加載COM接口:

        USES_CONVERSION;
        IDOT* m_pDot
        HRESULT hr;
    
        hr = CoInitialize(NULL);
        if (FAILED (hr))
        {
            ASSERT(0);
            return false;
        }
    
        hr = CoCreateInstance(CLSID_DOT, NULL, CLSCTX_ALL, IID_IDOT, reinterpret_cast<LPVOID*>(&m_pDot));
    
        if (FAILED (hr))
        {
           return false;
        }
    
        return true;
    

    將用戶自定義的節點、連線轉換成winGraphviz語言(假設使用是一個vector的節點和一個vector的連線):

    CMyGraphvizGraph* CDiagramLayout::LayoutDiagram( IGraphicvizView* pView, bool fLeftToRight /*= true*/ )
    {
        if (m_pDot == NULL || pView == NULL)
        {
            return NULL;
        }
    
        m_pView = pView;
    
        string strGraphviz ("digraph G { ordering=out;");
        if (fLeftToRight)
        {
            strGraphviz += " rankdir=LR;";
        }
        else
        {
            strGraphviz += " rankdir=TB;";
        }
    
        const TPNodeList& vctNodes = pView->GetNodes();
        for (size_t i = 0; i < vctNodes.size(); ++i)
        {
            strGraphviz += GetNodeString (vctNodes [i]);
        }
    
        const TPEdgeList& vctEdges= pView->GetEdges();
        for (size_t i = 0; i < vctEdges.size(); ++i)
        {
            strGraphviz += GetEdgeString (vctEdges [i]);
        }
    
        strGraphviz += "}";
    
        return GenerateGraph(strGraphviz.c_str());
    }
    
    CMyGraphvizGraph* CDiagramLayout::GenerateGraph( const char* pszToParse)
    {
        BSTR bsSvg = NULL;
        HRESULT hr = m_pDot->ToSvg(A2BSTR(pszToParse), &bsSvg);
    
        //BSTR bsPlain = NULL;
        //m_pDot->ToPlain(A2BSTR(pszToParse), &bsPlain);
    
    #ifdef _DEBUG
        IBinaryImage* image = NULL;
        hr = m_pDot->ToPNG(A2BSTR(pszToParse), &image);
    
        if (FAILED (hr))
        {
            ASSERT (0);
            return NULL;
        }
    
        VARIANT_BOOL fRet = FALSE;
        hr = image->Save(L"d:\a.png", &fRet);
    
        image->Release();
    
        if (FAILED (hr) || !fRet)
        {
            ASSERT(0);
    
            return false;
        }
    #endif
    
    
        int nLen = SysStringLen(bsSvg); //not including NULL
        int nBytes = WideCharToMultiByte(CP_ACP, 0, bsSvg, nLen,
            NULL, NULL, NULL, NULL); //number of bytes not including NULL
        BSTR bstrA = SysAllocStringByteLen(NULL, nBytes); // allocates nBytes
        VERIFY(WideCharToMultiByte(CP_ACP, 0, bsSvg, nLen, (LPSTR)bstrA, nBytes, NULL,
            NULL) == nBytes);
    
        CMyGraphvizGraph* pGraph = m_parser.Parse((char*)bstrA);
        ::SysFreeString(bsSvg);
        ::SysFreeString(bstrA);
    
        return pGraph;
    }
    
    string CDiagramLayout::GetEdgeString( GraphvizEdge* pEdge )
    {
        static char szRet [128];
    
        sprintf (szRet, "%s -> %s [label=%s];", 
            pEdge->strHead.c_str(), 
            pEdge->strTail.c_str(), 
            pEdge->strName.c_str());
    
        return szRet;
    }
    
    string CDiagramLayout::GetNodeString( GraphvizNode* pNode )
    {
        static char szRet [512];
    
        sprintf (szRet, "%s [shape=box,width=%4.3f,height=%4.3f];",
            pNode->strName.c_str(),
            (float)pNode->iWidth / PIXEL_PER_INCH_WIDTH (),
            (float)pNode->iHeight / PIXEL_PER_INCH_HEIGHT ());
    
        return szRet;
    }

    關於解析svg的代碼:

    CMyGraphvizGraph* CGraphvizPlainOutputParser::ParseSvgText( const char* pszText )
    {
        TiXmlDocument doc;
        doc.Parse(pszText);
        if (doc.Error())
        {
            return NULL;
        }
     
        CMyGraphvizGraph* pGraph = new CMyGraphvizGraph;
    
        TiXmlElement* pRoot = doc.RootElement();
    
        const char* pszWidth = pRoot->Attribute("width");
        const char* pszHeight = pRoot->Attribute("height");
    
        if (pszWidth != NULL && pszHeight != NULL)
        {
            pGraph->SetWidth(atoi (pszWidth));
            pGraph->SetHeight(atoi (pszHeight));
    
            for (TiXmlElement* pItem = pRoot->FirstChildElement ()->FirstChildElement(); pItem != NULL; pItem = pItem->NextSiblingElement())
            {
                const char* pszClass = pItem->Attribute("class");
                if (pszClass == NULL)
                {
                    continue;
                }
    
                if (stricmp (pszClass, "node") == 0)
                {
                    TiXmlElement* pTitle   = pItem->FirstChildElement("text");
                    TiXmlElement* pPolygon = pItem->FirstChildElement("polygon");
    
                    if (pTitle != NULL && pPolygon != NULL)
                    {
                        const char* pszName = pTitle->GetText();
                        const char* pszPts  = pPolygon->Attribute("points");
    
                        if (pszName == NULL || pszPts == NULL)
                        {
                            assert (0);
                            continue;
                        }
    
                        vector <string> vctPoints = split(pszPts, ", ");
    
                        GraphvizNode* pNode = new GraphvizNode;
    
                        pNode->strName = pszName;
                        
                        int iTop = 9999;
                        int iBottom = 0;
                        int iLeft = 9999;
                        int iRight = 0;
    
                        int iX, iY;
                        for (size_t i = 0; i < vctPoints.size(); i += 2)
                        {
                            iX = atoi (vctPoints [i].c_str());
                            iY = atoi (vctPoints [i + 1].c_str());
                            if (iX < iLeft) iLeft = iX;
                            if (iY < iTop) iTop = iY;
                            if (iX > iRight) iRight = iX;
                            if (iY > iBottom) iBottom = iY;
                        }
                        pNode->iLeft = iLeft;
                        pNode->iTop = iTop;
                        pNode->iWidth = iRight - iLeft;
                        pNode->iHeight = iBottom - iTop;
    
                        pGraph->AddNode(pNode);
                    }
                }
                else if (stricmp (pszClass, "edge") == 0)
                {
                    TiXmlElement*  pTitle   = pItem->FirstChildElement("text");
                    TiXmlElement* pPath     = pItem->FirstChildElement("path");
                    TiXmlElement* pPolygon  = pItem->FirstChildElement("polygon");
    
                    if (pTitle != NULL && pPath != NULL && pPolygon != NULL)
                    {
                        const char* pszName = pTitle->GetText();
                        const char* pszPts  = pPolygon->Attribute("points");
                        const char* pszPath = pPath->Attribute("d");
    
                        if (pszName == NULL || pszPts == NULL || pszPath == NULL)
                        {
                            assert (0);
                            continue;
                        }
    
                        vector <string> vctPathPoints = split(pszPath, "MC, ");
                        vector <string> vctPolygonPoints = split(pszPts, ", ");
    
                        GraphvizEdge* pEdge = new GraphvizEdge;
                        pEdge->strName = pszName;
    
                        for (size_t i = 0; i < vctPathPoints.size(); i += 2)
                        {
                            POINT pt = {atoi (vctPathPoints [i].c_str()), atoi (vctPathPoints [i + 1].c_str())};
                            pEdge->ptsLine.push_back(pt);
                        }
    
                        for (size_t j = 0; j < vctPolygonPoints.size(); j += 2)
                        {
                            POINT pt = {atoi (vctPolygonPoints [j].c_str()), atoi (vctPolygonPoints [j + 1].c_str())};
                            pEdge->ptsArrow.push_back(pt);
                        }
    
                        pGraph->AddEdge(pEdge);
                    }
                }
            }
        }
        
        return pGraph;
    }
    解析svg


    最後顯示:

    void CGraphDlg::OnPaint()
    {
        CPaintDC dc(this); // device context for painting
        // TODO: Add your message handler code here
        // Do not call CDialog::OnPaint() for painting messages
        if (m_pGraph != NULL)
        {
            CDC dcMem;
            CBitmap bmp;
    
            dcMem.CreateCompatibleDC(&dc);
            bmp.CreateCompatibleBitmap(&dc, m_pGraph->GetWidth(), m_pGraph->GetHeight());
            dcMem.SelectObject(&bmp);
    
            const TPNodeList& vctNodes = m_pGraph->GetNodes();
            const TPEdgeList& vctEdges = m_pGraph->GetEdges();
    
    
            for (size_t i = 0; i < vctNodes.size(); ++i)
            {
                RECT rc = {
                    vctNodes [i]->iLeft, 
                    vctNodes [i]->iTop, 
                    vctNodes [i]->iWidth  + vctNodes [i]->iLeft, 
                    vctNodes [i]->iHeight + vctNodes [i]->iTop
                };
    
                dcMem.FillSolidRect(&rc, RGB (128, 128, 128));
    
                CString str (vctNodes [i]->strName.c_str());
                dcMem.DrawText (str, &rc, DT_VCENTER | DT_SINGLELINE | DT_CENTER);
            }
    
    
            CPen pen (PS_SOLID, 1, RGB (255, 255, 255));
            CBrush br (RGB (255, 255, 255));
            dcMem.SelectObject(&pen);
            dcMem.SelectObject(&br);
    
            for (size_t j = 0; j < vctEdges.size(); ++j)
            {
                size_t iLineSize = vctEdges [j]->ptsLine.size();
                POINT* pptLine = new POINT [iLineSize];
    
                for (size_t k = 0; k < iLineSize; ++k)
                {
                    pptLine [k] = vctEdges [j]->ptsLine [k];
                }
                dcMem.PolyBezier(pptLine, iLineSize);
    
                // 箭头
                size_t iArrowSize = vctEdges [j]->ptsArrow.size();
                POINT* pptsArrow = new POINT [iArrowSize];
                for (size_t m = 0; m < iArrowSize; ++m)
                {
                    pptsArrow [m] = vctEdges [j]->ptsArrow [m];
                }
    
                dcMem.Polygon(pptsArrow, iArrowSize);
                delete [] pptsArrow;
                delete [] pptLine;
            }
    
            
    
            dc.BitBlt(0, 0, m_pGraph->GetWidth(), m_pGraph->GetHeight(), &dcMem, 0, 0, SRCCOPY);
    
            bmp.DeleteObject();
            dcMem.DeleteDC();
        }
    }
    顯示圖

    完整的代碼:

    #pragma once
    
    #include "NodeEdge.h"
    #include "WinGraphviz/WinGraphviz.h"
    #include "IGraphicvizView.h"
    
    #include <vector>
    #include <string>
    using std::vector;
    using std::string;
    
    class CGraphvizPlainOutputParser;
    
    class CMyGraphvizGraph
    {
        friend class CGraphvizPlainOutputParser;
    private:
        int     m_iWidth;
        int     m_iHeight;
    
        TPNodeList  m_nodes;
        TPEdgeList  m_edges;
    
    public:
        CMyGraphvizGraph ();
        ~CMyGraphvizGraph();
    
        GraphvizNode*   GetNode (size_t iIndex);
        size_t          GetNodeCount () const;
        GraphvizEdge*   GetEdge (size_t iIndex);
        size_t          GetEdgeCount () const;
    
        const TPNodeList& GetNodes () { return m_nodes; };
        const TPEdgeList& GetEdges () { return m_edges; }
        
        int GetWidth  () { return m_iWidth; };
        int GetHeight () { return m_iHeight; };
    
    protected:
        void SetWidth (int iWidth);
        void SetHeight (int iHeight);
        void AddNode( GraphvizNode* pNode );
        void AddEdge( GraphvizEdge* pEdge );
    };
    
    class CGraphvizPlainOutputParser
    {
    private:
        vector <string> m_vctTempStr;
    
    private:
        double  InchToPixel (double dblInch, BOOL fWidth);
        double  InchStrToPixel (const char* pszInch, BOOL fWidth);
    
        void                ParseLine (const char* pszLine, vector <string>& vctOut);
        CMyGraphvizGraph*   ParseGraphicLine (const char* pszLine);
        GraphvizNode*       ParseNodeLine (const char* pszLine);
        GraphvizEdge*       ParseEdgeLine (const char* pszLine);
    
        void SeparateLine( const char* pszText, std::vector<string>& vctPlainOut );
    
    public:
        CGraphvizPlainOutputParser ();
        ~CGraphvizPlainOutputParser();
    
        CMyGraphvizGraph* Parse (const char* pszText);
        CMyGraphvizGraph* ParsePlainText( const char* pszText );
        CMyGraphvizGraph* ParseSvgText( const char* pszText );
    };
    
    class CDiagramLayout
    {
    private:
        IDOT*                       m_pDot;     // viz图
        IGraphicvizView*            m_pView;    // 显示图
        CGraphvizPlainOutputParser  m_parser;   // 解析器,用于分析viz布局后的图
    
        bool Init();
    
        string GetNodeIdString ();
        string GetEdgeIdString ();
    
        CMyGraphvizGraph* GenerateGraph (const char* pszToParse);
        string GetEdgeString( GraphvizEdge* pEdge );
        string GetNodeString( GraphvizNode* pNode );
    
    public:
        CDiagramLayout ();
        ~CDiagramLayout();
    
        CMyGraphvizGraph* LayoutDiagram (IGraphicvizView* pView, bool fLeftToRight = true);
    };
    MyGraphvizGraph.h
    #include "stdafx.h"
    #include "MyGraphvizGraph.h"
    #include "tinyxml/tinyxml.h"
    #include <shlwapi.h>
    #include <math.h>
    
    #pragma comment (lib, "shlwapi.lib")
    #pragma warning (disable : 4996)
    
    int PIXEL_PER_INCH_WIDTH ()
    {
        HWND hwnd = GetDesktopWindow();
        HDC hdc = GetDC(hwnd);
        int iPixel = GetDeviceCaps (hdc, LOGPIXELSX);
        ReleaseDC(hwnd, hdc);
    
        return iPixel;
    }
    
    int PIXEL_PER_INCH_HEIGHT ()
    {
        HWND hwnd = GetDesktopWindow();
        HDC hdc = GetDC(hwnd);
        int iPixel = GetDeviceCaps (hdc, LOGPIXELSY);
        ReleaseDC(hwnd, hdc);
    
        return iPixel;
    }
    
    
    //字符串分割函数
    static std::vector<std::string> split(const char* pszToSplit, const char* pszSpplitor)
    {
        std::vector<std::string> result;
        char* pszTemp = strdup (pszToSplit);
    
        char* token = strtok(pszTemp , pszSpplitor );
        while( token != NULL )
        {
            /* While there are tokens in "string" */
            result.push_back(token);
    
            /* Get next token: */
            token = strtok( NULL, pszSpplitor );
        }
    
    
        free (pszTemp);
        return result;
    }
    
    
    GraphvizNode* CMyGraphvizGraph::GetNode( size_t iIndex )
    {
        if (iIndex >= m_nodes.size())
        {
            return NULL;
        }
    
        return m_nodes [iIndex];
    }
    
    size_t CMyGraphvizGraph::GetNodeCount() const
    {
        return m_nodes.size();
    }
    
    GraphvizEdge* CMyGraphvizGraph::GetEdge( size_t iIndex )
    {
        if (iIndex >= m_edges.size())
        {
            return NULL;
        }
    
        return m_edges [iIndex];
    }
    
    size_t CMyGraphvizGraph::GetEdgeCount() const
    {
        return m_edges.size();
    }
    
    CMyGraphvizGraph::CMyGraphvizGraph()
        : m_iWidth (0)
        , m_iHeight (0)
    {
    
    }
    
    CMyGraphvizGraph::~CMyGraphvizGraph()
    {
        for (size_t i = 0; i < m_nodes.size(); ++i)
        {
            delete m_nodes [i];
        }
        m_nodes.clear();
    
        for (size_t j = 0; j < m_edges.size(); ++j)
        {
            delete m_edges [j];
        }
        m_edges.clear();
    }
    
    void CMyGraphvizGraph::SetWidth( int iWidth )
    {
        m_iWidth = iWidth;
    }
    
    void CMyGraphvizGraph::SetHeight( int iHeight )
    {
        m_iHeight = iHeight;
    }
    
    void CMyGraphvizGraph::AddNode( GraphvizNode* pNode )
    {
        m_nodes.push_back(pNode);
    }
    
    void CMyGraphvizGraph::AddEdge( GraphvizEdge* pEdge )
    {
        m_edges.push_back(pEdge);
    }
    
    double CGraphvizPlainOutputParser::InchToPixel( double dblInch, BOOL fWidth )
    {
        return dblInch * (fWidth ? PIXEL_PER_INCH_WIDTH () : PIXEL_PER_INCH_HEIGHT ());
    }
    
    double CGraphvizPlainOutputParser::InchStrToPixel( const char* pszInch, BOOL fWidth )
    {
        char* pszEnd = NULL;
        return InchToPixel (strtod (pszInch, &pszEnd), fWidth);
    }
    
    void CGraphvizPlainOutputParser::ParseLine( const char* pszLine, vector <string>& vctOut )
    {
        char chSeparator = ' ';
        const char* pszNext = NULL;
    
        vctOut.clear();
        char* pszNewString = strdup (pszLine);
        StrTrimA (pszNewString, " ");
        
        const char* pszTemp = pszNewString;
        do 
        {
            pszNext = strchr (pszTemp, chSeparator);
            if (pszNext != NULL)
            {
                vctOut.push_back(string (pszTemp, 0, pszNext - pszTemp));
            }
            else
            {
                vctOut.push_back(pszTemp);
                break;
            }
    
            pszTemp = pszNext + 1; /* ' ' 的下一个字符位置 */
            
            while (*pszTemp == ' ' && *pszTemp != '')
            {
                ++pszTemp;
            }
    
        } while (pszNext != NULL);
    
        free (pszNewString);
    }
    
    CMyGraphvizGraph* CGraphvizPlainOutputParser::ParseGraphicLine( const char* pszLine )
    {
        CMyGraphvizGraph* pGraph = NULL;
    
        m_vctTempStr.clear();
        ParseLine(pszLine, m_vctTempStr);
    
        ASSERT(m_vctTempStr.size() >= 3);
    
        pGraph = new CMyGraphvizGraph;
        pGraph->SetWidth((int)InchStrToPixel(m_vctTempStr [2].c_str(), TRUE));
        pGraph->SetHeight((int)InchStrToPixel(m_vctTempStr [3].c_str(), FALSE));
    
        return pGraph;
    }
    
    GraphvizNode* CGraphvizPlainOutputParser::ParseNodeLine( const char* pszLine )
    {
        GraphvizNode* pNode = NULL;
    
        m_vctTempStr.clear();
        ParseLine(pszLine, m_vctTempStr);
    
        pNode = new GraphvizNode;
        pNode->strName      = m_vctTempStr [1];
        pNode->iLeft        = (int)InchStrToPixel (m_vctTempStr [2].c_str(), TRUE);
        pNode->iTop         = (int)InchStrToPixel (m_vctTempStr [3].c_str(), FALSE);
        pNode->iWidth       = (int)InchStrToPixel (m_vctTempStr [4].c_str(), TRUE);
        pNode->iHeight      = (int)InchStrToPixel (m_vctTempStr [5].c_str(), FALSE);
    
        return pNode;
    }
    
    GraphvizEdge* CGraphvizPlainOutputParser::ParseEdgeLine( const char* pszLine )
    {
        GraphvizEdge* pEdge = NULL;
        int iCount, i, iX, iY;
    
        m_vctTempStr.clear();
        ParseLine(pszLine, m_vctTempStr);
    
        pEdge = new GraphvizEdge;
        pEdge->strHead  = m_vctTempStr [1];
        pEdge->strTail  = m_vctTempStr [2];
        iCount          = atoi (m_vctTempStr [3].c_str ());
    
        pEdge->ptsLine.clear();
    
        for (i = 0; i < iCount; ++i)
        {
            iX = (int)InchStrToPixel(m_vctTempStr [4 + i * 2].c_str (), TRUE);
            iY = (int)InchStrToPixel(m_vctTempStr [4 + i * 2 + 1].c_str(), FALSE);
    
            POINT pt = {iX, iY};
            pEdge->ptsLine.push_back(pt);
        }
    
        pEdge->strName = m_vctTempStr [4 + iCount * 2];
    
        return pEdge;
    }
    
    CGraphvizPlainOutputParser::CGraphvizPlainOutputParser()
    {
    
    }
    
    CGraphvizPlainOutputParser::~CGraphvizPlainOutputParser()
    {
        
    }
    
    CMyGraphvizGraph* CGraphvizPlainOutputParser::Parse( const char* pszText )
    {
    #ifdef PARSE_PLAIN
        return ParsePlainText (pszText);
    #else
        return ParseSvgText (pszText);
    #endif
    }
    
    void CGraphvizPlainOutputParser::SeparateLine( const char* pszText, std::vector<string>& vctPlainOut )
    {
        const char* pszTemp = pszText;
    
        while (pszTemp != NULL && *pszTemp != '') 
        {
            const char* pszNext = strchr (pszTemp, '
    ');
    
            if (pszNext == NULL)
            {
                break;
            }
            
            vctPlainOut.push_back(string (pszTemp, 0, pszNext - pszTemp));
            pszTemp = pszNext + 1;
        }
    }
    
    CMyGraphvizGraph* CGraphvizPlainOutputParser::ParsePlainText( const char* pszText )
    {
        static const char* TOKEN4_NODE = "node";
        static const char* TOKEN4_EDGE = "edge";
    
        std::vector <string> vctPlainOut;
    
        CMyGraphvizGraph* pGraph = NULL;
        GraphvizNode* pNode = NULL;
        GraphvizEdge* pEdge = NULL;
    
        string strToken;
    
        size_t i;
    
        // 将多行文本分解成VECTOR
        SeparateLine (pszText, vctPlainOut);
        pGraph = ParseGraphicLine(vctPlainOut [0].c_str());
    
        // 解析字符串
        for (i = 1; i < vctPlainOut.size(); ++i)
        {
            if (strnicmp (vctPlainOut [i].c_str(), TOKEN4_NODE, 4) == 0)
            {
                pGraph->AddNode (ParseNodeLine (vctPlainOut [i].c_str()));
            }
            else if (strnicmp(vctPlainOut [i].c_str(), TOKEN4_EDGE, 4) == 0)
            {
                pGraph->AddEdge (ParseEdgeLine (vctPlainOut [i].c_str()));
            }
        }
    
        return pGraph;
    }
    
    CMyGraphvizGraph* CGraphvizPlainOutputParser::ParseSvgText( const char* pszText )
    {
        TiXmlDocument doc;
        doc.Parse(pszText);
        if (doc.Error())
        {
            return NULL;
        }
     
        CMyGraphvizGraph* pGraph = new CMyGraphvizGraph;
    
        TiXmlElement* pRoot = doc.RootElement();
    
        const char* pszWidth = pRoot->Attribute("width");
        const char* pszHeight = pRoot->Attribute("height");
    
        if (pszWidth != NULL && pszHeight != NULL)
        {
            pGraph->SetWidth(atoi (pszWidth));
            pGraph->SetHeight(atoi (pszHeight));
    
            for (TiXmlElement* pItem = pRoot->FirstChildElement ()->FirstChildElement(); pItem != NULL; pItem = pItem->NextSiblingElement())
            {
                const char* pszClass = pItem->Attribute("class");
                if (pszClass == NULL)
                {
                    continue;
                }
    
                if (stricmp (pszClass, "node") == 0)
                {
                    TiXmlElement* pTitle   = pItem->FirstChildElement("text");
                    TiXmlElement* pPolygon = pItem->FirstChildElement("polygon");
    
                    if (pTitle != NULL && pPolygon != NULL)
                    {
                        const char* pszName = pTitle->GetText();
                        const char* pszPts  = pPolygon->Attribute("points");
    
                        if (pszName == NULL || pszPts == NULL)
                        {
                            assert (0);
                            continue;
                        }
    
                        vector <string> vctPoints = split(pszPts, ", ");
    
                        GraphvizNode* pNode = new GraphvizNode;
    
                        pNode->strName = pszName;
                        
                        int iTop = 9999;
                        int iBottom = 0;
                        int iLeft = 9999;
                        int iRight = 0;
    
                        int iX, iY;
                        for (size_t i = 0; i < vctPoints.size(); i += 2)
                        {
                            iX = atoi (vctPoints [i].c_str());
                            iY = atoi (vctPoints [i + 1].c_str());
                            if (iX < iLeft) iLeft = iX;
                            if (iY < iTop) iTop = iY;
                            if (iX > iRight) iRight = iX;
                            if (iY > iBottom) iBottom = iY;
                        }
                        pNode->iLeft = iLeft;
                        pNode->iTop = iTop;
                        pNode->iWidth = iRight - iLeft;
                        pNode->iHeight = iBottom - iTop;
    
                        pGraph->AddNode(pNode);
                    }
                }
                else if (stricmp (pszClass, "edge") == 0)
                {
                    TiXmlElement*  pTitle   = pItem->FirstChildElement("text");
                    TiXmlElement* pPath     = pItem->FirstChildElement("path");
                    TiXmlElement* pPolygon  = pItem->FirstChildElement("polygon");
    
                    if (pTitle != NULL && pPath != NULL && pPolygon != NULL)
                    {
                        const char* pszName = pTitle->GetText();
                        const char* pszPts  = pPolygon->Attribute("points");
                        const char* pszPath = pPath->Attribute("d");
    
                        if (pszName == NULL || pszPts == NULL || pszPath == NULL)
                        {
                            assert (0);
                            continue;
                        }
    
                        vector <string> vctPathPoints = split(pszPath, "MC, ");
                        vector <string> vctPolygonPoints = split(pszPts, ", ");
    
                        GraphvizEdge* pEdge = new GraphvizEdge;
                        pEdge->strName = pszName;
    
                        for (size_t i = 0; i < vctPathPoints.size(); i += 2)
                        {
                            POINT pt = {atoi (vctPathPoints [i].c_str()), atoi (vctPathPoints [i + 1].c_str())};
                            pEdge->ptsLine.push_back(pt);
                        }
    
                        for (size_t j = 0; j < vctPolygonPoints.size(); j += 2)
                        {
                            POINT pt = {atoi (vctPolygonPoints [j].c_str()), atoi (vctPolygonPoints [j + 1].c_str())};
                            pEdge->ptsArrow.push_back(pt);
                        }
    
                        pGraph->AddEdge(pEdge);
                    }
                }
            }
        }
        
        return pGraph;
    }
    
    CDiagramLayout::CDiagramLayout()
    {
       Init ();
    }
    
    CDiagramLayout::~CDiagramLayout()
    {
        if (m_pDot != NULL)
        {
            m_pDot->Release();
            m_pDot = NULL;
        }
    
        CoUninitialize();
    }
    
    bool CDiagramLayout::Init()
    {
        m_pView = NULL;
        m_pDot = NULL;
    
        USES_CONVERSION;
        HRESULT hr;
        CComBSTR result;
    
        hr = CoInitialize(NULL);
        if (FAILED (hr))
        {
            ASSERT(0);
            return false;
        }
    
        hr = CoCreateInstance(CLSID_DOT, NULL, CLSCTX_ALL, IID_IDOT, reinterpret_cast<LPVOID*>(&m_pDot));
    
        if (FAILED (hr))
        {
            ASSERT(0);
            return false;
        }
    
        return true;
    }
    
    CMyGraphvizGraph* CDiagramLayout::LayoutDiagram( IGraphicvizView* pView, bool fLeftToRight /*= true*/ )
    {
        if (m_pDot == NULL || pView == NULL)
        {
            return NULL;
        }
    
        m_pView = pView;
    
        string strGraphviz ("digraph G { ordering=out;");
        if (fLeftToRight)
        {
            strGraphviz += " rankdir=LR;";
        }
        else
        {
            strGraphviz += " rankdir=TB;";
        }
    
        const TPNodeList& vctNodes = pView->GetNodes();
        for (size_t i = 0; i < vctNodes.size(); ++i)
        {
            strGraphviz += GetNodeString (vctNodes [i]);
        }
    
        const TPEdgeList& vctEdges= pView->GetEdges();
        for (size_t i = 0; i < vctEdges.size(); ++i)
        {
            strGraphviz += GetEdgeString (vctEdges [i]);
        }
    
        strGraphviz += "}";
    
        return GenerateGraph(strGraphviz.c_str());
    }
    
    CMyGraphvizGraph* CDiagramLayout::GenerateGraph( const char* pszToParse)
    {
        BSTR bsSvg = NULL;
        HRESULT hr = m_pDot->ToSvg(A2BSTR(pszToParse), &bsSvg);
    
        //BSTR bsPlain = NULL;
        //m_pDot->ToPlain(A2BSTR(pszToParse), &bsPlain);
    
    #ifdef _DEBUG
        IBinaryImage* image = NULL;
        hr = m_pDot->ToPNG(A2BSTR(pszToParse), &image);
    
        if (FAILED (hr))
        {
            ASSERT (0);
            return NULL;
        }
    
        VARIANT_BOOL fRet = FALSE;
        hr = image->Save(L"d:\a.png", &fRet);
    
        image->Release();
    
        if (FAILED (hr) || !fRet)
        {
            ASSERT(0);
    
            return false;
        }
    #endif
    
    
        int nLen = SysStringLen(bsSvg); //not including NULL
        int nBytes = WideCharToMultiByte(CP_ACP, 0, bsSvg, nLen,
            NULL, NULL, NULL, NULL); //number of bytes not including NULL
        BSTR bstrA = SysAllocStringByteLen(NULL, nBytes); // allocates nBytes
        VERIFY(WideCharToMultiByte(CP_ACP, 0, bsSvg, nLen, (LPSTR)bstrA, nBytes, NULL,
            NULL) == nBytes);
    
        CMyGraphvizGraph* pGraph = m_parser.Parse((char*)bstrA);
        ::SysFreeString(bsSvg);
        ::SysFreeString(bstrA);
    
        return pGraph;
    }
    
    string CDiagramLayout::GetEdgeString( GraphvizEdge* pEdge )
    {
        static char szRet [128];
    
        sprintf (szRet, "%s -> %s [label=%s];", 
            pEdge->strHead.c_str(), 
            pEdge->strTail.c_str(), 
            pEdge->strName.c_str());
    
        return szRet;
    }
    
    string CDiagramLayout::GetNodeString( GraphvizNode* pNode )
    {
        static char szRet [512];
    
        sprintf (szRet, "%s [shape=box,width=%4.3f,height=%4.3f];",
            pNode->strName.c_str(),
            (float)pNode->iWidth / PIXEL_PER_INCH_WIDTH (),
            (float)pNode->iHeight / PIXEL_PER_INCH_HEIGHT ());
    
        return szRet;
    }
    MyGraphvizGraph.cpp
    #pragma once
    
    #include <vector>
    #include <string>
    
    using std::vector;
    using std::string;
    
    struct GraphvizNode 
    {
        string  strName;
        int     iLeft;
        int     iTop;
        int     iWidth;
        int     iHeight;
    };
    
    typedef vector <GraphvizNode*> TPNodeList;
    
    struct GraphvizEdge
    {
        string  strName;
        string  strHead; // head node
        string  strTail; // tail node
    
        vector <POINT>  ptsLine;
        vector <POINT>  ptsArrow; // used for svg
    };
    
    typedef vector <GraphvizEdge*> TPEdgeList;
    NodeEdge.h
    #pragma once
    
    #include "NodeEdge.h"
    
    #include <vector>
    using std::vector;
    
    class IGraphicvizView
    {
    public:
        virtual const TPNodeList& GetNodes () = 0;
        virtual const TPEdgeList& GetEdges () = 0;
    };
    IGraphicvizView.h

    使用:

    CDiagramLayout m_layout;
    CMyGraphvizGraph* m_pGraph;
    
    InitGraphic ();
    
    m_pGraph = m_layout.LayoutDiagram(this, m_fLeftToRight);
    
    // -----------------------------------
    void CGraphDlg::InitGraphic()
    {
        for (int i = 0; i < 5; ++i)
        {
            GraphvizNode* pNode = new GraphvizNode;
            char szName [32];
            sprintf (szName, "n%d", i);
    
            pNode->strName = szName;
            pNode->iLeft = 0;
            pNode->iTop = 0;
            pNode->iWidth = 64;
            pNode->iHeight = 64;
            
            m_vctNodes.push_back(pNode);
        }
    
        GraphvizEdge* pEdge1 = new GraphvizEdge;
        pEdge1->strHead = "n1";
        pEdge1->strTail = "n0";
        pEdge1->strName = "e0";
        m_vctEdges.push_back(pEdge1);
    
        GraphvizEdge* pEdge2 = new GraphvizEdge;
        pEdge2->strHead = "n2";
        pEdge2->strTail = "n0";
        pEdge2->strName = "e1";
        m_vctEdges.push_back(pEdge2);
    
        GraphvizEdge* pEdge3 = new GraphvizEdge;
        pEdge3->strHead = "n3";
        pEdge3->strTail = "n0";
        pEdge3->strName = "e2";
        m_vctEdges.push_back(pEdge3);
    
        GraphvizEdge* pEdge4 = new GraphvizEdge;
        pEdge4->strHead = "n4";
        pEdge4->strTail = "n3";
        pEdge4->strName = "e3";
        m_vctEdges.push_back(pEdge4);
    
        GraphvizEdge* pEdge5 = new GraphvizEdge;
        pEdge5->strHead = "n1";
        pEdge5->strTail = "n2";
        pEdge5->strName = "e4";
        m_vctEdges.push_back(pEdge5);
    
        //GraphvizEdge* pEdge6 = new GraphvizEdge;
        //pEdge6->strHead = "n0";
        //pEdge6->strTail = "n3";
        //pEdge6->strName = "e5";
        //m_vctEdges.push_back(pEdge6);
    
    }


    需要注意的是:

    1)winGraphviz語言使用的單位是英吋,所以要獲取屏幕像素與英吋的比例。

    2)如果文字長度超過圖形長度,有可能會將圖形擴寬,以便顯示完整的文字。

    畢。

  • 相关阅读:
    Jmeter学习笔记-初级
    python selenium利用 126.com id自动生成
    Python代码无法保存
    卸载python2.7出错
    python pip用法
    pycharm 连接数据库
    Python打包PyPI上传实践
    Python实现Api的Mock测试
    抓包工具使用
    Jenkins+Ant可持续集成Jmeter脚本
  • 原文地址:https://www.cnblogs.com/lin1270/p/3556966.html
Copyright © 2011-2022 走看看