Browse Source

Significantly improve stroke smoothing and pressure handling. "Fix" issue with underallocation of WASM memory by doubling the size

ssao
A.Olokhtonov 12 months ago
parent
commit
eb66ffbcad
  1. 67
      client/math.js
  2. 3
      client/speed.js
  3. 2
      client/wasm/lod.c
  4. BIN
      client/wasm/lod.wasm
  5. 29
      client/webgl_geometry.js
  6. 11
      client/webgl_listeners.js

67
client/math.js

@ -60,20 +60,24 @@ function process_rdp_indices(state, zoom, stroke) {
return npoints; return npoints;
} }
function process_ewmv(points, round = false) { function exponential_smoothing(points, last, up_to) {
const result = [];
const alpha = 0.5; const alpha = 0.5;
result.push(points[0]); let pr = 0;
let start = points.length - up_to;
if (start < 0) {
start = 0;
}
for (let i = 1; i < points.length; ++i) { for (let i = start; i < points.length; ++i) {
const p = points[i]; const p = points[i];
const x = Math.round(alpha * p.x + (1 - alpha) * result[result.length - 1].x); pr = alpha * p.pressure + (1 - alpha) * pr;
const y = Math.round(alpha * p.y + (1 - alpha) * result[result.length - 1].y);
result.push({'x': x, 'y': y});
} }
return result; pr = alpha * last.pressure + (1 - alpha) * pr;
return pr;
} }
function process_stroke(state, zoom, stroke) { function process_stroke(state, zoom, stroke) {
@ -92,7 +96,7 @@ function process_stroke(state, zoom, stroke) {
} }
function rdp_find_max2(zoom, points, start, end) { function rdp_find_max2(zoom, points, start, end) {
const EPS = 1.0 / zoom; const EPS = 0.125 / zoom;
let result = -1; let result = -1;
let max_dist = 0; let max_dist = 0;
@ -107,7 +111,7 @@ function rdp_find_max2(zoom, points, start, end) {
const sin_theta = dy / dist_ab; const sin_theta = dy / dist_ab;
const cos_theta = dx / dist_ab; const cos_theta = dx / dist_ab;
for (let i = start; i < end; ++i) { for (let i = start + 1; i < end; ++i) {
const p = points[i]; const p = points[i];
const ox = p.x - a.x; const ox = p.x - a.x;
@ -145,9 +149,48 @@ function process_rdp_r2(zoom, points, start, end) {
} }
function process_rdp2(zoom, points) { function process_rdp2(zoom, points) {
const result = process_rdp_r2(zoom, points, 0, points.length - 1); const result = [];
result.unshift(points[0]); const stack = [];
stack.push({
'type': 0,
'start': 0,
'end': points.length - 1,
});
result.push(points[0]);
while (stack.length > 0) {
const entry = stack.pop();
if (entry.type === 0) {
const max = rdp_find_max2(zoom, points, entry.start, entry.end);
if (max !== -1) {
stack.push({
'type': 0,
'start': max,
'end': entry.end
});
stack.push({
'type': 1,
'index': max,
});
stack.push({
'type': 0,
'start': entry.start,
'end': max,
});
}
} else {
result.push(points[entry.index]);
}
}
result.push(points[points.length - 1]); result.push(points[points.length - 1]);
return result; return result;
} }

3
client/speed.js

@ -120,8 +120,9 @@ function wasm_ensure_by(state, nstrokes, ncoords) {
if (realloc) { if (realloc) {
const current_pages = Math.ceil(state.wasm.memory.buffer.byteLength / (4096 * 16)); const current_pages = Math.ceil(state.wasm.memory.buffer.byteLength / (4096 * 16));
const need_pages = Math.ceil((state.wasm.coords_bytes * 3 + state.wasm.stroke_bytes * 2) / (4096 * 16)); // TODO: figure out actual memory requirements const need_pages = 2 * Math.ceil((state.wasm.coords_bytes * 3 + state.wasm.stroke_bytes * 2) / (4096 * 16)); // TODO: figure out actual memory requirements
const grow_by = Math.max(1, need_pages - current_pages); const grow_by = Math.max(1, need_pages - current_pages);
// const grow_by = 16;
state.wasm.memory.grow(grow_by); state.wasm.memory.grow(grow_by);
state.wasm.exports.free_static(); state.wasm.exports.free_static();

2
client/wasm/lod.c

@ -220,6 +220,7 @@ do_lod(int *clipped_indices, int clipped_count, float zoom,
int segments_head = 0; int segments_head = 0;
int stack[4096]; // TODO: what's a reasonable max size for this? int stack[4096]; // TODO: what's a reasonable max size for this?
int max_stack_size = 0;
for (int i = 0; i < clipped_count; ++i) { for (int i = 0; i < clipped_count; ++i) {
int stroke_index = clipped_indices[i]; int stroke_index = clipped_indices[i];
@ -243,6 +244,7 @@ do_lod(int *clipped_indices, int clipped_count, float zoom,
stack[stack_head++] = point_count - 1; stack[stack_head++] = point_count - 1;
while (stack_head > 0) { while (stack_head > 0) {
if (stack_head > max_stack_size) { max_stack_size = stack_head; }
int end = stack[--stack_head]; int end = stack[--stack_head];
int start = stack[--stack_head]; int start = stack[--stack_head];
int type = stack[--stack_head]; int type = stack[--stack_head];

BIN
client/wasm/lod.wasm

Binary file not shown.

29
client/webgl_geometry.js

@ -151,10 +151,39 @@ function recompute_dynamic_data(state, context) {
context.dynamic_stroke_count = total_strokes; context.dynamic_stroke_count = total_strokes;
} }
let _head = null;
function geometry_add_point(state, context, player_id, point) { function geometry_add_point(state, context, player_id, point) {
if (!state.online) return; if (!state.online) return;
const points = state.players[player_id].points;
if (false && points.length === 1) {
// Fix up pressure of first point to get rid of ugly spike bit
const first_point = points[0];
if (first_point.pressure === 0) {
first_point.pressure = point.pressure;
}
}
if (points.length > 0) {
// pulled from "perfect-freehand" package. MIT
// https://github.com/steveruizok/perfect-freehand/
const streamline = 0.5;
const t = 0.15 + (1 - streamline) * 0.85
const smooth_pressure = exponential_smoothing(points, point, 3);
points.push({
'x': _head.x * t + point.x * (1 - t),
'y': _head.y * t + point.y * (1 - t),
'pressure': _head.pressure * t + smooth_pressure * (1 - t),
});
point.pressure = smooth_pressure;
} else {
state.players[player_id].points.push(point); state.players[player_id].points.push(point);
}
recompute_dynamic_data(state, context); recompute_dynamic_data(state, context);
_head = point;
} }
function geometry_clear_player(state, context, player_id) { function geometry_clear_player(state, context, player_id) {

11
client/webgl_listeners.js

@ -210,7 +210,7 @@ function mousedown(e, state, context) {
} }
if (state.tools.active === 'pencil') { if (state.tools.active === 'pencil') {
canvasp.pressure = 128; canvasp.pressure = Math.ceil(e.pressure * 255);
geometry_clear_player(state, context, state.me); geometry_clear_player(state, context, state.me);
geometry_add_point(state, context, state.me, canvasp); geometry_add_point(state, context, state.me, canvasp);
@ -249,9 +249,10 @@ function mousemove(e, state, context) {
if (state.me in state.players) { if (state.me in state.players) {
const me = state.players[state.me]; const me = state.players[state.me];
const width = Math.max(me.width * state.canvas.zoom, 2.0); const width = Math.max(me.width * state.canvas.zoom, 2.0);
const brush_x = screenp.x - width / 2 - 2; const radius = Math.round(width / 2);
const brush_y = screenp.y - width / 2 - 2; const brush_x = screenp.x - radius - 2;
document.querySelector('.brush-dom').style.transform = `translate(${Math.round(brush_x)}px, ${Math.round(brush_y)}px)`; const brush_y = screenp.y - radius - 2;
document.querySelector('.brush-dom').style.transform = `translate(${brush_x}px, ${brush_y}px)`;
} }
if (state.me in state.players && dist_v2(state.players[state.me].cursor, canvasp) > 5) { if (state.me in state.players && dist_v2(state.players[state.me].cursor, canvasp) > 5) {
@ -376,7 +377,7 @@ function update_cursor(state) {
const me = state.players[state.me]; const me = state.players[state.me];
const width = Math.max(me.width * state.canvas.zoom, 2.0); const width = Math.max(me.width * state.canvas.zoom, 2.0);
const radius = width / 2; const radius = Math.round(width / 2);
const current_color = color_from_u32(me.color); const current_color = color_from_u32(me.color);
const stroke = (me.color === 0xFFFFFF ? 'black' : 'white'); const stroke = (me.color === 0xFFFFFF ? 'black' : 'white');
const svg = `<svg style="display: block" xmlns="http://www.w3.org/2000/svg" width="${width + 4}" height="${width + 4}"> const svg = `<svg style="display: block" xmlns="http://www.w3.org/2000/svg" width="${width + 4}" height="${width + 4}">

Loading…
Cancel
Save