使用Epicycles和Fourier变换绘制/渲染3D对象[动画]

Cam*_*n S 8 javascript python algorithm 3d matlab

首先请注意:在我没有其他信誉点之前,他们不会让我嵌入图像(对不起),但是所有链接都是在imgur上发布的图像!:) 谢谢

我已经复制了一种使用傅立叶变换为任何单个路径(1条闭合路径)设置动画的方法。这样就创建了一个史诗般的动画(旋转的圆圈),它们彼此围绕旋转,并跟随插补点,以连续的循环/功能追踪路径。

我想将此系统用于3D。我可以想到的两种方法是使用球坐标系(两个复平面)或3个Epicycles->每个轴(x,y,z)一个,并带有各自的参数方程式。可能是最好的开始方式!

2个周期,一个用于X,一个用于Y: img

图片:一个周期->复数->对于X和Y img

傅立叶变换背景!!!:

•Eulers公式使我们能够将复杂平面中的每个点分解为一个角度(指数函数的自变量)和一个振幅(Cn系数)

•从这个意义上讲,与上面的无穷级数中的每个项的成像有关,代表半径为cn的圆上的点,偏移2?nt / T弧度

•下图显示了如何将相位/振幅方面的复数之和可视化为复平面中的一组串联曲线。每条红线是一个矢量,表示和序列中的一项:cne2?i(nT)t

•添加和数对应于简单地将复杂空间中的每个这些红色矢量串联起来:

动画旋转圆: img

圆到动画图纸:

