|
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/ios-test/ |
Upload File : |
var rulesmenu_data = [
// Note that each entry except the last one must be followed by a comma.
["B3/S23", "Rule Menu"],
["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", "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 3 State Fun", "VN_head Moore_tail"],
["VN 3 State Fun"],
["my TriGou"], // beware using the name "Goucher" for other rules - consider what the parser is looking for.
["Goucher Gliders"]
// Note that the last entry must NOT be followed by a comma!
];
var lifepatterns = {
"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"
}
};
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] )
}
}
}
function put_patterns_in_menu() {
for (var p in lifepatterns) {
addOption(document.getElementById("patternmenu"),p,p )
}
}
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);
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 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 change_speed_step() {
var themenu = document.getElementById("speed_step_menu")
speedstep = themenu.options[ themenu.selectedIndex].value
};
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 "Mesh Check":
// mouse_status = "select" // delete this line if "Select Mode" must be selected first
mesh_check();
// themenu.selectedIndex = select_mode_index;
// document.getElementById("selectradio").checked = true
break;
case "Mesh Info":
// mouse_status = "select" // delete this line if "Select Mode" must be selected first
mesh_info();
// themenu.selectedIndex = select_mode_index;
// document.getElementById("selectradio").checked = true
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"
}
function makesite() {
var element = document.createElement("div");
element.setAttribute("id", "buttonsdiv")
//element.setAttribute("style", "-webkit-user-select: none; -moz-user-select: none; -ms-user-select: none;") // commented out for ios
// this also might work: -webkit-tap-highlight-color:transparent;
document.getElementById("MainBody").appendChild(element);
makebutton( "gobutton" , "width: 40px" , " Go " , "buttonsdiv", doTimer)
makebutton("stepbutton", "", "Step", "buttonsdiv", stepCount)
// 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");
document.getElementById("buttonsdiv").appendChild(element);
element.setAttribute("id", "brushmenu");
element.onchange=choose_brush_action
addOption(element, "Mouse Menu", "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", "1")
addOption(element, "Mesh Info", "Mesh Info")
addOption(element, "Mesh ShowHoles", "Mesh Check")
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");
document.getElementById("buttonsdiv").appendChild(element);
element.setAttribute("id", "rulesmenu");
element.onchange=change_rule
put_rules_in_menu()
element = document.createElement("select");
document.getElementById("buttonsdiv").appendChild(element);
element.setAttribute("id", "patternmenu");
element.onchange=pick_pattern_action
addOption(element, "Pattern Menu", "state 1 via mouse")
makebutton("zoominbutton", "", "Zoom In", "buttonsdiv",increase_magnification)
makebutton("zommoutbutton", "", "Zoom Out","buttonsdiv", decrease_magnification)
// makebutton("exportmeshbutton", "", "Export mesh to new tab","buttonsdiv", print_obj)
element = document.createElement("select");
document.getElementById("buttonsdiv").appendChild(element);
element.setAttribute("id", "speed_step_menu");
element.onchange=change_speed_step
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, "20 Gen/Step", 20)
addOption(element, "21 Gen/Step", 21)
addOption(element, "98 Gen/Step", 98)
addOption(element, "99 Gen/Step", 99)
makebutton("randombutton", "width: 40px" , "Test", "buttonsdiv",call_make_random_field_with_top);
makebutton("clearbutton", "", "Clear", "buttonsdiv",clear_field)
element = document.createElement("div");
element.setAttribute("id", "canvasdiv")
element.setAttribute("style", "position: relative; ")
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; ") // commented out for ios
// element.setAttribute("style", "border:1px solid #c3c3c3; position: absolute;") // replacement for ios
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", "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);
/* commenting out for ios
document.getElementById("canvasdiv").onmousemove = cnvs_mousemove;
document.getElementById("canvasdiv").onmouseup = cnvs_mouseup;
document.getElementById("canvasdiv").onmousedown = cnvs_mousedown;
*/
// document.getElementById("MainBody").onkeydown = interpret_keys; // commented out for ios
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"
}
var ctx;
var maxLevel = 4;
var sigdigits = 6
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 generation = 0;
var changedcells = [];
var changecount = 0;
var initialize_flag = false
var the_dimension = 400 // change for ios
/*
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 = []
var y_to_verts = []
// this is for mesh info om=n
function mesh_info() {
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 + " "}
}
alert(answer)
}
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 ponit P1.
// from http://stackoverflow.com/questions/1211212/how-to-calculate-an-angle-from-three-points
function find_angle(p0,p1,c) {
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 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;
}
/*
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])
}
}
}
}
}
}
/* 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
}
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
}
// 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
}
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 3 State Fun
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;
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;
}
else if (inputrulestring.match("VN 3 State Fun")) {
birth1 = "VN 3 State Fun"
survive1 = ""
rulefamily = 7;
}
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("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;
}
}
var newchangedcells = []; // quick global experiment
var NewChangeCount = 0; // quick global experiment
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 drawboard() {
for (var i = aci; i--;) {
colorcell(allcells[i])
}
}
function drawgrid() {
for (var i = aci; i--;) {
strokecell(allcells[i])
}
}
function draw_changed () {
if (drawEdgesFlag) { // this flag might be replaced with the re-definition of colorcell where the flag is set.
for (var i=changecount; i--;){
colorcell( changedcells[i])
}
}
else if (drawShapesFlag) {
for (var i=changecount; i--;){
NoStrokecolorcell( changedcells[i])
}
}
else {
for (var i=changecount; i--;){
Rectcolorcell( changedcells[i])
}
}
}
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 - 28) )
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 - 28)/currentscale // - current_translate_y/currentscale;
draw_at_mouse_here(xmouse, ymouse, "moving")
event.preventDefault(); // this line is intended to ensure that mouseup eventually fires
}
}
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 - 28) )
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) {
var avg_cell_size = calc_cell_size(allcells[startindex]);
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_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 - 28)/currentscale // - current_translate_y/currentscale;
draw_at_mouse_here(xmouse,ymouse, "clicking")
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 - 28) ) // - 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;
function draw_at_mouse_here(xmouse,ymouse, clickingstatus) {
for (var i = aci; i--;) {
if (Math.abs(allcells[i].coords[0] - xmouse) < shapesize*3) {
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])
changedcells[changecount] = allcells[i];
changecount += 1;
lastdrawnhere = i
break;
}
}
}
}
}
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("randombutton").value = "Stop";
timer_is_on=1;
t=setInterval(timedCount,delay);
}
else {
stopCount();
}
};
function stopCount() {
clearInterval(t);
timer_is_on=0;
document.getElementById("gobutton").value = "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
switch(ev.keyCode) {
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;
}
}
function stepCount() {
/*
refresh()
draw_changed()
//drawboard() // pick which algo !!!!!!!!!!!!!!!!!!
generation += 1;
*/
timedCount();
stopCount();
}
// 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']
// 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'};
/*
Render Options:
default - prettiest
noEdges - faster
fillRect - fastest?
putImage - fastest?
Pretty & slowest
Plain & faster
Blocky & fastest
*/
function Rectcolorcell (x) {
var c = x.coords;
var st = x.state;
// if (st) {
ctx.fillStyle = colorlookup[x.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)
// }
//else {
// ctx.clearRect(c[0],c[1], scaledshapesize, scaledshapesize)
// }
}
function NoStrokecolorcell(x) { // candidate for deletion -- see function strokecell(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 colorcell(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();
}
/* 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();
}
*/
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();
}
/*
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]}}};
*/
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 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()
}
}
function print_obj() {
windowref = window.open();
var third = 0;
windowref.document.writeln("<pre>")
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>")
}
function print_pattern () {
windowref = window.open();
var third = 0;
windowref.document.writeln("<pre>")
windowref.document.writeln("#N "+ " Enter_name_here")
windowref.document.write("#R "+ birth1 + "/" + survive1)
if (rulefamily == 2) {windowref.document.write("/C" + n_states)};
windowref.document.writeln("");
//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>")
}
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 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] == "#R") {
newrule = entry[1]
continue;
}
if (entry[0] == "#C") {
newpatternstring = newpatternstring.concat(" " + pattern_entries[i] + " ");
continue;
}
if (newname != "") {
lifepatterns[newname] = {rule: newrule, patternstring: newpatternstring};
}
newname = "";
newrule = "all";
newpatternstring = "";
}
}
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)
}
}
*/
var scalefactor = 1.5;
function increase_magnification() {
clearboard();
ctx.scale(scalefactor, scalefactor);
ctxGrid.scale(scalefactor, scalefactor);
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);
currentscale = currentscale/scalefactor;
scaledshapesize = Math.max(Math.floor(shapesize*currentscale), 1);
// drawShapesFlag = scaledshapesize > shapesthreshhold
// drawEdgesFlag = scaledshapesize > edgesthreshold
drawboard();
drawgrid()
}
// var current_translate_x; // global
// var current_translate_y // global
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)
}
var currentscale;
var shapesize;
var scaledshapesize;
var drawShapesFlag = false; // ios
var drawEdgesFlag = false; // ios
// var shapesthreshhold = 1
// var edgesthreshold = 2
function initialize() {
if (initialize_flag == false) {
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 = "#000000"; // "#FFFFFF" //"#000000";
ctxGrid.lineJoin='round'; // ctx.lineJoin='bevel';
ctxGrid.lineCap='square';
ctxGrid.strokeStyle = "#000000"; // "#FFFFFF" //"#000000";
if (document.getElementById("OBJdata") != null) {
parse_OBJ_entries (document.getElementById("OBJdata").innerHTML);
document.getElementById("MainBody").removeChild(document.getElementById("OBJdata")) // not necessary
}
initialize_neighbors()
center_mesh();
// currentscale = 20/( cell_x_distance(allcells[0], allcells[1]) + cell_y_distance(allcells[0], allcells[1]) ) ;
// another way to do it: var shapesize = ( cell_x_distance(allcells[0], allcells[1]) + cell_y_distance(allcells[0], allcells[1]) ) / 1.6 ;
shapesize = calc_avg_cell_size(1, 10)
currentscale = 980 / mesh_y_size
scaledshapesize = Math.max(Math.floor(shapesize*currentscale), 1);
// drawShapesFlag = scaledshapesize > shapesthreshhold
// drawEdgesFlag = scaledshapesize > edgesthreshold
ctx.lineWidth = shapesize/12
ctxGrid.lineWidth = shapesize/12
ctx.scale(currentscale,currentscale);
ctxGrid.scale(currentscale,currentscale);
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()
initialize_flag = true
generation = 0
make_random_field("top")
drawboard();
// drawgrid() // commenting out for ios
}
}
// 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!
*/