// https://codeburst.io/dynamic-modules-with-vuex-and-vue-b9c481ca792 // https://www.brophy.org/post/instance-aware-vuex-modules-1/ // import qs from 'querystring' // import { REST } from 'api/rest-axios' import { GRAPHQL } from 'api/graphql-axios' import Query from 'graphql-query-builder' import { VIMEO } from 'api/vimeo-axios' import qs from 'querystring' import * as THREE from 'three' import { ThreeBSP } from 'three-js-csg-es6' export default { namespaced: true, // initial state state: { debug: true, obj3d: null, size: { x: 0, y: 0, z: 0 }, position: { x: 0, y: 0, z: 0 }, walls3dObj: null, wallsPos: null, top3dObj: null, topPos: null, topColor: null, floor3dObj: null, floorPos: null, florrColor: null, levels3dObj: null, levelsPos: null, wall: { wallW: 0.001, // dig windows on face and back winW: 2 + Math.random() * 2, // winH: 4 + Math.random() * 4, winH: 0, margin: 2, nbrWinX: 0, paddingX: 0, nbrWinY: 0, paddingY: 0, nbrWinZ: 0, paddingZ: 0 }, contents: {}, contents_size_factor: 1, // factor to get the contents (grid) size proportional to windows contentTypes: ['visible', 'context', 'process', 'concept'], grids: null, gridCel: { width: 6, height: 4.5 }, gridsContentPlaced: {}, // gridBebug3dObj: null, activeLevel: 'visibles' }, // getters getters: { position: (state) => { return state.position }, size: (state) => { return state.size }, createGradientCanvas: (state) => (c1, c2) => { var ctx = document.createElement('canvas').getContext('2d') ctx.canvas.width = 1024 ctx.canvas.height = 1024 var lingrad = ctx.createLinearGradient(0, 0, 0, 1024) lingrad.addColorStop(0, c1) lingrad.addColorStop(1, c2) ctx.fillStyle = lingrad ctx.fillRect(0, 0, 1024, 1024) return ctx.canvas } }, // mutations mutations: { setSize: (state, size) => { state.size = size }, setPosition: (state, pos) => { state.position = pos }, setWalls3dObj: (state, obj) => { state.walls3dObj = obj }, setWallsPos: (state, pos) => { state.wallsPos = pos }, setTop3dObj: (state, obj) => { state.top3dObj = obj }, setTopPos: (state, pos) => { state.topPos = pos }, setFloor3dObj: (state, obj) => { state.floor3dObj = obj }, setFloorPos: (state, pos) => { state.floorPos = pos }, setTopColor: (state, col) => { state.topColor = col }, setFloorColor: (state, col) => { state.floorColor = col }, setLevels3dObj: (state, obj) => { state.levels3dObj = obj }, setLevelsPos: (state, pos) => { state.levelsPos = pos }, setContents: (state, contents) => { state.contents = contents }, setGrids: (state, grids) => { state.grids = grids }, // setDebugGrids3dObj: (state, obj) => { state.gridBebug3dObj = obj }, shiftGrid: (state, c) => { let side switch (c.face) { case 'back': case 'front': side = 'x' break case 'left': case 'right': side = 'z' break } // console.log(`shiftGrid side: ${side}, face: ${c.face}, type: ${c.type}, id: ${c.id}`) let p = state.grids[side][c.face][c.type].pop() if (!state.gridsContentPlaced[side]) { state.gridsContentPlaced[side] = {} } if (!state.gridsContentPlaced[side][c.face]) { state.gridsContentPlaced[side][c.face] = {} } if (!state.gridsContentPlaced[side][c.face][c.type]) { state.gridsContentPlaced[side][c.face][c.type] = {} } state.gridsContentPlaced[side][c.face][c.type][c.id] = p }, setActiveLevel: (state, l) => { state.activeLevel = l } }, // actions actions: { init ({ dispatch, commit, state, rootGetters }) { console.log(`Init Project module ${state.id}`, state) dispatch('sizingBuilding') dispatch('build3dObjs') }, sizingBuilding ({ dispatch, commit, state, rootGetters }) { console.log('sizingBuilding, state', state) let totalW = rootGetters['Projects/totalW'] // console.log('totalW', totalW) let margin = rootGetters['Projects/marginBetweenBuildings'] // console.log('margin', margin) // positioning buildings on x regarding the widths // & setting up the window sizing // & setting up the content grid // let wall, a // let grid // X POS let x if (state.index === 0) { // if it's the first x = -1 * totalW / 2 + state.size.x / 2 } else { // else get the precedent pos let prevProjID = rootGetters['Projects/projectID'](state.index - 1) // console.log('prevProjID', prevProjID) let prevProjPos = rootGetters[`project:${prevProjID}/position`] // console.log(`project:${prevProjID}/position.x`, prevProjPos.x) let prevProjSize = rootGetters[`project:${prevProjID}/size`] // console.log(`project:${prevProjID}/size.x`, prevProjSize.x) // console.log('state.size.x', state.size.x) // prev X + alf of prev size x + margin + half of current size x x = prevProjPos.x + prevProjSize.x / 2 + margin + state.size.x / 2 } // console.log('x', x) commit('setPosition', { x: x, // y: -1 * state.size.y / 2 + 10 + Math.random() * 30, // -10 + Math.random() * this.size.y / 2 y: -state.size.y / 4, z: 0// -10 + Math.random() * 10 }) // WINDOWS let a = 0 state.wall.nbrWinX = Math.floor((state.size.x - 2 * state.wall.margin) / state.wall.winW) // removing windows on X until padding is enough while (state.wall.paddingX < 0.4) { state.wall.nbrWinX -= a state.wall.paddingX = (state.size.x - 2 * state.wall.margin - state.wall.winW * state.wall.nbrWinX) / (state.wall.nbrWinX - 1) a++ } a = 0 // state.wall.nbrWinY = Math.floor((state.size.y - 2 * state.wall.margin) / state.wall.winH) state.wall.nbrWinY = 4 * 10 state.wall.winH = (state.size.y - 2 * state.wall.margin) / state.wall.nbrWinY // removing windows on Y until padding is enough while (state.wall.paddingY < 0.4) { state.wall.nbrWinY -= a state.wall.paddingY = (state.size.y - 2 * state.wall.margin - state.wall.winH * state.wall.nbrWinY) / (state.wall.nbrWinY - 1) a += 4 } a = 0 state.wall.nbrWinZ = Math.floor((state.size.z - 2 * state.wall.margin) / state.wall.winW) while (state.wall.paddingZ < 0.4) { state.wall.nbrWinZ -= a state.wall.paddingZ = (state.size.z - 2 * state.wall.margin - state.wall.winW * state.wall.nbrWinZ) / (state.wall.nbrWinZ - 1) a++ } // CONTENTS GRID // create grids for right/left & face/back let grids = { x: { front: {}, back: {} }, z: { left: {}, right: {} } } // console.log('grids', grids) let grid, rows, cols, t, cel, paddingH // console.log('Object.keys(grids)', Object.keys(grids)) // Object.keys(grids).map(function (side) { Object.keys(grids).forEach(side => { // console.log('# GRID side:', side) // create one grid for each face x & z // will then clone both for each face front/back or left/right) grid = {} // calculate cols nbr regarding the face orientation switch (side) { case 'z': // cols = state.wall.nbrWinZ / state.contents_size_factor cols = Math.floor(state.size.z / state.gridCel.width) // compute padding to centering the grid horizontaly paddingH = (state.size.z - cols * state.gridCel.width) / 2 break case 'x': // cols = state.wall.nbrWinX / state.contents_size_factor cols = Math.floor(state.size.x / state.gridCel.width) // compute padding to centering the grid horizontaly paddingH = (state.size.x - cols * state.gridCel.width) / 2 break } // calculate rows // rows = state.wall.nbrWinY / state.contents_size_factor rows = Math.floor(state.size.y / state.gridCel.height) // dispatch grid rows on to 4 levels (t) for (var m = rows - 1; m > 0; m--) { // rows if (m > rows / 4 * 3 + 1) { t = state.contentTypes[0] } else if (m > rows / 4 * 2 + 1) { t = state.contentTypes[1] } else if (m > rows / 4 + 1) { t = state.contentTypes[2] } else { t = state.contentTypes[3] } // create level if not exists if (!grid[t]) { grid[t] = [] // // skip the first level's row as we will display wall title // continue } // create cols for each rows for (var l = cols - 1; l >= 0; l--) { // cols // cel = { y: margin + state.wall.winH * state.contents_size_factor * m } cel = { y: state.position.y - state.size.y / 2 + state.gridCel.height * m } switch (side) { case 'z': // cel.z = margin + state.wall.winW * state.contents_size_factor * l cel.z = state.position.z - state.size.z / 2 + paddingH + state.gridCel.width * l break case 'x': // cel.x = margin + state.wall.winW * state.contents_size_factor * l cel.x = state.position.x - state.size.x / 2 + paddingH + state.gridCel.width * l break } grid[t].push(cel) } } // console.log('grid', grid) // apply grid for each face of each side // Object.keys(grids[side]).map(function (face) { Object.keys(grids[side]).forEach(face => { // console.log('## GRID face:', face) // // shuffling the grid // for (var i = 0; i < state.contentTypes.length; i++) { // for (let n = grid[state.contentTypes[i]].length - 1; n > 0; n--) { // const o = Math.floor(Math.random() * n) // const temp = grid[state.contentTypes[i]][n] // grid[state.contentTypes[i]][n] = grid[state.contentTypes[i]][o] // grid[state.contentTypes[i]][o] = temp // } // } // // console.log('shuffeld grid', grid) // cloning the grid for each face // grids[side][face] = { ...grid } this is not working as it is not deep copy grids[side][face] = JSON.parse(JSON.stringify(grid)) // comput common values let x, z, ry switch (face) { case 'front': // side x z = state.position.z + state.size.z / 2 - 0.1 ry = 180 break case 'back': // side x z = state.position.z - state.size.z / 2 + 0.1 ry = 0 break case 'left': // side z x = state.position.x - state.size.x / 2 + 0.1 ry = 90 break case 'right': // side z x = state.position.x + state.size.x / 2 - 0.1 ry = -90 break } // loop through levels // apply common values // Object.keys(grids[side][face]).map(function (level) { Object.keys(grids[side][face]).forEach(level => { // console.log(`${side} ${face} ${level} x: ${x}, z: ${z}, ry: ${ry}`) // loop through all cels of the level for (var i = 0; i < grids[side][face][level].length; i++) { grids[side][face][level][i].ry = ry switch (side) { case 'z': grids[side][face][level][i].x = x break case 'x': grids[side][face][level][i].z = z break } } }) }) }) commit('setGrids', grids) console.log('state.grids', state.grids) }, build3dObjs ({ dispatch, commit, state, getters, rootGetters }) { console.log('build3dObjs') // http://learningthreejs.com/blog/2011/12/10/constructive-solid-geometry-with-csg-js/ let frontGeom = new THREE.BoxGeometry(state.size.x, state.size.y, state.wall.wallW) let frontMesh = new THREE.Mesh(frontGeom) frontMesh.position.z = 0.5 * state.size.z let frontBSP = new ThreeBSP(frontMesh) // https://medium.com/techtrument/multithreading-javascript-46156179cf9a let winGeom = new THREE.BoxGeometry(state.wall.winW, state.wall.winH, state.wall.wallW) let winMesh = new THREE.Mesh(winGeom) let windowsGeom = new THREE.Geometry() for (var i = 0; i < state.wall.nbrWinX; i++) { for (var j = 0; j < state.wall.nbrWinY; j++) { winMesh.position.z = 0.5 * state.size.z winMesh.position.x = -0.5 * state.size.x + state.wall.margin + state.wall.winW * 0.5 + i * (state.wall.winW + state.wall.paddingX) winMesh.position.y = 0.5 * state.size.y - state.wall.margin - state.wall.winH * 0.5 - j * (state.wall.winH + state.wall.paddingY) // winMesh.updateMatrix() windowsGeom.mergeMesh(winMesh) } } let windowsBSP = new ThreeBSP(windowsGeom) let frontWindowedBSP = frontBSP.subtract(windowsBSP) // console.log('state.Faces_opaques', state.Faces_opaques) let backWindowedMesh if (state.Faces_opaques === 3) { backWindowedMesh = frontMesh.clone() } else { let frontWindowedMesh = frontWindowedBSP.toMesh() backWindowedMesh = frontWindowedMesh.clone() } backWindowedMesh.position.z = -0.5 * state.size.z let backWindowedBSP = new ThreeBSP(backWindowedMesh) let rightGeom = new THREE.BoxGeometry(state.wall.wallW, state.size.y, state.size.z) let rightMesh = new THREE.Mesh(rightGeom) rightMesh.position.x = 0.5 * state.size.x // rightMesh.position.z = 0.5 * state.size.z let rightBSP = new ThreeBSP(rightMesh) let leftMesh = rightMesh.clone() leftMesh.position.x = -0.5 * state.size.x // leftMesh.position.z = 0.5 * state.size.z let leftBSP = new ThreeBSP(leftMesh) let buildingBSP = frontWindowedBSP.union(rightBSP) buildingBSP = buildingBSP.union(backWindowedBSP) buildingBSP = buildingBSP.union(leftBSP) // convert back to three.js mesh let building = buildingBSP.toMesh() // create a classical material for building // let topColor = `hsla(201, 100%, 95%, 1)` let hTop = Math.round(190 + Math.random() * 20) let sTop = Math.round(20 + Math.random() * 60) let lTop = Math.round(75) // let hFloor = Math.round(205 + Math.random() * 10) let hFloor = hTop // let sFloor = Math.round(30 + Math.random() * 40) let sFloor = sTop // let lFloor = Math.round(10 + Math.random() * 20) let lFloor = Math.round(10) let topColor = `hsla(${hTop}, ${sTop}%, ${lTop}%, 1)` commit('setTopColor', topColor) let floorColor = `hsla(${hFloor}, ${sFloor}%, ${lFloor}%, 1)` commit('setFloorColor', floorColor) let gradientTexture = new THREE.CanvasTexture(getters.createGradientCanvas(topColor, floorColor)) let materialOpts = { color: 0xffffff, side: THREE.DoubleSide, shininess: 30, map: gradientTexture } building.material = new THREE.MeshPhongMaterial(materialOpts) // commiting walls commit('setWalls3dObj', building) let buildingPos = { ...state.position, ...{ z: state.position.z + 0.5 * state.size.z } } commit('setWallsPos', buildingPos) // TOP & FLOOR let topGeom = new THREE.BoxGeometry(state.size.x, state.wall.wallW, state.size.z) let topOpts = { color: new THREE.Color(`hsl(${hTop}, ${sTop}%, ${lTop}%)`), shininess: 30 } let topMat = new THREE.MeshPhongMaterial(topOpts) let topMesh = new THREE.Mesh(topGeom, topMat) commit('setTop3dObj', topMesh) let topPosition = { ...state.position, ...{ y: state.position.y + 0.5 * state.size.y } } commit('setTopPos', topPosition) let floorOpts = { color: new THREE.Color(`hsl(${hFloor}, ${sFloor}%, ${lFloor}%)`), shininess: 10 } let floorMat = new THREE.MeshPhongMaterial(floorOpts) let floorMesh = new THREE.Mesh(topGeom, floorMat) commit('setFloor3dObj', floorMesh) let floorPosition = { ...state.position, ...{ y: state.position.y - 0.5 * state.size.y } } commit('setFloorPos', floorPosition) // LEVELS let levelGeom = new THREE.BoxGeometry(state.size.x - state.wall.wallW * 2, 0.1, state.size.z - state.wall.wallW * 2) let levelMesh = new THREE.Mesh(levelGeom) let levelBSP = new ThreeBSP(levelMesh) let levelHoleGeom = new THREE.BoxGeometry(state.size.x - state.wall.wallW * 2 - 3, 0.1, state.size.z - state.wall.wallW * 2 - 3) let levelHoleMesh = new THREE.Mesh(levelHoleGeom) let levelHoleBSP = new ThreeBSP(levelHoleMesh) levelBSP = levelBSP.subtract(levelHoleBSP) levelMesh = levelBSP.toMesh() let levelsGeom = new THREE.Geometry() for (var k = -1; k < 2; k++) { levelMesh.position.y = k * state.size.y * 0.25 levelsGeom.mergeMesh(levelMesh) } let levelsOpts = { color: new THREE.Color(`hsl(${hTop}, ${sTop}%, ${lTop}%)`), shininess: 10 } let levelsMat = new THREE.MeshPhongMaterial(levelsOpts) let levelsMesh = new THREE.Mesh(levelsGeom, levelsMat) commit('setLevels3dObj', levelsMesh) let levelsPos = { ...state.position } commit('setLevelsPos', levelsPos) // // DEBUG GRID // if (state.debug) { // let celDebugGeom = new THREE.BoxGeometry(1, 1, 0.01) // let celDebugMesh = new THREE.Mesh(celDebugGeom) // let gridsDebugGeom = new THREE.Geometry() // Object.keys(state.grids).map(function (side) { // Object.keys(state.grids[side]).map(function (face) { // Object.keys(state.grids[side][face]).map(function (level) { // state.grids[side][face][level].forEach((item, i) => { // // console.log(`grid ${side} ${face} ${level} ${i}`, item) // celDebugMesh.position.y = state.grids[side][face][level][i].y // celDebugMesh.position.x = state.grids[side][face][level][i].x // celDebugMesh.position.z = state.grids[side][face][level][i].z // celDebugMesh.rotation.y = state.grids[side][face][level][i].ry // gridsDebugGeom.mergeMesh(celDebugMesh) // }) // }) // }) // }) // let gridsDebugOpts = { // color: 0xff0000 // } // let gridsDebugMat = new THREE.MeshPhongMaterial(gridsDebugOpts) // let gridsDebugMesh = new THREE.Mesh(gridsDebugGeom, gridsDebugMat) // commit('setDebugGrids3dObj', gridsDebugMesh) // } }, getContents ({ dispatch, commit, state }) { return new Promise((resolve, reject) => { // get the list of corpuses (aka authors) dispatch('loadContents') .then(({ data: { data: { project } = null } }) => { console.log('graphql contents', project) dispatch('computeContents', project) .then((contents) => { console.log('computed contents', contents) commit('setContents', project) }) }) .catch((error) => { console.warn('Issue with getContents', error) Promise.reject(error) }) }) }, loadContents ({ dispatch, commit, state }) { console.log('loadContents') let contentsQuery = new Query('project', { id: state.id }) let visiblesQuery = new Query('visibles') visiblesQuery.filter({ Published: true }) visiblesQuery.find(['id', 'Name', 'Text2', 'Vimeo', 'Url', 'categories']) contentsQuery.find([visiblesQuery]) console.log('contentsQuery', `${contentsQuery}`) return GRAPHQL.post('', { query: `query { project(id: "${state.id}") { visibles( where: {Published: "true"}, sort: "Weight:asc" ){ id Name Media { url size } Text2 Vimeo Url categories country{ id Name } Weight } contexts( where: { Published: "true" }, sort: "Weight:asc" ){ id Name Images { url size } Text2 Vimeo Url country{ id Name } } processes( where: { Published: "true" }, sort: "Weight:asc" ){ id Name Media { url size } Text2 Vimeo Url country{ id Name } } concepts( where: { Published: "true" }, sort: "Weight:asc" ){ id Name Images { url size } Text2 Vimeo } } }` }) }, computeContents ({ dispatch, commit, state }, contents) { console.log('computeContents') return Promise.all(Object.keys(contents).map(function (level) { return Promise.all(contents[level].map(function (content) { if (content.Vimeo) { // console.log('Vimeo', content.Vimeo) // GET https://vimeo.com/api/oembed.json?url=https%3A//vimeo.com/286898202&width=480&height=360 let params = { url: content.Vimeo, width: 1280, height: 720, responsive: true } let q = qs.stringify(params) return VIMEO.get('?' + q, {}) .then(({ data }) => { // console.log('Vimeo data', data) content.Vimeo = data// thumbnail_url_with_play_button return content }) .catch((error) => { console.warn('Issue with vimeo', error) }) } else { return content } })).then((cts) => { // return an object with the right level (visible, etc, ...) // console.log(`${level} cts`, cts) let o = {} o[level] = cts return o }) })).then((a) => { // return an object with the right keys (visible, etc, ...) console.log('a', a) let o = {} for (var i = 0; i < a.length; i++) { o = { ...o, ...a[i] } } return o }) } // getGridPos ({ state, commit }) { // let p = state.grid[0] // commit('shiftGrid') // Promise.resolve(p) // } } }