diff --git a/header.php b/header.php index cdf8477..bc66ec1 100644 --- a/header.php +++ b/header.php @@ -9,7 +9,7 @@ } if(!isset($canonical)){ - $canonical = 'http://alanmckay.blog'; + $canonical = 'https://alanmckay.blog'; } if(!isset($title)){ @@ -39,7 +39,11 @@ } if(!isset($meta['url'])){ - $meta['url'] = 'http://alanmckay.blog/'; + $meta['url'] = 'https://alanmckay.blog/'; +} + +if(!isset($relative_path)){ + $relative_path = ""; } ?> @@ -50,6 +54,7 @@ + <?php echo $title;?> @@ -62,4 +67,4 @@ ' /> - \ No newline at end of file + diff --git a/images/address-book-new.svg b/images/address-book-new.svg new file mode 100644 index 0000000..c830289 --- /dev/null +++ b/images/address-book-new.svg @@ -0,0 +1,13 @@ + + + + + + diff --git a/images/code-class.svg b/images/code-class.svg new file mode 100644 index 0000000..10b9711 --- /dev/null +++ b/images/code-class.svg @@ -0,0 +1,13 @@ + + + + + + diff --git a/images/code-function.svg b/images/code-function.svg new file mode 100644 index 0000000..6992d2d --- /dev/null +++ b/images/code-function.svg @@ -0,0 +1,13 @@ + + + + + + diff --git a/images/code-variable.svg b/images/code-variable.svg new file mode 100644 index 0000000..1b2700e --- /dev/null +++ b/images/code-variable.svg @@ -0,0 +1,13 @@ + + + + + + diff --git a/images/cup_control_01.png b/images/cup_control_01.png new file mode 100644 index 0000000..d89647e Binary files /dev/null and b/images/cup_control_01.png differ diff --git a/images/cup_control_02.png b/images/cup_control_02.png new file mode 100644 index 0000000..07c16d6 Binary files /dev/null and b/images/cup_control_02.png differ diff --git a/images/cup_control_03.png b/images/cup_control_03.png new file mode 100644 index 0000000..ed3236a Binary files /dev/null and b/images/cup_control_03.png differ diff --git a/images/cup_control_04.png b/images/cup_control_04.png new file mode 100644 index 0000000..ba8150d Binary files /dev/null and b/images/cup_control_04.png differ diff --git a/images/cup_control_05.png b/images/cup_control_05.png new file mode 100644 index 0000000..e4fa62f Binary files /dev/null and b/images/cup_control_05.png differ diff --git a/images/cup_events.png b/images/cup_events.png new file mode 100644 index 0000000..8266a55 Binary files /dev/null and b/images/cup_events.png differ diff --git a/images/cup_events_red.png b/images/cup_events_red.png new file mode 100644 index 0000000..598ac84 Binary files /dev/null and b/images/cup_events_red.png differ diff --git a/images/cup_music.png b/images/cup_music.png new file mode 100644 index 0000000..3423a2c Binary files /dev/null and b/images/cup_music.png differ diff --git a/images/cup_story_desktop.png b/images/cup_story_desktop.png new file mode 100644 index 0000000..b554e87 Binary files /dev/null and b/images/cup_story_desktop.png differ diff --git a/images/cup_story_mobile.png b/images/cup_story_mobile.png new file mode 100644 index 0000000..b42cdcb Binary files /dev/null and b/images/cup_story_mobile.png differ diff --git a/images/cup_story_tablet.png b/images/cup_story_tablet.png new file mode 100644 index 0000000..4872bbb Binary files /dev/null and b/images/cup_story_tablet.png differ diff --git a/images/cycle.png b/images/cycle.png new file mode 100644 index 0000000..2006945 Binary files /dev/null and b/images/cycle.png differ diff --git a/images/cycle_AT.png b/images/cycle_AT.png new file mode 100644 index 0000000..b655bd4 Binary files /dev/null and b/images/cycle_AT.png differ diff --git a/images/description.svg b/images/description.svg new file mode 100644 index 0000000..8538cfd --- /dev/null +++ b/images/description.svg @@ -0,0 +1,12 @@ + + + + + + + + diff --git a/images/dist-degree.png b/images/dist-degree.png new file mode 100644 index 0000000..553dfec Binary files /dev/null and b/images/dist-degree.png differ diff --git a/images/dist-indeg.png b/images/dist-indeg.png new file mode 100644 index 0000000..d87a5e2 Binary files /dev/null and b/images/dist-indeg.png differ diff --git a/images/dist-outdeg.png b/images/dist-outdeg.png new file mode 100644 index 0000000..e5ad092 Binary files /dev/null and b/images/dist-outdeg.png differ diff --git a/images/insert-endnote.svg b/images/insert-endnote.svg new file mode 100644 index 0000000..fbd6541 --- /dev/null +++ b/images/insert-endnote.svg @@ -0,0 +1,22 @@ + + + + + + + diff --git a/images/ngr.png b/images/ngr.png new file mode 100644 index 0000000..59c0aad Binary files /dev/null and b/images/ngr.png differ diff --git a/images/pldist-degree.png b/images/pldist-degree.png new file mode 100644 index 0000000..550d38c Binary files /dev/null and b/images/pldist-degree.png differ diff --git a/images/pldist-indeg.png b/images/pldist-indeg.png new file mode 100644 index 0000000..c7999c2 Binary files /dev/null and b/images/pldist-indeg.png differ diff --git a/images/pldist-outdeg.png b/images/pldist-outdeg.png new file mode 100644 index 0000000..f49443e Binary files /dev/null and b/images/pldist-outdeg.png differ diff --git a/images/plrdist-degree.png b/images/plrdist-degree.png new file mode 100644 index 0000000..76c9c8c Binary files /dev/null and b/images/plrdist-degree.png differ diff --git a/images/pn.png b/images/pn.png new file mode 100644 index 0000000..fe9d5e4 Binary files /dev/null and b/images/pn.png differ diff --git a/images/pp_A.png b/images/pp_A.png new file mode 100644 index 0000000..3b13d75 Binary files /dev/null and b/images/pp_A.png differ diff --git a/images/pp_B.png b/images/pp_B.png new file mode 100644 index 0000000..d7c4c52 Binary files /dev/null and b/images/pp_B.png differ diff --git a/images/pp_C.png b/images/pp_C.png new file mode 100644 index 0000000..6695dfb Binary files /dev/null and b/images/pp_C.png differ diff --git a/images/project-archive-arrow.png b/images/project-archive-arrow.png new file mode 100644 index 0000000..ab2bf50 Binary files /dev/null and b/images/project-archive-arrow.png differ diff --git a/images/project-archive.png b/images/project-archive.png new file mode 100644 index 0000000..2cae720 Binary files /dev/null and b/images/project-archive.png differ diff --git a/images/rdist-degree.png b/images/rdist-degree.png new file mode 100644 index 0000000..0a3e473 Binary files /dev/null and b/images/rdist-degree.png differ diff --git a/images/rdist-indeg.png b/images/rdist-indeg.png new file mode 100644 index 0000000..9cb23a0 Binary files /dev/null and b/images/rdist-indeg.png differ diff --git a/images/rdist-outdeg.png b/images/rdist-outdeg.png new file mode 100644 index 0000000..5827568 Binary files /dev/null and b/images/rdist-outdeg.png differ diff --git a/images/rdist2-outdeg.png b/images/rdist2-outdeg.png new file mode 100644 index 0000000..cf138b6 Binary files /dev/null and b/images/rdist2-outdeg.png differ diff --git a/images/rdist3-outdeg.png b/images/rdist3-outdeg.png new file mode 100644 index 0000000..e8c2d43 Binary files /dev/null and b/images/rdist3-outdeg.png differ diff --git a/images/rdist4-outdeg.png b/images/rdist4-outdeg.png new file mode 100644 index 0000000..1e00f22 Binary files /dev/null and b/images/rdist4-outdeg.png differ diff --git a/images/rdist5-outdeg.png b/images/rdist5-outdeg.png new file mode 100644 index 0000000..7c18db3 Binary files /dev/null and b/images/rdist5-outdeg.png differ diff --git a/images/rnet-vis.png b/images/rnet-vis.png new file mode 100644 index 0000000..35705e1 Binary files /dev/null and b/images/rnet-vis.png differ diff --git a/images/sf-outliers.png b/images/sf-outliers.png new file mode 100644 index 0000000..2af4793 Binary files /dev/null and b/images/sf-outliers.png differ diff --git a/images/sfnet-vis.png b/images/sfnet-vis.png new file mode 100644 index 0000000..1278563 Binary files /dev/null and b/images/sfnet-vis.png differ diff --git a/images/sr.png b/images/sr.png new file mode 100644 index 0000000..05cc879 Binary files /dev/null and b/images/sr.png differ diff --git a/images/text-logo-grey.ico b/images/text-logo-grey.ico new file mode 100644 index 0000000..16a0434 Binary files /dev/null and b/images/text-logo-grey.ico differ diff --git a/index.php b/index.php index 8430ff7..45d5db0 100644 --- a/index.php +++ b/index.php @@ -17,6 +17,10 @@ Icon for blog link Writings + + Icon for projects link + Projects + Icon for VSCO link VSCO @@ -32,4 +36,4 @@ - \ No newline at end of file + diff --git a/js/index_functions.js b/js/index_functions.js new file mode 100644 index 0000000..af22b7e --- /dev/null +++ b/js/index_functions.js @@ -0,0 +1,30 @@ +function primeClassTransitions(class_name, css_property, initial_value, transition_time, transition_switch){ + elements = document.getElementsByClassName(class_name); + for (i = 0; i < elements.length; i++){ + element = elements[i]; + if (transition_switch == true){ + element.style['transition'] = css_property + ' ' + transition_time; + }else{ + element.style['transition'] = css_property + ' 0s'; + } + element.style[css_property] = initial_value; + } +} + + +function applyClassTransitionEffects(class_name, css_property, thresh_in_value, thresh_in_time, thresh_out_value, thresh_out_time, screen_threshold) { + height = screen.height; + elements = document.getElementsByClassName(class_name); + for (i = 0; i < elements.length; i++){ + element = elements[i]; + bound = element.getBoundingClientRect(); + if (bound.y < height - screen_threshold){ + element.style['transition'] = css_property + ' ' + thresh_in_time; + element.style[css_property] = thresh_in_value; + } + if ((bound.y < 0) || (bound.y > height - screen_threshold)){ + element.style['transition'] = css_property + ' ' + thresh_out_time; + element.style[css_property] = thresh_out_value; + } + } +} diff --git a/js/project_functions.js b/js/project_functions.js new file mode 100644 index 0000000..cff0e74 --- /dev/null +++ b/js/project_functions.js @@ -0,0 +1,88 @@ +/* ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- */ + +function toggleCollapsible(body_id, header_id, expand_str, collapse_str, collapsed_attribute){ + header = document.getElementById(header_id); + if(header.getAttribute(collapsed_attribute) == 'false'){ + new_status_bool = true; + header.setAttribute(collapsed_attribute,true); + }else{ + new_status_bool = false; + header.setAttribute(collapsed_attribute,false); + } + old_header_str = header.innerHTML; + if(new_status_bool == true){ + document.getElementById(body_id).style.display = "block"; + new_header_str = collapse_str + old_header_str.slice(expand_str.length,old_header_str.length); + }else{ + document.getElementById(body_id).style.display = "none"; + new_header_str = expand_str + old_header_str.slice(collapse_str.length,old_header_str.length); + } + document.getElementById(header_id).innerHTML = new_header_str; +} + +/* ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- */ + +// This solves the problem of proportional scaling with respect to +// height and an image's width being scaled on window resize; +// Something that native css can't handle as far as I know. +function setDynamicFigureStyle(event_type, screen_state,element,old_ele_height,class_id){ + + screen_height = window.outerHeight; + shrinking = screen_state['shrinking']; + growing = screen_state['growing']; + flex_switch = screen_state['flex_switch']; + old_screen_height = screen_state['old_screen_height']; + old_orientation = screen_state['old_orientation']; + + // Conditional considers prior values of shrinking and growing: + if(!(shrinking == true && growing == true)){ + // Determine if screen is currently shrinking or growing: + if(old_screen_height < screen_height){ + shrinking = false; + growing = true; + }else if(old_screen_height > screen_height){ + shrinking = true; + growing = false; + }else{ + shrinking = false; + growing = false; + // Factors the change in the x-axis causing the fig to be bigger: + if(element_height > screen_height){ + shrinking = true; + } + // Contingent Redundancy: + old_ele_height = element.getBoundingClientRect().height; + } + }else{ + shrinking = true; + growing = false; + } + // Get the height of the figure as a whole (not the window): + element_height = element.getBoundingClientRect().height; + if(shrinking == true){ + if(element_height >= screen_height){ + if(flex_switch == false){ + old_ele_height = element.getBoundingClientRect().height; + flex_switch = true; + } + element.classList.add(class_id); + element.style.display = 'flex'; + } + } + if(growing == true){ + if(old_ele_height < screen_height){ + element.classList.remove(class_id); + element.style.display = 'inherit'; + flex_switch = false; + } + } + old_screen_height = screen_height; + return [{"old_screen_height": old_screen_height, + "shrinking": shrinking, + "growing": growing, + "old_orientation": old_orientation, + "flex_switch": flex_switch + },old_ele_height]; +} + +/* ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- */ diff --git a/js/writing_functions.js b/js/writing_functions.js new file mode 100644 index 0000000..41cc161 --- /dev/null +++ b/js/writing_functions.js @@ -0,0 +1,11 @@ +//Abstract this function to include range parameters +function reframeImage(elementID,inPosition,outPosition, threshold){ + element = document.getElementById(elementID); + bounding = element.getBoundingClientRect(); + if ( (threshold - bounding.top > 0) && (bounding.top > 0) ) + { + element.style['object-position'] = inPosition; + } else{ + element.style['object-position'] = outPosition; + } +} diff --git a/projects/aquatint/generate_png/0.1_1.0_1-cycle_resize-aquatint.png b/projects/aquatint/generate_png/0.1_1.0_1-cycle_resize-aquatint.png new file mode 100644 index 0000000..da14686 Binary files /dev/null and b/projects/aquatint/generate_png/0.1_1.0_1-cycle_resize-aquatint.png differ diff --git a/projects/aquatint/generate_png/0.1_1.0_2-cycle_resize-aquatint.png b/projects/aquatint/generate_png/0.1_1.0_2-cycle_resize-aquatint.png new file mode 100644 index 0000000..92bccc6 Binary files /dev/null and b/projects/aquatint/generate_png/0.1_1.0_2-cycle_resize-aquatint.png differ diff --git a/projects/aquatint/generate_png/0.1_1.0_3-cycle_resize-aquatint.png b/projects/aquatint/generate_png/0.1_1.0_3-cycle_resize-aquatint.png new file mode 100644 index 0000000..72441b4 Binary files /dev/null and b/projects/aquatint/generate_png/0.1_1.0_3-cycle_resize-aquatint.png differ diff --git a/projects/aquatint/generate_png/0.1_1.0_4-cycle_resize-aquatint.png b/projects/aquatint/generate_png/0.1_1.0_4-cycle_resize-aquatint.png new file mode 100644 index 0000000..7cbfda5 Binary files /dev/null and b/projects/aquatint/generate_png/0.1_1.0_4-cycle_resize-aquatint.png differ diff --git a/projects/aquatint/generate_png/0.1_1.0_5-cycle_resize-aquatint.png b/projects/aquatint/generate_png/0.1_1.0_5-cycle_resize-aquatint.png new file mode 100644 index 0000000..1fefc7e Binary files /dev/null and b/projects/aquatint/generate_png/0.1_1.0_5-cycle_resize-aquatint.png differ diff --git a/projects/aquatint/generate_png/0.1_3.0_1-cycle_resize-aquatint.png b/projects/aquatint/generate_png/0.1_3.0_1-cycle_resize-aquatint.png new file mode 100644 index 0000000..a1b5bcb Binary files /dev/null and b/projects/aquatint/generate_png/0.1_3.0_1-cycle_resize-aquatint.png differ diff --git a/projects/aquatint/generate_png/0.1_3.0_2-cycle_resize-aquatint.png b/projects/aquatint/generate_png/0.1_3.0_2-cycle_resize-aquatint.png new file mode 100644 index 0000000..c16113f Binary files /dev/null and b/projects/aquatint/generate_png/0.1_3.0_2-cycle_resize-aquatint.png differ diff --git a/projects/aquatint/generate_png/0.1_3.0_3-cycle_resize-aquatint.png b/projects/aquatint/generate_png/0.1_3.0_3-cycle_resize-aquatint.png new file mode 100644 index 0000000..36b0bc9 Binary files /dev/null and b/projects/aquatint/generate_png/0.1_3.0_3-cycle_resize-aquatint.png differ diff --git a/projects/aquatint/generate_png/0.1_3.0_4-cycle_resize-aquatint.png b/projects/aquatint/generate_png/0.1_3.0_4-cycle_resize-aquatint.png new file mode 100644 index 0000000..bff1ead Binary files /dev/null and b/projects/aquatint/generate_png/0.1_3.0_4-cycle_resize-aquatint.png differ diff --git a/projects/aquatint/generate_png/0.1_3.0_5-cycle_resize-aquatint.png b/projects/aquatint/generate_png/0.1_3.0_5-cycle_resize-aquatint.png new file mode 100644 index 0000000..44eb313 Binary files /dev/null and b/projects/aquatint/generate_png/0.1_3.0_5-cycle_resize-aquatint.png differ diff --git a/projects/aquatint/generate_png/0.1_5.0_1-cycle_resize-aquatint.png b/projects/aquatint/generate_png/0.1_5.0_1-cycle_resize-aquatint.png new file mode 100644 index 0000000..dbf5dbf Binary files /dev/null and b/projects/aquatint/generate_png/0.1_5.0_1-cycle_resize-aquatint.png differ diff --git a/projects/aquatint/generate_png/0.1_5.0_2-cycle_resize-aquatint.png b/projects/aquatint/generate_png/0.1_5.0_2-cycle_resize-aquatint.png new file mode 100644 index 0000000..c7bf64a Binary files /dev/null and b/projects/aquatint/generate_png/0.1_5.0_2-cycle_resize-aquatint.png differ diff --git a/projects/aquatint/generate_png/0.1_5.0_3-cycle_resize-aquatint.png b/projects/aquatint/generate_png/0.1_5.0_3-cycle_resize-aquatint.png new file mode 100644 index 0000000..c5494bd Binary files /dev/null and b/projects/aquatint/generate_png/0.1_5.0_3-cycle_resize-aquatint.png differ diff --git a/projects/aquatint/generate_png/0.1_5.0_4-cycle_resize-aquatint.png b/projects/aquatint/generate_png/0.1_5.0_4-cycle_resize-aquatint.png new file mode 100644 index 0000000..0635f27 Binary files /dev/null and b/projects/aquatint/generate_png/0.1_5.0_4-cycle_resize-aquatint.png differ diff --git a/projects/aquatint/generate_png/0.1_5.0_5-cycle_resize-aquatint.png b/projects/aquatint/generate_png/0.1_5.0_5-cycle_resize-aquatint.png new file mode 100644 index 0000000..f173564 Binary files /dev/null and b/projects/aquatint/generate_png/0.1_5.0_5-cycle_resize-aquatint.png differ diff --git a/projects/aquatint/generate_png/0.1_7.0_1-cycle_resize-aquatint.png b/projects/aquatint/generate_png/0.1_7.0_1-cycle_resize-aquatint.png new file mode 100644 index 0000000..e95e5e6 Binary files /dev/null and b/projects/aquatint/generate_png/0.1_7.0_1-cycle_resize-aquatint.png differ diff --git a/projects/aquatint/generate_png/0.1_7.0_2-cycle_resize-aquatint.png b/projects/aquatint/generate_png/0.1_7.0_2-cycle_resize-aquatint.png new file mode 100644 index 0000000..0b840f2 Binary files /dev/null and b/projects/aquatint/generate_png/0.1_7.0_2-cycle_resize-aquatint.png differ diff --git a/projects/aquatint/generate_png/0.1_7.0_3-cycle_resize-aquatint.png b/projects/aquatint/generate_png/0.1_7.0_3-cycle_resize-aquatint.png new file mode 100644 index 0000000..de0088e Binary files /dev/null and b/projects/aquatint/generate_png/0.1_7.0_3-cycle_resize-aquatint.png differ diff --git a/projects/aquatint/generate_png/0.1_7.0_4-cycle_resize-aquatint.png b/projects/aquatint/generate_png/0.1_7.0_4-cycle_resize-aquatint.png new file mode 100644 index 0000000..1890908 Binary files /dev/null and b/projects/aquatint/generate_png/0.1_7.0_4-cycle_resize-aquatint.png differ diff --git a/projects/aquatint/generate_png/0.1_7.0_5-cycle_resize-aquatint.png b/projects/aquatint/generate_png/0.1_7.0_5-cycle_resize-aquatint.png new file mode 100644 index 0000000..9df6748 Binary files /dev/null and b/projects/aquatint/generate_png/0.1_7.0_5-cycle_resize-aquatint.png differ diff --git a/projects/aquatint/generate_png/0.1_9.0_1-cycle_resize-aquatint.png b/projects/aquatint/generate_png/0.1_9.0_1-cycle_resize-aquatint.png new file mode 100644 index 0000000..cff9964 Binary files /dev/null and b/projects/aquatint/generate_png/0.1_9.0_1-cycle_resize-aquatint.png differ diff --git a/projects/aquatint/generate_png/0.1_9.0_2-cycle_resize-aquatint.png b/projects/aquatint/generate_png/0.1_9.0_2-cycle_resize-aquatint.png new file mode 100644 index 0000000..5dbf956 Binary files /dev/null and b/projects/aquatint/generate_png/0.1_9.0_2-cycle_resize-aquatint.png differ diff --git a/projects/aquatint/generate_png/0.1_9.0_3-cycle_resize-aquatint.png b/projects/aquatint/generate_png/0.1_9.0_3-cycle_resize-aquatint.png new file mode 100644 index 0000000..012ab38 Binary files /dev/null and b/projects/aquatint/generate_png/0.1_9.0_3-cycle_resize-aquatint.png differ diff --git a/projects/aquatint/generate_png/0.1_9.0_4-cycle_resize-aquatint.png b/projects/aquatint/generate_png/0.1_9.0_4-cycle_resize-aquatint.png new file mode 100644 index 0000000..6fc887a Binary files /dev/null and b/projects/aquatint/generate_png/0.1_9.0_4-cycle_resize-aquatint.png differ diff --git a/projects/aquatint/generate_png/0.1_9.0_5-cycle_resize-aquatint.png b/projects/aquatint/generate_png/0.1_9.0_5-cycle_resize-aquatint.png new file mode 100644 index 0000000..2396a83 Binary files /dev/null and b/projects/aquatint/generate_png/0.1_9.0_5-cycle_resize-aquatint.png differ diff --git a/projects/aquatint/generate_png/0.3_1.0_1-cycle_resize-aquatint.png b/projects/aquatint/generate_png/0.3_1.0_1-cycle_resize-aquatint.png new file mode 100644 index 0000000..60a03cc Binary files /dev/null and b/projects/aquatint/generate_png/0.3_1.0_1-cycle_resize-aquatint.png differ diff --git a/projects/aquatint/generate_png/0.3_1.0_2-cycle_resize-aquatint.png b/projects/aquatint/generate_png/0.3_1.0_2-cycle_resize-aquatint.png new file mode 100644 index 0000000..5ab5a8b Binary files /dev/null and b/projects/aquatint/generate_png/0.3_1.0_2-cycle_resize-aquatint.png differ diff --git a/projects/aquatint/generate_png/0.3_1.0_3-cycle_resize-aquatint.png b/projects/aquatint/generate_png/0.3_1.0_3-cycle_resize-aquatint.png new file mode 100644 index 0000000..76f930a Binary files /dev/null and b/projects/aquatint/generate_png/0.3_1.0_3-cycle_resize-aquatint.png differ diff --git a/projects/aquatint/generate_png/0.3_1.0_4-cycle_resize-aquatint.png b/projects/aquatint/generate_png/0.3_1.0_4-cycle_resize-aquatint.png new file mode 100644 index 0000000..ede85cd Binary files /dev/null and b/projects/aquatint/generate_png/0.3_1.0_4-cycle_resize-aquatint.png differ diff --git a/projects/aquatint/generate_png/0.3_1.0_5-cycle_resize-aquatint.png b/projects/aquatint/generate_png/0.3_1.0_5-cycle_resize-aquatint.png new file mode 100644 index 0000000..f3463bd Binary files /dev/null and b/projects/aquatint/generate_png/0.3_1.0_5-cycle_resize-aquatint.png differ diff --git a/projects/aquatint/generate_png/0.3_3.0_1-cycle_resize-aquatint.png b/projects/aquatint/generate_png/0.3_3.0_1-cycle_resize-aquatint.png new file mode 100644 index 0000000..8933463 Binary files /dev/null and b/projects/aquatint/generate_png/0.3_3.0_1-cycle_resize-aquatint.png differ diff --git a/projects/aquatint/generate_png/0.3_3.0_2-cycle_resize-aquatint.png b/projects/aquatint/generate_png/0.3_3.0_2-cycle_resize-aquatint.png new file mode 100644 index 0000000..d9bfdf9 Binary files /dev/null and b/projects/aquatint/generate_png/0.3_3.0_2-cycle_resize-aquatint.png differ diff --git a/projects/aquatint/generate_png/0.3_3.0_3-cycle_resize-aquatint.png b/projects/aquatint/generate_png/0.3_3.0_3-cycle_resize-aquatint.png new file mode 100644 index 0000000..abd4342 Binary files /dev/null and b/projects/aquatint/generate_png/0.3_3.0_3-cycle_resize-aquatint.png differ diff --git a/projects/aquatint/generate_png/0.3_3.0_4-cycle_resize-aquatint.png b/projects/aquatint/generate_png/0.3_3.0_4-cycle_resize-aquatint.png new file mode 100644 index 0000000..9794858 Binary files /dev/null and b/projects/aquatint/generate_png/0.3_3.0_4-cycle_resize-aquatint.png differ diff --git a/projects/aquatint/generate_png/0.3_3.0_5-cycle_resize-aquatint.png b/projects/aquatint/generate_png/0.3_3.0_5-cycle_resize-aquatint.png new file mode 100644 index 0000000..7fd3de2 Binary files /dev/null and b/projects/aquatint/generate_png/0.3_3.0_5-cycle_resize-aquatint.png differ diff --git a/projects/aquatint/generate_png/0.3_5.0_1-cycle_resize-aquatint.png b/projects/aquatint/generate_png/0.3_5.0_1-cycle_resize-aquatint.png new file mode 100644 index 0000000..6af792c Binary files /dev/null and b/projects/aquatint/generate_png/0.3_5.0_1-cycle_resize-aquatint.png differ diff --git a/projects/aquatint/generate_png/0.3_5.0_2-cycle_resize-aquatint.png b/projects/aquatint/generate_png/0.3_5.0_2-cycle_resize-aquatint.png new file mode 100644 index 0000000..002c448 Binary files /dev/null and b/projects/aquatint/generate_png/0.3_5.0_2-cycle_resize-aquatint.png differ diff --git a/projects/aquatint/generate_png/0.3_5.0_3-cycle_resize-aquatint.png b/projects/aquatint/generate_png/0.3_5.0_3-cycle_resize-aquatint.png new file mode 100644 index 0000000..3a50199 Binary files /dev/null and b/projects/aquatint/generate_png/0.3_5.0_3-cycle_resize-aquatint.png differ diff --git a/projects/aquatint/generate_png/0.3_5.0_4-cycle_resize-aquatint.png b/projects/aquatint/generate_png/0.3_5.0_4-cycle_resize-aquatint.png new file mode 100644 index 0000000..2528447 Binary files /dev/null and b/projects/aquatint/generate_png/0.3_5.0_4-cycle_resize-aquatint.png differ diff --git a/projects/aquatint/generate_png/0.3_5.0_5-cycle_resize-aquatint.png b/projects/aquatint/generate_png/0.3_5.0_5-cycle_resize-aquatint.png new file mode 100644 index 0000000..a9428f1 Binary files /dev/null and b/projects/aquatint/generate_png/0.3_5.0_5-cycle_resize-aquatint.png differ diff --git a/projects/aquatint/generate_png/0.3_7.0_1-cycle_resize-aquatint.png b/projects/aquatint/generate_png/0.3_7.0_1-cycle_resize-aquatint.png new file mode 100644 index 0000000..976ca6d Binary files /dev/null and b/projects/aquatint/generate_png/0.3_7.0_1-cycle_resize-aquatint.png differ diff --git a/projects/aquatint/generate_png/0.3_7.0_2-cycle_resize-aquatint.png b/projects/aquatint/generate_png/0.3_7.0_2-cycle_resize-aquatint.png new file mode 100644 index 0000000..d39ab81 Binary files /dev/null and b/projects/aquatint/generate_png/0.3_7.0_2-cycle_resize-aquatint.png differ diff --git a/projects/aquatint/generate_png/0.3_7.0_3-cycle_resize-aquatint.png b/projects/aquatint/generate_png/0.3_7.0_3-cycle_resize-aquatint.png new file mode 100644 index 0000000..62ff342 Binary files /dev/null and b/projects/aquatint/generate_png/0.3_7.0_3-cycle_resize-aquatint.png differ diff --git a/projects/aquatint/generate_png/0.3_7.0_4-cycle_resize-aquatint.png b/projects/aquatint/generate_png/0.3_7.0_4-cycle_resize-aquatint.png new file mode 100644 index 0000000..d9dd347 Binary files /dev/null and b/projects/aquatint/generate_png/0.3_7.0_4-cycle_resize-aquatint.png differ diff --git a/projects/aquatint/generate_png/0.3_7.0_5-cycle_resize-aquatint.png b/projects/aquatint/generate_png/0.3_7.0_5-cycle_resize-aquatint.png new file mode 100644 index 0000000..ea6f6e4 Binary files /dev/null and b/projects/aquatint/generate_png/0.3_7.0_5-cycle_resize-aquatint.png differ diff --git a/projects/aquatint/generate_png/0.3_9.0_1-cycle_resize-aquatint.png b/projects/aquatint/generate_png/0.3_9.0_1-cycle_resize-aquatint.png new file mode 100644 index 0000000..c2ec240 Binary files /dev/null and b/projects/aquatint/generate_png/0.3_9.0_1-cycle_resize-aquatint.png differ diff --git a/projects/aquatint/generate_png/0.3_9.0_2-cycle_resize-aquatint.png b/projects/aquatint/generate_png/0.3_9.0_2-cycle_resize-aquatint.png new file mode 100644 index 0000000..fe656de Binary files /dev/null and b/projects/aquatint/generate_png/0.3_9.0_2-cycle_resize-aquatint.png differ diff --git a/projects/aquatint/generate_png/0.3_9.0_3-cycle_resize-aquatint.png b/projects/aquatint/generate_png/0.3_9.0_3-cycle_resize-aquatint.png new file mode 100644 index 0000000..fe433f5 Binary files /dev/null and b/projects/aquatint/generate_png/0.3_9.0_3-cycle_resize-aquatint.png differ diff --git a/projects/aquatint/generate_png/0.3_9.0_4-cycle_resize-aquatint.png b/projects/aquatint/generate_png/0.3_9.0_4-cycle_resize-aquatint.png new file mode 100644 index 0000000..d56857d Binary files /dev/null and b/projects/aquatint/generate_png/0.3_9.0_4-cycle_resize-aquatint.png differ diff --git a/projects/aquatint/generate_png/0.3_9.0_5-cycle_resize-aquatint.png b/projects/aquatint/generate_png/0.3_9.0_5-cycle_resize-aquatint.png new file mode 100644 index 0000000..287ed31 Binary files /dev/null and b/projects/aquatint/generate_png/0.3_9.0_5-cycle_resize-aquatint.png differ diff --git a/projects/aquatint/generate_png/0.5_1.0_1-cycle_resize-aquatint.png b/projects/aquatint/generate_png/0.5_1.0_1-cycle_resize-aquatint.png new file mode 100644 index 0000000..4b9a738 Binary files /dev/null and b/projects/aquatint/generate_png/0.5_1.0_1-cycle_resize-aquatint.png differ diff --git a/projects/aquatint/generate_png/0.5_1.0_2-cycle_resize-aquatint.png b/projects/aquatint/generate_png/0.5_1.0_2-cycle_resize-aquatint.png new file mode 100644 index 0000000..72ebcee Binary files /dev/null and b/projects/aquatint/generate_png/0.5_1.0_2-cycle_resize-aquatint.png differ diff --git a/projects/aquatint/generate_png/0.5_1.0_3-cycle_resize-aquatint.png b/projects/aquatint/generate_png/0.5_1.0_3-cycle_resize-aquatint.png new file mode 100644 index 0000000..4aedf49 Binary files /dev/null and b/projects/aquatint/generate_png/0.5_1.0_3-cycle_resize-aquatint.png differ diff --git a/projects/aquatint/generate_png/0.5_1.0_4-cycle_resize-aquatint.png b/projects/aquatint/generate_png/0.5_1.0_4-cycle_resize-aquatint.png new file mode 100644 index 0000000..28d679e Binary files /dev/null and b/projects/aquatint/generate_png/0.5_1.0_4-cycle_resize-aquatint.png differ diff --git a/projects/aquatint/generate_png/0.5_1.0_5-cycle_resize-aquatint.png b/projects/aquatint/generate_png/0.5_1.0_5-cycle_resize-aquatint.png new file mode 100644 index 0000000..8dc10af Binary files /dev/null and b/projects/aquatint/generate_png/0.5_1.0_5-cycle_resize-aquatint.png differ diff --git a/projects/aquatint/generate_png/0.5_3.0_1-cycle_resize-aquatint.png b/projects/aquatint/generate_png/0.5_3.0_1-cycle_resize-aquatint.png new file mode 100644 index 0000000..c691240 Binary files /dev/null and b/projects/aquatint/generate_png/0.5_3.0_1-cycle_resize-aquatint.png differ diff --git a/projects/aquatint/generate_png/0.5_3.0_2-cycle_resize-aquatint.png b/projects/aquatint/generate_png/0.5_3.0_2-cycle_resize-aquatint.png new file mode 100644 index 0000000..345e06f Binary files /dev/null and b/projects/aquatint/generate_png/0.5_3.0_2-cycle_resize-aquatint.png differ diff --git a/projects/aquatint/generate_png/0.5_3.0_3-cycle_resize-aquatint.png b/projects/aquatint/generate_png/0.5_3.0_3-cycle_resize-aquatint.png new file mode 100644 index 0000000..07daad7 Binary files /dev/null and b/projects/aquatint/generate_png/0.5_3.0_3-cycle_resize-aquatint.png differ diff --git a/projects/aquatint/generate_png/0.5_3.0_4-cycle_resize-aquatint.png b/projects/aquatint/generate_png/0.5_3.0_4-cycle_resize-aquatint.png new file mode 100644 index 0000000..2009f80 Binary files /dev/null and b/projects/aquatint/generate_png/0.5_3.0_4-cycle_resize-aquatint.png differ diff --git a/projects/aquatint/generate_png/0.5_3.0_5-cycle_resize-aquatint.png b/projects/aquatint/generate_png/0.5_3.0_5-cycle_resize-aquatint.png new file mode 100644 index 0000000..89fac78 Binary files /dev/null and b/projects/aquatint/generate_png/0.5_3.0_5-cycle_resize-aquatint.png differ diff --git a/projects/aquatint/generate_png/0.5_5.0_1-cycle_resize-aquatint.png b/projects/aquatint/generate_png/0.5_5.0_1-cycle_resize-aquatint.png new file mode 100644 index 0000000..6108663 Binary files /dev/null and b/projects/aquatint/generate_png/0.5_5.0_1-cycle_resize-aquatint.png differ diff --git a/projects/aquatint/generate_png/0.5_5.0_2-cycle_resize-aquatint.png b/projects/aquatint/generate_png/0.5_5.0_2-cycle_resize-aquatint.png new file mode 100644 index 0000000..d836bd7 Binary files /dev/null and b/projects/aquatint/generate_png/0.5_5.0_2-cycle_resize-aquatint.png differ diff --git a/projects/aquatint/generate_png/0.5_5.0_3-cycle_resize-aquatint.png b/projects/aquatint/generate_png/0.5_5.0_3-cycle_resize-aquatint.png new file mode 100644 index 0000000..2f57391 Binary files /dev/null and b/projects/aquatint/generate_png/0.5_5.0_3-cycle_resize-aquatint.png differ diff --git a/projects/aquatint/generate_png/0.5_5.0_4-cycle_resize-aquatint.png b/projects/aquatint/generate_png/0.5_5.0_4-cycle_resize-aquatint.png new file mode 100644 index 0000000..aa50c5d Binary files /dev/null and b/projects/aquatint/generate_png/0.5_5.0_4-cycle_resize-aquatint.png differ diff --git a/projects/aquatint/generate_png/0.5_5.0_5-cycle_resize-aquatint.png b/projects/aquatint/generate_png/0.5_5.0_5-cycle_resize-aquatint.png new file mode 100644 index 0000000..7922245 Binary files /dev/null and b/projects/aquatint/generate_png/0.5_5.0_5-cycle_resize-aquatint.png differ diff --git a/projects/aquatint/generate_png/0.5_7.0_1-cycle_resize-aquatint.png b/projects/aquatint/generate_png/0.5_7.0_1-cycle_resize-aquatint.png new file mode 100644 index 0000000..e0496da Binary files /dev/null and b/projects/aquatint/generate_png/0.5_7.0_1-cycle_resize-aquatint.png differ diff --git a/projects/aquatint/generate_png/0.5_7.0_2-cycle_resize-aquatint.png b/projects/aquatint/generate_png/0.5_7.0_2-cycle_resize-aquatint.png new file mode 100644 index 0000000..796d85d Binary files /dev/null and b/projects/aquatint/generate_png/0.5_7.0_2-cycle_resize-aquatint.png differ diff --git a/projects/aquatint/generate_png/0.5_7.0_3-cycle_resize-aquatint.png b/projects/aquatint/generate_png/0.5_7.0_3-cycle_resize-aquatint.png new file mode 100644 index 0000000..5dfdb98 Binary files /dev/null and b/projects/aquatint/generate_png/0.5_7.0_3-cycle_resize-aquatint.png differ diff --git a/projects/aquatint/generate_png/0.5_7.0_4-cycle_resize-aquatint.png b/projects/aquatint/generate_png/0.5_7.0_4-cycle_resize-aquatint.png new file mode 100644 index 0000000..751aa66 Binary files /dev/null and b/projects/aquatint/generate_png/0.5_7.0_4-cycle_resize-aquatint.png differ diff --git a/projects/aquatint/generate_png/0.5_7.0_5-cycle_resize-aquatint.png b/projects/aquatint/generate_png/0.5_7.0_5-cycle_resize-aquatint.png new file mode 100644 index 0000000..b303ceb Binary files /dev/null and b/projects/aquatint/generate_png/0.5_7.0_5-cycle_resize-aquatint.png differ diff --git a/projects/aquatint/generate_png/0.5_9.0_1-cycle_resize-aquatint.png b/projects/aquatint/generate_png/0.5_9.0_1-cycle_resize-aquatint.png new file mode 100644 index 0000000..2f0d101 Binary files /dev/null and b/projects/aquatint/generate_png/0.5_9.0_1-cycle_resize-aquatint.png differ diff --git a/projects/aquatint/generate_png/0.5_9.0_2-cycle_resize-aquatint.png b/projects/aquatint/generate_png/0.5_9.0_2-cycle_resize-aquatint.png new file mode 100644 index 0000000..b8a0e90 Binary files /dev/null and b/projects/aquatint/generate_png/0.5_9.0_2-cycle_resize-aquatint.png differ diff --git a/projects/aquatint/generate_png/0.5_9.0_3-cycle_resize-aquatint.png b/projects/aquatint/generate_png/0.5_9.0_3-cycle_resize-aquatint.png new file mode 100644 index 0000000..a086e2b Binary files /dev/null and b/projects/aquatint/generate_png/0.5_9.0_3-cycle_resize-aquatint.png differ diff --git a/projects/aquatint/generate_png/0.5_9.0_4-cycle_resize-aquatint.png b/projects/aquatint/generate_png/0.5_9.0_4-cycle_resize-aquatint.png new file mode 100644 index 0000000..29369b0 Binary files /dev/null and b/projects/aquatint/generate_png/0.5_9.0_4-cycle_resize-aquatint.png differ diff --git a/projects/aquatint/generate_png/0.5_9.0_5-cycle_resize-aquatint.png b/projects/aquatint/generate_png/0.5_9.0_5-cycle_resize-aquatint.png new file mode 100644 index 0000000..50a4747 Binary files /dev/null and b/projects/aquatint/generate_png/0.5_9.0_5-cycle_resize-aquatint.png differ diff --git a/projects/aquatint/generate_png/0.7_1.0_1-cycle_resize-aquatint.png b/projects/aquatint/generate_png/0.7_1.0_1-cycle_resize-aquatint.png new file mode 100644 index 0000000..14ba7a3 Binary files /dev/null and b/projects/aquatint/generate_png/0.7_1.0_1-cycle_resize-aquatint.png differ diff --git a/projects/aquatint/generate_png/0.7_1.0_2-cycle_resize-aquatint.png b/projects/aquatint/generate_png/0.7_1.0_2-cycle_resize-aquatint.png new file mode 100644 index 0000000..cd18417 Binary files /dev/null and b/projects/aquatint/generate_png/0.7_1.0_2-cycle_resize-aquatint.png differ diff --git a/projects/aquatint/generate_png/0.7_1.0_3-cycle_resize-aquatint.png b/projects/aquatint/generate_png/0.7_1.0_3-cycle_resize-aquatint.png new file mode 100644 index 0000000..f291a8e Binary files /dev/null and b/projects/aquatint/generate_png/0.7_1.0_3-cycle_resize-aquatint.png differ diff --git a/projects/aquatint/generate_png/0.7_1.0_4-cycle_resize-aquatint.png b/projects/aquatint/generate_png/0.7_1.0_4-cycle_resize-aquatint.png new file mode 100644 index 0000000..8417e20 Binary files /dev/null and b/projects/aquatint/generate_png/0.7_1.0_4-cycle_resize-aquatint.png differ diff --git a/projects/aquatint/generate_png/0.7_1.0_5-cycle_resize-aquatint.png b/projects/aquatint/generate_png/0.7_1.0_5-cycle_resize-aquatint.png new file mode 100644 index 0000000..cb5055f Binary files /dev/null and b/projects/aquatint/generate_png/0.7_1.0_5-cycle_resize-aquatint.png differ diff --git a/projects/aquatint/generate_png/0.7_3.0_1-cycle_resize-aquatint.png b/projects/aquatint/generate_png/0.7_3.0_1-cycle_resize-aquatint.png new file mode 100644 index 0000000..68dc5f5 Binary files /dev/null and b/projects/aquatint/generate_png/0.7_3.0_1-cycle_resize-aquatint.png differ diff --git a/projects/aquatint/generate_png/0.7_3.0_2-cycle_resize-aquatint.png b/projects/aquatint/generate_png/0.7_3.0_2-cycle_resize-aquatint.png new file mode 100644 index 0000000..3a2bbdd Binary files /dev/null and b/projects/aquatint/generate_png/0.7_3.0_2-cycle_resize-aquatint.png differ diff --git a/projects/aquatint/generate_png/0.7_3.0_3-cycle_resize-aquatint.png b/projects/aquatint/generate_png/0.7_3.0_3-cycle_resize-aquatint.png new file mode 100644 index 0000000..60e0a34 Binary files /dev/null and b/projects/aquatint/generate_png/0.7_3.0_3-cycle_resize-aquatint.png differ diff --git a/projects/aquatint/generate_png/0.7_3.0_4-cycle_resize-aquatint.png b/projects/aquatint/generate_png/0.7_3.0_4-cycle_resize-aquatint.png new file mode 100644 index 0000000..04746a6 Binary files /dev/null and b/projects/aquatint/generate_png/0.7_3.0_4-cycle_resize-aquatint.png differ diff --git a/projects/aquatint/generate_png/0.7_3.0_5-cycle_resize-aquatint.png b/projects/aquatint/generate_png/0.7_3.0_5-cycle_resize-aquatint.png new file mode 100644 index 0000000..239e07b Binary files /dev/null and b/projects/aquatint/generate_png/0.7_3.0_5-cycle_resize-aquatint.png differ diff --git a/projects/aquatint/generate_png/0.7_5.0_1-cycle_resize-aquatint.png b/projects/aquatint/generate_png/0.7_5.0_1-cycle_resize-aquatint.png new file mode 100644 index 0000000..02f8eda Binary files /dev/null and b/projects/aquatint/generate_png/0.7_5.0_1-cycle_resize-aquatint.png differ diff --git a/projects/aquatint/generate_png/0.7_5.0_2-cycle_resize-aquatint.png b/projects/aquatint/generate_png/0.7_5.0_2-cycle_resize-aquatint.png new file mode 100644 index 0000000..cc89e85 Binary files /dev/null and b/projects/aquatint/generate_png/0.7_5.0_2-cycle_resize-aquatint.png differ diff --git a/projects/aquatint/generate_png/0.7_5.0_3-cycle_resize-aquatint.png b/projects/aquatint/generate_png/0.7_5.0_3-cycle_resize-aquatint.png new file mode 100644 index 0000000..467c3e1 Binary files /dev/null and b/projects/aquatint/generate_png/0.7_5.0_3-cycle_resize-aquatint.png differ diff --git a/projects/aquatint/generate_png/0.7_5.0_4-cycle_resize-aquatint.png b/projects/aquatint/generate_png/0.7_5.0_4-cycle_resize-aquatint.png new file mode 100644 index 0000000..8aac60b Binary files /dev/null and b/projects/aquatint/generate_png/0.7_5.0_4-cycle_resize-aquatint.png differ diff --git a/projects/aquatint/generate_png/0.7_5.0_5-cycle_resize-aquatint.png b/projects/aquatint/generate_png/0.7_5.0_5-cycle_resize-aquatint.png new file mode 100644 index 0000000..d263cd1 Binary files /dev/null and b/projects/aquatint/generate_png/0.7_5.0_5-cycle_resize-aquatint.png differ diff --git a/projects/aquatint/generate_png/0.7_7.0_1-cycle_resize-aquatint.png b/projects/aquatint/generate_png/0.7_7.0_1-cycle_resize-aquatint.png new file mode 100644 index 0000000..e67b21a Binary files /dev/null and b/projects/aquatint/generate_png/0.7_7.0_1-cycle_resize-aquatint.png differ diff --git a/projects/aquatint/generate_png/0.7_7.0_2-cycle_resize-aquatint.png b/projects/aquatint/generate_png/0.7_7.0_2-cycle_resize-aquatint.png new file mode 100644 index 0000000..eb9baf8 Binary files /dev/null and b/projects/aquatint/generate_png/0.7_7.0_2-cycle_resize-aquatint.png differ diff --git a/projects/aquatint/generate_png/0.7_7.0_3-cycle_resize-aquatint.png b/projects/aquatint/generate_png/0.7_7.0_3-cycle_resize-aquatint.png new file mode 100644 index 0000000..17614c7 Binary files /dev/null and b/projects/aquatint/generate_png/0.7_7.0_3-cycle_resize-aquatint.png differ diff --git a/projects/aquatint/generate_png/0.7_7.0_4-cycle_resize-aquatint.png b/projects/aquatint/generate_png/0.7_7.0_4-cycle_resize-aquatint.png new file mode 100644 index 0000000..604be85 Binary files /dev/null and b/projects/aquatint/generate_png/0.7_7.0_4-cycle_resize-aquatint.png differ diff --git a/projects/aquatint/generate_png/0.7_7.0_5-cycle_resize-aquatint.png b/projects/aquatint/generate_png/0.7_7.0_5-cycle_resize-aquatint.png new file mode 100644 index 0000000..ce8f798 Binary files /dev/null and b/projects/aquatint/generate_png/0.7_7.0_5-cycle_resize-aquatint.png differ diff --git a/projects/aquatint/generate_png/0.7_9.0_1-cycle_resize-aquatint.png b/projects/aquatint/generate_png/0.7_9.0_1-cycle_resize-aquatint.png new file mode 100644 index 0000000..407baf3 Binary files /dev/null and b/projects/aquatint/generate_png/0.7_9.0_1-cycle_resize-aquatint.png differ diff --git a/projects/aquatint/generate_png/0.7_9.0_2-cycle_resize-aquatint.png b/projects/aquatint/generate_png/0.7_9.0_2-cycle_resize-aquatint.png new file mode 100644 index 0000000..c623942 Binary files /dev/null and b/projects/aquatint/generate_png/0.7_9.0_2-cycle_resize-aquatint.png differ diff --git a/projects/aquatint/generate_png/0.7_9.0_3-cycle_resize-aquatint.png b/projects/aquatint/generate_png/0.7_9.0_3-cycle_resize-aquatint.png new file mode 100644 index 0000000..4fbb47f Binary files /dev/null and b/projects/aquatint/generate_png/0.7_9.0_3-cycle_resize-aquatint.png differ diff --git a/projects/aquatint/generate_png/0.7_9.0_4-cycle_resize-aquatint.png b/projects/aquatint/generate_png/0.7_9.0_4-cycle_resize-aquatint.png new file mode 100644 index 0000000..dafc79d Binary files /dev/null and b/projects/aquatint/generate_png/0.7_9.0_4-cycle_resize-aquatint.png differ diff --git a/projects/aquatint/generate_png/0.7_9.0_5-cycle_resize-aquatint.png b/projects/aquatint/generate_png/0.7_9.0_5-cycle_resize-aquatint.png new file mode 100644 index 0000000..a2e0484 Binary files /dev/null and b/projects/aquatint/generate_png/0.7_9.0_5-cycle_resize-aquatint.png differ diff --git a/projects/aquatint/generate_png/0.9_1.0_1-cycle_resize-aquatint.png b/projects/aquatint/generate_png/0.9_1.0_1-cycle_resize-aquatint.png new file mode 100644 index 0000000..03de41f Binary files /dev/null and b/projects/aquatint/generate_png/0.9_1.0_1-cycle_resize-aquatint.png differ diff --git a/projects/aquatint/generate_png/0.9_1.0_2-cycle_resize-aquatint.png b/projects/aquatint/generate_png/0.9_1.0_2-cycle_resize-aquatint.png new file mode 100644 index 0000000..1374c9a Binary files /dev/null and b/projects/aquatint/generate_png/0.9_1.0_2-cycle_resize-aquatint.png differ diff --git a/projects/aquatint/generate_png/0.9_1.0_3-cycle_resize-aquatint.png b/projects/aquatint/generate_png/0.9_1.0_3-cycle_resize-aquatint.png new file mode 100644 index 0000000..dc569a4 Binary files /dev/null and b/projects/aquatint/generate_png/0.9_1.0_3-cycle_resize-aquatint.png differ diff --git a/projects/aquatint/generate_png/0.9_1.0_4-cycle_resize-aquatint.png b/projects/aquatint/generate_png/0.9_1.0_4-cycle_resize-aquatint.png new file mode 100644 index 0000000..4f6c200 Binary files /dev/null and b/projects/aquatint/generate_png/0.9_1.0_4-cycle_resize-aquatint.png differ diff --git a/projects/aquatint/generate_png/0.9_1.0_5-cycle_resize-aquatint.png b/projects/aquatint/generate_png/0.9_1.0_5-cycle_resize-aquatint.png new file mode 100644 index 0000000..a576148 Binary files /dev/null and b/projects/aquatint/generate_png/0.9_1.0_5-cycle_resize-aquatint.png differ diff --git a/projects/aquatint/generate_png/0.9_3.0_1-cycle_resize-aquatint.png b/projects/aquatint/generate_png/0.9_3.0_1-cycle_resize-aquatint.png new file mode 100644 index 0000000..0c7367b Binary files /dev/null and b/projects/aquatint/generate_png/0.9_3.0_1-cycle_resize-aquatint.png differ diff --git a/projects/aquatint/generate_png/0.9_3.0_2-cycle_resize-aquatint.png b/projects/aquatint/generate_png/0.9_3.0_2-cycle_resize-aquatint.png new file mode 100644 index 0000000..a845a52 Binary files /dev/null and b/projects/aquatint/generate_png/0.9_3.0_2-cycle_resize-aquatint.png differ diff --git a/projects/aquatint/generate_png/0.9_3.0_3-cycle_resize-aquatint.png b/projects/aquatint/generate_png/0.9_3.0_3-cycle_resize-aquatint.png new file mode 100644 index 0000000..00f0516 Binary files /dev/null and b/projects/aquatint/generate_png/0.9_3.0_3-cycle_resize-aquatint.png differ diff --git a/projects/aquatint/generate_png/0.9_3.0_4-cycle_resize-aquatint.png b/projects/aquatint/generate_png/0.9_3.0_4-cycle_resize-aquatint.png new file mode 100644 index 0000000..8a65471 Binary files /dev/null and b/projects/aquatint/generate_png/0.9_3.0_4-cycle_resize-aquatint.png differ diff --git a/projects/aquatint/generate_png/0.9_3.0_5-cycle_resize-aquatint.png b/projects/aquatint/generate_png/0.9_3.0_5-cycle_resize-aquatint.png new file mode 100644 index 0000000..755b12c Binary files /dev/null and b/projects/aquatint/generate_png/0.9_3.0_5-cycle_resize-aquatint.png differ diff --git a/projects/aquatint/generate_png/0.9_5.0_1-cycle_resize-aquatint.png b/projects/aquatint/generate_png/0.9_5.0_1-cycle_resize-aquatint.png new file mode 100644 index 0000000..a16886d Binary files /dev/null and b/projects/aquatint/generate_png/0.9_5.0_1-cycle_resize-aquatint.png differ diff --git a/projects/aquatint/generate_png/0.9_5.0_2-cycle_resize-aquatint.png b/projects/aquatint/generate_png/0.9_5.0_2-cycle_resize-aquatint.png new file mode 100644 index 0000000..f01f53b Binary files /dev/null and b/projects/aquatint/generate_png/0.9_5.0_2-cycle_resize-aquatint.png differ diff --git a/projects/aquatint/generate_png/0.9_5.0_3-cycle_resize-aquatint.png b/projects/aquatint/generate_png/0.9_5.0_3-cycle_resize-aquatint.png new file mode 100644 index 0000000..c72b6c0 Binary files /dev/null and b/projects/aquatint/generate_png/0.9_5.0_3-cycle_resize-aquatint.png differ diff --git a/projects/aquatint/generate_png/0.9_5.0_4-cycle_resize-aquatint.png b/projects/aquatint/generate_png/0.9_5.0_4-cycle_resize-aquatint.png new file mode 100644 index 0000000..9620ee9 Binary files /dev/null and b/projects/aquatint/generate_png/0.9_5.0_4-cycle_resize-aquatint.png differ diff --git a/projects/aquatint/generate_png/0.9_5.0_5-cycle_resize-aquatint.png b/projects/aquatint/generate_png/0.9_5.0_5-cycle_resize-aquatint.png new file mode 100644 index 0000000..da51ab5 Binary files /dev/null and b/projects/aquatint/generate_png/0.9_5.0_5-cycle_resize-aquatint.png differ diff --git a/projects/aquatint/generate_png/0.9_7.0_1-cycle_resize-aquatint.png b/projects/aquatint/generate_png/0.9_7.0_1-cycle_resize-aquatint.png new file mode 100644 index 0000000..31a1ea1 Binary files /dev/null and b/projects/aquatint/generate_png/0.9_7.0_1-cycle_resize-aquatint.png differ diff --git a/projects/aquatint/generate_png/0.9_7.0_2-cycle_resize-aquatint.png b/projects/aquatint/generate_png/0.9_7.0_2-cycle_resize-aquatint.png new file mode 100644 index 0000000..edfd3d9 Binary files /dev/null and b/projects/aquatint/generate_png/0.9_7.0_2-cycle_resize-aquatint.png differ diff --git a/projects/aquatint/generate_png/0.9_7.0_3-cycle_resize-aquatint.png b/projects/aquatint/generate_png/0.9_7.0_3-cycle_resize-aquatint.png new file mode 100644 index 0000000..85cf5bf Binary files /dev/null and b/projects/aquatint/generate_png/0.9_7.0_3-cycle_resize-aquatint.png differ diff --git a/projects/aquatint/generate_png/0.9_7.0_4-cycle_resize-aquatint.png b/projects/aquatint/generate_png/0.9_7.0_4-cycle_resize-aquatint.png new file mode 100644 index 0000000..e3d5eaf Binary files /dev/null and b/projects/aquatint/generate_png/0.9_7.0_4-cycle_resize-aquatint.png differ diff --git a/projects/aquatint/generate_png/0.9_7.0_5-cycle_resize-aquatint.png b/projects/aquatint/generate_png/0.9_7.0_5-cycle_resize-aquatint.png new file mode 100644 index 0000000..3efa8e8 Binary files /dev/null and b/projects/aquatint/generate_png/0.9_7.0_5-cycle_resize-aquatint.png differ diff --git a/projects/aquatint/generate_png/0.9_9.0_1-cycle_resize-aquatint.png b/projects/aquatint/generate_png/0.9_9.0_1-cycle_resize-aquatint.png new file mode 100644 index 0000000..002846e Binary files /dev/null and b/projects/aquatint/generate_png/0.9_9.0_1-cycle_resize-aquatint.png differ diff --git a/projects/aquatint/generate_png/0.9_9.0_2-cycle_resize-aquatint.png b/projects/aquatint/generate_png/0.9_9.0_2-cycle_resize-aquatint.png new file mode 100644 index 0000000..fd89408 Binary files /dev/null and b/projects/aquatint/generate_png/0.9_9.0_2-cycle_resize-aquatint.png differ diff --git a/projects/aquatint/generate_png/0.9_9.0_3-cycle_resize-aquatint.png b/projects/aquatint/generate_png/0.9_9.0_3-cycle_resize-aquatint.png new file mode 100644 index 0000000..e496519 Binary files /dev/null and b/projects/aquatint/generate_png/0.9_9.0_3-cycle_resize-aquatint.png differ diff --git a/projects/aquatint/generate_png/0.9_9.0_4-cycle_resize-aquatint.png b/projects/aquatint/generate_png/0.9_9.0_4-cycle_resize-aquatint.png new file mode 100644 index 0000000..4876af9 Binary files /dev/null and b/projects/aquatint/generate_png/0.9_9.0_4-cycle_resize-aquatint.png differ diff --git a/projects/aquatint/generate_png/0.9_9.0_5-cycle_resize-aquatint.png b/projects/aquatint/generate_png/0.9_9.0_5-cycle_resize-aquatint.png new file mode 100644 index 0000000..4f1d2a8 Binary files /dev/null and b/projects/aquatint/generate_png/0.9_9.0_5-cycle_resize-aquatint.png differ diff --git a/projects/aquatint/index.php b/projects/aquatint/index.php new file mode 100644 index 0000000..7bc8600 --- /dev/null +++ b/projects/aquatint/index.php @@ -0,0 +1,743 @@ + +
+
+
+
+
+

