-1

Trying to draw a metro graph using JUNG. Is it possible to draw an edge of straight line that uses 2 or more colors in parallel, using the transformer?

ssit
  • 1
  • 1

3 Answers3

0

You could do something like this....

Copy and modify some code from the EdgeShape class (where it makes the Bowtie shape) modified to a rectangle, then set the edge fill paint, draw paint, and stroke to look like what you want

public class RectangleEdge<V,E> extends AbstractEdgeShapeTransformer<V,E> {
    private static GeneralPath rectangle;

    public RectangleEdge(int width)  {
        rectangle = new GeneralPath(GeneralPath.WIND_EVEN_ODD);
        rectangle.moveTo(0, width/2); // change points to make rectangle
        rectangle.lineTo(1, width/2);
        rectangle.lineTo(1, -width/2);
        rectangle.lineTo(0, -width/2);
        rectangle.closePath();
    }

    public Shape transform(Context<Graph<V,E>,E> context) {
        return rectangle;
    }
}



    vv.getRenderContext().setEdgeShapeTransformer(new RectangleEdge<>(4));
    vv.getRenderContext().setEdgeFillPaintTransformer(new ConstantTransformer(Color.red));
    vv.getRenderContext().setEdgeStrokeTransformer(new ConstantTransformer(new BasicStroke(2)));
    vv.getRenderContext().setEdgeDrawPaintTransformer(new ConstantTransformer(Color.blue));

Here's a picture:enter image description here

0

ok, if you want a lot of colors, try this:

    vv.getRenderer().setEdgeRenderer(
            new NotSimpleEdgeRenderer(new Color[]{
                    Color.red, Color.blue, Color.pink, Color.green, Color.magenta,
                    Color.cyan, Color.black, Color.orange, Color.yellow
            }));
    vv.getRenderContext().setEdgeStrokeTransformer(new ConstantTransformer(new BasicStroke(2)));




public class NotSimpleEdgeRenderer<V,E> extends BasicEdgeRenderer<V,E> {

    Color[] colors;
    Shape[] shapes;

    public NotSimpleEdgeRenderer(Color... colors) {
        int count = colors.length;
        this.colors = colors;
        shapes = new Shape[count];
        for (int i=0; i<count; i++) {
            shapes[i] = new Line2D.Double(0, -count/2+(2*i), 1, -count/2+(2*i));
        }
    }

    protected void drawSimpleEdge(RenderContext<V,E> rc, Layout<V,E> layout, E e) {

        GraphicsDecorator g = rc.getGraphicsContext();
        Graph<V,E> graph = layout.getGraph();
        Pair<V> endpoints = graph.getEndpoints(e);
        V v1 = endpoints.getFirst();
        V v2 = endpoints.getSecond();

        Point2D p1 = layout.transform(v1);
        Point2D p2 = layout.transform(v2);
        p1 = rc.getMultiLayerTransformer().transform(Layer.LAYOUT, p1);
        p2 = rc.getMultiLayerTransformer().transform(Layer.LAYOUT, p2);
        float x1 = (float) p1.getX();
        float y1 = (float) p1.getY();
        float x2 = (float) p2.getX();
        float y2 = (float) p2.getY();

        boolean edgeHit;
        Rectangle deviceRectangle = null;
        JComponent vv = rc.getScreenDevice();
        if(vv != null) {
            Dimension d = vv.getSize();
            deviceRectangle = new Rectangle(0,0,d.width,d.height);
        }

        AffineTransform xform = AffineTransform.getTranslateInstance(x1, y1);
        float dx = x2-x1;
        float dy = y2-y1;
        float thetaRadians = (float) Math.atan2(dy, dx);
        xform.rotate(thetaRadians);
        float dist = (float) Math.sqrt(dx*dx + dy*dy);
        xform.scale(dist, 1.0);

        Paint oldPaint = g.getPaint();
        for (int i=0; i<shapes.length; i++) {
            Shape edgeShape = shapes[i];
            edgeShape = xform.createTransformedShape(edgeShape);

            MutableTransformer vt = rc.getMultiLayerTransformer().getTransformer(Layer.VIEW);
            edgeHit = vt.transform(edgeShape).intersects(deviceRectangle);

            if(edgeHit == true) {
                Paint fill_paint = colors[i];
                if (fill_paint != null) {
                    g.setPaint(fill_paint);
                    g.fill(edgeShape);
                }
                Paint draw_paint = colors[i];
                if (draw_paint != null) {
                    g.setPaint(draw_paint);
                    g.draw(edgeShape);
                }
            }
            // restore old paint
            g.setPaint(oldPaint);
        }
    }
}

