KGRKJGETMRETU895U-589TY5MIGM5JGB5SDFESFREWTGR54TY
Server : Apache/2.4.62
System : FreeBSD fbsdweb2.web.rcn.net 14.1-RELEASE FreeBSD 14.1-RELEASE releng/14.1-n267679-10e31f0946d8 GENERIC amd64
User : www ( 80)
PHP Version : 8.3.8
Disable Function : NONE
Directory :  /domains/gohover/cell/paprica/uploadtestfolder/Current Code/

Upload File :
current_dir [ Writeable ] document_root [ Writeable ]

 

Current File : /domains/gohover/cell/paprica/uploadtestfolder/Current Code/tiling-code-most-current.js



var rulesmenu_data = [ 
                 // Note that each entry except the last one must be followed by a comma.
            ["B3/S23", "Rules"],
            ["PromptForRule","Enter Your Own Rule"],
            ["B3/S23", "--Lifelike rules--"],
            ["B1/S"],
            ["B2/S"],
            ["B3/S23", "B3/S23 (Life)"],
            ["B36/S23","B36/S23 (HighLife)"],
            ["B37/S23", "B37/S23 (DryLife)"],
            ["B3/S23", "--Generations rules--"], 
            ["B2/S/C3","B2/S/C3  (B's Brain)"],
            ["B2/S/C4","B2/S/C4 (PenroseP3 & AB)"],  // Sedate Brain, Valium Brain, Calm Brian
            ["B2/S345/C4", "B2/S345/C4 (StarWars)"],
            ["B23/S/C4","B23/S/C4 (PenroseP1)"],     // also:  "B23/S459/C4" and "B2/S459/C4 (AB Gun)"
            ["B3/S23", "--Von Neumann rules--"], 
            ["B1/S1V"], 
            ["B3/S23", "--Bx2 rules--"], 
            ["B1x2/S23V"],
            ["B3/S23", "-Nontotalistic--"], 
            ["B2e/s"],
            ["B2a/S"],

            ["B3/S23", "--Misc Rules---"], 
            ["VN_head_Moore_tail", "PoisonTail"],
            ["VN_head_Moore_tail", "VN_head_Moore_tail"],
            ["Corner_Head Moore_Tail", "Cnr_head Moore_tail"],
            ["AlexV Snakes"],
            ["AlexV Ants-4"],

            ["my TriGou"],   // beware using the name "Goucher" for other rules - consider what the parser is looking for.
            ["Goucher Gliders"],
            ["KiteWorm Rule-1"],
            ["BorderRule"]
                 // Note that the last entry must NOT be followed by a comma! 
                 ];  

var lifepatterns = { 
        Test_Blob3: {rule: "all", 
                        patternstring: "#C 1 #S 1 #C 2 #S 1 #C 3 #S 1 #C 4 #S 1 #C 5 #S 1 #C 6 #S 1 #C 7 #S 1"
                       },
        "Test Blob": {rule: "all", 
                        patternstring: "#C 1 #S 1 #C 2 #S 1 #C 3 #S 1 #C 4 #S 1 #C 5 #S 1 #C 6 #S 1 #C 7 #S 1"
                       },
         "Test Blob2": {rule: "all", 
                        patternstring: "#C 8 #S 1 #C 9 #S 1 #C 10 #S 1 #C 11 #S 1 #C 12 #S 1 #C 13 #S 1 #C 14 #S 1"
                       }


                     };

 


   newScript = document.createElement('script');
   newScript.type = 'text/javascript';
   newScript.src = '../rhill-voronoi-core.min.js';
   document.getElementsByTagName('head')[0].appendChild(newScript);
   
   // End of Voronoi section.
    

   newScript = document.createElement('script');
   newScript.type = 'text/javascript';
   newScript.src = '../substitution_tiling_modules.js';
   document.getElementsByTagName('head')[0].appendChild(newScript);
console.log("I tried loading it the current directory.")


   newScript = document.createElement('script');
   newScript.type = 'text/javascript';
   newScript.src = '../periodic_and_spiral_tiling_code.js';
   document.getElementsByTagName('head')[0].appendChild(newScript);


   newScript = document.createElement('script');
   newScript.type = 'text/javascript';
   newScript.src = '../PaulZinnJustin-quasiperiodic-most-current.js';
   document.getElementsByTagName('head')[0].appendChild(newScript);


// see  http://stackoverflow.com/questions/5892845/how-to-load-one-javascript-file-from-another




function put_rules_in_menu() {
    document.getElementById("rulesmenu").options.length = 0;
    var len = rulesmenu_data.length;
    for (var i = 0; i<len; i++) {
        if (rulesmenu_data[i][1] != undefined) {
          // if there is an englishname option, use it
          addOption(document.getElementById("rulesmenu"),rulesmenu_data[i][1],rulesmenu_data[i][0] )
       }
      else { // there is no englishname option
           addOption(document.getElementById("rulesmenu"),rulesmenu_data[i][0],rulesmenu_data[i][0] )
       }
   } 
}

/*   put_patterns_in_menu()   Old version does not discriminate by tiling
function put_patterns_in_menu() {
    for (var p in lifepatterns) {
         addOption(document.getElementById("patternmenu"),p,p )    
    }
}
*/

function put_patterns_in_menu() {
    document.getElementById("patternmenu").options.length = 1;

    for (var p in lifepatterns) {
      var t = lifepatterns[p].tiling
      if (t == undefined || t == "") {
         addOption(document.getElementById("patternmenu"),p,p )  
      }
      else {
        var matches = true
        for (var tilingspec in t) {
           if (the_mesh_spec[tilingspec] !=  t[tilingspec]) {matches = false}
         
        }
        if (matches == true) {
           addOption(document.getElementById("patternmenu"),p,p )  

        }
       
      }  
    }
}


function addOption(selectbox,text,value ) {
	var optn = document.createElement("OPTION");
	optn.text = text;
	optn.value = value;
	selectbox.options.add(optn);
};




function pick_draw_action() {
   var themenu = document.getElementById("drawmenu")
   choice =  themenu.options[ themenu.selectedIndex].value
   switch(choice) {
       case "Randomize":   make_random_field(); break;
       case "Clear":       clear_field(); break;
   }
   themenu.selectedIndex = 0;
} 
    

   
function pick_pattern_action() {
   var themenu = document.getElementById("patternmenu")
   choice =  themenu.options[ themenu.selectedIndex].value
   load_pattern(choice)
   if (lifepatterns[choice].rule != "all"  )  {set_rule_from_outside(lifepatterns[choice].rule)} else {after_changing_rule()};
   themenu.selectedIndex = 0;
}

function choose_performance_action() {
   var themenu = document.getElementById("speed_step_menu")
   choice =  themenu.options[ themenu.selectedIndex].value
   switch (choice) {

    case "Polygon Fill":
       if (rendertype == 4) {gl.canvas.height = 0}  // still need to do a clear  <<-------------------------------
       rendertype = 1;
       drawboard()
     //  themenu.selectedIndex = 0;
       break;
     case "Rect Fill":
       if (rendertype == 4) {gl.canvas.height = 0}  // still need to do a clear  <<-------------------------------

       rendertype = 2;
       drawboard()
     //  themenu.selectedIndex = 0;
       break;
    case "Rect PutData":
       alert("Sorry, this option isn't implemented yet")
      // rendertype = 3;
      // drawboard()
       themenu.selectedIndex = 0;
       break;   
    case "Polygon WebGL":
       rendertype = 4;
       if (allcells[1].webglVertices == undefined) { 
           webgl_setup()  // this doesn't really need to be loaded again in many cases -- use a flag to check if it needs to be loaded.
           AddWeglVerticesToAllCells() 
       }
       else {gl.canvas.height = the_dimension }  // still need to do a clear  <<-------------------------------
      


//  some experimental lines (not a permanent solution)
// deleting  pjs, layer_Grid, and layer2
 
/*
var element = document.getElementById("pjs");
element.parentNode.removeChild(element);

var element = document.getElementById("layer_Grid");
element.parentNode.removeChild(element);

var element = document.getElementById("layer2");
element.parentNode.removeChild(element);
 */




       drawboard()
      // themenu.selectedIndex = 0;
       break;   
    case "Rect WebGL":
       alert("Sorry, this option isn't implemented yet")
      // rendertype = 5;
      // drawboard()
       themenu.selectedIndex = 0;
       break;
    case "Use Set Interval":
       flag_set_int = 1
       flag_req_ani = 0
       break;
    case "Use Requst Animation":
       flag_set_int = 0
       flag_req_ani = 1
       break;
  
  




     default:
       speedstep = themenu.options[ themenu.selectedIndex].value
     //  themenu.selectedIndex = 0;
       break;
    }
};

function choose_tiling_action() {
   var themenu = document.getElementById("tiling_menu")
   choice =  themenu.options[ themenu.selectedIndex].value
   switch (choice) {
       case "Export Selection": 
         print_pattern()
         themenu.selectedIndex = 0;

         break;

       case "Export Mesh": 
         print_obj()
         themenu.selectedIndex = 0;

         break;

       case "Mesh Check": 
        // mesh_check();
         show_border_cells()
         themenu.selectedIndex = 0;

         break;


       case "Mesh Info": 
         mesh_info("report");
          themenu.selectedIndex = 0;

         break;

       case "Subdivide Mesh":
         the_mesh_spec.mods =  the_mesh_spec.mods + "s"
         linearsubdivide();
         themenu.selectedIndex = 0;
         break;

       case "TriSubdivConcaveOnly":
         the_mesh_spec.mods =  the_mesh_spec.mods + "c"
         triangle_subdivide_concave_only();
         themenu.selectedIndex = 0;
         break;

       case "Triangle Subdivide":
         the_mesh_spec.mods =  the_mesh_spec.mods + "t"
         triangle_subdivide();
         themenu.selectedIndex = 0;
         break;

       case "Dual Tiling":
         the_mesh_spec.mods =  the_mesh_spec.mods + "d"
         easy_dual_tiling();
         themenu.selectedIndex = 0;
         break;

       case  "Triangle Corners":
         the_mesh_spec.mods =  the_mesh_spec.mods + "triangle corner experiment "
         inset_subdivide2();
         themenu.selectedIndex = 0;
         break;

       case  "Truncate Tiling":
         the_mesh_spec.mods =  the_mesh_spec.mods + "Truncate "  // fix these codes -- see comment below 
            easy_dual_tiling();
            triangle_subdivide();
            easy_dual_tiling();
         themenu.selectedIndex = 0;
         break;


       case  "Truncate(1/4) Tiling":
         the_mesh_spec.mods =  the_mesh_spec.mods + "truncate(1/4) experiment"  // fix these codes -- see comment below 
         truncate_tiling();
         themenu.selectedIndex = 0;
         break;

       case "Rectify Tiling":
         the_mesh_spec.mods =  the_mesh_spec.mods + "rectify experiment"
         rectify_tiling();
         themenu.selectedIndex = 0;
         break;

/*  candidate for deletion
       case "Alternation Tiling":
         the_mesh_spec.mods =  the_mesh_spec.mods + "experiment"
         alternation_tiling();
         themenu.selectedIndex = 0;
         break;
*/
       case "Honeycombization":
         the_mesh_spec.mods =  the_mesh_spec.mods + "honeycombization experiment "
         honeycombization();
         themenu.selectedIndex = 0;
         break;

       case "Chamfer Tiling":
         the_mesh_spec.mods =  the_mesh_spec.mods + "chamfer experiment "
         chamfer_tiling(1);  // 1 = not life on the edge
         themenu.selectedIndex = 0;
         break;

       case "Life On The Edge":
         the_mesh_spec.mods =  the_mesh_spec.mods + "life on the edge experiment "
         chamfer_tiling(0);  // 0 = life on the edge
         themenu.selectedIndex = 0;
         break;



       case "Snub Tiling":
         the_mesh_spec.mods =  the_mesh_spec.mods + "snub experiment "
            pentagonalization(); 
            easy_dual_tiling();
        // truncate_tiling; alternation_tiling();
         themenu.selectedIndex = 0;
         break;

       case "Stellate Tiling":
         the_mesh_spec.mods =  the_mesh_spec.mods + "stellate experiment "
         stellate_tiling();
         themenu.selectedIndex = 0;
         break;



       case "Selfridge Subdivide":
         the_mesh_spec.mods =  the_mesh_spec.mods + "r"
         Selfridge_subdivide();
         themenu.selectedIndex = 0;
         break;

       case "Inset Subdivide":
         the_mesh_spec.mods =  the_mesh_spec.mods + "i"
         inset_subdivide();
         themenu.selectedIndex = 0;
         break;

 

       case "Waffle Subdivide":
         the_mesh_spec.mods =  the_mesh_spec.mods + "r"
         waffle_subdivide();
         themenu.selectedIndex = 0;
         break;

       case "Pentagonalization":
         the_mesh_spec.mods =  the_mesh_spec.mods + "r"  // hey fix this and delete this comment - can't have same code as waffle - check all of these codes!
         pentagonalization();
         themenu.selectedIndex = 0;
         break;
/*
       case "Delaunay Tiling":
         the_mesh_spec.mods =  the_mesh_spec.mods + "u"
         DelaunayTiling()
         themenu.selectedIndex = 0;
         break;
*/
       case "Call Voronoi":
         the_mesh_spec.mods =  the_mesh_spec.mods + "v"
         callVoronoi();
         themenu.selectedIndex = 0;
         break;

       case "Unit Edge Dual":
         the_mesh_spec.mods =  ""
        // Zongker_dual_tiling()      //0.25 // .125 // .0625
         ZongkerPolygons_inside_easy_dual_tiling()
         themenu.selectedIndex = 0;
         break;

       case "Gen Experiment2":
         the_mesh_spec.mods =  ""
         my_dual_method_tiling_generator2()         
         themenu.selectedIndex = 0;
         break;

       case "Enable Wrap-around":
         the_mesh_spec.mods =  the_mesh_spec.mods + "enable wraparound "   // need to set a condition so that wrap around can't be enabled twice.  
                                                                           // Also figure out how to disable wraparound! (maybe just reload mesh)
         make_mesh_wraparound()
         themenu.selectedIndex = 0;
         break;


     }
                   
}

function choose_brush_action() {
   var themenu = document.getElementById("brushmenu")
   choice =  themenu.options[ themenu.selectedIndex].value
   switch (choice) {
       case "Draw Mode":
         mouse_status = "draw"
         themenu.selectedIndex = draw_mode_index;
         document.getElementById("brushradio").checked = true
         colored_pencil_choice = 1;
         break;


       case "--Draw Actions--":
         mouse_status = "draw"
         themenu.selectedIndex = draw_mode_index;
         document.getElementById("brushradio").checked = true
         colored_pencil_choice = 1;
         break;

       case "Select Mode":
         mouse_status = "select"
         themenu.selectedIndex = select_mode_index;
         document.getElementById("selectradio").checked = true
         break;

       case "--Select Actions--":
         mouse_status = "select"
         themenu.selectedIndex = select_mode_index;
         document.getElementById("selectradio").checked = true
         break;

       case "Random Fill": 
         make_random_field();
      //   themenu.selectedIndex = select_mode_index;
      //   document.getElementById("selectradio").checked = true

         if (mouse_status == "select") {
                 themenu.selectedIndex = select_mode_index;
                 document.getElementById("selectradio").checked = true
         }

         if (mouse_status == "draw") {
                 themenu.selectedIndex = draw_mode_index;
                 document.getElementById("brushradio").checked = true
         }

         break;

       case "Fill Selection":
         fill_selection(colored_pencil_choice);
         themenu.selectedIndex = select_mode_index;
         document.getElementById("selectradio").checked = true
         mouse_status = "select"  

         break;


       case "Clear Selection": 
         mouse_status = "select"  // delete this line if "Select Mode" must be selected first
         fill_selection(0);
         themenu.selectedIndex = select_mode_index;
         document.getElementById("selectradio").checked = true
         break;

       case "Clear Outside": 
         mouse_status = "select"  // delete this line if "Select Mode" must be selected first
         clear_outside();
         themenu.selectedIndex = select_mode_index;
         document.getElementById("selectradio").checked = true
         break;

       case "Clear All": 
         clear_field();


         if (mouse_status == "select") {
                 themenu.selectedIndex = select_mode_index;
                 document.getElementById("selectradio").checked = true
         }

         if (mouse_status == "draw") {
                 themenu.selectedIndex = draw_mode_index;
                 document.getElementById("brushradio").checked = true
         }

         break;

       case "Export Selection": 
         print_pattern()
         themenu.selectedIndex = select_mode_index;
         document.getElementById("selectradio").checked = true
         mouse_status = "select"  
         break;


       default:   
         colored_pencil_choice = parseInt(choice)
         mouse_status = "draw"
         themenu.selectedIndex = draw_mode_index;
         document.getElementById("brushradio").checked = true

     }
                   
}

function choose_radio_draw() {
    document.getElementById("brushmenu").selectedIndex = draw_mode_index;
    mouse_status = "draw"
    colored_pencil_choice = 1;
}

function choose_radio_select() {
    document.getElementById("brushmenu").selectedIndex = select_mode_index;
    mouse_status = "select"
}
/* old -- uses input type button

function makebutton(b_id, b_style, b_value,  b_div, b_onclick ) {
 
    //Create an input type dynamically.
    var element = document.createElement("input");
 
    //Assign different attributes to the element.
    element.setAttribute("type", "button");
    element.setAttribute("id", b_id);
    element.setAttribute("style", b_style+"height:5px;border:1px");
    element.setAttribute("value", b_value);

   // Is onclick an attribute?  If not, then don't use setAttribute
   // element.setAttribute("onclick", b_onclick); // this worked with //makebutton( "gobutton" , "width: 40px" , " Go " ,  "doTimer()")
   // but it might be wrong, might not work with all browsers(?), etc.

    document.getElementById(b_div).appendChild(element);
    document.getElementById(b_id).onclick = b_onclick
}

*/


function makebutton(b_id, b_style, b_value,  b_div, b_onclick ) {
 

    //Create an input type dynamically.
    var element = document.createElement("button");
 
    //Assign different attributes to the element.
    element.setAttribute("id", b_id);
    element.setAttribute("style", b_style);
    element.innerHTML += b_value;

   // Is onclick an attribute?  If not, then don't use setAttribute
   // element.setAttribute("onclick", b_onclick); // this worked with //makebutton( "gobutton" , "width: 40px" , " Go " ,  "doTimer()")
   // but it might be wrong, might not work with all browsers(?), etc.

    document.getElementById(b_div).appendChild(element);
    document.getElementById(b_id).onclick = b_onclick
}




function makesite() {
    var my_buttonstyle = "font-size:7pt;" //"font-size:7pt;" // border:0px
    var my_dropdownstyle = "font-size:7pt;"//"font-size:7pt;" //  -webkit-appearance: button;-moz-appearance: button; width:50px;" //  border:0px;

    var element = document.createElement("div");
    element.setAttribute("id", "buttonsdiv")
    element.setAttribute("style", "-webkit-user-select: none; -moz-user-select: none; -ms-user-select: none; font-size:10px;")
    // this also might work: -webkit-tap-highlight-color:transparent;
    document.getElementById("MainBody").appendChild(element);


    makebutton( "gobutton" , "width: 40px; "+my_buttonstyle, " Go " , "buttonsdiv",    doTimer)
    makebutton("stepbutton", "width: 40px;"+my_buttonstyle,  "Step", "buttonsdiv", stepCount)
   




 //   makebutton("zoominbutton", "width: 60px; "+my_buttonstyle,           " In", "buttonsdiv",increase_magnification)
 //   makebutton("zommoutbutton", "width: 70px; "+my_buttonstyle,          " Out","buttonsdiv", decrease_magnification)




// Radio Button Section
    element = document.createElement("input");
    element.setAttribute("type", "radio");
    element.setAttribute("name", "radiogroup1");
    //   element.setAttribute("style", "width: 50px; text-align:right");
    element.setAttribute("id", "brushradio");
    element.setAttribute("value", "pickeddraw");
    element.checked = true;
    // element.innerHTML = "Draw";
    element.onclick = choose_radio_draw
    document.getElementById("buttonsdiv").appendChild(element);

    element = document.createElement("label");
    element.setAttribute("for", "brushradio");
    element.innerHTML = "Draw";
    document.getElementById("buttonsdiv").appendChild(element);

    element = document.createElement("input");
    element.setAttribute("type", "radio");
    element.setAttribute("name", "radiogroup1");
    //  element.setAttribute("style", "width: 50px; text-align:right");
    element.setAttribute("id", "selectradio");
    element.setAttribute("value", "pickedselect");
   // element.innerHTML = "Select";
    element.onclick = choose_radio_select
     document.getElementById("buttonsdiv").appendChild(element);

    element = document.createElement("label");
    element.setAttribute("for", "selectradio");
    element.innerHTML = "Select";
    document.getElementById("buttonsdiv").appendChild(element);

 // End of radio button section
    

   // element = document.createElement("select");
   // document.getElementById("buttonsdiv").appendChild(element);
   // element.setAttribute("id", "drawmenu");
   // element.onchange=pick_draw_action
   // addOption(element, "Draw", "Draw")
   // addOption(element, "Spraypaint", "Spraypaint")
   // addOption(element, "(R)andomize", "Randomize")
   // addOption(element, "(C)lear", "Clear")


    element = document.createElement("select");
    element.setAttribute("style", my_dropdownstyle);
    document.getElementById("buttonsdiv").appendChild(element);
    element.setAttribute("id", "brushmenu");
    element.onchange=choose_brush_action
    addOption(element, "Edit", "1")
    addOption(element, "Draw Mode", "Draw Mode")
   // addOption(element, "--Draw Actions--", "--Draw Actions--")
    addOption(element, "--Color 1", "1")
    addOption(element, "--Color 2", "2")
    addOption(element, "--Color 3", "3")
    addOption(element, "--Color 4", "4")
    addOption(element, "------------", "--Draw Actions--")
    addOption(element, "Select Mode", "Select Mode")
   // addOption(element, "--Select Actions--", "--Select Actions--")
    addOption(element, "--Select", "Select Mode")
    addOption(element, "--Random Fill", "Random Fill")
    addOption(element, "--Fill Selection", "Fill Selection")
    addOption(element, "--Clear Selection", "Clear Selection")
    addOption(element, "--Clear All", "Clear All")
    addOption(element, "             ", "--Select Actions--")
    addOption(element, "--Clear Outside", "Clear Outside")
    addOption(element, "             ", "--Select Actions--")
    addOption(element, "Export Selection",  "Export Selection")

    draw_mode_index = 1   // change this when the above menu changes
    select_mode_index = 7 // change this when the above menu changes

    element = document.createElement("select");
    element.setAttribute("style", my_dropdownstyle);

    document.getElementById("buttonsdiv").appendChild(element);
    element.setAttribute("id", "rulesmenu");
    element.onchange=change_rule
    put_rules_in_menu()

    element = document.createElement("select");
    element.setAttribute("style", my_dropdownstyle);

    document.getElementById("buttonsdiv").appendChild(element);
    element.setAttribute("id", "patternmenu");
    element.onchange=pick_pattern_action
    addOption(element, "Patterns", "state 1 via mouse")

        element = document.createElement("select");
    element.setAttribute("style", my_dropdownstyle);
    document.getElementById("buttonsdiv").appendChild(element);
    element.setAttribute("id", "speed_step_menu");
    element.onchange=choose_performance_action
    addOption(element, "Performance", 1)
    addOption(element, "Polygon Fill", "Polygon Fill")
    addOption(element, "Rect Fill", "Rect Fill")
    addOption(element, "Rect PutData", "Rect PutData")
    addOption(element, "Polygon WebGL", "Polygon WebGL")
    addOption(element, "Rect WebGL", "Rect WebGL")

    addOption(element, "---------", 1)

    addOption(element, "1 Gen/Step", 1)
    addOption(element, "2 Gens/Step", 2)
    addOption(element, "3 Gens/Step", 3)
    addOption(element, "4 Gens/Step", 4)
    addOption(element, "5 Gens/Step", 5)
    addOption(element, "6 Gens/Step", 6)
    addOption(element, "98 Gen/Step", 98)
    addOption(element, "99 Gen/Step", 99)

    addOption(element, "---------", 1)

    addOption(element, "Use Set Interval", "Use Set Interval")
    addOption(element, "Use Requst Animation", "Use Requst Animation")




   makebutton("randombutton", "width: 40px; "+my_buttonstyle ,"Test", "buttonsdiv",call_make_random_field_with_top); 
   makebutton("clearbutton", "width: 40px; "+my_buttonstyle, "Clear", "buttonsdiv",clear_field)


  // document.getElementById("buttonsdiv").innerHTML += "<hr>";

    element = document.createElement("div");
    element.setAttribute("id", "testdiv")
    element.setAttribute("style", "-webkit-user-select: none; -moz-user-select: none; -ms-user-select: none; font-size:10px; ")   //  word-wrap: break-word;
    // this also might work: -webkit-tap-highlight-color:transparent;
    document.getElementById("buttonsdiv").appendChild(element)



    element = document.createElement("select");
    element.setAttribute("style", my_dropdownstyle);
    document.getElementById("buttonsdiv").appendChild(element);
    element.setAttribute("id", "tiling_menu");
    element.onchange=choose_tiling_action
    addOption(element, "Tiling",  "Mesh Info")
    addOption(element, "Export Mesh",  "Export Mesh")
    addOption(element, "Export Selection",  "Export Selection")
    addOption(element, "Mesh Info", "Mesh Info")
    addOption(element, "Mesh ShowHoles", "Mesh Check")
    addOption(element, "---------", "Mesh Info")

    addOption(element, "Triangle Subdivide", "Triangle Subdivide" )
    addOption(element, "Tri Subdivide Concave-Only", "TriSubdivConcaveOnly" )

    addOption(element, "Quad Subdivide", "Subdivide Mesh" )
    addOption(element, "Pentagon Subdivide", "Pentagonalization" )
    addOption(element, "---------", "Mesh Info")

    addOption(element, "Triangle Surround", "Inset Subdivide")
    addOption(element, "Triangle Corners", "Triangle Corners" )
    addOption(element, "Quad Surround", "Waffle Subdivide" )
    addOption(element, "Pentagon Surround", "Selfridge Subdivide" )
    addOption(element, "Hexagon Surround", "Chamfer Tiling" )
    addOption(element, "Honeycombization", "Honeycombization" )

    addOption(element, "---------", "Mesh Info")
    addOption(element, "Dual Tiling", "Dual Tiling" )
    addOption(element, "Truncate Tiling", "Truncate Tiling" )
    addOption(element, "Truncate(1/4) Tiling", "Truncate(1/4) Tiling" )
    addOption(element, "Rectify Tiling", "Rectify Tiling" )
    // candiate for deletion addOption(element, "Alternation Tiling", "Alternation Tiling" )
    addOption(element, "Snub Tiling", "Snub Tiling" )
    addOption(element, "Stellate Tiling", "Stellate Tiling" )
    addOption(element, "Life On The Edge", "Life On The Edge" )


    addOption(element,  "Unit Edge Dual", "Unit Edge Dual")

   // addOption(element, "Delaunay Experiment", "Delaunay Tiling" )

    addOption(element, "Voronoi Experiment", "Call Voronoi" )
    addOption(element,  "Gen Experiment2", "Gen Experiment2")
    addOption(element, "Enable Wrap-around", "Enable Wrap-around")




    makebutton("zoominbutton", "width: 60px; "+my_buttonstyle,           "Zoom In", "buttonsdiv",increase_magnification)
    makebutton("zommoutbutton", "width: 70px; "+my_buttonstyle,          "Zoom Out","buttonsdiv", decrease_magnification)


   makebutton("creditsbutton", "width: 90px; "+my_buttonstyle,"Credits & Links", "buttonsdiv",increase_magnification)



    element = document.createElement("div");
    element.setAttribute("id", "canvasdiv")
    element.setAttribute("style", "position: relative;")
   // element.setAttribute("style", "width: 400px; height: 400px; overflow: auto;")

    document.getElementById("MainBody").appendChild(element);
 
    element = document.createElement("canvas");
    element.setAttribute("id", "pjs")
    element.setAttribute("style", "border:1px solid #c3c3c3; position: absolute;  -webkit-user-select: none;  ")
    document.getElementById("canvasdiv").appendChild(element);
    ctx = document.getElementById('pjs').getContext('2d');
    ctx.canvas.width  = the_dimension   //  until these become variables:  if this value changes, change mousemove
    ctx.canvas.height = the_dimension
 

    element = document.createElement("canvas");
    element.setAttribute("id", "webglcanvas")
    element.setAttribute("style", "position: absolute; ")
    document.getElementById("canvasdiv").appendChild(element);
    element.width  = the_dimension   //  until these become variables:  if this value changes, change mousemove
    element.height = the_dimension
    // the context for this canvas will be created in setup_webgl
  
    element = document.createElement("canvas");
    element.setAttribute("id", "layer_Grid")
    element.setAttribute("style", "position: absolute; ")
    document.getElementById("canvasdiv").appendChild(element);
    ctxGrid = document.getElementById('layer_Grid').getContext('2d');
    ctxGrid.canvas.width  = the_dimension   //  until these become variables:  if this value changes, change mousemove
    ctxGrid.canvas.height = the_dimension



    element = document.createElement("canvas");
    element.setAttribute("id", "layer2")
    element.setAttribute("style", "position: absolute; ")
    document.getElementById("canvasdiv").appendChild(element);
    ctx2 = document.getElementById('layer2').getContext('2d');
    ctx2.canvas.width  = the_dimension   //  until these become variables:  if this value changes, change mousemove
    ctx2.canvas.height = the_dimension




 //  ctx2.fillStyle =  "rgba(0,0,0,0)"
//   ctx2.fillRect(0,0, the_dimension, the_dimension);

    greenish_select_color = "rgba(105, 135, 76, 0.4)" // "#69874C"  
   // ctx2.fillStyle = greenish_select_color;
  // ctx2.fillRect(200,200, 100,100);


    document.getElementById("canvasdiv").onmousemove = cnvs_mousemove;
    document.getElementById("canvasdiv").onmouseup = cnvs_mouseup;
    document.getElementById("canvasdiv").onmousedown = cnvs_mousedown;
/*
    document.getElementById("pjs").onmousemove = cnvs_mousemove;
    document.getElementById("pjs").onmouseup = cnvs_mouseup;
    document.getElementById("pjs").onmousedown = cnvs_mousedown;
 */

    document.getElementById("MainBody").onkeydown = interpret_keys;
     var info_div_position = the_dimension + 60

    var element = document.createElement("div");
    element.setAttribute("id", "infodiv")
    element.setAttribute("style", "position: absolute; top:" + info_div_position +";width:980;font-size: 80%;background-color:#b0c4de")   // Width tester!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
    document.getElementById("MainBody").appendChild(element);
    var element = document.createElement("textarea");
    element.setAttribute("id", "infodiv_textarea")
    document.getElementById("infodiv").appendChild(element);
    element.innerHTML = "Rule = B3/S23"

    my_offsetHeight = document.getElementById('buttonsdiv').offsetHeight  // global variable - used in code for mouseup mousedown etc



}


var ctx;

var maxLevel = 4;  

//******************************************************
var sigdigits = 2    //  Try 2, try 6, depending on intial set up of the tiling generator
//******************************************************


var timer_is_on=0; // this has nothing to do with actually timing anything - this is just "timer code" reuse.
var delay = 0;
var t; // for timer code


var initialize_flag = 0

var the_dimension =   window.innerWidth*0.9 // 980 //  2880


/*
will be useful

http://forum.processing.org/topic/filled-polygons-are-significantly-slowing-down-performance

http://go.yuri.at/some-notes-on-difficult-to-debug-processing-js-errors/

http://colorschemedesigner.com/#

VTU format
http://www.earthmodels.org/software/vtk-and-paraview/vtk-file-formats

Zip format
http://stackoverflow.com/questions/2095697/unzip-files-using-javascript

hit test -- point in polygon
wow:  http://www.ecse.rpi.edu/Homepages/wrf/Research/Short_Notes/pnpoly.html#Explanation
and also http://stackoverflow.com/questions/217578/point-in-polygon-aka-hit-test


http://www.lemoda.net/canvas/scribbler/



Dynamically add buttons etc, so that the javascript file can do the work instead of the html wrapper
http://viralpatel.net/blogs/dynamic-add-textbox-input-button-radio-element-html-javascript/

http://stackoverflow.com/questions/5490996/load-scripts-after-page-has-loaded
http://jsfiddle.net/Ea4kc/
http://stackoverflow.com/questions/8467822/html5-canvas-calculate-the-mouseposition-after-zooming-and-translating
http://stackoverflow.com/questions/9300590/html5-coordinates-of-a-scaled-canvas-element
http://stackoverflow.com/questions/1114465/getting-mouse-location-in-canvas

http://www.w3schools.com/tags/canvas_scale.asp
http://www.w3schools.com/svg/svg_path.asp


http://processingjs.org/learning/
http://processingjs.org/articles/PomaxGuide.html  >=== entering data into your processing script via xml and such. could be generally useful for javascript

http://www.logarithmic.net/ghost.xhtml#
http://stackoverflow.com/questions/9458239/svg-paths-to-canvas-paths

http://geometricolor.wordpress.com/page/15/
http://texgraph.tuxfamily.org/pavages/aperiodiques.html  <--- awesome version of the tiling encyclopedia

*/




function roundNumber(rnum, rlength) { // Arguments: number to round, number of decimal places
   return Math.round(rnum*Math.pow(10,rlength))/Math.pow(10,rlength);
}



function cellvertex (index, x, y, partof) {
 this.index = index;
 this.x = x;
 this.y = y;
 this.partof = partof;
}

function cell(index, coords, fnn, neighbors, VNneighbors, state, placeholder_state, message, type) {
  this.index  = index;   // not needed if we have coords?
  this.coords = coords; // not needed if we have index?
  this.fnn = fnn; // an array of vertex indices
  this.neighbors = neighbors; 
  this.VNneighbors = VNneighbors;
  this.state = state;
  this.placeholder_state = placeholder_state;
  this.message = message;
  this.type  = type; // it would be nice to say rhomb or square - dimensions can be measured from coords if not from initial shape generation stage.
}
  
var settypecell =  new cell(0, [],[], [],[], 0,0,  "message", "shape")
var settypecellvertex = new cellvertex(0, 0, 0, [])

var vertexArray = [] 
var v_index = 1;
vertexArray[0] = settypecellvertex  // This shouldn't be used, but prevents errors if v_index goes to zero instead of starting/stopping at 1.

var allcells = [] 
var aci = 0;  // aci stands for All Cells Index

var x_to_verts = []; flr_x_to_verts = []
var y_to_verts_for_wrap = []
var x_to_verts_for_wrap = []  // this is an experimental stand-in for x_to_verts

// this is for mesh info om=n
function mesh_info(report) {
  countarray = []
  var n = 0
  for (var i = aci; i--;) {
     n = allcells[i].neighbors.length
     var border = "no"
     for (var k = n; k--;) {  // go through the primary cell's neighbors -- if the neighbor is a border cell, the primary cell might secretly be one too, since border checks only work for neighbors which share an edge with the border, and the primary might share only a vertice with the border. 
        var a_neighbor = allcells[i].neighbors[k];
        if( a_neighbor.VNneighbors.length != a_neighbor.fnn.length) {border = "yes"}
      }
      


     if ((border == "no")  && (allcells[i].VNneighbors.length == allcells[i].fnn.length)) // This is the check from mesh_check below 
                                                                   // to see if the primary cell is surrounded by neighbors 
                                                                   // as opposed to being on the border or next to a hole.
        {
              countarray[n] = 1
         }
  }
  var answer = "Total number of cells = " + aci + ".  Cells which are not on the border will have one of the following number of neighboring cells: "
  for (var j = 0; j < countarray.length; j++) {
      if (countarray[j] == 1) { answer = answer + j + " "}
   }
   if (report == true|| report ==  "report") {alert(answer)}
   return countarray.length - 1
}
      

function mesh_check() {
   for (var i = aci; i--;) {
      if (allcells[i].VNneighbors.length != allcells[i].fnn.length) {allcells[i].state = 1}
   }
   drawboard()
}


// find_angle would be useful for characterizing tiles with the same number of sides, such as thick rhombs from thin rhombs.
//  See also parse_OBJ_entries regarding distinguishing triangles from squares from pentagons, etc.
// How to find the angle, given  lines  from point p0 to point C and from point C to point P1.
// from http://stackoverflow.com/questions/1211212/how-to-calculate-an-angle-from-three-points

/*
function find_angle_original(p0, c, p1) {   // requires structured inputs with .x and .y
    var p0c = Math.sqrt(Math.pow(c.x-p0.x,2)+
                        Math.pow(c.y-p0.y,2)); // p0->c (b)   
    var p1c = Math.sqrt(Math.pow(c.x-p1.x,2)+
                        Math.pow(c.y-p1.y,2)); // p1->c (a)
    var p0p1 = Math.sqrt(Math.pow(p1.x-p0.x,2)+
                         Math.pow(p1.y-p0.y,2)); // p0->p1 (c)
    return Math.acos((p1c*p1c+p0c*p0c-p0p1*p0p1)/(2*p1c*p0c));
}
*/

function find_angle(p0x, p0y, cx, cy, p1x, p1y) {

    var p0c = Math.sqrt(Math.pow(cx-p0x,2)+
                        Math.pow(cy-p0y,2)); // p0->c (b)   
    var p1c = Math.sqrt(Math.pow(cx-p1x,2)+
                        Math.pow(cy-p1y,2)); // p1->c (a)
    var p0p1 = Math.sqrt(Math.pow(p1x-p0x,2)+
                         Math.pow(p1y-p0y,2)); // p0->p1 (c)
    return Math.acos(((p1c*p1c+p0c*p0c-p0p1*p0p1)/(2*p1c*p0c)))*180/Math.PI;
}

function categorize_shapes_by_internal_angle() {
   for (var i = aci; i--;) {
      var cel = allcells[i]
      var cc = cel.coords
      cel.internal_angle = find_angle(cc[0], cc[1], cc[2], cc[3], cc[4], cc[5])
    }
}


function categorize_quads_by_orientation (maxdegrees) {   // candidate for deletion in favor of the function below
   for (var i = aci; i--;) {
      var cel = allcells[i]
      var cc = cel.coords
      var angle = roundNumber(Math.atan2((cc[6] - cc[2]),(cc[7] - cc[3]))*180/Math.PI, 0)
      cel.orientation = (maxdegrees - angle) % maxdegrees
    }
}

function categorize_shapes_by_orientation (maxdegrees) {
   for (var i = aci; i--;) {
      var cel = allcells[i]
      var cc = cel.coords
      var angle = roundNumber(Math.atan2((cc[0] - cc[4]),(cc[1] - cc[5]))*180/Math.PI, 0)
      cel.orientation = (maxdegrees - angle) % maxdegrees
    }
}


function color_by_neighbor_count(arr) {
  clear_field ()
   for (var i = aci; i--;) {
      var n = allcells[i].neighbors.length    
        for (var j = 0; j < arr.length; j++) {
           if (arr[j] == n) {allcells[i].state = j+1}
        }
    }
   drawboard()
}
// instead of inner for loop, you could use  // if (contains(n, arr))  {allcells[i].state = 1}


