0

I have a code for plotting a cone with some text and arrows like axes. How to change this code to get the bases of the cone looking like circles? Is the problem in the settings of margins? Or is it necessary to define circles in another way?

import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits import mplot3d
from matplotlib.patches import FancyArrowPatch
from mpl_toolkits.mplot3d import proj3d            

class Arrow3D(FancyArrowPatch):

    def __init__(self, xs, ys, zs, *args, **kwargs):
        FancyArrowPatch.__init__(self, (0, 0), (0, 0), *args, **kwargs)
        self._verts3d = xs, ys, zs

    def draw(self, renderer):
        xs3d, ys3d, zs3d = self._verts3d
        xs, ys, zs = proj3d.proj_transform(xs3d, ys3d, zs3d, renderer.M)
        self.set_positions((xs[0], ys[0]), (xs[1], ys[1]))
        FancyArrowPatch.draw(self, renderer)

nn = 400  # number of points along circle's perimeter
theta = np.linspace(0, 2*np.pi, nn)
rho = np.ones(nn)

# (x,y) represents points on circle's perimeter
x = np.ravel(rho*np.cos(theta))
y = np.ravel(rho*np.sin(theta))

fig, ax = plt.subplots()
plt.rcParams["figure.figsize"] = [5, 5]
figsize = (5, 5)
ax = plt.axes(projection='3d')  # set the axes for 3D plot

ax.azim = -88  # y rotation (default=270)
ax.elev = 13   # x rotation (default=0)

# Low, high values of z for plotting 2 circles at different elevation
loz, hiz = -15, 15

# Plot two circles
ax.plot(x, y, hiz)   
ax.plot(x, y, loz)  

# Set some indices to get proper (x,y) for line plotting
lo1,hi1 = 15, 15+nn//2
lo2,hi2 = lo1+nn//2-27, hi1-nn//2-27

# Plot 3d lines using coordinates of selected points
ax.plot([x[lo1], x[hi1]], [y[lo1], y[hi1]], [loz, hiz]) 
ax.plot([x[lo2], x[hi2]], [y[lo2], y[hi2]], [loz, hiz]) 

eps = 0.005
ax.plot([0, 0], [0, 0], [0, 20])               # extend in z direction
ax.plot([0-eps, 0], [0-eps, -5], [0-eps, 0])   # extend in y direction
ax.plot([0, 1.3], [0, 0], [0, 0])              # extend in x direction

ax.plot([0+eps, 0.6], [0+eps, -4], [0+eps, 16])      # v vector
ax.plot([0.63, 0.63], [-4, -4], [16, -0.005])                 # vertical projection
ax.plot([0+eps, 0.6], [0+eps, -4], [0+eps, -0.005])  # to the beginning

ax.scatter(0, 0, 20, marker=(3, 0, 0), s=100, clip_on=False)
ax.scatter(0, -5, 0, marker=(3, 0, 43), s=100, clip_on=False)
ax.scatter(1.3, 0, 0, marker=(3, 0, 30), s=100, clip_on=False)
ax.scatter(0.6, -4, 16, marker=(3, 0, 80), s=100, clip_on=False)
ax.scatter(0.6, -4, -0.005, marker=(3, 0, 0), s=100, clip_on=False)

a2 = Arrow3D([0.14, -0.515], [-5.581, 1.358], [14.73, 4.983], mutation_scale=20, arrowstyle="-|>", color="k", connectionstyle="arc3,rad=0.3")
ax.add_artist(a2)

ax.text3D(0.23, -5.23, 23.33, r'$A$') 
ax.text3D(1.41, 1.29, -2.7, r'$B$')   
ax.text3D(-0.31, 1.46, -12.6, r'$C$')  
ax.text3D(0.4, -5.48, 17, r'$D$') 
ax.text3D(0.64, 1.57, -9.95, r'$E$') 
ax.text3D(-0.2, -5.5, 15.73, r'$F$') 

# Hide axes
ax._axis3don = False

# Save the figure (.pdf)
margins = {  #     vvv margin in inches
    "left"   : 1 / figsize[0],
    "bottom" : -2.45 / figsize[1],
    "right"  : 1 - 0.5 / figsize[0],
    "top"    : 1 + 1.8  / figsize[1]
}
fig.subplots_adjust(**margins)
plt.savefig('output.pdf')
plt.show()

From this code I got the following output:

enter image description here

The desired output is bases looking like a circle.

This is the view from above:

enter image description here

It is not a circle but an ellipse.

Desired shape of the cone: enter image description here

Elena Greg
  • 1,061
  • 1
  • 11
  • 26
  • Three dimensional plots are rendered with perspective. Just like how parallel lines appear to intersect far off in the distance, the shape of the bases is a result of that perspective. If you want to draw two lines and two circles, you don't need a 3D plot. – Paul H Sep 23 '20 at 14:46
  • Have a look at [this picture](https://en.wikipedia.org/wiki/File:DoubleCone.png) of a cone from Wikipedia. To me it looks exactly like yours, except the Wikipedia picture has shading to better represent the perspective angle (and it's at a slightly different angle). So either: 1) you just need to see that your picture is basically correct as drawn, or 2) your question needs to be made more clear; but I'm not sure which. – tom10 Sep 23 '20 at 15:06
  • @PaulH And can you help me with the perspective, please? I added the view from above to my question. The bases are ellipses instead of circles. I need 3D plot from other reasons - vectors. – Elena Greg Sep 23 '20 at 15:23
  • @tom10 I know that the picture is basically correct. My question is about how to set the perspective or margins or axes in order to get circular bases at the first glance. – Elena Greg Sep 23 '20 at 15:24
  • the only way to get circular bases would be to view it from directly above. At that point it only *looks* like a circle. – Paul H Sep 23 '20 at 15:27
  • Could you send me ax.azim and ax.elev, please? Or would it help to print ellipse with the major axis in the right direction? – Elena Greg Sep 23 '20 at 15:51
  • 1
    The problem is mostly likely that your axes are not scaled the same with the same aspect. Unfortunately matplotlib doens't make this super easy in 3D plots, but [here's an example](https://stackoverflow.com/questions/13685386/matplotlib-equal-unit-length-with-equal-aspect-ratio-z-axis-is-not-equal-to) of how to set the aspect (and you should also set xlim and ylim so they are the same span as well. – tom10 Sep 23 '20 at 16:27
  • What to do with the error: `ax.set_aspect('equal') File "/home/linux/.local/lib/python3.6/site-packages/matplotlib/axes/_base.py", line 1281, in set_aspect 'It is not currently possible to manually set the aspect ' NotImplementedError: It is not currently possible to manually set the aspect on 3D axes` – Elena Greg Sep 23 '20 at 17:19
  • I'd suggest you follow and read the link in my previous comment. (Overall, though, I think it would be helpful for you to understand that if you had asked your question clearly in the first place, you would have an answer by now, and someone, including me, would have just written out the code. All this round and round is because it's hard to know what you want and what you need.) – tom10 Sep 23 '20 at 17:29
  • Thank you. I added the desired shape of the cone to my question. – Elena Greg Sep 23 '20 at 17:40
  • There is no perspective on a cone that will look like that picture (eg, where the cross-sections are circular, and the same size, but not concentric). One way to think of this is to just imagine a single cone where you're looking straight at the bottom face. Now translate the cone so you can see the side as well, so to do this you need to at least be looking at it with an angle wider than the angle of the cone. Therefore, to see the sides and the bottom, you'll need to be viewing the bottom at an angle, so it will look like an ellipse. – tom10 Sep 24 '20 at 03:44

0 Answers0