Browse Source

Some kind of shitty webgl line renderer

infinite
A.Olokhtonov 2 years ago
parent
commit
7011cc86be
  1. 20
      client/cursor.js
  2. 6
      client/draw.js
  3. 16
      client/index.js
  4. 6
      client/math.js
  5. 21
      client/texput.log
  6. 24
      client/webgl.html
  7. 358
      client/webgl.js

20
client/cursor.js

@ -4,8 +4,8 @@ function on_down(e) { @@ -4,8 +4,8 @@ function on_down(e) {
// Scroll wheel (mouse button 3)
if (e.button === 1) {
storage.state.moving = true;
storage.state.mousedown = true;
// storage.state.moving = true;
// storage.state.mousedown = true;
return;
}
@ -225,8 +225,18 @@ function on_leave(e) { @@ -225,8 +225,18 @@ function on_leave(e) {
}
function on_resize(e) {
storage.canvas.max_scroll_x = storage.canvas.width - window.innerWidth;
storage.canvas.max_scroll_y = storage.canvas.height - window.innerHeight;
const width = window.innerWidth;
const height = window.innerHeight;
elements.canvas0.width = elements.canvas1.width = width;
elements.canvas0.height = elements.canvas1.height = height;
storage.ctx1.lineJoin = storage.ctx1.lineCap = storage.ctx0.lineJoin = storage.ctx0.lineCap = 'round';
storage.ctx1.lineWidth = storage.ctx0.lineWidth = storage.cursor.width;
redraw_region({'xmin': 0, 'xmax': width, 'ymin': 0, 'ymax': width});
// storage.canvas.max_scroll_x = storage.canvas.width - window.innerWidth;
// storage.canvas.max_scroll_y = storage.canvas.height - window.innerHeight;
}
async function on_drop(e) {
@ -257,6 +267,8 @@ async function on_drop(e) { @@ -257,6 +267,8 @@ async function on_drop(e) {
}
function on_wheel(e) {
return;
const x = Math.round((e.clientX + storage.canvas.offset_x) / storage.canvas.zoom);
const y = Math.round((e.clientY + storage.canvas.offset_y) / storage.canvas.zoom);

6
client/draw.js

@ -43,6 +43,8 @@ function predraw_user(user_id, event) { @@ -43,6 +43,8 @@ function predraw_user(user_id, event) {
}
function redraw_region(bbox) {
// const start = performance.now();
if (bbox.xmin === bbox.xmax || bbox.ymin === bbox.ymax) {
return;
}
@ -63,4 +65,8 @@ function redraw_region(bbox) { @@ -63,4 +65,8 @@ function redraw_region(bbox) {
}
storage.ctx0.restore();
// const end = performance.now();
// if (config.debug_print) console.debug(`Redraw took ${end - start}ms`);
}

16
client/index.js

@ -307,17 +307,18 @@ function main() { @@ -307,17 +307,18 @@ function main() {
update_brush();
storage.canvas.offset_x = window.scrollX;
storage.canvas.offset_y = window.scrollY;
// storage.canvas.offset_x = window.scrollX;
// storage.canvas.offset_y = window.scrollY;
storage.canvas.max_scroll_x = storage.canvas.width - window.innerWidth;
storage.canvas.max_scroll_y = storage.canvas.height - window.innerHeight;
// storage.canvas.max_scroll_x = storage.canvas.width - window.innerWidth;
// storage.canvas.max_scroll_y = storage.canvas.height - window.innerHeight;
storage.ctx0 = elements.canvas0.getContext('2d');
storage.ctx1 = elements.canvas1.getContext('2d');
storage.ctx1.canvas.width = storage.ctx0.canvas.width = storage.canvas.width;
storage.ctx1.canvas.height = storage.ctx0.canvas.height = storage.canvas.height;
on_resize();
// storage.ctx1.canvas.width = storage.ctx0.canvas.width = storage.canvas.width;
// storage.ctx1.canvas.height = storage.ctx0.canvas.height = storage.canvas.height;
storage.ctx1.lineJoin = storage.ctx1.lineCap = storage.ctx0.lineJoin = storage.ctx0.lineCap = 'round';
storage.ctx1.lineWidth = storage.ctx0.lineWidth = storage.cursor.width;
@ -328,7 +329,6 @@ function main() { @@ -328,7 +329,6 @@ function main() {
elements.toucher.addEventListener('keydown', on_keydown);
elements.toucher.addEventListener('keyup', on_keyup);
elements.toucher.addEventListener('resize', on_resize);
elements.toucher.addEventListener('contextmenu', cancel);
elements.toucher.addEventListener('wheel', on_wheel);
@ -343,4 +343,6 @@ function main() { @@ -343,4 +343,6 @@ function main() {
elements.canvas0.addEventListener('dragover', on_move);
elements.canvas0.addEventListener('drop', on_drop);
elements.canvas0.addEventListener('mouseleave', on_leave);
window.addEventListener('resize', on_resize);
}

6
client/math.js

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
function rdp_find_max(points, start, end) {
const EPS = 0.5;
const EPS = 0.25;
let result = -1;
let max_dist = 0;
@ -75,8 +75,8 @@ function process_ewmv(points, round = false) { @@ -75,8 +75,8 @@ function process_ewmv(points, round = false) {
}
function process_stroke(points) {
const result0 = process_ewmv(points);
const result1 = process_rdp(result0, true);
// const result0 = process_ewmv(points);
const result1 = process_rdp(points, true);
return result1;
}

21
client/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.3.25) 8 APR 2023 22:14
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!

24
client/webgl.html

@ -0,0 +1,24 @@ @@ -0,0 +1,24 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Desk</title>
<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">
<script type="text/javascript" src="math.js?v=1"></script>
<script type="text/javascript" src="webgl.js?v=1"></script>
<style>
html, body {
margin: 0;
padding: 0;
width: 100%;
height: 100%;
overflow: hidden;
}
</style>
</head>
<body>
<canvas id="c"></canvas>
</body>
</html>

358
client/webgl.js

@ -0,0 +1,358 @@ @@ -0,0 +1,358 @@
document.addEventListener('DOMContentLoaded', main);
const vertex_shader_source = `
attribute vec2 pos;
uniform vec2 u_scale;
uniform vec2 u_res;
uniform vec2 u_translation;
uniform int u_layer;
void main() {
vec2 screen01 = (pos * u_scale + u_translation) / u_res;
vec2 screen02 = screen01 * 2.0;
screen02.y = 2.0 - screen02.y;
vec2 screen11 = screen02 - 1.0;
gl_Position = vec4(screen11, u_layer, 1);
}
`;
const fragment_shader_source = `
precision mediump float;
uniform vec3 u_color;
void main() {
gl_FragColor = vec4(u_color, 1);
}
`;
function create_shader(gl, type, source) {
const shader = gl.createShader(type);
gl.shaderSource(shader, source);
gl.compileShader(shader);
if (gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
return shader;
}
console.error(type, ':', gl.getShaderInfoLog(shader));
gl.deleteShader(shader);
}
function create_program(gl, vs, fs) {
const program = gl.createProgram();
gl.attachShader(program, vs);
gl.attachShader(program, fs);
gl.linkProgram(program);
if (gl.getProgramParameter(program, gl.LINK_STATUS)) {
return program;
}
console.error('link:', gl.getProgramInfoLog(program));
gl.deleteProgram(program);
}
function perpendicular(ax, ay, bx, by, width) {
// Place points at (stroke_width / 2) distance from the line
// The direction is an average of perpenducalars to the previous and next points
// if (i === 0) {
const dirx = bx - ax;
const diry = by - ay;
let pdirx = diry;
let pdiry = -dirx;
const pdir_norm = Math.sqrt(pdirx * pdirx + pdiry * pdiry);
pdirx /= pdir_norm;
pdiry /= pdir_norm;
return {
'p1': {
'x': ax + pdirx * width / 2,
'y': ay + pdiry * width / 2,
},
'p2': {
'x': ax - pdirx * width / 2,
'y': ay - pdiry * width / 2,
}
};
}
const canvas_offset = { 'x': 0, 'y': 0 };
let moving = false;
let spacedown = false;
let drawing = false;
let canvas_zoom = 1.0;
let current_stroke = [];
function push_stroke_positions(stroke, stroke_width, positions) {
let last_x1;
let last_y1;
let last_x2;
let last_y2;
const points = stroke.points;
for (let i = 0; i < points.length; ++i) {
const px = points[i].x;
const py = points[i].y;
// These might be undefined
let nextpx;
let nextpy;
if (i < points.length - 1) {
nextpx = points[i + 1].x;
nextpy = points[i + 1].y;
}
if (i === 0) {
const pps = perpendicular(px, py, nextpx, nextpy, stroke_width);
last_x1 = pps.p1.x;
last_y1 = pps.p1.y;
last_x2 = pps.p2.x;
last_y2 = pps.p2.y;
continue;
}
// Place points at (stroke_width / 2) distance from the line
const prevpx = points[i - 1].x;
const prevpy = points[i - 1].y;
let x1;
let y1;
let x2;
let y2;
if (i < points.length - 1) {
const pps1 = perpendicular(px, py, nextpx, nextpy, stroke_width);
const pps2 = perpendicular(px, py, prevpx, prevpy, stroke_width);
const dp1x = (pps1.p2.x - pps1.p1.x);
const dp1y = (pps1.p2.y - pps1.p1.y);
const dp2x = (pps2.p2.x - pps2.p1.x);
const dp2y = (pps2.p2.y - pps2.p1.y);
if (dp1x * dp2x + dp1y * dp2y < 0) {
x1 = (pps1.p1.x + pps2.p2.x) / 2.0;
y1 = (pps1.p1.y + pps2.p2.y) / 2.0;
x2 = (pps1.p2.x + pps2.p1.x) / 2.0;
y2 = (pps1.p2.y + pps2.p1.y) / 2.0;
} else {
x1 = (pps1.p1.x + pps2.p1.x) / 2.0;
y1 = (pps1.p1.y + pps2.p1.y) / 2.0;
x2 = (pps1.p2.x + pps2.p2.x) / 2.0;
y2 = (pps1.p2.y + pps2.p2.y) / 2.0;
}
} else {
const pps = perpendicular(px, py, prevpx, prevpy, stroke_width);
x1 = pps.p2.x;
y1 = pps.p2.y;
x2 = pps.p1.x;
y2 = pps.p1.y;
}
positions.push(last_x1, last_y1);
positions.push(x2, y2);
positions.push(last_x2, last_y2);
positions.push(last_x1, last_y1);
positions.push(x1, y1);
positions.push(x2, y2);
last_x1 = x1;
last_y1 = y1;
last_x2 = x2;
last_y2 = y2;
}
}
function draw(gl, program, locations, buffers, strokes) {
const width = window.innerWidth;
const height = window.innerHeight;
if (gl.canvas.width !== width || gl.canvas.height !== height) {
gl.canvas.width = width;
gl.canvas.height = height;
gl.viewport(0, 0, width, height);
}
gl.clearColor(0, 0, 0, 1);
gl.clear(gl.COLOR_BUFFER_BIT);
gl.useProgram(program);
gl.enableVertexAttribArray(locations['pos']);
gl.uniform2f(locations['u_res'], width, height);
gl.uniform2f(locations['u_scale'], canvas_zoom, canvas_zoom);
gl.uniform2f(locations['u_translation'], canvas_offset.x, canvas_offset.y);
const positions = [];
const stroke_width = 4;
for (const stroke of strokes) {
push_stroke_positions(stroke, stroke_width, positions);
}
if (current_stroke.length > 0) {
push_stroke_positions({'points': current_stroke}, stroke_width, positions);
}
gl.bindBuffer(gl.ARRAY_BUFFER, buffers['pos']);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(positions), gl.STATIC_DRAW);
{
// Tell the attribute how to get data out of positionBuffer (ARRAY_BUFFER)
const size = 2; // 2 components per iteration
const type = gl.FLOAT; // the data is 32bit floats
const normalize = false; // don't normalize the data
const stride = 0; // 0 = move forward size * sizeof(type) each iteration to get the next position
const offset = 0; // start at the beginning of the buffer
gl.vertexAttribPointer(locations['pos'], size, type, normalize, stride, offset);
}
{
const offset = 0;
const count = positions.length / 2;
gl.uniform3f(locations['u_color'], 0.2, 0.2, 0.2);
gl.uniform1i(locations['u_layer'], 0);
gl.drawArrays(gl.TRIANGLES, offset, count);
gl.uniform3f(locations['u_color'], 1, 0, 0);
gl.uniform1i(locations['u_layer'], 1);
gl.drawArrays(gl.POINTS, offset, count);
}
window.requestAnimationFrame(() => draw(gl, program, locations, buffers, strokes));
}
function main() {
const canvas = document.querySelector('#c');
const gl = canvas.getContext('webgl');
if (!gl) {
console.error('FUCK!')
return;
}
const vertex_shader = create_shader(gl, gl.VERTEX_SHADER, vertex_shader_source);
const fragment_shader = create_shader(gl, gl.FRAGMENT_SHADER, fragment_shader_source);
const program = create_program(gl, vertex_shader, fragment_shader)
const locations = {};
const buffers = {};
locations['pos'] = gl.getAttribLocation(program, 'pos');
locations['u_res'] = gl.getUniformLocation(program, 'u_res');
locations['u_scale'] = gl.getUniformLocation(program, 'u_scale');
locations['u_translation'] = gl.getUniformLocation(program, 'u_translation');
locations['u_color'] = gl.getUniformLocation(program, 'u_color');
locations['u_layer'] = gl.getUniformLocation(program, 'u_layer');
buffers['pos'] = gl.createBuffer();
const strokes = [
{
'points': [
{'x': 100, 'y': 100},
{'x': 200, 'y': 200},
{'x': 300, 'y': 100},
]
}
]
window.addEventListener('keydown', (e) => {
if (e.code === 'Space') {
spacedown = true;
}
});
window.addEventListener('keyup', (e) => {
if (e.code === 'Space') {
spacedown = false;
moving = false;
}
});
canvas.addEventListener('mousedown', (e) => {
if (spacedown) {
moving = true;
return;
}
const x = cursor_x = (e.clientX - canvas_offset.x) / canvas_zoom;
const y = cursor_y = (e.clientY - canvas_offset.y) / canvas_zoom;
current_stroke.length = 0;
current_stroke.push({'x': x, 'y': y});
drawing = true;
});
canvas.addEventListener('mousemove', (e) => {
if (moving) {
canvas_offset.x += e.movementX;
canvas_offset.y += e.movementY;
return;
}
if (drawing) {
const x = cursor_x = (e.clientX - canvas_offset.x) / canvas_zoom;
const y = cursor_y = (e.clientY - canvas_offset.y) / canvas_zoom;
current_stroke.push({'x': x, 'y': y});
}
});
canvas.addEventListener('mouseup', (e) => {
if (spacedown) {
moving = false;
return;
}
if (drawing) {
strokes.push({'points': process_stroke(current_stroke)});
current_stroke.length = 0;
drawing = false;
return;
}
});
canvas.addEventListener('wheel', (e) => {
const x = Math.round((e.clientX - canvas_offset.x) / canvas_zoom);
const y = Math.round((e.clientY - canvas_offset.y) / canvas_zoom);
const dz = (e.deltaY < 0 ? 0.1 : -0.1);
const old_zoom = canvas_zoom;
canvas_zoom *= (1.0 + dz);
if (canvas_zoom > 10.0) {
canvas_zoom = old_zoom;
return;
}
if (canvas_zoom < 0.2) {
canvas_zoom = old_zoom;
return;
}
const zoom_offset_x = Math.round((dz * old_zoom) * x);
const zoom_offset_y = Math.round((dz * old_zoom) * y);
canvas_offset.x -= zoom_offset_x;
canvas_offset.y -= zoom_offset_y;
});
window.requestAnimationFrame(() => draw(gl, program, locations, buffers, strokes));
}
Loading…
Cancel
Save