CNC_Simulator.html 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802
  1. <!--
  2. layout: false
  3. -->
  4. <!DOCTYPE html>
  5. <html xmlns="http://www.w3.org/1999/xhtml" lang="en">
  6. <head>
  7. <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
  8. <!--
  9. Useful websites:
  10. Comparison of Canvas and SVG
  11. http://people.mozilla.com/~vladimir/xtech2006/
  12. Canvas Tutorial
  13. https://developer.mozilla.org/en/Canvas_tutorial/
  14. Animating with Javascript + Canvas
  15. http://dev.opera.com/articles/view/blob-sallad-canvas-tag-and-javascrip/
  16. -->
  17. <title>CNC Simulator</title>
  18. <script type="text/javascript">
  19. // Global variables
  20. var temp = "";
  21. var start_second = 0;
  22. var intervalID = 0;
  23. var running = true;
  24. var seconds = 0; // 6 degrees
  25. var time_inc = 1; // in seconds
  26. var sec_hand_length = 70;
  27. var min_hand_length = 65;
  28. var hour_hand_length = 35;
  29. var clock_radius = 75;
  30. // Function to run as soon as page loads
  31. function init()
  32. {
  33. var d = new Date();
  34. var curr_hour = d.getHours();
  35. var curr_min = d.getMinutes();
  36. start_second = d.getSeconds();
  37. seconds = start_second + 60*curr_min + 60*60*((curr_hour) % 12);
  38. intervalID = window.setInterval(animate,1000);
  39. // Initialize the canvas.
  40. }
  41. // Animate one frame of the animation
  42. function animate()
  43. {
  44. var canvas = document.getElementById("cnc_canvas");
  45. var ctx = canvas.getContext("2d");
  46. var sec_angle = 0;
  47. var min_angle = 0;
  48. var hour_angle = 0;
  49. //alert("Breakpoint animate");
  50. // Always put the terminating code near the top in case there is an error later on and the function exits before it can stop the interval timer.
  51. seconds = seconds + time_inc;
  52. //if (seconds % 60 > (3+start_second) % 60) {
  53. // stop();
  54. //}
  55. with (ctx) {
  56. // What? No "clear canvas" command?
  57. clearRect(0,0,400,600);
  58. // Draw clock face
  59. strokeStyle = "black";
  60. lineWidth = 1;
  61. beginPath();
  62. arc(100, 100, clock_radius, 0.001, 2*Math.PI, false);
  63. //alert("Breakpoint arc");
  64. stroke();
  65. //alert("Breakpoint stroke");
  66. // Draw second hand.
  67. lineWidth = 1;
  68. strokeStyle = "green";
  69. beginPath();
  70. moveTo(100,100);
  71. sec_angle = 180-360/60*seconds;
  72. //alert("Breakpoint sec_angle = " + String(sec_angle));
  73. temp = "x = " + String(100+sec_hand_length*Sin(sec_angle)) + " y = " + String(100+sec_hand_length*Cos(sec_angle));
  74. //alert("Breakpoint " + temp);
  75. lineTo(100+sec_hand_length*Sin(sec_angle), 100+sec_hand_length*Cos(sec_angle));
  76. //alert("Breakpoint lineTo");
  77. stroke();
  78. // Draw minute hand.
  79. beginPath();
  80. strokeStyle = "red";
  81. lineWidth = 2;
  82. moveTo(100,100);
  83. min_angle = 180-360/60*seconds/60;
  84. lineTo(100+min_hand_length*Sin(min_angle), 100+min_hand_length*Cos(min_angle));
  85. stroke();
  86. // Draw hour hand.
  87. lineWidth = 3;
  88. strokeStyle = "blue";
  89. beginPath();
  90. moveTo(100,100);
  91. hour_angle = 180-360/12*seconds/60/60;
  92. lineTo(100+hour_hand_length*Sin(hour_angle), 100+hour_hand_length*Cos(hour_angle));
  93. stroke();
  94. }
  95. }
  96. function stop() {
  97. window.clearInterval(intervalID);
  98. running = false;
  99. }
  100. function start() {
  101. time_inc = 1;
  102. // Only start if we're not already running an animation.
  103. if (!running) {
  104. running = true;
  105. intervalID = window.setInterval(animate,1000);
  106. }
  107. }
  108. function start_high_speed() {
  109. // If we're not already running an animation.
  110. if (running) {
  111. // Kill previous interval
  112. stop();
  113. }
  114. time_inc = 15;
  115. running = true;
  116. intervalID = window.setInterval(animate,10);
  117. }
  118. function d2r(degrees) {
  119. return degrees*Math.PI/180;
  120. }
  121. function Sin(degrees) {
  122. return Math.sin(d2r(degrees));
  123. }
  124. function Cos(degrees) {
  125. return Math.cos(d2r(degrees));
  126. }
  127. </script>
  128. <style type="text/css">
  129. td {
  130. padding-top: 10px;
  131. }
  132. </style>
  133. </head>
  134. <body onload="init()">
  135. <h2 style="margin-bottom: 0">CNC Simulator</h2>
  136. <table>
  137. <tr>
  138. <td style="vertical-align: top">
  139. <div style="border-width: 5px; border-style: ridge;">
  140. <p style="font-weight: bold; margin: 10px">
  141. <span id="x_disp">X:</span>
  142. <br>
  143. <span id="y_disp">Y:</span>
  144. <br>
  145. <span id="z_disp">Z:</span>
  146. <br>
  147. <span id="time_disp">Time:</span>
  148. </p>
  149. </div>
  150. <form action="">
  151. <table>
  152. <tr>
  153. <td>
  154. <label for="ppi_input">Pixels per inch:</label>
  155. <br>
  156. <input id="ppi_input" type="text" value="127" style="width: 5em">
  157. </td>
  158. <td>
  159. <label for="time_step_input">Time step:</label>
  160. <br>
  161. <input id="time_step_input" type="text" value="1" style="width: 5em">
  162. </td>
  163. </tr>
  164. <tr>
  165. <td>
  166. <label for="speed_input">Speed factor:</label>
  167. <br>
  168. <input id="speed_input" type="text" value="5" style="width: 5em; margin-right: 5em;">
  169. </td>
  170. <td>
  171. <label for="blitz_input">Blitz mode:</label>
  172. <input type="checkbox" id="blitz_input" checked="checked">
  173. </td>
  174. </tr>
  175. <tr>
  176. <td>
  177. <label for="offset_x">Offset X:</label>
  178. <br>
  179. <input id="offset_x" type="text" value="50" style="width: 5em; margin-right: 5em;">
  180. </td>
  181. <td>
  182. <label for="offset_y">Offset Y:</label>
  183. <br>
  184. <input id="offset_y" type="text" value="50" style="width: 5em; margin-right: 5em;">
  185. </td>
  186. </tr>
  187. <tr>
  188. <td colspan="2">
  189. <label for="code_input">Enter CNC code here:<br></label>
  190. <textarea rows="20" cols="50" id="code_input">START MM 01 // Use millimeters, program ID 01
  191. TD= 3.175 // Tool diameter: 1/8 in = 3.175 mm
  192. FR XY =03.7 // Divided the suggested feedrate by two, because tool was flexing.
  193. FR Z =01.2 // Feedrate based on 1 cutting edge, instead of 4, then slightly reduced.
  194. SETUP &gt;zcxyu // Allow user to position tool above 1st hole.
  195. REPEAT 02 // Second traverse gets the edges better
  196. GOfX 0.000 // Go to 1st hole
  197. GOfY 0.000 //
  198. GO Z- 6.000 // Drill 1st hole
  199. Z&gt;C // Raise tool
  200. CALL SUB 10 // Cut left side...
  201. ZERO AT // (Set origin to right hole)
  202. X 47.000 // * Change depending on connector *
  203. Y 0.000 //
  204. CALL SUB 11 // ...Cut right side...
  205. ZERO AT // (Reset origin to left hole)
  206. X- 47.000 // * Change depending on connector *
  207. Y 0.000 //
  208. GO X 6.920 // ...And return to Point 1.
  209. Y 3.410 //
  210. Z&gt;C // Raise tool
  211. GOfX 47.000 // Go to 2nd hole * Change depending on connector *
  212. Y 0.000 //
  213. GO Z -6.000 // Drill 2nd hole
  214. Z&gt;C // Raise tool
  215. REPEAT END //
  216. END NEW PART //
  217. SUB 10 // Left Side (located on line 050)
  218. GO X 6.920 // Point 1
  219. Y 3.410 //
  220. GO Z- 9.000 // Insert tool
  221. ARC // Point 2
  222. XC= 6.920 //
  223. YC= 1.500 //
  224. a= 101.470 //
  225. GO X 5.740 // Point 3
  226. Y- 2.280 //
  227. ARC // Point 4
  228. XC= 7.120 //
  229. YC=- 2.000 //
  230. a= 79.530 //
  231. SUB RETURN //
  232. SUB 11 // Right Side (located on line 100)
  233. GO X- 7.120 // Point 5
  234. Y- 3.410 //
  235. ARC // Point 6
  236. XC=- 7.120 //
  237. YC=- 2.000 //
  238. a= 79.530 //
  239. GO X- 5.050 // Point 7
  240. Y 1.120 //
  241. ARC // Point 8
  242. XC=- 6.920 //
  243. YC= 1.500 //
  244. a= 101.470 //
  245. SUB RETURN //
  246. </textarea>
  247. <br>
  248. <input name="submit" type="button" value="Click To Run Simulator" onclick="startSimulation();">
  249. <input id="pause_input" type="button" value="Pause" onclick="pause();">
  250. </td>
  251. </tr>
  252. </table>
  253. </form>
  254. </td>
  255. <td style="vertical-align:top">
  256. <canvas id="cnc_canvas" width="780" height="650" style="border:1px solid black"></canvas>
  257. </td>
  258. </tr>
  259. </table>
  260. <hr>
  261. <div class="output_wrapper">
  262. <b>Output:</b>
  263. <pre id="output"></pre>
  264. </div>
  265. <script type="text/javascript">
  266. // The so-called Prototype Dollar function, a shortcut for document.getElementById()
  267. function $() {
  268. var elements = new Array();
  269. for (var i = 0; i < arguments.length; i++) {
  270. var element = arguments[i];
  271. if (typeof element == "string")
  272. element = document.getElementById(element);
  273. if (arguments.length == 1)
  274. return element;
  275. elements.push(element);
  276. }
  277. return elements;
  278. }
  279. ctx = null;
  280. canvas = null;
  281. function clear_vars() {// Global variables
  282. unit = "MM"; // or "IN"
  283. time_step = 1; // update every half second.
  284. ppi = 96; // Pixels per inch
  285. cf = 1; // Pixel to unit ratio
  286. td = 1; // Tool diameter
  287. max_frx = 1; // Maximum feed rate X
  288. max_fry = 1; // Maximum feed rate Y
  289. max_frz = 1; // Maximum feed rate Z
  290. x_inc = 1; // Distance to move per tick
  291. y_inc = 1; // Distance to move per tick
  292. z_inc = 1; // Distance to move per tick
  293. a_inc = 1; // Distance to move per tick
  294. mar_x = 10; // Horizontal margin of origin
  295. mar_y = 10; // Vertical margin of origin
  296. tool_c = 2; // Clearance height.
  297. tool_x = 0; // Tool position X
  298. tool_y = 0; // Tool position Y
  299. tool_z = 0; // Tool position Z
  300. tool_a = 0; // Tool angle (used for arcs)
  301. arc_r = 0; // Radius of currently drawn arc.
  302. arc_dir = false; // Counter-clockwise
  303. old_x = 0; // Previous tool position X
  304. old_y = 0; // Previous tool position Y
  305. old_z = 0; // Previous tool position Z
  306. old_a = 0; // Previous tool angle (used for arcs)
  307. goal_x = 0; // Destination coordinate
  308. goal_y = 0; // Destination coordinate
  309. goal_z = 0; // Destination coordinate
  310. xc = 0; // Used for drawing arcs
  311. yc = 0; // Used for drawing arcs
  312. ac = 0; // Used for drawing arcs
  313. time = 0; // Time needed to execute cut.
  314. time_x = 0;
  315. time_y = 0;
  316. time_z = 0;
  317. time_a = 0; // Used for arcs
  318. time_elapsed = 0;
  319. n = 0; // Line index
  320. lines = []; // Array of each line of code
  321. line = ""; // Current line
  322. moving = false; // Set to true when the tool is moving from one point to another.
  323. arcing = false; // Set to true when the tool is arcing.
  324. interval_id = 0; // Variable needed to stop the timer during real-time mode
  325. blitz_done = false; // Variable used to stop the timer in blitz mode.
  326. paused = false; // Pause/unpaused state
  327. speed = 1;
  328. stack = []; // Line numbers to return to
  329. canvas_stack = 0; // How many times restore() the canvas
  330. canvas = $("cnc_canvas");
  331. ctx = canvas.getContext("2d");
  332. }
  333. function startSimulation() {
  334. // Clean up variables from the last run.
  335. clear_vars();
  336. // Clear debug output
  337. $("output").innerHTML = "";
  338. // Because the previous run might not have finished
  339. for (var i = 0; i < 50; i++) {
  340. ctx.restore();
  341. }
  342. // Stop the clock
  343. stop();
  344. // Since there is no "clear canvas" command.
  345. ctx.fillStyle = "white"
  346. ctx.fillRect(0, 0, canvas.width, canvas.height);
  347. ctx.strokeStyle = "black";
  348. /*
  349. The canvas has it's origin in the top-left corner, but the CNC
  350. Machine has it's origin in the bottom-left corner. Therefore,
  351. we translate the origin and apply a transformation matrix to
  352. flip the Y direction.
  353. */
  354. ctx.save();
  355. canvas_stack = canvas_stack + 1;
  356. ctx.transform(1, 0, 0, -1, 0, 0);
  357. mar_x = parseFloat($("offset_x").value);
  358. mar_y = parseFloat($("offset_y").value);
  359. ctx.translate(mar_x, mar_y-canvas.height);
  360. ctx.save();
  361. canvas_stack = canvas_stack + 1;
  362. lines = $("code_input").value.split("\n"); //str.split("\n");
  363. ppi = parseFloat($("ppi_input").value);
  364. // Start the animation sequence
  365. n = -1;
  366. debugn("There are " + lines.length + " lines");
  367. // Stop all animations
  368. window.clearInterval(interval_id);
  369. paused = true;
  370. pause();
  371. }
  372. function pause() {
  373. if (paused) {
  374. paused = false;
  375. // Set the speed
  376. speed = parseFloat($("speed_input").value);
  377. time_step = parseFloat($("time_step_input").value);
  378. // Start the CNC simulation.
  379. if ($("blitz_input").checked) {
  380. while (blitz_done == false) {
  381. tick();
  382. }
  383. } else {
  384. interval_id = window.setInterval(tick,1000*time_step/speed);
  385. }
  386. $("pause_input").value = "Pause";
  387. } else {
  388. paused = true;
  389. window.clearInterval(interval_id);
  390. $("pause_input").value = "Resume";
  391. }
  392. }
  393. function nextLine() {
  394. n = n + 1;
  395. if (n >= lines.length) {
  396. return false;
  397. } else {
  398. line = lines[n];
  399. return true;
  400. }
  401. }
  402. function startsWith(s1, s2) {
  403. if (s1.substr(0,s2.length) == s2) {
  404. return true;
  405. } else {
  406. return false;
  407. }
  408. }
  409. function deg2rad(d) {
  410. return (Math.PI/180)*d;
  411. }
  412. function rad2deg(r) {
  413. return (180/Math.PI)*r;
  414. }
  415. function tick() {
  416. if (moving) {
  417. // Store previous position
  418. old_x = tool_x;
  419. old_y = tool_y;
  420. old_z = tool_z;
  421. old_a = tool_a;
  422. // Figure out new position
  423. if (time < time_step) {
  424. if (arcing) {
  425. tool_a = goal_a;
  426. tool_x = xc + Math.cos(tool_a)*arc_r;
  427. tool_y = yc + Math.sin(tool_a)*arc_r;
  428. } else {
  429. tool_x = goal_x;
  430. tool_y = goal_y;
  431. tool_z = goal_z;
  432. }
  433. time_elapsed = time_elapsed + time;
  434. } else {
  435. if (arcing) {
  436. tool_a = tool_a + a_inc;
  437. tool_x = xc + Math.cos(tool_a)*arc_r;
  438. tool_y = yc + Math.sin(tool_a)*arc_r;
  439. } else {
  440. tool_x = tool_x + x_inc;
  441. tool_y = tool_y + y_inc;
  442. tool_z = tool_z + z_inc;
  443. }
  444. time_elapsed = time_elapsed + time_step;
  445. }
  446. // Update display
  447. $("x_disp").innerHTML = "X: " + tool_x;
  448. $("y_disp").innerHTML = "Y: " + tool_y;
  449. $("z_disp").innerHTML = "Z: " + tool_z;
  450. $("time_disp").innerHTML = "Time: " + time_elapsed;
  451. // Draw line if tool is below surface.
  452. if (tool_z < 0) {
  453. with (ctx) {
  454. // Draw hole
  455. beginPath();
  456. fillStyle = "black";
  457. arc(tool_x, tool_y, td/2.0, 0, Math.PI*2.0, true);
  458. fill();
  459. // Draw line
  460. lineCap = "round";
  461. lineWidth = td;
  462. beginPath();
  463. strokeStyle = "black";
  464. if (arcing) {
  465. arc(xc, yc, arc_r, old_a, tool_a, arc_dir);
  466. } else {
  467. moveTo(old_x, old_y);
  468. lineTo((tool_x), (tool_y));
  469. }
  470. stroke();
  471. }
  472. } else {
  473. // Draw hollow dot (so we can see the path of the tool)
  474. with (ctx) {
  475. lineWidth = 1/cf;
  476. strokeStyle = "blue";
  477. strokeRect((tool_x)-1/cf, (tool_y) -1/cf, 3/cf, 3/cf);
  478. }
  479. }
  480. // Decrease time
  481. time = time - time_step;
  482. // Finish line
  483. if (time < 0) {
  484. time = 0;
  485. moving = false;
  486. arcing = false;
  487. }
  488. debug(".");
  489. } else {
  490. // Always safest to put this first, in case of unhandled syntax
  491. // errors later on in the code.
  492. if (nextLine()) {
  493. ndebug("Line " + n + ": ");
  494. } else {
  495. ndebug("Ended because there are no more lines.");
  496. n = -1;
  497. ndebug("canvas_stack = " + canvas_stack);
  498. for (var i = 0; i < canvas_stack; i++) {
  499. ndebug("ctx.restore()");
  500. ctx.restore();
  501. }
  502. blitz_done = true;
  503. window.clearInterval(interval_id);
  504. return;
  505. }
  506. //debug(line);
  507. if (startsWith(line, "START")) {
  508. // Set unit
  509. unit = line.substr(6,2);
  510. debugd("Unit: " + unit);
  511. if (unit == "MM") {
  512. debugd("Millimeters");
  513. cf = ppi / 25.4;
  514. } else if (unit == "IN") {
  515. debugd("Inches");
  516. cf = ppi;
  517. } else {
  518. ndebug("Invalid unit");
  519. n = lines.length;
  520. return;
  521. }
  522. debug("Scale: " + cf);
  523. ctx.scale(cf, cf);
  524. } else if (startsWith(line, " TD=")) {
  525. // Set tool diameter
  526. debugd("Tool Diameter: " + line.substring(4,12));
  527. td = parseFloat(line.substring(4,12));
  528. ctx.lineWidth = td;
  529. debug("ctx.lineWidth = " + ctx.lineWidth);
  530. } else if (startsWith(line, "FR")) {
  531. // TODO: set feed rate
  532. debugd("Setting feed rate for " + line.substr(3,3) + " to " + line.substring(8,12));
  533. if (line.indexOf("X") != -1) {
  534. max_frx = parseFloat(line.substring(8,12));
  535. debug(" set FR X = " + max_frx);
  536. }
  537. if (line.indexOf("Y") != -1) {
  538. max_fry = parseFloat(line.substring(8,12));
  539. debug(" set FR Y = " + max_fry);
  540. }
  541. if (line.indexOf("Z") != -1) {
  542. max_frz = parseFloat(line.substring(8,12));
  543. debug(" set FR Z = " + max_frz);
  544. }
  545. } else if (startsWith(line, "SETUP")) {
  546. // This step doesn't translate well, and would get annoying
  547. debug("Setup: " + line.substring(7,12));
  548. debug(" Note: in real life, this command would let you position the tool at a new 'zero' position.");
  549. } else if (startsWith(line, "Z>C")) {
  550. tool_z = tool_c;
  551. debug("Z>C: Z = " + String(tool_c));
  552. } else if (startsWith(line, "GO")) {
  553. goal_x = tool_x; time_x = 0;
  554. goal_y = tool_y; time_y = 0;
  555. goal_z = tool_z; time_z = 0;
  556. debugd("Go");
  557. do {
  558. if (line.substr(3,1) == "f") {
  559. // TODO: how fast is "fast" anyway?
  560. }
  561. if (line.substr(3,1) == "X") {
  562. goal_x = parseFloat(line.substring(5,12));
  563. if (line.substr(4,1) == "-") {
  564. goal_x = -goal_x;
  565. }
  566. time_x = Math.abs((goal_x - tool_x) / max_frx);
  567. debugd("X coor: " + goal_x);
  568. } else if (line.substr(3,1) == "Y") {
  569. goal_y = parseFloat(line.substring(5,12));
  570. if (line.substr(4,1) == "-") {
  571. goal_y = -goal_y;
  572. }
  573. time_y = Math.abs((goal_y - tool_y) / max_fry);
  574. debugd("Y coor: " + goal_y);
  575. } else if (line.substr(3,1) == "Z") {
  576. goal_z = parseFloat(line.substring(5,12));
  577. if (line.substr(4,1) == "-") {
  578. goal_z = -goal_z;
  579. }
  580. time_z = Math.abs((goal_z - tool_z) / max_frz);
  581. debugd("Z coor: " + goal_z);
  582. }
  583. n = n+1;
  584. if (n >= lines.length) {
  585. break;
  586. }
  587. line = lines[n];
  588. } while (line.substr(0,2) == " ")
  589. n = n - 1;
  590. // Compute how many seconds it will take to execute this cut
  591. time = Math.max(Math.max(time_x, time_y), time_z);
  592. debugd("Time Required: "+time);
  593. x_inc = (goal_x - tool_x) / time * time_step;
  594. y_inc = (goal_y - tool_y) / time * time_step;
  595. z_inc = (goal_z - tool_z) / time * time_step;
  596. debug("X inc: " + x_inc + " Y inc: " + y_inc +" Z inc: " + z_inc);
  597. moving = true;
  598. } else if (startsWith(line, "ZERO AT")) {
  599. // Translate the canvas origin
  600. debugd("ZERO AT");
  601. goal_x = 0; time_x = 0;
  602. goal_y = 0; time_y = 0;
  603. goal_z = 0; time_z = 0;
  604. if (!nextLine()) { return; }
  605. while (line.substr(0,2) == " ") {
  606. if (line.substr(3,1) == "X") {
  607. goal_x = parseFloat(line.substring(5,12));
  608. if (line.substr(4,1) == "-") {
  609. goal_x = -goal_x;
  610. }
  611. debug(" X coor: " + goal_x);
  612. } else if (line.substr(3,1) == "Y") {
  613. goal_y = parseFloat(line.substring(5,12));
  614. if (line.substr(4,1) == "-") {
  615. goal_y = -goal_y;
  616. }
  617. debug(" Y coor: " + goal_y);
  618. } else if (line.substr(3,1) == "Z") {
  619. goal_z = parseFloat(line.substring(5,12));
  620. if (line.substr(4,1) == "-") {
  621. goal_z = -goal_z;
  622. }
  623. debug(" Z coor: " + goal_z);
  624. }
  625. if (!nextLine()) { break; }
  626. }
  627. n = n - 1;
  628. // Translate the canvas origin
  629. ctx.save();
  630. ctx.translate(goal_x, goal_y);
  631. canvas_stack = canvas_stack + 1;
  632. // Untranslate tool
  633. tool_x = tool_x - goal_x;
  634. tool_y = tool_y - goal_y;
  635. } else if (startsWith(line, "ARC")) {
  636. debugd("ARC");
  637. if (!nextLine()) { return; }
  638. if (line.substr(0,4) == " XC=") {
  639. xc = parseFloat(line.substring(5,12));
  640. if (line.substr(4,1) == "-") {
  641. xc = -xc;
  642. }
  643. debug(" XC: " + xc);
  644. } else {
  645. debug("I really expected a line starting with ' XC='");
  646. }
  647. if (!nextLine()) { return; }
  648. if (line.substr(0,4) == " YC=") {
  649. yc = parseFloat(line.substring(5,12));
  650. if (line.substr(4,1) == "-") {
  651. yc = -yc;
  652. }
  653. debug(" YC: " + yc);
  654. } else {
  655. debug("I really expected a line starting with ' YC='");
  656. }
  657. if (!nextLine()) { return; }
  658. if (line.substr(0,4) == " a=") {
  659. // I call the variable 'ac' for resemblance
  660. ac = parseFloat(line.substring(5,12));
  661. if (line.substr(4,1) == "-") {
  662. ac = -ac;
  663. arc_dir = true;
  664. } else {
  665. arc_dir = false;
  666. }
  667. debugd(" AC: " + ac);
  668. ac = deg2rad(ac);
  669. debugd("in radians: " + ac);
  670. } else {
  671. debug("I really expected a line starting with ' a='");
  672. }
  673. arc_r = Math.sqrt( Math.pow(xc-tool_x, 2) +
  674. Math.pow(yc-tool_y, 2) );
  675. debugd("R = " + arc_r);
  676. var dist = Math.abs(arc_r*ac);
  677. var xyfr = (max_frx + max_fry)/2;
  678. time = dist/xyfr;
  679. debugd("Time Required: " + time);
  680. a_inc = ac/time*time_step;
  681. debugd("a_inc = " + a_inc);
  682. // Math.atan2 seems to be measuring Clockwise from the Y
  683. // axis. Ridiculous.
  684. tool_a = Math.atan2(tool_y-yc, tool_x-xc);
  685. debugd("tool_a = " + rad2deg(tool_a) + " degrees");
  686. goal_a = Math.atan2(tool_y-yc, tool_x-xc) + ac;
  687. debug("goal_a = " + rad2deg(goal_a) + " degrees");
  688. moving = true;
  689. arcing = true;
  690. //arcing = true;
  691. } else if (startsWith(line, "CALL SUB")) {
  692. // Push line # onto stack
  693. debugd("Calling Sub");
  694. stack.push(n);
  695. ctx.save();
  696. canvas_stack = canvas_stack + 1;
  697. // Get the SUB id
  698. var sub_id = line.substring(9,12).replace(" ","");
  699. var sub_id = "SUB " + sub_id;
  700. debugd("Scanning for '" + sub_id +"'");
  701. // Move the cursor location to the start of the subroutine.
  702. n = -1;
  703. while (n < lines.length) {
  704. n = n + 1;
  705. line = lines[n];
  706. if (line.substr(0,sub_id.length) == sub_id) {
  707. debugd("Found '" + sub_id + "' on line " + String(n));
  708. debug("Stack: " + String(stack));
  709. break;
  710. }
  711. }
  712. } else if (startsWith(line, "SUB RETURN")) {
  713. // Return to our line # in the stack.
  714. n = stack.pop();
  715. ctx.restore();
  716. canvas_stack = canvas_stack - 1;
  717. debugd("SUB RETURN return to line " + String(n));
  718. debug("Stack: " + String(stack));
  719. } else if (startsWith(line, "REPEAT END")) {
  720. if (stack.length > 0) {
  721. n = stack.pop();
  722. debug("REPEAT END return to line " + String(n));
  723. } else {
  724. debug("Passing REPEAT END");
  725. }
  726. } else if (startsWith(line, "REPEAT")) {
  727. var rep_num = parseInt(line.substring(10,12))
  728. debugd("REPEAT " + String(rep_num) + " times");
  729. for (var i = 1; i < rep_num; i++) {
  730. stack.push(n);
  731. }
  732. debug("Stack: " + String(stack));
  733. } else if (startsWith(line, "END")) {
  734. n = lines.length;
  735. debug("Encountered an END");
  736. } else {
  737. debug("Skipping line: " + line);
  738. }
  739. }
  740. }
  741. /*
  742. These are placed at the end, so if prior code has syntax errors,
  743. the page will clearly be broken.
  744. */
  745. function debug(str) {
  746. var obj;
  747. obj = $("output");
  748. obj.innerHTML = obj.innerHTML + str;
  749. }
  750. function ndebug(str) {
  751. var obj;
  752. obj = $("output");
  753. obj.innerHTML = obj.innerHTML + "\n" + str;
  754. }
  755. function debugn(str) {
  756. var obj;
  757. obj = $("output");
  758. obj.innerHTML = obj.innerHTML + str + "\n";
  759. }
  760. function ndebugn(str) {
  761. var obj;
  762. obj = $("output");
  763. obj.innerHTML = obj.innerHTML + "\n" + str + "\n";
  764. }
  765. function debugd(str) {
  766. var obj;
  767. obj = $("output");
  768. obj.innerHTML = obj.innerHTML + str + "... ";
  769. }
  770. </script>
  771. <footer>
  772. <hr>
  773. <p style="text-align: center; font-style:italic"><a href="http://validator.w3.org/#validate-by-upload">Freaking valid HTML5 baby!!!</a></p>
  774. </footer>
  775. </body>
  776. </html>