Preface

+
+

+ An appreciated facet of my life are the various happenstance connections I've made with a wide range of individuals. Despite having studied computer science for so long, most friendships and acquaintances were formed outside of the domain. This has been a strong theme in many of my writings, which often revolve around experiencing moments with this ever growing assorted group of individuals. During my graduate years, extra effort was spent casting a wider net. +

+ +

+ During a time, the casting of the social net would land upon the university's bouldering wall. One thing that impressed me about the wall were the people who attended it. If one were to walk through the recreation center, a glance to the right would show isolated individuals trying their best to ignore each other. This awkwardness would exist while taking silent turns having at equipment made for individual use. A glance to the left was stark; a climbing wall with a vibrant splay holds and groups of people accompanied by a positive murmur of discussion. Highlights of discussion include group strategy for approaching a given route and cheerful encouragement of one who was about finish a particular one. +

+ +

+ Discussion would typically occur amongst pockets of individuals who were college aged - early to mid 20s. Fortunately, this wasn't a rule. Being a non-traditional student in their early 30s, it was easy for me to integrate. Considering social norms, I could understand how it would be a circumstance a bit daunting to step into - especially if one were to look at the opposite side of the rec center. For this reason, I was sure to put forth effort to reciprocate the ease of experiencing the social perks associated with the climbing wall. +

