package jsetool.gui; import java.awt.*; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Paint; import java.awt.event.MouseEvent; import java.awt.geom.Line2D; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; import java.io.*; import java.io.Externalizable; /** * * @author Mike Lawson * * @since Nov 12, 2004 * * The PointToPointEdge class is a Drawable that represents an edge from point * to point. This class provides interfaces for determining the slope * and y-intersect of the edge as well. * * */ public class PointToPointEdge implements Drawable, Externalizable { public static final double MOUSE_OVER_THRESHOLD = 7; protected transient Line2D edge; protected double endX; protected double endY; protected boolean hasArrowHead = false; protected Paint highlightPaint = Color.RED; protected boolean isHighlighted = false; protected Paint normalPaint = Color.BLACK; protected boolean objectIsSelected; protected double startX; protected double startY; private boolean isEditable = true; private boolean toBeDeleted; /** * Constructs a new point to point Edge with no points */ public PointToPointEdge() { } /** * Constructs a new point to point Edge from the given point to the given * point. * * @param startX * @param startY * @param endX * @param endY */ public PointToPointEdge(double startX, double startY, double endX, double endY) { this.startX = startX; this.startY = startY; this.endX = endX; this.endY = endY; } /** * draws this objects * * @param g */ public void draw(Graphics g) { Graphics2D g2d = (Graphics2D) g; if (isHighlighted) { g2d.setPaint(highlightPaint); } else { g2d.setPaint(normalPaint); } edge = new Line2D.Double(startX, startY, endX, endY); g2d.draw(edge); if (hasArrowHead) { drawArrowHead(g2d); } if (objectIsSelected) { drawHighlightSquares(g2d); } } /** * Returns the X coordinate * @return double */ public double getX() { return edge.getBounds2D().getX(); } /** * Returns the Y coordinate * @return double */ public double getY() { return edge.getBounds2D().getY(); } /** * Returns the height of the object. * @return double */ public double getHeight() { return edge.getBounds2D().getHeight(); } /** * Returns the width of the object. * @return double */ public double getWidth() { return edge.getBounds2D().getWidth(); } /** * Returns a point on the line that is close to the given point. * * @param x * @param y * @return Point2D */ public Point2D getPointOnLineCloseTo(double x, double y) { Point2D answer = null; if (slope() == 0) { // If the line is flat, just drop the point straight down // onto the line. answer = new Point2D.Double(x, edge.getY1()); } else if (slope() == Double.NEGATIVE_INFINITY || slope() == Double.POSITIVE_INFINITY) { // If the line is vertical, shove the point onto the // line answer = new Point2D.Double(edge.getX1(), y); } else { // Who said geometry was useless? y = mx + b answer = new Point2D.Double(x, slope() * x + yIntersect()); } return answer; } /* * (non-Javadoc) * * @see jsetool.gui.Drawable#isMouseOn(java.awt.event.MouseEvent) */ public boolean isMouseOn(MouseEvent me) { if (edge == null) return false; if (me == null) return false; // Note, we can't just use edge.contains(point) because // the implementation of the line correctly states that // lines have no area, therefore the edge can never contain anything. // First check to see if the mouse is in the bounding box // of the line. If the width or height of the rectangle is too // small (i.e. the line is perfectly vertical or horizontal) // do the full calculation anyway. Rectangle2D bounds = edge.getBounds2D(); if (!bounds.contains(me.getPoint()) && bounds.getWidth() > MOUSE_OVER_THRESHOLD && bounds.getHeight() > MOUSE_OVER_THRESHOLD) { return false; } else { double dist = edge.ptLineDist(me.getX(), me.getY()); // The mouse is "on" the line if it is within the minimum threshold if (Math.abs(dist) < MOUSE_OVER_THRESHOLD) { return true; } else { return false; } } } /* * (non-Javadoc) * * @see jsetool.gui.Drawable#mouseDragged(java.awt.event.MouseEvent) */ public void mouseDragged(MouseEvent me) { // nada for PointToPointEdge } /* * (non-Javadoc) * * @see jsetool.gui.Drawable#mousePressed(java.awt.event.MouseEvent) */ public void mousePressed(MouseEvent me) { // nada for PointToPointEdge } /* * (non-Javadoc) * * @see jsetool.gui.Drawable#mouseReleased(java.awt.event.MouseEvent) */ public void mouseReleased(MouseEvent me) { // nada for PointToPointEdge } /* * (non-Javadoc) * * @see jsetool.gui.Drawable#objectIsBeingDeleted() */ public void objectIsBeingDeleted() { toBeDeleted = true; } /* * (non-Javadoc) * * @see jsetool.gui.Drawable#toBeDeleted() */ public boolean toBeDeleted() { return toBeDeleted; } /** * Externalizable implementation. Reads a PointToPointEdge from the * given input stream * @param in * @throws IOException * @throws ClassNotFoundException * */ public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { startX = in.readDouble(); startY = in.readDouble(); endX = in.readDouble(); endY = in.readDouble(); isEditable = in.readBoolean(); hasArrowHead = in.readBoolean(); edge = new Line2D.Double(startX, startY, endX, endY); } /** * Externalizable implementation. Writes a PointToPointEdge to the * given output stream * @param out * @throws IOException * */ public void writeExternal(ObjectOutput out) throws IOException { out.writeDouble(startX); out.writeDouble(startY); out.writeDouble(endX); out.writeDouble(endY); out.writeBoolean(isEditable); out.writeBoolean(hasArrowHead); } /** * Sets the points for the edge to the given points. * * @param startX * @param startY * @param endX * @param endY */ public void setEdgePoints(double startX, double startY, double endX, double endY) { this.startX = startX; this.startY = startY; this.endX = endX; this.endY = endY; } /* * (non-Javadoc) * * @see jsetool.gui.Drawable#setEditable(boolean) */ public void setEditable(boolean isEditable) { this.isEditable = isEditable; } /* * (non-Javadoc) * * @see jsetool.gui.Drawable#setExplored(boolean) */ public void setExplored(boolean b) { // does nothing for this object. } /** * Sets whether this edge has an arrow head or not. * * @param a */ public void setHasArrowHead(boolean a) { hasArrowHead = a; } /* * (non-Javadoc) * * @see jsetool.gui.Drawable#setHighlight(boolean) */ public void setHighlight(boolean doHighlight) { isHighlighted = doHighlight; } /* * (non-Javadoc) * * @see jsetool.gui.Drawable#setSelected(boolean, java.awt.event.MouseEvent) */ public void setSelected(boolean isSelected, MouseEvent me) { objectIsSelected = isSelected; } /* * (non-Javadoc) * * @see jsetool.gui.Drawable#showEditor(java.awt.event.MouseEvent) */ public void showEditor(MouseEvent me) { } /** * Returns the slope of the line * * @return double */ public double slope() { // m = dy / dx double m = (edge.getY1() - edge.getY2()) / (edge.getX1() - edge.getX2()); return m; } /** * Returns the y intersect of the line * * @return double */ public double yIntersect() { // b = y - mx double b = edge.getY1() - slope() * edge.getX1(); return b; } /** * Returns the distance of a line, based on delta x and delta y. * * @param dx * @param dy * @return double */ protected double dist(double dx, double dy) { return Math.sqrt(dx * dx + dy * dy); } /** * Draws an arrow head on this edge. * * @param g * graphics context on which to draw. * */ protected void drawArrowHead(Graphics2D g) { int arrow_height = 20; int arrow_width = 5; double xFrom = startX; double xTo = endX; double yFrom = startY; double yTo = endY; double dx = xTo - xFrom; double dy = yTo - yFrom; double denom = dist(dx, dy); if (denom == 0.0D) { return; } else { double cos = (double) (arrow_height / 2) / denom; double sin = (double) arrow_width / denom; double x = (double) xTo - cos * dx; double y = (double) yTo - cos * dy; int x1 = (int) (x - sin * dy); int y1 = (int) (y + sin * dx); int x2 = (int) (x + sin * dy); int y2 = (int) (y - sin * dx); Polygon triangle = new Polygon(); triangle.addPoint((int) xTo, (int) yTo); triangle.addPoint(x1, y1); triangle.addPoint(x2, y2); Paint arrowColor = isHighlighted ? highlightPaint : normalPaint; g.setPaint(arrowColor); g.fillPolygon(triangle); g.setPaint(arrowColor); g.drawPolygon(triangle); return; } } /** * Draws squares that show the bounds of this object. * @param g2d */ protected void drawHighlightSquares(Graphics2D g2d) { g2d.setPaint(Color.ORANGE); g2d.fill(new Rectangle.Double(startX - 3, startY - 3, 6, 6)); g2d.fill(new Rectangle.Double(endX - 3, endY - 3, 6, 6)); } }