Files
dougal-software/lib/www/client/source/src/components/groups/group-repeatability-summary.vue
2025-08-22 16:01:20 +02:00

119 lines
4.2 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<v-card class="ma-1">
<v-card-title>Group Repeatability Summary</v-card-title>
<v-card-text>
<p>Error ellipse area for each baseline-monitor pair. Lower values indicate better repeatability. Colors range from green (best) to red (worst).</p>
<v-simple-table dense>
<thead>
<tr>
<th>Baseline \ Monitor</th>
<th v-for="project in projects" :key="project.pid">{{ project.pid }}</th>
</tr>
</thead>
<tbody>
<tr v-for="(baselineProject, rowIndex) in projects" :key="baselineProject.pid">
<td>{{ baselineProject.pid }}</td>
<td v-for="(monitorProject, colIndex) in projects" :key="monitorProject.pid">
<v-tooltip v-if="colIndex > rowIndex" top>
<template v-slot:activator="{ on, attrs }">
<div
:style="{ backgroundColor: getEllipseAreaColor(baselineProject.pid, monitorProject.pid), color: 'white', textAlign: 'center', padding: '4px' }"
v-bind="attrs"
v-on="on"
@click="emitInput(baselineProject, monitorProject)"
>
{{ formatEllipseArea(baselineProject.pid, monitorProject.pid) }}
</div>
</template>
<span v-if="getComp(baselineProject.pid, monitorProject.pid)">
<div>σ_i: {{ getComp(baselineProject.pid, monitorProject.pid).meta['σ'][0].toFixed(2) }} m</div>
<div>σ_j: {{ getComp(baselineProject.pid, monitorProject.pid).meta['σ'][1].toFixed(2) }} m</div>
<div>Anisotropy: {{ getComp(baselineProject.pid, monitorProject.pid).meta.anisotropy.toFixed(0) }}</div>
<div>Ellipse Area: {{ getEllipseArea(baselineProject.pid, monitorProject.pid).toFixed(2) }} </div>
<div>Primary Direction: {{ formatPrimaryDirection(getComp(baselineProject.pid, monitorProject.pid)) }}°</div>
</span>
</v-tooltip>
</td>
</tr>
</tbody>
</v-simple-table>
</v-card-text>
</v-card>
</template>
<script>
export default {
name: 'DougalGroupRepeatabilitySummary',
props: {
comparisons: {
type: Array,
required: true
},
projects: {
type: Array,
required: true
}
},
data () {
return {
};
},
computed: {
compMap () {
return new Map(this.comparisons.map(c => [`${c.baseline_pid}-${c.monitor_pid}`, c]));
},
minEllipseArea () {
if (!this.comparisons.length) return 0;
return Math.min(...this.comparisons.map(c => {
const a = Math.sqrt(c.meta.eigenvalues[0]);
const b = Math.sqrt(c.meta.eigenvalues[1]);
return Math.PI * a * b;
}));
},
maxEllipseArea () {
if (!this.comparisons.length) return 0;
return Math.max(...this.comparisons.map(c => {
const a = Math.sqrt(c.meta.eigenvalues[0]);
const b = Math.sqrt(c.meta.eigenvalues[1]);
return Math.PI * a * b;
}));
}
},
methods: {
getComp (basePid, monPid) {
return this.compMap.get(`${basePid}-${monPid}`);
},
getEllipseArea (basePid, monPid) {
const comp = this.getComp(basePid, monPid);
if (!comp) return null;
const a = Math.sqrt(comp.meta.eigenvalues[0]);
const b = Math.sqrt(comp.meta.eigenvalues[1]);
return Math.PI * a * b;
},
formatEllipseArea (basePid, monPid) {
const val = this.getEllipseArea(basePid, monPid);
return val !== null ? val.toFixed(1) : '';
},
getEllipseAreaColor (basePid, monPid) {
const val = this.getEllipseArea(basePid, monPid);
if (val === null) return '';
const ratio = (val - this.minEllipseArea) / (this.maxEllipseArea - this.minEllipseArea);
const hue = (1 - ratio) * 120;
return `hsl(${hue}, 70%, 70%)`;
},
formatPrimaryDirection (comp) {
if (!comp) return '';
return (comp.meta.primaryDirection * 180 / Math.PI).toFixed(1);
},
emitInput (baselineProject, monitorProject) {
if (this.getComp(baselineProject.pid, monitorProject.pid)) {
this.$emit('input', baselineProject, monitorProject);
}
}
}
}
</script>