From d4421f84e0a726ce922e5d146b7d43d0b0382ff2 Mon Sep 17 00:00:00 2001 From: Piratux <58703216+Piratux@users.noreply.github.com> Date: Mon, 6 Nov 2023 16:06:14 +0200 Subject: [PATCH] connect points extra features + cleanup --- connect_points/Images/c_L.png | Bin 18073 -> 0 bytes connect_points/Images/formulas.png | Bin 7498 -> 6094 bytes connect_points/index.html | 236 +++--- connect_points/index.js | 1065 +++++++++++++++------------- connect_points/style.css | 64 +- 5 files changed, 736 insertions(+), 629 deletions(-) delete mode 100644 connect_points/Images/c_L.png diff --git a/connect_points/Images/c_L.png b/connect_points/Images/c_L.png deleted file mode 100644 index bfa0bf533b375bd7749e318cce6b515916ca9707..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 18073 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 diff --git a/connect_points/Images/formulas.png b/connect_points/Images/formulas.png index 9eaddf7f5d6539f3cb276c227a30cb36becca01c..68e3271d9fcf050ec32102291ceabd983b2a0441 100644 GIT binary patch delta 5955 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 - - - - - - - -
-

- Grid connector -

- - -
- -
-
-

- -
- - Grid size: 1 - - -
- - - -
- - - -

-
- -
-

- d(G): - 0 -
- - c(G): - 0 -
-

-
- -
-

- Vertices connected: - 0 - / - 0 -
- - Total connections: - 0 -

-
-
- -
-

- Controls -

-

- - Hold left mouse button: connect points with an edge. -
- - Hold right mouse button: disconnect points by removing edge between them (if there is one). -

-
- -
-

- Motivation -

-

- This page was made to aid solving graph connection related problems. Here are some of those problems. -

-

- Problem 1: Given Hamiltonian cycle whose edges do not self-intersect, find such cycle that maximises d(G). -
- Problem 2: Given Hamiltonian cycle whose edges do not self-intersect, find such cycle that minimises c(G). -
- Problem formulas -

-
- -

- Insights -

- -

- It was found that for grid sizes of nxn, where n < 6, If G maximises d(G), it also minimises c(G). It could be that this is true for all n. -

- -

-
-
- Problems author: Giedrius Alkauskas -

- + + + + + + + + +
+

+ Grid connector +

+ +
+
+ + + Grid size: 1 +
+ + + + Line width: 1 +
+ + + + Circle spacing: 1 +
+ + + + Circle radius: 1 +
+ + + Mark intersecting edges +
+ + + Mark connected vertices +
+ + + Auto connect circles +
+ + +
+ +
+ d(G): + 0 +
+
+ +
+ Vertices connected: + 0 + / + 0 +
+ + Total connections: + 0 +
+
+
+ +
+ +
+ +
+
+

+ Controls +

+

+ - Hold left mouse button: connect circles with an edge. +
+ - Hold right mouse button: disconnect circles by removing edge between them (if there is one). +

+
+ +
+

+ Motivation +

+

+ This page was made to aid solving graph connection related problems. +

+

+ Problem 1: Given Hamiltonian cycle whose edges do not self-intersect, find such cycle that maximises + d(G). +
+ Problem formulas +

+
+ +

+
+
+ Problems author: Giedrius Alkauskas +

