From a2ae70ef482fbf90240974a624e387751f02cd4b Mon Sep 17 00:00:00 2001 From: bach Date: Tue, 18 Apr 2023 17:16:33 +0200 Subject: [PATCH] enites as matter body and popup display on mouse over entites --- src/assets/main.scss | 2 +- src/components/ConcernementMapItem.vue | 105 ++++++++++++++++++------ src/components/ConcernementMapPopup.vue | 41 +++++++-- src/components/MapConcernements.vue | 59 ++++++++++--- src/stores/concernements.js | 16 +++- 5 files changed, 173 insertions(+), 50 deletions(-) diff --git a/src/assets/main.scss b/src/assets/main.scss index 511a9d9..db6cd13 100644 --- a/src/assets/main.scss +++ b/src/assets/main.scss @@ -104,7 +104,7 @@ body{ } } - #concernement-map-popup{ + #map-popup{ position: absolute; // top:0; // left: 0; diff --git a/src/components/ConcernementMapItem.vue b/src/components/ConcernementMapItem.vue index c9e1ca7..9da541a 100644 --- a/src/components/ConcernementMapItem.vue +++ b/src/components/ConcernementMapItem.vue @@ -32,6 +32,7 @@ export default { inject: ['canvasMap', 'matterEngine'], data() { return { + entities: null, // concernement: null, canvas: null, ctx: null, @@ -58,6 +59,7 @@ export default { created () { // console.log("ConcernementsMapItem concernement", this.canvasMap, this.matterEngine); this.entites = this.concernement.entites + this.entites_byid = this.concernement.entites_byid this.parsePoints() this.getSalientPoints() }, @@ -82,16 +84,11 @@ export default { }, opened: { handler (n, o) { - if(n){ - console.log('concernement item opened'); - // opened + if(n){ // opened this.openClose(true); - }else{ - console.log('concernement item closed'); - // closed + }else{ // closed this.openClose(false) } - console.log(`watch opened ${this.concernement.id}`, n, o, this.anim); }, deep: true } @@ -105,6 +102,10 @@ export default { // define init position of the item this.pos = this.getRandomPos(); + // + this.initMatterBody() + }, + initMatterBody (){ // MATTER // create the matter body and add it to the engine @@ -114,12 +115,38 @@ export default { // https://github.com/liabru/matter-attractors/issues/8 // https://github.com/liabru/matter-attractors/blob/master/index.js // MatterAttractors.Attractors.gravityConstant = -5; - this.body = Matter.Bodies.circle(this.pos.x, this.pos.y, this.ray, { + + // Create parts of the body : main big circle & entities + var parts = [ + Matter.Bodies.circle(0, 0, this.ray, { + item_type: 'concernement', + id: this.concernement.id, + }) + ]; + for (let i = 0; i < this.entites.length; i++) { + // parts.push(Matter.Bodies.circle(this.pos.x+this.entites[i].display.pos.x, this.pos.y+this.entites[i].display.pos.y, 15, { + // item_type: 'entite', + // id: this.entites[i].id + // })) + parts.push(Matter.Bodies.circle(this.entites[i].display.pos.x, this.entites[i].display.pos.y, 2, { + item_type: 'entite', + id: this.entites[i].entite.id, + isSensor: true + })) + } + + // create the body + this.body = Matter.Body.create({ + parts: parts, + item_type: 'concernement', + id: this.concernement.id, frictionAir: 0, // mass: Math.pow(3, this.entites.length), mass: 10, restitution: 0.4, - id: this.concernement.id, + collisionFilter: { + group: -1 + }, plugin: { attractors: [ // there is a built in helper function for Newtonian gravity! @@ -128,8 +155,10 @@ export default { ] } }); - let delta = 10; + Matter.Body.setPosition(this.body, this.pos); + // add init velocity + let delta = 10; Matter.Body.setVelocity(this.body, { x: -delta + Math.random()*delta*2, y: -delta + Math.random()*delta*2 @@ -137,14 +166,14 @@ export default { // console.log('concernementItem mass', this.body.mass); Matter.Composite.add(this.matterEngine.world, this.body); // console.log('concernement body', this.body); + + // // listen for animate event dispatched from parent + // this.canvas.addEventListener('animate', this.animate) + + // listen for afterUpdate event from Matter.Engine object + Matter.Events.on(this.matterEngine, "beforeUpdate", this.onBeforeEngineUpdate); + Matter.Events.on(this.matterEngine, "afterUpdate", this.onAfterEngineUpdate); } - - // // listen for animate event dispatched from parent - // this.canvas.addEventListener('animate', this.animate) - - // listen for afterUpdate event from Matter.Engine object - Matter.Events.on(this.matterEngine, "beforeUpdate", this.onBeforeEngineUpdate) - Matter.Events.on(this.matterEngine, "afterUpdate", this.onAfterEngineUpdate); }, getRandomPos(){ return { @@ -167,8 +196,13 @@ export default { // slope = (output_end - output_start) / (input_end - input_start) // output = output_start + slope * (input - input_start) // from range 0 -> 100 to range 0 -> this.ray - let slope = this.ray / 100 - this.entites[i].display.ray = slope * (100 - entite.prise); + let init_max = 100 + let slope = this.ray / init_max + this.entites[i].display.ray = slope * (init_max - entite.prise); + // if (this.concernement.id === 28) { + // console.log(`entity prise: ${entite.prise} | ray: ${this.entites[i].display.ray}`); + // } + // ANGLE // -90 <= mm <= 90 @@ -187,6 +221,8 @@ export default { x: this.entites[i].display.ray * Math.cos(this.entites[i].display.alpha * (Math.PI/180)), y: this.entites[i].display.ray * Math.sin(this.entites[i].display.alpha * (Math.PI/180)) } + + this.entites_byid[entite.entite.id].display = this.entites[i].display; } }, getSalientPoints () { @@ -309,7 +345,8 @@ export default { this.ctx.beginPath(); this.ctx.lineWidth = 2; this.ctx.strokeStyle = `rgba(255,255,255,${this.opacity})`; - this.ctx.arc(this.pos.x, this.pos.y, this.ray*this.scale, 0, 2 * Math.PI, false); + // external circle is %8 less than max ray = (*0.92) + this.ctx.arc(this.pos.x, this.pos.y, this.ray*this.scale*0.92, 0, 2 * Math.PI, false); // this.ctx.stroke(); // interieur circle @@ -327,9 +364,9 @@ export default { // fleches // haute - this.ctx.moveTo(this.pos.x - (8*this.scale), this.pos.y - this.ray*this.scale + (8*this.scale)); - this.ctx.lineTo(this.pos.x, this.pos.y - this.ray*this.scale); - this.ctx.lineTo(this.pos.x + (8*this.scale), this.pos.y - this.ray*this.scale + (8*this.scale)); + this.ctx.moveTo(this.pos.x - (8*this.scale), this.pos.y - this.ray*this.scale*0.92 + (8*this.scale)); + this.ctx.lineTo(this.pos.x, this.pos.y - this.ray*this.scale*0.92); + this.ctx.lineTo(this.pos.x + (8*this.scale), this.pos.y - this.ray*this.scale*0.92 + (8*this.scale)); // milieu this.ctx.moveTo(this.pos.x - (8*this.scale), this.pos.y + (8*this.scale)); this.ctx.lineTo(this.pos.x, this.pos.y); @@ -380,10 +417,26 @@ export default { let entite = this.entites[i]; // console.log('entite', entite); this.ctx.beginPath(); - this.ctx.arc(this.pos.x+entite.display.pos.x*this.scale, this.pos.y+entite.display.pos.y*this.scale, 2, 0, 2 * Math.PI, false); - this.ctx.fillStyle = "#000"; - this.ctx.fill(); + this.ctx.arc(this.pos.x+entite.display.pos.x*this.scale, this.pos.y+entite.display.pos.y*this.scale, 5, 0, 2 * Math.PI, false); + this.ctx.strokeStyle = "#F00"; + this.ctx.stroke(); + } + // OR + for (let i = 0; i < this.body.parts.length; i++) { + // let entite = this.entites[i]; + if (this.body.parts[i].item_type === 'entity') { + let part = this.body.parts[i]; + // console.log('part', part); + // console.log(`part pos x:${part.position.x} y:${part.position.y} || entity pos x:${this.pos.x+this.entites_byid[part.id].display.pos.x*this.scale} y:${this.pos.y+this.entites_byid[part.id].display.pos.y*this.scale}`); + this.ctx.beginPath(); + // this.ctx.arc(this.pos.x+entite.display.pos.x*this.scale, this.pos.y+entite.display.pos.y*this.scale, 2, 0, 2 * Math.PI, false); + this.ctx.arc(this.body.parts[i].position.x, this.body.parts[i].position.y, 2*this.scale, 0, 2 * Math.PI, false); + this.ctx.strokeStyle = "#000"; + this.ctx.stroke(); + + } } + } // concernement id @center diff --git a/src/components/ConcernementMapPopup.vue b/src/components/ConcernementMapPopup.vue index 505ff61..1b0db6f 100644 --- a/src/components/ConcernementMapPopup.vue +++ b/src/components/ConcernementMapPopup.vue @@ -7,28 +7,48 @@ import { ConcernementsStore } from '@/stores/concernements' export default { name: 'concernementMapPopup', - props: ['id'], + props: ['infos'], data() { return { dom: null, - concernement: null + type: null, + concernement: null, + entite: null } }, created () { - this.concernement = this.concernementsByID[this.id]; + // console.log(`popup created type: ${this.infos.type}`); + if (this.infos.type === 'concernement') { + this.concernement = this.concernementsByID[this.infos.id]; + } else { + this.entite = this.allEntitesById[this.infos.id]; + } }, mounted () { // console.log('APP onMounted') - this.dom = this.$refs['concernement-map-popup']; + this.dom = this.$refs['map-popup']; window.addEventListener('mousemove', this.onMousemove); }, computed: { ...mapState(ConcernementsStore,['concernements']), - ...mapState(ConcernementsStore,['concernementsByID']) + ...mapState(ConcernementsStore,['concernementsByID']), + ...mapState(ConcernementsStore,['allEntitesById']) + }, + watch: { + infos: { + handler (n, o){ + if (n.type === 'concernement') { + this.concernement = this.concernementsByID[n.id]; + } else { + this.entite = this.allEntitesById[n.id]; + } + }, + deep: true + }, }, methods: { onMousemove(e){ - // console.log('popup mousemove', e, this.dom); + // console.log(`popup move type: ${this.infos.type}`); this.dom.style.left = `${e.clientX + 5}px`; this.dom.style.top = `${e.clientY - this.dom.clientHeight - 5}px`; } @@ -40,8 +60,13 @@ export default { diff --git a/src/components/MapConcernements.vue b/src/components/MapConcernements.vue index c15b5ca..2282959 100644 --- a/src/components/MapConcernements.vue +++ b/src/components/MapConcernements.vue @@ -46,7 +46,7 @@ export default { world: null, // render: null, mouse: null, - concernementpopupid: null + mapPopupData: null, } }, provide() { @@ -59,7 +59,8 @@ export default { }, computed: { ...mapState(ConcernementsStore,['concernements']), - ...mapState(ConcernementsStore,['concernementsByID']) + ...mapState(ConcernementsStore,['concernementsByID']), + ...mapState(ConcernementsStore,['opened']) }, created() { // MATTER @@ -105,6 +106,7 @@ export default { }, methods: { ...mapActions(ConcernementsStore,['openCloseConcernement']), + ...mapActions(ConcernementsStore,['resetConcernementOpened']), animate () { this.canvasMap.ctx.clearRect(0, 0, this.canvasMap.canvas.width, this.canvasMap.canvas.height) // this.canvasMap.canvas.dispatchEvent(this.animateEvent) @@ -113,16 +115,45 @@ export default { }, onMouseMove (e) { // check concernement item mouse over - const query = Matter.Query.point(Matter.Composite.allBodies(this.world), this.mouse.position) - // console.log('mousemove query', query); - if (query.length) { - if (typeof this.concernementsByID[query[0].id] !== "undefined" && !this.concernementsByID[query[0].id].opened) { - this.concernementpopupid = query[0].id; - } else { - this.concernementpopupid = null; + // const query = Matter.Query.point(Matter.Composite.allBodies(this.world), this.mouse.position) + let query; + if (this.opened) { + // if a concernement is opened we query the opened concernement's parts (aka entitées bodies) + const bodies = Matter.Composite.allBodies(this.world); + for (let body of bodies) { + if (body.item_type === "concernement" && body.id === this.opened.id) { + query = Matter.Query.point(body.parts, this.mouse.position); + break; + } } - }else{ - this.concernementpopupid = null; + } else { + // if no concernement opened we query concernements + query = Matter.Query.point(this.world.bodies, this.mouse.position) + } + + this.mapPopupData = null; + if (query && query.length) { + // if we have a results + for (let body of query) { + if (!this.opened // if no concernement is opened + && body.item_type === "concernement" // if it is a concernement + && typeof this.concernementsByID[body.id] !== "undefined" // if the id exists + && !this.concernementsByID[body.id].opened) { // if the concernement is not opened + this.mapPopupData = { + type: 'concernement', + id: body.id + }; + } + if (body.item_type === "entite" // if it is an entite + && this.opened // if a concernement is opened + && typeof this.opened.entites_byid[body.id] !== "undefined") { // if the entity exists + this.mapPopupData = { + type: 'entite', + id: body.id + }; + } + } + // console.log(`this.mapPopupData type: ${this.mapPopupData.type}, id: ${this.mapPopupData.id}`); } }, onClick (e) { @@ -141,7 +172,9 @@ export default { this.openCloseConcernement(concernement.id, clickedIDs.indexOf(concernement.id) !== -1) }); // if no concernement opened retrun to home (closing concernement contents opened) + // and reset the opened state in concernement store if (!clickedIDs.length) { + this.resetConcernementOpened(); this.$router.push({name: 'home'}); } } @@ -190,8 +223,8 @@ export default { diff --git a/src/stores/concernements.js b/src/stores/concernements.js index b9fcb49..1d2a865 100644 --- a/src/stores/concernements.js +++ b/src/stores/concernements.js @@ -18,6 +18,7 @@ export const ConcernementsStore = defineStore({ state: () => ({ concernements: [], concernementsByID: {}, + allEntitesById: {}, opened: false, ct_concernement: {} }), @@ -39,9 +40,17 @@ export const ConcernementsStore = defineStore({ GQL.post('', { query: print(ast) }) .then(({ data : { data : { allconcernements } } }) => { console.log('loadconcernements loaded', allconcernements) - this.concernements = allconcernements + + this.concernements = []; allconcernements.forEach(concernement => { - this.concernementsByID[concernement.id] = concernement + concernement.entites_byid = {}; + concernement.entites.forEach(entite => { + concernement.entites_byid[entite.entite.id] = entite; + // record a flat list of all entités of all concernement for map-popup + this.allEntitesById[entite.entite.id] = entite; + }); + this.concernements.push(concernement); + this.concernementsByID[concernement.id] = concernement; }); }) .catch(error => { @@ -81,6 +90,9 @@ export const ConcernementsStore = defineStore({ this.opened = this.concernementsByID[id]; this.router.push({name: 'concernement', params: {id: id}}); } + }, + resetConcernementOpened () { + this.opened = null; } } }) \ No newline at end of file