复刻excel中带平滑线的散点图曲线,计算任意一点坐标
下面的案列是输入Y值,自动计算出X值
<template>
<div>
<label for="inputValue">输入数值:</label>
<input id="inputValue" v-model="inputValue" type="number" step="0.01" />
<button @click="calculate">计算</button>
<p v-if="result !== null">计算结果:{{ result }}</p>
</div>
</template>
<script>
export default {
data() {
return {
inputValue: null,
result: null,
originalDataPoints: [
{ x: 0.075, y: 4.5 },
{ x: 0.25, y: 9.3 },
{ x: 0.5, y: 15.4 },
{ x: 1, y: 23.6 },
{ x: 2, y: 40.7 },
{ x: 5, y: 63.3 },
{ x: 10, y: 82 },
{ x: 20, y: 100 },
{ x: 40, y: 100 },
{ x: 60, y: 100 }
],
dataPoints: []
}
},
methods: {
cubicBezier(p0, p1, p2, p3, t) {
// 三阶贝塞尔曲线计算逻辑
const result = {}
const oneMinusT = 1.0 - t
result.x =
oneMinusT * oneMinusT * oneMinusT * p0.x +
3 * oneMinusT * oneMinusT * t * p1.x +
3 * oneMinusT * t * t * p2.x +
t * t * t * p3.x
result.y =
oneMinusT * oneMinusT * oneMinusT * p0.y +
3 * oneMinusT * oneMinusT * t * p1.y +
3 * oneMinusT * t * t * p2.y +
t * t * t * p3.y
return result
},
ControlPoints4(p0, p1, p2, p3) {
// 控制点计算逻辑
const result = []
const P12 = {}
const P21 = {}
const d02 = (p0.x - p2.x) * (p0.x - p2.x) + (p0.y - p2.y) * (p0.y - p2.y)
const d12 = (p2.x - p1.x) * (p2.x - p1.x) + (p2.y - p1.y) * (p2.y - p1.y)
const d13 = (p3.x - p1.x) * (p3.x - p1.x) + (p3.y - p1.y) * (p3.y - p1.y)
if (d12 / 4 > d02 / 36) {
P12.x = (p2.x - p0.x) / 6 + p1.x
P12.y = (p2.y - p0.y) / 6 + p1.y
} else {
P12.x = ((p2.x - p0.x) / 2) * Math.sqrt(d12 / d02) + p1.x
P12.y = ((p2.y - p0.y) / 2) * Math.sqrt(d12 / d02) + p1.y
}
if (d12 / 4 > d13 / 36) {
P21.x = -(p3.x - p1.x) / 6 + p2.x
P21.y = -(p3.y - p1.y) / 6 + p2.y
} else {
P21.x = ((p1.x - p3.x) / 2) * Math.sqrt(d12 / d13) + p2.x
P21.y = ((p1.y - p3.y) / 2) * Math.sqrt(d12 / d13) + p2.y
}
result.push(P12)
result.push(P21)
return result
},
TfromY(p0, p1, p2, p3, y) {
// 已知y确定t:数值求解逻辑
const E = 0.001 // 误差
let e = 10
let left = 0
let right = 1
let t = 0.5
while (e > E) {
const smoothedPoint = this.cubicBezier(p0, p1, p2, p3, t)
if (y > smoothedPoint.y) {
left = t
e = y - smoothedPoint.y
} else {
right = t
e = smoothedPoint.y - y
}
t = (left + right) / 2
}
return t
},
Dy(dataPoints, Size, y) {
// y值查找对应x逻辑
if (y > dataPoints[Size - 1].y) {
return -1
}
let i = 0
while (y > dataPoints[i].y) {
i++
}
i--
let control = []
let t = 0
if (i === 0) {
control = this.ControlPoints4(dataPoints[i], dataPoints[i], dataPoints[i + 1], dataPoints[i + 2])
} else if (i === Size - 2) {
control = this.ControlPoints4(dataPoints[i - 1], dataPoints[i], dataPoints[i + 1], dataPoints[i + 1])
} else {
control = this.ControlPoints4(dataPoints[i - 1], dataPoints[i], dataPoints[i + 1], dataPoints[i + 2])
}
t = this.TfromY(dataPoints[i], control[0], control[1], dataPoints[i + 1], y)
const result = this.cubicBezier(dataPoints[i], control[0], control[1], dataPoints[i + 1], t)
return result.x
},
calculate() {
if (this.inputValue === null || this.inputValue === '') {
this.result = null
return
}
const y = parseFloat(this.inputValue)
const numPoints = this.originalDataPoints.length
this.dataPoints = JSON.parse(JSON.stringify(this.originalDataPoints))
for (let i = 0; i < numPoints; i++) {
this.dataPoints[i].x = Math.log10(this.dataPoints[i].x)
}
this.result = Math.pow(10, this.Dy(this.dataPoints, numPoints, y))
}
}
}
</script>