biltong-0.2.js 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195
  1. /**
  2. * Biltong v0.2
  3. *
  4. * Various geometry functions written as part of jsPlumb and perhaps useful for others.
  5. *
  6. * Copyright (c) 2014 Simon Porritt
  7. *
  8. * Permission is hereby granted, free of charge, to any person
  9. * obtaining a copy of this software and associated documentation
  10. * files (the "Software"), to deal in the Software without
  11. * restriction, including without limitation the rights to use,
  12. * copy, modify, merge, publish, distribute, sublicense, and/or sell
  13. * copies of the Software, and to permit persons to whom the
  14. * Software is furnished to do so, subject to the following
  15. * conditions:
  16. *
  17. * The above copyright notice and this permission notice shall be
  18. * included in all copies or substantial portions of the Software.
  19. *
  20. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  21. * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
  22. * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  23. * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
  24. * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
  25. * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  26. * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
  27. * OTHER DEALINGS IN THE SOFTWARE.
  28. */
  29. ;(function() {
  30. "use strict";
  31. var Biltong = this.Biltong = {};
  32. var _isa = function(a) { return Object.prototype.toString.call(a) === "[object Array]"; },
  33. _pointHelper = function(p1, p2, fn) {
  34. p1 = _isa(p1) ? p1 : [p1.x, p1.y];
  35. p2 = _isa(p2) ? p2 : [p2.x, p2.y];
  36. return fn(p1, p2);
  37. },
  38. /**
  39. * @name Biltong.gradient
  40. * @function
  41. * @desc Calculates the gradient of a line between the two points.
  42. * @param {Point} p1 First point, either as a 2 entry array or object with `left` and `top` properties.
  43. * @param {Point} p2 Second point, either as a 2 entry array or object with `left` and `top` properties.
  44. * @return {Float} The gradient of a line between the two points.
  45. */
  46. _gradient = Biltong.gradient = function(p1, p2) {
  47. return _pointHelper(p1, p2, function(_p1, _p2) {
  48. if (_p2[0] == _p1[0])
  49. return _p2[1] > _p1[1] ? Infinity : -Infinity;
  50. else if (_p2[1] == _p1[1])
  51. return _p2[0] > _p1[0] ? 0 : -0;
  52. else
  53. return (_p2[1] - _p1[1]) / (_p2[0] - _p1[0]);
  54. });
  55. },
  56. /**
  57. * @name Biltong.normal
  58. * @function
  59. * @desc Calculates the gradient of a normal to a line between the two points.
  60. * @param {Point} p1 First point, either as a 2 entry array or object with `left` and `top` properties.
  61. * @param {Point} p2 Second point, either as a 2 entry array or object with `left` and `top` properties.
  62. * @return {Float} The gradient of a normal to a line between the two points.
  63. */
  64. _normal = Biltong.normal = function(p1, p2) {
  65. return -1 / _gradient(p1, p2);
  66. },
  67. /**
  68. * @name Biltong.lineLength
  69. * @function
  70. * @desc Calculates the length of a line between the two points.
  71. * @param {Point} p1 First point, either as a 2 entry array or object with `left` and `top` properties.
  72. * @param {Point} p2 Second point, either as a 2 entry array or object with `left` and `top` properties.
  73. * @return {Float} The length of a line between the two points.
  74. */
  75. _lineLength = Biltong.lineLength = function(p1, p2) {
  76. return _pointHelper(p1, p2, function(_p1, _p2) {
  77. return Math.sqrt(Math.pow(_p2[1] - _p1[1], 2) + Math.pow(_p2[0] - _p1[0], 2));
  78. });
  79. },
  80. /**
  81. * @name Biltong.quadrant
  82. * @function
  83. * @desc Calculates the quadrant in which the angle between the two points lies.
  84. * @param {Point} p1 First point, either as a 2 entry array or object with `left` and `top` properties.
  85. * @param {Point} p2 Second point, either as a 2 entry array or object with `left` and `top` properties.
  86. * @return {Integer} The quadrant - 1 for upper right, 2 for lower right, 3 for lower left, 4 for upper left.
  87. */
  88. _quadrant = Biltong.quadrant = function(p1, p2) {
  89. return _pointHelper(p1, p2, function(_p1, _p2) {
  90. if (_p2[0] > _p1[0]) {
  91. return (_p2[1] > _p1[1]) ? 2 : 1;
  92. }
  93. else if (_p2[0] == _p1[0]) {
  94. return _p2[1] > _p1[1] ? 2 : 1;
  95. }
  96. else {
  97. return (_p2[1] > _p1[1]) ? 3 : 4;
  98. }
  99. });
  100. },
  101. /**
  102. * @name Biltong.theta
  103. * @function
  104. * @desc Calculates the angle between the two points.
  105. * @param {Point} p1 First point, either as a 2 entry array or object with `left` and `top` properties.
  106. * @param {Point} p2 Second point, either as a 2 entry array or object with `left` and `top` properties.
  107. * @return {Float} The angle between the two points.
  108. */
  109. _theta = Biltong.theta = function(p1, p2) {
  110. return _pointHelper(p1, p2, function(_p1, _p2) {
  111. var m = _gradient(_p1, _p2),
  112. t = Math.atan(m),
  113. s = _quadrant(_p1, _p2);
  114. if ((s == 4 || s== 3)) t += Math.PI;
  115. if (t < 0) t += (2 * Math.PI);
  116. return t;
  117. });
  118. },
  119. /**
  120. * @name Biltong.intersects
  121. * @function
  122. * @desc Calculates whether or not the two rectangles intersect.
  123. * @param {Rectangle} r1 First rectangle, as a js object in the form `{x:.., y:.., w:.., h:..}`
  124. * @param {Rectangle} r2 Second rectangle, as a js object in the form `{x:.., y:.., w:.., h:..}`
  125. * @return {Boolean} True if the rectangles intersect, false otherwise.
  126. */
  127. _intersects = Biltong.intersects = function(r1, r2) {
  128. var x1 = r1.x, x2 = r1.x + r1.w, y1 = r1.y, y2 = r1.y + r1.h,
  129. a1 = r2.x, a2 = r2.x + r2.w, b1 = r2.y, b2 = r2.y + r2.h;
  130. return ( (x1 <= a1 && a1 <= x2) && (y1 <= b1 && b1 <= y2) ) ||
  131. ( (x1 <= a2 && a2 <= x2) && (y1 <= b1 && b1 <= y2) ) ||
  132. ( (x1 <= a1 && a1 <= x2) && (y1 <= b2 && b2 <= y2) ) ||
  133. ( (x1 <= a2 && a1 <= x2) && (y1 <= b2 && b2 <= y2) ) ||
  134. ( (a1 <= x1 && x1 <= a2) && (b1 <= y1 && y1 <= b2) ) ||
  135. ( (a1 <= x2 && x2 <= a2) && (b1 <= y1 && y1 <= b2) ) ||
  136. ( (a1 <= x1 && x1 <= a2) && (b1 <= y2 && y2 <= b2) ) ||
  137. ( (a1 <= x2 && x1 <= a2) && (b1 <= y2 && y2 <= b2) );
  138. },
  139. /**
  140. * @name Biltong.encloses
  141. * @function
  142. * @desc Calculates whether or not r2 is completely enclosed by r1.
  143. * @param {Rectangle} r1 First rectangle, as a js object in the form `{x:.., y:.., w:.., h:..}`
  144. * @param {Rectangle} r2 Second rectangle, as a js object in the form `{x:.., y:.., w:.., h:..}`
  145. * @param {Boolean} [allowSharedEdges=false] If true, the concept of enclosure allows for one or more edges to be shared by the two rectangles.
  146. * @return {Boolean} True if r1 encloses r2, false otherwise.
  147. */
  148. _encloses = Biltong.encloses = function(r1, r2, allowSharedEdges) {
  149. var x1 = r1.x, x2 = r1.x + r1.w, y1 = r1.y, y2 = r1.y + r1.h,
  150. a1 = r2.x, a2 = r2.x + r2.w, b1 = r2.y, b2 = r2.y + r2.h,
  151. c = function(v1, v2, v3, v4) { return allowSharedEdges ? v1 <= v2 && v3>= v4 : v1 < v2 && v3 > v4; };
  152. return c(x1,a1,x2,a2) && c(y1,b1,y2,b2);
  153. },
  154. _segmentMultipliers = [null, [1, -1], [1, 1], [-1, 1], [-1, -1] ],
  155. _inverseSegmentMultipliers = [null, [-1, -1], [-1, 1], [1, 1], [1, -1] ],
  156. /**
  157. * @name Biltong.pointOnLine
  158. * @function
  159. * @desc Calculates a point on the line from `fromPoint` to `toPoint` that is `distance` units along the length of the line.
  160. * @param {Point} p1 First point, either as a 2 entry array or object with `left` and `top` properties.
  161. * @param {Point} p2 Second point, either as a 2 entry array or object with `left` and `top` properties.
  162. * @return {Point} Point on the line, in the form `{ x:..., y:... }`.
  163. */
  164. _pointOnLine = Biltong.pointOnLine = function(fromPoint, toPoint, distance) {
  165. var m = _gradient(fromPoint, toPoint),
  166. s = _quadrant(fromPoint, toPoint),
  167. segmentMultiplier = distance > 0 ? _segmentMultipliers[s] : _inverseSegmentMultipliers[s],
  168. theta = Math.atan(m),
  169. y = Math.abs(distance * Math.sin(theta)) * segmentMultiplier[1],
  170. x = Math.abs(distance * Math.cos(theta)) * segmentMultiplier[0];
  171. return { x:fromPoint.x + x, y:fromPoint.y + y };
  172. },
  173. /**
  174. * @name Biltong.perpendicularLineTo
  175. * @function
  176. * @desc Calculates a line of length `length` that is perpendicular to the line from `fromPoint` to `toPoint` and passes through `toPoint`.
  177. * @param {Point} p1 First point, either as a 2 entry array or object with `left` and `top` properties.
  178. * @param {Point} p2 Second point, either as a 2 entry array or object with `left` and `top` properties.
  179. * @return {Line} Perpendicular line, in the form `[ { x:..., y:... }, { x:..., y:... } ]`.
  180. */
  181. _perpendicularLineTo = Biltong.perpendicularLineTo = function(fromPoint, toPoint, length) {
  182. var m = _gradient(fromPoint, toPoint),
  183. theta2 = Math.atan(-1 / m),
  184. y = length / 2 * Math.sin(theta2),
  185. x = length / 2 * Math.cos(theta2);
  186. return [{x:toPoint.x + x, y:toPoint.y + y}, {x:toPoint.x - x, y:toPoint.y - y}];
  187. };
  188. }).call(this);