From aa1c2348ca38115adfd6220ef6580ca0e974f6b9 Mon Sep 17 00:00:00 2001 From: Semphris Date: Sun, 9 Jun 2024 14:45:37 -0400 Subject: [PATCH] Fix eraser behavior and refactor function This is one case where the Javascript + operator silently *concatenated* numbers because they were stored as strings, and silently converted back to numbers at the * operator. --- www/js/elements/stroke.mjs | 52 ++++++++++++++++++++++++++---------- www/js/tools/eraser.mjs | 2 +- www/js/tools/highlighter.mjs | 2 +- www/js/tools/pencil.mjs | 2 +- 4 files changed, 41 insertions(+), 17 deletions(-) diff --git a/www/js/elements/stroke.mjs b/www/js/elements/stroke.mjs index 7aac89f..2bb6b32 100644 --- a/www/js/elements/stroke.mjs +++ b/www/js/elements/stroke.mjs @@ -55,20 +55,35 @@ export class StrokeElement { /** * @returns true if a segment from `lastPoint` to `point` with thickness * `range` intersects with this element. - * - * This function is particularly ugly. I should refactor it when I can. */ touches(page, point, lastPoint, range) { + const scaleX = page.getCanvas().width; + const scaleY = page.getCanvas().height; + const scaleW = page.getCanvas().width / page.getBaseDims().width; + for (var i = 0; i < this.points.length; i++) { - const myPoint = this.points[i]; - const lineWidth = this.size * page.getScale().x; - const dist = Math.sqrt(Math.pow((myPoint.x - point.x) - * page.canvas.width, 2) - + Math.pow((myPoint.y - point.y) - * page.canvas.height, 2)); - - if (dist < (lineWidth + range) / 2) - return true; + // Test point collision (if circles with middle and radius + // collide) with both points + + for (const pt of [point, lastPoint]) { + + const ptx = pt.x * scaleX; + const pty = pt.y * scaleY; + const x = this.points[i].x * scaleX; + const y = this.points[i].y * scaleY; + + const len = (range + this.size) * scaleW / 2; + + if (Math.sqrt(Math.pow(ptx - x, 2) + Math.pow(pty - y, 2)) < len) { + return true; + } + } + + // Test segment collision (does not account for thickness) + + // Segment is point[i] -> point[i - 1], skip if no point[i - 1] + if (i === 0) + continue; // Taken from https://stackoverflow.com/questions/9043805/test-if-two-lines-intersect-javascript-function function intersects(a,b,c,d,p,q,r,s){ @@ -77,11 +92,20 @@ export class StrokeElement { 0<(λ=((s-q)*(r-a)+(p-r)*(s-b))/𝐴)&&λ<1&&0<γ&&γ<1) } - if (lastPoint && i > 0 && intersects(myPoint.x, myPoint.y, - this.points[i - 1].x, this.points[i - 1].y, point.x, point.y, - lastPoint.x, lastPoint.y)) + const ptx1 = point.x * scaleX; + const pty1 = point.y * scaleY; + const ptx2 = lastPoint.x * scaleX; + const pty2 = lastPoint.y * scaleY; + const x1 = this.points[i].x * scaleX; + const y1 = this.points[i].y * scaleY; + const x2 = this.points[i - 1].x * scaleX; + const y2 = this.points[i - 1].y * scaleY; + + if (intersects(ptx1, pty1, ptx2, pty2, x1, y1, x2, y2)) { return true; + } } + return false; } diff --git a/www/js/tools/eraser.mjs b/www/js/tools/eraser.mjs index 3acd3a8..c7906eb 100644 --- a/www/js/tools/eraser.mjs +++ b/www/js/tools/eraser.mjs @@ -19,7 +19,7 @@ export class EraserTool { sizeInput.type = 'number'; sizeInput.value = this.size; sizeInput.addEventListener('blur', () => { - this.size = sizeInput.value; + this.size = parseInt(sizeInput.value); }); sizeInput.addEventListener('click', () => { diff --git a/www/js/tools/highlighter.mjs b/www/js/tools/highlighter.mjs index 07da84f..cb20afd 100644 --- a/www/js/tools/highlighter.mjs +++ b/www/js/tools/highlighter.mjs @@ -46,7 +46,7 @@ export class HighlighterTool { sizeInput.type = 'number'; sizeInput.value = this.size; sizeInput.addEventListener('blur', () => { - this.size = sizeInput.value; + this.size = parseInt(sizeInput.value); }); sizeInput.addEventListener('click', () => { diff --git a/www/js/tools/pencil.mjs b/www/js/tools/pencil.mjs index 9c2fb43..c4e7f61 100644 --- a/www/js/tools/pencil.mjs +++ b/www/js/tools/pencil.mjs @@ -46,7 +46,7 @@ export class PencilTool { sizeInput.type = 'number'; sizeInput.value = this.size; sizeInput.addEventListener('blur', () => { - this.size = sizeInput.value; + this.size = parseInt(sizeInput.value); }); sizeInput.addEventListener('click', () => {