and it looks like this: enter image description here

  • Could a Transformer put into the method setEdgeRenderer() ? – ssit Jan 29 '19 at 16:26
  • Or, could the colors be differed by edge? – ssit Jan 29 '19 at 16:46
  • I would make the NotSimpleEdgeRenderer take a Map in its CTOR. Then you could modify the code to set the colors to be what you want for each edge. You'll have to change the drawSimpleEdge method to use the map. –  Jan 29 '19 at 17:29
  • Seems i need to dig into the source to see what is going on. It is a bit complicated. – ssit Jan 30 '19 at 06:52
0

I think you can do this with small modifications to the code samples I posted in previous answers. I assume that you know what colors you want for each edge. Make a Map that has the mappings from each edge to the colors you want for that edge. Then modify the code I answered with to look like this:

public class NotSimpleEdgeRenderer<V,E> extends BasicEdgeRenderer<V,E> {

    Map<E,Color[]> colorMap;

    public NotSimpleEdgeRenderer(Map<E,Color[]> colorMap) {
        this.colorMap = colorMap;
    }

    protected void drawSimpleEdge(RenderContext<V,E> rc, Layout<V,E> layout, E e) {

        GraphicsDecorator g = rc.getGraphicsContext();
        Graph<V,E> graph = layout.getGraph();
        Pair<V> endpoints = graph.getEndpoints(e);
        V v1 = endpoints.getFirst();
        V v2 = endpoints.getSecond();

        Point2D p1 = layout.transform(v1);
        Point2D p2 = layout.transform(v2);
        p1 = rc.getMultiLayerTransformer().transform(Layer.LAYOUT, p1);
        p2 = rc.getMultiLayerTransformer().transform(Layer.LAYOUT, p2);
        float x1 = (float) p1.getX();
        float y1 = (float) p1.getY();
        float x2 = (float) p2.getX();
        float y2 = (float) p2.getY();

        boolean edgeHit;
        Rectangle deviceRectangle = null;
        JComponent vv = rc.getScreenDevice();
        if(vv != null) {
            Dimension d = vv.getSize();
            deviceRectangle = new Rectangle(0,0,d.width,d.height);
        }

        AffineTransform xform = AffineTransform.getTranslateInstance(x1, y1);
        float dx = x2-x1;
        float dy = y2-y1;
        float thetaRadians = (float) Math.atan2(dy, dx);
        xform.rotate(thetaRadians);
        float dist = (float) Math.sqrt(dx*dx + dy*dy);
        xform.scale(dist, 1.0);

        Paint oldPaint = g.getPaint();

        // get the colors for this edge from the map
        Color[] colors = colorMap.get(e);
        int count = colors.length;
        // make the Shapes for this edge here
        Shape[] shapes = new Shape[count];
        for (int i=0; i<count; i++) {
             // this code offsets the lines enough to see the colors
            shapes[i] = new Line2D.Double(0, -count/2+(2*i), 1, -count/2+(2*i));
        }
        // iterate over the edge shapes and draw them with the corresponding colors
        for (int i=0; i<shapes.length; i++) {
            Shape edgeShape = shapes[i];
            edgeShape = xform.createTransformedShape(edgeShape);

            MutableTransformer vt = rc.getMultiLayerTransformer().getTransformer(Layer.VIEW);
            edgeHit = vt.transform(edgeShape).intersects(deviceRectangle);

            if(edgeHit == true) {
                Paint fill_paint = colors[i];
                if (fill_paint != null) {
                    g.setPaint(fill_paint);
                    g.fill(edgeShape);
                }
                Paint draw_paint = colors[i];
                if (draw_paint != null) {
                    g.setPaint(draw_paint);
                    g.draw(edgeShape);
                }
            }
            // restore old paint
            g.setPaint(oldPaint);
        }
    }
}
  • used an Edge object as E and put a Color array as attribute. Seems i can use e.getColors() instead of using a Map, but would need to handle the parameters of override method of drawSimpleEdge(). Is it safe to cast that E to a specific class object? – ssit Jan 31 '19 at 00:35