Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enable custom resolution #148

Merged
merged 4 commits into from
Mar 17, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
164 changes: 159 additions & 5 deletions app/src/components/LayerControlBody.vue
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,65 @@
</div>
</div>
</div>

<!-- Resolution -->
<div class="section-wrapper mb-1">
<div
@click="lockIncVol({ incVolTranslationLock: !incVolTranslationLock})">
<font-awesome-icon :icon="lockIconTranslation"></font-awesome-icon>
</div>
<section-component
:forceHide="incVolTranslationLock"
id="translation-component">
<template slot="header">
<strong :class="incVolTranslationLock ? 'text-muted' : ''">
Resolution
</strong>
</template>
<template slot="body">

<b-form-radio-group
id="btn-radios-1"
v-model="selectedUnit"
:options="units"
:aria-describedby="'ariaDescribedby'"
name="radios-btn-default"
buttons></b-form-radio-group>
<div>
<label class="mr-2">X-axis</label>
<div class="input-group">
<input v-model="selectedResolutionX" @input="setResolution" type="number" class="form-control rounded-0">
<div class = "input-group-append">
<span class = "input-group-text rounded-0">{{selectedUnit}}</span>
</div>
</div>
</div>
<div>
<label class="mr-2 mt-2">Y-axis</label>
<div class="input-group">
<input v-model="selectedResolutionY" @input="setResolution" type="number" class="form-control rounded-0">
<div class = "input-group-append">
<span class = "input-group-text rounded-0">{{selectedUnit}}</span>
</div>
</div>
</div>
<div>
<label class="mr-2 mt-2">Z-axis</label>
<div class="input-group">
<input v-model="selectedResolutionZ" @input="setResolution" type="number" class="form-control rounded-0">
<div class = "input-group-append">
<span class = "input-group-text rounded-0">{{selectedUnit}}</span>
</div>
</div>
</div>

<b-button size="sm" class="mt-3" @click="resetResolution">
Reset
</b-button>
</template>

</section-component>
</div>

<!-- scale -->
<div class="section-wrapper mb-1">
Expand Down Expand Up @@ -155,6 +214,7 @@
</section-component>
</div>


