Search in sources :

Example 1 with LegacyCustomNodeShape

use of org.cytoscape.graph.render.immed.nodeshape.LegacyCustomNodeShape in project cytoscape-impl by cytoscape.

the class GraphGraphics method defineCustomNodeShape.

/**
 * The custom node shape that is defined is a polygon specified by the
 * coordinates supplied. The polygon must meet several constraints listed
 * below.
 * <p>
 * If we define the value xCenter to be the average of the minimum and
 * maximum X values of the vertices and if we define yCenter likewise, then
 * the specified polygon must meet the following constraints:
 * <ol>
 * <li>Each polygon line segment must have nonzero length.</li>
 * <li>No two consecutive polygon line segments can be parallel (this
 * essentially implies that the polygon must have at least three vertices).</li>
 * <li>No two distinct non-consecutive polygon line segments may intersect
 * (not even at the endpoints); this makes possible the notion of interior
 * of the polygon.</li>
 * <li>The polygon must be star-shaped with respect to the point (xCenter,
 * yCenter); a polygon is said to be <i>star-shaped with respect to a point
 * (a,b)</i> if and only if for every point (x,y) in the interior or on the
 * boundary of the polygon, the interior of the segment (a,b)->(x,y) lies in
 * the interior of the polygon.</li>
 * <li>The path traversed by the polygon must be clockwise where +X points
 * right and +Y points down.</li>
 * </ol>
 * <p>
 * In addition to these constraints, when rendering custom nodes with
 * nonzero border width, possible problems may arise if the border width is
 * large with respect to the kinks in the polygon.
 *
 * @param coords
 *            vertexCount * 2 consecutive coordinate values are read from
 *            this array starting at coords[offset]; coords[offset],
 *            coords[offset + 1], coords[offset + 2], coords[offset + 3] and
 *            so on are interpreted as x0, y0, x1, y1, and so on; the
 *            initial vertex need not be repeated as the last vertex
 *            specified.
 * @param offset
 *            the starting index of where to read coordinates from in the
 *            coords parameter.
 * @param vertexCount
 *            the number of vertices to read from coords; vertexCount * 2
 *            entries in coords are read.
 * @return the node shape identifier to be used in future rendering calls
 *         (to be used as parameter nodeShape in method drawNodeFull()).
 * @exception IllegalArgumentException
 *                if any of the constraints are not met, or if the specified
 *                polygon has more than CUSTOM_SHAPE_MAX_VERTICES vertices.
 * @exception IllegalStateException
 *                if too many custom node shapes are already defined; a
 *                little over one hundered custom node shapes can be
 *                defined.
 */
public final byte defineCustomNodeShape(final float[] coords, final int offset, final int vertexCount) {
    if (m_debug) {
        checkDispatchThread();
    }
    if (vertexCount > CUSTOM_SHAPE_MAX_VERTICES) {
        throw new IllegalArgumentException("too many vertices (greater than " + CUSTOM_SHAPE_MAX_VERTICES + ")");
    }
    final double[] polyCoords = new double[vertexCount * 2];
    for (int i = 0; i < polyCoords.length; i++) polyCoords[i] = coords[offset + i];
    // Normalize the polygon so that it spans [-0.5, 0.5] x [-0.5, 0.5].
    double xMin = Double.POSITIVE_INFINITY;
    double yMin = Double.POSITIVE_INFINITY;
    double xMax = Double.NEGATIVE_INFINITY;
    double yMax = Double.NEGATIVE_INFINITY;
    for (int i = 0; i < polyCoords.length; ) {
        xMin = Math.min(xMin, coords[i]);
        xMax = Math.max(xMax, coords[i++]);
        yMin = Math.min(yMin, coords[i]);
        yMax = Math.max(yMax, coords[i++]);
    }
    final double xDist = xMax - xMin;
    if (xDist == 0.0d)
        throw new IllegalArgumentException("polygon does not move in the X direction");
    final double yDist = yMax - yMin;
    if (yDist == 0.0d)
        throw new IllegalArgumentException("polygon does not move in the Y direction");
    final double xMid = (xMin + xMax) / 2.0d;
    final double yMid = (yMin + yMax) / 2.0d;
    for (int i = 0; i < polyCoords.length; ) {
        double foo = (polyCoords[i] - xMid) / xDist;
        polyCoords[i++] = Math.min(Math.max(-0.5d, foo), 0.5d);
        foo = (polyCoords[i] - yMid) / yDist;
        polyCoords[i++] = Math.min(Math.max(-0.5d, foo), 0.5d);
    }
    int yInterceptsCenter = 0;
    for (int i = 0; i < vertexCount; i++) {
        final double x0 = polyCoords[i * 2];
        final double y0 = polyCoords[(i * 2) + 1];
        final double x1 = polyCoords[((i * 2) + 2) % (vertexCount * 2)];
        final double y1 = polyCoords[((i * 2) + 3) % (vertexCount * 2)];
        final double x2 = polyCoords[((i * 2) + 4) % (vertexCount * 2)];
        final double y2 = polyCoords[((i * 2) + 5) % (vertexCount * 2)];
        final double distP0P1 = Math.sqrt(((x1 - x0) * (x1 - x0)) + ((y1 - y0) * (y1 - y0)));
        // Too close to distance zero.
        if ((float) distP0P1 == 0.0f) {
            throw new IllegalArgumentException("a line segment has distance [too close to] zero");
        }
        final double distP2fromP0P1 = ((((y0 - y1) * x2) + ((x1 - x0) * y2) + (x0 * y1)) - (x1 * y0)) / distP0P1;
        // Too close to parallel.
        if ((float) distP2fromP0P1 == 0.0f) {
            throw new IllegalArgumentException("either a line segment has distance [too close to] zero or " + "two consecutive line segments are [too close to] parallel");
        }
        final double distCenterFromP0P1 = ((x0 * y1) - (x1 * y0)) / distP0P1;
        if (!((float) distCenterFromP0P1 > 0.0f)) {
            throw new IllegalArgumentException("polygon is going counter-clockwise or is not star-shaped with " + "respect to center");
        }
        if ((Math.min(y0, y1) < 0.0d) && (Math.max(y0, y1) >= 0.0d)) {
            yInterceptsCenter++;
        }
    }
    if (yInterceptsCenter != 2) {
        throw new IllegalArgumentException("the polygon self-intersects (we know this because the winding " + "number of the center is not one)");
    }
    // polyCoords now contains a polygon spanning [-0.5, 0.5] X [-0.5, 0.5]
    // that passes all of the criteria.
    final byte nextCustomShapeType = (byte) (m_lastCustomShapeType + 1);
    if (nextCustomShapeType < 0)
        throw new IllegalStateException("too many custom node shapes are already defined");
    m_lastCustomShapeType++;
    nodeShapes.put(nextCustomShapeType, new LegacyCustomNodeShape(polyCoords, nextCustomShapeType));
    return nextCustomShapeType;
}
Also used : LegacyCustomNodeShape(org.cytoscape.graph.render.immed.nodeshape.LegacyCustomNodeShape) Paint(java.awt.Paint) TexturePaint(java.awt.TexturePaint)

Aggregations

Paint (java.awt.Paint)1 TexturePaint (java.awt.TexturePaint)1 LegacyCustomNodeShape (org.cytoscape.graph.render.immed.nodeshape.LegacyCustomNodeShape)1