ExperimentBaseExtracts.vue 7.6 KB

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