Add edge midpoint drag handles to Grid_Setup
Drag indices 4-7 correspond to top/right/bottom/left edge midpoints. Dragging a midpoint applies the delta to both adjacent corners, making it easier to align bins with rounded corners where corner handles may be obscured. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -14,13 +14,17 @@ export class Grid_Setup {
|
||||
#cam_z = 1;
|
||||
|
||||
#corners = null; // in IMAGE coordinates
|
||||
#drag_idx = -1; // index of corner being dragged, or -1
|
||||
#drag_idx = -1; // 0-3: corners, 4-7: edge midpoints (top,right,bottom,left)
|
||||
#drag_prev_img = null; // previous image-space position for midpoint delta tracking
|
||||
#panning = false;
|
||||
#pan_last = { x: 0, y: 0 };
|
||||
|
||||
#rows = 4;
|
||||
#cols = 6;
|
||||
|
||||
// Edge pairs for midpoint handles: midpoint (idx-4) moves corners [a, b]
|
||||
static #MIDPOINT_EDGES = [[0,1],[1,2],[2,3],[3,0]];
|
||||
|
||||
constructor(canvas_el) {
|
||||
this.#canvas = canvas_el;
|
||||
this.#ctx = canvas_el.getContext('2d');
|
||||
@@ -126,12 +130,26 @@ export class Grid_Setup {
|
||||
return { x: w.x * this.#cam_z + this.#cam_x, y: w.y * this.#cam_z + this.#cam_y };
|
||||
}
|
||||
|
||||
#get_midpoints() {
|
||||
return Grid_Setup.#MIDPOINT_EDGES.map(([a, b]) => ({
|
||||
x: (this.#corners[a].x + this.#corners[b].x) / 2,
|
||||
y: (this.#corners[a].y + this.#corners[b].y) / 2,
|
||||
}));
|
||||
}
|
||||
|
||||
#find_handle(sp, radius = 18) {
|
||||
if (!this.#corners) return -1;
|
||||
// Corners take priority
|
||||
for (let i = 0; i < 4; i++) {
|
||||
const s = this.#img_to_screen(this.#corners[i]);
|
||||
if ((sp.x - s.x)**2 + (sp.y - s.y)**2 < radius**2) return i;
|
||||
}
|
||||
// Midpoints
|
||||
const mids = this.#get_midpoints();
|
||||
for (let i = 0; i < 4; i++) {
|
||||
const s = this.#img_to_screen(mids[i]);
|
||||
if ((sp.x - s.x)**2 + (sp.y - s.y)**2 < (radius * 0.85)**2) return i + 4;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -141,10 +159,13 @@ export class Grid_Setup {
|
||||
const hit = this.#find_handle(sp);
|
||||
if (hit !== -1) {
|
||||
this.#drag_idx = hit;
|
||||
if (hit >= 4) {
|
||||
this.#drag_prev_img = this.#world_to_img(this.#to_world(sp));
|
||||
}
|
||||
} else {
|
||||
this.#panning = true;
|
||||
this.#pan_last = sp;
|
||||
if (!is_touch) this.#canvas.style.cursor = 'grabbing';
|
||||
if (!is_touch) { this.#canvas.style.cursor = 'grabbing'; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -152,7 +173,16 @@ export class Grid_Setup {
|
||||
const sp = this.#screen_pos(e);
|
||||
if (this.#drag_idx !== -1) {
|
||||
const img_pos = this.#world_to_img(this.#to_world(sp));
|
||||
this.#corners[this.#drag_idx] = img_pos;
|
||||
if (this.#drag_idx < 4) {
|
||||
this.#corners[this.#drag_idx] = img_pos;
|
||||
} else {
|
||||
const [a, b] = Grid_Setup.#MIDPOINT_EDGES[this.#drag_idx - 4];
|
||||
const dx = img_pos.x - this.#drag_prev_img.x;
|
||||
const dy = img_pos.y - this.#drag_prev_img.y;
|
||||
this.#corners[a] = { x: this.#corners[a].x + dx, y: this.#corners[a].y + dy };
|
||||
this.#corners[b] = { x: this.#corners[b].x + dx, y: this.#corners[b].y + dy };
|
||||
this.#drag_prev_img = img_pos;
|
||||
}
|
||||
this.#draw();
|
||||
} else if (this.#panning) {
|
||||
this.#cam_x += sp.x - this.#pan_last.x;
|
||||
@@ -167,6 +197,7 @@ export class Grid_Setup {
|
||||
|
||||
#on_up(e) {
|
||||
this.#drag_idx = -1;
|
||||
this.#drag_prev_img = null;
|
||||
if (this.#panning) {
|
||||
this.#panning = false;
|
||||
const sp = this.#screen_pos(e);
|
||||
@@ -255,6 +286,19 @@ export class Grid_Setup {
|
||||
ctx.fillText(LABELS[i], pt.x, pt.y);
|
||||
});
|
||||
|
||||
// Midpoint handles — smaller, white with blue stroke
|
||||
const mid_r = 7 / this.#cam_z;
|
||||
const mids = this.#get_midpoints().map(m => this.#img_to_world(m));
|
||||
mids.forEach(pt => {
|
||||
ctx.beginPath();
|
||||
ctx.arc(pt.x, pt.y, mid_r, 0, Math.PI*2);
|
||||
ctx.fillStyle = 'rgba(255,255,255,0.75)';
|
||||
ctx.fill();
|
||||
ctx.strokeStyle = 'rgba(91,156,246,0.9)';
|
||||
ctx.lineWidth = 2 / this.#cam_z;
|
||||
ctx.stroke();
|
||||
});
|
||||
|
||||
ctx.restore();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user