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 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 plot_hyperplane(hyperplane, **kwds): r""" Return the plot of a single hyperplane. INPUT: - ``**kwds`` -- plot options: see below OUTPUT: A graphics object of the plot. .. RUBRIC:: Plot Options Beside the usual plot options (enter ``plot?``), the plot command for hyperplanes includes the following: - ``hyperplane_label`` -- Boolean value or string (default: ``True``). If ``True``, the hyperplane is labeled with its equation, if a string, it is labeled by that string, otherwise it is not labeled. - ``label_color`` -- (Default: ``'black'``) Color for hyperplane_label. - ``label_fontsize`` -- Size for ``hyperplane_label`` font (default: 14) (does not work in 3d, yet). - ``label_offset`` -- (Default: 0-dim: 0.1, 1-dim: (0,1), 2-dim: (0,0,0.2)) Amount by which label is offset from ``hyperplane.point()``. - ``point_size`` -- (Default: 50) Size of points in a zero-dimensional arrangement or of an arrangement over a finite field. - ``ranges`` -- Range for the parameters for the parametric plot of the hyperplane. If a single positive number ``r`` is given for the value of ``ranges``, then the ranges for all parameters are set to `[-r, r]`. Otherwise, for a line in the plane, ``ranges`` has the form ``[a, b]`` (default: [-3,3]), and for a plane in 3-space, the ``ranges`` has the form ``[[a, b], [c, d]]`` (default: [[-3,3],[-3,3]]). (The ranges are centered around ``hyperplane.point()``.) EXAMPLES:: sage: H1.<x> = HyperplaneArrangements(QQ) sage: a = 3*x + 4 sage: a.plot() # indirect doctest Graphics object consisting of 3 graphics primitives sage: a.plot(point_size=100,hyperplane_label='hello') Graphics object consisting of 3 graphics primitives sage: H2.<x,y> = HyperplaneArrangements(QQ) sage: b = 3*x + 4*y + 5 sage: b.plot() Graphics object consisting of 2 graphics primitives sage: b.plot(ranges=(1,5),label_offset=(2,-1)) Graphics object consisting of 2 graphics primitives sage: opts = {'hyperplane_label':True, 'label_color':'green', ....: 'label_fontsize':24, 'label_offset':(0,1.5)} sage: b.plot(**opts) Graphics object consisting of 2 graphics primitives sage: H3.<x,y,z> = HyperplaneArrangements(QQ) sage: c = 2*x + 3*y + 4*z + 5 sage: c.plot() Graphics3d Object sage: c.plot(label_offset=(1,0,1), color='green', label_color='red', frame=False) Graphics3d Object sage: d = -3*x + 2*y + 2*z + 3 sage: d.plot(opacity=0.8) Graphics3d Object sage: e = 4*x + 2*z + 3 sage: e.plot(ranges=[[-1,1],[0,8]], label_offset=(2,2,1), aspect_ratio=1) Graphics3d Object """ if hyperplane.base_ring().characteristic(): raise NotImplementedError('base field must have characteristic zero') elif hyperplane.dimension() not in [ 0, 1, 2 ]: # dimension of hyperplane, not ambient space raise ValueError('can only plot hyperplanes in dimensions 1, 2, 3') # handle extra keywords if 'hyperplane_label' in kwds: hyp_label = kwds.pop('hyperplane_label') if not hyp_label: has_hyp_label = False else: has_hyp_label = True else: # default hyp_label = True has_hyp_label = True if has_hyp_label: if hyp_label: # then label hyperplane with its equation if hyperplane.dimension() == 2: # jmol does not like latex label = hyperplane._repr_linear(include_zero=False) else: label = hyperplane._latex_() else: label = hyp_label # a string if 'label_color' in kwds: label_color = kwds.pop('label_color') else: label_color = 'black' if 'label_fontsize' in kwds: label_fontsize = kwds.pop('label_fontsize') else: label_fontsize = 14 if 'label_offset' in kwds: has_offset = True label_offset = kwds.pop('label_offset') else: has_offset = False # give default values below if 'point_size' in kwds: pt_size = kwds.pop('point_size') else: pt_size = 50 if 'ranges' in kwds: ranges_set = True ranges = kwds.pop('ranges') else: ranges_set = False # give default values below # the extra keywords have now been handled # now create the plot if hyperplane.dimension() == 0: # a point on a line x, = hyperplane.A() d = hyperplane.b() p = point((d / x, 0), size=pt_size, **kwds) if has_hyp_label: if not has_offset: label_offset = 0.1 p += text(label, (d / x, label_offset), color=label_color, fontsize=label_fontsize) p += text('', (d / x, label_offset + 0.4)) # add space at top if 'ymax' not in kwds: kwds['ymax'] = 0.5 elif hyperplane.dimension() == 1: # a line in the plane pnt = hyperplane.point() w = hyperplane.linear_part().matrix() t = SR.var('t') if ranges_set: if isinstance(ranges, (list, tuple)): t0, t1 = ranges else: # ranges should be a single positive number t0, t1 = -ranges, ranges else: # default t0, t1 = -3, 3 p = parametric_plot(pnt + t * w[0], (t, t0, t1), **kwds) if has_hyp_label: if has_offset: b0, b1 = label_offset else: b0, b1 = 0, 0.2 label = text(label, (pnt[0] + b0, pnt[1] + b1), color=label_color, fontsize=label_fontsize) p += label elif hyperplane.dimension() == 2: # a plane in 3-space pnt = hyperplane.point() w = hyperplane.linear_part().matrix() s, t = SR.var('s t') if ranges_set: if isinstance(ranges, (list, tuple)): s0, s1 = ranges[0] t0, t1 = ranges[1] else: # ranges should be a single positive integers s0, s1 = -ranges, ranges t0, t1 = -ranges, ranges else: # default s0, s1 = -3, 3 t0, t1 = -3, 3 p = parametric_plot3d(pnt + s * w[0] + t * w[1], (s, s0, s1), (t, t0, t1), **kwds) if has_hyp_label: if has_offset: b0, b1, b2 = label_offset else: b0, b1, b2 = 0, 0, 0 label = text3d(label, (pnt[0] + b0, pnt[1] + b1, pnt[2] + b2), color=label_color, fontsize=label_fontsize) p += label return p
def plot_hyperplane(hyperplane, **kwds): r""" Return the plot of a single hyperplane. INPUT: - ``**kwds`` -- plot options: see below OUTPUT: A graphics object of the plot. .. RUBRIC:: Plot Options Beside the usual plot options (enter ``plot?``), the plot command for hyperplanes includes the following: - ``hyperplane_label`` -- Boolean value or string (default: ``True``). If ``True``, the hyperplane is labeled with its equation, if a string, it is labeled by that string, otherwise it is not labeled. - ``label_color`` -- (Default: ``'black'``) Color for hyperplane_label. - ``label_fontsize`` -- Size for ``hyperplane_label`` font (default: 14) (does not work in 3d, yet). - ``label_offset`` -- (Default: 0-dim: 0.1, 1-dim: (0,1), 2-dim: (0,0,0.2)) Amount by which label is offset from ``hyperplane.point()``. - ``point_size`` -- (Default: 50) Size of points in a zero-dimensional arrangement or of an arrangement over a finite field. - ``ranges`` -- Range for the parameters for the parametric plot of the hyperplane. If a single positive number ``r`` is given for the value of ``ranges``, then the ranges for all parameters are set to `[-r, r]`. Otherwise, for a line in the plane, ``ranges`` has the form ``[a, b]`` (default: [-3,3]), and for a plane in 3-space, the ``ranges`` has the form ``[[a, b], [c, d]]`` (default: [[-3,3],[-3,3]]). (The ranges are centered around ``hyperplane.point()``.) EXAMPLES:: sage: H1.<x> = HyperplaneArrangements(QQ) sage: a = 3*x + 4 sage: a.plot() # indirect doctest Graphics object consisting of 3 graphics primitives sage: a.plot(point_size=100,hyperplane_label='hello') Graphics object consisting of 3 graphics primitives sage: H2.<x,y> = HyperplaneArrangements(QQ) sage: b = 3*x + 4*y + 5 sage: b.plot() Graphics object consisting of 2 graphics primitives sage: b.plot(ranges=(1,5),label_offset=(2,-1)) Graphics object consisting of 2 graphics primitives sage: opts = {'hyperplane_label':True, 'label_color':'green', ....: 'label_fontsize':24, 'label_offset':(0,1.5)} sage: b.plot(**opts) Graphics object consisting of 2 graphics primitives sage: H3.<x,y,z> = HyperplaneArrangements(QQ) sage: c = 2*x + 3*y + 4*z + 5 sage: c.plot() Graphics3d Object sage: c.plot(label_offset=(1,0,1), color='green', label_color='red', frame=False) Graphics3d Object sage: d = -3*x + 2*y + 2*z + 3 sage: d.plot(opacity=0.8) Graphics3d Object sage: e = 4*x + 2*z + 3 sage: e.plot(ranges=[[-1,1],[0,8]], label_offset=(2,2,1), aspect_ratio=1) Graphics3d Object """ if hyperplane.base_ring().characteristic() != 0: raise NotImplementedError('base field must have characteristic zero') elif hyperplane.dimension() not in [0, 1, 2]: # dimension of hyperplane, not ambient space raise ValueError('can only plot hyperplanes in dimensions 1, 2, 3') # handle extra keywords if 'hyperplane_label' in kwds: hyp_label = kwds.pop('hyperplane_label') if not hyp_label: has_hyp_label = False else: has_hyp_label = True else: # default hyp_label = True has_hyp_label = True if has_hyp_label: if hyp_label: # then label hyperplane with its equation if hyperplane.dimension() == 2: # jmol does not like latex label = hyperplane._repr_linear(include_zero=False) else: label = hyperplane._latex_() else: label = hyp_label # a string if 'label_color' in kwds: label_color = kwds.pop('label_color') else: label_color = 'black' if 'label_fontsize' in kwds: label_fontsize = kwds.pop('label_fontsize') else: label_fontsize = 14 if 'label_offset' in kwds: has_offset = True label_offset = kwds.pop('label_offset') else: has_offset = False # give default values below if 'point_size' in kwds: pt_size = kwds.pop('point_size') else: pt_size = 50 if 'ranges' in kwds: ranges_set = True ranges = kwds.pop('ranges') else: ranges_set = False # give default values below # the extra keywords have now been handled # now create the plot if hyperplane.dimension() == 0: # a point on a line x, = hyperplane.A() d = hyperplane.b() p = point((d/x,0), size = pt_size, **kwds) if has_hyp_label: if not has_offset: label_offset = 0.1 p += text(label, (d/x,label_offset), color=label_color,fontsize=label_fontsize) p += text('',(d/x,label_offset+0.4)) # add space at top if 'ymax' not in kwds: kwds['ymax'] = 0.5 elif hyperplane.dimension() == 1: # a line in the plane pnt = hyperplane.point() w = hyperplane.linear_part().matrix() x, y = hyperplane.A() d = hyperplane.b() t = SR.var('t') if ranges_set: if type(ranges) in [list,tuple]: t0, t1 = ranges else: # ranges should be a single positive number t0, t1 = -ranges, ranges else: # default t0, t1 = -3, 3 p = parametric_plot(pnt+t*w[0], (t,t0,t1), **kwds) if has_hyp_label: if has_offset: b0, b1 = label_offset else: b0, b1 = 0, 0.2 label = text(label,(pnt[0]+b0,pnt[1]+b1), color=label_color,fontsize=label_fontsize) p += label elif hyperplane.dimension() == 2: # a plane in 3-space pnt = hyperplane.point() w = hyperplane.linear_part().matrix() a, b, c = hyperplane.A() d = hyperplane.b() s,t = SR.var('s t') if ranges_set: if type(ranges) in [list,tuple]: s0, s1 = ranges[0] t0, t1 = ranges[1] else: # ranges should be a single positive integers s0, s1 = -ranges, ranges t0, t1 = -ranges, ranges else: # default s0, s1 = -3, 3 t0, t1 = -3, 3 p = parametric_plot3d(pnt+s*w[0]+t*w[1],(s,s0,s1),(t,t0,t1),**kwds) if has_hyp_label: if has_offset: b0, b1, b2 = label_offset else: b0, b1, b2 = 0, 0, 0 label = text3d(label,(pnt[0]+b0,pnt[1]+b1,pnt[2]+b2), color=label_color,fontsize=label_fontsize) p += label return p
radius=norm(first_vector) if norm(second_vector)!=radius: raise ValueError("Ellipse not implemented") first_unit_vector=first_vector/radius second_unit_vector=second_vector/radius normal_vector=second_vector-(second_vector*first_unit_vector)*first_unit_vector if norm(normal_vector)==0: print (first_point,second_point) return normal_unit_vector=normal_vector/norm(normal_vector) scalar_product=first_unit_vector*second_unit_vector if abs(scalar_product) == 1: raise ValueError("The points are alligned") angle=arccos(scalar_product) var('t') return parametric_plot3d(center+first_vector*cos(t)+radius*normal_unit_vector*sin(t),(0,angle),**kwds) def _arc(p,q,s,**kwds): #rewrite this to use polar_plot and get points to do filled triangles from sage.misc.functional import det from sage.plot.line import line from sage.misc.functional import norm from sage.symbolic.all import pi from sage.plot.arc import arc p,q,s = map( lambda x: vector(x), [p,q,s]) # to avoid running into division by 0 we set to be colinear vectors that are # almost colinear
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)
raise ValueError("Ellipse not implemented") first_unit_vector = first_vector / radius second_unit_vector = second_vector / radius normal_vector = second_vector - (second_vector * first_unit_vector) * first_unit_vector if norm(normal_vector) == 0: print(first_point, second_point) return normal_unit_vector = normal_vector / norm(normal_vector) scalar_product = first_unit_vector * second_unit_vector if abs(scalar_product) == 1: raise ValueError("The points are alligned") angle = arccos(scalar_product) var('t') return parametric_plot3d( center + first_vector * cos(t) + radius * normal_unit_vector * sin(t), (0, angle), **kwds) def _arc(p, q, s, **kwds): #rewrite this to use polar_plot and get points to do filled triangles from sage.misc.functional import det from sage.plot.line import line from sage.misc.functional import norm from sage.symbolic.all import pi from sage.plot.arc import arc p, q, s = map(lambda x: vector(x), [p, q, s]) # to avoid running into division by 0 we set to be colinear vectors that are # almost colinear