123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288 |
- <template>
- <div>
- <slot></slot>
- </div>
- </template>
- <script>
- import ExperimentBase from '@/mixins/ExperimentBase'
- import { mapGetters } from 'vuex'
- import { API_ROUTES, findNearestUpper, findNearestLower } from '@/functions'
- import { EXPERIMENT as experimentMsgId } from '@/../config.messagesId'
- export default {
- name: 'ExperimentBaseExtracts',
- mixins: [ExperimentBase],
- data() {
- return {
- // Updated when `setExtractConfig` is called
- extractConfig: {
- x: null,
- y: null
- },
- extracts: [],
- extractsInfos: null,
- showHoverBorder: null,
- lockConfig: null,
- comment: null,
- dialogMore: false,
- dialogLess: false
- }
- },
- computed: {
- ...mapGetters(['getHostURI'])
- },
- methods: {
- // Load extracts from the API
- async getExtracts(quality = 'min') {
- const URI = `${this.getHostURI}${API_ROUTES.getImageExtracts(this.sceneName, quality, this.extractConfig.x, this.extractConfig.y)}`
- const { data } = await fetch(URI)
- .then(async res => {
- res.json = await res.json()
- return res
- })
- .then(res => {
- if (!res.ok) throw new Error(res.json.message + res.json.data ? `\n${res.json.data}` : '')
- return res.json
- })
- data.extracts = data.extracts.map(x => this.getHostURI + x)
- return data
- },
- // There was an error loading extracts in v-img
- // (extracts have probably been removed from the server)
- // //
- // Get all extracts qualities and remove duplicates
- // Then do all the API extract-generation API calls then reload the page
- async extractsRemovedFromServerFallback() {
- this.loadingMessage = 'Synchronizing with the server...'
- try {
- const qualities = [...new Set(this.extracts.map(x => x.quality))]
- await Promise.all(qualities.map(x => this.getExtracts(x)))
- }
- catch (err) {
- console.error(err)
- this.loadingErrorMessage = 'Failed to synchronize with the server. Try reloading the page.'
- }
- finally {
- this.loadingMessage = null
- }
- },
- // Convert a simple API extracts object to get more informations
- getExtractFullObject(extractsApiObj) {
- return extractsApiObj.extracts.map((url, i) => ({
- link: url,
- quality: extractsApiObj.info.image.quality,
- zone: i + 1,
- index: i,
- nextQuality: findNearestUpper(extractsApiObj.info.image.quality, this.qualities),
- precQuality: findNearestLower(extractsApiObj.info.image.quality, this.qualities),
- loading: false
- }))
- },
- // Config was updated, load extracts and save progression
- async setExtractConfig(config, configuratorRef) {
- if (!config) return
- this.loadingMessage = 'Loading configuration extracts...'
- this.loadingErrorMessage = null
- try {
- this.extractConfig.x = config.x
- this.extractConfig.y = config.y
- this.extractConfig.quality = config.quality
- const data = await this.getExtracts(config.quality || undefined)
- this.extractsInfos = data.info
- // Put extracts in cache if not already there
- if (this.extracts.length === 0) this.extracts = this.getExtractFullObject(data)
- // If there is a configurator, retract it
- if (configuratorRef) configuratorRef.setVisibility(false)
- }
- catch (err) {
- console.error('Failed to load new configuration', err)
- this.loadingErrorMessage = 'Failed to load new configuration. ' + err.message
- }
- finally {
- this.loadingMessage = null
- this.saveProgress()
- }
- },
- // An action was triggered, load extracts and save progression
- async extractAction(event, extractObj) {
- const { index, nextQuality, precQuality, quality } = extractObj
- const qualityIndex = this.qualities.indexOf(quality)
- let action, newQuality
- if (event.button === 0) action = 'needMore' // Left click
- if (event.button === 2) action = 'needLess' // Right click
- if (event.button === 0 && event.ctrlKey) action = 'need10More' // ctrl + Right click
- if (event.button === 2 && event.ctrlKey) action = 'need10Less' // ctrl + Left click
- if (action === 'needLess') newQuality = precQuality
- if (action === 'needMore') newQuality = nextQuality
- if (action === 'need10More') {
- if (qualityIndex + 10 >= this.qualities.length - 1)
- newQuality = this.qualities[this.qualities.length - 1]
- else
- newQuality = this.qualities[qualityIndex + 10]
- }
- if (action === 'need10Less') {
- if (qualityIndex - 10 <= 0)
- newQuality = this.qualities[0]
- else
- newQuality = this.qualities[qualityIndex - 10]
- }
- // Do not load a new extract if same quality
- if (newQuality === quality) {
- // display alert once limit is reached
- if (action === 'needLess' || action === 'need10Less') {
- this.dialogLess = true
- }
- if (action === 'needMore' || action === 'need10More') {
- this.dialogMore = true
- }
- return
- }
- // Set loading state
- this.extracts[index].loading = true
- try {
- const collectedData = this.getClickDataObject(event, extractObj, action)
- this.sendMessage({ msgId: experimentMsgId.DATA, msg: collectedData })
- // Loading new extract
- const data = await this.getExtracts(newQuality)
- this.extracts[index].link = data.extracts[index]
- this.extracts[index].quality = data.info.image.quality
- this.extracts[index].nextQuality = findNearestUpper(data.info.image.quality, this.qualities)
- this.extracts[index].precQuality = findNearestLower(data.info.image.quality, this.qualities)
- this.extracts[index].loading = false
- }
- catch (err) {
- // TODO: toast message if fail
- console.error('Failed to load extract', err)
- }
- finally {
- this.extracts[index].loading = false
- this.saveProgress()
- }
- },
- getClickDataObject(event, extractObj, action) {
- const { index } = extractObj
- const clientSideData = {
- extractSize: {
- width: event.target.clientWidth,
- height: event.target.clientHeight
- },
- imageSize: {
- width: event.target.clientWidth * this.extractConfig.x,
- height: event.target.clientHeight * this.extractConfig.y
- },
- clickPosition: {
- extract: {
- x: event.offsetX,
- y: event.offsetY
- },
- image: {
- x: event.offsetX + (this.extracts[index].index % this.extractConfig.x) * event.target.clientWidth,
- y: event.offsetY + (Math.floor(this.extracts[index].index / this.extractConfig.x)) * event.target.clientHeight
- }
- }
- }
- const calculatedRealData = {}
- calculatedRealData.extractSize = {
- width: this.extractsInfos.extractsSize.width,
- height: this.extractsInfos.extractsSize.height
- }
- calculatedRealData.imageSize = {
- width: this.extractsInfos.image.metadata.width,
- height: this.extractsInfos.image.metadata.height
- }
- calculatedRealData.clickPosition = {
- extract: {
- x: Math.floor((calculatedRealData.imageSize.width * clientSideData.clickPosition.extract.x) / clientSideData.imageSize.width),
- y: Math.floor((calculatedRealData.imageSize.height * clientSideData.clickPosition.extract.y) / clientSideData.imageSize.height)
- },
- image: {
- x: Math.floor((calculatedRealData.imageSize.width * clientSideData.clickPosition.image.x) / clientSideData.imageSize.width),
- y: Math.floor((calculatedRealData.imageSize.height * clientSideData.clickPosition.image.y) / clientSideData.imageSize.height)
- }
- }
- // Sending event to WebSocket server
- const loggedObj = {
- experimentName: this.experimentName,
- sceneName: this.sceneName,
- extractConfig: this.extractConfig,
- clickedExtract: {
- link: this.extracts[index].link,
- quality: this.extracts[index].quality,
- nextQuality: this.extracts[index].nextQuality,
- precQuality: this.extracts[index].precQuality,
- zone: this.extracts[index].zone,
- index: this.extracts[index].index
- },
- action,
- clientSideData,
- calculatedRealData
- }
- return loggedObj
- },
- // Finish an experiment, sending full data to the server
- // Don't forget to surcharge this function when using this mixin to add more data
- finishExperiment() {
- const obj = {
- experimentName: this.experimentName,
- sceneName: this.sceneName,
- extractConfig: this.extractConfig,
- extracts: this.extracts.map(x => ({
- index: x.index,
- link: x.link,
- nextQuality: x.nextQuality,
- precQuality: x.precQuality,
- quality: x.quality,
- zone: x.zone
- })),
- qualities: this.qualities,
- referenceImage: this.referenceImage,
- comment: this.comment
- }
- this.sendMessage({ msgId: experimentMsgId.VALIDATED, msg: obj })
- this.setExperimentFinished()
- this.$router.push(`/experiments/${this.experimentName}/${this.sceneName}/validated`)
- }
- }
- }
- </script>
- <style>
- /* White border when hovering on extracts */
- .extract-hover-border:hover {
- z-index: 1;
- outline: 2px #f4f4f4 solid;
- }
- .img-extract-loader {
- height: 100%;
- width: 0px;
- display: flex;
- justify-content: center;
- align-items: center;
- }
- </style>
|