graphics/oval.js

  1. 'use strict';
  2. var Thing = require('./thing.js');
  3. /**
  4. * Note: this is not used as a subclass for Circle since drawing ovals
  5. * is much more complex than drawing circles, and there is no point in
  6. * complicating the drawing just for some code reuse.
  7. */
  8. /**
  9. * @class Oval
  10. * @augments Thing
  11. * @param {number} width - Desired width of the Oval
  12. * @param {number} height - Desired height of the Oval
  13. */
  14. function Oval(width, height) {
  15. if (arguments.length !== 2) {
  16. throw new Error('You should pass exactly 2 arguments to <span ' +
  17. 'class="code">new Oval(width, height)</span>');
  18. }
  19. if (typeof width !== 'number' || !isFinite(width)) {
  20. throw new TypeError('Invalid value for <span class="code">width' +
  21. '</span>. Make sure you are passing finite numbers to <span ' +
  22. 'class="code">new Oval(width, height)</span>. Did you ' +
  23. 'forget the parentheses in <span class="code">getWidth()</span> ' +
  24. 'or <span class="code">getHeight()</span>? Or did you perform a ' +
  25. 'calculation on a variable that is not a number?');
  26. }
  27. if (typeof height !== 'number' || !isFinite(height)) {
  28. throw new TypeError('Invalid value for <span class="code">height' +
  29. '</span>. Make sure you are passing finite numbers to <span ' +
  30. 'class="code">new Oval(width, height)</span>. Did you ' +
  31. 'forget the parentheses in <span class="code">getWidth()</span> ' +
  32. 'or <span class="code">getHeight()</span>? Or did you perform a ' +
  33. 'calculation on a variable that is not a number?');
  34. }
  35. Thing.call(this);
  36. this.width = Math.max(0, width);
  37. this.height = Math.max(0, height);
  38. this.type = 'Oval';
  39. }
  40. Oval.prototype = new Thing();
  41. Oval.prototype.constructor = Oval;
  42. /**
  43. * Draws an ellipse centered at this.x and this.y.
  44. * adapted from http://stackoverflow.com/questions/2172798/
  45. * how-to-draw-an-oval-in-html5-canvas
  46. *
  47. * @param {CodeHSGraphics} __graphics__ - Instance of the __graphics__ module.
  48. */
  49. Oval.prototype.draw = function(__graphics__) {
  50. var context = __graphics__.getContext();
  51. // http://stackoverflow.com/questions/17125632/html5-canvas-rotate-object-without-moving-coordinates
  52. context.save();
  53. context.translate(this.x, this.y);
  54. context.rotate(this.rotation);
  55. var w = this.width;
  56. var h = this.height;
  57. var x = -w / 2;
  58. var y = -h / 2;
  59. var kappa = 0.5522848;
  60. var ox = (w / 2) * kappa; // control point offset horizontal
  61. var oy = (h / 2) * kappa; // control point offset vertical
  62. var xe = x + w; // x-end
  63. var ye = y + h; // y-end
  64. var xm = x + w / 2; // x-middle
  65. var ym = y + h / 2; // y-middle
  66. context.beginPath();
  67. context.moveTo(x, ym);
  68. context.bezierCurveTo(x, ym - oy, xm - ox, y, xm, y);
  69. context.bezierCurveTo(xm + ox, y, xe, ym - oy, xe, ym);
  70. context.bezierCurveTo(xe, ym + oy, xm + ox, ye, xm, ye);
  71. context.bezierCurveTo(xm - ox, ye, x, ym + oy, x, ym);
  72. context.fillStyle = this.color.toString();
  73. context.fill();
  74. if (this.hasBorder) {
  75. context.strokeStyle = this.stroke.toString();
  76. context.lineWidth = this.lineWidth;
  77. context.stroke();
  78. }
  79. context.closePath();
  80. context.restore();
  81. };
  82. /**
  83. * Gets the height of the oval.
  84. *
  85. * @returns {number} Height of the oval.
  86. */
  87. Oval.prototype.getHeight = function() {
  88. return this.height;
  89. };
  90. /**
  91. * Gets the width of the oval.
  92. *
  93. * @returns {number} Width of the oval.
  94. */
  95. Oval.prototype.getWidth = function() {
  96. return this.width;
  97. };
  98. /**
  99. * Sets the width of the oval.
  100. *
  101. * @param {number} width - Desired width of the resulting oval.
  102. */
  103. Oval.prototype.setWidth = function(width) {
  104. if (arguments.length !== 1) {
  105. throw new Error('You should pass exactly 1 argument to <span ' +
  106. 'class="code">setWidth(width)</span>');
  107. }
  108. if (typeof width !== 'number' || !isFinite(width)) {
  109. throw new TypeError('You must pass a finite number to <span class=' +
  110. '"code">setWidth(width)</span>. Did you forget the ' +
  111. 'parentheses in <span class="code">getWidth()</span> or <span ' +
  112. 'class="code">getHeight()</span>? Or did you perform a ' +
  113. 'calculation on a variable that is not a number?');
  114. }
  115. this.width = Math.max(0, width);
  116. };
  117. /**
  118. * Sets the height of the oval.
  119. *
  120. * @param {number} height - Desired height of the resulting oval.
  121. */
  122. Oval.prototype.setHeight = function(height) {
  123. if (arguments.length !== 1) {
  124. throw new Error('You should pass exactly 1 argument to <span ' +
  125. 'class="code">setHeight(height)</span>');
  126. }
  127. if (typeof height !== 'number' || !isFinite(height)) {
  128. throw new TypeError('You must pass a finite number to <span class=' +
  129. '"code">setHeight(height)</span>. Did you forget the ' +
  130. 'parentheses in <span class="code">getWidth()</span> or <span ' +
  131. 'class="code">getHeight()</span>? Or did you perform a ' +
  132. 'calculation on a variable that is not a number?');
  133. }
  134. this.height = Math.max(0, height);
  135. };
  136. /**
  137. * Checks if the passed point is contained in the oval.
  138. * Uses the equation for an oval.
  139. *
  140. * @param {number} x - The x coordinate of the point being tested.
  141. * @param {number} y - The y coordinate of the point being tested.
  142. * @returns {boolean} Whether the passed point is contained in the circle.
  143. */
  144. Oval.prototype.containsPoint = function(x, y) {
  145. var xRadiusSquared = Math.pow(this.width / 2, 2);
  146. var yRadiusSquared = Math.pow(this.height / 2, 2);
  147. var xDifferenceSquared = Math.pow(x - this.x, 2);
  148. var yDifferenceSquared = Math.pow(y - this.y, 2);
  149. var result = xDifferenceSquared / xRadiusSquared +
  150. yDifferenceSquared / yRadiusSquared;
  151. return result <= 1;
  152. };
  153. module.exports = Oval;