function get_nonbackground_cells() {
  var nonbackgroundcells = []
   for (var i = aci; i--;) {
      if (allcells[i].state != 0) {nonbackgroundcells.push(allcells[i])}    
   }
  return nonbackgroundcells
}

// redundant with above, except for how it treates states > 1
function whoisalive() {
     var result = [];
   for (var i = aci; i--;) {
      if (allcells[i].state == 1) {result.push(allcells[i])}

   }
  return result
}



function contains(value, arr) {
    var i = arr.length;
    while (i--) {
        if (arr[i] == value) return true;
    }
    return false;
}

//  intersection of arrays 
// From: http://stackoverflow.com/questions/11076067/finding-matches-between-multiple-javascript-arrays
// this function is currently also embedded in the penrose module.  

function intersection_of_arrays(/* pass all arrays here */) {
    var output = [];
    var cntObj = {};
    var array, item, cnt;
    // for each array passed as an argument to the function
    for (var i = 0; i < arguments.length; i++) {
        array = arguments[i];
        // for each element in the array
        for (var j = 0; j < array.length; j++) {
            item = "-" + array[j];
            cnt = cntObj[item] || 0;
            // if cnt is exactly the number of previous arrays, 
            // then increment by one so we count only one per array
            if (cnt == i) {
                cntObj[item] = cnt + 1;
            }
        }
    }
    // now collect all results that are in all arrays
    for (item in cntObj) {
        if (cntObj.hasOwnProperty(item) && cntObj[item] === arguments.length) {
            output.push(item.substring(1));
        }
    }
    return(output);
}    

   

/*
This function is currently unused because Moore neighbors are now pre-calculated
function Moore_neighbors_p (alphafnn, betafnn) {
   for (var i = alphafnn.length; i--;) {
      if (contains(alphafnn[i], betafnn)) {return true};
   } 
   return false;
}

*/


/*  this function is currently unused because VN neighbors are now pre-calculated
function VN_neighbors_p (alphafnn, betafnn) {
   var sharedvertices = 0;
   for (var i = alphafnn.length; i--;) {
      if (contains(alphafnn[i], betafnn)) {sharedvertices +=  1};
   } 
    return (sharedvertices > 1)   //if (sharedvertices >= 2) {return true} else {return false}
}
*/


      
function c_contains(value, arr) {
  // this is a version of contains where "value" is a cell index but "arr" is an array of cells (rather than an array of cell indices)
    var i = arr.length;
    while (i--) {
        if (arr[i].index == value) return true;
    }
    return false;
}



function initialize_neighbors() {  
   for (var i = aci; i--;) {
     allcells[i].neighbors = []
     allcells[i].VNneighbors = []
     for (var j = allcells[i].fnn.length; j--;) {
        for (var k = vertexArray[allcells[i].fnn[j]].partof.length; k--;) {
           var a_cell_index = vertexArray[allcells[i].fnn[j]].partof[k]
           if (a_cell_index != i) { 
               if (c_contains(a_cell_index, allcells[i].neighbors) == false) {    
                  allcells[i].neighbors.push(allcells[a_cell_index])
               }
               else if (c_contains(a_cell_index, allcells[i].VNneighbors) == false) { 
                   allcells[i].VNneighbors.push(allcells[a_cell_index])
              }

           }
        }
     }
   }
}


function make_mesh_wraparound() { 
   x_to_verts_for_wrap = []
   y_to_verts_for_wrap = []
  
    //  setup_to_verts_rows_cols ()  (code immediately below instead of as a seperate function)
   var border_vert_indices = []
   for (var v = 1; v<v_index; v++) {
     var vert = vertexArray[v]
     if (border_neighbor_vertices(vert, allcells).length) {border_vert_indices.push(v)}
   }

  var meshrows = []
  var meshrowsindex = 0
  var meshcols = []
  var meshcolsindex = 0
  var vx, vy;
  for (var i = border_vert_indices.length; i--;) {
     vx = vertexArray[ border_vert_indices[i] ].x    // <-- roundoff to some very small fraction of shapesize?
     vy = vertexArray[ border_vert_indices[i] ].y    //  <-- roundoff to some very small fraction of shapesize?  will still have roundoff error this way


     if (x_to_verts_for_wrap[vx] == undefined) { x_to_verts_for_wrap[vx] = []; meshcols[meshcolsindex] = vx; meshcolsindex +=1; }
     x_to_verts_for_wrap[vx].push(border_vert_indices[i])

     if (y_to_verts_for_wrap[vy] == undefined) { y_to_verts_for_wrap[vy] = []; meshrows[meshrowsindex] = vy; meshrowsindex +=1; }
     y_to_verts_for_wrap[vy].push(border_vert_indices[i])
  }
    // End of setup_to_verts_rows_cols code

     var newpartoflist;
     var arowofverts;
     var acolofverts;

     // X wraparound section
     for (var i = meshrowsindex; i--;) {
          var arow = y_to_verts_for_wrap[meshrows[i]]
                  // find left and rightmost in arow of verts, share partof lists
          var xminindex = 0;
          var xmaxindex = 0;
          for (var j = arow.length; j--;) {
              if (vertexArray[arow[j]].x > vertexArray[arow[xmaxindex]].x) {xmaxindex = j}
              if (vertexArray[arow[j]].x < vertexArray[arow[xminindex]].x) {xminindex = j}

           }
          // console.log(newpartoflist)  // usually yields three faces

          newpartoflist = vertexArray[arow[xminindex]].partof.concat(vertexArray[arow[xmaxindex]].partof)
          vertexArray[arow[xminindex]].partof = newpartoflist;
          vertexArray[arow[xmaxindex]].partof = newpartoflist;
     }

     // Y wraparound section
     for (var i = meshcolsindex; i--;) {
          var acol = x_to_verts_for_wrap[meshcols[i]]
                  // find top and bottommost in a column of verts, share partof lists
          var yminindex = 0;
          var ymaxindex = 0;
          for (var j = acol.length; j--;) {
              if (vertexArray[acol[j]].y > vertexArray[acol[ymaxindex]].y) {ymaxindex = j}
              if (vertexArray[acol[j]].y < vertexArray[acol[yminindex]].y) {yminindex = j}

           }
          //if (i == 3) {console.log(vertexArray[acol[yminindex]]) }
         // if (i == 3) {console.log(vertexArray[acol[yminindex]].y) }
        //  if (i == 3) {console.log(vertexArray[acol[ymaxindex]]) }
       //   if (i == 3) {console.log(vertexArray[acol[ymaxindex]].y) }

          newpartoflist = vertexArray[acol[yminindex]].partof.concat(vertexArray[acol[ymaxindex]].partof)
       //  if (i == 3) { console.log(newpartoflist)} // yeilds multiple copies of the same face.  
          vertexArray[acol[yminindex]].partof = newpartoflist;
          vertexArray[acol[ymaxindex]].partof = newpartoflist;
     }
    initialize_neighbors()
}






















/*  Experimental



 */   
       


function one_alive_state_neighbor_count(c){
  var result = 0;
  for (var i =  c.neighbors.length; i--;) {
     if (c.neighbors[i].state == 1) {result += 1}   
  }
  return result
}

function state_neighbor_count(c, statenum){
  var result = 0;
  for (var i =  c.neighbors.length; i--;) {
     if (c.neighbors[i].state == statenum) {result += 1}
  }
  return result
}


function state_VNneighbor_count(c, statenum){
  var result = 0;
  for (var i =  c.VNneighbors.length; i--;) {
     if (c.VNneighbors[i].state == statenum) {result += 1}
  }
  return result
}

/*  old
function state_Corner_neighbor_count(c, statenum){
  var result = 0;
  for (var i =  c.neighbors.length; i--;) {
     if (c.neighbors[i].state == statenum && !(contains(c.neighbors[i], c.VNneighbors))) {result += 1}
  }
  return result
}
*/

// I wonder if it would be faster to say state_neighbor_count minus VNneighborcount?


function state_Corner_neighbor_count(c, statenum){
   return state_neighbor_count(c, statenum) -  state_VNneighbor_count(c, statenum)
}



// Functions for Conway worms based on the orientation of the tile (ie the angle of the tile)

function legalangle(a) {
   return (360 + a) % 360
}

function orientation_state_count(c, statenum, orientations) {
  var result = 0;
  for (var i =  c.neighbors.length; i--;) {
     if (c.neighbors[i].state == statenum && contains(c.neighbors[i].orientation, orientations)) {result += 1}
  }
  return result
}

function orientation_type_one_alive_count (c, type, orientations) {
  var result = 0;
  for (var i =  c.neighbors.length; i--;) {
     var neighborcell = c.neighbors[i]
     if (neighborcell.state == 1 && 
         neighborcell.type == type && 
         contains(neighborcell.orientation, orientations)) 
       {result += 1}
  }
  return result
}





// The following three functions are for 2a, 2e, and 2i   
//   Three different ways of defining when a cell has two neigbors which are adjacent to each other

function adjacent_two_VNneighbors(c) {
  var first = false;
  var second = false;
  for (var i =  c.neighbors.length; i--;) {
     if (c.neighbors[i].state == 1 && first == false)  {first = c.neighbors[i]}
     else if (c.neighbors[i].state == 1 && second == false)  {second = c.neighbors[i]}
  }
  return contains(first, second.VNneighbors)  
                                               // can also try Moore but not VN - which would be every other neighbor for some tilings
} 


function adjacent_two_Moore_neighbors(c) {
  var first = false;
  var second = false;
  for (var i =  c.neighbors.length; i--;) {
     if (c.neighbors[i].state == 1 && first == false)  {first = c.neighbors[i]}
     else if (c.neighbors[i].state == 1 && second == false)  {second = c.neighbors[i]}
  }
  return contains(first, second.neighbors)  
     // can also try Moore but not VN - which would be every other neighbor for some tilings
}    
 

// special rule for ants (maybe generalizable for 2nd_order reversible rules)
// treats state 3 like state 1
function adjacent_two_ant_Moore_neighbors(c) {
  var first = false;
  var second = false;
  for (var i =  c.neighbors.length; i--;) {
     if ((c.neighbors[i].state == 1 || c.neighbors[i].state ==  3) && first == false)  {first = c.neighbors[i]}
     else if ((c.neighbors[i].state == 1 || c.neighbors[i].state ==  3)  && second == false)  {second = c.neighbors[i]}
  }
  return contains(first, second.neighbors)  
     // can also try Moore but not VN - which would be every other neighbor for some tilings
}    


/*
function adjacent_two_Moore_neighbors(c, statenum) {
  if (statenum == undefined) {statenum = 1}  // an optional statenum can be included for all these functions
  var first = false;
  var second = false;
  for (var i =  c.neighbors.length; i--;) {
     if (c.neighbors[i].state == statenum && first == false)  {first = c.neighbors[i]}
     else if (c.neighbors[i].state == statenum && second == false)  {second = c.neighbors[i]}
  }
  if (first && second) { return contains(first, second.neighbors) }
  else {return false}
       // can also try Moore but not VN - which would be every other neighbor for some tilings
}    
*/

function adjacent_two_Corner_neighbors(c) {
  var first = false;
  var second = false;
  for (var i =  c.neighbors.length; i--;) {
     if (c.neighbors[i].state == 1 && first == false)  {first = c.neighbors[i]}
     else if (c.neighbors[i].state == 1 && second == false)  {second = c.neighbors[i]}
  }
  return  (  !( contains(first, second.VNneighbors) ) &&  contains(first, second.neighbors)  )  
           // The two neighbors are NOT VN neighbors, but are still neighbers => They are corner neighbors
} 



// B013469/S02 - rule with nice oscillators


       // B013469/S02 - rule with nice oscillators



function ca_rule3(c) {
   switch(rulefamily) {

      case 1:     // Life-like
         n = one_alive_state_neighbor_count(c);
         if (c.state) { return survivalrule[n] } else { return birthrule[n] };     // define cstate and then say if (cstate == 0){ return birthrule[n] } else if (cstate == 1) {  return survivalrule[n] }
         break;

      case 2:   // Generations
         n = one_alive_state_neighbor_count(c);
         cstate = c.state;
         if (cstate == 0){ return birthrule[n] };
         if (cstate == 1) { 
            if ( survivalrule[n] == 1) {return 1} else {return 2} 
         };
         if (cstate < (n_states - 1)) {return (cstate + 1)} else {return 0};
         break;

      case 3:  // VN_lifelike and VN_Generations combined 
         n = state_VNneighbor_count(c, 1);
         cstate = c.state;
         if (cstate == 0){ return birthrule[n] };
         if (cstate == 1) { 
            if ( survivalrule[n] == 1) {return 1} 
            else {if (n_states == 2) {return 0} 
                  else {return 2} };
         };
         if (cstate < (n_states - 1)) {return (cstate + 1)} else {return 0};
         break;

      case 4:  // Bx2 && VN 
         n = state_VNneighbor_count(c, 1);
         cstate = c.state;
         if (cstate == 0){ return birthrule[n] * 2 };  // return state 2 for intermediate state.
         if (cstate == 2){ return birthrule[n] };      // return state 1 for promoted to alive state
         return survivalrule[n]                      // if cstate == 1 (or anything else), treat as an alive state
         break;

      case 5:  // Bx2 && Moore 
         n = state_neighbor_count(c, 1);
         cstate = c.state;
         if (cstate == 0){ return birthrule[n] * 2 };  // return state 2 for intermediate state.
         if (cstate == 2){ return birthrule[n] };      // return state 1 for promoted to alive state
         return survivalrule[n]                      // if cstate == 1 (or anything else), treat as an alive state
         break;         
         
     
      case 6:   // Goucher Glider Rule  -- code translated from Ready 
         n1 = state_neighbor_count(c, 1)
         n2 = state_neighbor_count(c, 2)
         n3 = state_neighbor_count(c, 3)
         cstate = c.state;
         if (cstate == 0)   {
            if(n1>0 && n2>0)   {return 3}
            else if(n1>0 && n3>=2)  {return 1}
            else  {return  0}
         }
         else if(cstate==1)  { // head 
            if(n3>0) { return 2}
            else {  return 1}
         }
         else if(cstate==2)  {  return 3}  // tail
         else { return 0}
         break;
      case 7:  //   VN_head_Moore_tail
         VN_n1 = state_VNneighbor_count(c, 1)
         n2 = state_neighbor_count(c, 2)  // Not VN 
         cstate = c.state;
         if (cstate == 0) {
            if (VN_n1 == 1 && n2 == 0) {return 1} else {return 0}
          }
         if (cstate == 1) {return 2}
         if (cstate == 2) {return 0}
         else {return 0}                                         
         break;
      case 8:   //  my Tri-Goucher Glider Rule 
         n1 = state_neighbor_count(c, 1)
         n2 = state_neighbor_count(c, 2)
         n3 = state_neighbor_count(c, 3)
         VN_n3 = state_VNneighbor_count(c, 3)
         cstate = c.state;
         if (cstate == 0)   {
            if(n1>0 && n2>0)   {return 3}
            else if (n1>0 && n3>=2 && VN_n3==0) {return 1}  // original: else if(n1>0 && n3>=2)  {return 1} 
            else  {return  0}
         }
         else if(cstate==1)  { // head 
            if(n3>0) { return 2}
            else {  return 1}
         }
         else if(cstate==2)  {  return 3}  // tail
         else { return 0}
         break;
      case 9:
         n = state_neighbor_count(c, 1);
         cstate = c.state;
         if (n == 2) { if (cstate == 0 && Bnontotal_flag == "2a" && adjacent_two_VNneighbors(c)) {return 1} 
                       if (cstate == 0 && Bnontotal_flag == "2-a" && adjacent_two_VNneighbors(c) == false) {return 1} 
                       if (cstate == 1 && Snontotal_flag == "2a" && adjacent_two_VNneighbors(c)) {return 1} 
                       if (cstate == 1 && Snontotal_flag == "2-a" && adjacent_two_VNneighbors(c) == false) {return 1} 
                       return 0
         }
        
         else {
              if (cstate == 0){ return birthrule[n] };
              if (cstate == 1) { 
                 if ( survivalrule[n] == 1) {return 1} 
                 else {if (n_states == 2) {return 0} 
                       else {return 2} };
               };

              if (cstate < (n_states - 1)) {return (cstate + 1)} else {return 0};
         }
         break;

      case 10:  // Corner_lifelike and Corner_Generations combined  - this is like VN but for corners instead.
         n = state_Corner_neighbor_count(c, 1);
         cstate = c.state;
         if (cstate == 0){ return birthrule[n] };
         if (cstate == 1) { 
            if ( survivalrule[n] == 1) {return 1} 
            else {if (n_states == 2) {return 0} 
                  else {return 2} };
         };
         if (cstate < (n_states - 1)) {return (cstate + 1)} else {return 0};
         break;
      case 11:  // Bx2 && Corner
         n = state_Corner_neighbor_count(c, 1);
         cstate = c.state;
         if (cstate == 0){ return birthrule[n] * 2 };  // return state 2 for intermediate state.
         if (cstate == 2){ return birthrule[n] };      // return state 1 for promoted to alive state
         return survivalrule[n]                      // if cstate == 1 (or anything else), treat as an alive state
         break;
      case 12:
         n = state_neighbor_count(c, 1);
         cstate = c.state;
         if (n == 2) { if (cstate == 0 && Bnontotal_flag == "2e" && adjacent_two_Moore_neighbors(c)) {return 1} 
                       if (cstate == 0 && Bnontotal_flag == "2-e" && adjacent_two_Moore_neighbors(c) == false) {return 1} 
                       if (cstate == 1 && Snontotal_flag == "2e" && adjacent_two_Moore_neighbors(c)) {return 1} 
                       if (cstate == 1 && Snontotal_flag == "2-e" && adjacent_two_Moore_neighbors(c) == false) {return 1} 
                       return 0
         }
        
         else {
              if (cstate == 0){ return birthrule[n] };
              if (cstate == 1) { 
                 if ( survivalrule[n] == 1) {return 1} 
                 else {if (n_states == 2) {return 0} 
                       else {return 2} };
               };

              if (cstate < (n_states - 1)) {return (cstate + 1)} else {return 0};
         }
         break;
      
// state_Corner_neighbor_count

     case 13:  //  Corner head Moore Tail   Just like case 7 but with corners only instead of VN only
         VN_n1 = state_Corner_neighbor_count(c, 1)
         n2 = state_neighbor_count(c, 2)  // Not VN 
         cstate = c.state;
         if (cstate == 0) {
            if (VN_n1 == 1 && n2 == 0) {return 1} else {return 0}
          }
         if (cstate == 1) {return 2}
         if (cstate == 2) {return 0}
         else {return 0}                                         
         break;


      case 14:   // worm rule 1
         var cangle = c.orientation
         if (c.type == "dart") {
             var n1 = orientation_type_one_alive_count (c, "kite", [ legalangle(cangle + 108), legalangle(cangle -108)])
            // var n2 = orientation_type_one_alive_count (c, "dart", [legalangle(cangle + 72), legalangle(cangle -72)])
          n = n1 //   n = n1+n2

         } else { // if c.type == "kite"
             var n1 = orientation_type_one_alive_count (c, "kite", [ legalangle(cangle + 216), legalangle(cangle -216)])

             var n2 = orientation_type_one_alive_count (c, "dart", [legalangle(cangle + 108), legalangle(cangle -108)])
             n = n1+n2


         }
         cstate = c.state;
         if (cstate == 0){ return birthrule[n] };
         if (cstate == 1) { 
            if ( survivalrule[n] == 1) {return 1} 
            else {if (n_states == 2) {return 0} 
                  else {return 2} };
         };
         if (cstate < (n_states - 1)) {return (cstate + 1)} else {return 0};
         break;

      case 15: // BorderRule
         cstate = c.state;
         n = one_alive_state_neighbor_count(c);
         if (n == 2  && cstate == 0) {if (adjacent_two_VNneighbors(c)) {return 1} else {return 0}}
         if (cstate == 0 ){ return birthrule[n] };
         if (cstate == 1) { return survivalrule[n]};
         if (cstate > 1) {return cstate}

         break;
     
    case 16:  // AlexV Snakes
         var cstate = c.state;
         var n_moore = one_alive_state_neighbor_count(c) +  state_neighbor_count(c, 3)
         var n_vn =  state_VNneighbor_count(c, 1) +  state_VNneighbor_count(c, 3)
         if (cstate == 0) {if ((n_moore == 1) && (n_vn == 1)) {return 1} else {return 0}}
         if (cstate == 1) {if ((n_moore == 1) && (n_vn == 1)) {return 3} else {return 2}}
         if (cstate == 3) {if ((n_moore == 1) && (n_vn == 1)) {return 2} else {return 3}}
         if (cstate == 2) {if ((n_moore == 1) && (n_vn == 1)) {return 0} else {return 1}}

         break;
    case 17:  // AlexV Ants
         var cstate = c.state;
         var n_moore = one_alive_state_neighbor_count(c) +  state_neighbor_count(c, 3)
         var n_vn =  state_VNneighbor_count(c, 1) +  state_VNneighbor_count(c, 3)
         var Hensel_1e = ((n_moore == 1) && (n_vn == 1)) 
         var Hensel_2i = false
      
         if (n_moore == 2 && n_vn == 2 ) { 
            if  (adjacent_two_ant_Moore_neighbors(c) == false ) {
                  Hensel_2i = true
            }
         }
      
         var Hensel_1e2i = Hensel_1e ||  Hensel_2i
         if (cstate == 0) { if ( Hensel_1e2i ){return 1} else {return 0} }
         if (cstate == 1) { if ( Hensel_1e2i ){return 3} else {return 2} }
         if (cstate == 3) { if ( Hensel_1e2i ){return 2} else {return 3} }
         if (cstate == 2) { if ( Hensel_1e2i ){return 0} else {return 1} }

         break;


     

      default: return 0
   }
}
   

var birthrule =     [0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];   
var survivalrule =  [0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; 
birth1 = "B3"
survive1 = "S23"
var n_states = 2
var rulefamily = 1

var   Bnontotal_flag = "totalistic" 
var   Snontotal_flag = "totalistic" 

function parse_rule_string(inputrulestring) {
   birthrule =     [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];   
           // for weighted life, this doesn't work - may need more than 36 spaces
   survivalrule =  [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; 


   Bnontotal_flag = "totalistic" 
   Snontotal_flag = "totalistic" 

  if (inputrulestring.match("Goucher")) {
   birth1 = "Goucher Gliders"
   survive1 = ""
   rulefamily = 6;
   n_states = 3

  } 
  else if (inputrulestring.match("VN_head_Moore_tail")) {
   birth1 = "VN_head_Moore_tail"
   survive1 = ""
   rulefamily = 7;
   n_states = 3

  } 
  else if (inputrulestring.match("Corner_Head Moore_Tail")) {
   birth1 = "Corner_Head Moore_Tail"
   survive1 = ""
   rulefamily = 13;
   n_states = 3

  } 
  else if (inputrulestring.match("KiteWorm Rule-1")) {
   birth1 = "KiteWorm Rule-1"
   survive1 = ""
   birthrule =     [0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];   
   survivalrule =  [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; 
   n_states = 3
   rulefamily = 14;  
   if (allcells[1].orientation == undefined) {categorize_quads_by_orientation (360)} 
                                                     // consider using categorize_shapes_by_orientation instead
  } 
  else if (inputrulestring.match("AlexV Snakes")) {
   birth1 = "AlexV Snakes"
   survive1 = ""
   birthrule =     [0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];   
   survivalrule =  [0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; 
   n_states = 4
   rulefamily = 16;  
  } 
  else if (inputrulestring.match("AlexV Ants")) {
   birth1 = "AlexV Ants"
   survive1 = ""
   birthrule =     [0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];   
   survivalrule =  [0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; 
   n_states = 4
   rulefamily = 17;  
  } 



  else if (inputrulestring.match("my TriGou")) { 
   birth1 = "my TriGou"
   survive1 = ""
   rulefamily = 8;
  } 
  else {
 
  inputrulestring = inputrulestring.toUpperCase();
  inputrulestring = inputrulestring.replace("_", "/");

  inputrulestringparts = inputrulestring.split("/");
  	if (inputrulestringparts[0].match("B") != null) {
	   birth1 = inputrulestringparts[0];
           survive1 = inputrulestringparts[1];
          decay1 = inputrulestringparts[2];
 	  }
	 else {
           birth1 = inputrulestringparts[1];
           survive1 = inputrulestringparts[0];
    	   decay1 = inputrulestringparts[2];
 	  };
      
       
       if (decay1 == undefined) {n_states = 2} else {n_states = parseInt(decay1.replace("C", " "))};

       if (n_states ==  2) 
            {rulefamily = 1}  // lifelike  for now, non-totalistic later
       else {rulefamily = 2};   // Generations for now, Bx2 later.

       if (inputrulestring.match("X2") ) { 
           birth1 = birth1.replace("X2","");  // it is important to remove the "X2" so that the 2 isn't parsed as the birth rule B2.
// but put it back at the end for the menu entry, the save pattern entry, etc
           if (inputrulestring.match("V")) {rulefamily = 4} // Bx2 with Von Neuman neighobrs
           else if (inputrulestring.match("CORN") || inputrulestring.match("CNR") ) {rulefamily = 11}  // Bx2 with Corner neighbors
           else   {rulefamily = 5}   // Bx2 with Moore neighbors
           n_states = 3;   // not necessary but might be useful in the future
       }
       else if  ( inputrulestring.match("V")) {rulefamily = 3}
       else if  ( inputrulestring.match("BORDER")) {rulefamily = 15}

       else if  ( inputrulestring.match("CORN") || inputrulestring.match("CNR"))  {rulefamily = 10}
       else if  ( inputrulestring.match("2A") || inputrulestring.match("2-A") ) {rulefamily = 9}  // this precludes 2a and 2-a rules and von Neuman neighborhoods at the same time.
       else if  ( inputrulestring.match("2E") || inputrulestring.match("2-E") ) {rulefamily = 12}
         


        birth1 = birth1.split(",");
        survive1 = survive1.split(",");

        if (birth1[0].match("2A"))  {Bnontotal_flag = "2a"}
        else if (birth1[0].match("2-A"))  {Bnontotal_flag = "2-a"}

        if (survive1[0].match("2A"))  {Snontotal_flag = "2a"}
        else if (survive1[0].match("2-A"))  {Snontotal_flag = "2-a"}

        if (birth1[0].match("2E"))  {Bnontotal_flag = "2e"}
        else if (birth1[0].match("2-E"))  {Bnontotal_flag = "2-e"}

        if (survive1[0].match("2E"))  {Snontotal_flag = "2e"}
        else if (survive1[0].match("2-E"))  {Snontotal_flag = "2-e"}





	for (var i = 0; i<birth1[0].length; i++) {
           var x = parseInt( birth1[0][i]);
  	   if (x >= 0 && x <= 9) {birthrule[x] = 1};
	}
        
       // Then parse the rest as B,10,19,20,24
      	for (var i = 1; i<birth1.length; i++) {
           var x = parseInt( birth1[i]);
  	   if (!isNaN(x) ){birthrule[x] = 1};
	}
        



	for (var i = 0; i<survive1[0].length; i++) {
	   var x = parseInt( survive1[0][i]);
	   if (x >= 0 && x <= 9) {survivalrule[x] = 1};
	}

      	for (var i = 1; i<survive1.length; i++) {
           var x = parseInt( survive1[i]);
  	   if (!isNaN(x) ){survivalrule[x] = 1};
	}
         }  // end non-goucher section
 if (rulefamily == 4 || rulefamily == 4) {birth1 = birth1 + "x2"}
 return inputrulestring

}

function add_rule (rulestring) {
    // check if the rule is actually already on the list of rules
    var newrule_flag = 1;
    len = rulesmenu_data.length
    for (var i = 0; i<len; i++) {                                        
                                                                         
       if (rulestring == rulesmenu_data[i][0]) {                        // if the rulestring matches a known rule
           document.getElementById("rulesmenu").selectedIndex = i      // set the rulemenu to the old rule.
           newrule_flag = 0
           break;
          }
     }
    if (newrule_flag) {
                   var previousmenu = document.getElementById("rulesmenu").selectedIndex;
                   rulesmenu_data.splice(2,0,[rulestring]);  // add new rule to list of rules
                   put_rules_in_menu();
                   if (previousmenu > 0) {document.getElementById("rulesmenu").selectedIndex = previousmenu + 1};
     }
}

function after_changing_rule() {
  do_CA_rule_on_all_cells()
  generation += 1;

  //  clearboard()
  drawboard()
}
   

function change_rule () {
   var therulesmenu = document.getElementById("rulesmenu")
   choice =  therulesmenu.options[ therulesmenu.selectedIndex].value
   switch(choice) {
       case "PromptForRule": 
                   add_rule( parse_rule_string( prompt("Enter a Lifelike or Generations rule using  the Bxxx/Sxxx or Bxxx/Sxxx/Cx formats:")));
                   break;
       default: parse_rule_string(choice)
   }

  after_changing_rule()
}

function set_rule_from_outside (rulestring) {
  add_rule(parse_rule_string (rulestring))
  after_changing_rule()
}
    
   // # instead of default patterns, url-based rules and patterns
  // # use partof to calc (or pre-store) vn neighbors
  // #  Larger than life too?

   
   



function updatecells3() {
   for (var j=NewChangeCount; j--;){
      cel = newchangedcells[j];
      cel.state = cel.placeholder_state;
      changedcells[j] = cel;
    }
}



function refresh () {
 var cel = settypecell;
 var neighborcell = settypecell;
 var neighborlist = [];
 
 NewChangeCount = 0; 

 for (var i=changecount; i--;) {
   cel = changedcells[i];

   // first handle neighbors
   neighborlist = cel.neighbors;
   for (var j=neighborlist.length; j--;) {
      neighborcell = neighborlist[j];
      if (neighborcell.message != generation) {
         neighborcell.message = generation;
       
          neighborcell.placeholder_state = ca_rule3(neighborcell);
 
          if (neighborcell.placeholder_state != neighborcell.state) {
              newchangedcells[NewChangeCount] = neighborcell;
               NewChangeCount += 1;
            }
          }
   }
 // now almost repeat the code to handle cell itself
  if (cel.message != generation) {
       cel.message = generation;
       cel.placeholder_state = ca_rule3(cel);
       if (cel.placeholder_state != cel.state  ) { 
            newchangedcells[NewChangeCount] = cel;
            NewChangeCount += 1;
          } 
     }
  }
 
        updatecells3()
        changecount = NewChangeCount;
};




function do_CA_rule_on_all_cells() {
//  This is the slow way  -- see other functions for a faster way using a change list!!!
//   The slow way is useful just after the CA rule changes
   var cel;
   NewChangeCount = 0; 

   for (var i = aci; i--;) {
      cel = allcells[i];
      cel.message = generation;
      cel.placeholder_state = ca_rule3(cel);
      if (cel.placeholder_state != cel.state  ) { 
            newchangedcells[NewChangeCount] = cel;
            NewChangeCount += 1;
       }
   }
   updatecells3()
   changecount = NewChangeCount;
   
}





function clearboard () {
   ctx.save();
   ctx.setTransform(1, 0, 0, 1, 0, 0);  // Will always clear the right space
   ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
   ctx.restore(); 

   ctxGrid.save();
   ctxGrid.setTransform(1, 0, 0, 1, 0, 0);  // Will always clear the right space
   ctxGrid.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
   ctxGrid.restore(); 
}


function clear_field () {
   for (var i = aci; i--;) {
      allcells[i].state = 0;
      changedcells[changecount] = allcells[i];
      changecount += 1;

   }
 drawboard() 
}


   


function drawgrid() {
   for (var i = aci; i--;) {
       strokecell(allcells[i])
   }
}

function strokecell(x) {
  var c = x.coords;

       //ctx.fillStyle = colorlookup[x.state]
  ctxGrid.beginPath();   
  ctxGrid.moveTo(c[0], c[1]);
  for (var i=c.length; i-=2;) {
     ctxGrid.lineTo(c[i], c[i+1]);
  }
  ctxGrid.closePath();  
      // ctx.fill();
  ctxGrid.stroke();

}


/*

function drawboard() {
   for (var i = aci; i--;) {
        colorcell(allcells[i])
   }
}
*/

function drawboard() {
 switch(rendertype) {
   case 1:
     for (var i = aci; i--;) {
        colorcell(allcells[i])
      }
      break;
   case 2: 
      for (var i=aci; i--;){
          Rect_aabb_colorcell( allcells[i])
      }
      break;
   case 4:  // webgl polygons
     /*
      for (var i = 0 ; i<n_states; i++) {  // initialize i = 1 if drawing is not sticky
         webgl_pixel_triangles_by_color[i] = []
      }
      for (var i=aci; i--;){
          var st = allcells[i].state
        //  if (st) { // if st > 0  // use this if drawing is not sticky
            webgl_pixel_triangles_by_color[st] = webgl_pixel_triangles_by_color[st].concat(allcells[i].webglVertices)
        //  }
      }
      // now do the webgl render, per color:
      for (var i = 0 ; i<n_states; i++) {  // initialize i = 1 if drawing is not sticky
          gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(webgl_pixel_triangles_by_color[i]), gl.STATIC_DRAW);
          gl.uniform4f(colorLocation, webglcolorlookup[i][0], webglcolorlookup[i][1], webglcolorlookup[i][2], 1); 
          gl.drawArrays(gl.TRIANGLES, 0, webgl_pixel_triangles_by_color[i].length/2);
       }
     */
     for (var i = aci; i--;) {
     var st = allcells[i].state
     
        
           var triangles_by = allcells[i].webglVertices
        
      
      // now do the webgl render, per color:
          gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(triangles_by), gl.STATIC_DRAW);
          gl.uniform4f(colorLocation, webglcolorlookup[st][0], webglcolorlookup[st][1], webglcolorlookup[st][2], 1); 
          gl.drawArrays(gl.TRIANGLES, 0, triangles_by.length/2);
      }
     
      
      break;

    default:
      for (var i = aci; i--;) {
        colorcell(allcells[i])
      }
  }
}


function draw_changed () {
 switch(rendertype) {
   case 1:
      for (var i=changecount; i--;){
          colorcell( changedcells[i])
      }
      break;
   case 2: 
      for (var i=changecount; i--;){
          Rect_aabb_colorcell( changedcells[i])
      }
      break;
   case 4:  // webgl polygons
     
      for (var i = 0 ; i<n_states; i++) {  // initialize i = 1 if drawing is not sticky
         webgl_pixel_triangles_by_color[i] = []
      }
      for (var i=changecount; i--;){
          var st = changedcells[i].state
        //  if (st) { // if st > 0  // use this if drawing is not sticky
            webgl_pixel_triangles_by_color[st] = webgl_pixel_triangles_by_color[st].concat(changedcells[i].webglVertices)
        //  }
      }
      // now do the webgl render, per color:
      for (var i = 0 ; i<n_states; i++) {  // initialize i = 1 if drawing is not sticky
          gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(webgl_pixel_triangles_by_color[i]), gl.STATIC_DRAW);
          gl.uniform4f(colorLocation, webglcolorlookup[i][0], webglcolorlookup[i][1], webglcolorlookup[i][2], 1); 
          gl.drawArrays(gl.TRIANGLES, 0, webgl_pixel_triangles_by_color[i].length/2);
       }
      
   

     // since the above erased non-changed cells, lets try allcells instead of changedcellls
     /*
      for (var i = 0 ; i<n_states; i++) {  // initialize i = 1 if drawing is not sticky
         webgl_pixel_triangles_by_color[i] = []
      }
      for (var i=aci; i--;){
          var st = allcells[i].state
        //  if (st) { // if st > 0  // use this if drawing is not sticky
            webgl_pixel_triangles_by_color[st] = webgl_pixel_triangles_by_color[st].concat(allcells[i].webglVertices)
        //  }
      }
      // now do the webgl render, per color:
      for (var i = 0 ; i<n_states; i++) {  // initialize i = 1 if drawing is not sticky
          gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(webgl_pixel_triangles_by_color[i]), gl.STATIC_DRAW);
          gl.uniform4f(colorLocation, webglcolorlookup[i][0], webglcolorlookup[i][1], webglcolorlookup[i][2], 1); 
          gl.drawArrays(gl.TRIANGLES, 0, webgl_pixel_triangles_by_color[i].length/2);
       }
       */

          break;


    default:
      for (var i=changecount; i--;){
          colorcell( changedcells[i])
      }
  }
}

 
function rendertype_colorcell(cel) {
 switch(rendertype) {
   case 1:
          colorcell(cel)
       
      break;
   case 2: 
     
          Rect_aabb_colorcell(cel)
     
      break;
   case 4:  // webgl polygons
      var st = cel.state
         webgl_pixel_triangles_by_color[st] = []
     
        
            webgl_pixel_triangles_by_color[st] = cel.webglVertices
        
      
      // now do the webgl render, per color:
          gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(webgl_pixel_triangles_by_color[st]), gl.STATIC_DRAW);
          gl.uniform4f(colorLocation, webglcolorlookup[st][0], webglcolorlookup[st][1], webglcolorlookup[st][2], 1); 
          gl.drawArrays(gl.TRIANGLES, 0, webgl_pixel_triangles_by_color[st].length/2);
      
      
      break;

    default:
              colorcell(cel)
  }

}

 







//   background purple:  #9966FF
//  background  light blue that shows black lines:  #4A77BA as opposed to a simple blue like #00F

/* fillStyles for displaying decay in Generations rule family:

{ctx.fillStyle = "rgba(255,0,0," +  .3  + ")"}  // 0.5/st
               // alternatives for decay:  {ctx.fillStyle = '#F00'}   //"rgb(" + 255/(st*2) + "," + 255/(st*2) +",0)" }  //{fill(255/st)}

*/
                 // 0           1       2       3           4 
var colorlookup = ['#4A77BA', '#FF0', '#F00', '#00FF00', '#CC33FF', '#00FFFF', '#FFFFFF', '#000000', '#000000', '#000000', '#000000', '#000000', '#000000', '#000000', '#000000', '#000000']

 var webglcolorlookup = [[0.290196078431373, 0.466666667,  0.729411764705882], [1, 1, 0], [1, 0, 0], [0, 1, 0], [1, 1, 1], [1, 1, 1], [1, 1, 1], [1, 1, 1], [1, 1, 1], [1, 1, 1], [1, 1, 1], [1, 1, 1]]

// var webglcolorlookup = [[1,1,1], [1, 1, 0], [1, 0, 0], [0, 1, 0], [1, 1, 1], [1, 1, 1], [1, 1, 1], [1, 1, 1], [1, 1, 1], [1, 1, 1], [1, 1, 1], [1, 1, 1]]

// Alex's Ant colors below
// var colorlookup = ['#FFFFFF', '#FF0000', '#00FF00', '#0000FF', '#CC33FF', '#00FFFF', '#FFFFFF', '#000000', '#000000', '#000000', '#000000', '#000000', '#000000', '#000000', '#000000', '#000000']
// Alex's ant colors but with a black background
// var colorlookup = ['#000000', '#FF0000', '#00FF00', '#0000FF', '#CC33FF', '#00FFFF', '#FFFFFF', '#000000', '#000000', '#000000', '#000000', '#000000', '#000000', '#000000', '#000000', '#000000']

// http://www.w3schools.com/tags/canvas_globalcompositeoperation.asp
// http://stackoverflow.com/questions/3088229/clear-pixels-under-a-shape-in-html-canvas

var gridcolor =  "#000000"; // "#FFFFFF"  //"#000000";



//  ctx.fillStyles should be pre-calculated so that ctx.fillStyle = colorlookup[x.state]




//  var st =  x.state;
//  if (st == 1)   {ctx.fillStyle = '#FF0'}
//  else if (st > 1)    {ctx.fillStyle = "rgb(" + 300/st + "," + 300/st + "," + 300/st + ")"} // {ctx.fillStyle = "rgb(" + 255/(st-1) + ",0,0)"}  //
//  else  {ctx.fillStyle = '#4A77BA'}; 

 

  

function Rectcolorcell (cel) {
 var c = cel.coords;
 var st = cel.state;
// if (st) {
   ctx.fillStyle = colorlookup[st]
//  ctx.fillRect(Math.floor(c[0]),Math.floor(c[1]), scaledshapesize, scaledshapesize)
   ctx.fillRect(Math.floor(c[0]),Math.floor(c[1]), 2, 2)
   ctx.fillRect(Math.floor(c[0]),Math.floor(c[1]), 2, 2)
// }  
 //else {
 //  ctx.clearRect(c[0],c[1], scaledshapesize, scaledshapesize)
// }  
 
}


function Rect_aabb_colorcell (cel) {
   ctx.fillStyle = colorlookup[cel.state]
   
  //  ctx.fillRect(Math.floor(c[0]),Math.floor(c[1]), scaledshapesize, scaledshapesize)
  //   ctx.fillRect(Math.floor(c[0]),Math.floor(c[1]), 2, 2)
 //    ctx.fillRect(cel.aabb_x, cel.aabb_y, cel.aabb_width, cel.aabb_height) // good!

   ctx.fillRect(cel.aabb_x_rounded, cel.aabb_y_rounded, cel.aabb_width_rounded, cel.aabb_height_rounded)

 }


function colorcell(cel) {
 var c = cel.coords;
 ctx.fillStyle = colorlookup[cel.state]

 ctx.beginPath();   
 ctx.moveTo(c[0], c[1]);
 for (var i=c.length; i-=2;) {
     ctx.lineTo(c[i], c[i+1]);
 }
 ctx.closePath();  
 ctx.fill();
// ctx.stroke();

}

// the following function is useful for snapshots of the tiling
function stroke_and_fill_cell(x) {   
 var c = x.coords;
 ctx.fillStyle = colorlookup[x.state]

 ctx.beginPath();   
 ctx.moveTo(c[0], c[1]);
 for (var i=c.length; i-=2;) {
     ctx.lineTo(c[i], c[i+1]);
 }
 ctx.closePath();  
 ctx.fill();
 ctx.stroke();

}



function aabb_setup() {
  aabb_largest_width = 0
  aabb_largest_height = 0
  for (var i=aci; i--;) {
     var aabb_object = PolyK.GetAABB(allcells[i].coords)
     allcells[i].aabb_x = aabb_object.x
     allcells[i].aabb_y = aabb_object.y
     allcells[i].aabb_width = aabb_object.width
     allcells[i].aabb_height = aabb_object.height
     allcells[i].aabb_x_rounded =  roundNumber( aabb_object.x, 0)
     allcells[i].aabb_y_rounded =  roundNumber( aabb_object.y, 0)
     allcells[i].aabb_width_rounded =  roundNumber( aabb_object.width, 0)
     allcells[i].aabb_height_rounded =  roundNumber( aabb_object.height, 0)
     if (aabb_largest_width < aabb_object.width) {aabb_largest_width = aabb_object.width}
     if (aabb_largest_height < aabb_object.height) {aabb_largest_height = aabb_object.height}

  }
}

//   ***********  WebGL section **********************************

var gl // this is the webgl context for the webgl canvas -- see webgl_setup

function webgl_setup () {
    webgl_states = 50
    webgl_vertices_by_color = []

    webgl_pixel_triangles_by_color = []

    for (var i = webgl_states; i--;) { 
       webgl_pixel_triangles_by_color[i] = []
    }
    for (var i = webgl_states; i--;) { 
       webgl_vertices_by_color[i] = []
    }


   // actual webgl setup goes here  - this is code from Ming Example mymod4.html
   // credit to Ming goes here:


   var element = document.getElementById("webglcanvas")
   try {
      gl = element.getContext('experimental-webgl', {preserveDrawingBuffer: true});   // <----- see documenation on preserveDrawingBuffer - it may be a poor choice

   } catch (e) {
      throw new Error('no WebGL found');
   }

   var buffer = gl.createBuffer();
   gl.bindBuffer(gl.ARRAY_BUFFER, buffer);

   // Create a simple vertex shader
 
   var vertCode =
        'attribute vec2 coordinates;' +
        'void main(void) {' +
        '  gl_Position = vec4(coordinates, 0.0, 1.0);' +
        '}';

   var vertShader = gl.createShader(gl.VERTEX_SHADER);
   gl.shaderSource(vertShader, vertCode);
   gl.compileShader(vertShader);
   if (!gl.getShaderParameter(vertShader, gl.COMPILE_STATUS))
      throw new Error(gl.getShaderInfoLog(vertShader));

   // Create a simple fragment shader

   var fragCode =

       'precision mediump float;' +

       'uniform vec4 u_color;' +

       'void main() {' +
         'gl_FragColor = u_color;' +
       '}'


   var fragShader = gl.createShader(gl.FRAGMENT_SHADER);
   gl.shaderSource(fragShader, fragCode);
   gl.compileShader(fragShader);
   if (!gl.getShaderParameter(fragShader, gl.COMPILE_STATUS))
      throw new Error(gl.getShaderInfoLog(fragShader));

   // Put the vertex shader and fragment shader together into
   // a complete program
   //
   var shaderProgram = gl.createProgram();
   gl.attachShader(shaderProgram, vertShader);
   gl.attachShader(shaderProgram, fragShader);
   gl.linkProgram(shaderProgram);
   if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS))
      throw new Error(gl.getProgramInfoLog(shaderProgram));

   // Clear the drawing surface
   //
  // gl.clearColor(1.0, 1.0, 1.0, 1.0);   // white as the clear color
  // gl.clear(gl.COLOR_BUFFER_BIT);

   // Tell WebGL which shader program to use
   //
   gl.useProgram(shaderProgram);

   colorLocation = gl.getUniformLocation(shaderProgram, "u_color");


   // Tell WebGL that the data from the array of triangle
   // coordinates that we've already copied to the graphics
   // hardware should be fed to the vertex shader as the
   // parameter "coordinates"
   //
   var coordinatesVar = gl.getAttribLocation(shaderProgram, "coordinates");
   gl.enableVertexAttribArray(coordinatesVar);
   gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
   gl.vertexAttribPointer(coordinatesVar, 2, gl.FLOAT, false, 0, 0);

   // the following line needs to be run each time the tiling changes, whereas the above only needs to run once
   if  (allcells[1].webglVertices == undefined) { 
      AddWeglVerticesToAllCells() 
   }

}
    

