diff --git a/README.txt b/README.txt index 80a726b..b36a37a 100644 --- a/README.txt +++ b/README.txt @@ -16,7 +16,6 @@ Release: + Stroke previews get connected when drawn without panning on touch devices + Redraw HTML (cursors) on local canvas moves + New strokes dissapear on the HMH desk - - Color picker misses on strange line endings - Debug - Restore ability to limit event range * Listeners/events/multiplayer @@ -42,11 +41,12 @@ Release: + EYE DROPPER! + Dynamic svg cursor to represent the brush + Eraser - - Line drawing + * Line drawing + Undo - Undo for images (add, move, scale) - Undo for eraser - Redo + - Snapping to grid * Polish + Use typedvector where appropriate - Show what's happening while the desk is loading (downloading, processing, uploading to gpu) @@ -54,7 +54,7 @@ Release: - Set up VAOs - We are calling "geometry_prepare_stroke" twice for some reason - Presentation / "marketing" - - Title (InfiNotes?) + - Title (InfiNotes? MegaDesk?) - Icon - Product page (github readme, demo videos) diff --git a/client/bvh.js b/client/bvh.js index 345bf2c..04bee14 100644 --- a/client/bvh.js +++ b/client/bvh.js @@ -164,7 +164,7 @@ function bvh_delete_stroke(state, stroke) { while (node.parent_index !== null) { if (node.is_fullnode) { - let index_index = node.stroke_indices.data.indexOf(stroke.index); + let index_index = tv_data(node.stroke_indices).indexOf(stroke.index); if (index_index !== -1) { node.stroke_indices.data[index_index] = node.stroke_indices.data[node.stroke_indices.size - 1]; tv_pop(node.stroke_indices); diff --git a/client/math.js b/client/math.js index 5597bf5..46da54c 100644 --- a/client/math.js +++ b/client/math.js @@ -436,7 +436,7 @@ function circle_intersects_capsule(ax, ay, bx, by, p1, p2, cx, cy, r) { return dist <= r; } -function stroke_intersects_cursor(state, stroke, canvasp, radius) { +function stroke_intersects_capsule(state, stroke, a, b, radius) { const xs = state.wasm.buffers['xs'].tv.data; const ys = state.wasm.buffers['ys'].tv.data; const pressures = state.wasm.buffers['pressures'].tv.data; @@ -449,8 +449,17 @@ function stroke_intersects_cursor(state, stroke, canvasp, radius) { const p1 = pressures[i + 0]; const p2 = pressures[i + 1]; + // Test if p1 or p2 overlap the capsule + if (circle_intersects_capsule(x1, y1, x2, y2, p1 * stroke.width / 255, p2 * stroke.width / 255, a.x, a.y, radius)) { + return true; + } + if (circle_intersects_capsule(x1, y1, x2, y2, p1 * stroke.width / 255, p2 * stroke.width / 255, b.x, b.y, radius)) { + return true; + } - if (circle_intersects_capsule(x1, y1, x2, y2, p1 * stroke.width / 255, p2 * stroke.width / 255, canvasp.x, canvasp.y, radius)) { + // Lame test for the quad part, only test for line-line intersection + // TODO: actually test for rotated quad vs circle/rotated quad overlap + if (segments_intersect(a, b, {'x': x1, 'y': y1}, {'x': x2, 'y': y2})) { return true; } } diff --git a/client/webgl_listeners.js b/client/webgl_listeners.js index d50ba58..5a714c9 100644 --- a/client/webgl_listeners.js +++ b/client/webgl_listeners.js @@ -383,23 +383,27 @@ function mousemove(e, state, context) { if (state.erasing) { const me = state.players[state.me]; const radius = Math.round(me.width / 2); + const last_canvasp = screen_to_canvas(state, state.cursor); const cursor_bbox = { - 'x1': canvasp.x - radius, - 'y1': canvasp.y - radius, - 'x2': canvasp.x + radius, - 'y2': canvasp.y + radius, + 'x1': Math.min(canvasp.x, last_canvasp.x) - radius, + 'y1': Math.min(canvasp.y, last_canvasp.y) - radius, + 'x2': Math.max(canvasp.x, last_canvasp.x) + radius, + 'y2': Math.max(canvasp.y, last_canvasp.y) + radius, }; tv_ensure(state.erase_candidates, round_to_pow2(state.stroke_count, 4096)); tv_clear(state.erase_candidates); + + // Rough pass, not all of these might actually need to be erased bvh_intersect_quad(state, state.bvh, cursor_bbox, state.erase_candidates); + // Fine pass, actually run expensive capsule vs capsule intersection tests for (let i = 0; i < state.erase_candidates.size; ++i) { const stroke_id = state.erase_candidates.data[i]; const stroke = state.events[stroke_id]; - if (!stroke.deleted && stroke_intersects_cursor(state, stroke, canvasp, radius)) { + if (!stroke.deleted && stroke_intersects_capsule(state, stroke, last_canvasp, canvasp, radius)) { stroke.deleted = true; bvh_delete_stroke(state, stroke); queue_event(state, eraser_event(stroke_id));