quadratic-vis.js 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276
  1. 'use strict';
  2. /* global variables */
  3. var STRETCH_X = 3;
  4. var STRETCH_Y = 0.5;
  5. var X_MIN = -10;
  6. var X_MAX = +10;
  7. var Y_MIN = -10;
  8. var Y_MAX = +10;
  9. var X_OFFSET = -128;
  10. var Y_OFFSET = 256;
  11. var INITIAL_RADIUS = 20;
  12. var POINT_RADIUS = 5;
  13. /*******************************************************************/
  14. /* Graphics */
  15. /*******************************************************************/
  16. /**
  17. * Calculates coordinates from worldspace to screenspace
  18. * @param {Number} x the coordinate you want to transform
  19. * @param {bool} isX true iff x is a x-coordinate, otherwise false
  20. * @return {Number} transformed coordinate
  21. */
  22. function c(x, isX) {
  23. if (isX) {
  24. return STRETCH_X * (x - X_OFFSET);
  25. }
  26. return STRETCH_Y * (-x + Y_OFFSET);
  27. }
  28. /**
  29. * Calculates coordinates from screenspace to worldspace
  30. * @param {Number} x the coordinate you want to transform
  31. * @param {bool} isX true iff x is a x-coordinate, otherwise false
  32. * @return {Number} transformed coordinate
  33. */
  34. function r(x, isX) {
  35. if (isX) {
  36. return (x / STRETCH_X) + X_OFFSET;
  37. }
  38. return (-x / STRETCH_Y) + Y_OFFSET;
  39. }
  40. function setCursorByID(id,cursorStyle) {
  41. var elem;
  42. if (document.getElementById &&
  43. (elem=document.getElementById(id)) ) {
  44. if (elem.style) elem.style.cursor=cursorStyle;
  45. }
  46. }
  47. function drawEllipse(centerX, centerY, width, height) {
  48. context.beginPath() ;
  49. var x = centerX;
  50. var y = centerY;
  51. var rx = width;
  52. var ry = height;
  53. var rotation = 0; // The rotation of the ellipse (in radians)
  54. var start = 0; // The start angle (in radians)
  55. var end = 2 * Math.PI; // The end angle (in radians)
  56. var anticlockwise = false;
  57. context.ellipse(x, y, rx, ry, rotation, start, end, anticlockwise);
  58. context.fillStyle = "rgba(255, 0, 0, 0.5)";
  59. context.fill();
  60. }
  61. function getColor(i, transparency) {
  62. //var t = (i+1)*(360/k);
  63. //var color = 'hsla('+t+', 100%, 50%, '+transparency+')';
  64. var x = i / 256;
  65. if (x > 1) {x = 1.0;}
  66. x = parseInt(x*255);
  67. var color = 'rgba('+x+','+x+','+x+','+transparency+')';
  68. return color;
  69. }
  70. function drawQuadraticFunction(canvas) {
  71. var add = parseInt(document.getElementById("density").value);
  72. context.beginPath();
  73. context.fillStyle = 'red';
  74. context.moveTo(0, c(getValue(r(0))), false);
  75. for (var xS=0; xS < canvas.width; xS+=add) {
  76. var x = r(xS);
  77. var y = getValue(x);
  78. //context.fillRect(c(x), c(y, false), add/2, add/2);
  79. context.lineTo(c(x, true), c(y, false));
  80. }
  81. context.closePath();
  82. context.stroke();
  83. }
  84. /*******************************************************************/
  85. /* Math */
  86. /*******************************************************************/
  87. function euklideanDist(p1, p2) {
  88. return Math.sqrt(
  89. Math.pow(p1["x"]-p2["x"], 2)
  90. + Math.pow(p1["y"]-p2["y"], 2));
  91. }
  92. /**
  93. * Calculates the value of a cubic function at x
  94. * @param {Number} x
  95. * @return {Number} f(x)
  96. */
  97. function getValue(x) {
  98. var a = parseFloat(document.getElementById("a").value);
  99. var b = parseFloat(document.getElementById("b").value);
  100. var c1 = parseFloat(document.getElementById("c").value);
  101. return a*x*x+b*x+c1;
  102. }
  103. /**
  104. * Calculates the drivate f'(x)
  105. * @param {Number} x
  106. * @return {Number} f'(x)
  107. */
  108. function getDValue(x) {
  109. var a = parseFloat(document.getElementById("a").value);
  110. var b = parseFloat(document.getElementById("b").value);
  111. return 2*a*x+b;
  112. }
  113. /**
  114. * Calculates the drivate f''(x)
  115. * @param {Number} x
  116. * @return {Number} f''(x)
  117. */
  118. function getDDValue(x) {
  119. var a = parseFloat(document.getElementById("a").value);
  120. return 2*a;
  121. }
  122. /**
  123. * Calculates (f(x)^2)' = ((a*x*x+b*x+c)^2)' = 2 (b + 2 a x) (c + x (b + a x))
  124. * @param {Number} x
  125. * @return {Number} (f(x)^2)'
  126. */
  127. function gedSquaredValueD(x) {
  128. var a = parseFloat(document.getElementById("a").value);
  129. var b = parseFloat(document.getElementById("b").value);
  130. var c1 = parseFloat(document.getElementById("c").value);
  131. return 2*(2*a*x+b)*(x*(a*x+b)+c1);
  132. }
  133. /**
  134. * Calculates (f(x)^2)'' = ((a*x*x+b*x+c)^2)'' = 2 (b^2 + 6 a b x + 2 a (c + 3 a x^2))
  135. * @param {Number} x
  136. * @return {Number} (f(x)^2)''
  137. */
  138. function gedSquaredValueDD(x) {
  139. var a = parseFloat(document.getElementById("a").value);
  140. var b = parseFloat(document.getElementById("b").value);
  141. var c1 = parseFloat(document.getElementById("c").value);
  142. return 2*(b*b+6*a*b*x+2*a*(c1+3*a*x*x));
  143. }
  144. function findMin(p) {
  145. var a = parseFloat(document.getElementById("a").value);
  146. var b = parseFloat(document.getElementById("b").value);
  147. var c1 = parseFloat(document.getElementById("c").value);
  148. /* old iterative solution that had problems near the center*/
  149. var lastx = -10000;
  150. var x = p.x;
  151. var i = 0;
  152. while (Math.abs(lastx-x) > 1 && i < 100) {
  153. // first derivate of the square of the distance function
  154. var fx = -2.0*p.x+2.0*x-2.0*p.y*getDValue(x) +2*getValue(x)*getDValue(x);
  155. var fxd = 2.0 -2.0*p.y*getDDValue(x)+2*(getDValue(x)*getDValue(x) + getValue(x)*getDDValue(x));
  156. if (fxd == 0) {
  157. console.log("wow!");
  158. return x;
  159. }
  160. // x_{n+1} = x_n - f(x_n)/f'(x_n) wenn x gesucht, sodass f(x) = 0 gilt
  161. lastx = x;
  162. x -= fx / fxd;
  163. i++;
  164. }
  165. // New direct solution
  166. /*var xs = -b / (2*a);
  167. var w = p.y + b*b/(4*a)-c1;
  168. var z = p.x + b / (2*a);
  169. var alpha = (1-2*a*w)/(2*a*a);
  170. var beta = -z/(2*a*a);
  171. var t = Math.pow(Math.pow(3*(4*Math.pow(alpha,3) +27* Math.pow(beta,2)),1/2) - 9*beta, 1/3);
  172. var currentMinX = t / (Math.pow(18, 1/3)) - Math.pow(2/3*alpha, 1/3) / t;
  173. */
  174. return x;
  175. }
  176. function getDist(p, minX) {
  177. var minY = getValue(minX);
  178. return euklideanDist({"x":minX, "y":minY}, {"x":p.x, "y":p.y});
  179. }
  180. /*******************************************************************/
  181. /* Start / Update */
  182. /*******************************************************************/
  183. function drawBoard(canvas, mouseCoords, radius) {
  184. var context = canvas.getContext('2d');
  185. context.canvas.width = window.innerWidth - 50;
  186. context.canvas.height = window.innerHeight - 120;
  187. context.clearRect(0, 0, canvas.width, canvas.height);
  188. drawQuadraticFunction(canvas);
  189. if (document.getElementById("pDistance").checked) {
  190. var add = parseInt(document.getElementById("density").value)+10;
  191. for (var x=0; x < canvas.width; x+=add) {
  192. for (var y=0; y < canvas.height; y+=add) {
  193. var dist = getDist({"x":r(x,true), "y":r(y,false)}, findMin({"x":r(x,true), "y":r(y,false)}));
  194. context.fillStyle = getColor(dist,0.5);
  195. context.fillRect(x, y, add/2, add/2);
  196. }
  197. }
  198. }
  199. }
  200. function updateBoard(){
  201. var canvas = document.getElementById("myCanvas");
  202. STRETCH_X = parseFloat(document.getElementById("STRETCH_X").value);
  203. STRETCH_Y = parseFloat(document.getElementById("STRETCH_Y").value);
  204. X_OFFSET = parseFloat(document.getElementById("X_OFFSET").value);
  205. Y_OFFSET = parseFloat(document.getElementById("Y_OFFSET").value);
  206. drawBoard(canvas, {"x":0,"y":0}, INITIAL_RADIUS);
  207. }
  208. var canvas = document.getElementById("myCanvas");
  209. var context = canvas.getContext("2d");
  210. drawBoard(canvas, {"x":0,"y":0}, INITIAL_RADIUS);
  211. setCursorByID("myCanvas", "crosshair");
  212. /** get the current position of the mouse */
  213. function getMouseCoords(canvas, evt) {
  214. var rect = canvas.getBoundingClientRect();
  215. return {
  216. "x": evt.clientX - rect.left,
  217. "y": evt.clientY - rect.top
  218. };
  219. }
  220. /** event listeners */
  221. canvas.addEventListener('mousemove',
  222. function (evt) {
  223. var mouseCoords = getMouseCoords(canvas, evt);
  224. drawBoard(canvas, mouseCoords, 10);
  225. // draw coordinates next to mouse
  226. context.fillStyle = "blue";
  227. context.font = "bold 16px Arial";
  228. var x = r(mouseCoords.x, true).toFixed(3);
  229. var y = r(mouseCoords.y, false).toFixed(3);
  230. context.fillText("(" + x + ", " + y + ")", mouseCoords.x + 5, mouseCoords.y - 5);
  231. var minX = findMin({"x": mouseCoords.x, "y": mouseCoords.y});
  232. var minY = getValue(minX);
  233. context.beginPath();
  234. context.moveTo(c(minX, true), c(minY, false), false);
  235. context.lineTo(mouseCoords.x, mouseCoords.y, false);
  236. context.stroke();
  237. var minRadius = getDist({"x":x, "y":y}, minX);
  238. /* Draw circle */
  239. drawEllipse(mouseCoords.x, mouseCoords.y, minRadius*STRETCH_X, minRadius*STRETCH_Y);
  240. }, false);