MathWorx

Line.js

Summary

This file defines the Line class with a point-slope like definition.


Class Summary
Line The Line class defines a new object type for a line.

/**MathWorx Copyright (c) 2008 Shane Steinert-Threlkeld
Dual-licensed under the MIT and GNU GPL Licenses.
For more information, see LICENSE file */

/**@fileoverview This file defines the Line class with a point-slope like definition. */

dependencies = ['point2D','point3D'];
require(dependencies);

/**The constructor function for a new Line.  If only a Line object is supplied as a parameter, a duplicate of that Line will be created.
@class The Line class defines a new object type for a line.  It is in a sense a "point-slope" definition of a line.
A Line has a base point and a direction, the direction being a unit vector.
This is an effective way of defining a line in a way that does not limit it to any specific segment and allows for Affine Transformations to be applied.
@param {Object} base the base point of the Line; can be a point2D, point3D or Vector (of dimension 2 or 3) object.  The actual base point of the Line will be a point3D object. (If base is a Line object, creates a clone)
@param {Object} direction the direction of the Line; can be a point2D, point3D or Vector.  Actual direction will be a normalized point3D object.
@requires point3D
@constructor */
function Line(base, direction) {

	if(base instanceof Line) {
		return new Line(base.base, base.direction);
	}

	/**The "anchor" point.
	@type point3D */
	this.base = new point3D(0,0,0);
	/**The direction of the line; will be a unit vector. Use point3D object for this to allow Affine Transformations.
	@type point3D */
	this.direction = new point3D(0,0,0);

	if(!(base.dimension() && direction.dimension())) {
		return null;
	}
	if(base.n > 3 || direction.n > 3) {
		return null;
	}

	if(base.n == 2) {
		//handles point2D or Vector object
		this.base = new point3D(base.get(1), base.get(2), 0);
	} else {
		if(base instanceof point3D) {
			this.base = new point3D(base);
		} else if(base instanceof Vector) {
			this.base = new point3D(base.get(1), base.get(2), base.get(3));
		}
	}

	if(direction.n == 2) {
		this.direction = (new point3D(base.get(1), base.get(2), 0)).normalize();
	} else {
		if(direction instanceof point3D) {
			this.direction = (new point3D(direction)).normalize();
		} else if(direction instanceof Vector) {
			this.direction = (new point3D(direction.get(1), direction.get(2), direction.get(3))).normalize();
		}
	}

}

