Question

I'm trying to display a flowchart via JGraphx, and I need a parallelogram for input/output block. But JGraphx doesn't seem to know "shape=parallelogram". It seems odd not to have a parallelogram in a library for graphs and flowcharts (and it even has "actor" shape, how come it doesn't have a parallelogram?). Maybe it's only named some other way? Or in the case there is indeed no predefined parallelogram shape, what do I do to make a vertex into parallelogram?

Was it helpful?

Solution

Finally, I've found a way to make a parallelogram, yay! Here is how I made it work.
First of all, to make a custom shape I had to create my own class extending mxBasicShape and override the createShape method.

public class Parallelogram extends mxBasicShape {
public Shape createShape(mxGraphics2DCanvas canvas, mxCellState state){
    mxCell cell = (mxCell)state.getCell();
    Polygon polygon = new Polygon();
    if(cell != null && cell.getGeometry() != null) {
        mxGeometry g = cell.getGeometry();
        int dx = (int) (cell.getGeometry().getHeight()/4.0);
        polygon.addPoint((int)(g.getX()+dx), (int)g.getY());
        polygon.addPoint((int)(g.getX()+g.getWidth()+dx), (int)g.getY());
        polygon.addPoint((int)(g.getX()+g.getWidth()-dx), (int)(g.getY()+g.getHeight()));
        polygon.addPoint((int)((int)g.getX()-dx), (int)(g.getY()+g.getHeight()));
    }
    return polygon;

}

}

The second step is adding it to a list of shapes which appeared to be stored in mxGraphics2DCanvas.

mxGraphics2DCanvas.putShape("parallelogram", new Parallelogram());

And now "shape=parallelogram" works just fine!

UPD
It appeared creating just a shape is not enough and a perimeter is also to be created. This is how I've done it:

public class ParallelogramPerimeter implements mxPerimeterFunction {
    @Override
    public mxPoint apply(mxRectangle bounds, mxCellState vertex, mxPoint next,
            boolean orthogonal) {
        double cx = bounds.getCenterX();
        double cy = bounds.getCenterY();
        double nx = next.getX();
        double ny = next.getY();
        double pi = Math.PI;
        double pi2 = Math.PI/2.0;
        double h = bounds.getHeight();
        double w = bounds.getWidth();
        double alpha = Math.atan2(h/2.0, w/2.0+h/4.0);
        double beta = Math.atan2(h/2.0, w/2.0-h/4.0);
        double t = Math.atan2(ny-cy, nx-cx);

        mxPoint p = new mxPoint();

        //Left
        if (t > pi - alpha || t < (-pi)+beta){
            Line border = new Line(cx-w/2.0+h/4.0, cy-h/2.0, cx-w/2.0-h/4.0, cy+h/2.0);
            Line line = new Line(cx, cy, nx, ny);
            p = Line.intersection(border, line);
        }
        //Top
        else if (t > (-pi)+beta && t < (0-alpha)){
            p.setY(cy-h/2.0);
            p.setX(cx + h/2.0*Math.tan(pi2+t));
        }
        //Right
        else if (t > (0-alpha) && t < beta){
            Line border = new Line(cx+w/2.0+h/4.0, cy-h/2.0, cx+w/2.0-h/4.0, cy+h/2.0);
            Line line = new Line(cx, cy, nx, ny);
            p = Line.intersection(border, line);
        }
        //Bottom
        else {
            p.setY(cy+h/2.0);
            p.setX(cx + h/2.0*Math.tan(pi2-t));
        }

        if (orthogonal){
            //Top
            if (nx >= cx-w/2.0+h/4.0 && nx <= cx+w/2.0+h/4.0 && ny <= cy-h/2.0){
                p.setX(nx);
            }
            //Bottom
            else if (nx >= cx - w/2.0-h/4.0 && nx <= cx+w/2.0-h/4.0 && ny >= cy+h/2.0){
                p.setX(nx);
            }
            //Left or right
            else{
                Line left = new Line(cx-w/2.0+h/4.0, cy-h/2.0, cx-w/2.0-h/4.0, cy+h/2.0);
                Line right = new Line(cx+w/2.0+h/4.0, cy-h/2.0, cx+w/2.0-h/4.0, cy+h/2.0);
                p = left.projection(nx, ny);
                mxPoint p1 = right.projection(nx, ny);
                boolean r = false;
                if (distance(nx, ny, p.getX(), p.getY()) > distance(nx, ny, p1.getX(), p1.getY()))
                    {
                        p = p1;
                        r = true;
                    }
                //Upper corners
                if (p.getY() < cy-h/2.0){
                    p.setY(cy-h/2.0);
                    if(r){
                        p.setX(cx+w/2.0+h/4.0);
                    }
                    else
                    {
                        p.setX(cx-w/2.0+h/4.0);
                    }
                }
                //Lower corners
                else if (p.getY() > cy+h/2.0){
                    p.setY(cy+h/2.0);
                    if(r){
                        p.setX(cx+w/2.0-h/4.0);
                    }
                    else
                    {
                        p.setX(cx-w/2.0-h/4.0);
                    }
                }
            }
        }

        return p;

    }

    private double distance(double x1, double y1, double x2, double y2){
        return Math.sqrt(Math.pow(x2-x1, 2)+Math.pow(y2-y1, 2));
    }

}

class Line{
private double a;
private double b;
private double c;

Line(double x1, double y1, double x2, double y2){
    a = y1-y2;
    b = x2-x1;
    c = x1*y2-x2*y1;
}

private Line(double a, double b, double c){
    this.a = a;
    this.b = b;
    this.c = c;
}

static private double determinant(double i, double j, double k, double l){
    return i*l - k*j;
}

static mxPoint intersection(Line first, Line second){
    double x,y;
    double denominator = determinant(first.a, first.b, second.a, second.b);
    x = 0 - determinant(first.c, first.b, second.c, second.b)/denominator;
    y = 0 - determinant(first.a, first.c, second.a, second.c)/denominator;
    return new mxPoint(x,y);
}

mxPoint projection(double x, double y){
    double a,b,c;
    if (this.b!=0){
        a=1;
        b=-(this.a*a)/this.b;
    }
    else{
        b = 1;
        a=-(this.b*b)/this.a;
    }
    c = -(a*x+b*y);
    Line line = new Line(a,b,c);
    return intersection(this, line);
}
}

Then I had to add it to the perimeters in use, whose list appeared to be not in the same place as with shapes, but in mxStyleRegistry:

mxStyleRegistry.putValue("parallelogramPerimeter", new ParallelogramPerimeter());

And finally I've used "shape=parallelogram;perimeter=parallelogramPerimeter" for a style, which now works not only for displaying the parallelogram, but also for connecting edges to it properly.

OTHER TIPS

For completeness: equilateral parallelogram is predefined: SHAPE_RHOMBUS.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top