change format settings

This commit is contained in:
2024-12-04 20:00:18 -08:00
parent 07b585f956
commit 4c2b0f454b
14 changed files with 691 additions and 665 deletions

View File

@@ -1,303 +1,321 @@
<script setup lang="ts">
import { computed, inject, provide, ref, toValue, watch } from 'vue'
import { AXIS_DATA, type AxisData, AxisSide, AxisType } from '@/graph/axis'
import { useNow } from '@/composables/ticker'
import { GRAPH_DATA, type GraphData } from '@/graph/graph'
import { computed, inject, provide, ref, toValue, watch } from 'vue';
import { AXIS_DATA, type AxisData, AxisSide, AxisType } from '@/graph/axis';
import { useNow } from '@/composables/ticker';
import { GRAPH_DATA, type GraphData } from '@/graph/graph';
const props = defineProps<{
y_limits?: [number, number]
side?: AxisSide
type?: AxisType
}>()
y_limits?: [number, number];
side?: AxisSide;
type?: AxisType;
}>();
const minor_tick_length = computed(() => 4)
const major_tick_length = computed(() => 8)
const text_offset = computed(() => 5)
const side = computed(() => props.side || AxisSide.Right)
const type = computed(() => props.type || AxisType.Linear)
const minor_tick_length = computed(() => 4);
const major_tick_length = computed(() => 8);
const text_offset = computed(() => 5);
const side = computed(() => props.side || AxisSide.Right);
const type = computed(() => props.type || AxisType.Linear);
const ticker_locations = [Math.log10(1), Math.log10(2), Math.log10(5)]
ticker_locations.reverse()
const ticker_locations = [Math.log10(1), Math.log10(2), Math.log10(5)];
ticker_locations.reverse();
const ticker = useNow(33)
const ticker = useNow(33);
const min_y = ref(Infinity)
const max_y = ref(-Infinity)
const raw_min_y = ref(Infinity)
const raw_max_y = ref(-Infinity)
const min_y = ref(Infinity);
const max_y = ref(-Infinity);
const raw_min_y = ref(Infinity);
const raw_max_y = ref(-Infinity);
const axis_update_watch = ref(0)
const axis_update_watch = ref(0);
watch([ticker], () => {
axis_update_watch.value++
min_y.value = raw_min_y.value
max_y.value = raw_max_y.value
raw_min_y.value = Infinity
raw_max_y.value = -Infinity
})
axis_update_watch.value++;
min_y.value = raw_min_y.value;
max_y.value = raw_max_y.value;
raw_min_y.value = Infinity;
raw_max_y.value = -Infinity;
});
function update_min_y(y: number) {
if (y < min_y.value) {
min_y.value = y
}
if (y < raw_min_y.value) {
raw_min_y.value = y
}
if (y < min_y.value) {
min_y.value = y;
}
if (y < raw_min_y.value) {
raw_min_y.value = y;
}
}
function update_max_y(y: number) {
if (y > max_y.value) {
max_y.value = y
}
if (y > raw_max_y.value) {
raw_max_y.value = y
}
if (y > max_y.value) {
max_y.value = y;
}
if (y > raw_max_y.value) {
raw_max_y.value = y;
}
}
// const half_diff_y_value = computed(() => (max_y.value - min_y.value) / 2.0);
// const average_y_value = computed(() => min_y.value + half_diff_y_value.value);
const min_y_value = computed(() => {
if (props.y_limits) {
return props.y_limits[0]
}
if (type.value == AxisType.Linear) {
if (max_y.value > min_y.value) {
const half_diff_y_value = (max_y.value - min_y.value) / 2.0
const average_y_value = min_y.value + half_diff_y_value
return average_y_value - half_diff_y_value * 1.05
} else {
return -1.0
if (props.y_limits) {
return props.y_limits[0];
}
} else {
if (max_y.value > min_y.value) {
const half_diff_y_value =
(Math.log(max_y.value) - Math.log(min_y.value)) / 2.0
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)
if (type.value == AxisType.Linear) {
if (max_y.value > min_y.value) {
const half_diff_y_value = (max_y.value - min_y.value) / 2.0;
const average_y_value = min_y.value + half_diff_y_value;
return average_y_value - half_diff_y_value * 1.05;
} else {
return -1.0;
}
} else {
return 0.0
if (max_y.value > min_y.value) {
const half_diff_y_value =
(Math.log(max_y.value) - Math.log(min_y.value)) / 2.0;
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);
} else {
return 0.0;
}
}
}
})
});
const max_y_value = computed(() => {
if (props.y_limits) {
return props.y_limits[1]
}
if (type.value == AxisType.Linear) {
if (max_y.value > min_y.value) {
const half_diff_y_value = (max_y.value - min_y.value) / 2.0
const average_y_value = min_y.value + half_diff_y_value
return average_y_value + half_diff_y_value * 1.05
} else {
return 1.0
if (props.y_limits) {
return props.y_limits[1];
}
} else {
if (max_y.value > min_y.value) {
const half_diff_y_value =
(Math.log(max_y.value) - Math.log(min_y.value)) / 2.0
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)
if (type.value == AxisType.Linear) {
if (max_y.value > min_y.value) {
const half_diff_y_value = (max_y.value - min_y.value) / 2.0;
const average_y_value = min_y.value + half_diff_y_value;
return average_y_value + half_diff_y_value * 1.05;
} else {
return 1.0;
}
} else {
return 1.0
if (max_y.value > min_y.value) {
const half_diff_y_value =
(Math.log(max_y.value) - Math.log(min_y.value)) / 2.0;
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);
} else {
return 1.0;
}
}
}
})
});
const graph_data = inject<GraphData>(GRAPH_DATA)!
const graph_data = inject<GraphData>(GRAPH_DATA)!;
const y_map = (y: number) => {
const height = toValue(graph_data.height)
const border_top_bottom = toValue(graph_data.border_top_bottom)
let max_value = toValue(max_y_value)
let min_value = toValue(min_y_value)
let y_value = y
if (type.value == AxisType.Logarithmic) {
max_value = Math.log(max_value)
min_value = Math.log(min_value)
y_value = Math.log(y_value)
}
const diff_y = max_value - min_value
return height * (1 - (y_value - min_value) / diff_y) + border_top_bottom
}
const height = toValue(graph_data.height);
const border_top_bottom = toValue(graph_data.border_top_bottom);
let max_value = toValue(max_y_value);
let min_value = toValue(min_y_value);
let y_value = y;
if (type.value == AxisType.Logarithmic) {
max_value = Math.log(max_value);
min_value = Math.log(min_value);
y_value = Math.log(y_value);
}
const diff_y = max_value - min_value;
return height * (1 - (y_value - min_value) / diff_y) + border_top_bottom;
};
provide<AxisData>(AXIS_DATA, {
axis_update_watch: axis_update_watch,
min_y_callback: update_min_y,
max_y_callback: update_max_y,
y_map: y_map,
})
axis_update_watch: axis_update_watch,
min_y_callback: update_min_y,
max_y_callback: update_max_y,
y_map: y_map,
});
const lines = computed(() => {
const diff_y_val = (max_y_value.value - min_y_value.value) / 2
const diff_log10 = Math.log10(diff_y_val)
const diff_log10_fraction = diff_log10 - Math.floor(diff_log10)
const ticker_location = ticker_locations.find(
location => location < diff_log10_fraction,
)!
const grid_spread = Math.pow(10, Math.floor(diff_log10) + ticker_location)
const major_spread = grid_spread / 2
const minor_spread = major_spread / 2
const minor_ticks = []
const major_ticks = []
const grid_lines = []
for (
let i = Math.floor(min_y_value.value / grid_spread);
i <= Math.ceil(max_y_value.value / grid_spread);
i++
) {
const y = i * grid_spread
grid_lines.push(y)
}
for (
let i = Math.floor(min_y_value.value / major_spread);
i <= Math.ceil(max_y_value.value / major_spread);
i++
) {
const y = i * major_spread
if (grid_lines.indexOf(y) < 0) {
major_ticks.push(y)
const diff_y_val = (max_y_value.value - min_y_value.value) / 2;
const diff_log10 = Math.log10(diff_y_val);
const diff_log10_fraction = diff_log10 - Math.floor(diff_log10);
const ticker_location = ticker_locations.find(
(location) => location < diff_log10_fraction,
)!;
const grid_spread = Math.pow(10, Math.floor(diff_log10) + ticker_location);
const major_spread = grid_spread / 2;
const minor_spread = major_spread / 2;
const minor_ticks = [];
const major_ticks = [];
const grid_lines = [];
for (
let i = Math.floor(min_y_value.value / grid_spread);
i <= Math.ceil(max_y_value.value / grid_spread);
i++
) {
const y = i * grid_spread;
grid_lines.push(y);
}
}
for (
let i = Math.floor(min_y_value.value / minor_spread);
i <= Math.ceil(max_y_value.value / minor_spread);
i++
) {
const y = i * minor_spread
if (grid_lines.indexOf(y) < 0 && major_ticks.indexOf(y) < 0) {
minor_ticks.push(y)
for (
let i = Math.floor(min_y_value.value / major_spread);
i <= Math.ceil(max_y_value.value / major_spread);
i++
) {
const y = i * major_spread;
if (grid_lines.indexOf(y) < 0) {
major_ticks.push(y);
}
}
}
return [minor_ticks, major_ticks, grid_lines]
})
for (
let i = Math.floor(min_y_value.value / minor_spread);
i <= Math.ceil(max_y_value.value / minor_spread);
i++
) {
const y = i * minor_spread;
if (grid_lines.indexOf(y) < 0 && major_ticks.indexOf(y) < 0) {
minor_ticks.push(y);
}
}
return [minor_ticks, major_ticks, grid_lines];
});
</script>
<template>
<template v-if="side == AxisSide.Right">
<g class="minor_tick" clip-path="url(#y_ticker)">
<template v-for="tick of lines[0]" :key="tick">
<polyline
:points="`${graph_data.x_map(toValue(graph_data.max_x)) - minor_tick_length},${y_map(tick)} ${graph_data.x_map(toValue(graph_data.max_x))},${y_map(tick)}`"
></polyline>
<text
class="right_edge middle_text"
:x="graph_data.x_map(toValue(graph_data.max_x)) + text_offset"
:y="y_map(tick)"
>{{ tick.toFixed(2) }}</text
>
</template>
<template v-if="side == AxisSide.Right">
<g class="minor_tick" clip-path="url(#y_ticker)">
<template v-for="tick of lines[0]" :key="tick">
<polyline
:points="`${graph_data.x_map(toValue(graph_data.max_x)) - minor_tick_length},${y_map(tick)} ${graph_data.x_map(toValue(graph_data.max_x))},${y_map(tick)}`"
></polyline>
<text
class="right_edge middle_text"
:x="
graph_data.x_map(toValue(graph_data.max_x)) +
text_offset
"
:y="y_map(tick)"
>{{ tick.toFixed(2) }}</text
>
</template>
</g>
<g class="major_tick" clip-path="url(#y_ticker)">
<template v-for="tick of lines[1]" :key="tick">
<polyline
:points="`${graph_data.x_map(toValue(graph_data.max_x)) - major_tick_length},${y_map(tick)} ${graph_data.x_map(toValue(graph_data.max_x))},${y_map(tick)}`"
></polyline>
<text
class="right_edge middle_text"
:x="
graph_data.x_map(toValue(graph_data.max_x)) +
text_offset
"
:y="y_map(tick)"
>{{ tick.toFixed(1) }}</text
>
</template>
</g>
<g class="grid_tick" clip-path="url(#y_ticker)">
<template v-for="tick of lines[2]" :key="tick">
<polyline
:points="`${graph_data.x_map(toValue(graph_data.min_x))},${y_map(tick)} ${graph_data.x_map(toValue(graph_data.max_x))},${y_map(tick)}`"
></polyline>
<text
class="right_edge middle_text"
:x="
graph_data.x_map(toValue(graph_data.max_x)) +
text_offset
"
:y="y_map(tick)"
>{{ tick.toFixed(0) }}</text
>
</template>
</g>
</template>
<template v-if="side == AxisSide.Left">
<g class="minor_tick" clip-path="url(#y_ticker)">
<template v-for="tick of lines[0]" :key="tick">
<polyline
:points="`${graph_data.x_map(toValue(graph_data.min_x)) + minor_tick_length},${y_map(tick)} ${graph_data.x_map(toValue(graph_data.min_x))},${y_map(tick)}`"
></polyline>
<text
class="left_edge middle_text"
:x="
graph_data.x_map(toValue(graph_data.min_x)) -
text_offset
"
:y="y_map(tick)"
>{{ tick.toFixed(2) }}</text
>
</template>
</g>
<g class="major_tick" clip-path="url(#y_ticker)">
<template v-for="tick of lines[1]" :key="tick">
<polyline
:points="`${graph_data.x_map(toValue(graph_data.max_x)) - major_tick_length},${y_map(tick)} ${graph_data.x_map(toValue(graph_data.max_x))},${y_map(tick)}`"
></polyline>
<text
class="left_edge middle_text"
:x="
graph_data.x_map(toValue(graph_data.min_x)) -
text_offset
"
:y="y_map(tick)"
>{{ tick.toFixed(1) }}</text
>
</template>
</g>
<g class="grid_tick" clip-path="url(#y_ticker)">
<template v-for="tick of lines[2]" :key="tick">
<polyline
:points="`${graph_data.x_map(toValue(graph_data.min_x))},${y_map(tick)} ${graph_data.x_map(toValue(graph_data.max_x))},${y_map(tick)}`"
></polyline>
<text
class="left_edge middle_text"
:x="
graph_data.x_map(toValue(graph_data.min_x)) -
text_offset
"
:y="y_map(tick)"
>{{ tick.toFixed(0) }}</text
>
</template>
</g>
</template>
<g>
<slot></slot>
</g>
<g class="major_tick" clip-path="url(#y_ticker)">
<template v-for="tick of lines[1]" :key="tick">
<polyline
:points="`${graph_data.x_map(toValue(graph_data.max_x)) - major_tick_length},${y_map(tick)} ${graph_data.x_map(toValue(graph_data.max_x))},${y_map(tick)}`"
></polyline>
<text
class="right_edge middle_text"
:x="graph_data.x_map(toValue(graph_data.max_x)) + text_offset"
:y="y_map(tick)"
>{{ tick.toFixed(1) }}</text
>
</template>
</g>
<g class="grid_tick" clip-path="url(#y_ticker)">
<template v-for="tick of lines[2]" :key="tick">
<polyline
:points="`${graph_data.x_map(toValue(graph_data.min_x))},${y_map(tick)} ${graph_data.x_map(toValue(graph_data.max_x))},${y_map(tick)}`"
></polyline>
<text
class="right_edge middle_text"
:x="graph_data.x_map(toValue(graph_data.max_x)) + text_offset"
:y="y_map(tick)"
>{{ tick.toFixed(0) }}</text
>
</template>
</g>
</template>
<template v-if="side == AxisSide.Left">
<g class="minor_tick" clip-path="url(#y_ticker)">
<template v-for="tick of lines[0]" :key="tick">
<polyline
:points="`${graph_data.x_map(toValue(graph_data.min_x)) + minor_tick_length},${y_map(tick)} ${graph_data.x_map(toValue(graph_data.min_x))},${y_map(tick)}`"
></polyline>
<text
class="left_edge middle_text"
:x="graph_data.x_map(toValue(graph_data.min_x)) - text_offset"
:y="y_map(tick)"
>{{ tick.toFixed(2) }}</text
>
</template>
</g>
<g class="major_tick" clip-path="url(#y_ticker)">
<template v-for="tick of lines[1]" :key="tick">
<polyline
:points="`${graph_data.x_map(toValue(graph_data.max_x)) - major_tick_length},${y_map(tick)} ${graph_data.x_map(toValue(graph_data.max_x))},${y_map(tick)}`"
></polyline>
<text
class="left_edge middle_text"
:x="graph_data.x_map(toValue(graph_data.min_x)) - text_offset"
:y="y_map(tick)"
>{{ tick.toFixed(1) }}</text
>
</template>
</g>
<g class="grid_tick" clip-path="url(#y_ticker)">
<template v-for="tick of lines[2]" :key="tick">
<polyline
:points="`${graph_data.x_map(toValue(graph_data.min_x))},${y_map(tick)} ${graph_data.x_map(toValue(graph_data.max_x))},${y_map(tick)}`"
></polyline>
<text
class="left_edge middle_text"
:x="graph_data.x_map(toValue(graph_data.min_x)) - text_offset"
:y="y_map(tick)"
>{{ tick.toFixed(0) }}</text
>
</template>
</g>
</template>
<g clip-path="url(#content)">
<slot></slot>
</g>
</template>
<style scoped lang="scss">
.x_axis {
fill: #ffffff;
stroke: #ffffff;
color: #ffffff;
fill: #ffffff;
stroke: #ffffff;
color: #ffffff;
}
.right_edge {
text-anchor: start;
text-anchor: start;
}
.left_edge {
text-anchor: end;
text-anchor: end;
}
.middle_text {
alignment-baseline: middle;
font-family: Helvetica, sans-serif;
alignment-baseline: middle;
font-family: Helvetica, sans-serif;
}
.minor_tick {
stroke: #666666;
stroke-width: 1px;
fill: #666666;
color: #666666;
stroke: #666666;
stroke-width: 1px;
fill: #666666;
color: #666666;
}
.major_tick {
stroke: #aaaaaa;
stroke-width: 1px;
fill: #aaaaaa;
color: #aaaaaa;
stroke: #aaaaaa;
stroke-width: 1px;
fill: #aaaaaa;
color: #aaaaaa;
}
.grid_tick {
stroke: #ffffff;
stroke-width: 1px;
fill: #ffffff;
color: #ffffff;
stroke: #ffffff;
stroke-width: 1px;
fill: #ffffff;
color: #ffffff;
}
</style>