// 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 */