前端进度条性能优化、仿真结束后后处理优化;后端C代码生成流程优化:先识别来源,再按照已知未知量需求排序,最后局部求解
This commit is contained in:
1 parent
0dcb465d84
commit
91bd9fb252
77 files changed
+11223
-551
No files matched your search
@@ -0,0 +1,139 @@
|
||||
export type ChartSample = { x: number; y: number; dataIndex: number; breakBefore: boolean };
|
||||
type Bounds = { xMin: number; xMax: number; yMin: number; yMax: number; sorted: boolean };
|
||||
const seriesCache = new WeakMap<number[], WeakMap<number[], ChartSample[]>>();
|
||||
const boundsCache = new WeakMap<Array<{ x: number; y: number }>, Bounds>();
|
||||
const cursorCache = new WeakMap<number[], ChartSample[]>();
|
||||
const selectionCache = new WeakMap<ChartSample[], Map<string, ChartSample[]>>();
|
||||
|
||||
/** Cached only by immutable source arrays; collected together with their result snapshot. */
|
||||
export function preparedChartSamples(time: number[], values: number[]) {
|
||||
let byTime = seriesCache.get(values);
|
||||
if (!byTime) { byTime = new WeakMap(); seriesCache.set(values, byTime); }
|
||||
let samples = byTime.get(time);
|
||||
if (samples) return samples;
|
||||
samples = [];
|
||||
let breakBefore = true;
|
||||
for (let i = 0; i < time.length; i++) {
|
||||
const x = Number(time[i]), y = Number(values[i]);
|
||||
if (!Number.isFinite(x)) { breakBefore = true; continue; }
|
||||
// Preserve the existing isolated-force-event interpolation policy.
|
||||
if (!Number.isFinite(y)) continue;
|
||||
samples.push({ x, y, dataIndex: i, breakBefore });
|
||||
breakBefore = false;
|
||||
}
|
||||
byTime.set(time, samples);
|
||||
return samples;
|
||||
}
|
||||
|
||||
export function chartCursorSamples(time: number[]) {
|
||||
let samples = cursorCache.get(time);
|
||||
if (!samples) {
|
||||
samples = [];
|
||||
for (let i = 0; i < time.length; i++) {
|
||||
if (Number.isFinite(time[i])) samples.push({ x: time[i], y: i, dataIndex: i, breakBefore: false });
|
||||
}
|
||||
cursorCache.set(time, samples);
|
||||
}
|
||||
return samples;
|
||||
}
|
||||
|
||||
export function chartSampleBounds(samples: Array<{ x: number; y: number }>): Bounds {
|
||||
const cached = boundsCache.get(samples);
|
||||
if (cached) return cached;
|
||||
const bounds = { xMin: Infinity, xMax: -Infinity, yMin: Infinity, yMax: -Infinity, sorted: true };
|
||||
let previousX = -Infinity;
|
||||
for (const sample of samples) {
|
||||
bounds.xMin = Math.min(bounds.xMin, sample.x);
|
||||
bounds.xMax = Math.max(bounds.xMax, sample.x);
|
||||
bounds.yMin = Math.min(bounds.yMin, sample.y);
|
||||
bounds.yMax = Math.max(bounds.yMax, sample.y);
|
||||
if (sample.x < previousX) bounds.sorted = false;
|
||||
previousX = sample.x;
|
||||
}
|
||||
boundsCache.set(samples, bounds);
|
||||
return bounds;
|
||||
}
|
||||
|
||||
export function combinedChartBounds(curves: Array<{ samples: Array<{ x: number; y: number }> }>) {
|
||||
const result = { xMin: Infinity, xMax: -Infinity, yMin: Infinity, yMax: -Infinity };
|
||||
for (const curve of curves) {
|
||||
const bounds = chartSampleBounds(curve.samples);
|
||||
result.xMin = Math.min(result.xMin, bounds.xMin);
|
||||
result.xMax = Math.max(result.xMax, bounds.xMax);
|
||||
result.yMin = Math.min(result.yMin, bounds.yMin);
|
||||
result.yMax = Math.max(result.yMax, bounds.yMax);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
function lowerBound(samples: Array<{ x: number }>, x: number, upper = false) {
|
||||
let low = 0, high = samples.length;
|
||||
while (low < high) {
|
||||
const middle = (low + high) >>> 1;
|
||||
if (samples[middle].x < x || (upper && samples[middle].x === x)) low = middle + 1;
|
||||
else high = middle;
|
||||
}
|
||||
return low;
|
||||
}
|
||||
|
||||
export function visibleSampleRange(samples: Array<{ x: number; y: number }>, xMin: number, xMax: number) {
|
||||
if (!chartSampleBounds(samples).sorted) return [0, samples.length] as const;
|
||||
// Include both outside neighbours, preserving segments crossing viewport boundaries.
|
||||
return [Math.max(0, lowerBound(samples, xMin) - 1), Math.min(samples.length, lowerBound(samples, xMax, true) + 1)] as const;
|
||||
}
|
||||
|
||||
/** Keep first/min/max/last per pixel column in original order; no averaging of peaks. */
|
||||
export function chartDisplaySamples(samples: ChartSample[], xMin: number, xMax: number, pixelWidth: number) {
|
||||
const width = Math.max(1, Math.ceil(pixelWidth));
|
||||
const key = `${xMin}:${xMax}:${width}`;
|
||||
let cache = selectionCache.get(samples);
|
||||
if (!cache) { cache = new Map(); selectionCache.set(samples, cache); }
|
||||
const cached = cache.get(key);
|
||||
if (cached) return cached;
|
||||
const [start, end] = visibleSampleRange(samples, xMin, xMax);
|
||||
const result: ChartSample[] = [];
|
||||
if (end - start <= width * 4 || !chartSampleBounds(samples).sorted) {
|
||||
for (let i = start; i < end; i++) result.push(samples[i]);
|
||||
} else {
|
||||
let bucket = -Infinity;
|
||||
let first = -1, last = -1, minimum = -1, maximum = -1;
|
||||
const flush = () => {
|
||||
if (first < 0) return;
|
||||
const indices = [first, minimum, maximum, last].sort((a, b) => a - b);
|
||||
let previous = -1;
|
||||
for (const index of indices) {
|
||||
if (index !== previous) result.push(samples[index]);
|
||||
previous = index;
|
||||
}
|
||||
};
|
||||
for (let i = start; i < end; i++) {
|
||||
const sample = samples[i];
|
||||
const nextBucket = Math.max(-1, Math.min(width, Math.floor((sample.x - xMin) / Math.max(xMax - xMin, 1e-12) * width)));
|
||||
if (nextBucket !== bucket || sample.breakBefore || first < 0) {
|
||||
flush();
|
||||
first = last = minimum = maximum = i;
|
||||
bucket = nextBucket;
|
||||
} else {
|
||||
last = i;
|
||||
if (sample.y < samples[minimum].y) minimum = i;
|
||||
if (sample.y > samples[maximum].y) maximum = i;
|
||||
}
|
||||
}
|
||||
flush();
|
||||
}
|
||||
// A few active windows/viewports may share a curve without retaining unbounded history.
|
||||
if (cache.size >= 4) cache.delete(cache.keys().next().value!);
|
||||
cache.set(key, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
export function sampledChartPath(
|
||||
time: number[], values: number[],
|
||||
xPosition: (value: number) => number, yPosition: (value: number) => number,
|
||||
xMin: number, xMax: number, pixelWidth: number,
|
||||
) {
|
||||
const samples = chartDisplaySamples(preparedChartSamples(time, values), xMin, xMax, pixelWidth);
|
||||
return samples.map((sample, index) =>
|
||||
`${index === 0 || sample.breakBefore ? "M" : "L"} ${xPosition(sample.x).toFixed(2)} ${yPosition(sample.y).toFixed(2)}`,
|
||||
).join(" ");
|
||||
}
|
||||
Reference in new issue
Block a user