add graph legends

This commit is contained in:
2025-01-01 15:06:42 -05:00
parent 6a8e076ee7
commit 28c077b0b2
5 changed files with 81 additions and 4 deletions

View File

@@ -31,6 +31,12 @@ $major-tick: $gray-4;
$minor-tick: $gray-5; $minor-tick: $gray-5;
$text-font: Helvetica, sans-serif; $text-font: Helvetica, sans-serif;
$normal-text-size: 16px;
$small-text-size: 12px;
$monospace-text-font: Monaco, monospace, monospace;
$normal-monospace-text-size: 12px;
$small-monospace-text-size: 10px;
$colors: ( $colors: (
$red-1, $red-1,

View File

@@ -1,7 +1,7 @@
<script setup lang="ts"> <script setup lang="ts">
import { computed, provide, ref } from 'vue'; import { computed, provide, ref } from 'vue';
import { useNow } from '@/composables/ticker'; import { useNow } from '@/composables/ticker';
import { GRAPH_DATA, type GraphData } from '@/graph/graph'; import { GRAPH_DATA, type GraphData, GraphSide } from '@/graph/graph';
import TimeText from '@/components/TimeText.vue'; import TimeText from '@/components/TimeText.vue';
const props = defineProps<{ const props = defineProps<{
@@ -13,6 +13,7 @@ const props = defineProps<{
right_axis?: boolean; right_axis?: boolean;
hide_time_labels?: boolean; hide_time_labels?: boolean;
hide_time_ticks?: boolean; hide_time_ticks?: boolean;
legend?: GraphSide;
}>(); }>();
const width = computed(() => { const width = computed(() => {
@@ -62,8 +63,9 @@ const time_lines = [
time_lines.reverse(); time_lines.reverse();
const text_offset = computed(() => 5); const text_offset = computed(() => 5);
const border_left = computed(() => props.left_axis ? 96 : 0); const legend_width = 160;
const border_right = computed(() => props.right_axis ? 80 : 0); const border_left = computed(() => (props.left_axis ? 96 : 0) + (props.legend == GraphSide.Left ? legend_width : 0));
const border_right = computed(() => (props.right_axis ? 80 : 0) + (props.legend == GraphSide.Right ? legend_width : 0));
const border_top = computed(() => 6); const border_top = computed(() => 6);
const border_bottom = computed(() => props.hide_time_labels ? 6 : 24); const border_bottom = computed(() => props.hide_time_labels ? 6 : 24);
@@ -81,6 +83,13 @@ const x_map = (x: number) => {
const telemetry_lines = ref([]); const telemetry_lines = ref([]);
const legend_enabled = computed(() => props.legend === GraphSide.Left || props.legend === GraphSide.Right);
const legend_x = computed(() => (props.legend === GraphSide.Left) ? (8) : (width.value - legend_width + 8));
const legend_y = computed(() => border_top.value);
const legend_x_stride = computed(() => 0);
const legend_y_stride = computed(() => 16);
const legend_width_output = computed(() => legend_width - 8);
provide<GraphData>(GRAPH_DATA, { provide<GraphData>(GRAPH_DATA, {
border_top: border_top, border_top: border_top,
min_x: min_x, min_x: min_x,
@@ -90,6 +99,12 @@ provide<GraphData>(GRAPH_DATA, {
x_map: x_map, x_map: x_map,
lines: telemetry_lines, lines: telemetry_lines,
max_update_rate: 1000 / 10, max_update_rate: 1000 / 10,
legend_enabled: legend_enabled,
legend_x: legend_x,
legend_y: legend_y,
legend_x_stride: legend_x_stride,
legend_y_stride: legend_y_stride,
legend_width: legend_width_output,
}); });
const line_duration = computed(() => { const line_duration = computed(() => {

View File

@@ -31,6 +31,8 @@ const props = defineProps<{
const smoothing_distance_x = 5; const smoothing_distance_x = 5;
const maximum_minimum_separation_live = 100; // ms const maximum_minimum_separation_live = 100; // ms
const legend_line_length = 8;
const legend_text_offset = 4;
const text_offset = computed(() => 10); const text_offset = computed(() => 10);
const min_sep = computed(() => Math.min(props.minimum_separation || 0, maximum_minimum_separation_live)); const min_sep = computed(() => Math.min(props.minimum_separation || 0, maximum_minimum_separation_live));
@@ -247,6 +249,30 @@ const current_value = computed(() => {
} }
return undefined; return undefined;
}); });
const legend_x = computed(() => {
return toValue(graph_data.legend_x) + toValue(graph_data.legend_x_stride) * index.value;
});
const legend_y = computed(() => {
return toValue(graph_data.legend_y) + toValue(graph_data.legend_y_stride) * index.value;
});
const legend_text = computed(() => {
const max_chars = (toValue(graph_data.legend_width) - legend_line_length - legend_text_offset * 2) / 7;
const start_text = props.data;
if (start_text.length > max_chars) {
return start_text.substring(0, 3) + "..." + start_text.substring(start_text.length - max_chars + 6);
}
return start_text;
});
const legend_line = computed(() => {
let x = legend_x.value;
let y = legend_y.value;
return `${x},${y} ${x + legend_line_length},${y}`;
});
</script> </script>
<template> <template>
@@ -265,6 +291,17 @@ const current_value = computed(() => {
:value="current_value" :value="current_value"
> >
</ValueLabel> </ValueLabel>
<template v-if="toValue(graph_data.legend_enabled)">
<polyline
fill="none"
:points="legend_line"
></polyline>
<text
:x="legend_x + legend_line_length + legend_text_offset"
:y="legend_y">
{{ legend_text }}
</text>
</template>
</g> </g>
</template> </template>
@@ -275,4 +312,14 @@ polyline {
stroke-width: 1px; stroke-width: 1px;
stroke: var(--indexed-color); stroke: var(--indexed-color);
} }
text {
font-family: variables.$monospace-text-font;
text-anchor: start;
stroke: variables.$text-color;
fill: variables.$text-color;
dominant-baseline: middle;
font-size: variables.$small-monospace-text-size;
}
</style> </style>

View File

@@ -1,9 +1,9 @@
import type { MaybeRefOrGetter, Ref } from 'vue'; import type { MaybeRefOrGetter, Ref } from 'vue';
export enum GraphSide { export enum GraphSide {
Hidden,
Right, Right,
Left, Left,
Hidden,
} }
export const GRAPH_DATA = Symbol(); export const GRAPH_DATA = Symbol();
@@ -17,4 +17,10 @@ export interface GraphData {
x_map: (x: number) => number; x_map: (x: number) => number;
lines: Ref<symbol[]>; lines: Ref<symbol[]>;
max_update_rate: MaybeRefOrGetter<number>; max_update_rate: MaybeRefOrGetter<number>;
legend_enabled: MaybeRefOrGetter<boolean>;
legend_x: MaybeRefOrGetter<number>;
legend_x_stride: MaybeRefOrGetter<number>;
legend_y: MaybeRefOrGetter<number>;
legend_y_stride: MaybeRefOrGetter<number>;
legend_width: MaybeRefOrGetter<number>;
} }

View File

@@ -4,6 +4,7 @@ import { provide } from 'vue';
import Graph from '@/components/SvgGraph.vue'; import Graph from '@/components/SvgGraph.vue';
import Axis from '@/components/GraphAxis.vue'; import Axis from '@/components/GraphAxis.vue';
import Line from '@/components/TelemetryLine.vue'; import Line from '@/components/TelemetryLine.vue';
import { GraphSide } from '@/graph/graph';
const websocket = useWebsocket(); const websocket = useWebsocket();
provide(WEBSOCKET_SYMBOL, websocket); provide(WEBSOCKET_SYMBOL, websocket);
@@ -15,6 +16,7 @@ provide(WEBSOCKET_SYMBOL, websocket);
:width="800" :width="800"
:height="400" :height="400"
:right_axis="true" :right_axis="true"
:legend="GraphSide.Left"
> >
<Axis> <Axis>
<Line data="simple_producer/time_offset"></Line> <Line data="simple_producer/time_offset"></Line>
@@ -27,6 +29,7 @@ provide(WEBSOCKET_SYMBOL, websocket);
:height="400" :height="400"
:duration="60 * 1000 * 10" :duration="60 * 1000 * 10"
:right_axis="true" :right_axis="true"
:legend="GraphSide.Right"
> >
<Axis> <Axis>
<Line <Line