如何为整数点集生成贝塞尔或钟形曲线高度并支持偏移与交互?
自定义钟形曲线高度生成方案
针对点集[0,1,2,...,10]生成符合要求的钟形高度,我们可以基于正态分布公式调整参数,满足起点/终点高度为0、最大高度100、峰值可偏移的需求:
核心实现逻辑
正态分布的基础公式为:
f(x) = (1/(σ√(2π))) * e^(-((x-μ)²)/(2σ²))
我们对其进行缩放和平移,使其适配边界与高度要求:
- 强制
x=0和x=10时高度为0 - 将曲线最大值缩放至100
- 自定义峰值位置
μ(比如中点5或偏移点3)
可偏移峰值的代码实现(JavaScript)
function generateBellCurvePoints(peakPosition = 5, maxHeight = 100, range = 10) { // 计算标准差σ,确保曲线在端点趋近于0 const sigma = Math.abs(peakPosition - range/2) * 0.6 || range/4; const points = []; for (let x = 0; x <= range; x++) { // 计算正态分布原始值 const exponent = -Math.pow(x - peakPosition, 2) / (2 * Math.pow(sigma, 2)); const rawValue = Math.exp(exponent); // 缩放至目标高度,强制端点为0 let height = rawValue * maxHeight; if (x === 0 || x === range) height = 0; // 可选:对高度取整 points.push(Math.round(height)); } return points; } // 示例:峰值在3时的生成结果 console.log(generateBellCurvePoints(3)); // 输出:[0, 12, 44, 100, 73, 30, 8, 1, 0, 0, 0]
推荐工具库
- bezierjs:完全匹配你的需求,支持创建贝塞尔曲线、拖拽调整控制点,还能通过
get(t)方法在任意参数位置采样点。只需将t映射到0-10的整数范围,即可计算对应高度:// 示例:创建峰值在中间的贝塞尔曲线 const curve = new Bezier(0,0, 2,100, 8,100, 10,0); const heights = []; for (let x = 0; x <=10; x++) { const t = x / 10; const point = curve.get(t); heights.push(Math.round(point.y)); } - D3.js:提供多种曲线插值方法(如
d3.curveBasis),支持快速生成平滑曲线,结合交互功能可实现控制点拖拽,适合调整后采样整数点高度。
Canvas简化贝塞尔拟合与采样实现
下面是一个极简Canvas实现,支持拖拽贝塞尔控制点,实时采样0-10的整数点高度:
<canvas id="curveCanvas" width="400" height="200"></canvas> <pre id="sampleOutput"></pre> <script> const canvas = document.getElementById('curveCanvas'); const ctx = canvas.getContext('2d'); const output = document.getElementById('sampleOutput'); // 贝塞尔控制点:起点、控制点1、控制点2、终点 let points = [ {x: 0, y: canvas.height, draggable: false}, {x: 100, y: 50, draggable: true}, {x: 300, y: 50, draggable: true}, {x: 400, y: canvas.height, draggable: false} ]; let draggingPoint = null; // 绘制曲线与控制点 function draw() { ctx.clearRect(0,0,canvas.width,canvas.height); // 绘制贝塞尔曲线 ctx.beginPath(); ctx.moveTo(points[0].x, points[0].y); ctx.bezierCurveTo(points[1].x, points[1].y, points[2].x, points[2].y, points[3].x, points[3].y); ctx.strokeStyle = '#2c3e50'; ctx.lineWidth = 2; ctx.stroke(); // 绘制控制点 points.forEach(p => { ctx.beginPath(); ctx.arc(p.x, p.y, 6, 0, Math.PI*2); ctx.fillStyle = p.draggable ? '#e74c3c' : '#3498db'; ctx.fill(); }); // 实时采样并输出高度值 sampleHeights(); } // 采样0-10整数点的高度 function sampleHeights() { const curve = new Bezier( points[0].x, points[0].y, points[1].x, points[1].y, points[2].x, points[2].y, points[3].x, points[3].y ); const heights = []; for (let x = 0; x <=10; x++) { const t = x / 10; const point = curve.get(t); // 转换为底部为0、顶部为100的高度值 const height = Math.round(100 - (point.y / canvas.height) * 100); heights.push(height); } output.textContent = `采样高度值:[${heights.join(', ')}]`; } // 拖拽交互逻辑 canvas.addEventListener('mousedown', (e) => { const rect = canvas.getBoundingClientRect(); const mouseX = e.clientX - rect.left; const mouseY = e.clientY - rect.top; draggingPoint = points.find(p => p.draggable && Math.hypot(mouseX - p.x, mouseY - p.y) < 10); }); canvas.addEventListener('mousemove', (e) => { if (!draggingPoint) return; const rect = canvas.getBoundingClientRect(); draggingPoint.x = e.clientX - rect.left; draggingPoint.y = e.clientY - rect.top; draw(); }); canvas.addEventListener('mouseup', () => draggingPoint = null); // 初始化绘制 draw(); </script>
注:上述代码依赖bezierjs库,需提前引入。
内容的提问来源于stack exchange,提问作者Ole
相关产品推荐
相关产品推荐