<!-- translation -->
<div class="section-wrapper mb-1">
<div
Expand Down Expand Up @@ -296,7 +356,14 @@ export default {
scaleMax: 10,
scaleStep: 0.01,

testValue: 0
testValue: 0,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not used?


selectedResolutionX: 0,
selectedResolutionY: 0,
selectedResolutionZ: 0,
Comment on lines +361 to +363
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think these should be computed properties, rather than data.

They can be computed based on scale and selectedIncomingVolumeResolution{X,Y,Z}


units: ['nm', 'μm', 'mm', 'cm'],
selectedUnit: 'nm'
}
},
computed: {
Expand All @@ -310,10 +377,11 @@ export default {
'incVolScaleLock',
]),
...mapGetters('dataSelectionStore', [
'selectedIncomingVolume',
'selectedIncomingVolume'
]),
...mapState('dataSelectionStore', [
'incomingVolumes'
'incomingVolumes',
'selectedIncomingVolumeResolution',
]),
...mapState('nehubaStore', {
lockIconTranslation: state => state.incVolTranslationLock ? 'lock' : 'lock-open',
Expand All @@ -328,6 +396,7 @@ export default {
return this.testScale[0]
},
set: function (value) {
this.selectedResolutionX = this.fixed(+this.testScaleX * this.selectedIncomingVolumeResolutionX)
this.scaleEvent({
axis: 'x',
value
Expand All @@ -339,6 +408,7 @@ export default {
return this.testScale[1]
},
set: function (value) {
this.selectedResolutionY = this.fixed(+this.testScaleY * this.selectedIncomingVolumeResolutionY)
this.scaleEvent({
axis: 'y',
value
Expand All @@ -350,6 +420,7 @@ export default {
return this.testScale[2]
},
set: function (value) {
this.selectedResolutionZ = this.fixed(+this.testScaleZ * this.selectedIncomingVolumeResolutionZ)
this.scaleEvent({
axis: 'z',
value
Expand Down Expand Up @@ -471,13 +542,36 @@ export default {
},
renderedIncomingVolumes: function () {
return [this.dummyIncomingTemplate].concat(this.$store.state.incomingVolumes)
}
},
selectedIncomingVolumeResolutionX: {
get: function () {
return this.nmToSelectedUnit(this.selectedIncomingVolumeResolution[0])
}
},
selectedIncomingVolumeResolutionY: {
get: function () {
return this.nmToSelectedUnit(this.selectedIncomingVolumeResolution[1])
}
},
selectedIncomingVolumeResolutionZ: {
get: function () {
return this.nmToSelectedUnit(this.selectedIncomingVolumeResolution[2])
}
},
},
watch: {
isotropic: function (flag) {
if (flag) {
this.isotropicScaleEvent({ value: this.testScaleX })
}
},
selectedIncomingVolumeResolution: function (flag) {
this.resetResolution()
},
selectedUnit: function(newUnit, prevUnit) {
this.selectedResolutionX = this.changeUnit(this.selectedResolutionX, prevUnit)
this.selectedResolutionY = this.changeUnit(this.selectedResolutionY, prevUnit)
this.selectedResolutionZ = this.changeUnit(this.selectedResolutionZ, prevUnit)
}
},
methods: {
Expand Down Expand Up @@ -511,6 +605,14 @@ export default {
collapse: `scaleBySliderIsotropic`
})


this.selectedResolutionX = this.fixed(+this.testScaleX * this.selectedIncomingVolumeResolutionX)
this.selectedResolutionY = this.fixed(+this.testScaleY * this.selectedIncomingVolumeResolutionY)
this.selectedResolutionZ = this.fixed(+this.testScaleZ * this.selectedIncomingVolumeResolutionZ)

this.setIsotropicScale(value)
},
setIsotropicScale: function(value) {
this.setScaleInc({
axis: 'x',
value
Expand Down Expand Up @@ -610,7 +712,59 @@ export default {
name: `flip on axis ${axis}`
})
this.flipAxis({ axis })
}
},
setResolution: function () {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[minor point] it is slightly awkward to use the oninput to trigger state update.

It would probably more declarative to use a watch: { selectedResolutionX }, or indeed, if we are using computed properties, a setter method for the computed property to trigger state update.

semantically, it makes more sense (e.g. anything in the component can trigger a method call. But if the update state is hooked to a watch hook or setter, then only value update triggers the state update)


const isotropic = +this.selectedResolutionX === +this.selectedResolutionY && +this.selectedResolutionY === +this.selectedResolutionZ
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am somewhat confused.

isotropic is a state that's set by the user, not computed on the fly.

This should be a simple check if (this.isotropic) ? Or am I missing something?


if (isotropic) {
this.setIsotropicScale(+this.selectedResolutionX/this.selectedIncomingVolumeResolutionX)
} else {
this.isotropic = false
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ditto above.

this.isotropic should be a state that is exclusively controlled by the user. the application should not be able to influence it.

const scaleX = this.selectedResolutionX/this.selectedIncomingVolumeResolutionX
const scaleY = this.selectedResolutionY/this.selectedIncomingVolumeResolutionY
const scaleZ = this.selectedResolutionZ/this.selectedIncomingVolumeResolutionZ

this.scaleEvent({axis: 'x', value: scaleX})
this.scaleEvent({axis: 'y', value: scaleY})
this.scaleEvent({axis: 'z', value: scaleZ})
}
},

nmToSelectedUnit: function (num) {
const div = this.selectedUnit === 'μm'? 1000
: this.selectedUnit === 'mm'? 1e6
: this.selectedUnit === 'cm'? 1e7 : 1
return +num / div
},

changeUnit: function (num, prevUnit) {
const mult = prevUnit === 'μm'? 1000
: prevUnit === 'mm'? 1e6
: prevUnit === 'cm'? 1e7 : 1
const nm = +num * mult
return this.fixed(this.nmToSelectedUnit(nm))
},
fixed: function (num) {
const decimal = this.selectedUnit === 'μm'? 2
: this.selectedUnit === 'mm'? 3
: this.selectedUnit === 'cm'? 4 : 0
return parseFloat(num.toFixed(decimal))
},
resetResolution: function () {
this.testScaleX = 1
this.testScaleY = 1
this.testScaleZ = 1
this.isotropic = true

this.selectedUnit = 'nm'
this.selectedResolutionX = this.selectedIncomingVolumeResolutionX
this.selectedResolutionY = this.selectedIncomingVolumeResolutionY
this.selectedResolutionZ = this.selectedIncomingVolumeResolutionZ
},
},
created: function () {
this.resetResolution()
}
}
</script>
Expand Down
27 changes: 26 additions & 1 deletion app/src/store/dataSelectionStore.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ const dataSelectionStore = {
],

selectedIncomingVolumeId: null,
selectedIncomingVolumeResolution: null,

incomingVolumes: DEFAULT_BUNDLED_INCOMING_VOLUMES,
},
Expand All @@ -34,6 +35,9 @@ const dataSelectionStore = {
setSelectedIncomingVolumeId (state, id) {
state.selectedIncomingVolumeId = id
},
setSelectedIncomingVolumeResolution (state, data) {
state.selectedIncomingVolumeResolution = data.resolution
},
setIncomingVolumes (state, { volumes }) {
state.incomingVolumes = volumes
},
Expand All @@ -43,7 +47,7 @@ const dataSelectionStore = {
const vol = state.referenceVolumes.find(({ id: _id }) => _id === id)
if (vol) commit('setSelectedReferenceVolumeWithId', id)
},
selectIncomingVolumeWithId ({ commit, state }, id) {
selectIncomingVolumeWithId ({ commit, state, dispatch }, id) {
const vol = state.incomingVolumes.find(({ id: _id }) => _id === id)
if (vol) {
if (vol.extra && vol.extra.neuroglancer && vol.extra.neuroglancer.transform) {
Expand All @@ -56,11 +60,32 @@ const dataSelectionStore = {
{ matrix },
{ root: true })
}

dispatch('setIncomingVolumeResolution', id)

commit('setSelectedIncomingVolumeId', id)
const { shaderScaleFactor } = vol
commit('viewerPreferenceStore/setShaderScaleFactor', shaderScaleFactor || 1, { root: true })
}
},

async ['setIncomingVolumeResolution'] ({ commit, state, dispatch, rootGetters }, id) {
try {
const vol = state.incomingVolumes.find(({ id: _id }) => _id === id)
const { data } = await axios(`${vol.imageSource.substring(14)}/info`)
const resolution = data.scales[0].resolution
const size = data.scales[0].size
// const resolution = {}
// resolution.voxel = data.scales[0].resolution
// resolution.real = data.scales[0].resolution.map((r, i) => r * data.scales[0].size[i])
commit('setSelectedIncomingVolumeResolution', {resolution, size})
} catch (error) {
dispatch('updateIncVolumesResult', {
error: error ? error : null
})
}
},

updateIncVolumesResult (store, {error, message}) {
/**
* required for subscribe action
Expand Down