This commit is contained in:
Johnathon Slightham
2021-09-07 12:53:34 -04:00
parent 57b9827e64
commit 71017cc5b6
34 changed files with 27214 additions and 2626 deletions

7
API/games/circle.js Normal file
View File

@@ -0,0 +1,7 @@
exports.newCircle = function(center, radius, velocity) {
return {
center: center,
radius: radius,
velocity: velocity
}
}

32
API/games/vector.js Normal file
View File

@@ -0,0 +1,32 @@
exports.createVector = function(x, y) {
return {x: x, y: y};
}
exports.mag = function(v){
return Math.sqrt(exports.dot(v, v));
}
exports.add = function(v1, v2){
return {x:v1.x + v2.x, y:v1.y + v2.y};
}
exports.sub = function(v1, v2){
return {x: v1.x - v2.x, y:v1.y - v2.y};
}
exports.dot = function(v1, v2){
return v1.x*v2.x + v1.y*v2.y;
}
exports.mult = function(v1, k){
return {x: v1.x*k, y:v1.y*k};
}
exports.reflect = function(v1, n){
return exports.sub(exports.mult(exports.project(v1, n), 2), v1);
}
exports.project = function(v1, v2){
return exports.mult(v2, (1/Math.pow(exports.mag(v2), 2)) * exports.dot(v1, v2));
}

195
API/games/volleyball.js Normal file
View File

@@ -0,0 +1,195 @@
//import * as posenet from "@tensorflow-models/posenet";
let posenet = require("@tensorflow-models/posenet");
//var circle = require("./circle")
let vec = require("./vector");
var collide = require('line-circle-collision');
let prevTime = 0;
exports.process = function(game, players) {
if (Object.keys(game.data).length == 0) {
initialize(game);
}
let timeElapsed = Date.now() - prevTime;
prevTime = Date.now();
collideAll(game.data.objects, players);
moveItems(game.data.objects, timeElapsed);
}
function initialize(game){
prevTime = Date.now();
game.data = {
objects: []
}
for (let i = 0; i < 50; i++){
game.data.objects.push({
name: "circle",
position: vec.createVector(20*i, -100),
radius: 20,
velocity: vec.createVector(0, 0)
})
}
}
function moveItems(objects, timeElapsed) {
let acceleration = vec.createVector(0, 0.0002);
for (let i = 0; i < objects.length; i++){
objects[i].velocity = vec.add(objects[i].velocity, vec.mult(acceleration, timeElapsed))
objects[i].position = vec.add(objects[i].position, vec.mult(objects[i].velocity, timeElapsed))
if (objects[i].position.y > 500 && objects[i].velocity.y > 0) {
objects[i].velocity.y *= -1;
}
// if (objects[i].position.x < 0 && objects[i].velocity.x < 0) {
// objects[i].velocity.x *= -1;
// }
// if (objects[i].position.x > 1000 && objects[i].velocity.x > 0) {
// objects[i].velocity.x *= -1;
// }
}
}
function collideWithJoint(object, [start, end]){
let diff = vec.sub(end, start);
object.velocity = vec.mult(vec.reflect(object.velocity, {x:-diff.y, y:diff.x}), -1);
object.position = vec.add(object.position, vec.mult(object.velocity, 3));
}
/*
function pointInCircle(x1, y1, cx, cy, cr){
let distance = (x1-cx)*(x1-cx)+(y1-cy)*(y1-cy);
distance = Math.sqrt(distance);
return distance <= cr;
}
*/
function pointOnLine(point, start, end){
if (point.x < start.x && point.x > end.x || point.x > start.x && point.x < end.x){
if (point.y > start.y && point.y < end.y || point.y > end.y && point.y < start.y){
return true;
}
}
// if (vec.mag(vec.sub(point, start)) > vec.mag(vec.sub(end, start))) {
// return false;
// }
// if (vec.mag(vec.sub(point, end)) > vec.mag(vec.sub(start, end))) {
// return false;
// }
return false;
}
// is either end INSIDE the circle?
// if so, return true immediately
function overlappingWithJoint(object, start, end){
// let cx = object.position.x;
// let cy = object.position.y;
// let r = object.radius;
// let x1 = start.x;
// let y1 = start.y;
// let x2 = end.x;
// let y2 = end.y;
// let inside1 = pointCircle(x1,y1, cx,cy,r);
// let inside2 = pointCircle(x2,y2, cx,cy,r);
// if (inside1 || inside2) return true;
// // get length of the line
// let distX = x1 - x2;
// let distY = y1 - y2;
// let len = Math.sqrt( (distX*distX) + (distY*distY) );
// // get dot product of the line and circle
// let dot = ( ((cx-x1)*(x2-x1)) + ((cy-y1)*(y2-y1)) ) / Math.pow(len,2);
// // find the closest point on the line
// let closestX = x1 + (dot * (x2-x1));
// let closestY = y1 + (dot * (y2-y1));
// // is this point actually on the line segment?
// // if so keep going, but if not, return false
// let onSegment = linePoint(x1,y1,x2,y2, closestX,closestY);
// if (!onSegment) return false;
// // optionally, draw a circle at the closest
// // point on the line
// // fill(255,0,0);
// // noStroke();
// // ellipse(closestX, closestY, 20, 20);
// // get distance to closest point
// distX = closestX - cx;
// distY = closestY - cy;
// let distance = Math.sqrt( (distX*distX) + (distY*distY) );
// if (distance <= r) {
// return true;
// }
// return false;
// let pos = object.position;
// // if (pointInCircle(end.x, end.y, pos.x, pos.y, object.radius)){
// // return true;
// // }
// // if (pointInCircle(start.x, start.y, pos.x, pos.y, object.radius)){
// // return true;
// // }
// let diff = vec.sub(end, start);
// let len = vec.mag(diff);
// let proj = vec.project(vec.sub(end, pos), diff);
// let point = vec.sub(end, proj);
// console.log("diff: ")
// console.log(diff)
// console.log("proj")
// console.log(proj)
// console.log("new")
// //console.log(point);
// if (!pointOnLine(point, start, end)){
// return false;
// }
// //let dist = Math.sqrt(Math.pow(vec.mag(vec.sub(end, pos))) - Math.pow(vec.mag(proj)))
// let dist = vec.mag(vec.sub(point, pos));
// return dist <= object.radius;
// //console.log("overlapping detected");
// // let diff = vec.sub(end, start);
// // let pos = object.position;
// // let diff2 = vec.sub(end, pos);
// // let r = vec.project(diff2, diff);
// // let dist = Math.sqrt(vec.dot(pos, pos) + vec.dot(r, r));
// // let midPoint = vec.mult(vec.add(start, end), 0.5);
// // return Math.abs(dist) <= object.radius;
return collide([start.x, start.y], [end.x, end.y], [object.position.x, object.position.y], object.radius);
}
function collideAll(objects, players){
let minConfidence = 0.5;
if (playes){
players.forEach(player => {
if (player.universePairs){
console.log("sdfsdfg");
player.universePairs.forEach(pair => {
let start = vec.createVector(pair.x1, pair.y1);
let end = vec.createVector(pair.x2, pair.y2);
for (let i = 0; i < objects.length; i++){
let object = objects[i];
if (overlappingWithJoint(object, start, end)){
collideWithJoint(object, [start, end]);
}
}
})
}
})
}
}

