function CreateThreeAxisShankFromAssemblyInfo(assembly, vertical)
{
   var ring_info = new ProjectInfo();
   ring_info.RetrieveRingInfoFromAssembly(assembly);

   if (true != CreateThreeAxisModel(
      ring_info.diameter,
      ring_info.shank_thickness,
      ring_info.shank_width,
      ring_info.resolution,
      vertical, false))
   {
      return false;
   }

   if(vertical)
   {
      StoreTransformForComponentType("ThreeAxisV");
   }
   else
   {
      StoreTransformForComponentType("ThreeAxis");
   }

   artcam.CurrentModel.SetString("Ring_component_type", "Shank");
   return true;
}
//=== CreateRotaryShankFromAssemblyInfo ==============================================
//
// Creates a blank rotary axis model using the parameters in the passed assembly file
// - usually, but not necessarily, the artcam.ProjectAssembly object.
//
// History
// Who When     What
// --- -------- ---------------------------------------------------------
// ejp 13/01/04 Written
//-----------------------------------------------------------------------

function CreateRotaryShankFromAssemblyInfo
(
 assembly    // assembly to get project info from
 )
{
   var ring_info = new ProjectInfo();
   ring_info.RetrieveRingInfoFromAssembly(assembly);

   if (true != CreateRotaryModel(
      ring_info.diameter,
      ring_info.shank_thickness,
      ring_info.shank_width,
      ring_info.resolution))
   {
      return false;
   }

   StoreTransformForComponentType("RotaryAxis");
   artcam.CurrentModel.SetString("Ring_component_type", "Shank");

   return true;
}


//=== CreateRotaryShankFromShankObject ==============================================
//
//
//
// History
// Who When     What
// --- -------- ---------------------------------------------------------
// ejp 08/03/04 Written
//-----------------------------------------------------------------------

function CreateRotaryShankFromShankObject
   (
   shank    // object to get shank info from    //
   )
{

   if (true != CreateRotaryModel(
      shank.Diameter,
      shank.Thickness,
      shank.ModelHeight,
      shank.Resolution))
   {
      return false;
   }

   StoreTransformForComponentType("RotaryAxis");

   artcam.CurrentModel.SetString("ringinfo_size_description", shank.Description);
   artcam.CurrentModel.SetString("Ring_component_type", "Shank");

   return true;
}

function CreateThreeAxisShankFromShankObject(shank, vertical)
{
   if (true != CreateThreeAxisModel(
      shank.Diameter,
      shank.Thickness,
      shank.ModelHeight,
      shank.Resolution,
      vertical, false))
   {
      return false;
   }

   if(vertical)
   {
      StoreTransformForComponentType("ThreeAxisV");
   }
   else
   {
      StoreTransformForComponentType("ThreeAxis");
   }

   artcam.CurrentModel.SetString("ringinfo_size_description", shank.Description);
   artcam.CurrentModel.SetString("Ring_component_type", "Shank");

   return true;
}


//=== CreateThreeAxisShankFromShankObjectVert ==============================================
//
// Create a three axis vertical model which is aligned in the same way as the finished assembly
//

// History
// Who When     What
// --- -------- ---------------------------------------------------------
// sgc 15/08/06 Written
//-----------------------------------------------------------------------

function CreateThreeAxisShankFromShankObjectVert(shank)
{
   if (true != CreateThreeAxisModel(
      shank.Diameter,
      shank.Thickness,
      shank.ModelHeight,
      shank.Resolution,
      true, true))            // Vertical axis with vertical model
   {
      return false;
   }

   StoreTransformForComponentType("Indexed");   // Used indexed transform as this will not need rotating

   artcam.CurrentModel.SetString("ringinfo_size_description", shank.Description);
   artcam.CurrentModel.SetString("Ring_component_type", "Shank");

   return true;
}

function CreateThreeAxisShankFromShankObjectIndexed(shank)
{
   if (true != CreateIndexedModel(
      shank.Diameter,
      shank.Thickness,
      shank.ModelHeight,
      shank.Resolution))
   {
      return false;
   }

   StoreTransformForComponentType("Indexed");

   artcam.CurrentModel.SetString("ringinfo_size_description", shank.Description);
   artcam.CurrentModel.SetString("Ring_component_type", "Shank");

   return true;
}