function VerticesFromPolykTriangulate(cel) {
   var results = []
   var Tri_input = PolyK.Triangulate(cel.coords)
   for (var i = 0; i<Tri_input.length; i++) {
      results.push(cel.coords[Tri_input[i]*2])
      results.push(cel.coords[Tri_input[i]*2+1])
   } 
   return results
}


function AddWeglVerticesToAllCells() {  // run this in ititiate if render_type == webgl_rendertype3
  var element = document.getElementById("webglcanvas")

  for (var i = aci; i--;) {
    allcells[i].webglVertices =  pixelverts_to_webglverts (VerticesFromPolykTriangulate(allcells[i]), element.width, element.height)
  }
}

function  pixelpos_to_webglpos_x  (pixelpos, canvaswidth) {
      return  ((pixelpos / canvaswidth)*2) - 1.0
}   

function  pixelpos_to_webglpos_y  (pixelpos, canvasheight) {
      return  -1*(((pixelpos / canvasheight)*2) - 1.0)
}   

function pixelverts_to_webglverts (pixelverts, canvaswidth, canvasheight) {
   var results = []
   var pixlen = pixelverts.length
   if ((pixlen % 2) == 1) {pixlen = pixlen - 1} // chop off the last value if pixelverts has an odd length
   for (var i = 0; i<pixlen; i+=2) {
     results.push( pixelpos_to_webglpos_x (pixelverts[i],   canvaswidth) );   
     results.push( pixelpos_to_webglpos_y (pixelverts[i+1], canvasheight) );  
   };    
   return results  
}
    
//  ************  End of WebGL section ******************************

/*    **********  Pre-Render section - all commented out ************


   function prerender_colorcell(cel) {
// I might be overwriting colorcell here to test pre-rendering techniques
//function colorcell(cel) {
  // context.drawImage(img,sx,sy,swidth,sheight,x,y,width,height);
 // ctx.drawImage(prerender_canvases[cel.state], cel.prerender_aabb_x, cel.prerender_aabb_y, cel.prerender_aabb_width, cel.prerender_aabb_height, cel.aabb_x, cel.aabb_y, cel.aabb_width, cel.aabb_height);
//ctx.drawImage(prerender_canvases[1], cel.prerender_aabb_x, cel.prerender_aabb_y, cel.prerender_aabb_width, cel.prerender_aabb_height, cel.aabb_x, cel.aabb_y, cel.aabb_width, cel.aabb_height);
//console.log(cel.prerender_aabb_x + "   " + cel.prerender_aabb_y)
//ctx.drawImage(prerender_canvases[2], cel.prerender_aabb_x, 10, 500, 500, 10, 10, 500, 500);
 if (cel.prerender_aabb_x > 0 &&  cel.prerender_aabb_y > 0) {
   ctx.drawImage(prerender_canvases[cel.state], cel.prerender_aabb_x, cel.prerender_aabb_y, cel.prerender_aabb_width, cel.prerender_aabb_height, cel.aabb_x, cel.aabb_y, cel.aabb_width, cel.aabb_height);
 }
}




function prerender_visualize_spritesheet() {

  for (var celindex  = aci; celindex--;) {
    var cel = allcells[celindex]
    var c = cel.coords;
    ctx.fillStyle =  colorlookup[1]  //  colorlookup[cel.state] // need to do this for each valid color

    ctx.beginPath(); 
    var ytranslate = (Math.floor(celindex/ prerender_rows)*aabb_largest_height    *1.2)   + cel.aabb_y 
    var xtranslate =  ((celindex % prerender_cols)*aabb_largest_width             *1.2)   + cel.aabb_x  
    var x_first =     -c[0]+xtranslate
    var y_first =     -c[1]+ytranslate  
    ctx.moveTo(-c[0]+xtranslate, -c[1]+ytranslate);
    var prerender_polygon = []
    for (var i=c.length; i-=2;) {
          var x = -c[i]+xtranslate
          var y = -c[i+1]+ytranslate
         ctx.lineTo(x,y);
         prerender_polygon.push(x)
         prerender_polygon.push(y)
    }
     prerender_polygon.push(x_first)
     prerender_polygon.push(y_first)
    // allcells[celindex].prerender_poly =  prerender_polygon  // for debugging only
     var aabb_object = PolyK.GetAABB(prerender_polygon)
     allcells[celindex].prerender_aabb_x = aabb_object.x
     allcells[celindex].prerender_aabb_y = aabb_object.y
     allcells[celindex].prerender_aabb_width = aabb_object.width
     allcells[celindex].prerender_aabb_height = aabb_object.height
    ctx.closePath();  
    ctx.fill();
  }
}



function prerender_setup() {
  //  prerender_dimension_x = the_dimension

  prerender_cols = Math.floor(the_dimension/aabb_largest_width) 
  prerender_rows = Math.ceil(aci/prerender_cols)
  prerender_states = 10
  prerender_canvases = []
  prerender_contexts = []
  for (var i = 0; i<prerender_states; i++) {
  
    prerender_canvases[i] = document.createElement('canvas');
    prerender_canvases[i].width = the_dimension * 1.5; // the 1.5 is a kludge to account for tiles being placed past the limit of the canvas
    prerender_canvases[i].height = the_dimension * 1.5; // the 1.5 is a kludge to account for tiles being placed past the limit of the canvas
    prerender_contexts[i] = prerender_canvases[i].getContext('2d');
  }

  for (var celindex  = aci; celindex--;) {
   for (var st = 0; st <  prerender_states; st++) {
    var cel = allcells[celindex]
    var c = cel.coords;
    
    prerender_contexts[st].fillStyle =  colorlookup[st]  //  colorlookup[cel.state] // need to do this for each valid color

    prerender_contexts[st].beginPath(); 
    var ytranslate = (Math.floor(celindex/ prerender_rows)*aabb_largest_height    *1.2)   + cel.aabb_y 
    var xtranslate =  ((celindex % prerender_cols)*aabb_largest_width             *1.2)   + cel.aabb_x  
    var x_first =     -c[0]+xtranslate
    var y_first =     -c[1]+ytranslate  
    prerender_contexts[st].moveTo(-c[0]+xtranslate, -c[1]+ytranslate);
    var prerender_polygon = []  // should calculate this only once, not for each state
    for (var i=c.length; i-=2;) {
          var x = -c[i]+xtranslate
          var y = -c[i+1]+ytranslate
         prerender_contexts[st].lineTo(x,y);
         prerender_polygon.push(x)
         prerender_polygon.push(y)
    }
     prerender_polygon.push(x_first)
     prerender_polygon.push(y_first)
      // allcells[celindex].prerender_poly =  prerender_polygon  // for debugging only
     if (st == 0) {  // only do this once, should be outside the colorstate loop, this if is a quick fix
        var aabb_object = PolyK.GetAABB(prerender_polygon)
        allcells[celindex].prerender_aabb_x = aabb_object.x
        allcells[celindex].prerender_aabb_y = aabb_object.y
        allcells[celindex].prerender_aabb_width = aabb_object.width
        allcells[celindex].prerender_aabb_height = aabb_object.height
     }
    prerender_contexts[st].closePath();  
    prerender_contexts[st].fill();
   }
  }
}

*/



/* The for loop in this older version is more robust -- handles an f with nothing after it in the obj data
function colorcell(x) {
 var c = x.coords;
 var clen = c.length;

  ctx.fillStyle = colorlookup[x.state]

  ctx.beginPath();  // Start tracing the cell's shape
  ctx.moveTo(c[0], c[1]);
  for (var i = 2; i < clen-1; i+=2) {
     ctx.lineTo(c[i], c[i+1]);
  }
 ctx.closePath();  
 ctx.fill();
// ctx.stroke();
}
*/







/*
 var clen = c.length - 1;

  for (i=clen; i-=2;) {
          ctx.lineTo(c[i], c[i+1]);
  }
*/

/*
// An experimetn with PutImageData
var yellow1x1array = [255, 255, 0, 255];
var blue1x1array =   [0,255,255,255];
var  ypxid1 = cxt.createImageData(1,1);
var  ypxdd1 =  ypxid1.data;
var  ypxid2 = cxt.createImageData(2,2);
var  ypxdd2 =  ypxid2.data;
var  ypxid3 = cxt.createImageData(3,3);
var  ypxdd3 =  ypxid3.data;
var  ypxid4 = cxt.createImageData(4,4);
var  ypxdd4 =  ypxid4.data;
var  ypxid4 = cxt.createImageData(4,4);
var  ypxdd4 =  ypxid4.data;

var  bpxid1 = cxt.createImageData(1,1);
var  bpxdd1 =  bpxid1.data;
var  bpxid2 = cxt.createImageData(2,2);
var  bpxdd2 =  bpxid2.data;
var  bpxid3 = cxt.createImageData(3,3);
var  bpxdd3 =  bpxid3.data;
var  bpxid4 = cxt.createImageData(4,4);
var  bpxdd4 =  bpxid4.data;

for (var j = 0;  j<4; j++){ypxdd1[j] = yellow1x1array[j]};
for (var i = 0;  i<4; i++){for (var j=0; j<4; j++) {ypxdd2[(i*4)+ j ] = yellow1x1array[j]}};    
for (var i = 0;  i<9; i++){for (var j=0; j<4; j++) {ypxdd3[(i*4)+ j ] = yellow1x1array[j]}};
for (var i = 0;  i<12; i++){for (var j=0; j<4; j++) {if (i%4 != 0) {ypxdd4[(i*4)+ j ] = yellow1x1array[j]}}};

for (var j = 0;  j<4; j++){bpxdd1[j] = blue1x1array[j]};
for (var i = 0;  i<4; i++){for (var j=0; j<4; j++) {bpxdd2[(i*4)+ j ] = blue1x1array[j]}};    
for (var i = 0;  i<9; i++){for (var j=0; j<4; j++) {bpxdd3[(i*4)+ j ] = blue1x1array[j]}};
for (var i = 0;  i<12; i++){for (var j=0; j<4; j++) {if (i%4 != 0) {bpxdd4[(i*4)+ j ] = blue1x1array[j]}}};

*/














//****************** Mouse code **************  See initialize for more mouse code



function cnvs_mousemove(event) {
   if (mouse_status == "select") {
     if (select_clicks == 1) {
          xmouse_unscaled =  Math.round((event.clientX + window.pageXOffset - 10)  )
          ymouse_unscaled =  Math.round((event.clientY+ window.pageYOffset - my_offsetHeight)  )

           ctx2.clearRect(Math.min(select_1st_x,select_2nd_x),Math.min(select_1st_y,select_2nd_y),
                       Math.abs( select_2nd_x - select_1st_x), Math.abs(select_2nd_y - select_1st_y));

          ctx2.fillStyle = greenish_select_color;  
          ctx2.fillRect(Math.min(select_1st_x,xmouse_unscaled),Math.min(select_1st_y,ymouse_unscaled),
                        Math.abs(xmouse_unscaled - select_1st_x), Math.abs(ymouse_unscaled - select_1st_y));
          select_2nd_x =  xmouse_unscaled;
          select_2nd_y = ymouse_unscaled;
         //  Since these select_2nd values are provisional (before the second select click),
         //   there must be a provision to ensure they don't become numbers that go outside the boundaries of the canvas
          select_2nd_x =  Math.min(Math.max(select_2nd_x,0),the_dimension);
          select_2nd_y =  Math.min(Math.max(select_2nd_y,0),the_dimension);
       }
   }
   else if ( draw_status  == "on") {
      xmouse = (event.clientX + window.pageXOffset - 10)/currentscale  //  - current_translate_x/currentscale ;
      ymouse = (event.clientY+ window.pageYOffset - my_offsetHeight)/currentscale   //  - current_translate_y/currentscale;
    // draw_at_mouse_here(xmouse, ymouse, "moving")  //  // non drawline solution 
         drawline(Math.floor(xmouse), Math.floor(ymouse), Math.floor(oldMouseX), Math.floor(oldMouseY));
      //   drawline(Math.ceil(xmouse), Math.ceil(ymouse), Math.floor(oldMouseX), Math.floor(oldMouseY));

        oldMouseX = xmouse;     
        oldMouseY = ymouse;
        event.preventDefault();  // this line is intended to ensure that mouseup eventually fires
   }
   
 }

//  Draw Line
// http://en.wikipedia.org/wiki/Bresenham%27s_line_algorithm#Simplification
 function drawline(x0, y0, x1, y1) {
   var dx = Math.abs(x1-x0);
   var dy = Math.abs(y1-y0);
   var sx;
   var sy;
   if (x0 < x1) { sx = 1} else { sx = -1};
   if (y0 < y1) { sy = 1} else {sy = -1};
   var err = dx-dy;
   var e2;

   while (true) {
     draw_at_mouse_here(x0,y0, "moving");  
     if (x0 == x1 && y0 == y1) {break};
     e2 = 2*err;
     if (e2 > -dy) { 
       err = err - dy;
       x0 = x0 + sx;
     };
     if (e2 <  dx) { 
       err = err + dx;
       y0 = y0 + sy; 
     };
   } 
}



 
function cnvs_mouseup(event) {
         draw_status  = "off"
        lastdrawnhere = -1

    if (mouse_status == "select" && select_clicks == 1) {
          xmouse_unscaled =  Math.round( (event.clientX + window.pageXOffset - 10) ) 
          ymouse_unscaled =  Math.round( (event.clientY+ window.pageYOffset - my_offsetHeight) ) 

          select_clicks = 2;
          select_2nd_x = xmouse_unscaled;
          select_2nd_y = ymouse_unscaled;
          ctx2.fillStyle=greenish_select_color;  
          ctx2.fillRect(Math.min(select_1st_x,select_2nd_x),Math.min(select_1st_y,select_2nd_y),Math.abs( select_2nd_x - select_1st_x), Math.abs(select_2nd_y - select_1st_y));       
      }

}


function calc_cell_size (cel) {
   var xmin = Math.abs( cel.coords[0])
   var xmax = Math.abs( cel.coords[0])
   var ymin = Math.abs( cel.coords[1])
   var ymax = Math.abs( cel.coords[1])
   var coordslen = cel.coords.length
   for (var j = 0; j<coordslen; j = j + 2) {
      if (Math.abs( cel.coords[j] < xmin)) {xmin = Math.abs( cel.coords[j])}
      if (Math.abs( cel.coords[j] > xmax)) {xmax = Math.abs( cel.coords[j])}
      if (Math.abs( cel.coords[j+1] < ymin)) {ymin = Math.abs( cel.coords[j+1])}
      if (Math.abs( cel.coords[j+1] > ymax)) {ymax = Math.abs( cel.coords[j+1])}
    }
   return ((xmax - xmin) + (ymax - ymin))/2
}
    
function calc_avg_cell_size (startindex, endindex) {
   // console.log(startindex + "   " + endindex)
    var avg_cell_size = calc_cell_size(allcells[startindex]);
   if (endindex > startindex) {  // we want to handle the case of one cell in the mesh
       for (var i = startindex + 1; i <= endindex; i++) {
           avg_cell_size = avg_cell_size + calc_cell_size (allcells[i]);
       }
    }
    return avg_cell_size/(endindex - startindex + 1)
}
    
function calc_smallest_cell_size() {
  var result =      avg_cell_size = calc_cell_size(allcells[0]);
         for (var i = 1; i < aci; i++) {
            var thissize =   calc_cell_size (allcells[i]);
            if (thissize < result) {result = thissize}
         }
   return result
}



function calc_cell_center (cel) {
     var xtotal = 0
     var ytotal = 0
     var coordslen = cel.coords.length
     for (var j = 0; j<coordslen; j = j + 2) {
           xtotal = xtotal  + Math.abs( cel.coords[j])
           ytotal = ytotal +  Math.abs( cel.coords[j + 1])
     }
     var avgXtotal = xtotal/(coordslen/2)
     var avgYtotal = ytotal/(coordslen/2)
     return [avgXtotal, avgYtotal]
}

function cell_x_distance (cel1, cel2) {
 return  Math.abs( calc_cell_center(cel1)[0] - calc_cell_center(cel2)[0] )
}

function cell_y_distance (cel1, cel2) {
 return  Math.abs( calc_cell_center(cel1)[1] - calc_cell_center(cel2)[1] )
}
  
//*************************
//  pnpoly
// Credit:  http://www.ecse.rpi.edu/Homepages/wrf/Research/Short_Notes/pnpoly.html#Explanation
function pnpoly( nvert, vertx, verty, testx,  testy)
{
  var i,j,c =0;
  //var j = 0;
  //var c = 0;
  for (i = 0, j = nvert-1; i < nvert; j = i++) {
    if ( ((verty[i]>testy) != (verty[j]>testy)) &&
	 (testx < (vertx[j]-vertx[i]) * (testy-verty[i]) / (verty[j]-verty[i]) + vertx[i]) )
       c = !c;
  }
  return c;
}


function call_pnpoly (x, y, celll) {
  var vertx = [];
  var verty = [];
  var clen = celll.coords.length;
  for (i = 0; i < clen; i = i + 2) {
     vertx.push(celll.coords[i])
     verty.push(celll.coords[i+1])
  }
  var fnnlen = celll.fnn.length;
  return pnpoly(fnnlen, vertx, verty, x, y)
}


function cnvs_mousedown(event) {
   if (mouse_status == "draw") {
          xmouse = (event.clientX + window.pageXOffset - 10)/currentscale  //  - current_translate_x/currentscale ;
          ymouse = (event.clientY+ window.pageYOffset - my_offsetHeight)/currentscale   //  - current_translate_y/currentscale;

         draw_at_mouse_here(xmouse,ymouse, "clicking")
         oldMouseX = xmouse;
         oldMouseY = ymouse;
         draw_status = "on";
  } 
  else {  // select

      xmouse_unscaled = Math.round( (event.clientX + window.pageXOffset - 10) ) //  - current_translate_x/currentscale ;
      ymouse_unscaled = Math.round((event.clientY+ window.pageYOffset - my_offsetHeight)  ) //  - current_translate_y/currentscale;

     if (select_clicks == 0) {
          select_clicks = 1;
          select_1st_x = xmouse_unscaled;
          select_1st_y = ymouse_unscaled;
      }
      else if (select_clicks == 1) {
          select_clicks = 2;
          select_2nd_x = xmouse_unscaled;
          select_2nd_y = ymouse_unscaled;
          ctx2.fillStyle=greenish_select_color;  
          ctx2.fillRect(Math.min(select_1st_x,select_2nd_x),Math.min(select_1st_y,select_2nd_y),Math.abs( select_2nd_x - select_1st_x), Math.abs(select_2nd_y - select_1st_y));

      }
      else {
          select_clicks = 0
          //ctx2.fillStyle=black;  
          ctx2.clearRect(Math.min(select_1st_x,select_2nd_x),Math.min(select_1st_y,select_2nd_y),Math.abs( select_2nd_x - select_1st_x), Math.abs(select_2nd_y - select_1st_y));
      }
   }
        
       
    event.preventDefault();  // this line is intended to ensure that mouseup eventually fires

}


var  mouse_status =  "draw";
var  draw_status  = "off"
var  colored_pencil = 1
var  colored_pencil_choice = 1

var  lastdrawnhere;

// Selecting cells via the mouse
var select_clicks = 0;
var select_1st_x = 0;
var select_1st_y = 0;
var select_2nd_x = 0;
var select_2nd_y = 0;


var oldMouseX;
var oldMouseY;



function draw_at_mouse_here(xmouse,ymouse, clickingstatus) {
 // console.log(Math.floor(xmouse*currentscale))
 // console.log(Math.min(the_dimension, Math.max(0, Math.floor(xmouse*currentscale))))
//var t0 = performance.now();

   xcandidates = flooredcellarr[Math.min(the_dimension, Math.max(0, Math.floor(xmouse*initialscale)))]
   for (var ii = xcandidates.length; ii--;) {
    var i =xcandidates[ii]
     if (Math.abs(allcells[i].coords[1]*initialscale  - ymouse*initialscale) < shapesize*currentscale*1) {
      if (call_pnpoly( xmouse, ymouse, allcells[i])) {  // could rule out many candidates before calling  pnpoly
       if (lastdrawnhere != i) {
 
         if (clickingstatus == "clicking") {
         
            if ( allcells[i].state != 0) {
                colored_pencil = 0;
                allcells[i].state = 0;
             } 
             else {
                 colored_pencil = colored_pencil_choice;
                 allcells[i].state = colored_pencil_choice;
              }
          }
          else {  // the mouse is moving, not clicking, so colored_pencil can't change status
              allcells[i].state = colored_pencil
           }
         // colorcell(allcells[i])  // use this if there is no webgl option
         rendertype_colorcell(allcells[i]) // use this if there is a webgl option
         changedcells[changecount] = allcells[i];
         changecount += 1;
         lastdrawnhere = i
         break;  
       }
      }
   }
  }
//var t1 = performance.now();
//console.log("Call to doSomething took " + (t1 - t0) + " milliseconds.")

}




var speedstep = 1;
/*
 
function timedCount ()  { 
   for (var ij=speedstep; ij--;) {   

     refresh()

     draw_changed()
    

     generation += 1;
  }
   //drawboard()    // pick which algo !!!!!!!!!!!!!!!!!!

};


function doTimer() {
  if (!timer_is_on) {
   // document.getElementById("gobutton").value = "Stop";
    document.getElementById("gobutton").innerHTML = "Stop";
   // document.getElementById("randombutton").value = "Stop";
    timer_is_on=1;
    t=setInterval(timedCount,delay);
  }
  else { 
    stopCount();
  } 
};

 */

var flag_set_int = 1
var flag_req_ani = 0

function timedCount ()  { 
    if (flag_req_ani && timer_is_on) {requestAnimationFrame(timedCount)}
   for (var ij=speedstep; ij--;) {   
     
     refresh()

     draw_changed()
    

     generation += 1;
  }
   //drawboard()    // pick which algo !!!!!!!!!!!!!!!!!!

};


function doTimer() {
  if (!timer_is_on) {
    document.getElementById("gobutton").innerHTML = "Stop";
    timer_is_on=1;
    timedCount()
    if (flag_set_int) { t=setInterval(timedCount,delay) };
  }
  else { 
    stopCount();
  } 
};





function stepCount() {

   /*
     refresh()
     draw_changed()
     //drawboard()    // pick which algo !!!!!!!!!!!!!!!!!!
     generation += 1;
   */
    timedCount();

    stopCount();

}
   

function stopCount() {
  clearInterval(t);
 // clearTimeout(myVarTimeout);  // if spaceship search is running
  timer_is_on=0;

  //document.getElementById("gobutton").value = "Go";
  document.getElementById("gobutton").innerHTML = "Go";

 // document.getElementById("randombutton").value = "Test";
  //drawboard();   // cleans up the board
  draw_changed()

};


function interpret_keys(ev) {
    ev.preventDefault();  //  <-- this disables the normal scrolling effect that the arrow keys usually have
   var k = ev.keyCode
   if (k > 48 && k < 58) {
         colored_pencil_choice =  k-48 
               //  Number keys:  1 is keycode 49, 2 => 50, 9 => 57.  // also, note that 0 is 48 if it is needed.

         mouse_status = "draw"
         document.getElementById("brushmenu").selectedIndex = draw_mode_index;
         document.getElementById("brushradio").checked = true
   }
   else {
    switch(k) {
      case 13: // Enter Key
          doTimer();
          break;
      case 32: // space Key
          stepCount();
          break;
      case 221: // right bracket
          increase_magnification();
          break;
      case 219:  // left bracket
          decrease_magnification();
          break;
   }
  }
}



  

// *************************  spaceship detector section (version 1) **************


function  SpaceshipSearch(start_rulestring, end_rulestring, trials_per_rule, approx_velocity, maxNeighbors) {
   var endobject = convertRuleToNumbers(end_rulestring, maxNeighbors)  // this has the side effect of actually changing the current rule
   var end_birth_rulenum = endobject.birth
   var end_survival_rulenum = endobject.survival
   console.log(endobject)

   var startobject = convertRuleToNumbers(start_rulestring, maxNeighbors)  // this has the side effect of actually changing the current rule
   var start_birth_rulenum = startobject.birth
   var start_survival_rulenum = startobject.survival
   console.log(startobject)

   for (var i = start_birth_rulenum; i<=end_birth_rulenum; i++) {
      birthrule = convertNumberToRuleArray (i,maxNeighbors)

      for (var j = start_survival_rulenum; j<=end_survival_rulenum; j++) {
         survivalrule = convertNumberToRuleArray (j,maxNeighbors)

         console.log("Checking:  "+ convertRuleArraysToRuleName())

           detect_activity_on_lower_perimeter(trials_per_rule, approx_velocity)
        //  t=setInterval(detect_activity_on_lower_perimeter(trials_per_rule, approx_velocity),delay)
      }
   }
}

  /*

    var start_rulestring = "B1/s"
    var  end_rulestring = "B1/S0"
    var trials_per_rule = 1
    var approx_velocity = 0.5
    var maxNeighbors = 3
  */
/*
      start_rulestring = "B3/S23"
      end_rulestring = "B3/S236"
      trials_per_rule = 1
      approx_velocity = 0.25
      maxNeighbors = 8
  
var    myVarTimeout;

function  SpaceshipSearchGlobals() {
    document.getElementById("gobutton").value = "Stop";
    timer_is_on=1;


   var endobject = convertRuleToNumbers(end_rulestring, maxNeighbors)  // this has the side effect of actually changing the current rule
   var end_birth_rulenum = endobject.birth
   var end_survival_rulenum = endobject.survival

   var startobject = convertRuleToNumbers(start_rulestring, maxNeighbors)  // this has the side effect of actually changing the current rule
   var start_birth_rulenum = startobject.birth
   var start_survival_rulenum = startobject.survival

   for (var i = start_birth_rulenum; i<=end_birth_rulenum; i++) {
      birthrule = convertNumberToRuleArray (i,maxNeighbors)

      for (var j = start_survival_rulenum; j<=end_survival_rulenum; j++) {
         survivalrule = convertNumberToRuleArray (j,maxNeighbors)

        // console.log("Checking:  "+ convertRuleArraysToRuleName())

        //  detect_activity_on_lower_perimeter(trials_per_rule, approx_velocity)
        //  t=setInterval(detect_activity_on_lower_perimeter(trials_per_rule, approx_velocity),delay)
          myVarTimeout = setTimeout(detect_activity_on_lower_perimeter_globals,3000) // not sure if clearTimeout(myVarTimeout) is needed.
      }
   }
  //  document.getElementById("gobutton").value = "Go";
  //  timer_is_on=0;

}

*/

      start_rulestring = "B3/S23"
      end_rulestring = "B37/S236789"
      trials_per_rule = 5
      approx_velocity = 0.5
      maxNeighbors = 12
      MaxHitPercentage = 0.36

function  SpaceshipSearchGlobals() {

    document.getElementById("gobutton").value = "Stop";
    timer_is_on=1;

    endobject = convertRuleToNumbers(end_rulestring, maxNeighbors)  // this has the side effect of actually changing the current rule
    end_birth_rulenum = endobject.birth
    end_survival_rulenum = endobject.survival

    startobject = convertRuleToNumbers(start_rulestring, maxNeighbors)  // this has the side effect of actually changing the current rule
    start_birth_rulenum = startobject.birth
    start_survival_rulenum = startobject.survival

    ibirth = start_birth_rulenum;
    jsurvival = start_survival_rulenum 
 
         birthrule = convertNumberToRuleArray (ibirth,maxNeighbors)
         survivalrule = convertNumberToRuleArray (jsurvival,maxNeighbors)
         console.log("Checking:  "+ convertRuleArraysToRuleName())




    t=setInterval(SpaceshipSearchGlobals_looppass,delay)

}

function SpaceshipSearchGlobals_looppass () {
    detect_activity_on_lower_perimeter_globals();

    if (jsurvival < end_survival_rulenum) { 
          jsurvival++; 
          survivalrule = convertNumberToRuleArray (jsurvival,maxNeighbors)
          console.log("Checking:  "+ convertRuleArraysToRuleName())

    } else {
         jsurvival = start_survival_rulenum; 
         survivalrule = convertNumberToRuleArray (jsurvival,maxNeighbors)

         ibirth++
         birthrule = convertNumberToRuleArray (ibirth,maxNeighbors)
         console.log("Checking:  "+ convertRuleArraysToRuleName())
    }
    if (ibirth > end_birth_rulenum) { console.log("done"); stopCount()}  // or at least console.log("done")
}


/*
function doSpaceshipDetectorTimer() {

 if (!timer_is_on) {
    document.getElementById("gobutton").value = "Stop";
    timer_is_on=1;

      start_rulestring = "B1/S"
      end_rulestring = "B1/S0"
      trials_per_rule = 1
      approx_velocity = 1
      maxNeighbors = 3

   // t=setInterval(detect_activity_on_lower_perimeter_autogen,delay);
   //t=setInterval(detect_activity_on_lower_perimeter(300),delay);
  //  t=setInterval( SpaceshipSearch(start_rulestring, end_rulestring, trials_per_rule, approx_velocity, maxNeighbors),delay);
 // SpaceshipSearch(start_rulestring, end_rulestring, trials_per_rule, approx_velocity, maxNeighbors)
   t=setInterval( SpaceshipSearchGlobals,delay);  /// this only checks b3/s23 never gets further?
   //t=setInterval( SpaceshipSearchGlobals,delay);  // this seemed slower than the above, but it was evetually interruptable and it repeated
  }
  else { 
 
    stopCount();
  } 
};
*/

function convertRuleToNumbers (rulestring, maxNeighbors) {
   parse_rule_string(rulestring) // this has the side effect of actually changing the current rule
   return {birth:    parseInt(birthrule.join("").slice(0,maxNeighbors+1), 2),  
           survival: parseInt(survivalrule.join("").slice(0,maxNeighbors+1), 2)
         }
}

// slice(1) to remove leading 1 which is an artifact of the mask
// also, there is a distiction between maxNeighbors, which might be a convenient fiction, and the actual max neighbors returned by mesh_info
//   the right way to handle it is to concat up to maxNeighbors 0s onto the result.  
//    but for most meshes, a lazy conct of a bunch of zeros will do, 
//     and another solution would be needed anyway for weighted life -- see rule parser
function convertNumberToRuleArray (num,maxNeighbors) {
   var mask = Math.pow(2, maxNeighbors + 1)
   var temp = num | mask
   var tempresult = temp.toString(2).split("").map(function(x){return parseInt(x,10)})
   return tempresult.slice(1).concat([0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]) 
}

function convertRuleArraysToRuleName() {
    // fill this in so that you can make sense of the results if you find a candidate.
    // need this for canonical parse anyway

    var result = "B"
    for (var i = 0; i<birthrule.length; i++) {
        if (birthrule[i]) {
          if (i > 9){result += ","}
          result = result + i.toString()
        } // add 2e and other non-lifelike elaborations here
    }
    result = result + "/S"
    for (var i = 0; i<survivalrule.length; i++) {
        if (survivalrule[i]) {
           if (i > 9){result += ","}
           result = result + i.toString()
        } // add 2e and other non-lifelike elaborations here
    }
    if (n_states > 2) {result = result + "/C" + n_states}
    return result
}




var LowerPerimeterCells = []

//function  detect_activity_on_lower_perimeter_autogen(trials_per_rule, approx_velocity) {
//     var number_of_generations = Math.floor(mesh_y_size/shapesize * approx_velocity)
//     return detect_activity_on_lower_perimeter (trials_per_rule, number_of_generations) 
//}

function detect_activity_on_lower_perimeter (trials_per_rule, approx_velocity, MaxHitPercentage) {
   GetLowerPerimeter()
   var PerimeterLength = LowerPerimeterCells.length
   var number_of_generations = Math.floor(mesh_y_size/shapesize / approx_velocity)  

 
   for (var trials = trials_per_rule; trials--;) {  // this could have been while(trials_per_rule--)
     PrepareLowerPerimeter()
     make_random_top_for_spaceships()    
     for (var i = number_of_generations; i--;) {
         refresh()
         generation += 1; // essential

         scan_for_perimeter_hits()
     }
     var results = false
     var hits = count_perimeter_hits()
     if ( hits > 0 && hits < (PerimeterLength * MaxHitPercentage) ) {
        results =  true; 
        console.log("yes:  " + hits + " out of " + PerimeterLength + "; ratio = " + hits/PerimeterLength)
        break;
     }  
     else { console.log("no:  " + hits + " out of " + PerimeterLength + "; ratio = " + hits/PerimeterLength);}
   } // end of trials loop


     return results  
}
  
