64 lines
2.3 KiB
Vue
64 lines
2.3 KiB
Vue
<template>
|
|
<div class="space-y-2">
|
|
<div class="flex items-center justify-between">
|
|
<h3 class="text-sm font-semibold text-gray-300">相似度矩陣 V4</h3>
|
|
<span class="text-xs text-gray-500">{{ traces.length }} traces</span>
|
|
</div>
|
|
<div class="bg-gray-900 rounded p-4 overflow-x-auto" v-if="matrix.length">
|
|
<svg :width="cellS * matrix.length + 60" :height="cellS * matrix.length + 40" class="block">
|
|
<!-- labels -->
|
|
<text v-for="(_, i) in matrix" :key="'l'+i"
|
|
:x="cellS * i + cellS / 2 + 50" :y="14" fill="#9ca3af" font-size="7" text-anchor="end"
|
|
transform="rotate(-60, 10, 10)">{{ traces[i]?.trace_id }}</text>
|
|
<text v-for="(_, i) in matrix" :key="'r'+i"
|
|
:x="44" :y="cellS * i + cellS / 2 + 24" fill="#9ca3af" font-size="7">{{ traces[i]?.trace_id }}</text>
|
|
<!-- cells -->
|
|
<g v-for="(row, i) in matrix" :key="i">
|
|
<rect v-for="(v, j) in row" :key="j"
|
|
:x="cellS * j + 50" :y="cellS * i + 20"
|
|
:width="cellS" :height="cellS"
|
|
:fill="color(v)" stroke="#374151" stroke-width="0.5" />
|
|
</g>
|
|
</svg>
|
|
</div>
|
|
<div v-else class="text-center text-gray-500 text-sm py-8">no data</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import { computed } from 'vue'
|
|
|
|
const props = defineProps<{ traces: any[] }>()
|
|
const cellS = 14
|
|
const maxN = 40
|
|
|
|
const matrix = computed(() => {
|
|
const t = props.traces.slice(0, maxN)
|
|
if (t.length < 2) return []
|
|
const durs = t.map(x => x.duration_sec || 0)
|
|
const cnt = t.map(x => x.face_count || 0)
|
|
const maxDur = Math.max(...durs, 1)
|
|
const maxCnt = Math.max(...cnt, 1)
|
|
const m: number[][] = []
|
|
for (let i = 0; i < t.length; i++) {
|
|
const row: number[] = []
|
|
for (let j = 0; j < t.length; j++) {
|
|
if (i === j) { row.push(1); continue }
|
|
// Simple similarity: duration + face_count proximity
|
|
const durSim = 1 - Math.abs(durs[i] - durs[j]) / maxDur
|
|
const cntSim = 1 - Math.abs(cnt[i] - cnt[j]) / maxCnt
|
|
row.push((durSim + cntSim) / 2)
|
|
}
|
|
m.push(row)
|
|
}
|
|
return m
|
|
})
|
|
|
|
function color(v: number): string {
|
|
if (v > 0.85) return 'rgba(68, 255, 68, 0.8)' // bright green = similar
|
|
if (v > 0.7) return 'rgba(68, 200, 68, 0.6)'
|
|
if (v > 0.5) return 'rgba(100, 100, 100, 0.4)'
|
|
return 'rgba(40, 40, 50, 0.3)' // dark = dissimilar
|
|
}
|
|
</script>
|