View File

@@ -2,41 +2,114 @@ const express = require('express');
const app = express(); const app = express();
const webSockets = express(); const webSockets = express();
const bodyParser = require('body-parser'); const bodyParser = require('body-parser');
const PORT = 4000;
const cors = require('cors'); const cors = require('cors');
var fs = require('fs');
var http = require('http');
var https = require('https');
const WebSocket = require('ws');
const mongoose = require('mongoose'); const mongoose = require('mongoose');
const socket = require('socket.io');
const config = require('./DB.js');
const roomRoute = require('./room.route');
var expressWs = require('express-ws')(app);
// DB credentials
const config = require('./DB.js');
// Routes
const roomRoute = require('./room.route');
// All active rooms, store in map since faster than DB
var rooms = new Map();
// For SSL Connection
var privateKey = fs.readFileSync('/etc/letsencrypt/live/dance.cubehostingmc.com/privkey.pem', 'utf8');
var certificate = fs.readFileSync('/etc/letsencrypt/live/dance.cubehostingmc.com/fullchain.pem', 'utf8');
var credentials = {key: privateKey, cert: certificate};
// DB connection
mongoose.Promise = global.Promise; mongoose.Promise = global.Promise;
mongoose.connect(config.DB, { useNewUrlParser: true }).then( mongoose.connect(config.DB, { useNewUrlParser: true }).then(
() => { console.log('Database is connected') }, () => { console.log('Database is connected') },
err => { console.log('Can not connect to the database' + err) } err => { console.log('Can not connect to the database' + err) }
); );
// Express config
app.use(cors()); app.use(cors());
app.use(bodyParser.urlencoded({ extended: true })); app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json()); app.use(bodyParser.json());
app.use('/rooms', roomRoute); app.use('/rooms', roomRoute);
var httpServer = http.createServer(app);
var httpsServer = https.createServer(credentials, app);
app.listen(PORT, function () { //games
console.log('Express server running on port:', PORT); //import * as volleyball from 'games/volleyball.mjs';
})
app.ws('/:id', function (ws, req) { var volleyball = require('./games/volleyball');
ws.on('message', function (msg) { // Websocket config
msgJ = JSON.parse(msg); const wss = new WebSocket.Server({ server: httpsServer }); // Use SSL Server
console.log(req.params.id);
console.log(msgJ.playerId); // On WebSocket Connection
ws.send(msg); wss.on('connection', function connection(ws) {
// On Message Recieve
ws.on('message', function incoming(message) {
let msgJson = JSON.parse(message); // Convert text to JSON
// Process Player Positions
if (rooms.has(msgJson.roomId)) { // Check if the room already exists in the map
// Process Game Data
//TODO: implement a generic way to have the gameData sent to the right file
if (msgJson.game.name == "0"){
//console.log(msgJson.playerArr);
volleyball.process(rooms.get(msgJson.roomId).game, msgJson.playerArr);
}
let changed = false; // To see if the player already exists in the arena
for (let i = 0; i < rooms.get(msgJson.roomId).playerArr.length; i++) {
// Update existing player positions
if (rooms.get(msgJson.roomId).playerArr[i].playerId == msgJson.playerArr[0].playerId) {
//console.log(msgJson.name);
let newRoom = rooms.get(msgJson.roomId);
newRoom.playerArr[i] = msgJson.playerArr[0];
newRoom.playerArr[i].colour = msgJson.colour;
newRoom.playerArr[i].name = msgJson.name;
rooms.set(msgJson.roomId, newRoom);
changed = true;
}
}
// Create new player positions
if(!changed) {
let newRoom = rooms.get(msgJson.roomId);
newRoom.playerArr.push(msgJson.playerArr[0]);
newRoom.playerArr[newRoom.playerArr.length-1].session = ws.sessionIdContext; // Required for handling socket closing
newRoom.playerArr[newRoom.playerArr.length-1].colour = msgJson.colour;
newRoom.playerArr[newRoom.playerArr.length-1].name = msgJson.name;
rooms.set(msgJson.roomId, newRoom);
}
} else {
msgJson.game.data = {};
rooms.set(msgJson.roomId, msgJson); // Create the room
}
ws.send(JSON.stringify(rooms.get(msgJson.roomId))); // Send the updated room data
}); });
// On websocket close
ws.onclose = function (event) { ws.onclose = function (event) {
console.log("Client disconnected from: " + req.params.id); console.log("Client disconnected");
// Go through each existing room entry to find the websocket with the session that was closed with
for (const [key, value] of rooms.entries()) {
for (let i = 0; i < value.playerArr.length; i++){
if (value.playerArr[i].session == ws.sessionIdContext) {
newRoom = value;
newRoom.playerArr.splice(i); // Remove player with index i
rooms.set(key, newRoom);
}
// TODO: Add code to remove the room from both DB and Map if
}
}
}; };
console.log("Client connected to: " + req.params.id); });
});
// Host both an http and https server
httpServer.listen(4001);
httpsServer.listen(4000);