function detect_activity_on_lower_perimeter_globals () {
   GetLowerPerimeter()
   var PerimeterLength = LowerPerimeterCells.length
   var number_of_generations = Math.floor(mesh_y_size/shapesize / approx_velocity)  

 
   for (var trials = trials_per_rule; trials--;) {  // this could have been while(trials_per_rule--)
     PrepareLowerPerimeter()
     make_random_top_for_spaceships()    
     for (var i = number_of_generations; i--;) {
         refresh()
         generation += 1; // essential

         scan_for_perimeter_hits()
     }
     var results = false
     var hits = count_perimeter_hits()
     if ( hits > 0 && hits < (PerimeterLength * MaxHitPercentage) ) {
        results =  true; 
        console.log("yes:  " + hits + " out of " + PerimeterLength + "; ratio = " + hits/PerimeterLength)
        break;
     }  
     else { console.log("no:  " + hits + " out of " + PerimeterLength + "; ratio = " + hits/PerimeterLength);}
   } // end of trials loop


     return results  
}


function make_random_top_for_spaceships() {
    for (var i = aci ; i--;) {
      if ((getrandomnumber(0,3) == 1) && ( allcells[i].coords[1] < (mesh_y_size /3))) { allcells[i].state = 1} else { allcells[i].state = 0}
      changedcells[changecount] = allcells[i];
      changecount += 1;
    }
} 
   

function scan_for_perimeter_hits() {
     for (var i = LowerPerimeterCells.length; i--;) { 
        if (LowerPerimeterCells[i].ever_alive == 0  &&  LowerPerimeterCells[i].state != 0) {
            LowerPerimeterCells[i].ever_alive = 1
        }
     }
}

function count_perimeter_hits() {
   var results = 0
   for (var i = LowerPerimeterCells.length; i--;) { 
        results += LowerPerimeterCells[i].ever_alive
   }
   return results
}

function GetLowerPerimeter () {
    LowerPerimeterCells = [] // LowerPerimeterCell's scope is currently global
     // get lower perimeter  -- 0.6, a bit higher up than mesh_y_size*0.6777, is the arbitrary cutoff.
    for (var i = aci; i--;) {
       if ( allcells[i].coords[1] > (mesh_y_size *0.6)  && allcells[i].VNneighbors.length != allcells[i].fnn.length) { 
         LowerPerimeterCells.push(allcells[i])
       }  
    }
}

function PrepareLowerPerimeter() {
     for (var i = 0; i<LowerPerimeterCells.length; i++) { 
         LowerPerimeterCells[i].state = 0
         LowerPerimeterCells[i].ever_alive = 0
     }
}

// A debugging function
function ShowLowerPerimeter() {
     for (var i = 0; i<LowerPerimeterCells.length; i++) { 
         LowerPerimeterCells[i].state = 1
     }
     drawboard()
}


// **************** End of Spaceship Detector Section **********************************





function getrandomnumber(min, max) {
  return Math.floor(Math.random() * (max - min + 1)) + min
}

function call_make_random_field_with_top() {
 // if (timer_is_on) {
 //   stopCount()
 // } 
 // else { 


     make_random_field("top")

 //    doTimer();
 // }
}

function make_random_field(loc) {
 
 if (loc == "top" || (select_1st_x -select_2nd_x == 0 && select_1st_y - select_2nd_y == 0))  {   //  if (select_clicks == 0) {

   for (var i = aci ; i--;) {
      if ((getrandomnumber(0,3) == 1) && ( allcells[i].coords[1] < (mesh_y_size /3))) { allcells[i].state = 1} else { allcells[i].state = 0}
      changedcells[changecount] = allcells[i];
      changecount += 1;
    }
  } 
  else {
   var c_select_1st_x = Math.min(select_1st_x, select_2nd_x)/currentscale
   var c_select_2nd_x = Math.max(select_1st_x, select_2nd_x)/currentscale
   var c_select_1st_y = Math.min(select_1st_y, select_2nd_y)/currentscale
   var c_select_2nd_y = Math.max(select_1st_y, select_2nd_y)/currentscale
   for (var i = aci; i--;) {
     if (  allcells[i].coords[0] > c_select_1st_x  && allcells[i].coords[0] < c_select_2nd_x  &&  
           allcells[i].coords[1] > c_select_1st_y  && allcells[i].coords[1] < c_select_2nd_y
        ) {
           if (getrandomnumber(0,3) == 1) { allcells[i].state = 1} else { allcells[i].state = 0}
            changedcells[changecount] = allcells[i];
            changecount += 1;
          }
   }
 }
  drawboard() 
}


// This function doubles as  " function clear_selection "  by using zero as a parameter
function fill_selection(st) {
 if (select_clicks != 0) {
   var c_select_1st_x = Math.min(select_1st_x, select_2nd_x)/currentscale
   var c_select_2nd_x = Math.max(select_1st_x, select_2nd_x)/currentscale
   var c_select_1st_y = Math.min(select_1st_y, select_2nd_y)/currentscale
   var c_select_2nd_y = Math.max(select_1st_y, select_2nd_y)/currentscale
  for (var i = aci; i--;) {
     if (  allcells[i].coords[0] > c_select_1st_x  && allcells[i].coords[0] < c_select_2nd_x  &&  
           allcells[i].coords[1] > c_select_1st_y  && allcells[i].coords[1] < c_select_2nd_y
        ) {
            allcells[i].state = st;
            changedcells[changecount] = allcells[i];
            changecount += 1;
          }
   }
  drawboard() 
 }
}

function clear_outside() {
 if (select_clicks != 0) {
   var c_select_1st_x = Math.min(select_1st_x, select_2nd_x)/currentscale
   var c_select_2nd_x = Math.max(select_1st_x, select_2nd_x)/currentscale
   var c_select_1st_y = Math.min(select_1st_y, select_2nd_y)/currentscale
   var c_select_2nd_y = Math.max(select_1st_y, select_2nd_y)/currentscale
  for (var i = aci; i--;) {
     if (  allcells[i].coords[0] < c_select_1st_x  || allcells[i].coords[0] > c_select_2nd_x  ||  
           allcells[i].coords[1] < c_select_1st_y  || allcells[i].coords[1] > c_select_2nd_y
        ) {
            allcells[i].state = 0;
            changedcells[changecount] = allcells[i];
            changecount += 1;
          }
   }
  drawboard() 
 }
}




// ********************* Meshmaker Section ***************************



function get_v_index_from_point_faster(px, py) {
  if (x_to_verts[px] == undefined)  { return false} 
  var varr = x_to_verts[px];
  var vlen = varr.length;
  for (var i = 0;  i<vlen; i++) {
    if (vertexArray[varr[i]].x == px && vertexArray[varr[i]].y == py) {
      return varr[i]
    }
  }
    
 return false;
}

   

//  a fast_but_weak version would be even faster!   See old code for original weak version 
function  get_aci_from_fnn_fast_strong(fnnarray) {

      first_entry = fnnarray[0]
      corner = vertexArray[first_entry]
      relevantfaceindexes = corner.partof
      relavantfacesLen = relevantfaceindexes.length

      for (var i = 0; i < relavantfacesLen; i++) {
         flen = allcells[relevantfaceindexes[i]].fnn.length
         if (flen == fnnarray.length) { // dont bother comparing triangles, for example, to squares  
            var result = true;
            for (var f = flen; f--;) { // compare each fnn (vertex index)
               if ( contains(fnnarray[f], allcells[relevantfaceindexes[i]].fnn) == false) {result = false} 
            }
           if (result) {
              //alert(i); 
              return relevantfaceindexes[i]
           }  
         }  
      }
      return false
}              




// this is the non-processing version -- no screenX and screenY
function build_verts_and_faces (arr, shapetype) {
  var len = arr.length;
  var facearr = [];
  var coordarr = []
  var new_vertice_created = false
  for (var i = 0; i<len; i++) {
    var fx = roundNumber(arr[i][0], sigdigits);
    var fy = roundNumber(arr[i][1], sigdigits);
    var f1 = get_v_index_from_point_faster(fx, fy);
    if (f1 == false) {
          f1 = v_index; 
         // old:  vertexArray.push([fx, fy]);  // change this because verts are now structures, not arrays
          vertexArray[v_index] = new cellvertex(v_index, fx, fy, [])  // filling in that empty partof array at the end is critical.

          if (x_to_verts[fx] == undefined) { x_to_verts[fx] = []  }
          x_to_verts[fx].push(v_index)
          
          // if a new vertex is created, that gurantees that the face will be new too, so set a flag to indicate that get_aci_from_fnn will be false
          new_vertice_created = true
          v_index = v_index + 1;

          
    };
    facearr.push(f1);
    coordarr.push(vertexArray[f1].x);
    coordarr.push(vertexArray[f1].y);
  }
  var cell1;
   // even if a new vertice is not created, a new face might have been made out of old vertices -- filling a hole, for example,
   //  so call get_aci_from_fnn if needed.
  if (new_vertice_created == false) {cell1 = get_aci_from_fnn_fast_strong(facearr, allcells)};
  if (new_vertice_created == true || cell1 == false ) {
          allcells[aci] = new cell(aci, coordarr, facearr, [],[], 0, 0, "message", shapetype);
          for (var facearr_entry = facearr.length; facearr_entry--;) {
              vertexArray[facearr[facearr_entry]].partof.push(aci)
           }

          aci = aci + 1;
              
    };
}




function find_distance(x1, x2, y1, y2) {
  return Math.sqrt ( Math.pow(x1 - x2, 2) + Math.pow(y1 - y2, 2))
}

/*
function v_close_enough_in_same_polygon (x, y, newcoords, enough) {
   for (var i = newcoords.length; i--;) {
     if ( find_distance(x, newcoords[i][0], y,   newcoords[i][1]) < enough)   {return [newcoords[i][0], newcoords[i][1]]} 
   }

   return [x, y]
}
*/

function apply_v_snap_to_holes (enough) {  // in progress
   var old_allcells = allcells
   var old_aci = aci
   var old_v_index = v_index
   var old_vertexArray = vertexArray
   
    x_to_verts = []; flr_x_to_verts = []
     vertexArray = [] 
     v_index = 1;
     vertexArray[0] = settypecellvertex  // This shouldn't be used, but  
     allcells = [] 
     aci = 0;  
    
     var bordercell_indices = []

//      here's how to find bordercell_indicies using the code from mesh_check, 
//      which doesn't work for concave cells,and cells like sphinx with hidden vertices embedded in a line
//      for (var i = old_aci; i--;) {
//        if (old_allcells[i].VNneighbors.length != old_allcells[i].fnn.length) {bordercells.push(oldallcells[i].index)}
 //     }

    bordercell_indices = get_border_cell_indices (old_vertexArray, old_allcells)

    for (var i = old_aci; i--;) {
      var newcoords = [] 
      if (contains(i, bordercell_indices)) {
        var crds = old_allcells[i].coords
        var coordslen = crds.length
        var candidate = []
        for (var j = 0; j < coordslen-1; j+=2) {
              candidate = v_snap_to_closest_if_close_enough(crds[j], crds[j+1], newcoords, enough)  
                                                                                  
              if (detect_notflat_coord_duplicates(candidate, newcoords) == false) {
                      newcoords.push(candidate)
              }
        }
        if (newcoords.length > 2) {
          build_verts_and_faces(newcoords, old_allcells[i].type)
        }
      }
      else {
          for (var j = 0; j<old_allcells[i].coords.length-1; j+=2) {
            newcoords.push([old_allcells[i].coords[j], old_allcells[i].coords[j+1]])
          }
         build_verts_and_faces(newcoords, old_allcells[i].type)
         // If build_verts_and_faces didn't need to be called, this function would be faster.
        //  allcells[i] = old_allcells[i]  // not sufficient because vertexArray need to be restored as well.
        // there is probably a faster approach that doesn't call build_faces_and_verts at all - and doesn't call v_snap either.
      }
      
    }
        
      initialize()
      clearboard() 
      drawboard()  
      drawgrid()


     old_allcells = []  // not necessarily enough for garbage collection

}

function apply_v_snap_to_all (enough) {
   old_allcells = allcells
   old_aci = aci
   
    x_to_verts = []; flr_x_to_verts = []
     vertexArray = [] 
     v_index = 1;
     vertexArray[0] = settypecellvertex  // This shouldn't be used, but  
     allcells = [] 
     aci = 0;  

     for (var i = old_aci; i--;) {
        var crds = old_allcells[i].coords
        var coordslen = crds.length
        var candidate = []
        var newcoords = []
        for (var j = 0; j < coordslen-1; j+=2) {
              candidate = v_snap_to_closest_if_close_enough(crds[j], crds[j+1], newcoords, enough)  
                                                                                  
              if (detect_notflat_coord_duplicates(candidate, newcoords) == false) {
                      newcoords.push(candidate)
              }
        }
        if (newcoords.length > 2) {
          build_verts_and_faces(newcoords, "shape")
        }
      }
        
      initialize()
      clearboard() 
      drawboard()  
      drawgrid()


     old_allcells = []  // not necessarily enough for garbage collection

}




function v_snap_to_closest_if_close_enough (x, y, newcoords, enough) {

    for (var i = newcoords.length; i--;) {  // This loop could be moved to  v_close_enough_in_same_polygon()
     if ( find_distance(x, newcoords[i][0], y,   newcoords[i][1]) < enough)   {return [newcoords[i][0], newcoords[i][1]]} 
    }

    var flr = Math.floor(x)
    if (flr_x_to_verts[flr] == undefined) {flr_x_to_verts[flr] = []}
    var flrcandidates
    flrcandidates = flr_x_to_verts[flr]
    for (var i = flrcandidates.length; i--;) {
       if ( find_distance(x, flrcandidates[i][0],y, flrcandidates[i][1]) < enough) { return flrcandidates[i] }
    }

    var flr_plusone = Math.floor(x) + 1
    if (flr_x_to_verts[flr_plusone] == undefined) {flr_x_to_verts[flr_plusone] = []}
    flrcandidates = flr_x_to_verts[flr_plusone]
    for (var i = flrcandidates.length; i--;) {
       if ( find_distance(x, flrcandidates[i][0],y, flrcandidates[i][1]) < enough) { return flrcandidates[i] }
    }

    var flr_minusone = Math.floor(x) - 1
    if (flr_minusone >= 0)if (flr_x_to_verts[flr_minusone] == undefined){flr_x_to_verts[flr_minusone] = []}
    if (flr_minusone > 0) {
       flrcandidates = flr_x_to_verts[flr_minusone]
       for (var i = flrcandidates.length; i--;) {
          if ( find_distance(x, flrcandidates[i][0],y, flrcandidates[i][1]) < enough) { return flrcandidates[i] }
       }
    }
     
   flr_x_to_verts[flr].push([x,y])  
   return [x, y]
  
}

function snap_to_closest_if_close_enough (x, y, newcoords, enough) {
  for (var i = newcoords.length; i--;) {
     if ( find_distance(x, newcoords[i][0], y,   newcoords[i][1]) < enough)   {return [newcoords[i][0], newcoords[i][1]]} 
   }
   // this next loop should be replaced with a faster scheme!
   for (var i = v_index; i--;) {
    if ( find_distance(x, vertexArray[i].x, y,   vertexArray[i].y) < enough)   {return [vertexArray[i].x, vertexArray[i].y]} 
    }
      

   return [x, y]

}


   


function detect_notflat_coord_duplicates (pair, arr) {
  for (var i = arr.length; i--;) {
       if (pair[0] == arr[i][0] && pair[1] == arr[i][1]) {return true}
  }
  return false
}

function callVoronoi() {

    // first compute Voronoi, then erase the current memory

  var  bbox = {xl:0, xr:mesh_x_size, yt:0, yb:mesh_y_size};
  voronoi = new Voronoi();
  voronoi_result = voronoi.compute(vertexArray, bbox);


  // old_allcells = allcells
  // old_aci = aci
   x_to_verts = []; flr_x_to_verts = []
   vertexArray = [] 
   v_index = 1;
   vertexArray[0] = settypecellvertex  // This shouldn't be used, but  
   allcells = [] 
   aci = 0;  
   // debugkeeptrack =  0;

     var resultslen = voronoi_result.cells.length
     for (var i = resultslen; i--;) {
        var resultcell = voronoi_result.cells[i]
        bogus_border_cell = false;

        if (resultcell.closeMe == false && resultcell.halfedges.length > 0) {
           var newcoords = []
           var candidate = []
           for (var j = resultcell.halfedges.length; j--;) {
               if (resultcell.halfedges[j].edge.rSite == null ) {  bogus_border_cell = true; break;}
               var newx = resultcell.halfedges[j].getEndpoint().x  
               var newy = resultcell.halfedges[j].getEndpoint().y 
               newx =  roundNumber(newx, 1) //Math.floor(newx)      //roundNumber(newx, 0)
               newy =  roundNumber(newy, 1) // Math.floor(newy)     // roundNumber(newy, 0)
             
             /* incorporated into V_snap_to_closest_if_close_enough     
               var flr = Math.floor(newx)
               if (flr_x_to_verts[flr] == undefined) {flr_x_to_verts[flr] = []}
               var flr_plusone = flr + 1
               if (flr_x_to_verts[flr_plusone] == undefined) {flr_x_to_verts[flr_plusone] = []}
               var flr_minusone = flr - 1
               if (flr_minusone >= 0)if (flr_x_to_verts[flr_minusone] == undefined){flr_x_to_verts[flr_minusone] = []}
             */
         //  candidate = [newx, newy]
         //    candidate = snap_to_closest_if_close_enough (newx, newy, newcoords, 1.6)  
 
         candidate = v_snap_to_closest_if_close_enough(newx, newy, newcoords, shapesize/10)  
                                                                                   // instead of "1.6", the value of enough might have to be scaled
                                                                                  // ideally, it would be shapesize/10 or somesuch.
              if (detect_notflat_coord_duplicates(candidate, newcoords) == false) {
                      newcoords.push(candidate)
              }
        
           }
           if (bogus_border_cell == false) {
              build_verts_and_faces(newcoords, "voronoi-derived cell");
           }
        }
      }
      initialize()
      clearboard() 
      drawboard()  
      drawgrid()
        

     // To do:   Garbage collect old_allcells by setting to [] (but see link below), or save it to use as an undo step (although after the undo, there will be more vertices unless old_vertexArray is created and restored.  

   //  see http://stackoverflow.com/questions/1232040/how-to-empty-an-array-in-javascript?rq=1
}



function waffle_subdivide() {
   old_allcells = allcells
   old_aci = aci
   
    x_to_verts = []; flr_x_to_verts = []
     vertexArray = [] 
     v_index = 1;
     vertexArray[0] = settypecellvertex  // This shouldn't be used, but  
     allcells = [] 
     aci = 0;  
    // var newcoords = []
     var coordslen;
     for (var i = old_aci; i--;) {
        var crds = old_allcells[i].coords
        var coordslen = crds.length
        var center_x = crds[0]
        var center_y = crds[1] 
        for (var j= 2; j<coordslen; j+=2) {
            center_x = center_x + crds[j]
            center_y = center_y + crds[j+1]
        }
        center_x = center_x/(coordslen/2)
        center_y = center_y/(coordslen/2)

        var inset_pairs = []   // of the form [[x1, y1], [x2, y2] ...]
        for (var j = 0; j<coordslen; j+=2) {
           inset_pairs[j/2] = [(crds[j] + center_x)/2,  (crds[j+1] + center_y)/2]
        }
        build_verts_and_faces(inset_pairs, old_allcells[i].type) // or use "shape"

        build_verts_and_faces ([[crds[coordslen-2],crds[coordslen-1]],[crds[0], crds[1]], inset_pairs[0], inset_pairs[coordslen/2-1]], "quad");  // beginning
        for (var j = 2; j<coordslen-2; j+=2) {
            build_verts_and_faces ([[crds[j-2], crds[j-1]], [crds[j], crds[j+1]], inset_pairs[j/2], inset_pairs[j/2-1]],  "quad");  // middles        
         }
        build_verts_and_faces ([[crds[coordslen-4],crds[coordslen-3]], [crds[coordslen-2],crds[coordslen-1]], inset_pairs[coordslen/2-1],inset_pairs[coordslen/2-2]],   "quad");  // beginning
      }
      initialize()
      clearboard() 
      drawboard()  
      drawgrid()


     old_allcells = []  // not necessarily enough for garbage collection

 // To do:   Garbage collect old_allcells by setting to [] (but see link below), or save it to use as an undo step (although after the undo, there will be more vertices unless old_vertexArray is created and restored.  

   //  see http://stackoverflow.com/questions/1232040/how-to-empty-an-array-in-javascript?rq=1
}

function triangle_subdivide() {
   old_allcells = allcells
   old_aci = aci
   
    x_to_verts = []; flr_x_to_verts = []
     vertexArray = [] 
     v_index = 1;
     vertexArray[0] = settypecellvertex  // This shouldn't be used, but  
     allcells = [] 
     aci = 0;  
    // var newcoords = []
     var coordslen;
     for (var i = old_aci; i--;) {
        var crds = old_allcells[i].coords
        var coordslen = crds.length
        var center_x = crds[0]
        var center_y = crds[1] 
        for (var j= 2; j<coordslen; j+=2) {
            center_x = center_x + crds[j]
            center_y = center_y + crds[j+1]
        }
        center_x = center_x/(coordslen/2)
        center_y = center_y/(coordslen/2)

        for (var j = 2; j<coordslen-1; j+=2) {
            build_verts_and_faces ([[crds[j-2], crds[j-1]], [crds[j], crds[j+1]], [center_x, center_y]], "triangle");       
         }
        build_verts_and_faces ([[crds[coordslen-2],crds[coordslen-1]],[crds[0], crds[1]], [center_x, center_y]], "triangle");       

      
        }
      initialize()
      clearboard() 
      drawboard()  
      drawgrid()


     old_allcells = []  // not necessarily enough for garbage collection

 // To do:   Garbage collect old_allcells by setting to [] (but see link below), or save it to use as an undo step (although after the undo, there will be more vertices unless old_vertexArray is created and restored.  

   //  see http://stackoverflow.com/questions/1232040/how-to-empty-an-array-in-javascript?rq=1
}

function triangle_subdivide_concave_only() {
   old_allcells = allcells
   old_aci = aci
   
    x_to_verts = []; flr_x_to_verts = []
     vertexArray = [] 
     v_index = 1;
     vertexArray[0] = settypecellvertex  // This shouldn't be used, but  
     allcells = [] 
     aci = 0;  
    // var newcoords = []

     for (var i = old_aci; i--;) {
        var crds = old_allcells[i].coords
        var coordslen = crds.length
        if ( call_convex(old_allcells[i]) == "convex") {
               var not_flat = []
               for (var z=0; z<coordslen; z+=2) {
                 not_flat.push([crds[z], crds[z+1]])  // the coords are reversed, 
               }                                    // but this is ok, since build_verts_and_faces rebuilds everything
               build_verts_and_faces(not_flat, old_allcells[i].type)
        }
        else {
           var center_x = crds[0]
           var center_y = crds[1] 
           for (var j= 2; j<coordslen; j+=2) {
              center_x = center_x + crds[j]
              center_y = center_y + crds[j+1]
           }
           center_x = center_x/(coordslen/2)
           center_y = center_y/(coordslen/2)

           for (var j = 2; j<coordslen-1; j+=2) {
            build_verts_and_faces ([[crds[j-2], crds[j-1]], [crds[j], crds[j+1]], [center_x, center_y]], "triangle");       
           }
           build_verts_and_faces ([[crds[coordslen-2],crds[coordslen-1]],[crds[0], crds[1]], [center_x, center_y]], "triangle");       
        }
      
     }
      initialize()
      clearboard() 
      drawboard()  
      drawgrid()


     old_allcells = []  // not necessarily enough for garbage collection

 // To do:   Garbage collect old_allcells by setting to [] (but see link below), or save it to use as an undo step (although after the undo, there will be more vertices unless old_vertexArray is created and restored.  

   //  see http://stackoverflow.com/questions/1232040/how-to-empty-an-array-in-javascript?rq=1
}


function Selfridge_subdivide() {
   old_allcells = allcells
   old_aci = aci
   
    x_to_verts = []; flr_x_to_verts = []
     vertexArray = [] 
     v_index = 1;
     vertexArray[0] = settypecellvertex  // This shouldn't be used, but  
     allcells = [] 
     aci = 0;  
    // var newcoords = []
     var coordslen;
     for (var i = old_aci; i--;) {
        var crds = old_allcells[i].coords
        var coordslen = crds.length
        var center_x = crds[0]
        var center_y = crds[1] 
        for (var j= 2; j<coordslen; j+=2) {
            center_x = center_x + crds[j]
            center_y = center_y + crds[j+1]
        }
        center_x = center_x/(coordslen/2)
        center_y = center_y/(coordslen/2)

        var inset_pairs = []   // of the form [[x1, y1], [x2, y2] ...]
          
            trailing_midpoint_x  = (crds[coordslen-2] + crds[0])/2
            trailing_midpoint_y  = (crds[coordslen-1] + crds[1])/2
            leading_midpoint_x = (crds[0] + crds[2])/2
            leading_midpoint_y = (crds[1] + crds[3])/2
            trailing_inset_x  =  (center_x + trailing_midpoint_x)/2
            trailing_inset_y  =  (center_y + trailing_midpoint_y)/2
            leading_inset_x  =  (center_x + leading_midpoint_x)/2
            leading_inset_y  =  (center_y + leading_midpoint_y)/2
            inset_pairs.push([leading_inset_x,leading_inset_y])
            build_verts_and_faces ([[trailing_midpoint_x, trailing_midpoint_y],[crds[0], crds[1]], [leading_midpoint_x, leading_midpoint_y], [leading_inset_x,leading_inset_y ], [trailing_inset_x,trailing_inset_y ]], "pentagon");  // beginning

        for (var j = 2; j<coordslen-2; j+=2) {
            trailing_midpoint_x  = (crds[j-2] + crds[j])/2
            trailing_midpoint_y  = (crds[j-1] + crds[j+1])/2
            leading_midpoint_x = (crds[j] + crds[j+2])/2
            leading_midpoint_y = (crds[j+1] + crds[j+3])/2
            trailing_inset_x  =  (center_x + trailing_midpoint_x)/2
            trailing_inset_y  =  (center_y + trailing_midpoint_y)/2
            leading_inset_x  =  (center_x + leading_midpoint_x)/2
            leading_inset_y  =  (center_y + leading_midpoint_y)/2
            inset_pairs.push([leading_inset_x,leading_inset_y])
            build_verts_and_faces ([[trailing_midpoint_x, trailing_midpoint_y],[crds[j], crds[j+1]], [leading_midpoint_x, leading_midpoint_y], [leading_inset_x,leading_inset_y ], [trailing_inset_x,trailing_inset_y ]], "pentagon");  // middles        
         }

            trailing_midpoint_x  = (crds[coordslen-4] + crds[coordslen-2])/2
            trailing_midpoint_y  = (crds[coordslen-3] + crds[coordslen-1])/2
            leading_midpoint_x = (crds[coordslen-2] + crds[0])/2
            leading_midpoint_y = (crds[coordslen-1] + crds[1])/2
            trailing_inset_x  =  (center_x + trailing_midpoint_x)/2
            trailing_inset_y  =  (center_y + trailing_midpoint_y)/2
            leading_inset_x  =  (center_x + leading_midpoint_x)/2
            leading_inset_y  =  (center_y + leading_midpoint_y)/2
            inset_pairs.push([leading_inset_x,leading_inset_y])
            build_verts_and_faces ([[trailing_midpoint_x, trailing_midpoint_y],[crds[coordslen-2], crds[coordslen-1]], [leading_midpoint_x, leading_midpoint_y], [leading_inset_x,leading_inset_y ], [trailing_inset_x,trailing_inset_y ]], "pentagon");  // beginning

            build_verts_and_faces (inset_pairs, old_allcells[i].type) // or use "shape"


      }
      initialize()
      clearboard() 
      drawboard()  
      drawgrid()


     old_allcells = []  // not necessarily enough for garbage collection

 // To do:   Garbage collect old_allcells by setting to [] (but see link below), or save it to use as an undo step (although after the undo, there will be more vertices unless old_vertexArray is created and restored.  

   //  see http://stackoverflow.com/questions/1232040/how-to-empty-an-array-in-javascript?rq=1


    // idea:  something akin to the above could be used to simply create intermediate vertices (but not new cells). Not useful unless midpoints needed.
}


function inset_subdivide() {
   old_allcells = allcells
   old_aci = aci
   
    x_to_verts = []; flr_x_to_verts = []
     vertexArray = [] 
     v_index = 1;
     vertexArray[0] = settypecellvertex  // This shouldn't be used, but  
     allcells = [] 
     aci = 0;  
    // var newcoords = []
     var coordslen;
     for (var i = old_aci; i--;) {
           var crds = old_allcells[i].coords
           var coordslen = crds.length
           var inset_pairs = []   // of the form [[x1, y1], [x2, y2] ...]

          
            trailing_midpoint_x  = (crds[coordslen-2] + crds[0])/2
            trailing_midpoint_y  = (crds[coordslen-1] + crds[1])/2
            leading_midpoint_x = (crds[0] + crds[2])/2
            leading_midpoint_y = (crds[1] + crds[3])/2
            build_verts_and_faces ([[trailing_midpoint_x, trailing_midpoint_y],[crds[0], crds[1]], [leading_midpoint_x, leading_midpoint_y]], "triangle");  // beginning
            inset_pairs.push([leading_midpoint_x, leading_midpoint_y])

        for (var j = 2; j<coordslen-2; j+=2) {
            trailing_midpoint_x  = (crds[j-2] + crds[j])/2
            trailing_midpoint_y  = (crds[j-1] + crds[j+1])/2
            leading_midpoint_x = (crds[j] + crds[j+2])/2
            leading_midpoint_y = (crds[j+1] + crds[j+3])/2

            build_verts_and_faces ([[trailing_midpoint_x, trailing_midpoint_y],[crds[j], crds[j+1]], [leading_midpoint_x, leading_midpoint_y]], "triangle");  // middles  
            inset_pairs.push([leading_midpoint_x, leading_midpoint_y])
      
         }

            trailing_midpoint_x  = (crds[coordslen-4] + crds[coordslen-2])/2
            trailing_midpoint_y  = (crds[coordslen-3] + crds[coordslen-1])/2
            leading_midpoint_x = (crds[coordslen-2] + crds[0])/2
            leading_midpoint_y = (crds[coordslen-1] + crds[1])/2
            build_verts_and_faces ([[trailing_midpoint_x, trailing_midpoint_y],[crds[coordslen-2], crds[coordslen-1]], [leading_midpoint_x, leading_midpoint_y]], "triangle");  // beginning
            inset_pairs.push([leading_midpoint_x, leading_midpoint_y])

            build_verts_and_faces(inset_pairs, "shape")


      }
      initialize()
      clearboard() 
      drawboard()  
      drawgrid()

     old_allcells = []  // not necessarily enough for garbage collection

 // To do:   Garbage collect old_allcells by setting to [] (but see link below), or save it to use as an undo step (although after the undo, there will be more vertices unless old_vertexArray is created and restored.  

   //  see http://stackoverflow.com/questions/1232040/how-to-empty-an-array-in-javascript?rq=1


    // idea:  something akin to the above could be used to simply create intermediate vertices (but not new cells). Not useful unless midpoints needed.
}


// Experiment -- a truncation-like subdivide where small triangles are cut out at the corners of a cell 

function inset_subdivide2() {
   old_allcells = allcells
   old_aci = aci
   
    x_to_verts = []; flr_x_to_verts = []
     vertexArray = [] 
     v_index = 1;
     vertexArray[0] = settypecellvertex  // This shouldn't be used, but  
     allcells = [] 
     aci = 0;  
    // var newcoords = []
     var coordslen;
     for (var i = old_aci; i--;) {
           var crds = old_allcells[i].coords
           var coordslen = crds.length
           var inset_pairs = []   // of the form [[x1, y1], [x2, y2] ...]

          
            trailing_midpoint_x  =     ((crds[0] + (crds[coordslen-2] + crds[0])/2)/2)   // this puts them at the 1/4 point, we want the 1/3 point
            trailing_midpoint_y  =     ((crds[1] + (crds[coordslen-1] + crds[1])/2)/2)    // but it is topologically equivalent so let it go for now
            leading_midpoint_x = (crds[0] + (crds[0] + crds[2])/2)/2
            leading_midpoint_y = (crds[1] + (crds[1] + crds[3])/2)/2
            build_verts_and_faces ([[trailing_midpoint_x, trailing_midpoint_y],[crds[0], crds[1]], [leading_midpoint_x, leading_midpoint_y]], "triangle");  // beginning
            inset_pairs.push([trailing_midpoint_x, trailing_midpoint_y])
            inset_pairs.push([leading_midpoint_x, leading_midpoint_y])


        for (var j = 2; j<coordslen-2; j+=2) {
            trailing_midpoint_x  = (crds[j] + (crds[j-2] + crds[j])/2)/2
            trailing_midpoint_y  = (crds[j+1] + (crds[j-1] + crds[j+1])/2)/2
            leading_midpoint_x = (crds[j] + (crds[j] + crds[j+2])/2)/2
            leading_midpoint_y = (crds[j+1] + (crds[j+1] + crds[j+3])/2)/2

            build_verts_and_faces ([[trailing_midpoint_x, trailing_midpoint_y],[crds[j], crds[j+1]], [leading_midpoint_x, leading_midpoint_y]], "triangle");  // middles  
            inset_pairs.push([trailing_midpoint_x, trailing_midpoint_y])
            inset_pairs.push([leading_midpoint_x, leading_midpoint_y])      
         }

            trailing_midpoint_x  =  (crds[coordslen-2] + (crds[coordslen-4] + crds[coordslen-2])/2)/2
            trailing_midpoint_y  =  (crds[coordslen-1] + (crds[coordslen-3] + crds[coordslen-1])/2)/2
            leading_midpoint_x =  (crds[coordslen-2]  + (crds[coordslen-2] + crds[0])/2)/2
            leading_midpoint_y =  (crds[coordslen-1] + (crds[coordslen-1] + crds[1])/2)/2
            build_verts_and_faces ([[trailing_midpoint_x, trailing_midpoint_y],[crds[coordslen-2], crds[coordslen-1]], [leading_midpoint_x, leading_midpoint_y]], "triangle");  // beginning
            inset_pairs.push([trailing_midpoint_x, trailing_midpoint_y])
            inset_pairs.push([leading_midpoint_x, leading_midpoint_y])

            build_verts_and_faces(inset_pairs, "shape")


      }
      initialize()
      clearboard() 
      drawboard()  
      drawgrid()

     old_allcells = []  // not necessarily enough for garbage collection

 // To do:   Garbage collect old_allcells by setting to [] (but see link below), or save it to use as an undo step (although after the undo, there will be more vertices unless old_vertexArray is created and restored.  

   //  see http://stackoverflow.com/questions/1232040/how-to-empty-an-array-in-javascript?rq=1


    // idea:  something akin to the above could be used to simply create intermediate vertices (but not new cells). Not useful unless midpoints needed.
}

function truncate_tiling () {

   old_allcells = allcells
   old_aci = aci
   old_vertexArray = vertexArray
   old_v_index = v_index
   
    x_to_verts = []; flr_x_to_verts = []
     vertexArray = [] 
     v_index = 1;
     vertexArray[0] = settypecellvertex  // This shouldn't be used, but  
     allcells = [] 
     aci = 0;  

   // first handle the vertices (this is a lot like dualization)
   for (var v = 1; v<old_v_index; v++) {
      var newcell = []
      var vert = old_vertexArray[v]
      var neighbors = neighbor_vertices(vert, old_allcells)

    // associate with the needed midpoint, so it can be used after the sort
      var angles = [] // angles will need to be sorted
      for (var i = neighbors.length; i--;) {
      var midpt = [((vert.x + old_vertexArray[neighbors[i]].x)/2 +vert.x)/2, ((vert.y + old_vertexArray[neighbors[i]].y)/2 +  vert.y)/2]

        angles.push({angle: vertex_angle(vert.x, vert.y, old_vertexArray[neighbors[i]].x, old_vertexArray[neighbors[i]].y), midpoint: midpt})
      }
      angles.sort(function(a,b){return a.angle - b.angle})  // numerical sort lowest to highest
      var newcell = []
      for (var i = angles.length; i--;) {
        newcell.push(angles[i].midpoint)
      }
     if (newcell.length > 2) {
       build_verts_and_faces(newcell, "shape")
     }
   }
 
  // now handle the cells (this is the code from  inset_subdivide, but modified so that only the middle cell gets built.
     for (var i = old_aci; i--;) {
           var crds = old_allcells[i].coords
           var coordslen = crds.length
           var inset_pairs = []   // of the form [[x1, y1], [x2, y2] ...]

          
            trailing_midpoint_x  =     ((crds[0] + (crds[coordslen-2] + crds[0])/2)/2)   // this puts them at the 1/4 point, we want the 1/3 point
            trailing_midpoint_y  =     ((crds[1] + (crds[coordslen-1] + crds[1])/2)/2)    // but it is topologically equivalent so let it go for now
            leading_midpoint_x = (crds[0] + (crds[0] + crds[2])/2)/2
            leading_midpoint_y = (crds[1] + (crds[1] + crds[3])/2)/2
            //  build_verts_and_faces ([[trailing_midpoint_x, trailing_midpoint_y],[crds[0], crds[1]], [leading_midpoint_x, leading_midpoint_y]], "triangle");  // beginning
            inset_pairs.push([trailing_midpoint_x, trailing_midpoint_y])
            inset_pairs.push([leading_midpoint_x, leading_midpoint_y])


        for (var j = 2; j<coordslen-2; j+=2) {
            trailing_midpoint_x  = (crds[j] + (crds[j-2] + crds[j])/2)/2
            trailing_midpoint_y  = (crds[j+1] + (crds[j-1] + crds[j+1])/2)/2
            leading_midpoint_x = (crds[j] + (crds[j] + crds[j+2])/2)/2
            leading_midpoint_y = (crds[j+1] + (crds[j+1] + crds[j+3])/2)/2

           // build_verts_and_faces ([[trailing_midpoint_x, trailing_midpoint_y],[crds[j], crds[j+1]], [leading_midpoint_x, leading_midpoint_y]], "triangle");  // middles  
            inset_pairs.push([trailing_midpoint_x, trailing_midpoint_y])
            inset_pairs.push([leading_midpoint_x, leading_midpoint_y])      
         }

            trailing_midpoint_x  =  (crds[coordslen-2] + (crds[coordslen-4] + crds[coordslen-2])/2)/2
            trailing_midpoint_y  =  (crds[coordslen-1] + (crds[coordslen-3] + crds[coordslen-1])/2)/2
            leading_midpoint_x =  (crds[coordslen-2]  + (crds[coordslen-2] + crds[0])/2)/2
            leading_midpoint_y =  (crds[coordslen-1] + (crds[coordslen-1] + crds[1])/2)/2
            //  build_verts_and_faces ([[trailing_midpoint_x, trailing_midpoint_y],[crds[coordslen-2], crds[coordslen-1]], [leading_midpoint_x, leading_midpoint_y]], "triangle");  // beginning
            inset_pairs.push([trailing_midpoint_x, trailing_midpoint_y])
            inset_pairs.push([leading_midpoint_x, leading_midpoint_y])

            build_verts_and_faces(inset_pairs, "shape")


      }

   
      
      initialize()
      clearboard() 
      drawboard()  
      drawgrid()

     old_allcells = []  // not necessarily enough for garbage collection

}




