0

I have followed How can matplotlib 2D patches be transformed to 3D with arbitrary normals? to transform a matplotlib 2d patch (circle) into a 3d patch with an arbitrary normal vector. However, when I plot this normal vector using quiver, it turns out that the patch and the vector are not perpendicular.

Here is my code (where I load the functions given as 2nd Answer in link above):


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


def rotation_matrix(v1,v2):
    """
    Calculates the rotation matrix that changes v1 into v2.
    """
    v1/=np.linalg.norm(v1)
    v2/=np.linalg.norm(v2)

    cos_angle=np.dot(v1,v2)
    d=np.cross(v1,v2)
    sin_angle=np.linalg.norm(d)

    if sin_angle == 0:
        M = np.identity(3) if cos_angle>0. else -np.identity(3)
    else:
        d/=sin_angle

        eye = np.eye(3)
        ddt = np.outer(d, d)
        skew = np.array([[    0,  d[2],  -d[1]],
                      [-d[2],     0,  d[0]],
                      [d[1], -d[0],    0]], dtype=np.float64)

        M = ddt + cos_angle * (eye - ddt) + sin_angle * skew

    return M

def pathpatch_2d_to_3d(pathpatch, z = 0, normal = 'z'):
    """
    Transforms a 2D Patch to a 3D patch using the given normal vector.

    The patch is projected into they XY plane, rotated about the origin
    and finally translated by z.
    """
    if type(normal) is str: #Translate strings to normal vectors
        index = "xyz".index(normal)
        normal = np.roll((1,0,0), index)

    path = pathpatch.get_path() #Get the path and the associated transform
    trans = pathpatch.get_patch_transform()

    path = trans.transform_path(path) #Apply the transform

    pathpatch.__class__ = art3d.PathPatch3D #Change the class
    pathpatch._code3d = path.codes #Copy the codes
    pathpatch._facecolor3d = pathpatch.get_facecolor #Get the face color    

    verts = path.vertices #Get the vertices in 2D

    M = rotation_matrix(normal,(0, 0, 1)) #Get the rotation matrix

    pathpatch._segment3d = np.array([np.dot(M, (x, y, 0)) + (0, 0, z) for x, y in verts])

def pathpatch_translate(pathpatch, delta):
    """
    Translates the 3D pathpatch by the amount delta.
    """
    pathpatch._segment3d += delta



fig = plt.figure()
ax = fig.gca(projection='3d')
from matplotlib.patches import Circle, PathPatch

dirvec =(-0.420,  -0.757,  -0.500)
normal=dirvec
p = Circle((0,0), 18., facecolor = 'g', alpha = .6)
ax.add_patch(p)
pathpatch_2d_to_3d(p, z = 0, normal = normal)
pathpatch_translate(p, (0.,0.,0.)  )
ax.quiver( 0.,0.,0., -0.420,  -0.757,  -0.500,   length=50, color='g', lw=2, pivot='tail')
xlim( -50., 50. );ylim( -50., 50. );ax.set_zlim(-50.,50)

The result is not far from perpendicular but is clearly not exactly 90 degrees as should be. I appreciate any help to clarify what is going on. Thank you

isantos
  • 1
  • 1
  • 1
    Could you please post a [Minimal, Complete, and Verifiable example](https://stackoverflow.com/help/mcve)? Best would be if you also post the resulting figure of your MCVE. – Thomas Kühn Feb 10 '19 at 12:35
  • I ran your code and the first thing I noticed was that by default the aspect ratio of the plot is not equal (the grid lines don't form perfect squares). When using a somewhat simpler normal vector (say `(1,1,0)`), you can easily notice that the circle and the vector *become* perpendicular if you change the aspect ratio of the figure (and thus of the Axes). [Here](https://stackoverflow.com/q/8130823/2454357) and [here](https://stackoverflow.com/q/38149736/2454357) are some questions that deal with automatically setting aspect ratios. I didn't try them all, but maybe you find some help there. – Thomas Kühn Feb 11 '19 at 06:21
  • 1
    That was it! thank you very much – isantos Feb 11 '19 at 09:21

0 Answers0