graphics/polygon.js

  1. 'use strict';
  2. var Thing = require('./thing.js');
  3. /**
  4. * @class Polygon
  5. * @augments Thing
  6. */
  7. function Polygon() {
  8. if (arguments.length !== 0) {
  9. throw new Error(
  10. 'You should pass exactly 0 arguments to <span class="code">new Polygon()</span>'
  11. );
  12. }
  13. Thing.call(this);
  14. this.points = [];
  15. this.width = 0; // max x-distance of points in the polygon
  16. this.height = 0; // max y-distance of points in the polygon
  17. this.type = 'Polygon';
  18. }
  19. Polygon.prototype = new Thing();
  20. Polygon.prototype.constructor = Polygon;
  21. /**
  22. * Draws the polygon in the canvas.
  23. *
  24. * @param {CodeHSGraphics} __graphics__ - Instance of the __graphics__ module.
  25. */
  26. Polygon.prototype.draw = function(__graphics__) {
  27. if (this.points.length === 0) {
  28. return;
  29. }
  30. var context = __graphics__.getContext();
  31. context.fillStyle = this.color.toString();
  32. context.beginPath();
  33. var first = this.points[0];
  34. context.moveTo(first.x, first.y);
  35. for (var i = 1; i < this.points.length; i++) {
  36. var cur = this.points[i];
  37. context.lineTo(cur.x, cur.y);
  38. }
  39. context.closePath();
  40. context.fill();
  41. };
  42. /**
  43. * Checks if the passed point is contained in the polygon.
  44. *
  45. * @param {number} x - The x coordinate of the point being tested.
  46. * @param {number} y - The y coordinate of the point being tested.
  47. * @returns {boolean} Whether the passed point is contained in the polygon.
  48. */
  49. Polygon.prototype.containsPoint = function(x, y) {
  50. // https://www.eecs.umich.edu/courses/eecs380/HANDOUTS/PROJ2/InsidePoly.html
  51. // solution 3 from above
  52. var edges = [];
  53. var previousOrientation = -1;
  54. var x1, x2, y1, y2;
  55. for (var i = 0; i < this.points.length; i++) {
  56. x1 = this.points[i].x;
  57. y1 = this.points[i].y;
  58. x2 = this.points[(i + 1) % this.points.length].x;
  59. y2 = this.points[(i + 1) % this.points.length].y;
  60. var orientation = (y - y1) * (x2 - x1) - (x - x1) * (y2 - y1) <= 0;
  61. if (previousOrientation < 0) {
  62. previousOrientation = orientation;
  63. } else {
  64. if (previousOrientation !== orientation) {
  65. return false;
  66. }
  67. }
  68. }
  69. return true;
  70. };
  71. /**
  72. * Gets the width of the rectangle.
  73. *
  74. * @returns {number} Width of the rectangle.
  75. */
  76. Polygon.prototype.getWidth = function() {
  77. return this.width;
  78. };
  79. /**
  80. * Gets the height of the rectangle.
  81. *
  82. * @returns {number} Height of the rectangle.
  83. */
  84. Polygon.prototype.getHeight = function() {
  85. return this.height;
  86. };
  87. /**
  88. * Adds a vertex to the polygon.
  89. *
  90. * @param {number} x - The x coordinate of the desired new vertex.
  91. * @param {number} y - The y coordinate of the desired new vertex.
  92. */
  93. Polygon.prototype.addPoint = function(x, y) {
  94. if (arguments.length !== 2) {
  95. throw new Error(
  96. 'You should pass exactly 2 arguments to <span class="code">addPoint(x, y)</span>'
  97. );
  98. }
  99. if (typeof x !== 'number' || !isFinite(x)) {
  100. throw new TypeError(
  101. 'Invalid value for x-coordinate. ' +
  102. 'Make sure you are passing finite numbers to ' +
  103. '<span class="code">addPoint(x, y)</span>.'
  104. );
  105. }
  106. if (typeof y !== 'number' || !isFinite(y)) {
  107. throw new TypeError(
  108. 'Invalid value for y-coordinate. ' +
  109. 'Make sure you are passing finite numbers to ' +
  110. '<span class="code">addPoint(x, y)</span>.'
  111. );
  112. }
  113. for (var i = 0; i < this.points.length; i++) {
  114. if (Math.abs(x - this.points[i].x) > this.width) {
  115. this.width = Math.abs(x - this.points[i].x);
  116. }
  117. if (Math.abs(y - this.points[i].y) > this.height) {
  118. this.height = Math.abs(y - this.points[i].y);
  119. }
  120. }
  121. this.points.push({x: x, y: y});
  122. };
  123. /**
  124. * Moves the entire polygon.
  125. *
  126. * @param {number} dx - The change in x coordinate of all starting and ending points.
  127. * @param {number} dy - The change in y coordinate of all starting and ending points.
  128. */
  129. Polygon.prototype.move = function(dx, dy) {
  130. if (arguments.length !== 2) {
  131. throw new Error('You should pass exactly 2 arguments to <span class="code">move</span>');
  132. }
  133. if (typeof dx !== 'number' || !isFinite(dx)) {
  134. throw new TypeError(
  135. 'Invalid number passed for <span class="code">' +
  136. 'dx</span>. Make sure you are passing finite numbers to <span ' +
  137. 'class="code">move(dx, dy)</span>'
  138. );
  139. }
  140. if (typeof dy !== 'number' || !isFinite(dy)) {
  141. throw new TypeError(
  142. 'Invalid number passed for <span class="code">' +
  143. 'dy</span>. Make sure you are passing finite numbers to <span ' +
  144. 'class="code">move(dx, dy)</span>'
  145. );
  146. }
  147. for (var i = 0; i < this.points.length; i++) {
  148. this.points[i].x += dx;
  149. this.points[i].y += dy;
  150. }
  151. };
  152. module.exports = Polygon;