/**************************************************************************
* Copyright (c) 1996 Jim Crossley
*
* Although it's unlikely that anyone would actually want it,
* permission is hereby granted, without written agreement and
* without license or royalty fees, to use, copy, modify, and
* distribute this software and its documentation for any purpose,
* provided that the above copyright notice and the following three
* paragraphs appear in all copies of this software, its documentation
* and any derivative work.
*
* In no event shall Jim Crossley or Automated Design Systems, Inc. be
* liable to any party for direct, indirect, special, incidental, or
* consequential damages arising out of the use of this software and
* its documentation.
*
* The software provided hereunder is on an "as is" basis, and neither
* Jim Crossley nor Automated Design Systems, Inc. has any obligation
* to provide maintenance, support, updates, enhancements, or modifications.
*
* Derived or altered versions must be plainly marked as such, and
* must not be misrepresented as being the original software.
***************************************************************************/
package dots;

import java.awt.*;
import java.util.*;

/**
 * Given a beginning point and an end point, this thread repeatedly draws dots from
 * one to the other.
 *
 * @author Jim Crossley
 * @version 1.0, 10/17/1996
 */
class DottedLine implements Runnable {

    private Vector dots; /** The Dot objects that make up the line. */
    private int dotCount; /** The size of the above vector. */
    private Component target; /** The AWT component on which to draw. */
    private Color color; /** The color of the DottedLine. */
    private int diameter; /** The diameter of the Dots that make up the line. */
    private int delay; /** Determines how fast the Dots are displayed. */
    private int decay = 1; /** Determines how long a Dot lingers after being drawn. */

    private Thread line = null; /** The line drawer */

    /**
     * The constructor sets member variables and starts the thread.
     */
    public DottedLine(Point p, Point q, int d, Color c, int s, int decay, Component t) {
        target = t;
        color = c;
        diameter = d;
        delay = s;
        if (decay > 0) this.decay = decay;
        dotCount = MakeDots(p,q,d);

        start();
    }

    /**
     * Construct the dots vector using the beginning and end points, as well as each
     * Dot's diameter.
     *
     * y = mx + b
     */
    private int MakeDots(Point p, Point q, int increment) {
        dots = new Vector();
        if (p.equals(q)) {
            dots.addElement(new Dot(p, diameter));
        }
        else {
            int dx = p.x - q.x;
            int dy = p.y - q.y;
            int x;
            int y;
            // We use an offset to account for the fact that an oval's position is defined
            // not by its center, but by the upper-left corner of the rectangle around it.
            int offset = (int)(diameter/2);

            if (Math.abs(dx) > Math.abs(dy)) {
                double m = (double)dy/dx;
                double b = (double)(p.y - m*p.x);

                for (int i=0; Math.abs(i) <= Math.abs(dx); i += (dx<0 ? increment : -increment)) {
                    x = p.x + i - offset;
                    y = (int) (m * x + b - offset);
                    dots.addElement(new Dot(new Point(x, y), diameter));
                }
            }
            else {
                double m = (double)dx/dy;
                double b = (double)(p.x - m*p.y);

                for (int i=0; Math.abs(i) <= Math.abs(dy); i += (dy<0 ? increment : -increment)) {
                    y = p.y + i - offset;
                    x = (int) (m * y + b - offset);
                    dots.addElement(new Dot(new Point(x, y), diameter));
                }
            }
        }
        return dots.size();
    }

    /**
     * Access method for member variable, <em>delay</em>.
     */
    public void setDelay(int d) {
        delay = d;
    }

    /**
     * Access method for member variable, <em>decay</em>.
     */
    public void setDecay(int d) {
        decay = (d>0)?d:1;
    }

    /**
     * Cycle through the Dot objects, calling the draw method of each one.
     */
    private void draw() {
        Graphics g = target.getGraphics();
        for (int i=0; i < dotCount; i++) {
            Dot dot = (Dot) dots.elementAt(i);
            dot.draw(g);
        }
        g.dispose();
    }

    /**
     * Cycle through the Dots, allowing previous ones to decay, i.e. darken.
     */
    public void run() {
        do {
            try {
                for (int i=0; i < dotCount && Thread.currentThread() == line; i++) {
                    Dot dot = (Dot) dots.elementAt(i);
                    dot.setColor(color);
                    Color c = color;
                    for (int j=i-1; j>=0; j--) {
                        dot = (Dot) dots.elementAt(j);
                        if (dot.getColor().equals(Color.black)) break; // save some time
                        dot.setColor(c);
                        if ((i-j) % decay == 0) c = c.darker();
                    }
                    for (int j=dotCount-1; j>i; j--) {
                        dot = (Dot) dots.elementAt(j);
                        if (dot.getColor().equals(Color.black)) break; // save some time
                        dot.setColor(c);
                        if ((dotCount+i-j) % decay == 0) c = c.darker();
                    }
                    draw();
                    try {
                        Thread.sleep(delay);
                    }
                    catch (InterruptedException e) {}
                }
            }
            catch (Exception e) { break; }
        } while (Thread.currentThread() == line);

        line = null;
    }

	public void start() {
		if (line == null) {
			line = new Thread(this);
			line.start();
		}
	}
	public void stop() {
		line = null;
	}
}


