server.js 12 KB

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