Browse Source

Eraser!

master
A.Olokhtonov 1 year ago
parent
commit
8e96700c45
  1. 86
      client/cursor.js
  2. 23
      client/index.js
  3. 46
      client/math.js
  4. 22
      client/recv.js
  5. 6
      client/send.js
  6. 5
      server/deserializer.js
  7. 1
      server/enums.js
  8. 2
      server/recv.js
  9. 7
      server/send.js
  10. 6
      server/serializer.js

86
client/cursor.js

@ -37,10 +37,11 @@ function on_down(e) { @@ -37,10 +37,11 @@ function on_down(e) {
storage.cursor.x = x;
storage.cursor.y = y;
const predraw = predraw_event(x, y);
storage.current_stroke.push(predraw);
fire_event(predraw);
if (storage.tool === 'brush') {
const predraw = predraw_event(x, y);
storage.current_stroke.push(predraw);
fire_event(predraw);
}
}
}
@ -69,19 +70,40 @@ function on_move(e) { @@ -69,19 +70,40 @@ function on_move(e) {
}
if (storage.state.drawing) {
const width = storage.cursor.width;
storage.ctx1.beginPath();
storage.ctx1.moveTo(last_x, last_y);
storage.ctx1.lineTo(x, y);
storage.ctx1.stroke();
const predraw = predraw_event(x, y);
storage.current_stroke.push(predraw);
fire_event(predraw);
if (storage.tool === 'brush') {
const width = storage.cursor.width;
storage.ctx1.beginPath();
storage.ctx1.moveTo(last_x, last_y);
storage.ctx1.lineTo(x, y);
storage.ctx1.stroke();
const predraw = predraw_event(x, y);
storage.current_stroke.push(predraw);
fire_event(predraw);
} else if (storage.tool === 'eraser') {
const erased = strokes_intersect_line(last_x, last_y, x, y);
storage.erased.push(...erased);
if (erased.length > 0) {
for (const other_event of storage.events) {
for (const stroke_id of erased) {
if (stroke_id === other_event.stroke_id) {
if (!other_event.deleted) {
other_event.deleted = true;
const stats = stroke_stats(other_event.points, storage.cursor.width);
redraw_region(stats.bbox);
}
}
}
}
}
} else {
console.error('fuck');
}
} else if (storage.state.moving && storage.state.mousedown) {
storage.canvas.offset_x -= e.movementX;
storage.canvas.offset_y -= e.movementY;
@ -118,15 +140,39 @@ async function on_up(e) { @@ -118,15 +140,39 @@ async function on_up(e) {
}
if (storage.state.drawing && e.button === 0) {
if (storage.tool === 'brush') {
const event = stroke_event();
storage.current_stroke = [];
await queue_event(event);
} else if (storage.tool === 'eraser') {
const events = eraser_events();
storage.erased = [];
if (events.length > 0) {
for (const event of events) {
await queue_event(event);
}
}
} else {
console.error('fuck');
}
storage.state.drawing = false;
const event = stroke_event();
storage.current_stroke = [];
await queue_event(event);
return;
}
}
function on_keydown(e) {
if (e.code === 'KeyE') {
storage.tool = 'eraser';
return;
}
if (e.code === 'KeyB') {
storage.tool = 'brush';
return;
}
if (e.code === 'Space' && !storage.state.drawing) {
storage.state.moving = true;
storage.state.spacedown = true;

23
client/index.js

@ -10,6 +10,7 @@ const EVENT = Object.freeze({ @@ -10,6 +10,7 @@ const EVENT = Object.freeze({
REDO: 31,
IMAGE: 40,
IMAGE_MOVE: 41,
ERASER: 50,
});
const MESSAGE = Object.freeze({
@ -37,6 +38,8 @@ const storage = { @@ -37,6 +38,8 @@ const storage = {
'spacedown': false,
},
'erased': [],
'tool': 'brush',
'predraw': {},
'timers': {},
'me': {},
@ -87,7 +90,7 @@ function event_size(event) { @@ -87,7 +90,7 @@ function event_size(event) {
}
case EVENT.STROKE: {
size += 2 + 2 + 4 + event.points.length * 2 * 2; // u16 (count) + u16 (width) + u32 (color + count * (u16, u16) points
size += 4 + 2 + 2 + 4 + event.points.length * 2 * 2; // u32 stroke id + u16 (count) + u16 (width) + u32 (color + count * (u16, u16) points
break;
}
@ -102,6 +105,11 @@ function event_size(event) { @@ -102,6 +105,11 @@ function event_size(event) {
break;
}
case EVENT.ERASER: {
size += 4; // stroke id
break;
}
default: {
console.error('fuck');
}
@ -206,6 +214,19 @@ function image_move_event(image_id, x, y) { @@ -206,6 +214,19 @@ function image_move_event(image_id, x, y) {
}
}
function eraser_events() {
const result = [];
for (const stroke_id of storage.erased) {
result.push({
'type': EVENT.ERASER,
'stroke_id': stroke_id,
});
}
return result;
}
function main() {
const url = new URL(window.location.href);
const parts = url.pathname.split('/');

46
client/math.js

@ -130,6 +130,10 @@ function rectangles_intersect(a, b) { @@ -130,6 +130,10 @@ function rectangles_intersect(a, b) {
}
function stroke_intersects_region(points, bbox) {
if (points.length === 0) {
return false;
}
const stats = stroke_stats(points, storage.cursor.width);
return rectangles_intersect(stats.bbox, bbox);
}
@ -156,4 +160,46 @@ function color_from_u32(color_u32) { @@ -156,4 +160,46 @@ function color_from_u32(color_u32) {
if (b <= 0xF) b_str = '0' + b_str;
return '#' + r_str + g_str + b_str;
}
function ccw(A, B, C) {
return (C.y - A.y) * (B.x - A.x) > (B.y - A.y) * (C.x - A.x);
}
// https://stackoverflow.com/a/9997374/11420590
function segments_intersect(A, B, C, D) {
return ccw(A, C, D) != ccw(B, C, D) && ccw(A, B, C) !== ccw(A, B, D);
}
function strokes_intersect_line(x1, y1, x2, y2) {
const result = [];
for (const event of storage.events) {
if (event.type === EVENT.STROKE && !event.deleted) {
if (event.points.length < 2) {
continue;
}
for (let i = 0; i < event.points.length - 1; ++i) {
const sx1 = event.points[i].x;
const sy1 = event.points[i].y;
const sx2 = event.points[i + 1].x;
const sy2 = event.points[i + 1].y;
const A = {'x': x1, 'y': y1};
const B = {'x': x2, 'y': y2};
const C = {'x': sx1, 'y': sy1};
const D = {'x': sx2, 'y': sy2};
if (segments_intersect(A, B, C, D)) {
result.push(event.stroke_id);
break;
}
}
}
}
return result;
}

22
client/recv.js

@ -52,11 +52,13 @@ function des_event(d) { @@ -52,11 +52,13 @@ function des_event(d) {
}
case EVENT.STROKE: {
const stroke_id = des_u32(d);
const point_count = des_u16(d);
const width = des_u16(d);
const color = des_u32(d);
const coords = des_u16array(d, point_count * 2);
event.stroke_id = stroke_id;
event.points = [];
for (let i = 0; i < point_count; ++i) {
@ -84,6 +86,11 @@ function des_event(d) { @@ -84,6 +86,11 @@ function des_event(d) {
break;
}
case EVENT.ERASER: {
event.stroke_id = des_u32(d);
break;
}
default: {
console.error('fuck');
}
@ -176,6 +183,21 @@ async function handle_event(event) { @@ -176,6 +183,21 @@ async function handle_event(event) {
break;
}
case EVENT.ERASER: {
for (const other_event of storage.events) {
if (other_event.type === EVENT.STROKE && other_event.stroke_id === event.stroke_id) {
// Might already be deleted because of local prediction
if (!other_event.deleted) {
other_event.deleted = true;
const stats = stroke_stats(other_event.points, storage.cursor.width);
redraw_region(stats.bbox);
}
break;
}
}
break;
}
default: {
console.error('fuck');
}

6
client/send.js

@ -64,6 +64,11 @@ function ser_event(s, event) { @@ -64,6 +64,11 @@ function ser_event(s, event) {
break;
}
case EVENT.ERASER: {
ser_u32(s, event.stroke_id);
break;
}
default: {
console.error('fuck');
}
@ -145,6 +150,7 @@ function push_event(event) { @@ -145,6 +150,7 @@ function push_event(event) {
break;
}
case EVENT.ERASER:
case EVENT.IMAGE:
case EVENT.IMAGE_MOVE:
case EVENT.UNDO:

5
server/deserializer.js

@ -68,6 +68,11 @@ export function event(d) { @@ -68,6 +68,11 @@ export function event(d) {
break;
}
case EVENT.ERASER: {
event.stroke_id = u32(d);
break;
}
default: {
console.error('fuck');
console.trace();

1
server/enums.js

@ -11,6 +11,7 @@ export const EVENT = Object.freeze({ @@ -11,6 +11,7 @@ export const EVENT = Object.freeze({
REDO: 31,
IMAGE: 40,
IMAGE_MOVE: 41,
ERASER: 50,
});
export const MESSAGE = Object.freeze({

2
server/recv.js

@ -25,6 +25,7 @@ function handle_event(session, event) { @@ -25,6 +25,7 @@ function handle_event(session, event) {
break;
}
case EVENT.ERASER:
case EVENT.IMAGE:
case EVENT.IMAGE_MOVE:
case EVENT.UNDO: {
@ -58,7 +59,6 @@ async function recv_syn(d, session) { @@ -58,7 +59,6 @@ async function recv_syn(d, session) {
if (i >= first) {
event.desk_id = session.desk_id;
event.user_id = session.user_id;
event.stroke_id = null;
handle_event(session, event);
events.push(event);
}

7
server/send.js

@ -16,7 +16,7 @@ function event_size(event) { @@ -16,7 +16,7 @@ function event_size(event) {
}
case EVENT.STROKE: {
size += 2 + 2 + 4; // point count + width + color
size += 4 + 2 + 2 + 4; // stroke id + point count + width + color
size += event.points.byteLength;
break;
}
@ -32,6 +32,11 @@ function event_size(event) { @@ -32,6 +32,11 @@ function event_size(event) {
break;
}
case EVENT.ERASER: {
size += 4; // stroke id
break;
}
default: {
console.error('fuck');
console.trace();

6
server/serializer.js

@ -44,6 +44,7 @@ export function event(s, event) { @@ -44,6 +44,7 @@ export function event(s, event) {
case EVENT.STROKE: {
const points_bytes = event.points;
u32(s, event.stroke_id);
u16(s, points_bytes.byteLength / 2 / 2); // each point is 2 u16s
u16(s, event.width);
u32(s, event.color);
@ -64,6 +65,11 @@ export function event(s, event) { @@ -64,6 +65,11 @@ export function event(s, event) {
break;
}
case EVENT.ERASER: {
u32(s, event.stroke_id);
break;
}
default: {
console.error('fuck');
console.trace();

Loading…
Cancel
Save