Java 如何在jgraphx'中制作所需形状的边缘;s图?

Java 如何在jgraphx'中制作所需形状的边缘;s图?,java,css,graph,jgraphx,Java,Css,Graph,Jgraphx,我的图形包含具有单个输出边的矩形顶点和具有两个输出边的菱形顶点 我使用的是mxCompactTreeLayout,除了第二种类型的顶点的边缘对我来说是错误的之外,其他大部分都是正常的 我想让它们看起来像照片上的一样 而是连接到菱形底部分段的边 我应该怎么做才能使边缘看起来符合要求 编辑:添加源代码 PtNodeVertex.java package org.jsc.core.visualization.jgraphx; import com.mxgraph.model.mxCell; impo

我的图形包含具有单个输出边的矩形顶点和具有两个输出边的菱形顶点

我使用的是
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;
    }
}
`
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;
    }
}
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();
        }

    }
}
我找到了答案

一般来说,如果使用边缘样式或其他方式无法获得结果,则应编写自己的布局

它可以是从头编写的布局,也可以是扩展或组合现有布局实现功能的布局

关于我的问题,我扩展了
mxCompactTreeLayout
覆盖
setEdgePoints
方法

源代码:

PtCompactTreeLayout.java

package org.jsc.core.visualization.jgraphx;

import com.mxgraph.layout.mxCompactTreeLayout;
import com.mxgraph.model.mxGeometry;
import com.mxgraph.util.mxPoint;
import com.mxgraph.view.mxGraph;

import java.util.Collections;
import java.util.List;

public class PtCompactTreeLayout extends mxCompactTreeLayout
{
    public PtCompactTreeLayout( mxGraph graph )
    {
        super( graph, false );
    }

    @Override
    public void setEdgePoints( Object edge, List<mxPoint> points )
    {
        if ( !( edge instanceof IfElseEdge ) )
        {
            super.setEdgePoints( edge, points );

            return;
        }
        IfElseEdge ifElseEdge = ( IfElseEdge ) edge;
        IfElseNodeVertex v = ( IfElseNodeVertex ) ifElseEdge.getSource();
        mxGeometry geo = v.getGeometry();
        mxGeometry vb = ifElseEdge.getGeometry();
        if ( vb == null )
        {
            super.setEdgePoints( edge, points );

            return;
        }

        double xc = geo.getCenterX();
        double yc = geo.getCenterY();
        double w = geo.getWidth();
        double h = geo.getHeight();
        double xt;

        double yt;
        int i = ( ifElseEdge == v.getEdge( 0 ) ? 0 : 1 );
        PtNodeVertex vs = v.getSuccessor( i );

        mxGeometry sgeo0 = v.getSuccessor( 0 ).getGeometry();
        mxGeometry sgeo1 = v.getSuccessor( 1 ).getGeometry();

        double ws0 = sgeo0.getWidth();

        xt = ( i == 0 ? sgeo0.getCenterX() : sgeo1.getCenterX());
        yt = ( i == 0 ? sgeo0.getY() : sgeo1.getY() );

        vb.setTargetPoint( new mxPoint( xt, yt ) );

        double xm = xt;

        mxPoint mp = new mxPoint( xm, yc );
        vb.setPoints( Collections.singletonList( mp ) );
        vb.setSourcePoint( calcSourcePoint( v, i ) );
    }

    private mxPoint calcSourcePoint( PtNodeVertex v, int i )
    {
        mxGeometry geom = v.getGeometry();

        double w = geom.getWidth();
        double xs = ( i == 0 ? geom.getX() : geom.getX() + geom.getWidth() );
        double ys = geom.getCenterY();

        return new mxPoint( xs, ys );
    }
}
package org.jsc.core.visualization.jgraphx;
导入com.mxgraph.layout.mxCompactTreeLayout;
导入com.mxgraph.model.mxGeometry;
导入com.mxgraph.util.mxPoint;
导入com.mxgraph.view.mxgraph;
导入java.util.Collections;
导入java.util.List;
公共类PtCompactTreeLayout扩展了mxCompactTreeLayout
{
公共PtCompactTreeLayout(mxGraph)
{
超级(图,假);
}
@凌驾
公共无效集合点(对象边、列表点)
{
if(!(IfElseEdge的边实例))
{
super.setEdgePoints(边、点);
返回;
}
IfElseEdge IfElseEdge=(IfElseEdge)边;
IfElseNodeVertex v=(IfElseNodeVertex)ifElseEdge.getSource();
mxGeometry geo=v.getGeometry();
mxGeometry vb=ifElseEdge.getGeometry();
if(vb==null)
{
super.setEdgePoints(边、点);
返回;
}
double xc=geo.getCenterX();
double yc=geo.getCenterY();
double w=geo.getWidth();
双h=geo.getHeight();
双xt;
双yt;
inti=(ifElseEdge==v.getEdge(0)?0:1);
PtNodeVertex vs=v.getsuccession(i);
mxGeometry sgeo0=v.getsuccession(0).getGeometry();
mxGeometry sgeo1=v.getsuccession(1).getGeometry();
double ws0=sgeo0.getWidth();
xt=(i==0?sgeo0.getCenterX():sgeo1.getCenterX());
yt=(i==0?sgeo0.getY():sgeo1.getY());
vb.设置目标点(新mxPoint(xt,yt));
双xm=xt;
mxPoint mp=新的mxPoint(xm,yc);
vb.设定点(集合.单音表(mp));
vb.setSourcePoint(calcSourcePoint(v,i));
}
专用mxPoint calcSourcePoint(PtNodeVertex v,int i)
{
mxGeometry geom=v.getGeometry();
double w=geom.getWidth();
double xs=(i==0?geom.getX():geom.getX()+geom.getWidth());
double ys=geom.getCenterY();
返回新的mxPoint(xs,ys);
}
}
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();
        }

    }
}
package org.jsc.core.visualization.jgraphx;

import com.mxgraph.layout.mxCompactTreeLayout;
import com.mxgraph.model.mxGeometry;
import com.mxgraph.util.mxPoint;
import com.mxgraph.view.mxGraph;

import java.util.Collections;
import java.util.List;

public class PtCompactTreeLayout extends mxCompactTreeLayout
{
    public PtCompactTreeLayout( mxGraph graph )
    {
        super( graph, false );
    }

    @Override
    public void setEdgePoints( Object edge, List<mxPoint> points )
    {
        if ( !( edge instanceof IfElseEdge ) )
        {
            super.setEdgePoints( edge, points );

            return;
        }
        IfElseEdge ifElseEdge = ( IfElseEdge ) edge;
        IfElseNodeVertex v = ( IfElseNodeVertex ) ifElseEdge.getSource();
        mxGeometry geo = v.getGeometry();
        mxGeometry vb = ifElseEdge.getGeometry();
        if ( vb == null )
        {
            super.setEdgePoints( edge, points );

            return;
        }

        double xc = geo.getCenterX();
        double yc = geo.getCenterY();
        double w = geo.getWidth();
        double h = geo.getHeight();
        double xt;

        double yt;
        int i = ( ifElseEdge == v.getEdge( 0 ) ? 0 : 1 );
        PtNodeVertex vs = v.getSuccessor( i );

        mxGeometry sgeo0 = v.getSuccessor( 0 ).getGeometry();
        mxGeometry sgeo1 = v.getSuccessor( 1 ).getGeometry();

        double ws0 = sgeo0.getWidth();

        xt = ( i == 0 ? sgeo0.getCenterX() : sgeo1.getCenterX());
        yt = ( i == 0 ? sgeo0.getY() : sgeo1.getY() );

        vb.setTargetPoint( new mxPoint( xt, yt ) );

        double xm = xt;

        mxPoint mp = new mxPoint( xm, yc );
        vb.setPoints( Collections.singletonList( mp ) );
        vb.setSourcePoint( calcSourcePoint( v, i ) );
    }

    private mxPoint calcSourcePoint( PtNodeVertex v, int i )
    {
        mxGeometry geom = v.getGeometry();

        double w = geom.getWidth();
        double xs = ( i == 0 ? geom.getX() : geom.getX() + geom.getWidth() );
        double ys = geom.getCenterY();

        return new mxPoint( xs, ys );
    }
}