2
我的圖形包含具有單個傳出邊的矩形頂點和具有兩個傳出邊的菱形形狀的頂點。如何在jgraphx圖形中創建所需表單的邊界?
我使用的是mxCompactTreeLayout
,除了第二種類型的頂點邊緣對我來說看起來是錯誤的,大多數情況都可以。
我希望他們看起來像在畫面
相反,邊連接到菱形的底部段。
我該怎麼做才能使邊緣看起來像所需。
編輯:添加源代碼。
PtNodeVertex.java
package org.jsc.core.visualization.jgraphx;
import com.mxgraph.model.mxCell;
import com.mxgraph.model.mxGeometry;
import com.mxgraph.util.mxPoint;
import com.mxgraph.util.mxRectangle;
import com.mxgraph.util.mxUtils;
import org.jsc.core.ast.IResult;
import org.jsc.core.ptree.INodeCompleter;
import org.jsc.core.ptree.PtNode;
import org.jsc.core.term.ITerm;
import org.jsc.core.term.MethodInvocationTerm;
import org.jsc.core.term.expressions.ConditionalExpression;
import org.jsc.core.term.expressions.InstanceCreationExpression;
import org.jsc.core.term.expressions.VariableDeclarationTerm;
import org.jsc.core.term.statement.BlockTerm;
import org.jsc.core.term.statement.IfElse;
import org.jsc.core.term.statement.SwitchStatement;
import org.jsc.core.visualization.jgraphx.Edge.EdgeType;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static java.lang.Math.max;
import static java.lang.String.format;
class PtNodeVertex extends mxCell
{
private static Map<PtNode, PtNodeVertex> completingNodesToViews = new HashMap<PtNode, PtNodeVertex>();
private final PtNode node;
protected mxCell[] labels;
private List<PtNodeVertex> successors = new ArrayList<PtNodeVertex>(1);
private PtNodeVertex predecessor;
protected Edge[] edgeArray;
private int index;
PtNodeVertex(Object parent, PtNode value, String style)
{
super(parent, null, style);
node = value;
setVertex(true);
setVisible(true);
setGeometry(new mxGeometry());
setStyle("defaultVertex;fillColor=none;strokeColor=black;strokeWidth=2.5");
labels = new mxCell[ getLabelsCount() ];
createLabels();
calcBounds();
createEdges();
}
protected Edge[] createEdges()
{
int n = getMaxSuccessorsCount();
if (n == 0)
{
return new Edge[ 0 ];
}
edgeArray = new Edge[ n ];
Edge edge = new Edge(null, this);
edgeArray[ 0 ] = edge;
return edgeArray;
}
int getLabelsCount()
{
return 4;
}
int getMaxSuccessorsCount()
{
return 1;
}
final PtNode getNode()
{
return node;
}
protected void createLabels()
{
ITerm t = node.getTerm();
IResult tv = node.getTermValue();
labels[ 0 ] = createTextLabel(format("Term [%s]:", t.getClass().getSimpleName()));
labels[ 1 ] = createTextLabel(node.getTerm().toString(), true);
labels[ 2 ] = createTextLabel(format("Term value: %s", tv == null ? "n/a" : tv));
labels[ 3 ] = createTextLabel(format("State: %s", node.getState()));
}
protected void calcBounds()
{
mxGeometry b0 = labels[ 0 ].getGeometry();
mxGeometry b1 = labels[ 1 ].getGeometry();
mxGeometry b2 = labels[ 2 ].getGeometry();
mxGeometry b3 = labels[ 3 ].getGeometry();
double w = Math.max(b0.getWidth(), Math.max(b1.getWidth(), Math.max(b2.getWidth(), b3.getWidth())));
double h = b0.getHeight() + b1.getHeight() + b2.getHeight() + b3.getHeight();
mxGeometry b = getGeometry();
double x = b.getX() + 5;
double y = b.getY() + 5;
double x2 = x;//+ 5 + w01 + 5;
double y2 = y;
double x3 = x2;
double y3 = y2 + b2.getHeight();
double x0 = x;
double y0 = y3 + b3.getHeight();
double x1 = x0;
double y1 = y0 + b0.getHeight();
b.setWidth(w + 10);
b.setHeight(h + 10);
b0.setX(x0);
b0.setY(y0);
b1.setX(x1);
b1.setY(y1);
b2.setX(x2);
b2.setY(y2);
b3.setX(x3);
b3.setY(y3);
}
private static String prepareText(String s)
{
s = adjustNL(s);
// s = StringEscapeUtils.unescapeHtml4(s);
return s;
}
private static String adjustNL(String s)
{
s = s.replaceAll("\r\n", "\n");
s = s.replaceAll("\r", "\n");
return s;
}
protected mxCell createTextLabel(String text)
{
return createTextLabel(text, false);
}
protected mxCell createTextLabel(String text, boolean framed)
{
mxCell label = new mxCell();
// System.out.print(text);
text = prepareText(text);
// text = mxUtils.createHtmlDocument(new HashMap<String, Object>(), text, 1, 0,
// "<style type=\"text/css\">.selectRef { " +
// "font-size:9px;font-weight:normal; }</style>");
label.setValue(text);
mxRectangle b = mxUtils.getLabelSize(text, new HashMap<String, Object>(), false, 1.0);
mxGeometry geometry = new mxGeometry(b.getX(), b.getY(), b.getWidth(), b.getHeight());
label.setVertex(true);
label.setGeometry(geometry);
label.setVisible(true);
label.setParent(this);
this.insert(label);
label.setConnectable(false);
label.setStyle(format("defaultVertex;fillColor=none%s", (framed ? ";strokeColor=blue" : ";strokeColor=none")));
return label;
}
public static mxCell create(Object parent, PtNode node, String style)
{
PtNodeVertex vertex;
if (isCompletingNode(node))
{
vertex = new PtNodeVertex(parent, node, style);
completingNodesToViews.put(node, vertex);
}
else if (node instanceof INodeCompleter)
{
vertex = new NodeCompleterVertex(parent, node, style);
completingNodesToViews.put(node, vertex);
}
else if (node.getTerm() instanceof IfElse)
{
vertex = new IfElseNodeVertex(parent, node, style);
}
else if (node.getTerm() instanceof ConditionalExpression)
{
vertex = new ConditionalNodeVertex(parent, node, style);
}
else if (node.getTerm() instanceof SwitchStatement)
{
vertex = new SwitchNodeVertex(parent, node, style);
}
else
{
vertex = new PtNodeVertex(parent, node, style);
}
return vertex;
}
private static boolean isCompletingNode(PtNode node)
{
return node.getTerm() instanceof BlockTerm ||
node.getTerm() instanceof VariableDeclarationTerm ||
node.getTerm() instanceof InstanceCreationExpression ||
node.getTerm() instanceof MethodInvocationTerm;
}
Object getLabel(int i)
{
return labels[ i ];
}
final int getSuccessorCount()
{
return successors.size();
}
void addSuccessor(PtNodeVertex successor)
{
successors.add(successor);
successor.predecessor = this;
}
final PtNodeVertex getSuccessor(int i)
{
return successors.get(i);
}
final Edge getEdge(int i)
{
return edgeArray[ i ];
}
protected EdgeType getDefaultEdgeType()
{
return EdgeType.TYPE_1;
}
public void setIndex(int index)
{
this.index = index;
}
public int getIndex()
{
return index;
}
}
`
IfElseNodeVertex.java
package org.jsc.core.visualization.jgraphx;
import com.mxgraph.model.mxGeometry;
import com.mxgraph.util.mxPoint;
import org.jsc.core.ptree.PtNode;
import org.jsc.core.term.statement.IfElse;
import org.jsc.core.visualization.jgraphx.Edge.EdgeType;
import static java.lang.Math.max;
import static java.lang.String.format;
import static org.jsc.core.visualization.jgraphx.Direction.LEFT;
import static org.jsc.core.visualization.jgraphx.Direction.RIGHT;
class IfElseNodeVertex extends PtNodeVertex
{
private Edge thenEdge;
private Edge elseEdge;
IfElseNodeVertex(Object parent, PtNode value, String style)
{
super(parent, value, style);
if (value.getTerm().getClass() != IfElse.class)
{
throw new Error("IfElse term expected");
}
if (value.getOuts().size() != 2)
{
throw new Error("IfElse must have 2 successors\n" + value.getTerm()); //or mat have 1 (without else
}
setStyle("vRhombus;`fillColor=none;strokeColor=green");
}
@Override
protected void createLabels()
{
IfElse ifElse = (IfElse) getNode().getTerm();
String termString = format("if(%s)", ifElse.getCondition());
labels[ 0 ] = createTextLabel(termString);
labels[ 0 ].setStyle("\"vRhombus;shape=rhombus;fillColor=none;strokeColor=none");
labels[ 0 ].setGeometry(new mxGeometry(0, 0, 300, 150));
}
@Override
protected void calcBounds()
{
mxGeometry b0 = labels[ 0 ].getGeometry();
double w = 50 + b0.getWidth() + 50;
double h = max(b0.getHeight(), w * 0.618);
mxGeometry b = getGeometry();
double x = b.getX();
double y = b.getY();
double x0 = b0.getCenterX() - b0.getWidth()/2;
double y0 = b0.getCenterY();
b.setWidth(300);
b.setHeight(150);
}
@Override
public int getLabelsCount()
{
return 1;
}
protected Edge[] createEdges()
{
int n = getMaxSuccessorsCount();
if (n == 0)
{
return new Edge[ 0 ];
}
edgeArray = new Edge[ n ];
thenEdge = new IfElseEdge(null, this, "then", EdgeType.TYPE_2, LEFT);
elseEdge = new IfElseEdge(null, this, "else", EdgeType.TYPE_2, RIGHT);
edgeArray[ 0 ] = thenEdge;
edgeArray[ 1 ] = elseEdge;
return edgeArray;
}
@Override
public int getMaxSuccessorsCount()
{
return 2;
}
protected EdgeType getDefaultEdgeType()
{
return EdgeType.TYPE_2;
}
}
PtGraph.java
package org.jsc.core.visualization.jgraphx;
import com.mxgraph.canvas.mxICanvas;
import com.mxgraph.layout.mxCompactTreeLayout;
import com.mxgraph.model.mxCell;
import com.mxgraph.model.mxGeometry;
import com.mxgraph.view.mxCellState;
import com.mxgraph.view.mxGraph;
import org.jsc.core.ptree.PtEdge;
import org.jsc.core.ptree.PtNode;
public class PtGraph extends mxGraph
{
private mxCompactTreeLayout layout;
@Override
public Object insertVertex(Object parent,
String id,
Object value,
double x,
double y,
double width,
double height,
String style,
boolean relative)
{
Object v = super.insertVertex(parent, id, value, x, y, width, height, style, relative);
if (v instanceof PtNodeVertex)
{
insertLabels((PtNodeVertex) v);
}
return v;
}
protected void insertLabels(PtNodeVertex v)
{
for (int i = 0; i < v.getLabelsCount(); i++)
{
addCell(v.getLabel(i), v);
}
}
@Override
public Object createVertex(Object parent,
String id,
Object value,
double x,
double y,
double width,
double height,
String style,
boolean relative
)
{
if (!(value instanceof PtNode))
{
return super.createVertex(parent, id, value, x, y, width, height, style, relative);
}
mxCell vertex;
PtNode node = (PtNode) value;
if (node == ProcessTreeSVGView.dummyNode)
{
vertex = new HaltVertex(parent);
}
else
{
vertex = PtNodeVertex.create(parent, node, null);
}
vertex.setId(id);
vertex.setVertex(true);
vertex.setConnectable(true);
mxGeometry geometry = new mxGeometry(x, y, width, height);
vertex.setGeometry(geometry);
return vertex;
}
@Override
public Object createEdge(Object parent,
String id,
Object value,
Object source,
Object target,
String style)
{
if (!(value instanceof PtEdge))
{
return super.createEdge(parent, id, value, source, target, style);
}
PtEdge ptEdge = (PtEdge) value;
Edge edge = (Edge) Edge.create(parent, (PtNodeVertex) source, (PtNodeVertex)target, ptEdge.getContext(), null, layout);
edge.setId(id);
edge.setConnectable(false);
return edge;
}
@Override
public void drawState(mxICanvas canvas, mxCellState state, boolean drawLabel)
{
Object cell = state.getCell();
drawLabel = !(cell instanceof PtNodeVertex) && drawLabel;
super.drawState(canvas, state, drawLabel);
}
public void setLayout(mxCompactTreeLayout layout)
{
this.layout = layout;
}
}
Edge.java
package org.jsc.core.visualization.jgraphx;
import com.mxgraph.layout.mxCompactTreeLayout;
import com.mxgraph.model.mxCell;
import com.mxgraph.model.mxGeometry;
import org.jsc.core.DriveContext;
class Edge extends mxCell
{
enum EdgeType
{
TYPE_1,
TYPE_2
}
static mxCell create(Object parent,
PtNodeVertex source,
PtNodeVertex target,
DriveContext context,
String style,
mxCompactTreeLayout layout)
{
int index = target.getIndex();
Edge edge = source.getEdge(index);
edge.setValue(context);
edge.setEdge(true);
edge.setLayoutParamsForEdge(layout);
return edge;
}
protected void setLayoutParamsForEdge(mxCompactTreeLayout layout)
{
}
Edge(Object parent, PtNodeVertex source)
{
mxGeometry geo = new mxGeometry();
setEdge(true);
setGeometry(geo);
}
}
class IfElseEdge extends Edge
{
EdgeType type;
Direction direction;
IfElseEdge(Object parent, IfElseNodeVertex source, String text, EdgeType type, Direction direction)
{
super(parent, source);
this.type = type;
this.direction = direction;
mxGeometry geo = new mxGeometry(0, 0, 0, 0);
//geo.setRelative(true);
setGeometry(geo);
setStyle("rhombusEdge");
mxCell sourceLabel = new mxCell(text, new mxGeometry(-1, 0, 0, 0),
direction == Direction.RIGHT ?
"resizable=0;align=left;verticalAlign=top;" :
"resizable=0;align=right;verticalAlign=top;"
);
sourceLabel.getGeometry().setRelative(true);
sourceLabel.setConnectable(false);
sourceLabel.setVertex(true);
insert(sourceLabel);
}
@Override
protected void setLayoutParamsForEdge(mxCompactTreeLayout layout)
{
layout.setOrthogonalEdge(this, true);
layout.setEdgeStyleEnabled(this, true);
}
}
個
ProcessTreeSVGView.java
package org.jsc.core.visualization.jgraphx;
import com.mxgraph.layout.mxCompactTreeLayout;
import com.mxgraph.model.mxCell;
import com.mxgraph.swing.mxGraphComponent;
import com.mxgraph.util.mxConstants;
import com.mxgraph.view.mxGraph;
import com.mxgraph.view.mxPerimeter;
import com.mxgraph.view.mxStylesheet;
import org.apache.batik.dom.svg.SVGDOMImplementation;
import org.apache.batik.svggen.SVGGraphics2D;
import org.apache.batik.svggen.SVGGraphics2DIOException;
import org.jsc.core.ptree.ProcessTree;
import org.jsc.core.ptree.PtEdge;
import org.jsc.core.ptree.PtNode;
import org.w3c.dom.DOMImplementation;
import org.w3c.dom.Element;
import org.w3c.dom.svg.SVGDocument;
import java.awt.*;
import java.io.*;
import java.util.Hashtable;
import java.util.Map;
public class ProcessTreeSVGView
{
private static final String SVG_FILE_NAME = "C:\\users\\anthony\\g.svg";
static SVGGraphics2D g;
private final mxGraph graph;
private final mxGraphComponent graphComponent;
private String style;
public final static PtNode dummyNode = new PtNode();
public final static PtEdge dummyEdge = new PtEdge();
/**
* Constructs a new frame that is initially invisible.
* <p>
* This constructor sets the component's locale property to the value
* returned by <code>JComponent.getDefaultLocale</code>.
*
* @exception java.awt.HeadlessException if GraphicsEnvironment.isHeadless()
* returns true.
* @see java.awt.GraphicsEnvironment#isHeadless
* @see java.awt.Component#setSize
* @see java.awt.Component#setVisible
* @see javax.swing.JComponent#getDefaultLocale
*/
public ProcessTreeSVGView(ProcessTree pTree)
throws HeadlessException, SVGGraphics2DIOException, FileNotFoundException, UnsupportedEncodingException
{
System.out.printf("\nSaving Process tree(s) in '%s' ... ", SVG_FILE_NAME);
// Create an SVG document.
DOMImplementation impl = SVGDOMImplementation.getDOMImplementation();
String svgNS = SVGDOMImplementation.SVG_NAMESPACE_URI;
SVGDocument doc = (SVGDocument) impl.createDocument(svgNS, "svg", null);
// Create a converter for this document.
g = new SVGGraphics2D(doc);
graph = new PtGraph();
graphComponent = new mxGraphComponent(graph);
//First draw Graph to the SVGGraphics2D object using graph component objects draw method
drawTree(pTree);
// Do some drawing.
graphComponent.getGraphControl().drawGraph(g, true);
// Populate the document root with the generated SVG content.
Element root = doc.getDocumentElement();
g.getRoot(root);
//Once every thing is drawn on graphics find root element and update this by adding additional values for the required fields.
// Element root = g.getRoot();
Dimension size = graphComponent.getGraphControl().getPreferredSize();
root.setAttributeNS(null, "width", size.width + "");
root.setAttributeNS(null, "height", size.height + "");
root.setAttributeNS(null, "viewBox", "0 0 " + size.width + " " + size.height);
OutputStream outStream = new FileOutputStream(SVG_FILE_NAME);
Writer out = new OutputStreamWriter(outStream, "UTF-8");
g.stream(root, out);
System.out.println("done.");
runSVGViewer(SVG_FILE_NAME);
}
private void runSVGViewer(String svgFileName)
{
System.out.print("\nLoading SVG viewer ... ");
ProcessBuilder builder = new ProcessBuilder("C:\\Program Files (x86)\\Free Picture Solutions\\Free Svg Viewer\\SvgViewer.exe ", svgFileName);
try
{
builder.start();
}
catch (IOException e)
{
e.printStackTrace();
throw new Error();
}
System.out.println("done.");
}
//==================================================================================================================
private void drawTree(ProcessTree pTree)
{
Object parent = graph.getDefaultParent();
registerRhombusVertexStyle(graph);
mxCompactTreeLayout layout = setupLayout(graph);
graph.getModel().beginUpdate();
mxCell v;
try
{
v = build(pTree.getRoot(), (mxCell) parent, 0);
}
finally
{
graph.getModel().endUpdate();
}
layout.execute(parent, v);
}
private mxCompactTreeLayout setupLayout(mxGraph graph)
{
mxCompactTreeLayout layout = new mxCompactTreeLayout(graph, false);
layout.setEdgeRouting(true);
layout.setHorizontal(false);
layout.setLevelDistance(100);
layout.setNodeDistance(50);
layout.setUseBoundingBox(true);
((PtGraph) graph).setLayout(layout);
return layout;
}
private PtNodeVertex build(PtNode node, mxCell parent, int index)
{
PtNodeVertex source = insertVertex(node, parent, index);
if (node.getChildrenCount() == 0)
{
HaltVertex target = (HaltVertex) insertHaltVertex(parent);
source.addSuccessor(target);
insertEdge(parent, dummyEdge, source, target);
return source;
}
for (int i = 0; i < node.getChildrenCount(); i++)
{
PtNode node1 = node.getChild(i);
PtNodeVertex target = build(node1, parent, i);
source.addSuccessor(target);
insertEdge(parent, node1.getIn(), source, target);
}
return source;
}
PtNodeVertex insertVertex(PtNode node, mxCell parent, int index)
{
PtNodeVertex v = (PtNodeVertex) graph.insertVertex(parent, null, node, 0, 0, 0, 0, style);
v.setIndex(index);
return v;
}
private mxCell insertHaltVertex(mxCell parent)
{
mxCell v = (mxCell) graph.insertVertex(parent, null, dummyNode, 0, 0, 0, 0, style);
return v;
}
private void insertEdge(Object parent, PtEdge ptEdge, mxCell source, mxCell target)
{
if (!(source instanceof HaltVertex))
{
graph.insertEdge(parent, null, ptEdge, source, target);
}
}
private static void registerRhombusVertexStyle(mxGraph graph)
{
mxStylesheet ss = graph.getStylesheet();
Map<String, Object> style = new Hashtable<String, Object>();
style.put(mxConstants.STYLE_SHAPE, mxConstants.SHAPE_RHOMBUS);
style.put(mxConstants.STYLE_PERIMETER, mxPerimeter.RhombusPerimeter);
style.put(mxConstants.STYLE_VERTICAL_ALIGN, mxConstants.ALIGN_MIDDLE);
style.put(mxConstants.STYLE_ALIGN, mxConstants.ALIGN_CENTER);
style.put(mxConstants.STYLE_FILLCOLOR, "#C3D9FF");
style.put(mxConstants.STYLE_STROKECOLOR, "#6482B9");
style.put(mxConstants.STYLE_FONTCOLOR, "#774400");
ss.putCellStyle("vRhombus", style);
style = ss.getDefaultEdgeStyle();
style.put(mxConstants.STYLE_EDGE, mxConstants.EDGESTYLE_ELBOW);
style.put(mxConstants.STYLE_ROUTING_CENTER_Y, 0.0);
ss.putCellStyle("rhombusEdge", style);
////////////////////////////////////////////////////////////////////////////////////
Map<String, Map<String, Object>> styles = ss.getStyles();
for (String key : styles.keySet())
{
Map<String, Object> _style = styles.get(key);
System.out.printf("\n%s =\n", key);
System.out.println("---------------------");
for (String key1 : _style.keySet())
{
System.out.printf("\n%s = %s", key1, _style.get(key1));
}
System.out.println();
}
}
}
發佈您的代碼。 – CaffeineToCode