diff --git a/components/OperonStructure.vue b/components/OperonStructure.vue index 688d6ad6f9457a00153a788a8801dd7c5af4abd6..421a0f76baf4751c693bfb7f3d0382406d852761 100644 --- a/components/OperonStructure.vue +++ b/components/OperonStructure.vue @@ -35,15 +35,14 @@ const margin = ref<PlotMargin>({ const gbContainer = ref(null) const geneToHighlight = ref<string | null>(null) const snackbar = ref(false) -const innerPaddingRatio = ref<number>(0.1) +const innerPaddingRatio = ref<number>(0.05) // const color = d3.scaleOrdinal(d3.schemeCategory10); const domain = computed(() => { const genes = toValue(computedGenes) return genes?.map(d => { return d.gene }) }) -const maxOperonSize = computed(() => { - return props.maxOperonSize || 0 -}) +const maxOperonSize = toRef(props, "maxOperonSize") + const operonLength = computed(() => { // return props.maxOperonSize || 0 const genes = toValue(computedGenes) @@ -54,7 +53,6 @@ const operonLength = computed(() => { const innerPadding = computed(() => { const totalGeneLengthVal = toValue(operonLength) // const totalGeneLengthVal = props.maxOperonSize || 0 - const genes = toValue(computedGenes) let innerPadding = innerPaddingRatio.value // if (g enes.length === 1) innerPadding = 0 return totalGeneLengthVal * innerPadding @@ -75,7 +73,7 @@ const totalGeneLengthWithPadding = computed(() => { const allOperonPadding = computed(() => { - const maxOperonSizeVal = toValue(maxOperonSize) + const maxOperonSizeVal = toValue(maxOperonSize) || 0 const innerPaddingRatioVal = toValue(innerPaddingRatio) return maxOperonSizeVal * innerPaddingRatioVal }) @@ -89,21 +87,42 @@ const paddingPerGene = computed(() => { }) const maxOperonSizeWithPadding = computed(() => { - return toValue(allOperonPadding) + toValue(maxOperonSize) + return toValue(allOperonPadding) + (toValue(maxOperonSize) || 0) }) const domainGenes = computed(() => { return [0, maxOperonSizeWithPadding.value] // return [0, totalGeneLength.value] }) + +const structureRange = computed(() => { + const genes = toValue(computedGenes) + const minStructureWidth = genes.length * 100 + + // get genes width + const genesWidth = xScaleGenes.value(totalGeneLengthWithPadding.value) + + if (genesWidth > minStructureWidth) { + return [0, genesWidth] + } + else { + if (minStructureWidth > toValue(computedPlotWidth)) return [0, toValue(computedPlotWidth)] + return [0, minStructureWidth] + } + + + +}) + const xScaleStructure = computed(() => { return d3.scaleBand() .paddingInner(0) .domain(toValue(domain)) - .range([0, xScaleGenes.value(totalGeneLengthWithPadding.value)]) + .range(toValue(structureRange)) }) + const xScaleGenes = computed(() => { return d3.scaleLinear() .domain(toValue(domainGenes)) @@ -556,7 +575,6 @@ watch(genesTextRotate, (genesTextRotateVal) => { </v-btn></v-card-title> <v-card-subtitle>defenseFinder model version : {{ structureVersion }}</v-card-subtitle> </v-card-item> - <svg ref="svgRef" :width="computedContainerWidth" :height="plotHeight"> <g class="x-axis" /> </svg> diff --git a/components/content/ArticleStructure.vue b/components/content/ArticleStructure.vue index a7c99ab2fa0677afd69e506a1df8d5f9cc3ba5c8..1d9e2eae160a7a0fc905da60464752d38218b9ab 100644 --- a/components/content/ArticleStructure.vue +++ b/components/content/ArticleStructure.vue @@ -3,7 +3,7 @@ import { toValue } from '@vueuse/core'; // import MolstarPdbePlugin from './MolstarPdbePlugin.vue'; import * as d3 from "d3"; import SystemOperonStructure from './SystemOperonStructure.vue'; -import type { StructureItem } from '~/types/structure'; +import type { OperonStructureIndexName, StructureItem, StructureOperonGene } from '~/types/structure'; import type { SearchParams, SearchResponse } from 'meilisearch'; import { useStructuresBasket } from '~/stores/structuresBasket'; @@ -13,8 +13,10 @@ const client = useMeiliSearchRef() const structureBasket = useStructuresBasket() // get the structures const structures = ref<SearchResponse<StructureItem, SearchParams> | undefined>() +const operonStructures = ref<SearchResponse<StructureOperonGene, SearchParams> | undefined>() const structureTitle = ref("Structure") const msIndexName = ref<string>("structure") +const operonSructIndexName = ref<OperonStructureIndexName>("systemoperonstruct") // const stuctureUrls = ref<string[] | undefined>(undefined) const headers = ref<Record<string, any>[]>([ { title: 'Structure', key: 'structure', sortable: false, removable: false, fixed: true, minWidth: "110px" }, @@ -43,6 +45,7 @@ const computedSystem = computed(() => { }) onMounted(() => { fetchStructures() + fetchAllOperonStructures() }) function extractGeneName(name: string) { @@ -98,6 +101,31 @@ const perSubsystemStructures = computed(() => { else { return [] } }) +const groupedOperonStructures = computed(() => { + const operonStructuresVal = toValue(operonStructures) + if (operonStructuresVal && operonStructuresVal.hits.length > 0) { + return d3.groups(operonStructuresVal.hits, d => d.system, d => d.subsystem) + } + else { + return [] + } +}) + + +const maxSizeOperonStructures = computed(() => { + const groupedOperonStructuresVal = toValue(groupedOperonStructures) + return groupedOperonStructuresVal.reduce<number>((systemMax, [_, systems]) => { + const valMax = d3.max([ + systems.reduce<number>((subsystemMax, [_, subsystems]) => { + const valMax = d3.max([subsystems.reduce<number>((totSize, subsystem) => { + return totSize + subsystem.size + }, 0), subsystemMax]) || subsystemMax + return valMax + }, 0), systemMax] + ) || systemMax + return valMax + }, 0) +}) const allSubsystems = computed(() => { const perSubsystemStructuresVal = toValue(perSubsystemStructures) @@ -114,6 +142,7 @@ async function fetchStructures() { const d = await client.index(toValue(msIndexName)).search<StructureItem>("", { facets: ["*"], filter: [`system='${toValue(computedSystem)}'`, "completed='true'"], + limit: 500000 }) structures.value = d } catch (error) { @@ -121,6 +150,20 @@ async function fetchStructures() { } } +async function fetchAllOperonStructures() { + try { + const d = await client.index(toValue(operonSructIndexName)).search<StructureOperonGene>("", { + facets: ["*"], + filter: undefined, + limit: 500000 + }) + operonStructures.value = d + } catch (error) { + throw createError(`Cannot get structure for system: ${toValue(computedSystem)}`) + } +} + + </script> <template> <v-card flat color="transparent"> @@ -128,7 +171,8 @@ async function fetchStructures() { <template v-for="[subsystem, structures] in perSubsystemStructures" :key="subsystem[0]"> <!-- <ProseH3>{{ subsystem }}</ProseH3> --> <v-card-text class="pa-1"> - <SystemOperonStructure :system="computedSystem" :subsystem="subsystem" :structures="structures" :subsystems="allSubsystems"/> + <SystemOperonStructure :system="computedSystem" :subsystem="subsystem" :structures="structures" + :subsystems="allSubsystems" :max-operon-size="maxSizeOperonStructures" /> </v-card-text> </template> <!-- <ProseH3>Summary</ProseH3> --> diff --git a/components/content/SystemOperonStructure.vue b/components/content/SystemOperonStructure.vue index 3ac058bbc22760c5cbd0caeebefe0576e1195683..741ba4be5500f6eb1cb607831ec081c7228deb9e 100644 --- a/components/content/SystemOperonStructure.vue +++ b/components/content/SystemOperonStructure.vue @@ -11,17 +11,20 @@ interface Props { subsystem: string structures: StructureItem[] subsystems: string[] + maxOperonSize: number } const { page } = useContent(); -const { system, subsystem, structures, subsystems } = withDefaults(defineProps<Props>(), { +const props = withDefaults(defineProps<Props>(), { + }); +const { system, subsystem, structures, subsystems } = props const client = useMeiliSearchRef() const pending = ref<boolean>(false) const sizeGene = ref<number>(140) - +const maxOperonSize = toRef(props, "maxOperonSize") const msIndexName = ref<'systemoperonstruct'>("systemoperonstruct") const msResponse = ref<SearchResponse<StructureOperonGene> | undefined>(undefined) const allSubsystems = ref<SearchResponse<StructureOperonGene> | undefined>(undefined) @@ -114,15 +117,15 @@ onMounted(() => { fetchSubsytems() }) -const maxOperonSize = computed(() => { - const allSubsystemsVal = toValue(allSubsystems) - return d3.max(d3.groups(allSubsystemsVal?.hits ?? [], d => d.subsystem) - .map(([_, val]) => { - return val.reduce((acc, curr) => { - return acc + curr.size - }, 0) - })) -}) +// const maxOperonSize = computed(() => { +// const allSubsystemsVal = toValue(allSubsystems) +// return d3.max(d3.groups(allSubsystemsVal?.hits ?? [], d => d.subsystem) +// .map(([_, val]) => { +// return val.reduce((acc, curr) => { +// return acc + curr.size +// }, 0) +// })) +// }) const msFilters = computed(() => { diff --git a/types/structure.ts b/types/structure.ts index 67004bdd4f5ce1bbeb551cb1f8abfa19f1027cd2..5753b7264bd2b6fa34e0355e6e0bbcbdbec4d19e 100644 --- a/types/structure.ts +++ b/types/structure.ts @@ -1,3 +1,6 @@ +export type OperonStructureIndexName = 'systemoperonstruct' + + export interface StructureOperonGene { gene: string id: number