Browse Source

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

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

69
client/math.js

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

3
client/speed.js

@ -120,8 +120,9 @@ function wasm_ensure_by(state, nstrokes, ncoords) { @@ -120,8 +120,9 @@ function wasm_ensure_by(state, nstrokes, ncoords) {
if (realloc) {
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 = 16;
state.wasm.memory.grow(grow_by);
state.wasm.exports.free_static();

2
client/wasm/lod.c

@ -220,6 +220,7 @@ do_lod(int *clipped_indices, int clipped_count, float zoom, @@ -220,6 +220,7 @@ do_lod(int *clipped_indices, int clipped_count, float zoom,
int segments_head = 0;
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) {
int stroke_index = clipped_indices[i];
@ -243,6 +244,7 @@ do_lod(int *clipped_indices, int clipped_count, float zoom, @@ -243,6 +244,7 @@ do_lod(int *clipped_indices, int clipped_count, float zoom,
stack[stack_head++] = point_count - 1;
while (stack_head > 0) {
if (stack_head > max_stack_size) { max_stack_size = stack_head; }
int end = stack[--stack_head];
int start = stack[--stack_head];
int type = stack[--stack_head];

BIN
client/wasm/lod.wasm

Binary file not shown.

31
client/webgl_geometry.js

@ -151,10 +151,39 @@ function recompute_dynamic_data(state, context) { @@ -151,10 +151,39 @@ function recompute_dynamic_data(state, context) {
context.dynamic_stroke_count = total_strokes;
}
let _head = null;
function geometry_add_point(state, context, player_id, point) {
if (!state.online) return;
state.players[player_id].points.push(point);
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);
}
recompute_dynamic_data(state, context);
_head = point;
}
function geometry_clear_player(state, context, player_id) {

11
client/webgl_listeners.js

@ -210,7 +210,7 @@ function mousedown(e, state, context) { @@ -210,7 +210,7 @@ function mousedown(e, state, context) {
}
if (state.tools.active === 'pencil') {
canvasp.pressure = 128;
canvasp.pressure = Math.ceil(e.pressure * 255);
geometry_clear_player(state, context, state.me);
geometry_add_point(state, context, state.me, canvasp);
@ -249,9 +249,10 @@ function mousemove(e, state, context) { @@ -249,9 +249,10 @@ function mousemove(e, state, context) {
if (state.me in state.players) {
const me = state.players[state.me];
const width = Math.max(me.width * state.canvas.zoom, 2.0);
const brush_x = screenp.x - width / 2 - 2;
const brush_y = screenp.y - width / 2 - 2;
document.querySelector('.brush-dom').style.transform = `translate(${Math.round(brush_x)}px, ${Math.round(brush_y)}px)`;
const radius = Math.round(width / 2);
const brush_x = screenp.x - radius - 2;
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) {
@ -376,7 +377,7 @@ function update_cursor(state) { @@ -376,7 +377,7 @@ function update_cursor(state) {
const me = state.players[state.me];
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 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}">

Loading…
Cancel
Save