function CreateIndexedModel(diameter, thickness, width, resolution)
{
   var realwidth = 0.0;
   var realheight = 0.0;

   var min_x = artcam.ProjectAssembly.MinX();
   var max_x = artcam.ProjectAssembly.MaxX();

   max_x = Math.abs(max_x);
   min_x = Math.abs(min_x);

   // As the assembly can be offset from zero we need to use the maximum deviation from 0 and double this to be sure we can fit the
   // assembly in our 0 centered model
   if(max_x > min_x)
   {
      x_size = max_x + max_x;
   }
   else
   {
      x_size = min_x + min_x;
   }

   // Find the extents of the assembly in Y and Z (X is rotary axis)
   var min_y = artcam.ProjectAssembly.MinY();
   var max_y = artcam.ProjectAssembly.MaxY();
   var y_size = max_y - min_y;

   var min_z = artcam.ProjectAssembly.MinZ();
   var max_z = artcam.ProjectAssembly.MaxZ();
   var z_size = max_z - min_z;

   // Use the largest of these sides then multiply up in case the longest point on the ring is actually at 45%

   if(y_size > z_size)
   {
      y_size = y_size - (diameter/2);
      y_size *= 1.42;
      realheight = y_size + y_size;
   }
   else
   {
      z_size = z_size - (diameter/2);
      z_size *= 1.42;
      realheight = z_size + z_size;
   }

   // Make the model wide enough to hold the assembly and add a little extra
   realwidth = x_size + 10;

   var pixelwidth=parseInt(realwidth*resolution+0.5);
   var pixelheight=parseInt(realheight*resolution+0.5);
   if(pixelwidth & 1)
   {
      pixelwidth+=1;
      realwidth = pixelwidth/resolution;
   }
   if(pixelheight & 1)
   {
      pixelheight+=1;
      realheight = pixelheight/resolution;
   }

   var page_tab_index = artcam.CurrentTab;

   // Create a new model large enough to hold the assembly
   if(artcam.CreateNewModelWithBaseHeight( pixelwidth, pixelheight, realwidth, realheight, 0, true)==null)
      return false;

   artcam.CurrentModel.SetPosition(pixelwidth/2, (pixelheight/2)-1, 0, 0, 0);

   artcam.CurrentModel.SetDouble("Ring_distortion_free_height",thickness);
   artcam.CurrentModel.SetDouble("Ring_diameter",diameter);

   // Draw the rectangles on the model to represent the inner and outer diameter of the ring viewed from above
   artcam.Drawing.Rectangle( 0, 0, (realwidth/2.0 - 1), (diameter + thickness*2)/2.0,false,0,0);
   artcam.VectorSelection.LockSelectedVectors();

   artcam.Drawing.Rectangle( 0, 0, (realwidth/2.0 - 1), diameter/2.0,false,0,0);
   artcam.VectorSelection.LockSelectedVectors();

   var model_silhouette_centre_x;
   var model_silhouette_centre_y;

   var offset = 5;

   if(realheight > realwidth)
   {
      // Draw the silhouette of the ring off to the side of the model
      model_silhouette_centre_x= (realwidth + diameter)/2 + thickness + offset;
      model_silhouette_centre_y= 0;
   }
   else
   {
      // Draw the silhouette of the ring off to the side of the model
      model_silhouette_centre_x= 0;
      model_silhouette_centre_y= -((realheight + diameter)/2 + thickness + offset);
   }

   artcam.CurrentModel.SetDouble("Ring_silhouette_centre_x", model_silhouette_centre_x);
   artcam.CurrentModel.SetDouble("Ring_silhouette_centre_y", model_silhouette_centre_y);

   DrawReferenceSilhouette(model_silhouette_centre_x, model_silhouette_centre_y);
   DrawCentreLines();

   artcam.CurrentTab = page_tab_index;

   return true;
}

function CreateRotaryModel(diameter, thickness, width, resolution)
{

   var realwidth = 0.0;
   var realheight = 0.0;

   realwidth = Math.PI*(diameter+(2*thickness));
   realheight = width;

   var pixelwidth=parseInt(realwidth*resolution+0.5);
   var pixelheight=parseInt(realheight*resolution+0.5);
   if(pixelwidth & 1)
   {
      pixelwidth+=1;
      realwidth = pixelwidth/resolution;
   }
   if(pixelheight & 1)
   {
      pixelheight+=1;
      realheight = pixelheight/resolution;
   }

   var page_tab_index = artcam.CurrentTab;

   if(artcam.CreateNewModelWithBaseHeight( pixelwidth, pixelheight, realwidth, realheight, -thickness, true)==null)
      return false;

   artcam.CurrentModel.SetPosition(pixelwidth/2, (pixelheight/2)-1, 0, 0, thickness);

   artcam.Relief.CreateRingRelief(1);
   artcam.Relief.Reset();

   var silhouette_centre_x=0;
   var silhouette_centre_y=-(diameter + realheight);

   DrawReferenceSilhouette(silhouette_centre_x, silhouette_centre_y);
   DrawCentreLines();

   // Some tools (i.e. 2RailSweep) require these values to be stored in the model.
   artcam.CurrentModel.SetInteger("Ring_wrap_direction", 0); // ALWAYS wrapped in X
   artcam.CurrentModel.SetDouble("Ring_distortion_free_height",thickness);
   artcam.CurrentModel.SetDouble("Ring_diameter",diameter);

   // The following settings can be updated from the location of the silhouette vectors
   artcam.CurrentModel.SetDouble("Ring_silhouette_centre_x", silhouette_centre_x);
   artcam.CurrentModel.SetDouble("Ring_silhouette_centre_y", silhouette_centre_y);
   artcam.CurrentTab = page_tab_index;

   return true;
}
//=== GetRingSilhouetteCentre ==============================================
//
//
//
// History
// Who When     What
// --- -------- ---------------------------------------------------------
// ejp 13/01/04 Written
// ejp 01/07/04 If reference silhouette has been copied we must only find
//              the original - this should be the (oldest) last one pulled
//              out of the document. This system may require GUID rather than label
// ejp 17/09/04 Now follow this order: 1) Check for existing tags in the artcam model
//                                     2) Look for a labelled vector, but don't accept more than 2 (because of vector copy problem)
//                                     3) Try and find a circular vector matching the diameter tag in the Artfile
//-----------------------------------------------------------------------