+ +

+ One such individual met through these circumstances was someone who seemed to be from a generation or two beyond me; an older gentleman who was learning the art of bouldering whilst enjoying the benefits of both the body-weight exercise and the associated skills of body awareness. Conversation was spurred by discussing a how to climb particular route. +

+ +

+ Turns out this individual was a physics professor. Continued discussion would reveal that he was looking for a software engineer to make a user interface for an image processing script he had finished making. Providence would have it that I'm a computer scientist with a good amount of experience both in software development with user interface implementation. +

+ +

+ The script would reprocess an image to one that had the appearance of being produced using the aquatint printmaking technique. The product would be the same image with a more old-time aesthetic to it; the image would be more grainy with a warm temperature reminiscent of early print making. +

+
+

+ The professor's script, through the web app I would end up building, would be used to produce three pieces of building art for the Physics Research Center at the University of Chicago. It's fitting that a serendipitous connection at a climbing wall would manifest on a wall at an educational institution elsewhere. +

+ +

+ The main body of this page discusses three primary facets of the web app that I find interesting: handling of the original script, upload security, and supplying feedback to a user. +

+

To use the web app, follow this link: Aquatint Image Processor +


+
+
+

Web Development: Aquatint Image Processor

+
+

Interpretation of the original Python script

+

+ It seems to be typical for other academic domains to produce code within in a Jupyter notebook. The advantage here is it allows the output of running code to be interspersed within the script itself; contrary to being produced in a mutually exclusive environment, (such as stdout or some log file). This makes sense given the context of a computer scientist as someone who is moulded in such a manner as to think a like a machine - one that is able to easily parse through such output. +

+

+ An advantage to being provided a script within a Jupyter notebook is that it's easier to discern the sections the developer finds important. The script received from Professor Meurice took advantage of matplotlib to display the reprocessed images. Within the notebook, each significant step was capped by a display of the reprocessed image as-in progress. For example, the first significant step of the algorithm was to apply a grey-scale to each individual pixel. A given image would be read in using the imageio library then processed as such: +

+ +
+im2 = imageio.imread(filename)
+Nix=im2.shape[0]
+Niy=im2.shape[1]
+grayimage=np.zeros([Nix,Niy])
+for i in range(0,Nix):
+        for j in range(0,Niy):
+            blueComponent = im2[i][j][0]
+            greenComponent = im2[i][j][1]
+            redComponent = im2[i][j][2]
+            grayValue = 0.07 * blueComponent + 0.72 * greenComponent + 0.21 * redComponent
+            grayimage[i][j] = grayValue
+            pass
+dsqin=1-grayimage/255.0
+hsimage=plt.imshow(dsqin,cmap='Greys',aspect=1,interpolation='none')
+plt.colorbar(hsimage)
+plt.show(hsimage)
+                            
+
+

+ An import of matplotlib.pyplot as plt preceded this block. Knowing this, take note of the usage of pyplot's show method near the end of the code snippet. +

+

+ The set of images produced in the notebook, along with the various textual/comment blocks, made it easy to discover a set of variables that can be set by a user to tune the appearance of an image that has been processed by the Aquatint script. These variables are a greycut, temperature, and the amount of sweeps to be applied. +

+

+ Greycut was well defined within the documentation provided in the notebook: +

+ The output image will have only black and white pixels so it is a good exercise to convert the original one to this form. Provide a greycut (contrast) number between 0 and 1. This converts the grey pixels into black (above greycut) and white (below greycut). +
+

+

+ The other values weren't clearly defined here. Since the product of the script is of visual nature, this provided an opportunity to produce something which can visually inform the variance of these values can produce. This would require a combinatoric production of the same image using a valid range of values. I refactored the code from the Jupyter notebook into an external python file and ran the following bash script: +

+ +
+sweeps=1
+while [ $sweeps -le 5 ]
+do
+    greycut=1
+    while [ $greycut -le 9 ]
+    do
+        greycut_float=`bc <<< "scale=2; ${greycut}/10"`
+        temperature=1
+        while [ $temperature -le 9 ]
+        do
+            `python3 "aquatintScript.py" "cycle.png" $greycut_float $temperature $sweeps`
+            temperature=`expr $temperature + 2`
+        done
+        greycut=`expr $greycut + 2`
+    done
+    sweeps=`expr $sweeps + 1`
+done
+                            
+
+

+ The ranges of the loops were restricted to keep a reasonable runtime of this process. A lot of the image processing is dependent on the amount of pixels contained in a given image, so the input image size was kept low enough whilst ensuring enough pixels were available to be able to gauge the differences the other input parameters bring to the process. +

+

+ One-hundred and twenty-five images in total were produced on account of running the bash script. Those who have Javascript enabled for this page can view the effect of each variable, (with respect to the values set for the others), within the following figure: +

+ +

+ The view provided here helps establish the set of user controls needed to actually implement the web app. +

+
+

The Web App

+

+ What's been discussed so far has involved Jupyter notebooks, Python, and Bash scripting. These are technologies not often associated with the core of web development. A utilitarian product needed to be produced with a hint of time constraint. Thus, I opted for using the Bootstrap framework to handle the front-end styling. The view provided by the previous figure implies that Javascript is also at play for the web app. Finally, an engine was needed to process the uploads. +

+

+ My experience using Python as a web server back-end is minimal. It is likely that using Python here would lend well to the situation considering the Aquatint scripts were written in the language. At the time, I had no experience handling file uploads. The back-end most familiar to me was PHP. Thus I decided to take the opportunity to shore up that gap in my experience with the language. +

+

Upload Security

+

+ The upload form consists of three sliders and a file upload box. It needs to be ensured that the selections for the sliders are numbers that reside in a specific range. It also needs to be ensured that the file being uploaded is indeed an image with an applicable type. It is not sufficient to rely on the constraints set forth by the front-end in general. Any individual can change the arbitrary restrictions enforced by these mechanisms through either the element inspector or by circumventing a browser environment by means of an http post with some headless program. +

+

+ The assurance of valid slider input is trivial through the back-end logic. Each slider has an id associated with the control. On submission post, PHP will check whether the posted values are numeric and whether they exist in the expected range. Assurance that a given file is indeed an image is another story. +

+

+ An individual who is used to operating in a computing environment that abstracts away file information may think it's sufficient to simply check the extension as it is given within the file name. Any power user, (or any user of a linux distribution), will know this is lacking. +

+

+ The key methods used to ensure image upload are basename, pathinfo, exif_imagetype, and image_type_to_mime_type. +

+

+ The basename function is used to truncate any attempts to submit a filename that tries to traverse the server's file system. Consider a post variable with the identifier of uploadImage: +

+ +
+$origin_file = $target_dir . basename($_FILES['uploadImage']['name']);
+                            
+
+

+ The pathinfo function is used to isolate the extension of the filename string. Contrary to sentiment posed in paragraphs prior, this is still worthy of checking to provide useful feedback for those who are making sincere attempts at using the application. +

+ +
+$imageFileType = strtolower(pathinfo($target_dir . $origin_file,PATHINFO_EXTENSION));
+                            
+
+

+ The exif_imagetype function provides a means within PHP to drill down to byte-level in order to validate file structure. This is validated further by image_type_to_mime_type which makes use of Apache's mime_magic module to make the same assurance. +

+ +
+$target_file = $target_dir . $file_name  . "." . $imageFileType;
+$check = exif_imagetype($_FILES['uploadImage']['tmp_name']);
+$mimeType = image_type_to_mime_type($check);
+                            
+
+

+ These functions are used in conjunction with safe system administration procedure. Within the Linux environment, proper(ly strict) permissions are granted to the upload folder while Apache configuration restricts file-type access to the folder to only allow access to what is relevant. +

+

+ Slider and file selection input has been validated. A keen observer will discover a hidden input form. A decision was made to assign a random name to the uploaded file as it is placed into the upload folder. This is an attempt to decouple any malicious attempts at file system traversal and malicious script execution vectors that the previous measures may have missed. The back-end generates this random string as the template for the submission page is built. +

+ +
+$file_name = '';
+for($i = 0; $i <= rand(10,20); $i++){
+    $new_ord = rand(87,122);
+    if($new_ord >= 97){
+        $file_name = $file_name . chr($new_ord);
+    }else{
+        $file_name = $file_name . $new_ord;
+    }
+}
+                            
+
+

+ A hidden input form was opted instead of using a query string to embed this information. The reason for this was to keep the submission url clean. Another reason involves the necessity to know the filename before any submission is made! This relates to giving the user feedback of progress once they've made a submission. +

+
+
+ + +
+$target_dir = 'uploads/';
+$uploadOk = 1;
+
+//Validate string form controls:
+if(isset($_POST['hidden_file_name']) && isset($_FILES['uploadImage']['name'])){
+    if(strlen($_FILES['uploadImage']['name']) <= 0 || strlen($_POST['hidden_file_name']) <= 0){
+        $uploadOk = 0;
+    }else{
+        $preg_result = preg_match("/\A([a-z0-9]+)\z/",$_POST['hidden_file_name']);
+        if($preg_result == 0){
+            echo '<div class="alert alert-danger"><strong>Warning!</strong> Please refrain from altering hidden form.</div>';
+            $uploadOk = 0;
+        }
+    }
+
+    if($uploadOk == 1){
+        $file_name = $_POST['hidden_file_name'];
+        $origin_file = $target_dir . basename($_FILES['uploadImage']['name']);
+        $imageFileType = strtolower(pathinfo($target_dir . $origin_file,PATHINFO_EXTENSION));
+        $target_file = $target_dir . $file_name  . "." . $imageFileType;
+        $check = exif_imagetype($_FILES['uploadImage']['tmp_name']);
+        $mimeType = image_type_to_mime_type($check);
+    }
+}else{
+    $uploadOk = 0;
+}
+                                
+
+ + +
+//Validate filetype:
+if($uploadOk == 1 ){
+    if($check !== false){
+        //$uploadOk = 1;
+        if($_FILES['uploadImage']['size'] > 1048576){
+            echo '<div class="alert alert-danger"><strong>Warning!</strong> Sorry, your file is too large.</div>';
+            $uploadOk = 0;
+        }
+    }else{
+        echo '<div class="alert alert-danger"><strong>Warning!</strong> File is not an image.</div>';
+        $uploadOk = 0;
+    }
+
+    if( ($imageFileType != 'jpg') && ($imageFileType != 'png') && ($imageFileType != 'jpeg') && ($imageFileType != 'gif') ){
+        echo '<div class="alert alert-danger"><strong>Warning!</strong> Only jpg, jpeg, png, and gif files are allowed.</div>';
+        $uploadOk = 0;
+    }else
+
+    if( ($mimeType != 'image/gif') && ($mimeType != 'image/jpeg') && ($mimeType != 'image/png') ){
+        echo '<div class="alert alert-danger"><strong>Warning!</strong> Only jpg, jpeg, png, and gif files are allowed.</div>';
+        $uploadOk = 0;
+    }
+
+}
+                                
+
+ + +
+//Validate numeric form controls:
+if($uploadOk == 1){
+
+    //Greycut:
+    try{
+        $greycut = (float) $_POST['greycut'];
+        if($greycut < 0 || $greycut > 1){
+            echo '<div class="alert alert-danger"><strong>Warning!</strong> Please refrain from changing form values with the element inspector.</div>';
+            $uploadOk = 0;
+        }else{
+            $greycut = (string) $greycut;
+        }
+    }catch (Exception $ex){
+        echo '<div class="alert alert-danger"><strong>Warning!</strong> Please refrain from changing form values with the element inspector.</div>';
+        $uploadOk = 0;
+    }
+
+    //Temperature:
+    try{
+        $temperature = (float) $_POST['temperature'];
+        if($temperature < 0.1 || $temperature > 10){
+            echo '<div class="alert alert-danger"><strong>Warning!</strong> Please refrain from changing form values with the element inspector.</div>';
+            $uploadOk = 0;
+        }else{
+            $temperature = (string) $temperature;
+        }
+    }catch (Exception $ex){
+        echo '<div class="alert alert-danger"><strong>Warning!</strong> Please refrain from changing form values with the element inspector.</div>';
+        $uploadOk = 0;
+    }
+
+    //Total Sweeps:
+    try{
+        $totalsweeps = (float) $_POST['totalsweeps'];
+        if($totalsweeps < 1 || $totalsweeps > 10){
+            echo '<div class="alert alert-danger"><strong>Warning!</strong> Please refrain from changing form values with the element inspector.</div>';
+            $uploadOk = 0;
+        }else{
+            $totalsweeps = (string) $totalsweeps;
+        }
+    }catch (Exception $ex){
+        echo '<div class="alert alert-danger"><strong>Warning!</strong> Please refrain from changing form values with the element inspector.</div>';
+        $uploadOk = 0;
+    }
+
+}
+                                
+
+
+ Expand the headers above to see the relevant code-block for each tier of validation. +
+
+
+ +

Providing feedback

+

+ Once the validation check passes, move_uploaded_files is called such that the receiving file is given the random name generated within the hidden input form. This new file's name will be used in concatenating a string to use for an exec call: +

+ +
+$script = 'python3 aquatintScript.py "'.$target_file.'" '.$greycut.' '.$temperature.' '.$totalsweeps;
+exec($script,$output,$result);
+                            
+
+

+ All the user has to do now is wait for the Aquatint script to complete; And to wait they shall! Dependent on resolution, it may take a good amount of time for an image to process. The algorithm itself is at least quadratic in runtime with respect to the amount of pixels an image has. This is said ignoring unknown runtime of any library method calls embedded within the iteration of these pixels. This potential for slowness is compounded by the measly 1 gigabyte of ram and 2Ghz single core CPU that my LAMP server has access to. For these reasons, it is necessary to let the user know how far along the Aquatint conversion process is. +

+

+ Some would rightfully claim that using exec is a code smell. It took a bit to convince myself that the steps described in the previous section are adequate to scrub input of malicious intent. In terms of running the Aquatint script, a string is being passed that has been stripped of any POSIX compliant command. The same can be said in terms of the Python code embedded within the script itself. +

+

+ Initial intuition for supplying a user feedback on progress was to leverage print statements in the Aquatint script to expose progress through stdout. These print statements would occur upon the completion of significant steps within the script, such as when grey-scaling is finished. The problem here is that the exec call is an atomic operation in the eyes of PHP. One could skirt around this by instead of leveraging a call to proc_open, but programming intuition has me believing that this is an even greater code smell than using exec in the first place! +

+

+ The propensity to lean towards leveraging stdout is likely related to the opening discussion of this article; it is influenced by experiences of studying computer science which has been heavily guided by interpretation of the stdout environment. Program state can still be relayed through other mechanisms. The solution to this problem was leveraging the ability to write to file instead of standard output. +

+

+ The decision to write state to some file, which can then be read by the application, aligns more with the intuit of a web developer. Implementation of a Restful API has been the cornerstone of one significant project throughout my studies. This was by means of a PHP project whilst attending community college. The span of time since then once again warranted practice for the sake of refilling a gap of knowledge. +

+

+ The process is as follows: When the submission form is loaded, the back-end automatically generates a filename. This has already been discussed in the section prior. It's necessary to know the file-name before the submission is posted to the server. Write this string to the hidden form and write it to a JSON file that only the back-end may access. This file will serve as a map for an API to use. +

+ +
+$file_name = '';
+for($i = 0; $i <= rand(10,20); $i++){
+    $new_ord = rand(87,122);
+    if($new_ord >= 97){
+        $file_name = $file_name . chr($new_ord);
+    }else{
+        $file_name = $file_name . $new_ord;
+    }
+}
+$json = file_get_contents("map.json");
+$json_data = json_decode($json,true);
+$json_data[$file_name] = array("status" => 0, "time" => time());
+file_put_contents("map.json",json_encode($json_data));
+                            
+
+

+ Once the submit button is pressed, PHP will only serve the resulting page once all the program statements are completed. This includes the execution of the exec statement. This necessitates the need to have a filename pre-generated. Thus, a Javascript event listener is added to the submission button as a means know when it is pressed. This event listener must know the string that represents the filename. +

+ +
+<div class='form-group'>
+    <input type='submit' value='Upload Image' name='submit' onclick="submit_process('<?php echo $file_name; ?>');" />
+    <p id='wait' style='visibility:hidden;'><b>Please wait...</b></p>
+</div>
+                            
+
+

+ Once submit is pressed, toggle a visual prompt for the user that they should wait. Trigger an interval loop which runs an AJAX query to the server's API to query for the status of an upload based on the randomly generated filename. This query occurs once every three seconds. +

+ +
+submit_process = function(filestring){
+    document.getElementById("wait").style.visibility = "visible";
+    setInterval(function(){
+        query("<?php echo $file_name;?>");
+    },3000);
+}
+                            
+
+

+ Once submit is pressed, the Aquatint script will be run via the PHP script's exec command. Within the Aquatint script, create a JSON file that resides in the uploads folder. This JSON file is prefixed with the name of the file to be written. It will contain entry points to indicate when a certain step of the algorithm is completed. It will also have a spot to indicate the progress of the current step being executed. +

+ +
+status_dict = {"origin":False,"greycut":False,"temperature":False,"sweeps":dict(),"finished":0,"total":3+totalsweeps,"progress":0}
+
+for i in range(0,totalsweeps):
+    status_dict['sweeps']["sweep"+str(i)] = False
+
+write_to_json(filename.split('.')[-2]+'-status.json',json.dumps(status_dict))
+                            
+
+

+ As the Aquatint program is running, it will write to the file once a significant step has been completed. Within each significant step, (usually embedded in an outer-loop), it will write a value indicating the percentage of the step completed. This will only be written for every 3% completed to save the amount of times the progress is written to this file. +

+ +
+# !!! This is the same loop described earlier in the reading.
+#     It has been expanded to allow progress reporting.
+#     Note that this is only a subsection of the Aquatint script.
+
+im2 = imageio.imread(filename)
+Nix=im2.shape[0]
+Niy=im2.shape[1]
+grayimage=np.zeros([Nix,Niy])
+
+rewrite_switch = True
+for i in range(0,Nix):
+        for j in range(0,Niy):
+            blueComponent = im2[i][j][0]
+            greenComponent = im2[i][j][1]
+            redComponent = im2[i][j][2]
+            grayValue = 0.07 * blueComponent + 0.72 * greenComponent + 0.21 * redComponent
+            grayimage[i][j] = grayValue
+            pass
+        status_dict['progress'] = i / Nix
+        if round((i * 100) / Nix) % 3 == 0:
+            if rewrite_switch == True:
+                write_to_json(filename.split('.')[-2]+'-status.json',json.dumps(status_dict))
+                rewrite_switch = False
+        else:
+            rewrite_switch = True
+status_dict["progress"] = 0
+
+dsqin=1-grayimage/255.0
+hsimage=plt.imshow(dsqin,cmap='Greys',aspect=1,interpolation='none')
+#cb = plt.colorbar(hsimage)
+plt.savefig(filename.split('.')[-2]+'-origin.jpg',dpi=300)
+
+status_dict["origin"] = True
+status_dict['finished'] += 1
+write_to_json(filename.split('.')[-2]+'-status.json',json.dumps(status_dict))
+                            
+
+

+ As the user's browser is waiting for a response of the post, the AJAX method will be querying the API and receiving new state written by the Python script. The AJAX call will work through a set of states which represent the completion of a certain step of the Aquatint conversion process. Percentage of a given step will be reported, and once a step is completed, a progress bar will be filled in. +

+ +
+query = function(filestring){
+    var xmlhttp = new XMLHttpRequest();
+    xmlhttp.onreadystatechange = function(){
+        if (this.readyState == 4 && this.status == 200){
+            result = this.responseText;
+            finished = JSON.parse(result)[0];
+            total = JSON.parse(result)[1];
+            ratio = (finished/total) * 100;
+            new_width = '' + ratio + "%";
+            document.getElementById('progress-bar').style.width = new_width;
+            progress_text_object = document.getElementById('progress-text');
+            if(finished == 0){
+                progress = JSON.parse(result)[2];
+                progress_text_object.innerHTML = 'Step 1/'+total+'; Resizing and applying greyscale to original image: ' + Math.ceil(progress * 100) + '% complete.';
+            }else if(finished == 1){
+                progress_text_object.innerHTML = 'Step 2/'+total+'; Original image resized - Applying greycut...';
+            }else if(finished == 2){
+                progress_text_object.innerHTML = 'Step 3/'+total+'; Greycut applied - Applying temperature...';
+            }else if(finished == 3){
+                progress = JSON.parse(result)[2];
+                progress_text_object.innerHTML = 'Step 4/'+total+'; Greycut and Temperature applied - Applying first sweep: ' + Math.ceil(progress * 100) + '% complete.';
+            }else if(finished >= 4){
+                progress = JSON.parse(result)[2];
+                progress_text_object.innerHTML = 'Step '+(finished+1)+'/'+total+'; Applying sweep: ' + Math.ceil(progress * 100) + '% complete.';
+            }
+        }
+    };
+    xmlhttp.open("GET","status.php?id="+filestring,true);
+    xmlhttp.send();
+}
+                            
+
+ +
+
+<div class="progress">
+    <div id="progress-bar" class="progress-bar progress-bar-striped progress-bar-animated bg-info" role="progressbar" style="width: 0%" >
+    </div>
+</div>
+<div id='progress-text' class='alert alert-light'>
+</div>
+                            
+
+

+ For every query to the API, the PHP script will then look up the relevant JSON status file and report the relevant status. The AJAX query will make use of the returned information to fill in the relevant HTML elements to give the user a sense of progress. +

+ +
+<?php
+$not_ready = json_encode(array(0,0));
+
+if(isset($_GET['id'])){
+    $result = preg_match("/\A([a-z0-9]+)\z/",$_GET['id']);
+    $valid = 0;
+    if($result == 1){
+        $json = file_get_contents("map.json");
+        $json_data = json_decode($json,true);
+        if(isset($json_data[$_GET['id']])){
+            $valid = 1;
+        }else{
+            echo $not_ready;
+        }
+    }else{
+        echo $not_ready;
+    }
+}else{
+    echo $not_ready;
+}
+
+if($valid == 1){
+    try{
+        $json = file_get_contents("uploads/".$_GET['id']."-status.json");
+        $json_data = json_decode($json,true);
+        $return = array($json_data["finished"],$json_data["total"],$json_data['progress']);
+        echo json_encode($return);
+    }catch(Exception $ex){
+        echo $not_ready;
+    }
+}
+?>
+                            
+
+