•如果在2D(xy)空间中有线条图,则可以将该路径数学上描述为参数函数。(两个单独的单变量函数,均以辅助变量(在这种情况下为T表示):

在此处输入图片说明

•例如,以下是一匹马的简单线条图,以及穿过图像中黑色像素的参数路径,然后将该路径分为其X和Y分量:

马道

•此时,我们需要计算这两个路径的傅立叶近似,并使用该近似的系数来确定最终可视化所需的圆的相位和振幅。

Python代码:用于此示例的python代码可在guithub找到。

我已经成功地以2D动画化了此过程,但我想将其应用于3D。

以下代码表示2D动画->我已经在使用的东西:

[使用JavaScript和P5.js库]

傅里叶算法(fourier.js):

//  a + bi
class Complex {
  constructor(a, b) {
    this.re = a;
    this.im = b;
  }

  add(c) {
    this.re += c.re;
    this.im += c.im;
  }

  mult(c) {
    const re = this.re * c.re - this.im * c.im;
    const im = this.re * c.im + this.im * c.re;
    return new Complex(re, im);
  }
}



function dft(x) {
  const X = [];
  const Values = [];
  const N = x.length;
  for (let k = 0; k < N; k++) {
    let sum = new Complex(0, 0);
    for (let n = 0; n < N; n++) {
      const phi = (TWO_PI * k * n) / N;
      const c = new Complex(cos(phi), -sin(phi));
      sum.add(x[n].mult(c));
    }
    sum.re = sum.re / N;
    sum.im = sum.im / N;

    let freq = k;
    let amp = sqrt(sum.re * sum.re + sum.im * sum.im);
    let phase = atan2(sum.im, sum.re);
    X[k] = { re: sum.re, im: sum.im, freq, amp, phase };
Values[k] = {phase};
  console.log(Values[k]);

  }
  return X;
}
Run Code Online (Sandbox Code Playgroud)

素描功能/动画(Sketch.js):

let x = [];
let fourierX;
let time = 0;
let path = [];

function setup() {
  createCanvas(800, 600);
  const skip = 1;
  for (let i = 0; i < drawing.length; i += skip) {
    const c = new Complex(drawing[i].x, drawing[i].y);
    x.push(c);
  }
  fourierX = dft(x);
  fourierX.sort((a, b) => b.amp - a.amp);
}

function epicycles(x, y, rotation, fourier) {
  for (let i = 0; i < fourier.length; i++) {
    let prevx = x;
    let prevy = y;
    let freq = fourier[i].freq;
    let radius = fourier[i].amp;
    let phase = fourier[i].phase;
    x += radius * cos(freq * time + phase + rotation);
    y += radius * sin(freq * time + phase + rotation);

    stroke(255, 100);
    noFill();
    ellipse(prevx, prevy, radius * 2);
    stroke(255);
    line(prevx, prevy, x, y);
  }
  return createVector(x, y);
}

function draw() {
  background(0);

  let v = epicycles(width / 2, height / 2, 0, fourierX);
  path.unshift(v);

  beginShape();
  noFill();
  for (let i = 0; i < path.length; i++) {
    vertex(path[i].x, path[i].y);
  }
  endShape();

  const dt = TWO_PI / fourierX.length;
  time += dt;
Run Code Online (Sandbox Code Playgroud)

最重要的是!路径/坐标:( 这个是三角形)

let drawing = [

  { y:  -8.001009734    , x:    -50 },
  { y:  -7.680969345    , x:    -49 },
  { y:  -7.360928956    , x:    -48 },
  { y:  -7.040888566    , x:    -47 },
  { y:  -6.720848177    , x:    -46 },
  { y:  -6.400807788    , x:    -45 },
  { y:  -6.080767398    , x:    -44 },
  { y:  -5.760727009    , x:    -43 },
  { y:  -5.440686619    , x:    -42 },
  { y:  -5.12064623 , x:    -41 },
  { y:  -4.800605841    , x:    -40 },
...
...

  { y:  -8.001009734    , x:    -47 },
  { y:  -8.001009734    , x:    -48 },
  { y:  -8.001009734    , x:    -49 },


];

Run Code Online (Sandbox Code Playgroud)

Tre*_*ium 3

这个答案是为了回应:“你认为 [third.js] 可以通过旋转圆圈和其他东西来复制我在 2D 和 3D 中拥有的东西吗?”

我不确定您是否想从头开始学习 3D 建模(即创建您自己的矢量例程库、齐次坐标变换、渲染透视等),或者您是否只是想生产最终产品。对于后者,two.js 是一个基于 webGL 构建的强大图形库,在我看来,它对于初学者来说足够简单,但具有很大的深度,可以产生非常复杂的 3D 效果。(仔细阅读https://trijs.org/examples/上的示例,您就会亲眼看到。)

我碰巧正在开发自己的 Three.js 项目,并制作了一个周转圆的快速示例作为热身练习。这涉及从以下参考文献中提取零件和零件......

结果是一个简单的场景,一个圆圈绕着另一个圆圈运行,允许鼠标控制围绕场景旋转,从不同的角度和距离查看它。

<html>
  <head>
    <title>Epicyclic Circles</title>
    <style>
      body { margin: 0; }
      canvas { width: 100%; height: 100% }
    </style>
  </head>
  <body>

    <script src="https://rawgit.com/mrdoob/three.js/dev/build/three.js"></script>
    <script src="https://rawgit.com/mrdoob/three.js/dev/examples/js/controls/OrbitControls.js"></script>

    <script>

      // Set up the basic scene, camera, and lights.
      
      var scene = new THREE.Scene();
      scene.background = new THREE.Color( 0xf0f0f0 );
      
      var camera = new THREE.PerspectiveCamera( 75, window.innerWidth/window.innerHeight, 0.1, 1000 );
      scene.add(camera)
      
      var light = new THREE.PointLight( 0xffffff, 0.8 );
      camera.add( light );
      
      camera.position.z = 50;
        
      var renderer = new THREE.WebGLRenderer();
      renderer.setSize( window.innerWidth, window.innerHeight );
      document.body.appendChild( renderer.domElement );
      
      // Add the orbit controls to permit viewing the scene from different angles via the mouse.
      
      controls = new THREE.OrbitControls( camera, renderer.domElement );
      controls.enableDamping = true; // an animation loop is required when either damping or auto-rotation are enabled
      controls.dampingFactor = 0.25;
      controls.screenSpacePanning = false;
      controls.minDistance = 0;
      controls.maxDistance = 500;
      
      // Create center and epicyclic circles, extruding them to give them some depth.
      
      var extrudeSettings = { depth: 2, bevelEnabled: true, bevelSegments: 2, steps: 2, bevelSize: .25, bevelThickness: .25 };

      var arcShape1 = new THREE.Shape();
      arcShape1.moveTo( 0, 0 );
      arcShape1.absarc( 0, 0, 15, 0, Math.PI * 2, false );
      var holePath1 = new THREE.Path();
      holePath1.moveTo( 0, 10 );
      holePath1.absarc( 0, 10, 2, 0, Math.PI * 2, true );
      arcShape1.holes.push( holePath1 );
        
      var geometry1 = new THREE.ExtrudeBufferGeometry( arcShape1, extrudeSettings );
      var mesh1 = new THREE.Mesh( geometry1, new THREE.MeshPhongMaterial( { color: 0x804000 } ) );
      scene.add( mesh1 );
      
      var arcShape2 = new THREE.Shape();
      arcShape2.moveTo( 0, 0 );
      arcShape2.absarc( 0, 0, 15, 0, Math.PI * 2, false );
      var holePath2 = new THREE.Path();
      holePath2.moveTo( 0, 10 );
      holePath2.absarc( 0, 10, 2, 0, Math.PI * 2, true );
      arcShape2.holes.push( holePath2 );
          
      var geometry2 = new THREE.ExtrudeGeometry( arcShape2, extrudeSettings );
      var mesh2 = new THREE.Mesh( geometry2, new THREE.MeshPhongMaterial( { color: 0x00ff00 } ) );
      scene.add( mesh2 );

      // Define variables to hold the current epicyclic radius and current angle.
      var mesh2AxisRadius = 30;
      var mesh2AxisAngle = 0;

      var animate = function () {
        requestAnimationFrame( animate );

        // During each animation frame, let's rotate the objects on their center axis,  
        // and also set the position of the epicyclic circle.
        
        mesh1.rotation.z -= 0.02;

        mesh2.rotation.z += 0.02;
        mesh2AxisAngle += 0.01;
        mesh2.position.set ( mesh2AxisRadius * Math.cos(mesh2AxisAngle), mesh2AxisRadius * Math.sin(mesh2AxisAngle), 0 );

        renderer.render( scene, camera );
      };

      animate();
    </script>
  </body>
</html>
Run Code Online (Sandbox Code Playgroud)

请注意,我在函数中使用了基本三角学animate来将周转圆定位在中心圆周围,并捏造了圆的旋转速率(而不是进行精确的数学计算),但可能有一个更好的“三.js”-通过矩阵或内置函数来做到这一点的方法。鉴于您显然拥有强大的数学背景,我认为您在移植到 3D 时使用基本三角学转换多周转圆的 2D 模型不会有任何问题。

希望这有助于您决定如何继续进行 3D 版本的程序。