text: <!--    Name:        GCode_Conic_Spiral.hta
          Version:     2020-03-12
          Function:    Milling a conical spiral 
  		
  		2020-03-08 Initial version
  		2020-03-12 update 
  		
  Enabled Active X in the broswer security settings.
  In IE9:
  a) Go to Tools ->Internet Options
  b) Select security tab
  c) Click on Trusted Sites (or Local Intranet depending on whether your site is trusted or not)
  d) Click on Custom Level
  e) Ensure that "Initialize and script active x controls is not marked safe for scripting" is enabled.		
  -->
  
  <html>
  <head>
  <hta:application id="hta" 
      applicationname="GCode_Conic_Spiral"
      caption="yes" 
      contextmenu="yes"
      innerborder="no"
      navigable="yes" 
      scroll="no"
      scrollflat="yes" 
      selection="yes"
      singleinstance="yes"
      sysmenu="yes" 
      windowState="normal" >
  
  <meta http-equiv="content-type" content="text/html; charset=ISO-8859-1">
  <title>GCode Conic Spiral</title>
  <script type="text/javascript" src="_script_functions.js"></script>
  
  <script type="text/javascript">
  <!--
  
  var d_inside = 15;		// inner diameter
  var d_outside= 100;		// outer diameter
  var d_cutter = 2.2;		// cutter diameter
  var cutter_overlap = 0.9;
  var inc_radius = 6;		// increase of the radius from turn to turn
  var z_save =  2;		// save Z height
  var z_turn = -2;		// Z increment per turn
  var header = "G21 ";	// use mm
  var footer = "M30 ";	// program end
  var dir_cw = false;		// clock wise direction?
  
  var finalGCode = "";
  var headerCode = ""
  var copyCode = "";
  var lineCounter= 0;
  var last_gnr=0;		// needed in write_GCodeLine_XY
  
  function OnLoad()
  {   var xx=560;
      var yy=860;
      try { window.resizeTo(xx,yy);}         catch(err){}
      try { window.moveTo(screen.width - xx, 0);}     catch(err){}
  }
  
  var startTime = new Date();
  function start_OnClick()
  {   html_info.innerText = "";
  	startTime = new Date();
  
  	read_GRBL_Plotter_Settings();	// in _script_functions.js
  	read_SettingsFromForm();
  	create_GCode();
  
  	var endTime = new Date();
  	var timeDiff = (endTime - startTime)/1000; //in s
  
  	headerCode += "( GCode lines :"+lineCounter+" )\r\n";
  	headerCode += "( Elapsed time:"+timeDiff+" )\r\n";
  
  	copyCode = headerCode + finalGCode;
  	html_info.innerHTML = "<b><font color='red'>It takes time to copy large amounts of text to the clipboard</font></b>";
  	codeForm.result.value = copyCode;
  	setTimeout(copy_content,500);	// delayed copy to force <textarea> to show content
  
  //	copyTextToClipboard(finalGCode) // in _script_functions.js
  //	set_GRBL_Plotter_Update();		// in _script_functions.js
  }
  function copy_content()
  {
  //    copyTextToClipboard(copyCode);		// in _script_functions.js
  	codeForm.result.select();
  	document.execCommand('copy');
  	set_GRBL_Plotter_Update();		// in _script_functions.js
  	
  	endTime = new Date();
  	timeDiff = (endTime - startTime)/1000; //in s
  	codeForm.result.value += "( Elapsed time:"+timeDiff+" after copy to clipboard )\r\n";
  }
  
  function create_GCode()
  {	headerCode  = "( GCode Conic Spiral )\r\n";
  	headerCode += "( by GRBL-Plotter )\r\n";
  	finalGCode  = header + "(internal header data)\r\n";
  	finalGCode += GRBL_Plt_header + "(header from GRBL-Plotter)\r\n";
  	lineCounter= 5;
  
  	var angle_step = 1*Math.PI/180;						// initial angle step = 1 degree
  	var r_step  = inc_radius*angle_step/(2*Math.PI);	// initial radius step
  	var z_step  = z_turn*angle_step/(2*Math.PI);		// z radius step
  	var r_cut   = d_cutter/2;
  	var r_start = d_inside/2 + r_cut;					// start radius
  	var r_end   = d_outside/2+ r_cut;					// end radius
  	var a,r,z;
  
  	a = 0;
  	r = r_start;
  	z = z_turn;
  
  	finalGCode += PenUp();
  	finalGCode += "M3 S" + GRBL_Plt_spindle_speed + " (Spindle on)\r\n";
  	finalGCode += "G4 P" + GRBL_Plt_spindle_delay + " (Delay)\r\n";
  
  	finalGCode += write_GCodeLine_XY(0, r, 0);
  	
  	var turn_complete = true;
  	var figureStart = false;
  	var turns = 0;
  	var singlePass = (d_cutter > inc_radius);
  	var expectedTurns = Math.ceil((r_end - r_start) / inc_radius);
  
  	while (r < r_end)
  	{	
  		if (turn_complete)
  		{	if (figureStart)
  				finalGCode += "(</Revolution >)\r\n";
  
  			if (codeForm.clearance.checked)
  			{
  			    finalGCode += "(<Clearance " + turns + " / " + expectedTurns + " Z" + z.toFixed(GRBL_Plt_decimals) + ">)\r\n";
  			    if ((r + inc_radius) < (r_end + (r_cut/2 * expectedTurns)))
  			    {
  			        finalGCode += write_GCodeLine_XY(0, r + inc_radius, a);
  			       	finalGCode += PenDown(z);
  			        finalGCode += clear_OuterArea(r + inc_radius, r_end + (r_cut/2 * expectedTurns--), d_cutter * cutter_overlap, a);
  		        	finalGCode += PenUp();
  			        finalGCode += write_GCodeLine_XY(0, r, a);							// Move to start position
  			        if (singlePass)                                 // Z = save
  			            	finalGCode += PenDown(z);
  
  			    }
  				finalGCode += "(</Clearance >)\r\n";
  			}
  
  			finalGCode += "(<Revolution "+turns+" >)\r\n";
  			figureStart = true;
  			turns++;
  			turn_complete = false;
  		}
  		
  		
  		if (singlePass)			// if cutter diameter is >= radius-increment - just one pass is needed
  		{	finalGCode += write_GCodeLine_XYZ(1, r, a, z);	// calculate new XYZ depending on angle
  		
  			angle_step = Math.asin(1/r);					// calculate next step width (the larger the radius, the smaller the angle must be)
  			r_step  = inc_radius*angle_step/(2*Math.PI);
  			z_step  = z_turn * angle_step/(2*Math.PI);
  		
  			a += angle_step;								// apply next delta steps
  			r += r_step;
  			z += z_step;
  		}
  		else
  		{	for (i=0; i<inc_radius; i+=(d_cutter * cutter_overlap))
  			{	if ((r+i) < r_end)
  				{	finalGCode += "(<Pass "+i.toFixed(GRBL_Plt_decimals)+" >)\r\n";
  					finalGCode += write_GCodeLine_XY(0, r + i, a);					
  					finalGCode += PenDown(z);
  					finalGCode += do_ConicSpiralOnTurn(r + i, r_end, z);
  					finalGCode += PenUp();
  					finalGCode += "(</Pass >)\r\n";
  				}
  			}
  			z += z_turn;
  			r += inc_radius;
  			a += 2 * Math.PI;
  		}
  		
  		if ( a >= (turns * 2 * Math.PI))
  			turn_complete = true;
  	}
  	finalGCode += PenUp();
  	finalGCode += "(</Revolution >)\r\n";
  	finalGCode += "M5 (Spindle off)\r\n";
  	finalGCode += GRBL_Plt_footer + "(footer from GRBL-Plotter)\r\n";;
  	finalGCode += footer + "(internal footer data)\r\n";;
  }
  
  function G0(x,y)
  {	last_gnr=0; lineCounter++;
  	return "G0 X" + x.toFixed(GRBL_Plt_decimals) + " Y" + y.toFixed(GRBL_Plt_decimals) + "\r\n";
  }
  function PenDown(z)
  {	last_gnr=0; lineCounter++;
  	return "G1 Z" + z.toFixed(GRBL_Plt_decimals) + " F" + GRBL_Plt_feedrate_z + "\r\n";		// Tool down
  }
  function PenUp()
  {	last_gnr=0; lineCounter++;
  	return "G0 Z" + z_save.toFixed(GRBL_Plt_decimals) + "\r\n";
  }
  function G1(x,y)
  {	last_gnr=1; lineCounter++;
  	return "G1 X" + x.toFixed(GRBL_Plt_decimals) + " Y" + y.toFixed(GRBL_Plt_decimals) + " F" + GRBL_Plt_feedrate_xy + "\r\n";
  }
  function G2(x,y,i,j)
  {	last_gnr=2; lineCounter++;
  	return "G2 X" + x.toFixed(GRBL_Plt_decimals) + " Y" + y.toFixed(GRBL_Plt_decimals) + " I" + i.toFixed(GRBL_Plt_decimals) + " J" + j.toFixed(GRBL_Plt_decimals) + " F" + GRBL_Plt_feedrate_xy + "\r\n";
  }
  
  function do_ConicSpiralOnTurn(r,r_end,z)
  {	var a=0;
  	var code ="";
  	var	angle_step = Math.asin(1/r);					// calculate next step width (the larger the radius, the smaller the angle must be)
  	var r_step  = inc_radius*angle_step/(2*Math.PI);	// initial radius step
  	code += write_GCodeLine_XYZ(1, r, a, z);		    // calculate new XYZ depending on angle
  	while (a <= 2 * Math.PI)
  	{	
  		angle_step = Math.asin(1/r);					// calculate next step width (the larger the radius, the smaller the angle must be)
  		r_step = inc_radius * angle_step / (2 * Math.PI);
  		z_step  = z_turn * angle_step/(2*Math.PI);
  		
  		a += angle_step;								// apply next delta steps
  		r += r_step;
  		z += z_step;	
  		
  		if (r > r_end)
  			break;
  		code += write_GCodeLine_XYZ(1, r, a, z);		// calculate new XYZ depending on angle
      }
  	return code;
  }
  
  // remove outer material after each turn
  function clear_OuterArea(r_start, r_end, inc_radius, a_start)
  {
  	var r = r_start;
  	var a_step = 1;
  	var a = a_start;
  	var nr = (180 * a_start/Math.PI);
  	var code = "";
  	while (r < r_end)
  	{   code += write_GCodeLine_XY(1, r, a);
  		a_step = Math.asin(3/r);
  		r_step = inc_radius*a_step/(2*Math.PI);
  		
  		a += a_step;
  		r += r_step;
  	}
  	code += "( add circle )\r\n";
  	for (b = a; b <= (a + (2 * Math.PI)) ; b += a_step)
  	{ code += write_GCodeLine_XY(1, r, b); }
  	return code;
  }
  
  var last_gnr=0;
  function write_GCodeLine_XY(gnr, r, a)
  {	var x1 = r * Math.cos(a);
  	var y1 = r * Math.sin(a);
  	if (dir_cw)
  		y1= -y1;
  	var gmode = "";
  	var feed = "";
  	if ((gnr == 1) && (gnr != last_gnr))
  	{	gmode = "G" + gnr + " ";
  		feed = " F" + GRBL_Plt_feedrate_xy;
  	}
  	last_gnr = gnr; lineCounter++;
  	return gmode + "X" + x1.toFixed(GRBL_Plt_decimals) + " Y" + y1.toFixed(GRBL_Plt_decimals) + feed + "\r\n";
  }
  function write_GCodeLine_XYZ(gnr, r, a, z)
  {	var x1 = r * Math.cos(a);
  	var y1 = r * Math.sin(a);
  	if (dir_cw)
  		y1= -y1;
  	var gmode = "";
  	var feed = "";
  	if ((gnr == 1) && (gnr != last_gnr))
  	{	gmode = "G" + gnr + " ";
  		feed = " F" + GRBL_Plt_feedrate_xy;
  	}
  	last_gnr = gnr; lineCounter++;
  	return gmode + "X" + x1.toFixed(GRBL_Plt_decimals) + " Y" + y1.toFixed(GRBL_Plt_decimals) + " Z" + z.toFixed(GRBL_Plt_decimals) + feed + "\r\n";
  }
  
  function read_SettingsFromForm()
  {	d_inside = parseFloat(codeForm.d_inner.value);
  	d_outside= parseFloat(codeForm.d_outer.value);
  	d_cutter = parseFloat(codeForm.d_cutter.value);
  	inc_radius = parseFloat(codeForm.r_inc.value);
  	z_save = parseFloat(codeForm.z_save.value);
  	z_turn = parseFloat(codeForm.z_inc.value);
  	dir_cw = codeForm.dir_cw.checked;
  }
  
  //-->
  </script>
  <style type="text/css">
  body {margin:1px; }
  div, td, textarea { font-size:12px;  }
  select, input { font-size:12px;  }
  </style>
  
  </head>
  <body bgcolor="#F0F0F0" OnLoad='OnLoad();' style="margin:0;">
  <basefont face="arial, verdana, sans-serif" >
  <form name="codeForm">
  
  <table width='100%'>
  <tr>
    <td valign="top"><table border>
      <tr><th colspan="2"><b>GCode Conic Spiral</b></th></tr>
  	<tr><td colspan="2">Mills a spiral from inner diameter to outer diameter with increasing z depth.<br>Following settings will be taken from GRBL-Plotter:<br>
  	decimal places, XY feedrate, Z feedrate, GCode header, GCode footer.<br>
  	GCode can be checked in 3D online on <a href="https://ncviewer.com/" target="_blank">https://ncviewer.com/</a><br>- click 'New File', 'Paste (ctrl-V)' and 'PLOT'.</td></tr>
      <tr><td colspan="2" ><b><font color="red">Usage of the script and the generated GCode is on you own risk!</font></b></td></tr>
  	<tr><td width="30%">Inner diameter</td><td><input type="text" name="d_inner" value="20" size="4" style="text-align:right;"></td></tr>
  	<tr><td>Outer diameter</td><td><input type="text" name="d_outer" value="50" size="4" style="text-align:right;"></td></tr>
  	<tr><td>Cutter diameter</td><td><input type="text" name="d_cutter" value="2.2" size="4" style="text-align:right;"></td></tr>
  	<tr><td>Radius increment per turn</td><td><input type="text" name="r_inc" value="5" size="4" style="text-align:right;"></td></tr>
  	<tr><td>Z increment per turn</td><td><input type="text" name="z_inc" value="-2" size="4" style="text-align:right;"></td></tr>
  	<tr><td>Z save</td><td><input type="text" name="z_save" value="2" size="4" style="text-align:right;"></td></tr>
  	<tr><td>Option</td><td><input type="checkbox" id="dir_cw" name="dir_cw" value=""> check for clock wise direction</td></tr>
  	<tr><td>Option</td><td><input type="checkbox" id="clearance" name="clearance" value=""> remove outer material on every turn</td></tr>
  	<tr><td colspan="2"><input name="btn_start" type="BUTTON" value="Create GCode and copy to clipboard (paste in GRBL-Plotter via CTRL-V)" onclick="start_OnClick();" style="width:520px";></td></tr>
      <tr><td colspan="2" id='html_info'></td></tr>
      <tr><td colspan="2"><textarea autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" name="result" wrap="off" cols="66" rows="22" readonly>
  This script mills a conic spiral.
  	</textarea></td></tr>
      <tr><td><b>Status </b></td><td id='html_status'></td></tr>
    </table></td>
  </tr>
  <tr><td><font size="-2">SH 12.03.2020</font></td></tr>
  </table>  
  
  </form>
  </body>
  </html>
  