+ To clean-up things on the server-side, every time the submission page is accessed, the back end will take a look at the mapping json file and keep all the items that are less than 30 minutes old. A cronjob also works on the server and deletes all files within the uploads folder that meets this criteria as well. +

+

Finished product

+

+ The rest of the web app isn't worth elaborating upon. If a reader has been able to track what's been said thus far, the remaining details are both trivial and intuitive. +

+

+ A working version of the web app can be viewed here: Aquatint Image Processor. To label this as a finished project is a mischaracterization. Future work includes a cancel button that allows a user to discontinue waiting for an image to process within an arbitrary sweep stage which could then forward the user to a page that displays the most recent applied sweep. +

+

+ Another potential vector of future work is to add a mechanism to allow a user to bookmark a completion page and refer to it later. This would be a trivial endeavor by implementing a query string that the API can use to look up and return the relevant images in the uploads folder. There is hesitance in implementing this. It is at odds with the scheduled cleanup of the uploads folder. This scheduled cleanup is a security necessity from both a systems perspective and a social perspective - I cannot verify, in real time, the contents of an image and thus must assume the worse. The regularly scheduled deletion of the images helps moderate this. +

+
+
+

Concluding notes

+

+ Credit for the Aquatint script goes to Professor Yannick Meurice at the University of Iowa. Literature pertaining to the script can be read through the American Journal of Physics Volume 90, Issue 2. +

+
+
+ + + + + + + + + + + +
+ Images created by the University of Chicago in which the Aquatint Image Processor was leveraged. +
+ +
+
+
+
+ + + + + +
+
+ + + diff --git a/projects/compiler/index.php b/projects/compiler/index.php new file mode 100644 index 0000000..e2a9116 --- /dev/null +++ b/projects/compiler/index.php @@ -0,0 +1,924 @@ + +
+
+
+
+
+

Preface

+
+

+ Deciding to go to university was motivated by a want to understand how to program at the hardware-level. This was accomplished early in the curriculum by means of taking Computer Organization.  The course satisfied my curiosity in this regard, but it opened up a new avenue of curiosity that would continue to inspire me to succeed. Computer Organization showed me a mapping of two different languages. The mathematics of propositional logic were used to scaffold assembly. +

+

+ My time learning to program up to that point had an undercurrent of realizing that given a single problem, there are many, (if not infinite), solutions. The scaffolding shown through Computer Organization confirmed this intuition and highlighted the fact that this sentiment also applies to how a given problem can be represented as well. The Algorithms course I took the following semester generalized the concept of an algorithm and made it programming-language agnostic. This would serve as a prerequisite for the course that would synthesize these concepts. +

+

+ I took Translation of Programming Languages during the fall semester of 2019. Students of the course were tasked with building a compiler that Translates a high-level functional language to a low-level assembly language. Lectures consisted of covering the various components and steps in the process of translation. Despite the effort required to succeed, I look back on this course as a favorite. It helped me understand programming languages as an abstraction and thus helped trivialize the notion of learning a new programming language or framework. +

+

+ Detailed below is documentation of the project. A repository for the compiler can be found via GitHub. +

+
+
+
+

Project: Klein Compiler

+
+ +

+ The following acts both as documentation and as a progress journal for a compiler written in Python. The compiler's input language is a language called Klein which operates in the functional paradigm. The target language is an assembly language called Tiny Machine. Specifications of these two languages can be found in the concluding notes section of this page. +

+

+ This implementation of the compiler includes all features described in the language specification except: +

    +
  • Arithmetic operations operate from right to left when stringed with operations within their respective set: ({+,-},{*,/}). +
      +
    • This likely stems from a flaw in the evaluation of a resultant abstract syntax tree
    • +
    • This can be prevented by taking full advantage of parenthesis to group respective operands.
    • +
    +
  • +
+

+

The Repository

+

+ It's worth noting the structure of the repository for this implementation of the compiler. The key directories are as follows: +

    +
  • + The home directory is KLEINcompiler/ +
      +
    • + Error logs are output at this level
    • +
    • + Various shell scripts are housed here to test the stages of the compiler. Each represents running the compiler for each implementation stage, or phase, of the project: +
        +
      • ./kleins : scanner; creation of a set of tokens
      • +
      • ./kleinf : parse table lookups
      • +
      • ./kleinp : abstract syntax tree creation
      • +
      • ./kleinv : type checker
      • +
      • ./kleinc : code generation; the completed compiler
      • +
      + To run each script run the following command: + +
      +    sh <script name> <program to be tested>
      +
      +
      + For example: + +
      +    sh kleinc programs/exclusive_or.kln
      +
      +
      +
    • +
    +
  • + +
  • + The doc directory houses various forms of documentation, housed in folders with respect to the component of the compiler they belong. +
  • +
  • + The programs directory houses a small handful of Klein programs that were created for use of the compiler. It also houses a set of programs provided by Dr. Wallingford within the class-programs folder. +
  • +
  • + The tests directory houses various Klein programs used for testing the various stages of the complier. +
  • +
  • + The src directory houses all files used for implementation of the compiler. +
      +
    • src/drivers houses files used to run the shell scripts, noted above.
    • +
    +
  • +
+

+

Flaws in implementation

+

It's worth noting flaws of implementation. These illuminate steps that should be taken to fine-tune the compiler as a future project. They ultimately are a reflection of the result of the project and mark lessons learned.

+
    +
  • + A lot of the logic is loaded into AST_node.py. This is the file which houses the class information for the abstract syntax tree. The layout itself is quite clever, but type-check and code-generation methods are added to them. This poses a problem in terms of separation of responsibility. The logic in AST_node.code_gen() is specific to outputting code in TM. Provided more time, exploration should occur here as a means to refactor theser methods into some exterior mechanism beside the abstract syntax tree. +
  • +
  • + The TM code being output is not efficient, ignoring the lack of any optimizer implementation. Most instructions that are output have the value stored in some memory position. This produces a lot of redundant instructions to IMEM and a lot of redundant data to DMEM. The most egregious lack of optimization comes from the IdentifierNode outputing a load instruction. This was an adhoc fix to print statements having no means to access an argument. +
  • +
  • + The error handling needs to be expanded upon. Effort was strong in the beginning of the project, but stalled out when time started becoming more of a premium and workload was shifted away from this effort. Error returns pertaining to parse errors do not help the user of a compiler as they are more geared for compiler debugging. +
  • +
+

Implementation of Compiler: Progress Checks

+ +

The following list of phases represent a step of progress implementing each component of the compiler. Interact with the header to expand its details.

+ + +
+
    +
  • Associated files of implementation: +
      +
    • kleins
    • +
    • src/k_token.py
    • +
    • src/scanner.py
    • +
    +
  • + +
  • Scanner Description: +

    + The scanner is the portion of the compiler which takes input a Klein program as a single string and splits the string into a set of tokens. The token class is described in k_token.py. The scanner steps through each character of the program string and uses a set of conditions to determine if a resultant character, (or string of characters), is a valid token. These conditions are based on the possible terminals present in the language specification. These may either be a single character, such as the case of the unary and binary operators, or can be a string, such as any identifier or number. Thus, these conditions are based on a set of regular expressions, as noted in the scanner portion of the documents folder. These regular expressions are used to build the conditions in place, represented by the FSA diagram which is also housed in the same folder. +

    +
  • + +
  • Associated files of documentation: +
      +
    • doc/scanner/regex.txt
    • +
    • doc/scanner/regex_FSA.jpg +
        +
      • doc/regex_FSA.jff is the jflapfile used to create the above jpeg
      • +
      +
    • + +
    • doc/scanner/scanner_status_check.txt
    • +
    +
  • +
  • Date of completion: 09-20-2019
  • +
+
+ + +
+
    +
  • + Associated files of implementation: +
      +
    • kleinf
    • +
    • src/parser.py
    • +
    • src/parse_table.py
    • +
    +
  • + +
  • Syntactic Analyzer Description: +

    + The first phase of the parser is the implementation of the component which decides if a program is syntactically correct. That is, the component makes sure that any given combination of tokens are in valid order. This is determined by the grammar of the Klein language, which has been refactored to eliminate the need for the parser to have to read-in more than one token at a time from the scanner to decide whether or not a combination of tokens is valid. +

    + +

    + The validity is determined by a parse table, which is determined by the first and follow sets of every declaration statement within the refactored grammar. These first and follow sets help us know every possible token which leads a nonterminal and every possible token that ends a terminal. With this information we can surmise all possible combinations while considering nonterminal composition (including nonterminals composed of other nonterminals). This is then mapped to a table using a specific algorithm which considers whether or not a grammar statement has an empty character in its first set. +

    + +

    + Thus, the parser has a parse stack which assumes an end of file token exists in addition to a nonterminal which represents the program as a whole. It takes a look at the top element of the stack and uses it to look up the valid combination of preceding tokens while considering the current token that is being read by the scanner. The parser walks the table using this logic, pushing terminals and nonterminals onto the parse stack based on the result of the current index of the parse table until either an invalid combination of tokens is discovered or the end-of-file token is reached. +

    +
  • + +
  • Associated files of documentation: +
      +
    • doc/parser/extended_grammar.txt
    • +
    • doc/parser/first_and_follow_sets.txt
    • +
    • doc/parser/parse_table.pdf +
        +
      • doc/parser/parse_table.ods
      • +
      +
    • +
    +
  • +
  • Date of completion: 10-04-2019
  • +
+
+ + +
+
    +
  • + Associated files of implementation: +
      +
    • kleinp
    • +
    • src/parser.py
    • +
    • src/parse_table.py
    • +
    • src/AST_node.py
    • +
    +
  • + +
  • Abstract Syntax Tree Description: +

    + The second phase of the parser is to build an abstract syntax tree which represents the various expressions and operations of the input program, (in the most general sense). These expressions and the resultant operations are put into data structures which will later be used to convert the statements into their equivalents during code generation. +

    +
  • + +
  • What was finished: +
      +
    • The inclusion of the semantic stack and the implementation of the parse algorithm to communicate between the parse stack and the semantic stack.
    • +
    • The inclusion of semantic actions within the parse table.
    • +
    • An enumeration class in the parse table which is used to evaluate the semantic actions returned on the parse table.
    • +
    • When the semantic action is evaluated with respect to the parse algorithm, it is used to index into a dictionary called object_factory: +
        +
      • The object factory returns the relevant AST_node class which is used to construct the relevant node object within the parse algorithm.
      • +
      • The resultant node is then pushed onto the semantic stack. The resultant node is also passed the semantic stack upon construction. Logic will exist within the node's constructor to know what to do with the stack and the various nodes and relevant tokens that reside in it.
      • +
      +
    • +
    +
  • + +
  • What is not finished: +
      +
    • The AST nodes themselves are not done. A couple of simple nodes have been finished; those that only really need to house a value - such as an identifier.
    • +
    • No error handling is in place once the AST nodes are ready for testing.
    • +
    +
  • + +
  • What else has been accomplished: +
      +
    • Errors regarding the previous phase with the parse table have been fixed.
    • +
    • Error classes have been expanded upon. Each error writes to a relevant text file the initial program and the associated stdout error message. +
        +
      • Lexical errors append the remaining program string to its error log.
      • +
      • Parse errors append the parse stack trace to its error log.
      • +
      +
    • +
    • The scanner returning a none value when evaluating comments has been fixed. +
        +
      • Existing logic was extended to accomplish this. This has resulted in some messy code that needs to be refactored.
      • +
      +
    • +
    +
  • +
  • Date of completion: 10-18-2019
  • +
+
+ + +
+
    +
  • Associated files of implementation: +
      +
    • kleinp +
        +
      • To run, feed a program through kleinp. It will print back node information as the AST is being built. If there is a type error, a Semantic Error will be thrown.
      • +
      +
    • +
    • src/parser.py
    • +
    • src/AST_node.py
    • +
    +
  • + +
  • Type Checker Description: +

    + The next phase of the project was to augment the abstract syntax tree with type information. This is used to make sure a user is inputting the correct type of data for a program to execute. This was handled by adding type checking methods to the ASTnode class within AST_node.py and made changes to a subclass' method where applicable. The type check occurs after the parser builds the abstract syntax tree, where the parser grabs the outer node off the semantic stack and calls process_node() which recursively descends into the composition of nodes, calling typeCheck() when applicable. +

    +

    + When a type error is found, the recursive function starts to back-track, sending the error information as an error object back to the parser. If the error object exists on the semantic stack when the parser tries to call process_node(), it will instead raise an exception, feeding extra information about the program to it. +

    +
  • + +
  • What is not finished: +
      +
    • Error handling needs to be expanded upon. As of now, the error messages are pretty slim.
    • +
    • + Some code cleanup would be nice. This includes implementing correct OOP design such as making sure accessors are correctly called, as opposed to directly referring to an object's property. Some of the lines of code also need to be line-wrapped/escaped to allow easier viewing on smaller screen resolutions. +
        +
      • There exists some duplicate code within AST_node.py. Note the inclusion of the list functions. There is also some ad-hoc code duplication wihtin process_node(). This is in place to allow the recursive function to back-track once an error is found. The flag which causes this is the inclusion of 'errob' (an object class which needs to be renamed...) on the function_record list.
      • +
      +
    • +
    +
  • +
  • Date of completion: 11-01-2019
  • +
+
+ + +
+
    +
  • Associated files of implementation: +
      +
    • kleinc
    • +
    • AST_node.py
    • +
    • code-generator.py
    • +
    +
  • +
  • Code Generator Description: +

    The first phase of implementing the code generator involved priming behavior. What's meant by this is that the state of the generator was to set up the environment such that the address space for a main function is added to the runtime stack and relevant information is saved to the relevant registers. The body of the main is executed and registers are restored as per the TM specification. Lastly, functionality was put in place with respect to evaluating literals and print statements.

    +
  • +
  • Schema of environment: +
      +
    • The Stack Frame: + +
      +:----------------------:
      +:     return addr      : 0
      +:----------------------:
      +:     return value     : 1
      +:----------------------:
      +:     arg  0           :
      +:----------------------:
      +:     args             :
      +:----------------------:
      +: register values(0-6) : - can update to only save whats modified
      +:----------------------:
      +:     temp space       :
      +:----------------------:
      +
      +
      +
    • +
    • Registers: + +
      +   :------------------:
      +r0 :                  :
      +   :------------------:
      + 1 :                  :
      +   :------------------:
      + 2 :                  :
      +   :------------------:
      + 3 :                  :
      +   :------------------:
      + 4 :                  :
      +   :------------------:
      + 5 :                  :
      +   :------------------:
      + 6 :   top  (stack)   :
      +   :------------------:
      + 7 :    PC            :
      +   :------------------:
      +
      +
      +
    • +
    • DMEM: + +
      +0 :-------------------:
      +| :                   :
      +| :-------------------:
      +V :                   :
      +. :-------------------:
      +. :                   :
      +  :-------------------: Top (R6)
      +  :        |          :
      +  :        |          :
      +  :        |          :
      +  :        |  Grows   :
      +  :        V          :
      +  :                   :
      +  :                   :
      +  :-------------------:
      +
      +
      +
    • +
    • IMEM: + +
      +0 :---------------------:
      +  : LDA 7 , <main addr> :
      +  :---------------------:
      +  :         .           :
      +  :         .           :
      +  :         .           :
      +  :         .           :
      +  :         .           :
      +  :---------------------: main
      +  : / / / / / / / / / / :
      +  : / / / / / / / / / / :
      +  :---------------------:
      +
      +
      +
    • +
    +
  • +
  • What is not finished: +
      +
    • Better register management is still under consideration.
    • +
    • Setting up three-address-code templates for each node.
    • +
    +
  • +
  • Date of completion: 11-15-2019
  • +
+
+ + + +
+
    +
  • Associated files of implementation: +
      +
    • AST_node.py
    • +
    • code-generator.py
    • +
    • kleinc
    • +
    +
  • +
  • Phase 6 expands on the functionality of phase 5. The goal was to complete what wasn't finished. The following has been implemented: +
      +
    • Function Calls
    • +
    • Arithmetic Operations +
        +
      • Addition Operation
      • +
      • Subtraction Operation
      • +
      • Division Operation
      • +
      • Multiplication Operation
      • +
      +
    • Boolean Connective Operations +
        +
      • And Operation
      • +
      • Or Operation
      • +
      +
    • +
    • Boolean Comparison Operations +
        +
      • Less-Than Operation
      • +
      • Equal-To Operation
      • +
      +
    • +
    • Unary Operations +
        +
      • Not Operation
      • +
      • Negation Operation
      • +
      +
    • +
    +
  • + +
  • What needs to be implemented: +
      +
    • Grabbing arguments from the initial TM execution +
        +
      • The compiler does have logic that places the initial DMEM arguments into the control stack.
      • +
      +
    • +
    • There currently exists a logical error that allows klein programs to have arguments with the same name.
    • +
    • Some more clear error handling
    • +
    +
  • +
  • Date of completion: 12-06-2019
  • +
+
+ + + +
+
    +
  • What was completed: +
      +
    • TM programs now allow arguments to be fed in through the command line. +
        +
      • The DMEM allocated to the initial main function call now factors the arguments that may exist when the TM machine runs.
      • +
      • Function declaration output no longer saves and restores register values for temporary register usage.
      • +
      • Negation and Not operation TM output is fixed.
      • +
      • Type checking error messages have been refined and are more informative.
      • +
      • A whole slew of klein programs have been created for testing purposes.
      • +
      +
    • +
    +
  • +
  • Date of completion: 12-13-2019
  • +
+
+ + +
+
+

Concluding notes

+

+ The Klein language specification was written by Professor Eugene Wallingford of the University of Northern Iowa. + +

+ I chose the name to indicate the size of the language. klein is the German word for "small" or "little". It is one of the first German words I learned back in the eighth grade. + - Eugene Wallingford +
+ My looking back favorably on this course has a lot to do with having Dr. Wallingford as a professor. He was well equipped to teach the course and I always felt that he was very engaging with my level of curiosity. +

+

+ Tiny Machine (TM) is an assembly language written by Kenneth Louden. The specifications for both languages are in the following expandable sections. +

+ +
+

Klein is a small, mostly functional language that is designed specifically to be used as a manageable source language in a course on compiler design and implementation. Though small and simple, the language is Turing-complete.

+ +

Grammar

+ +
+        <PROGRAM> ::= <DEFINITIONS>
+
+        <DEFINITIONS> ::= ε
+                        | <DEF> <DEFINITIONS>
+
+                <DEF> ::= function <IDENTIFIER> ( <FORMALS> ) : <TYPE>
+                             <BODY>
+
+            <FORMALS> ::= ε
+                        | <NONEMPTYFORMALS>
+
+    <NONEMPTYFORMALS> ::= <FORMAL>
+                        | <FORMAL> , <NONEMPTYFORMALS>
+
+             <FORMAL> ::= <IDENTIFIER> : <TYPE>
+
+               <BODY> ::= <PRINT-STATEMENT> <BODY>
+                        | <EXPR>
+
+               <TYPE> ::= integer
+                        | boolean
+
+               <EXPR> ::= <EXPR> < <SIMPLE-EXPR>
+                        | <EXPR> = <SIMPLE-EXPR>
+                        | <SIMPLE-EXPR>
+
+        <SIMPLE-EXPR> ::= <SIMPLE-EXPR> or <TERM>
+                        | <SIMPLE-EXPR> + <TERM>
+                        | <SIMPLE-EXPR> - <TERM>
+                        | <TERM>
+
+               <TERM> ::= <TERM> and <FACTOR>
+                        | <TERM> * <FACTOR>
+                        | <TERM> / <FACTOR>
+                        | <FACTOR>
+
+             <FACTOR> ::= if <EXPR> then <EXPR> else <EXPR>
+                        | not <FACTOR>
+                        | <IDENTIFIER> ( <ACTUALS> )
+                        | <IDENTIFIER>
+                        | <LITERAL>
+                        | - <FACTOR>
+                        | ( <EXPR> )
+
+            <ACTUALS> ::= ε
+                        | <NONEMPTYACTUALS>
+
+    <NONEMPTYACTUALS> ::= <EXPR>
+                        | <EXPR> , <NONEMPTYACTUALS>
+
+            <LITERAL> ::= <NUMBER>
+                        | <BOOLEAN>
+
+    <PRINT-STATEMENT> ::= print ( <EXPR> )
+
+
+

Syntax Features

+
    +
  • + These are the reserved words of Klein: + +
    +    integer     boolean
    +    true        false
    +    if          then       else
    +    not         and        or
    +    function    print
    +
    +
    +
  • +
  • + print is a primitive identifier. true and false are boolean literals. The rest are keywords. +
  • +
  • + Klein reserved words may not be used as user-defined names of functions or formal parameters. +
  • +
  • + Klein reserved words and identifiers are case-sensitive. +
  • +
  • + Identifiers must be no longer than 256 characters. +
  • +
  • + The following are the primitive operators and punctuation marks of Klein: + +
    +    +           -          *        /
    +    <           =          (        )
    +    ,           :          (*       *)
    +
    +
    +
  • +
  • + Klein operators and punctuation are self-delimiting. +
  • +
  • + Integer Literals must be in the range from 0 to 231-1, inclusive. +
  • +
  • + A comment in Klein begins with the characters (* and continues up to the next occurrence of the characters *). Any characters inside a comment are ignored. +
  • +
  • + A function many have zero or more formal parameters. The scope of a formal parameter is the body of the function. Arguments are passed by value. +
  • +
  • + Binary operators and function calls evaluate their arguments from left to right. +
  • +
  • + Whitespace consists only of blanks, tabs, and the end-of-line characters \n and \r. Whitespace characters may not appear inside a literal, identifier, keyword, or operator. Otherwise, whitespace is insignificant. +
  • +
+ +

Data

+

+ All data in Klein is an integer or a boolean, and nearly every element in a program is an expression that produces an integer result or a boolean result. +

+ +

Atomic Expressions

+
    +
  • + There are only two boolean values. The two primitive boolean literals are true and false. +
  • +
  • + Integer literals are strings of digits. There are no leading plus or minus signs to indicate positive or negative values; all integer literals are positive. Leading zeros are not permitted for non-zero integer literals. +
  • +
  • + Klein supports integer values in the range of -231 to 231-1. +
  • +
  • + User-defined identifiers are strings beginning with a letter and consisting of letters, digits, and the underscore. +
  • +
+ +

Compound Expressions

+

The language provides the following kinds of expression:

+
    +
  • +
    Arithmetic
    + Adds, subtracts, multiplies, or divides two integers. + +
    +     x + y
    +     x - y
    +     x * y
    +     x / y
    +
    +
    +
  • +
  • +
    Boolean Comparisons
    + Compares two integers, yielding one of the boolean values true or false. < yields true if its left operand is less than its right operand, and false otherwise. = yields true if its left operand has the same value as its right operand, and false otherwise. + +
    +     x < y
    +     x = y
    +
    +
    +
  • +
  • +
    Boolean Connectives
    + Negates a single boolean value, or computes the disjunction or conjunction of two boolean values. The unary not yields true if its operand is false, and false otherwise. or yields true if either its left operand or its right operand yields true, and false otherwise. and yields true if both its left operand and its right operand yield true, and false otherwise. +
    +     not x
    +     x or y
    +     x and y
    +
    +
    + or and and short-circuit evaluation when possible. +
  • +
  • +
    Conditional Selection
    + Evaluates a test expression, and uses its value to select one of two expressions to evaluate. Yields the value of the first of these expressions if the test expression produces a true value, and the value of the second if the test expression yields a false value. The else clause is required.
    + For example: + +
    +     if flag < 0 then
    +        x + y
    +     else
    +        x - y
    +
    +
    + produces the sum of x and y if flag is less than 0; otherwise, it produces their difference. +
  • +
  • +
    Function call
    + Applies a function to zero or more arguments, and yields the value of the expression in the body of the function. All functions return an integer value or a boolean value; Klein has no notion of a "void" function.
    + For example: + +
    +     f( x+y, 1 )
    +
    +
    + computes the sum of x an dy, passes that value and a 1 to the function f, and produces the value returned by applying the function to its arguments. +
  • +
  • +
    Miscellaneous
    + Compound expressions can be nested to any depth.

    + Note that the only user-defined identifiers in Klein ar ethe names of functions and the names of formal parameters to functions. There are no "variables". +
  • +
+ +

User-Defined Functions

+
    +
  • + Each function declaration consists of the function's name, its formal parameters and their types, the type of the funtion, and its body. +
  • +
  • + Function names are unique. +
  • +
  • + A function may refer only to its formal parameters and to other functions. +
  • +
  • + A Klein function may call itself. +
  • +
+ +

Primitive Functions

+
    +
  • + For the purposes of user interaction, Klein provides the primitive function print(expression). For example: + +
    +     print( x+y )
    +
    +
    + print writes its argument on standard output, followed by a new line character. +
  • +
  • + Unlike all user-defined functions, the value of a call to print is undefined. For this reason, if a function contains a call to print, that call must come at the top of the function body. +
  • +
+ +

Programs

+
    +
  • + A Klein program consists of a sequence of function definitions. Every program must define a function named main, which is called first when the program runs. +
  • +
  • + Users may provide arguments to main on the command line. The result returned by main is printed on standard output. +
  • +
  • + For example, here is a complete Klein program that computes the absolute value of its argument: + +
    +     function main( n : integer ) : integer
    +        if n < 0
    +           then -n
    +           else n
    +
    +
    + If this program were compiled into an executable file named abs, then running it under Unix might look something like this: + +
    +    mac os x > abs -3
    +    3
    +
    +
    +
  • +
+
+ + +
+

+ TM is a simple target machine that has an architecture and instruction set complex enough to illustrate the important issues faced when writing a compiler, yet simple enough to not distract with unnecessary details. +

+ +

Architecture

+
    +
  • + TM provides two kinds of memory: +
      +
    • instruction memory, which is read-only
    • +
    • data memory
    • +
    +
  • +
  • + Memory addresses are non-negative integers. When the machine is started, all data memory is set to 0, except for the first memory location. That location contains the value of the highest legal address. +
  • +
  • + An extended version of the TM interpreter is used which accepts command-line arguments to the TM program and stores them in memory locations 1 through n. +
  • +
  • + TM provides eight registers, numbered 0 through 7. Register 7 is the program counter. The other seven registers are available for program use. WHen the machine is started, all registers are set to 0. +
  • +
  • + When the machine is started, after memory and registers have been initialized, TM begins execution of the program beginning in the first location of instruction memory. The machine follows a standard fetch-execute cycle: +
      +
    • fetch the current instruction from the address indicated by the program counter
    • +
    • increment the program counter
    • +
    • execute the instruction
    • +
    +
  • +
  • + The loop terminates when it reaches a HALT instruction or when an error occurs. TM has three native error conditions: +
      +
    • IMEM_ERR, which occurs in the fetch step whenever the address of the next instruction to be executed is out of bounds
    • +
    • DMEM_ERR, which occurs in the execute step whenever the address of a memory access is out of bounds
    • +
    • ZERO_DIV, which occurs in the execute step whenever the divisor to a div is zero
    • +
    +
  • +
+ +

Instruction Set

