Sampling Cross-sections
In the post about navigating curves. The frames define a coordinate system where the origin is at point we are currently observing in the curve. The orthogonal vector to the tangent i.e. the normal of the plane can be used to define the zeroth degree. By rotating this vector along the tangent we can define the full cross-section to query i.e. from 0..2π. With the rotational minimizing frames, we solve the problem w.r.t. continuity between the cross-sections. But what happens to the cross-sections when there is a high curvature? The cross-sections will intersect each other. That is points queried at a distance (shown by the green line below) that are in the i+k th cross-section may actually be closer to the i th cross-section. Non-linear rays help to alleviate this issue. They are proven not to intersect. They may ovelap however. Change the ray option to non-linear and observe what happens in the high curvature areas.
Non-linear rays
The main theory regarding nonlinear ray tracing can be found in the papers below.
- Gröller, E. (1995). Nonlinear ray tracing: Visualizing strange worlds. The Visual Computer, 11(5), 263-274.
- Bartroli, A. V., Wegenkittl, R., Konig, A., & Groller, E. (2001, October). Nonlinear virtual colon unfolding. In Proceedings Visualization, 2001. VIS'01. (pp. 411-579). IEEE.
To summarize, given a curve if we move towards the gradient of the distance to the curve then the rays won't cross each other. We can pre-compute gradient distance map as was done by Bartroli et al. However, I prefer a less memory expensive approach by computing the gradient during the fly by applying small shifts to the point and using the sobel operator.
We get an initial point location at a desired angle and an ɛ distance to the curve. This is the starting point. This location can be computed using our typical Frenet/Wang frames. Afterwards we call the GetNextPoint function defined below until the desired distance is found (or any other user selected stopping criterion). The step size should be small enough, think of it as if you were doing the Euler method. Same thing happens if you select too big of a step size.
function GetNextPoint( currentPoint, sizeStep){
let dv = new THREE.Vector3(0.0, 0.0, 0.0);
SobelKernel = [-1,-2,-1,-2,-4,-2,-1,-2,-1, 0,0,0,0,0,0,0,0,0, 1,2,1,2,4,2,1,2,1];
for( i = -1; i <= 1; i++){
for( j = -1; j <= 1; j++){
for( k = -1; k <= 1; k++){
dIndex = [ i + 1, j + 1, k + 1];
let tmp = new THREE.Vector3(currentPoint.x + 0.1*i,currentPoint.y +0.1*j, currentPoint.z + 0.1*k);
let dist = ClosestDistanceToSegment(tmp);
let positionInImageY = dIndex[2] + dIndex[0]* 3 + dIndex[1]* 3* 3 ;
let positionInImageX = dIndex[1] + dIndex[2]* 3 + dIndex[0]* 3* 3 ;
let positionInImageZ = dIndex[0] + dIndex[1]* 3 + dIndex[2]* 3* 3 ;
dv.x += dist * ( SobelKernel[positionInImageX] * (1.0/27.0));
dv.y += dist * ( SobelKernel[positionInImageY] * (1.0/27.0));
dv.z += dist * ( SobelKernel[positionInImageZ] * (1.0/27.0));
}
}
}
dv.normalize();
let nextPoint = currentPoint.add(dv.multiplyScalar(sizeStep));
return nextPoint;
}