project.js 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531
  1. // https://codeburst.io/dynamic-modules-with-vuex-and-vue-b9c481ca792
  2. // https://www.brophy.org/post/instance-aware-vuex-modules-1/
  3. // import qs from 'querystring'
  4. // import { REST } from 'api/rest-axios'
  5. import { GRAPHQL } from 'api/graphql-axios'
  6. import Query from 'graphql-query-builder'
  7. import { VIMEO } from 'api/vimeo-axios'
  8. import qs from 'querystring'
  9. import * as THREE from 'three'
  10. import { ThreeBSP } from 'three-js-csg-es6'
  11. export default {
  12. namespaced: true,
  13. // initial state
  14. state: {
  15. obj3d: null,
  16. size: { x: 0, y: 0, z: 0 },
  17. position: { x: 0, y: 0, z: 0 },
  18. walls3dObj: null,
  19. wallsPos: null,
  20. top3dObj: null,
  21. topPos: null,
  22. topColor: null,
  23. floor3dObj: null,
  24. floorPos: null,
  25. florrColor: null,
  26. levels3dObj: null,
  27. levelsPos: null,
  28. wall: {
  29. wallW: 0.001,
  30. // dig windows on face and back
  31. winW: 2 + Math.random() * 2,
  32. winH: 4 + Math.random() * 4,
  33. margin: 2,
  34. nbrWinX: 0,
  35. paddingX: 0,
  36. nbrWinY: 0,
  37. paddingY: 0,
  38. nbrWinZ: 0,
  39. paddingZ: 0
  40. },
  41. contents: {},
  42. contents_size_factor: 1, // factor to get the contents (grid) size proportional to windows
  43. contentTypes: ['visible', 'context', 'process', 'concept'],
  44. grids: null,
  45. gridsContentPlaced: {},
  46. activeLevel: 'visibles'
  47. },
  48. // getters
  49. getters: {
  50. position: (state) => {
  51. return state.position
  52. },
  53. size: (state) => {
  54. return state.size
  55. },
  56. createGradientCanvas: (state) => (c1, c2) => {
  57. var ctx = document.createElement('canvas').getContext('2d')
  58. ctx.canvas.width = 1024
  59. ctx.canvas.height = 1024
  60. var lingrad = ctx.createLinearGradient(0, 0, 0, 1024)
  61. lingrad.addColorStop(0, c1)
  62. lingrad.addColorStop(1, c2)
  63. ctx.fillStyle = lingrad
  64. ctx.fillRect(0, 0, 1024, 1024)
  65. return ctx.canvas
  66. }
  67. },
  68. // mutations
  69. mutations: {
  70. setSize: (state, size) => {
  71. state.size = size
  72. },
  73. setPosition: (state, pos) => {
  74. state.position = pos
  75. },
  76. setWalls3dObj: (state, obj) => { state.walls3dObj = obj },
  77. setWallsPos: (state, pos) => { state.wallsPos = pos },
  78. setTop3dObj: (state, obj) => { state.top3dObj = obj },
  79. setTopPos: (state, pos) => { state.topPos = pos },
  80. setFloor3dObj: (state, obj) => { state.floor3dObj = obj },
  81. setFloorPos: (state, pos) => { state.floorPos = pos },
  82. setTopColor: (state, col) => { state.topColor = col },
  83. setFloorColor: (state, col) => { state.floorColor = col },
  84. setLevels3dObj: (state, obj) => { state.levels3dObj = obj },
  85. setLevelsPos: (state, pos) => { state.levelsPos = pos },
  86. setContents: (state, contents) => { state.contents = contents },
  87. setGrids: (state, grids) => { state.grids = grids },
  88. shiftGrid: (state, c) => {
  89. console.log('shiftGrid c:', c)
  90. let side
  91. switch (c.face) {
  92. case 'back':
  93. case 'front':
  94. side = 'x'
  95. break
  96. case 'left':
  97. case 'right':
  98. side = 'z'
  99. break
  100. }
  101. let p = state.grids[side][c.face][c.type].shift()
  102. if (!state.gridsContentPlaced[side]) {
  103. state.gridsContentPlaced[side] = {}
  104. }
  105. if (!state.gridsContentPlaced[side][c.face]) {
  106. state.gridsContentPlaced[side][c.face] = {}
  107. }
  108. if (!state.gridsContentPlaced[side][c.face][c.type]) {
  109. state.gridsContentPlaced[side][c.face][c.type] = {}
  110. }
  111. state.gridsContentPlaced[side][c.face][c.type][c.id] = p
  112. },
  113. setActiveLevel: (state, l) => {
  114. state.activeLevel = l
  115. }
  116. },
  117. // actions
  118. actions: {
  119. init ({ dispatch, commit, state, rootGetters }) {
  120. console.log(`Init Project module ${state.id}`, state)
  121. dispatch('sizingBuilding')
  122. dispatch('build3dObjs')
  123. },
  124. sizingBuilding ({ dispatch, commit, state, rootGetters }) {
  125. console.log('sizingBuilding, state', state)
  126. let totalW = rootGetters['Projects/totalW']
  127. // console.log('totalW', totalW)
  128. let margin = rootGetters['Projects/marginBetweenBuildings']
  129. // console.log('margin', margin)
  130. // positioning buildings on x regarding the widths
  131. // & setting up the window sizing
  132. // & setting up the content grid
  133. // let wall, a
  134. // let grid
  135. // X POS
  136. let x
  137. if (state.index === 0) {
  138. // if it's the first
  139. x = -1 * totalW / 2 + state.size.x / 2
  140. } else {
  141. // else get the precedent pos
  142. let prevProjID = rootGetters['Projects/projectID'](state.index - 1)
  143. // console.log('prevProjID', prevProjID)
  144. let prevProjPos = rootGetters[`project:${prevProjID}/position`]
  145. // console.log(`project:${prevProjID}/position.x`, prevProjPos.x)
  146. let prevProjSize = rootGetters[`project:${prevProjID}/size`]
  147. // console.log(`project:${prevProjID}/size.x`, prevProjSize.x)
  148. // console.log('state.size.x', state.size.x)
  149. // prev X + alf of prev size x + margin + half of current size x
  150. x = prevProjPos.x + prevProjSize.x / 2 + margin + state.size.x / 2
  151. }
  152. // console.log('x', x)
  153. commit('setPosition', {
  154. x: x,
  155. // y: -1 * state.size.y / 2 + 10 + Math.random() * 30, // -10 + Math.random() * this.size.y / 2
  156. y: -state.size.y / 4,
  157. z: 0// -10 + Math.random() * 10
  158. })
  159. // WINDOWS
  160. let a = 0
  161. state.wall.nbrWinX = Math.floor((state.size.x - 2 * state.wall.margin) / state.wall.winW)
  162. // removing windows on X until padding is enough
  163. while (state.wall.paddingX < 0.4) {
  164. state.wall.nbrWinX -= a
  165. state.wall.paddingX = (state.size.x - 2 * state.wall.margin - state.wall.winW * state.wall.nbrWinX) / (state.wall.nbrWinX - 1)
  166. a++
  167. }
  168. a = 0
  169. state.wall.nbrWinY = Math.floor((state.size.y - 2 * state.wall.margin) / state.wall.winH)
  170. // removing windows on Y until padding is enough
  171. while (state.wall.paddingY < 0.4) {
  172. state.wall.nbrWinY -= a
  173. state.wall.paddingY = (state.size.y - 2 * state.wall.margin - state.wall.winH * state.wall.nbrWinY) / (state.wall.nbrWinY - 1)
  174. a++
  175. }
  176. // CONTENTS GRID
  177. a = 0
  178. state.wall.nbrWinZ = Math.floor((state.size.z - 2 * state.wall.margin) / state.wall.winW)
  179. while (state.wall.paddingZ < 0.4) {
  180. state.wall.nbrWinZ -= a
  181. state.wall.paddingZ = (state.size.z - 2 * state.wall.margin - state.wall.winW * state.wall.nbrWinZ) / (state.wall.nbrWinZ - 1)
  182. a++
  183. }
  184. // create grids for right/left & face/back
  185. let grids = {
  186. x: { front: {}, back: {} },
  187. z: { left: {}, right: {} }
  188. }
  189. // console.log('grids', grids)
  190. let grid, rows, cols, t, cel
  191. // console.log('Object.keys(grids)', Object.keys(grids))
  192. Object.keys(grids).map(function (side) {
  193. // console.log('GRID side:', side)
  194. grid = {}
  195. // calculate cols nbr regarding the face orientation
  196. switch (side) {
  197. case 'z':
  198. cols = state.wall.nbrWinZ / state.contents_size_factor
  199. break
  200. case 'x':
  201. cols = state.wall.nbrWinX / state.contents_size_factor
  202. break
  203. }
  204. // calculate rows
  205. rows = state.wall.nbrWinY / state.contents_size_factor
  206. // dispatch grid rows on to 4 levels (t)
  207. for (var m = 0; m < rows; m++) { // rows
  208. if (m > rows / 4 * 3 + 1) {
  209. t = state.contentTypes[0]
  210. } else if (m > rows / 4 * 2 + 1) {
  211. t = state.contentTypes[1]
  212. } else if (m > rows / 4 + 1) {
  213. t = state.contentTypes[2]
  214. } else {
  215. t = state.contentTypes[3]
  216. }
  217. // create level if not exists
  218. if (!grid[t]) {
  219. grid[t] = []
  220. }
  221. // create cols for each rows
  222. for (var l = 0; l < cols; l++) { // cols
  223. cel = { y: margin + state.wall.winH * state.contents_size_factor * m }
  224. switch (side) {
  225. case 'z':
  226. cel.z = margin + state.wall.winW * state.contents_size_factor * l
  227. break
  228. case 'x':
  229. cel.x = margin + state.wall.winW * state.contents_size_factor * l
  230. break
  231. }
  232. grid[t].push(cel)
  233. }
  234. }
  235. // console.log('grid', grid)
  236. // clown and shuffle the grids (one by face)
  237. // console.log('Object.keys(grids[side])', Object.keys(grids[side]))
  238. Object.keys(grids[side]).map(function (face) {
  239. for (var i = 0; i < state.contentTypes.length; i++) {
  240. for (let n = grid[state.contentTypes[i]].length - 1; n > 0; n--) {
  241. const o = Math.floor(Math.random() * n)
  242. const temp = grid[state.contentTypes[i]][n]
  243. grid[state.contentTypes[i]][n] = grid[state.contentTypes[i]][o]
  244. grid[state.contentTypes[i]][o] = temp
  245. }
  246. }
  247. // console.log('shuffeld grid', grid)
  248. grids[side][face] = { ...grid }
  249. })
  250. })
  251. commit('setGrids', grids)
  252. console.log('state.grids', state.grids)
  253. },
  254. build3dObjs ({ dispatch, commit, state, getters, rootGetters }) {
  255. console.log('build3dObjs')
  256. // http://learningthreejs.com/blog/2011/12/10/constructive-solid-geometry-with-csg-js/
  257. let frontGeom = new THREE.BoxGeometry(state.size.x, state.size.y, state.wall.wallW)
  258. let frontMesh = new THREE.Mesh(frontGeom)
  259. frontMesh.position.z = 0.5 * state.size.z
  260. let frontBSP = new ThreeBSP(frontMesh)
  261. // https://medium.com/techtrument/multithreading-javascript-46156179cf9a
  262. let winGeom = new THREE.BoxGeometry(state.wall.winW, state.wall.winH, state.wall.wallW)
  263. let winMesh = new THREE.Mesh(winGeom)
  264. let windowsGeom = new THREE.Geometry()
  265. for (var i = 0; i < state.wall.nbrWinX; i++) {
  266. for (var j = 0; j < state.wall.nbrWinY; j++) {
  267. winMesh.position.z = 0.5 * state.size.z
  268. winMesh.position.x = -0.5 * state.size.x + state.wall.margin + state.wall.winW * 0.5 + i * (state.wall.winW + state.wall.paddingX)
  269. winMesh.position.y = 0.5 * state.size.y - state.wall.margin - state.wall.winH * 0.5 - j * (state.wall.winH + state.wall.paddingY)
  270. // winMesh.updateMatrix()
  271. windowsGeom.mergeMesh(winMesh)
  272. }
  273. }
  274. let windowsBSP = new ThreeBSP(windowsGeom)
  275. let frontWindowedBSP = frontBSP.subtract(windowsBSP)
  276. // console.log('state.Faces_opaques', state.Faces_opaques)
  277. let backWindowedMesh
  278. if (state.Faces_opaques === 3) {
  279. backWindowedMesh = frontMesh.clone()
  280. } else {
  281. let frontWindowedMesh = frontWindowedBSP.toMesh()
  282. backWindowedMesh = frontWindowedMesh.clone()
  283. }
  284. backWindowedMesh.position.z = -0.5 * state.size.z
  285. let backWindowedBSP = new ThreeBSP(backWindowedMesh)
  286. let rightGeom = new THREE.BoxGeometry(state.wall.wallW, state.size.y, state.size.z)
  287. let rightMesh = new THREE.Mesh(rightGeom)
  288. rightMesh.position.x = 0.5 * state.size.x
  289. // rightMesh.position.z = 0.5 * state.size.z
  290. let rightBSP = new ThreeBSP(rightMesh)
  291. let leftMesh = rightMesh.clone()
  292. leftMesh.position.x = -0.5 * state.size.x
  293. // leftMesh.position.z = 0.5 * state.size.z
  294. let leftBSP = new ThreeBSP(leftMesh)
  295. let buildingBSP = frontWindowedBSP.union(rightBSP)
  296. buildingBSP = buildingBSP.union(backWindowedBSP)
  297. buildingBSP = buildingBSP.union(leftBSP)
  298. // convert back to three.js mesh
  299. let building = buildingBSP.toMesh()
  300. // create a classical material for building
  301. // let topColor = `hsla(201, 100%, 95%, 1)`
  302. let hTop = Math.round(190 + Math.random() * 20)
  303. let sTop = Math.round(20 + Math.random() * 60)
  304. let lTop = Math.round(75)
  305. // let hFloor = Math.round(205 + Math.random() * 10)
  306. let hFloor = hTop
  307. // let sFloor = Math.round(30 + Math.random() * 40)
  308. let sFloor = sTop
  309. // let lFloor = Math.round(10 + Math.random() * 20)
  310. let lFloor = Math.round(10)
  311. let topColor = `hsla(${hTop}, ${sTop}%, ${lTop}%, 1)`
  312. commit('setTopColor', topColor)
  313. let floorColor = `hsla(${hFloor}, ${sFloor}%, ${lFloor}%, 1)`
  314. commit('setFloorColor', floorColor)
  315. let gradientTexture = new THREE.CanvasTexture(getters.createGradientCanvas(topColor, floorColor))
  316. let materialOpts = {
  317. color: 0xffffff,
  318. side: THREE.DoubleSide,
  319. shininess: 30,
  320. map: gradientTexture
  321. }
  322. building.material = new THREE.MeshPhongMaterial(materialOpts)
  323. // commiting walls
  324. commit('setWalls3dObj', building)
  325. let buildingPos = { ...state.position, ...{ z: state.position.z + 0.5 * state.size.z } }
  326. commit('setWallsPos', buildingPos)
  327. // TOP & FLOOR
  328. let topGeom = new THREE.BoxGeometry(state.size.x, state.wall.wallW, state.size.z)
  329. let topOpts = {
  330. color: new THREE.Color(`hsl(${hTop}, ${sTop}%, ${lTop}%)`),
  331. shininess: 30
  332. }
  333. let topMat = new THREE.MeshPhongMaterial(topOpts)
  334. let topMesh = new THREE.Mesh(topGeom, topMat)
  335. commit('setTop3dObj', topMesh)
  336. let topPosition = { ...state.position, ...{ y: state.position.y + 0.5 * state.size.y } }
  337. commit('setTopPos', topPosition)
  338. let floorOpts = {
  339. color: new THREE.Color(`hsl(${hFloor}, ${sFloor}%, ${lFloor}%)`),
  340. shininess: 10
  341. }
  342. let floorMat = new THREE.MeshPhongMaterial(floorOpts)
  343. let floorMesh = new THREE.Mesh(topGeom, floorMat)
  344. commit('setFloor3dObj', floorMesh)
  345. let floorPosition = { ...state.position, ...{ y: state.position.y - 0.5 * state.size.y } }
  346. commit('setFloorPos', floorPosition)
  347. // LEVELS
  348. let levelGeom = new THREE.BoxGeometry(state.size.x - state.wall.wallW * 2, 0.1, state.size.z - state.wall.wallW * 2)
  349. let levelMesh = new THREE.Mesh(levelGeom)
  350. let levelBSP = new ThreeBSP(levelMesh)
  351. let levelHoleGeom = new THREE.BoxGeometry(state.size.x - state.wall.wallW * 2 - 3, 0.1, state.size.z - state.wall.wallW * 2 - 3)
  352. let levelHoleMesh = new THREE.Mesh(levelHoleGeom)
  353. let levelHoleBSP = new ThreeBSP(levelHoleMesh)
  354. levelBSP = levelBSP.subtract(levelHoleBSP)
  355. levelMesh = levelBSP.toMesh()
  356. let levelsGeom = new THREE.Geometry()
  357. for (var k = -1; k < 2; k++) {
  358. levelMesh.position.y = k * state.size.y * 0.25
  359. levelsGeom.mergeMesh(levelMesh)
  360. }
  361. let levelsOpts = {
  362. color: new THREE.Color(`hsl(${hTop}, ${sTop}%, ${lTop}%)`),
  363. shininess: 10
  364. }
  365. let levelsMat = new THREE.MeshPhongMaterial(levelsOpts)
  366. let levelsMesh = new THREE.Mesh(levelsGeom, levelsMat)
  367. commit('setLevels3dObj', levelsMesh)
  368. let levelsPos = { ...state.position }
  369. commit('setLevelsPos', levelsPos)
  370. // // repère
  371. // let repGeom = new THREE.BoxGeometry(1, 1, 1)
  372. // let repMesh = new THREE.Mesh(repGeom)
  373. // let repOpts = {
  374. // color: 0x0000ff,
  375. // shininess: 30
  376. // }
  377. // repMesh.material = new THREE.MeshPhongMaterial(repOpts)
  378. // repMesh.position = { ...state.position, ...{ y: 0 } }
  379. // commit('setRep3dObj', levelsMesh)
  380. },
  381. getContents ({ dispatch, commit, state }) {
  382. return new Promise((resolve, reject) => {
  383. // get the list of corpuses (aka authors)
  384. dispatch('loadContents')
  385. .then(({ data: { data: { project } = null } }) => {
  386. console.log('graphql contents', project)
  387. dispatch('computeContents', project)
  388. .then((contents) => {
  389. console.log('computed contents', contents)
  390. commit('setContents', project)
  391. })
  392. })
  393. .catch((error) => {
  394. console.warn('Issue with getContents', error)
  395. Promise.reject(error)
  396. })
  397. })
  398. },
  399. loadContents ({ dispatch, commit, state }) {
  400. console.log('loadContents')
  401. let contentsQuery = new Query('project', { id: state.id })
  402. let visiblesQuery = new Query('visibles')
  403. visiblesQuery.filter({ Published: true })
  404. visiblesQuery.find(['id', 'Name', 'Text2', 'Vimeo', 'Url', 'categories'])
  405. contentsQuery.find([visiblesQuery])
  406. console.log('contentsQuery', `${contentsQuery}`)
  407. return GRAPHQL.post('', { query: `query {
  408. project(id: "${state.id}") {
  409. visibles(where: { Published: "true" }){
  410. id
  411. Name
  412. Media {
  413. url
  414. size
  415. }
  416. Text2
  417. Vimeo
  418. Url
  419. categories
  420. }
  421. contexts(where: { Published: "true" }){
  422. id
  423. Name
  424. Images {
  425. url
  426. size
  427. }
  428. Text2
  429. Vimeo
  430. Url
  431. }
  432. processes(where: { Published: "true" }){
  433. id
  434. Name
  435. Media {
  436. url
  437. size
  438. }
  439. Text2
  440. Vimeo
  441. Url
  442. }
  443. concepts(where: { Published: "true" }){
  444. id
  445. Name
  446. Images {
  447. url
  448. size
  449. }
  450. Text2
  451. Vimeo
  452. }
  453. }
  454. }` })
  455. },
  456. computeContents ({ dispatch, commit, state }, contents) {
  457. console.log('computeContents')
  458. return Promise.all(Object.keys(contents).map(function (key) {
  459. return Promise.all(contents[key].map(function (content) {
  460. if (content.Vimeo) {
  461. console.log('Vimeo', content.Vimeo)
  462. // GET https://vimeo.com/api/oembed.json?url=https%3A//vimeo.com/286898202&width=480&height=360
  463. let params = {
  464. url: content.Vimeo,
  465. width: 1280,
  466. height: 720,
  467. responsive: true
  468. }
  469. let q = qs.stringify(params)
  470. return VIMEO.get('?' + q, {})
  471. .then(({ data }) => {
  472. console.log('Vimeo data', data)
  473. content.Vimeo = data// thumbnail_url_with_play_button
  474. return content
  475. })
  476. .catch((error) => {
  477. console.warn('Issue with vimeo', error)
  478. })
  479. } else {
  480. return content
  481. }
  482. })).then((cts) => {
  483. // return an object with the right key (visible, etc, ...)
  484. console.log(`${key} cts`, cts)
  485. let o = {}
  486. o[key] = cts
  487. return o
  488. })
  489. })).then((a) => {
  490. // return an object with the right keys (visible, etc, ...)
  491. console.log('a', a)
  492. let o = {}
  493. for (var i = 0; i < a.length; i++) {
  494. o = { ...o, ...a[i] }
  495. }
  496. return o
  497. })
  498. }
  499. // getGridPos ({ state, commit }) {
  500. // let p = state.grid[0]
  501. // commit('shiftGrid')
  502. // Promise.resolve(p)
  503. // }
  504. }
  505. }