function rectify_tiling () {

   old_allcells = allcells
   old_aci = aci
   old_vertexArray = vertexArray
   old_v_index = v_index
   
    x_to_verts = []; flr_x_to_verts = []
     vertexArray = [] 
     v_index = 1;
     vertexArray[0] = settypecellvertex  // This shouldn't be used, but  
     allcells = [] 
     aci = 0;  

   // first handle the vertices (this is a lot like dualization)
   for (var v = 1; v<old_v_index; v++) {
      var newcell = []
      var vert = old_vertexArray[v]
      var neighbors = neighbor_vertices(vert, old_allcells)

    // associate with the needed midpoint, so it can be used after the sort
      var angles = [] // angles will need to be sorted
      for (var i = neighbors.length; i--;) {
        var midpt = [(vert.x + old_vertexArray[neighbors[i]].x)/2, (vert.y + old_vertexArray[neighbors[i]].y)/2]
        angles.push({angle: vertex_angle(vert.x, vert.y, old_vertexArray[neighbors[i]].x, old_vertexArray[neighbors[i]].y), midpoint: midpt})
      }
      angles.sort(function(a,b){return a.angle - b.angle})  // numerical sort lowest to highest
      var newcell = []
      for (var i = angles.length; i--;) {
        newcell.push(angles[i].midpoint)
      }
     if (newcell.length > 2) {
        build_verts_and_faces(newcell, "shape")
     }
   }
  // now handle the cells (this is the code from  inset_subdivide, but modified so that only the middle cell gets built.
    for (var i = old_aci; i--;) {
           var crds = old_allcells[i].coords
           var coordslen = crds.length
           var inset_pairs = []   // of the form [[x1, y1], [x2, y2] ...]

          
            trailing_midpoint_x  = (crds[coordslen-2] + crds[0])/2
            trailing_midpoint_y  = (crds[coordslen-1] + crds[1])/2
            leading_midpoint_x = (crds[0] + crds[2])/2
            leading_midpoint_y = (crds[1] + crds[3])/2
           // build_verts_and_faces ([[trailing_midpoint_x, trailing_midpoint_y],[crds[0], crds[1]], [leading_midpoint_x, leading_midpoint_y]], "triangle");  // beginning
            inset_pairs.push([leading_midpoint_x, leading_midpoint_y])

        for (var j = 2; j<coordslen-2; j+=2) {
            trailing_midpoint_x  = (crds[j-2] + crds[j])/2
            trailing_midpoint_y  = (crds[j-1] + crds[j+1])/2
            leading_midpoint_x = (crds[j] + crds[j+2])/2
            leading_midpoint_y = (crds[j+1] + crds[j+3])/2

         //   build_verts_and_faces ([[trailing_midpoint_x, trailing_midpoint_y],[crds[j], crds[j+1]], [leading_midpoint_x, leading_midpoint_y]], "triangle");  // middles  
            inset_pairs.push([leading_midpoint_x, leading_midpoint_y])
      
         }

            trailing_midpoint_x  = (crds[coordslen-4] + crds[coordslen-2])/2
            trailing_midpoint_y  = (crds[coordslen-3] + crds[coordslen-1])/2
            leading_midpoint_x = (crds[coordslen-2] + crds[0])/2
            leading_midpoint_y = (crds[coordslen-1] + crds[1])/2
           // build_verts_and_faces ([[trailing_midpoint_x, trailing_midpoint_y],[crds[coordslen-2], crds[coordslen-1]], [leading_midpoint_x, leading_midpoint_y]], "triangle");  // beginning
            inset_pairs.push([leading_midpoint_x, leading_midpoint_y])

            build_verts_and_faces(inset_pairs, "shape")


      }


      
      initialize()
      clearboard() 
      drawboard()  
      drawgrid()

     old_allcells = []  // not necessarily enough for garbage collection

}

/*
candidate for deletion
function alternation_tiling() {
   old_allcells = allcells
   old_aci = aci
   old_vertexArray = vertexArray
   old_v_index = v_index
   
    x_to_verts = []; flr_x_to_verts = []
     vertexArray = [] 
     v_index = 1;
     vertexArray[0] = settypecellvertex  // This shouldn't be used, but  
     allcells = [] 
     aci = 0;  
    // var newcoords = []
     var coordslen;
     for (var i = old_aci; i--;) {
        var verts = old_allcells[i].fnn
        var vertslen = verts.length
        var newcell = []
        


        for (var j = 0; j<vertslen-1; j+=2) {
           var v = old_vertexArray[verts[j]]
           v.used = true
           newcell.push([v.x, v.y])
        }


        build_verts_and_faces(newcell, "shape")
     }

      
      initialize()
      clearboard() 
      drawboard()  
      drawgrid()

     old_allcells = []  // not necessarily enough for garbage collection

}
  
*/         
      
function stellate_tiling() {
   calculate_all_centers ()
   old_allcells = allcells
   old_aci = aci
   old_vertexArray = vertexArray
   old_v_index = v_index
   
    x_to_verts = []; flr_x_to_verts = []
     vertexArray = [] 
     v_index = 1;
     vertexArray[0] = settypecellvertex  // This shouldn't be used, but  
     allcells = [] 
     aci = 0;  
    // var newcoords = []
     var coordslen;
     for (var i = old_aci; i--;) {
           var crds = old_allcells[i].coords
           var coordslen = crds.length

           var inset_pairs = []   // of the form [[x1, y1], [x2, y2] ...]
 
           var centerX = old_allcells[i].center[0]
           var centerY = old_allcells[i].center[1]


          
       //     trailing_midpoint_x  = (crds[coordslen-2] + crds[0])/2
       //     trailing_midpoint_y  = (crds[coordslen-1] + crds[1])/2
            leading_midpoint_x = (crds[0] + crds[2])/2
            leading_midpoint_y = (crds[1] + crds[3])/2
        //    build_verts_and_faces ([[trailing_midpoint_x, trailing_midpoint_y],[crds[0], crds[1]], [leading_midpoint_x, leading_midpoint_y]], "triangle");  // beginning
            
            inset_pairs.push([leading_midpoint_x, leading_midpoint_y])
            inset_pairs.push([((crds[2] + centerX)/2 + centerX)/2, ((crds[3] + centerY)/2 + centerY)/2])
 


        for (var j = 2; j<coordslen-2; j+=2) {
        //    trailing_midpoint_x  = (crds[j-2] + crds[j])/2
        //    trailing_midpoint_y  = (crds[j-1] + crds[j+1])/2
            leading_midpoint_x = (crds[j] + crds[j+2])/2
            leading_midpoint_y = (crds[j+1] + crds[j+3])/2

         //   build_verts_and_faces ([[trailing_midpoint_x, trailing_midpoint_y],[crds[j], crds[j+1]], [leading_midpoint_x, leading_midpoint_y]], "triangle");  // middles  
            inset_pairs.push([leading_midpoint_x, leading_midpoint_y])
            inset_pairs.push([((crds[j+2] + centerX)/2  + centerX)/2, ((crds[j+3] + centerY)/2 +  centerY)/2])

      
         }

         //   trailing_midpoint_x  = (crds[coordslen-4] + crds[coordslen-2])/2
         //   trailing_midpoint_y  = (crds[coordslen-3] + crds[coordslen-1])/2
            leading_midpoint_x = (crds[coordslen-2] + crds[0])/2
            leading_midpoint_y = (crds[coordslen-1] + crds[1])/2
          //  build_verts_and_faces ([[trailing_midpoint_x, trailing_midpoint_y],[crds[coordslen-2], crds[coordslen-1]], [leading_midpoint_x, leading_midpoint_y]], "triangle");  // beginning
            inset_pairs.push([leading_midpoint_x, leading_midpoint_y])
            inset_pairs.push([((crds[0]  + centerX)/2 +  centerX)/2, ((crds[1] + centerY)/2 +  centerY)/2])

            build_verts_and_faces(inset_pairs, "shape")


      }

  
   // now, optionally, handle the vertices to fill the holes between the stars
   for (var v = 1; v<old_v_index; v++) {
      var newcell = []
      var vert = old_vertexArray[v]
      var neighbors = neighbor_vertices(vert, old_allcells)

    // associate with the needed midpoint, so it can be used after the sort
      var angles = [] // angles will need to be sorted
      for (var i = neighbors.length; i--;) {
        var midpt = [(vert.x + old_vertexArray[neighbors[i]].x)/2, (vert.y + old_vertexArray[neighbors[i]].y)/2]
        angles.push({angle: vertex_angle(vert.x, vert.y, old_vertexArray[neighbors[i]].x, old_vertexArray[neighbors[i]].y), midpoint: midpt, partof: old_vertexArray[neighbors[i]].partof})
      }
      angles.sort(function(a,b){return a.angle - b.angle})  // numerical sort lowest to highest
     // now that the angles are sorted (equivalent to vn neighbors around the clock), get the leading_concave_point for each edge

     for (var i = 0; i<angles.length-1; i++) {

       var cellindex_contributor = intersection_of_arrays(angles[i].partof, angles[i+1].partof)
       var er_cellindex =  parseInt(cellindex_contributor[0])

        if (er_cellindex != undefined && isNaN(er_cellindex) == false) {
          if (old_allcells[er_cellindex].fnn.length > 3 &&  cellindex_contributor.length == 1) {
           var thecellcenter = old_allcells[er_cellindex].center
           angles[i].leading_concave_point = [((thecellcenter[0] + vert.x)/2 +thecellcenter[0])/2, ((thecellcenter[1] + vert.y)/2 +thecellcenter[1])/2]
          }
        }
     }
 
        var cellindex_contributor = intersection_of_arrays(angles[angles.length-1].partof, angles[0].partof)
        var er_cellindex =  parseInt(cellindex_contributor[0])

        if (er_cellindex != undefined && isNaN(er_cellindex) == false) {
          if ( old_allcells[er_cellindex].fnn.length > 3 && cellindex_contributor.length == 1) {
           var thecellcenter = old_allcells[er_cellindex].center
           angles[angles.length-1].leading_concave_point = [((thecellcenter[0] + vert.x)/2 +thecellcenter[0])/2, ((thecellcenter[1] + vert.y)/2 +thecellcenter[1])/2]
         }
        }
    
   
      var newcell = []
      for (var i = angles.length; i--;) {
        if (angles[i].leading_concave_point != undefined) {
          newcell.push(angles[i].leading_concave_point)
        }
        newcell.push(angles[i].midpoint)

      }
     if (newcell.length > 2) {
        build_verts_and_faces(newcell, "shape")
     }
   }

  

      initialize()
      clearboard() 
      drawboard()  
      drawgrid()

     old_allcells = []  // not necessarily enough for garbage collection
}

function chamfer_tiling(not_LifeOnTheEdge) {
   calculate_all_centers ()
   old_allcells = allcells
   old_aci = aci
   old_vertexArray = vertexArray
   old_v_index = v_index
   
    x_to_verts = []; flr_x_to_verts = []
     vertexArray = [] 
     v_index = 1;
     vertexArray[0] = settypecellvertex  // This shouldn't be used, but  
     allcells = [] 
     aci = 0;  

     var bordercells = get_border_cell_indices (old_vertexArray, old_allcells, "thin")
     var coordslen;
 //   if (not_LifeOnTheEdge) {
     for (var i = old_aci; i--;) {
      if (contains(i, bordercells) == false) {
        var crds = old_allcells[i].coords
        var coordslen = crds.length
        var inset_pairs = []   // of the form [[x1, y1], [x2, y2] ...]
        var centerX = old_allcells[i].center[0]
        var centerY = old_allcells[i].center[1]

        for (var j = 0; j<coordslen; j+=2) {
           inset_pairs[j/2] = [(crds[j] + centerX)/2,  (crds[j+1] + centerY)/2]
        }
        build_verts_and_faces(inset_pairs, old_allcells[i].type) // or use "shape"
      }
     }
   //  }
      // handle vertices here

   for (var v = 1; v<old_v_index; v++) {
      var vert = old_vertexArray[v]
      var neighbors = neighbor_vertices(vert, old_allcells)

    // associate with the needed midpoint, so it can be used after the sort
      var angles = [] // angles will need to be sorted
      for (var i = neighbors.length; i--;) {
        angles.push({angle: vertex_angle(vert.x, vert.y, old_vertexArray[neighbors[i]].x, old_vertexArray[neighbors[i]].y),  neighbor_vert: old_vertexArray[neighbors[i]] })
      }
      angles.sort(function(a,b){return a.angle - b.angle})  // numerical sort lowest to highest
      vert.angles = angles
      vert.already_chamfered = []
   }
   for (var v = 1; v<old_v_index; v++) {
     vert = old_vertexArray[v]
 
     if (border_neighbor_vertices(vert, old_allcells).length) {continue}  // ignore border vertices
     
     var vert_angles = vert.angles
     var anglelen = vert_angles.length

 
         var newcell = []
         newcell.push([vert.x, vert.y])
       if (contains(vert.index, vert_angles[0].neighbor_vert.already_chamfered) == false && anglelen > 1) {
 
       var cellindex_contributor = intersection_of_arrays(vert_angles[0].neighbor_vert.partof, vert_angles[1].neighbor_vert.partof, vert.partof)
       var er_cellindex =  parseInt(cellindex_contributor[0])
 
        if (er_cellindex != undefined && isNaN(er_cellindex) == false) {
            var thecellcenter = old_allcells[er_cellindex].center
           if (not_LifeOnTheEdge) {
            newcell.push([(vert.x + thecellcenter[0])/2, (vert.y + thecellcenter[1])/2])
            newcell.push([(vert_angles[0].neighbor_vert.x + thecellcenter[0])/2, (vert_angles[0].neighbor_vert.y + thecellcenter[1])/2])
           }
           else {
            newcell.push([(((vert.x + thecellcenter[0])/2)+vert.x)/2, (((vert.y + thecellcenter[1])/2)+vert.y)/2])
            newcell.push([(((vert_angles[0].neighbor_vert.x + thecellcenter[0])/2) +vert_angles[0].neighbor_vert.x)/2, (((vert_angles[0].neighbor_vert.y + thecellcenter[1])/2)+vert_angles[0].neighbor_vert.y)/2])
           }

        }
        newcell.push([vert_angles[0].neighbor_vert.x, vert_angles[0].neighbor_vert.y])
 
       // now do the same thing for angles[i-1] (except, maybe do angles[i-1] first for counter_clockwise versus clockwise??)

       var cellindex_contributor = intersection_of_arrays(vert_angles[0].neighbor_vert.partof, vert_angles[anglelen-1].neighbor_vert.partof, vert.partof)
       var er_cellindex =  parseInt(cellindex_contributor[0])

        if (er_cellindex != undefined && isNaN(er_cellindex) == false) {
            var thecellcenter = old_allcells[er_cellindex].center
           if (not_LifeOnTheEdge) {
            newcell.push([(vert_angles[0].neighbor_vert.x + thecellcenter[0])/2, (vert_angles[0].neighbor_vert.y + thecellcenter[1])/2])
            newcell.push([(vert.x + thecellcenter[0])/2, (vert.y + thecellcenter[1])/2])
           }
           else {
            newcell.push([((vert_angles[0].neighbor_vert.x + thecellcenter[0])/2 +vert_angles[0].neighbor_vert.x)/2, ((vert_angles[0].neighbor_vert.y + thecellcenter[1])/2 +vert_angles[0].neighbor_vert.y)/2])
            newcell.push([((vert.x + thecellcenter[0])/2+vert.x)/2, ((vert.y + thecellcenter[1])/2 +vert.y)/2])

           }

        }
      // now mark the corresponding vertex for each vertex as "converted" - a way of not reusing edges
        vert.already_chamfered.push(vert_angles[0].neighbor_vert.index) // index??!
        vert_angles[0].neighbor_vert.already_chamfered.push(vert.index)
       
        if (newcell.length == 6) {
           build_verts_and_faces(newcell, "shape")  // "shape" == hex?
        }
     }





         var newcell = []
        
         newcell.push([vert.x, vert.y])
       if (contains(vert.index, vert_angles[anglelen-1].neighbor_vert.already_chamfered) == false && anglelen > 1) {
 
       var cellindex_contributor = intersection_of_arrays(vert_angles[anglelen-1].neighbor_vert.partof, vert_angles[0].neighbor_vert.partof, vert.partof)
       var er_cellindex =  parseInt(cellindex_contributor[0])
 
        if (er_cellindex != undefined && isNaN(er_cellindex) == false) {
            var thecellcenter = old_allcells[er_cellindex].center
           if (not_LifeOnTheEdge) {
            newcell.push([(vert.x + thecellcenter[0])/2, (vert.y + thecellcenter[1])/2])
            newcell.push([(vert_angles[anglelen-1].neighbor_vert.x + thecellcenter[0])/2, (vert_angles[anglelen-1].neighbor_vert.y + thecellcenter[1])/2])
           }
           else {
            newcell.push([(((vert.x + thecellcenter[0])/2)+vert.x)/2, (((vert.y + thecellcenter[1])/2)+vert.y)/2])
            newcell.push([(((vert_angles[anglelen-1].neighbor_vert.x + thecellcenter[0])/2) +vert_angles[anglelen-1].neighbor_vert.x)/2, (((vert_angles[anglelen-1].neighbor_vert.y + thecellcenter[1])/2)+vert_angles[anglelen-1].neighbor_vert.y)/2])
           }

        }
        newcell.push([vert_angles[anglelen-1].neighbor_vert.x, vert_angles[anglelen-1].neighbor_vert.y])
 
       // now do the same thing for angles[i-1] (except, maybe do angles[i-1] first for counter_clockwise versus clockwise??)

       var cellindex_contributor = intersection_of_arrays(vert_angles[anglelen-1].neighbor_vert.partof, vert_angles[anglelen-2].neighbor_vert.partof, vert.partof)
       var er_cellindex =  parseInt(cellindex_contributor[0])

        if (er_cellindex != undefined && isNaN(er_cellindex) == false) {
            var thecellcenter = old_allcells[er_cellindex].center
           if (not_LifeOnTheEdge) {
            newcell.push([(vert_angles[anglelen-1].neighbor_vert.x + thecellcenter[0])/2, (vert_angles[anglelen-1].neighbor_vert.y + thecellcenter[1])/2])
            newcell.push([(vert.x + thecellcenter[0])/2, (vert.y + thecellcenter[1])/2])
           }
           else {
            newcell.push([((vert_angles[anglelen-1].neighbor_vert.x + thecellcenter[0])/2 +vert_angles[anglelen-1].neighbor_vert.x)/2, ((vert_angles[anglelen-1].neighbor_vert.y + thecellcenter[1])/2 +vert_angles[anglelen-1].neighbor_vert.y)/2])
            newcell.push([((vert.x + thecellcenter[0])/2+vert.x)/2, ((vert.y + thecellcenter[1])/2 +vert.y)/2])

           }

        }
      // now mark the corresponding vertex for each vertex as "converted" - a way of not reusing edges
        vert.already_chamfered.push(vert_angles[anglelen-1].neighbor_vert.index) 
        vert_angles[anglelen-1].neighbor_vert.already_chamfered.push(vert.index)
       
        if (newcell.length == 6) {
           build_verts_and_faces(newcell, "shape")  // "shape" == hex?
        }
     }






     for (var i = 1; i<anglelen-1; i++) {
         var newcell = []
         newcell.push([vert.x, vert.y])
        // if (contains(vert.index, vert_angles[i].neighbor_vert.already_chamfered) == false &&  contains( vert_angles[i].neighbor_vert.index,vert.already_chamfered) == false && anglelen > 1) {
        if (contains(vert.index, vert_angles[i].neighbor_vert.already_chamfered) == false && anglelen > 1) {

       var cellindex_contributor = intersection_of_arrays(vert_angles[i].neighbor_vert.partof, vert_angles[i+1].neighbor_vert.partof, vert.partof)
       var er_cellindex =  parseInt(cellindex_contributor[0])

        if (er_cellindex != undefined && isNaN(er_cellindex) == false) {
            var thecellcenter = old_allcells[er_cellindex].center
           if (not_LifeOnTheEdge) {
            newcell.push([(vert.x + thecellcenter[0])/2, (vert.y + thecellcenter[1])/2])
            newcell.push([(vert_angles[i].neighbor_vert.x + thecellcenter[0])/2, (vert_angles[i].neighbor_vert.y + thecellcenter[1])/2])
           }
           else {
            newcell.push([(((vert.x + thecellcenter[0])/2)+vert.x)/2, (((vert.y + thecellcenter[1])/2)+vert.y)/2])
            newcell.push([(((vert_angles[i].neighbor_vert.x + thecellcenter[0])/2)+vert_angles[i].neighbor_vert.x)/2, (((vert_angles[i].neighbor_vert.y + thecellcenter[1])/2)+vert_angles[i].neighbor_vert.y)/2])
           }
        }
        newcell.push([vert_angles[i].neighbor_vert.x, vert_angles[i].neighbor_vert.y])

       // now do the same thing for angles[i-1] (except, maybe do angles[i-1] first for counter_clockwise versus clockwise??)

       var cellindex_contributor = intersection_of_arrays(vert_angles[i].neighbor_vert.partof, vert_angles[i-1].neighbor_vert.partof, vert.partof)
       var er_cellindex =  parseInt(cellindex_contributor[0])

        if (er_cellindex != undefined && isNaN(er_cellindex) == false) {
            var thecellcenter = old_allcells[er_cellindex].center
           if (not_LifeOnTheEdge) {
            newcell.push([(vert_angles[i].neighbor_vert.x + thecellcenter[0])/2, (vert_angles[i].neighbor_vert.y + thecellcenter[1])/2])
            newcell.push([(vert.x + thecellcenter[0])/2, (vert.y + thecellcenter[1])/2])
           }
           else {
            newcell.push([(((vert_angles[i].neighbor_vert.x + thecellcenter[0])/2)+vert_angles[i].neighbor_vert.x)/2, (((vert_angles[i].neighbor_vert.y + thecellcenter[1])/2)+vert_angles[i].neighbor_vert.y)/2])
            newcell.push([(((vert.x + thecellcenter[0])/2)+vert.x)/2, (((vert.y + thecellcenter[1])/2)+vert.y)/2])

           }

        }
      // now mark the corresponding vertex for each vertex as "converted" - a way of not reusing edges
        vert.already_chamfered.push(vert_angles[i].neighbor_vert.index) // index??!
        vert_angles[i].neighbor_vert.already_chamfered.push(vert.index)
        if (newcell.length == 6) {
           build_verts_and_faces(newcell, "shape")  // "shape" == hex?
        }
      }
     }
   }
/*

      initialize()

   old_allcells = allcells
   old_aci = aci
   old_vertexArray = vertexArray
   old_v_index = v_index
   
    x_to_verts = []; flr_x_to_verts = []
     vertexArray = [] 
     v_index = 1;
     vertexArray[0] = settypecellvertex  // This shouldn't be used, but  
     allcells = [] 
     aci = 0;  

    var bordercells = get_border_cell_indices (old_vertexArray, old_allcells, "thick")
    for (var i = old_aci; i--;) {
        if (contains(i, bordercells) == false) { 
          var oldcellcoords = old_allcells[i].coords
          var newcell = []
          var lencell = oldcellcoords.length
          for (var j = 0; j<lencell; j+=2) {
              newcell.push([oldcellcoords[j], oldcellcoords[j+1]])
          }
          build_verts_and_faces(newcell, "shape")
        }
     }

           
           
   initialize()

   old_allcells = allcells
   old_aci = aci
   old_vertexArray = vertexArray
   old_v_index = v_index
   
    x_to_verts = []; flr_x_to_verts = []
     vertexArray = [] 
     v_index = 1;
     vertexArray[0] = settypecellvertex  // This shouldn't be used, but  
     allcells = [] 
     aci = 0;  

    var bordercells = get_border_cell_indices (old_vertexArray, old_allcells, "thin")
    for (var i = old_aci; i--;) {
        if (contains(i, bordercells) == false) { 
          var oldcellcoords = old_allcells[i].coords
          var newcell = []
          var lencell = oldcellcoords.length
          for (var j = 0; j<lencell; j+=2) {
              newcell.push([oldcellcoords[j], oldcellcoords[j+1]])
          }
          build_verts_and_faces(newcell, "shape")
        }
     }

*/         
           

      initialize()

      clearboard() 
      drawboard()  
      drawgrid()
     old_allcells = []  // not necessarily enough for garbage collection
}


      
function honeycombization() {
   calculate_all_centers ()
   old_allcells = allcells
   old_aci = aci
   old_vertexArray = vertexArray
   old_v_index = v_index
   
    x_to_verts = []; flr_x_to_verts = []
     vertexArray = [] 
     v_index = 1;
     vertexArray[0] = settypecellvertex  // This shouldn't be used, but  
     allcells = [] 
     aci = 0;  

     var coordslen;
     for (var i = old_aci; i--;) {
           var crds = old_allcells[i].coords
           var coordslen = crds.length

           var inset_pairs = []   // of the form [[x1, y1], [x2, y2] ...]
 
           var centerX = old_allcells[i].center[0]
           var centerY = old_allcells[i].center[1]


          
       //     trailing_midpoint_x  = (crds[coordslen-2] + crds[0])/2
       //     trailing_midpoint_y  = (crds[coordslen-1] + crds[1])/2
            leading_midpoint_x = (crds[0] + crds[2])/2
            leading_midpoint_y = (crds[1] + crds[3])/2
        //    build_verts_and_faces ([[trailing_midpoint_x, trailing_midpoint_y],[crds[0], crds[1]], [leading_midpoint_x, leading_midpoint_y]], "triangle");  // beginning
            
         //   inset_pairs.push([leading_midpoint_x, leading_midpoint_y])
         //   inset_pairs.push([((crds[2] + centerX)/2 + centerX)/2, ((crds[3] + centerY)/2 + centerY)/2])

              inset_pairs.push([(leading_midpoint_x + centerX)/2, (leading_midpoint_y + centerY)/2])
 


        for (var j = 2; j<coordslen-2; j+=2) {
        //    trailing_midpoint_x  = (crds[j-2] + crds[j])/2
        //    trailing_midpoint_y  = (crds[j-1] + crds[j+1])/2
            leading_midpoint_x = (crds[j] + crds[j+2])/2
            leading_midpoint_y = (crds[j+1] + crds[j+3])/2

         //   build_verts_and_faces ([[trailing_midpoint_x, trailing_midpoint_y],[crds[j], crds[j+1]], [leading_midpoint_x, leading_midpoint_y]], "triangle");  // middles  
          //  inset_pairs.push([leading_midpoint_x, leading_midpoint_y])
          //  inset_pairs.push([((crds[j+2] + centerX)/2  + centerX)/2, ((crds[j+3] + centerY)/2 +  centerY)/2])

              inset_pairs.push([(leading_midpoint_x + centerX)/2, (leading_midpoint_y + centerY)/2])


      
         }

         //   trailing_midpoint_x  = (crds[coordslen-4] + crds[coordslen-2])/2
         //   trailing_midpoint_y  = (crds[coordslen-3] + crds[coordslen-1])/2
            leading_midpoint_x = (crds[coordslen-2] + crds[0])/2
            leading_midpoint_y = (crds[coordslen-1] + crds[1])/2
          //  build_verts_and_faces ([[trailing_midpoint_x, trailing_midpoint_y],[crds[coordslen-2], crds[coordslen-1]], [leading_midpoint_x, leading_midpoint_y]], "triangle");  // beginning
         //   inset_pairs.push([leading_midpoint_x, leading_midpoint_y])
         //   inset_pairs.push([((crds[0]  + centerX)/2 +  centerX)/2, ((crds[1] + centerY)/2 +  centerY)/2])
        
              inset_pairs.push([(leading_midpoint_x + centerX)/2, (leading_midpoint_y + centerY)/2])


           if (inset_pairs.length > 2) {
               build_verts_and_faces(inset_pairs, "shape")
            }



      }

  
   // now handle the vertices to fill the holes between the insets

   for (var v = 1; v<old_v_index; v++) {
      var newcell = []
      var vert = old_vertexArray[v]
      var neighbors = neighbor_vertices(vert, old_allcells)

    // associate with the needed midpoint, so it can be used after the sort
      var angles = [] // angles will need to be sorted
      for (var i = neighbors.length; i--;) {
        var midpt = [(vert.x + old_vertexArray[neighbors[i]].x)/2, (vert.y + old_vertexArray[neighbors[i]].y)/2]
        angles.push({angle: vertex_angle(vert.x, vert.y, old_vertexArray[neighbors[i]].x, old_vertexArray[neighbors[i]].y), midpoint: midpt, partof: old_vertexArray[neighbors[i]].partof})
      }
      angles.sort(function(a,b){return a.angle - b.angle})  // numerical sort lowest to highest
     // now that the angles are sorted (equivalent to vn neighbors around the clock), get the leading_inset_point for each edge

      if (angles.length > 2) {
        var cellindex_contributor = intersection_of_arrays(angles[0].partof, angles[1].partof, vert.partof) // vert.partof
        var er_cellindex =  parseInt(cellindex_contributor[0])
        if (er_cellindex != undefined && isNaN(er_cellindex) == false) {
           var thecellcenter = old_allcells[er_cellindex].center
           angles[0].leading_inset_point = [(thecellcenter[0] + angles[0].midpoint[0])/2 , (thecellcenter[1] + angles[0].midpoint[1])/2]
        }
     

        var cellindex_contributor2 = intersection_of_arrays(angles[0].partof, angles[angles.length-1].partof, vert.partof) // vert.partof and angles[angles.length-1]
        var er_cellindex =  parseInt(cellindex_contributor2[0])
        if (er_cellindex != undefined && isNaN(er_cellindex) == false) {
           var thecellcenter = old_allcells[er_cellindex].center
           angles[0].trailing_inset_point = [(thecellcenter[0] + angles[0].midpoint[0])/2 , (thecellcenter[1] + angles[0].midpoint[1])/2]
        }
      
     for (var i = 1; i<angles.length-1; i++) {
       
       var cellindex_contributor = intersection_of_arrays(angles[i].partof, angles[i+1].partof, vert.partof) // vert.partof
       var er_cellindex =  parseInt(cellindex_contributor[0])

        if (er_cellindex != undefined && isNaN(er_cellindex) == false) {
           var thecellcenter = old_allcells[er_cellindex].center
           angles[i].leading_inset_point = [(thecellcenter[0] + angles[i].midpoint[0])/2, (thecellcenter[1] + angles[i].midpoint[1])/2 ]
        }


       var cellindex_contributor2 = intersection_of_arrays(angles[i].partof, angles[i-1].partof, vert.partof) // vert.partof and angles[i-1] inststead of angles[i+1]
       var er_cellindex =  parseInt(cellindex_contributor2[0])

        if (er_cellindex != undefined && isNaN(er_cellindex) == false) {
           var thecellcenter = old_allcells[er_cellindex].center
           angles[i].trailing_inset_point = [(thecellcenter[0] + angles[i].midpoint[0])/2, (thecellcenter[1] + angles[i].midpoint[1])/2 ]
        }

     }
 

        var cellindex_contributor = intersection_of_arrays(angles[angles.length-1].partof, angles[0].partof, vert.partof) // vert.partof
        var er_cellindex =  parseInt(cellindex_contributor[0])
        if (er_cellindex != undefined && isNaN(er_cellindex) == false) {
           var thecellcenter = old_allcells[er_cellindex].center
           angles[angles.length-1].leading_inset_point = [(thecellcenter[0] + angles[angles.length-1].midpoint[0])/2 , (thecellcenter[1] + angles[angles.length-1].midpoint[1])/2]
        }

        var cellindex_contributor2 = intersection_of_arrays(angles[angles.length-1].partof, angles[angles.length-2].partof, vert.partof) // vert.partof and angles[angles.length-2]
        var er_cellindex =  parseInt(cellindex_contributor2[0])
        if (er_cellindex != undefined && isNaN(er_cellindex) == false) {
           var thecellcenter = old_allcells[er_cellindex].center
           angles[angles.length-1].trailing_inset_point = [(thecellcenter[0] + angles[angles.length-1].midpoint[0])/2 , (thecellcenter[1] + angles[angles.length-1].midpoint[1])/2]
        }
      }

    
   
      var newcell = []
      for (var i = angles.length; i--;) {

        if (angles[i].leading_inset_point != undefined) {
          newcell.push(angles[i].leading_inset_point)
        }
        if (angles[i].trailing_inset_point != undefined) {
          newcell.push(angles[i].trailing_inset_point)
        }
       // newcell.push(angles[i].midpoint)

      }
     if (newcell.length > 2) {
        build_verts_and_faces(newcell, "shape")
     }
   }

  

      initialize()
      clearboard() 
      drawboard()  
      drawgrid()

     old_allcells = []  // not necessarily enough for garbage collection
}

/*
function mutant_honeycombization() { // mutant honeycombization makes a visually compelling tiling with holes that need filling
   calculate_all_centers ()
   old_allcells = allcells
   old_aci = aci
   old_vertexArray = vertexArray
   old_v_index = v_index
   
    x_to_verts = []; flr_x_to_verts = []
     vertexArray = [] 
     v_index = 1;
     vertexArray[0] = settypecellvertex  // This shouldn't be used, but  
     allcells = [] 
     aci = 0;  

     var coordslen;
     for (var i = old_aci; i--;) {
           var crds = old_allcells[i].coords
           var coordslen = crds.length

           var inset_pairs = []   // of the form [[x1, y1], [x2, y2] ...]
 
           var centerX = old_allcells[i].center[0]
           var centerY = old_allcells[i].center[1]
       //     trailing_midpoint_x  = (crds[coordslen-2] + crds[0])/2
       //     trailing_midpoint_y  = (crds[coordslen-1] + crds[1])/2
            leading_midpoint_x = (crds[0] + crds[2])/2
            leading_midpoint_y = (crds[1] + crds[3])/2
        //    build_verts_and_faces ([[trailing_midpoint_x, trailing_midpoint_y],[crds[0], crds[1]], [leading_midpoint_x, leading_midpoint_y]], "triangle");  // beginning
            
         //   inset_pairs.push([leading_midpoint_x, leading_midpoint_y])
         //   inset_pairs.push([((crds[2] + centerX)/2 + centerX)/2, ((crds[3] + centerY)/2 + centerY)/2])

              inset_pairs.push([(leading_midpoint_x + centerX)/2, (leading_midpoint_y + centerY)/2])
 


        for (var j = 2; j<coordslen-2; j+=2) {
        //    trailing_midpoint_x  = (crds[j-2] + crds[j])/2
        //    trailing_midpoint_y  = (crds[j-1] + crds[j+1])/2
            leading_midpoint_x = (crds[j] + crds[j+2])/2
            leading_midpoint_y = (crds[j+1] + crds[j+3])/2

         //   build_verts_and_faces ([[trailing_midpoint_x, trailing_midpoint_y],[crds[j], crds[j+1]], [leading_midpoint_x, leading_midpoint_y]], "triangle");  // middles  
          //  inset_pairs.push([leading_midpoint_x, leading_midpoint_y])
          //  inset_pairs.push([((crds[j+2] + centerX)/2  + centerX)/2, ((crds[j+3] + centerY)/2 +  centerY)/2])

              inset_pairs.push([(leading_midpoint_x + centerX)/2, (leading_midpoint_y + centerY)/2])
         }

         //   trailing_midpoint_x  = (crds[coordslen-4] + crds[coordslen-2])/2
         //   trailing_midpoint_y  = (crds[coordslen-3] + crds[coordslen-1])/2
            leading_midpoint_x = (crds[coordslen-2] + crds[0])/2
            leading_midpoint_y = (crds[coordslen-1] + crds[1])/2
          //  build_verts_and_faces ([[trailing_midpoint_x, trailing_midpoint_y],[crds[coordslen-2], crds[coordslen-1]], [leading_midpoint_x, leading_midpoint_y]], "triangle");  // beginning
         //   inset_pairs.push([leading_midpoint_x, leading_midpoint_y])
         //   inset_pairs.push([((crds[0]  + centerX)/2 +  centerX)/2, ((crds[1] + centerY)/2 +  centerY)/2])
        
              inset_pairs.push([(leading_midpoint_x + centerX)/2, (leading_midpoint_y + centerY)/2])
            build_verts_and_faces(inset_pairs, "shape")
      }
   // now handle the vertices to fill the holes between the insets

   for (var v = 1; v<old_v_index; v++) {
      var newcell = []
      var vert = old_vertexArray[v]
      var neighbors = neighbor_vertices(vert, old_allcells)

    // associate with the needed midpoint, so it can be used after the sort
      var angles = [] // angles will need to be sorted
      for (var i = neighbors.length; i--;) {
        var midpt = [(vert.x + old_vertexArray[neighbors[i]].x)/2, (vert.y + old_vertexArray[neighbors[i]].y)/2]
        angles.push({angle: vertex_angle(vert.x, vert.y, old_vertexArray[neighbors[i]].x, old_vertexArray[neighbors[i]].y), midpoint: midpt, partof: old_vertexArray[neighbors[i]].partof})
      }
      angles.sort(function(a,b){return a.angle - b.angle})  // numerical sort lowest to highest
     // now that the angles are sorted (equivalent to vn neighbors around the clock), get the leading_inset_point for each edge

     for (var i = 0; i<angles.length-1; i++) {

       var cellindex_contributor = intersection_of_arrays(angles[i].partof, angles[i+1].partof, vert.partof) // vert.partof
       var er_cellindex =  parseInt(cellindex_contributor[0])

        if (er_cellindex != undefined && isNaN(er_cellindex) == false) {
           var thecellcenter = old_allcells[er_cellindex].center
           angles[i].leading_inset_point = [(thecellcenter[0] + angles[i].midpoint[0])/2, (thecellcenter[1] + angles[i].midpoint[1])/2 ]
        }
     }
        var cellindex_contributor = intersection_of_arrays(angles[angles.length-1].partof, angles[0].partof, vert.partof) // vert.partof
        var er_cellindex =  parseInt(cellindex_contributor[0])

        if (er_cellindex != undefined && isNaN(er_cellindex) == false) {
           var thecellcenter = old_allcells[er_cellindex].center
           angles[angles.length-1].leading_inset_point = [(thecellcenter[0] + angles[angles.length-1].midpoint[0])/2 , (thecellcenter[1] + angles[angles.length-1].midpoint[1])/2]
        }
      var newcell = []
      for (var i = angles.length; i--;) {
        if (angles[i].leading_inset_point != undefined) {
          newcell.push(angles[i].leading_inset_point)
        }
       // newcell.push(angles[i].midpoint)

      }
     if (newcell.length > 2) {
        build_verts_and_faces(newcell, "shape")
     }
   }
       initialize()
      clearboard() 
      drawboard()  
      drawgrid()
     old_allcells = []  // not necessarily enough for garbage collection
}
*/

