22

在 SVG 中绘制正弦波的最简单解决方案是什么?我想正弦波应该用 JavaScript 在一个简单的循环中重复...... :)

这是 XY 坐标作为一个好的开始...... :)

http://jsbin.com/adaxuy/1/edit

<svg>
  <line x1="0" y1="250" x2="500" y2="250"
        style="stroke:black;stroke-width:1"/>
  <line x1="250" y1="0" x2="250" y2="500"
        style="stroke:black;stroke-width:1"/>
</svg>
4

7 回答 7

29

直线近似的替代方法是贝塞尔近似。一个周期的第一季度的一个非常好的近似值是具有以下控制点的三次贝塞尔曲线:

 0   0
1/2 1/2
 1   1
π/2  1

编辑: 使用以下控制点可以进行更精确的近似:

0                    0
0.512286623256592433 0.512286623256592433
1.002313685767898599 1
1.570796326794896619 1

(见评论中 NominalAnimal 的解释)

演示比较线元素(灰色)和“好”贝塞尔曲线(红色)和“更好”贝塞尔曲线(绿色)。

精确插值样条端点的斜率和曲率的近似值是

       0                0 
(6−(3/2π−3)²)/6  (6−(3/2π−3)²)/6
       1                1
      π/2               1

见推导

于 2012-12-18T14:46:37.990 回答
12

line这是向 SVG 元素添加多个元素的概念证明:

var svg = document.getElementById('sine_wave').children[0];
var origin = { //origin of axes
    x: 100,
    y: 100
};
var amplitude = 10; // wave amplitude
var rarity = 1; // point spacing
var freq = 0.1; // angular frequency
var phase = 0; // phase angle

for (var i = -100; i < 1000; i++) {
    var line = document.createElementNS("http://www.w3.org/2000/svg", "line");

    line.setAttribute('x1', (i - 1) * rarity + origin.x);
    line.setAttribute('y1', Math.sin(freq*(i - 1 + phase)) * amplitude + origin.y);

    line.setAttribute('x2', i * rarity + origin.x);
    line.setAttribute('y2', Math.sin(freq*(i + phase)) * amplitude + origin.y);

    line.setAttribute('style', "stroke:black;stroke-width:1");

    svg.appendChild(line);
}
html, body, div{
    height:100%;
}
<div id="sine_wave">

  <svg width="1000" height="1000">
    <line x1="100" y1="0" x2="100" y2="200"
          style="stroke:black;stroke-width:1"/>
    <line x1="0" y1="100" x2="1000" y2="100"
          style="stroke:black;stroke-width:1"/>
  </svg>

</div>

于 2012-12-18T13:13:06.317 回答
11

以下将为您的 SVG 图形添加一个单周期正弦波:

const XMAX = 500;
const YMAX = 100;

// Create path instructions
const path = [];
for (let x = 0; x <= XMAX; x++) {
    const angle = (x / XMAX) * Math.PI * 2;  // angle = 0 -> 2π
    const y = Math.sin(angle) * (YMAX / 2) + (YMAX / 2);
    // M = move to, L = line to
    path.push((x == 0 ? 'M' : 'L') + x.toFixed(2) + ',' + y.toFixed(2));
}

// Create PATH element
const pathEl = document.createElementNS("http://www.w3.org/2000/svg", "path");
pathEl.setAttribute('d', path.join(' ') );
pathEl.style.stroke = 'blue';
pathEl.style.fill = 'none';

// Add it to svg element
document.querySelector('svg').appendChild(pathEl);
<svg width="500" height="100"/>

这使用由“lineto”(直线)命令组成的PATH 元素。这是有效的,因为毫不奇怪,它包含许多 (500) 个小线段。您可以通过使用贝塞尔曲线绘制线段来简化路径以减少点,但这会使代码复杂化。你要求简单。:)

于 2012-12-18T13:02:51.673 回答
4

如果它对任何人有用:这是一个单线 SVG,它使用三次贝塞尔近似近似接近正弦波的一半。

<svg width="100px" height="100px" viewBox="0 0 100 100">
    <path stroke="#000000" fill="none" d="M0,0 C36.42,0,63.58,100,100,100" />
</svg>

36.42通过最小化贝塞尔曲线和真实余弦曲线之间的平方和 (l2) 距离来拟合参数。https://octave-online.net/bucket~AN33qHTHk7eARgoSe7xpYg

我的回答部分基于如何在 SVG 中用贝塞尔路径近似半余弦曲线?

于 2017-12-27T04:02:56.060 回答
1

