/** * Copyright 2015 Santhosh Kumar Tekuri * * The JLibs authors license this file to you under the Apache License, * version 2.0 (the "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations * under the License. */ package jlibs.nblr.editor.layout; /** * @author Santhosh Kumar T */ import jlibs.core.lang.NotImplementedException; import jlibs.nblr.editor.RuleScene; import jlibs.nblr.rules.Edge; import jlibs.nblr.rules.Node; import org.netbeans.api.visual.graph.layout.GraphLayout; import org.netbeans.api.visual.graph.layout.UniversalGraph; import org.netbeans.api.visual.widget.LabelWidget; import org.netbeans.api.visual.widget.Widget; import java.awt.*; import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; import java.util.List; /** * @author Santhosh Kumar T */ public class RuleLayout extends GraphLayout<Node, Edge>{ private boolean animated; public static final int originX = 100; public static final int originY = 50; public static final int horizontalGap = 100; public static final int verticalGap = 25; public RuleLayout(boolean animated){ this.animated = animated; } private List<List<Node>> coordinates; @Override protected void performGraphLayout(UniversalGraph<Node, Edge> graph){ RuleScene scene = (RuleScene)graph.getScene(); if(scene.getRule()==null) return; coordinates = scene.getRule().node.coordinates(); List<Integer> columns = new ArrayList<Integer>(); columns.add(originX); for(List<Node> nodes: coordinates){ for(int col=1; col<nodes.size(); col++){ while(columns.size()<=col) columns.add(horizontalGap); Node minNode = nodes.get(col-1); Node maxNode = nodes.get(col); int width = horizontalGap; if(maxNode!=null && minNode!=null){ int middleWidth = 0; for(Edge edge: minNode.outgoing){ if(edge.target==maxNode){ LabelWidget label = (LabelWidget)scene.findWidget(edge).getChildren().get(0); if(label.getLabel()!=null) middleWidth = Math.max(middleWidth, 30+label.getPreferredBounds().width); } } for(Edge edge: maxNode.outgoing){ if(edge.target==minNode){ LabelWidget label = (LabelWidget)scene.findWidget(edge).getChildren().get(0); if(label.getLabel()!=null) middleWidth = Math.max(middleWidth, 30+label.getPreferredBounds().width); } } middleWidth = Math.max(horizontalGap, middleWidth); int minWidth = scene.findWidget(minNode).getPreferredBounds().width/2; int minLoopWidth = 0; for(Edge edge: minNode.outgoing){ if(edge.loop()){ LabelWidget label = (LabelWidget)scene.findWidget(edge).getChildren().get(0); if(label.getLabel()!=null) minLoopWidth = Math.max(minLoopWidth, label.getPreferredBounds().width/2); } } int maxWidth = scene.findWidget(maxNode).getPreferredBounds().width/2; int maxLoopWidth = 0; for(Edge edge: maxNode.outgoing){ if(edge.loop()){ LabelWidget label = (LabelWidget)scene.findWidget(edge).getChildren().get(0); if(label.getLabel()!=null) maxLoopWidth = Math.max(maxLoopWidth, label.getPreferredBounds().width/2); } } width = Math.max(horizontalGap, Math.max(minWidth, minLoopWidth) + middleWidth + Math.max(maxWidth, maxLoopWidth)); } columns.set(col, Math.max(columns.get(col), width)); } } for(int i=1; i<columns.size(); i++) columns.set(i, columns.get(i-1)+columns.get(i)); analyzeEdges(graph, columns.size()); int rowCount = coordinates.size(); int rows[] = new int[rowCount]; for(int row=0; row<rowCount; row++){ int top = 0; for(Node node: coordinates.get(row)){ if(node!=null){ top = Math.max(top, node.conLeftTop); top = Math.max(top, node.conRightTop); int loop = 0; for(Edge edge: node.outgoing){ if(edge.loop()) loop++; } top = Math.max(top, loop); } } int bottom = 0; if(row>0){ for(Node node: coordinates.get(row-1)){ if(node!=null){ bottom = Math.max(bottom, node.conLeftBottom); bottom = Math.max(bottom, node.conRightBottom); } } } int height = top+bottom; rows[row] = 50*height; if(row>0) rows[row] += rows[row-1]; if(height==0) rows[row] += originY; else rows[row] += verticalGap; } for(Node node: graph.getNodes()){ Widget widget = scene.findWidget(node); Point location = new Point(columns.get(node.col), rows[node.row]/*originY + node.row * verticalGap*/); if(animated) scene.getSceneAnimator().animatePreferredLocation(widget, location); else widget.setPreferredLocation(location); } } private void analyzeEdges(UniversalGraph<Node, Edge> graph, int colCount){ for(Node node: graph.getNodes()){ node.conLeft = false; node.conRight = false; node.conTop = false; node.conBottom = false; node.conLeftTop = 0; node.conLeftBottom = 0; node.conRightTop = 0; node.conRightBottom = 0; } int rowCount = coordinates.size(); ArrayList<Edge> edgesList = new ArrayList<Edge>(graph.getEdges()); for(int row=0; row<rowCount; row++){ for(int jump=0; jump<colCount-1; jump++){ Iterator<Edge> edges = edgesList.iterator(); while(edges.hasNext()){ Edge edge = edges.next(); if(edge.forward() && edge.sameRow(row) && edge.jump()==jump){ analyzeEdge(row, jump, edge); edges.remove(); } } edges = edgesList.iterator(); while(edges.hasNext()){ Edge edge = edges.next(); if(edge.backward() && edge.sameRow(row) && edge.jump()==jump){ analyzeEdge(row, jump, edge); edges.remove(); } } } } } private void analyzeEdge(int row, int jump, Edge edge){ Node minNode = edge.min(); Node maxNode = edge.max(); if(jump==0 && !minNode.conRight && !maxNode.conLeft){ edge.con = 0; minNode.conRight = maxNode.conLeft = true; }else{ boolean topPossible = !minNode.conTop && !maxNode.conTop; if(topPossible){ int topHeight = Math.max(minNode.conRightTop, maxNode.conLeftTop); List<Node> nodes = coordinates.get(row); for(int i=minNode.col+1; i<maxNode.col; i++){ Node node = nodes.get(i); if(node!=null){ topHeight = Math.max(topHeight, node.conRightTop); topHeight = Math.max(topHeight, node.conLeftTop); } } topHeight++; edge.con = -topHeight; minNode.conRightTop = maxNode.conLeftTop = topHeight; for(int i=minNode.col+1; i<maxNode.col; i++){ Node node = nodes.get(i); if(node!=null) node.conTop = true; } }else{ //boolean bottomPossible = minNode.conBottom==0 && maxNode.conBottom==0; int bottomHeight = Math.max(minNode.conRightBottom, maxNode.conLeftBottom); List<Node> nodes = coordinates.get(row); for(int i=minNode.col+1; i<maxNode.col; i++){ Node node = nodes.get(i); if(node!=null){ bottomHeight = Math.max(bottomHeight, node.conRightBottom); bottomHeight = Math.max(bottomHeight, node.conLeftBottom); } } bottomHeight++; edge.con = +bottomHeight; minNode.conRightBottom = maxNode.conLeftBottom = bottomHeight; for(int i=minNode.col+1; i<maxNode.col; i++){ Node node = nodes.get(i); if(node!=null) node.conBottom = true; } } } } @Override protected void performNodesLayout(UniversalGraph<Node, Edge> neUniversalGraph, Collection<Node> ns){ throw new NotImplementedException(); } }