bool ClassLayouter::computeNormally() { FuzzyDependAttr::Ptr fuzzyAttr = m_parent->getAttr<FuzzyDependAttr>(); if (!fuzzyAttr) return false; Graph G; GraphAttributes GA(G, GraphAttributes::nodeGraphics | GraphAttributes::edgeGraphics | GraphAttributes::nodeLabel | GraphAttributes::nodeTemplate | GraphAttributes::edgeDoubleWeight); SparseMatrix& veMat = fuzzyAttr->vtxEdgeMatrix(); VectorXd edgeWeight = fuzzyAttr->edgeWeightVector(); if (edgeWeight.size() != veMat.cols()) { m_status |= WARNING_USE_DEFAULT_EDGE_WEIGHT; edgeWeight.setOnes(veMat.cols()); } const int nNodes = veMat.rows(); const int nEdges = veMat.cols(); if (nNodes <= 0 || nNodes != m_childList.size() || nEdges < 1) { m_status |= WARNING_NO_EDGE; return false; } bool isUseOrthoLayout = nEdges < 50; vector<node> nodeArray; vector<edge> edgeArray; NodeArray<float> nodeSize(G); EdgeArray<double> edgeLength(G); for (int i = 0; i < nNodes; ++i) { nodeArray.push_back(G.newNode()); float r = m_nodeRadius[i]; GA.width(nodeArray.back()) = r*2; GA.height(nodeArray.back()) = r*2; nodeSize[nodeArray.back()] = r * 2; } float maxEdgeWeight = edgeWeight.maxCoeff(); float minEdgeWeight = edgeWeight.minCoeff(); for (int ithEdge = 0; ithEdge < nEdges; ++ithEdge) { int src, dst; GraphUtility::getVtxFromEdge(veMat, ithEdge, src, dst); edgeArray.push_back(G.newEdge(nodeArray[src], nodeArray[dst])); //GA.doubleWeight(edgeArray.back()) = edgeWeight[ithEdge]; edgeLength[edgeArray.back()] = 1;//log(edgeWeight[ithEdge] + 1); } // add dummy vertices and edges in order to merge parallel edge segments attached to the same node VectorXi compVec; int nComp = 1; const float dummyNodeRadius = 0.01; if(m_isMergeEdgeEnd && isUseOrthoLayout && GraphUtility::findConnectedComponentsVE( veMat, compVec, nComp )) { bool* isCompSet = new bool[nComp]; for (int i = 0; i < nComp; ++i) isCompSet[i] = false; // add a dummy node and edge for each connect component for (int ithVtx = 0; ithVtx < compVec.size(); ++ithVtx) { int ithCmp = compVec[ithVtx]; if (isCompSet[ithCmp] == false) { // add dummy node and set its radius nodeArray.push_back(G.newNode()); GA.width(nodeArray.back()) = dummyNodeRadius; GA.height(nodeArray.back()) = dummyNodeRadius; // add dummy edge edgeArray.push_back(G.newEdge(nodeArray[ithVtx], nodeArray.back())); isCompSet[ithCmp] = true; } } delete[] isCompSet; } MatrixXd pos; pos.resize(nNodes, 2); try { if (isUseOrthoLayout) { PlanarizationLayout layouter; OrthoLayout *ol = new OrthoLayout; float sep = max(m_nodeRadius.sum() / nNodes, LayoutSetting::s_baseRadius * 12); ol->separation(sep); ol->cOverhang(0.1); ol->setOptions(1+4); layouter.setPlanarLayouter(ol); layouter.call(GA); for (int v = 0; v < nNodes; ++v) { double x = GA.x(nodeArray[v]); double y = GA.y(nodeArray[v]); pos(v,0) = x; pos(v,1) = y; } } else { FMMMLayout layouter; //CircularLayout layouter; float avgRadius = m_nodeRadius.sum(); layouter.unitEdgeLength(avgRadius * 4); //layouter.call(GA, edgeLength); layouter.call(GA); // layouter.resizeDrawing(true); // layouter.resizingScalar(3); for (int v = 0; v < nNodes; ++v) { double x = GA.x(nodeArray[v]); double y = GA.y(nodeArray[v]); pos(v,0) = x; pos(v,1) = y; } MDSPostProcesser m_postProcessor(5000, 1, 1.0, 1.0, LayoutSetting::s_baseRadius * 2); m_postProcessor.set2DPos(pos, m_nodeRadius.cast<double>()); m_postProcessor.compute(); m_postProcessor.getFinalPos(pos); } } catch(...)//AlgorithmFailureException e { return false; } VectorXd halfSize,offset; GeometryUtility::moveToOrigin(pos, m_nodeRadius.cast<double>(), halfSize, &offset); m_nodePos = pos.cast<float>(); // postprocessing, and set edges float edgeBound[2] = {halfSize[0], halfSize[1]}; if (isUseOrthoLayout) { for(int ithEdge = 0; ithEdge < nEdges; ++ithEdge) { DPolyline& p = GA.bends(edgeArray[ithEdge]); int nPoints = p.size(); BSpline splineBuilder(5, BSpline::BSPLINE_OPEN_UNIFORM, true); int ithPnt = 0; for (DPolyline::iterator pLine = p.begin(); pLine != p.end(); ++pLine, ++ithPnt) { float px = (*pLine).m_x + offset[0]; float py = (*pLine).m_y + offset[1]; splineBuilder.addPoint(px,py); splineBuilder.addPoint(px,py); } splineBuilder.computeLine((nPoints) * 5); const QList<QPointF>& curve = splineBuilder.getCurvePnts(); m_edgeParam.push_back(EdgeParam()); EdgeParam& ep = m_edgeParam.back(); ep.m_points.resize(curve.size(),2); for (int i = 0; i < curve.size(); ++i) { ep.m_points(i, 0) = curve[i].x(); ep.m_points(i, 1) = curve[i].y(); edgeBound[0] = qMax(edgeBound[0], (float)qAbs(curve[i].x())); edgeBound[1] = qMax(edgeBound[1], (float)qAbs(curve[i].y())); } ep.m_weight = edgeWeight[ithEdge]; } } else { DelaunayCore::DelaunayRouter router; router.setLaneWidthRatio(2); router.setSmoothParam(0.5,2); router.setEndPointNormalRatio(0.8); computeEdgeRoute(router); } m_totalRadius = qSqrt(edgeBound[0]*edgeBound[0] + edgeBound[1]*edgeBound[1]); return true; }
CRevisionGraphWnd::CRevisionGraphWnd() : CWnd() , m_SelectedEntry1(NULL) , m_SelectedEntry2(NULL) , m_HeadNode(nullptr) , m_pDlgTip(NULL) , m_nFontSize(12) , m_bTweakTrunkColors(true) , m_bTweakTagsColors(true) , m_fZoomFactor(DEFAULT_ZOOM) , m_ptRubberEnd(0,0) , m_ptMoveCanvas(0,0) , m_bShowOverview(false) , m_parent (NULL) , m_hoverIndex (NULL) , m_hoverGlyphs (0) , m_tooltipIndex(nullptr) , m_showHoverGlyphs (false) , m_bIsCanvasMove(false) , m_previewWidth(0) , m_previewHeight(0) , m_previewZoom(1) , m_dwTicks(0) , m_logEntries(&m_LogCache) , m_bCurrentBranch(false) , m_bLocalBranches(FALSE) { memset(&m_lfBaseFont, 0, sizeof(LOGFONT)); std::fill_n(m_apFonts, MAXFONTS, (CFont*)NULL); WNDCLASS wndcls; HINSTANCE hInst = AfxGetInstanceHandle(); #define REVGRAPH_CLASSNAME _T("Revgraph_windowclass") if (!(::GetClassInfo(hInst, REVGRAPH_CLASSNAME, &wndcls))) { // otherwise we need to register a new class wndcls.style = CS_DBLCLKS | CS_OWNDC; wndcls.lpfnWndProc = ::DefWindowProc; wndcls.cbClsExtra = wndcls.cbWndExtra = 0; wndcls.hInstance = hInst; wndcls.hIcon = NULL; wndcls.hCursor = AfxGetApp()->LoadStandardCursor(IDC_ARROW); wndcls.hbrBackground = (HBRUSH) (COLOR_WINDOW + 1); wndcls.lpszMenuName = NULL; wndcls.lpszClassName = REVGRAPH_CLASSNAME; RegisterClass(&wndcls); } m_bTweakTrunkColors = CRegDWORD(_T("Software\\TortoiseGit\\RevisionGraph\\TweakTrunkColors"), TRUE) != FALSE; m_bTweakTagsColors = CRegDWORD(_T("Software\\TortoiseGit\\RevisionGraph\\TweakTagsColors"), TRUE) != FALSE; m_szTip[0] = 0; m_wszTip[0] = 0; m_GraphAttr.init(this->m_Graph, ogdf::GraphAttributes::nodeGraphics | ogdf::GraphAttributes::edgeGraphics | ogdf:: GraphAttributes::nodeLabel | ogdf::GraphAttributes::nodeColor | ogdf::GraphAttributes::edgeColor | ogdf::GraphAttributes::edgeStyle | ogdf::GraphAttributes::nodeStyle | ogdf::GraphAttributes::nodeTemplate); m_SugiyamLayout.setRanking(::new ogdf::OptimalRanking()); m_SugiyamLayout.setCrossMin(::new ogdf::MedianHeuristic()); double pi = 3.1415926; m_ArrowCos = cos(pi/8); m_ArrowSin = sin(pi/8); this->m_ArrowSize = 8; #if 0 ogdf::node one = this->m_Graph.newNode(); ogdf::node two = this->m_Graph.newNode(); ogdf::node three = this->m_Graph.newNode(); ogdf::node four = this->m_Graph.newNode(); m_GraphAttr.width(one)=100; m_GraphAttr.height(one)=200; m_GraphAttr.width(two)=100; m_GraphAttr.height(two)=100; m_GraphAttr.width(three)=100; m_GraphAttr.height(three)=20; m_GraphAttr.width(four)=100; m_GraphAttr.height(four)=20; m_GraphAttr.labelNode(one)="One"; m_GraphAttr.labelNode(two)="Two"; m_GraphAttr.labelNode(three)="three"; this->m_Graph.newEdge(one, two); this->m_Graph.newEdge(one, three); this->m_Graph.newEdge(two, four); this->m_Graph.newEdge(three, four); #endif FastHierarchyLayout *pOHL = ::new FastHierarchyLayout; //It will auto delte when m_SugiyamLayout destory pOHL->layerDistance(30.0); pOHL->nodeDistance(25.0); m_SugiyamLayout.setLayout(pOHL); #if 0 //this->m_OHL.layerDistance(30.0); //this->m_OHL.nodeDistance(25.0); //this->m_OHL.weightBalancing(0.8); m_SugiyamLayout.setLayout(&m_OHL); m_SugiyamLayout.call(m_GraphAttr); #endif #if 0 PlanarizationLayout pl; FastPlanarSubgraph *ps = ::new FastPlanarSubgraph; ps->runs(100); VariableEmbeddingInserter *ves = ::new VariableEmbeddingInserter; ves->removeReinsert(EdgeInsertionModule::rrAll); pl.setSubgraph(ps); pl.setInserter(ves); EmbedderMinDepthMaxFaceLayers *emb = ::new EmbedderMinDepthMaxFaceLayers; pl.setEmbedder(emb); OrthoLayout *ol =::new OrthoLayout; ol->separation(20.0); ol->cOverhang(0.4); ol->setOptions(2+4); ol->preferedDir(OrthoDir::odEast); pl.setPlanarLayouter(ol); pl.call(m_GraphAttr); node v; forall_nodes(v,m_Graph) { TRACE(_T("node x %f y %f %f %f\n"),/* m_GraphAttr.idNode(v), */ m_GraphAttr.x(v), m_GraphAttr.y(v), m_GraphAttr.width(v), m_GraphAttr.height(v) ); }