在 X 轴上循环,每次迭代都使用当前 X 值的正弦函数计算 Y 位置。

于 2012-12-18T12:53:13.913 回答
0

为了在插图中使用,此近似值效果很好。

<path d="M0,10 q5,20,10,0 t 10 0 10 0 10 0 10 0 10 0 10 0 10 0 10 0 10 0 10 0 10 0 10 0 10 0 10 0 10 0 10 0 10 0 10 0 10,0" stroke="black" fill="none"/>

它结构紧凑,使用起来相对容易。

  • M0,10

    • M 之后的第一个 10 将其向下移动 10(如果使用默认视图框,则进入视图)
    • 后面的零可用于向右或向左移动波。
    • 移动一小部分波长以显示相移
  • q5,20,10,0

    • 绘制前半波
    • 5 和 10 使半波 10 宽,因此全波的 20
      • 您可以将它们与 t 之后的所有 10 一起缩放
      • 您可以通过调整周期来说明 FM(见下文)
    • 20 使波的幅度为 10。(按口味缩放)
  • t 10 0

    • 重复前一个半波反转。
    • 每增加一个 10 0 10 0 产生一个额外的全波
    • 您也可以调制频率
      • 例如 10 0 10 0 7.5 0 5 0 5 0 5 0 7.5 0 10 0 10 0 10 0
      • 移动频率时,使用中间值,如 7.5
      • 中间值不会使波浪倾斜

我发现这对于说明数据通信中的调制很有用。为了说明 AM(甚至 QAM),只需使用新参数重复 q 命令。如果增加幅度,您可能需要调整 M 命令以将其移到视图中

要在 HTML5 中使用它,只需将其放入 svg 元素中

<h1>FM and QAM Examples</h1>
<svg>
  <path d=" 
M0,20
q 5 20 10 0 t 10 0 10 0 10 0 10 0 
7.5 0 5 0 5 0 5 0 5 0 5 0 5 0 7.5 0 
10 0 10 0 10 0 10 0 10 0 10 0 
10 0 10 0 10 0 10 0 10 0 10 0 

M0,60
q 5 20 10 0 t 10 0 10 0 10 0 10 0 
q 5 20 10 0 t 10 0 10 0 10 0 10 0 
q 5 40 10 0 t 10 0 10 0 10 0 10 0
q 5 -20 10 0 t 10 0 10 0 10 0 10 0 10 

" stroke="black" fill="none"/>
</svg>

在此处输入图像描述

于 2022-01-30T03:21:41.563 回答
0

这是一个 CDMA 插图,其中我使用三次样条来说明 CDMA 概念。首先定义这些函数:

<script>
function go(x,y) {return(`M ${x},${y}`)}
function to(y) {return(`c 5 0 5 ${y} 10 ${y}`)}
function dn(y=10) {return to(y)}
function up(y=10) {return to(-y)}
function path(d,color='black') {return `<path d="${d}" stroke=${color} fill="none"/>`}
function svg(t) {return `<svg>${t}</svg>`}
function bits(n) {
  let s='', n0=(n>>1)
  for (m=0x80;m;m>>=1) s+= up(10* (!(m&n0)-!(m&n))  )
  return s;
}
function plot(a) {
  let s='', y0=0
  for (let y of a) {
    s += up(y-y0); y0=y
  }
  return s
}
function add(a) {
  let s=''
  if typeof y0 == 'undefined' var y0=0
  for (m=0x80;m;m>>=1) {
    let y=0; for (let e of a) y+= 5-10*!(e&m)
    s += up(y-y0); y0=y   
  }
  return s
}
</script>

然后你可以大致说明波浪是这样的:

<script>
  document.write(svg(path(go(0,25)+(up()+dn()).repeat(10))))
</script>

用于说明目的的简单余弦波

这是使用此技术的 CDMA 示例

<h1>CDMA Example</h1>
<script>
a=0b00010010 
b=0b00010101 
document.write(svg(
  path(go(0,40)+bits(a)+bits(~a)+bits(a)+bits(~a)+bits(a),'red')+
  path(go(0,80)+bits(b)+bits(b)+bits(~b)+bits(~b)+bits(~b),'orange')+
  path(go(0,100+add([a,b])+add([~a,b])+add([a,~b])+add([~a,~b])+add([a,b])+add([~a,~b])))
))
</script>

使用 CDMA 风格技术复用两个信号(简化)

注意:实际的 CDMA 信号不会是位对齐的,甚至不会是芯片对齐的

于 2022-02-07T05:37:57.067 回答