Line.prototype = {

	/**Tests two lines for equality.  Will return true if line is parallel or antiparallel.
	@param {Line} line the line to compare
	@return {Boolean} true if line has same base point and is parallel or antiparallel */
	equalTo: function(line) {
		return (this.isParallel(line) && this.contains(line.base));
	},

	/**Tests to see if a point lies on the line.
	@param {point3D} the point to test
	@return {Boolean} true iff point is on line, false otherwise */
	contains: function(point) {
		var d = this.distanceFrom(point);
		return (d != null && d < Zero);
	},

	/**Tests to see if a line is parallel to another line or plane.
	@param {Object} obj either a Line or a Plane, with which to test parallelity
	@return {Boolean} true if parallel or anti-parallel, false otherwise */
	isParallel: function(obj) {
		//if(obj instanceof Plane)
		var theta = this.direction.angleBetween(obj.direction);
		return (Math.abs(theta) < Zero || Math.abs(theta - Math.PI) < Zero);
	},

	//For distanceFrom and pointClosestTo, thanks goes out to: http://web.uct.ac.za/courses/end107w/notes/closestpoint.pdf

	/**Gets the distance between a line and another line, plane, or a point.
	@param {Object} obj either a Line, Plane, point2D or point3D, from which to find the distance to the line
	@return {Number} the distance between obj and the line; 0 if two lines have same base or one contains the base of the other */
	distanceFrom: function(obj) {
		/*if(obj instanceof Plane) {

		}*/
		if(obj instanceof Line) {
			if(this.isParallel(obj)) {
				return this.distanceFrom(obj.base);
			}
			var norm = this.direction.cross(obj.direction).normalize();
			var d = this.base.subtract(obj.base);
			return Math.abs(d.dot(norm));
		} else { //obj is a point
			var point = obj;
			if(obj instanceof point2D) {
				point = point.make3D();
			}
			var pointDist = point.subtract(this.base);
			var pointLength = pointDist.getLength();
			if(pointLength == 0) { 
				return 0;
			}
			var cosT = pointDist.dot(this.direction) / pointLength;
			var sin2 = 1 - cosT * cosT;
			return Math.abs(pointLength * Math.sqrt(sin2 < 0 ? 0 : sin2));
		}
	},

	/**Returns the point on the line at a given value of t.
	@param {Number} t the time value at which to find the point on the line 
	@return {point3D} this.base + t*this.direction */
	pointAt: function(t) {
		return this.base.add(this.direction.multiplyBy(t));
	},

	/**Finds the point on this line closest to a given line or point.
	@param {Object} obj a Line or point (or Vector)
	@return {point3D} the point on the original line closes to obj */
	pointClosestTo: function(obj) {
		if(obj instanceof Line) {
			if(this.intersects(obj)) {
				return this.intersection(obj);
			}
			if(this.isParallel(obj)) {
				return null;
			}
			var norm = this.direction.cross(obj.direction);
			var p = new Plane(obj.base, norm);
			return p.intersection(this);
		} else if(obj.dimension()) {
			var p = obj;
			if(obj instanceof point2D) {
				p = obj.make3D();
			}
			if(p instanceof Vector && p.n == 2) {
				p = p.make3D().toPoint();
			}
			if(this.contains(p)) {
				return p;
			}
			var plane = new Plane(p, this.direction);
			return plane.intersection(this);
		} else {
			return null;
		}		
	},

	/**Rotates a line through an angle about an arbitrary line.  Be careful, rotation axis' direction affects the outcome!
	@param {Number} deg the degrees, in radians, to rotate the line
	@param {Line} the line about which to rotate this line
	@return {Line} a new line that is the old one rotated */
	rotate: function(deg, line) {
		var C = line.pointClosestTo(this.base);
		var base = C.add(this.base.subtract(C).rotate(deg,line.direction));
		var direction = this.direction.rotate(deg,line.direction);
		return new Line(base, direction);
	},

	/**Returns the line's reflection in the given point, line, or plane.
	@param {Object} obj Plane, Line, or point2D/3D/Vector about which to reflect the line
	@return {Line} a new line, the old one reflected in obj */
	reflectionIn: function(obj) {
		if(obj instanceof Plane) {
			var B = this.base, D = this.direction;
			var newB = this.base.reflectionIn(obj);
			var BD = B.add(D);
			var Q = obj.pointClosestTo(BD);
			var newD = Q.multiplyBy(2).subtract(BD).subtract(newB);
			return new Line(newB, newD);
		}
		if(obj instanceof Line) {
			return this.rotate(Math.PI, obj);
		}
		if(obj.dimension()) {
			return new Line(this.base.reflectionIn(obj), this.direction);
		}
	},

	/**Tests if a Line lies in a given Plane.
	@param {Plane} plane the plane which may or may not contain this Line
	@return {Boolean} true iff Line lies in plane, false otherwise */
	liesIn: function(plane) {
		return plane.contains(this);
	},

	/**Tests if this line intersects with a given line or plane.
	@param {Object} obj Line or Plane, to test for intersection with the line
	@return {Boolean} true if there is a unique intersection point between the line and the given line or plane, false otherwise */
	intersects: function(obj) {
		if(obj instanceof Plane) {
			return obj.intersects(this);
		}
		if(!(obj instanceof Line)) {
			return null;
		}
		return (!this.isParallel(obj) && this.distanceFrom(obj) < Zero);
	},

	/**Finds the UNIQUE intersection of a line and another Line or Plane.
	@param {Object} obj the line or plane with which to find the intersection point
	@return {point3D} the intersection point (null if obj does not intersect with line) */
	intersection: function(obj) {
		//if(obj instanceof Plane)
		if(!this.intersects(obj)) {
			return null;
		}
		var p = this.base, x = this.direction, q = obj.base, y = obj.direction;
		var psubq = p.subtract(q);
		var xdotqsubp = -x.dot(psubq);
		var ydotpsubq = y.dot(psubq);
		var xdotx = x.dot(x), ydoty = y.dot(y), xdoty = x.dot(y);
		var k = (xdotqsubp * ydoty / xdotx + xdoty * ydotpsubq) / (ydoty - xdoty * xdoty);
		return p.add(x.multiplyBy(k));
	},

	/**Returns a new Line that is the old one translated by dx, dy, and dz.  Note that direction does not change in a translation, only the base point.
	@param {Number} dx the x translation
	@param {Number} dy the y translation
	@param {Number} dz the z translation
	@return {Line} the translated line */
	translate: function(dx, dy, dz) {
		var base = this.base.translate(dx, dy, dz);
		return new Line(base, this.direction);
	},

	/**Returns a string representation of the Line.
	@return {String} the string representation of the line, containing all relevant info */
	toString: function() {
		return "base: " +this.base+ ", direction: " + this.direction;
	}

} //end Line prototype

/**The x-axis.  Base: (0,0,0), Direction: Vector.i
@type Line */
Line.x = new Line(point3D.zero, Vector.i);
/**The y-axis.  Base: (0,0,0), Direction: Vector.j
@type Line */
Line.y = new Line(point3D.zero, Vector.j);
/**The z-axis.  Base: (0,0,0), Direction: Vector.k
@type Line */
Line.z = new Line(point3D.zero, Vector.k);

MathWorx

Documentation generated by JSDoc on Mon Aug 11 13:58:31 2008