Smooth sensor card more when "Show more detail" is disabled (#27891)

* Smooth sensor card more when "Show more detail" is disabled

* Set minimum sample points to 10
This commit is contained in:
Petar Petrov 2025-11-10 15:23:26 +02:00 committed by GitHub
parent 7179bb2d26
commit 1c05afebd7
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 61 additions and 30 deletions

View File

@ -6,7 +6,8 @@ export function downSampleLineData<
data: T[] | undefined,
maxDetails: number,
minX?: number,
maxX?: number
maxX?: number,
useMean = false
): T[] {
if (!data) {
return [];
@ -17,15 +18,13 @@ export function downSampleLineData<
const min = minX ?? getPointData(data[0]!)[0];
const max = maxX ?? getPointData(data[data.length - 1]!)[0];
const step = Math.ceil((max - min) / Math.floor(maxDetails));
const frames = new Map<
number,
{
min: { point: (typeof data)[number]; x: number; y: number };
max: { point: (typeof data)[number]; x: number; y: number };
}
>();
// Group points into frames
const frames = new Map<
number,
{ point: (typeof data)[number]; x: number; y: number }[]
>();
for (const point of data) {
const pointData = getPointData(point);
if (!Array.isArray(pointData)) continue;
@ -36,28 +35,53 @@ export function downSampleLineData<
const frameIndex = Math.floor((x - min) / step);
const frame = frames.get(frameIndex);
if (!frame) {
frames.set(frameIndex, { min: { point, x, y }, max: { point, x, y } });
frames.set(frameIndex, [{ point, x, y }]);
} else {
if (frame.min.y > y) {
frame.min = { point, x, y };
}
if (frame.max.y < y) {
frame.max = { point, x, y };
}
frame.push({ point, x, y });
}
}
// Convert frames back to points
const result: T[] = [];
for (const [_i, frame] of frames) {
// Use min/max points to preserve visual accuracy
// The order of the data must be preserved so max may be before min
if (frame.min.x > frame.max.x) {
result.push(frame.max.point);
if (useMean) {
// Use mean values for each frame
for (const [_i, framePoints] of frames) {
const sumY = framePoints.reduce((acc, p) => acc + p.y, 0);
const meanY = sumY / framePoints.length;
const sumX = framePoints.reduce((acc, p) => acc + p.x, 0);
const meanX = sumX / framePoints.length;
const firstPoint = framePoints[0].point;
const pointData = getPointData(firstPoint);
const meanPoint = (
Array.isArray(pointData) ? [meanX, meanY] : { value: [meanX, meanY] }
) as T;
result.push(meanPoint);
}
result.push(frame.min.point);
if (frame.min.x < frame.max.x) {
result.push(frame.max.point);
} else {
// Use min/max values for each frame
for (const [_i, framePoints] of frames) {
let minPoint = framePoints[0];
let maxPoint = framePoints[0];
for (const p of framePoints) {
if (p.y < minPoint.y) {
minPoint = p;
}
if (p.y > maxPoint.y) {
maxPoint = p;
}
}
// The order of the data must be preserved so max may be before min
if (minPoint.x > maxPoint.x) {
result.push(maxPoint.point);
}
result.push(minPoint.point);
if (minPoint.x < maxPoint.x) {
result.push(maxPoint.point);
}
}
}

View File

@ -50,7 +50,8 @@ export const coordinates = (
width: number,
height: number,
maxDetails: number,
limits?: { minX?: number; maxX?: number; minY?: number; maxY?: number }
limits?: { minX?: number; maxX?: number; minY?: number; maxY?: number },
useMean = false
) => {
history = history.filter((item) => !Number.isNaN(item[1]));
@ -58,7 +59,8 @@ export const coordinates = (
history,
maxDetails,
limits?.minX,
limits?.maxX
limits?.maxX,
useMean
);
return calcPoints(sampledData, width, height, limits);
};
@ -68,7 +70,8 @@ export const coordinatesMinimalResponseCompressedState = (
width: number,
height: number,
maxDetails: number,
limits?: { minX?: number; maxX?: number; minY?: number; maxY?: number }
limits?: { minX?: number; maxX?: number; minY?: number; maxY?: number },
useMean = false
) => {
if (!history?.length) {
return { points: [], yAxisOrigin: 0 };
@ -80,5 +83,5 @@ export const coordinatesMinimalResponseCompressedState = (
item.lu * 1000,
Number(item.s),
]);
return coordinates(mappedHistory, width, height, maxDetails, limits);
return coordinates(mappedHistory, width, height, maxDetails, limits, useMean);
};

View File

@ -155,16 +155,20 @@ export class HuiGraphHeaderFooter
}
const width = this.clientWidth || this.offsetWidth;
// sample to 1 point per hour or 1 point per 5 pixels
const maxDetails =
const maxDetails = Math.max(
10,
this._config.detail! > 1
? Math.max(width / 5, this._config.hours_to_show!)
: this._config.hours_to_show!;
: this._config.hours_to_show!
);
const useMean = this._config.detail !== 2;
const { points } = coordinatesMinimalResponseCompressedState(
combinedHistory[this._config.entity],
width,
width / 5,
maxDetails,
{ minY: this._config.limits?.min, maxY: this._config.limits?.max }
{ minY: this._config.limits?.min, maxY: this._config.limits?.max },
useMean
);
this._coordinates = points;
},