#StackBounty: #android #3d #augmented-reality #arcore How can i scan a qr code and place an 3d object using ar core?

Bounty: 100

here i need some suggestion or a want to a way of doing this

scenario : i want to scan a qr code and after scanning the qr code i want place a 3d object in the ar scene . here i dont want to use google vision api and instead i want to use any qr reader package and want to place it. is there reference / url so that it will help me to achieve this ?

i used this package for qr scan https://github.com/zxing/zxing


Get this bounty!!!

#StackBounty: #android #android-studio #3d #arcore #sceneform Draw Renderable Shape on 3D Model using Sceneform Android

Bounty: 50

I am using SceneForm Android SDK to render 3D Model in Android APP.

To display the 3D Model I am using this code below which also supports Rotating and Zooming feature in 3D Model

 private fun renderLocalObject(position: Int) {

    ModelRenderable.builder()
            .setSource(appCompatActivity, Uri.parse(models.get(position)))
            .setRegistryId(models.get(position))
            .build()
            .thenAccept { modelRenderable: ModelRenderable ->
                addNodeToScene(modelRenderable)
            }
            .exceptionally { throwable: Throwable? ->
                var message: String?
                message = if (throwable is CompletionException) {
                    Logger.DebugLog("ERROR LOADING 3D", throwable.message)
                    "Internet is not working"
                } else {
                    Logger.DebugLog("ERROR LOADING 3D", "FAILED TO LOAD")
                    "Can't load Model"

                }
                null
            }
}

private fun addNodeToScene(model: ModelRenderable) {

    if (sceneView != null) {

        val transformationSystem = makeTransformationSystem()
        val dragTransformableNode = DragTransformableNode(5f, transformationSystem)
        dragTransformableNode.renderable = model
        sceneView!!.scene.addChild(dragTransformableNode)
        dragTransformableNode.select()


        sceneView!!.getScene()
                .addOnPeekTouchListener { hitTestResult: HitTestResult?, motionEvent: MotionEvent? ->
                    transformationSystem.onTouch(
                            hitTestResult,
                            motionEvent
                    )

                    when (motionEvent?.action) {
                        MotionEvent.ACTION_UP ->
                            if (hitTestResult != null) {
                                addDot(hitTestResult)
                            }

                    }


                }
    }
}

I want to draw a sphere renderable shape directly over the 3d model, but the Node is not getting stuck model.

Below is the required output

Expected Output

Below is the final output of my code

Final Output

This is my code for adding node

  private fun addDot(hitTestResult: HitTestResult){
    val color = Color(.8f, 0f, 0f)
    MaterialFactory.makeOpaqueWithColor(appCompatActivity, color)
            .thenAccept { material->
                // The sphere is in local coordinate space, so make the center 0,0,0
                val sphere = ShapeFactory.makeSphere(0.05f, Vector3.zero(),
                        material)
                val indicatorModel = Node()
                indicatorModel.setParent(hitTestResult.node)
                indicatorModel.worldPosition = hitTestResult.point
              //  indicatorModel.localPosition = Vector3(0f, 0f, -1f)
                indicatorModel.renderable = sphere
               // sceneView!!.getScene().addChild(indicatorModel)


            }
}

Is there any way to draw shape directly over the 3d model.


Get this bounty!!!

#StackBounty: #javascript #math #graph #3d #2d JavaScript 3D Chart Library for Math/Engineering. Projection of 3D figures to 2D

Bounty: 100

I’m looking for a JavaScript library for plotting 3D charts with figures like Cylinder, Box, Torus, Sphere etc for mathematical/engineering purposes.

The library I’m looking for should have ability to mark some important sections of the graph like function name, intersections, crossing points etc.

There is no need for any 3D shading to show a shape of a 3D figure. Instead the library should draw dashed lines in case they are covered by the front of a 3D figure.

I found a good 2D library for drawing geometry JSX Graph that is well suited for the purpose but unlucky it lacks drawing 2D projection of 3D figures.

Below some examples from JSX Graph to picture some of the features that are required.

enter image description here

Adding auxiliary lines as dashed, adding labels to graphs, marking an important joint point

enter image description here

Adding points of lines’ cross, marking an intersection area.

In short the library should be able to make 3D graphs like these you can find in books or a technical documentation.
Do you know any library like JSX graph but with a support of projecting 3D figures to the 2D?


Get this bounty!!!

#StackBounty: #3d #computational-geometry #mesh #geometry #cad Algorithm to select regions based on curvature on a mesh

Bounty: 50

I’m trying to understand how to implement an algorithm similar to the one used by Magics’ mark surface tool, you can see such behaviour on this video.

Quoting the video: "Basically with this tool you’re able to select surfaces, which unlike planes, surfaces take curvature into account."

The first idea that come to my mind to implement something similar was starting by considering the adjacency information of the mesh and consider on the computation the angle adjacent triangle normals. My idea was that if such an angle wasn’t on the range [pi/2-tol, pi/2+tol] two adjacent triangles would be "smooth". This thought was too naive and the idea would just work for a very limited of cases and it’d start fail for many of them.

After that, I’ve spent a little bit of time reading some papers talking about mesh segmentation and it seems this has been an area of research for many years… But before even considering implementing any of these one I’d like to ask here if you knew some basic&good enough algorithm I could implement that could behave in a similar fashion to Magic’s.

So yeah, that’s my question basically, assuming a triangular mesh that has adjacency information built (ie: you can check adjacent triangles from any given face) and a starting selected triangle, how would you detect the "surface" region associated to it?

Thanks in advance.


Get this bounty!!!

#StackBounty: #tikz-pgf #diagrams #3d #tikz-cd #tikz-3d Deforming/projecting text in TikZ(-cd) along a curved surface

Bounty: 50

This is a continuation of STeX Exchange 552946.

I’ve been trying to get some 3D effects on tikz-cd, and got an amazing answer by @ZhiyuanLck. The only remaining problem is getting text/arrows to be drawn along curved surfaces (or look that way). Is it possible to achieve such an effect?

For instance, how would we do that for the Rightarrows and their labels theta_f and theta_g in the diagram below?

enter image description here

(This diagram is the ice cream cone condition for lax slice bicategories, as in Section 7.1 of Johnson–Yau’s new book on bicategories.)

Compilable code for this diagram:

documentclass[english,11pt]{standalone}
RequirePackage{luatex85}
usepackage{tikz}
usepackage{tikz-cd}
usetikzlibrary{3d}
makeatletter
tikzset{
  plane/.code args={#1and#2}{
    tikz@scan@one@pointpgf@process#1
    edeftemp@a{(thepgf@x, thepgf@y)};
    tikz@scan@one@pointpgf@process#2
    edeftemp@b{(thepgf@x, thepgf@y)};
    pgfkeysalso{
      plane x={temp@a},
      plane y={temp@b},
      canvas is plane,
    }
  },
}
makeatother
usepackage{libertine}
usepackage{mathtools}
usepackage[libertine]{newtxmath}
tikzcdset{
    arrow style=tikz,
    %diagrams={>={Straight Barb[scale=1.5]}}
    diagrams={>={Stealth[round,length=4pt,width=4.95pt,inset=2.75pt]}}
}
begin{document}
newsavebox{BoxNodeOne}
savebox{BoxNodeOne}{
    begin{tikzcd}[row sep={4.5em,between origins}, column sep={4.5em,between origins}, ampersand replacement=&]
        {}
        arrow[r, "F(A)"{plane={(1,0) and (0,0.7)}},phantom]
        &
        {}
    end{tikzcd}
} 
newsavebox{BoxNodeTwo}
savebox{BoxNodeTwo}{
    begin{tikzcd}[row sep={4.5em,between origins}, column sep={4.5em,between origins}, ampersand replacement=&]
        {}
        arrow[r, "F(B)"{plane={(1,0) and (0,0.7)}},phantom]
        &
        {}
    end{tikzcd}
} 
newsavebox{BoxOne}
savebox{BoxOne}{
    begin{tikzcd}[row sep={4.5em,between origins}, column sep={4.5em,between origins}, ampersand replacement=&]
        {}
        \
        arrow[u, Rightarrow]
        {}
    end{tikzcd}
} 
newsavebox{BoxTwo}
savebox{BoxTwo}{
    begin{tikzcd}[row sep={3.6em,between origins}, column sep={3.6em,between origins}, ampersand replacement=&]
        {}
        &
        {}
        \
        {}
        arrow[ru, Rightarrow, bend right=35]
        &
        {}
    end{tikzcd}
} 
begin{tikzcd}[row sep={14.4em,between origins}, column sep={6.3em,between origins}, ampersand replacement=&]
    {hspace{+1.25em}usebox{BoxNodeOne}}
    arrow[rr, "F(g)"{name=3,description,plane={(1,0) and (0,0.7)}},bend left=30]
    arrow[rr, "F(f)"{name=2,description,plane={(1,0) and (0,0.7)}},bend right=30]
    arrow[rd, "phi_{A}"'{name=1},start anchor={[xshift=+0.5em]}]
    &
    &
    {hspace{-1.25em}usebox{BoxNodeTwo}}
    arrow[ld, "phi_{B}",start anchor={[xshift=-0.5em]}]
    \
    {}
    &
    X
    &
    {}
    % 2-Arrows
    arrow[from=1,to=1-3,"theta_{f}"{description,yshift=-0.2em},shorten=2.5em,Rightarrow,xshift=-1.0em,yshift=-1.0em,bend right=15]
    arrow[from=2,to=3,"usebox{BoxOne}"{plane={(1,0) and (0,0.6)}},shorten=0.5em,phantom]
    arrow[from=2,to=3,"scalebox{0.75}{$F(alpha)$}"{description,plane={(1,0) and (0,0.6)}},shorten=0.5em,phantom]
end{tikzcd}
=
begin{tikzcd}[row sep={14.4em,between origins}, column sep={6.3em,between origins}, ampersand replacement=&]
    {hspace{+1.25em}usebox{BoxNodeOne}}
    arrow[rr, "F(g)"{name=3,description,plane={(1,0) and (0,0.7)}},bend left=30]
    arrow[rd, "phi_{A}"'{name=1},start anchor={[xshift=+0.5em]}]
    &
    &
    {hspace{-1.25em}usebox{BoxNodeTwo}}
    arrow[ld, "phi_{B}",start anchor={[xshift=-0.5em]}]
    \
    &
    X
    &
    % 2-Arrows
    arrow[from=1,to=1-3,"theta_{g}"{description,yshift=+0.15em},shorten=1.5em,Rightarrow,bend left=15,xshift=+0.25em,yshift=0.5em]
end{tikzcd}
end{document}


Get this bounty!!!

#StackBounty: #3d Showing planes in 3D

Bounty: 150

I have a GeoGebra diagram but I want to make a Tikz picture out of it. My goal is to make the faded green planes. The vector w-v is on the plane and m is on a plane that is parallel to that green plane as well.

So far, I have a 3D tikz picture and I’ve been experimenting with the best way to make this image. In my code, I have the vectors w, v, and w-v. (The vectors w and v are perpendicular, and w-v would lie on the lower green plane.) I’ve yet to put in vectors x, m, and y, and the plane that goes through m.

enter image description here

documentclass[t]{beamer}
usepackage[utf8]{inputenc}
useoutertheme[subsection=false]{miniframes} 
usepackage{pgfplots}
usepackage{amssymb}
pgfplotsset{compat=1.16,width=10cm}
usetikzlibrary{3d}
usetikzlibrary{calc}
begin{document}

begin{frame}
begin{center}
 begin{tikzpicture} [x={(-0.6cm,-0.4cm)}, y={(1cm,0cm)}, z={(0cm,1cm)}, scale=1]
   begin{scope}[canvas is zy plane at x=0]
     fill[red, opacity = 0.1] (0, 0) rectangle (5, 5);
     draw[gray!40] (0, 0) grid (5, 5);
   end{scope}
   begin{scope}[canvas is zx plane at y=0]
     fill[green, opacity = 0.1] (0, 0) rectangle (5, 5);
     draw[gray!40] (0, 0) grid (5, 5);
     draw[black, -latex] (0, 0) -- (5.5, 0) node[left] {$z$};
     draw[gray, -latex] (0, 0) -- (-0.5, 0);
     foreach z in {1,...,5} draw[] (z, -0.1) -- (z, 0.1) node[left]{z};
     foreach z in {-1,...,-1} draw[gray] (z, -0.1) -- (z, 0.1) node[left]{z};
   end{scope}
   begin{scope}[canvas is yx plane at z=0]
     fill[blue, opacity = 0.1] (0, 0) rectangle (5, 5);
     draw[gray!40] (0, 0) grid (5, 5);
     draw[black, -latex] (0, 0) -- (5.5, 0) node[above] {$y$};
     draw[black, -latex] (0, 0) -- (0, 5.5) node[above] {$x$};
     draw[gray, -latex] (0, 0) -- (0, -0.5);
     foreach x in {0,...,5} draw[] (-0.1, x) -- (0.1, x) node[right]{x};
     foreach x in {-1,...,-1} draw[gray] (-0.1, x) -- (0.1, x) node[right]{x};
     draw[gray, -latex] (0, 0) -- (-0.5, 0);
     foreach y in {1,...,5} draw[] (y, -0.1) -- (y, 0.1) node[below]{y};
     foreach y in {-1,...,-1} draw[gray] (y, -0.1) -- (y, 0.1) node[below]{-y};
   end{scope}
%
   draw[red, thick] (0, 2, 0) -- (-1, 2, 0);
   draw[blue, thick] (-1, 2, 0) -- (-1, 2, 2);
   draw[green, thick] (-1, 0, 0) -- (-1, 2, 0); 
   draw[black, ultra thick, ->] (0,0,0) -- (-1,2,2) node[below right]{$mathbf{v}$};
   %
    draw[red, thick] (0, 1, 0) -- (4, 1, 0);
    draw[blue, thick] (4, 1, 0) -- (4, 1, 1);
    draw[green, thick] (4, 0, 0) -- (4, 1, 0); 
    draw[black, ultra thick, ->] (0,0,0) -- (4,1,1) node[below left]{$mathbf{w}$};
   %
    draw[black, ultra thick, ->] (-1,2,2) -- (4,1,1);
    draw[black] (2.5,1.5,1.5) node[above left]{$mathbf{w-v}$};
 end{tikzpicture}
 end{center}
end{frame}
end{document}


Get this bounty!!!

#StackBounty: #tikz-pgf #3d #tikz-3dplot #tikz-3d Symbolic 3d coordinates in TikZ

Bounty: 500

Warning: answering this question may require some efforts. The purpose of question is to “teach TikZ 3d coordinates”. What does that mean? If we define a coordinate in TikZ,

 path (<x>,<y>) coordinate(A);

this coordinate A gets associated with 2 lengths that specify the location. In any transformed (shifted, rotated, slanted) coordinate system, we can still refer to this coordinate and, say, draw an arrow to it. What is more important for this question, we can always work backwards and figure out what the relative location to another coordinate is, e.g. with the calc library

path let p1=($(A)-(B)$),n1={veclen(x1,y1)},n2={atan2(y1,x1)} in <do something with this information>;

This is impossible in 3d since TikZ truncates the coordinates.

One possible way to deal with this has been proposed in this nice answer. This great, but does not quite work as smoothly as the above-mentioned calc syntax. Perhaps more importantly, one has to make extra efforts to store the 3d coordinates. Ideally, one would have something like

 path (x,y,z) coordinate(A);

and TikZ would remember the z coordinate as well.

Notice that this request may sound more innocent at first glance than it really is. In 2d, we have a predefined reference frame, the screen coordinates. Moreover, the rotations form an Abelian group, so it is less cumbersome to keep track of them and to invert them. The above-mentioned answer saves the coordinates in local frames, so it is impossible to compare coordinates in different frames. However, this would be instrumental for many applications, in which one switches into, say, canvas is xy plane at z=0. Ideally, an answer to this question should associate each symbolic point with some three lengths that are the coordinates in a cleverly chosen reference frame, and there should be means to determine the relative location of two points in a coordinate-independent way, similarly to veclen in 2d.

In the best of all worlds, an answer would also come with an appropriate parser that allows us to do scalar products, vector products, compute the norm of a vector and do matrix multiplications, i.e. orthogonal transformations. (I think that going beyond orthogonal transformations is a mess because then matrix inversion will be really cumbersome.) Some progress regarding parsing has been made in the answers of this question but again it is probably fair to say that this not yet as convenient as the 2d counterparts.

Answers may or may not be based on tikz-3dplot. (tikz-3dplot comes with nice orthonormal projections.) Of course, the best of all options would be something that also works with the Three point perspective library.

I plan to award a bounty on this question.


Get this bounty!!!

#StackBounty: #video #video-conversion #matroska #3d #oculus-rift How do I convert an MK3D file into an MKV file while preserving the 3D?

Bounty: 50

I have an MK3D video file containing a 3D movie. But my video player (Skybox for Oculus Quest) does not support MK3D files, only MKV files. My question is, how do I convert my MK3D file into an MKV file, while keeping it as a 3D video?

Both MKV and MK3D are part of the Matroska file format, so I tried simply renaming the file from “.mk3d” to “.mkv”. But that makes it play as a 2D movie, it loses the 3D aspect. So how do I preserve the 3D nature of the file?


Get this bounty!!!

#StackBounty: #javascript #three.js #3d #nurbs #cubic-bezier How can I create a 3D cubic-bezier curved triangle from 3D points in Three…

Bounty: 50

Following this topic, I am trying to generate a 3D curved triangle as a NURBS surface, but I don’t understand how to set up my 3D points to do that.

Here is the current implementation :

var edges = this.getEdges(), // An edge is a line following 4 dots as a bezier curve.
    dots = self.getDotsFromEdges(edges), // Get all dots in order for building the surface.

    ctrlPoints = [ // Is generated only once before, but copy-pasted here for this sample code.
        [
            new THREE.Vector4(0, 0, 0, 1),
            new THREE.Vector4(0, 0, 0, 1),
            new THREE.Vector4(0, 0, 0, 1),
            new THREE.Vector4(0, 0, 0, 1)
        ],
        [
            new THREE.Vector4(0, 0, 0, 1),
            new THREE.Vector4(0, 0, 0, 1),
            new THREE.Vector4(0, 0, 0, 1),
            new THREE.Vector4(0, 0, 0, 1)
        ],
        [
            new THREE.Vector4(0, 0, 0, 1),
            new THREE.Vector4(0, 0, 0, 1),
            new THREE.Vector4(0, 0, 0, 1),
            new THREE.Vector4(0, 0, 0, 1)
        ]
    ],

    nc,
    deg1 = ctrlPoints.length - 1,
    knots1 = [],
    deg2 = 3,                           // Cubic bezier
    knots2 = [0, 0, 0, 0, 1, 1, 1, 1],  // <-
    cpts,
    nurbs ;

nc = ctrlPoints.length ;
while (nc-- > 0) knots1.push(0) ;
nc = ctrlPoints.length ;
while (nc-- > 0) knots1.push(1) ;


// The following seems to be the problem... :

cpts = ctrlPoints[0] ;
cpts[0].set(dots[0].x, dots[0].y, dots[0].z, 1) ;
cpts[1].set(dots[1].x, dots[1].y, dots[1].z, 1) ;
cpts[2].set(dots[2].x, dots[2].y, dots[2].z, 1) ;
cpts[3].set(dots[3].x, dots[3].y, dots[3].z, 1) ;

cpts = ctrlPoints[1] ;
cpts[0].set(dots[6].x, dots[6].y, dots[6].z, 1) ;
cpts[1].set(dots[5].x, dots[5].y, dots[5].z, 1) ;
cpts[2].set(dots[4].x, dots[4].y, dots[4].z, 1) ;
cpts[3].set(dots[3].x, dots[3].y, dots[3].z, 1) ;

cpts = ctrlPoints[2] ;
cpts[0].set(dots[6].x, dots[6].y, dots[6].z, 1) ;
cpts[1].set(dots[7].x, dots[7].y, dots[7].z, 1) ;
cpts[2].set(dots[8].x, dots[8].y, dots[8].z, 1) ;
cpts[3].set(dots[0].x, dots[0].y, dots[0].z, 1) ;



nurbs = new THREE.NURBSSurface(deg1, deg2, knots1, knots2, ctrlPoints) ;

this.mesh.geometry.dispose() ;
this.mesh.geometry = new THREE.ParametricBufferGeometry(function(u, v, target) {
    return nurbs.getPoint(u, v, target) ;
}, 10, 10) ;

And here is the result:

enter image description here

I tried many different settings but can’t find any working well.

Note: The white points are the edges ends ; The red points are the bezier curve middle points.
Note 2: dots[0] refers to the point 0 in the sample picture, and so on.

Here is working snippet (and fiddle version here)

const
        PI = Math.PI,
    sin = Math.sin,
    cos = Math.cos,
        W = 480,
    H = 400,
    log = console.log,
    DISTANCE = 100 ;

let renderer = new THREE.WebGLRenderer({
      canvas : document.querySelector('canvas'),
      antialias : true,
      alpha : true
    }),
    camera = new THREE.PerspectiveCamera(25, W/H),
    scene = new THREE.Scene(),
    center = new THREE.Vector3(0, 0, 0),

        pts = [] ;

renderer.setClearColor(0x000000, 0) ;

renderer.setSize(W, H) ;
// camera.position.set(-48, 32, 80) ;
camera.position.set(0, 0, DISTANCE) ;
camera.lookAt(center) ;

function createPoint(x, y, z, color) {
        let pt = new THREE.Mesh(
      new THREE.SphereGeometry(1, 10, 10),
      new THREE.MeshBasicMaterial({ color })
    ) ;
    pt.position.set(x, y, z) ;
    pt.x = x ;
    pt.y = y ;
    pt.z = z ;
    pts.push(pt) ;
    
    scene.add(pt) ;
}

function createEdge(pt1, pt2, pt3, pt4) {
        let curve = new THREE.CubicBezierCurve3(
          pt1.position,
          pt2.position,
          pt3.position,
          pt4.position
        ),
            mesh = new THREE.Mesh(
          new THREE.TubeGeometry(curve, 8, 0.5, 8, false),
          new THREE.MeshBasicMaterial({
            color : 0x203040
          })
        ) ;
        
    scene.add(mesh) ;
}

///////////////////////////////////////////////

// POINTS //
createPoint(-16, -8, 0, 0xcc0000) ; // RED
createPoint(-8, -12, 0, 0x999999) ;
createPoint(8, -12, 0, 0x888888) ;
createPoint(16, -8, 0, 0x00cc00) ; // GREEN
createPoint(12, -6, -8, 0x777777) ;
createPoint(8, 6, -8, 0x666666) ;
createPoint(0, 12, 0, 0x0000cc) ; // BLUE
createPoint(-8, 6, -8, 0x555555) ;
createPoint(-12, -6, -8, 0x444444) ;

// EDGES //
createEdge(pts[0], pts[1], pts[2], pts[3]) ;
createEdge(pts[3], pts[4], pts[5], pts[6]) ;
createEdge(pts[6], pts[7], pts[8], pts[0]) ;

// SURFACE //
let ctrlPoints = [
        [
            new THREE.Vector4(pts[0].x, pts[0].y, pts[0].z, 1),
            new THREE.Vector4(pts[1].x, pts[1].y, pts[1].z, 1),
            new THREE.Vector4(pts[2].x, pts[2].y, pts[2].z, 1),
            new THREE.Vector4(pts[3].x, pts[3].y, pts[3].z, 1)
        ],
        [
            new THREE.Vector4(pts[6].x, pts[6].y, pts[6].z, 1),
            new THREE.Vector4(pts[5].x, pts[5].y, pts[5].z, 1),
            new THREE.Vector4(pts[4].x, pts[4].y, pts[4].z, 1),
            new THREE.Vector4(pts[3].x, pts[3].y, pts[3].z, 1)
        ],
        [
            new THREE.Vector4(pts[6].x, pts[6].y, pts[6].z, 1),
            new THREE.Vector4(pts[7].x, pts[7].y, pts[7].z, 1),
            new THREE.Vector4(pts[8].x, pts[8].y, pts[8].z, 1),
            new THREE.Vector4(pts[0].x, pts[0].y, pts[0].z, 1)
        ]
    ],

    nc,
    deg1 = ctrlPoints.length - 1,
    knots1 = [],
    deg2 = 3,                           // Cubic bezier
    knots2 = [0, 0, 0, 0, 1, 1, 1, 1],  // <-
    cpts,
    nurbs ;

nc = ctrlPoints.length ;
while (nc-- > 0) knots1.push(0) ;
nc = ctrlPoints.length ;
while (nc-- > 0) knots1.push(1) ;

nurbs = new THREE.NURBSSurface(deg1, deg2, knots1, knots2, ctrlPoints) ;

let surfaceMesh = new THREE.Mesh(
    new THREE.ParametricBufferGeometry(function(u, v, target) {
        return nurbs.getPoint(u, v, target) ;
    }, 10, 10),
    new THREE.MeshBasicMaterial({
        side : THREE.DoubleSide,
        opacity : 0.9,
        transparent : true,
        color : 0x405060
    })
) ;

scene.add(surfaceMesh) ;



///////////////////////////////////////////////

let azimut = 0,
      pitch = 90,
    isDown = false,
    prevEv ;

function down(de) {
        prevEv = de ;
    isDown = true ;
}

function move(me) {
        if (!isDown) return ;
    
        azimut -= (me.clientX - prevEv.clientX) * 0.5 ;
    azimut %= 360 ;
    if (azimut < 0) azimut = 360 - azimut ;
    
        pitch -= (me.clientY - prevEv.clientY) * 0.5 ;
    if (pitch < 1) pitch = 1 ;
    if (pitch > 180) pitch = 180 ;
    
    prevEv = me ;
    
    let theta = pitch / 180 * PI,
        phi = azimut / 180 * PI,
        radius = DISTANCE ;
    
    camera.position.set(
        radius * sin(theta) * sin(phi),
        radius * cos(theta),
        radius * sin(theta) * cos(phi),
    ) ;
    camera.lookAt(center) ;
    
    renderer.render(scene, camera) ;
}

function up(ue) {
        isDown = false ;
}

renderer.domElement.onmousedown = down ;
window.onmousemove = move ;
window.onmouseup = up ;

renderer.render(scene, camera) ;
body {
  display: flex;
  flex-direction: row;
  justify-content: center;
  align-items: center;
  height: 100vh;
  margin: 0;
  background: #1c2228;
  overflow: hidden;
}
https://cdnjs.cloudflare.com/ajax/libs/three.js/101/three.min.js
https://threejs.org/examples/js/curves/NURBSUtils.js
https://threejs.org/examples/js/curves/NURBSCurve.js
https://threejs.org/examples/js/curves/NURBSSurface.js

<canvas></canvas>


Get this bounty!!!