def plot3d(f, urange, vrange, adaptive=False, transformation=None, **kwds): """ INPUT: - ``f`` - a symbolic expression or function of 2 variables - ``urange`` - a 2-tuple (u_min, u_max) or a 3-tuple (u, u_min, u_max) - ``vrange`` - a 2-tuple (v_min, v_max) or a 3-tuple (v, v_min, v_max) - ``adaptive`` - (default: False) whether to use adaptive refinement to draw the plot (slower, but may look better). This option does NOT work in conjuction with a transformation (see below). - ``mesh`` - bool (default: False) whether to display mesh grid lines - ``dots`` - bool (default: False) whether to display dots at mesh grid points - ``plot_points`` - (default: "automatic") initial number of sample points in each direction; an integer or a pair of integers - ``transformation`` - (default: None) a transformation to apply. May be a 3 or 4-tuple (x_func, y_func, z_func, independent_vars) where the first 3 items indicate a transformation to Cartesian coordinates (from your coordinate system) in terms of u, v, and the function variable fvar (for which the value of f will be substituted). If a 3-tuple is specified, the independent variables are chosen from the range variables. If a 4-tuple is specified, the 4th element is a list of independent variables. ``transformation`` may also be a predefined coordinate system transformation like Spherical or Cylindrical. .. note:: ``mesh`` and ``dots`` are not supported when using the Tachyon raytracer renderer. EXAMPLES: We plot a 3d function defined as a Python function:: sage: plot3d(lambda x, y: x^2 + y^2, (-2,2), (-2,2)) Graphics3d Object .. PLOT:: sphinx_plot(plot3d(lambda x, y: x**2 + y**2, (-2,2), (-2,2))) We plot the same 3d function but using adaptive refinement:: sage: plot3d(lambda x, y: x^2 + y^2, (-2,2), (-2,2), adaptive=True) Graphics3d Object .. PLOT:: sphinx_plot(plot3d(lambda x, y: x**2 + y**2, (-2,2), (-2,2), adaptive=True)) Adaptive refinement but with more points:: sage: plot3d(lambda x, y: x^2 + y^2, (-2,2), (-2,2), adaptive=True, initial_depth=5) Graphics3d Object .. PLOT:: sphinx_plot(plot3d(lambda x, y: x**2 + y**2, (-2,2), (-2,2), adaptive=True, initial_depth=5)) We plot some 3d symbolic functions:: sage: var('x,y') (x, y) sage: plot3d(x^2 + y^2, (x,-2,2), (y,-2,2)) Graphics3d Object .. PLOT:: var('x y') sphinx_plot(plot3d(x**2 + y**2, (x,-2,2), (y,-2,2))) :: sage: plot3d(sin(x*y), (x, -pi, pi), (y, -pi, pi)) Graphics3d Object .. PLOT:: var('x y') sphinx_plot(plot3d(sin(x*y), (x, -pi, pi), (y, -pi, pi))) We give a plot with extra sample points:: sage: var('x,y') (x, y) sage: plot3d(sin(x^2+y^2),(x,-5,5),(y,-5,5), plot_points=200) Graphics3d Object .. PLOT:: var('x y') sphinx_plot(plot3d(sin(x**2+y**2),(x,-5,5),(y,-5,5), plot_points=200)) :: sage: plot3d(sin(x^2+y^2),(x,-5,5),(y,-5,5), plot_points=[10,100]) Graphics3d Object .. PLOT:: var('x y') sphinx_plot(plot3d(sin(x**2+y**2),(x,-5,5),(y,-5,5), plot_points=[10,100])) A 3d plot with a mesh:: sage: var('x,y') (x, y) sage: plot3d(sin(x-y)*y*cos(x),(x,-3,3),(y,-3,3), mesh=True) Graphics3d Object .. PLOT:: var('x y') sphinx_plot(plot3d(sin(x-y)*y*cos(x),(x,-3,3),(y,-3,3), mesh=True)) Two wobby translucent planes:: sage: x,y = var('x,y') sage: P = plot3d(x+y+sin(x*y), (x,-10,10),(y,-10,10), opacity=0.87, color='blue') sage: Q = plot3d(x-2*y-cos(x*y),(x,-10,10),(y,-10,10),opacity=0.3,color='red') sage: P + Q Graphics3d Object .. PLOT:: x,y=var('x y') P = plot3d(x+y+sin(x*y), (x,-10,10),(y,-10,10), opacity=0.87, color='blue') Q = plot3d(x-2*y-cos(x*y),(x,-10,10),(y,-10,10),opacity=0.3,color='red') sphinx_plot(P+Q) We draw two parametric surfaces and a transparent plane:: sage: L = plot3d(lambda x,y: 0, (-5,5), (-5,5), color="lightblue", opacity=0.8) sage: P = plot3d(lambda x,y: 4 - x^3 - y^2, (-2,2), (-2,2), color='green') sage: Q = plot3d(lambda x,y: x^3 + y^2 - 4, (-2,2), (-2,2), color='orange') sage: L + P + Q Graphics3d Object .. PLOT:: L = plot3d(lambda x,y: 0, (-5,5), (-5,5), color="lightblue", opacity=0.8) P = plot3d(lambda x,y: 4 - x**3 - y**2, (-2,2), (-2,2), color='green') Q = plot3d(lambda x,y: x**3 + y**2 - 4, (-2,2), (-2,2), color='orange') sphinx_plot(L+P+Q) We draw the "Sinus" function (water ripple-like surface):: sage: x, y = var('x y') sage: plot3d(sin(pi*(x^2+y^2))/2,(x,-1,1),(y,-1,1)) Graphics3d Object .. PLOT:: x, y = var('x y') sphinx_plot(plot3d(sin(pi*(x**2+y**2))/2,(x,-1,1),(y,-1,1))) Hill and valley (flat surface with a bump and a dent):: sage: x, y = var('x y') sage: plot3d( 4*x*exp(-x^2-y^2), (x,-2,2), (y,-2,2)) Graphics3d Object .. PLOT:: x, y = var('x y') sphinx_plot(plot3d( 4*x*exp(-x**2-y**2), (x,-2,2), (y,-2,2))) An example of a transformation:: sage: r, phi, z = var('r phi z') sage: trans=(r*cos(phi),r*sin(phi),z) sage: plot3d(cos(r),(r,0,17*pi/2),(phi,0,2*pi),transformation=trans,opacity=0.87).show(aspect_ratio=(1,1,2),frame=False) .. PLOT:: r, phi, z = var('r phi z') trans = (r*cos(phi),r*sin(phi),z) P = plot3d(cos(r),(r,0,17*pi/2),(phi,0,2*pi),transformation=trans,opacity=0.87) P.aspect_ratio([1,1,2]) sphinx_plot(P) An example of a transformation with symbolic vector:: sage: cylindrical(r,theta,z)=[r*cos(theta),r*sin(theta),z] sage: plot3d(3,(theta,0,pi/2),(z,0,pi/2),transformation=cylindrical) Graphics3d Object .. PLOT:: r, theta, z = var('r theta z') cylindrical=(r*cos(theta),r*sin(theta),z) P = plot3d(z-z+3,(theta,0,pi/2),(z,0,pi/2),transformation=cylindrical) sphinx_plot(P) Many more examples of transformations:: sage: u, v, w = var('u v w') sage: rectangular=(u,v,w) sage: spherical=(w*cos(u)*sin(v),w*sin(u)*sin(v),w*cos(v)) sage: cylindric_radial=(w*cos(u),w*sin(u),v) sage: cylindric_axial=(v*cos(u),v*sin(u),w) sage: parabolic_cylindrical=(w*v,(v^2-w^2)/2,u) Plot a constant function of each of these to get an idea of what it does:: sage: A = plot3d(2,(u,-pi,pi),(v,0,pi),transformation=rectangular,plot_points=[100,100]) sage: B = plot3d(2,(u,-pi,pi),(v,0,pi),transformation=spherical,plot_points=[100,100]) sage: C = plot3d(2,(u,-pi,pi),(v,0,pi),transformation=cylindric_radial,plot_points=[100,100]) sage: D = plot3d(2,(u,-pi,pi),(v,0,pi),transformation=cylindric_axial,plot_points=[100,100]) sage: E = plot3d(2,(u,-pi,pi),(v,-pi,pi),transformation=parabolic_cylindrical,plot_points=[100,100]) sage: @interact ... def _(which_plot=[A,B,C,D,E]): ... show(which_plot) <html>... Now plot a function:: sage: g=3+sin(4*u)/2+cos(4*v)/2 sage: F = plot3d(g,(u,-pi,pi),(v,0,pi),transformation=rectangular,plot_points=[100,100]) sage: G = plot3d(g,(u,-pi,pi),(v,0,pi),transformation=spherical,plot_points=[100,100]) sage: H = plot3d(g,(u,-pi,pi),(v,0,pi),transformation=cylindric_radial,plot_points=[100,100]) sage: I = plot3d(g,(u,-pi,pi),(v,0,pi),transformation=cylindric_axial,plot_points=[100,100]) sage: J = plot3d(g,(u,-pi,pi),(v,0,pi),transformation=parabolic_cylindrical,plot_points=[100,100]) sage: @interact ... def _(which_plot=[F, G, H, I, J]): ... show(which_plot) <html>... TESTS: Make sure the transformation plots work:: sage: show(A + B + C + D + E) sage: show(F + G + H + I + J) Listing the same plot variable twice gives an error:: sage: x, y = var('x y') sage: plot3d( 4*x*exp(-x^2-y^2), (x,-2,2), (x,-2,2)) Traceback (most recent call last): ... ValueError: range variables should be distinct, but there are duplicates """ if transformation is not None: params=None from sage.symbolic.callable import is_CallableSymbolicExpression # First, determine the parameters for f (from the first item of urange # and vrange, preferably). if len(urange) == 3 and len(vrange) == 3: params = (urange[0], vrange[0]) elif is_CallableSymbolicExpression(f): params = f.variables() from sage.modules.vector_callable_symbolic_dense import Vector_callable_symbolic_dense if isinstance(transformation, (tuple, list,Vector_callable_symbolic_dense)): if len(transformation)==3: if params is None: raise ValueError("must specify independent variable names in the ranges when using generic transformation") indep_vars = params elif len(transformation)==4: indep_vars = transformation[3] transformation = transformation[0:3] else: raise ValueError("unknown transformation type") # find out which variable is the function variable by # eliminating the parameter variables. all_vars = set(sum([list(s.variables()) for s in transformation],[])) dep_var=all_vars - set(indep_vars) if len(dep_var)==1: dep_var = dep_var.pop() transformation = _ArbitraryCoordinates(transformation, dep_var, indep_vars) else: raise ValueError("unable to determine the function variable in the transform") if isinstance(transformation, _Coordinates): R = transformation.to_cartesian(f, params) return parametric_plot3d.parametric_plot3d(R, urange, vrange, **kwds) else: raise ValueError('unknown transformation type') elif adaptive: P = plot3d_adaptive(f, urange, vrange, **kwds) else: u=fast_float_arg(0) v=fast_float_arg(1) P=parametric_plot3d.parametric_plot3d((u,v,f), urange, vrange, **kwds) P.frame_aspect_ratio([1.0,1.0,0.5]) return P
def bezier3d(path, **options): """ Draws a 3-dimensional bezier path. Input is similar to bezier_path, but each point in the path and each control point is required to have 3 coordinates. INPUT: - ``path`` - a list of curves, which each is a list of points. See further detail below. - ``thickness`` - (default: 2) - ``color`` - a word that describes a color - ``opacity`` - (default: 1) if less than 1 then is transparent - ``aspect_ratio`` - (default:[1,1,1]) The path is a list of curves, and each curve is a list of points. Each point is a tuple (x,y,z). The first curve contains the endpoints as the first and last point in the list. All other curves assume a starting point given by the last entry in the preceding list, and take the last point in the list as their opposite endpoint. A curve can have 0, 1 or 2 control points listed between the endpoints. In the input example for path below, the first and second curves have 2 control points, the third has one, and the fourth has no control points:: path = [[p1, c1, c2, p2], [c3, c4, p3], [c5, p4], [p5], ...] In the case of no control points, a straight line will be drawn between the two endpoints. If one control point is supplied, then the curve at each of the endpoints will be tangent to the line from that endpoint to the control point. Similarly, in the case of two control points, at each endpoint the curve will be tangent to the line connecting that endpoint with the control point immediately after or immediately preceding it in the list. So in our example above, the curve between p1 and p2 is tangent to the line through p1 and c1 at p1, and tangent to the line through p2 and c2 at p2. Similarly, the curve between p2 and p3 is tangent to line(p2,c3) at p2 and tangent to line(p3,c4) at p3. Curve(p3,p4) is tangent to line(p3,c5) at p3 and tangent to line(p4,c5) at p4. Curve(p4,p5) is a straight line. EXAMPLES:: sage: path = [[(0,0,0),(.5,.1,.2),(.75,3,-1),(1,1,0)],[(.5,1,.2),(1,.5,0)],[(.7,.2,.5)]] sage: b = bezier3d(path, color='green') sage: b To construct a simple curve, create a list containing a single list:: sage: path = [[(0,0,0),(1,0,0),(0,1,0),(0,1,1)]] sage: curve = bezier3d(path, thickness=5, color='blue') sage: curve """ import parametric_plot3d as P3D from sage.modules.free_module_element import vector from sage.calculus.calculus import var p0 = vector(path[0][-1]) t = var('t') if len(path[0]) > 2: B = (1-t)**3*vector(path[0][0])+3*t*(1-t)**2*vector(path[0][1])+3*t**2*(1-t)*vector(path[0][-2])+t**3*p0 G = P3D.parametric_plot3d(list(B), (0, 1), color=options['color'], aspect_ratio=options['aspect_ratio'], thickness=options['thickness'], opacity=options['opacity']) else: G = line3d([path[0][0], p0], color=options['color'], thickness=options['thickness'], opacity=options['opacity']) for curve in path[1:]: if len(curve) > 1: p1 = vector(curve[0]) p2 = vector(curve[-2]) p3 = vector(curve[-1]) B = (1-t)**3*p0+3*t*(1-t)**2*p1+3*t**2*(1-t)*p2+t**3*p3 G += P3D.parametric_plot3d(list(B), (0, 1), color=options['color'], aspect_ratio=options['aspect_ratio'], thickness=options['thickness'], opacity=options['opacity']) else: G += line3d([p0,curve[0]], color=options['color'], thickness=options['thickness'], opacity=options['opacity']) p0 = curve[-1] return G
def plot3d(f, urange, vrange, adaptive=False, transformation=None, **kwds): """ INPUT: - ``f`` - a symbolic expression or function of 2 variables - ``urange`` - a 2-tuple (u_min, u_max) or a 3-tuple (u, u_min, u_max) - ``vrange`` - a 2-tuple (v_min, v_max) or a 3-tuple (v, v_min, v_max) - ``adaptive`` - (default: False) whether to use adaptive refinement to draw the plot (slower, but may look better). This option does NOT work in conjuction with a transformation (see below). - ``mesh`` - bool (default: False) whether to display mesh grid lines - ``dots`` - bool (default: False) whether to display dots at mesh grid points - ``plot_points`` - (default: "automatic") initial number of sample points in each direction; an integer or a pair of integers - ``transformation`` - (default: None) a transformation to apply. May be a 3 or 4-tuple (x_func, y_func, z_func, independent_vars) where the first 3 items indicate a transformation to cartesian coordinates (from your coordinate system) in terms of u, v, and the function variable fvar (for which the value of f will be substituted). If a 3-tuple is specified, the independent variables are chosen from the range variables. If a 4-tuple is specified, the 4th element is a list of independent variables. ``transformation`` may also be a predefined coordinate system transformation like Spherical or Cylindrical. .. note:: ``mesh`` and ``dots`` are not supported when using the Tachyon raytracer renderer. EXAMPLES: We plot a 3d function defined as a Python function:: sage: plot3d(lambda x, y: x^2 + y^2, (-2,2), (-2,2)) Graphics3d Object We plot the same 3d function but using adaptive refinement:: sage: plot3d(lambda x, y: x^2 + y^2, (-2,2), (-2,2), adaptive=True) Graphics3d Object Adaptive refinement but with more points:: sage: plot3d(lambda x, y: x^2 + y^2, (-2,2), (-2,2), adaptive=True, initial_depth=5) Graphics3d Object We plot some 3d symbolic functions:: sage: var('x,y') (x, y) sage: plot3d(x^2 + y^2, (x,-2,2), (y,-2,2)) Graphics3d Object sage: plot3d(sin(x*y), (x, -pi, pi), (y, -pi, pi)) Graphics3d Object We give a plot with extra sample points:: sage: var('x,y') (x, y) sage: plot3d(sin(x^2+y^2),(x,-5,5),(y,-5,5), plot_points=200) Graphics3d Object sage: plot3d(sin(x^2+y^2),(x,-5,5),(y,-5,5), plot_points=[10,100]) Graphics3d Object A 3d plot with a mesh:: sage: var('x,y') (x, y) sage: plot3d(sin(x-y)*y*cos(x),(x,-3,3),(y,-3,3), mesh=True) Graphics3d Object Two wobby translucent planes:: sage: x,y = var('x,y') sage: P = plot3d(x+y+sin(x*y), (x,-10,10),(y,-10,10), opacity=0.87, color='blue') sage: Q = plot3d(x-2*y-cos(x*y),(x,-10,10),(y,-10,10),opacity=0.3,color='red') sage: P + Q Graphics3d Object We draw two parametric surfaces and a transparent plane:: sage: L = plot3d(lambda x,y: 0, (-5,5), (-5,5), color="lightblue", opacity=0.8) sage: P = plot3d(lambda x,y: 4 - x^3 - y^2, (-2,2), (-2,2), color='green') sage: Q = plot3d(lambda x,y: x^3 + y^2 - 4, (-2,2), (-2,2), color='orange') sage: L + P + Q Graphics3d Object We draw the "Sinus" function (water ripple-like surface):: sage: x, y = var('x y') sage: plot3d(sin(pi*(x^2+y^2))/2,(x,-1,1),(y,-1,1)) Graphics3d Object Hill and valley (flat surface with a bump and a dent):: sage: x, y = var('x y') sage: plot3d( 4*x*exp(-x^2-y^2), (x,-2,2), (y,-2,2)) Graphics3d Object An example of a transformation:: sage: r, phi, z = var('r phi z') sage: trans=(r*cos(phi),r*sin(phi),z) sage: plot3d(cos(r),(r,0,17*pi/2),(phi,0,2*pi),transformation=trans,opacity=0.87).show(aspect_ratio=(1,1,2),frame=False) An example of a transformation with symbolic vector:: sage: cylindrical(r,theta,z)=[r*cos(theta),r*sin(theta),z] sage: plot3d(3,(theta,0,pi/2),(z,0,pi/2),transformation=cylindrical) Graphics3d Object Many more examples of transformations:: sage: u, v, w = var('u v w') sage: rectangular=(u,v,w) sage: spherical=(w*cos(u)*sin(v),w*sin(u)*sin(v),w*cos(v)) sage: cylindric_radial=(w*cos(u),w*sin(u),v) sage: cylindric_axial=(v*cos(u),v*sin(u),w) sage: parabolic_cylindrical=(w*v,(v^2-w^2)/2,u) Plot a constant function of each of these to get an idea of what it does:: sage: A = plot3d(2,(u,-pi,pi),(v,0,pi),transformation=rectangular,plot_points=[100,100]) sage: B = plot3d(2,(u,-pi,pi),(v,0,pi),transformation=spherical,plot_points=[100,100]) sage: C = plot3d(2,(u,-pi,pi),(v,0,pi),transformation=cylindric_radial,plot_points=[100,100]) sage: D = plot3d(2,(u,-pi,pi),(v,0,pi),transformation=cylindric_axial,plot_points=[100,100]) sage: E = plot3d(2,(u,-pi,pi),(v,-pi,pi),transformation=parabolic_cylindrical,plot_points=[100,100]) sage: @interact ... def _(which_plot=[A,B,C,D,E]): ... show(which_plot) <html>... Now plot a function:: sage: g=3+sin(4*u)/2+cos(4*v)/2 sage: F = plot3d(g,(u,-pi,pi),(v,0,pi),transformation=rectangular,plot_points=[100,100]) sage: G = plot3d(g,(u,-pi,pi),(v,0,pi),transformation=spherical,plot_points=[100,100]) sage: H = plot3d(g,(u,-pi,pi),(v,0,pi),transformation=cylindric_radial,plot_points=[100,100]) sage: I = plot3d(g,(u,-pi,pi),(v,0,pi),transformation=cylindric_axial,plot_points=[100,100]) sage: J = plot3d(g,(u,-pi,pi),(v,0,pi),transformation=parabolic_cylindrical,plot_points=[100,100]) sage: @interact ... def _(which_plot=[F, G, H, I, J]): ... show(which_plot) <html>... TESTS: Make sure the transformation plots work:: sage: show(A + B + C + D + E) sage: show(F + G + H + I + J) Listing the same plot variable twice gives an error:: sage: x, y = var('x y') sage: plot3d( 4*x*exp(-x^2-y^2), (x,-2,2), (x,-2,2)) Traceback (most recent call last): ... ValueError: range variables should be distinct, but there are duplicates """ if transformation is not None: params = None from sage.symbolic.callable import is_CallableSymbolicExpression # First, determine the parameters for f (from the first item of urange # and vrange, preferably). if len(urange) == 3 and len(vrange) == 3: params = (urange[0], vrange[0]) elif is_CallableSymbolicExpression(f): params = f.variables() from sage.modules.vector_callable_symbolic_dense import Vector_callable_symbolic_dense if isinstance(transformation, (tuple, list, Vector_callable_symbolic_dense)): if len(transformation) == 3: if params is None: raise ValueError( "must specify independent variable names in the ranges when using generic transformation" ) indep_vars = params elif len(transformation) == 4: indep_vars = transformation[3] transformation = transformation[0:3] else: raise ValueError("unknown transformation type") # find out which variable is the function variable by # eliminating the parameter variables. all_vars = set( sum([list(s.variables()) for s in transformation], [])) dep_var = all_vars - set(indep_vars) if len(dep_var) == 1: dep_var = dep_var.pop() transformation = _ArbitraryCoordinates(transformation, dep_var, indep_vars) else: raise ValueError( "unable to determine the function variable in the transform" ) if isinstance(transformation, _Coordinates): R = transformation.to_cartesian(f, params) return parametric_plot3d.parametric_plot3d(R, urange, vrange, **kwds) else: raise ValueError('unknown transformation type') elif adaptive: P = plot3d_adaptive(f, urange, vrange, **kwds) else: u = fast_float_arg(0) v = fast_float_arg(1) P = parametric_plot3d.parametric_plot3d((u, v, f), urange, vrange, **kwds) P.frame_aspect_ratio([1.0, 1.0, 0.5]) return P