// Copyright (c) 1995, 1996 Regents of the University of California. // All rights reserved. // // This software was developed by the Arcadia project // at the University of California, Irvine. // // Redistribution and use in source and binary forms are permitted // provided that the above copyright notice and this paragraph are // duplicated in all such forms and that any documentation, // advertising materials, and other materials related to such // distribution and use acknowledge that the software was developed // by the University of California, Irvine. The name of the // University may not be used to endorse or promote products derived // from this software without specific prior written permission. // THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR // IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED // WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. // File: FigLine.java // Classes: FigLine // Original Author: ics125b spring 1996 // $Id: FigLine.java,v 1.1.1.1 1997/02/27 20:52:40 chandra Exp $ package uci.graphedit; import java.awt.Color; import java.awt.Rectangle; import java.awt.Point; import java.awt.Graphics; import java.util.Hashtable; /** Class to implement lines in diagrams. */ public class FigLine extends Fig { public static final int FUDGE_FACTOR = 4; /** Construct a new FigLine with the given coordinates and color. */ public FigLine(int x, int y, int r_width, int r_height, Color l_color){ super(x, y, r_width, r_height, l_color, null, 2); } public FigLine(int x,int y,int r_width,int r_height, Hashtable gAttrs){ super(x, y, r_width, r_height, Color.black, null, 2); setGraphicAttributes(gAttrs); } /** Translate this Fig. */ public void translate(int dx, int dy) { position().x += dx; position().y += dy; objectWidth += dx; objectHeight += dy; } /** Set the position of this Fig to the goven location */ public void position(int x, int y) { int dx = x - position().x; int dy = y - position().y; translate(dx,dy); } /** draw this line object */ public void draw(Graphics g) { g.setColor(objectLineColor); g.drawLine(position().x, position().y, objectWidth, objectHeight); } /** draw this line when it is selected. */ public void drawSelected(Graphics g) { _handleRects[0].x = position().x - 3; _handleRects[0].y = position().y - 3; _handleRects[1].x = objectWidth - 3; _handleRects[1].y = objectHeight - 3; drawHandles(g); } /** When this Fig is selected, the Editor should use an instance of * SelectionHandles to record that fact. */ public Selection selectionObject() { return new SelectionHandles(this); } /** Reply true iff the given point is "near" the line. Nearness * allows the user to more easily select the line with the * mouse. Needs-More-Work: I should probably have two functions * inside() which gives a strict geometric version, and near() which * is for selection by mouse clicks. */ public boolean inside(int x, int y) { return intersects(new Rectangle(x - GRIP_MARGIN, y - GRIP_MARGIN, 2 * GRIP_MARGIN, 2 * GRIP_MARGIN)); } /** Reply true if the given point is counter-clockwise from the * vector defined by the position of this line and its endpoint. This * is used as in determining intersection between lines and * rectangles. Taken from Algorithms in C by Sedgewick, page * 350. */ int counterClockWise(int x, int y) { Point p0 = position(); Point p1 = new Point(objectWidth, objectHeight); Point p2 = new Point(x, y); /* the point to test */ int dx1 = p1.x - p0.x; int dy1 = p1.y - p0.y; int dx2 = p2.x - p0.x; int dy2 = p2.y - p0.y; if (dx1*dy2 > dy1*dx2) return +1; if (dx1*dy2 < dy1*dx2) return -1; if ((dx1*dx2 < 0) || (dy1*dy2 < 0)) return -1; if ((dx1*dx1+dy1*dy1) < (dx2*dx2+dy2*dy2)) return +1; return 0; } /** Reply true iff this line passes through, or is even partly * inside the given rectangle, or if any corner of the rect is on the * line. What happens if the line runs along one edge of the rect, * but not all the way to either corner? */ public boolean intersects(Rectangle r) { if (! r.intersects(getBoundingBox())) return false; int ccw1 = counterClockWise(r.x, r.y); int ccw2 = counterClockWise(r.x, r.y + r.height); int ccw3 = counterClockWise(r.x + r.width, r.y); int ccw4 = counterClockWise(r.x + r.width, r.y + r.height); // reply true iff any of the points are on opposite sides of the // line, or if any of them are on the line return ((ccw1 == 1 || ccw2 == 1 || ccw3 == 1 || ccw4 == 1) && (ccw1 == -1 || ccw2 == -1 || ccw3 == -1 || ccw4 == -1) || ccw1 == 0 || ccw2 == 0 || ccw3 == 0 || ccw4 == 0); } /** Reply a rect that encloses this line. I add a few pixels on * either side so that the rect will have non-zero area. */ public Rectangle getBoundingBox() { Point p = position(); return new Rectangle(Math.min(p.x, objectWidth) - 1, Math.min(p.y, objectHeight) - 1, Math.max(p.x, objectWidth) - Math.min(p.x, objectWidth) + 2, Math.max(p.y, objectHeight) - Math.min(p.y, objectHeight) + 2); } /** Allow he user to change either end point of the line by dragging * on a handle. */ public void dragHandle(int m_x, int m_y, int an_x, int an_y, int handle) { int world_x = m_x; int world_y = m_y; switch (handle) { case 0: // The origin handle position().x = m_x; position().y = m_y; break; case 1: // the endpoint handle objectWidth = m_x; objectHeight = m_y; break; } } /** Resize the object for drag on creation. It bypasses the things * done in resize so that the position of the object can be kept as * the anchor point. Needs-More-Work: do I really need this * function? * @see FigLine#drag */ public void createDrag(int anchorX, int anchorY, int x, int y) { objectWidth = x; objectHeight = y; } } /* end class FigLine */