{"ScriptPreparationCode":"\u0022use strict\u0022;\r\nconst isArrayLike = function (value) {\r\n return value !== null \u0026\u0026 typeof value !== \u0027function\u0027 \u0026\u0026 isFinite(value.length);\r\n};\r\nfunction last(o) {\r\n if (isArrayLike(o)) {\r\n const arr = o;\r\n return arr[arr.length - 1];\r\n }\r\n return undefined;\r\n}\r\n// isFinite,\r\nconst isNil = function (value) {\r\n /**\r\n * isNil(null) =\u003E true\r\n * isNil() =\u003E true\r\n */\r\n return value === null || value === undefined;\r\n};\r\nfunction size(o) {\r\n if (isNil(o)) {\r\n return 0;\r\n }\r\n if (isArrayLike(o)) {\r\n return o.length;\r\n }\r\n return Object.keys(o).length;\r\n}\r\nfunction head(o) {\r\n if (isArrayLike(o)) {\r\n return o[0];\r\n }\r\n return undefined;\r\n}\r\nconst indexOf = function (arr, obj) {\r\n if (!isArrayLike(arr)) {\r\n return -1;\r\n }\r\n const m = Array.prototype.indexOf;\r\n if (m) {\r\n return m.call(arr, obj);\r\n }\r\n let index = -1;\r\n for (let i = 0; i \u003C arr.length; i\u002B\u002B) {\r\n if (arr[i] === obj) {\r\n index = i;\r\n break;\r\n }\r\n }\r\n return index;\r\n};\r\nconst DEFAULT_Q = [1, 5, 2, 2.5, 4, 3];\r\nconst ALL_Q = [1, 5, 2, 2.5, 4, 3, 1.5, 7, 6, 8, 9];\r\nconst eps = Number.EPSILON * 100;\r\nfunction mod(n, m) {\r\n return ((n % m) \u002B m) % m;\r\n}\r\nfunction round(n) {\r\n return Math.round(n * 1e12) / 1e12;\r\n}\r\nfunction simplicity(q, Q, j, lmin, lmax, lstep) {\r\n const n = size(Q);\r\n const i = indexOf(Q, q);\r\n let v = 0;\r\n const m = mod(lmin, lstep);\r\n if ((m \u003C eps || lstep - m \u003C eps) \u0026\u0026 lmin \u003C= 0 \u0026\u0026 lmax \u003E= 0) {\r\n v = 1;\r\n }\r\n return 1 - i / (n - 1) - j \u002B v;\r\n}\r\nfunction simplicityMax(q, Q, j) {\r\n const n = size(Q);\r\n const i = indexOf(Q, q);\r\n const v = 1;\r\n return 1 - i / (n - 1) - j \u002B v;\r\n}\r\nfunction density(k, m, dMin, dMax, lMin, lMax) {\r\n const r = (k - 1) / (lMax - lMin);\r\n const rt = (m - 1) / (Math.max(lMax, dMax) - Math.min(dMin, lMin));\r\n return 2 - Math.max(r / rt, rt / r);\r\n}\r\nfunction densityMax(k, m) {\r\n if (k \u003E= m) {\r\n return 2 - (k - 1) / (m - 1);\r\n }\r\n return 1;\r\n}\r\nfunction coverage(dMin, dMax, lMin, lMax) {\r\n const range = dMax - dMin;\r\n return 1 - (0.5 * ((dMax - lMax) ** 2 \u002B (dMin - lMin) ** 2)) / (0.1 * range) ** 2;\r\n}\r\nfunction coverageMax(dMin, dMax, span) {\r\n const range = dMax - dMin;\r\n if (span \u003E range) {\r\n const half = (span - range) / 2;\r\n return 1 - half ** 2 / (0.1 * range) ** 2;\r\n }\r\n return 1;\r\n}\r\nfunction legibility() {\r\n return 1;\r\n}\r\nfunction extended(dMin, dMax, n = 5, onlyLoose = true, Q = DEFAULT_Q, w = [0.25, 0.2, 0.5, 0.05]) {\r\n // \u5904\u7406\u5C0F\u4E8E 0 \u548C\u5C0F\u6570\u7684 tickCount\r\n const m = n \u003C 0 ? 0 : Math.round(n);\r\n // nan \u4E5F\u4F1A\u5BFC\u81F4\u5F02\u5E38\r\n if (Number.isNaN(dMin) || Number.isNaN(dMax) || typeof dMin !== \u0027number\u0027 || typeof dMax !== \u0027number\u0027 || !m) {\r\n return {\r\n min: 0,\r\n max: 0,\r\n ticks: [],\r\n };\r\n }\r\n // js \u6781\u5927\u503C\u6781\u5C0F\u503C\u95EE\u9898\uFF0C\u5DEE\u503C\u5C0F\u4E8E 1e-15 \u4F1A\u5BFC\u81F4\u8BA1\u7B97\u51FA\u9519\r\n if (dMax - dMin \u003C 1e-15 || m === 1) {\r\n return {\r\n min: dMin,\r\n max: dMax,\r\n ticks: [dMin],\r\n };\r\n }\r\n // js \u8D85\u5927\u503C\u95EE\u9898\r\n if (dMax - dMin \u003E 1e148) {\r\n const count = n || 5;\r\n const step = (dMax - dMin) / count;\r\n return {\r\n min: dMin,\r\n max: dMax,\r\n ticks: Array(count)\r\n .fill(null)\r\n .map((_, idx) =\u003E {\r\n return prettyNumber(dMin \u002B step * idx);\r\n }),\r\n };\r\n }\r\n const best = {\r\n score: -2,\r\n lmin: 0,\r\n lmax: 0,\r\n lstep: 0,\r\n };\r\n let j = 1;\r\n while (j \u003C Infinity) {\r\n for (let i = 0; i \u003C Q.length; i \u002B= 1) {\r\n const q = Q[i];\r\n const sm = simplicityMax(q, Q, j);\r\n if (w[0] * sm \u002B w[1] \u002B w[2] \u002B w[3] \u003C best.score) {\r\n j = Infinity;\r\n break;\r\n }\r\n let k = 2;\r\n while (k \u003C Infinity) {\r\n const dm = densityMax(k, m);\r\n if (w[0] * sm \u002B w[1] \u002B w[2] * dm \u002B w[3] \u003C best.score) {\r\n break;\r\n }\r\n const delta = (dMax - dMin) / (k \u002B 1) / j / q;\r\n let z = Math.ceil(Math.log10(delta));\r\n while (z \u003C Infinity) {\r\n const step = j * q * 10 ** z;\r\n const cm = coverageMax(dMin, dMax, step * (k - 1));\r\n if (w[0] * sm \u002B w[1] * cm \u002B w[2] * dm \u002B w[3] \u003C best.score) {\r\n break;\r\n }\r\n const minStart = Math.floor(dMax / step) * j - (k - 1) * j;\r\n const maxStart = Math.ceil(dMin / step) * j;\r\n if (minStart \u003C= maxStart) {\r\n const count = maxStart - minStart;\r\n for (let i = 0; i \u003C= count; i \u002B= 1) {\r\n const start = minStart \u002B i;\r\n const lMin = start * (step / j);\r\n const lMax = lMin \u002B step * (k - 1);\r\n const lStep = step;\r\n const s = simplicity(q, Q, j, lMin, lMax, lStep);\r\n const c = coverage(dMin, dMax, lMin, lMax);\r\n const g = density(k, m, dMin, dMax, lMin, lMax);\r\n const l = legibility();\r\n const score = w[0] * s \u002B w[1] * c \u002B w[2] * g \u002B w[3] * l;\r\n if (score \u003E best.score \u0026\u0026 (!onlyLoose || (lMin \u003C= dMin \u0026\u0026 lMax \u003E= dMax))) {\r\n best.lmin = lMin;\r\n best.lmax = lMax;\r\n best.lstep = lStep;\r\n best.score = score;\r\n }\r\n }\r\n }\r\n z \u002B= 1;\r\n }\r\n k \u002B= 1;\r\n }\r\n }\r\n j \u002B= 1;\r\n }\r\n // \u5904\u7406\u7CBE\u5EA6\u95EE\u9898\uFF0C\u4FDD\u8BC1\u8FD9\u4E09\u4E2A\u6570\u6CA1\u6709\u7CBE\u5EA6\u95EE\u9898\r\n const lmax = prettyNumber(best.lmax);\r\n const lmin = prettyNumber(best.lmin);\r\n const lstep = prettyNumber(best.lstep);\r\n // \u52A0 round \u662F\u4E3A\u5904\u7406 extended(0.94, 1, 5)\r\n // \u4FDD\u8BC1\u751F\u6210\u7684 tickCount \u6CA1\u6709\u7CBE\u5EA6\u95EE\u9898\r\n const tickCount = Math.floor(round((lmax - lmin) / lstep)) \u002B 1;\r\n const ticks = new Array(tickCount);\r\n // \u5C11\u7528\u4E58\u6CD5\uFF1A\u9632\u6B62\u51FA\u73B0 -1.2 \u002B 1.2 * 3 = 2.3999999999999995 \u7684\u60C5\u51B5\r\n ticks[0] = prettyNumber(lmin);\r\n for (let i = 1; i \u003C tickCount; i\u002B\u002B) {\r\n ticks[i] = prettyNumber(ticks[i - 1] \u002B lstep);\r\n }\r\n return {\r\n min: Math.min(dMin, head(ticks)),\r\n max: Math.max(dMax, last(ticks)),\r\n ticks,\r\n };\r\n}\r\nfunction prettyNumber(n) {\r\n if (Math.abs(n) \u003C 1e-15)\r\n return 0;\r\n // \u5904\u7406\u63A5\u8FD1\u6574\u6570\u7684\u503C\r\n const rounded = Math.round(n);\r\n if (Math.abs(n - rounded) \u003C 1e-10)\r\n return rounded;\r\n // \u57FA\u4E8E\u6570\u503C\u5927\u5C0F\u52A8\u6001\u8C03\u6574\u7CBE\u5EA6\r\n const magnitude = Math.abs(n);\r\n if (magnitude \u003E= 1000) {\r\n return Number(n.toFixed(2));\r\n }\r\n return parseFloat(n.toFixed(15));\r\n}\r\nfunction dualAxisNice(primaryRange, secondaryRange, minTickCount = 4, maxTickCount = 7) {\r\n // \u751F\u6210\u523B\u5EA6\u65F6\u7684\u6743\u91CD - \u63D0\u9AD8density\u6743\u91CD\u4EE5\u786E\u4FDD\u523B\u5EA6\u6570\u91CF\u66F4\u63A5\u8FD1\u76EE\u6807\u503C\r\n const generationWeights = [0.1, 0.1, 0.75, 0.05];\r\n // \u4E3A\u6BCF\u4E2A\u8F74\u751F\u6210\u591A\u79CD\u5019\u9009\u65B9\u6848\r\n const primaryCandidates = [];\r\n const secondaryCandidates = [];\r\n // \u751F\u6210\u4E0D\u540C\u523B\u5EA6\u6570\u91CF\u7684\u5019\u9009\u65B9\u6848\r\n for (let tickCount = minTickCount; tickCount \u003C= maxTickCount; tickCount\u002B\u002B) {\r\n // \u4E3A\u6BCF\u4E2A\u523B\u5EA6\u6570\u91CF\u4F7F\u7528\u4E0D\u540C\u7684Q\u503C\u96C6\u5408\r\n const primaryQs = [\r\n DEFAULT_Q, // \u9ED8\u8BA4\u4F18\u96C5\u6570\u5B57\u96C6\u5408[1,5,2,2.5,4,3]\r\n ALL_Q, // \u6269\u5C55\u96C6\u5408\u5305\u542B\u66F4\u591A\u500D\u6570\uFF08\u59820.1,10\u7B49\uFF09\r\n generateSmartQ(primaryRange[0], primaryRange[1], tickCount), // \u6839\u636E\u6570\u636E\u8303\u56F4\u548C\u76EE\u6807\u523B\u5EA6\u6570\u91CF\u751F\u6210\u667A\u80FDQ\u503C\r\n ];\r\n const secondaryQs = [\r\n DEFAULT_Q, // \u9ED8\u8BA4\u4F18\u96C5\u6570\u5B57\u96C6\u5408[1,5,2,2.5,4,3]\r\n ALL_Q, // \u6269\u5C55\u96C6\u5408\u5305\u542B\u66F4\u591A\u500D\u6570\uFF08\u59820.1,10\u7B49\uFF09\r\n generateSmartQ(secondaryRange[0], secondaryRange[1], tickCount), // \u6839\u636E\u6570\u636E\u8303\u56F4\u548C\u76EE\u6807\u523B\u5EA6\u6570\u91CF\u751F\u6210\u667A\u80FDQ\u503C\r\n ];\r\n // \u4E3A\u4E3B\u8F74\u751F\u6210\u5019\u9009\u65B9\u6848 - \u4F7F\u7528\u589E\u5F3Adensity\u6743\u91CD\u7684\u53C2\u6570\r\n for (const Q of primaryQs) {\r\n primaryCandidates.push({\r\n result: extended(primaryRange[0], primaryRange[1], tickCount, true, Q, generationWeights),\r\n targetCount: tickCount\r\n });\r\n }\r\n // \u4E3A\u6B21\u8F74\u751F\u6210\u5019\u9009\u65B9\u6848 - \u4F7F\u7528\u589E\u5F3Adensity\u6743\u91CD\u7684\u53C2\u6570\r\n for (const Q of secondaryQs) {\r\n secondaryCandidates.push({\r\n result: extended(secondaryRange[0], secondaryRange[1], tickCount, true, Q, generationWeights),\r\n targetCount: tickCount\r\n });\r\n }\r\n }\r\n // \u6309\u523B\u5EA6\u6570\u91CF\u5206\u7EC4\r\n const groupedByCount = new Map();\r\n // \u6536\u96C6\u6240\u6709\u51FA\u73B0\u7684\u523B\u5EA6\u6570\u91CF\r\n const allTickCounts = new Set();\r\n // \u5BF9\u4E3B\u8F74\u5019\u9009\u65B9\u6848\u6309\u523B\u5EA6\u6570\u91CF\u5206\u7EC4\r\n primaryCandidates.forEach(candidate =\u003E {\r\n const tickCount = candidate.result.ticks.length;\r\n allTickCounts.add(tickCount);\r\n if (!groupedByCount.has(tickCount)) {\r\n groupedByCount.set(tickCount, { primary: [], secondary: [] });\r\n }\r\n groupedByCount.get(tickCount).primary.push(candidate);\r\n });\r\n // \u5BF9\u6B21\u8F74\u5019\u9009\u65B9\u6848\u6309\u523B\u5EA6\u6570\u91CF\u5206\u7EC4\r\n secondaryCandidates.forEach(candidate =\u003E {\r\n const tickCount = candidate.result.ticks.length;\r\n allTickCounts.add(tickCount);\r\n if (!groupedByCount.has(tickCount)) {\r\n groupedByCount.set(tickCount, { primary: [], secondary: [] });\r\n }\r\n groupedByCount.get(tickCount).secondary.push(candidate);\r\n });\r\n // \u627E\u5230\u6700\u4F73\u5339\u914D\u7684\u523B\u5EA6\u7EC4\u5408\r\n let bestScore = -Infinity;\r\n let bestResult = null;\r\n // \u5BF9\u6BCF\u4E2A\u523B\u5EA6\u6570\u91CF\u5206\u522B\u8BC4\u4F30\r\n allTickCounts.forEach(tickCount =\u003E {\r\n const group = groupedByCount.get(tickCount);\r\n // \u786E\u4FDD\u8FD9\u4E2A\u523B\u5EA6\u6570\u91CF\u5BF9\u4E8E\u4E24\u4E2A\u8F74\u90FD\u6709\u5019\u9009\u65B9\u6848\r\n if (group.primary.length \u003E 0 \u0026\u0026 group.secondary.length \u003E 0) {\r\n for (const primary of group.primary) {\r\n for (const secondary of group.secondary) {\r\n // \u4F7F\u7528\u8BC4\u4F30\u51FD\u6570\u8BC4\u4F30\u5F53\u524D\u7EC4\u5408\r\n const score = evaluateDualAxisMatch(primary.result, secondary.result, primaryRange, secondaryRange, primary.targetCount, secondary.targetCount);\r\n if (score \u003E bestScore) {\r\n bestScore = score;\r\n bestResult = { primary: primary.result, secondary: secondary.result };\r\n }\r\n }\r\n }\r\n }\r\n });\r\n return bestResult;\r\n}\r\nfunction generateSmartQ(min, max, n) {\r\n const span = max - min;\r\n if (span === 0)\r\n return [1];\r\n const power = 10 ** Math.floor(Math.log10(span));\r\n const baseQ = [1, 2, 5, 2.5, 4, 3];\r\n let Q = baseQ.map((q) =\u003E q * power);\r\n // \u6839\u636E\u57FA\u51C6\u6B65\u957F\u6269\u5C55\u5019\u9009\r\n const step = span / (n - 1);\r\n Q.push(step); // \u52A0\u5165\u57FA\u51C6\u6B65\u957F\r\n Q.push(step * 2); // \u52A0\u5165\u500D\u6570\r\n // \u53BB\u91CD\u3001\u6392\u5E8F\u3001\u622A\u65AD\r\n Q = Array.from(new Set(Q))\r\n .sort((a, b) =\u003E a - b)\r\n .slice(0, 10)\r\n // \u4FEE\u590D\u5C0F\u6570\u7CBE\u5EA6\u95EE\u9898: \u6839\u636E\u6570\u503C\u5927\u5C0F\u667A\u80FD\u9650\u5236\u5C0F\u6570\u4F4D\u6570\r\n .map((i) =\u003E {\r\n // \u5BF9\u5927\u6570\u503C\u53D6\u6574\uFF0C\u5BF9\u4E2D\u7B49\u6570\u503C\u4FDD\u75591\u4F4D\u5C0F\u6570\uFF0C\u5BF9\u5C0F\u6570\u503C\u4FDD\u75592\u4F4D\u5C0F\u6570\r\n if (i \u003E= 100) {\r\n return Math.round(i);\r\n }\r\n else if (i \u003E= 10) {\r\n return Number(i.toFixed(1));\r\n }\r\n else {\r\n return Number(i.toFixed(2));\r\n }\r\n });\r\n return Q;\r\n}\r\n// \u8BC4\u4F30\u53CC\u8F74\u523B\u5EA6\u5339\u914D\u5EA6 - \u4E0D\u8003\u8651density\uFF0C\u63D0\u9AD8simplicity\u548Ccoverage\u7684\u6743\u91CD\r\nfunction evaluateDualAxisMatch(primary, secondary, primaryRange, secondaryRange, primaryTargetCount = 0, secondaryTargetCount = 0) {\r\n // \u786E\u4FDD\u4E24\u4E2A\u8F74\u6709\u76F8\u540C\u6570\u91CF\u7684\u523B\u5EA6\r\n if (primary.ticks.length !== secondary.ticks.length) {\r\n return -Infinity;\r\n }\r\n // \u8BC4\u4F30\u6743\u91CD - \u63D0\u9AD8simplicity\u548Ccoverage\u6743\u91CD\uFF0C\u4E0D\u8003\u8651density\r\n const evaluationWeights = [0.45, 0.45, 0, 0.1];\r\n // \u8BA1\u7B97\u4E3B\u8F74\u548C\u6B21\u8F74\u7684\u523B\u5EA6\u7F8E\u89C2\u5EA6 - \u4F7F\u7528\u8C03\u6574\u540E\u7684\u8BC4\u4F30\u6743\u91CD\r\n const primaryScore = calculateAxisScore(primary.ticks, primaryRange, primaryTargetCount, evaluationWeights);\r\n const secondaryScore = calculateAxisScore(secondary.ticks, secondaryRange, secondaryTargetCount, evaluationWeights);\r\n // \u8BA1\u7B97\u6620\u5C04\u4E00\u81F4\u6027 - \u7406\u60F3\u60C5\u51B5\u4E0B\uFF0C\u76F8\u5BF9\u4F4D\u7F6E\u5E94\u8BE5\u5747\u5300\u5BF9\u5E94\r\n const mappingConsistency = calculateMappingConsistency(primary.ticks, primaryRange, secondary.ticks, secondaryRange);\r\n // \u8BA1\u7B97\u603B\u5206 - \u6743\u91CD\u53EF\u4EE5\u6839\u636E\u9700\u8981\u8C03\u6574\r\n return 0.3 * primaryScore \u002B 0.3 * secondaryScore \u002B 0.4 * mappingConsistency;\r\n}\r\n// \u8BA1\u7B97\u5355\u4E2A\u8F74\u7684\u8BC4\u5206\r\nfunction calculateAxisScore(ticks, dataRange, targetCount = 0, weights = [0.45, 0.45, 0, 0.1]) {\r\n if (ticks.length \u003C 2)\r\n return 0;\r\n // \u4ECEticks\u4E2D\u63D0\u53D6\u5173\u952E\u4FE1\u606F\r\n const lmin = ticks[0];\r\n const lmax = ticks[ticks.length - 1];\r\n const lstep = ticks[1] - ticks[0];\r\n // \u786E\u4FDD\u6B65\u957F\u4E00\u81F4\u6027\r\n const isConsistentStep = ticks.every((t, i) =\u003E i === 0 || Math.abs((t - ticks[i - 1]) - lstep) \u003C 1e-10);\r\n if (!isConsistentStep)\r\n return 0; // \u5982\u679C\u6B65\u957F\u4E0D\u4E00\u81F4\uFF0C\u8BC4\u5206\u4E3A0\r\n // \u63D0\u53D6\u6570\u636E\u8303\u56F4\r\n const [dMin, dMax] = dataRange;\r\n const m = targetCount || ticks.length;\r\n // \u8BA1\u7B97q\u503C\uFF08\u4ECEstep\u53CD\u63A8\uFF09\r\n const magnitude = Math.pow(10, Math.floor(Math.log10(lstep)));\r\n const q = lstep / magnitude;\r\n const j = 1; // \u7B80\u5316\u5904\u7406\r\n // \u590D\u7528\u73B0\u6709\u51FD\u6570\u8BA1\u7B97\u5404\u9879\u8BC4\u5206\r\n const s = simplicity(q, DEFAULT_Q, j, lmin, lmax, lstep);\r\n const c = coverage(dMin, dMax, lmin, lmax);\r\n const g = density(ticks.length, m, dMin, dMax, lmin, lmax);\r\n const l = legibility();\r\n // \u4F7F\u7528\u63D0\u4F9B\u7684\u6743\u91CD\u8BA1\u7B97\u603B\u5206\r\n return weights[0] * s \u002B weights[1] * c \u002B weights[2] * g \u002B weights[3] * l;\r\n}\r\n// \u8BA1\u7B97\u6620\u5C04\u4E00\u81F4\u6027 - \u68C0\u67E5\u76F8\u5BF9\u4F4D\u7F6E\u7684\u7EBF\u6027\u5173\u7CFB\r\nfunction calculateMappingConsistency(primaryTicks, primaryRange, secondaryTicks, secondaryRange) {\r\n // \u9488\u5BF9\u4E0D\u540C\u957F\u5EA6\u7684\u523B\u5EA6\u6570\u7EC4\u5904\u7406\u6620\u5C04\u4E00\u81F4\u6027\r\n // \u8BA1\u7B97\u5F52\u4E00\u5316\u4F4D\u7F6E\r\n const primaryNormalized = primaryTicks.map(t =\u003E (t - primaryRange[0]) / (primaryRange[1] - primaryRange[0]));\r\n const secondaryNormalized = secondaryTicks.map(t =\u003E (t - secondaryRange[0]) / (secondaryRange[1] - secondaryRange[0]));\r\n // \u5982\u679C\u4E24\u4E2A\u8F74\u7684\u523B\u5EA6\u6570\u4E0D\u540C\uFF0C\u6211\u4EEC\u9700\u8981\u901A\u8FC7\u63D2\u503C\u6765\u6BD4\u8F83\u5B83\u4EEC\u7684\u6620\u5C04\u4E00\u81F4\u6027\r\n if (primaryNormalized.length !== secondaryNormalized.length) {\r\n // \u83B7\u53D6\u8F83\u77ED\u7684\u6570\u7EC4\u957F\u5EA6\u548C\u8F83\u957F\u7684\u6570\u7EC4\r\n const minLength = Math.min(primaryNormalized.length, secondaryNormalized.length);\r\n const maxLength = Math.max(primaryNormalized.length, secondaryNormalized.length);\r\n const shorterArray = primaryNormalized.length \u003C secondaryNormalized.length ? primaryNormalized : secondaryNormalized;\r\n const longerArray = primaryNormalized.length \u003C secondaryNormalized.length ? secondaryNormalized : primaryNormalized;\r\n // \u8BA1\u7B97\u957F\u5EA6\u6BD4\u4F8B\u60E9\u7F5A - \u957F\u5EA6\u5DEE\u8DDD\u8D8A\u5927\uFF0C\u60E9\u7F5A\u8D8A\u5927\r\n const lengthPenalty = 1 - Math.min(0.5, (maxLength - minLength) / maxLength);\r\n // \u8BA1\u7B97\u5173\u952E\u70B9\u7684\u6620\u5C04\u5DEE\u5F02\r\n let sumSquaredDiff = 0;\r\n // \u5BF9\u8F83\u77ED\u6570\u7EC4\u4E2D\u7684\u6BCF\u4E2A\u70B9\uFF0C\u5728\u8F83\u957F\u6570\u7EC4\u4E2D\u627E\u5230\u5BF9\u5E94\u4F4D\u7F6E\u7684\u63D2\u503C\r\n for (let i = 0; i \u003C minLength; i\u002B\u002B) {\r\n // \u6839\u636E\u4F4D\u7F6E\u6BD4\u4F8B\u5728\u8F83\u957F\u6570\u7EC4\u4E2D\u67E5\u627E\u5BF9\u5E94\u70B9\r\n const position = i / (minLength - 1);\r\n const interpolatedIndex = position * (maxLength - 1);\r\n const lowerIndex = Math.floor(interpolatedIndex);\r\n const upperIndex = Math.ceil(interpolatedIndex);\r\n // \u5982\u679C\u7D22\u5F15\u76F8\u540C\uFF0C\u76F4\u63A5\u53D6\u503C\r\n let interpolatedValue;\r\n if (lowerIndex === upperIndex) {\r\n interpolatedValue = longerArray[lowerIndex];\r\n }\r\n else {\r\n // \u5426\u5219\u8FDB\u884C\u7EBF\u6027\u63D2\u503C\r\n const weight = interpolatedIndex - lowerIndex;\r\n interpolatedValue = longerArray[lowerIndex] * (1 - weight) \u002B longerArray[upperIndex] * weight;\r\n }\r\n // \u8BA1\u7B97\u5DEE\u5F02\r\n const diff = shorterArray[i] - interpolatedValue;\r\n sumSquaredDiff \u002B= diff * diff;\r\n }\r\n const rmsDiff = Math.sqrt(sumSquaredDiff / minLength);\r\n // \u8FD4\u56DE\u8003\u8651\u957F\u5EA6\u60E9\u7F5A\u7684\u8BC4\u5206\r\n return lengthPenalty * (1 - Math.min(1, rmsDiff * 10));\r\n }\r\n // \u5982\u679C\u4E24\u4E2A\u8F74\u7684\u523B\u5EA6\u6570\u76F8\u540C\uFF0C\u4F7F\u7528\u539F\u6765\u7684\u903B\u8F91\r\n let sumSquaredDiff = 0;\r\n for (let i = 0; i \u003C primaryNormalized.length; i\u002B\u002B) {\r\n const diff = primaryNormalized[i] - secondaryNormalized[i];\r\n sumSquaredDiff \u002B= diff * diff;\r\n }\r\n const rmsDiff = Math.sqrt(sumSquaredDiff / primaryNormalized.length);\r\n // \u8FD4\u56DE[0,1]\u8303\u56F4\u7684\u8BC4\u5206\uFF0C0\u8868\u793A\u5B8C\u5168\u4E0D\u5339\u914D\uFF0C1\u8868\u793A\u5B8C\u7F8E\u5339\u914D\r\n return 1 - Math.min(1, rmsDiff * 10);\r\n}\r\nfunction linearInterpolation(range, proportions, decimals) {\r\n const { min, max } = range;\r\n const propMin = Math.min(...proportions);\r\n const propMax = Math.max(...proportions);\r\n const propGap = propMax - propMin;\r\n const rangeGap = max - min;\r\n const interpolate = (value) =\u003E {\r\n const result = min \u002B ((value - propMin) * rangeGap) / propGap;\r\n return decimals == null ? result : Number(result.toFixed(decimals));\r\n };\r\n const ticks = proportions.map(interpolate);\r\n // \u82E5\u6709NaN\u5219\u8868\u793A\u5931\u8D25\uFF08\u6BD4\u5982\u9664\u6570\u4E3A0\uFF09\uFF0C\u8FD4\u56DE\u7A7A\r\n return ticks.some(isNaN) ? undefined : ticks;\r\n}\r\nconst testArray = [\r\n [10, 50], [200, 800],\r\n [0.06, 0.09], [400, 800],\r\n [0.273, 0.894], [-800, -200],\r\n [0.01, 0.05], [1000, 5000],\r\n [0.1, 0.8], [10000, 90000],\r\n [2.71, 3.14], [100, 900],\r\n [0.001, 0.009], [100, 900],\r\n [1, 2], [0, 1000],\r\n [0.1, 0.5], [1.5, 3.5],\r\n [0.999, 1.001], [9999, 10001],\r\n]","TestCases":[{"Name":"\u7EBF\u6027\u63D2\u503C","Code":"testArray.forEach(item =\u003E {\r\n linearInterpolation({ min: item[0][0], max: item[0][1] }, extended(item[1][0], item[1][1]).ticks);\r\n})","IsDeferred":false},{"Name":"\u53CC\u8F74\u5171\u540C\u6700\u4F18\u89E3","Code":"testArray.forEach(item =\u003E {\r\n dualAxisNice(item[0], item[1]);\r\n})","IsDeferred":false}]}