+
    +
  • + TM provides two kinds of instructions: register-only and register-memory. +
  • +
  • + Register-only (RO) instructions are of the form + +
    +    opcode r1,r2,r3
    +
    +
    + where the ri are legal registers. +
  • +
  • + These are the RO opcodes: + +
    +IN      read an integer from stdin and place result in r1; ignore operands r2 and r3
    +OUT     write contents of r1 to stdout; ignore operands r2 and r3
    +ADD     add contents of r2 and r3 and place result in r1
    +SUB     subtract contents of r3 from contents of r2 and place result in r1
    +MUL     multiply contents of r2 and contents of r3 and place result in r1
    +DIV     divide contents of r2 by contents of r3 and place result in r1
    +HALT    ignore operands and terminate the machine
    +
    +
    +
  • +
  • + Register-memory (RM) instructions are of the form + +
    +    opcode r1,offset(r2)
    +
    +
    + Where the ri are legal registers and offset is an integer offset. offset may be negative. With the exception of the LDC instruction, the expression offset(r2) is used to compute the address of a memory at location: + +
    +    address = (contents of r2) + offset
    +
    +
    +
  • +
  • + There are four RM opcodes for memory manipulation: + +
    +LDC    place the constant offset in r1; ignore r2
    +LDA    place the address address in r1
    +LD     place the contents of data memory location address in r1
    +ST     place the contents of r1 to data memory location address
    +
    +
    +
  • +
  • + There are six RM opcodes for branching. If the value of r1 satisfies the opcode's condition, then branch to the instruction at memory location address. + +
    +JEQ    equal to 0
    +JNE    not equal to 0
    +JLT    less than 0
    +JLE    less than or equal to 0
    +JGT    greater than 0
    +JGE    greater than or equal to 0
    +
    +
    +
  • +
  • + All arithmetic is done with registers (not memory locations) and on integers. Floating-point numbers must be simulated in the run-time system. +
  • +
  • + There are no restrictions on the usage of registers. For example, the source and target registers for an operation can be the same. +
  • +
  • + The note above is true of the program counter, Register 7. For example, +
      +
    • To branch unconditionally to an instruction, a program can load the target address into the PC using an LDA instruction.
    • +
    • To branch unconditionally to an instruction whose address is stored in data memory, a program can load the target address into the PC using an LD instruction.
    • +
    • To branch conditionally to an instruction whose address is relative to the current position in the program, a program can use the PC as r2 in any of the jxx instructions.
    • +
    +
  • +
+
+
+
+
+ + +
+
+ + + + diff --git a/projects/cup/demo.php b/projects/cup/demo.php new file mode 100644 index 0000000..93c857b --- /dev/null +++ b/projects/cup/demo.php @@ -0,0 +1,38 @@ + +
+
+
+
+

Web Development: Cup of Joe

+
+

+ +

+ +
+ +
+
+ + + diff --git a/projects/cup/index.php b/projects/cup/index.php new file mode 100644 index 0000000..0616cde --- /dev/null +++ b/projects/cup/index.php @@ -0,0 +1,309 @@ + +
+
+
+
+

Web Development: Cup of Joe

+
+

+ Having concluded a semester involving both Intermediate Computing and Design & Analysis of Algorithms, my time was spent going back to my roots as a web developer. This occurred during the winter transition from the semester of Fall 2018. +

+

+ A local coffee shop, Cup of Joe, was a frequent spot of study; this fact helped contribute to successful grades. So what better way to give back by doing a redesign of the current website? A redesign which would make it mobile friendly. +

+
+ +
+ Screenshot of the layout of the Cup of Joe website using a mobile display. +
+ Mobile variant of the Story page. +
+
+
+
+

+ The redesign entailed a rebuild of the HTML and CSS - motivated specifically to rid the site of the usage and elaborate nesting of table tags. More modern semantic tags were used instead of the table tags: an aside to note tangential information, lists to designate groupings of links, article to note groupings of paragraphs, sections to differentiate these major components, etc. +

+

+ The foundation of this implementation was built upon a mobile-first design. Here, media queries would kick a certain layout into view based on window size where the initial template being considered was designed for mobile devices. This involved careful consideration towards maintaining the website's aesthetic - an aesthetic which does a good job adhering to the aesthetic of the shop proper. One key facet in accomplishing this was the decision to maintain the site's identity with the navigation bar. Instead of leveraging a hamburger button to splay navigational options, the navigation bar is fixed to the left where the size of its selections maintain a good compromise between easy interaction while not obscuring too much of the view, (regardless of view-screen size). The redesign differentiated further by fixing the navigation bar into view as a user scrolls through a given page. +

+ +
+ Screenshot of the layout of the Cup of Joe website using a mobile display. +
+ Mobile variant of the Story page. +
+
+
+
+

+ In terms of layout, there were two different types of pages that the Cup of Joe website contained. The main page had a unique layout in the sense that the majority of the display emphasized on a slideshow of images from the coffee shop. The other pages consisted of textual content with an image banner and occasionally information placed as an aside. The responsive redesign would not only determine the sizes of these elements, but the location they would be drawn. +

+
+
+
+ +
+ Comparison of desktop variant and tablet variant of the website. Note the repositioning of elements which emphasizes on key components which lend well to the browsing context. +
+
+
+

