make canvas example

This commit is contained in:
William Bell committed 2023-07-10 22:10:15 +01:00
1 parent ee781a224b
commit c3c026a7d4
27 files changed
+369 -104

No files matched your search

+2 -1
View File
@@ -1,3 +1,4 @@
.DS_Store
movie.mp4
movie.mp4
examples/canvas/canvas.json
-60
View File
@@ -1,60 +0,0 @@
import "http" as http
import "http/bleach" as bleach
app = http.server()
app.use(http.logger)
app.use(http.cookie)
let setCookie(req, res) = do
if (not ("token" in req.cookies)) do
res.setCookie("token", {value: string(random())})
res.next()
app.use(setCookie)
app.use(http.static("static"))
let form(req, res) = do
term.log(req, res)
res.send("<pre>"+bleach.clean(req.buffer.header.to('string'))+bleach.clean(req.buffer.body.to('string'))+"</pre><form method='POST'><input type='text' name='test'><input type='submit'></form>")
app.route("/form", ['POST'], form)
let api(req, res) = do
data = "cool"
res.json({(data): "Hello, World!", description: "This is a test.", π: π, πRational: fraction(π)})
app.route("/api", null, api)
let google(req, res) = do
res.redirect("https://google.com/")
app.route("/google", null, google)
let api(req, res) = do
res.json(req.params)
app.route("/params/example/<name>", null, api)
let wait(req, res) = do
for (i from 0 to number(req.params.seconds)) do
time.snooze(1)
res.text((1+i)+" seconds have passed. ")
res.close()
app.route("/wait/<seconds>", null, wait)
let rand(req, res) = do
res.json(random())
app.route("/random", null, rand)
let about(req, res) = do
res.send("<h1>About</h1>")
app.route("/about", null, about)
let contact(req, res) = do
res.send("<h1>Contact</h1>")
app.route("/contact", null, contact)
let err404(req, res) = do
res.code = 404
res.sendFile("static/404.html")
app.use(err404)
app.run({port: 8000, callback: (port) = term.log("Listening on port "+string(port))})
+82
View File
@@ -0,0 +1,82 @@
import "../../src/http" as http
let parent = path.parent(program.file.path)
let saveFile = path.join([parent, "canvas.json"])
let changes = []
let canvas = []
let save() = do
let f = file.write(saveFile)
f.json({canvas:canvas, changes: changes})
let load() = do
try do
let data = file.read(saveFile).json()
canvas = data.canvas
changes = data.changes
catch (e) do
let row = []
for (i from 0 to size.width) do
row.append(null)
for (i from 0 to size.height) do
canvas.append(row.copy())
let app = http.server()
let size = { width: 500, height: 500 }
app.use(http.logger)
let setPixel(x,y, colour) = do
if (x >= size.width || y >= size.height) do
return
canvas[y][x] = colour
changes.insert(0, {x: x, y: y, colour: colour, time: time.now().unixMilli()})
load()
let saveThread() = do
forever do
time.snooze(30)
save()
let t = thread(saveThread)
t.start()
let err404path = path.join([parent,"static", "404.html"])
app.use(http.static(path.join([parent,"static"])))
let getCanvas(req, res) = do
res.json(canvas)
app.route('/get-canvas', null, getCanvas)
let getCanvasUpdates(req, res) = do
if ("lastTime" not in req.query) do
res.json(changes)
return
let lastTime = number(req.query.lastTime)
let filtered = []
for (i from 0 to changes.length) do
if (changes[i].time > lastTime) do
filtered.append(changes[i])
else break
res.json(filtered)
app.route('/get-canvas-updates', null, getCanvasUpdates)
let updatePixel(req, res) = do
let data = json.parse(req.buffer.body.to('string'))
setPixel(data.x, data.y, data.colour)
res.send('200 OK')
app.route('/update-canvas', ['POST'], updatePixel)
let err404(req, res) = do
res.code = 404
res.sendFile(err404path)
app.use(err404)
app.port = 5000
app.run()
File renamed without changes.
+15
View File
@@ -0,0 +1,15 @@
<html>
<head>
<title>canvas.ar</title>
<link rel="stylesheet" href="/style.css">
<script src="/script.js" defer async></script>
</head>
<body>
<h1 class="title">Canvas.AR</h1>
<canvas id="canvas"></canvas>
<div class="colourSelect">
<input type="color" id="colour" value="#000000">
<input type="text" id="colourhex" value="#000000">
</div>
</body>
</html>
+107
View File
@@ -0,0 +1,107 @@
const canvas = document.getElementById("canvas");
const ctx = canvas.getContext("2d");
const size = { width: 500, height: 500 };
const colourPicker = document.getElementById("colour");
const colourHex = document.getElementById("colourhex");
colourHex.innerText = colourPicker.value;
let currentColour = colourPicker.value;
console.log(colourPicker, colourHex);
colourPicker.addEventListener("input", (e) => {
colourHex.value = e.target.value;
currentColour = e.target.value;
});
colourHex.addEventListener("input", () => {
colourPicker.value = colourHex.value;
currentColour = colourHex.value;
});
const addPixel = (x, y, colour) => {
pixels[y][x] = colour;
};
new Event("colour-change");
let pixels = [];
let updatesSince = new Date();
const resize = () => {
const { innerWidth, innerHeight } = window;
const ratio = Math.min(innerWidth / size.width, innerHeight / size.height);
canvas.style.width = `${size.width * ratio}px`;
canvas.style.height = `${size.height * ratio}px`;
canvas.width = size.width * ratio;
canvas.height = size.height * ratio;
};
window.addEventListener("resize", resize);
const draw = () => {
ctx.fillStyle = "#ffffff";
ctx.fillRect(0, 0, canvas.width, canvas.height);
const pixelsize = canvas.width / size.width;
for (let i = 0; i < pixels.length; i++) {
for (let j = 0; j < pixels[i].length; j++) {
if (!pixels[i][j]) continue;
ctx.fillStyle = pixels[i][j];
ctx.fillRect(j * pixelsize, i * pixelsize, pixelsize, pixelsize);
}
}
requestAnimationFrame(draw);
};
const getCanvas = async () => {
const resp = await fetch("/get-canvas");
const data = await resp.json();
pixels = data;
};
const getUpdates = async () => {
const url = new URL("/get-canvas-updates", window.location.href);
url.searchParams.append("lastTime", updatesSince.getTime());
const resp = await fetch(url);
const data = await resp.json();
for (const update of data) {
addPixel(update.x, update.y, update.colour);
}
updatesSince = new Date();
};
canvas.addEventListener("click", async (e) => {
const pixelsize = canvas.width / size.width;
const x = Math.floor(e.offsetX / pixelsize);
const y = Math.floor(e.offsetY / pixelsize);
addPixel(x, y, currentColour);
updatesSince = new Date();
await fetch("/update-canvas", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
x,
y,
colour: currentColour,
}),
});
});
resize();
requestAnimationFrame(draw);
const snooze = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
const main = async () => {
await getCanvas();
while (true) {
try {
await getUpdates();
} catch (e) {
console.error(e);
}
await snooze(500);
}
};
main();
+44
View File
@@ -0,0 +1,44 @@
body {
background-color: #f0f0f0;
font-family: sans-serif;
margin: 0;
padding: 0;
}
.title {
text-align: center;
margin: 0;
padding: 1rem;
}
#canvas {
display: block;
background-color: #ffffff;
margin: 0 auto 1rem;
padding: 0;
}
.colourSelect {
display: flex;
padding: 1rem;
width: 100%;
max-width: 500px;
margin: 0 auto 1rem;
background-color: rgb(188, 188, 188);
border-radius: 10px;
}
#colour {
border: none;
background-color: #ffffff;
margin: 0;
padding: 0;
}
#colourhex {
background-color: #ffffff;
width: 100%;
border: none;
margin: 0;
padding: 0;
text-align: center;
}
+22
View File
@@ -0,0 +1,22 @@
import "../../src/http" as http
import "../../src/bleach" as bleach
let app = http.server()
app.use(http.logger)
let err404path = path.join([path.parent(program.file.path),"static", "404.html"])
app.use(http.static(path.join([path.parent(program.file.path),"static"])))
let form(req, res) = do
term.log(req, res)
res.send("<pre>"+bleach.clean(req.buffer.header.to('string'))+bleach.clean(req.buffer.body.to('string'))+"</pre><form method='POST'><input type='text' name='test'><input type='submit'></form>")
app.route("/form", ['POST'], form)
let err404(req, res) = do
res.code = 404
res.sendFile(err404path)
app.use(err404)
app.run()
+9
View File
@@ -0,0 +1,9 @@
<html>
<head>
<title>404</title>
</head>
<body>
<h1>404</h1>
<p>Page not found</p>
</body>
</html>
File renamed without changes.
File renamed without changes.
-43
View File
@@ -1,43 +0,0 @@
import "listener" as listener
import "logger" as logger
import "static" as static
import "cookie" as cookie
let cookie = cookie.cookie
let static = static.static
let logger = logger.logger
let server() = do
let class = {}
let threads = []
let numThreads = 10
let routes = []
let Socket = null
class.setNumberOfThreads(num) = do
numThreads = num
class.run(options) = do
Socket = socket.server("tcp", options.port)
let onErr(e) = do
term.error(e.type+": "+e.message)
term.error(e.code)
term.error(e.path+" on line "+e.line)
if ("onErr" in options) do
onErr = options.onErr
for (i from 0 to numThreads) do
t = thread(()=listener.listener(Socket, routes, onErr))
threads.append(t)
t.start()
if ("callback" in options) do
options.callback(options.port)
for (i from 0 to threads.length) do
threads[i].join()
class.close() = Socket.close()
class.route(path, methods, callback) = do
if (methods == null) do
methods = ["GET"]
routes.append({path: path, callback: callback, methods: methods, type: "route"})
class.use(callback) = do
routes.append({callback: callback, type: "middleware"})
return class.object()
+1
View File
@@ -0,0 +1 @@
import "http/init" as *
File renamed without changes.
File renamed without changes.
File renamed without changes.
+54
View File
@@ -0,0 +1,54 @@
let formdata(req, res) = do
let boundary = null
if ("Content-Type" in req.headers) do
let contentType = req.headers["Content-Type"]
if (contentType.startsWith("multipart/form-data")) do
let parts = contentType.split("; ")
for (i from 0 to parts.length) do
let part = parts[i]
if (part.startsWith("boundary=")) do
boundary = part.split("=")[1]
break
if (boundary == null) do
res.next()
return
let body = req.body
let parts = body.split("--"+boundary)
let data = {}
for (i from 0 to parts.length) do
let part = parts[i]
if (part == "" || part == "--") do
continue
let lines = part.split("\r\n")
let headers = {}
let body = ""
for (j from 0 to lines.length) do
let line = lines[j]
if (line == "") do
body = lines.slice(j+1).join("\r\n")
break
let header = line.split(": ")
headers[header[0]] = header[1]
if ("Content-Disposition" in headers) do
let disposition = headers["Content-Disposition"]
let name = null
let filename = null
let dispositionParts = disposition.split("; ")
for (j from 0 to dispositionParts.length) do
let part = dispositionParts[j]
if (part.startsWith("name=")) do
name = part.split("=")[1]
else if (part.startsWith("filename=")) do
filename = part.split("=")[1]
if (name == null) do
continue
if (filename == null) do
data[name] = body
else do
data[name] = {
"filename": filename,
"contentType": headers["Content-Type"],
"data": body
}
req.formdata = data
res.next()
File renamed without changes.
+33
View File
@@ -0,0 +1,33 @@
import "listener" as listener
import "logger" as *
import "static" as *
import "cookie" as *
let server() = do
let class = {port: 3000, numThreads: 10, callback: ()=term.log("Server running on port "+class.port)}
let threads = []
let routes = []
let Socket = null
class.onErr(e) = do
term.error(e.type+": "+e.message)
term.error(e.code)
term.error(e.path+" on line "+e.line)
class.run() = do
Socket = socket.server("tcp", class.port)
for (i from 0 to class.numThreads) do
t = thread(()=listener.listener(Socket, routes, class.onErr))
threads.append(t)
t.start()
class.callback()
for (i from 0 to threads.length) do
threads[i].join()
class.close() = Socket.close()
class.route(path, methods, callback) = do
if (methods == null) do
methods = ["GET"]
routes.append({path: path, callback: callback, methods: methods, type: "route"})
class.use(callback) = do
routes.append({callback: callback, type: "middleware"})
return class
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
View File
File renamed without changes.
File renamed without changes.
BIN
View File
Binary file not shown.