def revolution_plot3d(curve, trange, phirange=None, parallel_axis='z', axis=(0, 0), print_vector=False, show_curve=False, **kwds): """ Return a plot of a revolved curve. There are three ways to call this function: - ``revolution_plot3d(f,trange)`` where `f` is a function located in the `x z` plane. - ``revolution_plot3d((f_x,f_z),trange)`` where `(f_x,f_z)` is a parametric curve on the `x z` plane. - ``revolution_plot3d((f_x,f_y,f_z),trange)`` where `(f_x,f_y,f_z)` can be any parametric curve. INPUT: - ``curve`` - A curve to be revolved, specified as a function, a 2-tuple or a 3-tuple. - ``trange`` - A 3-tuple `(t,t_{\min},t_{\max})` where t is the independent variable of the curve. - ``phirange`` - A 2-tuple of the form `(\phi_{\min},\phi_{\max})`, (default `(0,\pi)`) that specifies the angle in which the curve is to be revolved. - ``parallel_axis`` - A string (Either 'x', 'y', or 'z') that specifies the coordinate axis parallel to the revolution axis. - ``axis`` - A 2-tuple that specifies the position of the revolution axis. If parallel is: - 'z' - then axis is the point in which the revolution axis intersects the `x y` plane. - 'x' - then axis is the point in which the revolution axis intersects the `y z` plane. - 'y' - then axis is the point in which the revolution axis intersects the `x z` plane. - ``print_vector`` - If True, the parametrization of the surface of revolution will be printed. - ``show_curve`` - If True, the curve will be displayed. EXAMPLES: Let's revolve a simple function around different axes:: sage: u = var('u') sage: f=u^2 sage: revolution_plot3d(f,(u,0,2),show_curve=True,opacity=0.7).show(aspect_ratio=(1,1,1)) If we move slightly the axis, we get a goblet-like surface:: sage: revolution_plot3d(f,(u,0,2),axis=(1,0.2),show_curve=True,opacity=0.5).show(aspect_ratio=(1,1,1)) A common problem in calculus books, find the volume within the following revolution solid:: sage: line=u sage: parabola=u^2 sage: sur1=revolution_plot3d(line,(u,0,1),opacity=0.5,rgbcolor=(1,0.5,0),show_curve=True,parallel_axis='x') sage: sur2=revolution_plot3d(parabola,(u,0,1),opacity=0.5,rgbcolor=(0,1,0),show_curve=True,parallel_axis='x') sage: (sur1+sur2).show() Now let's revolve a parametrically defined circle. We can play with the topology of the surface by changing the axis, an axis in `(0,0)` (as the previous one) will produce a sphere-like surface:: sage: u = var('u') sage: circle=(cos(u),sin(u)) sage: revolution_plot3d(circle,(u,0,2*pi),axis=(0,0),show_curve=True,opacity=0.5).show(aspect_ratio=(1,1,1)) An axis on `(0,y)` will produce a cylinder-like surface:: sage: revolution_plot3d(circle,(u,0,2*pi),axis=(0,2),show_curve=True,opacity=0.5).show(aspect_ratio=(1,1,1)) And any other axis will produce a torus-like surface:: sage: revolution_plot3d(circle,(u,0,2*pi),axis=(2,0),show_curve=True,opacity=0.5).show(aspect_ratio=(1,1,1)) Now, we can get another goblet-like surface by revolving a curve in 3d:: sage: u = var('u') sage: curve=(u,cos(4*u),u^2) sage: revolution_plot3d(curve,(u,0,2),show_curve=True,parallel_axis='z',axis=(1,.2),opacity=0.5).show(aspect_ratio=(1,1,1)) A curvy curve with only a quarter turn:: sage: u = var('u') sage: curve=(sin(3*u),.8*cos(4*u),cos(u)) sage: revolution_plot3d(curve,(u,0,pi),(0,pi/2),show_curve=True,parallel_axis='z',opacity=0.5).show(aspect_ratio=(1,1,1),frame=False) """ from sage.symbolic.ring import SR from sage.symbolic.constants import pi from sage.functions.other import sqrt from sage.functions.trig import sin from sage.functions.trig import cos from sage.functions.trig import atan2 if parallel_axis not in ['x', 'y', 'z']: raise ValueError("parallel_axis must be either 'x', 'y', or 'z'.") vart = trange[0] if str(vart) == 'phi': phi = SR.var('fi') else: phi = SR.var('phi') if phirange is None: #this if-else provides a phirange phirange = (phi, 0, 2 * pi) elif len(phirange) == 3: phi = phirange[0] pass else: phirange = (phi, phirange[0], phirange[1]) if isinstance(curve, tuple) or isinstance(curve, list): #this if-else provides a vector v to be plotted #if curve is a tuple or a list of length 2, it is interpreted as a parametric curve #in the x-z plane. #if it is of length 3 it is interpreted as a parametric curve in 3d space if len(curve) == 2: x = curve[0] y = 0 z = curve[1] elif len(curve) == 3: x = curve[0] y = curve[1] z = curve[2] else: x = vart y = 0 z = curve if parallel_axis == 'z': x0 = axis[0] y0 = axis[1] # (0,0) must be handled separately for the phase value phase = 0 if x0 != 0 or y0 != 0: phase = atan2((y - y0), (x - x0)) R = sqrt((x - x0)**2 + (y - y0)**2) v = (R * cos(phi + phase) + x0, R * sin(phi + phase) + y0, z) elif parallel_axis == 'x': y0 = axis[0] z0 = axis[1] # (0,0) must be handled separately for the phase value phase = 0 if z0 != 0 or y0 != 0: phase = atan2((z - z0), (y - y0)) R = sqrt((y - y0)**2 + (z - z0)**2) v = (x, R * cos(phi + phase) + y0, R * sin(phi + phase) + z0) elif parallel_axis == 'y': x0 = axis[0] z0 = axis[1] # (0,0) must be handled separately for the phase value phase = 0 if z0 != 0 or x0 != 0: phase = atan2((z - z0), (x - x0)) R = sqrt((x - x0)**2 + (z - z0)**2) v = (R * cos(phi + phase) + x0, y, R * sin(phi + phase) + z0) if print_vector: print(v) if show_curve: curveplot = parametric_plot3d((x, y, z), trange, thickness=2, rgbcolor=(1, 0, 0)) return parametric_plot3d(v, trange, phirange, **kwds) + curveplot return parametric_plot3d(v, trange, phirange, **kwds)
def revolution_plot3d(curve, trange, phirange=None, parallel_axis='z', axis=(0, 0), print_vector=False, show_curve=False, **kwds): r""" Return a plot of a revolved curve. There are three ways to call this function: - ``revolution_plot3d(f,trange)`` where `f` is a function located in the `x z` plane. - ``revolution_plot3d((f_x,f_z),trange)`` where `(f_x,f_z)` is a parametric curve on the `x z` plane. - ``revolution_plot3d((f_x,f_y,f_z),trange)`` where `(f_x,f_y,f_z)` can be any parametric curve. INPUT: - ``curve`` - A curve to be revolved, specified as a function, a 2-tuple or a 3-tuple. - ``trange`` - A 3-tuple `(t,t_{\min},t_{\max})` where t is the independent variable of the curve. - ``phirange`` - A 2-tuple of the form `(\phi_{\min},\phi_{\max})`, (default `(0,\pi)`) that specifies the angle in which the curve is to be revolved. - ``parallel_axis`` - A string (Either 'x', 'y', or 'z') that specifies the coordinate axis parallel to the revolution axis. - ``axis`` - A 2-tuple that specifies the position of the revolution axis. If parallel is: - 'z' - then axis is the point in which the revolution axis intersects the `x y` plane. - 'x' - then axis is the point in which the revolution axis intersects the `y z` plane. - 'y' - then axis is the point in which the revolution axis intersects the `x z` plane. - ``print_vector`` - If True, the parametrization of the surface of revolution will be printed. - ``show_curve`` - If True, the curve will be displayed. EXAMPLES: Let's revolve a simple function around different axes:: sage: u = var('u') sage: f = u^2 sage: revolution_plot3d(f, (u,0,2), show_curve=True, opacity=0.7).show(aspect_ratio=(1,1,1)) .. PLOT:: u = var('u') f = u**2 P = revolution_plot3d(f, (u,0,2), show_curve=True, opacity=0.7).plot() sphinx_plot(P) If we move slightly the axis, we get a goblet-like surface:: sage: revolution_plot3d(f, (u,0,2), axis=(1,0.2), show_curve=True, opacity=0.5).show(aspect_ratio=(1,1,1)) .. PLOT:: u = var('u') f = u**2 P = revolution_plot3d(f, (u,0,2), axis=(1,0.2), show_curve=True, opacity=0.5).plot() sphinx_plot(P) A common problem in calculus books, find the volume within the following revolution solid:: sage: line = u sage: parabola = u^2 sage: sur1 = revolution_plot3d(line, (u,0,1), opacity=0.5, rgbcolor=(1,0.5,0), show_curve=True, parallel_axis='x') sage: sur2 = revolution_plot3d(parabola, (u,0,1), opacity=0.5, rgbcolor=(0,1,0), show_curve=True, parallel_axis='x') sage: (sur1+sur2).show() .. PLOT:: u = var('u') line = u parabola = u**2 sur1 = revolution_plot3d(line, (u,0,1), opacity=0.5, rgbcolor=(1,0.5,0), show_curve=True, parallel_axis='x') sur2 = revolution_plot3d(parabola, (u,0,1), opacity=0.5, rgbcolor=(0,1,0), show_curve=True, parallel_axis='x') P = sur1 + sur2 sphinx_plot(P) Now let's revolve a parametrically defined circle. We can play with the topology of the surface by changing the axis, an axis in `(0,0)` (as the previous one) will produce a sphere-like surface:: sage: u = var('u') sage: circle = (cos(u), sin(u)) sage: revolution_plot3d(circle, (u,0,2*pi), axis=(0,0), show_curve=True, opacity=0.5).show(aspect_ratio=(1,1,1)) .. PLOT:: u = var('u') circle = (cos(u), sin(u)) P = revolution_plot3d(circle, (u,0,2*pi), axis=(0,0), show_curve=True, opacity=0.5) sphinx_plot(P) An axis on `(0,y)` will produce a cylinder-like surface:: sage: revolution_plot3d(circle, (u,0,2*pi), axis=(0,2), show_curve=True, opacity=0.5).show(aspect_ratio=(1,1,1)) .. PLOT:: u = var('u') circle = (cos(u), sin(u)) P = revolution_plot3d(circle, (u,0,2*pi), axis=(0,2), show_curve=True, opacity=0.5) sphinx_plot(P) And any other axis will produce a torus-like surface:: sage: revolution_plot3d(circle, (u,0,2*pi), axis=(2,0), show_curve=True, opacity=0.5).show(aspect_ratio=(1,1,1)) .. PLOT:: u = var('u') circle = (cos(u), sin(u)) P = revolution_plot3d(circle, (u,0,2*pi), axis=(2,0), show_curve=True, opacity=0.5) sphinx_plot(P) Now, we can get another goblet-like surface by revolving a curve in 3d:: sage: u = var('u') sage: curve = (u, cos(4*u), u^2) sage: P = revolution_plot3d(curve, (u,0,2), show_curve=True, parallel_axis='z',axis=(1,.2), opacity=0.5) sage: P.show(aspect_ratio=(1,1,1)) .. PLOT:: u = var('u') curve = (u, cos(4*u), u**2) P = revolution_plot3d(curve, (u,0,2), show_curve=True, parallel_axis='z', axis=(1,.2), opacity=0.5) sphinx_plot(P) A curvy curve with only a quarter turn:: sage: u = var('u') sage: curve = (sin(3*u), .8*cos(4*u), cos(u)) sage: revolution_plot3d(curve, (u,0,pi), (0,pi/2), show_curve=True, parallel_axis='z', opacity=0.5).show(aspect_ratio=(1,1,1),frame=False) .. PLOT:: u = var('u') curve = (sin(3*u), .8*cos(4*u), cos(u)) P = revolution_plot3d(curve, (u,0,pi), (0,pi/2), show_curve=True, parallel_axis='z', opacity=0.5) sphinx_plot(P) One can also color the surface using a coloring function of two parameters and a colormap as follows. Note that the coloring function must take values in the interval [0,1]. :: sage: u, phi = var('u,phi') sage: def cf(u,phi): return sin(phi+u) ^ 2 sage: curve = (1+u^2/4, 0, u) sage: revolution_plot3d(curve, (u,-2,2), (0,2*pi), parallel_axis='z', color=(cf, colormaps.PiYG)).show(aspect_ratio=(1,1,1)) .. PLOT:: u, phi = var('u,phi') def cf(u, phi): return sin(phi+u) ** 2 curve = (1+u**2/4, 0, u) P = revolution_plot3d(curve, (u,-2,2), (0,2*pi), parallel_axis='z', color=(cf, colormaps.PiYG)) sphinx_plot(P) The first parameter of the coloring function will be identified with the parameter of the curve, and the second with the angle parameter. .. WARNING:: This kind of coloring using a colormap can be visualized using Jmol, Tachyon (option ``viewer='tachyon'``) and Canvas3D (option ``viewer='canvas3d'`` in the notebook). Another colored example, illustrating that one can use (colormap, color function) instead of (color function, colormap):: sage: u, phi = var('u,phi') sage: def cf(u, phi): return float(2 * u / pi) % 1 sage: curve = (sin(u), 0, u) sage: revolution_plot3d(curve, (u,0,pi), (0,2*pi), parallel_axis ....: ='z', color=(colormaps.brg, cf)).show(aspect_ratio=1) .. PLOT:: u, phi = var('u,phi') def cf(u, phi): return float(2 * u / pi) % 1 curve = (sin(u), 0, u) P = revolution_plot3d(curve, (u,0,pi), (0,2*pi), parallel_axis='z', color=(colormaps.brg, cf)) sphinx_plot(P) """ from sage.symbolic.ring import SR from sage.symbolic.constants import pi from sage.misc.functional import sqrt from sage.functions.trig import sin from sage.functions.trig import cos from sage.functions.trig import atan2 if parallel_axis not in ['x', 'y', 'z']: raise ValueError("parallel_axis must be either 'x', 'y', or 'z'.") vart = trange[0] if str(vart) == 'phi': phi = SR.var('fi') else: phi = SR.var('phi') if phirange is None: # this if-else provides a phirange phirange = (phi, 0, 2 * pi) elif len(phirange) == 3: phi = phirange[0] else: phirange = (phi, phirange[0], phirange[1]) if isinstance(curve, (tuple, list)): #this if-else provides a vector v to be plotted #if curve is a tuple or a list of length 2, it is interpreted as a parametric curve #in the x-z plane. #if it is of length 3 it is interpreted as a parametric curve in 3d space if len(curve) == 2: x = curve[0] y = 0 z = curve[1] elif len(curve) == 3: x = curve[0] y = curve[1] z = curve[2] else: x = vart y = 0 z = curve phase = 0 if parallel_axis == 'z': x0 = axis[0] y0 = axis[1] # (0,0) must be handled separately for the phase value if x0 != 0 or y0 != 0: phase = atan2(y - y0, x - x0) R = sqrt((x - x0)**2 + (y - y0)**2) v = (R * cos(phi + phase) + x0, R * sin(phi + phase) + y0, z) elif parallel_axis == 'x': y0 = axis[0] z0 = axis[1] # (0,0) must be handled separately for the phase value if z0 != 0 or y0 != 0: phase = atan2(z - z0, y - y0) R = sqrt((y - y0)**2 + (z - z0)**2) v = (x, R * cos(phi + phase) + y0, R * sin(phi + phase) + z0) elif parallel_axis == 'y': x0 = axis[0] z0 = axis[1] # (0,0) must be handled separately for the phase value if z0 != 0 or x0 != 0: phase = atan2(z - z0, x - x0) R = sqrt((x - x0)**2 + (z - z0)**2) v = (R * cos(phi + phase) + x0, y, R * sin(phi + phase) + z0) if print_vector: print(v) if show_curve: curveplot = parametric_plot3d((x, y, z), trange, thickness=2, rgbcolor=(1, 0, 0)) return parametric_plot3d(v, trange, phirange, **kwds) + curveplot return parametric_plot3d(v, trange, phirange, **kwds)
def _transition_spher_stereo(self): r""" Initialize the transition map between spherical and stereographic coordinates. TESTS:: sage: S1 = manifolds.Sphere(1) sage: spher = S1.spherical_coordinates(); spher Chart (A, (phi,)) sage: A = spher.domain() sage: stereoN = S1.stereographic_coordinates(pole='north'); stereoN Chart (S^1-{NP}, (y1,)) sage: S1.coord_change(spher, stereoN.restrict(A)) Change of coordinates from Chart (A, (phi,)) to Chart (A, (y1,)) """ # speed-up via simplification method... self.set_simplify_function(lambda expr: expr.simplify()) # configure preexisting charts... W = self._stereoN_dom.intersection(self._stereoS_dom) A = self._spher_dom stereoN, stereoS = self._coordinates['stereographic'][:] coordN = stereoN[:] coordS = stereoS[:] rstN = (coordN[0] != 0, ) rstS = (coordS[0] != 0, ) if self._dim > 1: rstN += (coordN[0] > 0, ) rstS += (coordS[0] > 0, ) stereoN_A = stereoN.restrict(A, rstN) stereoS_A = stereoS.restrict(A, rstS) self._coord_changes[(stereoN.restrict(W), stereoS.restrict(W))].restrict(A) self._coord_changes[(stereoS.restrict(W), stereoN.restrict(W))].restrict(A) spher = self._coordinates['spherical'][0] R = self._radius n = self._dim # transition: spher to stereoN... imm = self.embedding() cart = self._ambient.cartesian_coordinates() # get ambient coordinates and shift to coordinate origin: x = self._shift_coords(imm.expr(spher, cart), s='-') coordfunc = [(R * x[i]) / (R - x[-1]) for i in range(n)] # define transition map: spher_to_stereoN = spher.transition_map(stereoN_A, coordfunc) # transition: stereoN to spher... from sage.functions.trig import acos, atan2 from sage.misc.functional import sqrt # get ambient coordinates and shift to coordinate origin: x = self._shift_coords(imm.expr(stereoN, cart), s='-') coordfunc = [atan2(x[1], x[0])] for k in range(2, n + 1): c = acos(x[k] / sqrt(sum(x[i]**2 for i in range(k + 1)))) coordfunc.append(c) coordfunc = reversed(coordfunc) spher_to_stereoN.set_inverse(*coordfunc, check=False) # transition spher <-> stereoS... stereoN_to_S_A = self.coord_change(stereoN_A, stereoS_A) stereoN_to_S_A * spher_to_stereoN # generates spher_to_stereoS stereoS_to_N_A = self.coord_change(stereoS_A, stereoN_A) spher_to_stereoN.inverse( ) * stereoS_to_N_A # generates stereoS_to_spher
def revolution_plot3d(curve,trange,phirange=None,parallel_axis='z',axis=(0,0),print_vector=False,show_curve=False,**kwds): """ Return a plot of a revolved curve. There are three ways to call this function: - ``revolution_plot3d(f,trange)`` where `f` is a function located in the `x z` plane. - ``revolution_plot3d((f_x,f_z),trange)`` where `(f_x,f_z)` is a parametric curve on the `x z` plane. - ``revolution_plot3d((f_x,f_y,f_z),trange)`` where `(f_x,f_y,f_z)` can be any parametric curve. INPUT: - ``curve`` - A curve to be revolved, specified as a function, a 2-tuple or a 3-tuple. - ``trange`` - A 3-tuple `(t,t_{\min},t_{\max})` where t is the independent variable of the curve. - ``phirange`` - A 2-tuple of the form `(\phi_{\min},\phi_{\max})`, (default `(0,\pi)`) that specifies the angle in which the curve is to be revolved. - ``parallel_axis`` - A string (Either 'x', 'y', or 'z') that specifies the coordinate axis parallel to the revolution axis. - ``axis`` - A 2-tuple that specifies the position of the revolution axis. If parallel is: - 'z' - then axis is the point in which the revolution axis intersects the `x y` plane. - 'x' - then axis is the point in which the revolution axis intersects the `y z` plane. - 'y' - then axis is the point in which the revolution axis intersects the `x z` plane. - ``print_vector`` - If True, the parametrization of the surface of revolution will be printed. - ``show_curve`` - If True, the curve will be displayed. EXAMPLES: Let's revolve a simple function around different axes:: sage: u = var('u') sage: f=u^2 sage: revolution_plot3d(f,(u,0,2),show_curve=True,opacity=0.7).show(aspect_ratio=(1,1,1)) If we move slightly the axis, we get a goblet-like surface:: sage: revolution_plot3d(f,(u,0,2),axis=(1,0.2),show_curve=True,opacity=0.5).show(aspect_ratio=(1,1,1)) A common problem in calculus books, find the volume within the following revolution solid:: sage: line=u sage: parabola=u^2 sage: sur1=revolution_plot3d(line,(u,0,1),opacity=0.5,rgbcolor=(1,0.5,0),show_curve=True,parallel_axis='x') sage: sur2=revolution_plot3d(parabola,(u,0,1),opacity=0.5,rgbcolor=(0,1,0),show_curve=True,parallel_axis='x') sage: (sur1+sur2).show() Now let's revolve a parametrically defined circle. We can play with the topology of the surface by changing the axis, an axis in `(0,0)` (as the previous one) will produce a sphere-like surface:: sage: u = var('u') sage: circle=(cos(u),sin(u)) sage: revolution_plot3d(circle,(u,0,2*pi),axis=(0,0),show_curve=True,opacity=0.5).show(aspect_ratio=(1,1,1)) An axis on `(0,y)` will produce a cylinder-like surface:: sage: revolution_plot3d(circle,(u,0,2*pi),axis=(0,2),show_curve=True,opacity=0.5).show(aspect_ratio=(1,1,1)) And any other axis will produce a torus-like surface:: sage: revolution_plot3d(circle,(u,0,2*pi),axis=(2,0),show_curve=True,opacity=0.5).show(aspect_ratio=(1,1,1)) Now, we can get another goblet-like surface by revolving a curve in 3d:: sage: u = var('u') sage: curve=(u,cos(4*u),u^2) sage: revolution_plot3d(curve,(u,0,2),show_curve=True,parallel_axis='z',axis=(1,.2),opacity=0.5).show(aspect_ratio=(1,1,1)) A curvy curve with only a quarter turn:: sage: u = var('u') sage: curve=(sin(3*u),.8*cos(4*u),cos(u)) sage: revolution_plot3d(curve,(u,0,pi),(0,pi/2),show_curve=True,parallel_axis='z',opacity=0.5).show(aspect_ratio=(1,1,1),frame=False) """ from sage.symbolic.ring import var from sage.symbolic.constants import pi from sage.functions.other import sqrt from sage.functions.trig import sin from sage.functions.trig import cos from sage.functions.trig import atan2 if parallel_axis not in ['x','y','z']: raise ValueError, "parallel_axis must be either 'x', 'y', or 'z'." vart=trange[0] if str(vart)=='phi': phi=var('fi') else: phi=var('phi') if phirange is None:#this if-else provides a phirange phirange=(phi,0,2*pi) elif len(phirange)==3: phi=phirange[0] pass else: phirange=(phi,phirange[0],phirange[1]) if isinstance(curve,tuple) or isinstance(curve,list): #this if-else provides a vector v to be plotted #if curve is a tuple or a list of length 2, it is interpreted as a parametric curve #in the x-z plane. #if it is of length 3 it is interpreted as a parametric curve in 3d space if len(curve) ==2: x=curve[0] y=0 z=curve[1] elif len(curve)==3: x=curve[0] y=curve[1] z=curve[2] else: x=vart y=0 z=curve if parallel_axis=='z': x0=axis[0] y0=axis[1] # (0,0) must be handled separately for the phase value phase=0 if x0!=0 or y0!=0: phase=atan2((y-y0),(x-x0)) R=sqrt((x-x0)**2+(y-y0)**2) v=(R*cos(phi+phase)+x0,R*sin(phi+phase)+y0,z) elif parallel_axis=='x': y0=axis[0] z0=axis[1] # (0,0) must be handled separately for the phase value phase=0 if z0!=0 or y0!=0: phase=atan2((z-z0),(y-y0)) R=sqrt((y-y0)**2+(z-z0)**2) v=(x,R*cos(phi+phase)+y0,R*sin(phi+phase)+z0) elif parallel_axis=='y': x0=axis[0] z0=axis[1] # (0,0) must be handled separately for the phase value phase=0 if z0!=0 or x0!=0: phase=atan2((z-z0),(x-x0)) R=sqrt((x-x0)**2+(z-z0)**2) v=(R*cos(phi+phase)+x0,y,R*sin(phi+phase)+z0) if print_vector: print v if show_curve: curveplot=parametric_plot3d((x,y,z),trange,thickness=2,rgbcolor=(1,0,0)) return parametric_plot3d(v,trange,phirange,**kwds)+curveplot return parametric_plot3d(v,trange,phirange,**kwds)
def Sphere(dim=None, radius=1, names=None, stereo2d=False, stereo_lim=None): """ Generate a sphere embedded in Euclidean space. The shortcut operator ``.<,>`` can be used to specify the coordinates. INPUT: - ``dim`` -- (optional) the dimension of the sphere; if not specified, equals to the number of coordinate names - ``radius`` -- (default: ``1``) radius of the sphere - ``names`` -- (default: ``None``) name of the coordinates, automatically set by the shortcut operator - ``stereo2d`` -- (default: ``False``) if ``True``, defines only the stereographic charts, only implemented in 2d - ``stereo_lim`` -- (default: ``None``) parameter used to restrict the span of the stereographic charts, so that they don't cover the whole sphere; valid domain will be ``x**2 + y**2 < stereo_lim**2`` OUTPUT: - Riemannian manifold EXAMPLES:: sage: S.<th, ph> = manifolds.Sphere() sage: S 2-dimensional Riemannian submanifold S embedded in the Euclidean space E^3 sage: S.atlas() [Chart (S, (th, ph))] sage: S.metric().display() gamma = dth*dth + sin(th)^2 dph*dph sage: S = manifolds.Sphere(2, stereo2d=True) # long time sage: S # long time 2-dimensional Riemannian submanifold S embedded in the Euclidean space E^3 sage: S.metric().display() # long time gamma = 4/(x^4 + y^4 + 2*(x^2 + 1)*y^2 + 2*x^2 + 1) dx*dx + 4/(x^4 + y^4 + 2*(x^2 + 1)*y^2 + 2*x^2 + 1) dy*dy """ from sage.functions.trig import cos, sin, atan, atan2 from sage.functions.other import sqrt from sage.symbolic.constants import pi from sage.misc.misc_c import prod from sage.manifolds.manifold import Manifold from sage.manifolds.differentiable.euclidean import EuclideanSpace if dim is None: if names is None: raise ValueError("either the names or the dimension must be specified") dim = len(names) else: if names is not None and dim != len(names): raise ValueError("the number of coordinates does not match the dimension") if stereo2d: if dim != 2: raise NotImplementedError("stereographic charts only " "implemented for 2d spheres") E = EuclideanSpace(3, names=("X", "Y", "Z")) S2 = Manifold(dim, 'S', ambient=E, structure='Riemannian') U = S2.open_subset('U') V = S2.open_subset('V') stereoN = U.chart(names=("x", "y")) x, y = stereoN[:] stereoS = V.chart(names=("xp", "yp")) xp, yp = stereoS[:] if stereo_lim is not None: stereoN.add_restrictions(x**2+y**2 < stereo_lim**2) stereoS.add_restrictions(xp**2+yp**2 < stereo_lim**2) stereoN_to_S = stereoN.transition_map(stereoS, (x / (x**2 + y**2), y / (x**2 + y**2)), intersection_name='W', restrictions1=x**2 + y**2 != 0, restrictions2=xp**2+yp**2!=0) stereoN_to_S.set_inverse(xp / (xp**2 + yp**2), yp / (xp**2 + yp**2), check=False) W = U.intersection(V) stereoN_W = stereoN.restrict(W) stereoS_W = stereoS.restrict(W) A = W.open_subset('A', coord_def={stereoN_W: (y != 0, x < 0), stereoS_W: (yp != 0, xp < 0)}) stereoN_A = stereoN_W.restrict(A) if names is None: names = tuple(["phi_{}:(0,pi)".format(i) for i in range(dim - 1)] + ["phi_{}:(-pi,pi):periodic".format(dim - 1)]) else: names = tuple([names[i] + ":(0,pi)" for i in range(dim - 1)] + [names[dim - 1] + ":(-pi,pi):periodic"]) spher = A.chart(names=names) th, ph = spher[:] spher_to_stereoN = spher.transition_map(stereoN_A, (sin(th)*cos(ph) / (1-cos(th)), sin(th)*sin(ph) / (1-cos(th)))) spher_to_stereoN.set_inverse(2*atan(1/sqrt(x**2+y**2)), atan2(-y, -x)+pi, check=False) stereoN_to_S_A = stereoN_to_S.restrict(A) stereoN_to_S_A * spher_to_stereoN # generates spher_to_stereoS stereoS_to_N_A = stereoN_to_S.inverse().restrict(A) spher_to_stereoN.inverse() * stereoS_to_N_A # generates stereoS_to_spher coordfunc1 = [sin(th)*cos(ph), sin(th)*sin(ph), cos(th)] coordfunc2 = [2*x/(1+x**2+y**2), 2*y/(1+x**2+y**2), (x**2+y**2-1)/(1+x**2+y**2)] coordfunc3 = [2*xp/(1+xp**2+yp**2), 2*yp/(1+xp**2+yp**2),(1-xp**2-yp**2)/(1+xp**2+yp**2)] imm = S2.diff_map(E, {(spher, E.default_chart()): coordfunc1, (stereoN, E.default_chart()): coordfunc2, (stereoS, E.default_chart()): coordfunc3}) S2.set_embedding(imm) S2.induced_metric() return S2 if dim != 2: raise NotImplementedError("only implemented for 2 dimensional spheres") E = EuclideanSpace(3, symbols='X Y Z') M = Manifold(dim, 'S', ambient=E, structure='Riemannian') if names is None: names = tuple(["phi_{}:(0,pi)".format(i) for i in range(dim-1)] + ["phi_{}:(-pi,pi):periodic".format(dim-1)]) else: names = tuple([names[i]+":(0,pi)"for i in range(dim - 1)] + [names[dim-1]+":(-pi,pi):periodic"]) C = M.chart(names=names) M._first_ngens = C._first_ngens phi = M._first_ngens(dim)[:] coordfunc = ([radius * prod(sin(phi[j]) for j in range(dim))] + [radius * cos(phi[i]) * prod(sin(phi[j]) for j in range(i)) for i in range(dim)]) imm = M.diff_map(E, coordfunc) M.set_embedding(imm) M.induced_metric() return M