use of org.cytoscape.view.presentation.property.values.ArrowShape in project cytoscape-impl by cytoscape.
the class GraphGraphics method drawEdgeFull.
/**
* Draws an edge with medium to high detail, depending on parameters
* specified. Something is rendered in all cases except where the length of
* the edge is zero (because in that case directionality cannot be
* determined for at least some arrowheads).
* <p>
* The arrow types must each be one of the ARROW_* constants. The arrow at
* endpoint 1 is always "on top of" the arrow at endpoint 0 if they overlap
* because the arrow at endpoint 0 gets rendered first.
* <p>
* If an arrow other than NONE is rendered, its size must be greater
* than or equal to edge thickness specified. The table below describes, to
* some extent, the nature of each arrow type. <blockquote><table
* border="1" cellpadding="5" cellspacing="0">
* <tr>
* <th>arrow type</th>
* <th>description</th>
* </tr>
* <tr>
* <td>NONE</td>
* <td>the edge line segment has endpoint specified, and the line segment
* has a round end (center of round semicircle end exactly equal to endpoint
* specified); arrow size and arrow paint are ignored</td>
* </tr>
* <tr>
* <td>DELTA</td>
* <td>the sharp tip of the arrowhead is exactly at the endpint specified;
* the delta is as wide as the arrow size specified and twice that in length</td>
* </tr>
* <tr>
* <td>DIAMOND</td>
* <td>the sharp tip of the arrowhead is exactly at the endpoint specified;
* the diamond is as wide as the arrow size specified and twice that in
* length</td>
* </tr>
* <tr>
* <td>CIRCLE</td>
* <td>the disc arrowhead is placed such that its center is at the
* specified endpoint; the diameter of the disk is the arrow size specified</td>
* </tr>
* <tr>
* <td>T</td>
* <td>the center of the tee intersection lies at the specified endpoint;
* the width of the top of the tee is one quarter of the arrow size
* specified, and the span of the top of the tee is two times the arrow size</td>
* </tr>
* <tr>
* <td>HALF_TOP</td>
* <td>Draws a line the width of the stroke away from the node at the midpoint
* between the edge and the node on the "top" of the edge.</td>
* </tr>
* <tr>
* <td>HALF_BOTTOM</td>
* <td>Draws a line the width of the stroke away from the node at the midpoint
* between the edge and the node on the "bottom" of the edge.</td>
* </tr>
* <tr>
* <td>NEW: OPEN_CIRCLE & OPEN_DELTA</td>
* <td>Unfilled versions of existing arrows.</td>
* </tr>
* <tr>
* <td>NEW: SQUARE & OPEN_SQUARE</td>
* <td>Draws a square at the end of the edge.</td>
* </tr>
* <tr>
* <td>NEW: HALF_CIRCLE & OPEN_HALF_CIRCLE</td>
* <td>Draws a 180 degree arc, filled or open</td>
* </tr>
* <tr>
* <td>NEW: CROSS_DELTA & OPEN_CROSS_DELTA</td>
* <td>Draws a cross hatch line behind the arrow head. Comes in flavors: filled or open</td>
* </tr>
* </table></blockquote>
* <p>
* Note that if the edge segment length is zero then nothing gets rendered.
* <p>
* This method will not work unless clear() has been called at least once
* previously.
* <p>
* A discussion pertaining to edge anchors. At most MAX_EDGE_ANCHORS
* edge anchors may be specified. The edge anchors are used to define cubic
* Bezier curves. The exact algorithm for determining the Bezier curves from
* the input parameters is too complicated to describe in this Javadoc. Some
* parts of the algorithm: <blockquote>
* <ul>
* <li>the conglomerated curve is [probably] not going to pass through the
* edge anchors points specified; the curve will pass through the midpoint
* between every consecutive pair of anchors</li>
* <li>when determining the edge path as a whole, an ordered list of points
* is created by putting point (x0, y0) at the beginning of the list,
* followed by the anchor points, followed by point (x1, y1); then,
* duplicate points are removed from the beginning and end of this list</li>
* <li>from the list described above, the first two points define the arrow
* direction at point (x0, y0) and the initial curve direction; likewise,
* the last two points in this list define the arrow direction at point (x1,
* y1) and the ending curve direction</li>
* </ul>
* </blockquote> In order to specify a straight-line edge path, simply
* duplicate each edge anchor in the EdgeAnchors instance. For example, a
* smooth curve would be drawn by specifying consecutive-pairwise disctinct
* points {(x0,y0), A0, A1, A2, (x1,y1)}; a straight-line edge path would be
* drawn by specifying {(x0, y0), A0, A0, A1, A1, A2, A2, (x1, y1)}.
*
* @param arrow0Type
* the type of arrow shape to use for drawing the arrow at point
* (x0, y0); this value must be one of the ARROW_* constants.
* @param arrow0Size
* the size of arrow at point (x0, y0); how size is interpreted
* for different arrow types is described in the table above.
* @param arrow0Paint
* the paint to use when drawing the arrow at point (x0, y0).
* @param arrow1Type
* the type of arrow shape to use for drawing the arrow at point
* (x1, y1); this value must be one of the ARROW_* constants.
* @param arrow1Size
* the size of arrow at point (x1, y1); how size is interpreted
* for different arrow types is described in the table above.
* @param arrow1Paint
* the paint to use when drawing the arrow at point (x1, y1).
* @param x0
* the X coordinate of the first edge endpoint.
* @param y0
* the Y coordinate of the first edge endpoint.
* @param anchors
* anchor points between the two edge endpoints; null is an
* acceptable value to indicate no edge anchors.
* @param x1
* the X coordinate of the second edge endpoint.
* @param y1
* the Y coordinate of the second edge endpoint.
* @param edgeThickness
* the thickness of the edge segment; the edge segment is the
* part of the edge between the two endpoint arrows.
* @param edgeStroke
* the Stroke to use when drawing the edge segment.
* @param edgePaint
* the paint to use when drawing the edge segment.
* @exception IllegalArgumentException
* if edgeThickness is less than zero, if any one of the arrow
* configurations does not meet specified criteria, or if more
* than MAX_EDGE_ANCHORS anchors are specified.
*/
public final void drawEdgeFull(final ArrowShape arrow0Type, final float arrow0Size, final Paint arrow0Paint, final ArrowShape arrow1Type, final float arrow1Size, final Paint arrow1Paint, final float x0, final float y0, EdgeAnchors anchors, final float x1, final float y1, final float edgeThickness, final Stroke edgeStroke, final Paint edgePaint) {
final double curveFactor = CURVE_ELLIPTICAL;
if (anchors == null) {
anchors = m_noAnchors;
}
if (m_debug) {
edgeFullDebug(arrow0Type, arrow0Size, arrow1Type, arrow1Size, edgeStroke, edgeThickness, anchors);
}
if (!computeCubicPolyEdgePath(arrow0Type, (arrow0Type == ArrowShapeVisualProperty.NONE) ? 0.0f : arrow0Size, arrow1Type, (arrow1Type == ArrowShapeVisualProperty.NONE) ? 0.0f : arrow1Size, x0, y0, anchors, x1, y1, curveFactor)) {
// than 3 total.
if (m_edgePtsCount == 2) {
// Draw an ordinary edge.
drawSimpleEdgeFull(arrow0Type, arrow0Size, arrow0Paint, arrow1Type, arrow1Size, arrow1Paint, (float) m_edgePtsBuff[0], (float) m_edgePtsBuff[1], (float) m_edgePtsBuff[2], (float) m_edgePtsBuff[3], edgeThickness, edgeStroke, edgePaint);
}
return;
}
// Render the edge polypath.
final boolean simpleSegment = arrow0Type == ArrowShapeVisualProperty.NONE && arrow1Type == ArrowShapeVisualProperty.NONE;
m_g2d.setStroke(edgeStroke);
// Set m_path2d to contain the cubic curves computed in
// m_edgePtsBuff.
m_path2d.reset();
m_path2d.moveTo((float) m_edgePtsBuff[2], (float) m_edgePtsBuff[3]);
int inx = 4;
final int count = ((m_edgePtsCount - 1) * 6) - 2;
while (inx < count) {
m_path2d.curveTo((float) m_edgePtsBuff[inx++], (float) m_edgePtsBuff[inx++], (float) m_edgePtsBuff[inx++], (float) m_edgePtsBuff[inx++], (float) m_edgePtsBuff[inx++], (float) m_edgePtsBuff[inx++]);
}
m_g2d.setPaint(edgePaint);
m_g2d.draw(m_path2d);
if (simpleSegment) {
return;
}
// We need to figure out the phase at the end of the cubic poly-path
// for dashed segments. I cannot find a Java API to do this; our
// best
// bet would be to implement our own cubic curve length calculating
// function, but our computation may not agree with BasicStroke's
// computation. So what we're going to do is never render the arrow
// caps for dashed edges.
final double dx0 = m_edgePtsBuff[0] - m_edgePtsBuff[4];
final double dy0 = m_edgePtsBuff[1] - m_edgePtsBuff[5];
final double len0 = Math.sqrt((dx0 * dx0) + (dy0 * dy0));
final double cosTheta0 = dx0 / len0;
final double sinTheta0 = dy0 / len0;
final double dx1 = m_edgePtsBuff[((m_edgePtsCount - 1) * 6) - 2] - m_edgePtsBuff[((m_edgePtsCount - 1) * 6) - 6];
final double dy1 = m_edgePtsBuff[((m_edgePtsCount - 1) * 6) - 1] - m_edgePtsBuff[((m_edgePtsCount - 1) * 6) - 5];
final double len1 = Math.sqrt((dx1 * dx1) + (dy1 * dy1));
final double cosTheta1 = dx1 / len1;
final double sinTheta1 = dy1 / len1;
// say, don't worry about how fancy strokes intersect the arrow.
if (edgeStroke instanceof BasicStroke) {
// Render arrow cap at origin of poly path.
final Shape arrow0Cap = computeUntransformedArrowCap(arrow0Type, ((double) arrow0Size) / edgeThickness);
if (arrow0Cap != null) {
m_xformUtil.setTransform(cosTheta0, sinTheta0, -sinTheta0, cosTheta0, m_edgePtsBuff[2], m_edgePtsBuff[3]);
m_g2d.transform(m_xformUtil);
m_g2d.scale(edgeThickness, edgeThickness);
// The paint is already set to edge paint.
m_g2d.fill(arrow0Cap);
m_g2d.setTransform(m_currNativeXform);
}
// Render arrow cap at end of poly path.
final Shape arrow1Cap = computeUntransformedArrowCap(arrow1Type, ((double) arrow1Size) / edgeThickness);
if (arrow1Cap != null) {
m_xformUtil.setTransform(cosTheta1, sinTheta1, -sinTheta1, cosTheta1, m_edgePtsBuff[((m_edgePtsCount - 1) * 6) - 4], m_edgePtsBuff[((m_edgePtsCount - 1) * 6) - 3]);
m_g2d.transform(m_xformUtil);
m_g2d.scale(edgeThickness, edgeThickness);
// The paint is already set to edge paint.
m_g2d.fill(arrow1Cap);
m_g2d.setTransform(m_currNativeXform);
}
}
// Render arrow at origin of poly path.
final Shape arrow0 = computeUntransformedArrow(arrow0Type);
if (arrow0 != null) {
m_xformUtil.setTransform(cosTheta0, sinTheta0, -sinTheta0, cosTheta0, m_edgePtsBuff[0], m_edgePtsBuff[1]);
m_g2d.transform(m_xformUtil);
m_g2d.scale(arrow0Size, arrow0Size);
m_g2d.setPaint(arrow0Paint);
boolean filled = arrow0Type.isFilled();
if (filled)
m_g2d.fill(arrow0);
else {
float strokeWidth = 0.25f;
m_g2d.setStroke(new BasicStroke(0.25f));
m_g2d.draw(arrow0);
}
m_g2d.setTransform(m_currNativeXform);
}
// Render arrow at end of poly path.
final Shape arrow1 = computeUntransformedArrow(arrow1Type);
if (arrow1 != null) {
m_xformUtil.setTransform(cosTheta1, sinTheta1, -sinTheta1, cosTheta1, m_edgePtsBuff[((m_edgePtsCount - 1) * 6) - 2], m_edgePtsBuff[((m_edgePtsCount - 1) * 6) - 1]);
m_g2d.transform(m_xformUtil);
m_g2d.scale(arrow1Size, arrow1Size);
m_g2d.setPaint(arrow1Paint);
boolean filled = arrow1Type.isFilled();
if (filled)
m_g2d.fill(arrow1);
else {
m_g2d.setStroke(new BasicStroke(0.025f));
m_g2d.draw(arrow1);
}
m_g2d.setTransform(m_currNativeXform);
}
}
use of org.cytoscape.view.presentation.property.values.ArrowShape in project cytoscape-impl by cytoscape.
the class GraphGraphics method drawSimpleEdgeFull.
private final void drawSimpleEdgeFull(final ArrowShape arrow0Type, final float arrow0Size, final Paint arrow0Paint, final ArrowShape arrow1Type, final float arrow1Size, final Paint arrow1Paint, final float x0, final float y0, final float x1, final float y1, final float edgeThickness, final Stroke edgeStroke, final Paint edgePaint) {
final double len = Math.sqrt(((((double) x1) - x0) * (((double) x1) - x0)) + ((((double) y1) - y0) * (((double) y1) - y0)));
// calls us makes this check automatically.
if (len == 0.0d)
return;
final double x0Adj;
final double y0Adj;
final double x1Adj;
final double y1Adj;
final byte simpleSegment;
// Render the line segment if necessary.
final double t0 = (getT(arrow0Type) * arrow0Size) / len;
x0Adj = (t0 * (((double) x1) - x0)) + x0;
y0Adj = (t0 * (((double) y1) - y0)) + y0;
final double t1 = (getT(arrow1Type) * arrow1Size) / len;
x1Adj = (t1 * (((double) x0) - x1)) + x1;
y1Adj = (t1 * (((double) y0) - y1)) + y1;
// Dot product determines this.
if ((((((double) x1) - x0) * (x1Adj - x0Adj)) + ((((double) y1) - y0) * (y1Adj - y0Adj))) > 0.0d) {
if (arrow0Type == ArrowShapeVisualProperty.NONE && arrow1Type == ArrowShapeVisualProperty.NONE) {
simpleSegment = 1;
} else {
simpleSegment = -1;
}
m_g2d.setStroke(edgeStroke);
m_line2d.setLine(x0Adj, y0Adj, x1Adj, y1Adj);
m_g2d.setPaint(edgePaint);
m_g2d.draw(m_line2d);
if (simpleSegment > 0)
return;
} else {
// Did not render segment.
simpleSegment = 0;
}
// End rendering of line segment."
// Using x0, x1, y0, and y1 instead of the "adjusted" endpoints is
// accurate enough in computation of cosine and sine because the
// length is guaranteed to be at least as large. Remember that the
// original endpoint values are specified as float whereas the adjusted
// points are double.
final double cosTheta = (((double) x0) - x1) / len;
final double sinTheta = (((double) y0) - y1) / len;
if (simpleSegment < 0 && edgeStroke instanceof BasicStroke) {
// Arrow cap at point 0.
final Shape arrow0Cap = computeUntransformedArrowCap(arrow0Type, ((double) arrow0Size) / edgeThickness);
if (arrow0Cap != null) {
m_xformUtil.setTransform(cosTheta, sinTheta, -sinTheta, cosTheta, x0Adj, y0Adj);
m_g2d.transform(m_xformUtil);
m_g2d.scale(edgeThickness, edgeThickness);
// The paint is already set to edge paint.
m_g2d.fill(arrow0Cap);
m_g2d.setTransform(m_currNativeXform);
}
// Arrow cap at point 1.
final Shape arrow1Cap = computeUntransformedArrowCap(arrow1Type, ((double) arrow1Size) / edgeThickness);
if (arrow1Cap != null) {
m_xformUtil.setTransform(-cosTheta, -sinTheta, sinTheta, -cosTheta, x1Adj, y1Adj);
m_g2d.transform(m_xformUtil);
m_g2d.scale(edgeThickness, edgeThickness);
// The paint is already set to edge paint.
m_g2d.fill(arrow1Cap);
m_g2d.setTransform(m_currNativeXform);
}
}
// Render arrow at point 0.
final Shape arrow0 = computeUntransformedArrow(arrow0Type);
if (arrow0 != null) {
m_xformUtil.setTransform(cosTheta, sinTheta, -sinTheta, cosTheta, x0, y0);
m_g2d.transform(m_xformUtil);
m_g2d.scale(arrow0Size, arrow0Size);
m_g2d.setPaint(arrow0Paint);
boolean filled = arrow0Type.isFilled();
if (filled)
m_g2d.fill(arrow0);
else {
m_g2d.setStroke(new BasicStroke(0.25f));
if (arrow0Type == ArrowShapeVisualProperty.OPEN_CIRCLE)
m_g2d.setStroke(new BasicStroke(0.1f));
m_g2d.draw(arrow0);
}
m_g2d.setTransform(m_currNativeXform);
}
// Render arrow at point 1.
final Shape arrow1 = computeUntransformedArrow(arrow1Type);
if (arrow1 != null) {
m_xformUtil.setTransform(-cosTheta, -sinTheta, sinTheta, -cosTheta, x1, y1);
m_g2d.transform(m_xformUtil);
m_g2d.scale(arrow1Size, arrow1Size);
m_g2d.setPaint(arrow1Paint);
boolean filled = arrow1Type.isFilled();
if (filled)
m_g2d.fill(arrow1);
else {
m_g2d.setStroke(new BasicStroke(0.25f));
if (arrow1Type == ArrowShapeVisualProperty.OPEN_CIRCLE)
m_g2d.setStroke(new BasicStroke(0.1f));
m_g2d.draw(arrow1);
}
m_g2d.setTransform(m_currNativeXform);
}
}
Aggregations