function GetRingSilhouetteCentre
   (
   axis,           // "x" or "y"
   update_model    // true - also sets the stored value in the model to the returned value
   )
{
   var initial_selection = artcam.VectorSelection.Selection;
   // Set the flag to determine if we return the x or y centre postion
   var x_flag = axis.toLowerCase()=="x" ? true: false;

   // Simple check for tags in the ArtCAM model
   if(  x_flag && artcam.CurrentModel.DoubleExists("Ring_silhouette_centre_x"))
   {
      return artcam.CurrentModel.GetDouble("Ring_silhouette_centre_x", 0);
   }
   if( (!x_flag) && artcam.CurrentModel.DoubleExists("Ring_silhouette_centre_y"))
   {
      return artcam.CurrentModel.GetDouble("Ring_silhouette_centre_y", 0);
   }

   // Otherwise search for labelled vectors...

   // Select the silhouette vectors according to labels
   var vector_ids = FindSilhouetteVectors();
   var reference_vector;

   // ejp 01/07/04 Only use the last (oldest) id in the list
   var vector_array = vector_ids.split(",");

   if(vector_array.length>2)
   {
      artcam.VectorSelection.Selection = "";
   }
   else
   {
      if(vector_array.length==0)
         reference_vector = vector_ids;
      else
         reference_vector = vector_array[vector_array.length-1];
      artcam.VectorSelection.Selection = reference_vector;
   }

   // If nothing was labelled...
   if(artcam.VectorSelection.IsEmpty())
   {
      var ring_info = new ProjectInfo();
      ring_info.RetrieveRingInfoFromAssembly(artcam.ProjectAssembly);
      // Try and find vectors matching the diameter defined in the RingProject
      SelectAllIncludingLocked();
      artcam.VectorSelection.Selection = GetVectorsInSelectionBySizeWithinTolerance("width", ring_info.diameter, 0.1);
      artcam.VectorSelection.Selection = GetVectorsInSelectionBySizeWithinTolerance("height", ring_info.diameter, 0.1);
      // If we still can't find anything, bail out...
      if(artcam.VectorSelection.IsEmpty())
      {
         artcam.VectorSelection.Selection = initial_selection;
         return 0;
      }
      // Choose the first one we found
      var silhouette_vector = artcam.VectorSelection.Item(0);
      silhouette_vector.SetString("RingSilhouette", "Inner");
      artcam.VectorSelection.Selection = silhouette_vector.VectorID;
   }
   var vector_centre;
   // The ring silhouettes should now be selected so return the appropriate centre axis
   if(x_flag)
   {
      vector_centre = (artcam.VectorSelection.MaxX+artcam.VectorSelection.MinX)/2;
      vector_centre += artcam.CurrentModel.XPosition;

      // Update the value stored in the ArtCAM model if requested
      if(update_model)
         artcam.CurrentModel.SetDouble("Ring_silhouette_centre_x", vector_centre);
   }
   else
   {
      vector_centre = (artcam.VectorSelection.MaxY+artcam.VectorSelection.MinY)/2;
      vector_centre += artcam.CurrentModel.YPosition;

      if(update_model)
         artcam.CurrentModel.SetDouble("Ring_silhouette_centre_y", vector_centre);
   }

   artcam.VectorSelection.Selection = initial_selection;
   return  vector_centre;
}


//=== FindSilhouetteVectors ==============================================
//
// Returns IDs of vectors lebelled as "RingSilhouette" -
// usually an inner and outer circle
//
// History
// Who When     What
// --- -------- ---------------------------------------------------------
// ejp 13/01/04 Written
//-----------------------------------------------------------------------

function FindSilhouetteVectors()
{
   var vector_ids = new Array();
   artcam.VectorSelection.SelectAllVectors();
   var index;
   // If the type is not passed, find all the GemVectors
      for(index=0;index<artcam.VectorSelection.Count;index++)
      {
         if( artcam.VectorSelection.Item(index).StringExists("RingSilhouette") )
         {
            vector_ids.push(artcam.VectorSelection.Item(index).VectorID);
         }
      }
   return vector_ids.join(",");
}

function GetOuterSilhouetteVectorID()
{
   var initial_selection = artcam.VectorSelection.Selection;
   artcam.VectorSelection.SelectAllVectors();
   var index;
   // If the type is not passed, find all the GemVectors
      for(index=0;index<artcam.VectorSelection.Count;index++)
      {
         if( artcam.VectorSelection.Item(index).StringExists("RingSilhouette") )
         {
            if(artcam.VectorSelection.Item(index).GetString("RingSilhouette", "Failed") == "Outer")
            {
               var return_id = artcam.VectorSelection.Item(index).VectorID;
               artcam.VectorSelection.Selection = initial_selection;
               return return_id;
            }
         }
      }
      artcam.VectorSelection.Selection = initial_selection;
      return "";
}

//=== GetVectorsInSelectionBySizeWithinTolerance ==============================================
//
// Narrows the selection to those vectors whose width or height matches the size
//
// History
// Who When     What
// --- -------- ---------------------------------------------------------
// ejp 13/01/04 Written
//-----------------------------------------------------------------------

