const express = require('express'); const { Bundle, Client } = require('node-osc'); const path = require('path'); const bodyParser = require('body-parser'); const app = express(); const oscClient = new Client('127.0.0.1', 3333); let alive = []; let fseq; function getHypotenuse(touch1, touch2) { var x = Math.abs(touch1.x - touch2.x); var y = Math.abs(touch1.y - touch2.y); return Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2)); } const server = require('http').Server(app); app.use(bodyParser.json()); app.use(bodyParser.urlencoded({extended:true})); app.use(express.static('./frontend/assets')); app.get('/', function (req, res) { res.sendFile('./frontend/index.html', { root: path.resolve(__dirname + '/..') }) }); app.post('/json', function (req, res) { if (req.body.debug) { //console.log(req.body); } let oscBundle; if (req.body.event === 'touchend') { fseq = fseq ? fseq + 1 : 1; const aliveMessage = [ '/tuio/2Dcur', 'alive' ].concat(alive); oscBundle = new Bundle( [ '/tuio/2Dcur', 'source', `tangibles${req.body.section.toString()}@127.0.0.1` ], aliveMessage, [ '/tuio/2Dcur', 'fseq', fseq ], [ '/tuio/2Dcur', 'del', req.body.changedTouches[0].identifier ] ); oscClient.send(oscBundle, () => { const index = alive.indexOf(req.body.changedTouches[0].identifier); alive.splice(index, 1); if (alive.length === 0) { oscBundle = new Bundle( [ '/tuio/2Dcur', 'source', `tangibles${req.body.section.toString()}@127.0.0.1` ], [ '/tuio/2Dcur', 'alive' ], [ '/tuio/2Dcur', 'fseq', fseq ] ); oscClient.send(oscBundle, () => { res.status(200).json(JSON.stringify(req.body)); fseq = 0; }); } else { res.status(200).send(); } }); } else { if (req.body.changedTouches && req.body.changedTouches.length && req.body.changedTouches.length > 0) { fseq = fseq ? fseq + 1 : 1; const touches = Object.keys(req.body.changedTouches); const aliveMessage = [ '/tuio/2Dcur', 'alive' ].concat(alive); touches.forEach(touch => { const id = req.body.changedTouches[touch].identifier; if (!alive.includes(id)) { alive.push(id); aliveMessage.push(id); } }); /* Listage de tous les points */ const dots = []; touches.forEach(function(touch) { dots.push({ id: req.body.changedTouches[touch].identifier, x: req.body.changedTouches[touch].clientX, y: req.body.changedTouches[touch].clientY }); }); // console.log(dots, dots.length); /* Listage des segments */ const segments = []; if (dots.length > 2) { for (var i = 0; i < dots.length; i++) { for (var j = 0; j < dots.length; j++) { if (j !== i) { /* on vérifie que le segment n'est pas déjà listé */ const alreadyExists = segments.find(segment => { return segment.identifiers.includes(i) && segment.identifiers.includes(j); }); /* on calcule la taille du segment (l'hypoténuse) */ var hyp = getHypotenuse(dots[i], dots[j]); /* on garde uniquement les segments inférieurs à 550px * cette valeur devra être une variable de configuration */ if (!alreadyExists && hyp <= 550) { segments.push({ identifiers: [i, j], x1: dots[i].x, x2: dots[j].x, y1: dots[i].y, y2: dots[j].y, hyp }); } } } } } // console.log(segments, segments.length); /* Listage des triangles */ var triangles = []; /* on boucle sur les segments */ segments.forEach((segment) => { const dot1 = segment.identifiers[0]; const dot2 = segment.identifiers[1]; /* on vérifie que le triangle n'est pas déjà listé */ const alreadyExists = triangles.find(triangle => { return triangle.includes(dot1) && triangle.includes(dot2); }); if (!alreadyExists) { /* on cherche les segments qui contiennent un des 2 points du segment actuel * ex: si le segment actuel est AB, on cherche un segment contenant A (pour AC) et un autre contenant B (pour BC) */ const found1 = segments.findIndex(seg => { return (seg.identifiers.includes(dot1) && !seg.identifiers.includes(dot2)); }); const found2 = segments.findIndex(seg => { return (seg.identifiers.includes(dot2) && !seg.identifiers.includes(dot1)); }); /* si on trouve bien les 2 segments (AC et BC), on peut créer un triangle */ if (found1 !== -1 && found2 !== -1) { /* on devine quel est le 3ème point du triangle par rapport au segment actuel (le point C par rapport au segment AB) */ const dot3 = segments[found1].identifiers.find(identifier => { return identifier !== dot1 && identifier !== dot2; }); triangles.push([dot1, dot2, dot3]); } } }); // console.log(triangles, triangles.length); /* Définition de l'apex */ triangles.forEach(triangle => { /* on récupère les 3 côtés (segments) du triangle */ const segment1 = segments.find(segment => { return segment.identifiers.includes(triangle[0]) && segment.identifiers.includes(triangle[1]); }); const segment2 = segments.find(segment => { return segment.identifiers.includes(triangle[1]) && segment.identifiers.includes(triangle[2]); }); const segment3 = segments.find(segment => { return segment.identifiers.includes(triangle[0]) && segment.identifiers.includes(triangle[2]); }); /* on trouve quel côté est le plus court * NOTE: dans notre cas, l'apex sera toujours le point opposé au plus petit côté * cette méthode n'est pas généralisable à d'autres projets */ const smallestSegment = [segment1, segment2, segment3].reduce(function(prev, current) { return (prev.hyp < current.hyp) ? prev : current }); /* on déduit quel point du triangle est l'apex */ const apexDot = triangle.find(dot => { return dot !== smallestSegment.identifiers[0] && dot !== smallestSegment.identifiers[1]; }); console.log(apexDot); /* Reste à faire: * trouver la valeur de l'angle de l'apex (théorème des cosinus) * trouver le "centre" du triangle (via les orthocentres ou médianes??) * déduire l'orientation du triangle */ }); oscBundle = new Bundle( [ '/tuio/2Dcur', 'source', `tangibles${req.body.section.toString()}@127.0.0.1` ], aliveMessage, [ '/tuio/2Dcur', 'fseq', fseq ] ); touches.forEach(touch => { oscBundle.append( [ '/tuio/2Dcur', 'set', req.body.changedTouches[touch].identifier, req.body.changedTouches[touch].clientX / req.body.screenW, req.body.changedTouches[touch].clientY / req.body.screenH, 0.0, 0.0 ] ); }); oscClient.send(oscBundle, () => { res.status(200).json(JSON.stringify(req.body)); }); } else { res.status(400).send(); } } }); server.listen(5001, function () { console.log('Votre app est disponible sur localhost:5001 !') });