API Docs for: 1.0.0
Show:

File: src/gallery-mathcanvas/js/RectList.js

"use strict";

/**
 * @module gallery-mathcanvas
 */

/**********************************************************************
 * <p>Manages all the bounding rectangles for an expression.</p>
 * 
 * <p>Each item contains rect (top,left,bottom,right), midline,
 * font_size(%), func.</p>
 * 
 * @class RectList
 * @namespace MathCanvas
 * @constructor
 */

function RectList()
{
	this.list = [];
}

/**
 * @method width
 * @static
 * @param r {Rect} rectangle
 * @return width
 */
RectList.width = function(r)
{
	return r.right - r.left;
};

/**
 * @method height
 * @static
 * @param r {Rect} rectangle
 * @return height
 */
RectList.height = function(r)
{
	return r.bottom - r.top;
};

/**
 * @method xcenter
 * @static
 * @param r {Rect} rectangle
 * @return horizontal center
 */
RectList.xcenter = function(r)
{
	return Math.floor((r.left + r.right)/2);
};

/**
 * @method ycenter
 * @static
 * @param r {Rect} rectangle
 * @return vertical center
 */
RectList.ycenter = function(r)
{
	return Math.floor((r.top + r.bottom)/2);
};

/**
 * @method area
 * @static
 * @param r {Rect} rectangle
 * @return area
 */
RectList.area = function(r)
{
	return RectList.width(r) * RectList.height(r);
};

/**
 * @method containsPt
 * @static
 * @param r {Rect} rectangle
 * @param xy {point} point
 * @return true if rectangle contains point
 */
RectList.containsPt = function(r, xy)
{
	return (r.left <= xy[0] && xy[0] < r.right &&
			r.top  <= xy[1] && xy[1] < r.bottom);
};

/**
 * @method containsRect
 * @static
 * @param r1 {Rect}
 * @param r2 {Rect}
 * @return true if r1 contains r2
 */
RectList.containsRect = function(r1, r2)
{
	return (r1.left <= r2.left && r2.left <= r2.right && r2.right <= r1.right &&
			r1.top <= r2.top && r2.top <= r2.bottom && r2.bottom <= r1.bottom);
};

/**
 * @method cover
 * @static
 * @param r1 {Rect} rectangle
 * @param r2 {Rect} rectangle
 * @return rectangle convering both input arguments
 */
RectList.cover = function(r1, r2)
{
	var r =
	{
		top:    Math.min(r1.top, r2.top),
		left:   Math.min(r1.left, r2.left),
		bottom: Math.max(r1.bottom, r2.bottom),
		right:  Math.max(r1.right, r2.right)
	};
	return r;
};

RectList.prototype =
{
	/**
	 * @method add
	 * @param r {Rect}
	 * @param midline {int}
	 * @param font_size {int} percentage
	 * @param func {MathFunction}
	 * @return index of inserted item
	 */
	add: function(
		/* rect */			r,
		/* int */			midline,
		/* percentage */	font_size,
		/* MathFunction */	func)
	{
		this.list.push(
		{
			rect:      r,
			midline:   midline,
			font_size: font_size,
			func:      func
		});

		return this.list.length-1;
	},

	/**
	 * @method size
	 * @return number of items
	 */
	size: function()
	{
		return this.list.length;
	},

	/**
	 * @method get
	 * @param index {int}
	 * @return item at index
	 */
	get: function(
		/* int */	index)
	{
		return this.list[ index ];
	},

	/**
	 * @method find
	 * @param f {MathFunction} search target
	 * @return data for specified MathFunction, or null if not found
	 */
	find: function(
		/* MathFunction */	f)
	{
		return Y.Array.find(this.list, function(r)
		{
			return (r.func === f);
		});
	},

	/**
	 * @method findIndex
	 * @param f {MathFunction} search target
	 * @return index of item for specified MathFunction, or -1 if not found
	 */
	findIndex: function(
		/* MathFunction */	f)
	{
		return Y.Array.indexOf(this.list, this.find(f));
	},

	/**
	 * Shift the specified rect and all rects inside it.
	 * 
	 * @method shift
	 * @param index {int}
	 * @param dx {int} horizontal shift
	 * @param dy {int} vertical shift
	 */
	shift: function(
		/* int */	index,
		/* int */	dx,
		/* int */	dy)
	{
		if (dx === 0 && dy === 0)
		{
			return;
		}

		var info = this.list[ index ];
		var orig = Y.clone(info.rect, true);
		info.rect.top    += dy;
		info.rect.left   += dx;
		info.rect.bottom += dy;
		info.rect.right  += dx;
		info.midline     += dy;

		Y.Array.each(this.list, function(info1)
		{
			if (orig.left <= info1.rect.left && info1.rect.right <= orig.right &&
				orig.top <= info1.rect.top && info1.rect.bottom <= orig.bottom)
			{
				info1.rect.top    += dy;
				info1.rect.left   += dx;
				info1.rect.bottom += dy;
				info1.rect.right  += dx;
				info1.midline     += dy;
			}
		});
	},

	/**
	 * Set the midline of the specified rectangle.
	 * 
	 * @method setMidline
	 * @param index {int}
	 * @param y {int} midline
	 */
	setMidline: function(
		/* int */	index,
		/* int */	y)
	{
		this.shift(index, 0, y - this.list[index].midline);
	},

	/**
	 * @method getBounds
	 * @return the bounding rect of all the rects in the list
	 */
	getBounds: function()
	{
		return this.list[ this.list.length-1 ].rect;
	},

	/**
	 * Returns the index of the smallest rectangle that contains both
	 * startPt and currPt.  Returns -1 if there is no such rectangle.  If
	 * startPt is inside the bounding rectangle and currPt is outside, we
	 * return the index of the bounding rectangle.
	 * 	
	 * @method getSelection
	 * @param start_pt {point} point where the drag started
	 * @param curr_pt {point} current cursor location
	 */
	getSelection: function(
		/* point */	start_pt,
		/* point */	curr_pt)
	{
		// Check if start_pt is in the bounding rect.

		var bounds = this.getBounds();
		if (!RectList.containsPt(bounds, start_pt))
		{
			return -1;
		}

		// The bounding rect is the last rect in the list.

		var minArea = 0;
		var result  = this.list.length-1;
		Y.Array.each(this.list, function(info, i)
		{
			var area = RectList.area(info.rect);
			if (RectList.containsPt(info.rect, start_pt) &&
				RectList.containsPt(info.rect, curr_pt) &&
				(minArea === 0 || area < minArea))
			{
				result  = i;
				minArea = area;
			}
		});

		return result;
	},

	/**
	 * Returns the index of the smallest rectangle enclosing the given one.
	 * 
	 * @method getParent
	 * @param index {int}
	 */
	getParent: function(
		/* int */	index)
	{
		var small_rect = this.list[index].rect;
		for (var i=index+1; i<this.list.length; i++)
			{
			var big_rect = this.list[i].rect;
			if (RectList.containsRect(big_rect, small_rect))
				{
				return i;
				}
			}

		// The last element is always the largest, and includes all others.

		return this.list.length-1;
	}
};