ExperimentBaseExtracts.vue 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228
  1. <template>
  2. <div>
  3. <slot></slot>
  4. </div>
  5. </template>
  6. <script>
  7. import ExperimentBase from '@/mixins/ExperimentBase'
  8. import { mapGetters } from 'vuex'
  9. import { API_ROUTES, findNearestUpper, findNearestLower } from '@/functions'
  10. import { EXPERIMENT as experimentMsgId } from '@/../config.messagesId'
  11. export default {
  12. name: 'ExperimentBaseExtracts',
  13. mixins: [ExperimentBase],
  14. data() {
  15. return {
  16. // Updated when `setExtractConfig` is called
  17. extractConfig: {
  18. x: null,
  19. y: null
  20. },
  21. extracts: [],
  22. extractsInfos: null,
  23. showHoverBorder: null
  24. }
  25. },
  26. computed: {
  27. ...mapGetters(['getHostURI'])
  28. },
  29. methods: {
  30. // Load extracts from the API
  31. async getExtracts(quality = 'min') {
  32. const URI = `${this.getHostURI}${API_ROUTES.getImageExtracts(this.sceneName, quality, this.extractConfig.x, this.extractConfig.y)}`
  33. const { data } = await fetch(URI)
  34. .then(async res => {
  35. res.json = await res.json()
  36. return res
  37. })
  38. .then(res => {
  39. if (!res.ok) throw new Error(res.json.message + res.json.data ? `\n${res.json.data}` : '')
  40. return res.json
  41. })
  42. return data
  43. },
  44. // Config was updated, load extracts and save progression
  45. async setExtractConfig(config, configuratorRef) {
  46. if (!config) return
  47. this.loadingMessage = 'Loading configuration extracts...'
  48. this.loadingErrorMessage = null
  49. try {
  50. this.extractConfig.x = config.x
  51. this.extractConfig.y = config.y
  52. const data = await this.getExtracts()
  53. const hostURI = this.getHostURI
  54. this.extractsInfos = data.info
  55. this.extracts = data.extracts.map((url, i) => ({
  56. link: hostURI + url,
  57. quality: data.info.image.quality,
  58. zone: i + 1,
  59. index: i,
  60. nextQuality: findNearestUpper(data.info.image.quality, this.qualities),
  61. precQuality: findNearestLower(data.info.image.quality, this.qualities),
  62. loading: false
  63. }))
  64. // If there is a configurator, retract it
  65. if (configuratorRef) configuratorRef.setVisibility(false)
  66. }
  67. catch (err) {
  68. console.error('Failed to load new configuration', err)
  69. this.loadingErrorMessage = 'Failed to load new configuration. ' + err.message
  70. }
  71. finally {
  72. this.loadingMessage = null
  73. this.saveProgress()
  74. }
  75. },
  76. // An action was triggered, load extracts and save progression
  77. async extractAction(event, extractObj) {
  78. const { index, nextQuality, precQuality, quality } = extractObj
  79. let action, newQuality
  80. if (event.button === 0) action = 'needLess' // Left click
  81. if (event.button === 2) action = 'needMore' // Right click
  82. if (action === 'needLess') newQuality = precQuality
  83. if (action === 'needMore') newQuality = nextQuality
  84. // Do not load a new extract if same quality
  85. if (newQuality === quality) return
  86. // Set loading state
  87. this.extracts[index].loading = true
  88. try {
  89. const collectedData = this.getClickDataObject(event, extractObj, action)
  90. this.sendMessage({ msgId: experimentMsgId.DATA, msg: collectedData })
  91. // Loading new extract
  92. const data = await this.getExtracts(newQuality)
  93. this.extracts[index].link = this.getHostURI + data.extracts[index]
  94. this.extracts[index].quality = data.info.image.quality
  95. this.extracts[index].nextQuality = findNearestUpper(data.info.image.quality, this.qualities)
  96. this.extracts[index].precQuality = findNearestLower(data.info.image.quality, this.qualities)
  97. this.extracts[index].loading = false
  98. }
  99. catch (err) {
  100. // TODO: toast message if fail
  101. console.error('Failed to load extract', err)
  102. }
  103. finally {
  104. this.extracts[index].loading = false
  105. this.saveProgress()
  106. }
  107. },
  108. getClickDataObject(event, extractObj, action) {
  109. const { index } = extractObj
  110. const clientSideData = {
  111. extractSize: {
  112. width: event.target.clientWidth,
  113. height: event.target.clientHeight
  114. },
  115. imageSize: {
  116. width: event.target.clientWidth * this.extractConfig.x,
  117. height: event.target.clientHeight * this.extractConfig.y
  118. },
  119. clickPosition: {
  120. extract: {
  121. x: event.offsetX,
  122. y: event.offsetY
  123. },
  124. image: {
  125. x: event.offsetX + (this.extracts[index].index % this.extractConfig.x) * event.target.clientWidth,
  126. y: event.offsetY + (Math.floor(this.extracts[index].index / this.extractConfig.x)) * event.target.clientHeight
  127. }
  128. }
  129. }
  130. const calculatedRealData = {}
  131. calculatedRealData.extractSize = {
  132. width: this.extractsInfos.extractsSize.width,
  133. height: this.extractsInfos.extractsSize.height
  134. }
  135. calculatedRealData.imageSize = {
  136. width: this.extractsInfos.image.metadata.width,
  137. height: this.extractsInfos.image.metadata.height
  138. }
  139. calculatedRealData.clickPosition = {
  140. extract: {
  141. x: Math.floor((calculatedRealData.imageSize.width * clientSideData.clickPosition.extract.x) / clientSideData.imageSize.width),
  142. y: Math.floor((calculatedRealData.imageSize.height * clientSideData.clickPosition.extract.y) / clientSideData.imageSize.height)
  143. },
  144. image: {
  145. x: Math.floor((calculatedRealData.imageSize.width * clientSideData.clickPosition.image.x) / clientSideData.imageSize.width),
  146. y: Math.floor((calculatedRealData.imageSize.height * clientSideData.clickPosition.image.y) / clientSideData.imageSize.height)
  147. }
  148. }
  149. // Sending event to WebSocket server
  150. const loggedObj = {
  151. experimentName: this.experimentName,
  152. sceneName: this.sceneName,
  153. extractConfig: this.extractConfig,
  154. clickedExtract: {
  155. link: this.extracts[index].link,
  156. quality: this.extracts[index].quality,
  157. nextQuality: this.extracts[index].nextQuality,
  158. precQuality: this.extracts[index].precQuality,
  159. zone: this.extracts[index].zone,
  160. index: this.extracts[index].index
  161. },
  162. action,
  163. clientSideData,
  164. calculatedRealData
  165. }
  166. return loggedObj
  167. },
  168. // Finish an experiment, sending full data to the server
  169. // Don't forget to surcharge this function when using this mixin to add more data
  170. finishExperiment() {
  171. const obj = {
  172. experimentName: this.experimentName,
  173. sceneName: this.sceneName,
  174. extractConfig: this.extractConfig,
  175. extracts: this.extracts.map(x => ({
  176. index: x.index,
  177. link: x.link,
  178. nextQuality: x.nextQuality,
  179. precQuality: x.precQuality,
  180. quality: x.quality,
  181. zone: x.zone
  182. })),
  183. qualities: this.qualities,
  184. referenceImage: this.referenceImage
  185. }
  186. this.sendMessage({ msgId: experimentMsgId.VALIDATED, msg: obj })
  187. this.setExperimentDone({ experimentName: this.experimentName, sceneName: this.sceneName, done: true })
  188. this.$router.push(`/experiments/${this.experimentName}`)
  189. }
  190. }
  191. }
  192. </script>
  193. <style>
  194. /* White border when hovering on extracts */
  195. .extract-hover-border:hover {
  196. z-index: 1;
  197. outline: 2px #f4f4f4 solid;
  198. }
  199. .img-extract-loader {
  200. height: 100%;
  201. width: 0px;
  202. display: flex;
  203. justify-content: center;
  204. align-items: center;
  205. }
  206. </style>