All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jgraph.graph.EdgeRenderer Maven / Gradle / Ivy

The newest version!
/*
 * $Id: EdgeRenderer.java,v 1.19 2008/05/20 18:21:48 david Exp $ * 
 * Copyright (c) 2001-2007 Gaudenz Alder
 *  
 * See LICENSE file in distribution for licensing details of this source file
 */
package org.jgraph.graph;

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.GradientPaint;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Polygon;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.Shape;
import java.awt.Stroke;
import java.awt.geom.Ellipse2D;
import java.awt.geom.GeneralPath;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.io.Serializable;
import java.lang.ref.WeakReference;
import java.util.Map;

import javax.swing.JComponent;
import javax.swing.UIManager;

import org.jgraph.JGraph;
import org.jgraph.util.Bezier;
import org.jgraph.util.Spline2D;

/**
 * This renderer displays entries that implement the CellView interface.
 * 
 * @version 1.0 1/1/02
 * @author Gaudenz Alder
 */

public class EdgeRenderer extends JComponent implements CellViewRenderer,
Serializable {

	/** Static Graphics used for Font Metrics */
	protected static transient Graphics fontGraphics;

	// Headless environment does not allow graphics
	static {
		try {
			fontGraphics = new BufferedImage(1, 1, BufferedImage.TYPE_INT_RGB)
			.getGraphics();
		} catch (Error e) {
			// No font graphics
			fontGraphics = null;
		}
	}

	/** When zooming a graph the font size jumps at certain zoom levels rather than
	 *  scaling smoothly. Sometimes the zoom on the font is more than the component zoom
	 *  and cropping occurs. This buffer allows for the maximum occurance of this
	 */
	public static double LABELWIDTHBUFFER = 1.1;

	/** A switch for painting the extra labels */
	public boolean simpleExtraLabels = true;

	/** Override this if you want the extra labels to appear in a special fontJ */
	public Font extraLabelFont = null;

	/** Reference to the font metrics of the above */
	protected transient FontMetrics metrics;

	/** Cache the current graph for drawing */
	protected transient WeakReference graph;

	/** Cache the current edgeview for drawing */
	private transient EdgeView view;

	/** Painting attributes of the current edgeview */
	protected transient int beginDeco, endDeco, beginSize, endSize, lineStyle;

	/** Width of the current edge view */
	protected transient float lineWidth;

	/**
	 * Boolean attributes of the current edgeview. Fill flags are checked for
	 * valid decorations.
	 */
	protected transient boolean labelBorder, beginFill, endFill, focus,
	selected, preview, opaque, childrenSelected, labelTransformEnabled,
	isMoveBelowZero;

	/**
	 * Color attributes of the current edgeview. This components foreground is
	 * set to the edgecolor, the fontColor is in an extra variable. If the
	 * fontColor is null, the current foreground is used. The default background
	 * instead is used for text and is not visible if the label is not visible
	 * or if opaque is true.
	 */
	protected transient Color borderColor, defaultForeground,
	defaultBackground, fontColor;

	/** Contains the current dash pattern. Null means no pattern. */
	protected transient float[] lineDash;

	/** Contains the current dash offset. Null means no offset. */
	protected transient float dashOffset = 0.0f;

	/** The gradient color of the edge */
	protected transient Color gradientColor = null;

	/** The color of the graph grid */
	protected transient Color gridColor = null;

	/** The color of the second available handle */
	protected transient Color lockedHandleColor = null;

	/** The color of highlighted cells */
	protected transient Color highlightColor = null;

	/** Cached bezier curve */
	protected transient Bezier bezier;

	/** Cached spline curve */
	protected transient Spline2D spline;

	/**
	 * Constructs a renderer that may be used to render edges.
	 */
	public EdgeRenderer() {
		defaultForeground = UIManager.getColor("Tree.textForeground");
		defaultBackground = UIManager.getColor("Tree.textBackground");
	}

	/**
	 * Sets view to work with, caching necessary values until the next call of
	 * this method or until some other methods with explicitly specified
	 * different view
	 */
	synchronized void  setView(CellView value) {
		if (value instanceof EdgeView) {
			view = (EdgeView) value;
			installAttributes(view);
		} else {
			view = null;
		}
	}

	/**
	 * Configure and return the renderer based on the passed in components. The
	 * value is typically set from messaging the graph with
	 * convertValueToString.
	 * 
	 * @param graph
	 *            the graph that that defines the rendering context.
	 * @param view
	 *            the cell view that should be rendered.
	 * @param sel
	 *            whether the object is selected.
	 * @param focus
	 *            whether the object has the focus.
	 * @param preview
	 *            whether we are drawing a preview.
	 * @return the component used to render the value.
	 */
	public Component getRendererComponent(JGraph graph, CellView view,
			boolean sel, boolean focus, boolean preview) {
		if (view instanceof EdgeView && graph != null) {
			this.gridColor = graph.getGridColor();
			this.lockedHandleColor = graph.getLockedHandleColor();
			this.highlightColor = graph.getHighlightColor();
			this.isMoveBelowZero = graph.isMoveBelowZero();
			this.graph = new WeakReference(graph);
			this.focus = focus;
			this.selected = sel;
			this.preview = preview;
			this.childrenSelected = graph.getSelectionModel()
					.isChildrenSelected(view.getCell());
			setView(view);
			return this;
		}
		return null;
	}

	/**
	 * Returns true if the edge shape intersects the given rectangle.
	 */
	public boolean intersects(JGraph graph, CellView value, Rectangle rect) {
		if (value instanceof EdgeView && graph != null && value != null) {
			setView(value);

			// If we have two control points, we can get rid of hit
			// detection on do an intersection test on the two diagonals
			// of rect and the line between the two points
			EdgeView edgeView = (EdgeView) value;
			if (edgeView.getPointCount() == 2) {
				Point2D p0 = edgeView.getPoint(0);
				Point2D p1 = edgeView.getPoint(1);
				if (rect.intersectsLine(p0.getX(), p0.getY(), p1.getX(), p1
						.getY()))
					return true;
			} else {
				Graphics2D g2 = (Graphics2D) graph.getGraphics();
				if (g2.hit(rect, view.getShape(), true))
					return true;
			}
			Rectangle2D r = getLabelBounds(graph, view);
			if (r != null && r.intersects(rect))
				return true;
			Object[] labels = GraphConstants.getExtraLabels(view
					.getAllAttributes());
			if (labels != null) {
				for (int i = 0; i < labels.length; i++) {
					r = getExtraLabelBounds(graph, view, i);
					if (r != null && r.intersects(rect))
						return true;
				}
			}
		}
		return false;
	}

	/**
	 * Returns the bounds of the edge shape.
	 */
	public Rectangle2D getBounds(CellView value) {
		if (value instanceof EdgeView && value != null) {
			// No need to call setView as getPaintBounds will
			view = (EdgeView) value;
			Rectangle2D r = getPaintBounds(view);
			JGraph graph = null;
			if (this.graph != null) {
				graph = (JGraph)this.graph.get();
			}
			Rectangle2D rect = getLabelBounds(graph, view);
			if (rect != null)
				Rectangle2D.union(r, rect, r);
			Object[] labels = GraphConstants.getExtraLabels(view
					.getAllAttributes());
			if (labels != null) {
				for (int i = 0; i < labels.length; i++) {
					rect = getExtraLabelBounds(graph, view, i);
					if (rect != null)
						Rectangle2D.union(r, rect, r);
				}
			}
			int b = (int) Math.ceil(lineWidth);
			r.setFrame(r.getX() - b, r.getY() - b, r.getWidth() + 2 * b, r
					.getHeight()
					+ 2 * b);
			return r;
		}
		return null;
	}

	private boolean isLabelTransformEnabled() {
		return labelTransformEnabled;
	}

	/**
	 * Estimates whether the transform for label should be applied. With the
	 * transform, the label will be painted along the edge. To apply transform,
	 * rotate graphics by the angle returned from {@link #getLabelAngle}
	 * 
	 * @return true, if transform can be applied, false otherwise
	 */
	private boolean isLabelTransform(String label) {
		if (!isLabelTransformEnabled()) {
			return false;
		}
		Point2D p = getLabelPosition(view);
		if (p != null && label != null && label.length() > 0) {
			int sw = metrics.stringWidth(label);
			Point2D p1 = view.getPoint(0);
			Point2D p2 = view.getPoint(view.getPointCount() - 1);
			double length = Math.sqrt((p2.getX() - p1.getX())
					* (p2.getX() - p1.getX()) + (p2.getY() - p1.getY())
					* (p2.getY() - p1.getY()));
			if (!(length <= Double.NaN || length < sw)) {
				return true;
			}
		}
		return false;
	}

	/**
	 * Calculates the angle at which graphics should be rotated to paint label
	 * along the edge. Before calling this method always check that transform
	 * should be applied using {@linkisLabelTransform}
	 * 
	 * @return the value of the angle, 0 if the angle is zero or can't be
	 *         calculated
	 */
	private double getLabelAngle(String label) {
		Point2D p = getLabelPosition(view);
		double angle = 0;
		if (p != null && label != null && label.length() > 0) {
			int sw = metrics.stringWidth(label);
			// Note: For control points you may want to choose other
			// points depending on the segment the label is in.
			Point2D p1 = view.getPoint(0);
			Point2D p2 = view.getPoint(view.getPointCount() - 1);
			// Length of the edge
			double length = Math.sqrt((p2.getX() - p1.getX())
					* (p2.getX() - p1.getX()) + (p2.getY() - p1.getY())
					* (p2.getY() - p1.getY()));
			if (!(length <= Double.NaN || length < sw)) { // Label fits into
				// edge's length

				// To calculate projections of edge
				double cos = (p2.getX() - p1.getX()) / length;
				double sin = (p2.getY() - p1.getY()) / length;

				// Determine angle
				angle = Math.acos(cos);
				if (sin < 0) { // Second half
					angle = 2 * Math.PI - angle;
				}
			}
			if (angle > Math.PI / 2 && angle <= Math.PI * 3 / 2) {
				angle -= Math.PI;
			}
		}
		return angle;
	}

	/**
	 * Returns the label bounds of the specified view in the given graph.
	 */
	public Rectangle2D getLabelBounds(JGraph paintingContext, EdgeView view) {
		if (paintingContext == null && graph != null) {
			JGraph graph = (JGraph)this.graph.get();
			paintingContext = graph;
		}
		// No need to call setView as getLabelPosition will
		String label = (paintingContext != null) ? paintingContext
				.convertValueToString(view) : String.valueOf(view.getCell());
				if (label != null) {
					Point2D p = getLabelPosition(view);
					Dimension d = getLabelSize(view, label);
					return getLabelBounds(p, d, label);
				} else {
					return null;
				}
	}

	/**
	 * Returns the label bounds of the specified view in the given graph. Note:
	 * The index is the position of the String object for the label in the extra
	 * labels array of the view.
	 */
	public Rectangle2D getExtraLabelBounds(JGraph paintingContext,
			EdgeView view, int index) {
		if (paintingContext == null && graph != null) {
			JGraph graph = (JGraph)this.graph.get();
			paintingContext = graph;
		}
		setView(view);
		Object[] labels = GraphConstants
				.getExtraLabels(view.getAllAttributes());
		if (labels != null && index < labels.length) {
			Point2D p = getExtraLabelPosition(this.view, index);
			Dimension d = getExtraLabelSize(paintingContext, this.view, index);
			String label = (paintingContext != null) ? paintingContext
					.convertValueToString(labels[index]) : String
					.valueOf(labels[index]);
					return getLabelBounds(p, d, label);
		}
		return new Rectangle2D.Double(getX(), getY(), 0, 0);
	}

	/**
	 * Returns the label bounds of the specified view in the given graph.
	 */
	public Rectangle2D getLabelBounds(Point2D p, Dimension d, String label) {
		if (label != null && isLabelTransform(label)) {
			// With transform label is rotated, so we should
			// rotate the rectangle (sw, sh) and return the
			// bounding rectangle
			double angle = getLabelAngle(label);
			if (angle < 0)
				angle = -angle;
			if (angle > Math.PI / 2)
				angle %= Math.PI / 2;
			double yside = Math.abs(Math.cos(angle) * d.height
					+ Math.sin(angle) * d.width);
			double xside = Math.abs(d.width * Math.cos(angle) + d.height
					* Math.sin(angle));
			// Getting maximum is not good, but I don't want to be
			// drown in calculations
			if (xside > yside)
				yside = xside;
			if (yside > xside)
				xside = yside;
			angle = getLabelAngle(label);

			// Increasing by height is safe, but I think the precise
			// value is font.descent layed on edge vector and
			// projected on each axis
			d.width = (int) xside + d.height;
			d.height = (int) yside + d.height;
		}
		if (p != null && d != null) {
			double x = Math.max(0, p.getX() - (d.width / 2));
			double y = Math.max(0, p.getY() - (d.height / 2));
			return new Rectangle2D.Double(x, y, d.width + 1, d.height + 1);
		}
		return null;
	}

	/**
	 * Returns the label position of the specified view in the given graph.
	 */
	public Point2D getLabelPosition(EdgeView view) {
		setView(view);
		return getLabelPosition(view.getLabelPosition());
	}

	/**
	 * Returns the label position of the specified view in the given graph.
	 */
	public Point2D getExtraLabelPosition(EdgeView view, int index) {
		setView(view);
		Point2D[] pts = GraphConstants.getExtraLabelPositions(view
				.getAllAttributes());
		if (pts != null && index < pts.length)
			return getLabelPosition(pts[index]);
		return null;
	}

	/**
	 * Returns the label position of the specified view in the given graph.
	 */
	protected synchronized Point2D getLabelPosition(Point2D pos) {
		EdgeView view=this.view;
		if (view!=null){
			Rectangle2D tmp = getPaintBounds(view);
			int unit = GraphConstants.PERMILLE;
			Point2D p0 = view.getPoint(0);
			if (pos != null && tmp != null && p0 != null) {
				if (!isLabelTransformEnabled()) {
					return view.getAbsoluteLabelPositionFromRelative(pos);
				} else {
					Point2D vector = view.getLabelVector();
					if (vector!=null) {
						double dx = vector.getX();
						double dy = vector.getY();
						double len = Math.sqrt(dx * dx + dy * dy);
						if (len > 0) {
							double x = p0.getX() + (dx * pos.getX() / unit);
							double y = p0.getY() + (dy * pos.getX() / unit);
							x += (-dy * pos.getY() / len);
							y += (dx * pos.getY() / len);
							return new Point2D.Double(x, y);
						} else {
							return new Point2D.Double(p0.getX() + pos.getX(), p0.getY()
									+ pos.getY());
						}
					}else
						return pos;
				}
			}
		}
		return null;
	}

	/**
	 * Returns the label size of the specified view in the given graph.
	 */
	public Dimension getExtraLabelSize(JGraph paintingContext, EdgeView view,
			int index) {
		Object[] labels = GraphConstants
				.getExtraLabels(view.getAllAttributes());
		if (labels != null && index < labels.length) {
			String label = (paintingContext != null) ? paintingContext
					.convertValueToString(labels[index]) : String
					.valueOf(labels[index]);
					return getLabelSize(view, label);
		}
		return null;
	}

	/**
	 * Returns the label size of the specified view in the given graph.
	 */
	public Dimension getLabelSize(EdgeView view, String label) {
		if (label != null && fontGraphics != null) {
			fontGraphics.setFont(GraphConstants
					.getFont(view.getAllAttributes()));
			metrics = fontGraphics.getFontMetrics();
			int sw = (int)(metrics.stringWidth(label) * LABELWIDTHBUFFER);
			int sh = metrics.getHeight();
			return new Dimension(sw, sh);
		}
		return null;
	}

	/**
	 * Installs the attributes of specified cell in this renderer instance. This
	 * means, retrieve every published key from the cells hashtable and set
	 * global variables or superclass properties accordingly.
	 * 
	 * @param view
	 *            the cell view to retrieve the attribute values from.
	 */
	protected void installAttributes(CellView view) {
		Map map = view.getAllAttributes();
		beginDeco = GraphConstants.getLineBegin(map);
		beginSize = GraphConstants.getBeginSize(map);
		beginFill = GraphConstants.isBeginFill(map) && isFillable(beginDeco);
		endDeco = GraphConstants.getLineEnd(map);
		endSize = GraphConstants.getEndSize(map);
		endFill = GraphConstants.isEndFill(map) && isFillable(endDeco);
		lineWidth = GraphConstants.getLineWidth(map);
		Edge.Routing routing = GraphConstants.getRouting(map);
		lineStyle = (routing != null && view instanceof EdgeView) ? routing
				.getPreferredLineStyle((EdgeView) view)
				: Edge.Routing.NO_PREFERENCE;
				if (lineStyle == Edge.Routing.NO_PREFERENCE)
					lineStyle = GraphConstants.getLineStyle(map);
				lineDash = GraphConstants.getDashPattern(map);
				dashOffset = GraphConstants.getDashOffset(map);
				borderColor = GraphConstants.getBorderColor(map);
				Color foreground = GraphConstants.getLineColor(map);
				setForeground((foreground != null) ? foreground : defaultForeground);
				Color background = GraphConstants.getBackground(map);
				setBackground((background != null) ? background : defaultBackground);
				Color gradientColor = GraphConstants.getGradientColor(map);
				setGradientColor(gradientColor);
				setOpaque(GraphConstants.isOpaque(map));
				setFont(GraphConstants.getFont(map));
				Color tmp = GraphConstants.getForeground(map);
				fontColor = (tmp != null) ? tmp : getForeground();
				labelTransformEnabled = GraphConstants.isLabelAlongEdge(map);
	}

	protected boolean isFillable(int decoration) {
		return !(decoration == GraphConstants.ARROW_SIMPLE
				|| decoration == GraphConstants.ARROW_LINE || decoration == GraphConstants.ARROW_DOUBLELINE);
	}

	/**
	 * Returns the bounds of the edge shape without label
	 */
	public Rectangle2D getPaintBounds(EdgeView view) {
		Rectangle2D rec = null;
		setView(view);
		if (view.getShape() != null && view.getShape().getBounds()!=null)
			rec = view.getShape().getBounds();
		else
			rec = new Rectangle2D.Double(0, 0, 0, 0);
		return rec;
	}

	/**
	 * Paint the renderer.
	 */
	public void paint(Graphics g) {
		if (view.isLeaf()) {
			Shape edgeShape = view.getShape();
			// Sideeffect: beginShape, lineShape, endShape
			if (edgeShape != null) {
				Graphics2D g2 = (Graphics2D) g;
				g2.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL,
						RenderingHints.VALUE_STROKE_PURE);
				int c = BasicStroke.CAP_BUTT;
				int j = BasicStroke.JOIN_MITER;
				setOpaque(false);
				super.paint(g);
				translateGraphics(g);
				g.setColor(getForeground());
				if (lineWidth > 0) {
					//	g2.setStroke(new BasicStroke(lineWidth, c, j));
					if (gradientColor != null && !preview) {
						g2.setPaint(new GradientPaint(0, 0, getBackground(),
								getWidth(), getHeight(), gradientColor, true));
					}
					if (view.beginShape != null) {
						if (beginFill)
							g2.fill(view.beginShape);
						g2.draw(view.beginShape);
					}
					if (view.endShape != null) {
						if (endFill)
							g2.fill(view.endShape);
						g2.draw(view.endShape);
					}
					if (lineDash != null) // Dash For Line Only
						g2.setStroke(new BasicStroke(lineWidth, c, j, 10.0f,
								lineDash, dashOffset));
					if (view.lineShape != null)
						g2.draw(view.lineShape);
				}

				if (selected) { // Paint Selected
					g2.setStroke(GraphConstants.SELECTION_STROKE);
					g2.setColor(highlightColor);
					if (view.beginShape != null)
						g2.draw(view.beginShape);
					if (view.lineShape != null)
						g2.draw(view.lineShape);
					if (view.endShape != null)
						g2.draw(view.endShape);
				}
				g2.setStroke(new BasicStroke(1));
				g
				.setFont((extraLabelFont != null) ? extraLabelFont
						: getFont());
				Object[] labels = GraphConstants.getExtraLabels(view
						.getAllAttributes());
				JGraph graph = (JGraph)this.graph.get();
				if (labels != null) {
					for (int i = 0; i < labels.length; i++)
						paintLabel(g, graph.convertValueToString(labels[i]),
								getExtraLabelPosition(view, i),
								false || !simpleExtraLabels);
				}
				if (graph.getEditingCell() != view.getCell()) {
					g.setFont(getFont());
					Object label = graph.convertValueToString(view);
					if (label != null) {
						paintLabel(g, label.toString(), getLabelPosition(view),
								true);
					}
				}
			}
		} else {
			paintSelectionBorder(g);
		}
	}

	/**
	 * Provided for subclassers to paint a selection border.
	 */
	protected void paintSelectionBorder(Graphics g) {
		((Graphics2D) g).setStroke(GraphConstants.SELECTION_STROKE);
		if (childrenSelected)
			g.setColor(gridColor);
		else if (focus && selected)
			g.setColor(lockedHandleColor);
		else if (selected)
			g.setColor(highlightColor);
		if (childrenSelected || selected) {
			Dimension d = getSize();
			g.drawRect(0, 0, d.width - 1, d.height - 1);
		}
	}

	// This if for subclassers that to not want the graphics
	// to be relative to the top, left corner of this component.
	// Note: Override this method with an empty implementation
	// if you want absolute positions for your edges
	protected void translateGraphics(Graphics g) {
		g.translate(-getX(), -getY());
	}

	/**
	 * Paint the specified label for the current edgeview.
	 */
	protected void paintLabel(Graphics g, String label, Point2D p,
			boolean mainLabel) {
		if (p != null && label != null && label.length() > 0 && metrics != null) {
			Shape shape=g.getClip();
			g.setClip(null);	
			int sw = metrics.stringWidth(label);
			String[] lines = label.split("\n");
			boolean multiline=false;
			if (label.contains("\n")){
				multiline=true;
				int maxWidth=-1;				
				for (String line:lines)
					maxWidth=Math.max(maxWidth, metrics.stringWidth(line));
				sw=maxWidth;
			}
			int sh = metrics.getHeight();
			Graphics2D g2 = (Graphics2D) g;
			boolean applyTransform = isLabelTransform(label);
			double angle = 0;
			int dx = -sw / 3;
			int dy = +sh / 4;
			int offset = isMoveBelowZero || applyTransform ? 0 : Math
					.min(0, (int) (dx + p.getX()));

			g2.translate(p.getX() - offset, p.getY());
			if (applyTransform) {
				angle = getLabelAngle(label);
				g2.rotate(angle);
			}
			if (GraphConstants.isOpaque(view.getAllAttributes())){				
				g.setColor(GraphConstants.getBackground(view.getAllAttributes()));
				g.fillRect(dx-2 , -10, sw + 4,
						dy+sh*lines.length+2);
			} else
				if (isOpaque() && mainLabel) {						
					g.setColor(getBackground());			
					g.fillRect(-sw / 2 - 1, -sh / 2 - 1, sw + 2,
							sh + 2);
				}

			if (GraphConstants.getBorderColor(view.getAllAttributes())!=null){
				Stroke os = g2.getStroke();
				BasicStroke bs= new BasicStroke(2.0f,                      // Width
						BasicStroke.CAP_ROUND,    // End cap
						BasicStroke.JOIN_ROUND,    // Join style
						10.0f,                     // Miter limit
						new float[] {5,3}, // Dash pattern
						0.0f);                     // Dash phase
				g2.setStroke(bs);
				g.setColor(GraphConstants.getBorderColor(view.getAllAttributes()));
				g.drawRect(dx -2, -10, sw + 4,
						dy+sh*lines.length+2 );
				g2.setStroke(os);
			} else

				if (borderColor != null  && mainLabel) {					
					g.setColor(borderColor);					
					g.drawRect(-sw / 2 - 1, -sh / 2 - 1, sw + 2,
							sh + 2);
				}





			g.setColor(fontColor);
			if (applyTransform && borderColor == null && !isOpaque()) {
				// Shift label perpendicularly by the descent so it
				// doesn't cross the line.
				dy = -metrics.getDescent();
			}

			for (int k=0;k 1) {
					// Following block may modify static vars as side effect (Flyweight
					// Design)
					EdgeView tmp = view;
					Point2D[] p = null;
					p = new Point2D[n];
					for (int i = 0; i < n; i++) {
						Point2D pt = tmp.getPoint(i);
						if (pt == null)
							return null; // exit
						p[i] = new Point2D.Double(pt.getX(), pt.getY());
					}

					// End of Side-Effect Block
					// Undo Possible MT-Side Effects
					if (view != tmp) {
						view = tmp;
						installAttributes(view);
					}
					GeneralPath sharedPath = getCachedViewSharedPath();
					// End of Undo
					if (sharedPath == null) {					
						sharedPath=new GeneralPath(GeneralPath.WIND_NON_ZERO, n);					
					} else {					
						sharedPath.reset();					
					}

					view.beginShape = view.lineShape = view.endShape = null;
					Point2D p0 = p[0];
					Point2D pe = p[n - 1];
					Point2D p1 = p[1];
					Point2D p2 = p[n - 2];				
					//			if (p0!=null && pe!=null){
					if (lineStyle == GraphConstants.STYLE_BEZIER && n > 2) {
						bezier = new Bezier(p);
						p2 = bezier.getPoint(bezier.getPointCount() - 1);					
					} else if (lineStyle == GraphConstants.STYLE_SPLINE && n > 2) {
						spline = new Spline2D(p);
						double[] point = spline.getPoint(0.9875);
						// Extrapolate p2 away from the end point, pe, to avoid integer
						// rounding errors becoming too large when creating the line end
						double scaledX = pe.getX() - ((pe.getX() - point[0]) * 128);
						double scaledY = pe.getY() - ((pe.getY() - point[1]) * 128);
						p2.setLocation(scaledX, scaledY);
					}

					if (beginDeco != GraphConstants.ARROW_NONE) {
						view.beginShape = createLineEnd(beginSize, beginDeco, p1, p0);					
					}
					if (endDeco != GraphConstants.ARROW_NONE) {
						view.endShape = createLineEnd(endSize, endDeco, p2, pe);				
					}
					//if ( view!=null && view.sharedPath!=null && p0!=null) 
					boolean viewnull=view==null;
					boolean sharedpnull=getCachedViewSharedPath()==null;
					boolean p0null=p0==null;
					sharedPath.moveTo((float) p0.getX(), (float) p0.getY());
					//else 
					//	return null;
					/* THIS CODE WAS ADDED BY MARTIN KRUEGER 10/20/2003 */
					if (lineStyle == GraphConstants.STYLE_BEZIER && n > 2) {
						Point2D[] b = bezier.getPoints();
						sharedPath.quadTo((float) b[0].getX(),
								(float) b[0].getY(), (float) p1.getX(), (float) p1
								.getY());
						for (int i = 2; i < n - 1; i++) {
							Point2D b0 = b[2 * i - 3];
							Point2D b1 = b[2 * i - 2];
							sharedPath.curveTo((float) b0.getX(), (float) b0
									.getY(), (float) b1.getX(), (float) b1.getY(),
									(float) p[i].getX(), (float) p[i].getY());
						}
						sharedPath.quadTo((float) b[b.length - 1].getX(),
								(float) b[b.length - 1].getY(),
								(float) p[n - 1].getX(), (float) p[n - 1].getY());
					} else if (lineStyle == GraphConstants.STYLE_SPLINE && n > 2) {
						for (double t = 0; t <= 1; t += 0.0125) {
							double[] xy = spline.getPoint(t);
							sharedPath.lineTo((float) xy[0], (float) xy[1]);
						}
					}
					/* END */
					else {
						for (int i = 1; i < n - 1; i++)
							sharedPath.lineTo((float) p[i].getX(), (float) p[i]
									.getY());
						boolean penull=pe==null;
						boolean sharedpathnull=getCachedViewSharedPath()==null;
						//if ( view!=null && view.sharedPath!=null && pe!=null)
						sharedPath.lineTo((float) pe.getX(), (float) pe.getY());
					}
					//	if ( view!=null && view.sharedPath!=null && pe!=null)
					sharedPath.moveTo((float) pe.getX(), (float) pe.getY());
					if (view.endShape == null && view.beginShape == null) {
						// With no end decorations the line shape is the same as the
						// shared path and memory
						view.lineShape = sharedPath;
					} else {
						//				if ( view!=null && view.lineShape!=null &&  view.sharedPath!=null)
						view.lineShape = (GeneralPath) sharedPath.clone();
						Shape beginShape = view.beginShape;
						Shape endShape = view.endShape;// there must be some race condition that changes the 
						// view values before they are actually used. The other thread must set endShape attribute
						// to null, since after checking view.endShape!=null, the sharedPath.append can trigger a nullpointer
						// when trying to use view.endShape. To avoid nulls, a copy is made before the use
						if (endShape != null)
							//					if ( view!=null && view.sharedPath!=null &&  view.endShape!=null)
							sharedPath.append(endShape, true);
						if (beginShape != null)
							sharedPath.append(beginShape, true);
					}
					setCachedViewSharedPath(sharedPath);
					this.view=view;
					return sharedPath;
					//	}
				}
			} catch (Throwable t){
				t.printStackTrace();
			}
		}
		return null;
	}

	private  synchronized void setCachedViewSharedPath(GeneralPath generalPath) {
		view.sharedPath=generalPath;		
	}

	private synchronized GeneralPath getCachedViewSharedPath() {
		return view.sharedPath;
	}

	/**
	 * Paint the current view's direction. Sets tmpPoint as a side-effect such
	 * that the invoking method can use it to determine the connection point to
	 * this decoration.
	 */
	protected Shape createLineEnd(int size, int style, Point2D src, Point2D dst) {
		if (src == null || dst == null)
			return null;
		int d = (int) Math.max(1, dst.distance(src));
		int ax = (int) -(size * (dst.getX() - src.getX()) / d);
		int ay = (int) -(size * (dst.getY() - src.getY()) / d);
		if (style == GraphConstants.ARROW_DIAMOND) {
			Polygon poly = new Polygon();
			poly.addPoint((int) dst.getX(), (int) dst.getY());
			poly.addPoint((int) (dst.getX() + ax / 2 + ay / 3), (int) (dst
					.getY()
					+ ay / 2 - ax / 3));
			Point2D last = (Point2D) dst.clone();
			dst.setLocation(dst.getX() + ax, dst.getY() + ay);
			poly.addPoint((int) dst.getX(), (int) dst.getY());
			poly.addPoint((int) (last.getX() + ax / 2 - ay / 3), (int) (last
					.getY()
					+ ay / 2 + ax / 3));
			return poly;

		} else if (style == GraphConstants.ARROW_TECHNICAL
				|| style == GraphConstants.ARROW_CLASSIC) {
			Polygon poly = new Polygon();
			poly.addPoint((int) dst.getX(), (int) dst.getY());
			poly.addPoint((int) (dst.getX() + ax + ay / 2), (int) (dst.getY()
					+ ay - ax / 2));
			Point2D last = (Point2D) dst.clone();
			if (style == GraphConstants.ARROW_CLASSIC) {
				dst.setLocation((int) (dst.getX() + ax * 2 / 3), (int) (dst
						.getY() + ay * 2 / 3));
				poly.addPoint((int) dst.getX(), (int) dst.getY());
			} else if (style == GraphConstants.ARROW_DIAMOND) {
				dst.setLocation(dst.getX() + 2 * ax, dst.getY() + 2 * ay);
				poly.addPoint((int) dst.getX(), (int) dst.getY());
			} else
				dst.setLocation((int) (dst.getX() + ax),
						(int) (dst.getY() + ay));
			poly.addPoint((int) (last.getX() + ax - ay / 2), (int) (last.getY()
					+ ay + ax / 2));
			return poly;

		} else if (style == GraphConstants.ARROW_SIMPLE) {
			GeneralPath path = new GeneralPath(GeneralPath.WIND_NON_ZERO, 4);
			path.moveTo((float) (dst.getX() + ax + ay / 2), (float) (dst.getY()
					+ ay - ax / 2));
			path.lineTo((float) dst.getX(), (float) dst.getY());
			path.lineTo((float) (dst.getX() + ax - ay / 2), (float) (dst.getY()
					+ ay + ax / 2));
			return path;

		} else if (style == GraphConstants.ARROW_CIRCLE) {
			Ellipse2D ellipse = new Ellipse2D.Float((float) (dst.getX() + ax
					/ 2 - size / 2), (float) (dst.getY() + ay / 2 - size / 2),
					size, size);
			dst.setLocation(dst.getX() + ax, dst.getY() + ay);
			return ellipse;

		} else if (style == GraphConstants.ARROW_LINE
				|| style == GraphConstants.ARROW_DOUBLELINE) {
			GeneralPath path = new GeneralPath(GeneralPath.WIND_NON_ZERO, 4);
			path.moveTo((float) (dst.getX() + ax / 2 + ay / 2), (float) (dst
					.getY()
					+ ay / 2 - ax / 2));
			path.lineTo((float) (dst.getX() + ax / 2 - ay / 2), (float) (dst
					.getY()
					+ ay / 2 + ax / 2));
			if (style == GraphConstants.ARROW_DOUBLELINE) {
				path.moveTo((float) (dst.getX() + ax / 3 + ay / 2),
						(float) (dst.getY() + ay / 3 - ax / 2));
				path.lineTo((float) (dst.getX() + ax / 3 - ay / 2),
						(float) (dst.getY() + ay / 3 + ax / 2));
			}
			return path;
		}
		return null;
	}

	/**
	 * @return Returns the gradientColor.
	 */
	public Color getGradientColor() {
		return gradientColor;
	}

	/**
	 * @param gradientColor
	 *            The gradientColor to set.
	 */
	public void setGradientColor(Color gradientColor) {
		this.gradientColor = gradientColor;
	}

	/**
	 * Overridden for performance reasons. See the Implementation Note  for more information.
	 */
	public void validate() {
	}

	/**
	 * Overridden for performance reasons. See the Implementation Note  for more information.
	 */
	public void revalidate() {
	}

	/**
	 * Overridden for performance reasons. See the Implementation Note  for more information.
	 */
	public void repaint(long tm, int x, int y, int width, int height) {
	}

	/**
	 * Overridden for performance reasons. See the Implementation Note  for more information.
	 */
	public void repaint(Rectangle r) {
	}

	/**
	 * Overridden for performance reasons. See the Implementation Note  for more information.
	 */
	protected void firePropertyChange(String propertyName, Object oldValue,
			Object newValue) {
		// Strings get interned...
		if (propertyName == "text")
			super.firePropertyChange(propertyName, oldValue, newValue);
	}

	/**
	 * Overridden for performance reasons. See the Implementation Note  for more information.
	 */
	public void firePropertyChange(String propertyName, byte oldValue,
			byte newValue) {
	}

	/**
	 * Overridden for performance reasons. See the Implementation Note  for more information.
	 */
	public void firePropertyChange(String propertyName, char oldValue,
			char newValue) {
	}

	/**
	 * Overridden for performance reasons. See the Implementation Note  for more information.
	 */
	public void firePropertyChange(String propertyName, short oldValue,
			short newValue) {
	}

	/**
	 * Overridden for performance reasons. See the Implementation Note  for more information.
	 */
	public void firePropertyChange(String propertyName, int oldValue,
			int newValue) {
	}

	/**
	 * Overridden for performance reasons. See the Implementation Note  for more information.
	 */
	public void firePropertyChange(String propertyName, long oldValue,
			long newValue) {
	}

	/**
	 * Overridden for performance reasons. See the Implementation Note  for more information.
	 */
	public void firePropertyChange(String propertyName, float oldValue,
			float newValue) {
	}

	/**
	 * Overridden for performance reasons. See the Implementation Note  for more information.
	 */
	public void firePropertyChange(String propertyName, double oldValue,
			double newValue) {
	}

	/**
	 * Overridden for performance reasons. See the Implementation Note  for more information.
	 */
	public void firePropertyChange(String propertyName, boolean oldValue,
			boolean newValue) {
	}

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy