protected void replaceOperator( final Operator newOP, final VisualGraph<Operator> parent, final GraphWrapper oldGW) { newOP.cloneFrom(this); this.replaceWith(newOP); final GraphWrapper gw = new GraphWrapperOperator(newOP); final GraphBox box = parent.getBoxes().get(oldGW); box.initBox(gw); box.arrange(Arrange.values()[0]); parent.getBoxes().remove(oldGW); parent.getBoxes().put(gw, box); parent.remove(this.panel); parent.add(box.getElement()); for (final Operator preOp : this.precedingOperators) { final GraphWrapper preGW = new GraphWrapperOperator(preOp); final GraphBox preBox = parent.getBoxes().get(preGW); preBox.setLineAnnotations(preOp.drawAnnotations(parent)); } parent.revalidate(); parent.repaint(); }
/** * Method compares the average edge-length and the average node-distance with the optimal * Sugiyama-distance. * * @param graph */ public static String smallestSeparation_Test(OperatorGraph graph) { HashMap<GraphWrapper, GraphBox> boxes = graph.getBoxes(); int nodeAmount = boxes.size(); double scaleConst = 1.0; int width = GraphHelper.graphWidth(graph); int height = GraphHelper.graphHeight(graph); LinkedList<Double> distances = new LinkedList<Double>(); // compute optimal distance after Sugiyama double optiDist = scaleConst * Math.sqrt((width * height) / nodeAmount); double ariMiddle = 0.0; int edges = 0; LinkedList<Double> length = new LinkedList<Double>(); for (GraphWrapper gw : boxes.keySet()) { LinkedList<GraphWrapper> children = gw.getPrecedingElements(); for (GraphWrapper child : children) { double edgeLen = edgeLength(graph, gw, child); length.add(edgeLen); ariMiddle += edgeLen; edges++; } } ariMiddle = ariMiddle / edges; double avrDist = 0.0; for (GraphWrapper node1 : boxes.keySet()) { LinkedList<GraphWrapperIDTuple> childrenTuple = node1.getSucceedingElements(); LinkedList<GraphWrapper> children = new LinkedList<GraphWrapper>(); for (GraphWrapperIDTuple child : childrenTuple) { children.add(child.getOperator()); } GraphBox box1 = boxes.get(node1); for (GraphWrapper node2 : boxes.keySet()) { if ((!node2.equals(node1))) { GraphBox box2 = boxes.get(node2); // compute distance between node1 and node 2 int x = box2.getX() - box1.getX(); int y = box2.getY() - box1.getY(); double distance = Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2)); distances.add(distance); avrDist += distance; } } } avrDist = avrDist / distances.size(); String result = "Test: Smallest Separation:\n"; result += "The optimal distance based on Sugiyama is " + optiDist + ".\n"; result += "The average distance between not connected nodes is " + avrDist + ".\n"; result += "The average edge length is " + ariMiddle + ".\n"; return result; }
/** * Method gets the edge-length between the nodes a and b * * @param graph the graph * @param a first node * @param b second node * @return edge length */ private static double edgeLength(OperatorGraph graph, GraphWrapper a, GraphWrapper b) { double length = 0.0; HashMap<GraphWrapper, GraphBox> boxes = graph.getBoxes(); GraphBox boxA = boxes.get(a); GraphBox boxB = boxes.get(b); double x = boxA.getX() - boxB.getX(); double y = boxA.getY() - boxB.getY(); length = Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2)); return length; }
/** * Method gets the of edge-crossings, if an edge is vertical * * @param edge the vertical edge * @param edges the rest of edges in the graph * @param op the graph * @return crossing-amount */ private static int infinitSlope(Edge edge, LinkedList<Edge> edges, OperatorGraph op) { int crossings = 0; HashMap<GraphWrapper, GraphBox> boxes = op.getBoxes(); GraphBox source1 = boxes.get(edge.getSource()); GraphBox target1 = boxes.get(edge.getTarget()); for (Edge edge2 : edges) { GraphBox source2 = boxes.get(edge2.getSource()); GraphBox target2 = boxes.get(edge2.getTarget()); if (target2.getX() == source2.getX()) { double m = ((double) target2.getY() - (double) source2.getY()) / ((double) target2.getX() - (double) source2.getX()); // slope of line 2 double b = target2.getY() - (m * target2.getX()); // crossing with y-axis off line 2 double y = m * source1.getX() + b; double minY = Math.min(source1.getY(), target1.getY()); double maxY = Math.max(source1.getY(), target1.getY()); if ((y > minY) && (y < maxY)) { crossings++; } } } return crossings; }
/** * Method tests if a graph-layout is symmetric to an axis and shows the result on command-line * * @param graph */ public static String symmetry_Test(OperatorGraph graph) { int width = GraphHelper.graphWidth(graph); double symLine = width / 2; HashMap<GraphWrapper, GraphBox> boxes = graph.getBoxes(); LinkedList<GraphBox> left = new LinkedList<GraphBox>(); LinkedList<GraphBox> right = new LinkedList<GraphBox>(); String result = "Test: Axially Symmetry:\n"; // get boxes of left and right side of symmetry-line for (GraphBox box : boxes.values()) { if ((box.getX() + box.width) <= symLine) { left.add(box); } else if (box.getX() >= symLine) { right.add(box); } else { // test if node is centered on symmetry-line if ((box.getX() + (box.width / 2)) != symLine) { result += "Node not in the middle of the symmetry axe!\n"; result += "Node with x = " + box.getX() + " y = " + box.getY() + ".\n"; } } } // test if node-amount on left and right side of symmetry-line is equal if (left.size() != right.size()) { result += "Different number of nodes on both sides of the symmetry axe:\n"; result += "Left: " + left.size() + " Right: " + right.size() + "\n"; } // compute symmetric node-counter-parts int symCounter = 0; boolean sym = true; for (GraphBox box1 : left) { double symSpace = symLine - (box1.getX() + box1.width); int y = box1.getY(); for (GraphBox box2 : right) { if (y == box2.getY()) { if ((symLine + symSpace) == box2.getX()) { sym = true; symCounter++; break; } else { sym = false; } } sym = false; } } // compute number of symmetric nodes final int sumSizes = left.size() + right.size(); double p = (sumSizes == 0) ? 100 : (2 * symCounter * 100) / ((double) sumSizes); if (sym == false) { result += "The graph is " + p + "% axially symmetric!\n"; } else { result += "The graph is 100% axially symmetric!\n"; } return result; }
/** * Method tests if graph-nodes are uniform distributed over the screen. * * @param op the graph */ public static String uniformDistribution_Test(OperatorGraph op) { HashMap<GraphWrapper, GraphBox> boxes = op.getBoxes(); double width = GraphHelper.graphWidth(op); double height = GraphHelper.graphHeight(op); int nodeAmount = boxes.size(); int fieldNumber = 0; // compute number of grid-fields fieldNumber = (int) Math.floor(Math.sqrt(nodeAmount)); int[][] grid = new int[fieldNumber][fieldNumber]; // compute width and height of grid-fields int fieldSpaceX = (int) Math.ceil(width / fieldNumber); int fieldSpaceY = (int) Math.ceil(height / fieldNumber); int x = fieldSpaceX; int y = fieldSpaceY; // compute how many nodes are in the grid-fields for (int i = 0; i < fieldNumber; i++) { for (int j = 0; j < fieldNumber; j++) { for (GraphBox box : boxes.values()) { if ((box.getX() + box.width < x) && (box.getX() >= x - fieldSpaceX) && (box.getY() + box.height < y) && (box.getY() >= y - fieldSpaceY)) { grid[i][j]++; } } y += fieldSpaceY; } x += fieldSpaceX; y = fieldSpaceY; } // test if nodes are uniformly distributed int notUniform = 0; boolean equ = true; for (int i = 0; i < fieldNumber; i++) { for (int j = 0; j < fieldNumber; j++) { if ((grid[i][j] > 2) || (grid[i][j] < 1)) { equ = false; notUniform++; } } } String result = "Test: uniform distribution of nodes:\n"; result += "Size of grid: " + fieldNumber + " * " + fieldNumber + "\n"; System.out.println(""); if (equ == false) { int fields = fieldNumber * fieldNumber; // compute percent of uniform node distribution double p = 100 - ((notUniform * 100) / fields); result += "The graph is about " + p + "% uniformly filled with nodes...\n"; } else { result += "All grid fields contain 1 or 2 nodes.\n"; result += "The graph is 100% uniformly filled with nodes...\n"; } return result; }
/** * Method computes the amount of edge-crossing in a graph and displays the result on command-line */ public static String minEdgeCrossing_Test(OperatorGraph graph) { HashMap<GraphWrapper, GraphBox> boxes = graph.getBoxes(); LinkedList<Edge> edges = new LinkedList<Edge>(); int crossCounter = 0; // get all edges of the graph for (GraphWrapper gw : boxes.keySet()) { LinkedList<GraphWrapperIDTuple> children = gw.getSucceedingElements(); for (GraphWrapperIDTuple child : children) { Edge edge = new Edge(gw, child.getOperator(), 0); edges.add(edge); } } int edgecount = 0; LinkedList<Edge> visited = new LinkedList<Edge>(); for (int i = 0; i < edges.size(); i++ /** Edge edge1 : edges* */ ) { Edge edge1 = edges.get(i); edgecount++; GraphBox sourceE1 = boxes.get(edge1.getSource()); GraphBox targetE1 = boxes.get(edge1.getTarget()); // edge 1 is vertical if ((targetE1.getX() == sourceE1.getX())) { crossCounter = infinitSlope(edge1, edges, graph); } else { double m1 = ((double) targetE1.getY() - (double) sourceE1.getY()) / ((double) targetE1.getX() - (double) sourceE1.getX()); // slope of line 1 double b1 = (targetE1.getY()) - (m1 * targetE1.getX()); // slope of line 1 for (int j = 0; j < edges.size(); j++ /** Edge edge2 : edges* */ ) { Edge edge2 = edges.get(j); if ((!edge2.equals(edge1)) && (!visited.contains(edge2)) && (m1 != 0.0)) { GraphBox sourceE2 = boxes.get(edge2.getSource()); GraphBox targetE2 = boxes.get(edge2.getTarget()); // edge 2 is vertical if (targetE2.getX() == sourceE2.getX()) { crossCounter += infinitSlope(edge2, edge1, graph); } else { double m2 = ((double) targetE2.getY() - (double) sourceE2.getY()) / ((double) targetE2.getX() - (double) sourceE2.getX()); // slope of line 2 double b2 = (targetE2.getY()) - (m2 * targetE2.getX()); // crossing with y-axis of line 2 double x = ((b2 - b1) / (m1 - m2)); double y = ((m1 * x) + b1); // cross-point-coordinates of the 2 lines double maxXe1 = Math.max(sourceE1.getX(), targetE1.getX()); double maxXe2 = Math.max(sourceE2.getX(), targetE2.getX()); double minXe1 = Math.min(sourceE1.getX(), targetE1.getX()); double minXe2 = Math.min(sourceE2.getX(), targetE2.getX()); double maxYe1 = Math.max(sourceE1.getY(), targetE1.getY()); double maxYe2 = Math.max(sourceE2.getY(), targetE2.getY()); double minYe1 = Math.min(sourceE1.getY(), targetE1.getY()); double minYe2 = Math.min(sourceE2.getY(), targetE2.getY()); // test if cross-point is part of edge 1 if ((x < maxXe1) && (x > minXe1) && (y < maxYe1) && (y > minYe1)) { if ((x < maxXe2) && (x > minXe2) && (y < maxYe2) && (y > minYe2)) { crossCounter++; } } } // visited.add(edge2); } } } visited.add(edge1); } return "Test: Crossing Edges:\nThe number of edges crossing is " + crossCounter + " of total " + edgecount + " edges.\n"; }