Bessel curve (plane) two 3D visualization (Three+ D3)

After learning Games101 geometry, I started practicing and visualizing Bezier curves

I want to achieve three-dimensional Bessel curve with Threejs, but threejs control is too troublesome, therefore, I use D3JS to achieve two-dimensional Bessel curve control, threejs to achieve three-dimensional Bessel curve visualization

Let me show you what a Bessel curve looks like in two or three dimensions

Function 1: Reproduce two-dimensional and three-dimensional Bezier curves; Function two: it can control the two-dimensional Bezier curve

Theoretical basis

First of all, how do we draw bezier curves for the three control points

B0, B1 and B2 are three control points respectively, and an independent variable t is set. The value range of T is [0, 1]. 0 corresponds to the starting point of a line and 1 corresponds to the end point of a line segment. For the first line segment b0 b1, t=0 represents b0 and t=1 represents B1. Let t now be some value in the domain, such as 0.3, and take the points b10 and b11 at this proportional position on B0 b1 and B1 B2 respectively. Connect b10, b11, and take the point B20 at 0.3. The final result is b20, which is the position at t=0.3.

If you change t from 0 to 1, you get a Bezier curve.

When there are four control points, the principle of picking points is the same

By setting a variable t, the situation of the four control points can be converted to the situation of the three control points by selecting the point at t for the first time. In this way, we can calculate the Bezier curve of any control point. Since we understand the process principle, the final calculation can be understood, as shown in the formula below

It can be seen that the coefficient in front of bi is the expansion of (1-t+t)^2


b n ( t ) = b 0 n ( t ) = i = 0 n C n i x t i x ( 1 t ) n i x b i b^n(t)=b^n_0(t)=\sum^n_{i=0} C^i_n \times t^i \times (1-t)^{n-i} \times b_i

So once you have this formula, you can actually do it, you need two independent variables when you make bezier surfaces, u, v replaces t, essentially replacing T with u, and then making v the new T, and iterating again. Here we take 16 points as an example, and every four points we do Bessel curve transformation first

You can see here that each of the four points has been converted to the Bezier curve, and then based on that, the four points on the Bezier curve have been converted to the bezier curve again.

  1. You go from 0 to 1, you get four curves
  2. When u is a certain value, you get four vertices on four curves as control points
  3. Based on the control point, v goes from 0 to 1, and you get a curve
  4. If you go through it, you get curves that look like surfaces

Code practice

In the code, the most important thing is to reproduce the algorithm, first look at the three control points how to achieve

// Find the position at time t
function getFinalPosition(t, controls, length){
    const n = length-1;
    const sum = {x:0.y:0};
    for (let i = 0; i <= n; i++) {
        const Bjn = getCombination(n, i)*Math.pow(t, i)*Math.pow(1-t, n-i);
        sum.x += controls[i].x * Bjn;
        sum.y += controls[i].y * Bjn;
    }
    return sum;
}

/ / factorial
function getFactorial(n){
    let result = 1;
    for (let i = 2; i <= n; i++) {
        result *= i;
    }
    return result;
}

// find the combination C
function getCombination(n, m){
    if(m === 0 || m === n){
        return 1;
    }
    return getFactorial(n)/(getFactorial(m)*getFactorial(n-m))
}

function fillPoints(){
    const resultPoints = [];
    for (let i = 0; i < NUMBER; i++) {
        const v1 = getFinalPosition(i/NUMBER, data, 4);
        resultPoints.push(v1);
    }
    path.attr('d', line(resultPoints))
}
fillPoints()
Copy the code

Pay attention to the point

  • The getFinalPosition method is just a conversion of the calculation formula
  • The formula for combination C is n factorial. /(m! *(n-m)!) , there are ways to speed things up, such as C0n=Cnn
  • The fillPoints method is to collect all the points and connect them to a line

This piece is displayed with D3JS, and balls and lines are created manually. Since they are self-defined graphics, it is very convenient to draw freely with D3JS. The codes for drawing dots and lines are as follows

/ / points
const points = canvas.selectAll('.point')
    .data(data)
    .enter()
    .append('circle')
    .attr('cx'.d= >d.x)
    .attr('cy'.d= >d.y)
    .attr('r'.d= >10)
    .attr('fill'.'#f9d2bb')
    .call(
        d3.drag()
            .on("drag".(event, d) = >{
                d.x = event.x;
                d.y = event.y;
            })
            .on("end".() = >{
                console.log(3)
            })
            .on('drag.update'.() = >{
                points.attr('cx'.d= >d.x)
                    .attr('cy'.d= >d.y); fillPoints(); }))const line = d3.line()
    .x(d= > {
        return d.x})
    .y(d= > d.y);

/ / line
const path = canvas
    .append('path')
    .attr("stroke"."red")
    .attr("stroke-width".3)
    .attr("fill"."none")
    .attr("stroke-opacity".0.4);
Copy the code

Add drag events to the four points to reproduce the computed lines each time you drag them

It’s a little bit more complicated in three dimensions, but the core stays the same

const NUMBER = 1000;
for (let u = 0; u < NUMBER; u++) {
    const resultPoints = [];
    const resultColor = [];
    for (let v = 0; v < NUMBER; v++) {
        const acrossPoints = [];
        for (let i = 0; i < 4; i++) {
            const v1 = getFinalPosition(v/NUMBER, points[i], 4);
            acrossPoints.push(v1.clone());
        }
        const v2 = getFinalPosition(u/NUMBER, acrossPoints, 4);
        resultPoints.push(v2.clone());
        const color = new THREE.Color(
            u/NUMBER, 1-u/NUMBER, u/NUMBER)
        resultColor.push(color.r, color.g, color.b);
    }
    const lineGeometry = new THREE.BufferGeometry().setFromPoints(resultPoints);
    lineGeometry.setAttribute('color'.new THREE.Float32BufferAttribute(resultColor, 3))
    const lineMaterial = new THREE.LineBasicMaterial({
        vertexColors: true
    })
    const line = new THREE.Line(lineGeometry, lineMaterial);
    scene.add(line)
}
Copy the code

Pay attention to the point

  1. The getFinalPosition method is similar to the two-dimensional method, which recreates the equation
  2. Cycle u and V, u is the outer layer, V is the inner layer, v traversal is equivalent to produce four control lines; U is traversed to produce the final point position
  3. It uses some of the threejs methods, so you can talk to each other if you’re interested

conclusion

The most important thing to realize Bezier curve is to understand the formula. There are many languages and visualization methods to realize the formula. I used three to show three and D3JS to show two dimensions. Code is too messy, there is interest can communicate.

Bezier curve currently uses lines to replace control points, but due to time constraints, d3JS is not used to achieve this function, if enough likes, give an encouragement, I will have energy to achieve it, 😀