【问题标题】:Creating a helix following a curve沿着曲线创建螺旋线
【发布时间】:2018-12-02 05:20:09
【问题描述】:

我希望创建一个遵循曲线的螺旋线,看看我在追求什么,这个链接有一个 gif 代表它......

https://www.mapleprimes.com/posts/202940-Spiral-Around-The-Curve

(我对动画或其他几何图形不感兴趣,只是产生的粉红色螺旋)

理想情况下,我应该能够拥有任何形状的曲线,知道我想要的螺旋线的直径/半径,并从中生成第二条曲线(螺旋线),以恒定的螺距围绕它行进

我在 Javascript (threeJS) 中执行此操作,但我认为它更多的是一般的数学问题。

使用以下方法,我可以在直线部分周围得到一个螺旋线,但是当它改变方向/弯曲时它会惨遭失败......

let helixPoints = [];
let helixDiameter = 30;

for (let t = 0; t < 1; t += 0.01) {
    let curvePoint = curve.getPointAt(t);
    let helixX = curvePoint.x + (helixDiameter * Math.cos(t*100));
    let helixY = curvePoint.y +(helixDiameter * Math.sin(t*100))
    let helixZ = curvePoint.z;

    helixPoints.push(new THREE.Vector3(helixX, helixY, helixZ));
}

let helixCurve = new THREE.CatmullRomCurve3(helixPoints);

我知道我需要对 helixZ 做更多的事情,我认为它遵循任何曲线,我可能需要得到点的切线?

我只是无法理解数学,如果有人能指出我正确的方向,我将不胜感激

【问题讨论】:

    标签: math three.js


    【解决方案1】:

    让螺旋跟随螺旋......这很有趣。可能有更好的方法,但这是我的解决方案:

    我定义了一条曲线,与您获得初始螺旋线的方式相同(我的演示中的黄线)。调用 getPoints 会得到一堆 Vector3 对象,然后我将其投入使用。

    首先,从第二点开始,回头看前一点,形成了一个方向。这接近于曲线的切线,但如果从曲线中收集的点越少,它的准确度就会降低。

    接下来,我将它与up 向量交叉。我使用了(0, 1, 0),但你原来的螺旋方向可能需要你使用不同的方向。这个想法是up 始终朝向你的切线,这样穿过它们就会产生一个垂直于曲线的向量。

    说到交叉向量,这就是我接下来所做的,得到了​​一个很好的统一向量包。然后我使用applyAxisAngle 将垂直向量围绕切线旋转了一个从当前点的索引计算的量。

    最后,将原始点位置添加到旋转矢量后,我们在 3D 空间中得到一个与旋转点等效的点,但遵循原始曲线。

    您可以使用不同的值,例如temp 长度和用于实现不同半径、频率等的间隔。

    这是一个相当大的挑战。谢谢你的练习!如果您有任何问题,请发表评论,我会尽力解答。

    // prepare the renderer
    
    let WIDTH
    let HEIGHT
    let aspectRatio = function() {
      return WIDTH / HEIGHT
    }
    
    const renderer = new THREE.WebGLRenderer({
      antialias: true,
      alpha: true
    })
    document.body.appendChild(renderer.domElement)
    
    const camera = new THREE.PerspectiveCamera(32, aspectRatio(), 1, 1000)
    camera.position.set(0, 0, 50)
    
    function resize() {
      WIDTH = window.innerWidth
      HEIGHT = window.innerHeight
      renderer.setSize(WIDTH, HEIGHT)
      camera.aspect = aspectRatio()
      camera.updateProjectionMatrix()
    }
    resize()
    
    window.addEventListener("resize", resize)
    
    const scene = new THREE.Scene()
    
    const light = new THREE.DirectionalLight(0xffffff, 1, Infinity)
    light.position.set(0, 0, 1)
    camera.add(light)
    
    scene.add(camera)
    
    // populate the scene
    
    let group = new THREE.Group()
    group.scale.set(5, 5, 5)
    scene.add(group)
    
    let curvePoints = []
    let rad = Math.PI / 4
    for (let i = -10; i <= 10; ++i) {
      curvePoints.push(new THREE.Vector3(i / 5, Math.sin(rad * i), Math.cos(rad * i)))
    }
    let curve = new THREE.CatmullRomCurve3(curvePoints)
    
    let geoPoints = curve.getPoints(500);
    
    let geo = new THREE.BufferGeometry().setFromPoints(geoPoints)
    let mat = new THREE.LineBasicMaterial({
      color: "yellow"
    })
    let mesh = new THREE.Line(geo, mat)
    group.add(mesh)
    
    let dir = new THREE.Vector3()
    let up = new THREE.Vector3(0, 1, 0)
    let temp = new THREE.Vector3()
    let amount = 0.25;
    let innerHelixPoints = []
    geoPoints.forEach(function(point, index, arr) {
      if (index > 0) {
        dir.subVectors(point, arr[index - 1])
        dir.normalize()
        temp.crossVectors(dir, up)
        temp.applyAxisAngle(dir, amount * index)
        temp.setLength(0.5)
        temp.add(arr[index - 1])
        innerHelixPoints.push(temp.clone())
      }
    })
    
    geo = new THREE.BufferGeometry().setFromPoints(innerHelixPoints)
    mat = new THREE.LineBasicMaterial({
      color: "blue"
    })
    mesh = new THREE.Line(geo, mat)
    group.add(mesh)
    
    /* let geo = new THREE.BoxBufferGeometry(10, 10, 10)
    let mat = new THREE.MeshLambertMaterial({
        color: "red"
    })
    let mesh = new THREE.Mesh(geo, mat)
    scene.add(mesh) */
    
    function updateObjects() {
      group.rotation.x += 0.001
      group.rotation.y += 0.002
    }
    
    // rendering functions
    
    function render() {
      renderer.render(scene, camera)
    }
    
    let animationLoopId = null
    
    function animationLoop() {
      animationLoopId = requestAnimationFrame(animationLoop)
      updateObjects()
      render()
    }
    
    animationLoop()
    html,
    body {
      padding: 0;
      margin: 0;
      overflow: hidden;
      background: slateGray;
    }
    &lt;script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/93/three.js"&gt;&lt;/script&gt;

    【讨论】:

    • 哇,谢谢,看起来很棒。我目前无法进行测试,但是出现了一个问题(这可能真的很愚蠢,但在这里)..我的螺旋线将绕行的曲线实际上可以是任何东西,所以如何知道向上向量(0 , 1, 0),什么时候可能会根据曲线方向而改变?
    • 我同意,这是需要改进的领域之一。在大多数情况下,只要您的曲线不改变 general 方向(意味着它不会回环或任何东西),那么单个 up 应该没问题。可能有一种方法可以从曲线中获得更准确的 up,而不是从 getTangent 获得的切线,但我将把它留给你来修改,因为这满足了最初的指定要求。
    • @TheJim01 非常感谢您的建议和反馈,非常有用
    猜你喜欢
    • 2019-11-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-12-29
    • 1970-01-01
    • 1970-01-01
    • 2022-10-15
    • 2017-03-30
    相关资源
    最近更新 更多