function GetVectorsInSelectionBySizeWithinTolerance
   (
   dimension,   // "width" or "height" to be checked
   size,        // size to compare to
   tolerance    // tolerance of check (> means less exact)
   )
{
   if(artcam.VectorSelection.IsEmpty())
   {
      //artcam.MessageBox(gNoVectorsSelected);
      return "";
   }
   var return_ids = new Array();
   var vector_ids = new Array();
   var selection_ids = artcam.VectorSelection.Selection;
   vector_ids = selection_ids.split(",");
   if(dimension.toLowerCase()=="width")
   {
      for(var index=0;index<vector_ids.length;index++)
      {
         artcam.VectorSelection.Selection = vector_ids[index];
         var vector_width = artcam.VectorSelection.MaxX - artcam.VectorSelection.MinX;
         if( Math.abs(vector_width -  size) < tolerance)
         {
            return_ids.push(vector_ids[index]);
         }
      }
   }
   else
   {
      for(var index=0;index<vector_ids.length;index++)
      {
         artcam.VectorSelection.Selection = vector_ids[index];
         var vector_height = artcam.VectorSelection.MaxY - artcam.VectorSelection.MinY;
         if( Math.abs(vector_height -  size) < tolerance)
         {
            return_ids.push(vector_ids[index]);
         }
      }
   }
   return return_ids.join(",");
}



//=== SelectAllIncludingLocked ==============================================
//
// Basic SelectAll ignores locked vectors -
// this one trawls each layer to select EVERYTHING
//
// History
// Who When     What
// --- -------- ---------------------------------------------------------
// ejp 13/01/04 Written
//-----------------------------------------------------------------------

function SelectAllIncludingLocked()
{
   var vector_ids = new Array();
   for(var index = artcam.VectorLayers.FindFirst();
         artcam.VectorLayers.IsValid(index);
         index = artcam.VectorLayers.FindNext(index) )
   {
      var layer = artcam.VectorLayers.GetLayer( index );
      layer.SelectAll();
      vector_ids.push(artcam.VectorSelection.Selection);
   }
   return vector_ids.join(",");
}



//=== FileCheck ==============================================
//
// Called by ArtCAM for any page in the csutom tab
// whenever a file is created/opened/closed
//
// History
// Who When     What
// --- -------- ---------------------------------------------------------
// ejp 29/01/04 Written
// ejp 28/09/04 Check if buildflag exists before querying, as otherwise it
//              automatically is created and thus 'modifies' the assembly
//-----------------------------------------------------------------------

function FileCheck()
{
   return;
   // If we have flagged that we are in a building process, just return
   if(artcam.ProjectAssemblyExists())
   {
      if(artcam.ProjectAssembly.FlagExists("BuildingFlag"))
      {
         if(artcam.ProjectAssembly.GetFlag("BuildingFlag",false)==true)
         {
            if(artcam.ModelLoaded)
            {
               artcam.DisplayAssistantTab = true;
            }
            else
            {
               artcam.DisplayAssistantTab = false;
            }
            return;
         }
      }
   }

   if(artcam.ModelLoaded)
   {
      // Show all the tabs
      artcam.DisplayAssistantTab = true;
      artcam.DisplayProjectTab = true;

      // Hide the Project Assembly if it exists
      if(artcam.ProjectAssemblyExists())
      {
         // Open the appropriate page on the custom tab
         GotoRingCreationPage();
         artcam.DisplayCustomTab = true;
         artcam.ProjectAssembly.DrawFlag = false;
         // Set the JewelSmith Tab
         artcam.CurrentTab = 4;
      }
      else
         artcam.DisplayCustomTab = false;

      return;
   }
   else // No model, but is there a project?
   {
      if(artcam.ProjectAssemblyExists())
      {

         // Project open - hide assistant and show custom & project
         artcam.DisplayCustomTab = true;
         artcam.DisplayProjectTab = true;
         artcam.DisplayAssistantTab = false;

         // Show the Ring Components
         var creation_page = artcam.HtmlRootDir() + "..\\..\\Custom\\HTML\\";
         creation_page += "RingComponents\\RingComponentsFrame.htm";
         artcam.DisplayPageInCustomTab(creation_page,0);
         artcam.CurrentTab = 0;

      }
      else // No model or project - only show the assistant tab
      {
         artcam.DisplayAssistantTab = true;
         artcam.DisplayCustomTab = false;
         artcam.DisplayProjectTab = false;
      }
   }
}

function SetRingDesignerInfoFromModel(ring_designer)
{
   if(!artcam.ModelLoaded)
      return;

   ring_designer.wrap_direction         = 0; // Always in X
   ring_designer.diameter               = (artcam.CurrentModel.RealWidth/Math.PI) + (artcam.Relief.BaseHeight*2);
   ring_designer.distortion_free_height = -artcam.Relief.BaseHeight;

   // Find the centres from the vectors.
   // The 2RailSweep tools require these values to also be stored in the model so
   // update these stored values as we go.
   ring_designer.SilhouetteCentreX      = GetRingSilhouetteCentre("x", true);
   ring_designer.SilhouetteCentreY      = GetRingSilhouetteCentre("y", true);
}

function DrawReferenceSilhouette(silhouette_centre_x, silhouette_centre_y)
{
   if(!artcam.ModelLoaded)
      return;
   var outer_radius = 0;
   var inner_radius = 0;

   // If this is a rotary model use relief to find zero distortion height
   if(artcam.Relief.BaseHeight!=0)
   {
      outer_radius = (artcam.CurrentModel.RealWidth/Math.PI)/2;
      inner_radius = outer_radius+artcam.Relief.BaseHeight;
   }
   // Otherwise rely on string settings
   else
   {
      inner_radius= artcam.CurrentModel.GetDouble("Ring_diameter",0)/2;
      outer_radius = inner_radius + artcam.CurrentModel.GetDouble("Ring_distortion_free_height",0);
   }

   if(inner_radius==0 || outer_radius==0)
      return false;

   artcam.CurrentModel.SetDouble("Ring_silhouette_centre_x", silhouette_centre_x);
   artcam.CurrentModel.SetDouble("Ring_silhouette_centre_y", silhouette_centre_y);

   var layer_index = artcam.VectorLayers.Create(-1,"Reference Silhouette");
   artcam.VectorLayers.selectedIndex = layer_index;
   artcam.DrawCircle(silhouette_centre_x, silhouette_centre_y, inner_radius , false);
   var vector = artcam.VectorSelection.Item(0);
   vector.SetString("RingSilhouette", "Inner");

   artcam.DrawCircle(silhouette_centre_x, silhouette_centre_y, outer_radius, false);
   vector = artcam.VectorSelection.Item(0);
   vector.SetString("RingSilhouette", "Outer");

   artcam.VectorLayers.GetLayer(layer_index).locked = true;
   artcam.VectorLayers.selectedIndex = 0;
}