+
+ + \ No newline at end of file diff --git a/connect_points/index.js b/connect_points/index.js index b1f1b32..ca37788 100644 --- a/connect_points/index.js +++ b/connect_points/index.js @@ -1,16 +1,30 @@ let canvas; let canvas_ctx; -let circle_radius = 16; -let circle_grid_size = 4; -let min_circle_grid_size = 1; -let max_circle_grid_size = 8; +let grid_size = 4; +let min_grid_size = 1; +let max_grid_size = 50; + +let circle_line_width_increment = 2; +let line_width = 6; +let min_line_width = 2; +let max_line_width = 20; + +let circle_spacing_increment = 5; +let circle_spacing = 80; +let min_circle_spacing = 20; +let max_circle_spacing = 100; + +let circle_radius_increment = 5; +let circle_radius = 15; +let min_circle_radius = 5; +let max_circle_radius = 50; + +// distance between circle edge and canvas edge +let canvas_padding = 20; + let circle_grid_start_pos_x = 56; let circle_grid_start_pos_y = 56; -let circle_grid_spacing = 84; - -let start_drag_pos_x = 0; -let start_drag_pos_y = 0; // Last circle row/column where mouse was pressed on let last_grid_circle_x = -1; @@ -40,34 +54,38 @@ const colour_green = "#35c13d"; const colour_red = "#ff0000"; const colour_dark_red = "#a10000"; -window.onload = function(){ - canvas = document.getElementById('main_canvas'); - canvas_ctx = canvas.getContext('2d'); - - canvas.addEventListener("mousedown", mouse_down); - canvas.addEventListener("mouseup", mouse_up); - canvas.addEventListener("mousemove", mouse_move); - - window.addEventListener('contextmenu', (event) => { - event.preventDefault() - }) - - set_grid_size(circle_grid_size); - - set_vertices_connected(0); - set_total_vertices(circle_grid_size * circle_grid_size); - set_total_connections(0); - - setInterval(draw_everything, 1000/60); -} - -function draw_everything(){ - canvas_ctx.fillStyle = 'white'; - canvas_ctx.fillRect(0,0,canvas.width,canvas.height); - - draw_grid_circles(); - draw_grid_lines(); - draw_currently_held_line(); +window.onload = function () { + canvas = document.getElementById('main_canvas'); + canvas_ctx = canvas.getContext('2d'); + + canvas.addEventListener("mousedown", mouse_down); + canvas.addEventListener("mouseup", mouse_up); + canvas.addEventListener("mousemove", mouse_move); + + window.addEventListener('contextmenu', (event) => { + event.preventDefault() + }) + + set_grid_size(grid_size); + set_line_width(line_width); + set_circle_spacing(circle_spacing); + set_circle_radius(circle_radius); + auto_resize_canvas(); + + set_vertices_connected(0); + set_total_vertices(grid_size * grid_size); + set_total_connections(0); + + setInterval(draw_everything, 1000 / 60); +} + +function draw_everything() { + canvas_ctx.fillStyle = 'white'; + canvas_ctx.fillRect(0, 0, canvas.width, canvas.height); + + draw_grid_circles(); + draw_grid_lines(); + draw_currently_held_line(); } // Source: https://www.geeksforgeeks.org/check-if-two-given-line-segments-intersect/ @@ -89,20 +107,20 @@ function orientation(p, q, r) { // Returns true if line segment 'p1q1' and 'p2q2' intersect // assuming they're not collinear and they don't share vertexes. function segments_intersect(p1, q1, p2, q2) { - // Discard cases where segments share the same vertex - if (p1.x === p2.x && p1.y === p2.y) - return false; - - if (p1.x === q2.x && p1.y === q2.y) - return false; - - if (q1.x === p2.x && q1.y === p2.y) - return false; - - if (q1.x === q2.x && q1.y === q2.y) - return false; - - + // Discard cases where segments share the same vertex + if (p1.x === p2.x && p1.y === p2.y) + return false; + + if (p1.x === q2.x && p1.y === q2.y) + return false; + + if (q1.x === p2.x && q1.y === p2.y) + return false; + + if (q1.x === q2.x && q1.y === q2.y) + return false; + + let o1 = orientation(p1, q1, p2); let o2 = orientation(p1, q1, q2); let o3 = orientation(p2, q2, p1); @@ -114,462 +132,515 @@ function segments_intersect(p1, q1, p2, q2) { return false; } -function make_1D_index(x, y, arr_width){ - return y * arr_width + x; -} - -function draw_grid_circles(){ - let mark_connected_vertices_enabled = is_mark_connected_vertices_enabled(); - - for(let y = 0; y < circle_grid_size; y++){ - for(let x = 0; x < circle_grid_size; x++){ - let pos_x = circle_grid_start_pos_x + x * circle_grid_spacing; - let pos_y = circle_grid_start_pos_y + y * circle_grid_spacing; - - let diff_x = pos_x - last_mouse_pos_x; - let diff_y = pos_y - last_mouse_pos_y; - - let vertex_idx = make_1D_index(x, y, circle_grid_size); - let vertex_is_taken = false; - if(taken_vertices[vertex_idx] === 1){ - vertex_is_taken = true; - } - - let distance = Math.sqrt(diff_x * diff_x + diff_y * diff_y); - if(distance <= circle_radius){ - if(mark_connected_vertices_enabled && vertex_is_taken){ - draw_circle(pos_x, pos_y, circle_radius, colour_light_orange); - } - else{ - draw_circle(pos_x, pos_y, circle_radius, colour_light_blue); - } - } - else{ - if(mark_connected_vertices_enabled && vertex_is_taken){ - draw_circle(pos_x, pos_y, circle_radius, colour_orange); - } - else{ - draw_circle(pos_x, pos_y, circle_radius, colour_blue); - } - // draw_circle(pos_x, pos_y, circle_radius, colour_blue); - } - } - } -} - -function draw_grid_lines(){ - let mark_intersecting_edges_enabled = is_mark_intersecting_edges_enabled(); - - if(mark_intersecting_edges_enabled){ - // Split up drawing iterations, to avoid random order of line draws on top of another - // First draw green lines, then red lines - - for(let i = 0; i < grid_connection_data.length; i++){ - if(intersecting_edges[i] !== 0){ - continue; - } - - from_x = grid_connection_data[i][0]; - from_y = grid_connection_data[i][1]; - to_x = grid_connection_data[i][2]; - to_y = grid_connection_data[i][3]; - - from_x = from_x * circle_grid_spacing + circle_grid_start_pos_x; - from_y = from_y * circle_grid_spacing + circle_grid_start_pos_y; - to_x = to_x * circle_grid_spacing + circle_grid_start_pos_x; - to_y = to_y * circle_grid_spacing + circle_grid_start_pos_y; - - draw_line(from_x, from_y, to_x, to_y, colour_green); - } - - for(let i = 0; i < grid_connection_data.length; i++){ - if(intersecting_edges[i] !== 1){ - continue; - } - - from_x = grid_connection_data[i][0]; - from_y = grid_connection_data[i][1]; - to_x = grid_connection_data[i][2]; - to_y = grid_connection_data[i][3]; - - from_x = from_x * circle_grid_spacing + circle_grid_start_pos_x; - from_y = from_y * circle_grid_spacing + circle_grid_start_pos_y; - to_x = to_x * circle_grid_spacing + circle_grid_start_pos_x; - to_y = to_y * circle_grid_spacing + circle_grid_start_pos_y; - - draw_line(from_x, from_y, to_x, to_y, colour_red); - } - } - else{ - for(let i = 0; i < grid_connection_data.length; i++){ - from_x = grid_connection_data[i][0]; - from_y = grid_connection_data[i][1]; - to_x = grid_connection_data[i][2]; - to_y = grid_connection_data[i][3]; - - from_x = from_x * circle_grid_spacing + circle_grid_start_pos_x; - from_y = from_y * circle_grid_spacing + circle_grid_start_pos_y; - to_x = to_x * circle_grid_spacing + circle_grid_start_pos_x; - to_y = to_y * circle_grid_spacing + circle_grid_start_pos_y; - - draw_line(from_x, from_y, to_x, to_y, colour_green); - } - } -} - -function draw_currently_held_line(){ - if(last_grid_circle_x == -1 && last_grid_circle_y == -1){ - return; - } - - from_x = last_grid_circle_x * circle_grid_spacing + circle_grid_start_pos_x; - from_y = last_grid_circle_y * circle_grid_spacing + circle_grid_start_pos_y; - to_x = last_mouse_pos_x; - to_y = last_mouse_pos_y; - - if(left_mouse_button_is_down){ - draw_line(from_x, from_y, to_x, to_y, colour_green); - } - else{ - draw_line(from_x, from_y, to_x, to_y, colour_dark_red); - } -} - -function draw_circle(x, y, radius, style){ - let ctx = canvas_ctx; - ctx.save(); - - ctx.beginPath(); - ctx.arc(x, y, radius, 0, 2 * Math.PI, false); - ctx.fillStyle = style; - ctx.fill(); - - ctx.restore(); -} - -function draw_line(from_x, from_y, to_x, to_y, style){ - let ctx = canvas_ctx; - ctx.save(); - - ctx.lineWidth = 5; - ctx.lineCap = "round"; - ctx.beginPath(); - ctx.moveTo(from_x, from_y); - ctx.lineTo(to_x, to_y); - ctx.strokeStyle = style; - ctx.stroke(); - - ctx.restore(); -} - -function reset_grid(){ - grid_connection_data.length = 0; - update_connection_info(); -} - -function set_grid_size(new_value){ - circle_grid_size = clamp(new_value, min_circle_grid_size, max_circle_grid_size); - document.getElementById("grid_size").textContent = circle_grid_size; -} - -function increase_grid_size(){ - set_grid_size(circle_grid_size + 1); - update_connection_info(); -} - -function decrease_grid_size(){ - set_grid_size(circle_grid_size - 1); - - // remove out of bounds grid connections - for(let i = 0; i < grid_connection_data.length; i++){ - for(let j = 0; j < 4; j++){ - if(grid_connection_data[i][j] >= circle_grid_size){ - grid_connection_data.splice(i, 1); - i--; - break; - } - } - } - - update_connection_info(); -} - -function calculate_intersection_location(mouse_pos_x, mouse_pos_y){ - for(let y = 0; y < circle_grid_size; y++){ - for(let x = 0; x < circle_grid_size; x++){ - let pos_x = circle_grid_start_pos_x + x * circle_grid_spacing; - let pos_y = circle_grid_start_pos_y + y * circle_grid_spacing; - let diff_x = pos_x - mouse_pos_x; - let diff_y = pos_y - mouse_pos_y; - - let distance = Math.sqrt(diff_x * diff_x + diff_y * diff_y); - if(distance <= circle_radius){ - return [x, y] - } - } - } - - return [] -} - -function add_grid_entry_if_needed(mouse_pos_x, mouse_pos_y){ - let intersection = calculate_intersection_location(mouse_pos_x, mouse_pos_y); - if(intersection.length == 0){ - return; - } - - let old_last_grid_circle_x = last_grid_circle_x; - let old_last_grid_circle_y = last_grid_circle_y; - - last_grid_circle_x = intersection[0]; - last_grid_circle_y = intersection[1]; - - if(old_last_grid_circle_x == -1 && old_last_grid_circle_y == -1){ - return; - } - - let a_x = old_last_grid_circle_x; - let a_y = old_last_grid_circle_y; - let b_x = last_grid_circle_x; - let b_y = last_grid_circle_y; - let from_idx = make_1D_index(a_x, a_y, max_circle_grid_size - 1); - let to_idx = make_1D_index(b_x, b_y, max_circle_grid_size - 1); - - // we don't want want connections going from same to same element - if (from_idx == to_idx){ - return; - } - - let vec_x = Math.abs(a_x - b_x); - let vec_y = Math.abs(a_y - b_y); - - let vector_count = gcd(vec_x, vec_y); - let simplified_vector = {x: vec_x / vector_count, y: vec_y / vector_count}; - - if(a_x > b_x){ - simplified_vector.x *= -1; - } - - if(a_y > b_y){ - simplified_vector.y *= -1; - } - - // hack to avoid adding duplicate data such as: - // (0, 1) -> (2, 3) - // (2, 3) -> (0, 1) - if (from_idx > to_idx){ - [a_x, b_x] = [b_x, a_x]; - [a_y, b_y] = [b_y, a_y]; - simplified_vector.x *= -1; - simplified_vector.y *= -1; - } - - for(let i = 0; i < vector_count; i++){ - grid_connection_data.push([ - a_x + simplified_vector.x * i, - a_y + simplified_vector.y * i, - a_x + simplified_vector.x * (i + 1), - a_y + simplified_vector.y * (i + 1) - ]); - } - - // remove duplicate entries - // https://stackoverflow.com/a/44014849 - grid_connection_data = Array.from(new Set(grid_connection_data.map(JSON.stringify)), JSON.parse) - - update_connection_info(); -} - -function remove_grid_entry_if_needed(mouse_pos_x, mouse_pos_y){ - let intersection = calculate_intersection_location(mouse_pos_x, mouse_pos_y); - if(intersection.length == 0){ - return; - } - - let old_last_grid_circle_x = last_grid_circle_x; - let old_last_grid_circle_y = last_grid_circle_y; - - last_grid_circle_x = intersection[0]; - last_grid_circle_y = intersection[1]; - - if(old_last_grid_circle_x == -1 && old_last_grid_circle_y == -1){ - return; - } - - let arr_to_remove1 = [old_last_grid_circle_x, old_last_grid_circle_y, last_grid_circle_x, last_grid_circle_y]; - let arr_to_remove2 = [last_grid_circle_x, last_grid_circle_y, old_last_grid_circle_x, old_last_grid_circle_y]; - - for(let i = 0; i < grid_connection_data.length; i++){ - let is_equal1 = true; - let is_equal2 = true; - for(let j = 0; j < 4; j++){ - if(grid_connection_data[i][j] != arr_to_remove1[j]){ - is_equal1 = false; - } - if(grid_connection_data[i][j] != arr_to_remove2[j]){ - is_equal2 = false; - } - } - - if(is_equal1 || is_equal2){ - grid_connection_data.splice(i, 1); - } - } - - update_connection_info(); -} - -function set_d_g(new_value){ - document.getElementById("d_g").textContent = new_value; -} - -function set_c_g(new_value){ - document.getElementById("c_g").textContent = new_value; -} - -function set_vertices_connected(new_value){ - document.getElementById("vertices_connected").textContent = new_value; -} - -function set_total_vertices(new_value){ - document.getElementById("total_vertices").textContent = new_value; -} - -function set_total_connections(new_value){ - document.getElementById("total_connections").textContent = new_value; -} - -function is_mark_intersecting_edges_enabled(){ - return document.querySelector('.mark_intersecting_edges').checked; -} - -function is_mark_connected_vertices_enabled(){ - return document.querySelector('.mark_connected_vertices').checked; -} - -function update_connection_info(){ - let d_g = 0; - for(let i = 0; i < grid_connection_data.length; i++){ - let diff_x = Math.abs(grid_connection_data[i][0] - grid_connection_data[i][2]); - let diff_y = Math.abs(grid_connection_data[i][1] - grid_connection_data[i][3]); - d_g += diff_x * diff_x + diff_y * diff_y; - } - set_d_g(d_g); - - let c_g = 0; - for(let i = 0; i < grid_connection_data.length; i++){ - let x_i0 = grid_connection_data[i][0] + 1; - let y_i0 = grid_connection_data[i][1] + 1; - let x_i1 = grid_connection_data[i][2] + 1; - let y_i1 = grid_connection_data[i][3] + 1; - c_g += x_i0 * x_i1 + y_i0 * y_i1; - } - - set_c_g(c_g); - - // initialise array with 0 values - taken_vertices = Array.apply(null, Array(circle_grid_size * circle_grid_size)).map(function (x, i) { return 0; }); - let vertices_connected = 0; - for(let i = 0; i < grid_connection_data.length; i++){ - let from_idx = make_1D_index(grid_connection_data[i][0], grid_connection_data[i][1], circle_grid_size); - let to_idx = make_1D_index(grid_connection_data[i][2], grid_connection_data[i][3], circle_grid_size); - if(taken_vertices[from_idx] === 0){ - taken_vertices[from_idx] = 1; - vertices_connected += 1; - } - if(taken_vertices[to_idx] === 0){ - taken_vertices[to_idx] = 1; - vertices_connected += 1; - } - } - - set_vertices_connected(vertices_connected); - - set_total_vertices(circle_grid_size * circle_grid_size); - - set_total_connections(grid_connection_data.length); - - // initialise array with 0 values - intersecting_edges = Array.apply(null, Array(grid_connection_data.length)).map(function (x, i) { return 0; }); - for(let i = 0; i < grid_connection_data.length; i++){ - for(let j = 0; j < grid_connection_data.length; j++){ - if(i === j){ - continue; - } - - p1 = {x: grid_connection_data[i][0], y: grid_connection_data[i][1]}; - q1 = {x: grid_connection_data[i][2], y: grid_connection_data[i][3]}; - p2 = {x: grid_connection_data[j][0], y: grid_connection_data[j][1]}; - q2 = {x: grid_connection_data[j][2], y: grid_connection_data[j][3]}; - - if(segments_intersect(p1, q1, p2, q2)){ - intersecting_edges[i] = 1; - break; - } - } - } -} - -function get_canvas_mouse_pos(event){ - let rect = canvas.getBoundingClientRect(); - return { - x: event.clientX - rect.left, - y:event.clientY - rect.top - } +function make_1D_index(x, y, arr_width) { + return y * arr_width + x; +} + +function draw_grid_circles() { + let mark_connected_vertices_enabled = is_mark_connected_vertices_enabled(); + + for (let y = 0; y < grid_size; y++) { + for (let x = 0; x < grid_size; x++) { + let pos_x = circle_grid_start_pos_x + x * circle_spacing; + let pos_y = circle_grid_start_pos_y + y * circle_spacing; + + let diff_x = pos_x - last_mouse_pos_x; + let diff_y = pos_y - last_mouse_pos_y; + + let vertex_idx = make_1D_index(x, y, grid_size); + let vertex_is_taken = false; + if (taken_vertices[vertex_idx] === 1) { + vertex_is_taken = true; + } + + let distance = Math.sqrt(diff_x * diff_x + diff_y * diff_y); + if (distance <= circle_radius) { + if (mark_connected_vertices_enabled && vertex_is_taken) { + draw_circle(pos_x, pos_y, circle_radius, colour_light_orange); + } + else { + draw_circle(pos_x, pos_y, circle_radius, colour_light_blue); + } + } + else { + if (mark_connected_vertices_enabled && vertex_is_taken) { + draw_circle(pos_x, pos_y, circle_radius, colour_orange); + } + else { + draw_circle(pos_x, pos_y, circle_radius, colour_blue); + } + // draw_circle(pos_x, pos_y, circle_radius, colour_blue); + } + } + } +} + +function draw_grid_lines() { + let mark_intersecting_edges_enabled = is_mark_intersecting_edges_enabled(); + + if (mark_intersecting_edges_enabled) { + // Split up drawing iterations, to avoid random order of line draws on top of another + // First draw green lines, then red lines + + for (let i = 0; i < grid_connection_data.length; i++) { + if (intersecting_edges[i] !== 0) { + continue; + } + + from_x = grid_connection_data[i][0]; + from_y = grid_connection_data[i][1]; + to_x = grid_connection_data[i][2]; + to_y = grid_connection_data[i][3]; + + from_x = from_x * circle_spacing + circle_grid_start_pos_x; + from_y = from_y * circle_spacing + circle_grid_start_pos_y; + to_x = to_x * circle_spacing + circle_grid_start_pos_x; + to_y = to_y * circle_spacing + circle_grid_start_pos_y; + + draw_line(from_x, from_y, to_x, to_y, colour_green); + } + + for (let i = 0; i < grid_connection_data.length; i++) { + if (intersecting_edges[i] !== 1) { + continue; + } + + from_x = grid_connection_data[i][0]; + from_y = grid_connection_data[i][1]; + to_x = grid_connection_data[i][2]; + to_y = grid_connection_data[i][3]; + + from_x = from_x * circle_spacing + circle_grid_start_pos_x; + from_y = from_y * circle_spacing + circle_grid_start_pos_y; + to_x = to_x * circle_spacing + circle_grid_start_pos_x; + to_y = to_y * circle_spacing + circle_grid_start_pos_y; + + draw_line(from_x, from_y, to_x, to_y, colour_red); + } + } + else { + for (let i = 0; i < grid_connection_data.length; i++) { + from_x = grid_connection_data[i][0]; + from_y = grid_connection_data[i][1]; + to_x = grid_connection_data[i][2]; + to_y = grid_connection_data[i][3]; + + from_x = from_x * circle_spacing + circle_grid_start_pos_x; + from_y = from_y * circle_spacing + circle_grid_start_pos_y; + to_x = to_x * circle_spacing + circle_grid_start_pos_x; + to_y = to_y * circle_spacing + circle_grid_start_pos_y; + + draw_line(from_x, from_y, to_x, to_y, colour_green); + } + } +} + +function draw_currently_held_line() { + if (last_grid_circle_x == -1 && last_grid_circle_y == -1) { + return; + } + + from_x = last_grid_circle_x * circle_spacing + circle_grid_start_pos_x; + from_y = last_grid_circle_y * circle_spacing + circle_grid_start_pos_y; + to_x = last_mouse_pos_x; + to_y = last_mouse_pos_y; + + if (left_mouse_button_is_down) { + draw_line(from_x, from_y, to_x, to_y, colour_green); + } + else { + draw_line(from_x, from_y, to_x, to_y, colour_dark_red); + } +} + +function draw_circle(x, y, radius, style) { + let ctx = canvas_ctx; + ctx.save(); + + ctx.beginPath(); + ctx.arc(x, y, radius, 0, 2 * Math.PI, false); + ctx.fillStyle = style; + ctx.fill(); + + ctx.restore(); +} + +function draw_line(from_x, from_y, to_x, to_y, style) { + let ctx = canvas_ctx; + ctx.save(); + + ctx.lineWidth = line_width; + ctx.lineCap = "round"; + ctx.beginPath(); + ctx.moveTo(from_x, from_y); + ctx.lineTo(to_x, to_y); + ctx.strokeStyle = style; + ctx.stroke(); + + ctx.restore(); +} + +function reset_grid() { + grid_connection_data.length = 0; + update_connection_info(); +} + +function set_grid_size(new_value) { + grid_size = clamp(new_value, min_grid_size, max_grid_size); + document.getElementById("grid_size").textContent = grid_size; + auto_resize_canvas(); +} + +function increase_grid_size() { + set_grid_size(grid_size + 1); + update_connection_info(); +} + +function decrease_grid_size() { + set_grid_size(grid_size - 1); + + // remove out of bounds grid connections + for (let i = 0; i < grid_connection_data.length; i++) { + for (let j = 0; j < 4; j++) { + if (grid_connection_data[i][j] >= grid_size) { + grid_connection_data.splice(i, 1); + i--; + break; + } + } + } + + update_connection_info(); +} + +function set_line_width(new_value) { + line_width = clamp(new_value, min_line_width, max_line_width); + document.getElementById("line_width").textContent = line_width; +} + +function increase_line_width() { + set_line_width(line_width + circle_line_width_increment); +} + +function decrease_line_width() { + set_line_width(line_width - circle_line_width_increment); +} + +function set_circle_spacing(new_value) { + circle_spacing = clamp(new_value, min_circle_spacing, max_circle_spacing); + document.getElementById("circle_spacing").textContent = circle_spacing; + auto_resize_canvas(); +} + +function increase_circle_spacing() { + set_circle_spacing(circle_spacing + circle_spacing_increment); +} + +function decrease_circle_spacing() { + set_circle_spacing(circle_spacing - circle_spacing_increment); +} + +function set_circle_radius(new_value) { + circle_radius = clamp(new_value, min_circle_radius, max_circle_radius); + document.getElementById("circle_radius").textContent = circle_radius; + recalculate_grid_start_pos(); + auto_resize_canvas(); +} + +function increase_circle_radius() { + set_circle_radius(circle_radius + circle_radius_increment); +} + +function decrease_circle_radius() { + set_circle_radius(circle_radius - circle_radius_increment); +} + +function auto_resize_canvas() { + let canvas_size = 2 * canvas_padding + 2 * circle_radius + circle_spacing * (grid_size - 1); + document.getElementById("main_canvas").width = canvas_size; + document.getElementById("main_canvas").height = canvas_size; + console.log(document.getElementById("main_canvas").width); + console.log(document.getElementById("main_canvas").height); + + draw_everything(); +} + +function recalculate_grid_start_pos() { + let start_pos = canvas_padding + circle_radius; + circle_grid_start_pos_x = start_pos; + circle_grid_start_pos_y = start_pos; +} + +function calculate_intersection_location(mouse_pos_x, mouse_pos_y) { + for (let y = 0; y < grid_size; y++) { + for (let x = 0; x < grid_size; x++) { + let pos_x = circle_grid_start_pos_x + x * circle_spacing; + let pos_y = circle_grid_start_pos_y + y * circle_spacing; + let diff_x = pos_x - mouse_pos_x; + let diff_y = pos_y - mouse_pos_y; + + let distance = Math.sqrt(diff_x * diff_x + diff_y * diff_y); + if (distance <= circle_radius) { + return [x, y] + } + } + } + + return [] +} + +function add_grid_entry_if_needed(mouse_pos_x, mouse_pos_y) { + let intersection = calculate_intersection_location(mouse_pos_x, mouse_pos_y); + if (intersection.length == 0) { + return; + } + + let old_last_grid_circle_x = last_grid_circle_x; + let old_last_grid_circle_y = last_grid_circle_y; + + last_grid_circle_x = intersection[0]; + last_grid_circle_y = intersection[1]; + + if (old_last_grid_circle_x == -1 && old_last_grid_circle_y == -1) { + return; + } + + let a_x = old_last_grid_circle_x; + let a_y = old_last_grid_circle_y; + let b_x = last_grid_circle_x; + let b_y = last_grid_circle_y; + let from_idx = make_1D_index(a_x, a_y, max_grid_size - 1); + let to_idx = make_1D_index(b_x, b_y, max_grid_size - 1); + + // we don't want want connections going from same to same element + if (from_idx == to_idx) { + return; + } + + let vec_x = Math.abs(a_x - b_x); + let vec_y = Math.abs(a_y - b_y); + + let vector_count = gcd(vec_x, vec_y); + let simplified_vector = { x: vec_x / vector_count, y: vec_y / vector_count }; + + if (a_x > b_x) { + simplified_vector.x *= -1; + } + + if (a_y > b_y) { + simplified_vector.y *= -1; + } + + // hack to avoid adding duplicate data such as: + // (0, 1) -> (2, 3) + // (2, 3) -> (0, 1) + if (from_idx > to_idx) { + [a_x, b_x] = [b_x, a_x]; + [a_y, b_y] = [b_y, a_y]; + simplified_vector.x *= -1; + simplified_vector.y *= -1; + } + + for (let i = 0; i < vector_count; i++) { + grid_connection_data.push([ + a_x + simplified_vector.x * i, + a_y + simplified_vector.y * i, + a_x + simplified_vector.x * (i + 1), + a_y + simplified_vector.y * (i + 1) + ]); + } + + // remove duplicate entries + // https://stackoverflow.com/a/44014849 + grid_connection_data = Array.from(new Set(grid_connection_data.map(JSON.stringify)), JSON.parse) + + update_connection_info(); +} + +function remove_grid_entry_if_needed(mouse_pos_x, mouse_pos_y) { + let intersection = calculate_intersection_location(mouse_pos_x, mouse_pos_y); + if (intersection.length == 0) { + return; + } + + let old_last_grid_circle_x = last_grid_circle_x; + let old_last_grid_circle_y = last_grid_circle_y; + + last_grid_circle_x = intersection[0]; + last_grid_circle_y = intersection[1]; + + if (old_last_grid_circle_x == -1 && old_last_grid_circle_y == -1) { + return; + } + + let arr_to_remove1 = [old_last_grid_circle_x, old_last_grid_circle_y, last_grid_circle_x, last_grid_circle_y]; + let arr_to_remove2 = [last_grid_circle_x, last_grid_circle_y, old_last_grid_circle_x, old_last_grid_circle_y]; + + for (let i = 0; i < grid_connection_data.length; i++) { + let is_equal1 = true; + let is_equal2 = true; + for (let j = 0; j < 4; j++) { + if (grid_connection_data[i][j] != arr_to_remove1[j]) { + is_equal1 = false; + } + if (grid_connection_data[i][j] != arr_to_remove2[j]) { + is_equal2 = false; + } + } + + if (is_equal1 || is_equal2) { + grid_connection_data.splice(i, 1); + } + } + + update_connection_info(); +} + +function set_d_g(new_value) { + document.getElementById("d_g").textContent = new_value; +} + +function set_vertices_connected(new_value) { + document.getElementById("vertices_connected").textContent = new_value; +} + +function set_total_vertices(new_value) { + document.getElementById("total_vertices").textContent = new_value; +} + +function set_total_connections(new_value) { + document.getElementById("total_connections").textContent = new_value; +} + +function is_mark_intersecting_edges_enabled() { + return document.getElementById("mark_intersecting_edges").checked; +} + +function is_mark_connected_vertices_enabled() { + return document.getElementById("mark_connected_vertices").checked; +} + +function is_auto_connect_circles_enabled() { + return document.getElementById("auto_connect_circles").checked; +} + +function update_connection_info() { + let d_g = 0; + for (let i = 0; i < grid_connection_data.length; i++) { + let diff_x = Math.abs(grid_connection_data[i][0] - grid_connection_data[i][2]); + let diff_y = Math.abs(grid_connection_data[i][1] - grid_connection_data[i][3]); + d_g += diff_x * diff_x + diff_y * diff_y; + } + set_d_g(d_g); + + // initialise array with 0 values + taken_vertices = Array.apply(null, Array(grid_size * grid_size)).map(function (x, i) { return 0; }); + let vertices_connected = 0; + for (let i = 0; i < grid_connection_data.length; i++) { + let from_idx = make_1D_index(grid_connection_data[i][0], grid_connection_data[i][1], grid_size); + let to_idx = make_1D_index(grid_connection_data[i][2], grid_connection_data[i][3], grid_size); + if (taken_vertices[from_idx] === 0) { + taken_vertices[from_idx] = 1; + vertices_connected += 1; + } + if (taken_vertices[to_idx] === 0) { + taken_vertices[to_idx] = 1; + vertices_connected += 1; + } + } + + set_vertices_connected(vertices_connected); + + set_total_vertices(grid_size * grid_size); + + set_total_connections(grid_connection_data.length); + + // initialise array with 0 values + intersecting_edges = Array.apply(null, Array(grid_connection_data.length)).map(function (x, i) { return 0; }); + for (let i = 0; i < grid_connection_data.length; i++) { + for (let j = 0; j < grid_connection_data.length; j++) { + if (i === j) { + continue; + } + + p1 = { x: grid_connection_data[i][0], y: grid_connection_data[i][1] }; + q1 = { x: grid_connection_data[i][2], y: grid_connection_data[i][3] }; + p2 = { x: grid_connection_data[j][0], y: grid_connection_data[j][1] }; + q2 = { x: grid_connection_data[j][2], y: grid_connection_data[j][3] }; + + if (segments_intersect(p1, q1, p2, q2)) { + intersecting_edges[i] = 1; + break; + } + } + } +} + +function get_canvas_mouse_pos(event) { + let rect = canvas.getBoundingClientRect(); + return { + x: event.clientX - rect.left, + y: event.clientY - rect.top + } } function mouse_down(event) { - mouse_is_down = true; - if(event.button == 0){ - left_mouse_button_is_down = true; - } - - if (event.button == 2){ - right_mouse_button_is_down = true; - } - - last_grid_circle_x = -1; - last_grid_circle_y = -1; - - let mouse_pos = get_canvas_mouse_pos(event); - - let intersection = calculate_intersection_location(mouse_pos.x, mouse_pos.y); - if(intersection.length == 0){ - return; - } - - last_grid_circle_x = intersection[0]; - last_grid_circle_y = intersection[1]; - - mouse_down_pos_x = mouse_pos.x; - mouse_down_pos_y = mouse_pos.y; + mouse_is_down = true; + if (event.button == 0) { + left_mouse_button_is_down = true; + } + + if (event.button == 2) { + right_mouse_button_is_down = true; + } + + last_grid_circle_x = -1; + last_grid_circle_y = -1; + + let mouse_pos = get_canvas_mouse_pos(event); + + let intersection = calculate_intersection_location(mouse_pos.x, mouse_pos.y); + if (intersection.length == 0) { + return; + } + + last_grid_circle_x = intersection[0]; + last_grid_circle_y = intersection[1]; } function mouse_up(event) { - if(event.button == 0){ - left_mouse_button_is_down = false; - } - - if (event.button == 2){ - right_mouse_button_is_down = false; - } - - last_grid_circle_x = -1; - last_grid_circle_y = -1; + let mouse_pos = get_canvas_mouse_pos(event); + + if (event.button == 0) { + add_grid_entry_if_needed(mouse_pos.x, mouse_pos.y); + left_mouse_button_is_down = false; + } + + if (event.button == 2) { + remove_grid_entry_if_needed(mouse_pos.x, mouse_pos.y); + right_mouse_button_is_down = false; + } + + last_grid_circle_x = -1; + last_grid_circle_y = -1; } function mouse_move(event) { - let mouse_pos = get_canvas_mouse_pos(event); - - last_mouse_pos_x = mouse_pos.x - last_mouse_pos_y = mouse_pos.y - - if (left_mouse_button_is_down == true){ - add_grid_entry_if_needed(mouse_pos.x, mouse_pos.y); - } - else if (right_mouse_button_is_down == true){ - remove_grid_entry_if_needed(mouse_pos.x, mouse_pos.y); - } + let mouse_pos = get_canvas_mouse_pos(event); + + last_mouse_pos_x = mouse_pos.x + last_mouse_pos_y = mouse_pos.y + + if (is_auto_connect_circles_enabled() == false) { + return; + } + + if (left_mouse_button_is_down == true) { + add_grid_entry_if_needed(mouse_pos.x, mouse_pos.y); + } + else if (right_mouse_button_is_down == true) { + remove_grid_entry_if_needed(mouse_pos.x, mouse_pos.y); + } } function gcd(a, b) { diff --git a/connect_points/style.css b/connect_points/style.css index 66ac453..b88bdbb 100644 --- a/connect_points/style.css +++ b/connect_points/style.css @@ -1,53 +1,57 @@ body { - font: 18px/1.5 -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; - background-color:lightgrey; - margin: 40px auto; - padding: 0 10px; - max-width: 700px; + font: 18px/1.5 -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; + background-color: lightgrey; + margin: 1em auto; + padding: 0 0.5em; } -div.canvas { - text-align: center; +.maxwidth{ + max-width: 700px; + margin: 1em auto; +} + +.centered{ + text-align: center; } div.option_column { - float: left; - width: 40%; + float: left; + width: 40%; } div.function_column { - float: left; - width: 20%; + float: left; + width: 20%; } div.info_column { - float: left; - width: 40%; + float: left; + width: 40%; } /* Clear floats after the columns */ div.canvas_info:after { - content: ""; - display: table; - clear: both; + content: ""; + display: table; + clear: both; } img { - width: auto; - height: 250px; + width: auto; + height: 250px; } @media (prefers-color-scheme: dark) { - body { - color: #c9d1d9; - background: #0d1117; - } - - a:link { - color: #58a6ff; - } - - a:visited { - color: #8e96f0; - } + body { + color: #c9d1d9; + background: #0d1117; + } + + a:link { + color: #58a6ff; + } + + a:visited { + color: #8e96f0; + } } \ No newline at end of file