Skip to content

Commit

Permalink
Merge pull request #153 from LCOGT/update/remove-rgb-filter-restriction
Browse files Browse the repository at this point in the history
base scaling off input filter, remove check for correct filter to allow any
  • Loading branch information
LTDakin authored Feb 12, 2025
2 parents fef3745 + 392f411 commit 5c0c69e
Show file tree
Hide file tree
Showing 7 changed files with 74 additions and 96 deletions.
45 changes: 23 additions & 22 deletions src/components/DataSession/OperationWizard.vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<script setup>
import { ref, onMounted, computed, defineEmits, defineProps} from 'vue'
import { ref, onMounted, computed} from 'vue'
import { fetchApiCall, handleError } from '@/utils/api'
import { calculateColumnSpan } from '@/utils/common'
import ImageGrid from '../Global/ImageGrid'
Expand Down Expand Up @@ -119,7 +119,8 @@ const wizardTitle = computed(() => {
const isInputComplete = computed(() => {
for (const inputKey in selectedOperationInput.value) {
const input = selectedOperationInput.value[inputKey]
if ( input === undefined || input === null || input.length == 0) {
const minimum = selectedOperationInputs.value[inputKey].minimum
if ( input === undefined || input === null || (minimum ? input.length < minimum : input.length == 0)) {
return false
}
}
Expand All @@ -135,12 +136,24 @@ const operationRequiresInputScaling = computed(() => {
return false
})
function sortImagesByFilter(filters){
// Trim and lowercase all filters
for (const filter in filters){
filters[filter] = filters[filter].trim().toLowerCase()
}
// Place desired filters at the front, followed by the rest
return [
...props.images.filter(image => filters.includes(image.filter?.trim().toLowerCase())),
...props.images.filter(image => !filters.includes(image.filter?.trim().toLowerCase()))
]
}
function goForward() {
if (page.value == 'select') {
// if there are no images for a filter required by the operation, do not proceed
for (const inputKey in selectedOperationInputs.value) {
const inputField = selectedOperationInputs.value[inputKey]
if (inputField.type == 'file' && imagesWithFilter(inputField.filter).length == 0){
if (inputField.type == 'file' && props.images.length == 0){
return
}
}
Expand Down Expand Up @@ -173,35 +186,23 @@ function submitOperation() {
function setOperationInputImages() {
for (const inputKey in selectedImages.value) {
let input = []
const filter = selectedOperationInputs.value[inputKey].filter
selectedImages.value[inputKey].forEach(index => {
input.push(imagesWithFilter(filter)[index])
selectedImages.value[inputKey].forEach(basename => {
input.push(props.images.find(image => image.basename == basename))
})
selectedOperationInput.value[inputKey] = input
}
}
function imagesWithFilter(filters) {
if(!filters) {
return props.images
}
const imagesFiltered = props.images.filter((image) => filters.includes(image.filter))
if (imagesFiltered.length == 0) {
alert.setAlert('warning', 'Operation requires images with filter ' + filters.join(', '))
}
return imagesFiltered
}
function selectImage(inputKey, imageIndex) {
function selectImage(inputKey, basename) {
const inputImages = selectedImages.value[inputKey]
const maxImages = selectedOperationInputs.value[inputKey].maximum
if (inputImages.includes(imageIndex)) {
inputImages.splice(inputImages.indexOf(imageIndex), 1)
if (inputImages.includes(basename)) {
inputImages.splice(inputImages.indexOf(basename), 1)
setOperationInputImages()
}
else if (!maxImages || inputImages.length < maxImages){
inputImages.push(imageIndex)
inputImages.push(basename)
setOperationInputImages()
}
else{
Expand Down Expand Up @@ -279,7 +280,7 @@ function selectImage(inputKey, imageIndex) {
{{ inputDescription.name }}
</div>
<image-grid
:images="imagesWithFilter(inputDescription.filter)"
:images="inputDescription.filter ? sortImagesByFilter(inputDescription.filter) : props.images"
:selected-images="selectedImages[inputKey]"
:column-span="calculateColumnSpan(images.length, imagesPerRow)"
:allow-selection="true"
Expand Down
14 changes: 7 additions & 7 deletions src/components/Global/ImageGrid.vue
Original file line number Diff line number Diff line change
Expand Up @@ -25,20 +25,20 @@ const props = defineProps({
}
})
let imageDetails = ref({})
const configurationStore = useConfigurationStore()
const alertsStore = useAlertsStore()
const thumbnailsStore = useThumbnailsStore()
const emit = defineEmits(['selectImage'])
const showAnalysisDialog = ref(false)
const imageDetails = ref({})
const analysisImage = ref({})
var doubleClickTimer = 0
const handleClick = (index) => {
const handleClick = (basename) => {
clearTimeout(doubleClickTimer)
// timeout length indicates how long to wait for a second click before treating as a single click
doubleClickTimer = setTimeout(() => {
emit('selectImage', index)
emit('selectImage', basename)
doubleClickTimer = 0
}, 250)
}
Expand Down Expand Up @@ -69,8 +69,8 @@ const launchAnalysis = (image) => {
}
}
const isSelected = (index) => {
return props.selectedImages.includes(index)
const isSelected = (basename) => {
return props.selectedImages.includes(basename)
}
watch(() => props.images, () => {
Expand Down Expand Up @@ -102,9 +102,9 @@ watch(() => props.images, () => {
:src="imageDetails[image.basename]"
:alt="image.basename"
cover
:class="{ 'selected-image': isSelected(index) }"
:class="{ 'selected-image': isSelected(image.basename) }"
aspect-ratio="1"
@click="handleClick(index)"
@click="handleClick(image.basename)"
@dblclick="handleDoubleClick(image)"
>
<filter-badge
Expand Down
8 changes: 4 additions & 4 deletions src/components/Global/Scaling/ImageScaler.vue
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,8 @@ const rawData = ref({})
const sliderRange = ref([0, 65535])
const zScaleValues = ref([0, 65535])
const titleText = computed(() => {
return props.imageName.replace('_', ' ')
const filterName = computed(() => {
return props.imageName.replace('_input', ' ')
})
const maxPixelValue = computed(() => {
Expand Down Expand Up @@ -105,13 +105,13 @@ onMounted(async () => {
:max-size="props.maxSize"
:image-data="rawData"
:scale-points="sliderRange"
:filter="props.image.filter"
:filter="filterName"
:image-name="props.imageName"
:composite-name="props.compositeName"
/>
<v-col>
<h3 class="image-scale-title text-capitalize">
{{ titleText }}
{{ filterName }} Input
</h3>
<histogram-slider
:selected-color="filterToColor(props.image.filter)"
Expand Down
20 changes: 5 additions & 15 deletions src/components/Project/ImageList.vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<script setup>
import { ref, computed, defineProps } from 'vue'
import { ref, defineProps } from 'vue'
import { useThumbnailsStore } from '@/stores/thumbnails'
import { useAlertsStore } from '@/stores/alerts'
import { useConfigurationStore } from '@/stores/configuration'
Expand All @@ -20,16 +20,6 @@ const props = defineProps({
const emit = defineEmits(['selectImage'])
// Adding index key so @select component emits selected image index
const indexedImages = computed(() => {
return props.images ? props.images.map((image, index) => {
return {
...image,
index
}
}) : []
})
const headers = ref([
{ title: 'IMAGE', align: 'start', sortable: false, key: 'url' },
{ title: 'FILTER', align: 'start', sortable: true, key: 'FILTER' },
Expand All @@ -48,8 +38,8 @@ const configurationStore = useConfigurationStore()
// checks difference between table and parent selected images and emits the difference
function select(tableModel){
const symDiffSelected = tableModel.filter(image => !props.selectedImages.includes(image)).concat(props.selectedImages.filter(image => !tableModel.includes(image)))
for (const index of symDiffSelected) {
emit('selectImage', index)
for (const basename of symDiffSelected) {
emit('selectImage', basename)
}
}
Expand Down Expand Up @@ -82,8 +72,8 @@ function launchAnalysis(image){
v-if="images"
:model-value="selectedImages"
:headers="headers"
:items="indexedImages"
item-value="index"
:items="images"
item-value="basename"
:items-per-page="images.length"
show-select
hover
Expand Down
7 changes: 4 additions & 3 deletions src/stores/scaling.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { defineStore } from 'pinia'
import { convertFilter, filterToPixelIndex } from '@/utils/common'
import { filterToPixelIndex } from '@/utils/common'

/**
* This store is used to store intermediate images in scaling to then use in a composite image
Expand All @@ -18,13 +18,14 @@ export const useScalingStore = defineStore('scaling', {
if (!(combinedImageName in this.scaledImageArrays)) {
this.scaledImageArrays[combinedImageName] = {}
}
let rgbFilter = convertFilter(filter)
if (!('combined' in this.scaledImageArrays[combinedImageName])) {
this.arrayChanged[combinedImageName] = 0
this.scaledImageArrays[combinedImageName]['combined'] = new ImageData(maxSize, maxSize)
this.scaledImageArrays[combinedImageName]['combined'].data.fill(255)
}
let filterIndex = filterToPixelIndex(rgbFilter)

const filterIndex = filterToPixelIndex(filter)

for (let i = filterIndex, j=0; j < imageDataArray.length; i += 4, j++) {
this.scaledImageArrays[combinedImageName]['combined'].data[i] = imageDataArray[j]
}
Expand Down
64 changes: 25 additions & 39 deletions src/utils/common.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,53 +3,39 @@ const calculateColumnSpan = (imageCount, imagesPerRow) => {
return totalColumns
}

const convertFilter = (filter) => {
if (filter === 'V' || filter === 'gp') {
return 'g'
function filterToPixelIndex(filter) {
const filterPixelMap = {
'red': 0,
'green': 1,
'blue': 2,
}
else if (filter === 'rp' || filter === 'r') {
return 'r'
}
else if (filter === 'B') {
return 'b'
}
}

const filterToPixelIndex = (filter) => {
if (filter === 'r') {
return 0
}
else if (filter === 'g') {
return 1
}
else if (filter === 'b') {
return 2
}
return filterPixelMap[filter.trim().toLowerCase()]
}

const filterToColor = (filter) => {
switch (filter) {
case 'R': case 'rp':
return 'red'
case 'V': case 'gp':
return 'green'
case 'B':
return 'blue'
default:
return 'var(--light-blue)'
}
filter = filter || ''
const filterColorMap = [
{ color: 'red', filters: ['r', 'rp', 'ip', 'h-alpha'] },
{ color: 'green', filters: ['v', 'gp', 'oiii'] },
{ color: 'blue', filters: ['b', 'sii'] },
]

const lowerCaseFilter = filter.trim().toLowerCase()
const found = filterColorMap.find(({ filters }) => filters.includes(lowerCaseFilter))
return found ? found.color : 'var(--light-blue)'
}

function siteIDToName(siteID) {
const siteIDMap = {
'COJ': 'Siding Spring Observatory @ New South Wales',
'CPT': 'South African Astronomical Observatory @ Cape Town',
'TFN': 'Teide Observatory @ Tenerife',
'LSC': 'Cerro Tololo Inter-American Observatory @ Chile',
'ELP': 'McDonald Observatory @ University of Texas',
'OGG': 'Haleakala Observatory @ Maui',
'TLV': 'Wise Observatory @ Tel Aviv University',
'NGQ': 'Ali Observatory @ Tibet',
'COJ': 'Siding Spring @ New South Wales',
'CPT': 'South African Astronomical @ Cape Town',
'TFN': 'Teide @ Tenerife',
'LSC': 'Cerro Tololo Inter-American @ Chile',
'ELP': 'McDonald @ University of Texas',
'OGG': 'Haleakala @ Maui',
'TLV': 'Wise @ Tel Aviv University',
'NGQ': 'Ali @ Tibet',
}

return siteIDMap[siteID?.toUpperCase()] || siteID
Expand All @@ -67,4 +53,4 @@ function initializeDate(dateString = 'none', defaultOffsetDays = 0) {
return isNaN(date.getTime()) ? new Date(Date.now() + defaultOffsetDays * 24 * 3600 * 1000) : date
}

export { calculateColumnSpan, siteIDToName, initializeDate, convertFilter, filterToPixelIndex, filterToColor }
export { calculateColumnSpan, siteIDToName, initializeDate, filterToPixelIndex, filterToColor }
12 changes: 6 additions & 6 deletions src/views/ProjectView.vue
Original file line number Diff line number Diff line change
Expand Up @@ -34,19 +34,19 @@ const selectedImages = computed(() => {
// returns a list combining all the selected images in all projects to be used for a new data session
return Object.keys(selectedImagesByProposal.value).reduce((acc, projectId) => {
const proposalImages = imagesByProposal.value[projectId] ? imagesByProposal.value[projectId] : []
const proposalSelectedImageIndexes = selectedImagesByProposal.value[projectId]
return acc.concat(proposalSelectedImageIndexes.map(imageIndex => proposalImages[imageIndex]))
const proposalSelectedImages = selectedImagesByProposal.value[projectId]
return acc.concat(proposalImages.filter(image => proposalSelectedImages.includes(image.basename)))
}, [])
})
function selectImage(proposalIndex, imageIndex) {
function selectImage(proposalIndex, basename) {
// accepts either a list of selected images or a single image index to toggle selection on
const proposalSelectedImages = selectedImagesByProposal.value[proposalIndex]
if(proposalSelectedImages.includes(imageIndex)){
proposalSelectedImages.splice(proposalSelectedImages.indexOf(imageIndex), 1)
if(proposalSelectedImages.includes(basename)){
proposalSelectedImages.splice(proposalSelectedImages.indexOf(basename), 1)
} else {
proposalSelectedImages.push(imageIndex)
proposalSelectedImages.push(basename)
}
}
Expand Down

0 comments on commit 5c0c69e

Please sign in to comment.