function DrawCentreLines()
{
   if(!artcam.ModelLoaded)
      return;

   var layer_index = artcam.VectorLayers.Create(-1,"Reference Lines");
   artcam.VectorLayers.selectedIndex = layer_index;

   var vector = artcam.Vectors.CreateVector();
   if (typeof(vector)!="object")
   {
      artcam.Alert("Failed to create vector",0);
      return false;
   }

   vector.MoveTo(0,-artcam.CurrentModel.RealHeight/2.0);
   vector.LineTo(0,artcam.CurrentModel.RealHeight/2.0);

   artcam.Vectors.AddVectorToArtcam(vector);

   vector = artcam.Vectors.CreateVector();
   if (typeof(vector)!="object")
   {
      artcam.Alert("Failed to create vector",0);
      return false;
   }

   vector.MoveTo(-artcam.CurrentModel.RealWidth/2.0,0);
   vector.LineTo(artcam.CurrentModel.RealWidth/2.0,0);

   artcam.Vectors.AddVectorToArtcam(vector);

   artcam.VectorLayers.GetLayer(layer_index).locked = true;
   artcam.VectorLayers.selectedIndex = 0;

}

function RenderAssemblyIntoRelief(assembly, relief, merge_high)
{
   var success = false;
   var transform_string = "";
   // Reverse stored transform
   if(artcam.CurrentModel.StringExists("_js_AutoMeshTransform1"))
   {
      transform_string = artcam.CurrentModel.GetString("_js_AutoMeshTransform1", "failed");
      TransformObject(assembly,transform_string, true);
   }
   
   if(artcam.RingReliefExists())
   {
      // Store the X,Y,Z positions for restoring the model later
      var x = artcam.CurrentModel.XPosition;
      var y = artcam.CurrentModel.YPosition;
      var z = artcam.CurrentModel.ZPosition;
      
      // Move the model into position for unwrapping... maybe we need to adjust by y position?
      artcam.CurrentModel.SetPosition(0, 0, 0, artcam.CurrentModel.RealHeight, 0);
      
      var unwrap_radius = (artcam.CurrentModel.RealWidth / Math.PI)/2;
      // This transform of 90 around Z is require to turn the object to 'face' along Y axis
      // as expected by the unwrap code. JewelSmith objects conventional face along X from >7.1c
      
      // We also now need to transform the object for unwrapping including moving in Z to position it correctly on the current relief.      
      var width_of_pixel = artcam.CurrentModel.RealWidth/artcam.CurrentModel.PixelWidth;      
      var alignment_transform_string = "0 0 "+(-y-artcam.CurrentModel.RealHeight/2+width_of_pixel)+" 1 1 1 0 0 -90 0 0 0 false";
      //var alignment_transform_string = "0 0 0 1 1 1 0 0 -90 0 0 0 false";
      TransformObject(assembly, alignment_transform_string , false);

      // Now unwrap into the relief.
      success = assembly.UnwrapIntoRelief
         (
         relief,
         2,       // Positioned along Z Axis
         0,       // x coord of central axis
         0,       // y coord of central axis
         0,       // z coord of central axis
         unwrap_radius,
         0,        // y offset for final value in case triangles go below z 0
         merge_high
         );

      // Restore model position
      artcam.CurrentModel.SetPosition(0, artcam.CurrentModel.PixelHeight - 1, x, y, z);
      //artcam.CurrentModel.SetPosition(artcam.CurrentModel.PixelWidth/2, (artcam.CurrentModel.PixelHeight/2)-1, 0, 0, -artcam.Relief.BaseHeight);
      
      TransformObject(assembly,alignment_transform_string, true);
      //if( z != -artcam.Relief.BaseHeight )
      //   artcam.MessageBox("Z and BASE height don't match! z: " + z + ", base: " + (-artcam.Relief.BaseHeight));
   }
   else
   {
      assembly.RenderIntoRelief(relief, merge_high);
      success = true;
   }

   // Return Assembly to stored transform
   if(transform_string!="")
   {
      TransformObject(assembly,transform_string, false);
   }

   if(!success)
      return false;
   return true;
}