function pentagon_restricted_easy_dual_tiling() {
  calculate_all_centers()  // could test to see if .centers have already been calculated earlier

   old_allcells = allcells
   old_aci = aci
   old_vertexArray = vertexArray
   old_v_index = v_index
   
    x_to_verts = []; flr_x_to_verts = []
     vertexArray = [] 
     v_index = 1;
     vertexArray[0] = settypecellvertex  // This shouldn't be used, but  
     allcells = [] 
     aci = 0;  

  for (var v = 1; v<old_v_index; v++) {
     var newcell = []
     var center_donors  = old_vertexArray[v].partof.slice()   // The slice() makes a copy so that center_donors has values not a reference!!!!
     if (center_donors.length <=3) continue;  // was <=2 but this is restricted to pentagons
   /*
     if (center_donors.length == 3) { 
         build_verts_and_faces([old_allcells[center_donors[0]].center, old_allcells[center_donors[1]].center, old_allcells[center_donors[2]].center], "triangle")
     }
   */
     else { // we have to figure out what order to put the center donors in:
       var current_cell, next_cell;
       current_cell = center_donors[0] // note that current cell will be a cell.index, not an actual cell   
       newcell.push(old_allcells[current_cell].center)  //and add current_cell's center to newcell
       center_donors.splice(0,1)   // and remove current_cell from center_donors using splice

       while (center_donors.length > 1) {
         next_cell = find_first_vn_neighbor_from_indices(current_cell, center_donors) 
         if (next_cell == "none") break;
         newcell.push(old_allcells[next_cell].center)     
         var temp_cell_index = center_donors.indexOf(next_cell)   // info needed to remove next_cell from center_donors // doesnt work with IE7, IE8
         center_donors.splice(temp_cell_index, 1) // remove cell from center_donors 
         current_cell = next_cell
       }

      newcell.push(old_allcells[center_donors[0]].center)  // finally, push on the very last center donor.
      if (next_cell != "none") {
        if (newcell.length == 5) {
           build_verts_and_faces(newcell,  "shape")  // <-- optionally classify the shape by number of sides, etc 
        }
      }
    }  // end else
  }  // end for all vertices
  initialize()
       clearboard() 
       drawboard()  
       drawgrid()

  old_allcells = []  // not necessarily enough for garbage collection
}


function easy_dual_tiling() {
  calculate_all_centers()  // could test to see if .centers have already been calculated earlier

   old_allcells = allcells
   old_aci = aci
   old_vertexArray = vertexArray
   old_v_index = v_index
   
    x_to_verts = []; flr_x_to_verts = []
     vertexArray = [] 
     v_index = 1;
     vertexArray[0] = settypecellvertex  // This shouldn't be used, but  
     allcells = [] 
     aci = 0;  

  for (var v = 1; v<old_v_index; v++) {
     var newcell = []
     var center_donors  = old_vertexArray[v].partof.slice()   // The slice() makes a copy so that center_donors has values not a reference!!!!
     if (center_donors.length <=2) continue;
     if (center_donors.length == 3) { 
         build_verts_and_faces([old_allcells[center_donors[0]].center, old_allcells[center_donors[1]].center, old_allcells[center_donors[2]].center], "triangle")
     }
     else { // we have to figure out what order to put the center donors in:
       var current_cell, next_cell;
       current_cell = center_donors[0] // note that current cell will be a cell.index, not an actual cell   
       newcell.push(old_allcells[current_cell].center)  //and add current_cell's center to newcell
       center_donors.splice(0,1)   // and remove current_cell from center_donors using splice

       while (center_donors.length > 1) {
         next_cell = find_first_vn_neighbor_from_indices(current_cell, center_donors) 
         if (next_cell == "none") break;
         newcell.push(old_allcells[next_cell].center)     
         var temp_cell_index = center_donors.indexOf(next_cell)   // info needed to remove next_cell from center_donors // doesnt work with IE7, IE8
         center_donors.splice(temp_cell_index, 1) // remove cell from center_donors 
         current_cell = next_cell
       }

      newcell.push(old_allcells[center_donors[0]].center)  // finally, push on the very last center donor.
      if (next_cell != "none") {
        build_verts_and_faces(newcell,  "shape")  // <-- optionally classify the shape by number of sides, etc 
      }
    }  // end else
  }  // end for all vertices
  initialize()
       clearboard() 
       drawboard()  
       drawgrid()

  old_allcells = []  // not necessarily enough for garbage collection
}

// *** refer to old_allcells here
// this is an auxilliary function for easy_dual_tiling()   
function find_first_vn_neighbor_from_indices(current_cell, center_donors) {
  for (var i = center_donors.length; i--;) {
     if (contains(old_allcells[current_cell], old_allcells[center_donors[i]].VNneighbors)) {return center_donors[i]}
  } 
  return "none"
}

       //  next_cell =  center_donors[0];  // this is a fallback value until the correct next_cell can be calculated below.  
       //  for (var i = center_donors.length; i--;) { // essentially find_first_vn_neighbor_from_indices(current_cell, center_donors) 
       //     if (contains(old_allcells[current_cell], old_allcells[center_donors[i]].VNneighbors)) {next_cell =  center_donors[i]}
       //  }


// this is an auxilliary function for easy_dual_tiling() but it might be useful for other projects.
function calculate_all_centers () {
  for (var i = aci; i--;) {
        var crds = allcells[i].coords
        var coordslen = crds.length
        var center_x = crds[0]
        var center_y = crds[1] 
        for (var j= 2; j<coordslen; j+=2) {
            center_x = center_x + crds[j]
            center_y = center_y + crds[j+1]
        }
        allcells[i].center= [ center_x/(coordslen/2), center_y = center_y/(coordslen/2)]
  }
}



// From http://debian.fmi.uni-sofia.bg/~sergei/cgsr/docs/clockwise.htm
//  Convex function, Written by Paul Bourke
//      See also http://stackoverflow.com/questions/471962/how-do-determine-if-a-polygon-is-complex-convex-nonconvex


function convex(p, n)  {
   var i,j,k;
   var flag = 0;
   var z;

   if (n < 3)
      return 0;

   for (i=0;i<n;i++) {
      j = (i + 1) % n;
      k = (i + 2) % n;
      z  = (p[j][0] - p[i][0]) * (p[k][1] - p[j][1]);
      z -= (p[j][1] - p[i][1]) * (p[k][0] - p[j][0]);
      if (z < 0)
         flag |= 1;
      else if (z > 0)
         flag |= 2;
      if (flag == 3)
         return "concave";
   }
   if (flag != 0)
      return "convex";
   else
      return "complicated";
}


/*
function coords_pairs (crds) {
   var coordslen = crds.length
   var not_flat = []
   for (var z=0; z<coordslen; z+=2) {
       not_flat.push([crds[z], crds[z+1]])   
   }   
   return not_flat   
} 

function call_convex(cel) {

   return convex( coords_pairs(cel.coords), cel.fnn.length)
}
*/


function call_convex(cel) {
   var crds = cel.coords
   var coordslen = crds.length
   var coords_pairs = []
   for (var z=0; z<coordslen; z+=2) {
       coords_pairs.push([crds[z], crds[z+1]])   
   } 
   return convex( coords_pairs, cel.fnn.length)
}



// A test for clockwise or counterclockwise polygon vertices
   // from http://paulbourke.net/geometry/polygonmesh/
   // See "Determining whether or not a polygon (2D) has its vertices ordered clockwise or counterclockwise"
   // 0, 1  i-1
   // 2, 3  i
   // 4, 5  i+1

//  Note this only works for convex polygons -- see the link for a test that works for concave polygons

function clockwise_test_for_convex_polygons (cel) {
   var crds = cel.coords
   var result = (crds[2] - crds[0])*(crds[5]-crds[3]) - (crds[3]-crds[1])*(crds[4]-crds[2])
   if (result > 0) {return "counter_clockwise"} else {return "clockwise"}
}




function pentagonalization() {
   old_allcells = allcells
   old_aci = aci
   
    x_to_verts = []; flr_x_to_verts = []
     vertexArray = [] 
     v_index = 1;
     vertexArray[0] = settypecellvertex  // This shouldn't be used, but  
     allcells = [] 
     aci = 0;  
    // var newcoords = []
     var coordslen;
     for (var i = old_aci; i--;) {
        var crds = old_allcells[i].coords

        var coordslen = crds.length
        var center_x = crds[0]
        var center_y = crds[1] 
        for (var j= 2; j<coordslen; j+=2) {
            center_x = center_x + crds[j]
            center_y = center_y + crds[j+1]
        }
        center_x = center_x/(coordslen/2)
        center_y = center_y/(coordslen/2)

        if  ( clockwise_test_for_convex_polygons(old_allcells[i]) == "counter_clockwise" ) {       // || old_allcells[i].type == "star") {
          
            trailing_midpoint_x  =  (  (crds[coordslen-2] + crds[0])/2   +    crds[coordslen-2])/2
            trailing_midpoint_y  =  (  (crds[coordslen-1] + crds[1])/2   +   crds[coordslen-1])/2
            pent_x =  (  (crds[coordslen-2] + crds[0])/2   +    crds[0])/2
            pent_y =  (  (crds[coordslen-1] + crds[1])/2   +    crds[1])/2
            leading_midpoint_x =    ( (crds[0] + crds[2])/2              +    crds[0]  ) /2   
            leading_midpoint_y =    ( (crds[1] + crds[3])/2              +    crds[1]  )/2     
            build_verts_and_faces ([[trailing_midpoint_x, trailing_midpoint_y],[pent_x, pent_y], [crds[0], crds[1]], [leading_midpoint_x, leading_midpoint_y], [center_x, center_y]], "pent");  // beginning

        for (var j = 2; j<coordslen-2; j+=2) {
            trailing_midpoint_x  = (  (crds[j-2] + crds[j])/2   +  crds[j-2] ) /2  
            trailing_midpoint_y  = (  (crds[j-1] + crds[j+1])/2 +  crds[j-1] ) /2
            pent_x =  (  (crds[j-2] + crds[j])/2     +    crds[j])/2
            pent_y =  (  (crds[j-1] + crds[j+1])/2   +    crds[j+1])/2
            leading_midpoint_x =   (  (crds[j] + crds[j+2])/2   +  crds[j]   ) /2   
            leading_midpoint_y =   (  (crds[j+1] + crds[j+3])/2 +  crds[j+1] ) /2  

            build_verts_and_faces ([[trailing_midpoint_x, trailing_midpoint_y],[pent_x, pent_y],[crds[j], crds[j+1]], [leading_midpoint_x, leading_midpoint_y], [center_x, center_y]], "pent");  // middles        
         }

            trailing_midpoint_x  = (  (crds[coordslen-4] + crds[coordslen-2])/2   + crds[coordslen-4] )/2
            trailing_midpoint_y  = (  (crds[coordslen-3] + crds[coordslen-1])/2   + crds[coordslen-3] )/2
            pent_x  = (  (crds[coordslen-4] + crds[coordslen-2])/2   + crds[coordslen-2] )/2
            pent_y  = (  (crds[coordslen-3] + crds[coordslen-1])/2   + crds[coordslen-1] )/2
            leading_midpoint_x =   (  (crds[coordslen-2] + crds[0])/2             + crds[coordslen-2] )/2
            leading_midpoint_y =   (  (crds[coordslen-1] + crds[1])/2             + crds[coordslen-1] )/2
       
           build_verts_and_faces ([[trailing_midpoint_x, trailing_midpoint_y],[pent_x, pent_y],[crds[coordslen-2], crds[coordslen-1]], [leading_midpoint_x, leading_midpoint_y], [center_x, center_y]], "pent");  // beginning
        }
       else { // skew in other direction 

            trailing_midpoint_x  =  (  (crds[coordslen-2] + crds[0])/2   +    crds[0])/2
            trailing_midpoint_y  =  (  (crds[coordslen-1] + crds[1])/2   +   crds[1])/2
            pent_x =   ( (crds[0] + crds[2])/2              +    crds[0]  ) /2  
            pent_y =   ( (crds[1] + crds[3])/2              +    crds[1]  )/2   
            leading_midpoint_x =    ( (crds[0] + crds[2])/2              +    crds[2]  ) /2   
            leading_midpoint_y =    ( (crds[1] + crds[3])/2              +    crds[3]  )/2     
            build_verts_and_faces ([[trailing_midpoint_x, trailing_midpoint_y], [crds[0], crds[1]], [pent_x, pent_y],[leading_midpoint_x, leading_midpoint_y], [center_x, center_y]], "pent");  // beginning

        for (var j = 2; j<coordslen-2; j+=2) {
            trailing_midpoint_x  = (  (crds[j-2] + crds[j])/2   +  crds[j] ) /2  
            trailing_midpoint_y  = (  (crds[j-1] + crds[j+1])/2 +  crds[j+1] ) /2
            pent_x =  (  (crds[j] + crds[j+2])/2   +  crds[j]   ) /2   
            pent_y =  (  (crds[j+1] + crds[j+3])/2 +  crds[j+1] ) /2 
            leading_midpoint_x =   (  (crds[j] + crds[j+2])/2   +  crds[j+2]   ) /2   
            leading_midpoint_y =   (  (crds[j+1] + crds[j+3])/2 +  crds[j+3] ) /2  

           build_verts_and_faces ([[trailing_midpoint_x, trailing_midpoint_y],[crds[j], crds[j+1]],[pent_x, pent_y], [leading_midpoint_x, leading_midpoint_y], [center_x, center_y]], "pent");  // middles        
         }

            trailing_midpoint_x  = (  (crds[coordslen-4] + crds[coordslen-2])/2   + crds[coordslen-2] )/2
            trailing_midpoint_y  = (  (crds[coordslen-3] + crds[coordslen-1])/2   + crds[coordslen-1] )/2
            pent_x  =  (  (crds[coordslen-2] + crds[0])/2             + crds[coordslen-2] )/2
            pent_y  =  (  (crds[coordslen-1] + crds[1])/2             + crds[coordslen-1] )/2
            leading_midpoint_x =   (  (crds[coordslen-2] + crds[0])/2             + crds[0] )/2
            leading_midpoint_y =   (  (crds[coordslen-1] + crds[1])/2             + crds[1] )/2
       
            build_verts_and_faces ([[trailing_midpoint_x, trailing_midpoint_y],[crds[coordslen-2], crds[coordslen-1]], [pent_x, pent_y],[leading_midpoint_x, leading_midpoint_y], [center_x, center_y]], "pent");  // beginning
        }


      }
      initialize()
      clearboard() 
      drawboard()  
      drawgrid()

     old_allcells = []  // not necessarily enough for garbage collection

      easy_dual_tiling()
     pentagon_restricted_easy_dual_tiling()  // easy_dual_tiling()
}



function linearsubdivide() {
   old_allcells = allcells
   old_aci = aci
   
    x_to_verts = []; flr_x_to_verts = []
     vertexArray = [] 
     v_index = 1;
     vertexArray[0] = settypecellvertex  // This shouldn't be used, but  
     allcells = [] 
     aci = 0;  
    // var newcoords = []
     var coordslen;
     for (var i = old_aci; i--;) {
        var crds = old_allcells[i].coords
        var coordslen = crds.length
        var center_x = crds[0]
        var center_y = crds[1] 
        for (var j= 2; j<coordslen; j+=2) {
            center_x = center_x + crds[j]
            center_y = center_y + crds[j+1]
        }
        center_x = center_x/(coordslen/2)
        center_y = center_y/(coordslen/2)

        
          
            trailing_midpoint_x  = (crds[coordslen-2] + crds[0])/2
            trailing_midpoint_y  = (crds[coordslen-1] + crds[1])/2
            leading_midpoint_x = (crds[0] + crds[2])/2
            leading_midpoint_y = (crds[1] + crds[3])/2
            build_verts_and_faces ([[trailing_midpoint_x, trailing_midpoint_y],[crds[0], crds[1]], [leading_midpoint_x, leading_midpoint_y], [center_x, center_y]], "quad");  // beginning

        for (var j = 2; j<coordslen-2; j+=2) {
            trailing_midpoint_x  = (crds[j-2] + crds[j])/2
            trailing_midpoint_y  = (crds[j-1] + crds[j+1])/2
            leading_midpoint_x = (crds[j] + crds[j+2])/2
            leading_midpoint_y = (crds[j+1] + crds[j+3])/2

            build_verts_and_faces ([[trailing_midpoint_x, trailing_midpoint_y],[crds[j], crds[j+1]], [leading_midpoint_x, leading_midpoint_y], [center_x, center_y]], "quad");  // middles        
         }

            trailing_midpoint_x  = (crds[coordslen-4] + crds[coordslen-2])/2
            trailing_midpoint_y  = (crds[coordslen-3] + crds[coordslen-1])/2
            leading_midpoint_x = (crds[coordslen-2] + crds[0])/2
            leading_midpoint_y = (crds[coordslen-1] + crds[1])/2
            build_verts_and_faces ([[trailing_midpoint_x, trailing_midpoint_y],[crds[coordslen-2], crds[coordslen-1]], [leading_midpoint_x, leading_midpoint_y], [center_x, center_y]], "quad");  // beginning

      }
      initialize()
      clearboard() 
      drawboard()  
      drawgrid()

     old_allcells = []  // not necessarily enough for garbage collection

 // To do:   Garbage collect old_allcells by setting to [] (but see link below), or save it to use as an undo step (although after the undo, there will be more vertices unless old_vertexArray is created and restored.  

   //  see http://stackoverflow.com/questions/1232040/how-to-empty-an-array-in-javascript?rq=1


    // idea:  something akin to the above could be used to simply create intermediate vertices (but not new cells). Not useful unless midpoints needed.
}

    
function snapshot_of_canvas_center(x, y) {
  var snapshotheight = 75
  var snapshotwidth = 75
 
  for (var i = aci; i--;) {
      stroke_and_fill_cell(allcells[i])
  }
      
  var imgData=ctx.getImageData(x,y, snapshotwidth, snapshotheight);

  ctx.canvas.height  = 0  // clear the canvas
  ctx.canvas.height = snapshotheight
  ctx.canvas.width =  snapshotwidth

  ctx.putImageData(imgData,0,0);

  window.open(ctx.canvas.toDataURL());
}


function print_obj() {
  windowref = window.open();
  var third = 0;
  windowref.document.writeln("<pre>")
                                                // WARNING!   toFixed is sometimes not the right solution -- may introduce roundoff error
  for (var i = 1; i<v_index; i++) {
      windowref.document.writeln("v " + vertexArray[i].x.toFixed(sigdigits) + " " + vertexArray[i].y.toFixed(sigdigits) + " " + third.toFixed(1));
  } 
  for (var i = 0; i<aci; i++) {
      windowref.document.write("f");
      var flen = allcells[i].fnn.length;
      for (var j = 0; j<flen; j++) {
          windowref.document.write(" " + allcells[i].fnn[j])
      }
      windowref.document.writeln();
  }
  windowref.document.writeln();
/*
  for (var i = 0; i<aci; i++) {
      windowref.document.write("#C " + i + " #N ")
      for (var j = 0; j<allcells[i].neighbors.length; j++) {
         windowref.document.write(allcells[i].neighbors[j].index)
         windowref.document.write(" ")
      }
      windowref.document.writeln();
   }
*/

   windowref.document.writeln(" ")
   windowref.document.writeln(" ")
   windowref.document.writeln("#R "+ birth1 + "_" + survive1 + "_C" + n_states)
   windowref.document.writeln(" ")
   windowref.document.writeln(" ")


  for (var i = 0; i<aci; i++) {
    if (allcells[i].state != 0) {
      windowref.document.writeln("#C " + i + " #S " + allcells[i].state)
     }
   }
 
  windowref.document.writeln("</pre>")
}
 
/*  print_pattern    old version 
  This old version grabs the whole screen, not just the selection
function print_pattern () {
  windowref = window.open();
  var third = 0;
  windowref.document.writeln("<pre>")
  windowref.document.writeln("#N "+ " Enter_name_here")
  windowref.document.writeln("#T "+ JSON.stringify(the_mesh_spec))

  windowref.document.write("#R "+ birth1 + "/" + survive1)
  if (rulefamily == 2) {windowref.document.write("/C" + n_states)};
  windowref.document.writeln("");

  for (var i = 0; i<aci; i++) {
    if (allcells[i].state != 0) {
      windowref.document.writeln("#C " + i + " #S " + allcells[i].state)
     }
   }
  windowref.document.writeln("</pre>")
}
*/

function print_pattern () {
  windowref = window.open();
  var third = 0;
  windowref.document.writeln("<pre>")
  windowref.document.writeln("#N "+ " Enter_name_here")
  windowref.document.writeln("#T "+ JSON.stringify(the_mesh_spec))

  windowref.document.write("#R "+ birth1 + "/" + survive1)
  if (rulefamily == 2) {windowref.document.write("/C" + n_states)};
  windowref.document.writeln("");

 if (select_clicks != 0) {
   var c_select_1st_x = Math.min(select_1st_x, select_2nd_x)/currentscale
   var c_select_2nd_x = Math.max(select_1st_x, select_2nd_x)/currentscale
   var c_select_1st_y = Math.min(select_1st_y, select_2nd_y)/currentscale
   var c_select_2nd_y = Math.max(select_1st_y, select_2nd_y)/currentscale
  for (var i = aci; i--;) {
     if (  allcells[i].coords[0] > c_select_1st_x  && allcells[i].coords[0] < c_select_2nd_x  &&  
           allcells[i].coords[1] > c_select_1st_y  && allcells[i].coords[1] < c_select_2nd_y
        ) {

             if (allcells[i].state != 0) {
                 windowref.document.writeln("#C " + i + " #S " + allcells[i].state)
             } // end if
          } // end if
   } // end for
  } // end if


  else {  // nothing is selected so just dump the whole screen  -- commented out for now

  for (var i = 0; i<aci; i++) {
    if (allcells[i].state != 0) {
      windowref.document.writeln("#C " + i + " #S " + allcells[i].state)
     }
   }
  } // end else

  windowref.document.writeln("</pre>")
}


 




  



//  JSON.stringify(the_mesh_spec) == > goes into file
// the_mesh_spec = JSON.parse(stuff from file)
     
          
      


function load_pattern(pattern_name) {
   var p = lifepatterns[pattern_name].patternstring;
   p = p.replace(" ", "");
   p = p.split("#C");
   var len = p.length;
   var entry;
   for (var i = 1; i<len; i++) {
      entry = p[i].split("#S");
      allcells[parseInt(entry[0])].state = parseInt(entry[1]);
      changedcells[changecount] = allcells[parseInt(entry[0])];
      changecount += 1;
   }
}      
   


function parse_pattern_entries(pattern_entries) {
   pattern_entries = pattern_entries.replace(/(\r\n|\n|\r)/gm,"-----newline-----");
   pattern_entries = pattern_entries.split("-----newline-----");
   pattern_entries[pattern_entries.length] = "";  // ????
    var newname = "";
    var newtiling = ""
    var newrule = "all";
    var newpatternstring = "";
   var len = pattern_entries.length
   for (var i = 0; i < len; i++) {
      entry = pattern_entries[i].split(" ");
      if (entry[0] == "#N") {
          newname = entry[1]
          continue;
       }
      if (entry[0] == "#T") {
          newtiling = JSON.parse(entry[1])
          continue;
       }
       if (entry[0] == "#R") {
          newrule = entry[1]
          continue;
       }
       if (entry[0] == "#C") {
          newpatternstring = newpatternstring.concat(" " + pattern_entries[i] + " ");
          continue;
       }
       if (newname != "") {
         lifepatterns[newname] = {tiling: newtiling, rule: newrule, patternstring: newpatternstring};
       }
      newname = "";
      newtiling = ""
      newrule = "all";
      newpatternstring = "";
    }
}

  
function parse_init_div(div_entries) {
    if (document.getElementById("Init_div") == null) { return ""}   
    div_entries = document.getElementById("Init_div").innerHTML
    div_entries =  div_entries.replace(/(\r\n|\n|\r)/gm,"-----newline-----");
    div_entries = div_entries.split("-----newline-----");
    var arr = div_entries;
    var len = div_entries.length

    for (var i = 0; i < len; i++) {
       var entry = arr[i].split(" ");
 
      if (entry[0] == "#T") {
          the_mesh_spec = JSON.parse(entry[1])
          continue;
      }
      if (entry[0] == "#R") {
          initial_rule = entry[1]
          continue;
      }
      if (entry[0] == "#initial_pattern=") {
          initial_pattern = entry[1]
          continue;
      }
    }
}

  
function  parse_url_specs() {
  var urlspec = location.search
  if (urlspec == "") {return ""}
  urlspec = urlspec.replace("?", "")
  var components = urlspec.split("&")
  for (var i = components.length; i--;) {
     if (components[i].match("tiling_name=")) {
       the_mesh_spec.tiling_name = components[i].replace("tiling_name=", "")
     }
     if (components[i].match("recursion_level=")) {
       the_mesh_spec.recursion_level = parseInt(components[i].replace("recursion_level=", ""))
     }
     if (components[i].match("rosette_p=")) {        
      if (components[i].replace("rosette_p=", "").match("true")) {the_mesh_spec.rosette_p = true} else {the_mesh_spec.rosette_p = false}  
     }
     if (components[i].match("initial_rule=")) {
        initial_rule = components[i].replace("initial_rule=", "")  // global variable instead of part of the mesh spec
     }
     if (components[i].match("initial_pattern=")) {
        initial_pattern = components[i].replace("initial_pattern=", "")  // global variable instead of part of the mesh spec
     }

     if (components[i].match("mods=")) {
       the_mesh_spec.mods = components[i].replace("mods=", "")
     }

     if (components[i].match("xmax=")) {
       the_mesh_spec.xmax = components[i].replace("xmax=", "")
     }

     if (components[i].match("ymax=")) {
       the_mesh_spec.ymax = components[i].replace("ymax=", "")
     }


   }
  return the_mesh_spec  // returning it for convenience but it is currently a global variable
}


 
function parse_OBJ_entries (OBJinput) {
     OBJinput = OBJinput.replace(/(\r\n|\n|\r)/gm,"-----newline-----");
     OBJinput = OBJinput.split("-----newline-----");
     var arr = OBJinput;
     var len = OBJinput.length
     var facearr;
     var coordarr;
     var shapetype = "shape"

     for (var i = 0; i < len; i++) {
       entry = arr[i].split(" ");
 
       if (entry[0] == "v") {
        vertexArray[v_index] =  new cellvertex(v_index, parseFloat(entry[1]), parseFloat(entry[2]), []);    
        v_index = v_index + 1;


       
       }
       else if (entry[0] == "f") {
           facearr = [];
           coordarr = [];
           var jlen = entry.length;
           if (jlen > 2) {
              for (var j = 1; j<jlen; j++) {
                 facearr[j - 1] = parseInt(entry[j]);
                 coordarr[(j-1)*2] = vertexArray[entry[j]].x
                 coordarr[(j-1)*2+1] = vertexArray[entry[j]].y
                 vertexArray[entry[j]].partof.push(aci);
              }
           
               // if (jlen - 1 == 4) {shapetype = "triangle"} // jlen - 1 == 4 for quads, jlen -1 == 6 for shields and other hexagons, etc.
              allcells[aci] = new cell(aci, coordarr, facearr, [], [],  0, 0, "message", shapetype);

              aci = aci + 1;
           }
       }
     }   
}

/*
This is just for reference - delete this section after load and save are written
  windowref.document.writeln("#R "+ birth1 + "_" + survive1 + "_C" + n_states)
   windowref.document.writeln(" ")
   windowref.document.writeln(" ")


  for (var i = 0; i<aci; i++) {
    if (allcells[i].state != 0) {
      windowref.document.writeln("#C " + i + " #S " + allcells[i].state)
     }
   }

*/








function increase_magnification() {
   clearboard();
   ctx.scale(scalefactor, scalefactor);
   ctxGrid.scale(scalefactor, scalefactor);
   if (rendertype == 4) {gl.scale(scalefactor, scalefactor)};  // need to use a flag instead, so that webglcanvas keeps up with everyone regardless of rendertype
   currentscale = currentscale*scalefactor;
   scaledshapesize = Math.max(Math.floor(shapesize*currentscale), 1);
   //   drawShapesFlag = scaledshapesize > shapesthreshhold
   //   drawEdgesFlag = scaledshapesize > edgesthreshold

 
   drawboard();
   drawgrid()
}

function decrease_magnification() {
   clearboard();
   ctx.scale(1/scalefactor, 1/scalefactor);
   ctxGrid.scale(1/scalefactor, 1/scalefactor);
   if (rendertype == 4) { gl.scale(1/scalefactor, 1/scalefactor)}

   currentscale = currentscale/scalefactor;
   scaledshapesize = Math.max(Math.floor(shapesize*currentscale), 1);
    //  drawShapesFlag = scaledshapesize > shapesthreshhold
    //  drawEdgesFlag = scaledshapesize > edgesthreshold

   drawboard();
   drawgrid()
 }


/*   candidate for deletion?
function center_mesh () {
 
   var xmin = vertexArray[1].x  // rename before making this variable global
   var xmax = vertexArray[1].x

   var ymin =  vertexArray[1].y
   var ymax =  vertexArray[1].y


   for (var i = 1; i < v_index; i++) {
       if (vertexArray[i].x < xmin) { xmin = vertexArray[i].x}
       if (vertexArray[i].y< ymin) { ymin = vertexArray[i].y}
       if (vertexArray[i].x > xmax) { xmax = vertexArray[i].x}
       if (vertexArray[i].y> ymax) { ymax = vertexArray[i].y}
   }

  
  // current_translate_x = -xmin;
  // current_translate_y = -ymin;
  


      // this is one approach:  ctx.translate(current_translate_x , current_translate_y ) 
     // but it makes zooming harder.
   // instead of translating, lets change the vertices 


   for (var i = 0; i < v_index; i++) {
       vertexArray[i].x += -xmin 
       vertexArray[i].y += -ymin
   }
   for (var i = 0; i < aci; i++) {
     var clen = allcells[i].coords.length
     for (var j = 0; j < clen; j = j + 2) {
        allcells[i].coords[j] += -xmin
        allcells[i].coords[j+1] += -ymin
     }
   }
  mesh_x_size = Math.abs(xmax - xmin)
  mesh_y_size = Math.abs(ymax - ymin)


}
*/

function center_and_scale_mesh () {
 
   var xmin = vertexArray[1].x  // rename before making this variable global
   var xmax = vertexArray[1].x

   var ymin =  vertexArray[1].y
   var ymax =  vertexArray[1].y


   for (var i = 1; i < v_index; i++) {
       if (vertexArray[i].x < xmin) { xmin = vertexArray[i].x}
       if (vertexArray[i].y< ymin) { ymin = vertexArray[i].y}
       if (vertexArray[i].x > xmax) { xmax = vertexArray[i].x}
       if (vertexArray[i].y> ymax) { ymax = vertexArray[i].y}
   }

  
  // current_translate_x = -xmin;
  // current_translate_y = -ymin;
  


      // this is one approach:  ctx.translate(current_translate_x , current_translate_y ) 
     // but it makes zooming harder.
   // instead of translating, lets change the vertices 

   


  mesh_x_size = Math.abs(xmax - xmin)
  mesh_y_size = Math.abs(ymax - ymin)

  initialscale =  980/mesh_y_size


   for (var i = 0; i < v_index; i++) {
       vertexArray[i].x =  (vertexArray[i].x - xmin) * initialscale
       vertexArray[i].y =  (vertexArray[i].y - ymin) * initialscale
   }
   for (var i = 0; i < aci; i++) {
     var clen = allcells[i].coords.length
     for (var j = 0; j < clen; j = j + 2) {
        allcells[i].coords[j] =   // roundNumber(   
                                      (allcells[i].coords[j] - xmin) * initialscale  
                                  // ,sigdigits)
        allcells[i].coords[j+1] = // roundNumber( 
                                      (allcells[i].coords[j+1] - ymin) * initialscale 
                                  // ,sigdigits)
     }
   }




   mesh_x_size =  mesh_x_size * initialscale
   mesh_y_size = mesh_y_size * initialscale
   pre_min_initialscale = initialscale
   initialscale = Math.min(1,initialscale )
   currentscale =  initialscale  // initiql and current are both necessary - they are not redundant
    // currentscale = Math.min(1,initialscale )
      ctx.scale(currentscale,currentscale); 
      ctxGrid.scale(currentscale,currentscale); 

 //  mesh_x_size =  mesh_x_size * initialscale
 //  mesh_y_size = mesh_y_size * initialscale

}



var initialscale;
var currentscale;
var shapesize; 

var initial_rule;  // see parse URL
var initial_pattern;

var scaledshapesize;
var rendertype = 1
//var drawShapesFlag = true;
//var drawEdgesFlag = true;
// var shapesthreshhold = 1
// var edgesthreshold = 2

function new_mesh_spec() {
   the_mesh_spec = {tiling_name: "none", mods: ""} 
}

new_mesh_spec() // currently this must be called outside and before initialize() is called, even the first time


function initialize() {
 // console.log (initialize_flag)
  if (initialize_flag == 0) {  
 
       makesite()


     // *********Lines*******
     // See https://developer.mozilla.org/en-US/docs/Web/Guide/HTML/Canvas_tutorial/Applying_styles_and_colors
 
       ctx.lineJoin='round';  //  ctx.lineJoin='bevel';
       ctx.lineCap='square';
       ctx.strokeStyle =  gridcolor // "#000000"; // "#FFFFFF"  //"#000000";
       ctxGrid.lineJoin='round';  //  ctx.lineJoin='bevel';
       ctxGrid.lineCap='square';
       ctxGrid.strokeStyle =  gridcolor  //  "#000000"; // "#FFFFFF"  //"#000000";
 

        parse_init_div()

        parse_url_specs()  // the URL specs can overwrite the init_div specs

        parse_mesh_specification()
        if (initial_rule != undefined)  {  add_rule(parse_rule_string (initial_rule))}
       
  


      if (document.getElementById("OBJdata") != null) {
            // Unless the user has provided a tilng name and other tling data, get the filename and set the_mesh_spec.tiling_name to the filename.
                   // first get window.location.pathname and then apply one of these two functions:
                      //function getName(s) {
                     //    var s1=s.split("?")[0]
                    //    return s1.replace(/^.*[\\\/]/, '');
                    //}


                  //function getName(s) {
                 //adapted from http://stackoverflow.com/questions/423376/how-to-get-the-file-name-from-a-full-path-using-javascript
                //    return s.split("?")[0].replace(/^.*[\\\/]/, '');
                //}

            parse_OBJ_entries (document.getElementById("OBJdata").innerHTML);    
            document.getElementById("MainBody").removeChild(document.getElementById("OBJdata"))  // not necessary

      }

  } // end of  initialize_flag section


       generation = 0
       changedcells = [];
       changecount = 0;

       newchangedcells = [];   
       NewChangeCount = 0;   

       scalefactor = 1.5; 


      initialize_neighbors()     // initialize neighbors comes before center-and_scale mesh, so that scaling doesn't introduce roundoff error


      if (initialize_flag == 0) {  
        center_and_scale_mesh();   // also sets initialscale
       }

      aabb_setup() // axis aligned bounding box  ( keywords for search: prerender pre_render pre-render pre render) 
                   // aabb is a late addition.  it might be useful for shapesize, mouse code, rect drawing of course, and more.
      //prerender_setup() 

     if  (rendertype == 4 && allcells[1].webglVertices == undefined) { 
        webgl_setup()  // this doesn't really need to be loaded again in many cases -- use a flag to check if it needs to be loaded.
        AddWeglVerticesToAllCells() 
     }



      if (aci-1 >= 1) {
         shapesize =  calc_avg_cell_size(1, Math.min(10, aci-1))
        } else {shapesize = calc_cell_size (allcells[0])}
      

     // currentscale =  initialscale  // initiql and current are both necessary - they are not redundant
      scaledshapesize =  Math.max(Math.floor(shapesize*currentscale), 1);
    //  drawShapesFlag = scaledshapesize > shapesthreshhold
    //  drawEdgesFlag = scaledshapesize > edgesthreshold


       var gridwidthfactor = 25 // 20 // 11
       ctx.lineWidth =  shapesize/gridwidthfactor
       ctxGrid.lineWidth =  shapesize/gridwidthfactor
     

   

       if (document.getElementById("Pattern_div") != null) {
                parse_pattern_entries(document.getElementById("Pattern_div").innerHTML)
                document.getElementById("MainBody").removeChild(document.getElementById("Pattern_div"))  // not necessary
       }
       put_patterns_in_menu()
   

      // either load intial_pattern or a random field
      if (initialize_flag == 0 && initial_pattern != undefined) {  
            var choice = initial_pattern // I'm using "choice" so that this code snippet matches the snippet for the pattern menu 
                                         // eventually merge snippets into single function 
            load_pattern(choice)
            if (lifepatterns[choice].rule != "all"  )  {set_rule_from_outside(lifepatterns[choice].rule)} 
            else {after_changing_rule()};
      }
      else {
         make_random_field("top") 
      }

 
      drawboard();
      drawgrid()

    // *************** Additional code to improve drawing with the mouse starts here *******
    // 
      flooredcellarr = [];

      for (var i=0; i<=the_dimension; i++) {
          flooredcellarr[i] = [];
       }
     
     flooredRange = Math.floor(shapesize*initialscale)*2  // old:  flooredRange = flooredshapesize*2 or *1 or *3

     for (var i = aci; i--;) {
        var xi = Math.floor(allcells[i].coords[0]*initialscale)
        var xlowest = Math.max(xi - flooredRange, 0)
        var xhighest =  Math.min(xi + flooredRange, the_dimension)
        for (var c = xlowest; c <= xhighest; c++) {
           flooredcellarr[c].push(i)
        }
     }
   // *************** end of extra mouse code *************************************************

 

  if (initialize_flag == 0) {            
        initialize_flag = 1
        for (var i = 0; i<the_mesh_spec.mods.length; i++) {
           if (the_mesh_spec.mods[i] == "s") {linearsubdivide()}
           if (the_mesh_spec.mods[i] == "d") {easy_dual_tiling()}
           if (the_mesh_spec.mods[i] == "t") {triangle_subdivide()}
           if (the_mesh_spec.mods[i] == "c") {triangle_subdivide_concave_only()}
           if (the_mesh_spec.mods[i] == "r") { Selfridge_subdivide()}
           if (the_mesh_spec.mods[i] == "i") {inset_subdivide()}
           if (the_mesh_spec.mods[i] == "w") { waffle_subdivide()}
           if (the_mesh_spec.mods[i] == "p") { pentagonalization()}
           if (the_mesh_spec.mods[i] == "v") {callVoronoi()}
          // if (the_mesh_spec.mods[i] == "u") {DelaunayTiling()}
        }
   }


   initialize_flag = 1



}




// Idea:  scale up or down the actual vertex values in centermesh, just in case scale() is causing trouble.


