MapConcernements.vue 40 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277
  1. <script>
  2. // import { mapActions, mapState } from 'pinia'
  3. import { computed } from 'vue'
  4. import { nextTick } from 'vue'
  5. import MapBackground from '@components/MapBackground.vue'
  6. // https://brm.io/matter-js/docs/classes/Engine.html
  7. import Matter from "matter-js";
  8. import MatterAttractors from "matter-attractors";
  9. Matter.use(MatterAttractors);
  10. import paper from 'paper';
  11. import { mapState, mapActions } from 'pinia'
  12. import { ConcernementsStore } from '@/stores/concernements'
  13. import { CommonStore } from '@/stores/common'
  14. import ConcernementMapPopup from '@components/ConcernementMapPopup.vue';
  15. import RecitPlayer from '@components/RecitPlayer.vue';
  16. // import iconTerraindevie from "@/assets/icons/terraindevie.svg"
  17. // import iconProximite from "@/assets/icons/proximite.svg"
  18. // import iconSuperposition from "@/assets/icons/superposition.svg"
  19. import iconPuissanceagir from "@/assets/icons/puissancedagir.svg"
  20. import iconAction from "@/assets/icons/action_2.svg"
  21. import iconDoleancer from "@/assets/icons/doleancer.svg"
  22. export default {
  23. data() {
  24. return {
  25. canvasMap: {
  26. canvas: null,
  27. ctx: null
  28. },
  29. animateEvent: new Event('animate'),
  30. granim: null,
  31. // MATTER
  32. engine: null,
  33. world: null,
  34. // PAPERJS
  35. paper: null,
  36. //
  37. mapPopupData: null,
  38. //
  39. superpositions_constraints: []
  40. }
  41. },
  42. provide() {
  43. // https://www.digitalocean.com/community/tutorials/vuejs-vue-html5-canvas
  44. return {
  45. // explicitly provide a computed property
  46. canvasMap: computed(() => this.canvasMap),
  47. matterEngine: computed(() => this.engine)
  48. }
  49. },
  50. computed: {
  51. ...mapState(ConcernementsStore,['map_mode',
  52. 'concernements',
  53. 'concernementsByID',
  54. 'opened_concernement',
  55. 'opened_entite_id',
  56. 'opened_recit',
  57. 'allSuperpositions',
  58. 'allSuperpositions_byid'
  59. ]),
  60. ...mapState(CommonStore,['map_item_ray',
  61. 'hover_elmt',
  62. 'paper_symbol_definitions'])
  63. },
  64. created() {
  65. // MATTER
  66. // create an engine
  67. let engineOptions = {
  68. enableSleeping: true,
  69. timing: {
  70. //timestamp: 0.5,
  71. timeScale: 0.5
  72. },
  73. // constraintIterations: 20,
  74. // positionIterations: 20,
  75. // velocityIterations: 20
  76. }
  77. this.engine = Matter.Engine.create(engineOptions);
  78. this.engine.gravity.scale = 0;
  79. this.world = this.engine.world;
  80. // listen for afterUpdate event from Matter.Engine object
  81. Matter.Events.on(this.engine, "beforeUpdate", this.onBeforeEngineUpdate);
  82. Matter.Events.on(this.engine, "afterUpdate", this.onAfterEngineUpdate);
  83. },
  84. mounted() {
  85. console.log('map mounted');
  86. this.canvasMap.canvas = this.$refs['canvas-map'];
  87. this.canvasMap.ctx = this.canvasMap.canvas.getContext('2d');
  88. let canvas_w = this.canvasMap.canvas.width = this.canvasMap.canvas.parentElement.clientWidth;
  89. let canvas_h = this.canvasMap.canvas.height = this.canvasMap.canvas.parentElement.clientHeight;
  90. console.log(`canvas_w: ${canvas_w}, canvas_h: ${canvas_h}`);
  91. // PAPER
  92. this.paper = paper.setup(this.canvasMap.canvas);
  93. // symbol defintions
  94. this.initPaperSymbols();
  95. // use the paper.view click to get back if no items is clicked
  96. this.paper.view.onClick = function(event) {
  97. console.log("view onClick", this, event.target);
  98. if(event.target._id === "paper-view-0") {
  99. this.resetConcernementOpened();
  100. this.$router.push({
  101. name: 'home',
  102. hash: `#${this.map_mode}`
  103. });
  104. }
  105. }.bind(this);
  106. // use the paper.view mousemouve to removed mappopup
  107. this.paper.view.onMouseMove = function(event) {
  108. // console.log("view onMouseMove", event.target);
  109. if(event.target._id === "paper-view-0") {
  110. this.setHoverElmt(null);
  111. }
  112. }.bind(this);
  113. // MATTER
  114. let wall_w = 1000;
  115. Matter.Composite.add(this.world, [
  116. // walls
  117. Matter.Bodies.rectangle(canvas_w/2, -wall_w/2, canvas_w, wall_w, { isStatic: true }), // top
  118. Matter.Bodies.rectangle(canvas_w/2, canvas_h+wall_w/2, canvas_w, wall_w, { isStatic: true }), // bottom
  119. Matter.Bodies.rectangle(-wall_w/2, canvas_h/2, wall_w, canvas_h, { isStatic: true }), // left
  120. Matter.Bodies.rectangle(canvas_w+wall_w/2, canvas_h/2, wall_w, canvas_h, { isStatic: true }), // right
  121. // make the items never goes under menus
  122. Matter.Bodies.rectangle(550, 25, 900, 50, { isStatic: true }), // menu top
  123. Matter.Bodies.rectangle(550, canvas_h-15, 900, 30, { isStatic: true }) // menu bottom
  124. ]);
  125. // add mouse control
  126. // https://github.com/liabru/matter-js/issues/491#issuecomment-331329404
  127. // this.mouse = Matter.Mouse.create(this.canvasMap.canvas);
  128. this.animate()
  129. },
  130. watch: {
  131. hover_elmt: {
  132. handler (n, o) {
  133. console.log(`watch hover_elmt map: o, n`, o, n);
  134. // over highlight effect on paper items
  135. if (n && n.paper_id) {
  136. let nitem = paper.project.getItem({id: n.paper_id});
  137. console.log('watch hover_element nitem', nitem.definition);
  138. if (!nitem.is_symbol_instance) { // not symbol instance
  139. nitem.bringToFront();
  140. if (nitem.strokeColor) {
  141. nitem.data.prevStrokeColor = nitem.strokeColor.toCSS(true);
  142. nitem.strokeColor = "#01ffe2";
  143. } else {
  144. nitem.data.prevFillColor = nitem.fillColor.toCSS(true);
  145. nitem.fillColor = "#01ffe2";
  146. }
  147. } else { // is a symbol instanceof, then swap
  148. console.log(`symbol instance n.type:${n.type}, nitem`, nitem);
  149. switch (n.type) {
  150. case 'entite':
  151. nitem.definition = this.paper_symbol_definitions.entite_hover;
  152. break;
  153. case 'besoin':
  154. nitem.definition = this.paper_symbol_definitions.besoin_hover;
  155. break;
  156. case 'reponse':
  157. nitem.definition = this.paper_symbol_definitions.reponse_hover;
  158. break;
  159. case 'entite_action':
  160. nitem.definition = this.paper_symbol_definitions.entite_action_hover;
  161. break;
  162. }
  163. }
  164. }
  165. if (o && o.paper_id && (!n || o.paper_id !== n.paper_id)) {
  166. let oitem = paper.project.getItem({id: o.paper_id})
  167. if (oitem) {
  168. console.log('watch hover_element oitem', oitem);
  169. if (!oitem.is_symbol_instance) { // not symbol instance
  170. if (oitem.data.prevStrokeColor) {
  171. oitem.strokeColor = oitem.data.prevStrokeColor;
  172. } else {
  173. oitem.fillColor = oitem.data.prevFillColor;
  174. }
  175. } else { // is a symbol instanceof, then swap
  176. console.log(`symbol instance o.type:${o.type}, oitem`, oitem);
  177. switch (o.type) {
  178. case 'entite':
  179. if (!this.opened_entite_id || this.opened_entite_id !== oitem.item_id) {
  180. oitem.definition = this.paper_symbol_definitions.entite;
  181. }
  182. break;
  183. case 'besoin':
  184. oitem.definition = this.paper_symbol_definitions.besoin;
  185. break;
  186. case 'reponse':
  187. oitem.definition = this.paper_symbol_definitions.reponse;
  188. break;
  189. case 'entite_action':
  190. if (!this.opened_entite_id || this.opened_entite_id !== oitem.item_id) {
  191. oitem.definition = this.paper_symbol_definitions.entite_action;
  192. }
  193. break;
  194. }
  195. }
  196. }
  197. }
  198. },
  199. deep: true
  200. },
  201. map_mode: {
  202. handler (n, o) {
  203. console.log('concernementMap watch map_mode', o, n);
  204. if (n === 'superposition' && !this.opened_concernement) {
  205. // create constraints
  206. this.setSuperpositionsMatterConstraints();
  207. }else{
  208. // destroy constraints
  209. this.clearSuperpositionsMatterConstraints();
  210. }
  211. },
  212. deep: true
  213. },
  214. allSuperpositions: {
  215. handler (n, o) {
  216. console.log('concernementMap watch allSuperpositions', o, n);
  217. if (this.map_mode === "superposition" && n && n.length) {
  218. // create constraints with a delay (watch is needed for first page load)
  219. window.setTimeout(this.setSuperpositionsMatterConstraints, 200);
  220. }
  221. // else{
  222. // // destroy constraints
  223. // this.clearSuperpositionsMatterConstraints();
  224. // }
  225. },
  226. deep: true
  227. },
  228. // opened_concernement: {
  229. // handler (n, o) {
  230. // console.log('map opened_concernement', this.opened_concernement);
  231. // if (this.map_mode === 'superposition' && n) {
  232. // this.resetSuperpositionsMatterConstraints();
  233. // }
  234. // },
  235. // deep: true
  236. // }
  237. },
  238. methods: {
  239. ...mapActions(ConcernementsStore,['setMapMode',
  240. 'resetConcernementOpened']),
  241. ...mapActions(CommonStore,['addPaperSymbolDefinition',
  242. 'setHoverElmt']),
  243. animate () {
  244. Matter.Engine.update(this.engine, 1);
  245. window.requestAnimationFrame(this.animate);
  246. },
  247. initPaperSymbols(){
  248. this.addPaperSymbolDefinition('boussole_bg', this.setPaperBoussoleBGSymbol());
  249. this.addPaperSymbolDefinition('puissanceagir_bg', this.setPaperPuissanceagirBGSymbol());
  250. this.addPaperSymbolDefinition('puissanceagir_icon', this.setPaperPuissanceagirICONSymbol());
  251. this.addPaperSymbolDefinition('doleance_bg', this.setPaperDoleanceBGSymbol());
  252. this.addPaperSymbolDefinition('doleance_icon', this.setPaperDoleanceICONSymbol());
  253. //
  254. this.addPaperSymbolDefinition('entite', this.setPaperEntiteSymbol());
  255. this.addPaperSymbolDefinition('entite_hidden', this.setPaperHiddenEntiteSymbol());
  256. this.addPaperSymbolDefinition('entite_hover', this.setPaperEntiteHoverSymbol());
  257. this.addPaperSymbolDefinition('entite_action_icon', this.setPaperEntiteActionIconSymbol());
  258. this.addPaperSymbolDefinition('entite_action', this.setPaperEntiteActionSymbol());
  259. this.addPaperSymbolDefinition('entite_action_hover', this.setPaperEntiteActionHoverSymbol());
  260. this.addPaperSymbolDefinition('besoin', this.setPaperBesoinSymbol());
  261. this.addPaperSymbolDefinition('besoin_hover', this.setPaperBesoinHoverSymbol());
  262. this.addPaperSymbolDefinition('reponse', this.setPaperReponseSymbol());
  263. this.addPaperSymbolDefinition('reponse_hover', this.setPaperReponseHoverSymbol());
  264. },
  265. setPaperBoussoleBGSymbol(){
  266. // BOUSSOLE
  267. let children = [];
  268. let ray = this.map_item_ray;
  269. let pos = {x:0, y:0};
  270. // big global exterior circle to keep center aligned
  271. children.push(new paper.Path.Circle({
  272. center: [0, 0],
  273. radius: ray*3,
  274. style: {
  275. strokeColor: 'rgba(255,255,255,0)',
  276. strokeWidth: 0.5
  277. }
  278. }));
  279. // cercles pointillés
  280. for (let i = 1; i < 9; i++) {
  281. let sw = i === 4 || i === 8 ? 0.5 : 0.25;
  282. let da = i === 4 || i === 8 ? null : [5,5];
  283. children.push(new paper.Path.Circle({
  284. center: [pos.x, pos.y],
  285. radius: ray/8*i,
  286. strokeColor: '#fff',
  287. strokeWidth: sw,
  288. dashArray: da
  289. }));
  290. }
  291. // axes
  292. // vertical
  293. children.push(new paper.Path.Line({
  294. from: [pos.x, pos.y - ray],
  295. to: [pos.x, pos.y + ray],
  296. strokeColor: '#fff',
  297. strokeWidth: 0.5
  298. }));
  299. // horizontal
  300. children.push(new paper.Path.Line({
  301. from: [pos.x - ray, pos.y],
  302. to: [pos.x + ray, pos.y],
  303. strokeColor: '#fff',
  304. strokeWidth: 0.5
  305. }))
  306. // fleches
  307. // haute
  308. children.push(new paper.Path({
  309. segments: [
  310. [pos.x - 8, pos.y - ray + 8],
  311. [pos.x, pos.y - ray],
  312. [pos.x + 8, pos.y - ray + 8],
  313. ],
  314. strokeWidth: 0.5,
  315. strokeColor: '#fff',
  316. }));
  317. // milieu
  318. children.push(new paper.Path({
  319. segments: [
  320. [pos.x - 8, pos.y + 8],
  321. [pos.x, pos.y],
  322. [pos.x + 8, pos.y + 8],
  323. ],
  324. strokeWidth: 0.5,
  325. strokeColor: '#fff',
  326. }));
  327. // MOINS - PLUS
  328. // PLUS
  329. // horizontal
  330. children.push(new paper.Path.Line({
  331. from: [pos.x + ray - 5, pos.y - ray],
  332. to: [pos.x + ray + 5, pos.y - ray],
  333. strokeWidth: 2,
  334. strokeColor: '#fff',
  335. }))
  336. // vertical
  337. children.push(new paper.Path.Line({
  338. from: [pos.x + ray, pos.y - ray - 5],
  339. to: [pos.x + ray, pos.y - ray + 5],
  340. strokeWidth: 2,
  341. strokeColor: '#fff',
  342. }))
  343. // MOINS
  344. // horizontal
  345. children.push(new paper.Path.Line({
  346. from: [pos.x - ray - 5, pos.y - ray],
  347. to: [pos.x - ray + 5, pos.y - ray],
  348. strokeWidth: 2,
  349. strokeColor: '#fff',
  350. }))
  351. let fontsize = 4;
  352. let fontFamily = "public_sans";
  353. children.push(new paper.PointText({
  354. point: [pos.x + 4.5, pos.y - ray - 5],
  355. content: `entités qui menacent \u2194 entités qui maintiennent`,
  356. fontSize: fontsize,
  357. fontFamily: fontFamily,
  358. justification: 'center',
  359. fillColor: '#000',
  360. }))
  361. children.push(new paper.PointText({
  362. point: [pos.x - ray - 5, pos.y + 1],
  363. content: "axe d'intensité",
  364. fontSize: fontsize,
  365. fontFamily: fontFamily,
  366. justification: 'right',
  367. fillColor: '#000',
  368. }))
  369. children.push(new paper.PointText({
  370. point: [pos.x + ray + 5, pos.y - 3],
  371. content: "situation future\n\u2195\nsituation actuelle",
  372. fontSize: fontsize,
  373. fontFamily: fontFamily,
  374. justification: 'left',
  375. fillColor: '#000',
  376. }))
  377. let t1 = new paper.PointText({
  378. point: [pos.x - ray/8*2.3, pos.y - ray/8*2.3],
  379. content: "avec prise",
  380. fontSize: fontsize,
  381. fontFamily: fontFamily,
  382. justification: 'center',
  383. fillColor: '#000',
  384. })
  385. t1.rotate(-45)
  386. children.push(t1)
  387. let t2 = new paper.PointText({
  388. point: [pos.x - ray/8*2.95, pos.y - ray/8*2.95],
  389. content: "sans prise",
  390. fontSize: fontsize,
  391. fontFamily: fontFamily,
  392. justification: 'center',
  393. fillColor: '#000',
  394. })
  395. t2.rotate(-45)
  396. children.push(t2)
  397. return new paper.Group({
  398. children: children,
  399. pivot: new paper.Point(pos),
  400. name: 'boussole_bg',
  401. // locked: true,
  402. });
  403. },
  404. setPaperPuissanceagirBGSymbol(){
  405. let children = [];
  406. let ray = this.map_item_ray;
  407. let pos = {x:0,y:0};
  408. // cercles interieur
  409. for (let i = 1; i < 6; i++) {
  410. children.push(new paper.Path.Circle({
  411. center: [pos.x, pos.y],
  412. radius: (ray/5)*i,
  413. strokeWidth: 0.25
  414. }));
  415. }
  416. // rayons
  417. for (let j = 0; j < 16; j++) {
  418. let a = (360 / 16) * j;
  419. let ext_x = Math.cos(a*(Math.PI/180)) * ray;
  420. let ext_y = Math.sin(a*(Math.PI/180)) * ray;
  421. let int_x = Math.cos(a*(Math.PI/180)) * 2;
  422. let int_y = Math.sin(a*(Math.PI/180)) * 2;
  423. children.push(new paper.Path.Line({
  424. from: [pos.x + ext_x, pos.y + ext_y],
  425. to: [pos.x + int_x, pos.y + int_y],
  426. strokeWidth: 0.25,
  427. dashArray: [0.5,1]
  428. }))
  429. }
  430. // cercle exterieur
  431. children.push(new paper.Path.Circle({
  432. center: [pos.x, pos.y],
  433. radius: ray,
  434. strokeWidth: 0.5,
  435. fillColor: `rgba(255,255,255,0.6)`
  436. }));
  437. return new paper.Group({
  438. children: children,
  439. pivot: new paper.Point(pos),
  440. name: 'puissanceagir_bg',
  441. // locked: true,
  442. style: {
  443. strokeColor: '#fff'
  444. }
  445. });
  446. },
  447. setPaperPuissanceagirICONSymbol(){
  448. let children = [];
  449. let svgIcon = paper.project.importSVG(iconPuissanceagir);
  450. children.push(svgIcon);
  451. svgIcon.position = this.pos;
  452. return new paper.Group({
  453. children: children,
  454. pivot: new paper.Point(this.pos),
  455. name: 'puissanceagir_icon',
  456. locked: true,
  457. style: {
  458. strokeColor: '#000',
  459. strokeWidth: 0.75,
  460. fillColor: null
  461. }
  462. });
  463. },
  464. setPaperDoleanceBGSymbol(){
  465. let ray = this.map_item_ray;
  466. let pos = {x:0,y:0};
  467. var r = ray * 0.8; // ray
  468. var dr = r/2; // demi ray
  469. var pcr = 3; // petits cercle rayon
  470. // https://fr.wikipedia.org/wiki/Trigonom%C3%A9trie#/media/Fichier:Unit_circle_angles_color.svg
  471. // https://fr.wikipedia.org/wiki/Identit%C3%A9_trigonom%C3%A9trique_pythagoricienne#Preuve_utilisant_le_cercle_unit%C3%A9
  472. // radians = degrees * (pi/180)
  473. // degrees = radians * (180/pi)
  474. // Points for 45° axes
  475. let m = Math.sin(45*(Math.PI/180)) * r; // x = y for rayon
  476. let n = Math.sin(45*(Math.PI/180)) * r/2; // x = y for demi rayon
  477. // console.log('m', m);
  478. // points for legende arcs
  479. var lar = r*1.1; // legendes arcs rayon
  480. let o = Math.cos(22.5*(Math.PI/180)) * lar; // x @ 22.5° for legende arc rayon
  481. let p = Math.sin(22.5*(Math.PI/180)) * lar; // y @ 22.5° for legende arc rayon
  482. let q = Math.sin(45*(Math.PI/180)) * lar; // x = y @ 45° for legende arc rayon
  483. var ltr = lar + 4; // legendes texts rayon
  484. let o_t = Math.cos(22.5*(Math.PI/180)) * ltr; // x @ 22.5° for legende text rayon
  485. let p_t = Math.sin(22.5*(Math.PI/180)) * ltr; // y @ 22.5° for legende text rayon
  486. let q_t = Math.sin(45*(Math.PI/180)) * ltr; // x = y @ 45° for legende text rayon
  487. let style = {strokeColor: '#fff', strokeWidth: 0.25}
  488. let felchesstyle = {strokeColor: '#fff', strokeWidth: 0.5}
  489. let legende_style = {strokeColor: '#000', strokeWidth: 0.25}
  490. let fontsize = 4;
  491. let fontFamily = "public_sans";
  492. let children = [
  493. // big global exterior circle to keep center aligned
  494. new paper.Path.Circle({
  495. center: [0, 0],
  496. radius: r*3,
  497. style: {
  498. strokeColor: 'rgba(255,255,255,0)',
  499. strokeWidth: 0.5
  500. }
  501. }),
  502. //
  503. // ARCS EXTERIEURS
  504. // haut gauche
  505. new paper.Path.Arc({
  506. from: [- r, -pcr],
  507. through: [- m, -m],
  508. to: [ -pcr, -r],
  509. style: style
  510. }),
  511. // haut droite
  512. new paper.Path.Arc({
  513. from: [pcr, -r],
  514. through: [m, -m],
  515. to: [r, -pcr],
  516. style: style
  517. }),
  518. // bas droite
  519. new paper.Path.Arc({
  520. from: [r, pcr],
  521. through: [m, m],
  522. to: [pcr, r],
  523. style: style
  524. }),
  525. // bas gauche
  526. new paper.Path.Arc({
  527. from: [-pcr, r],
  528. through: [-m, m],
  529. to: [-r, pcr],
  530. style: style
  531. }),
  532. //
  533. // cercle interieur
  534. new paper.Path.Circle({
  535. center: [0, 0],
  536. radius: dr,
  537. style: style
  538. }),
  539. //
  540. // petit cercles
  541. new paper.Path.Circle({
  542. center: [0, -r],
  543. radius: pcr,
  544. style: style
  545. }),
  546. new paper.Path.Circle({
  547. center: [0, r],
  548. radius: pcr,
  549. style: {...style, ...{fillColor: 'rgba(255,255,255,0.9)'}},
  550. }),
  551. new paper.Path.Circle({
  552. center: [r, 0],
  553. radius: pcr,
  554. style: style
  555. }),
  556. new paper.Path.Circle({
  557. center: [-r, 0],
  558. radius: pcr,
  559. style: style
  560. }),
  561. //
  562. // AXES
  563. // vertical haut
  564. new paper.Path.Line({
  565. from: [0, -r + pcr],
  566. to: [0, -dr],
  567. style: style
  568. }),
  569. // vertical bas
  570. new paper.Path.Line({
  571. from: [0, r - pcr],
  572. to: [0, dr],
  573. style: style
  574. }),
  575. // horizontal gauche
  576. new paper.Path.Line({
  577. from: [-r + pcr, 0],
  578. to: [-dr, 0],
  579. style: style
  580. }),
  581. // horizontal droite
  582. new paper.Path.Line({
  583. from: [r - pcr, 0],
  584. to: [dr, 0],
  585. style: style
  586. }),
  587. //
  588. // DIAGONALES
  589. // bas droite
  590. new paper.Path.Line({
  591. from: [m, m],
  592. to: [n, n],
  593. style: style
  594. }),
  595. // bas gauche
  596. new paper.Path.Line({
  597. from: [-m, m],
  598. to: [-n, n],
  599. style: style
  600. }),
  601. // fleches
  602. // haut
  603. new paper.Path({
  604. segments: [
  605. [-4, -dr*1.5 - 4],
  606. [0, -dr*1.5],
  607. [-4, -dr*1.5 + 4]
  608. ],
  609. style: felchesstyle
  610. }),
  611. // bas
  612. new paper.Path({
  613. segments: [
  614. [4, dr*1.5 - 4],
  615. [0, dr*1.5],
  616. [4, dr*1.5 + 4]
  617. ],
  618. style: felchesstyle
  619. }),
  620. // gauche
  621. new paper.Path({
  622. segments: [
  623. [-dr*1.5 - 4, 4],
  624. [-dr*1.5, 0],
  625. [-dr*1.5 + 4, 4]
  626. ],
  627. style: felchesstyle
  628. }),
  629. // droite
  630. new paper.Path({
  631. segments: [
  632. [dr*1.5 - 4, -4],
  633. [dr*1.5, 0],
  634. [dr*1.5 + 4, -4]
  635. ],
  636. style: felchesstyle
  637. }),
  638. //
  639. // LEGENDES
  640. //
  641. // arc bas gauche 1
  642. new paper.Path.Arc({
  643. from: [-pcr, lar],
  644. through: [-p, o],
  645. to: [-q + pcr/2, q + pcr/2],
  646. style: legende_style
  647. }),
  648. // tiret
  649. new paper.Path.Line({
  650. from: [-p, o],
  651. to: [-p_t, o_t],
  652. style: legende_style
  653. }),
  654. //text
  655. new paper.PointText({
  656. point: [-p_t - 1, o_t],
  657. content: "2. Enquête menée\nsur le terrain de vie",
  658. fontSize: fontsize,
  659. fontFamily: fontFamily,
  660. justification: 'right'
  661. }),
  662. // arc bas gauche 2
  663. new paper.Path.Arc({
  664. from: [-q - pcr/2, q - pcr/2],
  665. through: [-o, p],
  666. to: [-lar, pcr],
  667. style: legende_style
  668. }),
  669. // tiret
  670. new paper.Path.Line({
  671. from: [-o, p],
  672. to: [-o_t, p_t],
  673. style: legende_style
  674. }),
  675. // texte
  676. new paper.PointText({
  677. point: [-o_t - 1, p_t],
  678. content: "3. Construction de groupes d'intérets\navec qui composer la doléance",
  679. fontSize: fontsize,
  680. fontFamily: fontFamily,
  681. justification: 'right'
  682. }),
  683. // arc haut gauche
  684. new paper.Path.Arc({
  685. from: [-lar, -pcr],
  686. through: [-q, -q],
  687. to: [-pcr, -lar],
  688. style: legende_style
  689. }),
  690. // tiret
  691. new paper.Path.Line({
  692. from: [-q, -q],
  693. to: [-q_t, -q_t],
  694. style: legende_style
  695. }),
  696. // texte
  697. new paper.PointText({
  698. point: [-q_t - 1, -q_t],
  699. content: "5. Réception et traitement\nde la doléance",
  700. fontSize: fontsize,
  701. fontFamily: fontFamily,
  702. justification: 'right'
  703. }),
  704. // arc haut droite
  705. new paper.Path.Arc({
  706. from: [pcr, -lar],
  707. through: [q, -q],
  708. to: [lar, -pcr],
  709. style: legende_style
  710. }),
  711. // tiret
  712. new paper.Path.Line({
  713. from: [q, -q],
  714. to: [q_t, -q_t],
  715. style: legende_style
  716. }),
  717. // texte
  718. new paper.PointText({
  719. point: [q_t + 1, -q_t],
  720. content: "7. Mise-en-œuvre\nde la décision",
  721. fontSize: fontsize,
  722. fontFamily: fontFamily,
  723. justification: 'left'
  724. }),
  725. // arc bas droite 1
  726. new paper.Path.Arc({
  727. from: [lar, pcr],
  728. through: [o, p],
  729. to: [q + pcr/2, q - pcr/2],
  730. style: legende_style
  731. }),
  732. // tiret
  733. new paper.Path.Line({
  734. from: [o, p],
  735. to: [o_t, p_t],
  736. style: legende_style
  737. }),
  738. // texte
  739. new paper.PointText({
  740. point: [o_t + 1, p_t],
  741. content: "9. Réception et application\nde la décision",
  742. fontSize: fontsize,
  743. fontFamily: fontFamily,
  744. justification: 'left'
  745. }),
  746. // arc bas droite 2
  747. new paper.Path.Arc({
  748. from: [q - pcr/2, q + pcr/2],
  749. through: [p, o],
  750. to: [pcr, lar],
  751. style: legende_style
  752. }),
  753. // tiret
  754. new paper.Path.Line({
  755. from: [p, o],
  756. to: [p_t, o_t],
  757. style: legende_style
  758. }),
  759. // texte
  760. new paper.PointText({
  761. point: [p_t + 1, o_t],
  762. content: "10. Réussite / échec / reprise\ndu cercle politique",
  763. fontSize: fontsize,
  764. fontFamily: fontFamily,
  765. justification: 'left'
  766. }),
  767. //
  768. // Points Cardinaux
  769. //
  770. // haut
  771. new paper.Path.Circle({
  772. center: [0, -r],
  773. radius: 0.5,
  774. style: {
  775. fillColor: '#000'
  776. }
  777. }),
  778. new paper.Path.Line({
  779. from: [0, -r],
  780. to: [0, -r - 9],
  781. style: legende_style
  782. }),
  783. new paper.PointText({
  784. point: [0, -r - 11],
  785. content: "6. Décision",
  786. fontSize: fontsize,
  787. fontFamily: fontFamily,
  788. justification: 'center'
  789. }),
  790. // bas
  791. new paper.Path.Circle({
  792. center: [0, r],
  793. radius: 0.5,
  794. style: {
  795. fillColor: '#000'
  796. }
  797. }),
  798. new paper.Path.Line({
  799. from: [0, r],
  800. to: [0, r + 9],
  801. style: legende_style
  802. }),
  803. new paper.PointText({
  804. point: [0, r + 14],
  805. content: "1. Début du cercle\nLe problème\n(injustice, indignation, plainte...)",
  806. fontSize: fontsize,
  807. fontFamily: fontFamily,
  808. justification: 'center'
  809. }),
  810. // droite
  811. new paper.Path.Circle({
  812. center: [r, 0],
  813. radius: 0.5,
  814. style: {
  815. fillColor: '#000'
  816. }
  817. }),
  818. new paper.Path.Line({
  819. from: [r, 0],
  820. to: [r + 8, 0],
  821. style: legende_style
  822. }),
  823. new paper.PointText({
  824. point: [r + 10, -0.5],
  825. content: "8. Adresse de la décision\nà appliquer",
  826. fontSize: fontsize,
  827. fontFamily: fontFamily,
  828. justification: 'left'
  829. }),
  830. // gauche
  831. new paper.Path.Circle({
  832. center: [-r, 0],
  833. radius: 0.5,
  834. style: {
  835. fillColor: '#000'
  836. }
  837. }),
  838. new paper.Path.Line({
  839. from: [-r, 0],
  840. to: [-r - 8, 0],
  841. style: legende_style
  842. }),
  843. new paper.PointText({
  844. point: [-r - 10, 0.4],
  845. content: "4. Adresse de la doléance",
  846. fontSize: fontsize,
  847. fontFamily: fontFamily,
  848. justification: 'right'
  849. }),
  850. ];
  851. return new paper.Group({
  852. children: children,
  853. pivot: new paper.Point(pos),
  854. name: 'doleance_bg',
  855. // locked: true
  856. });
  857. },
  858. setPaperDoleanceICONSymbol(){
  859. let children = [];
  860. let svgIcon = paper.project.importSVG(iconDoleancer);
  861. children.push(svgIcon);
  862. svgIcon.position = this.pos;
  863. return new paper.Group({
  864. children: children,
  865. pivot: new paper.Point(this.pos),
  866. name: 'doleance_icon',
  867. locked: true,
  868. style: {
  869. strokeColor: '#000',
  870. strokeWidth: 0.75,
  871. fillColor: null
  872. }
  873. });
  874. },
  875. setPaperEntiteSymbol(){
  876. return new paper.Path.Circle({
  877. pivot: new paper.Point({x:0,y:0}),
  878. center: [0,0],
  879. radius: 0.5, //0.3
  880. fillColor: '#000',
  881. strokeColor: 'rgba(255,255,255,0.05)',
  882. strokeWidth:2
  883. })
  884. },
  885. setPaperHiddenEntiteSymbol(){
  886. return new paper.Path.Circle({
  887. pivot: new paper.Point({x:0,y:0}),
  888. center: [0,0],
  889. radius: 0.7, //0.3
  890. fillColor: '#fff',
  891. strokeColor: 'rgba(255,255,255,0.05)',
  892. strokeWidth:2
  893. })
  894. },
  895. setPaperEntiteHoverSymbol(){
  896. return new paper.Path.Circle({
  897. pivot: new paper.Point({x:0,y:0}),
  898. center: [0,0],
  899. radius: 0.5,
  900. fillColor: '#01ffe2',
  901. strokeColor: 'rgba(255,255,255,0.05)',
  902. strokeWidth:2
  903. })
  904. },
  905. setPaperEntiteActionIconSymbol(){
  906. let svgIcon = paper.project.importSVG(iconAction);
  907. svgIcon.strokeWidth = 0.8;
  908. svgIcon.scale(0.6);
  909. svgIcon.strokeColor = '#000';
  910. svgIcon.fillColor = null;
  911. svgIcon.position = {x:0, y:0};
  912. // let circle = new paper.Path.Circle({
  913. // radius: 3,
  914. // fillColor: 'rgba(255,255,255,0.01)'
  915. // })
  916. return new paper.Group({
  917. children: [svgIcon],
  918. name: 'action_icon'
  919. });
  920. },
  921. setPaperEntiteActionSymbol(){
  922. let svgIcon = paper.project.importSVG(iconAction);
  923. svgIcon.strokeWidth = 0.25;
  924. svgIcon.scale(0.15);
  925. svgIcon.strokeColor = '#000';
  926. svgIcon.fillColor = null;
  927. svgIcon.position = {x:0, y:0};
  928. let circle = new paper.Path.Circle({
  929. radius: 3,
  930. fillColor: 'rgba(255,255,255,0.01)'
  931. })
  932. return new paper.Group({
  933. children: [circle, svgIcon],
  934. name: 'action_icon'
  935. });
  936. },
  937. setPaperEntiteActionHoverSymbol(){
  938. let svgIcon = paper.project.importSVG(iconAction);
  939. svgIcon.strokeColor = '#01ffe2';
  940. svgIcon.strokeWidth = 0.25;
  941. svgIcon.scale(0.15);
  942. svgIcon.fillColor = null;
  943. svgIcon.position = {x:0, y:0};
  944. let circle = new paper.Path.Circle({
  945. radius: 3,
  946. fillColor: 'rgba(255,255,255,0.05)'
  947. })
  948. return new paper.Group({
  949. children: [circle, svgIcon],
  950. name: 'action_icon'
  951. });
  952. },
  953. setPaperBesoinSymbol(){
  954. return new paper.Path({
  955. pivot: new paper.Point(this.pos),
  956. segments: [[0, -1],[1, 0],[0, 1],[-1, 0],[0, -1]],
  957. fillColor: '#000'
  958. })
  959. },
  960. setPaperBesoinHoverSymbol(){
  961. return new paper.Path({
  962. pivot: new paper.Point(this.pos),
  963. segments: [[0, -1],[1, 0],[0, 1],[-1, 0],[0, -1]],
  964. fillColor: '#01ffe2'
  965. })
  966. },
  967. setPaperReponseSymbol(){
  968. return new paper.Path({
  969. pivot: new paper.Point(this.pos),
  970. segments: [[0, -1],[1, 0],[0, 1],[-1, 0],[0, -1]],
  971. fillColor: '#eee',
  972. strokeColor: "#000",
  973. strokeWidth: 0.25,
  974. })
  975. },
  976. setPaperReponseHoverSymbol(){
  977. return new paper.Path({
  978. pivot: new paper.Point(this.pos),
  979. segments: [[0, -1],[1, 0],[0, 1],[-1, 0],[0, -1]],
  980. fillColor: '#eee',
  981. strokeColor: "#01ffe2",
  982. strokeWidth: 0.25,
  983. })
  984. },
  985. async setSuperpositionsMatterConstraints(){
  986. await nextTick(); // wait for dom to be upadted before applying consraint
  987. console.log('setSuperpositionsMatterConstraints this.allSuperpositions', this.allSuperpositions);
  988. // let allBodies = Matter.Composite.allBodies(this.world);
  989. // console.log('allBodies', allBodies);
  990. // let allComposites = Matter.Composite.allComposites(this.world);
  991. // console.log('allComposites', allComposites);
  992. // loop through all supperposition couple
  993. for(let [couple_id, superpositions] of Object.entries(this.allSuperpositions_byid)){
  994. // if couple has only one superposition, use regular mapItems
  995. let superpositions_ids = Object.keys(superpositions);
  996. let i = 0;
  997. for(let [superposition_id, superposition] of Object.entries(superpositions)){
  998. i++;
  999. // console.log('superposition', superposition[0].cid, superposition[1].cid);
  1000. let matter_bodyA_id, matter_bodyB_id;
  1001. if (superpositions_ids.length === 1 || i === 1) {
  1002. matter_bodyA_id = superposition[0].cid
  1003. matter_bodyB_id = superposition[1].cid
  1004. } else {
  1005. matter_bodyA_id = `${superposition[0].cid}___${superposition_id}`
  1006. matter_bodyB_id = `${superposition[1].cid}___${superposition_id}`
  1007. }
  1008. // get the concernement matter bodies with id
  1009. let bodyA = Matter.Composite.get(this.world, matter_bodyA_id, 'body');
  1010. let bodyB = Matter.Composite.get(this.world, matter_bodyB_id, 'body');
  1011. // console.log('bodyA, bodyB', bodyA, bodyB);
  1012. // get the entite coordinates inside the concernement body
  1013. let pointA = null;
  1014. let concernementA = this.concernementsByID[superposition[0].cid];
  1015. // console.log('concernementA', concernementA);
  1016. for(let entiteA of concernementA.revisions_byid[concernementA.active_revision].entites){
  1017. if (entiteA.entite && entiteA.entite.id === superposition[0].eid && entiteA.display) {
  1018. // console.log('entiteA', entiteA);
  1019. pointA = Matter.Vector.create(entiteA.display.pos.x, entiteA.display.pos.y);
  1020. break;
  1021. }
  1022. }
  1023. let pointB = null;
  1024. let concernementB = this.concernementsByID[superposition[1].cid];
  1025. // console.log('concernementB', concernementB);
  1026. for(let entiteB of concernementB.revisions_byid[concernementB.active_revision].entites){
  1027. if (entiteB.entite && entiteB.entite.id === superposition[1].eid && entiteB.display) {
  1028. // console.log('entiteB', entiteB);
  1029. pointB = Matter.Vector.create(entiteB.display.pos.x, entiteB.display.pos.y);
  1030. break;
  1031. }
  1032. }
  1033. // console.log(`pointA:`, pointA,` pointB:`, pointB);
  1034. if (bodyA && bodyB && pointA && pointB) {
  1035. let c = Matter.Constraint.create({
  1036. bodyA: bodyA,
  1037. pointA: pointA,
  1038. bodyB: bodyB,
  1039. pointB: pointB,
  1040. stiffness: 1,
  1041. length: 0,
  1042. damping: 1,
  1043. concernementA: concernementA,
  1044. concernementB: concernementB
  1045. });
  1046. this.superpositions_constraints.push(c);
  1047. Matter.Composite.add(this.world, c);
  1048. // keep a link the constraint into the concernement object (useful for tweening the constraint pointA & pointB in concernementMapItem)
  1049. concernementA.superposition_constraints_id.push(c.id);
  1050. concernementB.superposition_constraints_id.push(c.id);
  1051. // record all superposed concernements for each concernement
  1052. if (concernementA.superposed_concernements_id.indexOf(concernementB.id) < 0) {
  1053. concernementA.superposed_concernements_id.push(concernementB.id);
  1054. }
  1055. if (concernementB.superposed_concernements_id.indexOf(concernementA.id) < 0) {
  1056. concernementB.superposed_concernements_id.push(concernementA.id);
  1057. }
  1058. }
  1059. }
  1060. }
  1061. },
  1062. clearSuperpositionsMatterConstraints(){
  1063. console.log('clearSuperpositionsMatterConstraints', this.superpositions_constraints);
  1064. for(let constraint of this.superpositions_constraints){
  1065. // Delete superposition constraints recorded in concernement object
  1066. // delete constraint.concernementA.superposition_constraints[constraint.id];
  1067. // delete constraint.concernementB.superposition_constraints[constraint.id];
  1068. let keysA = constraint.concernementA.superposition_constraints_id;
  1069. keysA.forEach(i => {
  1070. if(constraint.concernementA.superposition_constraints_id[i] === constraint.id) {
  1071. delete constraint.concernementA.superposition_constraints_id[i];
  1072. }
  1073. });
  1074. let keysB = constraint.concernementB.superposition_constraints_id;
  1075. keysB.forEach(i => {
  1076. if(constraint.concernementB.superposition_constraints_id[i] === constraint.id) {
  1077. delete constraint.concernementB.superposition_constraints_id[i];
  1078. }
  1079. });
  1080. Matter.Composite.remove(this.world, constraint, true);
  1081. }
  1082. this.superpositions_constraints = [];
  1083. },
  1084. // resetSuperpositionsMatterConstraints(){
  1085. // this.clearSuperpositionsMatterConstraints();
  1086. // this.setSuperpositionsMatterConstraints();
  1087. // },
  1088. onBeforeEngineUpdate(){
  1089. },
  1090. onAfterEngineUpdate(){
  1091. // // START OF DEBUGGING
  1092. // // draw lines of constraints for debuging
  1093. // let constraints_lines = this.paper.project.getItem({name: 'constraints_lines', class: paper.Group});
  1094. // if (constraints_lines) {
  1095. // constraints_lines.removeChildren();
  1096. // }else{
  1097. // constraints_lines = new paper.Group({
  1098. // pivot: new paper.Point({x:0,y:0}),
  1099. // name: 'constraints_lines',
  1100. // });
  1101. // }
  1102. // let all_constrains = Matter.Composite.allConstraints(this.world);
  1103. // let children = [];
  1104. // for(let constraint of all_constrains){
  1105. // // console.log('constrain', constraint);
  1106. // let pointAWorld = Matter.Constraint.pointAWorld(constraint);
  1107. // let pointBWorld = Matter.Constraint.pointBWorld(constraint);
  1108. // // console.log('pointAWorld, pointBWorld', pointAWorld, pointBWorld);
  1109. // children.push(new paper.Path.Line({
  1110. // from: [pointAWorld.x, pointAWorld.y],
  1111. // to: [pointBWorld.x, pointBWorld.y],
  1112. // strokeColor: '#f00',
  1113. // strokeWidth: 1
  1114. // }));
  1115. // }
  1116. // constraints_lines.addChildren(children);
  1117. // // END OF DEBUGGING
  1118. }
  1119. },
  1120. beforeUpdate () {
  1121. },
  1122. components: {
  1123. MapBackground,
  1124. ConcernementMapPopup,
  1125. RecitPlayer
  1126. }
  1127. }
  1128. </script>
  1129. <template>
  1130. <div id="map-backgrounds">
  1131. <MapBackground />
  1132. </div>
  1133. <div id="map-matter">
  1134. <canvas ref="canvas-engine"></canvas>
  1135. </div>
  1136. <div id="map-concernements">
  1137. <canvas ref="canvas-map"></canvas>
  1138. <slot></slot>
  1139. </div>
  1140. <nav id="map-nav">
  1141. <ul>
  1142. <li>
  1143. <a
  1144. title="terrain de vie" href="#terraindevie" @click="setMapMode('terraindevie')"
  1145. >
  1146. <span class="icon terraindevie"></span> <span class="label"> terrain de vie</span>
  1147. </a>
  1148. </li>
  1149. <li>
  1150. <a
  1151. title="proximite" href="#proximite" @click="setMapMode('proximite')"
  1152. :class="{ disabled: opened_concernement && !opened_concernement.has_proximite }"
  1153. >
  1154. <span class="icon proximite"></span> <span class="label"> proximité</span>
  1155. </a>
  1156. </li>
  1157. <li>
  1158. <a
  1159. title="superposition" href="#superposition" @click="setMapMode('superposition')"
  1160. :class="{ disabled: opened_concernement && !opened_concernement.has_superposition }"
  1161. >
  1162. <span class="icon superposition"></span> <span class="label"> superposition</span>
  1163. </a>
  1164. </li>
  1165. <li>
  1166. <a
  1167. title="puissance d'agir" href="#puissancedagir" @click="setMapMode('puissancedagir')"
  1168. :class="{ disabled: opened_concernement && !opened_concernement.has_puissancedagir }"
  1169. >
  1170. <span class="icon puissancedagir"></span> <span class="label"> puissance d'agir</span>
  1171. </a>
  1172. </li>
  1173. <li>
  1174. <a
  1175. title="action" href="#action" @click="setMapMode('action')"
  1176. :class="{ disabled: opened_concernement && !opened_concernement.has_agissantes }"
  1177. >
  1178. <span class="icon action"></span> <span class="label"> action</span>
  1179. </a>
  1180. </li>
  1181. <li>
  1182. <a
  1183. title="cercle politique" href="#doleancer" @click="setMapMode('doleancer')"
  1184. :class="{ disabled: opened_concernement && !opened_concernement.has_doleance }"
  1185. >
  1186. <span class="icon doleancer"></span> <span class="label"> cercle politique</span>
  1187. </a>
  1188. </li>
  1189. </ul>
  1190. </nav>
  1191. <RecitPlayer />
  1192. <ConcernementMapPopup
  1193. v-if="hover_elmt && hover_elmt.type !== 'doleance_step' && !hover_elmt.no_popup"
  1194. :infos="hover_elmt"
  1195. />
  1196. </template>
  1197. <style lang="css" scoped>
  1198. </style>