function CreateThreeAxisModel(diameter, thickness, width, resolution, vertical, vertModel)
{
   var realwidth = 0.0;
   var realheight = 0.0;

   realwidth = realheight = (diameter + thickness) * 3;

   var pixelwidth=parseInt(realwidth*resolution+0.5);
   var pixelheight=parseInt(realheight*resolution+0.5);
   if(pixelwidth & 1)
   {
      pixelwidth+=1;
      realwidth = pixelwidth/resolution;
   }
   if(pixelheight & 1)
   {
      pixelheight+=1;
      realheight = pixelheight/resolution;
   }

   var page_tab_index = artcam.CurrentTab;
   if(artcam.CreateNewModel( pixelwidth, pixelheight, realwidth, realheight, true)==null)
      return false;

   artcam.CurrentModel.SetPosition(pixelwidth/2, (pixelheight/2)-1, 0, 0, 0);

   var model_distortion_free_height = 0
   var model_wrap_direction = -1; // No wrapping needed

   //Reset the silhouette centre to reflect new model position
   var model_silhouette_centre_x=0;
   var model_silhouette_centre_y=0;

   if(vertical)
   {
      model_silhouette_centre_y=-(diameter + realheight);
      //artcam.DrawSquare( 0, 0, (diameter + thickness*2)/2.0, width/2.0,false,0,0);
      if(vertModel)
         artcam.Drawing.Rectangle( 0, 0, width/2.0, (diameter + thickness*2)/2.0,false,0,0);
      else
         artcam.Drawing.Rectangle( 0, 0, (diameter + thickness*2)/2.0, width/2.0,false,0,0);

      artcam.VectorSelection.LockSelectedVectors();
      //artcam.DrawSquare( 0, 0, diameter/2.0, width/2.0,false,0,0);
      if(vertModel)
         artcam.Drawing.Rectangle( 0, 0, width/2.0, diameter/2.0,false,0,0);
      else
         artcam.Drawing.Rectangle( 0, 0, diameter/2.0, width/2.0,false,0,0);

      artcam.VectorSelection.LockSelectedVectors();
   }
   else
   {
      model_silhouette_centre_y=0;
   }

   artcam.CurrentModel.SetDouble("Ring_distortion_free_height",thickness);
   artcam.CurrentModel.SetDouble("Ring_diameter",diameter);

   DrawReferenceSilhouette(model_silhouette_centre_x, model_silhouette_centre_y);
   DrawCentreLines();
   artcam.CurrentTab = page_tab_index;
   return true;
}

function CreateThreeAxisFlat(width, height, resolution)
{
   var realwidth = width;
   var realheight = height;

   var pixelwidth=parseInt(realwidth*resolution+0.5);
   var pixelheight=parseInt(realheight*resolution+0.5);
   if(pixelwidth & 1)
   {
      pixelwidth+=1;
      realwidth = pixelwidth/resolution;
   }
   if(pixelheight & 1)
   {
      pixelheight+=1;
      realheight = pixelheight/resolution;
   }

   var page_tab_index = artcam.CurrentTab;
   if(artcam.CreateNewModel( pixelwidth, pixelheight, realwidth, realheight, true)==null)
      return false;

   artcam.CurrentModel.SetPosition(pixelwidth/2, (pixelheight/2)-1, 0, 0, 0);

   DrawCentreLines();
   artcam.CurrentTab = page_tab_index;

   artcam.CurrentModel.SetString("Ring_creation_page_name", "ThreeAxisFlat");
   artcam.CurrentModel.SetString("Ring_component_type", "Flat");
   return true;
}

function IncludeAtextFile(filename, default_directory)
{
   var translation_filepath =  artcam.HtmlLangDir + "JewelSmith/" + filename;
   var fs = artcam.CreateFileSelector();

   if(!fs.FileExists(translation_filepath))
      translation_filepath = default_directory + filename;

   var translation_file = fs.ReadTextFile(translation_filepath);
   eval(translation_file);
}

function IncludeFile(filepath)
{
   var fs = artcam.CreateFileSelector();
   if(!fs.FileExists(filepath))
   {
      alert("Failed to Find Include file: " + filepath);
      return;
   }
   var text_file = fs.ReadTextFile(filepath);
   eval(text_file);
}

// === TransformObject =========================================
//
// Transform passed object using data in passed string.
//
// String has format ...
//
// "move_x move_y move_z scale_x scale_y_scale_z rot_x rot_y rot_z orig_x orig_y orig_z use_workplane"
//
// with all fields being double except the last which should be "true" or "false"
//

function TransformObject
    (
    obj,   // object to transform
    data,  // data in above format
    inverse
    )
{
   if (obj==null)
      {
      alert("SimpleMesh.js::TransformObject - null object passed");
      return false;
      }
   // split our parameters
   var data_array = data.split(" ");
   if (data_array.length < 13)
      {
      alert("SimpleMesh.js::TransformObject - insufficient parameters in passed data\n" + data);
      return false;
      }

   obj.Transform(
                parseFloat(data_array[0]),
                parseFloat(data_array[1]),  // move
                parseFloat(data_array[2]),

                parseFloat(data_array[3]),
                parseFloat(data_array[4]),  // scale
                parseFloat(data_array[5]),

                parseFloat(data_array[6]),
                parseFloat(data_array[7]),  // rotate
                parseFloat(data_array[8]),

                parseFloat(data_array[9]),
                parseFloat(data_array[10]),  // origin
                parseFloat(data_array[11]),

                data_array[12].indexOf("true") != -1,  //relative to workplane or world
                inverse // Inverse transform
                );
   // alert(data);
   return true;
}

function GetReliefFromAssemblyTop(assembly)
{
   var temp_relief = artcam.CreateEmptyRelief();
   temp_relief.CopyFromCurrent();
   temp_relief.MakeTransparent();
   //temp_relief.Reset();
   RenderAssemblyIntoRelief(assembly, temp_relief, true);
   temp_relief.ReplaceHeightsValues(9876543, temp_relief.MinZ + temp_relief.BaseHeight);
   return temp_relief;
}

