使用SVG绘制含渐近线函数图像的问题求助
使用SVG绘制含渐近线函数图像的问题求助
我现在尝试用SVG绘制带有渐近线的函数图像,比如f(x) = tan(x)和f(x) = 1 / x。参考相关实现方法后,我已经能生成函数上的点,再通过二次和三次贝塞尔曲线把这些点连接起来,但目前的代码实现还存在两个问题:
- 只要图像存在渐近线(间断点),曲线总会把间断的两端错误地连接起来。我想知道怎么自动检测这些渐近线位置,确保间断的部分不会被连接?
- 绘制出的图像和
tan(x)或1/x的真实图形有偏差,间断点附近的部分没有被正确呈现出来。
我的代码实现
JavaScript代码
let smooth = 0.1; let container = document.getElementById("svg"); let width = container.getAttribute("width"); let height = container.getAttribute("height"); let xmin = -5 * 2 * Math.PI; let xmax = 5 * 2 * Math.PI; let ymin = -10; let ymax = 10; function f(x) { //use tan(x) or 1 / x return Math.tan(x); //return 1 / x; } let points = generatePoints(xmin, xmax, 150); let thePath = document.getElementById("thePath"); thePath.setAttribute("d", drawCurve(points)); thePath.setAttribute("stroke-width", 2); thePath.setAttribute("opacity", 1); thePath.setAttribute("stroke-linecap", "circle"); thePath.setAttribute("stroke-linejoin", "miter"); thePath.setAttribute("shape-rendering", "auto"); points.forEach((point) => { // magenta points plotSinglePoint(point.x, point.y); }); //////////////////////////////////////////////////////////////////////////////// // Implementation below // //////////////////////////////////////////////////////////////////////////////// function generatePoints(xMin, xMax, samples = 50) { result = []; let xInc = (xMax - xMin) / samples; for (let x = xMin; x < xMax; x += xInc) { let xVal = coordX(x); let yVal = coordY(f(x)); let point = { x: xVal, y: yVal }; if (isValidPoint(point)) { result.push(point); } } result.forEach((p) => { if (p.x == NaN || p.y == NaN) { console.log(p); } }); return result; } function isValidPoint(point) { return 0 <= point.x && point.x <= width && 0 <= point.y && point.y <= height; } function plotSinglePoint(x, y, r = 3) { let svgns = "http://www.w3.org/2000/svg"; let circle = document.createElementNS(svgns, "circle"); circle.setAttributeNS(null, "cx", x); circle.setAttributeNS(null, "cy", y); circle.setAttributeNS(null, "r", r); circle.setAttributeNS( null, "style", "fill: magenta; stroke: red; stroke-width: 0px;" ); container.appendChild(circle); } function controlPoints(p) { // given the points array p calculate the control points for the cubic Bezier curves var pc = []; for (var i = 1; i < p.length - 1; i++) { var dx = p[i - 1].x - p[i + 1].x; // difference x var dy = p[i - 1].y - p[i + 1].y; // difference y // the first control point var x1 = p[i].x - dx * smooth; var y1 = p[i].y - dy * smooth; var o1 = { x: x1, y: y1, }; // the second control point var x2 = p[i].x + dx * smooth; var y2 = p[i].y + dy * smooth; var o2 = { x: x2, y: y2, }; // building the control points array pc[i] = []; pc[i].push(o1); pc[i].push(o2); } return pc; } function drawCurve(p) { var pc = controlPoints(p); // the control points array let d = `M${p[0].x},${p[0].y} Q${pc[1][1].x},${pc[1][1].y}, ${p[1].x},${p[1].y} `; if (p.length > 2) { // central curves are cubic Bezier for (var i = 1; i < p.length - 2; i++) { d += `C${pc[i][0].x}, ${pc[i][0].y}, ${pc[i + 1][1].x}, ${ pc[i + 1][1].y }, ${p[i + 1].x},${p[i + 1].y}`; } // the first & the last curve are quadratic Bezier var n = p.length - 1; d += `Q${pc[n - 1][0].x}, ${pc[n - 1][0].y}, ${p[n].x}, ${p[n].y}`; } return d; } function coordX(x) { return (width * (x - xmin)) / (xmax - xmin); } function coordY(y) { return height * (1 - (y - ymin) / (ymax - ymin)); }
HTML代码
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <meta http-equiv="X-UA-Compatible" content="IE=edge" /> <title></title> <meta name="description" content="" /> <meta name="viewport" content="width=device-width, initial-scale=1" /> <link rel="stylesheet" href="" /> </head> <body> <svg id="svg" width="500" height="500" style="border: solid; fill: none; stroke: black" > <path id="thePath" /> <!-- stroke="url(#left-to-right)" --> </svg> <script src="script.js" async defer></script> </body> </html>
备注:内容来源于stack exchange,提问作者M. Al Jumaily
相关产品推荐
相关产品推荐

