campy
campy

Reputation: 86

ploting the normal vector to a plane

The normal vector is calculated with the cross product of two vectors on the plane, so it shoud be perpendicular to the plane. But as you can seein the plot the normal vector produced with quiver isn't perpendicular. Is the calculation of the plane wrong, my normal vector or the way i plot the normal vector?

import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D

points = [[3.2342, 1.8487, -1.8186],
           [2.9829, 1.6434, -1.8019],
           [3.4247, 1.5550, -1.8093]]

p0, p1, p2 = points
x0, y0, z0 = p0
x1, y1, z1 = p1
x2, y2, z2 = p2

ux, uy, uz = u = [x1-x0, y1-y0, z1-z0] #first vector
vx, vy, vz = v = [x2-x0, y2-y0, z2-z0] #sec vector

u_cross_v = [uy*vz-uz*vy, uz*vx-ux*vz, ux*vy-uy*vx] #cross product

point  = np.array(p1)
normal = np.array(u_cross_v)

d = -point.dot(normal)

print('plane equation:\n{:1.4f}x + {:1.4f}y + {:1.4f}z + {:1.4f} = 0'.format(normal[0], normal[1], normal[2], d))

xx, yy = np.meshgrid(range(10), range(10))

z = (-normal[0] * xx - normal[1] * yy - d) * 1. / normal[2]

# plot the surface
plt3d = plt.figure().gca(projection='3d')
plt3d.quiver(x0, y0, z0, normal[0], normal[1], normal[2], color="m")

plt3d.plot_surface(xx, yy, z)

plt3d.set_xlabel("X", color='red', size=18)
plt3d.set_ylabel("Y", color='green', size=18)
plt3d.set_zlabel("Z", color='b', size=18)
plt.show()

enter image description here

Upvotes: 4

Views: 9765

Answers (1)

Dorian
Dorian

Reputation: 1488

Actually, your plot is 100% correct. The scale of your Z axis does not correspond to the same scale on X & Y axis. If you use a function to set the scale correct, you can see that:

...
plt3d.set_zlabel("Z", color='b', size=18)

# insert these lines
ax = plt.gca()
set_axis_equal(ax)

plt.show()

and the corresponding function from this post:

def set_axes_radius(ax, origin, radius):
    '''
        From StackOverflow question:
        https://stackoverflow.com/questions/13685386/
    '''
    ax.set_xlim3d([origin[0] - radius, origin[0] + radius])
    ax.set_ylim3d([origin[1] - radius, origin[1] + radius])
    ax.set_zlim3d([origin[2] - radius, origin[2] + radius])


def set_axes_equal(ax, zoom=1.):
    '''
        Make axes of 3D plot have equal scale so that spheres appear as spheres,
        cubes as cubes, etc..  This is one possible solution to Matplotlib's
        ax.set_aspect("equal") and ax.axis("equal") not working for 3D.
        input:
          ax:   a matplotlib axis, e.g., as output from plt.gca().

    '''

    limits = np.array([
        ax.get_xlim3d(),
        ax.get_ylim3d(),
        ax.get_zlim3d(),
    ])

    origin = np.mean(limits, axis=1)
    radius = 0.5 * np.max(np.abs(limits[:, 1] - limits[:, 0])) / zoom
    set_axes_radius(ax, origin, radius)

Upvotes: 3

Related Questions