function GetReliefFromAssemblyBottom(assembly, doBorders)
{
   var replaceBorders;

   if(doBorders)
      replaceBorders = doBorders;
   else
      replaceBorders = false;

   var temp_relief = artcam.CreateEmptyRelief();
   temp_relief.CopyFromCurrent();
   temp_relief.MakeTransparent();
   RenderAssemblyIntoRelief(assembly, temp_relief, false);
   temp_relief.ReplaceHeightsValues(9876543, temp_relief.MaxZ + temp_relief.BaseHeight);

   if(!replaceBorders)
      return temp_relief;

   // Code below replaces border areas with the relief base height
   var image = artcam.CreateEmptyImage();
   if (image == null)
      return;

   if (!image.CopyFromCurrentImage())
   {
      image=null;
      return;
   }
   image.CreateGreyScaleFromPassedRelief(temp_relief, 200, true, true);
   var yellow_index = image.GetColourIndex(255,255,128);
   image.FloodFill(0,0,yellow_index);
   temp_relief.SetHeightsUnderImageMask(temp_relief.BaseHeight, yellow_index, image);
   return temp_relief;
}

function StoreTransformForComponentType(type)
{
   var transform_string = "";
   switch(type)
   {
   case "RotaryAxis":
      transform_string = "0 0 0 1 1 1 -90 0 -90 0 0 0";
      break;
   case "ThreeAxis":
      transform_string = "0 0 0 1 1 1 -90 0 90 0 0 0";
      break;
   case "ThreeAxisV":
      transform_string = "0 0 0 1 1 1 0 0 90 0 0 0";
      break;
   case "RotarySetting":
      transform_string = "0 0 0 1 1 1 0 0 -90 0 0 0";
      break;
   case "Indexed":
      transform_string = "0 0 0 1 1 1 0 0 0 0 0 0";
   }
   if(transform_string!="")
      artcam.CurrentModel.SetString("_js_AutoMeshTransform1", transform_string + " false");

   artcam.CurrentModel.SetString("Ring_creation_page_name", type);
}

function OrientateMeshToMachiningOrigin(mesh)
{
   if (artcam.CurrentModel.StringExists("_js_AutoMeshTransform1"))
   {
      if(artcam.Relief.BaseHeight!=0)
      {
         TransformObject(mesh, "0 0 " + artcam.Relief.BaseHeight + " 1 1 1 0 0 0 0 0 0 false", false);
      }

      TransformObject(mesh, artcam.CurrentModel.GetString("_js_AutoMeshTransform1",""), false);
      // do any other optional transforms ...
      if (artcam.CurrentModel.StringExists("_js_AutoMeshTransform2"))
         TransformObject(mesh, artcam.CurrentModel.GetString("_js_AutoMeshTransform2",""), false);
      if (artcam.CurrentModel.StringExists("_js_AutoMeshTransform3"))
         TransformObject(mesh, artcam.CurrentModel.GetString("_js_AutoMeshTransform3",""), false);
      if (artcam.CurrentModel.StringExists("_js_AutoMeshTransform4"))
         TransformObject(mesh, artcam.CurrentModel.GetString("_js_AutoMeshTransform4",""), false);
      if (artcam.CurrentModel.StringExists("_js_AutoMeshTransform5"))
         TransformObject(mesh, artcam.CurrentModel.GetString("_js_AutoMeshTransform5",""), false);
   }
   else
   {
      var model_creation_page_name = artcam.CurrentModel.GetString("Ring_creation_page_name", "Failed");
      if(model_creation_page_name == "ThreeAxis")
      {
         mesh.Transform(0,0,0,1,1,1,-90,0,90,0,0,0,false, false);
      }
      else if (artcam.RingReliefExists())
      {
         //mesh.Transform(0,0,artcam.Relief.BaseHeight,1,1,1,0,0,0,0,0,0,false);
         mesh.Transform(artcam.Relief.BaseHeight,0,0,1,1,1,-90,0,-90,0,0,0,false, false);
      }
   }
   mesh.ResetWorkplane();
}