/*
http://diveintohtml5.info/canvas.html#paths
http://stackoverflow.com/questions/7017998/html-5-canvas-avoid-fill-behaviour-on-overlap
https://developer.mozilla.org/en-US/docs/Web/Guide/HTML/Canvas_tutorial/Applying_styles_and_colors

clear   save pattern   load pattern   brushcolor brush    timesteps    export mesh, export mesh+pattern, export pattern only


GGG:
x = 36, y = 9, rule = B3/S23
23b2o$23b2o$10bo15b2o6b2o$8bobo15b3o5b2o$2o4b2o18b2o$2o4b2o15b2o$6b2o
15b2o$8bobo$10bo!

*/





	/*
		PolyK library
		url: http://polyk.ivank.net
		Released under MIT licence.
		
		Copyright (c) 2012 - 2014 Ivan Kuckir

		Permission is hereby granted, free of charge, to any person
		obtaining a copy of this software and associated documentation
		files (the "Software"), to deal in the Software without
		restriction, including without limitation the rights to use,
		copy, modify, merge, publish, distribute, sublicense, and/or sell
		copies of the Software, and to permit persons to whom the
		Software is furnished to do so, subject to the following
		conditions:

		The above copyright notice and this permission notice shall be
		included in all copies or substantial portions of the Software.

		THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
		EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
		OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
		NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
		HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
		WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
		FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
		OTHER DEALINGS IN THE SOFTWARE.
		
		19. 5. 2014 - Problem with slicing fixed.
	*/

	var PolyK = {};
	
	/*
		Is Polygon self-intersecting?
		
		O(n^2)
	*/
	
	PolyK.IsSimple = function(p)
	{
		var n = p.length>>1;
		if(n<4) return true;
		var a1 = new PolyK._P(), a2 = new PolyK._P();
		var b1 = new PolyK._P(), b2 = new PolyK._P();
		var c = new PolyK._P();
		
		for(var i=0; i<n; i++)
		{
			a1.x = p[2*i  ];
			a1.y = p[2*i+1];
			if(i==n-1)	{ a2.x = p[0    ];  a2.y = p[1    ]; }
			else		{ a2.x = p[2*i+2];  a2.y = p[2*i+3]; }
			
			for(var j=0; j<n; j++)
			{
				if(Math.abs(i-j) < 2) continue;
				if(j==n-1 && i==0) continue;
				if(i==n-1 && j==0) continue;
				
				b1.x = p[2*j  ];
				b1.y = p[2*j+1];
				if(j==n-1)	{ b2.x = p[0    ];  b2.y = p[1    ]; }
				else		{ b2.x = p[2*j+2];  b2.y = p[2*j+3]; }
				
				if(PolyK._GetLineIntersection(a1,a2,b1,b2,c) != null) return false;
			}
		}
		return true;
	}
	
	PolyK.IsConvex = function(p)
	{
		if(p.length<6) return true;
		var l = p.length - 4;
		for(var i=0; i<l; i+=2)
			if(!PolyK._convex(p[i], p[i+1], p[i+2], p[i+3], p[i+4], p[i+5])) return false;
		if(!PolyK._convex(p[l  ], p[l+1], p[l+2], p[l+3], p[0], p[1])) return false;
		if(!PolyK._convex(p[l+2], p[l+3], p[0  ], p[1  ], p[2], p[3])) return false;
		return true;
	}
	
	PolyK.GetArea = function(p)
	{
		if(p.length <6) return 0;
		var l = p.length - 2;
		var sum = 0;
		for(var i=0; i<l; i+=2)
			sum += (p[i+2]-p[i]) * (p[i+1]+p[i+3]);
		sum += (p[0]-p[l]) * (p[l+1]+p[1]);
		return - sum * 0.5;
	}
	
	PolyK.GetAABB = function(p)
	{
		var minx = Infinity; 
		var miny = Infinity;
		var maxx = -minx;
		var maxy = -miny;
		for(var i=0; i<p.length; i+=2)
		{
			minx = Math.min(minx, p[i  ]);
			maxx = Math.max(maxx, p[i  ]);
			miny = Math.min(miny, p[i+1]);
			maxy = Math.max(maxy, p[i+1]);
		}
		return {x:minx, y:miny, width:maxx-minx, height:maxy-miny};
	}
	
	PolyK.Reverse = function(p)
	{
		var np = [];
		for(var j=p.length-2; j>=0; j-=2)  np.push(p[j], p[j+1])
		return np;
	}
	 
   
	PolyK.Triangulate = function(p)
	{
		var n = p.length>>1;
		if(n<3) return [];
		var tgs = [];
		var avl = [];
		for(var i=0; i<n; i++) avl.push(i);
		
		var i = 0;
		var al = n;
		while(al > 3)
		{
			var i0 = avl[(i+0)%al];
			var i1 = avl[(i+1)%al];
			var i2 = avl[(i+2)%al];
			
			var ax = p[2*i0],  ay = p[2*i0+1];
			var bx = p[2*i1],  by = p[2*i1+1];
			var cx = p[2*i2],  cy = p[2*i2+1];
			
			var earFound = false;
			if(PolyK._convex(ax, ay, bx, by, cx, cy))
			{
				earFound = true;
				for(var j=0; j<al; j++)
				{
					var vi = avl[j];
					if(vi==i0 || vi==i1 || vi==i2) continue;
					if(PolyK._PointInTriangle(p[2*vi], p[2*vi+1], ax, ay, bx, by, cx, cy)) {earFound = false; break;}
				}
			}
			if(earFound)
			{
				tgs.push(i0, i1, i2);
				avl.splice((i+1)%al, 1);
				al--;
				i= 0;
			}
			else if(i++ > 3*al) break;		// no convex angles :(
		}
		tgs.push(avl[0], avl[1], avl[2]);
		return tgs;
	}
	 
   /*
// Here is the version from PixiJS   "slightly modified by Mat Groves (matgroves.com);"
	PolyK.Triangulate = function(p)
	{
var sign = true;
 
var n = p.length >> 1;
if(n < 3) return [];
 
var tgs = [];
var avl = [];
for(var i = 0; i < n; i++) avl.push(i);
 
i = 0;
var al = n;
while(al > 3)
{
var i0 = avl[(i+0)%al];
var i1 = avl[(i+1)%al];
var i2 = avl[(i+2)%al];
 
var ax = p[2*i0], ay = p[2*i0+1];
var bx = p[2*i1], by = p[2*i1+1];
var cx = p[2*i2], cy = p[2*i2+1];
 
var earFound = false;
if(PolyK._convex(ax, ay, bx, by, cx, cy, sign))
{
earFound = true;
for(var j = 0; j < al; j++)
{
var vi = avl[j];
if(vi === i0 || vi === i1 || vi === i2) continue;
 
if(PolyK._PointInTriangle(p[2*vi], p[2*vi+1], ax, ay, bx, by, cx, cy)) {
earFound = false;
break;
}
}
}
 
if(earFound)
{
tgs.push(i0, i1, i2);
avl.splice((i+1)%al, 1);
al--;
i = 0;
}
else if(i++ > 3*al)
{
// need to flip flip reverse it!
// reset!
if(sign)
{
tgs = [];
avl = [];
for(i = 0; i < n; i++) avl.push(i);
 
i = 0;
al = n;
 
sign = false;
}
else
{
window.console.log(" Warning: shape too complex to fill");
return [];
}
}
}
 
tgs.push(avl[0], avl[1], avl[2]);
return tgs;
};

*/

	PolyK.ContainsPoint = function(p, px, py)
	{
		var n = p.length>>1;
		var ax, ay = p[2*n-3]-py, bx = p[2*n-2]-px, by = p[2*n-1]-py;
		
		//var lup = by > ay;
		for(var i=0; i<n; i++)
		{
			ax = bx;  ay = by;
			bx = p[2*i  ] - px;
			by = p[2*i+1] - py;
			if(ay==by) continue;
			lup = by>ay;
		}
		
		var depth = 0;
		for(var i=0; i<n; i++)
		{
			ax = bx;  ay = by;
			bx = p[2*i  ] - px;
			by = p[2*i+1] - py;
			if(ay< 0 && by< 0) continue;	// both "up" or both "down"
			if(ay> 0 && by> 0) continue;	// both "up" or both "down"
			if(ax< 0 && bx< 0) continue; 	// both points on the left
			
			if(ay==by && Math.min(ax,bx)<=0) return true;
			if(ay==by) continue;
			
			var lx = ax + (bx-ax)*(-ay)/(by-ay);
			if(lx==0) return true;			// point on edge
			if(lx> 0) depth++;
			if(ay==0 &&  lup && by>ay) depth--;	// hit vertex, both up
			if(ay==0 && !lup && by<ay) depth--; // hit vertex, both down
			lup = by>ay;
		}
		//console.log(depth);
		return (depth & 1) == 1;
	}
	
	PolyK.Slice = function(p, ax, ay, bx, by)
	{
		if(PolyK.ContainsPoint(p, ax, ay) || PolyK.ContainsPoint(p, bx, by)) return [p.slice(0)];

		var a = new PolyK._P(ax, ay);
		var b = new PolyK._P(bx, by);
		var iscs = [];	// intersections
		var ps = [];	// points
		for(var i=0; i<p.length; i+=2) ps.push(new PolyK._P(p[i], p[i+1]));
		
		for(var i=0; i<ps.length; i++)
		{
			var isc = new PolyK._P(0,0);
			isc = PolyK._GetLineIntersection(a, b, ps[i], ps[(i+1)%ps.length], isc);
			var fisc = iscs[0];
			var lisc = iscs[iscs.length-1];
			if(isc && (fisc==null || PolyK._P.dist(isc,fisc)>1e-10) && (lisc==null || PolyK._P.dist(isc,lisc)>1e-10 ) )//&& (isc.x!=ps[i].x || isc.y!=ps[i].y) )
			{
				isc.flag = true;
				iscs.push(isc);
				ps.splice(i+1,0,isc);
				i++;
			}
		}
		
		if(iscs.length <2) return [p.slice(0)];
		var comp = function(u,v) { return PolyK._P.dist(a,u) - PolyK._P.dist(a,v); }
		iscs.sort(comp);
		
		//console.log("Intersections: "+iscs.length, JSON.stringify(iscs));
		
		var pgs = [];
		var dir = 0;
		while(iscs.length > 0)
		{
			var n = ps.length;
			var i0 = iscs[0];
			var i1 = iscs[1];
			//if(i0.x==i1.x && i0.y==i1.y) { iscs.splice(0,2); continue;}
			var ind0 = ps.indexOf(i0);
			var ind1 = ps.indexOf(i1);
			var solved = false;
			
			//console.log(i0, i1);
			
			if(PolyK._firstWithFlag(ps, ind0) == ind1) solved = true;
			else
			{
				i0 = iscs[1];
				i1 = iscs[0];
				ind0 = ps.indexOf(i0);
				ind1 = ps.indexOf(i1);
				if(PolyK._firstWithFlag(ps, ind0) == ind1) solved = true;
			}
			if(solved)
			{
				dir--;
				var pgn = PolyK._getPoints(ps, ind0, ind1);
				pgs.push(pgn);
				ps = PolyK._getPoints(ps, ind1, ind0);
				i0.flag = i1.flag = false;
				iscs.splice(0,2);
				if(iscs.length == 0) pgs.push(ps);
			}
			else { dir++; iscs.reverse(); }
			if(dir>1) break;
		}
		var result = [];
		for(var i=0; i<pgs.length; i++)
		{
			var pg = pgs[i];
			var npg = [];
			for(var j=0; j<pg.length; j++) npg.push(pg[j].x, pg[j].y);
			result.push(npg);
		}
		return result;
	}
	
	PolyK.Raycast = function(p, x, y, dx, dy, isc)
	{
		var l = p.length - 2;
		var tp = PolyK._tp;
		var a1 = tp[0], a2 = tp[1], 
		b1 = tp[2], b2 = tp[3], c = tp[4];
		a1.x = x; a1.y = y;
		a2.x = x+dx; a2.y = y+dy;
		
		if(isc==null) isc = {dist:0, edge:0, norm:{x:0, y:0}, refl:{x:0, y:0}};
		isc.dist = Infinity;
		
		for(var i=0; i<l; i+=2)
		{
			b1.x = p[i  ];  b1.y = p[i+1];
			b2.x = p[i+2];  b2.y = p[i+3];
			var nisc = PolyK._RayLineIntersection(a1, a2, b1, b2, c);
			if(nisc) PolyK._updateISC(dx, dy, a1, b1, b2, c, i/2, isc);
		}
		b1.x = b2.x;  b1.y = b2.y;
		b2.x = p[0];  b2.y = p[1];
		var nisc = PolyK._RayLineIntersection(a1, a2, b1, b2, c);
		if(nisc) PolyK._updateISC(dx, dy, a1, b1, b2, c, p.length/2, isc);
		
		return (isc.dist != Infinity) ? isc : null;
	}
	
	PolyK.ClosestEdge = function(p, x, y, isc)
	{
		var l = p.length - 2;
		var tp = PolyK._tp;
		var a1 = tp[0], 
		b1 = tp[2], b2 = tp[3], c = tp[4];
		a1.x = x; a1.y = y;
		
		if(isc==null) isc = {dist:0, edge:0, point:{x:0, y:0}, norm:{x:0, y:0}};
		isc.dist = Infinity;
		
		for(var i=0; i<l; i+=2)
		{
			b1.x = p[i  ];  b1.y = p[i+1];
			b2.x = p[i+2];  b2.y = p[i+3];
			PolyK._pointLineDist(a1, b1, b2, i>>1, isc);
		}
		b1.x = b2.x;  b1.y = b2.y;
		b2.x = p[0];  b2.y = p[1];
		PolyK._pointLineDist(a1, b1, b2, l>>1, isc);
		
		var idst = 1/isc.dist;
		isc.norm.x = (x-isc.point.x)*idst;
		isc.norm.y = (y-isc.point.y)*idst;
		return isc;
	}
	
	PolyK._pointLineDist = function(p, a, b, edge, isc)
	{
		var x = p.x, y = p.y, x1 = a.x, y1 = a.y, x2 = b.x, y2 = b.y;
		
		var A = x - x1;
		var B = y - y1;
		var C = x2 - x1;
		var D = y2 - y1;

		var dot = A * C + B * D;
		var len_sq = C * C + D * D;
		var param = dot / len_sq;

		var xx, yy;

		if (param < 0 || (x1 == x2 && y1 == y2)) {
			xx = x1;
			yy = y1;
		}
		else if (param > 1) {
			xx = x2;
			yy = y2;
		}
		else {
			xx = x1 + param * C;
			yy = y1 + param * D;
		}

		var dx = x - xx;
		var dy = y - yy;
		var dst = Math.sqrt(dx * dx + dy * dy);
		if(dst<isc.dist)
		{
			isc.dist = dst;
			isc.edge = edge;
			isc.point.x = xx;
			isc.point.y = yy;
		}
	}
	
	PolyK._updateISC = function(dx, dy, a1, b1, b2, c, edge, isc)
	{
		var nrl = PolyK._P.dist(a1, c);
		if(nrl<isc.dist)
		{
			var ibl = 1/PolyK._P.dist(b1, b2);
			var nx = -(b2.y-b1.y)*ibl;
			var ny =  (b2.x-b1.x)*ibl;
			var ddot = 2*(dx*nx+dy*ny);
			isc.dist = nrl;
			isc.norm.x = nx;  
			isc.norm.y = ny; 
			isc.refl.x = -ddot*nx+dx;
			isc.refl.y = -ddot*ny+dy;
			isc.edge = edge;
		}
	}
	
	PolyK._getPoints = function(ps, ind0, ind1)
	{
		var n = ps.length;
		var nps = [];
		if(ind1<ind0) ind1 += n;
		for(var i=ind0; i<= ind1; i++) nps.push(ps[i%n]);
		return nps;
	}
	
	PolyK._firstWithFlag = function(ps, ind)
	{
		var n = ps.length;
		while(true)
		{
			ind = (ind+1)%n;
			if(ps[ind].flag) return ind;
		}
	}
	
	PolyK._PointInTriangle = function(px, py, ax, ay, bx, by, cx, cy)
	{
		var v0x = cx-ax;
		var v0y = cy-ay;
		var v1x = bx-ax;
		var v1y = by-ay;
		var v2x = px-ax;
		var v2y = py-ay;
		
		var dot00 = v0x*v0x+v0y*v0y;
		var dot01 = v0x*v1x+v0y*v1y;
		var dot02 = v0x*v2x+v0y*v2y;
		var dot11 = v1x*v1x+v1y*v1y;
		var dot12 = v1x*v2x+v1y*v2y;
		
		var invDenom = 1 / (dot00 * dot11 - dot01 * dot01);
		var u = (dot11 * dot02 - dot01 * dot12) * invDenom;
		var v = (dot00 * dot12 - dot01 * dot02) * invDenom;

		// Check if point is in triangle
		return (u >= 0) && (v >= 0) && (u + v < 1);
	}
	
	PolyK._RayLineIntersection = function(a1, a2, b1, b2, c)
	{
		var dax = (a1.x-a2.x), dbx = (b1.x-b2.x);
		var day = (a1.y-a2.y), dby = (b1.y-b2.y);

		var Den = dax*dby - day*dbx;
		if (Den == 0) return null;	// parallel
		
		var A = (a1.x * a2.y - a1.y * a2.x);
		var B = (b1.x * b2.y - b1.y * b2.x);
		
		var I = c;
		var iDen = 1/Den;
		I.x = ( A*dbx - dax*B ) * iDen;
		I.y = ( A*dby - day*B ) * iDen;
		
		if(!PolyK._InRect(I, b1, b2)) return null;
		if((day>0 && I.y>a1.y) || (day<0 && I.y<a1.y)) return null; 
		if((dax>0 && I.x>a1.x) || (dax<0 && I.x<a1.x)) return null; 
		return I;
	}
	
	PolyK._GetLineIntersection = function(a1, a2, b1, b2, c)
	{
		var dax = (a1.x-a2.x), dbx = (b1.x-b2.x);
		var day = (a1.y-a2.y), dby = (b1.y-b2.y);

		var Den = dax*dby - day*dbx;
		if (Den == 0) return null;	// parallel
		
		var A = (a1.x * a2.y - a1.y * a2.x);
		var B = (b1.x * b2.y - b1.y * b2.x);
		
		var I = c;
		I.x = ( A*dbx - dax*B ) / Den;
		I.y = ( A*dby - day*B ) / Den;
		
		if(PolyK._InRect(I, a1, a2) && PolyK._InRect(I, b1, b2)) return I;
		return null;
	}
	
	PolyK._InRect = function(a, b, c)	// a in rect (b,c)
	{
		var minx = Math.min(b.x,c.x), maxx = Math.max(b.x,c.x);
		var miny = Math.min(b.y,c.y), maxy = Math.max(b.y,c.y);
		
		if	(minx == maxx) return (miny<=a.y && a.y<=maxy);
		if	(miny == maxy) return (minx<=a.x && a.x<=maxx);
		
		return (minx <= a.x && a.x <= maxx && miny <= a.y && a.y <= maxy) 
	}
	
	PolyK._convex = function(ax, ay, bx, by, cx, cy)
	{
		return (ay-by)*(cx-bx) + (bx-ax)*(cy-by) >= 0;
	}
		
	PolyK._P = function(x,y)
	{
		this.x = x;
		this.y = y;
		this.flag = false;
	}
	PolyK._P.prototype.toString = function()
	{
		return "Point ["+this.x+", "+this.y+"]";
	}
	PolyK._P.dist = function(a,b)
	{
		var dx = b.x-a.x;
		var dy = b.y-a.y;
		return Math.sqrt(dx*dx + dy*dy);
	}
	
	PolyK._tp = [];
	for(var i=0; i<10; i++) PolyK._tp.push(new PolyK._P(0,0));










//  Tiling Generator

debug3 = 0

/*  currently unused, but maybe it or something like it is needed 
function axis_brush_against(crds, ax, ay, bx, by) {
  var haiq_flag = false  
  if (ax == bx) {
      var haiq = ax  // horizontal_axis_in_question ("haiq")
      for (var i = 0;  i<crds.length-2; i=i+2) {
      if (crds[i] == haiq && crds[i+2] == haiq) {haiq_flag = true}
      }
      if (crds[crds.length-2] == haiq && crds[0] == haiq) {haiq_flag = true}
  }
  var vaiq_flag = false
  if (ay == by) {
    var vaiq = ay // vertical_axis_in_question ("vaiq")
    for (var i = 1;  i<crds.length-1; i=i+2) {
     if (crds[i] == vaiq && crds[i+2] == vaiq) {vaiq_flag = true}
    }
    if (crds[crds.length-1] == vaiq && crds[1] == vaiq) {vaiq_flag = true}
  }
  return haiq_flag || vaiq_flag
}
*/


/* bisect_cells_with_line(ax, ay, bx, by)
  
    The following version worked really well, but I wondered if it could be made faster.
   It trashes all of allcells and vertexArray  and rebuilds the mesh, which might be what makes it slow.
   (But arguably, this isn't what makes it slow.  Is it significantly slower to call build_verts_and_faces for all the cells 
    versus only the cells which are sliced by the line? Or is the bottlneck elsewehre?)
   If there are problems with faster_bisect_cells_with_line, try going back to using this version 
*/


function bisect_cells_with_line(ax, ay, bx, by) {

 /*************************/
 //  var enough = .03 //.02
/**************************/

   old_allcells = allcells
   old_aci = aci
   
    x_to_verts = []; flr_x_to_verts = []
     vertexArray = [] 
     v_index = 1;
     vertexArray[0] = settypecellvertex  // This shouldn't be used, but  
     allcells = [] 
     aci = 0;  


   for (var i = old_aci; i--;) {

       try { 
         newcells = PolyK.Slice(old_allcells[i].coords, ax, ay, bx, by) 
        }     
       catch(err) {
            newcells = [old_allcells[i].coords]
            debug3 = debug3 + 1
       }
           
       for (var j = 0; j<newcells.length; j++) {
             var coords_pairs = []
             for (var z=0; z<newcells[j].length; z+=2) { // this loop puts the coords into pairs, checks for too close, and checks for duplicate points
 /*************************/
  //              var candidate = v_snap_to_closest_if_close_enough (newcells[j][z],newcells[j][z+1], newcells, enough) 
 /*************************/

                var candidate = [newcells[j][z], newcells[j][z+1]]  // no enough needed


               if (detect_notflat_coord_duplicates(candidate, coords_pairs) == false) {
                coords_pairs.push(candidate) 
               }  
             } 
            // need to remove duplicate pairs eg [[500, 500], [500,500], [500, 500]]  is not a cell, so reduce it to [[500,500]] and then eliminate it on the basis of length
             if (coords_pairs.length > 2) {
                build_verts_and_faces(coords_pairs, "shape")
             }
       }
   }
     // initialize must be called before dual tiling is called, but not before that -- not after each line.
     /*
         console.log("---------")
         console.log(JSON.stringify(allcells))
         console.log(allcells.length)
         console.log("---------")
     */

     old_allcells = []  // not necessarily enough for garbage collection
}


/*   plain version of the above
function bisect_cells_with_line(ax, ay, bx, by) {
    old_allcells = allcells
    old_aci = aci
    x_to_verts = []; flr_x_to_verts = []
    vertexArray = [] 
    v_index = 1;
    vertexArray[0] = settypecellvertex  // This shouldn't be used, but  
    allcells = [] 
    aci = 0;  
      for (var i = old_aci; i--;) {
       try { 
         newcells = PolyK.Slice(old_allcells[i].coords, ax, ay, bx, by) 
        }     
       catch(err) {
            newcells = [old_allcells[i].coords]
            debug3 = debug3 + 1
       }
       for (var j = 0; j<newcells.length; j++) {
             var coords_pairs = []
             for (var z=0; z<newcells[j].length; z+=2) { // this loop puts the coords into pairs, checks for too close, and checks for duplicate points

                var candidate = [newcells[j][z], newcells[j][z+1]]  // no enough needed

               if (detect_notflat_coord_duplicates(candidate, coords_pairs) == false) {
                coords_pairs.push(candidate) 
               }  
             } 
             if (coords_pairs.length > 2) {
                build_verts_and_faces(coords_pairs, "shape")
             }
       }
   }
   old_allcells = []  // not necessarily enough for garbage collection
}
*/


// this one works and is 50% faster, but still recreates a new array
function bisectArr_bisect_cells_with_line(ax, ay, bx, by) {
  new_bisectArr = []
  for (var i = 0 ; i<bisectArr.length; i++) {
         var coords_pairs = bisectArr[i]
         var coords_flat = []
         for (var z = 0; z<coords_pairs.length; z++) {
            coords_flat.push(coords_pairs[z][0])
            coords_flat.push(coords_pairs[z][1])
         }
         
       try { newcells = PolyK.Slice(coords_flat, ax, ay, bx, by) }  // flat vs pairs!  
       catch(err) {
              newcells = [bisectArr[i]]
              debug3 = debug3 + 1
       }
   
         for (var j = 0; j<newcells.length; j++) {
             var coords_pairs = []
             for (var z=0; z<newcells[j].length; z+=2) { // this loop puts the coords into pairs, and checks for duplicate points
               var candidate = [roundNumber(newcells[j][z], sigdigits), roundNumber(newcells[j][z+1], sigdigits)]
               if (detect_notflat_coord_duplicates(candidate, coords_pairs) == false) {
                   coords_pairs.push(candidate) 
               }  
             } 
             if (coords_pairs.length > 2) {
                  new_bisectArr.push(coords_pairs)
             }
       }
   }
         bisectArr = new_bisectArr
                
}


/* this version reuses bisectArr instead of making a new one

function bisectArr_bisect_cells_with_line(ax, ay, bx, by) {
  for (var i = bisectArr.length; i--;) {
         var coords_pairs = bisectArr[i]
         if (coords_pairs.length < 3) {continue}  // jump over placeholders (see below)
         var coords_flat = []
         for (var z = 0; z<coords_pairs.length; z++) {
            coords_flat.push(coords_pairs[z][0])
            coords_flat.push(coords_pairs[z][1])
         }
       try { newcells = PolyK.Slice(coords_flat, ax, ay, bx, by) }  // flat vs pairs!  
       catch(err) {
              newcells = [bisectArr[i]]
              debug3 = debug3 + 1
       }
             if (newcells.length < 2) {continue}
             // otherwise, replace bisectArr[i] with newcells[0] after rounding, and removing duplicates
             var coords_pairs = []
             for (var z=0; z<newcells[0].length; z+=2) { // this loop puts the coords into pairs, and checks for duplicate points
               var candidate = [roundNumber(newcells[0][z], sigdigits), roundNumber(newcells[0][z+1], sigdigits)]
               if (detect_notflat_coord_duplicates(candidate, coords_pairs) == false) {
                   coords_pairs.push(candidate) 
               }  
             } 
           //  if (coords_pairs.length > 2) {
                bisectArr[i] =   coords_pairs  // even if length < 2 it will be a placeholder
           // }

          // now handle all the other newcells by pushing them onto the end of bisectArr if they are polygons
   
         for (var j = 1; j<newcells.length; j++) {
             var coords_pairs = []
             for (var z=0; z<newcells[j].length; z+=2) { // this loop puts the coords into pairs, and checks for duplicate points
               var candidate = [roundNumber(newcells[j][z], sigdigits), roundNumber(newcells[j][z+1], sigdigits)]
               if (detect_notflat_coord_duplicates(candidate, coords_pairs) == false) {
                   coords_pairs.push(candidate) 
               }  
             } 
             if (coords_pairs.length > 2) {
                  bisectArr.push(coords_pairs)
             }
       }
   }
         
                
}

*/


function  bisectArr_convert_to_cells() {
    x_to_verts = []; flr_x_to_verts = []
     vertexArray = [] 
     v_index = 1;
     vertexArray[0] = settypecellvertex  // This shouldn't be used, but  
     allcells = [] 
     aci = 0;  
   for (var i =  bisectArr.length; i--;) {
     if (bisectArr[i].length > 2) {   
       build_verts_and_faces(bisectArr[i], "shape")
     }
   }
}

/*
function faster_bisect_cells_with_line(ax, ay, bx, by) {
   for (var i = aci; i--;) {

       try { newcells = PolyK.Slice(allcells[i].coords, ax, ay, bx, by) }  // flat vs pairs!  
       catch(err) {
            // newcells = [old_allcells[i].coords]
            // debug3 = debug3 + 1
            continue
       }
       if (newcells.length < 2) {continue}  
      // if there are two or more...
      // make first of newcells take the place of allcells[i]
       var newoldcell = newcells[0];
       var coords_pairs = []
       for (var z=0; z<newoldcell.length; z+=2) { // this loop puts the coords into pairs, and checks for duplicate points
               var candidate = [newoldcell[z], newoldcell[z+1]]
               if (detect_notflat_coord_duplicates(candidate, coords_pairs) == false) {
                coords_pairs.push(candidate) 
               }  
        }
     if (coords_pairs.length > 2) {
              //  build_verts_and_faces(coords_pairs, "shape")
         var coords_flat = []
         for (var z = coords_pairs.length; z--;) {
            coords_flat.push(coords_pairs[z][1])
            coords_flat.push(coords_pairs[z][0])
         }

         var new_fnn = []
         for (var z = coords_pairs.length; z--;) {
            var nf = get_v_index_from_point_faster(coords_pairs[z][0], coords_pairs[z][1])
            if (nf == false) {debug3 = "false"}
            new_fnn.push(nf)
         } 
         
         for (var z = allcells[i].fnn.length; z--;) {
         //  if a vertice in allcells[i] is not contained in allcells[aci-1]
         //   remove i from the partof field of that vertice
           var vvi = allcells[i].fnn[z]
           if (contains (vvi, new_fnn) == false) {
               var index =  vertexArray[vvi].partof.indexOf(i);
               if (index > -1) {
                   vertexArray[vvi].partof.splice(index,1)
                }
            }
         }
         allcells[i].coords = coords_flat
         allcells[i].fnn = new_fnn
         allcells[i].type = "shape"
        
        for (var z = allcells[i].fnn.length; z--;) {
           if (contains(i, vertexArray[allcells[i].fnn[z]].partof) == false) {
              vertexArray[allcells[i].fnn[z]].partof.push(i)
           }
        }
       
      }


       // now handle the rest of the new cells as truly new cells
       for (var j = 1; j<newcells.length; j++) {
             var coords_pairs = []
             for (var z=0; z<newcells[j].length; z+=2) { // this loop puts the coords into pairs, and checks for duplicate points
               var candidate = [newcells[j][z], newcells[j][z+1]]
               if (detect_notflat_coord_duplicates(candidate, coords_pairs) == false) {
                coords_pairs.push(candidate) 
               }  
             } 
            // need to remove duplicate pairs eg [[500, 500], [500,500], [500, 500]]  is not a cell, so reduce it to [[500,500]] and then eliminate it on the basis of length
             if (coords_pairs.length > 2) {
                build_verts_and_faces(coords_pairs, "shape")
             }
       }
   }
     // initialize must be called before dual tiling is called, but not before that -- not after each line.
                   
}
*/



function make_paralell_lines_at_angle(shift, center_x, center_y,number_of_lines, width, height, angle, n_fold_symmetry) {
   var ystart =  -height  //-1600 //center_y - height/2 - 100
   var yfinish = height    //1600 //center_y + height/2 + 100
   var t = new Transform();
   t.translate(center_x, center_y);  
   t.rotate(radians(angle));
   var lines = [] 
   var scale = width/number_of_lines
   if (shift == "shift1") { shift =  1/n_fold_symmetry * scale} 
   else if (shift == "shift2") {shift = (n_fold_symmetry - 1)/(n_fold_symmetry *2) * scale}
   else if (isNaN(shift) == true) {shift = 0.1 * scale}   
 //  var shift =  (n_fold_symmetry - 1)/(n_fold_symmetry *2) * scale // 0.1*scale // 0.05*scale // 0.1*scale // (n_fold_symmetry - 1)/(n_fold_symmetry *2) * scale /2 // 0 // .1 * scale // (n_fold_symmetry - 1)/(n_fold_symmetry *2) * scale //  1/n_fold_symmetry * scale  //   //.1 * scale  // .1 * scale  //0 // Math.random()/3 //  Math.sin(radians(angle))+0.5 // Math.random()

   for (xcurrent = (-number_of_lines/2)*scale; xcurrent < (number_of_lines/2)*scale; xcurrent=xcurrent+1*scale) {
        
    /*
      var start = t.transformPoint(xcurrent+0.25*shift,  ystart)
      var finish = t.transformPoint(xcurrent+0.25*shift, yfinish)
      lines.push( start.concat(finish)  )


      var start = t.transformPoint(xcurrent+0.5*shift,  ystart)
      var finish = t.transformPoint(xcurrent+0.5*shift, yfinish)
      lines.push( start.concat(finish)  )
 

      var start = t.transformPoint(xcurrent+0.75*shift,  ystart)
      var finish = t.transformPoint(xcurrent+0.75*shift, yfinish)
      lines.push( start.concat(finish)  )
  
  
      var start = t.transformPoint(xcurrent+1.25*shift,  ystart)
      var finish = t.transformPoint(xcurrent+1.25*shift, yfinish)
      lines.push( start.concat(finish)  )
  
      var start = t.transformPoint(xcurrent+1.5*shift,  ystart)
      var finish = t.transformPoint(xcurrent+1.5*shift, yfinish)
      lines.push( start.concat(finish)  )

      var start = t.transformPoint(xcurrent+1.75*shift,  ystart)
      var finish = t.transformPoint(xcurrent+1.75*shift, yfinish)
      lines.push( start.concat(finish)  )
   
      var start = t.transformPoint(xcurrent+2.0*shift,  ystart)
      var finish = t.transformPoint(xcurrent+2.0*shift, yfinish)
      lines.push( start.concat(finish)  )
   */
   /*
      var start = t.transformPoint(xcurrent-shift,  ystart)
      var finish = t.transformPoint(xcurrent-shift, yfinish)
      lines.push( start.concat(finish)  )  
   */


      // original
      var start = t.transformPoint(xcurrent+shift,  ystart)
      var finish = t.transformPoint(xcurrent+shift, yfinish)
      lines.push( start.concat(finish)  )  
    }
    return lines
}    


// unfortunately, this appears to be an error with slice:  
// PolyK.Slice([1000, 211.32, 500, 500, 0, 211.32, 0, 0, 633.97, 0], 500,  -1100 ,  500  ,  2100)
//  also
// PolyK.Slice([1000, 211.32, 500, 498, 0, 211.32, 0, 0, 633.97, 0], 500,  -1100 ,  500  ,  2100)
 //  it can be patched, as per the discussion section on IvanK's website, like this
 // while(iscs.length > 1)	// original: while(iscs.length > 0)
 // But that fails to handle the case where iscs.length == 1.   Maybe treat it the same as when it == 0?   




function my_dual_method_tiling_generator_ver001(n_fold_symmetry, number_of_lines, shift)  {     
    x_to_verts = []; flr_x_to_verts = []
     vertexArray = [] 
     v_index = 1;
     vertexArray[0] = settypecellvertex  // This shouldn't be used, but  
     allcells = [] 
     aci = 0;  

   var box_width =  1000 // 1000 // 5000   // 1000
   var box_height = 1000 //1000 //  5000  // 1000
   var box_x_pos =  0
   var box_y_pos =  0
     build_verts_and_faces([[box_x_pos, box_y_pos],[box_width+box_x_pos, box_y_pos],[box_width+box_x_pos, box_height+box_y_pos],[box_x_pos,box_height+box_y_pos]], "shape")

    // bisectArr = [[[box_x_pos, box_y_pos],[box_width+box_x_pos, box_y_pos],[box_width+box_x_pos, box_height+box_y_pos],[box_x_pos,box_height+box_y_pos]]]



   var lines =  [] 
   var skew = 2 // if this value is zero, it can be eliminated below
   var angle_increment = 360/n_fold_symmetry
   // if n_fold_symmetry is even, we handle things differently.
 /*
   if (n_fold_symmetry % 2 == 0) {
           // n_fold_symmetry = n_fold_symmetry/2;  // original line
           // angle_increment = 180/n_fold_symmetry  // original line
             angle_increment = 180/n_fold_symmetry
   }
  */
   
   for (var i = 0; i<n_fold_symmetry; i++) {
      lines =  lines.concat(make_paralell_lines_at_angle(shift,(box_x_pos+box_width)/2, (box_y_pos+box_height)/2, number_of_lines, box_width, box_height,i*angle_increment+skew, n_fold_symmetry))
   }
    
   for (var i = lines.length; i--;) {
     var l = lines[i]
        bisect_cells_with_line(l[0], l[1], l[2], l[3])
      //  bisectArr_bisect_cells_with_line(l[0], l[1], l[2], l[3])
   }
      // bisectArr_convert_to_cells()
}

function my_dual_method_tiling_generator2(n_fold_symmetry, number_of_lines, shift)  {     
    x_to_verts = []; flr_x_to_verts = []
     vertexArray = [] 
     v_index = 1;
     vertexArray[0] = settypecellvertex  // This shouldn't be used, but  
     allcells = [] 
     aci = 0;  

   var box_width =  1000 // 1000 // 5000   // 1000
   var box_height = 1000 //1000 //  5000  // 1000
   var box_x_pos =  0
   var box_y_pos =  0
    // build_verts_and_faces([[box_x_pos, box_y_pos],[box_width+box_x_pos, box_y_pos],[box_width+box_x_pos, box_height+box_y_pos],[box_x_pos,box_height+box_y_pos]], "shape")

     bisectArr = [[[box_x_pos, box_y_pos],[box_width+box_x_pos, box_y_pos],[box_width+box_x_pos, box_height+box_y_pos],[box_x_pos,box_height+box_y_pos]]]



   var lines =  [] 
   var skew = 2 // if this value is zero, it can be eliminated below
   var angle_increment = 360/n_fold_symmetry

   if (n_fold_symmetry % 2 == 0) {
           //  n_fold_symmetry = n_fold_symmetry/2;  // original line  // this line appears to allow the creation of Ammann-Beenker when n_fold_symmetry initially == 8
           //  angle_increment = 180/n_fold_symmetry  // original line
   }
 

   
   for (var i = 0; i<n_fold_symmetry; i++) {
      lines =  lines.concat(make_paralell_lines_at_angle(shift,(box_x_pos+box_width)/2, (box_y_pos+box_height)/2, number_of_lines, box_width, box_height,i*angle_increment+skew, n_fold_symmetry))
   }
    
   for (var i = lines.length; i--;) {
     var l = lines[i]
      //   bisect_cells_with_line(l[0], l[1], l[2], l[3])
        bisectArr_bisect_cells_with_line(l[0], l[1], l[2], l[3])
   }
       bisectArr_convert_to_cells()
}



