ConcernementMapItem.vue 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860
  1. <script>
  2. // https://brm.io/matter-js/docs/classes/Engine.html
  3. // import {
  4. // // Engine,
  5. // // Render,
  6. // // World,
  7. // Bodies,
  8. // Body,
  9. // Events,
  10. // Composite,
  11. // // Composites,
  12. // // Constraint,
  13. // // Vertices,
  14. // // Mouse,
  15. // // MouseConstraint,
  16. // // Query,
  17. // // Common
  18. // } from "matter-js";
  19. import Matter from "matter-js";
  20. import MatterAttractors from "matter-attractors";
  21. // Matter.use(MatterAttractors);
  22. // import polydecomp from "poly-decomp";
  23. // import { easeInOutQuad, easeInOutQuart } from 'easing-utils';
  24. import Tween from "@tweenjs/tween.js";
  25. import { mapState, mapActions } from 'pinia'
  26. import { ConcernementsStore } from '@/stores/concernements'
  27. import { CommonStore } from '@/stores/common'
  28. export default {
  29. inject: ['canvasMap', 'matterEngine'],
  30. data() {
  31. return {
  32. id: null,
  33. entities: null,
  34. // opened_entite_id: null,
  35. canvas: null,
  36. ctx: null,
  37. pos : {
  38. x: 0,
  39. y: 0
  40. },
  41. ray: 60,
  42. time: 0,
  43. salientPoints: [],
  44. scale: 1,
  45. opacity: 0,
  46. tween: null,
  47. body: null,
  48. body_parts: [],
  49. constraint: null,
  50. isHover: false
  51. }
  52. },
  53. props: ['concernement', 'opened'],
  54. computed: {
  55. ...mapState(ConcernementsStore,['map_mode']),
  56. ...mapState(ConcernementsStore,['concernementsByID']),
  57. ...mapState(ConcernementsStore,['opened_entite_id']),
  58. ...mapState(CommonStore,['hover_elmt'])
  59. },
  60. created () {
  61. // console.log(`ConcernementsMapItem ${this.concernement.id} created`, this.canvasMap, this.matterEngine);
  62. this.id = this.concernement.id
  63. this.entites = this.concernement.entites
  64. this.entites_byid = this.concernement.entites_byid
  65. // console.log(`ConcernementsMapItem ${this.concernement.id} $route`, this.id, this.$route);
  66. // if (this.$route.name === 'concernement'
  67. // && parseInt(this.$route.params.id) === this.id
  68. // && typeof this.$route.params.eid !== "undefined") {
  69. // // console.log("we have an entity");
  70. // this.opened_entite_id = parseInt(this.$route.params.eid);
  71. // }
  72. this.parsePoints()
  73. this.getSalientPoints()
  74. if (this.salientPoints.length > 3) { // do not build item if it doesn't get enougth salient points
  75. if (this.canvasMap) {
  76. this.initCanvasMap()
  77. }
  78. }
  79. },
  80. // mounted() {
  81. // console.log(`ConcernementsMapItem ${this.concernement.id} mounted`, this.canvasMap.canvas);
  82. // },
  83. watch: {
  84. // canvasMap (n, o) {
  85. // console.log("concernementItem watch canvasMap", o, n);
  86. // }
  87. canvasMap: {
  88. handler (n, o){
  89. // console.log("concernementItem watch canvasMap.ctx", typeof this.canvas, o, n);
  90. if (!this.canvas) {
  91. this.initCanvasMap()
  92. }
  93. },
  94. deep: true
  95. },
  96. opened: {
  97. handler (n, o) {
  98. if(n){ // opened
  99. this.openClose(true);
  100. }else{ // closed
  101. this.openClose(false)
  102. }
  103. },
  104. deep: true
  105. },
  106. map_mode: {
  107. handler (n, o) {
  108. console.log('watch map_mode', o, n);
  109. if (n === 'terraindevie') {
  110. this.applyShuffleForces();
  111. }
  112. },
  113. deep: true
  114. },
  115. hover_elmt: {
  116. handler (n, o) {
  117. // console.log('watch hover_elmt', o, n);
  118. if (n && n.type === 'concernement' && n.id === this.id) {
  119. this.isHover = true;
  120. } else {
  121. this.isHover = false;
  122. }
  123. },
  124. deep: true
  125. }
  126. },
  127. methods: {
  128. parsePoints (){
  129. // converts data (menace/maintien, actuel/future, prise) into atcual position x,y
  130. for (let i = 0; i < this.entites.length; i++) {
  131. let entite = this.entites[i]
  132. // console.log('entite', entite);
  133. this.entites[i].display = {
  134. alpha: null,
  135. ray: null
  136. }
  137. // RAYON
  138. // https://stackoverflow.com/questions/5731863/mapping-a-numeric-range-onto-another
  139. // slope = (output_end - output_start) / (input_end - input_start)
  140. // output = output_start + slope * (input - input_start)
  141. // from range 0 -> 100 to range 0 -> this.ray
  142. let init_max = 100
  143. let slope = this.ray / init_max
  144. this.entites[i].display.ray = slope * (init_max - entite.prise);
  145. // if (this.concernement.id === 28) {
  146. // console.log(`entity prise: ${entite.prise} | ray: ${this.entites[i].display.ray}`);
  147. // }
  148. // ANGLE
  149. // -90 <= mm <= 90
  150. if (entite.actuelfuture) {
  151. // future en haut : 180 <= a <= 360
  152. // from -90 -> 90 to range 180 -> 360
  153. this.entites[i].display.alpha = entite.menacemaintien + 270
  154. } else {
  155. // actuel: en bas : O <= a <= 180
  156. // from -90 -> 90 to range 180 -> 0
  157. this.entites[i].display.alpha = -1 * entite.menacemaintien + 90
  158. }
  159. // POSITION X Y (par rapport au centre du concernement)
  160. this.entites[i].display.pos = {
  161. x: this.entites[i].display.ray * Math.cos(this.entites[i].display.alpha * (Math.PI/180)),
  162. y: this.entites[i].display.ray * Math.sin(this.entites[i].display.alpha * (Math.PI/180))
  163. }
  164. this.entites_byid[entite.entite.id].display = this.entites[i].display;
  165. }
  166. },
  167. getSalientPoints () {
  168. // debugger
  169. // console.log(this.entites);
  170. let arc = 360/10;
  171. // loop through arcs
  172. // for (let i = 360/arc; i >= 0 ; i--) {
  173. for (let i = 0; i <= 360/arc ; i++) {
  174. // loop through entities to find the farest on the arc
  175. let max_r = 0;
  176. let farest = null;
  177. for (let j = 0; j < this.entites.length; j++) {
  178. let entite = this.entites[j];
  179. if(arc*i <= entite.display.alpha && entite.display.alpha <= arc*i+arc) { // if entity is in arc
  180. if (entite.display.ray > max_r) { // && entite.display.ray > this.ray/2 // and farest from minimu
  181. // if entity is farest from precedent one
  182. max_r = entite.display.ray;
  183. // recalcul x & y to get a little padding between entite and contour by increasing ray
  184. farest = {
  185. alpha: entite.display.alpha,
  186. ray: entite.display.ray,
  187. pos: {
  188. x: (entite.display.ray + 3) * Math.cos(entite.display.alpha * (Math.PI/180)),
  189. y: (entite.display.ray + 3) * Math.sin(entite.display.alpha * (Math.PI/180))
  190. }
  191. };
  192. }
  193. }
  194. }
  195. if (farest) {
  196. this.salientPoints.push(farest)
  197. }
  198. }
  199. console.log(`this.salientPoints ${this.concernement.id}`, this.salientPoints);
  200. },
  201. initCanvasMap (){
  202. // console.log(`ConcernementsMapItem ${this.concernement.id} initCanvasMap`);
  203. // record canvas and ctx for rendering (drawing)
  204. this.canvas = this.canvasMap.canvas
  205. this.ctx = this.canvasMap.ctx
  206. // define init position of the item
  207. this.pos = this.getRandomPos();
  208. //
  209. this.initMatterBody()
  210. },
  211. getRandomPos(){
  212. let pad = 200;
  213. return {
  214. x: pad + this.ray/2 + Math.random()*(this.canvas.width - this.ray - pad),
  215. y: pad + this.ray/2 + Math.random()*(this.canvas.height - this.ray - pad)
  216. };
  217. },
  218. initMatterBody (){
  219. // MATTER
  220. // create the matter body and add it to the engine
  221. if (!this.body) {
  222. // console.log('concernementItem creating body');
  223. // https://github.com/liabru/matter-attractors/issues/8
  224. // https://github.com/liabru/matter-attractors/blob/master/index.js
  225. // MatterAttractors.Attractors.gravityConstant = -5;
  226. // Create parts of the body : main big circle & entities
  227. this.body_parts = [
  228. Matter.Bodies.circle(0, 0, this.ray, {
  229. item_type: 'concernement',
  230. id: this.concernement.id,
  231. })
  232. ];
  233. // // Create parts of the body : contours
  234. // if (this.salientPoints.length > 2) {
  235. // var decomp = require('poly-decomp');
  236. // // window.decomp = decomp;
  237. // // debugger;
  238. // Matter.Common.setDecomp(decomp);
  239. // // Matter.Common.setDecomp(require('poly-decomp'));
  240. // // let contourpoints = [];
  241. // // for (let j = 0; j < this.salientPoints.length; j++) {
  242. // // contourpoints.push(this.salientPoints[j].pos);
  243. // // }
  244. // let contourpoints = this.salientPoints.map(function(point) {
  245. // return point.pos; //[point.pos.x, point.pos.y]
  246. // });
  247. // // console.log('contourpoints', contourpoints);
  248. // // let contourpoints = Matter.Vertices.fromPath('35 7 19 17 14 38 14 58 25 79 45 85 65 84 65 66 46 67 34 59 30 44 33 29 45 23 66 23 66 7 53 7');
  249. // // let ccw_contourpoints = decomp.makeCCW(contourpoints);
  250. // // console.log('ccw_contourpoints', ccw_contourpoints);
  251. // let vertices_contour = Matter.Vertices.create(contourpoints);
  252. // // console.log('vertices_contour', vertices_contour);
  253. // // let vertices_contour_bounds = Matter.Bounds.create(vertices_contour);
  254. // // console.log('vertices_contour_bounds', vertices_contour_bounds);
  255. // // debugger;
  256. // let body_contour = Matter.Bodies.fromVertices(0, 0, vertices_contour, {
  257. // mass: 0,
  258. // item_type: 'concernement-contours',
  259. // id: this.concernement.id,
  260. // }, false, 0, 0, 0);
  261. // // debugger;
  262. // // console.log('body_contour.bounds', body_contour.bounds);
  263. // // // https://github.com/liabru/matter-js/issues/186
  264. // // Matter.Body.translate(body_contour, Matter.Vector.sub(vertices_contour_bounds.min, body_contour.bounds.min))
  265. // this.body_parts.push(body_contour);
  266. // }
  267. // Create parts of the body : entities
  268. for (let i = 0; i < this.entites.length; i++) {
  269. // parts.push(Matter.Bodies.circle(this.pos.x+this.entites[i].display.pos.x, this.pos.y+this.entites[i].display.pos.y, 15, {
  270. // item_type: 'entite',
  271. // id: this.entites[i].id
  272. // }))
  273. this.body_parts.push(Matter.Bodies.circle(this.entites[i].display.pos.x, this.entites[i].display.pos.y, 0.8, {
  274. item_type: 'entite',
  275. id: this.entites[i].entite.id,
  276. cid: this.concernement.id,
  277. agissante: this.entites[i].entite.agissante,
  278. isSensor: true
  279. }))
  280. }
  281. // Create parts of the body : besoins and responses
  282. this.createBesoinsBodyParts();
  283. // create the body
  284. this.body = Matter.Body.create({
  285. parts: this.body_parts,
  286. item_type: 'concernement',
  287. id: this.concernement.id,
  288. frictionAir: 0,
  289. // mass: Math.pow(3, this.entites.length),
  290. mass: 10,
  291. restitution: 0.15,
  292. collisionFilter: {
  293. group: -1
  294. },
  295. plugin: {
  296. attractors: [
  297. // there is a built in helper function for Newtonian gravity!
  298. // you can find out how it works in index.js
  299. MatterAttractors.Attractors.gravity
  300. ]
  301. }
  302. });
  303. Matter.Body.setPosition(this.body, this.pos);
  304. // add init velocity
  305. let delta = 10;
  306. Matter.Body.setVelocity(this.body, {
  307. x: -delta + Math.random()*delta*2,
  308. y: -delta + Math.random()*delta*2
  309. });
  310. // console.log('concernementItem mass', this.body.mass);
  311. Matter.Composite.add(this.matterEngine.world, this.body);
  312. // console.log('concernement body', this.body);
  313. // // listen for animate event dispatched from parent
  314. // this.canvas.addEventListener('animate', this.animate)
  315. // listen for afterUpdate event from Matter.Engine object
  316. Matter.Events.on(this.matterEngine, "beforeUpdate", this.onBeforeEngineUpdate);
  317. Matter.Events.on(this.matterEngine, "afterUpdate", this.onAfterEngineUpdate);
  318. }
  319. },
  320. createBesoinsBodyParts(){
  321. let res_fields = ['qui','quoi','ou','avec'];
  322. let arc = (360 / 16); // unit arc
  323. let r = (this.ray * this.scale)/5; // unit ray
  324. let br = r - r/3; // besoin ray
  325. for (let i = 0; i < this.concernement.besoins.length; i++) {
  326. let start_a = arc * i; // angle depart (for reponses)
  327. let center_a = start_a + arc/2; // angle central
  328. let x = Math.cos(center_a*(Math.PI/180)) * br;
  329. let y = Math.sin(center_a*(Math.PI/180)) * br;
  330. this.body_parts.push(Matter.Bodies.circle(x, y, 0.8, {
  331. item_type: 'besoin',
  332. id: this.concernement.besoins[i].id,
  333. cid: this.concernement.id,
  334. isSensor: true
  335. }));
  336. let res_arc = arc / (1 + this.concernement.besoins[i].reponses.length); // unit arc for responses depending responses number
  337. for (let j = 0; j < this.concernement.besoins[i].reponses.length; j++) {
  338. let res_a = start_a + res_arc * (j+1); // angle for response line
  339. for (let f = 0; f < res_fields.length; f++) {
  340. if(this.concernement.besoins[i].reponses[j][res_fields[f]]){
  341. let rr = this.ray * this.scale - r*f - r/2; // reponse field ray
  342. let rx = Math.cos(res_a*(Math.PI/180)) * rr;
  343. let ry = Math.sin(res_a*(Math.PI/180)) * rr;
  344. this.body_parts.push(Matter.Bodies.circle(rx, ry, 0.8, {
  345. item_type: 'reponse',
  346. id: this.concernement.besoins[i].reponses[j].id,
  347. bid: this.concernement.besoins[i].id,
  348. cid: this.concernement.id,
  349. isSensor: true
  350. }));
  351. }
  352. }
  353. }
  354. }
  355. },
  356. // onBeforeEngineUpdate (event) {
  357. // if (this.opened) {
  358. // // Matter.Body.setPosition(this.body, this.pos);
  359. // }
  360. // },
  361. openClose(open) {
  362. // console.log(`ConcernementsMapItem ${this.concernement.id} openClose: ${open}`);
  363. if (this.tween) {
  364. this.tween.stop();
  365. }
  366. if (open) {
  367. // opening tweening
  368. this.tween = new Tween.Tween({s: this.scale, x: this.pos.x, y: this.pos.y, o: 0})
  369. .to({
  370. s: 7,
  371. x: (this.canvas.width - 450) / 2,
  372. y: this.canvas.height / 2,
  373. o: 0.8
  374. }, 800)
  375. .onUpdate((obj) => {
  376. // https://github.com/liabru/matter-js/issues/986#issuecomment-812488873
  377. // revert to the original size (by reverting the previous scale)
  378. Matter.Body.scale(this.body, 1 / this.scale, 1 / this.scale)
  379. // then scale again to new scale
  380. Matter.Body.scale(this.body, obj.s, obj.s)
  381. // record new scale
  382. this.scale = obj.s;
  383. this.opacity = obj.o;
  384. Matter.Body.setPosition(this.body, {x:obj.x, y:obj.y});
  385. this.pos = {x:obj.x, y:obj.y};
  386. })
  387. .onComplete((obj) => {
  388. this.constraint = Matter.Constraint.create({
  389. pointA: this.pos,
  390. bodyB: this.body,
  391. stiffness: 1,
  392. damping: 0,
  393. length: 0
  394. });
  395. Matter.Composite.add(this.matterEngine.world, [this.body, this.constraint]);
  396. });
  397. // recreate the matter engine event to get it a the end of the events stack
  398. Matter.Events.off(this.matterEngine, "afterUpdate", this.onAfterEngineUpdate);
  399. Matter.Events.on(this.matterEngine, "afterUpdate", this.onAfterEngineUpdate);
  400. } else {
  401. // closing
  402. if(this.constraint){
  403. Matter.Composite.remove(this.matterEngine.world, this.constraint);
  404. }
  405. this.tween = new Tween.Tween({s: this.scale, o: 1})
  406. .to({s: 1, o: 0}, 500)
  407. .onUpdate((obj) => {
  408. // https://github.com/liabru/matter-js/issues/986#issuecomment-812488873
  409. // revert to the original size (by reverting the previous scale)
  410. Matter.Body.scale(this.body, 1 / this.scale, 1 / this.scale)
  411. // then scale again to new scale
  412. Matter.Body.scale(this.body, obj.s, obj.s)
  413. // record new scale
  414. this.scale = obj.s;
  415. this.opacity = obj.o;
  416. });
  417. }
  418. this.tween.easing(Tween.Easing.Quadratic.InOut).start();
  419. },
  420. onBeforeEngineUpdate (event) {
  421. if (this.tween) {
  422. this.tween.update();
  423. }
  424. if (this.map_mode === 'action' || this.map_mode === 'puissancedagir'){
  425. this.applyFocusForces();
  426. // TODO apply a little force to check the map when returning to terrain de vie
  427. }
  428. Matter.Body.setAngle(this.body, 0);
  429. Matter.Body.setAngularSpeed(this.body, 0);
  430. },
  431. applyFocusForces(){
  432. // map_mode action
  433. var dist, dir, ori_pos;
  434. var x_force = 0;
  435. if(!this.isFocused()) {
  436. // does not has actions -> take a side
  437. // apply a force in direction of one side or an other depending of the start position
  438. // the force is exponentialy proportional to the distance from the side
  439. dir = this.pos.x > this.canvas.width/2 ? 1 : -1; // get the direction to the closest side
  440. dist = (dir < 0 ? this.pos.x : this.canvas.width - this.pos.x); // get the distance from the side
  441. ori_pos = {x:this.canvas.width/2, y:this.body.position.y};
  442. x_force = Math.pow(dist/700,10) * dir;
  443. }else{
  444. // has action, get to the centre
  445. dir = this.pos.x > this.canvas.width/2 ? -1 : 1; // get the direction to the centre
  446. dist = (dir < 0 ? this.pos.x - this.canvas.width/2 : this.canvas.width/2 - this.pos.x); // get the distance from the side
  447. ori_pos = dir < 0 ? {x:this.canvas.width, y:this.body.position.y} : {x:0, y:this.body.position.y}
  448. x_force = Math.pow(dist/800,10) * dir;
  449. this.body.frictionAir = 0.05;
  450. }
  451. // x_force = (dist > 200 ? Math.pow(dist/700,10) : 0) * dir
  452. Matter.Body.applyForce(
  453. this.body,
  454. ori_pos,
  455. {
  456. x: x_force,
  457. y: 0
  458. }
  459. );
  460. },
  461. applyShuffleForces() {
  462. var dist, dir, x_velocity;
  463. dir = this.pos.x > this.canvas.width/2 ? -1 : 1; // get the direction to the centre
  464. dist = (dir < 0 ? this.pos.x - this.canvas.width/2 : this.canvas.width/2 - this.pos.x); // get the distance from the side
  465. x_velocity = Math.pow(dist/650,10) * dir;
  466. Matter.Body.setVelocity(this.body, {x: x_velocity, y: 0});
  467. },
  468. onAfterEngineUpdate (event) {
  469. // respawn element if outside screen
  470. if(this.pos.x < 0
  471. || this.pos.x > this.canvas.width
  472. || this.pos.y < 0
  473. || this.pos.y > this.canvas.height){
  474. this.pos = this.getRandomPos()
  475. Matter.Body.setPosition(this.body, {x:this.pos.x, y:this.pos.y});
  476. }
  477. this.draw()
  478. },
  479. draw() {
  480. if (!this.ctx) return;
  481. // record the position from the main matter body
  482. this.pos = this.body.position;
  483. if (this.opened) {
  484. switch (this.map_mode) {
  485. case 'terraindevie':
  486. this.drawBoussole();
  487. break;
  488. case 'puissancedagir':
  489. this.drawPuissanceagir();
  490. break;
  491. }
  492. }
  493. // contours
  494. if (!this.opened
  495. || (this.opened && this.map_mode !== "puissancedagir")) {
  496. this.drawContour();
  497. }
  498. // map mode puissance d'agir
  499. // if not opened and has_puissancedagir draw the puissance d'agir icone
  500. if (this.concernement.has_puissancedagir && this.map_mode === "puissancedagir") {
  501. if (!this.opened) {
  502. this.drawPuissanceagirIcon();
  503. } else {
  504. this.drawBesoins()
  505. }
  506. }
  507. if (this.map_mode !== 'puissancedagir') {
  508. this.drawEntites()
  509. }
  510. },
  511. drawPuissanceagir(){
  512. for (let i = 1; i < 6; i++) {
  513. this.ctx.beginPath();
  514. this.ctx.lineWidth = 0.5;
  515. this.ctx.strokeStyle = `rgba(255,255,255,1)`;
  516. this.ctx.arc(this.pos.x, this.pos.y, ((this.ray*this.scale)/5)*i, 0, 2 * Math.PI, false);
  517. this.ctx.stroke();
  518. }
  519. this.ctx.beginPath();
  520. this.ctx.lineWidth = 1;
  521. this.ctx.strokeStyle = `rgba(255,255,255,1)`;
  522. this.ctx.setLineDash([5,5]);
  523. for (let j = 0; j < 4; j++) {
  524. let a = (90 / 4) * j;
  525. // diagonale
  526. // https://fr.wikipedia.org/wiki/Trigonom%C3%A9trie#/media/Fichier:Unit_circle_angles_color.svg
  527. // https://fr.wikipedia.org/wiki/Identit%C3%A9_trigonom%C3%A9trique_pythagoricienne#Preuve_utilisant_le_cercle_unit%C3%A9
  528. // radians = degrees * (pi/180)
  529. // degrees = radians * (180/pi)
  530. let x = Math.cos(a*(Math.PI/180)) * this.ray * this.scale;
  531. let y = Math.sin(a*(Math.PI/180)) * this.ray * this.scale;
  532. // console.log('m', m);
  533. this.ctx.moveTo(this.pos.x + x, this.pos.y + y);
  534. this.ctx.lineTo(this.pos.x - x, this.pos.y - y);
  535. //
  536. this.ctx.moveTo(this.pos.x - y, this.pos.y + x);
  537. this.ctx.lineTo(this.pos.x + y, this.pos.y - x);
  538. }
  539. this.ctx.stroke();
  540. this.ctx.setLineDash([]);
  541. this.ctx.beginPath();
  542. this.ctx.fillStyle = `rgba(255,255,255,0.6)`;
  543. this.ctx.lineWidth = 2;
  544. this.ctx.strokeStyle = `#fff`;
  545. this.ctx.arc(this.pos.x, this.pos.y, this.ray*this.scale, 0, 2 * Math.PI, false);
  546. this.ctx.fill();
  547. this.ctx.stroke()
  548. this.ctx.closePath();
  549. },
  550. drawPuissanceagirIcon(){
  551. var r = 20 * this.scale; // ray
  552. var dr = r/2; // demi ray
  553. var d = r*2; // diameter
  554. this.ctx.beginPath();
  555. this.ctx.lineWidth = 1;
  556. this.ctx.strokeStyle = `#000`;
  557. this.ctx.arc(this.pos.x, this.pos.y, r, 0, 2 * Math.PI, false);
  558. this.ctx.stroke();
  559. this.ctx.beginPath();
  560. this.ctx.lineWidth = 1;
  561. this.ctx.strokeStyle = `#000`;
  562. this.ctx.arc(this.pos.x, this.pos.y, dr, 0, 2 * Math.PI, false);
  563. this.ctx.stroke();
  564. this.ctx.beginPath();
  565. this.ctx.lineWidth = 1;
  566. this.ctx.strokeStyle = `#000`;
  567. this.ctx.fillStyle = '#000';
  568. this.ctx.arc(this.pos.x, this.pos.y, 2*this.scale, 0, 2 * Math.PI, false);
  569. this.ctx.fill();
  570. this.ctx.stroke();
  571. // axes
  572. this.ctx.beginPath();
  573. this.ctx.lineWidth = 1;
  574. this.ctx.strokeStyle = `#000`;
  575. // vertical
  576. // this.ctx.moveTo(this.pos.x, this.pos.y - r);
  577. // this.ctx.lineTo(this.pos.x , this.pos.y - dr);
  578. // this.ctx.moveTo(this.pos.x, this.pos.y + r);
  579. // this.ctx.lineTo(this.pos.x , this.pos.y + dr);
  580. this.ctx.moveTo(this.pos.x, this.pos.y - r);
  581. this.ctx.lineTo(this.pos.x , this.pos.y + r);
  582. // horizontal
  583. // this.ctx.moveTo(this.pos.x - r, this.pos.y);
  584. // this.ctx.lineTo(this.pos.x - dr, this.pos.y);
  585. // this.ctx.moveTo(this.pos.x + r, this.pos.y);
  586. // this.ctx.lineTo(this.pos.x + dr, this.pos.y);
  587. this.ctx.moveTo(this.pos.x - r, this.pos.y);
  588. this.ctx.lineTo(this.pos.x + r, this.pos.y);
  589. // diagonale
  590. // https://fr.wikipedia.org/wiki/Trigonom%C3%A9trie#/media/Fichier:Unit_circle_angles_color.svg
  591. // https://fr.wikipedia.org/wiki/Identit%C3%A9_trigonom%C3%A9trique_pythagoricienne#Preuve_utilisant_le_cercle_unit%C3%A9
  592. // radians = degrees * (pi/180)
  593. // degrees = radians * (180/pi)
  594. var m = Math.sin(45*(Math.PI/180)) * r;
  595. // console.log('m', m);
  596. this.ctx.moveTo(this.pos.x + m, this.pos.y + m);
  597. this.ctx.lineTo(this.pos.x - m, this.pos.y - m);
  598. //
  599. this.ctx.moveTo(this.pos.x - m, this.pos.y + m);
  600. this.ctx.lineTo(this.pos.x + m, this.pos.y - m);
  601. this.ctx.stroke();
  602. },
  603. drawBesoins(){
  604. for (let i = 0; i < this.body.parts.length; i++) {
  605. if (this.body.parts[i].item_type === 'besoin' || this.body.parts[i].item_type === 'reponse') {
  606. let part = this.body.parts[i];
  607. switch (part.item_type) {
  608. case 'besoin':
  609. this.ctx.beginPath();
  610. this.ctx.fillStyle = "#000";
  611. this.drawDiamond(part.position.x, part.position.y, 4);
  612. this.ctx.fill();
  613. break;
  614. case 'reponse':
  615. this.ctx.beginPath();
  616. this.ctx.fillStyle = "#eee";
  617. this.ctx.strokeStyle = "#000";
  618. this.ctx.lineWidth = 1;
  619. // this.ctx.arc(this.pos.x + rx, this.pos.y + ry, 2*(f+1), 0, 2 * Math.PI, false);
  620. this.drawDiamond(part.position.x, part.position.y, 5);
  621. this.ctx.fill();
  622. this.ctx.stroke();
  623. break;
  624. }
  625. }
  626. }
  627. },
  628. drawDiamond(x,y,r){
  629. this.ctx.moveTo(x, y - r);
  630. this.ctx.lineTo(x + r, y);
  631. this.ctx.lineTo(x, y + r);
  632. this.ctx.lineTo(x - r, y);
  633. this.ctx.lineTo(x, y - r);
  634. },
  635. drawBoussole(){
  636. // BOUSSOLE
  637. // exterieur circle
  638. this.ctx.beginPath();
  639. this.ctx.lineWidth = 2;
  640. this.ctx.strokeStyle = `rgba(255,255,255,${this.opacity})`;
  641. // external circle is %8 less than max ray = (*0.92)
  642. this.ctx.arc(this.pos.x, this.pos.y, this.ray*this.scale*0.92, 0, 2 * Math.PI, false);
  643. // this.ctx.stroke();
  644. // interieur circle
  645. this.ctx.arc(this.pos.x, this.pos.y, this.ray/2*this.scale, 0, 2 * Math.PI, false);
  646. // this.ctx.stroke();
  647. // axes
  648. // vertical
  649. this.ctx.moveTo(this.pos.x, this.pos.y - this.ray*this.scale);
  650. this.ctx.lineTo(this.pos.x, this.pos.y + this.ray*this.scale);
  651. // horizontal
  652. this.ctx.moveTo(this.pos.x - this.ray*this.scale, this.pos.y);
  653. this.ctx.lineTo(this.pos.x + this.ray*this.scale, this.pos.y);
  654. // this.ctx.stroke();
  655. // fleches
  656. // haute
  657. this.ctx.moveTo(this.pos.x - (8*this.scale), this.pos.y - this.ray*this.scale*0.92 + (8*this.scale));
  658. this.ctx.lineTo(this.pos.x, this.pos.y - this.ray*this.scale*0.92);
  659. this.ctx.lineTo(this.pos.x + (8*this.scale), this.pos.y - this.ray*this.scale*0.92 + (8*this.scale));
  660. // milieu
  661. this.ctx.moveTo(this.pos.x - (8*this.scale), this.pos.y + (8*this.scale));
  662. this.ctx.lineTo(this.pos.x, this.pos.y);
  663. this.ctx.lineTo(this.pos.x + (8*this.scale), this.pos.y + (8*this.scale));
  664. this.ctx.stroke();
  665. // MOINS - PLUS
  666. this.ctx.beginPath();
  667. this.ctx.lineWidth = 8;
  668. this.ctx.strokeStyle = `rgba(255,255,255,${this.opacity})`;;
  669. // PLUS
  670. // horizontal
  671. this.ctx.moveTo(this.pos.x + this.ray*this.scale - (5 * this.scale), this.pos.y - this.ray*this.scale);
  672. this.ctx.lineTo(this.pos.x + this.ray*this.scale + (5 * this.scale), this.pos.y - this.ray*this.scale);
  673. // vertical
  674. this.ctx.moveTo(this.pos.x + this.ray*this.scale, this.pos.y - this.ray*this.scale - (5 * this.scale));
  675. this.ctx.lineTo(this.pos.x + this.ray*this.scale, this.pos.y - this.ray*this.scale + (5 * this.scale));
  676. // MOINS
  677. // horizontal
  678. this.ctx.moveTo(this.pos.x - this.ray*this.scale - (5 * this.scale), this.pos.y - this.ray*this.scale);
  679. this.ctx.lineTo(this.pos.x - this.ray*this.scale + (5 * this.scale), this.pos.y - this.ray*this.scale);
  680. // vertical
  681. this.ctx.stroke();
  682. },
  683. drawEntites(){
  684. // IF OPENED
  685. if (this.opened) {
  686. // place all entities points
  687. // OR using entitées matter bodies
  688. for (let i = 0; i < this.body.parts.length; i++) {
  689. if (this.body.parts[i].item_type === 'entite'
  690. // draw only entite agissante if map mode is action
  691. && ((this.map_mode === 'action' && this.body.parts[i].agissante) || this.map_mode !== "action")) {
  692. let part = this.body.parts[i];
  693. this.ctx.beginPath();
  694. this.ctx.arc(part.position.x, part.position.y, 0.3*this.scale, 0, 2 * Math.PI, false);
  695. // console.log(part.id, this.opened_entite_id);
  696. if (part.id === this.opened_entite_id) {
  697. this.ctx.fillStyle = "#01ffe2";
  698. } else {
  699. this.ctx.fillStyle = "#000";
  700. }
  701. this.ctx.fill();
  702. }
  703. }
  704. }
  705. // IF CLOSED
  706. else {
  707. // map mode action
  708. // if not opened and has_agissantes draw only entites agissantes
  709. if (this.concernement.has_agissantes && this.map_mode === "action") {
  710. for (let i = 0; i < this.body.parts.length; i++) {
  711. if (this.body.parts[i].item_type === 'entite' && this.body.parts[i].agissante) {
  712. let part = this.body.parts[i];
  713. this.ctx.beginPath();
  714. this.ctx.arc(part.position.x, part.position.y, 1*this.scale, 0, 2 * Math.PI, false);
  715. // console.log(part.id, this.opened_entite_id);
  716. if (part.id === this.opened_entite_id) {
  717. this.ctx.fillStyle = "#01ffe2";
  718. } else {
  719. this.ctx.fillStyle = "#000";
  720. }
  721. this.ctx.fill();
  722. }
  723. }
  724. }
  725. }
  726. },
  727. drawContour(){
  728. if (this.salientPoints.length > 3) {
  729. var fillStyle;
  730. let strokeStyle = "#000";
  731. if (!this.isFocused()){
  732. fillStyle = "rgba(255,255,255,0.3)";
  733. }else{
  734. fillStyle = "rgba(255,255,255,0.8)"
  735. if (this.isHover) {
  736. strokeStyle = "#01ffe2";
  737. }
  738. }
  739. this.ctx.beginPath();
  740. this.ctx.lineWidth = 1;
  741. this.ctx.strokeStyle = strokeStyle;
  742. this.ctx.fillStyle = fillStyle;
  743. let gap = 1;//1.15;
  744. this.ctx.moveTo(this.pos.x+this.salientPoints[0].pos.x*this.scale*gap, this.pos.y+this.salientPoints[0].pos.y*this.scale*gap)
  745. for (let j = 1; j < this.salientPoints.length; j++) {
  746. this.ctx.lineTo(this.pos.x+this.salientPoints[j].pos.x*this.scale*gap, this.pos.y+this.salientPoints[j].pos.y*this.scale*gap)
  747. }
  748. this.ctx.lineTo(this.pos.x+this.salientPoints[0].pos.x*this.scale*gap, this.pos.y+this.salientPoints[0].pos.y*this.scale*gap)
  749. this.ctx.fill();
  750. this.ctx.stroke();
  751. // // test draw contour from body part
  752. // for (let i = 0; i < this.body.parts.length; i++) {
  753. // if (this.body.parts[i].item_type === 'concernement-contours'){
  754. // // console.log('concernement contours', this.body.parts[i]);
  755. // this.ctx.beginPath();
  756. // this.ctx.lineWidth = 1;
  757. // this.ctx.strokeStyle = "#F00";
  758. // this.ctx.moveTo(this.body.parts[i].vertices[0].x, this.body.parts[i].vertices[0].y);
  759. // for (let j = 1; j < this.body.parts[i].vertices.length; j++) {
  760. // this.ctx.lineTo(this.body.parts[i].vertices[j].x, this.body.parts[i].vertices[j].y);
  761. // }
  762. // this.ctx.lineTo(this.body.parts[i].vertices[0].x, this.body.parts[i].vertices[0].y);
  763. // this.ctx.stroke();
  764. // // for (let k = 0; k < this.body.parts[i].parts.length; k++) {
  765. // // let part = this.body.parts[i];
  766. // // let partpart = part.parts[k];
  767. // // debugger;
  768. // // this.ctx.beginPath();
  769. // // this.ctx.lineWidth = 1;
  770. // // this.ctx.strokeStyle = "#F00";
  771. // // this.ctx.moveTo(this.body.parts[i].parts[k].vertices[0].x, this.body.parts[i].parts[k].vertices[0].y);
  772. // // for (let j = 1; j < this.body.parts[i].parts[k].vertices.length; j++) {
  773. // // this.ctx.lineTo(this.body.parts[i].parts[k].vertices[j].x, this.body.parts[i].parts[k].vertices[j].y);
  774. // // }
  775. // // this.ctx.lineTo(this.body.parts[i].parts[k].vertices[0].x, this.body.parts[i].parts[k].vertices[0].y);
  776. // // this.ctx.stroke();
  777. // // }
  778. // }
  779. // }
  780. }
  781. },
  782. isFocused(){
  783. return this.map_mode === 'terraindevie'
  784. || (this.map_mode === 'action' && this.concernement.has_agissantes)
  785. || (this.map_mode === 'puissancedagir' && this.concernement.has_puissancedagir);
  786. }
  787. },
  788. render() {
  789. // console.log('render()', this.ctx);
  790. },
  791. }
  792. </script>