summaryrefslogtreecommitdiff
path: root/fractiform.js
diff options
context:
space:
mode:
authorpommicket <pommicket@gmail.com>2023-06-18 11:17:05 +0100
committerpommicket <pommicket@gmail.com>2023-06-18 11:17:05 +0100
commitfd4e7b9b9a73fb9078fb95a8355405d5540d6b5a (patch)
treec863a641045084ea33559bc93f31b379f9b12c99 /fractiform.js
parent63a2e6bdb46cb1a81dab57d7e4372a943783f21b (diff)
snap to vertices
Diffstat (limited to 'fractiform.js')
-rw-r--r--fractiform.js61
1 files changed, 52 insertions, 9 deletions
diff --git a/fractiform.js b/fractiform.js
index f41e311..79c8631 100644
--- a/fractiform.js
+++ b/fractiform.js
@@ -34,6 +34,7 @@ let ui_color_input;
let ui_color_mix_input;
let ui_div;
let vertex_id = 0;
+let snapping = true;
const TOOL_TRIANGLE = 1;
const TOOL_UV = 2;
@@ -42,6 +43,8 @@ const TOOL_SELECT = 3;
let ui_tool;
const vertex_radius = 10;
+// radius for snapping to vertices, selecting vertices
+const vertex_mouse_radius = 10;
let width = 1920, height = 1920;
@@ -178,6 +181,38 @@ function is_mouse_in_canvas() {
return Math.abs(mouse_pos.x) <= 1 && Math.abs(mouse_pos.y) <= 1;
}
+function distance(p0, p1) {
+ let dx = p0.x - p1.x;
+ let dy = p0.y - p1.y;
+ return Math.sqrt(dx * dx + dy * dy);
+}
+
+function snapped_pos(p) {
+ let px = ndc_to_px(p);
+ let closest_vertex = null;
+ let closest_dist = Infinity;
+ function consider_vertex(v) {
+ let dist = distance(ndc_to_px(v), px);
+ if (dist < closest_dist) {
+ closest_dist = dist;
+ closest_vertex = v;
+ }
+ }
+ vertices_main.forEach(consider_vertex);
+ ui_vertices.forEach(consider_vertex);
+ if (closest_dist < vertex_mouse_radius) {
+ return Object.preventExtensions({
+ x: closest_vertex.x,
+ y: closest_vertex.y,
+ });
+ }
+ return Object.preventExtensions({x: p.x, y: p.y});
+}
+
+function snapped_mouse_pos() {
+ return snapping ? snapped_pos(mouse_pos) : mouse_pos;
+}
+
function lerp(a, b, x) {
return a + (b - a) * x;
}
@@ -238,10 +273,11 @@ function on_click(e) {
}
if (ui_is_editing_shape()) {
+ let pos = snapped_mouse_pos();
let vertex = {
id: ++vertex_id,
- x: mouse_pos.x,
- y: mouse_pos.y,
+ x: pos.x,
+ y: pos.y,
color: rgba_hex_to_float(ui_get_color_rgba()),
};
ui_shape.push(vertex);
@@ -356,6 +392,13 @@ function ui_is_editing_vertex() {
return ui_tool === TOOL_TRIANGLE;
}
+function draw_vertex(vertex) {
+ ui_circle(vertex, vertex_radius, {
+ strokeStyle: '#ffffff',
+ fillStyle: rgba_float_to_hex(vertex.color),
+ });
+}
+
function frame(time) {
ui_vertex_properties_div.style.display = ui_is_editing_vertex() ? 'inline-block' : 'none';
current_time = time * 1e-3;
@@ -427,10 +470,7 @@ function frame(time) {
if (ui_shown) {
for (let i = 0; i < vertices_main.length; i++) {
let vertex = vertices_main[i];
- ui_circle(vertex, vertex_radius, {
- strokeStyle: '#ffffff',
- fillStyle: rgba_float_to_hex(vertex.color),
- });
+ draw_vertex(vertex);
if (i % 3 === 0) {
console.assert(i + 2 < vertices_main.length, 'vertices_main.length not a multiple of 3');
const line_options = {
@@ -447,6 +487,7 @@ function frame(time) {
strokeStyle: '#ffffff',
fillStyle: '#ffffff44',
});
+ ui_vertices.forEach(draw_vertex);
}
if (ui_is_editing_shape()) {
@@ -462,17 +503,19 @@ function frame(time) {
};
if (ui_shape.length < 3 && is_mouse_in_canvas()) {
+ let mpos = snapped_mouse_pos();
+
// vertex where the mouse is
- ui_circle(mouse_pos, vertex_radius, {
+ ui_circle(mpos, vertex_radius, {
strokeStyle: options_shape.strokeStyle,
fillStyle: ui_tool === TOOL_UV ? color + '44' : ui_get_color_rgba(),
});
if (ui_shape.length === 1) {
- ui_line(ui_shape[0], mouse_pos, options_shape);
+ ui_line(ui_shape[0], mpos, options_shape);
} else if (ui_shape.length === 2) {
// triangle preview
- ui_polygon([ui_shape[0], ui_shape[1], mouse_pos], options_shape);
+ ui_polygon([ui_shape[0], ui_shape[1], mpos], options_shape);
}
}