/** * */ package agg.layout.evolutionary; //import java.awt.Dimension; import java.awt.geom.Line2D; //import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; import java.util.List; import java.util.Vector; import java.util.Hashtable; import agg.editor.impl.EdArc; import agg.editor.impl.EdGraGra; import agg.editor.impl.EdGraph; import agg.editor.impl.EdNode; /** * This class implements layout metrics. */ public class LayoutMetrics { private int epsilon; private int clusterplus, clusterminus; /** * */ public LayoutMetrics() { super(); this.epsilon = 200; // 300 this.clusterminus = 0; this.clusterplus = 0; } /** * bestimmt die Anzahl der Kantenueberschneidungen * * @param arcs * Kanten die auf eine Ueberschneidung zu pruefen sind * @return Anzahl der Kantenueberscheidungen */ public int getArcArcIntersect(final List<EdArc> arcs) { int ret = 0; EdArc arc1, arc2; // LayoutNode lnodesource1, lnodetarget1, lnodesource2, lnodetarget2; // Point2D source, target; Line2D line1, line2; for (int i = 0; i < arcs.size(); i++) {// einmal die source und target // positionen fuer alle Arcs // aktualisieren arcs.get(i).getLArc().calcSourceTargetpos(); } for (int i = 0; i < arcs.size() - 1; i++) { arc1 = arcs.get(i); if (!arc1.isLine()) { continue; } // bei kanten zu sich selbst nicht berechnen for (int j = i + 1; j < arcs.size(); j++) { arc2 = arcs.get(j); if (!arc2.isLine()) { continue; }// bei kanten zu sich selbst nicht berechnen if (arc1.getSource().equals(arc2.getSource()) || arc1.getSource().equals(arc2.getTarget()) || arc1.getTarget().equals(arc2.getSource()) || arc1.getTarget().equals(arc2.getTarget())) { // wenn die kanten direkte nachbarn sind, ueberscheiden sie // sich zwangslaeufig in dem gemeinsamen knoten und nur // dort! diese ueberschneidungen sind nicht relevant, werden // also nicht berechnet continue; } line1 = new Line2D.Double(arc1.getLArc().getSourcepos(), arc1 .getLArc().getTargetpos()); line2 = new Line2D.Double(arc2.getLArc().getSourcepos(), arc2 .getLArc().getTargetpos()); if (line1.intersectsLine(line2)) { ret++; } } } return ret; } /** * Bestimmt die Anzahl der Ueberschneidungen von Kanten und Knoten */ public int getArcNodeIntersect(final List<EdNode> nodes, final List<EdArc> arcs) { int ret = 0; EdArc arc; EdNode node; Line2D line; Rectangle2D rect; for (int i = 0; i < arcs.size(); i++) { arc = arcs.get(i); if (!arc.isLine()) { continue; }// kanten zu sich selbst werden nicht betrachtet. arc.getLArc().calcSourceTargetpos();// position der endpunkte der // kante updaten for (int j = 0; j < nodes.size(); j++) { node = nodes.get(j); if (arc.getSource().equals(node) || arc.getTarget().equals(node)) { // kanten schneiden zwangslaeufig ihre source- und // target-knoten, diese ueberschneidungen sind also // irrelevant. continue; } line = new Line2D.Double(arc.getLArc().getSourcepos(), arc .getLArc().getTargetpos()); rect = new Rectangle2D.Double(node.getX(), node.getY(), node .getWidth(), node.getHeight()); if (line.intersects(rect)) { ret++; } } } return ret; } /** * bestimmt die Anzahl der Knotenueberschneidungen * * @param nodes * Knoten die untersucht werden sollen * @param mark * wenn true, sollen die EdNodes(bzw.Layoutnodes), bei denen * ueberschneidungen gefunden worden sind, markiert werden * @return Anzahl der Knotenueberschneidungen. */ public int getNodeIntersect(final List<EdNode> nodes, boolean mark) { final Hashtable<EdNode, EdNode> intersect = new Hashtable<EdNode, EdNode>(); int ret = 0; EdNode node1, node2; // Rectangle2D rect1, rect2; for (int i = 0; i < nodes.size() - 1; i++) {// bis auf das letzte // element alle node1 = nodes.get(i); for (int j = i + 1; j < nodes.size(); j++) {// vom naechsten bis zu // letzen element, // dadurch keine // doppelberechnung node2 = nodes.get(j); if (intersect.get(node2) != null) continue; if (nodesIntersect(node1, node2)) { intersect.put(node2, node1); ret++; if (mark) { node1.getLNode().setOverlap(); node2.getLNode().setOverlap(); // node1.getLNode().setFrozen(false); // node2.getLNode().setFrozen(false); // if(node1.getLNode().isFrozenAsDefault()) // node1.getLNode().setFrozenAsDefault(false); // if(node2.getLNode().isFrozenAsDefault()) // node2.getLNode().setFrozenAsDefault(false); } } } } intersect.clear(); return ret; } private boolean nodesIntersect(EdNode n1, EdNode n2) { boolean ret = false; // System.out.println("nodesIntersect:: w/h: "+ n1.getWidth()+" / // "+n1.getHeight()); // System.out.println("nodesIntersect:: w/h: "+ n2.getWidth()+" / // "+n2.getHeight()); int w1 = n1.getWidth(); int h1 = n1.getHeight(); int x1 = n1.getLNode().getAkt().x - (w1 + 1) / 2; int y1 = n1.getLNode().getAkt().y - (h1 + 1) / 2; int w2 = n2.getWidth(); int h2 = n2.getHeight(); int x2 = n2.getLNode().getAkt().x - (w2 + 1) / 2; int y2 = n2.getLNode().getAkt().y - (h2 + 1) / 2; Rectangle2D rect1 = new Rectangle2D.Double(x1, y1, w1, h1); Rectangle2D rect2 = new Rectangle2D.Double(x2, y2, w2, h2); if (rect1.intersects(rect2)) { ret = true; // System.out.println("nodesIntersect:: "+rect1+" "+rect2); // System.out.println("nodesIntersect:: "+n1.getTypeName()+" // "+n2.getTypeName()); } return ret; } public int getOverlappingNode(Vector<LayoutNode> lnodes, int index) { int ret = -1; LayoutNode node1, node2; node1 = lnodes.get(index); for (int i = 0; i < lnodes.size(); i++) { if (i == index) { continue; } node2 = lnodes.get(i); if (nodesIntersect(node1.getEdNode(), node2.getEdNode())) { ret = i; break; } } return ret; } /** * bestimmt alle Arten von ueberschneidungen und gibt die ergebnisse * gewichtet zurueck. eine Knoten-Kanten Ueberschneidung wiegt doppelt so * schwer wie eine Kanten-Kantenueberschneidung, eine * Knoten-Knotenueberschneidung 3 mal so schwer. * * @return gewichteter wert aller Ueberschneidungsarten */ public int getOverallIntersect(final List<EdNode> nodes, final List<EdArc> arcs) { int ret = 0; ret += getArcArcIntersect(arcs); ret += getArcNodeIntersect(nodes, arcs) * 2; ret += getNodeIntersect(nodes, false) * 3; return ret; } // not used now /* private int getspaceusage(EdGraph eg) { Dimension dim = eg.getGraphDim(); int ret = 0; Vector<EdNode> nodes = eg.getNodes(); int parts = (int) Math.floor(Math.sqrt(nodes.size())); System.out.println("Parts: " + parts + " dim x: " + dim.width + " dim y: " + dim.height); ret = parts * parts; EdNode node; Rectangle2D partrect; for (int i = 0; i < parts; i++) { for (int j = 0; j < parts; j++) { // TODO: hier ist noch irgendwas falsch, hin und wieder gibts ne // NullPointerException!!! rausfinden warum und beseitigen partrect = new Rectangle2D.Double(i * (dim.width / parts), j * (dim.height / parts), dim.width / parts, dim.height / parts); for (int n = 0; n < nodes.size(); n++) { node = nodes.get(n); if (partrect.contains(node.getLNode().getAkt())) { // wenn der linke obere eckpunkt des knotens in dem // bereich von partrect sind, wird der returnwert um // eins reduziert und fuer diesen bereich keine weiteren // knoten betrachtet ret--; break; } } } } return ret; } */ /** * gibt die durchschnittliche Abweichung der tatsaechlichen zu den * bevorzugten Laengen aller Kanten in eg * * @param arcs * Kanten deren Laengen untersucht werden soll * @return durchschnittliche abweichung der aller Kantenlaengen * real--bevorzugt */ public int getAverageArcLengthDeviation(final List<EdArc> arcs) { int ret = 0; LayoutArc larc; int sum = 0; for (int i = 0; i < arcs.size(); i++) { larc = arcs.get(i).getLArc(); larc.calcAktLength(); sum += Math.abs(larc.getPrefLength() - larc.getAktLength()); } if (arcs.size() != 0) { ret = sum / arcs.size(); } return ret; } // diferenzmetriken: /** * bestimmt die durchschnittliche Positionsaenderung aller Nodes in * newedgraph im vergleich zu ihrer Position in oldedgraph. * * es werden nur nodes betrachtet die in beiden vectoren enthalten sind * Dabei werden die x und y werte der abweichung addiert. * * @param oldednodes * Knoten vor der aktuellen Transformation * @param newednodes * Knoten nach der aktuellen Transformation und dem erneuerten * Layout. */ public int getAverageNodeMove(final List<EdNode> oldednodes, final List<EdNode> newednodes) { int ret = 0; LayoutNode newlnode; EdNode oldednode, newednode; int sum = 0; int oldindex; for (int i = 0; i < newednodes.size(); i++) { newednode = newednodes.get(i); newlnode = newednode.getLNode(); oldindex = newednode.isInVectorByBasisNode(oldednodes); if (oldindex != -1) { oldednode = oldednodes.get(oldindex); sum += Math.abs(newlnode.getAkt().x - oldednode.getX()); sum += Math.abs(newlnode.getAkt().y - oldednode.getY()); } } ret = sum / newednodes.size(); return ret; } public float getSingleDistance(int nodes, int arcs, int nodeOverlapping, int arcNodeOverlapping, int arcArcOverlapping) { float B = nodes; float C = arcs; float D = nodeOverlapping; float E = arcNodeOverlapping; float F = arcArcOverlapping; float singleDistance = B / (D + 1) + (B + C) / (E + 1) + C / (F + 1); return singleDistance; } public float getMentalDistance(int nodes, int arcs, int movementsOfNodes, int movementsOfArcs) { float B = nodes; float C = arcs; float H = movementsOfNodes; float G = movementsOfArcs; float mentalDistance = H / B + G / C; return mentalDistance; } public float getLayoutQuality(float singleDistance, float mentalDistance) { float lquality = singleDistance - mentalDistance; return lquality; } public void calcClusterDiffs(EdGraph eg) { Vector<EdNode> nodes = eg.getNodes(); EdNode node; Vector<Integer> oldcluster, newcluster; int sumplus = 0, summinus = 0; // int oldid, newid; for (int i = 0; i < nodes.size(); i++) { node = nodes.get(i); oldcluster = node.getOldCluster(); newcluster = node.getCluster(); if (oldcluster == null) { sumplus += newcluster.size(); } else { for (int j = 0; j < oldcluster.size(); j++) { if (newcluster.indexOf(oldcluster.get(j)) == -1) { summinus++; } } for (int j = 0; j < newcluster.size(); j++) { if (oldcluster.indexOf(newcluster.get(j)) == -1) { sumplus++; } } } } this.clusterminus = summinus / nodes.size(); this.clusterplus = sumplus / nodes.size(); } public int getClusterMinus() { return this.clusterminus; } public int getClusterPlus() { return this.clusterplus; } public int getEpsilon() { return this.epsilon; } public void setEpsilon(int e) { this.epsilon = e; } public int getPatternMistakes(EdGraph eg) { if (eg.getGraGra() == null) return 0; int ret = 0; EdGraGra edgra = eg.getGraGra(); Vector<EdArc> arcs = eg.getArcs(); EdArc arc; LayoutPattern lp; EdNode source, target; int sum = 0; int diff = 0; for (int i = 0; i < arcs.size(); i++) { arc = arcs.get(i); Vector<LayoutPattern> paterns = edgra.getLayoutPatternsForType(arc .getType().getBasisType()); for (int j = 0; j < paterns.size(); j++) { lp = paterns.get(j); if (lp != null && lp.isEdgePattern()) { source = (EdNode) arc.getSource(); target = (EdNode) arc.getTarget(); if (lp.isXOffset()) { diff = target.getLNode().getAkt().x - source.getLNode().getAkt().x; if (lp.getOffset() > 0) {// target.x sollte groesser // als source.x sein if (!(diff > 0)) {// ist aber nicht so sum++; } } else {// offset <=0 target.x also kleiner als source.x if (diff > 0) { // ist nicht so sum++; } } } else { diff = target.getLNode().getAkt().y - source.getLNode().getAkt().y; if (lp.getOffset() > 0) {// target.y sollte groesser // als source.y sein if (!(diff > 0)) {// ist aber nicht so sum++; } } else {// offset <=0 target.y also kleiner als source.y if (diff > 0) { // ist nicht so sum++; } } } } } } ret = sum; return ret; } }