Add raw and filtered projection graphs
This commit is contained in:
160
public/ProjectionGraphComponent.js
Normal file
160
public/ProjectionGraphComponent.js
Normal file
@@ -0,0 +1,160 @@
|
||||
export const ProjectionGraphComponent = {
|
||||
view: function (vnode) {
|
||||
const {
|
||||
data,
|
||||
title,
|
||||
width = 300,
|
||||
height = 150,
|
||||
color = "steelblue",
|
||||
} = vnode.attrs;
|
||||
|
||||
if (!data || data.length === 0) {
|
||||
return m(
|
||||
"div.projection-graph-container",
|
||||
{
|
||||
style: {
|
||||
width: `${width}px`,
|
||||
height: `${height}px`,
|
||||
border: "1px solid #ccc",
|
||||
display: "flex",
|
||||
"align-items": "center",
|
||||
"justify-content": "center",
|
||||
"background-color": "#f9f9f9",
|
||||
"margin-bottom": "10px",
|
||||
},
|
||||
},
|
||||
m("p.text-gray-500", title ? `${title}: No data` : "No data available")
|
||||
);
|
||||
}
|
||||
|
||||
const padding = { top: 20, right: 20, bottom: 30, left: 40 };
|
||||
const chartWidth = width - padding.left - padding.right;
|
||||
const chartHeight = height - padding.top - padding.bottom;
|
||||
|
||||
const maxVal = Math.max(...data, 0); // Ensure maxVal is at least 0
|
||||
const minVal = Math.min(...data, 0); // Ensure minVal is at most 0
|
||||
|
||||
// Adjust scale if all values are 0 or very close to 0
|
||||
let yMax = maxVal;
|
||||
let yMin = minVal;
|
||||
|
||||
if (maxVal === 0 && minVal === 0) {
|
||||
yMax = 1; // Avoid division by zero if all data points are 0
|
||||
yMin = -1;
|
||||
} else if (maxVal === minVal) {
|
||||
// All values are the same but not zero
|
||||
yMax = maxVal + Math.abs(maxVal * 0.1) + 1; // Add some padding
|
||||
yMin = minVal - Math.abs(minVal * 0.1) - 1;
|
||||
}
|
||||
|
||||
const xScale = (index) => (index / (data.length - 1)) * chartWidth;
|
||||
const yScale = (value) =>
|
||||
chartHeight - ((value - yMin) / (yMax - yMin)) * chartHeight;
|
||||
|
||||
const linePath = data
|
||||
.map((d, i) => `${i === 0 ? "M" : "L"}${xScale(i)},${yScale(d)}`)
|
||||
.join(" ");
|
||||
|
||||
// Generate Y-axis ticks (e.g., 3 ticks: min, mid, max)
|
||||
const yTicks = [];
|
||||
if (yMin !== yMax) {
|
||||
yTicks.push({ value: yMin, y: yScale(yMin) });
|
||||
yTicks.push({ value: (yMin + yMax) / 2, y: yScale((yMin + yMax) / 2) });
|
||||
yTicks.push({ value: yMax, y: yScale(yMax) });
|
||||
} else {
|
||||
// Handle case where all values are the same
|
||||
yTicks.push({ value: yMin - 1, y: yScale(yMin - 1) });
|
||||
yTicks.push({ value: yMin, y: yScale(yMin) });
|
||||
yTicks.push({ value: yMin + 1, y: yScale(yMin + 1) });
|
||||
}
|
||||
|
||||
return m(
|
||||
"div.projection-graph-container",
|
||||
{
|
||||
style: {
|
||||
"margin-bottom": "15px",
|
||||
padding: "10px",
|
||||
border: "1px solid #e2e8f0",
|
||||
"border-radius": "0.375rem",
|
||||
"background-color": "#fff",
|
||||
},
|
||||
},
|
||||
[
|
||||
m(
|
||||
"h4.text-md.font-semibold.text-gray-700.mb-2.text-center",
|
||||
title || "Projection Data"
|
||||
),
|
||||
m("svg", { width: width, height: height }, [
|
||||
m("g", { transform: `translate(${padding.left}, ${padding.top})` }, [
|
||||
// Y-axis
|
||||
m("line", { x1: 0, y1: 0, x2: 0, y2: chartHeight, stroke: "#ccc" }),
|
||||
yTicks.map((tick) =>
|
||||
m("g", { transform: `translate(0, ${tick.y})` }, [
|
||||
m("line", { x1: -5, y1: 0, x2: 0, y2: 0, stroke: "#ccc" }),
|
||||
m(
|
||||
"text",
|
||||
{
|
||||
x: -10,
|
||||
y: 0,
|
||||
"font-size": "10px",
|
||||
"text-anchor": "end",
|
||||
dy: ".32em",
|
||||
},
|
||||
parseFloat(tick.value).toFixed(1)
|
||||
),
|
||||
])
|
||||
),
|
||||
|
||||
// X-axis
|
||||
m("line", {
|
||||
x1: 0,
|
||||
y1: chartHeight,
|
||||
x2: chartWidth,
|
||||
y2: chartHeight,
|
||||
stroke: "#ccc",
|
||||
}),
|
||||
// X-axis labels (optional, e.g., start, mid, end)
|
||||
m(
|
||||
"text",
|
||||
{
|
||||
x: 0,
|
||||
y: chartHeight + 15,
|
||||
"font-size": "10px",
|
||||
"text-anchor": "start",
|
||||
},
|
||||
"0"
|
||||
),
|
||||
m(
|
||||
"text",
|
||||
{
|
||||
x: chartWidth / 2,
|
||||
y: chartHeight + 15,
|
||||
"font-size": "10px",
|
||||
"text-anchor": "middle",
|
||||
},
|
||||
`${Math.floor(data.length / 2)}`
|
||||
),
|
||||
m(
|
||||
"text",
|
||||
{
|
||||
x: chartWidth,
|
||||
y: chartHeight + 15,
|
||||
"font-size": "10px",
|
||||
"text-anchor": "end",
|
||||
},
|
||||
`${data.length - 1}`
|
||||
),
|
||||
|
||||
// Data line
|
||||
m("path", {
|
||||
d: linePath,
|
||||
stroke: color,
|
||||
"stroke-width": 2,
|
||||
fill: "none",
|
||||
}),
|
||||
]),
|
||||
]),
|
||||
]
|
||||
);
|
||||
},
|
||||
};
|
||||
Reference in New Issue
Block a user