add initial controls bar

This commit is contained in:
2025-01-01 22:10:42 -05:00
parent 59431ebfff
commit 623c394446
4 changed files with 164 additions and 117 deletions

View File

@@ -63,7 +63,7 @@ const min_y_value = computed(() => {
const average_y_value = min_y.value + half_diff_y_value; const average_y_value = min_y.value + half_diff_y_value;
return average_y_value - half_diff_y_value * 1.05; return average_y_value - half_diff_y_value * 1.05;
} else { } else {
return -1.0; return -1.05;
} }
} else { } else {
if (max_y.value > min_y.value) { if (max_y.value > min_y.value) {
@@ -72,7 +72,7 @@ const min_y_value = computed(() => {
const average_y_value = Math.log(min_y.value) + half_diff_y_value; const average_y_value = Math.log(min_y.value) + half_diff_y_value;
return Math.exp(average_y_value - half_diff_y_value * 1.05); return Math.exp(average_y_value - half_diff_y_value * 1.05);
} else { } else {
return 0.0; return 0.01;
} }
} }
}); });
@@ -86,7 +86,7 @@ const max_y_value = computed(() => {
const average_y_value = min_y.value + half_diff_y_value; const average_y_value = min_y.value + half_diff_y_value;
return average_y_value + half_diff_y_value * 1.05; return average_y_value + half_diff_y_value * 1.05;
} else { } else {
return 1.0; return 1.05;
} }
} else { } else {
if (max_y.value > min_y.value) { if (max_y.value > min_y.value) {
@@ -95,7 +95,7 @@ const max_y_value = computed(() => {
const average_y_value = Math.log(min_y.value) + half_diff_y_value; const average_y_value = Math.log(min_y.value) + half_diff_y_value;
return Math.exp(average_y_value + half_diff_y_value * 1.05); return Math.exp(average_y_value + half_diff_y_value * 1.05);
} else { } else {
return 1.0; return 1.05;
} }
} }
}); });
@@ -127,7 +127,7 @@ const lines = computed(() => {
const diff_log10 = Math.log10(diff_y_val); const diff_log10 = Math.log10(diff_y_val);
const diff_log10_fraction = diff_log10 - Math.floor(diff_log10); const diff_log10_fraction = diff_log10 - Math.floor(diff_log10);
const ticker_location = ticker_locations.find( const ticker_location = ticker_locations.find(
(location) => location < diff_log10_fraction, (location) => location <= diff_log10_fraction,
)!; )!;
const grid_spread = Math.pow(10, Math.floor(diff_log10) + ticker_location); const grid_spread = Math.pow(10, Math.floor(diff_log10) + ticker_location);
const major_spread = grid_spread / 2; const major_spread = grid_spread / 2;

View File

@@ -1,27 +1,46 @@
<script setup lang="ts"> <script setup lang="ts">
import { computed, provide, ref } from 'vue'; import { computed, onUnmounted, onWatcherCleanup, provide, ref, useTemplateRef, watch } from 'vue';
import { useNow } from '@/composables/ticker'; import { useNow } from '@/composables/ticker';
import { GRAPH_DATA, type GraphData, GraphSide } 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<{
width: number;
height: number;
duration?: number; duration?: number;
utc?: boolean; utc?: boolean;
left_axis?: boolean; left_axis?: boolean;
right_axis?: boolean; right_axis?: boolean;
hide_time_labels?: boolean; hide_time_labels?: boolean;
hide_time_ticks?: boolean; hide_time_ticks?: boolean;
include_controls?: boolean;
legend?: GraphSide; legend?: GraphSide;
}>(); }>();
const width = computed(() => { const divRef = useTemplateRef<HTMLDivElement>("graph-div");
return props.width;
const width = ref(0);
const height = ref(0);
const controls_height = 32;
const resize_observer = new ResizeObserver((elements) => {
for (const element of elements) {
if (element.target == divRef.value) {
width.value = element.contentBoxSize[0].inlineSize;
height.value = element.contentBoxSize[0].blockSize - (props.include_controls ? controls_height : 0);
}
}
}); });
const height = computed(() => { watch([divRef], ([divRef]) => {
return props.height; if (divRef) {
resize_observer.observe(divRef);
onWatcherCleanup(() => {
resize_observer.unobserve(divRef);
});
}
});
onUnmounted(() => {
resize_observer.disconnect();
}); });
const now = useNow(33); const now = useNow(33);
@@ -107,8 +126,8 @@ provide<GraphData>(GRAPH_DATA, {
border_top: border_top, border_top: border_top,
min_x: min_x, min_x: min_x,
max_x: now, max_x: now,
width: () => width.value - border_left.value - border_right.value, width: () => Math.max(width.value - border_left.value - border_right.value, 0),
height: () => height.value - border_top.value - border_bottom.value, height: () => Math.max(height.value - border_top.value - border_bottom.value, 0),
x_map: x_map, x_map: x_map,
lines: telemetry_lines, lines: telemetry_lines,
max_update_rate: 1000 / 10, max_update_rate: 1000 / 10,
@@ -140,14 +159,21 @@ const lines = computed(() => {
</script> </script>
<template> <template>
<div ref="graph-div" class="full-size">
<div v-if="include_controls" class="controls-header" :style="`height: ${controls_height}px`">
<div class="grow"></div>
<div>
<span>Duration Dropdown</span>
</div>
</div>
<svg ref="svg_graph" class="graph" :width="width" :height="height"> <svg ref="svg_graph" class="graph" :width="width" :height="height">
<defs> <defs>
<clipPath id="content"> <clipPath id="content">
<rect <rect
:x="border_left" :x="border_left"
:y="border_top" :y="border_top"
:width="width - border_left - border_right" :width="Math.max(width - border_left - border_right, 0)"
:height="height - border_top - border_bottom" :height="Math.max(height - border_top - border_bottom, 0)"
></rect> ></rect>
</clipPath> </clipPath>
<clipPath id="y_ticker"> <clipPath id="y_ticker">
@@ -155,14 +181,14 @@ const lines = computed(() => {
:x="0" :x="0"
:y="border_top" :y="border_top"
:width="width" :width="width"
:height="height - border_top - border_bottom" :height="Math.max(height - border_top - border_bottom, 0)"
></rect> ></rect>
</clipPath> </clipPath>
<clipPath id="x_ticker"> <clipPath id="x_ticker">
<rect <rect
:x="border_left" :x="border_left"
:y="0" :y="0"
:width="width - border_left - border_right" :width="Math.max(width - border_left - border_right, 0)"
:height="height" :height="height"
></rect> ></rect>
</clipPath> </clipPath>
@@ -186,6 +212,7 @@ const lines = computed(() => {
</g> </g>
<slot></slot> <slot></slot>
</svg> </svg>
</div>
</template> </template>
<style scoped lang="scss"> <style scoped lang="scss">
@@ -201,4 +228,24 @@ const lines = computed(() => {
stroke: variables.$time-tick; stroke: variables.$time-tick;
fill: variables.$time-tick; fill: variables.$time-tick;
} }
div.full-size {
width: 100%;
height: 100%;
}
div.controls-header {
display: flex;
flex-flow: row nowrap;
justify-content: center;
align-items: center;
align-content: stretch;
gap: 1em 0;
margin: 0 1em 0 1em;
}
div.controls-header > div.grow {
flex-grow: 1;
}
</style> </style>

View File

@@ -24,3 +24,4 @@ export interface GraphData {
legend_y_stride: MaybeRefOrGetter<number>; legend_y_stride: MaybeRefOrGetter<number>;
legend_width: MaybeRefOrGetter<number>; legend_width: MaybeRefOrGetter<number>;
} }

View File

@@ -1,10 +1,10 @@
<script setup lang="ts"> <script setup lang="ts">
import { useWebsocket, WEBSOCKET_SYMBOL } from '@/composables/websocket'; import { useWebsocket, WEBSOCKET_SYMBOL } from '@/composables/websocket';
import { provide } from 'vue'; import { provide } from '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'; import { GraphSide } from '@/graph/graph';
import SvgGraph from '@/components/SvgGraph.vue';
const websocket = useWebsocket(); const websocket = useWebsocket();
provide(WEBSOCKET_SYMBOL, websocket); provide(WEBSOCKET_SYMBOL, websocket);
@@ -12,62 +12,61 @@ provide(WEBSOCKET_SYMBOL, websocket);
<template> <template>
<main> <main>
<Graph <div style="width: 100vw; height: 50vh">
:width="800" <SvgGraph
:height="400"
:right_axis="true"
:legend="GraphSide.Left" :legend="GraphSide.Left"
right_axis
include_controls
> >
<Axis> <Axis>
<Line data="simple_producer/time_offset"></Line> <Line data="simple_producer/time_offset"></Line>
<Line data="simple_producer/publish_offset"></Line> <Line data="simple_producer/publish_offset"></Line>
<Line data="simple_producer/await_offset"></Line> <Line data="simple_producer/await_offset"></Line>
</Axis> </Axis>
</Graph> </SvgGraph>
<Graph </div>
:width="800" <div style="width: 100vw; height: 50vh">
:height="400" <SvgGraph
:duration="60 * 1000 * 10" :duration="60 * 1000 * 10"
:right_axis="true"
:legend="GraphSide.Right" :legend="GraphSide.Right"
right_axis
> >
<Axis> <Axis>
<Line <Line
data="simple_producer/sin" data="simple_producer/sin"
:minimum_separation="2000" :minimum_separation="1000"
></Line> ></Line>
<Line <Line
data="simple_producer/cos4" data="simple_producer/cos4"
:minimum_separation="2000" :minimum_separation="1000"
></Line> ></Line>
<Line <Line
data="simple_producer/sin2" data="simple_producer/sin2"
:minimum_separation="2000" :minimum_separation="1000"
></Line> ></Line>
<Line <Line
data="simple_producer/cos" data="simple_producer/cos"
:minimum_separation="2000" :minimum_separation="1000"
></Line> ></Line>
<Line <Line
data="simple_producer/sin3" data="simple_producer/sin3"
:minimum_separation="2000" :minimum_separation="1000"
></Line> ></Line>
<Line <Line
data="simple_producer/cos2" data="simple_producer/cos2"
:minimum_separation="2000" :minimum_separation="1000"
></Line> ></Line>
<Line <Line
data="simple_producer/sin4" data="simple_producer/sin4"
:minimum_separation="2000" :minimum_separation="1000"
></Line> ></Line>
<Line <Line
data="simple_producer/cos3" data="simple_producer/cos3"
:minimum_separation="2000" :minimum_separation="1000"
></Line> ></Line>
</Axis> </Axis>
</Graph> </SvgGraph>
<Graph :width="800" :height="400" :duration="5 * 1000"> </Graph> </div>
<Graph :width="800" :height="400" :duration="2 * 1000"> </Graph>
</main> </main>
</template> </template>