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 webSockets = express();
const bodyParser = require('body-parser');
const PORT = 4000;
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 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.connect(config.DB, { useNewUrlParser: true }).then(
() => { console.log('Database is connected') },
err => { console.log('Can not connect to the database' + err) }
);
// Express config
app.use(cors());
app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());
app.use('/rooms', roomRoute);
var httpServer = http.createServer(app);
var httpsServer = https.createServer(credentials, app);
app.listen(PORT, function () {
console.log('Express server running on port:', PORT);
})
//games
//import * as volleyball from 'games/volleyball.mjs';
app.ws('/:id', function (ws, req) {
var volleyball = require('./games/volleyball');
ws.on('message', function (msg) {
msgJ = JSON.parse(msg);
console.log(req.params.id);
console.log(msgJ.playerId);
ws.send(msg);
// Websocket config
const wss = new WebSocket.Server({ server: httpsServer }); // Use SSL Server
// On WebSocket Connection
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) {
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",
"express": "^4.17.1",
"express-ws": "^4.0.0",
"line-circle-collision": "^1.1.3",
"mongoose": "^5.11.17",
"node-p5": "^1.0.3",
"p5": "^1.2.0",
"socket.io": "^3.1.1"
},
"devDependencies": {

View File

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

View File

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

View File

@@ -8,12 +8,15 @@ let Player = require('./player.model');
postRoutes.route('/add').get(function (req, res) {
let r = new Room();
let p = new Player();
p.colour = Math.trunc(Math.random()*8);
p.score = 0;
r.members.push(p);
r.save()
.then(() => {
let ret = {};
ret._id = r._id;
ret.playerId = p._id;
ret.colour = p.colour;
res.send(ret);
console.log("Created Room");
})
@@ -31,39 +34,26 @@ postRoutes.route('/join').post(function (req, res) {
res.json(err);
console.log("Error Joining Room");
} else {
let p = new Player();
r.members.push(p);
r.save().then(() => {
res.send(p);
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")
})
if (r) {
let p = new Player();
p.colour = Math.trunc(Math.random()*8);
p.score = 0;
r.members.push(p);
r.save().then(() => {
res.send(p);
console.log(p);
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;

View File

@@ -1,4 +1,4 @@
# stick-figure-game
# posenet-test
## 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",
"private": true,
"scripts": {
@@ -12,13 +12,14 @@
"@tensorflow/tfjs": "^2.8.6",
"@tensorflow/tfjs-converter": "^1.7.4",
"axios": "^0.21.1",
"bootstrap": "^4.6.0",
"core-js": "^3.6.5",
"socket.io": "^3.1.1",
"socket.io-client": "^3.1.1",
"node-p5": "^1.0.3",
"p5": "^1.2.0",
"vue": "^2.6.11",
"vue-axios": "^3.2.4",
"vue-router": "^3.5.1",
"vue-socket.io": "^3.0.10"
"vue-window-size": "^0.6.2"
},
"devDependencies": {
"@tailwindcss/postcss7-compat": "^2.0.3",
@@ -30,10 +31,10 @@
"eslint": "^6.7.2",
"eslint-plugin-vue": "^6.2.2",
"postcss": "^7.0.35",
"tailwindcss": "npm:@tailwindcss/postcss7-compat@^2.0.3",
"vue-template-compiler": "^2.6.11",
"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": {
"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 name="viewport" content="width=device-width,initial-scale=1.0">
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
<script src="https://cdn.socket.io/socket.io-3.0.1.min.js"></script>
<title><%= htmlWebpackPlugin.options.title %></title>
<title>ChillSpace</title>
</head>
<body>
<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>
<div id="app"></div>
<!-- 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>
<div>
<div class="app">
<router-view></router-view>
</div>
</template>
@@ -13,8 +13,8 @@ export default {
</script>
<style>
@tailwind base;
@tailwind components;
@tailwind utilities;
.app{
background-color: #f2f2f2;
height: 100vh;
}
</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>
<div>
<button v-on:click="sendMessage()">Send Message</button>
<video ref="video" autoplay></video>
<canvas ref="canvas"></canvas>
<!-- The Modal -->
<div id="myModal" class="modal">
<!-- 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>
</template>
<script>
import * as posenet from "@tensorflow-models/posenet";
import * as utils from "../utils";
import { useWindowSize } from "vue-window-size";
export default {
setup() {
const { width, height } = useWindowSize();
return {
windowWidth: width,
windowHeight: height,
};
},
data() {
return {
connection: null,
message: {},
people: {},
resultWidth: 0,
resultHeight: 0,
colorSelect: 1,
username: "",
videoLoaded: false,
windowWidth: window.innerWidth,
modal: {},
span: {},
};
},
created() {
console.log("Starting connection to websocket server");
console.log(this.$route.params.id);
this.connection = new WebSocket(
"ws://50.100.180.37:4000/" + this.$route.params.id
);
//console.log(this.$route.params.id);
this.connection = new WebSocket("wss://dance.cubehostingmc.com:4000/");
this.connection.onopen = function (event) {
this.connection.onopen = (event) => {
console.log(event);
console.log("Successfully connected to the echo WebSocket Server");
};
this.connection.onmessage = function (event) {
console.log(event); // When we recieve a message
};
},
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.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.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: {
sendMessage() {
console.log(this.connection);
//console.log(this.connection);
this.message.playerId = this.getCookie("playerId");
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);
this.connection.send(str);
},
getCookie(cname) {
var name = cname + "=";
@@ -69,7 +217,7 @@ export default {
return navigator.mediaDevices
.getUserMedia({
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) => {
// set <video> source as the webcam input
@@ -83,6 +231,8 @@ export default {
return new Promise((resolve) => {
// when video is loaded
video.onloadedmetadata = () => {
this.videoLoaded = true;
this.modal.style.display = "none";
// calculate the video ratio
this.videoRatio = video.offsetHeight / video.offsetWidth;
// 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
let clientWidth = document.documentElement.clientWidth
let clientWidth = document.documentElement.clientWidth;
// 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
this.resultHeight = this.resultWidth * this.videoRatio
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
let video = this.$refs.video;
video.width = this.resultWidth;
video.height = this.resultHeight;
},
timeout(ms){
return new Promise(resolve => setTimeout(resolve, ms));
timeout(ms) {
return new Promise((resolve) => setTimeout(resolve, ms));
},
async detectPose() {
const imageScaleFactor = 0.5;
const flipHorizontal = false;
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();
})
}
const imageScaleFactor = 0.5;
const flipHorizontal = true;
const outputStride = 16;
this.pose = await this.net.estimateSinglePose(
this.$refs.video,
imageScaleFactor,
flipHorizontal,
outputStride
);
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>
<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>

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>
<div class="hello">
Create Room: <button v-on:click="createRoom" style="border: solid">Create Room</button>
<div class="cont">
<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>
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>
</template>
@@ -11,23 +26,43 @@ export default {
data() {
return {
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: {
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 playerId = res.data.playerId;
let playerColour = res.data.colour;
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}`});
});
}).catch(e => {console.log(e)});
},
joinRoom() {
console.log(this.room);
this.axios.post("http://50.100.180.37:4000/rooms/join", this.room).then((res) => {
let playerId = res.data._id;
this.setCookie("playerId", playerId, 1);
this.$router.push({path: `game/${this.room.id}`});
this.axios.post("https://dance.cubehostingmc.com:4000/rooms/join", this.room).then((res) => {
if (res.data){
console.log(res.data);
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) {
@@ -36,9 +71,80 @@ export default {
var expires = "expires=" + d.toUTCString();
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>
<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>

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 App from './App.vue'
import './assets/reset.css'
import 'bootstrap/dist/css/bootstrap.min.css'
import VueRouter from 'vue-router';
Vue.use(VueRouter);

View File

@@ -1,44 +1,147 @@
import * as posenet from "@tensorflow-models/posenet";
const color = "aqua";
const lineWidth = 2;
const lineWidth = 6;
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];
}
export function drawPoint(ctx, x, y, r, color){
ctx.beginPath();
ctx.arc(x, y, r, 0, 2*Math.PI);
ctx.fillStyle = color;
ctx.fill;
export function drawPlayers(players, ctx) {
players.forEach(player => {
drawPoint(ctx, player.head.x, player.head.y, 50, colors[player.colour]);
ctx.font = "24px Arial";
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.moveTo(ax*scale, ay*scale);
ctx.lineTo(bx*scale, by*scale);
ctx.arc(x, y, r, 0, 2 * Math.PI);
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.strokeStyle = color;
ctx.stroke();
}
export function drawSkeleton(keypoints, minConfidence, ctx, scale=1){
ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height)
export function drawRawSkeleton(keypoints, minConfidence, ctx, color, scale = 1) {
const adjacentKeyPoints = posenet.getAdjacentKeyPoints(keypoints, minConfidence);
adjacentKeyPoints.forEach(keypoints => {
drawSegment(toTuple(keypoints[0].position), toTuple(keypoints[1].position), color, scale, ctx);
adjacentKeyPoints.forEach(keypoint => {
drawSegment(toTuple(keypoint[0].position), toTuple(keypoint[1].position), color, scale, ctx);
})
}
export function drawKeypoints(keypoints, minConfidence, ctx, scale = 1){
keypoints.forEach(keypoints => {
if (keypoints.score < minConfidence){
return;
export function drawKeypoints(keypoints, minConfidence, ctx, color, scale = 1) {
keypoints.forEach(keypoint => {
if (keypoint.score < minConfidence) {
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,
},
}