|
@@ -0,0 +1,802 @@
|
|
|
|
|
+<!--
|
|
|
|
|
+layout: false
|
|
|
|
|
+-->
|
|
|
|
|
+<!DOCTYPE html>
|
|
|
|
|
+<html xmlns="http://www.w3.org/1999/xhtml" lang="en">
|
|
|
|
|
+
|
|
|
|
|
+<head>
|
|
|
|
|
+<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
|
|
|
|
+<!--
|
|
|
|
|
+Useful websites:
|
|
|
|
|
+Comparison of Canvas and SVG
|
|
|
|
|
+http://people.mozilla.com/~vladimir/xtech2006/
|
|
|
|
|
+
|
|
|
|
|
+Canvas Tutorial
|
|
|
|
|
+https://developer.mozilla.org/en/Canvas_tutorial/
|
|
|
|
|
+
|
|
|
|
|
+Animating with Javascript + Canvas
|
|
|
|
|
+http://dev.opera.com/articles/view/blob-sallad-canvas-tag-and-javascrip/
|
|
|
|
|
+-->
|
|
|
|
|
+<title>CNC Simulator</title>
|
|
|
|
|
+
|
|
|
|
|
+<script type="text/javascript">
|
|
|
|
|
+// Global variables
|
|
|
|
|
+var temp = "";
|
|
|
|
|
+var start_second = 0;
|
|
|
|
|
+var intervalID = 0;
|
|
|
|
|
+var running = true;
|
|
|
|
|
+var seconds = 0; // 6 degrees
|
|
|
|
|
+var time_inc = 1; // in seconds
|
|
|
|
|
+var sec_hand_length = 70;
|
|
|
|
|
+var min_hand_length = 65;
|
|
|
|
|
+var hour_hand_length = 35;
|
|
|
|
|
+var clock_radius = 75;
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+// Function to run as soon as page loads
|
|
|
|
|
+function init()
|
|
|
|
|
+{
|
|
|
|
|
+ var d = new Date();
|
|
|
|
|
+ var curr_hour = d.getHours();
|
|
|
|
|
+ var curr_min = d.getMinutes();
|
|
|
|
|
+ start_second = d.getSeconds();
|
|
|
|
|
+ seconds = start_second + 60*curr_min + 60*60*((curr_hour) % 12);
|
|
|
|
|
+ intervalID = window.setInterval(animate,1000);
|
|
|
|
|
+
|
|
|
|
|
+ // Initialize the canvas.
|
|
|
|
|
+
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+// Animate one frame of the animation
|
|
|
|
|
+function animate()
|
|
|
|
|
+{
|
|
|
|
|
+ var canvas = document.getElementById("cnc_canvas");
|
|
|
|
|
+ var ctx = canvas.getContext("2d");
|
|
|
|
|
+ var sec_angle = 0;
|
|
|
|
|
+ var min_angle = 0;
|
|
|
|
|
+ var hour_angle = 0;
|
|
|
|
|
+ //alert("Breakpoint animate");
|
|
|
|
|
+ // 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.
|
|
|
|
|
+ seconds = seconds + time_inc;
|
|
|
|
|
+ //if (seconds % 60 > (3+start_second) % 60) {
|
|
|
|
|
+ // stop();
|
|
|
|
|
+ //}
|
|
|
|
|
+
|
|
|
|
|
+ with (ctx) {
|
|
|
|
|
+ // What? No "clear canvas" command?
|
|
|
|
|
+ clearRect(0,0,400,600);
|
|
|
|
|
+
|
|
|
|
|
+ // Draw clock face
|
|
|
|
|
+ strokeStyle = "black";
|
|
|
|
|
+ lineWidth = 1;
|
|
|
|
|
+ beginPath();
|
|
|
|
|
+ arc(100, 100, clock_radius, 0.001, 2*Math.PI, false);
|
|
|
|
|
+ //alert("Breakpoint arc");
|
|
|
|
|
+ stroke();
|
|
|
|
|
+ //alert("Breakpoint stroke");
|
|
|
|
|
+
|
|
|
|
|
+ // Draw second hand.
|
|
|
|
|
+ lineWidth = 1;
|
|
|
|
|
+ strokeStyle = "green";
|
|
|
|
|
+ beginPath();
|
|
|
|
|
+ moveTo(100,100);
|
|
|
|
|
+ sec_angle = 180-360/60*seconds;
|
|
|
|
|
+ //alert("Breakpoint sec_angle = " + String(sec_angle));
|
|
|
|
|
+ temp = "x = " + String(100+sec_hand_length*Sin(sec_angle)) + " y = " + String(100+sec_hand_length*Cos(sec_angle));
|
|
|
|
|
+ //alert("Breakpoint " + temp);
|
|
|
|
|
+ lineTo(100+sec_hand_length*Sin(sec_angle), 100+sec_hand_length*Cos(sec_angle));
|
|
|
|
|
+ //alert("Breakpoint lineTo");
|
|
|
|
|
+ stroke();
|
|
|
|
|
+
|
|
|
|
|
+ // Draw minute hand.
|
|
|
|
|
+ beginPath();
|
|
|
|
|
+ strokeStyle = "red";
|
|
|
|
|
+ lineWidth = 2;
|
|
|
|
|
+ moveTo(100,100);
|
|
|
|
|
+ min_angle = 180-360/60*seconds/60;
|
|
|
|
|
+ lineTo(100+min_hand_length*Sin(min_angle), 100+min_hand_length*Cos(min_angle));
|
|
|
|
|
+ stroke();
|
|
|
|
|
+
|
|
|
|
|
+ // Draw hour hand.
|
|
|
|
|
+ lineWidth = 3;
|
|
|
|
|
+ strokeStyle = "blue";
|
|
|
|
|
+ beginPath();
|
|
|
|
|
+ moveTo(100,100);
|
|
|
|
|
+ hour_angle = 180-360/12*seconds/60/60;
|
|
|
|
|
+ lineTo(100+hour_hand_length*Sin(hour_angle), 100+hour_hand_length*Cos(hour_angle));
|
|
|
|
|
+ stroke();
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+function stop() {
|
|
|
|
|
+ window.clearInterval(intervalID);
|
|
|
|
|
+ running = false;
|
|
|
|
|
+}
|
|
|
|
|
+function start() {
|
|
|
|
|
+ time_inc = 1;
|
|
|
|
|
+ // Only start if we're not already running an animation.
|
|
|
|
|
+ if (!running) {
|
|
|
|
|
+ running = true;
|
|
|
|
|
+ intervalID = window.setInterval(animate,1000);
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+function start_high_speed() {
|
|
|
|
|
+ // If we're not already running an animation.
|
|
|
|
|
+ if (running) {
|
|
|
|
|
+ // Kill previous interval
|
|
|
|
|
+ stop();
|
|
|
|
|
+ }
|
|
|
|
|
+ time_inc = 15;
|
|
|
|
|
+ running = true;
|
|
|
|
|
+ intervalID = window.setInterval(animate,10);
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+function d2r(degrees) {
|
|
|
|
|
+ return degrees*Math.PI/180;
|
|
|
|
|
+}
|
|
|
|
|
+function Sin(degrees) {
|
|
|
|
|
+ return Math.sin(d2r(degrees));
|
|
|
|
|
+}
|
|
|
|
|
+function Cos(degrees) {
|
|
|
|
|
+ return Math.cos(d2r(degrees));
|
|
|
|
|
+}
|
|
|
|
|
+</script>
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+<style type="text/css">
|
|
|
|
|
+td {
|
|
|
|
|
+ padding-top: 10px;
|
|
|
|
|
+}
|
|
|
|
|
+</style>
|
|
|
|
|
+</head>
|
|
|
|
|
+
|
|
|
|
|
+<body onload="init()">
|
|
|
|
|
+<h2 style="margin-bottom: 0">CNC Simulator</h2>
|
|
|
|
|
+<table>
|
|
|
|
|
+ <tr>
|
|
|
|
|
+ <td style="vertical-align: top">
|
|
|
|
|
+ <div style="border-width: 5px; border-style: ridge;">
|
|
|
|
|
+ <p style="font-weight: bold; margin: 10px">
|
|
|
|
|
+ <span id="x_disp">X:</span>
|
|
|
|
|
+ <br>
|
|
|
|
|
+ <span id="y_disp">Y:</span>
|
|
|
|
|
+ <br>
|
|
|
|
|
+ <span id="z_disp">Z:</span>
|
|
|
|
|
+ <br>
|
|
|
|
|
+ <span id="time_disp">Time:</span>
|
|
|
|
|
+ </p>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <form action="">
|
|
|
|
|
+ <table>
|
|
|
|
|
+ <tr>
|
|
|
|
|
+ <td>
|
|
|
|
|
+ <label for="ppi_input">Pixels per inch:</label>
|
|
|
|
|
+ <br>
|
|
|
|
|
+ <input id="ppi_input" type="text" value="127" style="width: 5em">
|
|
|
|
|
+ </td>
|
|
|
|
|
+ <td>
|
|
|
|
|
+ <label for="time_step_input">Time step:</label>
|
|
|
|
|
+ <br>
|
|
|
|
|
+ <input id="time_step_input" type="text" value="1" style="width: 5em">
|
|
|
|
|
+ </td>
|
|
|
|
|
+ </tr>
|
|
|
|
|
+ <tr>
|
|
|
|
|
+ <td>
|
|
|
|
|
+ <label for="speed_input">Speed factor:</label>
|
|
|
|
|
+ <br>
|
|
|
|
|
+ <input id="speed_input" type="text" value="5" style="width: 5em; margin-right: 5em;">
|
|
|
|
|
+ </td>
|
|
|
|
|
+ <td>
|
|
|
|
|
+ <label for="blitz_input">Blitz mode:</label>
|
|
|
|
|
+ <input type="checkbox" id="blitz_input" checked="checked">
|
|
|
|
|
+ </td>
|
|
|
|
|
+ </tr>
|
|
|
|
|
+ <tr>
|
|
|
|
|
+ <td>
|
|
|
|
|
+ <label for="offset_x">Offset X:</label>
|
|
|
|
|
+ <br>
|
|
|
|
|
+ <input id="offset_x" type="text" value="50" style="width: 5em; margin-right: 5em;">
|
|
|
|
|
+ </td>
|
|
|
|
|
+ <td>
|
|
|
|
|
+ <label for="offset_y">Offset Y:</label>
|
|
|
|
|
+ <br>
|
|
|
|
|
+ <input id="offset_y" type="text" value="50" style="width: 5em; margin-right: 5em;">
|
|
|
|
|
+ </td>
|
|
|
|
|
+ </tr>
|
|
|
|
|
+ <tr>
|
|
|
|
|
+ <td colspan="2">
|
|
|
|
|
+ <label for="code_input">Enter CNC code here:<br></label>
|
|
|
|
|
+ <textarea rows="20" cols="50" id="code_input">START MM 01 // Use millimeters, program ID 01
|
|
|
|
|
+ TD= 3.175 // Tool diameter: 1/8 in = 3.175 mm
|
|
|
|
|
+FR XY =03.7 // Divided the suggested feedrate by two, because tool was flexing.
|
|
|
|
|
+FR Z =01.2 // Feedrate based on 1 cutting edge, instead of 4, then slightly reduced.
|
|
|
|
|
+SETUP >zcxyu // Allow user to position tool above 1st hole.
|
|
|
|
|
+REPEAT 02 // Second traverse gets the edges better
|
|
|
|
|
+GOfX 0.000 // Go to 1st hole
|
|
|
|
|
+GOfY 0.000 //
|
|
|
|
|
+GO Z- 6.000 // Drill 1st hole
|
|
|
|
|
+Z>C // Raise tool
|
|
|
|
|
+CALL SUB 10 // Cut left side...
|
|
|
|
|
+ZERO AT // (Set origin to right hole)
|
|
|
|
|
+ X 47.000 // * Change depending on connector *
|
|
|
|
|
+ Y 0.000 //
|
|
|
|
|
+CALL SUB 11 // ...Cut right side...
|
|
|
|
|
+ZERO AT // (Reset origin to left hole)
|
|
|
|
|
+ X- 47.000 // * Change depending on connector *
|
|
|
|
|
+ Y 0.000 //
|
|
|
|
|
+GO X 6.920 // ...And return to Point 1.
|
|
|
|
|
+ Y 3.410 //
|
|
|
|
|
+Z>C // Raise tool
|
|
|
|
|
+GOfX 47.000 // Go to 2nd hole * Change depending on connector *
|
|
|
|
|
+ Y 0.000 //
|
|
|
|
|
+GO Z -6.000 // Drill 2nd hole
|
|
|
|
|
+Z>C // Raise tool
|
|
|
|
|
+REPEAT END //
|
|
|
|
|
+END NEW PART //
|
|
|
|
|
+
|
|
|
|
|
+SUB 10 // Left Side (located on line 050)
|
|
|
|
|
+GO X 6.920 // Point 1
|
|
|
|
|
+ Y 3.410 //
|
|
|
|
|
+GO Z- 9.000 // Insert tool
|
|
|
|
|
+ARC // Point 2
|
|
|
|
|
+ XC= 6.920 //
|
|
|
|
|
+ YC= 1.500 //
|
|
|
|
|
+ a= 101.470 //
|
|
|
|
|
+GO X 5.740 // Point 3
|
|
|
|
|
+ Y- 2.280 //
|
|
|
|
|
+ARC // Point 4
|
|
|
|
|
+ XC= 7.120 //
|
|
|
|
|
+ YC=- 2.000 //
|
|
|
|
|
+ a= 79.530 //
|
|
|
|
|
+SUB RETURN //
|
|
|
|
|
+
|
|
|
|
|
+SUB 11 // Right Side (located on line 100)
|
|
|
|
|
+GO X- 7.120 // Point 5
|
|
|
|
|
+ Y- 3.410 //
|
|
|
|
|
+ARC // Point 6
|
|
|
|
|
+ XC=- 7.120 //
|
|
|
|
|
+ YC=- 2.000 //
|
|
|
|
|
+ a= 79.530 //
|
|
|
|
|
+GO X- 5.050 // Point 7
|
|
|
|
|
+ Y 1.120 //
|
|
|
|
|
+ARC // Point 8
|
|
|
|
|
+ XC=- 6.920 //
|
|
|
|
|
+ YC= 1.500 //
|
|
|
|
|
+ a= 101.470 //
|
|
|
|
|
+SUB RETURN //
|
|
|
|
|
+ </textarea>
|
|
|
|
|
+ <br>
|
|
|
|
|
+ <input name="submit" type="button" value="Click To Run Simulator" onclick="startSimulation();">
|
|
|
|
|
+ <input id="pause_input" type="button" value="Pause" onclick="pause();">
|
|
|
|
|
+ </td>
|
|
|
|
|
+ </tr>
|
|
|
|
|
+ </table>
|
|
|
|
|
+ </form>
|
|
|
|
|
+ </td>
|
|
|
|
|
+ <td style="vertical-align:top">
|
|
|
|
|
+ <canvas id="cnc_canvas" width="780" height="650" style="border:1px solid black"></canvas>
|
|
|
|
|
+ </td>
|
|
|
|
|
+ </tr>
|
|
|
|
|
+</table>
|
|
|
|
|
+<hr>
|
|
|
|
|
+<div class="output_wrapper">
|
|
|
|
|
+ <b>Output:</b>
|
|
|
|
|
+ <pre id="output"></pre>
|
|
|
|
|
+</div>
|
|
|
|
|
+<script type="text/javascript">
|
|
|
|
|
+// The so-called Prototype Dollar function, a shortcut for document.getElementById()
|
|
|
|
|
+function $() {
|
|
|
|
|
+ var elements = new Array();
|
|
|
|
|
+ for (var i = 0; i < arguments.length; i++) {
|
|
|
|
|
+ var element = arguments[i];
|
|
|
|
|
+ if (typeof element == "string")
|
|
|
|
|
+ element = document.getElementById(element);
|
|
|
|
|
+ if (arguments.length == 1)
|
|
|
|
|
+ return element;
|
|
|
|
|
+ elements.push(element);
|
|
|
|
|
+ }
|
|
|
|
|
+ return elements;
|
|
|
|
|
+}
|
|
|
|
|
+ctx = null;
|
|
|
|
|
+canvas = null;
|
|
|
|
|
+function clear_vars() {// Global variables
|
|
|
|
|
+ unit = "MM"; // or "IN"
|
|
|
|
|
+ time_step = 1; // update every half second.
|
|
|
|
|
+ ppi = 96; // Pixels per inch
|
|
|
|
|
+ cf = 1; // Pixel to unit ratio
|
|
|
|
|
+ td = 1; // Tool diameter
|
|
|
|
|
+ max_frx = 1; // Maximum feed rate X
|
|
|
|
|
+ max_fry = 1; // Maximum feed rate Y
|
|
|
|
|
+ max_frz = 1; // Maximum feed rate Z
|
|
|
|
|
+ x_inc = 1; // Distance to move per tick
|
|
|
|
|
+ y_inc = 1; // Distance to move per tick
|
|
|
|
|
+ z_inc = 1; // Distance to move per tick
|
|
|
|
|
+ a_inc = 1; // Distance to move per tick
|
|
|
|
|
+ mar_x = 10; // Horizontal margin of origin
|
|
|
|
|
+ mar_y = 10; // Vertical margin of origin
|
|
|
|
|
+ tool_c = 2; // Clearance height.
|
|
|
|
|
+ tool_x = 0; // Tool position X
|
|
|
|
|
+ tool_y = 0; // Tool position Y
|
|
|
|
|
+ tool_z = 0; // Tool position Z
|
|
|
|
|
+ tool_a = 0; // Tool angle (used for arcs)
|
|
|
|
|
+ arc_r = 0; // Radius of currently drawn arc.
|
|
|
|
|
+ arc_dir = false; // Counter-clockwise
|
|
|
|
|
+ old_x = 0; // Previous tool position X
|
|
|
|
|
+ old_y = 0; // Previous tool position Y
|
|
|
|
|
+ old_z = 0; // Previous tool position Z
|
|
|
|
|
+ old_a = 0; // Previous tool angle (used for arcs)
|
|
|
|
|
+ goal_x = 0; // Destination coordinate
|
|
|
|
|
+ goal_y = 0; // Destination coordinate
|
|
|
|
|
+ goal_z = 0; // Destination coordinate
|
|
|
|
|
+ xc = 0; // Used for drawing arcs
|
|
|
|
|
+ yc = 0; // Used for drawing arcs
|
|
|
|
|
+ ac = 0; // Used for drawing arcs
|
|
|
|
|
+ time = 0; // Time needed to execute cut.
|
|
|
|
|
+ time_x = 0;
|
|
|
|
|
+ time_y = 0;
|
|
|
|
|
+ time_z = 0;
|
|
|
|
|
+ time_a = 0; // Used for arcs
|
|
|
|
|
+ time_elapsed = 0;
|
|
|
|
|
+ n = 0; // Line index
|
|
|
|
|
+ lines = []; // Array of each line of code
|
|
|
|
|
+ line = ""; // Current line
|
|
|
|
|
+ moving = false; // Set to true when the tool is moving from one point to another.
|
|
|
|
|
+ arcing = false; // Set to true when the tool is arcing.
|
|
|
|
|
+ interval_id = 0; // Variable needed to stop the timer during real-time mode
|
|
|
|
|
+ blitz_done = false; // Variable used to stop the timer in blitz mode.
|
|
|
|
|
+ paused = false; // Pause/unpaused state
|
|
|
|
|
+ speed = 1;
|
|
|
|
|
+ stack = []; // Line numbers to return to
|
|
|
|
|
+ canvas_stack = 0; // How many times restore() the canvas
|
|
|
|
|
+ canvas = $("cnc_canvas");
|
|
|
|
|
+ ctx = canvas.getContext("2d");
|
|
|
|
|
+}
|
|
|
|
|
+function startSimulation() {
|
|
|
|
|
+ // Clean up variables from the last run.
|
|
|
|
|
+ clear_vars();
|
|
|
|
|
+ // Clear debug output
|
|
|
|
|
+ $("output").innerHTML = "";
|
|
|
|
|
+ // Because the previous run might not have finished
|
|
|
|
|
+ for (var i = 0; i < 50; i++) {
|
|
|
|
|
+ ctx.restore();
|
|
|
|
|
+ }
|
|
|
|
|
+ // Stop the clock
|
|
|
|
|
+ stop();
|
|
|
|
|
+ // Since there is no "clear canvas" command.
|
|
|
|
|
+ ctx.fillStyle = "white"
|
|
|
|
|
+ ctx.fillRect(0, 0, canvas.width, canvas.height);
|
|
|
|
|
+ ctx.strokeStyle = "black";
|
|
|
|
|
+ /*
|
|
|
|
|
+ The canvas has it's origin in the top-left corner, but the CNC
|
|
|
|
|
+ Machine has it's origin in the bottom-left corner. Therefore,
|
|
|
|
|
+ we translate the origin and apply a transformation matrix to
|
|
|
|
|
+ flip the Y direction.
|
|
|
|
|
+ */
|
|
|
|
|
+ ctx.save();
|
|
|
|
|
+ canvas_stack = canvas_stack + 1;
|
|
|
|
|
+ ctx.transform(1, 0, 0, -1, 0, 0);
|
|
|
|
|
+ mar_x = parseFloat($("offset_x").value);
|
|
|
|
|
+ mar_y = parseFloat($("offset_y").value);
|
|
|
|
|
+ ctx.translate(mar_x, mar_y-canvas.height);
|
|
|
|
|
+ ctx.save();
|
|
|
|
|
+ canvas_stack = canvas_stack + 1;
|
|
|
|
|
+
|
|
|
|
|
+ lines = $("code_input").value.split("\n"); //str.split("\n");
|
|
|
|
|
+ ppi = parseFloat($("ppi_input").value);
|
|
|
|
|
+ // Start the animation sequence
|
|
|
|
|
+ n = -1;
|
|
|
|
|
+ debugn("There are " + lines.length + " lines");
|
|
|
|
|
+ // Stop all animations
|
|
|
|
|
+ window.clearInterval(interval_id);
|
|
|
|
|
+ paused = true;
|
|
|
|
|
+ pause();
|
|
|
|
|
+}
|
|
|
|
|
+function pause() {
|
|
|
|
|
+ if (paused) {
|
|
|
|
|
+ paused = false;
|
|
|
|
|
+ // Set the speed
|
|
|
|
|
+ speed = parseFloat($("speed_input").value);
|
|
|
|
|
+ time_step = parseFloat($("time_step_input").value);
|
|
|
|
|
+ // Start the CNC simulation.
|
|
|
|
|
+ if ($("blitz_input").checked) {
|
|
|
|
|
+ while (blitz_done == false) {
|
|
|
|
|
+ tick();
|
|
|
|
|
+ }
|
|
|
|
|
+ } else {
|
|
|
|
|
+ interval_id = window.setInterval(tick,1000*time_step/speed);
|
|
|
|
|
+ }
|
|
|
|
|
+ $("pause_input").value = "Pause";
|
|
|
|
|
+ } else {
|
|
|
|
|
+ paused = true;
|
|
|
|
|
+ window.clearInterval(interval_id);
|
|
|
|
|
+ $("pause_input").value = "Resume";
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+function nextLine() {
|
|
|
|
|
+ n = n + 1;
|
|
|
|
|
+ if (n >= lines.length) {
|
|
|
|
|
+ return false;
|
|
|
|
|
+ } else {
|
|
|
|
|
+ line = lines[n];
|
|
|
|
|
+ return true;
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+function startsWith(s1, s2) {
|
|
|
|
|
+ if (s1.substr(0,s2.length) == s2) {
|
|
|
|
|
+ return true;
|
|
|
|
|
+ } else {
|
|
|
|
|
+ return false;
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+function deg2rad(d) {
|
|
|
|
|
+ return (Math.PI/180)*d;
|
|
|
|
|
+}
|
|
|
|
|
+function rad2deg(r) {
|
|
|
|
|
+ return (180/Math.PI)*r;
|
|
|
|
|
+}
|
|
|
|
|
+function tick() {
|
|
|
|
|
+ if (moving) {
|
|
|
|
|
+ // Store previous position
|
|
|
|
|
+ old_x = tool_x;
|
|
|
|
|
+ old_y = tool_y;
|
|
|
|
|
+ old_z = tool_z;
|
|
|
|
|
+ old_a = tool_a;
|
|
|
|
|
+ // Figure out new position
|
|
|
|
|
+ if (time < time_step) {
|
|
|
|
|
+ if (arcing) {
|
|
|
|
|
+ tool_a = goal_a;
|
|
|
|
|
+ tool_x = xc + Math.cos(tool_a)*arc_r;
|
|
|
|
|
+ tool_y = yc + Math.sin(tool_a)*arc_r;
|
|
|
|
|
+ } else {
|
|
|
|
|
+ tool_x = goal_x;
|
|
|
|
|
+ tool_y = goal_y;
|
|
|
|
|
+ tool_z = goal_z;
|
|
|
|
|
+ }
|
|
|
|
|
+ time_elapsed = time_elapsed + time;
|
|
|
|
|
+ } else {
|
|
|
|
|
+ if (arcing) {
|
|
|
|
|
+ tool_a = tool_a + a_inc;
|
|
|
|
|
+ tool_x = xc + Math.cos(tool_a)*arc_r;
|
|
|
|
|
+ tool_y = yc + Math.sin(tool_a)*arc_r;
|
|
|
|
|
+ } else {
|
|
|
|
|
+ tool_x = tool_x + x_inc;
|
|
|
|
|
+ tool_y = tool_y + y_inc;
|
|
|
|
|
+ tool_z = tool_z + z_inc;
|
|
|
|
|
+ }
|
|
|
|
|
+ time_elapsed = time_elapsed + time_step;
|
|
|
|
|
+ }
|
|
|
|
|
+ // Update display
|
|
|
|
|
+ $("x_disp").innerHTML = "X: " + tool_x;
|
|
|
|
|
+ $("y_disp").innerHTML = "Y: " + tool_y;
|
|
|
|
|
+ $("z_disp").innerHTML = "Z: " + tool_z;
|
|
|
|
|
+ $("time_disp").innerHTML = "Time: " + time_elapsed;
|
|
|
|
|
+
|
|
|
|
|
+ // Draw line if tool is below surface.
|
|
|
|
|
+ if (tool_z < 0) {
|
|
|
|
|
+ with (ctx) {
|
|
|
|
|
+ // Draw hole
|
|
|
|
|
+ beginPath();
|
|
|
|
|
+ fillStyle = "black";
|
|
|
|
|
+ arc(tool_x, tool_y, td/2.0, 0, Math.PI*2.0, true);
|
|
|
|
|
+ fill();
|
|
|
|
|
+ // Draw line
|
|
|
|
|
+ lineCap = "round";
|
|
|
|
|
+ lineWidth = td;
|
|
|
|
|
+ beginPath();
|
|
|
|
|
+ strokeStyle = "black";
|
|
|
|
|
+ if (arcing) {
|
|
|
|
|
+ arc(xc, yc, arc_r, old_a, tool_a, arc_dir);
|
|
|
|
|
+ } else {
|
|
|
|
|
+ moveTo(old_x, old_y);
|
|
|
|
|
+ lineTo((tool_x), (tool_y));
|
|
|
|
|
+ }
|
|
|
|
|
+ stroke();
|
|
|
|
|
+ }
|
|
|
|
|
+ } else {
|
|
|
|
|
+ // Draw hollow dot (so we can see the path of the tool)
|
|
|
|
|
+ with (ctx) {
|
|
|
|
|
+ lineWidth = 1/cf;
|
|
|
|
|
+ strokeStyle = "blue";
|
|
|
|
|
+ strokeRect((tool_x)-1/cf, (tool_y) -1/cf, 3/cf, 3/cf);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ // Decrease time
|
|
|
|
|
+ time = time - time_step;
|
|
|
|
|
+ // Finish line
|
|
|
|
|
+ if (time < 0) {
|
|
|
|
|
+ time = 0;
|
|
|
|
|
+ moving = false;
|
|
|
|
|
+ arcing = false;
|
|
|
|
|
+ }
|
|
|
|
|
+ debug(".");
|
|
|
|
|
+ } else {
|
|
|
|
|
+ // Always safest to put this first, in case of unhandled syntax
|
|
|
|
|
+ // errors later on in the code.
|
|
|
|
|
+ if (nextLine()) {
|
|
|
|
|
+ ndebug("Line " + n + ": ");
|
|
|
|
|
+ } else {
|
|
|
|
|
+ ndebug("Ended because there are no more lines.");
|
|
|
|
|
+ n = -1;
|
|
|
|
|
+ ndebug("canvas_stack = " + canvas_stack);
|
|
|
|
|
+ for (var i = 0; i < canvas_stack; i++) {
|
|
|
|
|
+ ndebug("ctx.restore()");
|
|
|
|
|
+ ctx.restore();
|
|
|
|
|
+ }
|
|
|
|
|
+ blitz_done = true;
|
|
|
|
|
+ window.clearInterval(interval_id);
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ //debug(line);
|
|
|
|
|
+ if (startsWith(line, "START")) {
|
|
|
|
|
+ // Set unit
|
|
|
|
|
+ unit = line.substr(6,2);
|
|
|
|
|
+ debugd("Unit: " + unit);
|
|
|
|
|
+ if (unit == "MM") {
|
|
|
|
|
+ debugd("Millimeters");
|
|
|
|
|
+ cf = ppi / 25.4;
|
|
|
|
|
+ } else if (unit == "IN") {
|
|
|
|
|
+ debugd("Inches");
|
|
|
|
|
+ cf = ppi;
|
|
|
|
|
+ } else {
|
|
|
|
|
+ ndebug("Invalid unit");
|
|
|
|
|
+ n = lines.length;
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ debug("Scale: " + cf);
|
|
|
|
|
+ ctx.scale(cf, cf);
|
|
|
|
|
+ } else if (startsWith(line, " TD=")) {
|
|
|
|
|
+ // Set tool diameter
|
|
|
|
|
+ debugd("Tool Diameter: " + line.substring(4,12));
|
|
|
|
|
+ td = parseFloat(line.substring(4,12));
|
|
|
|
|
+ ctx.lineWidth = td;
|
|
|
|
|
+ debug("ctx.lineWidth = " + ctx.lineWidth);
|
|
|
|
|
+ } else if (startsWith(line, "FR")) {
|
|
|
|
|
+ // TODO: set feed rate
|
|
|
|
|
+ debugd("Setting feed rate for " + line.substr(3,3) + " to " + line.substring(8,12));
|
|
|
|
|
+ if (line.indexOf("X") != -1) {
|
|
|
|
|
+ max_frx = parseFloat(line.substring(8,12));
|
|
|
|
|
+ debug(" set FR X = " + max_frx);
|
|
|
|
|
+ }
|
|
|
|
|
+ if (line.indexOf("Y") != -1) {
|
|
|
|
|
+ max_fry = parseFloat(line.substring(8,12));
|
|
|
|
|
+ debug(" set FR Y = " + max_fry);
|
|
|
|
|
+ }
|
|
|
|
|
+ if (line.indexOf("Z") != -1) {
|
|
|
|
|
+ max_frz = parseFloat(line.substring(8,12));
|
|
|
|
|
+ debug(" set FR Z = " + max_frz);
|
|
|
|
|
+ }
|
|
|
|
|
+ } else if (startsWith(line, "SETUP")) {
|
|
|
|
|
+ // This step doesn't translate well, and would get annoying
|
|
|
|
|
+ debug("Setup: " + line.substring(7,12));
|
|
|
|
|
+ debug(" Note: in real life, this command would let you position the tool at a new 'zero' position.");
|
|
|
|
|
+ } else if (startsWith(line, "Z>C")) {
|
|
|
|
|
+ tool_z = tool_c;
|
|
|
|
|
+ debug("Z>C: Z = " + String(tool_c));
|
|
|
|
|
+ } else if (startsWith(line, "GO")) {
|
|
|
|
|
+ goal_x = tool_x; time_x = 0;
|
|
|
|
|
+ goal_y = tool_y; time_y = 0;
|
|
|
|
|
+ goal_z = tool_z; time_z = 0;
|
|
|
|
|
+ debugd("Go");
|
|
|
|
|
+ do {
|
|
|
|
|
+ if (line.substr(3,1) == "f") {
|
|
|
|
|
+ // TODO: how fast is "fast" anyway?
|
|
|
|
|
+ }
|
|
|
|
|
+ if (line.substr(3,1) == "X") {
|
|
|
|
|
+ goal_x = parseFloat(line.substring(5,12));
|
|
|
|
|
+ if (line.substr(4,1) == "-") {
|
|
|
|
|
+ goal_x = -goal_x;
|
|
|
|
|
+ }
|
|
|
|
|
+ time_x = Math.abs((goal_x - tool_x) / max_frx);
|
|
|
|
|
+ debugd("X coor: " + goal_x);
|
|
|
|
|
+ } else if (line.substr(3,1) == "Y") {
|
|
|
|
|
+ goal_y = parseFloat(line.substring(5,12));
|
|
|
|
|
+ if (line.substr(4,1) == "-") {
|
|
|
|
|
+ goal_y = -goal_y;
|
|
|
|
|
+ }
|
|
|
|
|
+ time_y = Math.abs((goal_y - tool_y) / max_fry);
|
|
|
|
|
+ debugd("Y coor: " + goal_y);
|
|
|
|
|
+ } else if (line.substr(3,1) == "Z") {
|
|
|
|
|
+ goal_z = parseFloat(line.substring(5,12));
|
|
|
|
|
+ if (line.substr(4,1) == "-") {
|
|
|
|
|
+ goal_z = -goal_z;
|
|
|
|
|
+ }
|
|
|
|
|
+ time_z = Math.abs((goal_z - tool_z) / max_frz);
|
|
|
|
|
+ debugd("Z coor: " + goal_z);
|
|
|
|
|
+ }
|
|
|
|
|
+ n = n+1;
|
|
|
|
|
+ if (n >= lines.length) {
|
|
|
|
|
+ break;
|
|
|
|
|
+ }
|
|
|
|
|
+ line = lines[n];
|
|
|
|
|
+ } while (line.substr(0,2) == " ")
|
|
|
|
|
+ n = n - 1;
|
|
|
|
|
+ // Compute how many seconds it will take to execute this cut
|
|
|
|
|
+ time = Math.max(Math.max(time_x, time_y), time_z);
|
|
|
|
|
+ debugd("Time Required: "+time);
|
|
|
|
|
+ x_inc = (goal_x - tool_x) / time * time_step;
|
|
|
|
|
+ y_inc = (goal_y - tool_y) / time * time_step;
|
|
|
|
|
+ z_inc = (goal_z - tool_z) / time * time_step;
|
|
|
|
|
+ debug("X inc: " + x_inc + " Y inc: " + y_inc +" Z inc: " + z_inc);
|
|
|
|
|
+ moving = true;
|
|
|
|
|
+ } else if (startsWith(line, "ZERO AT")) {
|
|
|
|
|
+ // Translate the canvas origin
|
|
|
|
|
+ debugd("ZERO AT");
|
|
|
|
|
+ goal_x = 0; time_x = 0;
|
|
|
|
|
+ goal_y = 0; time_y = 0;
|
|
|
|
|
+ goal_z = 0; time_z = 0;
|
|
|
|
|
+ if (!nextLine()) { return; }
|
|
|
|
|
+ while (line.substr(0,2) == " ") {
|
|
|
|
|
+ if (line.substr(3,1) == "X") {
|
|
|
|
|
+ goal_x = parseFloat(line.substring(5,12));
|
|
|
|
|
+ if (line.substr(4,1) == "-") {
|
|
|
|
|
+ goal_x = -goal_x;
|
|
|
|
|
+ }
|
|
|
|
|
+ debug(" X coor: " + goal_x);
|
|
|
|
|
+ } else if (line.substr(3,1) == "Y") {
|
|
|
|
|
+ goal_y = parseFloat(line.substring(5,12));
|
|
|
|
|
+ if (line.substr(4,1) == "-") {
|
|
|
|
|
+ goal_y = -goal_y;
|
|
|
|
|
+ }
|
|
|
|
|
+ debug(" Y coor: " + goal_y);
|
|
|
|
|
+ } else if (line.substr(3,1) == "Z") {
|
|
|
|
|
+ goal_z = parseFloat(line.substring(5,12));
|
|
|
|
|
+ if (line.substr(4,1) == "-") {
|
|
|
|
|
+ goal_z = -goal_z;
|
|
|
|
|
+ }
|
|
|
|
|
+ debug(" Z coor: " + goal_z);
|
|
|
|
|
+ }
|
|
|
|
|
+ if (!nextLine()) { break; }
|
|
|
|
|
+ }
|
|
|
|
|
+ n = n - 1;
|
|
|
|
|
+ // Translate the canvas origin
|
|
|
|
|
+ ctx.save();
|
|
|
|
|
+ ctx.translate(goal_x, goal_y);
|
|
|
|
|
+ canvas_stack = canvas_stack + 1;
|
|
|
|
|
+ // Untranslate tool
|
|
|
|
|
+ tool_x = tool_x - goal_x;
|
|
|
|
|
+ tool_y = tool_y - goal_y;
|
|
|
|
|
+ } else if (startsWith(line, "ARC")) {
|
|
|
|
|
+ debugd("ARC");
|
|
|
|
|
+ if (!nextLine()) { return; }
|
|
|
|
|
+ if (line.substr(0,4) == " XC=") {
|
|
|
|
|
+ xc = parseFloat(line.substring(5,12));
|
|
|
|
|
+ if (line.substr(4,1) == "-") {
|
|
|
|
|
+ xc = -xc;
|
|
|
|
|
+ }
|
|
|
|
|
+ debug(" XC: " + xc);
|
|
|
|
|
+ } else {
|
|
|
|
|
+ debug("I really expected a line starting with ' XC='");
|
|
|
|
|
+ }
|
|
|
|
|
+ if (!nextLine()) { return; }
|
|
|
|
|
+ if (line.substr(0,4) == " YC=") {
|
|
|
|
|
+ yc = parseFloat(line.substring(5,12));
|
|
|
|
|
+ if (line.substr(4,1) == "-") {
|
|
|
|
|
+ yc = -yc;
|
|
|
|
|
+ }
|
|
|
|
|
+ debug(" YC: " + yc);
|
|
|
|
|
+ } else {
|
|
|
|
|
+ debug("I really expected a line starting with ' YC='");
|
|
|
|
|
+ }
|
|
|
|
|
+ if (!nextLine()) { return; }
|
|
|
|
|
+ if (line.substr(0,4) == " a=") {
|
|
|
|
|
+ // I call the variable 'ac' for resemblance
|
|
|
|
|
+ ac = parseFloat(line.substring(5,12));
|
|
|
|
|
+ if (line.substr(4,1) == "-") {
|
|
|
|
|
+ ac = -ac;
|
|
|
|
|
+ arc_dir = true;
|
|
|
|
|
+ } else {
|
|
|
|
|
+ arc_dir = false;
|
|
|
|
|
+ }
|
|
|
|
|
+ debugd(" AC: " + ac);
|
|
|
|
|
+ ac = deg2rad(ac);
|
|
|
|
|
+ debugd("in radians: " + ac);
|
|
|
|
|
+ } else {
|
|
|
|
|
+ debug("I really expected a line starting with ' a='");
|
|
|
|
|
+ }
|
|
|
|
|
+ arc_r = Math.sqrt( Math.pow(xc-tool_x, 2) +
|
|
|
|
|
+ Math.pow(yc-tool_y, 2) );
|
|
|
|
|
+ debugd("R = " + arc_r);
|
|
|
|
|
+ var dist = Math.abs(arc_r*ac);
|
|
|
|
|
+ var xyfr = (max_frx + max_fry)/2;
|
|
|
|
|
+ time = dist/xyfr;
|
|
|
|
|
+ debugd("Time Required: " + time);
|
|
|
|
|
+ a_inc = ac/time*time_step;
|
|
|
|
|
+ debugd("a_inc = " + a_inc);
|
|
|
|
|
+ // Math.atan2 seems to be measuring Clockwise from the Y
|
|
|
|
|
+ // axis. Ridiculous.
|
|
|
|
|
+ tool_a = Math.atan2(tool_y-yc, tool_x-xc);
|
|
|
|
|
+ debugd("tool_a = " + rad2deg(tool_a) + " degrees");
|
|
|
|
|
+ goal_a = Math.atan2(tool_y-yc, tool_x-xc) + ac;
|
|
|
|
|
+ debug("goal_a = " + rad2deg(goal_a) + " degrees");
|
|
|
|
|
+ moving = true;
|
|
|
|
|
+ arcing = true;
|
|
|
|
|
+ //arcing = true;
|
|
|
|
|
+ } else if (startsWith(line, "CALL SUB")) {
|
|
|
|
|
+ // Push line # onto stack
|
|
|
|
|
+ debugd("Calling Sub");
|
|
|
|
|
+ stack.push(n);
|
|
|
|
|
+ ctx.save();
|
|
|
|
|
+ canvas_stack = canvas_stack + 1;
|
|
|
|
|
+ // Get the SUB id
|
|
|
|
|
+ var sub_id = line.substring(9,12).replace(" ","");
|
|
|
|
|
+ var sub_id = "SUB " + sub_id;
|
|
|
|
|
+ debugd("Scanning for '" + sub_id +"'");
|
|
|
|
|
+ // Move the cursor location to the start of the subroutine.
|
|
|
|
|
+ n = -1;
|
|
|
|
|
+ while (n < lines.length) {
|
|
|
|
|
+ n = n + 1;
|
|
|
|
|
+ line = lines[n];
|
|
|
|
|
+ if (line.substr(0,sub_id.length) == sub_id) {
|
|
|
|
|
+ debugd("Found '" + sub_id + "' on line " + String(n));
|
|
|
|
|
+ debug("Stack: " + String(stack));
|
|
|
|
|
+ break;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ } else if (startsWith(line, "SUB RETURN")) {
|
|
|
|
|
+ // Return to our line # in the stack.
|
|
|
|
|
+ n = stack.pop();
|
|
|
|
|
+ ctx.restore();
|
|
|
|
|
+ canvas_stack = canvas_stack - 1;
|
|
|
|
|
+ debugd("SUB RETURN return to line " + String(n));
|
|
|
|
|
+ debug("Stack: " + String(stack));
|
|
|
|
|
+ } else if (startsWith(line, "REPEAT END")) {
|
|
|
|
|
+ if (stack.length > 0) {
|
|
|
|
|
+ n = stack.pop();
|
|
|
|
|
+ debug("REPEAT END return to line " + String(n));
|
|
|
|
|
+ } else {
|
|
|
|
|
+ debug("Passing REPEAT END");
|
|
|
|
|
+ }
|
|
|
|
|
+ } else if (startsWith(line, "REPEAT")) {
|
|
|
|
|
+ var rep_num = parseInt(line.substring(10,12))
|
|
|
|
|
+ debugd("REPEAT " + String(rep_num) + " times");
|
|
|
|
|
+ for (var i = 1; i < rep_num; i++) {
|
|
|
|
|
+ stack.push(n);
|
|
|
|
|
+ }
|
|
|
|
|
+ debug("Stack: " + String(stack));
|
|
|
|
|
+ } else if (startsWith(line, "END")) {
|
|
|
|
|
+ n = lines.length;
|
|
|
|
|
+ debug("Encountered an END");
|
|
|
|
|
+ } else {
|
|
|
|
|
+ debug("Skipping line: " + line);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+/*
|
|
|
|
|
+These are placed at the end, so if prior code has syntax errors,
|
|
|
|
|
+the page will clearly be broken.
|
|
|
|
|
+*/
|
|
|
|
|
+function debug(str) {
|
|
|
|
|
+ var obj;
|
|
|
|
|
+ obj = $("output");
|
|
|
|
|
+ obj.innerHTML = obj.innerHTML + str;
|
|
|
|
|
+}
|
|
|
|
|
+function ndebug(str) {
|
|
|
|
|
+ var obj;
|
|
|
|
|
+ obj = $("output");
|
|
|
|
|
+ obj.innerHTML = obj.innerHTML + "\n" + str;
|
|
|
|
|
+}
|
|
|
|
|
+function debugn(str) {
|
|
|
|
|
+ var obj;
|
|
|
|
|
+ obj = $("output");
|
|
|
|
|
+ obj.innerHTML = obj.innerHTML + str + "\n";
|
|
|
|
|
+}
|
|
|
|
|
+function ndebugn(str) {
|
|
|
|
|
+ var obj;
|
|
|
|
|
+ obj = $("output");
|
|
|
|
|
+ obj.innerHTML = obj.innerHTML + "\n" + str + "\n";
|
|
|
|
|
+}
|
|
|
|
|
+function debugd(str) {
|
|
|
|
|
+ var obj;
|
|
|
|
|
+ obj = $("output");
|
|
|
|
|
+ obj.innerHTML = obj.innerHTML + str + "... ";
|
|
|
|
|
+}
|
|
|
|
|
+</script>
|
|
|
|
|
+<footer>
|
|
|
|
|
+<hr>
|
|
|
|
|
+<p style="text-align: center; font-style:italic"><a href="http://validator.w3.org/#validate-by-upload">Freaking valid HTML5 baby!!!</a></p>
|
|
|
|
|
+</footer>
|
|
|
|
|
+</body>
|
|
|
|
|
+</html>
|