void PivotMDS::pivotMDSLayout(GraphAttributes& GA) { const Graph& G = GA.constGraph(); bool use3D = GA.has(GraphAttributes::threeD) && DIMENSION_COUNT > 2; const int n = G.numberOfNodes(); // trivial cases if (n == 0) return; if (n == 1) { node v1 = G.firstNode(); GA.x(v1) = 0.0; GA.y(v1) = 0.0; if (use3D) GA.z(v1) = 0.0; return; } // check whether the graph is a path or not const node head = getRootedPath(G); if (head != nullptr) { doPathLayout(GA, head); } else { Array<Array<double> > pivDistMatrix; // compute the pivot matrix getPivotDistanceMatrix(GA, pivDistMatrix); // center the pivot matrix centerPivotmatrix(pivDistMatrix); // init the coordinate matrix Array<Array<double> > coord(DIMENSION_COUNT); for (int i = 0; i < coord.size(); i++) { coord[i].init(n); } // init the eigen values array Array<double> eVals(DIMENSION_COUNT); singularValueDecomposition(pivDistMatrix, coord, eVals); // compute the correct aspect ratio for (int i = 0; i < coord.size(); i++) { eVals[i] = sqrt(eVals[i]); for (int j = 0; j < n; j++) { coord[i][j] *= eVals[i]; } } // set the new positions to the graph int i = 0; for (node v : G.nodes) { GA.x(v) = coord[0][i]; GA.y(v) = coord[1][i]; if (use3D){ GA.z(v) = coord[2][i];//cout << coord[2][i] << "\n"; } ++i; } } }
static inline bool readVizAttribute( GraphAttributes &GA, node v, const pugi::xml_node tag) { const long attrs = GA.attributes(); if(string(tag.name()) == "viz:position") { if(attrs & GraphAttributes::nodeGraphics) { pugi::xml_attribute xAttr = tag.attribute("x"); pugi::xml_attribute yAttr = tag.attribute("y"); pugi::xml_attribute zAttr = tag.attribute("z"); if(!xAttr || !yAttr) { GraphIO::logger.lout() << "Missing \"x\" or \"y\" in position tag." << std::endl; return false; } GA.x(v) = xAttr.as_int(); GA.y(v) = yAttr.as_int(); // z attribute is optional and avaliable only in \a threeD mode GA.y(v) = yAttr.as_int(); if (zAttr && (attrs & GraphAttributes::threeD)) { GA.z(v) = zAttr.as_int(); } } } else if(string(tag.name()) == "viz:size") { if(attrs & GraphAttributes::nodeGraphics) { pugi::xml_attribute valueAttr = tag.attribute("value"); if (!valueAttr) { GraphIO::logger.lout() << "\"size\" attribute is missing a value." << std::endl; return false; } double size = valueAttr.as_double(); GA.width(v) = size * LayoutStandards::defaultNodeWidth(); GA.height(v) = size * LayoutStandards::defaultNodeHeight(); } } else if(string(tag.name()) == "viz:shape") { if(attrs & GraphAttributes::nodeGraphics) { pugi::xml_attribute valueAttr = tag.attribute("value"); if(!valueAttr) { GraphIO::logger.lout() << "\"shape\" attribute is missing a value." << std::endl; return false; } GA.shape(v) = toShape(valueAttr.value()); } } else if(string(tag.name()) == "viz:color") { if(attrs & GraphAttributes::nodeStyle) { return readColor(GA.fillColor(v), tag); } } else { GraphIO::logger.lout() << "Incorrect tag: \"" << tag.name() << "\"." << std::endl; return false; } return true; }
static inline void writeAttributes( std::ostream &out, const GraphAttributes &GA, const node &v) { const long flags = GA.attributes(); out << "["; bool separator = false; // Wheter to put separator before attribute. if(flags & GraphAttributes::nodeId) { writeAttribute(out, separator, "id", GA.idNode(v)); } if(flags & GraphAttributes::nodeLabel) { writeAttribute(out, separator, "label", GA.label(v)); } if(flags & GraphAttributes::nodeTemplate) { writeAttribute(out, separator, "comment", GA.templateNode(v)); } if(flags & GraphAttributes::nodeGraphics) { writeAttribute(out, separator, "width", GA.width(v)); writeAttribute(out, separator, "height", GA.height(v)); writeAttribute(out, separator, "shape", dot::toString(GA.shape(v))); out << ", pos=\"" << GA.x(v) << "," << GA.y(v); if(flags & GraphAttributes::threeD) { out << "," << GA.z(v); } out << "\""; } if(flags & GraphAttributes::nodeStyle) { writeAttribute(out, separator, "color", GA.strokeColor(v)); writeAttribute(out, separator, "fillcolor", GA.fillColor(v)); writeAttribute(out, separator, "stroketype", toString(GA.strokeType(v))); writeAttribute(out, separator, "strokewidth", GA.strokeWidth(v)); writeAttribute(out, separator, "fillpattern", toString(GA.fillPattern(v))); } if(flags & GraphAttributes::nodeType) { writeAttribute(out, separator, "type", int(GA.type(v))); } if(flags & GraphAttributes::nodeWeight) { writeAttribute(out, separator, "weight", GA.weight(v)); } out << "]"; }
void PivotMDS::pivotMDSLayout(GraphAttributes& GA) { const Graph& G = GA.constGraph(); if (G.numberOfNodes() <= 1) { // make it exception save node v; forall_nodes(v,G) { GA.x(v) = 0.0; GA.y(v) = 0.0; if (DIMENSION_COUNT > 2) GA.z(v) = 0.0; }
void StressMinimization::copyLayout( const GraphAttributes& GA, NodeArray<double>& newX, NodeArray<double>& newY, NodeArray<double>& newZ) { // copy the layout for(node v : GA.constGraph().nodes) { newX[v] = GA.x(v); newY[v] = GA.y(v); newZ[v] = GA.z(v); } }
double StressMinimization::calcStress( const GraphAttributes& GA, NodeArray<NodeArray<double> >& shortestPathMatrix, NodeArray<NodeArray<double> >& weightMatrix) { double stress = 0; for (node v = GA.constGraph().firstNode(); v != nullptr; v = v->succ()) { for (node w = v->succ(); w != nullptr; w = w->succ()) { double xDiff = GA.x(v) - GA.x(w); double yDiff = GA.y(v) - GA.y(w); double zDiff = 0.0; if (GA.has(GraphAttributes::threeD)) { zDiff = GA.z(v) - GA.z(w); } double dist = sqrt(xDiff * xDiff + yDiff * yDiff + zDiff * zDiff); if (dist != 0) { stress += weightMatrix[v][w] * (shortestPathMatrix[v][w] - dist) * (shortestPathMatrix[v][w] - dist);// } } } return stress; }
static inline void writeAttributes( std::ostream &out, const GraphAttributes &GA, const node &v) { const long flags = GA.attributes(); out << "["; bool separator = false; // Wheter to put separator before attribute. if(flags & GraphAttributes::nodeId) { writeAttribute(out, separator, "id", GA.idNode(v)); } if(flags & GraphAttributes::nodeLabel) { writeAttribute(out, separator, "label", GA.label(v)); } if(flags & GraphAttributes::nodeTemplate) { writeAttribute(out, separator, "comment", GA.templateNode(v)); } if(flags & GraphAttributes::nodeGraphics) { writeAttribute(out, separator, "width", GA.width(v)); writeAttribute(out, separator, "height", GA.height(v)); writeAttribute(out, separator, "shape", dot::toString(GA.shape(v))); out << ", pos=\"" << GA.x(v) << "," << GA.y(v); if(flags & GraphAttributes::threeD) { out << "," << GA.z(v); } out << "\""; } if(flags & GraphAttributes::nodeStyle) { writeAttribute(out, separator, "color", GA.strokeColor(v)); writeAttribute(out, separator, "fillcolor", GA.fillColor(v)); } // NOTE: Node type is weird and (probably) cannot be mapped to DOT. // NOTE: Node weight is not supported. out << "]"; }
static inline bool readVizAttribute( GraphAttributes &GA, node v, const XmlTagObject &tag) { const long attrs = GA.attributes(); if(tag.getName() == "viz:position") { if(attrs & GraphAttributes::nodeGraphics) { XmlAttributeObject *xAttr, *yAttr, *zAttr; tag.findXmlAttributeObjectByName("x", xAttr); tag.findXmlAttributeObjectByName("y", yAttr); tag.findXmlAttributeObjectByName("z", zAttr); if(!xAttr || !yAttr) { OGDF_ERROR("Missing \"x\" or \"y\" on position tag " << "(line " << tag.getLine() << ")."); return false; } std::istringstream is; is.clear(); is.str(xAttr->getValue()); is >> GA.x(v); is.clear(); is.str(yAttr->getValue()); is >> GA.y(v); // z attribute is optional and avaliable only in \a threeD mode. if(zAttr && (attrs & GraphAttributes::threeD)) { is.clear(); is.str(zAttr->getValue()); is >> GA.z(v); } }
bool GraphMLParser::readData( GraphAttributes &GA, const node &v, const pugi::xml_node nodeData) { pugi::xml_attribute keyId = nodeData.attribute("key"); if (!keyId) { GraphIO::logger.lout() << "Node data does not have a key." << endl; return false; } const long attrs = GA.attributes(); pugi::xml_text text = nodeData.text(); switch (graphml::toAttribute(m_attrName[keyId.value()])) { case graphml::a_nodeLabel: if(attrs & GraphAttributes::nodeLabel) { GA.label(v) = text.get(); } break; case graphml::a_x: if(attrs & GraphAttributes::nodeGraphics) { GA.x(v) = text.as_double(); } break; case graphml::a_y: if(attrs & GraphAttributes::nodeGraphics) { GA.y(v) = text.as_double();; } break; case graphml::a_width: if(attrs & GraphAttributes::nodeGraphics) { GA.width(v) = text.as_double(); } break; case graphml::a_height: if(attrs & GraphAttributes::nodeGraphics) { GA.height(v) = text.as_double(); } break; case graphml::a_size: if(attrs & GraphAttributes::nodeGraphics) { double size = text.as_double(); // We want to set a new size only if width and height was not set. if (GA.height(v) == GA.width(v)) { GA.height(v) = GA.width(v) = size; } } break; case graphml::a_shape: if(attrs & GraphAttributes::nodeGraphics) { GA.shape(v) = graphml::toShape(text.get()); } break; case graphml::a_z: if(attrs & GraphAttributes::threeD) { GA.z(v) = text.as_double(); } break; case graphml::a_r: if (attrs & GraphAttributes::nodeStyle && !GraphIO::setColorValue(text.as_int(), [&](uint8_t val) { GA.fillColor(v).red(val); })) { return false; } break; case graphml::a_g: if(attrs & GraphAttributes::nodeStyle && !GraphIO::setColorValue(text.as_int(), [&](uint8_t val) { GA.fillColor(v).green(val); })) { return false; } break; case graphml::a_b: if(attrs & GraphAttributes::nodeStyle && !GraphIO::setColorValue(text.as_int(), [&](uint8_t val) { GA.fillColor(v).blue(val); })) { return false; } break; case graphml::a_nodeFill: if(attrs & GraphAttributes::nodeStyle) { GA.fillColor(v) = text.get(); } break; case graphml::a_nodeStroke: if(attrs & GraphAttributes::nodeStyle) { GA.strokeColor(v) = text.get(); } break; case graphml::a_nodeType: if(attrs & GraphAttributes::nodeType) { GA.type(v) = graphml::toNodeType(text.get()); } break; case graphml::a_template: if(attrs & GraphAttributes::nodeTemplate) { GA.templateNode(v) = text.get(); } break; case graphml::a_nodeWeight: if(attrs & GraphAttributes::nodeWeight) { GA.weight(v) = text.as_int(); } break; default: GraphIO::logger.lout(Logger::LL_MINOR) << "Unknown node attribute: \"" << keyId.value() << "\"." << endl; } return true; }
void StressMinimization::nextIteration( GraphAttributes& GA, NodeArray<NodeArray<double> >& shortestPathMatrix, NodeArray<NodeArray<double> >& weights) { const Graph& G = GA.constGraph(); for (node v : G.nodes) { double newXCoord = 0.0; double newYCoord = 0.0; double newZCoord = 0.0; double& currXCoord = GA.x(v); double& currYCoord = GA.y(v); double totalWeight = 0; for (node w : G.nodes) { if (v == w) { continue; } // calculate euclidean distance between both points double xDiff = currXCoord - GA.x(w); double yDiff = currYCoord - GA.y(w); double zDiff = (GA.has(GraphAttributes::threeD)) ? GA.z(v) - GA.z(w) : 0.0; double euclideanDist = sqrt(xDiff * xDiff + yDiff * yDiff + zDiff * zDiff); // get the weight double weight = weights[v][w]; // get the desired distance double desDistance = shortestPathMatrix[v][w]; // reset the voted x coordinate // if x is not fixed if (!m_fixXCoords) { double voteX = GA.x(w); if (euclideanDist != 0) { // calc the vote voteX += desDistance * (currXCoord - voteX) / euclideanDist; } // add the vote newXCoord += weight * voteX; } // reset the voted y coordinate // y is not fixed if (!m_fixYCoords) { double voteY = GA.y(w); if (euclideanDist != 0) { // calc the vote voteY += desDistance * (currYCoord - voteY) / euclideanDist; } newYCoord += weight * voteY; } if (GA.has(GraphAttributes::threeD)) { // reset the voted z coordinate // z is not fixed if (!m_fixZCoords) { double voteZ = GA.z(w); if (euclideanDist != 0) { // calc the vote voteZ += desDistance * (GA.z(v) - voteZ) / euclideanDist; } newZCoord += weight * voteZ; } } // sum up the weights totalWeight += weight; } // update the positions if (totalWeight != 0) { if (!m_fixXCoords) { currXCoord = newXCoord / totalWeight; } if (!m_fixYCoords) { currYCoord = newYCoord / totalWeight; } if (GA.has(GraphAttributes::threeD)) { if (!m_fixZCoords) { GA.z(v) = newZCoord / totalWeight; } } } } }
static inline void writeAttributes( std::ostream &out, int depth, const GraphAttributes &GA, node v) { const long attrs = GA.attributes(); if(attrs & GraphAttributes::nodeGraphics) { const double z = (attrs & GraphAttributes::threeD) ? GA.z(v) : 0.0; GraphIO::indent(out, depth) << "<viz:position " << "x=\"" << GA.x(v) << "\" " << "y=\"" << GA.y(v) << "\" " << "z=\"" << z << "\" " << "/>\n"; // TODO: size is a scale here, so we have to know average size first. // const double size = std::max(GA.width(v), GA.height(v)); // GraphIO::indent(out, depth) << "<viz:size " // << "value=\"" << size << "\" " // << "/>\n"; const Shape shape = GA.shape(v); GraphIO::indent(out, depth) << "<viz:shape " << "value=\"" << toString(shape) << "\" " << "/>\n"; } if(attrs & GraphAttributes::nodeStyle) { const Color &color = GA.fillColor(v); const int red = color.red(); const int green = color.green(); const int blue = color.blue(); const int alpha = color.alpha(); GraphIO::indent(out, depth) << "<viz:color " << "red=\"" << red << "\" " << "green=\"" << green << "\" " << "blue=\"" << blue << "\" " << "alpha=\"" << alpha << "\" " << "/>\n"; } /* * Node type, template and weight are not supported by VIZ module. So, they * need to be written using <attvalues> tag (for estetic reasons, we write * them only if either of them is present). For convenience reasons, we use * the same names and values as in GraphML format. */ if(!(attrs & (GraphAttributes::nodeType | GraphAttributes::nodeTemplate | GraphAttributes::nodeWeight))) { return; } GraphIO::indent(out, depth) << "<attvalues>\n"; if(attrs & GraphAttributes::nodeType) { writeAttValue( out, depth + 1, graphml::a_nodeType, graphml::toString(GA.type(v))); } if(attrs & GraphAttributes::nodeTemplate) { writeAttValue(out, depth + 1, graphml::a_template, GA.templateNode(v)); } if(attrs & GraphAttributes::nodeWeight) { writeAttValue(out, depth + 1, graphml::a_nodeWeight, GA.weight(v)); } GraphIO::indent(out, depth) << "</attvalues>\n"; }
void ComponentSplitterLayout::call(GraphAttributes &GA) { // Only do preparations and call if layout is valid if (m_secondaryLayout.valid()) { //first we split the graph into its components const Graph& G = GA.constGraph(); NodeArray<int> componentNumber(G); int numberOfComponents = connectedComponents(G, componentNumber); if (numberOfComponents == 0) { return; } // intialize the array of lists of nodes contained in a CC Array<List<node> > nodesInCC(numberOfComponents); for(node v : G.nodes) nodesInCC[componentNumber[v]].pushBack(v); // Create copies of the connected components and corresponding // GraphAttributes GraphCopy GC; GC.createEmpty(G); EdgeArray<edge> auxCopy(G); for (int i = 0; i < numberOfComponents; i++) { GC.initByNodes(nodesInCC[i],auxCopy); GraphAttributes cGA(GC, GA.attributes()); //copy information into copy GA for(node v : GC.nodes) { cGA.width(v) = GA.width(GC.original(v)); cGA.height(v) = GA.height(GC.original(v)); cGA.x(v) = GA.x(GC.original(v)); cGA.y(v) = GA.y(GC.original(v)); } // copy information on edges if (GA.attributes() & GraphAttributes::edgeDoubleWeight) { for (edge e : GC.edges) { cGA.doubleWeight(e) = GA.doubleWeight(GC.original(e)); } } m_secondaryLayout.get().call(cGA); //copy layout information back into GA for(node v : GC.nodes) { node w = GC.original(v); if (w != nullptr) { GA.x(w) = cGA.x(v); GA.y(w) = cGA.y(v); if (GA.attributes() & GraphAttributes::threeD) { GA.z(w) = cGA.z(v); } } } } // rotate component drawings and call the packer reassembleDrawings(GA, nodesInCC); }//if valid }