graphics/thing.js

  1. 'use strict';
  2. /**
  3. * @constructor
  4. */
  5. function Thing() {
  6. this.x = 0;
  7. this.y = 0;
  8. this.color = '#000000';
  9. this.stroke = '#000000';
  10. this.type = 'Thing';
  11. this.lineWidth = 1;
  12. this.filled = true;
  13. this.hasBorder = false;
  14. this.rotation = 0;
  15. }
  16. Thing.DEGREES = 0;
  17. Thing.RADIANS = 1;
  18. /**
  19. * Sets a Thing object to filled.
  20. * Throws an error if an argument is not passed.
  21. *
  22. * @param {bool} filled - A boolean of whether or not Thing is filled.
  23. */
  24. Thing.prototype.setFilled = function(filled) {
  25. if (arguments.length !== 1) {
  26. throw new Error('You should pass exactly 1 argument to ' +
  27. '<span class="code">setFilled</span>');
  28. }
  29. if (typeof filled !== 'boolean') {
  30. throw new Error('Invalid value passed to <span class="code">' +
  31. 'setFilled</span>. Make sure you are passing a ' +
  32. 'boolean value.');
  33. }
  34. this.filled = filled;
  35. };
  36. /**
  37. * Returns if a Thing is filled.
  38. *
  39. * @returns {boolean} True if the Thing is filled.
  40. */
  41. Thing.prototype.isFilled = function() {
  42. return this.filled;
  43. };
  44. /**
  45. * Sets a Thing object to filled.
  46. * Throws an error if an argument is not passed.
  47. *
  48. * @param {bool} hasBorder - A boolean of whether or not Thing has a border.
  49. */
  50. Thing.prototype.setBorder = function(hasBorder) {
  51. if (arguments.length !== 1) {
  52. throw new Error('You should pass exactly 1 argument to <span class=' +
  53. '"code">setBorder</span>');
  54. }
  55. if (typeof hasBorder !== 'boolean') {
  56. throw new Error('Invalid value passed to <span class="code">' +
  57. 'setBorder</span>. Make sure you are passing a ' +
  58. 'boolean value.');
  59. }
  60. this.hasBorder = hasBorder;
  61. };
  62. /**
  63. * Returns if a Thing has a border.
  64. *
  65. * @returns {boolean} True if the Thing has a border.
  66. */
  67. Thing.prototype.hasBorder = function() {
  68. return this.hasBorder;
  69. };
  70. /**
  71. * Sets a Thing object's type.
  72. * Questionable of whether or not this method is used.
  73. *
  74. * @param {type} type - A type to set the Thing to.
  75. */
  76. Thing.prototype.setType = function(type) {
  77. if (arguments.length !== 1) {
  78. throw new Error('You should pass exactly 1 argument to <span ' +
  79. 'class="code">setType</span>');
  80. }
  81. this.type = type;
  82. };
  83. /**
  84. * Returns the type of a Thing.
  85. *
  86. * @returns {type} The type of the Thing.
  87. */
  88. Thing.prototype.getType = function() {
  89. return this.type;
  90. };
  91. /**
  92. * Sets the position of a Thing.
  93. * Throws an error if there are fewer than 2 params or if
  94. * they are not numbers.
  95. *
  96. * @param {number} x - The destination x coordinate of this Thing.
  97. * @param {number} y - The destination y coordinate of this Thing.
  98. */
  99. Thing.prototype.setPosition = function(x, y) {
  100. if (arguments.length !== 2) {
  101. throw new Error('You should pass exactly 2 arguments to <span ' +
  102. 'class="code">setPosition(x, y)</span>');
  103. }
  104. if (typeof x !== 'number' || !isFinite(x)) {
  105. throw new TypeError('Invalid value for x-coordinate. ' +
  106. 'Make sure you are passing finite numbers to <span ' +
  107. 'class="code">setPosition(x, y)</span>. Did you ' +
  108. 'forget the parentheses in <span class="code">getWidth()</span> ' +
  109. 'or <span class="code">getHeight()</span>? Or did you perform a ' +
  110. 'calculation on a variable that is not a number?');
  111. }
  112. if (typeof y !== 'number' || !isFinite(y)) {
  113. throw new TypeError('Invalid value for y-coordinate. ' +
  114. 'Make sure you are passing finite numbers to <span ' +
  115. 'class="code">setPosition(x, y)</span>. Did you ' +
  116. 'forget the parentheses in <span class="code">getWidth()</span> ' +
  117. 'or <span class="code">getHeight()</span>? Or did you perform a ' +
  118. 'calculation on a variable that is not a number?');
  119. }
  120. this.x = x;
  121. this.y = y;
  122. };
  123. /**
  124. * Sets the rotation of a Thing in degrees.
  125. * Throws an error if there are fewer than 1 params or if they
  126. * are not numbers.
  127. *
  128. * @param {number} degrees - The degrees to rotate degrees.
  129. * @param {number} angleUnit - Whether it is degrees or radians. Defaults to
  130. * degrees.
  131. */
  132. Thing.prototype.setRotation = function(degrees, angleUnit) {
  133. if (arguments.length < 1 || arguments.length > 2) {
  134. throw new Error('You should pass 1 or 2 arguments to <span ' +
  135. 'class="code">setRotation(degrees, angleUnit)</span>');
  136. }
  137. if (typeof degrees !== 'number' || !isFinite(degrees)) {
  138. throw new TypeError('Invalid value for degrees. ' +
  139. 'Make sure you are passing finite numbers to <span ' +
  140. 'class="code">setRotation(degrees, angleUnit)</span>. Did you ' +
  141. 'perform a calculation on a variable that is not a number?');
  142. }
  143. if (!angleUnit) {
  144. angleUnit = Thing.DEGREES;
  145. }
  146. if (typeof angleUnit !== 'number' || !isFinite(angleUnit)) {
  147. throw new TypeError('Invalid value for <span class="code">angleUnit' +
  148. '</span>. Make sure you are passing finite numbers to <span ' +
  149. 'class="code">setRotation(degrees, angleUnit)</span>');
  150. }
  151. if (angleUnit == Thing.DEGREES) {
  152. this.rotation = degrees * Math.PI / 180;
  153. } else {
  154. this.rotation = degrees;
  155. }
  156. };
  157. /**
  158. * Rotates a Thing an additional amount of degrees.
  159. *
  160. * @param {number} degrees - The degrees to rotate degrees.
  161. * @param {number} angleUnit - Whether it is degrees or radians. Defaults to
  162. * degrees.
  163. */
  164. Thing.prototype.rotate = function(degrees, angleUnit) {
  165. if (arguments.length < 1 || arguments.length > 2) {
  166. throw new Error('You should pass exactly 1 argument to <span ' +
  167. 'class="code">rotate(degrees, angleUnit)</span>');
  168. }
  169. if (typeof degrees !== 'number' || !isFinite(degrees)) {
  170. throw new TypeError('Invalid value for degrees. ' +
  171. 'Make sure you are passing finite numbers to <span ' +
  172. 'class="code">rotate(degrees, angleUnit)</span>. Did you perform ' +
  173. 'a calculation on a variable that is not a number?');
  174. }
  175. if (!angleUnit) {
  176. angleUnit = Thing.DEGREES;
  177. }
  178. if (typeof angleUnit !== 'number' || !isFinite(angleUnit)) {
  179. throw new TypeError('Invalid value for <span class="code">angleUnit' +
  180. '</span>. Make sure you are passing finite numbers to <span ' +
  181. 'class="code">rotate(degrees, angleUnit)</span>');
  182. }
  183. if (angleUnit == Thing.DEGREES) {
  184. this.rotation += degrees * Math.PI / 180;
  185. } else {
  186. this.rotation += degrees;
  187. }
  188. };
  189. /**
  190. * Sets the color of a Thing.
  191. * Throws an error if there are fewer than 1 params or if
  192. * the param is undefined.
  193. *
  194. * @param {Color} color - The resulting color of Thing.
  195. */
  196. Thing.prototype.setColor = function(color) {
  197. if (arguments.length !== 1) {
  198. throw new Error('You should pass exactly 1 argument to <span ' +
  199. 'class="code">setColor</span>');
  200. }
  201. if (color === undefined) {
  202. throw new TypeError('Invalid color');
  203. }
  204. this.color = color;
  205. };
  206. /**
  207. * Gets the color of a Thing.
  208. *
  209. * @returns {Color} The destination y coordinate of this Thing.
  210. */
  211. Thing.prototype.getColor = function() {
  212. return this.color;
  213. };
  214. /**
  215. * Sets the border color of a Thing.
  216. * Throws an error if there are fewer than 1 params or if
  217. * the param is undefined.
  218. *
  219. * @param {Color} color - The resulting color of the Thing's border.
  220. */
  221. Thing.prototype.setBorderColor = function(color) {
  222. if (arguments.length !== 1) {
  223. throw new Error('You should pass exactly 1 argument to <span ' +
  224. 'class="code">setBorderColor</span>');
  225. }
  226. if (color === undefined) {
  227. throw new TypeError('Invalid color.');
  228. }
  229. this.stroke = color;
  230. this.hasBorder = true;
  231. };
  232. /**
  233. * Gets the border color of a Thing.
  234. *
  235. * @returns {Color} The color of the Thing's border.
  236. */
  237. Thing.prototype.getBorderColor = function() {
  238. return this.stroke;
  239. };
  240. /**
  241. * Sets the width of a Thing's border.
  242. * Throws an error if there is not 1 argument.
  243. *
  244. * @param {number} width - The resulting width of the Thing's border.
  245. */
  246. Thing.prototype.setBorderWidth = function(width) {
  247. if (arguments.length !== 1) {
  248. throw new Error('You should pass exactly 1 argument to <span ' +
  249. 'class="code">setBorderWidth</span>');
  250. }
  251. if (typeof width !== 'number' || !isFinite(width)) {
  252. throw new Error('Invalid value for border width. Make sure you are ' +
  253. 'passing a finite number to <span class="code">' +
  254. 'setBorderWidth</span>');
  255. }
  256. this.lineWidth = width;
  257. this.hasBorder = true;
  258. };
  259. /**
  260. * Gets the width of the Thing's border.
  261. *
  262. * @returns {number} The width of the Thing's border.
  263. */
  264. Thing.prototype.getBorderWidth = function() {
  265. return this.lineWidth;
  266. };
  267. /**
  268. * Changes the possition of a thing by a specified x and y amount.
  269. *
  270. * @param {number} dx - The resulting change in the Thing's x position.
  271. * @param {number} dy - The resulting change in the Thing's y position.
  272. */
  273. Thing.prototype.move = function(dx, dy) {
  274. if (arguments.length !== 2) {
  275. throw new Error('You should pass exactly 2 arguments to <span ' +
  276. 'class="code">move</span>');
  277. }
  278. if (typeof dx !== 'number' || !isFinite(dx)) {
  279. throw new TypeError('Invalid number passed for <span class="code">' +
  280. 'dx</span>. Make sure you are passing finite numbers to <span ' +
  281. 'class="code">move(dx, dy)</span>');
  282. }
  283. if (typeof dy !== 'number' || !isFinite(dy)) {
  284. throw new TypeError('Invalid number passed for <span class="code">' +
  285. 'dy</span>. Make sure you are passing finite numbers to <span ' +
  286. 'class="code">move(dx, dy)</span>');
  287. }
  288. this.x += dx;
  289. this.y += dy;
  290. };
  291. /**
  292. * Gets the x position of the Thing.
  293. *
  294. * @returns {number} The x position of the Thing.
  295. */
  296. Thing.prototype.getX = function() {
  297. return this.x;
  298. };
  299. /**
  300. * Gets the y position of the Thing.
  301. *
  302. * @returns {number} The y position of the Thing.
  303. */
  304. Thing.prototype.getY = function() {
  305. return this.y;
  306. };
  307. /**
  308. * This function is overridden in subclasses of Thing.
  309. */
  310. Thing.prototype.draw = function() {
  311. };
  312. /**
  313. * Check if a given point is within the Thing.
  314. * This function only works in subclasses of Thing.
  315. *
  316. * @param {number} x - The x coordinate of the point being checked.
  317. * @param {number} y - The y coordinate of the point being checked.
  318. * @returns {boolean} Whether the point x, y is within the Thing.
  319. */
  320. Thing.prototype.containsPoint = function(x, y) {
  321. return false;
  322. };
  323. module.exports = Thing;