From 9edc8f5b3c469a404d090785f223224a42ba1b99 Mon Sep 17 00:00:00 2001 From: Volker Austrup Date: Tue, 6 Jun 2023 11:32:52 +0100 Subject: [PATCH 1/8] interactive pull plots --- src/components/WorkspaceDetailView.vue | 45 +++- src/components/charts/PullChart.vue | 43 +--- src/components/charts/PullChartEntry.vue | 84 +++++++- src/components/charts/StackedChart.vue | 59 +++--- src/components/charts/SystematicChart.vue | 238 ++++++++++++++++++++++ src/interfaces.ts | 10 +- src/stores/channel.ts | 12 ++ src/stores/workspace.ts | 175 ++++++++++++---- 8 files changed, 557 insertions(+), 109 deletions(-) create mode 100644 src/components/charts/SystematicChart.vue create mode 100644 src/stores/channel.ts diff --git a/src/components/WorkspaceDetailView.vue b/src/components/WorkspaceDetailView.vue index 96b2f34..50ac9e0 100644 --- a/src/components/WorkspaceDetailView.vue +++ b/src/components/WorkspaceDetailView.vue @@ -11,6 +11,7 @@ import ChannelListItem from 'src/components/ChannelListItem.vue'; import NormFactorListItem from 'src/components/NormFactorListItem.vue'; import { useWorkspaceStore } from 'src/stores/workspace'; import CorrelationChart from 'src/components/charts/CorrelationChart.vue'; +import SystematicChart from 'src/components/charts/SystematicChart.vue'; const props = defineProps<{ id: number; @@ -120,6 +121,7 @@ const height = computed(() => { :key="channel.name" :id="id" :channel_index="channel_index" + :postfit="false" /> @@ -131,15 +133,54 @@ const height = computed(() => { /> + +
+

{{ modifier_name }}

+
+ +
+
+
+ Fit Workspace{{ + workspace_store.fitted ? 'Reset to fit results' : 'Fit Workspace' + }}
+
+ +
diff --git a/src/components/charts/PullChart.vue b/src/components/charts/PullChart.vue index 2bbca9b..eca5590 100644 --- a/src/components/charts/PullChart.vue +++ b/src/components/charts/PullChart.vue @@ -19,7 +19,7 @@ const workspace_store = useWorkspaceStore(props.id)(); const height_per_entry = 25; const height = computed(() => { - return workspace_store.fitresults.labels.length * height_per_entry + 100; + return workspace_store.nps.labels.length * height_per_entry + 100; }); const sigma_width = 125; @@ -38,7 +38,7 @@ const x_ticks = [ const xaxis_path = axis_path( ylabel_offset, height.value - 100, - 4 * sigma_width + ylabel_offset, + four_sigma_width + ylabel_offset, x_ticks, true, true @@ -75,50 +75,17 @@ const xaxis_path = axis_path( stroke-dasharray="8" /> -defineProps<{ - np_name: string; +import { useWorkspaceStore } from 'src/stores/workspace'; +import { computed, ref, watch } from 'vue'; + +const props = defineProps<{ + id: number; + np_index: number; y: number; x_offset: number; - bestfit: number; sigma_width: number; - uncertainty: number; isnothighlighted: boolean; }>(); + +const workspace_store = useWorkspaceStore(props.id)(); + +const bestfit = computed(() => { + return workspace_store.nps.bestfit[props.np_index]; +}); +const uncertainty = computed(() => { + return workspace_store.nps.uncertainty[props.np_index]; +}); + +// need to watch the bestfit props in case we want to reset the values +watch(bestfit, (newValue) => { + x_position.value = props.x_offset + newValue * props.sigma_width; +}); + +const np_name = workspace_store.nps.labels[props.np_index]; +const is_dragging = ref(false); +const x_position = ref(props.x_offset + bestfit.value * props.sigma_width); +const start_x = ref(0); + +function startDrag(event: MouseEvent): void { + is_dragging.value = true; + start_x.value = event.clientX; +} + +function drag(event: MouseEvent): void { + if (!is_dragging.value) return; + const delta = event.clientX - start_x.value; + const new_position = x_position.value + delta; + x_position.value = Math.max( + props.x_offset - 2 * props.sigma_width, + Math.min(props.x_offset + 2 * props.sigma_width, new_position) + ); + workspace_store.nps.bestfit[props.np_index] = + (x_position.value - props.x_offset) / props.sigma_width; + start_x.value = event.clientX; +} + +function endDrag(): void { + is_dragging.value = false; +} diff --git a/src/components/charts/StackedChart.vue b/src/components/charts/StackedChart.vue index e837d17..76f70a5 100644 --- a/src/components/charts/StackedChart.vue +++ b/src/components/charts/StackedChart.vue @@ -13,30 +13,40 @@ const { highlight, unhighlight, ishighlighted } = useHighlighted(); const props = defineProps<{ id: number; channel_index: number; + postfit: boolean; }>(); const workspace_store = useWorkspaceStore(props.id)(); -const channel = workspace_store.workspace.channels[props.channel_index]; -const channel_name = workspace_store.channel_names[props.channel_index]; -const channel_stacked_data = - workspace_store.stacked_data_per_bin[props.channel_index]; +const name = 'stackedchart' + (props.postfit ? 'postfit' : 'prefit'); -const number_of_bins = channel.samples[0].data.length; +const number_of_bins = + workspace_store.workspace.channels[props.channel_index].samples[0].data + .length; const bins = Array.from({ length: number_of_bins }, (e, i) => i); +const stacked_data = computed(() => { + return props.postfit + ? workspace_store.stacked_data_per_bin_postfit[props.channel_index] + : workspace_store.stacked_data_per_bin[props.channel_index]; +}); + +const channel_name = computed(() => { + return workspace_store.channel_names[props.channel_index]; +}); + const maximum = computed(() => { let max = 0; - for (let i_bin = 0; i_bin < channel_stacked_data.content.length; i_bin++) { + for (let i_bin = 0; i_bin < stacked_data.value.content.length; i_bin++) { const high_value = - channel_stacked_data.content[i_bin][ + stacked_data.value.content[i_bin][ workspace_store.process_names.length - 1 ].high; if (max < high_value) { max = high_value; } - const data_value = channel_stacked_data.data[i_bin]; + const data_value = stacked_data.value.data[i_bin]; if (max < data_value) { max = data_value; } @@ -44,7 +54,9 @@ const maximum = computed(() => { return max; }); -const yscale = linear_scale(0, maximum.value, 0, 300); +const yscale = computed(() => { + return linear_scale(0, maximum.value, 0, 300); +}); const bin_width = computed(() => { const max_width = 1000; @@ -100,18 +112,16 @@ const y_ticks = computed(() => { const y_tick_positions = computed(() => { const max = maximum_normalised.value; - let tick_positions = []; - for (const tick of y_ticks.value) { - tick_positions.push(-(300 / max) * tick); - } - return tick_positions; + return y_ticks.value.map((tick) => -(300 / max) * tick); }); -const yaxis_path = axis_path(100, 350, 40, y_tick_positions.value, false, true); +const yaxis_path = computed(() => { + return axis_path(100, 350, 40, y_tick_positions.value, false, true); +});