6295
API/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -13,7 +13,10 @@
"cors": "^2.8.5", "cors": "^2.8.5",
"express": "^4.17.1", "express": "^4.17.1",
"express-ws": "^4.0.0", "express-ws": "^4.0.0",
"line-circle-collision": "^1.1.3",
"mongoose": "^5.11.17", "mongoose": "^5.11.17",
"node-p5": "^1.0.3",
"p5": "^1.2.0",
"socket.io": "^3.1.1" "socket.io": "^3.1.1"
}, },
"devDependencies": { "devDependencies": {

View File

@@ -5,6 +5,15 @@ const Schema = mongoose.Schema;
let Player = new Schema({ let Player = new Schema({
name: { name: {
type: String type: String
},
session: {
type: String
},
colour: {
type: Number
},
score: {
type: Number
} }
}, { }, {
collection: 'players' collection: 'players'

View File

@@ -8,6 +8,9 @@ let Room = new Schema({
}, },
members: { members: {
type: Array type: Array
},
game: {
type: String
} }
}, { }, {
collection: 'rooms' collection: 'rooms'

View File

@@ -8,12 +8,15 @@ let Player = require('./player.model');
postRoutes.route('/add').get(function (req, res) { postRoutes.route('/add').get(function (req, res) {
let r = new Room(); let r = new Room();
let p = new Player(); let p = new Player();
p.colour = Math.trunc(Math.random()*8);
p.score = 0;
r.members.push(p); r.members.push(p);
r.save() r.save()
.then(() => { .then(() => {
let ret = {}; let ret = {};
ret._id = r._id; ret._id = r._id;
ret.playerId = p._id; ret.playerId = p._id;
ret.colour = p.colour;
res.send(ret); res.send(ret);
console.log("Created Room"); console.log("Created Room");
}) })
@@ -31,39 +34,26 @@ postRoutes.route('/join').post(function (req, res) {
res.json(err); res.json(err);
console.log("Error Joining Room"); console.log("Error Joining Room");
} else { } else {
let p = new Player(); if (r) {
r.members.push(p); let p = new Player();
r.save().then(() => { p.colour = Math.trunc(Math.random()*8);
res.send(p); p.score = 0;
console.log("Joined Room"); r.members.push(p);
console.log(r.members); r.save().then(() => {
}).catch(() => { res.send(p);
console.log("Unable to save to db"); console.log(p);
res.status(400).send("Unable to save to the database") console.log("Joined Room");
}) console.log(r.members);
}).catch(() => {
console.log("Unable to save to db");
res.status(400).send("Unable to save to the database")
})
} else {
res.send(null);
}
} }
}) })
}); });
// Defined get data(index or listing) route
postRoutes.route('/').get(function (req, res) {
Post.find(function (err, posts) {
if (err) {
res.json(err);
}
else {
res.json(posts);
}
});
});
// Defined delete | remove | destroy route
postRoutes.route('/delete/:id').delete(function (req, res) {
Post.findByIdAndRemove({ _id: req.params.id }, function (err) {
if (err) res.json(err);
else res.json('Successfully removed');
});
});
module.exports = postRoutes; module.exports = postRoutes;

View File

@@ -1,4 +1,4 @@
# stick-figure-game # posenet-test
## Project setup ## Project setup
``` ```

22217
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,5 +1,5 @@
{ {
"name": "stick-figure-game", "name": "posenet-test",
"version": "0.1.0", "version": "0.1.0",
"private": true, "private": true,
"scripts": { "scripts": {
@@ -12,13 +12,14 @@
"@tensorflow/tfjs": "^2.8.6", "@tensorflow/tfjs": "^2.8.6",
"@tensorflow/tfjs-converter": "^1.7.4", "@tensorflow/tfjs-converter": "^1.7.4",
"axios": "^0.21.1", "axios": "^0.21.1",
"bootstrap": "^4.6.0",
"core-js": "^3.6.5", "core-js": "^3.6.5",
"socket.io": "^3.1.1", "node-p5": "^1.0.3",
"socket.io-client": "^3.1.1", "p5": "^1.2.0",
"vue": "^2.6.11", "vue": "^2.6.11",
"vue-axios": "^3.2.4", "vue-axios": "^3.2.4",
"vue-router": "^3.5.1", "vue-router": "^3.5.1",
"vue-socket.io": "^3.0.10" "vue-window-size": "^0.6.2"
}, },
"devDependencies": { "devDependencies": {
"@tailwindcss/postcss7-compat": "^2.0.3", "@tailwindcss/postcss7-compat": "^2.0.3",
@@ -30,10 +31,10 @@
"eslint": "^6.7.2", "eslint": "^6.7.2",
"eslint-plugin-vue": "^6.2.2", "eslint-plugin-vue": "^6.2.2",
"postcss": "^7.0.35", "postcss": "^7.0.35",
"tailwindcss": "npm:@tailwindcss/postcss7-compat@^2.0.3",
"vue-template-compiler": "^2.6.11",
"sass": "^1.22.3", "sass": "^1.22.3",
"sass-loader": "^7.1.0" "sass-loader": "^7.1.0",
"tailwindcss": "npm:@tailwindcss/postcss7-compat@^2.0.3",
"vue-template-compiler": "^2.6.11"
}, },
"eslintConfig": { "eslintConfig": {
"root": true, "root": true,

View File

@@ -1,6 +0,0 @@
module.exports = {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 KiB

View File

@@ -5,12 +5,11 @@
<meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0"> <meta name="viewport" content="width=device-width,initial-scale=1.0">
<link rel="icon" href="<%= BASE_URL %>favicon.ico"> <link rel="icon" href="<%= BASE_URL %>favicon.ico">
<script src="https://cdn.socket.io/socket.io-3.0.1.min.js"></script> <title>ChillSpace</title>
<title><%= htmlWebpackPlugin.options.title %></title>
</head> </head>
<body> <body>
<noscript> <noscript>
<strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong> <strong>We're sorry but Party With Friends doesn't work without JavaScript enabled.</strong>
</noscript> </noscript>
<div id="app"></div> <div id="app"></div>
<!-- built files will be auto injected --> <!-- built files will be auto injected -->

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

23
server.cert Normal file
View File

@@ -0,0 +1,23 @@
-----BEGIN CERTIFICATE-----
MIIDvzCCAqegAwIBAgIUAimJ6DpDaSAz2e2O284vuyav6lMwDQYJKoZIhvcNAQEL
BQAwbzELMAkGA1UEBhMCQ0ExEDAOBgNVBAgMB09udGFyaW8xEDAOBgNVBAcMB1Rv
cm9udG8xDDAKBgNVBAoMA04vQTEMMAoGA1UECwwDTi9BMQwwCgYDVQQDDANOL0Ex
EjAQBgkqhkiG9w0BCQEWA04vQTAeFw0yMTAyMjAxMDMyNDBaFw0yMTAzMjIxMDMy
NDBaMG8xCzAJBgNVBAYTAkNBMRAwDgYDVQQIDAdPbnRhcmlvMRAwDgYDVQQHDAdU
b3JvbnRvMQwwCgYDVQQKDANOL0ExDDAKBgNVBAsMA04vQTEMMAoGA1UEAwwDTi9B
MRIwEAYJKoZIhvcNAQkBFgNOL0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK
AoIBAQDg1EzFmnZBdB9gclrdpcyTM2OQlOyur5LHvZ56eiIqQhnkqC3fTtEMvylM
34P9q/zWqJUlbPbxhIl8K3QNKGxX5PIQiZqoG2yixZO7ixxIH6E0kzeM8arkWTHo
taFq9y0zg2RBBDwa5l20+YZWfWE4ph6tqnp81vpcGheN++q6hUmRiyfIigrGoQpI
YXD9L5VPV1r6MIdMY8xWR7lgVH/IvNv7zv8H/lPw7POlvHYPZ9Csg7FrqU1fYubG
9pK9Z3PxHU/IWkrLp4JE58rOTIVrVEqfktzSHrft8FZjUu+Lefk/V5AjM4br0WEc
M0szXWo5NK7QXzC1bYgTdwEsSmcLAgMBAAGjUzBRMB0GA1UdDgQWBBRuN1ro+DPR
3rk/X/P6Fklbwbr3DDAfBgNVHSMEGDAWgBRuN1ro+DPR3rk/X/P6Fklbwbr3DDAP
BgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQCKGJZ3DqHLEUZeQ5VO
LHpgP97Yq3wrdgbR45jeufu0BtGVXzc9nT+9d0Dm0C5IdVtsXQ5fYXBQzY5xzyj0
OltoHXcEDr9P0E9QponFV1PQuajEYfzu7gSXaGhbGEKknaY0VOmdMfRa9cIW6sGP
uo0zuoXglqWj7287WPgdbcVk76rOfWCKJqISagh/f/C70zL0LBgcSFF1CcFq9uc3
korhjtduCJutyQHlvMHhlWBdjFpVmB7ndGPTcirIsb8qAAD+BxBWsQnhgUyj8H+z
IbaIwSh6dlkAfvn4129OlzidY4Q+nDaK3knfgouLuSH7d/cCZsGrq54o9KJjVKw0
eMey
-----END CERTIFICATE-----

28
server.key Normal file
View File

@@ -0,0 +1,28 @@
-----BEGIN PRIVATE KEY-----
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDg1EzFmnZBdB9g
clrdpcyTM2OQlOyur5LHvZ56eiIqQhnkqC3fTtEMvylM34P9q/zWqJUlbPbxhIl8
K3QNKGxX5PIQiZqoG2yixZO7ixxIH6E0kzeM8arkWTHotaFq9y0zg2RBBDwa5l20
+YZWfWE4ph6tqnp81vpcGheN++q6hUmRiyfIigrGoQpIYXD9L5VPV1r6MIdMY8xW
R7lgVH/IvNv7zv8H/lPw7POlvHYPZ9Csg7FrqU1fYubG9pK9Z3PxHU/IWkrLp4JE
58rOTIVrVEqfktzSHrft8FZjUu+Lefk/V5AjM4br0WEcM0szXWo5NK7QXzC1bYgT
dwEsSmcLAgMBAAECggEAbZYNdaO8UFsRkCktMSxHcthxh1+PnfXmTYWXYYt2a7Kc
fF+dPGNmchgbQWURKOxT6S1yxTM/iqlXejaT8nXpmU32zyjDQX1cjlpPj/JWbrE8
GixHv2NsMLFpKkydyU2BssIwc6Bky5fNxRB68l1argoKmgumeIMwtQPix0orN44m
FMi+Tn9k8BV4P9U4mvbixAmXNWeTkqPp6exYNS5UB3pLliPDl25vkiyMKm4BZi5P
haUkOQxNhJgtrutQUiMvk0/TEKieXMlBP7BtoD2QJUnzBwimd00hxdv69Vtbdvjl
K3PjVcRpsfhyVIGXsiVJkiv10LtuhaInic+HGIDmgQKBgQD7VI+TZAWXzHCUkqtI
eFa5p9Q+v89czOIRokN0n6e/xmWIGoW1Tcdp4evvq4fwAaG5IsL2QEtyr+5kqXDX
yL/84/IQ3cDuEb5bDRj4JDITSFwD+uAs7WsSHnxalU9QsWQoYwYMUDMeXuzsNt40
utcdisJklSnLWtapIkAIDP+LUwKBgQDlAbA5GlulcBKESO71SrOQG1P2N7JS/8yj
1RzOdBKM7ecbVF90+j8QhWLdMptDytfOKXuq1dY0TpmcaJb7iX6cohs7ENgzM8qF
99/l9PX2gWPq9Hz7KVZKrtaEezHWvTMlY8dtTr16xWwVZKC03LEpKuhy56kPb1s0
gBBvN3Z2aQKBgQCASU1wAmIIdcYRUDw1pZc+9LeVv/psd/f84EJmSQgrD23L5x3Q
yX68QSFZGMkubObLxT6Wy4K3a63Xm9WJj2LQBtoMWeScoFn1x92y62bUCN9O+MNs
q1M30G/RHN17ZtCk/MadimJNYk6009zgNW6QGb/X73uB3UAs3NqqQVg/GQKBgBfK
8h0ssOLiXvohfbo8daV+QV3ucXeQHhnTdXe5tYew2/cJ8BT+PvkfcqMas+j6NSu1
QaUBLI0osWr/rtgZc+8gJIYhfOTs95itpTSGG9vtm4z4s9eAdvexbJY9GnN+GsdC
s6CWsrcDtfQPDWddGob1b0so6HazEh3FRG/ZqlQpAoGADL6Dew4qokh4t+U3ocJu
1Aih0EcZEYHmd4jfp5up+4G0B7LV2/vgvstJJCbkQ8zuySzIVLVLOo068m4IgsJr
gNoMJtzWosraONM9pPaE/xylzSWD+m+JNWimRgAqsWwOA9VhIyVzK7HKueDkWGYp
oXbdgzI/46r7UoIjrXrduI4=
-----END PRIVATE KEY-----

View File

@@ -1,5 +1,5 @@
<template> <template>
<div> <div class="app">
<router-view></router-view> <router-view></router-view>
</div> </div>
</template> </template>
@@ -13,8 +13,8 @@ export default {
</script> </script>
<style> <style>
@tailwind base; .app{
@tailwind components; background-color: #f2f2f2;
@tailwind utilities; height: 100vh;
}
</style> </style>

BIN
src/assets/dance.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

BIN
src/assets/logo.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 107 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.7 KiB

0
src/assets/reset.css Normal file
View File

BIN
src/assets/soccer.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

BIN
src/assets/volleyball.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

View File

@@ -1,52 +1,200 @@
<template> <template>
<div> <div>
<button v-on:click="sendMessage()">Send Message</button> <!-- The Modal -->
<video ref="video" autoplay></video> <div id="myModal" class="modal">
<canvas ref="canvas"></canvas> <!-- Modal content -->
<div class="modal-content">
<p>Loading video...</p>
</div>
</div>
<div>
<br />
<br />
<center><h2>Your Room </h2>
<h4 class="text-muted">Code: {{message.roomId}}</h4></center>
<div id="canvas-div" style="text-align: center">
<canvas
ref="canvas"
class="top"
id="2d"
v-bind:width="windowWidth - 30"
height="500"
style="
background-color: white;
border-style: solid;
border-width: 0.5px;
border-radius: 15px;
height: 50%;
background: url('https://i.ibb.co/X3sHrcM/Pngtree-summer-sandy-beach-background-986369.png');
">
</canvas>
</div>
<center>
<div class="container">
<div class="row">
<div class="col">
<div class="card" style="width: 18rem">
<div class="card-body">
<h5 class="card-title">Your Camera</h5>
<h6 class="card-subtitle mb-2 text-muted">
(Only you can see this)
</h6>
<div class="card-text">
<div id="video" style="display: inline-block; width: 100%">
<video
ref="video"
autoplay
class="top"
style="display: inline-block; height: 100%; width: 100%"
></video>
</div>
</div>
</div>
</div>
</div>
<div class="col">
<div class="card" style="width: 18rem">
<div class="card-body">
<h5 class="card-title">Settings:</h5>
<h6 class="card-subtitle mb-2 text-muted">Edit your player:</h6>
<div class="card-text">
<label for="customRange3" class="form-label"><b>Colour</b></label>
<input
type="range"
class="form-range"
min="1"
max="8"
step="1"
v-model="colorSelect"
@change="updateColor"
id="customRange3"
style="width: 100%;"
/>
<br />
<input
type="text"
v-model="username"
placeholder="Enter a username"
@change="updateUsername"
/>
<br />
Color: {{ colorSelect }}
<br />
Username: {{ username }}
<br />
Video Loaded: {{ videoLoaded }}
<br>
</div>
</div>
</div>
</div>
</div>
</div>
</center>
<br />
</div>
</div> </div>
</template> </template>
<script> <script>
import * as posenet from "@tensorflow-models/posenet"; import * as posenet from "@tensorflow-models/posenet";
import * as utils from "../utils"; import * as utils from "../utils";
import { useWindowSize } from "vue-window-size";
export default { export default {
setup() {
const { width, height } = useWindowSize();
return {
windowWidth: width,
windowHeight: height,
};
},
data() { data() {
return { return {
connection: null, connection: null,
message: {}, message: {},
people: {},
resultWidth: 0,
resultHeight: 0,
colorSelect: 1,
username: "",
videoLoaded: false,
windowWidth: window.innerWidth,
modal: {},
span: {},
}; };
}, },
created() { created() {
console.log("Starting connection to websocket server"); console.log("Starting connection to websocket server");
console.log(this.$route.params.id); //console.log(this.$route.params.id);
this.connection = new WebSocket( this.connection = new WebSocket("wss://dance.cubehostingmc.com:4000/");
"ws://50.100.180.37:4000/" + this.$route.params.id
);
this.connection.onopen = function (event) { this.connection.onopen = (event) => {
console.log(event); console.log(event);
console.log("Successfully connected to the echo WebSocket Server"); console.log("Successfully connected to the echo WebSocket Server");
}; };
this.connection.onmessage = function (event) {
console.log(event); // When we recieve a message
};
}, },
async mounted() { async mounted() {
this.modal = document.getElementById("myModal");
this.span = document.getElementsByClassName("close")[0];
this.modal.style.display = "block";
window.addEventListener("resize", () => {
this.windowWidth = window.innerWidth;
});
this.ctx = this.$refs.canvas.getContext("2d"); this.ctx = this.$refs.canvas.getContext("2d");
this.net = await posenet.load(); this.net = await posenet.load({
architecture: "ResNet50",
outputStride: 32,
inputResolution: 250,
multiplier: 1,
quantBytes: 2,
});
this.streamPromise = await this.initWebcamStream(); this.streamPromise = await this.initWebcamStream();
this.detectPose(); this.connection.onmessage = (event) => {
let data = JSON.parse(event.data);
this.game = data.game;
this.playerArr = data.playerArr; // When we recieve a message
//utils.assignAllUniversePairs(this.playerArr, this.ctx)
//let players = this.playerArr;
let numPlayers = this.playerArr.length;
let width = this.ctx.canvas.width;
let x_spacing = width / (numPlayers + 1);
for (let i = 0; i < numPlayers; i++) {
let x = (i + 1) * x_spacing;
let y = this.ctx.canvas.height / 2;
let com = utils.getCenterOfMass(this.playerArr[i].pose.keypoints);
this.playerArr[i].universePairs = utils.keypointsToUniverse(this.playerArr[i].pose.keypoints, com, [x, y])
this.playerArr[i].pose.keypoints.forEach(keypoint => {
if (keypoint.part == "nose") {
this.playerArr[i].head = utils.getUniversePoint(keypoint, com, [x, y])
}
})
}
this.message.playerArr = this.playerArr;
};
this.gameCycle();
}, },
methods: { methods: {
sendMessage() { sendMessage() {
console.log(this.connection); //console.log(this.connection);
this.message.playerId = this.getCookie("playerId"); this.message.playerId = this.getCookie("playerId");
this.message.gameId = this.$route.params.id; this.message.gameId = this.$route.params.id;
console.log(this.message); this.message.colour = this.getCookie("playerColour");
this.message.name = this.username;
if (!this.message.game) {
this.message.game = {};
}
utils.assignAllUniversePairs(this.message.playerArr, this.ctx)
//this.message.playerArr = this.playerArr;
this.message.game.name = this.getCookie("game");
//console.log(this.message);
let str = JSON.stringify(this.message); let str = JSON.stringify(this.message);
this.connection.send(str); this.connection.send(str);
}, },
getCookie(cname) { getCookie(cname) {
var name = cname + "="; var name = cname + "=";
@@ -69,7 +217,7 @@ export default {
return navigator.mediaDevices return navigator.mediaDevices
.getUserMedia({ .getUserMedia({
audio: false, // don't capture audio audio: false, // don't capture audio
video: { facingMode: "environment" }, // use the rear camera if there is video: { facingMode: "user" }, // use the rear camera if there is
}) })
.then((stream) => { .then((stream) => {
// set <video> source as the webcam input // set <video> source as the webcam input
@@ -83,6 +231,8 @@ export default {
return new Promise((resolve) => { return new Promise((resolve) => {
// when video is loaded // when video is loaded
video.onloadedmetadata = () => { video.onloadedmetadata = () => {
this.videoLoaded = true;
this.modal.style.display = "none";
// calculate the video ratio // calculate the video ratio
this.videoRatio = video.offsetHeight / video.offsetWidth; this.videoRatio = video.offsetHeight / video.offsetWidth;
// add event listener on resize to reset the <video> and <canvas> sizes // add event listener on resize to reset the <video> and <canvas> sizes
@@ -105,43 +255,119 @@ export default {
); );
} }
}, },
setResultSize () { setResultSize() {
// get the current browser window size // get the current browser window size
let clientWidth = document.documentElement.clientWidth let clientWidth = document.documentElement.clientWidth;
// set max width as 600 // set max width as 600
this.resultWidth = Math.min(600, clientWidth) this.resultWidth = Math.min(600, clientWidth);
// set the height according to the video ratio // set the height according to the video ratio
this.resultHeight = this.resultWidth * this.videoRatio this.resultHeight = this.resultWidth * this.videoRatio;
// set <video> width and height // set <video> width and height
/* /*
Doesn't use vue binding :width and :height, Doesn't use vue binding :width and :height,
because the initial value of resultWidth and resultHeight because the initial value of resultWidth and resultHeight
will affect the ratio got from the initWebcamStream() will affect the ratio got from the initWebcamStream()
*/ */
let video = this.$refs.video let video = this.$refs.video;
video.width = this.resultWidth video.width = this.resultWidth;
video.height = this.resultHeight video.height = this.resultHeight;
}, },
timeout(ms){ timeout(ms) {
return new Promise(resolve => setTimeout(resolve, ms)); return new Promise((resolve) => setTimeout(resolve, ms));
}, },
async detectPose() { async detectPose() {
const imageScaleFactor = 0.5; const imageScaleFactor = 0.5;
const flipHorizontal = false; const flipHorizontal = true;
const outputStride = 16; const outputStride = 16;
this.pose = await this.net.estimateSinglePose(this.$refs.video, imageScaleFactor, flipHorizontal, outputStride) this.pose = await this.net.estimateSinglePose(
console.log(this.pose); this.$refs.video,
utils.drawSkeleton(this.pose.keypoints, 0.3, this.ctx); imageScaleFactor,
//this.renderPose(); flipHorizontal,
// await this.timeout(1000/20); outputStride
// return this.detectPose(); );
requestAnimationFrame(() => {
this.detectPose(); let playerId = this.getCookie("playerId");
}) let roomId = this.$route.params.id;
} let send = {
roomId: roomId,
playerArr: [{ playerId: playerId, pose: this.pose }],
};
this.message = send;
this.sendMessage();
},
updateColor() {
this.setCookie("playerColour", this.colorSelect, 1);
},
updateUsername() {
this.setCookie("playerUsername", this.username, 1);
},
setCookie(cname, cvalue, exdays) {
var d = new Date();
d.setTime(d.getTime() + exdays * 24 * 60 * 60 * 1000);
var expires = "expires=" + d.toUTCString();
document.cookie = cname + "=" + cvalue + ";" + expires + ";path=/";
},
draw() {
utils.clearCanvas(this.ctx);
if (this.playerArr) {
utils.drawPlayers(this.playerArr, this.ctx);
}
if (this.game != undefined) {
utils.drawGame(this.game, this.ctx);
}
},
async gameCycle() {
this.draw();
this.detectPose();
await this.timeout(100);
return this.gameCycle();
//requestAnimationFrame(() => {
// this.gameCycle();
//})
},
getColour(colourSelect){
const colours = {0: "red", 1: "green", 2:"blue", 3:"orange", 4:"purple", 5:"pink", 6:"black", 7:"brown", 8:"aqua"}
return colours[colourSelect];
},
}, },
}; };
</script> </script>
<style> <style scoped>
.modal {
display: none;
position: fixed;
z-index: 1;
left: 0;
top: 0;
width: 100%;
height: 100%;
overflow: auto;
background-color: rgb(0, 0, 0);
background-color: rgba(0, 0, 0, 0.4);
}
.modal-content {
background-color: #fefefe;
margin: 15% auto;
padding: 20px;
border: 1px solid #888;
width: 25%;
}
.close {
color: #aaa;
float: right;
font-size: 28px;
font-weight: bold;
}
.close:hover,
.close:focus {
color: black;
text-decoration: none;
cursor: pointer;
}
</style> </style>

View File

@@ -0,0 +1,58 @@
<template>
<div class="hello">
<h1>{{ msg }}</h1>
<p>
For a guide and recipes on how to configure / customize this project,<br>
check out the
<a href="https://cli.vuejs.org" target="_blank" rel="noopener">vue-cli documentation</a>.
</p>
<h3>Installed CLI Plugins</h3>
<ul>
<li><a href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-babel" target="_blank" rel="noopener">babel</a></li>
<li><a href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-eslint" target="_blank" rel="noopener">eslint</a></li>
</ul>
<h3>Essential Links</h3>
<ul>
<li><a href="https://vuejs.org" target="_blank" rel="noopener">Core Docs</a></li>
<li><a href="https://forum.vuejs.org" target="_blank" rel="noopener">Forum</a></li>
<li><a href="https://chat.vuejs.org" target="_blank" rel="noopener">Community Chat</a></li>
<li><a href="https://twitter.com/vuejs" target="_blank" rel="noopener">Twitter</a></li>
<li><a href="https://news.vuejs.org" target="_blank" rel="noopener">News</a></li>
</ul>
<h3>Ecosystem</h3>
<ul>
<li><a href="https://router.vuejs.org" target="_blank" rel="noopener">vue-router</a></li>
<li><a href="https://vuex.vuejs.org" target="_blank" rel="noopener">vuex</a></li>
<li><a href="https://github.com/vuejs/vue-devtools#vue-devtools" target="_blank" rel="noopener">vue-devtools</a></li>
<li><a href="https://vue-loader.vuejs.org" target="_blank" rel="noopener">vue-loader</a></li>
<li><a href="https://github.com/vuejs/awesome-vue" target="_blank" rel="noopener">awesome-vue</a></li>
</ul>
</div>
</template>
<script>
export default {
name: 'HelloWorld',
props: {
msg: String
}
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
h3 {
margin: 40px 0 0;
}
ul {
list-style-type: none;
padding: 0;
}
li {
display: inline-block;
margin: 0 10px;
}
a {
color: #42b983;
}
</style>

View File

@@ -1,8 +1,23 @@
<template> <template>
<div class="hello"> <div class="cont">
Create Room: <button v-on:click="createRoom" style="border: solid">Create Room</button> <main class="form-signin">
<center><img class="mb-4" :src="logo" alt="" width="30%" height="60%" style="width:60%; height 60%"></center>
<h1 class="h3 mb-3 fw-normal text-center">Welcome to Chillspace!</h1>
<label for="inputEmail" class="visually-hidden">Create a New Game:</label>
<br> <br>
Join Room: <input type="text" v-model="room.id" style="border: solid"> <button v-on:click="joinRoom" style="border: solid">Join Room</button> <center><img :src="voleyball" @click="changeGame('0')" id="game-0" style="opacity: 0.25;"> <img :src="soccer" @click="changeGame('1')" id="game-1" style="opacity: 1;"> <img :src="dance" @click="changeGame('2')" id="game-2" style="opacity: 1;"></center>
<button v-on:click="createRoom" class="w-100 btn btn-lg btn-primary" style="margin-top:15px;">Create Room</button>
<br>
<label for="input" class="visually-hidden" style="margin-top: 15px;">Join an Existing Room:</label>
<input type="text" id="input" class="form-control" placeholder="Game ID" v-model="room.id">
<div class="checkbox mb-3">
</div>
<button v-on:click="joinRoom" class="w-100 btn btn-lg btn-primary">Join Room</button>
<p class="mt-5 mb-3 text-muted"><a href="https://github.com/alex-alexiev" class="text-secondary">Alex Alexiev</a>, <a href="https://github.com/jslightham" class="text-secondary">Johnathon Slightham</a>, and <a href="https://github.com/lcarnegie" class="text-secondary">Luca Carnegie</a></p>
</main>
</div> </div>
</template> </template>
@@ -11,23 +26,43 @@ export default {
data() { data() {
return { return {
room: {}, room: {},
logo: require('@/assets/logo.jpg'),
dance: require('@/assets/dance.jpg'),
soccer: require('@/assets/soccer.jpg'),
voleyball: require('@/assets/volleyball.jpg'),
game: 0,
}; };
}, created() {
this.setCookie("game", 0, 0);
}, },
methods: { methods: {
createRoom() { createRoom() {
this.axios.get("http://50.100.180.37:4000/rooms/add").then((res) => { this.axios.get("https://dance.cubehostingmc.com:4000/rooms/add").then((res) => {
console.log("create");
let id = res.data._id; let id = res.data._id;
let playerId = res.data.playerId; let playerId = res.data.playerId;
let playerColour = res.data.colour;
this.setCookie("playerId", playerId, 1); this.setCookie("playerId", playerId, 1);
this.setCookie("playerColour", playerColour, 1);
this.setCookie("game", this.game, 1);
console.log(res.data);
this.$router.push({path: `game/${id}`}); this.$router.push({path: `game/${id}`});
}); }).catch(e => {console.log(e)});
}, },
joinRoom() { joinRoom() {
console.log(this.room); console.log(this.room);
this.axios.post("http://50.100.180.37:4000/rooms/join", this.room).then((res) => { this.axios.post("https://dance.cubehostingmc.com:4000/rooms/join", this.room).then((res) => {
let playerId = res.data._id; if (res.data){
this.setCookie("playerId", playerId, 1); console.log(res.data);
this.$router.push({path: `game/${this.room.id}`}); let playerId = res.data._id;
let playerColour = res.data.colour;
this.setCookie("playerId", playerId, 1);
this.setCookie("playerColour", playerColour, 1);
console.log(res.data);
this.$router.push({path: `game/${this.room.id}`});
} else {
alert("Please enter a valid room!");
}
}); });
}, },
setCookie(cname, cvalue, exdays) { setCookie(cname, cvalue, exdays) {
@@ -36,9 +71,80 @@ export default {
var expires = "expires=" + d.toUTCString(); var expires = "expires=" + d.toUTCString();
document.cookie = cname + "=" + cvalue + ";" + expires + ";path=/"; document.cookie = cname + "=" + cvalue + ";" + expires + ";path=/";
}, },
changeGame(game) {
this.game = game;
this.setCookie("game", game, 1);
if (game == '0') {
document.getElementById('game-0').style.opacity = "0.25";
document.getElementById('game-1').style.opacity = "1";
document.getElementById('game-2').style.opacity = "1";
}
if (game == '1') {
document.getElementById('game-0').style.opacity = "1";
document.getElementById('game-1').style.opacity = "0.25";
document.getElementById('game-2').style.opacity = "1";
}
if (game == '2') {
document.getElementById('game-0').style.opacity = "1";
document.getElementById('game-1').style.opacity = "1";
document.getElementById('game-2').style.opacity = "0.25";
}
}
}, },
}; };
</script> </script>
<style scoped> <style scoped>
.cont {
display: flex;
align-items: center;
padding-top: 40px;
padding-bottom: 40px;
background-color: #f5f5f5;
height: 100vh;
width: 100vw;
}
.form-signin {
width: 100%;
max-width: 500px;
padding: 15px;
margin: auto;
}
.form-signin .checkbox {
font-weight: 400;
}
.form-signin .form-control {
position: relative;
box-sizing: border-box;
height: auto;
padding: 10px;
font-size: 16px;
}
.form-signin .form-control:focus {
z-index: 2;
}
.form-signin input[type="email"] {
margin-bottom: -1px;
border-bottom-right-radius: 0;
border-bottom-left-radius: 0;
}
.form-signin input[type="password"] {
margin-bottom: 10px;
border-top-left-radius: 0;
border-top-right-radius: 0;
}
.bd-placeholder-img {
font-size: 1.125rem;
text-anchor: middle;
-webkit-user-select: none;
-moz-user-select: none;
user-select: none;
}
@media (min-width: 768px) {
.bd-placeholder-img-lg {
font-size: 3.5rem;
}
}
</style> </style>

126
src/components/MyCamera.vue Normal file
View File

@@ -0,0 +1,126 @@
<template>
<div id="my-camera">
<div class="resultFrame">
<video ref="video" autoplay></video>
<canvas ref="canvas" :width="resultWidth" :height="resultHeight"></canvas>
</div>
</div>
</template>
<script>
import * as posenet from "@tensorflow-models/posenet";
import * as utils from "../utils";
export default {
name: 'app',
data () {
return {
// store the promises of initialization
streamPromise: null,
// control the UI visibilities
isVideoStreamReady: false,
isModelReady: false,
initFailMessage: '',
pose: [],
resultHeight: 0,
resultWidth: 0
}
},
methods: {
initWebcamStream () {
// if the browser supports mediaDevices.getUserMedia API
if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) {
return navigator.mediaDevices.getUserMedia({
audio: false, // don't capture audio
video: { facingMode: 'environment' } // use the rear camera if there is
})
.then(stream => {
// set <video> source as the webcam input
let video = this.$refs.video
try {
video.srcObject = stream
} catch (error) {
// support older browsers
video.src = URL.createObjectURL(stream)
}
return new Promise((resolve) => {
// when video is loaded
video.onloadedmetadata = () => {
// calculate the video ratio
this.videoRatio = video.offsetHeight / video.offsetWidth
// add event listener on resize to reset the <video> and <canvas> sizes
window.addEventListener('resize', this.setResultSize)
// set the initial size
this.setResultSize()
this.isVideoStreamReady = true
console.log('webcam stream initialized')
resolve()
}
})
})
.catch(error => {
console.log('failed to initialize webcam stream', error)
throw (error)
})
} else {
return Promise.reject(new Error('Your browser does not support mediaDevices.getUserMedia API'))
}
},
setResultSize () {
// get the current browser window size
let clientWidth = document.documentElement.clientWidth
// set max width as 600
this.resultWidth = Math.min(600, clientWidth)
// set the height according to the video ratio
this.resultHeight = this.resultWidth * this.videoRatio
// set <video> width and height
/*
Doesn't use vue binding :width and :height,
because the initial value of resultWidth and resultHeight
will affect the ratio got from the initWebcamStream()
*/
let video = this.$refs.video
video.width = this.resultWidth
video.height = this.resultHeight
},
timeout(ms){
return new Promise(resolve => setTimeout(resolve, ms));
},
async detectPose() {
const imageScaleFactor = 0.5;
const flipHorizontal = true;
const outputStride = 16;
this.pose = await this.net.estimateSinglePose(this.$refs.video, imageScaleFactor, flipHorizontal, outputStride)
console.log(this.pose);
utils.drawSkeleton(this.pose.keypoints, 0.3, this.ctx);
//this.renderPose();
// await this.timeout(1000/20);
// return this.detectPose();
requestAnimationFrame(() => {
this.detectPose();
})
}
},
async mounted () {
this.ctx = this.$refs.canvas.getContext("2d");
this.net = await posenet.load();
this.streamPromise = await this.initWebcamStream()
this.detectPose();
}
}
</script>
<style lang="scss">
body {
margin: 0;
}
.resultFrame {
display: grid;
video {
grid-area: 1 / 1 / 2 / 2;
}
canvas {
grid-area: 1 / 1 / 2 / 2;
}
}
</style>

View File

@@ -0,0 +1,48 @@
<template>
<div>
<h1>my canvas</h1>
<canvas ref="canv"></canvas>
<button @click="drawRect">Add Rect</button>
<button @click="subWidth">-</button>
<button @click="addWidth">+</button>
</div>
</template>
<script>
export default {
name: 'my-canvas',
data() {
return {
rectWidth: 200,
width: 0,
height: 0,
pose: []
}
},
async mounted () {
this.ctx = this.$refs.canv.getContext("2d");
},
methods: {
drawRect() {
this.ctx.clearRect(0, 0, 400, 200);
this.ctx.beginPath();
this.ctx.rect(20, 20, this.rectWidth, 100);
this.ctx.stroke();
},
addWidth(){
this.rectWidth += 10
this.drawRect()
},
subWidth() {
this.rectWidth -= 10
this.drawRect()
}
}
}
</script>
<style></style>

View File

@@ -1,5 +1,7 @@
import Vue from 'vue' import Vue from 'vue'
import App from './App.vue' import App from './App.vue'
import './assets/reset.css'
import 'bootstrap/dist/css/bootstrap.min.css'
import VueRouter from 'vue-router'; import VueRouter from 'vue-router';
Vue.use(VueRouter); Vue.use(VueRouter);

View File

@@ -1,44 +1,147 @@
import * as posenet from "@tensorflow-models/posenet"; import * as posenet from "@tensorflow-models/posenet";
const color = "aqua"; const lineWidth = 6;
const lineWidth = 2; const defaultConfidence = 0.5;
export const colors = { 0: "red", 1: "green", 2: "blue", 3: "orange", 4: "purple", 5: "pink", 6: "black", 7: "brown", 8: "aqua" }
function toTuple({x, y}){ export function drawGame(game, ctx) {
if (game && game.name == "0") {
try {
game.data.objects.forEach(object => {
let pos = object.position;
let r = object.radius;
drawPoint(ctx, pos.x, pos.y, r, "black");
})
} catch (e) {
console.log(e);
}
}
return;
}
function toTuple({ x, y }) {
return [x, y]; return [x, y];
} }
export function drawPoint(ctx, x, y, r, color){ export function drawPlayers(players, ctx) {
ctx.beginPath(); players.forEach(player => {
ctx.arc(x, y, r, 0, 2*Math.PI); drawPoint(ctx, player.head.x, player.head.y, 50, colors[player.colour]);
ctx.fillStyle = color; ctx.font = "24px Arial";
ctx.fill; ctx.textAlign = "center";
ctx.fillStyle = colors[player.colour];
ctx.fillText(player.name, player.head.x, player.head.y - 60);
player.universePairs.forEach(pair => {
drawSegment([pair.x1, pair.y1], [pair.x2, pair.y2], colors[player.colour], 1, ctx);
})
})
} }
export function drawSegment([ax, ay], [bx, by], color, scale, ctx){ export function assignAllUniversePairs(players, ctx) {
try{
let numPlayers = players.length;
let width = ctx.canvas.width;
let x_spacing = width / (numPlayers + 1);
for (let i = 0; i < numPlayers; i++) {
let x = (i + 1) * x_spacing;
let y = ctx.canvas.height / 2;
let com = getCenterOfMass(players[i].pose.keypoints);
players[i].universePairs = keypointsToUniverse(players[i].pose.keypoints, com, [x, y])
players[i].pose.keypoints.forEach(keypoint => {
if (keypoint.part == "nose") {
players[i].head = getUniversePoint(keypoint, com, [x, y])
}
})
}
}catch(e) {
console.log(e);
}
}
export function keypointsToUniverse(keypoints, com, [x, y]) {
let universePairs = []
let minConfidence = 0.5;
const adjacentKeyPoints = posenet.getAdjacentKeyPoints(keypoints, minConfidence);
adjacentKeyPoints.forEach(keypoint => {
universePairs.push(getUniversePair(keypoint[0], keypoint[1], com, [x, y]));
})
return universePairs;
}
export function getUniversePair(keypoint1, keypoint2, com, [x, y]) {
let p1 = getUniversePoint(keypoint1, com, [x, y]);
let p2 = getUniversePoint(keypoint2, com, [x, y]);
return { x1: p1.x, y1: p1.y, x2: p2.x, y2: p2.y }
}
export function getUniversePoint(keypoint, com, [x, y]) {
let xNew = keypoint.position.x - com[0];
let yNew = keypoint.position.y - com[1];
xNew = -xNew;// flip horizontally
xNew += x;
yNew += y;
return { x: xNew, y: yNew }
}
export function getCenterOfMass(keypoints) {
let x_sum = 0, y_sum = 0, num = 0;
keypoints.forEach(keypoint => {
if (isConfident(keypoint)) {
x_sum += keypoint.position.x;
y_sum += keypoint.position.y;
num++;
}
})
let com = [(x_sum / num), (y_sum / num)];
return com;
}
export function isConfident(keypoint) {
return keypoint.score > defaultConfidence;
}
export function clearCanvas(ctx) {
ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height)
}
export function drawPoint(ctx, x, y, r, color) {
ctx.beginPath(); ctx.beginPath();
ctx.moveTo(ax*scale, ay*scale); ctx.arc(x, y, r, 0, 2 * Math.PI);
ctx.lineTo(bx*scale, by*scale); ctx.fillStyle = color;
ctx.fill();
}
export function drawSegment([ax, ay], [bx, by], color, scale, ctx) {
ctx.beginPath();
ctx.moveTo(ax * scale, ay * scale);
ctx.lineTo(bx * scale, by * scale);
ctx.lineWidth = lineWidth; ctx.lineWidth = lineWidth;
ctx.strokeStyle = color; ctx.strokeStyle = color;
ctx.stroke(); ctx.stroke();
} }
export function drawSkeleton(keypoints, minConfidence, ctx, scale=1){ export function drawRawSkeleton(keypoints, minConfidence, ctx, color, scale = 1) {
ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height)
const adjacentKeyPoints = posenet.getAdjacentKeyPoints(keypoints, minConfidence); const adjacentKeyPoints = posenet.getAdjacentKeyPoints(keypoints, minConfidence);
adjacentKeyPoints.forEach(keypoints => { adjacentKeyPoints.forEach(keypoint => {
drawSegment(toTuple(keypoints[0].position), toTuple(keypoints[1].position), color, scale, ctx); drawSegment(toTuple(keypoint[0].position), toTuple(keypoint[1].position), color, scale, ctx);
}) })
} }
export function drawKeypoints(keypoints, minConfidence, ctx, scale = 1){ export function drawKeypoints(keypoints, minConfidence, ctx, color, scale = 1) {
keypoints.forEach(keypoints => { keypoints.forEach(keypoint => {
if (keypoints.score < minConfidence){ if (keypoint.score < minConfidence) {
return; return;
}
const { x, y } = keypoint.position;
if (keypoint.part == "nose") {
drawPoint(ctx, x * scale, y * scale, 40, color);
} else {
drawPoint(ctx, x * scale, y * scale, 3, color);
} }
const {x, y} = keypoints.position;
drawPoint(ctx, x*scale, y*scale, 3, color);
}) })
} }

View File

@@ -1,11 +0,0 @@
module.exports = {
purge: [],
darkMode: false, // or 'media' or 'class'
theme: {
extend: {},
},
variants: {
extend: {},
},
plugins: [],
}

9
vue.config.js Normal file
View File

@@ -0,0 +1,9 @@
module.exports = {
devServer: {
open: process.platform === 'darwin',
host: '0.0.0.0',
port: 8080, // CHANGE YOUR PORT HERE!
https: true,
hotOnly: false,
},
}