Drawing a tubular path with Meshes.jl (blog post)


Follow the link to my blog.

1 Like

It is much simpler to define a tubular surface of directional curve, γ, using quaternions:

import ForwardDiff.derivative
import LinearAlgebra: norm, cross
import Rotations.UnitQuaternion

dγ(t)=derivative(γ, t) # t->̇γ(t)
ddγ(t)=derivative(dγ, t) #t->̈γ(t)

#get the tubular surface  parameterization:
function tube(γ::T, u::S, v::S; radius=0.1) where {S<:Real, T<:Function}
    tang = dγ(u)   #tangent(γ, u)
    ~iszero(tang) || error("null tangent vector!")
    unittan = tang/norm(tang)
    θ = acos(unittan[1])/2
    crossp= [0, -unittan[3], unittan[2]]  # cross product [1,0,0] x unittan
    quvect = sin(θ)*crossp/norm(crossp) #3-vector to define the quaternion 
                                          #q=(cos(θ), sin(θ)*unitvect)
    q = isapprox(θ, π/2) ? UnitQuaternion(0, 0, 1, 0) : isapprox(θ, 0) ? 
                   UnitQuaternion(1, 0 , 0, 0) : UnitQuaternion(cos(θ), quvect...)
    _, n₁, n₂,   = eachcol(q)
    return γ(u) + radius*cos(v) * n₁ + radius*sin(v) * n₂


1 Like

Very nice examples. I invite you all to contribute the Tube primitive to Meshes.jl:

We had an attempt by @_stla in the past, but it didn’t went through. Happy to review other attempts and parametrizations.

@empet With “my” method (it’s not mine), you can choose the number of sides and you can twist the tube.

The conciseness of yours is amazing.

Wow I’ve just seen your examples, awesome!

@_stla It was a kind of challenge to illustrate how short can be the Julia code to generate the same geometric
object, defined by Krane in C, and by someone elese in Python (see the links in the README file here).
I discovered the Hanson’s algorithm for more than an year, but I postponed its implementation, and this was an opportunity to do it.
@juliohm I have also defined the tube triangulation, and I can provide it, but I cannot make a PR, because I have a little experience with git. I can only assist you with the theoretical approach of the surfaces listed above, in the to do list.

@empet The main difference between our two implementations is that yours takes as input a function, that you can derivate to get the tangents, the normals and the binormals, whereas mine is for a discrete curve. But it’s true that usually we sample the trajectory of a given function to get a discrete path, so in this case it’s better to use your method.

@juliohm I have no Linux laptop currently (the one I had is dead) and it’s a bit annoying to work with Windows.

Moreover I’m a bit lost regarding this potential PR. I don’t know where to start. Should we preliminarily define a path primitive? Should we consider whether it is closed from an argument given by the user or should we rather detect whether it is closed?

And what about the method implemented by @empet? Should we do a method for a given differentiable function and another method (mine) for a discrete path?

@empet Off-topic but your avatar reminds me this picture:

Is it based on the Klein j-invariant function?

Regarding paths, we have the BezierCurve primitive for 2D and 3D paths, which can be sampled at any parameter value t in [0, 1]. It has the isclosed trait implemented already.

For discrete path we have the Rope and Ring, which are types of polygonal Chain. They also have the isclosed trait implemented.

Given these two paths we just need to expand laterally to produce the tube. So the Tube struct could take a path field and another field representing the shape, and then produce the tube. What do you think?

@empet you don’t need to know Git to contribute, you can go to the repository and press “.” on the keyboard to start editing the files on GitHub directly.

Hmm looks good.

@empet You can do “.” as said by Julio or we can also help you for git if you want to contribute. This would be an opportunity to learn, and this is not complicated.

1 Like