+ Once implementing the redesign for the main page and the story page, I shared them with the proprietor of Cup of Joe. She was impressed! More importantly, I had good timing. This was due to the fact that her website was built upon a framework that was about to have its support revoked - (if I recall correctly, it involved Microsoft's Silverlight). The host of her website did not have the time to redesign and re-implement the website using some other framework. +

+

+ What's been shown thus far has been static in nature. There indeed was a non-static feature in place. There was a music page which allowed the posting of related events. This had its own set of controls that the owner had access to. Thus, my initial intention of a straight-foward mobile redesign would balloon into the inclusion and implementation of a simple content management system. +

+

+ Implementation of the content management system would be through a PHP back-end interacting with a MySQL database. Here, user accounts could be created, events could be posted and edited, and a new feature was provided with shop integration where products being sold could also be posted. All this control was provided through a set of pages that could be accessed using any browser. These control pages would leverage a simple bootstrap theme to allow mobile access; I was not shy to use the hamburger button in the context of using these control pages. +

+ +

+ The public-facing pages of the Cup of Joe website were then templated using PHP and thus integrated access to the database to allow the display of events and shop links. The mobile redesign was made easier through the modularity provided through this template. +

+

+ It's worth reflecting on the work done here. While looking through the archive of this website, I'm drawn to the difference between what was then called the music page and what is currently called the event page. This was a place where I decided to introduce new styling in an attempt to make the website more coherent in style. The new styling bore similarity to how the aside blurbs were displayed within the various pages, providing a more cohesive experience with respect to the aesthetic. +

+
+
+ +
+ Comparison between the new layout of the events page (left) and the old layout from what was called the music page (right). +
+
+
+

+ The result of my efforts can be viewed on the company's domain: www.cupofjoe-cedarfalls.com. It seems that events are no longer being hosted at the location, thus the events page has been removed altogether. My personal domain is hosting a live demo of the website as well. It can be viewed here. +

+ +
+ +
+
+ + + diff --git a/projects/grid/hex.js b/projects/grid/hex.js new file mode 100755 index 0000000..80c7c7b --- /dev/null +++ b/projects/grid/hex.js @@ -0,0 +1,781 @@ + +grid_producer = function(canvas_id, s, cols, x_margin, y_margin){ + hexV = [];//this array holds all the variables and values associated with this script. + //*** Putting everything in the above hex array allows helps prevent any other script variables from having conflict with this script. This also makes it easy to include multiples of this file within a web page to house multiple hexagonal grids. All that's required is to use a search and replace function in a text editor to replace the string "hexV." with whatever arbitrary name you want to use for the object ***// + + hexV.canvas = document.getElementById(canvas_id); + hexV.context = hexV.canvas.getContext('2d'); + + hexV.s = s;//This is the circumradius and the length of each hexegonal side + hexV.h = (Math.sin((30*(Math.PI/180))) * hexV.s); + hexV.r = (Math.cos((30*(Math.PI/180))) * hexV.s);//this is the inradius + hexV.a = 2*hexV.r; + + hexV.hexCount = 0; + hexV.newHex = true; + hexV.subHex = 1; + hexV.endHex = 0; + hexV.rows = 0; + hexV.cols = cols; + hexV.currentRow = 1; + hexV.adjacencyInit = false; + + //** These variables are used in the grid_handler function. The help determine where each vertex should be positioned **// + hexV.actualx = x_margin; + hexV.actualy = y_margin; + hexV.x = hexV.actualx; + hexV.y = hexV.actualy; + hexV.counter = 1; + hexV.rowswitch = false; + hexV.rowinit = false; + hexV.init = false; + + hexV.adjLines = false; + hexV.hexLines = false; + hexV.drawOrigins = false; + + hexV.mousePos; + hexV.grid = [];//This global array will be populated by objects representing the coordinates of each hexegon + hexV.origin = []; + + /* ---- Event listeners ---- */ + hexV.canvas.addEventListener('mousemove', function(evt){ + hexV.mousePos = getMousePos(hexV.canvas,evt,hexV); + hexV.x = 0; + hexV.y = 0; + hexV.counter = 1; + hexV.rowswitch = false; + hexV.rowinit = false; + hexV.init = false; + hexV.context.clearRect(0, 0, hexV.canvas.width, hexV.canvas.height); + drawHexes(1,hexV); + }, false); + + hexV.canvas.addEventListener('mouseout', function(evt){ + hexV.mousePos = getMousePos(hexV.canvas,evt,hexV); + hexV.x = 0; + hexV.y = 0; + hexV.counter = 1; + hexV.rowswitch = false; + hexV.rowinit = false; + hexV.init = false; + hexV.context.clearRect(0, 0, hexV.canvas.width, hexV.canvas.height); + drawHexes(0,hexV); + }, false); + + hexV.canvas.addEventListener('mousedown', function(evt){ + for (i = 0; i < hexV.grid.length; i++){ + this.vertices = hexV.grid[i];//resume + this.selectedHex = getselectedHex(hexV); + if(hexV.origin[i] === this.selectedHex.selected && hexV.origin[i].type !== null){ + if(hexV.grid['selected'] === this.vertices){ + //document.getElementById("output").innerHTML = '
'; + hexV.grid['selected'] = null; + }else{ + //document.getElementById("output").innerHTML = hexV.origin[i].name; + hexV.grid['selected'] = this.vertices; + } + drawHexes(1,hexV); + } + } + }, false); + + return hexV; +} + + + +//*** grid_handler is used to handle the offest of every other hexagon. This also handles shifting the required hexegon to the next row. ***// +grid_handler = function(hexV){ + if(hexV.counter <= hexV.cols){//it's not time to start a new row of hexegons + if(hexV.rowinit === true){ + if(hexV.rowswitch === true){//a hexegon with an even designation in regards to how many hexegons have been generated in it's row + hexV.x = hexV.x + hexV.h + hexV.s; + hexV.y = hexV.y + hexV.r; + }else{//a hexegon with an odd designation in regards to how many hexegons have been generated in it's row + hexV.x = hexV.x + hexV.h + hexV.s; + hexV.y = hexV.y - hexV.r; + } + }else{ + hexV.rowinit = true; + if(hexV.init === true){//the first hexegon generated in each row after the first. + hexV.x = hexV.x + hexV.h + hexV.s; + hexV.y = hexV.y + hexV.r; + }else{//the very first hexegon to be generated. + hexV.init = true; + } + } + }else{ + hexV.rowinit = false; + hexV.counter = 1; + hexV.x = hexV.actualx; + if(hexV.cols % 2 === 0){ + hexV.y = hexV.y + hexV.r; + }else{ + hexV.y = hexV.y + hexV.a; + } + hexV.rowswitch = false; + } + hexV.counter++; + hexV.rowswitch = !hexV.rowswitch; +} + + + +//*** hex_handler uses the grid_handler function to calculate the set of vertices associated with each hexegon. It then returns these values back to the hexegon object ***// +hex_handler = function(hexV){ + grid_handler(hexV); + hexV.hexCount++; + hexV.y = hexV.y + hexV.r;//priming the initial vertex. + this.center = {x: hexV.x + hexV.s, y: hexV.y}; + this.sideVertices = []; + //Taking a walk around the hexegon, generating each side vertex, then storing them in the sideVertices array + this.sideVertices.v1 = {x: hexV.x, y: hexV.y}; + hexV.x = hexV.x + hexV.h; + hexV.y = hexV.y - hexV.r; + this.sideVertices.v2 = {x: hexV.x, y: hexV.y}; + hexV.x = hexV.x + hexV.s; + this.sideVertices.v3 = {x: hexV.x, y: hexV.y}; + hexV.x = hexV.x + hexV.h; + hexV.y = hexV.y + hexV.r; + this.sideVertices.v4 = {x: hexV.x, y: hexV.y}; + hexV.x = hexV.x - hexV.h; + hexV.y = hexV.y + hexV.r; + this.sideVertices.v5 = {x: hexV.x, y: hexV.y}; + hexV.x = hexV.x - hexV.s; + this.sideVertices.v6 = {x: hexV.x, y: hexV.y}; + hexV.x = hexV.x - hexV.h; + hexV.y = hexV.y - hexV.r; + this.v7 = {x: hexV.x, y: hexV.y};//this is redundant, can trace back to v1 + hexV.y = hexV.y - hexV.r; + return{ + center: this.center, + sideVertices: this.sideVertices + } + +} + + + +//*** This is the hexagon data collection hub. Data is gathered from the above functions to be stored in array to be used by the functions used bellow ***// +hex = function(hexV, name, type){ + this.vertices = hex_handler(hexV); + this.vertices.center.name = name; + if(type === null){ + this.vertices.center.type = type; + }else{ + this.vertices.center.type = 'standard'; + } + hexV.grid.push(this.vertices.sideVertices); + hexV.origin.push(this.vertices.center); +} + + + +function calculateAdjacentOrigins(hexV){ + hexV.rows = Math.ceil(hexV.hexCount / hexV.cols); + hexV.endHex = hexV.cols; + //*** This function calculates and stores any center coordinates of any imaginary AND null hexegons that will be used in the grid's selection logic. Imaginary hexegons can be defined as adjacent hexegons to hexegons that exist on the border of the grid. Null hexegons are hexegons with the type of null; hexegons that a user may choose not to display. ***// + + //The coordinates are generated and stored in the origin array. + //As of now, this function is only run once: When the first javascript event occurs. This is determined outside of the function so any implementation can redraw these values at their own discretion. + + //*** The following if/else if statements factor three contingencies: + + //*** ONLY ONE HEXEGON EXISTS ***// + if(hexV.hexCount === 1){ + if(hexV.origin[0].type !== null){ + this.center = hexV.origin[0]; + hexV.origin.push({x: center.x, y: center.y - hexV.a});//hex top + hexV.origin.push({x: center.x, y: center.y + hexV.a});//hex bottom + hexV.origin.push({x: center.x + 2.5*hexV.s - hexV.s, y: center.y - hexV.r});//hex top right + hexV.origin.push({x: center.x - 0.5*hexV.s - hexV.s, y: center.y - hexV.r});//hex top left + hexV.origin.push({x: center.x + 2.5*hexV.s - hexV.s, y: center.y + hexV.r});//hex bottom right + hexV.origin.push({x: center.x - 0.5*hexV.s - hexV.s, y: center.y + hexV.r});//hex bottom left + } + + //*** ONLY ONE ROW OF HEXEGONS EXIST --- ***// + }else if (hexV.rows === 1){ + for(i = 0; i < hexV.hexCount; i++){ + if(hexV.origin[i].type !== null){ + this.center = hexV.origin[i]; + hexV.origin.push({x: center.x, y: center.y - hexV.a});//hex top + hexV.origin.push({x: center.x, y: center.y + hexV.a});//hex bottom + if(hexV.origin[i+1].type === null && hexV.origin[i] !== hexV.hexCount-1){ + if(i % 2 === 0){ + hexV.origin.push({x: center.x + 2.5*hexV.s - hexV.s, y: center.y - hexV.r});//hex top right + } + if(i % 2 !== 0){ + hexV.origin.push({x: center.x + 2.5*hexV.s - hexV.s, y: center.y + hexV.r});//hex bottom right + } + } + if(i > 0 && i < hexV.hexCount){//making sure origin reference exists + if(hexV.origin[i-1].type === null){ + if(hexV.origin[i-2].type === null){ + if(i % 2 === 0){ + hexV.origin.push({x: center.x - 0.5*hexV.s - hexV.s, y: center.y - hexV.r});//hex top left + } + if(i % 2 !== 0){ + hexV.origin.push({x: center.x - 0.5*hexV.s - hexV.s, y: center.y + hexV.r});//hex bottom left + } + } + } + } + + + if(i === 0){ + hexV.origin.push({x: center.x - 0.5*hexV.s - hexV.s, y: center.y - hexV.r});//hex top left + hexV.origin.push({x: center.x - 0.5*hexV.s - hexV.s, y: center.y + hexV.r});//hex bottom left + } + if(i === hexV.hexCount - 1){ + hexV.origin.push({x: center.x + 2.5*hexV.s - hexV.s, y: center.y - hexV.r});//hex top right + hexV.origin.push({x: center.x + 2.5*hexV.s - hexV.s, y: center.y + hexV.r});//hex bottom right + } + } + } + + //*** MULTIPLE ROWS OF HEXEGONS EXISTS ***// + }else{ + for(i = 0; i < hexV.hexCount; i++){ + if(hexV.origin[i].type !== null){ + this.center = hexV.origin[i]; + + //*** FIRST ROW CONDITION ***// + if(hexV.currentRow === 1){ + hexV.origin.push({x: center.x, y: center.y - hexV.a});//generate the top origin - applies to every hexagon in this row + if(hexV.origin[i+1].type === null && i !== hexV.endHex-1){ + if(i % 2 === 0){ + hexV.origin.push({x: center.x + 2.5*hexV.s - hexV.s, y: center.y - hexV.r});//hex top right + } + } + if(i > 0 && i < hexV.hexCount){ + if(hexV.origin[i-1].type === null){ + if(hexV.origin[i-2].type === null){ + if(i % 2 === 0){ + hexV.origin.push({x: center.x - 0.5*hexV.s - hexV.s, y: center.y - hexV.r});//hex top left + } + } + } + } + + if(i === 0){ + hexV.origin.push({x: center.x - 0.5*hexV.s - hexV.s, y: center.y - hexV.r});//hex top left + if(hexV.origin[i+hexV.cols].type === null){ + hexV.origin.push({x: center.x - 0.5*hexV.s - hexV.s, y: center.y + hexV.r});//hex bottom left + } + } + if(i === hexV.cols-1){ + hexV.origin.push({x: center.x + 2.5*hexV.s - hexV.s, y: center.y - hexV.r});//hex top right + if(hexV.origin[i+hexV.cols].type === null){ + hexV.origin.push({x: center.x + 2.5*hexV.s - hexV.s, y: center.y + hexV.r});//hex bottom right + } + } + + if(typeof hexV.origin[i+hexV.cols].type === "undefined" && i !== 0){ + hexV.origin.push({x: center.x, y: center.y + hexV.a});//hex bottom + if(i === hexV.cols-1){ + hexV.origin.push({x: center.x + 2.5*hexV.s - hexV.s, y: center.y + hexV.r});//hex bottom right + } + if(typeof hexV.origin[i-1+hexV.cols].type === "undefined"){ + hexV.origin.push({x: center.x - 0.5*hexV.s - hexV.s, y: center.y + hexV.r});//hex bottom left + } + } + + if( hexV.rows === 2){ + if(typeof hexV.origin[i+hexV.cols].type === "undefined" && hexV.origin[i+1].type === null){ + hexV.origin.push({x: center.x + 2.5*hexV.s - hexV.s, y: center.y + hexV.r});//hex bottom right + } + } + + //*** MIDDLE ROW CONDITION ***// + }else if((hexV.currentRow > 1 && hexV.currentRow < hexV.rows) || (hexV.rows === 2 && hexV.currentRow === 1)){ + if(hexV.newHex === true){ + hexV.origin.push({x: center.x - 0.5*hexV.s - hexV.s, y: center.y - hexV.r});//hex top left + if(hexV.origin[i+hexV.cols].type === null){ + hexV.origin.push({x: center.x - 0.5*hexV.s - hexV.s, y: center.y + hexV.r});//hex bottom left + } + } + + if(i === hexV.endHex-1){ + hexV.origin.push({x: center.x + 2.5*hexV.s - hexV.s, y: center.y - hexV.r});//hex top right + if(hexV.origin[i+hexV.cols].type === null){ + hexV.origin.push({x: center.x + 2.5*hexV.s - hexV.s, y: center.y + hexV.r});//hex bottom right + } + } + + if(hexV.currentRow === hexV.rows - 1){ + if(hexV.newHex !== true){ + if(typeof hexV.origin[i+hexV.cols].type === "undefined"){ + hexV.origin.push({x: center.x, y: center.y + hexV.a});//hex bottom + if(i === hexV.endHex-1){ + hexV.origin.push({x: center.x + 2.5*hexV.s - hexV.s, y: center.y + hexV.r});//hex bottom right + } + } + if(typeof hexV.origin[i-1+hexV.cols].type === "undefined" && hexV.origin[i-1].type === null){ + if(hexV.subHex % 2 === 0){ + hexV.origin.push({x: center.x - 0.5*hexV.s - hexV.s, y: center.y + hexV.r});//hex bottom left + } + } + if(typeof hexV.origin[i+1+hexV.cols].type === "undefined" && hexV.subHex % 2 === 0 && hexV.origin[i+1].type === null){ + hexV.origin.push({x: center.x + 2.5*hexV.s - hexV.s, y: center.y + hexV.r});//hex bottom right + } + } + } + + //*** LAST ROW CONDITION ***/ + }else if(hexV.currentRow === hexV.rows){ + hexV.origin.push({x: center.x, y: center.y + hexV.a});//hex bottom + if(hexV.newHex === true){ + hexV.origin.push({x: center.x - 0.5*hexV.s - hexV.s, y: center.y - hexV.r});//hex top left + hexV.origin.push({x: center.x - 0.5*hexV.s - hexV.s, y: center.y + hexV.r});//hex bottom left + } + + if(i === hexV.hexCount - 1){ + hexV.origin.push({x: center.x + 2.5*hexV.s - hexV.s, y: center.y + hexV.r});//hex bottom right + if(i === hexV.endHex-1 || hexV.origin[i+1-hexV.cols].type === null){ + hexV.origin.push({x: center.x + 2.5*hexV.s - hexV.s, y: center.y - hexV.r});//hex top right + } + } + + if(hexV.origin[i-1].type === null){ + if(hexV.subHex % 2 === 0){ + hexV.origin.push({x: center.x - 0.5*hexV.s - hexV.s, y: center.y + hexV.r});//hex bottom left + } + } + + if(hexV.origin[i+1].type === null){ + if(hexV.subHex % 2 === 0){ + hexV.origin.push({x: center.x + 2.5*hexV.s - hexV.s, y: center.y + hexV.r});//hex bottom right + } + } + + // 1-1blank-2-5blank-3-3blank-2 + //6cols - 7 - 2blank - 4 + }//end if + }//end if + + if(hexV.newHex === true){ + hexV.newHex = false; + } + + hexV.subHex++; + + if(i === hexV.endHex-1){ + hexV.newHex = true; + hexV.subHex = 1; + hexV.endHex = hexV.endHex + hexV.cols; + hexV.currentRow++; + } + } + } + hexV.adjacencyInit = true;//this has now been initialized. +} + + + +function getselectedHex(hexV){ + if(hexV.adjacencyInit === false){ + calculateAdjacentOrigins(hexV); + } + + //*** This function starts by calculating the points of origin for adjacent hexagons that have yet to be factored. It then runs through the origin array and applies the distance formula between each set of coordinates and the current mouse position. The shortest length is considered the 'selection', as noted in the logic below ***// + + for(n = 0; n < hexV.origin.length; n++){ + this.original = hexV.origin[n]; + if(n === 0){//Initiate the calculations... + this.minDistance = /*Math.floor*/(Math.sqrt( (original.x - hexV.mousePos.x)*(original.x - hexV.mousePos.x) + (original.y - hexV.mousePos.y)*(original.y - hexV.mousePos.y) ) ); + this.selected = hexV.origin[n]; + }else{//grab the next distance calculation... + this.centerDistance = /*Math.floor*/(Math.sqrt( (original.x - hexV.mousePos.x)*(original.x - hexV.mousePos.x) + (original.y - hexV.mousePos.y)*(original.y - hexV.mousePos.y) ) ); + if(this.centerDistance < this.minDistance){//Compare the two calculations... + this.minDistance = this.centerDistance;//if comparison is true, establish a new minimum distance... + this.selected = hexV.origin[n];//...and prime the selected parameter. + } + } + } + //adjacencyInit = true; + //console.log(this.selected.name); + //console.log(this.selected.x+","+this.selected.y); + return{ + selected: this.selected + } +} + + +//drawHexes is the function that uses canvas-based methods to draw the hexagon +function drawHexes(evtinit, hexV){ + //*** This function grabs all the data that has been stored in both the grid and origin arrays and draws the hexagon on the canvas element. ***// + //The evtinit parameter is used to decide whether or not certain elements should be drawn. This is relevant when a user may choose to move their cursor off the canvas element. + + if(evtinit === 1){ + this.selectedHex = getselectedHex(hexV); + } + + for (i = 0; i < hexV.grid.length; i++){//loop through each set of side vertices stored in the grid array + if(hexV.origin[i].type !== null){//check to see if the corresponding hexagon type hasn't been declared a null by the user + this.vertices = hexV.grid[i]; + + hexV.context.beginPath(vertices.v1.x, vertices.v1.y); + hexV.context.moveTo(vertices.v2.x, vertices.v2.y); + hexV.context.lineTo(vertices.v3.x, vertices.v3.y); + hexV.context.lineTo(vertices.v4.x, vertices.v4.y); + hexV.context.lineTo(vertices.v5.x, vertices.v5.y); + hexV.context.lineTo(vertices.v6.x, vertices.v6.y); + hexV.context.lineTo(vertices.v1.x, vertices.v1.y); + hexV.context.closePath(); + hexV.context.lineWidth = 1; + if(evtinit === 1){ + //need to factor overlapping lines + //console.log(this.selectedHex.selected.x); + if(hexV.origin[i] === this.selectedHex.selected){ + hexV.grid['hover'] = this.vertices; + }else{ + hexV.context.strokeStyle = 'grey'; + } + }else{ + hexV.context.strokeStyle = 'grey'; + } + hexV.context.fillStyle='#ebebe0'; + hexV.context.fill(); + hexV.context.stroke(); + + /*if(evtinit === 1){ + hexV.context.fillStyle = "black"; + hexV.context.font = "18px arial"; + hexV.context.fillText(i, hexV.origin[i].x, hexV.origin[i].y); + }*/ + } + } + if(hexV.grid['selected'] != null){ + this.vertices = hexV.grid['selected']; + hexV.context.beginPath(vertices.v1.x, vertices.v1.y); + hexV.context.moveTo(vertices.v2.x, vertices.v2.y); + hexV.context.lineTo(vertices.v3.x, vertices.v3.y); + hexV.context.lineTo(vertices.v4.x, vertices.v4.y); + hexV.context.lineTo(vertices.v5.x, vertices.v5.y); + hexV.context.lineTo(vertices.v6.x, vertices.v6.y); + hexV.context.lineTo(vertices.v1.x, vertices.v1.y); + hexV.context.closePath(); + hexV.context.lineWidth = 1; + hexV.context.fillStyle='#d7d7c1'; + hexV.context.fill(); + hexV.context.stroke(); + } + if(hexV.grid['hover'] != null){ + this.vertices = hexV.grid['hover']; + hexV.context.beginPath(vertices.v1.x, vertices.v1.y); + hexV.context.moveTo(vertices.v2.x, vertices.v2.y); + hexV.context.lineTo(vertices.v3.x, vertices.v3.y); + hexV.context.lineTo(vertices.v4.x, vertices.v4.y); + hexV.context.lineTo(vertices.v5.x, vertices.v5.y); + hexV.context.lineTo(vertices.v6.x, vertices.v6.y); + hexV.context.lineTo(vertices.v1.x, vertices.v1.y); + hexV.context.closePath(); + hexV.context.lineWidth = 1; + hexV.context.strokeStyle='black'; + hexV.context.stroke(); + } + if(hexV.drawOrigins === true){ + for(n = 0; n < hexV.origin.length;n++){ + hexV.context.beginPath(); + hexV.context.arc(hexV.origin[n].x, hexV.origin[n].y, 1, 0, 2*Math.PI, false); + hexV.context.fillStyle = 'black'; + hexV.context.fill(); + hexV.context.strokeStyle = 'black'; + hexV.context.stroke(); + } + } + if(evtinit === 1){ + for(j = 0; j < hexV.origin.length; j++){ + if(hexV.hexLines === true && j < hexV.hexCount){ + hexV.context.beginPath(); + hexV.context.moveTo(hexV.origin[j].x, hexV.origin[j].y); + hexV.context.lineTo(hexV.mousePos.x, hexV.mousePos.y); + hexV.context.closePath(); + hexV.context.lineWidth = 1; + hexV.context.strokeStyle = 'black'; + hexV.context.stroke(); + } + if(hexV.adjLines === true && j >= hexV.hexCount){ + hexV.context.beginPath(); + hexV.context.moveTo(hexV.origin[j].x, hexV.origin[j].y); + hexV.context.lineTo(hexV.mousePos.x, hexV.mousePos.y); + hexV.context.closePath(); + hexV.context.lineWidth = 1; + hexV.context.strokeStyle = 'black'; + hexV.context.stroke(); + } + } + } + hexV.grid['hover'] = null; +} + + + +function getMousePos(canvas, evt, hexV) { + hexV.rect = hexV.canvas.getBoundingClientRect(); + return { + x: Math.floor((evt.clientX-hexV.rect.left)/(hexV.rect.right-hexV.rect.left)*hexV.canvas.width), + y: Math.floor((evt.clientY-hexV.rect.top)/(hexV.rect.bottom-hexV.rect.top)*hexV.canvas.height) + }; +} + + + +//** Page specific functions **// +slider_function = function(hexV){ + hexContainer = []; + hexV.s = parseInt(document.getElementById("sizeslider").value); + hexV.h = (Math.sin((30*(Math.PI/180))) * hexV.s); + hexV.r = (Math.cos((30*(Math.PI/180))) * hexV.s); + hexV.a = 2*hexV.r; + hexV.hexCount2 = hexV.hexCount; + + hexV.origin2 = []; + + hexV.hexCount = 0; + hexV.newHex = true; + hexV.currentRow = 1; + hexV.adjacencyInit = false; + + hexV.x = hexV.actualx; + hexV.y = hexV.actualy; + hexV.counter = 1; + hexV.rowswitch = false; + hexV.rowinit = false; + hexV.init = false; + + for(c = 0; c < hexV.hexCount2; c++){ + hexV.origin2[c] = hexV.origin[c]; + } + + hexV.mousePos; + hexV.grid = []; + hexV.origin = []; + + for(c = 0; c < hexV.hexCount2; c++) + { + if(hexV.origin2[c].type !== null){ + hexContainer[c] = new hex(hexV, "tile"+c); + }else{ + hexContainer[c] = new hex(hexV, "tile"+c, null); + } + //console.log(hexV.hexCount); + } + hexV.origin2 = []; + calculateAdjacentOrigins(hexV); + hexV.context.clearRect(0, 0, hexV.canvas.width, hexV.canvas.height); + drawHexes(0,hexV); +} + + + +trace_Adj = function(hexV){ + if(hexV.adjLines === false){ + hexV.adjLines = true; + document.getElementById("traceAdjOrig").value = "Disable Adjacency Trace"; + }else{ + hexV.adjLines = false; + document.getElementById("traceAdjOrig").value = "Trace Adjacency Lines"; + } +} + +trace_Orig = function(hexV){ + if(hexV.hexLines === false){ + hexV.hexLines = true; + document.getElementById("traceOrig").value = 'Disable Origin Trace'; + }else{ + hexV.hexLines = false; + document.getElementById("traceOrig").value = 'Trace Origin Lines'; + } +} + + + +add_Hex = function(hexV, type){ + hexV.origin2 = []; + + for(c = 0; c < hexV.hexCount;c++){ + hexV.origin2[c] = hexV.origin[c]; + } + + hexContainer = []; + hexV.hexCount2 = hexV.hexCount; + hexV.hexCount = 0; + hexV.newHex = true; + hexV.currentRow = 1; + hexV.adjacencyInit = false; + + hexV.x = hexV.actualx; + hexV.y = hexV.actualy; + hexV.counter = 1; + hexV.rowswitch = false; + hexV.rowinit = false; + hexV.init = false; + + hexV.mousePos; + hexV.grid = []; + hexV.origin = []; + + for(c = 0; c < hexV.hexCount2;c++){ + if(hexV.origin2[c].type === null){ + hexContainer[c] = new hex(hexV, c, null); + }else{ + hexContainer[c] = new hex(hexV, "tile"+c); + } + } + + if(type === null){ + hexContainer[hexV.hexCount+1] = new hex(hexV, "tile"+(hexV.hexCount+1), null); + }else{ + hexContainer[hexV.hexCount+1] = new hex(hexV, "tile"+(hexV.hexCount+1)); + } + + hexV.origin2 = []; + + calculateAdjacentOrigins(hexV); + hexV.context.clearRect(0, 0, hexV.canvas.width, hexV.canvas.height); + drawHexes(0, hexV); +} + + + +remove_Hex = function(hexV){ + if(hexV.hexCount > 1){ + hexV.origin2 = []; + + for(c = 0; c < hexV.hexCount;c++){ + hexV.origin2[c] = hexV.origin[c]; + } + + hexContainer = []; + hexV.hexCount2 = hexV.hexCount-1; + hexV.hexCount = 0; + hexV.newHex = true; + hexV.currentRow = 1; + hexV.adjacencyInit = false; + + hexV.x = hexV.actualx; + hexV.y = hexV.actualy; + hexV.counter = 1; + hexV.rowswitch = false; + hexV.rowinit = false; + hexV.init = false; + + hexV.mousePos; + hexV.grid = []; + hexV.origin = []; + + for(c = 0; c < hexV.hexCount2;c++){ + if(hexV.origin2[c].type === null){ + hexContainer[c] = new hex(hexV, c, null); + }else{ + hexContainer[c] = new hex(hexV, "tile"+c); + } + } + hexV.origin2 = []; + calculateAdjacentOrigins(hexV); + hexV.context.clearRect(0, 0, hexV.canvas.width, hexV.canvas.height); + drawHexes(0, hexV); + } +} + + + +draw_Origin = function(hexV){ + if(hexV.drawOrigins === false){ + hexV.drawOrigins = true; + document.getElementById("originDisplay").value = 'Hide Points of Origin'; + }else{ + hexV.drawOrigins = false; + document.getElementById("originDisplay").value = 'Show Points of Origin'; + } + hexV.context.clearRect(0, 0, hexV.canvas.width, hexV.canvas.height); + drawHexes(0, hexV); +} + + + +add_Column = function(hexV){ + hexV.hexCount2 = hexV.hexCount; + hexV.origin2 = []; + + hexV.hexCount = 0; + hexV.newHex = true; + hexV.cols = hexV.cols + 1; + hexV.currentRow = 1; + hexV.adjacencyInit = false; + + hexV.x = hexV.actualx; + hexV.y = hexV.actualy; + hexV.counter = 1; + hexV.rowswitch = false; + hexV.rowinit = false; + hexV.init = false; + + for(c = 0; c < hexV.hexCount2; c++){ + hexV.origin2[c] = hexV.origin[c]; + } + + hexV.mousePos; + hexV.grid = []; + hexV.origin = []; + + for(c = 0; c < hexV.hexCount2; c++) + { + if(hexV.origin2[c].type !== null){ + hexContainer[c] = new hex(hexV, "tile"+c); + }else{ + hexContainer[c] = new hex(hexV, "tile"+c, null); + } + //console.log(hexV.hexCount); + } + hexV.origin2 = []; + calculateAdjacentOrigins(hexV); + hexV.context.clearRect(0, 0, hexV.canvas.width, hexV.canvas.height); + drawHexes(0, hexV); +} + + + +remove_Column = function(hexV){ + if(hexV.cols > 1){ + hexV.hexCount2 = hexV.hexCount; + hexV.origin2 = []; + + hexV.hexCount = 0; + hexV.newHex = true; + hexV.cols = hexV.cols - 1; + hexV.currentRow = 1; + hexV.adjacencyInit = false; + + hexV.x = hexV.actualx; + hexV.y = hexV.actualy; + hexV.counter = 1; + hexV.rowswitch = false; + hexV.rowinit = false; + hexV.init = false; + + for(c = 0; c < hexV.hexCount2; c++){ + hexV.origin2[c] = hexV.origin[c]; + } + + hexV.mousePos; + hexV.grid = []; + hexV.origin = []; + + for(c = 0; c < hexV.hexCount2; c++) + { + if(hexV.origin2[c].type !== null){ + hexContainer[c] = new hex(hexV, "tile"+c); + }else{ + hexContainer[c] = new hex(hexV, "tile"+c, null); + } + //console.log(hexV.hexCount); + } + hexV.origin2 = []; + calculateAdjacentOrigins(hexV); + hexV.context.clearRect(0, 0, hexV.canvas.width, hexV.canvas.height); + drawHexes(0, hexV); + } +} diff --git a/projects/grid/index.php b/projects/grid/index.php new file mode 100644 index 0000000..618d38e --- /dev/null +++ b/projects/grid/index.php @@ -0,0 +1,144 @@ + +
+
+
+
+
+

Preface

+
+

+ Having recently finished my Master's degree, it is good to look back and reflect on all the pieces that compose this accomplishment. My educational journey began January 2015. This first semester consisted of working on prerequisite courses to begin working on the degree I was seeking - an Associates of Applied Sciences for web development and programming. Motivation for pursuing this was simply to learn how to code. Skill was quickly formed from that motivation. +

+

+ Fall 2016 had me taking a Web Scripting course. The only scripting programming language I had prior experience with was PhP. This course would introduce Javascript. Web Scripting had us implementing simple things such as client-side form validation or simple canvas animations. +

+

+ It was during this course that I started and finished a project that I am still proud of to this day. It was the first time I dug real deep to become intimate with a language while applying some other facet of my studies - The functions learned through the trigonometry course I was also taking. These functions provided the framework to scaffold atop to the task. +

+

+ This project went beyond the expectations of a student of Web Scripting. It produced something tangible in terms representing data in a dynamic way. It involved implementing a lot of components that would in turn produce more components to build the larger framework. +

+

+ What is this project? It is a dynamically generated hexagonal grid which can be interacted with via mouse input. Think of the layer on the map of the user-interface for a video game like Civilization. The end result of my efforts cumulated in a Javascript file getting close to 800 lines of code - something significant for an Alan who only had one semester prior of proper programming! +

+

+ A live version of the script is below. In addition to the buttons included below the visual panel, note that the grid responds to mouse input on both mouse-over and by tapping/clicking on it. +

+ +
+
+
+

Javascript: Hexagon Grid

+
+
+ +
+
+

Grid Controls:

+
+
    +
  • +
    +
    +
  • +
+
    +
  • +
    + +
  • + +
  • +
    + +
  • + +
  • +
    +
    +
  • +
+ +
    +
  • +
    + +
  • + +
  • +
    +
    +
  • + +
  • +
    + +
  • +
+
+
    +
  • +
    + +
  • + +
  • +
    + +
  • +
+
+ + +
+
+

Concluding notes

+

+ I've learned a lot since initially making this script. This hex-grid was developed before a time I even knew what object-oriented programming was. The patterns I employed to solve the task were based on primitive approaches I only knew at the time. I have an old writing designed for an older portfolio website which describes how it works. Not understanding the prototyping nature of javascript, I tried my best to describe it as a set of function calls operating on a set of arrays; a lot of what was written here showed wider lack of understanding. +

+

+ The design of the script itself shows a lack of regard for modularity. Indeed, I've done a little bit of refactoring to open options for hosting it on this page. Viewing the unedited version of the script can happen via this website's repository, specifically here at commit hash 644aa33. Note that the refactoring I've done is fairly minimal. A key reason for this is that this project warrants a fresh restart. +

+

+ Through the live script, I've left in place some buttons that act as a tool to show how it works through interaction. These tools will inform the user that a brute-force application of the closest pair algorithm is in use. If I decide to re-implement this project, I'll certainly be taking advantage of the divide and conquer algorithm that I've since learned. +

+

+ I may revisited this page in the future. I would like to add a section describing the logical sequence of events in terms of how it works, (just described broadly/ambiguously). +

+
+ +
+
+ + diff --git a/projects/index.php b/projects/index.php new file mode 100644 index 0000000..b278a2a --- /dev/null +++ b/projects/index.php @@ -0,0 +1,77 @@ + +
+
+

Alan McKay

+
+ +
+ + + + diff --git a/projects/organization/index.php b/projects/organization/index.php new file mode 100644 index 0000000..2c3c005 --- /dev/null +++ b/projects/organization/index.php @@ -0,0 +1,471 @@ + +
+
+
+
+
+

Preface

+
+

Pedagogy: College Education and Computer Science

+

+ Upon concluding the first semester of grad-school, I was asked to take on a sole-responsibility role to teach Computer Organization during the summer semester. As someone who had only been in grad-school for five months, I took pride that I was offered the position. I must have impressed as a TA. Being a non-traditional student afforded me valuable life experience which contributed to confidence as both an individual and an instructor. My diverse background experience also lent well to being able to communicate and teach using a multitude of different approaches. +

+ +

+ My brief stint as a PhD student has shown me that the fact the university is an R1 school likely also played a role in my selection - no tenured faculty member wanted to take on the task and a fresh grad student is low in the rung of production for what the school ultimately values - research. +

+ +

+ Regardless of motive of selection, things aligned with my motives as a student. I did not pursue any of my degrees, (Master's included), for the sake of getting my name on a paper. I attended these educational institutions to learn and to satisfy my curiosity of the discipline. This curiosity extended to wanting to learn how to effectively teach the subject. +

+ +

+ Computer Science is a hard discipline to learn. I pose that most have difficulty teaching the discipline. Frustratingly, most professors have difficulty teaching the discipline - Experts of the field! Why is this? Intuition built by my experiences has shown that most professors are post-doc researchers who are obligated to teach. As alluded above, their motives don't often align with quality instruction. To teach a subject is an annoyance that comes with the job. Anyone who has gone to college, regardless of discipline, will likely agree with this assertion whilst mentally (re)evaluating majority of their professors. Doubly so for those that are tenured. +

+ +

+ Who is at a disadvantage through this environment? What type of student is at risk here? These questions are too general for the focus of this writing, but I invite the reader to linger and muse on them. I will make the following assertions based on my experiences which should also be considered: +

    +
  • + In the domain of computer science, there are three different types of students: +
      +
    • + There are those who are studying a different discipline who are required to take a CS course as a prerequisite. +
    • +
    • + There are those who have heard jobs related to the field pay well, and thus are studying on the prospect of future paycheck. +
    • +
    • + Finally, there are the individuals who are genuinely curious of the subject. +
    • +
    +
  • +
  • + An individual can initially be one of the former two and eventually be molded to fit the later of the three categories. +
  • +
  • + Students who are genuinely curious of the subject will succeed. The definition of success is that they will get a degree and they will have a solid and flexible intuition of the machinations of the discipline. +
      +
    • + The students in the other categories will get just a degree. +
    • +
    +
  • +
  • + Students applying for a a job related to the field of computer science will have a technical quiz during an interview. This is rare; I know of no other disciplines that exhibit this. A doctor who is applying for work at a practice does not have to recite information and procedure they learned as a pre-med student. A computer science student needs to memorize what they've learned in data structures, despite of the fact that as a software developer you have access to a wide range of resources to help solve problems. +
      +
    • + The fact this is how things exist must be due to the fact that students of the above mentioned second category exist and need to be filtered. +
    • +
    +
  • +
  • + No instructor, myself included, can wholly prepare an individual for these technical quizzes. It is up to the individual to take a vested interest in the subject - this involves taking it upon themselves to truly comprehend a lot of the core concepts that are a part of this discipline. +
  • +
+

+ +

+ The fact that technical quizzes exist in the interview stage implies that a lifetime of irreversible damage has been done within the instructional system. Some will say that instructors aren't doing a good enough job of filtering out those who don't do well enough. I will instead assert that instructors aren't doing a good enough job teaching the material to a wider range of individuals. +

+ +
+ +

+ Loops are easy. They're a concept taught early in the stage of programming. They are also super frustrating as a beginner; an esoteric construct that goes against the grain of normal human experience. This is said emphasizing the level of granularity required to convey that some thing needs to be repeated. I almost dropped out of community college because of loops. My instructor simply told me the surface facts of a loop and did not encourage any form of reflection. I was given a loop of a specific problem, the pieces that describe the condition of execution were highlighted, and it was noted that the body acted as the logic that needed to be repeated. There was no exploration of how to determine what belongs in the body and what doesn't; or how to fine-tune the condition of execution. +

+ +

+ Loops are easy in the context of studying Computer Science. Due to the fact a loop is relatively easy, it becomes intuitive for anyone who has enough experience to be able to teach. It's difficult to teach an intuitive concept to someone who does not have that intuit. My approach has always been through analog. This begins with discussion of the various iterative happenings that may occur through life and then discussion of how to encode them. After this discussion, examples are given. +

+ +

+ Once examples are given, I've always been keen to sharing alternatives to a given implementation of a loop. A for-loop absolutely should be taught in tandem with a while loop. This should occur with a light exposure to recursion. Various conditions of execution should be discussed here as well. Analog is not only given through context of every day living but also with respect to evaluating the loops themselves! +

+
+
+ +
+def sum_a(aList):
+    total = 0
+    for value in aList:
+        total += value
+    return total
+
+def sum_b(aList):
+    total = 0
+    length = len(aList)
+    for index in range(0,length):
+        value = aList[index]
+        total += value
+    return total
+
+def sum_c(aList):
+    total = 0
+    index = 0
+    length = len(aList)
+    while index < length:
+        value = aList[index]
+        total = total + value
+        index = index + 1
+    return total
+
+def sum_d(aList):
+    listCopy = list(aList)
+    total = 0
+    length = len(listCopy)
+    while length > 0:
+        value = listCopy.pop()
+        total = total + value
+        length = len(listCopy)
+    return total
+
+def sum_e(aList):
+    listCopy = list(aList)
+    total = 0
+    while len(listCopy) > 0:
+        value = listCopy.pop()
+        total = total + value
+    return total
+
+def sum_f(aList):
+    total = sum(aList) #python's built-in sum function
+    return total
+
+
+
+ A set of functions which take a list as an input and sums their values. Each function accomplishes the same task. This illustrates that there are many different approaches to solving a given problem - a notion that is not intuitive to one first experiencing the rigidity of programming. +
+
+ +
+ +
+def sum_g(aList):
+    return sum(alist)
+
+def sum_h_helper(aList,index):
+    length = len(aList)
+    if index >= length:
+        return 0
+    else:
+        value = aList[index]
+        return value + sum_h_helper(aList,index + 1)
+
+def sum_h(aList):
+    return sum_h_helper(aList,0)
+
+def sum_i_helper(aList,index):
+    length = len(aList)
+    value = aList[index]
+    if index >= (length - 1):
+        return value
+    else:
+        return value + sum_i_helper(aList,index + 1)
+
+def sum_i(aList):
+    if len(aList) > 0:
+        return sum_i_helper(aList,0)
+    else:
+        return 0
+
+def sum_j_helper(aList,value):
+    length = len(aList)
+    if length <= 0:
+        return value
+    else:
+        new_value = aList.pop()
+        return value + sum_j_helper(aList,new_value)
+
+def sum_j(aList):
+    listCopy = list(aList)
+    if len(listCopy) > 0:
+        value = listCopy.pop()
+        return sum_j_helper(listCopy,value)
+    else:
+        return 0
+
+def sum_k(aList):
+    if len(aList) <= 0:
+        return 0
+    else:
+        value = aList[0]
+        if len(aList) <= 1:
+            return value
+        else:
+            other_values = aList[1:]
+            return value + sum_k(other_values)
+
+def sum_l(aList):
+    if len(aList) <= 0:
+        return 0
+    elif len(aList) <= 1:
+        return aList[0]
+    else:
+        return aList[0] + sum_l(aList[1:])
+
+
+
+ Even more functions which take a list as an input and sums their values. This time leveraging the functional paradigm of programming. +
+
+
+

+ The usage of analogous example shows that the same problem can have many solutions. This allows a student to view solutions through different perspectives. This abstracts the study to what it is - how to abstract the algorithm. It also encourages a student to think outside of the box and to be creative in their own solutions. This helps foster one of the most crucial traits of being successful within this study - being curious while thinking critically.** +

+ +

+ A student who isn't genuinely interested in the study is less likely to apply critical thought. They may have been boxed into a narrow view of the study where no interest is curated. This type of student is more likely to find solutions on stack overflow and not learn from them. This type of student is more likely to learn what little they need to from generative tools like Chat GPT. +

+ +

+ At the end of day, regardless of which category a student belongs, they are paying good money for the education. They are taking a class to learn the subject involved. The goal of an educator should be to engage with the students' expectations and maximize the value of the experience. Value of the experience is subjective to each individual, but given the context of school being a place to learn, my efforts are focused on providing the value of knowing the subject. Within the example of teaching loops, I take extra effort in providing the value of knowing what a loop is and does by inviting a student to be a critical thinker. +

+ +

+ What is it that a paying student seeks to learn by taking Computer Organization? Any student who is paying to be in the class is likely a Computer Science major. Many would describe it as a weed-out course that teaches a programming paradigm that is antiquated. A generic student may not be able to justify why it has this perception, but those who have a genuine interest will tell you that it's due to a combination of Moore's law and compiler optimization techniques. +

+ +

+ To answer this question, post-doc researcher teaching the course will rattle off a set of objectives set in place by the institution, likely falling upon the course description provided by the university. They may say: +

+ By the end of the course, you will be able to explain how a computer works, from “bottom to top”: transistors to digital logic to assembly language (e.g., x86, MIPS) to the programming language (e.g., Javascript, Java, Python). To do so, you will uncover how data is represented and stored in digital computers. You will learn how to write programs in an assembly language made up of basic instructions (e.g., multiply two numbers, write a number to memory) that a computer can understand. You will learn how to construct and use the basic blocks of a computer processor: arithmetic, memories, I/O, and gates built using digital logic (circuits whose wires hold 1’s and 0’s). You will use design tools to build a CPU that runs assembly programs. Finally, you will analyze computer systems with respect to engineering metrics like cost and performance and discuss factors, such as data locality and parallelism, that affect these metrics. - UIowa.edu +
+ This course description was indeed the ultimate appeal for continuing my education from community college into university. Having learned how to program at a higher-level, I was interested in knowing what occurs close to the circuit; I wanted to learn the "1's and 0's," as it is said. +

+ +

+ I do not believe the above course description does the class justice. This course brings together and synthesizes Data and Discrete Structures. Data Structures formally introduces the concept of data representation by exploring alternative implementations of algorithms that are commonly abstracted by higher-level programming languages. Discrete Structures provides the mathematical background to describe the actual representation of data as it exists within the logical circuitry. This synthesis is more than the sum of its parts. The propositional logic used to describe the circuitry is represented as a certain set of symbols which is then translated to the symbolic diagrams which compose the logical circuitry. The assembly languages translated to this circuitry alludes that there may exist any amount of layers to translate a problem represented in one language to another. This course implicitly informs any curious student that the algorithm is programming-language agnostic. Wholly understanding this course trivializes studying Theory of Computation; this course teaches a student a model of computation! +

+ +

+ The context provided by this preface is exempt from the post-course report I've written for the university. The context provided by this preface hopefully conveys to the reader that I tried to maximize the value of of learning the subject to my students. The extra value was explicitly relayed to my students with an emphasis that what we are building a model of computation. This gives extra context to the student by pushing the course beyond the study of some antiquated paradigm. +

+ +
+
+
+

Teaching: Computer Organization

+
+

+ The following is the report I submitted to the university upon the conclusion of hosting Computer Organization as a sole-responsibility instructor. +

+

CS:2630 - Context and Methodology

+

+ People tend to have the perception of Computer Organization being a difficult class involving a lot of grit. This perception typically dominates discussion about the topic. Emphasis on key concepts can help alleviate these points. Specifically, directly correlating concepts of this course to concepts of previous courses. +

+ +

+ Personally, it was Computer Organization that cemented the idea that learning a new programming language need not be daunting. The data and functions described throughout this course ultimately represent the same functions and data described in the higher-level languages of courses prior. Knowing this, the primary focus while teaching this course was to emphasize this fact. The means of describing the data in machine language in addition to the propositional logic used to manipulate that data serve as the model of computation in which the higher-level languages are built. This theme was highlighted quite often. Hopefully this made the course less gritty as concepts were built up from propositional logic to logical gates, logical circuits, control units, and assembly. +

+ +

+ Recognizing this scaffolding does require familiarity with some higher-level language. To fully appreciate it, it also requires being comfortable* with some higher-level language. This is based on my experience as an undergraduate student. Stepping into university, my context was being comfortable with a handful of high-level scripting languages for web development. One key observation during the onset of my university experience was noticing that, within various courses that followed the introductory programming course, a lot of students were still gripping with the core concept of learning how to program. This tended to prevent them from understanding and appreciating the primary concepts being taught in said courses. This is especially true in data structures. +

+ +

+ The university of Iowa has an advantage over the university I attended in the fact that Data Structures is a prerequisite to Computer Organization, as opposed to a corequisite. The point made in the previous paragraph still holds true regardless of this fact. The advantage becomes apparent when looking over the lab assignments given during this session. +

+ +

+ As indicated in the topic outline above, the first couple of weeks were spent discussing the representation of data. Something that stuck out to me in the (optional) textbook associated with this session is the following aside: +

+ For converting larger values between decimal and hexadecimal, it is best to let a computer or calculator do the work. There are numerous tools that can do this. One simple way is to use any of the standard search engines, with queries such as
+ Convert 0xabcd to decimal
+ Or
+ 123 in hex
+
+

+ +

+ It is amazing that this was suggested in such a textbook, especially one that is of “a programmer’s perspective.” Knowing this cheeky blurb existed, the first lab was one to practice and test the programming skills of the student. They were to implement the functions which perform the conversions indicated by the aside in some high-level language of their choice. It was made apparent to those given the assignment that not only were they being gauged on the correctness of the functions but their coding styles were also being gleaned. Additionally, it was stated that should they find and use a solution on stack-overflow or an alternative that taking such a measure likely was indicative of a lack of problem-solving skills. It was made apparent that not taking the time to shore up those skills, (through this lab), that this lack of skill will make the course more difficult as time progresses and as more difficult problems are presented; problems that will not be so easy to find using said sources. +

+ +

+ Looking at the subsequent labs, one may surmise that the reason it may be difficult finding solutions would be on account of using both a relatively obscure assembly language, MARIE, and an oversaturation of programs solved in higher-level languages, as per the lab assignments pertaining to MIPS. +

+ +

+ While the MARIE programs acted as a means for one to become comfortable coding assembly, (while also being able to track the control and flow of data within the architecture), the MIPS programs acted as the means to cement the idea that the data and functions discussed throughout this course are the very same as those described in other languages. As the first lab asked to represent the algorithm which was applied by pen and paper in earlier lecture assignments and as it was through lecture that various representations of both data and functions were explored, the MIPS labs asked to represent various algorithms that should have been learned through data structures. Each facet drawing heavily from prior courses, something that was emphasized during instruction. +

+ +

+ The submission policy outlined in the syllabus is an indication that should a student not be solid in any of these fundamental areas they will have the opportunity to make it up. What is not shown in the syllabus is the test policy. The tests given were take-home tests where a few days were given to complete them. They were essentially glorified labs with less focus on a specific topic. The tests were designed in a manner where, should one be up to speed with the course, it should take one and a half to two hours to complete. Should one not be up to speed, then they will need to invest more time to complete the test; a last chance to put in the time and learn the topics covered. +

+ +
+ +

+ The following pages contain the syllabus, lecture assignments, and lab assignments in the order in which they were given, followed by the two tests. This was the first class I have taught. My initial intentions were to give smaller lecture assignments following each lecture. My intentions were to also give a lab assignment every week. It did not turn out this way. To anyone looking this over, think of more lecture assignments to give. If I am to teach this course again, I will be doing that. As for the lab assignments, time was better spent giving lecture, as opposed to dedicating work time for the labs. This course was taught during the summer session, and it is my opinion that prolonged lectures were too dense. I instead opted to give shorter lectures and shoring them up with a supplemental 20–30-minute follow-up video which retouched lecture concepts and provided more examples or contexts. Labs were kicked off during the last class day of the week where the students had the entire weekend to complete. +

+ +

+ If there are any lingering questions, feel free to contact me. +

+ +
+ +

+ * What it means to be comfortable with a programming language deserves its own essay. I will leave it to your own judgement to decide what this means, or I will leave an open invitation to personal conversation on the matter. +

+ +
+
+

Concluding notes

+

+ There is hesitation releasing the assignments as described in the concluding paragraph the report. This is in part due to the ambiguity of the University's copyright policy as it is currently written: The University will not claim pedagogical related materials but any production expected of a given position can still be claimed. Their literature doesn't discuss the contradiction this poses. +

+ +

+ As such, time will be needed to generalize these assignments. Once finished, they will be posted within this concluding section. +

+ +
+ +
+

+ ** Engaging material isn't the sole component of fostering the development and application of critical thought. An engaging instructor is also an important aspect. Effective communication requires feedback from all parties involved. As an instructor, I would seek input and feedback from students as individuals. This is difficult in the lecture setting, needing to be general enough to adhere to the class as a whole. This focus on individuality manifests whilst hosting labs or while engaging with the students who themselves engage with the instructor. +

+ +

+ I had no experience programming in MIPS prior to this teaching assignment. The last time I implemented any involving algorithm in assembly was when I initially took this course three years prior. My studying of MIPs occurred one or two days prior to giving the lecture. Was it laziness that caused me to take this approach? +

+ +

+ The answer to the above question is partially yes. This laziness was a convenience afforded by my comfort in learning something new within this domain. This comfort is on account of my ability to critically think. Learning a new language is made trivial by this critical thought and through my experiences abstracting other languages. This 'laziness' affords the following benefits to the students: +

    +
  • + Having a fresh take on a language highlights some non-intuitive concepts someone who is more experienced may take for granted. This allowed me to know to take the time to elaborate on something that another instructor may have glossed over. +
  • + +
  • + Being inexperienced in the nuances of a language provided opportunities for me to make an unexpected mistake! This provides opportunity for students to witness how I think critically by showing them how I walk through an unknown problem and apply my own problem-solving skills. +
  • +
+

+ +

+ The second bullet point is so god-damn important that it warrants appalling language in a professional writing. As a student, there is nothing more frustrating than seeing an instructor blow through some coding problem like they are a higher-being. In reality, the instructor probably has the solution key written off to the side or just memorized. This usually isn't revealed to the student, which implicitly sets an unreasonable expectation of performance. I will make a conjecture that this facade contributes to a barrier of entry that many find daunting. +

+ +

+ My lack of experience with MIPS made the course more engaging in a way that creates a sincere connection between myself and the class in general. Any act of engagement to address an individual would usually involve addressing the questions/concerns by the use of analog and nudging the individual towards the conclusion they seek. This was done by asking them a set of questions to help them make the correct conclusion on their own! I believe this helps an individual feel personal agency by abstractly exposing them to how to think critically. One of my favorite pieces of feedback through student evaluation has been, "He makes you think." +

+
+
+ +
+01    # --- --- --- --- --- --- #
+02    sum:
+03    # Two arguments $a0 (memory address of list) and $a1 (length value of list)
+04
+05    li $s0  0         #total <- 0
+06    li $s1  0         #index <- 0
+07    li $s2  $a0       #point to the base address of the array
+08
+09    # ---
+10    sum_loop_condition:
+11
+12    bge $s1 $a1 end_loop
+13
+14    mult $s2 $s1 4    #adjust index offset
+15    add  $s2 $s2 $a0  #point to the next value of the array
+16
+17    lw $s2 0($s2)     #access the value within array
+18
+19    add $s0 $s0 $s2   #add value to the total
+20
+21    addi $s1 $s1 1    #increment index
+22
+23    j sum_loop_condition
+24
+25    # ---
+26    end_loop:
+27    move $v0 $s0      #place total into return register
+28    jr $ra            #return to caller
+29    # --- --- --- --- --- --- #
+
+
+ +
+ Just another way to sum up an array of values. +
+
+
+
+
+
+ +
+
+ + diff --git a/projects/organization/sum_examples.py b/projects/organization/sum_examples.py new file mode 100644 index 0000000..c4930f8 --- /dev/null +++ b/projects/organization/sum_examples.py @@ -0,0 +1,235 @@ +def sum_a(aList): + total = 0 + for value in aList: + total += value + return total + +def sum_b(aList): + total = 0 + length = len(aList) + for index in range(0,length): + value = aList[index] + total += value + return total + +def sum_c(aList): + total = 0 + index = 0 + length = len(aList) + while index < length: + value = aList[index] + total = total + value + index = index + 1 + return total + +def sum_d(aList): + listCopy = list(aList) + total = 0 + length = len(listCopy) + while length > 0: + value = listCopy.pop() + total = total + value + length = len(listCopy) + return total + +def sum_e(aList): + listCopy = list(aList) + total = 0 + while len(listCopy) > 0: + value = listCopy.pop() + total = total + value + return total + +def sum_f(aList): + total = sum(aList) #python's built-in sum function + return total + + + +def sum_g(aList): + return sum(alist) + +def sum_h_helper(aList,index): + length = len(aList) + if index >= length: + return 0 + else: + value = aList[index] + return value + sum_h_helper(aList,index + 1) + +def sum_h(aList): + return sum_h_helper(aList,0) + +def sum_i_helper(aList,index): + length = len(aList) + value = aList[index] + if index >= (length - 1): + return value + else: + return value + sum_i_helper(aList,index + 1) + +def sum_i(aList): + if len(aList) > 0: + return sum_i_helper(aList,0) + else: + return 0 + +def sum_j_helper(aList,value): + length = len(aList) + if length <= 0: + return value + else: + new_value = aList.pop() + return value + sum_j_helper(aList,new_value) + +def sum_j(aList): + listCopy = list(aList) + if len(listCopy) > 0: + value = listCopy.pop() + return sum_j_helper(listCopy,value) + else: + return 0 + +def sum_k(aList): + if len(aList) <= 0: + return 0 + else: + value = aList[0] + if len(aList) <= 1: + return value + else: + other_values = aList[1:] + return value + sum_k(other_values) + +def sum_l(aList): + if len(aList) <= 0: + return 0 + elif len(aList) <= 1: + return aList[0] + else: + return aList[0] + sum_l(aList[1:]) + +if __name__ == "__main__": + case1 = [i for i in range(0,20)] + case2 = [i*2 for i in range(0,20)] + case3 = [i+3 for i in range(0,20)] + case4 = [i for i in range(0,20,2)] + case5 = [] + case6 = [10] + + result1 = sum(case1) + result2 = sum(case2) + result3 = sum(case3) + result4 = sum(case4) + result5 = sum(case5) + result6 = sum(case6) + + print("--- Sum A ---") + print(sum_a(case1) == result1) + print(sum_a(case2) == result2) + print(sum_a(case3) == result3) + print(sum_a(case4) == result4) + print(sum_a(case5) == result5) + print(sum_a(case6) == result6) + print() + + + print("--- Sum B ---") + print(sum_b(case1) == result1) + print(sum_b(case2) == result2) + print(sum_b(case3) == result3) + print(sum_b(case4) == result4) + print(sum_b(case5) == result5) + print(sum_b(case6) == result6) + print() + + + print("--- Sum C ---") + print(sum_c(case1) == result1) + print(sum_c(case2) == result2) + print(sum_c(case3) == result3) + print(sum_c(case4) == result4) + print(sum_c(case5) == result5) + print(sum_c(case6) == result6) + print() + + + print("--- Sum D ---") + print(sum_d(case1) == result1) + print(sum_d(case2) == result2) + print(sum_d(case3) == result3) + print(sum_d(case4) == result4) + print(sum_d(case5) == result5) + print(sum_d(case6) == result6) + print() + + + print("--- Sum E ---") + print(sum_e(case1) == result1) + print(sum_e(case2) == result2) + print(sum_e(case3) == result3) + print(sum_e(case4) == result4) + print(sum_e(case5) == result5) + print(sum_e(case6) == result6) + print() + + + print("--- Sum F ---") + print(sum_f(case1) == result1) + print(sum_f(case2) == result2) + print(sum_f(case3) == result3) + print(sum_f(case4) == result4) + print(sum_f(case5) == result5) + print(sum_f(case6) == result6) + print() + + + print("--- Sum H ---") + print(sum_h(case1) == result1) + print(sum_h(case2) == result2) + print(sum_h(case3) == result3) + print(sum_h(case4) == result4) + print(sum_h(case5) == result5) + print(sum_h(case6) == result6) + print() + + + print("--- Sum I ---") + print(sum_i(case1) == result1) + print(sum_i(case2) == result2) + print(sum_i(case3) == result3) + print(sum_i(case4) == result4) + print(sum_i(case5) == result5) + print(sum_i(case6) == result6) + print() + + + print("--- Sum J ---") + print(sum_j(case1) == result1) + print(sum_j(case2) == result2) + print(sum_j(case3) == result3) + print(sum_j(case4) == result4) + print(sum_j(case5) == result5) + print(sum_j(case6) == result6) + print() + + + print("--- Sum K ---") + print(sum_k(case1) == result1) + print(sum_k(case2) == result2) + print(sum_k(case3) == result3) + print(sum_k(case4) == result4) + print(sum_k(case5) == result5) + print(sum_k(case6) == result6) + print() + + + print("--- Sum L ---") + print(sum_l(case1) == result1) + print(sum_l(case2) == result2) + print(sum_l(case3) == result3) + print(sum_l(case4) == result4) + print(sum_l(case5) == result5) + print(sum_l(case6) == result6) + print() + diff --git a/projects/protocol/index.php b/projects/protocol/index.php new file mode 100644 index 0000000..5236e7d --- /dev/null +++ b/projects/protocol/index.php @@ -0,0 +1,183 @@ + +
+
+
+
+
+

Preface

+
+

+ The following is a summary of research conducted to help earn my Bachelor's of Science in Computer Science. The research was an exploration of a model of computation called the Population Protoocol. Those who have studied Theory of Computer Science should find it intuitive. +

+

+ A presentation was given discussing the particulars of this research and can be viewed via YouTube. General administrative information pertaining to the research and the presentation can be viewed via the University of Northern Iowa's SURP 2020 Symposium website. Finally, a repository of the work done can be viewed via my personal GitHub page. +

+
+
+
+

Research: Population Protocol

+
+

Background

+

+ The modern computer uses a specific computational model that allows it to compute the set of functions which affords the utility that its users are familiar with. This computational model is the Boolean Circuit, often associated with 0’s, 1’s, and the various circuits which manipulate these values. +

+ +

+ Another model of computation is the Turing Machine. A Turing Machine can be thought of as a set of nodes which determine the various states of a machine in conjunction with an infinite tape that allows the machine to read and write values. This model is able to compute the same set of problems as the Boolean Circuit model. +

+ +

+ A new model has recently emerged: The Population Protocol. This new model was conceived by Angluin et al. [1][2] in the mid 2000’s, the primary motivator being distributed computing. Theory has since been established, proving that the set of problems the model can solve is a subset of the previously discussed models. This subset is still powerful. Research has been done since exploring possible ways of expanding the model to afford more computational power and to find ways to make the model more reliable. +

+ +

The Model

+

+ The Population Protocol consists of a set of anonymous agents which randomly interact with each other. Each agent is a Finite State Machine, or a memory register, which holds a value from a predefined set of values. The state of an agent may change depending on an interaction with another. The conditions and results which define these special interactions are noted in a transition function. The protocol carries out these interactions within the set of agents indefinitely. +

+ +

+ Formally, a Population Protocol consists of the following elements: +

    +
  • Set Q; finite set of possible states for an agent
  • +
  • Set Σ; a finite input alphabet
  • +
  • Input map ι; an input map from Σ to Q, where ι(σ) represents the initial state of an agent whose input is σ
  • +
  • Output map ω; an output map from Q to the output range Y, where ω(q) represents the output value of an agent in state q
  • +
  • Transition function δ; a transition relation that describes how pairs of agents interact
  • +
+

+ +

Algorithms, Simulations, and Analysis

+

+ Consider a scenario in which a group of birds are given sensory tags. These sensors are very primitive and throw a flag when an individuals’ temperature reaches a certain threshold. The sensors can also communicate with each other when they are within a certain range. Does a protocol exist that allows these sensors to communicate to the population if an individual’s temperature has reached the specific threshold? +

+

+ Consider the following protocol: +

+ +
+Q = {L,H}
+Σ = {L,H}
+ι(x) = x
+ω(x) = x
+δ = {
+    (L,L) -> (L,L),
+    (L,H) -> (H,H),
+    (H,L) -> (H,H),
+    (H, H) -> (H,H)
+    }
+
+
+

+ The above protocol is equivalent to running an AND boolean operation on a population where L=1 and H=0. +

+

+ A bird’s sensor switches from L to H when it’s temperature reaches the threshold. When it interacts with another bird, it will then communicate this fact. The following is one possible set of interactions a group of 5 birds may exhibit when one has a status of H: +

+
+ A series of graphs which describe the interaction of nodes in a singular network. These interactions are highlighted by the edges the nodes share. Each subsequent graph in the series indicates the state of the network upon each interaction. +
+ Figure A. +
+
+

+ Note the above set of interactions is a subset of a set of potential interactions. Within this superset exists an infinite amount of sets which may contain an infinite amount of interactions. The fifth step highlights this reason by showing two agents have interacted with each other redundantly. That is, the interaction has no bearing on progressing the state of the population to convergence. It is by the convergence of a solution enforced by a loose fairness condition that these algorithms are evaluated and developed. +

+

+ This Population Protocol helps highlight some of the pitfalls of the model. What happens if the agent initially marked with H fails before it is able to interact with another? Fault tolerance is an aspect Dr. Berns and I are looking into. +

+
+
+

Expanded Algorithms

+

+ What if a population wants to report if a certain percentage meets a threshold? One approach is to allow each agent access to 3 finite state machines. One to track a numerator, another to track a denominator, and the third as a boolean value to determine if an agent has already been evaluated for computation. +

+ + +
+Q = {L -/-, H -/-, 0 n/m, 1 x/y} where n,m,x, y are Integers
+Σ = {L, H}
+ι(x) = x
+ω(0 n/m) = ω(1 n/m) = n/m
+δ = {
+    (H -/-,H -/-) -> (2/2, 2/2),
+    (L -/-, H -/-) -> (1/2, 1/2),
+    (H -/-, L -/-) -> (1/2, 1/2),
+    (L -/-, L -/-) -> (0/2, 0/2),
+    (0 n/m, 0 x/y) -> (0 (n+x)/(m+y), (1 (n+x)/(m+y)),
+    (0 n/m, 1 x/y) -> (0 n/m, 1 n/m)
+    }
+
+
+

+ This Algorithm can be used in conjunction with leader election and epidemics[3][4] to ensure data is ready to be collected. Once it is, the collector simply has to check the given ratio. +

+ +
+
+

+ To help better understand the concept as a whole, we have built a simulator which keeps track of the iterations of a protocol’s execution while monitoring relevant data such as redundant interactions that may contribute to developing more complex algorithms for this model. Figure B represents a heatmap of redundant interactions between agents of a population of 15. Figures A, and B both include graphs output by the simulator we have constructed. +

+
+

Future and Application

+
+
+ A graph which represents a network of nodes. Each edge connecting two nodes indicates an interaction channel. The more vibrant an edge, the more interactions occurred between the two nodes. +
+ Figure B. +
+
+

+ With the simulator we’ve built, the goal is to be able to implement and test a suite of population protocols to explore fault tolerance. Heatmap output should help us hone in on any potential bottlenecks caused by more advanced algorithms involving agents with differing roles. +

+ +

+ This specific heat map (Figure B) shows the set of agents and each potential path of interaction. Higher intensity of color between a pair of agents indicates a greater amount of redundant interactions between the two. This emphasizes the fact that an agent’s failure may cause problems in terms of the execution of the protocol as it’s information may be critical for convergence. +

+

+ Exploration of this computational model will allow us to make the most of the primitive computational power afforded by simple hardware. This can be extended to automated drone communication. Beyond development of the simulator, we would like to develop and test practical application with the usage of drones and drones communicating with sensory networks. +

+
+

References

+
    +
  • [1] Angluin, Dana ; Aspnes, James ; Diamadi, Zoë ; Fischer, Michael ; Peralta, René. Computation in networks of passively mobile finite-state sensors. Distributed Computing, 2006, Vol.18(4), pp.235-253
  • + +
  • [2] Aspnes, James & Ruppert, Eric. 2009. An Introduction to Population Protocols. Bulletin of The European Association for Theoretical Computer Science - EATCS. 93
  • + +
  • [3] Angluin, Dana ; Aspnes, James ; Eisenstat, David. Fast computation by population protocols with a leader. Distributed Computing, 2008, Vol.21(3), pp.183-199
  • + +
  • [4] Alistarh, Dan & Gelashvili, Rati. (2018). Recent Algorithmic Advances in Population Protocols. ACM SIGACT News. 49. 63-73.
  • +
+
+ +
+
+ + diff --git a/writings/safety/index.php b/projects/safety/index.php similarity index 93% rename from writings/safety/index.php rename to projects/safety/index.php index 1df2d0f..fa84c7a 100644 --- a/writings/safety/index.php +++ b/projects/safety/index.php @@ -4,20 +4,22 @@ $style = '../../style.css'; -$canonical = 'http://alanmckay.blog/writings/safety/'; +$canonical = 'https://alanmckay.blog/projects/safety/'; -$title = 'Alan McKay | Writing | Geography and Safety'; +$title = 'Alan McKay | Project | Cycling Safety Database'; -$meta['title'] = 'Alan McKay | Geography and Safety'; +$meta['title'] = 'Alan McKay | Cycling Safety Database'; -$meta['description'] = "A primary facet to continuing my education through graduate school is to hone my ability to effectively communicate with people of other disciplines. Iowa..."; +$meta['description'] = "Project description of building a PostGIS database which cumulates DOT traffic information to provide safety insights for cyclists."; -$meta['url'] = 'http://alanmckay.blog/writings/safety/'; +$meta['url'] = 'https://alanmckay.blog/projects/safety/'; + +$relative_path = "../../"; include('../../header.php'); ?> -
+
@@ -135,7 +137,7 @@ Iowa Authoritative Annual Daily Traffic (AADT) volume
  • - https://data.iowadot.gov/datasets/IowaDOT::traffic-information/about + Data Source: DOT Traffic Information
  • Data attributes and types are given in the weblink above. The important/relevant @@ -158,7 +160,7 @@ Iowa Crash Vehicle Data (SOR)

    - For the scope of this project, the activity data is gathered from a personal Strava set of activities via the - public developer API, (https://developers.strava.com/docs/reference/). + For the scope of this project, the activity data is gathered from a personal Strava set of activities via the public developer API.

    Data Prep

    Using the Strava API

    @@ -204,7 +205,7 @@ application has a unique client ID. Said application should direct a user to the following URL:

    -
    +
     https://www.strava.com/oauth/authorize?client_id=<CLIENT_ID>response_type=code&redirect_uri=<APPLICATION_LOCATION>/exchange_token&approval_prompt=force&scope=activity:<SCOPE>
     
    @@ -229,7 +230,7 @@ response with an access code to use to query the API. I.e.,

    -
    +
     curl -X POST https://www.strava.com/oauth/token \
     -F client_id=<CLIENT_ID> \
     -F client_secret=<CLIENT_SECRET> \
    @@ -241,7 +242,7 @@
                             Responds with a user access token to be used via:
                         

    -
    +
     http GET "https://www.strava.com/api/v3/athlete/activities" "Authorization: Bearer <access token>"
     
    @@ -251,7 +252,7 @@

    The above http GET query returns, by default, 100 activities. Let's say this information is piped into a json file called morerides.json. A look through the file will expose a lot more attributes and their values - than what's required of this database. The attributes are listed here: https://developers.strava.com/docs/reference/#api-Activities-getLoggedInAthleteActivities + than what's required of this database. The attributes are listed here.

    This data needs to be sanitized. A script was developed to gather this information and generate a csv file @@ -261,7 +262,7 @@ The python script developed for this project is as follows:

    -
    +
     with open('morerides.json','r') as rides_file:
         rides_data = json.load(rides_file)
     
    @@ -299,7 +300,7 @@
                             table definition can be described as:
                         

    -
    +
     CREATE TABLE cycling.activities_staging
     (
         activity_id BIGINT,
    @@ -327,7 +328,7 @@
                             The activities table is created via:
                         

    -
    +
     CREATE TABLE cycling.activities
     (
         activity_id BIGINT,
    @@ -352,7 +353,7 @@
                             And the data insertion query is as follows:
                         

    -
    +
     INSERT INTO cycling.activities(
         activity_id,
         athlete_id,
    @@ -406,7 +407,7 @@
                             The traffic volume table can be described as:
                         

    -
    +
     CREATE TABLE cycling.traffic_vol
     (
         gid INT,
    @@ -441,7 +442,7 @@
                             The following insertion query addresses these two obstacles:
                         

    -
    +
     INSERT INTO cycling.traffic_vol (gid,route_id, aadt, aadt_year, effective_start_date,
     effective_end_date,geom)
         SELECT distinct gid,route_id, aadt, aadt_year, effective_, effectiv_1,
    @@ -501,7 +502,7 @@
                             Queries gauging proximity safety can start being employed. I.e.,
                         

    -
    +
     SELECT crash.geom FROM cycling.crashes as crash, cycling.activities as activity
     WHERE ST_DWithin(crash.geom,activity.route,0.01);
     
    @@ -523,7 +524,7 @@ Consider the following query:

    -
    +
     SELECT route_id, (dp).path[1] As path_index, ST_AsTEXT((dp).geom) AS node
     FROM (SELECT route_id, ST_DumpPoints(route) AS dp FROM cycling.activities) as segments;
     
    @@ -534,7 +535,7 @@ into individual lines. This can be accomplished with the following query:

    -
    +
     SELECT lp1.route_id, st_makeline(lp1.node, lp2.node) as line FROM list_points as lp1,
     list_points as lp2 WHERE lp2.path_index - lp1.path_index = 1 AND lp1.route_id = lp2.route_id;
     
    @@ -617,26 +618,5 @@
- diff --git a/projects/social/index.php b/projects/social/index.php new file mode 100644 index 0000000..bc9d5bd --- /dev/null +++ b/projects/social/index.php @@ -0,0 +1,389 @@ + +
+
+
+
+
+

Preface

+
+

+ The final semester of my Master's degree had me taking a course called Web Mining. Admittedly, I was fatigued at the time of registering for this course, which motivated my registration as the course's description reminded me of one that I took as an undergrad student - Information Storage and Retrieval (ISR). +

+ +

+ ISR covered document representation and how to provide query mechanisms for groupings of texts. Typical search engine fodder involving scoring and term weighting which would then culminate into discussions of the vector space model. The course project was to build a crawler to collect and represent texts taken from MIT's collection of Shakespeare writings. +

+ +
+ Natural language processing; analysis of textual material by statistical, syntactic, and logical methods; retrieval systems models, dictionary construction, query processing, file structures, content analysis; automatic retrieval systems and question-answering systems; and evaluation of retrieval effectiveness. - Verbatim course description from the University of Northern Iowa +
+ +

+ ISR was fascinating as it gave insight to how complex data search can work. One invaluable resource to helping build an intuition for the subject was supplementary lecture provided by Victor Lavrenko through at his Youtube Channel. Specifically, his lecture series labeled ISR3 Vector Space Model. This supplement also complemented thorough reading from "Introduction to Information Retrieval" authored by Manning, Raghavan, and Schütze. The textbook would also be a required reading within Web Mining, where a good chunk of time was also spent discussing language models and how to represent texts. +

+ +

+ Web Mining would turn out to be a deceptive course title. It differed from ISR by the exclusion of a project in which one would build a web crawler to harvest information. The course instead revolved around reading various research papers involving the processing of big data while interpreting the results said data provided. These papers asked questions such as "How has happiness shifted since a given event?" or "How does misinformation spread throughout social media communities?" +

+ +
+ Core methods underlying development of applications on the Web; examples of relevant applications, including those pertaining to information retrieval, summarization of Web documents, and identifying social networks. - Verbatim course description from the University of Iowa +
+ +

+ To answer these types of questions, one needs a basic understanding of network science. Network science is the study of complex networks; In the context of this course, it was an application of social computing to understand how networks of humans interact. It provides the understanding of scale-free networks and how they have a power-law distribution. These were concepts unknown to me prior to taking the course, which provided a pleasant surprise in terms of giving something new and interesting to study. +

+ +

+ To provide a basic understanding, "Network Science" authored by Albert-László Barabási was used. The chapters on graph theory, random networks, and the scale-free property provide a great resource in terms of understanding complex social networks. Additionally, Leonid Zhukov's lecture series on YouTube discussing Network Science was another invaluable resource. +

+ +

+ An assigned project that I found interesting involved assigning each student a web scrape from a social network. Each student was ambiguously tasked to analyze the data. Below is the result of data analysis that I personally drew from the dataset. I feel this is worth sharing to help those who have a genuine interest in the subject understand the processes involved.* +

+
+
+
+

Project: Social Computing

+
+

+ Consider a dataset which describes interactions between Reddit users for two different subreddits during the span of a specific month. +

    +
  • + The given dataset is given as a file which is formatted as an adjacency list. Each line of the file is represented as such: User1, User2, User3, ... Userx\n where User1 replied to a comment made by User2 and another one by User3, and every user up to and including Userx. +
  • +
+

+ +

+ Many different software solutions can be chosen for analysis. Gephi is one that is often recommended. Personal experience has found that Gephi is limited; It is handy for network visualization and generating a set of metrics which are indeed indicative of the properties of the network. Unfortunately, there seems to be no mechanism to display the calculated metrics using different scaling factors for any given scatterplot graph. For example, displaying a degree distribution graph in log-log scale doesn't seem to be an available option. To account for this, Python was leveraged, taking advantage of the matplotlib, numpy, and powerlaw libraries. +

    +
  • + numpy arrays are used to interface with matplotlib.pyplot and powerlaw. +
  • +
  • + powerlaw allows the generation of a fitting function for a bin of data. It can also generate relevant alpha values, (the gamma value with respect to the textbook used in this class), for a power-law distribution function. powerlaw also interfaces with matplotlib to allow visual representation of these functions. +
      +
    • + Relevant class methods are powerlaw.Fit and powerlaw.plot_pdf. Relevant instance methods are powerlaw.Fit(<args>).plot_pdf. Relevant instance variables are powerlaw.Fit(<args>).alpha. +
    • +
    +
  • +
  • + matplotlib.pyplot allows plotting of arrays as scatterplot. It has scaling methods to allow for the display of some graph in log-log scale. +
  • +
+ The python files associated with this are ad hoc scripts leveraged to complete the required analysis. +

+ +

+ It's worth elaborating on what is happening within the represention of the dataset. The existence of some edge in the adjacency list is a means of communication. Communication can be interpreted ambiguously. So it should also be noted that communication here is directed. The node that is representative of a list entry is replying to a comment made by a user within that list entry: +

    +
  • + User1 replied to a comment by User2 and another comment by User3; +
  • +
  • + Can be interpreted as (user1 -> user2) and (user1 -> user3) +
  • +
+ Thus, an entry of the adjacency list is indicative of the out-degree of a given node, (a node representing a user). In order to calculate the in-degree of the same node requires parsing through the adjacency list and taking note of how many times communication is directed towards it. +

+ +

+ This was considered whilst initially examining the data. The initial dataset contains 17406 edges which connect 8129 nodes. While parsing through the data, it can be determined that the minimum out-degree is 1 and the minimum in-degree is 0. Likewise, the maximum out-degree is 203 while the maximum in-degree is 144. While examining the network as an undirected graph, the minimum degree is 1 and the maximum degree is 347. +

+ +

+ How are the various degrees distributed? The following figures are indicative of distribution: +

+ + + +

+ The associated distribution function seems to be exponential in shape. What is this function? The proportion of some node having degree k must be k raised to a negative power: k, with some constant factor, c. Discovering the value of this power can be made on the observation that kmax ≈ kmin * N(1/γ-1), where N is the total number of nodes in the network. +

    +
  • + Algebraic manipulation can isolate gamma here with application of the logarithm manipulation rules. This generates an approximation of the exponent. The constant factor, c, can be discovered once gamma is found; c = (γ-1)*kmin-γ+1 +
  • +
+ The powerlaw python library makes use of statistical binning to generate these values with respect to a continuous power-law distribution function: +
    +
  • + γ-out: 2.931904108581441; c-in: 1.931904108581441 +
  • + +
  • + γ-in: 2.586342073080467; c-in: 1.5863420730804672 +
  • + +
  • + γ-total: 2.0875259166393976; c-total: 1.0875259166393976 +
  • +
+

+ +

+ With gamma values in hand, average expected degrees can be calculated, dependent on statistical moment. This occurs when gamma is in [2,3]. The formula used here is <k> = (γ-1)/(γ-2)*kmin +

    +
  • + <kout> ≈ 2.0730717793724676 ≈ <kin> ≈ <k> +
  • +
+ Average expected distance can also be computed: +
    +
  • + <d> ≈ lnlnN ≈ lnln(8129) ≈ 2.1975793137150044 +
  • +
+ The actual values computed by Gephi are as such: +
    +
  • + <k>: 2.141 +
  • + +
  • + <d>: 7.07 +
  • +
+

+ +

+ Curiously, there is a significant difference between the expected distance and the actual distance. This likely is due to the fact the sample set fits in the ultra-small-world regime and the slice taken from reddit doesn't represent the full expected picture. +

+ +

+ To confirm a power-law distribution, these distributions can be plotted in a log-log scale. The following figures show that a power-law distribution is indeed in play. The light blue points represent the distribution plot. The dotted blue line overlayed by the red line is a plotting of the power-law distribution function (C*k). +

+ + +

+ Random networks were generated to contrast this data. The algorithm that created these networks ensured the same node count and edge count. It also ensured there exists no node that does not have an edge – as is the case for the reddit data set. The distribution of these networks differ. Consider the following figures: +

+ + + +

+ The distribution figures are similar for the other four randomized networks. This similarity holds true for the log-log scale plotting of the same data: +

+ + +
+ A graph showing the log-log scale distribution plotting of nodes in terms of both degrees. +
+ Figure 10: Power Law Distribution plotting of node degrees (inbound and outbound) of Randomized Network +
+
+
+ +

+ The following table of figures are the log-log scale plotting of four other randomized networks, with respect to evaluating out-bound degree: +

+ +
+ + + A graph showing the log-log scale distribution plotting of nodes in terms of both degrees. + + + A graph showing the log-log scale distribution plotting of nodes in terms of both degrees. + + + A graph showing the log-log scale distribution plotting of nodes in terms of both degrees. + + + A graph showing the log-log scale distribution plotting of nodes in terms of both degrees. + + +
+ Figure 12: Power Law distribution plotting of outbound node degrees for four other randomized networks. +
+ +
+ +

+ These distributions are Poisson/binomial. They do not allow for the reasonable probability of having nodes with large degrees, (degrees that approach kmax). This is emphasized by the values given in the x-axis. The maximum node degree here is anywhere from 7 to 9; much smaller than the maximum node degrees of the Reddit dataset. There seems to be a higher occurrence nodes with degree quantities close to the maximum as well. This is shown in the network representation of the involved data, shown in the following figures: +

+ + +
+ A Gephi visualized graph which shows the clustering of the agents within a randomized network. The clustering shows consistency all around. +
+ Figure 13: Full visualization of randomized network indicates a lack of any hubs. The network seems to have reached a transition where there exists only one component. +
+
+
+ + +
+ A Gephi visualized graph which shows the clustering of the agents within a social network. Clustering is not consistent here, where clustering seems to revolve around a select few agents. +
+ Figure 14: Partial visualization of network representative of the Reddit dataset. Notice the significant hubs which are indicative of a higher degree. +
+
+
+ + +
+ A gephi visualized grpah which shows outliers of the social network; disconnected interactions between a subset of agents involved. +
+ Figure 15: Partial visualization of the network representative of the Reddit dataset; structures like these exist in the orbit of the larger connected component of the prior figure. This is a property not observed in the generated randomized networks. +
+
+
+ +

+ The connectivity of Figure 13 tracks once consideration of average node degree is taken. The average degree measured by Gephi is 2.141. This tracks considering the expected node degree of |E|/|V| which is 17406/8129 = 2.141. Once the average node degree surpasses 1, a randomized graph is in the super critical regime where there exists some gigantic component. This component is not fully connected, though; the average node degree has not reached a point to exceed ln(|V|). +

+ +

+ The observation of the paragraph above helps us see the property of the complex network given by the Reddit dataset is a scale-free network; a means to visually support this assertion. +

+
+
+

Concluding notes

+

+ * - My writing on pedagogy makes the following claims: +

+ In the domain of computer science, there are three different types of students: +
    +
  • + There are those who are studying a different discipline who are required to take a CS course as a prerequisite. +
  • + +
  • + There are those who have heard jobs related to the field pay well, and thus are studying on the prospect of future paycheck. +
  • + +
  • + Finally, there are the individuals who are genuinely curious of the subject. +
  • +
+
+
+ Students who are genuinely curious of the subject will succeed. The definition of success is that they will get a degree and they will have a solid and flexible intuition of the machinations of the discipline. +
    +
  • + The students in the other categories will get just a degree. +
  • +
+
+

+ +

+ I feel the need to emphasize on the fact this is for an individual who is genuinely curious. The description of the project as described is ambiguous, but there are metrics listed here that can turn a learning experience into an easy grade. Should this be the case, you are doing yourself as much of a disservice as an instructor who chooses to not provide a new/different dataset. +

+
+
+
+ +
+
+ + diff --git a/sitemap.xml b/sitemap.xml index 0824114..53f7ccc 100644 --- a/sitemap.xml +++ b/sitemap.xml @@ -2,11 +2,11 @@ - http://alanmckay.blog/ + https://alanmckay.blog/ - 2022-01-08 + 2024-01-03 @@ -16,7 +16,7 @@ - http://alanmckay.blog/writings/ + https://alanmckay.blog/writings/ @@ -26,7 +26,7 @@ - http://alanmckay.blog/writings/bench/ + https://alanmckay.blog/writings/bench/ @@ -36,7 +36,7 @@ - http://alanmckay.blog/writings/flow/ + https://alanmckay.blog/writings/flow/ @@ -46,17 +46,17 @@ - http://alanmckay.blog/writings/safety/ + https://alanmckay.blog/projects/safety/ - 2023-02-02 + 2024-01-03 - http://alanmckay.blog/writings/leaf/ + https://alanmckay.blog/writings/leaf/ @@ -65,7 +65,7 @@ - http://alanmckay.blog/writings/social/ + https://alanmckay.blog/writings/social/ @@ -75,7 +75,7 @@ - http://alanmckay.blog/writings/sound/ + https://alanmckay.blog/writings/sound/ @@ -84,7 +84,7 @@ - http://alanmckay.blog/writings/statement/ + https://alanmckay.blog/writings/statement/ @@ -93,7 +93,7 @@ - http://alanmckay.blog/writings/elsewhere/ + https://alanmckay.blog/writings/elsewhere/ @@ -102,12 +102,84 @@ - http://alanmckay.blog/writings/describe/ + https://alanmckay.blog/writings/describe/ 2022-02-06 + + + https://alanmckay.blog/projects/ + + + + 2024-01-15 + + + + + https://alanmckay.blog/projects/compiler/ + + + + 2024-01-06 + + + + + https://alanmckay.blog/projects/cup/ + + + + 2024-01-06 + + + + + https://alanmckay.blog/projects/grid/ + + + + 2024-01-06 + + + + + https://alanmckay.blog/projects/organization/ + + + + 2024-01-06 + + + + + https://alanmckay.blog/projects/protocol/ + + + + 2024-01-06 + + + + + https://alanmckay.blog/projects/social/ + + + + 2024-01-06 + + + + + https://alanmckay.blog/projects/aquatint/ + + + + 2024-01-15 + + diff --git a/style.css b/style.css index 014cb02..c313ca4 100644 --- a/style.css +++ b/style.css @@ -1,3 +1,8 @@ +body{ + max-width: 1600px; + margin: auto; +} + #homeWrapper{ width:100%; margin:auto; @@ -61,7 +66,12 @@ #homeWrapper nav a:hover{ border-left:solid 25px white; } - +/* +#homeWrapper nav a:first-child:hover{ + border-left:inherit; + cursor:default; +} +*/ #homeWrapper nav a img{ width:50px; height:50px; @@ -121,7 +131,11 @@ #writingsWrapper nav a, a{ color:#7b869d; - text-decoration:none; + text-decoration-color:#c4c9d4; +} + +#writingsWrapper a:has(> figure){ + text-decoration-line:none; } #writingsWrapper nav a:visited, a:visited{ @@ -146,6 +160,10 @@ font-size:17px; } +#writingsWrapper article h1, #writingsWrapper article h2, #writingsWrapper article h3, #writingsWrapper article h4, #writingsWrapper article h5, #writingsWrapper article h6{ + text-align:left; +} + #writingsWrapper header{ text-align: left; } @@ -157,6 +175,12 @@ padding-bottom:0%; } +#writingsWrapper blockquote{ + border-left: solid 3px #778088; + padding-left:10px; + color:#6b747b +} + #writingsWrapper figcaption{ color:#7b869d; font-size:15px; @@ -175,6 +199,191 @@ object-fit:cover; } +#writingsWrapper div.aside{ + clear:both; + overflow:auto; +} + +#writingsWrapper div.aside figure{ + float:right; + width:25%; + margin-left:5%; + margin-right:1%; + margin-top:1%; + margin-bottom:2% +} + +#writingsWrapper div.aside figure.responsive_aside{ + margin-top:1%; + margin-bottom:2%; + margin:auto; + width:65%; + float:none; + display:none; +} + +#grid-control h4{ + margin-bottom:0px; +} + +#grid-control ul{ + text-align: left; + margin-top:10px; +} + +#grid-control ul li{ + margin:1%; +} + +#grid-control ul li input{ + margin:1%; +} + +#grid-control ul.horizontal{ + width:42%; + float:left; +} + +figure.image-collage{ + +} + +figure.image-collage ul li{ + display:inline-block; +} + +#writingsWrapper figure.image-collage ul li{ + width:49%; +} + +#writingsWrapper figure.image-collage ul li#sixty{ + width: 60%; +} + +#writingsWrapper figure.image-collage ul li#thirty{ + width: 35%; +} + +#writingsWrapper figure.image-collage ul li figure figcaption{ + display:none; +} + +.expandable:hover{ + cursor:pointer; + color:black; +} + +div.slide-deck figure figcaption ul{ + margin:auto; + text-align:center; +} + +div.slide-deck figure figcaption ul li{ + display:inline-block; +} + +div.slide-deck figure ul li{ + list-style-type:none; +} + +div.slide-deck figure ul li figure img{ + border: solid #E9ECEF 3px; + /*padding-left:5px; + padding-right:5px;*/ +} + +div.slide-deck figure figcaption ul li a{ + font-size:24px; + padding-left:30px; + padding-right:30px; + cursor:pointer; +} + +div.slide-deck figure figcaption ul li a:hover{ + color:black; +} + + +div.slide-deck figure figcaption ul li:hover{ + color:black; +} + +figure.graph img{ + max-width:550px; +} + +figure.graph figcaption{ + text-align:center; +} + +ul.filelist{ +} + +ul.filelist li{ +} + +.tech code{ + font-size:90%; +} + +.formulas{ + text-align:left; +} + +/*ul{ + text-align:left; +}*/ + +code pre.code{ + overflow:scroll; + background-color:#f2f2f2; + width:95vw; + max-width:40em; + padding-left:10px; +} + +code pre.info-code{ + width:70vw; +} + +.dynamic-figure-container{ + align-items:center; +} + +.dynamic-figure-container .dynamic-figure-view{ + width:45%; +} + +.dynamic-figure-container .dynamic-figure-controls{ + width:50%; +} + +.dynamic-figure-container .dynamic-figure-controls p{ + margin:0px; + float:right; +} + +@media screen and (min-width:800px){ + #writingsWrapper .fig-col{ + overflow:auto; + } + + #writingsWrapper .fig-col figure{ + width:45%; + float:left; + margin:auto; + } + + #writingsWrapper figure.col-fig{ + overflow:auto; + } + + #writingsWrapper figure.col-fig img{ + width:45%; + float:left; + margin:auto; + } +} @media screen and (min-width: 1100px){ #writingsWrapper{ width:50%; @@ -189,13 +398,26 @@ #writingsWrapper article img#black-hills-path{ height:350px; } -} +} @media screen and (max-width: 480px){ #writingsWrapper article img#black-hills-path{ height:200px; } + + #writingsWrapper .info{ + width:100%; + } + + #writingsWrapper ul.filelist li{ + margin-bottom:0px; + } + + ul.filelist li code{ + font-size:65% + } + } @media screen and (max-width:600px) and (min-width: 481px){ @@ -208,7 +430,193 @@ #writingsWrapper article img.animate{ height:250px; } + #writingsWrapper article img#black-hills-path{ height:250px; } -} \ No newline at end of file +} + +@media screen and (max-width:800px){ + #writingsWrapper article ul{ + padding-left:5%; + } + + #writingsWrapper article li{ + margin-bottom:10px; + } + + #writingsWrapper div.aside figure{ + width:35%; + } + + #writingsWrapper figure.image-collage ul li figure{ + width:100%; + } + + #grid-control ul.horizontal{ + width:45%; + } + + #grid-control ul li{ + font-size:14px; + } + + #grid-control ul li input{ + font-size:12px; + } + +} + +@media screen and (max-width:600px){ + #grid-control ul.horizontal{ + margin-top:10px; + margin-bottom:5px; + display:inline-block; + width:90%; + } + + #grid-control ul.horizontal li{ + display:inline-block; + } + + #grid-control ul.horizontal li label{ + display:none; + } + + #grid-control ul.horizontal li input{ + + } + + #grid-control ul li br{ + display:none; + } + + #writingsWrapper div.aside figure.responsive_aside{ + display:block; + width:60%; + } + + #writingsWrapper div.aside figure{ + display:none + } + + #writingsWrapper div.aside figure.narrow{ + display:block; + width:95%; + border-bottom:solid #9A9A9A 1px; + border-top: solid #9A9A9A 1px; + } + + #writingsWrapper div.slide-deck figure ul{ + padding-left:0%; + } + + div.slide-deck figure figcaption ul li{ + font-size:16px; + } + + div.slide-deck figure figcaption ul li a{ + padding:10px; + font-size:18px; + } + + #writingsWrapper figure.image-collage ul li{ + width:100%; + } + + #writingsWrapper figure.image-collage ul li figure figcaption{ + display:block; + } + + #writingsWrapper figure.image-collage figcaption{ + display:none; + } + + #writingsWrapper figure.image-collage ul li#sixty{ + width: 100%; + } + + #writingsWrapper figure.image-collage ul li#thirty{ + width: 85%; + margin-left:5%; + } + + code pre.code{ + width:80vw; + } + + code pre.info-code{ + width:65vw; + } + + .dynamic-figure-container .dynamic-figure-view{ + width:55%; + } + + .dynamic-figure-container .dynamic-figure-controls{ + width:40%; + } + + .dynamic-figure-container .dynamic-figure-controls label{ + display:block; + width: 89px; + float:left; + } + + .dynamic-figure-container .dynamic-figure-controls p{ + width:67px; + float:left; + } +} + +@media screen and (max-width:425px){ + .tech ul{ + text-align:left; + } +} + +@media screen and (max-width:400px){ + div.slide-deck{ + display:none; + } + + code pre.code{ + min-width:260px; + } + + code pre.info-code{ + min-width:230px + } + + blockquote{ + width:95%; + margin:0px; + margin-bottom:10px; + } +} + +@media screen and (max-width:300px){ + body{ + width:100%; + } + + #writingsWrapper{ + justify-content:inherit; + min-width:300px; + } + + #writingsWrapper section{ + margin-left:5px; + margin-right:20px; + } + + #writingsWrapper article ul li{ + /*list-style-type:none;*/ + margin-left:20px; + } + + #writingsWrapper div.aside figure.responsive_aside{ + width:80%; + } + +} diff --git a/writings/bench/index.php b/writings/bench/index.php index 245a2d1..b52cf8b 100644 --- a/writings/bench/index.php +++ b/writings/bench/index.php @@ -4,7 +4,7 @@ $style = '../../style.css'; -$canonical = 'http://alanmckay.blog/writings/bench/'; +$canonical = 'https://alanmckay.blog/writings/bench/'; $title = 'Alan McKay | Writing | The Bench'; @@ -12,7 +12,9 @@ $meta['description'] = 'Last weekend, I visited the town in which I studied for my undergraduate degree - Cedar Falls. A friend was visiting, whom also went to the school. We had...'; -$meta['url'] = 'http://alanmckay.blog/writings/bench/'; +$meta['url'] = 'https://alanmckay.blog/writings/bench/'; + +$relative_path = "../../"; include('../../header.php'); @@ -124,4 +126,4 @@
- \ No newline at end of file + diff --git a/writings/describe/index.php b/writings/describe/index.php index 9adde73..528ec28 100644 --- a/writings/describe/index.php +++ b/writings/describe/index.php @@ -4,7 +4,7 @@ $style = '../../style.css'; -$canonical = 'http://alanmckay.blog/writings/describe/'; +$canonical = 'https://alanmckay.blog/writings/describe/'; $title = 'Alan McKay | Writing | Describing Elsewhere'; @@ -12,7 +12,9 @@ $meta['description'] = 'A conversation once had a friend observing that too many people are reliant on showing the detail of a story being told. That is, instead of...'; -$meta['url'] = 'http://alanmckay.blog/writings/describe/'; +$meta['url'] = 'https://alanmckay.blog/writings/describe/'; + +$relative_path = "../../"; include('../../header.php'); @@ -90,7 +92,7 @@ In terms of technical detail, a CSS class was set for all the affected elements. Here, the width is set to 100%, the height to some fixed pixel value, and the object-fit attribute set to cover. Inline styling is then applied to each of these image tags where the object-position attribute is set to define the position in which an image will be centered with respect to the cropped container. Then, also within the inline styling, the transition property of the object-position attribute is set to define how long it will take for an image to scan to a position.

-
+
 img.animate{
     width:100%;
     height:300px;
@@ -103,7 +105,7 @@
                     

-
+
 function inViewRange(elementID,inPosition,outPosition){
     element = document.getElementById(elementID);
     bounding = element.getBoundingClientRect();
@@ -122,7 +124,7 @@ function inViewRange(elementID,inPosition,outPosition){
                     

-
+
 window.onscroll = function(){
     inViewRange('image-tag-ID', '0 20%', '0 45%');
 }
@@ -162,48 +164,18 @@ function inViewRange(elementID,inPosition,outPosition){
             
         
     
+    
     
 
diff --git a/writings/elsewhere/index.php b/writings/elsewhere/index.php
index 839f154..c11a229 100644
--- a/writings/elsewhere/index.php
+++ b/writings/elsewhere/index.php
@@ -4,7 +4,7 @@
 
 $style = '../../style.css';
 
-$canonical = 'http://alanmckay.blog/writings/elsewhere/';
+$canonical = 'https://alanmckay.blog/writings/elsewhere/';
 
 $title = 'Alan McKay | Writing | Experiencing Elsewhere';
 
@@ -12,7 +12,9 @@
 
 $meta['description'] = 'A wintery haze shrouded the top of the Black Hills as the car wound the roads towards our stepping off point. There always has been a distaste for a commute...';
 
-$meta['url'] = 'http://alanmckay.blog/writings/elsewhere/';
+$meta['url'] = 'https://alanmckay.blog/writings/elsewhere/';
+
+$relative_path = "../../";
 
 include('../../header.php');
 
@@ -147,29 +149,18 @@
             
         
     
+    
     
 
diff --git a/writings/flow/index.php b/writings/flow/index.php
index 3bf2013..2ba74c5 100644
--- a/writings/flow/index.php
+++ b/writings/flow/index.php
@@ -4,7 +4,7 @@
 
 $style = '../../style.css';
 
-$canonical = 'http://alanmckay.blog/writings/flow/';
+$canonical = 'https://alanmckay.blog/writings/flow/';
 
 $title = 'Alan McKay | Writing | Flow';
 
@@ -12,7 +12,9 @@
 
 $meta['description'] = 'The old capitol building is a place I often spend time at during the evening to witness the sunset. The ledging adjacent to the steps provide a good place to...';
 
-$meta['url'] = 'http://alanmckay.blog/writings/flow/';
+$meta['url'] = 'https://alanmckay.blog/writings/flow/';
+
+$relative_path = "../../";
 
 include('../../header.php');
 
@@ -108,4 +110,4 @@
             
         
     
-
\ No newline at end of file
+
diff --git a/writings/index.php b/writings/index.php
index 39397d4..b2ba6cc 100644
--- a/writings/index.php
+++ b/writings/index.php
@@ -4,7 +4,7 @@
 
 $style = '../style.css';
 
-$canonical = 'http://alanmckay.blog/writings/';
+$canonical = 'https://alanmckay.blog/writings/';
 
 $title = 'Alan McKay | Writings';
 
@@ -12,7 +12,9 @@
 
 $meta['description'] = "Various writings of Alan McKay. Writings of the some observations of people and the places they exist; Wrtings of the various happenings within these spaces.";
 
-$meta['url'] = 'http://alanmckay.blog/writings/';
+$meta['url'] = 'https://alanmckay.blog/writings/';
+
+$relative_path = "../";
 
 include('../header.php');
 
@@ -26,7 +28,6 @@
                     Icon for blog link
                     Writings
                 
-                 Geography and Safety
                  Describing Elsewhere
                  Experiencing Elsewhere
                  The Bench
@@ -38,62 +39,39 @@
             
         
     
+    
     
 
diff --git a/writings/leaf/index.php b/writings/leaf/index.php
index 8c67f14..fceaefc 100644
--- a/writings/leaf/index.php
+++ b/writings/leaf/index.php
@@ -4,7 +4,7 @@
 
 $style = '../../style.css';
 
-$canonical = 'http://alanmckay.blog/writings/leaf/';
+$canonical = 'https://alanmckay.blog/writings/leaf/';
 
 $title = 'Alan McKay | Writing | Leaves';
 
@@ -12,7 +12,9 @@
 
 $meta['description'] = '';
 
-$meta['url'] = 'http://alanmckay.blog/writings/leaf/';
+$meta['url'] = 'https://alanmckay.blog/writings/leaf/';
+
+$relative_path = "../../";
 
 include('../../header.php');
 
@@ -75,4 +77,4 @@
             
         
     
-
\ No newline at end of file
+
diff --git a/writings/social/index.php b/writings/social/index.php
index ed38f74..edda654 100644
--- a/writings/social/index.php
+++ b/writings/social/index.php
@@ -4,7 +4,7 @@
 
 $style = '../../style.css';
 
-$canonical = 'http://alanmckay.blog/writings/social/';
+$canonical = 'https://alanmckay.blog/writings/social/';
 
 $title = 'Alan McKay | Writing | Social Media';
 
@@ -14,7 +14,9 @@
 
 $meta['image'] = 'https://image-aws-us-west-2.vsco.co/5ac5d6/146300964/5f41289e6bd09b5ab9000003/1600x900/vsco5f4128a0276b5.jpg';
 
-$meta['url'] = 'http://alanmckay.blog/writings/social/';
+$meta['url'] = 'https://alanmckay.blog/writings/social/';
+
+$relative_path = "../../";
 
 include('../../header.php');
 
@@ -78,4 +80,4 @@
             
         
     
-
\ No newline at end of file
+
diff --git a/writings/sound/index.php b/writings/sound/index.php
index 8c7d1e1..057e2fb 100644
--- a/writings/sound/index.php
+++ b/writings/sound/index.php
@@ -4,7 +4,7 @@
 
 $style = '../../style.css';
 
-$canonical = 'http://alanmckay.blog/writings/sound/';
+$canonical = 'https://alanmckay.blog/writings/sound/';
 
 $title = 'Alan McKay | Writing | The Connectivity of Sound';
 
@@ -12,7 +12,9 @@
 
 $meta['description'] = 'Seven years ago, I woke up looking at a moving hospital ceiling. I was being carted in, voices sounding off around me; all informing each other information...';
 
-$meta['url'] = 'http://alanmckay.blog/writings/sound/';
+$meta['url'] = 'https://alanmckay.blog/writings/sound/';
+
+$relative_path = "../../";
 
 include('../../header.php');
 
@@ -126,4 +128,4 @@
             
         
     
-
\ No newline at end of file
+
diff --git a/writings/statement/index.php b/writings/statement/index.php
index f84758c..cf2d998 100644
--- a/writings/statement/index.php
+++ b/writings/statement/index.php
@@ -4,7 +4,7 @@
 
 $style = '../../style.css';
 
-$canonical = 'http://alanmckay.blog/writings/statement/';
+$canonical = 'https://alanmckay.blog/writings/statement/';
 
 $title = 'Alan McKay | Writing | Statement of Purpose';
 
@@ -12,7 +12,9 @@
 
 $meta['description'] = "The following is the statement of purpose I wrote to pursue a Master's of Computer Science at the University of Iowa. The primary goal of this statement was...";
 
-$meta['url'] = 'http://alanmckay.blog/writings/statement/';
+$meta['url'] = 'https://alanmckay.blog/writings/statement/';
+
+$relative_path = "../../";
 
 include('../../header.php');
 
@@ -88,4 +90,4 @@
             
         
     
-
\ No newline at end of file
+