/**
* @author Wolfgang Kowarschick
* @copyright 2012-2013, Wolfgang Kowarschick
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted under the terms of the
* Creative Commons License Attribution-NonCommercial-ShareAlike 3.0 Unported
* (CC BY-NC-SA 3.0: http://creativecommons.org/licenses/by-nc-sa/3.0/).
*/
////////////////////////////////////////////////////////////////////////////////
// Class "$wk$.math.geo2d.WKcVector2D"
////////////////////////////////////////////////////////////////////////////////
(function(pkg)
{ "use strict";
pkg("$wk$.math.geo2d");
// Import.
var WKcClass = $wk$.WKcClass;
var f_merge_into = WKcClass.mergeInto;
var WKcMath = $wk$.math.WKcMath;
var WKtVector2D = $wk$.math.geo2d.WKtVector2D;
/**
* @param {WKcVector2D|{min: WKcVector2D, max: WKcVector2D}} p_interval
* A single vector, an object with two vectors defining an interval,
* for each element, or an array of such values.
* @returns {WKcVector2D}
* A random vector out of the values described by
* <code>p_interval</code>.
*/
WKcMath.randomVector2D =
function(p_interval)
{ if (p_interval == null)
new WKcVector2D;
if (p_interval instanceof Array)
p_interval = p_interval[Math.floor(Math.random()*p_interval.length)];
if (p_interval instanceof Object && p_interval.min && p_interval.max)
{ var l_0 = p_interval.min, l_1 = p_interval.max;
return new WKcVector2D(WKcMath.random({min: l_0.x, max: l_1.x}),
WKcMath.random({min: l_0.y, max: l_1.y})
);
}
else
return new WKcVector2D(p_interval.x, p_interval.y);
};
//============================================================================
// Constructor
//============================================================================
/**
* @constructor
*
* Creates a new vector and initializes it.
*
* @param {Number} [p_x = 0] The x-coordinate of the new vector.
* @param {Number} [p_y = 0] The y-coordinate of the new vector.
*/
function WKcVector2D(p_x, p_y)
{ this.v_x = p_x || 0.0;
this.v_y = p_y || 0.0;
this.v_cache = [];
};
//============================================================================
// Private Methods
//============================================================================
// Deletes all cached attribute values.
function m_clear_cache()
{ // Clear the cache, if it has not already been cleared.
if (this.v_cache.length)
this.v_cache = [];
};
//============================================================================
// Public Methods
//============================================================================
var l_prototype = WKcVector2D.prototype =
{ constructor: WKcVector2D,
//--------------------------------------------------------------------------
// Cloning
//--------------------------------------------------------------------------
/**
* Clones <code>this</code> vector and returns a modifyable vector.
*
* <p>The return types of <code>WKtVector2D.clone</code> and
* <code>WKcVector2D.clone</code> differ: The first method returns
* a <code>WKtVector2D<code> object, the second method returns a
* <code>WKcVector2D<code> object.
* </p>
*
* @returns {WKcVector2D} A <code>WKtVector2D</code>
* clone of <code>this</code> vector.
*/
clone:
function(){ return new WKcVector2D(this.v_x, this.v_y); },
/**
* Clones <code>this</code> vector, but returns a non-modifyable vector.
*
* <p>The return type of <code>WKtVector2D.clone</code> and
* <code>WKcVector2D.clone</code> are identical: Both methods return
* a <code>WKtVector2D<code> object.
* </p>
*
* @returns {WKtVector2D} A <code>WKtVector2D</code>
* clone of <code>this</code> vector.
*/
cloneN:
function(){ return new WKtVector2D(this.v_x, this.v_y); },
/**
* Clones <code>this</code> vector and returns a modifyable Vector.
*
* <p>The return type of <code>WKtVector2D.clone</code> and
* <code>WKcVector2D.clone</code> are identical: Both methods return
* a <code>WKcVector2D<code> object.
* </p>
*
* @returns {WKcVector2D} A <code>WKcVector2D</code>
* clone of <code>this</code> vector.
*/
cloneM:
function(){ return new WKcVector2D(this.v_x, this.v_y); },
};
f_merge_into(l_prototype, WKtVector2D.prototype);
f_merge_into
(l_prototype,
{ //--------------------------------------------------------------------------
// Attributes
//--------------------------------------------------------------------------
/** The x-coordinate ({Number}) of <code>this</code> vector. */
set x(p_x) { if (this.v_x != p_x)
{ this.v_x = p_x;
m_clear_cache.call(this);
}
},
/** The y-coordinate ({Number}) of <code>this</code> vector. */
set y(p_y) { if (this.v_y != p_y)
{ this.v_y = p_y;
m_clear_cache.call(this);
}
},
/** The length r ({Number}) of <code>this</code> vector. */
set r(p_r) { if (this.r != p_r)
{ var l_φ = this.φ;
this.v_x = p_r*Math.cos(l_φ); // this.φ has changed now!!
this.v_y = p_r*Math.sin(l_φ); // So here we must use l_φ!
m_clear_cache.call(this);
};
},
set length(p_length) { this.r = p_length; },
/** The angle φ ({Number}) of <code>this</code> vector */
set φ(p_φ) { if (this.φ != p_φ)
{ var l_r = this.r;
this.v_x = l_r*Math.cos(p_φ); // this.r has changed now!!
this.v_y = l_r*Math.sin(p_φ); // So here we must use l_r!
m_clear_cache.call(this);
};
},
/**
* Sets both cartesian coordinate attributes
* <code>x</code> and <code>y</code>
* of <code>this</code> vector simultaneously.
*
* @param {Number} p_x The x-coordinate of <code>this</code> vector.
* @param {Number} p_y The y-coordinate of <code>this</code> vector.
*/
setXY:
function(p_x, p_y)
{ if (this.v_x != p_x && this.v_y != p_y)
{ this.v_x = p_x;
this.v_y = p_y;
m_clear_cache.call(this);
};
return this;
},
/**
* Sets both polar coordinate attributes
* <code>r</code> and <code>φ</code>
* of <code>this</code> vector simultaneously.
*
* @param {Number} p_r The length of <code>this</code> vector.
* @param {Number} p_φ The angle (w.r.t the x-axis)
* of <code>this</code> vector.
*/
setRΘ:
function(p_r, p_φ)
{ if (this.r != p_r && this.v_φ != p_φ)
{ this.v_x = p_r*Math.cos(p_φ);
this.v_y = p_r*Math.sin(p_φ);
m_clear_cache.call(this);
};
return this;
},
//--------------------------------------------------------------------------
// Update Methods
//--------------------------------------------------------------------------
/**
* Replaces the coordinates of <code>this</code> vector
* by the coordinates of another vector <code>p_v</code>.
*
* @param {WKcVector2D} p_v The other vector.
*
* @returns {WKcVector2D} <code>this</code>
*/
replace:
function(p_v)
{ this.v_x = p_v.x;
this.v_y = p_v.y;
m_clear_cache.call(this);
return this;
},
/**
* Modifies <code>this</code> vector by adding another vector
* <code>p_v</code> multiplied by a scalar factor to it.
*
* @param {WKcVector2D} p_v The vector to be added.
* @param {Number} [p_s=1] The scalar factor.
*
* @returns {WKcVector2D} <code>this</code>
*/
addReplace:
function(p_v, p_s)
{ if (p_s != null)
{ this.v_x += p_s*p_v.x; this.v_y += p_s*p_v.y; }
else
{ this.v_x += p_v.x; this.v_y += p_v.y; };
m_clear_cache.call(this);
return this;
},
/**
* Modifies <code>this</code> vector by subtracting another vector
* <code>p_v</code> multiplied by a scalar factor from it.
*
* @param {WKcVector2D} p_v The vector to be added.
* @param {Number} [p_s=1] The scalar factor.
*
* @returns {WKcVector2D} <code>this</code>
*/
subReplace:
function(p_v, p_s)
{ if (p_s != null)
{ this.v_x -= p_s*p_v.x; this.v_y -= p_s*p_v.y; }
else
{ this.v_x -= p_v.x; this.v_y -= p_v.y; };
m_clear_cache.call(this);
return this;
},
/**
* Modifies <code>this</code> vector by multiplying it with a scalar value.
*
* @param {Number} p_s The scalar value.
*
* @returns {WKcVector2D} <code>this</code>
*/
scaleReplace:
function(p_s)
{ this.v_x *= p_s;
this.v_y *= p_s;
m_clear_cache.call(this);
return this;
},
/**
* Modifies <code>this</code> vector by normalizing it.
*
* @returns {WKcVector2D} <code>this</code>
*/
unitReplace:
function()
{ var l_r = this.r;
if (l_r == 0)
return this;
else
{ this.v_cache.m_get_is_unit = true;
return this.scaleReplace(1/l_r);
}
},
/**
* Replaces <code>this</code> vector by a vector that is rotated
* 90 degrees counterclockwise. The length is preserved.
*
* @returns {WKcVector2D} <code>this</code>
*/
normalReplace:
function()
{ var l_x = this.x;
this.v_x = -this.y;
this.v_y = l_x;
m_clear_cache.call(this);
return this;
},
/**
* Replaces <code>this</code> vector by a projection of
* <code>this</code> vector onto another vector <code>p_v</code>.
*
* @param {WKcVector2D} p_v The vector onto which <code>this</code> vector
* is projected.
* @precondition <code>!p_v.isZero()</code>
*
* @returns {WKcVector2D} <code>this</code>, which now is
* a multiple of <code>p_v</code>.
*/
projReplace:
function(p_v)
{ if (p_v.isUnit)
this.replace(p_v.scale(this.dot(p_v)));
else
this.replace(p_v.scale(this.dot(p_v)/p_v.rSqr));
},
/**
* Replaces <code>this</code> vector by the reflection vector
* that arises from <code>this</code> vector being reflected by a
* "mirror" vector <code>v_m</code>.
* The length of the reflection vector is the same as the length of
* <code>this</code> vector multiplied by a scalar factor.
*
* @param {WKcVector2D} p_m The mirror vector.
* @param {Number} [p_s=1] The scalar factor.
*
* @precondition <code>!p_m.isZero()</code>
*
* @returns {WKcVector2D} <code>this</code>
*/
reflectReplace:
function(p_m, p_s)
{ p_s = p_s || 1;
var l_n = p_m.normal();
if (l_n.isUnit)
this.subReplace(l_n.scale(2*p_s*this.dot(l_n)));
else
this.subReplace(l_n.scale(2*p_s*this.dot(l_n)/l_n.rSqr));
},
}
);
//============================================================================
// End of Member Definitions
//============================================================================
// Export the class definition.
$wk$.math.geo2d.WKcVector2D = WKcVector2D;
})($wk$.pkg);
////////////////////////////////////////////////////////////////////////////////
// End of Class Definition
////////////////////////////////////////////////////////////////////////////////