Browse Source

Add line-line intersection test to the eraser to kinda make it work for faster movement

ssao
A.Olokhtonov 5 months ago
parent
commit
b11c46354f
  1. 6
      README.txt
  2. 2
      client/bvh.js
  3. 13
      client/math.js
  4. 14
      client/webgl_listeners.js

6
README.txt

@ -16,7 +16,6 @@ Release:
+ Stroke previews get connected when drawn without panning on touch devices + Stroke previews get connected when drawn without panning on touch devices
+ Redraw HTML (cursors) on local canvas moves + Redraw HTML (cursors) on local canvas moves
+ New strokes dissapear on the HMH desk + New strokes dissapear on the HMH desk
- Color picker misses on strange line endings
- Debug - Debug
- Restore ability to limit event range - Restore ability to limit event range
* Listeners/events/multiplayer * Listeners/events/multiplayer
@ -42,11 +41,12 @@ Release:
+ EYE DROPPER! + EYE DROPPER!
+ Dynamic svg cursor to represent the brush + Dynamic svg cursor to represent the brush
+ Eraser + Eraser
- Line drawing * Line drawing
+ Undo + Undo
- Undo for images (add, move, scale) - Undo for images (add, move, scale)
- Undo for eraser - Undo for eraser
- Redo - Redo
- Snapping to grid
* Polish * Polish
+ Use typedvector where appropriate + Use typedvector where appropriate
- Show what's happening while the desk is loading (downloading, processing, uploading to gpu) - Show what's happening while the desk is loading (downloading, processing, uploading to gpu)
@ -54,7 +54,7 @@ Release:
- Set up VAOs - Set up VAOs
- We are calling "geometry_prepare_stroke" twice for some reason - We are calling "geometry_prepare_stroke" twice for some reason
- Presentation / "marketing" - Presentation / "marketing"
- Title (InfiNotes?) - Title (InfiNotes? MegaDesk?)
- Icon - Icon
- Product page (github readme, demo videos) - Product page (github readme, demo videos)

2
client/bvh.js

@ -164,7 +164,7 @@ function bvh_delete_stroke(state, stroke) {
while (node.parent_index !== null) { while (node.parent_index !== null) {
if (node.is_fullnode) { 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) { if (index_index !== -1) {
node.stroke_indices.data[index_index] = node.stroke_indices.data[node.stroke_indices.size - 1]; node.stroke_indices.data[index_index] = node.stroke_indices.data[node.stroke_indices.size - 1];
tv_pop(node.stroke_indices); tv_pop(node.stroke_indices);

13
client/math.js

@ -436,7 +436,7 @@ function circle_intersects_capsule(ax, ay, bx, by, p1, p2, cx, cy, r) {
return dist <= 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 xs = state.wasm.buffers['xs'].tv.data;
const ys = state.wasm.buffers['ys'].tv.data; const ys = state.wasm.buffers['ys'].tv.data;
const pressures = state.wasm.buffers['pressures'].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 p1 = pressures[i + 0];
const p2 = pressures[i + 1]; 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; return true;
} }
} }

14
client/webgl_listeners.js

@ -383,23 +383,27 @@ function mousemove(e, state, context) {
if (state.erasing) { if (state.erasing) {
const me = state.players[state.me]; const me = state.players[state.me];
const radius = Math.round(me.width / 2); const radius = Math.round(me.width / 2);
const last_canvasp = screen_to_canvas(state, state.cursor);
const cursor_bbox = { const cursor_bbox = {
'x1': canvasp.x - radius, 'x1': Math.min(canvasp.x, last_canvasp.x) - radius,
'y1': canvasp.y - radius, 'y1': Math.min(canvasp.y, last_canvasp.y) - radius,
'x2': canvasp.x + radius, 'x2': Math.max(canvasp.x, last_canvasp.x) + radius,
'y2': canvasp.y + radius, 'y2': Math.max(canvasp.y, last_canvasp.y) + radius,
}; };
tv_ensure(state.erase_candidates, round_to_pow2(state.stroke_count, 4096)); tv_ensure(state.erase_candidates, round_to_pow2(state.stroke_count, 4096));
tv_clear(state.erase_candidates); 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); 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) { for (let i = 0; i < state.erase_candidates.size; ++i) {
const stroke_id = state.erase_candidates.data[i]; const stroke_id = state.erase_candidates.data[i];
const stroke = state.events[stroke_id]; 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; stroke.deleted = true;
bvh_delete_stroke(state, stroke); bvh_delete_stroke(state, stroke);
queue_event(state, eraser_event(stroke_id)); queue_event(state, eraser_event(stroke_id));

Loading…
Cancel
Save