|
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 : |
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()
}