Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • hlgr/drawing-app
  • sweng-group-15/drawing-app
2 results
Show changes
Commits on Source (251)
Showing with 12232 additions and 4955 deletions
{
"env": {
"test": {
"plugins": ["@babel/plugin-transform-modules-commonjs"]
}
}
}
......@@ -5,11 +5,13 @@
"node": true,
"jest": true
},
"extends": "eslint:recommended",
"plugins": ["testcafe"],
"extends": ["eslint:recommended", "prettier", "plugin:testcafe/recommended"],
"globals": {
"Atomics": "readonly",
"SharedArrayBuffer": "readonly"
},
"parser": "babel-eslint",
"parserOptions": {
"ecmaVersion": 2018,
"sourceType": "module"
......
# Created by https://www.gitignore.io/api/vim,node,macos,visualstudiocode
# Edit at https://www.gitignore.io/?templates=vim,node,macos,visualstudiocode
......@@ -10,6 +9,54 @@ src/liowebrtc
src/rtcpeerconnection
src/signalbuddy
src/yjs
src/drawing-crdt
# Temporary benchmark dump files
.dot-seq-add-packets.json
.dot-seq-erase-packets.json
.dot-seq-sync-packets.json
.dot-seq-add-events.json
.dot-seq-erase-events.json
.dot-seq-sync-events.json
.dot-par-add-packets.json
.dot-par-erase-packets.json
.dot-par-sync-packets.json
.dot-par-add-events.json
.dot-par-erase-events.json
.dot-par-sync-events.json
.path-seq-add-packets.json
.path-seq-erase-packets.json
.path-seq-sync-packets.json
.path-seq-add-events.json
.path-seq-erase-events.json
.path-seq-sync-events.json
.path-par-add-packets.json
.path-par-erase-packets.json
.path-par-sync-packets.json
.path-par-add-events.json
.path-par-erase-events.json
.path-par-sync-events.json
.dot-ver-add-packets.json
.dot-ver-erase-packets.json
.dot-ver-sync-packets.json
.dot-ver-add-events.json
.dot-ver-erase-events.json
.dot-ver-sync-events.json
.path-ver-add-packets.json
.path-ver-erase-packets.json
.path-ver-sync-packets.json
.path-ver-add-events.json
.path-ver-erase-events.json
.path-ver-sync-events.json
# Benchmark output files
plots/*.tsv
plots/*.pdf
### macOS ###
# General
......@@ -118,8 +165,10 @@ typings/
.nuxt
# react / gatsby (customised)
public/js/app.js
public/js/queue.js
public/service-worker.js
public/js
public/benchmarks.html
public/assets/fonts/font-awesome
# vuepress build output
.vuepress/dist
......@@ -155,10 +204,6 @@ tags
### VisualStudioCode ###
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
### VisualStudioCode Patch ###
# Ignore all local history of files
......
default:
image: node:12
image: amio/node-chrome
stages:
- fetch
......@@ -8,21 +8,23 @@ stages:
- build
- test
- deploy
- benchmark
submodule_fetch:
stage: fetch
script:
- chmod 600 .drawing-app-deploy.rsa
- git submodule sync --recursive
- GIT_SSH_COMMAND='ssh -i .drawing-app-deploy.rsa -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no' git submodule update --init
- GIT_SSH_COMMAND='ssh -i '`pwd`'/.drawing-app-deploy.rsa -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no' git submodule update --init --recursive
artifacts:
paths:
- src/liowebrtc
- src/rtcpeerconnection
- src/signalbuddy
- src/yjs
- src/drawing-crdt/pkg
node_install:
npm_install_prod:
stage: deps
dependencies:
- submodule_fetch
......@@ -31,12 +33,9 @@ node_install:
artifacts:
paths:
- node_modules
- src/liowebrtc
- src/rtcpeerconnection
- src/signalbuddy
- src/yjs
dev_node_install:
npm_install:
stage: deps
dependencies:
- submodule_fetch
......@@ -47,48 +46,76 @@ dev_node_install:
- node_modules
- src/liowebrtc
- src/rtcpeerconnection
- src/signalbuddy
- src/yjs
- src/drawing-crdt/pkg
- src/signalbuddy
check_format:
format_check:
stage: check
dependencies:
- dev_node_install
- npm_install
script:
- npx prettier --ignore-path .gitignore --check "**/*.{html,js,json,md}"
- npm run format-check
lint:
stage: check
dependencies:
- dev_node_install
- npm_install
script:
- npx eslint --ignore-path .gitignore "**/*.js"
- npm run lint
backend_build:
build:
stage: build
dependencies:
- dev_node_install
- npm_install
script:
- npm run build
- gcc -E -P -traditional-cpp -o /dev/stdout -DFILES_TO_CACHE_LIST=`find public/ -type f "!" -iname service-worker.js -and "!" -name '.*' | cut -c6- | sed 's_._"_' | sed 's/$/",/' | sort | tr -d '\n'` - < src/service-worker.js | npx prettier --parser babel > public/service-worker.js
artifacts:
paths:
- public/
backend_test:
test:
stage: test
dependencies:
- npm_install
script:
- npm run test
chrome_test:
stage: test
dependencies:
- dev_node_install
- npm_install
- build
script:
- npm test
- npm run start-bg
- npm run test-e2e
deploy:
stage: deploy
dependencies:
- node_install
- backend_build
- npm_install_prod
- build
only:
- master
script:
- apt-get update -qq && apt-get install -qq zip
- zip -r application.zip *
- curl -X POST -u "$DEPLOYMENT_USERNAME:$DEPLOYMENT_PASSWORD" $DEPLOYMENT_ENDPOINT -T application.zip
benchmark:
stage: benchmark
dependencies:
- npm_install
only:
- master
script:
- apt-get -y install gnuplot
- npm run build:bench
- cp __benchmarks__/benchmarks.html public/benchmarks.html
- npm run start-bg
- npm run benchmarks
- npm run plot
artifacts:
paths:
- plots/
......@@ -10,3 +10,6 @@
[submodule "src/yjs"]
path = src/yjs
url = git@gitlab.doc.ic.ac.uk:sweng-group-15/yjs.git
[submodule "src/drawing-crdt"]
path = src/drawing-crdt
url = git@gitlab.doc.ic.ac.uk:sweng-group-15/drawing-crdt.git
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
</head>
<body>
<script src="js/benchmarks.js"></script>
</body>
</html>
import { test, equal, ok, notOk } from "zora"
import chalk from "chalk"
import { connect } from "../src/room.js"
import CRDT, { benchmark } from "../src/wasm-crdt.js"
//import CRDT, { benchmark } from "../src/y-crdt.js"
const { blocksize, eventsGC, syncStep1 } = benchmark
import MockConnection, {
userID,
sendListener,
broadcastListener,
getEventListener,
} from "../src/connection/MockConnection.js"
import { handshake, dotDraw, dotErase, pathDraw, pathErase } from "./data.js"
const remoteUserID = "392bf960-1d18-4482-bbbf-1c85e0132c9a"
function createMessageReceivedEvent(
message,
channel = "crdt",
uid = remoteUserID,
) {
return {
detail: {
uid,
channel,
message,
},
}
}
// Start: Adapted from https://github.com/jprichardson/buffer-json (MIT license)
function stringify(value, space) {
return JSON.stringify(value, replacer, space)
}
function parse(text) {
return JSON.parse(text, reviver)
}
function replacer(key, value) {
if (value instanceof Uint8Array) {
return "base64:" + Buffer.from(value).toString("base64")
}
return value
}
function reviver(key, value) {
if (typeof value == "string" && value.startsWith("base64:")) {
return Uint8Array.from(Buffer.from(value.slice("base64:".length), "base64"))
}
return value
}
// End: Adapted from https://github.com/jprichardson/buffer-json (MIT license)
let fs
async function initFileSystem() {
fs = await new Promise((resolve, reject) =>
window.webkitRequestFileSystem(
window.TEMPORARY,
1024 * 1024 * 100,
resolve,
reject,
),
)
}
async function dumpBSON(filename, data) {
await new Promise((resolve) => {
fs.root.getFile(
filename,
{ create: false },
(fileEntry) => {
fileEntry.remove(resolve, console.error)
},
resolve,
)
})
await new Promise((resolve) => {
fs.root.getFile(
filename,
{ create: true, exclusive: true },
(fileEntry) => {
fileEntry.createWriter((fileWriter) => {
fileWriter.onwriteend = resolve
fileWriter.onerror = console.error
const blob = new Blob([stringify(data)], { type: "text/plain" })
fileWriter.write(blob)
}, console.error)
},
console.error,
)
})
}
async function loadBSON(filename) {
return await new Promise((resolve) => {
fs.root.getFile(
filename,
{},
(fileEntry) => {
fileEntry.file((file) => {
const reader = new FileReader()
reader.onloadend = () => resolve(parse(reader.result))
reader.readAsText(file)
}, console.error)
},
console.error,
)
})
}
function printBenchmark(title, iterations, results) {
console.debug(`\n ${title} (${iterations} iterations):\n`)
for (const title in results) {
const {
timeLoc,
encodeRAM,
packets,
size,
timeRem,
decodeRAM,
events,
} = results[title]
const synchronisation = title == "synchronisation"
console.debug(
chalk` {yellow ⧗} {dim ${title}:} {yellow.inverse ${(
timeLoc /
(1e3 * (synchronisation ? 1 : iterations))
).toFixed(3)}ms ${synchronisation ? "total" : "/ it"}} + {red.inverse ${(
encodeRAM /
(1024 * 1024)
).toFixed(
3,
)}MB} => {dim ${packets} packet(s)} => {magenta.inverse ${size}B} => {yellow.inverse ${(
timeRem /
(1e3 * (synchronisation ? 1 : iterations))
).toFixed(3)}ms ${synchronisation ? "total" : "/ it"}} + {red.inverse ${(
decodeRAM /
(1024 * 1024)
).toFixed(3)}MB} => {dim ${events} event(s)}\n`,
)
}
console.debug(`\n`)
}
function writeBenchmarkHeader(filename, title, results) {
console.info(JSON.stringify({ filename, title, results }))
}
function appendBenchmark(filename, iterations, results) {
console.info(JSON.stringify({ filename, iterations, results }))
}
function captureHeapUsage() {
window.gc()
return window.performance.memory.usedJSHeapSize
}
function runBidirectionalBenchmark(
BENCHMARK,
FILENAME,
ITERATIONSLIST,
BLOCKSIZE,
addData,
eraseData,
addOnInitFrontend,
addBroadcastGroupTimeout,
addOnBroadcastGroup,
addPacketsFilename,
eraseOnInitFrontend,
eraseBroadcastGroupTimeout,
eraseOnBroadcastGroup,
erasePacketsFilename,
syncSendGroupTimeout,
syncOnSendGroup,
syncPacketsFilename,
addOnInitBackend,
addEventGroupTimeout,
addOnEventGroup,
addEventsCache,
addEventsFilename,
eraseOnInitBackend,
eraseEventGroupTimeout,
eraseOnEventGroup,
eraseEventsCache,
eraseEventsFilename,
syncEventGroupTimeout,
syncOnEventGroup,
syncEventsCache,
syncEventsFilename,
) {
if (BENCHMARK && FILENAME) {
writeBenchmarkHeader(FILENAME, BENCHMARK, [
"addPath",
"extendErasureIntervals",
"synchronisation",
])
}
return ITERATIONSLIST.reduce(
(promise, ITERATIONS) =>
promise.then(() => {
const pathIDs = []
let prevTime
let currTime
let connectRAM // eslint-disable-line no-unused-vars
let addLocTime = 0
let addPackets = []
let addSize = 0
let addRAM
let eraseLocTime = 0
let erasePackets = []
let eraseSize = 0
let eraseRAM
let syncLocTime
let syncPackets = []
let syncSize = 0
let syncRAM
let disconnectRAM // eslint-disable-line no-unused-vars
let connectUpdRAM // eslint-disable-line no-unused-vars
let addRemTime = 0
let addEvents = addEventsCache ? [] : 0
let addRemRAM
let eraseRemTime = 0
let eraseEvents = eraseEventsCache ? [] : 0
let eraseRemRAM
let disconnectUpdRAM // eslint-disable-line no-unused-vars
let connectSyncRAM // eslint-disable-line no-unused-vars
let syncRemTime = 0
let syncEvents = syncEventsCache ? [] : 0
let syncRemRAM
let disconnectSyncRAM // eslint-disable-line no-unused-vars
let timeout
let addEventListener = null
let eraseEventListener = null
let room = null
let updateRoom = null
let syncRoom = null
return (
// eslint-disable-next-line no-async-promise-executor
new Promise(async (resolve) => {
userID.uuid = "61a540d6-4522-48c7-a660-1ed501503cb7"
room = await connect("room", CRDT, MockConnection)
getEventListener(
"room",
"messageReceived",
)(createMessageReceivedEvent(handshake, "tw-ml"))
connectRAM = captureHeapUsage()
return resolve()
})
.then(
() =>
new Promise((resolve) => {
let broadcasts = 0
broadcastListener.callback = (channel, message) => {
currTime = window.performance.now()
equal(channel, "crdt")
ok(message instanceof Uint8Array)
addPackets[addPackets.length - 1].push(message)
addSize += message.length
clearTimeout(timeout)
timeout = setTimeout(() => {
broadcasts += 1
addLocTime += (currTime - prevTime) * 1e3
prevTime = window.performance.now()
addOnBroadcastGroup(
room,
addPackets,
pathIDs,
addData,
ITERATIONS,
broadcasts,
resolve,
)
}, addBroadcastGroupTimeout)
}
prevTime = window.performance.now()
addOnInitFrontend(
room,
addPackets,
pathIDs,
addData,
ITERATIONS,
BLOCKSIZE,
)
}),
)
.then(async () => {
broadcastListener.callback = null
await dumpBSON(addPacketsFilename, addPackets)
addPackets = null
addRAM = captureHeapUsage()
})
.then(
() =>
new Promise((resolve) => {
let broadcasts = 0
broadcastListener.callback = (channel, message) => {
currTime = window.performance.now()
equal(channel, "crdt")
ok(message instanceof Uint8Array)
erasePackets[erasePackets.length - 1].push(message)
eraseSize += message.length
clearTimeout(timeout)
timeout = setTimeout(() => {
broadcasts += 1
eraseLocTime += (currTime - prevTime) * 1e3
prevTime = window.performance.now()
eraseOnBroadcastGroup(
room,
erasePackets,
pathIDs,
eraseData,
ITERATIONS,
broadcasts,
resolve,
)
}, eraseBroadcastGroupTimeout)
}
prevTime = window.performance.now()
eraseOnInitFrontend(
room,
erasePackets,
pathIDs,
eraseData,
ITERATIONS,
BLOCKSIZE,
)
}),
)
.then(async () => {
broadcastListener.callback = null
await dumpBSON(erasePacketsFilename, erasePackets)
erasePackets = null
eraseRAM = captureHeapUsage()
})
.then(
() =>
new Promise((resolve) => {
sendListener.callback = (uid, channel, message) => {
const currTime = window.performance.now()
equal(uid, remoteUserID)
equal(channel, "crdt")
ok(message instanceof Uint8Array)
syncLocTime = (currTime - prevTime) * 1e3
syncPackets.push(message)
syncSize += message.length
clearTimeout(timeout)
timeout = setTimeout(
() => syncOnSendGroup(syncPackets, resolve),
syncSendGroupTimeout,
)
}
prevTime = window.performance.now()
getEventListener(
"room",
"messageReceived",
)(createMessageReceivedEvent(syncStep1))
}),
)
.then(async () => {
sendListener.callback = null
await dumpBSON(syncPacketsFilename, syncPackets)
syncPackets = null
syncRAM = captureHeapUsage()
room.disconnect()
room = null
disconnectRAM = captureHeapUsage()
})
.then(
() =>
// eslint-disable-next-line no-async-promise-executor
new Promise(async (resolve) => {
userID.uuid = "5c9e550b-3de8-4a32-80e1-80c08c19891a"
updateRoom = await connect("update", CRDT, MockConnection)
getEventListener(
"update",
"messageReceived",
)(createMessageReceivedEvent(handshake, "tw-ml"))
connectUpdRAM = captureHeapUsage()
addPackets = await loadBSON(addPacketsFilename)
return resolve()
}),
)
.then(
() =>
new Promise((resolve) => {
let broadcasts = 0
let currTime
const timeoutCallback = () => {
broadcasts += 1
addRemTime += (currTime - prevTime) * 1e3
prevTime = window.performance.now()
addOnEventGroup(
addPackets,
ITERATIONS,
broadcasts,
resolve,
addEvents,
pathIDs,
addData,
)
}
addEventListener = (event) => {
currTime = window.performance.now()
if (addEventsCache) {
addEvents.push({ add: event })
} else {
addEvents += 1
}
clearTimeout(timeout)
timeout = setTimeout(timeoutCallback, addEventGroupTimeout)
}
eraseEventListener = (event) => {
currTime = window.performance.now()
if (addEventsCache) {
addEvents.push({ erase: event })
} else {
addEvents += 1
}
clearTimeout(timeout)
timeout = setTimeout(timeoutCallback, addEventGroupTimeout)
}
updateRoom.addEventListener(
"addOrUpdatePath",
addEventListener,
)
updateRoom.addEventListener(
"removedIntervalsChange",
eraseEventListener,
)
prevTime = window.performance.now()
addOnInitBackend(addPackets, BLOCKSIZE)
}),
)
.then(async () => {
updateRoom.removeEventListener(
"addOrUpdatePath",
addEventListener,
)
addEventListener = null
updateRoom.removeEventListener(
"removedIntervalsChange",
eraseEventListener,
)
eraseEventListener = null
addPackets = null
await dumpBSON(addEventsFilename, addEvents)
addEvents = null
addRemRAM = captureHeapUsage()
erasePackets = await loadBSON(erasePacketsFilename)
})
.then(
() =>
new Promise((resolve) => {
let broadcasts = 0
let currTime
const timeoutCallback = () => {
broadcasts += 1
eraseRemTime += (currTime - prevTime) * 1e3
prevTime = window.performance.now()
eraseOnEventGroup(
erasePackets,
ITERATIONS,
broadcasts,
resolve,
eraseEvents,
pathIDs,
addData,
eraseData,
)
}
eraseEventListener = (event) => {
currTime = window.performance.now()
if (eraseEventsCache) {
eraseEvents.push({ erase: event })
} else {
eraseEvents += 1
}
clearTimeout(timeout)
timeout = setTimeout(
timeoutCallback,
eraseEventGroupTimeout,
)
}
updateRoom.addEventListener(
"removedIntervalsChange",
eraseEventListener,
)
prevTime = window.performance.now()
eraseOnInitBackend(erasePackets, BLOCKSIZE)
}),
)
.then(async () => {
updateRoom.removeEventListener(
"removedIntervalsChange",
eraseEventListener,
)
eraseEventListener = null
erasePackets = null
await dumpBSON(eraseEventsFilename, eraseEvents)
eraseEvents = null
eraseRemRAM = captureHeapUsage()
updateRoom.disconnect()
updateRoom = null
disconnectUpdRAM = captureHeapUsage()
})
.then(
() =>
// eslint-disable-next-line no-async-promise-executor
new Promise(async (resolve) => {
userID.uuid = "a2108f84-3785-4696-8dd5-fb89b38d4f7f"
syncRoom = await connect("sync", CRDT, MockConnection)
getEventListener(
"sync",
"messageReceived",
)(createMessageReceivedEvent(handshake, "tw-ml"))
connectSyncRAM = captureHeapUsage()
syncPackets = await loadBSON(syncPacketsFilename)
return resolve()
}),
)
.then(
() =>
new Promise((resolve) => {
addEventListener = (event) => {
const currTime = window.performance.now()
syncRemTime = (currTime - prevTime) * 1e3
if (syncEventsCache) {
syncEvents.push({ add: event })
} else {
syncEvents += 1
if (syncEvents % eventsGC == 1) {
captureHeapUsage()
}
}
clearTimeout(timeout)
timeout = setTimeout(
() =>
syncOnEventGroup(
resolve,
syncEvents,
pathIDs,
addData,
eraseData,
),
syncEventGroupTimeout,
)
}
eraseEventListener = (event) => {
const currTime = window.performance.now()
syncRemTime = (currTime - prevTime) * 1e3
if (syncEventsCache) {
syncEvents.push({ erase: event })
} else {
syncEvents += 1
if (syncEvents % eventsGC == 1) {
captureHeapUsage()
}
}
clearTimeout(timeout)
timeout = setTimeout(
() =>
syncOnEventGroup(
resolve,
syncEvents,
pathIDs,
addData,
eraseData,
),
syncEventGroupTimeout,
)
}
syncRoom.addEventListener("addOrUpdatePath", addEventListener)
syncRoom.addEventListener(
"removedIntervalsChange",
eraseEventListener,
)
prevTime = window.performance.now()
for (const syncPacket of syncPackets) {
getEventListener(
"sync",
"messageReceived",
)(createMessageReceivedEvent(syncPacket))
}
syncPackets = null
captureHeapUsage()
}),
)
.then(async () => {
syncRoom.removeEventListener("addOrUpdatePath", addEventListener)
addEventListener = null
syncRoom.removeEventListener(
"removedIntervalsChange",
eraseEventListener,
)
eraseEventListener = null
syncPackets = null
await dumpBSON(syncEventsFilename, syncEvents)
syncEvents = null
syncRemRAM = captureHeapUsage()
syncRoom.disconnect()
syncRoom = null
disconnectSyncRAM = captureHeapUsage()
})
.then(async () => {
if (!BENCHMARK) {
return
}
addPackets = (await loadBSON(addPacketsFilename)).reduce(
(sum, packets) => sum + packets.length,
0,
)
erasePackets = (await loadBSON(erasePacketsFilename)).reduce(
(sum, packets) => sum + packets.length,
0,
)
syncPackets = (await loadBSON(syncPacketsFilename)).length
addEvents = await loadBSON(addEventsFilename)
addEvents = addEventsCache ? addEvents.length : addEvents
eraseEvents = await loadBSON(eraseEventsFilename)
eraseEvents = eraseEventsCache ? eraseEvents.length : eraseEvents
syncEvents = await loadBSON(syncEventsFilename)
syncEvents = syncEventsCache ? syncEvents.length : syncEvents
const results = {
addPath: {
timeLoc: addLocTime,
encodeRAM: addRAM,
packets: addPackets,
size: addSize,
timeRem: addRemTime,
decodeRAM: addRemRAM,
events: addEvents,
},
extendErasureIntervals: {
timeLoc: eraseLocTime,
encodeRAM: eraseRAM,
packets: erasePackets,
size: eraseSize,
timeRem: eraseRemTime,
decodeRAM: eraseRemRAM,
events: eraseEvents,
},
synchronisation: {
timeLoc: syncLocTime,
encodeRAM: syncRAM,
packets: syncPackets,
size: syncSize,
timeRem: syncRemTime,
decodeRAM: syncRemRAM,
events: syncEvents,
},
}
printBenchmark(BENCHMARK, ITERATIONS, results)
appendBenchmark(FILENAME, ITERATIONS, results)
})
)
}),
Promise.resolve(),
)
}
function addOnInitFrontendSequential(
room,
addPackets,
pathIDs,
addData,
ITERATIONS, // eslint-disable-line no-unused-vars
BLOCKSIZE, // eslint-disable-line no-unused-vars
) {
addPackets.push([])
const drawPathID = room.addPath(addData[0])
pathIDs.push(drawPathID)
for (let i = 1; i < addData.length; i++) {
room.extendPath(drawPathID, addData[i])
}
room.endPath(drawPathID)
}
function addOnBroadcastGroupSequential(
room,
addPackets,
pathIDs,
addData,
ITERATIONS,
broadcasts,
resolve,
) {
if (broadcasts < ITERATIONS) {
addPackets.push([])
const drawPathID = room.addPath(addData[0])
pathIDs.push(drawPathID)
for (let i = 1; i < addData.length; i++) {
room.extendPath(drawPathID, addData[i])
}
room.endPath(drawPathID)
} else {
resolve()
}
}
function eraseOnInitFrontendSequential(
room,
erasePackets,
pathIDs,
eraseData,
ITERATIONS, // eslint-disable-line no-unused-vars
BLOCKSIZE, // eslint-disable-line no-unused-vars
) {
erasePackets.push([])
const erasePathID = pathIDs[0]
for (let i = 0; i < eraseData.length; i++) {
room.extendErasureIntervals(erasePathID, eraseData[i][0], eraseData[i][1])
}
}
function eraseOnBroadcastGroupSequential(
room,
erasePackets,
pathIDs,
eraseData,
ITERATIONS,
broadcasts,
resolve,
) {
if (broadcasts < ITERATIONS) {
erasePackets.push([])
const erasePathID = pathIDs[broadcasts]
for (let i = 0; i < eraseData.length; i++) {
room.extendErasureIntervals(erasePathID, eraseData[i][0], eraseData[i][1])
}
} else {
resolve()
}
}
function syncOnSendGroup(syncPackets, resolve) {
resolve()
}
function addOnInitBackendSequential(
addPackets,
BLOCKSIZE, // eslint-disable-line no-unused-vars
) {
for (const packet of addPackets[0]) {
getEventListener(
"update",
"messageReceived",
)(createMessageReceivedEvent(packet))
}
}
function addOnEventGroupSequential(
addPackets,
ITERATIONS,
broadcasts,
resolve,
addEvents, // eslint-disable-line no-unused-vars
pathIDs, // eslint-disable-line no-unused-vars
addData, // eslint-disable-line no-unused-vars
) {
if (broadcasts >= ITERATIONS) {
return resolve()
}
for (const packet of addPackets[broadcasts]) {
getEventListener(
"update",
"messageReceived",
)(createMessageReceivedEvent(packet))
}
}
function eraseOnInitBackendSequential(
erasePackets,
BLOCKSIZE, // eslint-disable-line no-unused-vars
) {
for (const packet of erasePackets[0]) {
getEventListener(
"update",
"messageReceived",
)(createMessageReceivedEvent(packet))
}
}
function eraseOnEventGroupSequential(
erasePackets,
ITERATIONS,
broadcasts,
resolve,
eraseEvents, // eslint-disable-line no-unused-vars
pathIDs, // eslint-disable-line no-unused-vars
addData, // eslint-disable-line no-unused-vars
eraseData, // eslint-disable-line no-unused-vars
) {
if (broadcasts >= ITERATIONS) {
return resolve()
}
for (const packet of erasePackets[broadcasts]) {
getEventListener(
"update",
"messageReceived",
)(createMessageReceivedEvent(packet))
}
}
function syncOnEventGroup(
resolve,
syncEvents, // eslint-disable-line no-unused-vars
pathIDs, // eslint-disable-line no-unused-vars
addData, // eslint-disable-line no-unused-vars
eraseData, // eslint-disable-line no-unused-vars
) {
resolve()
}
function addOnInitFrontendParallel(
room,
addPackets,
pathIDs,
addData,
ITERATIONS,
BLOCKSIZE,
) {
addPackets.push([])
// Necessary to allow yjs to execute transactions (majority of processing time)
function addPath(sj) {
if (sj >= ITERATIONS) return
for (let j = sj; j < Math.min(sj + BLOCKSIZE, ITERATIONS); j++) {
const drawPathID = room.addPath(addData[0])
pathIDs.push(drawPathID)
for (let i = 1; i < addData.length; i++) {
room.extendPath(drawPathID, addData[i])
}
room.endPath(drawPathID)
}
captureHeapUsage()
setTimeout(addPath, 0, sj + BLOCKSIZE)
}
addPath(0)
}
function addOnBroadcastGroupParallel(
room,
addPackets,
pathIDs,
addData,
ITERATIONS,
broadcasts,
resolve,
) {
resolve()
}
function eraseOnInitFrontendParallel(
room,
erasePackets,
pathIDs,
eraseData,
ITERATIONS,
BLOCKSIZE,
) {
erasePackets.push([])
// Necessary to allow yjs to execute transactions (majority of processing time)
function erasePath(sj) {
if (sj >= ITERATIONS) return
for (let j = sj; j < Math.min(sj + BLOCKSIZE, ITERATIONS); j++) {
const erasePathID = pathIDs[j]
for (let i = 0; i < eraseData.length; i++) {
room.extendErasureIntervals(
erasePathID,
eraseData[i][0],
eraseData[i][1],
)
}
}
captureHeapUsage()
setTimeout(erasePath, 0, sj + BLOCKSIZE)
}
erasePath(0)
}
function eraseOnBroadcastGroupParallel(
room,
erasePackets,
pathIDs,
eraseData,
ITERATIONS,
broadcasts,
resolve,
) {
resolve()
}
function addOnInitBackendParallel(addPackets, BLOCKSIZE) {
const packets = addPackets[0]
function addPath(sj) {
if (sj >= packets.length) return
for (let j = sj; j < Math.min(sj + BLOCKSIZE, packets.length); j++) {
getEventListener(
"update",
"messageReceived",
)(createMessageReceivedEvent(packets[j]))
}
captureHeapUsage()
setTimeout(addPath, 0, sj + BLOCKSIZE)
}
addPath(0)
}
function addOnEventGroupParallel(
addPackets,
ITERATIONS,
broadcasts,
resolve,
addEvents, // eslint-disable-line no-unused-vars
pathIDs, // eslint-disable-line no-unused-vars
addData, // eslint-disable-line no-unused-vars
) {
resolve()
}
function eraseOnInitBackendParallel(erasePackets, BLOCKSIZE) {
const packets = erasePackets[0]
function erasePath(sj) {
if (sj >= packets.length) return
for (let j = sj; j < Math.min(sj + BLOCKSIZE, packets.length); j++) {
getEventListener(
"update",
"messageReceived",
)(createMessageReceivedEvent(packets[j]))
}
captureHeapUsage()
setTimeout(erasePath, 0, sj + BLOCKSIZE)
}
erasePath(0)
}
function eraseOnEventGroupParallel(
erasePackets,
ITERATIONS,
broadcasts,
resolve,
eraseEvents, // eslint-disable-line no-unused-vars
pathIDs, // eslint-disable-line no-unused-vars
addData, // eslint-disable-line no-unused-vars
eraseData, // eslint-disable-line no-unused-vars
) {
resolve()
}
function addOnEventGroupVerify(
addPackets,
ITERATIONS,
broadcasts,
resolve,
addEvents,
pathIDs,
addData,
) {
const updatePaths = {}
const updateIntervals = {}
for (const event of addEvents) {
equal(!event.add + !event.erase, 1)
if (event.add) {
const {
detail: { id, points },
} = event.add
updatePaths[id] = points
} else if (event.erase) {
const {
detail: { id, intervals },
} = event.erase
updateIntervals[id] = intervals
}
}
equal(updatePaths, { [pathIDs[0]]: addData })
equal(updateIntervals, { [pathIDs[0]]: {} })
resolve()
}
function eraseOnEventGroupVerify(
erasePackets,
ITERATIONS,
broadcasts,
resolve,
eraseEvents,
pathIDs,
addData,
eraseData, // eslint-disable-line no-unused-vars
) {
const updateIntervals = {}
for (const event of eraseEvents) {
ok(!event.add)
notOk(!event.erase)
const {
detail: { id, intervals },
} = event.erase
updateIntervals[id] = intervals
}
equal(updateIntervals, {
[pathIDs[0]]: Object.assign(
{},
new Array(addData.length).fill([[0, 0 + (addData.length > 1)]]),
),
})
resolve()
}
function syncOnEventGroupVerify(
resolve,
syncEvents,
pathIDs,
addData,
eraseData, // eslint-disable-line no-unused-vars
) {
const syncPaths = {}
const syncIntervals = {}
for (const event of syncEvents) {
equal(!event.add + !event.erase, 1)
if (event.add) {
const {
detail: { id, points },
} = event.add
syncPaths[id] = points
} else if (event.erase) {
const {
detail: { id, intervals },
} = event.erase
syncIntervals[id] = intervals
}
}
equal(syncPaths, { [pathIDs[0]]: addData })
equal(syncIntervals, {
[pathIDs[0]]: Object.assign(
{},
new Array(addData.length).fill([[0, 0 + (addData.length > 1)]]),
),
})
resolve()
}
test("benchmark", async (t) => {
await initFileSystem()
const ITERATIONSLIST = [10, 25, 50, 75, 100, 250, 500]
const BLOCKSIZE = blocksize
await t.test("communicates a dot draw and erase update", async (/*t*/) => {
return runBidirectionalBenchmark(
null /* BENCHMARK */,
null /* FILENAME */,
[1] /* ITERATIONSLIST */,
BLOCKSIZE /* BLOCKSIZE */,
dotDraw /* addData */,
dotErase /* eraseData */,
addOnInitFrontendSequential /* addOnInitFrontend */,
1000 /* addBroadcastGroupTimeout */,
addOnBroadcastGroupParallel /* addOnBroadcastGroup */,
".dot-ver-add-packets.json" /* addPacketsFilename */,
eraseOnInitFrontendSequential /* eraseOnInitFrontend */,
1000 /* eraseBroadcastGroupTimeout */,
eraseOnBroadcastGroupParallel /* eraseOnBroadcastGroup */,
".dot-ver-erase-packets.json" /* erasePacketsFilename */,
1000 /* syncSendGroupTimeout */,
syncOnSendGroup /* syncOnSendGroup */,
".dot-ver-sync-packets.json" /* syncPacketsFilename */,
addOnInitBackendSequential /* addOnInitBackend */,
1000 /* addEventGroupTimeout */,
addOnEventGroupVerify /* addOnEventGroup */,
true /* addEventsCache */,
".dot-ver-add-events.json" /* addEventsFilename */,
eraseOnInitBackendSequential /* eraseOnInitBackend */,
1000 /* eraseEventGroupTimeout */,
eraseOnEventGroupVerify /* eraseOnEventGroupTimeout */,
true /* eraseEventsCache */,
".dot-ver-erase-events.json" /* eraseEventsFilename */,
1000 /* syncEventGroupTimeout */,
syncOnEventGroupVerify /* syncOnEventGroup */,
true /* syncEventsCache */,
".dot-ver-sync-events.json" /* syncEventsFilename */,
)
})
await t.test(
"benchmarks a dot draw and erase update sequentially",
async (/*t*/) => {
return runBidirectionalBenchmark(
"dot draw and erase [sequential]" /* BENCHMARK */,
"plots/dot-seq-benchmark.tsv" /* FILENAME */,
ITERATIONSLIST /* ITERATIONSLIST */,
BLOCKSIZE /* BLOCKSIZE */,
dotDraw /* addData */,
dotErase /* eraseData */,
addOnInitFrontendSequential /* addOnInitFrontend */,
100 /* addBroadcastGroupTimeout */,
addOnBroadcastGroupSequential /* addOnBroadcastGroup */,
".dot-seq-add-packets.json" /* addPacketsFilename */,
eraseOnInitFrontendSequential /* eraseOnInitFrontend */,
100 /* eraseBroadcastGroupTimeout */,
eraseOnBroadcastGroupSequential /* eraseOnBroadcastGroup */,
".dot-seq-erase-packets.json" /* erasePacketsFilename */,
1000 /* syncSendGroupTimeout */,
syncOnSendGroup /* syncOnSendGroup */,
".dot-seq-sync-packets.json" /* syncPacketsFilename */,
addOnInitBackendSequential /* addOnInitBackend */,
100 /* addEventGroupTimeout */,
addOnEventGroupSequential /* addOnEventGroup */,
false /* addEventsCache */,
".dot-seq-add-events.json" /* addEventsFilename */,
eraseOnInitBackendSequential /* eraseOnInitBackend */,
100 /* eraseEventGroupTimeout */,
eraseOnEventGroupSequential /* eraseOnEventGroupTimeout */,
false /* eraseEventsCache */,
".dot-seq-erase-events.json" /* eraseEventsFilename */,
1000 /* syncEventGroupTimeout */,
syncOnEventGroup /* syncOnEventGroup */,
false /* syncEventsCache */,
".dot-seq-sync-events.json" /* syncEventsFilename */,
)
},
)
await t.test(
"benchmarks a dot draw and erase update in parallel",
async (/*t*/) => {
return runBidirectionalBenchmark(
"dot draw and erase [parallel]" /* BENCHMARK */,
"plots/dot-par-benchmark.tsv" /* FILENAME */,
ITERATIONSLIST /* ITERATIONSLIST */,
BLOCKSIZE /* BLOCKSIZE */,
dotDraw /* addData */,
dotErase /* eraseData */,
addOnInitFrontendParallel /* addOnInitFrontend */,
1000 /* addBroadcastGroupTimeout */,
addOnBroadcastGroupParallel /* addOnBroadcastGroup */,
".dot-par-add-packets.json" /* addPacketsFilename */,
eraseOnInitFrontendParallel /* eraseOnInitFrontend */,
1000 /* eraseBroadcastGroupTimeout */,
eraseOnBroadcastGroupParallel /* eraseOnBroadcastGroup */,
".dot-par-erase-packets.json" /* erasePacketsFilename */,
1000 /* syncSendGroupTimeout */,
syncOnSendGroup /* syncOnSendGroup */,
".dot-par-sync-packets.json" /* syncPacketsFilename */,
addOnInitBackendParallel /* addOnInitBackend */,
1000 /* addEventGroupTimeout */,
addOnEventGroupParallel /* addOnEventGroup */,
false /* addEventsCache */,
".dot-par-add-events.json" /* addEventsFilename */,
eraseOnInitBackendParallel /* eraseOnInitBackend */,
1000 /* eraseEventGroupTimeout */,
eraseOnEventGroupParallel /* eraseOnEventGroupTimeout */,
false /* eraseEventsCache */,
".dot-par-erase-events.json" /* eraseEventsFilename */,
1000 /* syncEventGroupTimeout */,
syncOnEventGroup /* syncOnEventGroup */,
false /* syncEventsCache */,
".dot-par-sync-events.json" /* syncEventsFilename */,
)
},
)
await t.test("communicates a path draw and erase update", async (/*t*/) => {
return runBidirectionalBenchmark(
null /* BENCHMARK */,
null /* FILENAME */,
[1] /* ITERATIONSLIST */,
BLOCKSIZE /* BLOCKSIZE */,
pathDraw /* addData */,
pathErase /* eraseData */,
addOnInitFrontendSequential /* addOnInitFrontend */,
1000 /* addBroadcastGroupTimeout */,
addOnBroadcastGroupParallel /* addOnBroadcastGroup */,
".path-ver-add-packets.json" /* addPacketsFilename */,
eraseOnInitFrontendSequential /* eraseOnInitFrontend */,
1000 /* eraseBroadcastGroupTimeout */,
eraseOnBroadcastGroupParallel /* eraseOnBroadcastGroup */,
".path-ver-erase-packets.json" /* erasePacketsFilename */,
1000 /* syncSendGroupTimeout */,
syncOnSendGroup /* syncOnSendGroup */,
".path-ver-sync-packets.json" /* syncPacketsFilename */,
addOnInitBackendSequential /* addOnInitBackend */,
1000 /* addEventGroupTimeout */,
addOnEventGroupVerify /* addOnEventGroup */,
true /* addEventsCache */,
".path-ver-add-events.json" /* addEventsFilename */,
eraseOnInitBackendSequential /* eraseOnInitBackend */,
1000 /* eraseEventGroupTimeout */,
eraseOnEventGroupVerify /* eraseOnEventGroupTimeout */,
true /* eraseEventsCache */,
".path-ver-erase-events.json" /* eraseEventsFilename */,
1000 /* syncEventGroupTimeout */,
syncOnEventGroupVerify /* syncOnEventGroup */,
true /* syncEventsCache */,
".path-ver-sync-events.json" /* syncEventsFilename */,
)
})
await t.test(
"benchmarks a path draw and erase update sequentially",
async (/*t*/) => {
return runBidirectionalBenchmark(
"path draw and erase [sequential]" /* BENCHMARK */,
"plots/path-seq-benchmark.tsv" /* FILENAME */,
ITERATIONSLIST /* ITERATIONSLIST */,
BLOCKSIZE /* BLOCKSIZE */,
pathDraw /* addData */,
pathErase /* eraseData */,
addOnInitFrontendSequential /* addOnInitFrontend */,
100 /* addBroadcastGroupTimeout */,
addOnBroadcastGroupSequential /* addOnBroadcastGroup */,
".path-seq-add-packets.json" /* addPacketsFilename */,
eraseOnInitFrontendSequential /* eraseOnInitFrontend */,
100 /* eraseBroadcastGroupTimeout */,
eraseOnBroadcastGroupSequential /* eraseOnBroadcastGroup */,
".path-seq-erase-packets.json" /* erasePacketsFilename */,
1000 /* syncSendGroupTimeout */,
syncOnSendGroup /* syncOnSendGroup */,
".path-seq-sync-packets.json" /* syncPacketsFilename */,
addOnInitBackendSequential /* addOnInitBackend */,
100 /* addEventGroupTimeout */,
addOnEventGroupSequential /* addOnEventGroup */,
false /* addEventsCache */,
".path-seq-add-events.json" /* addEventsFilename */,
eraseOnInitBackendSequential /* eraseOnInitBackend */,
100 /* eraseEventGroupTimeout */,
eraseOnEventGroupSequential /* eraseOnEventGroupTimeout */,
false /* eraseEventsCache */,
".path-seq-erase-events.json" /* eraseEventsFilename */,
1000 /* syncEventGroupTimeout */,
syncOnEventGroup /* syncOnEventGroup */,
false /* syncEventsCache */,
".path-seq-sync-events.json" /* syncEventsFilename */,
)
},
)
await t.test(
"benchmarks a path draw and erase update in parallel",
async (/*t*/) => {
return runBidirectionalBenchmark(
"path draw and erase [parallel]" /* BENCHMARK */,
"plots/path-par-benchmark.tsv" /* FILENAME */,
ITERATIONSLIST /* ITERATIONSLIST */,
BLOCKSIZE /* BLOCKSIZE */,
pathDraw /* addData */,
pathErase /* eraseData */,
addOnInitFrontendParallel /* addOnInitFrontend */,
5000 /* addBroadcastGroupTimeout */,
addOnBroadcastGroupParallel /* addOnBroadcastGroup */,
".path-par-add-packets.json" /* addPacketsFilename */,
eraseOnInitFrontendParallel /* eraseOnInitFrontend */,
5000 /* eraseBroadcastGroupTimeout */,
eraseOnBroadcastGroupParallel /* eraseOnBroadcastGroup */,
".path-par-erase-packets.json" /* erasePacketsFilename */,
5000 /* syncSendGroupTimeout */,
syncOnSendGroup /* syncOnSendGroup */,
".path-par-sync-packets.json" /* syncPacketsFilename */,
addOnInitBackendParallel /* addOnInitBackend */,
5000 /* addEventGroupTimeout */,
addOnEventGroupParallel /* addOnEventGroup */,
false /* addEventsCache */,
".path-par-add-events.json" /* addEventsFilename */,
eraseOnInitBackendParallel /* eraseOnInitBackend */,
5000 /* eraseEventGroupTimeout */,
eraseOnEventGroupParallel /* eraseOnEventGroupTimeout */,
false /* eraseEventsCache */,
".path-par-erase-events.json" /* eraseEventsFilename */,
5000 /* syncEventGroupTimeout */,
syncOnEventGroup /* syncOnEventGroup */,
false /* syncEventsCache */,
".path-par-sync-events.json" /* syncEventsFilename */,
)
},
)
})
export const handshake = Uint8Array.of(
133,
164,
117,
117,
105,
100,
217,
36,
97,
54,
100,
57,
50,
55,
97,
56,
45,
53,
50,
57,
101,
45,
52,
55,
50,
54,
45,
97,
102,
48,
98,
45,
56,
48,
57,
55,
98,
48,
48,
102,
55,
99,
49,
55,
167,
109,
101,
115,
115,
97,
103,
101,
196,
3,
162,
109,
108,
165,
115,
108,
105,
99,
101,
0,
166,
108,
101,
110,
103,
116,
104,
3,
170,
99,
111,
109,
112,
114,
101,
115,
115,
101,
100,
194,
)
export const dotDraw = [[209, 88, 5.0, "#0000ff"]]
export const dotErase = [[0, [[0, 0]]]]
export const pathDraw = [
[229, 147, 5.0, "#0000ff"],
[239, 149, 5.0, "#0000ff"],
[265, 154, 5.0, "#0000ff"],
[329, 158, 5.0, "#0000ff"],
[428, 168, 5.0, "#0000ff"],
[559, 172, 5.0, "#0000ff"],
[689, 176, 5.0, "#0000ff"],
[789, 176, 5.0, "#0000ff"],
[871, 178, 5.0, "#0000ff"],
[915, 179, 5.0, "#0000ff"],
[937, 179, 5.0, "#0000ff"],
[942, 179, 5.0, "#0000ff"],
]
export const pathErase = [
[0, [[0, 0.030367582231477598]]],
[0, [[0, 0.21377102974828613]]],
[0, [[0, 0.2537463508623564]]],
[0, [[0, 0.34615384615384587]]],
[0, [[0, 0.4383613354449363]]],
[0, [[0, 0.5303675822314777]]],
[0, [[0, 0.6221712759429692]]],
[0, [[0, 0.7137710297482863]]],
[0, [[0, 0.8051653782004998]]],
[0, [[0, 0.9383613354449363]]],
[0, [[0, 1]]],
[1, [[0, 0.011716311379342382]]],
[1, [[0, 0.11776520948773353]]],
[1, [[0, 0.24006055181058228]]],
[1, [[0, 0.3107159148890084]]],
[1, [[0, 0.3810746303975673]]],
[1, [[0, 0.4329788671985464]]],
[1, [[0, 0.46835801309405006]]],
[1, [[0, 0.5036636541863274]]],
[1, [[0, 0.5740522382479196]]],
[1, [[0, 0.6441399664465288]]],
[1, [[0, 0.731854882371686]]],
[1, [[0, 0.8021237085400632]]],
[1, [[0, 0.8371448744989799]]],
[1, [[0, 0.88955025220044]]],
[1, [[0, 0.9599984343180302]]],
[1, [[0, 1]]],
[2, [[0, 0.028177027652500065]]],
[2, [[0, 0.03543420592413073]]],
[2, [[0, 0.06582991759489924]]],
[2, [[0, 0.10275381734879]]],
[2, [[0, 0.11799692411252251]]],
[2, [[0, 0.13323648198760243]]],
[2, [[0, 0.16370490408198182]]],
[2, [[0, 0.2154080009026301]]],
[2, [[0, 0.24595364724469834]]],
[2, [[0, 0.27648544742767633]]],
[2, [[0, 0.31247481764398494]]],
[2, [[0, 0.3478072470007028]]],
[2, [[0, 0.3785134252130592]]],
[2, [[0, 0.3938616312677431]]],
[2, [[0, 0.42869266182685783]]],
[2, [[0, 0.44408271983476183]]],
[2, [[0, 0.4594695941376009]]],
[2, [[0, 0.4902337694168022]]],
[2, [[0, 0.505611059005356]]],
[2, [[0, 0.5246181146997148]]],
[2, [[0, 0.5400397300348875]]],
[2, [[0, 0.6047746406708809]]],
[2, [[0, 0.6202304708442001]]],
[2, [[0, 0.6356832087546725]]],
[2, [[0, 0.6535687967630837]]],
[2, [[0, 0.7000469301249926]]],
[2, [[0, 0.7155335204468358]]],
[2, [[0, 0.7484433368793898]]],
[2, [[0, 0.8415551828545537]]],
[2, [[0, 0.8896745156013701]]],
[2, [[0, 0.9207665061568425]]],
[2, [[0, 0.9518463520584276]]],
[2, [[0, 0.9840324922550672]]],
[2, [[0, 1]]],
[3, [[0, 0.01983903263791853]]],
[3, [[0, 0.029876622283678104]]],
[3, [[0, 0.03990912701215377]]],
[3, [[0, 0.0806437862168895]]],
[3, [[0, 0.09070123866218649]]],
[3, [[0, 0.10101007524615979]]],
[3, [[0, 0.11110799523965204]]],
[3, [[0, 0.12120077057322706]]],
[3, [[0, 0.13128840847448436]]],
[3, [[0, 0.15144829885129574]]],
[3, [[0, 0.16152056375068494]]],
[3, [[0, 0.1818098260284825]]],
[3, [[0, 0.20202009896967427]]],
[3, [[0, 0.21211750416023725]]],
[3, [[0, 0.2727084630640253]]],
[3, [[0, 0.29292802997685985]]],
[3, [[0, 0.3030300711783685]]],
[3, [[0, 0.33330532017010783]]],
[3, [[0, 0.3636029076253441]]],
[3, [[0, 0.37371993253130364]]],
[3, [[0, 0.3838317824361028]]],
[3, [[0, 0.40403999188002426]]],
[3, [[0, 0.41413636769223433]]],
[3, [[0, 0.42422760105585006]]],
[3, [[0, 0.43431369907567324]]],
[3, [[0, 0.4443662699271226]]],
[3, [[0, 0.4544931529993546]]],
[3, [[0, 0.46461484324950236]]],
[3, [[0, 0.4747313502489194]]],
[3, [[0, 0.5050498610823838]]],
[3, [[0, 0.5151457223183467]]],
[3, [[0, 0.5353220266951074]]],
[3, [[0, 0.5454024835564785]]],
[3, [[0, 0.5858535321955943]]],
[3, [[0, 0.5959591836309699]]],
[3, [[0, 0.616155025527601]]],
[3, [[0, 0.6463920624411829]]],
[3, [[0, 0.6666385590106888]]],
[3, [[0, 0.6767540304709369]]],
[3, [[0, 0.6969694650467834]]],
[3, [[0, 0.7474179601261248]]],
[3, [[0, 0.7675614683096057]]],
[3, [[0, 0.7776255556078067]]],
[3, [[0, 0.8282626561208872]]],
[3, [[0, 0.8484256216002729]]],
[3, [[0, 0.8584994213492495]]],
[3, [[0, 0.8685681068775302]]],
[3, [[0, 0.9087918074364576]]],
[3, [[0, 0.9188349938933553]]],
[3, [[0, 0.9288730921857218]]],
[3, [[0, 0.9889949697600137]]],
[3, [[0, 0.99974858647489]]],
[3, [[0, 1]]],
[4, [[0, 0.015052512188419602]]],
[4, [[0, 0.03029426962088413]]],
[4, [[0, 0.06839243749770468]]],
[4, [[0, 0.0836292133608997]]],
[4, [[0, 0.0912470672285808]]],
[4, [[0, 0.09886456497394153]]],
[4, [[0, 0.13734696870310906]]],
[4, [[0, 0.14497397901135212]]],
[4, [[0, 0.16022693354357556]]],
[4, [[0, 0.16785287776755597]]],
[4, [[0, 0.2061009857691912]]],
[4, [[0, 0.2213715432205954]]],
[4, [[0, 0.2290062878232259]]],
[4, [[0, 0.23664067642184725]]],
[4, [[0, 0.31287013784360607]]],
[4, [[0, 0.33579551026422]]],
[4, [[0, 0.3434365859114192]]],
[4, [[0, 0.37399731542592607]]],
[4, [[0, 0.3892755376794567]]],
[4, [[0, 0.3969141134976991]]],
[4, [[0, 0.41219019494203474]]],
[4, [[0, 0.47328026274975304]]],
[4, [[0, 0.48854921795242295]]],
[4, [[0, 0.5267153779648435]]],
[4, [[0, 0.5343475426943275]]],
[4, [[0, 0.5419793517647892]]],
[4, [[0, 0.5496108052135158]]],
[4, [[0, 0.5801330634496403]]],
[4, [[0, 0.5877627392618375]]],
[4, [[0, 0.5953920596237392]]],
[4, [[0, 0.610649634073643]]],
[4, [[0, 0.6487873509046499]]],
[4, [[0, 0.6564138281707615]]],
[4, [[0, 0.6640399500759696]]],
[4, [[0, 0.7174128527857359]]],
[4, [[0, 0.7326590547531385]]],
[4, [[0, 0.7402816225030254]]],
[4, [[0, 0.7555256913985305]]],
[4, [[0, 0.8469602239274668]]],
[4, [[0, 0.8621943289183245]]],
[4, [[0, 0.8698108469953234]]],
[4, [[0, 0.8774270087010998]]],
[4, [[0, 0.9078880903271799]]],
[4, [[0, 0.9155024690411608]]],
[4, [[0, 0.9231164909119645]]],
[4, [[0, 0.9307301558520772]]],
[4, [[0, 0.9769418956440951]]],
[4, [[0, 0.9845647300195252]]],
[4, [[0, 0.992187208890867]]],
[4, [[0, 0.9998093322301707]]],
[4, [[0, 1]]],
[5, [[0, 0.03820574873127833]]],
[5, [[0, 0.045884233047770166]]],
[5, [[0, 0.061240109456200745]]],
[5, [[0, 0.1226490440345489]]],
[5, [[0, 0.13032352057732857]]],
[5, [[0, 0.14567137935938093]]],
[5, [[0, 0.15334476144043718]]],
[5, [[0, 0.19170619534531316]]],
[5, [[0, 0.19937738618962128]]],
[5, [[0, 0.2070482114971963]]],
[5, [[0, 0.2453968513383652]]],
[5, [[0, 0.26073374476033434]]],
[5, [[0, 0.26840164189649224]]],
[5, [[0, 0.3067356268350702]]],
[5, [[0, 0.322066651341349]]],
[5, [[0, 0.32973161244332394]]],
[5, [[0, 0.34506043162274563]]],
[5, [[0, 0.39869970096661816]]],
[5, [[0, 0.4063609788597506]]],
[5, [[0, 0.41402188753199426]]],
[5, [[0, 0.4676378950251564]]],
[5, [[0, 0.47529584340574377]]],
[5, [[0, 0.5518548940788806]]],
[5, [[0, 0.5595087511478618]]],
[5, [[0, 0.56716223496407]]],
[5, [[0, 0.5824680819078778]]],
[5, [[0, 0.6686904305673842]]],
[5, [[0, 0.6917061953453132]]],
[5, [[0, 0.7070482114971963]]],
[5, [[0, 0.7766873397479431]]],
[5, [[0, 0.7923021861323849]]],
[5, [[0, 0.7999923098661218]]],
[5, [[0, 0.8076820697480561]]],
[5, [[0, 0.8614794103478556]]],
[5, [[0, 0.8768764254973112]]],
[5, [[0, 0.8845743850827851]]],
[5, [[0, 0.8922719794659787]]],
[5, [[0, 0.9458689803376649]]],
[5, [[0, 0.9612817605421127]]],
[5, [[0, 0.9689875986837584]]],
[5, [[0, 1]]],
[6, [[0, 0.029749843554381775]]],
[6, [[0, 0.059749843554381774]]],
[6, [[0, 0.06974984355438178]]],
[6, [[0, 0.13974984355438178]]],
[6, [[0, 0.14974984355438178]]],
[6, [[0, 0.15974984355438177]]],
[6, [[0, 0.16974984355438177]]],
[6, [[0, 0.17974984355438178]]],
[6, [[0, 0.21974984355438176]]],
[6, [[0, 0.22974984355438177]]],
[6, [[0, 0.23974984355438178]]],
[6, [[0, 0.2497498435543818]]],
[6, [[0, 0.3197498435543818]]],
[6, [[0, 0.3397498435543818]]],
[6, [[0, 0.3497498435543818]]],
[6, [[0, 0.35974984355438183]]],
[6, [[0, 0.468997487421324]]],
[6, [[0, 0.48899748742132404]]],
[6, [[0, 0.49899748742132405]]],
[6, [[0, 0.518997487421324]]],
[6, [[0, 0.6277371993328519]]],
[6, [[0, 0.6377371993328519]]],
[6, [[0, 0.6477371993328519]]],
[6, [[0, 0.6677371993328519]]],
[6, [[0, 0.7777371993328519]]],
[6, [[0, 0.7877371993328519]]],
[6, [[0, 0.8077371993328519]]],
[6, [[0, 0.9377371993328519]]],
[6, [[0, 0.957737199332852]]],
[6, [[0, 0.977737199332852]]],
[6, [[0, 1]]],
[7, [[0, 0.08434122461529223]]],
[7, [[0, 0.09736319249220814]]],
[7, [[0, 0.12177670236609432]]],
[7, [[0, 0.13398291047706393]]],
[7, [[0, 0.2560176149231152]]],
[7, [[0, 0.26820494288749264]]],
[7, [[0, 0.2803919084377965]]],
[7, [[0, 0.30476475227840155]]],
[7, [[0, 0.42434250381943905]]],
[7, [[0, 0.4364947880419225]]],
[7, [[0, 0.44864670195280104]]],
[7, [[0, 0.4607982453870106]]],
[7, [[0, 0.5822932659066377]]],
[7, [[0, 0.5944407226857343]]],
[7, [[0, 0.6065878068658583]]],
[7, [[0, 0.7298557216513387]]],
[7, [[0, 0.7420139098657976]]],
[7, [[0, 0.7541717301678039]]],
[7, [[0, 0.7663291824206042]]],
[7, [[0, 0.8284299052782537]]],
[7, [[0, 0.8406001691299525]]],
[7, [[0, 0.8649396036740605]]],
[7, [[0, 0.9753340606845997]]],
[7, [[0, 0.9875148632580261]]],
[7, [[0, 0.9996953030971739]]],
[7, [[0, 1]]],
[8, [[0, 0.022133273001338587]]],
[8, [[0, 0.20418547065439502]]],
[8, [[0, 0.22693303640624127]]],
[8, [[0, 0.24968001232099726]]],
[8, [[0, 0.272426398521291]]],
[8, [[0, 0.47595107564942674]]],
[8, [[0, 0.5214835194176608]]],
[8, [[0, 0.5442488489860375]]],
[8, [[0, 0.5670135839399206]]],
[8, [[0, 0.5897777244732638]]],
[8, [[0, 0.749110080215568]]],
[8, [[0, 0.7718694720767475]]],
[8, [[0, 0.7946282711542776]]],
[8, [[0, 0.8173864776180217]]],
[8, [[0, 1]]],
[9, [[0, 0.04431747070173535]]],
[9, [[0, 0.0897720161562808]]],
[9, [[0, 0.18068110706537172]]],
[9, [[0, 0.4988629252471899]]],
[9, [[0, 0.5897720161562808]]],
[9, [[0, 0.6352265616108262]]],
[9, [[0, 0.7261356525199172]]],
[9, [[0, 1]]],
[10, [[0, 1]]],
[11, [[0, 1]]],
]
import puppeteer from "puppeteer-core"
import fs from "fs"
;(async () => {
const browser = await puppeteer.launch({
executablePath: "google-chrome",
headless: true,
args: [
"--js-flags=--expose-gc",
"--no-sandbox",
"--enable-precise-memory-info",
],
})
const page = await browser.newPage()
const done = new Promise((resolve) => {
page.on("console", (msg) => {
if (msg.type() == "debug") {
process.stderr.write(msg.text())
} else if (msg.type() == "info") {
const { filename, title, iterations, results } = JSON.parse(msg.text())
if (title) {
const columns = ["iterations"]
for (const title of results) {
columns.push(`${title}_timeLoc`)
columns.push(`${title}_encodeRAM`)
columns.push(`${title}_packets`)
columns.push(`${title}_size`)
columns.push(`${title}_timeRem`)
columns.push(`${title}_decodeRAM`)
columns.push(`${title}_events`)
}
fs.writeFileSync(
filename,
`# Benchmark: ${title}\n# ${columns.join("\t")}\n`,
)
} else {
const columns = [iterations]
for (const title in results) {
const {
timeLoc,
encodeRAM,
packets,
size,
timeRem,
decodeRAM,
events,
} = results[title]
columns.push(timeLoc)
columns.push(encodeRAM)
columns.push(packets)
columns.push(size)
columns.push(timeRem)
columns.push(decodeRAM)
columns.push(events)
}
fs.appendFileSync(filename, `${columns.join("\t")}\n`)
}
} else if (msg.type() == "log") {
if (msg.text().startsWith("# failure")) {
resolve()
}
process.stdout.write(msg.text() + "\n")
}
})
})
await page.goto("http://localhost:3000/benchmarks.html").catch(console.error)
await done
await browser.close()
})()
import { Selector } from "testcafe"
const host = "127.0.0.1"
const port = process.env.PORT || 3000
fixture`Peer 1`.page`${host}:${port}`
const idAppearTimeout = 1000
const syncTimeout = 5000
const selectorOptions = { timeout: 1000 }
test("Connection id appears", async (t) => {
const userId = async () =>
await Selector("#user-avatar")
.child(1)
.addCustomDOMProperties({ innerHTML: (el) => el.innerHTML })
.with(selectorOptions).innerHTML
await t
.wait(idAppearTimeout)
.expect((await userId()).length)
.gt(0)
})
test("Clicking and dragging on canvas creates a single child element", async (t) => {
const canvas = Selector("#canvas").with(selectorOptions)
await t
.drag(canvas, 10, 10)
.wait(syncTimeout)
.expect(canvas.childElementCount)
// first draw also creates last recognised path placeholder
.eql(2)
})
import { Selector } from "testcafe"
const host = "127.0.0.1"
const port = 3000
fixture`Peer 2`.page`${host}:${port}`
const idAppearTimeout = 1000
const syncTimeout = 15000
const selectorOptions = { timeout: 1000 }
test("Connection id appears", async (t) => {
const userId = async () =>
await Selector("#user-avatar")
.child(1)
.addCustomDOMProperties({ innerHTML: (el) => el.innerHTML })
.with(selectorOptions).innerHTML
await t
.wait(idAppearTimeout)
.expect((await userId()).length)
.gt(0)
})
test("Canvas eventually gets a child element", async (t) => {
const canvas = Selector("#canvas").with(selectorOptions)
await t.expect(canvas.childElementCount).eql(1, { timeout: syncTimeout })
})
import {
computeErasureIntervals,
combineErasureIntervals,
spreadErasureIntervals,
flattenErasureIntervals,
} from "../src/erasure.js"
describe("erasure intervals", () => {
it("computes simple erasure intervals", () => {
const points = [
[0, 0],
[100, 100],
]
const erasureCenter = [50, 50]
const erasureRadius = 25 * Math.SQRT2
const erasureIntervals = computeErasureIntervals(
points,
erasureCenter,
erasureRadius,
)
expect(erasureIntervals).toStrictEqual({ 0: [[0.25, 0.75]] })
})
it("computes complex erasure intervals", () => {
const points = [
[0, 0],
[100, 100],
[0, 200],
]
const erasureCenter = [100, 100]
const erasureRadius = 25 * Math.SQRT2
const erasureIntervals = computeErasureIntervals(
points,
erasureCenter,
erasureRadius,
)
expect(erasureIntervals).toStrictEqual({ 0: [[0.75, 1]], 1: [[0, 0.25]] })
})
it("computes erasure intervals when point projection is not on the segment", () => {
const points = [
[800, 400],
[800, 450],
[800, 500],
]
const erasureCenter = [800, 432]
const erasureRadius = 20 //* Math.SQRT2
const erasureIntervals = computeErasureIntervals(
points,
erasureCenter,
erasureRadius,
)
expect(erasureIntervals).toStrictEqual({ 0: [[0.24, 1]], 1: [[0, 0.04]] })
})
it("computes erasure intervals ???", () => {
const points = [
[100, 100],
[1100, 100],
]
const erasureCenter = [448, 86]
const erasureRadius = 100
const erasureIntervals = computeErasureIntervals(
points,
erasureCenter,
erasureRadius,
)
expect(erasureIntervals).toStrictEqual({
0: [[0.2489848496441075, 0.4470151503558925]],
})
})
it("combines distinct intervals", () => {
const i1 = { 0: [[0.1, 0.6]] }
const i2 = { 0: [[0.7, 0.8]] }
const combined = combineErasureIntervals(i1, i2)
const expected = {
0: [
[0.1, 0.6],
[0.7, 0.8],
],
}
expect(combined).toStrictEqual(expected)
})
it("combines overlapping intervals", () => {
const i1 = { 0: [[0.1, 0.6]] }
const i2 = { 0: [[0.5, 0.8]] }
const combined = combineErasureIntervals(i1, i2)
const expected = { 0: [[0.1, 0.8]] }
expect(combined).toStrictEqual(expected)
})
it("combines overlapping inside intervals", () => {
const i1 = { 0: [[0.1, 0.6]] }
const i2 = { 0: [[0.2, 0.3]] }
const combined = combineErasureIntervals(i1, i2)
const expected = { 0: [[0.1, 0.6]] }
expect(combined).toStrictEqual(expected)
})
it("spreads flattened intervals", () => {
const il = [
[0.1, 1.25],
[1.5, 2.0],
[7.5, 7.75],
]
const spread = spreadErasureIntervals(il)
const expected = {
0: [[0.1, 1.0]],
1: [
[0.0, 0.25],
[0.5, 1.0],
],
7: [[0.5, 0.75]],
}
expect(spread).toStrictEqual(expected)
})
it("spreads singulatity intervals", () => {
const il = [
[0.0, 0.0],
[3.5, 3.5],
[99.0, 99.0],
]
const spread = spreadErasureIntervals(il)
const expected = { 0: [[0.0, 0.0]], 3: [[0.5, 0.5]], 99: [[0.0, 0.0]] }
expect(spread).toStrictEqual(expected)
})
it("flattens spread intervals", () => {
const is = {
0: [[0.1, 1.0]],
1: [
[0.0, 0.3],
[0.4, 1.0],
],
7: [[0.35, 0.75]],
}
const flattened = flattenErasureIntervals(is)
const expected = [
[0.1, 1.0],
[1.0, 1.3],
[1.4, 2.0],
[7.35, 7.75],
]
expect(flattened).toStrictEqual(expected)
})
})
const sum = (a, b) => a + b
describe("number adding", () => {
describe("number summation", () => {
it("adds 1 + 2 to equal 3", () => {
expect(sum(1, 2)).toBe(3)
})
})
})
describe("server", () => {
it("should work", () => {})
})
import recognizeFromPoints, { Shapes } from "../src/shapes"
describe("shape recognition", () => {
describe("general", () => {
test("should return a shape description object", () => {
const points = [[0, 0]]
const result = recognizeFromPoints(points)
expect(result).not.toBeNull()
})
})
describe("lines", () => {
test("should recognize a simple horizontal line", () => {
const points = [
[0, 0],
[100, 0],
]
const result = recognizeFromPoints(points)
expect(result.shape).toBe(Shapes.line)
expect(result.firstPoint).toStrictEqual([0, 0])
expect(result.lastPoint).toStrictEqual([100, 0])
})
test("should recognize a simple vertical line", () => {
const points = [
[0, 50],
[0, -100],
]
const result = recognizeFromPoints(points)
expect(result.shape).toBe(Shapes.line)
expect(result.firstPoint).toStrictEqual([0, 50])
expect(result.lastPoint).toStrictEqual([0, -100])
})
test("should recognize a slightly curve horizontal line", () => {
const points = [
[0, 0],
[30, 5],
[100, 0],
]
const result = recognizeFromPoints(points)
expect(result.shape).toBe(Shapes.line)
})
test("should not recognize a heavily curved horizontal line", () => {
const points = [
[0, 0],
[30, 30],
[100, -4],
]
const result = recognizeFromPoints(points)
expect(result.shape).not.toBe(Shapes.line)
})
test("should recognize a long horizontal line", () => {
const points = [
[48.675496688741724, 197.8062913907285],
[50.66225165562914, 197.8062913907285],
[53.64238410596026, 197.8062913907285],
[57.6158940397351, 197.8062913907285],
[61.58940397350993, 197.8062913907285],
[66.55629139072848, 197.8062913907285],
[71.52317880794702, 197.8062913907285],
[76.49006622516558, 197.8062913907285],
[77.48344370860927, 197.8062913907285],
[82.45033112582782, 197.8062913907285],
[84.43708609271523, 197.8062913907285],
[87.41721854304636, 197.8062913907285],
[88.41059602649007, 197.8062913907285],
[88.41059602649007, 197.8062913907285],
[89.40397350993378, 197.8062913907285],
]
const result = recognizeFromPoints(points)
expect(result.shape).toBe(Shapes.line)
})
test("should recognize a long vertical line", () => {
const points = [
[218.54304635761588, 84.56125827814569],
[218.54304635761588, 86.54801324503312],
[218.54304635761588, 87.54139072847681],
[218.54304635761588, 88.53476821192052],
[218.54304635761588, 90.52152317880794],
[218.54304635761588, 91.51490066225165],
[218.54304635761588, 92.50827814569537],
[218.54304635761588, 93.50165562913908],
[218.54304635761588, 94.49503311258277],
[218.54304635761588, 94.49503311258277],
[218.54304635761588, 95.4884105960265],
[218.54304635761588, 96.4817880794702],
[218.54304635761588, 97.4751655629139],
[218.54304635761588, 98.46854304635761],
[218.54304635761588, 100.45529801324503],
[218.54304635761588, 101.44867549668874],
[218.54304635761588, 103.43543046357617],
[218.54304635761588, 105.42218543046357],
[218.54304635761588, 108.4023178807947],
[218.54304635761588, 110.38907284768212],
[218.54304635761588, 112.37582781456953],
[218.54304635761588, 114.36258278145695],
[218.54304635761588, 116.34933774834438],
[218.54304635761588, 118.33609271523179],
[218.54304635761588, 119.32947019867551],
[218.54304635761588, 120.3228476821192],
[218.54304635761588, 122.30960264900662],
[218.54304635761588, 123.30298013245033],
[218.54304635761588, 124.29635761589404],
[218.54304635761588, 125.28973509933775],
[218.54304635761588, 125.28973509933775],
[218.54304635761588, 125.28973509933775],
[218.54304635761588, 126.28311258278147],
[218.54304635761588, 126.28311258278147],
[218.54304635761588, 127.27649006622516],
]
const result = recognizeFromPoints(points)
expect(result.shape).toBe(Shapes.line)
})
test("should recognize a line at 20 degrees", () => {
const points = [
[34.7682119205298, 268.3360927152318],
[34.7682119205298, 268.3360927152318],
[36.75496688741722, 268.3360927152318],
[37.74834437086093, 268.3360927152318],
[38.741721854304636, 268.3360927152318],
[39.735099337748345, 268.3360927152318],
[40.728476821192054, 268.3360927152318],
[41.721854304635755, 268.3360927152318],
[42.71523178807947, 268.3360927152318],
[43.70860927152318, 268.3360927152318],
[43.70860927152318, 268.3360927152318],
[44.70198675496689, 268.3360927152318],
[44.70198675496689, 268.3360927152318],
[45.6953642384106, 267.3427152317881],
[45.6953642384106, 267.3427152317881],
[46.6887417218543, 267.3427152317881],
[47.682119205298015, 267.3427152317881],
[48.675496688741724, 266.34933774834434],
[48.675496688741724, 266.34933774834434],
[49.66887417218543, 266.34933774834434],
[50.66225165562914, 266.34933774834434],
[51.65562913907284, 265.35596026490066],
[52.64900662251656, 265.35596026490066],
[52.64900662251656, 265.35596026490066],
[53.64238410596026, 265.35596026490066],
[54.63576158940397, 264.362582781457],
[54.63576158940397, 264.362582781457],
[55.629139072847686, 264.362582781457],
[55.629139072847686, 264.362582781457],
[57.6158940397351, 263.36920529801324],
[57.6158940397351, 263.36920529801324],
[58.609271523178805, 263.36920529801324],
[58.609271523178805, 263.36920529801324],
[59.602649006622514, 262.37582781456956],
[60.59602649006623, 262.37582781456956],
[61.58940397350993, 262.37582781456956],
[62.58278145695365, 261.3824503311258],
[63.57615894039735, 261.3824503311258],
[64.56953642384106, 260.3890728476821],
[66.55629139072848, 259.3956953642384],
[67.54966887417218, 259.3956953642384],
[68.54304635761589, 259.3956953642384],
[69.5364238410596, 258.4023178807947],
[69.5364238410596, 258.4023178807947],
[70.52980132450331, 258.4023178807947],
[71.52317880794702, 258.4023178807947],
[71.52317880794702, 257.408940397351],
[71.52317880794702, 257.408940397351],
[72.51655629139073, 257.408940397351],
[72.51655629139073, 257.408940397351],
[72.51655629139073, 257.408940397351],
[73.50993377483444, 257.408940397351],
[73.50993377483444, 257.408940397351],
[73.50993377483444, 257.408940397351],
[74.50331125827815, 257.408940397351],
[76.49006622516558, 257.408940397351],
[77.48344370860927, 257.408940397351],
[79.47019867549669, 256.4155629139073],
[80.4635761589404, 256.4155629139073],
[82.45033112582782, 255.42218543046357],
[83.44370860927151, 255.42218543046357],
[84.43708609271523, 255.42218543046357],
[85.43046357615894, 255.42218543046357],
[85.43046357615894, 254.42880794701986],
[86.42384105960264, 254.42880794701986],
[86.42384105960264, 254.42880794701986],
[87.41721854304636, 254.42880794701986],
[87.41721854304636, 254.42880794701986],
]
const result = recognizeFromPoints(points)
expect(result.shape).toBe(Shapes.line)
})
test("should recognize line 1", () => {
const points = [
[380, 355],
[380, 356],
[381, 355],
[383, 354],
[386, 352],
[391, 349],
[395, 346],
[400, 343],
[405, 339],
[411, 335],
[416, 332],
[425, 325],
[433, 318],
[440, 312],
[447, 306],
[454, 300],
[462, 293],
[471, 283],
[479, 274],
[487, 266],
[498, 255],
[509, 244],
[520, 235],
[531, 226],
[539, 218],
[543, 215],
[550, 208],
[558, 203],
[563, 199],
[567, 196],
[571, 193],
[575, 190],
[577, 189],
[578, 188],
[579, 187],
[580, 187],
[580, 187],
[581, 187],
[581, 186],
]
const result = recognizeFromPoints(points)
expect(result.shape).toBe(Shapes.line)
})
test("should recognize line 2", () => {
const points = [
[648, 509],
[648, 509],
[648, 509],
[646, 509],
[641, 509],
[634, 509],
[628, 509],
[608, 509],
[591, 509],
[571, 509],
[554, 509],
[534, 509],
[513, 505],
[486, 499],
[462, 495],
[454, 494],
[433, 490],
[413, 488],
[396, 485],
[382, 485],
[368, 482],
[360, 482],
[356, 482],
[348, 479],
[341, 479],
[336, 478],
[331, 478],
[329, 478],
[327, 477],
[326, 476],
[326, 476],
[325, 476],
[325, 476],
[325, 476],
[325, 476],
[323, 476],
[322, 476],
[320, 476],
[319, 476],
[318, 476],
[316, 476],
[315, 475],
[315, 475],
[314, 475],
[314, 475],
[313, 475],
]
const result = recognizeFromPoints(points)
expect(result.shape).toBe(Shapes.line)
})
test("should recognize line 3", () => {
const points = [
[204, 590],
[205, 590],
[208, 584],
[219, 574],
[254, 534],
[276, 500],
[305, 456],
[334, 410],
[346, 388],
[376, 336],
[404, 284],
[430, 238],
[454, 197],
[458, 190],
[474, 160],
[483, 142],
[485, 138],
[492, 126],
[495, 121],
[498, 117],
[499, 115],
[500, 114],
[500, 114],
[500, 113],
]
const result = recognizeFromPoints(points)
expect(result.shape).toBe(Shapes.line)
})
})
describe("rectangles", () => {
test("should recognize simple rectangle", () => {
const points = [
[0, 0],
[5, 0],
[10, 0],
[10, 5],
[10, 10],
[5, 10],
[0, 10],
]
const result = recognizeFromPoints(points)
expect(result.shape).toBe(Shapes.rectangle)
})
test("should recognize rectangle with varying points", () => {
const points = [
[0, 0],
[5, 1],
[10, 0],
[10, 6],
[10, 10],
[6, 10],
[0, 10],
]
const result = recognizeFromPoints(points)
expect(result.shape).toBe(Shapes.rectangle)
})
test("should not recognize rectangle with non-rectangular points", () => {
const points = [
[5, 1],
[23, 0],
[10, 54],
[0, 10],
]
const result = recognizeFromPoints(points)
expect(result.shape).not.toBe(Shapes.rectangle)
})
test("should not recognize unclosed rectangle", () => {
const points = [
[-10, -10],
[10, -10],
[10, 10],
]
const result = recognizeFromPoints(points)
expect(result.shape).not.toBe(Shapes.rectangle)
})
test("should recognize almost-closed rectangle", () => {
const points = [
[0, 0],
[5, 0],
[10, 0],
[10, 5],
[10, 10],
[5, 10],
[0, 8],
]
const result = recognizeFromPoints(points)
expect(result.shape).toBe(Shapes.rectangle)
})
test("should recognize half-closed rectangle", () => {
const points = [
[380, 503],
[379, 503],
[379, 503],
[379, 498],
[379, 491],
[379, 471],
[379, 468],
[379, 457],
[379, 440],
[379, 428],
[379, 412],
[379, 398],
[379, 384],
[379, 373],
[379, 369],
[379, 361],
[379, 354],
[379, 349],
[379, 346],
[379, 344],
[379, 343],
[379, 342],
[379, 341],
[381, 340],
[385, 340],
[389, 340],
[398, 340],
[407, 340],
[418, 340],
[429, 340],
[440, 340],
[451, 340],
[463, 340],
[476, 342],
[488, 343],
[502, 345],
[515, 348],
[520, 349],
[531, 350],
[540, 351],
[549, 352],
[552, 353],
[557, 354],
[558, 355],
[560, 355],
[560, 355],
[560, 355],
[561, 355],
[561, 356],
[561, 356],
[561, 356],
[561, 357],
[561, 360],
[561, 363],
[561, 368],
[561, 377],
[561, 388],
[561, 399],
[561, 406],
[561, 414],
[561, 423],
[561, 432],
[561, 441],
[561, 447],
[561, 452],
[561, 459],
[561, 463],
[561, 468],
[561, 471],
[561, 475],
[561, 478],
[561, 479],
[561, 480],
[561, 480],
[561, 481],
[561, 482],
[561, 482],
[561, 483],
[560, 483],
[560, 483],
[558, 484],
[556, 485],
[551, 487],
[546, 488],
[540, 490],
[533, 492],
[522, 493],
[513, 494],
[502, 496],
[491, 497],
[480, 497],
[466, 499],
[452, 500],
[447, 500],
[436, 500],
[427, 501],
[418, 501],
[412, 503],
[407, 503],
[402, 504],
[400, 504],
[398, 504],
[398, 504],
[397, 504],
[396, 504],
]
const result = recognizeFromPoints(points)
expect(result.shape).toBe(Shapes.rectangle)
})
test("should recognize half-closed rectangle 2", () => {
const points = [
[294, 477],
[293, 477],
[293, 477],
[293, 473],
[293, 469],
[293, 465],
[293, 459],
[293, 455],
[293, 451],
[293, 444],
[293, 437],
[293, 431],
[293, 423],
[293, 414],
[293, 403],
[293, 394],
[293, 391],
[293, 384],
[293, 379],
[293, 376],
[293, 373],
[293, 372],
[293, 371],
[293, 370],
[293, 370],
[293, 369],
[294, 369],
[295, 368],
[298, 368],
[303, 367],
[308, 366],
[317, 364],
[328, 362],
[341, 360],
[358, 357],
[375, 355],
[392, 352],
[412, 350],
[436, 348],
[457, 346],
[480, 344],
[501, 342],
[518, 342],
[535, 340],
[539, 339],
[551, 339],
[559, 338],
[568, 337],
[573, 337],
[576, 337],
[578, 337],
[579, 337],
[579, 337],
[580, 337],
[580, 337],
[581, 337],
[581, 338],
[581, 341],
[581, 346],
[581, 349],
[581, 358],
[579, 366],
[578, 373],
[576, 382],
[575, 386],
[571, 403],
[571, 407],
[568, 420],
[567, 424],
[565, 430],
[562, 437],
[561, 442],
[559, 447],
[558, 450],
[557, 452],
[556, 454],
[556, 456],
[555, 458],
[555, 459],
[554, 460],
[554, 461],
[553, 462],
[553, 462],
[553, 463],
[552, 463],
[552, 463],
[552, 464],
[552, 464],
[551, 464],
[550, 465],
[549, 465],
[544, 466],
[541, 467],
[534, 468],
[527, 469],
[521, 470],
[512, 472],
[509, 472],
[500, 474],
[493, 475],
[485, 476],
[478, 477],
[471, 478],
[465, 479],
[460, 480],
[454, 481],
[450, 482],
[446, 483],
[442, 483],
[439, 483],
[437, 484],
[436, 484],
[435, 484],
[434, 484],
[433, 484],
[433, 484],
[433, 484],
]
const result = recognizeFromPoints(points)
expect(result.shape).toBe(Shapes.rectangle)
})
})
})
Source diff could not be displayed: it is too large. Options to address this: view the blob.
......@@ -13,38 +13,69 @@
"build": "webpack --config webpack.prod.js",
"build:analyze": "webpack --env.analyze --config webpack.prod.js",
"build:dev": "webpack --config webpack.dev.js",
"build:bench": "webpack --config webpack.bench.js",
"watch": "webpack --watch --config webpack.dev.js",
"start": "node --experimental-modules src/server.js",
"test": "jest --testPathIgnorePatterns src/liowebrtc src/rtcpeerconnection src/signalbuddy src/yjs",
"test-changed": "jest --only-changed --testPathIgnorePatterns src/liowebrtc src/rtcpeerconnection src/signalbuddy src/yjs",
"test-coverage": "jest --coverage --testPathIgnorePatterns src/liowebrtc src/rtcpeerconnection src/signalbuddy src/yjs",
"lint": "jshint .",
"validate": "npm ls"
"test": "jest --testPathIgnorePatterns src/liowebrtc src/rtcpeerconnection src/signalbuddy src/yjs src/drawing-crdt",
"test-changed": "jest --only-changed --testPathIgnorePatterns src/liowebrtc src/rtcpeerconnection src/signalbuddy src/yjs src/drawing-crdt",
"test-coverage": "jest --coverage --testPathIgnorePatterns src/liowebrtc src/rtcpeerconnection src/signalbuddy src/yjs src/drawing-crdt",
"benchmarks": "node --experimental-modules __benchmarks__/puppeteer.js | npx tap-summary --no-progress",
"test-e2e:peer1": "testcafe chrome:headless __e2e_tests__/peer1.e2e.js",
"test-e2e:peer2": "testcafe chrome:headless __e2e_tests__/peer2.e2e.js",
"test-e2e": "run-p test-e2e:*",
"test-e2e-start": "run-s start-bg test-e2e",
"start-bg": "npm run start > /dev/null 2>&1 &",
"format": "prettier --ignore-path .gitignore --check --write '**/*.{html,js,json,md}'",
"format-check": "prettier --ignore-path .gitignore --check '**/*.{html,js,json,md}'",
"lint": "eslint --ignore-path .gitignore '**/*.js'",
"validate": "npm ls",
"plot": "find plot-scripts/ -maxdepth 1 -type f -name '*.p' -exec gnuplot {} \\;"
},
"dependencies": {
"@ungap/event-target": "^0.1.0",
"d3-shape": "^1.3.5",
"@xmpp/client": "^0.9.2",
"dotenv": "^8.2.0",
"express": "^4.17.1",
"liowebrtc": "file:src/liowebrtc",
"pako": "^1.0.10",
"signalbuddy": "file:src/signalbuddy",
"uuid": "^3.3.3",
"webrtc-adapter": "^7.3.0",
"what-the-pack": "^2.0.3",
"y-array": "^10.1.4",
"y-map": "^10.1.3",
"y-memory": "^8.0.9",
"yjs": "file:src/yjs"
"uuid": "^3.3.3"
},
"devDependencies": {
"@babel/plugin-transform-modules-commonjs": "^7.6.0",
"@fortawesome/fontawesome-free": "^5.12.0",
"@ungap/event-target": "^0.1.0",
"array-flat-polyfill": "^1.0.1",
"babel-eslint": "^10.0.3",
"chalk": "^3.0.0",
"css-loader": "^3.4.1",
"d3-shape": "^1.3.5",
"drawing-crdt": "file:src/drawing-crdt/pkg",
"eslint": "^6.5.1",
"eslint-config-prettier": "^6.5.0",
"eslint-plugin-testcafe": "^0.2.1",
"fastbitset": "^0.2.8",
"file-loader": "^5.0.2",
"humanhash": "^1.0.4",
"jdenticon": "^2.2.0",
"jest": "^24.9.0",
"liowebrtc": "file:src/liowebrtc",
"npm-run-all": "^4.1.5",
"pako": "^1.0.10",
"prettier": "^1.18.2",
"puppeteer-core": "^2.0.0",
"rtcpeerconnection": "file:src/rtcpeerconnection",
"style-loader": "^1.1.2",
"tap-summary": "^4.0.0",
"testcafe": "^1.5.0",
"webpack": "^4.41.0",
"webpack-bundle-analyzer": "^3.6.0",
"webpack-cli": "^3.3.9",
"webpack-merge": "^4.2.2"
"webpack-merge": "^4.2.2",
"webrtc-adapter": "^7.3.0",
"what-the-pack": "^2.0.3",
"y-array": "^10.1.4",
"y-map": "^10.1.3",
"y-memory": "^8.0.9",
"yjs": "file:src/yjs",
"zora": "^3.1.8"
},
"pre-commit": [
"lint",
......
set xlabel "Iterations"
set ylabel "Time [ms]"
set title "addPath() performance scalability"
set key inside bottom right
set terminal dumb size 120, 30
set autoscale
plot "plots/dot-seq-benchmark.tsv" using 1:($2/(1e6*$1)) with lines title "dot [sequential]"
set terminal pdf
set output "plots/dot-seq-benchmark.pdf"
plot "plots/dot-seq-benchmark.tsv" using 1:($2/(1e6*$1)) with lines title "dot [sequential]"
<svg viewBox="-10 -10 30 30" xmlns="http://www.w3.org/2000/svg">
<circle cx="10" cy="10" r="10"/>
</svg>
\ No newline at end of file
public/favicon.ico

1.12 KiB