Browse Source

Per-user stroke width and color (for dynamic strokes) kinda work

infinite
A.Olokhtonov 2 years ago
parent
commit
cb783db614
  1. 47
      client/client_recv.js
  2. 26
      client/client_send.js
  3. 22
      client/index.html
  4. 19
      client/tools.js
  5. 19
      client/webgl.js
  6. 34
      client/webgl_geometry.js
  7. 8
      client/webgl_listeners.js
  8. 10
      server/deserializer.js
  9. 2
      server/enums.js
  10. 4
      server/recv.js
  11. 13
      server/send.js
  12. 10
      server/serializer.js
  13. 21
      server/texput.log

47
client/client_recv.js

@ -57,6 +57,16 @@ function des_event(d) { @@ -57,6 +57,16 @@ function des_event(d) {
break;
}
case EVENT.SET_COLOR: {
event.color = des_u32(d);
break;
}
case EVENT.SET_WIDTH: {
event.width = des_u16(d);
break;
}
case EVENT.STROKE: {
const stroke_id = des_u32(d);
const point_count = des_u16(d);
@ -116,12 +126,39 @@ function bitmap_bbox(event) { @@ -116,12 +126,39 @@ function bitmap_bbox(event) {
return bbox;
}
function init_player_defaults(state, player_id) {
state.players[player_id] = {
'color': config.default_color,
'width': config.default_width,
};
}
function handle_event(state, context, event, relax = false) {
if (config.debug_print) console.debug(`event type ${event.type} from user ${event.user_id}`);
let need_draw = false;
if (!(event.user_id in state.players)) {
init_player_defaults(state, event.user_id);
}
switch (event.type) {
case EVENT.PREDRAW: {
update_dynamic_stroke(state, context, event.user_id, {'x': event.x, 'y': event.y});
need_draw = true;
break;
}
case EVENT.SET_COLOR: {
state.players[event.user_id].color = event.color;
break;
}
case EVENT.SET_WIDTH: {
state.players[event.user_id].width = event.width;
break;
}
case EVENT.STROKE: {
if (event.user_id != state.me) {
clear_dynamic_stroke(state, context, event.user_id);
@ -262,6 +299,8 @@ async function handle_message(state, context, d) { @@ -262,6 +299,8 @@ async function handle_message(state, context, d) {
state.me = des_u32(d);
state.server_lsn = des_u32(d);
init_player_defaults(state, state.me);
if (state.server_lsn > state.lsn) {
// Server knows something that we don't
state.lsn = state.server_lsn;
@ -298,12 +337,10 @@ async function handle_message(state, context, d) { @@ -298,12 +337,10 @@ async function handle_message(state, context, d) {
}
case MESSAGE.FIRE: {
const user_id = des_u32(d);
const predraw_event = des_event(d);
update_dynamic_stroke(state, context, user_id, {'x': predraw_event.x, 'y': predraw_event.y});
const event = des_event(d);
const need_draw = handle_event(state, context, event);
do_draw = true;
do_draw = do_draw || need_draw;
break;
}

26
client/client_send.js

@ -45,6 +45,16 @@ function ser_event(s, event) { @@ -45,6 +45,16 @@ function ser_event(s, event) {
break;
}
case EVENT.SET_COLOR: {
ser_u32(s, event.color);
break;
}
case EVENT.SET_WIDTH: {
ser_u16(s, event.width);
break;
}
case EVENT.STROKE: {
ser_u16(s, event.points.length);
ser_u16(s, event.width);
@ -217,6 +227,20 @@ function predraw_event(x, y) { @@ -217,6 +227,20 @@ function predraw_event(x, y) {
};
}
function color_event(color_u32) {
return {
'type': EVENT.SET_COLOR,
'color': color_u32,
};
}
function width_event(width) {
return {
'type': EVENT.SET_WIDTH,
'width': width,
};
}
function stroke_event(state) {
return {
'type': EVENT.STROKE,
@ -224,4 +248,4 @@ function stroke_event(state) { @@ -224,4 +248,4 @@ function stroke_event(state) {
'width': state.current_strokes[state.me].width,
'color': state.current_strokes[state.me].color,
};
}
}

22
client/index.html

@ -6,19 +6,19 @@ @@ -6,19 +6,19 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<link rel="shortcut icon" href="icons/favicon.svg" id="favicon">
<link rel="stylesheet" type="text/css" href="default.css?v=14">
<link rel="stylesheet" type="text/css" href="default.css?v=17">
<script type="text/javascript" src="math.js?v=14"></script>
<script type="text/javascript" src="aux.js?v=14"></script>
<script type="text/javascript" src="tools.js?v=14"></script>
<script type="text/javascript" src="webgl_geometry.js?v=14"></script>
<script type="text/javascript" src="webgl_shaders.js?v=14"></script>
<script type="text/javascript" src="webgl_listeners.js?v=14"></script>
<script type="text/javascript" src="webgl.js?v=14"></script>
<script type="text/javascript" src="math.js?v=17"></script>
<script type="text/javascript" src="aux.js?v=17"></script>
<script type="text/javascript" src="tools.js?v=17"></script>
<script type="text/javascript" src="webgl_geometry.js?v=17"></script>
<script type="text/javascript" src="webgl_shaders.js?v=17"></script>
<script type="text/javascript" src="webgl_listeners.js?v=17"></script>
<script type="text/javascript" src="webgl.js?v=17"></script>
<script type="text/javascript" src="client_send.js?v=14"></script>
<script type="text/javascript" src="client_recv.js?v=14"></script>
<script type="text/javascript" src="websocket.js?v=14"></script>
<script type="text/javascript" src="client_send.js?v=17"></script>
<script type="text/javascript" src="client_recv.js?v=17"></script>
<script type="text/javascript" src="websocket.js?v=17"></script>
</head>
<body>
<canvas id="c"></canvas>

19
client/tools.js

@ -17,7 +17,12 @@ function switch_color(state, item) { @@ -17,7 +17,12 @@ function switch_color(state, item) {
state.colors.active_element.classList.remove('active');
}
state.colors.active = color_to_u32(color);
if (state.me in state.players) {
const color_u32 = color_to_u32(color);
state.players[state.me].color = color_u32
fire_event(color_event(color_u32));
}
state.colors.active_element = item;
state.colors.active_element.classList.add('active');
}
@ -27,7 +32,7 @@ function show_stroke_preview(state, size) { @@ -27,7 +32,7 @@ function show_stroke_preview(state, size) {
preview.style.width = size * state.canvas.zoom + 'px';
preview.style.height = size * state.canvas.zoom + 'px';
preview.style.background = color_from_u32(state.colors.active);
preview.style.background = color_from_u32(state.players[state.me].color);
preview.classList.remove('dhide');
}
@ -38,7 +43,7 @@ function hide_stroke_preview() { @@ -38,7 +43,7 @@ function hide_stroke_preview() {
function switch_stroke_width(e, state) {
const value = e.target.value;
state.stroke_width = value;
state.players[state.me].width = value;
show_stroke_preview(state, value);
if (state.hide_preview) {
@ -48,6 +53,11 @@ function switch_stroke_width(e, state) { @@ -48,6 +53,11 @@ function switch_stroke_width(e, state) {
state.hide_preview = setTimeout(hide_stroke_preview, config.brush_preview_timeout);
}
function broadcast_stroke_width(e, state) {
const value = e.target.value;
fire_event(width_event(value));
}
function init_tools(state) {
const tools = document.querySelectorAll('.tools .tool');
const colors = document.querySelectorAll('.pallete .color');
@ -61,8 +71,9 @@ function init_tools(state) { @@ -61,8 +71,9 @@ function init_tools(state) {
const slider = document.querySelector('#stroke-width');
slider.value = state.stroke_width;
// slider.value = state.players[state.me].width;
slider.addEventListener('input', (e) => switch_stroke_width(e, state));
slider.addEventListener('change', (e) => broadcast_stroke_width(e, state));
document.querySelector('.phone-extra-controls').addEventListener('click', zenmode);
}

19
client/webgl.js

@ -90,10 +90,16 @@ const config = { @@ -90,10 +90,16 @@ const config = {
debug_print: true,
min_zoom: 0.01,
max_zoom: 100.0,
default_color: 0x00,
default_width: 8,
};
const EVENT = Object.freeze({
PREDRAW: 10,
SET_COLOR: 11,
SET_WIDTH: 12,
STROKE: 20,
RULER: 21, /* gets re-written with EVENT.STROKE before sending to server */
UNDO: 30,
@ -121,6 +127,16 @@ function event_size(event) { @@ -121,6 +127,16 @@ function event_size(event) {
break;
}
case EVENT.SET_COLOR: {
size += 4;
break;
}
case EVENT.SET_WIDTH: {
size += 2;
break;
}
case EVENT.STROKE: {
size += 4 + 2 + 2 + 4 + event.points.length * 4 * 2; // u32 stroke id + u16 (count) + u16 (width) + u32 (color + count * (f32, f32) points
break;
@ -168,8 +184,6 @@ function main() { @@ -168,8 +184,6 @@ function main() {
'lsn': 0,
'server_lsn': 0,
'stroke_width': 8,
'touch': {
'moves': 0,
'drawing': false,
@ -197,7 +211,6 @@ function main() { @@ -197,7 +211,6 @@ function main() {
},
'colors': {
'active': null,
'active_element': null,
},

34
client/webgl_geometry.js

@ -141,24 +141,21 @@ function recompute_dynamic_data(state, context) { @@ -141,24 +141,21 @@ function recompute_dynamic_data(state, context) {
context.dynamic_positions_f32 = new Float32Array(total_dynamic_length);
context.dynamic_colors_u8 = new Uint8Array(total_dynamic_length / 2 * 3);
// TODO: preview stroke colors for other users
context.dynamic_colors_u8.fill(0);
let at = 0;
for (const player_id in context.dynamic_positions) {
context.dynamic_positions_f32.set(context.dynamic_positions[player_id], at);
const color_u32 = state.players[player_id].color;
if (parseInt(player_id) === state.me) {
const color_u32 = state.colors.active;
const r = (color_u32 >> 16) & 0xFF;
const g = (color_u32 >> 8) & 0xFF;
const b = color_u32 & 0xFF;
for (let i = 0; i < context.dynamic_positions[player_id].length; ++i) {
context.dynamic_colors_u8[at / 2 * 3 + i * 3 + 0] = r;
context.dynamic_colors_u8[at / 2 * 3 + i * 3 + 1] = g;
context.dynamic_colors_u8[at / 2 * 3 + i * 3 + 2] = b;
}
const r = (color_u32 >> 16) & 0xFF;
const g = (color_u32 >> 8) & 0xFF;
const b = color_u32 & 0xFF;
for (let i = 0; i < context.dynamic_positions[player_id].length; ++i) {
context.dynamic_colors_u8[at / 2 * 3 + i * 3 + 0] = r;
context.dynamic_colors_u8[at / 2 * 3 + i * 3 + 1] = g;
context.dynamic_colors_u8[at / 2 * 3 + i * 3 + 2] = b;
}
at += context.dynamic_positions[player_id].length;
@ -169,14 +166,17 @@ function update_dynamic_stroke(state, context, player_id, point) { @@ -169,14 +166,17 @@ function update_dynamic_stroke(state, context, player_id, point) {
if (!(player_id in state.current_strokes)) {
state.current_strokes[player_id] = {
'points': [],
'width': state.stroke_width,
'color': state.colors.active,
'width': state.players[player_id].width,
'color': state.players[player_id].color,
};
context.dynamic_positions[player_id] = [];
context.dynamic_colors[player_id] = [];
}
state.current_strokes[player_id].color = state.players[player_id].color;
state.current_strokes[player_id].width = state.players[player_id].width;
// TODO: incremental
context.dynamic_positions[player_id].length = 0;
context.dynamic_colors[player_id].length = 0;
@ -190,8 +190,8 @@ function update_dynamic_stroke(state, context, player_id, point) { @@ -190,8 +190,8 @@ function update_dynamic_stroke(state, context, player_id, point) {
function clear_dynamic_stroke(state, context, player_id) {
if (player_id in state.current_strokes) {
state.current_strokes[player_id].points.length = 0;
state.current_strokes[player_id].color = state.colors.active;
state.current_strokes[player_id].width = state.stroke_width;
state.current_strokes[player_id].color = state.players[state.me].color;
state.current_strokes[player_id].width = state.players[state.me].width;
context.dynamic_positions[player_id].length = 0;
recompute_dynamic_data(state, context);
}

8
client/webgl_listeners.js

@ -108,8 +108,8 @@ function mouseup(e, state, context) { @@ -108,8 +108,8 @@ function mouseup(e, state, context) {
if (state.drawing) {
const stroke = {
'color': state.colors.active,
'width': state.stroke_width,
'color': state.players[state.me].color,
'width': state.players[state.me].width,
'points': process_stroke(state.current_strokes[state.me].points),
'user_id': state.me,
};
@ -321,8 +321,8 @@ function touchend(e, state, context) { @@ -321,8 +321,8 @@ function touchend(e, state, context) {
// await queue_event(event);
const stroke = {
'color': state.colors.active,
'width': state.stroke_width,
'color': state.players[state.me].color,
'width': state.players[state.me].width,
'points': process_stroke(state.current_strokes[state.me].points),
'user_id': state.me,
};

10
server/deserializer.js

@ -56,6 +56,16 @@ export function event(d) { @@ -56,6 +56,16 @@ export function event(d) {
break;
}
case EVENT.SET_COLOR: {
event.color = u32(d);
break;
}
case EVENT.SET_WIDTH: {
event.width = u16(d);
break;
}
case EVENT.STROKE: {
// point_count + width align to 4 bytes :D
const point_count = u16(d);

2
server/enums.js

@ -6,6 +6,8 @@ export const SESSION = Object.freeze({ @@ -6,6 +6,8 @@ export const SESSION = Object.freeze({
export const EVENT = Object.freeze({
PREDRAW: 10,
SET_COLOR: 11,
SET_WIDTH: 12,
STROKE: 20,
UNDO: 30,
REDO: 31,

4
server/recv.js

@ -77,6 +77,8 @@ async function recv_syn(d, session) { @@ -77,6 +77,8 @@ async function recv_syn(d, session) {
function recv_fire(d, session) {
const event = des.event(d);
event.user_id = session.user_id;
for (const sid in sessions) {
const other = sessions[sid];
@ -92,7 +94,7 @@ function recv_fire(d, session) { @@ -92,7 +94,7 @@ function recv_fire(d, session) {
continue;
}
send.send_fire(other.ws, session.user_id, event);
send.send_fire(other.ws, event);
}
}

13
server/send.js

@ -15,6 +15,16 @@ function event_size(event) { @@ -15,6 +15,16 @@ function event_size(event) {
break;
}
case EVENT.SET_COLOR: {
size += 4;
break;
}
case EVENT.SET_WIDTH: {
size += 2;
break;
}
case EVENT.STROKE: {
size += 4 + 2 + 2 + 4; // stroke id + point count + width + color
size += event.points.byteLength;
@ -139,7 +149,7 @@ export function send_ack(ws, lsn) { @@ -139,7 +149,7 @@ export function send_ack(ws, lsn) {
ws.send(s.buffer);
}
export function send_fire(ws, user_id, event) {
export function send_fire(ws, event) {
if (!ws) {
return;
}
@ -147,7 +157,6 @@ export function send_fire(ws, user_id, event) { @@ -147,7 +157,6 @@ export function send_fire(ws, user_id, event) {
const s = ser.create(1 + 4 + event_size(event));
ser.u8(s, MESSAGE.FIRE);
ser.u32(s, user_id);
ser.event(s, event);
ws.send(s.buffer);

10
server/serializer.js

@ -47,6 +47,16 @@ export function event(s, event) { @@ -47,6 +47,16 @@ export function event(s, event) {
break;
}
case EVENT.SET_COLOR: {
u32(s, event.color);
break;
}
case EVENT.SET_WIDTH: {
u16(s, event.width);
break;
}
case EVENT.STROKE: {
const points_bytes = event.points;
u32(s, event.stroke_id);

21
server/texput.log

@ -0,0 +1,21 @@ @@ -0,0 +1,21 @@
This is pdfTeX, Version 3.141592653-2.6-1.40.24 (TeX Live 2022/Debian) (preloaded format=pdflatex 2023.4.13) 16 APR 2023 21:20
entering extended mode
restricted \write18 enabled.
%&-line parsing enabled.
**
! Emergency stop.
<*>
End of file on the terminal!
Here is how much of TeX's memory you used:
3 strings out of 476091
111 string characters out of 5794081
1849330 words of memory out of 5000000
20488 multiletter control sequences out of 15000+600000
512287 words of font info for 32 fonts, out of 8000000 for 9000
1141 hyphenation exceptions out of 8191
0i,0n,0p,1b,6s stack positions out of 10000i,1000n,20000p,200000b,200000s
! ==> Fatal error occurred, no output PDF file produced!
Loading…
Cancel
Save