function CreateWrappedMeshFromCurrentModel(tolerance, use_back_relief)
{
   var ReliefTriangulator = artcam.CreateReliefTriangulator();
   ReliefTriangulator.DeleteTriangulation();

   ReliefTriangulator.MaxTriangles = 0;
   ReliefTriangulator.MaxError = tolerance;
   ReliefTriangulator.ForceBoundary = true;
   ReliefTriangulator.CreateBackFace = true;
   ReliefTriangulator.StitchToSelectedVectors = false;
   ReliefTriangulator.SmoothAngle = 20;
   ReliefTriangulator.DrawSmoothTriangles = true;
   ReliefTriangulator.DrawNormals   = false;
   ReliefTriangulator.StitchDistance = 0.0;

   // calculate max triangle length using tolerance, ring diameter
   var minX   = artcam.Relief.MinX;
   var minY   = artcam.Relief.MinY;
   var minZ   = artcam.MaterialMinZ;
   var maxZ   = artcam.MaterialMaxZ;
   var width  = artcam.Relief.MaxX - minX;
   var height = artcam.Relief.MaxY - minY;

   // get 'radius' of ring we add max height
   var circumference = width;

   var radius = circumference / (2.0 * Math.PI);
   if (maxZ > 0)
      radius += maxZ;

   var side_len = radius - tolerance;

   var max_tri_length = Math.sqrt((radius * radius) - (side_len * side_len)) * 2.0;
   ReliefTriangulator.MaxTriangleLength = max_tri_length;
   ReliefTriangulator.LimitTriangleLengthType = 2;
   ReliefTriangulator.WrappingType = 1;

   // we create the triangulation in the same position as the relief
   ReliefTriangulator.XOffset = artcam.relief.MinX;
   ReliefTriangulator.YOffset = artcam.relief.MinY;
   ReliefTriangulator.ZOffset = artcam.relief.ZeroPlaneZ;

   var back_relief = artcam.CreateEmptyRelief();
   back_relief.CopyFromCurrentDisplay();
   back_relief.BaseHeight = artcam.Relief.BaseHeight;
   back_relief.SetAllHeights(back_relief.BaseHeight);

   if(use_back_relief)
   {
      artcam.Relief.MakeBackReliefCurrent();
      back_relief.CopyFromCurrentDisplay();
      artcam.Relief.MakeFrontReliefCurrent();
   }
   else
      back_relief = artcam.CreateEmptyRelief();

   if (artcam.CommandsOk)
   {
      ReliefTriangulator.Calculate(back_relief);
   }
   else
   {
      artcam.Relief.Visible = true;
      artcam.MessageBox(gArtCAMisRedrawing);
      return;
   }


   if (!ReliefTriangulator.TriangulationExists())
   {
      artcam.Relief.Visible = true;
      artcam.Alert("CreateWrappedMeshFromCurrentModel::No Triangulation Created",0);
      return;
   }
   var mesh = ReliefTriangulator.GetTriangulation();
   if(mesh==null)
   {
      artcam.Alert("CreateWrappedMeshFromCurrentModel::Failed to get triangulation");
      return;
   }
   OrientateMeshToMachiningOrigin(mesh);
   var ring_designer = artcam.CreateRingDesigner();
   ring_designer.RingReliefVisible = false;
   return mesh;
}


function CreateFlatMeshFromCurrentModel(tolerance, use_back_relief)
{
   var ReliefTriangulator = artcam.CreateReliefTriangulator();
   ReliefTriangulator.DeleteTriangulation();

   ReliefTriangulator.MaxTriangles = 0;
   ReliefTriangulator.MaxError = tolerance;
   ReliefTriangulator.ForceBoundary = true;
   ReliefTriangulator.CreateBackFace = true;
   ReliefTriangulator.StitchToSelectedVectors = false;
   ReliefTriangulator.SmoothAngle = 20;
   ReliefTriangulator.DrawSmoothTriangles = true;
   ReliefTriangulator.DrawNormals   = false;
   ReliefTriangulator.StitchDistance = 0.0;

      // we create the triangulation in the same position as the relief
   ReliefTriangulator.XOffset = artcam.relief.MinX;
   ReliefTriangulator.YOffset = artcam.relief.MinY;
   ReliefTriangulator.ZOffset = artcam.relief.ZeroPlaneZ;

   ReliefTriangulator.WrappingType = 0;

   var back_relief = artcam.CreateEmptyRelief();
   back_relief.CopyFromCurrentDisplay();
   back_relief.Reset();

   if(use_back_relief)
   {
      artcam.Relief.MakeBackReliefCurrent();
      back_relief.CopyFromCurrentDisplay();
      artcam.Relief.MakeFrontReliefCurrent();
   }
   else
      back_relief = artcam.CreateEmptyRelief();

   if (artcam.CommandsOk)
   {
      ReliefTriangulator.Calculate(back_relief);
   }
   else
   {
      artcam.Relief.Visible = true;
      artcam.MessageBox(gArtCAMisRedrawing);
      return;
   }


   if (!ReliefTriangulator.TriangulationExists())
   {
      artcam.Relief.Visible = true;
      artcam.Alert("CreateWrappedMeshFromCurrentModel::No Triangulation Created",0);
      return;
   }

   var mesh = ReliefTriangulator.GetTriangulation();
   if(mesh==null)
   {
      artcam.Alert("CreateFlatMeshFromCurrentModel::Failed to get triangulation");
      return;
   }
   OrientateMeshToMachiningOrigin(mesh);

   return mesh;
}

// === MergeAssembly =====================================================
//
// Function taken from plugin_main, MergeAssembly.js for ease of calling from ArtCAM C++
//
// =======================================================================

function MergeAssembly(main_assembly)
{
   //main_assembly = artcam.ProjectAssembly;  
   var rlm = artcam.ReliefLayerManager;

   var assembly_draw_flag = artcam.ProjectAssembly.DrawFlag;
   if(!assembly_draw_flag)
      artcam.ProjectAssembly.DrawFlag = true;
   
   artcam.Relief.MakeFrontReliefCurrent();
   var temp_relief = GetReliefFromAssemblyTop(main_assembly);
   rlm.AddNewLayer(main_assembly.Name + " " + gTop, true, 3);
   rlm.GetCurrentLayer.CombineMode = 2; // Merge Highest
   temp_relief.CopyToCurrent();
   
   artcam.Relief.MakeBackReliefCurrent();
   temp_relief = GetReliefFromAssemblyBottom(main_assembly);
   rlm.AddNewLayer(main_assembly.Name + " " + gBottom, true, 3); 
   rlm.GetCurrentLayer.CombineMode = 3; // Merge Lowest
   temp_relief.CopyToCurrent();      
   
   artcam.Relief.MakeFrontReliefCurrent();

   if(!assembly_draw_flag)
      artcam.ProjectAssembly.DrawFlag = false;

   artcam.Refresh3dView();
}

