Skip to content
Snippets Groups Projects
Commit edfe21b8 authored by Giovanni Caruso's avatar Giovanni Caruso
Browse files

Merge branch 'master' into avatar

parents e3ee3d0f 188789b4
No related branches found
No related tags found
No related merge requests found
// Room connection and synchronisation. // Room connection and synchronisation.
// Translate local canvas input events to draw messages and send to the room. // Translate local canvas input events to draw messages in light of current tool
// selections and send to the room.
// Get back room updates and invoke the local canvas renderer. // Get back room updates and invoke the local canvas renderer.
import * as canvas from "./canvas.js" import * as canvas from "./canvas.js"
import * as HTML from "./elements.js" import * as HTML from "./elements.js"
import { connect } from "./room.js" import { connect } from "./room.js"
import * as toolSelection from "./tool-selection.js"
const TEST_ROOM = "imperial" const TEST_ROOM = "imperial"
const MIN_PRESSURE_FACTOR = 0.1
const MAX_PRESSURE_FACTOR = 1.5
// This is a quadratic such that:
// - getPressureFactor(0.0) = MIN_PRESSURE_FACTOR
// - getPressureFactor(0.5) = 1.0
// - getPressureFactor(1.0) = MAX_PRESSURE_FACTOR
// For sensible results, maintain that:
// - 0.0 <= MIN_PRESSURE_FACTOR <= 1.0
// - 1.0 <= MAX_PRESSURE_FACTOR
// For intuitive results, maintain that:
// - MAX_PRESSURE_FACTOR <= ~2.0
const getPressureFactor = (pressure) => {
const a = 2 * (MAX_PRESSURE_FACTOR + MIN_PRESSURE_FACTOR) - 4
const b = -MAX_PRESSURE_FACTOR - 3 * MIN_PRESSURE_FACTOR + 4
const c = MIN_PRESSURE_FACTOR
return a * pressure ** 2 + b * pressure + c
}
let room = null let room = null
const onRoomConnect = (room_) => { const onRoomConnect = (room_) => {
...@@ -89,102 +110,8 @@ const tryRoomConnect = async (roomID) => { ...@@ -89,102 +110,8 @@ const tryRoomConnect = async (roomID) => {
.catch((err) => alert(`Error connecting to a room:\n${err}`)) .catch((err) => alert(`Error connecting to a room:\n${err}`))
} }
const ERASER_RADIUS = 10
const tools = {
PEN: Symbol("pen"),
ERASER: Symbol("eraser"),
}
let currentTool = tools.PEN
const pathIDsByPointerID = new Map() const pathIDsByPointerID = new Map()
HTML.penButton.addEventListener("click", () => {
if (currentTool == tools.PEN) {
showElement(HTML.penProperties)
} else {
currentTool = tools.PEN
HTML.penButton.classList.add("selected")
HTML.eraserButton.classList.remove("selected")
}
})
HTML.closeButton.forEach((element) => {
element.addEventListener("click", () => {
hideElement(element.parentNode.parentNode.parentNode)
})
})
window.addEventListener("click", (event) => {
if (event.target == HTML.penProperties) {
hideElement(HTML.penProperties)
} else if (event.target == HTML.palette) {
hideElement(HTML.palette)
hideElement(HTML.penProperties)
}
})
HTML.rectangle.addEventListener("click", () => {
showElement(HTML.palette)
})
const svg = HTML.wheel.children
for (let i = 1; i < svg.length; i++) {
svg[i].addEventListener("click", (event) => {
const paletteColour = event.target.getAttribute("fill")
HTML.rectangle.style.backgroundColor = paletteColour
HTML.picker.value = paletteColour
HTML.labelColours.style.backgroundColor = paletteColour
canvas.setStrokeColour(paletteColour)
hideElement(HTML.palette)
})
}
function showElement(element) {
element.style.display = "block"
}
function hideElement(element) {
element.style.display = "none"
}
HTML.picker.addEventListener("change", () => {
const paletteColour = event.target.value
HTML.rectangle.style.backgroundColor = paletteColour
HTML.labelColours.style.backgroundColor = paletteColour
canvas.setStrokeColour(paletteColour)
})
HTML.output.innerHTML = HTML.slider.value
HTML.slider.oninput = function() {
HTML.output.innerHTML = this.value
canvas.setStrokeRadius(this.value / 10)
}
const x = window.matchMedia(
"only screen and (orientation: landscape) and (max-width: 600px)",
)
x.addListener(() => {
if (x.matches) {
HTML.wheel.setAttribute("viewBox", "-50 10 200 100")
HTML.palette.setAttribute("style", "padding-top: 50px")
} else {
HTML.wheel.setAttribute("viewBox", "0 10 100 100")
}
})
HTML.picker.addEventListener("change", () => {
const paletteColour = event.target.value
HTML.rectangle.style.backgroundColor = paletteColour
HTML.labelColours.style.backgroundColor = paletteColour
canvas.setStrokeColour(paletteColour)
})
HTML.eraserButton.addEventListener("click", () => {
currentTool = tools.ERASER
HTML.penButton.classList.remove("selected")
HTML.eraserButton.classList.add("selected")
})
HTML.peerButton.addEventListener("click", () => { HTML.peerButton.addEventListener("click", () => {
const peerID = HTML.peerIDElem.value const peerID = HTML.peerIDElem.value
if (room == null || peerID == "") { if (room == null || peerID == "") {
...@@ -293,7 +220,7 @@ const erasePoint = ([x, y]) => { ...@@ -293,7 +220,7 @@ const erasePoint = ([x, y]) => {
} }
room.getPaths().forEach((points, pathID) => { room.getPaths().forEach((points, pathID) => {
points.forEach((point, i) => { points.forEach((point, i) => {
if (getDistance([x, y], point) <= ERASER_RADIUS) { if (getDistance([x, y], point) <= toolSelection.getEraseRadius()) {
room.erasePoint(pathID, i) room.erasePoint(pathID, i)
} }
}) })
...@@ -304,19 +231,18 @@ canvas.input.addEventListener("strokestart", ({ detail: e }) => { ...@@ -304,19 +231,18 @@ canvas.input.addEventListener("strokestart", ({ detail: e }) => {
if (room == null) { if (room == null) {
return return
} }
const currentTool = toolSelection.getTool()
const mousePos = [e.offsetX, e.offsetY] const mousePos = [e.offsetX, e.offsetY]
if (currentTool == toolSelection.Tools.PEN) {
if (currentTool == tools.PEN) {
pathIDsByPointerID.set( pathIDsByPointerID.set(
e.pointerId, e.pointerId,
room.addPath([ room.addPath([
...mousePos, ...mousePos,
canvas.getStrokeRadius(e.pressure), toolSelection.getStrokeRadius() * getPressureFactor(e.pressure),
canvas.getStrokeColour(), toolSelection.getStrokeColour(),
]), ]),
) )
} else if (currentTool == tools.ERASER) { } else if (currentTool == toolSelection.Tools.ERASER) {
erasePoint(mousePos) erasePoint(mousePos)
} }
}) })
...@@ -329,16 +255,15 @@ canvas.input.addEventListener("strokemove", ({ detail: e }) => { ...@@ -329,16 +255,15 @@ canvas.input.addEventListener("strokemove", ({ detail: e }) => {
if (room == null) { if (room == null) {
return return
} }
const currentTool = toolSelection.getTool()
const mousePos = [e.offsetX, e.offsetY] const mousePos = [e.offsetX, e.offsetY]
if (currentTool == toolSelection.Tools.PEN) {
if (currentTool == tools.PEN) {
room.extendPath(pathIDsByPointerID.get(e.pointerId), [ room.extendPath(pathIDsByPointerID.get(e.pointerId), [
...mousePos, ...mousePos,
canvas.getStrokeRadius(e.pressure), toolSelection.getStrokeRadius() * getPressureFactor(e.pressure),
canvas.getStrokeColour(), toolSelection.getStrokeColour(),
]) ])
} else if (currentTool == tools.ERASER) { } else if (currentTool == toolSelection.Tools.ERASER) {
erasePoint(mousePos) erasePoint(mousePos)
} }
}) })
......
...@@ -16,9 +16,6 @@ const lineFn = line() ...@@ -16,9 +16,6 @@ const lineFn = line()
const pathGroupElems = new Map() const pathGroupElems = new Map()
let strokeColour = "#0000ff"
let strokeRadius = 5
const MAX_POINT_DISTANCE = 5 const MAX_POINT_DISTANCE = 5
const MAX_RADIUS_DELTA = 0.05 const MAX_RADIUS_DELTA = 0.05
...@@ -168,38 +165,3 @@ canvas.addEventListener("pointerup", dispatchPointerEvent("strokeend")) ...@@ -168,38 +165,3 @@ canvas.addEventListener("pointerup", dispatchPointerEvent("strokeend"))
canvas.addEventListener("pointerleave", dispatchPointerEvent("strokeend")) canvas.addEventListener("pointerleave", dispatchPointerEvent("strokeend"))
canvas.addEventListener("pointermove", dispatchPointerEvent("strokemove")) canvas.addEventListener("pointermove", dispatchPointerEvent("strokemove"))
canvas.addEventListener("touchmove", (e) => e.preventDefault()) canvas.addEventListener("touchmove", (e) => e.preventDefault())
export function setStrokeColour(colour) {
strokeColour = colour
}
export function getStrokeColour() {
return strokeColour
}
const MIN_PRESSURE_FACTOR = 0.1
const MAX_PRESSURE_FACTOR = 1.5
// This is a quadratic such that:
// - getPressureFactor(0.0) = MIN_PRESSURE_FACTOR
// - getPressureFactor(0.5) = 1.0
// - getPressureFactor(1.0) = MAX_PRESSURE_FACTOR
// For sensible results, maintain that:
// - 0.0 <= MIN_PRESSURE_FACTOR <= 1.0
// - 1.0 <= MAX_PRESSURE_FACTOR
// For intuitive results, maintain that:
// - MAX_PRESSURE_FACTOR <= ~2.0
const getPressureFactor = (pressure) => {
const a = 2 * (MAX_PRESSURE_FACTOR + MIN_PRESSURE_FACTOR) - 4
const b = -MAX_PRESSURE_FACTOR - 3 * MIN_PRESSURE_FACTOR + 4
const c = MIN_PRESSURE_FACTOR
return a * pressure ** 2 + b * pressure + c
}
export function getStrokeRadius(pressure) {
return strokeRadius * getPressureFactor(pressure)
}
export function setStrokeRadius(radius) {
strokeRadius = radius
}
import * as HTML from "./elements.js"
export const Tools = Object.freeze({
PEN: Symbol("pen"),
ERASER: Symbol("eraser"),
})
let tool = Tools.PEN
let strokeColour = "#0000ff"
let strokeRadius = 5
// TODO: The erase radius should also be selectable.
const ERASE_RADIUS = 10
export const getTool = () => tool
export const getStrokeColour = () => strokeColour
export const getStrokeRadius = () => strokeRadius
export const getEraseRadius = () => ERASE_RADIUS
const showElement = (element) => {
element.style.display = "block"
}
const hideElement = (element) => {
element.style.display = "none"
}
HTML.penButton.addEventListener("click", () => {
if (tool == Tools.PEN) {
showElement(HTML.penProperties)
} else {
tool = Tools.PEN
HTML.penButton.classList.add("selected")
HTML.eraserButton.classList.remove("selected")
}
})
HTML.eraserButton.addEventListener("click", () => {
tool = Tools.ERASER
HTML.penButton.classList.remove("selected")
HTML.eraserButton.classList.add("selected")
})
HTML.picker.addEventListener("change", () => {
const paletteColour = event.target.value
HTML.rectangle.style.backgroundColor = paletteColour
HTML.labelColours.style.backgroundColor = paletteColour
strokeColour = paletteColour
})
HTML.slider.oninput = function() {
HTML.output.innerHTML = this.value
strokeRadius = this.value / 10
}
HTML.output.innerHTML = HTML.slider.value
const x = window.matchMedia(
"only screen and (orientation: landscape) and (max-width: 600px)",
)
x.addListener(() => {
if (x.matches) {
HTML.wheel.setAttribute("viewBox", "-50 10 200 100")
HTML.palette.setAttribute("style", "padding-top: 50px")
} else {
HTML.wheel.setAttribute("viewBox", "0 10 100 100")
}
})
HTML.closeButton.forEach((element) => {
element.addEventListener("click", () => {
hideElement(element.parentNode.parentNode.parentNode)
})
})
window.addEventListener("click", (event) => {
if (event.target == HTML.penProperties) {
hideElement(HTML.penProperties)
} else if (event.target == HTML.palette) {
hideElement(HTML.palette)
hideElement(HTML.penProperties)
}
})
HTML.rectangle.addEventListener("click", () => {
showElement(HTML.palette)
})
const svg = HTML.wheel.children
for (let i = 1; i < svg.length; i++) {
svg[i].addEventListener("click", (event) => {
const paletteColour = event.target.getAttribute("fill")
HTML.rectangle.style.backgroundColor = paletteColour
HTML.picker.value = paletteColour
HTML.labelColours.style.backgroundColor = paletteColour
strokeColour = paletteColour
hideElement(HTML.palette)
})
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment