How to plot / compute the normal at a given point for a differentiable parametric surface?

40 Views Asked by At

To assess my understanding of parametric surfaces and local coordinates differential geometry I am trying to compute and plot the normal of a parametric surface at a point.


Here is what I did :

To keep the example simple I choose a saddle surface parameterized by the map : $$ S : \mathbb{R^2} \rightarrow \mathbb{R^3} \\ (u,v) \rightarrow (u,v,u^2 -v^2 ) $$ I computed the partial derivatives with respect to $ u$ and $v$: $$ S_u = (1,v,2u -v^2) \\ S_v = (u,1,u^2 -2v) $$ From my understanding of the definitions the normal $N$ at point $q$ should be $$ N(q)= \frac{S_u \wedge S_v}{|S_u \wedge S_v|} $$

So for a given point it should be easy to compute the normal we have : $$ S_u \wedge S_v = (-3v^2-2u+u^2v,3u^2-uv^2 +2v, 1-vu) $$

Lets take for example the point $q_1$ with coordinates $(1,1)$ and compute the normal vector :

$$ S_u(q_1) \wedge S_v(q_1) = (-4,4,0)\\ |S_u(q_1) \wedge S_v(q_1)| = 4\sqrt2\\ N(q_1) = (\frac{-1}{\sqrt2}, \frac{1}{\sqrt2},0) \approx (-0.7,0.7,0) $$ Which is not the true since the normal a this point should point toward $z$ axis...


I have done the same in python with equivalently unconvincing results (red lines correspond to $S_u,S_v$and $N$ at point $q_1$ ). :

enter image description here

Here is the python code :

import plotly.figure_factory as ff
import numpy as np
from scipy.spatial import Delaunay
import plotly.graph_objects as go

#Sampling the space of inputs (u,v)
samples =41
u = np.linspace(-10, 10, samples)
v = np.linspace(-10, 10, samples)
u,v = np.meshgrid(u,v)
u = u.flatten()
v = v.flatten()

#Create an array of 1 to fill the derivatives
onespace = np.ones(samples)
u_0,v_0 = np.meshgrid(onespace, onespace)
u_0 = u_0.flatten()
v_0 = v_0.flatten()

#Parametric equations of the sadle 
x = u
y = v
z = u*u- v*v

#Partial derivatives 
x_u = u_0
x_v= u
y_u= v
y_v=  v_0
z_u =2*u - v*v
z_v = u*u - 2*v

#Choosing a point p (u,v) on surface to compute normal
#ids (20,20) correspond to point (0.,0.) 
u_test_id = 10
v_test_id = 10


#Display the parametric surface
points2D = np.vstack([u,v]).T
tri = Delaunay(points2D)
simplices = tri.simplices

fig = ff.create_trisurf(x=x, y=y, z=z,
                         simplices=simplices,
                         title="Sadle", aspectratio=dict(x=1, y=1, z=1))

#compute X at p and partial derivatives at p 
Xp = [x[u_test_id+ v_test_id*samples] ,y[u_test_id+ v_test_id*samples],z[u_test_id+ v_test_id*samples]]
X_u  = np.array([x_u[u_test_id+v_test_id*samples], y_u[u_test_id+v_test_id*samples], z_u[u_test_id+v_test_id*samples]])
X_v  = np.array([x_v[u_test_id+v_test_id*samples], y_v[u_test_id+v_test_id*samples], z_v[u_test_id+v_test_id*samples]])

#computing non unit normals
N = np.cross(X_u,X_v)

#Display the frame using tubes 
fig.add_trace(go.Streamtube(x=[Xp[0]],y=[Xp[1]],z=[Xp[2]],u=[X_v[0]],v=[X_v[1]],w=[X_v[2]],sizeref=0.1))
fig.add_trace(go.Streamtube(x=[Xp[0]],y=[Xp[1]],z=[Xp[2]],u=[X_u[0]],v=[X_u[1]],w=[X_u[2]],sizeref=0.1))
fig.add_trace(go.Streamtube(x=[Xp[0]],y=[Xp[1]],z=[Xp[2]],u=[N[0]],v=[N[1]],w=[N[2]],sizeref=0.1))

fig.show()
1

There are 1 best solutions below

0
On

In order to work with surfaces, you can use gradient vectors, since they are by definition normal to the surface.

You can notice that your caracterization is equivalent to :

$$(x,y,z)\in S \Leftrightarrow z=x^2-y^2\\ \Leftrightarrow f(x,y,z)=x^2-y^2-z=0 $$

Then you know that $\nabla f(x,y,z) $ is the normal vector to your surface :

$$\nabla f(x,y,z)=(2x,-2y,-1) $$

By the way, your normal vector is only on the $z$-axis when $x=y=0$ (as you can see in your python plot)