document . addEventListener ( 'DOMContentLoaded' , main ) ;
const config = {
// ws_url: 'wss://desk.some.website/ws/',
// ping_url: 'https://desk.some.website/api/ping',
// image_url: 'https://desk.some.website/images/',
ws _url : ` wss:// ${ window . location . host } /ws/ ` ,
ping _url : ` https:// ${ window . location . host } /api/ping ` ,
image _url : ` https:// ${ window . location . host } /images/ ` ,
sync _timeout : 1000 ,
ws _reconnect _timeout : 2000 ,
brush _preview _timeout : 1000 ,
second _finger _timeout : 500 ,
buffer _first _touchmoves : 5 ,
debug _print : false ,
min _zoom : 0.00001 ,
max _zoom : 1 ,
initial _offline _timeout : 1000 ,
default _color : 0x00 ,
default _width : 8 ,
bytes _per _instance : 4 * 2 + 4 , // axy, stroke_id
bytes _per _stroke : 2 * 3 + 2 , // r, g, b, width
initial _static _bytes : 4096 * 16 ,
initial _dynamic _bytes : 4096 ,
stroke _texture _size : 1024 ,
benchmark : {
zoom : 0.035 ,
offset : { x : 900 , y : 400 } ,
frames : 500 ,
} ,
} ;
const EVENT = Object . freeze ( {
PREDRAW : 10 ,
SET _COLOR : 11 ,
SET _WIDTH : 12 ,
CLEAR : 13 , // clear predraw events from me (because I started a pan instead of drawing)
STROKE : 20 ,
RULER : 21 , // gets re-written with EVENT.STROKE before sending to server
UNDO : 30 ,
REDO : 31 ,
IMAGE : 40 ,
IMAGE _MOVE : 41 ,
ERASER : 50 ,
} ) ;
const MESSAGE = Object . freeze ( {
INIT : 100 ,
SYN : 101 ,
ACK : 102 ,
FULL : 103 ,
FIRE : 104 ,
JOIN : 105 ,
} ) ;
// Source:
// https://stackoverflow.com/a/18473154
function polarToCartesian ( centerX , centerY , radius , angleInDegrees ) {
var angleInRadians = ( angleInDegrees - 90 ) * Math . PI / 180.0 ;
return {
x : centerX + ( radius * Math . cos ( angleInRadians ) ) ,
y : centerY + ( radius * Math . sin ( angleInRadians ) )
} ;
}
function describeArc ( x , y , radius , startAngle , endAngle ) {
var start = polarToCartesian ( x , y , radius , endAngle ) ;
var end = polarToCartesian ( x , y , radius , startAngle ) ;
var largeArcFlag = ( Math . abs ( endAngle - startAngle ) % 360 ) <= 180 ? "0" : "1" ;
var d = [
"M" , start . x , start . y ,
"A" , radius , radius , 0 , largeArcFlag , 0 , end . x , end . y
] . join ( " " ) ;
return d ;
}
let iii = 0 ;
let a _angel = 0 ;
let b _angel = 180 ;
let speed _a = 2 ;
let speed _b = 6 ;
let b _fast = true ;
function start _spinner ( state ) {
const str = describeArc ( 64 , 64 , 32 , a _angel , b _angel ) ;
a _angel += speed _a ;
b _angel += speed _b ;
const diff = b _angel - a _angel ;
if ( diff > 320 ) {
speed _a = 6 ;
speed _b = 2 ;
} else if ( diff < 40 ) {
speed _a = 2 ;
speed _b = 6 ;
}
// if ((speed_a === 1) && Math.abs(a_angel - b_angel) % 360 < 90) {
// speed_a = 3;
// speed_b = 1;
// } else if (Math.abs(a_angel - b_angel) % 360 > 180) {
// speed_a = 1;
// speed_b = 3;
// }
document . querySelector ( '#spinner-path' ) . setAttribute ( 'd' , str ) ;
if ( ! state . online ) {
window . requestAnimationFrame ( ( ) => start _spinner ( state ) ) ;
} else {
document . querySelector ( '.loader' ) . classList . add ( 'hidden' ) ;
}
}
function main ( ) {
const state = {
'online' : false ,
'me' : null ,
'canvas' : {
'offset' : { 'x' : 0 , 'y' : 0 } ,
'zoom' : 1.0 ,
} ,
'cursor' : {
'x' : 0 ,
'y' : 0 ,
} ,
'sn' : 0 ,
'lsn' : 0 ,
'server_lsn' : 0 ,
'touch' : {
'moves' : 0 ,
'drawing' : false ,
'moving' : false ,
'erasing' : false ,
'waiting_for_second_finger' : false ,
'first_finger_position' : null ,
'second_finger_position' : null ,
'buffered' : [ ] ,
'ids' : [ ] ,
} ,
'moving' : false ,
'drawing' : false ,
'spacedown' : false ,
'moving_image' : null ,
'current_strokes' : { } ,
'rdp_mask' : new Uint8Array ( 1024 ) ,
'queue' : [ ] ,
'events' : [ ] ,
'stroke_count' : 0 ,
'starting_index' : 0 ,
'total_points' : 0 ,
'coordinates' : {
'data' : null ,
'count' : 0 ,
} ,
'segments_from' : {
'data' : null ,
'count' : 0 ,
'cap' : 0 ,
} ,
'segments' : {
'data' : null ,
'count' : 0 ,
'cap' : 0 ,
} ,
'bvh' : {
'nodes' : [ ] ,
'root' : null ,
'pqueue' : new MinQueue ( 1024 ) ,
} ,
'tools' : {
'active' : null ,
'active_element' : null ,
} ,
'colors' : {
'active_element' : null ,
} ,
'timers' : {
'hide_preview' : null ,
'offline_toast' : null ,
'raf' : false ,
} ,
'players' : { } ,
'onscreen_segments' : new Uint32Array ( 1024 ) ,
'debug' : {
'red' : false ,
'do_prepass' : true ,
'limit_from' : false ,
'limit_to' : false ,
'render_from' : 0 ,
'render_to' : 0 ,
} ,
'rdp_cache' : { } ,
'stats' : { } ,
} ;
const context = {
'canvas' : null ,
'gl' : null ,
'programs' : { } ,
'buffers' : { } ,
'locations' : { } ,
'textures' : { } ,
'dynamic_serializer' : serializer _create ( config . initial _dynamic _bytes ) ,
'dynamic_index_serializer' : serializer _create ( config . initial _dynamic _bytes ) ,
// TODO: i seem to have a lot of these, maybe make a few utility functions? similar to serializer, but for pure typedarray
'clipped_indices' : {
'data' : null ,
'count' : 0 ,
'cap' : 0 ,
} ,
'instance_data_points' : tv _create ( Float32Array , 4096 ) ,
'instance_data_ids' : tv _create ( Uint32Array , 4096 ) ,
'lods' : [ ] ,
'stroke_data' : serializer _create ( config . initial _static _bytes ) ,
'bgcolor' : { 'r' : 1.0 , 'g' : 1.0 , 'b' : 1.0 } ,
'gpu_timer_ext' : null ,
'active_image' : null ,
} ;
start _spinner ( state ) ;
const url = new URL ( window . location . href ) ;
const parts = url . pathname . split ( '/' ) ;
config . lod _levels = Math . ceil ( Math . log2 ( 1.0 / config . min _zoom ) ) ;
for ( let i = 0 ; i < config . lod _levels ; ++ i ) {
context . lods . push ( {
'max_zoom' : Math . pow ( 0.25 , i ) , // use this LOD level when current canvas.zoom is less than this value, but not less than the next level max_zoom (or if this is the last zoom level)
'total_points' : 0 ,
'segments' : serializer _create ( config . initial _static _bytes ) ,
'data_buffer' : null ,
'index_buffer' : null ,
} ) ;
}
state . desk _id = parts . length > 0 ? parts [ parts . length - 1 ] : 0 ;
init _webgl ( state , context ) ;
init _listeners ( state , context ) ;
init _tools ( state ) ;
ws _connect ( state , context , true ) ;
schedule _draw ( state , context ) ;
state . timers . offline _toast = setTimeout ( ( ) => ui _offline ( ) , config . initial _offline _timeout ) ;
}