function my_dual_method_tiling_generator3(n_fold_symmetry, number_of_lines, shift)     {
console.log("here")
    x_to_verts = []; flr_x_to_verts = []
     vertexArray = [] 
     v_index = 1;
     vertexArray[0] = settypecellvertex  // This shouldn't be used, but  
     allcells = [] 
     aci = 0;  

//  var n_fold_symmetry= 3 // parseInt(prompt("Enter a number for n_fold symmetry","5"));
  

   var box_width =  1000   
   var box_height = 1000  
   var box_x_pos =  0
   var box_y_pos =  0
   build_verts_and_faces([[box_x_pos, box_y_pos],[box_width+box_x_pos, box_y_pos],[box_width+box_x_pos, box_height+box_y_pos],[box_x_pos,box_height+box_y_pos]], "shape")


   var lines =  [] 
   var skew = 2 // if this value is zero, it can be eliminated below
   var angle_increment = 360/n_fold_symmetry
   // if n_fold_symmetry is even, we handle things differently.
  // if (n_fold_symmetry % 2 == 0) {n_fold_symmetry = n_fold_symmetry/2;  angle_increment = 180/n_fold_symmetry}
   
   for (var i = 0; i<n_fold_symmetry; i++) {
      lines =  lines.concat(make_paralell_lines_at_angle(shift,(box_x_pos+box_width)/2, (box_y_pos+box_height)/2, number_of_lines, box_width, box_height,i*angle_increment+skew, n_fold_symmetry))
   }
    



  // n_fold_symmetry = 
   var skew = 2+30 // 30 very good, 45 very good, 10 and 33 ok, 
   var angle_increment = 360/n_fold_symmetry
  // if (n_fold_symmetry % 2 == 0) {n_fold_symmetry = n_fold_symmetry/2;  angle_increment = 180/n_fold_symmetry}
   
   for (var i = 0; i<n_fold_symmetry; i++) {
      lines =  lines.concat(make_paralell_lines_at_angle(shift,(box_x_pos+box_width)/2, (box_y_pos+box_height)/2, number_of_lines, box_width, box_height,i*angle_increment+skew, n_fold_symmetry))
   }
    
    

   for (var i = lines.length; i--;) {
      var l = lines[i]
        bisect_cells_with_line(l[0], l[1], l[2], l[3])

      //  build_verts_and_faces([[l[0], l[1]],[ l[2], l[3]]], "s")  // surprisingly, this works, if you want to see where the lines are.
   }
}
 

//  *************** Converting Multigrids into Rhombs and Regular Polygons

/*  Candidate for deletion -- see turt1 below
function turt(xpos, ypos, turn_deg, forward_len) {
      var t = new Transform();
      t.translate(xpos, ypos);
      t.rotate(radians(turn_deg));
      return t.transformPoint(forward_len, 0)
}
*/
 

function turt1(xpos, ypos, turn_deg, forward_len) {
 return [forward_len*Math.cos(radians(turn_deg))+xpos, forward_len*Math.sin(radians(turn_deg))+ypos]
}
  

function vertex_angle(originX, originY, px, py) {
   return Math.atan2(py-originY, px-originX) *180/Math.PI
}
          


function neighbor_vertices(vert, cellarray) {
   var vertindex = vert.index
   var cellstocheck = vert.partof
   var neighbors = []  // the index of each neighboring vertex will go here -- access with vertexArray
   for (var i = cellstocheck.length; i--;) {
       var vertstocheck = cellarray[cellstocheck[i]].fnn
       var len = vertstocheck.length
       for (var j = 1; j<len-1; j++) {
         if (vertstocheck[j] == vertindex) { 
             neighbors.push(vertstocheck[j+1]); 
             neighbors.push(vertstocheck[j-1]); 
         }
       }
       if (vertstocheck[0] == vertindex) { 
             neighbors.push(vertstocheck[1]); 
             neighbors.push(vertstocheck[len-1]); 
       }
       if (vertstocheck[len-1] == vertindex) { 
             neighbors.push(vertstocheck[len-2]); 
             neighbors.push(vertstocheck[0]); 
       }
   }
   // now remove duplicates
   // using this trick:   http://stackoverflow.com/questions/9229645/remove-duplicates-from-javascript-array
   neighbors = neighbors.filter(function(elem, pos) {
    return neighbors.indexOf(elem) == pos;
   })
   return neighbors
}

//**********************  A super useful function ****************
 
function border_neighbor_vertices(vert, cellarray) {
   var vertindex = vert.index
   var cellstocheck = vert.partof
   var neighbors = []  // the index of each neighboring vertex will go here -- access with vertexArray
   for (var i = cellstocheck.length; i--;) {
       var vertstocheck = cellarray[cellstocheck[i]].fnn
       var len = vertstocheck.length
       for (var j = 1; j<len-1; j++) {
         if (vertstocheck[j] == vertindex) { 
             neighbors.push(vertstocheck[j+1]); 
             neighbors.push(vertstocheck[j-1]); 
         }
       }
       if (vertstocheck[0] == vertindex) { 
             neighbors.push(vertstocheck[1]); 
             neighbors.push(vertstocheck[len-1]); 
       }
       if (vertstocheck[len-1] == vertindex) { 
             neighbors.push(vertstocheck[len-2]); 
             neighbors.push(vertstocheck[0]); 
       }
   }
   // now remove the non-duplicates
   // using this trick:   http://stackoverflow.com/questions/9229645/remove-duplicates-from-javascript-array
  neighbors = neighbors.filter(function(elem, pos) {
     if (neighbors.indexOf(elem) == neighbors.lastIndexOf(elem)) {return neighbors.indexOf(elem) == pos};
   })
   return neighbors
}


function show_border_cells () {
   var bordercell_indices = get_border_cell_indices (vertexArray, allcells, "thick")
   for (var i = aci; i--;) {
     if (contains(i, bordercell_indices)) {allcells[i].state = 1}
   }
   drawboard()
}

 

function get_border_cell_indices (vertarray, cellarray, width) {
    var bordercell_indices = []
    var vertlen = vertarray.length
    for (var v = 1; v<vertlen; v++) {
      var border_neighbor_verts = border_neighbor_vertices(vertarray[v], cellarray)
      if (border_neighbor_verts.length) {
         var cells_part_of = vertarray[v].partof
         for (var c = cells_part_of.length; c--;) {
              bordercell_indices.push(cells_part_of[c])
          }
      }
    }
   // now remove the duplicates
   // using this trick:   http://stackoverflow.com/questions/9229645/remove-duplicates-from-javascript-array
   // if the trick is used,  a thinner border is found, containing only VN_neighbors to the borders.
    // (more precisely, containing only neighbors with two (or more?) vertices on the border -- see snub square for example of non-VN neighbor with two border vertices)
  if (width == "thin")
  bordercell_indices = bordercell_indices.filter(function(elem, pos) {
     if (bordercell_indices.indexOf(elem) != bordercell_indices.lastIndexOf(elem)) {return bordercell_indices.indexOf(elem) == pos};
   })
  

   return bordercell_indices
}


 


// ***********************
 
function The_Full_Zongker (vert, sidelen, vertarray, cellarray) { // note that vert is vertexArray object, not an index number
  var xcenter = vert.x
  var ycenter = vert.y
  var neighbors = neighbor_vertices(vert, cellarray)
  var points = []
  var newshape = []
  var angles = [] // angles will need to be sorted
  for (var i = neighbors.length; i--;) {
    angles.push( vertex_angle(xcenter, ycenter, vertarray[neighbors[i]].x, vertarray[neighbors[i]].y) + 90)
  }
 angles.sort(function(a,b){return a - b})  // numerical sort lowest to highest

 points[0] = turt1(0,0,angles[0], sidelen)
 for (var i = 1; i< angles.length; i++) {
    points.push( turt1(points[i-1][0], points[i-1][1], angles[i], sidelen))
 }
 var  new_centerx = 0
 var  new_centery = 0
 for (var i = points.length; i--;) {
   new_centerx += points[i][0]
   new_centery += points[i][1]
 }
 new_centerx = new_centerx/ points.length
 new_centery = new_centery/ points.length

   var xtranslate = vert.x - new_centerx
   var ytranslate = vert.y - new_centery
 for (var i = points.length; i--;) {
    newshape.push( [points[i][0]+xtranslate, points[i][1]+ytranslate])
 }

   return newshape
}


 
// be sure to distinguish between vertice indicies and vertice objects

  alpha = 66;   // alpha is the index of the vertice which will become the anchor polygon of the new tiling

function Zongker_dual_tiling() {
   var sidelen    = calc_smallest_cell_size()  // (calc_smallest_cell_size() / 2)   // 1 //0.25 // 0.0625   //0.25 // 0.125 // 0.0625

   old_allcells = allcells
   old_aci = aci
   old_vertexArray = vertexArray
   old_v_index = v_index
   
     x_to_verts = []; flr_x_to_verts = []
     vertexArray = [] 
     v_index = 1;
     vertexArray[0] = settypecellvertex  // This shouldn't be used, but  
     allcells = [] 
     aci = 0;  

  // find border vertices and nearby vertices
   indexesof_vertices_who_are_part_of_bordercells = []
   for (var i = old_aci; i--;) {
      if (old_allcells[i].VNneighbors.length != old_allcells[i].fnn.length) {
          indexesof_vertices_who_are_part_of_bordercells =  indexesof_vertices_who_are_part_of_bordercells.concat(old_allcells[i].fnn)
       } 
     // should do the remove duplicates trick here 
   }

     // do the full Zongker and store Zongker polygon and Zongker polygon midpoints
     for (var v = 1; v<old_v_index; v++) {
         var crds = The_Full_Zongker(old_vertexArray[v], sidelen, old_vertexArray, old_allcells)
         var len = crds.length
         var midpoints = []
         for (var i = 0; i<len - 1; i++) {
             midpoints.push([ ( crds[i][0] + crds[i+1][0] )/ 2, ( crds[i][1] + crds[i+1][1] )/ 2])
         }
         midpoints.push( [ (crds[len - 1][0] + crds[0][0])/2, (crds[len - 1][1] + crds[0][1])/2])
         old_vertexArray[v].zongker_polygon = crds
         old_vertexArray[v].zongker_midpoints = midpoints
      }
    
     // Now find a good anchor point

     for (var v = 1; v<old_v_index; v++) { 
        if  (border_neighbor_vertices(old_vertexArray[v], old_allcells).length == 0) {alpha = v; break;}
          //if (contains(old_vertexArray[v], indexesof_vertices_who_are_part_of_bordercells) == false) {alpha = v; break;}
     }
     

     build_verts_and_faces(old_vertexArray[alpha].zongker_polygon, "shape")
     old_vertexArray[alpha].converted_zongker_polygon = old_vertexArray[alpha].zongker_polygon
     old_vertexArray[alpha].converted = true
     var first_targets =  neighbor_vertices(old_vertexArray[alpha], old_allcells)
     for (var i = 0; i<first_targets.length; i++) {
        var first_target_index = first_targets[i]
        if (old_vertexArray[first_target_index].converted != true 
            && first_target_index != 0
            && (border_neighbor_vertices(old_vertexArray[first_target_index], old_allcells).length == 0) 
           // && contains(first_target_index, indexesof_vertices_who_are_part_of_bordercells) == false
            && (old_vertexArray[first_target_index].zongker_polygon.length > 2) 
           ) {target_anchor(old_vertexArray[first_target_index], sidelen, old_vertexArray[alpha])}
     }

  
     // when target_anchor returns from its recursive adventure, 
 
      center_and_scale_mesh()  

       initialize()
       clearboard() 
       drawboard()  
       drawgrid()

    //  old_allcells = []  // not necessarily enough for garbage collection
   //   old_vertexArray = []
     //  initialize() // why does this have to be called twice?
}

  
   

function target_anchor(target_vertex,sidelen, anchor_vertex) {
    var crds =  target_vertex.zongker_polygon
    var anchor_midpoints = anchor_vertex.zongker_midpoints
    var target_midpoints = target_vertex.zongker_midpoints
    var anchor_closest = 0 
    var target_closest = 0
    var distance_so_far = find_distance(anchor_midpoints[0][0], target_midpoints[0][0], anchor_midpoints[0][1], target_midpoints[0][1]) 

    for (var i = 0; i<anchor_midpoints.length; i++) {
      for (var j = 0; j<target_midpoints.length; j++) {
        var this_distance = find_distance(anchor_midpoints[i][0], target_midpoints[j][0], anchor_midpoints[i][1], target_midpoints[j][1])
        if (this_distance < distance_so_far) {
              anchor_closest = i 
              target_closest = j 
              distance_so_far = this_distance
         }
      }
    }

              target_closest += 1
              if (target_closest > target_midpoints.length-1) {target_closest = 0}
   var x_translate = anchor_vertex.converted_zongker_polygon[anchor_closest][0] - crds[target_closest][0]
   var y_translate = anchor_vertex.converted_zongker_polygon[anchor_closest][1] - crds[target_closest][1]
 

   for (var i = 0; i<crds.length; i++) {
       crds[i][0] +=  x_translate
       crds[i][1] +=  y_translate

             /* incorporated into V_snap_to_closest_if_close_enough     
               var flr = Math.floor(crds[i][0])
               if (flr_x_to_verts[flr] == undefined) {flr_x_to_verts[flr] = []}
               var flr_plusone = flr + 1
               if (flr_x_to_verts[flr_plusone] == undefined) {flr_x_to_verts[flr_plusone] = []}
               var flr_minusone = flr - 1
               if (flr_minusone >= 0)if (flr_x_to_verts[flr_minusone] == undefined){flr_x_to_verts[flr_minusone] = []}
             */
               crds[i] = v_snap_to_closest_if_close_enough( crds[i][0] , crds[i][1] , [], sidelen/10) // sidelen/5 worked well for small # of lines
    }

   build_verts_and_faces(crds, "shape")


  target_vertex.converted_zongker_polygon  = crds 
  target_vertex.converted = true

  var neighbors =  neighbor_vertices(target_vertex, old_allcells)
  for (var i = 0; i<neighbors.length; i++) {
    if (old_vertexArray[neighbors[i]].converted != true 
        && neighbors[i] != 0
        && (border_neighbor_vertices(old_vertexArray[neighbors[i]], old_allcells).length == 0) 
       // && contains(neighbors[i], indexesof_vertices_who_are_part_of_bordercells) == false
        && (old_vertexArray[neighbors[i]].zongker_polygon.length > 2) // this was > 2 to exclude lines. >3 excludes triangels  But why are there lines?!!!
       ) {target_anchor(old_vertexArray[neighbors[i]], sidelen, target_vertex)}
  }
   
   
}

        // && border_neighbor_vertices(old_vertexArray[neighbors[i]], old_allcells) == []
       // vs 
       // && contains(neighbors[i], indexesof_vertices_who_are_part_of_bordercells) == false

// -----------










//************************************

//  An approach to Zongker dual tiling which starts with easy dual:

// TRIANGLES ARE COMMENTED OUT

function ZongkerPolygons_inside_easy_dual_tiling() {
  var sidelen    =   calc_smallest_cell_size() /3
  calculate_all_centers()  // could test to see if .centers have already been calculated earlier

   old_allcells = allcells
   old_aci = aci
   old_vertexArray = vertexArray
   old_v_index = v_index
   
    x_to_verts = []; flr_x_to_verts = []
     vertexArray = [] 
     v_index = 1;
     vertexArray[0] = settypecellvertex  // This shouldn't be used, but  
     allcells = [] 
     aci = 0;  

  for (var v = 1; v<old_v_index; v++) {
     var newcell = []
     var center_donors  = old_vertexArray[v].partof.slice()   // The slice() makes a copy so that center_donors has values not a reference!!!!
     if (center_donors.length <=2) {continue;}
     if  (border_neighbor_vertices(old_vertexArray[v], old_allcells).length != 0) {continue;}
   /*
     if (center_donors.length == 3) { 
        var newcell = [old_allcells[center_donors[0]].center, old_allcells[center_donors[1]].center, old_allcells[center_donors[2]].center]
        if (notflat_clockwise_test_for_convex_polygons(newcell) == "counter_clockwise") {
           newcell.reverse()
        }
        build_verts_and_faces(newcell,  "triangle")  
        allcells[aci-1].zongker_polygon = The_Full_Zongker(old_vertexArray[v], sidelen, old_vertexArray, old_allcells)

     }
    */
    // else { // we have to figure out what order to put the center donors in:
       var current_cell, next_cell;
       current_cell = center_donors[0] // note that current cell will be a cell.index, not an actual cell   
       newcell.push(old_allcells[current_cell].center)  //and add current_cell's center to newcell
       center_donors.splice(0,1)   // and remove current_cell from center_donors using splice

       while (center_donors.length > 1) {
         next_cell = find_first_vn_neighbor_from_indices(current_cell, center_donors) 
         if (next_cell == "none") break;
         newcell.push(old_allcells[next_cell].center)     
         var temp_cell_index = center_donors.indexOf(next_cell)   // info needed to remove next_cell from center_donors // doesnt work with IE7, IE8
         center_donors.splice(temp_cell_index, 1) // remove cell from center_donors 
         current_cell = next_cell
       }

      newcell.push(old_allcells[center_donors[0]].center)  // finally, push on the very last center donor.
      if (next_cell != "none") {
        if (notflat_clockwise_test_for_convex_polygons(newcell) == "counter_clockwise") {
           newcell.reverse()
        }
        build_verts_and_faces(newcell,  "shape")  
        allcells[aci-1].zongker_polygon = The_Full_Zongker(old_vertexArray[v], sidelen, old_vertexArray, old_allcells)
      }
   // }  // end else
  }  // end for all vertices

  // now find a good anchor
  



  initialize()


       clearboard() 
       drawboard()  
       drawgrid()

  old_allcells = []  // not necessarily enough for garbage collection

  // temp_show_zdual_The_Full_Zongker_on_top_of_grid ()

      Z_first_anchor()
      center_and_scale_mesh()  

  initialize()

       clearboard() 
       drawboard()  
       drawgrid()
}

    // no need to create converted polygons?  allcells[alpha].converted_zongker_polygon = allcells[alpha].zongker_polygon
    // instead, first find out which vertex in the zongker polygon is closest to the vertex in allcells[alpha].coords
    // find the offset, so that you can use [index%arraylength-1] and change the .coords and the .x and .y
    // for each vertex used, set .converted to true, and for each vertex used, if already converted, use those .x and .y instead of translating from zongker_polygon.  




function Z_first_anchor() {
     var alpha = 0
     for (var i = 0; i <aci; i++) {  // don't start on the border - code from meshcheck
       if (allcells[i].VNneighbors.length == allcells[i].fnn.length) {alpha = i; break}
     }
     console.log("alpha = " + alpha)

 
     var ZP_corresponding_index_to_coord_zero = 0
     var closest_distance = find_distance(allcells[alpha].coords[0], allcells[alpha].zongker_polygon[0][0], allcells[alpha].coords[1], allcells[alpha].zongker_polygon[0][1])
     var ZP_len = allcells[alpha].zongker_polygon.length
     for (var i = 0; i < ZP_len; i++) {
       var this_distance = find_distance(allcells[alpha].coords[0], allcells[alpha].zongker_polygon[i][0], allcells[alpha].coords[1], allcells[alpha].zongker_polygon[i][1])
       if (this_distance < closest_distance)
         {closest_distance = this_distance;  ZP_corresponding_index_to_coord_zero = i}
     }
 //   console.log(ZP_corresponding_index_to_coord_zero)

     // first, convert the alpha's coords to the corresponding Zongker Polygon coordinates
     for (var i = 0; i<ZP_len; i++) {
       allcells[alpha].coords[i*2] =    allcells[alpha].zongker_polygon[(i+ZP_corresponding_index_to_coord_zero) % ZP_len][0]
       allcells[alpha].coords[i*2+1] =  allcells[alpha].zongker_polygon[(i+ZP_corresponding_index_to_coord_zero) % ZP_len][1]
     }
     allcells[alpha].converted = true
     // now change each vertex mentioned in fnn (since we know each isn't converted yet)
    // Lets redefine ZP_len to avoid problems when zongker_polygon.length doesn't, for wahever reason equal allcells[alpha].fnn.length
     ZP_len = allcells[alpha].fnn.length

    for (var i = 0; i<ZP_len; i++) { // ZP_len is equivalent to allcells[alpha].fnn.length
       var v = allcells[alpha].fnn[i]
       vertexArray[v].x =  allcells[alpha].coords[i*2]
       vertexArray[v].y =  allcells[alpha].coords[i*2+1]
       vertexArray[v].converted = true
     }

         // http://stackoverflow.com/questions/5278580/non-recursive-depth-first-search-algorithm

      nodes_to_visit = [ allcells[alpha].VNneighbors[0]];
      while( nodes_to_visit.length > 0 ) {
        currentnode = nodes_to_visit.shift();
        for (var i = currentnode.VNneighbors.length; i--;) {   // nodes_to_visit.prepend( currentnode.children );
           if (currentnode.VNneighbors[i].converted != true){ 
              nodes_to_visit.unshift(currentnode.VNneighbors[i])
           }
        }
        Z_target_anchor( currentnode)
      }


    //  now call the recursive function Z_target_anchor on the anchor's neighbors.


      // Z_target_anchor( allcells[alpha].VNneighbors[0])
   //  Z_target_anchor( allcells[alpha].VNneighbors[0])

/*
    // Now go on a recursive adventure
     var first_targets = allcells[alpha].VNneighbors 
     for (var i = 0; i<first_targets.length; i++) {
        if (first_targets[i].converted != true) {         
           Z_target_anchor(first_targets[i], allcells[alpha])
        }
     }

*/

/*   Or perhaps there is a non-recursive way to do it
      // now walk through the tiling, via a path of VN neighobrs

     var next_target = get_next_target(allcells[alpha])

      while (next_target != "none") {
            Z_target_anchor(next_target)

           next_target = get_next_target(next_target)
      }
        
    
     var first_targets = allcells[alpha].neighbors 
     for (var i = 0; i<first_targets.length; i++) {
        if (first_targets[i].converted != true) {  
          // Z_target_anchor(first_targets[i])
          first_targets[i].state = 1
          first_targets[i].converted = true
           var next_target = get_next_target(first_targets[i])
           while (next_target != "none") {
               //Z_target_anchor(next_target)
               next_target.state = 1
               next_target.converted = true
               next_target = get_next_target(next_target)
           }
        }
     }
*/
                  
   


}

/*
function get_next_target(cel) {
   // var targets = cel.VNneighbors 
   var targets = cel.neighbors 
     for (var i = 0; i<targets.length; i++) {
        if (targets[i].converted != true) {    
           return targets[i]     
        }
     }
     return "none"
}
*/
   


function Z_target_anchor(targetcell) { // oddly, it looks like anchor cell isn't needed

     var ZP_corresponding_index_to_coord_zero = 0
     var closest_distance = find_distance(targetcell.coords[0], targetcell.zongker_polygon[0][0], targetcell.coords[1], targetcell.zongker_polygon[0][1])
     var ZP_len = targetcell.zongker_polygon.length
     for (var i = 0; i < ZP_len; i++) {
       var this_distance = find_distance(targetcell.coords[0], targetcell.zongker_polygon[i][0], targetcell.coords[1], targetcell.zongker_polygon[i][1])
       if (this_distance < closest_distance)
         {closest_distance = this_distance;  ZP_corresponding_index_to_coord_zero = i}
     }  

     var ZP_corresponding_index_to_coord_one = 0
     var closest_distance_one = find_distance(targetcell.coords[2], targetcell.zongker_polygon[1][0], targetcell.coords[3], targetcell.zongker_polygon[1][1])
     var ZP_len = targetcell.zongker_polygon.length
     for (var i = 0; i < ZP_len; i++) {
       var this_distance = find_distance(targetcell.coords[2], targetcell.zongker_polygon[i][0], targetcell.coords[3], targetcell.zongker_polygon[i][1])
       if (this_distance < closest_distance_one)
         {closest_distance_one = this_distance;  ZP_corresponding_index_to_coord_one = i}
     }  
 


    var translate_x = 0
    var translate_y = 0
    // Lets redefine ZP_len to avoid problems when zongker_polygon.length doesn't, for wahever reason equal allcells[alpha].fnn.length
     ZP_len = targetcell.fnn.length
    for (var i = 0; i<ZP_len; i++) { // ZP_len should be equivalent to allcells[alpha].fnn.length
       var v = targetcell.fnn[i]
       if (vertexArray[v].converted == true) {
           targetcell.coords[i*2]   = vertexArray[v].x  
           targetcell.coords[i*2+1] = vertexArray[v].y   
           if (translate_x == 0) {
              translate_x = vertexArray[v].x -  targetcell.zongker_polygon[((i+ ZP_corresponding_index_to_coord_zero ) % ZP_len)][0]
              translate_y = vertexArray[v].y -  targetcell.zongker_polygon[((i+ ZP_corresponding_index_to_coord_zero ) % ZP_len)][1]
           }
        }
     }
    
    for (var i = 0; i<ZP_len; i++) {
       var v = targetcell.fnn[i]
       if  (vertexArray[v].converted != true) {
           targetcell.coords[i*2]   = translate_x +  targetcell.zongker_polygon[((i+ ZP_corresponding_index_to_coord_zero ) % ZP_len)][0]
           targetcell.coords[i*2+1] = translate_y +  targetcell.zongker_polygon[((i+ ZP_corresponding_index_to_coord_zero ) % ZP_len)][1]
           vertexArray[v].x = targetcell.coords[i*2]
           vertexArray[v].y = targetcell.coords[i*2+1]
           vertexArray[v].converted = true
       }
    }

    targetcell.converted = true

  
   
}
             






function notflat_clockwise_test_for_convex_polygons (crds) {
   var result = (crds[1][0] - crds[0][0])*(crds[2][1]-crds[1][1]) - (crds[1][1]-crds[0][1])*(crds[2][0]-crds[1][0])
   if (result > 0) {return "counter_clockwise"} else {return "clockwise"}
}



/*  clockwise counter-clockwise report
var clockwise = 0;
var counter_clockwise = 0
for (var i = aci; i--;) {
  if (clockwise_test_for_convex_polygons (allcells[i]) == "clockwise") {
     clockwise += 1
   }
   else {counter_clockwise +=1}
} 
console.log("clockwise = " + clockwise + "  counterclockwise = " + counter_clockwise)
*/



function temp_show_zdual_The_Full_Zongker_on_top_of_grid () {
       for (var i =aci; i--;) {
         var newshape = allcells[i].zongker_polygon
         if (newshape != undefined && newshape.length > 2) {
             build_verts_and_faces(newshape, "shape")
         }

      }

     clearboard() 
     drawboard()  
     drawgrid()
}















//*****************************************************************************************************
//     Stampfli's combination of grids
//*****************************************************************************************************
                  // This code is from rosettacode.org
                  // Sutherland_Hodgman_polygon_clipping


        function Sutherland_Hodgman_polygon_clipping (subjectPolygon, clipPolygon) {
 
            var cp1, cp2, s, e;
            var inside = function (p) {
                return (cp2[0]-cp1[0])*(p[1]-cp1[1]) > (cp2[1]-cp1[1])*(p[0]-cp1[0]);
            };
            var intersection = function () {
                var dc = [ cp1[0] - cp2[0], cp1[1] - cp2[1] ],
                    dp = [ s[0] - e[0], s[1] - e[1] ],
                    n1 = cp1[0] * cp2[1] - cp1[1] * cp2[0],
                    n2 = s[0] * e[1] - s[1] * e[0], 
                    n3 = 1.0 / (dc[0] * dp[1] - dc[1] * dp[0]);
                return [(n1*dp[0] - n2*dc[0]) * n3, (n1*dp[1] - n2*dc[1]) * n3];
            };
            var outputList = subjectPolygon;
            cp1 = clipPolygon[clipPolygon.length-1];
            for (j in clipPolygon) {
                var cp2 = clipPolygon[j];
                var inputList = outputList;
                outputList = [];
                s = inputList[inputList.length - 1]; //last on the input list
                for (i in inputList) {
                    var e = inputList[i];
                    if (inside(e)) {
                        if (!inside(s)) {
                            outputList.push(intersection());
                        }
                        outputList.push(e);
                    }
                    else if (inside(s)) {
                        outputList.push(intersection());
                    }
                    s = e;
                }
                cp1 = cp2;
            }
            return outputList
        }

function TArray_swap_x_y (tilingarray, shift) {
  for (var i = tilingarray.length; i--;) {
     for (var j = tilingarray[i].length; j--;) {
        tilingarray[i][j] = [tilingarray[i][j][1]+shift, tilingarray[i][j][0]]  // xshift and optional yshift?
     }
  }
  return tilingarray
}


function TArray_rotate_tiling (tilingarray, angle, shift) {
  var t = new Transform();
  t.translate(shift, shift);
  t.rotate(radians(angle));
  for (var i = tilingarray.length; i--;) {
     for (var j = tilingarray[i].length; j--;) {

           tilingarray[i][j] = t.transformPoint(tilingarray[i][j][0],tilingarray[i][j][1])
     }
   }
  return tilingarray
}
  
   

 // t.translate(center_x, center_y);   //<---- question this line -- add shift at t.transform instead of as a translate?

/*
function make_paralell_lines_at_angle ...

   var t = new Transform();
   t.translate(center_x, center_y);  
   t.rotate(radians(angle));
      var start = t.transformPoint(xcurrent-0.25*shift,  ystart)
      var finish = t.transformPoint(xcurrent-0.25*shift, yfinish)

*/



function  TArray_convert_to_cells(tilingarray) {

   for (var i =  tilingarray.length; i--;) {
     if (tilingarray[i].length > 2) {   
       build_verts_and_faces(tilingarray[i], "shape")
       allcells[aci-1].original_index = i
     }
   }
}

function TArray_calc_cell_size (cel) {
   var xmin = Math.abs( cel[0][0])
   var xmax = Math.abs( cel[0][0])
   var ymin = Math.abs( cel[0][1])
   var ymax = Math.abs( cel[0][1])
   var coordslen = cel.length
   for (var j = 0; j<coordslen; j++) {
      if (Math.abs( cel[j][0] < xmin)) {xmin = Math.abs(cel[j][0])}
      if (Math.abs( cel[j][0] > xmax)) {xmax = Math.abs(cel[j][0])}
      if (Math.abs( cel[j][1] < ymin)) {ymin = Math.abs( cel[j][1])}
      if (Math.abs( cel[j][1] > ymax)) {ymax = Math.abs( cel[j][1])}
    }
   return ((xmax - xmin) + (ymax - ymin))/2
}
    
function TArray_calc_avg_cell_size (tilingarray, startindex, endindex) {
   // console.log(startindex + "   " + endindex)
    var avg_cell_size = TArray_calc_cell_size(tilingarray[startindex]);
   if (endindex > startindex) {  // we want to handle the case of one cell in the mesh
       for (var i = startindex + 1; i <= endindex; i++) {
           avg_cell_size = avg_cell_size + TArray_calc_cell_size (tilingarray[i]);
       }
    }
    return avg_cell_size/(endindex - startindex + 1)
}


function TArray_from_name(name, xmax, ymax) {
    var tiling = []
    switch (name) {
    case "Square": tiling = TArray_make_square_tiling(xmax, ymax); break;
    case "Tetrakis_Square":  tiling = TArray_make_tetrakis_square_tiling(xmax, ymax); break;
    case "Cairo":  tiling = TArray_make_cairo_tiling(xmax, ymax); break;
     //  case "Truncated_Square": make_truncated_square_tiling(xmax, ymax); break;
     //  case "Snub_Square" :  make_snub_square_tiling(xmax, ymax); break;
    case "Regular_Trianglular" :   tiling = TArray_make_regular_triangular_tiling(xmax, ymax); break;
    case "Hexagonal" :   tiling = TArray_make_hexagonal_tiling_from_triangles(xmax, ymax); break;
    case "Rhombille" :   tiling = TArray_make_regular_rhombic_tiling(xmax, ymax); break;
    // case "Truncated_Hexagonal" :  make_truncated_hexagonal_tiling(xmax, ymax); break;
    // case "Rhombitrihexagonal" :  make_rhombitrihexagonal_tiling_from_triangles(xmax, ymax); break;
    case "Deltoidal_Trihexagonal":  tiling = TArray_make_deltoidal_trihexagonal_tiling (xmax, ymax); break;
    case "Floret_Pentagonal" :   tiling = TArray_make_floret_pentagonal_tiling(xmax, ymax); break;
    case "Triakis_Triangular" :   tiling = TArray_make_triakis_triangular_tiling(xmax, ymax); break;
    //  case "Truncated_TriHexagonal" :  make_truncated_trihexagonal_tiling(xmax, ymax); break;
    case "Kisrhombille" :   tiling = TArray_make_kisrhombille_tiling (xmax, ymax); break;
    //  case  "Snub_Trihexagonal" :  make_snub_trihexagonal_tiling(xmax, ymax);  break;
    //  case  "Elongated_Triangular" :  make_elongated_triangular_tiling(xmax, ymax); break; 
    case "Prismatic_Pentagonal" : tiling = TArray_make_prismatic_pentagonal_tiling(xmax, ymax); break;

    case "Trihexagonal" :   tiling = TArray_make_trihexagonal_tiling(xmax, ymax); break;
    case "Martini" :  tiling = TArray_make_martini_lattice_tiling (xmax,ymax); break;
    }

    return tiling
  }

/* this kind of works:
    var name1 = "Tetrakis_Square" 
    var name2 = "Tetrakis_Square" 
    var xmax = 70
    var ymax = 70
    var angledif = 45
    shift_calc =2 // .333 // 0

    var swath = tiling2_shapesize * 11 //  15 was ok for hex, could have been smaller

*/ 


function my_dual_method_tiling_generator4 (nfold, lines, shift_calc) {
    // parameters for this function will be name1, name2, xmax, ymax, angledif, and maybe shift (or a number that shift can be derived from using shapesize)
    var name1 = "Hexagonal" 
    var name2 = "Hexagonal" 
    var xmax = 40
    var ymax = 40
    var angledif = 90
    shift_calc = .333 // .25 // .333 // 0

    var tiling1 =  TArray_from_name(name1, xmax, ymax) // TArray_make_hexagonal_tiling_from_triangles (60, 40) 
    var tiling2 =  TArray_from_name(name2, xmax, ymax) // TArray_make_hexagonal_tiling_from_triangles (60, 40) (20,20)
    var tiling2_shapesize =  TArray_calc_avg_cell_size (tiling2, 0, 10)
    
    if (shift_calc != 0) {shift = tiling2_shapesize/shift_calc} else {shift = 0}
 
// tiling1 =  TArray_rotate_tiling (tiling1, 1, shift)

    if (angledif == 90) {
        tiling2 =  TArray_swap_x_y (tiling2, shift)
    }
    else { 
         tiling2 =  TArray_rotate_tiling (tiling2, angledif, shift)
    } 

    //   TArray_convert_to_cells(tiling1)
    //   TArray_convert_to_cells(tiling2)  

    var swath = tiling2_shapesize * 15 //  15 was ok for hex, could have been smaller
     TArr_result = []

     for (var i = tiling1.length; i--;) {
       for (var j = tiling2.length; j--;) {
            if (find_distance(tiling1[i][0][0], tiling2[i][0][0], tiling1[i][0][1], tiling2[i][0][1]) < tiling2_shapesize*swath) {
               
            
                 var candidatecell = Sutherland_Hodgman_polygon_clipping (tiling2[j], tiling1[i])
                 if (candidatecell.length > 2) {  // length == 0 is the most common case

                    var newcell = []
                    for (var k = candidatecell.length; k--;) {
                       var p = [roundNumber(candidatecell[k][0], sigdigits+3), roundNumber(candidatecell[k][1], sigdigits+3)]
                       if (detect_notflat_coord_duplicates (p, newcell) == false) {
                         newcell.push(p)
                       }
                    }
                    TArr_result.push(newcell)
                 }
              
           
                 var candidatecell = Sutherland_Hodgman_polygon_clipping (tiling1[i], tiling2[j]) // reversed from above
                 if (candidatecell.length > 2) {  // length == 0 is the most common case

                    var newcell = []
                    for (var k = candidatecell.length; k--;) {
                       var p = [roundNumber(candidatecell[k][0], sigdigits+3), roundNumber(candidatecell[k][1], sigdigits+3)]
                         if (detect_notflat_coord_duplicates (p, newcell) == false) {
                           newcell.push(p)
                         }
                    }
                    TArr_result.push(newcell)
                 }
          
                              
             } // the if for find_distance
       }
     }
   
     TArray_convert_to_cells(TArr_result) 
   
}


//*****************************************************************************************************












/*  We want to exclude vertices that are on the border, because their vertex stars are inconsistent with the multigrid. 
   Why?  Say the multigrid was formed by slicing up a box.  The box itself is not part of the multigrid.
    So, the right way to do it is to have a way of detecting which vertices are part of an edge which is part of only one polygon.
   But a quick way to do it is to use mesh_check to detect border cells, and then exclude any vertex which has one of those border cells
   in the vertex's partof list.


function mesh_check() {
   for (var i = aci; i--;) {
      if (allcells[i].VNneighbors.length != allcells[i].fnn.length) {allcells[i].state = 1}
   }
   drawboard()
}


*/






function current_temp_show_one_full_zonker(vertexindex, edgelength) {
     var newshape = The_Full_Zongker(vertexArray[vertexindex], edgelength, vertexArray, allcells)

     build_verts_and_faces(newshape, "shape")
     clearboard() 
     drawboard()  
     drawgrid()
}


function current_temp_impose_The_Full_Zongker_on_top_of_grid (edgelength) {
       for (var v = v_index; v--;) {
         var newshape = The_Full_Zongker(vertexArray[v], edgelength, vertexArray, allcells)
         build_verts_and_faces(newshape, "shape")

      }

     clearboard() 
     drawboard()  
     drawgrid()
}



function May31_ZongkerStyle_dualRhomb(cel, vi, sidelen) {
     // for testing vi must be specified, for production, it would be in a loop of all the cell's unprocessed vertices
     var coordslen = allcells[cel].coords.length
     var va = vi - 2
     var vb = vi + 2
     if (vi < 2) {va = coordslen-2} 
     if (vi > coordslen -3) {vb = 0}
 
   var p0x = allcells[cel].coords[va]
   var p0y = allcells[cel].coords[va+1]
   var tvx = allcells[cel].coords[vi]
   var tvy = allcells[cel].coords[vi+1]
   var p1x = allcells[cel].coords[vb]
   var p1y = allcells[cel].coords[vb+1]

     var angle0 =   vertex_angle(tvx, tvy, p0x, p0y)  
     var angle1 =  vertex_angle(tvx, tvy, p1x, p1y)    

     angle0 += 90
     angle1 += 90

   var point1 = turt1(0,0,angle0, sidelen)
   var point2 = turt1(point1[0], point1[1], angle1, sidelen)
   var point3 = turt1(point2[0], point2[1],angle0+180, sidelen)
   var point4 = turt1(point3[0], point3[1],angle1+180, sidelen) // this should be [0,0] and not necesssary to compute

   var new_centerx =(point1[0]+point2[0]+point3[0]+point4[0])/4
   var new_centery =(point1[1]+point2[1]+point3[1]+point4[1])/4
   var xtranslate = tvx - new_centerx
   var ytranslate = tvy - new_centery

    var newshape = [[point1[0]+xtranslate, point1[1]+ytranslate], [point2[0]+xtranslate, point2[1]+ytranslate], [point3[0]+xtranslate, point3[1]+ytranslate],[point4[0]+xtranslate, point4[1]+ytranslate]]

   //new_centerx += xtranslate
  // new_centery += ytranslate


   
     build_verts_and_faces(newshape, "shape")
 
// for testing:
//     clearboard() 
//     drawboard()  
//     drawgrid()

}

function temp_impose_alldualrhombs_on_top_of_grid (edgelength) {
   // the right move is mark each vertex as visited but this is a quick temp move
  for (var i = aci; i--;) {
       for (var v = 0; v <allcells[i].coords.length-1 ; v+=2) {

         May31_ZongkerStyle_dualRhomb(i,v, edgelength)
      }
  }
     clearboard() 
     drawboard()  
     drawgrid()
}







Anon7 - 2021