server.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429
  1. const express = require('express');
  2. const dgram = require('dgram');
  3. const oscParser = require('./osc-parser');
  4. const bodyParser = require('body-parser');
  5. const { Bundle, Client } = require('node-osc');
  6. const path = require('path');
  7. const app = express();
  8. app.use(bodyParser.json());
  9. app.use(bodyParser.urlencoded({extended:true}));
  10. app.use(express.static('./frontend/assets'));
  11. app.get('/', function (req, res) {
  12. res.sendFile('./frontend/index.html', {
  13. root: path.resolve(__dirname + '/..')
  14. });
  15. });
  16. const httpServer = require('http').Server(app);
  17. /* --------
  18. * RECEIVER
  19. * --------
  20. */
  21. const receiverIo = require('socket.io')(httpServer, {
  22. transports: ['websocket']
  23. });
  24. const onSocketListening = function() {
  25. const address = receiverUdpSocket.address();
  26. console.log('Serveur TUIO en écoute sur : ' + address.address + ':' + address.port);
  27. };
  28. const onSocketConnection = function(socket) {
  29. receiverUdpSocket.on('message', function(msg) {
  30. socket.emit('osc', oscParser.decode(msg));
  31. });
  32. };
  33. const receiverUdpSocket = dgram.createSocket('udp4');
  34. receiverUdpSocket.on('listening', onSocketListening);
  35. receiverUdpSocket.bind(3333, '127.0.0.1');
  36. app.get('/receiver/json', function (req, res) {
  37. res.status(200).send();
  38. });
  39. receiverIo.sockets.on('connection', (socket) =>{
  40. console.log(`Connecté au client ${socket.id}`);
  41. const dgramCallback = function (buf) {
  42. // console.log(oscParser.decode(buf));
  43. socket.emit('osc', oscParser.decode(buf));
  44. };
  45. // forward UDP packets via socket.io
  46. receiverUdpSocket.on('message', dgramCallback);
  47. // prevent memory leak on disconnect
  48. socket.on('disconnect', function (socket) {
  49. receiverUdpSocket.removeListener('message', dgramCallback);
  50. });
  51. });
  52. /* -------
  53. * EMITTER
  54. * -------
  55. */
  56. const emitterOscClient = new Client('127.0.0.1', 3333);
  57. let alive = [];
  58. let fseq;
  59. let objTriangle = [] ;
  60. function getHypotenuse(touch1, touch2) {
  61. const x = Math.abs(touch1.x - touch2.x);
  62. const y = Math.abs(touch1.y - touch2.y);
  63. return Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2));
  64. }
  65. function getTop(dotTrio) {
  66. const dist01 = getHypotenuse(dotTrio[0], dotTrio[1]);
  67. const dist02 = getHypotenuse(dotTrio[0], dotTrio[2]);
  68. const dist12 = getHypotenuse(dotTrio[1], dotTrio[2]);
  69. const diff01m02 = Math.abs(dist01 - dist02);
  70. const diff01m12 = Math.abs(dist01 - dist12);
  71. const diff02m12 = Math.abs(dist02 - dist12);
  72. if (diff01m02 < diff02m12 && diff01m02 < diff01m12) {return 0;}
  73. else if (diff01m12<diff01m02 && diff01m12<diff02m12) {return 1;}
  74. else if (diff02m12<diff01m02 && diff02m12<diff01m12) {return 2;}
  75. }
  76. function getAngleApex(dotTrio, topIndex) {
  77. let dotA;
  78. let dotB;
  79. let dotC;
  80. dotB = dotTrio[topIndex];
  81. if (topIndex == 0) {
  82. dotA = dotTrio[1];
  83. dotC = dotTrio[2];
  84. }
  85. else if (topIndex == 1) {
  86. dotA = dotTrio[0];
  87. dotC = dotTrio[2];
  88. }
  89. else if (topIndex == 2) {
  90. dotA = dotTrio[0];
  91. dotC = dotTrio[1];
  92. }
  93. const AB = [dotB.x - dotA.x, dotB.y - dotA.y] ;
  94. const CB = [dotB.x - dotC.x, dotB.y - dotC.y] ;
  95. const dotProd = (AB[0] * CB[0] + AB[1] * CB[1]);
  96. const crossProd = (AB[0]*CB[1] - AB[1] * CB[0]);
  97. const alpha = Math.atan2(crossProd, dotProd);
  98. //return alpha ;
  99. return Math.floor(alpha * 180. / Math.PI + 0.5) ;
  100. }
  101. function getOrientation(dotTrio, topIndex) {
  102. let dotA;
  103. let dotB;
  104. let dotC;
  105. dotB = dotTrio[topIndex];
  106. if (topIndex == 0) {
  107. dotA = dotTrio[1];
  108. dotC = dotTrio[2];
  109. }
  110. else if (topIndex == 1) {
  111. dotA = dotTrio[0];
  112. dotC = dotTrio[2];
  113. }
  114. else if (topIndex == 2) {
  115. dotA = dotTrio[0];
  116. dotC = dotTrio[1];
  117. }
  118. const middlePt = [(dotA.x+dotC.x)/2 ,(dotA.y+dotC.y)/2 ] ;
  119. let diff = [dotB.x - middlePt[0], dotB.y - middlePt[1]] ;
  120. const length = Math.sqrt(Math.pow(diff[0],2) + Math.pow(diff[1], 2) ) ;
  121. //normalize diff
  122. diff = [diff[0]/length, diff[1]/length];
  123. const rad = Math.atan2(diff[0], diff[1]) ;
  124. return Math.floor( -1 * rad * 180 / Math.PI) ;
  125. //return length ;
  126. }
  127. app.post('/emitter/json', function (req, res) {
  128. if (req.body.debug) {
  129. //console.log(req.body);
  130. }
  131. let oscBundle;
  132. if (req.body.event === 'touchend') {
  133. fseq = fseq ? fseq + 1 : 1;
  134. const aliveMessage = [ '/tuio/2Dcur', 'alive' ].concat(alive);
  135. oscBundle = new Bundle(
  136. [ '/tuio/2Dcur', 'source', `tangibles${req.body.section.toString()}@127.0.0.1` ],
  137. aliveMessage,
  138. [ '/tuio/2Dcur', 'fseq', fseq ],
  139. [
  140. '/tuio/2Dcur',
  141. 'del',
  142. req.body.changedTouches[0].identifier
  143. ]
  144. );
  145. emitterOscClient.send(oscBundle, () => {
  146. const index = alive.indexOf(req.body.changedTouches[0].identifier);
  147. alive.splice(index, 1);
  148. if (alive.length === 0) {
  149. oscBundle = new Bundle(
  150. [ '/tuio/2Dcur', 'source', `tangibles${req.body.section.toString()}@127.0.0.1` ],
  151. [ '/tuio/2Dcur', 'alive' ],
  152. [ '/tuio/2Dcur', 'fseq', fseq ]
  153. );
  154. emitterOscClient.send(oscBundle, () => {
  155. res.status(200).send();
  156. fseq = 0;
  157. });
  158. } else {
  159. res.status(200).send();
  160. }
  161. });
  162. } else {
  163. if (req.body.changedTouches && req.body.changedTouches.length && req.body.changedTouches.length > 0) {
  164. fseq = fseq ? fseq + 1 : 1;
  165. const touches = Object.keys(req.body.changedTouches);
  166. const aliveMessage = [ '/tuio/2Dcur', 'alive' ].concat(alive);
  167. touches.forEach(touch => {
  168. const id = req.body.changedTouches[touch].identifier;
  169. if (!alive.includes(id)) {
  170. alive.push(id);
  171. aliveMessage.push(id);
  172. }
  173. });
  174. /* Listage de tous les points */
  175. const dots = [];
  176. touches.forEach(function(touch) {
  177. dots.push({
  178. id: req.body.changedTouches[touch].identifier,
  179. x: req.body.changedTouches[touch].clientX,
  180. y: req.body.changedTouches[touch].clientY
  181. });
  182. });
  183. // console.log(dots, dots.length);
  184. /* Listage des segments */
  185. const segments = [];
  186. if (dots.length > 2) {
  187. for (var i = 0; i < dots.length; i++) {
  188. for (var j = 0; j < dots.length; j++) {
  189. if (j !== i) {
  190. /* on vérifie que le segment n'est pas déjà listé */
  191. const alreadyExists = segments.find(segment => {
  192. return segment.identifiers.includes(i) && segment.identifiers.includes(j);
  193. });
  194. /* on calcule la taille du segment (l'hypoténuse) */
  195. var hyp = getHypotenuse(dots[i], dots[j]);
  196. /* on garde uniquement les segments inférieurs à 550px
  197. * cette valeur devra être une variable de configuration */
  198. if (!alreadyExists && hyp <= 750) {
  199. segments.push({
  200. identifiers: [i, j],
  201. x1: dots[i].x,
  202. x2: dots[j].x,
  203. y1: dots[i].y,
  204. y2: dots[j].y,
  205. hyp
  206. });
  207. }
  208. }
  209. }
  210. }
  211. }
  212. // console.log(segments, segments.length);
  213. /* Listage des triangles */
  214. const triangles = [];
  215. /* on boucle sur les segments */
  216. segments.forEach((segment) => {
  217. const dot1 = segment.identifiers[0];
  218. const dot2 = segment.identifiers[1];
  219. /* on vérifie que le triangle n'est pas déjà listé */
  220. const alreadyExists = triangles.find(triangle => {
  221. return triangle.includes(dot1) && triangle.includes(dot2);
  222. });
  223. if (!alreadyExists) {
  224. /* on cherche les segments qui contiennent un des 2 points du segment actuel
  225. * ex: si le segment actuel est AB, on cherche un segment contenant A (pour AC) et un autre contenant B (pour BC) */
  226. const found1 = segments.findIndex(seg => {
  227. return (seg.identifiers.includes(dot1) && !seg.identifiers.includes(dot2));
  228. });
  229. const found2 = segments.findIndex(seg => {
  230. return (seg.identifiers.includes(dot2) && !seg.identifiers.includes(dot1));
  231. });
  232. /* si on trouve bien les 2 segments (AC et BC), on peut créer un triangle */
  233. if (found1 !== -1 && found2 !== -1) {
  234. /* on devine quel est le 3ème point du triangle par rapport au segment actuel (le point C par rapport au segment AB) */
  235. const dot3 = segments[found1].identifiers.find(identifier => {
  236. return identifier !== dot1 && identifier !== dot2;
  237. });
  238. triangles.push([dot1, dot2, dot3]);
  239. }
  240. }
  241. });
  242. console.log(triangles, triangles.length);
  243. //MOD TITI
  244. //objet pour stocker les informations des triangles identifiés (points, centre, apexAngle, orientation, indice apex, width, height)
  245. objTriangle = {} ;
  246. /* Définition de l'apex */
  247. triangles.forEach(triangle => {
  248. objTriangle.dots = [];
  249. objTriangle.dots[0] = dots[triangle[0]];
  250. objTriangle.dots[1] = dots[triangle[1]];
  251. objTriangle.dots[2] = dots[triangle[2]];
  252. objTriangle.apex = getTop(objTriangle.dots);
  253. objTriangle.center = [
  254. (objTriangle.dots[0].x+objTriangle.dots[1].x+objTriangle.dots[2].x)/3 ,
  255. (objTriangle.dots[0].y+objTriangle.dots[1].y+objTriangle.dots[2].y)/3
  256. ];
  257. objTriangle.angleApex = getAngleApex(objTriangle.dots, objTriangle.apex) ;
  258. objTriangle.orientation = getOrientation(objTriangle.dots, objTriangle.apex) ;
  259. // /* on récupère les 3 côtés (segments) du triangle */
  260. // const segment1 = segments.find(segment => {
  261. // return segment.identifiers.includes(triangle[0]) && segment.identifiers.includes(triangle[1]);
  262. // });
  263. // const segment2 = segments.find(segment => {
  264. // return segment.identifiers.includes(triangle[1]) && segment.identifiers.includes(triangle[2]);
  265. // });
  266. // const segment3 = segments.find(segment => {
  267. // return segment.identifiers.includes(triangle[0]) && segment.identifiers.includes(triangle[2]);
  268. // });
  269. // /* on trouve quel côté est le plus court
  270. // * NOTE: dans notre cas, l'apex sera toujours le point opposé au plus petit côté
  271. // * cette méthode n'est pas généralisable à d'autres projets */
  272. // const smallestSegment = [segment1, segment2, segment3].reduce(function(prev, current) {
  273. // return (prev.hyp < current.hyp) ? prev : current
  274. // });
  275. // /* on déduit quel point du triangle est l'apex */
  276. // const apexDot = triangle.find(dot => {
  277. // return dot !== smallestSegment.identifiers[0] && dot !== smallestSegment.identifiers[1];
  278. // });
  279. console.log(objTriangle.apex);
  280. console.log('centerPos : ' + objTriangle.center + ' orientation : ' + objTriangle.orientation);
  281. /* Reste à faire:
  282. * trouver la valeur de l'angle de l'apex (théorème des cosinus)
  283. * trouver le "centre" du triangle (via les orthocentres ou médianes??)
  284. * déduire l'orientation du triangle
  285. */
  286. });
  287. //plante vite
  288. // if (objTriangle.dots != undefined){
  289. // let oscBundleObj ;
  290. // oscBundleObj = new Bundle(
  291. // [ '/tuio/2Dobj', 'source', `tangibles${req.body.section.toString()}@127.0.0.1` ],
  292. // [ '/tuio/2Dobj', 'alive', 1 ],
  293. // [
  294. // '/tuio/2Dobj',
  295. // 'set',
  296. // 1,
  297. // 1,
  298. // objTriangle.center[0],
  299. // objTriangle.center[1],
  300. // objTriangle.orientation,
  301. // 0.0,
  302. // 0.0,
  303. // 0.0,
  304. // 0.0,
  305. // 0.0
  306. // ],
  307. // [ '/tuio/2Dobj', 'fseq', fseq ]
  308. // );
  309. // // // objTriangle.forEach(triangleIndex => {
  310. // // // oscBundle.append(
  311. // // // [
  312. // // // '/tuio/2Dobj',
  313. // // // 'set',
  314. // // // triangleIndex,
  315. // // // triangleIndex,
  316. // // // objTriangle[triangleIndex].center[0],
  317. // // // objTriangle[triangleIndex].center[1],
  318. // // // objTriangle[triangleIndex].orientation,
  319. // // // 0.0,
  320. // // // 0.0,
  321. // // // 0.0,
  322. // // // 0.0,
  323. // // // 0.0
  324. // // // ]
  325. // // // );
  326. // // // });
  327. // console.log(oscBundleObj);
  328. // emitterOscClient.send(oscBundleObj, () => {
  329. // res.status(200).send();
  330. // });
  331. // }
  332. // oscBundle = new Bundle(
  333. // [ '/tuio/2Dcur', 'source', `tangibles${req.body.section.toString()}@127.0.0.1` ],
  334. // aliveMessage,
  335. // [ '/tuio/2Dcur', 'fseq', fseq ]
  336. // );
  337. // touches.forEach(touch => {
  338. // oscBundle.append(
  339. // [
  340. // '/tuio/2Dcur',
  341. // 'set',
  342. // req.body.changedTouches[touch].identifier,
  343. // req.body.changedTouches[touch].clientX / req.body.screenW,
  344. // req.body.changedTouches[touch].clientY / req.body.screenH,
  345. // 0.0,
  346. // 0.0
  347. // ]
  348. // );
  349. // });
  350. oscBundle = new Bundle ;
  351. oscBundle.append([ '/tuio/2Dobj', 'alive', 1 ]);
  352. if (objTriangle.dots != undefined){
  353. oscBundle.append([
  354. '/tuio/2Dobj',
  355. 'set',
  356. 1,
  357. 1,
  358. objTriangle.center[0],
  359. objTriangle.center[1],
  360. objTriangle.orientation,
  361. 0.0,
  362. 0.0,
  363. 0.0,
  364. 0.0,
  365. 0.0
  366. ]);
  367. }
  368. oscBundle.append([ '/tuio/2Dobj', 'fseq', fseq ]);
  369. emitterOscClient.send(oscBundle, () => {
  370. res.status(200).send();
  371. });
  372. } else {
  373. res.status(400).send();
  374. }
  375. }
  376. });
  377. httpServer.listen(5000, function () {
  378. console.log('Votre app est disponible sur localhost:5000 !')
  379. });